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