]>
git.ipfire.org Git - thirdparty/util-linux.git/blob - libmount/src/utils.c
3cab936441de596186a0163af777693ca02bca9c
2 * Copyright (C) 2008-2009 Karel Zak <kzak@redhat.com>
4 * This file may be redistributed under the terms of the
5 * GNU Lesser General Public License.
11 * @short_description: misc utils.
19 #include "pathnames.h"
22 #include "canonicalize.h"
26 int append_string(char **a
, const char *b
)
37 return !*a
? -ENOMEM
: 0;
41 bl
= b
? strlen(b
) : 0;
43 tmp
= realloc(*a
, al
+ bl
+ 1);
47 memcpy((*a
) + al
, b
, bl
+ 1);
52 * Return 1 if the file is not accessible or empty
54 int is_file_empty(const char *name
)
59 return (stat(name
, &st
) != 0 || st
.st_size
== 0);
62 int mnt_valid_tagname(const char *tagname
)
64 if (tagname
&& *tagname
&& (
65 strcmp("UUID", tagname
) == 0 ||
66 strcmp("LABEL", tagname
) == 0 ||
67 strcmp("PARTUUID", tagname
) == 0 ||
68 strcmp("PARTLABEL", tagname
) == 0))
74 int mnt_parse_offset(const char *str
, size_t len
, uintmax_t *res
)
82 p
= strndup(str
, len
);
86 if (strtosize(p
, res
))
92 /* used as a callback by bsearch in mnt_fstype_is_pseudofs() */
93 static int fstype_cmp(const void *v1
, const void *v2
)
95 const char *s1
= *(const char **)v1
;
96 const char *s2
= *(const char **)v2
;
98 return strcmp(s1
, s2
);
101 /* returns basename and keeps dirname in the @path, if @path is "/" (root)
102 * then returns empty string */
103 char *stripoff_last_component(char *path
)
105 char *p
= path
? strrchr(path
, '/') : NULL
;
114 * Note that the @target has to be an absolute path (so at least "/"). The
115 * @filename returns an allocated buffer with the last path component, for example:
117 * mnt_chdir_to_parent("/mnt/test", &buf) ==> chdir("/mnt"), buf="test"
119 int mnt_chdir_to_parent(const char *target
, char **filename
)
121 char *buf
, *parent
, *last
= NULL
;
125 if (!target
|| *target
!= '/')
128 DBG(UTILS
, mnt_debug("moving to %s parent", target
));
130 buf
= strdup(target
);
134 if (*(buf
+ 1) != '\0') {
135 last
= stripoff_last_component(buf
);
140 parent
= buf
&& *buf
? buf
: "/";
142 if (chdir(parent
) == -1) {
143 DBG(UTILS
, mnt_debug("failed to chdir to %s: %m", parent
));
147 if (!getcwd(cwd
, sizeof(cwd
))) {
148 DBG(UTILS
, mnt_debug("failed to obtain current directory: %m"));
152 if (strcmp(cwd
, parent
) != 0) {
153 DBG(UTILS
, mnt_debug(
154 "unexpected chdir (expected=%s, cwd=%s)", parent
, cwd
));
159 "current directory moved to %s [last_component='%s']",
166 memcpy(*filename
, ".", 2);
168 memmove(*filename
, last
, strlen(last
) + 1);
178 * Check if @path is on a read-only filesystem independently of file permissions.
180 int mnt_is_readonly(const char *path
)
182 if (access(path
, W_OK
) == 0)
191 * access(2) returns EACCES on read-only FS:
193 * - for set-uid application if one component of the path is not
194 * accessible for the current rUID. (Note that euidaccess(2) does not
195 * check for EROFS at all).
197 * - for a read-write filesystem with a read-only VFS node (aka -o remount,ro,bind)
200 struct timespec times
[2];
202 times
[0].tv_nsec
= UTIME_NOW
; /* atime */
203 times
[1].tv_nsec
= UTIME_OMIT
; /* mtime */
205 if (utimensat(AT_FDCWD
, path
, times
, 0) == -1)
206 return errno
== EROFS
;
216 * Encode @str to be compatible with fstab/mtab
218 * Returns: newly allocated string or NULL in case of error.
220 char *mnt_mangle(const char *str
)
229 * Decode @str from fstab/mtab
231 * Returns: newly allocated string or NULL in case of error.
233 char *mnt_unmangle(const char *str
)
235 return unmangle(str
, NULL
);
239 * mnt_fstype_is_pseudofs:
240 * @type: filesystem name
242 * Returns: 1 for filesystems like proc, sysfs, ... or 0.
244 int mnt_fstype_is_pseudofs(const char *type
)
246 /* This array must remain sorted when adding new fstypes */
247 static const char *pseudofs
[] = {
260 "fuse.gvfs-fuse-daemon",
281 return !(bsearch(&type
, pseudofs
, ARRAY_SIZE(pseudofs
),
282 sizeof(char*), fstype_cmp
) == NULL
);
286 * mnt_fstype_is_netfs:
287 * @type: filesystem name
289 * Returns: 1 for filesystems like cifs, nfs, ... or 0.
291 int mnt_fstype_is_netfs(const char *type
)
295 if (strcmp(type
, "cifs") == 0 ||
296 strcmp(type
, "smbfs") == 0 ||
297 strncmp(type
,"nfs", 3) == 0 ||
298 strcmp(type
, "afs") == 0 ||
299 strcmp(type
, "ncpfs") == 0 ||
300 strncmp(type
,"9p", 2) == 0)
307 * @type: filesystem type
308 * @pattern: filesystem name or comma delimited list of names
310 * The @pattern list of filesystems can be prefixed with a global
311 * "no" prefix to invert matching of the whole list. The "no" could
312 * also be used for individual items in the @pattern list. So,
313 * "nofoo,bar" has the same meaning as "nofoo,nobar".
315 * "bar" : "nofoo,bar" -> False (global "no" prefix)
317 * "bar" : "foo,bar" -> True
319 * "bar" : "foo,nobar" -> False
321 * Returns: 1 if type is matching, else 0. This function also returns
322 * 0 if @pattern is NULL and @type is non-NULL.
324 int mnt_match_fstype(const char *type
, const char *pattern
)
326 return match_fstype(type
, pattern
);
330 /* Returns 1 if needle found or noneedle not found in haystack
331 * Otherwise returns 0
333 static int check_option(const char *haystack
, size_t len
,
334 const char *needle
, size_t needle_len
)
339 if (needle_len
>= 1 && *needle
== '+') {
342 } else if (needle_len
>= 2 && !strncmp(needle
, "no", 2)) {
348 for (p
= haystack
; p
&& p
< haystack
+ len
; p
++) {
349 char *sep
= strchr(p
, ',');
350 size_t plen
= sep
? (size_t) (sep
- p
) :
351 len
- (p
- haystack
);
353 if (plen
== needle_len
) {
354 if (!strncmp(p
, needle
, plen
))
355 return !no
; /* foo or nofoo was found */
360 return no
; /* foo or nofoo was not found */
365 * @optstr: options string
366 * @pattern: comma delimited list of options
368 * The "no" could be used for individual items in the @options list. The "no"
369 * prefix does not have a global meaning.
371 * Unlike fs type matching, nonetdev,user and nonetdev,nouser have
372 * DIFFERENT meanings; each option is matched explicitly as specified.
374 * The "no" prefix interpretation could be disabled by the "+" prefix, for example
375 * "+noauto" matches if @optstr literally contains the "noauto" string.
377 * "xxx,yyy,zzz" : "nozzz" -> False
379 * "xxx,yyy,zzz" : "xxx,noeee" -> True
381 * "bar,zzz" : "nofoo" -> True
383 * "nofoo,bar" : "+nofoo" -> True
385 * "bar,zzz" : "+nofoo" -> False
388 * Returns: 1 if pattern is matching, else 0. This function also returns 0
389 * if @pattern is NULL and @optstr is non-NULL.
391 int mnt_match_options(const char *optstr
, const char *pattern
)
394 size_t len
, optstr_len
= 0;
396 if (!pattern
&& !optstr
)
401 len
= strlen(pattern
);
403 optstr_len
= strlen(optstr
);
405 for (p
= pattern
; p
< pattern
+ len
; p
++) {
406 char *sep
= strchr(p
, ',');
407 size_t plen
= sep
? (size_t) (sep
- p
) :
411 continue; /* if two ',' appear in a row */
413 if (!check_option(optstr
, optstr_len
, p
, plen
))
414 return 0; /* any match failure means failure */
419 /* no match failures in list means success */
423 void mnt_free_filesystems(char **filesystems
)
429 for (p
= filesystems
; *p
; p
++)
434 static int add_filesystem(char ***filesystems
, char *name
)
443 for (n
= 0, p
= *filesystems
; *p
; p
++, n
++) {
444 if (strcmp(*p
, name
) == 0)
451 if (n
== 0 || !((n
+ 1) % MYCHUNK
)) {
452 size_t items
= ((n
+ 1 + MYCHUNK
) / MYCHUNK
) * MYCHUNK
;
453 char **x
= realloc(*filesystems
, items
* sizeof(char *));
462 (*filesystems
)[n
] = name
;
463 (*filesystems
)[n
+ 1] = NULL
;
466 mnt_free_filesystems(*filesystems
);
470 static int get_filesystems(const char *filename
, char ***filesystems
, const char *pattern
)
476 f
= fopen(filename
, "r" UL_CLOEXECSTR
);
480 DBG(UTILS
, mnt_debug("reading filesystems list from: %s", filename
));
482 while (fgets(line
, sizeof(line
), f
)) {
483 char name
[sizeof(line
)];
485 if (*line
== '#' || strncmp(line
, "nodev", 5) == 0)
487 if (sscanf(line
, " %128[^\n ]\n", name
) != 1)
489 if (strcmp(name
, "*") == 0) {
491 break; /* end of the /etc/filesystems */
493 if (pattern
&& !mnt_match_fstype(name
, pattern
))
495 rc
= add_filesystem(filesystems
, name
);
505 * Always check the @filesystems pointer!
509 * ...mount will try to read the file /etc/filesystems, or, if that does not
510 * exist, /proc/filesystems. All of the filesystem types listed there will
511 * be tried, except for those that are labeled "nodev" (e.g., devpts,
512 * proc and nfs). If /etc/filesystems ends in a line with a single * only,
513 * mount will read /proc/filesystems afterwards.
515 int mnt_get_filesystems(char ***filesystems
, const char *pattern
)
524 rc
= get_filesystems(_PATH_FILESYSTEMS
, filesystems
, pattern
);
528 rc
= get_filesystems(_PATH_PROC_FILESYSTEMS
, filesystems
, pattern
);
529 if (rc
== 1 && *filesystems
)
530 rc
= 0; /* /proc/filesystems not found */
535 static size_t get_pw_record_size(void)
537 #ifdef _SC_GETPW_R_SIZE_MAX
538 long sz
= sysconf(_SC_GETPW_R_SIZE_MAX
);
546 * Returns an allocated string with username or NULL.
548 char *mnt_get_username(const uid_t uid
)
552 size_t sz
= get_pw_record_size();
553 char *buf
, *username
= NULL
;
559 if (!getpwuid_r(uid
, &pwd
, buf
, sz
, &res
) && res
)
560 username
= strdup(pwd
.pw_name
);
566 int mnt_get_uid(const char *username
, uid_t
*uid
)
571 size_t sz
= get_pw_record_size();
574 if (!username
|| !uid
)
581 if (!getpwnam_r(username
, &pwd
, buf
, sz
, &pw
) && pw
) {
585 DBG(UTILS
, mnt_debug(
586 "cannot convert '%s' username to UID", username
));
587 rc
= errno
? -errno
: -EINVAL
;
594 int mnt_get_gid(const char *groupname
, gid_t
*gid
)
599 size_t sz
= get_pw_record_size();
602 if (!groupname
|| !gid
)
609 if (!getgrnam_r(groupname
, &grp
, buf
, sz
, &gr
) && gr
) {
613 DBG(UTILS
, mnt_debug(
614 "cannot convert '%s' groupname to GID", groupname
));
615 rc
= errno
? -errno
: -EINVAL
;
622 int mnt_in_group(gid_t gid
)
630 n
= getgroups(0, NULL
);
634 grps
= malloc(n
* sizeof(*grps
));
638 if (getgroups(n
, grps
) == n
) {
639 for (i
= 0; i
< n
; i
++) {
640 if (grps
[i
] == gid
) {
651 static int try_write(const char *filename
)
658 fd
= open(filename
, O_RDWR
|O_CREAT
|O_CLOEXEC
,
659 S_IWUSR
|S_IRUSR
|S_IRGRP
|S_IROTH
);
668 * mnt_has_regular_mtab:
669 * @mtab: returns path to mtab
670 * @writable: returns 1 if the file is writable
672 * If the file does not exist and @writable argument is not NULL, then it will
673 * try to create the file.
675 * Returns: 1 if /etc/mtab is a regular file, and 0 in case of error (check
676 * errno for more details).
678 int mnt_has_regular_mtab(const char **mtab
, int *writable
)
682 const char *filename
= mtab
&& *mtab
? *mtab
: mnt_get_mtab_path();
689 DBG(UTILS
, mnt_debug("mtab: %s", filename
));
691 rc
= lstat(filename
, &st
);
695 if (S_ISREG(st
.st_mode
)) {
697 *writable
= !try_write(filename
);
703 /* try to create the file */
705 *writable
= !try_write(filename
);
711 DBG(UTILS
, mnt_debug("%s: irregular/non-writable", filename
));
716 * Don't export this to libmount API -- utab is private library stuff.
718 * If the file does not exist and @writable argument is not NULL, then it will
719 * try to create the directory (e.g. /run/mount) and the file.
721 * Returns: 1 if utab is a regular file, and 0 in case of
722 * error (check errno for more details).
724 int mnt_has_regular_utab(const char **utab
, int *writable
)
728 const char *filename
= utab
&& *utab
? *utab
: mnt_get_utab_path();
735 DBG(UTILS
, mnt_debug("utab: %s", filename
));
737 rc
= lstat(filename
, &st
);
741 if (S_ISREG(st
.st_mode
)) {
743 *writable
= !try_write(filename
);
746 goto done
; /* it's not a regular file */
750 char *dirname
= strdup(filename
);
755 stripoff_last_component(dirname
); /* remove filename */
757 rc
= mkdir(dirname
, S_IWUSR
|
758 S_IRUSR
|S_IRGRP
|S_IROTH
|
759 S_IXUSR
|S_IXGRP
|S_IXOTH
);
761 if (rc
&& errno
!= EEXIST
)
762 goto done
; /* probably EACCES */
764 *writable
= !try_write(filename
);
769 DBG(UTILS
, mnt_debug("%s: irregular/non-writable file", filename
));
774 * mnt_get_swaps_path:
776 * Returns: path to /proc/swaps or $LIBMOUNT_SWAPS.
778 const char *mnt_get_swaps_path(void)
780 const char *p
= safe_getenv("LIBMOUNT_SWAPS");
781 return p
? : _PATH_PROC_SWAPS
;
785 * mnt_get_fstab_path:
787 * Returns: path to /etc/fstab or $LIBMOUNT_FSTAB.
789 const char *mnt_get_fstab_path(void)
791 const char *p
= safe_getenv("LIBMOUNT_FSTAB");
792 return p
? : _PATH_MNTTAB
;
798 * This function returns the *default* location of the mtab file. The result does
799 * not have to be writable. See also mnt_has_regular_mtab().
801 * Returns: path to /etc/mtab or $LIBMOUNT_MTAB.
803 const char *mnt_get_mtab_path(void)
805 const char *p
= safe_getenv("LIBMOUNT_MTAB");
806 return p
? : _PATH_MOUNTED
;
810 * Don't export this to libmount API -- utab is private library stuff.
812 * Returns: path to /run/mount/utab (or /dev/.mount/utab) or $LIBMOUNT_UTAB.
814 const char *mnt_get_utab_path(void)
817 const char *p
= safe_getenv("LIBMOUNT_UTAB");
822 if (stat(MNT_RUNTIME_TOPDIR
, &st
) == 0)
823 return MNT_PATH_UTAB
;
825 return MNT_PATH_UTAB_OLD
;
829 /* returns file descriptor or -errno, @name returns a unique filename
831 int mnt_open_uniq_filename(const char *filename
, char **name
)
842 rc
= asprintf(&n
, "%s.XXXXXX", filename
);
846 /* This is for very old glibc and for compatibility with Posix, which says
847 * nothing about mkstemp() mode. All sane glibc use secure mode (0600).
849 oldmode
= umask(S_IRGRP
|S_IWGRP
|S_IXGRP
|
850 S_IROTH
|S_IWOTH
|S_IXOTH
);
851 fd
= mkostemp(n
, O_RDWR
|O_CREAT
|O_EXCL
|O_CLOEXEC
);
859 return fd
< 0 ? -errno
: fd
;
863 * mnt_get_mountpoint:
866 * This function finds the mountpoint that a given path resides in. @path
867 * should be canonicalized. The returned pointer should be freed by the caller.
869 * Returns: allocated string with the target of the mounted device or NULL on error
871 char *mnt_get_mountpoint(const char *path
)
882 if (*mnt
== '/' && *(mnt
+ 1) == '\0')
890 char *p
= stripoff_last_component(mnt
);
894 if (stat(*mnt
? mnt
: "/", &st
))
903 } while (mnt
&& *(mnt
+ 1) != '\0');
907 DBG(UTILS
, mnt_debug("%s mountpoint is %s", path
, mnt
));
914 char *mnt_get_fs_root(const char *path
, const char *mnt
)
916 char *m
= (char *) mnt
, *res
;
921 m
= mnt_get_mountpoint(path
);
926 p
= sz
> 1 ? path
+ sz
: path
;
931 res
= *p
? strdup(p
) : strdup("/");
932 DBG(UTILS
, mnt_debug("%s fs-root is %s", path
, res
));
937 * Search for @name kernel command parametr.
939 * Returns newly allocated string with a parameter argument if the @name is
940 * specified as "name=" or returns pointer to @name or returns NULL if not
943 * For example cmdline: "aaa bbb=BBB ccc"
945 * @name is "aaa" --returns--> "aaa" (pointer to @name)
946 * @name is "bbb=" --returns--> "BBB" (allocated)
947 * @name is "foo" --returns--> NULL
949 char *mnt_get_kernel_cmdline_option(const char *name
)
954 char *p
, *res
= NULL
;
955 char buf
[BUFSIZ
]; /* see kernel include/asm-generic/setup.h: COMMAND_LINE_SIZE */
956 const char *path
= _PATH_PROC_CMDLINE
;
962 path
= getenv("LIBMOUNT_KERNEL_CMDLINE");
964 path
= _PATH_PROC_CMDLINE
;
966 f
= fopen(path
, "r" UL_CLOEXECSTR
);
970 p
= fgets(buf
, sizeof(buf
), f
);
973 if (!p
|| !*p
|| *p
== '\n')
977 *(buf
+ len
- 1) = '\0'; /* remove last '\n' */
980 if (len
&& *(name
+ len
- 1) == '=')
983 for ( ; p
&& *p
; p
++) {
984 if (!(p
= strstr(p
, name
)))
985 break; /* not found the option */
986 if (p
!= buf
&& !isblank(*(p
- 1)))
987 continue; /* no space before the option */
988 if (!val
&& *(p
+ len
) != '\0' && !isblank(*(p
+ len
)))
989 continue; /* no space after the option */
993 while (*p
&& !isblank(*p
)) /* jump to the end of the argument */
999 res
= (char *) name
; /* option without '=' */
1006 int mkdir_p(const char *path
, mode_t mode
)
1011 if (!path
|| !*path
)
1014 dir
= p
= strdup(path
);
1022 char *e
= strchr(p
, '/');
1026 rc
= mkdir(dir
, mode
);
1027 if (rc
&& errno
!= EEXIST
)
1037 DBG(UTILS
, mnt_debug("%s mkdir %s", path
, rc
? "FAILED" : "SUCCESS"));
1044 int test_match_fstype(struct libmnt_test
*ts
, int argc
, char *argv
[])
1046 char *type
= argv
[1];
1047 char *pattern
= argv
[2];
1049 printf("%s\n", mnt_match_fstype(type
, pattern
) ? "MATCH" : "NOT-MATCH");
1053 int test_match_options(struct libmnt_test
*ts
, int argc
, char *argv
[])
1055 char *optstr
= argv
[1];
1056 char *pattern
= argv
[2];
1058 printf("%s\n", mnt_match_options(optstr
, pattern
) ? "MATCH" : "NOT-MATCH");
1062 int test_startswith(struct libmnt_test
*ts
, int argc
, char *argv
[])
1064 char *optstr
= argv
[1];
1065 char *pattern
= argv
[2];
1067 printf("%s\n", startswith(optstr
, pattern
) ? "YES" : "NOT");
1071 int test_endswith(struct libmnt_test
*ts
, int argc
, char *argv
[])
1073 char *optstr
= argv
[1];
1074 char *pattern
= argv
[2];
1076 printf("%s\n", endswith(optstr
, pattern
) ? "YES" : "NOT");
1080 int test_appendstr(struct libmnt_test
*ts
, int argc
, char *argv
[])
1082 char *str
= strdup(argv
[1]);
1083 const char *ap
= argv
[2];
1085 append_string(&str
, ap
);
1086 printf("new string: '%s'\n", str
);
1092 int test_mountpoint(struct libmnt_test
*ts
, int argc
, char *argv
[])
1094 char *path
= canonicalize_path(argv
[1]),
1095 *mnt
= path
? mnt_get_mountpoint(path
) : NULL
;
1097 printf("%s: %s\n", argv
[1], mnt
? : "unknown");
1103 int test_fsroot(struct libmnt_test
*ts
, int argc
, char *argv
[])
1105 char *path
= canonicalize_path(argv
[1]),
1106 *mnt
= path
? mnt_get_fs_root(path
, NULL
) : NULL
;
1108 printf("%s: %s\n", argv
[1], mnt
? : "unknown");
1114 int test_filesystems(struct libmnt_test
*ts
, int argc
, char *argv
[])
1116 char **filesystems
= NULL
;
1119 rc
= mnt_get_filesystems(&filesystems
, argc
? argv
[1] : NULL
);
1122 for (p
= filesystems
; *p
; p
++)
1124 mnt_free_filesystems(filesystems
);
1129 int test_chdir(struct libmnt_test
*ts
, int argc
, char *argv
[])
1132 char *path
= canonicalize_path(argv
[1]),
1138 rc
= mnt_chdir_to_parent(path
, &last
);
1140 printf("path='%s', abs='%s', last='%s'\n",
1141 argv
[1], path
, last
);
1148 int test_kernel_cmdline(struct libmnt_test
*ts
, int argc
, char *argv
[])
1150 char *name
= argv
[1];
1153 res
= mnt_get_kernel_cmdline_option(name
);
1155 printf("'%s' not found\n", name
);
1156 else if (res
== name
)
1157 printf("'%s' found\n", name
);
1159 printf("'%s' found, argument: '%s'\n", name
, res
);
1166 int test_mkdir(struct libmnt_test
*ts
, int argc
, char *argv
[])
1170 rc
= mkdir_p(argv
[1], S_IRWXU
|
1174 printf("mkdir %s failed\n", argv
[1]);
1179 int main(int argc
, char *argv
[])
1181 struct libmnt_test tss
[] = {
1182 { "--match-fstype", test_match_fstype
, "<type> <pattern> FS types matching" },
1183 { "--match-options", test_match_options
, "<options> <pattern> options matching" },
1184 { "--filesystems", test_filesystems
, "[<pattern>] list /{etc,proc}/filesystems" },
1185 { "--starts-with", test_startswith
, "<string> <prefix>" },
1186 { "--ends-with", test_endswith
, "<string> <prefix>" },
1187 { "--append-string", test_appendstr
, "<string> <appendix>" },
1188 { "--mountpoint", test_mountpoint
, "<path>" },
1189 { "--fs-root", test_fsroot
, "<path>" },
1190 { "--cd-parent", test_chdir
, "<path>" },
1191 { "--kernel-cmdline",test_kernel_cmdline
, "<option> | <option>=" },
1192 { "--mkdir", test_mkdir
, "<path>" },
1197 return mnt_run_test(tss
, argc
, argv
);
1200 #endif /* TEST_PROGRAM */