]> git.ipfire.org Git - thirdparty/util-linux.git/blame - shlibs/mount/src/utils.c
libmount: rewrite update
[thirdparty/util-linux.git] / shlibs / mount / 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
KZ
13#include <unistd.h>
14#include <errno.h>
15#include <stdlib.h>
16#include <string.h>
17#ifdef HAVE_SYS_PRCTL_H
18#include <sys/prctl.h>
19#else
20#define PR_GET_DUMPABLE 3
21#endif
22#if (!defined(HAVE_PRCTL) && defined(linux))
23#include <sys/syscall.h>
24#endif
25#include <sys/stat.h>
26#include <ctype.h>
27#include <sys/types.h>
28#include <fcntl.h>
29#include <pwd.h>
a1e8af75 30#include <grp.h>
69b7e41e 31
b49103ed 32#include "strutils.h"
0532ba1d 33#include "pathnames.h"
69b7e41e 34#include "mountP.h"
3c5e4ef8 35#include "mangle.h"
0bb44be3 36#include "canonicalize.h"
69b7e41e
KZ
37
38char *mnt_getenv_safe(const char *arg)
39{
40 if ((getuid() != geteuid()) || (getgid() != getegid()))
41 return NULL;
42#if HAVE_PRCTL
43 if (prctl(PR_GET_DUMPABLE, 0, 0, 0, 0) == 0)
44 return NULL;
45#else
46#if (defined(linux) && defined(SYS_prctl))
47 if (syscall(SYS_prctl, PR_GET_DUMPABLE, 0, 0, 0, 0) == 0)
48 return NULL;
49#endif
50#endif
51
52#ifdef HAVE___SECURE_GETENV
53 return __secure_getenv(arg);
54#else
55 return getenv(arg);
56#endif
57}
58
b49103ed
KZ
59int endswith(const char *s, const char *sx)
60{
61 ssize_t off;
62
63 assert(s);
64 assert(sx);
65
66 off = strlen(s);
67 if (!off)
68 return 0;
69 off -= strlen(sx);
70 if (off < 0)
71 return 0;
72
73 return !strcmp(s + off, sx);
74}
75
76int startswith(const char *s, const char *sx)
77{
78 size_t off;
79
80 assert(s);
81 assert(sx);
82
83 off = strlen(sx);
84 if (!off)
85 return 0;
86
87 return !strncmp(s, sx, off);
88}
89
1d0cd73f
KZ
90/* returns basename and keeps dirname in the @path, if @path is "/" (root)
91 * then returns empty string */
92static char *stripoff_last_component(char *path)
93{
94 char *p = path ? strrchr(path, '/') : NULL;
95
96 if (!p)
97 return NULL;
98 *p = '\0';
99 return ++p;
100}
101
3c5e4ef8
KZ
102/**
103 * mnt_mangle:
104 * @str: string
105 *
106 * Encode @str to be compatible with fstab/mtab
107 *
108 * Returns: new allocated string or NULL in case of error.
109 */
110char *mnt_mangle(const char *str)
111{
112 return mangle(str);
113}
114
115/**
116 * mnt_unmangle:
117 * @str: string
118 *
119 * Decode @str from fstab/mtab
120 *
121 * Returns: new allocated string or NULL in case of error.
122 */
123char *mnt_unmangle(const char *str)
124{
125 return unmangle(str);
126}
127
69b7e41e
KZ
128/**
129 * mnt_fstype_is_pseudofs:
130 * @type: filesystem name
131 *
132 * Returns: 1 for filesystems like proc, sysfs, ... or 0.
133 */
134int mnt_fstype_is_pseudofs(const char *type)
135{
136 if (!type)
137 return 0;
138 if (strcmp(type, "none") == 0 ||
139 strcmp(type, "proc") == 0 ||
140 strcmp(type, "tmpfs") == 0 ||
141 strcmp(type, "sysfs") == 0 ||
142 strcmp(type, "devpts") == 0||
143 strcmp(type, "cgroups") == 0 ||
144 strcmp(type, "devfs") == 0 ||
145 strcmp(type, "dlmfs") == 0 ||
146 strcmp(type, "cpuset") == 0 ||
1bbe1bc7 147 strcmp(type, "securityfs") == 0 ||
c5a6f7a9
KZ
148 strcmp(type, "rpc_pipefs") == 0 ||
149 strcmp(type, "fusectl") == 0 ||
150 strcmp(type, "binfmt_misc") == 0 ||
151 strcmp(type, "fuse.gvfs-fuse-daemon") == 0 ||
1bbe1bc7 152 strcmp(type, "debugfs") == 0 ||
69b7e41e
KZ
153 strcmp(type, "spufs") == 0)
154 return 1;
155 return 0;
156}
157
158/**
159 * mnt_fstype_is_netfs:
160 * @type: filesystem name
161 *
162 * Returns: 1 for filesystems like cifs, nfs, ... or 0.
163 */
164int mnt_fstype_is_netfs(const char *type)
165{
166 if (!type)
167 return 0;
168 if (strcmp(type, "cifs") == 0 ||
169 strcmp(type, "smbfs") == 0 ||
170 strncmp(type, "nfs", 3) == 0 ||
171 strcmp(type, "afs") == 0 ||
172 strcmp(type, "ncpfs") == 0)
173 return 1;
174 return 0;
175}
176
abc9c0f7
KZ
177/**
178 * mnt_match_fstype:
179 * @type: filesystem type
180 * @pattern: filesystem name or comma delimitted list of names
181 *
182 * The @pattern list of filesystem can be prefixed with a global
183 * "no" prefix to invert matching of the whole list. The "no" could
3d735589
KZ
184 * also used for individual items in the @pattern list. So,
185 * "nofoo,bar" has the same meaning as "nofoo,nobar".
abc9c0f7 186 *
3d735589
KZ
187 * "bar" : "nofoo,bar" -> False (global "no" prefix)
188 *
189 * "bar" : "foo,bar" -> True
abc9c0f7 190 *
abc9c0f7
KZ
191 * "bar" : "foo,nobar" -> False
192 *
193 * Returns: 1 if type is matching, else 0. This function also returns
194 * 0 if @pattern is NULL and @type is non-NULL.
195 */
196int mnt_match_fstype(const char *type, const char *pattern)
197{
198 int no = 0; /* negated types list */
199 int len;
200 const char *p;
201
202 if (!pattern && !type)
203 return 1;
204 if (!pattern)
205 return 0;
206
207 if (!strncmp(pattern, "no", 2)) {
208 no = 1;
209 pattern += 2;
210 }
211
212 /* Does type occur in types, separated by commas? */
213 len = strlen(type);
214 p = pattern;
215 while(1) {
216 if (!strncmp(p, "no", 2) && !strncmp(p+2, type, len) &&
217 (p[len+2] == 0 || p[len+2] == ','))
218 return 0;
219 if (strncmp(p, type, len) == 0 && (p[len] == 0 || p[len] == ','))
220 return !no;
221 p = strchr(p,',');
222 if (!p)
223 break;
224 p++;
225 }
226 return no;
227}
228
229
230/* Returns 1 if needle found or noneedle not found in haystack
231 * Otherwise returns 0
232 */
233static int check_option(const char *haystack, size_t len,
234 const char *needle, size_t needle_len)
235{
236 const char *p;
237 int no = 0;
238
239 if (needle_len >= 2 && !strncmp(needle, "no", 2)) {
240 no = 1;
241 needle += 2;
242 needle_len -= 2;
243 }
244
245 for (p = haystack; p && p < haystack + len; p++) {
246 char *sep = strchr(p, ',');
247 size_t plen = sep ? sep - p : len - (p - haystack);
248
249 if (plen == needle_len) {
250 if (!strncmp(p, needle, plen))
251 return !no; /* foo or nofoo was found */
252 }
253 p += plen;
254 }
255
256 return no; /* foo or nofoo was not found */
257}
258
259/**
260 * mnt_match_options:
261 * @optstr: options string
262 * @pattern: comma delimitted list of options
263 *
264 * The "no" could used for individual items in the @options list. The "no"
265 * prefix does not have a global meanning.
266 *
267 * Unlike fs type matching, nonetdev,user and nonetdev,nouser have
268 * DIFFERENT meanings; each option is matched explicitly as specified.
269 *
3d735589
KZ
270 * "xxx,yyy,zzz" : "nozzz" -> False
271 *
272 * "xxx,yyy,zzz" : "xxx,noeee" -> True
abc9c0f7
KZ
273 *
274 * Returns: 1 if pattern is matching, else 0. This function also returns 0
275 * if @pattern is NULL and @optstr is non-NULL.
276 */
277int mnt_match_options(const char *optstr, const char *pattern)
278{
279 const char *p;
280 size_t len, optstr_len = 0;
281
282 if (!pattern && !optstr)
283 return 1;
284 if (!pattern)
285 return 0;
286
287 len = strlen(pattern);
288 if (optstr)
289 optstr_len = strlen(optstr);
290
291 for (p = pattern; p < pattern + len; p++) {
292 char *sep = strchr(p, ',');
293 size_t plen = sep ? sep - p : len - (p - pattern);
294
295 if (!plen)
296 continue; /* if two ',' appear in a row */
297
298 if (!check_option(optstr, optstr_len, p, plen))
299 return 0; /* any match failure means failure */
300
301 p += plen;
302 }
303
304 /* no match failures in list means success */
305 return 1;
306}
307
69b7e41e
KZ
308/*
309 * Returns allocated string with username or NULL.
310 */
311char *mnt_get_username(const uid_t uid)
312{
313 struct passwd pwd;
314 struct passwd *res;
315 size_t sz = sysconf(_SC_GETPW_R_SIZE_MAX);
316 char *buf, *username = NULL;
317
318 if (sz <= 0)
319 sz = 16384; /* Should be more than enough */
320
321 buf = malloc(sz);
322 if (!buf)
323 return NULL;
324
325 if (!getpwuid_r(uid, &pwd, buf, sz, &res) && res)
326 username = strdup(pwd.pw_name);
327
328 free(buf);
329 return username;
330}
abc9c0f7 331
a1e8af75
KZ
332int mnt_get_uid(const char *username, uid_t *uid)
333{
188dc15a 334 int rc = -1;
a1e8af75
KZ
335 struct passwd pwd;
336 struct passwd *pw;
337 size_t sz = sysconf(_SC_GETPW_R_SIZE_MAX);
338 char *buf;
339
188dc15a
KZ
340 if (!username || !uid)
341 return -EINVAL;
a1e8af75
KZ
342 if (sz <= 0)
343 sz = 16384; /* Should be more than enough */
344
345 buf = malloc(sz);
346 if (!buf)
347 return -ENOMEM;
348
188dc15a 349 if (!getpwnam_r(username, &pwd, buf, sz, &pw) && pw) {
a1e8af75 350 *uid= pw->pw_uid;
188dc15a
KZ
351 rc = 0;
352 } else {
353 DBG(UTILS, mnt_debug(
354 "cannot convert '%s' username to UID", username));
355 }
a1e8af75
KZ
356
357 free(buf);
188dc15a 358 return rc;
a1e8af75
KZ
359}
360
361int mnt_get_gid(const char *groupname, gid_t *gid)
362{
188dc15a 363 int rc = -1;
a1e8af75
KZ
364 struct group grp;
365 struct group *gr;
366 size_t sz = sysconf(_SC_GETGR_R_SIZE_MAX);
367 char *buf;
368
188dc15a
KZ
369 if (!groupname || !gid)
370 return -EINVAL;
a1e8af75
KZ
371 if (sz <= 0)
372 sz = 16384; /* Should be more than enough */
373
374 buf = malloc(sz);
375 if (!buf)
376 return -ENOMEM;
377
188dc15a 378 if (!getgrnam_r(groupname, &grp, buf, sz, &gr) && gr) {
a1e8af75 379 *gid= gr->gr_gid;
188dc15a
KZ
380 rc = 0;
381 } else {
382 DBG(UTILS, mnt_debug(
383 "cannot convert '%s' groupname to GID", groupname));
384 }
a1e8af75
KZ
385
386 free(buf);
188dc15a
KZ
387 return rc;
388}
389
390int mnt_in_group(gid_t gid)
391{
392 int rc = 0, n, i;
393 gid_t *grps = NULL;
394
395 if (getgid() == gid)
396 return 1;
397
398 n = getgroups(0, NULL);
399 if (n <= 0)
400 goto done;
401
402 grps = malloc(n * sizeof(*grps));
403 if (!grps)
404 goto done;
405
406 if (getgroups(n, grps) == n) {
407 for (i = 0; i < n; i++) {
408 if (grps[i] == gid) {
409 rc = 1;
410 break;
411 }
412 }
413 }
414done:
415 free(grps);
416 return rc;
a1e8af75
KZ
417}
418
1d0cd73f
KZ
419static int try_write(const char *filename)
420{
421 int fd;
422
423 if (!filename)
424 return -EINVAL;
425
426 fd = open(filename, O_RDWR, 0644);
427 if (fd >= 0) {
428 close(fd);
429 return 0;
430 }
431 return -errno;
432}
433
434/**
435 * mnt_has_regular_mtab:
436 * @mtab: returns path to mtab
437 * @writable: returns 1 if the file is writable
438 *
439 * If the file does not exist and @writable argument is not NULL then it will
440 * try to create the file
441 *
442 * Returns: 1 if /etc/mtab is a reqular file, and 0 in case of error (check
443 * errno for more details).
0532ba1d 444 */
1d0cd73f 445int mnt_has_regular_mtab(const char **mtab, int *writable)
0532ba1d
KZ
446{
447 struct stat st;
70bf97ae 448 int rc;
1d0cd73f 449 const char *filename = mtab && *mtab ? *mtab : mnt_get_mtab_path();
0532ba1d 450
70bf97ae 451 if (mtab && !*mtab)
1d0cd73f
KZ
452 *mtab = filename;
453
454 DBG(UTILS, mnt_debug("mtab: %s", filename));
70bf97ae 455
1d0cd73f
KZ
456 rc = lstat(filename, &st);
457
458 if (rc == 0) {
459 /* file exist */
460 if (S_ISREG(st.st_mode)) {
461 if (writable)
462 *writable = !try_write(filename);
463 return 1;
464 }
465 return 0; /* it's not regular file */
466 }
467
468 /* try to create the file */
70bf97ae 469 if (writable) {
1d0cd73f
KZ
470 *writable = !try_write(filename);
471 return *writable;
472 }
473
474 return 0;
475}
476/**
477 *
478 * mnt_has_regular_utab:
479 * @utab: returns path to utab (usually /dev/.mount/utab)
480 * @writable: returns 1 if the file is writable
481 *
482 * If the file does not exist and @writable argument is not NULL then it will
483 * try to create the directory (e.g. /dev/.mount) and the file.
484 *
485 * Returns: 1 if /etc/utab is a reqular file, and 0 in case of error (check
486 * errno for more details).
487 */
488
489int mnt_has_regular_utab(const char **utab, int *writable)
490{
491 struct stat st;
492 int rc;
493 const char *filename = utab && *utab ? *utab : mnt_get_utab_path();
494
495 if (utab && !*utab)
496 *utab = filename;
497
498 DBG(UTILS, mnt_debug("utab: %s", filename));
499
500 rc = lstat(filename, &st);
501
502 if (rc == 0) {
503 /* file exist */
504 if (S_ISREG(st.st_mode)) {
505 if (writable)
506 *writable = try_write(filename);
507 return 1;
70bf97ae 508 }
1d0cd73f 509 return 0; /* it's not regular file */
70bf97ae 510 }
1d0cd73f
KZ
511
512 if (writable) {
513 char *dirname = strdup(filename);
514
515 if (!dirname)
516 return 0;
517
518 stripoff_last_component(dirname); /* remove filename */
519
520 rc = mkdir(dirname, 755);
521 free(dirname);
522 if (rc && errno != EEXIST)
523 return 0; /* probably EACCES */
524
525 *writable = !try_write(filename);
526 return *writable;
527 }
528 return 0;
0532ba1d
KZ
529}
530
3a5b1b1d
KZ
531/**
532 * mnt_get_fstab_path:
533 *
534 * Returns: path to /etc/fstab or $LIBMOUNT_FSTAB.
535 */
536const char *mnt_get_fstab_path(void)
537{
538 const char *p = mnt_getenv_safe("LIBMOUNT_FSTAB");
539 return p ? : _PATH_MNTTAB;
540}
541
542/**
543 * mnt_get_mtab_path:
544 *
b37dd175
KZ
545 * This function returns *default* location of the mtab file. The result does
546 * not have to be writable. See also mnt_get_writable_mtab_path().
3a5b1b1d
KZ
547 *
548 * Returns: path to /etc/mtab or $LIBMOUNT_MTAB.
549 */
550const char *mnt_get_mtab_path(void)
551{
552 const char *p = mnt_getenv_safe("LIBMOUNT_MTAB");
553 return p ? : _PATH_MOUNTED;
554}
555
d1be0c34 556/**
b37dd175 557 * mnt_get_utab_path:
d1be0c34 558 *
b37dd175 559 * This function returns *default* location of the utab file.
be1a5180 560 *
b37dd175
KZ
561 * Returns: path to /dev/.mount/utab or $LIBMOUNT_UTAB.
562 */
563const char *mnt_get_utab_path(void)
564{
565 const char *p = mnt_getenv_safe("LIBMOUNT_UTAB");
566 return p ? : MNT_PATH_UTAB;
567}
568
d1be0c34 569
b37dd175
KZ
570/* returns file descriptor or -errno, @name returns uniques filename
571 */
572int mnt_open_uniq_filename(const char *filename, char **name, int flags)
573{
574 int rc, fd;
575 char *n;
576
577 assert(filename);
578
579 if (name)
580 *name = NULL;
581
582 rc = asprintf(&n, "%s.XXXXXX", filename);
583 if (rc <= 0)
584 return -errno;
585
586 fd = mkostemp(n, flags | O_EXCL);
587 if (fd >= 0 && name)
588 *name = n;
589 else
590 free(n);
591
592 return fd < 0 ? -errno : fd;
593}
0bb44be3 594
0bb44be3
KZ
595char *mnt_get_mountpoint(const char *path)
596{
597 char *mnt = strdup(path);
598 struct stat st;
599 dev_t dir, base;
600
601 if (!mnt)
602 return NULL;
603 if (*mnt == '/' && *(mnt + 1) == '\0')
9758c88a 604 goto done;
0bb44be3
KZ
605
606 if (stat(mnt, &st))
607 goto err;
608 base = st.st_dev;
609
610 do {
611 char *p = stripoff_last_component(mnt);
612
613 if (!p)
614 break;
615 if (stat(*mnt ? mnt : "/", &st))
616 goto err;
617 dir = st.st_dev;
618 if (dir != base) {
619 *(p - 1) = '/';
9758c88a 620 goto done;
0bb44be3
KZ
621 }
622 base = dir;
623 } while (mnt && *(mnt + 1) != '\0');
624
625 memcpy(mnt, "/", 2);
9758c88a 626done:
3f31a959 627 DBG(UTILS, mnt_debug("fs-root for %s is %s", path, mnt));
9758c88a 628 return mnt;
0bb44be3
KZ
629err:
630 free(mnt);
631 return NULL;
632}
633
9758c88a 634char *mnt_get_fs_root(const char *path, const char *mnt)
0bb44be3 635{
9758c88a 636 char *m = (char *) mnt;
0bb44be3
KZ
637 const char *p;
638 size_t sz;
639
9758c88a
KZ
640 if (!m)
641 m = mnt_get_mountpoint(path);
642 if (!m)
0bb44be3
KZ
643 return NULL;
644
9758c88a 645 sz = strlen(m);
0bb44be3
KZ
646 p = sz > 1 ? path + sz : path;
647
9758c88a
KZ
648 if (m != mnt)
649 free(m);
0bb44be3
KZ
650
651 return *p ? strdup(p) : strdup("/");
652}
653
abc9c0f7
KZ
654#ifdef TEST_PROGRAM
655int test_match_fstype(struct mtest *ts, int argc, char *argv[])
656{
657 char *type = argv[1];
658 char *pattern = argv[2];
659
660 printf("%s\n", mnt_match_fstype(type, pattern) ? "MATCH" : "NOT-MATCH");
661 return 0;
662}
663
664int test_match_options(struct mtest *ts, int argc, char *argv[])
665{
666 char *optstr = argv[1];
667 char *pattern = argv[2];
668
669 printf("%s\n", mnt_match_options(optstr, pattern) ? "MATCH" : "NOT-MATCH");
670 return 0;
671}
672
b49103ed
KZ
673int test_startswith(struct mtest *ts, int argc, char *argv[])
674{
675 char *optstr = argv[1];
676 char *pattern = argv[2];
677
678 printf("%s\n", startswith(optstr, pattern) ? "YES" : "NOT");
679 return 0;
680}
681
682int test_endswith(struct mtest *ts, int argc, char *argv[])
683{
684 char *optstr = argv[1];
685 char *pattern = argv[2];
686
687 printf("%s\n", endswith(optstr, pattern) ? "YES" : "NOT");
688 return 0;
689}
690
0bb44be3
KZ
691int test_mountpoint(struct mtest *ts, int argc, char *argv[])
692{
693 char *path = canonicalize_path(argv[1]),
694 *mnt = path ? mnt_get_mountpoint(path) : NULL;
695
696 printf("%s: %s\n", argv[1], mnt ? : "unknown");
697 free(mnt);
698 free(path);
699 return 0;
700}
701
702int test_fsroot(struct mtest *ts, int argc, char *argv[])
703{
704 char *path = canonicalize_path(argv[1]),
d672fb26 705 *mnt = path ? mnt_get_fs_root(path, NULL) : NULL;
0bb44be3
KZ
706
707 printf("%s: %s\n", argv[1], mnt ? : "unknown");
708 free(mnt);
709 free(path);
710 return 0;
711}
abc9c0f7
KZ
712
713int main(int argc, char *argv[])
714{
715 struct mtest tss[] = {
716 { "--match-fstype", test_match_fstype, "<type> <pattern> FS types matching" },
717 { "--match-options", test_match_options, "<options> <pattern> options matching" },
b49103ed
KZ
718 { "--starts-with", test_startswith, "<string> <prefix>" },
719 { "--ends-with", test_endswith, "<string> <prefix>" },
0bb44be3
KZ
720 { "--mountpoint", test_mountpoint, "<path>" },
721 { "--fs-root", test_fsroot, "<path>" },
abc9c0f7
KZ
722 { NULL }
723 };
724
725 return mnt_run_test(tss, argc, argv);
726}
727
728#endif /* TEST_PROGRAM */