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