]> git.ipfire.org Git - thirdparty/util-linux.git/blame - libmount/src/utils.c
mount: document x-* options
[thirdparty/util-linux.git] / libmount / src / utils.c
CommitLineData
69b7e41e
KZ
1/*
2 * Copyright (C) 2008-2009 Karel Zak <kzak@redhat.com>
3 *
4 * This file may be redistributed under the terms of the
5 * GNU Lesser General Public License.
6 */
7
192c6aad
KZ
8/**
9 * SECTION: utils
10 * @title: Utils
11 * @short_description: misc utils.
12 */
69b7e41e 13#include <ctype.h>
69b7e41e
KZ
14#include <fcntl.h>
15#include <pwd.h>
a1e8af75 16#include <grp.h>
69b7e41e 17
b49103ed 18#include "strutils.h"
0532ba1d 19#include "pathnames.h"
69b7e41e 20#include "mountP.h"
3c5e4ef8 21#include "mangle.h"
0bb44be3 22#include "canonicalize.h"
035507c8 23#include "env.h"
b483debb 24#include "match.h"
69b7e41e 25
b49103ed
KZ
26int endswith(const char *s, const char *sx)
27{
28 ssize_t off;
29
30 assert(s);
31 assert(sx);
32
33 off = strlen(s);
34 if (!off)
35 return 0;
36 off -= strlen(sx);
37 if (off < 0)
38 return 0;
39
40 return !strcmp(s + off, sx);
41}
42
43int startswith(const char *s, const char *sx)
44{
45 size_t off;
46
47 assert(s);
48 assert(sx);
49
50 off = strlen(sx);
51 if (!off)
52 return 0;
53
54 return !strncmp(s, sx, off);
55}
56
fd1eb7a7
KZ
57int mnt_parse_offset(const char *str, size_t len, uintmax_t *res)
58{
59 char *p;
60 int rc = 0;
61
0208ae2e 62 if (!str || !*str)
fd1eb7a7
KZ
63 return -EINVAL;
64
65 p = strndup(str, len);
66 if (!p)
67 return -errno;
68
69 if (strtosize(p, res))
70 rc = -EINVAL;
71 free(p);
72 return rc;
73}
74
7383ebce
DR
75/* used as a callback by bsearch in mnt_fstype_is_pseudofs() */
76static int fstype_cmp(const void *v1, const void *v2)
77{
78 const char *s1 = *(const char **)v1;
79 const char *s2 = *(const char **)v2;
80
81 return strcmp(s1, s2);
82}
83
1d0cd73f
KZ
84/* returns basename and keeps dirname in the @path, if @path is "/" (root)
85 * then returns empty string */
86static char *stripoff_last_component(char *path)
87{
88 char *p = path ? strrchr(path, '/') : NULL;
89
90 if (!p)
91 return NULL;
92 *p = '\0';
61073773 93 return p + 1;
1d0cd73f
KZ
94}
95
61073773
KZ
96/*
97 * Note that the @target has to be absolute path (so at least "/"). The
98 * @filename returns allocated buffer with last path component, for example:
99 *
100 * mnt_chdir_to_parent("/mnt/test", &buf) ==> chdir("/mnt"), buf="test"
66bb8267
KZ
101 */
102int mnt_chdir_to_parent(const char *target, char **filename)
103{
61073773 104 char *buf, *parent, *last = NULL;
66bb8267
KZ
105 char cwd[PATH_MAX];
106 int rc = -EINVAL;
107
108 if (!target || *target != '/')
109 return -EINVAL;
110
61073773
KZ
111 DBG(UTILS, mnt_debug("moving to %s parent", target));
112
113 buf = strdup(target);
114 if (!buf)
66bb8267
KZ
115 return -ENOMEM;
116
61073773
KZ
117 if (*(buf + 1) != '\0') {
118 last = stripoff_last_component(buf);
66bb8267
KZ
119 if (!last)
120 goto err;
121 }
66bb8267 122
61073773
KZ
123 parent = buf && *buf ? buf : "/";
124
125 if (chdir(parent) == -1) {
126 DBG(UTILS, mnt_debug("failed to chdir to %s: %m", parent));
66bb8267
KZ
127 rc = -errno;
128 goto err;
129 }
130 if (!getcwd(cwd, sizeof(cwd))) {
131 DBG(UTILS, mnt_debug("failed to obtain current directory: %m"));
132 rc = -errno;
133 goto err;
134 }
61073773
KZ
135 if (strcmp(cwd, parent) != 0) {
136 DBG(UTILS, mnt_debug(
137 "unexpected chdir (expected=%s, cwd=%s)", parent, cwd));
66bb8267
KZ
138 goto err;
139 }
140
61073773
KZ
141 DBG(CXT, mnt_debug(
142 "current directory moved to %s [last_component='%s']",
143 parent, last));
66bb8267 144
ba2bdf41
KZ
145 if (filename) {
146 *filename = buf;
66bb8267 147
ba2bdf41
KZ
148 if (!last || !*last)
149 memcpy(*filename, ".", 2);
150 else
151 memcpy(*filename, last, strlen(last) + 1);
152 }
66bb8267
KZ
153 return 0;
154err:
61073773 155 free(buf);
66bb8267
KZ
156 return rc;
157}
158
f9906424
KZ
159/*
160 * Check if @path is on read-only filesystem independently on file permissions.
161 */
162int mnt_is_readonly(const char *path)
163{
164 if (access(path, W_OK) == 0)
165 return 0;
166 if (errno == EROFS)
167 return 1;
168 if (errno != EACCES)
169 return 0;
170
171#ifdef HAVE_FUTIMENS
172 /*
173 * access(2) returns EACCES on read-only FS:
174 *
175 * - for set-uid application if one component of the path is not
176 * accessible for the current rUID. (Note that euidaccess(2) does not
177 * check for EROFS at all).
178 *
179 * - for read-write filesystem with read-only VFS node (aka -o remount,ro,bind)
180 */
181 {
182 struct timespec times[2];
183
184 times[0].tv_nsec = UTIME_NOW; /* atime */
185 times[1].tv_nsec = UTIME_OMIT; /* mtime */
186
187 if (utimensat(AT_FDCWD, path, times, 0) == -1)
188 return errno == EROFS;
189 }
190#endif
191 return 0;
192}
193
3c5e4ef8
KZ
194/**
195 * mnt_mangle:
196 * @str: string
197 *
198 * Encode @str to be compatible with fstab/mtab
199 *
200 * Returns: new allocated string or NULL in case of error.
201 */
202char *mnt_mangle(const char *str)
203{
204 return mangle(str);
205}
206
207/**
208 * mnt_unmangle:
209 * @str: string
210 *
211 * Decode @str from fstab/mtab
212 *
213 * Returns: new allocated string or NULL in case of error.
214 */
215char *mnt_unmangle(const char *str)
216{
dd369652 217 return unmangle(str, NULL);
3c5e4ef8
KZ
218}
219
69b7e41e
KZ
220/**
221 * mnt_fstype_is_pseudofs:
222 * @type: filesystem name
223 *
224 * Returns: 1 for filesystems like proc, sysfs, ... or 0.
225 */
226int mnt_fstype_is_pseudofs(const char *type)
227{
7383ebce
DR
228 /* This array must remain sorted when adding new fstypes */
229 static const char *pseudofs[] = {
230 "anon_inodefs",
231 "autofs",
232 "bdev",
233 "binfmt_misc",
234 "cgroup",
235 "configfs",
236 "cpuset",
237 "debugfs",
238 "devfs",
239 "devpts",
240 "devtmpfs",
241 "dlmfs",
242 "fuse.gvfs-fuse-daemon",
243 "fusectl",
244 "hugetlbfs",
245 "mqueue",
246 "nfsd",
247 "none",
248 "pipefs",
249 "proc",
250 "pstore",
251 "ramfs",
252 "rootfs",
253 "rpc_pipefs",
254 "securityfs",
255 "sockfs",
256 "spufs",
257 "sysfs",
258 "tmpfs"
259 };
260
261 return !(bsearch(&type, pseudofs, ARRAY_SIZE(pseudofs),
262 sizeof(char*), fstype_cmp) == NULL);
69b7e41e
KZ
263}
264
265/**
266 * mnt_fstype_is_netfs:
267 * @type: filesystem name
268 *
269 * Returns: 1 for filesystems like cifs, nfs, ... or 0.
270 */
271int mnt_fstype_is_netfs(const char *type)
272{
c59cf20c 273 if (strcmp(type, "cifs") == 0 ||
69b7e41e 274 strcmp(type, "smbfs") == 0 ||
c59cf20c
KZ
275 strncmp(type,"nfs", 3) == 0 ||
276 strcmp(type, "afs") == 0 ||
277 strcmp(type, "ncpfs") == 0 ||
278 strncmp(type,"9p", 2) == 0)
69b7e41e
KZ
279 return 1;
280 return 0;
281}
282
abc9c0f7
KZ
283/**
284 * mnt_match_fstype:
285 * @type: filesystem type
6ad929bb 286 * @pattern: filesystem name or comma delimited list of names
abc9c0f7
KZ
287 *
288 * The @pattern list of filesystem can be prefixed with a global
289 * "no" prefix to invert matching of the whole list. The "no" could
6ad929bb 290 * also be used for individual items in the @pattern list. So,
3d735589 291 * "nofoo,bar" has the same meaning as "nofoo,nobar".
abc9c0f7 292 *
3d735589
KZ
293 * "bar" : "nofoo,bar" -> False (global "no" prefix)
294 *
295 * "bar" : "foo,bar" -> True
abc9c0f7 296 *
abc9c0f7
KZ
297 * "bar" : "foo,nobar" -> False
298 *
299 * Returns: 1 if type is matching, else 0. This function also returns
300 * 0 if @pattern is NULL and @type is non-NULL.
301 */
302int mnt_match_fstype(const char *type, const char *pattern)
303{
12089155 304 return match_fstype(type, pattern);
abc9c0f7
KZ
305}
306
307
308/* Returns 1 if needle found or noneedle not found in haystack
309 * Otherwise returns 0
310 */
311static int check_option(const char *haystack, size_t len,
312 const char *needle, size_t needle_len)
313{
314 const char *p;
315 int no = 0;
316
ede8a60e
KZ
317 if (needle_len >= 1 && *needle == '+') {
318 needle++;
319 needle_len--;
320 } else if (needle_len >= 2 && !strncmp(needle, "no", 2)) {
abc9c0f7
KZ
321 no = 1;
322 needle += 2;
323 needle_len -= 2;
324 }
325
326 for (p = haystack; p && p < haystack + len; p++) {
327 char *sep = strchr(p, ',');
7fc6d2b8
KZ
328 size_t plen = sep ? (size_t) (sep - p) :
329 len - (p - haystack);
abc9c0f7
KZ
330
331 if (plen == needle_len) {
332 if (!strncmp(p, needle, plen))
333 return !no; /* foo or nofoo was found */
334 }
335 p += plen;
336 }
337
338 return no; /* foo or nofoo was not found */
339}
340
341/**
342 * mnt_match_options:
343 * @optstr: options string
6ad929bb 344 * @pattern: comma delimited list of options
abc9c0f7
KZ
345 *
346 * The "no" could used for individual items in the @options list. The "no"
6ad929bb 347 * prefix does not have a global meaning.
abc9c0f7
KZ
348 *
349 * Unlike fs type matching, nonetdev,user and nonetdev,nouser have
350 * DIFFERENT meanings; each option is matched explicitly as specified.
351 *
ede8a60e
KZ
352 * The "no" prefix interpretation could be disable by "+" prefix, for example
353 * "+noauto" matches if @optstr literally contains "noauto" string.
354 *
3d735589
KZ
355 * "xxx,yyy,zzz" : "nozzz" -> False
356 *
357 * "xxx,yyy,zzz" : "xxx,noeee" -> True
abc9c0f7 358 *
ede8a60e
KZ
359 * "bar,zzz" : "nofoo" -> True
360 *
361 * "nofoo,bar" : "+nofoo" -> True
362 *
363 * "bar,zzz" : "+nofoo" -> False
364 *
365 *
abc9c0f7
KZ
366 * Returns: 1 if pattern is matching, else 0. This function also returns 0
367 * if @pattern is NULL and @optstr is non-NULL.
368 */
369int mnt_match_options(const char *optstr, const char *pattern)
370{
371 const char *p;
372 size_t len, optstr_len = 0;
373
374 if (!pattern && !optstr)
375 return 1;
376 if (!pattern)
377 return 0;
378
379 len = strlen(pattern);
380 if (optstr)
381 optstr_len = strlen(optstr);
382
383 for (p = pattern; p < pattern + len; p++) {
384 char *sep = strchr(p, ',');
7fc6d2b8
KZ
385 size_t plen = sep ? (size_t) (sep - p) :
386 len - (p - pattern);
abc9c0f7
KZ
387
388 if (!plen)
389 continue; /* if two ',' appear in a row */
390
391 if (!check_option(optstr, optstr_len, p, plen))
392 return 0; /* any match failure means failure */
393
394 p += plen;
395 }
396
397 /* no match failures in list means success */
398 return 1;
399}
400
97e23b5e
KZ
401void mnt_free_filesystems(char **filesystems)
402{
403 char **p;
404
405 if (!filesystems)
406 return;
407 for (p = filesystems; *p; p++)
408 free(*p);
409 free(filesystems);
410}
411
412static int add_filesystem(char ***filesystems, char *name)
413{
414 int n = 0;
415
416 assert(filesystems);
417 assert(name);
418
419 if (*filesystems) {
420 char **p;
421 for (n = 0, p = *filesystems; *p; p++, n++) {
422 if (strcmp(*p, name) == 0)
423 return 0;
424 }
425 }
426
427 #define MYCHUNK 16
428
429 if (n == 0 || !((n + 1) % MYCHUNK)) {
430 size_t items = ((n + 1 + MYCHUNK) / MYCHUNK) * MYCHUNK;
431 char **x = realloc(*filesystems, items * sizeof(char *));
432
433 if (!x)
434 goto err;
435 *filesystems = x;
436 }
437 name = strdup(name);
438 if (!name)
439 goto err;
440 (*filesystems)[n] = name;
441 (*filesystems)[n + 1] = NULL;
442 return 0;
443err:
444 mnt_free_filesystems(*filesystems);
445 return -ENOMEM;
446}
447
448static int get_filesystems(const char *filename, char ***filesystems, const char *pattern)
449{
4f69189f 450 int rc = 0;
97e23b5e
KZ
451 FILE *f;
452 char line[128];
453
454 f = fopen(filename, "r");
455 if (!f)
ec8121b1
KZ
456 return 1;
457
458 DBG(UTILS, mnt_debug("reading filesystems list from: %s", filename));
97e23b5e
KZ
459
460 while (fgets(line, sizeof(line), f)) {
461 char name[sizeof(line)];
97e23b5e
KZ
462
463 if (*line == '#' || strncmp(line, "nodev", 5) == 0)
464 continue;
465 if (sscanf(line, " %128[^\n ]\n", name) != 1)
466 continue;
ec8121b1
KZ
467 if (strcmp(name, "*") == 0) {
468 rc = 1;
469 break; /* end of the /etc/filesystems */
470 }
97e23b5e
KZ
471 if (pattern && !mnt_match_fstype(name, pattern))
472 continue;
473 rc = add_filesystem(filesystems, name);
474 if (rc)
4f69189f 475 break;
97e23b5e 476 }
4f69189f
KZ
477
478 fclose(f);
479 return rc;
97e23b5e
KZ
480}
481
3de77c21 482/*
ec8121b1
KZ
483 * Always check @filesystems pointer!
484 *
485 * man mount:
486 *
487 * ...mount will try to read the file /etc/filesystems, or, if that does not
488 * exist, /proc/filesystems. All of the filesystem types listed there will
489 * be tried, except for those that are labeled "nodev" (e.g., devpts,
490 * proc and nfs). If /etc/filesystems ends in a line with a single * only,
491 * mount will read /proc/filesystems after‐ wards.
3de77c21 492 */
97e23b5e
KZ
493int mnt_get_filesystems(char ***filesystems, const char *pattern)
494{
495 int rc;
496
497 if (!filesystems)
498 return -EINVAL;
ec8121b1 499
97e23b5e
KZ
500 *filesystems = NULL;
501
502 rc = get_filesystems(_PATH_FILESYSTEMS, filesystems, pattern);
ec8121b1 503 if (rc != 1)
97e23b5e 504 return rc;
ec8121b1
KZ
505
506 rc = get_filesystems(_PATH_PROC_FILESYSTEMS, filesystems, pattern);
507 if (rc == 1 && *filesystems)
508 rc = 0; /* not found /proc/filesystems */
509
510 return rc;
97e23b5e
KZ
511}
512
2af0a839
KZ
513static size_t get_pw_record_size(void)
514{
515#ifdef _SC_GETPW_R_SIZE_MAX
516 long sz = sysconf(_SC_GETPW_R_SIZE_MAX);
517 if (sz > 0)
518 return sz;
519#endif
520 return 16384;
521}
522
69b7e41e
KZ
523/*
524 * Returns allocated string with username or NULL.
525 */
526char *mnt_get_username(const uid_t uid)
527{
528 struct passwd pwd;
529 struct passwd *res;
2af0a839 530 size_t sz = get_pw_record_size();
69b7e41e
KZ
531 char *buf, *username = NULL;
532
69b7e41e
KZ
533 buf = malloc(sz);
534 if (!buf)
535 return NULL;
536
537 if (!getpwuid_r(uid, &pwd, buf, sz, &res) && res)
538 username = strdup(pwd.pw_name);
539
540 free(buf);
541 return username;
542}
abc9c0f7 543
a1e8af75
KZ
544int mnt_get_uid(const char *username, uid_t *uid)
545{
188dc15a 546 int rc = -1;
a1e8af75
KZ
547 struct passwd pwd;
548 struct passwd *pw;
2af0a839 549 size_t sz = get_pw_record_size();
a1e8af75
KZ
550 char *buf;
551
188dc15a
KZ
552 if (!username || !uid)
553 return -EINVAL;
b47b7b3a 554
a1e8af75
KZ
555 buf = malloc(sz);
556 if (!buf)
557 return -ENOMEM;
558
188dc15a 559 if (!getpwnam_r(username, &pwd, buf, sz, &pw) && pw) {
a1e8af75 560 *uid= pw->pw_uid;
188dc15a
KZ
561 rc = 0;
562 } else {
563 DBG(UTILS, mnt_debug(
564 "cannot convert '%s' username to UID", username));
61f5ff6c 565 rc = errno ? -errno : -EINVAL;
188dc15a 566 }
a1e8af75
KZ
567
568 free(buf);
188dc15a 569 return rc;
a1e8af75
KZ
570}
571
572int mnt_get_gid(const char *groupname, gid_t *gid)
573{
188dc15a 574 int rc = -1;
a1e8af75
KZ
575 struct group grp;
576 struct group *gr;
2af0a839 577 size_t sz = get_pw_record_size();
a1e8af75
KZ
578 char *buf;
579
188dc15a
KZ
580 if (!groupname || !gid)
581 return -EINVAL;
b47b7b3a 582
a1e8af75
KZ
583 buf = malloc(sz);
584 if (!buf)
585 return -ENOMEM;
586
188dc15a 587 if (!getgrnam_r(groupname, &grp, buf, sz, &gr) && gr) {
a1e8af75 588 *gid= gr->gr_gid;
188dc15a
KZ
589 rc = 0;
590 } else {
591 DBG(UTILS, mnt_debug(
592 "cannot convert '%s' groupname to GID", groupname));
61f5ff6c 593 rc = errno ? -errno : -EINVAL;
188dc15a 594 }
a1e8af75
KZ
595
596 free(buf);
188dc15a
KZ
597 return rc;
598}
599
600int mnt_in_group(gid_t gid)
601{
602 int rc = 0, n, i;
603 gid_t *grps = NULL;
604
605 if (getgid() == gid)
606 return 1;
607
608 n = getgroups(0, NULL);
609 if (n <= 0)
610 goto done;
611
612 grps = malloc(n * sizeof(*grps));
613 if (!grps)
614 goto done;
615
616 if (getgroups(n, grps) == n) {
617 for (i = 0; i < n; i++) {
618 if (grps[i] == gid) {
619 rc = 1;
620 break;
621 }
622 }
623 }
624done:
625 free(grps);
626 return rc;
a1e8af75
KZ
627}
628
1d0cd73f
KZ
629static int try_write(const char *filename)
630{
631 int fd;
632
633 if (!filename)
634 return -EINVAL;
635
d39c2b43
KZ
636 fd = open(filename, O_RDWR|O_CREAT|O_CLOEXEC,
637 S_IWUSR|S_IRUSR|S_IRGRP|S_IROTH);
1d0cd73f
KZ
638 if (fd >= 0) {
639 close(fd);
640 return 0;
641 }
642 return -errno;
643}
644
645/**
646 * mnt_has_regular_mtab:
647 * @mtab: returns path to mtab
648 * @writable: returns 1 if the file is writable
649 *
650 * If the file does not exist and @writable argument is not NULL then it will
651 * try to create the file
652 *
6ad929bb 653 * Returns: 1 if /etc/mtab is a regular file, and 0 in case of error (check
1d0cd73f 654 * errno for more details).
0532ba1d 655 */
1d0cd73f 656int mnt_has_regular_mtab(const char **mtab, int *writable)
0532ba1d
KZ
657{
658 struct stat st;
70bf97ae 659 int rc;
1d0cd73f 660 const char *filename = mtab && *mtab ? *mtab : mnt_get_mtab_path();
0532ba1d 661
7c118af7
KZ
662 if (writable)
663 *writable = 0;
70bf97ae 664 if (mtab && !*mtab)
1d0cd73f
KZ
665 *mtab = filename;
666
667 DBG(UTILS, mnt_debug("mtab: %s", filename));
70bf97ae 668
1d0cd73f
KZ
669 rc = lstat(filename, &st);
670
671 if (rc == 0) {
672 /* file exist */
673 if (S_ISREG(st.st_mode)) {
674 if (writable)
675 *writable = !try_write(filename);
676 return 1;
677 }
7c118af7 678 goto done;
1d0cd73f
KZ
679 }
680
681 /* try to create the file */
70bf97ae 682 if (writable) {
1d0cd73f 683 *writable = !try_write(filename);
7c118af7
KZ
684 if (*writable)
685 return 1;
1d0cd73f
KZ
686 }
687
7c118af7
KZ
688done:
689 DBG(UTILS, mnt_debug("%s: irregular/non-writable", filename));
1d0cd73f
KZ
690 return 0;
691}
7c118af7 692
77417bc0
KZ
693/*
694 * Don't export this to libmount API -- utab is private library stuff.
1d0cd73f
KZ
695 *
696 * If the file does not exist and @writable argument is not NULL then it will
a362ae60 697 * try to create the directory (e.g. /run/mount) and the file.
1d0cd73f 698 *
a362ae60 699 * Returns: 1 if utab is a regular file, and 0 in case of
6f5788c5 700 * error (check errno for more details).
1d0cd73f 701 */
1d0cd73f
KZ
702int mnt_has_regular_utab(const char **utab, int *writable)
703{
704 struct stat st;
705 int rc;
706 const char *filename = utab && *utab ? *utab : mnt_get_utab_path();
707
7c118af7
KZ
708 if (writable)
709 *writable = 0;
1d0cd73f
KZ
710 if (utab && !*utab)
711 *utab = filename;
712
713 DBG(UTILS, mnt_debug("utab: %s", filename));
714
715 rc = lstat(filename, &st);
716
717 if (rc == 0) {
718 /* file exist */
719 if (S_ISREG(st.st_mode)) {
720 if (writable)
7c118af7 721 *writable = !try_write(filename);
1d0cd73f 722 return 1;
70bf97ae 723 }
7c118af7 724 goto done; /* it's not regular file */
70bf97ae 725 }
1d0cd73f
KZ
726
727 if (writable) {
728 char *dirname = strdup(filename);
729
730 if (!dirname)
7c118af7 731 goto done;
1d0cd73f
KZ
732
733 stripoff_last_component(dirname); /* remove filename */
734
b0bb8fb6
KZ
735 rc = mkdir(dirname, S_IWUSR|
736 S_IRUSR|S_IRGRP|S_IROTH|
737 S_IXUSR|S_IXGRP|S_IXOTH);
1d0cd73f
KZ
738 free(dirname);
739 if (rc && errno != EEXIST)
7c118af7 740 goto done; /* probably EACCES */
1d0cd73f
KZ
741
742 *writable = !try_write(filename);
7c118af7
KZ
743 if (*writable)
744 return 1;
1d0cd73f 745 }
7c118af7
KZ
746done:
747 DBG(UTILS, mnt_debug("%s: irregular/non-writable file", filename));
1d0cd73f 748 return 0;
0532ba1d
KZ
749}
750
ce4dd666
KZ
751/**
752 * mnt_get_swaps_path:
753 *
754 * Returns: path to /proc/swaps or $LIBMOUNT_SWAPS.
755 */
756const char *mnt_get_swaps_path(void)
757{
758 const char *p = safe_getenv("LIBMOUNT_SWAPS");
759 return p ? : _PATH_PROC_SWAPS;
760}
761
3a5b1b1d
KZ
762/**
763 * mnt_get_fstab_path:
764 *
765 * Returns: path to /etc/fstab or $LIBMOUNT_FSTAB.
766 */
767const char *mnt_get_fstab_path(void)
768{
035507c8 769 const char *p = safe_getenv("LIBMOUNT_FSTAB");
3a5b1b1d
KZ
770 return p ? : _PATH_MNTTAB;
771}
772
773/**
774 * mnt_get_mtab_path:
775 *
b37dd175 776 * This function returns *default* location of the mtab file. The result does
0f32f1e2 777 * not have to be writable. See also mnt_has_regular_mtab().
3a5b1b1d
KZ
778 *
779 * Returns: path to /etc/mtab or $LIBMOUNT_MTAB.
780 */
781const char *mnt_get_mtab_path(void)
782{
035507c8 783 const char *p = safe_getenv("LIBMOUNT_MTAB");
3a5b1b1d
KZ
784 return p ? : _PATH_MOUNTED;
785}
786
77417bc0
KZ
787/*
788 * Don't export this to libmount API -- utab is private library stuff.
be1a5180 789 *
a362ae60 790 * Returns: path to /run/mount/utab (or /dev/.mount/utab) or $LIBMOUNT_UTAB.
b37dd175
KZ
791 */
792const char *mnt_get_utab_path(void)
793{
a362ae60 794 struct stat st;
035507c8 795 const char *p = safe_getenv("LIBMOUNT_UTAB");
a362ae60
KZ
796
797 if (p)
798 return p;
799
800 if (stat(MNT_RUNTIME_TOPDIR, &st) == 0)
801 return MNT_PATH_UTAB;
802
803 return MNT_PATH_UTAB_OLD;
b37dd175
KZ
804}
805
d1be0c34 806
b37dd175
KZ
807/* returns file descriptor or -errno, @name returns uniques filename
808 */
4b6cf485 809int mnt_open_uniq_filename(const char *filename, char **name)
b37dd175
KZ
810{
811 int rc, fd;
812 char *n;
6fc81224 813 mode_t oldmode;
b37dd175 814
ba2bdf41
KZ
815 if (!filename)
816 return -EINVAL;
b37dd175
KZ
817 if (name)
818 *name = NULL;
819
820 rc = asprintf(&n, "%s.XXXXXX", filename);
821 if (rc <= 0)
822 return -errno;
823
6fc81224
KZ
824 /* This is for very old glibc and for compatibility with Posix where is
825 * nothing about mkstemp() mode. All sane glibc use secure mode (0600).
826 */
51479069
KZ
827 oldmode = umask(S_IRGRP|S_IWGRP|S_IXGRP|
828 S_IROTH|S_IWOTH|S_IXOTH);
4b6cf485 829 fd = mkstemp(n);
51479069 830 umask(oldmode);
6fc81224 831
b37dd175
KZ
832 if (fd >= 0 && name)
833 *name = n;
834 else
835 free(n);
836
837 return fd < 0 ? -errno : fd;
838}
0bb44be3 839
cd3d6c5b
DR
840/**
841 * mnt_get_mountpoint:
842 * @path: pathname
843 *
844 * This function finds the mountpoint that a given path resides in. @path
845 * should be canonicalized. The returned pointer should be freed by the caller.
846 *
90cd46cb 847 * Returns: allocated string with target of the mounted device or NULL on error
cd3d6c5b 848 */
0bb44be3
KZ
849char *mnt_get_mountpoint(const char *path)
850{
851 char *mnt = strdup(path);
852 struct stat st;
853 dev_t dir, base;
854
855 if (!mnt)
856 return NULL;
857 if (*mnt == '/' && *(mnt + 1) == '\0')
9758c88a 858 goto done;
0bb44be3
KZ
859
860 if (stat(mnt, &st))
861 goto err;
862 base = st.st_dev;
863
864 do {
865 char *p = stripoff_last_component(mnt);
866
867 if (!p)
868 break;
869 if (stat(*mnt ? mnt : "/", &st))
870 goto err;
871 dir = st.st_dev;
872 if (dir != base) {
9d670a2a
KZ
873 if (p > mnt)
874 *(p - 1) = '/';
9758c88a 875 goto done;
0bb44be3
KZ
876 }
877 base = dir;
878 } while (mnt && *(mnt + 1) != '\0');
879
880 memcpy(mnt, "/", 2);
9758c88a 881done:
f84fa6f7 882 DBG(UTILS, mnt_debug("%s mountpoint is %s", path, mnt));
9758c88a 883 return mnt;
0bb44be3
KZ
884err:
885 free(mnt);
886 return NULL;
887}
888
9758c88a 889char *mnt_get_fs_root(const char *path, const char *mnt)
0bb44be3 890{
f84fa6f7 891 char *m = (char *) mnt, *res;
0bb44be3
KZ
892 const char *p;
893 size_t sz;
894
9758c88a
KZ
895 if (!m)
896 m = mnt_get_mountpoint(path);
897 if (!m)
0bb44be3
KZ
898 return NULL;
899
9758c88a 900 sz = strlen(m);
0bb44be3
KZ
901 p = sz > 1 ? path + sz : path;
902
9758c88a
KZ
903 if (m != mnt)
904 free(m);
0bb44be3 905
f84fa6f7
KZ
906 res = *p ? strdup(p) : strdup("/");
907 DBG(UTILS, mnt_debug("%s fs-root is %s", path, res));
908 return res;
0bb44be3
KZ
909}
910
f308ec19
KZ
911/*
912 * Search for @name kernel command parametr.
913 *
914 * Returns newly allocated string with parameter argument if the @name is
915 * specified as "name=" or returns pointer to @name or returns NULL if not
916 * found.
917 *
918 * For example cmdline: "aaa bbb=BBB ccc"
919 *
920 * @name is "aaa" --returns--> "aaa" (pointer to @name)
921 * @name is "bbb=" --returns--> "BBB" (allocated)
922 * @name is "foo" --returns--> NULL
923 */
924char *mnt_get_kernel_cmdline_option(const char *name)
925{
926 FILE *f;
927 size_t len;
928 int val = 0;
929 char *p, *res = NULL;
930 char buf[BUFSIZ]; /* see kernel include/asm-generic/setup.h: COMMAND_LINE_SIZE */
931 const char *path = _PATH_PROC_CMDLINE;
932
ba2bdf41
KZ
933 if (!name)
934 return NULL;
f308ec19
KZ
935
936#ifdef TEST_PROGRAM
cd793967 937 path = getenv("LIBMOUNT_KERNEL_CMDLINE");
f308ec19
KZ
938 if (!path)
939 path = _PATH_PROC_CMDLINE;
940#endif
941 f = fopen(path, "r");
942 if (!f)
943 return NULL;
944
945 p = fgets(buf, sizeof(buf), f);
946 fclose(f);
947
948 if (!p || !*p || *p == '\n')
949 return NULL;
950
951 len = strlen(buf);
952 *(buf + len - 1) = '\0'; /* remove last '\n' */
953
954 len = strlen(name);
955 if (len && *(name + len - 1) == '=')
956 val = 1;
957
2c0ba1ce 958 for ( ; p && *p; p++) {
f308ec19
KZ
959 if (!(p = strstr(p, name)))
960 break; /* not found the option */
961 if (p != buf && !isblank(*(p - 1)))
962 continue; /* no space before the option */
963 if (!val && *(p + len) != '\0' && !isblank(*(p + len)))
964 continue; /* no space behind the option */
965 if (val) {
966 char *v = p + len;
967
968 while (*p && !isblank(*p)) /* jump to the end of the argument */
969 p++;
970 *p = '\0';
971 res = strdup(v);
972 break;
973 } else
974 res = (char *) name; /* option without '=' */
975 break;
976 }
977
978 return res;
979}
980
abc9c0f7 981#ifdef TEST_PROGRAM
68164f6c 982int test_match_fstype(struct libmnt_test *ts, int argc, char *argv[])
abc9c0f7
KZ
983{
984 char *type = argv[1];
985 char *pattern = argv[2];
986
987 printf("%s\n", mnt_match_fstype(type, pattern) ? "MATCH" : "NOT-MATCH");
988 return 0;
989}
990
68164f6c 991int test_match_options(struct libmnt_test *ts, int argc, char *argv[])
abc9c0f7
KZ
992{
993 char *optstr = argv[1];
994 char *pattern = argv[2];
995
996 printf("%s\n", mnt_match_options(optstr, pattern) ? "MATCH" : "NOT-MATCH");
997 return 0;
998}
999
68164f6c 1000int test_startswith(struct libmnt_test *ts, int argc, char *argv[])
b49103ed
KZ
1001{
1002 char *optstr = argv[1];
1003 char *pattern = argv[2];
1004
1005 printf("%s\n", startswith(optstr, pattern) ? "YES" : "NOT");
1006 return 0;
1007}
1008
68164f6c 1009int test_endswith(struct libmnt_test *ts, int argc, char *argv[])
b49103ed
KZ
1010{
1011 char *optstr = argv[1];
1012 char *pattern = argv[2];
1013
1014 printf("%s\n", endswith(optstr, pattern) ? "YES" : "NOT");
1015 return 0;
1016}
1017
68164f6c 1018int test_mountpoint(struct libmnt_test *ts, int argc, char *argv[])
0bb44be3
KZ
1019{
1020 char *path = canonicalize_path(argv[1]),
1021 *mnt = path ? mnt_get_mountpoint(path) : NULL;
1022
1023 printf("%s: %s\n", argv[1], mnt ? : "unknown");
1024 free(mnt);
1025 free(path);
1026 return 0;
1027}
1028
68164f6c 1029int test_fsroot(struct libmnt_test *ts, int argc, char *argv[])
0bb44be3
KZ
1030{
1031 char *path = canonicalize_path(argv[1]),
d672fb26 1032 *mnt = path ? mnt_get_fs_root(path, NULL) : NULL;
0bb44be3
KZ
1033
1034 printf("%s: %s\n", argv[1], mnt ? : "unknown");
1035 free(mnt);
1036 free(path);
1037 return 0;
1038}
abc9c0f7 1039
68164f6c 1040int test_filesystems(struct libmnt_test *ts, int argc, char *argv[])
97e23b5e
KZ
1041{
1042 char **filesystems = NULL;
1043 int rc;
1044
1045 rc = mnt_get_filesystems(&filesystems, argc ? argv[1] : NULL);
1046 if (!rc) {
1047 char **p;
1048 for (p = filesystems; *p; p++)
1049 printf("%s\n", *p);
1050 mnt_free_filesystems(filesystems);
1051 }
1052 return rc;
1053}
1054
66bb8267
KZ
1055int test_chdir(struct libmnt_test *ts, int argc, char *argv[])
1056{
1057 int rc;
1058 char *path = canonicalize_path(argv[1]),
1059 *last = NULL;
1060
1061 if (!path)
1062 return -errno;
1063
1064 rc = mnt_chdir_to_parent(path, &last);
1065 if (!rc) {
1066 printf("path='%s', abs='%s', last='%s'\n",
1067 argv[1], path, last);
1068 }
1069 free(path);
1070 free(last);
1071 return rc;
1072}
1073
f308ec19
KZ
1074int test_kernel_cmdline(struct libmnt_test *ts, int argc, char *argv[])
1075{
1076 char *name = argv[1];
1077 char *res;
1078
1079 res = mnt_get_kernel_cmdline_option(name);
1080 if (!res)
1081 printf("'%s' not found\n", name);
1082 else if (res == name)
1083 printf("'%s' found\n", name);
1084 else {
1085 printf("'%s' found, argument: '%s'\n", name, res);
1086 free(res);
1087 }
1088
1089 return 0;
1090}
1091
66bb8267 1092
abc9c0f7
KZ
1093int main(int argc, char *argv[])
1094{
68164f6c 1095 struct libmnt_test tss[] = {
abc9c0f7
KZ
1096 { "--match-fstype", test_match_fstype, "<type> <pattern> FS types matching" },
1097 { "--match-options", test_match_options, "<options> <pattern> options matching" },
97e23b5e 1098 { "--filesystems", test_filesystems, "[<pattern>] list /{etc,proc}/filesystems" },
b49103ed
KZ
1099 { "--starts-with", test_startswith, "<string> <prefix>" },
1100 { "--ends-with", test_endswith, "<string> <prefix>" },
0bb44be3
KZ
1101 { "--mountpoint", test_mountpoint, "<path>" },
1102 { "--fs-root", test_fsroot, "<path>" },
66bb8267 1103 { "--cd-parent", test_chdir, "<path>" },
f308ec19 1104 { "--kernel-cmdline",test_kernel_cmdline, "<option> | <option>=" },
abc9c0f7
KZ
1105 { NULL }
1106 };
1107
1108 return mnt_run_test(tss, argc, argv);
1109}
1110
1111#endif /* TEST_PROGRAM */