chiark / gitweb /
nspawn: beef up nspawn with some btrfs magic
[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
24 #include "util.h"
25 #include "btrfs-util.h"
26 #include "copy.h"
27
28 int copy_bytes(int fdf, int fdt, off_t max_bytes) {
29         bool try_sendfile = true;
30         int r;
31
32         assert(fdf >= 0);
33         assert(fdt >= 0);
34
35         /* Try btrfs reflinks first. */
36         if (max_bytes == (off_t) -1) {
37                 r = btrfs_reflink(fdf, fdt);
38                 if (r >= 0)
39                         return 0;
40         }
41
42         for (;;) {
43                 size_t m = PIPE_BUF;
44                 ssize_t n;
45
46                 if (max_bytes != (off_t) -1) {
47
48                         if (max_bytes <= 0)
49                                 return -EFBIG;
50
51                         if ((off_t) m > max_bytes)
52                                 m = (size_t) max_bytes;
53                 }
54
55                 /* First try sendfile(), unless we already tried */
56                 if (try_sendfile) {
57
58                         n = sendfile(fdt, fdf, NULL, m);
59                         if (n < 0) {
60                                 if (errno != EINVAL && errno != ENOSYS)
61                                         return -errno;
62
63                                 try_sendfile = false;
64                                 /* use fallback below */
65                         } else if (n == 0) /* EOF */
66                                 break;
67                         else if (n > 0)
68                                 /* Succcess! */
69                                 goto next;
70                 }
71
72                 /* As a fallback just copy bits by hand */
73                 {
74                         char buf[m];
75
76                         n = read(fdf, buf, m);
77                         if (n < 0)
78                                 return -errno;
79                         if (n == 0) /* EOF */
80                                 break;
81
82                         r = loop_write(fdt, buf, (size_t) n, false);
83                         if (r < 0)
84                                 return r;
85                 }
86
87         next:
88                 if (max_bytes != (off_t) -1) {
89                         assert(max_bytes >= n);
90                         max_bytes -= n;
91                 }
92         }
93
94         return 0;
95 }
96
97 static int fd_copy_symlink(int df, const char *from, const struct stat *st, int dt, const char *to) {
98         _cleanup_free_ char *target = NULL;
99         int r;
100
101         assert(from);
102         assert(st);
103         assert(to);
104
105         r = readlinkat_malloc(df, from, &target);
106         if (r < 0)
107                 return r;
108
109         if (symlinkat(target, dt, to) < 0)
110                 return -errno;
111
112         if (fchownat(dt, to, st->st_uid, st->st_gid, AT_SYMLINK_NOFOLLOW) < 0)
113                 return -errno;
114
115         return 0;
116 }
117
118 static int fd_copy_regular(int df, const char *from, const struct stat *st, int dt, const char *to) {
119         _cleanup_close_ int fdf = -1, fdt = -1;
120         int r, q;
121
122         assert(from);
123         assert(st);
124         assert(to);
125
126         fdf = openat(df, from, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
127         if (fdf < 0)
128                 return -errno;
129
130         fdt = openat(dt, to, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, st->st_mode & 07777);
131         if (fdt < 0)
132                 return -errno;
133
134         r = copy_bytes(fdf, fdt, (off_t) -1);
135         if (r < 0) {
136                 unlinkat(dt, to, 0);
137                 return r;
138         }
139
140         if (fchown(fdt, st->st_uid, st->st_gid) < 0)
141                 r = -errno;
142
143         if (fchmod(fdt, st->st_mode & 07777) < 0)
144                 r = -errno;
145
146         q = close(fdt);
147         fdt = -1;
148
149         if (q < 0) {
150                 r = -errno;
151                 unlinkat(dt, to, 0);
152         }
153
154         return r;
155 }
156
157 static int fd_copy_fifo(int df, const char *from, const struct stat *st, int dt, const char *to) {
158         int r;
159
160         assert(from);
161         assert(st);
162         assert(to);
163
164         r = mkfifoat(dt, to, st->st_mode & 07777);
165         if (r < 0)
166                 return -errno;
167
168         if (fchownat(dt, to, st->st_uid, st->st_gid, AT_SYMLINK_NOFOLLOW) < 0)
169                 r = -errno;
170
171         if (fchmodat(dt, to, st->st_mode & 07777, 0) < 0)
172                 r = -errno;
173
174         return r;
175 }
176
177 static int fd_copy_node(int df, const char *from, const struct stat *st, int dt, const char *to) {
178         int r;
179
180         assert(from);
181         assert(st);
182         assert(to);
183
184         r = mknodat(dt, to, st->st_mode, st->st_rdev);
185         if (r < 0)
186                 return -errno;
187
188         if (fchownat(dt, to, st->st_uid, st->st_gid, AT_SYMLINK_NOFOLLOW) < 0)
189                 r = -errno;
190
191         if (fchmodat(dt, to, st->st_mode & 07777, 0) < 0)
192                 r = -errno;
193
194         return r;
195 }
196
197 static int fd_copy_directory(
198                 int df,
199                 const char *from,
200                 const struct stat *st,
201                 int dt,
202                 const char *to,
203                 dev_t original_device,
204                 bool merge) {
205
206         _cleanup_close_ int fdf = -1, fdt = -1;
207         _cleanup_closedir_ DIR *d = NULL;
208         struct dirent *de;
209         bool created;
210         int r;
211
212         assert(st);
213         assert(to);
214
215         if (from)
216                 fdf = openat(df, from, O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
217         else
218                 fdf = fcntl(df, F_DUPFD_CLOEXEC, 3);
219
220         d = fdopendir(fdf);
221         if (!d)
222                 return -errno;
223         fdf = -1;
224
225         r = mkdirat(dt, to, st->st_mode & 07777);
226         if (r >= 0)
227                 created = true;
228         else if (errno == EEXIST && merge)
229                 created = false;
230         else
231                 return -errno;
232
233         fdt = openat(dt, to, O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
234         if (fdt < 0)
235                 return -errno;
236
237         r = 0;
238
239         if (created) {
240                 if (fchown(fdt, st->st_uid, st->st_gid) < 0)
241                         r = -errno;
242
243                 if (fchmod(fdt, st->st_mode & 07777) < 0)
244                         r = -errno;
245         }
246
247         FOREACH_DIRENT(de, d, return -errno) {
248                 struct stat buf;
249                 int q;
250
251                 if (fstatat(dirfd(d), de->d_name, &buf, AT_SYMLINK_NOFOLLOW) < 0) {
252                         r = -errno;
253                         continue;
254                 }
255
256                 if (buf.st_dev != original_device)
257                         continue;
258
259                 if (S_ISREG(buf.st_mode))
260                         q = fd_copy_regular(dirfd(d), de->d_name, &buf, fdt, de->d_name);
261                 else if (S_ISDIR(buf.st_mode))
262                         q = fd_copy_directory(dirfd(d), de->d_name, &buf, fdt, de->d_name, original_device, merge);
263                 else if (S_ISLNK(buf.st_mode))
264                         q = fd_copy_symlink(dirfd(d), de->d_name, &buf, fdt, de->d_name);
265                 else if (S_ISFIFO(buf.st_mode))
266                         q = fd_copy_fifo(dirfd(d), de->d_name, &buf, fdt, de->d_name);
267                 else if (S_ISBLK(buf.st_mode) || S_ISCHR(buf.st_mode))
268                         q = fd_copy_node(dirfd(d), de->d_name, &buf, fdt, de->d_name);
269                 else
270                         q = -ENOTSUP;
271
272                 if (q == -EEXIST && merge)
273                         q = 0;
274
275                 if (q < 0)
276                         r = q;
277         }
278
279         return r;
280 }
281
282 int copy_tree(const char *from, const char *to, bool merge) {
283         struct stat st;
284
285         assert(from);
286         assert(to);
287
288         if (lstat(from, &st) < 0)
289                 return -errno;
290
291         if (S_ISREG(st.st_mode))
292                 return fd_copy_regular(AT_FDCWD, from, &st, AT_FDCWD, to);
293         else if (S_ISDIR(st.st_mode))
294                 return fd_copy_directory(AT_FDCWD, from, &st, AT_FDCWD, to, st.st_dev, merge);
295         else if (S_ISLNK(st.st_mode))
296                 return fd_copy_symlink(AT_FDCWD, from, &st, AT_FDCWD, to);
297         else if (S_ISFIFO(st.st_mode))
298                 return fd_copy_fifo(AT_FDCWD, from, &st, AT_FDCWD, to);
299         else if (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode))
300                 return fd_copy_node(AT_FDCWD, from, &st, AT_FDCWD, to);
301         else
302                 return -ENOTSUP;
303 }
304
305 int copy_tree_fd(int dirfd, const char *to, bool merge) {
306
307         struct stat st;
308
309         assert(dirfd >= 0);
310         assert(to);
311
312         if (fstat(dirfd, &st) < 0)
313                 return -errno;
314
315         if (!S_ISDIR(st.st_mode))
316                 return -ENOTDIR;
317
318         return fd_copy_directory(dirfd, NULL, &st, AT_FDCWD, to, st.st_dev, merge);
319 }
320
321 int copy_file_fd(const char *from, int fdt) {
322         _cleanup_close_ int fdf = -1;
323
324         assert(from);
325         assert(fdt >= 0);
326
327         fdf = open(from, O_RDONLY|O_CLOEXEC|O_NOCTTY);
328         if (fdf < 0)
329                 return -errno;
330
331         return copy_bytes(fdf, fdt, (off_t) -1);
332 }
333
334 int copy_file(const char *from, const char *to, int flags, mode_t mode) {
335         int fdt, r;
336
337         assert(from);
338         assert(to);
339
340         fdt = open(to, flags|O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY, mode);
341         if (fdt < 0)
342                 return -errno;
343
344         r = copy_file_fd(from, fdt);
345         if (r < 0) {
346                 close(fdt);
347                 unlink(to);
348                 return r;
349         }
350
351         if (close(fdt) < 0) {
352                 unlink_noerrno(to);
353                 return -errno;
354         }
355
356         return 0;
357 }