]> git.ipfire.org Git - thirdparty/util-linux.git/blame - libmount/src/utils.c
Merge branch 'pg-manual-page-long-options' of https://github.com/jaalto/util-linux
[thirdparty/util-linux.git] / libmount / src / utils.c
CommitLineData
69b7e41e
KZ
1/*
2 * Copyright (C) 2008-2009 Karel Zak <kzak@redhat.com>
3 *
4 * This file may be redistributed under the terms of the
5 * GNU Lesser General Public License.
6 */
7
192c6aad
KZ
8/**
9 * SECTION: utils
10 * @title: Utils
11 * @short_description: misc utils.
12 */
69b7e41e 13#include <ctype.h>
69b7e41e
KZ
14#include <fcntl.h>
15#include <pwd.h>
a1e8af75 16#include <grp.h>
a53cc4e0 17#include <blkid.h>
69b7e41e 18
b49103ed 19#include "strutils.h"
0532ba1d 20#include "pathnames.h"
69b7e41e 21#include "mountP.h"
3c5e4ef8 22#include "mangle.h"
0bb44be3 23#include "canonicalize.h"
035507c8 24#include "env.h"
b483debb 25#include "match.h"
df019e9b 26#include "statfs_magic.h"
69b7e41e 27
40b27864
KZ
28int append_string(char **a, const char *b)
29{
30 size_t al, bl;
31 char *tmp;
32
33 assert(a);
34
35 if (!b || !*b)
36 return 0;
37 if (!*a) {
38 *a = strdup(b);
39 return !*a ? -ENOMEM : 0;
40 }
41
42 al = strlen(*a);
c7616e4c 43 bl = strlen(b);
40b27864
KZ
44
45 tmp = realloc(*a, al + bl + 1);
46 if (!tmp)
47 return -ENOMEM;
48 *a = tmp;
49 memcpy((*a) + al, b, bl + 1);
50 return 0;
51}
52
dad88cb3 53/*
d58b3157 54 * Return 1 if the file is not accessible or empty
dad88cb3
KZ
55 */
56int is_file_empty(const char *name)
57{
58 struct stat st;
59 assert(name);
60
61 return (stat(name, &st) != 0 || st.st_size == 0);
62}
63
2c6b25f0
KZ
64int mnt_valid_tagname(const char *tagname)
65{
66 if (tagname && *tagname && (
67 strcmp("UUID", tagname) == 0 ||
68 strcmp("LABEL", tagname) == 0 ||
69 strcmp("PARTUUID", tagname) == 0 ||
70 strcmp("PARTLABEL", tagname) == 0))
71 return 1;
72
73 return 0;
74}
75
a53cc4e0
KZ
76/**
77 * mnt_tag_is_valid:
78 * @tag: NAME=value string
79 *
80 * Returns: 1 if the @tag is parsable and tag NAME= is supported by libmount, or 0.
81 */
82int mnt_tag_is_valid(const char *tag)
83{
84 char *t = NULL;
85 int rc = tag && blkid_parse_tag_string(tag, &t, NULL) == 0
86 && mnt_valid_tagname(t);
87
88 free(t);
89 return rc;
90}
91
fd1eb7a7
KZ
92int mnt_parse_offset(const char *str, size_t len, uintmax_t *res)
93{
94 char *p;
95 int rc = 0;
96
0208ae2e 97 if (!str || !*str)
fd1eb7a7
KZ
98 return -EINVAL;
99
100 p = strndup(str, len);
101 if (!p)
102 return -errno;
103
104 if (strtosize(p, res))
105 rc = -EINVAL;
106 free(p);
107 return rc;
108}
109
7383ebce
DR
110/* used as a callback by bsearch in mnt_fstype_is_pseudofs() */
111static int fstype_cmp(const void *v1, const void *v2)
112{
113 const char *s1 = *(const char **)v1;
114 const char *s2 = *(const char **)v2;
115
116 return strcmp(s1, s2);
117}
118
1d0cd73f
KZ
119/* returns basename and keeps dirname in the @path, if @path is "/" (root)
120 * then returns empty string */
dcc15ce5 121char *stripoff_last_component(char *path)
1d0cd73f
KZ
122{
123 char *p = path ? strrchr(path, '/') : NULL;
124
125 if (!p)
126 return NULL;
127 *p = '\0';
61073773 128 return p + 1;
1d0cd73f
KZ
129}
130
61073773 131/*
d58b3157
OO
132 * Note that the @target has to be an absolute path (so at least "/"). The
133 * @filename returns an allocated buffer with the last path component, for example:
61073773
KZ
134 *
135 * mnt_chdir_to_parent("/mnt/test", &buf) ==> chdir("/mnt"), buf="test"
66bb8267
KZ
136 */
137int mnt_chdir_to_parent(const char *target, char **filename)
138{
61073773 139 char *buf, *parent, *last = NULL;
66bb8267
KZ
140 char cwd[PATH_MAX];
141 int rc = -EINVAL;
142
143 if (!target || *target != '/')
144 return -EINVAL;
145
83a78332 146 DBG(UTILS, ul_debug("moving to %s parent", target));
61073773
KZ
147
148 buf = strdup(target);
149 if (!buf)
66bb8267
KZ
150 return -ENOMEM;
151
61073773
KZ
152 if (*(buf + 1) != '\0') {
153 last = stripoff_last_component(buf);
66bb8267
KZ
154 if (!last)
155 goto err;
156 }
66bb8267 157
61073773
KZ
158 parent = buf && *buf ? buf : "/";
159
160 if (chdir(parent) == -1) {
83a78332 161 DBG(UTILS, ul_debug("failed to chdir to %s: %m", parent));
66bb8267
KZ
162 rc = -errno;
163 goto err;
164 }
165 if (!getcwd(cwd, sizeof(cwd))) {
83a78332 166 DBG(UTILS, ul_debug("failed to obtain current directory: %m"));
66bb8267
KZ
167 rc = -errno;
168 goto err;
169 }
61073773 170 if (strcmp(cwd, parent) != 0) {
83a78332 171 DBG(UTILS, ul_debug(
61073773 172 "unexpected chdir (expected=%s, cwd=%s)", parent, cwd));
66bb8267
KZ
173 goto err;
174 }
175
83a78332 176 DBG(CXT, ul_debug(
61073773
KZ
177 "current directory moved to %s [last_component='%s']",
178 parent, last));
66bb8267 179
ba2bdf41
KZ
180 if (filename) {
181 *filename = buf;
66bb8267 182
ba2bdf41
KZ
183 if (!last || !*last)
184 memcpy(*filename, ".", 2);
185 else
4e9f59d1 186 memmove(*filename, last, strlen(last) + 1);
cfa44747
KZ
187 } else
188 free(buf);
66bb8267
KZ
189 return 0;
190err:
61073773 191 free(buf);
66bb8267
KZ
192 return rc;
193}
194
f9906424 195/*
d58b3157 196 * Check if @path is on a read-only filesystem independently of file permissions.
f9906424
KZ
197 */
198int mnt_is_readonly(const char *path)
199{
200 if (access(path, W_OK) == 0)
201 return 0;
202 if (errno == EROFS)
203 return 1;
204 if (errno != EACCES)
205 return 0;
206
207#ifdef HAVE_FUTIMENS
208 /*
209 * access(2) returns EACCES on read-only FS:
210 *
211 * - for set-uid application if one component of the path is not
212 * accessible for the current rUID. (Note that euidaccess(2) does not
213 * check for EROFS at all).
214 *
d58b3157 215 * - for a read-write filesystem with a read-only VFS node (aka -o remount,ro,bind)
f9906424
KZ
216 */
217 {
218 struct timespec times[2];
219
220 times[0].tv_nsec = UTIME_NOW; /* atime */
221 times[1].tv_nsec = UTIME_OMIT; /* mtime */
222
223 if (utimensat(AT_FDCWD, path, times, 0) == -1)
224 return errno == EROFS;
225 }
226#endif
227 return 0;
228}
229
3c5e4ef8
KZ
230/**
231 * mnt_mangle:
232 * @str: string
233 *
234 * Encode @str to be compatible with fstab/mtab
235 *
d58b3157 236 * Returns: newly allocated string or NULL in case of error.
3c5e4ef8
KZ
237 */
238char *mnt_mangle(const char *str)
239{
240 return mangle(str);
241}
242
243/**
244 * mnt_unmangle:
245 * @str: string
246 *
247 * Decode @str from fstab/mtab
248 *
d58b3157 249 * Returns: newly allocated string or NULL in case of error.
3c5e4ef8
KZ
250 */
251char *mnt_unmangle(const char *str)
252{
dd369652 253 return unmangle(str, NULL);
3c5e4ef8
KZ
254}
255
69b7e41e
KZ
256/**
257 * mnt_fstype_is_pseudofs:
258 * @type: filesystem name
259 *
260 * Returns: 1 for filesystems like proc, sysfs, ... or 0.
261 */
262int mnt_fstype_is_pseudofs(const char *type)
263{
7383ebce
DR
264 /* This array must remain sorted when adding new fstypes */
265 static const char *pseudofs[] = {
266 "anon_inodefs",
267 "autofs",
268 "bdev",
269 "binfmt_misc",
270 "cgroup",
271 "configfs",
272 "cpuset",
273 "debugfs",
274 "devfs",
275 "devpts",
276 "devtmpfs",
277 "dlmfs",
84ceaf7a 278 "efivarfs",
7383ebce 279 "fusectl",
84ceaf7a 280 "fuse.gvfs-fuse-daemon",
7383ebce
DR
281 "hugetlbfs",
282 "mqueue",
283 "nfsd",
284 "none",
285 "pipefs",
286 "proc",
287 "pstore",
288 "ramfs",
289 "rootfs",
290 "rpc_pipefs",
291 "securityfs",
292 "sockfs",
293 "spufs",
294 "sysfs",
295 "tmpfs"
296 };
297
4569bbea
KZ
298 assert(type);
299
7383ebce
DR
300 return !(bsearch(&type, pseudofs, ARRAY_SIZE(pseudofs),
301 sizeof(char*), fstype_cmp) == NULL);
69b7e41e
KZ
302}
303
304/**
305 * mnt_fstype_is_netfs:
306 * @type: filesystem name
307 *
308 * Returns: 1 for filesystems like cifs, nfs, ... or 0.
309 */
310int mnt_fstype_is_netfs(const char *type)
311{
4569bbea
KZ
312 assert(type);
313
c59cf20c 314 if (strcmp(type, "cifs") == 0 ||
69b7e41e 315 strcmp(type, "smbfs") == 0 ||
c59cf20c
KZ
316 strncmp(type,"nfs", 3) == 0 ||
317 strcmp(type, "afs") == 0 ||
318 strcmp(type, "ncpfs") == 0 ||
319 strncmp(type,"9p", 2) == 0)
69b7e41e
KZ
320 return 1;
321 return 0;
322}
323
6a52473e
KZ
324const char *mnt_statfs_get_fstype(struct statfs *vfs)
325{
326 assert(vfs);
327
328 switch (vfs->f_type) {
df019e9b
KZ
329 case STATFS_ADFS_MAGIC: return "adfs";
330 case STATFS_AFFS_MAGIC: return "affs";
331 case STATFS_AFS_MAGIC: return "afs";
332 case STATFS_AUTOFS_MAGIC: return "autofs";
333 case STATFS_BDEVFS_MAGIC: return "bdev";
334 case STATFS_BEFS_MAGIC: return "befs";
335 case STATFS_BFS_MAGIC: return "befs";
336 case STATFS_BINFMTFS_MAGIC: return "binfmt_misc";
337 case STATFS_BTRFS_MAGIC: return "btrfs";
338 case STATFS_CEPH_MAGIC: return "ceph";
339 case STATFS_CGROUP_MAGIC: return "cgroup";
340 case STATFS_CIFS_MAGIC: return "cifs";
341 case STATFS_CODA_MAGIC: return "coda";
342 case STATFS_CONFIGFS_MAGIC: return "configfs";
343 case STATFS_CRAMFS_MAGIC: return "cramfs";
344 case STATFS_DEBUGFS_MAGIC: return "debugfs";
345 case STATFS_DEVPTS_MAGIC: return "devpts";
346 case STATFS_ECRYPTFS_MAGIC: return "ecryptfs";
347 case STATFS_EFIVARFS_MAGIC: return "efivarfs";
348 case STATFS_EFS_MAGIC: return "efs";
349 case STATFS_EXOFS_MAGIC: return "exofs";
350 case STATFS_EXT4_MAGIC: return "ext4"; /* all extN use the same magic */
351 case STATFS_F2FS_MAGIC: return "f2fs";
352 case STATFS_FUSE_MAGIC: return "fuse";
353 case STATFS_FUTEXFS_MAGIC: return "futexfs";
354 case STATFS_GFS2_MAGIC: return "gfs2";
355 case STATFS_HFSPLUS_MAGIC: return "hfsplus";
356 case STATFS_HOSTFS_MAGIC: return "hostfs";
357 case STATFS_HPFS_MAGIC: return "hpfs";
358 case STATFS_HPPFS_MAGIC: return "hppfs";
359 case STATFS_HUGETLBFS_MAGIC: return "hugetlbfs";
360 case STATFS_ISOFS_MAGIC: return "iso9660";
361 case STATFS_JFFS2_MAGIC: return "jffs2";
362 case STATFS_JFS_MAGIC: return "jfs";
363 case STATFS_LOGFS_MAGIC: return "logfs";
364 case STATFS_MINIX2_MAGIC:
365 case STATFS_MINIX2_MAGIC2:
366 case STATFS_MINIX3_MAGIC:
367 case STATFS_MINIX_MAGIC:
368 case STATFS_MINIX_MAGIC2: return "minix";
369 case STATFS_MQUEUE_MAGIC: return "mqueue";
370 case STATFS_MSDOS_MAGIC: return "vfat";
371 case STATFS_NCP_MAGIC: return "ncp";
372 case STATFS_NFS_MAGIC: return "nfs";
373 case STATFS_NILFS_MAGIC: return "nilfs2";
374 case STATFS_NTFS_MAGIC: return "ntfs";
375 case STATFS_OCFS2_MAGIC: return "ocfs2";
376 case STATFS_OMFS_MAGIC: return "omfs";
377 case STATFS_OPENPROMFS_MAGIC: return "openpromfs";
378 case STATFS_PIPEFS_MAGIC: return "pipefs";
379 case STATFS_PROC_MAGIC: return "proc";
380 case STATFS_PSTOREFS_MAGIC: return "pstore";
381 case STATFS_QNX4_MAGIC: return "qnx4";
382 case STATFS_QNX6_MAGIC: return "qnx6";
383 case STATFS_RAMFS_MAGIC: return "ramfs";
384 case STATFS_REISERFS_MAGIC: return "reiser4";
385 case STATFS_ROMFS_MAGIC: return "romfs";
386 case STATFS_SECURITYFS_MAGIC: return "securityfs";
387 case STATFS_SELINUXFS_MAGIC: return "selinuxfs";
388 case STATFS_SMACKFS_MAGIC: return "smackfs";
389 case STATFS_SMB_MAGIC: return "smb";
390 case STATFS_SOCKFS_MAGIC: return "sockfs";
391 case STATFS_SQUASHFS_MAGIC: return "squashfs";
392 case STATFS_SYSFS_MAGIC: return "sysfs";
393 case STATFS_TMPFS_MAGIC: return "tmpfs";
394 case STATFS_UBIFS_MAGIC: return "ubifs";
395 case STATFS_UDF_MAGIC: return "udf";
396 case STATFS_UFS2_MAGIC:
397 case STATFS_UFS_MAGIC: return "ufs";
398 case STATFS_V9FS_MAGIC: return "9p";
399 case STATFS_VXFS_MAGIC: return "vxfs";
400 case STATFS_XENFS_MAGIC: return "xenfs";
401 case STATFS_XFS_MAGIC: return "xfs";
6a52473e
KZ
402 default:
403 break;
404 }
405
406 return NULL;
407}
408
409
abc9c0f7
KZ
410/**
411 * mnt_match_fstype:
412 * @type: filesystem type
6ad929bb 413 * @pattern: filesystem name or comma delimited list of names
abc9c0f7 414 *
d58b3157 415 * The @pattern list of filesystems can be prefixed with a global
abc9c0f7 416 * "no" prefix to invert matching of the whole list. The "no" could
6ad929bb 417 * also be used for individual items in the @pattern list. So,
3d735589 418 * "nofoo,bar" has the same meaning as "nofoo,nobar".
abc9c0f7 419 *
3d735589
KZ
420 * "bar" : "nofoo,bar" -> False (global "no" prefix)
421 *
422 * "bar" : "foo,bar" -> True
abc9c0f7 423 *
abc9c0f7
KZ
424 * "bar" : "foo,nobar" -> False
425 *
426 * Returns: 1 if type is matching, else 0. This function also returns
427 * 0 if @pattern is NULL and @type is non-NULL.
428 */
429int mnt_match_fstype(const char *type, const char *pattern)
430{
12089155 431 return match_fstype(type, pattern);
abc9c0f7
KZ
432}
433
434
435/* Returns 1 if needle found or noneedle not found in haystack
436 * Otherwise returns 0
437 */
438static int check_option(const char *haystack, size_t len,
439 const char *needle, size_t needle_len)
440{
441 const char *p;
442 int no = 0;
443
ede8a60e
KZ
444 if (needle_len >= 1 && *needle == '+') {
445 needle++;
446 needle_len--;
447 } else if (needle_len >= 2 && !strncmp(needle, "no", 2)) {
abc9c0f7
KZ
448 no = 1;
449 needle += 2;
450 needle_len -= 2;
451 }
452
453 for (p = haystack; p && p < haystack + len; p++) {
454 char *sep = strchr(p, ',');
7fc6d2b8
KZ
455 size_t plen = sep ? (size_t) (sep - p) :
456 len - (p - haystack);
abc9c0f7
KZ
457
458 if (plen == needle_len) {
459 if (!strncmp(p, needle, plen))
460 return !no; /* foo or nofoo was found */
461 }
462 p += plen;
463 }
464
465 return no; /* foo or nofoo was not found */
466}
467
468/**
469 * mnt_match_options:
470 * @optstr: options string
6ad929bb 471 * @pattern: comma delimited list of options
abc9c0f7 472 *
d58b3157 473 * The "no" could be used for individual items in the @options list. The "no"
6ad929bb 474 * prefix does not have a global meaning.
abc9c0f7
KZ
475 *
476 * Unlike fs type matching, nonetdev,user and nonetdev,nouser have
477 * DIFFERENT meanings; each option is matched explicitly as specified.
478 *
d58b3157
OO
479 * The "no" prefix interpretation could be disabled by the "+" prefix, for example
480 * "+noauto" matches if @optstr literally contains the "noauto" string.
ede8a60e 481 *
3d735589
KZ
482 * "xxx,yyy,zzz" : "nozzz" -> False
483 *
484 * "xxx,yyy,zzz" : "xxx,noeee" -> True
abc9c0f7 485 *
ede8a60e
KZ
486 * "bar,zzz" : "nofoo" -> True
487 *
488 * "nofoo,bar" : "+nofoo" -> True
489 *
490 * "bar,zzz" : "+nofoo" -> False
491 *
492 *
abc9c0f7
KZ
493 * Returns: 1 if pattern is matching, else 0. This function also returns 0
494 * if @pattern is NULL and @optstr is non-NULL.
495 */
496int mnt_match_options(const char *optstr, const char *pattern)
497{
498 const char *p;
499 size_t len, optstr_len = 0;
500
501 if (!pattern && !optstr)
502 return 1;
503 if (!pattern)
504 return 0;
505
506 len = strlen(pattern);
507 if (optstr)
508 optstr_len = strlen(optstr);
509
510 for (p = pattern; p < pattern + len; p++) {
511 char *sep = strchr(p, ',');
7fc6d2b8
KZ
512 size_t plen = sep ? (size_t) (sep - p) :
513 len - (p - pattern);
abc9c0f7
KZ
514
515 if (!plen)
516 continue; /* if two ',' appear in a row */
517
518 if (!check_option(optstr, optstr_len, p, plen))
519 return 0; /* any match failure means failure */
520
521 p += plen;
522 }
523
524 /* no match failures in list means success */
525 return 1;
526}
527
97e23b5e
KZ
528void mnt_free_filesystems(char **filesystems)
529{
530 char **p;
531
532 if (!filesystems)
533 return;
534 for (p = filesystems; *p; p++)
535 free(*p);
536 free(filesystems);
537}
538
539static int add_filesystem(char ***filesystems, char *name)
540{
541 int n = 0;
542
543 assert(filesystems);
544 assert(name);
545
546 if (*filesystems) {
547 char **p;
548 for (n = 0, p = *filesystems; *p; p++, n++) {
549 if (strcmp(*p, name) == 0)
550 return 0;
551 }
552 }
553
554 #define MYCHUNK 16
555
556 if (n == 0 || !((n + 1) % MYCHUNK)) {
557 size_t items = ((n + 1 + MYCHUNK) / MYCHUNK) * MYCHUNK;
558 char **x = realloc(*filesystems, items * sizeof(char *));
559
560 if (!x)
561 goto err;
562 *filesystems = x;
563 }
564 name = strdup(name);
565 if (!name)
566 goto err;
567 (*filesystems)[n] = name;
568 (*filesystems)[n + 1] = NULL;
569 return 0;
570err:
571 mnt_free_filesystems(*filesystems);
572 return -ENOMEM;
573}
574
575static int get_filesystems(const char *filename, char ***filesystems, const char *pattern)
576{
4f69189f 577 int rc = 0;
97e23b5e
KZ
578 FILE *f;
579 char line[128];
580
1eb8539d 581 f = fopen(filename, "r" UL_CLOEXECSTR);
97e23b5e 582 if (!f)
ec8121b1
KZ
583 return 1;
584
83a78332 585 DBG(UTILS, ul_debug("reading filesystems list from: %s", filename));
97e23b5e
KZ
586
587 while (fgets(line, sizeof(line), f)) {
588 char name[sizeof(line)];
97e23b5e
KZ
589
590 if (*line == '#' || strncmp(line, "nodev", 5) == 0)
591 continue;
592 if (sscanf(line, " %128[^\n ]\n", name) != 1)
593 continue;
ec8121b1
KZ
594 if (strcmp(name, "*") == 0) {
595 rc = 1;
596 break; /* end of the /etc/filesystems */
597 }
97e23b5e
KZ
598 if (pattern && !mnt_match_fstype(name, pattern))
599 continue;
600 rc = add_filesystem(filesystems, name);
601 if (rc)
4f69189f 602 break;
97e23b5e 603 }
4f69189f
KZ
604
605 fclose(f);
606 return rc;
97e23b5e
KZ
607}
608
3de77c21 609/*
d58b3157 610 * Always check the @filesystems pointer!
ec8121b1
KZ
611 *
612 * man mount:
613 *
614 * ...mount will try to read the file /etc/filesystems, or, if that does not
615 * exist, /proc/filesystems. All of the filesystem types listed there will
616 * be tried, except for those that are labeled "nodev" (e.g., devpts,
617 * proc and nfs). If /etc/filesystems ends in a line with a single * only,
123ddced 618 * mount will read /proc/filesystems afterwards.
3de77c21 619 */
97e23b5e
KZ
620int mnt_get_filesystems(char ***filesystems, const char *pattern)
621{
622 int rc;
623
624 if (!filesystems)
625 return -EINVAL;
ec8121b1 626
97e23b5e
KZ
627 *filesystems = NULL;
628
629 rc = get_filesystems(_PATH_FILESYSTEMS, filesystems, pattern);
ec8121b1 630 if (rc != 1)
97e23b5e 631 return rc;
ec8121b1
KZ
632
633 rc = get_filesystems(_PATH_PROC_FILESYSTEMS, filesystems, pattern);
634 if (rc == 1 && *filesystems)
d58b3157 635 rc = 0; /* /proc/filesystems not found */
ec8121b1
KZ
636
637 return rc;
97e23b5e
KZ
638}
639
2af0a839
KZ
640static size_t get_pw_record_size(void)
641{
642#ifdef _SC_GETPW_R_SIZE_MAX
643 long sz = sysconf(_SC_GETPW_R_SIZE_MAX);
644 if (sz > 0)
645 return sz;
646#endif
647 return 16384;
648}
649
69b7e41e 650/*
d58b3157 651 * Returns an allocated string with username or NULL.
69b7e41e
KZ
652 */
653char *mnt_get_username(const uid_t uid)
654{
655 struct passwd pwd;
656 struct passwd *res;
2af0a839 657 size_t sz = get_pw_record_size();
69b7e41e
KZ
658 char *buf, *username = NULL;
659
69b7e41e
KZ
660 buf = malloc(sz);
661 if (!buf)
662 return NULL;
663
664 if (!getpwuid_r(uid, &pwd, buf, sz, &res) && res)
665 username = strdup(pwd.pw_name);
666
667 free(buf);
668 return username;
669}
abc9c0f7 670
a1e8af75
KZ
671int mnt_get_uid(const char *username, uid_t *uid)
672{
188dc15a 673 int rc = -1;
a1e8af75
KZ
674 struct passwd pwd;
675 struct passwd *pw;
2af0a839 676 size_t sz = get_pw_record_size();
a1e8af75
KZ
677 char *buf;
678
188dc15a
KZ
679 if (!username || !uid)
680 return -EINVAL;
b47b7b3a 681
a1e8af75
KZ
682 buf = malloc(sz);
683 if (!buf)
684 return -ENOMEM;
685
188dc15a 686 if (!getpwnam_r(username, &pwd, buf, sz, &pw) && pw) {
a1e8af75 687 *uid= pw->pw_uid;
188dc15a
KZ
688 rc = 0;
689 } else {
83a78332 690 DBG(UTILS, ul_debug(
188dc15a 691 "cannot convert '%s' username to UID", username));
61f5ff6c 692 rc = errno ? -errno : -EINVAL;
188dc15a 693 }
a1e8af75
KZ
694
695 free(buf);
188dc15a 696 return rc;
a1e8af75
KZ
697}
698
699int mnt_get_gid(const char *groupname, gid_t *gid)
700{
188dc15a 701 int rc = -1;
a1e8af75
KZ
702 struct group grp;
703 struct group *gr;
2af0a839 704 size_t sz = get_pw_record_size();
a1e8af75
KZ
705 char *buf;
706
188dc15a
KZ
707 if (!groupname || !gid)
708 return -EINVAL;
b47b7b3a 709
a1e8af75
KZ
710 buf = malloc(sz);
711 if (!buf)
712 return -ENOMEM;
713
188dc15a 714 if (!getgrnam_r(groupname, &grp, buf, sz, &gr) && gr) {
a1e8af75 715 *gid= gr->gr_gid;
188dc15a
KZ
716 rc = 0;
717 } else {
83a78332 718 DBG(UTILS, ul_debug(
188dc15a 719 "cannot convert '%s' groupname to GID", groupname));
61f5ff6c 720 rc = errno ? -errno : -EINVAL;
188dc15a 721 }
a1e8af75
KZ
722
723 free(buf);
188dc15a
KZ
724 return rc;
725}
726
727int mnt_in_group(gid_t gid)
728{
729 int rc = 0, n, i;
730 gid_t *grps = NULL;
731
732 if (getgid() == gid)
733 return 1;
734
735 n = getgroups(0, NULL);
736 if (n <= 0)
737 goto done;
738
739 grps = malloc(n * sizeof(*grps));
740 if (!grps)
741 goto done;
742
743 if (getgroups(n, grps) == n) {
744 for (i = 0; i < n; i++) {
745 if (grps[i] == gid) {
746 rc = 1;
747 break;
748 }
749 }
750 }
751done:
752 free(grps);
753 return rc;
a1e8af75
KZ
754}
755
1d0cd73f
KZ
756static int try_write(const char *filename)
757{
758 int fd;
759
760 if (!filename)
761 return -EINVAL;
762
d39c2b43
KZ
763 fd = open(filename, O_RDWR|O_CREAT|O_CLOEXEC,
764 S_IWUSR|S_IRUSR|S_IRGRP|S_IROTH);
1d0cd73f
KZ
765 if (fd >= 0) {
766 close(fd);
767 return 0;
768 }
769 return -errno;
770}
771
772/**
773 * mnt_has_regular_mtab:
774 * @mtab: returns path to mtab
775 * @writable: returns 1 if the file is writable
776 *
d58b3157
OO
777 * If the file does not exist and @writable argument is not NULL, then it will
778 * try to create the file.
1d0cd73f 779 *
6ad929bb 780 * Returns: 1 if /etc/mtab is a regular file, and 0 in case of error (check
1d0cd73f 781 * errno for more details).
0532ba1d 782 */
1d0cd73f 783int mnt_has_regular_mtab(const char **mtab, int *writable)
0532ba1d
KZ
784{
785 struct stat st;
70bf97ae 786 int rc;
1d0cd73f 787 const char *filename = mtab && *mtab ? *mtab : mnt_get_mtab_path();
0532ba1d 788
7c118af7
KZ
789 if (writable)
790 *writable = 0;
70bf97ae 791 if (mtab && !*mtab)
1d0cd73f
KZ
792 *mtab = filename;
793
83a78332 794 DBG(UTILS, ul_debug("mtab: %s", filename));
70bf97ae 795
1d0cd73f
KZ
796 rc = lstat(filename, &st);
797
798 if (rc == 0) {
d58b3157 799 /* file exists */
1d0cd73f
KZ
800 if (S_ISREG(st.st_mode)) {
801 if (writable)
802 *writable = !try_write(filename);
803 return 1;
804 }
7c118af7 805 goto done;
1d0cd73f
KZ
806 }
807
808 /* try to create the file */
70bf97ae 809 if (writable) {
1d0cd73f 810 *writable = !try_write(filename);
7c118af7
KZ
811 if (*writable)
812 return 1;
1d0cd73f
KZ
813 }
814
7c118af7 815done:
83a78332 816 DBG(UTILS, ul_debug("%s: irregular/non-writable", filename));
1d0cd73f
KZ
817 return 0;
818}
7c118af7 819
77417bc0
KZ
820/*
821 * Don't export this to libmount API -- utab is private library stuff.
1d0cd73f 822 *
d58b3157 823 * If the file does not exist and @writable argument is not NULL, then it will
a362ae60 824 * try to create the directory (e.g. /run/mount) and the file.
1d0cd73f 825 *
a362ae60 826 * Returns: 1 if utab is a regular file, and 0 in case of
6f5788c5 827 * error (check errno for more details).
1d0cd73f 828 */
1d0cd73f
KZ
829int mnt_has_regular_utab(const char **utab, int *writable)
830{
831 struct stat st;
832 int rc;
833 const char *filename = utab && *utab ? *utab : mnt_get_utab_path();
834
7c118af7
KZ
835 if (writable)
836 *writable = 0;
1d0cd73f
KZ
837 if (utab && !*utab)
838 *utab = filename;
839
83a78332 840 DBG(UTILS, ul_debug("utab: %s", filename));
1d0cd73f
KZ
841
842 rc = lstat(filename, &st);
843
844 if (rc == 0) {
d58b3157 845 /* file exists */
1d0cd73f
KZ
846 if (S_ISREG(st.st_mode)) {
847 if (writable)
7c118af7 848 *writable = !try_write(filename);
1d0cd73f 849 return 1;
70bf97ae 850 }
d58b3157 851 goto done; /* it's not a regular file */
70bf97ae 852 }
1d0cd73f
KZ
853
854 if (writable) {
855 char *dirname = strdup(filename);
856
857 if (!dirname)
7c118af7 858 goto done;
1d0cd73f
KZ
859
860 stripoff_last_component(dirname); /* remove filename */
861
b0bb8fb6
KZ
862 rc = mkdir(dirname, S_IWUSR|
863 S_IRUSR|S_IRGRP|S_IROTH|
864 S_IXUSR|S_IXGRP|S_IXOTH);
1d0cd73f
KZ
865 free(dirname);
866 if (rc && errno != EEXIST)
7c118af7 867 goto done; /* probably EACCES */
1d0cd73f
KZ
868
869 *writable = !try_write(filename);
7c118af7
KZ
870 if (*writable)
871 return 1;
1d0cd73f 872 }
7c118af7 873done:
83a78332 874 DBG(UTILS, ul_debug("%s: irregular/non-writable file", filename));
1d0cd73f 875 return 0;
0532ba1d
KZ
876}
877
ce4dd666
KZ
878/**
879 * mnt_get_swaps_path:
880 *
881 * Returns: path to /proc/swaps or $LIBMOUNT_SWAPS.
882 */
883const char *mnt_get_swaps_path(void)
884{
885 const char *p = safe_getenv("LIBMOUNT_SWAPS");
886 return p ? : _PATH_PROC_SWAPS;
887}
888
3a5b1b1d
KZ
889/**
890 * mnt_get_fstab_path:
891 *
892 * Returns: path to /etc/fstab or $LIBMOUNT_FSTAB.
893 */
894const char *mnt_get_fstab_path(void)
895{
035507c8 896 const char *p = safe_getenv("LIBMOUNT_FSTAB");
3a5b1b1d
KZ
897 return p ? : _PATH_MNTTAB;
898}
899
900/**
901 * mnt_get_mtab_path:
902 *
d58b3157 903 * This function returns the *default* location of the mtab file. The result does
0f32f1e2 904 * not have to be writable. See also mnt_has_regular_mtab().
3a5b1b1d
KZ
905 *
906 * Returns: path to /etc/mtab or $LIBMOUNT_MTAB.
907 */
908const char *mnt_get_mtab_path(void)
909{
035507c8 910 const char *p = safe_getenv("LIBMOUNT_MTAB");
3a5b1b1d
KZ
911 return p ? : _PATH_MOUNTED;
912}
913
77417bc0
KZ
914/*
915 * Don't export this to libmount API -- utab is private library stuff.
be1a5180 916 *
a362ae60 917 * Returns: path to /run/mount/utab (or /dev/.mount/utab) or $LIBMOUNT_UTAB.
b37dd175
KZ
918 */
919const char *mnt_get_utab_path(void)
920{
a362ae60 921 struct stat st;
035507c8 922 const char *p = safe_getenv("LIBMOUNT_UTAB");
a362ae60
KZ
923
924 if (p)
925 return p;
926
927 if (stat(MNT_RUNTIME_TOPDIR, &st) == 0)
928 return MNT_PATH_UTAB;
929
930 return MNT_PATH_UTAB_OLD;
b37dd175
KZ
931}
932
d1be0c34 933
d58b3157 934/* returns file descriptor or -errno, @name returns a unique filename
b37dd175 935 */
4b6cf485 936int mnt_open_uniq_filename(const char *filename, char **name)
b37dd175
KZ
937{
938 int rc, fd;
939 char *n;
6fc81224 940 mode_t oldmode;
b37dd175 941
ba2bdf41
KZ
942 if (!filename)
943 return -EINVAL;
b37dd175
KZ
944 if (name)
945 *name = NULL;
946
947 rc = asprintf(&n, "%s.XXXXXX", filename);
948 if (rc <= 0)
949 return -errno;
950
d58b3157 951 /* This is for very old glibc and for compatibility with Posix, which says
6fc81224
KZ
952 * nothing about mkstemp() mode. All sane glibc use secure mode (0600).
953 */
51479069
KZ
954 oldmode = umask(S_IRGRP|S_IWGRP|S_IXGRP|
955 S_IROTH|S_IWOTH|S_IXOTH);
1eb8539d 956 fd = mkostemp(n, O_RDWR|O_CREAT|O_EXCL|O_CLOEXEC);
1dbaf5cc
NK
957 if (fd < 0)
958 fd = -errno;
51479069 959 umask(oldmode);
6fc81224 960
b37dd175
KZ
961 if (fd >= 0 && name)
962 *name = n;
963 else
964 free(n);
965
1dbaf5cc 966 return fd;
b37dd175 967}
0bb44be3 968
cd3d6c5b
DR
969/**
970 * mnt_get_mountpoint:
971 * @path: pathname
972 *
973 * This function finds the mountpoint that a given path resides in. @path
974 * should be canonicalized. The returned pointer should be freed by the caller.
975 *
d58b3157 976 * Returns: allocated string with the target of the mounted device or NULL on error
cd3d6c5b 977 */
0bb44be3
KZ
978char *mnt_get_mountpoint(const char *path)
979{
4569bbea 980 char *mnt;
0bb44be3
KZ
981 struct stat st;
982 dev_t dir, base;
983
4569bbea
KZ
984 assert(path);
985
986 mnt = strdup(path);
0bb44be3
KZ
987 if (!mnt)
988 return NULL;
989 if (*mnt == '/' && *(mnt + 1) == '\0')
9758c88a 990 goto done;
0bb44be3
KZ
991
992 if (stat(mnt, &st))
993 goto err;
994 base = st.st_dev;
995
996 do {
997 char *p = stripoff_last_component(mnt);
998
999 if (!p)
1000 break;
1001 if (stat(*mnt ? mnt : "/", &st))
1002 goto err;
1003 dir = st.st_dev;
1004 if (dir != base) {
9d670a2a
KZ
1005 if (p > mnt)
1006 *(p - 1) = '/';
9758c88a 1007 goto done;
0bb44be3
KZ
1008 }
1009 base = dir;
1010 } while (mnt && *(mnt + 1) != '\0');
1011
1012 memcpy(mnt, "/", 2);
9758c88a 1013done:
83a78332 1014 DBG(UTILS, ul_debug("%s mountpoint is %s", path, mnt));
9758c88a 1015 return mnt;
0bb44be3
KZ
1016err:
1017 free(mnt);
1018 return NULL;
1019}
1020
9758c88a 1021char *mnt_get_fs_root(const char *path, const char *mnt)
0bb44be3 1022{
f84fa6f7 1023 char *m = (char *) mnt, *res;
0bb44be3
KZ
1024 const char *p;
1025 size_t sz;
1026
9758c88a
KZ
1027 if (!m)
1028 m = mnt_get_mountpoint(path);
1029 if (!m)
0bb44be3
KZ
1030 return NULL;
1031
9758c88a 1032 sz = strlen(m);
0bb44be3
KZ
1033 p = sz > 1 ? path + sz : path;
1034
9758c88a
KZ
1035 if (m != mnt)
1036 free(m);
0bb44be3 1037
f84fa6f7 1038 res = *p ? strdup(p) : strdup("/");
83a78332 1039 DBG(UTILS, ul_debug("%s fs-root is %s", path, res));
f84fa6f7 1040 return res;
0bb44be3
KZ
1041}
1042
f308ec19
KZ
1043/*
1044 * Search for @name kernel command parametr.
1045 *
d58b3157 1046 * Returns newly allocated string with a parameter argument if the @name is
f308ec19
KZ
1047 * specified as "name=" or returns pointer to @name or returns NULL if not
1048 * found.
1049 *
1050 * For example cmdline: "aaa bbb=BBB ccc"
1051 *
1052 * @name is "aaa" --returns--> "aaa" (pointer to @name)
1053 * @name is "bbb=" --returns--> "BBB" (allocated)
1054 * @name is "foo" --returns--> NULL
1055 */
1056char *mnt_get_kernel_cmdline_option(const char *name)
1057{
1058 FILE *f;
1059 size_t len;
1060 int val = 0;
1061 char *p, *res = NULL;
1062 char buf[BUFSIZ]; /* see kernel include/asm-generic/setup.h: COMMAND_LINE_SIZE */
1063 const char *path = _PATH_PROC_CMDLINE;
1064
ba2bdf41
KZ
1065 if (!name)
1066 return NULL;
f308ec19
KZ
1067
1068#ifdef TEST_PROGRAM
cd793967 1069 path = getenv("LIBMOUNT_KERNEL_CMDLINE");
f308ec19
KZ
1070 if (!path)
1071 path = _PATH_PROC_CMDLINE;
1072#endif
1eb8539d 1073 f = fopen(path, "r" UL_CLOEXECSTR);
f308ec19
KZ
1074 if (!f)
1075 return NULL;
1076
1077 p = fgets(buf, sizeof(buf), f);
1078 fclose(f);
1079
1080 if (!p || !*p || *p == '\n')
1081 return NULL;
1082
1083 len = strlen(buf);
1084 *(buf + len - 1) = '\0'; /* remove last '\n' */
1085
1086 len = strlen(name);
1087 if (len && *(name + len - 1) == '=')
1088 val = 1;
1089
2c0ba1ce 1090 for ( ; p && *p; p++) {
f308ec19
KZ
1091 if (!(p = strstr(p, name)))
1092 break; /* not found the option */
1093 if (p != buf && !isblank(*(p - 1)))
1094 continue; /* no space before the option */
1095 if (!val && *(p + len) != '\0' && !isblank(*(p + len)))
d58b3157 1096 continue; /* no space after the option */
f308ec19
KZ
1097 if (val) {
1098 char *v = p + len;
1099
1100 while (*p && !isblank(*p)) /* jump to the end of the argument */
1101 p++;
1102 *p = '\0';
1103 res = strdup(v);
1104 break;
1105 } else
1106 res = (char *) name; /* option without '=' */
1107 break;
1108 }
1109
1110 return res;
1111}
1112
fd73f468
KZ
1113int mkdir_p(const char *path, mode_t mode)
1114{
1115 char *p, *dir;
1116 int rc = 0;
1117
1118 if (!path || !*path)
1119 return -EINVAL;
1120
1121 dir = p = strdup(path);
1122 if (!dir)
1123 return -ENOMEM;
1124
1125 if (*p == '/')
1126 p++;
1127
1128 while (p && *p) {
1129 char *e = strchr(p, '/');
1130 if (e)
1131 *e = '\0';
1132 if (*p) {
1133 rc = mkdir(dir, mode);
1134 if (rc && errno != EEXIST)
1135 break;
1136 rc = 0;
1137 }
1138 if (!e)
1139 break;
1140 *e = '/';
1141 p = e + 1;
1142 }
1143
83a78332 1144 DBG(UTILS, ul_debug("%s mkdir %s", path, rc ? "FAILED" : "SUCCESS"));
fd73f468
KZ
1145
1146 free(dir);
1147 return rc;
1148}
1149
abc9c0f7 1150#ifdef TEST_PROGRAM
68164f6c 1151int test_match_fstype(struct libmnt_test *ts, int argc, char *argv[])
abc9c0f7
KZ
1152{
1153 char *type = argv[1];
1154 char *pattern = argv[2];
1155
1156 printf("%s\n", mnt_match_fstype(type, pattern) ? "MATCH" : "NOT-MATCH");
1157 return 0;
1158}
1159
68164f6c 1160int test_match_options(struct libmnt_test *ts, int argc, char *argv[])
abc9c0f7
KZ
1161{
1162 char *optstr = argv[1];
1163 char *pattern = argv[2];
1164
1165 printf("%s\n", mnt_match_options(optstr, pattern) ? "MATCH" : "NOT-MATCH");
1166 return 0;
1167}
1168
68164f6c 1169int test_startswith(struct libmnt_test *ts, int argc, char *argv[])
b49103ed
KZ
1170{
1171 char *optstr = argv[1];
1172 char *pattern = argv[2];
1173
1174 printf("%s\n", startswith(optstr, pattern) ? "YES" : "NOT");
1175 return 0;
1176}
1177
68164f6c 1178int test_endswith(struct libmnt_test *ts, int argc, char *argv[])
b49103ed
KZ
1179{
1180 char *optstr = argv[1];
1181 char *pattern = argv[2];
1182
1183 printf("%s\n", endswith(optstr, pattern) ? "YES" : "NOT");
1184 return 0;
1185}
1186
40b27864
KZ
1187int test_appendstr(struct libmnt_test *ts, int argc, char *argv[])
1188{
1189 char *str = strdup(argv[1]);
1190 const char *ap = argv[2];
1191
1192 append_string(&str, ap);
1193 printf("new string: '%s'\n", str);
1194
1195 free(str);
1196 return 0;
1197}
1198
68164f6c 1199int test_mountpoint(struct libmnt_test *ts, int argc, char *argv[])
0bb44be3
KZ
1200{
1201 char *path = canonicalize_path(argv[1]),
1202 *mnt = path ? mnt_get_mountpoint(path) : NULL;
1203
1204 printf("%s: %s\n", argv[1], mnt ? : "unknown");
1205 free(mnt);
1206 free(path);
1207 return 0;
1208}
1209
68164f6c 1210int test_fsroot(struct libmnt_test *ts, int argc, char *argv[])
0bb44be3
KZ
1211{
1212 char *path = canonicalize_path(argv[1]),
d672fb26 1213 *mnt = path ? mnt_get_fs_root(path, NULL) : NULL;
0bb44be3
KZ
1214
1215 printf("%s: %s\n", argv[1], mnt ? : "unknown");
1216 free(mnt);
1217 free(path);
1218 return 0;
1219}
abc9c0f7 1220
68164f6c 1221int test_filesystems(struct libmnt_test *ts, int argc, char *argv[])
97e23b5e
KZ
1222{
1223 char **filesystems = NULL;
1224 int rc;
1225
1226 rc = mnt_get_filesystems(&filesystems, argc ? argv[1] : NULL);
1227 if (!rc) {
1228 char **p;
1229 for (p = filesystems; *p; p++)
1230 printf("%s\n", *p);
1231 mnt_free_filesystems(filesystems);
1232 }
1233 return rc;
1234}
1235
66bb8267
KZ
1236int test_chdir(struct libmnt_test *ts, int argc, char *argv[])
1237{
1238 int rc;
1239 char *path = canonicalize_path(argv[1]),
1240 *last = NULL;
1241
1242 if (!path)
1243 return -errno;
1244
1245 rc = mnt_chdir_to_parent(path, &last);
1246 if (!rc) {
1247 printf("path='%s', abs='%s', last='%s'\n",
1248 argv[1], path, last);
1249 }
1250 free(path);
1251 free(last);
1252 return rc;
1253}
1254
f308ec19
KZ
1255int test_kernel_cmdline(struct libmnt_test *ts, int argc, char *argv[])
1256{
1257 char *name = argv[1];
1258 char *res;
1259
1260 res = mnt_get_kernel_cmdline_option(name);
1261 if (!res)
1262 printf("'%s' not found\n", name);
1263 else if (res == name)
1264 printf("'%s' found\n", name);
1265 else {
1266 printf("'%s' found, argument: '%s'\n", name, res);
1267 free(res);
1268 }
1269
1270 return 0;
1271}
1272
fd73f468
KZ
1273int test_mkdir(struct libmnt_test *ts, int argc, char *argv[])
1274{
1275 int rc;
1276
1277 rc = mkdir_p(argv[1], S_IRWXU |
1278 S_IRGRP | S_IXGRP |
1279 S_IROTH | S_IXOTH);
1280 if (rc)
1281 printf("mkdir %s failed\n", argv[1]);
1282 return rc;
1283}
1284
6a52473e
KZ
1285int test_statfs_type(struct libmnt_test *ts, int argc, char *argv[])
1286{
1287 struct statfs vfs;
1288 int rc;
1289
1290 rc = statfs(argv[1], &vfs);
1291 if (rc)
1292 printf("%s: statfs failed: %m\n", argv[1]);
1293 else
1294 printf("%-30s: statfs type: %-12s [0x%lx]\n", argv[1],
1295 mnt_statfs_get_fstype(&vfs),
1296 (long) vfs.f_type);
1297 return rc;
1298}
1299
66bb8267 1300
abc9c0f7
KZ
1301int main(int argc, char *argv[])
1302{
68164f6c 1303 struct libmnt_test tss[] = {
abc9c0f7
KZ
1304 { "--match-fstype", test_match_fstype, "<type> <pattern> FS types matching" },
1305 { "--match-options", test_match_options, "<options> <pattern> options matching" },
97e23b5e 1306 { "--filesystems", test_filesystems, "[<pattern>] list /{etc,proc}/filesystems" },
b49103ed
KZ
1307 { "--starts-with", test_startswith, "<string> <prefix>" },
1308 { "--ends-with", test_endswith, "<string> <prefix>" },
40b27864 1309 { "--append-string", test_appendstr, "<string> <appendix>" },
0bb44be3
KZ
1310 { "--mountpoint", test_mountpoint, "<path>" },
1311 { "--fs-root", test_fsroot, "<path>" },
66bb8267 1312 { "--cd-parent", test_chdir, "<path>" },
f308ec19 1313 { "--kernel-cmdline",test_kernel_cmdline, "<option> | <option>=" },
fd73f468 1314 { "--mkdir", test_mkdir, "<path>" },
6a52473e 1315 { "--statfs-type", test_statfs_type, "<path>" },
fd73f468 1316
abc9c0f7
KZ
1317 { NULL }
1318 };
1319
1320 return mnt_run_test(tss, argc, argv);
1321}
1322
1323#endif /* TEST_PROGRAM */