]> git.ipfire.org Git - thirdparty/util-linux.git/blame - shlibs/mount/src/utils.c
tests: check for mtab
[thirdparty/util-linux.git] / shlibs / mount / 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
KZ
13#include <unistd.h>
14#include <errno.h>
15#include <stdlib.h>
16#include <string.h>
17#ifdef HAVE_SYS_PRCTL_H
18#include <sys/prctl.h>
19#else
20#define PR_GET_DUMPABLE 3
21#endif
22#if (!defined(HAVE_PRCTL) && defined(linux))
23#include <sys/syscall.h>
24#endif
25#include <sys/stat.h>
26#include <ctype.h>
27#include <sys/types.h>
28#include <fcntl.h>
29#include <pwd.h>
a1e8af75 30#include <grp.h>
69b7e41e 31
b49103ed 32#include "strutils.h"
0532ba1d 33#include "pathnames.h"
69b7e41e 34#include "mountP.h"
3c5e4ef8 35#include "mangle.h"
0bb44be3 36#include "canonicalize.h"
69b7e41e
KZ
37
38char *mnt_getenv_safe(const char *arg)
39{
b0bb8fb6
KZ
40 return getenv(arg);
41
69b7e41e
KZ
42 if ((getuid() != geteuid()) || (getgid() != getegid()))
43 return NULL;
44#if HAVE_PRCTL
45 if (prctl(PR_GET_DUMPABLE, 0, 0, 0, 0) == 0)
46 return NULL;
47#else
48#if (defined(linux) && defined(SYS_prctl))
49 if (syscall(SYS_prctl, PR_GET_DUMPABLE, 0, 0, 0, 0) == 0)
50 return NULL;
51#endif
52#endif
53
54#ifdef HAVE___SECURE_GETENV
55 return __secure_getenv(arg);
56#else
57 return getenv(arg);
58#endif
59}
60
b49103ed
KZ
61int endswith(const char *s, const char *sx)
62{
63 ssize_t off;
64
65 assert(s);
66 assert(sx);
67
68 off = strlen(s);
69 if (!off)
70 return 0;
71 off -= strlen(sx);
72 if (off < 0)
73 return 0;
74
75 return !strcmp(s + off, sx);
76}
77
78int startswith(const char *s, const char *sx)
79{
80 size_t off;
81
82 assert(s);
83 assert(sx);
84
85 off = strlen(sx);
86 if (!off)
87 return 0;
88
89 return !strncmp(s, sx, off);
90}
91
1d0cd73f
KZ
92/* returns basename and keeps dirname in the @path, if @path is "/" (root)
93 * then returns empty string */
94static char *stripoff_last_component(char *path)
95{
96 char *p = path ? strrchr(path, '/') : NULL;
97
98 if (!p)
99 return NULL;
100 *p = '\0';
101 return ++p;
102}
103
3c5e4ef8
KZ
104/**
105 * mnt_mangle:
106 * @str: string
107 *
108 * Encode @str to be compatible with fstab/mtab
109 *
110 * Returns: new allocated string or NULL in case of error.
111 */
112char *mnt_mangle(const char *str)
113{
114 return mangle(str);
115}
116
117/**
118 * mnt_unmangle:
119 * @str: string
120 *
121 * Decode @str from fstab/mtab
122 *
123 * Returns: new allocated string or NULL in case of error.
124 */
125char *mnt_unmangle(const char *str)
126{
dd369652 127 return unmangle(str, NULL);
3c5e4ef8
KZ
128}
129
69b7e41e
KZ
130/**
131 * mnt_fstype_is_pseudofs:
132 * @type: filesystem name
133 *
134 * Returns: 1 for filesystems like proc, sysfs, ... or 0.
135 */
136int mnt_fstype_is_pseudofs(const char *type)
137{
138 if (!type)
139 return 0;
140 if (strcmp(type, "none") == 0 ||
141 strcmp(type, "proc") == 0 ||
142 strcmp(type, "tmpfs") == 0 ||
143 strcmp(type, "sysfs") == 0 ||
144 strcmp(type, "devpts") == 0||
145 strcmp(type, "cgroups") == 0 ||
146 strcmp(type, "devfs") == 0 ||
147 strcmp(type, "dlmfs") == 0 ||
148 strcmp(type, "cpuset") == 0 ||
1bbe1bc7 149 strcmp(type, "securityfs") == 0 ||
c5a6f7a9
KZ
150 strcmp(type, "rpc_pipefs") == 0 ||
151 strcmp(type, "fusectl") == 0 ||
152 strcmp(type, "binfmt_misc") == 0 ||
153 strcmp(type, "fuse.gvfs-fuse-daemon") == 0 ||
1bbe1bc7 154 strcmp(type, "debugfs") == 0 ||
69b7e41e
KZ
155 strcmp(type, "spufs") == 0)
156 return 1;
157 return 0;
158}
159
160/**
161 * mnt_fstype_is_netfs:
162 * @type: filesystem name
163 *
164 * Returns: 1 for filesystems like cifs, nfs, ... or 0.
165 */
166int mnt_fstype_is_netfs(const char *type)
167{
168 if (!type)
169 return 0;
170 if (strcmp(type, "cifs") == 0 ||
171 strcmp(type, "smbfs") == 0 ||
172 strncmp(type, "nfs", 3) == 0 ||
173 strcmp(type, "afs") == 0 ||
174 strcmp(type, "ncpfs") == 0)
175 return 1;
176 return 0;
177}
178
abc9c0f7
KZ
179/**
180 * mnt_match_fstype:
181 * @type: filesystem type
182 * @pattern: filesystem name or comma delimitted list of names
183 *
184 * The @pattern list of filesystem can be prefixed with a global
185 * "no" prefix to invert matching of the whole list. The "no" could
3d735589
KZ
186 * also used for individual items in the @pattern list. So,
187 * "nofoo,bar" has the same meaning as "nofoo,nobar".
abc9c0f7 188 *
3d735589
KZ
189 * "bar" : "nofoo,bar" -> False (global "no" prefix)
190 *
191 * "bar" : "foo,bar" -> True
abc9c0f7 192 *
abc9c0f7
KZ
193 * "bar" : "foo,nobar" -> False
194 *
195 * Returns: 1 if type is matching, else 0. This function also returns
196 * 0 if @pattern is NULL and @type is non-NULL.
197 */
198int mnt_match_fstype(const char *type, const char *pattern)
199{
200 int no = 0; /* negated types list */
201 int len;
202 const char *p;
203
204 if (!pattern && !type)
205 return 1;
206 if (!pattern)
207 return 0;
208
209 if (!strncmp(pattern, "no", 2)) {
210 no = 1;
211 pattern += 2;
212 }
213
214 /* Does type occur in types, separated by commas? */
215 len = strlen(type);
216 p = pattern;
217 while(1) {
218 if (!strncmp(p, "no", 2) && !strncmp(p+2, type, len) &&
219 (p[len+2] == 0 || p[len+2] == ','))
220 return 0;
221 if (strncmp(p, type, len) == 0 && (p[len] == 0 || p[len] == ','))
222 return !no;
223 p = strchr(p,',');
224 if (!p)
225 break;
226 p++;
227 }
228 return no;
229}
230
231
232/* Returns 1 if needle found or noneedle not found in haystack
233 * Otherwise returns 0
234 */
235static int check_option(const char *haystack, size_t len,
236 const char *needle, size_t needle_len)
237{
238 const char *p;
239 int no = 0;
240
241 if (needle_len >= 2 && !strncmp(needle, "no", 2)) {
242 no = 1;
243 needle += 2;
244 needle_len -= 2;
245 }
246
247 for (p = haystack; p && p < haystack + len; p++) {
248 char *sep = strchr(p, ',');
249 size_t plen = sep ? sep - p : len - (p - haystack);
250
251 if (plen == needle_len) {
252 if (!strncmp(p, needle, plen))
253 return !no; /* foo or nofoo was found */
254 }
255 p += plen;
256 }
257
258 return no; /* foo or nofoo was not found */
259}
260
261/**
262 * mnt_match_options:
263 * @optstr: options string
264 * @pattern: comma delimitted list of options
265 *
266 * The "no" could used for individual items in the @options list. The "no"
267 * prefix does not have a global meanning.
268 *
269 * Unlike fs type matching, nonetdev,user and nonetdev,nouser have
270 * DIFFERENT meanings; each option is matched explicitly as specified.
271 *
3d735589
KZ
272 * "xxx,yyy,zzz" : "nozzz" -> False
273 *
274 * "xxx,yyy,zzz" : "xxx,noeee" -> True
abc9c0f7
KZ
275 *
276 * Returns: 1 if pattern is matching, else 0. This function also returns 0
277 * if @pattern is NULL and @optstr is non-NULL.
278 */
279int mnt_match_options(const char *optstr, const char *pattern)
280{
281 const char *p;
282 size_t len, optstr_len = 0;
283
284 if (!pattern && !optstr)
285 return 1;
286 if (!pattern)
287 return 0;
288
289 len = strlen(pattern);
290 if (optstr)
291 optstr_len = strlen(optstr);
292
293 for (p = pattern; p < pattern + len; p++) {
294 char *sep = strchr(p, ',');
295 size_t plen = sep ? sep - p : len - (p - pattern);
296
297 if (!plen)
298 continue; /* if two ',' appear in a row */
299
300 if (!check_option(optstr, optstr_len, p, plen))
301 return 0; /* any match failure means failure */
302
303 p += plen;
304 }
305
306 /* no match failures in list means success */
307 return 1;
308}
309
97e23b5e
KZ
310void mnt_free_filesystems(char **filesystems)
311{
312 char **p;
313
314 if (!filesystems)
315 return;
316 for (p = filesystems; *p; p++)
317 free(*p);
318 free(filesystems);
319}
320
321static int add_filesystem(char ***filesystems, char *name)
322{
323 int n = 0;
324
325 assert(filesystems);
326 assert(name);
327
328 if (*filesystems) {
329 char **p;
330 for (n = 0, p = *filesystems; *p; p++, n++) {
331 if (strcmp(*p, name) == 0)
332 return 0;
333 }
334 }
335
336 #define MYCHUNK 16
337
338 if (n == 0 || !((n + 1) % MYCHUNK)) {
339 size_t items = ((n + 1 + MYCHUNK) / MYCHUNK) * MYCHUNK;
340 char **x = realloc(*filesystems, items * sizeof(char *));
341
342 if (!x)
343 goto err;
344 *filesystems = x;
345 }
346 name = strdup(name);
347 if (!name)
348 goto err;
349 (*filesystems)[n] = name;
350 (*filesystems)[n + 1] = NULL;
351 return 0;
352err:
353 mnt_free_filesystems(*filesystems);
354 return -ENOMEM;
355}
356
357static int get_filesystems(const char *filename, char ***filesystems, const char *pattern)
358{
359 FILE *f;
360 char line[128];
361
362 f = fopen(filename, "r");
363 if (!f)
364 return 0;
365
366 while (fgets(line, sizeof(line), f)) {
367 char name[sizeof(line)];
368 int rc;
369
370 if (*line == '#' || strncmp(line, "nodev", 5) == 0)
371 continue;
372 if (sscanf(line, " %128[^\n ]\n", name) != 1)
373 continue;
374 if (pattern && !mnt_match_fstype(name, pattern))
375 continue;
376 rc = add_filesystem(filesystems, name);
377 if (rc)
378 return rc;
379 }
380 return 0;
381}
382
383int mnt_get_filesystems(char ***filesystems, const char *pattern)
384{
385 int rc;
386
387 if (!filesystems)
388 return -EINVAL;
389 *filesystems = NULL;
390
391 rc = get_filesystems(_PATH_FILESYSTEMS, filesystems, pattern);
392 if (rc)
393 return rc;
394 return get_filesystems(_PATH_PROC_FILESYSTEMS, filesystems, pattern);
395}
396
69b7e41e
KZ
397/*
398 * Returns allocated string with username or NULL.
399 */
400char *mnt_get_username(const uid_t uid)
401{
402 struct passwd pwd;
403 struct passwd *res;
404 size_t sz = sysconf(_SC_GETPW_R_SIZE_MAX);
405 char *buf, *username = NULL;
406
407 if (sz <= 0)
408 sz = 16384; /* Should be more than enough */
409
410 buf = malloc(sz);
411 if (!buf)
412 return NULL;
413
414 if (!getpwuid_r(uid, &pwd, buf, sz, &res) && res)
415 username = strdup(pwd.pw_name);
416
417 free(buf);
418 return username;
419}
abc9c0f7 420
a1e8af75
KZ
421int mnt_get_uid(const char *username, uid_t *uid)
422{
188dc15a 423 int rc = -1;
a1e8af75
KZ
424 struct passwd pwd;
425 struct passwd *pw;
426 size_t sz = sysconf(_SC_GETPW_R_SIZE_MAX);
427 char *buf;
428
188dc15a
KZ
429 if (!username || !uid)
430 return -EINVAL;
a1e8af75
KZ
431 if (sz <= 0)
432 sz = 16384; /* Should be more than enough */
433
434 buf = malloc(sz);
435 if (!buf)
436 return -ENOMEM;
437
188dc15a 438 if (!getpwnam_r(username, &pwd, buf, sz, &pw) && pw) {
a1e8af75 439 *uid= pw->pw_uid;
188dc15a
KZ
440 rc = 0;
441 } else {
442 DBG(UTILS, mnt_debug(
443 "cannot convert '%s' username to UID", username));
444 }
a1e8af75
KZ
445
446 free(buf);
188dc15a 447 return rc;
a1e8af75
KZ
448}
449
450int mnt_get_gid(const char *groupname, gid_t *gid)
451{
188dc15a 452 int rc = -1;
a1e8af75
KZ
453 struct group grp;
454 struct group *gr;
455 size_t sz = sysconf(_SC_GETGR_R_SIZE_MAX);
456 char *buf;
457
188dc15a
KZ
458 if (!groupname || !gid)
459 return -EINVAL;
a1e8af75
KZ
460 if (sz <= 0)
461 sz = 16384; /* Should be more than enough */
462
463 buf = malloc(sz);
464 if (!buf)
465 return -ENOMEM;
466
188dc15a 467 if (!getgrnam_r(groupname, &grp, buf, sz, &gr) && gr) {
a1e8af75 468 *gid= gr->gr_gid;
188dc15a
KZ
469 rc = 0;
470 } else {
471 DBG(UTILS, mnt_debug(
472 "cannot convert '%s' groupname to GID", groupname));
473 }
a1e8af75
KZ
474
475 free(buf);
188dc15a
KZ
476 return rc;
477}
478
479int mnt_in_group(gid_t gid)
480{
481 int rc = 0, n, i;
482 gid_t *grps = NULL;
483
484 if (getgid() == gid)
485 return 1;
486
487 n = getgroups(0, NULL);
488 if (n <= 0)
489 goto done;
490
491 grps = malloc(n * sizeof(*grps));
492 if (!grps)
493 goto done;
494
495 if (getgroups(n, grps) == n) {
496 for (i = 0; i < n; i++) {
497 if (grps[i] == gid) {
498 rc = 1;
499 break;
500 }
501 }
502 }
503done:
504 free(grps);
505 return rc;
a1e8af75
KZ
506}
507
1d0cd73f
KZ
508static int try_write(const char *filename)
509{
510 int fd;
511
512 if (!filename)
513 return -EINVAL;
514
b0bb8fb6
KZ
515 fd = open(filename, O_RDWR|O_CREAT, S_IWUSR| \
516 S_IRUSR|S_IRGRP|S_IROTH);
1d0cd73f
KZ
517 if (fd >= 0) {
518 close(fd);
519 return 0;
520 }
521 return -errno;
522}
523
524/**
525 * mnt_has_regular_mtab:
526 * @mtab: returns path to mtab
527 * @writable: returns 1 if the file is writable
528 *
529 * If the file does not exist and @writable argument is not NULL then it will
530 * try to create the file
531 *
532 * Returns: 1 if /etc/mtab is a reqular file, and 0 in case of error (check
533 * errno for more details).
0532ba1d 534 */
1d0cd73f 535int mnt_has_regular_mtab(const char **mtab, int *writable)
0532ba1d
KZ
536{
537 struct stat st;
70bf97ae 538 int rc;
1d0cd73f 539 const char *filename = mtab && *mtab ? *mtab : mnt_get_mtab_path();
0532ba1d 540
7c118af7
KZ
541 if (writable)
542 *writable = 0;
70bf97ae 543 if (mtab && !*mtab)
1d0cd73f
KZ
544 *mtab = filename;
545
546 DBG(UTILS, mnt_debug("mtab: %s", filename));
70bf97ae 547
1d0cd73f
KZ
548 rc = lstat(filename, &st);
549
550 if (rc == 0) {
551 /* file exist */
552 if (S_ISREG(st.st_mode)) {
553 if (writable)
554 *writable = !try_write(filename);
555 return 1;
556 }
7c118af7 557 goto done;
1d0cd73f
KZ
558 }
559
560 /* try to create the file */
70bf97ae 561 if (writable) {
1d0cd73f 562 *writable = !try_write(filename);
7c118af7
KZ
563 if (*writable)
564 return 1;
1d0cd73f
KZ
565 }
566
7c118af7
KZ
567done:
568 DBG(UTILS, mnt_debug("%s: irregular/non-writable", filename));
1d0cd73f
KZ
569 return 0;
570}
7c118af7 571
77417bc0
KZ
572/*
573 * Don't export this to libmount API -- utab is private library stuff.
1d0cd73f
KZ
574 *
575 * If the file does not exist and @writable argument is not NULL then it will
576 * try to create the directory (e.g. /dev/.mount) and the file.
577 *
578 * Returns: 1 if /etc/utab is a reqular file, and 0 in case of error (check
579 * errno for more details).
580 */
581
582int mnt_has_regular_utab(const char **utab, int *writable)
583{
584 struct stat st;
585 int rc;
586 const char *filename = utab && *utab ? *utab : mnt_get_utab_path();
587
7c118af7
KZ
588 if (writable)
589 *writable = 0;
1d0cd73f
KZ
590 if (utab && !*utab)
591 *utab = filename;
592
593 DBG(UTILS, mnt_debug("utab: %s", filename));
594
595 rc = lstat(filename, &st);
596
597 if (rc == 0) {
598 /* file exist */
599 if (S_ISREG(st.st_mode)) {
600 if (writable)
7c118af7 601 *writable = !try_write(filename);
1d0cd73f 602 return 1;
70bf97ae 603 }
7c118af7 604 goto done; /* it's not regular file */
70bf97ae 605 }
1d0cd73f
KZ
606
607 if (writable) {
608 char *dirname = strdup(filename);
609
610 if (!dirname)
7c118af7 611 goto done;
1d0cd73f
KZ
612
613 stripoff_last_component(dirname); /* remove filename */
614
b0bb8fb6
KZ
615 rc = mkdir(dirname, S_IWUSR|
616 S_IRUSR|S_IRGRP|S_IROTH|
617 S_IXUSR|S_IXGRP|S_IXOTH);
1d0cd73f
KZ
618 free(dirname);
619 if (rc && errno != EEXIST)
7c118af7 620 goto done; /* probably EACCES */
1d0cd73f
KZ
621
622 *writable = !try_write(filename);
7c118af7
KZ
623 if (*writable)
624 return 1;
1d0cd73f 625 }
7c118af7
KZ
626done:
627 DBG(UTILS, mnt_debug("%s: irregular/non-writable file", filename));
1d0cd73f 628 return 0;
0532ba1d
KZ
629}
630
3a5b1b1d
KZ
631/**
632 * mnt_get_fstab_path:
633 *
634 * Returns: path to /etc/fstab or $LIBMOUNT_FSTAB.
635 */
636const char *mnt_get_fstab_path(void)
637{
638 const char *p = mnt_getenv_safe("LIBMOUNT_FSTAB");
639 return p ? : _PATH_MNTTAB;
640}
641
642/**
643 * mnt_get_mtab_path:
644 *
b37dd175
KZ
645 * This function returns *default* location of the mtab file. The result does
646 * not have to be writable. See also mnt_get_writable_mtab_path().
3a5b1b1d
KZ
647 *
648 * Returns: path to /etc/mtab or $LIBMOUNT_MTAB.
649 */
650const char *mnt_get_mtab_path(void)
651{
652 const char *p = mnt_getenv_safe("LIBMOUNT_MTAB");
653 return p ? : _PATH_MOUNTED;
654}
655
77417bc0
KZ
656/*
657 * Don't export this to libmount API -- utab is private library stuff.
be1a5180 658 *
b37dd175
KZ
659 * Returns: path to /dev/.mount/utab or $LIBMOUNT_UTAB.
660 */
661const char *mnt_get_utab_path(void)
662{
663 const char *p = mnt_getenv_safe("LIBMOUNT_UTAB");
664 return p ? : MNT_PATH_UTAB;
665}
666
d1be0c34 667
b37dd175
KZ
668/* returns file descriptor or -errno, @name returns uniques filename
669 */
670int mnt_open_uniq_filename(const char *filename, char **name, int flags)
671{
672 int rc, fd;
673 char *n;
674
675 assert(filename);
676
677 if (name)
678 *name = NULL;
679
680 rc = asprintf(&n, "%s.XXXXXX", filename);
681 if (rc <= 0)
682 return -errno;
683
684 fd = mkostemp(n, flags | O_EXCL);
685 if (fd >= 0 && name)
686 *name = n;
687 else
688 free(n);
689
690 return fd < 0 ? -errno : fd;
691}
0bb44be3 692
0bb44be3
KZ
693char *mnt_get_mountpoint(const char *path)
694{
695 char *mnt = strdup(path);
696 struct stat st;
697 dev_t dir, base;
698
699 if (!mnt)
700 return NULL;
701 if (*mnt == '/' && *(mnt + 1) == '\0')
9758c88a 702 goto done;
0bb44be3
KZ
703
704 if (stat(mnt, &st))
705 goto err;
706 base = st.st_dev;
707
708 do {
709 char *p = stripoff_last_component(mnt);
710
711 if (!p)
712 break;
713 if (stat(*mnt ? mnt : "/", &st))
714 goto err;
715 dir = st.st_dev;
716 if (dir != base) {
717 *(p - 1) = '/';
9758c88a 718 goto done;
0bb44be3
KZ
719 }
720 base = dir;
721 } while (mnt && *(mnt + 1) != '\0');
722
723 memcpy(mnt, "/", 2);
9758c88a 724done:
3f31a959 725 DBG(UTILS, mnt_debug("fs-root for %s is %s", path, mnt));
9758c88a 726 return mnt;
0bb44be3
KZ
727err:
728 free(mnt);
729 return NULL;
730}
731
9758c88a 732char *mnt_get_fs_root(const char *path, const char *mnt)
0bb44be3 733{
9758c88a 734 char *m = (char *) mnt;
0bb44be3
KZ
735 const char *p;
736 size_t sz;
737
9758c88a
KZ
738 if (!m)
739 m = mnt_get_mountpoint(path);
740 if (!m)
0bb44be3
KZ
741 return NULL;
742
9758c88a 743 sz = strlen(m);
0bb44be3
KZ
744 p = sz > 1 ? path + sz : path;
745
9758c88a
KZ
746 if (m != mnt)
747 free(m);
0bb44be3
KZ
748
749 return *p ? strdup(p) : strdup("/");
750}
751
abc9c0f7
KZ
752#ifdef TEST_PROGRAM
753int test_match_fstype(struct mtest *ts, int argc, char *argv[])
754{
755 char *type = argv[1];
756 char *pattern = argv[2];
757
758 printf("%s\n", mnt_match_fstype(type, pattern) ? "MATCH" : "NOT-MATCH");
759 return 0;
760}
761
762int test_match_options(struct mtest *ts, int argc, char *argv[])
763{
764 char *optstr = argv[1];
765 char *pattern = argv[2];
766
767 printf("%s\n", mnt_match_options(optstr, pattern) ? "MATCH" : "NOT-MATCH");
768 return 0;
769}
770
b49103ed
KZ
771int test_startswith(struct mtest *ts, int argc, char *argv[])
772{
773 char *optstr = argv[1];
774 char *pattern = argv[2];
775
776 printf("%s\n", startswith(optstr, pattern) ? "YES" : "NOT");
777 return 0;
778}
779
780int test_endswith(struct mtest *ts, int argc, char *argv[])
781{
782 char *optstr = argv[1];
783 char *pattern = argv[2];
784
785 printf("%s\n", endswith(optstr, pattern) ? "YES" : "NOT");
786 return 0;
787}
788
0bb44be3
KZ
789int test_mountpoint(struct mtest *ts, int argc, char *argv[])
790{
791 char *path = canonicalize_path(argv[1]),
792 *mnt = path ? mnt_get_mountpoint(path) : NULL;
793
794 printf("%s: %s\n", argv[1], mnt ? : "unknown");
795 free(mnt);
796 free(path);
797 return 0;
798}
799
800int test_fsroot(struct mtest *ts, int argc, char *argv[])
801{
802 char *path = canonicalize_path(argv[1]),
d672fb26 803 *mnt = path ? mnt_get_fs_root(path, NULL) : NULL;
0bb44be3
KZ
804
805 printf("%s: %s\n", argv[1], mnt ? : "unknown");
806 free(mnt);
807 free(path);
808 return 0;
809}
abc9c0f7 810
97e23b5e
KZ
811int test_filesystems(struct mtest *ts, int argc, char *argv[])
812{
813 char **filesystems = NULL;
814 int rc;
815
816 rc = mnt_get_filesystems(&filesystems, argc ? argv[1] : NULL);
817 if (!rc) {
818 char **p;
819 for (p = filesystems; *p; p++)
820 printf("%s\n", *p);
821 mnt_free_filesystems(filesystems);
822 }
823 return rc;
824}
825
abc9c0f7
KZ
826int main(int argc, char *argv[])
827{
828 struct mtest tss[] = {
829 { "--match-fstype", test_match_fstype, "<type> <pattern> FS types matching" },
830 { "--match-options", test_match_options, "<options> <pattern> options matching" },
97e23b5e 831 { "--filesystems", test_filesystems, "[<pattern>] list /{etc,proc}/filesystems" },
b49103ed
KZ
832 { "--starts-with", test_startswith, "<string> <prefix>" },
833 { "--ends-with", test_endswith, "<string> <prefix>" },
0bb44be3
KZ
834 { "--mountpoint", test_mountpoint, "<path>" },
835 { "--fs-root", test_fsroot, "<path>" },
abc9c0f7
KZ
836 { NULL }
837 };
838
839 return mnt_run_test(tss, argc, argv);
840}
841
842#endif /* TEST_PROGRAM */