]>
git.ipfire.org Git - thirdparty/util-linux.git/blob - libmount/src/utils.c
5b30f2ac0756e58a11093f71362da3aaa4c50fe1
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.
26 #include "pathnames.h"
29 #include "canonicalize.h"
32 #include "fileutils.h"
33 #include "statfs_magic.h"
36 int append_string(char **a
, const char *b
)
47 return !*a
? -ENOMEM
: 0;
53 tmp
= realloc(*a
, al
+ bl
+ 1);
57 memcpy((*a
) + al
, b
, bl
+ 1);
62 * Return 1 if the file is not accessible or empty
64 int is_file_empty(const char *name
)
69 return (stat(name
, &st
) != 0 || st
.st_size
== 0);
72 int mnt_valid_tagname(const char *tagname
)
74 if (tagname
&& *tagname
&& (
75 strcmp("UUID", tagname
) == 0 ||
76 strcmp("LABEL", tagname
) == 0 ||
77 strcmp("PARTUUID", tagname
) == 0 ||
78 strcmp("PARTLABEL", tagname
) == 0))
86 * @tag: NAME=value string
88 * Returns: 1 if the @tag is parsable and tag NAME= is supported by libmount, or 0.
90 int mnt_tag_is_valid(const char *tag
)
93 int rc
= tag
&& blkid_parse_tag_string(tag
, &t
, NULL
) == 0
94 && mnt_valid_tagname(t
);
100 int mnt_parse_offset(const char *str
, size_t len
, uintmax_t *res
)
108 p
= strndup(str
, len
);
112 if (strtosize(p
, res
))
118 /* used as a callback by bsearch in mnt_fstype_is_pseudofs() */
119 static int fstype_cmp(const void *v1
, const void *v2
)
121 const char *s1
= *(char * const *)v1
;
122 const char *s2
= *(char * const *)v2
;
124 return strcmp(s1
, s2
);
127 int mnt_stat_mountpoint(const char *target
, struct stat
*st
)
129 #ifdef AT_NO_AUTOMOUNT
130 return fstatat(AT_FDCWD
, target
, st
, AT_NO_AUTOMOUNT
);
132 return stat(target
, st
);
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:
140 * mnt_chdir_to_parent("/mnt/test", &buf) ==> chdir("/mnt"), buf="test"
142 int mnt_chdir_to_parent(const char *target
, char **filename
)
144 char *buf
, *parent
, *last
= NULL
;
148 if (!target
|| *target
!= '/')
151 DBG(UTILS
, ul_debug("moving to %s parent", target
));
153 buf
= strdup(target
);
157 if (*(buf
+ 1) != '\0') {
158 last
= stripoff_last_component(buf
);
163 parent
= buf
&& *buf
? buf
: "/";
165 if (chdir(parent
) == -1) {
166 DBG(UTILS
, ul_debug("failed to chdir to %s: %m", parent
));
170 if (!getcwd(cwd
, sizeof(cwd
))) {
171 DBG(UTILS
, ul_debug("failed to obtain current directory: %m"));
175 if (strcmp(cwd
, parent
) != 0) {
177 "unexpected chdir (expected=%s, cwd=%s)", parent
, cwd
));
182 "current directory moved to %s [last_component='%s']",
189 memcpy(*filename
, ".", 2);
191 memmove(*filename
, last
, strlen(last
) + 1);
201 * Check if @path is on a read-only filesystem independently of file permissions.
203 int mnt_is_readonly(const char *path
)
205 if (access(path
, W_OK
) == 0)
212 #ifdef HAVE_UTIMENSAT
214 * access(2) returns EACCES on read-only FS:
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).
220 * - for a read-write filesystem with a read-only VFS node (aka -o remount,ro,bind)
223 struct timespec times
[2];
225 DBG(UTILS
, ul_debug(" doing utimensat() based write test"));
227 times
[0].tv_nsec
= UTIME_NOW
; /* atime */
228 times
[1].tv_nsec
= UTIME_OMIT
; /* mtime */
230 if (utimensat(AT_FDCWD
, path
, times
, 0) == -1)
231 return errno
== EROFS
;
241 * Encode @str to be compatible with fstab/mtab
243 * Returns: newly allocated string or NULL in case of error.
245 char *mnt_mangle(const char *str
)
254 * Decode @str from fstab/mtab
256 * Returns: newly allocated string or NULL in case of error.
258 char *mnt_unmangle(const char *str
)
260 return unmangle(str
, NULL
);
264 * mnt_fstype_is_pseudofs:
265 * @type: filesystem name
267 * Returns: 1 for filesystems like proc, sysfs, ... or 0.
269 int mnt_fstype_is_pseudofs(const char *type
)
271 /* This array must remain sorted when adding new fstypes */
272 static const char *pseudofs
[] = {
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. */
319 return !(bsearch(&type
, pseudofs
, ARRAY_SIZE(pseudofs
),
320 sizeof(char*), fstype_cmp
) == NULL
);
324 * mnt_fstype_is_netfs:
325 * @type: filesystem name
327 * Returns: 1 for filesystems like cifs, nfs, ... or 0.
329 int mnt_fstype_is_netfs(const char *type
)
331 if (strcmp(type
, "cifs") == 0 ||
332 strcmp(type
, "smbfs") == 0 ||
333 strncmp(type
,"nfs", 3) == 0 ||
334 strcmp(type
, "afs") == 0 ||
335 strcmp(type
, "ncpfs") == 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";
428 int is_procfs_fd(int fd
)
432 return fstatfs(fd
, &sfs
) == 0 && sfs
.f_type
== STATFS_PROC_MAGIC
;
437 * @type: filesystem type
438 * @pattern: filesystem name or comma delimited list of names
440 * The @pattern list of filesystems can be prefixed with a global
441 * "no" prefix to invert matching of the whole list. The "no" could
442 * also be used for individual items in the @pattern list. So,
443 * "nofoo,bar" has the same meaning as "nofoo,nobar".
445 * "bar" : "nofoo,bar" -> False (global "no" prefix)
447 * "bar" : "foo,bar" -> True
449 * "bar" : "foo,nobar" -> False
451 * Returns: 1 if type is matching, else 0. This function also returns
452 * 0 if @pattern is NULL and @type is non-NULL.
454 int mnt_match_fstype(const char *type
, const char *pattern
)
456 return match_fstype(type
, pattern
);
459 void mnt_free_filesystems(char **filesystems
)
465 for (p
= filesystems
; *p
; p
++)
470 static int add_filesystem(char ***filesystems
, char *name
)
479 for (n
= 0, p
= *filesystems
; *p
; p
++, n
++) {
480 if (strcmp(*p
, name
) == 0)
487 if (n
== 0 || !((n
+ 1) % MYCHUNK
)) {
488 size_t items
= ((n
+ 1 + MYCHUNK
) / MYCHUNK
) * MYCHUNK
;
489 char **x
= realloc(*filesystems
, items
* sizeof(char *));
496 (*filesystems
)[n
] = name
;
497 (*filesystems
)[n
+ 1] = NULL
;
502 mnt_free_filesystems(*filesystems
);
506 static int get_filesystems(const char *filename
, char ***filesystems
, const char *pattern
)
512 f
= fopen(filename
, "r" UL_CLOEXECSTR
);
516 DBG(UTILS
, ul_debug("reading filesystems list from: %s", filename
));
518 while (fgets(line
, sizeof(line
), f
)) {
519 char name
[sizeof(line
)];
521 if (*line
== '#' || strncmp(line
, "nodev", 5) == 0)
523 if (sscanf(line
, " %128[^\n ]\n", name
) != 1)
525 if (strcmp(name
, "*") == 0) {
527 break; /* end of the /etc/filesystems */
529 if (pattern
&& !mnt_match_fstype(name
, pattern
))
531 rc
= add_filesystem(filesystems
, name
);
541 * Always check the @filesystems pointer!
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,
549 * mount will read /proc/filesystems afterwards.
551 int mnt_get_filesystems(char ***filesystems
, const char *pattern
)
560 rc
= get_filesystems(_PATH_FILESYSTEMS
, filesystems
, pattern
);
564 rc
= get_filesystems(_PATH_PROC_FILESYSTEMS
, filesystems
, pattern
);
565 if (rc
== 1 && *filesystems
)
566 rc
= 0; /* /proc/filesystems not found */
572 * Returns an allocated string with username or NULL.
574 char *mnt_get_username(const uid_t uid
)
578 char *buf
, *username
= NULL
;
580 buf
= malloc(UL_GETPW_BUFSIZ
);
584 if (!getpwuid_r(uid
, &pwd
, buf
, UL_GETPW_BUFSIZ
, &res
) && res
)
585 username
= strdup(pwd
.pw_name
);
591 int mnt_get_uid(const char *username
, uid_t
*uid
)
598 if (!username
|| !uid
)
601 buf
= malloc(UL_GETPW_BUFSIZ
);
605 if (!getpwnam_r(username
, &pwd
, buf
, UL_GETPW_BUFSIZ
, &pw
) && pw
) {
610 "cannot convert '%s' username to UID", username
));
611 rc
= errno
? -errno
: -EINVAL
;
618 int mnt_get_gid(const char *groupname
, gid_t
*gid
)
625 if (!groupname
|| !gid
)
628 buf
= malloc(UL_GETPW_BUFSIZ
);
632 if (!getgrnam_r(groupname
, &grp
, buf
, UL_GETPW_BUFSIZ
, &gr
) && gr
) {
637 "cannot convert '%s' groupname to GID", groupname
));
638 rc
= errno
? -errno
: -EINVAL
;
645 int mnt_in_group(gid_t gid
)
653 n
= getgroups(0, NULL
);
657 grps
= malloc(n
* sizeof(*grps
));
661 if (getgroups(n
, grps
) == n
) {
662 for (i
= 0; i
< n
; i
++) {
663 if (grps
[i
] == gid
) {
674 static int try_write(const char *filename
, const char *directory
)
681 DBG(UTILS
, ul_debug("try write %s dir: %s", filename
, directory
));
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...
687 if (eaccess(filename
, R_OK
|W_OK
) == 0) {
688 DBG(UTILS
, ul_debug(" access OK"));
690 } else if (errno
!= ENOENT
) {
691 DBG(UTILS
, ul_debug(" access FAILED"));
693 } else if (directory
) {
694 /* file does not exist; try if directory is writable */
695 if (eaccess(directory
, R_OK
|W_OK
) != 0)
698 DBG(UTILS
, ul_debug(" access %s [%s]", rc
? "FAILED" : "OK", directory
));
703 DBG(UTILS
, ul_debug(" doing open-write test"));
705 int fd
= open(filename
, O_RDWR
|O_CREAT
|O_CLOEXEC
,
706 S_IWUSR
|S_IRUSR
|S_IRGRP
|S_IROTH
);
716 * mnt_has_regular_mtab:
717 * @mtab: returns path to mtab
718 * @writable: returns 1 if the file is writable
720 * If the file does not exist and @writable argument is not NULL, then it will
721 * try to create the file.
723 * Returns: 1 if /etc/mtab is a regular file, and 0 in case of error (check
724 * errno for more details).
726 int mnt_has_regular_mtab(const char **mtab
, int *writable
)
730 const char *filename
= mtab
&& *mtab
? *mtab
: mnt_get_mtab_path();
737 DBG(UTILS
, ul_debug("mtab: %s", filename
));
739 rc
= lstat(filename
, &st
);
743 if (S_ISREG(st
.st_mode
)) {
745 *writable
= !try_write(filename
, NULL
);
746 DBG(UTILS
, ul_debug("%s: writable", filename
));
752 /* try to create the file */
754 *writable
= !try_write(filename
, NULL
);
756 DBG(UTILS
, ul_debug("%s: writable", filename
));
762 DBG(UTILS
, ul_debug("%s: irregular/non-writable", filename
));
767 * Don't export this to libmount API -- utab is private library stuff.
769 * If the file does not exist and @writable argument is not NULL, then it will
770 * try to create the directory (e.g. /run/mount) and the file.
772 * Returns: 1 if utab is a regular file, and 0 in case of
773 * error (check errno for more details).
775 int mnt_has_regular_utab(const char **utab
, int *writable
)
779 const char *filename
= utab
&& *utab
? *utab
: mnt_get_utab_path();
786 DBG(UTILS
, ul_debug("utab: %s", filename
));
788 rc
= lstat(filename
, &st
);
792 if (S_ISREG(st
.st_mode
)) {
794 *writable
= !try_write(filename
, NULL
);
797 goto done
; /* it's not a regular file */
801 char *dirname
= strdup(filename
);
806 stripoff_last_component(dirname
); /* remove filename */
808 rc
= mkdir(dirname
, S_IWUSR
|
809 S_IRUSR
|S_IRGRP
|S_IROTH
|
810 S_IXUSR
|S_IXGRP
|S_IXOTH
);
811 if (rc
&& errno
!= EEXIST
) {
813 goto done
; /* probably EACCES */
816 *writable
= !try_write(filename
, dirname
);
822 DBG(UTILS
, ul_debug("%s: irregular/non-writable file", filename
));
827 * mnt_get_swaps_path:
829 * Returns: path to /proc/swaps or $LIBMOUNT_SWAPS.
831 const char *mnt_get_swaps_path(void)
833 const char *p
= safe_getenv("LIBMOUNT_SWAPS");
834 return p
? : _PATH_PROC_SWAPS
;
838 * mnt_get_fstab_path:
840 * Returns: path to /etc/fstab or $LIBMOUNT_FSTAB.
842 const char *mnt_get_fstab_path(void)
844 const char *p
= safe_getenv("LIBMOUNT_FSTAB");
845 return p
? : _PATH_MNTTAB
;
851 * This function returns the *default* location of the mtab file. The result does
852 * not have to be writable. See also mnt_has_regular_mtab().
854 * Returns: path to /etc/mtab or $LIBMOUNT_MTAB.
856 const char *mnt_get_mtab_path(void)
858 const char *p
= safe_getenv("LIBMOUNT_MTAB");
859 return p
? : _PATH_MOUNTED
;
863 * Don't export this to libmount API -- utab is private library stuff.
865 * Returns: path to /run/mount/utab (or /dev/.mount/utab) or $LIBMOUNT_UTAB.
867 const char *mnt_get_utab_path(void)
870 const char *p
= safe_getenv("LIBMOUNT_UTAB");
875 if (stat(MNT_RUNTIME_TOPDIR
, &st
) == 0)
876 return MNT_PATH_UTAB
;
878 return MNT_PATH_UTAB_OLD
;
882 /* returns file descriptor or -errno, @name returns a unique filename
884 int mnt_open_uniq_filename(const char *filename
, char **name
)
895 rc
= asprintf(&n
, "%s.XXXXXX", filename
);
899 /* This is for very old glibc and for compatibility with Posix, which says
900 * nothing about mkstemp() mode. All sane glibc use secure mode (0600).
902 oldmode
= umask(S_IRGRP
|S_IWGRP
|S_IXGRP
|
903 S_IROTH
|S_IWOTH
|S_IXOTH
);
904 fd
= mkostemp(n
, O_RDWR
|O_CREAT
|O_EXCL
|O_CLOEXEC
);
918 * mnt_get_mountpoint:
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.
924 * WARNING: the function compares st_dev of the @path elements. This traditional
925 * way may be insufficient on filesystems like Linux "overlay". See also
926 * mnt_table_find_target().
928 * Returns: allocated string with the target of the mounted device or NULL on error
930 char *mnt_get_mountpoint(const char *path
)
942 if (*mnt
== '/' && *(mnt
+ 1) == '\0')
945 if (mnt_stat_mountpoint(mnt
, &st
))
950 char *p
= stripoff_last_component(mnt
);
954 if (mnt_stat_mountpoint(*mnt
? mnt
: "/", &st
))
963 } while (mnt
&& *(mnt
+ 1) != '\0');
967 DBG(UTILS
, ul_debug("%s mountpoint is %s", path
, mnt
));
975 * Search for @name kernel command parameter.
977 * Returns newly allocated string with a parameter argument if the @name is
978 * specified as "name=" or returns pointer to @name or returns NULL if not
979 * found. If it is specified more than once, we grab the last copy.
981 * For example cmdline: "aaa bbb=BBB ccc"
983 * @name is "aaa" --returns--> "aaa" (pointer to @name)
984 * @name is "bbb=" --returns--> "BBB" (allocated)
985 * @name is "foo" --returns--> NULL
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.
991 char *mnt_get_kernel_cmdline_option(const char *name
)
996 char *p
, *res
= NULL
, *mem
= NULL
;
997 char buf
[BUFSIZ
]; /* see kernel include/asm-generic/setup.h: COMMAND_LINE_SIZE */
998 const char *path
= _PATH_PROC_CMDLINE
;
1000 if (!name
|| !name
[0])
1004 path
= getenv("LIBMOUNT_KERNEL_CMDLINE");
1006 path
= _PATH_PROC_CMDLINE
;
1008 f
= fopen(path
, "r" UL_CLOEXECSTR
);
1012 p
= fgets(buf
, sizeof(buf
), f
);
1015 if (!p
|| !*p
|| *p
== '\n')
1018 p
= strstr(p
, " -- ");
1020 /* no more kernel args after this */
1024 buf
[len
- 1] = '\0'; /* remove last '\n' */
1028 if (name
[len
- 1] == '=')
1031 for (p
= buf
; p
&& *p
; p
++) {
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
)))
1037 continue; /* no space after the option */
1042 while (*p
&& !isblank(*p
)) /* jump to the end of the argument */
1047 res
= mem
= strdup(v
);
1051 res
= (char *) name
; /* option without '=' */
1052 /* don't break -- keep scanning for more options */
1059 * mnt_guess_system_root:
1060 * @devno: device number or zero
1061 * @cache: paths cache or NULL
1062 * @path: returns allocated path
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.
1067 * The function uses /sys to convert devno to device name.
1069 * Returns: 0 = success, 1 = not found, <0 = error
1073 int mnt_guess_system_root(dev_t devno
, struct libmnt_cache
*cache
, char **path
)
1076 char *dev
= NULL
, *spec
= NULL
;
1082 DBG(UTILS
, ul_debug("guessing system root [devno %u:%u]", major(devno
), minor(devno
)));
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.
1087 if (major(devno
) > 0) {
1088 dev
= sysfs_devno_to_devpath(devno
, buf
, sizeof(buf
));
1090 DBG(UTILS
, ul_debug(" devno converted to %s", dev
));
1095 /* Let's try to use root= kernel command line option
1097 spec
= mnt_get_kernel_cmdline_option("root=");
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
));
1105 DBG(UTILS
, ul_debug(" root=%s converted to %s", spec
, dev
));
1109 /* hexhex notation */
1110 } else if (isxdigit_string(spec
)) {
1115 n
= strtoul(spec
, &end
, 16);
1117 if (errno
|| spec
== end
|| (end
&& *end
))
1118 DBG(UTILS
, ul_debug(" failed to parse root='%s'", spec
));
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
));
1125 DBG(UTILS
, ul_debug(" root=%s converted to %s", spec
, dev
));
1130 /* devname or PARTUUID= etc. */
1132 DBG(UTILS
, ul_debug(" converting root='%s'", spec
));
1134 dev
= mnt_resolve_spec(spec
, cache
);
1141 *path
= allocated
? dev
: strdup(dev
);
1150 #if defined(HAVE_FMEMOPEN) || defined(TEST_PROGRAM)
1153 * This function tries to minimize possible races when we read
1154 * /proc/#/{mountinfo,mount} files.
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).
1160 * Returns: <0 error, 0 success, 1 too many attempts
1162 static int read_procfs_file(int fd
, char **buf
, size_t *bufsiz
)
1165 int rc
= 0, tries
= 0;
1176 if (bufmax
== *bufsiz
) {
1179 bufmax
= bufmax
? bufmax
* 2 : (16 * 1024);
1181 tmp
= realloc(*buf
, bufmax
);
1185 bufptr
= tmp
+ *bufsiz
;
1189 ret
= read(fd
, bufptr
, bufmax
);
1193 if (errno
== EAGAIN
|| errno
== EINTR
) {
1200 } else if (ret
> 0) {
1201 /* success -- verify no event during read */
1202 struct pollfd fds
[] = {
1203 { .fd
= fd
, .events
= POLLPRI
}
1206 rc
= poll(fds
, 1, 0);
1208 break; /* poll() error */
1210 /* event -- read all again */
1211 if (lseek(fd
, 0, SEEK_SET
) != 0)
1219 /* successful read() without active poll() */
1220 *bufsiz
+= (size_t) ret
;
1227 } while (tries
<= 5);
1229 rc
= errno
? -errno
: 1;
1238 * Create FILE stream for data from read_procfs_file()
1240 FILE *mnt_get_procfs_memstream(int fd
, char **membuf
)
1246 /* in case of error, rewind to the original position */
1247 cur
= lseek(fd
, 0, SEEK_CUR
);
1249 if (read_procfs_file(fd
, membuf
, &sz
) == 0
1251 && (memf
= fmemopen(*membuf
, sz
, "r")))
1255 lseek(fd
, cur
, SEEK_SET
);
1259 FILE *mnt_get_procfs_memstream(int fd
__attribute((__unused__
)),
1260 char **membuf
__attribute((__unused__
)))
1264 #endif /* HAVE_FMEMOPEN */
1268 static int test_proc_read(struct libmnt_test
*ts
, int argc
, char *argv
[])
1271 char *filename
= argv
[1];
1273 int rc
= 0, fd
= open(filename
, O_RDONLY
);
1276 warn("%s: cannot open", filename
);
1280 rc
= read_procfs_file(fd
, &buf
, &bufsiz
);
1285 fwrite(buf
, 1, bufsiz
, stdout
);
1289 warnx("too many attempts");
1292 warn("%s: cannot read", filename
);
1299 static int test_match_fstype(struct libmnt_test
*ts
, int argc
, char *argv
[])
1301 char *type
= argv
[1];
1302 char *pattern
= argv
[2];
1304 printf("%s\n", mnt_match_fstype(type
, pattern
) ? "MATCH" : "NOT-MATCH");
1308 static int test_match_options(struct libmnt_test
*ts
, int argc
, char *argv
[])
1310 char *optstr
= argv
[1];
1311 char *pattern
= argv
[2];
1313 printf("%s\n", mnt_match_options(optstr
, pattern
) ? "MATCH" : "NOT-MATCH");
1317 static int test_startswith(struct libmnt_test
*ts
, int argc
, char *argv
[])
1319 char *optstr
= argv
[1];
1320 char *pattern
= argv
[2];
1322 printf("%s\n", startswith(optstr
, pattern
) ? "YES" : "NOT");
1326 static int test_endswith(struct libmnt_test
*ts
, int argc
, char *argv
[])
1328 char *optstr
= argv
[1];
1329 char *pattern
= argv
[2];
1331 printf("%s\n", endswith(optstr
, pattern
) ? "YES" : "NOT");
1335 static int test_appendstr(struct libmnt_test
*ts
, int argc
, char *argv
[])
1337 char *str
= strdup(argv
[1]);
1338 const char *ap
= argv
[2];
1340 append_string(&str
, ap
);
1341 printf("new string: '%s'\n", str
);
1347 static int test_mountpoint(struct libmnt_test
*ts
, int argc
, char *argv
[])
1349 char *path
= canonicalize_path(argv
[1]),
1350 *mnt
= path
? mnt_get_mountpoint(path
) : NULL
;
1352 printf("%s: %s\n", argv
[1], mnt
? : "unknown");
1358 static int test_filesystems(struct libmnt_test
*ts
, int argc
, char *argv
[])
1360 char **filesystems
= NULL
;
1363 rc
= mnt_get_filesystems(&filesystems
, argc
? argv
[1] : NULL
);
1366 for (p
= filesystems
; *p
; p
++)
1368 mnt_free_filesystems(filesystems
);
1373 static int test_chdir(struct libmnt_test
*ts
, int argc
, char *argv
[])
1376 char *path
= canonicalize_path(argv
[1]),
1382 rc
= mnt_chdir_to_parent(path
, &last
);
1384 printf("path='%s', abs='%s', last='%s'\n",
1385 argv
[1], path
, last
);
1392 static int test_kernel_cmdline(struct libmnt_test
*ts
, int argc
, char *argv
[])
1394 char *name
= argv
[1];
1397 res
= mnt_get_kernel_cmdline_option(name
);
1399 printf("'%s' not found\n", name
);
1400 else if (res
== name
)
1401 printf("'%s' found\n", name
);
1403 printf("'%s' found, argument: '%s'\n", name
, res
);
1411 static int test_guess_root(struct libmnt_test
*ts
, int argc
, char *argv
[])
1420 if (sscanf(argv
[1], "%u:%u", &x
, &y
) != 2)
1422 devno
= makedev(x
, y
);
1425 rc
= mnt_guess_system_root(devno
, NULL
, &real
);
1429 fputs("not found\n", stdout
);
1431 printf("%s\n", real
);
1437 static int test_mkdir(struct libmnt_test
*ts
, int argc
, char *argv
[])
1441 rc
= mkdir_p(argv
[1], S_IRWXU
|
1445 printf("mkdir %s failed\n", argv
[1]);
1449 static int test_statfs_type(struct libmnt_test
*ts
, int argc
, char *argv
[])
1454 rc
= statfs(argv
[1], &vfs
);
1456 printf("%s: statfs failed: %m\n", argv
[1]);
1458 printf("%-30s: statfs type: %-12s [0x%lx]\n", argv
[1],
1459 mnt_statfs_get_fstype(&vfs
),
1465 int main(int argc
, char *argv
[])
1467 struct libmnt_test tss
[] = {
1468 { "--match-fstype", test_match_fstype
, "<type> <pattern> FS types matching" },
1469 { "--match-options", test_match_options
, "<options> <pattern> options matching" },
1470 { "--filesystems", test_filesystems
, "[<pattern>] list /{etc,proc}/filesystems" },
1471 { "--starts-with", test_startswith
, "<string> <prefix>" },
1472 { "--ends-with", test_endswith
, "<string> <prefix>" },
1473 { "--append-string", test_appendstr
, "<string> <appendix>" },
1474 { "--mountpoint", test_mountpoint
, "<path>" },
1475 { "--cd-parent", test_chdir
, "<path>" },
1476 { "--kernel-cmdline",test_kernel_cmdline
, "<option> | <option>=" },
1477 { "--guess-root", test_guess_root
, "[<maj:min>]" },
1478 { "--mkdir", test_mkdir
, "<path>" },
1479 { "--statfs-type", test_statfs_type
, "<path>" },
1480 { "--read-procfs", test_proc_read
, "<path>" },
1485 return mnt_run_test(tss
, argc
, argv
);
1488 #endif /* TEST_PROGRAM */