]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/copy.c
Merge pull request #692 from poettering/fd-copy-directory-all
[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, off_t max_bytes, bool try_reflink) {
33 bool try_sendfile = true;
34 int r;
35
36 assert(fdf >= 0);
37 assert(fdt >= 0);
38
39 /* Try btrfs reflinks first. */
40 if (try_reflink && max_bytes == (off_t) -1) {
41 r = btrfs_reflink(fdf, fdt);
42 if (r >= 0)
43 return r;
44 }
45
46 for (;;) {
47 size_t m = COPY_BUFFER_SIZE;
48 ssize_t n;
49
50 if (max_bytes != (off_t) -1) {
51
52 if (max_bytes <= 0)
53 return -EFBIG;
54
55 if ((off_t) m > max_bytes)
56 m = (size_t) max_bytes;
57 }
58
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];
79
80 n = read(fdf, buf, m);
81 if (n < 0)
82 return -errno;
83 if (n == 0) /* EOF */
84 break;
85
86 r = loop_write(fdt, buf, (size_t) n, false);
87 if (r < 0)
88 return r;
89 }
90
91 next:
92 if (max_bytes != (off_t) -1) {
93 assert(max_bytes >= n);
94 max_bytes -= n;
95 }
96 }
97
98 return 0;
99 }
100
101 static 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
113 if (symlinkat(target, dt, to) < 0)
114 return -errno;
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
122 static 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;
124 struct timespec ts[2];
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);
136 if (fdt < 0)
137 return -errno;
138
139 r = copy_bytes(fdf, fdt, (off_t) -1, true);
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
151 ts[0] = st->st_atim;
152 ts[1] = st->st_mtim;
153 (void) futimens(fdt, ts);
154
155 (void) copy_xattr(fdf, fdt);
156
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
168 static 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);
176 if (r < 0)
177 return -errno;
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
188 static 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);
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_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
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
223 assert(st);
224 assert(to);
225
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);
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;
239 else if (errno == EEXIST && merge)
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
248 r = 0;
249
250 if (created) {
251 struct timespec ut[2] = {
252 st->st_atim,
253 st->st_mtim
254 };
255
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;
261
262 (void) futimens(fdt, ut);
263 (void) copy_xattr(dirfd(d), fdt);
264 }
265
266 FOREACH_DIRENT_ALL(de, d, return -errno) {
267 struct stat buf;
268 int q;
269
270 if (STR_IN_SET(de->d_name, ".", ".."))
271 continue;
272
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))
284 q = fd_copy_directory(dirfd(d), de->d_name, &buf, fdt, de->d_name, original_device, merge);
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
292 q = -EOPNOTSUPP;
293
294 if (q == -EEXIST && merge)
295 q = 0;
296
297 if (q < 0)
298 r = q;
299 }
300
301 return r;
302 }
303
304 int copy_tree_at(int fdf, const char *from, int fdt, const char *to, bool merge) {
305 struct stat st;
306
307 assert(from);
308 assert(to);
309
310 if (fstatat(fdf, from, &st, AT_SYMLINK_NOFOLLOW) < 0)
311 return -errno;
312
313 if (S_ISREG(st.st_mode))
314 return fd_copy_regular(fdf, from, &st, fdt, to);
315 else if (S_ISDIR(st.st_mode))
316 return fd_copy_directory(fdf, from, &st, fdt, to, st.st_dev, merge);
317 else if (S_ISLNK(st.st_mode))
318 return fd_copy_symlink(fdf, from, &st, fdt, to);
319 else if (S_ISFIFO(st.st_mode))
320 return fd_copy_fifo(fdf, from, &st, fdt, to);
321 else if (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode))
322 return fd_copy_node(fdf, from, &st, fdt, to);
323 else
324 return -EOPNOTSUPP;
325 }
326
327 int copy_tree(const char *from, const char *to, bool merge) {
328 return copy_tree_at(AT_FDCWD, from, AT_FDCWD, to, merge);
329 }
330
331 int copy_directory_fd(int dirfd, const char *to, bool merge) {
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
347 int copy_file_fd(const char *from, int fdt, bool try_reflink) {
348 _cleanup_close_ int fdf = -1;
349 int r;
350
351 assert(from);
352 assert(fdt >= 0);
353
354 fdf = open(from, O_RDONLY|O_CLOEXEC|O_NOCTTY);
355 if (fdf < 0)
356 return -errno;
357
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;
364 }
365
366 int copy_file(const char *from, const char *to, int flags, mode_t mode, unsigned chattr_flags) {
367 int fdt = -1, r;
368
369 assert(from);
370 assert(to);
371
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 }
377
378 if (chattr_flags != 0)
379 (void) chattr_fd(fdt, chattr_flags, (unsigned) -1);
380
381 r = copy_file_fd(from, fdt, true);
382 if (r < 0) {
383 close(fdt);
384 unlink(to);
385 return r;
386 }
387
388 if (close(fdt) < 0) {
389 unlink_noerrno(to);
390 return -errno;
391 }
392
393 return 0;
394 }
395
396 int copy_file_atomic(const char *from, const char *to, mode_t mode, bool replace, unsigned chattr_flags) {
397 _cleanup_free_ char *t = NULL;
398 int r;
399
400 assert(from);
401 assert(to);
402
403 r = tempfn_random(to, NULL, &t);
404 if (r < 0)
405 return r;
406
407 r = copy_file(from, t, O_NOFOLLOW|O_EXCL, mode, chattr_flags);
408 if (r < 0)
409 return r;
410
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;
420 }
421
422 return 0;
423 }
424
425 int copy_times(int fdf, int fdt) {
426 struct timespec ut[2];
427 struct stat st;
428 usec_t crtime = 0;
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
448 int 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
470 free(bufa);
471 bufa = NULL;
472 }
473
474 p = bufa;
475 while (n > 0) {
476 size_t l;
477
478 l = strlen(p);
479 assert(l < (size_t) n);
480
481 if (startswith(p, "user.")) {
482 ssize_t m;
483
484 if (!bufb) {
485 bufb = malloc(szb);
486 if (!bufb)
487 return -ENOMEM;
488 }
489
490 m = fgetxattr(fdf, p, bufb, szb);
491 if (m < 0) {
492 if (errno == ERANGE) {
493 szb *= 2;
494 free(bufb);
495 bufb = NULL;
496 continue;
497 }
498
499 return -errno;
500 }
501
502 if (fsetxattr(fdt, p, bufb, m, 0) < 0)
503 ret = -errno;
504 }
505
506 p += l + 1;
507 n -= l + 1;
508 }
509
510 return ret;
511 }