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