#include "lint.h"
#include <mtlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mnttab.h>
#include <sys/mntio.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <stdlib.h>
#include <thread.h>
#include <synch.h>
#include <libc.h>
#include <unistd.h>
#include "tsd.h"
#include <atomic.h>
#include <strings.h>
static int getmntent_compat(FILE *fp, struct mnttab *mp);
#define GETTOK_R(xx, ll, tmp)\
if ((mp->xx = (char *)strtok_r(ll, sepstr, tmp)) == NULL)\
return (MNT_TOOFEW);\
if (strcmp(mp->xx, dash) == 0)\
mp->xx = NULL
#define DIFF(xx)\
(mrefp->xx != NULL && (mgetp->xx == NULL ||\
strcmp(mrefp->xx, mgetp->xx) != 0))
#define SDIFF(xx, typem, typer)\
((mgetp->xx == NULL) || (stat64(mgetp->xx, &statb) == -1) ||\
((statb.st_mode & S_IFMT) != typem) ||\
(statb.st_rdev != typer))
static const char sepstr[] = " \t\n";
static const char dash[] = "-";
typedef struct {
size_t buflen;
char *buf;
} thread_data_t;
static void
destroy_thread_data(void *arg)
{
thread_data_t *thread_data = arg;
if (thread_data->buf != NULL) {
free(thread_data->buf);
thread_data->buf = NULL;
}
thread_data->buflen = 0;
}
static char *
getmntbuf(size_t size)
{
thread_data_t *thread_data;
thread_data = tsdalloc(_T_GETMNTENT,
sizeof (thread_data_t), destroy_thread_data);
if (thread_data == NULL)
return (NULL);
if (thread_data->buf == NULL ||
thread_data->buflen < size) {
if (thread_data->buf != NULL)
free(thread_data->buf);
thread_data->buflen = 0;
if ((thread_data->buf = malloc(size)) == NULL)
return (NULL);
thread_data->buflen = size;
}
return (thread_data->buf);
}
static int
getmntany_compat(FILE *fp, struct mnttab *mgetp, struct mnttab *mrefp)
{
int ret, bstat;
mode_t bmode;
dev_t brdev;
struct stat64 statb;
if (mrefp->mnt_special && mrefp->mnt_special[0] == '/' &&
stat64(mrefp->mnt_special, &statb) == 0 &&
((bmode = (statb.st_mode & S_IFMT)) == S_IFBLK ||
bmode == S_IFCHR)) {
bstat = 1;
brdev = statb.st_rdev;
} else {
bstat = 0;
}
while ((ret = getmntent_compat(fp, mgetp)) == 0 &&
((bstat == 0 && DIFF(mnt_special)) ||
(bstat == 1 && SDIFF(mnt_special, bmode, brdev)) ||
DIFF(mnt_mountp) ||
DIFF(mnt_fstype) ||
DIFF(mnt_mntopts) ||
DIFF(mnt_time)))
;
return (ret);
}
int
getmntany(FILE *fp, struct mnttab *mgetp, struct mnttab *mrefp)
{
struct mntentbuf embuf;
char *copyp, *bufp;
int ret;
if ((copyp = bufp = getmntbuf(MNT_LINE_MAX)) == NULL) {
errno = ENOMEM;
return (-1);
}
bzero(mgetp, sizeof (struct mnttab));
if (mrefp->mnt_special) {
mgetp->mnt_special = copyp;
copyp += snprintf(mgetp->mnt_special, MNT_LINE_MAX, "%s",
mrefp->mnt_special) + 1;
}
if (mrefp->mnt_mountp) {
mgetp->mnt_mountp = copyp;
copyp += snprintf(mgetp->mnt_mountp,
bufp + MNT_LINE_MAX - copyp, "%s", mrefp->mnt_mountp) + 1;
}
if (mrefp->mnt_fstype) {
mgetp->mnt_fstype = copyp;
copyp += snprintf(mgetp->mnt_fstype,
bufp + MNT_LINE_MAX - copyp, "%s", mrefp->mnt_fstype) + 1;
}
if (mrefp->mnt_mntopts) {
mgetp->mnt_mntopts = copyp;
copyp += snprintf(mgetp->mnt_mntopts,
bufp + MNT_LINE_MAX - copyp, "%s", mrefp->mnt_mntopts) + 1;
}
if (mrefp->mnt_time) {
mgetp->mnt_time = copyp;
(void) snprintf(mgetp->mnt_time, bufp + MNT_LINE_MAX - copyp,
"%s", mrefp->mnt_time);
}
embuf.mbuf_emp = (struct extmnttab *)mgetp;
embuf.mbuf_bufsize = MNT_LINE_MAX;
embuf.mbuf_buf = bufp;
switch (ret = ioctl(fileno(fp), MNTIOC_GETMNTANY, &embuf)) {
case 0:
return (0);
case MNTFS_EOF:
return (-1);
case MNTFS_TOOLONG:
return (MNT_TOOLONG);
default:
if (errno == ENOTTY)
return (getmntany_compat(fp, mgetp, mrefp));
else
return (ret);
}
}
static int
getmntent_common(FILE *fp, struct extmnttab *emp, int command)
{
struct mntentbuf embuf;
static size_t bufsize = MNT_LINE_MAX;
int ret;
embuf.mbuf_emp = emp;
embuf.mbuf_bufsize = bufsize;
if ((embuf.mbuf_buf = getmntbuf(embuf.mbuf_bufsize)) == NULL) {
errno = ENOMEM;
return (-1);
}
while ((ret = ioctl(fileno(fp), command, &embuf)) == MNTFS_TOOLONG) {
(void) atomic_swap_ulong((unsigned long *)&bufsize,
2 * embuf.mbuf_bufsize);
embuf.mbuf_bufsize = bufsize;
if ((embuf.mbuf_buf = getmntbuf(embuf.mbuf_bufsize)) == NULL) {
errno = ENOMEM;
return (-1);
}
}
switch (ret) {
case 0:
if (command == MNTIOC_GETMNTENT &&
(emp->mnt_time + strlen(emp->mnt_time) + 1 -
emp->mnt_special > MNT_LINE_MAX))
return (MNT_TOOLONG);
else
return (0);
case MNTFS_EOF:
return (-1);
default:
if (command == MNTIOC_GETMNTENT)
return (getmntent_compat(fp, (struct mnttab *)emp));
else
return (ret);
}
}
int
getmntent(FILE *fp, struct mnttab *mp)
{
return (getmntent_common(fp, (struct extmnttab *)mp, MNTIOC_GETMNTENT));
}
int
getextmntent(FILE *fp, struct extmnttab *emp, size_t len __unused)
{
return (getmntent_common(fp, emp, MNTIOC_GETEXTMNTENT));
}
char *
mntopt(char **p)
{
char *cp = *p;
char *retstr;
while (*cp && isspace(*cp))
cp++;
retstr = cp;
while (*cp && *cp != ',')
cp++;
if (*cp) {
*cp = '\0';
cp++;
}
*p = cp;
return (retstr);
}
char *
hasmntopt(const struct mnttab *mnt, const char *opt)
{
char tmpopts[MNT_LINE_MAX];
char *f, *opts = tmpopts;
size_t len;
if (mnt->mnt_mntopts == NULL)
return (NULL);
(void) strcpy(opts, mnt->mnt_mntopts);
len = strlen(opt);
f = mntopt(&opts);
for (; *f; f = mntopt(&opts)) {
if (strncmp(opt, f, len) == 0 &&
(f[len] == '\0' || !isalnum(f[len])))
return (f - tmpopts + mnt->mnt_mntopts);
}
return (NULL);
}
void
resetmnttab(FILE *fp)
{
rewind(fp);
}
static int
getaline(char *lp, FILE *fp)
{
char *cp;
while ((lp = fgets(lp, MNT_LINE_MAX, fp)) != NULL) {
if (strlen(lp) == MNT_LINE_MAX-1 && lp[MNT_LINE_MAX-2] != '\n')
return (MNT_TOOLONG);
for (cp = lp; *cp == ' ' || *cp == '\t'; cp++)
;
if (*cp != '#' && *cp != '\n')
return (0);
}
return (-1);
}
static int
getmntent_compat(FILE *fp, struct mnttab *mp)
{
int ret;
char *tmp;
char *line = getmntbuf(MNT_LINE_MAX);
if (line == NULL) {
errno = ENOMEM;
return (-1);
}
if ((ret = getaline(line, fp)) != 0)
return (ret);
GETTOK_R(mnt_special, line, &tmp);
GETTOK_R(mnt_mountp, NULL, &tmp);
GETTOK_R(mnt_fstype, NULL, &tmp);
GETTOK_R(mnt_mntopts, NULL, &tmp);
GETTOK_R(mnt_time, NULL, &tmp);
if (strtok_r(NULL, sepstr, &tmp) != NULL)
return (MNT_TOOMANY);
return (0);
}