chiark / gitweb /
machined: support non-btrfs file systems with "machinectl clone"
[elogind.git] / src / basic / copy.c
1 /***
2   This file is part of systemd.
3
4   Copyright 2014 Lennart Poettering
5
6   systemd is free software; you can redistribute it and/or modify it
7   under the terms of the GNU Lesser General Public License as published by
8   the Free Software Foundation; either version 2.1 of the License, or
9   (at your option) any later version.
10
11   systemd is distributed in the hope that it will be useful, but
12   WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14   Lesser General Public License for more details.
15
16   You should have received a copy of the GNU Lesser General Public License
17   along with systemd; If not, see <http://www.gnu.org/licenses/>.
18 ***/
19
20 //#include <dirent.h>
21 //#include <errno.h>
22 //#include <fcntl.h>
23 //#include <stddef.h>
24 //#include <stdio.h>
25 //#include <stdlib.h>
26 //#include <string.h>
27 #include <sys/sendfile.h>
28 //#include <sys/stat.h>
29 #include <sys/xattr.h>
30 //#include <time.h>
31 //#include <unistd.h>
32
33 //#include "alloc-util.h"
34 //#include "btrfs-util.h"
35 //#include "chattr-util.h"
36 #include "copy.h"
37 //#include "dirent-util.h"
38 //#include "fd-util.h"
39 //#include "fileio.h"
40 //#include "fs-util.h"
41 #include "io-util.h"
42 //#include "macro.h"
43 //#include "string-util.h"
44 #include "strv.h"
45 #include "time-util.h"
46 //#include "umask-util.h"
47 //#include "xattr-util.h"
48
49 #define COPY_BUFFER_SIZE (16*1024)
50
51 int copy_bytes(int fdf, int fdt, uint64_t max_bytes, bool try_reflink) {
52         bool try_sendfile = true, try_splice = true;
53         int r;
54
55         assert(fdf >= 0);
56         assert(fdt >= 0);
57 #if 0 /// UNNEEDED by elogind
58         /* Try btrfs reflinks first. */
59         if (try_reflink &&
60             max_bytes == (uint64_t) -1 &&
61             lseek(fdf, 0, SEEK_CUR) == 0 &&
62             lseek(fdt, 0, SEEK_CUR) == 0) {
63
64                 r = btrfs_reflink(fdf, fdt);
65                 if (r >= 0)
66                         return 0; /* we copied the whole thing, hence hit EOF, return 0 */
67         }
68 #endif // 0
69         for (;;) {
70                 size_t m = COPY_BUFFER_SIZE;
71                 ssize_t n;
72
73                 if (max_bytes != (uint64_t) -1) {
74
75                         if (max_bytes <= 0)
76                                 return 1; /* return > 0 if we hit the max_bytes limit */
77
78                         if ((uint64_t) m > max_bytes)
79                                 m = (size_t) max_bytes;
80                 }
81
82                 /* First try sendfile(), unless we already tried */
83                 if (try_sendfile) {
84
85                         n = sendfile(fdt, fdf, NULL, m);
86                         if (n < 0) {
87                                 if (errno != EINVAL && errno != ENOSYS)
88                                         return -errno;
89
90                                 try_sendfile = false;
91                                 /* use fallback below */
92                         } else if (n == 0) /* EOF */
93                                 break;
94                         else if (n > 0)
95                                 /* Success! */
96                                 goto next;
97                 }
98
99                 /* The try splice, unless we already tried */
100                 if (try_splice) {
101                         n  = splice(fdf, NULL, fdt, NULL, m, 0);
102                         if (n < 0) {
103                                 if (errno != EINVAL && errno != ENOSYS)
104                                         return -errno;
105
106                                 try_splice = false;
107                                 /* use fallback below */
108                         } else if (n == 0) /* EOF */
109                                 break;
110                         else if (n > 0)
111                                 /* Success! */
112                                 goto next;
113                 }
114
115                 /* As a fallback just copy bits by hand */
116                 {
117                         uint8_t buf[m];
118
119                         n = read(fdf, buf, m);
120                         if (n < 0)
121                                 return -errno;
122                         if (n == 0) /* EOF */
123                                 break;
124
125                         r = loop_write(fdt, buf, (size_t) n, false);
126                         if (r < 0)
127                                 return r;
128                 }
129
130         next:
131                 if (max_bytes != (uint64_t) -1) {
132                         assert(max_bytes >= (uint64_t) n);
133                         max_bytes -= n;
134                 }
135         }
136
137         return 0; /* return 0 if we hit EOF earlier than the size limit */
138 }
139
140 #if 0 /// UNNEEDED by elogind
141 static int fd_copy_symlink(int df, const char *from, const struct stat *st, int dt, const char *to) {
142         _cleanup_free_ char *target = NULL;
143         int r;
144
145         assert(from);
146         assert(st);
147         assert(to);
148
149         r = readlinkat_malloc(df, from, &target);
150         if (r < 0)
151                 return r;
152
153         if (symlinkat(target, dt, to) < 0)
154                 return -errno;
155
156         if (fchownat(dt, to, st->st_uid, st->st_gid, AT_SYMLINK_NOFOLLOW) < 0)
157                 return -errno;
158
159         return 0;
160 }
161
162 static int fd_copy_regular(int df, const char *from, const struct stat *st, int dt, const char *to) {
163         _cleanup_close_ int fdf = -1, fdt = -1;
164         struct timespec ts[2];
165         int r, q;
166
167         assert(from);
168         assert(st);
169         assert(to);
170
171         fdf = openat(df, from, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
172         if (fdf < 0)
173                 return -errno;
174
175         fdt = openat(dt, to, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, st->st_mode & 07777);
176         if (fdt < 0)
177                 return -errno;
178
179         r = copy_bytes(fdf, fdt, (uint64_t) -1, true);
180         if (r < 0) {
181                 unlinkat(dt, to, 0);
182                 return r;
183         }
184
185         if (fchown(fdt, st->st_uid, st->st_gid) < 0)
186                 r = -errno;
187
188         if (fchmod(fdt, st->st_mode & 07777) < 0)
189                 r = -errno;
190
191         ts[0] = st->st_atim;
192         ts[1] = st->st_mtim;
193         (void) futimens(fdt, ts);
194
195         (void) copy_xattr(fdf, fdt);
196
197         q = close(fdt);
198         fdt = -1;
199
200         if (q < 0) {
201                 r = -errno;
202                 unlinkat(dt, to, 0);
203         }
204
205         return r;
206 }
207
208 static int fd_copy_fifo(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 = mkfifoat(dt, to, st->st_mode & 07777);
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_node(int df, const char *from, const struct stat *st, int dt, const char *to) {
229         int r;
230
231         assert(from);
232         assert(st);
233         assert(to);
234
235         r = mknodat(dt, to, st->st_mode, st->st_rdev);
236         if (r < 0)
237                 return -errno;
238
239         if (fchownat(dt, to, st->st_uid, st->st_gid, AT_SYMLINK_NOFOLLOW) < 0)
240                 r = -errno;
241
242         if (fchmodat(dt, to, st->st_mode & 07777, 0) < 0)
243                 r = -errno;
244
245         return r;
246 }
247
248 static int fd_copy_directory(
249                 int df,
250                 const char *from,
251                 const struct stat *st,
252                 int dt,
253                 const char *to,
254                 dev_t original_device,
255                 bool merge) {
256
257         _cleanup_close_ int fdf = -1, fdt = -1;
258         _cleanup_closedir_ DIR *d = NULL;
259         struct dirent *de;
260         bool created;
261         int r;
262
263         assert(st);
264         assert(to);
265
266         if (from)
267                 fdf = openat(df, from, O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
268         else
269                 fdf = fcntl(df, F_DUPFD_CLOEXEC, 3);
270         if (fdf < 0)
271                 return -errno;
272
273         d = fdopendir(fdf);
274         if (!d)
275                 return -errno;
276         fdf = -1;
277
278         r = mkdirat(dt, to, st->st_mode & 07777);
279         if (r >= 0)
280                 created = true;
281         else if (errno == EEXIST && merge)
282                 created = false;
283         else
284                 return -errno;
285
286         fdt = openat(dt, to, O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
287         if (fdt < 0)
288                 return -errno;
289
290         r = 0;
291
292         FOREACH_DIRENT_ALL(de, d, return -errno) {
293                 struct stat buf;
294                 int q;
295
296                 if (STR_IN_SET(de->d_name, ".", ".."))
297                         continue;
298
299                 if (fstatat(dirfd(d), de->d_name, &buf, AT_SYMLINK_NOFOLLOW) < 0) {
300                         r = -errno;
301                         continue;
302                 }
303
304                 if (buf.st_dev != original_device)
305                         continue;
306
307                 if (S_ISREG(buf.st_mode))
308                         q = fd_copy_regular(dirfd(d), de->d_name, &buf, fdt, de->d_name);
309                 else if (S_ISDIR(buf.st_mode))
310                         q = fd_copy_directory(dirfd(d), de->d_name, &buf, fdt, de->d_name, original_device, merge);
311                 else if (S_ISLNK(buf.st_mode))
312                         q = fd_copy_symlink(dirfd(d), de->d_name, &buf, fdt, de->d_name);
313                 else if (S_ISFIFO(buf.st_mode))
314                         q = fd_copy_fifo(dirfd(d), de->d_name, &buf, fdt, de->d_name);
315                 else if (S_ISBLK(buf.st_mode) || S_ISCHR(buf.st_mode) || S_ISSOCK(buf.st_mode))
316                         q = fd_copy_node(dirfd(d), de->d_name, &buf, fdt, de->d_name);
317                 else
318                         q = -EOPNOTSUPP;
319
320                 if (q == -EEXIST && merge)
321                         q = 0;
322
323                 if (q < 0)
324                         r = q;
325         }
326
327         if (created) {
328                 struct timespec ut[2] = {
329                         st->st_atim,
330                         st->st_mtim
331                 };
332
333                 if (fchown(fdt, st->st_uid, st->st_gid) < 0)
334                         r = -errno;
335
336                 if (fchmod(fdt, st->st_mode & 07777) < 0)
337                         r = -errno;
338
339                 (void) copy_xattr(dirfd(d), fdt);
340                 (void) futimens(fdt, ut);
341         }
342
343         return r;
344 }
345
346 int copy_tree_at(int fdf, const char *from, int fdt, const char *to, bool merge) {
347         struct stat st;
348
349         assert(from);
350         assert(to);
351
352         if (fstatat(fdf, from, &st, AT_SYMLINK_NOFOLLOW) < 0)
353                 return -errno;
354
355         if (S_ISREG(st.st_mode))
356                 return fd_copy_regular(fdf, from, &st, fdt, to);
357         else if (S_ISDIR(st.st_mode))
358                 return fd_copy_directory(fdf, from, &st, fdt, to, st.st_dev, merge);
359         else if (S_ISLNK(st.st_mode))
360                 return fd_copy_symlink(fdf, from, &st, fdt, to);
361         else if (S_ISFIFO(st.st_mode))
362                 return fd_copy_fifo(fdf, from, &st, fdt, to);
363         else if (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode) || S_ISSOCK(st.st_mode))
364                 return fd_copy_node(fdf, from, &st, fdt, to);
365         else
366                 return -EOPNOTSUPP;
367 }
368
369 int copy_tree(const char *from, const char *to, bool merge) {
370         return copy_tree_at(AT_FDCWD, from, AT_FDCWD, to, merge);
371 }
372
373 int copy_directory_fd(int dirfd, const char *to, bool merge) {
374         struct stat st;
375
376         assert(dirfd >= 0);
377         assert(to);
378
379         if (fstat(dirfd, &st) < 0)
380                 return -errno;
381
382         if (!S_ISDIR(st.st_mode))
383                 return -ENOTDIR;
384
385         return fd_copy_directory(dirfd, NULL, &st, AT_FDCWD, to, st.st_dev, merge);
386 }
387
388 int copy_directory(const char *from, const char *to, bool merge) {
389         struct stat st;
390
391         assert(from);
392         assert(to);
393
394         if (lstat(from, &st) < 0)
395                 return -errno;
396
397         if (!S_ISDIR(st.st_mode))
398                 return -ENOTDIR;
399
400         return fd_copy_directory(AT_FDCWD, from, &st, AT_FDCWD, to, st.st_dev, merge);
401 }
402
403 int copy_file_fd(const char *from, int fdt, bool try_reflink) {
404         _cleanup_close_ int fdf = -1;
405         int r;
406
407         assert(from);
408         assert(fdt >= 0);
409
410         fdf = open(from, O_RDONLY|O_CLOEXEC|O_NOCTTY);
411         if (fdf < 0)
412                 return -errno;
413
414         r = copy_bytes(fdf, fdt, (uint64_t) -1, try_reflink);
415
416         (void) copy_times(fdf, fdt);
417         (void) copy_xattr(fdf, fdt);
418
419         return r;
420 }
421
422 int copy_file(const char *from, const char *to, int flags, mode_t mode, unsigned chattr_flags) {
423         int fdt = -1, r;
424
425         assert(from);
426         assert(to);
427
428         RUN_WITH_UMASK(0000) {
429                 fdt = open(to, flags|O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY, mode);
430                 if (fdt < 0)
431                         return -errno;
432         }
433
434         if (chattr_flags != 0)
435                 (void) chattr_fd(fdt, chattr_flags, (unsigned) -1);
436
437         r = copy_file_fd(from, fdt, true);
438         if (r < 0) {
439                 close(fdt);
440                 unlink(to);
441                 return r;
442         }
443
444         if (close(fdt) < 0) {
445                 unlink_noerrno(to);
446                 return -errno;
447         }
448
449         return 0;
450 }
451
452 int copy_file_atomic(const char *from, const char *to, mode_t mode, bool replace, unsigned chattr_flags) {
453         _cleanup_free_ char *t = NULL;
454         int r;
455
456         assert(from);
457         assert(to);
458
459         r = tempfn_random(to, NULL, &t);
460         if (r < 0)
461                 return r;
462
463         r = copy_file(from, t, O_NOFOLLOW|O_EXCL, mode, chattr_flags);
464         if (r < 0)
465                 return r;
466
467         if (replace) {
468                 r = renameat(AT_FDCWD, t, AT_FDCWD, to);
469                 if (r < 0)
470                         r = -errno;
471         } else
472                 r = rename_noreplace(AT_FDCWD, t, AT_FDCWD, to);
473         if (r < 0) {
474                 (void) unlink_noerrno(t);
475                 return r;
476         }
477
478         return 0;
479 }
480
481 int copy_times(int fdf, int fdt) {
482         struct timespec ut[2];
483         struct stat st;
484         usec_t crtime = 0;
485
486         assert(fdf >= 0);
487         assert(fdt >= 0);
488
489         if (fstat(fdf, &st) < 0)
490                 return -errno;
491
492         ut[0] = st.st_atim;
493         ut[1] = st.st_mtim;
494
495         if (futimens(fdt, ut) < 0)
496                 return -errno;
497
498         if (fd_getcrtime(fdf, &crtime) >= 0)
499                 (void) fd_setcrtime(fdt, crtime);
500
501         return 0;
502 }
503
504 int copy_xattr(int fdf, int fdt) {
505         _cleanup_free_ char *bufa = NULL, *bufb = NULL;
506         size_t sza = 100, szb = 100;
507         ssize_t n;
508         int ret = 0;
509         const char *p;
510
511         for (;;) {
512                 bufa = malloc(sza);
513                 if (!bufa)
514                         return -ENOMEM;
515
516                 n = flistxattr(fdf, bufa, sza);
517                 if (n == 0)
518                         return 0;
519                 if (n > 0)
520                         break;
521                 if (errno != ERANGE)
522                         return -errno;
523
524                 sza *= 2;
525
526                 bufa = mfree(bufa);
527         }
528
529         p = bufa;
530         while (n > 0) {
531                 size_t l;
532
533                 l = strlen(p);
534                 assert(l < (size_t) n);
535
536                 if (startswith(p, "user.")) {
537                         ssize_t m;
538
539                         if (!bufb) {
540                                 bufb = malloc(szb);
541                                 if (!bufb)
542                                         return -ENOMEM;
543                         }
544
545                         m = fgetxattr(fdf, p, bufb, szb);
546                         if (m < 0) {
547                                 if (errno == ERANGE) {
548                                         szb *= 2;
549                                         bufb = mfree(bufb);
550                                         continue;
551                                 }
552
553                                 return -errno;
554                         }
555
556                         if (fsetxattr(fdt, p, bufb, m, 0) < 0)
557                                 ret = -errno;
558                 }
559
560                 p += l + 1;
561                 n -= l + 1;
562         }
563
564         return ret;
565 }
566 #endif // 0