]> git.ipfire.org Git - thirdparty/util-linux.git/blob - libmount/src/utils.c
6be7a72c0a6cd01600581ccb169da9e9de0b95d5
[thirdparty/util-linux.git] / libmount / src / utils.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 /*
3 * This file is part of libmount from util-linux project.
4 *
5 * Copyright (C) 2008-2018 Karel Zak <kzak@redhat.com>
6 *
7 * libmount is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU Lesser General Public License as published by
9 * the Free Software Foundation; either version 2.1 of the License, or
10 * (at your option) any later version.
11 */
12
13 /**
14 * SECTION: utils
15 * @title: Utils
16 * @short_description: misc utils.
17 */
18 #include <ctype.h>
19 #include <fcntl.h>
20 #include <pwd.h>
21 #include <grp.h>
22 #include <poll.h>
23 #include <blkid.h>
24
25 #include "strutils.h"
26 #include "pathnames.h"
27 #include "mountP.h"
28 #include "mangle.h"
29 #include "canonicalize.h"
30 #include "env.h"
31 #include "match.h"
32 #include "fileutils.h"
33 #include "statfs_magic.h"
34 #include "sysfs.h"
35
36 int append_string(char **a, const char *b)
37 {
38 size_t al, bl;
39 char *tmp;
40
41 assert(a);
42
43 if (!b || !*b)
44 return 0;
45 if (!*a) {
46 *a = strdup(b);
47 return !*a ? -ENOMEM : 0;
48 }
49
50 al = strlen(*a);
51 bl = strlen(b);
52
53 tmp = realloc(*a, al + bl + 1);
54 if (!tmp)
55 return -ENOMEM;
56 *a = tmp;
57 memcpy((*a) + al, b, bl + 1);
58 return 0;
59 }
60
61 /*
62 * Return 1 if the file is not accessible or empty
63 */
64 int is_file_empty(const char *name)
65 {
66 struct stat st;
67 assert(name);
68
69 return (stat(name, &st) != 0 || st.st_size == 0);
70 }
71
72 int mnt_valid_tagname(const char *tagname)
73 {
74 if (tagname && *tagname && (
75 strcmp("UUID", tagname) == 0 ||
76 strcmp("LABEL", tagname) == 0 ||
77 strcmp("PARTUUID", tagname) == 0 ||
78 strcmp("PARTLABEL", tagname) == 0))
79 return 1;
80
81 return 0;
82 }
83
84 /**
85 * mnt_tag_is_valid:
86 * @tag: NAME=value string
87 *
88 * Returns: 1 if the @tag is parsable and tag NAME= is supported by libmount, or 0.
89 */
90 int mnt_tag_is_valid(const char *tag)
91 {
92 char *t = NULL;
93 int rc = tag && blkid_parse_tag_string(tag, &t, NULL) == 0
94 && mnt_valid_tagname(t);
95
96 free(t);
97 return rc;
98 }
99
100 int mnt_parse_offset(const char *str, size_t len, uintmax_t *res)
101 {
102 char *p;
103 int rc = 0;
104
105 if (!str || !*str)
106 return -EINVAL;
107
108 p = strndup(str, len);
109 if (!p)
110 return -errno;
111
112 if (strtosize(p, res))
113 rc = -EINVAL;
114 free(p);
115 return rc;
116 }
117
118 /* used as a callback by bsearch in mnt_fstype_is_pseudofs() */
119 static int fstype_cmp(const void *v1, const void *v2)
120 {
121 const char *s1 = *(char * const *)v1;
122 const char *s2 = *(char * const *)v2;
123
124 return strcmp(s1, s2);
125 }
126
127 int mnt_stat_mountpoint(const char *target, struct stat *st)
128 {
129 #ifdef AT_NO_AUTOMOUNT
130 return fstatat(AT_FDCWD, target, st, AT_NO_AUTOMOUNT);
131 #else
132 return stat(target, st);
133 #endif
134 }
135
136 int mnt_lstat_mountpoint(const char *target, struct stat *st)
137 {
138 #ifdef AT_NO_AUTOMOUNT
139 return fstatat(AT_FDCWD, target, st, AT_NO_AUTOMOUNT | AT_SYMLINK_NOFOLLOW);
140 #else
141 return lstat(target, st);
142 #endif
143 }
144
145
146 /*
147 * Note that the @target has to be an absolute path (so at least "/"). The
148 * @filename returns an allocated buffer with the last path component, for example:
149 *
150 * mnt_chdir_to_parent("/mnt/test", &buf) ==> chdir("/mnt"), buf="test"
151 */
152 int mnt_chdir_to_parent(const char *target, char **filename)
153 {
154 char *buf, *parent, *last = NULL;
155 char cwd[PATH_MAX];
156 int rc = -EINVAL;
157
158 if (!target || *target != '/')
159 return -EINVAL;
160
161 DBG(UTILS, ul_debug("moving to %s parent", target));
162
163 buf = strdup(target);
164 if (!buf)
165 return -ENOMEM;
166
167 if (*(buf + 1) != '\0') {
168 last = stripoff_last_component(buf);
169 if (!last)
170 goto err;
171 }
172
173 parent = buf && *buf ? buf : "/";
174
175 if (chdir(parent) == -1) {
176 DBG(UTILS, ul_debug("failed to chdir to %s: %m", parent));
177 rc = -errno;
178 goto err;
179 }
180 if (!getcwd(cwd, sizeof(cwd))) {
181 DBG(UTILS, ul_debug("failed to obtain current directory: %m"));
182 rc = -errno;
183 goto err;
184 }
185 if (strcmp(cwd, parent) != 0) {
186 DBG(UTILS, ul_debug(
187 "unexpected chdir (expected=%s, cwd=%s)", parent, cwd));
188 goto err;
189 }
190
191 DBG(CXT, ul_debug(
192 "current directory moved to %s [last_component='%s']",
193 parent, last));
194
195 if (filename) {
196 *filename = buf;
197
198 if (!last || !*last)
199 memcpy(*filename, ".", 2);
200 else
201 memmove(*filename, last, strlen(last) + 1);
202 } else
203 free(buf);
204 return 0;
205 err:
206 free(buf);
207 return rc;
208 }
209
210 /*
211 * Check if @path is on a read-only filesystem independently of file permissions.
212 */
213 int mnt_is_readonly(const char *path)
214 {
215 if (access(path, W_OK) == 0)
216 return 0;
217 if (errno == EROFS)
218 return 1;
219 if (errno != EACCES)
220 return 0;
221
222 #ifdef HAVE_UTIMENSAT
223 /*
224 * access(2) returns EACCES on read-only FS:
225 *
226 * - for set-uid application if one component of the path is not
227 * accessible for the current rUID. (Note that euidaccess(2) does not
228 * check for EROFS at all).
229 *
230 * - for a read-write filesystem with a read-only VFS node (aka -o remount,ro,bind)
231 */
232 {
233 struct timespec times[2];
234
235 DBG(UTILS, ul_debug(" doing utimensat() based write test"));
236
237 times[0].tv_nsec = UTIME_NOW; /* atime */
238 times[1].tv_nsec = UTIME_OMIT; /* mtime */
239
240 if (utimensat(AT_FDCWD, path, times, 0) == -1)
241 return errno == EROFS;
242 }
243 #endif
244 return 0;
245 }
246
247 /**
248 * mnt_mangle:
249 * @str: string
250 *
251 * Encode @str to be compatible with fstab/mtab
252 *
253 * Returns: newly allocated string or NULL in case of error.
254 */
255 char *mnt_mangle(const char *str)
256 {
257 return mangle(str);
258 }
259
260 /**
261 * mnt_unmangle:
262 * @str: string
263 *
264 * Decode @str from fstab/mtab
265 *
266 * Returns: newly allocated string or NULL in case of error.
267 */
268 char *mnt_unmangle(const char *str)
269 {
270 return unmangle(str, NULL);
271 }
272
273 /**
274 * mnt_fstype_is_pseudofs:
275 * @type: filesystem name
276 *
277 * Returns: 1 for filesystems like proc, sysfs, ... or 0.
278 */
279 int mnt_fstype_is_pseudofs(const char *type)
280 {
281 /* This array must remain sorted when adding new fstypes */
282 static const char *pseudofs[] = {
283 "anon_inodefs",
284 "apparmorfs",
285 "autofs",
286 "bdev",
287 "binder",
288 "binfmt_misc",
289 "bpf",
290 "cgroup",
291 "cgroup2",
292 "configfs",
293 "cpuset",
294 "debugfs",
295 "devfs",
296 "devpts",
297 "devtmpfs",
298 "dlmfs",
299 "dmabuf",
300 "drm",
301 "efivarfs",
302 "fuse", /* Fallback name of fuse used by many poorly written drivers. */
303 "fuse.archivemount", /* Not a true pseudofs (has source), but source is not reported. */
304 "fuse.avfsd", /* Not a true pseudofs (has source), but source is not reported. */
305 "fuse.dumpfs", /* In fact, it is a netfs, but source is not reported. */
306 "fuse.encfs", /* Not a true pseudofs (has source), but source is not reported. */
307 "fuse.gvfs-fuse-daemon", /* Old name, not used by gvfs any more. */
308 "fuse.gvfsd-fuse",
309 "fuse.lxcfs",
310 "fuse.rofiles-fuse",
311 "fuse.vmware-vmblock",
312 "fuse.xwmfs",
313 "fusectl",
314 "hugetlbfs",
315 "ipathfs",
316 "mqueue",
317 "nfsd",
318 "none",
319 "nsfs",
320 "overlay",
321 "pipefs",
322 "proc",
323 "pstore",
324 "ramfs",
325 "resctrl",
326 "rootfs",
327 "rpc_pipefs",
328 "securityfs",
329 "selinuxfs",
330 "smackfs",
331 "sockfs",
332 "spufs",
333 "sysfs",
334 "tmpfs",
335 "tracefs"
336 };
337
338 assert(type);
339
340 return !(bsearch(&type, pseudofs, ARRAY_SIZE(pseudofs),
341 sizeof(char*), fstype_cmp) == NULL);
342 }
343
344 /**
345 * mnt_fstype_is_netfs:
346 * @type: filesystem name
347 *
348 * Returns: 1 for filesystems like cifs, nfs, ... or 0.
349 */
350 int mnt_fstype_is_netfs(const char *type)
351 {
352 if (strcmp(type, "cifs") == 0 ||
353 strcmp(type, "smb3") == 0 ||
354 strcmp(type, "smbfs") == 0 ||
355 strncmp(type,"nfs", 3) == 0 ||
356 strcmp(type, "afs") == 0 ||
357 strcmp(type, "ncpfs") == 0 ||
358 strcmp(type, "fuse.curlftpfs") == 0 ||
359 strcmp(type, "fuse.sshfs") == 0 ||
360 strncmp(type,"9p", 2) == 0)
361 return 1;
362 return 0;
363 }
364
365 const char *mnt_statfs_get_fstype(struct statfs *vfs)
366 {
367 assert(vfs);
368
369 switch (vfs->f_type) {
370 case STATFS_ADFS_MAGIC: return "adfs";
371 case STATFS_AFFS_MAGIC: return "affs";
372 case STATFS_AFS_MAGIC: return "afs";
373 case STATFS_AUTOFS_MAGIC: return "autofs";
374 case STATFS_BDEVFS_MAGIC: return "bdev";
375 case STATFS_BEFS_MAGIC: return "befs";
376 case STATFS_BFS_MAGIC: return "befs";
377 case STATFS_BINFMTFS_MAGIC: return "binfmt_misc";
378 case STATFS_BTRFS_MAGIC: return "btrfs";
379 case STATFS_CEPH_MAGIC: return "ceph";
380 case STATFS_CGROUP_MAGIC: return "cgroup";
381 case STATFS_CIFS_MAGIC: return "cifs";
382 case STATFS_CODA_MAGIC: return "coda";
383 case STATFS_CONFIGFS_MAGIC: return "configfs";
384 case STATFS_CRAMFS_MAGIC: return "cramfs";
385 case STATFS_DEBUGFS_MAGIC: return "debugfs";
386 case STATFS_DEVPTS_MAGIC: return "devpts";
387 case STATFS_ECRYPTFS_MAGIC: return "ecryptfs";
388 case STATFS_EFIVARFS_MAGIC: return "efivarfs";
389 case STATFS_EFS_MAGIC: return "efs";
390 case STATFS_EXOFS_MAGIC: return "exofs";
391 case STATFS_EXT4_MAGIC: return "ext4"; /* all extN use the same magic */
392 case STATFS_F2FS_MAGIC: return "f2fs";
393 case STATFS_FUSE_MAGIC: return "fuse";
394 case STATFS_FUTEXFS_MAGIC: return "futexfs";
395 case STATFS_GFS2_MAGIC: return "gfs2";
396 case STATFS_HFSPLUS_MAGIC: return "hfsplus";
397 case STATFS_HOSTFS_MAGIC: return "hostfs";
398 case STATFS_HPFS_MAGIC: return "hpfs";
399 case STATFS_HPPFS_MAGIC: return "hppfs";
400 case STATFS_HUGETLBFS_MAGIC: return "hugetlbfs";
401 case STATFS_ISOFS_MAGIC: return "iso9660";
402 case STATFS_JFFS2_MAGIC: return "jffs2";
403 case STATFS_JFS_MAGIC: return "jfs";
404 case STATFS_LOGFS_MAGIC: return "logfs";
405 case STATFS_MINIX2_MAGIC:
406 case STATFS_MINIX2_MAGIC2:
407 case STATFS_MINIX3_MAGIC:
408 case STATFS_MINIX_MAGIC:
409 case STATFS_MINIX_MAGIC2: return "minix";
410 case STATFS_MQUEUE_MAGIC: return "mqueue";
411 case STATFS_MSDOS_MAGIC: return "vfat";
412 case STATFS_NCP_MAGIC: return "ncp";
413 case STATFS_NFS_MAGIC: return "nfs";
414 case STATFS_NILFS_MAGIC: return "nilfs2";
415 case STATFS_NTFS_MAGIC: return "ntfs";
416 case STATFS_OCFS2_MAGIC: return "ocfs2";
417 case STATFS_OMFS_MAGIC: return "omfs";
418 case STATFS_OPENPROMFS_MAGIC: return "openpromfs";
419 case STATFS_PIPEFS_MAGIC: return "pipefs";
420 case STATFS_PROC_MAGIC: return "proc";
421 case STATFS_PSTOREFS_MAGIC: return "pstore";
422 case STATFS_QNX4_MAGIC: return "qnx4";
423 case STATFS_QNX6_MAGIC: return "qnx6";
424 case STATFS_RAMFS_MAGIC: return "ramfs";
425 case STATFS_REISERFS_MAGIC: return "reiser4";
426 case STATFS_ROMFS_MAGIC: return "romfs";
427 case STATFS_SECURITYFS_MAGIC: return "securityfs";
428 case STATFS_SELINUXFS_MAGIC: return "selinuxfs";
429 case STATFS_SMACKFS_MAGIC: return "smackfs";
430 case STATFS_SMB_MAGIC: return "smb";
431 case STATFS_SOCKFS_MAGIC: return "sockfs";
432 case STATFS_SQUASHFS_MAGIC: return "squashfs";
433 case STATFS_SYSFS_MAGIC: return "sysfs";
434 case STATFS_TMPFS_MAGIC: return "tmpfs";
435 case STATFS_UBIFS_MAGIC: return "ubifs";
436 case STATFS_UDF_MAGIC: return "udf";
437 case STATFS_UFS2_MAGIC:
438 case STATFS_UFS_MAGIC: return "ufs";
439 case STATFS_V9FS_MAGIC: return "9p";
440 case STATFS_VXFS_MAGIC: return "vxfs";
441 case STATFS_XENFS_MAGIC: return "xenfs";
442 case STATFS_XFS_MAGIC: return "xfs";
443 default:
444 break;
445 }
446
447 return NULL;
448 }
449
450 int is_procfs_fd(int fd)
451 {
452 struct statfs sfs;
453
454 return fstatfs(fd, &sfs) == 0 && sfs.f_type == STATFS_PROC_MAGIC;
455 }
456
457 /**
458 * mnt_match_fstype:
459 * @type: filesystem type
460 * @pattern: filesystem name or comma delimited list of names
461 *
462 * The @pattern list of filesystems can be prefixed with a global
463 * "no" prefix to invert matching of the whole list. The "no" could
464 * also be used for individual items in the @pattern list. So,
465 * "nofoo,bar" has the same meaning as "nofoo,nobar".
466 *
467 * "bar" : "nofoo,bar" -> False (global "no" prefix)
468 *
469 * "bar" : "foo,bar" -> True
470 *
471 * "bar" : "foo,nobar" -> False
472 *
473 * Returns: 1 if type is matching, else 0. This function also returns
474 * 0 if @pattern is NULL and @type is non-NULL.
475 */
476 int mnt_match_fstype(const char *type, const char *pattern)
477 {
478 return match_fstype(type, pattern);
479 }
480
481 void mnt_free_filesystems(char **filesystems)
482 {
483 char **p;
484
485 if (!filesystems)
486 return;
487 for (p = filesystems; *p; p++)
488 free(*p);
489 free(filesystems);
490 }
491
492 static int add_filesystem(char ***filesystems, char *name)
493 {
494 int n = 0;
495
496 assert(filesystems);
497 assert(name);
498
499 if (*filesystems) {
500 char **p;
501 for (n = 0, p = *filesystems; *p; p++, n++) {
502 if (strcmp(*p, name) == 0)
503 return 0;
504 }
505 }
506
507 #define MYCHUNK 16
508
509 if (n == 0 || !((n + 1) % MYCHUNK)) {
510 size_t items = ((n + 1 + MYCHUNK) / MYCHUNK) * MYCHUNK;
511 char **x = realloc(*filesystems, items * sizeof(char *));
512
513 if (!x)
514 goto err;
515 *filesystems = x;
516 }
517 name = strdup(name);
518 (*filesystems)[n] = name;
519 (*filesystems)[n + 1] = NULL;
520 if (!name)
521 goto err;
522 return 0;
523 err:
524 mnt_free_filesystems(*filesystems);
525 return -ENOMEM;
526 }
527
528 static int get_filesystems(const char *filename, char ***filesystems, const char *pattern)
529 {
530 int rc = 0;
531 FILE *f;
532 char line[129];
533
534 f = fopen(filename, "r" UL_CLOEXECSTR);
535 if (!f)
536 return 1;
537
538 DBG(UTILS, ul_debug("reading filesystems list from: %s", filename));
539
540 while (fgets(line, sizeof(line), f)) {
541 char name[sizeof(line)];
542
543 if (*line == '#' || strncmp(line, "nodev", 5) == 0)
544 continue;
545 if (sscanf(line, " %128[^\n ]\n", name) != 1)
546 continue;
547 if (strcmp(name, "*") == 0) {
548 rc = 1;
549 break; /* end of the /etc/filesystems */
550 }
551 if (pattern && !mnt_match_fstype(name, pattern))
552 continue;
553 rc = add_filesystem(filesystems, name);
554 if (rc)
555 break;
556 }
557
558 fclose(f);
559 return rc;
560 }
561
562 /*
563 * Always check the @filesystems pointer!
564 *
565 * man mount:
566 *
567 * ...mount will try to read the file /etc/filesystems, or, if that does not
568 * exist, /proc/filesystems. All of the filesystem types listed there will
569 * be tried, except for those that are labeled "nodev" (e.g., devpts,
570 * proc and nfs). If /etc/filesystems ends in a line with a single * only,
571 * mount will read /proc/filesystems afterwards.
572 */
573 int mnt_get_filesystems(char ***filesystems, const char *pattern)
574 {
575 int rc;
576
577 if (!filesystems)
578 return -EINVAL;
579
580 *filesystems = NULL;
581
582 rc = get_filesystems(_PATH_FILESYSTEMS, filesystems, pattern);
583 if (rc != 1)
584 return rc;
585
586 rc = get_filesystems(_PATH_PROC_FILESYSTEMS, filesystems, pattern);
587 if (rc == 1 && *filesystems)
588 rc = 0; /* /proc/filesystems not found */
589
590 return rc;
591 }
592
593 /*
594 * Returns an allocated string with username or NULL.
595 */
596 char *mnt_get_username(const uid_t uid)
597 {
598 struct passwd pwd;
599 struct passwd *res;
600 char *buf, *username = NULL;
601
602 buf = malloc(UL_GETPW_BUFSIZ);
603 if (!buf)
604 return NULL;
605
606 if (!getpwuid_r(uid, &pwd, buf, UL_GETPW_BUFSIZ, &res) && res)
607 username = strdup(pwd.pw_name);
608
609 free(buf);
610 return username;
611 }
612
613 int mnt_get_uid(const char *username, uid_t *uid)
614 {
615 int rc = -1;
616 struct passwd pwd;
617 struct passwd *pw;
618 char *buf;
619
620 if (!username || !uid)
621 return -EINVAL;
622
623 buf = malloc(UL_GETPW_BUFSIZ);
624 if (!buf)
625 return -ENOMEM;
626
627 if (!getpwnam_r(username, &pwd, buf, UL_GETPW_BUFSIZ, &pw) && pw) {
628 *uid= pw->pw_uid;
629 rc = 0;
630 } else {
631 DBG(UTILS, ul_debug(
632 "cannot convert '%s' username to UID", username));
633 rc = errno ? -errno : -EINVAL;
634 }
635
636 free(buf);
637 return rc;
638 }
639
640 int mnt_get_gid(const char *groupname, gid_t *gid)
641 {
642 int rc = -1;
643 struct group grp;
644 struct group *gr;
645 char *buf;
646
647 if (!groupname || !gid)
648 return -EINVAL;
649
650 buf = malloc(UL_GETPW_BUFSIZ);
651 if (!buf)
652 return -ENOMEM;
653
654 if (!getgrnam_r(groupname, &grp, buf, UL_GETPW_BUFSIZ, &gr) && gr) {
655 *gid= gr->gr_gid;
656 rc = 0;
657 } else {
658 DBG(UTILS, ul_debug(
659 "cannot convert '%s' groupname to GID", groupname));
660 rc = errno ? -errno : -EINVAL;
661 }
662
663 free(buf);
664 return rc;
665 }
666
667 int mnt_in_group(gid_t gid)
668 {
669 int rc = 0, n, i;
670 gid_t *grps = NULL;
671
672 if (getgid() == gid)
673 return 1;
674
675 n = getgroups(0, NULL);
676 if (n <= 0)
677 goto done;
678
679 grps = malloc(n * sizeof(*grps));
680 if (!grps)
681 goto done;
682
683 if (getgroups(n, grps) == n) {
684 for (i = 0; i < n; i++) {
685 if (grps[i] == gid) {
686 rc = 1;
687 break;
688 }
689 }
690 }
691 done:
692 free(grps);
693 return rc;
694 }
695
696 static int try_write(const char *filename, const char *directory)
697 {
698 int rc = 0;
699
700 if (!filename)
701 return -EINVAL;
702
703 DBG(UTILS, ul_debug("try write %s dir: %s", filename, directory));
704
705 #ifdef HAVE_EACCESS
706 /* Try eaccess() first, because open() is overkill, may be monitored by
707 * audit and we don't want to fill logs by our checks...
708 */
709 if (eaccess(filename, R_OK|W_OK) == 0) {
710 DBG(UTILS, ul_debug(" access OK"));
711 return 0;
712 }
713
714 if (errno != ENOENT) {
715 DBG(UTILS, ul_debug(" access FAILED"));
716 return -errno;
717 }
718
719 if (directory) {
720 /* file does not exist; try if directory is writable */
721 if (eaccess(directory, R_OK|W_OK) != 0)
722 rc = -errno;
723
724 DBG(UTILS, ul_debug(" access %s [%s]", rc ? "FAILED" : "OK", directory));
725 return rc;
726 }
727 #endif
728
729 DBG(UTILS, ul_debug(" doing open-write test"));
730
731 int fd = open(filename, O_RDWR|O_CREAT|O_CLOEXEC,
732 S_IWUSR|S_IRUSR|S_IRGRP|S_IROTH);
733 if (fd < 0)
734 rc = -errno;
735 else
736 close(fd);
737
738 return rc;
739 }
740
741 /**
742 * mnt_has_regular_mtab:
743 * @mtab: returns path to mtab
744 * @writable: returns 1 if the file is writable
745 *
746 * If the file does not exist and @writable argument is not NULL, then it will
747 * try to create the file.
748 *
749 * Returns: 1 if /etc/mtab is a regular file, and 0 in case of error (check
750 * errno for more details).
751 */
752 int mnt_has_regular_mtab(const char **mtab, int *writable)
753 {
754 struct stat st;
755 int rc;
756 const char *filename = mtab && *mtab ? *mtab : mnt_get_mtab_path();
757
758 if (writable)
759 *writable = 0;
760 if (mtab && !*mtab)
761 *mtab = filename;
762
763 DBG(UTILS, ul_debug("mtab: %s", filename));
764
765 rc = lstat(filename, &st);
766
767 if (rc == 0) {
768 /* file exists */
769 if (S_ISREG(st.st_mode)) {
770 if (writable)
771 *writable = !try_write(filename, NULL);
772 DBG(UTILS, ul_debug("%s: writable", filename));
773 return 1;
774 }
775 goto done;
776 }
777
778 /* try to create the file */
779 if (writable) {
780 *writable = !try_write(filename, NULL);
781 if (*writable) {
782 DBG(UTILS, ul_debug("%s: writable", filename));
783 return 1;
784 }
785 }
786
787 done:
788 DBG(UTILS, ul_debug("%s: irregular/non-writable", filename));
789 return 0;
790 }
791
792 /*
793 * Don't export this to libmount API -- utab is private library stuff.
794 *
795 * If the file does not exist and @writable argument is not NULL, then it will
796 * try to create the directory (e.g. /run/mount) and the file.
797 *
798 * Returns: 1 if utab is a regular file, and 0 in case of
799 * error (check errno for more details).
800 */
801 int mnt_has_regular_utab(const char **utab, int *writable)
802 {
803 struct stat st;
804 int rc;
805 const char *filename = utab && *utab ? *utab : mnt_get_utab_path();
806
807 if (writable)
808 *writable = 0;
809 if (utab && !*utab)
810 *utab = filename;
811
812 DBG(UTILS, ul_debug("utab: %s", filename));
813
814 rc = lstat(filename, &st);
815
816 if (rc == 0) {
817 /* file exists */
818 if (S_ISREG(st.st_mode)) {
819 if (writable)
820 *writable = !try_write(filename, NULL);
821 return 1;
822 }
823 goto done; /* it's not a regular file */
824 }
825
826 if (writable) {
827 char *dirname = strdup(filename);
828
829 if (!dirname)
830 goto done;
831
832 stripoff_last_component(dirname); /* remove filename */
833
834 rc = mkdir(dirname, S_IWUSR|
835 S_IRUSR|S_IRGRP|S_IROTH|
836 S_IXUSR|S_IXGRP|S_IXOTH);
837 if (rc && errno != EEXIST) {
838 free(dirname);
839 goto done; /* probably EACCES */
840 }
841
842 *writable = !try_write(filename, dirname);
843 free(dirname);
844 if (*writable)
845 return 1;
846 }
847 done:
848 DBG(UTILS, ul_debug("%s: irregular/non-writable file", filename));
849 return 0;
850 }
851
852 /**
853 * mnt_get_swaps_path:
854 *
855 * Returns: path to /proc/swaps or $LIBMOUNT_SWAPS.
856 */
857 const char *mnt_get_swaps_path(void)
858 {
859 const char *p = safe_getenv("LIBMOUNT_SWAPS");
860 return p ? : _PATH_PROC_SWAPS;
861 }
862
863 /**
864 * mnt_get_fstab_path:
865 *
866 * Returns: path to /etc/fstab or $LIBMOUNT_FSTAB.
867 */
868 const char *mnt_get_fstab_path(void)
869 {
870 const char *p = safe_getenv("LIBMOUNT_FSTAB");
871 return p ? : _PATH_MNTTAB;
872 }
873
874 /**
875 * mnt_get_mtab_path:
876 *
877 * This function returns the *default* location of the mtab file. The result does
878 * not have to be writable. See also mnt_has_regular_mtab().
879 *
880 * Returns: path to /etc/mtab or $LIBMOUNT_MTAB.
881 */
882 const char *mnt_get_mtab_path(void)
883 {
884 const char *p = safe_getenv("LIBMOUNT_MTAB");
885 return p ? : _PATH_MOUNTED;
886 }
887
888 /*
889 * Don't export this to libmount API -- utab is private library stuff.
890 *
891 * Returns: path to /run/mount/utab (or /dev/.mount/utab) or $LIBMOUNT_UTAB.
892 */
893 const char *mnt_get_utab_path(void)
894 {
895 struct stat st;
896 const char *p = safe_getenv("LIBMOUNT_UTAB");
897
898 if (p)
899 return p;
900
901 if (stat(MNT_RUNTIME_TOPDIR, &st) == 0)
902 return MNT_PATH_UTAB;
903
904 return MNT_PATH_UTAB_OLD;
905 }
906
907
908 /* returns file descriptor or -errno, @name returns a unique filename
909 */
910 int mnt_open_uniq_filename(const char *filename, char **name)
911 {
912 int rc, fd;
913 char *n;
914 mode_t oldmode;
915
916 if (!filename)
917 return -EINVAL;
918 if (name)
919 *name = NULL;
920
921 rc = asprintf(&n, "%s.XXXXXX", filename);
922 if (rc <= 0)
923 return -errno;
924
925 /* This is for very old glibc and for compatibility with Posix, which says
926 * nothing about mkstemp() mode. All sane glibc use secure mode (0600).
927 */
928 oldmode = umask(S_IRGRP|S_IWGRP|S_IXGRP|
929 S_IROTH|S_IWOTH|S_IXOTH);
930 fd = mkostemp(n, O_RDWR|O_CREAT|O_EXCL|O_CLOEXEC);
931 if (fd < 0)
932 fd = -errno;
933 umask(oldmode);
934
935 if (fd >= 0 && name)
936 *name = n;
937 else
938 free(n);
939
940 return fd;
941 }
942
943 /**
944 * mnt_get_mountpoint:
945 * @path: pathname
946 *
947 * This function finds the mountpoint that a given path resides in. @path
948 * should be canonicalized. The returned pointer should be freed by the caller.
949 *
950 * WARNING: the function compares st_dev of the @path elements. This traditional
951 * way may be insufficient on filesystems like Linux "overlay". See also
952 * mnt_table_find_target().
953 *
954 * Returns: allocated string with the target of the mounted device or NULL on error
955 */
956 char *mnt_get_mountpoint(const char *path)
957 {
958 char *mnt;
959 struct stat st;
960 dev_t dir, base;
961
962 if (!path)
963 return NULL;
964
965 mnt = strdup(path);
966 if (!mnt)
967 return NULL;
968 if (*mnt == '/' && *(mnt + 1) == '\0')
969 goto done;
970
971 if (mnt_stat_mountpoint(mnt, &st))
972 goto err;
973 base = st.st_dev;
974
975 do {
976 char *p = stripoff_last_component(mnt);
977
978 if (!p)
979 break;
980 if (mnt_stat_mountpoint(*mnt ? mnt : "/", &st))
981 goto err;
982 dir = st.st_dev;
983 if (dir != base) {
984 if (p > mnt)
985 *(p - 1) = '/';
986 goto done;
987 }
988 base = dir;
989 } while (mnt && *(mnt + 1) != '\0');
990
991 memcpy(mnt, "/", 2);
992 done:
993 DBG(UTILS, ul_debug("%s mountpoint is %s", path, mnt));
994 return mnt;
995 err:
996 free(mnt);
997 return NULL;
998 }
999
1000 /*
1001 * Search for @name kernel command parameter.
1002 *
1003 * Returns newly allocated string with a parameter argument if the @name is
1004 * specified as "name=" or returns pointer to @name or returns NULL if not
1005 * found. If it is specified more than once, we grab the last copy.
1006 *
1007 * For example cmdline: "aaa bbb=BBB ccc"
1008 *
1009 * @name is "aaa" --returns--> "aaa" (pointer to @name)
1010 * @name is "bbb=" --returns--> "BBB" (allocated)
1011 * @name is "foo" --returns--> NULL
1012 *
1013 * Note: It is not really feasible to parse the command line exactly the same
1014 * as the kernel does since we don't know which options are valid. We can use
1015 * the -- marker though and not walk past that.
1016 */
1017 char *mnt_get_kernel_cmdline_option(const char *name)
1018 {
1019 FILE *f;
1020 size_t len;
1021 int val = 0;
1022 char *p, *res = NULL, *mem = NULL;
1023 char buf[BUFSIZ]; /* see kernel include/asm-generic/setup.h: COMMAND_LINE_SIZE */
1024 const char *path = _PATH_PROC_CMDLINE;
1025
1026 if (!name || !name[0])
1027 return NULL;
1028
1029 #ifdef TEST_PROGRAM
1030 path = getenv("LIBMOUNT_KERNEL_CMDLINE");
1031 if (!path)
1032 path = _PATH_PROC_CMDLINE;
1033 #endif
1034 f = fopen(path, "r" UL_CLOEXECSTR);
1035 if (!f)
1036 return NULL;
1037
1038 p = fgets(buf, sizeof(buf), f);
1039 fclose(f);
1040
1041 if (!p || !*p || *p == '\n')
1042 return NULL;
1043
1044 p = strstr(p, " -- ");
1045 if (p) {
1046 /* no more kernel args after this */
1047 *p = '\0';
1048 } else {
1049 len = strlen(buf);
1050 buf[len - 1] = '\0'; /* remove last '\n' */
1051 }
1052
1053 len = strlen(name);
1054 if (name[len - 1] == '=')
1055 val = 1;
1056
1057 for (p = buf; p && *p; p++) {
1058 if (!(p = strstr(p, name)))
1059 break; /* not found the option */
1060 if (p != buf && !isblank(*(p - 1)))
1061 continue; /* no space before the option */
1062 if (!val && *(p + len) != '\0' && !isblank(*(p + len)))
1063 continue; /* no space after the option */
1064 if (val) {
1065 char *v = p + len;
1066 int end;
1067
1068 while (*p && !isblank(*p)) /* jump to the end of the argument */
1069 p++;
1070 end = (*p == '\0');
1071 *p = '\0';
1072 free(mem);
1073 res = mem = strdup(v);
1074 if (end)
1075 break;
1076 } else
1077 res = (char *) name; /* option without '=' */
1078 /* don't break -- keep scanning for more options */
1079 }
1080
1081 return res;
1082 }
1083
1084 /**
1085 * mnt_guess_system_root:
1086 * @devno: device number or zero
1087 * @cache: paths cache or NULL
1088 * @path: returns allocated path
1089 *
1090 * Converts @devno to the real device name if devno major number is greater
1091 * than zero, otherwise use root= kernel cmdline option to get device name.
1092 *
1093 * The function uses /sys to convert devno to device name.
1094 *
1095 * Returns: 0 = success, 1 = not found, <0 = error
1096 *
1097 * Since: 2.34
1098 */
1099 int mnt_guess_system_root(dev_t devno, struct libmnt_cache *cache, char **path)
1100 {
1101 char buf[PATH_MAX];
1102 char *dev = NULL, *spec = NULL;
1103 unsigned int x, y;
1104 int allocated = 0;
1105
1106 assert(path);
1107
1108 DBG(UTILS, ul_debug("guessing system root [devno %u:%u]", major(devno), minor(devno)));
1109
1110 /* The pseudo-fs, net-fs or btrfs devno is useless, otherwise it
1111 * usually matches with the source device, let's try to use it.
1112 */
1113 if (major(devno) > 0) {
1114 dev = sysfs_devno_to_devpath(devno, buf, sizeof(buf));
1115 if (dev) {
1116 DBG(UTILS, ul_debug(" devno converted to %s", dev));
1117 goto done;
1118 }
1119 }
1120
1121 /* Let's try to use root= kernel command line option
1122 */
1123 spec = mnt_get_kernel_cmdline_option("root=");
1124 if (!spec)
1125 goto done;
1126
1127 /* maj:min notation */
1128 if (sscanf(spec, "%u:%u", &x, &y) == 2) {
1129 dev = sysfs_devno_to_devpath(makedev(x, y), buf, sizeof(buf));
1130 if (dev) {
1131 DBG(UTILS, ul_debug(" root=%s converted to %s", spec, dev));
1132 goto done;
1133 }
1134
1135 /* hexhex notation */
1136 } else if (isxdigit_string(spec)) {
1137 char *end = NULL;
1138 uint32_t n;
1139
1140 errno = 0;
1141 n = strtoul(spec, &end, 16);
1142
1143 if (errno || spec == end || (end && *end))
1144 DBG(UTILS, ul_debug(" failed to parse root='%s'", spec));
1145 else {
1146 /* kernel new_decode_dev() */
1147 x = (n & 0xfff00) >> 8;
1148 y = (n & 0xff) | ((n >> 12) & 0xfff00);
1149 dev = sysfs_devno_to_devpath(makedev(x, y), buf, sizeof(buf));
1150 if (dev) {
1151 DBG(UTILS, ul_debug(" root=%s converted to %s", spec, dev));
1152 goto done;
1153 }
1154 }
1155
1156 /* devname or PARTUUID= etc. */
1157 } else {
1158 DBG(UTILS, ul_debug(" converting root='%s'", spec));
1159
1160 dev = mnt_resolve_spec(spec, cache);
1161 if (dev && !cache)
1162 allocated = 1;
1163 }
1164 done:
1165 free(spec);
1166 if (dev) {
1167 *path = allocated ? dev : strdup(dev);
1168 if (!*path)
1169 return -ENOMEM;
1170 return 0;
1171 }
1172
1173 return 1;
1174 }
1175
1176 #if defined(HAVE_FMEMOPEN) || defined(TEST_PROGRAM)
1177
1178 /*
1179 * This function tries to minimize possible races when we read
1180 * /proc/#/{mountinfo,mount} files.
1181 *
1182 * The idea is to minimize number of read()s and check by poll() that during
1183 * the read the mount table has not been modified. If yes, than re-read it
1184 * (with some limitations to avoid never ending loop).
1185 *
1186 * Returns: <0 error, 0 success, 1 too many attempts
1187 */
1188 static int read_procfs_file(int fd, char **buf, size_t *bufsiz)
1189 {
1190 size_t bufmax = 0;
1191 int rc = 0, tries = 0, ninters = 0;
1192 char *bufptr = NULL;
1193
1194 assert(buf);
1195 assert(bufsiz);
1196
1197 *bufsiz = 0;
1198 *buf = NULL;
1199
1200 do {
1201 ssize_t ret;
1202
1203 if (!bufptr || bufmax == *bufsiz) {
1204 char *tmp;
1205
1206 bufmax = bufmax ? bufmax * 2 : (16 * 1024);
1207 tmp = realloc(*buf, bufmax);
1208 if (!tmp)
1209 break;
1210 *buf = tmp;
1211 bufptr = tmp + *bufsiz;
1212 }
1213
1214 errno = 0;
1215 ret = read(fd, bufptr, bufmax - *bufsiz);
1216
1217 if (ret < 0) {
1218 /* error */
1219 if ((errno == EAGAIN || errno == EINTR) && (ninters++ < 5)) {
1220 xusleep(200000);
1221 continue;
1222 }
1223 break;
1224
1225 } if (ret > 0) {
1226 /* success -- verify no event during read */
1227 struct pollfd fds[] = {
1228 { .fd = fd, .events = POLLPRI }
1229 };
1230
1231 rc = poll(fds, 1, 0);
1232 if (rc < 0)
1233 break; /* poll() error */
1234 if (rc > 0) {
1235 /* event -- read all again */
1236 if (lseek(fd, 0, SEEK_SET) != 0)
1237 break;
1238 *bufsiz = 0;
1239 bufptr = *buf;
1240 tries++;
1241
1242 if (tries > 10)
1243 /* busy system? -- wait */
1244 xusleep(10000);
1245 continue;
1246 }
1247
1248 /* successful read() without active poll() */
1249 (*bufsiz) += (size_t) ret;
1250 bufptr += ret;
1251 tries = ninters = 0;
1252 } else {
1253 /* end-of-file */
1254 goto success;
1255 }
1256 } while (tries <= 100);
1257
1258 rc = errno ? -errno : 1;
1259 free(*buf);
1260 return rc;
1261
1262 success:
1263 return 0;
1264 }
1265
1266 /*
1267 * Create FILE stream for data from read_procfs_file()
1268 */
1269 FILE *mnt_get_procfs_memstream(int fd, char **membuf)
1270 {
1271 size_t sz = 0;
1272 off_t cur;
1273
1274 *membuf = NULL;
1275
1276 /* in case of error, rewind to the original position */
1277 cur = lseek(fd, 0, SEEK_CUR);
1278
1279 if (read_procfs_file(fd, membuf, &sz) == 0 && sz > 0) {
1280 FILE *memf = fmemopen(*membuf, sz, "r");
1281 if (memf)
1282 return memf; /* success */
1283
1284 free(*membuf);
1285 *membuf = NULL;
1286 }
1287
1288 /* error */
1289 if (cur != (off_t) -1)
1290 lseek(fd, cur, SEEK_SET);
1291 return NULL;
1292 }
1293 #else
1294 FILE *mnt_get_procfs_memstream(int fd __attribute((__unused__)),
1295 char **membuf __attribute((__unused__)))
1296 {
1297 return NULL;
1298 }
1299 #endif /* HAVE_FMEMOPEN */
1300
1301
1302 #ifdef TEST_PROGRAM
1303 static int test_proc_read(struct libmnt_test *ts, int argc, char *argv[])
1304 {
1305 char *buf = NULL;
1306 char *filename = argv[1];
1307 size_t bufsiz = 0;
1308 int rc = 0, fd = open(filename, O_RDONLY);
1309
1310 if (fd <= 0) {
1311 warn("%s: cannot open", filename);
1312 return -errno;
1313 }
1314
1315 rc = read_procfs_file(fd, &buf, &bufsiz);
1316 close(fd);
1317
1318 switch (rc) {
1319 case 0:
1320 fwrite(buf, 1, bufsiz, stdout);
1321 free(buf);
1322 break;
1323 case 1:
1324 warnx("too many attempts");
1325 break;
1326 default:
1327 warn("%s: cannot read", filename);
1328 break;
1329 }
1330
1331 return rc;
1332 }
1333
1334 static int test_match_fstype(struct libmnt_test *ts, int argc, char *argv[])
1335 {
1336 char *type = argv[1];
1337 char *pattern = argv[2];
1338
1339 printf("%s\n", mnt_match_fstype(type, pattern) ? "MATCH" : "NOT-MATCH");
1340 return 0;
1341 }
1342
1343 static int test_match_options(struct libmnt_test *ts, int argc, char *argv[])
1344 {
1345 char *optstr = argv[1];
1346 char *pattern = argv[2];
1347
1348 printf("%s\n", mnt_match_options(optstr, pattern) ? "MATCH" : "NOT-MATCH");
1349 return 0;
1350 }
1351
1352 static int test_startswith(struct libmnt_test *ts, int argc, char *argv[])
1353 {
1354 char *optstr = argv[1];
1355 char *pattern = argv[2];
1356
1357 printf("%s\n", startswith(optstr, pattern) ? "YES" : "NOT");
1358 return 0;
1359 }
1360
1361 static int test_endswith(struct libmnt_test *ts, int argc, char *argv[])
1362 {
1363 char *optstr = argv[1];
1364 char *pattern = argv[2];
1365
1366 printf("%s\n", endswith(optstr, pattern) ? "YES" : "NOT");
1367 return 0;
1368 }
1369
1370 static int test_appendstr(struct libmnt_test *ts, int argc, char *argv[])
1371 {
1372 char *str = strdup(argv[1]);
1373 const char *ap = argv[2];
1374
1375 append_string(&str, ap);
1376 printf("new string: '%s'\n", str);
1377
1378 free(str);
1379 return 0;
1380 }
1381
1382 static int test_mountpoint(struct libmnt_test *ts, int argc, char *argv[])
1383 {
1384 char *path = canonicalize_path(argv[1]),
1385 *mnt = path ? mnt_get_mountpoint(path) : NULL;
1386
1387 printf("%s: %s\n", argv[1], mnt ? : "unknown");
1388 free(mnt);
1389 free(path);
1390 return 0;
1391 }
1392
1393 static int test_filesystems(struct libmnt_test *ts, int argc, char *argv[])
1394 {
1395 char **filesystems = NULL;
1396 int rc;
1397
1398 rc = mnt_get_filesystems(&filesystems, argc ? argv[1] : NULL);
1399 if (!rc) {
1400 char **p;
1401 for (p = filesystems; *p; p++)
1402 printf("%s\n", *p);
1403 mnt_free_filesystems(filesystems);
1404 }
1405 return rc;
1406 }
1407
1408 static int test_chdir(struct libmnt_test *ts, int argc, char *argv[])
1409 {
1410 int rc;
1411 char *path = canonicalize_path(argv[1]),
1412 *last = NULL;
1413
1414 if (!path)
1415 return -errno;
1416
1417 rc = mnt_chdir_to_parent(path, &last);
1418 if (!rc) {
1419 printf("path='%s', abs='%s', last='%s'\n",
1420 argv[1], path, last);
1421 }
1422 free(path);
1423 free(last);
1424 return rc;
1425 }
1426
1427 static int test_kernel_cmdline(struct libmnt_test *ts, int argc, char *argv[])
1428 {
1429 char *name = argv[1];
1430 char *res;
1431
1432 res = mnt_get_kernel_cmdline_option(name);
1433 if (!res)
1434 printf("'%s' not found\n", name);
1435 else if (res == name)
1436 printf("'%s' found\n", name);
1437 else {
1438 printf("'%s' found, argument: '%s'\n", name, res);
1439 free(res);
1440 }
1441
1442 return 0;
1443 }
1444
1445
1446 static int test_guess_root(struct libmnt_test *ts, int argc, char *argv[])
1447 {
1448 int rc;
1449 char *real;
1450 dev_t devno = 0;
1451
1452 if (argc) {
1453 unsigned int x, y;
1454
1455 if (sscanf(argv[1], "%u:%u", &x, &y) != 2)
1456 return -EINVAL;
1457 devno = makedev(x, y);
1458 }
1459
1460 rc = mnt_guess_system_root(devno, NULL, &real);
1461 if (rc < 0)
1462 return rc;
1463 if (rc == 1)
1464 fputs("not found\n", stdout);
1465 else {
1466 printf("%s\n", real);
1467 free(real);
1468 }
1469 return 0;
1470 }
1471
1472 static int test_mkdir(struct libmnt_test *ts, int argc, char *argv[])
1473 {
1474 int rc;
1475
1476 rc = mkdir_p(argv[1], S_IRWXU |
1477 S_IRGRP | S_IXGRP |
1478 S_IROTH | S_IXOTH);
1479 if (rc)
1480 printf("mkdir %s failed\n", argv[1]);
1481 return rc;
1482 }
1483
1484 static int test_statfs_type(struct libmnt_test *ts, int argc, char *argv[])
1485 {
1486 struct statfs vfs;
1487 int rc;
1488
1489 rc = statfs(argv[1], &vfs);
1490 if (rc)
1491 printf("%s: statfs failed: %m\n", argv[1]);
1492 else
1493 printf("%-30s: statfs type: %-12s [0x%lx]\n", argv[1],
1494 mnt_statfs_get_fstype(&vfs),
1495 (long) vfs.f_type);
1496 return rc;
1497 }
1498
1499
1500 int main(int argc, char *argv[])
1501 {
1502 struct libmnt_test tss[] = {
1503 { "--match-fstype", test_match_fstype, "<type> <pattern> FS types matching" },
1504 { "--match-options", test_match_options, "<options> <pattern> options matching" },
1505 { "--filesystems", test_filesystems, "[<pattern>] list /{etc,proc}/filesystems" },
1506 { "--starts-with", test_startswith, "<string> <prefix>" },
1507 { "--ends-with", test_endswith, "<string> <prefix>" },
1508 { "--append-string", test_appendstr, "<string> <appendix>" },
1509 { "--mountpoint", test_mountpoint, "<path>" },
1510 { "--cd-parent", test_chdir, "<path>" },
1511 { "--kernel-cmdline",test_kernel_cmdline, "<option> | <option>=" },
1512 { "--guess-root", test_guess_root, "[<maj:min>]" },
1513 { "--mkdir", test_mkdir, "<path>" },
1514 { "--statfs-type", test_statfs_type, "<path>" },
1515 { "--read-procfs", test_proc_read, "<path>" },
1516
1517 { NULL }
1518 };
1519
1520 return mnt_run_test(tss, argc, argv);
1521 }
1522
1523 #endif /* TEST_PROGRAM */