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