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 ((qf = calloc(1, sizeof(*qf))) == NULL)
126 qf->quotatype = quotatype;
127 strlcpy(qf->fsname, fs->fs_file, sizeof(qf->fsname));
128 if (stat(qf->fsname, &st) != 0)
131 qcmd = QCMD(Q_GETQUOTASIZE, quotatype);
132 if (quotactl(qf->fsname, qcmd, 0, &qf->wordsize) == 0)
134 /* We only check the quota file for ufs */
135 if (strcmp(fs->fs_vfstype, "ufs")) {
139 serrno = hasquota(fs, quotatype, qf->qfname, sizeof(qf->qfname));
144 qf->accmode = openflags & O_ACCMODE;
145 if ((qf->fd = open(qf->qfname, qf->accmode|O_CLOEXEC)) < 0 &&
146 (openflags & O_CREAT) != O_CREAT)
148 /* File open worked, so process it */
151 switch (read(qf->fd, &dqh, sizeof(dqh))) {
155 if (strcmp(dqh.dqh_magic, Q_DQHDR64_MAGIC) != 0) {
156 /* no magic, assume 32 bits */
160 if (be32toh(dqh.dqh_version) != Q_DQHDR64_VERSION ||
161 be32toh(dqh.dqh_hdrlen) != sizeof(struct dqhdr64) ||
162 be32toh(dqh.dqh_reclen) != sizeof(struct dqblk64)) {
163 /* correct magic, wrong version / lengths */
175 /* open failed, but O_CREAT was specified, so create a new file */
176 if ((qf->fd = open(qf->qfname, O_RDWR|O_CREAT|O_TRUNC|O_CLOEXEC, 0)) <
180 memset(&dqh, 0, sizeof(dqh));
181 memcpy(dqh.dqh_magic, Q_DQHDR64_MAGIC, sizeof(dqh.dqh_magic));
182 dqh.dqh_version = htobe32(Q_DQHDR64_VERSION);
183 dqh.dqh_hdrlen = htobe32(sizeof(struct dqhdr64));
184 dqh.dqh_reclen = htobe32(sizeof(struct dqblk64));
185 if (write(qf->fd, &dqh, sizeof(dqh)) != sizeof(dqh)) {
186 /* it was one we created ourselves */
190 grp = getgrnam(QUOTAGROUP);
191 fchown(qf->fd, 0, grp ? grp->gr_gid : 0);
192 fchmod(qf->fd, 0640);
196 /* did we have an open file? */
205 quota_close(struct quotafile *qf)
214 quota_on(struct quotafile *qf)
218 qcmd = QCMD(Q_QUOTAON, qf->quotatype);
219 return (quotactl(qf->fsname, qcmd, 0, qf->qfname));
223 quota_off(struct quotafile *qf)
226 return (quotactl(qf->fsname, QCMD(Q_QUOTAOFF, qf->quotatype), 0, 0));
230 quota_fsname(const struct quotafile *qf)
237 quota_qfname(const struct quotafile *qf)
244 quota_check_path(const struct quotafile *qf, const char *path)
248 if (stat(path, &st) == -1)
250 return (st.st_dev == qf->dev);
254 quota_maxid(struct quotafile *qf)
259 if (stat(qf->qfname, &st) < 0)
261 switch (qf->wordsize) {
263 maxid = st.st_size / sizeof(struct dqblk32) - 1;
266 maxid = st.st_size / sizeof(struct dqblk64) - 2;
272 return (maxid > 0 ? maxid : 0);
276 quota_read32(struct quotafile *qf, struct dqblk *dqb, int id)
278 struct dqblk32 dqb32;
281 off = id * sizeof(struct dqblk32);
282 if (lseek(qf->fd, off, SEEK_SET) == -1)
284 switch (read(qf->fd, &dqb32, sizeof(dqb32))) {
286 memset(dqb, 0, sizeof(*dqb));
289 dqb->dqb_bhardlimit = dqb32.dqb_bhardlimit;
290 dqb->dqb_bsoftlimit = dqb32.dqb_bsoftlimit;
291 dqb->dqb_curblocks = dqb32.dqb_curblocks;
292 dqb->dqb_ihardlimit = dqb32.dqb_ihardlimit;
293 dqb->dqb_isoftlimit = dqb32.dqb_isoftlimit;
294 dqb->dqb_curinodes = dqb32.dqb_curinodes;
295 dqb->dqb_btime = dqb32.dqb_btime;
296 dqb->dqb_itime = dqb32.dqb_itime;
304 quota_read64(struct quotafile *qf, struct dqblk *dqb, int id)
306 struct dqblk64 dqb64;
309 off = sizeof(struct dqhdr64) + id * sizeof(struct dqblk64);
310 if (lseek(qf->fd, off, SEEK_SET) == -1)
312 switch (read(qf->fd, &dqb64, sizeof(dqb64))) {
314 memset(dqb, 0, sizeof(*dqb));
317 dqb->dqb_bhardlimit = be64toh(dqb64.dqb_bhardlimit);
318 dqb->dqb_bsoftlimit = be64toh(dqb64.dqb_bsoftlimit);
319 dqb->dqb_curblocks = be64toh(dqb64.dqb_curblocks);
320 dqb->dqb_ihardlimit = be64toh(dqb64.dqb_ihardlimit);
321 dqb->dqb_isoftlimit = be64toh(dqb64.dqb_isoftlimit);
322 dqb->dqb_curinodes = be64toh(dqb64.dqb_curinodes);
323 dqb->dqb_btime = be64toh(dqb64.dqb_btime);
324 dqb->dqb_itime = be64toh(dqb64.dqb_itime);
332 quota_read(struct quotafile *qf, struct dqblk *dqb, int id)
337 qcmd = QCMD(Q_GETQUOTA, qf->quotatype);
338 return (quotactl(qf->fsname, qcmd, id, dqb));
340 switch (qf->wordsize) {
342 return (quota_read32(qf, dqb, id));
344 return (quota_read64(qf, dqb, id));
352 #define CLIP32(u64) ((u64) > UINT32_MAX ? UINT32_MAX : (uint32_t)(u64))
355 quota_write32(struct quotafile *qf, const struct dqblk *dqb, int id)
357 struct dqblk32 dqb32;
360 dqb32.dqb_bhardlimit = CLIP32(dqb->dqb_bhardlimit);
361 dqb32.dqb_bsoftlimit = CLIP32(dqb->dqb_bsoftlimit);
362 dqb32.dqb_curblocks = CLIP32(dqb->dqb_curblocks);
363 dqb32.dqb_ihardlimit = CLIP32(dqb->dqb_ihardlimit);
364 dqb32.dqb_isoftlimit = CLIP32(dqb->dqb_isoftlimit);
365 dqb32.dqb_curinodes = CLIP32(dqb->dqb_curinodes);
366 dqb32.dqb_btime = CLIP32(dqb->dqb_btime);
367 dqb32.dqb_itime = CLIP32(dqb->dqb_itime);
369 off = id * sizeof(struct dqblk32);
370 if (lseek(qf->fd, off, SEEK_SET) == -1)
372 if (write(qf->fd, &dqb32, sizeof(dqb32)) == sizeof(dqb32))
378 quota_write64(struct quotafile *qf, const struct dqblk *dqb, int id)
380 struct dqblk64 dqb64;
383 dqb64.dqb_bhardlimit = htobe64(dqb->dqb_bhardlimit);
384 dqb64.dqb_bsoftlimit = htobe64(dqb->dqb_bsoftlimit);
385 dqb64.dqb_curblocks = htobe64(dqb->dqb_curblocks);
386 dqb64.dqb_ihardlimit = htobe64(dqb->dqb_ihardlimit);
387 dqb64.dqb_isoftlimit = htobe64(dqb->dqb_isoftlimit);
388 dqb64.dqb_curinodes = htobe64(dqb->dqb_curinodes);
389 dqb64.dqb_btime = htobe64(dqb->dqb_btime);
390 dqb64.dqb_itime = htobe64(dqb->dqb_itime);
392 off = sizeof(struct dqhdr64) + id * sizeof(struct dqblk64);
393 if (lseek(qf->fd, off, SEEK_SET) == -1)
395 if (write(qf->fd, &dqb64, sizeof(dqb64)) == sizeof(dqb64))
401 quota_write_usage(struct quotafile *qf, struct dqblk *dqb, int id)
407 qcmd = QCMD(Q_SETUSE, qf->quotatype);
408 return (quotactl(qf->fsname, qcmd, id, dqb));
411 * Have to do read-modify-write of quota in file.
413 if ((qf->accmode & O_RDWR) != O_RDWR) {
417 if (quota_read(qf, &dqbuf, id) != 0)
420 * Reset time limit if have a soft limit and were
421 * previously under it, but are now over it.
423 if (dqbuf.dqb_bsoftlimit && id != 0 &&
424 dqbuf.dqb_curblocks < dqbuf.dqb_bsoftlimit &&
425 dqb->dqb_curblocks >= dqbuf.dqb_bsoftlimit)
427 if (dqbuf.dqb_isoftlimit && id != 0 &&
428 dqbuf.dqb_curinodes < dqbuf.dqb_isoftlimit &&
429 dqb->dqb_curinodes >= dqbuf.dqb_isoftlimit)
431 dqbuf.dqb_curinodes = dqb->dqb_curinodes;
432 dqbuf.dqb_curblocks = dqb->dqb_curblocks;
436 switch (qf->wordsize) {
438 return (quota_write32(qf, &dqbuf, id));
440 return (quota_write64(qf, &dqbuf, id));
449 quota_write_limits(struct quotafile *qf, struct dqblk *dqb, int id)
455 qcmd = QCMD(Q_SETQUOTA, qf->quotatype);
456 return (quotactl(qf->fsname, qcmd, id, dqb));
459 * Have to do read-modify-write of quota in file.
461 if ((qf->accmode & O_RDWR) != O_RDWR) {
465 if (quota_read(qf, &dqbuf, id) != 0)
468 * Reset time limit if have a soft limit and were
469 * previously under it, but are now over it
470 * or if there previously was no soft limit, but
471 * now have one and are over it.
473 if (dqbuf.dqb_bsoftlimit && id != 0 &&
474 dqbuf.dqb_curblocks < dqbuf.dqb_bsoftlimit &&
475 dqbuf.dqb_curblocks >= dqb->dqb_bsoftlimit)
477 if (dqbuf.dqb_bsoftlimit == 0 && id != 0 &&
478 dqb->dqb_bsoftlimit > 0 &&
479 dqbuf.dqb_curblocks >= dqb->dqb_bsoftlimit)
481 if (dqbuf.dqb_isoftlimit && id != 0 &&
482 dqbuf.dqb_curinodes < dqbuf.dqb_isoftlimit &&
483 dqbuf.dqb_curinodes >= dqb->dqb_isoftlimit)
485 if (dqbuf.dqb_isoftlimit == 0 && id !=0 &&
486 dqb->dqb_isoftlimit > 0 &&
487 dqbuf.dqb_curinodes >= dqb->dqb_isoftlimit)
489 dqb->dqb_curinodes = dqbuf.dqb_curinodes;
490 dqb->dqb_curblocks = dqbuf.dqb_curblocks;
494 switch (qf->wordsize) {
496 return (quota_write32(qf, dqb, id));
498 return (quota_write64(qf, dqb, id));
507 * Convert a quota file from one format to another.
510 quota_convert(struct quotafile *qf, int wordsize)
512 struct quotafile *newqf;
516 int serrno, maxid, id, fd;
519 * Quotas must not be active and quotafile must be open
520 * for reading and writing.
522 if ((qf->accmode & O_RDWR) != O_RDWR || qf->fd == -1) {
526 if ((wordsize != 32 && wordsize != 64) ||
527 wordsize == qf->wordsize) {
531 maxid = quota_maxid(qf);
532 if ((newqf = calloc(1, sizeof(*qf))) == NULL) {
537 snprintf(newqf->qfname, MAXPATHLEN + 1, "%s_%d.orig", qf->qfname,
539 if (rename(qf->qfname, newqf->qfname) < 0) {
543 if ((newqf->fd = open(qf->qfname, O_RDWR|O_CREAT|O_TRUNC|O_CLOEXEC,
548 newqf->wordsize = wordsize;
549 if (wordsize == 64) {
550 memset(&dqh, 0, sizeof(dqh));
551 memcpy(dqh.dqh_magic, Q_DQHDR64_MAGIC, sizeof(dqh.dqh_magic));
552 dqh.dqh_version = htobe32(Q_DQHDR64_VERSION);
553 dqh.dqh_hdrlen = htobe32(sizeof(struct dqhdr64));
554 dqh.dqh_reclen = htobe32(sizeof(struct dqblk64));
555 if (write(newqf->fd, &dqh, sizeof(dqh)) != sizeof(dqh)) {
560 grp = getgrnam(QUOTAGROUP);
561 fchown(newqf->fd, 0, grp ? grp->gr_gid : 0);
562 fchmod(newqf->fd, 0640);
563 for (id = 0; id <= maxid; id++) {
564 if ((quota_read(qf, &dqblk, id)) < 0)
566 switch (newqf->wordsize) {
568 if ((quota_write32(newqf, &dqblk, id)) < 0)
572 if ((quota_write64(newqf, &dqblk, id)) < 0)
585 * Update the passed in quotafile to reference the new file
586 * of the converted format size.
591 qf->wordsize = newqf->wordsize;
595 /* put back the original file */
596 (void) rename(newqf->qfname, qf->qfname);