Line data Source code
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 :
22 : #include <sys/sendfile.h>
23 : #include <sys/xattr.h>
24 :
25 : #include "util.h"
26 : #include "btrfs-util.h"
27 : #include "strv.h"
28 : #include "copy.h"
29 :
30 : #define COPY_BUFFER_SIZE (16*1024)
31 :
32 10 : int copy_bytes(int fdf, int fdt, off_t max_bytes, bool try_reflink) {
33 10 : bool try_sendfile = true;
34 : int r;
35 :
36 10 : assert(fdf >= 0);
37 10 : assert(fdt >= 0);
38 :
39 : /* Try btrfs reflinks first. */
40 10 : if (try_reflink && max_bytes == (off_t) -1) {
41 6 : r = btrfs_reflink(fdf, fdt);
42 6 : if (r >= 0)
43 0 : return r;
44 : }
45 :
46 : for (;;) {
47 17 : size_t m = COPY_BUFFER_SIZE;
48 : ssize_t n;
49 :
50 17 : if (max_bytes != (off_t) -1) {
51 :
52 3 : if (max_bytes <= 0)
53 0 : return -EFBIG;
54 :
55 3 : if ((off_t) m > max_bytes)
56 3 : m = (size_t) max_bytes;
57 : }
58 :
59 : /* First try sendfile(), unless we already tried */
60 17 : if (try_sendfile) {
61 :
62 17 : n = sendfile(fdt, fdf, NULL, m);
63 17 : if (n < 0) {
64 3 : if (errno != EINVAL && errno != ENOSYS)
65 3 : return -errno;
66 :
67 0 : try_sendfile = false;
68 : /* use fallback below */
69 14 : } else if (n == 0) /* EOF */
70 7 : break;
71 7 : else if (n > 0)
72 : /* Succcess! */
73 7 : goto next;
74 : }
75 :
76 : /* As a fallback just copy bits by hand */
77 0 : {
78 0 : char buf[m];
79 :
80 0 : n = read(fdf, buf, m);
81 0 : if (n < 0)
82 0 : return -errno;
83 0 : if (n == 0) /* EOF */
84 0 : break;
85 :
86 0 : r = loop_write(fdt, buf, (size_t) n, false);
87 0 : if (r < 0)
88 0 : return r;
89 : }
90 :
91 : next:
92 7 : if (max_bytes != (off_t) -1) {
93 0 : assert(max_bytes >= n);
94 0 : max_bytes -= n;
95 : }
96 7 : }
97 :
98 7 : return 0;
99 : }
100 :
101 2 : static int fd_copy_symlink(int df, const char *from, const struct stat *st, int dt, const char *to) {
102 4 : _cleanup_free_ char *target = NULL;
103 : int r;
104 :
105 2 : assert(from);
106 2 : assert(st);
107 2 : assert(to);
108 :
109 2 : r = readlinkat_malloc(df, from, &target);
110 2 : if (r < 0)
111 0 : return r;
112 :
113 2 : if (symlinkat(target, dt, to) < 0)
114 0 : return -errno;
115 :
116 2 : if (fchownat(dt, to, st->st_uid, st->st_gid, AT_SYMLINK_NOFOLLOW) < 0)
117 0 : return -errno;
118 :
119 2 : return 0;
120 : }
121 :
122 4 : static int fd_copy_regular(int df, const char *from, const struct stat *st, int dt, const char *to) {
123 8 : _cleanup_close_ int fdf = -1, fdt = -1;
124 : struct timespec ts[2];
125 : int r, q;
126 :
127 4 : assert(from);
128 4 : assert(st);
129 4 : assert(to);
130 :
131 4 : fdf = openat(df, from, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
132 4 : if (fdf < 0)
133 0 : return -errno;
134 :
135 4 : fdt = openat(dt, to, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, st->st_mode & 07777);
136 4 : if (fdt < 0)
137 0 : return -errno;
138 :
139 4 : r = copy_bytes(fdf, fdt, (off_t) -1, true);
140 4 : if (r < 0) {
141 0 : unlinkat(dt, to, 0);
142 0 : return r;
143 : }
144 :
145 4 : if (fchown(fdt, st->st_uid, st->st_gid) < 0)
146 0 : r = -errno;
147 :
148 4 : if (fchmod(fdt, st->st_mode & 07777) < 0)
149 0 : r = -errno;
150 :
151 4 : ts[0] = st->st_atim;
152 4 : ts[1] = st->st_mtim;
153 4 : (void) futimens(fdt, ts);
154 :
155 4 : (void) copy_xattr(fdf, fdt);
156 :
157 4 : q = close(fdt);
158 4 : fdt = -1;
159 :
160 4 : if (q < 0) {
161 0 : r = -errno;
162 0 : unlinkat(dt, to, 0);
163 : }
164 :
165 4 : return r;
166 : }
167 :
168 0 : static int fd_copy_fifo(int df, const char *from, const struct stat *st, int dt, const char *to) {
169 : int r;
170 :
171 0 : assert(from);
172 0 : assert(st);
173 0 : assert(to);
174 :
175 0 : r = mkfifoat(dt, to, st->st_mode & 07777);
176 0 : if (r < 0)
177 0 : return -errno;
178 :
179 0 : if (fchownat(dt, to, st->st_uid, st->st_gid, AT_SYMLINK_NOFOLLOW) < 0)
180 0 : r = -errno;
181 :
182 0 : if (fchmodat(dt, to, st->st_mode & 07777, 0) < 0)
183 0 : r = -errno;
184 :
185 0 : return r;
186 : }
187 :
188 0 : static int fd_copy_node(int df, const char *from, const struct stat *st, int dt, const char *to) {
189 : int r;
190 :
191 0 : assert(from);
192 0 : assert(st);
193 0 : assert(to);
194 :
195 0 : r = mknodat(dt, to, st->st_mode, st->st_rdev);
196 0 : if (r < 0)
197 0 : return -errno;
198 :
199 0 : if (fchownat(dt, to, st->st_uid, st->st_gid, AT_SYMLINK_NOFOLLOW) < 0)
200 0 : r = -errno;
201 :
202 0 : if (fchmodat(dt, to, st->st_mode & 07777, 0) < 0)
203 0 : r = -errno;
204 :
205 0 : return r;
206 : }
207 :
208 7 : static int fd_copy_directory(
209 : int df,
210 : const char *from,
211 : const struct stat *st,
212 : int dt,
213 : const char *to,
214 : dev_t original_device,
215 : bool merge) {
216 :
217 14 : _cleanup_close_ int fdf = -1, fdt = -1;
218 14 : _cleanup_closedir_ DIR *d = NULL;
219 : struct dirent *de;
220 : bool created;
221 : int r;
222 :
223 7 : assert(st);
224 7 : assert(to);
225 :
226 7 : if (from)
227 7 : fdf = openat(df, from, O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
228 : else
229 0 : fdf = fcntl(df, F_DUPFD_CLOEXEC, 3);
230 :
231 7 : d = fdopendir(fdf);
232 7 : if (!d)
233 0 : return -errno;
234 7 : fdf = -1;
235 :
236 7 : r = mkdirat(dt, to, st->st_mode & 07777);
237 7 : if (r >= 0)
238 6 : created = true;
239 1 : else if (errno == EEXIST && merge)
240 0 : created = false;
241 : else
242 1 : return -errno;
243 :
244 6 : fdt = openat(dt, to, O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
245 6 : if (fdt < 0)
246 0 : return -errno;
247 :
248 6 : r = 0;
249 :
250 6 : if (created) {
251 6 : struct timespec ut[2] = {
252 : st->st_atim,
253 : st->st_mtim
254 : };
255 :
256 6 : if (fchown(fdt, st->st_uid, st->st_gid) < 0)
257 0 : r = -errno;
258 :
259 6 : if (fchmod(fdt, st->st_mode & 07777) < 0)
260 0 : r = -errno;
261 :
262 6 : (void) futimens(fdt, ut);
263 6 : (void) copy_xattr(dirfd(d), fdt);
264 : }
265 :
266 29 : FOREACH_DIRENT_ALL(de, d, return -errno) {
267 : struct stat buf;
268 : int q;
269 :
270 23 : if (STR_IN_SET(de->d_name, ".", ".."))
271 24 : continue;
272 :
273 11 : if (fstatat(dirfd(d), de->d_name, &buf, AT_SYMLINK_NOFOLLOW) < 0) {
274 0 : r = -errno;
275 0 : continue;
276 : }
277 :
278 11 : if (buf.st_dev != original_device)
279 0 : continue;
280 :
281 11 : if (S_ISREG(buf.st_mode))
282 4 : q = fd_copy_regular(dirfd(d), de->d_name, &buf, fdt, de->d_name);
283 7 : else if (S_ISDIR(buf.st_mode))
284 5 : q = fd_copy_directory(dirfd(d), de->d_name, &buf, fdt, de->d_name, original_device, merge);
285 2 : else if (S_ISLNK(buf.st_mode))
286 2 : q = fd_copy_symlink(dirfd(d), de->d_name, &buf, fdt, de->d_name);
287 0 : else if (S_ISFIFO(buf.st_mode))
288 0 : q = fd_copy_fifo(dirfd(d), de->d_name, &buf, fdt, de->d_name);
289 0 : else if (S_ISBLK(buf.st_mode) || S_ISCHR(buf.st_mode))
290 0 : q = fd_copy_node(dirfd(d), de->d_name, &buf, fdt, de->d_name);
291 : else
292 0 : q = -EOPNOTSUPP;
293 :
294 11 : if (q == -EEXIST && merge)
295 0 : q = 0;
296 :
297 11 : if (q < 0)
298 0 : r = q;
299 23 : }
300 :
301 6 : return r;
302 : }
303 :
304 3 : int copy_tree_at(int fdf, const char *from, int fdt, const char *to, bool merge) {
305 : struct stat st;
306 :
307 3 : assert(from);
308 3 : assert(to);
309 :
310 3 : if (fstatat(fdf, from, &st, AT_SYMLINK_NOFOLLOW) < 0)
311 1 : return -errno;
312 :
313 2 : if (S_ISREG(st.st_mode))
314 0 : return fd_copy_regular(fdf, from, &st, fdt, to);
315 2 : else if (S_ISDIR(st.st_mode))
316 2 : return fd_copy_directory(fdf, from, &st, fdt, to, st.st_dev, merge);
317 0 : else if (S_ISLNK(st.st_mode))
318 0 : return fd_copy_symlink(fdf, from, &st, fdt, to);
319 0 : else if (S_ISFIFO(st.st_mode))
320 0 : return fd_copy_fifo(fdf, from, &st, fdt, to);
321 0 : else if (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode))
322 0 : return fd_copy_node(fdf, from, &st, fdt, to);
323 : else
324 0 : return -EOPNOTSUPP;
325 : }
326 :
327 3 : int copy_tree(const char *from, const char *to, bool merge) {
328 3 : return copy_tree_at(AT_FDCWD, from, AT_FDCWD, to, merge);
329 : }
330 :
331 0 : int copy_directory_fd(int dirfd, const char *to, bool merge) {
332 :
333 : struct stat st;
334 :
335 0 : assert(dirfd >= 0);
336 0 : assert(to);
337 :
338 0 : if (fstat(dirfd, &st) < 0)
339 0 : return -errno;
340 :
341 0 : if (!S_ISDIR(st.st_mode))
342 0 : return -ENOTDIR;
343 :
344 0 : return fd_copy_directory(dirfd, NULL, &st, AT_FDCWD, to, st.st_dev, merge);
345 : }
346 :
347 3 : int copy_file_fd(const char *from, int fdt, bool try_reflink) {
348 6 : _cleanup_close_ int fdf = -1;
349 : int r;
350 :
351 3 : assert(from);
352 3 : assert(fdt >= 0);
353 :
354 3 : fdf = open(from, O_RDONLY|O_CLOEXEC|O_NOCTTY);
355 3 : if (fdf < 0)
356 1 : return -errno;
357 :
358 2 : r = copy_bytes(fdf, fdt, (off_t) -1, try_reflink);
359 :
360 2 : (void) copy_times(fdf, fdt);
361 2 : (void) copy_xattr(fdf, fdt);
362 :
363 2 : return r;
364 : }
365 :
366 1 : int copy_file(const char *from, const char *to, int flags, mode_t mode, unsigned chattr_flags) {
367 1 : int fdt = -1, r;
368 :
369 1 : assert(from);
370 1 : assert(to);
371 :
372 2 : RUN_WITH_UMASK(0000) {
373 1 : fdt = open(to, flags|O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY, mode);
374 1 : if (fdt < 0)
375 0 : return -errno;
376 : }
377 :
378 1 : if (chattr_flags != 0)
379 0 : (void) chattr_fd(fdt, chattr_flags, (unsigned) -1);
380 :
381 1 : r = copy_file_fd(from, fdt, true);
382 1 : if (r < 0) {
383 0 : close(fdt);
384 0 : unlink(to);
385 0 : return r;
386 : }
387 :
388 1 : if (close(fdt) < 0) {
389 0 : unlink_noerrno(to);
390 0 : return -errno;
391 : }
392 :
393 1 : return 0;
394 : }
395 :
396 0 : int copy_file_atomic(const char *from, const char *to, mode_t mode, bool replace, unsigned chattr_flags) {
397 0 : _cleanup_free_ char *t = NULL;
398 : int r;
399 :
400 0 : assert(from);
401 0 : assert(to);
402 :
403 0 : r = tempfn_random(to, NULL, &t);
404 0 : if (r < 0)
405 0 : return r;
406 :
407 0 : r = copy_file(from, t, O_NOFOLLOW|O_EXCL, mode, chattr_flags);
408 0 : if (r < 0)
409 0 : return r;
410 :
411 0 : if (replace) {
412 0 : r = renameat(AT_FDCWD, t, AT_FDCWD, to);
413 0 : if (r < 0)
414 0 : r = -errno;
415 : } else
416 0 : r = rename_noreplace(AT_FDCWD, t, AT_FDCWD, to);
417 0 : if (r < 0) {
418 0 : (void) unlink_noerrno(t);
419 0 : return r;
420 : }
421 :
422 0 : return 0;
423 : }
424 :
425 2 : int copy_times(int fdf, int fdt) {
426 : struct timespec ut[2];
427 : struct stat st;
428 2 : usec_t crtime = 0;
429 :
430 2 : assert(fdf >= 0);
431 2 : assert(fdt >= 0);
432 :
433 2 : if (fstat(fdf, &st) < 0)
434 0 : return -errno;
435 :
436 2 : ut[0] = st.st_atim;
437 2 : ut[1] = st.st_mtim;
438 :
439 2 : if (futimens(fdt, ut) < 0)
440 0 : return -errno;
441 :
442 2 : if (fd_getcrtime(fdf, &crtime) >= 0)
443 0 : (void) fd_setcrtime(fdt, crtime);
444 :
445 2 : return 0;
446 : }
447 :
448 12 : int copy_xattr(int fdf, int fdt) {
449 24 : _cleanup_free_ char *bufa = NULL, *bufb = NULL;
450 12 : size_t sza = 100, szb = 100;
451 : ssize_t n;
452 12 : int ret = 0;
453 : const char *p;
454 :
455 : for (;;) {
456 12 : bufa = malloc(sza);
457 12 : if (!bufa)
458 0 : return -ENOMEM;
459 :
460 12 : n = flistxattr(fdf, bufa, sza);
461 12 : if (n == 0)
462 12 : return 0;
463 0 : if (n > 0)
464 0 : break;
465 0 : if (errno != ERANGE)
466 0 : return -errno;
467 :
468 0 : sza *= 2;
469 :
470 0 : free(bufa);
471 0 : bufa = NULL;
472 0 : }
473 :
474 0 : p = bufa;
475 0 : while (n > 0) {
476 : size_t l;
477 :
478 0 : l = strlen(p);
479 0 : assert(l < (size_t) n);
480 :
481 0 : if (startswith(p, "user.")) {
482 : ssize_t m;
483 :
484 0 : if (!bufb) {
485 0 : bufb = malloc(szb);
486 0 : if (!bufb)
487 0 : return -ENOMEM;
488 : }
489 :
490 0 : m = fgetxattr(fdf, p, bufb, szb);
491 0 : if (m < 0) {
492 0 : if (errno == ERANGE) {
493 0 : szb *= 2;
494 0 : free(bufb);
495 0 : bufb = NULL;
496 0 : continue;
497 : }
498 :
499 0 : return -errno;
500 : }
501 :
502 0 : if (fsetxattr(fdt, p, bufb, m, 0) < 0)
503 0 : ret = -errno;
504 : }
505 :
506 0 : p += l + 1;
507 0 : n -= l + 1;
508 : }
509 :
510 0 : return ret;
511 : }
|