]> git.ipfire.org Git - thirdparty/util-linux.git/blame - shlibs/mount/src/utils.c
libmount: add {start,end}swith() functions
[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
KZ
33#include "mountP.h"
34
35char *mnt_getenv_safe(const char *arg)
36{
37 if ((getuid() != geteuid()) || (getgid() != getegid()))
38 return NULL;
39#if HAVE_PRCTL
40 if (prctl(PR_GET_DUMPABLE, 0, 0, 0, 0) == 0)
41 return NULL;
42#else
43#if (defined(linux) && defined(SYS_prctl))
44 if (syscall(SYS_prctl, PR_GET_DUMPABLE, 0, 0, 0, 0) == 0)
45 return NULL;
46#endif
47#endif
48
49#ifdef HAVE___SECURE_GETENV
50 return __secure_getenv(arg);
51#else
52 return getenv(arg);
53#endif
54}
55
b49103ed
KZ
56int endswith(const char *s, const char *sx)
57{
58 ssize_t off;
59
60 assert(s);
61 assert(sx);
62
63 off = strlen(s);
64 if (!off)
65 return 0;
66 off -= strlen(sx);
67 if (off < 0)
68 return 0;
69
70 return !strcmp(s + off, sx);
71}
72
73int startswith(const char *s, const char *sx)
74{
75 size_t off;
76
77 assert(s);
78 assert(sx);
79
80 off = strlen(sx);
81 if (!off)
82 return 0;
83
84 return !strncmp(s, sx, off);
85}
86
69b7e41e
KZ
87/**
88 * mnt_fstype_is_pseudofs:
89 * @type: filesystem name
90 *
91 * Returns: 1 for filesystems like proc, sysfs, ... or 0.
92 */
93int mnt_fstype_is_pseudofs(const char *type)
94{
95 if (!type)
96 return 0;
97 if (strcmp(type, "none") == 0 ||
98 strcmp(type, "proc") == 0 ||
99 strcmp(type, "tmpfs") == 0 ||
100 strcmp(type, "sysfs") == 0 ||
101 strcmp(type, "devpts") == 0||
102 strcmp(type, "cgroups") == 0 ||
103 strcmp(type, "devfs") == 0 ||
104 strcmp(type, "dlmfs") == 0 ||
105 strcmp(type, "cpuset") == 0 ||
106 strcmp(type, "spufs") == 0)
107 return 1;
108 return 0;
109}
110
111/**
112 * mnt_fstype_is_netfs:
113 * @type: filesystem name
114 *
115 * Returns: 1 for filesystems like cifs, nfs, ... or 0.
116 */
117int mnt_fstype_is_netfs(const char *type)
118{
119 if (!type)
120 return 0;
121 if (strcmp(type, "cifs") == 0 ||
122 strcmp(type, "smbfs") == 0 ||
123 strncmp(type, "nfs", 3) == 0 ||
124 strcmp(type, "afs") == 0 ||
125 strcmp(type, "ncpfs") == 0)
126 return 1;
127 return 0;
128}
129
abc9c0f7
KZ
130/**
131 * mnt_match_fstype:
132 * @type: filesystem type
133 * @pattern: filesystem name or comma delimitted list of names
134 *
135 * The @pattern list of filesystem can be prefixed with a global
136 * "no" prefix to invert matching of the whole list. The "no" could
3d735589
KZ
137 * also used for individual items in the @pattern list. So,
138 * "nofoo,bar" has the same meaning as "nofoo,nobar".
abc9c0f7 139 *
3d735589
KZ
140 * "bar" : "nofoo,bar" -> False (global "no" prefix)
141 *
142 * "bar" : "foo,bar" -> True
abc9c0f7 143 *
abc9c0f7
KZ
144 * "bar" : "foo,nobar" -> False
145 *
146 * Returns: 1 if type is matching, else 0. This function also returns
147 * 0 if @pattern is NULL and @type is non-NULL.
148 */
149int mnt_match_fstype(const char *type, const char *pattern)
150{
151 int no = 0; /* negated types list */
152 int len;
153 const char *p;
154
155 if (!pattern && !type)
156 return 1;
157 if (!pattern)
158 return 0;
159
160 if (!strncmp(pattern, "no", 2)) {
161 no = 1;
162 pattern += 2;
163 }
164
165 /* Does type occur in types, separated by commas? */
166 len = strlen(type);
167 p = pattern;
168 while(1) {
169 if (!strncmp(p, "no", 2) && !strncmp(p+2, type, len) &&
170 (p[len+2] == 0 || p[len+2] == ','))
171 return 0;
172 if (strncmp(p, type, len) == 0 && (p[len] == 0 || p[len] == ','))
173 return !no;
174 p = strchr(p,',');
175 if (!p)
176 break;
177 p++;
178 }
179 return no;
180}
181
182
183/* Returns 1 if needle found or noneedle not found in haystack
184 * Otherwise returns 0
185 */
186static int check_option(const char *haystack, size_t len,
187 const char *needle, size_t needle_len)
188{
189 const char *p;
190 int no = 0;
191
192 if (needle_len >= 2 && !strncmp(needle, "no", 2)) {
193 no = 1;
194 needle += 2;
195 needle_len -= 2;
196 }
197
198 for (p = haystack; p && p < haystack + len; p++) {
199 char *sep = strchr(p, ',');
200 size_t plen = sep ? sep - p : len - (p - haystack);
201
202 if (plen == needle_len) {
203 if (!strncmp(p, needle, plen))
204 return !no; /* foo or nofoo was found */
205 }
206 p += plen;
207 }
208
209 return no; /* foo or nofoo was not found */
210}
211
212/**
213 * mnt_match_options:
214 * @optstr: options string
215 * @pattern: comma delimitted list of options
216 *
217 * The "no" could used for individual items in the @options list. The "no"
218 * prefix does not have a global meanning.
219 *
220 * Unlike fs type matching, nonetdev,user and nonetdev,nouser have
221 * DIFFERENT meanings; each option is matched explicitly as specified.
222 *
3d735589
KZ
223 * "xxx,yyy,zzz" : "nozzz" -> False
224 *
225 * "xxx,yyy,zzz" : "xxx,noeee" -> True
abc9c0f7
KZ
226 *
227 * Returns: 1 if pattern is matching, else 0. This function also returns 0
228 * if @pattern is NULL and @optstr is non-NULL.
229 */
230int mnt_match_options(const char *optstr, const char *pattern)
231{
232 const char *p;
233 size_t len, optstr_len = 0;
234
235 if (!pattern && !optstr)
236 return 1;
237 if (!pattern)
238 return 0;
239
240 len = strlen(pattern);
241 if (optstr)
242 optstr_len = strlen(optstr);
243
244 for (p = pattern; p < pattern + len; p++) {
245 char *sep = strchr(p, ',');
246 size_t plen = sep ? sep - p : len - (p - pattern);
247
248 if (!plen)
249 continue; /* if two ',' appear in a row */
250
251 if (!check_option(optstr, optstr_len, p, plen))
252 return 0; /* any match failure means failure */
253
254 p += plen;
255 }
256
257 /* no match failures in list means success */
258 return 1;
259}
260
69b7e41e
KZ
261/*
262 * Returns allocated string with username or NULL.
263 */
264char *mnt_get_username(const uid_t uid)
265{
266 struct passwd pwd;
267 struct passwd *res;
268 size_t sz = sysconf(_SC_GETPW_R_SIZE_MAX);
269 char *buf, *username = NULL;
270
271 if (sz <= 0)
272 sz = 16384; /* Should be more than enough */
273
274 buf = malloc(sz);
275 if (!buf)
276 return NULL;
277
278 if (!getpwuid_r(uid, &pwd, buf, sz, &res) && res)
279 username = strdup(pwd.pw_name);
280
281 free(buf);
282 return username;
283}
abc9c0f7 284
0532ba1d
KZ
285/*
286 * Returns 1 if /etc/mtab is a reqular file.
287 */
288int mnt_has_regular_mtab(void)
289{
290 struct stat st;
291
292 if (lstat(_PATH_MOUNTED, &st) == 0 && S_ISREG(st.st_mode))
293 return 1;
294 return 0;
295}
296
d1be0c34
KZ
297/**
298 * mnt_get_writable_mtab_path:
299 *
300 * Returns: pointer to the static string with path to the file with userspace
301 * mount options (classic /etc/mtab or /var/run/mount/mountinfo)
302 */
303const char *mnt_get_writable_mtab_path(void)
304{
305 struct stat mst, ist;
306 int mtab, info;
307
308 mtab = !lstat(_PATH_MOUNTED, &mst);
309 info = !stat(MNT_PATH_RUNDIR, &ist);
310
311 /* A) mtab is symlink, /var/run/mount is available */
312 if (mtab && S_ISLNK(mst.st_mode) && info) {
313 int fd = open(MNT_PATH_MOUNTINFO, O_RDWR | O_CREAT, 0644);
314 if (fd >= 0) {
315 close(fd);
316 return MNT_PATH_MOUNTINFO;
317 }
318 return NULL; /* probably EACCES */
319 }
320
321 /* B) classis system with /etc/mtab */
322 if (mtab && S_ISREG(mst.st_mode)) {
323 int fd = open(_PATH_MOUNTED, O_RDWR, 0644);
324 if (fd >= 0) {
325 close(fd);
326 return _PATH_MOUNTED;
327 }
328 return NULL; /* probably EACCES */
329 }
330
331 return NULL;
332}
333
abc9c0f7
KZ
334#ifdef TEST_PROGRAM
335int test_match_fstype(struct mtest *ts, int argc, char *argv[])
336{
337 char *type = argv[1];
338 char *pattern = argv[2];
339
340 printf("%s\n", mnt_match_fstype(type, pattern) ? "MATCH" : "NOT-MATCH");
341 return 0;
342}
343
344int test_match_options(struct mtest *ts, int argc, char *argv[])
345{
346 char *optstr = argv[1];
347 char *pattern = argv[2];
348
349 printf("%s\n", mnt_match_options(optstr, pattern) ? "MATCH" : "NOT-MATCH");
350 return 0;
351}
352
b49103ed
KZ
353int test_startswith(struct mtest *ts, int argc, char *argv[])
354{
355 char *optstr = argv[1];
356 char *pattern = argv[2];
357
358 printf("%s\n", startswith(optstr, pattern) ? "YES" : "NOT");
359 return 0;
360}
361
362int test_endswith(struct mtest *ts, int argc, char *argv[])
363{
364 char *optstr = argv[1];
365 char *pattern = argv[2];
366
367 printf("%s\n", endswith(optstr, pattern) ? "YES" : "NOT");
368 return 0;
369}
370
abc9c0f7
KZ
371
372int main(int argc, char *argv[])
373{
374 struct mtest tss[] = {
375 { "--match-fstype", test_match_fstype, "<type> <pattern> FS types matching" },
376 { "--match-options", test_match_options, "<options> <pattern> options matching" },
b49103ed
KZ
377 { "--starts-with", test_startswith, "<string> <prefix>" },
378 { "--ends-with", test_endswith, "<string> <prefix>" },
abc9c0f7
KZ
379 { NULL }
380 };
381
382 return mnt_run_test(tss, argc, argv);
383}
384
385#endif /* TEST_PROGRAM */