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