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