]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/copy.c
util: in make_stdio() use dup2() rather than dup3()
[thirdparty/systemd.git] / src / shared / copy.c
CommitLineData
849958d1
LP
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
cda134ab
LP
22#include <sys/sendfile.h>
23
849958d1 24#include "util.h"
d7c7c334 25#include "btrfs-util.h"
849958d1
LP
26#include "copy.h"
27
7430ec6a 28int copy_bytes(int fdf, int fdt, off_t max_bytes, bool try_reflink) {
cda134ab 29 bool try_sendfile = true;
0254b455 30 int r;
cda134ab 31
849958d1
LP
32 assert(fdf >= 0);
33 assert(fdt >= 0);
34
0254b455 35 /* Try btrfs reflinks first. */
7430ec6a 36 if (try_reflink && max_bytes == (off_t) -1) {
0254b455
LP
37 r = btrfs_reflink(fdf, fdt);
38 if (r >= 0)
7430ec6a 39 return r;
0254b455
LP
40 }
41
849958d1 42 for (;;) {
cda134ab
LP
43 size_t m = PIPE_BUF;
44 ssize_t n;
93240d3a
LP
45
46 if (max_bytes != (off_t) -1) {
47
48 if (max_bytes <= 0)
84ee0960 49 return -EFBIG;
93240d3a
LP
50
51 if ((off_t) m > max_bytes)
52 m = (size_t) max_bytes;
53 }
54
cda134ab
LP
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];
849958d1 75
cda134ab
LP
76 n = read(fdf, buf, m);
77 if (n < 0)
78 return -errno;
79 if (n == 0) /* EOF */
80 break;
81
0254b455 82 r = loop_write(fdt, buf, (size_t) n, false);
553acb7b
ZJS
83 if (r < 0)
84 return r;
cda134ab 85 }
93240d3a 86
cda134ab 87 next:
93240d3a
LP
88 if (max_bytes != (off_t) -1) {
89 assert(max_bytes >= n);
90 max_bytes -= n;
91 }
849958d1
LP
92 }
93
94 return 0;
95}
96
97static 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
e156347e 109 if (symlinkat(target, dt, to) < 0)
849958d1 110 return -errno;
849958d1
LP
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
118static 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);
e156347e 131 if (fdt < 0)
849958d1 132 return -errno;
849958d1 133
7430ec6a 134 r = copy_bytes(fdf, fdt, (off_t) -1, true);
849958d1
LP
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
157static 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);
e156347e 165 if (r < 0)
849958d1 166 return -errno;
849958d1
LP
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
177static 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);
e156347e 185 if (r < 0)
849958d1 186 return -errno;
849958d1
LP
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
d7c7c334
LP
197static 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
849958d1
LP
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
849958d1
LP
212 assert(st);
213 assert(to);
214
d7c7c334
LP
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);
849958d1
LP
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;
e156347e 228 else if (errno == EEXIST && merge)
849958d1
LP
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
2c455af4
LP
237 r = 0;
238
849958d1
LP
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))
e156347e 262 q = fd_copy_directory(dirfd(d), de->d_name, &buf, fdt, de->d_name, original_device, merge);
849958d1
LP
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
e156347e
LP
272 if (q == -EEXIST && merge)
273 q = 0;
274
849958d1
LP
275 if (q < 0)
276 r = q;
277 }
278
279 return r;
280}
281
e156347e 282int copy_tree(const char *from, const char *to, bool merge) {
849958d1
LP
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))
e156347e 294 return fd_copy_directory(AT_FDCWD, from, &st, AT_FDCWD, to, st.st_dev, merge);
849958d1
LP
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
d7c7c334
LP
305int 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
7430ec6a 321int copy_file_fd(const char *from, int fdt, bool try_reflink) {
cda134ab 322 _cleanup_close_ int fdf = -1;
849958d1
LP
323
324 assert(from);
cda134ab 325 assert(fdt >= 0);
849958d1
LP
326
327 fdf = open(from, O_RDONLY|O_CLOEXEC|O_NOCTTY);
328 if (fdf < 0)
329 return -errno;
330
7430ec6a 331 return copy_bytes(fdf, fdt, (off_t) -1, try_reflink);
cda134ab
LP
332}
333
334int 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
849958d1
LP
340 fdt = open(to, flags|O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY, mode);
341 if (fdt < 0)
342 return -errno;
343
7430ec6a 344 r = copy_file_fd(from, fdt, true);
849958d1 345 if (r < 0) {
cda134ab 346 close(fdt);
849958d1
LP
347 unlink(to);
348 return r;
349 }
350
cda134ab
LP
351 if (close(fdt) < 0) {
352 unlink_noerrno(to);
353 return -errno;
849958d1
LP
354 }
355
356 return 0;
357}