]> git.ipfire.org Git - thirdparty/util-linux.git/blob - libmount/src/utils.c
misc: use everywhere mkstemp_cloexec() as fallback to mkostemp()
[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 <blkid.h>
23
24 #include "strutils.h"
25 #include "pathnames.h"
26 #include "mountP.h"
27 #include "mangle.h"
28 #include "canonicalize.h"
29 #include "env.h"
30 #include "match.h"
31 #include "fileutils.h"
32 #include "statfs_magic.h"
33 #include "sysfs.h"
34
35 /*
36 * Return 1 if the file is not accessible or empty
37 */
38 int is_file_empty(const char *name)
39 {
40 struct stat st;
41 assert(name);
42
43 return (stat(name, &st) != 0 || st.st_size == 0);
44 }
45
46 int mnt_valid_tagname(const char *tagname)
47 {
48 if (tagname && *tagname && (
49 strcmp("ID", tagname) == 0 ||
50 strcmp("UUID", tagname) == 0 ||
51 strcmp("LABEL", tagname) == 0 ||
52 strcmp("PARTUUID", tagname) == 0 ||
53 strcmp("PARTLABEL", tagname) == 0))
54 return 1;
55
56 return 0;
57 }
58
59 /**
60 * mnt_tag_is_valid:
61 * @tag: NAME=value string
62 *
63 * Returns: 1 if the @tag is parsable and tag NAME= is supported by libmount, or 0.
64 */
65 int mnt_tag_is_valid(const char *tag)
66 {
67 char *t = NULL;
68 int rc = tag && blkid_parse_tag_string(tag, &t, NULL) == 0
69 && mnt_valid_tagname(t);
70
71 free(t);
72 return rc;
73 }
74
75 int mnt_parse_offset(const char *str, size_t len, uintmax_t *res)
76 {
77 char *p;
78 int rc = 0;
79
80 if (!str || !*str)
81 return -EINVAL;
82
83 p = strndup(str, len);
84 if (!p)
85 return -errno;
86
87 if (strtosize(p, res))
88 rc = -EINVAL;
89 free(p);
90 return rc;
91 }
92
93 /* used as a callback by bsearch in mnt_fstype_is_pseudofs() */
94 static int fstype_cmp(const void *v1, const void *v2)
95 {
96 const char *s1 = *(char * const *)v1;
97 const char *s2 = *(char * const *)v2;
98
99 return strcmp(s1, s2);
100 }
101
102 int mnt_stat_mountpoint(const char *target, struct stat *st)
103 {
104 #ifdef AT_NO_AUTOMOUNT
105 return fstatat(AT_FDCWD, target, st, AT_NO_AUTOMOUNT);
106 #else
107 return stat(target, st);
108 #endif
109 }
110
111 int mnt_lstat_mountpoint(const char *target, struct stat *st)
112 {
113 #ifdef AT_NO_AUTOMOUNT
114 return fstatat(AT_FDCWD, target, st, AT_NO_AUTOMOUNT | AT_SYMLINK_NOFOLLOW);
115 #else
116 return lstat(target, st);
117 #endif
118 }
119
120
121 /*
122 * Note that the @target has to be an absolute path (so at least "/"). The
123 * @filename returns an allocated buffer with the last path component, for example:
124 *
125 * mnt_chdir_to_parent("/mnt/test", &buf) ==> chdir("/mnt"), buf="test"
126 */
127 int mnt_chdir_to_parent(const char *target, char **filename)
128 {
129 char *buf, *parent, *last = NULL;
130 char cwd[PATH_MAX];
131 int rc = -EINVAL;
132
133 if (!target || *target != '/')
134 return -EINVAL;
135
136 DBG(UTILS, ul_debug("moving to %s parent", target));
137
138 buf = strdup(target);
139 if (!buf)
140 return -ENOMEM;
141
142 if (*(buf + 1) != '\0') {
143 last = stripoff_last_component(buf);
144 if (!last)
145 goto err;
146 }
147
148 parent = buf && *buf ? buf : "/";
149
150 if (chdir(parent) == -1) {
151 DBG(UTILS, ul_debug("failed to chdir to %s: %m", parent));
152 rc = -errno;
153 goto err;
154 }
155 if (!getcwd(cwd, sizeof(cwd))) {
156 DBG(UTILS, ul_debug("failed to obtain current directory: %m"));
157 rc = -errno;
158 goto err;
159 }
160 if (strcmp(cwd, parent) != 0) {
161 DBG(UTILS, ul_debug(
162 "unexpected chdir (expected=%s, cwd=%s)", parent, cwd));
163 goto err;
164 }
165
166 DBG(CXT, ul_debug(
167 "current directory moved to %s [last_component='%s']",
168 parent, last));
169
170 if (filename) {
171 *filename = buf;
172
173 if (!last || !*last)
174 memcpy(*filename, ".", 2);
175 else
176 memmove(*filename, last, strlen(last) + 1);
177 } else
178 free(buf);
179 return 0;
180 err:
181 free(buf);
182 return rc;
183 }
184
185 /*
186 * Check if @path is on a read-only filesystem independently of file permissions.
187 */
188 int mnt_is_readonly(const char *path)
189 {
190 if (access(path, W_OK) == 0)
191 return 0;
192 if (errno == EROFS)
193 return 1;
194 if (errno != EACCES)
195 return 0;
196
197 #ifdef HAVE_UTIMENSAT
198 /*
199 * access(2) returns EACCES on read-only FS:
200 *
201 * - for set-uid application if one component of the path is not
202 * accessible for the current rUID. (Note that euidaccess(2) does not
203 * check for EROFS at all).
204 *
205 * - for a read-write filesystem with a read-only VFS node (aka -o remount,ro,bind)
206 */
207 {
208 struct timespec times[2];
209
210 DBG(UTILS, ul_debug(" doing utimensat() based write test"));
211
212 times[0].tv_nsec = UTIME_NOW; /* atime */
213 times[1].tv_nsec = UTIME_OMIT; /* mtime */
214
215 if (utimensat(AT_FDCWD, path, times, 0) == -1)
216 return errno == EROFS;
217 }
218 #endif
219 return 0;
220 }
221
222 /**
223 * mnt_mangle:
224 * @str: string
225 *
226 * Encode @str to be compatible with fstab/mtab
227 *
228 * Returns: newly allocated string or NULL in case of error.
229 */
230 char *mnt_mangle(const char *str)
231 {
232 return mangle(str);
233 }
234
235 /**
236 * mnt_unmangle:
237 * @str: string
238 *
239 * Decode @str from fstab/mtab
240 *
241 * Returns: newly allocated string or NULL in case of error.
242 */
243 char *mnt_unmangle(const char *str)
244 {
245 return unmangle(str, NULL);
246 }
247
248 /**
249 * mnt_fstype_is_pseudofs:
250 * @type: filesystem name
251 *
252 * Returns: 1 for filesystems like proc, sysfs, ... or 0.
253 */
254 int mnt_fstype_is_pseudofs(const char *type)
255 {
256 /* This array must remain sorted when adding new fstypes */
257 static const char *pseudofs[] = {
258 "anon_inodefs",
259 "apparmorfs",
260 "autofs",
261 "bdev",
262 "binder",
263 "binfmt_misc",
264 "bpf",
265 "cgroup",
266 "cgroup2",
267 "configfs",
268 "cpuset",
269 "debugfs",
270 "devfs",
271 "devpts",
272 "devtmpfs",
273 "dlmfs",
274 "dmabuf",
275 "drm",
276 "efivarfs",
277 "fuse", /* Fallback name of fuse used by many poorly written drivers. */
278 "fuse.archivemount", /* Not a true pseudofs (has source), but source is not reported. */
279 "fuse.avfsd", /* Not a true pseudofs (has source), but source is not reported. */
280 "fuse.dumpfs", /* In fact, it is a netfs, but source is not reported. */
281 "fuse.encfs", /* Not a true pseudofs (has source), but source is not reported. */
282 "fuse.gvfs-fuse-daemon", /* Old name, not used by gvfs any more. */
283 "fuse.gvfsd-fuse",
284 "fuse.lxcfs",
285 "fuse.rofiles-fuse",
286 "fuse.vmware-vmblock",
287 "fuse.xwmfs",
288 "fusectl",
289 "hugetlbfs",
290 "ipathfs",
291 "mqueue",
292 "nfsd",
293 "none",
294 "nsfs",
295 "overlay",
296 "pipefs",
297 "proc",
298 "pstore",
299 "ramfs",
300 "resctrl",
301 "rootfs",
302 "rpc_pipefs",
303 "securityfs",
304 "selinuxfs",
305 "smackfs",
306 "sockfs",
307 "spufs",
308 "sysfs",
309 "tmpfs",
310 "tracefs",
311 "vboxsf",
312 "virtiofs"
313 };
314
315 assert(type);
316
317 return !(bsearch(&type, pseudofs, ARRAY_SIZE(pseudofs),
318 sizeof(char*), fstype_cmp) == NULL);
319 }
320
321 /**
322 * mnt_fstype_is_netfs:
323 * @type: filesystem name
324 *
325 * Returns: 1 for filesystems like cifs, nfs, ... or 0.
326 */
327 int mnt_fstype_is_netfs(const char *type)
328 {
329 if (strcmp(type, "cifs") == 0 ||
330 strcmp(type, "smb3") == 0 ||
331 strcmp(type, "smbfs") == 0 ||
332 strncmp(type,"nfs", 3) == 0 ||
333 strcmp(type, "afs") == 0 ||
334 strcmp(type, "ncpfs") == 0 ||
335 strcmp(type, "glusterfs") == 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 /**
429 * mnt_match_fstype:
430 * @type: filesystem type
431 * @pattern: filesystem name or comma delimited list of names
432 *
433 * The @pattern list of filesystems can be prefixed with a global
434 * "no" prefix to invert matching of the whole list. The "no" could
435 * also be used for individual items in the @pattern list. So,
436 * "nofoo,bar" has the same meaning as "nofoo,nobar".
437 *
438 * "bar" : "nofoo,bar" -> False (global "no" prefix)
439 *
440 * "bar" : "foo,bar" -> True
441 *
442 * "bar" : "foo,nobar" -> False
443 *
444 * Returns: 1 if type is matching, else 0. This function also returns
445 * 0 if @pattern is NULL and @type is non-NULL.
446 */
447 int mnt_match_fstype(const char *type, const char *pattern)
448 {
449 return match_fstype(type, pattern);
450 }
451
452 void mnt_free_filesystems(char **filesystems)
453 {
454 char **p;
455
456 if (!filesystems)
457 return;
458 for (p = filesystems; *p; p++)
459 free(*p);
460 free(filesystems);
461 }
462
463 static int add_filesystem(char ***filesystems, char *name)
464 {
465 int n = 0;
466
467 assert(filesystems);
468 assert(name);
469
470 if (*filesystems) {
471 char **p;
472 for (n = 0, p = *filesystems; *p; p++, n++) {
473 if (strcmp(*p, name) == 0)
474 return 0;
475 }
476 }
477
478 #define MYCHUNK 16
479
480 if (n == 0 || !((n + 1) % MYCHUNK)) {
481 size_t items = ((n + 1 + MYCHUNK) / MYCHUNK) * MYCHUNK;
482 char **x = realloc(*filesystems, items * sizeof(char *));
483
484 if (!x)
485 goto err;
486 *filesystems = x;
487 }
488 name = strdup(name);
489 (*filesystems)[n] = name;
490 (*filesystems)[n + 1] = NULL;
491 if (!name)
492 goto err;
493 return 0;
494 err:
495 mnt_free_filesystems(*filesystems);
496 return -ENOMEM;
497 }
498
499 static int get_filesystems(const char *filename, char ***filesystems, const char *pattern)
500 {
501 int rc = 0;
502 FILE *f;
503 char line[129];
504
505 f = fopen(filename, "r" UL_CLOEXECSTR);
506 if (!f)
507 return 1;
508
509 DBG(UTILS, ul_debug("reading filesystems list from: %s", filename));
510
511 while (fgets(line, sizeof(line), f)) {
512 char name[sizeof(line)];
513
514 if (*line == '#' || strncmp(line, "nodev", 5) == 0)
515 continue;
516 if (sscanf(line, " %128[^\n ]\n", name) != 1)
517 continue;
518 if (strcmp(name, "*") == 0) {
519 rc = 1;
520 break; /* end of the /etc/filesystems */
521 }
522 if (pattern && !mnt_match_fstype(name, pattern))
523 continue;
524 rc = add_filesystem(filesystems, name);
525 if (rc)
526 break;
527 }
528
529 fclose(f);
530 return rc;
531 }
532
533 /*
534 * Always check the @filesystems pointer!
535 *
536 * man mount:
537 *
538 * ...mount will try to read the file /etc/filesystems, or, if that does not
539 * exist, /proc/filesystems. All of the filesystem types listed there will
540 * be tried, except for those that are labeled "nodev" (e.g., devpts,
541 * proc and nfs). If /etc/filesystems ends in a line with a single * only,
542 * mount will read /proc/filesystems afterwards.
543 */
544 int mnt_get_filesystems(char ***filesystems, const char *pattern)
545 {
546 int rc;
547
548 if (!filesystems)
549 return -EINVAL;
550
551 *filesystems = NULL;
552
553 rc = get_filesystems(_PATH_FILESYSTEMS, filesystems, pattern);
554 if (rc != 1)
555 return rc;
556
557 rc = get_filesystems(_PATH_PROC_FILESYSTEMS, filesystems, pattern);
558 if (rc == 1 && *filesystems)
559 rc = 0; /* /proc/filesystems not found */
560
561 return rc;
562 }
563
564 /*
565 * Returns an allocated string with username or NULL.
566 */
567 char *mnt_get_username(const uid_t uid)
568 {
569 struct passwd pwd;
570 struct passwd *res;
571 char *buf, *username = NULL;
572
573 buf = malloc(UL_GETPW_BUFSIZ);
574 if (!buf)
575 return NULL;
576
577 if (!getpwuid_r(uid, &pwd, buf, UL_GETPW_BUFSIZ, &res) && res)
578 username = strdup(pwd.pw_name);
579
580 free(buf);
581 return username;
582 }
583
584 int mnt_get_uid(const char *username, uid_t *uid)
585 {
586 int rc = -1;
587 struct passwd pwd;
588 struct passwd *pw;
589 char *buf;
590
591 if (!username || !uid)
592 return -EINVAL;
593
594 buf = malloc(UL_GETPW_BUFSIZ);
595 if (!buf)
596 return -ENOMEM;
597
598 if (!getpwnam_r(username, &pwd, buf, UL_GETPW_BUFSIZ, &pw) && pw) {
599 *uid= pw->pw_uid;
600 rc = 0;
601 } else {
602 DBG(UTILS, ul_debug(
603 "cannot convert '%s' username to UID", username));
604 rc = errno ? -errno : -EINVAL;
605 }
606
607 free(buf);
608 return rc;
609 }
610
611 int mnt_get_gid(const char *groupname, gid_t *gid)
612 {
613 int rc = -1;
614 struct group grp;
615 struct group *gr;
616 char *buf;
617
618 if (!groupname || !gid)
619 return -EINVAL;
620
621 buf = malloc(UL_GETPW_BUFSIZ);
622 if (!buf)
623 return -ENOMEM;
624
625 if (!getgrnam_r(groupname, &grp, buf, UL_GETPW_BUFSIZ, &gr) && gr) {
626 *gid= gr->gr_gid;
627 rc = 0;
628 } else {
629 DBG(UTILS, ul_debug(
630 "cannot convert '%s' groupname to GID", groupname));
631 rc = errno ? -errno : -EINVAL;
632 }
633
634 free(buf);
635 return rc;
636 }
637
638 int mnt_in_group(gid_t gid)
639 {
640 int rc = 0, n, i;
641 gid_t *grps = NULL;
642
643 if (getgid() == gid)
644 return 1;
645
646 n = getgroups(0, NULL);
647 if (n <= 0)
648 goto done;
649
650 grps = malloc(n * sizeof(*grps));
651 if (!grps)
652 goto done;
653
654 if (getgroups(n, grps) == n) {
655 for (i = 0; i < n; i++) {
656 if (grps[i] == gid) {
657 rc = 1;
658 break;
659 }
660 }
661 }
662 done:
663 free(grps);
664 return rc;
665 }
666
667 static int try_write(const char *filename, const char *directory)
668 {
669 int rc = 0;
670
671 if (!filename)
672 return -EINVAL;
673
674 DBG(UTILS, ul_debug("try write %s dir: %s", filename, directory));
675
676 #ifdef HAVE_EACCESS
677 /* Try eaccess() first, because open() is overkill, may be monitored by
678 * audit and we don't want to fill logs by our checks...
679 */
680 if (eaccess(filename, R_OK|W_OK) == 0) {
681 DBG(UTILS, ul_debug(" access OK"));
682 return 0;
683 }
684
685 if (errno != ENOENT) {
686 DBG(UTILS, ul_debug(" access FAILED"));
687 return -errno;
688 }
689
690 if (directory) {
691 /* file does not exist; try if directory is writable */
692 if (eaccess(directory, R_OK|W_OK) != 0)
693 rc = -errno;
694
695 DBG(UTILS, ul_debug(" access %s [%s]", rc ? "FAILED" : "OK", directory));
696 return rc;
697 }
698 #endif
699
700 DBG(UTILS, ul_debug(" doing open-write test"));
701
702 int fd = open(filename, O_RDWR|O_CREAT|O_CLOEXEC,
703 S_IWUSR|S_IRUSR|S_IRGRP|S_IROTH);
704 if (fd < 0)
705 rc = -errno;
706 else
707 close(fd);
708
709 return rc;
710 }
711
712 /**
713 * mnt_has_regular_mtab:
714 * @mtab: returns path to mtab
715 * @writable: returns 1 if the file is writable
716 *
717 * If the file does not exist and @writable argument is not NULL, then it will
718 * try to create the file.
719 *
720 * Returns: 1 if /etc/mtab is a regular file, and 0 in case of error (check
721 * errno for more details).
722 */
723 int mnt_has_regular_mtab(const char **mtab, int *writable)
724 {
725 struct stat st;
726 int rc;
727 const char *filename = mtab && *mtab ? *mtab : mnt_get_mtab_path();
728
729 if (writable)
730 *writable = 0;
731 if (mtab && !*mtab)
732 *mtab = filename;
733
734 DBG(UTILS, ul_debug("mtab: %s", filename));
735
736 rc = lstat(filename, &st);
737
738 if (rc == 0) {
739 /* file exists */
740 if (S_ISREG(st.st_mode)) {
741 if (writable)
742 *writable = !try_write(filename, NULL);
743 DBG(UTILS, ul_debug("%s: writable", filename));
744 return 1;
745 }
746 goto done;
747 }
748
749 /* try to create the file */
750 if (writable) {
751 *writable = !try_write(filename, NULL);
752 if (*writable) {
753 DBG(UTILS, ul_debug("%s: writable", filename));
754 return 1;
755 }
756 }
757
758 done:
759 DBG(UTILS, ul_debug("%s: irregular/non-writable", filename));
760 return 0;
761 }
762
763 /*
764 * Don't export this to libmount API -- utab is private library stuff.
765 *
766 * If the file does not exist and @writable argument is not NULL, then it will
767 * try to create the directory (e.g. /run/mount) and the file.
768 *
769 * Returns: 1 if utab is a regular file, and 0 in case of
770 * error (check errno for more details).
771 */
772 int mnt_has_regular_utab(const char **utab, int *writable)
773 {
774 struct stat st;
775 int rc;
776 const char *filename = utab && *utab ? *utab : mnt_get_utab_path();
777
778 if (writable)
779 *writable = 0;
780 if (utab && !*utab)
781 *utab = filename;
782
783 DBG(UTILS, ul_debug("utab: %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, NULL);
792 return 1;
793 }
794 goto done; /* it's not a regular file */
795 }
796
797 if (writable) {
798 char *dirname = strdup(filename);
799
800 if (!dirname)
801 goto done;
802
803 stripoff_last_component(dirname); /* remove filename */
804
805 rc = mkdir(dirname, S_IWUSR|
806 S_IRUSR|S_IRGRP|S_IROTH|
807 S_IXUSR|S_IXGRP|S_IXOTH);
808 if (rc && errno != EEXIST) {
809 free(dirname);
810 goto done; /* probably EACCES */
811 }
812
813 *writable = !try_write(filename, dirname);
814 free(dirname);
815 if (*writable)
816 return 1;
817 }
818 done:
819 DBG(UTILS, ul_debug("%s: irregular/non-writable file", filename));
820 return 0;
821 }
822
823 /**
824 * mnt_get_swaps_path:
825 *
826 * Returns: path to /proc/swaps or $LIBMOUNT_SWAPS.
827 */
828 const char *mnt_get_swaps_path(void)
829 {
830 const char *p = safe_getenv("LIBMOUNT_SWAPS");
831 return p ? : _PATH_PROC_SWAPS;
832 }
833
834 /**
835 * mnt_get_fstab_path:
836 *
837 * Returns: path to /etc/fstab or $LIBMOUNT_FSTAB.
838 */
839 const char *mnt_get_fstab_path(void)
840 {
841 const char *p = safe_getenv("LIBMOUNT_FSTAB");
842 return p ? : _PATH_MNTTAB;
843 }
844
845 /**
846 * mnt_get_mtab_path:
847 *
848 * This function returns the *default* location of the mtab file. The result does
849 * not have to be writable. See also mnt_has_regular_mtab().
850 *
851 * Returns: path to /etc/mtab or $LIBMOUNT_MTAB.
852 */
853 const char *mnt_get_mtab_path(void)
854 {
855 const char *p = safe_getenv("LIBMOUNT_MTAB");
856 return p ? : _PATH_MOUNTED;
857 }
858
859 /*
860 * Don't export this to libmount API -- utab is private library stuff.
861 *
862 * Returns: path to /run/mount/utab or $LIBMOUNT_UTAB.
863 */
864 const char *mnt_get_utab_path(void)
865 {
866 const char *p = safe_getenv("LIBMOUNT_UTAB");
867 return p ? : MNT_PATH_UTAB;
868 }
869
870
871 /* returns file descriptor or -errno, @name returns a unique filename
872 */
873 int mnt_open_uniq_filename(const char *filename, char **name)
874 {
875 int rc, fd;
876 char *n;
877 mode_t oldmode;
878
879 if (!filename)
880 return -EINVAL;
881 if (name)
882 *name = NULL;
883
884 rc = asprintf(&n, "%s.XXXXXX", filename);
885 if (rc <= 0)
886 return -errno;
887
888 /* This is for very old glibc and for compatibility with Posix, which says
889 * nothing about mkstemp() mode. All sane glibc use secure mode (0600).
890 */
891 oldmode = umask(S_IRGRP|S_IWGRP|S_IXGRP|
892 S_IROTH|S_IWOTH|S_IXOTH);
893
894 fd = mkstemp_cloexec(n);
895 if (fd < 0)
896 fd = -errno;
897 umask(oldmode);
898
899 if (fd >= 0 && name)
900 *name = n;
901 else
902 free(n);
903
904 return fd;
905 }
906
907 /**
908 * mnt_get_mountpoint:
909 * @path: pathname
910 *
911 * This function finds the mountpoint that a given path resides in. @path
912 * should be canonicalized. The returned pointer should be freed by the caller.
913 *
914 * WARNING: the function compares st_dev of the @path elements. This traditional
915 * way may be insufficient on filesystems like Linux "overlay". See also
916 * mnt_table_find_target().
917 *
918 * Returns: allocated string with the target of the mounted device or NULL on error
919 */
920 char *mnt_get_mountpoint(const char *path)
921 {
922 char *mnt;
923 struct stat st;
924 dev_t dir, base;
925
926 if (!path)
927 return NULL;
928
929 mnt = strdup(path);
930 if (!mnt)
931 return NULL;
932 if (*mnt == '/' && *(mnt + 1) == '\0')
933 goto done;
934
935 if (mnt_stat_mountpoint(mnt, &st))
936 goto err;
937 base = st.st_dev;
938
939 do {
940 char *p = stripoff_last_component(mnt);
941
942 if (!p)
943 break;
944 if (mnt_stat_mountpoint(*mnt ? mnt : "/", &st))
945 goto err;
946 dir = st.st_dev;
947 if (dir != base) {
948 if (p > mnt)
949 *(p - 1) = '/';
950 goto done;
951 }
952 base = dir;
953 } while (mnt && *(mnt + 1) != '\0');
954
955 memcpy(mnt, "/", 2);
956 done:
957 DBG(UTILS, ul_debug("%s mountpoint is %s", path, mnt));
958 return mnt;
959 err:
960 free(mnt);
961 return NULL;
962 }
963
964 /*
965 * Search for @name kernel command parameter.
966 *
967 * Returns newly allocated string with a parameter argument if the @name is
968 * specified as "name=" or returns pointer to @name or returns NULL if not
969 * found. If it is specified more than once, we grab the last copy.
970 *
971 * For example cmdline: "aaa bbb=BBB ccc"
972 *
973 * @name is "aaa" --returns--> "aaa" (pointer to @name)
974 * @name is "bbb=" --returns--> "BBB" (allocated)
975 * @name is "foo" --returns--> NULL
976 *
977 * Note: It is not really feasible to parse the command line exactly the same
978 * as the kernel does since we don't know which options are valid. We can use
979 * the -- marker though and not walk past that.
980 */
981 char *mnt_get_kernel_cmdline_option(const char *name)
982 {
983 FILE *f;
984 size_t len;
985 int val = 0;
986 char *p, *res = NULL, *mem = NULL;
987 char buf[BUFSIZ]; /* see kernel include/asm-generic/setup.h: COMMAND_LINE_SIZE */
988 const char *path = _PATH_PROC_CMDLINE;
989
990 if (!name || !name[0])
991 return NULL;
992
993 #ifdef TEST_PROGRAM
994 path = getenv("LIBMOUNT_KERNEL_CMDLINE");
995 if (!path)
996 path = _PATH_PROC_CMDLINE;
997 #endif
998 f = fopen(path, "r" UL_CLOEXECSTR);
999 if (!f)
1000 return NULL;
1001
1002 p = fgets(buf, sizeof(buf), f);
1003 fclose(f);
1004
1005 if (!p || !*p || *p == '\n')
1006 return NULL;
1007
1008 p = strstr(p, " -- ");
1009 if (p) {
1010 /* no more kernel args after this */
1011 *p = '\0';
1012 } else {
1013 len = strlen(buf);
1014 buf[len - 1] = '\0'; /* remove last '\n' */
1015 }
1016
1017 len = strlen(name);
1018 if (name[len - 1] == '=')
1019 val = 1;
1020
1021 for (p = buf; p && *p; p++) {
1022 if (!(p = strstr(p, name)))
1023 break; /* not found the option */
1024 if (p != buf && !isblank(*(p - 1)))
1025 continue; /* no space before the option */
1026 if (!val && *(p + len) != '\0' && !isblank(*(p + len)))
1027 continue; /* no space after the option */
1028 if (val) {
1029 char *v = p + len;
1030 int end;
1031
1032 while (*p && !isblank(*p)) /* jump to the end of the argument */
1033 p++;
1034 end = (*p == '\0');
1035 *p = '\0';
1036 free(mem);
1037 res = mem = strdup(v);
1038 if (end)
1039 break;
1040 } else
1041 res = (char *) name; /* option without '=' */
1042 /* don't break -- keep scanning for more options */
1043 }
1044
1045 return res;
1046 }
1047
1048 /**
1049 * mnt_guess_system_root:
1050 * @devno: device number or zero
1051 * @cache: paths cache or NULL
1052 * @path: returns allocated path
1053 *
1054 * Converts @devno to the real device name if devno major number is greater
1055 * than zero, otherwise use root= kernel cmdline option to get device name.
1056 *
1057 * The function uses /sys to convert devno to device name.
1058 *
1059 * Returns: 0 = success, 1 = not found, <0 = error
1060 *
1061 * Since: 2.34
1062 */
1063 int mnt_guess_system_root(dev_t devno, struct libmnt_cache *cache, char **path)
1064 {
1065 char buf[PATH_MAX];
1066 char *dev = NULL, *spec = NULL;
1067 unsigned int x, y;
1068 int allocated = 0;
1069
1070 assert(path);
1071
1072 DBG(UTILS, ul_debug("guessing system root [devno %u:%u]", major(devno), minor(devno)));
1073
1074 /* The pseudo-fs, net-fs or btrfs devno is useless, otherwise it
1075 * usually matches with the source device, let's try to use it.
1076 */
1077 if (major(devno) > 0) {
1078 dev = sysfs_devno_to_devpath(devno, buf, sizeof(buf));
1079 if (dev) {
1080 DBG(UTILS, ul_debug(" devno converted to %s", dev));
1081 goto done;
1082 }
1083 }
1084
1085 /* Let's try to use root= kernel command line option
1086 */
1087 spec = mnt_get_kernel_cmdline_option("root=");
1088 if (!spec)
1089 goto done;
1090
1091 /* maj:min notation */
1092 if (sscanf(spec, "%u:%u", &x, &y) == 2) {
1093 dev = sysfs_devno_to_devpath(makedev(x, y), buf, sizeof(buf));
1094 if (dev) {
1095 DBG(UTILS, ul_debug(" root=%s converted to %s", spec, dev));
1096 goto done;
1097 }
1098
1099 /* hexhex notation */
1100 } else if (isxdigit_string(spec)) {
1101 char *end = NULL;
1102 uint32_t n;
1103
1104 errno = 0;
1105 n = strtoul(spec, &end, 16);
1106
1107 if (errno || spec == end || (end && *end))
1108 DBG(UTILS, ul_debug(" failed to parse root='%s'", spec));
1109 else {
1110 /* kernel new_decode_dev() */
1111 x = (n & 0xfff00) >> 8;
1112 y = (n & 0xff) | ((n >> 12) & 0xfff00);
1113 dev = sysfs_devno_to_devpath(makedev(x, y), buf, sizeof(buf));
1114 if (dev) {
1115 DBG(UTILS, ul_debug(" root=%s converted to %s", spec, dev));
1116 goto done;
1117 }
1118 }
1119
1120 /* devname or PARTUUID= etc. */
1121 } else {
1122 DBG(UTILS, ul_debug(" converting root='%s'", spec));
1123
1124 dev = mnt_resolve_spec(spec, cache);
1125 if (dev && !cache)
1126 allocated = 1;
1127 }
1128 done:
1129 free(spec);
1130 if (dev) {
1131 *path = allocated ? dev : strdup(dev);
1132 if (!*path)
1133 return -ENOMEM;
1134 return 0;
1135 }
1136
1137 return 1;
1138 }
1139
1140 /*
1141 * Initialize MNT_PATH_TMPTGT; mkdir, create a new namespace and
1142 * mark (bind mount) the directory as private.
1143 */
1144 int mnt_tmptgt_unshare(int *old_ns_fd)
1145 {
1146 #ifdef USE_LIBMOUNT_SUPPORT_NAMESPACES
1147 int rc = 0, fd = -1;
1148
1149 assert(old_ns_fd);
1150
1151 *old_ns_fd = -1;
1152
1153 /* remember the current namespace */
1154 fd = open("/proc/self/ns/mnt", O_RDONLY | O_CLOEXEC);
1155 if (fd < 0)
1156 goto fail;
1157
1158 /* create new namespace */
1159 if (unshare(CLONE_NEWNS) != 0)
1160 goto fail;
1161
1162 /* create directory */
1163 rc = ul_mkdir_p(MNT_PATH_TMPTGT, S_IRWXU);
1164 if (rc)
1165 goto fail;
1166
1167 /* try to set top-level directory as private, this is possible if
1168 * MNT_RUNTIME_TOPDIR (/run) is a separated filesystem. */
1169 if (mount("none", MNT_RUNTIME_TOPDIR, NULL, MS_PRIVATE, NULL) != 0) {
1170
1171 /* failed; create a mountpoint from MNT_PATH_TMPTGT */
1172 if (mount(MNT_PATH_TMPTGT, MNT_PATH_TMPTGT, "none", MS_BIND, NULL) != 0)
1173 goto fail;
1174 if (mount("none", MNT_PATH_TMPTGT, NULL, MS_PRIVATE, NULL) != 0)
1175 goto fail;
1176 }
1177
1178 DBG(UTILS, ul_debug(MNT_PATH_TMPTGT " unshared"));
1179 *old_ns_fd = fd;
1180 return 0;
1181 fail:
1182 if (rc == 0)
1183 rc = errno ? -errno : -EINVAL;
1184
1185 mnt_tmptgt_cleanup(fd);
1186 DBG(UTILS, ul_debug(MNT_PATH_TMPTGT " unshare failed"));
1187 return rc;
1188 #else
1189 return -ENOSYS;
1190 #endif
1191 }
1192
1193 /*
1194 * Clean up MNT_PATH_TMPTGT; umount and switch back to old namespace
1195 */
1196 int mnt_tmptgt_cleanup(int old_ns_fd)
1197 {
1198 #ifdef USE_LIBMOUNT_SUPPORT_NAMESPACES
1199 umount(MNT_PATH_TMPTGT);
1200
1201 if (old_ns_fd >= 0) {
1202 setns(old_ns_fd, CLONE_NEWNS);
1203 close(old_ns_fd);
1204 }
1205
1206 DBG(UTILS, ul_debug(MNT_PATH_TMPTGT " cleanup done"));
1207 return 0;
1208 #else
1209 return -ENOSYS;
1210 #endif
1211 }
1212
1213 #ifdef TEST_PROGRAM
1214 static int test_match_fstype(struct libmnt_test *ts, int argc, char *argv[])
1215 {
1216 char *type = argv[1];
1217 char *pattern = argv[2];
1218
1219 printf("%s\n", mnt_match_fstype(type, pattern) ? "MATCH" : "NOT-MATCH");
1220 return 0;
1221 }
1222
1223 static int test_match_options(struct libmnt_test *ts, int argc, char *argv[])
1224 {
1225 char *optstr = argv[1];
1226 char *pattern = argv[2];
1227
1228 printf("%s\n", mnt_match_options(optstr, pattern) ? "MATCH" : "NOT-MATCH");
1229 return 0;
1230 }
1231
1232 static int test_startswith(struct libmnt_test *ts, int argc, char *argv[])
1233 {
1234 char *optstr = argv[1];
1235 char *pattern = argv[2];
1236
1237 printf("%s\n", startswith(optstr, pattern) ? "YES" : "NOT");
1238 return 0;
1239 }
1240
1241 static int test_endswith(struct libmnt_test *ts, int argc, char *argv[])
1242 {
1243 char *optstr = argv[1];
1244 char *pattern = argv[2];
1245
1246 printf("%s\n", endswith(optstr, pattern) ? "YES" : "NOT");
1247 return 0;
1248 }
1249
1250 static int test_mountpoint(struct libmnt_test *ts, int argc, char *argv[])
1251 {
1252 char *path = canonicalize_path(argv[1]),
1253 *mnt = path ? mnt_get_mountpoint(path) : NULL;
1254
1255 printf("%s: %s\n", argv[1], mnt ? : "unknown");
1256 free(mnt);
1257 free(path);
1258 return 0;
1259 }
1260
1261 static int test_filesystems(struct libmnt_test *ts, int argc, char *argv[])
1262 {
1263 char **filesystems = NULL;
1264 int rc;
1265
1266 rc = mnt_get_filesystems(&filesystems, argc ? argv[1] : NULL);
1267 if (!rc) {
1268 char **p;
1269 for (p = filesystems; *p; p++)
1270 printf("%s\n", *p);
1271 mnt_free_filesystems(filesystems);
1272 }
1273 return rc;
1274 }
1275
1276 static int test_chdir(struct libmnt_test *ts, int argc, char *argv[])
1277 {
1278 int rc;
1279 char *path = canonicalize_path(argv[1]),
1280 *last = NULL;
1281
1282 if (!path)
1283 return -errno;
1284
1285 rc = mnt_chdir_to_parent(path, &last);
1286 if (!rc) {
1287 printf("path='%s', abs='%s', last='%s'\n",
1288 argv[1], path, last);
1289 }
1290 free(path);
1291 free(last);
1292 return rc;
1293 }
1294
1295 static int test_kernel_cmdline(struct libmnt_test *ts, int argc, char *argv[])
1296 {
1297 char *name = argv[1];
1298 char *res;
1299
1300 res = mnt_get_kernel_cmdline_option(name);
1301 if (!res)
1302 printf("'%s' not found\n", name);
1303 else if (res == name)
1304 printf("'%s' found\n", name);
1305 else {
1306 printf("'%s' found, argument: '%s'\n", name, res);
1307 free(res);
1308 }
1309
1310 return 0;
1311 }
1312
1313
1314 static int test_guess_root(struct libmnt_test *ts, int argc, char *argv[])
1315 {
1316 int rc;
1317 char *real;
1318 dev_t devno = 0;
1319
1320 if (argc) {
1321 unsigned int x, y;
1322
1323 if (sscanf(argv[1], "%u:%u", &x, &y) != 2)
1324 return -EINVAL;
1325 devno = makedev(x, y);
1326 }
1327
1328 rc = mnt_guess_system_root(devno, NULL, &real);
1329 if (rc < 0)
1330 return rc;
1331 if (rc == 1)
1332 fputs("not found\n", stdout);
1333 else {
1334 printf("%s\n", real);
1335 free(real);
1336 }
1337 return 0;
1338 }
1339
1340 static int test_mkdir(struct libmnt_test *ts, int argc, char *argv[])
1341 {
1342 int rc;
1343
1344 rc = ul_mkdir_p(argv[1], S_IRWXU |
1345 S_IRGRP | S_IXGRP |
1346 S_IROTH | S_IXOTH);
1347 if (rc)
1348 printf("mkdir %s failed\n", argv[1]);
1349 return rc;
1350 }
1351
1352 static int test_statfs_type(struct libmnt_test *ts, int argc, char *argv[])
1353 {
1354 struct statfs vfs;
1355 int rc;
1356
1357 rc = statfs(argv[1], &vfs);
1358 if (rc)
1359 printf("%s: statfs failed: %m\n", argv[1]);
1360 else
1361 printf("%-30s: statfs type: %-12s [0x%lx]\n", argv[1],
1362 mnt_statfs_get_fstype(&vfs),
1363 (long) vfs.f_type);
1364 return rc;
1365 }
1366
1367
1368 int main(int argc, char *argv[])
1369 {
1370 struct libmnt_test tss[] = {
1371 { "--match-fstype", test_match_fstype, "<type> <pattern> FS types matching" },
1372 { "--match-options", test_match_options, "<options> <pattern> options matching" },
1373 { "--filesystems", test_filesystems, "[<pattern>] list /{etc,proc}/filesystems" },
1374 { "--starts-with", test_startswith, "<string> <prefix>" },
1375 { "--ends-with", test_endswith, "<string> <prefix>" },
1376 { "--mountpoint", test_mountpoint, "<path>" },
1377 { "--cd-parent", test_chdir, "<path>" },
1378 { "--kernel-cmdline",test_kernel_cmdline, "<option> | <option>=" },
1379 { "--guess-root", test_guess_root, "[<maj:min>]" },
1380 { "--mkdir", test_mkdir, "<path>" },
1381 { "--statfs-type", test_statfs_type, "<path>" },
1382
1383 { NULL }
1384 };
1385
1386 return mnt_run_test(tss, argc, argv);
1387 }
1388
1389 #endif /* TEST_PROGRAM */