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