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