This commit is contained in:
Dag-Erling Smørgrav 2009-01-30 13:54:03 +00:00
parent e57c2b130f
commit 1b3515f39b
12 changed files with 760 additions and 135 deletions

View File

@ -12,8 +12,8 @@ SRCS= _secure_path.c auth.c expand_number.c flopen.c fparseln.c gr_util.c \
hexdump.c humanize_number.c kinfo_getfile.c kinfo_getvmmap.c kld.c \
login.c login_auth.c login_cap.c \
login_class.c login_crypt.c login_ok.c login_times.c login_tty.c \
logout.c logwtmp.c pidfile.c property.c pty.c pw_util.c realhostname.c \
stub.c trimdomain.c uucplock.c
logout.c logwtmp.c pidfile.c property.c pty.c pw_util.c quotafile.c \
realhostname.c stub.c trimdomain.c uucplock.c
INCS= libutil.h login_cap.h
WARNS?= 6
@ -30,7 +30,7 @@ MAN+= kld.3 login.3 login_auth.3 login_tty.3 logout.3 logwtmp.3 pty.3 \
login_cap.3 login_class.3 login_times.3 login_ok.3 \
_secure_path.3 uucplock.3 property.3 auth.3 realhostname.3 \
realhostname_sa.3 trimdomain.3 fparseln.3 humanize_number.3 \
pidfile.3 flopen.3 expand_number.3
pidfile.3 flopen.3 expand_number.3 quotafile.3
MAN+= login.conf.5 auth.conf.5
MLINKS+= kld.3 kld_isloaded.3 kld.3 kld_load.3
MLINKS+= property.3 properties_read.3 property.3 properties_free.3
@ -58,5 +58,9 @@ MLINKS+=pidfile.3 pidfile_open.3 \
pidfile.3 pidfile_write.3 \
pidfile.3 pidfile_close.3 \
pidfile.3 pidfile_remove.3
MLINKS+=quotafile.3 quota_open.3 \
quotafile.3 quota_read.3 \
quotafile.3 quota_write.3 \
quotafile.3 quota_close.3
.include <bsd.lib.mk>

View File

@ -140,6 +140,15 @@ int pidfile_close(struct pidfh *pfh);
int pidfile_remove(struct pidfh *pfh);
#endif
#ifdef _UFS_UFS_QUOTA_H_
struct quotafile;
struct quotafile *quota_open(const char *);
struct quotafile *quota_create(const char *);
void quota_close(struct quotafile *);
int quota_read(struct quotafile *, struct dqblk *, int);
int quota_write(struct quotafile *, const struct dqblk *, int);
#endif
__END_DECLS
#define UU_LOCK_INUSE (1)

69
lib/libutil/quotafile.3 Normal file
View File

