pathrs 0.2.3

C-friendly API to make path resolution safer on Linux.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
# Changelog #
All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).

## [Unreleased] ##

## [0.2.3] - 2026-01-29 ##

> この閃きを俺は信じる!

### Fixed ###
- 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.so` when 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.so` with 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 requires `lld` to compile,
    even on older Rust versions**. Rust 1.90 switched to `lld` by default for
    x86 platforms.

## [0.2.2] - 2025-11-25 ##

> 貴様ら全員刀の錆にしてやるぜ。

### Changed ###
- Add `_test_race` feature to allow you to opt-out of the race tests when
  testing with `cargo test` (which resolves the spurious `EAGAIN` failures you
  get with naive `cargo test` and `cargo nextest` runs, along with making the
  whole test run ~50x faster).

### Fixed ###
- Previously, `cargo test` runs would fail with errors due to the unique
  execution environment that `cargo nextest` provides. We have resolved those
  differences and now have smoke tests in our CI to make sure that a naive
  `cargo test` run will succeed.
- libpathrs will now apply the same `openat2` retry logic for any usage of
  `openat2` (for scoped lookups), to further improve resiliency on busy
  systems.
- Our logic for deciding whether to use `openat2(2)` or fallback to an `O_PATH`
  resolver would cache the result to avoid doing needless test runs of
  `openat2(2)`. However, this causes issues when libpathrs is being used by a
  program that applies new seccomp-bpf filters onto itself -- if the filter
  denies `openat2(2)` then we would return that error rather than falling back
  to the `O_PATH` resolver. To resolve this issue, we have introduced more
  flexible fallback mechanisms and no longer cache the result if `openat2(2)`
  was successful (only if there was an error).

## [0.2.1] - 2025-11-03 ##

> やられたらやり返す。倍返しだ!

### Security ###
- When using `ProcfsHandle::open_follow` on **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_follow` that has also been fixed), privileged users would
  likely still get an error in this case.

### Added ###
- `Error` and `ErrorKind` now have a `can_retry` helper 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.

### Fixed ###
- python bindings: fix `pathrs.procfs` examples in README.
- go bindings: fix the internal `os.FileMode` to `S_IF*` conversion to not
  auto-include `S_IFREG` for non-`Mknod` operations (previously this would
  cause `MkdirAll` to error out).
- `Root::create_file` now supports `O_TMPFILE`.
- Previously, trying to use `ProcfsHandle::open_follow` with 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 use `ProcfsBase::ProcSelf` instead in the above
  example).
- `ProcfsHandle::open_follow` was missing the logic to temporarily allocate a
  non-`subset=pid` if the target does not exit. This ended up accidentally
  mitigating the `ProcfsHandle::open_follow` security issue mentioned above
  (for `fsopen(2)` users trying to open symlinks in `ProcfsBase::ProcRoot` --
  note that only `ProcfsBase::ProcRoot` contains such symlinks in the first
  place).
- Quite a few `Root` operations 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.

### Changed ###
- The `openat2` resolver will now return `-EAGAIN` if the number of `openat2`
  retries 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 `32` to `128`
  based on some benchmarking which showed that `32` could fail up to 3% of the
  time but `128` would only fail ~0.1% of the time in the worst case scenario
  of an attacker that can saturate all cores with `rename(2)` operations.

  Users that need stronger resiliency guarantees can do their own additional
  retry loop on top of `libpathrs` by checking the return value for `EAGAIN`.
  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_PATH` resolver for `ProcfsHandle` will now return `ELOOP` for
  magic-links that look like `foo:[bar]` in order to better match `openat2(2)`
  (examples include `anon_inode`, `nsfs`, `pipe`, and other such special
  inodes). Previously we would just return `ENOENT`.

## [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`]./COPYING.md and the "License"
> section of the [README]./README.md.
>
> 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+.

### Breaking ###
- python bindings: `Root.creat` has had its `filemode` and `flags` arguments
  swapped to match the argument order of `openat2` (and `Root.creat_raw`). This
  also now makes `filemode` have a default value of `0o644` if unspecified.
- Most of the C FFI functions have been renamed:
  - Operations on a `Root` have been renamed to have a `pathrs_inroot_` prefix.
  - `pathrs_root_open` has been renamed to `pathrs_open_root`, to avoid
    confusion with `pathrs_inroot_*` functions and clarify what it is opening.
  - However, `libpathrs.so` now 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.
- python bindings: `Root.open` has been changed to be a wrapper of
  `pathrs_inroot_open` instead of being a wrapper around the `Root`
  constructor.
