]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/copy.c
Merge pull request #197 from dvdhrm/hashmap
[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 "copy.h"
28
29 #define COPY_BUFFER_SIZE (16*1024)
30
31 int copy_bytes(int fdf, int fdt, off_t max_bytes, bool try_reflink) {
32 bool try_sendfile = true;
33 int r;
34
35 assert(fdf >= 0);
36 assert(fdt >= 0);
37
38 /* Try btrfs reflinks first. */
39 if (try_reflink && max_bytes == (off_t) -1) {
40 r = btrfs_reflink(fdf, fdt);
41 if (r >= 0)
42 return r;
43 }
44
45 for (;;) {
46 size_t m = COPY_BUFFER_SIZE;
47 ssize_t n;
48
49 if (max_bytes != (off_t) -1) {
50
51 if (max_bytes <= 0)
52 return -EFBIG;
53
54 if ((off_t) m > max_bytes)
55 m = (size_t) max_bytes;
56 }
57
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];
78
79 n = read(fdf, buf, m);
80 if (n < 0)
81 return -errno;
82 if (n == 0) /* EOF */
83 break;
84
85 r = loop_write(fdt, buf, (size_t) n, false);
86 if (r < 0)
87 return r;
88 }
89
90 next:
91 if (max_bytes != (off_t) -1) {
92 assert(max_bytes >= n);
93 max_bytes -= n;
94 }
95 }
96
97 return 0;
98 }
99
100 static 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
112 if (symlinkat(target, dt, to) < 0)
113 return -errno;
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
121 static 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;
123 struct timespec ts[2];
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);
135 if (fdt < 0)
136 return -errno;
137
138 r = copy_bytes(fdf, fdt, (off_t) -1, true);
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
150 ts[0] = st->st_atim;
151 ts[1] = st->st_mtim;
152 (void) futimens(fdt, ts);
153
154 (void) copy_xattr(fdf, fdt);
155
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
167 static 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);
175 if (r < 0)
176 return -errno;
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
187 static 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);
195 if (r < 0)
196 return -errno;
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
207 static 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
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
222 assert(st);
223 assert(to);
224
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);
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;
238 else if (errno == EEXIST && merge)
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
247 r = 0;
248
249 if (created) {
250 struct timespec ut[2] = {
251 st->st_atim,
252 st->st_mtim
253 };
254
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;
260
261 (void) futimens(fdt, ut);
262 (void) copy_xattr(dirfd(d), fdt);
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))
280 q = fd_copy_directory(dirfd(d), de->d_name, &buf, fdt, de->d_name, original_device, merge);
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
288 q = -EOPNOTSUPP;
289
290 if (q == -EEXIST && merge)
291 q = 0;
292
293 if (q < 0)
294 r = q;
295 }
296
297 return r;
298 }
299
300 int copy_tree_at(int fdf, const char *from, int fdt, const char *to, bool merge) {
301 struct stat st;
302
303 assert(from);
304 assert(to);
305
306 if (fstatat(fdf, from, &st, AT_SYMLINK_NOFOLLOW) < 0)
307 return -errno;
308
309 if (S_ISREG(st.st_mode))
310 return fd_copy_regular(fdf, from, &st, fdt, to);
311 else if (S_ISDIR(st.st_mode))
312 return fd_copy_directory(fdf, from, &st, fdt, to, st.st_dev, merge);
313 else if (S_ISLNK(st.st_mode))
314 return fd_copy_symlink(fdf, from, &st, fdt, to);
315 else if (S_ISFIFO(st.st_mode))
316 return fd_copy_fifo(fdf, from, &st, fdt, to);
317 else if (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode))
318 return fd_copy_node(fdf, from, &st, fdt, to);
319 else
320 return -EOPNOTSUPP;
321 }
322
323 int copy_tree(const char *from, const char *to, bool merge) {
324 return copy_tree_at(AT_FDCWD, from, AT_FDCWD, to, merge);
325 }
326
327 int copy_directory_fd(int dirfd, const char *to, bool merge) {
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
343 int copy_file_fd(const char *from, int fdt, bool try_reflink) {
344 _cleanup_close_ int fdf = -1;
345 int r;
346
347 assert(from);
348 assert(fdt >= 0);
349
350 fdf = open(from, O_RDONLY|O_CLOEXEC|O_NOCTTY);
351 if (fdf < 0)
352 return -errno;
353
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;
360 }
361
362 int copy_file(const char *from, const char *to, int flags, mode_t mode, unsigned chattr_flags) {
363 int fdt = -1, r;
364
365 assert(from);
366 assert(to);
367
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 }
373
374 if (chattr_flags != 0)
375 (void) chattr_fd(fdt, chattr_flags, (unsigned) -1);
376
377 r = copy_file_fd(from, fdt, true);
378 if (r < 0) {
379 close(fdt);
380 unlink(to);
381 return r;
382 }
383
384 if (close(fdt) < 0) {
385 unlink_noerrno(to);
386 return -errno;
387 }
388
389 return 0;
390 }
391
392 int copy_file_atomic(const char *from, const char *to, mode_t mode, bool replace, unsigned chattr_flags) {
393 _cleanup_free_ char *t = NULL;
394 int r;
395
396 assert(from);
397 assert(to);
398
399 r = tempfn_random(to, NULL, &t);
400 if (r < 0)
401 return r;
402
403 r = copy_file(from, t, O_NOFOLLOW|O_EXCL, mode, chattr_flags);
404 if (r < 0)
405 return r;
406
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;
416 }
417
418 return 0;
419 }
420
421 int copy_times(int fdf, int fdt) {
422 struct timespec ut[2];
423 struct stat st;
424 usec_t crtime = 0;
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
444 int 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 }