Update the quotafile library to manage both active quotas via the

quotactl(2) interface and inactive quotas by accessing the quota
files directly.

Update the edquota program to use this new interface as proof of
concept.
This commit is contained in:
Kirk McKusick 2009-02-14 08:08:08 +00:00
parent a88984f248
commit 8bd6a3ab4b
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/projects/quota64/; revision=188604
4 changed files with 359 additions and 205 deletions

View File

@ -143,12 +143,11 @@ int pidfile_remove(struct pidfh *pfh);
#ifdef _UFS_UFS_QUOTA_H_
struct quotafile;
struct fstab;
struct quotafile *quota_open(const char *);
struct quotafile *quota_create(const char *);
struct quotafile *quota_open(struct fstab *, int, int);
void quota_close(struct quotafile *);
int quota_read(struct quotafile *, struct dqblk *, int);
int quota_write(struct quotafile *, const struct dqblk *, int);
int hasquota(struct fstab *, int, char *, int);
int quota_write_limits(struct quotafile *, struct dqblk *, int);
int quota_write_usage(struct quotafile *, struct dqblk *, int);
#endif
__END_DECLS

View File

@ -25,35 +25,132 @@
.\"
.\" $FreeBSD$
.\"
.Dd November 4, 2008
.Dd February 14, 2009
.Dt QUOTAFILE 3
.Os
.Sh NAME
.Nm quota_open
.Nm quota_create
.Nm quota_read
.Nm quota_write
.Nm quota_write_limits
.Nm quota_write_usage
.Nm quota_close
.Nd "Manipulate quota files"
.Nd "Manipulate quotas"
.Sh LIBRARY
.Lb libutil
.Sh SYNOPSIS
.In ufs/ufs/quota.h
.In libutil.h
.In fstab.h
.Ft "struct quotafile *"
.Fn quota_open "const char *path"
.Ft "struct quotafile *"
.Fn quota_create "const char *path"
.Fn quota_open "struct fstab *fs" "int quotatype" "int openflags"
.Ft int
.Fn quota_read "struct quotafile *qf" "struct dqblk *dqb" "int id"
.Ft int
.Fn quota_write "struct quotafile *qf" "const struct dqblk *dqb" "int id"
.Fn quota_write_limits "struct quotafile *qf" "struct dqblk *dqb" "int id"
.Ft int
.Fn quota_write_usage "struct quotafile *qf" "struct dqblk *dqb" "int id"
.Ft int
.Fn quota_close "struct quotafile *qf"
.Ft int
.Fn hasquota "struct fstab *fs" "int type" "char **qfnamep"
.Sh DESCRIPTION
These functions are designed to simplify access to filesystem quotas.
If quotas are active on a filesystem,
these functions will access them directly from the kernel using the
.Fn quotactl
system call.
If quotas are not active,
these functions will access them by reading and writing
the quota files directly.
.Pp
The
.Fn quota_open
function takes a pointer to an
.Vt fstab
entry corresponding to the filesystem on which quotas
are to be accessed.
The
.Va quotatype
field indicates the type of quotas being sought, either
.Dv USRQUOTA
or
.Dv GRPQUOTA .
The
.Va openflags
are those used by the
.Fn open
system call, usually either
.Dv O_RDONLY
if the quotas are just to be read, or
.Dv O_RDWR
if the quotas are to be updated.
The
.Dv O_CREAT
flag should be specified if a new quota file of the requested type should
be created if it does not already exist.
.Pp
The
.Fn quota_read
function reads the quota from the filesystem and quota type referenced by
.Va qf
for the user (or group) specified by
.Va id
into the
.Vt dqblk
quota structure pointed to by
.Va dqb .
.Pp
The
.Fn quota_write_limits
function updates the limit fields (but not the usage fields)
for the filesystem and quota type referenced by
.Va qf
for the user (or group) specified by
.Va id
from the
.Vt dqblk
quota structure pointed to by
.Va dqb .
.Pp
The
.Fn quota_write_usage
function updates the usage fields (but not the limit fields)
for the filesystem and quota type referenced by
.Va qf
for the user (or group) specified by
.Va id
from the
.Vt dqblk
quota structure pointed to by
.Va dqb .
.Pp
The
.Fn quota_close
function closes any open file descriptors and frees any storage
associated with the filesystem and quota type referenced by
.Va qf .
.Sh RETURN VALUES
If the filesystem has quotas associated with it,
.Fn quota_open
returns a pointer to a
.Vt quotafile
structure used in subsequent quota access calls.
If the filesystem has no quotas, or access permission is denied
.Dv NULL
is returned and
.Va errno
is set to indicate the cause of failure.
.Pp
The
.Fn quota_read ,
.Fn quota_write_limits ,
.Fn quota_write_usage ,
and
.Fn quota_close
functions return zero on success.
On error they return
.Dv -1
and set
.Va errno
to indicate the cause of failure.
.Sh SEE ALSO
.Xr quotactl 2 ,
.Xr quota.user 5 ,
@ -68,4 +165,6 @@ functions first appeared in
The
.Nm
functions and this manual page were written by
.An Dag-Erling Sm\(/orgrav Aq des@FreeBSD.org .
.An Dag-Erling Sm\(/orgrav Aq des@FreeBSD.org
and
.An Marshall Kirk McKusick .

View File

@ -1,5 +1,6 @@
/*-
* Copyright (c) 2008 Dag-Erling Coïdan Smørgrav
* Copyright (c) 2008 Marshall Kirk McKusick
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -47,76 +48,132 @@
#include <unistd.h>
struct quotafile {
int fd;
int type; /* 32 or 64 */
int fd; /* -1 means using quotactl for access */
int wordsize; /* 32-bit or 64-bit limits */
int quotatype; /* USRQUOTA or GRPQUOTA */
char fsname[MAXPATHLEN + 1]; /* mount point of filesystem */
char qfname[MAXPATHLEN + 1]; /* quota file if not using quotactl */
};
static const char *qfextension[] = INITQFNAMES;
struct quotafile *
quota_open(const char *fn)
/*
* Check to see if a particular quota is to be enabled.
*/
static int
hasquota(struct fstab *fs, int type, char *qfnamep, int qfbufsize)
{
struct quotafile *qf;
struct dqhdr64 dqh;
int serrno;
char *opt;
char *cp;
struct statfs sfb;
char buf[BUFSIZ];
static char initname, usrname[100], grpname[100];
if ((qf = calloc(1, sizeof(*qf))) == NULL)
return (NULL);
if ((qf->fd = open(fn, O_RDWR)) < 0) {
serrno = errno;
free(qf);
errno = serrno;
return (NULL);
if (!initname) {
(void)snprintf(usrname, sizeof(usrname), "%s%s",
qfextension[USRQUOTA], QUOTAFILENAME);
(void)snprintf(grpname, sizeof(grpname), "%s%s",
qfextension[GRPQUOTA], QUOTAFILENAME);
initname = 1;
}
qf->type = 32;
switch (read(qf->fd, &dqh, sizeof(dqh))) {
case -1:
serrno = errno;
close(qf->fd);
free(qf);
errno = serrno;
return (NULL);
case sizeof(dqh):
if (strcmp(dqh.dqh_magic, Q_DQHDR64_MAGIC) != 0) {
/* no magic, assume 32 bits */
qf->type = 32;
return (qf);
}
if (be32toh(dqh.dqh_version) != Q_DQHDR64_VERSION ||
be32toh(dqh.dqh_hdrlen) != sizeof(struct dqhdr64) ||
be32toh(dqh.dqh_reclen) != sizeof(struct dqblk64)) {
/* correct magic, wrong version / lengths */
close(qf->fd);
free(qf);
errno = EINVAL;
return (NULL);
}
qf->type = 64;
return (qf);
default:
qf->type = 32;
return (qf);
strcpy(buf, fs->fs_mntops);
for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) {
if ((cp = index(opt, '=')))
*cp++ = '\0';
if (type == USRQUOTA && strcmp(opt, usrname) == 0)
break;
if (type == GRPQUOTA && strcmp(opt, grpname) == 0)
break;
}
/* not reached */
if (!opt)
return (0);
/*
* Ensure that the filesystem is mounted.
*/
if (statfs(fs->fs_file, &sfb) != 0 ||
strcmp(fs->fs_file, sfb.f_mntonname)) {
return (0);
}
if (cp) {
strncpy(qfnamep, cp, qfbufsize);
} else {
(void)snprintf(qfnamep, qfbufsize, "%s/%s.%s", fs->fs_file,
QUOTAFILENAME, qfextension[type]);
}
return (1);
}
struct quotafile *
quota_create(const char *fn)
quota_open(struct fstab *fs, int quotatype, int openflags)
{
struct quotafile *qf;
struct dqhdr64 dqh;
struct group *grp;
int serrno;
int qcmd, serrno;
if ((qf = calloc(1, sizeof(*qf))) == NULL)
return (NULL);
if ((qf->fd = open(fn, O_RDWR|O_CREAT|O_TRUNC, 0)) < 0) {
qf->quotatype = quotatype;
strncpy(qf->fsname, fs->fs_file, sizeof(qf->fsname));
qcmd = QCMD(Q_GETQUOTA, quotatype);
if (quotactl(fs->fs_file, qcmd, 0, &dqh) == 0) {
qf->wordsize = 64;
qf->fd = -1;
return (qf);
}
if (!hasquota(fs, quotatype, qf->qfname, sizeof(qf->qfname))) {
free(qf);
errno = EOPNOTSUPP;
return (NULL);
}
if ((qf->fd = open(qf->qfname, openflags & O_ACCMODE)) < 0 &&
(openflags & O_CREAT) == 0) {
serrno = errno;
free(qf);
errno = serrno;
return (NULL);
}
qf->type = 64;
/* File open worked, so process it */
if (qf->fd != -1) {
qf->wordsize = 32;
switch (read(qf->fd, &dqh, sizeof(dqh))) {
case -1:
serrno = errno;
close(qf->fd);
free(qf);
errno = serrno;
return (NULL);
case sizeof(dqh):
if (strcmp(dqh.dqh_magic, Q_DQHDR64_MAGIC) != 0) {
/* no magic, assume 32 bits */
qf->wordsize = 32;
return (qf);
}
if (be32toh(dqh.dqh_version) != Q_DQHDR64_VERSION ||
be32toh(dqh.dqh_hdrlen) != sizeof(struct dqhdr64) ||
be32toh(dqh.dqh_reclen) != sizeof(struct dqblk64)) {
/* correct magic, wrong version / lengths */
close(qf->fd);
free(qf);
errno = EINVAL;
return (NULL);
}
qf->wordsize = 64;
return (qf);
default:
qf->wordsize = 32;
return (qf);
}
/* not reached */
}
/* Open failed above, but O_CREAT specified, so create a new file */
if ((qf->fd = open(qf->qfname, O_RDWR|O_CREAT|O_TRUNC, 0)) < 0) {
serrno = errno;
free(qf);
errno = serrno;
return (NULL);
}
qf->wordsize = 64;
memset(&dqh, 0, sizeof(dqh));
memcpy(dqh.dqh_magic, Q_DQHDR64_MAGIC, sizeof(dqh.dqh_magic));
dqh.dqh_version = htobe32(Q_DQHDR64_VERSION);
@ -124,7 +181,7 @@ quota_create(const char *fn)
dqh.dqh_reclen = htobe32(sizeof(struct dqblk64));
if (write(qf->fd, &dqh, sizeof(dqh)) != sizeof(dqh)) {
serrno = errno;
unlink(fn);
unlink(qf->qfname);
close(qf->fd);
free(qf);
errno = serrno;
@ -140,7 +197,8 @@ void
quota_close(struct quotafile *qf)
{
close(qf->fd);
if (qf->fd != -1)
close(qf->fd);
free(qf);
}
@ -203,8 +261,13 @@ quota_read64(struct quotafile *qf, struct dqblk *dqb, int id)
int
quota_read(struct quotafile *qf, struct dqblk *dqb, int id)
{
int qcmd;
switch (qf->type) {
if (qf->fd == -1) {
qcmd = QCMD(Q_GETQUOTA, qf->quotatype);
return (quotactl(qf->fsname, qcmd, id, dqb));
}
switch (qf->wordsize) {
case 32:
return quota_read32(qf, dqb, id);
case 64:
@ -236,7 +299,9 @@ quota_write32(struct quotafile *qf, const struct dqblk *dqb, int id)
off = id * sizeof(struct dqblk32);
if (lseek(qf->fd, off, SEEK_SET) == -1)
return (-1);
return (write(qf->fd, &dqb32, sizeof(dqb32)) == sizeof(dqb32));
if (write(qf->fd, &dqb32, sizeof(dqb32)) == sizeof(dqb32))
return (0);
return (-1);
}
static int
@ -257,14 +322,98 @@ quota_write64(struct quotafile *qf, const struct dqblk *dqb, int id)
off = sizeof(struct dqhdr64) + id * sizeof(struct dqblk64);
if (lseek(qf->fd, off, SEEK_SET) == -1)
return (-1);
return (write(qf->fd, &dqb64, sizeof(dqb64)) == sizeof(dqb64));
if (write(qf->fd, &dqb64, sizeof(dqb64)) == sizeof(dqb64))
return (0);
return (-1);
}
int
quota_write(struct quotafile *qf, const struct dqblk *dqb, int id)
quota_write_usage(struct quotafile *qf, struct dqblk *dqb, int id)
{
struct dqblk dqbuf;
int qcmd;
switch (qf->type) {
if (qf->fd == -1) {
qcmd = QCMD(Q_SETUSE, qf->quotatype);
return (quotactl(qf->fsname, qcmd, id, dqb));
}
/*
* Have to do read-modify-write of quota in file.
*/
if (quota_read(qf, &dqbuf, id) != 0)
return (-1);
/*
* Reset time limit if have a soft limit and were
* previously under it, but are now over it.
*/
if (dqbuf.dqb_bsoftlimit && id != 0 &&
dqbuf.dqb_curblocks < dqbuf.dqb_bsoftlimit &&
dqb->dqb_curblocks >= dqbuf.dqb_bsoftlimit)
dqbuf.dqb_btime = 0;
if (dqbuf.dqb_isoftlimit && id != 0 &&
dqbuf.dqb_curinodes < dqbuf.dqb_isoftlimit &&
dqb->dqb_curinodes >= dqbuf.dqb_isoftlimit)
dqbuf.dqb_itime = 0;
dqbuf.dqb_curinodes = dqb->dqb_curinodes;
dqbuf.dqb_curblocks = dqb->dqb_curblocks;
/*
* Write it back.
*/
switch (qf->wordsize) {
case 32:
return quota_write32(qf, &dqbuf, id);
case 64:
return quota_write64(qf, &dqbuf, id);
default:
errno = EINVAL;
return (-1);
}
/* not reached */
}
int
quota_write_limits(struct quotafile *qf, struct dqblk *dqb, int id)
{
struct dqblk dqbuf;
int qcmd;
if (qf->fd == -1) {
qcmd = QCMD(Q_SETQUOTA, qf->quotatype);
return (quotactl(qf->fsname, qcmd, id, dqb));
}
/*
* Have to do read-modify-write of quota in file.
*/
if (quota_read(qf, &dqbuf, id) != 0)
return (-1);
/*
* Reset time limit if have a soft limit and were
* previously under it, but are now over it
* or if there previously was no soft limit, but
* now have one and are over it.
*/
if (dqbuf.dqb_bsoftlimit && id != 0 &&
dqbuf.dqb_curblocks < dqbuf.dqb_bsoftlimit &&
dqbuf.dqb_curblocks >= dqb->dqb_bsoftlimit)
dqb->dqb_btime = 0;
if (dqbuf.dqb_bsoftlimit == 0 && id != 0 &&
dqb->dqb_bsoftlimit > 0 &&
dqbuf.dqb_curblocks >= dqb->dqb_bsoftlimit)
dqb->dqb_btime = 0;
if (dqbuf.dqb_isoftlimit && id != 0 &&
dqbuf.dqb_curinodes < dqbuf.dqb_isoftlimit &&
dqbuf.dqb_curinodes >= dqb->dqb_isoftlimit)
dqb->dqb_itime = 0;
if (dqbuf.dqb_isoftlimit == 0 && id !=0 &&
dqb->dqb_isoftlimit > 0 &&
dqbuf.dqb_curinodes >= dqb->dqb_isoftlimit)
dqb->dqb_itime = 0;
dqb->dqb_curinodes = dqbuf.dqb_curinodes;
dqb->dqb_curblocks = dqbuf.dqb_curblocks;
/*
* Write it back.
*/
switch (qf->wordsize) {
case 32:
return quota_write32(qf, dqb, id);
case 64:
@ -275,49 +424,3 @@ quota_write(struct quotafile *qf, const struct dqblk *dqb, int id)
}
/* not reached */
}
/*
* Check to see if a particular quota is to be enabled.
*/
int
hasquota(struct fstab *fs, int type, char *qfnamep, int qfbufsize)
{
char *opt;
char *cp;
struct statfs sfb;
char buf[BUFSIZ];
static char initname, usrname[100], grpname[100];
if (!initname) {
(void)snprintf(usrname, sizeof(usrname), "%s%s",
qfextension[USRQUOTA], QUOTAFILENAME);
(void)snprintf(grpname, sizeof(grpname), "%s%s",
qfextension[GRPQUOTA], QUOTAFILENAME);
initname = 1;
}
strcpy(buf, fs->fs_mntops);
for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) {
if ((cp = index(opt, '=')))
*cp++ = '\0';
if (type == USRQUOTA && strcmp(opt, usrname) == 0)
break;
if (type == GRPQUOTA && strcmp(opt, grpname) == 0)
break;
}
if (!opt)
return (0);
/*
* Ensure that the filesystem is mounted.
*/
if (statfs(fs->fs_file, &sfb) != 0 ||
strcmp(fs->fs_file, sfb.f_mntonname)) {
return (0);
}
if (cp) {
strncpy(qfnamep, cp, qfbufsize);
} else {
(void)snprintf(qfnamep, qfbufsize, "%s/%s.%s", fs->fs_file,
QUOTAFILENAME, qfextension[type]);
}
return (1);
}

View File

@ -84,16 +84,15 @@ __FBSDID("$FreeBSD$");
#endif
const char *qfextension[] = INITQFNAMES;
const char *quotagroup = QUOTAGROUP;
char tmpfil[] = _PATH_TMP;
int hflag;
struct quotause {
struct quotause *next;
long flags;
struct quotafile *qf;
struct dqblk dqblk;
int flags;
char fsname[MAXPATHLEN + 1];
char qfname[1]; /* actually longer */
};
#define FOUND 0x01
@ -108,7 +107,7 @@ char *fmthumanvalinos(int64_t);
void freeprivs(struct quotause *);
int getentry(const char *, int);
struct quotause *getprivs(long, int, char *);
void putprivs(long, int, struct quotause *);
void putprivs(long, struct quotause *);
int readprivs(struct quotause *, char *);
int readtimes(struct quotause *, char *);
static void usage(void);
@ -142,6 +141,11 @@ main(int argc, char *argv[])
fspath = optarg;
break;
case 'p':
if (eflag) {
warnx("cannot specify both -e and -p");
usage();
/* not reached */
}
protoname = optarg;
pflag++;
break;
@ -158,7 +162,12 @@ main(int argc, char *argv[])
tflag++;
break;
case 'e':
if ((qup = calloc(1, sizeof(*qup) + BUFSIZ)) == NULL)
if (pflag) {
warnx("cannot specify both -e and -p");
usage();
/* not reached */
}
if ((qup = calloc(1, sizeof(*qup))) == NULL)
errx(2, "out of memory");
oldoptarg = optarg;
for (i = 0, cp = optarg;
@ -214,7 +223,6 @@ main(int argc, char *argv[])
curprivs = qup;
}
eflag++;
pflag++;
break;
default:
usage();
@ -223,8 +231,8 @@ main(int argc, char *argv[])
}
argc -= optind;
argv += optind;
if (pflag) {
if (protoprivs == NULL) {
if (pflag || eflag) {
if (pflag) {
if ((protoid = getentry(protoname, quotatype)) == -1)
exit(1);
protoprivs = getprivs(protoid, quotatype, fspath);
@ -259,22 +267,23 @@ main(int argc, char *argv[])
*argv);
if ((id = getentry(buf, quotatype)) < 0)
continue;
if (!eflag) {
putprivs(id, quotatype, protoprivs);
if (pflag) {
putprivs(id, protoprivs);
continue;
}
for (qup = protoprivs; qup;
qup = qup->next) {
for (qup = protoprivs; qup; qup = qup->next) {
curprivs = getprivs(id, quotatype,
qup->fsname);
if (curprivs == NULL)
continue;
strcpy(qup->qfname, curprivs->qfname);
strcpy(qup->fsname, curprivs->fsname);
putprivs(id, quotatype, protoprivs);
curprivs->dqblk = qup->dqblk;
putprivs(id, curprivs);
freeprivs(curprivs);
}
}
}
if (pflag)
freeprivs(protoprivs);
exit(0);
}
tmpfd = mkstemp(tmpfil);
@ -283,7 +292,7 @@ main(int argc, char *argv[])
if ((protoprivs = getprivs(0, quotatype, fspath)) != NULL) {
if (writetimes(protoprivs, tmpfd, quotatype) != 0 &&
editit(tmpfil) && readtimes(protoprivs, tmpfil))
putprivs(0L, quotatype, protoprivs);
putprivs(0L, protoprivs);
freeprivs(protoprivs);
}
close(tmpfd);
@ -298,7 +307,7 @@ main(int argc, char *argv[])
if (writeprivs(curprivs, tmpfd, *argv, quotatype) == 0)
continue;
if (editit(tmpfil) && readprivs(curprivs, tmpfil))
putprivs(id, quotatype, curprivs);
putprivs(id, curprivs);
freeprivs(curprivs);
}
close(tmpfd);
@ -366,46 +375,29 @@ getprivs(long id, int quotatype, char *fspath)
struct fstab *fs;
struct quotause *qup, *quptail;
struct quotause *quphead;
int qcmd, qupsize;
char *qfpathname;
static int warned = 0;
setfsent();
quphead = quptail = NULL;
qcmd = QCMD(Q_GETQUOTA, quotatype);
while ((fs = getfsent())) {
if (fspath && *fspath && strcmp(fspath, fs->fs_spec) &&
strcmp(fspath, fs->fs_file))
continue;
if (strcmp(fs->fs_vfstype, "ufs"))
continue;
if (!hasquota(fs, quotatype, &qfpathname))
if ((qf = quota_open(fs, quotatype, O_CREAT|O_RDWR)) == NULL) {
if (errno != EOPNOTSUPP)
warn("cannot open quotas on %s", fs->fs_file);
continue;
}
if ((qup = (struct quotause *)calloc(1, sizeof(*qup))) == NULL)
errx(2, "out of memory");
qup->qf = qf;
strncpy(qup->fsname, fs->fs_file, sizeof(qup->fsname));
if (quota_read(qf, &qup->dqblk, id) == -1) {
warn("cannot read quotas on %s", fs->fs_file);
freeprivs(qup);
continue;
qupsize = sizeof(*qup) + strlen(qfpathname);
if ((qup = (struct quotause *)malloc(qupsize)) == NULL)
errx(2, "out of memory");
if (quotactl(fs->fs_file, qcmd, id, &qup->dqblk) != 0) {
if (errno == EOPNOTSUPP && !warned) {
warned++;
warnx("warning: quotas are not compiled into this kernel");
sleep(3);
}
if ((qf = quota_open(qfpathname)) == NULL &&
(qf = quota_create(qfpathname)) == NULL) {
warn("%s", qfpathname);
free(qup);
continue;
}
if (quota_read(qf, &qup->dqblk, id) != 0) {
warn("read error in %s", qfpathname);
quota_close(qf);
free(qup);
continue;
}
quota_close(qf);
}
strcpy(qup->qfname, qfpathname);
strcpy(qup->fsname, fs->fs_file);
if (quphead == NULL)
quphead = qup;
else
@ -424,53 +416,13 @@ getprivs(long id, int quotatype, char *fspath)
* Store the requested quota information.
*/
void
putprivs(long id, int quotatype, struct quotause *quplist)
putprivs(long id, struct quotause *quplist)
{
struct quotafile *qf;
struct quotause *qup;
int qcmd;
struct dqblk dqbuf;
qcmd = QCMD(Q_SETQUOTA, quotatype);
for (qup = quplist; qup; qup = qup->next) {
if (quotactl(qup->fsname, qcmd, id, &qup->dqblk) == 0)
continue;
if ((qf = quota_open(qup->qfname)) == NULL) {
warn("%s", qup->qfname);
continue;
}
if (quota_read(qf, &dqbuf, id) != 0) {
warn("read error in %s", qup->qfname);
quota_close(qf);
continue;
}
/*
* Reset time limit if have a soft limit and were
* previously under it, but are now over it
* or if there previously was no soft limit, but
* now have one and are over it.
*/
if (dqbuf.dqb_bsoftlimit && id != 0 &&
dqbuf.dqb_curblocks < dqbuf.dqb_bsoftlimit &&
dqbuf.dqb_curblocks >= qup->dqblk.dqb_bsoftlimit)
qup->dqblk.dqb_btime = 0;
if (dqbuf.dqb_bsoftlimit == 0 && id != 0 &&
dqbuf.dqb_curblocks >= qup->dqblk.dqb_bsoftlimit)
qup->dqblk.dqb_btime = 0;
if (dqbuf.dqb_isoftlimit && id != 0 &&
dqbuf.dqb_curinodes < dqbuf.dqb_isoftlimit &&
dqbuf.dqb_curinodes >= qup->dqblk.dqb_isoftlimit)
qup->dqblk.dqb_itime = 0;
if (dqbuf.dqb_isoftlimit == 0 && id !=0 &&
dqbuf.dqb_curinodes >= qup->dqblk.dqb_isoftlimit)
qup->dqblk.dqb_itime = 0;
qup->dqblk.dqb_curinodes = dqbuf.dqb_curinodes;
qup->dqblk.dqb_curblocks = dqbuf.dqb_curblocks;
if (quota_write(qf, &qup->dqblk, id) == 0) {
warn("%s", qup->qfname);
}
quota_close(qf);
}
for (qup = quplist; qup; qup = qup->next)
if (quota_write_limits(qup->qf, &qup->dqblk, id) == -1)
warn("%s", qup->fsname);
}
/*
@ -978,6 +930,7 @@ freeprivs(struct quotause *quplist)
struct quotause *qup, *nextqup;
for (qup = quplist; qup; qup = nextqup) {
quota_close(qup->qf);
nextqup = qup->next;
free(qup);
}