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