diff options
-rw-r--r-- | Makefile | 2 | ||||
-rwxr-xr-x | src/ficlonerange.py | 63 |
2 files changed, 64 insertions, 1 deletions
@@ -49,7 +49,7 @@ btrfs-send-root.sh var.sh grub-efi.sh keygen.sh initrd.sh qemu.sh | |||
49 | dnsmasq-dhcp-script.sh samizdat-password-agent samizdat-gpg-agent publish-ip.sh \ | 49 | dnsmasq-dhcp-script.sh samizdat-password-agent samizdat-gpg-agent publish-ip.sh \ |
50 | selfstrap samizdat-daily-snapshot-root samizdat-diff-root kiki-export-stdout \ | 50 | selfstrap samizdat-daily-snapshot-root samizdat-diff-root kiki-export-stdout \ |
51 | kiki-import-stdin store-child-permanently git-ll-remote usb \ | 51 | kiki-import-stdin store-child-permanently git-ll-remote usb \ |
52 | hostname.cryptonomic.net partvi ${dyndns_progs} | 52 | hostname.cryptonomic.net partvi ficlonerange.py ${dyndns_progs} |
53 | 53 | ||
54 | bin_programs=$(addprefix src/, $(src_bin_programs)) samizdat-paths.sh ${cc_files} ${btrfs_utils} | 54 | bin_programs=$(addprefix src/, $(src_bin_programs)) samizdat-paths.sh ${cc_files} ${btrfs_utils} |
55 | 55 | ||
diff --git a/src/ficlonerange.py b/src/ficlonerange.py new file mode 100755 index 0000000..8df96ad --- /dev/null +++ b/src/ficlonerange.py | |||
@@ -0,0 +1,63 @@ | |||
1 | #!/usr/bin/python3 | ||
2 | |||
3 | import sys | ||
4 | import os | ||
5 | from fcntl import ioctl | ||
6 | import struct | ||
7 | import argparse | ||
8 | import errno | ||
9 | import textwrap | ||
10 | |||
11 | def fail(message): | ||
12 | print(textwrap.fill("FICLONERANGE: " + message)) | ||
13 | sys.exit(3) | ||
14 | |||
15 | FICLONERANGE = 0x4020940d | ||
16 | |||
17 | def struct_file_clone_range(src_fd, src_offset, src_length, dst_offset): | ||
18 | return struct.pack("qQQQ", src_fd, src_offset, src_length, dst_offset) | ||
19 | |||
20 | parser = argparse.ArgumentParser(description="Reflink bytes from source file to destination. " | ||
21 | "If no optional parameters are provided, append contents of src to dest. See ioctl_ficlonerange(2) for details.") | ||
22 | parser.add_argument("-s", type=int, default=0, help="Source file offset in bytes (default: 0)") | ||
23 | parser.add_argument("-l", type=int, default=0, help="Number of bytes to reflink (default: all data)") | ||
24 | parser.add_argument("-d", type=int, help="Destination file offset in bytes (default: end of file)") | ||
25 | parser.add_argument("src", help="Source filename") | ||
26 | parser.add_argument("dest", help="Destination filename") | ||
27 | args = parser.parse_args() | ||
28 | |||
29 | src_fd = os.open(args.src, os.O_RDONLY) | ||
30 | dst_fd = os.open(args.dest, os.O_WRONLY) | ||
31 | |||
32 | try: | ||
33 | ioctl(dst_fd, FICLONERANGE, struct_file_clone_range(src_fd, | ||
34 | args.s or 0, | ||
35 | args.l or 0, | ||
36 | args.d if args.d is not None else os.fstat(dst_fd).st_size, | ||
37 | )) | ||
38 | except OSError as e: | ||
39 | if e.errno == errno.EXDEV: | ||
40 | fail("src and dest are not on the same mounted filesystem") | ||
41 | |||
42 | if e.errno == errno.EISDIR: | ||
43 | fail("One of the files is a directory and the filesystem does not support shared regions in directories.") | ||
44 | |||
45 | if e.errno == errno.EINVAL: | ||
46 | fail("The filesystem does not support reflinking the ranges of the given files. This error can also appear " | ||
47 | "if either file descriptor represents a device, FIFO, or socket. Disk filesystems generally require the " | ||
48 | "offset and length arguments to be aligned to the fundamental block size ({} bytes). XFS and Btrfs do not support " | ||
49 | "overlapping reflink ranges in the same file.".format(os.fstat(dst_fd).st_blksize)) | ||
50 | |||
51 | if e.errno == errno.EBADF: | ||
52 | fail("Filesystem does not support reflink (EBADF).") | ||
53 | |||
54 | if e.errno == errno.EPERM: | ||
55 | fail("dest is immutable.") | ||
56 | |||
57 | if e.errno == errno.ETXTBSY: | ||
58 | fail("One of the files is a swap file. Swap files cannot share storage.") | ||
59 | |||
60 | if e.errno == errno.EOPNOTSUPP: | ||
61 | fail("Filesystem does not support reflink (EOPNOTSUPP).") | ||
62 | |||
63 | raise | ||