/*
* Copyright (C) 2008-2009 Karel Zak <kzak@redhat.com>
*
* This file may be redistributed under the terms of the
* GNU Lesser General Public License.
*/
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#ifdef HAVE_SYS_PRCTL_H
#include <sys/prctl.h>
#else
#define PR_GET_DUMPABLE 3
#endif
#if (!defined(HAVE_PRCTL) && defined(linux))
#include <sys/syscall.h>
#endif
#include <sys/stat.h>
#include <ctype.h>
#include <sys/types.h>
#include <fcntl.h>
#include <pwd.h>
#include "mountP.h"
char *mnt_getenv_safe(const char *arg)
{
if ((getuid() != geteuid()) || (getgid() != getegid()))
return NULL;
#if HAVE_PRCTL
if (prctl(PR_GET_DUMPABLE, 0, 0, 0, 0) == 0)
return NULL;
#else
#if (defined(linux) && defined(SYS_prctl))
if (syscall(SYS_prctl, PR_GET_DUMPABLE, 0, 0, 0, 0) == 0)
return NULL;
#endif
#endif
#ifdef HAVE___SECURE_GETENV
return __secure_getenv(arg);
#else
return getenv(arg);
#endif
}
/* TODO: move strn<...> functions to top-level lib/strn.c */
#ifndef HAVE_STRNLEN
size_t strnlen(const char *s, size_t maxlen)
{
int i;
for (i = 0; i < maxlen; i++) {
if (s[i] == '\0')
return i + 1;
}
return maxlen;
}
#endif
#ifndef HAVE_STRNCHR
char *strnchr(const char *s, size_t maxlen, int c)
{
for (; maxlen-- && *s != '\0'; ++s)
if (*s == (char)c)
return (char *)s;
return NULL;
}
#endif
#ifndef HAVE_STRNDUP
char *strndup(const char *s, size_t n)
{
size_t len = strnlen (s, n);
char *new = (char *) malloc (len + 1);
if (new == NULL)
return NULL;
new[len] = '\0';
return (char *) memcpy (new, s, len);
}
#endif
/**
* mnt_fstype_is_pseudofs:
* @type: filesystem name
*
* Returns: 1 for filesystems like proc, sysfs, ... or 0.
*/
int mnt_fstype_is_pseudofs(const char *type)
{
if (!type)
return 0;
if (strcmp(type, "none") == 0 ||
strcmp(type, "proc") == 0 ||
strcmp(type, "tmpfs") == 0 ||
strcmp(type, "sysfs") == 0 ||
strcmp(type, "devpts") == 0||
strcmp(type, "cgroups") == 0 ||
strcmp(type, "devfs") == 0 ||
strcmp(type, "dlmfs") == 0 ||
strcmp(type, "cpuset") == 0 ||
strcmp(type, "spufs") == 0)
return 1;
return 0;
}
/**
* mnt_fstype_is_netfs:
* @type: filesystem name
*
* Returns: 1 for filesystems like cifs, nfs, ... or 0.
*/
int mnt_fstype_is_netfs(const char *type)
{
if (!type)
return 0;
if (strcmp(type, "cifs") == 0 ||
strcmp(type, "smbfs") == 0 ||
strncmp(type, "nfs", 3) == 0 ||
strcmp(type, "afs") == 0 ||
strcmp(type, "ncpfs") == 0)
return 1;
return 0;
}
/**
* mnt_match_fstype:
* @type: filesystem type
* @pattern: filesystem name or comma delimitted list of names
*
* The @pattern list of filesystem can be prefixed with a global
* "no" prefix to invert matching of the whole list. The "no" could
* also used for individual items in the @pattern list.
*
* "nofoo,bar" has the same meaning as "nofoo,nobar"
*
* "bar" : "nofoo,bar" -> False (global "no" prefix)
* "bar" : "foo,bar" -> True
* "bar" : "foo,nobar" -> False
*
* Returns: 1 if type is matching, else 0. This function also returns
* 0 if @pattern is NULL and @type is non-NULL.
*/
int mnt_match_fstype(const char *type, const char *pattern)
{
int no = 0; /* negated types list */
int len;
const char *p;
if (!pattern && !type)
return 1;
if (!pattern)
return 0;
if (!strncmp(pattern, "no", 2)) {
no = 1;
pattern += 2;
}
/* Does type occur in types, separated by commas? */
len = strlen(type);
p = pattern;
while(1) {
if (!strncmp(p, "no", 2) && !strncmp(p+2, type, len) &&
(p[len+2] == 0 || p[len+2] == ','))
return 0;
if (strncmp(p, type, len) == 0 && (p[len] == 0 || p[len] == ','))
return !no;
p = strchr(p,',');
if (!p)
break;
p++;
}
return no;
}
/* Returns 1 if needle found or noneedle not found in haystack
* Otherwise returns 0
*/
static int check_option(const char *haystack, size_t len,
const char *needle, size_t needle_len)
{
const char *p;
int no = 0;
if (needle_len >= 2 && !strncmp(needle, "no", 2)) {
no = 1;
needle += 2;
needle_len -= 2;
}
for (p = haystack; p && p < haystack + len; p++) {
char *sep = strchr(p, ',');
size_t plen = sep ? sep - p : len - (p - haystack);
if (plen == needle_len) {
if (!strncmp(p, needle, plen))
return !no; /* foo or nofoo was found */
}
p += plen;
}
return no; /* foo or nofoo was not found */
}
/**
* mnt_match_options:
* @optstr: options string
* @pattern: comma delimitted list of options
*
* The "no" could used for individual items in the @options list. The "no"
* prefix does not have a global meanning.
*
* Unlike fs type matching, nonetdev,user and nonetdev,nouser have
* DIFFERENT meanings; each option is matched explicitly as specified.
*
* xxx,yyy,zzz : nozzz -> False
* xxx,yyy,zzz : xxx,noeee -> True
*
* Returns: 1 if pattern is matching, else 0. This function also returns 0
* if @pattern is NULL and @optstr is non-NULL.
*/
int mnt_match_options(const char *optstr, const char *pattern)
{
const char *p;
size_t len, optstr_len = 0;
if (!pattern && !optstr)
return 1;
if (!pattern)
return 0;
len = strlen(pattern);
if (optstr)
optstr_len = strlen(optstr);
for (p = pattern; p < pattern + len; p++) {
char *sep = strchr(p, ',');
size_t plen = sep ? sep - p : len - (p - pattern);
if (!plen)
continue; /* if two ',' appear in a row */
if (!check_option(optstr, optstr_len, p, plen))
return 0; /* any match failure means failure */
p += plen;
}
/* no match failures in list means success */
return 1;
}
/*
* Reallocates its first arg @s - typical use: s = mnt_strconcat3(s,t,u);
* Returns reallocated @s ion succes or NULL in case of error.
*/
char *mnt_strconcat3(char *s, const char *t, const char *u)
{
size_t len = 0;
len = (s ? strlen(s) : 0) + (t ? strlen(t) : 0) + (u ? strlen(u) : 0);
if (!len)
return NULL;
if (!s) {
s = malloc(len + 1);
*s = '\0';
} else
s = realloc(s, len + 1);
if (!s)
return NULL;
if (t)
strcat(s, t);
if (u)
strcat(s, u);
return s;
}
/**
* mnt_open_device:
* @devname: device path
* @flags: open(2) flags
*
* Opens device like open(2), but waits for cdrom medium (if errno=ENOMEDIUM).
*
* Returns: file descriptor or -1 in case of error.
*/
int mnt_open_device(const char *devname, int flags)
{
int retries = 0;
do {
int fd = open(devname, flags);
if (fd >= 0)
return fd;
if (errno != ENOMEDIUM)
break;
if (retries >= CONFIG_CDROM_NOMEDIUM_RETRIES)
break;
++retries;
sleep(3);
} while(1);
return -1;
}
/*
* Returns allocated string with username or NULL.
*/
char *mnt_get_username(const uid_t uid)
{
struct passwd pwd;
struct passwd *res;
size_t sz = sysconf(_SC_GETPW_R_SIZE_MAX);
char *buf, *username = NULL;
if (sz <= 0)
sz = 16384; /* Should be more than enough */
buf = malloc(sz);
if (!buf)
return NULL;
if (!getpwuid_r(uid, &pwd, buf, sz, &res) && res)
username = strdup(pwd.pw_name);
free(buf);
return username;
}
#ifdef TEST_PROGRAM
int test_match_fstype(struct mtest *ts, int argc, char *argv[])
{
char *type = argv[1];
char *pattern = argv[2];
printf("%s\n", mnt_match_fstype(type, pattern) ? "MATCH" : "NOT-MATCH");
return 0;
}
int test_match_options(struct mtest *ts, int argc, char *argv[])
{
char *optstr = argv[1];
char *pattern = argv[2];
printf("%s\n", mnt_match_options(optstr, pattern) ? "MATCH" : "NOT-MATCH");
return 0;
}
int main(int argc, char *argv[])
{
struct mtest tss[] = {
{ "--match-fstype", test_match_fstype, "<type> <pattern> FS types matching" },
{ "--match-options", test_match_options, "<options> <pattern> options matching" },
{ NULL }
};
return mnt_run_test(tss, argc, argv);
}
#endif /* TEST_PROGRAM */