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
//! What:
//! Two more Linux primitives: clone a whole file's data blocks into another file (`FICLONE`), and swap two paths
//! atomically (`renameat2` with `RENAME_EXCHANGE`).
//! Why:
//! A reflink copy shares the data blocks of a file instead of copying them (btrfs, XFS with reflink, and files inside
//! one overlayfs whose layers share such a filesystem), and an exchange swaps a directory with its replacement in one
//! step, so no process ever sees the path missing. Both fail with a typed error the caller falls through on:
//! `FICLONE` answers `EXDEV` across filesystems, `EOPNOTSUPP` or `EINVAL` where the filesystem has no reflinks; the
//! exchange answers `EINVAL` or `EXDEV` where the filesystem cannot swap. The ladder of
//! `doc/planning/cli-git-btrfs-worktree.md` uses the clone as its second rung.
//!
//! In TS you'd write (pseudocode):
//! ```ts
//! await copyFile(source, destination, constants.COPYFILE_FICLONE_FORCE);
//! await renameExchange(first, second); // no Node API; the kernel call is renameat2(RENAME_EXCHANGE)
//! ```
use crate;
use File;
use AsRawFd;
use Path;
/// What:
/// Clone the whole content of `source` into `destination` (`FICLONE`).
/// `&File` borrows an open file (sibling `&Path` names one that is not open).
/// Why:
/// The destination must be open for writing and empty or about to be replaced entirely; the kernel makes it share
/// the source's data blocks. `path` is only used to word the error.
///
/// In TS you'd write (pseudocode):
/// ```ts
/// await copyFile(sourcePath, destinationPath, constants.COPYFILE_FICLONE_FORCE);
/// ```
/// What:
/// Swap two existing paths atomically (`renameat2(AT_FDCWD, first, AT_FDCWD, second, RENAME_EXCHANGE)`).
/// Why:
/// After the call `first` names what `second` named and the other way round, with no moment at which either path is
/// missing. This is what `mv --exchange` runs, so the migration needs no `mv`. Both paths must exist and be on one
/// filesystem.
///
/// In TS you'd write (pseudocode):
/// ```ts
/// await renameExchange(first, second); // atomic swap
/// ```
/// Clone and exchange controls stay out of the release executable.