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