11b3515f3SDag-Erling Smørgrav /*-
24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
35e53a4f9SPedro F. Giffuni *
4*e738085bSDag-Erling Smørgrav * Copyright (c) 2008 Dag-Erling 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
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
hasquota(struct fstab * fs,int type,char * qfnamep,int qfbufsize)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, ",")) {
87b3608ae1SEd Schouten if ((cp = strchr(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) {
1041e4da04fSXin LI strlcpy(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 *
quota_open(struct fstab * fs,int quotatype,int openflags)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;
11918bb746bSSean Eric Fagan int qcmd, serrno = 0;
120148d31b8SSean Eric Fagan int ufs;
1211b3515f3SDag-Erling Smørgrav
1221b3515f3SDag-Erling Smørgrav if ((qf = calloc(1, sizeof(*qf))) == NULL)
1231b3515f3SDag-Erling Smørgrav return (NULL);
1245666aadbSDag-Erling Smørgrav qf->fd = -1;
1258bd6a3abSKirk McKusick qf->quotatype = quotatype;
1264238b561SDon Lewis strlcpy(qf->fsname, fs->fs_file, sizeof(qf->fsname));
1275666aadbSDag-Erling Smørgrav if (stat(qf->fsname, &st) != 0)
1285666aadbSDag-Erling Smørgrav goto error;
1295666aadbSDag-Erling Smørgrav qf->dev = st.st_dev;
130516ad57bSKirk McKusick qcmd = QCMD(Q_GETQUOTASIZE, quotatype);
131148d31b8SSean Eric Fagan ufs = strcmp(fs->fs_vfstype, "ufs") == 0;
132148d31b8SSean Eric Fagan /*
133148d31b8SSean Eric Fagan * On UFS, hasquota() fills in qf->qfname. But we only care about
134148d31b8SSean Eric Fagan * this for UFS. So we need to call hasquota() for UFS, first.
135148d31b8SSean Eric Fagan */
136148d31b8SSean Eric Fagan if (ufs) {
137148d31b8SSean Eric Fagan serrno = hasquota(fs, quotatype, qf->qfname,
138148d31b8SSean Eric Fagan sizeof(qf->qfname));
139148d31b8SSean Eric Fagan }
140516ad57bSKirk McKusick if (quotactl(qf->fsname, qcmd, 0, &qf->wordsize) == 0)
1418bd6a3abSKirk McKusick return (qf);
142148d31b8SSean Eric Fagan if (!ufs) {
143aad5531eSSean Eric Fagan errno = 0;
144aad5531eSSean Eric Fagan goto error;
145148d31b8SSean Eric Fagan } else if (serrno == 0) {
1468bd6a3abSKirk McKusick errno = EOPNOTSUPP;
1475666aadbSDag-Erling Smørgrav goto error;
1488bd6a3abSKirk McKusick }
1495666aadbSDag-Erling Smørgrav qf->accmode = openflags & O_ACCMODE;
150d1d4d952SJilles Tjoelker if ((qf->fd = open(qf->qfname, qf->accmode|O_CLOEXEC)) < 0 &&
1515666aadbSDag-Erling Smørgrav (openflags & O_CREAT) != O_CREAT)
1525666aadbSDag-Erling Smørgrav goto error;
1538bd6a3abSKirk McKusick /* File open worked, so process it */
1548bd6a3abSKirk McKusick if (qf->fd != -1) {
1558bd6a3abSKirk McKusick qf->wordsize = 32;
1561b3515f3SDag-Erling Smørgrav switch (read(qf->fd, &dqh, sizeof(dqh))) {
1571b3515f3SDag-Erling Smørgrav case -1:
1585666aadbSDag-Erling Smørgrav goto error;
1591b3515f3SDag-Erling Smørgrav case sizeof(dqh):
1601b3515f3SDag-Erling Smørgrav if (strcmp(dqh.dqh_magic, Q_DQHDR64_MAGIC) != 0) {
1611b3515f3SDag-Erling Smørgrav /* no magic, assume 32 bits */
1628bd6a3abSKirk McKusick qf->wordsize = 32;
1631b3515f3SDag-Erling Smørgrav return (qf);
1641b3515f3SDag-Erling Smørgrav }
1651b3515f3SDag-Erling Smørgrav if (be32toh(dqh.dqh_version) != Q_DQHDR64_VERSION ||
1661b3515f3SDag-Erling Smørgrav be32toh(dqh.dqh_hdrlen) != sizeof(struct dqhdr64) ||
1671b3515f3SDag-Erling Smørgrav be32toh(dqh.dqh_reclen) != sizeof(struct dqblk64)) {
1681b3515f3SDag-Erling Smørgrav /* correct magic, wrong version / lengths */
1691b3515f3SDag-Erling Smørgrav errno = EINVAL;
1705666aadbSDag-Erling Smørgrav goto error;
1711b3515f3SDag-Erling Smørgrav }
1728bd6a3abSKirk McKusick qf->wordsize = 64;
1731b3515f3SDag-Erling Smørgrav return (qf);
1741b3515f3SDag-Erling Smørgrav default:
1758bd6a3abSKirk McKusick qf->wordsize = 32;
1761b3515f3SDag-Erling Smørgrav return (qf);
1771b3515f3SDag-Erling Smørgrav }
1781b3515f3SDag-Erling Smørgrav /* not reached */
1791b3515f3SDag-Erling Smørgrav }
1805666aadbSDag-Erling Smørgrav /* open failed, but O_CREAT was specified, so create a new file */
181d1d4d952SJilles Tjoelker if ((qf->fd = open(qf->qfname, O_RDWR|O_CREAT|O_TRUNC|O_CLOEXEC, 0)) <
182d1d4d952SJilles Tjoelker 0)
1835666aadbSDag-Erling Smørgrav goto error;
1848bd6a3abSKirk McKusick qf->wordsize = 64;
1851b3515f3SDag-Erling Smørgrav memset(&dqh, 0, sizeof(dqh));
1861b3515f3SDag-Erling Smørgrav memcpy(dqh.dqh_magic, Q_DQHDR64_MAGIC, sizeof(dqh.dqh_magic));
1871b3515f3SDag-Erling Smørgrav dqh.dqh_version = htobe32(Q_DQHDR64_VERSION);
1881b3515f3SDag-Erling Smørgrav dqh.dqh_hdrlen = htobe32(sizeof(struct dqhdr64));
1891b3515f3SDag-Erling Smørgrav dqh.dqh_reclen = htobe32(sizeof(struct dqblk64));
190c3616249SKirk McKusick if (write(qf->fd, &dqh, sizeof(dqh)) != sizeof(dqh)) {
191c3616249SKirk McKusick /* it was one we created ourselves */
192c3616249SKirk McKusick unlink(qf->qfname);
1935666aadbSDag-Erling Smørgrav goto error;
194c3616249SKirk McKusick }
1951b3515f3SDag-Erling Smørgrav grp = getgrnam(QUOTAGROUP);
1961b3515f3SDag-Erling Smørgrav fchown(qf->fd, 0, grp ? grp->gr_gid : 0);
1971b3515f3SDag-Erling Smørgrav fchmod(qf->fd, 0640);
1981b3515f3SDag-Erling Smørgrav return (qf);
1995666aadbSDag-Erling Smørgrav error:
2005666aadbSDag-Erling Smørgrav serrno = errno;
2015666aadbSDag-Erling Smørgrav /* did we have an open file? */
202c3616249SKirk McKusick if (qf->fd != -1)
2035666aadbSDag-Erling Smørgrav close(qf->fd);
2045666aadbSDag-Erling Smørgrav free(qf);
2055666aadbSDag-Erling Smørgrav errno = serrno;
2065666aadbSDag-Erling Smørgrav return (NULL);
2071b3515f3SDag-Erling Smørgrav }
2081b3515f3SDag-Erling Smørgrav
2091b3515f3SDag-Erling Smørgrav void
quota_close(struct quotafile * qf)2101b3515f3SDag-Erling Smørgrav quota_close(struct quotafile *qf)
2111b3515f3SDag-Erling Smørgrav {
2121b3515f3SDag-Erling Smørgrav
2138bd6a3abSKirk McKusick if (qf->fd != -1)
2141b3515f3SDag-Erling Smørgrav close(qf->fd);
2151b3515f3SDag-Erling Smørgrav free(qf);
2161b3515f3SDag-Erling Smørgrav }
2171b3515f3SDag-Erling Smørgrav
218e525d16aSKirk McKusick int
quota_on(struct quotafile * qf)219e525d16aSKirk McKusick quota_on(struct quotafile *qf)
220e525d16aSKirk McKusick {
221e525d16aSKirk McKusick int qcmd;
222e525d16aSKirk McKusick
223e525d16aSKirk McKusick qcmd = QCMD(Q_QUOTAON, qf->quotatype);
224e525d16aSKirk McKusick return (quotactl(qf->fsname, qcmd, 0, qf->qfname));
225e525d16aSKirk McKusick }
226e525d16aSKirk McKusick
227e525d16aSKirk McKusick int
quota_off(struct quotafile * qf)228e525d16aSKirk McKusick quota_off(struct quotafile *qf)
229e525d16aSKirk McKusick {
230e525d16aSKirk McKusick
231e525d16aSKirk McKusick return (quotactl(qf->fsname, QCMD(Q_QUOTAOFF, qf->quotatype), 0, 0));
232e525d16aSKirk McKusick }
233e525d16aSKirk McKusick
2345666aadbSDag-Erling Smørgrav const char *
quota_fsname(const struct quotafile * qf)2355666aadbSDag-Erling Smørgrav quota_fsname(const struct quotafile *qf)
2365666aadbSDag-Erling Smørgrav {
2375666aadbSDag-Erling Smørgrav
2385666aadbSDag-Erling Smørgrav return (qf->fsname);
2395666aadbSDag-Erling Smørgrav }
2405666aadbSDag-Erling Smørgrav
2415666aadbSDag-Erling Smørgrav const char *
quota_qfname(const struct quotafile * qf)2425666aadbSDag-Erling Smørgrav quota_qfname(const struct quotafile *qf)
2435666aadbSDag-Erling Smørgrav {
2445666aadbSDag-Erling Smørgrav
2455666aadbSDag-Erling Smørgrav return (qf->qfname);
2465666aadbSDag-Erling Smørgrav }
2475666aadbSDag-Erling Smørgrav
2485666aadbSDag-Erling Smørgrav int
quota_check_path(const struct quotafile * qf,const char * path)2495666aadbSDag-Erling Smørgrav quota_check_path(const struct quotafile *qf, const char *path)
2505666aadbSDag-Erling Smørgrav {
2515666aadbSDag-Erling Smørgrav struct stat st;
2525666aadbSDag-Erling Smørgrav
2535666aadbSDag-Erling Smørgrav if (stat(path, &st) == -1)
2545666aadbSDag-Erling Smørgrav return (-1);
2555666aadbSDag-Erling Smørgrav return (st.st_dev == qf->dev);
2565666aadbSDag-Erling Smørgrav }
2575666aadbSDag-Erling Smørgrav
2586197731dSKirk McKusick int
quota_maxid(struct quotafile * qf)2596197731dSKirk McKusick quota_maxid(struct quotafile *qf)
2606197731dSKirk McKusick {
2616197731dSKirk McKusick struct stat st;
262516ad57bSKirk McKusick int maxid;
2636197731dSKirk McKusick
2646197731dSKirk McKusick if (stat(qf->qfname, &st) < 0)
2656197731dSKirk McKusick return (0);
2666197731dSKirk McKusick switch (qf->wordsize) {
2676197731dSKirk McKusick case 32:
268516ad57bSKirk McKusick maxid = st.st_size / sizeof(struct dqblk32) - 1;
269516ad57bSKirk McKusick break;
2706197731dSKirk McKusick case 64:
271516ad57bSKirk McKusick maxid = st.st_size / sizeof(struct dqblk64) - 2;
272516ad57bSKirk McKusick break;
2736197731dSKirk McKusick default:
274516ad57bSKirk McKusick maxid = 0;
275516ad57bSKirk McKusick break;
2766197731dSKirk McKusick }
277516ad57bSKirk McKusick return (maxid > 0 ? maxid : 0);
2786197731dSKirk McKusick }
2796197731dSKirk McKusick
2801b3515f3SDag-Erling Smørgrav static int
quota_read32(struct quotafile * qf,struct dqblk * dqb,int id)2811b3515f3SDag-Erling Smørgrav quota_read32(struct quotafile *qf, struct dqblk *dqb, int id)
2821b3515f3SDag-Erling Smørgrav {
2831b3515f3SDag-Erling Smørgrav struct dqblk32 dqb32;
2841b3515f3SDag-Erling Smørgrav off_t off;
2851b3515f3SDag-Erling Smørgrav
2861b3515f3SDag-Erling Smørgrav off = id * sizeof(struct dqblk32);
2871b3515f3SDag-Erling Smørgrav if (lseek(qf->fd, off, SEEK_SET) == -1)
2881b3515f3SDag-Erling Smørgrav return (-1);
2891b3515f3SDag-Erling Smørgrav switch (read(qf->fd, &dqb32, sizeof(dqb32))) {
2901b3515f3SDag-Erling Smørgrav case 0:
2916197731dSKirk McKusick memset(dqb, 0, sizeof(*dqb));
2921b3515f3SDag-Erling Smørgrav return (0);
2931b3515f3SDag-Erling Smørgrav case sizeof(dqb32):
2941b3515f3SDag-Erling Smørgrav dqb->dqb_bhardlimit = dqb32.dqb_bhardlimit;
2951b3515f3SDag-Erling Smørgrav dqb->dqb_bsoftlimit = dqb32.dqb_bsoftlimit;
2961b3515f3SDag-Erling Smørgrav dqb->dqb_curblocks = dqb32.dqb_curblocks;
2971b3515f3SDag-Erling Smørgrav dqb->dqb_ihardlimit = dqb32.dqb_ihardlimit;
2981b3515f3SDag-Erling Smørgrav dqb->dqb_isoftlimit = dqb32.dqb_isoftlimit;
2991b3515f3SDag-Erling Smørgrav dqb->dqb_curinodes = dqb32.dqb_curinodes;
3001b3515f3SDag-Erling Smørgrav dqb->dqb_btime = dqb32.dqb_btime;
3011b3515f3SDag-Erling Smørgrav dqb->dqb_itime = dqb32.dqb_itime;
3021b3515f3SDag-Erling Smørgrav return (0);
3031b3515f3SDag-Erling Smørgrav default:
3041b3515f3SDag-Erling Smørgrav return (-1);
3051b3515f3SDag-Erling Smørgrav }
3061b3515f3SDag-Erling Smørgrav }
3071b3515f3SDag-Erling Smørgrav
3081b3515f3SDag-Erling Smørgrav static int
quota_read64(struct quotafile * qf,struct dqblk * dqb,int id)3091b3515f3SDag-Erling Smørgrav quota_read64(struct quotafile *qf, struct dqblk *dqb, int id)
3101b3515f3SDag-Erling Smørgrav {
3111b3515f3SDag-Erling Smørgrav struct dqblk64 dqb64;
3121b3515f3SDag-Erling Smørgrav off_t off;
3131b3515f3SDag-Erling Smørgrav
3141b3515f3SDag-Erling Smørgrav off = sizeof(struct dqhdr64) + id * sizeof(struct dqblk64);
3151b3515f3SDag-Erling Smørgrav if (lseek(qf->fd, off, SEEK_SET) == -1)
3161b3515f3SDag-Erling Smørgrav return (-1);
3171b3515f3SDag-Erling Smørgrav switch (read(qf->fd, &dqb64, sizeof(dqb64))) {
3181b3515f3SDag-Erling Smørgrav case 0:
3196197731dSKirk McKusick memset(dqb, 0, sizeof(*dqb));
3201b3515f3SDag-Erling Smørgrav return (0);
3211b3515f3SDag-Erling Smørgrav case sizeof(dqb64):
3221b3515f3SDag-Erling Smørgrav dqb->dqb_bhardlimit = be64toh(dqb64.dqb_bhardlimit);
3231b3515f3SDag-Erling Smørgrav dqb->dqb_bsoftlimit = be64toh(dqb64.dqb_bsoftlimit);
3241b3515f3SDag-Erling Smørgrav dqb->dqb_curblocks = be64toh(dqb64.dqb_curblocks);
3251b3515f3SDag-Erling Smørgrav dqb->dqb_ihardlimit = be64toh(dqb64.dqb_ihardlimit);
3261b3515f3SDag-Erling Smørgrav dqb->dqb_isoftlimit = be64toh(dqb64.dqb_isoftlimit);
3271b3515f3SDag-Erling Smørgrav dqb->dqb_curinodes = be64toh(dqb64.dqb_curinodes);
3281b3515f3SDag-Erling Smørgrav dqb->dqb_btime = be64toh(dqb64.dqb_btime);
3291b3515f3SDag-Erling Smørgrav dqb->dqb_itime = be64toh(dqb64.dqb_itime);
3301b3515f3SDag-Erling Smørgrav return (0);
3311b3515f3SDag-Erling Smørgrav default:
3321b3515f3SDag-Erling Smørgrav return (-1);
3331b3515f3SDag-Erling Smørgrav }
3341b3515f3SDag-Erling Smørgrav }
3351b3515f3SDag-Erling Smørgrav
3361b3515f3SDag-Erling Smørgrav int
quota_read(struct quotafile * qf,struct dqblk * dqb,int id)3371b3515f3SDag-Erling Smørgrav quota_read(struct quotafile *qf, struct dqblk *dqb, int id)
3381b3515f3SDag-Erling Smørgrav {
3398bd6a3abSKirk McKusick int qcmd;
3401b3515f3SDag-Erling Smørgrav
3418bd6a3abSKirk McKusick if (qf->fd == -1) {
3428bd6a3abSKirk McKusick qcmd = QCMD(Q_GETQUOTA, qf->quotatype);
3438bd6a3abSKirk McKusick return (quotactl(qf->fsname, qcmd, id, dqb));
3448bd6a3abSKirk McKusick }
3458bd6a3abSKirk McKusick switch (qf->wordsize) {
3461b3515f3SDag-Erling Smørgrav case 32:
347fdd356a8SDag-Erling Smørgrav return (quota_read32(qf, dqb, id));
3481b3515f3SDag-Erling Smørgrav case 64:
349fdd356a8SDag-Erling Smørgrav return (quota_read64(qf, dqb, id));
3501b3515f3SDag-Erling Smørgrav default:
3511b3515f3SDag-Erling Smørgrav errno = EINVAL;
3521b3515f3SDag-Erling Smørgrav return (-1);
3531b3515f3SDag-Erling Smørgrav }
3541b3515f3SDag-Erling Smørgrav /* not reached */
3551b3515f3SDag-Erling Smørgrav }
3561b3515f3SDag-Erling Smørgrav
3571b3515f3SDag-Erling Smørgrav #define CLIP32(u64) ((u64) > UINT32_MAX ? UINT32_MAX : (uint32_t)(u64))
3581b3515f3SDag-Erling Smørgrav
3591b3515f3SDag-Erling Smørgrav static int
quota_write32(struct quotafile * qf,const struct dqblk * dqb,int id)3601b3515f3SDag-Erling Smørgrav quota_write32(struct quotafile *qf, const struct dqblk *dqb, int id)
3611b3515f3SDag-Erling Smørgrav {
3621b3515f3SDag-Erling Smørgrav struct dqblk32 dqb32;
3631b3515f3SDag-Erling Smørgrav off_t off;
3641b3515f3SDag-Erling Smørgrav
3651b3515f3SDag-Erling Smørgrav dqb32.dqb_bhardlimit = CLIP32(dqb->dqb_bhardlimit);
3661b3515f3SDag-Erling Smørgrav dqb32.dqb_bsoftlimit = CLIP32(dqb->dqb_bsoftlimit);
3671b3515f3SDag-Erling Smørgrav dqb32.dqb_curblocks = CLIP32(dqb->dqb_curblocks);
3681b3515f3SDag-Erling Smørgrav dqb32.dqb_ihardlimit = CLIP32(dqb->dqb_ihardlimit);
3691b3515f3SDag-Erling Smørgrav dqb32.dqb_isoftlimit = CLIP32(dqb->dqb_isoftlimit);
3701b3515f3SDag-Erling Smørgrav dqb32.dqb_curinodes = CLIP32(dqb->dqb_curinodes);
3711b3515f3SDag-Erling Smørgrav dqb32.dqb_btime = CLIP32(dqb->dqb_btime);
3721b3515f3SDag-Erling Smørgrav dqb32.dqb_itime = CLIP32(dqb->dqb_itime);
3731b3515f3SDag-Erling Smørgrav
3741b3515f3SDag-Erling Smørgrav off = id * sizeof(struct dqblk32);
3751b3515f3SDag-Erling Smørgrav if (lseek(qf->fd, off, SEEK_SET) == -1)
3761b3515f3SDag-Erling Smørgrav return (-1);
3778bd6a3abSKirk McKusick if (write(qf->fd, &dqb32, sizeof(dqb32)) == sizeof(dqb32))
3788bd6a3abSKirk McKusick return (0);
3798bd6a3abSKirk McKusick return (-1);
3801b3515f3SDag-Erling Smørgrav }
3811b3515f3SDag-Erling Smørgrav
3821b3515f3SDag-Erling Smørgrav static int
quota_write64(struct quotafile * qf,const struct dqblk * dqb,int id)3831b3515f3SDag-Erling Smørgrav quota_write64(struct quotafile *qf, const struct dqblk *dqb, int id)
3841b3515f3SDag-Erling Smørgrav {
3851b3515f3SDag-Erling Smørgrav struct dqblk64 dqb64;
3861b3515f3SDag-Erling Smørgrav off_t off;
3871b3515f3SDag-Erling Smørgrav
3881b3515f3SDag-Erling Smørgrav dqb64.dqb_bhardlimit = htobe64(dqb->dqb_bhardlimit);
3891b3515f3SDag-Erling Smørgrav dqb64.dqb_bsoftlimit = htobe64(dqb->dqb_bsoftlimit);
3901b3515f3SDag-Erling Smørgrav dqb64.dqb_curblocks = htobe64(dqb->dqb_curblocks);
3911b3515f3SDag-Erling Smørgrav dqb64.dqb_ihardlimit = htobe64(dqb->dqb_ihardlimit);
3921b3515f3SDag-Erling Smørgrav dqb64.dqb_isoftlimit = htobe64(dqb->dqb_isoftlimit);
3931b3515f3SDag-Erling Smørgrav dqb64.dqb_curinodes = htobe64(dqb->dqb_curinodes);
3941b3515f3SDag-Erling Smørgrav dqb64.dqb_btime = htobe64(dqb->dqb_btime);
3951b3515f3SDag-Erling Smørgrav dqb64.dqb_itime = htobe64(dqb->dqb_itime);
3961b3515f3SDag-Erling Smørgrav
3971b3515f3SDag-Erling Smørgrav off = sizeof(struct dqhdr64) + id * sizeof(struct dqblk64);
3981b3515f3SDag-Erling Smørgrav if (lseek(qf->fd, off, SEEK_SET) == -1)
3991b3515f3SDag-Erling Smørgrav return (-1);
4008bd6a3abSKirk McKusick if (write(qf->fd, &dqb64, sizeof(dqb64)) == sizeof(dqb64))
4018bd6a3abSKirk McKusick return (0);
4028bd6a3abSKirk McKusick return (-1);
4031b3515f3SDag-Erling Smørgrav }
4041b3515f3SDag-Erling Smørgrav
4051b3515f3SDag-Erling Smørgrav int
quota_write_usage(struct quotafile * qf,struct dqblk * dqb,int id)4068bd6a3abSKirk McKusick quota_write_usage(struct quotafile *qf, struct dqblk *dqb, int id)
4071b3515f3SDag-Erling Smørgrav {
4088bd6a3abSKirk McKusick struct dqblk dqbuf;
4098bd6a3abSKirk McKusick int qcmd;
4101b3515f3SDag-Erling Smørgrav
4118bd6a3abSKirk McKusick if (qf->fd == -1) {
4128bd6a3abSKirk McKusick qcmd = QCMD(Q_SETUSE, qf->quotatype);
4138bd6a3abSKirk McKusick return (quotactl(qf->fsname, qcmd, id, dqb));
4148bd6a3abSKirk McKusick }
4158bd6a3abSKirk McKusick /*
4168bd6a3abSKirk McKusick * Have to do read-modify-write of quota in file.
4178bd6a3abSKirk McKusick */
418516ad57bSKirk McKusick if ((qf->accmode & O_RDWR) != O_RDWR) {
419516ad57bSKirk McKusick errno = EBADF;
420516ad57bSKirk McKusick return (-1);
421516ad57bSKirk McKusick }
4228bd6a3abSKirk McKusick if (quota_read(qf, &dqbuf, id) != 0)
4238bd6a3abSKirk McKusick return (-1);
4248bd6a3abSKirk McKusick /*
4258bd6a3abSKirk McKusick * Reset time limit if have a soft limit and were
4268bd6a3abSKirk McKusick * previously under it, but are now over it.
4278bd6a3abSKirk McKusick */
4288bd6a3abSKirk McKusick if (dqbuf.dqb_bsoftlimit && id != 0 &&
4298bd6a3abSKirk McKusick dqbuf.dqb_curblocks < dqbuf.dqb_bsoftlimit &&
4308bd6a3abSKirk McKusick dqb->dqb_curblocks >= dqbuf.dqb_bsoftlimit)
4318bd6a3abSKirk McKusick dqbuf.dqb_btime = 0;
4328bd6a3abSKirk McKusick if (dqbuf.dqb_isoftlimit && id != 0 &&
4338bd6a3abSKirk McKusick dqbuf.dqb_curinodes < dqbuf.dqb_isoftlimit &&
4348bd6a3abSKirk McKusick dqb->dqb_curinodes >= dqbuf.dqb_isoftlimit)
4358bd6a3abSKirk McKusick dqbuf.dqb_itime = 0;
4368bd6a3abSKirk McKusick dqbuf.dqb_curinodes = dqb->dqb_curinodes;
4378bd6a3abSKirk McKusick dqbuf.dqb_curblocks = dqb->dqb_curblocks;
4388bd6a3abSKirk McKusick /*
4398bd6a3abSKirk McKusick * Write it back.
4408bd6a3abSKirk McKusick */
4418bd6a3abSKirk McKusick switch (qf->wordsize) {
4428bd6a3abSKirk McKusick case 32:
443fdd356a8SDag-Erling Smørgrav return (quota_write32(qf, &dqbuf, id));
4448bd6a3abSKirk McKusick case 64:
445fdd356a8SDag-Erling Smørgrav return (quota_write64(qf, &dqbuf, id));
4468bd6a3abSKirk McKusick default:
4478bd6a3abSKirk McKusick errno = EINVAL;
4488bd6a3abSKirk McKusick return (-1);
4498bd6a3abSKirk McKusick }
4508bd6a3abSKirk McKusick /* not reached */
4518bd6a3abSKirk McKusick }
4528bd6a3abSKirk McKusick
4538bd6a3abSKirk McKusick int
quota_write_limits(struct quotafile * qf,struct dqblk * dqb,int id)4548bd6a3abSKirk McKusick quota_write_limits(struct quotafile *qf, struct dqblk *dqb, int id)
4558bd6a3abSKirk McKusick {
4568bd6a3abSKirk McKusick struct dqblk dqbuf;
4578bd6a3abSKirk McKusick int qcmd;
4588bd6a3abSKirk McKusick
4598bd6a3abSKirk McKusick if (qf->fd == -1) {
4608bd6a3abSKirk McKusick qcmd = QCMD(Q_SETQUOTA, qf->quotatype);
4618bd6a3abSKirk McKusick return (quotactl(qf->fsname, qcmd, id, dqb));
4628bd6a3abSKirk McKusick }
4638bd6a3abSKirk McKusick /*
4648bd6a3abSKirk McKusick * Have to do read-modify-write of quota in file.
4658bd6a3abSKirk McKusick */
466516ad57bSKirk McKusick if ((qf->accmode & O_RDWR) != O_RDWR) {
467516ad57bSKirk McKusick errno = EBADF;
468516ad57bSKirk McKusick return (-1);
469516ad57bSKirk McKusick }
4708bd6a3abSKirk McKusick if (quota_read(qf, &dqbuf, id) != 0)
4718bd6a3abSKirk McKusick return (-1);
4728bd6a3abSKirk McKusick /*
4738bd6a3abSKirk McKusick * Reset time limit if have a soft limit and were
4748bd6a3abSKirk McKusick * previously under it, but are now over it
4758bd6a3abSKirk McKusick * or if there previously was no soft limit, but
4768bd6a3abSKirk McKusick * now have one and are over it.
4778bd6a3abSKirk McKusick */
4788bd6a3abSKirk McKusick if (dqbuf.dqb_bsoftlimit && id != 0 &&
4798bd6a3abSKirk McKusick dqbuf.dqb_curblocks < dqbuf.dqb_bsoftlimit &&
4808bd6a3abSKirk McKusick dqbuf.dqb_curblocks >= dqb->dqb_bsoftlimit)
4818bd6a3abSKirk McKusick dqb->dqb_btime = 0;
4828bd6a3abSKirk McKusick if (dqbuf.dqb_bsoftlimit == 0 && id != 0 &&
4838bd6a3abSKirk McKusick dqb->dqb_bsoftlimit > 0 &&
4848bd6a3abSKirk McKusick dqbuf.dqb_curblocks >= dqb->dqb_bsoftlimit)
4858bd6a3abSKirk McKusick dqb->dqb_btime = 0;
4868bd6a3abSKirk McKusick if (dqbuf.dqb_isoftlimit && id != 0 &&
4878bd6a3abSKirk McKusick dqbuf.dqb_curinodes < dqbuf.dqb_isoftlimit &&
4888bd6a3abSKirk McKusick dqbuf.dqb_curinodes >= dqb->dqb_isoftlimit)
4898bd6a3abSKirk McKusick dqb->dqb_itime = 0;
4908bd6a3abSKirk McKusick if (dqbuf.dqb_isoftlimit == 0 && id !=0 &&
4918bd6a3abSKirk McKusick dqb->dqb_isoftlimit > 0 &&
4928bd6a3abSKirk McKusick dqbuf.dqb_curinodes >= dqb->dqb_isoftlimit)
4938bd6a3abSKirk McKusick dqb->dqb_itime = 0;
4948bd6a3abSKirk McKusick dqb->dqb_curinodes = dqbuf.dqb_curinodes;
4958bd6a3abSKirk McKusick dqb->dqb_curblocks = dqbuf.dqb_curblocks;
4968bd6a3abSKirk McKusick /*
4978bd6a3abSKirk McKusick * Write it back.
4988bd6a3abSKirk McKusick */
4998bd6a3abSKirk McKusick switch (qf->wordsize) {
5001b3515f3SDag-Erling Smørgrav case 32:
501fdd356a8SDag-Erling Smørgrav return (quota_write32(qf, dqb, id));
5021b3515f3SDag-Erling Smørgrav case 64:
503fdd356a8SDag-Erling Smørgrav return (quota_write64(qf, dqb, id));
5041b3515f3SDag-Erling Smørgrav default:
5051b3515f3SDag-Erling Smørgrav errno = EINVAL;
5061b3515f3SDag-Erling Smørgrav return (-1);
5071b3515f3SDag-Erling Smørgrav }
5081b3515f3SDag-Erling Smørgrav /* not reached */
5091b3515f3SDag-Erling Smørgrav }
510aee785baSKirk McKusick
511aee785baSKirk McKusick /*
512aee785baSKirk McKusick * Convert a quota file from one format to another.
513aee785baSKirk McKusick */
514aee785baSKirk McKusick int
quota_convert(struct quotafile * qf,int wordsize)515aee785baSKirk McKusick quota_convert(struct quotafile *qf, int wordsize)
516aee785baSKirk McKusick {
517aee785baSKirk McKusick struct quotafile *newqf;
518aee785baSKirk McKusick struct dqhdr64 dqh;
519aee785baSKirk McKusick struct dqblk dqblk;
520aee785baSKirk McKusick struct group *grp;
521aee785baSKirk McKusick int serrno, maxid, id, fd;
522aee785baSKirk McKusick
523aee785baSKirk McKusick /*
524aee785baSKirk McKusick * Quotas must not be active and quotafile must be open
525aee785baSKirk McKusick * for reading and writing.
526aee785baSKirk McKusick */
527aee785baSKirk McKusick if ((qf->accmode & O_RDWR) != O_RDWR || qf->fd == -1) {
528aee785baSKirk McKusick errno = EBADF;
529aee785baSKirk McKusick return (-1);
530aee785baSKirk McKusick }
531aee785baSKirk McKusick if ((wordsize != 32 && wordsize != 64) ||
532aee785baSKirk McKusick wordsize == qf->wordsize) {
533aee785baSKirk McKusick errno = EINVAL;
534aee785baSKirk McKusick return (-1);
535aee785baSKirk McKusick }
536aee785baSKirk McKusick maxid = quota_maxid(qf);
537aee785baSKirk McKusick if ((newqf = calloc(1, sizeof(*qf))) == NULL) {
538aee785baSKirk McKusick errno = ENOMEM;
539aee785baSKirk McKusick return (-1);
540aee785baSKirk McKusick }
541aee785baSKirk McKusick *newqf = *qf;
542aee785baSKirk McKusick snprintf(newqf->qfname, MAXPATHLEN + 1, "%s_%d.orig", qf->qfname,
543aee785baSKirk McKusick qf->wordsize);
544aee785baSKirk McKusick if (rename(qf->qfname, newqf->qfname) < 0) {
545aee785baSKirk McKusick free(newqf);
546aee785baSKirk McKusick return (-1);
547aee785baSKirk McKusick }
548d1d4d952SJilles Tjoelker if ((newqf->fd = open(qf->qfname, O_RDWR|O_CREAT|O_TRUNC|O_CLOEXEC,
549d1d4d952SJilles Tjoelker 0)) < 0) {
550aee785baSKirk McKusick serrno = errno;
551aee785baSKirk McKusick goto error;
552aee785baSKirk McKusick }
553aee785baSKirk McKusick newqf->wordsize = wordsize;
554aee785baSKirk McKusick if (wordsize == 64) {
555aee785baSKirk McKusick memset(&dqh, 0, sizeof(dqh));
556aee785baSKirk McKusick memcpy(dqh.dqh_magic, Q_DQHDR64_MAGIC, sizeof(dqh.dqh_magic));
557aee785baSKirk McKusick dqh.dqh_version = htobe32(Q_DQHDR64_VERSION);
558aee785baSKirk McKusick dqh.dqh_hdrlen = htobe32(sizeof(struct dqhdr64));
559aee785baSKirk McKusick dqh.dqh_reclen = htobe32(sizeof(struct dqblk64));
560aee785baSKirk McKusick if (write(newqf->fd, &dqh, sizeof(dqh)) != sizeof(dqh)) {
561aee785baSKirk McKusick serrno = errno;
562aee785baSKirk McKusick goto error;
563aee785baSKirk McKusick }
564aee785baSKirk McKusick }
565aee785baSKirk McKusick grp = getgrnam(QUOTAGROUP);
566aee785baSKirk McKusick fchown(newqf->fd, 0, grp ? grp->gr_gid : 0);
567aee785baSKirk McKusick fchmod(newqf->fd, 0640);
568aee785baSKirk McKusick for (id = 0; id <= maxid; id++) {
569aee785baSKirk McKusick if ((quota_read(qf, &dqblk, id)) < 0)
570aee785baSKirk McKusick break;
571aee785baSKirk McKusick switch (newqf->wordsize) {
572aee785baSKirk McKusick case 32:
573aee785baSKirk McKusick if ((quota_write32(newqf, &dqblk, id)) < 0)
574aee785baSKirk McKusick break;
575aee785baSKirk McKusick continue;
576aee785baSKirk McKusick case 64:
577aee785baSKirk McKusick if ((quota_write64(newqf, &dqblk, id)) < 0)
578aee785baSKirk McKusick break;
579aee785baSKirk McKusick continue;
580aee785baSKirk McKusick default:
581aee785baSKirk McKusick errno = EINVAL;
582aee785baSKirk McKusick break;
583aee785baSKirk McKusick }
584aee785baSKirk McKusick }
585aee785baSKirk McKusick if (id < maxid) {
586aee785baSKirk McKusick serrno = errno;
587aee785baSKirk McKusick goto error;
588aee785baSKirk McKusick }
589aee785baSKirk McKusick /*
590aee785baSKirk McKusick * Update the passed in quotafile to reference the new file
591aee785baSKirk McKusick * of the converted format size.
592aee785baSKirk McKusick */
593aee785baSKirk McKusick fd = qf->fd;
594aee785baSKirk McKusick qf->fd = newqf->fd;
595aee785baSKirk McKusick newqf->fd = fd;
596aee785baSKirk McKusick qf->wordsize = newqf->wordsize;
597aee785baSKirk McKusick quota_close(newqf);
598aee785baSKirk McKusick return (0);
599aee785baSKirk McKusick error:
600aee785baSKirk McKusick /* put back the original file */
601aee785baSKirk McKusick (void) rename(newqf->qfname, qf->qfname);
602aee785baSKirk McKusick quota_close(newqf);
603aee785baSKirk McKusick errno = serrno;
604aee785baSKirk McKusick return (-1);
605aee785baSKirk McKusick }
606