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