]> git.ipfire.org Git - thirdparty/util-linux.git/blame - shlibs/mount/src/utils.c
libmount: update mount.{h,sym}
[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>
30
b49103ed 31#include "strutils.h"
0532ba1d 32#include "pathnames.h"
69b7e41e 33#include "mountP.h"
3c5e4ef8 34#include "mangle.h"
0bb44be3 35#include "canonicalize.h"
69b7e41e
KZ
36
37char *mnt_getenv_safe(const char *arg)
38{
39 if ((getuid() != geteuid()) || (getgid() != getegid()))
40 return NULL;
41#if HAVE_PRCTL
42 if (prctl(PR_GET_DUMPABLE, 0, 0, 0, 0) == 0)
43 return NULL;
44#else
45#if (defined(linux) && defined(SYS_prctl))
46 if (syscall(SYS_prctl, PR_GET_DUMPABLE, 0, 0, 0, 0) == 0)
47 return NULL;
48#endif
49#endif
50
51#ifdef HAVE___SECURE_GETENV
52 return __secure_getenv(arg);
53#else
54 return getenv(arg);
55#endif
56}
57
b49103ed
KZ
58int endswith(const char *s, const char *sx)
59{
60 ssize_t off;
61
62 assert(s);
63 assert(sx);
64
65 off = strlen(s);
66 if (!off)
67 return 0;
68 off -= strlen(sx);
69 if (off < 0)
70 return 0;
71
72 return !strcmp(s + off, sx);
73}
74
75int startswith(const char *s, const char *sx)
76{
77 size_t off;
78
79 assert(s);
80 assert(sx);
81
82 off = strlen(sx);
83 if (!off)
84 return 0;
85
86 return !strncmp(s, sx, off);
87}
88
3c5e4ef8
KZ
89/**
90 * mnt_mangle:
91 * @str: string
92 *
93 * Encode @str to be compatible with fstab/mtab
94 *
95 * Returns: new allocated string or NULL in case of error.
96 */
97char *mnt_mangle(const char *str)
98{
99 return mangle(str);
100}
101
102/**
103 * mnt_unmangle:
104 * @str: string
105 *
106 * Decode @str from fstab/mtab
107 *
108 * Returns: new allocated string or NULL in case of error.
109 */
110char *mnt_unmangle(const char *str)
111{
112 return unmangle(str);
113}
114
69b7e41e
KZ
115/**
116 * mnt_fstype_is_pseudofs:
117 * @type: filesystem name
118 *
119 * Returns: 1 for filesystems like proc, sysfs, ... or 0.
120 */
121int mnt_fstype_is_pseudofs(const char *type)
122{
123 if (!type)
124 return 0;
125 if (strcmp(type, "none") == 0 ||
126 strcmp(type, "proc") == 0 ||
127 strcmp(type, "tmpfs") == 0 ||
128 strcmp(type, "sysfs") == 0 ||
129 strcmp(type, "devpts") == 0||
130 strcmp(type, "cgroups") == 0 ||
131 strcmp(type, "devfs") == 0 ||
132 strcmp(type, "dlmfs") == 0 ||
133 strcmp(type, "cpuset") == 0 ||
134 strcmp(type, "spufs") == 0)
135 return 1;
136 return 0;
137}
138
139/**
140 * mnt_fstype_is_netfs:
141 * @type: filesystem name
142 *
143 * Returns: 1 for filesystems like cifs, nfs, ... or 0.
144 */
145int mnt_fstype_is_netfs(const char *type)
146{
147 if (!type)
148 return 0;
149 if (strcmp(type, "cifs") == 0 ||
150 strcmp(type, "smbfs") == 0 ||
151 strncmp(type, "nfs", 3) == 0 ||
152 strcmp(type, "afs") == 0 ||
153 strcmp(type, "ncpfs") == 0)
154 return 1;
155 return 0;
156}
157
abc9c0f7
KZ
158/**
159 * mnt_match_fstype:
160 * @type: filesystem type
161 * @pattern: filesystem name or comma delimitted list of names
162 *
163 * The @pattern list of filesystem can be prefixed with a global
164 * "no" prefix to invert matching of the whole list. The "no" could
3d735589
KZ
165 * also used for individual items in the @pattern list. So,
166 * "nofoo,bar" has the same meaning as "nofoo,nobar".
abc9c0f7 167 *
3d735589
KZ
168 * "bar" : "nofoo,bar" -> False (global "no" prefix)
169 *
170 * "bar" : "foo,bar" -> True
abc9c0f7 171 *
abc9c0f7
KZ
172 * "bar" : "foo,nobar" -> False
173 *
174 * Returns: 1 if type is matching, else 0. This function also returns
175 * 0 if @pattern is NULL and @type is non-NULL.
176 */
177int mnt_match_fstype(const char *type, const char *pattern)
178{
179 int no = 0; /* negated types list */
180 int len;
181 const char *p;
182
183 if (!pattern && !type)
184 return 1;
185 if (!pattern)
186 return 0;
187
188 if (!strncmp(pattern, "no", 2)) {
189 no = 1;
190 pattern += 2;
191 }
192
193 /* Does type occur in types, separated by commas? */
194 len = strlen(type);
195 p = pattern;
196 while(1) {
197 if (!strncmp(p, "no", 2) && !strncmp(p+2, type, len) &&
198 (p[len+2] == 0 || p[len+2] == ','))
199 return 0;
200 if (strncmp(p, type, len) == 0 && (p[len] == 0 || p[len] == ','))
201 return !no;
202 p = strchr(p,',');
203 if (!p)
204 break;
205 p++;
206 }
207 return no;
208}
209
210
211/* Returns 1 if needle found or noneedle not found in haystack
212 * Otherwise returns 0
213 */
214static int check_option(const char *haystack, size_t len,
215 const char *needle, size_t needle_len)
216{
217 const char *p;
218 int no = 0;
219
220 if (needle_len >= 2 && !strncmp(needle, "no", 2)) {
221 no = 1;
222 needle += 2;
223 needle_len -= 2;
224 }
225
226 for (p = haystack; p && p < haystack + len; p++) {
227 char *sep = strchr(p, ',');
228 size_t plen = sep ? sep - p : len - (p - haystack);
229
230 if (plen == needle_len) {
231 if (!strncmp(p, needle, plen))
232 return !no; /* foo or nofoo was found */
233 }
234 p += plen;
235 }
236
237 return no; /* foo or nofoo was not found */
238}
239
240/**
241 * mnt_match_options:
242 * @optstr: options string
243 * @pattern: comma delimitted list of options
244 *
245 * The "no" could used for individual items in the @options list. The "no"
246 * prefix does not have a global meanning.
247 *
248 * Unlike fs type matching, nonetdev,user and nonetdev,nouser have
249 * DIFFERENT meanings; each option is matched explicitly as specified.
250 *
3d735589
KZ
251 * "xxx,yyy,zzz" : "nozzz" -> False
252 *
253 * "xxx,yyy,zzz" : "xxx,noeee" -> True
abc9c0f7
KZ
254 *
255 * Returns: 1 if pattern is matching, else 0. This function also returns 0
256 * if @pattern is NULL and @optstr is non-NULL.
257 */
258int mnt_match_options(const char *optstr, const char *pattern)
259{
260 const char *p;
261 size_t len, optstr_len = 0;
262
263 if (!pattern && !optstr)
264 return 1;
265 if (!pattern)
266 return 0;
267
268 len = strlen(pattern);
269 if (optstr)
270 optstr_len = strlen(optstr);
271
272 for (p = pattern; p < pattern + len; p++) {
273 char *sep = strchr(p, ',');
274 size_t plen = sep ? sep - p : len - (p - pattern);
275
276 if (!plen)
277 continue; /* if two ',' appear in a row */
278
279 if (!check_option(optstr, optstr_len, p, plen))
280 return 0; /* any match failure means failure */
281
282 p += plen;
283 }
284
285 /* no match failures in list means success */
286 return 1;
287}
288
69b7e41e
KZ
289/*
290 * Returns allocated string with username or NULL.
291 */
292char *mnt_get_username(const uid_t uid)
293{
294 struct passwd pwd;
295 struct passwd *res;
296 size_t sz = sysconf(_SC_GETPW_R_SIZE_MAX);
297 char *buf, *username = NULL;
298
299 if (sz <= 0)
300 sz = 16384; /* Should be more than enough */
301
302 buf = malloc(sz);
303 if (!buf)
304 return NULL;
305
306 if (!getpwuid_r(uid, &pwd, buf, sz, &res) && res)
307 username = strdup(pwd.pw_name);
308
309 free(buf);
310 return username;
311}
abc9c0f7 312
0532ba1d
KZ
313/*
314 * Returns 1 if /etc/mtab is a reqular file.
315 */
316int mnt_has_regular_mtab(void)
317{
318 struct stat st;
319
320 if (lstat(_PATH_MOUNTED, &st) == 0 && S_ISREG(st.st_mode))
321 return 1;
322 return 0;
323}
324
d1be0c34
KZ
325/**
326 * mnt_get_writable_mtab_path:
327 *
be1a5180
KZ
328 * It's not error if this function return NULL and errno is not set. In case of
329 * error the errno is set by open(2).
330 *
331 * Returns: pointer to the static string with path to mtab or NULL.
d1be0c34
KZ
332 */
333const char *mnt_get_writable_mtab_path(void)
334{
335 struct stat mst, ist;
336 int mtab, info;
337
338 mtab = !lstat(_PATH_MOUNTED, &mst);
339 info = !stat(MNT_PATH_RUNDIR, &ist);
340
be1a5180
KZ
341 errno = 0;
342
d1be0c34
KZ
343 /* A) mtab is symlink, /var/run/mount is available */
344 if (mtab && S_ISLNK(mst.st_mode) && info) {
345 int fd = open(MNT_PATH_MOUNTINFO, O_RDWR | O_CREAT, 0644);
346 if (fd >= 0) {
347 close(fd);
348 return MNT_PATH_MOUNTINFO;
349 }
350 return NULL; /* probably EACCES */
351 }
352
353 /* B) classis system with /etc/mtab */
354 if (mtab && S_ISREG(mst.st_mode)) {
355 int fd = open(_PATH_MOUNTED, O_RDWR, 0644);
356 if (fd >= 0) {
357 close(fd);
358 return _PATH_MOUNTED;
359 }
360 return NULL; /* probably EACCES */
361 }
362
363 return NULL;
364}
365
0bb44be3
KZ
366
367/* returns basename and keeps dirname in the @path, if @path is "/" (root)
368 * then returns empty string */
369static char *stripoff_last_component(char *path)
370{
371 char *p = strrchr(path, '/');
372
373 if (!p)
374 return NULL;
375 *p = '\0';
376 return ++p;
377}
378
379char *mnt_get_mountpoint(const char *path)
380{
381 char *mnt = strdup(path);
382 struct stat st;
383 dev_t dir, base;
384
385 if (!mnt)
386 return NULL;
387 if (*mnt == '/' && *(mnt + 1) == '\0')
9758c88a 388 goto done;
0bb44be3
KZ
389
390 if (stat(mnt, &st))
391 goto err;
392 base = st.st_dev;
393
394 do {
395 char *p = stripoff_last_component(mnt);
396
397 if (!p)
398 break;
399 if (stat(*mnt ? mnt : "/", &st))
400 goto err;
401 dir = st.st_dev;
402 if (dir != base) {
403 *(p - 1) = '/';
9758c88a 404 goto done;
0bb44be3
KZ
405 }
406 base = dir;
407 } while (mnt && *(mnt + 1) != '\0');
408
409 memcpy(mnt, "/", 2);
9758c88a
KZ
410done:
411 DBG(DEBUG_UTILS, fprintf(stderr,
412 "libmount: utils: fs-root for %s is %s\n", path, mnt));
413 return mnt;
0bb44be3
KZ
414err:
415 free(mnt);
416 return NULL;
417}
418
9758c88a 419char *mnt_get_fs_root(const char *path, const char *mnt)
0bb44be3 420{
9758c88a 421 char *m = (char *) mnt;
0bb44be3
KZ
422 const char *p;
423 size_t sz;
424
9758c88a
KZ
425 if (!m)
426 m = mnt_get_mountpoint(path);
427 if (!m)
0bb44be3
KZ
428 return NULL;
429
9758c88a 430 sz = strlen(m);
0bb44be3
KZ
431 p = sz > 1 ? path + sz : path;
432
9758c88a
KZ
433 if (m != mnt)
434 free(m);
0bb44be3
KZ
435
436 return *p ? strdup(p) : strdup("/");
437}
438
abc9c0f7
KZ
439#ifdef TEST_PROGRAM
440int test_match_fstype(struct mtest *ts, int argc, char *argv[])
441{
442 char *type = argv[1];
443 char *pattern = argv[2];
444
445 printf("%s\n", mnt_match_fstype(type, pattern) ? "MATCH" : "NOT-MATCH");
446 return 0;
447}
448
449int test_match_options(struct mtest *ts, int argc, char *argv[])
450{
451 char *optstr = argv[1];
452 char *pattern = argv[2];
453
454 printf("%s\n", mnt_match_options(optstr, pattern) ? "MATCH" : "NOT-MATCH");
455 return 0;
456}
457
b49103ed
KZ
458int test_startswith(struct mtest *ts, int argc, char *argv[])
459{
460 char *optstr = argv[1];
461 char *pattern = argv[2];
462
463 printf("%s\n", startswith(optstr, pattern) ? "YES" : "NOT");
464 return 0;
465}
466
467int test_endswith(struct mtest *ts, int argc, char *argv[])
468{
469 char *optstr = argv[1];
470 char *pattern = argv[2];
471
472 printf("%s\n", endswith(optstr, pattern) ? "YES" : "NOT");
473 return 0;
474}
475
0bb44be3
KZ
476int test_mountpoint(struct mtest *ts, int argc, char *argv[])
477{
478 char *path = canonicalize_path(argv[1]),
479 *mnt = path ? mnt_get_mountpoint(path) : NULL;
480
481 printf("%s: %s\n", argv[1], mnt ? : "unknown");
482 free(mnt);
483 free(path);
484 return 0;
485}
486
487int test_fsroot(struct mtest *ts, int argc, char *argv[])
488{
489 char *path = canonicalize_path(argv[1]),
490 *mnt = path ? mnt_get_fs_root(path) : NULL;
491
492 printf("%s: %s\n", argv[1], mnt ? : "unknown");
493 free(mnt);
494 free(path);
495 return 0;
496}
abc9c0f7
KZ
497
498int main(int argc, char *argv[])
499{
500 struct mtest tss[] = {
501 { "--match-fstype", test_match_fstype, "<type> <pattern> FS types matching" },
502 { "--match-options", test_match_options, "<options> <pattern> options matching" },
b49103ed
KZ
503 { "--starts-with", test_startswith, "<string> <prefix>" },
504 { "--ends-with", test_endswith, "<string> <prefix>" },
0bb44be3
KZ
505 { "--mountpoint", test_mountpoint, "<path>" },
506 { "--fs-root", test_fsroot, "<path>" },
abc9c0f7
KZ
507 { NULL }
508 };
509
510 return mnt_run_test(tss, argc, argv);
511}
512
513#endif /* TEST_PROGRAM */