chiark / gitweb /
Prep v229: Add missing fixes from upstream [1/6] src/basic
[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
271         d = fdopendir(fdf);
272         if (!d)
273                 return -errno;
274         fdf = -1;
275
276         r = mkdirat(dt, to, st->st_mode & 07777);
277         if (r >= 0)
278                 created = true;
279         else if (errno == EEXIST && merge)
280                 created = false;
281         else
282                 return -errno;
283
284         fdt = openat(dt, to, O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
285         if (fdt < 0)
286                 return -errno;
287
288         r = 0;
289
290         if (created) {
291                 struct timespec ut[2] = {
292                         st->st_atim,
293                         st->st_mtim
294                 };
295
296                 if (fchown(fdt, st->st_uid, st->st_gid) < 0)
297                         r = -errno;
298
299                 if (fchmod(fdt, st->st_mode & 07777) < 0)
300                         r = -errno;
301
302                 (void) futimens(fdt, ut);
303                 (void) copy_xattr(dirfd(d), fdt);
304         }
305
306         FOREACH_DIRENT_ALL(de, d, return -errno) {
307                 struct stat buf;
308                 int q;
309
310                 if (STR_IN_SET(de->d_name, ".", ".."))
311                         continue;
312
313                 if (fstatat(dirfd(d), de->d_name, &buf, AT_SYMLINK_NOFOLLOW) < 0) {
314                         r = -errno;
315                         continue;
316                 }
317
318                 if (buf.st_dev != original_device)
319                         continue;
320
321                 if (S_ISREG(buf.st_mode))
322                         q = fd_copy_regular(dirfd(d), de->d_name, &buf, fdt, de->d_name);
323                 else if (S_ISDIR(buf.st_mode))
324                         q = fd_copy_directory(dirfd(d), de->d_name, &buf, fdt, de->d_name, original_device, merge);
325                 else if (S_ISLNK(buf.st_mode))
326                         q = fd_copy_symlink(dirfd(d), de->d_name, &buf, fdt, de->d_name);
327                 else if (S_ISFIFO(buf.st_mode))
328                         q = fd_copy_fifo(dirfd(d), de->d_name, &buf, fdt, de->d_name);
329                 else if (S_ISBLK(buf.st_mode) || S_ISCHR(buf.st_mode))
330                         q = fd_copy_node(dirfd(d), de->d_name, &buf, fdt, de->d_name);
331                 else
332                         q = -EOPNOTSUPP;
333
334                 if (q == -EEXIST && merge)
335                         q = 0;
336
337                 if (q < 0)
338                         r = q;
339         }
340
341         return r;
342 }
343
344 int copy_tree_at(int fdf, const char *from, int fdt, const char *to, bool merge) {
345         struct stat st;
346
347         assert(from);
348         assert(to);
349
350         if (fstatat(fdf, from, &st, AT_SYMLINK_NOFOLLOW) < 0)
351                 return -errno;
352
353         if (S_ISREG(st.st_mode))
354                 return fd_copy_regular(fdf, from, &st, fdt, to);
355         else if (S_ISDIR(st.st_mode))
356                 return fd_copy_directory(fdf, from, &st, fdt, to, st.st_dev, merge);
357         else if (S_ISLNK(st.st_mode))
358                 return fd_copy_symlink(fdf, from, &st, fdt, to);
359         else if (S_ISFIFO(st.st_mode))
360                 return fd_copy_fifo(fdf, from, &st, fdt, to);
361         else if (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode))
362                 return fd_copy_node(fdf, from, &st, fdt, to);
363         else
364                 return -EOPNOTSUPP;
365 }
366
367 int copy_tree(const char *from, const char *to, bool merge) {
368         return copy_tree_at(AT_FDCWD, from, AT_FDCWD, to, merge);
369 }
370
371 int copy_directory_fd(int dirfd, const char *to, bool merge) {
372
373         struct stat st;
374
375         assert(dirfd >= 0);
376         assert(to);
377
378         if (fstat(dirfd, &st) < 0)
379                 return -errno;
380
381         if (!S_ISDIR(st.st_mode))
382                 return -ENOTDIR;
383
384         return fd_copy_directory(dirfd, NULL, &st, AT_FDCWD, to, st.st_dev, merge);
385 }
386
387 int copy_file_fd(const char *from, int fdt, bool try_reflink) {
388         _cleanup_close_ int fdf = -1;
389         int r;
390
391         assert(from);
392         assert(fdt >= 0);
393
394         fdf = open(from, O_RDONLY|O_CLOEXEC|O_NOCTTY);
395         if (fdf < 0)
396                 return -errno;
397
398         r = copy_bytes(fdf, fdt, (uint64_t) -1, try_reflink);
399
400         (void) copy_times(fdf, fdt);
401         (void) copy_xattr(fdf, fdt);
402
403         return r;
404 }
405
406 int copy_file(const char *from, const char *to, int flags, mode_t mode, unsigned chattr_flags) {
407         int fdt = -1, r;
408
409         assert(from);
410         assert(to);
411
412         RUN_WITH_UMASK(0000) {
413                 fdt = open(to, flags|O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY, mode);
414                 if (fdt < 0)
415                         return -errno;
416         }
417
418         if (chattr_flags != 0)
419                 (void) chattr_fd(fdt, chattr_flags, (unsigned) -1);
420
421         r = copy_file_fd(from, fdt, true);
422         if (r < 0) {
423                 close(fdt);
424                 unlink(to);
425                 return r;
426         }
427
428         if (close(fdt) < 0) {
429                 unlink_noerrno(to);
430                 return -errno;
431         }
432
433         return 0;
434 }
435
436 int copy_file_atomic(const char *from, const char *to, mode_t mode, bool replace, unsigned chattr_flags) {
437         _cleanup_free_ char *t = NULL;
438         int r;
439
440         assert(from);
441         assert(to);
442
443         r = tempfn_random(to, NULL, &t);
444         if (r < 0)
445                 return r;
446
447         r = copy_file(from, t, O_NOFOLLOW|O_EXCL, mode, chattr_flags);
448         if (r < 0)
449                 return r;
450
451         if (replace) {
452                 r = renameat(AT_FDCWD, t, AT_FDCWD, to);
453                 if (r < 0)
454                         r = -errno;
455         } else
456                 r = rename_noreplace(AT_FDCWD, t, AT_FDCWD, to);
457         if (r < 0) {
458                 (void) unlink_noerrno(t);
459                 return r;
460         }
461
462         return 0;
463 }
464
465 int copy_times(int fdf, int fdt) {
466         struct timespec ut[2];
467         struct stat st;
468         usec_t crtime = 0;
469
470         assert(fdf >= 0);
471         assert(fdt >= 0);
472
473         if (fstat(fdf, &st) < 0)
474                 return -errno;
475
476         ut[0] = st.st_atim;
477         ut[1] = st.st_mtim;
478
479         if (futimens(fdt, ut) < 0)
480                 return -errno;
481
482         if (fd_getcrtime(fdf, &crtime) >= 0)
483                 (void) fd_setcrtime(fdt, crtime);
484
485         return 0;
486 }
487
488 int copy_xattr(int fdf, int fdt) {
489         _cleanup_free_ char *bufa = NULL, *bufb = NULL;
490         size_t sza = 100, szb = 100;
491         ssize_t n;
492         int ret = 0;
493         const char *p;
494
495         for (;;) {
496                 bufa = malloc(sza);
497                 if (!bufa)
498                         return -ENOMEM;
499
500                 n = flistxattr(fdf, bufa, sza);
501                 if (n == 0)
502                         return 0;
503                 if (n > 0)
504                         break;
505                 if (errno != ERANGE)
506                         return -errno;
507
508                 sza *= 2;
509
510                 bufa = mfree(bufa);
511         }
512
513         p = bufa;
514         while (n > 0) {
515                 size_t l;
516
517                 l = strlen(p);
518                 assert(l < (size_t) n);
519
520                 if (startswith(p, "user.")) {
521                         ssize_t m;
522
523                         if (!bufb) {
524                                 bufb = malloc(szb);
525                                 if (!bufb)
526                                         return -ENOMEM;
527                         }
528
529                         m = fgetxattr(fdf, p, bufb, szb);
530                         if (m < 0) {
531                                 if (errno == ERANGE) {
532                                         szb *= 2;
533                                         bufb = mfree(bufb);
534                                         continue;
535                                 }
536
537                                 return -errno;
538                         }
539
540                         if (fsetxattr(fdt, p, bufb, m, 0) < 0)
541                                 ret = -errno;
542                 }
543
544                 p += l + 1;
545                 n -= l + 1;
546         }
547
548         return ret;
549 }
550 #endif // 0