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