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