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