xref: /freebsd/lib/libutil/quotafile.c (revision aad5531e716d37f9e0b0a0ee50499a33e63a1337)
11b3515f3SDag-Erling Smørgrav /*-
25e53a4f9SPedro F. Giffuni  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
35e53a4f9SPedro F. Giffuni  *
41b3515f3SDag-Erling Smørgrav  * Copyright (c) 2008 Dag-Erling Coïdan Smørgrav
58bd6a3abSKirk McKusick  * Copyright (c) 2008 Marshall Kirk McKusick
61b3515f3SDag-Erling Smørgrav  * All rights reserved.
71b3515f3SDag-Erling Smørgrav  *
81b3515f3SDag-Erling Smørgrav  * Redistribution and use in source and binary forms, with or without
91b3515f3SDag-Erling Smørgrav  * modification, are permitted provided that the following conditions
101b3515f3SDag-Erling Smørgrav  * are met:
111b3515f3SDag-Erling Smørgrav  * 1. Redistributions of source code must retain the above copyright
121b3515f3SDag-Erling Smørgrav  *    notice, this list of conditions and the following disclaimer
131b3515f3SDag-Erling Smørgrav  *    in this position and unchanged.
141b3515f3SDag-Erling Smørgrav  * 2. Redistributions in binary form must reproduce the above copyright
151b3515f3SDag-Erling Smørgrav  *    notice, this list of conditions and the following disclaimer in the
161b3515f3SDag-Erling Smørgrav  *    documentation and/or other materials provided with the distribution.
171b3515f3SDag-Erling Smørgrav  *
181b3515f3SDag-Erling Smørgrav  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
191b3515f3SDag-Erling Smørgrav  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
201b3515f3SDag-Erling Smørgrav  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
211b3515f3SDag-Erling Smørgrav  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
221b3515f3SDag-Erling Smørgrav  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
231b3515f3SDag-Erling Smørgrav  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
241b3515f3SDag-Erling Smørgrav  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
251b3515f3SDag-Erling Smørgrav  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
261b3515f3SDag-Erling Smørgrav  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
271b3515f3SDag-Erling Smørgrav  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
281b3515f3SDag-Erling Smørgrav  * SUCH DAMAGE.
291b3515f3SDag-Erling Smørgrav  *
301b3515f3SDag-Erling Smørgrav  * $FreeBSD$
311b3515f3SDag-Erling Smørgrav  */
321b3515f3SDag-Erling Smørgrav 
331b3515f3SDag-Erling Smørgrav #include <sys/types.h>
341b3515f3SDag-Erling Smørgrav #include <sys/endian.h>
35916e406eSKirk McKusick #include <sys/mount.h>
361b3515f3SDag-Erling Smørgrav #include <sys/stat.h>
371b3515f3SDag-Erling Smørgrav 
381b3515f3SDag-Erling Smørgrav #include <ufs/ufs/quota.h>
391b3515f3SDag-Erling Smørgrav 
401b3515f3SDag-Erling Smørgrav #include <errno.h>
411b3515f3SDag-Erling Smørgrav #include <fcntl.h>
42916e406eSKirk McKusick #include <fstab.h>
431b3515f3SDag-Erling Smørgrav #include <grp.h>
441b3515f3SDag-Erling Smørgrav #include <pwd.h>
451b3515f3SDag-Erling Smørgrav #include <libutil.h>
461b3515f3SDag-Erling Smørgrav #include <stdint.h>
47916e406eSKirk McKusick #include <stdio.h>
481b3515f3SDag-Erling Smørgrav #include <stdlib.h>
491b3515f3SDag-Erling Smørgrav #include <string.h>
501b3515f3SDag-Erling Smørgrav #include <unistd.h>
511b3515f3SDag-Erling Smørgrav 
521b3515f3SDag-Erling Smørgrav struct quotafile {
538bd6a3abSKirk McKusick 	int fd;				/* -1 means using quotactl for access */
545666aadbSDag-Erling Smørgrav 	int accmode;			/* access mode */
558bd6a3abSKirk McKusick 	int wordsize;			/* 32-bit or 64-bit limits */
568bd6a3abSKirk McKusick 	int quotatype;			/* USRQUOTA or GRPQUOTA */
575666aadbSDag-Erling Smørgrav 	dev_t dev;			/* device */
588bd6a3abSKirk McKusick 	char fsname[MAXPATHLEN + 1];	/* mount point of filesystem */
598bd6a3abSKirk McKusick 	char qfname[MAXPATHLEN + 1];	/* quota file if not using quotactl */
601b3515f3SDag-Erling Smørgrav };
611b3515f3SDag-Erling Smørgrav 
62916e406eSKirk McKusick static const char *qfextension[] = INITQFNAMES;
63916e406eSKirk McKusick 
648bd6a3abSKirk McKusick /*
658bd6a3abSKirk McKusick  * Check to see if a particular quota is to be enabled.
668bd6a3abSKirk McKusick  */
678bd6a3abSKirk McKusick static int
688bd6a3abSKirk McKusick hasquota(struct fstab *fs, int type, char *qfnamep, int qfbufsize)
698bd6a3abSKirk McKusick {
708bd6a3abSKirk McKusick 	char *opt;
718bd6a3abSKirk McKusick 	char *cp;
728bd6a3abSKirk McKusick 	struct statfs sfb;
738bd6a3abSKirk McKusick 	char buf[BUFSIZ];
748bd6a3abSKirk McKusick 	static char initname, usrname[100], grpname[100];
758bd6a3abSKirk McKusick 
765666aadbSDag-Erling Smørgrav 	/*
775666aadbSDag-Erling Smørgrav 	 * 1) we only need one of these
785666aadbSDag-Erling Smørgrav 	 * 2) fstab may specify a different filename
795666aadbSDag-Erling Smørgrav 	 */
808bd6a3abSKirk McKusick 	if (!initname) {
818bd6a3abSKirk McKusick 		(void)snprintf(usrname, sizeof(usrname), "%s%s",
828bd6a3abSKirk McKusick 		    qfextension[USRQUOTA], QUOTAFILENAME);
838bd6a3abSKirk McKusick 		(void)snprintf(grpname, sizeof(grpname), "%s%s",
848bd6a3abSKirk McKusick 		    qfextension[GRPQUOTA], QUOTAFILENAME);
858bd6a3abSKirk McKusick 		initname = 1;
868bd6a3abSKirk McKusick 	}
878bd6a3abSKirk McKusick 	strcpy(buf, fs->fs_mntops);
888bd6a3abSKirk McKusick 	for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) {
89b3608ae1SEd Schouten 		if ((cp = strchr(opt, '=')))
908bd6a3abSKirk McKusick 			*cp++ = '\0';
918bd6a3abSKirk McKusick 		if (type == USRQUOTA && strcmp(opt, usrname) == 0)
928bd6a3abSKirk McKusick 			break;
938bd6a3abSKirk McKusick 		if (type == GRPQUOTA && strcmp(opt, grpname) == 0)
948bd6a3abSKirk McKusick 			break;
958bd6a3abSKirk McKusick 	}
968bd6a3abSKirk McKusick 	if (!opt)
978bd6a3abSKirk McKusick 		return (0);
988bd6a3abSKirk McKusick 	/*
998bd6a3abSKirk McKusick 	 * Ensure that the filesystem is mounted.
1008bd6a3abSKirk McKusick 	 */
1018bd6a3abSKirk McKusick 	if (statfs(fs->fs_file, &sfb) != 0 ||
1028bd6a3abSKirk McKusick 	    strcmp(fs->fs_file, sfb.f_mntonname)) {
1038bd6a3abSKirk McKusick 		return (0);
1048bd6a3abSKirk McKusick 	}
1058bd6a3abSKirk McKusick 	if (cp) {
1068bd6a3abSKirk McKusick 		strncpy(qfnamep, cp, qfbufsize);
1078bd6a3abSKirk McKusick 	} else {
1088bd6a3abSKirk McKusick 		(void)snprintf(qfnamep, qfbufsize, "%s/%s.%s", fs->fs_file,
1098bd6a3abSKirk McKusick 		    QUOTAFILENAME, qfextension[type]);
1108bd6a3abSKirk McKusick 	}
1118bd6a3abSKirk McKusick 	return (1);
1128bd6a3abSKirk McKusick }
1138bd6a3abSKirk McKusick 
1141b3515f3SDag-Erling Smørgrav struct quotafile *
1158bd6a3abSKirk McKusick quota_open(struct fstab *fs, int quotatype, int openflags)
1161b3515f3SDag-Erling Smørgrav {
1171b3515f3SDag-Erling Smørgrav 	struct quotafile *qf;
1181b3515f3SDag-Erling Smørgrav 	struct dqhdr64 dqh;
1198bd6a3abSKirk McKusick 	struct group *grp;
1205666aadbSDag-Erling Smørgrav 	struct stat st;
1218bd6a3abSKirk McKusick 	int qcmd, serrno;
1221b3515f3SDag-Erling Smørgrav 
1231b3515f3SDag-Erling Smørgrav 	if ((qf = calloc(1, sizeof(*qf))) == NULL)
1241b3515f3SDag-Erling Smørgrav 		return (NULL);
1255666aadbSDag-Erling Smørgrav 	qf->fd = -1;
1268bd6a3abSKirk McKusick 	qf->quotatype = quotatype;
1274238b561SDon Lewis 	strlcpy(qf->fsname, fs->fs_file, sizeof(qf->fsname));
1285666aadbSDag-Erling Smørgrav 	if (stat(qf->fsname, &st) != 0)
1295666aadbSDag-Erling Smørgrav 		goto error;
1305666aadbSDag-Erling Smørgrav 	qf->dev = st.st_dev;
131516ad57bSKirk McKusick 	qcmd = QCMD(Q_GETQUOTASIZE, quotatype);
132516ad57bSKirk McKusick 	if (quotactl(qf->fsname, qcmd, 0, &qf->wordsize) == 0)
1338bd6a3abSKirk McKusick 		return (qf);
134*aad5531eSSean Eric Fagan 	/* We only check the quota file for ufs */
135*aad5531eSSean Eric Fagan 	if (strcmp(fs->fs_vfstype, "ufs")) {
136*aad5531eSSean Eric Fagan 		errno = 0;
137*aad5531eSSean Eric Fagan 		goto error;
138*aad5531eSSean Eric Fagan 	}
139*aad5531eSSean Eric Fagan 	serrno = hasquota(fs, quotatype, qf->qfname, sizeof(qf->qfname));
1406197731dSKirk McKusick 	if (serrno == 0) {
1418bd6a3abSKirk McKusick 		errno = EOPNOTSUPP;
1425666aadbSDag-Erling Smørgrav 		goto error;
1438bd6a3abSKirk McKusick 	}
1445666aadbSDag-Erling Smørgrav 	qf->accmode = openflags & O_ACCMODE;
145d1d4d952SJilles Tjoelker 	if ((qf->fd = open(qf->qfname, qf->accmode|O_CLOEXEC)) < 0 &&
1465666aadbSDag-Erling Smørgrav 	    (openflags & O_CREAT) != O_CREAT)
1475666aadbSDag-Erling Smørgrav 		goto error;
1488bd6a3abSKirk McKusick 	/* File open worked, so process it */
1498bd6a3abSKirk McKusick 	if (qf->fd != -1) {
1508bd6a3abSKirk McKusick 		qf->wordsize = 32;
1511b3515f3SDag-Erling Smørgrav 		switch (read(qf->fd, &dqh, sizeof(dqh))) {
1521b3515f3SDag-Erling Smørgrav 		case -1:
1535666aadbSDag-Erling Smørgrav 			goto error;
1541b3515f3SDag-Erling Smørgrav 		case sizeof(dqh):
1551b3515f3SDag-Erling Smørgrav 			if (strcmp(dqh.dqh_magic, Q_DQHDR64_MAGIC) != 0) {
1561b3515f3SDag-Erling Smørgrav 				/* no magic, assume 32 bits */
1578bd6a3abSKirk McKusick 				qf->wordsize = 32;
1581b3515f3SDag-Erling Smørgrav 				return (qf);
1591b3515f3SDag-Erling Smørgrav 			}
1601b3515f3SDag-Erling Smørgrav 			if (be32toh(dqh.dqh_version) != Q_DQHDR64_VERSION ||
1611b3515f3SDag-Erling Smørgrav 			    be32toh(dqh.dqh_hdrlen) != sizeof(struct dqhdr64) ||
1621b3515f3SDag-Erling Smørgrav 			    be32toh(dqh.dqh_reclen) != sizeof(struct dqblk64)) {
1631b3515f3SDag-Erling Smørgrav 				/* correct magic, wrong version / lengths */
1641b3515f3SDag-Erling Smørgrav 				errno = EINVAL;
1655666aadbSDag-Erling Smørgrav 				goto error;
1661b3515f3SDag-Erling Smørgrav 			}
1678bd6a3abSKirk McKusick 			qf->wordsize = 64;
1681b3515f3SDag-Erling Smørgrav 			return (qf);
1691b3515f3SDag-Erling Smørgrav 		default:
1708bd6a3abSKirk McKusick 			qf->wordsize = 32;
1711b3515f3SDag-Erling Smørgrav 			return (qf);
1721b3515f3SDag-Erling Smørgrav 		}
1731b3515f3SDag-Erling Smørgrav 		/* not reached */
1741b3515f3SDag-Erling Smørgrav 	}
1755666aadbSDag-Erling Smørgrav 	/* open failed, but O_CREAT was specified, so create a new file */
176d1d4d952SJilles Tjoelker 	if ((qf->fd = open(qf->qfname, O_RDWR|O_CREAT|O_TRUNC|O_CLOEXEC, 0)) <
177d1d4d952SJilles Tjoelker 	    0)
1785666aadbSDag-Erling Smørgrav 		goto error;
1798bd6a3abSKirk McKusick 	qf->wordsize = 64;
1801b3515f3SDag-Erling Smørgrav 	memset(&dqh, 0, sizeof(dqh));
1811b3515f3SDag-Erling Smørgrav 	memcpy(dqh.dqh_magic, Q_DQHDR64_MAGIC, sizeof(dqh.dqh_magic));
1821b3515f3SDag-Erling Smørgrav 	dqh.dqh_version = htobe32(Q_DQHDR64_VERSION);
1831b3515f3SDag-Erling Smørgrav 	dqh.dqh_hdrlen = htobe32(sizeof(struct dqhdr64));
1841b3515f3SDag-Erling Smørgrav 	dqh.dqh_reclen = htobe32(sizeof(struct dqblk64));
185c3616249SKirk McKusick 	if (write(qf->fd, &dqh, sizeof(dqh)) != sizeof(dqh)) {
186c3616249SKirk McKusick 		/* it was one we created ourselves */
187c3616249SKirk McKusick 		unlink(qf->qfname);
1885666aadbSDag-Erling Smørgrav 		goto error;
189c3616249SKirk McKusick 	}
1901b3515f3SDag-Erling Smørgrav 	grp = getgrnam(QUOTAGROUP);
1911b3515f3SDag-Erling Smørgrav 	fchown(qf->fd, 0, grp ? grp->gr_gid : 0);
1921b3515f3SDag-Erling Smørgrav 	fchmod(qf->fd, 0640);
1931b3515f3SDag-Erling Smørgrav 	return (qf);
1945666aadbSDag-Erling Smørgrav error:
1955666aadbSDag-Erling Smørgrav 	serrno = errno;
1965666aadbSDag-Erling Smørgrav 	/* did we have an open file? */
197c3616249SKirk McKusick 	if (qf->fd != -1)
1985666aadbSDag-Erling Smørgrav 		close(qf->fd);
1995666aadbSDag-Erling Smørgrav 	free(qf);
2005666aadbSDag-Erling Smørgrav 	errno = serrno;
2015666aadbSDag-Erling Smørgrav 	return (NULL);
2021b3515f3SDag-Erling Smørgrav }
2031b3515f3SDag-Erling Smørgrav 
2041b3515f3SDag-Erling Smørgrav void
2051b3515f3SDag-Erling Smørgrav quota_close(struct quotafile *qf)
2061b3515f3SDag-Erling Smørgrav {
2071b3515f3SDag-Erling Smørgrav 
2088bd6a3abSKirk McKusick 	if (qf->fd != -1)
2091b3515f3SDag-Erling Smørgrav 		close(qf->fd);
2101b3515f3SDag-Erling Smørgrav 	free(qf);
2111b3515f3SDag-Erling Smørgrav }
2121b3515f3SDag-Erling Smørgrav 
213e525d16aSKirk McKusick int
214e525d16aSKirk McKusick quota_on(struct quotafile *qf)
215e525d16aSKirk McKusick {
216e525d16aSKirk McKusick 	int qcmd;
217e525d16aSKirk McKusick 
218e525d16aSKirk McKusick 	qcmd = QCMD(Q_QUOTAON, qf->quotatype);
219e525d16aSKirk McKusick 	return (quotactl(qf->fsname, qcmd, 0, qf->qfname));
220e525d16aSKirk McKusick }
221e525d16aSKirk McKusick 
222e525d16aSKirk McKusick int
223e525d16aSKirk McKusick quota_off(struct quotafile *qf)
224e525d16aSKirk McKusick {
225e525d16aSKirk McKusick 
226e525d16aSKirk McKusick 	return (quotactl(qf->fsname, QCMD(Q_QUOTAOFF, qf->quotatype), 0, 0));
227e525d16aSKirk McKusick }
228e525d16aSKirk McKusick 
2295666aadbSDag-Erling Smørgrav const char *
2305666aadbSDag-Erling Smørgrav quota_fsname(const struct quotafile *qf)
2315666aadbSDag-Erling Smørgrav {
2325666aadbSDag-Erling Smørgrav 
2335666aadbSDag-Erling Smørgrav 	return (qf->fsname);
2345666aadbSDag-Erling Smørgrav }
2355666aadbSDag-Erling Smørgrav 
2365666aadbSDag-Erling Smørgrav const char *
2375666aadbSDag-Erling Smørgrav quota_qfname(const struct quotafile *qf)
2385666aadbSDag-Erling Smørgrav {
2395666aadbSDag-Erling Smørgrav 
2405666aadbSDag-Erling Smørgrav 	return (qf->qfname);
2415666aadbSDag-Erling Smørgrav }
2425666aadbSDag-Erling Smørgrav 
2435666aadbSDag-Erling Smørgrav int
2445666aadbSDag-Erling Smørgrav quota_check_path(const struct quotafile *qf, const char *path)
2455666aadbSDag-Erling Smørgrav {
2465666aadbSDag-Erling Smørgrav 	struct stat st;
2475666aadbSDag-Erling Smørgrav 
2485666aadbSDag-Erling Smørgrav 	if (stat(path, &st) == -1)
2495666aadbSDag-Erling Smørgrav 		return (-1);
2505666aadbSDag-Erling Smørgrav 	return (st.st_dev == qf->dev);
2515666aadbSDag-Erling Smørgrav }
2525666aadbSDag-Erling Smørgrav 
2536197731dSKirk McKusick int
2546197731dSKirk McKusick quota_maxid(struct quotafile *qf)
2556197731dSKirk McKusick {
2566197731dSKirk McKusick 	struct stat st;
257516ad57bSKirk McKusick 	int maxid;
2586197731dSKirk McKusick 
2596197731dSKirk McKusick 	if (stat(qf->qfname, &st) < 0)
2606197731dSKirk McKusick 		return (0);
2616197731dSKirk McKusick 	switch (qf->wordsize) {
2626197731dSKirk McKusick 	case 32:
263516ad57bSKirk McKusick 		maxid = st.st_size / sizeof(struct dqblk32) - 1;
264516ad57bSKirk McKusick 		break;
2656197731dSKirk McKusick 	case 64:
266516ad57bSKirk McKusick 		maxid = st.st_size / sizeof(struct dqblk64) - 2;
267516ad57bSKirk McKusick 		break;
2686197731dSKirk McKusick 	default:
269516ad57bSKirk McKusick 		maxid = 0;
270516ad57bSKirk McKusick 		break;
2716197731dSKirk McKusick 	}
272516ad57bSKirk McKusick 	return (maxid > 0 ? maxid : 0);
2736197731dSKirk McKusick }
2746197731dSKirk McKusick 
2751b3515f3SDag-Erling Smørgrav static int
2761b3515f3SDag-Erling Smørgrav quota_read32(struct quotafile *qf, struct dqblk *dqb, int id)
2771b3515f3SDag-Erling Smørgrav {
2781b3515f3SDag-Erling Smørgrav 	struct dqblk32 dqb32;
2791b3515f3SDag-Erling Smørgrav 	off_t off;
2801b3515f3SDag-Erling Smørgrav 
2811b3515f3SDag-Erling Smørgrav 	off = id * sizeof(struct dqblk32);
2821b3515f3SDag-Erling Smørgrav 	if (lseek(qf->fd, off, SEEK_SET) == -1)
2831b3515f3SDag-Erling Smørgrav 		return (-1);
2841b3515f3SDag-Erling Smørgrav 	switch (read(qf->fd, &dqb32, sizeof(dqb32))) {
2851b3515f3SDag-Erling Smørgrav 	case 0:
2866197731dSKirk McKusick 		memset(dqb, 0, sizeof(*dqb));
2871b3515f3SDag-Erling Smørgrav 		return (0);
2881b3515f3SDag-Erling Smørgrav 	case sizeof(dqb32):
2891b3515f3SDag-Erling Smørgrav 		dqb->dqb_bhardlimit = dqb32.dqb_bhardlimit;
2901b3515f3SDag-Erling Smørgrav 		dqb->dqb_bsoftlimit = dqb32.dqb_bsoftlimit;
2911b3515f3SDag-Erling Smørgrav 		dqb->dqb_curblocks = dqb32.dqb_curblocks;
2921b3515f3SDag-Erling Smørgrav 		dqb->dqb_ihardlimit = dqb32.dqb_ihardlimit;
2931b3515f3SDag-Erling Smørgrav 		dqb->dqb_isoftlimit = dqb32.dqb_isoftlimit;
2941b3515f3SDag-Erling Smørgrav 		dqb->dqb_curinodes = dqb32.dqb_curinodes;
2951b3515f3SDag-Erling Smørgrav 		dqb->dqb_btime = dqb32.dqb_btime;
2961b3515f3SDag-Erling Smørgrav 		dqb->dqb_itime = dqb32.dqb_itime;
2971b3515f3SDag-Erling Smørgrav 		return (0);
2981b3515f3SDag-Erling Smørgrav 	default:
2991b3515f3SDag-Erling Smørgrav 		return (-1);
3001b3515f3SDag-Erling Smørgrav 	}
3011b3515f3SDag-Erling Smørgrav }
3021b3515f3SDag-Erling Smørgrav 
3031b3515f3SDag-Erling Smørgrav static int
3041b3515f3SDag-Erling Smørgrav quota_read64(struct quotafile *qf, struct dqblk *dqb, int id)
3051b3515f3SDag-Erling Smørgrav {
3061b3515f3SDag-Erling Smørgrav 	struct dqblk64 dqb64;
3071b3515f3SDag-Erling Smørgrav 	off_t off;
3081b3515f3SDag-Erling Smørgrav 
3091b3515f3SDag-Erling Smørgrav 	off = sizeof(struct dqhdr64) + id * sizeof(struct dqblk64);
3101b3515f3SDag-Erling Smørgrav 	if (lseek(qf->fd, off, SEEK_SET) == -1)
3111b3515f3SDag-Erling Smørgrav 		return (-1);
3121b3515f3SDag-Erling Smørgrav 	switch (read(qf->fd, &dqb64, sizeof(dqb64))) {
3131b3515f3SDag-Erling Smørgrav 	case 0:
3146197731dSKirk McKusick 		memset(dqb, 0, sizeof(*dqb));
3151b3515f3SDag-Erling Smørgrav 		return (0);
3161b3515f3SDag-Erling Smørgrav 	case sizeof(dqb64):
3171b3515f3SDag-Erling Smørgrav 		dqb->dqb_bhardlimit = be64toh(dqb64.dqb_bhardlimit);
3181b3515f3SDag-Erling Smørgrav 		dqb->dqb_bsoftlimit = be64toh(dqb64.dqb_bsoftlimit);
3191b3515f3SDag-Erling Smørgrav 		dqb->dqb_curblocks = be64toh(dqb64.dqb_curblocks);
3201b3515f3SDag-Erling Smørgrav 		dqb->dqb_ihardlimit = be64toh(dqb64.dqb_ihardlimit);
3211b3515f3SDag-Erling Smørgrav 		dqb->dqb_isoftlimit = be64toh(dqb64.dqb_isoftlimit);
3221b3515f3SDag-Erling Smørgrav 		dqb->dqb_curinodes = be64toh(dqb64.dqb_curinodes);
3231b3515f3SDag-Erling Smørgrav 		dqb->dqb_btime = be64toh(dqb64.dqb_btime);
3241b3515f3SDag-Erling Smørgrav 		dqb->dqb_itime = be64toh(dqb64.dqb_itime);
3251b3515f3SDag-Erling Smørgrav 		return (0);
3261b3515f3SDag-Erling Smørgrav 	default:
3271b3515f3SDag-Erling Smørgrav 		return (-1);
3281b3515f3SDag-Erling Smørgrav 	}
3291b3515f3SDag-Erling Smørgrav }
3301b3515f3SDag-Erling Smørgrav 
3311b3515f3SDag-Erling Smørgrav int
3321b3515f3SDag-Erling Smørgrav quota_read(struct quotafile *qf, struct dqblk *dqb, int id)
3331b3515f3SDag-Erling Smørgrav {
3348bd6a3abSKirk McKusick 	int qcmd;
3351b3515f3SDag-Erling Smørgrav 
3368bd6a3abSKirk McKusick 	if (qf->fd == -1) {
3378bd6a3abSKirk McKusick 		qcmd = QCMD(Q_GETQUOTA, qf->quotatype);
3388bd6a3abSKirk McKusick 		return (quotactl(qf->fsname, qcmd, id, dqb));
3398bd6a3abSKirk McKusick 	}
3408bd6a3abSKirk McKusick 	switch (qf->wordsize) {
3411b3515f3SDag-Erling Smørgrav 	case 32:
342fdd356a8SDag-Erling Smørgrav 		return (quota_read32(qf, dqb, id));
3431b3515f3SDag-Erling Smørgrav 	case 64:
344fdd356a8SDag-Erling Smørgrav 		return (quota_read64(qf, dqb, id));
3451b3515f3SDag-Erling Smørgrav 	default:
3461b3515f3SDag-Erling Smørgrav 		errno = EINVAL;
3471b3515f3SDag-Erling Smørgrav 		return (-1);
3481b3515f3SDag-Erling Smørgrav 	}
3491b3515f3SDag-Erling Smørgrav 	/* not reached */
3501b3515f3SDag-Erling Smørgrav }
3511b3515f3SDag-Erling Smørgrav 
3521b3515f3SDag-Erling Smørgrav #define CLIP32(u64) ((u64) > UINT32_MAX ? UINT32_MAX : (uint32_t)(u64))
3531b3515f3SDag-Erling Smørgrav 
3541b3515f3SDag-Erling Smørgrav static int
3551b3515f3SDag-Erling Smørgrav quota_write32(struct quotafile *qf, const struct dqblk *dqb, int id)
3561b3515f3SDag-Erling Smørgrav {
3571b3515f3SDag-Erling Smørgrav 	struct dqblk32 dqb32;
3581b3515f3SDag-Erling Smørgrav 	off_t off;
3591b3515f3SDag-Erling Smørgrav 
3601b3515f3SDag-Erling Smørgrav 	dqb32.dqb_bhardlimit = CLIP32(dqb->dqb_bhardlimit);
3611b3515f3SDag-Erling Smørgrav 	dqb32.dqb_bsoftlimit = CLIP32(dqb->dqb_bsoftlimit);
3621b3515f3SDag-Erling Smørgrav 	dqb32.dqb_curblocks = CLIP32(dqb->dqb_curblocks);
3631b3515f3SDag-Erling Smørgrav 	dqb32.dqb_ihardlimit = CLIP32(dqb->dqb_ihardlimit);
3641b3515f3SDag-Erling Smørgrav 	dqb32.dqb_isoftlimit = CLIP32(dqb->dqb_isoftlimit);
3651b3515f3SDag-Erling Smørgrav 	dqb32.dqb_curinodes = CLIP32(dqb->dqb_curinodes);
3661b3515f3SDag-Erling Smørgrav 	dqb32.dqb_btime = CLIP32(dqb->dqb_btime);
3671b3515f3SDag-Erling Smørgrav 	dqb32.dqb_itime = CLIP32(dqb->dqb_itime);
3681b3515f3SDag-Erling Smørgrav 
3691b3515f3SDag-Erling Smørgrav 	off = id * sizeof(struct dqblk32);
3701b3515f3SDag-Erling Smørgrav 	if (lseek(qf->fd, off, SEEK_SET) == -1)
3711b3515f3SDag-Erling Smørgrav 		return (-1);
3728bd6a3abSKirk McKusick 	if (write(qf->fd, &dqb32, sizeof(dqb32)) == sizeof(dqb32))
3738bd6a3abSKirk McKusick 		return (0);
3748bd6a3abSKirk McKusick 	return (-1);
3751b3515f3SDag-Erling Smørgrav }
3761b3515f3SDag-Erling Smørgrav 
3771b3515f3SDag-Erling Smørgrav static int
3781b3515f3SDag-Erling Smørgrav quota_write64(struct quotafile *qf, const struct dqblk *dqb, int id)
3791b3515f3SDag-Erling Smørgrav {
3801b3515f3SDag-Erling Smørgrav 	struct dqblk64 dqb64;
3811b3515f3SDag-Erling Smørgrav 	off_t off;
3821b3515f3SDag-Erling Smørgrav 
3831b3515f3SDag-Erling Smørgrav 	dqb64.dqb_bhardlimit = htobe64(dqb->dqb_bhardlimit);
3841b3515f3SDag-Erling Smørgrav 	dqb64.dqb_bsoftlimit = htobe64(dqb->dqb_bsoftlimit);
3851b3515f3SDag-Erling Smørgrav 	dqb64.dqb_curblocks = htobe64(dqb->dqb_curblocks);
3861b3515f3SDag-Erling Smørgrav 	dqb64.dqb_ihardlimit = htobe64(dqb->dqb_ihardlimit);
3871b3515f3SDag-Erling Smørgrav 	dqb64.dqb_isoftlimit = htobe64(dqb->dqb_isoftlimit);
3881b3515f3SDag-Erling Smørgrav 	dqb64.dqb_curinodes = htobe64(dqb->dqb_curinodes);
3891b3515f3SDag-Erling Smørgrav 	dqb64.dqb_btime = htobe64(dqb->dqb_btime);
3901b3515f3SDag-Erling Smørgrav 	dqb64.dqb_itime = htobe64(dqb->dqb_itime);
3911b3515f3SDag-Erling Smørgrav 
3921b3515f3SDag-Erling Smørgrav 	off = sizeof(struct dqhdr64) + id * sizeof(struct dqblk64);
3931b3515f3SDag-Erling Smørgrav 	if (lseek(qf->fd, off, SEEK_SET) == -1)
3941b3515f3SDag-Erling Smørgrav 		return (-1);
3958bd6a3abSKirk McKusick 	if (write(qf->fd, &dqb64, sizeof(dqb64)) == sizeof(dqb64))
3968bd6a3abSKirk McKusick 		return (0);
3978bd6a3abSKirk McKusick 	return (-1);
3981b3515f3SDag-Erling Smørgrav }
3991b3515f3SDag-Erling Smørgrav 
4001b3515f3SDag-Erling Smørgrav int
4018bd6a3abSKirk McKusick quota_write_usage(struct quotafile *qf, struct dqblk *dqb, int id)
4021b3515f3SDag-Erling Smørgrav {
4038bd6a3abSKirk McKusick 	struct dqblk dqbuf;
4048bd6a3abSKirk McKusick 	int qcmd;
4051b3515f3SDag-Erling Smørgrav 
4068bd6a3abSKirk McKusick 	if (qf->fd == -1) {
4078bd6a3abSKirk McKusick 		qcmd = QCMD(Q_SETUSE, qf->quotatype);
4088bd6a3abSKirk McKusick 		return (quotactl(qf->fsname, qcmd, id, dqb));
4098bd6a3abSKirk McKusick 	}
4108bd6a3abSKirk McKusick 	/*
4118bd6a3abSKirk McKusick 	 * Have to do read-modify-write of quota in file.
4128bd6a3abSKirk McKusick 	 */
413516ad57bSKirk McKusick 	if ((qf->accmode & O_RDWR) != O_RDWR) {
414516ad57bSKirk McKusick 		errno = EBADF;
415516ad57bSKirk McKusick 		return (-1);
416516ad57bSKirk McKusick 	}
4178bd6a3abSKirk McKusick 	if (quota_read(qf, &dqbuf, id) != 0)
4188bd6a3abSKirk McKusick 		return (-1);
4198bd6a3abSKirk McKusick 	/*
4208bd6a3abSKirk McKusick 	 * Reset time limit if have a soft limit and were
4218bd6a3abSKirk McKusick 	 * previously under it, but are now over it.
4228bd6a3abSKirk McKusick 	 */
4238bd6a3abSKirk McKusick 	if (dqbuf.dqb_bsoftlimit && id != 0 &&
4248bd6a3abSKirk McKusick 	    dqbuf.dqb_curblocks < dqbuf.dqb_bsoftlimit &&
4258bd6a3abSKirk McKusick 	    dqb->dqb_curblocks >= dqbuf.dqb_bsoftlimit)
4268bd6a3abSKirk McKusick 		dqbuf.dqb_btime = 0;
4278bd6a3abSKirk McKusick 	if (dqbuf.dqb_isoftlimit && id != 0 &&
4288bd6a3abSKirk McKusick 	    dqbuf.dqb_curinodes < dqbuf.dqb_isoftlimit &&
4298bd6a3abSKirk McKusick 	    dqb->dqb_curinodes >= dqbuf.dqb_isoftlimit)
4308bd6a3abSKirk McKusick 		dqbuf.dqb_itime = 0;
4318bd6a3abSKirk McKusick 	dqbuf.dqb_curinodes = dqb->dqb_curinodes;
4328bd6a3abSKirk McKusick 	dqbuf.dqb_curblocks = dqb->dqb_curblocks;
4338bd6a3abSKirk McKusick 	/*
4348bd6a3abSKirk McKusick 	 * Write it back.
4358bd6a3abSKirk McKusick 	 */
4368bd6a3abSKirk McKusick 	switch (qf->wordsize) {
4378bd6a3abSKirk McKusick 	case 32:
438fdd356a8SDag-Erling Smørgrav 		return (quota_write32(qf, &dqbuf, id));
4398bd6a3abSKirk McKusick 	case 64:
440fdd356a8SDag-Erling Smørgrav 		return (quota_write64(qf, &dqbuf, id));
4418bd6a3abSKirk McKusick 	default:
4428bd6a3abSKirk McKusick 		errno = EINVAL;
4438bd6a3abSKirk McKusick 		return (-1);
4448bd6a3abSKirk McKusick 	}
4458bd6a3abSKirk McKusick 	/* not reached */
4468bd6a3abSKirk McKusick }
4478bd6a3abSKirk McKusick 
4488bd6a3abSKirk McKusick int
4498bd6a3abSKirk McKusick quota_write_limits(struct quotafile *qf, struct dqblk *dqb, int id)
4508bd6a3abSKirk McKusick {
4518bd6a3abSKirk McKusick 	struct dqblk dqbuf;
4528bd6a3abSKirk McKusick 	int qcmd;
4538bd6a3abSKirk McKusick 
4548bd6a3abSKirk McKusick 	if (qf->fd == -1) {
4558bd6a3abSKirk McKusick 		qcmd = QCMD(Q_SETQUOTA, qf->quotatype);
4568bd6a3abSKirk McKusick 		return (quotactl(qf->fsname, qcmd, id, dqb));
4578bd6a3abSKirk McKusick 	}
4588bd6a3abSKirk McKusick 	/*
4598bd6a3abSKirk McKusick 	 * Have to do read-modify-write of quota in file.
4608bd6a3abSKirk McKusick 	 */
461516ad57bSKirk McKusick 	if ((qf->accmode & O_RDWR) != O_RDWR) {
462516ad57bSKirk McKusick 		errno = EBADF;
463516ad57bSKirk McKusick 		return (-1);
464516ad57bSKirk McKusick 	}
4658bd6a3abSKirk McKusick 	if (quota_read(qf, &dqbuf, id) != 0)
4668bd6a3abSKirk McKusick 		return (-1);
4678bd6a3abSKirk McKusick 	/*
4688bd6a3abSKirk McKusick 	 * Reset time limit if have a soft limit and were
4698bd6a3abSKirk McKusick 	 * previously under it, but are now over it
4708bd6a3abSKirk McKusick 	 * or if there previously was no soft limit, but
4718bd6a3abSKirk McKusick 	 * now have one and are over it.
4728bd6a3abSKirk McKusick 	 */
4738bd6a3abSKirk McKusick 	if (dqbuf.dqb_bsoftlimit && id != 0 &&
4748bd6a3abSKirk McKusick 	    dqbuf.dqb_curblocks < dqbuf.dqb_bsoftlimit &&
4758bd6a3abSKirk McKusick 	    dqbuf.dqb_curblocks >= dqb->dqb_bsoftlimit)
4768bd6a3abSKirk McKusick 		dqb->dqb_btime = 0;
4778bd6a3abSKirk McKusick 	if (dqbuf.dqb_bsoftlimit == 0 && id != 0 &&
4788bd6a3abSKirk McKusick 	    dqb->dqb_bsoftlimit > 0 &&
4798bd6a3abSKirk McKusick 	    dqbuf.dqb_curblocks >= dqb->dqb_bsoftlimit)
4808bd6a3abSKirk McKusick 		dqb->dqb_btime = 0;
4818bd6a3abSKirk McKusick 	if (dqbuf.dqb_isoftlimit && id != 0 &&
4828bd6a3abSKirk McKusick 	    dqbuf.dqb_curinodes < dqbuf.dqb_isoftlimit &&
4838bd6a3abSKirk McKusick 	    dqbuf.dqb_curinodes >= dqb->dqb_isoftlimit)
4848bd6a3abSKirk McKusick 		dqb->dqb_itime = 0;
4858bd6a3abSKirk McKusick 	if (dqbuf.dqb_isoftlimit == 0 && id !=0 &&
4868bd6a3abSKirk McKusick 	    dqb->dqb_isoftlimit > 0 &&
4878bd6a3abSKirk McKusick 	    dqbuf.dqb_curinodes >= dqb->dqb_isoftlimit)
4888bd6a3abSKirk McKusick 		dqb->dqb_itime = 0;
4898bd6a3abSKirk McKusick 	dqb->dqb_curinodes = dqbuf.dqb_curinodes;
4908bd6a3abSKirk McKusick 	dqb->dqb_curblocks = dqbuf.dqb_curblocks;
4918bd6a3abSKirk McKusick 	/*
4928bd6a3abSKirk McKusick 	 * Write it back.
4938bd6a3abSKirk McKusick 	 */
4948bd6a3abSKirk McKusick 	switch (qf->wordsize) {
4951b3515f3SDag-Erling Smørgrav 	case 32:
496fdd356a8SDag-Erling Smørgrav 		return (quota_write32(qf, dqb, id));
4971b3515f3SDag-Erling Smørgrav 	case 64:
498fdd356a8SDag-Erling Smørgrav 		return (quota_write64(qf, dqb, id));
4991b3515f3SDag-Erling Smørgrav 	default:
5001b3515f3SDag-Erling Smørgrav 		errno = EINVAL;
5011b3515f3SDag-Erling Smørgrav 		return (-1);
5021b3515f3SDag-Erling Smørgrav 	}
5031b3515f3SDag-Erling Smørgrav 	/* not reached */
5041b3515f3SDag-Erling Smørgrav }
505aee785baSKirk McKusick 
506aee785baSKirk McKusick /*
507aee785baSKirk McKusick  * Convert a quota file from one format to another.
508aee785baSKirk McKusick  */
509aee785baSKirk McKusick int
510aee785baSKirk McKusick quota_convert(struct quotafile *qf, int wordsize)
511aee785baSKirk McKusick {
512aee785baSKirk McKusick 	struct quotafile *newqf;
513aee785baSKirk McKusick 	struct dqhdr64 dqh;
514aee785baSKirk McKusick 	struct dqblk dqblk;
515aee785baSKirk McKusick 	struct group *grp;
516aee785baSKirk McKusick 	int serrno, maxid, id, fd;
517aee785baSKirk McKusick 
518aee785baSKirk McKusick 	/*
519aee785baSKirk McKusick 	 * Quotas must not be active and quotafile must be open
520aee785baSKirk McKusick 	 * for reading and writing.
521aee785baSKirk McKusick 	 */
522aee785baSKirk McKusick 	if ((qf->accmode & O_RDWR) != O_RDWR || qf->fd == -1) {
523aee785baSKirk McKusick 		errno = EBADF;
524aee785baSKirk McKusick 		return (-1);
525aee785baSKirk McKusick 	}
526aee785baSKirk McKusick 	if ((wordsize != 32 && wordsize != 64) ||
527aee785baSKirk McKusick 	     wordsize == qf->wordsize) {
528aee785baSKirk McKusick 		errno = EINVAL;
529aee785baSKirk McKusick 		return (-1);
530aee785baSKirk McKusick 	}
531aee785baSKirk McKusick 	maxid = quota_maxid(qf);
532aee785baSKirk McKusick 	if ((newqf = calloc(1, sizeof(*qf))) == NULL) {
533aee785baSKirk McKusick 		errno = ENOMEM;
534aee785baSKirk McKusick 		return (-1);
535aee785baSKirk McKusick 	}
536aee785baSKirk McKusick 	*newqf = *qf;
537aee785baSKirk McKusick 	snprintf(newqf->qfname, MAXPATHLEN + 1, "%s_%d.orig", qf->qfname,
538aee785baSKirk McKusick 	    qf->wordsize);
539aee785baSKirk McKusick 	if (rename(qf->qfname, newqf->qfname) < 0) {
540aee785baSKirk McKusick 		free(newqf);
541aee785baSKirk McKusick 		return (-1);
542aee785baSKirk McKusick 	}
543d1d4d952SJilles Tjoelker 	if ((newqf->fd = open(qf->qfname, O_RDWR|O_CREAT|O_TRUNC|O_CLOEXEC,
544d1d4d952SJilles Tjoelker 	    0)) < 0) {
545aee785baSKirk McKusick 		serrno = errno;
546aee785baSKirk McKusick 		goto error;
547aee785baSKirk McKusick 	}
548aee785baSKirk McKusick 	newqf->wordsize = wordsize;
549aee785baSKirk McKusick 	if (wordsize == 64) {
550aee785baSKirk McKusick 		memset(&dqh, 0, sizeof(dqh));
551aee785baSKirk McKusick 		memcpy(dqh.dqh_magic, Q_DQHDR64_MAGIC, sizeof(dqh.dqh_magic));
552aee785baSKirk McKusick 		dqh.dqh_version = htobe32(Q_DQHDR64_VERSION);
553aee785baSKirk McKusick 		dqh.dqh_hdrlen = htobe32(sizeof(struct dqhdr64));
554aee785baSKirk McKusick 		dqh.dqh_reclen = htobe32(sizeof(struct dqblk64));
555aee785baSKirk McKusick 		if (write(newqf->fd, &dqh, sizeof(dqh)) != sizeof(dqh)) {
556aee785baSKirk McKusick 			serrno = errno;
557aee785baSKirk McKusick 			goto error;
558aee785baSKirk McKusick 		}
559aee785baSKirk McKusick 	}
560aee785baSKirk McKusick 	grp = getgrnam(QUOTAGROUP);
561aee785baSKirk McKusick 	fchown(newqf->fd, 0, grp ? grp->gr_gid : 0);
562aee785baSKirk McKusick 	fchmod(newqf->fd, 0640);
563aee785baSKirk McKusick 	for (id = 0; id <= maxid; id++) {
564aee785baSKirk McKusick 		if ((quota_read(qf, &dqblk, id)) < 0)
565aee785baSKirk McKusick 			break;
566aee785baSKirk McKusick 		switch (newqf->wordsize) {
567aee785baSKirk McKusick 		case 32:
568aee785baSKirk McKusick 			if ((quota_write32(newqf, &dqblk, id)) < 0)
569aee785baSKirk McKusick 				break;
570aee785baSKirk McKusick 			continue;
571aee785baSKirk McKusick 		case 64:
572aee785baSKirk McKusick 			if ((quota_write64(newqf, &dqblk, id)) < 0)
573aee785baSKirk McKusick 				break;
574aee785baSKirk McKusick 			continue;
575aee785baSKirk McKusick 		default:
576aee785baSKirk McKusick 			errno = EINVAL;
577aee785baSKirk McKusick 			break;
578aee785baSKirk McKusick 		}
579aee785baSKirk McKusick 	}
580aee785baSKirk McKusick 	if (id < maxid) {
581aee785baSKirk McKusick 		serrno = errno;
582aee785baSKirk McKusick 		goto error;
583aee785baSKirk McKusick 	}
584aee785baSKirk McKusick 	/*
585aee785baSKirk McKusick 	 * Update the passed in quotafile to reference the new file
586aee785baSKirk McKusick 	 * of the converted format size.
587aee785baSKirk McKusick 	 */
588aee785baSKirk McKusick 	fd = qf->fd;
589aee785baSKirk McKusick 	qf->fd = newqf->fd;
590aee785baSKirk McKusick 	newqf->fd = fd;
591aee785baSKirk McKusick 	qf->wordsize = newqf->wordsize;
592aee785baSKirk McKusick 	quota_close(newqf);
593aee785baSKirk McKusick 	return (0);
594aee785baSKirk McKusick error:
595aee785baSKirk McKusick 	/* put back the original file */
596aee785baSKirk McKusick 	(void) rename(newqf->qfname, qf->qfname);
597aee785baSKirk McKusick 	quota_close(newqf);
598aee785baSKirk McKusick 	errno = serrno;
599aee785baSKirk McKusick 	return (-1);
600aee785baSKirk McKusick }
601