]> git.ipfire.org Git - thirdparty/util-linux.git/blame - libmount/src/utils.c
Merge branch 'evelu-epyc' of https://github.com/ErwanAliasr1/util-linux
[thirdparty/util-linux.git] / libmount / src / utils.c
CommitLineData
2c37ca7c 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
69b7e41e 2/*
2c37ca7c 3 * This file is part of libmount from util-linux project.
69b7e41e 4 *
2c37ca7c
KZ
5 * Copyright (C) 2008-2018 Karel Zak <kzak@redhat.com>
6 *
7 * libmount is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU Lesser General Public License as published by
9 * the Free Software Foundation; either version 2.1 of the License, or
10 * (at your option) any later version.
69b7e41e
KZ
11 */
12
192c6aad
KZ
13/**
14 * SECTION: utils
15 * @title: Utils
16 * @short_description: misc utils.
17 */
69b7e41e 18#include <ctype.h>
69b7e41e
KZ
19#include <fcntl.h>
20#include <pwd.h>
a1e8af75 21#include <grp.h>
a53cc4e0 22#include <blkid.h>
69b7e41e 23
b49103ed 24#include "strutils.h"
0532ba1d 25#include "pathnames.h"
69b7e41e 26#include "mountP.h"
3c5e4ef8 27#include "mangle.h"
0bb44be3 28#include "canonicalize.h"
035507c8 29#include "env.h"
b483debb 30#include "match.h"
934530c7 31#include "fileutils.h"
df019e9b 32#include "statfs_magic.h"
40f00b4f 33#include "sysfs.h"
69b7e41e 34
40b27864
KZ
35int append_string(char **a, const char *b)
36{
37 size_t al, bl;
38 char *tmp;
39
40 assert(a);
41
42 if (!b || !*b)
43 return 0;
44 if (!*a) {
45 *a = strdup(b);
46 return !*a ? -ENOMEM : 0;
47 }
48
49 al = strlen(*a);
c7616e4c 50 bl = strlen(b);
40b27864
KZ
51
52 tmp = realloc(*a, al + bl + 1);
53 if (!tmp)
54 return -ENOMEM;
55 *a = tmp;
56 memcpy((*a) + al, b, bl + 1);
57 return 0;
58}
59
dad88cb3 60/*
d58b3157 61 * Return 1 if the file is not accessible or empty
dad88cb3
KZ
62 */
63int is_file_empty(const char *name)
64{
65 struct stat st;
66 assert(name);
67
68 return (stat(name, &st) != 0 || st.st_size == 0);
69}
70
2c6b25f0
KZ
71int mnt_valid_tagname(const char *tagname)
72{
73 if (tagname && *tagname && (
74 strcmp("UUID", tagname) == 0 ||
75 strcmp("LABEL", tagname) == 0 ||
76 strcmp("PARTUUID", tagname) == 0 ||
77 strcmp("PARTLABEL", tagname) == 0))
78 return 1;
79
80 return 0;
81}
82
a53cc4e0
KZ
83/**
84 * mnt_tag_is_valid:
85 * @tag: NAME=value string
86 *
87 * Returns: 1 if the @tag is parsable and tag NAME= is supported by libmount, or 0.
88 */
89int mnt_tag_is_valid(const char *tag)
90{
91 char *t = NULL;
92 int rc = tag && blkid_parse_tag_string(tag, &t, NULL) == 0
93 && mnt_valid_tagname(t);
94
95 free(t);
96 return rc;
97}
98
fd1eb7a7
KZ
99int mnt_parse_offset(const char *str, size_t len, uintmax_t *res)
100{
101 char *p;
102 int rc = 0;
103
0208ae2e 104 if (!str || !*str)
fd1eb7a7
KZ
105 return -EINVAL;
106
107 p = strndup(str, len);
108 if (!p)
109 return -errno;
110
111 if (strtosize(p, res))
112 rc = -EINVAL;
113 free(p);
114 return rc;
115}
116
7383ebce
DR
117/* used as a callback by bsearch in mnt_fstype_is_pseudofs() */
118static int fstype_cmp(const void *v1, const void *v2)
119{
9c566fce
KZ
120 const char *s1 = *(char * const *)v1;
121 const char *s2 = *(char * const *)v2;
7383ebce
DR
122
123 return strcmp(s1, s2);
124}
125
6589a163
KZ
126int mnt_stat_mountpoint(const char *target, struct stat *st)
127{
128#ifdef AT_NO_AUTOMOUNT
2859592e 129 return fstatat(AT_FDCWD, target, st, AT_NO_AUTOMOUNT);
6589a163
KZ
130#else
131 return stat(target, st);
132#endif
133}
134
61073773 135/*
d58b3157
OO
136 * Note that the @target has to be an absolute path (so at least "/"). The
137 * @filename returns an allocated buffer with the last path component, for example:
61073773
KZ
138 *
139 * mnt_chdir_to_parent("/mnt/test", &buf) ==> chdir("/mnt"), buf="test"
66bb8267
KZ
140 */
141int mnt_chdir_to_parent(const char *target, char **filename)
142{
61073773 143 char *buf, *parent, *last = NULL;
66bb8267
KZ
144 char cwd[PATH_MAX];
145 int rc = -EINVAL;
146
147 if (!target || *target != '/')
148 return -EINVAL;
149
83a78332 150 DBG(UTILS, ul_debug("moving to %s parent", target));
61073773
KZ
151
152 buf = strdup(target);
153 if (!buf)
66bb8267
KZ
154 return -ENOMEM;
155
61073773
KZ
156 if (*(buf + 1) != '\0') {
157 last = stripoff_last_component(buf);
66bb8267
KZ
158 if (!last)
159 goto err;
160 }
66bb8267 161
61073773
KZ
162 parent = buf && *buf ? buf : "/";
163
164 if (chdir(parent) == -1) {
83a78332 165 DBG(UTILS, ul_debug("failed to chdir to %s: %m", parent));
66bb8267
KZ
166 rc = -errno;
167 goto err;
168 }
169 if (!getcwd(cwd, sizeof(cwd))) {
83a78332 170 DBG(UTILS, ul_debug("failed to obtain current directory: %m"));
66bb8267
KZ
171 rc = -errno;
172 goto err;
173 }
61073773 174 if (strcmp(cwd, parent) != 0) {
83a78332 175 DBG(UTILS, ul_debug(
61073773 176 "unexpected chdir (expected=%s, cwd=%s)", parent, cwd));
66bb8267
KZ
177 goto err;
178 }
179
83a78332 180 DBG(CXT, ul_debug(
61073773
KZ
181 "current directory moved to %s [last_component='%s']",
182 parent, last));
66bb8267 183
ba2bdf41
KZ
184 if (filename) {
185 *filename = buf;
66bb8267 186
ba2bdf41
KZ
187 if (!last || !*last)
188 memcpy(*filename, ".", 2);
189 else
4e9f59d1 190 memmove(*filename, last, strlen(last) + 1);
cfa44747
KZ
191 } else
192 free(buf);
66bb8267
KZ
193 return 0;
194err:
61073773 195 free(buf);
66bb8267
KZ
196 return rc;
197}
198
f9906424 199/*
d58b3157 200 * Check if @path is on a read-only filesystem independently of file permissions.
f9906424
KZ
201 */
202int mnt_is_readonly(const char *path)
203{
204 if (access(path, W_OK) == 0)
205 return 0;
206 if (errno == EROFS)
207 return 1;
208 if (errno != EACCES)
209 return 0;
210
473c5fb8 211#ifdef HAVE_UTIMENSAT
f9906424
KZ
212 /*
213 * access(2) returns EACCES on read-only FS:
214 *
215 * - for set-uid application if one component of the path is not
216 * accessible for the current rUID. (Note that euidaccess(2) does not
217 * check for EROFS at all).
218 *
d58b3157 219 * - for a read-write filesystem with a read-only VFS node (aka -o remount,ro,bind)
f9906424
KZ
220 */
221 {
222 struct timespec times[2];
223
42ee7882
KZ
224 DBG(UTILS, ul_debug(" doing utimensat() based write test"));
225
f9906424
KZ
226 times[0].tv_nsec = UTIME_NOW; /* atime */
227 times[1].tv_nsec = UTIME_OMIT; /* mtime */
228
229 if (utimensat(AT_FDCWD, path, times, 0) == -1)
230 return errno == EROFS;
231 }
232#endif
233 return 0;
234}
235
3c5e4ef8
KZ
236/**
237 * mnt_mangle:
238 * @str: string
239 *
240 * Encode @str to be compatible with fstab/mtab
241 *
d58b3157 242 * Returns: newly allocated string or NULL in case of error.
3c5e4ef8
KZ
243 */
244char *mnt_mangle(const char *str)
245{
246 return mangle(str);
247}
248
249/**
250 * mnt_unmangle:
251 * @str: string
252 *
253 * Decode @str from fstab/mtab
254 *
d58b3157 255 * Returns: newly allocated string or NULL in case of error.
3c5e4ef8
KZ
256 */
257char *mnt_unmangle(const char *str)
258{
dd369652 259 return unmangle(str, NULL);
3c5e4ef8
KZ
260}
261
69b7e41e
KZ
262/**
263 * mnt_fstype_is_pseudofs:
264 * @type: filesystem name
265 *
266 * Returns: 1 for filesystems like proc, sysfs, ... or 0.
267 */
268int mnt_fstype_is_pseudofs(const char *type)
269{
7383ebce
DR
270 /* This array must remain sorted when adding new fstypes */
271 static const char *pseudofs[] = {
272 "anon_inodefs",
273 "autofs",
274 "bdev",
275 "binfmt_misc",
276 "cgroup",
624996a9 277 "cgroup2",
7383ebce
DR
278 "configfs",
279 "cpuset",
280 "debugfs",
281 "devfs",
282 "devpts",
283 "devtmpfs",
284 "dlmfs",
84ceaf7a 285 "efivarfs",
89342e04
SB
286 "fuse", /* Fallback name of fuse used by many poorly written drivers. */
287 "fuse.archivemount", /* Not a true pseudofs (has source), but source is not reported. */
288 "fuse.dumpfs", /* In fact, it is a netfs, but source is not reported. */
289 "fuse.encfs", /* Not a true pseudofs (has source), but source is not reported. */
290 "fuse.gvfs-fuse-daemon", /* Old name, not used by gvfs any more. */
291 "fuse.gvfsd-fuse",
292 "fuse.rofiles-fuse",
293 "fuse.xwmfs",
462a5703 294 "fusectl",
7383ebce
DR
295 "hugetlbfs",
296 "mqueue",
297 "nfsd",
298 "none",
ae6fd680 299 "nsfs",
209fd7a7 300 "overlay",
7383ebce
DR
301 "pipefs",
302 "proc",
303 "pstore",
304 "ramfs",
305 "rootfs",
306 "rpc_pipefs",
307 "securityfs",
308 "sockfs",
309 "spufs",
310 "sysfs",
311 "tmpfs"
312 };
313
4569bbea
KZ
314 assert(type);
315
7383ebce
DR
316 return !(bsearch(&type, pseudofs, ARRAY_SIZE(pseudofs),
317 sizeof(char*), fstype_cmp) == NULL);
69b7e41e
KZ
318}
319
320/**
321 * mnt_fstype_is_netfs:
322 * @type: filesystem name
323 *
324 * Returns: 1 for filesystems like cifs, nfs, ... or 0.
325 */
326int mnt_fstype_is_netfs(const char *type)
327{
c59cf20c 328 if (strcmp(type, "cifs") == 0 ||
69b7e41e 329 strcmp(type, "smbfs") == 0 ||
c59cf20c
KZ
330 strncmp(type,"nfs", 3) == 0 ||
331 strcmp(type, "afs") == 0 ||
332 strcmp(type, "ncpfs") == 0 ||
89342e04
SB
333 strcmp(type, "fuse.curlftpfs") == 0 ||
334 strcmp(type, "fuse.sshfs") == 0 ||
c59cf20c 335 strncmp(type,"9p", 2) == 0)
69b7e41e
KZ
336 return 1;
337 return 0;
338}
339
6a52473e
KZ
340const char *mnt_statfs_get_fstype(struct statfs *vfs)
341{
342 assert(vfs);
343
344 switch (vfs->f_type) {
df019e9b
KZ
345 case STATFS_ADFS_MAGIC: return "adfs";
346 case STATFS_AFFS_MAGIC: return "affs";
347 case STATFS_AFS_MAGIC: return "afs";
348 case STATFS_AUTOFS_MAGIC: return "autofs";
349 case STATFS_BDEVFS_MAGIC: return "bdev";
350 case STATFS_BEFS_MAGIC: return "befs";
351 case STATFS_BFS_MAGIC: return "befs";
352 case STATFS_BINFMTFS_MAGIC: return "binfmt_misc";
353 case STATFS_BTRFS_MAGIC: return "btrfs";
354 case STATFS_CEPH_MAGIC: return "ceph";
355 case STATFS_CGROUP_MAGIC: return "cgroup";
356 case STATFS_CIFS_MAGIC: return "cifs";
357 case STATFS_CODA_MAGIC: return "coda";
358 case STATFS_CONFIGFS_MAGIC: return "configfs";
359 case STATFS_CRAMFS_MAGIC: return "cramfs";
360 case STATFS_DEBUGFS_MAGIC: return "debugfs";
361 case STATFS_DEVPTS_MAGIC: return "devpts";
362 case STATFS_ECRYPTFS_MAGIC: return "ecryptfs";
363 case STATFS_EFIVARFS_MAGIC: return "efivarfs";
364 case STATFS_EFS_MAGIC: return "efs";
365 case STATFS_EXOFS_MAGIC: return "exofs";
366 case STATFS_EXT4_MAGIC: return "ext4"; /* all extN use the same magic */
367 case STATFS_F2FS_MAGIC: return "f2fs";
368 case STATFS_FUSE_MAGIC: return "fuse";
369 case STATFS_FUTEXFS_MAGIC: return "futexfs";
370 case STATFS_GFS2_MAGIC: return "gfs2";
371 case STATFS_HFSPLUS_MAGIC: return "hfsplus";
372 case STATFS_HOSTFS_MAGIC: return "hostfs";
373 case STATFS_HPFS_MAGIC: return "hpfs";
374 case STATFS_HPPFS_MAGIC: return "hppfs";
375 case STATFS_HUGETLBFS_MAGIC: return "hugetlbfs";
376 case STATFS_ISOFS_MAGIC: return "iso9660";
377 case STATFS_JFFS2_MAGIC: return "jffs2";
378 case STATFS_JFS_MAGIC: return "jfs";
379 case STATFS_LOGFS_MAGIC: return "logfs";
380 case STATFS_MINIX2_MAGIC:
381 case STATFS_MINIX2_MAGIC2:
382 case STATFS_MINIX3_MAGIC:
383 case STATFS_MINIX_MAGIC:
384 case STATFS_MINIX_MAGIC2: return "minix";
385 case STATFS_MQUEUE_MAGIC: return "mqueue";
386 case STATFS_MSDOS_MAGIC: return "vfat";
387 case STATFS_NCP_MAGIC: return "ncp";
388 case STATFS_NFS_MAGIC: return "nfs";
389 case STATFS_NILFS_MAGIC: return "nilfs2";
390 case STATFS_NTFS_MAGIC: return "ntfs";
391 case STATFS_OCFS2_MAGIC: return "ocfs2";
392 case STATFS_OMFS_MAGIC: return "omfs";
393 case STATFS_OPENPROMFS_MAGIC: return "openpromfs";
394 case STATFS_PIPEFS_MAGIC: return "pipefs";
395 case STATFS_PROC_MAGIC: return "proc";
396 case STATFS_PSTOREFS_MAGIC: return "pstore";
397 case STATFS_QNX4_MAGIC: return "qnx4";
398 case STATFS_QNX6_MAGIC: return "qnx6";
399 case STATFS_RAMFS_MAGIC: return "ramfs";
400 case STATFS_REISERFS_MAGIC: return "reiser4";
401 case STATFS_ROMFS_MAGIC: return "romfs";
402 case STATFS_SECURITYFS_MAGIC: return "securityfs";
403 case STATFS_SELINUXFS_MAGIC: return "selinuxfs";
404 case STATFS_SMACKFS_MAGIC: return "smackfs";
405 case STATFS_SMB_MAGIC: return "smb";
406 case STATFS_SOCKFS_MAGIC: return "sockfs";
407 case STATFS_SQUASHFS_MAGIC: return "squashfs";
408 case STATFS_SYSFS_MAGIC: return "sysfs";
409 case STATFS_TMPFS_MAGIC: return "tmpfs";
410 case STATFS_UBIFS_MAGIC: return "ubifs";
411 case STATFS_UDF_MAGIC: return "udf";
412 case STATFS_UFS2_MAGIC:
413 case STATFS_UFS_MAGIC: return "ufs";
414 case STATFS_V9FS_MAGIC: return "9p";
415 case STATFS_VXFS_MAGIC: return "vxfs";
416 case STATFS_XENFS_MAGIC: return "xenfs";
417 case STATFS_XFS_MAGIC: return "xfs";
6a52473e
KZ
418 default:
419 break;
420 }
421
422 return NULL;
423}
424
425
abc9c0f7
KZ
426/**
427 * mnt_match_fstype:
428 * @type: filesystem type
6ad929bb 429 * @pattern: filesystem name or comma delimited list of names
abc9c0f7 430 *
d58b3157 431 * The @pattern list of filesystems can be prefixed with a global
abc9c0f7 432 * "no" prefix to invert matching of the whole list. The "no" could
6ad929bb 433 * also be used for individual items in the @pattern list. So,
3d735589 434 * "nofoo,bar" has the same meaning as "nofoo,nobar".
abc9c0f7 435 *
3d735589
KZ
436 * "bar" : "nofoo,bar" -> False (global "no" prefix)
437 *
438 * "bar" : "foo,bar" -> True
abc9c0f7 439 *
abc9c0f7
KZ
440 * "bar" : "foo,nobar" -> False
441 *
442 * Returns: 1 if type is matching, else 0. This function also returns
443 * 0 if @pattern is NULL and @type is non-NULL.
444 */
445int mnt_match_fstype(const char *type, const char *pattern)
446{
12089155 447 return match_fstype(type, pattern);
abc9c0f7
KZ
448}
449
97e23b5e
KZ
450void mnt_free_filesystems(char **filesystems)
451{
452 char **p;
453
454 if (!filesystems)
455 return;
456 for (p = filesystems; *p; p++)
457 free(*p);
458 free(filesystems);
459}
460
461static int add_filesystem(char ***filesystems, char *name)
462{
463 int n = 0;
464
465 assert(filesystems);
466 assert(name);
467
468 if (*filesystems) {
469 char **p;
470 for (n = 0, p = *filesystems; *p; p++, n++) {
471 if (strcmp(*p, name) == 0)
472 return 0;
473 }
474 }
475
476 #define MYCHUNK 16
477
478 if (n == 0 || !((n + 1) % MYCHUNK)) {
479 size_t items = ((n + 1 + MYCHUNK) / MYCHUNK) * MYCHUNK;
480 char **x = realloc(*filesystems, items * sizeof(char *));
481
482 if (!x)
483 goto err;
484 *filesystems = x;
485 }
486 name = strdup(name);
97e23b5e
KZ
487 (*filesystems)[n] = name;
488 (*filesystems)[n + 1] = NULL;
6b9784b4
KZ
489 if (!name)
490 goto err;
97e23b5e
KZ
491 return 0;
492err:
493 mnt_free_filesystems(*filesystems);
494 return -ENOMEM;
495}
496
497static int get_filesystems(const char *filename, char ***filesystems, const char *pattern)
498{
4f69189f 499 int rc = 0;
97e23b5e 500 FILE *f;
e3c27186 501 char line[129];
97e23b5e 502
1eb8539d 503 f = fopen(filename, "r" UL_CLOEXECSTR);
97e23b5e 504 if (!f)
ec8121b1
KZ
505 return 1;
506
83a78332 507 DBG(UTILS, ul_debug("reading filesystems list from: %s", filename));
97e23b5e
KZ
508
509 while (fgets(line, sizeof(line), f)) {
510 char name[sizeof(line)];
97e23b5e
KZ
511
512 if (*line == '#' || strncmp(line, "nodev", 5) == 0)
513 continue;
514 if (sscanf(line, " %128[^\n ]\n", name) != 1)
515 continue;
ec8121b1
KZ
516 if (strcmp(name, "*") == 0) {
517 rc = 1;
518 break; /* end of the /etc/filesystems */
519 }
97e23b5e
KZ
520 if (pattern && !mnt_match_fstype(name, pattern))
521 continue;
522 rc = add_filesystem(filesystems, name);
523 if (rc)
4f69189f 524 break;
97e23b5e 525 }
4f69189f
KZ
526
527 fclose(f);
528 return rc;
97e23b5e
KZ
529}
530
3de77c21 531/*
d58b3157 532 * Always check the @filesystems pointer!
ec8121b1
KZ
533 *
534 * man mount:
535 *
536 * ...mount will try to read the file /etc/filesystems, or, if that does not
537 * exist, /proc/filesystems. All of the filesystem types listed there will
538 * be tried, except for those that are labeled "nodev" (e.g., devpts,
539 * proc and nfs). If /etc/filesystems ends in a line with a single * only,
123ddced 540 * mount will read /proc/filesystems afterwards.
3de77c21 541 */
97e23b5e
KZ
542int mnt_get_filesystems(char ***filesystems, const char *pattern)
543{
544 int rc;
545
546 if (!filesystems)
547 return -EINVAL;
ec8121b1 548
97e23b5e
KZ
549 *filesystems = NULL;
550
551 rc = get_filesystems(_PATH_FILESYSTEMS, filesystems, pattern);
ec8121b1 552 if (rc != 1)
97e23b5e 553 return rc;
ec8121b1
KZ
554
555 rc = get_filesystems(_PATH_PROC_FILESYSTEMS, filesystems, pattern);
556 if (rc == 1 && *filesystems)
d58b3157 557 rc = 0; /* /proc/filesystems not found */
ec8121b1
KZ
558
559 return rc;
97e23b5e
KZ
560}
561
69b7e41e 562/*
d58b3157 563 * Returns an allocated string with username or NULL.
69b7e41e
KZ
564 */
565char *mnt_get_username(const uid_t uid)
566{
567 struct passwd pwd;
568 struct passwd *res;
69b7e41e
KZ
569 char *buf, *username = NULL;
570
f7ac9e71 571 buf = malloc(UL_GETPW_BUFSIZ);
69b7e41e
KZ
572 if (!buf)
573 return NULL;
574
f7ac9e71 575 if (!getpwuid_r(uid, &pwd, buf, UL_GETPW_BUFSIZ, &res) && res)
69b7e41e
KZ
576 username = strdup(pwd.pw_name);
577
578 free(buf);
579 return username;
580}
abc9c0f7 581
a1e8af75
KZ
582int mnt_get_uid(const char *username, uid_t *uid)
583{
188dc15a 584 int rc = -1;
a1e8af75
KZ
585 struct passwd pwd;
586 struct passwd *pw;
a1e8af75
KZ
587 char *buf;
588
188dc15a
KZ
589 if (!username || !uid)
590 return -EINVAL;
b47b7b3a 591
f7ac9e71 592 buf = malloc(UL_GETPW_BUFSIZ);
a1e8af75
KZ
593 if (!buf)
594 return -ENOMEM;
595
f7ac9e71 596 if (!getpwnam_r(username, &pwd, buf, UL_GETPW_BUFSIZ, &pw) && pw) {
a1e8af75 597 *uid= pw->pw_uid;
188dc15a
KZ
598 rc = 0;
599 } else {
83a78332 600 DBG(UTILS, ul_debug(
188dc15a 601 "cannot convert '%s' username to UID", username));
61f5ff6c 602 rc = errno ? -errno : -EINVAL;
188dc15a 603 }
a1e8af75
KZ
604
605 free(buf);
188dc15a 606 return rc;
a1e8af75
KZ
607}
608
609int mnt_get_gid(const char *groupname, gid_t *gid)
610{
188dc15a 611 int rc = -1;
a1e8af75
KZ
612 struct group grp;
613 struct group *gr;
a1e8af75
KZ
614 char *buf;
615
188dc15a
KZ
616 if (!groupname || !gid)
617 return -EINVAL;
b47b7b3a 618
f7ac9e71 619 buf = malloc(UL_GETPW_BUFSIZ);
a1e8af75
KZ
620 if (!buf)
621 return -ENOMEM;
622
f7ac9e71 623 if (!getgrnam_r(groupname, &grp, buf, UL_GETPW_BUFSIZ, &gr) && gr) {
a1e8af75 624 *gid= gr->gr_gid;
188dc15a
KZ
625 rc = 0;
626 } else {
83a78332 627 DBG(UTILS, ul_debug(
188dc15a 628 "cannot convert '%s' groupname to GID", groupname));
61f5ff6c 629 rc = errno ? -errno : -EINVAL;
188dc15a 630 }
a1e8af75
KZ
631
632 free(buf);
188dc15a
KZ
633 return rc;
634}
635
636int mnt_in_group(gid_t gid)
637{
638 int rc = 0, n, i;
639 gid_t *grps = NULL;
640
641 if (getgid() == gid)
642 return 1;
643
644 n = getgroups(0, NULL);
645 if (n <= 0)
646 goto done;
647
648 grps = malloc(n * sizeof(*grps));
649 if (!grps)
650 goto done;
651
652 if (getgroups(n, grps) == n) {
653 for (i = 0; i < n; i++) {
654 if (grps[i] == gid) {
655 rc = 1;
656 break;
657 }
658 }
659 }
660done:
661 free(grps);
662 return rc;
a1e8af75
KZ
663}
664
06ff935e 665static int try_write(const char *filename, const char *directory)
1d0cd73f 666{
c08396c7 667 int rc = 0;
1d0cd73f
KZ
668
669 if (!filename)
670 return -EINVAL;
671
06ff935e
KZ
672 DBG(UTILS, ul_debug("try write %s dir: %s", filename, directory));
673
c08396c7 674#ifdef HAVE_EACCESS
06ff935e
KZ
675 /* Try eaccess() first, because open() is overkill, may be monitored by
676 * audit and we don't want to fill logs by our checks...
677 */
678 if (eaccess(filename, R_OK|W_OK) == 0) {
679 DBG(UTILS, ul_debug(" access OK"));
680 return 0;
681 } else if (errno != ENOENT) {
682 DBG(UTILS, ul_debug(" access FAILED"));
683 return -errno;
684 } else if (directory) {
685 /* file does not exist; try if directory is writable */
686 if (eaccess(directory, R_OK|W_OK) != 0)
687 rc = -errno;
688
689 DBG(UTILS, ul_debug(" access %s [%s]", rc ? "FAILED" : "OK", directory));
690 return rc;
691 } else
692#endif
c08396c7 693 {
06ff935e
KZ
694 DBG(UTILS, ul_debug(" doing open-write test"));
695
c08396c7
KZ
696 int fd = open(filename, O_RDWR|O_CREAT|O_CLOEXEC,
697 S_IWUSR|S_IRUSR|S_IRGRP|S_IROTH);
698 if (fd < 0)
699 rc = -errno;
700 else
701 close(fd);
702 }
7a5dbd22 703 return rc;
1d0cd73f
KZ
704}
705
706/**
707 * mnt_has_regular_mtab:
708 * @mtab: returns path to mtab
709 * @writable: returns 1 if the file is writable
710 *
d58b3157
OO
711 * If the file does not exist and @writable argument is not NULL, then it will
712 * try to create the file.
1d0cd73f 713 *
6ad929bb 714 * Returns: 1 if /etc/mtab is a regular file, and 0 in case of error (check
1d0cd73f 715 * errno for more details).
0532ba1d 716 */
1d0cd73f 717int mnt_has_regular_mtab(const char **mtab, int *writable)
0532ba1d
KZ
718{
719 struct stat st;
70bf97ae 720 int rc;
1d0cd73f 721 const char *filename = mtab && *mtab ? *mtab : mnt_get_mtab_path();
0532ba1d 722
7c118af7
KZ
723 if (writable)
724 *writable = 0;
70bf97ae 725 if (mtab && !*mtab)
1d0cd73f
KZ
726 *mtab = filename;
727
83a78332 728 DBG(UTILS, ul_debug("mtab: %s", filename));
70bf97ae 729
1d0cd73f
KZ
730 rc = lstat(filename, &st);
731
732 if (rc == 0) {
d58b3157 733 /* file exists */
1d0cd73f
KZ
734 if (S_ISREG(st.st_mode)) {
735 if (writable)
06ff935e 736 *writable = !try_write(filename, NULL);
dc7c3d65 737 DBG(UTILS, ul_debug("%s: writable", filename));
1d0cd73f
KZ
738 return 1;
739 }
7c118af7 740 goto done;
1d0cd73f
KZ
741 }
742
743 /* try to create the file */
70bf97ae 744 if (writable) {
06ff935e 745 *writable = !try_write(filename, NULL);
dc7c3d65
KZ
746 if (*writable) {
747 DBG(UTILS, ul_debug("%s: writable", filename));
7c118af7 748 return 1;
dc7c3d65 749 }
1d0cd73f
KZ
750 }
751
7c118af7 752done:
83a78332 753 DBG(UTILS, ul_debug("%s: irregular/non-writable", filename));
1d0cd73f
KZ
754 return 0;
755}
7c118af7 756
77417bc0
KZ
757/*
758 * Don't export this to libmount API -- utab is private library stuff.
1d0cd73f 759 *
d58b3157 760 * If the file does not exist and @writable argument is not NULL, then it will
a362ae60 761 * try to create the directory (e.g. /run/mount) and the file.
1d0cd73f 762 *
a362ae60 763 * Returns: 1 if utab is a regular file, and 0 in case of
6f5788c5 764 * error (check errno for more details).
1d0cd73f 765 */
1d0cd73f
KZ
766int mnt_has_regular_utab(const char **utab, int *writable)
767{
768 struct stat st;
769 int rc;
770 const char *filename = utab && *utab ? *utab : mnt_get_utab_path();
771
7c118af7
KZ
772 if (writable)
773 *writable = 0;
1d0cd73f
KZ
774 if (utab && !*utab)
775 *utab = filename;
776
83a78332 777 DBG(UTILS, ul_debug("utab: %s", filename));
1d0cd73f
KZ
778
779 rc = lstat(filename, &st);
780
781 if (rc == 0) {
d58b3157 782 /* file exists */
1d0cd73f
KZ
783 if (S_ISREG(st.st_mode)) {
784 if (writable)
06ff935e 785 *writable = !try_write(filename, NULL);
1d0cd73f 786 return 1;
70bf97ae 787 }
d58b3157 788 goto done; /* it's not a regular file */
70bf97ae 789 }
1d0cd73f
KZ
790
791 if (writable) {
792 char *dirname = strdup(filename);
793
794 if (!dirname)
7c118af7 795 goto done;
1d0cd73f
KZ
796
797 stripoff_last_component(dirname); /* remove filename */
798
b0bb8fb6
KZ
799 rc = mkdir(dirname, S_IWUSR|
800 S_IRUSR|S_IRGRP|S_IROTH|
801 S_IXUSR|S_IXGRP|S_IXOTH);
06ff935e
KZ
802 if (rc && errno != EEXIST) {
803 free(dirname);
7c118af7 804 goto done; /* probably EACCES */
06ff935e 805 }
1d0cd73f 806
06ff935e
KZ
807 *writable = !try_write(filename, dirname);
808 free(dirname);
7c118af7
KZ
809 if (*writable)
810 return 1;
1d0cd73f 811 }
7c118af7 812done:
83a78332 813 DBG(UTILS, ul_debug("%s: irregular/non-writable file", filename));
1d0cd73f 814 return 0;
0532ba1d
KZ
815}
816
ce4dd666
KZ
817/**
818 * mnt_get_swaps_path:
819 *
820 * Returns: path to /proc/swaps or $LIBMOUNT_SWAPS.
821 */
822const char *mnt_get_swaps_path(void)
823{
824 const char *p = safe_getenv("LIBMOUNT_SWAPS");
825 return p ? : _PATH_PROC_SWAPS;
826}
827
3a5b1b1d
KZ
828/**
829 * mnt_get_fstab_path:
830 *
831 * Returns: path to /etc/fstab or $LIBMOUNT_FSTAB.
832 */
833const char *mnt_get_fstab_path(void)
834{
035507c8 835 const char *p = safe_getenv("LIBMOUNT_FSTAB");
3a5b1b1d
KZ
836 return p ? : _PATH_MNTTAB;
837}
838
839/**
840 * mnt_get_mtab_path:
841 *
d58b3157 842 * This function returns the *default* location of the mtab file. The result does
0f32f1e2 843 * not have to be writable. See also mnt_has_regular_mtab().
3a5b1b1d
KZ
844 *
845 * Returns: path to /etc/mtab or $LIBMOUNT_MTAB.
846 */
847const char *mnt_get_mtab_path(void)
848{
035507c8 849 const char *p = safe_getenv("LIBMOUNT_MTAB");
3a5b1b1d
KZ
850 return p ? : _PATH_MOUNTED;
851}
852
77417bc0
KZ
853/*
854 * Don't export this to libmount API -- utab is private library stuff.
be1a5180 855 *
a362ae60 856 * Returns: path to /run/mount/utab (or /dev/.mount/utab) or $LIBMOUNT_UTAB.
b37dd175
KZ
857 */
858const char *mnt_get_utab_path(void)
859{
a362ae60 860 struct stat st;
035507c8 861 const char *p = safe_getenv("LIBMOUNT_UTAB");
a362ae60
KZ
862
863 if (p)
864 return p;
865
866 if (stat(MNT_RUNTIME_TOPDIR, &st) == 0)
867 return MNT_PATH_UTAB;
868
869 return MNT_PATH_UTAB_OLD;
b37dd175
KZ
870}
871
d1be0c34 872
d58b3157 873/* returns file descriptor or -errno, @name returns a unique filename
b37dd175 874 */
4b6cf485 875int mnt_open_uniq_filename(const char *filename, char **name)
b37dd175
KZ
876{
877 int rc, fd;
878 char *n;
6fc81224 879 mode_t oldmode;
b37dd175 880
ba2bdf41
KZ
881 if (!filename)
882 return -EINVAL;
b37dd175
KZ
883 if (name)
884 *name = NULL;
885
886 rc = asprintf(&n, "%s.XXXXXX", filename);
887 if (rc <= 0)
888 return -errno;
889
d58b3157 890 /* This is for very old glibc and for compatibility with Posix, which says
6fc81224
KZ
891 * nothing about mkstemp() mode. All sane glibc use secure mode (0600).
892 */
51479069
KZ
893 oldmode = umask(S_IRGRP|S_IWGRP|S_IXGRP|
894 S_IROTH|S_IWOTH|S_IXOTH);
1eb8539d 895 fd = mkostemp(n, O_RDWR|O_CREAT|O_EXCL|O_CLOEXEC);
1dbaf5cc
NK
896 if (fd < 0)
897 fd = -errno;
51479069 898 umask(oldmode);
6fc81224 899
b37dd175
KZ
900 if (fd >= 0 && name)
901 *name = n;
902 else
903 free(n);
904
1dbaf5cc 905 return fd;
b37dd175 906}
0bb44be3 907
cd3d6c5b
DR
908/**
909 * mnt_get_mountpoint:
910 * @path: pathname
911 *
912 * This function finds the mountpoint that a given path resides in. @path
913 * should be canonicalized. The returned pointer should be freed by the caller.
914 *
cd41b385
KZ
915 * WARNING: the function compares st_dev of the @path elements. This traditional
916 * way maybe be insufficient on filesystems like Linux "overlay". See also
917 * mnt_table_find_target().
918 *
d58b3157 919 * Returns: allocated string with the target of the mounted device or NULL on error
cd3d6c5b 920 */
0bb44be3
KZ
921char *mnt_get_mountpoint(const char *path)
922{
4569bbea 923 char *mnt;
0bb44be3
KZ
924 struct stat st;
925 dev_t dir, base;
926
37290a53
KZ
927 if (!path)
928 return NULL;
4569bbea
KZ
929
930 mnt = strdup(path);
0bb44be3
KZ
931 if (!mnt)
932 return NULL;
933 if (*mnt == '/' && *(mnt + 1) == '\0')
9758c88a 934 goto done;
0bb44be3 935
6589a163 936 if (mnt_stat_mountpoint(mnt, &st))
0bb44be3
KZ
937 goto err;
938 base = st.st_dev;
939
940 do {
941 char *p = stripoff_last_component(mnt);
942
943 if (!p)
944 break;
6589a163 945 if (mnt_stat_mountpoint(*mnt ? mnt : "/", &st))
0bb44be3
KZ
946 goto err;
947 dir = st.st_dev;
948 if (dir != base) {
9d670a2a
KZ
949 if (p > mnt)
950 *(p - 1) = '/';
9758c88a 951 goto done;
0bb44be3
KZ
952 }
953 base = dir;
954 } while (mnt && *(mnt + 1) != '\0');
955
956 memcpy(mnt, "/", 2);
9758c88a 957done:
83a78332 958 DBG(UTILS, ul_debug("%s mountpoint is %s", path, mnt));
9758c88a 959 return mnt;
0bb44be3
KZ
960err:
961 free(mnt);
962 return NULL;
963}
964
f308ec19 965/*
9e930041 966 * Search for @name kernel command parameter.
f308ec19 967 *
d58b3157 968 * Returns newly allocated string with a parameter argument if the @name is
f308ec19 969 * specified as "name=" or returns pointer to @name or returns NULL if not
72a2a3f5 970 * found. If it is specified more than once, we grab the last copy.
f308ec19
KZ
971 *
972 * For example cmdline: "aaa bbb=BBB ccc"
973 *
974 * @name is "aaa" --returns--> "aaa" (pointer to @name)
975 * @name is "bbb=" --returns--> "BBB" (allocated)
976 * @name is "foo" --returns--> NULL
72a2a3f5
MF
977 *
978 * Note: It is not really feasible to parse the command line exactly the same
979 * as the kernel does since we don't know which options are valid. We can use
980 * the -- marker though and not walk past that.
f308ec19
KZ
981 */
982char *mnt_get_kernel_cmdline_option(const char *name)
983{
984 FILE *f;
985 size_t len;
986 int val = 0;
72a2a3f5 987 char *p, *res = NULL, *mem = NULL;
f308ec19
KZ
988 char buf[BUFSIZ]; /* see kernel include/asm-generic/setup.h: COMMAND_LINE_SIZE */
989 const char *path = _PATH_PROC_CMDLINE;
990
72a2a3f5 991 if (!name || !name[0])
ba2bdf41 992 return NULL;
f308ec19
KZ
993
994#ifdef TEST_PROGRAM
cd793967 995 path = getenv("LIBMOUNT_KERNEL_CMDLINE");
f308ec19
KZ
996 if (!path)
997 path = _PATH_PROC_CMDLINE;
998#endif
1eb8539d 999 f = fopen(path, "r" UL_CLOEXECSTR);
f308ec19
KZ
1000 if (!f)
1001 return NULL;
1002
1003 p = fgets(buf, sizeof(buf), f);
1004 fclose(f);
1005
1006 if (!p || !*p || *p == '\n')
1007 return NULL;
1008
72a2a3f5
MF
1009 p = strstr(p, " -- ");
1010 if (p) {
1011 /* no more kernel args after this */
1012 *p = '\0';
1013 } else {
1014 len = strlen(buf);
1015 buf[len - 1] = '\0'; /* remove last '\n' */
1016 }
f308ec19
KZ
1017
1018 len = strlen(name);
72a2a3f5 1019 if (name[len - 1] == '=')
f308ec19
KZ
1020 val = 1;
1021
72a2a3f5 1022 for (p = buf; p && *p; p++) {
f308ec19
KZ
1023 if (!(p = strstr(p, name)))
1024 break; /* not found the option */
1025 if (p != buf && !isblank(*(p - 1)))
1026 continue; /* no space before the option */
1027 if (!val && *(p + len) != '\0' && !isblank(*(p + len)))
d58b3157 1028 continue; /* no space after the option */
f308ec19
KZ
1029 if (val) {
1030 char *v = p + len;
72a2a3f5 1031 int end;
f308ec19
KZ
1032
1033 while (*p && !isblank(*p)) /* jump to the end of the argument */
1034 p++;
72a2a3f5 1035 end = (*p == '\0');
f308ec19 1036 *p = '\0';
72a2a3f5
MF
1037 free(mem);
1038 res = mem = strdup(v);
1039 if (end)
1040 break;
f308ec19
KZ
1041 } else
1042 res = (char *) name; /* option without '=' */
72a2a3f5 1043 /* don't break -- keep scanning for more options */
f308ec19
KZ
1044 }
1045
1046 return res;
1047}
1048
1dae161c
KZ
1049/**
1050 * mnt_guess_system_root:
1051 * @devno: device number or zero
1052 * @cache: paths cache or NULL
1053 * @path: returns allocated path
1054 *
40f00b4f
KZ
1055 * Converts @devno to the real device name if devno major number is greater
1056 * than zero, otherwise use root= kernel cmdline option to get device name.
1057 *
1058 * The function uses /sys to convert devno to device name.
1059 *
1060 * Returns: 0 = success, 1 = not found, <0 = error
1061 */
1062int mnt_guess_system_root(dev_t devno, struct libmnt_cache *cache, char **path)
1063{
1064 char buf[PATH_MAX];
ac943a66 1065 char *dev = NULL, *spec = NULL;
40f00b4f
KZ
1066 unsigned int x, y;
1067 int allocated = 0;
1068
1069 assert(path);
1070
1071 DBG(UTILS, ul_debug("guessing system root [devno %u:%u]", major(devno), minor(devno)));
1072
1073 /* The pseudo-fs, net-fs or btrfs devno is useless, otherwise it
1074 * usually matches with the source device, let's try to use it.
1075 */
1076 if (major(devno) > 0) {
1077 dev = sysfs_devno_to_devpath(devno, buf, sizeof(buf));
1078 if (dev) {
1079 DBG(UTILS, ul_debug(" devno converted to %s", dev));
1080 goto done;
1081 }
1082 }
1083
1084 /* Let's try to use root= kernel command line option
1085 */
1086 spec = mnt_get_kernel_cmdline_option("root=");
1087 if (!spec)
1088 goto done;
1089
1090 /* maj:min notation */
1091 if (sscanf(spec, "%u:%u", &x, &y) == 2) {
1092 dev = sysfs_devno_to_devpath(makedev(x, y), buf, sizeof(buf));
1093 if (dev) {
1094 DBG(UTILS, ul_debug(" root=%s converted to %s", spec, dev));
1095 goto done;
1096 }
1097
1098 /* hexhex notation */
1099 } else if (isxdigit_string(spec)) {
1100 char *end = NULL;
1101 uint32_t n;
1102
1103 errno = 0;
1104 n = strtoul(spec, &end, 16);
1105
1106 if (errno || spec == end || (end && *end))
1107 DBG(UTILS, ul_debug(" failed to parse root='%s'", spec));
1108 else {
1109 /* kernel new_decode_dev() */
1110 x = (n & 0xfff00) >> 8;
1111 y = (n & 0xff) | ((n >> 12) & 0xfff00);
1112 dev = sysfs_devno_to_devpath(makedev(x, y), buf, sizeof(buf));
1113 if (dev) {
1114 DBG(UTILS, ul_debug(" root=%s converted to %s", spec, dev));
1115 goto done;
1116 }
1117 }
1118
1119 /* devname or PARTUUID= etc. */
1120 } else {
1121 DBG(UTILS, ul_debug(" converting root='%s'", spec));
1122
1123 dev = mnt_resolve_spec(spec, cache);
1124 if (dev && !cache)
1125 allocated = 1;
1126 }
40f00b4f 1127done:
ac943a66 1128 free(spec);
40f00b4f
KZ
1129 if (dev) {
1130 *path = allocated ? dev : strdup(dev);
9e13b1c1 1131 if (!*path)
40f00b4f
KZ
1132 return -ENOMEM;
1133 return 0;
1134 }
1135
1136 return 1;
1137}
1138
1139
abc9c0f7 1140#ifdef TEST_PROGRAM
5fde1d9f 1141static int test_match_fstype(struct libmnt_test *ts, int argc, char *argv[])
abc9c0f7
KZ
1142{
1143 char *type = argv[1];
1144 char *pattern = argv[2];
1145
1146 printf("%s\n", mnt_match_fstype(type, pattern) ? "MATCH" : "NOT-MATCH");
1147 return 0;
1148}
1149
5fde1d9f 1150static int test_match_options(struct libmnt_test *ts, int argc, char *argv[])
abc9c0f7
KZ
1151{
1152 char *optstr = argv[1];
1153 char *pattern = argv[2];
1154
1155 printf("%s\n", mnt_match_options(optstr, pattern) ? "MATCH" : "NOT-MATCH");
1156 return 0;
1157}
1158
5fde1d9f 1159static int test_startswith(struct libmnt_test *ts, int argc, char *argv[])
b49103ed
KZ
1160{
1161 char *optstr = argv[1];
1162 char *pattern = argv[2];
1163
1164 printf("%s\n", startswith(optstr, pattern) ? "YES" : "NOT");
1165 return 0;
1166}
1167
5fde1d9f 1168static int test_endswith(struct libmnt_test *ts, int argc, char *argv[])
b49103ed
KZ
1169{
1170 char *optstr = argv[1];
1171 char *pattern = argv[2];
1172
1173 printf("%s\n", endswith(optstr, pattern) ? "YES" : "NOT");
1174 return 0;
1175}
1176
5fde1d9f 1177static int test_appendstr(struct libmnt_test *ts, int argc, char *argv[])
40b27864
KZ
1178{
1179 char *str = strdup(argv[1]);
1180 const char *ap = argv[2];
1181
1182 append_string(&str, ap);
1183 printf("new string: '%s'\n", str);
1184
1185 free(str);
1186 return 0;
1187}
1188
5fde1d9f 1189static int test_mountpoint(struct libmnt_test *ts, int argc, char *argv[])
0bb44be3
KZ
1190{
1191 char *path = canonicalize_path(argv[1]),
1192 *mnt = path ? mnt_get_mountpoint(path) : NULL;
1193
1194 printf("%s: %s\n", argv[1], mnt ? : "unknown");
1195 free(mnt);
1196 free(path);
1197 return 0;
1198}
1199
5fde1d9f 1200static int test_filesystems(struct libmnt_test *ts, int argc, char *argv[])
97e23b5e
KZ
1201{
1202 char **filesystems = NULL;
1203 int rc;
1204
1205 rc = mnt_get_filesystems(&filesystems, argc ? argv[1] : NULL);
1206 if (!rc) {
1207 char **p;
1208 for (p = filesystems; *p; p++)
1209 printf("%s\n", *p);
1210 mnt_free_filesystems(filesystems);
1211 }
1212 return rc;
1213}
1214
5fde1d9f 1215static int test_chdir(struct libmnt_test *ts, int argc, char *argv[])
66bb8267
KZ
1216{
1217 int rc;
1218 char *path = canonicalize_path(argv[1]),
1219 *last = NULL;
1220
1221 if (!path)
1222 return -errno;
1223
1224 rc = mnt_chdir_to_parent(path, &last);
1225 if (!rc) {
1226 printf("path='%s', abs='%s', last='%s'\n",
1227 argv[1], path, last);
1228 }
1229 free(path);
1230 free(last);
1231 return rc;
1232}
1233
5fde1d9f 1234static int test_kernel_cmdline(struct libmnt_test *ts, int argc, char *argv[])
f308ec19
KZ
1235{
1236 char *name = argv[1];
1237 char *res;
1238
1239 res = mnt_get_kernel_cmdline_option(name);
1240 if (!res)
1241 printf("'%s' not found\n", name);
1242 else if (res == name)
1243 printf("'%s' found\n", name);
1244 else {
1245 printf("'%s' found, argument: '%s'\n", name, res);
1246 free(res);
1247 }
1248
1249 return 0;
1250}
1251
40f00b4f
KZ
1252
1253static int test_guess_root(struct libmnt_test *ts, int argc, char *argv[])
1254{
1255 int rc;
1256 char *real;
1257 dev_t devno = 0;
1258
1259 if (argc) {
1260 unsigned int x, y;
1261
1262 if (sscanf(argv[1], "%u:%u", &x, &y) != 2)
1263 return -EINVAL;
1264 devno = makedev(x, y);
1265 }
1266
1267 rc = mnt_guess_system_root(devno, NULL, &real);
1268 if (rc < 0)
1269 return rc;
1270 if (rc == 1)
1271 fputs("not found\n", stdout);
1272 else {
1273 printf("%s\n", real);
1274 free(real);
1275 }
1276 return 0;
1277}
1278
5fde1d9f 1279static int test_mkdir(struct libmnt_test *ts, int argc, char *argv[])
fd73f468
KZ
1280{
1281 int rc;
1282
1283 rc = mkdir_p(argv[1], S_IRWXU |
1284 S_IRGRP | S_IXGRP |
1285 S_IROTH | S_IXOTH);
1286 if (rc)
1287 printf("mkdir %s failed\n", argv[1]);
1288 return rc;
1289}
1290
5fde1d9f 1291static int test_statfs_type(struct libmnt_test *ts, int argc, char *argv[])
6a52473e
KZ
1292{
1293 struct statfs vfs;
1294 int rc;
1295
1296 rc = statfs(argv[1], &vfs);
1297 if (rc)
1298 printf("%s: statfs failed: %m\n", argv[1]);
1299 else
1300 printf("%-30s: statfs type: %-12s [0x%lx]\n", argv[1],
1301 mnt_statfs_get_fstype(&vfs),
1302 (long) vfs.f_type);
1303 return rc;
1304}
1305
66bb8267 1306
abc9c0f7
KZ
1307int main(int argc, char *argv[])
1308{
68164f6c 1309 struct libmnt_test tss[] = {
abc9c0f7
KZ
1310 { "--match-fstype", test_match_fstype, "<type> <pattern> FS types matching" },
1311 { "--match-options", test_match_options, "<options> <pattern> options matching" },
97e23b5e 1312 { "--filesystems", test_filesystems, "[<pattern>] list /{etc,proc}/filesystems" },
b49103ed
KZ
1313 { "--starts-with", test_startswith, "<string> <prefix>" },
1314 { "--ends-with", test_endswith, "<string> <prefix>" },
40b27864 1315 { "--append-string", test_appendstr, "<string> <appendix>" },
0bb44be3 1316 { "--mountpoint", test_mountpoint, "<path>" },
66bb8267 1317 { "--cd-parent", test_chdir, "<path>" },
f308ec19 1318 { "--kernel-cmdline",test_kernel_cmdline, "<option> | <option>=" },
40f00b4f 1319 { "--guess-root", test_guess_root, "[<maj:min>]" },
fd73f468 1320 { "--mkdir", test_mkdir, "<path>" },
6a52473e 1321 { "--statfs-type", test_statfs_type, "<path>" },
fd73f468 1322
abc9c0f7
KZ
1323 { NULL }
1324 };
1325
1326 return mnt_run_test(tss, argc, argv);
1327}
1328
1329#endif /* TEST_PROGRAM */