chiark / gitweb /
core: add new ConditionNeedsUpdate= unit condition
[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) {
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
50 static 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
75 static 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 = copy_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
118 static 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
142 static 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
166 static 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
238 int 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
261 int 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 = copy_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 }