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