Re: Should /usr/include/fts.h adding fts_dirfd and __fts_reserved[3] to FTSENT (a.k.a. struct _ftsent) require a change from libc.so.7 to .8 because of some resulting binary incompatibilities?
- Reply: Mark Millard : "Re: Should /usr/include/fts.h adding fts_dirfd and __fts_reserved[3] to FTSENT (a.k.a. struct _ftsent) require a change from libc.so.7 to .8 because of some resulting binary incompatibilities?"
- In reply to: Mark Millard : "Should /usr/include/fts.h adding fts_dirfd and __fts_reserved[3] to FTSENT (a.k.a. struct _ftsent) require a change from libc.so.7 to .8 because of some resulting binary incompatibilities?"
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Thu, 24 Sep 2026 23:40:47 UTC
No. Symbol versioning.
Warner
On Thu, Sep 24, 2026, 5:37 PM Mark Millard <marklmi@yahoo.com> wrote:
> The SHLIB_MAJOR last changed on 2006-05-21 and is:
>
> LIB=c
> SHLIB_MAJOR= 7
>
> Changes to the binary interface for function (pointers) passed into any
> FreeBSD libc inteferface functions lead to incompatibilities. In this
> case FTSENT (a.k.a. struct _ftsent) is used via function arguments in:
>
> FTS *fts_open(char * const *, int,
> int (*)(const FTSENT * const *, const FTSENT * const *));
> FTS *fts_openat(int, char * const *, int,
> int (*)(const FTSENT * const *, const FTSENT * const *));
> #ifdef __BLOCKS__
> FTS *fts_open_b(char * const *, int,
> int (^)(const FTSENT * const *, const FTSENT * const *));
>
> but, as of 2026-08-03, the content of the *FTSENT (i.e.,
> struct _ftsent) was updated in a non-binary compatible way:
>
> author Jitendra Bhati <bhatijitendra2022@gmail.com> 2026-06-12
> 17:07:55
> +0000
> committer Alan Somers <asomers@FreeBSD.org> 2026-08-03
> 19:12:28 +0000
> commit 4bd01d6ae01632501b63438b8d9a401db9744a78 (patch)
> tree e896c1844fa0fa5897efc4e591ebe02f59a64e61 /include/fts.h
> parent 9590878fca68e62c63d607da73139698e204d0f0 (diff)
>
> . . .
>
> Sponsored by: Google LLC (GSoC 2026)
> Reviewed by: asomers
> Pull Request: https://github.com/freebsd/freebsd-src/pull/2303
> Diffstat (limited to 'include/fts.h')
> -rw-r--r-- include/fts.h 2
> 1 files changed, 2 insertions, 0 deletions
> diff --git a/include/fts.h b/include/fts.h
> index 479905bda463..0308b8ff880b 100644
> --- a/include/fts.h
> +++ b/include/fts.h
> @@ -92,6 +92,8 @@ struct _ftsent {
> char *fts_path; /* root path */
> int fts_errno; /* errno for this node */
> int fts_symfd; /* fd for symlink */
> + int fts_dirfd; /* fd for parent directory */
> + int __fts_reserved[3]; /* reserved for future use */
> __size_t fts_pathlen; /* strlen(fts_path) */
> __size_t fts_namelen; /* strlen(fts_name) */
>
> There is a lot more after what is visible in the diff (starting with the
> inserted fields to have it all together):
>
> int fts_dirfd; /* fd for this directory, if a
> directory */
> int __fts_reserved[3]; /* reserved for future use */
> __size_t fts_pathlen; /* strlen(fts_path) */
> __size_t fts_namelen; /* strlen(fts_name) */
>
> __ino_t fts_ino; /* inode */
> __dev_t fts_dev; /* device */
> __nlink_t fts_nlink; /* link count */
>
> #define FTS_ROOTPARENTLEVEL -1
> #define FTS_ROOTLEVEL 0
> long fts_level; /* depth (-1 to N) */
>
> #define FTS_D 1 /* preorder directory */
> . . .
> #define FTS_W 14 /* whiteout object */
> int fts_info; /* user status for FTSENT
> structure */
>
> #define FTS_DONTCHDIR 0x01 /* don't chdir .. to the parent */
> #define FTS_SYMFOLLOW 0x02 /* followed a symlink to get here
> */
> #define FTS_ISW 0x04 /* this is a whiteout object */
> unsigned fts_flags; /* private flags for FTSENT
> structure */
>
> #define FTS_AGAIN 1 /* read node again */
> ...
> #define FTS_SKIP 4 /* discard node */
> int fts_instr; /* fts_set() instructions */
>
> struct stat *fts_statp; /* stat(2) information */
> char *fts_name; /* file name */
> FTS *fts_fts; /* back pointer to main FTS */
> };
>
>
> An example is in src/tree/lib/libpmc/pmu-events/jevents.c that has:
>
> #include <fts.h>
>
> static int
> #if defined(__linux__) || defined(__APPLE__)
> fts_compare(const FTSENT **a, const FTSENT **b)
> #else
> fts_compare(const FTSENT * const *a, const FTSENT * const *b)
> #endif
> {
> return (strcmp((*a)->fts_name, (*b)->fts_name));
> }
>
> Note that the compile time offset for ->fts_name change changes
> depending on which header version was used and that it will not track
> the environment that jevents is run in.
>
> The same jevent executable file's fts_compare code (non-linux/non-apple)
> is compatible with only one of:
>
> ) a libc.so.7 *FTSENT from before the 2026-08-03 change
> vs.
> ) a libc.so.7 *FTSENT from after the 2026-08-03 change
>
>
> I have observed SIGSEGV with backtraces that show the failure at such a
> fts_compare (*?)->fts_name based deference. That is what eventually
> resulted in my noticing the above in this unfamiliar code.
>
>
> --
> ===
> Mark Millard
> marklmi at yahoo.com
>
>
>