summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJonathan Lebon <jonathan@jlebon.com>2022-07-29 17:29:28 -0400
committerJonathan Lebon <jonathan@jlebon.com>2022-07-29 17:52:43 -0400
commit7a2f26a312c92e335652962bb99bf2e160720c8d (patch)
tree0bcc43019db4d0d7b3214a02862c2c5b5bdb5653
parentc59eb271b5127309c73117bbc871dcfd9fd159a6 (diff)
downloadlibglnx-7a2f26a312c92e335652962bb99bf2e160720c8d.tar.gz
fdio: fix fd offset handling with `FICLONE`
When using `FICLONE`, the kernel does a full file clone and disregards the fd offsets. Users of this API however assume that it *is* offset-sensitive. So we need to verify that the fd offsets are at the start of the files before we call `FICLONE`. This was done in systemd also in: https://github.com/systemd/systemd/commit/c622fbdb8d37 The commit message does not explain this but `ioctl_ficlone(2)` says: The `FICLONE` ioctl clones entire files. (Compare with `FICLONERANGE`, which takes a struct with offsets and the length). Similarly, we need to seek to the end of the file descriptors on success so that we're consistent with the behaviour of the other backends available (`copy_file_range`, `sendfile` and manual copying). This also matches what systemd does nowadays: https://github.com/systemd/systemd/blob/80f967311ac5/src/shared/copy.c#L199
-rw-r--r--glnx-fdio.c15
1 files changed, 13 insertions, 2 deletions
diff --git a/glnx-fdio.c b/glnx-fdio.c
index ee69aa1..b1af679 100644
--- a/glnx-fdio.c
+++ b/glnx-fdio.c
@@ -794,10 +794,21 @@ glnx_regfile_copy_bytes (int fdf, int fdt, off_t max_bytes)
/* If we've requested to copy the whole range, try a full-file clone first.
*/
- if (max_bytes == (off_t) -1)
+ if (max_bytes == (off_t) -1 &&
+ lseek (fdf, 0, SEEK_CUR) == 0 &&
+ lseek (fdt, 0, SEEK_CUR) == 0)
{
if (ioctl (fdt, FICLONE, fdf) == 0)
- return 0;
+ {
+ /* All the other methods advance the fds. Do it here too for consistency. */
+ if (lseek (fdf, 0, SEEK_END) < 0)
+ return -1;
+ if (lseek (fdt, 0, SEEK_END) < 0)
+ return -1;
+
+ return 0;
+ }
+
/* Fall through */
struct stat stbuf;