]> git.ipfire.org Git - thirdparty/util-linux.git/blame - libmount/src/utils.c
libmount: fix mount.nfs segfault, rely on assert() rather than on nonnull
[thirdparty/util-linux.git] / libmount / src / utils.c
CommitLineData
69b7e41e
KZ
1/*
2 * Copyright (C) 2008-2009 Karel Zak <kzak@redhat.com>
3 *
4 * This file may be redistributed under the terms of the
5 * GNU Lesser General Public License.
6 */
7
192c6aad
KZ
8/**
9 * SECTION: utils
10 * @title: Utils
11 * @short_description: misc utils.
12 */
69b7e41e 13#include <ctype.h>
69b7e41e
KZ
14#include <fcntl.h>
15#include <pwd.h>
a1e8af75 16#include <grp.h>
69b7e41e 17
b49103ed 18#include "strutils.h"
0532ba1d 19#include "pathnames.h"
69b7e41e 20#include "mountP.h"
3c5e4ef8 21#include "mangle.h"
0bb44be3 22#include "canonicalize.h"
035507c8 23#include "env.h"
b483debb 24#include "match.h"
69b7e41e 25
b49103ed
KZ
26int endswith(const char *s, const char *sx)
27{
28 ssize_t off;
29
30 assert(s);
31 assert(sx);
32
33 off = strlen(s);
34 if (!off)
35 return 0;
36 off -= strlen(sx);
37 if (off < 0)
38 return 0;
39
40 return !strcmp(s + off, sx);
41}
42
43int startswith(const char *s, const char *sx)
44{
45 size_t off;
46
47 assert(s);
48 assert(sx);
49
50 off = strlen(sx);
51 if (!off)
52 return 0;
53
54 return !strncmp(s, sx, off);
55}
56
dad88cb3
KZ
57/*
58 * Return 1 if the file does not accessible of empty
59 */
60int is_file_empty(const char *name)
61{
62 struct stat st;
63 assert(name);
64
65 return (stat(name, &st) != 0 || st.st_size == 0);
66}
67
fd1eb7a7
KZ
68int mnt_parse_offset(const char *str, size_t len, uintmax_t *res)
69{
70 char *p;
71 int rc = 0;
72
0208ae2e 73 if (!str || !*str)
fd1eb7a7
KZ
74 return -EINVAL;
75
76 p = strndup(str, len);
77 if (!p)
78 return -errno;
79
80 if (strtosize(p, res))
81 rc = -EINVAL;
82 free(p);
83 return rc;
84}
85
7383ebce
DR
86/* used as a callback by bsearch in mnt_fstype_is_pseudofs() */
87static int fstype_cmp(const void *v1, const void *v2)
88{
89 const char *s1 = *(const char **)v1;
90 const char *s2 = *(const char **)v2;
91
92 return strcmp(s1, s2);
93}
94
1d0cd73f
KZ
95/* returns basename and keeps dirname in the @path, if @path is "/" (root)
96 * then returns empty string */
dcc15ce5 97char *stripoff_last_component(char *path)
1d0cd73f
KZ
98{
99 char *p = path ? strrchr(path, '/') : NULL;
100
101 if (!p)
102 return NULL;
103 *p = '\0';
61073773 104 return p + 1;
1d0cd73f
KZ
105}
106
61073773
KZ
107/*
108 * Note that the @target has to be absolute path (so at least "/"). The
109 * @filename returns allocated buffer with last path component, for example:
110 *
111 * mnt_chdir_to_parent("/mnt/test", &buf) ==> chdir("/mnt"), buf="test"
66bb8267
KZ
112 */
113int mnt_chdir_to_parent(const char *target, char **filename)
114{
61073773 115 char *buf, *parent, *last = NULL;
66bb8267
KZ
116 char cwd[PATH_MAX];
117 int rc = -EINVAL;
118
119 if (!target || *target != '/')
120 return -EINVAL;
121
61073773
KZ
122 DBG(UTILS, mnt_debug("moving to %s parent", target));
123
124 buf = strdup(target);
125 if (!buf)
66bb8267
KZ
126 return -ENOMEM;
127
61073773
KZ
128 if (*(buf + 1) != '\0') {
129 last = stripoff_last_component(buf);
66bb8267
KZ
130 if (!last)
131 goto err;
132 }
66bb8267 133
61073773
KZ
134 parent = buf && *buf ? buf : "/";
135
136 if (chdir(parent) == -1) {
137 DBG(UTILS, mnt_debug("failed to chdir to %s: %m", parent));
66bb8267
KZ
138 rc = -errno;
139 goto err;
140 }
141 if (!getcwd(cwd, sizeof(cwd))) {
142 DBG(UTILS, mnt_debug("failed to obtain current directory: %m"));
143 rc = -errno;
144 goto err;
145 }
61073773
KZ
146 if (strcmp(cwd, parent) != 0) {
147 DBG(UTILS, mnt_debug(
148 "unexpected chdir (expected=%s, cwd=%s)", parent, cwd));
66bb8267
KZ
149 goto err;
150 }
151
61073773
KZ
152 DBG(CXT, mnt_debug(
153 "current directory moved to %s [last_component='%s']",
154 parent, last));
66bb8267 155
ba2bdf41
KZ
156 if (filename) {
157 *filename = buf;
66bb8267 158
ba2bdf41
KZ
159 if (!last || !*last)
160 memcpy(*filename, ".", 2);
161 else
162 memcpy(*filename, last, strlen(last) + 1);
163 }
66bb8267
KZ
164 return 0;
165err:
61073773 166 free(buf);
66bb8267
KZ
167 return rc;
168}
169
f9906424
KZ
170/*
171 * Check if @path is on read-only filesystem independently on file permissions.
172 */
173int mnt_is_readonly(const char *path)
174{
175 if (access(path, W_OK) == 0)
176 return 0;
177 if (errno == EROFS)
178 return 1;
179 if (errno != EACCES)
180 return 0;
181
182#ifdef HAVE_FUTIMENS
183 /*
184 * access(2) returns EACCES on read-only FS:
185 *
186 * - for set-uid application if one component of the path is not
187 * accessible for the current rUID. (Note that euidaccess(2) does not
188 * check for EROFS at all).
189 *
190 * - for read-write filesystem with read-only VFS node (aka -o remount,ro,bind)
191 */
192 {
193 struct timespec times[2];
194
195 times[0].tv_nsec = UTIME_NOW; /* atime */
196 times[1].tv_nsec = UTIME_OMIT; /* mtime */
197
198 if (utimensat(AT_FDCWD, path, times, 0) == -1)
199 return errno == EROFS;
200 }
201#endif
202 return 0;
203}
204
3c5e4ef8
KZ
205/**
206 * mnt_mangle:
207 * @str: string
208 *
209 * Encode @str to be compatible with fstab/mtab
210 *
211 * Returns: new allocated string or NULL in case of error.
212 */
213char *mnt_mangle(const char *str)
214{
215 return mangle(str);
216}
217
218/**
219 * mnt_unmangle:
220 * @str: string
221 *
222 * Decode @str from fstab/mtab
223 *
224 * Returns: new allocated string or NULL in case of error.
225 */
226char *mnt_unmangle(const char *str)
227{
dd369652 228 return unmangle(str, NULL);
3c5e4ef8
KZ
229}
230
69b7e41e
KZ
231/**
232 * mnt_fstype_is_pseudofs:
233 * @type: filesystem name
234 *
235 * Returns: 1 for filesystems like proc, sysfs, ... or 0.
236 */
237int mnt_fstype_is_pseudofs(const char *type)
238{
7383ebce
DR
239 /* This array must remain sorted when adding new fstypes */
240 static const char *pseudofs[] = {
241 "anon_inodefs",
242 "autofs",
243 "bdev",
244 "binfmt_misc",
245 "cgroup",
246 "configfs",
247 "cpuset",
248 "debugfs",
249 "devfs",
250 "devpts",
251 "devtmpfs",
252 "dlmfs",
253 "fuse.gvfs-fuse-daemon",
254 "fusectl",
255 "hugetlbfs",
256 "mqueue",
257 "nfsd",
258 "none",
259 "pipefs",
260 "proc",
261 "pstore",
262 "ramfs",
263 "rootfs",
264 "rpc_pipefs",
265 "securityfs",
266 "sockfs",
267 "spufs",
268 "sysfs",
269 "tmpfs"
270 };
271
4569bbea
KZ
272 assert(type);
273
7383ebce
DR
274 return !(bsearch(&type, pseudofs, ARRAY_SIZE(pseudofs),
275 sizeof(char*), fstype_cmp) == NULL);
69b7e41e
KZ
276}
277
278/**
279 * mnt_fstype_is_netfs:
280 * @type: filesystem name
281 *
282 * Returns: 1 for filesystems like cifs, nfs, ... or 0.
283 */
284int mnt_fstype_is_netfs(const char *type)
285{
4569bbea
KZ
286 assert(type);
287
c59cf20c 288 if (strcmp(type, "cifs") == 0 ||
69b7e41e 289 strcmp(type, "smbfs") == 0 ||
c59cf20c
KZ
290 strncmp(type,"nfs", 3) == 0 ||
291 strcmp(type, "afs") == 0 ||
292 strcmp(type, "ncpfs") == 0 ||
293 strncmp(type,"9p", 2) == 0)
69b7e41e
KZ
294 return 1;
295 return 0;
296}
297
abc9c0f7
KZ
298/**
299 * mnt_match_fstype:
300 * @type: filesystem type
6ad929bb 301 * @pattern: filesystem name or comma delimited list of names
abc9c0f7
KZ
302 *
303 * The @pattern list of filesystem can be prefixed with a global
304 * "no" prefix to invert matching of the whole list. The "no" could
6ad929bb 305 * also be used for individual items in the @pattern list. So,
3d735589 306 * "nofoo,bar" has the same meaning as "nofoo,nobar".
abc9c0f7 307 *
3d735589
KZ
308 * "bar" : "nofoo,bar" -> False (global "no" prefix)
309 *
310 * "bar" : "foo,bar" -> True
abc9c0f7 311 *
abc9c0f7
KZ
312 * "bar" : "foo,nobar" -> False
313 *
314 * Returns: 1 if type is matching, else 0. This function also returns
315 * 0 if @pattern is NULL and @type is non-NULL.
316 */
317int mnt_match_fstype(const char *type, const char *pattern)
318{
12089155 319 return match_fstype(type, pattern);
abc9c0f7
KZ
320}
321
322
323/* Returns 1 if needle found or noneedle not found in haystack
324 * Otherwise returns 0
325 */
326static int check_option(const char *haystack, size_t len,
327 const char *needle, size_t needle_len)
328{
329 const char *p;
330 int no = 0;
331
ede8a60e
KZ
332 if (needle_len >= 1 && *needle == '+') {
333 needle++;
334 needle_len--;
335 } else if (needle_len >= 2 && !strncmp(needle, "no", 2)) {
abc9c0f7
KZ
336 no = 1;
337 needle += 2;
338 needle_len -= 2;
339 }
340
341 for (p = haystack; p && p < haystack + len; p++) {
342 char *sep = strchr(p, ',');
7fc6d2b8
KZ
343 size_t plen = sep ? (size_t) (sep - p) :
344 len - (p - haystack);
abc9c0f7
KZ
345
346 if (plen == needle_len) {
347 if (!strncmp(p, needle, plen))
348 return !no; /* foo or nofoo was found */
349 }
350 p += plen;
351 }
352
353 return no; /* foo or nofoo was not found */
354}
355
356/**
357 * mnt_match_options:
358 * @optstr: options string
6ad929bb 359 * @pattern: comma delimited list of options
abc9c0f7
KZ
360 *
361 * The "no" could used for individual items in the @options list. The "no"
6ad929bb 362 * prefix does not have a global meaning.
abc9c0f7
KZ
363 *
364 * Unlike fs type matching, nonetdev,user and nonetdev,nouser have
365 * DIFFERENT meanings; each option is matched explicitly as specified.
366 *
ede8a60e
KZ
367 * The "no" prefix interpretation could be disable by "+" prefix, for example
368 * "+noauto" matches if @optstr literally contains "noauto" string.
369 *
3d735589
KZ
370 * "xxx,yyy,zzz" : "nozzz" -> False
371 *
372 * "xxx,yyy,zzz" : "xxx,noeee" -> True
abc9c0f7 373 *
ede8a60e
KZ
374 * "bar,zzz" : "nofoo" -> True
375 *
376 * "nofoo,bar" : "+nofoo" -> True
377 *
378 * "bar,zzz" : "+nofoo" -> False
379 *
380 *
abc9c0f7
KZ
381 * Returns: 1 if pattern is matching, else 0. This function also returns 0
382 * if @pattern is NULL and @optstr is non-NULL.
383 */
384int mnt_match_options(const char *optstr, const char *pattern)
385{
386 const char *p;
387 size_t len, optstr_len = 0;
388
389 if (!pattern && !optstr)
390 return 1;
391 if (!pattern)
392 return 0;
393
394 len = strlen(pattern);
395 if (optstr)
396 optstr_len = strlen(optstr);
397
398 for (p = pattern; p < pattern + len; p++) {
399 char *sep = strchr(p, ',');
7fc6d2b8
KZ
400 size_t plen = sep ? (size_t) (sep - p) :
401 len - (p - pattern);
abc9c0f7
KZ
402
403 if (!plen)
404 continue; /* if two ',' appear in a row */
405
406 if (!check_option(optstr, optstr_len, p, plen))
407 return 0; /* any match failure means failure */
408
409 p += plen;
410 }
411
412 /* no match failures in list means success */
413 return 1;
414}
415
97e23b5e
KZ
416void mnt_free_filesystems(char **filesystems)
417{
418 char **p;
419
420 if (!filesystems)
421 return;
422 for (p = filesystems; *p; p++)
423 free(*p);
424 free(filesystems);
425}
426
427static int add_filesystem(char ***filesystems, char *name)
428{
429 int n = 0;
430
431 assert(filesystems);
432 assert(name);
433
434 if (*filesystems) {
435 char **p;
436 for (n = 0, p = *filesystems; *p; p++, n++) {
437 if (strcmp(*p, name) == 0)
438 return 0;
439 }
440 }
441
442 #define MYCHUNK 16
443
444 if (n == 0 || !((n + 1) % MYCHUNK)) {
445 size_t items = ((n + 1 + MYCHUNK) / MYCHUNK) * MYCHUNK;
446 char **x = realloc(*filesystems, items * sizeof(char *));
447
448 if (!x)
449 goto err;
450 *filesystems = x;
451 }
452 name = strdup(name);
453 if (!name)
454 goto err;
455 (*filesystems)[n] = name;
456 (*filesystems)[n + 1] = NULL;
457 return 0;
458err:
459 mnt_free_filesystems(*filesystems);
460 return -ENOMEM;
461}
462
463static int get_filesystems(const char *filename, char ***filesystems, const char *pattern)
464{
4f69189f 465 int rc = 0;
97e23b5e
KZ
466 FILE *f;
467 char line[128];
468
1eb8539d 469 f = fopen(filename, "r" UL_CLOEXECSTR);
97e23b5e 470 if (!f)
ec8121b1
KZ
471 return 1;
472
473 DBG(UTILS, mnt_debug("reading filesystems list from: %s", filename));
97e23b5e
KZ
474
475 while (fgets(line, sizeof(line), f)) {
476 char name[sizeof(line)];
97e23b5e
KZ
477
478 if (*line == '#' || strncmp(line, "nodev", 5) == 0)
479 continue;
480 if (sscanf(line, " %128[^\n ]\n", name) != 1)
481 continue;
ec8121b1
KZ
482 if (strcmp(name, "*") == 0) {
483 rc = 1;
484 break; /* end of the /etc/filesystems */
485 }
97e23b5e
KZ
486 if (pattern && !mnt_match_fstype(name, pattern))
487 continue;
488 rc = add_filesystem(filesystems, name);
489 if (rc)
4f69189f 490 break;
97e23b5e 491 }
4f69189f
KZ
492
493 fclose(f);
494 return rc;
97e23b5e
KZ
495}
496
3de77c21 497/*
ec8121b1
KZ
498 * Always check @filesystems pointer!
499 *
500 * man mount:
501 *
502 * ...mount will try to read the file /etc/filesystems, or, if that does not
503 * exist, /proc/filesystems. All of the filesystem types listed there will
504 * be tried, except for those that are labeled "nodev" (e.g., devpts,
505 * proc and nfs). If /etc/filesystems ends in a line with a single * only,
123ddced 506 * mount will read /proc/filesystems afterwards.
3de77c21 507 */
97e23b5e
KZ
508int mnt_get_filesystems(char ***filesystems, const char *pattern)
509{
510 int rc;
511
512 if (!filesystems)
513 return -EINVAL;
ec8121b1 514
97e23b5e
KZ
515 *filesystems = NULL;
516
517 rc = get_filesystems(_PATH_FILESYSTEMS, filesystems, pattern);
ec8121b1 518 if (rc != 1)
97e23b5e 519 return rc;
ec8121b1
KZ
520
521 rc = get_filesystems(_PATH_PROC_FILESYSTEMS, filesystems, pattern);
522 if (rc == 1 && *filesystems)
523 rc = 0; /* not found /proc/filesystems */
524
525 return rc;
97e23b5e
KZ
526}
527
2af0a839
KZ
528static size_t get_pw_record_size(void)
529{
530#ifdef _SC_GETPW_R_SIZE_MAX
531 long sz = sysconf(_SC_GETPW_R_SIZE_MAX);
532 if (sz > 0)
533 return sz;
534#endif
535 return 16384;
536}
537
69b7e41e
KZ
538/*
539 * Returns allocated string with username or NULL.
540 */
541char *mnt_get_username(const uid_t uid)
542{
543 struct passwd pwd;
544 struct passwd *res;
2af0a839 545 size_t sz = get_pw_record_size();
69b7e41e
KZ
546 char *buf, *username = NULL;
547
69b7e41e
KZ
548 buf = malloc(sz);
549 if (!buf)
550 return NULL;
551
552 if (!getpwuid_r(uid, &pwd, buf, sz, &res) && res)
553 username = strdup(pwd.pw_name);
554
555 free(buf);
556 return username;
557}
abc9c0f7 558
a1e8af75
KZ
559int mnt_get_uid(const char *username, uid_t *uid)
560{
188dc15a 561 int rc = -1;
a1e8af75
KZ
562 struct passwd pwd;
563 struct passwd *pw;
2af0a839 564 size_t sz = get_pw_record_size();
a1e8af75
KZ
565 char *buf;
566
188dc15a
KZ
567 if (!username || !uid)
568 return -EINVAL;
b47b7b3a 569
a1e8af75
KZ
570 buf = malloc(sz);
571 if (!buf)
572 return -ENOMEM;
573
188dc15a 574 if (!getpwnam_r(username, &pwd, buf, sz, &pw) && pw) {
a1e8af75 575 *uid= pw->pw_uid;
188dc15a
KZ
576 rc = 0;
577 } else {
578 DBG(UTILS, mnt_debug(
579 "cannot convert '%s' username to UID", username));
61f5ff6c 580 rc = errno ? -errno : -EINVAL;
188dc15a 581 }
a1e8af75
KZ
582
583 free(buf);
188dc15a 584 return rc;
a1e8af75
KZ
585}
586
587int mnt_get_gid(const char *groupname, gid_t *gid)
588{
188dc15a 589 int rc = -1;
a1e8af75
KZ
590 struct group grp;
591 struct group *gr;
2af0a839 592 size_t sz = get_pw_record_size();
a1e8af75
KZ
593 char *buf;
594
188dc15a
KZ
595 if (!groupname || !gid)
596 return -EINVAL;
b47b7b3a 597
a1e8af75
KZ
598 buf = malloc(sz);
599 if (!buf)
600 return -ENOMEM;
601
188dc15a 602 if (!getgrnam_r(groupname, &grp, buf, sz, &gr) && gr) {
a1e8af75 603 *gid= gr->gr_gid;
188dc15a
KZ
604 rc = 0;
605 } else {
606 DBG(UTILS, mnt_debug(
607 "cannot convert '%s' groupname to GID", groupname));
61f5ff6c 608 rc = errno ? -errno : -EINVAL;
188dc15a 609 }
a1e8af75
KZ
610
611 free(buf);
188dc15a
KZ
612 return rc;
613}
614
615int mnt_in_group(gid_t gid)
616{
617 int rc = 0, n, i;
618 gid_t *grps = NULL;
619
620 if (getgid() == gid)
621 return 1;
622
623 n = getgroups(0, NULL);
624 if (n <= 0)
625 goto done;
626
627 grps = malloc(n * sizeof(*grps));
628 if (!grps)
629 goto done;
630
631 if (getgroups(n, grps) == n) {
632 for (i = 0; i < n; i++) {
633 if (grps[i] == gid) {
634 rc = 1;
635 break;
636 }
637 }
638 }
639done:
640 free(grps);
641 return rc;
a1e8af75
KZ
642}
643
1d0cd73f
KZ
644static int try_write(const char *filename)
645{
646 int fd;
647
648 if (!filename)
649 return -EINVAL;
650
d39c2b43
KZ
651 fd = open(filename, O_RDWR|O_CREAT|O_CLOEXEC,
652 S_IWUSR|S_IRUSR|S_IRGRP|S_IROTH);
1d0cd73f
KZ
653 if (fd >= 0) {
654 close(fd);
655 return 0;
656 }
657 return -errno;
658}
659
660/**
661 * mnt_has_regular_mtab:
662 * @mtab: returns path to mtab
663 * @writable: returns 1 if the file is writable
664 *
665 * If the file does not exist and @writable argument is not NULL then it will
666 * try to create the file
667 *
6ad929bb 668 * Returns: 1 if /etc/mtab is a regular file, and 0 in case of error (check
1d0cd73f 669 * errno for more details).
0532ba1d 670 */
1d0cd73f 671int mnt_has_regular_mtab(const char **mtab, int *writable)
0532ba1d
KZ
672{
673 struct stat st;
70bf97ae 674 int rc;
1d0cd73f 675 const char *filename = mtab && *mtab ? *mtab : mnt_get_mtab_path();
0532ba1d 676
7c118af7
KZ
677 if (writable)
678 *writable = 0;
70bf97ae 679 if (mtab && !*mtab)
1d0cd73f
KZ
680 *mtab = filename;
681
682 DBG(UTILS, mnt_debug("mtab: %s", filename));
70bf97ae 683
1d0cd73f
KZ
684 rc = lstat(filename, &st);
685
686 if (rc == 0) {
687 /* file exist */
688 if (S_ISREG(st.st_mode)) {
689 if (writable)
690 *writable = !try_write(filename);
691 return 1;
692 }
7c118af7 693 goto done;
1d0cd73f
KZ
694 }
695
696 /* try to create the file */
70bf97ae 697 if (writable) {
1d0cd73f 698 *writable = !try_write(filename);
7c118af7
KZ
699 if (*writable)
700 return 1;
1d0cd73f
KZ
701 }
702
7c118af7
KZ
703done:
704 DBG(UTILS, mnt_debug("%s: irregular/non-writable", filename));
1d0cd73f
KZ
705 return 0;
706}
7c118af7 707
77417bc0
KZ
708/*
709 * Don't export this to libmount API -- utab is private library stuff.
1d0cd73f
KZ
710 *
711 * If the file does not exist and @writable argument is not NULL then it will
a362ae60 712 * try to create the directory (e.g. /run/mount) and the file.
1d0cd73f 713 *
a362ae60 714 * Returns: 1 if utab is a regular file, and 0 in case of
6f5788c5 715 * error (check errno for more details).
1d0cd73f 716 */
1d0cd73f
KZ
717int mnt_has_regular_utab(const char **utab, int *writable)
718{
719 struct stat st;
720 int rc;
721 const char *filename = utab && *utab ? *utab : mnt_get_utab_path();
722
7c118af7
KZ
723 if (writable)
724 *writable = 0;
1d0cd73f
KZ
725 if (utab && !*utab)
726 *utab = filename;
727
728 DBG(UTILS, mnt_debug("utab: %s", filename));
729
730 rc = lstat(filename, &st);
731
732 if (rc == 0) {
733 /* file exist */
734 if (S_ISREG(st.st_mode)) {
735 if (writable)
7c118af7 736 *writable = !try_write(filename);
1d0cd73f 737 return 1;
70bf97ae 738 }
7c118af7 739 goto done; /* it's not regular file */
70bf97ae 740 }
1d0cd73f
KZ
741
742 if (writable) {
743 char *dirname = strdup(filename);
744
745 if (!dirname)
7c118af7 746 goto done;
1d0cd73f
KZ
747
748 stripoff_last_component(dirname); /* remove filename */
749
b0bb8fb6
KZ
750 rc = mkdir(dirname, S_IWUSR|
751 S_IRUSR|S_IRGRP|S_IROTH|
752 S_IXUSR|S_IXGRP|S_IXOTH);
1d0cd73f
KZ
753 free(dirname);
754 if (rc && errno != EEXIST)
7c118af7 755 goto done; /* probably EACCES */
1d0cd73f
KZ
756
757 *writable = !try_write(filename);
7c118af7
KZ
758 if (*writable)
759 return 1;
1d0cd73f 760 }
7c118af7
KZ
761done:
762 DBG(UTILS, mnt_debug("%s: irregular/non-writable file", filename));
1d0cd73f 763 return 0;
0532ba1d
KZ
764}
765
ce4dd666
KZ
766/**
767 * mnt_get_swaps_path:
768 *
769 * Returns: path to /proc/swaps or $LIBMOUNT_SWAPS.
770 */
771const char *mnt_get_swaps_path(void)
772{
773 const char *p = safe_getenv("LIBMOUNT_SWAPS");
774 return p ? : _PATH_PROC_SWAPS;
775}
776
3a5b1b1d
KZ
777/**
778 * mnt_get_fstab_path:
779 *
780 * Returns: path to /etc/fstab or $LIBMOUNT_FSTAB.
781 */
782const char *mnt_get_fstab_path(void)
783{
035507c8 784 const char *p = safe_getenv("LIBMOUNT_FSTAB");
3a5b1b1d
KZ
785 return p ? : _PATH_MNTTAB;
786}
787
788/**
789 * mnt_get_mtab_path:
790 *
b37dd175 791 * This function returns *default* location of the mtab file. The result does
0f32f1e2 792 * not have to be writable. See also mnt_has_regular_mtab().
3a5b1b1d
KZ
793 *
794 * Returns: path to /etc/mtab or $LIBMOUNT_MTAB.
795 */
796const char *mnt_get_mtab_path(void)
797{
035507c8 798 const char *p = safe_getenv("LIBMOUNT_MTAB");
3a5b1b1d
KZ
799 return p ? : _PATH_MOUNTED;
800}
801
77417bc0
KZ
802/*
803 * Don't export this to libmount API -- utab is private library stuff.
be1a5180 804 *
a362ae60 805 * Returns: path to /run/mount/utab (or /dev/.mount/utab) or $LIBMOUNT_UTAB.
b37dd175
KZ
806 */
807const char *mnt_get_utab_path(void)
808{
a362ae60 809 struct stat st;
035507c8 810 const char *p = safe_getenv("LIBMOUNT_UTAB");
a362ae60
KZ
811
812 if (p)
813 return p;
814
815 if (stat(MNT_RUNTIME_TOPDIR, &st) == 0)
816 return MNT_PATH_UTAB;
817
818 return MNT_PATH_UTAB_OLD;
b37dd175
KZ
819}
820
d1be0c34 821
b37dd175
KZ
822/* returns file descriptor or -errno, @name returns uniques filename
823 */
4b6cf485 824int mnt_open_uniq_filename(const char *filename, char **name)
b37dd175
KZ
825{
826 int rc, fd;
827 char *n;
6fc81224 828 mode_t oldmode;
b37dd175 829
ba2bdf41
KZ
830 if (!filename)
831 return -EINVAL;
b37dd175
KZ
832 if (name)
833 *name = NULL;
834
835 rc = asprintf(&n, "%s.XXXXXX", filename);
836 if (rc <= 0)
837 return -errno;
838
6fc81224
KZ
839 /* This is for very old glibc and for compatibility with Posix where is
840 * nothing about mkstemp() mode. All sane glibc use secure mode (0600).
841 */
51479069
KZ
842 oldmode = umask(S_IRGRP|S_IWGRP|S_IXGRP|
843 S_IROTH|S_IWOTH|S_IXOTH);
1eb8539d 844 fd = mkostemp(n, O_RDWR|O_CREAT|O_EXCL|O_CLOEXEC);
51479069 845 umask(oldmode);
6fc81224 846
b37dd175
KZ
847 if (fd >= 0 && name)
848 *name = n;
849 else
850 free(n);
851
852 return fd < 0 ? -errno : fd;
853}
0bb44be3 854
cd3d6c5b
DR
855/**
856 * mnt_get_mountpoint:
857 * @path: pathname
858 *
859 * This function finds the mountpoint that a given path resides in. @path
860 * should be canonicalized. The returned pointer should be freed by the caller.
861 *
90cd46cb 862 * Returns: allocated string with target of the mounted device or NULL on error
cd3d6c5b 863 */
0bb44be3
KZ
864char *mnt_get_mountpoint(const char *path)
865{
4569bbea 866 char *mnt;
0bb44be3
KZ
867 struct stat st;
868 dev_t dir, base;
869
4569bbea
KZ
870 assert(path);
871
872 mnt = strdup(path);
0bb44be3
KZ
873 if (!mnt)
874 return NULL;
875 if (*mnt == '/' && *(mnt + 1) == '\0')
9758c88a 876 goto done;
0bb44be3
KZ
877
878 if (stat(mnt, &st))
879 goto err;
880 base = st.st_dev;
881
882 do {
883 char *p = stripoff_last_component(mnt);
884
885 if (!p)
886 break;
887 if (stat(*mnt ? mnt : "/", &st))
888 goto err;
889 dir = st.st_dev;
890 if (dir != base) {
9d670a2a
KZ
891 if (p > mnt)
892 *(p - 1) = '/';
9758c88a 893 goto done;
0bb44be3
KZ
894 }
895 base = dir;
896 } while (mnt && *(mnt + 1) != '\0');
897
898 memcpy(mnt, "/", 2);
9758c88a 899done:
f84fa6f7 900 DBG(UTILS, mnt_debug("%s mountpoint is %s", path, mnt));
9758c88a 901 return mnt;
0bb44be3
KZ
902err:
903 free(mnt);
904 return NULL;
905}
906
9758c88a 907char *mnt_get_fs_root(const char *path, const char *mnt)
0bb44be3 908{
f84fa6f7 909 char *m = (char *) mnt, *res;
0bb44be3
KZ
910 const char *p;
911 size_t sz;
912
9758c88a
KZ
913 if (!m)
914 m = mnt_get_mountpoint(path);
915 if (!m)
0bb44be3
KZ
916 return NULL;
917
9758c88a 918 sz = strlen(m);
0bb44be3
KZ
919 p = sz > 1 ? path + sz : path;
920
9758c88a
KZ
921 if (m != mnt)
922 free(m);
0bb44be3 923
f84fa6f7
KZ
924 res = *p ? strdup(p) : strdup("/");
925 DBG(UTILS, mnt_debug("%s fs-root is %s", path, res));
926 return res;
0bb44be3
KZ
927}
928
f308ec19
KZ
929/*
930 * Search for @name kernel command parametr.
931 *
932 * Returns newly allocated string with parameter argument if the @name is
933 * specified as "name=" or returns pointer to @name or returns NULL if not
934 * found.
935 *
936 * For example cmdline: "aaa bbb=BBB ccc"
937 *
938 * @name is "aaa" --returns--> "aaa" (pointer to @name)
939 * @name is "bbb=" --returns--> "BBB" (allocated)
940 * @name is "foo" --returns--> NULL
941 */
942char *mnt_get_kernel_cmdline_option(const char *name)
943{
944 FILE *f;
945 size_t len;
946 int val = 0;
947 char *p, *res = NULL;
948 char buf[BUFSIZ]; /* see kernel include/asm-generic/setup.h: COMMAND_LINE_SIZE */
949 const char *path = _PATH_PROC_CMDLINE;
950
ba2bdf41
KZ
951 if (!name)
952 return NULL;
f308ec19
KZ
953
954#ifdef TEST_PROGRAM
cd793967 955 path = getenv("LIBMOUNT_KERNEL_CMDLINE");
f308ec19
KZ
956 if (!path)
957 path = _PATH_PROC_CMDLINE;
958#endif
1eb8539d 959 f = fopen(path, "r" UL_CLOEXECSTR);
f308ec19
KZ
960 if (!f)
961 return NULL;
962
963 p = fgets(buf, sizeof(buf), f);
964 fclose(f);
965
966 if (!p || !*p || *p == '\n')
967 return NULL;
968
969 len = strlen(buf);
970 *(buf + len - 1) = '\0'; /* remove last '\n' */
971
972 len = strlen(name);
973 if (len && *(name + len - 1) == '=')
974 val = 1;
975
2c0ba1ce 976 for ( ; p && *p; p++) {
f308ec19
KZ
977 if (!(p = strstr(p, name)))
978 break; /* not found the option */
979 if (p != buf && !isblank(*(p - 1)))
980 continue; /* no space before the option */
981 if (!val && *(p + len) != '\0' && !isblank(*(p + len)))
982 continue; /* no space behind the option */
983 if (val) {
984 char *v = p + len;
985
986 while (*p && !isblank(*p)) /* jump to the end of the argument */
987 p++;
988 *p = '\0';
989 res = strdup(v);
990 break;
991 } else
992 res = (char *) name; /* option without '=' */
993 break;
994 }
995
996 return res;
997}
998
fd73f468
KZ
999int mkdir_p(const char *path, mode_t mode)
1000{
1001 char *p, *dir;
1002 int rc = 0;
1003
1004 if (!path || !*path)
1005 return -EINVAL;
1006
1007 dir = p = strdup(path);
1008 if (!dir)
1009 return -ENOMEM;
1010
1011 if (*p == '/')
1012 p++;
1013
1014 while (p && *p) {
1015 char *e = strchr(p, '/');
1016 if (e)
1017 *e = '\0';
1018 if (*p) {
1019 rc = mkdir(dir, mode);
1020 if (rc && errno != EEXIST)
1021 break;
1022 rc = 0;
1023 }
1024 if (!e)
1025 break;
1026 *e = '/';
1027 p = e + 1;
1028 }
1029
1030 DBG(UTILS, mnt_debug("%s mkdir %s", path, rc ? "FAILED" : "SUCCESS"));
1031
1032 free(dir);
1033 return rc;
1034}
1035
abc9c0f7 1036#ifdef TEST_PROGRAM
68164f6c 1037int test_match_fstype(struct libmnt_test *ts, int argc, char *argv[])
abc9c0f7
KZ
1038{
1039 char *type = argv[1];
1040 char *pattern = argv[2];
1041
1042 printf("%s\n", mnt_match_fstype(type, pattern) ? "MATCH" : "NOT-MATCH");
1043 return 0;
1044}
1045
68164f6c 1046int test_match_options(struct libmnt_test *ts, int argc, char *argv[])
abc9c0f7
KZ
1047{
1048 char *optstr = argv[1];
1049 char *pattern = argv[2];
1050
1051 printf("%s\n", mnt_match_options(optstr, pattern) ? "MATCH" : "NOT-MATCH");
1052 return 0;
1053}
1054
68164f6c 1055int test_startswith(struct libmnt_test *ts, int argc, char *argv[])
b49103ed
KZ
1056{
1057 char *optstr = argv[1];
1058 char *pattern = argv[2];
1059
1060 printf("%s\n", startswith(optstr, pattern) ? "YES" : "NOT");
1061 return 0;
1062}
1063
68164f6c 1064int test_endswith(struct libmnt_test *ts, int argc, char *argv[])
b49103ed
KZ
1065{
1066 char *optstr = argv[1];
1067 char *pattern = argv[2];
1068
1069 printf("%s\n", endswith(optstr, pattern) ? "YES" : "NOT");
1070 return 0;
1071}
1072
68164f6c 1073int test_mountpoint(struct libmnt_test *ts, int argc, char *argv[])
0bb44be3
KZ
1074{
1075 char *path = canonicalize_path(argv[1]),
1076 *mnt = path ? mnt_get_mountpoint(path) : NULL;
1077
1078 printf("%s: %s\n", argv[1], mnt ? : "unknown");
1079 free(mnt);
1080 free(path);
1081 return 0;
1082}
1083
68164f6c 1084int test_fsroot(struct libmnt_test *ts, int argc, char *argv[])
0bb44be3
KZ
1085{
1086 char *path = canonicalize_path(argv[1]),
d672fb26 1087 *mnt = path ? mnt_get_fs_root(path, NULL) : NULL;
0bb44be3
KZ
1088
1089 printf("%s: %s\n", argv[1], mnt ? : "unknown");
1090 free(mnt);
1091 free(path);
1092 return 0;
1093}
abc9c0f7 1094
68164f6c 1095int test_filesystems(struct libmnt_test *ts, int argc, char *argv[])
97e23b5e
KZ
1096{
1097 char **filesystems = NULL;
1098 int rc;
1099
1100 rc = mnt_get_filesystems(&filesystems, argc ? argv[1] : NULL);
1101 if (!rc) {
1102 char **p;
1103 for (p = filesystems; *p; p++)
1104 printf("%s\n", *p);
1105 mnt_free_filesystems(filesystems);
1106 }
1107 return rc;
1108}
1109
66bb8267
KZ
1110int test_chdir(struct libmnt_test *ts, int argc, char *argv[])
1111{
1112 int rc;
1113 char *path = canonicalize_path(argv[1]),
1114 *last = NULL;
1115
1116 if (!path)
1117 return -errno;
1118
1119 rc = mnt_chdir_to_parent(path, &last);
1120 if (!rc) {
1121 printf("path='%s', abs='%s', last='%s'\n",
1122 argv[1], path, last);
1123 }
1124 free(path);
1125 free(last);
1126 return rc;
1127}
1128
f308ec19
KZ
1129int test_kernel_cmdline(struct libmnt_test *ts, int argc, char *argv[])
1130{
1131 char *name = argv[1];
1132 char *res;
1133
1134 res = mnt_get_kernel_cmdline_option(name);
1135 if (!res)
1136 printf("'%s' not found\n", name);
1137 else if (res == name)
1138 printf("'%s' found\n", name);
1139 else {
1140 printf("'%s' found, argument: '%s'\n", name, res);
1141 free(res);
1142 }
1143
1144 return 0;
1145}
1146
fd73f468
KZ
1147int test_mkdir(struct libmnt_test *ts, int argc, char *argv[])
1148{
1149 int rc;
1150
1151 rc = mkdir_p(argv[1], S_IRWXU |
1152 S_IRGRP | S_IXGRP |
1153 S_IROTH | S_IXOTH);
1154 if (rc)
1155 printf("mkdir %s failed\n", argv[1]);
1156 return rc;
1157}
1158
66bb8267 1159
abc9c0f7
KZ
1160int main(int argc, char *argv[])
1161{
68164f6c 1162 struct libmnt_test tss[] = {
abc9c0f7
KZ
1163 { "--match-fstype", test_match_fstype, "<type> <pattern> FS types matching" },
1164 { "--match-options", test_match_options, "<options> <pattern> options matching" },
97e23b5e 1165 { "--filesystems", test_filesystems, "[<pattern>] list /{etc,proc}/filesystems" },
b49103ed
KZ
1166 { "--starts-with", test_startswith, "<string> <prefix>" },
1167 { "--ends-with", test_endswith, "<string> <prefix>" },
0bb44be3
KZ
1168 { "--mountpoint", test_mountpoint, "<path>" },
1169 { "--fs-root", test_fsroot, "<path>" },
66bb8267 1170 { "--cd-parent", test_chdir, "<path>" },
f308ec19 1171 { "--kernel-cmdline",test_kernel_cmdline, "<option> | <option>=" },
fd73f468
KZ
1172 { "--mkdir", test_mkdir, "<path>" },
1173
abc9c0f7
KZ
1174 { NULL }
1175 };
1176
1177 return mnt_run_test(tss, argc, argv);
1178}
1179
1180#endif /* TEST_PROGRAM */