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