]> git.ipfire.org Git - thirdparty/util-linux.git/blame - libmount/src/utils.c
findmnt: fallback to mountinfo for polling
[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
fd1eb7a7
KZ
57int mnt_parse_offset(const char *str, size_t len, uintmax_t *res)
58{
59 char *p;
60 int rc = 0;
61
0208ae2e 62 if (!str || !*str)
fd1eb7a7
KZ
63 return -EINVAL;
64
65 p = strndup(str, len);
66 if (!p)
67 return -errno;
68
69 if (strtosize(p, res))
70 rc = -EINVAL;
71 free(p);
72 return rc;
73}
74
1d0cd73f
KZ
75/* returns basename and keeps dirname in the @path, if @path is "/" (root)
76 * then returns empty string */
77static char *stripoff_last_component(char *path)
78{
79 char *p = path ? strrchr(path, '/') : NULL;
80
81 if (!p)
82 return NULL;
83 *p = '\0';
61073773 84 return p + 1;
1d0cd73f
KZ
85}
86
61073773
KZ
87/*
88 * Note that the @target has to be absolute path (so at least "/"). The
89 * @filename returns allocated buffer with last path component, for example:
90 *
91 * mnt_chdir_to_parent("/mnt/test", &buf) ==> chdir("/mnt"), buf="test"
66bb8267
KZ
92 */
93int mnt_chdir_to_parent(const char *target, char **filename)
94{
61073773 95 char *buf, *parent, *last = NULL;
66bb8267
KZ
96 char cwd[PATH_MAX];
97 int rc = -EINVAL;
98
99 if (!target || *target != '/')
100 return -EINVAL;
101
61073773
KZ
102 DBG(UTILS, mnt_debug("moving to %s parent", target));
103
104 buf = strdup(target);
105 if (!buf)
66bb8267
KZ
106 return -ENOMEM;
107
61073773
KZ
108 if (*(buf + 1) != '\0') {
109 last = stripoff_last_component(buf);
66bb8267
KZ
110 if (!last)
111 goto err;
112 }
66bb8267 113
61073773
KZ
114 parent = buf && *buf ? buf : "/";
115
116 if (chdir(parent) == -1) {
117 DBG(UTILS, mnt_debug("failed to chdir to %s: %m", parent));
66bb8267
KZ
118 rc = -errno;
119 goto err;
120 }
121 if (!getcwd(cwd, sizeof(cwd))) {
122 DBG(UTILS, mnt_debug("failed to obtain current directory: %m"));
123 rc = -errno;
124 goto err;
125 }
61073773
KZ
126 if (strcmp(cwd, parent) != 0) {
127 DBG(UTILS, mnt_debug(
128 "unexpected chdir (expected=%s, cwd=%s)", parent, cwd));
66bb8267
KZ
129 goto err;
130 }
131
61073773
KZ
132 DBG(CXT, mnt_debug(
133 "current directory moved to %s [last_component='%s']",
134 parent, last));
66bb8267 135
61073773 136 *filename = buf;
66bb8267
KZ
137
138 if (!last || !*last)
139 memcpy(*filename, ".", 2);
140 else
141 memcpy(*filename, last, strlen(last) + 1);
142 return 0;
143err:
61073773 144 free(buf);
66bb8267
KZ
145 return rc;
146}
147
f9906424
KZ
148/*
149 * Check if @path is on read-only filesystem independently on file permissions.
150 */
151int mnt_is_readonly(const char *path)
152{
153 if (access(path, W_OK) == 0)
154 return 0;
155 if (errno == EROFS)
156 return 1;
157 if (errno != EACCES)
158 return 0;
159
160#ifdef HAVE_FUTIMENS
161 /*
162 * access(2) returns EACCES on read-only FS:
163 *
164 * - for set-uid application if one component of the path is not
165 * accessible for the current rUID. (Note that euidaccess(2) does not
166 * check for EROFS at all).
167 *
168 * - for read-write filesystem with read-only VFS node (aka -o remount,ro,bind)
169 */
170 {
171 struct timespec times[2];
172
173 times[0].tv_nsec = UTIME_NOW; /* atime */
174 times[1].tv_nsec = UTIME_OMIT; /* mtime */
175
176 if (utimensat(AT_FDCWD, path, times, 0) == -1)
177 return errno == EROFS;
178 }
179#endif
180 return 0;
181}
182
3c5e4ef8
KZ
183/**
184 * mnt_mangle:
185 * @str: string
186 *
187 * Encode @str to be compatible with fstab/mtab
188 *
189 * Returns: new allocated string or NULL in case of error.
190 */
191char *mnt_mangle(const char *str)
192{
193 return mangle(str);
194}
195
196/**
197 * mnt_unmangle:
198 * @str: string
199 *
200 * Decode @str from fstab/mtab
201 *
202 * Returns: new allocated string or NULL in case of error.
203 */
204char *mnt_unmangle(const char *str)
205{
dd369652 206 return unmangle(str, NULL);
3c5e4ef8
KZ
207}
208
69b7e41e
KZ
209/**
210 * mnt_fstype_is_pseudofs:
211 * @type: filesystem name
212 *
213 * Returns: 1 for filesystems like proc, sysfs, ... or 0.
214 */
215int mnt_fstype_is_pseudofs(const char *type)
216{
217 if (!type)
218 return 0;
219 if (strcmp(type, "none") == 0 ||
220 strcmp(type, "proc") == 0 ||
221 strcmp(type, "tmpfs") == 0 ||
222 strcmp(type, "sysfs") == 0 ||
854fa6d9 223 strcmp(type, "autofs") == 0 ||
69b7e41e 224 strcmp(type, "devpts") == 0||
f5017242 225 strcmp(type, "cgroup") == 0 ||
854fa6d9 226 strcmp(type, "devtmpfs") == 0 ||
69b7e41e
KZ
227 strcmp(type, "devfs") == 0 ||
228 strcmp(type, "dlmfs") == 0 ||
229 strcmp(type, "cpuset") == 0 ||
1e1df43c 230 strcmp(type, "configfs") == 0 ||
1bbe1bc7 231 strcmp(type, "securityfs") == 0 ||
854fa6d9 232 strcmp(type, "hugetlbfs") == 0 ||
c5a6f7a9
KZ
233 strcmp(type, "rpc_pipefs") == 0 ||
234 strcmp(type, "fusectl") == 0 ||
854fa6d9 235 strcmp(type, "mqueue") == 0 ||
c5a6f7a9
KZ
236 strcmp(type, "binfmt_misc") == 0 ||
237 strcmp(type, "fuse.gvfs-fuse-daemon") == 0 ||
1bbe1bc7 238 strcmp(type, "debugfs") == 0 ||
69b7e41e
KZ
239 strcmp(type, "spufs") == 0)
240 return 1;
241 return 0;
242}
243
244/**
245 * mnt_fstype_is_netfs:
246 * @type: filesystem name
247 *
248 * Returns: 1 for filesystems like cifs, nfs, ... or 0.
249 */
250int mnt_fstype_is_netfs(const char *type)
251{
252 if (!type)
253 return 0;
c59cf20c 254 if (strcmp(type, "cifs") == 0 ||
69b7e41e 255 strcmp(type, "smbfs") == 0 ||
c59cf20c
KZ
256 strncmp(type,"nfs", 3) == 0 ||
257 strcmp(type, "afs") == 0 ||
258 strcmp(type, "ncpfs") == 0 ||
259 strncmp(type,"9p", 2) == 0)
69b7e41e
KZ
260 return 1;
261 return 0;
262}
263
abc9c0f7
KZ
264/**
265 * mnt_match_fstype:
266 * @type: filesystem type
6ad929bb 267 * @pattern: filesystem name or comma delimited list of names
abc9c0f7
KZ
268 *
269 * The @pattern list of filesystem can be prefixed with a global
270 * "no" prefix to invert matching of the whole list. The "no" could
6ad929bb 271 * also be used for individual items in the @pattern list. So,
3d735589 272 * "nofoo,bar" has the same meaning as "nofoo,nobar".
abc9c0f7 273 *
3d735589
KZ
274 * "bar" : "nofoo,bar" -> False (global "no" prefix)
275 *
276 * "bar" : "foo,bar" -> True
abc9c0f7 277 *
abc9c0f7
KZ
278 * "bar" : "foo,nobar" -> False
279 *
280 * Returns: 1 if type is matching, else 0. This function also returns
281 * 0 if @pattern is NULL and @type is non-NULL.
282 */
283int mnt_match_fstype(const char *type, const char *pattern)
284{
12089155 285 return match_fstype(type, pattern);
abc9c0f7
KZ
286}
287
288
289/* Returns 1 if needle found or noneedle not found in haystack
290 * Otherwise returns 0
291 */
292static int check_option(const char *haystack, size_t len,
293 const char *needle, size_t needle_len)
294{
295 const char *p;
296 int no = 0;
297
ede8a60e
KZ
298 if (needle_len >= 1 && *needle == '+') {
299 needle++;
300 needle_len--;
301 } else if (needle_len >= 2 && !strncmp(needle, "no", 2)) {
abc9c0f7
KZ
302 no = 1;
303 needle += 2;
304 needle_len -= 2;
305 }
306
307 for (p = haystack; p && p < haystack + len; p++) {
308 char *sep = strchr(p, ',');
7fc6d2b8
KZ
309 size_t plen = sep ? (size_t) (sep - p) :
310 len - (p - haystack);
abc9c0f7
KZ
311
312 if (plen == needle_len) {
313 if (!strncmp(p, needle, plen))
314 return !no; /* foo or nofoo was found */
315 }
316 p += plen;
317 }
318
319 return no; /* foo or nofoo was not found */
320}
321
322/**
323 * mnt_match_options:
324 * @optstr: options string
6ad929bb 325 * @pattern: comma delimited list of options
abc9c0f7
KZ
326 *
327 * The "no" could used for individual items in the @options list. The "no"
6ad929bb 328 * prefix does not have a global meaning.
abc9c0f7
KZ
329 *
330 * Unlike fs type matching, nonetdev,user and nonetdev,nouser have
331 * DIFFERENT meanings; each option is matched explicitly as specified.
332 *
ede8a60e
KZ
333 * The "no" prefix interpretation could be disable by "+" prefix, for example
334 * "+noauto" matches if @optstr literally contains "noauto" string.
335 *
3d735589
KZ
336 * "xxx,yyy,zzz" : "nozzz" -> False
337 *
338 * "xxx,yyy,zzz" : "xxx,noeee" -> True
abc9c0f7 339 *
ede8a60e
KZ
340 * "bar,zzz" : "nofoo" -> True
341 *
342 * "nofoo,bar" : "+nofoo" -> True
343 *
344 * "bar,zzz" : "+nofoo" -> False
345 *
346 *
abc9c0f7
KZ
347 * Returns: 1 if pattern is matching, else 0. This function also returns 0
348 * if @pattern is NULL and @optstr is non-NULL.
349 */
350int mnt_match_options(const char *optstr, const char *pattern)
351{
352 const char *p;
353 size_t len, optstr_len = 0;
354
355 if (!pattern && !optstr)
356 return 1;
357 if (!pattern)
358 return 0;
359
360 len = strlen(pattern);
361 if (optstr)
362 optstr_len = strlen(optstr);
363
364 for (p = pattern; p < pattern + len; p++) {
365 char *sep = strchr(p, ',');
7fc6d2b8
KZ
366 size_t plen = sep ? (size_t) (sep - p) :
367 len - (p - pattern);
abc9c0f7
KZ
368
369 if (!plen)
370 continue; /* if two ',' appear in a row */
371
372 if (!check_option(optstr, optstr_len, p, plen))
373 return 0; /* any match failure means failure */
374
375 p += plen;
376 }
377
378 /* no match failures in list means success */
379 return 1;
380}
381
97e23b5e
KZ
382void mnt_free_filesystems(char **filesystems)
383{
384 char **p;
385
386 if (!filesystems)
387 return;
388 for (p = filesystems; *p; p++)
389 free(*p);
390 free(filesystems);
391}
392
393static int add_filesystem(char ***filesystems, char *name)
394{
395 int n = 0;
396
397 assert(filesystems);
398 assert(name);
399
400 if (*filesystems) {
401 char **p;
402 for (n = 0, p = *filesystems; *p; p++, n++) {
403 if (strcmp(*p, name) == 0)
404 return 0;
405 }
406 }
407
408 #define MYCHUNK 16
409
410 if (n == 0 || !((n + 1) % MYCHUNK)) {
411 size_t items = ((n + 1 + MYCHUNK) / MYCHUNK) * MYCHUNK;
412 char **x = realloc(*filesystems, items * sizeof(char *));
413
414 if (!x)
415 goto err;
416 *filesystems = x;
417 }
418 name = strdup(name);
419 if (!name)
420 goto err;
421 (*filesystems)[n] = name;
422 (*filesystems)[n + 1] = NULL;
423 return 0;
424err:
425 mnt_free_filesystems(*filesystems);
426 return -ENOMEM;
427}
428
429static int get_filesystems(const char *filename, char ***filesystems, const char *pattern)
430{
4f69189f 431 int rc = 0;
97e23b5e
KZ
432 FILE *f;
433 char line[128];
434
435 f = fopen(filename, "r");
436 if (!f)
437 return 0;
438
439 while (fgets(line, sizeof(line), f)) {
440 char name[sizeof(line)];
97e23b5e
KZ
441
442 if (*line == '#' || strncmp(line, "nodev", 5) == 0)
443 continue;
444 if (sscanf(line, " %128[^\n ]\n", name) != 1)
445 continue;
446 if (pattern && !mnt_match_fstype(name, pattern))
447 continue;
448 rc = add_filesystem(filesystems, name);
449 if (rc)
4f69189f 450 break;
97e23b5e 451 }
4f69189f
KZ
452
453 fclose(f);
454 return rc;
97e23b5e
KZ
455}
456
457int mnt_get_filesystems(char ***filesystems, const char *pattern)
458{
459 int rc;
460
461 if (!filesystems)
462 return -EINVAL;
463 *filesystems = NULL;
464
465 rc = get_filesystems(_PATH_FILESYSTEMS, filesystems, pattern);
466 if (rc)
467 return rc;
468 return get_filesystems(_PATH_PROC_FILESYSTEMS, filesystems, pattern);
469}
470
2af0a839
KZ
471static size_t get_pw_record_size(void)
472{
473#ifdef _SC_GETPW_R_SIZE_MAX
474 long sz = sysconf(_SC_GETPW_R_SIZE_MAX);
475 if (sz > 0)
476 return sz;
477#endif
478 return 16384;
479}
480
69b7e41e
KZ
481/*
482 * Returns allocated string with username or NULL.
483 */
484char *mnt_get_username(const uid_t uid)
485{
486 struct passwd pwd;
487 struct passwd *res;
2af0a839 488 size_t sz = get_pw_record_size();
69b7e41e
KZ
489 char *buf, *username = NULL;
490
69b7e41e
KZ
491 buf = malloc(sz);
492 if (!buf)
493 return NULL;
494
495 if (!getpwuid_r(uid, &pwd, buf, sz, &res) && res)
496 username = strdup(pwd.pw_name);
497
498 free(buf);
499 return username;
500}
abc9c0f7 501
a1e8af75
KZ
502int mnt_get_uid(const char *username, uid_t *uid)
503{
188dc15a 504 int rc = -1;
a1e8af75
KZ
505 struct passwd pwd;
506 struct passwd *pw;
2af0a839 507 size_t sz = get_pw_record_size();
a1e8af75
KZ
508 char *buf;
509
188dc15a
KZ
510 if (!username || !uid)
511 return -EINVAL;
b47b7b3a 512
a1e8af75
KZ
513 buf = malloc(sz);
514 if (!buf)
515 return -ENOMEM;
516
188dc15a 517 if (!getpwnam_r(username, &pwd, buf, sz, &pw) && pw) {
a1e8af75 518 *uid= pw->pw_uid;
188dc15a
KZ
519 rc = 0;
520 } else {
521 DBG(UTILS, mnt_debug(
522 "cannot convert '%s' username to UID", username));
523 }
a1e8af75
KZ
524
525 free(buf);
188dc15a 526 return rc;
a1e8af75
KZ
527}
528
529int mnt_get_gid(const char *groupname, gid_t *gid)
530{
188dc15a 531 int rc = -1;
a1e8af75
KZ
532 struct group grp;
533 struct group *gr;
2af0a839 534 size_t sz = get_pw_record_size();
a1e8af75
KZ
535 char *buf;
536
188dc15a
KZ
537 if (!groupname || !gid)
538 return -EINVAL;
b47b7b3a 539
a1e8af75
KZ
540 buf = malloc(sz);
541 if (!buf)
542 return -ENOMEM;
543
188dc15a 544 if (!getgrnam_r(groupname, &grp, buf, sz, &gr) && gr) {
a1e8af75 545 *gid= gr->gr_gid;
188dc15a
KZ
546 rc = 0;
547 } else {
548 DBG(UTILS, mnt_debug(
549 "cannot convert '%s' groupname to GID", groupname));
550 }
a1e8af75
KZ
551
552 free(buf);
188dc15a
KZ
553 return rc;
554}
555
556int mnt_in_group(gid_t gid)
557{
558 int rc = 0, n, i;
559 gid_t *grps = NULL;
560
561 if (getgid() == gid)
562 return 1;
563
564 n = getgroups(0, NULL);
565 if (n <= 0)
566 goto done;
567
568 grps = malloc(n * sizeof(*grps));
569 if (!grps)
570 goto done;
571
572 if (getgroups(n, grps) == n) {
573 for (i = 0; i < n; i++) {
574 if (grps[i] == gid) {
575 rc = 1;
576 break;
577 }
578 }
579 }
580done:
581 free(grps);
582 return rc;
a1e8af75
KZ
583}
584
1d0cd73f
KZ
585static int try_write(const char *filename)
586{
587 int fd;
588
589 if (!filename)
590 return -EINVAL;
591
b0bb8fb6
KZ
592 fd = open(filename, O_RDWR|O_CREAT, S_IWUSR| \
593 S_IRUSR|S_IRGRP|S_IROTH);
1d0cd73f
KZ
594 if (fd >= 0) {
595 close(fd);
596 return 0;
597 }
598 return -errno;
599}
600
601/**
602 * mnt_has_regular_mtab:
603 * @mtab: returns path to mtab
604 * @writable: returns 1 if the file is writable
605 *
606 * If the file does not exist and @writable argument is not NULL then it will
607 * try to create the file
608 *
6ad929bb 609 * Returns: 1 if /etc/mtab is a regular file, and 0 in case of error (check
1d0cd73f 610 * errno for more details).
0532ba1d 611 */
1d0cd73f 612int mnt_has_regular_mtab(const char **mtab, int *writable)
0532ba1d
KZ
613{
614 struct stat st;
70bf97ae 615 int rc;
1d0cd73f 616 const char *filename = mtab && *mtab ? *mtab : mnt_get_mtab_path();
0532ba1d 617
7c118af7
KZ
618 if (writable)
619 *writable = 0;
70bf97ae 620 if (mtab && !*mtab)
1d0cd73f
KZ
621 *mtab = filename;
622
623 DBG(UTILS, mnt_debug("mtab: %s", filename));
70bf97ae 624
1d0cd73f
KZ
625 rc = lstat(filename, &st);
626
627 if (rc == 0) {
628 /* file exist */
629 if (S_ISREG(st.st_mode)) {
630 if (writable)
631 *writable = !try_write(filename);
632 return 1;
633 }
7c118af7 634 goto done;
1d0cd73f
KZ
635 }
636
637 /* try to create the file */
70bf97ae 638 if (writable) {
1d0cd73f 639 *writable = !try_write(filename);
7c118af7
KZ
640 if (*writable)
641 return 1;
1d0cd73f
KZ
642 }
643
7c118af7
KZ
644done:
645 DBG(UTILS, mnt_debug("%s: irregular/non-writable", filename));
1d0cd73f
KZ
646 return 0;
647}
7c118af7 648
77417bc0
KZ
649/*
650 * Don't export this to libmount API -- utab is private library stuff.
1d0cd73f
KZ
651 *
652 * If the file does not exist and @writable argument is not NULL then it will
a362ae60 653 * try to create the directory (e.g. /run/mount) and the file.
1d0cd73f 654 *
a362ae60 655 * Returns: 1 if utab is a regular file, and 0 in case of
6f5788c5 656 * error (check errno for more details).
1d0cd73f 657 */
1d0cd73f
KZ
658int mnt_has_regular_utab(const char **utab, int *writable)
659{
660 struct stat st;
661 int rc;
662 const char *filename = utab && *utab ? *utab : mnt_get_utab_path();
663
7c118af7
KZ
664 if (writable)
665 *writable = 0;
1d0cd73f
KZ
666 if (utab && !*utab)
667 *utab = filename;
668
669 DBG(UTILS, mnt_debug("utab: %s", filename));
670
671 rc = lstat(filename, &st);
672
673 if (rc == 0) {
674 /* file exist */
675 if (S_ISREG(st.st_mode)) {
676 if (writable)
7c118af7 677 *writable = !try_write(filename);
1d0cd73f 678 return 1;
70bf97ae 679 }
7c118af7 680 goto done; /* it's not regular file */
70bf97ae 681 }
1d0cd73f
KZ
682
683 if (writable) {
684 char *dirname = strdup(filename);
685
686 if (!dirname)
7c118af7 687 goto done;
1d0cd73f
KZ
688
689 stripoff_last_component(dirname); /* remove filename */
690
b0bb8fb6
KZ
691 rc = mkdir(dirname, S_IWUSR|
692 S_IRUSR|S_IRGRP|S_IROTH|
693 S_IXUSR|S_IXGRP|S_IXOTH);
1d0cd73f
KZ
694 free(dirname);
695 if (rc && errno != EEXIST)
7c118af7 696 goto done; /* probably EACCES */
1d0cd73f
KZ
697
698 *writable = !try_write(filename);
7c118af7
KZ
699 if (*writable)
700 return 1;
1d0cd73f 701 }
7c118af7
KZ
702done:
703 DBG(UTILS, mnt_debug("%s: irregular/non-writable file", filename));
1d0cd73f 704 return 0;
0532ba1d
KZ
705}
706
ce4dd666
KZ
707/**
708 * mnt_get_swaps_path:
709 *
710 * Returns: path to /proc/swaps or $LIBMOUNT_SWAPS.
711 */
712const char *mnt_get_swaps_path(void)
713{
714 const char *p = safe_getenv("LIBMOUNT_SWAPS");
715 return p ? : _PATH_PROC_SWAPS;
716}
717
3a5b1b1d
KZ
718/**
719 * mnt_get_fstab_path:
720 *
721 * Returns: path to /etc/fstab or $LIBMOUNT_FSTAB.
722 */
723const char *mnt_get_fstab_path(void)
724{
035507c8 725 const char *p = safe_getenv("LIBMOUNT_FSTAB");
3a5b1b1d
KZ
726 return p ? : _PATH_MNTTAB;
727}
728
729/**
730 * mnt_get_mtab_path:
731 *
b37dd175 732 * This function returns *default* location of the mtab file. The result does
0f32f1e2 733 * not have to be writable. See also mnt_has_regular_mtab().
3a5b1b1d
KZ
734 *
735 * Returns: path to /etc/mtab or $LIBMOUNT_MTAB.
736 */
737const char *mnt_get_mtab_path(void)
738{
035507c8 739 const char *p = safe_getenv("LIBMOUNT_MTAB");
3a5b1b1d
KZ
740 return p ? : _PATH_MOUNTED;
741}
742
77417bc0
KZ
743/*
744 * Don't export this to libmount API -- utab is private library stuff.
be1a5180 745 *
a362ae60 746 * Returns: path to /run/mount/utab (or /dev/.mount/utab) or $LIBMOUNT_UTAB.
b37dd175
KZ
747 */
748const char *mnt_get_utab_path(void)
749{
a362ae60 750 struct stat st;
035507c8 751 const char *p = safe_getenv("LIBMOUNT_UTAB");
a362ae60
KZ
752
753 if (p)
754 return p;
755
756 if (stat(MNT_RUNTIME_TOPDIR, &st) == 0)
757 return MNT_PATH_UTAB;
758
759 return MNT_PATH_UTAB_OLD;
b37dd175
KZ
760}
761
d1be0c34 762
b37dd175
KZ
763/* returns file descriptor or -errno, @name returns uniques filename
764 */
4b6cf485 765int mnt_open_uniq_filename(const char *filename, char **name)
b37dd175
KZ
766{
767 int rc, fd;
768 char *n;
769
770 assert(filename);
771
772 if (name)
773 *name = NULL;
774
775 rc = asprintf(&n, "%s.XXXXXX", filename);
776 if (rc <= 0)
777 return -errno;
778
4b6cf485 779 fd = mkstemp(n);
b37dd175
KZ
780 if (fd >= 0 && name)
781 *name = n;
782 else
783 free(n);
784
785 return fd < 0 ? -errno : fd;
786}
0bb44be3 787
cd3d6c5b
DR
788/**
789 * mnt_get_mountpoint:
790 * @path: pathname
791 *
792 * This function finds the mountpoint that a given path resides in. @path
793 * should be canonicalized. The returned pointer should be freed by the caller.
794 *
795 * Returns: target of mounted device or NULL on error
796 */
0bb44be3
KZ
797char *mnt_get_mountpoint(const char *path)
798{
799 char *mnt = strdup(path);
800 struct stat st;
801 dev_t dir, base;
802
803 if (!mnt)
804 return NULL;
805 if (*mnt == '/' && *(mnt + 1) == '\0')
9758c88a 806 goto done;
0bb44be3
KZ
807
808 if (stat(mnt, &st))
809 goto err;
810 base = st.st_dev;
811
812 do {
813 char *p = stripoff_last_component(mnt);
814
815 if (!p)
816 break;
817 if (stat(*mnt ? mnt : "/", &st))
818 goto err;
819 dir = st.st_dev;
820 if (dir != base) {
821 *(p - 1) = '/';
9758c88a 822 goto done;
0bb44be3
KZ
823 }
824 base = dir;
825 } while (mnt && *(mnt + 1) != '\0');
826
827 memcpy(mnt, "/", 2);
9758c88a 828done:
f84fa6f7 829 DBG(UTILS, mnt_debug("%s mountpoint is %s", path, mnt));
9758c88a 830 return mnt;
0bb44be3
KZ
831err:
832 free(mnt);
833 return NULL;
834}
835
9758c88a 836char *mnt_get_fs_root(const char *path, const char *mnt)
0bb44be3 837{
f84fa6f7 838 char *m = (char *) mnt, *res;
0bb44be3
KZ
839 const char *p;
840 size_t sz;
841
9758c88a
KZ
842 if (!m)
843 m = mnt_get_mountpoint(path);
844 if (!m)
0bb44be3
KZ
845 return NULL;
846
9758c88a 847 sz = strlen(m);
0bb44be3
KZ
848 p = sz > 1 ? path + sz : path;
849
9758c88a
KZ
850 if (m != mnt)
851 free(m);
0bb44be3 852
f84fa6f7
KZ
853 res = *p ? strdup(p) : strdup("/");
854 DBG(UTILS, mnt_debug("%s fs-root is %s", path, res));
855 return res;
0bb44be3
KZ
856}
857
abc9c0f7 858#ifdef TEST_PROGRAM
68164f6c 859int test_match_fstype(struct libmnt_test *ts, int argc, char *argv[])
abc9c0f7
KZ
860{
861 char *type = argv[1];
862 char *pattern = argv[2];
863
864 printf("%s\n", mnt_match_fstype(type, pattern) ? "MATCH" : "NOT-MATCH");
865 return 0;
866}
867
68164f6c 868int test_match_options(struct libmnt_test *ts, int argc, char *argv[])
abc9c0f7
KZ
869{
870 char *optstr = argv[1];
871 char *pattern = argv[2];
872
873 printf("%s\n", mnt_match_options(optstr, pattern) ? "MATCH" : "NOT-MATCH");
874 return 0;
875}
876
68164f6c 877int test_startswith(struct libmnt_test *ts, int argc, char *argv[])
b49103ed
KZ
878{
879 char *optstr = argv[1];
880 char *pattern = argv[2];
881
882 printf("%s\n", startswith(optstr, pattern) ? "YES" : "NOT");
883 return 0;
884}
885
68164f6c 886int test_endswith(struct libmnt_test *ts, int argc, char *argv[])
b49103ed
KZ
887{
888 char *optstr = argv[1];
889 char *pattern = argv[2];
890
891 printf("%s\n", endswith(optstr, pattern) ? "YES" : "NOT");
892 return 0;
893}
894
68164f6c 895int test_mountpoint(struct libmnt_test *ts, int argc, char *argv[])
0bb44be3
KZ
896{
897 char *path = canonicalize_path(argv[1]),
898 *mnt = path ? mnt_get_mountpoint(path) : NULL;
899
900 printf("%s: %s\n", argv[1], mnt ? : "unknown");
901 free(mnt);
902 free(path);
903 return 0;
904}
905
68164f6c 906int test_fsroot(struct libmnt_test *ts, int argc, char *argv[])
0bb44be3
KZ
907{
908 char *path = canonicalize_path(argv[1]),
d672fb26 909 *mnt = path ? mnt_get_fs_root(path, NULL) : NULL;
0bb44be3
KZ
910
911 printf("%s: %s\n", argv[1], mnt ? : "unknown");
912 free(mnt);
913 free(path);
914 return 0;
915}
abc9c0f7 916
68164f6c 917int test_filesystems(struct libmnt_test *ts, int argc, char *argv[])
97e23b5e
KZ
918{
919 char **filesystems = NULL;
920 int rc;
921
922 rc = mnt_get_filesystems(&filesystems, argc ? argv[1] : NULL);
923 if (!rc) {
924 char **p;
925 for (p = filesystems; *p; p++)
926 printf("%s\n", *p);
927 mnt_free_filesystems(filesystems);
928 }
929 return rc;
930}
931
66bb8267
KZ
932int test_chdir(struct libmnt_test *ts, int argc, char *argv[])
933{
934 int rc;
935 char *path = canonicalize_path(argv[1]),
936 *last = NULL;
937
938 if (!path)
939 return -errno;
940
941 rc = mnt_chdir_to_parent(path, &last);
942 if (!rc) {
943 printf("path='%s', abs='%s', last='%s'\n",
944 argv[1], path, last);
945 }
946 free(path);
947 free(last);
948 return rc;
949}
950
951
abc9c0f7
KZ
952int main(int argc, char *argv[])
953{
68164f6c 954 struct libmnt_test tss[] = {
abc9c0f7
KZ
955 { "--match-fstype", test_match_fstype, "<type> <pattern> FS types matching" },
956 { "--match-options", test_match_options, "<options> <pattern> options matching" },
97e23b5e 957 { "--filesystems", test_filesystems, "[<pattern>] list /{etc,proc}/filesystems" },
b49103ed
KZ
958 { "--starts-with", test_startswith, "<string> <prefix>" },
959 { "--ends-with", test_endswith, "<string> <prefix>" },
0bb44be3
KZ
960 { "--mountpoint", test_mountpoint, "<path>" },
961 { "--fs-root", test_fsroot, "<path>" },
66bb8267 962 { "--cd-parent", test_chdir, "<path>" },
abc9c0f7
KZ
963 { NULL }
964 };
965
966 return mnt_run_test(tss, argc, argv);
967}
968
969#endif /* TEST_PROGRAM */