]>
git.ipfire.org Git - thirdparty/util-linux.git/blob - libmount/src/utils.c
46050ff51f5bbe517e464ca019847d08f715acde
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"
34 #include "namespace.h"
37 * Return 1 if the file is not accessible or empty
39 int is_file_empty(const char *name
)
44 return (stat(name
, &st
) != 0 || st
.st_size
== 0);
47 int mnt_valid_tagname(const char *tagname
)
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))
62 * @tag: NAME=value string
64 * Returns: 1 if the @tag is parsable and tag NAME= is supported by libmount, or 0.
66 int mnt_tag_is_valid(const char *tag
)
69 int rc
= tag
&& blkid_parse_tag_string(tag
, &t
, NULL
) == 0
70 && mnt_valid_tagname(t
);
76 int mnt_parse_offset(const char *str
, size_t len
, uintmax_t *res
)
84 p
= strndup(str
, len
);
88 if (strtosize(p
, res
))
94 /* used as a callback by bsearch in mnt_fstype_is_pseudofs() */
95 static int fstype_cmp(const void *v1
, const void *v2
)
97 const char *s1
= *(char * const *)v1
;
98 const char *s2
= *(char * const *)v2
;
100 return strcmp(s1
, s2
);
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!
107 static int safe_stat(const char *target
, struct stat
*st
, int nofollow
)
112 memset(st
, 0, sizeof(struct stat
));
114 #if defined(HAVE_STATX) && defined(HAVE_STRUCT_STATX) && defined(AT_STATX_DONT_SYNC)
117 struct statx stx
= { 0 };
119 rc
= statx(AT_FDCWD
, target
,
123 | (nofollow
? AT_SYMLINK_NOFOLLOW
: 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
;
137 (errno
!= EOPNOTSUPP
&& errno
!= ENOSYS
&& errno
!= EINVAL
))
142 #ifdef AT_NO_AUTOMOUNT
143 return fstatat(AT_FDCWD
, target
, st
,
144 AT_NO_AUTOMOUNT
| (nofollow
? AT_SYMLINK_NOFOLLOW
: 0));
146 return nofollow
? lstat(target
, st
) : stat(target
, st
);
149 int mnt_safe_stat(const char *target
, struct stat
*st
)
151 return safe_stat(target
, st
, 0);
154 int mnt_safe_lstat(const char *target
, struct stat
*st
)
156 return safe_stat(target
, st
, 1);
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. */
161 int mnt_is_path(const char *target
)
165 return safe_stat(target
, &st
, 0) == 0;
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:
172 * mnt_chdir_to_parent("/mnt/test", &buf) ==> chdir("/mnt"), buf="test"
174 int mnt_chdir_to_parent(const char *target
, char **filename
)
176 char *buf
, *parent
, *last
= NULL
;
180 if (!target
|| *target
!= '/')
183 DBG(UTILS
, ul_debug("moving to %s parent", target
));
185 buf
= strdup(target
);
189 if (*(buf
+ 1) != '\0') {
190 last
= stripoff_last_component(buf
);
195 parent
= buf
&& *buf
? buf
: "/";
197 if (chdir(parent
) == -1) {
198 DBG(UTILS
, ul_debug("failed to chdir to %s: %m", parent
));
202 if (!getcwd(cwd
, sizeof(cwd
))) {
203 DBG(UTILS
, ul_debug("failed to obtain current directory: %m"));
207 if (strcmp(cwd
, parent
) != 0) {
209 "unexpected chdir (expected=%s, cwd=%s)", parent
, cwd
));
214 "current directory moved to %s [last_component='%s']",
221 memcpy(*filename
, ".", 2);
223 memmove(*filename
, last
, strlen(last
) + 1);
233 * Check if @path is on a read-only filesystem independently of file permissions.
235 int mnt_is_readonly(const char *path
)
237 if (access(path
, W_OK
) == 0)
244 #ifdef HAVE_UTIMENSAT
246 * access(2) returns EACCES on read-only FS:
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).
252 * - for a read-write filesystem with a read-only VFS node (aka -o remount,ro,bind)
255 struct timespec times
[2];
257 DBG(UTILS
, ul_debug(" doing utimensat() based write test"));
259 times
[0].tv_nsec
= UTIME_NOW
; /* atime */
260 times
[1].tv_nsec
= UTIME_OMIT
; /* mtime */
262 if (utimensat(AT_FDCWD
, path
, times
, 0) == -1)
263 return errno
== EROFS
;
269 #if defined(HAVE_STATX) && defined(HAVE_STRUCT_STATX) && defined(HAVE_STRUCT_STATX_STX_MNT_ID)
270 static int get_mnt_id( int fd
, const char *path
,
271 uint64_t *uniq_id
, int *id
)
274 struct statx sx
= { 0 };
275 int flags
= AT_STATX_DONT_SYNC
| AT_NO_AUTOMOUNT
;
278 flags
|= AT_EMPTY_PATH
;
281 rc
= statx(fd
, path
? path
: "", flags
,
288 # ifdef STATX_MNT_ID_UNIQUE
290 rc
= statx(fd
, path
? path
: "", flags
,
291 STATX_MNT_ID_UNIQUE
, &sx
);
293 if (rc
&& errno
== EINVAL
)
294 return -ENOSYS
; /* *_ID_UNIQUE unsupported? */
297 *uniq_id
= sx
.stx_mnt_id
;
304 #else /* HAVE_STATX && HAVE_STRUCT_STATX && AVE_STRUCT_STATX_STX_MNT_ID */
305 static 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__
)))
314 int mnt_id_from_fd(int fd
, uint64_t *uniq_id
, int *id
)
316 return get_mnt_id(fd
, NULL
, uniq_id
, id
);
322 * @uniq_id: returns STATX_MNT_ID_UNIQUE (optional)
323 * @id: returns STATX_MNT_ID (optional)
325 * Converts @path to ID.
327 * Returns: 0 on success, <0 on error
331 int mnt_id_from_path(const char *path
, uint64_t *uniq_id
, int *id
)
333 return get_mnt_id(-1, path
, uniq_id
, id
);
340 * Encode @str to be compatible with fstab/mtab
342 * Returns: newly allocated string or NULL in case of error.
344 char *mnt_mangle(const char *str
)
353 * Decode @str from fstab/mtab
355 * Returns: newly allocated string or NULL in case of error.
357 char *mnt_unmangle(const char *str
)
359 return unmangle(str
, NULL
);
363 * mnt_fstype_is_pseudofs:
364 * @type: filesystem name
366 * Returns: 1 for filesystems like proc, sysfs, ... or 0.
368 int mnt_fstype_is_pseudofs(const char *type
)
370 /* This array must remain sorted when adding new fstypes */
371 static const char *pseudofs
[] = {
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. */
401 "fuse.vmware-vmblock",
433 return !(bsearch(&type
, pseudofs
, ARRAY_SIZE(pseudofs
),
434 sizeof(char*), fstype_cmp
) == NULL
);
438 * mnt_fstype_is_netfs:
439 * @type: filesystem name
441 * Returns: 1 for filesystems like cifs, nfs, ... or 0.
443 int mnt_fstype_is_netfs(const char *type
)
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)
459 const char *mnt_statfs_get_fstype(struct statfs
*vfs
)
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";
546 * @type: filesystem type
547 * @pattern: filesystem name or comma delimited list of names
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".
554 * "bar" : "nofoo,bar" -> False (global "no" prefix)
556 * "bar" : "foo,bar" -> True
558 * "bar" : "foo,nobar" -> False
560 * Returns: 1 if type is matching, else 0. This function also returns
561 * 0 if @pattern is NULL and @type is non-NULL.
563 int mnt_match_fstype(const char *type
, const char *pattern
)
565 return match_fstype(type
, pattern
);
568 void mnt_free_filesystems(char **filesystems
)
574 for (p
= filesystems
; *p
; p
++)
579 static int add_filesystem(char ***filesystems
, char *name
)
588 for (n
= 0, p
= *filesystems
; *p
; p
++, n
++) {
589 if (strcmp(*p
, name
) == 0)
596 if (n
== 0 || !((n
+ 1) % MYCHUNK
)) {
597 size_t items
= ((n
+ 1 + MYCHUNK
) / MYCHUNK
) * MYCHUNK
;
598 char **x
= reallocarray(*filesystems
, items
, sizeof(char *));
605 (*filesystems
)[n
] = name
;
606 (*filesystems
)[n
+ 1] = NULL
;
611 mnt_free_filesystems(*filesystems
);
615 static int get_filesystems(const char *filename
, char ***filesystems
, const char *pattern
)
621 f
= fopen(filename
, "r" UL_CLOEXECSTR
);
625 DBG(UTILS
, ul_debug("reading filesystems list from: %s", filename
));
627 while (fgets(line
, sizeof(line
), f
)) {
628 char name
[sizeof(line
)];
630 if (*line
== '#' || strncmp(line
, "nodev", 5) == 0)
632 if (sscanf(line
, " %128[^\n ]\n", name
) != 1)
634 if (strcmp(name
, "*") == 0) {
636 break; /* end of the /etc/filesystems */
638 if (pattern
&& !mnt_match_fstype(name
, pattern
))
640 rc
= add_filesystem(filesystems
, name
);
650 * Always check the @filesystems pointer!
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.
660 int mnt_get_filesystems(char ***filesystems
, const char *pattern
)
669 rc
= get_filesystems(_PATH_FILESYSTEMS
, filesystems
, pattern
);
673 rc
= get_filesystems(_PATH_PROC_FILESYSTEMS
, filesystems
, pattern
);
674 if (rc
== 1 && *filesystems
)
675 rc
= 0; /* /proc/filesystems not found */
681 * Returns an allocated string with username or NULL.
683 char *mnt_get_username(const uid_t uid
)
687 char *buf
, *username
= NULL
;
689 buf
= malloc(UL_GETPW_BUFSIZ
);
693 if (!getpwuid_r(uid
, &pwd
, buf
, UL_GETPW_BUFSIZ
, &res
) && res
)
694 username
= strdup(pwd
.pw_name
);
700 int mnt_get_uid(const char *username
, uid_t
*uid
)
707 if (!username
|| !uid
)
710 buf
= malloc(UL_GETPW_BUFSIZ
);
714 if (!getpwnam_r(username
, &pwd
, buf
, UL_GETPW_BUFSIZ
, &pw
) && pw
) {
719 "cannot convert '%s' username to UID", username
));
729 int mnt_get_gid(const char *groupname
, gid_t
*gid
)
736 if (!groupname
|| !gid
)
739 buf
= malloc(UL_GETPW_BUFSIZ
);
743 if (!getgrnam_r(groupname
, &grp
, buf
, UL_GETPW_BUFSIZ
, &gr
) && gr
) {
748 "cannot convert '%s' groupname to GID", groupname
));
758 static int parse_uid_numeric(const char *value
, uid_t
*uid
)
766 rc
= ul_strtou64(value
, &num
, 10);
770 if (num
> ULONG_MAX
|| (uid_t
) num
!= num
) {
771 rc
= -(errno
= ERANGE
);
778 DBG(UTILS
, ul_debug("failed to convert '%s' to number [rc=%d, errno=%d]", value
, rc
, errno
));
782 /* Parse user_len-sized user; returns <0 on error, or 0 on success */
783 int mnt_parse_uid(const char *user
, size_t user_len
, uid_t
*uid
)
785 char *user_tofree
= NULL
;
792 if (user
[user_len
] != '\0') {
793 user
= user_tofree
= strndup(user
, user_len
);
798 rc
= mnt_get_uid(user
, uid
);
799 if (rc
!= 0 && isdigit(*user
))
800 rc
= parse_uid_numeric(user
, uid
);
806 static int parse_gid_numeric(const char *value
, gid_t
*gid
)
814 rc
= ul_strtou64(value
, &num
, 10);
818 if (num
> ULONG_MAX
|| (gid_t
) num
!= num
) {
819 rc
= -(errno
= ERANGE
);
826 DBG(UTILS
, ul_debug("failed to convert '%s' to number [rc=%d, errno=%d]", value
, rc
, errno
));
830 /* POSIX-parse group_len-sized group; -1 and errno set, or 0 on success */
831 int mnt_parse_gid(const char *group
, size_t group_len
, gid_t
*gid
)
833 char *group_tofree
= NULL
;
840 if (group
[group_len
] != '\0') {
841 group
= group_tofree
= strndup(group
, group_len
);
846 rc
= mnt_get_gid(group
, gid
);
847 if (rc
!= 0 && isdigit(*group
))
848 rc
= parse_gid_numeric(group
, gid
);
854 int mnt_parse_mode(const char *mode
, size_t mode_len
, mode_t
*uid
)
856 char buf
[sizeof(stringify_value(UINT32_MAX
))];
864 if (mode_len
> sizeof(buf
) - 1) {
865 rc
= -(errno
= ERANGE
);
868 mem2strcpy(buf
, mode
, mode_len
, sizeof(buf
));
870 rc
= ul_strtou32(buf
, &num
, 8);
874 rc
= -(errno
= ERANGE
);
881 DBG(UTILS
, ul_debug("failed to convert '%.*s' to mode [rc=%d, errno=%d]",
882 (int) mode_len
, mode
, rc
, errno
));
886 int mnt_in_group(gid_t gid
)
894 n
= getgroups(0, NULL
);
898 grps
= malloc(n
* sizeof(*grps
));
902 if (getgroups(n
, grps
) == n
) {
903 for (i
= 0; i
< n
; i
++) {
904 if (grps
[i
] == gid
) {
915 static int try_write(const char *filename
, const char *directory
)
922 DBG(UTILS
, ul_debug("try write %s dir: %s", filename
, directory
));
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...
928 if (eaccess(filename
, R_OK
|W_OK
) == 0) {
929 DBG(UTILS
, ul_debug(" access OK"));
933 if (errno
!= ENOENT
) {
934 DBG(UTILS
, ul_debug(" access FAILED"));
939 /* file does not exist; try if directory is writable */
940 if (eaccess(directory
, R_OK
|W_OK
) != 0)
943 DBG(UTILS
, ul_debug(" access %s [%s]", rc
? "FAILED" : "OK", directory
));
948 DBG(UTILS
, ul_debug(" doing open-write test"));
950 int fd
= open(filename
, O_RDWR
|O_CREAT
|O_CLOEXEC
,
951 S_IWUSR
|S_IRUSR
|S_IRGRP
|S_IROTH
);
961 * mnt_has_regular_mtab:
962 * @mtab: returns NULL
963 * @writable: returns 0
967 * Deprecated: libmount does not use /etc/mtab at all since v2.39.
969 int mnt_has_regular_mtab(const char **mtab
, int *writable
)
979 * Don't export this to libmount API -- utab is private library stuff.
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.
984 * Returns: 1 if utab is a regular file, and 0 in case of
985 * error (check errno for more details).
987 int mnt_has_regular_utab(const char **utab
, int *writable
)
991 const char *filename
= utab
&& *utab
? *utab
: mnt_get_utab_path();
998 DBG(UTILS
, ul_debug("utab: %s", filename
));
1000 rc
= lstat(filename
, &st
);
1004 if (S_ISREG(st
.st_mode
)) {
1006 *writable
= !try_write(filename
, NULL
);
1009 goto done
; /* it's not a regular file */
1013 char *dirname
= strdup(filename
);
1018 stripoff_last_component(dirname
); /* remove filename */
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
) {
1025 goto done
; /* probably EACCES */
1028 *writable
= !try_write(filename
, dirname
);
1034 DBG(UTILS
, ul_debug("%s: irregular/non-writable file", filename
));
1039 * mnt_get_swaps_path:
1041 * Returns: path to /proc/swaps or $LIBMOUNT_SWAPS.
1043 const char *mnt_get_swaps_path(void)
1045 const char *p
= safe_getenv("LIBMOUNT_SWAPS");
1046 return p
? : _PATH_PROC_SWAPS
;
1050 * mnt_get_fstab_path:
1052 * Returns: path to /etc/fstab or $LIBMOUNT_FSTAB.
1054 const char *mnt_get_fstab_path(void)
1056 const char *p
= safe_getenv("LIBMOUNT_FSTAB");
1057 return p
? : _PATH_MNTTAB
;
1061 * mnt_get_mtab_path:
1063 * This function returns the *default* location of the mtab file.
1066 * Returns: path to /etc/mtab or $LIBMOUNT_MTAB.
1068 * Deprecated: libmount uses /proc/self/mountinfo only.
1071 const char *mnt_get_mtab_path(void)
1073 const char *p
= safe_getenv("LIBMOUNT_MTAB");
1074 return p
? : _PATH_MOUNTED
;
1078 * Don't export this to libmount API -- utab is private library stuff.
1080 * Returns: path to /run/mount/utab or $LIBMOUNT_UTAB.
1082 const char *mnt_get_utab_path(void)
1084 const char *p
= safe_getenv("LIBMOUNT_UTAB");
1085 return p
? : MNT_PATH_UTAB
;
1089 /* returns file descriptor or -errno, @name returns a unique filename
1091 int mnt_open_uniq_filename(const char *filename
, char **name
)
1102 rc
= asprintf(&n
, "%s.XXXXXX", filename
);
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).
1109 oldmode
= umask(S_IRGRP
|S_IWGRP
|S_IXGRP
|
1110 S_IROTH
|S_IWOTH
|S_IXOTH
);
1112 fd
= mkstemp_cloexec(n
);
1117 if (fd
>= 0 && name
)
1126 * mnt_get_mountpoint:
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.
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().
1136 * Returns: allocated string with the target of the mounted device or NULL on error
1138 char *mnt_get_mountpoint(const char *path
)
1150 if (*mnt
== '/' && *(mnt
+ 1) == '\0')
1153 if (mnt_safe_stat(mnt
, &st
))
1158 char *p
= stripoff_last_component(mnt
);
1162 if (mnt_safe_stat(*mnt
? mnt
: "/", &st
))
1171 } while (mnt
&& *(mnt
+ 1) != '\0');
1173 memcpy(mnt
, "/", 2);
1175 DBG(UTILS
, ul_debug("%s mountpoint is %s", path
, mnt
));
1183 * Search for @name kernel command parameter.
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.
1189 * For example cmdline: "aaa bbb=BBB ccc"
1191 * @name is "aaa" --returns--> "aaa" (pointer to @name)
1192 * @name is "bbb=" --returns--> "BBB" (allocated)
1193 * @name is "foo" --returns--> NULL
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.
1199 char *mnt_get_kernel_cmdline_option(const char *name
)
1204 char *p
, *res
= NULL
, *mem
= NULL
;
1205 char buf
[BUFSIZ
]; /* see kernel include/asm-generic/setup.h: COMMAND_LINE_SIZE */
1208 if (!name
|| !name
[0])
1212 path
= getenv("LIBMOUNT_KERNEL_CMDLINE");
1214 path
= _PATH_PROC_CMDLINE
;
1216 path
= _PATH_PROC_CMDLINE
;
1218 f
= fopen(path
, "r" UL_CLOEXECSTR
);
1222 p
= fgets(buf
, sizeof(buf
), f
);
1225 if (!p
|| !*p
|| *p
== '\n')
1228 p
= strstr(p
, " -- ");
1230 /* no more kernel args after this */
1234 buf
[len
- 1] = '\0'; /* remove last '\n' */
1238 if (name
[len
- 1] == '=')
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 */
1252 while (*p
&& !isblank(*p
)) /* jump to the end of the argument */
1257 res
= mem
= strdup(v
);
1261 res
= (char *) name
; /* option without '=' */
1262 /* don't break -- keep scanning for more options */
1269 * mnt_guess_system_root:
1270 * @devno: device number or zero
1271 * @cache: paths cache or NULL
1272 * @path: returns allocated path
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.
1277 * The function uses /sys to convert devno to device name.
1279 * Returns: 0 = success, 1 = not found, <0 = error
1283 int mnt_guess_system_root(dev_t devno
, struct libmnt_cache
*cache
, char **path
)
1286 char *dev
= NULL
, *spec
= NULL
;
1292 DBG(UTILS
, ul_debug("guessing system root [devno %u:%u]", major(devno
), minor(devno
)));
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.
1297 if (major(devno
) > 0) {
1298 dev
= sysfs_devno_to_devpath(devno
, buf
, sizeof(buf
));
1300 DBG(UTILS
, ul_debug(" devno converted to %s", dev
));
1305 /* Let's try to use root= kernel command line option
1307 spec
= mnt_get_kernel_cmdline_option("root=");
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
));
1315 DBG(UTILS
, ul_debug(" root=%s converted to %s", spec
, dev
));
1319 /* hexhex notation */
1320 } else if (isxdigit_string(spec
)) {
1325 n
= strtoul(spec
, &end
, 16);
1327 if (errno
|| spec
== end
|| (end
&& *end
))
1328 DBG(UTILS
, ul_debug(" failed to parse root='%s'", spec
));
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
));
1335 DBG(UTILS
, ul_debug(" root=%s converted to %s", spec
, dev
));
1340 /* devname or PARTUUID= etc. */
1342 DBG(UTILS
, ul_debug(" converting root='%s'", spec
));
1344 dev
= mnt_resolve_spec(spec
, cache
);
1351 *path
= allocated
? dev
: strdup(dev
);
1361 static int test_match_fstype(struct libmnt_test
*ts
__attribute__((unused
)),
1362 int argc
, char *argv
[])
1367 char *type
= argv
[1];
1368 char *pattern
= argv
[2];
1370 printf("%s\n", mnt_match_fstype(type
, pattern
) ? "MATCH" : "NOT-MATCH");
1374 static int test_match_options(struct libmnt_test
*ts
__attribute__((unused
)),
1375 int argc
, char *argv
[])
1380 char *optstr
= argv
[1];
1381 char *pattern
= argv
[2];
1383 printf("%s\n", mnt_match_options(optstr
, pattern
) ? "MATCH" : "NOT-MATCH");
1387 static int test_startswith(struct libmnt_test
*ts
__attribute__((unused
)),
1388 int argc
, char *argv
[])
1393 char *optstr
= argv
[1];
1394 char *pattern
= argv
[2];
1396 printf("%s\n", ul_startswith(optstr
, pattern
) ? "YES" : "NOT");
1400 static int test_endswith(struct libmnt_test
*ts
__attribute__((unused
)),
1401 int argc
, char *argv
[])
1406 char *optstr
= argv
[1];
1407 char *pattern
= argv
[2];
1409 printf("%s\n", ul_endswith(optstr
, pattern
) ? "YES" : "NOT");
1413 static int test_mountpoint(struct libmnt_test
*ts
__attribute__((unused
)),
1414 int argc
, char *argv
[])
1419 char *path
= canonicalize_path(argv
[1]),
1420 *mnt
= path
? mnt_get_mountpoint(path
) : NULL
;
1422 printf("%s: %s\n", argv
[1], mnt
? : "unknown");
1428 static int test_filesystems(struct libmnt_test
*ts
__attribute__((unused
)),
1429 int argc
, char *argv
[])
1431 char **filesystems
= NULL
;
1434 if (argc
!= 1 && argc
!= 2)
1437 rc
= mnt_get_filesystems(&filesystems
, argc
== 2 ? argv
[1] : NULL
);
1438 if (!rc
&& filesystems
) {
1440 for (p
= filesystems
; *p
; p
++)
1442 mnt_free_filesystems(filesystems
);
1447 static int test_chdir(struct libmnt_test
*ts
__attribute__((unused
)),
1448 int argc
, char *argv
[])
1454 char *path
= canonicalize_path(argv
[1]),
1460 rc
= mnt_chdir_to_parent(path
, &last
);
1462 printf("path='%s', abs='%s', last='%s'\n",
1463 argv
[1], path
, last
);
1470 static int test_kernel_cmdline(struct libmnt_test
*ts
__attribute__((unused
)),
1471 int argc
, char *argv
[])
1476 char *name
= argv
[1];
1479 res
= mnt_get_kernel_cmdline_option(name
);
1481 printf("'%s' not found\n", name
);
1482 else if (res
== name
)
1483 printf("'%s' found\n", name
);
1485 printf("'%s' found, argument: '%s'\n", name
, res
);
1493 static int test_guess_root(struct libmnt_test
*ts
__attribute__((unused
)),
1494 int argc
, char *argv
[])
1503 if (sscanf(argv
[1], "%u:%u", &x
, &y
) != 2)
1505 devno
= makedev(x
, y
);
1508 rc
= mnt_guess_system_root(devno
, NULL
, &real
);
1512 fputs("not found\n", stdout
);
1514 printf("%s\n", real
);
1520 static int test_mkdir(struct libmnt_test
*ts
__attribute__((unused
)),
1521 int argc
, char *argv
[])
1528 rc
= ul_mkdir_p(argv
[1], S_IRWXU
|
1532 printf("mkdir %s failed\n", argv
[1]);
1536 static int test_statfs_type(struct libmnt_test
*ts
__attribute__((unused
)),
1537 int argc
, char *argv
[])
1545 rc
= statfs(argv
[1], &vfs
);
1547 printf("%s: statfs failed: %m\n", argv
[1]);
1549 printf("%-30s: statfs type: %-12s [0x%lx]\n", argv
[1],
1550 mnt_statfs_get_fstype(&vfs
),
1555 static int tests_parse_uid(struct libmnt_test
*ts
__attribute__((unused
)),
1556 int argc
, char *argv
[])
1561 char *str
= argv
[1];
1562 uid_t uid
= (uid_t
) -1;
1565 rc
= mnt_parse_uid(str
, strlen(str
), &uid
);
1567 printf("failed: rc=%d: %m\n", rc
);
1569 printf("'%s' --> %lu\n", str
, (unsigned long) uid
);
1574 static int tests_parse_gid(struct libmnt_test
*ts
__attribute__((unused
)),
1575 int argc
, char *argv
[])
1580 char *str
= argv
[1];
1581 gid_t gid
= (gid_t
) -1;
1584 rc
= mnt_parse_gid(str
, strlen(str
), &gid
);
1586 printf("failed: rc=%d: %m\n", rc
);
1588 printf("'%s' --> %lu\n", str
, (unsigned long) gid
);
1593 static int tests_parse_mode(struct libmnt_test
*ts
__attribute__((unused
)),
1594 int argc
, char *argv
[])
1599 char *str
= argv
[1];
1600 mode_t mod
= (mode_t
) -1;
1603 rc
= mnt_parse_mode(str
, strlen(str
), &mod
);
1605 printf("failed: rc=%d: %m\n", rc
);
1609 xstrmode(mod
, modstr
);
1610 printf("'%s' --> %04o [%s]\n", str
, (unsigned int) mod
, modstr
);
1615 static int tests_stat(struct libmnt_test
*ts
__attribute__((unused
)),
1616 int argc
, char *argv
[])
1621 char *path
= argv
[1];
1625 if (strcmp(argv
[0], "--lstat") == 0)
1626 rc
= mnt_safe_lstat(path
, &st
);
1628 rc
= mnt_safe_stat(path
, &st
);
1630 printf("%s: failed: rc=%d: %m\n", path
, rc
);
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");
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
);
1645 int main(int argc
, char *argv
[])
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>" },
1667 return mnt_run_test(tss
, argc
, argv
);
1670 #endif /* TEST_PROGRAM */