]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/copy.c
path-util: fix path_is_mount_point() for symlinks
[thirdparty/systemd.git] / src / shared / copy.c
CommitLineData
849958d1
LP
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
cda134ab 22#include <sys/sendfile.h>
e6bd041c 23#include <sys/xattr.h>
cda134ab 24
849958d1 25#include "util.h"
d7c7c334 26#include "btrfs-util.h"
849958d1
LP
27#include "copy.h"
28
f2cbe59e
LP
29#define COPY_BUFFER_SIZE (16*1024)
30
7430ec6a 31int copy_bytes(int fdf, int fdt, off_t max_bytes, bool try_reflink) {
cda134ab 32 bool try_sendfile = true;
0254b455 33 int r;
cda134ab 34
849958d1
LP
35 assert(fdf >= 0);
36 assert(fdt >= 0);
37
0254b455 38 /* Try btrfs reflinks first. */
7430ec6a 39 if (try_reflink && max_bytes == (off_t) -1) {
0254b455
LP
40 r = btrfs_reflink(fdf, fdt);
41 if (r >= 0)
7430ec6a 42 return r;
0254b455
LP
43 }
44
849958d1 45 for (;;) {
f2cbe59e 46 size_t m = COPY_BUFFER_SIZE;
cda134ab 47 ssize_t n;
93240d3a
LP
48
49 if (max_bytes != (off_t) -1) {
50
51 if (max_bytes <= 0)
84ee0960 52 return -EFBIG;
93240d3a
LP
53
54 if ((off_t) m > max_bytes)
55 m = (size_t) max_bytes;
56 }
57
cda134ab
LP
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];
849958d1 78
cda134ab
LP
79 n = read(fdf, buf, m);
80 if (n < 0)
81 return -errno;
82 if (n == 0) /* EOF */
83 break;
84
0254b455 85 r = loop_write(fdt, buf, (size_t) n, false);
553acb7b
ZJS
86 if (r < 0)
87 return r;
cda134ab 88 }
93240d3a 89
cda134ab 90 next:
93240d3a
LP
91 if (max_bytes != (off_t) -1) {
92 assert(max_bytes >= n);
93 max_bytes -= n;
94 }
849958d1
LP
95 }
96
97 return 0;
98}
99
100static 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
e156347e 112 if (symlinkat(target, dt, to) < 0)
849958d1 113 return -errno;
849958d1
LP
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
121static 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;
ebd93cb6 123 struct timespec ts[2];
849958d1
LP
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);
e156347e 135 if (fdt < 0)
849958d1 136 return -errno;
849958d1 137
7430ec6a 138 r = copy_bytes(fdf, fdt, (off_t) -1, true);
849958d1
LP
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
ebd93cb6
LP
150 ts[0] = st->st_atim;
151 ts[1] = st->st_mtim;
152 (void) futimens(fdt, ts);
153
e6bd041c
LP
154 (void) copy_xattr(fdf, fdt);
155
849958d1
LP
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
167static 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);
e156347e 175 if (r < 0)
849958d1 176 return -errno;
849958d1
LP
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
187static 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);
e156347e 195 if (r < 0)
849958d1 196 return -errno;
849958d1
LP
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
d7c7c334
LP
207static 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
849958d1
LP
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
849958d1
LP
222 assert(st);
223 assert(to);
224
d7c7c334
LP
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);
849958d1
LP
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;
e156347e 238 else if (errno == EEXIST && merge)
849958d1
LP
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
2c455af4
LP
247 r = 0;
248
849958d1 249 if (created) {
ebd93cb6
LP
250 struct timespec ut[2] = {
251 st->st_atim,
252 st->st_mtim
253 };
254
849958d1
LP
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;
e6bd041c 260
ebd93cb6
LP
261 (void) futimens(fdt, ut);
262 (void) copy_xattr(dirfd(d), fdt);
849958d1
LP
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))
e156347e 280 q = fd_copy_directory(dirfd(d), de->d_name, &buf, fdt, de->d_name, original_device, merge);
849958d1
LP
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 = -ENOTSUP;
289
e156347e
LP
290 if (q == -EEXIST && merge)
291 q = 0;
292
849958d1
LP
293 if (q < 0)
294 r = q;
295 }
296
297 return r;
298}
299
f2cbe59e 300int copy_tree_at(int fdf, const char *from, int fdt, const char *to, bool merge) {
849958d1
LP
301 struct stat st;
302
303 assert(from);
304 assert(to);
305
f2cbe59e 306 if (fstatat(fdf, from, &st, AT_SYMLINK_NOFOLLOW) < 0)
849958d1
LP
307 return -errno;
308
309 if (S_ISREG(st.st_mode))
f2cbe59e 310 return fd_copy_regular(fdf, from, &st, fdt, to);
849958d1 311 else if (S_ISDIR(st.st_mode))
f2cbe59e 312 return fd_copy_directory(fdf, from, &st, fdt, to, st.st_dev, merge);
849958d1 313 else if (S_ISLNK(st.st_mode))
f2cbe59e 314 return fd_copy_symlink(fdf, from, &st, fdt, to);
849958d1 315 else if (S_ISFIFO(st.st_mode))
f2cbe59e 316 return fd_copy_fifo(fdf, from, &st, fdt, to);
849958d1 317 else if (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode))
f2cbe59e 318 return fd_copy_node(fdf, from, &st, fdt, to);
849958d1
LP
319 else
320 return -ENOTSUP;
321}
322
f2cbe59e
LP
323int copy_tree(const char *from, const char *to, bool merge) {
324 return copy_tree_at(AT_FDCWD, from, AT_FDCWD, to, merge);
325}
326
327int copy_directory_fd(int dirfd, const char *to, bool merge) {
d7c7c334
LP
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
7430ec6a 343int copy_file_fd(const char *from, int fdt, bool try_reflink) {
cda134ab 344 _cleanup_close_ int fdf = -1;
e6bd041c 345 int r;
849958d1
LP
346
347 assert(from);
cda134ab 348 assert(fdt >= 0);
849958d1
LP
349
350 fdf = open(from, O_RDONLY|O_CLOEXEC|O_NOCTTY);
351 if (fdf < 0)
352 return -errno;
353
e6bd041c
LP
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;
cda134ab
LP
360}
361
45030287 362int copy_file(const char *from, const char *to, int flags, mode_t mode, unsigned chattr_flags) {
cda134ab
LP
363 int fdt, r;
364
365 assert(from);
366 assert(to);
367
ebd93cb6
LP
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 }
849958d1 373
f2068bcc
LP
374 if (chattr_flags != 0)
375 (void) chattr_fd(fdt, true, chattr_flags);
376
7430ec6a 377 r = copy_file_fd(from, fdt, true);
849958d1 378 if (r < 0) {
cda134ab 379 close(fdt);
849958d1
LP
380 unlink(to);
381 return r;
382 }
383
cda134ab
LP
384 if (close(fdt) < 0) {
385 unlink_noerrno(to);
386 return -errno;
849958d1
LP
387 }
388
389 return 0;
390}
e6bd041c 391
45030287 392int copy_file_atomic(const char *from, const char *to, mode_t mode, bool replace, unsigned chattr_flags) {
ebd93cb6
LP
393 _cleanup_free_ char *t;
394 int r;
395
396 assert(from);
397 assert(to);
398
399 r = tempfn_random(to, &t);
400 if (r < 0)
401 return r;
402
f2068bcc 403 r = copy_file(from, t, O_NOFOLLOW|O_EXCL, mode, chattr_flags);
ebd93cb6
LP
404 if (r < 0)
405 return r;
406
407 if (renameat2(AT_FDCWD, t, AT_FDCWD, to, replace ? 0 : RENAME_NOREPLACE) < 0) {
408 unlink_noerrno(t);
409 return -errno;
410 }
411
412 return 0;
413}
414
e6bd041c
LP
415int copy_times(int fdf, int fdt) {
416 struct timespec ut[2];
417 struct stat st;
418 usec_t crtime;
419
420 assert(fdf >= 0);
421 assert(fdt >= 0);
422
423 if (fstat(fdf, &st) < 0)
424 return -errno;
425
426 ut[0] = st.st_atim;
427 ut[1] = st.st_mtim;
428
429 if (futimens(fdt, ut) < 0)
430 return -errno;
431
432 if (fd_getcrtime(fdf, &crtime) >= 0)
433 (void) fd_setcrtime(fdt, crtime);
434
435 return 0;
436}
437
438int copy_xattr(int fdf, int fdt) {
439 _cleanup_free_ char *bufa = NULL, *bufb = NULL;
440 size_t sza = 100, szb = 100;
441 ssize_t n;
442 int ret = 0;
443 const char *p;
444
445 for (;;) {
446 bufa = malloc(sza);
447 if (!bufa)
448 return -ENOMEM;
449
450 n = flistxattr(fdf, bufa, sza);
451 if (n == 0)
452 return 0;
453 if (n > 0)
454 break;
455 if (errno != ERANGE)
456 return -errno;
457
458 sza *= 2;
459
460 free(bufa);
461 bufa = NULL;
462 }
463
464 p = bufa;
465 while (n > 0) {
466 size_t l;
467
468 l = strlen(p);
469 assert(l < (size_t) n);
470
471 if (startswith(p, "user.")) {
472 ssize_t m;
473
474 if (!bufb) {
475 bufb = malloc(szb);
476 if (!bufb)
477 return -ENOMEM;
478 }
479
480 m = fgetxattr(fdf, p, bufb, szb);
481 if (m < 0) {
482 if (errno == ERANGE) {
483 szb *= 2;
484 free(bufb);
485 bufb = NULL;
486 continue;
487 }
488
489 return -errno;
490 }
491
492 if (fsetxattr(fdt, p, bufb, m, 0) < 0)
493 ret = -errno;
494 }
495
496 p += l + 1;
497 n -= l + 1;
498 }
499
500 return ret;
501}