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"
25
int endswith(const char *s, const char *sx)
39
return !strcmp(s + off, sx);
42
int startswith(const char *s, const char *sx)
53
return !strncmp(s, sx, off);
56
/* returns basename and keeps dirname in the @path, if @path is "/" (root)
57
* then returns empty string */
58
static char *stripoff_last_component(char *path)
60
char *p = path ? strrchr(path, '/') : NULL;
68
/* Note that the @target has to be absolute path (so at least "/")
70
int mnt_chdir_to_parent(const char *target, char **filename)
72
char *path, *last = NULL;
76
if (!target || *target != '/')
79
path = strdup(target);
83
if (*(path + 1) != '\0') {
84
last = stripoff_last_component(path);
89
*path = '/'; /* root */
91
if (chdir(path) == -1) {
92
DBG(UTILS, mnt_debug("failed to chdir to %s: %m", path));
96
if (!getcwd(cwd, sizeof(cwd))) {
97
DBG(UTILS, mnt_debug("failed to obtain current directory: %m"));
101
if (strcmp(cwd, path) != 0) {
102
DBG(UTILS, mnt_debug("path moved (%s -> %s)", path, cwd));
106
DBG(CXT, mnt_debug("current directory moved to %s", path));
111
memcpy(*filename, ".", 2);
113
memcpy(*filename, last, strlen(last) + 1);
124
* Encode @str to be compatible with fstab/mtab
126
* Returns: new allocated string or NULL in case of error.
128
char *mnt_mangle(const char *str)
137
* Decode @str from fstab/mtab
139
* Returns: new allocated string or NULL in case of error.
141
char *mnt_unmangle(const char *str)
143
return unmangle(str, NULL);
147
* mnt_fstype_is_pseudofs:
148
* @type: filesystem name
150
* Returns: 1 for filesystems like proc, sysfs, ... or 0.
152
int mnt_fstype_is_pseudofs(const char *type)
156
if (strcmp(type, "none") == 0 ||
157
strcmp(type, "proc") == 0 ||
158
strcmp(type, "tmpfs") == 0 ||
159
strcmp(type, "sysfs") == 0 ||
160
strcmp(type, "autofs") == 0 ||
161
strcmp(type, "devpts") == 0||
162
strcmp(type, "cgroup") == 0 ||
163
strcmp(type, "devtmpfs") == 0 ||
164
strcmp(type, "devfs") == 0 ||
165
strcmp(type, "dlmfs") == 0 ||
166
strcmp(type, "cpuset") == 0 ||
167
strcmp(type, "securityfs") == 0 ||
168
strcmp(type, "hugetlbfs") == 0 ||
169
strcmp(type, "rpc_pipefs") == 0 ||
170
strcmp(type, "fusectl") == 0 ||
171
strcmp(type, "mqueue") == 0 ||
172
strcmp(type, "binfmt_misc") == 0 ||
173
strcmp(type, "fuse.gvfs-fuse-daemon") == 0 ||
174
strcmp(type, "debugfs") == 0 ||
175
strcmp(type, "spufs") == 0)
181
* mnt_fstype_is_netfs:
182
* @type: filesystem name
184
* Returns: 1 for filesystems like cifs, nfs, ... or 0.
186
int mnt_fstype_is_netfs(const char *type)
190
if (strcmp(type, "cifs") == 0 ||
191
strcmp(type, "smbfs") == 0 ||
192
strncmp(type,"nfs", 3) == 0 ||
193
strcmp(type, "afs") == 0 ||
194
strcmp(type, "ncpfs") == 0 ||
195
strncmp(type,"9p", 2) == 0)
202
* @type: filesystem type
203
* @pattern: filesystem name or comma delimited list of names
205
* The @pattern list of filesystem can be prefixed with a global
206
* "no" prefix to invert matching of the whole list. The "no" could
207
* also be used for individual items in the @pattern list. So,
208
* "nofoo,bar" has the same meaning as "nofoo,nobar".
210
* "bar" : "nofoo,bar" -> False (global "no" prefix)
212
* "bar" : "foo,bar" -> True
214
* "bar" : "foo,nobar" -> False
216
* Returns: 1 if type is matching, else 0. This function also returns
217
* 0 if @pattern is NULL and @type is non-NULL.
219
int mnt_match_fstype(const char *type, const char *pattern)
221
int no = 0; /* negated types list */
225
if (!pattern && !type)
230
if (!strncmp(pattern, "no", 2)) {
235
/* Does type occur in types, separated by commas? */
239
if (!strncmp(p, "no", 2) && !strncmp(p+2, type, len) &&
240
(p[len+2] == 0 || p[len+2] == ','))
242
if (strncmp(p, type, len) == 0 && (p[len] == 0 || p[len] == ','))
253
/* Returns 1 if needle found or noneedle not found in haystack
254
* Otherwise returns 0
256
static int check_option(const char *haystack, size_t len,
257
const char *needle, size_t needle_len)
262
if (needle_len >= 2 && !strncmp(needle, "no", 2)) {
268
for (p = haystack; p && p < haystack + len; p++) {
269
char *sep = strchr(p, ',');
270
size_t plen = sep ? (size_t) (sep - p) :
271
len - (p - haystack);
273
if (plen == needle_len) {
274
if (!strncmp(p, needle, plen))
275
return !no; /* foo or nofoo was found */
280
return no; /* foo or nofoo was not found */
285
* @optstr: options string
286
* @pattern: comma delimited list of options
288
* The "no" could used for individual items in the @options list. The "no"
289
* prefix does not have a global meaning.
291
* Unlike fs type matching, nonetdev,user and nonetdev,nouser have
292
* DIFFERENT meanings; each option is matched explicitly as specified.
294
* "xxx,yyy,zzz" : "nozzz" -> False
296
* "xxx,yyy,zzz" : "xxx,noeee" -> True
298
* Returns: 1 if pattern is matching, else 0. This function also returns 0
299
* if @pattern is NULL and @optstr is non-NULL.
301
int mnt_match_options(const char *optstr, const char *pattern)
304
size_t len, optstr_len = 0;
306
if (!pattern && !optstr)
311
len = strlen(pattern);
313
optstr_len = strlen(optstr);
315
for (p = pattern; p < pattern + len; p++) {
316
char *sep = strchr(p, ',');
317
size_t plen = sep ? (size_t) (sep - p) :
321
continue; /* if two ',' appear in a row */
323
if (!check_option(optstr, optstr_len, p, plen))
324
return 0; /* any match failure means failure */
329
/* no match failures in list means success */
333
void mnt_free_filesystems(char **filesystems)
339
for (p = filesystems; *p; p++)
344
static int add_filesystem(char ***filesystems, char *name)
353
for (n = 0, p = *filesystems; *p; p++, n++) {
354
if (strcmp(*p, name) == 0)
361
if (n == 0 || !((n + 1) % MYCHUNK)) {
362
size_t items = ((n + 1 + MYCHUNK) / MYCHUNK) * MYCHUNK;
363
char **x = realloc(*filesystems, items * sizeof(char *));
372
(*filesystems)[n] = name;
373
(*filesystems)[n + 1] = NULL;
376
mnt_free_filesystems(*filesystems);
380
static int get_filesystems(const char *filename, char ***filesystems, const char *pattern)
385
f = fopen(filename, "r");
389
while (fgets(line, sizeof(line), f)) {
390
char name[sizeof(line)];
393
if (*line == '#' || strncmp(line, "nodev", 5) == 0)
395
if (sscanf(line, " %128[^\n ]\n", name) != 1)
397
if (pattern && !mnt_match_fstype(name, pattern))
399
rc = add_filesystem(filesystems, name);
406
int mnt_get_filesystems(char ***filesystems, const char *pattern)
414
rc = get_filesystems(_PATH_FILESYSTEMS, filesystems, pattern);
417
return get_filesystems(_PATH_PROC_FILESYSTEMS, filesystems, pattern);
421
* Returns allocated string with username or NULL.
423
char *mnt_get_username(const uid_t uid)
427
#ifdef _SC_GETPW_R_SIZE_MAX
428
size_t sz = sysconf(_SC_GETPW_R_SIZE_MAX);
432
char *buf, *username = NULL;
435
sz = 16384; /* Should be more than enough */
441
if (!getpwuid_r(uid, &pwd, buf, sz, &res) && res)
442
username = strdup(pwd.pw_name);
448
int mnt_get_uid(const char *username, uid_t *uid)
453
size_t sz = sysconf(_SC_GETPW_R_SIZE_MAX);
456
if (!username || !uid)
459
sz = 16384; /* Should be more than enough */
465
if (!getpwnam_r(username, &pwd, buf, sz, &pw) && pw) {
469
DBG(UTILS, mnt_debug(
470
"cannot convert '%s' username to UID", username));
477
int mnt_get_gid(const char *groupname, gid_t *gid)
482
size_t sz = sysconf(_SC_GETGR_R_SIZE_MAX);
485
if (!groupname || !gid)
488
sz = 16384; /* Should be more than enough */
494
if (!getgrnam_r(groupname, &grp, buf, sz, &gr) && gr) {
498
DBG(UTILS, mnt_debug(
499
"cannot convert '%s' groupname to GID", groupname));
506
int mnt_in_group(gid_t gid)
514
n = getgroups(0, NULL);
518
grps = malloc(n * sizeof(*grps));
522
if (getgroups(n, grps) == n) {
523
for (i = 0; i < n; i++) {
524
if (grps[i] == gid) {
535
static int try_write(const char *filename)
542
fd = open(filename, O_RDWR|O_CREAT, S_IWUSR| \
543
S_IRUSR|S_IRGRP|S_IROTH);
552
* mnt_has_regular_mtab:
553
* @mtab: returns path to mtab
554
* @writable: returns 1 if the file is writable
556
* If the file does not exist and @writable argument is not NULL then it will
557
* try to create the file
559
* Returns: 1 if /etc/mtab is a regular file, and 0 in case of error (check
560
* errno for more details).
562
int mnt_has_regular_mtab(const char **mtab, int *writable)
566
const char *filename = mtab && *mtab ? *mtab : mnt_get_mtab_path();
573
DBG(UTILS, mnt_debug("mtab: %s", filename));
575
rc = lstat(filename, &st);
579
if (S_ISREG(st.st_mode)) {
581
*writable = !try_write(filename);
587
/* try to create the file */
589
*writable = !try_write(filename);
595
DBG(UTILS, mnt_debug("%s: irregular/non-writable", filename));
600
* Don't export this to libmount API -- utab is private library stuff.
602
* If the file does not exist and @writable argument is not NULL then it will
603
* try to create the directory (e.g. /run/mount) and the file.
605
* Returns: 1 if utab is a regular file, and 0 in case of
606
* error (check errno for more details).
608
int mnt_has_regular_utab(const char **utab, int *writable)
612
const char *filename = utab && *utab ? *utab : mnt_get_utab_path();
619
DBG(UTILS, mnt_debug("utab: %s", filename));
621
rc = lstat(filename, &st);
625
if (S_ISREG(st.st_mode)) {
627
*writable = !try_write(filename);
630
goto done; /* it's not regular file */
634
char *dirname = strdup(filename);
639
stripoff_last_component(dirname); /* remove filename */
641
rc = mkdir(dirname, S_IWUSR|
642
S_IRUSR|S_IRGRP|S_IROTH|
643
S_IXUSR|S_IXGRP|S_IXOTH);
645
if (rc && errno != EEXIST)
646
goto done; /* probably EACCES */
648
*writable = !try_write(filename);
653
DBG(UTILS, mnt_debug("%s: irregular/non-writable file", filename));
658
* mnt_get_fstab_path:
660
* Returns: path to /etc/fstab or $LIBMOUNT_FSTAB.
662
const char *mnt_get_fstab_path(void)
664
const char *p = safe_getenv("LIBMOUNT_FSTAB");
665
return p ? : _PATH_MNTTAB;
671
* This function returns *default* location of the mtab file. The result does
672
* not have to be writable. See also mnt_has_regular_mtab().
674
* Returns: path to /etc/mtab or $LIBMOUNT_MTAB.
676
const char *mnt_get_mtab_path(void)
678
const char *p = safe_getenv("LIBMOUNT_MTAB");
679
return p ? : _PATH_MOUNTED;
683
* Don't export this to libmount API -- utab is private library stuff.
685
* Returns: path to /run/mount/utab (or /dev/.mount/utab) or $LIBMOUNT_UTAB.
687
const char *mnt_get_utab_path(void)
690
const char *p = safe_getenv("LIBMOUNT_UTAB");
695
if (stat(MNT_RUNTIME_TOPDIR, &st) == 0)
696
return MNT_PATH_UTAB;
698
return MNT_PATH_UTAB_OLD;
702
/* returns file descriptor or -errno, @name returns uniques filename
704
int mnt_open_uniq_filename(const char *filename, char **name)
714
rc = asprintf(&n, "%s.XXXXXX", filename);
724
return fd < 0 ? -errno : fd;
727
char *mnt_get_mountpoint(const char *path)
729
char *mnt = strdup(path);
735
if (*mnt == '/' && *(mnt + 1) == '\0')
743
char *p = stripoff_last_component(mnt);
747
if (stat(*mnt ? mnt : "/", &st))
755
} while (mnt && *(mnt + 1) != '\0');
759
DBG(UTILS, mnt_debug("%s mountpoint is %s", path, mnt));
766
char *mnt_get_fs_root(const char *path, const char *mnt)
768
char *m = (char *) mnt, *res;
773
m = mnt_get_mountpoint(path);
778
p = sz > 1 ? path + sz : path;
783
res = *p ? strdup(p) : strdup("/");
784
DBG(UTILS, mnt_debug("%s fs-root is %s", path, res));
789
int test_match_fstype(struct libmnt_test *ts, int argc, char *argv[])
791
char *type = argv[1];
792
char *pattern = argv[2];
794
printf("%s\n", mnt_match_fstype(type, pattern) ? "MATCH" : "NOT-MATCH");
798
int test_match_options(struct libmnt_test *ts, int argc, char *argv[])
800
char *optstr = argv[1];
801
char *pattern = argv[2];
803
printf("%s\n", mnt_match_options(optstr, pattern) ? "MATCH" : "NOT-MATCH");
807
int test_startswith(struct libmnt_test *ts, int argc, char *argv[])
809
char *optstr = argv[1];
810
char *pattern = argv[2];
812
printf("%s\n", startswith(optstr, pattern) ? "YES" : "NOT");
816
int test_endswith(struct libmnt_test *ts, int argc, char *argv[])
818
char *optstr = argv[1];
819
char *pattern = argv[2];
821
printf("%s\n", endswith(optstr, pattern) ? "YES" : "NOT");
825
int test_mountpoint(struct libmnt_test *ts, int argc, char *argv[])
827
char *path = canonicalize_path(argv[1]),
828
*mnt = path ? mnt_get_mountpoint(path) : NULL;
830
printf("%s: %s\n", argv[1], mnt ? : "unknown");
836
int test_fsroot(struct libmnt_test *ts, int argc, char *argv[])
838
char *path = canonicalize_path(argv[1]),
839
*mnt = path ? mnt_get_fs_root(path, NULL) : NULL;
841
printf("%s: %s\n", argv[1], mnt ? : "unknown");
847
int test_filesystems(struct libmnt_test *ts, int argc, char *argv[])
849
char **filesystems = NULL;
852
rc = mnt_get_filesystems(&filesystems, argc ? argv[1] : NULL);
855
for (p = filesystems; *p; p++)
857
mnt_free_filesystems(filesystems);
862
int test_chdir(struct libmnt_test *ts, int argc, char *argv[])
865
char *path = canonicalize_path(argv[1]),
871
rc = mnt_chdir_to_parent(path, &last);
873
printf("path='%s', abs='%s', last='%s'\n",
874
argv[1], path, last);
882
int main(int argc, char *argv[])
884
struct libmnt_test tss[] = {
885
{ "--match-fstype", test_match_fstype, "<type> <pattern> FS types matching" },
886
{ "--match-options", test_match_options, "<options> <pattern> options matching" },
887
{ "--filesystems", test_filesystems, "[<pattern>] list /{etc,proc}/filesystems" },
888
{ "--starts-with", test_startswith, "<string> <prefix>" },
889
{ "--ends-with", test_endswith, "<string> <prefix>" },
890
{ "--mountpoint", test_mountpoint, "<path>" },
891
{ "--fs-root", test_fsroot, "<path>" },
892
{ "--cd-parent", test_chdir, "<path>" },
896
return mnt_run_test(tss, argc, argv);
899
#endif /* TEST_PROGRAM */