]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/copy.c
coredumpctl: show comm name next to PID
[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
1b992147 25int copy_bytes(int fdf, int fdt) {
849958d1
LP
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
e156347e 62 if (symlinkat(target, dt, to) < 0)
849958d1 63 return -errno;
849958d1
LP
64
65 if (fchownat(dt, to, st->st_uid, st->st_gid, AT_SYMLINK_NOFOLLOW) < 0)
66 return -errno;
67
68 return 0;
69}
70
71static int fd_copy_regular(int df, const char *from, const struct stat *st, int dt, const char *to) {
72 _cleanup_close_ int fdf = -1, fdt = -1;
73 int r, q;
74
75 assert(from);
76 assert(st);
77 assert(to);
78
79 fdf = openat(df, from, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
80 if (fdf < 0)
81 return -errno;
82
83 fdt = openat(dt, to, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, st->st_mode & 07777);
e156347e 84 if (fdt < 0)
849958d1 85 return -errno;
849958d1 86
1b992147 87 r = copy_bytes(fdf, fdt);
849958d1
LP
88 if (r < 0) {
89 unlinkat(dt, to, 0);
90 return r;
91 }
92
93 if (fchown(fdt, st->st_uid, st->st_gid) < 0)
94 r = -errno;
95
96 if (fchmod(fdt, st->st_mode & 07777) < 0)
97 r = -errno;
98
99 q = close(fdt);
100 fdt = -1;
101
102 if (q < 0) {
103 r = -errno;
104 unlinkat(dt, to, 0);
105 }
106
107 return r;
108}
109
110static int fd_copy_fifo(int df, const char *from, const struct stat *st, int dt, const char *to) {
111 int r;
112
113 assert(from);
114 assert(st);
115 assert(to);
116
117 r = mkfifoat(dt, to, st->st_mode & 07777);
e156347e 118 if (r < 0)
849958d1 119 return -errno;
849958d1
LP
120
121 if (fchownat(dt, to, st->st_uid, st->st_gid, AT_SYMLINK_NOFOLLOW) < 0)
122 r = -errno;
123
124 if (fchmodat(dt, to, st->st_mode & 07777, 0) < 0)
125 r = -errno;
126
127 return r;
128}
129
130static int fd_copy_node(int df, const char *from, const struct stat *st, int dt, const char *to) {
131 int r;
132
133 assert(from);
134 assert(st);
135 assert(to);
136
137 r = mknodat(dt, to, st->st_mode, st->st_rdev);
e156347e 138 if (r < 0)
849958d1 139 return -errno;
849958d1
LP
140
141 if (fchownat(dt, to, st->st_uid, st->st_gid, AT_SYMLINK_NOFOLLOW) < 0)
142 r = -errno;
143
144 if (fchmodat(dt, to, st->st_mode & 07777, 0) < 0)
145 r = -errno;
146
147 return r;
148}
149
e156347e 150static int fd_copy_directory(int df, const char *from, const struct stat *st, int dt, const char *to, dev_t original_device, bool merge) {
849958d1
LP
151 _cleanup_close_ int fdf = -1, fdt = -1;
152 _cleanup_closedir_ DIR *d = NULL;
153 struct dirent *de;
154 bool created;
155 int r;
156
157 assert(from);
158 assert(st);
159 assert(to);
160
161 fdf = openat(df, from, O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
162 if (fdf < 0)
163 return -errno;
164
165 d = fdopendir(fdf);
166 if (!d)
167 return -errno;
168 fdf = -1;
169
170 r = mkdirat(dt, to, st->st_mode & 07777);
171 if (r >= 0)
172 created = true;
e156347e 173 else if (errno == EEXIST && merge)
849958d1
LP
174 created = false;
175 else
176 return -errno;
177
178 fdt = openat(dt, to, O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
179 if (fdt < 0)
180 return -errno;
181
2c455af4
LP
182 r = 0;
183
849958d1
LP
184 if (created) {
185 if (fchown(fdt, st->st_uid, st->st_gid) < 0)
186 r = -errno;
187
188 if (fchmod(fdt, st->st_mode & 07777) < 0)
189 r = -errno;
190 }
191
192 FOREACH_DIRENT(de, d, return -errno) {
193 struct stat buf;
194 int q;
195
196 if (fstatat(dirfd(d), de->d_name, &buf, AT_SYMLINK_NOFOLLOW) < 0) {
197 r = -errno;
198 continue;
199 }
200
201 if (buf.st_dev != original_device)
202 continue;
203
204 if (S_ISREG(buf.st_mode))
205 q = fd_copy_regular(dirfd(d), de->d_name, &buf, fdt, de->d_name);
206 else if (S_ISDIR(buf.st_mode))
e156347e 207 q = fd_copy_directory(dirfd(d), de->d_name, &buf, fdt, de->d_name, original_device, merge);
849958d1
LP
208 else if (S_ISLNK(buf.st_mode))
209 q = fd_copy_symlink(dirfd(d), de->d_name, &buf, fdt, de->d_name);
210 else if (S_ISFIFO(buf.st_mode))
211 q = fd_copy_fifo(dirfd(d), de->d_name, &buf, fdt, de->d_name);
212 else if (S_ISBLK(buf.st_mode) || S_ISCHR(buf.st_mode))
213 q = fd_copy_node(dirfd(d), de->d_name, &buf, fdt, de->d_name);
214 else
215 q = -ENOTSUP;
216
e156347e
LP
217 if (q == -EEXIST && merge)
218 q = 0;
219
849958d1
LP
220 if (q < 0)
221 r = q;
222 }
223
224 return r;
225}
226
e156347e 227int copy_tree(const char *from, const char *to, bool merge) {
849958d1
LP
228 struct stat st;
229
230 assert(from);
231 assert(to);
232
233 if (lstat(from, &st) < 0)
234 return -errno;
235
236 if (S_ISREG(st.st_mode))
237 return fd_copy_regular(AT_FDCWD, from, &st, AT_FDCWD, to);
238 else if (S_ISDIR(st.st_mode))
e156347e 239 return fd_copy_directory(AT_FDCWD, from, &st, AT_FDCWD, to, st.st_dev, merge);
849958d1
LP
240 else if (S_ISLNK(st.st_mode))
241 return fd_copy_symlink(AT_FDCWD, from, &st, AT_FDCWD, to);
242 else if (S_ISFIFO(st.st_mode))
243 return fd_copy_fifo(AT_FDCWD, from, &st, AT_FDCWD, to);
244 else if (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode))
245 return fd_copy_node(AT_FDCWD, from, &st, AT_FDCWD, to);
246 else
247 return -ENOTSUP;
248}
249
250int copy_file(const char *from, const char *to, int flags, mode_t mode) {
251 _cleanup_close_ int fdf = -1, fdt = -1;
252 int r;
253
254 assert(from);
255 assert(to);
256
257 fdf = open(from, O_RDONLY|O_CLOEXEC|O_NOCTTY);
258 if (fdf < 0)
259 return -errno;
260
261 fdt = open(to, flags|O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY, mode);
262 if (fdt < 0)
263 return -errno;
264
1b992147 265 r = copy_bytes(fdf, fdt);
849958d1
LP
266 if (r < 0) {
267 unlink(to);
268 return r;
269 }
270
271 r = close(fdt);
272 fdt = -1;
273
274 if (r < 0) {
275 r = -errno;
276 unlink(to);
277 return r;
278 }
279
280 return 0;
281}