Gentoo Archives: gentoo-portage-dev

From: Zac Medico <zmedico@g.o>
To: gentoo-portage-dev@l.g.o
Cc: Zac Medico <zmedico@g.o>
Subject: [gentoo-portage-dev] [PATCH v5] file_copy: use sendfile return value to measure bytes copied (bug 635126)
Date: Tue, 24 Oct 2017 18:42:12
Message-Id: 20171024184137.27947-1-zmedico@gentoo.org
In Reply to: [gentoo-portage-dev] [PATCH] file_copy: use sendfile return value to measure bytes copied (bug 635126) by Zac Medico
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

Replies