@ -0,0 +1,69 @@
.\"-
.\" Copyright (c) 2008 Dag-Erling Coïdan Smørgrav
.\" All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.\" $FreeBSD$
.\"
.Dd November 4, 2008
.Dt QUOTAFILE 3
.Os
.Sh NAME
.Nm quota_open
.Nm quota_create
.Nm quota_read
.Nm quota_write
.Nm quota_close
.Nd "Manipulate quota files"
.Sh LIBRARY
.Lb libutil
.Sh SYNOPSIS
.In ufs/ufs/quota.h
.In libutil.h
.Ft "struct quotafile *"
.Fn quota_open "const char *path"
.Ft "struct quotafile *"
.Fn quota_create "const char *path"
.Ft int
.Fn quota_read "struct quotafile *qf" "struct dqblk *dqb" "int type"
.Ft int
.Fn quota_write "struct quotafile *qf" "const struct dqblk *dqb" "int type"
.Ft int
.Fn quota_close "struct quotafile *qf"
.Sh DESCRIPTION
.Sh RETURN VALUES
.Sh SEE ALSO
.Xr quotactl 2 ,
.Xr quota.user 5 ,
.Xr quota.group 5
.Sh HISTORY
The
.Nm
functions first appeared in
.Fx 8.0 .
.Sh AUTHORS
.An -nosplit
The
.Nm
functions and this manual page were written by
.An Dag-Erling Sm\(/orgrav Aq des@FreeBSD.org .

272
lib/libutil/quotafile.c Normal file
View File

@ -0,0 +1,272 @@
/*-
* Copyright (c) 2008 Dag-Erling Coïdan Smørgrav
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer
* in this position and unchanged.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#include <sys/types.h>
#include <sys/endian.h>
#include <sys/stat.h>
#include <ufs/ufs/quota.h>
#include <errno.h>
#include <fcntl.h>
#include <grp.h>
#include <pwd.h>
#include <libutil.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
struct quotafile {
int fd;
int type; /* 32 or 64 */
};
struct quotafile *
quota_open(const char *fn)
{
struct quotafile *qf;
struct dqhdr64 dqh;
int serrno;
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);
}
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);
}
/* not reached */
}
struct quotafile *
quota_create(const char *fn)
{
struct quotafile *qf;
struct dqhdr64 dqh;
struct group *grp;
int serrno;
if ((qf = calloc(1, sizeof(*qf))) == NULL)
return (NULL);
if ((qf->fd = open(fn, O_RDWR|O_CREAT|O_TRUNC, 0)) < 0) {
serrno = errno;
free(qf);
errno = serrno;
return (NULL);
}
qf->type = 64;
memset(&dqh, 0, sizeof(dqh));
memcpy(dqh.dqh_magic, Q_DQHDR64_MAGIC, sizeof(dqh.dqh_magic));
dqh.dqh_version = htobe32(Q_DQHDR64_VERSION);
dqh.dqh_hdrlen = htobe32(sizeof(struct dqhdr64));
dqh.dqh_reclen = htobe32(sizeof(struct dqblk64));
if (write(qf->fd, &dqh, sizeof(dqh)) != sizeof(dqh)) {
serrno = errno;
unlink(fn);
close(qf->fd);
free(qf);
errno = serrno;
return (NULL);
}
grp = getgrnam(QUOTAGROUP);
fchown(qf->fd, 0, grp ? grp->gr_gid : 0);
fchmod(qf->fd, 0640);
return (qf);
}
void
quota_close(struct quotafile *qf)
{
close(qf->fd);
free(qf);
}
static int
quota_read32(struct quotafile *qf, struct dqblk *dqb, int id)
{
struct dqblk32 dqb32;
off_t off;
off = id * sizeof(struct dqblk32);
if (lseek(qf->fd, off, SEEK_SET) == -1)
return (-1);
switch (read(qf->fd, &dqb32, sizeof(dqb32))) {
case 0:
memset(&dqb, 0, sizeof(*dqb));
return (0);
case sizeof(dqb32):
dqb->dqb_bhardlimit = dqb32.dqb_bhardlimit;
dqb->dqb_bsoftlimit = dqb32.dqb_bsoftlimit;
dqb->dqb_curblocks = dqb32.dqb_curblocks;
dqb->dqb_ihardlimit = dqb32.dqb_ihardlimit;
dqb->dqb_isoftlimit = dqb32.dqb_isoftlimit;
dqb->dqb_curinodes = dqb32.dqb_curinodes;
dqb->dqb_btime = dqb32.dqb_btime;
dqb->dqb_itime = dqb32.dqb_itime;
return (0);
default:
return (-1);
}
}
static int
quota_read64(struct quotafile *qf, struct dqblk *dqb, int id)
{
struct dqblk64 dqb64;
off_t off;
off = sizeof(struct dqhdr64) + id * sizeof(struct dqblk64);
if (lseek(qf->fd, off, SEEK_SET) == -1)
return (-1);
switch (read(qf->fd, &dqb64, sizeof(dqb64))) {
case 0:
memset(&dqb, 0, sizeof(*dqb));
return (0);
case sizeof(dqb64):
dqb->dqb_bhardlimit = be64toh(dqb64.dqb_bhardlimit);
dqb->dqb_bsoftlimit = be64toh(dqb64.dqb_bsoftlimit);
dqb->dqb_curblocks = be64toh(dqb64.dqb_curblocks);
dqb->dqb_ihardlimit = be64toh(dqb64.dqb_ihardlimit);
dqb->dqb_isoftlimit = be64toh(dqb64.dqb_isoftlimit);
dqb->dqb_curinodes = be64toh(dqb64.dqb_curinodes);
dqb->dqb_btime = be64toh(dqb64.dqb_btime);
dqb->dqb_itime = be64toh(dqb64.dqb_itime);
return (0);
default:
return (-1);
}
}
int
quota_read(struct quotafile *qf, struct dqblk *dqb, int id)
{
switch (qf->type) {
case 32:
return quota_read32(qf, dqb, id);
case 64:
return quota_read64(qf, dqb, id);
default:
errno = EINVAL;
return (-1);
}
/* not reached */
}
#define CLIP32(u64) ((u64) > UINT32_MAX ? UINT32_MAX : (uint32_t)(u64))
static int
quota_write32(struct quotafile *qf, const struct dqblk *dqb, int id)
{
struct dqblk32 dqb32;
off_t off;
dqb32.dqb_bhardlimit = CLIP32(dqb->dqb_bhardlimit);
dqb32.dqb_bsoftlimit = CLIP32(dqb->dqb_bsoftlimit);
dqb32.dqb_curblocks = CLIP32(dqb->dqb_curblocks);
dqb32.dqb_ihardlimit = CLIP32(dqb->dqb_ihardlimit);
dqb32.dqb_isoftlimit = CLIP32(dqb->dqb_isoftlimit);
dqb32.dqb_curinodes = CLIP32(dqb->dqb_curinodes);
dqb32.dqb_btime = CLIP32(dqb->dqb_btime);
dqb32.dqb_itime = CLIP32(dqb->dqb_itime);
off = id * sizeof(struct dqblk32);
if (lseek(qf->fd, off, SEEK_SET) == -1)
return (-1);
return (write(qf->fd, &dqb32, sizeof(dqb32)));
}
static int
quota_write64(struct quotafile *qf, const struct dqblk *dqb, int id)
{
struct dqblk64 dqb64;
off_t off;
dqb64.dqb_bhardlimit = htobe64(dqb->dqb_bhardlimit);
dqb64.dqb_bsoftlimit = htobe64(dqb->dqb_bsoftlimit);
dqb64.dqb_curblocks = htobe64(dqb->dqb_curblocks);
dqb64.dqb_ihardlimit = htobe64(dqb->dqb_ihardlimit);
dqb64.dqb_isoftlimit = htobe64(dqb->dqb_isoftlimit);
dqb64.dqb_curinodes = htobe64(dqb->dqb_curinodes);
dqb64.dqb_btime = htobe64(dqb->dqb_btime);
dqb64.dqb_itime = htobe64(dqb->dqb_itime);
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)));
}
int
quota_write(struct quotafile *qf, const struct dqblk *dqb, int id)
{
switch (qf->type) {
case 32:
return quota_write32(qf, dqb, id);
case 64:
return quota_write64(qf, dqb, id);
default:
errno = EINVAL;
return (-1);
}
/* not reached */
}

