/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
/*
* University Copyright- Copyright (c) 1982, 1986, 1988
* The Regents of the University of California
* All Rights Reserved
*
* University Acknowledgment- Portions of this document are derived from
* software developed by the University of California, Berkeley, and its
* contributors.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* Turn quota on/off for a filesystem.
*/
#include <sys/param.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mntent.h>
#define bcopy(f, t, n) memcpy(t, f, n)
#define bzero(s, n) memset(s, 0, n)
#define bcmp(s, d, n) memcmp(s, d, n)
#define index(s, r) strchr(s, r)
#define rindex(s, r) strrchr(s, r)
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/file.h>
#include <signal.h>
#include <fcntl.h>
#include <sys/fs/ufs_quota.h>
#include <stdio.h>
#include <sys/mnttab.h>
#include <errno.h>
#include <sys/vfstab.h>
int vflag; /* verbose */
int aflag; /* all file systems */
#define QFNAME "quotas"
#define CHUNK 50
char **listbuf;
char *mntopt(), *hasvfsopt(), *hasmntopt();
char *whoami;
static void fixmntent();
static void mnterror();
static void usage(char *);
static int oneof();
static int quotaonoff();
static int quotactl(int, char *, uid_t, caddr_t);
extern int optind;
extern char *optarg;
int
main(int argc, char **argv)
{
struct mnttab mntp;
struct vfstab vfsbuf;
char **listp;
int listcnt;
FILE *mtab, *vfstab, *tmp;
int offmode = 0;
int listmax = 0;
int errs = 0;
char *tmpname = "/etc/mnttab.temp";
int status;
int opt;
mode_t oldumask;
struct stat statbuf;
whoami = (char *)rindex(*argv, '/') + 1;
if (whoami == (char *)1)
whoami = *argv;
if (strcmp(whoami, "quotaoff") == 0)
offmode++;
else if (strcmp(whoami, "quotaon") != 0) {
fprintf(stderr, "Name must be quotaon or quotaoff not %s\n",
whoami);
exit(31+1);
}
if ((listbuf = (char **)malloc(sizeof (char *) * CHUNK)) == NULL) {
fprintf(stderr, "Can't alloc lisbuf array.");
exit(31+1);
}
listmax = CHUNK;
while ((opt = getopt(argc, argv, "avV")) != EOF) {
switch (opt) {
case 'v':
vflag++;
break;
case 'a':
aflag++;
break;
case 'V': /* Print command line */
{
char *opt_text;
int opt_cnt;
(void) fprintf(stdout, "%s -F UFS ", whoami);
for (opt_cnt = 1; opt_cnt < argc; opt_cnt++) {
opt_text = argv[opt_cnt];
if (opt_text)
(void) fprintf(stdout, " %s ",
opt_text);
}
(void) fprintf(stdout, "\n");
}
break;
case '?':
usage(whoami);
}
}
if (argc <= optind && !aflag) {
usage(whoami);
}
/*
* If aflag go through vfstab and make a list of appropriate
* filesystems.
*/
if (aflag) {
listp = listbuf;
listcnt = 0;
vfstab = fopen(VFSTAB, "r");
if (vfstab == NULL) {
fprintf(stderr, "Can't open %s\n", VFSTAB);
perror(VFSTAB);
exit(31+1);
}
while ((status = getvfsent(vfstab, &vfsbuf)) == NULL) {
if (strcmp(vfsbuf.vfs_fstype, MNTTYPE_UFS) != 0 ||
(vfsbuf.vfs_mntopts == 0) ||
hasvfsopt(&vfsbuf, MNTOPT_RO) ||
(!hasvfsopt(&vfsbuf, MNTOPT_RQ) &&
!hasvfsopt(&vfsbuf, MNTOPT_QUOTA)))
continue;
*listp = malloc(strlen(vfsbuf.vfs_special) + 1);
strcpy(*listp, vfsbuf.vfs_special);
listp++;
listcnt++;
/* grow listbuf if needed */
if (listcnt >= listmax) {
listmax += CHUNK;
listbuf = (char **)realloc(listbuf,
sizeof (char *) * listmax);
if (listbuf == NULL) {
fprintf(stderr,
"Can't grow listbuf.\n");
exit(31+1);
}
listp = &listbuf[listcnt];
}
}
fclose(vfstab);
*listp = (char *)0;
listp = listbuf;
} else {
listp = &argv[optind];
listcnt = argc - optind;
}
/*
* Open real mnttab
*/
mtab = fopen(MNTTAB, "r");
if (mtab == NULL) {
fprintf(stderr, "Can't open %s\n", MNTTAB);
perror(whoami);
exit(31+1);
}
/* check every entry for validity before we change mnttab */
while ((status = getmntent(mtab, &mntp)) == 0)
;
if (status > 0)
mnterror(status);
rewind(mtab);
signal(SIGHUP, SIG_IGN);
signal(SIGQUIT, SIG_IGN);
signal(SIGINT, SIG_IGN);
/*
* Loop through mnttab, if a file system gets turned on or off
* do the quota call.
*/
while ((status = getmntent(mtab, &mntp)) == NULL) {
if (strcmp(mntp.mnt_fstype, MNTTYPE_UFS) == 0 &&
!hasmntopt(&mntp, MNTOPT_RO) &&
(oneof(mntp.mnt_special, listp, listcnt) ||
oneof(mntp.mnt_mountp, listp, listcnt))) {
errs += quotaonoff(&mntp, offmode);
}
}
fclose(mtab);
while (listcnt--) {
if (*listp) {
fprintf(stderr, "Cannot do %s\n", *listp);
errs++;
}
listp++;
}
if (errs > 0)
errs += 31;
return (errs);
}
int
quotaonoff(struct mnttab *mntp, int offmode)
{
if (offmode) {
if (quotactl(Q_QUOTAOFF, mntp->mnt_mountp, (uid_t)0, NULL) < 0)
goto bad;
if (vflag)
printf("%s: quotas turned off\n", mntp->mnt_mountp);
} else {
if (quotactl(Q_QUOTAON, mntp->mnt_mountp, (uid_t)0, NULL) <
0)
goto bad;
if (vflag)
printf("%s: quotas turned on\n", mntp->mnt_mountp);
}
return (0);
bad:
fprintf(stderr, "quotactl: ");
perror(mntp->mnt_special);
return (1);
}
int
oneof(char *target, char **olistp, int on)
{
int n = on;
char **listp = olistp;
while (n--) {
if (*listp && strcmp(target, *listp) == 0) {
*listp = (char *)0;
return (1);
}
listp++;
}
return (0);
}
void
usage(char *whoami)
{
fprintf(stderr, "ufs usage:\n");
fprintf(stderr, "\t%s [-v] -a\n", whoami);
fprintf(stderr, "\t%s [-v] filesys ...\n", whoami);
exit(31+1);
}
int
quotactl(int cmd, char *mountpt, uid_t uid, caddr_t addr)
{
int fd;
int status;
struct quotctl quota;
char qfile[MAXPATHLEN];
if (mountpt == NULL || mountpt[0] == '\0') {
errno = ENOENT;
return (-1);
}
if ((strlcpy(qfile, mountpt, sizeof (qfile)) >= sizeof (qfile)) ||
(strlcat(qfile, "/" QFNAME, sizeof (qfile)) >= sizeof (qfile))) {
errno = ENOENT;
return (-1);
}
if ((fd = open64(qfile, O_RDWR)) < 0) {
fprintf(stderr, "quotactl: %s ", qfile);
perror("open");
exit(31+1);
}
quota.op = cmd;
quota.uid = uid;
quota.addr = addr;
status = ioctl(fd, Q_QUOTACTL, &quota);
close(fd);
return (status);
}
char *
hasvfsopt(struct vfstab *vfs, char *opt)
{
char *f, *opts;
static char *tmpopts;
if (tmpopts == 0) {
tmpopts = (char *)calloc(256, sizeof (char));
if (tmpopts == 0)
return (0);
}
strcpy(tmpopts, vfs->vfs_mntopts);
opts = tmpopts;
f = mntopt(&opts);
for (; *f; f = mntopt(&opts)) {
if (strncmp(opt, f, strlen(opt)) == 0)
return (f - tmpopts + vfs->vfs_mntopts);
}
return (NULL);
}
void
mnterror(int flag)
{
switch (flag) {
case MNT_TOOLONG:
fprintf(stderr, "%s: line in mnttab exceeds %d characters\n",
whoami, MNT_LINE_MAX-2);
break;
case MNT_TOOFEW:
fprintf(stderr, "%s: line in mnttab has too few entries\n",
whoami);
break;
case MNT_TOOMANY:
fprintf(stderr, "%s: line in mnttab has too many entries\n",
whoami);
break;
}
exit(1);
}