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