View File

@ -6,7 +6,7 @@ MAN = rpc.rquotad.8
WARNS ?= 6
DPADD= ${LIBRPCSVC}
LDADD= -lrpcsvc
DPADD= ${LIBRPCSVC} ${LIBUTIL}
LDADD= -lrpcsvc -lutil
.include <bsd.prog.mk>

View File

@ -23,6 +23,7 @@ __FBSDID("$FreeBSD$");
#include <errno.h>
#include <fstab.h>
#include <grp.h>
#include <libutil.h>
#include <pwd.h>
#include <signal.h>
#include <stdio.h>
@ -247,9 +248,10 @@ initfs(void)
int
getfsquota(long id, char *path, struct dqblk *dqblk)
{
struct quotafile *qf;
struct stat st_path;
struct fs_stat *fs;
int qcmd, fd, ret = 0;
int qcmd, ret = 0;
if (stat(path, &st_path) < 0)
return (0);
@ -265,32 +267,16 @@ getfsquota(long id, char *path, struct dqblk *dqblk)
if (quotactl(fs->fs_file, qcmd, id, dqblk) == 0)
return (1);
if ((fd = open(fs->qfpathname, O_RDONLY)) < 0) {
if ((qf = quota_open(fs->qfpathname)) == NULL) {
syslog(LOG_ERR, "open error: %s: %m", fs->qfpathname);
return (0);
}
if (lseek(fd, (off_t)(id * sizeof(struct dqblk)), L_SET) == (off_t)-1) {
close(fd);
return (1);
}
switch (read(fd, dqblk, sizeof(struct dqblk))) {
case 0:
/*
* Convert implicit 0 quota (EOF)
* into an explicit one (zero'ed dqblk)
*/
bzero(dqblk, sizeof(struct dqblk));
ret = 1;
break;
case sizeof(struct dqblk): /* OK */
ret = 1;
break;
default: /* ERROR */
if (quota_read(qf, dqblk, id) != 0) {
syslog(LOG_ERR, "read error: %s: %m", fs->qfpathname);
close(fd);
quota_close(qf);
return (0);
}
close(fd);
quota_close(qf);
}
return (ret);
}

View File

@ -81,10 +81,13 @@
#define Q_QUOTAON 0x0100 /* enable quotas */
#define Q_QUOTAOFF 0x0200 /* disable quotas */
#define Q_GETQUOTA 0x0300 /* get limits and usage */
#define Q_SETQUOTA 0x0400 /* set limits and usage */
#define Q_SETUSE 0x0500 /* set usage */
#define Q_GETQUOTA32 0x0300 /* get limits and usage (32-bit version) */
#define Q_SETQUOTA32 0x0400 /* set limits and usage (32-bit version) */
#define Q_SETUSE32 0x0500 /* set usage (32-bit version) */
#define Q_SYNC 0x0600 /* sync disk copy of a filesystems quotas */
#define Q_GETQUOTA 0x0700 /* get limits and usage (64-bit version) */
#define Q_SETQUOTA 0x0800 /* set limits and usage (64-bit version) */
#define Q_SETUSE 0x0900 /* set usage (64-bit version) */
/*
* The following structure defines the format of the disk quota file
@ -93,7 +96,7 @@
* the vnode for each quota file (a pointer is retained in the ufsmount
* structure).
*/
struct dqblk {
struct dqblk32 {
u_int32_t dqb_bhardlimit; /* absolute limit on disk blks alloc */
u_int32_t dqb_bsoftlimit; /* preferred limit on disk blks */
u_int32_t dqb_curblocks; /* current block count */
@ -104,6 +107,30 @@ struct dqblk {
int32_t dqb_itime; /* time limit for excessive files */
};
struct dqblk64 {
u_int64_t dqb_bhardlimit; /* absolute limit on disk blks alloc */
u_int64_t dqb_bsoftlimit; /* preferred limit on disk blks */
u_int64_t dqb_curblocks; /* current block count */
u_int64_t dqb_ihardlimit; /* maximum # allocated inodes + 1 */
u_int64_t dqb_isoftlimit; /* preferred inode limit */
u_int64_t dqb_curinodes; /* current # allocated inodes */
int64_t dqb_btime; /* time limit for excessive disk use */
int64_t dqb_itime; /* time limit for excessive files */
};
#define dqblk dqblk64
#define Q_DQHDR64_MAGIC "QUOTA64"
#define Q_DQHDR64_VERSION 0x20081104
struct dqhdr64 {
char dqh_magic[8]; /* Q_DQHDR64_MAGIC */
uint32_t dqh_version; /* Q_DQHDR64_VERSION */
uint32_t dqh_hdrlen; /* header length */
uint32_t dqh_reclen; /* record length */
char dqh_unused[44]; /* reserved for future extension */
};
#ifdef _KERNEL
#include <sys/queue.h>
@ -125,7 +152,7 @@ struct dquot {
u_int32_t dq_id; /* identifier this applies to */
struct ufsmount *dq_ump; /* (h) filesystem that this is
taken from */
struct dqblk dq_dqb; /* actual usage & quotas */
struct dqblk64 dq_dqb; /* actual usage & quotas */
};
/*
* Flag values.
@ -199,10 +226,13 @@ void dqinit(void);
void dqrele(struct vnode *, struct dquot *);
void dquninit(void);
int getinoquota(struct inode *);
int getquota(struct thread *, struct mount *, u_long, int, void *);
int qsync(struct mount *mp);
int quotaoff(struct thread *td, struct mount *, int);
int quotaon(struct thread *td, struct mount *, int, void *);
int getquota32(struct thread *, struct mount *, u_long, int, void *);
int setquota32(struct thread *, struct mount *, u_long, int, void *);
int setuse32(struct thread *, struct mount *, u_long, int, void *);
int getquota(struct thread *, struct mount *, u_long, int, void *);
int setquota(struct thread *, struct mount *, u_long, int, void *);
int setuse(struct thread *, struct mount *, u_long, int, void *);
vfs_quotactl_t ufs_quotactl;

View File

@ -39,6 +39,7 @@ __FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/endian.h>
#include <sys/fcntl.h>
#include <sys/kernel.h>
#include <sys/lock.h>
@ -73,6 +74,7 @@ static char *quotatypes[] = INITQFNAMES;
static int chkdqchg(struct inode *, ufs2_daddr_t, struct ucred *, int, int *);
static int chkiqchg(struct inode *, int, struct ucred *, int, int *);
static int dqopen(struct vnode *, struct ufsmount *, int);
static int dqget(struct vnode *,
u_long, struct ufsmount *, int, struct dquot **);
static int dqsync(struct vnode *, struct dquot *);
@ -80,6 +82,14 @@ static void dqflush(struct vnode *);
static int quotaoff1(struct thread *td, struct mount *mp, int type);
static int quotaoff_inchange(struct thread *td, struct mount *mp, int type);
/* conversion functions - from_to() */
static void dqb32_dq(const struct dqblk32 *, struct dquot *);
static void dqb64_dq(const struct dqblk64 *, struct dquot *);
static void dq_dqb32(const struct dquot *, struct dqblk32 *);
static void dq_dqb64(const struct dquot *, struct dqblk64 *);
static void dqb32_dqb64(const struct dqblk32 *, struct dqblk64 *);
static void dqb64_dqb32(const struct dqblk64 *, struct dqblk32 *);
#ifdef DIAGNOSTIC
static void dqref(struct dquot *);
static void chkdquot(struct inode *);
@ -90,7 +100,7 @@ static void chkdquot(struct inode *);
*
* This routine completely defines the semantics of quotas.
* If other criterion want to be used to establish quotas, the
* MAXQUOTAS value in quotas.h should be increased, and the
* MAXQUOTAS value in quota.h should be increased, and the
* additional dquots set up here.
*/
int
@ -522,6 +532,13 @@ quotaon(struct thread *td, struct mount *mp, int type, void *fname)
return (EALREADY);
}
ump->um_qflags[type] |= QTF_OPENING|QTF_CLOSING;
if ((error = dqopen(vp, ump, type)) != 0) {
ump->um_qflags[type] &= ~(QTF_OPENING|QTF_CLOSING);
UFS_UNLOCK(ump);
(void) vn_close(vp, FREAD|FWRITE, td->td_ucred, td);
VFS_UNLOCK_GIANT(vfslocked);
return (error);
}
MNT_ILOCK(mp);
mp->mnt_flag |= MNT_QUOTA;
MNT_IUNLOCK(mp);
@ -734,8 +751,9 @@ quotaoff(struct thread *td, struct mount *mp, int type)
/*
* Q_GETQUOTA - return current values in a dqblk structure.
*/
int
getquota(struct thread *td, struct mount *mp, u_long id, int type, void *addr)
static int
_getquota(struct thread *td, struct mount *mp, u_long id, int type,
struct dqblk64 *dqb)
{
struct dquot *dq;
int error;
@ -766,7 +784,7 @@ getquota(struct thread *td, struct mount *mp, u_long id, int type, void *addr)
error = dqget(NULLVP, id, VFSTOUFS(mp), type, &dq);
if (error)
return (error);
error = copyout(&dq->dq_dqb, addr, sizeof (struct dqblk));
*dqb = dq->dq_dqb;
dqrele(NULLVP, dq);
return (error);
}
@ -774,23 +792,21 @@ getquota(struct thread *td, struct mount *mp, u_long id, int type, void *addr)
/*
* Q_SETQUOTA - assign an entire dqblk structure.
*/
int
setquota(struct thread *td, struct mount *mp, u_long id, int type, void *addr)
static int
_setquota(struct thread *td, struct mount *mp, u_long id, int type,
struct dqblk64 *dqb)
{
struct dquot *dq;
struct dquot *ndq;
struct ufsmount *ump;
struct dqblk newlim;
struct dqblk64 newlim;
int error;
error = priv_check(td, PRIV_VFS_SETQUOTA);
if (error)
return (error);
ump = VFSTOUFS(mp);
error = copyin(addr, &newlim, sizeof (struct dqblk));
if (error)
return (error);
newlim = *dqb;
ndq = NODQUOT;
ump = VFSTOUFS(mp);
@ -839,23 +855,21 @@ setquota(struct thread *td, struct mount *mp, u_long id, int type, void *addr)
/*
* Q_SETUSE - set current inode and block usage.
*/
int
setuse(struct thread *td, struct mount *mp, u_long id, int type, void *addr)
static int
_setuse(struct thread *td, struct mount *mp, u_long id, int type,
struct dqblk64 *dqb)
{
struct dquot *dq;
struct ufsmount *ump;
struct dquot *ndq;
struct dqblk usage;
struct dqblk64 usage;
int error;
error = priv_check(td, PRIV_UFS_SETUSE);
if (error)
return (error);
ump = VFSTOUFS(mp);
error = copyin(addr, &usage, sizeof (struct dqblk));
if (error)
return (error);
usage = *dqb;
ump = VFSTOUFS(mp);
ndq = NODQUOT;
@ -888,6 +902,90 @@ setuse(struct thread *td, struct mount *mp, u_long id, int type, void *addr)
return (0);
}
int
getquota32(struct thread *td, struct mount *mp, u_long id, int type, void *addr)
{
struct dqblk32 dqb32;
struct dqblk64 dqb64;
int error;
error = _getquota(td, mp, id, type, &dqb64);
if (error)
return (error);
dqb64_dqb32(&dqb64, &dqb32);
error = copyout(&dqb32, addr, sizeof(dqb32));
return (error);
}
int
setquota32(struct thread *td, struct mount *mp, u_long id, int type, void *addr)
{
struct dqblk32 dqb32;
struct dqblk64 dqb64;
int error;
error = copyin(addr, &dqb32, sizeof(dqb32));
if (error)
return (error);
dqb32_dqb64(&dqb32, &dqb64);
error = _setquota(td, mp, id, type, &dqb64);
return (error);
}
int
setuse32(struct thread *td, struct mount *mp, u_long id, int type, void *addr)
{
struct dqblk32 dqb32;
struct dqblk64 dqb64;
int error;
error = copyin(addr, &dqb32, sizeof(dqb32));
if (error)
return (error);
dqb32_dqb64(&dqb32, &dqb64);
error = _setuse(td, mp, id, type, &dqb64);
return (error);
}
int
getquota(struct thread *td, struct mount *mp, u_long id, int type, void *addr)
{
struct dqblk64 dqb64;
int error;
error = _getquota(td, mp, id, type, &dqb64);
if (error)
return (error);
error = copyout(&dqb64, addr, sizeof(dqb64));
return (error);
}
int
setquota(struct thread *td, struct mount *mp, u_long id, int type, void *addr)
{
struct dqblk64 dqb64;
int error;
error = copyin(addr, &dqb64, sizeof(dqb64));
if (error)
return (error);
error = _setquota(td, mp, id, type, &dqb64);
return (error);
}
int
setuse(struct thread *td, struct mount *mp, u_long id, int type, void *addr)
{
struct dqblk64 dqb64;
int error;
error = copyin(addr, &dqb64, sizeof(dqb64));
if (error)
return (error);
error = _setuse(td, mp, id, type, &dqb64);
return (error);
}
/*
* Q_SYNC - sync quota files to disk.
*/
@ -1024,6 +1122,47 @@ dqhashfind(struct dqhash *dqh, u_long id, struct vnode *dqvp)
return (NODQUOT);
}
/*
* Determine the quota file type.
*/
static int
dqopen(struct vnode *vp, struct ufsmount *ump, int type)
{
struct dqhdr64 dqh;
struct iovec aiov;
struct uio auio;
int error, vfslocked;
auio.uio_iov = &aiov;
auio.uio_iovcnt = 1;
aiov.iov_base = &dqh;
aiov.iov_len = sizeof(dqh);
auio.uio_resid = sizeof(dqh);
auio.uio_offset = 0;
auio.uio_segflg = UIO_SYSSPACE;
auio.uio_rw = UIO_READ;
auio.uio_td = (struct thread *)0;
vfslocked = VFS_LOCK_GIANT(vp->v_mount);
error = VOP_READ(vp, &auio, 0, ump->um_cred[type]);
VFS_UNLOCK_GIANT(vfslocked);
if (error != 0)
return (error);
if (auio.uio_resid > 0) {
/* assume 32 bits */
return (0);
}
if (strcmp(dqh.dqh_magic, Q_DQHDR64_MAGIC) == 0 &&
be32toh(dqh.dqh_version) == Q_DQHDR64_VERSION &&
be32toh(dqh.dqh_hdrlen) == (uint32_t)sizeof(struct dqhdr64) &&
be32toh(dqh.dqh_reclen) == (uint32_t)sizeof(struct dqblk64))
ump->um_qflags[type] |= QTF_64BIT;
/* XXX: what if the magic matches, but the sizes are wrong? */
return (0);
}
/*
* Obtain a dquot structure for the specified identifier and quota file
* reading the information from the file if necessary.
@ -1032,6 +1171,8 @@ static int
dqget(struct vnode *vp, u_long id, struct ufsmount *ump, int type,
struct dquot **dqp)
{
uint8_t buf[sizeof(struct dqblk64)];
off_t base, recsize;
struct dquot *dq, *dq1;
struct dqhash *dqh;
struct vnode *dqvp;
@ -1121,8 +1262,7 @@ hfound: DQI_LOCK(dq);
if (numdquot < desireddquot) {
numdquot++;
DQH_UNLOCK();
dq1 = (struct dquot *)malloc(sizeof *dq, M_DQUOT,
M_WAITOK | M_ZERO);
dq1 = malloc(sizeof *dq1, M_DQUOT, M_WAITOK | M_ZERO);
mtx_init(&dq1->dq_lock, "dqlock", NULL, MTX_DEF);
DQH_LOCK();
/*
@ -1169,20 +1309,52 @@ hfound: DQI_LOCK(dq);
DQREF(dq);
DQH_UNLOCK();
/*
* Read the requested quota record from the quota file, performing
* any necessary conversions.
*
* The record's offset within the file depends on the size of the
* record, which means we need to know whether it's a 32-bit file
* or a 64-bit file.
*
* Luckily, root's record is always at offset 0, and most of it is
* unused, so we can use it to store a magic number indicating the
* file format. Due to an acute lack of imagination, this magic
* number, stored in the first byte of root's record and hence the
* first byte of the file, is 64.
*
* Another lucky break is that quotaon() always loads root's
* record, to get the default values for dq_btime and dq_itime, so
* we will always have a chance to check the file format before
* being asked for a "real" record.
*/
if (id == 0 || (ump->um_qflags[type] & QTF_64BIT)) {
recsize = sizeof(struct dqblk64);
base = sizeof(struct dqhdr64);
} else {
recsize = sizeof(struct dqblk32);
base = 0;
}
auio.uio_iov = &aiov;
auio.uio_iovcnt = 1;
aiov.iov_base = &dq->dq_dqb;
aiov.iov_len = sizeof (struct dqblk);
auio.uio_resid = sizeof (struct dqblk);
auio.uio_offset = (off_t)id * sizeof (struct dqblk);
aiov.iov_base = buf;
aiov.iov_len = recsize;
auio.uio_resid = recsize;
auio.uio_offset = base + id * recsize;
auio.uio_segflg = UIO_SYSSPACE;
auio.uio_rw = UIO_READ;
auio.uio_td = (struct thread *)0;
vfslocked = VFS_LOCK_GIANT(dqvp->v_mount);
error = VOP_READ(dqvp, &auio, 0, ump->um_cred[type]);
if (auio.uio_resid == sizeof(struct dqblk) && error == 0)
bzero(&dq->dq_dqb, sizeof(struct dqblk));
if (auio.uio_resid == recsize && error == 0) {
bzero(&dq->dq_dqb, sizeof(dq->dq_dqb));
} else {
if (ump->um_qflags[type] & QTF_64BIT)
dqb64_dq((struct dqblk64 *)buf, dq);
else
dqb32_dq((struct dqblk32 *)buf, dq);
}
if (dqvplocked)
vput(dqvp);
else
@ -1281,6 +1453,8 @@ dqrele(struct vnode *vp, struct dquot *dq)
static int
dqsync(struct vnode *vp, struct dquot *dq)
{
uint8_t buf[sizeof(struct dqblk64)];
off_t base, recsize;
struct vnode *dqvp;
struct iovec aiov;
struct uio auio;
@ -1327,12 +1501,26 @@ dqsync(struct vnode *vp, struct dquot *dq)
dq->dq_flags |= DQ_LOCK;
DQI_UNLOCK(dq);
/*
* Write the quota record to the quota file, performing any
* necessary conversions. See dqget() for additional details.
*/
if (ump->um_qflags[dq->dq_type] & QTF_64BIT) {
dq_dqb64(dq, (struct dqblk64 *)buf);
recsize = sizeof(struct dqblk64);
base = sizeof(struct dqhdr64);
} else {
dq_dqb32(dq, (struct dqblk32 *)buf);
recsize = sizeof(struct dqblk32);
base = 0;
}
auio.uio_iov = &aiov;
auio.uio_iovcnt = 1;
aiov.iov_base = &dq->dq_dqb;
aiov.iov_len = sizeof (struct dqblk);
auio.uio_resid = sizeof (struct dqblk);
auio.uio_offset = (off_t)dq->dq_id * sizeof (struct dqblk);
aiov.iov_base = buf;
aiov.iov_len = recsize;
auio.uio_resid = recsize;
auio.uio_offset = base + dq->dq_id * recsize;
auio.uio_segflg = UIO_SYSSPACE;
auio.uio_rw = UIO_WRITE;
auio.uio_td = (struct thread *)0;
@ -1345,7 +1533,8 @@ dqsync(struct vnode *vp, struct dquot *dq)
DQI_LOCK(dq);
DQI_WAKEUP(dq);
dq->dq_flags &= ~DQ_MOD;
out: DQI_UNLOCK(dq);
out:
DQI_UNLOCK(dq);
vfslocked = VFS_LOCK_GIANT(dqvp->v_mount);
if (vp != dqvp)
vput(dqvp);
@ -1384,3 +1573,98 @@ dqflush(struct vnode *vp)
}
DQH_UNLOCK();
}
/*
* 32-bit / 64-bit conversion functions.
*
* 32-bit quota records are stored in native byte order. Attention must
* be paid to overflow issues.
*
* 64-bit quota records are stored in network byte order.
*/
#define CLIP32(u64) (u64 > UINT32_MAX ? UINT32_MAX : (uint32_t)u64)
static void
dqb32_dq(const struct dqblk32 *dqb32, struct dquot *dq)
{
dq->dq_bhardlimit = dqb32->dqb_bhardlimit;
dq->dq_bsoftlimit = dqb32->dqb_bsoftlimit;
dq->dq_curblocks = dqb32->dqb_curblocks;
dq->dq_ihardlimit = dqb32->dqb_ihardlimit;
dq->dq_isoftlimit = dqb32->dqb_isoftlimit;
dq->dq_curinodes = dqb32->dqb_curinodes;
dq->dq_btime = dqb32->dqb_btime;
dq->dq_itime = dqb32->dqb_itime;
}
static void
dqb64_dq(const struct dqblk64 *dqb64, struct dquot *dq)
{
dq->dq_bhardlimit = be64toh(dqb64->dqb_bhardlimit);
dq->dq_bsoftlimit = be64toh(dqb64->dqb_bsoftlimit);
dq->dq_curblocks = be64toh(dqb64->dqb_curblocks);
dq->dq_ihardlimit = be64toh(dqb64->dqb_ihardlimit);
dq->dq_isoftlimit = be64toh(dqb64->dqb_isoftlimit);
dq->dq_curinodes = be64toh(dqb64->dqb_curinodes);
dq->dq_btime = be64toh(dqb64->dqb_btime);
dq->dq_itime = be64toh(dqb64->dqb_itime);
}
static void
dq_dqb32(const struct dquot *dq, struct dqblk32 *dqb32)
{
dqb32->dqb_bhardlimit = CLIP32(dq->dq_bhardlimit);
dqb32->dqb_bsoftlimit = CLIP32(dq->dq_bsoftlimit);
dqb32->dqb_curblocks = CLIP32(dq->dq_curblocks);
dqb32->dqb_ihardlimit = CLIP32(dq->dq_ihardlimit);
dqb32->dqb_isoftlimit = CLIP32(dq->dq_isoftlimit);
dqb32->dqb_curinodes = CLIP32(dq->dq_curinodes);
dqb32->dqb_btime = CLIP32(dq->dq_btime);
dqb32->dqb_itime = CLIP32(dq->dq_itime);
}
static void
dq_dqb64(const struct dquot *dq, struct dqblk64 *dqb64)
{
dqb64->dqb_bhardlimit = htobe64(dq->dq_bhardlimit);
dqb64->dqb_bsoftlimit = htobe64(dq->dq_bsoftlimit);
dqb64->dqb_curblocks = htobe64(dq->dq_curblocks);
dqb64->dqb_ihardlimit = htobe64(dq->dq_ihardlimit);
dqb64->dqb_isoftlimit = htobe64(dq->dq_isoftlimit);
dqb64->dqb_curinodes = htobe64(dq->dq_curinodes);
dqb64->dqb_btime = htobe64(dq->dq_btime);
dqb64->dqb_itime = htobe64(dq->dq_itime);
}
static void
dqb64_dqb32(const struct dqblk64 *dqb64, struct dqblk32 *dqb32)
{
dqb32->dqb_bhardlimit = CLIP32(dqb64->dqb_bhardlimit);
dqb32->dqb_bsoftlimit = CLIP32(dqb64->dqb_bsoftlimit);
dqb32->dqb_curblocks = CLIP32(dqb64->dqb_curblocks);
dqb32->dqb_ihardlimit = CLIP32(dqb64->dqb_ihardlimit);
dqb32->dqb_isoftlimit = CLIP32(dqb64->dqb_isoftlimit);
dqb32->dqb_curinodes = CLIP32(dqb64->dqb_curinodes);
dqb32->dqb_btime = CLIP32(dqb64->dqb_btime);
dqb32->dqb_itime = CLIP32(dqb64->dqb_itime);
}
static void
dqb32_dqb64(const struct dqblk32 *dqb32, struct dqblk64 *dqb64)
{
dqb64->dqb_bhardlimit = dqb32->dqb_bhardlimit;
dqb64->dqb_bsoftlimit = dqb32->dqb_bsoftlimit;
dqb64->dqb_curblocks = dqb32->dqb_curblocks;
dqb64->dqb_ihardlimit = dqb32->dqb_ihardlimit;
dqb64->dqb_isoftlimit = dqb32->dqb_isoftlimit;
dqb64->dqb_curinodes = dqb32->dqb_curinodes;
dqb64->dqb_btime = dqb32->dqb_btime;
dqb64->dqb_itime = dqb32->dqb_itime;
}

View File

@ -130,6 +130,18 @@ ufs_quotactl(mp, cmds, id, arg, td)
error = quotaoff(td, mp, type);
break;
case Q_SETQUOTA32:
error = setquota32(td, mp, id, type, arg);
break;
case Q_SETUSE32:
error = setuse32(td, mp, id, type, arg);
break;
case Q_GETQUOTA32:
error = getquota32(td, mp, id, type, arg);
break;
case Q_SETQUOTA:
error = setquota(td, mp, id, type, arg);
break;

View File

@ -120,6 +120,7 @@ struct ufsmount {
*/
#define QTF_OPENING 0x01 /* Q_QUOTAON in progress */
#define QTF_CLOSING 0x02 /* Q_QUOTAOFF in progress */
#define QTF_64BIT 0x04 /* 64-bit quota file */
/* Convert mount ptr to ufsmount ptr. */
#define VFSTOUFS(mp) ((struct ufsmount *)((mp)->mnt_data))

View File

@ -6,4 +6,7 @@ MAN= edquota.8
WARNS?= 4
DPADD= ${LIBUTIL}
LDADD= -lutil
.include <bsd.prog.mk>

View File

@ -55,17 +55,21 @@ __FBSDID("$FreeBSD$");
#include <sys/mount.h>
#include <sys/wait.h>
#include <ufs/ufs/quota.h>
#include <ctype.h>
#include <err.h>
#include <errno.h>
#include <fstab.h>
#include <grp.h>
#include <inttypes.h>
#include <libutil.h>
#include <pwd.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "pathnames.h"
const char *qfname = QUOTAFILENAME;
@ -102,11 +106,11 @@ main(int argc, char *argv[])
{
struct quotause *qup, *protoprivs, *curprivs;
long id, protoid;
long long lim;
uintmax_t lim;
int i, quotatype, range, tmpfd;
uid_t startuid, enduid;
u_int32_t *limp;
char *protoname, *cp, *oldoptarg;
u_int64_t *limp;
char *protoname, *cp, *oldoptarg, *end;
int eflag = 0, tflag = 0, pflag = 0, ch;
char *fspath = NULL;
char buf[MAXLOGNAME];
@ -172,11 +176,10 @@ main(int argc, char *argv[])
break; /* XXX: report an error */
}
if (limp != NULL) {
lim = strtoll(cp, NULL, 10);
if (lim < 0 || lim > UINT_MAX)
errx(1, "invalid limit value: "
"%lld", lim);
*limp = (u_int32_t)lim;
lim = strtoumax(cp, &end, 10);
if (end == cp || *end != '\0' || lim > UINT64_MAX)
errx(1, "invalid limit: %s", cp);
*limp = (u_int64_t)lim;
}
}
qup->dqblk.dqb_bsoftlimit =
@ -331,10 +334,11 @@ getentry(const char *name, int quotatype)
struct quotause *
getprivs(long id, int quotatype, char *fspath)
{
struct quotafile *qf;
struct fstab *fs;
struct quotause *qup, *quptail;
struct quotause *quphead;
int qcmd, qupsize, fd;
int qcmd, qupsize;
char *qfpathname;
static int warned = 0;
@ -358,46 +362,19 @@ getprivs(long id, int quotatype, char *fspath)
warnx("warning: quotas are not compiled into this kernel");
sleep(3);
}
if ((fd = open(qfpathname, O_RDONLY)) < 0) {
fd = open(qfpathname, O_RDWR|O_CREAT, 0640);
if (fd < 0 && errno != ENOENT) {
warn("%s", qfpathname);
free(qup);
continue;
}
warnx("creating quota file %s", qfpathname);
sleep(3);
(void) fchown(fd, getuid(),
getentry(quotagroup, GRPQUOTA));
(void) fchmod(fd, 0640);
}
if (lseek(fd, (off_t)id * sizeof(struct dqblk),
L_SET) < 0) {
warn("seek error on %s", qfpathname);
close(fd);
if ((qf = quota_open(qfpathname)) == NULL &&
(qf = quota_create(qfpathname)) == NULL) {
warn("%s", qfpathname);
free(qup);
continue;
}
switch (read(fd, &qup->dqblk, sizeof(struct dqblk))) {
case 0: /* EOF */
/*
* Convert implicit 0 quota (EOF)
* into an explicit one (zero'ed dqblk)
*/
bzero((caddr_t)&qup->dqblk,
sizeof(struct dqblk));
break;
case sizeof(struct dqblk): /* OK */
break;
default: /* ERROR */
if (quota_read(qf, &qup->dqblk, id) != 0) {
warn("read error in %s", qfpathname);
close(fd);
quota_close(qf);
free(qup);
continue;
}
close(fd);
quota_close(qf);
}
strcpy(qup->qfname, qfpathname);
strcpy(qup->fsname, fs->fs_file);
@ -418,38 +395,22 @@ getprivs(long id, int quotatype, char *fspath)
void
putprivs(long id, int quotatype, struct quotause *quplist)
{
struct quotafile *qf;
struct quotause *qup;
int qcmd, fd;
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 ((fd = open(qup->qfname, O_RDWR)) < 0) {
if ((qf = quota_open(qup->qfname)) == NULL) {
warn("%s", qup->qfname);
continue;
}
if (lseek(fd, (off_t)id * sizeof(struct dqblk), L_SET) < 0) {
warn("seek error on %s", qup->qfname);
close(fd);
continue;
}
switch (read(fd, &dqbuf, sizeof(struct dqblk))) {
case 0: /* EOF */
/*
* Convert implicit 0 quota (EOF)
* into an explicit one (zero'ed dqblk)
*/
bzero(&dqbuf, sizeof(struct dqblk));
break;
case sizeof(struct dqblk): /* OK */
break;
default: /* ERROR */
if (quota_read(qf, &dqbuf, id) != 0) {
warn("read error in %s", qup->qfname);
close(fd);
quota_close(qf);
continue;
}
/*
@ -474,16 +435,10 @@ putprivs(long id, int quotatype, struct quotause *quplist)
qup->dqblk.dqb_itime = 0;
qup->dqblk.dqb_curinodes = dqbuf.dqb_curinodes;
qup->dqblk.dqb_curblocks = dqbuf.dqb_curblocks;
if (lseek(fd, (off_t)id * sizeof(struct dqblk), L_SET) < 0) {
warn("seek error on %s", qup->qfname);
close(fd);
continue;
}
if (write(fd, &qup->dqblk, sizeof (struct dqblk)) !=
sizeof (struct dqblk)) {
if (quota_write(qf, &qup->dqblk, id) != 0) {
warn("%s", qup->qfname);
}
close(fd);
}
quota_close(qf);
}
}