• Re: Libfuse interoperability/ABI broken.

    From Bernd Schubert@21:1/5 to Ashley Pittman on Thu Mar 7 20:10:01 2024
    Hi all,

    this is certainly not kind of the mail I was hoping for as a new libfuse maintainer.

    As you can see from the title and from discussion below (sorry this is
    not typical ML discussion style), we have a bit of of problem with
    libfuse ABI compatibility.

    While scanning through git history, Ashley found a commit that was adding members in the middle of struct - doesn't break API, but binary
    compatibility. That commit already landed a year ago in these releases:

    git tag --contains a5eb7f2a0117ab43119ef5724cf5f4f2f181804a
    fuse-3.14.1
    fuse-3.15.0
    fuse-3.15.1
    fuse-3.16.1
    fuse-3.16.2


    Obviously this needs improved testing for, but right now we wonder what
    would be the preferred action to avoid issues.

    a) We could fix it and move bits to the right place. Fixes everything
    compiled with old versions, but breaks everything compiled with the
    versions above

    b) Increase the .so version - enforces recompilation of all binaries. Intrusive, especially for distributions, but probably safest.

    c) Other ideas?



    I don't think there is anything in libfuse that would allow us to
    detect which version of libfuse a library was linked to.


    The commit shifted these members in struct fuse_file_info {

    struct fuse_file_info {
    ...
    /** Can be filled by open/create, to allow parallel direct writes on this
    * file */
    unsigned int parallel_direct_writes : 1; --> introduced the shift

    /** Indicates a flush operation. Set in flush operation, also
    maybe set in highlevel lock operation and lowlevel release
    operation. */
    unsigned int flush : 1;

    /** Can be filled in by open, to indicate that the file is not
    seekable. */
    unsigned int nonseekable : 1;

    /* Indicates that flock locks for this file should be
    released. If set, lock_owner shall contain a valid value.
    May only be set in ->release(). */
    unsigned int flock_release : 1;

    /** Can be filled in by opendir. It signals the kernel to
    enable caching of entries returned by readdir(). Has no
    effect when set in other contexts (in particular it does
    nothing when set by open()). */
    unsigned int cache_readdir : 1;

    /** Can be filled in by open, to indicate that flush is not needed
    on close. */
    unsigned int noflush : 1;
    };

    I.e. setting flush would actually set parallel_direct_writes
    (note: with binaries compiled against older libfuse versions)


    For the high level API it is probably less critical, in struct fuse_config these fields are shifted:

    struct fuse_config {
    ...
    /**
    * Allow parallel direct-io writes to operate on the same file.
    *
    * FUSE implementations which do not handle parallel writes on
    * same file/region should NOT enable this option at all as it
    * might lead to data inconsistencies.
    *
    * For the FUSE implementations which have their own mechanism
    * of cache/data integrity are beneficiaries of this setting as
    * it now open doors to parallel writes on the same file (without
    * enabling this setting, all direct writes on the same file are
    * serialized, resulting in huge data bandwidth loss).
    */
    int parallel_direct_writes;

    /**
    * The remaining options are used by libfuse internally and
    * should not be touched.
    */
    int show_help;
    char *modules;
    int debug;
    };


    I don't think there is a security concern, but probably more a
    data safety issue. So I included open mailing lists.


    Thanks,
    Bernd


    On 3/7/24 19:02, Ashley Pittman wrote:

    Simply bumping the .so number and forcing a rebuild would certainly work. It would probably be the safest option but also put the highest cost on users, although I think for this kind of bug then a manual step of verifying the versions are correct is
    needed so forcing a build failure isn’t a bad option. It would massively increase the visibility if this if nothing else.

    Perhaps we should bring in the distro package maintainers here for their guidance/input? Like I say I’ve not had experience of this type of issue before but I’m sure the distros will have.

    Ashley.

    On 7 Mar 2024, at 16:05, Bernd Schubert <bernd.schubert@fastmail.fm> wrote: >>
    Hi Ashley,

    thanks for spotting and embarrassing as I had been involved in
    development that feature.

    Hard to decide if revert it right - issue is, if we revert, everything
    linked with the new version would broken as well. And it is now already
    a year...

    I think we can only increase the .so version and send out a warning
    (mailing list, distributions).

    In order to avoid it in the future, we can probably add some compile
    time asserts about struct member positions.


    Bernd

    On 3/7/24 16:43, Ashley Pittman wrote:

    I’ve spotted an issue with the linked commit, the fuse_file_info struct should have been modified by adding new entries just before the padding, with this commit then members after the new entry will be moved creating a change in the ABI for
    members after this.

    https://github.com/libfuse/libfuse/commit/a5eb7f2a0117ab43119ef5724cf5f4f2f181804a

    This affects the flush, nonseekable, flock_release, cache_readdir and noflush flags, each one of which could be set or cleared accidentally with one of the flags before or after it depending on what version of libfuse the application is compiled and
    linked with.

    This isn’t a failure mode that I’ve experience before when using linux so I don’t have a playbook to work from in how to correct this but essentially fuse3 releases up to and including 3.13 have one ABI and 3.13 to 3.16 have a different ABI.

    I think the best course of action would be to revert to the previous ABI, move the entry to the correct place in the structure, make a new 3.17 release and then publicise that releases 3.13-3.16 should not be used. Of course this fix is part
    technical (we can simply change the code) however there’s no obvious way of enforcing or detecting this incompatibility at run-time so a fuse-users announcement will need to be made, and probably check with maintainers for various distros etc to see if
    they’re on 3.13 or later already, essentially part of the resolution is going to be a PR exercise to retire broken releases.

    One thing we could and perhaps should do is add a extra placeholder to the fuse_lowlevel_ops struct, this would allow the library at runtime to compare the size passed to fuse_session_new() to at least detect if a potentially incompatible set of
    headers had been used, this is far from perfect though as the last change to this structure was five years ago so the closest we could work out is if a binary was compiled using versions 3.8 to 3.16 inclusive, a start but not ideal. Of course if the
    size matches or doesn’t include the lseek entry then there the API change would not be an issue.


    Thoughts? Looking at the flags affected then there are several cases for surprising or unexpected behaviour and intended caching or data loss so I think this is going to be a big issue, at least theoretically if not in practice and we do need to take
    this seriously.

    Ashley.


    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Wookey@21:1/5 to Bernd Schubert on Sat Mar 9 02:50:01 2024
    On 2024-03-07 19:47 +0100, Bernd Schubert wrote:
    Hi all,

    this is certainly not kind of the mail I was hoping for as a new libfuse maintainer.

    As you can see from the title and from discussion below (sorry this is
    not typical ML discussion style), we have a bit of of problem with
    libfuse ABI compatibility.

    Oops.

    While scanning through git history, Ashley found a commit that was adding members in the middle of struct - doesn't break API, but binary compatibility. That commit already landed a year ago in these releases:

    git tag --contains a5eb7f2a0117ab43119ef5724cf5f4f2f181804a
    fuse-3.14.1
    fuse-3.15.0
    fuse-3.15.1
    fuse-3.16.1
    fuse-3.16.2

    Perhaps we should bring in the distro package maintainers here for their guidance/input? Like I say I’ve not had experience of this type of issue before but I’m sure the distros will have.

    Looking at
    https://tracker.debian.org/pkg/fuse
    and
    https://tracker.debian.org/pkg/fuse3

    It looks like Debian (and thus most debian-derived distros) has not
    yet moved past 3.14.0, so we appear to have narrowly avoided building anything using the broken ABI.
    So a revert to the old ABI in 3.17 along with advice not to use 3.14.1-3.16.x would work fine for us.

    I have no particular expertise in libfuse, so I don't know if somehow
    the offending commit has been included depsite the version
    numbers. https://sources.debian.org/src/fuse3/3.14.0-5/debian/patches/
    suggests not. Hopefully the maintainer will weigh in on this.

    So I don't think we care whether you revert or do a soname bump -
    that's something you need to decide based on the fallout elsewhere in
    the ecosystem. I have no idea how many people/groups/distros/softwares
    are following fuse development more closely and thus have already got
    'wrong' binaries.

    A soname bump is probably your best bet, but if no-one even noticed
    this for a year then maybe it's not that bad and just reverting will
    suffice? (And send everyone on a crash course about ABIs and struct padding!)

    Wookey
    --
    Principal hats: Debian, Wookware, ARM
    http://wookware.org/

    -----BEGIN PGP SIGNATURE-----

    iQIzBAABCgAdFiEER4nvI8Pe/wVWh5yq+4YyUahvnkcFAmXrvr0ACgkQ+4YyUahv nkezGw/+L6zWsPtuMHJ3VFrs90X57XFS89dG0khxd9aqTlednV0loY2//rZjUYM3 X0Lj5ROOax52SVS8LUDbaNTFZKHFh6fmvgQV82R25/x6e9ZvPV68t8FbXod23Vok qiICcjkCSIyFrrQOdWBaYxUbbMlLdPZ0sf8v0MTvznlrmciLbN1TEErdC6Usp8Ih 4pcAqpOnz+WDH0K3Yc7o+r9fqI2wjit+TWSF8Iz4c0PMAA8U/3OknmfC1x79X5t1 /GzrVKGT08TBookXe17rrgZ2n5Mvh6Qc1/Fg2BnQkN7tZG2qZSZ0Y+UOxDDvX2FR AqbiUmph0C8r2bvqGpKPFLPJlU59ievPtmc+SdSHRr4ov3XdgYkoJr9NeA8FOSqJ H/P/NqfmbAnRRW8YzhrthxtJrZ8nfzvejKRJAHK2icaQH80W2a2XTIp5GJyq8NGj D4/hEKgXzZAotGVpd26Qlg76F4z+Ffhnd6ExonAbeSMr+skaLt8eNyhXWuk5KKst RoTuVWzPnq9+YwSBHYXZyiXiP1F3fCuhqGRjik0QOVT8IbTYd/I7n5k504r9ev3e 7DbpHFytewURy/xiU7vno5kWDTa0JRj8sDYSs+WKztVKt6xRfqSuZ39cE6Zr9OTv DXQOSdUWmJg/5h1ezdLjjM2rkRgfK+pnd7IiqBQ/kEQhBiaVrEk=
    =0Wjn
    -----END PGP SIGNATURE-----

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Andrea Bolognani@21:1/5 to Bernd Schubert on Mon Mar 11 14:50:01 2024
    On Thu, Mar 07, 2024 at 07:47:23PM +0100, Bernd Schubert wrote:
    Hi all,

    this is certainly not kind of the mail I was hoping for as a new libfuse maintainer.

    As you can see from the title and from discussion below (sorry this is
    not typical ML discussion style), we have a bit of of problem with
    libfuse ABI compatibility.

    [...]

    On 3/7/24 16:43, Ashley Pittman wrote:
    I’ve spotted an issue with the linked commit, the fuse_file_info struct should have been modified by adding new entries just before the padding, with this commit then members after the new entry will be moved creating a change in the ABI for
    members after this.

    https://github.com/libfuse/libfuse/commit/a5eb7f2a0117ab43119ef5724cf5f4f2f181804a

    This affects the flush, nonseekable, flock_release, cache_readdir and noflush flags, each one of which could be set or cleared accidentally with one of the flags before or after it depending on what version of libfuse the application is compiled
    and linked with.

    This isn’t a failure mode that I’ve experience before when using linux so I don’t have a playbook to work from in how to correct this but essentially fuse3 releases up to and including 3.13 have one ABI and 3.13 to 3.16 have a different ABI.

    Not strictly related to the change at hand, but since we're
    discussing recent-ish changes that negatively affected backwards
    compatibility in libfuse I will mention this too:

    https://bugs.debian.org/1031802

    It has been reported downstream a year ago, but I'm not sure if it
    ever got upstream's attention. Now it should have :)

    --
    Andrea Bolognani <eof@kiyuko.org>
    Resistance is futile, you will be garbage collected.

    -----BEGIN PGP SIGNATURE-----

    iQIzBAABCgAdFiEEO48t9niVypx3EjLf954fxUKFg6wFAmXvB9IACgkQ954fxUKF g6yQJw/+Ow7ZteruN4+NwRW4r6vNdDgYu+XEiN1FjLPe3WHg6xfFYplPUHhCQWkT yFln+uXe+XPCbO+MbApMeD0Cfsezo8Q/TN0BTYUg0C/4CIAVwSsjgYXOTKAnE9S5 lh/syM2qA4l0Eim68kIQtmzeigCfWRFbhxdguS2bCpsykCtg03v9xU1N1mcv9J+D 868HZ98H0bIFOYoLY38wyy975+o1RncmuHTrvs8b3pKK4mb0VCg+ZpXSXbIRVAWz IkCoi4bDEtOtnnWxajyZOBDMwpgvV7ADawZlYDGhrxWBhDmEfKq/2ysc6+ZEwpgu ic0okDndogopAk87zCF5xhkSvzm6ChvqVn1yX/rVCJWlM+9UZ0WSI+qRt8M12klr 8D+pjU/sc77JPQki1EzSJnwISTk2GSs4S/ZGkoXdAtXwwOZurenkPuj9fHytVS5J 7DVlHkLtXUg+g/s5Zl2xG931UODJYKX3BdYOIxHPAtYs9E88n0AVp4/8gMUxhD8m yX80i1D/Q5f/dZL533BIuhCpIFYkgdXN784INiw7HMWjbdE9CAO4ly+fFvheZ2LO PYfxo+ig8UhIWKPT/wx9cxYC2k62AtRalqfdYrGbYhl4tS//ncTstRbrLF/vX5Ch zArnCqiBVOehsb5BuQ2Ym57vnj6/UxznsaPb2dWKIqPMYrauHYs=
    =LWUI
    -----END PGP SIGNATURE-----

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bernd Schubert@21:1/5 to Andrea Bolognani on Wed Mar 13 01:30:01 2024
    On 3/11/24 14:32, Andrea Bolognani wrote:
    On Thu, Mar 07, 2024 at 07:47:23PM +0100, Bernd Schubert wrote:
    Hi all,

    this is certainly not kind of the mail I was hoping for as a new libfuse
    maintainer.

    As you can see from the title and from discussion below (sorry this is
    not typical ML discussion style), we have a bit of of problem with
    libfuse ABI compatibility.

    [...]

    On 3/7/24 16:43, Ashley Pittman wrote:
    I’ve spotted an issue with the linked commit, the fuse_file_info struct should have been modified by adding new entries just before the padding, with this commit then members after the new entry will be moved creating a change in the ABI for
    members after this.

    https://github.com/libfuse/libfuse/commit/a5eb7f2a0117ab43119ef5724cf5f4f2f181804a

    This affects the flush, nonseekable, flock_release, cache_readdir and noflush flags, each one of which could be set or cleared accidentally with one of the flags before or after it depending on what version of libfuse the application is compiled
    and linked with.

    This isn’t a failure mode that I’ve experience before when using linux so I don’t have a playbook to work from in how to correct this but essentially fuse3 releases up to and including 3.13 have one ABI and 3.13 to 3.16 have a different ABI.

    Not strictly related to the change at hand, but since we're
    discussing recent-ish changes that negatively affected backwards compatibility in libfuse I will mention this too:

    https://bugs.debian.org/1031802

    It has been reported downstream a year ago, but I'm not sure if it
    ever got upstream's attention. Now it should have :)


    Arg thanks, I don't think that was ever posted. Clearly my fault, I had
    added that symbol when I noticed it is missing.

    Going to follow up in the debian report.


    Thanks,
    Bernd

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bernd Schubert@21:1/5 to Amir Goldstein on Wed Mar 13 01:20:02 2024
    Hi Amir,

    thanks for your help!

    On 3/9/24 03:46, Amir Goldstein wrote:
    On Thu, Mar 7, 2024 at 8:47 PM Bernd Schubert
    <bernd.schubert@fastmail.fm> wrote:

    Hi all,

    this is certainly not kind of the mail I was hoping for as a new libfuse
    maintainer.

    As you can see from the title and from discussion below (sorry this is
    not typical ML discussion style), we have a bit of of problem with
    libfuse ABI compatibility.

    While scanning through git history, Ashley found a commit that was adding
    members in the middle of struct - doesn't break API, but binary
    compatibility. That commit already landed a year ago in these releases:

    git tag --contains a5eb7f2a0117ab43119ef5724cf5f4f2f181804a
    fuse-3.14.1
    fuse-3.15.0
    fuse-3.15.1
    fuse-3.16.1
    fuse-3.16.2


    Obviously this needs improved testing for, but right now we wonder what
    would be the preferred action to avoid issues.

    a) We could fix it and move bits to the right place. Fixes everything
    compiled with old versions, but breaks everything compiled with the
    versions above

    b) Increase the .so version - enforces recompilation of all binaries.
    Intrusive, especially for distributions, but probably safest.

    c) Other ideas?


    Heuristically, you can detect most of the shifted flags at runtime
    because...



    I don't think there is anything in libfuse that would allow us to
    detect which version of libfuse a library was linked to.


    I think we do know for sure if fs was linked with libfuse < 3.12
    without fuse_loop_mt_312?
    so only left to detect 3.12..3.14 vs. 3.14..3.16

    Hmm, I guess I miss something, but how would I know if it was linked
    with fuse_loop_mt_312? That needs an elf reader? Assuming we would put
    this into the library, somehow, how does this work with stripped binaries?

    bschubert2@imesrv6 example>nm passthrough_ll | head -n1
    00000000000003b4 r __abi_tag
    bschubert2@imesrv6 example>strip passthrough_ll
    bschubert2@imesrv6 example>nm passthrough_ll | head -n1
    nm: passthrough_ll: no symbols





    The commit shifted these members in struct fuse_file_info {

    struct fuse_file_info {
    ...
    /** Can be filled by open/create, to allow parallel direct writes on this
    * file */
    unsigned int parallel_direct_writes : 1; --> introduced the shift

    Not expected in flush/release, so can be heuristically interpreted as flush


    /** Indicates a flush operation. Set in flush operation, also
    maybe set in highlevel lock operation and lowlevel release
    operation. */
    unsigned int flush : 1;


    Not expected in open/create, so can be heuristically interpreted as nonseekable

    /** Can be filled in by open, to indicate that the file is not
    seekable. */
    unsigned int nonseekable : 1;


    Not expected in release, so can be heuristically interpreted as flock_release

    /* Indicates that flock locks for this file should be
    released. If set, lock_owner shall contain a valid value.
    May only be set in ->release(). */
    unsigned int flock_release : 1;


    Not expected in opendir, so can be heuristically interpreted as cache_readdir

    /** Can be filled in by opendir. It signals the kernel to
    enable caching of entries returned by readdir(). Has no
    effect when set in other contexts (in particular it does
    nothing when set by open()). */
    unsigned int cache_readdir : 1;


    Ignored in open, but based on the comment above, it may be
    implied that some fs may set it in open() reply

    /** Can be filled in by open, to indicate that flush is not needed >> on close. */
    unsigned int noflush : 1;

    noflush is just an optimization, which the kernel ignores anyway
    when writeback cache is enabled, so no harm done if it is wrongly
    interpreted as readdir_cache in open() and ignored.
    It is also quite recent (3.11) so not very likely used.

    };

    I.e. setting flush would actually set parallel_direct_writes
    (note: with binaries compiled against older libfuse versions)


    It would be suspicious if fs sets parallel_direct_writes flush at
    flush() or release(), so that can be used as a hint of old ABI
    interpreted as flush and issue a warning.

    It would be pretty ugly to use these heuristics in the library forever,
    but perhaps as safety measure for binaries built with a range of
    libfuse version that is not likely to be found in the wild (3.14..3.16)
    it is an acceptable compromise?

    I really like your idea about heuristics, I just don't know how to limit
    the libfuse version range.


    and perhaps the next libfuse version can pass the header version
    fs was compiled with as argument to fuse_loop_mt_317()?


    I think adding it to fuse_loop_mt_317 would be easy and could be
    auto-added with a macro. I think more difficult is to add it to the old api/abi. I.e. it would take a few years until that api version gets
    mostly used?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Ashley Pittman@21:1/5 to All on Fri Mar 22 12:30:02 2024
    I’m back from holiday now and working on this properly. Yes, the below is what I had in mind but haven’t had a chance to go through all the permutations yet and see if we can patch this up at run-time without potentially missing any calls.

    The good news at least is once you detect one one incorrect/invalid option you can then make assumptions about what version the binary was with and then just apply the fix from that point out without further needing the run the heuristics to detect on
    every call. The heuristics and what flags collied with which other flags depend on if we choose to revert to the older ABI or stick with the most recent one, my initial though was to revert on the basis that most distros haven’t updated past the
    change point yet but Bernd was arguing for keeping the new interface. Being able to construct a set of heuristics which is safe in either direction could swing this decision.

    One option we do have is that fuse_session_new passes in struct fuse_lowlevel_ops and it’s size, whilst it’s also a hack I could add padding to this structure to increase it’s size and then have a way of knowing conclusively at run-time that this
    bug and any work-arounds are not applicable.

    Thank you everyone who's contributed to this discussion, hopefully we can find a path where we can detect this and patch it up entirely in the libfuse source.

    Ashley.

    On 9 Mar 2024, at 02:46, Amir Goldstein <amir73il@gmail.com> wrote:

    On Thu, Mar 7, 2024 at 8:47 PM Bernd Schubert
    <bernd.schubert@fastmail.fm> wrote:

    Hi all,

    this is certainly not kind of the mail I was hoping for as a new libfuse
    maintainer.

    As you can see from the title and from discussion below (sorry this is
    not typical ML discussion style), we have a bit of of problem with
    libfuse ABI compatibility.

    While scanning through git history, Ashley found a commit that was adding
    members in the middle of struct - doesn't break API, but binary
    compatibility. That commit already landed a year ago in these releases:

    git tag --contains a5eb7f2a0117ab43119ef5724cf5f4f2f181804a
    fuse-3.14.1
    fuse-3.15.0
    fuse-3.15.1
    fuse-3.16.1
    fuse-3.16.2


    Obviously this needs improved testing for, but right now we wonder what
    would be the preferred action to avoid issues.

    a) We could fix it and move bits to the right place. Fixes everything
    compiled with old versions, but breaks everything compiled with the
    versions above

    b) Increase the .so version - enforces recompilation of all binaries.
    Intrusive, especially for distributions, but probably safest.

    c) Other ideas?


    Heuristically, you can detect most of the shifted flags at runtime
    because...



    I don't think there is anything in libfuse that would allow us to
    detect which version of libfuse a library was linked to.


    I think we do know for sure if fs was linked with libfuse < 3.12
    without fuse_loop_mt_312?
    so only left to detect 3.12..3.14 vs. 3.14..3.16


    The commit shifted these members in struct fuse_file_info {

    struct fuse_file_info {
    ...
    /** Can be filled by open/create, to allow parallel direct writes on this
    * file */
    unsigned int parallel_direct_writes : 1; --> introduced the shift

    Not expected in flush/release, so can be heuristically interpreted as flush


    /** Indicates a flush operation. Set in flush operation, also
    maybe set in highlevel lock operation and lowlevel release
    operation. */
    unsigned int flush : 1;


    Not expected in open/create, so can be heuristically interpreted as nonseekable

    /** Can be filled in by open, to indicate that the file is not
    seekable. */
    unsigned int nonseekable : 1;


    Not expected in release, so can be heuristically interpreted as flock_release

    /* Indicates that flock locks for this file should be
    released. If set, lock_owner shall contain a valid value.
    May only be set in ->release(). */
    unsigned int flock_release : 1;


    Not expected in opendir, so can be heuristically interpreted as cache_readdir

    /** Can be filled in by opendir. It signals the kernel to
    enable caching of entries returned by readdir(). Has no
    effect when set in other contexts (in particular it does
    nothing when set by open()). */
    unsigned int cache_readdir : 1;


    Ignored in open, but based on the comment above, it may be
    implied that some fs may set it in open() reply

    /** Can be filled in by open, to indicate that flush is not needed
    on close. */
    unsigned int noflush : 1;

    noflush is just an optimization, which the kernel ignores anyway
    when writeback cache is enabled, so no harm done if it is wrongly
    interpreted as readdir_cache in open() and ignored.
    It is also quite recent (3.11) so not very likely used.

    };

    I.e. setting flush would actually set parallel_direct_writes
    (note: with binaries compiled against older libfuse versions)


    It would be suspicious if fs sets parallel_direct_writes flush at
    flush() or release(), so that can be used as a hint of old ABI
    interpreted as flush and issue a warning.

    It would be pretty ugly to use these heuristics in the library forever,
    but perhaps as safety measure for binaries built with a range of
    libfuse version that is not likely to be found in the wild (3.14..3.16)
    it is an acceptable compromise?

    and perhaps the next libfuse version can pass the header version
    fs was compiled with as argument to fuse_loop_mt_317()?

    Thanks,
    Amir.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)