chiark / gitweb /
92f6e1e114329738b852ba7abde0cfb28681a529
[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         int r, q;
124
125         assert(from);
126         assert(st);
127         assert(to);
128
129         fdf = openat(df, from, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
130         if (fdf < 0)
131                 return -errno;
132
133         fdt = openat(dt, to, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, st->st_mode & 07777);
134         if (fdt < 0)
135                 return -errno;
136
137         r = copy_bytes(fdf, fdt, (off_t) -1, true);
138         if (r < 0) {
139                 unlinkat(dt, to, 0);
140                 return r;
141         }
142
143         if (fchown(fdt, st->st_uid, st->st_gid) < 0)
144                 r = -errno;
145
146         if (fchmod(fdt, st->st_mode & 07777) < 0)
147                 r = -errno;
148
149         (void) copy_times(fdf, fdt);
150         (void) copy_xattr(fdf, fdt);
151
152         q = close(fdt);
153         fdt = -1;
154
155         if (q < 0) {
156                 r = -errno;
157                 unlinkat(dt, to, 0);
158         }
159
160         return r;
161 }
162
163 static int fd_copy_fifo(int df, const char *from, const struct stat *st, int dt, const char *to) {
164         int r;
165
166         assert(from);
167         assert(st);
168         assert(to);
169
170         r = mkfifoat(dt, to, st->st_mode & 07777);
171         if (r < 0)
172                 return -errno;
173
174         if (fchownat(dt, to, st->st_uid, st->st_gid, AT_SYMLINK_NOFOLLOW) < 0)
175                 r = -errno;
176
177         if (fchmodat(dt, to, st->st_mode & 07777, 0) < 0)
178                 r = -errno;
179
180         return r;
181 }
182
183 static int fd_copy_node(int df, const char *from, const struct stat *st, int dt, const char *to) {
184         int r;
185
186         assert(from);
187         assert(st);
188         assert(to);
189
190         r = mknodat(dt, to, st->st_mode, st->st_rdev);
191         if (r < 0)
192                 return -errno;
193
194         if (fchownat(dt, to, st->st_uid, st->st_gid, AT_SYMLINK_NOFOLLOW) < 0)
195                 r = -errno;
196
197         if (fchmodat(dt, to, st->st_mode & 07777, 0) < 0)
198                 r = -errno;
199
200         return r;
201 }
202
203 static int fd_copy_directory(
204                 int df,
205                 const char *from,
206                 const struct stat *st,
207                 int dt,
208                 const char *to,
209                 dev_t original_device,
210                 bool merge) {
211
212         _cleanup_close_ int fdf = -1, fdt = -1;
213         _cleanup_closedir_ DIR *d = NULL;
214         struct dirent *de;
215         bool created;
216         int r;
217
218         assert(st);
219         assert(to);
220
221         if (from)
222                 fdf = openat(df, from, O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
223         else
224                 fdf = fcntl(df, F_DUPFD_CLOEXEC, 3);
225
226         d = fdopendir(fdf);
227         if (!d)
228                 return -errno;
229         fdf = -1;
230
231         r = mkdirat(dt, to, st->st_mode & 07777);
232         if (r >= 0)
233                 created = true;
234         else if (errno == EEXIST && merge)
235                 created = false;
236         else
237                 return -errno;
238
239         fdt = openat(dt, to, O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
240         if (fdt < 0)
241                 return -errno;
242
243         r = 0;
244
245         if (created) {
246                 if (fchown(fdt, st->st_uid, st->st_gid) < 0)
247                         r = -errno;
248
249                 if (fchmod(fdt, st->st_mode & 07777) < 0)
250                         r = -errno;
251
252                 (void) copy_times(fdf, fdt);
253                 (void) copy_xattr(fdf, fdt);
254         }
255
256         FOREACH_DIRENT(de, d, return -errno) {
257                 struct stat buf;
258                 int q;
259
260                 if (fstatat(dirfd(d), de->d_name, &buf, AT_SYMLINK_NOFOLLOW) < 0) {
261                         r = -errno;
262                         continue;
263                 }
264
265                 if (buf.st_dev != original_device)
266                         continue;
267
268                 if (S_ISREG(buf.st_mode))
269                         q = fd_copy_regular(dirfd(d), de->d_name, &buf, fdt, de->d_name);
270                 else if (S_ISDIR(buf.st_mode))
271                         q = fd_copy_directory(dirfd(d), de->d_name, &buf, fdt, de->d_name, original_device, merge);
272                 else if (S_ISLNK(buf.st_mode))
273                         q = fd_copy_symlink(dirfd(d), de->d_name, &buf, fdt, de->d_name);
274                 else if (S_ISFIFO(buf.st_mode))
275                         q = fd_copy_fifo(dirfd(d), de->d_name, &buf, fdt, de->d_name);
276                 else if (S_ISBLK(buf.st_mode) || S_ISCHR(buf.st_mode))
277                         q = fd_copy_node(dirfd(d), de->d_name, &buf, fdt, de->d_name);
278                 else
279                         q = -ENOTSUP;
280
281                 if (q == -EEXIST && merge)
282                         q = 0;
283
284                 if (q < 0)
285                         r = q;
286         }
287
288         return r;
289 }
290
291 int copy_tree_at(int fdf, const char *from, int fdt, const char *to, bool merge) {
292         struct stat st;
293
294         assert(from);
295         assert(to);
296
297         if (fstatat(fdf, from, &st, AT_SYMLINK_NOFOLLOW) < 0)
298                 return -errno;
299
300         if (S_ISREG(st.st_mode))
301                 return fd_copy_regular(fdf, from, &st, fdt, to);
302         else if (S_ISDIR(st.st_mode))
303                 return fd_copy_directory(fdf, from, &st, fdt, to, st.st_dev, merge);
304         else if (S_ISLNK(st.st_mode))
305                 return fd_copy_symlink(fdf, from, &st, fdt, to);
306         else if (S_ISFIFO(st.st_mode))
307                 return fd_copy_fifo(fdf, from, &st, fdt, to);
308         else if (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode))
309                 return fd_copy_node(fdf, from, &st, fdt, to);
310         else
311                 return -ENOTSUP;
312 }
313
314 int copy_tree(const char *from, const char *to, bool merge) {
315         return copy_tree_at(AT_FDCWD, from, AT_FDCWD, to, merge);
316 }
317
318 int copy_directory_fd(int dirfd, const char *to, bool merge) {
319
320         struct stat st;
321
322         assert(dirfd >= 0);
323         assert(to);
324
325         if (fstat(dirfd, &st) < 0)
326                 return -errno;
327
328         if (!S_ISDIR(st.st_mode))
329                 return -ENOTDIR;
330
331         return fd_copy_directory(dirfd, NULL, &st, AT_FDCWD, to, st.st_dev, merge);
332 }
333
334 int copy_file_fd(const char *from, int fdt, bool try_reflink) {
335         _cleanup_close_ int fdf = -1;
336         int r;
337
338         assert(from);
339         assert(fdt >= 0);
340
341         fdf = open(from, O_RDONLY|O_CLOEXEC|O_NOCTTY);
342         if (fdf < 0)
343                 return -errno;
344
345         r = copy_bytes(fdf, fdt, (off_t) -1, try_reflink);
346
347         (void) copy_times(fdf, fdt);
348         (void) copy_xattr(fdf, fdt);
349
350         return r;
351 }
352
353 int copy_file(const char *from, const char *to, int flags, mode_t mode) {
354         int fdt, r;
355
356         assert(from);
357         assert(to);
358
359         fdt = open(to, flags|O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY, mode);
360         if (fdt < 0)
361                 return -errno;
362
363         r = copy_file_fd(from, fdt, true);
364         if (r < 0) {
365                 close(fdt);
366                 unlink(to);
367                 return r;
368         }
369
370         if (close(fdt) < 0) {
371                 unlink_noerrno(to);
372                 return -errno;
373         }
374
375         return 0;
376 }
377
378 int copy_times(int fdf, int fdt) {
379         struct timespec ut[2];
380         struct stat st;
381         usec_t crtime;
382
383         assert(fdf >= 0);
384         assert(fdt >= 0);
385
386         if (fstat(fdf, &st) < 0)
387                 return -errno;
388
389         ut[0] = st.st_atim;
390         ut[1] = st.st_mtim;
391
392         if (futimens(fdt, ut) < 0)
393                 return -errno;
394
395         if (fd_getcrtime(fdf, &crtime) >= 0)
396                 (void) fd_setcrtime(fdt, crtime);
397
398         return 0;
399 }
400
401 int copy_xattr(int fdf, int fdt) {
402         _cleanup_free_ char *bufa = NULL, *bufb = NULL;
403         size_t sza = 100, szb = 100;
404         ssize_t n;
405         int ret = 0;
406         const char *p;
407
408         for (;;) {
409                 bufa = malloc(sza);
410                 if (!bufa)
411                         return -ENOMEM;
412
413                 n = flistxattr(fdf, bufa, sza);
414                 if (n == 0)
415                         return 0;
416                 if (n > 0)
417                         break;
418                 if (errno != ERANGE)
419                         return -errno;
420
421                 sza *= 2;
422
423                 free(bufa);
424                 bufa = NULL;
425         }
426
427         p = bufa;
428         while (n > 0) {
429                 size_t l;
430
431                 l = strlen(p);
432                 assert(l < (size_t) n);
433
434                 if (startswith(p, "user.")) {
435                         ssize_t m;
436
437                         if (!bufb) {
438                                 bufb = malloc(szb);
439                                 if (!bufb)
440                                         return -ENOMEM;
441                         }
442
443                         m = fgetxattr(fdf, p, bufb, szb);
444                         if (m < 0) {
445                                 if (errno == ERANGE) {
446                                         szb *= 2;
447                                         free(bufb);
448                                         bufb = NULL;
449                                         continue;
450                                 }
451
452                                 return -errno;
453                         }
454
455                         if (fsetxattr(fdt, p, bufb, m, 0) < 0)
456                                 ret = -errno;
457                 }
458
459                 p += l + 1;
460                 n -= l + 1;
461         }
462
463         return ret;
464 }