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