]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/basic/copy.c
NEWS: add some information about v227
[thirdparty/systemd.git] / src / basic / copy.c
CommitLineData
849958d1
LP
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
cda134ab 22#include <sys/sendfile.h>
e6bd041c 23#include <sys/xattr.h>
cda134ab 24
849958d1 25#include "util.h"
d7c7c334 26#include "btrfs-util.h"
8420fa3a 27#include "strv.h"
849958d1
LP
28#include "copy.h"
29
f2cbe59e
LP
30#define COPY_BUFFER_SIZE (16*1024)
31
59f448cf 32int copy_bytes(int fdf, int fdt, uint64_t max_bytes, bool try_reflink) {
81d20007 33 bool try_sendfile = true, try_splice = true;
0254b455 34 int r;
cda134ab 35
849958d1
LP
36 assert(fdf >= 0);
37 assert(fdt >= 0);
38
0254b455 39 /* Try btrfs reflinks first. */
59f448cf 40 if (try_reflink && max_bytes == (uint64_t) -1) {
0254b455
LP
41 r = btrfs_reflink(fdf, fdt);
42 if (r >= 0)
7430ec6a 43 return r;
0254b455
LP
44 }
45
849958d1 46 for (;;) {
f2cbe59e 47 size_t m = COPY_BUFFER_SIZE;
cda134ab 48 ssize_t n;
93240d3a 49
59f448cf 50 if (max_bytes != (uint64_t) -1) {
93240d3a
LP
51
52 if (max_bytes <= 0)
84ee0960 53 return -EFBIG;
93240d3a 54
59f448cf 55 if ((uint64_t) m > max_bytes)
93240d3a
LP
56 m = (size_t) max_bytes;
57 }
58
cda134ab
LP
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)
81d20007
LP
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! */
cda134ab
LP
89 goto next;
90 }
91
92 /* As a fallback just copy bits by hand */
93 {
94 char buf[m];
849958d1 95
cda134ab
LP
96 n = read(fdf, buf, m);
97 if (n < 0)
98 return -errno;
99 if (n == 0) /* EOF */
100 break;
101
0254b455 102 r = loop_write(fdt, buf, (size_t) n, false);
553acb7b
ZJS
103 if (r < 0)
104 return r;
cda134ab 105 }
93240d3a 106
cda134ab 107 next:
59f448cf
LP
108 if (max_bytes != (uint64_t) -1) {
109 assert(max_bytes >= (uint64_t) n);
93240d3a
LP
110 max_bytes -= n;
111 }
849958d1
LP
112 }
113
114 return 0;
115}
116
117static 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
e156347e 129 if (symlinkat(target, dt, to) < 0)
849958d1 130 return -errno;
849958d1
LP
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
138static 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;
ebd93cb6 140 struct timespec ts[2];
849958d1
LP
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);
e156347e 152 if (fdt < 0)
849958d1 153 return -errno;
849958d1 154
59f448cf 155 r = copy_bytes(fdf, fdt, (uint64_t) -1, true);
849958d1
LP
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
ebd93cb6
LP
167 ts[0] = st->st_atim;
168 ts[1] = st->st_mtim;
169 (void) futimens(fdt, ts);
170
e6bd041c
LP
171 (void) copy_xattr(fdf, fdt);
172
849958d1
LP
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
184static 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);
e156347e 192 if (r < 0)
849958d1 193 return -errno;
849958d1
LP
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
204static 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);
e156347e 212 if (r < 0)
849958d1 213 return -errno;
849958d1
LP
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
d7c7c334
LP
224static 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
849958d1
LP
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
849958d1
LP
239 assert(st);
240 assert(to);
241
d7c7c334
LP
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);
849958d1
LP
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;
e156347e 255 else if (errno == EEXIST && merge)
849958d1
LP
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
2c455af4
LP
264 r = 0;
265
849958d1 266 if (created) {
ebd93cb6
LP
267 struct timespec ut[2] = {
268 st->st_atim,
269 st->st_mtim
270 };
271
849958d1
LP
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;
e6bd041c 277
ebd93cb6
LP
278 (void) futimens(fdt, ut);
279 (void) copy_xattr(dirfd(d), fdt);
849958d1
LP
280 }
281
8420fa3a 282 FOREACH_DIRENT_ALL(de, d, return -errno) {
849958d1
LP
283 struct stat buf;
284 int q;
285
8420fa3a
LP
286 if (STR_IN_SET(de->d_name, ".", ".."))
287 continue;
288
849958d1
LP
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))
e156347e 300 q = fd_copy_directory(dirfd(d), de->d_name, &buf, fdt, de->d_name, original_device, merge);
849958d1
LP
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
15411c0c 308 q = -EOPNOTSUPP;
849958d1 309
e156347e
LP
310 if (q == -EEXIST && merge)
311 q = 0;
312
849958d1
LP
313 if (q < 0)
314 r = q;
315 }
316
317 return r;
318}
319
f2cbe59e 320int copy_tree_at(int fdf, const char *from, int fdt, const char *to, bool merge) {
849958d1
LP
321 struct stat st;
322
323 assert(from);
324 assert(to);
325
f2cbe59e 326 if (fstatat(fdf, from, &st, AT_SYMLINK_NOFOLLOW) < 0)
849958d1
LP
327 return -errno;
328
329 if (S_ISREG(st.st_mode))
f2cbe59e 330 return fd_copy_regular(fdf, from, &st, fdt, to);
849958d1 331 else if (S_ISDIR(st.st_mode))
f2cbe59e 332 return fd_copy_directory(fdf, from, &st, fdt, to, st.st_dev, merge);
849958d1 333 else if (S_ISLNK(st.st_mode))
f2cbe59e 334 return fd_copy_symlink(fdf, from, &st, fdt, to);
849958d1 335 else if (S_ISFIFO(st.st_mode))
f2cbe59e 336 return fd_copy_fifo(fdf, from, &st, fdt, to);
849958d1 337 else if (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode))
f2cbe59e 338 return fd_copy_node(fdf, from, &st, fdt, to);
849958d1 339 else
15411c0c 340 return -EOPNOTSUPP;
849958d1
LP
341}
342
f2cbe59e
LP
343int copy_tree(const char *from, const char *to, bool merge) {
344 return copy_tree_at(AT_FDCWD, from, AT_FDCWD, to, merge);
345}
346
347int copy_directory_fd(int dirfd, const char *to, bool merge) {
d7c7c334
LP
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
7430ec6a 363int copy_file_fd(const char *from, int fdt, bool try_reflink) {
cda134ab 364 _cleanup_close_ int fdf = -1;
e6bd041c 365 int r;
849958d1
LP
366
367 assert(from);
cda134ab 368 assert(fdt >= 0);
849958d1
LP
369
370 fdf = open(from, O_RDONLY|O_CLOEXEC|O_NOCTTY);
371 if (fdf < 0)
372 return -errno;
373
59f448cf 374 r = copy_bytes(fdf, fdt, (uint64_t) -1, try_reflink);
e6bd041c
LP
375
376 (void) copy_times(fdf, fdt);
377 (void) copy_xattr(fdf, fdt);
378
379 return r;
cda134ab
LP
380}
381
45030287 382int copy_file(const char *from, const char *to, int flags, mode_t mode, unsigned chattr_flags) {
a7f7d1bd 383 int fdt = -1, r;
cda134ab
LP
384
385 assert(from);
386 assert(to);
387
ebd93cb6
LP
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 }
849958d1 393
f2068bcc 394 if (chattr_flags != 0)
1ed8f8c1 395 (void) chattr_fd(fdt, chattr_flags, (unsigned) -1);
f2068bcc 396
7430ec6a 397 r = copy_file_fd(from, fdt, true);
849958d1 398 if (r < 0) {
cda134ab 399 close(fdt);
849958d1
LP
400 unlink(to);
401 return r;
402 }
403
cda134ab
LP
404 if (close(fdt) < 0) {
405 unlink_noerrno(to);
406 return -errno;
849958d1
LP
407 }
408
409 return 0;
410}
e6bd041c 411
45030287 412int copy_file_atomic(const char *from, const char *to, mode_t mode, bool replace, unsigned chattr_flags) {
a7f7d1bd 413 _cleanup_free_ char *t = NULL;
ebd93cb6
LP
414 int r;
415
416 assert(from);
417 assert(to);
418
14bcf25c 419 r = tempfn_random(to, NULL, &t);
ebd93cb6
LP
420 if (r < 0)
421 return r;
422
f2068bcc 423 r = copy_file(from, t, O_NOFOLLOW|O_EXCL, mode, chattr_flags);
ebd93cb6
LP
424 if (r < 0)
425 return r;
426
f85ef957
AC
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;
ebd93cb6
LP
436 }
437
438 return 0;
439}
440
e6bd041c
LP
441int copy_times(int fdf, int fdt) {
442 struct timespec ut[2];
443 struct stat st;
a7f7d1bd 444 usec_t crtime = 0;
e6bd041c
LP
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
464int 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
97b11eed 486 bufa = mfree(bufa);
e6bd041c
LP
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;
97b11eed 509 bufb = mfree(bufb);
e6bd041c
LP
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}