/* * Convert a quota file from one format to another. */ int quota_convert(struct quotafile *qf, int wordsize) { struct quotafile *newqf; struct dqhdr64 dqh; struct dqblk dqblk; struct group *grp; int serrno, maxid, id, fd; /* * Quotas must not be active and quotafile must be open * for reading and writing. */ if ((qf->accmode & O_RDWR) != O_RDWR || qf->fd == -1) { errno = EBADF; return (-1); } if ((wordsize != 32 && wordsize != 64) || wordsize == qf->wordsize) { errno = EINVAL; return (-1); } maxid = quota_maxid(qf); if ((newqf = calloc(1, sizeof(*qf))) == NULL) { errno = ENOMEM; return (-1); } *newqf = *qf; snprintf(newqf->qfname, MAXPATHLEN + 1, "%s_%d.orig", qf->qfname, qf->wordsize); if (rename(qf->qfname, newqf->qfname) < 0) { free(newqf); return (-1); } if ((newqf->fd = open(qf->qfname, O_RDWR|O_CREAT|O_TRUNC, 0)) < 0) { serrno = errno; goto error; } newqf->wordsize = wordsize; if (wordsize == 64) { memset(&dqh, 0, sizeof(dqh)); memcpy(dqh.dqh_magic, Q_DQHDR64_MAGIC, sizeof(dqh.dqh_magic)); dqh.dqh_version = htobe32(Q_DQHDR64_VERSION); dqh.dqh_hdrlen = htobe32(sizeof(struct dqhdr64)); dqh.dqh_reclen = htobe32(sizeof(struct dqblk64)); if (write(newqf->fd, &dqh, sizeof(dqh)) != sizeof(dqh)) { serrno = errno; goto error; } } grp = getgrnam(QUOTAGROUP); fchown(newqf->fd, 0, grp ? grp->gr_gid : 0); fchmod(newqf->fd, 0640); for (id = 0; id <= maxid; id++) { if ((quota_read(qf, &dqblk, id)) < 0) break; switch (newqf->wordsize) { case 32: if ((quota_write32(newqf, &dqblk, id)) < 0) break; continue; case 64: if ((quota_write64(newqf, &dqblk, id)) < 0) break; continue; default: errno = EINVAL; break; } } if (id < maxid) { serrno = errno; goto error; } /* * Update the passed in quotafile to reference the new file * of the converted format size. */ fd = qf->fd; qf->fd = newqf->fd; newqf->fd = fd; qf->wordsize = newqf->wordsize; quota_close(newqf); return (0); error: /* put back the original file */ (void) rename(newqf->qfname, qf->qfname); quota_close(newqf); errno = serrno; return (-1); }
EXPORTED int quota_update_useds(const char *quotaroot, const quota_t diff[QUOTA_NUMRESOURCES], const char *mboxname) { struct quota q; struct txn *tid = NULL; int r = 0; struct mboxevent *mboxevents = NULL; if (!quotaroot || !*quotaroot) return IMAP_QUOTAROOT_NONEXISTENT; quota_init(&q, quotaroot); r = quota_read(&q, &tid, 1); if (!r) { int res; int cmp = 1; if (q.scanmbox) { cmp = cyrusdb_compar(qdb, mboxname, strlen(mboxname), q.scanmbox, strlen(q.scanmbox)); } for (res = 0; res < QUOTA_NUMRESOURCES; res++) { int was_over = quota_is_overquota(&q, res, NULL); quota_use(&q, res, diff[res]); if (cmp <= 0) q.scanuseds[res] += diff[res]; if (was_over && !quota_is_overquota(&q, res, NULL)) { struct mboxevent *mboxevent = mboxevent_enqueue(EVENT_QUOTA_WITHIN, &mboxevents); mboxevent_extract_quota(mboxevent, &q, res); } } r = quota_write(&q, &tid); } if (r) { quota_abort(&tid); goto out; } quota_commit(&tid); mboxevent_notify(mboxevents); out: quota_free(&q); if (r) { syslog(LOG_ERR, "LOSTQUOTA: unable to record change of " QUOTA_T_FMT " bytes and " QUOTA_T_FMT " messages in quota %s: %s", diff[QUOTA_STORAGE], diff[QUOTA_MESSAGE], quotaroot, error_message(r)); } mboxevent_freequeue(&mboxevents); return r; }
int quota_write_limits(struct quotafile *qf, struct dqblk *dqb, int id) { struct dqblk dqbuf; int qcmd; if (qf->fd == -1) { qcmd = QCMD(Q_SETQUOTA, qf->quotatype); return (quotactl(qf->fsname, qcmd, id, dqb)); } /* * Have to do read-modify-write of quota in file. */ if ((qf->accmode & O_RDWR) != O_RDWR) { errno = EBADF; return (-1); } if (quota_read(qf, &dqbuf, id) != 0) return (-1); /* * Reset time limit if have a soft limit and were * previously under it, but are now over it * or if there previously was no soft limit, but * now have one and are over it. */ if (dqbuf.dqb_bsoftlimit && id != 0 && dqbuf.dqb_curblocks < dqbuf.dqb_bsoftlimit && dqbuf.dqb_curblocks >= dqb->dqb_bsoftlimit) dqb->dqb_btime = 0; if (dqbuf.dqb_bsoftlimit == 0 && id != 0 && dqb->dqb_bsoftlimit > 0 && dqbuf.dqb_curblocks >= dqb->dqb_bsoftlimit) dqb->dqb_btime = 0; if (dqbuf.dqb_isoftlimit && id != 0 && dqbuf.dqb_curinodes < dqbuf.dqb_isoftlimit && dqbuf.dqb_curinodes >= dqb->dqb_isoftlimit) dqb->dqb_itime = 0; if (dqbuf.dqb_isoftlimit == 0 && id !=0 && dqb->dqb_isoftlimit > 0 && dqbuf.dqb_curinodes >= dqb->dqb_isoftlimit) dqb->dqb_itime = 0; dqb->dqb_curinodes = dqbuf.dqb_curinodes; dqb->dqb_curblocks = dqbuf.dqb_curblocks; /* * Write it back. */ switch (qf->wordsize) { case 32: return (quota_write32(qf, dqb, id)); case 64: return (quota_write64(qf, dqb, id)); default: errno = EINVAL; return (-1); } /* not reached */ }
/* * Check to see if a particular quota is available. */ static int getufsquota(struct fstab *fs, struct quotause *qup, long id, int quotatype) { struct quotafile *qf; if ((qf = quota_open(fs, quotatype, O_RDONLY)) == NULL) return (0); if (quota_read(qf, &qup->dqblk, id) != 0) return (0); quota_close(qf); return (1); }
/* * Collect the requested quota information. */ struct quotause * getprivs(long id, int quotatype, char *fspath) { struct quotafile *qf; struct fstab *fs; struct quotause *qup, *quptail; struct quotause *quphead; setfsent(); quphead = quptail = NULL; while ((fs = getfsent())) { if (fspath && *fspath && strcmp(fspath, fs->fs_spec) && strcmp(fspath, fs->fs_file)) continue; if (strcmp(fs->fs_vfstype, "ufs")) continue; if ((qf = quota_open(fs, quotatype, O_CREAT|O_RDWR)) == NULL) { if (errno != EOPNOTSUPP) warn("cannot open quotas on %s", fs->fs_file); continue; } if ((qup = (struct quotause *)calloc(1, sizeof(*qup))) == NULL) errx(2, "out of memory"); qup->qf = qf; strncpy(qup->fsname, fs->fs_file, sizeof(qup->fsname)); if (quota_read(qf, &qup->dqblk, id) == -1) { warn("cannot read quotas on %s", fs->fs_file); freeprivs(qup); continue; } if (quphead == NULL) quphead = qup; else quptail->next = qup; quptail = qup; qup->next = 0; } if (quphead == NULL) { warnx("No quotas on %s", fspath ? fspath : "any filesystems"); } endfsent(); return (quphead); }
EXPORTED int quota_check_useds(const char *quotaroot, const quota_t diff[QUOTA_NUMRESOURCES]) { int r = 0; struct quota q; int res; /* * We are always allowed to *reduce* usage even if it doesn't get us * below the quota. As a side effect this allows our caller to pass * delta = -1 meaning "don't care about quota checks". */ for (res = 0 ; res < QUOTA_NUMRESOURCES ; res++) { if (diff[res] >= 0) break; } if (res == QUOTA_NUMRESOURCES) return 0; /* all negative */ quota_init(&q, quotaroot); r = quota_read(&q, NULL, /*wrlock*/0); if (r == IMAP_QUOTAROOT_NONEXISTENT) { r = 0; goto done; } if (r) goto done; for (res = 0 ; res < QUOTA_NUMRESOURCES ; res++) { r = quota_check(&q, res, diff[res]); if (r) goto done; } done: quota_free(&q); return r; }
/* * Update a specified quota file. */ int update(const char *fsname, struct quotafile *qf, int type) { struct fileusage *fup; u_long id, lastid, highid = 0; struct dqblk dqbuf; struct stat sb; static struct dqblk zerodqbuf; static struct fileusage zerofileusage; /* * Scan the on-disk quota file and record any usage changes. */ lastid = quota_maxid(qf); for (id = 0; id <= lastid; id++) { if (quota_read(qf, &dqbuf, id) < 0) dqbuf = zerodqbuf; if ((fup = lookup(id, type)) == NULL) fup = &zerofileusage; if (fup->fu_curinodes || fup->fu_curblocks || dqbuf.dqb_bsoftlimit || dqbuf.dqb_bhardlimit || dqbuf.dqb_isoftlimit || dqbuf.dqb_ihardlimit) highid = id; if (dqbuf.dqb_curinodes == fup->fu_curinodes && dqbuf.dqb_curblocks == fup->fu_curblocks) { fup->fu_curinodes = 0; fup->fu_curblocks = 0; continue; } printchanges(fsname, type, &dqbuf, fup, id); dqbuf.dqb_curinodes = fup->fu_curinodes; dqbuf.dqb_curblocks = fup->fu_curblocks; (void) quota_write_usage(qf, &dqbuf, id); fup->fu_curinodes = 0; fup->fu_curblocks = 0; } /* * Walk the hash table looking for ids with non-zero usage * that are not currently recorded in the quota file. E.g. * ids that are past the end of the current file. */ for (id = 0; id < FUHASH; id++) { for (fup = fuhead[type][id]; fup != NULL; fup = fup->fu_next) { if (fup->fu_id <= lastid) continue; if (fup->fu_curinodes == 0 && fup->fu_curblocks == 0) continue; bzero(&dqbuf, sizeof(struct dqblk)); if (fup->fu_id > highid) highid = fup->fu_id; printchanges(fsname, type, &dqbuf, fup, fup->fu_id); dqbuf.dqb_curinodes = fup->fu_curinodes; dqbuf.dqb_curblocks = fup->fu_curblocks; (void) quota_write_usage(qf, &dqbuf, fup->fu_id); fup->fu_curinodes = 0; fup->fu_curblocks = 0; } } /* * If this is old format file, then size may be smaller, * so ensure that we only truncate when it will make things * smaller, and not if it will grow an old format file. */ if (highid < lastid && stat(quota_qfname(qf), &sb) == 0 && sb.st_size > (((off_t)highid + 2) * sizeof(struct dqblk))) truncate(quota_qfname(qf), (((off_t)highid + 2) * sizeof(struct dqblk))); return (0); }
int repquota(struct fstab *fs, int type) { struct fileusage *fup; struct quotafile *qf; u_long id, maxid; struct dqblk dqbuf; static int multiple = 0; if ((qf = quota_open(fs, type, O_RDONLY)) == NULL) { if (vflag && !aflag) { if (multiple++) printf("\n"); fprintf(stdout, "*** No %s quotas on %s (%s)\n", qfextension[type], fs->fs_file, fs->fs_spec); return(1); } return(0); } if (multiple++) printf("\n"); if (vflag) fprintf(stdout, "*** Report for %s quotas on %s (%s)\n", qfextension[type], fs->fs_file, fs->fs_spec); printf("%*s Block limits File limits\n", max(MAXLOGNAME - 1, 10), " "); printf("User%*s used soft hard grace used soft hard grace\n", max(MAXLOGNAME - 1, 10), " "); maxid = quota_maxid(qf); for (id = 0; id <= maxid; id++) { if (quota_read(qf, &dqbuf, id) != 0) break; if (dqbuf.dqb_curinodes == 0 && dqbuf.dqb_curblocks == 0) continue; if ((fup = lookup(id, type)) == 0) fup = addid(id, type, (char *)0); printf("%-*s ", max(MAXLOGNAME - 1, 10), fup->fu_name); printf("%c%c", dqbuf.dqb_bsoftlimit && dqbuf.dqb_curblocks >= dqbuf.dqb_bsoftlimit ? '+' : '-', dqbuf.dqb_isoftlimit && dqbuf.dqb_curinodes >= dqbuf.dqb_isoftlimit ? '+' : '-'); prthumanval(dqbuf.dqb_curblocks); prthumanval(dqbuf.dqb_bsoftlimit); prthumanval(dqbuf.dqb_bhardlimit); printf(" %6s", dqbuf.dqb_bsoftlimit && dqbuf.dqb_curblocks >= dqbuf.dqb_bsoftlimit ? timeprt(dqbuf.dqb_btime) : "-"); printf(" %7ju %7ju %7ju %6s\n", (uintmax_t)dqbuf.dqb_curinodes, (uintmax_t)dqbuf.dqb_isoftlimit, (uintmax_t)dqbuf.dqb_ihardlimit, dqbuf.dqb_isoftlimit && dqbuf.dqb_curinodes >= dqbuf.dqb_isoftlimit ? timeprt(dqbuf.dqb_itime) : "-"); } quota_close(qf); return (0); }