1 |
The sendfile *offset parameter refers to the input file offest, so |
2 |
it cannot be used in the same way as the copy_file_range *off_out |
3 |
parameter. Therefore, add sf_wrapper function which implements the |
4 |
*off_out behavior for sendfile. |
5 |
|
6 |
Also update cfr_wrapper so that it does not rely on the fd_in file |
7 |
offset, and remove corresponding fd_in lseek calls which are no |
8 |
longer needed. |
9 |
|
10 |
The file offset of fd_in is now completely unused, except in the |
11 |
plain read/write loop, where lseek is called prior to entering |
12 |
the loop. |
13 |
|
14 |
Bug: https://bugs.gentoo.org/635126 |
15 |
--- |
16 |
[PATCH v5] eliminates all reliance on the file offset of fd_in, |
17 |
except in the plain read/write loop, where lseek is called prior |
18 |
to entering the loop. |
19 |
|
20 |
src/portage_util_file_copy_reflink_linux.c | 88 ++++++++++++++++++++---------- |
21 |
1 file changed, 59 insertions(+), 29 deletions(-) |
22 |
|
23 |
diff --git a/src/portage_util_file_copy_reflink_linux.c b/src/portage_util_file_copy_reflink_linux.c |
24 |
index 4be9e0568..4c3303181 100644 |
25 |
--- a/src/portage_util_file_copy_reflink_linux.c |
26 |
+++ b/src/portage_util_file_copy_reflink_linux.c |
27 |
@@ -56,12 +56,18 @@ initreflink_linux(void) |
28 |
|
29 |
/** |
30 |
* cfr_wrapper - A copy_file_range syscall wrapper function, having a |
31 |
- * function signature that is compatible with sendfile. |
32 |
+ * function signature that is compatible with sf_wrapper. |
33 |
* @fd_out: output file descriptor |
34 |
* @fd_in: input file descriptor |
35 |
- * @off_out: offset of the output file |
36 |
+ * @off_out: must point to a buffer that specifies the starting offset |
37 |
+ * where bytes will be copied to fd_out, and this buffer is adjusted by |
38 |
+ * the number of bytes copied. |
39 |
* @len: number of bytes to copy between the file descriptors |
40 |
* |
41 |
+ * Bytes are copied from fd_in starting from *off_out, and the file |
42 |
+ * offset of fd_in is not changed. Effects on the file offset of |
43 |
+ * fd_out are undefined. |
44 |
+ * |
45 |
* Return: Number of bytes written to out_fd on success, -1 on failure |
46 |
* (errno is set appropriately). |
47 |
*/ |
48 |
@@ -69,7 +75,8 @@ static ssize_t |
49 |
cfr_wrapper(int fd_out, int fd_in, off_t *off_out, size_t len) |
50 |
{ |
51 |
#ifdef __NR_copy_file_range |
52 |
- return syscall(__NR_copy_file_range, fd_in, NULL, fd_out, |
53 |
+ off_t off_in = *off_out; |
54 |
+ return syscall(__NR_copy_file_range, fd_in, &off_in, fd_out, |
55 |
off_out, len, 0); |
56 |
#else |
57 |
/* This is how it fails at runtime when the syscall is not supported. */ |
58 |
@@ -79,18 +86,50 @@ cfr_wrapper(int fd_out, int fd_in, off_t *off_out, size_t len) |
59 |
} |
60 |
|
61 |
/** |
62 |
+ * sf_wrapper - A sendfile wrapper function, having a function signature |
63 |
+ * that is compatible with cfr_wrapper. |
64 |
+ * @fd_out: output file descriptor |
65 |
+ * @fd_in: input file descriptor |
66 |
+ * @off_out: must point to a buffer that specifies the starting offset |
67 |
+ * where bytes will be copied to fd_out, and this buffer is adjusted by |
68 |
+ * the number of bytes copied. |
69 |
+ * @len: number of bytes to copy between the file descriptors |
70 |
+ * |
71 |
+ * Bytes are copied from fd_in starting from *off_out, and the file |
72 |
+ * offset of fd_in is not changed. Effects on the file offset of |
73 |
+ * fd_out are undefined. |
74 |
+ * |
75 |
+ * Return: Number of bytes written to out_fd on success, -1 on failure |
76 |
+ * (errno is set appropriately). |
77 |
+ */ |
78 |
+static ssize_t |
79 |
+sf_wrapper(int fd_out, int fd_in, off_t *off_out, size_t len) |
80 |
+{ |
81 |
+ ssize_t ret; |
82 |
+ off_t off_in = *off_out; |
83 |
+ /* The sendfile docs do not specify behavior of the output file |
84 |
+ * offset, therefore it must be adjusted with lseek. |
85 |
+ */ |
86 |
+ if (lseek(fd_out, *off_out, SEEK_SET) < 0) |
87 |
+ return -1; |
88 |
+ ret = sendfile(fd_out, fd_in, &off_in, len); |
89 |
+ if (ret > 0) |
90 |
+ *off_out += ret; |
91 |
+ return ret; |
92 |
+} |
93 |
+ |
94 |
+ |
95 |
+/** |
96 |
* do_lseek_data - Adjust file offsets to the next location containing |
97 |
* data, creating sparse empty blocks in the output file as needed. |
98 |
* @fd_in: input file descriptor |
99 |
* @fd_out: output file descriptor |
100 |
* @off_out: offset of the output file |
101 |
* |
102 |
- * Use lseek SEEK_DATA to adjust the fd_in file offset to the next |
103 |
- * location containing data, and adjust the fd_in file offset and |
104 |
- * off_out to the same location (creating sparse empty blocks as |
105 |
- * needed). On success, both fd_in and fd_out file offsets are |
106 |
- * guaranteed to be exactly equal to the value that off_out points to. |
107 |
- * |
108 |
+ * Use lseek SEEK_DATA to adjust off_out to the next location from fd_in |
109 |
+ * containing data (creates sparse empty blocks when appropriate). Effects |
110 |
+ * on file offsets are undefined. |
111 |
+ * |
112 |
* Return: On success, the number of bytes to copy before the next hole, |
113 |
* and -1 on failure (errno is set appropriately). Returns 0 when fd_in |
114 |
* reaches EOF. |
115 |
@@ -250,7 +289,7 @@ _reflink_linux_file_copy(PyObject *self, PyObject *args) |
116 |
* syscall is not available (less than Linux 4.5). |
117 |
*/ |
118 |
error = 0; |
119 |
- copyfunc = sendfile; |
120 |
+ copyfunc = sf_wrapper; |
121 |
copyfunc_ret = copyfunc(fd_out, |
122 |
fd_in, |
123 |
&offset_out, |
124 |
@@ -284,27 +323,18 @@ _reflink_linux_file_copy(PyObject *self, PyObject *args) |
125 |
} else { |
126 |
stat_in_acquired = 1; |
127 |
|
128 |
- /* For the sendfile call, the fd_in file offset must be |
129 |
- * exactly equal to offset_out. Use lseek to ensure |
130 |
- * correct state, in case an EINTR retry caused it to |
131 |
- * get out of sync somewhow. |
132 |
- */ |
133 |
- if (lseek(fd_in, offset_out, SEEK_SET) < 0) { |
134 |
- error = errno; |
135 |
- } else { |
136 |
- while (offset_out < stat_in.st_size) { |
137 |
- copyfunc_ret = sendfile(fd_out, |
138 |
- fd_in, |
139 |
- &offset_out, |
140 |
- stat_in.st_size - offset_out); |
141 |
+ while (offset_out < stat_in.st_size) { |
142 |
+ copyfunc_ret = sf_wrapper(fd_out, |
143 |
+ fd_in, |
144 |
+ &offset_out, |
145 |
+ stat_in.st_size - offset_out); |
146 |
|
147 |
- if (copyfunc_ret < 0) { |
148 |
- error = errno; |
149 |
- if (errno == EINVAL && !offset_out) { |
150 |
- sendfile_works = 0; |
151 |
- } |
152 |
- break; |
153 |
+ if (copyfunc_ret < 0) { |
154 |
+ error = errno; |
155 |
+ if (errno == EINVAL && !offset_out) { |
156 |
+ sendfile_works = 0; |
157 |
} |
158 |
+ break; |
159 |
} |
160 |
} |
161 |
} |
162 |
-- |
163 |
2.13.5 |