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