]>
Commit | Line | Data |
---|---|---|
849958d1 LP |
1 | /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ |
2 | ||
3 | /*** | |
4 | This file is part of systemd. | |
5 | ||
6 | Copyright 2014 Lennart Poettering | |
7 | ||
8 | systemd is free software; you can redistribute it and/or modify it | |
9 | under the terms of the GNU Lesser General Public License as published by | |
10 | the Free Software Foundation; either version 2.1 of the License, or | |
11 | (at your option) any later version. | |
12 | ||
13 | systemd is distributed in the hope that it will be useful, but | |
14 | WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
16 | Lesser General Public License for more details. | |
17 | ||
18 | You should have received a copy of the GNU Lesser General Public License | |
19 | along with systemd; If not, see <http://www.gnu.org/licenses/>. | |
20 | ***/ | |
21 | ||
cda134ab LP |
22 | #include <sys/sendfile.h> |
23 | ||
849958d1 | 24 | #include "util.h" |
d7c7c334 | 25 | #include "btrfs-util.h" |
849958d1 LP |
26 | #include "copy.h" |
27 | ||
7430ec6a | 28 | int copy_bytes(int fdf, int fdt, off_t max_bytes, bool try_reflink) { |
cda134ab | 29 | bool try_sendfile = true; |
0254b455 | 30 | int r; |
cda134ab | 31 | |
849958d1 LP |
32 | assert(fdf >= 0); |
33 | assert(fdt >= 0); | |
34 | ||
0254b455 | 35 | /* Try btrfs reflinks first. */ |
7430ec6a | 36 | if (try_reflink && max_bytes == (off_t) -1) { |
0254b455 LP |
37 | r = btrfs_reflink(fdf, fdt); |
38 | if (r >= 0) | |
7430ec6a | 39 | return r; |
0254b455 LP |
40 | } |
41 | ||
849958d1 | 42 | for (;;) { |
cda134ab LP |
43 | size_t m = PIPE_BUF; |
44 | ssize_t n; | |
93240d3a LP |
45 | |
46 | if (max_bytes != (off_t) -1) { | |
47 | ||
48 | if (max_bytes <= 0) | |
84ee0960 | 49 | return -EFBIG; |
93240d3a LP |
50 | |
51 | if ((off_t) m > max_bytes) | |
52 | m = (size_t) max_bytes; | |
53 | } | |
54 | ||
cda134ab LP |
55 | /* First try sendfile(), unless we already tried */ |
56 | if (try_sendfile) { | |
57 | ||
58 | n = sendfile(fdt, fdf, NULL, m); | |
59 | if (n < 0) { | |
60 | if (errno != EINVAL && errno != ENOSYS) | |
61 | return -errno; | |
62 | ||
63 | try_sendfile = false; | |
64 | /* use fallback below */ | |
65 | } else if (n == 0) /* EOF */ | |
66 | break; | |
67 | else if (n > 0) | |
68 | /* Succcess! */ | |
69 | goto next; | |
70 | } | |
71 | ||
72 | /* As a fallback just copy bits by hand */ | |
73 | { | |
74 | char buf[m]; | |
849958d1 | 75 | |
cda134ab LP |
76 | n = read(fdf, buf, m); |
77 | if (n < 0) | |
78 | return -errno; | |
79 | if (n == 0) /* EOF */ | |
80 | break; | |
81 | ||
0254b455 | 82 | r = loop_write(fdt, buf, (size_t) n, false); |
553acb7b ZJS |
83 | if (r < 0) |
84 | return r; | |
cda134ab | 85 | } |
93240d3a | 86 | |
cda134ab | 87 | next: |
93240d3a LP |
88 | if (max_bytes != (off_t) -1) { |
89 | assert(max_bytes >= n); | |
90 | max_bytes -= n; | |
91 | } | |
849958d1 LP |
92 | } |
93 | ||
94 | return 0; | |
95 | } | |
96 | ||
97 | static int fd_copy_symlink(int df, const char *from, const struct stat *st, int dt, const char *to) { | |
98 | _cleanup_free_ char *target = NULL; | |
99 | int r; | |
100 | ||
101 | assert(from); | |
102 | assert(st); | |
103 | assert(to); | |
104 | ||
105 | r = readlinkat_malloc(df, from, &target); | |
106 | if (r < 0) | |
107 | return r; | |
108 | ||
e156347e | 109 | if (symlinkat(target, dt, to) < 0) |
849958d1 | 110 | return -errno; |
849958d1 LP |
111 | |
112 | if (fchownat(dt, to, st->st_uid, st->st_gid, AT_SYMLINK_NOFOLLOW) < 0) | |
113 | return -errno; | |
114 | ||
115 | return 0; | |
116 | } | |
117 | ||
118 | static int fd_copy_regular(int df, const char *from, const struct stat *st, int dt, const char *to) { | |
119 | _cleanup_close_ int fdf = -1, fdt = -1; | |
120 | int r, q; | |
121 | ||
122 | assert(from); | |
123 | assert(st); | |
124 | assert(to); | |
125 | ||
126 | fdf = openat(df, from, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW); | |
127 | if (fdf < 0) | |
128 | return -errno; | |
129 | ||
130 | fdt = openat(dt, to, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, st->st_mode & 07777); | |
e156347e | 131 | if (fdt < 0) |
849958d1 | 132 | return -errno; |
849958d1 | 133 | |
7430ec6a | 134 | r = copy_bytes(fdf, fdt, (off_t) -1, true); |
849958d1 LP |
135 | if (r < 0) { |
136 | unlinkat(dt, to, 0); | |
137 | return r; | |
138 | } | |
139 | ||
140 | if (fchown(fdt, st->st_uid, st->st_gid) < 0) | |
141 | r = -errno; | |
142 | ||
143 | if (fchmod(fdt, st->st_mode & 07777) < 0) | |
144 | r = -errno; | |
145 | ||
146 | q = close(fdt); | |
147 | fdt = -1; | |
148 | ||
149 | if (q < 0) { | |
150 | r = -errno; | |
151 | unlinkat(dt, to, 0); | |
152 | } | |
153 | ||
154 | return r; | |
155 | } | |
156 | ||
157 | static int fd_copy_fifo(int df, const char *from, const struct stat *st, int dt, const char *to) { | |
158 | int r; | |
159 | ||
160 | assert(from); | |
161 | assert(st); | |
162 | assert(to); | |
163 | ||
164 | r = mkfifoat(dt, to, st->st_mode & 07777); | |
e156347e | 165 | if (r < 0) |
849958d1 | 166 | return -errno; |
849958d1 LP |
167 | |
168 | if (fchownat(dt, to, st->st_uid, st->st_gid, AT_SYMLINK_NOFOLLOW) < 0) | |
169 | r = -errno; | |
170 | ||
171 | if (fchmodat(dt, to, st->st_mode & 07777, 0) < 0) | |
172 | r = -errno; | |
173 | ||
174 | return r; | |
175 | } | |
176 | ||
177 | static int fd_copy_node(int df, const char *from, const struct stat *st, int dt, const char *to) { | |
178 | int r; | |
179 | ||
180 | assert(from); | |
181 | assert(st); | |
182 | assert(to); | |
183 | ||
184 | r = mknodat(dt, to, st->st_mode, st->st_rdev); | |
e156347e | 185 | if (r < 0) |
849958d1 | 186 | return -errno; |
849958d1 LP |
187 | |
188 | if (fchownat(dt, to, st->st_uid, st->st_gid, AT_SYMLINK_NOFOLLOW) < 0) | |
189 | r = -errno; | |
190 | ||
191 | if (fchmodat(dt, to, st->st_mode & 07777, 0) < 0) | |
192 | r = -errno; | |
193 | ||
194 | return r; | |
195 | } | |
196 | ||
d7c7c334 LP |
197 | static int fd_copy_directory( |
198 | int df, | |
199 | const char *from, | |
200 | const struct stat *st, | |
201 | int dt, | |
202 | const char *to, | |
203 | dev_t original_device, | |
204 | bool merge) { | |
205 | ||
849958d1 LP |
206 | _cleanup_close_ int fdf = -1, fdt = -1; |
207 | _cleanup_closedir_ DIR *d = NULL; | |
208 | struct dirent *de; | |
209 | bool created; | |
210 | int r; | |
211 | ||
849958d1 LP |
212 | assert(st); |
213 | assert(to); | |
214 | ||
d7c7c334 LP |
215 | if (from) |
216 | fdf = openat(df, from, O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW); | |
217 | else | |
218 | fdf = fcntl(df, F_DUPFD_CLOEXEC, 3); | |
849958d1 LP |
219 | |
220 | d = fdopendir(fdf); | |
221 | if (!d) | |
222 | return -errno; | |
223 | fdf = -1; | |
224 | ||
225 | r = mkdirat(dt, to, st->st_mode & 07777); | |
226 | if (r >= 0) | |
227 | created = true; | |
e156347e | 228 | else if (errno == EEXIST && merge) |
849958d1 LP |
229 | created = false; |
230 | else | |
231 | return -errno; | |
232 | ||
233 | fdt = openat(dt, to, O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW); | |
234 | if (fdt < 0) | |
235 | return -errno; | |
236 | ||
2c455af4 LP |
237 | r = 0; |
238 | ||
849958d1 LP |
239 | if (created) { |
240 | if (fchown(fdt, st->st_uid, st->st_gid) < 0) | |
241 | r = -errno; | |
242 | ||
243 | if (fchmod(fdt, st->st_mode & 07777) < 0) | |
244 | r = -errno; | |
245 | } | |
246 | ||
247 | FOREACH_DIRENT(de, d, return -errno) { | |
248 | struct stat buf; | |
249 | int q; | |
250 | ||
251 | if (fstatat(dirfd(d), de->d_name, &buf, AT_SYMLINK_NOFOLLOW) < 0) { | |
252 | r = -errno; | |
253 | continue; | |
254 | } | |
255 | ||
256 | if (buf.st_dev != original_device) | |
257 | continue; | |
258 | ||
259 | if (S_ISREG(buf.st_mode)) | |
260 | q = fd_copy_regular(dirfd(d), de->d_name, &buf, fdt, de->d_name); | |
261 | else if (S_ISDIR(buf.st_mode)) | |
e156347e | 262 | q = fd_copy_directory(dirfd(d), de->d_name, &buf, fdt, de->d_name, original_device, merge); |
849958d1 LP |
263 | else if (S_ISLNK(buf.st_mode)) |
264 | q = fd_copy_symlink(dirfd(d), de->d_name, &buf, fdt, de->d_name); | |
265 | else if (S_ISFIFO(buf.st_mode)) | |
266 | q = fd_copy_fifo(dirfd(d), de->d_name, &buf, fdt, de->d_name); | |
267 | else if (S_ISBLK(buf.st_mode) || S_ISCHR(buf.st_mode)) | |
268 | q = fd_copy_node(dirfd(d), de->d_name, &buf, fdt, de->d_name); | |
269 | else | |
270 | q = -ENOTSUP; | |
271 | ||
e156347e LP |
272 | if (q == -EEXIST && merge) |
273 | q = 0; | |
274 | ||
849958d1 LP |
275 | if (q < 0) |
276 | r = q; | |
277 | } | |
278 | ||
279 | return r; | |
280 | } | |
281 | ||
e156347e | 282 | int copy_tree(const char *from, const char *to, bool merge) { |
849958d1 LP |
283 | struct stat st; |
284 | ||
285 | assert(from); | |
286 | assert(to); | |
287 | ||
288 | if (lstat(from, &st) < 0) | |
289 | return -errno; | |
290 | ||
291 | if (S_ISREG(st.st_mode)) | |
292 | return fd_copy_regular(AT_FDCWD, from, &st, AT_FDCWD, to); | |
293 | else if (S_ISDIR(st.st_mode)) | |
e156347e | 294 | return fd_copy_directory(AT_FDCWD, from, &st, AT_FDCWD, to, st.st_dev, merge); |
849958d1 LP |
295 | else if (S_ISLNK(st.st_mode)) |
296 | return fd_copy_symlink(AT_FDCWD, from, &st, AT_FDCWD, to); | |
297 | else if (S_ISFIFO(st.st_mode)) | |
298 | return fd_copy_fifo(AT_FDCWD, from, &st, AT_FDCWD, to); | |
299 | else if (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode)) | |
300 | return fd_copy_node(AT_FDCWD, from, &st, AT_FDCWD, to); | |
301 | else | |
302 | return -ENOTSUP; | |
303 | } | |
304 | ||
d7c7c334 LP |
305 | int copy_tree_fd(int dirfd, const char *to, bool merge) { |
306 | ||
307 | struct stat st; | |
308 | ||
309 | assert(dirfd >= 0); | |
310 | assert(to); | |
311 | ||
312 | if (fstat(dirfd, &st) < 0) | |
313 | return -errno; | |
314 | ||
315 | if (!S_ISDIR(st.st_mode)) | |
316 | return -ENOTDIR; | |
317 | ||
318 | return fd_copy_directory(dirfd, NULL, &st, AT_FDCWD, to, st.st_dev, merge); | |
319 | } | |
320 | ||
7430ec6a | 321 | int copy_file_fd(const char *from, int fdt, bool try_reflink) { |
cda134ab | 322 | _cleanup_close_ int fdf = -1; |
849958d1 LP |
323 | |
324 | assert(from); | |
cda134ab | 325 | assert(fdt >= 0); |
849958d1 LP |
326 | |
327 | fdf = open(from, O_RDONLY|O_CLOEXEC|O_NOCTTY); | |
328 | if (fdf < 0) | |
329 | return -errno; | |
330 | ||
7430ec6a | 331 | return copy_bytes(fdf, fdt, (off_t) -1, try_reflink); |
cda134ab LP |
332 | } |
333 | ||
334 | int copy_file(const char *from, const char *to, int flags, mode_t mode) { | |
335 | int fdt, r; | |
336 | ||
337 | assert(from); | |
338 | assert(to); | |
339 | ||
849958d1 LP |
340 | fdt = open(to, flags|O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY, mode); |
341 | if (fdt < 0) | |
342 | return -errno; | |
343 | ||
7430ec6a | 344 | r = copy_file_fd(from, fdt, true); |
849958d1 | 345 | if (r < 0) { |
cda134ab | 346 | close(fdt); |
849958d1 LP |
347 | unlink(to); |
348 | return r; | |
349 | } | |
350 | ||
cda134ab LP |
351 | if (close(fdt) < 0) { |
352 | unlink_noerrno(to); | |
353 | return -errno; | |
849958d1 LP |
354 | } |
355 | ||
356 | return 0; | |
357 | } |