]>
git.ipfire.org Git - thirdparty/util-linux.git/blob - libmount/src/utils.c
6be7a72c0a6cd01600581ccb169da9e9de0b95d5
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
);
136 int mnt_lstat_mountpoint(const char *target
, struct stat
*st
)
138 #ifdef AT_NO_AUTOMOUNT
139 return fstatat(AT_FDCWD
, target
, st
, AT_NO_AUTOMOUNT
| AT_SYMLINK_NOFOLLOW
);
141 return lstat(target
, st
);
147 * Note that the @target has to be an absolute path (so at least "/"). The
148 * @filename returns an allocated buffer with the last path component, for example:
150 * mnt_chdir_to_parent("/mnt/test", &buf) ==> chdir("/mnt"), buf="test"
152 int mnt_chdir_to_parent(const char *target
, char **filename
)
154 char *buf
, *parent
, *last
= NULL
;
158 if (!target
|| *target
!= '/')
161 DBG(UTILS
, ul_debug("moving to %s parent", target
));
163 buf
= strdup(target
);
167 if (*(buf
+ 1) != '\0') {
168 last
= stripoff_last_component(buf
);
173 parent
= buf
&& *buf
? buf
: "/";
175 if (chdir(parent
) == -1) {
176 DBG(UTILS
, ul_debug("failed to chdir to %s: %m", parent
));
180 if (!getcwd(cwd
, sizeof(cwd
))) {
181 DBG(UTILS
, ul_debug("failed to obtain current directory: %m"));
185 if (strcmp(cwd
, parent
) != 0) {
187 "unexpected chdir (expected=%s, cwd=%s)", parent
, cwd
));
192 "current directory moved to %s [last_component='%s']",
199 memcpy(*filename
, ".", 2);
201 memmove(*filename
, last
, strlen(last
) + 1);
211 * Check if @path is on a read-only filesystem independently of file permissions.
213 int mnt_is_readonly(const char *path
)
215 if (access(path
, W_OK
) == 0)
222 #ifdef HAVE_UTIMENSAT
224 * access(2) returns EACCES on read-only FS:
226 * - for set-uid application if one component of the path is not
227 * accessible for the current rUID. (Note that euidaccess(2) does not
228 * check for EROFS at all).
230 * - for a read-write filesystem with a read-only VFS node (aka -o remount,ro,bind)
233 struct timespec times
[2];
235 DBG(UTILS
, ul_debug(" doing utimensat() based write test"));
237 times
[0].tv_nsec
= UTIME_NOW
; /* atime */
238 times
[1].tv_nsec
= UTIME_OMIT
; /* mtime */
240 if (utimensat(AT_FDCWD
, path
, times
, 0) == -1)
241 return errno
== EROFS
;
251 * Encode @str to be compatible with fstab/mtab
253 * Returns: newly allocated string or NULL in case of error.
255 char *mnt_mangle(const char *str
)
264 * Decode @str from fstab/mtab
266 * Returns: newly allocated string or NULL in case of error.
268 char *mnt_unmangle(const char *str
)
270 return unmangle(str
, NULL
);
274 * mnt_fstype_is_pseudofs:
275 * @type: filesystem name
277 * Returns: 1 for filesystems like proc, sysfs, ... or 0.
279 int mnt_fstype_is_pseudofs(const char *type
)
281 /* This array must remain sorted when adding new fstypes */
282 static const char *pseudofs
[] = {
302 "fuse", /* Fallback name of fuse used by many poorly written drivers. */
303 "fuse.archivemount", /* Not a true pseudofs (has source), but source is not reported. */
304 "fuse.avfsd", /* Not a true pseudofs (has source), but source is not reported. */
305 "fuse.dumpfs", /* In fact, it is a netfs, but source is not reported. */
306 "fuse.encfs", /* Not a true pseudofs (has source), but source is not reported. */
307 "fuse.gvfs-fuse-daemon", /* Old name, not used by gvfs any more. */
311 "fuse.vmware-vmblock",
340 return !(bsearch(&type
, pseudofs
, ARRAY_SIZE(pseudofs
),
341 sizeof(char*), fstype_cmp
) == NULL
);
345 * mnt_fstype_is_netfs:
346 * @type: filesystem name
348 * Returns: 1 for filesystems like cifs, nfs, ... or 0.
350 int mnt_fstype_is_netfs(const char *type
)
352 if (strcmp(type
, "cifs") == 0 ||
353 strcmp(type
, "smb3") == 0 ||
354 strcmp(type
, "smbfs") == 0 ||
355 strncmp(type
,"nfs", 3) == 0 ||
356 strcmp(type
, "afs") == 0 ||
357 strcmp(type
, "ncpfs") == 0 ||
358 strcmp(type
, "fuse.curlftpfs") == 0 ||
359 strcmp(type
, "fuse.sshfs") == 0 ||
360 strncmp(type
,"9p", 2) == 0)
365 const char *mnt_statfs_get_fstype(struct statfs
*vfs
)
369 switch (vfs
->f_type
) {
370 case STATFS_ADFS_MAGIC
: return "adfs";
371 case STATFS_AFFS_MAGIC
: return "affs";
372 case STATFS_AFS_MAGIC
: return "afs";
373 case STATFS_AUTOFS_MAGIC
: return "autofs";
374 case STATFS_BDEVFS_MAGIC
: return "bdev";
375 case STATFS_BEFS_MAGIC
: return "befs";
376 case STATFS_BFS_MAGIC
: return "befs";
377 case STATFS_BINFMTFS_MAGIC
: return "binfmt_misc";
378 case STATFS_BTRFS_MAGIC
: return "btrfs";
379 case STATFS_CEPH_MAGIC
: return "ceph";
380 case STATFS_CGROUP_MAGIC
: return "cgroup";
381 case STATFS_CIFS_MAGIC
: return "cifs";
382 case STATFS_CODA_MAGIC
: return "coda";
383 case STATFS_CONFIGFS_MAGIC
: return "configfs";
384 case STATFS_CRAMFS_MAGIC
: return "cramfs";
385 case STATFS_DEBUGFS_MAGIC
: return "debugfs";
386 case STATFS_DEVPTS_MAGIC
: return "devpts";
387 case STATFS_ECRYPTFS_MAGIC
: return "ecryptfs";
388 case STATFS_EFIVARFS_MAGIC
: return "efivarfs";
389 case STATFS_EFS_MAGIC
: return "efs";
390 case STATFS_EXOFS_MAGIC
: return "exofs";
391 case STATFS_EXT4_MAGIC
: return "ext4"; /* all extN use the same magic */
392 case STATFS_F2FS_MAGIC
: return "f2fs";
393 case STATFS_FUSE_MAGIC
: return "fuse";
394 case STATFS_FUTEXFS_MAGIC
: return "futexfs";
395 case STATFS_GFS2_MAGIC
: return "gfs2";
396 case STATFS_HFSPLUS_MAGIC
: return "hfsplus";
397 case STATFS_HOSTFS_MAGIC
: return "hostfs";
398 case STATFS_HPFS_MAGIC
: return "hpfs";
399 case STATFS_HPPFS_MAGIC
: return "hppfs";
400 case STATFS_HUGETLBFS_MAGIC
: return "hugetlbfs";
401 case STATFS_ISOFS_MAGIC
: return "iso9660";
402 case STATFS_JFFS2_MAGIC
: return "jffs2";
403 case STATFS_JFS_MAGIC
: return "jfs";
404 case STATFS_LOGFS_MAGIC
: return "logfs";
405 case STATFS_MINIX2_MAGIC
:
406 case STATFS_MINIX2_MAGIC2
:
407 case STATFS_MINIX3_MAGIC
:
408 case STATFS_MINIX_MAGIC
:
409 case STATFS_MINIX_MAGIC2
: return "minix";
410 case STATFS_MQUEUE_MAGIC
: return "mqueue";
411 case STATFS_MSDOS_MAGIC
: return "vfat";
412 case STATFS_NCP_MAGIC
: return "ncp";
413 case STATFS_NFS_MAGIC
: return "nfs";
414 case STATFS_NILFS_MAGIC
: return "nilfs2";
415 case STATFS_NTFS_MAGIC
: return "ntfs";
416 case STATFS_OCFS2_MAGIC
: return "ocfs2";
417 case STATFS_OMFS_MAGIC
: return "omfs";
418 case STATFS_OPENPROMFS_MAGIC
: return "openpromfs";
419 case STATFS_PIPEFS_MAGIC
: return "pipefs";
420 case STATFS_PROC_MAGIC
: return "proc";
421 case STATFS_PSTOREFS_MAGIC
: return "pstore";
422 case STATFS_QNX4_MAGIC
: return "qnx4";
423 case STATFS_QNX6_MAGIC
: return "qnx6";
424 case STATFS_RAMFS_MAGIC
: return "ramfs";
425 case STATFS_REISERFS_MAGIC
: return "reiser4";
426 case STATFS_ROMFS_MAGIC
: return "romfs";
427 case STATFS_SECURITYFS_MAGIC
: return "securityfs";
428 case STATFS_SELINUXFS_MAGIC
: return "selinuxfs";
429 case STATFS_SMACKFS_MAGIC
: return "smackfs";
430 case STATFS_SMB_MAGIC
: return "smb";
431 case STATFS_SOCKFS_MAGIC
: return "sockfs";
432 case STATFS_SQUASHFS_MAGIC
: return "squashfs";
433 case STATFS_SYSFS_MAGIC
: return "sysfs";
434 case STATFS_TMPFS_MAGIC
: return "tmpfs";
435 case STATFS_UBIFS_MAGIC
: return "ubifs";
436 case STATFS_UDF_MAGIC
: return "udf";
437 case STATFS_UFS2_MAGIC
:
438 case STATFS_UFS_MAGIC
: return "ufs";
439 case STATFS_V9FS_MAGIC
: return "9p";
440 case STATFS_VXFS_MAGIC
: return "vxfs";
441 case STATFS_XENFS_MAGIC
: return "xenfs";
442 case STATFS_XFS_MAGIC
: return "xfs";
450 int is_procfs_fd(int fd
)
454 return fstatfs(fd
, &sfs
) == 0 && sfs
.f_type
== STATFS_PROC_MAGIC
;
459 * @type: filesystem type
460 * @pattern: filesystem name or comma delimited list of names
462 * The @pattern list of filesystems can be prefixed with a global
463 * "no" prefix to invert matching of the whole list. The "no" could
464 * also be used for individual items in the @pattern list. So,
465 * "nofoo,bar" has the same meaning as "nofoo,nobar".
467 * "bar" : "nofoo,bar" -> False (global "no" prefix)
469 * "bar" : "foo,bar" -> True
471 * "bar" : "foo,nobar" -> False
473 * Returns: 1 if type is matching, else 0. This function also returns
474 * 0 if @pattern is NULL and @type is non-NULL.
476 int mnt_match_fstype(const char *type
, const char *pattern
)
478 return match_fstype(type
, pattern
);
481 void mnt_free_filesystems(char **filesystems
)
487 for (p
= filesystems
; *p
; p
++)
492 static int add_filesystem(char ***filesystems
, char *name
)
501 for (n
= 0, p
= *filesystems
; *p
; p
++, n
++) {
502 if (strcmp(*p
, name
) == 0)
509 if (n
== 0 || !((n
+ 1) % MYCHUNK
)) {
510 size_t items
= ((n
+ 1 + MYCHUNK
) / MYCHUNK
) * MYCHUNK
;
511 char **x
= realloc(*filesystems
, items
* sizeof(char *));
518 (*filesystems
)[n
] = name
;
519 (*filesystems
)[n
+ 1] = NULL
;
524 mnt_free_filesystems(*filesystems
);
528 static int get_filesystems(const char *filename
, char ***filesystems
, const char *pattern
)
534 f
= fopen(filename
, "r" UL_CLOEXECSTR
);
538 DBG(UTILS
, ul_debug("reading filesystems list from: %s", filename
));
540 while (fgets(line
, sizeof(line
), f
)) {
541 char name
[sizeof(line
)];
543 if (*line
== '#' || strncmp(line
, "nodev", 5) == 0)
545 if (sscanf(line
, " %128[^\n ]\n", name
) != 1)
547 if (strcmp(name
, "*") == 0) {
549 break; /* end of the /etc/filesystems */
551 if (pattern
&& !mnt_match_fstype(name
, pattern
))
553 rc
= add_filesystem(filesystems
, name
);
563 * Always check the @filesystems pointer!
567 * ...mount will try to read the file /etc/filesystems, or, if that does not
568 * exist, /proc/filesystems. All of the filesystem types listed there will
569 * be tried, except for those that are labeled "nodev" (e.g., devpts,
570 * proc and nfs). If /etc/filesystems ends in a line with a single * only,
571 * mount will read /proc/filesystems afterwards.
573 int mnt_get_filesystems(char ***filesystems
, const char *pattern
)
582 rc
= get_filesystems(_PATH_FILESYSTEMS
, filesystems
, pattern
);
586 rc
= get_filesystems(_PATH_PROC_FILESYSTEMS
, filesystems
, pattern
);
587 if (rc
== 1 && *filesystems
)
588 rc
= 0; /* /proc/filesystems not found */
594 * Returns an allocated string with username or NULL.
596 char *mnt_get_username(const uid_t uid
)
600 char *buf
, *username
= NULL
;
602 buf
= malloc(UL_GETPW_BUFSIZ
);
606 if (!getpwuid_r(uid
, &pwd
, buf
, UL_GETPW_BUFSIZ
, &res
) && res
)
607 username
= strdup(pwd
.pw_name
);
613 int mnt_get_uid(const char *username
, uid_t
*uid
)
620 if (!username
|| !uid
)
623 buf
= malloc(UL_GETPW_BUFSIZ
);
627 if (!getpwnam_r(username
, &pwd
, buf
, UL_GETPW_BUFSIZ
, &pw
) && pw
) {
632 "cannot convert '%s' username to UID", username
));
633 rc
= errno
? -errno
: -EINVAL
;
640 int mnt_get_gid(const char *groupname
, gid_t
*gid
)
647 if (!groupname
|| !gid
)
650 buf
= malloc(UL_GETPW_BUFSIZ
);
654 if (!getgrnam_r(groupname
, &grp
, buf
, UL_GETPW_BUFSIZ
, &gr
) && gr
) {
659 "cannot convert '%s' groupname to GID", groupname
));
660 rc
= errno
? -errno
: -EINVAL
;
667 int mnt_in_group(gid_t gid
)
675 n
= getgroups(0, NULL
);
679 grps
= malloc(n
* sizeof(*grps
));
683 if (getgroups(n
, grps
) == n
) {
684 for (i
= 0; i
< n
; i
++) {
685 if (grps
[i
] == gid
) {
696 static int try_write(const char *filename
, const char *directory
)
703 DBG(UTILS
, ul_debug("try write %s dir: %s", filename
, directory
));
706 /* Try eaccess() first, because open() is overkill, may be monitored by
707 * audit and we don't want to fill logs by our checks...
709 if (eaccess(filename
, R_OK
|W_OK
) == 0) {
710 DBG(UTILS
, ul_debug(" access OK"));
714 if (errno
!= ENOENT
) {
715 DBG(UTILS
, ul_debug(" access FAILED"));
720 /* file does not exist; try if directory is writable */
721 if (eaccess(directory
, R_OK
|W_OK
) != 0)
724 DBG(UTILS
, ul_debug(" access %s [%s]", rc
? "FAILED" : "OK", directory
));
729 DBG(UTILS
, ul_debug(" doing open-write test"));
731 int fd
= open(filename
, O_RDWR
|O_CREAT
|O_CLOEXEC
,
732 S_IWUSR
|S_IRUSR
|S_IRGRP
|S_IROTH
);
742 * mnt_has_regular_mtab:
743 * @mtab: returns path to mtab
744 * @writable: returns 1 if the file is writable
746 * If the file does not exist and @writable argument is not NULL, then it will
747 * try to create the file.
749 * Returns: 1 if /etc/mtab is a regular file, and 0 in case of error (check
750 * errno for more details).
752 int mnt_has_regular_mtab(const char **mtab
, int *writable
)
756 const char *filename
= mtab
&& *mtab
? *mtab
: mnt_get_mtab_path();
763 DBG(UTILS
, ul_debug("mtab: %s", filename
));
765 rc
= lstat(filename
, &st
);
769 if (S_ISREG(st
.st_mode
)) {
771 *writable
= !try_write(filename
, NULL
);
772 DBG(UTILS
, ul_debug("%s: writable", filename
));
778 /* try to create the file */
780 *writable
= !try_write(filename
, NULL
);
782 DBG(UTILS
, ul_debug("%s: writable", filename
));
788 DBG(UTILS
, ul_debug("%s: irregular/non-writable", filename
));
793 * Don't export this to libmount API -- utab is private library stuff.
795 * If the file does not exist and @writable argument is not NULL, then it will
796 * try to create the directory (e.g. /run/mount) and the file.
798 * Returns: 1 if utab is a regular file, and 0 in case of
799 * error (check errno for more details).
801 int mnt_has_regular_utab(const char **utab
, int *writable
)
805 const char *filename
= utab
&& *utab
? *utab
: mnt_get_utab_path();
812 DBG(UTILS
, ul_debug("utab: %s", filename
));
814 rc
= lstat(filename
, &st
);
818 if (S_ISREG(st
.st_mode
)) {
820 *writable
= !try_write(filename
, NULL
);
823 goto done
; /* it's not a regular file */
827 char *dirname
= strdup(filename
);
832 stripoff_last_component(dirname
); /* remove filename */
834 rc
= mkdir(dirname
, S_IWUSR
|
835 S_IRUSR
|S_IRGRP
|S_IROTH
|
836 S_IXUSR
|S_IXGRP
|S_IXOTH
);
837 if (rc
&& errno
!= EEXIST
) {
839 goto done
; /* probably EACCES */
842 *writable
= !try_write(filename
, dirname
);
848 DBG(UTILS
, ul_debug("%s: irregular/non-writable file", filename
));
853 * mnt_get_swaps_path:
855 * Returns: path to /proc/swaps or $LIBMOUNT_SWAPS.
857 const char *mnt_get_swaps_path(void)
859 const char *p
= safe_getenv("LIBMOUNT_SWAPS");
860 return p
? : _PATH_PROC_SWAPS
;
864 * mnt_get_fstab_path:
866 * Returns: path to /etc/fstab or $LIBMOUNT_FSTAB.
868 const char *mnt_get_fstab_path(void)
870 const char *p
= safe_getenv("LIBMOUNT_FSTAB");
871 return p
? : _PATH_MNTTAB
;
877 * This function returns the *default* location of the mtab file. The result does
878 * not have to be writable. See also mnt_has_regular_mtab().
880 * Returns: path to /etc/mtab or $LIBMOUNT_MTAB.
882 const char *mnt_get_mtab_path(void)
884 const char *p
= safe_getenv("LIBMOUNT_MTAB");
885 return p
? : _PATH_MOUNTED
;
889 * Don't export this to libmount API -- utab is private library stuff.
891 * Returns: path to /run/mount/utab (or /dev/.mount/utab) or $LIBMOUNT_UTAB.
893 const char *mnt_get_utab_path(void)
896 const char *p
= safe_getenv("LIBMOUNT_UTAB");
901 if (stat(MNT_RUNTIME_TOPDIR
, &st
) == 0)
902 return MNT_PATH_UTAB
;
904 return MNT_PATH_UTAB_OLD
;
908 /* returns file descriptor or -errno, @name returns a unique filename
910 int mnt_open_uniq_filename(const char *filename
, char **name
)
921 rc
= asprintf(&n
, "%s.XXXXXX", filename
);
925 /* This is for very old glibc and for compatibility with Posix, which says
926 * nothing about mkstemp() mode. All sane glibc use secure mode (0600).
928 oldmode
= umask(S_IRGRP
|S_IWGRP
|S_IXGRP
|
929 S_IROTH
|S_IWOTH
|S_IXOTH
);
930 fd
= mkostemp(n
, O_RDWR
|O_CREAT
|O_EXCL
|O_CLOEXEC
);
944 * mnt_get_mountpoint:
947 * This function finds the mountpoint that a given path resides in. @path
948 * should be canonicalized. The returned pointer should be freed by the caller.
950 * WARNING: the function compares st_dev of the @path elements. This traditional
951 * way may be insufficient on filesystems like Linux "overlay". See also
952 * mnt_table_find_target().
954 * Returns: allocated string with the target of the mounted device or NULL on error
956 char *mnt_get_mountpoint(const char *path
)
968 if (*mnt
== '/' && *(mnt
+ 1) == '\0')
971 if (mnt_stat_mountpoint(mnt
, &st
))
976 char *p
= stripoff_last_component(mnt
);
980 if (mnt_stat_mountpoint(*mnt
? mnt
: "/", &st
))
989 } while (mnt
&& *(mnt
+ 1) != '\0');
993 DBG(UTILS
, ul_debug("%s mountpoint is %s", path
, mnt
));
1001 * Search for @name kernel command parameter.
1003 * Returns newly allocated string with a parameter argument if the @name is
1004 * specified as "name=" or returns pointer to @name or returns NULL if not
1005 * found. If it is specified more than once, we grab the last copy.
1007 * For example cmdline: "aaa bbb=BBB ccc"
1009 * @name is "aaa" --returns--> "aaa" (pointer to @name)
1010 * @name is "bbb=" --returns--> "BBB" (allocated)
1011 * @name is "foo" --returns--> NULL
1013 * Note: It is not really feasible to parse the command line exactly the same
1014 * as the kernel does since we don't know which options are valid. We can use
1015 * the -- marker though and not walk past that.
1017 char *mnt_get_kernel_cmdline_option(const char *name
)
1022 char *p
, *res
= NULL
, *mem
= NULL
;
1023 char buf
[BUFSIZ
]; /* see kernel include/asm-generic/setup.h: COMMAND_LINE_SIZE */
1024 const char *path
= _PATH_PROC_CMDLINE
;
1026 if (!name
|| !name
[0])
1030 path
= getenv("LIBMOUNT_KERNEL_CMDLINE");
1032 path
= _PATH_PROC_CMDLINE
;
1034 f
= fopen(path
, "r" UL_CLOEXECSTR
);
1038 p
= fgets(buf
, sizeof(buf
), f
);
1041 if (!p
|| !*p
|| *p
== '\n')
1044 p
= strstr(p
, " -- ");
1046 /* no more kernel args after this */
1050 buf
[len
- 1] = '\0'; /* remove last '\n' */
1054 if (name
[len
- 1] == '=')
1057 for (p
= buf
; p
&& *p
; p
++) {
1058 if (!(p
= strstr(p
, name
)))
1059 break; /* not found the option */
1060 if (p
!= buf
&& !isblank(*(p
- 1)))
1061 continue; /* no space before the option */
1062 if (!val
&& *(p
+ len
) != '\0' && !isblank(*(p
+ len
)))
1063 continue; /* no space after the option */
1068 while (*p
&& !isblank(*p
)) /* jump to the end of the argument */
1073 res
= mem
= strdup(v
);
1077 res
= (char *) name
; /* option without '=' */
1078 /* don't break -- keep scanning for more options */
1085 * mnt_guess_system_root:
1086 * @devno: device number or zero
1087 * @cache: paths cache or NULL
1088 * @path: returns allocated path
1090 * Converts @devno to the real device name if devno major number is greater
1091 * than zero, otherwise use root= kernel cmdline option to get device name.
1093 * The function uses /sys to convert devno to device name.
1095 * Returns: 0 = success, 1 = not found, <0 = error
1099 int mnt_guess_system_root(dev_t devno
, struct libmnt_cache
*cache
, char **path
)
1102 char *dev
= NULL
, *spec
= NULL
;
1108 DBG(UTILS
, ul_debug("guessing system root [devno %u:%u]", major(devno
), minor(devno
)));
1110 /* The pseudo-fs, net-fs or btrfs devno is useless, otherwise it
1111 * usually matches with the source device, let's try to use it.
1113 if (major(devno
) > 0) {
1114 dev
= sysfs_devno_to_devpath(devno
, buf
, sizeof(buf
));
1116 DBG(UTILS
, ul_debug(" devno converted to %s", dev
));
1121 /* Let's try to use root= kernel command line option
1123 spec
= mnt_get_kernel_cmdline_option("root=");
1127 /* maj:min notation */
1128 if (sscanf(spec
, "%u:%u", &x
, &y
) == 2) {
1129 dev
= sysfs_devno_to_devpath(makedev(x
, y
), buf
, sizeof(buf
));
1131 DBG(UTILS
, ul_debug(" root=%s converted to %s", spec
, dev
));
1135 /* hexhex notation */
1136 } else if (isxdigit_string(spec
)) {
1141 n
= strtoul(spec
, &end
, 16);
1143 if (errno
|| spec
== end
|| (end
&& *end
))
1144 DBG(UTILS
, ul_debug(" failed to parse root='%s'", spec
));
1146 /* kernel new_decode_dev() */
1147 x
= (n
& 0xfff00) >> 8;
1148 y
= (n
& 0xff) | ((n
>> 12) & 0xfff00);
1149 dev
= sysfs_devno_to_devpath(makedev(x
, y
), buf
, sizeof(buf
));
1151 DBG(UTILS
, ul_debug(" root=%s converted to %s", spec
, dev
));
1156 /* devname or PARTUUID= etc. */
1158 DBG(UTILS
, ul_debug(" converting root='%s'", spec
));
1160 dev
= mnt_resolve_spec(spec
, cache
);
1167 *path
= allocated
? dev
: strdup(dev
);
1176 #if defined(HAVE_FMEMOPEN) || defined(TEST_PROGRAM)
1179 * This function tries to minimize possible races when we read
1180 * /proc/#/{mountinfo,mount} files.
1182 * The idea is to minimize number of read()s and check by poll() that during
1183 * the read the mount table has not been modified. If yes, than re-read it
1184 * (with some limitations to avoid never ending loop).
1186 * Returns: <0 error, 0 success, 1 too many attempts
1188 static int read_procfs_file(int fd
, char **buf
, size_t *bufsiz
)
1191 int rc
= 0, tries
= 0, ninters
= 0;
1192 char *bufptr
= NULL
;
1203 if (!bufptr
|| bufmax
== *bufsiz
) {
1206 bufmax
= bufmax
? bufmax
* 2 : (16 * 1024);
1207 tmp
= realloc(*buf
, bufmax
);
1211 bufptr
= tmp
+ *bufsiz
;
1215 ret
= read(fd
, bufptr
, bufmax
- *bufsiz
);
1219 if ((errno
== EAGAIN
|| errno
== EINTR
) && (ninters
++ < 5)) {
1226 /* success -- verify no event during read */
1227 struct pollfd fds
[] = {
1228 { .fd
= fd
, .events
= POLLPRI
}
1231 rc
= poll(fds
, 1, 0);
1233 break; /* poll() error */
1235 /* event -- read all again */
1236 if (lseek(fd
, 0, SEEK_SET
) != 0)
1243 /* busy system? -- wait */
1248 /* successful read() without active poll() */
1249 (*bufsiz
) += (size_t) ret
;
1251 tries
= ninters
= 0;
1256 } while (tries
<= 100);
1258 rc
= errno
? -errno
: 1;
1267 * Create FILE stream for data from read_procfs_file()
1269 FILE *mnt_get_procfs_memstream(int fd
, char **membuf
)
1276 /* in case of error, rewind to the original position */
1277 cur
= lseek(fd
, 0, SEEK_CUR
);
1279 if (read_procfs_file(fd
, membuf
, &sz
) == 0 && sz
> 0) {
1280 FILE *memf
= fmemopen(*membuf
, sz
, "r");
1282 return memf
; /* success */
1289 if (cur
!= (off_t
) -1)
1290 lseek(fd
, cur
, SEEK_SET
);
1294 FILE *mnt_get_procfs_memstream(int fd
__attribute((__unused__
)),
1295 char **membuf
__attribute((__unused__
)))
1299 #endif /* HAVE_FMEMOPEN */
1303 static int test_proc_read(struct libmnt_test
*ts
, int argc
, char *argv
[])
1306 char *filename
= argv
[1];
1308 int rc
= 0, fd
= open(filename
, O_RDONLY
);
1311 warn("%s: cannot open", filename
);
1315 rc
= read_procfs_file(fd
, &buf
, &bufsiz
);
1320 fwrite(buf
, 1, bufsiz
, stdout
);
1324 warnx("too many attempts");
1327 warn("%s: cannot read", filename
);
1334 static int test_match_fstype(struct libmnt_test
*ts
, int argc
, char *argv
[])
1336 char *type
= argv
[1];
1337 char *pattern
= argv
[2];
1339 printf("%s\n", mnt_match_fstype(type
, pattern
) ? "MATCH" : "NOT-MATCH");
1343 static int test_match_options(struct libmnt_test
*ts
, int argc
, char *argv
[])
1345 char *optstr
= argv
[1];
1346 char *pattern
= argv
[2];
1348 printf("%s\n", mnt_match_options(optstr
, pattern
) ? "MATCH" : "NOT-MATCH");
1352 static int test_startswith(struct libmnt_test
*ts
, int argc
, char *argv
[])
1354 char *optstr
= argv
[1];
1355 char *pattern
= argv
[2];
1357 printf("%s\n", startswith(optstr
, pattern
) ? "YES" : "NOT");
1361 static int test_endswith(struct libmnt_test
*ts
, int argc
, char *argv
[])
1363 char *optstr
= argv
[1];
1364 char *pattern
= argv
[2];
1366 printf("%s\n", endswith(optstr
, pattern
) ? "YES" : "NOT");
1370 static int test_appendstr(struct libmnt_test
*ts
, int argc
, char *argv
[])
1372 char *str
= strdup(argv
[1]);
1373 const char *ap
= argv
[2];
1375 append_string(&str
, ap
);
1376 printf("new string: '%s'\n", str
);
1382 static int test_mountpoint(struct libmnt_test
*ts
, int argc
, char *argv
[])
1384 char *path
= canonicalize_path(argv
[1]),
1385 *mnt
= path
? mnt_get_mountpoint(path
) : NULL
;
1387 printf("%s: %s\n", argv
[1], mnt
? : "unknown");
1393 static int test_filesystems(struct libmnt_test
*ts
, int argc
, char *argv
[])
1395 char **filesystems
= NULL
;
1398 rc
= mnt_get_filesystems(&filesystems
, argc
? argv
[1] : NULL
);
1401 for (p
= filesystems
; *p
; p
++)
1403 mnt_free_filesystems(filesystems
);
1408 static int test_chdir(struct libmnt_test
*ts
, int argc
, char *argv
[])
1411 char *path
= canonicalize_path(argv
[1]),
1417 rc
= mnt_chdir_to_parent(path
, &last
);
1419 printf("path='%s', abs='%s', last='%s'\n",
1420 argv
[1], path
, last
);
1427 static int test_kernel_cmdline(struct libmnt_test
*ts
, int argc
, char *argv
[])
1429 char *name
= argv
[1];
1432 res
= mnt_get_kernel_cmdline_option(name
);
1434 printf("'%s' not found\n", name
);
1435 else if (res
== name
)
1436 printf("'%s' found\n", name
);
1438 printf("'%s' found, argument: '%s'\n", name
, res
);
1446 static int test_guess_root(struct libmnt_test
*ts
, int argc
, char *argv
[])
1455 if (sscanf(argv
[1], "%u:%u", &x
, &y
) != 2)
1457 devno
= makedev(x
, y
);
1460 rc
= mnt_guess_system_root(devno
, NULL
, &real
);
1464 fputs("not found\n", stdout
);
1466 printf("%s\n", real
);
1472 static int test_mkdir(struct libmnt_test
*ts
, int argc
, char *argv
[])
1476 rc
= mkdir_p(argv
[1], S_IRWXU
|
1480 printf("mkdir %s failed\n", argv
[1]);
1484 static int test_statfs_type(struct libmnt_test
*ts
, int argc
, char *argv
[])
1489 rc
= statfs(argv
[1], &vfs
);
1491 printf("%s: statfs failed: %m\n", argv
[1]);
1493 printf("%-30s: statfs type: %-12s [0x%lx]\n", argv
[1],
1494 mnt_statfs_get_fstype(&vfs
),
1500 int main(int argc
, char *argv
[])
1502 struct libmnt_test tss
[] = {
1503 { "--match-fstype", test_match_fstype
, "<type> <pattern> FS types matching" },
1504 { "--match-options", test_match_options
, "<options> <pattern> options matching" },
1505 { "--filesystems", test_filesystems
, "[<pattern>] list /{etc,proc}/filesystems" },
1506 { "--starts-with", test_startswith
, "<string> <prefix>" },
1507 { "--ends-with", test_endswith
, "<string> <prefix>" },
1508 { "--append-string", test_appendstr
, "<string> <appendix>" },
1509 { "--mountpoint", test_mountpoint
, "<path>" },
1510 { "--cd-parent", test_chdir
, "<path>" },
1511 { "--kernel-cmdline",test_kernel_cmdline
, "<option> | <option>=" },
1512 { "--guess-root", test_guess_root
, "[<maj:min>]" },
1513 { "--mkdir", test_mkdir
, "<path>" },
1514 { "--statfs-type", test_statfs_type
, "<path>" },
1515 { "--read-procfs", test_proc_read
, "<path>" },
1520 return mnt_run_test(tss
, argc
, argv
);
1523 #endif /* TEST_PROGRAM */