- All C FFI functions that return a file descriptor now set `O_CLOEXEC` by
  default. Previously some functions that took `O_*` flags would only set
  `O_CLOEXEC` if the user explicitly requested it, but `O_CLOEXEC` is easy to
  unset on file descriptors and having it enabled is a more sane default.
- The C API values of the `pathrs_proc_base_t` enum (`PATHRS_PROC_BASE_*`) have
  different values, in order to support `ProcfsBase::ProcPid` passing 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.
    `ProcfsBase` is now an opaque `struct` type rather than a simple `int`
    wrapper -- this was necessary in order to add support for
    `ProcfsBase::ProcPid` in the form of the `ProcBasePid` helper function.
- 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 updating
  `go.mod` and `go.sum` to use the new name.
- go bindings: the procfs APIs have been moved to a `procfs` subpackage, 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 `procfs` submodule. We
  have not provided any compatibility aliases.

### Added ###
- python bindings: add `Root.creat_raw` to create a new file and wrap it in a
  raw `WrappedFd` (os opposed to `Root.creat` which returns an `os.fdopen`).
- Root: it is now possible to open a file in one shot without having to do an
  intermediate `resolve` step with `Root::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: `ErrorKind` is now exported, allowing you to programmatically handle
  errors returned by libpathrs. This interface may change in the future (in
  particular, `ErrorKind::OsError` might change its representation of `errno`
  values).
- capi: errors that are returned by libpathrs itself (such as invalid arguments
  being passed by users) will now contain a `saved_errno` value that makes
  sense for the error type (so `ErrorKind::InvalidArgument` will result in an
  `EINVAL` value for `saved_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 the `pid_t` instead of a special `pathrs_proc_base_t`
  value).
- procfs: we now make use of `/proc/thread-self/fdinfo`'s `mnt_id` field to try
  to thwart bind-mount attacks on systems without `STATX_MNT_ID` support.

  On systems with `openat2(2)`, this protection is effectively just as safe as
  `STATX_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: `ProcfsHandle` is now `ProcfsHandleRef<'static>`, and it is now
  possible to construct borrowed versions of `ProcfsHandleRef<'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 for
  `ProcfsHandle` in the future.
- capi: All of that `pathrs_proc_*` methods now have a `pathrs_proc_*at`
  variant which allows users to pass a file descriptor to use as the `/proc`
  handle (effectively acting as a C version of `ProcfsHandleRef<'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 pass `PATHRS_PROC_DEFAULT_ROOTFD` (`-EBADF`) as a file descriptor
  to use the cached API (the old API methods just do this internally).
- procfs: a new `ProcfsHandleBuilder` builder 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 `procfs` handle
  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 `ProcfsHandleBuilder` in
  the future, but `ProcfsHandleBuilder::unmasked` will always give you an
  unmasked version of `/proc` regardless of any new features.
- procfs: `ProcfsHandleRef` can now be converted to `OwnedFd` with
  `.into_owned_fd()` (if it is internally an `OwnedFd`) and borrowed as
  `BorrowedFd` with `AsFd::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_open` method to create a new `ProcfsHandle` with a
  custom configuration (a-la `ProcfsHandleBuilder`). As with
  `ProcfsHandleBuilder`, most users do not need to use this.
  - python bindings: `ProcfsHandle` wraps this new API, and you can construct
    custom `ProcfsHandle`s with `ProcfsHandle.new(...)`.
    `ProcfsHandle.cached()` returns the cached global `ProcfsHandle`. The
    top-level `proc_*` functions (which may be removed in future versions) are
    now just bound methods of `ProcfsHandle.cached()` and have been renamed to
    remove the `proc_` prefix (now that the procfs API lives in a separate
    `pathrs.procfs` module).
  - go bindings: `ProcfsHandle` wraps this new API, and you can construct a
    custom `ProcfsHandle`s with `OpenProcRoot` (calling this with no arguments
    will produce the global cached handle if the handle is being cached). The
    old `Proc*` functions have been removed entirely.
- 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).

### Changed ###
- procfs: the caching strategy for the internal procfs handle has been
  adjusted, and the public `GLOBAL_PROCFS_HANDLE` has 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::new` to opportunistically cache
  the underlying file descriptor if it is considered relatively safe to cache
  (i.e., it is `subset=pid` and 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 like `PR_SET_DUMPABLE`. (#249)
- api: many of the generic type parameters have been replaced with `impl Trait`
  arguments, 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.

### Fixes ###
- 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::reopen` will now return an error if you attempt to reopen a handle
  to a symlink (such as one created with `Root::resolve_nofollow`). Previously,
  you would get various errors and unexpected behaviour. If you wish to make an
  `O_PATH|O_NOFOLLOW` copy of a symlink handle, you can simply use `try_clone`
  (i.e. `dup(2)` the file descriptor).
