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