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