xref: /freebsd/lib/libutil/quotafile.c (revision e525d16a)
11b3515f3SDag-Erling Smørgrav /*-
21b3515f3SDag-Erling Smørgrav  * Copyright (c) 2008 Dag-Erling Coïdan Smørgrav
38bd6a3abSKirk McKusick  * Copyright (c) 2008 Marshall Kirk McKusick
41b3515f3SDag-Erling Smørgrav  * All rights reserved.
51b3515f3SDag-Erling Smørgrav  *
61b3515f3SDag-Erling Smørgrav  * Redistribution and use in source and binary forms, with or without
71b3515f3SDag-Erling Smørgrav  * modification, are permitted provided that the following conditions
81b3515f3SDag-Erling Smørgrav  * are met:
91b3515f3SDag-Erling Smørgrav  * 1. Redistributions of source code must retain the above copyright
101b3515f3SDag-Erling Smørgrav  *    notice, this list of conditions and the following disclaimer
111b3515f3SDag-Erling Smørgrav  *    in this position and unchanged.
121b3515f3SDag-Erling Smørgrav  * 2. Redistributions in binary form must reproduce the above copyright
131b3515f3SDag-Erling Smørgrav  *    notice, this list of conditions and the following disclaimer in the
141b3515f3SDag-Erling Smørgrav  *    documentation and/or other materials provided with the distribution.
151b3515f3SDag-Erling Smørgrav  *
161b3515f3SDag-Erling Smørgrav  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
171b3515f3SDag-Erling Smørgrav  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
181b3515f3SDag-Erling Smørgrav  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
191b3515f3SDag-Erling Smørgrav  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
201b3515f3SDag-Erling Smørgrav  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
211b3515f3SDag-Erling Smørgrav  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
221b3515f3SDag-Erling Smørgrav  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
231b3515f3SDag-Erling Smørgrav  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
241b3515f3SDag-Erling Smørgrav  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
251b3515f3SDag-Erling Smørgrav  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
261b3515f3SDag-Erling Smørgrav  * SUCH DAMAGE.
271b3515f3SDag-Erling Smørgrav  *
281b3515f3SDag-Erling Smørgrav  * $FreeBSD$
291b3515f3SDag-Erling Smørgrav  */
301b3515f3SDag-Erling Smørgrav 
311b3515f3SDag-Erling Smørgrav #include <sys/types.h>
321b3515f3SDag-Erling Smørgrav #include <sys/endian.h>
33916e406eSKirk McKusick #include <sys/mount.h>
341b3515f3SDag-Erling Smørgrav #include <sys/stat.h>
351b3515f3SDag-Erling Smørgrav 
361b3515f3SDag-Erling Smørgrav #include <ufs/ufs/quota.h>
371b3515f3SDag-Erling Smørgrav 
381b3515f3SDag-Erling Smørgrav #include <errno.h>
391b3515f3SDag-Erling Smørgrav #include <fcntl.h>
40916e406eSKirk McKusick #include <fstab.h>
411b3515f3SDag-Erling Smørgrav #include <grp.h>
421b3515f3SDag-Erling Smørgrav #include <pwd.h>
431b3515f3SDag-Erling Smørgrav #include <libutil.h>
441b3515f3SDag-Erling Smørgrav #include <stdint.h>
45916e406eSKirk McKusick #include <stdio.h>
461b3515f3SDag-Erling Smørgrav #include <stdlib.h>
471b3515f3SDag-Erling Smørgrav #include <string.h>
481b3515f3SDag-Erling Smørgrav #include <unistd.h>
491b3515f3SDag-Erling Smørgrav 
501b3515f3SDag-Erling Smørgrav struct quotafile {
518bd6a3abSKirk McKusick 	int fd;				/* -1 means using quotactl for access */
525666aadbSDag-Erling Smørgrav 	int accmode;			/* access mode */
538bd6a3abSKirk McKusick 	int wordsize;			/* 32-bit or 64-bit limits */
548bd6a3abSKirk McKusick 	int quotatype;			/* USRQUOTA or GRPQUOTA */
555666aadbSDag-Erling Smørgrav 	dev_t dev;			/* device */
568bd6a3abSKirk McKusick 	char fsname[MAXPATHLEN + 1];	/* mount point of filesystem */
578bd6a3abSKirk McKusick 	char qfname[MAXPATHLEN + 1];	/* quota file if not using quotactl */
581b3515f3SDag-Erling Smørgrav };
591b3515f3SDag-Erling Smørgrav 
60916e406eSKirk McKusick static const char *qfextension[] = INITQFNAMES;
61916e406eSKirk McKusick 
628bd6a3abSKirk McKusick /*
638bd6a3abSKirk McKusick  * Check to see if a particular quota is to be enabled.
648bd6a3abSKirk McKusick  */
658bd6a3abSKirk McKusick static int
668bd6a3abSKirk McKusick hasquota(struct fstab *fs, int type, char *qfnamep, int qfbufsize)
678bd6a3abSKirk McKusick {
688bd6a3abSKirk McKusick 	char *opt;
698bd6a3abSKirk McKusick 	char *cp;
708bd6a3abSKirk McKusick 	struct statfs sfb;
718bd6a3abSKirk McKusick 	char buf[BUFSIZ];
728bd6a3abSKirk McKusick 	static char initname, usrname[100], grpname[100];
738bd6a3abSKirk McKusick 
745666aadbSDag-Erling Smørgrav 	/*
755666aadbSDag-Erling Smørgrav 	 * 1) we only need one of these
765666aadbSDag-Erling Smørgrav 	 * 2) fstab may specify a different filename
775666aadbSDag-Erling Smørgrav 	 */
788bd6a3abSKirk McKusick 	if (!initname) {
798bd6a3abSKirk McKusick 		(void)snprintf(usrname, sizeof(usrname), "%s%s",
808bd6a3abSKirk McKusick 		    qfextension[USRQUOTA], QUOTAFILENAME);
818bd6a3abSKirk McKusick 		(void)snprintf(grpname, sizeof(grpname), "%s%s",
828bd6a3abSKirk McKusick 		    qfextension[GRPQUOTA], QUOTAFILENAME);
838bd6a3abSKirk McKusick 		initname = 1;
848bd6a3abSKirk McKusick 	}
858bd6a3abSKirk McKusick 	strcpy(buf, fs->fs_mntops);
868bd6a3abSKirk McKusick 	for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) {
878bd6a3abSKirk McKusick 		if ((cp = index(opt, '=')))
888bd6a3abSKirk McKusick 			*cp++ = '\0';
898bd6a3abSKirk McKusick 		if (type == USRQUOTA && strcmp(opt, usrname) == 0)
908bd6a3abSKirk McKusick 			break;
918bd6a3abSKirk McKusick 		if (type == GRPQUOTA && strcmp(opt, grpname) == 0)
928bd6a3abSKirk McKusick 			break;
938bd6a3abSKirk McKusick 	}
948bd6a3abSKirk McKusick 	if (!opt)
958bd6a3abSKirk McKusick 		return (0);
968bd6a3abSKirk McKusick 	/*
978bd6a3abSKirk McKusick 	 * Ensure that the filesystem is mounted.
988bd6a3abSKirk McKusick 	 */
998bd6a3abSKirk McKusick 	if (statfs(fs->fs_file, &sfb) != 0 ||
1008bd6a3abSKirk McKusick 	    strcmp(fs->fs_file, sfb.f_mntonname)) {
1018bd6a3abSKirk McKusick 		return (0);
1028bd6a3abSKirk McKusick 	}
1038bd6a3abSKirk McKusick 	if (cp) {
1048bd6a3abSKirk McKusick 		strncpy(qfnamep, cp, qfbufsize);
1058bd6a3abSKirk McKusick 	} else {
1068bd6a3abSKirk McKusick 		(void)snprintf(qfnamep, qfbufsize, "%s/%s.%s", fs->fs_file,
1078bd6a3abSKirk McKusick 		    QUOTAFILENAME, qfextension[type]);
1088bd6a3abSKirk McKusick 	}
1098bd6a3abSKirk McKusick 	return (1);
1108bd6a3abSKirk McKusick }
1118bd6a3abSKirk McKusick 
1121b3515f3SDag-Erling Smørgrav struct quotafile *
1138bd6a3abSKirk McKusick quota_open(struct fstab *fs, int quotatype, int openflags)
1141b3515f3SDag-Erling Smørgrav {
1151b3515f3SDag-Erling Smørgrav 	struct quotafile *qf;
1161b3515f3SDag-Erling Smørgrav 	struct dqhdr64 dqh;
1178bd6a3abSKirk McKusick 	struct group *grp;
1185666aadbSDag-Erling Smørgrav 	struct stat st;
1198bd6a3abSKirk McKusick 	int qcmd, serrno;
1201b3515f3SDag-Erling Smørgrav 
1211b3515f3SDag-Erling Smørgrav 	if ((qf = calloc(1, sizeof(*qf))) == NULL)
1221b3515f3SDag-Erling Smørgrav 		return (NULL);
1235666aadbSDag-Erling Smørgrav 	qf->fd = -1;
1248bd6a3abSKirk McKusick 	qf->quotatype = quotatype;
1258bd6a3abSKirk McKusick 	strncpy(qf->fsname, fs->fs_file, sizeof(qf->fsname));
1265666aadbSDag-Erling Smørgrav 	if (stat(qf->fsname, &st) != 0)
1275666aadbSDag-Erling Smørgrav 		goto error;
1285666aadbSDag-Erling Smørgrav 	qf->dev = st.st_dev;
1296197731dSKirk McKusick 	serrno = hasquota(fs, quotatype, qf->qfname, sizeof(qf->qfname));
1308bd6a3abSKirk McKusick 	qcmd = QCMD(Q_GETQUOTA, quotatype);
1318bd6a3abSKirk McKusick 	if (quotactl(fs->fs_file, qcmd, 0, &dqh) == 0) {
1328bd6a3abSKirk McKusick 		qf->wordsize = 64;
1338bd6a3abSKirk McKusick 		return (qf);
1348bd6a3abSKirk McKusick 	}
1356197731dSKirk McKusick 	if (serrno == 0) {
1368bd6a3abSKirk McKusick 		errno = EOPNOTSUPP;
1375666aadbSDag-Erling Smørgrav 		goto error;
1388bd6a3abSKirk McKusick 	}
1395666aadbSDag-Erling Smørgrav 	qf->accmode = openflags & O_ACCMODE;
1405666aadbSDag-Erling Smørgrav 	if ((qf->fd = open(qf->qfname, qf->accmode)) < 0 &&
1415666aadbSDag-Erling Smørgrav 	    (openflags & O_CREAT) != O_CREAT)
1425666aadbSDag-Erling Smørgrav 		goto error;
1438bd6a3abSKirk McKusick 	/* File open worked, so process it */
1448bd6a3abSKirk McKusick 	if (qf->fd != -1) {
1458bd6a3abSKirk McKusick 		qf->wordsize = 32;
1461b3515f3SDag-Erling Smørgrav 		switch (read(qf->fd, &dqh, sizeof(dqh))) {
1471b3515f3SDag-Erling Smørgrav 		case -1:
1485666aadbSDag-Erling Smørgrav 			goto error;
1491b3515f3SDag-Erling Smørgrav 		case sizeof(dqh):
1501b3515f3SDag-Erling Smørgrav 			if (strcmp(dqh.dqh_magic, Q_DQHDR64_MAGIC) != 0) {
1511b3515f3SDag-Erling Smørgrav 				/* no magic, assume 32 bits */
1528bd6a3abSKirk McKusick 				qf->wordsize = 32;
1531b3515f3SDag-Erling Smørgrav 				return (qf);
1541b3515f3SDag-Erling Smørgrav 			}
1551b3515f3SDag-Erling Smørgrav 			if (be32toh(dqh.dqh_version) != Q_DQHDR64_VERSION ||
1561b3515f3SDag-Erling Smørgrav 			    be32toh(dqh.dqh_hdrlen) != sizeof(struct dqhdr64) ||
1571b3515f3SDag-Erling Smørgrav 			    be32toh(dqh.dqh_reclen) != sizeof(struct dqblk64)) {
1581b3515f3SDag-Erling Smørgrav 				/* correct magic, wrong version / lengths */
1591b3515f3SDag-Erling Smørgrav 				errno = EINVAL;
1605666aadbSDag-Erling Smørgrav 				goto error;
1611b3515f3SDag-Erling Smørgrav 			}
1628bd6a3abSKirk McKusick 			qf->wordsize = 64;
1631b3515f3SDag-Erling Smørgrav 			return (qf);
1641b3515f3SDag-Erling Smørgrav 		default:
1658bd6a3abSKirk McKusick 			qf->wordsize = 32;
1661b3515f3SDag-Erling Smørgrav 			return (qf);
1671b3515f3SDag-Erling Smørgrav 		}
1681b3515f3SDag-Erling Smørgrav 		/* not reached */
1691b3515f3SDag-Erling Smørgrav 	}
1705666aadbSDag-Erling Smørgrav 	/* open failed, but O_CREAT was specified, so create a new file */
1715666aadbSDag-Erling Smørgrav 	if ((qf->fd = open(qf->qfname, O_RDWR|O_CREAT|O_TRUNC, 0)) < 0)
1725666aadbSDag-Erling Smørgrav 		goto error;
1738bd6a3abSKirk McKusick 	qf->wordsize = 64;
1741b3515f3SDag-Erling Smørgrav 	memset(&dqh, 0, sizeof(dqh));
1751b3515f3SDag-Erling Smørgrav 	memcpy(dqh.dqh_magic, Q_DQHDR64_MAGIC, sizeof(dqh.dqh_magic));
1761b3515f3SDag-Erling Smørgrav 	dqh.dqh_version = htobe32(Q_DQHDR64_VERSION);
1771b3515f3SDag-Erling Smørgrav 	dqh.dqh_hdrlen = htobe32(sizeof(struct dqhdr64));
1781b3515f3SDag-Erling Smørgrav 	dqh.dqh_reclen = htobe32(sizeof(struct dqblk64));
1795666aadbSDag-Erling Smørgrav 	if (write(qf->fd, &dqh, sizeof(dqh)) != sizeof(dqh))
1805666aadbSDag-Erling Smørgrav 		goto error;
1811b3515f3SDag-Erling Smørgrav 	grp = getgrnam(QUOTAGROUP);
1821b3515f3SDag-Erling Smørgrav 	fchown(qf->fd, 0, grp ? grp->gr_gid : 0);
1831b3515f3SDag-Erling Smørgrav 	fchmod(qf->fd, 0640);
1841b3515f3SDag-Erling Smørgrav 	return (qf);
1855666aadbSDag-Erling Smørgrav error:
1865666aadbSDag-Erling Smørgrav 	serrno = errno;
1875666aadbSDag-Erling Smørgrav 	/* did we have an open file? */
1885666aadbSDag-Erling Smørgrav 	if (qf->fd != -1) {
1895666aadbSDag-Erling Smørgrav 		/* was it one we created ourselves? */
1905666aadbSDag-Erling Smørgrav 		if ((openflags & O_CREAT) == O_CREAT)
1915666aadbSDag-Erling Smørgrav 			unlink(qf->qfname);
1925666aadbSDag-Erling Smørgrav 		close(qf->fd);
1935666aadbSDag-Erling Smørgrav 	}
1945666aadbSDag-Erling Smørgrav 	free(qf);
1955666aadbSDag-Erling Smørgrav 	errno = serrno;
1965666aadbSDag-Erling Smørgrav 	return (NULL);
1971b3515f3SDag-Erling Smørgrav }
1981b3515f3SDag-Erling Smørgrav 
1991b3515f3SDag-Erling Smørgrav void
2001b3515f3SDag-Erling Smørgrav quota_close(struct quotafile *qf)
2011b3515f3SDag-Erling Smørgrav {
2021b3515f3SDag-Erling Smørgrav 
2038bd6a3abSKirk McKusick 	if (qf->fd != -1)
2041b3515f3SDag-Erling Smørgrav 		close(qf->fd);
2051b3515f3SDag-Erling Smørgrav 	free(qf);
2061b3515f3SDag-Erling Smørgrav }
2071b3515f3SDag-Erling Smørgrav 
208e525d16aSKirk McKusick int
209e525d16aSKirk McKusick quota_on(struct quotafile *qf)
210e525d16aSKirk McKusick {
211e525d16aSKirk McKusick 	int qcmd;
212e525d16aSKirk McKusick 
213e525d16aSKirk McKusick 	qcmd = QCMD(Q_QUOTAON, qf->quotatype);
214e525d16aSKirk McKusick 	return (quotactl(qf->fsname, qcmd, 0, qf->qfname));
215e525d16aSKirk McKusick }
216e525d16aSKirk McKusick 
217e525d16aSKirk McKusick int
218e525d16aSKirk McKusick quota_off(struct quotafile *qf)
219e525d16aSKirk McKusick {
220e525d16aSKirk McKusick 
221e525d16aSKirk McKusick 	return (quotactl(qf->fsname, QCMD(Q_QUOTAOFF, qf->quotatype), 0, 0));
222e525d16aSKirk McKusick }
223e525d16aSKirk McKusick 
2245666aadbSDag-Erling Smørgrav const char *
2255666aadbSDag-Erling Smørgrav quota_fsname(const struct quotafile *qf)
2265666aadbSDag-Erling Smørgrav {
2275666aadbSDag-Erling Smørgrav 
2285666aadbSDag-Erling Smørgrav 	return (qf->fsname);
2295666aadbSDag-Erling Smørgrav }
2305666aadbSDag-Erling Smørgrav 
2315666aadbSDag-Erling Smørgrav const char *
2325666aadbSDag-Erling Smørgrav quota_qfname(const struct quotafile *qf)
2335666aadbSDag-Erling Smørgrav {
2345666aadbSDag-Erling Smørgrav 
2355666aadbSDag-Erling Smørgrav 	return (qf->qfname);
2365666aadbSDag-Erling Smørgrav }
2375666aadbSDag-Erling Smørgrav 
2385666aadbSDag-Erling Smørgrav int
2395666aadbSDag-Erling Smørgrav quota_check_path(const struct quotafile *qf, const char *path)
2405666aadbSDag-Erling Smørgrav {
2415666aadbSDag-Erling Smørgrav 	struct stat st;
2425666aadbSDag-Erling Smørgrav 
2435666aadbSDag-Erling Smørgrav 	if (stat(path, &st) == -1)
2445666aadbSDag-Erling Smørgrav 		return (-1);
2455666aadbSDag-Erling Smørgrav 	return (st.st_dev == qf->dev);
2465666aadbSDag-Erling Smørgrav }
2475666aadbSDag-Erling Smørgrav 
2486197731dSKirk McKusick int
2496197731dSKirk McKusick quota_maxid(struct quotafile *qf)
2506197731dSKirk McKusick {
2516197731dSKirk McKusick 	struct stat st;
2526197731dSKirk McKusick 
2536197731dSKirk McKusick 	if (stat(qf->qfname, &st) < 0)
2546197731dSKirk McKusick 		return (0);
2556197731dSKirk McKusick 	switch (qf->wordsize) {
2566197731dSKirk McKusick 	case 32:
2576197731dSKirk McKusick 		return (st.st_size / sizeof(struct dqblk32));
2586197731dSKirk McKusick 	case 64:
2596197731dSKirk McKusick 		return (st.st_size / sizeof(struct dqblk64) - 1);
2606197731dSKirk McKusick 	default:
2616197731dSKirk McKusick 		return (0);
2626197731dSKirk McKusick 	}
2636197731dSKirk McKusick 	/* not reached */
2646197731dSKirk McKusick }
2656197731dSKirk McKusick 
2661b3515f3SDag-Erling Smørgrav static int
2671b3515f3SDag-Erling Smørgrav quota_read32(struct quotafile *qf, struct dqblk *dqb, int id)
2681b3515f3SDag-Erling Smørgrav {
2691b3515f3SDag-Erling Smørgrav 	struct dqblk32 dqb32;
2701b3515f3SDag-Erling Smørgrav 	off_t off;
2711b3515f3SDag-Erling Smørgrav 
2721b3515f3SDag-Erling Smørgrav 	off = id * sizeof(struct dqblk32);
2731b3515f3SDag-Erling Smørgrav 	if (lseek(qf->fd, off, SEEK_SET) == -1)
2741b3515f3SDag-Erling Smørgrav 		return (-1);
2751b3515f3SDag-Erling Smørgrav 	switch (read(qf->fd, &dqb32, sizeof(dqb32))) {
2761b3515f3SDag-Erling Smørgrav 	case 0:
2776197731dSKirk McKusick 		memset(dqb, 0, sizeof(*dqb));
2781b3515f3SDag-Erling Smørgrav 		return (0);
2791b3515f3SDag-Erling Smørgrav 	case sizeof(dqb32):
2801b3515f3SDag-Erling Smørgrav 		dqb->dqb_bhardlimit = dqb32.dqb_bhardlimit;
2811b3515f3SDag-Erling Smørgrav 		dqb->dqb_bsoftlimit = dqb32.dqb_bsoftlimit;
2821b3515f3SDag-Erling Smørgrav 		dqb->dqb_curblocks = dqb32.dqb_curblocks;
2831b3515f3SDag-Erling Smørgrav 		dqb->dqb_ihardlimit = dqb32.dqb_ihardlimit;
2841b3515f3SDag-Erling Smørgrav 		dqb->dqb_isoftlimit = dqb32.dqb_isoftlimit;
2851b3515f3SDag-Erling Smørgrav 		dqb->dqb_curinodes = dqb32.dqb_curinodes;
2861b3515f3SDag-Erling Smørgrav 		dqb->dqb_btime = dqb32.dqb_btime;
2871b3515f3SDag-Erling Smørgrav 		dqb->dqb_itime = dqb32.dqb_itime;
2881b3515f3SDag-Erling Smørgrav 		return (0);
2891b3515f3SDag-Erling Smørgrav 	default:
2901b3515f3SDag-Erling Smørgrav 		return (-1);
2911b3515f3SDag-Erling Smørgrav 	}
2921b3515f3SDag-Erling Smørgrav }
2931b3515f3SDag-Erling Smørgrav 
2941b3515f3SDag-Erling Smørgrav static int
2951b3515f3SDag-Erling Smørgrav quota_read64(struct quotafile *qf, struct dqblk *dqb, int id)
2961b3515f3SDag-Erling Smørgrav {
2971b3515f3SDag-Erling Smørgrav 	struct dqblk64 dqb64;
2981b3515f3SDag-Erling Smørgrav 	off_t off;
2991b3515f3SDag-Erling Smørgrav 
3001b3515f3SDag-Erling Smørgrav 	off = sizeof(struct dqhdr64) + id * sizeof(struct dqblk64);
3011b3515f3SDag-Erling Smørgrav 	if (lseek(qf->fd, off, SEEK_SET) == -1)
3021b3515f3SDag-Erling Smørgrav 		return (-1);
3031b3515f3SDag-Erling Smørgrav 	switch (read(qf->fd, &dqb64, sizeof(dqb64))) {
3041b3515f3SDag-Erling Smørgrav 	case 0:
3056197731dSKirk McKusick 		memset(dqb, 0, sizeof(*dqb));
3061b3515f3SDag-Erling Smørgrav 		return (0);
3071b3515f3SDag-Erling Smørgrav 	case sizeof(dqb64):
3081b3515f3SDag-Erling Smørgrav 		dqb->dqb_bhardlimit = be64toh(dqb64.dqb_bhardlimit);
3091b3515f3SDag-Erling Smørgrav 		dqb->dqb_bsoftlimit = be64toh(dqb64.dqb_bsoftlimit);
3101b3515f3SDag-Erling Smørgrav 		dqb->dqb_curblocks = be64toh(dqb64.dqb_curblocks);
3111b3515f3SDag-Erling Smørgrav 		dqb->dqb_ihardlimit = be64toh(dqb64.dqb_ihardlimit);
3121b3515f3SDag-Erling Smørgrav 		dqb->dqb_isoftlimit = be64toh(dqb64.dqb_isoftlimit);
3131b3515f3SDag-Erling Smørgrav 		dqb->dqb_curinodes = be64toh(dqb64.dqb_curinodes);
3141b3515f3SDag-Erling Smørgrav 		dqb->dqb_btime = be64toh(dqb64.dqb_btime);
3151b3515f3SDag-Erling Smørgrav 		dqb->dqb_itime = be64toh(dqb64.dqb_itime);
3161b3515f3SDag-Erling Smørgrav 		return (0);
3171b3515f3SDag-Erling Smørgrav 	default:
3181b3515f3SDag-Erling Smørgrav 		return (-1);
3191b3515f3SDag-Erling Smørgrav 	}
3201b3515f3SDag-Erling Smørgrav }
3211b3515f3SDag-Erling Smørgrav 
3221b3515f3SDag-Erling Smørgrav int
3231b3515f3SDag-Erling Smørgrav quota_read(struct quotafile *qf, struct dqblk *dqb, int id)
3241b3515f3SDag-Erling Smørgrav {
3258bd6a3abSKirk McKusick 	int qcmd;
3261b3515f3SDag-Erling Smørgrav 
3278bd6a3abSKirk McKusick 	if (qf->fd == -1) {
3288bd6a3abSKirk McKusick 		qcmd = QCMD(Q_GETQUOTA, qf->quotatype);
3298bd6a3abSKirk McKusick 		return (quotactl(qf->fsname, qcmd, id, dqb));
3308bd6a3abSKirk McKusick 	}
3318bd6a3abSKirk McKusick 	switch (qf->wordsize) {
3321b3515f3SDag-Erling Smørgrav 	case 32:
333fdd356a8SDag-Erling Smørgrav 		return (quota_read32(qf, dqb, id));
3341b3515f3SDag-Erling Smørgrav 	case 64:
335fdd356a8SDag-Erling Smørgrav 		return (quota_read64(qf, dqb, id));
3361b3515f3SDag-Erling Smørgrav 	default:
3371b3515f3SDag-Erling Smørgrav 		errno = EINVAL;
3381b3515f3SDag-Erling Smørgrav 		return (-1);
3391b3515f3SDag-Erling Smørgrav 	}
3401b3515f3SDag-Erling Smørgrav 	/* not reached */
3411b3515f3SDag-Erling Smørgrav }
3421b3515f3SDag-Erling Smørgrav 
3431b3515f3SDag-Erling Smørgrav #define CLIP32(u64) ((u64) > UINT32_MAX ? UINT32_MAX : (uint32_t)(u64))
3441b3515f3SDag-Erling Smørgrav 
3451b3515f3SDag-Erling Smørgrav static int
3461b3515f3SDag-Erling Smørgrav quota_write32(struct quotafile *qf, const struct dqblk *dqb, int id)
3471b3515f3SDag-Erling Smørgrav {
3481b3515f3SDag-Erling Smørgrav 	struct dqblk32 dqb32;
3491b3515f3SDag-Erling Smørgrav 	off_t off;
3501b3515f3SDag-Erling Smørgrav 
3511b3515f3SDag-Erling Smørgrav 	dqb32.dqb_bhardlimit = CLIP32(dqb->dqb_bhardlimit);
3521b3515f3SDag-Erling Smørgrav 	dqb32.dqb_bsoftlimit = CLIP32(dqb->dqb_bsoftlimit);
3531b3515f3SDag-Erling Smørgrav 	dqb32.dqb_curblocks = CLIP32(dqb->dqb_curblocks);
3541b3515f3SDag-Erling Smørgrav 	dqb32.dqb_ihardlimit = CLIP32(dqb->dqb_ihardlimit);
3551b3515f3SDag-Erling Smørgrav 	dqb32.dqb_isoftlimit = CLIP32(dqb->dqb_isoftlimit);
3561b3515f3SDag-Erling Smørgrav 	dqb32.dqb_curinodes = CLIP32(dqb->dqb_curinodes);
3571b3515f3SDag-Erling Smørgrav 	dqb32.dqb_btime = CLIP32(dqb->dqb_btime);
3581b3515f3SDag-Erling Smørgrav 	dqb32.dqb_itime = CLIP32(dqb->dqb_itime);
3591b3515f3SDag-Erling Smørgrav 
3601b3515f3SDag-Erling Smørgrav 	off = id * sizeof(struct dqblk32);
3611b3515f3SDag-Erling Smørgrav 	if (lseek(qf->fd, off, SEEK_SET) == -1)
3621b3515f3SDag-Erling Smørgrav 		return (-1);
3638bd6a3abSKirk McKusick 	if (write(qf->fd, &dqb32, sizeof(dqb32)) == sizeof(dqb32))
3648bd6a3abSKirk McKusick 		return (0);
3658bd6a3abSKirk McKusick 	return (-1);
3661b3515f3SDag-Erling Smørgrav }
3671b3515f3SDag-Erling Smørgrav 
3681b3515f3SDag-Erling Smørgrav static int
3691b3515f3SDag-Erling Smørgrav quota_write64(struct quotafile *qf, const struct dqblk *dqb, int id)
3701b3515f3SDag-Erling Smørgrav {
3711b3515f3SDag-Erling Smørgrav 	struct dqblk64 dqb64;
3721b3515f3SDag-Erling Smørgrav 	off_t off;
3731b3515f3SDag-Erling Smørgrav 
3741b3515f3SDag-Erling Smørgrav 	dqb64.dqb_bhardlimit = htobe64(dqb->dqb_bhardlimit);
3751b3515f3SDag-Erling Smørgrav 	dqb64.dqb_bsoftlimit = htobe64(dqb->dqb_bsoftlimit);
3761b3515f3SDag-Erling Smørgrav 	dqb64.dqb_curblocks = htobe64(dqb->dqb_curblocks);
3771b3515f3SDag-Erling Smørgrav 	dqb64.dqb_ihardlimit = htobe64(dqb->dqb_ihardlimit);
3781b3515f3SDag-Erling Smørgrav 	dqb64.dqb_isoftlimit = htobe64(dqb->dqb_isoftlimit);
3791b3515f3SDag-Erling Smørgrav 	dqb64.dqb_curinodes = htobe64(dqb->dqb_curinodes);
3801b3515f3SDag-Erling Smørgrav 	dqb64.dqb_btime = htobe64(dqb->dqb_btime);
3811b3515f3SDag-Erling Smørgrav 	dqb64.dqb_itime = htobe64(dqb->dqb_itime);
3821b3515f3SDag-Erling Smørgrav 
3831b3515f3SDag-Erling Smørgrav 	off = sizeof(struct dqhdr64) + id * sizeof(struct dqblk64);
3841b3515f3SDag-Erling Smørgrav 	if (lseek(qf->fd, off, SEEK_SET) == -1)
3851b3515f3SDag-Erling Smørgrav 		return (-1);
3868bd6a3abSKirk McKusick 	if (write(qf->fd, &dqb64, sizeof(dqb64)) == sizeof(dqb64))
3878bd6a3abSKirk McKusick 		return (0);
3888bd6a3abSKirk McKusick 	return (-1);
3891b3515f3SDag-Erling Smørgrav }
3901b3515f3SDag-Erling Smørgrav 
3911b3515f3SDag-Erling Smørgrav int
3928bd6a3abSKirk McKusick quota_write_usage(struct quotafile *qf, struct dqblk *dqb, int id)
3931b3515f3SDag-Erling Smørgrav {
3948bd6a3abSKirk McKusick 	struct dqblk dqbuf;
3958bd6a3abSKirk McKusick 	int qcmd;
3961b3515f3SDag-Erling Smørgrav 
3975666aadbSDag-Erling Smørgrav 	if ((qf->accmode & O_RDWR) != O_RDWR) {
3985666aadbSDag-Erling Smørgrav 		errno = EBADF;
3995666aadbSDag-Erling Smørgrav 		return (-1);
4005666aadbSDag-Erling Smørgrav 	}
4018bd6a3abSKirk McKusick 	if (qf->fd == -1) {
4028bd6a3abSKirk McKusick 		qcmd = QCMD(Q_SETUSE, qf->quotatype);
4038bd6a3abSKirk McKusick 		return (quotactl(qf->fsname, qcmd, id, dqb));
4048bd6a3abSKirk McKusick 	}
4058bd6a3abSKirk McKusick 	/*
4068bd6a3abSKirk McKusick 	 * Have to do read-modify-write of quota in file.
4078bd6a3abSKirk McKusick 	 */
4088bd6a3abSKirk McKusick 	if (quota_read(qf, &dqbuf, id) != 0)
4098bd6a3abSKirk McKusick 		return (-1);
4108bd6a3abSKirk McKusick 	/*
4118bd6a3abSKirk McKusick 	 * Reset time limit if have a soft limit and were
4128bd6a3abSKirk McKusick 	 * previously under it, but are now over it.
4138bd6a3abSKirk McKusick 	 */
4148bd6a3abSKirk McKusick 	if (dqbuf.dqb_bsoftlimit && id != 0 &&
4158bd6a3abSKirk McKusick 	    dqbuf.dqb_curblocks < dqbuf.dqb_bsoftlimit &&
4168bd6a3abSKirk McKusick 	    dqb->dqb_curblocks >= dqbuf.dqb_bsoftlimit)
4178bd6a3abSKirk McKusick 		dqbuf.dqb_btime = 0;
4188bd6a3abSKirk McKusick 	if (dqbuf.dqb_isoftlimit && id != 0 &&
4198bd6a3abSKirk McKusick 	    dqbuf.dqb_curinodes < dqbuf.dqb_isoftlimit &&
4208bd6a3abSKirk McKusick 	    dqb->dqb_curinodes >= dqbuf.dqb_isoftlimit)
4218bd6a3abSKirk McKusick 		dqbuf.dqb_itime = 0;
4228bd6a3abSKirk McKusick 	dqbuf.dqb_curinodes = dqb->dqb_curinodes;
4238bd6a3abSKirk McKusick 	dqbuf.dqb_curblocks = dqb->dqb_curblocks;
4248bd6a3abSKirk McKusick 	/*
4258bd6a3abSKirk McKusick 	 * Write it back.
4268bd6a3abSKirk McKusick 	 */
4278bd6a3abSKirk McKusick 	switch (qf->wordsize) {
4288bd6a3abSKirk McKusick 	case 32:
429fdd356a8SDag-Erling Smørgrav 		return (quota_write32(qf, &dqbuf, id));
4308bd6a3abSKirk McKusick 	case 64:
431fdd356a8SDag-Erling Smørgrav 		return (quota_write64(qf, &dqbuf, id));
4328bd6a3abSKirk McKusick 	default:
4338bd6a3abSKirk McKusick 		errno = EINVAL;
4348bd6a3abSKirk McKusick 		return (-1);
4358bd6a3abSKirk McKusick 	}
4368bd6a3abSKirk McKusick 	/* not reached */
4378bd6a3abSKirk McKusick }
4388bd6a3abSKirk McKusick 
4398bd6a3abSKirk McKusick int
4408bd6a3abSKirk McKusick quota_write_limits(struct quotafile *qf, struct dqblk *dqb, int id)
4418bd6a3abSKirk McKusick {
4428bd6a3abSKirk McKusick 	struct dqblk dqbuf;
4438bd6a3abSKirk McKusick 	int qcmd;
4448bd6a3abSKirk McKusick 
4455666aadbSDag-Erling Smørgrav 	if ((qf->accmode & O_RDWR) != O_RDWR) {
4465666aadbSDag-Erling Smørgrav 		errno = EBADF;
4475666aadbSDag-Erling Smørgrav 		return (-1);
4485666aadbSDag-Erling Smørgrav 	}
4498bd6a3abSKirk McKusick 	if (qf->fd == -1) {
4508bd6a3abSKirk McKusick 		qcmd = QCMD(Q_SETQUOTA, qf->quotatype);
4518bd6a3abSKirk McKusick 		return (quotactl(qf->fsname, qcmd, id, dqb));
4528bd6a3abSKirk McKusick 	}
4538bd6a3abSKirk McKusick 	/*
4548bd6a3abSKirk McKusick 	 * Have to do read-modify-write of quota in file.
4558bd6a3abSKirk McKusick 	 */
4568bd6a3abSKirk McKusick 	if (quota_read(qf, &dqbuf, id) != 0)
4578bd6a3abSKirk McKusick 		return (-1);
4588bd6a3abSKirk McKusick 	/*
4598bd6a3abSKirk McKusick 	 * Reset time limit if have a soft limit and were
4608bd6a3abSKirk McKusick 	 * previously under it, but are now over it
4618bd6a3abSKirk McKusick 	 * or if there previously was no soft limit, but
4628bd6a3abSKirk McKusick 	 * now have one and are over it.
4638bd6a3abSKirk McKusick 	 */
4648bd6a3abSKirk McKusick 	if (dqbuf.dqb_bsoftlimit && id != 0 &&
4658bd6a3abSKirk McKusick 	    dqbuf.dqb_curblocks < dqbuf.dqb_bsoftlimit &&
4668bd6a3abSKirk McKusick 	    dqbuf.dqb_curblocks >= dqb->dqb_bsoftlimit)
4678bd6a3abSKirk McKusick 		dqb->dqb_btime = 0;
4688bd6a3abSKirk McKusick 	if (dqbuf.dqb_bsoftlimit == 0 && id != 0 &&
4698bd6a3abSKirk McKusick 	    dqb->dqb_bsoftlimit > 0 &&
4708bd6a3abSKirk McKusick 	    dqbuf.dqb_curblocks >= dqb->dqb_bsoftlimit)
4718bd6a3abSKirk McKusick 		dqb->dqb_btime = 0;
4728bd6a3abSKirk McKusick 	if (dqbuf.dqb_isoftlimit && id != 0 &&
4738bd6a3abSKirk McKusick 	    dqbuf.dqb_curinodes < dqbuf.dqb_isoftlimit &&
4748bd6a3abSKirk McKusick 	    dqbuf.dqb_curinodes >= dqb->dqb_isoftlimit)
4758bd6a3abSKirk McKusick 		dqb->dqb_itime = 0;
4768bd6a3abSKirk McKusick 	if (dqbuf.dqb_isoftlimit == 0 && id !=0 &&
4778bd6a3abSKirk McKusick 	    dqb->dqb_isoftlimit > 0 &&
4788bd6a3abSKirk McKusick 	    dqbuf.dqb_curinodes >= dqb->dqb_isoftlimit)
4798bd6a3abSKirk McKusick 		dqb->dqb_itime = 0;
4808bd6a3abSKirk McKusick 	dqb->dqb_curinodes = dqbuf.dqb_curinodes;
4818bd6a3abSKirk McKusick 	dqb->dqb_curblocks = dqbuf.dqb_curblocks;
4828bd6a3abSKirk McKusick 	/*
4838bd6a3abSKirk McKusick 	 * Write it back.
4848bd6a3abSKirk McKusick 	 */
4858bd6a3abSKirk McKusick 	switch (qf->wordsize) {
4861b3515f3SDag-Erling Smørgrav 	case 32:
487fdd356a8SDag-Erling Smørgrav 		return (quota_write32(qf, dqb, id));
4881b3515f3SDag-Erling Smørgrav 	case 64:
489fdd356a8SDag-Erling Smørgrav 		return (quota_write64(qf, dqb, id));
4901b3515f3SDag-Erling Smørgrav 	default:
4911b3515f3SDag-Erling Smørgrav 		errno = EINVAL;
4921b3515f3SDag-Erling Smørgrav 		return (-1);
4931b3515f3SDag-Erling Smørgrav 	}
4941b3515f3SDag-Erling Smørgrav 	/* not reached */
4951b3515f3SDag-Erling Smørgrav }
496