]> git.ipfire.org Git - thirdparty/util-linux.git/blob - libmount/src/utils.c
Merge branch 'pg-manual-page-long-options' of https://github.com/jaalto/util-linux
[thirdparty/util-linux.git] / libmount / src / utils.c
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
8 /**
9 * SECTION: utils
10 * @title: Utils
11 * @short_description: misc utils.
12 */
13 #include <ctype.h>
14 #include <fcntl.h>
15 #include <pwd.h>
16 #include <grp.h>
17 #include <blkid.h>
18
19 #include "strutils.h"
20 #include "pathnames.h"
21 #include "mountP.h"
22 #include "mangle.h"
23 #include "canonicalize.h"
24 #include "env.h"
25 #include "match.h"
26 #include "statfs_magic.h"
27
28 int append_string(char **a, const char *b)
29 {
30 size_t al, bl;
31 char *tmp;
32
33 assert(a);
34
35 if (!b || !*b)
36 return 0;
37 if (!*a) {
38 *a = strdup(b);
39 return !*a ? -ENOMEM : 0;
40 }
41
42 al = strlen(*a);
43 bl = strlen(b);
44
45 tmp = realloc(*a, al + bl + 1);
46 if (!tmp)
47 return -ENOMEM;
48 *a = tmp;
49 memcpy((*a) + al, b, bl + 1);
50 return 0;
51 }
52
53 /*
54 * Return 1 if the file is not accessible or empty
55 */
56 int is_file_empty(const char *name)
57 {
58 struct stat st;
59 assert(name);
60
61 return (stat(name, &st) != 0 || st.st_size == 0);
62 }
63
64 int mnt_valid_tagname(const char *tagname)
65 {
66 if (tagname && *tagname && (
67 strcmp("UUID", tagname) == 0 ||
68 strcmp("LABEL", tagname) == 0 ||
69 strcmp("PARTUUID", tagname) == 0 ||
70 strcmp("PARTLABEL", tagname) == 0))
71 return 1;
72
73 return 0;
74 }
75
76 /**
77 * mnt_tag_is_valid:
78 * @tag: NAME=value string
79 *
80 * Returns: 1 if the @tag is parsable and tag NAME= is supported by libmount, or 0.
81 */
82 int mnt_tag_is_valid(const char *tag)
83 {
84 char *t = NULL;
85 int rc = tag && blkid_parse_tag_string(tag, &t, NULL) == 0
86 && mnt_valid_tagname(t);
87
88 free(t);
89 return rc;
90 }
91
92 int mnt_parse_offset(const char *str, size_t len, uintmax_t *res)
93 {
94 char *p;
95 int rc = 0;
96
97 if (!str || !*str)
98 return -EINVAL;
99
100 p = strndup(str, len);
101 if (!p)
102 return -errno;
103
104 if (strtosize(p, res))
105 rc = -EINVAL;
106 free(p);
107 return rc;
108 }
109
110 /* used as a callback by bsearch in mnt_fstype_is_pseudofs() */
111 static int fstype_cmp(const void *v1, const void *v2)
112 {
113 const char *s1 = *(const char **)v1;
114 const char *s2 = *(const char **)v2;
115
116 return strcmp(s1, s2);
117 }
118
119 /* returns basename and keeps dirname in the @path, if @path is "/" (root)
120 * then returns empty string */
121 char *stripoff_last_component(char *path)
122 {
123 char *p = path ? strrchr(path, '/') : NULL;
124
125 if (!p)
126 return NULL;
127 *p = '\0';
128 return p + 1;
129 }
130
131 /*
132 * Note that the @target has to be an absolute path (so at least "/"). The
133 * @filename returns an allocated buffer with the last path component, for example:
134 *
135 * mnt_chdir_to_parent("/mnt/test", &buf) ==> chdir("/mnt"), buf="test"
136 */
137 int mnt_chdir_to_parent(const char *target, char **filename)
138 {
139 char *buf, *parent, *last = NULL;
140 char cwd[PATH_MAX];
141 int rc = -EINVAL;
142
143 if (!target || *target != '/')
144 return -EINVAL;
145
146 DBG(UTILS, ul_debug("moving to %s parent", target));
147
148 buf = strdup(target);
149 if (!buf)
150 return -ENOMEM;
151
152 if (*(buf + 1) != '\0') {
153 last = stripoff_last_component(buf);
154 if (!last)
155 goto err;
156 }
157
158 parent = buf && *buf ? buf : "/";
159
160 if (chdir(parent) == -1) {
161 DBG(UTILS, ul_debug("failed to chdir to %s: %m", parent));
162 rc = -errno;
163 goto err;
164 }
165 if (!getcwd(cwd, sizeof(cwd))) {
166 DBG(UTILS, ul_debug("failed to obtain current directory: %m"));
167 rc = -errno;
168 goto err;
169 }
170 if (strcmp(cwd, parent) != 0) {
171 DBG(UTILS, ul_debug(
172 "unexpected chdir (expected=%s, cwd=%s)", parent, cwd));
173 goto err;
174 }
175
176 DBG(CXT, ul_debug(
177 "current directory moved to %s [last_component='%s']",
178 parent, last));
179
180 if (filename) {
181 *filename = buf;
182
183 if (!last || !*last)
184 memcpy(*filename, ".", 2);
185 else
186 memmove(*filename, last, strlen(last) + 1);
187 } else
188 free(buf);
189 return 0;
190 err:
191 free(buf);
192 return rc;
193 }
194
195 /*
196 * Check if @path is on a read-only filesystem independently of file permissions.
197 */
198 int mnt_is_readonly(const char *path)
199 {
200 if (access(path, W_OK) == 0)
201 return 0;
202 if (errno == EROFS)
203 return 1;
204 if (errno != EACCES)
205 return 0;
206
207 #ifdef HAVE_FUTIMENS
208 /*
209 * access(2) returns EACCES on read-only FS:
210 *
211 * - for set-uid application if one component of the path is not
212 * accessible for the current rUID. (Note that euidaccess(2) does not
213 * check for EROFS at all).
214 *
215 * - for a read-write filesystem with a read-only VFS node (aka -o remount,ro,bind)
216 */
217 {
218 struct timespec times[2];
219
220 times[0].tv_nsec = UTIME_NOW; /* atime */
221 times[1].tv_nsec = UTIME_OMIT; /* mtime */
222
223 if (utimensat(AT_FDCWD, path, times, 0) == -1)
224 return errno == EROFS;
225 }
226 #endif
227 return 0;
228 }
229
230 /**
231 * mnt_mangle:
232 * @str: string
233 *
234 * Encode @str to be compatible with fstab/mtab
235 *
236 * Returns: newly allocated string or NULL in case of error.
237 */
238 char *mnt_mangle(const char *str)
239 {
240 return mangle(str);
241 }
242
243 /**
244 * mnt_unmangle:
245 * @str: string
246 *
247 * Decode @str from fstab/mtab
248 *
249 * Returns: newly allocated string or NULL in case of error.
250 */
251 char *mnt_unmangle(const char *str)
252 {
253 return unmangle(str, NULL);
254 }
255
256 /**
257 * mnt_fstype_is_pseudofs:
258 * @type: filesystem name
259 *
260 * Returns: 1 for filesystems like proc, sysfs, ... or 0.
261 */
262 int mnt_fstype_is_pseudofs(const char *type)
263 {
264 /* This array must remain sorted when adding new fstypes */
265 static const char *pseudofs[] = {
266 "anon_inodefs",
267 "autofs",
268 "bdev",
269 "binfmt_misc",
270 "cgroup",
271 "configfs",
272 "cpuset",
273 "debugfs",
274 "devfs",
275 "devpts",
276 "devtmpfs",
277 "dlmfs",
278 "efivarfs",
279 "fusectl",
280 "fuse.gvfs-fuse-daemon",
281 "hugetlbfs",
282 "mqueue",
283 "nfsd",
284 "none",
285 "pipefs",
286 "proc",
287 "pstore",
288 "ramfs",
289 "rootfs",
290 "rpc_pipefs",
291 "securityfs",
292 "sockfs",
293 "spufs",
294 "sysfs",
295 "tmpfs"
296 };
297
298 assert(type);
299
300 return !(bsearch(&type, pseudofs, ARRAY_SIZE(pseudofs),
301 sizeof(char*), fstype_cmp) == NULL);
302 }
303
304 /**
305 * mnt_fstype_is_netfs:
306 * @type: filesystem name
307 *
308 * Returns: 1 for filesystems like cifs, nfs, ... or 0.
309 */
310 int mnt_fstype_is_netfs(const char *type)
311 {
312 assert(type);
313
314 if (strcmp(type, "cifs") == 0 ||
315 strcmp(type, "smbfs") == 0 ||
316 strncmp(type,"nfs", 3) == 0 ||
317 strcmp(type, "afs") == 0 ||
318 strcmp(type, "ncpfs") == 0 ||
319 strncmp(type,"9p", 2) == 0)
320 return 1;
321 return 0;
322 }
323
324 const char *mnt_statfs_get_fstype(struct statfs *vfs)
325 {
326 assert(vfs);
327
328 switch (vfs->f_type) {
329 case STATFS_ADFS_MAGIC: return "adfs";
330 case STATFS_AFFS_MAGIC: return "affs";
331 case STATFS_AFS_MAGIC: return "afs";
332 case STATFS_AUTOFS_MAGIC: return "autofs";
333 case STATFS_BDEVFS_MAGIC: return "bdev";
334 case STATFS_BEFS_MAGIC: return "befs";
335 case STATFS_BFS_MAGIC: return "befs";
336 case STATFS_BINFMTFS_MAGIC: return "binfmt_misc";
337 case STATFS_BTRFS_MAGIC: return "btrfs";
338 case STATFS_CEPH_MAGIC: return "ceph";
339 case STATFS_CGROUP_MAGIC: return "cgroup";
340 case STATFS_CIFS_MAGIC: return "cifs";
341 case STATFS_CODA_MAGIC: return "coda";
342 case STATFS_CONFIGFS_MAGIC: return "configfs";
343 case STATFS_CRAMFS_MAGIC: return "cramfs";
344 case STATFS_DEBUGFS_MAGIC: return "debugfs";
345 case STATFS_DEVPTS_MAGIC: return "devpts";
346 case STATFS_ECRYPTFS_MAGIC: return "ecryptfs";
347 case STATFS_EFIVARFS_MAGIC: return "efivarfs";
348 case STATFS_EFS_MAGIC: return "efs";
349 case STATFS_EXOFS_MAGIC: return "exofs";
350 case STATFS_EXT4_MAGIC: return "ext4"; /* all extN use the same magic */
351 case STATFS_F2FS_MAGIC: return "f2fs";
352 case STATFS_FUSE_MAGIC: return "fuse";
353 case STATFS_FUTEXFS_MAGIC: return "futexfs";
354 case STATFS_GFS2_MAGIC: return "gfs2";
355 case STATFS_HFSPLUS_MAGIC: return "hfsplus";
356 case STATFS_HOSTFS_MAGIC: return "hostfs";
357 case STATFS_HPFS_MAGIC: return "hpfs";
358 case STATFS_HPPFS_MAGIC: return "hppfs";
359 case STATFS_HUGETLBFS_MAGIC: return "hugetlbfs";
360 case STATFS_ISOFS_MAGIC: return "iso9660";
361 case STATFS_JFFS2_MAGIC: return "jffs2";
362 case STATFS_JFS_MAGIC: return "jfs";
363 case STATFS_LOGFS_MAGIC: return "logfs";
364 case STATFS_MINIX2_MAGIC:
365 case STATFS_MINIX2_MAGIC2:
366 case STATFS_MINIX3_MAGIC:
367 case STATFS_MINIX_MAGIC:
368 case STATFS_MINIX_MAGIC2: return "minix";
369 case STATFS_MQUEUE_MAGIC: return "mqueue";
370 case STATFS_MSDOS_MAGIC: return "vfat";
371 case STATFS_NCP_MAGIC: return "ncp";
372 case STATFS_NFS_MAGIC: return "nfs";
373 case STATFS_NILFS_MAGIC: return "nilfs2";
374 case STATFS_NTFS_MAGIC: return "ntfs";
375 case STATFS_OCFS2_MAGIC: return "ocfs2";
376 case STATFS_OMFS_MAGIC: return "omfs";
377 case STATFS_OPENPROMFS_MAGIC: return "openpromfs";
378 case STATFS_PIPEFS_MAGIC: return "pipefs";
379 case STATFS_PROC_MAGIC: return "proc";
380 case STATFS_PSTOREFS_MAGIC: return "pstore";
381 case STATFS_QNX4_MAGIC: return "qnx4";
382 case STATFS_QNX6_MAGIC: return "qnx6";
383 case STATFS_RAMFS_MAGIC: return "ramfs";
384 case STATFS_REISERFS_MAGIC: return "reiser4";
385 case STATFS_ROMFS_MAGIC: return "romfs";
386 case STATFS_SECURITYFS_MAGIC: return "securityfs";
387 case STATFS_SELINUXFS_MAGIC: return "selinuxfs";
388 case STATFS_SMACKFS_MAGIC: return "smackfs";
389 case STATFS_SMB_MAGIC: return "smb";
390 case STATFS_SOCKFS_MAGIC: return "sockfs";
391 case STATFS_SQUASHFS_MAGIC: return "squashfs";
392 case STATFS_SYSFS_MAGIC: return "sysfs";
393 case STATFS_TMPFS_MAGIC: return "tmpfs";
394 case STATFS_UBIFS_MAGIC: return "ubifs";
395 case STATFS_UDF_MAGIC: return "udf";
396 case STATFS_UFS2_MAGIC:
397 case STATFS_UFS_MAGIC: return "ufs";
398 case STATFS_V9FS_MAGIC: return "9p";
399 case STATFS_VXFS_MAGIC: return "vxfs";
400 case STATFS_XENFS_MAGIC: return "xenfs";
401 case STATFS_XFS_MAGIC: return "xfs";
402 default:
403 break;
404 }
405
406 return NULL;
407 }
408
409
410 /**
411 * mnt_match_fstype:
412 * @type: filesystem type
413 * @pattern: filesystem name or comma delimited list of names
414 *
415 * The @pattern list of filesystems can be prefixed with a global
416 * "no" prefix to invert matching of the whole list. The "no" could
417 * also be used for individual items in the @pattern list. So,
418 * "nofoo,bar" has the same meaning as "nofoo,nobar".
419 *
420 * "bar" : "nofoo,bar" -> False (global "no" prefix)
421 *
422 * "bar" : "foo,bar" -> True
423 *
424 * "bar" : "foo,nobar" -> False
425 *
426 * Returns: 1 if type is matching, else 0. This function also returns
427 * 0 if @pattern is NULL and @type is non-NULL.
428 */
429 int mnt_match_fstype(const char *type, const char *pattern)
430 {
431 return match_fstype(type, pattern);
432 }
433
434
435 /* Returns 1 if needle found or noneedle not found in haystack
436 * Otherwise returns 0
437 */
438 static int check_option(const char *haystack, size_t len,
439 const char *needle, size_t needle_len)
440 {
441 const char *p;
442 int no = 0;
443
444 if (needle_len >= 1 && *needle == '+') {
445 needle++;
446 needle_len--;
447 } else if (needle_len >= 2 && !strncmp(needle, "no", 2)) {
448 no = 1;
449 needle += 2;
450 needle_len -= 2;
451 }
452
453 for (p = haystack; p && p < haystack + len; p++) {
454 char *sep = strchr(p, ',');
455 size_t plen = sep ? (size_t) (sep - p) :
456 len - (p - haystack);
457
458 if (plen == needle_len) {
459 if (!strncmp(p, needle, plen))
460 return !no; /* foo or nofoo was found */
461 }
462 p += plen;
463 }
464
465 return no; /* foo or nofoo was not found */
466 }
467
468 /**
469 * mnt_match_options:
470 * @optstr: options string
471 * @pattern: comma delimited list of options
472 *
473 * The "no" could be used for individual items in the @options list. The "no"
474 * prefix does not have a global meaning.
475 *
476 * Unlike fs type matching, nonetdev,user and nonetdev,nouser have
477 * DIFFERENT meanings; each option is matched explicitly as specified.
478 *
479 * The "no" prefix interpretation could be disabled by the "+" prefix, for example
480 * "+noauto" matches if @optstr literally contains the "noauto" string.
481 *
482 * "xxx,yyy,zzz" : "nozzz" -> False
483 *
484 * "xxx,yyy,zzz" : "xxx,noeee" -> True
485 *
486 * "bar,zzz" : "nofoo" -> True
487 *
488 * "nofoo,bar" : "+nofoo" -> True
489 *
490 * "bar,zzz" : "+nofoo" -> False
491 *
492 *
493 * Returns: 1 if pattern is matching, else 0. This function also returns 0
494 * if @pattern is NULL and @optstr is non-NULL.
495 */
496 int mnt_match_options(const char *optstr, const char *pattern)
497 {
498 const char *p;
499 size_t len, optstr_len = 0;
500
501 if (!pattern && !optstr)
502 return 1;
503 if (!pattern)
504 return 0;
505
506 len = strlen(pattern);
507 if (optstr)
508 optstr_len = strlen(optstr);
509
510 for (p = pattern; p < pattern + len; p++) {
511 char *sep = strchr(p, ',');
512 size_t plen = sep ? (size_t) (sep - p) :
513 len - (p - pattern);
514
515 if (!plen)
516 continue; /* if two ',' appear in a row */
517
518 if (!check_option(optstr, optstr_len, p, plen))
519 return 0; /* any match failure means failure */
520
521 p += plen;
522 }
523
524 /* no match failures in list means success */
525 return 1;
526 }
527
528 void mnt_free_filesystems(char **filesystems)
529 {
530 char **p;
531
532 if (!filesystems)
533 return;
534 for (p = filesystems; *p; p++)
535 free(*p);
536 free(filesystems);
537 }
538
539 static int add_filesystem(char ***filesystems, char *name)
540 {
541 int n = 0;
542
543 assert(filesystems);
544 assert(name);
545
546 if (*filesystems) {
547 char **p;
548 for (n = 0, p = *filesystems; *p; p++, n++) {
549 if (strcmp(*p, name) == 0)
550 return 0;
551 }
552 }
553
554 #define MYCHUNK 16
555
556 if (n == 0 || !((n + 1) % MYCHUNK)) {
557 size_t items = ((n + 1 + MYCHUNK) / MYCHUNK) * MYCHUNK;
558 char **x = realloc(*filesystems, items * sizeof(char *));
559
560 if (!x)
561 goto err;
562 *filesystems = x;
563 }
564 name = strdup(name);
565 if (!name)
566 goto err;
567 (*filesystems)[n] = name;
568 (*filesystems)[n + 1] = NULL;
569 return 0;
570 err:
571 mnt_free_filesystems(*filesystems);
572 return -ENOMEM;
573 }
574
575 static int get_filesystems(const char *filename, char ***filesystems, const char *pattern)
576 {
577 int rc = 0;
578 FILE *f;
579 char line[128];
580
581 f = fopen(filename, "r" UL_CLOEXECSTR);
582 if (!f)
583 return 1;
584
585 DBG(UTILS, ul_debug("reading filesystems list from: %s", filename));
586
587 while (fgets(line, sizeof(line), f)) {
588 char name[sizeof(line)];
589
590 if (*line == '#' || strncmp(line, "nodev", 5) == 0)
591 continue;
592 if (sscanf(line, " %128[^\n ]\n", name) != 1)
593 continue;
594 if (strcmp(name, "*") == 0) {
595 rc = 1;
596 break; /* end of the /etc/filesystems */
597 }
598 if (pattern && !mnt_match_fstype(name, pattern))
599 continue;
600 rc = add_filesystem(filesystems, name);
601 if (rc)
602 break;
603 }
604
605 fclose(f);
606 return rc;
607 }
608
609 /*
610 * Always check the @filesystems pointer!
611 *
612 * man mount:
613 *
614 * ...mount will try to read the file /etc/filesystems, or, if that does not
615 * exist, /proc/filesystems. All of the filesystem types listed there will
616 * be tried, except for those that are labeled "nodev" (e.g., devpts,
617 * proc and nfs). If /etc/filesystems ends in a line with a single * only,
618 * mount will read /proc/filesystems afterwards.
619 */
620 int mnt_get_filesystems(char ***filesystems, const char *pattern)
621 {
622 int rc;
623
624 if (!filesystems)
625 return -EINVAL;
626
627 *filesystems = NULL;
628
629 rc = get_filesystems(_PATH_FILESYSTEMS, filesystems, pattern);
630 if (rc != 1)
631 return rc;
632
633 rc = get_filesystems(_PATH_PROC_FILESYSTEMS, filesystems, pattern);
634 if (rc == 1 && *filesystems)
635 rc = 0; /* /proc/filesystems not found */
636
637 return rc;
638 }
639
640 static size_t get_pw_record_size(void)
641 {
642 #ifdef _SC_GETPW_R_SIZE_MAX
643 long sz = sysconf(_SC_GETPW_R_SIZE_MAX);
644 if (sz > 0)
645 return sz;
646 #endif
647 return 16384;
648 }
649
650 /*
651 * Returns an allocated string with username or NULL.
652 */
653 char *mnt_get_username(const uid_t uid)
654 {
655 struct passwd pwd;
656 struct passwd *res;
657 size_t sz = get_pw_record_size();
658 char *buf, *username = NULL;
659
660 buf = malloc(sz);
661 if (!buf)
662 return NULL;
663
664 if (!getpwuid_r(uid, &pwd, buf, sz, &res) && res)
665 username = strdup(pwd.pw_name);
666
667 free(buf);
668 return username;
669 }
670
671 int mnt_get_uid(const char *username, uid_t *uid)
672 {
673 int rc = -1;
674 struct passwd pwd;
675 struct passwd *pw;
676 size_t sz = get_pw_record_size();
677 char *buf;
678
679 if (!username || !uid)
680 return -EINVAL;
681
682 buf = malloc(sz);
683 if (!buf)
684 return -ENOMEM;
685
686 if (!getpwnam_r(username, &pwd, buf, sz, &pw) && pw) {
687 *uid= pw->pw_uid;
688 rc = 0;
689 } else {
690 DBG(UTILS, ul_debug(
691 "cannot convert '%s' username to UID", username));
692 rc = errno ? -errno : -EINVAL;
693 }
694
695 free(buf);
696 return rc;
697 }
698
699 int mnt_get_gid(const char *groupname, gid_t *gid)
700 {
701 int rc = -1;
702 struct group grp;
703 struct group *gr;
704 size_t sz = get_pw_record_size();
705 char *buf;
706
707 if (!groupname || !gid)
708 return -EINVAL;
709
710 buf = malloc(sz);
711 if (!buf)
712 return -ENOMEM;
713
714 if (!getgrnam_r(groupname, &grp, buf, sz, &gr) && gr) {
715 *gid= gr->gr_gid;
716 rc = 0;
717 } else {
718 DBG(UTILS, ul_debug(
719 "cannot convert '%s' groupname to GID", groupname));
720 rc = errno ? -errno : -EINVAL;
721 }
722
723 free(buf);
724 return rc;
725 }
726
727 int mnt_in_group(gid_t gid)
728 {
729 int rc = 0, n, i;
730 gid_t *grps = NULL;
731
732 if (getgid() == gid)
733 return 1;
734
735 n = getgroups(0, NULL);
736 if (n <= 0)
737 goto done;
738
739 grps = malloc(n * sizeof(*grps));
740 if (!grps)
741 goto done;
742
743 if (getgroups(n, grps) == n) {
744 for (i = 0; i < n; i++) {
745 if (grps[i] == gid) {
746 rc = 1;
747 break;
748 }
749 }
750 }
751 done:
752 free(grps);
753 return rc;
754 }
755
756 static int try_write(const char *filename)
757 {
758 int fd;
759
760 if (!filename)
761 return -EINVAL;
762
763 fd = open(filename, O_RDWR|O_CREAT|O_CLOEXEC,
764 S_IWUSR|S_IRUSR|S_IRGRP|S_IROTH);
765 if (fd >= 0) {
766 close(fd);
767 return 0;
768 }
769 return -errno;
770 }
771
772 /**
773 * mnt_has_regular_mtab:
774 * @mtab: returns path to mtab
775 * @writable: returns 1 if the file is writable
776 *
777 * If the file does not exist and @writable argument is not NULL, then it will
778 * try to create the file.
779 *
780 * Returns: 1 if /etc/mtab is a regular file, and 0 in case of error (check
781 * errno for more details).
782 */
783 int mnt_has_regular_mtab(const char **mtab, int *writable)
784 {
785 struct stat st;
786 int rc;
787 const char *filename = mtab && *mtab ? *mtab : mnt_get_mtab_path();
788
789 if (writable)
790 *writable = 0;
791 if (mtab && !*mtab)
792 *mtab = filename;
793
794 DBG(UTILS, ul_debug("mtab: %s", filename));
795
796 rc = lstat(filename, &st);
797
798 if (rc == 0) {
799 /* file exists */
800 if (S_ISREG(st.st_mode)) {
801 if (writable)
802 *writable = !try_write(filename);
803 return 1;
804 }
805 goto done;
806 }
807
808 /* try to create the file */
809 if (writable) {
810 *writable = !try_write(filename);
811 if (*writable)
812 return 1;
813 }
814
815 done:
816 DBG(UTILS, ul_debug("%s: irregular/non-writable", filename));
817 return 0;
818 }
819
820 /*
821 * Don't export this to libmount API -- utab is private library stuff.
822 *
823 * If the file does not exist and @writable argument is not NULL, then it will
824 * try to create the directory (e.g. /run/mount) and the file.
825 *
826 * Returns: 1 if utab is a regular file, and 0 in case of
827 * error (check errno for more details).
828 */
829 int mnt_has_regular_utab(const char **utab, int *writable)
830 {
831 struct stat st;
832 int rc;
833 const char *filename = utab && *utab ? *utab : mnt_get_utab_path();
834
835 if (writable)
836 *writable = 0;
837 if (utab && !*utab)
838 *utab = filename;
839
840 DBG(UTILS, ul_debug("utab: %s", filename));
841
842 rc = lstat(filename, &st);
843
844 if (rc == 0) {
845 /* file exists */
846 if (S_ISREG(st.st_mode)) {
847 if (writable)
848 *writable = !try_write(filename);
849 return 1;
850 }
851 goto done; /* it's not a regular file */
852 }
853
854 if (writable) {
855 char *dirname = strdup(filename);
856
857 if (!dirname)
858 goto done;
859
860 stripoff_last_component(dirname); /* remove filename */
861
862 rc = mkdir(dirname, S_IWUSR|
863 S_IRUSR|S_IRGRP|S_IROTH|
864 S_IXUSR|S_IXGRP|S_IXOTH);
865 free(dirname);
866 if (rc && errno != EEXIST)
867 goto done; /* probably EACCES */
868
869 *writable = !try_write(filename);
870 if (*writable)
871 return 1;
872 }
873 done:
874 DBG(UTILS, ul_debug("%s: irregular/non-writable file", filename));
875 return 0;
876 }
877
878 /**
879 * mnt_get_swaps_path:
880 *
881 * Returns: path to /proc/swaps or $LIBMOUNT_SWAPS.
882 */
883 const char *mnt_get_swaps_path(void)
884 {
885 const char *p = safe_getenv("LIBMOUNT_SWAPS");
886 return p ? : _PATH_PROC_SWAPS;
887 }
888
889 /**
890 * mnt_get_fstab_path:
891 *
892 * Returns: path to /etc/fstab or $LIBMOUNT_FSTAB.
893 */
894 const char *mnt_get_fstab_path(void)
895 {
896 const char *p = safe_getenv("LIBMOUNT_FSTAB");
897 return p ? : _PATH_MNTTAB;
898 }
899
900 /**
901 * mnt_get_mtab_path:
902 *
903 * This function returns the *default* location of the mtab file. The result does
904 * not have to be writable. See also mnt_has_regular_mtab().
905 *
906 * Returns: path to /etc/mtab or $LIBMOUNT_MTAB.
907 */
908 const char *mnt_get_mtab_path(void)
909 {
910 const char *p = safe_getenv("LIBMOUNT_MTAB");
911 return p ? : _PATH_MOUNTED;
912 }
913
914 /*
915 * Don't export this to libmount API -- utab is private library stuff.
916 *
917 * Returns: path to /run/mount/utab (or /dev/.mount/utab) or $LIBMOUNT_UTAB.
918 */
919 const char *mnt_get_utab_path(void)
920 {
921 struct stat st;
922 const char *p = safe_getenv("LIBMOUNT_UTAB");
923
924 if (p)
925 return p;
926
927 if (stat(MNT_RUNTIME_TOPDIR, &st) == 0)
928 return MNT_PATH_UTAB;
929
930 return MNT_PATH_UTAB_OLD;
931 }
932
933
934 /* returns file descriptor or -errno, @name returns a unique filename
935 */
936 int mnt_open_uniq_filename(const char *filename, char **name)
937 {
938 int rc, fd;
939 char *n;
940 mode_t oldmode;
941
942 if (!filename)
943 return -EINVAL;
944 if (name)
945 *name = NULL;
946
947 rc = asprintf(&n, "%s.XXXXXX", filename);
948 if (rc <= 0)
949 return -errno;
950
951 /* This is for very old glibc and for compatibility with Posix, which says
952 * nothing about mkstemp() mode. All sane glibc use secure mode (0600).
953 */
954 oldmode = umask(S_IRGRP|S_IWGRP|S_IXGRP|
955 S_IROTH|S_IWOTH|S_IXOTH);
956 fd = mkostemp(n, O_RDWR|O_CREAT|O_EXCL|O_CLOEXEC);
957 if (fd < 0)
958 fd = -errno;
959 umask(oldmode);
960
961 if (fd >= 0 && name)
962 *name = n;
963 else
964 free(n);
965
966 return fd;
967 }
968
969 /**
970 * mnt_get_mountpoint:
971 * @path: pathname
972 *
973 * This function finds the mountpoint that a given path resides in. @path
974 * should be canonicalized. The returned pointer should be freed by the caller.
975 *
976 * Returns: allocated string with the target of the mounted device or NULL on error
977 */
978 char *mnt_get_mountpoint(const char *path)
979 {
980 char *mnt;
981 struct stat st;
982 dev_t dir, base;
983
984 assert(path);
985
986 mnt = strdup(path);
987 if (!mnt)
988 return NULL;
989 if (*mnt == '/' && *(mnt + 1) == '\0')
990 goto done;
991
992 if (stat(mnt, &st))
993 goto err;
994 base = st.st_dev;
995
996 do {
997 char *p = stripoff_last_component(mnt);
998
999 if (!p)
1000 break;
1001 if (stat(*mnt ? mnt : "/", &st))
1002 goto err;
1003 dir = st.st_dev;
1004 if (dir != base) {
1005 if (p > mnt)
1006 *(p - 1) = '/';
1007 goto done;
1008 }
1009 base = dir;
1010 } while (mnt && *(mnt + 1) != '\0');
1011
1012 memcpy(mnt, "/", 2);
1013 done:
1014 DBG(UTILS, ul_debug("%s mountpoint is %s", path, mnt));
1015 return mnt;
1016 err:
1017 free(mnt);
1018 return NULL;
1019 }
1020
1021 char *mnt_get_fs_root(const char *path, const char *mnt)
1022 {
1023 char *m = (char *) mnt, *res;
1024 const char *p;
1025 size_t sz;
1026
1027 if (!m)
1028 m = mnt_get_mountpoint(path);
1029 if (!m)
1030 return NULL;
1031
1032 sz = strlen(m);
1033 p = sz > 1 ? path + sz : path;
1034
1035 if (m != mnt)
1036 free(m);
1037
1038 res = *p ? strdup(p) : strdup("/");
1039 DBG(UTILS, ul_debug("%s fs-root is %s", path, res));
1040 return res;
1041 }
1042
1043 /*
1044 * Search for @name kernel command parametr.
1045 *
1046 * Returns newly allocated string with a parameter argument if the @name is
1047 * specified as "name=" or returns pointer to @name or returns NULL if not
1048 * found.
1049 *
1050 * For example cmdline: "aaa bbb=BBB ccc"
1051 *
1052 * @name is "aaa" --returns--> "aaa" (pointer to @name)
1053 * @name is "bbb=" --returns--> "BBB" (allocated)
1054 * @name is "foo" --returns--> NULL
1055 */
1056 char *mnt_get_kernel_cmdline_option(const char *name)
1057 {
1058 FILE *f;
1059 size_t len;
1060 int val = 0;
1061 char *p, *res = NULL;
1062 char buf[BUFSIZ]; /* see kernel include/asm-generic/setup.h: COMMAND_LINE_SIZE */
1063 const char *path = _PATH_PROC_CMDLINE;
1064
1065 if (!name)
1066 return NULL;
1067
1068 #ifdef TEST_PROGRAM
1069 path = getenv("LIBMOUNT_KERNEL_CMDLINE");
1070 if (!path)
1071 path = _PATH_PROC_CMDLINE;
1072 #endif
1073 f = fopen(path, "r" UL_CLOEXECSTR);
1074 if (!f)
1075 return NULL;
1076
1077 p = fgets(buf, sizeof(buf), f);
1078 fclose(f);
1079
1080 if (!p || !*p || *p == '\n')
1081 return NULL;
1082
1083 len = strlen(buf);
1084 *(buf + len - 1) = '\0'; /* remove last '\n' */
1085
1086 len = strlen(name);
1087 if (len && *(name + len - 1) == '=')
1088 val = 1;
1089
1090 for ( ; p && *p; p++) {
1091 if (!(p = strstr(p, name)))
1092 break; /* not found the option */
1093 if (p != buf && !isblank(*(p - 1)))
1094 continue; /* no space before the option */
1095 if (!val && *(p + len) != '\0' && !isblank(*(p + len)))
1096 continue; /* no space after the option */
1097 if (val) {
1098 char *v = p + len;
1099
1100 while (*p && !isblank(*p)) /* jump to the end of the argument */
1101 p++;
1102 *p = '\0';
1103 res = strdup(v);
1104 break;
1105 } else
1106 res = (char *) name; /* option without '=' */
1107 break;
1108 }
1109
1110 return res;
1111 }
1112
1113 int mkdir_p(const char *path, mode_t mode)
1114 {
1115 char *p, *dir;
1116 int rc = 0;
1117
1118 if (!path || !*path)
1119 return -EINVAL;
1120
1121 dir = p = strdup(path);
1122 if (!dir)
1123 return -ENOMEM;
1124
1125 if (*p == '/')
1126 p++;
1127
1128 while (p && *p) {
1129 char *e = strchr(p, '/');
1130 if (e)
1131 *e = '\0';
1132 if (*p) {
1133 rc = mkdir(dir, mode);
1134 if (rc && errno != EEXIST)
1135 break;
1136 rc = 0;
1137 }
1138 if (!e)
1139 break;
1140 *e = '/';
1141 p = e + 1;
1142 }
1143
1144 DBG(UTILS, ul_debug("%s mkdir %s", path, rc ? "FAILED" : "SUCCESS"));
1145
1146 free(dir);
1147 return rc;
1148 }
1149
1150 #ifdef TEST_PROGRAM
1151 int test_match_fstype(struct libmnt_test *ts, int argc, char *argv[])
1152 {
1153 char *type = argv[1];
1154 char *pattern = argv[2];
1155
1156 printf("%s\n", mnt_match_fstype(type, pattern) ? "MATCH" : "NOT-MATCH");
1157 return 0;
1158 }
1159
1160 int test_match_options(struct libmnt_test *ts, int argc, char *argv[])
1161 {
1162 char *optstr = argv[1];
1163 char *pattern = argv[2];
1164
1165 printf("%s\n", mnt_match_options(optstr, pattern) ? "MATCH" : "NOT-MATCH");
1166 return 0;
1167 }
1168
1169 int test_startswith(struct libmnt_test *ts, int argc, char *argv[])
1170 {
1171 char *optstr = argv[1];
1172 char *pattern = argv[2];
1173
1174 printf("%s\n", startswith(optstr, pattern) ? "YES" : "NOT");
1175 return 0;
1176 }
1177
1178 int test_endswith(struct libmnt_test *ts, int argc, char *argv[])
1179 {
1180 char *optstr = argv[1];
1181 char *pattern = argv[2];
1182
1183 printf("%s\n", endswith(optstr, pattern) ? "YES" : "NOT");
1184 return 0;
1185 }
1186
1187 int test_appendstr(struct libmnt_test *ts, int argc, char *argv[])
1188 {
1189 char *str = strdup(argv[1]);
1190 const char *ap = argv[2];
1191
1192 append_string(&str, ap);
1193 printf("new string: '%s'\n", str);
1194
1195 free(str);
1196 return 0;
1197 }
1198
1199 int test_mountpoint(struct libmnt_test *ts, int argc, char *argv[])
1200 {
1201 char *path = canonicalize_path(argv[1]),
1202 *mnt = path ? mnt_get_mountpoint(path) : NULL;
1203
1204 printf("%s: %s\n", argv[1], mnt ? : "unknown");
1205 free(mnt);
1206 free(path);
1207 return 0;
1208 }
1209
1210 int test_fsroot(struct libmnt_test *ts, int argc, char *argv[])
1211 {
1212 char *path = canonicalize_path(argv[1]),
1213 *mnt = path ? mnt_get_fs_root(path, NULL) : NULL;
1214
1215 printf("%s: %s\n", argv[1], mnt ? : "unknown");
1216 free(mnt);
1217 free(path);
1218 return 0;
1219 }
1220
1221 int test_filesystems(struct libmnt_test *ts, int argc, char *argv[])
1222 {
1223 char **filesystems = NULL;
1224 int rc;
1225
1226 rc = mnt_get_filesystems(&filesystems, argc ? argv[1] : NULL);
1227 if (!rc) {
1228 char **p;
1229 for (p = filesystems; *p; p++)
1230 printf("%s\n", *p);
1231 mnt_free_filesystems(filesystems);
1232 }
1233 return rc;
1234 }
1235
1236 int test_chdir(struct libmnt_test *ts, int argc, char *argv[])
1237 {
1238 int rc;
1239 char *path = canonicalize_path(argv[1]),
1240 *last = NULL;
1241
1242 if (!path)
1243 return -errno;
1244
1245 rc = mnt_chdir_to_parent(path, &last);
1246 if (!rc) {
1247 printf("path='%s', abs='%s', last='%s'\n",
1248 argv[1], path, last);
1249 }
1250 free(path);
1251 free(last);
1252 return rc;
1253 }
1254
1255 int test_kernel_cmdline(struct libmnt_test *ts, int argc, char *argv[])
1256 {
1257 char *name = argv[1];
1258 char *res;
1259
1260 res = mnt_get_kernel_cmdline_option(name);
1261 if (!res)
1262 printf("'%s' not found\n", name);
1263 else if (res == name)
1264 printf("'%s' found\n", name);
1265 else {
1266 printf("'%s' found, argument: '%s'\n", name, res);
1267 free(res);
1268 }
1269
1270 return 0;
1271 }
1272
1273 int test_mkdir(struct libmnt_test *ts, int argc, char *argv[])
1274 {
1275 int rc;
1276
1277 rc = mkdir_p(argv[1], S_IRWXU |
1278 S_IRGRP | S_IXGRP |
1279 S_IROTH | S_IXOTH);
1280 if (rc)
1281 printf("mkdir %s failed\n", argv[1]);
1282 return rc;
1283 }
1284
1285 int test_statfs_type(struct libmnt_test *ts, int argc, char *argv[])
1286 {
1287 struct statfs vfs;
1288 int rc;
1289
1290 rc = statfs(argv[1], &vfs);
1291 if (rc)
1292 printf("%s: statfs failed: %m\n", argv[1]);
1293 else
1294 printf("%-30s: statfs type: %-12s [0x%lx]\n", argv[1],
1295 mnt_statfs_get_fstype(&vfs),
1296 (long) vfs.f_type);
1297 return rc;
1298 }
1299
1300
1301 int main(int argc, char *argv[])
1302 {
1303 struct libmnt_test tss[] = {
1304 { "--match-fstype", test_match_fstype, "<type> <pattern> FS types matching" },
1305 { "--match-options", test_match_options, "<options> <pattern> options matching" },
1306 { "--filesystems", test_filesystems, "[<pattern>] list /{etc,proc}/filesystems" },
1307 { "--starts-with", test_startswith, "<string> <prefix>" },
1308 { "--ends-with", test_endswith, "<string> <prefix>" },
1309 { "--append-string", test_appendstr, "<string> <appendix>" },
1310 { "--mountpoint", test_mountpoint, "<path>" },
1311 { "--fs-root", test_fsroot, "<path>" },
1312 { "--cd-parent", test_chdir, "<path>" },
1313 { "--kernel-cmdline",test_kernel_cmdline, "<option> | <option>=" },
1314 { "--mkdir", test_mkdir, "<path>" },
1315 { "--statfs-type", test_statfs_type, "<path>" },
1316
1317 { NULL }
1318 };
1319
1320 return mnt_run_test(tss, argc, argv);
1321 }
1322
1323 #endif /* TEST_PROGRAM */