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