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