chiark / gitweb /
tree-wide: drop license boilerplate
[elogind.git] / src / basic / copy.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3   This file is part of systemd.
4
5   Copyright 2014 Lennart Poettering
6 ***/
7
8 //#include <dirent.h>
9 //#include <errno.h>
10 //#include <fcntl.h>
11 //#include <stddef.h>
12 //#include <stdio.h>
13 //#include <stdlib.h>
14 //#include <string.h>
15 #include <sys/sendfile.h>
16 //#include <sys/stat.h>
17 #include <sys/xattr.h>
18 //#include <time.h>
19 //#include <unistd.h>
20
21 //#include "alloc-util.h"
22 //#include "btrfs-util.h"
23 //#include "chattr-util.h"
24 #include "copy.h"
25 //#include "dirent-util.h"
26 //#include "fd-util.h"
27 //#include "fileio.h"
28 //#include "fs-util.h"
29 #include "io-util.h"
30 //#include "macro.h"
31 #include "missing.h"
32 //#include "string-util.h"
33 #include "strv.h"
34 #include "time-util.h"
35 //#include "umask-util.h"
36 #include "user-util.h"
37 //#include "xattr-util.h"
38
39 #define COPY_BUFFER_SIZE (16*1024u)
40
41 static ssize_t try_copy_file_range(int fd_in, loff_t *off_in,
42                                    int fd_out, loff_t *off_out,
43                                    size_t len,
44                                    unsigned int flags) {
45         static int have = -1;
46         ssize_t r;
47
48         if (have == false)
49                 return -ENOSYS;
50
51         r = copy_file_range(fd_in, off_in, fd_out, off_out, len, flags);
52         if (_unlikely_(have < 0))
53                 have = r >= 0 || errno != ENOSYS;
54         if (r >= 0)
55                 return r;
56         else
57                 return -errno;
58 }
59
60 int copy_bytes(int fdf, int fdt, uint64_t max_bytes, CopyFlags copy_flags) {
61         bool try_cfr = true, try_sendfile = true, try_splice = true;
62         int r;
63         size_t m = SSIZE_MAX; /* that is the maximum that sendfile and c_f_r accept */
64
65         assert(fdf >= 0);
66         assert(fdt >= 0);
67
68 #if 0 /// UNNEEDED by elogind
69         /* Try btrfs reflinks first. */
70         if ((copy_flags & COPY_REFLINK) &&
71             max_bytes == (uint64_t) -1 &&
72             lseek(fdf, 0, SEEK_CUR) == 0 &&
73             lseek(fdt, 0, SEEK_CUR) == 0) {
74
75                 r = btrfs_reflink(fdf, fdt);
76                 if (r >= 0)
77                         return 0; /* we copied the whole thing, hence hit EOF, return 0 */
78         }
79 #endif // 0
80
81         for (;;) {
82                 ssize_t n;
83
84                 if (max_bytes != (uint64_t) -1) {
85                         if (max_bytes <= 0)
86                                 return 1; /* return > 0 if we hit the max_bytes limit */
87
88                         if (m > max_bytes)
89                                 m = max_bytes;
90                 }
91
92                 /* First try copy_file_range(), unless we already tried */
93                 if (try_cfr) {
94                         n = try_copy_file_range(fdf, NULL, fdt, NULL, m, 0u);
95                         if (n < 0) {
96                                 if (!IN_SET(n, -EINVAL, -ENOSYS, -EXDEV, -EBADF))
97                                         return n;
98
99                                 try_cfr = false;
100                                 /* use fallback below */
101                         } else if (n == 0) /* EOF */
102                                 break;
103                         else
104                                 /* Success! */
105                                 goto next;
106                 }
107
108                 /* First try sendfile(), unless we already tried */
109                 if (try_sendfile) {
110                         n = sendfile(fdt, fdf, NULL, m);
111                         if (n < 0) {
112                                 if (!IN_SET(errno, EINVAL, ENOSYS))
113                                         return -errno;
114
115                                 try_sendfile = false;
116                                 /* use fallback below */
117                         } else if (n == 0) /* EOF */
118                                 break;
119                         else
120                                 /* Success! */
121                                 goto next;
122                 }
123
124                 /* Then try splice, unless we already tried */
125                 if (try_splice) {
126                         n = splice(fdf, NULL, fdt, NULL, m, 0);
127                         if (n < 0) {
128                                 if (!IN_SET(errno, EINVAL, ENOSYS))
129                                         return -errno;
130
131                                 try_splice = false;
132                                 /* use fallback below */
133                         } else if (n == 0) /* EOF */
134                                 break;
135                         else
136                                 /* Success! */
137                                 goto next;
138                 }
139
140                 /* As a fallback just copy bits by hand */
141                 {
142                         uint8_t buf[MIN(m, COPY_BUFFER_SIZE)];
143
144                         n = read(fdf, buf, sizeof buf);
145                         if (n < 0)
146                                 return -errno;
147                         if (n == 0) /* EOF */
148                                 break;
149
150                         r = loop_write(fdt, buf, (size_t) n, false);
151                         if (r < 0)
152                                 return r;
153                 }
154
155         next:
156                 if (max_bytes != (uint64_t) -1) {
157                         assert(max_bytes >= (uint64_t) n);
158                         max_bytes -= n;
159                 }
160                 /* sendfile accepts at most SSIZE_MAX-offset bytes to copy,
161                  * so reduce our maximum by the amount we already copied,
162                  * but don't go below our copy buffer size, unless we are
163                  * close the limit of bytes we are allowed to copy. */
164                 m = MAX(MIN(COPY_BUFFER_SIZE, max_bytes), m - n);
165         }
166
167         return 0; /* return 0 if we hit EOF earlier than the size limit */
168 }
169
170 #if 0 /// UNNEEDED by elogind
171 static int fd_copy_symlink(
172                 int df,
173                 const char *from,
174                 const struct stat *st,
175                 int dt,
176                 const char *to,
177                 uid_t override_uid,
178                 gid_t override_gid,
179                 CopyFlags copy_flags) {
180
181         _cleanup_free_ char *target = NULL;
182         int r;
183
184         assert(from);
185         assert(st);
186         assert(to);
187
188         r = readlinkat_malloc(df, from, &target);
189         if (r < 0)
190                 return r;
191
192         if (symlinkat(target, dt, to) < 0)
193                 return -errno;
194
195         if (fchownat(dt, to,
196                      uid_is_valid(override_uid) ? override_uid : st->st_uid,
197                      gid_is_valid(override_gid) ? override_gid : st->st_gid,
198                      AT_SYMLINK_NOFOLLOW) < 0)
199                 return -errno;
200
201         return 0;
202 }
203
204 static int fd_copy_regular(
205                 int df,
206                 const char *from,
207                 const struct stat *st,
208                 int dt,
209                 const char *to,
210                 uid_t override_uid,
211                 gid_t override_gid,
212                 CopyFlags copy_flags) {
213
214         _cleanup_close_ int fdf = -1, fdt = -1;
215         struct timespec ts[2];
216         int r, q;
217
218         assert(from);
219         assert(st);
220         assert(to);
221
222         fdf = openat(df, from, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
223         if (fdf < 0)
224                 return -errno;
225
226         fdt = openat(dt, to, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, st->st_mode & 07777);
227         if (fdt < 0)
228                 return -errno;
229
230         r = copy_bytes(fdf, fdt, (uint64_t) -1, copy_flags);
231         if (r < 0) {
232                 (void) unlinkat(dt, to, 0);
233                 return r;
234         }
235
236         if (fchown(fdt,
237                    uid_is_valid(override_uid) ? override_uid : st->st_uid,
238                    gid_is_valid(override_gid) ? override_gid : st->st_gid) < 0)
239                 r = -errno;
240
241         if (fchmod(fdt, st->st_mode & 07777) < 0)
242                 r = -errno;
243
244         ts[0] = st->st_atim;
245         ts[1] = st->st_mtim;
246         (void) futimens(fdt, ts);
247         (void) copy_xattr(fdf, fdt);
248
249         q = close(fdt);
250         fdt = -1;
251
252         if (q < 0) {
253                 r = -errno;
254                 (void) unlinkat(dt, to, 0);
255         }
256
257         return r;
258 }
259
260 static int fd_copy_fifo(
261                 int df,
262                 const char *from,
263                 const struct stat *st,
264                 int dt,
265                 const char *to,
266                 uid_t override_uid,
267                 gid_t override_gid,
268                 CopyFlags copy_flags) {
269         int r;
270
271         assert(from);
272         assert(st);
273         assert(to);
274
275         r = mkfifoat(dt, to, st->st_mode & 07777);
276         if (r < 0)
277                 return -errno;
278
279         if (fchownat(dt, to,
280                      uid_is_valid(override_uid) ? override_uid : st->st_uid,
281                      gid_is_valid(override_gid) ? override_gid : st->st_gid,
282                      AT_SYMLINK_NOFOLLOW) < 0)
283                 r = -errno;
284
285         if (fchmodat(dt, to, st->st_mode & 07777, 0) < 0)
286                 r = -errno;
287
288         return r;
289 }
290
291 static int fd_copy_node(
292                 int df,
293                 const char *from,
294                 const struct stat *st,
295                 int dt,
296                 const char *to,
297                 uid_t override_uid,
298                 gid_t override_gid,
299                 CopyFlags copy_flags) {
300         int r;
301
302         assert(from);
303         assert(st);
304         assert(to);
305
306         r = mknodat(dt, to, st->st_mode, st->st_rdev);
307         if (r < 0)
308                 return -errno;
309
310         if (fchownat(dt, to,
311                      uid_is_valid(override_uid) ? override_uid : st->st_uid,
312                      gid_is_valid(override_gid) ? override_gid : st->st_gid,
313                      AT_SYMLINK_NOFOLLOW) < 0)
314                 r = -errno;
315
316         if (fchmodat(dt, to, st->st_mode & 07777, 0) < 0)
317                 r = -errno;
318
319         return r;
320 }
321
322 static int fd_copy_directory(
323                 int df,
324                 const char *from,
325                 const struct stat *st,
326                 int dt,
327                 const char *to,
328                 dev_t original_device,
329                 uid_t override_uid,
330                 gid_t override_gid,
331                 CopyFlags copy_flags) {
332
333         _cleanup_close_ int fdf = -1, fdt = -1;
334         _cleanup_closedir_ DIR *d = NULL;
335         struct dirent *de;
336         bool created;
337         int r;
338
339         assert(st);
340         assert(to);
341
342         if (from)
343                 fdf = openat(df, from, O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
344         else
345                 fdf = fcntl(df, F_DUPFD_CLOEXEC, 3);
346         if (fdf < 0)
347                 return -errno;
348
349         d = fdopendir(fdf);
350         if (!d)
351                 return -errno;
352         fdf = -1;
353
354         r = mkdirat(dt, to, st->st_mode & 07777);
355         if (r >= 0)
356                 created = true;
357         else if (errno == EEXIST && (copy_flags & COPY_MERGE))
358                 created = false;
359         else
360                 return -errno;
361
362         fdt = openat(dt, to, O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
363         if (fdt < 0)
364                 return -errno;
365
366         r = 0;
367
368         FOREACH_DIRENT_ALL(de, d, return -errno) {
369                 struct stat buf;
370                 int q;
371
372                 if (dot_or_dot_dot(de->d_name))
373                         continue;
374
375                 if (fstatat(dirfd(d), de->d_name, &buf, AT_SYMLINK_NOFOLLOW) < 0) {
376                         r = -errno;
377                         continue;
378                 }
379
380                 if (buf.st_dev != original_device)
381                         continue;
382
383                 if (S_ISREG(buf.st_mode))
384                         q = fd_copy_regular(dirfd(d), de->d_name, &buf, fdt, de->d_name, override_uid, override_gid, copy_flags);
385                 else if (S_ISDIR(buf.st_mode))
386                         q = fd_copy_directory(dirfd(d), de->d_name, &buf, fdt, de->d_name, original_device, override_uid, override_gid, copy_flags);
387                 else if (S_ISLNK(buf.st_mode))
388                         q = fd_copy_symlink(dirfd(d), de->d_name, &buf, fdt, de->d_name, override_uid, override_gid, copy_flags);
389                 else if (S_ISFIFO(buf.st_mode))
390                         q = fd_copy_fifo(dirfd(d), de->d_name, &buf, fdt, de->d_name, override_uid, override_gid, copy_flags);
391                 else if (S_ISBLK(buf.st_mode) || S_ISCHR(buf.st_mode) || S_ISSOCK(buf.st_mode))
392                         q = fd_copy_node(dirfd(d), de->d_name, &buf, fdt, de->d_name, override_uid, override_gid, copy_flags);
393                 else
394                         q = -EOPNOTSUPP;
395
396                 if (q == -EEXIST && (copy_flags & COPY_MERGE))
397                         q = 0;
398
399                 if (q < 0)
400                         r = q;
401         }
402
403         if (created) {
404                 struct timespec ut[2] = {
405                         st->st_atim,
406                         st->st_mtim
407                 };
408
409                 if (fchown(fdt,
410                            uid_is_valid(override_uid) ? override_uid : st->st_uid,
411                            gid_is_valid(override_gid) ? override_gid : st->st_gid) < 0)
412                         r = -errno;
413
414                 if (fchmod(fdt, st->st_mode & 07777) < 0)
415                         r = -errno;
416
417                 (void) copy_xattr(dirfd(d), fdt);
418                 (void) futimens(fdt, ut);
419         }
420
421         return r;
422 }
423
424 int copy_tree_at(int fdf, const char *from, int fdt, const char *to, uid_t override_uid, gid_t override_gid, CopyFlags copy_flags) {
425         struct stat st;
426
427         assert(from);
428         assert(to);
429
430         if (fstatat(fdf, from, &st, AT_SYMLINK_NOFOLLOW) < 0)
431                 return -errno;
432
433         if (S_ISREG(st.st_mode))
434                 return fd_copy_regular(fdf, from, &st, fdt, to, override_uid, override_gid, copy_flags);
435         else if (S_ISDIR(st.st_mode))
436                 return fd_copy_directory(fdf, from, &st, fdt, to, st.st_dev, override_uid, override_gid, copy_flags);
437         else if (S_ISLNK(st.st_mode))
438                 return fd_copy_symlink(fdf, from, &st, fdt, to, override_uid, override_gid, copy_flags);
439         else if (S_ISFIFO(st.st_mode))
440                 return fd_copy_fifo(fdf, from, &st, fdt, to, override_uid, override_gid, copy_flags);
441         else if (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode) || S_ISSOCK(st.st_mode))
442                 return fd_copy_node(fdf, from, &st, fdt, to, override_uid, override_gid, copy_flags);
443         else
444                 return -EOPNOTSUPP;
445 }
446
447 int copy_tree(const char *from, const char *to, uid_t override_uid, gid_t override_gid, CopyFlags copy_flags) {
448         return copy_tree_at(AT_FDCWD, from, AT_FDCWD, to, override_uid, override_gid, copy_flags);
449 }
450
451 int copy_directory_fd(int dirfd, const char *to, CopyFlags copy_flags) {
452         struct stat st;
453
454         assert(dirfd >= 0);
455         assert(to);
456
457         if (fstat(dirfd, &st) < 0)
458                 return -errno;
459
460         if (!S_ISDIR(st.st_mode))
461                 return -ENOTDIR;
462
463         return fd_copy_directory(dirfd, NULL, &st, AT_FDCWD, to, st.st_dev, UID_INVALID, GID_INVALID, copy_flags);
464 }
465
466 int copy_directory(const char *from, const char *to, CopyFlags copy_flags) {
467         struct stat st;
468
469         assert(from);
470         assert(to);
471
472         if (lstat(from, &st) < 0)
473                 return -errno;
474
475         if (!S_ISDIR(st.st_mode))
476                 return -ENOTDIR;
477
478         return fd_copy_directory(AT_FDCWD, from, &st, AT_FDCWD, to, st.st_dev, UID_INVALID, GID_INVALID, copy_flags);
479 }
480
481 int copy_file_fd(const char *from, int fdt, CopyFlags copy_flags) {
482         _cleanup_close_ int fdf = -1;
483         int r;
484
485         assert(from);
486         assert(fdt >= 0);
487
488         fdf = open(from, O_RDONLY|O_CLOEXEC|O_NOCTTY);
489         if (fdf < 0)
490                 return -errno;
491
492         r = copy_bytes(fdf, fdt, (uint64_t) -1, copy_flags);
493
494         (void) copy_times(fdf, fdt);
495         (void) copy_xattr(fdf, fdt);
496
497         return r;
498 }
499
500 int copy_file(const char *from, const char *to, int flags, mode_t mode, unsigned chattr_flags, CopyFlags copy_flags) {
501         int fdt = -1, r;
502
503         assert(from);
504         assert(to);
505
506         RUN_WITH_UMASK(0000) {
507                 fdt = open(to, flags|O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY, mode);
508                 if (fdt < 0)
509                         return -errno;
510         }
511
512         if (chattr_flags != 0)
513                 (void) chattr_fd(fdt, chattr_flags, (unsigned) -1);
514
515         r = copy_file_fd(from, fdt, copy_flags);
516         if (r < 0) {
517                 close(fdt);
518                 (void) unlink(to);
519                 return r;
520         }
521
522         if (close(fdt) < 0) {
523                 unlink_noerrno(to);
524                 return -errno;
525         }
526
527         return 0;
528 }
529
530 int copy_file_atomic(const char *from, const char *to, mode_t mode, unsigned chattr_flags, CopyFlags copy_flags) {
531         _cleanup_free_ char *t = NULL;
532         int r;
533
534         assert(from);
535         assert(to);
536
537         r = tempfn_random(to, NULL, &t);
538         if (r < 0)
539                 return r;
540
541         r = copy_file(from, t, O_NOFOLLOW|O_EXCL, mode, chattr_flags, copy_flags);
542         if (r < 0)
543                 return r;
544
545         if (copy_flags & COPY_REPLACE) {
546                 r = renameat(AT_FDCWD, t, AT_FDCWD, to);
547                 if (r < 0)
548                         r = -errno;
549         } else
550                 r = rename_noreplace(AT_FDCWD, t, AT_FDCWD, to);
551         if (r < 0) {
552                 (void) unlink(t);
553                 return r;
554         }
555
556         return 0;
557 }
558
559 int copy_times(int fdf, int fdt) {
560         struct timespec ut[2];
561         struct stat st;
562         usec_t crtime = 0;
563
564         assert(fdf >= 0);
565         assert(fdt >= 0);
566
567         if (fstat(fdf, &st) < 0)
568                 return -errno;
569
570         ut[0] = st.st_atim;
571         ut[1] = st.st_mtim;
572
573         if (futimens(fdt, ut) < 0)
574                 return -errno;
575
576         if (fd_getcrtime(fdf, &crtime) >= 0)
577                 (void) fd_setcrtime(fdt, crtime);
578
579         return 0;
580 }
581
582 int copy_xattr(int fdf, int fdt) {
583         _cleanup_free_ char *bufa = NULL, *bufb = NULL;
584         size_t sza = 100, szb = 100;
585         ssize_t n;
586         int ret = 0;
587         const char *p;
588
589         for (;;) {
590                 bufa = malloc(sza);
591                 if (!bufa)
592                         return -ENOMEM;
593
594                 n = flistxattr(fdf, bufa, sza);
595                 if (n == 0)
596                         return 0;
597                 if (n > 0)
598                         break;
599                 if (errno != ERANGE)
600                         return -errno;
601
602                 sza *= 2;
603
604                 bufa = mfree(bufa);
605         }
606
607         p = bufa;
608         while (n > 0) {
609                 size_t l;
610
611                 l = strlen(p);
612                 assert(l < (size_t) n);
613
614                 if (startswith(p, "user.")) {
615                         ssize_t m;
616
617                         if (!bufb) {
618                                 bufb = malloc(szb);
619                                 if (!bufb)
620                                         return -ENOMEM;
621                         }
622
623                         m = fgetxattr(fdf, p, bufb, szb);
624                         if (m < 0) {
625                                 if (errno == ERANGE) {
626                                         szb *= 2;
627                                         bufb = mfree(bufb);
628                                         continue;
629                                 }
630
631                                 return -errno;
632                         }
633
634                         if (fsetxattr(fdt, p, bufb, m, 0) < 0)
635                                 ret = -errno;
636                 }
637
638                 p += l + 1;
639                 n -= l + 1;
640         }
641
642         return ret;
643 }
644 #endif // 0