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