chiark / gitweb /
[2/5] Apply missing fixes from upstream
[elogind.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 "strv.h"
28 #include "copy.h"
29
30 #define COPY_BUFFER_SIZE (16*1024)
31
32 int copy_bytes(int fdf, int fdt, uint64_t max_bytes, bool try_reflink) {
33         bool try_sendfile = true, try_splice = true;
34         int r;
35
36         assert(fdf >= 0);
37         assert(fdt >= 0);
38 #if 0
39         /* Try btrfs reflinks first. */
40         if (try_reflink &&
41             max_bytes == (uint64_t) -1 &&
42             lseek(fdf, 0, SEEK_CUR) == 0 &&
43             lseek(fdt, 0, SEEK_CUR) == 0) {
44
45                 r = btrfs_reflink(fdf, fdt);
46                 if (r >= 0)
47                         return 0; /* we copied the whole thing, hence hit EOF, return 0 */
48         }
49 #endif // 0
50         for (;;) {
51                 size_t m = COPY_BUFFER_SIZE;
52                 ssize_t n;
53
54                 if (max_bytes != (uint64_t) -1) {
55
56                         if (max_bytes <= 0)
57                                 return -EFBIG;
58
59                         if ((uint64_t) m > max_bytes)
60                                 m = (size_t) max_bytes;
61                 }
62
63                 /* First try sendfile(), unless we already tried */
64                 if (try_sendfile) {
65
66                         n = sendfile(fdt, fdf, NULL, m);
67                         if (n < 0) {
68                                 if (errno != EINVAL && errno != ENOSYS)
69                                         return -errno;
70
71                                 try_sendfile = false;
72                                 /* use fallback below */
73                         } else if (n == 0) /* EOF */
74                                 break;
75                         else if (n > 0)
76                                 /* Success! */
77                                 goto next;
78                 }
79
80                 /* The try splice, unless we already tried */
81                 if (try_splice) {
82                         n  = splice(fdf, NULL, fdt, NULL, m, 0);
83                         if (n < 0) {
84                                 if (errno != EINVAL && errno != ENOSYS)
85                                         return -errno;
86
87                                 try_splice = false;
88                                 /* use fallback below */
89                         } else if (n == 0) /* EOF */
90                                 break;
91                         else if (n > 0)
92                                 /* Success! */
93                                 goto next;
94                 }
95
96                 /* As a fallback just copy bits by hand */
97                 {
98                         uint8_t buf[m];
99
100                         n = read(fdf, buf, m);
101                         if (n < 0)
102                                 return -errno;
103                         if (n == 0) /* EOF */
104                                 break;
105
106                         r = loop_write(fdt, buf, (size_t) n, false);
107                         if (r < 0)
108                                 return r;
109                 }
110
111         next:
112                 if (max_bytes != (uint64_t) -1) {
113                         assert(max_bytes >= (uint64_t) n);
114                         max_bytes -= n;
115                 }
116         }
117
118         return 0; /* return 0 if we hit EOF earlier than the size limit */
119 }
120
121 // UNNEEDED by elogind
122 #if 0
123 static int fd_copy_symlink(int df, const char *from, const struct stat *st, int dt, const char *to) {
124         _cleanup_free_ char *target = NULL;
125         int r;
126
127         assert(from);
128         assert(st);
129         assert(to);
130
131         r = readlinkat_malloc(df, from, &target);
132         if (r < 0)
133                 return r;
134
135         if (symlinkat(target, dt, to) < 0)
136                 return -errno;
137
138         if (fchownat(dt, to, st->st_uid, st->st_gid, AT_SYMLINK_NOFOLLOW) < 0)
139                 return -errno;
140
141         return 0;
142 }
143
144 static int fd_copy_regular(int df, const char *from, const struct stat *st, int dt, const char *to) {
145         _cleanup_close_ int fdf = -1, fdt = -1;
146         struct timespec ts[2];
147         int r, q;
148
149         assert(from);
150         assert(st);
151         assert(to);
152
153         fdf = openat(df, from, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
154         if (fdf < 0)
155                 return -errno;
156
157         fdt = openat(dt, to, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, st->st_mode & 07777);
158         if (fdt < 0)
159                 return -errno;
160
161         r = copy_bytes(fdf, fdt, (uint64_t) -1, true);
162         if (r < 0) {
163                 unlinkat(dt, to, 0);
164                 return r;
165         }
166
167         if (fchown(fdt, st->st_uid, st->st_gid) < 0)
168                 r = -errno;
169
170         if (fchmod(fdt, st->st_mode & 07777) < 0)
171                 r = -errno;
172
173         ts[0] = st->st_atim;
174         ts[1] = st->st_mtim;
175         (void) futimens(fdt, ts);
176
177         (void) copy_xattr(fdf, fdt);
178
179         q = close(fdt);
180         fdt = -1;
181
182         if (q < 0) {
183                 r = -errno;
184                 unlinkat(dt, to, 0);
185         }
186
187         return r;
188 }
189
190 static int fd_copy_fifo(int df, const char *from, const struct stat *st, int dt, const char *to) {
191         int r;
192
193         assert(from);
194         assert(st);
195         assert(to);
196
197         r = mkfifoat(dt, to, st->st_mode & 07777);
198         if (r < 0)
199                 return -errno;
200
201         if (fchownat(dt, to, st->st_uid, st->st_gid, AT_SYMLINK_NOFOLLOW) < 0)
202                 r = -errno;
203
204         if (fchmodat(dt, to, st->st_mode & 07777, 0) < 0)
205                 r = -errno;
206
207         return r;
208 }
209
210 static int fd_copy_node(int df, const char *from, const struct stat *st, int dt, const char *to) {
211         int r;
212
213         assert(from);
214         assert(st);
215         assert(to);
216
217         r = mknodat(dt, to, st->st_mode, st->st_rdev);
218         if (r < 0)
219                 return -errno;
220
221         if (fchownat(dt, to, st->st_uid, st->st_gid, AT_SYMLINK_NOFOLLOW) < 0)
222                 r = -errno;
223
224         if (fchmodat(dt, to, st->st_mode & 07777, 0) < 0)
225                 r = -errno;
226
227         return r;
228 }
229
230 static int fd_copy_directory(
231                 int df,
232                 const char *from,
233                 const struct stat *st,
234                 int dt,
235                 const char *to,
236                 dev_t original_device,
237                 bool merge) {
238
239         _cleanup_close_ int fdf = -1, fdt = -1;
240         _cleanup_closedir_ DIR *d = NULL;
241         struct dirent *de;
242         bool created;
243         int r;
244
245         assert(st);
246         assert(to);
247
248         if (from)
249                 fdf = openat(df, from, O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
250         else
251                 fdf = fcntl(df, F_DUPFD_CLOEXEC, 3);
252
253         d = fdopendir(fdf);
254         if (!d)
255                 return -errno;
256         fdf = -1;
257
258         r = mkdirat(dt, to, st->st_mode & 07777);
259         if (r >= 0)
260                 created = true;
261         else if (errno == EEXIST && merge)
262                 created = false;
263         else
264                 return -errno;
265
266         fdt = openat(dt, to, O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
267         if (fdt < 0)
268                 return -errno;
269
270         r = 0;
271
272         if (created) {
273                 struct timespec ut[2] = {
274                         st->st_atim,
275                         st->st_mtim
276                 };
277
278                 if (fchown(fdt, st->st_uid, st->st_gid) < 0)
279                         r = -errno;
280
281                 if (fchmod(fdt, st->st_mode & 07777) < 0)
282                         r = -errno;
283
284                 (void) futimens(fdt, ut);
285                 (void) copy_xattr(dirfd(d), fdt);
286         }
287
288         FOREACH_DIRENT_ALL(de, d, return -errno) {
289                 struct stat buf;
290                 int q;
291
292                 if (STR_IN_SET(de->d_name, ".", ".."))
293                         continue;
294
295                 if (fstatat(dirfd(d), de->d_name, &buf, AT_SYMLINK_NOFOLLOW) < 0) {
296                         r = -errno;
297                         continue;
298                 }
299
300                 if (buf.st_dev != original_device)
301                         continue;
302
303                 if (S_ISREG(buf.st_mode))
304                         q = fd_copy_regular(dirfd(d), de->d_name, &buf, fdt, de->d_name);
305                 else if (S_ISDIR(buf.st_mode))
306                         q = fd_copy_directory(dirfd(d), de->d_name, &buf, fdt, de->d_name, original_device, merge);
307                 else if (S_ISLNK(buf.st_mode))
308                         q = fd_copy_symlink(dirfd(d), de->d_name, &buf, fdt, de->d_name);
309                 else if (S_ISFIFO(buf.st_mode))
310                         q = fd_copy_fifo(dirfd(d), de->d_name, &buf, fdt, de->d_name);
311                 else if (S_ISBLK(buf.st_mode) || S_ISCHR(buf.st_mode))
312                         q = fd_copy_node(dirfd(d), de->d_name, &buf, fdt, de->d_name);
313                 else
314                         q = -EOPNOTSUPP;
315
316                 if (q == -EEXIST && merge)
317                         q = 0;
318
319                 if (q < 0)
320                         r = q;
321         }
322
323         return r;
324 }
325
326 int copy_tree_at(int fdf, const char *from, int fdt, const char *to, bool merge) {
327         struct stat st;
328
329         assert(from);
330         assert(to);
331
332         if (fstatat(fdf, from, &st, AT_SYMLINK_NOFOLLOW) < 0)
333                 return -errno;
334
335         if (S_ISREG(st.st_mode))
336                 return fd_copy_regular(fdf, from, &st, fdt, to);
337         else if (S_ISDIR(st.st_mode))
338                 return fd_copy_directory(fdf, from, &st, fdt, to, st.st_dev, merge);
339         else if (S_ISLNK(st.st_mode))
340                 return fd_copy_symlink(fdf, from, &st, fdt, to);
341         else if (S_ISFIFO(st.st_mode))
342                 return fd_copy_fifo(fdf, from, &st, fdt, to);
343         else if (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode))
344                 return fd_copy_node(fdf, from, &st, fdt, to);
345         else
346                 return -EOPNOTSUPP;
347 }
348
349 int copy_tree(const char *from, const char *to, bool merge) {
350         return copy_tree_at(AT_FDCWD, from, AT_FDCWD, to, merge);
351 }
352
353 int copy_directory_fd(int dirfd, const char *to, bool merge) {
354
355         struct stat st;
356
357         assert(dirfd >= 0);
358         assert(to);
359
360         if (fstat(dirfd, &st) < 0)
361                 return -errno;
362
363         if (!S_ISDIR(st.st_mode))
364                 return -ENOTDIR;
365
366         return fd_copy_directory(dirfd, NULL, &st, AT_FDCWD, to, st.st_dev, merge);
367 }
368
369 int copy_file_fd(const char *from, int fdt, bool try_reflink) {
370         _cleanup_close_ int fdf = -1;
371         int r;
372
373         assert(from);
374         assert(fdt >= 0);
375
376         fdf = open(from, O_RDONLY|O_CLOEXEC|O_NOCTTY);
377         if (fdf < 0)
378                 return -errno;
379
380         r = copy_bytes(fdf, fdt, (uint64_t) -1, try_reflink);
381
382         (void) copy_times(fdf, fdt);
383         (void) copy_xattr(fdf, fdt);
384
385         return r;
386 }
387
388 int copy_file(const char *from, const char *to, int flags, mode_t mode, unsigned chattr_flags) {
389         int fdt = -1, r;
390
391         assert(from);
392         assert(to);
393
394         RUN_WITH_UMASK(0000) {
395                 fdt = open(to, flags|O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY, mode);
396                 if (fdt < 0)
397                         return -errno;
398         }
399
400         if (chattr_flags != 0)
401                 (void) chattr_fd(fdt, chattr_flags, (unsigned) -1);
402
403         r = copy_file_fd(from, fdt, true);
404         if (r < 0) {
405                 close(fdt);
406                 unlink(to);
407                 return r;
408         }
409
410         if (close(fdt) < 0) {
411                 unlink_noerrno(to);
412                 return -errno;
413         }
414
415         return 0;
416 }
417
418 int copy_file_atomic(const char *from, const char *to, mode_t mode, bool replace, unsigned chattr_flags) {
419         _cleanup_free_ char *t = NULL;
420         int r;
421
422         assert(from);
423         assert(to);
424
425         r = tempfn_random(to, NULL, &t);
426         if (r < 0)
427                 return r;
428
429         r = copy_file(from, t, O_NOFOLLOW|O_EXCL, mode, chattr_flags);
430         if (r < 0)
431                 return r;
432
433         if (replace) {
434                 r = renameat(AT_FDCWD, t, AT_FDCWD, to);
435                 if (r < 0)
436                         r = -errno;
437         } else
438                 r = rename_noreplace(AT_FDCWD, t, AT_FDCWD, to);
439         if (r < 0) {
440                 (void) unlink_noerrno(t);
441                 return r;
442         }
443
444         return 0;
445 }
446
447 int copy_times(int fdf, int fdt) {
448         struct timespec ut[2];
449         struct stat st;
450         usec_t crtime = 0;
451
452         assert(fdf >= 0);
453         assert(fdt >= 0);
454
455         if (fstat(fdf, &st) < 0)
456                 return -errno;
457
458         ut[0] = st.st_atim;
459         ut[1] = st.st_mtim;
460
461         if (futimens(fdt, ut) < 0)
462                 return -errno;
463
464         if (fd_getcrtime(fdf, &crtime) >= 0)
465                 (void) fd_setcrtime(fdt, crtime);
466
467         return 0;
468 }
469
470 int copy_xattr(int fdf, int fdt) {
471         _cleanup_free_ char *bufa = NULL, *bufb = NULL;
472         size_t sza = 100, szb = 100;
473         ssize_t n;
474         int ret = 0;
475         const char *p;
476
477         for (;;) {
478                 bufa = malloc(sza);
479                 if (!bufa)
480                         return -ENOMEM;
481
482                 n = flistxattr(fdf, bufa, sza);
483                 if (n == 0)
484                         return 0;
485                 if (n > 0)
486                         break;
487                 if (errno != ERANGE)
488                         return -errno;
489
490                 sza *= 2;
491
492                 bufa = mfree(bufa);
493         }
494
495         p = bufa;
496         while (n > 0) {
497                 size_t l;
498
499                 l = strlen(p);
500                 assert(l < (size_t) n);
501
502                 if (startswith(p, "user.")) {
503                         ssize_t m;
504
505                         if (!bufb) {
506                                 bufb = malloc(szb);
507                                 if (!bufb)
508                                         return -ENOMEM;
509                         }
510
511                         m = fgetxattr(fdf, p, bufb, szb);
512                         if (m < 0) {
513                                 if (errno == ERANGE) {
514                                         szb *= 2;
515                                         bufb = mfree(bufb);
516                                         continue;
517                                 }
518
519                                 return -errno;
520                         }
521
522                         if (fsetxattr(fdt, p, bufb, m, 0) < 0)
523                                 ret = -errno;
524                 }
525
526                 p += l + 1;
527                 n -= l + 1;
528         }
529
530         return ret;
531 }
532 #endif // 0