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