All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog and this project adheres to Semantic Versioning.
0.2.4 - 2026-03-03
そう。神を生贄に捧げる!
- New
EXTRA_RUSTC_FLAGSandEXTRA_CARGO_FLAGSvariables have been added to ourMakefile, making it easier for packaging tools to adjust builds while still usingmake release. install.shnow accepts--rust-targetand--rust-buildmodeas parameters to make cross-compilation workflows easier to write (in particular, this is needed for runc's release scripts).- We now produce signed release artefacts for our releases (though currently
only in the form of signed source and
cargo vendortarballs). The accepted set of signing keys are available inlibpathrs.keyring.
- The
O_PATHresolver forprocfsnow has an additional bit of hardening (each component must be on a procfs -- previously we would check that it is on the same mount, which is an even stronger requirement but on older kernels it is possible to not have a mount ID to check against).
-
Previously,
staticlibbuilds of libpathrs (i.e.,libpathrs.a) inadvertently included symbol versioned symbols (@@LIBPATHRS_X.Y), which would cause linker errors when trying to compile programs statically against libpathrs.This has been resolved, but downstream users who build runc without using
make releasewill need to take care to ensure they correctly set theLIBPATHRS_CAPI_BUILDMODEenvironment variable when building and buildlibpathrs.aandlibpathrs.soin separatecargo build(orcargo rustc) invocations. This is mostly necessary due to the lack of support for#[cfg(crate_type)]. -
go-pathrsnow correctly builds on 32-bit architectures. -
When doing
procfsoperations, previously libpathrs would internally keep a handle toProcfsBaseopen during the entire operation (due toDropsemantics in Rust) rather than closing the file descriptor as quickly as possible. The file descriptor would be closed soon afterwards (and thus was not a leak) but tools that search for file descriptor leaks (such as runc's test suite) could incorrectly classify this as a leak. We now close thisProcfsBasehandle far more aggressively. -
RHEL 8 kernels have backports of the fd-based mount API (
fsopen(2),open_tree(2), et al.) but somerunctesting found that they have very bad (and very difficult to debug) performance issues. Thus, to avoid broken backports libpathrs will now explicitly refuse to use the fd-based mount API if the reported kernel version is pre-5.2 and will instead fallback to the less-secureopen("/proc"). -
libpathrs 0.2.0 added some
fdinfo-based hardening to the procfs resolver whenopenat2is not available. Unfortunately, one aspect of this hardening had a hard requirement on a kernel feature only added in Linux 5.14 (namely theinofield infdinfo) and thus inadvertently increased our minimum kernel version requirement quite significantly. This additional hardening is now only treated as mandatory if the host kernel version is Linux 5.14 or newer. -
Some of the same
fdinfo-based hardening had a separate issue when running in the context of a non-dumpable process on pre-5.14 kernels -- causing spuriousEACCESerrors. We now permitfdinfoto be inaccessible in this very specific situation.
0.2.3 - 2026-01-29
この閃きを俺は信じる!
- While our MSRV is Rust 1.63 (to allow libpathrs to be packaged for Debian
12), we inadvertently depended on a Rust 1.64 feature in our build scripts
(
cargo rustc --crate-type). We have added a temporary workaround for Rust 1.63, which will be removed after we update our MSRV. - A bug in our system for generating symbol versions actually resulted in no
versioned symbols for
libpathrs.sowhen compiled with Rust 1.72 or newer. This bug has been present since libpathrs 0.2, meaning that this has been broken for all versions with versioned symbols. This has been fixed, and now our symbols are properly versioned.- In addition, building
libpathrs.sowith pre-1.90 Rust was broken because of unfortunate interactions with GNU ld and our version scripts. This has been resolved, but means that libpathrs now requireslldto compile, even on older Rust versions. Rust 1.90 switched tolldby default for x86 platforms.
- In addition, building
0.2.2 - 2025-11-25
貴様ら全員刀の錆にしてやるぜ。
- Add
_test_racefeature to allow you to opt-out of the race tests when testing withcargo test(which resolves the spuriousEAGAINfailures you get with naivecargo testandcargo nextestruns, along with making the whole test run ~50x faster).
- Previously,
cargo testruns would fail with errors due to the unique execution environment thatcargo nextestprovides. We have resolved those differences and now have smoke tests in our CI to make sure that a naivecargo testrun will succeed. - libpathrs will now apply the same
openat2retry logic for any usage ofopenat2(for scoped lookups), to further improve resiliency on busy systems. - Our logic for deciding whether to use
openat2(2)or fallback to anO_PATHresolver would cache the result to avoid doing needless test runs ofopenat2(2). However, this causes issues when libpathrs is being used by a program that applies new seccomp-bpf filters onto itself -- if the filter deniesopenat2(2)then we would return that error rather than falling back to theO_PATHresolver. To resolve this issue, we have introduced more flexible fallback mechanisms and no longer cache the result ifopenat2(2)was successful (only if there was an error).
0.2.1 - 2025-11-03
やられたらやり返す。倍返しだ!
-
When using
ProcfsHandle::open_followon non-magic-link symlinks, libpathrs could fall victim to an overmount attack because we had incorrectly assumed that opening a symlink as a final component would be "atomic" (this is only true for magic-links, which was the primary usecase we had in mind for this API).We now try to use the safe procfs resolver even on symlinks to handle the "regular symlink case". Note that (due to a separate bug in
ProcfsHandle::open_followthat has also been fixed), privileged users would likely still get an error in this case.
ErrorandErrorKindnow have acan_retryhelper that can be used to make retry loops easier for callers.- libpathrs now has fairly comprehensive end-to-end tests for all our bindings (written in a language-agnostic way), to ensure correctness and uniformty when you use libpathrs, no matter which language you use.
- python bindings: fix
pathrs.procfsexamples in README. - go bindings: fix the internal
os.FileModetoS_IF*conversion to not auto-includeS_IFREGfor non-Mknodoperations (previously this would causeMkdirAllto error out). Root::create_filenow supportsO_TMPFILE.- Previously, trying to use
ProcfsHandle::open_followwith a path whose penultimate component was a symlink (i.e.,ProcfsHandle::open_follow(ProcfsBase::ProcRoot, "self/status")) would result in an error due to a mistake in how we handled looking up parent directories. This has been fixed, and this will now work the way you expect (though you should still useProcfsBase::ProcSelfinstead in the above example). ProcfsHandle::open_followwas missing the logic to temporarily allocate a non-subset=pidif the target does not exit. This ended up accidentally mitigating theProcfsHandle::open_followsecurity issue mentioned above (forfsopen(2)users trying to open symlinks inProcfsBase::ProcRoot-- note that onlyProcfsBase::ProcRootcontains such symlinks in the first place).- Quite a few
Rootoperations that required resolving the parent directory of the user-provided path could crash if passed/or return an unhelpful error when passed.. We now return a proper error in these cases.
-
The
openat2resolver will now return-EAGAINif the number ofopenat2retries is exceeded -- this allows higher-level users easily detect if an error is an indication they should retry (based on their own retry policy).In addition, the number of retries done has been bumped from
32to128based on some benchmarking which showed that32could fail up to 3% of the time but128would only fail ~0.1% of the time in the worst case scenario of an attacker that can saturate all cores withrename(2)operations.Users that need stronger resiliency guarantees can do their own additional retry loop on top of
libpathrsby checking the return value forEAGAIN. Please note that we would strongly recommend having some restriction to avoid denial-of-service attacks (such as a deadline -- for reference, our testing showed that even with >50k trials containing >200k operations a deadline of 1ms was never exceeded even in the most pessimistic attack scenario). -
The
O_PATHresolver forProcfsHandlewill now returnELOOPfor magic-links that look likefoo:[bar]in order to better matchopenat2(2)(examples includeanon_inode,nsfs,pipe, and other such special inodes). Previously we would just returnENOENT.
0.2.0 - 2025-10-17
You're gonna need a bigger boat.
Note
As of this release, the libpathrs repository has been moved to https://github.com/cyphar/libpathrs. Please update any references you have (though GitHub will redirect the old repository name to the new one).
In addition, the go-pathrs package has been moved to a vanity URL of
cyphar.com/go-pathrs. Please update your Go import paths accordingly.
Important
The license of this project has changed. Now, the entire project (including
all language bindings and examples) is licensed under the terms of the
Mozilla Public License version 2.0. Additionally, the Rust crate (and
cdylib) may also be used (at your option) under the terms of the GNU Lesser
General Public License version 3 (or later).
For more information, see COPYING.md and the "License"
section of the README.
The long-term plan is to restructure the project so that src/capi is a
separate crate that is only licensed as GNU LGPLv3+ and the Rust crate is
only licensed under as MPLv2, but for technical reasons this is difficult to
achieve at the moment. The primary purpose for dual-licensing is to try to
assuage possible concerns around the GNU LGPLv3 requirements to be able to
"recombine or relink the Application with a modified version of the Linked
Version to produce a modified Combined Work" in the context of the Rust build
system, while also allowing us to license the cdylib portion under the GNU
LGPLv3+.
- python bindings:
Root.creathas had itsfilemodeandflagsarguments swapped to match the argument order ofopenat2(andRoot.creat_raw). This also now makesfilemodehave a default value of0o644if unspecified. - Most of the C FFI functions have been renamed:
- Operations on a
Roothave been renamed to have apathrs_inroot_prefix. pathrs_root_openhas been renamed topathrs_open_root, to avoid confusion withpathrs_inroot_*functions and clarify what it is opening.- However,
libpathrs.sonow uses symbol versioning and so (mostly as a proof-of-concept) programs compiled against libpathrs 0.1 will continue to function with libpathrs 0.2.
- Operations on a
- python bindings:
Root.openhas been changed to be a wrapper ofpathrs_inroot_openinstead of being a wrapper around theRootconstructor. - All C FFI functions that return a file descriptor now set
O_CLOEXECby default. Previously some functions that tookO_*flags would only setO_CLOEXECif the user explicitly requested it, butO_CLOEXECis easy to unset on file descriptors and having it enabled is a more sane default. - The C API values of the
pathrs_proc_base_tenum (PATHRS_PROC_BASE_*) have different values, in order to supportProcfsBase::ProcPidpassing from C callers. Any binaries compiled with the old headers will need to be recompiled to avoid spurious behaviour.- This required a breaking change in the Go bindings for libpathrs.
ProcfsBaseis now an opaquestructtype rather than a simpleintwrapper -- this was necessary in order to add support forProcfsBase::ProcPidin the form of theProcBasePidhelper function.
- This required a breaking change in the Go bindings for libpathrs.
- go bindings: the Go module name for the libpathrs Go bindings has been
renamed to
cyphar.com/go-pathrs. This will cause build errors for existing users which used the old repository path, but can easily be fixed by updatinggo.modandgo.sumto use the new name. - go bindings: the procfs APIs have been moved to a
procfssubpackage, and several of the exported types and functions have changed names. We have not provided any compatibility aliases. - python bindings: the procfs APIs have been moved to a
procfssubmodule. We have not provided any compatibility aliases.
-
python bindings: add
Root.creat_rawto create a new file and wrap it in a rawWrappedFd(os opposed toRoot.creatwhich returns anos.fdopen). -
Root: it is now possible to open a file in one shot without having to do an intermediate
resolvestep withRoot::open_subpath. This can be more efficient in some scenarios (especially with the openat2-based resolver or for C FFI users where function calls are expensive) as it saves one file descriptor allocation and extra function calls. -
Error:
ErrorKindis now exported, allowing you to programmatically handle errors returned by libpathrs. This interface may change in the future (in particular,ErrorKind::OsErrormight change its representation oferrnovalues). -
capi: errors that are returned by libpathrs itself (such as invalid arguments being passed by users) will now contain a
saved_errnovalue that makes sense for the error type (soErrorKind::InvalidArgumentwill result in anEINVALvalue forsaved_errno). This will allow C users to have a nicer time handling errors programmatically. -
tests: we now have a large array of tests for verifying that the core lookup logic of libpathrs is race-safe against various attacks. This is no big surprise, given libpathrs's design, but we now have more extensive tests than
github.com/cyphar/filepath-securejoin. -
procfs: added
ProcfsBase::ProcPid(n)which is just shorthand when operating on a operating on a different process. This is also now supported by the C API (by just passing thepid_tinstead of a specialpathrs_proc_base_tvalue). -
procfs: we now make use of
/proc/thread-self/fdinfo'smnt_idfield to try to thwart bind-mount attacks on systems withoutSTATX_MNT_IDsupport.On systems with
openat2(2), this protection is effectively just as safe asSTATX_MNT_ID(which lets us lower the minimum recommended kernel version from Linux 5.8 to Linux 5.6). For older systems, this protection is not perfect, but is designed to be difficult for an attacker to bypass as consistently and easily as it would be without these protections.Note that it is still the case that post-6.8 kernels (
STATX_MNT_ID_UNIQUE) are still the most strongly recommended kernels to use. -
procfs:
ProcfsHandleis nowProcfsHandleRef<'static>, and it is now possible to construct borrowed versions ofProcfsHandleRef<'fd>and still use them. This is primarily intended for our C API, but Rust users can make use of it if you wish. It is possible we will move away from a type alias forProcfsHandlein the future. -
capi: All of that
pathrs_proc_*methods now have apathrs_proc_*atvariant which allows users to pass a file descriptor to use as the/prochandle (effectively acting as a C version ofProcfsHandleRef<'fd>). Only users that operate heavily on global procfs files are expected to make use of this API -- the regular API still lets you operate on global procfs files. Users can passPATHRS_PROC_DEFAULT_ROOTFD(-EBADF) as a file descriptor to use the cached API (the old API methods just do this internally). -
procfs: a new
ProcfsHandleBuilderbuilder has been added to the API, which allows users to construct an unmasked (i.e., no-subset=pid)ProcfsHandle.This should only be used sparingly and with great care to avoid leaks, but it allows some programs to amortise the cost of constructing a
procfshandle when doing a series of operations on global procfs files (such as configuring a large number of sysctls).We plan to add a few more configuration options to
ProcfsHandleBuilderin the future, butProcfsHandleBuilder::unmaskedwill always give you an unmasked version of/procregardless of any new features. -
procfs:
ProcfsHandleRefcan now be converted toOwnedFdwith.into_owned_fd()(if it is internally anOwnedFd) and borrowed asBorrowedFdwithAsFd::as_fd. Users should take great care when using the underlying file descriptor directly, as using it opens you up to all of the attacks that libpathrs protects you against. -
capi: add
pathrs_procfs_openmethod to create a newProcfsHandlewith a custom configuration (a-laProcfsHandleBuilder). As withProcfsHandleBuilder, most users do not need to use this.- python bindings:
ProcfsHandlewraps this new API, and you can construct customProcfsHandles withProcfsHandle.new(...).ProcfsHandle.cached()returns the cached globalProcfsHandle. The top-levelproc_*functions (which may be removed in future versions) are now just bound methods ofProcfsHandle.cached()and have been renamed to remove theproc_prefix (now that the procfs API lives in a separatepathrs.procfsmodule). - go bindings:
ProcfsHandlewraps this new API, and you can construct a customProcfsHandles withOpenProcRoot(calling this with no arguments will produce the global cached handle if the handle is being cached). The oldProc*functions have been removed entirely.
- python bindings:
-
capi: We now use symbol versioning for
libpathrs.so, which should avoid concerns about future API breakages. I have tested all of the key aspects of this new symbol versioning setup and it seems Rust provides everything necessary (when testing this last year, I was unable to get backward-compatibity working).
-
procfs: the caching strategy for the internal procfs handle has been adjusted, and the public
GLOBAL_PROCFS_HANDLEhas been removed.The initial plan was to remove caching entirely, as there is a risk of leaked long-lived file descriptors leading to attacks like CVE-2024-21626. However, we found that the performance impact could be quite noticeable (
fsconfig(2)in particular is somewhat heavy in practice if you do it for every VFS operation very frequently). (#203)The current approach is for
ProcfsHandle::newto opportunistically cache the underlying file descriptor if it is considered relatively safe to cache (i.e., it issubset=pidand is a detached mount object, which should stop host breakouts even if a privileged container attacker snoops on the file descriptor). Programs need not be aware of the caching behaviour, though programs which need to change security contexts should still use common-sense protections likePR_SET_DUMPABLE. (#249) -
api: many of the generic type parameters have been replaced with
impl Traitarguments, in order to make using libpathrs a bit more ergonomic. Unless you were specifically setting the generic types with::<>syntax, this change should not affect you. -
syscalls: switch to rustix for most of our syscall wrappers to simplify how much code we have for wrapper raw syscalls. This also lets us build on musl-based targets because musl doesn't support some of the syscalls we need.
There are some outstanding issues with rustix that make this switch a little uglier than necessary (rustix#1186, rustix#1187), but this is a net improvement overall.
- multiarch: we now build correctly on 32-bit architectures as well as
architectures that have unsigned char. We also have CI jobs that verify that
builds work on a fairly large number of architectures (all relevant tier-1
and tier-2-with-host-tools architectures). If there is an architecture you
would like us to add to the build matrix and it is well-supported by
rustc, feel free to open an issue or PR! Handle::reopenwill now return an error if you attempt to reopen a handle to a symlink (such as one created withRoot::resolve_nofollow). Previously, you would get various errors and unexpected behaviour. If you wish to make anO_PATH|O_NOFOLLOWcopy of a symlink handle, you can simply usetry_clone(i.e.dup(2)the file descriptor).Handle::reopen(O_NOFOLLOW)will now return reasonable results. Previously, it would return-ELOOPin most cases and in other cases it would return unexpected results because theO_NOFOLLOWwould have an effect on the magic-link used internally byHandle::reopen.Root::mkdir_allwill no longer return-EEXISTif another process tried to doRoot::mkdir_allat the same time, instead the race winner's directory will be used by both processes. See opencontainers/runc#4543 for more details.Root::remove_allwill now handle missing paths that disappear from underneath it more gracefully, ensuring that multipleRoot::remove_alloperations run on the same directory tree will all succeed without errors. The need for this is similar to the need forRoot::mkdir_allto handle such cases.- opath resolver: in some cases with trailing symlinks in the symlink stack
(i.e. for partial lookups caused by
Root::mkdir_all) we would not correctly handle leading..components, leading to safety errors when libpathrs thought that the symlink stack had been corrupted. - openat2 resolver: always return a hard
SafetyViolationif we encounter one during partial lookups to match the opath resolver behaviour and to avoid confusion by users (it is theoretically safe to fall back from aSafetyViolationduring a partial lookup, but it's better to be safe here). - The error handling for
Root::*operations that require splitting paths into a parent directory and single basename component (such asRoot::create) has now been unified and cases like trailing/.and/..will now always result inErrorKind::InvalidArgument. - Trailing slash behaviour (i.e. where a user specifies a trailing slash in a
path passed to libpathrs) throughout libpathrs has been improved to better
match the kernel APIs (where possible) or otherwise has been made consistent
and intentional:
Root::createwill always error out with anInvalidArgumentfor the target path unless the inode being created is anInodeType::Directory, in which case the trailing slash will be ignored (to match the behaviour ofmkdir(2)on Linux). Hard links with a trailing slash will also produce an error, as hard-links to directories are also forbidden on Unix.Root::create_filewill always error out with anInvalidArgument.Root::remove_allandRoot::remove_dirwill ignore trailing slashes, whileRoot::remove_filewill always fail withENOTDIR. The reason forRoot::remove_allalways succeeding is that it matches the behaviour of Go'sos.RemoveAllandrm -rf, as well as being impractical for us to determine if the target to be deleted is a directory in a race-free way.Root::renamematchesrenameat2(2)'s behaviour to the best of our ability.- Trailing slashes on the source path are only allowed if the source is
actually a directory (otherwise you get
ENOTDIR). - For
RENAME_EXCHANGE, the target path may only have trailing slashes if it is actually a directory (same as the source path). Otherwise, if the target path has a trailing slash then the source path must be a directory (otherwise you getENOTDIR).
- Trailing slashes on the source path are only allowed if the source is
actually a directory (otherwise you get
- opath resolver: we now return
ELOOPwhen we run into a symlink that came from mount with theMS_NOSYMFOLLOWset, to match the behaviour ofopenat2. - openat2: we now set
O_NOCTTYandO_NOFOLLOWmore aggressively when doingopenat2operations, to avoid theoretical DoS attacks (these were set foropenatbut we missed including them foropenat2).
0.1.3 - 2024-10-10
自動化って物は試しとすればいい物だ
- gha: our Rust crate and Python bindings are now uploaded automatically from a GitHub action when a tag is pushed.
- syscalls: the pretty-printing of
openat2errors now gives a string description of the flags passed rather that just a hex value (to match other syscalls). - python bindings: restrict how our methods and functions can be called using
/and*to reduce the possibility of future breakages if we rename or re-order some of our arguments.
0.1.2 - 2024-10-09
蛇のように賢く、鳩のように素直でありなさい
- python bindings: add a minimal README for PyPI.
- python bindings: actually export
PROC_ROOT. - python bindings: add type annotations and
py.typedto allow for downstream users to get proper type annotations for the API.
0.1.1 - 2024-10-01
頒布と聞いたら蛇に睨まれた蛙になるよ
-
procfs: add support for operating on files in the
/procroot (or other processes) withProcfsBase::ProcRoot.While the cached file descriptor shouldn't leak into containers (container runtimes know to set
PR_SET_DUMPABLE, and our cached file descriptor isO_CLOEXEC), I felt a little uncomfortable about having a global unmasked procfs handle sitting around inlibpathrs. So, in order to avoid making a file descriptor leak by alibpathrsuser catastrophic,libpathrswill always try to use a "limited" procfs handle as the global cached handle (which is much safer to leak into a container) and for operations onProcfsBase::ProcRoot, a temporary new "unrestricted" procfs handle is created just for that operartion. This is more expensive, but it avoids a potential leak turning into a breakout or other nightmare scenario. -
python bindings: The
cffibuild script is now a little easier to use for distributions that want to build the python bindings at the same time as the main library. After compiling the library, set thePATHRS_SRC_ROOTenvironment variable to the root of thelibpathrssource directory. This will instruct thecffibuild script (when called fromsetup.pyorpython3 -m build) to link against the library built in the source directory rather than using system libraries. As long as you install the same library later, this should cause no issues.Standard wheel builds still work the same way, so users that want to link against the system libraries don't need to make any changes.
-
Root::mkdir_allno longer does strict verification that directories created bymkdir_all"look right" after opening each component. These checks didn't protect against any practical attack (since an attacker could just get us to use a directory by creating it beforeRoot::mkdir_alland we would happily use it) and just resulted in spurious errors when dealing with complicated filesystem configurations (POSIX ACLs, weird filesystem-specific mount options). (#71) -
capi: Passing invalid
pathrs_proc_base_tvalues topathrs_proc_*will now return an error rather than resulting in Undefined Behaviour™.
0.1.0 - 2024-09-14
負けたくないことに理由って要る?
-
libpathrs now has an official MSRV of 1.63, which is verified by our CI. The MSRV was chosen because it's the Rust version in Debian stable and it has
io_safetywhich is one of the last bits we absolutely need. -
libpathrs now has a "safe procfs resolver" implementation that verifies all of our operations on
/procare done safely (including usingfsopen(2)oropen_tree(2)to create a private/procto protect against race attacks).This is mainly motivated by issues like CVE-2019-16884 and CVE-2019-19921, where an attacker could configure a malicious mount table such that naively doing
/procoperations could result in security issues. While there are limited things you can do in such a scenario, it is far more preferable to be able to detect these kinds of attacks and at least error out if there is a malicious/proc.This is based on similar work I did in filepath-securejoin.
- This API is also exposed to users through the Rust and C FFI because this is something a fair number of system tools (such as container runtimes) need.
-
root: new
Rootmethods:readlinkandresolve_nofollowto allow users to operate on symlinks directly (though it is still unsafe to use the returned path for lookups!).remove_allso that Go users can switch fromos.RemoveAll(though Go'sos.RemoveAllis safe against races since Go 1.21.11 and Go 1.22.4).mkdir_allso that Go users can switch fromos.MkdirAll. This is based on similar work done in filepath-securejoin.
-
root: The method for configuring the resolver has changed to be more akin to a getter-setter style. This allows for more ergonomic usage (see the
RootRef::with_resolver_flagsexamples) and also lets us avoid exposing internal types needlessly.As part of this change, the ability to choose the resolver backend was removed (because the C API also no longer supports it). This will probably be re-added in the future, but for now it seems best to not add extra APIs that aren't necessary until someone asks.
-
opath resolver: We now emulate
fs.protected_symlinkswhen resolving symlinks using the emulated opath resolver. This is only done iffs.protected_symlinksis enabled on the system (to mirror the behaviour ofopenat2). -
tests: Add a large number of integration tests, mainly based on the test suite in filepath-securejoin. This test suite tests all of the Rust code and the C FFI code from within Rust, giving us ~89% test coverage.
-
tests: Add some smoke tests using our bindings to ensure that you can actually build with them and run a basic
catprogram. In the future we will do proper e2e testing with all of the bindings. -
packaging: Add an autoconf-like
install.shscript that generates apkg-configspecification for libpathrs. This should help distributions package libpathrs.
- Handling of
//and trailing slashes has been fixed to better match what users expect and what the kernel does. - opath resolver: Use reference counting to avoid needlessly cloning files internally when doing lookups.
- Remove the
try_clone_hotfixworkaround, since the Rust stdlib patch was merged several years ago. - cffi: Building the C API is now optional, so Rust crates won't contain any of the C FFI code and we only build the C FFI crate types manually in the makefile. This also lets us remove some dependencies and other annoying things in the Rust crate (since those things are only needed for the C API).
- python bindings: Switch to setuptools to allow for a proper Python package
install. This also includes some reworking of the layout to avoid leaking
stuff to users that just do
import pathrs.
-
cffi: Redesign the entire API to be file descriptor based, removing the need for complicated freeing logic and matching what most kernel APIs actually look like. While there is a risk that users would operate on file descriptors themselves, the benefits of a pure-fd-based API outweigh those issues (and languages with bindings like Python and Go can easily wrap the file descriptor to provide helper methods and avoid this mistake by users).
Aside from making C users happier, this makes writing bindings much simpler because every language has native support for handling the freeing of file objects (Go in particular has
*os.Filewhich would be too difficult to emulate outside of the stdlib because of it's uniqueClosehandling).- Unfortunately, this API change also removed some information from the C API
because it was too difficult to deal with:
- Backtraces are no longer provided to the C API. There is no plan to re-add them because they complicate the C API a fair bit and it turns out that it's basically impossible to graft backtraces to languages that have native backtrace support (Go and Python) so providing this information has no real benefit to anyone.
- The configuration API has been removed for now. In the future we will probably want to re-add it, but figuring out a nice API for this is left for a future (pre-1.0) release. In practice, the default settings are the best settings to use for most people anyway.
- Unfortunately, this API change also removed some information from the C API
because it was too difficult to deal with:
-
bindings: All of the bindings were rewritten to use the new API.
-
rust: Rework libpathrs to use the (stabilised in Rust 1.63)
io_safetyfeatures. This lets us avoid possible "use after free" issues with file descriptors that were closed by accident.This required the addition of
HandleRefandRootRefto wrapBorrowedFd(this is needed for the C API, but is almost certainly useful to other folks). Unfortunately we can't implementDerefso all of the methods need to be duplicated for the new types. -
Split
Root::removeintoRoot::remove_file(unlink) andRoot::remove_dir(rmdir) so we don't need to do the retry loop anymore. Some users care about what kind of inode they're removing, and if a user really wants to nuke a path they would want to useRoot::remove_allanyway because the oldRoot::removewould not remove non-empty directories. -
Switch from
snafutothiserrorfor generating our error impls. One upshot of this change is that our errors are more opaque to Rust users. However, this change resulted in us removing backtraces from our errors (becausethiserroronly supportsstd::backtrace::Backtracewhich was stabilised after our MSRV, and even then it is somewhat limited until some more bits ofstd::backtrace::Backtraceare stabilised). We do plan to re-add backtraces but they probably aren't strictly needed by most library users.In the worst case we could write our own handling of backtraces using the
backtracecrate, but I'd like to see a user actually ask for that before sitting down to work on it.
0.0.2 - 2020-02-15
- bindings: Go bindings (thanks to Maxim Zhiburt for the initial version!).
- bindings: Add support for converting to/from file descriptors.
- Update to the newest
openat2API (which now uses extensible structs).
- cffi: Make all objects thread-safe so multi-threaded programs don't hit data races.
- cffi: Major rework of the CPointer locking design to split the single Mutex (used for both the inner type and errors) into two separate locks. As the inner value is almost always read, this should massively reduce lock contention in multi-threaded programs.
- cffi:
pathrs_from_fdnow clones the passed file descriptor. Some GC'd languages are annoying to deal with when a file descriptor's ownership is meant to be transferred outside of the program.
0.0.1 - 2020-01-05
- docs: Fix rustdoc build errors.
0.0.0 - 2020-01-05 [YANKED]
Initial release.
(This release was yanked because the rust docs were broken.)
- Initial implementation of libpathrs, with most of the major functionality
we need implemented:
Root:openat2- andO_PATH-based resolvers.resolvecreateandcreate_fileremoverename
Handle:reopen
- C FFI.
- Python bindings.