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