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