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