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