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