]>
git.ipfire.org Git - thirdparty/util-linux.git/blob - libmount/src/utils.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 * This file is part of libmount from util-linux project.
5 * Copyright (C) 2008-2018 Karel Zak <kzak@redhat.com>
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.
16 * @short_description: misc utils.
25 #include "pathnames.h"
28 #include "canonicalize.h"
31 #include "fileutils.h"
32 #include "statfs_magic.h"
36 * Return 1 if the file is not accessible or empty
38 int is_file_empty(const char *name
)
43 return (stat(name
, &st
) != 0 || st
.st_size
== 0);
46 int mnt_valid_tagname(const char *tagname
)
48 if (tagname
&& *tagname
&& (
49 strcmp("ID", tagname
) == 0 ||
50 strcmp("UUID", tagname
) == 0 ||
51 strcmp("LABEL", tagname
) == 0 ||
52 strcmp("PARTUUID", tagname
) == 0 ||
53 strcmp("PARTLABEL", tagname
) == 0))
61 * @tag: NAME=value string
63 * Returns: 1 if the @tag is parsable and tag NAME= is supported by libmount, or 0.
65 int mnt_tag_is_valid(const char *tag
)
68 int rc
= tag
&& blkid_parse_tag_string(tag
, &t
, NULL
) == 0
69 && mnt_valid_tagname(t
);
75 int mnt_parse_offset(const char *str
, size_t len
, uintmax_t *res
)
83 p
= strndup(str
, len
);
87 if (strtosize(p
, res
))
93 /* used as a callback by bsearch in mnt_fstype_is_pseudofs() */
94 static int fstype_cmp(const void *v1
, const void *v2
)
96 const char *s1
= *(char * const *)v1
;
97 const char *s2
= *(char * const *)v2
;
99 return strcmp(s1
, s2
);
102 int mnt_stat_mountpoint(const char *target
, struct stat
*st
)
104 #ifdef AT_NO_AUTOMOUNT
105 return fstatat(AT_FDCWD
, target
, st
, AT_NO_AUTOMOUNT
);
107 return stat(target
, st
);
111 int mnt_lstat_mountpoint(const char *target
, struct stat
*st
)
113 #ifdef AT_NO_AUTOMOUNT
114 return fstatat(AT_FDCWD
, target
, st
, AT_NO_AUTOMOUNT
| AT_SYMLINK_NOFOLLOW
);
116 return lstat(target
, st
);
122 * Note that the @target has to be an absolute path (so at least "/"). The
123 * @filename returns an allocated buffer with the last path component, for example:
125 * mnt_chdir_to_parent("/mnt/test", &buf) ==> chdir("/mnt"), buf="test"
127 int mnt_chdir_to_parent(const char *target
, char **filename
)
129 char *buf
, *parent
, *last
= NULL
;
133 if (!target
|| *target
!= '/')
136 DBG(UTILS
, ul_debug("moving to %s parent", target
));
138 buf
= strdup(target
);
142 if (*(buf
+ 1) != '\0') {
143 last
= stripoff_last_component(buf
);
148 parent
= buf
&& *buf
? buf
: "/";
150 if (chdir(parent
) == -1) {
151 DBG(UTILS
, ul_debug("failed to chdir to %s: %m", parent
));
155 if (!getcwd(cwd
, sizeof(cwd
))) {
156 DBG(UTILS
, ul_debug("failed to obtain current directory: %m"));
160 if (strcmp(cwd
, parent
) != 0) {
162 "unexpected chdir (expected=%s, cwd=%s)", parent
, cwd
));
167 "current directory moved to %s [last_component='%s']",
174 memcpy(*filename
, ".", 2);
176 memmove(*filename
, last
, strlen(last
) + 1);
186 * Check if @path is on a read-only filesystem independently of file permissions.
188 int mnt_is_readonly(const char *path
)
190 if (access(path
, W_OK
) == 0)
197 #ifdef HAVE_UTIMENSAT
199 * access(2) returns EACCES on read-only FS:
201 * - for set-uid application if one component of the path is not
202 * accessible for the current rUID. (Note that euidaccess(2) does not
203 * check for EROFS at all).
205 * - for a read-write filesystem with a read-only VFS node (aka -o remount,ro,bind)
208 struct timespec times
[2];
210 DBG(UTILS
, ul_debug(" doing utimensat() based write test"));
212 times
[0].tv_nsec
= UTIME_NOW
; /* atime */
213 times
[1].tv_nsec
= UTIME_OMIT
; /* mtime */
215 if (utimensat(AT_FDCWD
, path
, times
, 0) == -1)
216 return errno
== EROFS
;
226 * Encode @str to be compatible with fstab/mtab
228 * Returns: newly allocated string or NULL in case of error.
230 char *mnt_mangle(const char *str
)
239 * Decode @str from fstab/mtab
241 * Returns: newly allocated string or NULL in case of error.
243 char *mnt_unmangle(const char *str
)
245 return unmangle(str
, NULL
);
249 * mnt_fstype_is_pseudofs:
250 * @type: filesystem name
252 * Returns: 1 for filesystems like proc, sysfs, ... or 0.
254 int mnt_fstype_is_pseudofs(const char *type
)
256 /* This array must remain sorted when adding new fstypes */
257 static const char *pseudofs
[] = {
277 "fuse", /* Fallback name of fuse used by many poorly written drivers. */
278 "fuse.archivemount", /* Not a true pseudofs (has source), but source is not reported. */
279 "fuse.avfsd", /* Not a true pseudofs (has source), but source is not reported. */
280 "fuse.dumpfs", /* In fact, it is a netfs, but source is not reported. */
281 "fuse.encfs", /* Not a true pseudofs (has source), but source is not reported. */
282 "fuse.gvfs-fuse-daemon", /* Old name, not used by gvfs any more. */
286 "fuse.vmware-vmblock",
317 return !(bsearch(&type
, pseudofs
, ARRAY_SIZE(pseudofs
),
318 sizeof(char*), fstype_cmp
) == NULL
);
322 * mnt_fstype_is_netfs:
323 * @type: filesystem name
325 * Returns: 1 for filesystems like cifs, nfs, ... or 0.
327 int mnt_fstype_is_netfs(const char *type
)
329 if (strcmp(type
, "cifs") == 0 ||
330 strcmp(type
, "smb3") == 0 ||
331 strcmp(type
, "smbfs") == 0 ||
332 strncmp(type
,"nfs", 3) == 0 ||
333 strcmp(type
, "afs") == 0 ||
334 strcmp(type
, "ncpfs") == 0 ||
335 strcmp(type
, "glusterfs") == 0 ||
336 strcmp(type
, "fuse.curlftpfs") == 0 ||
337 strcmp(type
, "fuse.sshfs") == 0 ||
338 strncmp(type
,"9p", 2) == 0)
343 const char *mnt_statfs_get_fstype(struct statfs
*vfs
)
347 switch (vfs
->f_type
) {
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";
430 * @type: filesystem type
431 * @pattern: filesystem name or comma delimited list of names
433 * The @pattern list of filesystems can be prefixed with a global
434 * "no" prefix to invert matching of the whole list. The "no" could
435 * also be used for individual items in the @pattern list. So,
436 * "nofoo,bar" has the same meaning as "nofoo,nobar".
438 * "bar" : "nofoo,bar" -> False (global "no" prefix)
440 * "bar" : "foo,bar" -> True
442 * "bar" : "foo,nobar" -> False
444 * Returns: 1 if type is matching, else 0. This function also returns
445 * 0 if @pattern is NULL and @type is non-NULL.
447 int mnt_match_fstype(const char *type
, const char *pattern
)
449 return match_fstype(type
, pattern
);
452 void mnt_free_filesystems(char **filesystems
)
458 for (p
= filesystems
; *p
; p
++)
463 static int add_filesystem(char ***filesystems
, char *name
)
472 for (n
= 0, p
= *filesystems
; *p
; p
++, n
++) {
473 if (strcmp(*p
, name
) == 0)
480 if (n
== 0 || !((n
+ 1) % MYCHUNK
)) {
481 size_t items
= ((n
+ 1 + MYCHUNK
) / MYCHUNK
) * MYCHUNK
;
482 char **x
= realloc(*filesystems
, items
* sizeof(char *));
489 (*filesystems
)[n
] = name
;
490 (*filesystems
)[n
+ 1] = NULL
;
495 mnt_free_filesystems(*filesystems
);
499 static int get_filesystems(const char *filename
, char ***filesystems
, const char *pattern
)
505 f
= fopen(filename
, "r" UL_CLOEXECSTR
);
509 DBG(UTILS
, ul_debug("reading filesystems list from: %s", filename
));
511 while (fgets(line
, sizeof(line
), f
)) {
512 char name
[sizeof(line
)];
514 if (*line
== '#' || strncmp(line
, "nodev", 5) == 0)
516 if (sscanf(line
, " %128[^\n ]\n", name
) != 1)
518 if (strcmp(name
, "*") == 0) {
520 break; /* end of the /etc/filesystems */
522 if (pattern
&& !mnt_match_fstype(name
, pattern
))
524 rc
= add_filesystem(filesystems
, name
);
534 * Always check the @filesystems pointer!
538 * ...mount will try to read the file /etc/filesystems, or, if that does not
539 * exist, /proc/filesystems. All of the filesystem types listed there will
540 * be tried, except for those that are labeled "nodev" (e.g., devpts,
541 * proc and nfs). If /etc/filesystems ends in a line with a single * only,
542 * mount will read /proc/filesystems afterwards.
544 int mnt_get_filesystems(char ***filesystems
, const char *pattern
)
553 rc
= get_filesystems(_PATH_FILESYSTEMS
, filesystems
, pattern
);
557 rc
= get_filesystems(_PATH_PROC_FILESYSTEMS
, filesystems
, pattern
);
558 if (rc
== 1 && *filesystems
)
559 rc
= 0; /* /proc/filesystems not found */
565 * Returns an allocated string with username or NULL.
567 char *mnt_get_username(const uid_t uid
)
571 char *buf
, *username
= NULL
;
573 buf
= malloc(UL_GETPW_BUFSIZ
);
577 if (!getpwuid_r(uid
, &pwd
, buf
, UL_GETPW_BUFSIZ
, &res
) && res
)
578 username
= strdup(pwd
.pw_name
);
584 int mnt_get_uid(const char *username
, uid_t
*uid
)
591 if (!username
|| !uid
)
594 buf
= malloc(UL_GETPW_BUFSIZ
);
598 if (!getpwnam_r(username
, &pwd
, buf
, UL_GETPW_BUFSIZ
, &pw
) && pw
) {
603 "cannot convert '%s' username to UID", username
));
604 rc
= errno
? -errno
: -EINVAL
;
611 int mnt_get_gid(const char *groupname
, gid_t
*gid
)
618 if (!groupname
|| !gid
)
621 buf
= malloc(UL_GETPW_BUFSIZ
);
625 if (!getgrnam_r(groupname
, &grp
, buf
, UL_GETPW_BUFSIZ
, &gr
) && gr
) {
630 "cannot convert '%s' groupname to GID", groupname
));
631 rc
= errno
? -errno
: -EINVAL
;
638 int mnt_in_group(gid_t gid
)
646 n
= getgroups(0, NULL
);
650 grps
= malloc(n
* sizeof(*grps
));
654 if (getgroups(n
, grps
) == n
) {
655 for (i
= 0; i
< n
; i
++) {
656 if (grps
[i
] == gid
) {
667 static int try_write(const char *filename
, const char *directory
)
674 DBG(UTILS
, ul_debug("try write %s dir: %s", filename
, directory
));
677 /* Try eaccess() first, because open() is overkill, may be monitored by
678 * audit and we don't want to fill logs by our checks...
680 if (eaccess(filename
, R_OK
|W_OK
) == 0) {
681 DBG(UTILS
, ul_debug(" access OK"));
685 if (errno
!= ENOENT
) {
686 DBG(UTILS
, ul_debug(" access FAILED"));
691 /* file does not exist; try if directory is writable */
692 if (eaccess(directory
, R_OK
|W_OK
) != 0)
695 DBG(UTILS
, ul_debug(" access %s [%s]", rc
? "FAILED" : "OK", directory
));
700 DBG(UTILS
, ul_debug(" doing open-write test"));
702 int fd
= open(filename
, O_RDWR
|O_CREAT
|O_CLOEXEC
,
703 S_IWUSR
|S_IRUSR
|S_IRGRP
|S_IROTH
);
713 * mnt_has_regular_mtab:
714 * @mtab: returns path to mtab
715 * @writable: returns 1 if the file is writable
717 * If the file does not exist and @writable argument is not NULL, then it will
718 * try to create the file.
720 * Returns: 1 if /etc/mtab is a regular file, and 0 in case of error (check
721 * errno for more details).
723 int mnt_has_regular_mtab(const char **mtab
, int *writable
)
727 const char *filename
= mtab
&& *mtab
? *mtab
: mnt_get_mtab_path();
734 DBG(UTILS
, ul_debug("mtab: %s", filename
));
736 rc
= lstat(filename
, &st
);
740 if (S_ISREG(st
.st_mode
)) {
742 *writable
= !try_write(filename
, NULL
);
743 DBG(UTILS
, ul_debug("%s: writable", filename
));
749 /* try to create the file */
751 *writable
= !try_write(filename
, NULL
);
753 DBG(UTILS
, ul_debug("%s: writable", filename
));
759 DBG(UTILS
, ul_debug("%s: irregular/non-writable", filename
));
764 * Don't export this to libmount API -- utab is private library stuff.
766 * If the file does not exist and @writable argument is not NULL, then it will
767 * try to create the directory (e.g. /run/mount) and the file.
769 * Returns: 1 if utab is a regular file, and 0 in case of
770 * error (check errno for more details).
772 int mnt_has_regular_utab(const char **utab
, int *writable
)
776 const char *filename
= utab
&& *utab
? *utab
: mnt_get_utab_path();
783 DBG(UTILS
, ul_debug("utab: %s", filename
));
785 rc
= lstat(filename
, &st
);
789 if (S_ISREG(st
.st_mode
)) {
791 *writable
= !try_write(filename
, NULL
);
794 goto done
; /* it's not a regular file */
798 char *dirname
= strdup(filename
);
803 stripoff_last_component(dirname
); /* remove filename */
805 rc
= mkdir(dirname
, S_IWUSR
|
806 S_IRUSR
|S_IRGRP
|S_IROTH
|
807 S_IXUSR
|S_IXGRP
|S_IXOTH
);
808 if (rc
&& errno
!= EEXIST
) {
810 goto done
; /* probably EACCES */
813 *writable
= !try_write(filename
, dirname
);
819 DBG(UTILS
, ul_debug("%s: irregular/non-writable file", filename
));
824 * mnt_get_swaps_path:
826 * Returns: path to /proc/swaps or $LIBMOUNT_SWAPS.
828 const char *mnt_get_swaps_path(void)
830 const char *p
= safe_getenv("LIBMOUNT_SWAPS");
831 return p
? : _PATH_PROC_SWAPS
;
835 * mnt_get_fstab_path:
837 * Returns: path to /etc/fstab or $LIBMOUNT_FSTAB.
839 const char *mnt_get_fstab_path(void)
841 const char *p
= safe_getenv("LIBMOUNT_FSTAB");
842 return p
? : _PATH_MNTTAB
;
848 * This function returns the *default* location of the mtab file. The result does
849 * not have to be writable. See also mnt_has_regular_mtab().
851 * Returns: path to /etc/mtab or $LIBMOUNT_MTAB.
853 const char *mnt_get_mtab_path(void)
855 const char *p
= safe_getenv("LIBMOUNT_MTAB");
856 return p
? : _PATH_MOUNTED
;
860 * Don't export this to libmount API -- utab is private library stuff.
862 * Returns: path to /run/mount/utab or $LIBMOUNT_UTAB.
864 const char *mnt_get_utab_path(void)
866 const char *p
= safe_getenv("LIBMOUNT_UTAB");
867 return p
? : MNT_PATH_UTAB
;
871 /* returns file descriptor or -errno, @name returns a unique filename
873 int mnt_open_uniq_filename(const char *filename
, char **name
)
884 rc
= asprintf(&n
, "%s.XXXXXX", filename
);
888 /* This is for very old glibc and for compatibility with Posix, which says
889 * nothing about mkstemp() mode. All sane glibc use secure mode (0600).
891 oldmode
= umask(S_IRGRP
|S_IWGRP
|S_IXGRP
|
892 S_IROTH
|S_IWOTH
|S_IXOTH
);
894 fd
= mkstemp_cloexec(n
);
908 * mnt_get_mountpoint:
911 * This function finds the mountpoint that a given path resides in. @path
912 * should be canonicalized. The returned pointer should be freed by the caller.
914 * WARNING: the function compares st_dev of the @path elements. This traditional
915 * way may be insufficient on filesystems like Linux "overlay". See also
916 * mnt_table_find_target().
918 * Returns: allocated string with the target of the mounted device or NULL on error
920 char *mnt_get_mountpoint(const char *path
)
932 if (*mnt
== '/' && *(mnt
+ 1) == '\0')
935 if (mnt_stat_mountpoint(mnt
, &st
))
940 char *p
= stripoff_last_component(mnt
);
944 if (mnt_stat_mountpoint(*mnt
? mnt
: "/", &st
))
953 } while (mnt
&& *(mnt
+ 1) != '\0');
957 DBG(UTILS
, ul_debug("%s mountpoint is %s", path
, mnt
));
965 * Search for @name kernel command parameter.
967 * Returns newly allocated string with a parameter argument if the @name is
968 * specified as "name=" or returns pointer to @name or returns NULL if not
969 * found. If it is specified more than once, we grab the last copy.
971 * For example cmdline: "aaa bbb=BBB ccc"
973 * @name is "aaa" --returns--> "aaa" (pointer to @name)
974 * @name is "bbb=" --returns--> "BBB" (allocated)
975 * @name is "foo" --returns--> NULL
977 * Note: It is not really feasible to parse the command line exactly the same
978 * as the kernel does since we don't know which options are valid. We can use
979 * the -- marker though and not walk past that.
981 char *mnt_get_kernel_cmdline_option(const char *name
)
986 char *p
, *res
= NULL
, *mem
= NULL
;
987 char buf
[BUFSIZ
]; /* see kernel include/asm-generic/setup.h: COMMAND_LINE_SIZE */
988 const char *path
= _PATH_PROC_CMDLINE
;
990 if (!name
|| !name
[0])
994 path
= getenv("LIBMOUNT_KERNEL_CMDLINE");
996 path
= _PATH_PROC_CMDLINE
;
998 f
= fopen(path
, "r" UL_CLOEXECSTR
);
1002 p
= fgets(buf
, sizeof(buf
), f
);
1005 if (!p
|| !*p
|| *p
== '\n')
1008 p
= strstr(p
, " -- ");
1010 /* no more kernel args after this */
1014 buf
[len
- 1] = '\0'; /* remove last '\n' */
1018 if (name
[len
- 1] == '=')
1021 for (p
= buf
; p
&& *p
; p
++) {
1022 if (!(p
= strstr(p
, name
)))
1023 break; /* not found the option */
1024 if (p
!= buf
&& !isblank(*(p
- 1)))
1025 continue; /* no space before the option */
1026 if (!val
&& *(p
+ len
) != '\0' && !isblank(*(p
+ len
)))
1027 continue; /* no space after the option */
1032 while (*p
&& !isblank(*p
)) /* jump to the end of the argument */
1037 res
= mem
= strdup(v
);
1041 res
= (char *) name
; /* option without '=' */
1042 /* don't break -- keep scanning for more options */
1049 * mnt_guess_system_root:
1050 * @devno: device number or zero
1051 * @cache: paths cache or NULL
1052 * @path: returns allocated path
1054 * Converts @devno to the real device name if devno major number is greater
1055 * than zero, otherwise use root= kernel cmdline option to get device name.
1057 * The function uses /sys to convert devno to device name.
1059 * Returns: 0 = success, 1 = not found, <0 = error
1063 int mnt_guess_system_root(dev_t devno
, struct libmnt_cache
*cache
, char **path
)
1066 char *dev
= NULL
, *spec
= NULL
;
1072 DBG(UTILS
, ul_debug("guessing system root [devno %u:%u]", major(devno
), minor(devno
)));
1074 /* The pseudo-fs, net-fs or btrfs devno is useless, otherwise it
1075 * usually matches with the source device, let's try to use it.
1077 if (major(devno
) > 0) {
1078 dev
= sysfs_devno_to_devpath(devno
, buf
, sizeof(buf
));
1080 DBG(UTILS
, ul_debug(" devno converted to %s", dev
));
1085 /* Let's try to use root= kernel command line option
1087 spec
= mnt_get_kernel_cmdline_option("root=");
1091 /* maj:min notation */
1092 if (sscanf(spec
, "%u:%u", &x
, &y
) == 2) {
1093 dev
= sysfs_devno_to_devpath(makedev(x
, y
), buf
, sizeof(buf
));
1095 DBG(UTILS
, ul_debug(" root=%s converted to %s", spec
, dev
));
1099 /* hexhex notation */
1100 } else if (isxdigit_string(spec
)) {
1105 n
= strtoul(spec
, &end
, 16);
1107 if (errno
|| spec
== end
|| (end
&& *end
))
1108 DBG(UTILS
, ul_debug(" failed to parse root='%s'", spec
));
1110 /* kernel new_decode_dev() */
1111 x
= (n
& 0xfff00) >> 8;
1112 y
= (n
& 0xff) | ((n
>> 12) & 0xfff00);
1113 dev
= sysfs_devno_to_devpath(makedev(x
, y
), buf
, sizeof(buf
));
1115 DBG(UTILS
, ul_debug(" root=%s converted to %s", spec
, dev
));
1120 /* devname or PARTUUID= etc. */
1122 DBG(UTILS
, ul_debug(" converting root='%s'", spec
));
1124 dev
= mnt_resolve_spec(spec
, cache
);
1131 *path
= allocated
? dev
: strdup(dev
);
1141 * Initialize MNT_PATH_TMPTGT; mkdir, create a new namespace and
1142 * mark (bind mount) the directory as private.
1144 int mnt_tmptgt_unshare(int *old_ns_fd
)
1146 #ifdef USE_LIBMOUNT_SUPPORT_NAMESPACES
1147 int rc
= 0, fd
= -1;
1153 /* remember the current namespace */
1154 fd
= open("/proc/self/ns/mnt", O_RDONLY
| O_CLOEXEC
);
1158 /* create new namespace */
1159 if (unshare(CLONE_NEWNS
) != 0)
1162 /* create directory */
1163 rc
= ul_mkdir_p(MNT_PATH_TMPTGT
, S_IRWXU
);
1167 /* try to set top-level directory as private, this is possible if
1168 * MNT_RUNTIME_TOPDIR (/run) is a separated filesystem. */
1169 if (mount("none", MNT_RUNTIME_TOPDIR
, NULL
, MS_PRIVATE
, NULL
) != 0) {
1171 /* failed; create a mountpoint from MNT_PATH_TMPTGT */
1172 if (mount(MNT_PATH_TMPTGT
, MNT_PATH_TMPTGT
, "none", MS_BIND
, NULL
) != 0)
1174 if (mount("none", MNT_PATH_TMPTGT
, NULL
, MS_PRIVATE
, NULL
) != 0)
1178 DBG(UTILS
, ul_debug(MNT_PATH_TMPTGT
" unshared"));
1183 rc
= errno
? -errno
: -EINVAL
;
1185 mnt_tmptgt_cleanup(fd
);
1186 DBG(UTILS
, ul_debug(MNT_PATH_TMPTGT
" unshare failed"));
1194 * Clean up MNT_PATH_TMPTGT; umount and switch back to old namespace
1196 int mnt_tmptgt_cleanup(int old_ns_fd
)
1198 #ifdef USE_LIBMOUNT_SUPPORT_NAMESPACES
1199 umount(MNT_PATH_TMPTGT
);
1201 if (old_ns_fd
>= 0) {
1202 setns(old_ns_fd
, CLONE_NEWNS
);
1206 DBG(UTILS
, ul_debug(MNT_PATH_TMPTGT
" cleanup done"));
1214 static int test_match_fstype(struct libmnt_test
*ts
, int argc
, char *argv
[])
1216 char *type
= argv
[1];
1217 char *pattern
= argv
[2];
1219 printf("%s\n", mnt_match_fstype(type
, pattern
) ? "MATCH" : "NOT-MATCH");
1223 static int test_match_options(struct libmnt_test
*ts
, int argc
, char *argv
[])
1225 char *optstr
= argv
[1];
1226 char *pattern
= argv
[2];
1228 printf("%s\n", mnt_match_options(optstr
, pattern
) ? "MATCH" : "NOT-MATCH");
1232 static int test_startswith(struct libmnt_test
*ts
, int argc
, char *argv
[])
1234 char *optstr
= argv
[1];
1235 char *pattern
= argv
[2];
1237 printf("%s\n", startswith(optstr
, pattern
) ? "YES" : "NOT");
1241 static int test_endswith(struct libmnt_test
*ts
, int argc
, char *argv
[])
1243 char *optstr
= argv
[1];
1244 char *pattern
= argv
[2];
1246 printf("%s\n", endswith(optstr
, pattern
) ? "YES" : "NOT");
1250 static int test_mountpoint(struct libmnt_test
*ts
, int argc
, char *argv
[])
1252 char *path
= canonicalize_path(argv
[1]),
1253 *mnt
= path
? mnt_get_mountpoint(path
) : NULL
;
1255 printf("%s: %s\n", argv
[1], mnt
? : "unknown");
1261 static int test_filesystems(struct libmnt_test
*ts
, int argc
, char *argv
[])
1263 char **filesystems
= NULL
;
1266 rc
= mnt_get_filesystems(&filesystems
, argc
? argv
[1] : NULL
);
1269 for (p
= filesystems
; *p
; p
++)
1271 mnt_free_filesystems(filesystems
);
1276 static int test_chdir(struct libmnt_test
*ts
, int argc
, char *argv
[])
1279 char *path
= canonicalize_path(argv
[1]),
1285 rc
= mnt_chdir_to_parent(path
, &last
);
1287 printf("path='%s', abs='%s', last='%s'\n",
1288 argv
[1], path
, last
);
1295 static int test_kernel_cmdline(struct libmnt_test
*ts
, int argc
, char *argv
[])
1297 char *name
= argv
[1];
1300 res
= mnt_get_kernel_cmdline_option(name
);
1302 printf("'%s' not found\n", name
);
1303 else if (res
== name
)
1304 printf("'%s' found\n", name
);
1306 printf("'%s' found, argument: '%s'\n", name
, res
);
1314 static int test_guess_root(struct libmnt_test
*ts
, int argc
, char *argv
[])
1323 if (sscanf(argv
[1], "%u:%u", &x
, &y
) != 2)
1325 devno
= makedev(x
, y
);
1328 rc
= mnt_guess_system_root(devno
, NULL
, &real
);
1332 fputs("not found\n", stdout
);
1334 printf("%s\n", real
);
1340 static int test_mkdir(struct libmnt_test
*ts
, int argc
, char *argv
[])
1344 rc
= ul_mkdir_p(argv
[1], S_IRWXU
|
1348 printf("mkdir %s failed\n", argv
[1]);
1352 static int test_statfs_type(struct libmnt_test
*ts
, int argc
, char *argv
[])
1357 rc
= statfs(argv
[1], &vfs
);
1359 printf("%s: statfs failed: %m\n", argv
[1]);
1361 printf("%-30s: statfs type: %-12s [0x%lx]\n", argv
[1],
1362 mnt_statfs_get_fstype(&vfs
),
1368 int main(int argc
, char *argv
[])
1370 struct libmnt_test tss
[] = {
1371 { "--match-fstype", test_match_fstype
, "<type> <pattern> FS types matching" },
1372 { "--match-options", test_match_options
, "<options> <pattern> options matching" },
1373 { "--filesystems", test_filesystems
, "[<pattern>] list /{etc,proc}/filesystems" },
1374 { "--starts-with", test_startswith
, "<string> <prefix>" },
1375 { "--ends-with", test_endswith
, "<string> <prefix>" },
1376 { "--mountpoint", test_mountpoint
, "<path>" },
1377 { "--cd-parent", test_chdir
, "<path>" },
1378 { "--kernel-cmdline",test_kernel_cmdline
, "<option> | <option>=" },
1379 { "--guess-root", test_guess_root
, "[<maj:min>]" },
1380 { "--mkdir", test_mkdir
, "<path>" },
1381 { "--statfs-type", test_statfs_type
, "<path>" },
1386 return mnt_run_test(tss
, argc
, argv
);
1389 #endif /* TEST_PROGRAM */