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