]> git.ipfire.org Git - thirdparty/util-linux.git/blame - libmount/src/utils.c
libmount: fix trivial typos
[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
61073773 145 *filename = buf;
66bb8267
KZ
146
147 if (!last || !*last)
148 memcpy(*filename, ".", 2);
149 else
150 memcpy(*filename, last, strlen(last) + 1);
151 return 0;
152err:
61073773 153 free(buf);
66bb8267
KZ
154 return rc;
155}
156
f9906424
KZ
157/*
158 * Check if @path is on read-only filesystem independently on file permissions.
159 */
160int mnt_is_readonly(const char *path)
161{
162 if (access(path, W_OK) == 0)
163 return 0;
164 if (errno == EROFS)
165 return 1;
166 if (errno != EACCES)
167 return 0;
168
169#ifdef HAVE_FUTIMENS
170 /*
171 * access(2) returns EACCES on read-only FS:
172 *
173 * - for set-uid application if one component of the path is not
174 * accessible for the current rUID. (Note that euidaccess(2) does not
175 * check for EROFS at all).
176 *
177 * - for read-write filesystem with read-only VFS node (aka -o remount,ro,bind)
178 */
179 {
180 struct timespec times[2];
181
182 times[0].tv_nsec = UTIME_NOW; /* atime */
183 times[1].tv_nsec = UTIME_OMIT; /* mtime */
184
185 if (utimensat(AT_FDCWD, path, times, 0) == -1)
186 return errno == EROFS;
187 }
188#endif
189 return 0;
190}
191
3c5e4ef8
KZ
192/**
193 * mnt_mangle:
194 * @str: string
195 *
196 * Encode @str to be compatible with fstab/mtab
197 *
198 * Returns: new allocated string or NULL in case of error.
199 */
200char *mnt_mangle(const char *str)
201{
202 return mangle(str);
203}
204
205/**
206 * mnt_unmangle:
207 * @str: string
208 *
209 * Decode @str from fstab/mtab
210 *
211 * Returns: new allocated string or NULL in case of error.
212 */
213char *mnt_unmangle(const char *str)
214{
dd369652 215 return unmangle(str, NULL);
3c5e4ef8
KZ
216}
217
69b7e41e
KZ
218/**
219 * mnt_fstype_is_pseudofs:
220 * @type: filesystem name
221 *
222 * Returns: 1 for filesystems like proc, sysfs, ... or 0.
223 */
224int mnt_fstype_is_pseudofs(const char *type)
225{
7383ebce
DR
226 /* This array must remain sorted when adding new fstypes */
227 static const char *pseudofs[] = {
228 "anon_inodefs",
229 "autofs",
230 "bdev",
231 "binfmt_misc",
232 "cgroup",
233 "configfs",
234 "cpuset",
235 "debugfs",
236 "devfs",
237 "devpts",
238 "devtmpfs",
239 "dlmfs",
240 "fuse.gvfs-fuse-daemon",
241 "fusectl",
242 "hugetlbfs",
243 "mqueue",
244 "nfsd",
245 "none",
246 "pipefs",
247 "proc",
248 "pstore",
249 "ramfs",
250 "rootfs",
251 "rpc_pipefs",
252 "securityfs",
253 "sockfs",
254 "spufs",
255 "sysfs",
256 "tmpfs"
257 };
258
259 return !(bsearch(&type, pseudofs, ARRAY_SIZE(pseudofs),
260 sizeof(char*), fstype_cmp) == NULL);
69b7e41e
KZ
261}
262
263/**
264 * mnt_fstype_is_netfs:
265 * @type: filesystem name
266 *
267 * Returns: 1 for filesystems like cifs, nfs, ... or 0.
268 */
269int mnt_fstype_is_netfs(const char *type)
270{
271 if (!type)
272 return 0;
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)
456 return 0;
457
458 while (fgets(line, sizeof(line), f)) {
459 char name[sizeof(line)];
97e23b5e
KZ
460
461 if (*line == '#' || strncmp(line, "nodev", 5) == 0)
462 continue;
463 if (sscanf(line, " %128[^\n ]\n", name) != 1)
464 continue;
465 if (pattern && !mnt_match_fstype(name, pattern))
466 continue;
467 rc = add_filesystem(filesystems, name);
468 if (rc)
4f69189f 469 break;
97e23b5e 470 }
4f69189f
KZ
471
472 fclose(f);
473 return rc;
97e23b5e
KZ
474}
475
476int mnt_get_filesystems(char ***filesystems, const char *pattern)
477{
478 int rc;
479
480 if (!filesystems)
481 return -EINVAL;
482 *filesystems = NULL;
483
484 rc = get_filesystems(_PATH_FILESYSTEMS, filesystems, pattern);
485 if (rc)
486 return rc;
487 return get_filesystems(_PATH_PROC_FILESYSTEMS, filesystems, pattern);
488}
489
2af0a839
KZ
490static size_t get_pw_record_size(void)
491{
492#ifdef _SC_GETPW_R_SIZE_MAX
493 long sz = sysconf(_SC_GETPW_R_SIZE_MAX);
494 if (sz > 0)
495 return sz;
496#endif
497 return 16384;
498}
499
69b7e41e
KZ
500/*
501 * Returns allocated string with username or NULL.
502 */
503char *mnt_get_username(const uid_t uid)
504{
505 struct passwd pwd;
506 struct passwd *res;
2af0a839 507 size_t sz = get_pw_record_size();
69b7e41e
KZ
508 char *buf, *username = NULL;
509
69b7e41e
KZ
510 buf = malloc(sz);
511 if (!buf)
512 return NULL;
513
514 if (!getpwuid_r(uid, &pwd, buf, sz, &res) && res)
515 username = strdup(pwd.pw_name);
516
517 free(buf);
518 return username;
519}
abc9c0f7 520
a1e8af75
KZ
521int mnt_get_uid(const char *username, uid_t *uid)
522{
188dc15a 523 int rc = -1;
a1e8af75
KZ
524 struct passwd pwd;
525 struct passwd *pw;
2af0a839 526 size_t sz = get_pw_record_size();
a1e8af75
KZ
527 char *buf;
528
188dc15a
KZ
529 if (!username || !uid)
530 return -EINVAL;
b47b7b3a 531
a1e8af75
KZ
532 buf = malloc(sz);
533 if (!buf)
534 return -ENOMEM;
535
188dc15a 536 if (!getpwnam_r(username, &pwd, buf, sz, &pw) && pw) {
a1e8af75 537 *uid= pw->pw_uid;
188dc15a
KZ
538 rc = 0;
539 } else {
540 DBG(UTILS, mnt_debug(
541 "cannot convert '%s' username to UID", username));
61f5ff6c 542 rc = errno ? -errno : -EINVAL;
188dc15a 543 }
a1e8af75
KZ
544
545 free(buf);
188dc15a 546 return rc;
a1e8af75
KZ
547}
548
549int mnt_get_gid(const char *groupname, gid_t *gid)
550{
188dc15a 551 int rc = -1;
a1e8af75
KZ
552 struct group grp;
553 struct group *gr;
2af0a839 554 size_t sz = get_pw_record_size();
a1e8af75
KZ
555 char *buf;
556
188dc15a
KZ
557 if (!groupname || !gid)
558 return -EINVAL;
b47b7b3a 559
a1e8af75
KZ
560 buf = malloc(sz);
561 if (!buf)
562 return -ENOMEM;
563
188dc15a 564 if (!getgrnam_r(groupname, &grp, buf, sz, &gr) && gr) {
a1e8af75 565 *gid= gr->gr_gid;
188dc15a
KZ
566 rc = 0;
567 } else {
568 DBG(UTILS, mnt_debug(
569 "cannot convert '%s' groupname to GID", groupname));
61f5ff6c 570 rc = errno ? -errno : -EINVAL;
188dc15a 571 }
a1e8af75
KZ
572
573 free(buf);
188dc15a
KZ
574 return rc;
575}
576
577int mnt_in_group(gid_t gid)
578{
579 int rc = 0, n, i;
580 gid_t *grps = NULL;
581
582 if (getgid() == gid)
583 return 1;
584
585 n = getgroups(0, NULL);
586 if (n <= 0)
587 goto done;
588
589 grps = malloc(n * sizeof(*grps));
590 if (!grps)
591 goto done;
592
593 if (getgroups(n, grps) == n) {
594 for (i = 0; i < n; i++) {
595 if (grps[i] == gid) {
596 rc = 1;
597 break;
598 }
599 }
600 }
601done:
602 free(grps);
603 return rc;
a1e8af75
KZ
604}
605
1d0cd73f
KZ
606static int try_write(const char *filename)
607{
608 int fd;
609
610 if (!filename)
611 return -EINVAL;
612
b0bb8fb6
KZ
613 fd = open(filename, O_RDWR|O_CREAT, S_IWUSR| \
614 S_IRUSR|S_IRGRP|S_IROTH);
1d0cd73f
KZ
615 if (fd >= 0) {
616 close(fd);
617 return 0;
618 }
619 return -errno;
620}
621
622/**
623 * mnt_has_regular_mtab:
624 * @mtab: returns path to mtab
625 * @writable: returns 1 if the file is writable
626 *
627 * If the file does not exist and @writable argument is not NULL then it will
628 * try to create the file
629 *
6ad929bb 630 * Returns: 1 if /etc/mtab is a regular file, and 0 in case of error (check
1d0cd73f 631 * errno for more details).
0532ba1d 632 */
1d0cd73f 633int mnt_has_regular_mtab(const char **mtab, int *writable)
0532ba1d
KZ
634{
635 struct stat st;
70bf97ae 636 int rc;
1d0cd73f 637 const char *filename = mtab && *mtab ? *mtab : mnt_get_mtab_path();
0532ba1d 638
7c118af7
KZ
639 if (writable)
640 *writable = 0;
70bf97ae 641 if (mtab && !*mtab)
1d0cd73f
KZ
642 *mtab = filename;
643
644 DBG(UTILS, mnt_debug("mtab: %s", filename));
70bf97ae 645
1d0cd73f
KZ
646 rc = lstat(filename, &st);
647
648 if (rc == 0) {
649 /* file exist */
650 if (S_ISREG(st.st_mode)) {
651 if (writable)
652 *writable = !try_write(filename);
653 return 1;
654 }
7c118af7 655 goto done;
1d0cd73f
KZ
656 }
657
658 /* try to create the file */
70bf97ae 659 if (writable) {
1d0cd73f 660 *writable = !try_write(filename);
7c118af7
KZ
661 if (*writable)
662 return 1;
1d0cd73f
KZ
663 }
664
7c118af7
KZ
665done:
666 DBG(UTILS, mnt_debug("%s: irregular/non-writable", filename));
1d0cd73f
KZ
667 return 0;
668}
7c118af7 669
77417bc0
KZ
670/*
671 * Don't export this to libmount API -- utab is private library stuff.
1d0cd73f
KZ
672 *
673 * If the file does not exist and @writable argument is not NULL then it will
a362ae60 674 * try to create the directory (e.g. /run/mount) and the file.
1d0cd73f 675 *
a362ae60 676 * Returns: 1 if utab is a regular file, and 0 in case of
6f5788c5 677 * error (check errno for more details).
1d0cd73f 678 */
1d0cd73f
KZ
679int mnt_has_regular_utab(const char **utab, int *writable)
680{
681 struct stat st;
682 int rc;
683 const char *filename = utab && *utab ? *utab : mnt_get_utab_path();
684
7c118af7
KZ
685 if (writable)
686 *writable = 0;
1d0cd73f
KZ
687 if (utab && !*utab)
688 *utab = filename;
689
690 DBG(UTILS, mnt_debug("utab: %s", filename));
691
692 rc = lstat(filename, &st);
693
694 if (rc == 0) {
695 /* file exist */
696 if (S_ISREG(st.st_mode)) {
697 if (writable)
7c118af7 698 *writable = !try_write(filename);
1d0cd73f 699 return 1;
70bf97ae 700 }
7c118af7 701 goto done; /* it's not regular file */
70bf97ae 702 }
1d0cd73f
KZ
703
704 if (writable) {
705 char *dirname = strdup(filename);
706
707 if (!dirname)
7c118af7 708 goto done;
1d0cd73f
KZ
709
710 stripoff_last_component(dirname); /* remove filename */
711
b0bb8fb6
KZ
712 rc = mkdir(dirname, S_IWUSR|
713 S_IRUSR|S_IRGRP|S_IROTH|
714 S_IXUSR|S_IXGRP|S_IXOTH);
1d0cd73f
KZ
715 free(dirname);
716 if (rc && errno != EEXIST)
7c118af7 717 goto done; /* probably EACCES */
1d0cd73f
KZ
718
719 *writable = !try_write(filename);
7c118af7
KZ
720 if (*writable)
721 return 1;
1d0cd73f 722 }
7c118af7
KZ
723done:
724 DBG(UTILS, mnt_debug("%s: irregular/non-writable file", filename));
1d0cd73f 725 return 0;
0532ba1d
KZ
726}
727
ce4dd666
KZ
728/**
729 * mnt_get_swaps_path:
730 *
731 * Returns: path to /proc/swaps or $LIBMOUNT_SWAPS.
732 */
733const char *mnt_get_swaps_path(void)
734{
735 const char *p = safe_getenv("LIBMOUNT_SWAPS");
736 return p ? : _PATH_PROC_SWAPS;
737}
738
3a5b1b1d
KZ
739/**
740 * mnt_get_fstab_path:
741 *
742 * Returns: path to /etc/fstab or $LIBMOUNT_FSTAB.
743 */
744const char *mnt_get_fstab_path(void)
745{
035507c8 746 const char *p = safe_getenv("LIBMOUNT_FSTAB");
3a5b1b1d
KZ
747 return p ? : _PATH_MNTTAB;
748}
749
750/**
751 * mnt_get_mtab_path:
752 *
b37dd175 753 * This function returns *default* location of the mtab file. The result does
0f32f1e2 754 * not have to be writable. See also mnt_has_regular_mtab().
3a5b1b1d
KZ
755 *
756 * Returns: path to /etc/mtab or $LIBMOUNT_MTAB.
757 */
758const char *mnt_get_mtab_path(void)
759{
035507c8 760 const char *p = safe_getenv("LIBMOUNT_MTAB");
3a5b1b1d
KZ
761 return p ? : _PATH_MOUNTED;
762}
763
77417bc0
KZ
764/*
765 * Don't export this to libmount API -- utab is private library stuff.
be1a5180 766 *
a362ae60 767 * Returns: path to /run/mount/utab (or /dev/.mount/utab) or $LIBMOUNT_UTAB.
b37dd175
KZ
768 */
769const char *mnt_get_utab_path(void)
770{
a362ae60 771 struct stat st;
035507c8 772 const char *p = safe_getenv("LIBMOUNT_UTAB");
a362ae60
KZ
773
774 if (p)
775 return p;
776
777 if (stat(MNT_RUNTIME_TOPDIR, &st) == 0)
778 return MNT_PATH_UTAB;
779
780 return MNT_PATH_UTAB_OLD;
b37dd175
KZ
781}
782
d1be0c34 783
b37dd175
KZ
784/* returns file descriptor or -errno, @name returns uniques filename
785 */
4b6cf485 786int mnt_open_uniq_filename(const char *filename, char **name)
b37dd175
KZ
787{
788 int rc, fd;
789 char *n;
790
791 assert(filename);
792
793 if (name)
794 *name = NULL;
795
796 rc = asprintf(&n, "%s.XXXXXX", filename);
797 if (rc <= 0)
798 return -errno;
799
4b6cf485 800 fd = mkstemp(n);
b37dd175
KZ
801 if (fd >= 0 && name)
802 *name = n;
803 else
804 free(n);
805
806 return fd < 0 ? -errno : fd;
807}
0bb44be3 808
cd3d6c5b
DR
809/**
810 * mnt_get_mountpoint:
811 * @path: pathname
812 *
813 * This function finds the mountpoint that a given path resides in. @path
814 * should be canonicalized. The returned pointer should be freed by the caller.
815 *
816 * Returns: target of mounted device or NULL on error
817 */
0bb44be3
KZ
818char *mnt_get_mountpoint(const char *path)
819{
820 char *mnt = strdup(path);
821 struct stat st;
822 dev_t dir, base;
823
824 if (!mnt)
825 return NULL;
826 if (*mnt == '/' && *(mnt + 1) == '\0')
9758c88a 827 goto done;
0bb44be3
KZ
828
829 if (stat(mnt, &st))
830 goto err;
831 base = st.st_dev;
832
833 do {
834 char *p = stripoff_last_component(mnt);
835
836 if (!p)
837 break;
838 if (stat(*mnt ? mnt : "/", &st))
839 goto err;
840 dir = st.st_dev;
841 if (dir != base) {
842 *(p - 1) = '/';
9758c88a 843 goto done;
0bb44be3
KZ
844 }
845 base = dir;
846 } while (mnt && *(mnt + 1) != '\0');
847
848 memcpy(mnt, "/", 2);
9758c88a 849done:
f84fa6f7 850 DBG(UTILS, mnt_debug("%s mountpoint is %s", path, mnt));
9758c88a 851 return mnt;
0bb44be3
KZ
852err:
853 free(mnt);
854 return NULL;
855}
856
9758c88a 857char *mnt_get_fs_root(const char *path, const char *mnt)
0bb44be3 858{
f84fa6f7 859 char *m = (char *) mnt, *res;
0bb44be3
KZ
860 const char *p;
861 size_t sz;
862
9758c88a
KZ
863 if (!m)
864 m = mnt_get_mountpoint(path);
865 if (!m)
0bb44be3
KZ
866 return NULL;
867
9758c88a 868 sz = strlen(m);
0bb44be3
KZ
869 p = sz > 1 ? path + sz : path;
870
9758c88a
KZ
871 if (m != mnt)
872 free(m);
0bb44be3 873
f84fa6f7
KZ
874 res = *p ? strdup(p) : strdup("/");
875 DBG(UTILS, mnt_debug("%s fs-root is %s", path, res));
876 return res;
0bb44be3
KZ
877}
878
abc9c0f7 879#ifdef TEST_PROGRAM
68164f6c 880int test_match_fstype(struct libmnt_test *ts, int argc, char *argv[])
abc9c0f7
KZ
881{
882 char *type = argv[1];
883 char *pattern = argv[2];
884
885 printf("%s\n", mnt_match_fstype(type, pattern) ? "MATCH" : "NOT-MATCH");
886 return 0;
887}
888
68164f6c 889int test_match_options(struct libmnt_test *ts, int argc, char *argv[])
abc9c0f7
KZ
890{
891 char *optstr = argv[1];
892 char *pattern = argv[2];
893
894 printf("%s\n", mnt_match_options(optstr, pattern) ? "MATCH" : "NOT-MATCH");
895 return 0;
896}
897
68164f6c 898int test_startswith(struct libmnt_test *ts, int argc, char *argv[])
b49103ed
KZ
899{
900 char *optstr = argv[1];
901 char *pattern = argv[2];
902
903 printf("%s\n", startswith(optstr, pattern) ? "YES" : "NOT");
904 return 0;
905}
906
68164f6c 907int test_endswith(struct libmnt_test *ts, int argc, char *argv[])
b49103ed
KZ
908{
909 char *optstr = argv[1];
910 char *pattern = argv[2];
911
912 printf("%s\n", endswith(optstr, pattern) ? "YES" : "NOT");
913 return 0;
914}
915
68164f6c 916int test_mountpoint(struct libmnt_test *ts, int argc, char *argv[])
0bb44be3
KZ
917{
918 char *path = canonicalize_path(argv[1]),
919 *mnt = path ? mnt_get_mountpoint(path) : NULL;
920
921 printf("%s: %s\n", argv[1], mnt ? : "unknown");
922 free(mnt);
923 free(path);
924 return 0;
925}
926
68164f6c 927int test_fsroot(struct libmnt_test *ts, int argc, char *argv[])
0bb44be3
KZ
928{
929 char *path = canonicalize_path(argv[1]),
d672fb26 930 *mnt = path ? mnt_get_fs_root(path, NULL) : NULL;
0bb44be3
KZ
931
932 printf("%s: %s\n", argv[1], mnt ? : "unknown");
933 free(mnt);
934 free(path);
935 return 0;
936}
abc9c0f7 937
68164f6c 938int test_filesystems(struct libmnt_test *ts, int argc, char *argv[])
97e23b5e
KZ
939{
940 char **filesystems = NULL;
941 int rc;
942
943 rc = mnt_get_filesystems(&filesystems, argc ? argv[1] : NULL);
944 if (!rc) {
945 char **p;
946 for (p = filesystems; *p; p++)
947 printf("%s\n", *p);
948 mnt_free_filesystems(filesystems);
949 }
950 return rc;
951}
952
66bb8267
KZ
953int test_chdir(struct libmnt_test *ts, int argc, char *argv[])
954{
955 int rc;
956 char *path = canonicalize_path(argv[1]),
957 *last = NULL;
958
959 if (!path)
960 return -errno;
961
962 rc = mnt_chdir_to_parent(path, &last);
963 if (!rc) {
964 printf("path='%s', abs='%s', last='%s'\n",
965 argv[1], path, last);
966 }
967 free(path);
968 free(last);
969 return rc;
970}
971
972
abc9c0f7
KZ
973int main(int argc, char *argv[])
974{
68164f6c 975 struct libmnt_test tss[] = {
abc9c0f7
KZ
976 { "--match-fstype", test_match_fstype, "<type> <pattern> FS types matching" },
977 { "--match-options", test_match_options, "<options> <pattern> options matching" },
97e23b5e 978 { "--filesystems", test_filesystems, "[<pattern>] list /{etc,proc}/filesystems" },
b49103ed
KZ
979 { "--starts-with", test_startswith, "<string> <prefix>" },
980 { "--ends-with", test_endswith, "<string> <prefix>" },
0bb44be3
KZ
981 { "--mountpoint", test_mountpoint, "<path>" },
982 { "--fs-root", test_fsroot, "<path>" },
66bb8267 983 { "--cd-parent", test_chdir, "<path>" },
abc9c0f7
KZ
984 { NULL }
985 };
986
987 return mnt_run_test(tss, argc, argv);
988}
989
990#endif /* TEST_PROGRAM */