chiark / gitweb /
fix gcc warnings about uninitialized variables
[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 = -EOPNOTSUPP;
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 -EOPNOTSUPP;
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, unsigned chattr_flags) {
363         int fdt = -1, 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         if (chattr_flags != 0)
375                 (void) chattr_fd(fdt, true, chattr_flags);
376
377         r = copy_file_fd(from, fdt, true);
378         if (r < 0) {
379                 close(fdt);
380                 unlink(to);
381                 return r;
382         }
383
384         if (close(fdt) < 0) {
385                 unlink_noerrno(to);
386                 return -errno;
387         }
388
389         return 0;
390 }
391
392 int copy_file_atomic(const char *from, const char *to, mode_t mode, bool replace, unsigned chattr_flags) {
393         _cleanup_free_ char *t = NULL;
394         int r;
395
396         assert(from);
397         assert(to);
398
399         r = tempfn_random(to, &t);
400         if (r < 0)
401                 return r;
402
403         r = copy_file(from, t, O_NOFOLLOW|O_EXCL, mode, chattr_flags);
404         if (r < 0)
405                 return r;
406
407         if (replace) {
408                 r = renameat(AT_FDCWD, t, AT_FDCWD, to);
409                 if (r < 0)
410                         r = -errno;
411         } else
412                 r = rename_noreplace(AT_FDCWD, t, AT_FDCWD, to);
413         if (r < 0) {
414                 (void) unlink_noerrno(t);
415                 return r;
416         }
417
418         return 0;
419 }
420
421 int copy_times(int fdf, int fdt) {
422         struct timespec ut[2];
423         struct stat st;
424         usec_t crtime = 0;
425
426         assert(fdf >= 0);
427         assert(fdt >= 0);
428
429         if (fstat(fdf, &st) < 0)
430                 return -errno;
431
432         ut[0] = st.st_atim;
433         ut[1] = st.st_mtim;
434
435         if (futimens(fdt, ut) < 0)
436                 return -errno;
437
438         if (fd_getcrtime(fdf, &crtime) >= 0)
439                 (void) fd_setcrtime(fdt, crtime);
440
441         return 0;
442 }
443
444 int copy_xattr(int fdf, int fdt) {
445         _cleanup_free_ char *bufa = NULL, *bufb = NULL;
446         size_t sza = 100, szb = 100;
447         ssize_t n;
448         int ret = 0;
449         const char *p;
450
451         for (;;) {
452                 bufa = malloc(sza);
453                 if (!bufa)
454                         return -ENOMEM;
455
456                 n = flistxattr(fdf, bufa, sza);
457                 if (n == 0)
458                         return 0;
459                 if (n > 0)
460                         break;
461                 if (errno != ERANGE)
462                         return -errno;
463
464                 sza *= 2;
465
466                 free(bufa);
467                 bufa = NULL;
468         }
469
470         p = bufa;
471         while (n > 0) {
472                 size_t l;
473
474                 l = strlen(p);
475                 assert(l < (size_t) n);
476
477                 if (startswith(p, "user.")) {
478                         ssize_t m;
479
480                         if (!bufb) {
481                                 bufb = malloc(szb);
482                                 if (!bufb)
483                                         return -ENOMEM;
484                         }
485
486                         m = fgetxattr(fdf, p, bufb, szb);
487                         if (m < 0) {
488                                 if (errno == ERANGE) {
489                                         szb *= 2;
490                                         free(bufb);
491                                         bufb = NULL;
492                                         continue;
493                                 }
494
495                                 return -errno;
496                         }
497
498                         if (fsetxattr(fdt, p, bufb, m, 0) < 0)
499                                 ret = -errno;
500                 }
501
502                 p += l + 1;
503                 n -= l + 1;
504         }
505
506         return ret;
507 }