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