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