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