]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/fs-util.c
util-lib: move a number of fs operations into fs-util.[ch]
[thirdparty/systemd.git] / src / basic / fs-util.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2010 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 "dirent-util.h"
23 #include "fd-util.h"
24 #include "fileio.h"
25 #include "fs-util.h"
26 #include "mkdir.h"
27 #include "path-util.h"
28 #include "string-util.h"
29 #include "strv.h"
30 #include "util.h"
31
32 int unlink_noerrno(const char *path) {
33 PROTECT_ERRNO;
34 int r;
35
36 r = unlink(path);
37 if (r < 0)
38 return -errno;
39
40 return 0;
41 }
42
43 int rmdir_parents(const char *path, const char *stop) {
44 size_t l;
45 int r = 0;
46
47 assert(path);
48 assert(stop);
49
50 l = strlen(path);
51
52 /* Skip trailing slashes */
53 while (l > 0 && path[l-1] == '/')
54 l--;
55
56 while (l > 0) {
57 char *t;
58
59 /* Skip last component */
60 while (l > 0 && path[l-1] != '/')
61 l--;
62
63 /* Skip trailing slashes */
64 while (l > 0 && path[l-1] == '/')
65 l--;
66
67 if (l <= 0)
68 break;
69
70 t = strndup(path, l);
71 if (!t)
72 return -ENOMEM;
73
74 if (path_startswith(stop, t)) {
75 free(t);
76 return 0;
77 }
78
79 r = rmdir(t);
80 free(t);
81
82 if (r < 0)
83 if (errno != ENOENT)
84 return -errno;
85 }
86
87 return 0;
88 }
89
90
91 int rename_noreplace(int olddirfd, const char *oldpath, int newdirfd, const char *newpath) {
92 struct stat buf;
93 int ret;
94
95 ret = renameat2(olddirfd, oldpath, newdirfd, newpath, RENAME_NOREPLACE);
96 if (ret >= 0)
97 return 0;
98
99 /* renameat2() exists since Linux 3.15, btrfs added support for it later.
100 * If it is not implemented, fallback to another method. */
101 if (!IN_SET(errno, EINVAL, ENOSYS))
102 return -errno;
103
104 /* The link()/unlink() fallback does not work on directories. But
105 * renameat() without RENAME_NOREPLACE gives the same semantics on
106 * directories, except when newpath is an *empty* directory. This is
107 * good enough. */
108 ret = fstatat(olddirfd, oldpath, &buf, AT_SYMLINK_NOFOLLOW);
109 if (ret >= 0 && S_ISDIR(buf.st_mode)) {
110 ret = renameat(olddirfd, oldpath, newdirfd, newpath);
111 return ret >= 0 ? 0 : -errno;
112 }
113
114 /* If it is not a directory, use the link()/unlink() fallback. */
115 ret = linkat(olddirfd, oldpath, newdirfd, newpath, 0);
116 if (ret < 0)
117 return -errno;
118
119 ret = unlinkat(olddirfd, oldpath, 0);
120 if (ret < 0) {
121 /* backup errno before the following unlinkat() alters it */
122 ret = errno;
123 (void) unlinkat(newdirfd, newpath, 0);
124 errno = ret;
125 return -errno;
126 }
127
128 return 0;
129 }
130
131 int readlinkat_malloc(int fd, const char *p, char **ret) {
132 size_t l = 100;
133 int r;
134
135 assert(p);
136 assert(ret);
137
138 for (;;) {
139 char *c;
140 ssize_t n;
141
142 c = new(char, l);
143 if (!c)
144 return -ENOMEM;
145
146 n = readlinkat(fd, p, c, l-1);
147 if (n < 0) {
148 r = -errno;
149 free(c);
150 return r;
151 }
152
153 if ((size_t) n < l-1) {
154 c[n] = 0;
155 *ret = c;
156 return 0;
157 }
158
159 free(c);
160 l *= 2;
161 }
162 }
163
164 int readlink_malloc(const char *p, char **ret) {
165 return readlinkat_malloc(AT_FDCWD, p, ret);
166 }
167
168 int readlink_value(const char *p, char **ret) {
169 _cleanup_free_ char *link = NULL;
170 char *value;
171 int r;
172
173 r = readlink_malloc(p, &link);
174 if (r < 0)
175 return r;
176
177 value = basename(link);
178 if (!value)
179 return -ENOENT;
180
181 value = strdup(value);
182 if (!value)
183 return -ENOMEM;
184
185 *ret = value;
186
187 return 0;
188 }
189
190 int readlink_and_make_absolute(const char *p, char **r) {
191 _cleanup_free_ char *target = NULL;
192 char *k;
193 int j;
194
195 assert(p);
196 assert(r);
197
198 j = readlink_malloc(p, &target);
199 if (j < 0)
200 return j;
201
202 k = file_in_same_dir(p, target);
203 if (!k)
204 return -ENOMEM;
205
206 *r = k;
207 return 0;
208 }
209
210 int readlink_and_canonicalize(const char *p, char **r) {
211 char *t, *s;
212 int j;
213
214 assert(p);
215 assert(r);
216
217 j = readlink_and_make_absolute(p, &t);
218 if (j < 0)
219 return j;
220
221 s = canonicalize_file_name(t);
222 if (s) {
223 free(t);
224 *r = s;
225 } else
226 *r = t;
227
228 path_kill_slashes(*r);
229
230 return 0;
231 }
232
233 int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid) {
234 assert(path);
235
236 /* Under the assumption that we are running privileged we
237 * first change the access mode and only then hand out
238 * ownership to avoid a window where access is too open. */
239
240 if (mode != MODE_INVALID)
241 if (chmod(path, mode) < 0)
242 return -errno;
243
244 if (uid != UID_INVALID || gid != GID_INVALID)
245 if (chown(path, uid, gid) < 0)
246 return -errno;
247
248 return 0;
249 }
250
251 int fchmod_and_fchown(int fd, mode_t mode, uid_t uid, gid_t gid) {
252 assert(fd >= 0);
253
254 /* Under the assumption that we are running privileged we
255 * first change the access mode and only then hand out
256 * ownership to avoid a window where access is too open. */
257
258 if (mode != MODE_INVALID)
259 if (fchmod(fd, mode) < 0)
260 return -errno;
261
262 if (uid != UID_INVALID || gid != GID_INVALID)
263 if (fchown(fd, uid, gid) < 0)
264 return -errno;
265
266 return 0;
267 }
268
269 int fchmod_umask(int fd, mode_t m) {
270 mode_t u;
271 int r;
272
273 u = umask(0777);
274 r = fchmod(fd, m & (~u)) < 0 ? -errno : 0;
275 umask(u);
276
277 return r;
278 }
279
280 int fd_warn_permissions(const char *path, int fd) {
281 struct stat st;
282
283 if (fstat(fd, &st) < 0)
284 return -errno;
285
286 if (st.st_mode & 0111)
287 log_warning("Configuration file %s is marked executable. Please remove executable permission bits. Proceeding anyway.", path);
288
289 if (st.st_mode & 0002)
290 log_warning("Configuration file %s is marked world-writable. Please remove world writability permission bits. Proceeding anyway.", path);
291
292 if (getpid() == 1 && (st.st_mode & 0044) != 0044)
293 log_warning("Configuration file %s is marked world-inaccessible. This has no effect as configuration data is accessible via APIs without restrictions. Proceeding anyway.", path);
294
295 return 0;
296 }
297
298 int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gid, mode_t mode) {
299 _cleanup_close_ int fd;
300 int r;
301
302 assert(path);
303
304 if (parents)
305 mkdir_parents(path, 0755);
306
307 fd = open(path, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY, mode > 0 ? mode : 0644);
308 if (fd < 0)
309 return -errno;
310
311 if (mode > 0) {
312 r = fchmod(fd, mode);
313 if (r < 0)
314 return -errno;
315 }
316
317 if (uid != UID_INVALID || gid != GID_INVALID) {
318 r = fchown(fd, uid, gid);
319 if (r < 0)
320 return -errno;
321 }
322
323 if (stamp != USEC_INFINITY) {
324 struct timespec ts[2];
325
326 timespec_store(&ts[0], stamp);
327 ts[1] = ts[0];
328 r = futimens(fd, ts);
329 } else
330 r = futimens(fd, NULL);
331 if (r < 0)
332 return -errno;
333
334 return 0;
335 }
336
337 int touch(const char *path) {
338 return touch_file(path, false, USEC_INFINITY, UID_INVALID, GID_INVALID, 0);
339 }
340
341 int symlink_idempotent(const char *from, const char *to) {
342 _cleanup_free_ char *p = NULL;
343 int r;
344
345 assert(from);
346 assert(to);
347
348 if (symlink(from, to) < 0) {
349 if (errno != EEXIST)
350 return -errno;
351
352 r = readlink_malloc(to, &p);
353 if (r < 0)
354 return r;
355
356 if (!streq(p, from))
357 return -EINVAL;
358 }
359
360 return 0;
361 }
362
363 int symlink_atomic(const char *from, const char *to) {
364 _cleanup_free_ char *t = NULL;
365 int r;
366
367 assert(from);
368 assert(to);
369
370 r = tempfn_random(to, NULL, &t);
371 if (r < 0)
372 return r;
373
374 if (symlink(from, t) < 0)
375 return -errno;
376
377 if (rename(t, to) < 0) {
378 unlink_noerrno(t);
379 return -errno;
380 }
381
382 return 0;
383 }
384
385 int mknod_atomic(const char *path, mode_t mode, dev_t dev) {
386 _cleanup_free_ char *t = NULL;
387 int r;
388
389 assert(path);
390
391 r = tempfn_random(path, NULL, &t);
392 if (r < 0)
393 return r;
394
395 if (mknod(t, mode, dev) < 0)
396 return -errno;
397
398 if (rename(t, path) < 0) {
399 unlink_noerrno(t);
400 return -errno;
401 }
402
403 return 0;
404 }
405
406 int mkfifo_atomic(const char *path, mode_t mode) {
407 _cleanup_free_ char *t = NULL;
408 int r;
409
410 assert(path);
411
412 r = tempfn_random(path, NULL, &t);
413 if (r < 0)
414 return r;
415
416 if (mkfifo(t, mode) < 0)
417 return -errno;
418
419 if (rename(t, path) < 0) {
420 unlink_noerrno(t);
421 return -errno;
422 }
423
424 return 0;
425 }
426
427 int get_files_in_directory(const char *path, char ***list) {
428 _cleanup_closedir_ DIR *d = NULL;
429 size_t bufsize = 0, n = 0;
430 _cleanup_strv_free_ char **l = NULL;
431
432 assert(path);
433
434 /* Returns all files in a directory in *list, and the number
435 * of files as return value. If list is NULL returns only the
436 * number. */
437
438 d = opendir(path);
439 if (!d)
440 return -errno;
441
442 for (;;) {
443 struct dirent *de;
444
445 errno = 0;
446 de = readdir(d);
447 if (!de && errno != 0)
448 return -errno;
449 if (!de)
450 break;
451
452 dirent_ensure_type(d, de);
453
454 if (!dirent_is_file(de))
455 continue;
456
457 if (list) {
458 /* one extra slot is needed for the terminating NULL */
459 if (!GREEDY_REALLOC(l, bufsize, n + 2))
460 return -ENOMEM;
461
462 l[n] = strdup(de->d_name);
463 if (!l[n])
464 return -ENOMEM;
465
466 l[++n] = NULL;
467 } else
468 n++;
469 }
470
471 if (list) {
472 *list = l;
473 l = NULL; /* avoid freeing */
474 }
475
476 return n;
477 }