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