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