2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4 * Copyright (c) 2008 Dag-Erling Coïdan Smørgrav
5 * Copyright (c) 2008 Marshall Kirk McKusick
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer
13 * in this position and unchanged.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 #include <sys/types.h>
34 #include <sys/endian.h>
35 #include <sys/mount.h>
38 #include <ufs/ufs/quota.h>
53 int fd; /* -1 means using quotactl for access */
54 int accmode; /* access mode */
55 int wordsize; /* 32-bit or 64-bit limits */
56 int quotatype; /* USRQUOTA or GRPQUOTA */
57 dev_t dev; /* device */
58 char fsname[MAXPATHLEN + 1]; /* mount point of filesystem */
59 char qfname[MAXPATHLEN + 1]; /* quota file if not using quotactl */
62 static const char *qfextension[] = INITQFNAMES;
65 * Check to see if a particular quota is to be enabled.
68 hasquota(struct fstab *fs, int type, char *qfnamep, int qfbufsize)
74 static char initname, usrname[100], grpname[100];
77 * 1) we only need one of these
78 * 2) fstab may specify a different filename
81 (void)snprintf(usrname, sizeof(usrname), "%s%s",
82 qfextension[USRQUOTA], QUOTAFILENAME);
83 (void)snprintf(grpname, sizeof(grpname), "%s%s",
84 qfextension[GRPQUOTA], QUOTAFILENAME);
87 strcpy(buf, fs->fs_mntops);
88 for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) {
89 if ((cp = strchr(opt, '=')))
91 if (type == USRQUOTA && strcmp(opt, usrname) == 0)
93 if (type == GRPQUOTA && strcmp(opt, grpname) == 0)
99 * Ensure that the filesystem is mounted.
101 if (statfs(fs->fs_file, &sfb) != 0 ||
102 strcmp(fs->fs_file, sfb.f_mntonname)) {
106 strncpy(qfnamep, cp, qfbufsize);
108 (void)snprintf(qfnamep, qfbufsize, "%s/%s.%s", fs->fs_file,
109 QUOTAFILENAME, qfextension[type]);
115 quota_open(struct fstab *fs, int quotatype, int openflags)
117 struct quotafile *qf;
123 if (strcmp(fs->fs_vfstype, "ufs"))
125 if ((qf = calloc(1, sizeof(*qf))) == NULL)
128 qf->quotatype = quotatype;
129 strlcpy(qf->fsname, fs->fs_file, sizeof(qf->fsname));
130 if (stat(qf->fsname, &st) != 0)
133 serrno = hasquota(fs, quotatype, qf->qfname, sizeof(qf->qfname));
134 qcmd = QCMD(Q_GETQUOTASIZE, quotatype);
135 if (quotactl(qf->fsname, qcmd, 0, &qf->wordsize) == 0)
141 qf->accmode = openflags & O_ACCMODE;
142 if ((qf->fd = open(qf->qfname, qf->accmode|O_CLOEXEC)) < 0 &&
143 (openflags & O_CREAT) != O_CREAT)
145 /* File open worked, so process it */
148 switch (read(qf->fd, &dqh, sizeof(dqh))) {
152 if (strcmp(dqh.dqh_magic, Q_DQHDR64_MAGIC) != 0) {
153 /* no magic, assume 32 bits */
157 if (be32toh(dqh.dqh_version) != Q_DQHDR64_VERSION ||
158 be32toh(dqh.dqh_hdrlen) != sizeof(struct dqhdr64) ||
159 be32toh(dqh.dqh_reclen) != sizeof(struct dqblk64)) {
160 /* correct magic, wrong version / lengths */
172 /* open failed, but O_CREAT was specified, so create a new file */
173 if ((qf->fd = open(qf->qfname, O_RDWR|O_CREAT|O_TRUNC|O_CLOEXEC, 0)) <
177 memset(&dqh, 0, sizeof(dqh));
178 memcpy(dqh.dqh_magic, Q_DQHDR64_MAGIC, sizeof(dqh.dqh_magic));
179 dqh.dqh_version = htobe32(Q_DQHDR64_VERSION);
180 dqh.dqh_hdrlen = htobe32(sizeof(struct dqhdr64));
181 dqh.dqh_reclen = htobe32(sizeof(struct dqblk64));
182 if (write(qf->fd, &dqh, sizeof(dqh)) != sizeof(dqh)) {
183 /* it was one we created ourselves */
187 grp = getgrnam(QUOTAGROUP);
188 fchown(qf->fd, 0, grp ? grp->gr_gid : 0);
189 fchmod(qf->fd, 0640);
193 /* did we have an open file? */
202 quota_close(struct quotafile *qf)
211 quota_on(struct quotafile *qf)
215 qcmd = QCMD(Q_QUOTAON, qf->quotatype);
216 return (quotactl(qf->fsname, qcmd, 0, qf->qfname));
220 quota_off(struct quotafile *qf)
223 return (quotactl(qf->fsname, QCMD(Q_QUOTAOFF, qf->quotatype), 0, 0));
227 quota_fsname(const struct quotafile *qf)
234 quota_qfname(const struct quotafile *qf)
241 quota_check_path(const struct quotafile *qf, const char *path)
245 if (stat(path, &st) == -1)
247 return (st.st_dev == qf->dev);
251 quota_maxid(struct quotafile *qf)
256 if (stat(qf->qfname, &st) < 0)
258 switch (qf->wordsize) {
260 maxid = st.st_size / sizeof(struct dqblk32) - 1;
263 maxid = st.st_size / sizeof(struct dqblk64) - 2;
269 return (maxid > 0 ? maxid : 0);
273 quota_read32(struct quotafile *qf, struct dqblk *dqb, int id)
275 struct dqblk32 dqb32;
278 off = id * sizeof(struct dqblk32);
279 if (lseek(qf->fd, off, SEEK_SET) == -1)
281 switch (read(qf->fd, &dqb32, sizeof(dqb32))) {
283 memset(dqb, 0, sizeof(*dqb));
286 dqb->dqb_bhardlimit = dqb32.dqb_bhardlimit;
287 dqb->dqb_bsoftlimit = dqb32.dqb_bsoftlimit;
288 dqb->dqb_curblocks = dqb32.dqb_curblocks;
289 dqb->dqb_ihardlimit = dqb32.dqb_ihardlimit;
290 dqb->dqb_isoftlimit = dqb32.dqb_isoftlimit;
291 dqb->dqb_curinodes = dqb32.dqb_curinodes;
292 dqb->dqb_btime = dqb32.dqb_btime;
293 dqb->dqb_itime = dqb32.dqb_itime;
301 quota_read64(struct quotafile *qf, struct dqblk *dqb, int id)
303 struct dqblk64 dqb64;
306 off = sizeof(struct dqhdr64) + id * sizeof(struct dqblk64);
307 if (lseek(qf->fd, off, SEEK_SET) == -1)
309 switch (read(qf->fd, &dqb64, sizeof(dqb64))) {
311 memset(dqb, 0, sizeof(*dqb));
314 dqb->dqb_bhardlimit = be64toh(dqb64.dqb_bhardlimit);
315 dqb->dqb_bsoftlimit = be64toh(dqb64.dqb_bsoftlimit);
316 dqb->dqb_curblocks = be64toh(dqb64.dqb_curblocks);
317 dqb->dqb_ihardlimit = be64toh(dqb64.dqb_ihardlimit);
318 dqb->dqb_isoftlimit = be64toh(dqb64.dqb_isoftlimit);
319 dqb->dqb_curinodes = be64toh(dqb64.dqb_curinodes);
320 dqb->dqb_btime = be64toh(dqb64.dqb_btime);
321 dqb->dqb_itime = be64toh(dqb64.dqb_itime);
329 quota_read(struct quotafile *qf, struct dqblk *dqb, int id)
334 qcmd = QCMD(Q_GETQUOTA, qf->quotatype);
335 return (quotactl(qf->fsname, qcmd, id, dqb));
337 switch (qf->wordsize) {
339 return (quota_read32(qf, dqb, id));
341 return (quota_read64(qf, dqb, id));
349 #define CLIP32(u64) ((u64) > UINT32_MAX ? UINT32_MAX : (uint32_t)(u64))
352 quota_write32(struct quotafile *qf, const struct dqblk *dqb, int id)
354 struct dqblk32 dqb32;
357 dqb32.dqb_bhardlimit = CLIP32(dqb->dqb_bhardlimit);
358 dqb32.dqb_bsoftlimit = CLIP32(dqb->dqb_bsoftlimit);
359 dqb32.dqb_curblocks = CLIP32(dqb->dqb_curblocks);
360 dqb32.dqb_ihardlimit = CLIP32(dqb->dqb_ihardlimit);
361 dqb32.dqb_isoftlimit = CLIP32(dqb->dqb_isoftlimit);
362 dqb32.dqb_curinodes = CLIP32(dqb->dqb_curinodes);
363 dqb32.dqb_btime = CLIP32(dqb->dqb_btime);
364 dqb32.dqb_itime = CLIP32(dqb->dqb_itime);
366 off = id * sizeof(struct dqblk32);
367 if (lseek(qf->fd, off, SEEK_SET) == -1)
369 if (write(qf->fd, &dqb32, sizeof(dqb32)) == sizeof(dqb32))
375 quota_write64(struct quotafile *qf, const struct dqblk *dqb, int id)
377 struct dqblk64 dqb64;
380 dqb64.dqb_bhardlimit = htobe64(dqb->dqb_bhardlimit);
381 dqb64.dqb_bsoftlimit = htobe64(dqb->dqb_bsoftlimit);
382 dqb64.dqb_curblocks = htobe64(dqb->dqb_curblocks);
383 dqb64.dqb_ihardlimit = htobe64(dqb->dqb_ihardlimit);
384 dqb64.dqb_isoftlimit = htobe64(dqb->dqb_isoftlimit);
385 dqb64.dqb_curinodes = htobe64(dqb->dqb_curinodes);
386 dqb64.dqb_btime = htobe64(dqb->dqb_btime);
387 dqb64.dqb_itime = htobe64(dqb->dqb_itime);
389 off = sizeof(struct dqhdr64) + id * sizeof(struct dqblk64);
390 if (lseek(qf->fd, off, SEEK_SET) == -1)
392 if (write(qf->fd, &dqb64, sizeof(dqb64)) == sizeof(dqb64))
398 quota_write_usage(struct quotafile *qf, struct dqblk *dqb, int id)
404 qcmd = QCMD(Q_SETUSE, qf->quotatype);
405 return (quotactl(qf->fsname, qcmd, id, dqb));
408 * Have to do read-modify-write of quota in file.
410 if ((qf->accmode & O_RDWR) != O_RDWR) {
414 if (quota_read(qf, &dqbuf, id) != 0)
417 * Reset time limit if have a soft limit and were
418 * previously under it, but are now over it.
420 if (dqbuf.dqb_bsoftlimit && id != 0 &&
421 dqbuf.dqb_curblocks < dqbuf.dqb_bsoftlimit &&
422 dqb->dqb_curblocks >= dqbuf.dqb_bsoftlimit)
424 if (dqbuf.dqb_isoftlimit && id != 0 &&
425 dqbuf.dqb_curinodes < dqbuf.dqb_isoftlimit &&
426 dqb->dqb_curinodes >= dqbuf.dqb_isoftlimit)
428 dqbuf.dqb_curinodes = dqb->dqb_curinodes;
429 dqbuf.dqb_curblocks = dqb->dqb_curblocks;
433 switch (qf->wordsize) {
435 return (quota_write32(qf, &dqbuf, id));
437 return (quota_write64(qf, &dqbuf, id));
446 quota_write_limits(struct quotafile *qf, struct dqblk *dqb, int id)
452 qcmd = QCMD(Q_SETQUOTA, qf->quotatype);
453 return (quotactl(qf->fsname, qcmd, id, dqb));
456 * Have to do read-modify-write of quota in file.
458 if ((qf->accmode & O_RDWR) != O_RDWR) {
462 if (quota_read(qf, &dqbuf, id) != 0)
465 * Reset time limit if have a soft limit and were
466 * previously under it, but are now over it
467 * or if there previously was no soft limit, but
468 * now have one and are over it.
470 if (dqbuf.dqb_bsoftlimit && id != 0 &&
471 dqbuf.dqb_curblocks < dqbuf.dqb_bsoftlimit &&
472 dqbuf.dqb_curblocks >= dqb->dqb_bsoftlimit)
474 if (dqbuf.dqb_bsoftlimit == 0 && id != 0 &&
475 dqb->dqb_bsoftlimit > 0 &&
476 dqbuf.dqb_curblocks >= dqb->dqb_bsoftlimit)
478 if (dqbuf.dqb_isoftlimit && id != 0 &&
479 dqbuf.dqb_curinodes < dqbuf.dqb_isoftlimit &&
480 dqbuf.dqb_curinodes >= dqb->dqb_isoftlimit)
482 if (dqbuf.dqb_isoftlimit == 0 && id !=0 &&
483 dqb->dqb_isoftlimit > 0 &&
484 dqbuf.dqb_curinodes >= dqb->dqb_isoftlimit)
486 dqb->dqb_curinodes = dqbuf.dqb_curinodes;
487 dqb->dqb_curblocks = dqbuf.dqb_curblocks;
491 switch (qf->wordsize) {
493 return (quota_write32(qf, dqb, id));
495 return (quota_write64(qf, dqb, id));
504 * Convert a quota file from one format to another.
507 quota_convert(struct quotafile *qf, int wordsize)
509 struct quotafile *newqf;
513 int serrno, maxid, id, fd;
516 * Quotas must not be active and quotafile must be open
517 * for reading and writing.
519 if ((qf->accmode & O_RDWR) != O_RDWR || qf->fd == -1) {
523 if ((wordsize != 32 && wordsize != 64) ||
524 wordsize == qf->wordsize) {
528 maxid = quota_maxid(qf);
529 if ((newqf = calloc(1, sizeof(*qf))) == NULL) {
534 snprintf(newqf->qfname, MAXPATHLEN + 1, "%s_%d.orig", qf->qfname,
536 if (rename(qf->qfname, newqf->qfname) < 0) {
540 if ((newqf->fd = open(qf->qfname, O_RDWR|O_CREAT|O_TRUNC|O_CLOEXEC,
545 newqf->wordsize = wordsize;
546 if (wordsize == 64) {
547 memset(&dqh, 0, sizeof(dqh));
548 memcpy(dqh.dqh_magic, Q_DQHDR64_MAGIC, sizeof(dqh.dqh_magic));
549 dqh.dqh_version = htobe32(Q_DQHDR64_VERSION);
550 dqh.dqh_hdrlen = htobe32(sizeof(struct dqhdr64));
551 dqh.dqh_reclen = htobe32(sizeof(struct dqblk64));
552 if (write(newqf->fd, &dqh, sizeof(dqh)) != sizeof(dqh)) {
557 grp = getgrnam(QUOTAGROUP);
558 fchown(newqf->fd, 0, grp ? grp->gr_gid : 0);
559 fchmod(newqf->fd, 0640);
560 for (id = 0; id <= maxid; id++) {
561 if ((quota_read(qf, &dqblk, id)) < 0)
563 switch (newqf->wordsize) {
565 if ((quota_write32(newqf, &dqblk, id)) < 0)
569 if ((quota_write64(newqf, &dqblk, id)) < 0)
582 * Update the passed in quotafile to reference the new file
583 * of the converted format size.
588 qf->wordsize = newqf->wordsize;
592 /* put back the original file */
593 (void) rename(newqf->qfname, qf->qfname);