- `Handle::reopen(O_NOFOLLOW)` will now return reasonable results. Previously,
  it would return `-ELOOP` in most cases and in other cases it would return
  unexpected results because the `O_NOFOLLOW` would have an effect on the
  magic-link used internally by `Handle::reopen`.
- `Root::mkdir_all` will no longer return `-EEXIST` if another process tried to
  do `Root::mkdir_all` at the same time, instead the race winner's directory
  will be used by both processes. See [opencontainers/runc#4543][] for more
  details.
- `Root::remove_all` will now handle missing paths that disappear from
  underneath it more gracefully, ensuring that multiple `Root::remove_all`
  operations run on the same directory tree will all succeed without errors.
  The need for this is similar to the need for `Root::mkdir_all` to 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 `SafetyViolation` if 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 a
  `SafetyViolation` during 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 as `Root::create`) has
  now been unified and cases like trailing `/.` and `/..` will now always
  result in `ErrorKind::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::create` will always error out with an `InvalidArgument` for the
    target path unless the inode being created is an `InodeType::Directory`, in
    which case the trailing slash will be ignored (to match the behaviour of
    `mkdir(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_file` will always error out with an `InvalidArgument`.
  - `Root::remove_all` and `Root::remove_dir` will ignore trailing slashes,
    while `Root::remove_file` will always fail with `ENOTDIR`. The reason for
    `Root::remove_all` always succeeding is that it matches the behaviour of
    Go's `os.RemoveAll` and `rm -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::rename` matches `renameat2(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 get `ENOTDIR`).
- opath resolver: we now return `ELOOP` when we run into a symlink that came
  from mount with the `MS_NOSYMFOLLOW` set, to match the behaviour of `openat2`.
- openat2: we now set `O_NOCTTY` and `O_NOFOLLOW` more aggressively when doing
  `openat2` operations, to avoid theoretical DoS attacks (these were set for
  `openat` but we missed including them for `openat2`).

[CVE-2024-21626]: https://github.com/opencontainers/runc/security/advisories/GHSA-xr7r-f8xq-vfvv
[rustix#1186]: https://github.com/bytecodealliance/rustix/issues/1186
[rustix#1187]: https://github.com/bytecodealliance/rustix/issues/1187
[opencontainers/runc#4543]: https://github.com/opencontainers/runc/issues/4543

## [0.1.3] - 2024-10-10 ##

> 自動化って物は試しとすればいい物だ

### Changed ###
- gha: our Rust crate and Python bindings are now uploaded automatically from a
  GitHub action when a tag is pushed.

### Fixes ###
- syscalls: the pretty-printing of `openat2` errors 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 ##

> 蛇のように賢く、鳩のように素直でありなさい

### Fixes ###
- python bindings: add a minimal README for PyPI.
- python bindings: actually export `PROC_ROOT`.
- python bindings: add type annotations and `py.typed` to allow for downstream
  users to get proper type annotations for the API.

## [0.1.1] - 2024-10-01 ##

> 頒布と聞いたら蛇に睨まれた蛙になるよ

### Added ###
- procfs: add support for operating on files in the `/proc` root (or other
  processes) with `ProcfsBase::ProcRoot`.

  While the cached file descriptor shouldn't leak into containers (container
  runtimes know to set `PR_SET_DUMPABLE`, and our cached file descriptor is
  `O_CLOEXEC`), I felt a little uncomfortable about having a global unmasked
  procfs handle sitting around in `libpathrs`. So, in order to avoid making a
  file descriptor leak by a `libpathrs` user catastrophic, `libpathrs` will
  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 on
  `ProcfsBase::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 `cffi` build 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 the `PATHRS_SRC_ROOT`
  environment variable to the root of the `libpathrs` source directory. This
  will instruct the `cffi` build script (when called from `setup.py` or
  `python3 -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.

### Fixed ###
- `Root::mkdir_all` no longer does strict verification that directories created
  by `mkdir_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 before `Root::mkdir_all` and 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_t` values to `pathrs_proc_*` will now
  return an error rather than resulting in Undefined Behaviour™.

## [0.1.0] - 2024-09-14 ##

> 負けたくないことに理由って要る?

### Added ###
- 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_safety` which is one of the last bits we absolutely need.

- libpathrs now has a "safe procfs resolver" implementation that verifies all
  of our operations on `/proc` are done safely (including using `fsopen(2)` or
  `open_tree(2)` to create a private `/proc` to 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 `/proc` operations 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 `Root` methods:
  - `readlink` and `resolve_nofollow` to allow users to operate on symlinks
    directly (though it is still unsafe to use the returned path for lookups!).
  - `remove_all` so that Go users can switch from `os.RemoveAll` (though [Go's
    `os.RemoveAll` is safe against races since Go 1.21.11 and Go
    1.22.4][go-52745]).
  - `mkdir_all` so that Go users can switch from `os.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_flags` examples) 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_symlinks` when resolving
  symlinks using the emulated opath resolver. This is only done if
  `fs.protected_symlinks` is enabled on the system (to mirror the behaviour of
  `openat2`).

- 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 `cat` program. In the future we will
  do proper e2e testing with all of the bindings.

- packaging: Add an autoconf-like `install.sh` script that generates a
  `pkg-config` specification for libpathrs. This should help distributions
  package libpathrs.

[CVE-2019-16884]: https://nvd.nist.gov/vuln/detail/CVE-2019-16884
[CVE-2019-19921]: https://nvd.nist.gov/vuln/detail/CVE-2019-19921
[filepath-securejoin]: https://github.com/cyphar/filepath-securejoin
[go-52745]: https://github.com/golang/go/issues/52745

### Fixed ###
- 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_hotfix` workaround, 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`.

### Changed ###
- 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.File` which would be too difficult to
  emulate outside of the stdlib because of it's unique `Close` handling).

  - 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.

- bindings: All of the bindings were rewritten to use the new API.

- rust: Rework libpathrs to use the (stabilised in Rust 1.63) `io_safety`
  features. This lets us avoid possible "use after free" issues with file
  descriptors that were closed by accident.

  This required the addition of `HandleRef` and `RootRef` to wrap `BorrowedFd`
  (this is needed for the C API, but is almost certainly useful to other
  folks). Unfortunately we can't implement `Deref` so all of the methods need
  to be duplicated for the new types.

- Split `Root::remove` into `Root::remove_file` (`unlink`) and
  `Root::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 use `Root::remove_all` anyway
  because the old `Root::remove` would not remove non-empty directories.

- Switch from `snafu` to `thiserror` for 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 (because
  `thiserror` only supports `std::backtrace::Backtrace` which was stabilised
  after our MSRV, and even then it is somewhat limited until some more bits of
  `std::backtrace::Backtrace` are 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
  `backtrace` crate, 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 ##

### Added ###
- bindings: Go bindings (thanks to Maxim Zhiburt for the initial version!).
- bindings: Add support for converting to/from file descriptors.

### Fixed ###
- Update to the newest `openat2` API (which now uses extensible structs).

### Changed ###
- 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_fd` now 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 ##

### Fixed ###
- docs: Fix rustdoc build errors.

## [0.0.0] - 2020-01-05 `[YANKED]` ##

Initial release.

(This release was yanked because the rust docs were broken.)

### Added ###
- Initial implementation of libpathrs, with most of the major functionality
  we need implemented:
  - `Root`:
    - `openat2`- and `O_PATH`-based resolvers.
    - `resolve`
    - `create` and `create_file`
    - `remove`
    - `rename`
  - `Handle`:
    - `reopen`
  - C FFI.
  - Python bindings.

[Unreleased]: https://github.com/cyphar/libpathrs/compare/v0.2.3...HEAD
[0.2.3]: https://github.com/cyphar/libpathrs/compare/v0.2.2...v0.2.3
[0.2.2]: https://github.com/cyphar/libpathrs/compare/v0.2.1...v0.2.2
[0.2.1]: https://github.com/cyphar/libpathrs/compare/v0.2.0...v0.2.1
[0.2.0]: https://github.com/cyphar/libpathrs/compare/v0.1.3...v0.2.0
[0.1.3]: https://github.com/cyphar/libpathrs/compare/v0.1.2...v0.1.3
[0.1.2]: https://github.com/cyphar/libpathrs/compare/v0.1.1...v0.1.2
[0.1.1]: https://github.com/cyphar/libpathrs/compare/v0.1.0...v0.1.1
[0.1.0]: https://github.com/cyphar/libpathrs/compare/v0.0.2...v0.1.0
[0.0.2]: https://github.com/cyphar/libpathrs/compare/v0.0.1...v0.0.2
[0.0.1]: https://github.com/cyphar/libpathrs/compare/v0.0.0...v0.0.1
[0.0.0]: https://github.com/cyphar/libpathrs/commits/v0.0.0/