1187914Sdes/*- 2187914Sdes * Copyright (c) 2008 Dag-Erling Co��dan Sm��rgrav 3188604Smckusick * Copyright (c) 2008 Marshall Kirk McKusick 4187914Sdes * All rights reserved. 5187914Sdes * 6187914Sdes * Redistribution and use in source and binary forms, with or without 7187914Sdes * modification, are permitted provided that the following conditions 8187914Sdes * are met: 9187914Sdes * 1. Redistributions of source code must retain the above copyright 10187914Sdes * notice, this list of conditions and the following disclaimer 11187914Sdes * in this position and unchanged. 12187914Sdes * 2. Redistributions in binary form must reproduce the above copyright 13187914Sdes * notice, this list of conditions and the following disclaimer in the 14187914Sdes * documentation and/or other materials provided with the distribution. 15187914Sdes * 16187914Sdes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17187914Sdes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18187914Sdes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19187914Sdes * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20187914Sdes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21187914Sdes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22187914Sdes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23187914Sdes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24187914Sdes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25187914Sdes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26187914Sdes * SUCH DAMAGE. 27187914Sdes * 28187914Sdes * $FreeBSD$ 29187914Sdes */ 30187914Sdes 31187914Sdes#include <sys/types.h> 32187914Sdes#include <sys/endian.h> 33188568Smckusick#include <sys/mount.h> 34187914Sdes#include <sys/stat.h> 35187914Sdes 36187914Sdes#include <ufs/ufs/quota.h> 37187914Sdes 38187914Sdes#include <errno.h> 39187914Sdes#include <fcntl.h> 40188568Smckusick#include <fstab.h> 41187914Sdes#include <grp.h> 42187914Sdes#include <pwd.h> 43187914Sdes#include <libutil.h> 44187914Sdes#include <stdint.h> 45188568Smckusick#include <stdio.h> 46187914Sdes#include <stdlib.h> 47187914Sdes#include <string.h> 48187914Sdes#include <unistd.h> 49187914Sdes 50187914Sdesstruct quotafile { 51188604Smckusick int fd; /* -1 means using quotactl for access */ 52197532Sdes int accmode; /* access mode */ 53188604Smckusick int wordsize; /* 32-bit or 64-bit limits */ 54188604Smckusick int quotatype; /* USRQUOTA or GRPQUOTA */ 55197532Sdes dev_t dev; /* device */ 56188604Smckusick char fsname[MAXPATHLEN + 1]; /* mount point of filesystem */ 57188604Smckusick char qfname[MAXPATHLEN + 1]; /* quota file if not using quotactl */ 58187914Sdes}; 59187914Sdes 60188568Smckusickstatic const char *qfextension[] = INITQFNAMES; 61188568Smckusick 62188604Smckusick/* 63188604Smckusick * Check to see if a particular quota is to be enabled. 64188604Smckusick */ 65188604Smckusickstatic int 66188604Smckusickhasquota(struct fstab *fs, int type, char *qfnamep, int qfbufsize) 67188604Smckusick{ 68188604Smckusick char *opt; 69188604Smckusick char *cp; 70188604Smckusick struct statfs sfb; 71188604Smckusick char buf[BUFSIZ]; 72188604Smckusick static char initname, usrname[100], grpname[100]; 73188604Smckusick 74197532Sdes /* 75197532Sdes * 1) we only need one of these 76197532Sdes * 2) fstab may specify a different filename 77197532Sdes */ 78188604Smckusick if (!initname) { 79188604Smckusick (void)snprintf(usrname, sizeof(usrname), "%s%s", 80188604Smckusick qfextension[USRQUOTA], QUOTAFILENAME); 81188604Smckusick (void)snprintf(grpname, sizeof(grpname), "%s%s", 82188604Smckusick qfextension[GRPQUOTA], QUOTAFILENAME); 83188604Smckusick initname = 1; 84188604Smckusick } 85188604Smckusick strcpy(buf, fs->fs_mntops); 86188604Smckusick for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) { 87229403Sed if ((cp = strchr(opt, '='))) 88188604Smckusick *cp++ = '\0'; 89188604Smckusick if (type == USRQUOTA && strcmp(opt, usrname) == 0) 90188604Smckusick break; 91188604Smckusick if (type == GRPQUOTA && strcmp(opt, grpname) == 0) 92188604Smckusick break; 93188604Smckusick } 94188604Smckusick if (!opt) 95188604Smckusick return (0); 96188604Smckusick /* 97188604Smckusick * Ensure that the filesystem is mounted. 98188604Smckusick */ 99188604Smckusick if (statfs(fs->fs_file, &sfb) != 0 || 100188604Smckusick strcmp(fs->fs_file, sfb.f_mntonname)) { 101188604Smckusick return (0); 102188604Smckusick } 103188604Smckusick if (cp) { 104188604Smckusick strncpy(qfnamep, cp, qfbufsize); 105188604Smckusick } else { 106188604Smckusick (void)snprintf(qfnamep, qfbufsize, "%s/%s.%s", fs->fs_file, 107188604Smckusick QUOTAFILENAME, qfextension[type]); 108188604Smckusick } 109188604Smckusick return (1); 110188604Smckusick} 111188604Smckusick 112187914Sdesstruct quotafile * 113188604Smckusickquota_open(struct fstab *fs, int quotatype, int openflags) 114187914Sdes{ 115187914Sdes struct quotafile *qf; 116187914Sdes struct dqhdr64 dqh; 117188604Smckusick struct group *grp; 118197532Sdes struct stat st; 119188604Smckusick int qcmd, serrno; 120187914Sdes 121201037Smckusick if (strcmp(fs->fs_vfstype, "ufs")) 122201037Smckusick return (NULL); 123187914Sdes if ((qf = calloc(1, sizeof(*qf))) == NULL) 124187914Sdes return (NULL); 125197532Sdes qf->fd = -1; 126188604Smckusick qf->quotatype = quotatype; 127188604Smckusick strncpy(qf->fsname, fs->fs_file, sizeof(qf->fsname)); 128197532Sdes if (stat(qf->fsname, &st) != 0) 129197532Sdes goto error; 130197532Sdes qf->dev = st.st_dev; 131198265Smckusick serrno = hasquota(fs, quotatype, qf->qfname, sizeof(qf->qfname)); 132205207Smckusick qcmd = QCMD(Q_GETQUOTASIZE, quotatype); 133205207Smckusick if (quotactl(qf->fsname, qcmd, 0, &qf->wordsize) == 0) 134188604Smckusick return (qf); 135198265Smckusick if (serrno == 0) { 136188604Smckusick errno = EOPNOTSUPP; 137197532Sdes goto error; 138187914Sdes } 139197532Sdes qf->accmode = openflags & O_ACCMODE; 140255007Sjilles if ((qf->fd = open(qf->qfname, qf->accmode|O_CLOEXEC)) < 0 && 141197532Sdes (openflags & O_CREAT) != O_CREAT) 142197532Sdes goto error; 143188604Smckusick /* File open worked, so process it */ 144188604Smckusick if (qf->fd != -1) { 145188604Smckusick qf->wordsize = 32; 146188604Smckusick switch (read(qf->fd, &dqh, sizeof(dqh))) { 147188604Smckusick case -1: 148197532Sdes goto error; 149188604Smckusick case sizeof(dqh): 150188604Smckusick if (strcmp(dqh.dqh_magic, Q_DQHDR64_MAGIC) != 0) { 151188604Smckusick /* no magic, assume 32 bits */ 152188604Smckusick qf->wordsize = 32; 153188604Smckusick return (qf); 154188604Smckusick } 155188604Smckusick if (be32toh(dqh.dqh_version) != Q_DQHDR64_VERSION || 156188604Smckusick be32toh(dqh.dqh_hdrlen) != sizeof(struct dqhdr64) || 157188604Smckusick be32toh(dqh.dqh_reclen) != sizeof(struct dqblk64)) { 158188604Smckusick /* correct magic, wrong version / lengths */ 159188604Smckusick errno = EINVAL; 160197532Sdes goto error; 161188604Smckusick } 162188604Smckusick qf->wordsize = 64; 163188604Smckusick return (qf); 164188604Smckusick default: 165188604Smckusick qf->wordsize = 32; 166188604Smckusick return (qf); 167187914Sdes } 168188604Smckusick /* not reached */ 169187914Sdes } 170197532Sdes /* open failed, but O_CREAT was specified, so create a new file */ 171255007Sjilles if ((qf->fd = open(qf->qfname, O_RDWR|O_CREAT|O_TRUNC|O_CLOEXEC, 0)) < 172255007Sjilles 0) 173197532Sdes goto error; 174188604Smckusick qf->wordsize = 64; 175187914Sdes memset(&dqh, 0, sizeof(dqh)); 176187914Sdes memcpy(dqh.dqh_magic, Q_DQHDR64_MAGIC, sizeof(dqh.dqh_magic)); 177187914Sdes dqh.dqh_version = htobe32(Q_DQHDR64_VERSION); 178187914Sdes dqh.dqh_hdrlen = htobe32(sizeof(struct dqhdr64)); 179187914Sdes dqh.dqh_reclen = htobe32(sizeof(struct dqblk64)); 180201037Smckusick if (write(qf->fd, &dqh, sizeof(dqh)) != sizeof(dqh)) { 181201037Smckusick /* it was one we created ourselves */ 182201037Smckusick unlink(qf->qfname); 183197532Sdes goto error; 184201037Smckusick } 185187914Sdes grp = getgrnam(QUOTAGROUP); 186187914Sdes fchown(qf->fd, 0, grp ? grp->gr_gid : 0); 187187914Sdes fchmod(qf->fd, 0640); 188187914Sdes return (qf); 189197532Sdeserror: 190197532Sdes serrno = errno; 191197532Sdes /* did we have an open file? */ 192201037Smckusick if (qf->fd != -1) 193197532Sdes close(qf->fd); 194197532Sdes free(qf); 195197532Sdes errno = serrno; 196197532Sdes return (NULL); 197187914Sdes} 198187914Sdes 199187914Sdesvoid 200187914Sdesquota_close(struct quotafile *qf) 201187914Sdes{ 202187914Sdes 203188604Smckusick if (qf->fd != -1) 204188604Smckusick close(qf->fd); 205187914Sdes free(qf); 206187914Sdes} 207187914Sdes 208199328Smckusickint 209199328Smckusickquota_on(struct quotafile *qf) 210199328Smckusick{ 211199328Smckusick int qcmd; 212199328Smckusick 213199328Smckusick qcmd = QCMD(Q_QUOTAON, qf->quotatype); 214199328Smckusick return (quotactl(qf->fsname, qcmd, 0, qf->qfname)); 215199328Smckusick} 216199328Smckusick 217199328Smckusickint 218199328Smckusickquota_off(struct quotafile *qf) 219199328Smckusick{ 220199328Smckusick 221199328Smckusick return (quotactl(qf->fsname, QCMD(Q_QUOTAOFF, qf->quotatype), 0, 0)); 222199328Smckusick} 223199328Smckusick 224197532Sdesconst char * 225197532Sdesquota_fsname(const struct quotafile *qf) 226197532Sdes{ 227197532Sdes 228197532Sdes return (qf->fsname); 229197532Sdes} 230197532Sdes 231197532Sdesconst char * 232197532Sdesquota_qfname(const struct quotafile *qf) 233197532Sdes{ 234197532Sdes 235197532Sdes return (qf->qfname); 236197532Sdes} 237197532Sdes 238197532Sdesint 239197532Sdesquota_check_path(const struct quotafile *qf, const char *path) 240197532Sdes{ 241197532Sdes struct stat st; 242197532Sdes 243197532Sdes if (stat(path, &st) == -1) 244197532Sdes return (-1); 245197532Sdes return (st.st_dev == qf->dev); 246197532Sdes} 247197532Sdes 248198265Smckusickint 249198265Smckusickquota_maxid(struct quotafile *qf) 250198265Smckusick{ 251198265Smckusick struct stat st; 252205207Smckusick int maxid; 253198265Smckusick 254198265Smckusick if (stat(qf->qfname, &st) < 0) 255198265Smckusick return (0); 256198265Smckusick switch (qf->wordsize) { 257198265Smckusick case 32: 258205207Smckusick maxid = st.st_size / sizeof(struct dqblk32) - 1; 259205207Smckusick break; 260198265Smckusick case 64: 261205207Smckusick maxid = st.st_size / sizeof(struct dqblk64) - 2; 262205207Smckusick break; 263198265Smckusick default: 264205207Smckusick maxid = 0; 265205207Smckusick break; 266198265Smckusick } 267205207Smckusick return (maxid > 0 ? maxid : 0); 268198265Smckusick} 269198265Smckusick 270187914Sdesstatic int 271187914Sdesquota_read32(struct quotafile *qf, struct dqblk *dqb, int id) 272187914Sdes{ 273187914Sdes struct dqblk32 dqb32; 274187914Sdes off_t off; 275187914Sdes 276187914Sdes off = id * sizeof(struct dqblk32); 277187914Sdes if (lseek(qf->fd, off, SEEK_SET) == -1) 278187914Sdes return (-1); 279187914Sdes switch (read(qf->fd, &dqb32, sizeof(dqb32))) { 280187914Sdes case 0: 281198265Smckusick memset(dqb, 0, sizeof(*dqb)); 282187914Sdes return (0); 283187914Sdes case sizeof(dqb32): 284187914Sdes dqb->dqb_bhardlimit = dqb32.dqb_bhardlimit; 285187914Sdes dqb->dqb_bsoftlimit = dqb32.dqb_bsoftlimit; 286187914Sdes dqb->dqb_curblocks = dqb32.dqb_curblocks; 287187914Sdes dqb->dqb_ihardlimit = dqb32.dqb_ihardlimit; 288187914Sdes dqb->dqb_isoftlimit = dqb32.dqb_isoftlimit; 289187914Sdes dqb->dqb_curinodes = dqb32.dqb_curinodes; 290187914Sdes dqb->dqb_btime = dqb32.dqb_btime; 291187914Sdes dqb->dqb_itime = dqb32.dqb_itime; 292187914Sdes return (0); 293187914Sdes default: 294187914Sdes return (-1); 295187914Sdes } 296187914Sdes} 297187914Sdes 298187914Sdesstatic int 299187914Sdesquota_read64(struct quotafile *qf, struct dqblk *dqb, int id) 300187914Sdes{ 301187914Sdes struct dqblk64 dqb64; 302187914Sdes off_t off; 303187914Sdes 304187914Sdes off = sizeof(struct dqhdr64) + id * sizeof(struct dqblk64); 305187914Sdes if (lseek(qf->fd, off, SEEK_SET) == -1) 306187914Sdes return (-1); 307187914Sdes switch (read(qf->fd, &dqb64, sizeof(dqb64))) { 308187914Sdes case 0: 309198265Smckusick memset(dqb, 0, sizeof(*dqb)); 310187914Sdes return (0); 311187914Sdes case sizeof(dqb64): 312187914Sdes dqb->dqb_bhardlimit = be64toh(dqb64.dqb_bhardlimit); 313187914Sdes dqb->dqb_bsoftlimit = be64toh(dqb64.dqb_bsoftlimit); 314187914Sdes dqb->dqb_curblocks = be64toh(dqb64.dqb_curblocks); 315187914Sdes dqb->dqb_ihardlimit = be64toh(dqb64.dqb_ihardlimit); 316187914Sdes dqb->dqb_isoftlimit = be64toh(dqb64.dqb_isoftlimit); 317187914Sdes dqb->dqb_curinodes = be64toh(dqb64.dqb_curinodes); 318187914Sdes dqb->dqb_btime = be64toh(dqb64.dqb_btime); 319187914Sdes dqb->dqb_itime = be64toh(dqb64.dqb_itime); 320187914Sdes return (0); 321187914Sdes default: 322187914Sdes return (-1); 323187914Sdes } 324187914Sdes} 325187914Sdes 326187914Sdesint 327187914Sdesquota_read(struct quotafile *qf, struct dqblk *dqb, int id) 328187914Sdes{ 329188604Smckusick int qcmd; 330187914Sdes 331188604Smckusick if (qf->fd == -1) { 332188604Smckusick qcmd = QCMD(Q_GETQUOTA, qf->quotatype); 333188604Smckusick return (quotactl(qf->fsname, qcmd, id, dqb)); 334188604Smckusick } 335188604Smckusick switch (qf->wordsize) { 336187914Sdes case 32: 337197510Sdes return (quota_read32(qf, dqb, id)); 338187914Sdes case 64: 339197510Sdes return (quota_read64(qf, dqb, id)); 340187914Sdes default: 341187914Sdes errno = EINVAL; 342187914Sdes return (-1); 343187914Sdes } 344187914Sdes /* not reached */ 345187914Sdes} 346187914Sdes 347187914Sdes#define CLIP32(u64) ((u64) > UINT32_MAX ? UINT32_MAX : (uint32_t)(u64)) 348187914Sdes 349187914Sdesstatic int 350187914Sdesquota_write32(struct quotafile *qf, const struct dqblk *dqb, int id) 351187914Sdes{ 352187914Sdes struct dqblk32 dqb32; 353187914Sdes off_t off; 354187914Sdes 355187914Sdes dqb32.dqb_bhardlimit = CLIP32(dqb->dqb_bhardlimit); 356187914Sdes dqb32.dqb_bsoftlimit = CLIP32(dqb->dqb_bsoftlimit); 357187914Sdes dqb32.dqb_curblocks = CLIP32(dqb->dqb_curblocks); 358187914Sdes dqb32.dqb_ihardlimit = CLIP32(dqb->dqb_ihardlimit); 359187914Sdes dqb32.dqb_isoftlimit = CLIP32(dqb->dqb_isoftlimit); 360187914Sdes dqb32.dqb_curinodes = CLIP32(dqb->dqb_curinodes); 361187914Sdes dqb32.dqb_btime = CLIP32(dqb->dqb_btime); 362187914Sdes dqb32.dqb_itime = CLIP32(dqb->dqb_itime); 363187914Sdes 364187914Sdes off = id * sizeof(struct dqblk32); 365187914Sdes if (lseek(qf->fd, off, SEEK_SET) == -1) 366187914Sdes return (-1); 367188604Smckusick if (write(qf->fd, &dqb32, sizeof(dqb32)) == sizeof(dqb32)) 368188604Smckusick return (0); 369188604Smckusick return (-1); 370187914Sdes} 371187914Sdes 372187914Sdesstatic int 373187914Sdesquota_write64(struct quotafile *qf, const struct dqblk *dqb, int id) 374187914Sdes{ 375187914Sdes struct dqblk64 dqb64; 376187914Sdes off_t off; 377187914Sdes 378187914Sdes dqb64.dqb_bhardlimit = htobe64(dqb->dqb_bhardlimit); 379187914Sdes dqb64.dqb_bsoftlimit = htobe64(dqb->dqb_bsoftlimit); 380187914Sdes dqb64.dqb_curblocks = htobe64(dqb->dqb_curblocks); 381187914Sdes dqb64.dqb_ihardlimit = htobe64(dqb->dqb_ihardlimit); 382187914Sdes dqb64.dqb_isoftlimit = htobe64(dqb->dqb_isoftlimit); 383187914Sdes dqb64.dqb_curinodes = htobe64(dqb->dqb_curinodes); 384187914Sdes dqb64.dqb_btime = htobe64(dqb->dqb_btime); 385187914Sdes dqb64.dqb_itime = htobe64(dqb->dqb_itime); 386187914Sdes 387187914Sdes off = sizeof(struct dqhdr64) + id * sizeof(struct dqblk64); 388187914Sdes if (lseek(qf->fd, off, SEEK_SET) == -1) 389187914Sdes return (-1); 390188604Smckusick if (write(qf->fd, &dqb64, sizeof(dqb64)) == sizeof(dqb64)) 391188604Smckusick return (0); 392188604Smckusick return (-1); 393187914Sdes} 394187914Sdes 395187914Sdesint 396188604Smckusickquota_write_usage(struct quotafile *qf, struct dqblk *dqb, int id) 397187914Sdes{ 398188604Smckusick struct dqblk dqbuf; 399188604Smckusick int qcmd; 400187914Sdes 401188604Smckusick if (qf->fd == -1) { 402188604Smckusick qcmd = QCMD(Q_SETUSE, qf->quotatype); 403188604Smckusick return (quotactl(qf->fsname, qcmd, id, dqb)); 404188604Smckusick } 405188604Smckusick /* 406188604Smckusick * Have to do read-modify-write of quota in file. 407188604Smckusick */ 408205207Smckusick if ((qf->accmode & O_RDWR) != O_RDWR) { 409205207Smckusick errno = EBADF; 410205207Smckusick return (-1); 411205207Smckusick } 412188604Smckusick if (quota_read(qf, &dqbuf, id) != 0) 413188604Smckusick return (-1); 414188604Smckusick /* 415188604Smckusick * Reset time limit if have a soft limit and were 416188604Smckusick * previously under it, but are now over it. 417188604Smckusick */ 418188604Smckusick if (dqbuf.dqb_bsoftlimit && id != 0 && 419188604Smckusick dqbuf.dqb_curblocks < dqbuf.dqb_bsoftlimit && 420188604Smckusick dqb->dqb_curblocks >= dqbuf.dqb_bsoftlimit) 421188604Smckusick dqbuf.dqb_btime = 0; 422188604Smckusick if (dqbuf.dqb_isoftlimit && id != 0 && 423188604Smckusick dqbuf.dqb_curinodes < dqbuf.dqb_isoftlimit && 424188604Smckusick dqb->dqb_curinodes >= dqbuf.dqb_isoftlimit) 425188604Smckusick dqbuf.dqb_itime = 0; 426188604Smckusick dqbuf.dqb_curinodes = dqb->dqb_curinodes; 427188604Smckusick dqbuf.dqb_curblocks = dqb->dqb_curblocks; 428188604Smckusick /* 429188604Smckusick * Write it back. 430188604Smckusick */ 431188604Smckusick switch (qf->wordsize) { 432187914Sdes case 32: 433197510Sdes return (quota_write32(qf, &dqbuf, id)); 434187914Sdes case 64: 435197510Sdes return (quota_write64(qf, &dqbuf, id)); 436187914Sdes default: 437187914Sdes errno = EINVAL; 438187914Sdes return (-1); 439187914Sdes } 440187914Sdes /* not reached */ 441187914Sdes} 442188568Smckusick 443188568Smckusickint 444188604Smckusickquota_write_limits(struct quotafile *qf, struct dqblk *dqb, int id) 445188568Smckusick{ 446188604Smckusick struct dqblk dqbuf; 447188604Smckusick int qcmd; 448188568Smckusick 449188604Smckusick if (qf->fd == -1) { 450188604Smckusick qcmd = QCMD(Q_SETQUOTA, qf->quotatype); 451188604Smckusick return (quotactl(qf->fsname, qcmd, id, dqb)); 452188568Smckusick } 453188568Smckusick /* 454188604Smckusick * Have to do read-modify-write of quota in file. 455188568Smckusick */ 456205207Smckusick if ((qf->accmode & O_RDWR) != O_RDWR) { 457205207Smckusick errno = EBADF; 458205207Smckusick return (-1); 459205207Smckusick } 460188604Smckusick if (quota_read(qf, &dqbuf, id) != 0) 461188604Smckusick return (-1); 462188604Smckusick /* 463188604Smckusick * Reset time limit if have a soft limit and were 464188604Smckusick * previously under it, but are now over it 465188604Smckusick * or if there previously was no soft limit, but 466188604Smckusick * now have one and are over it. 467188604Smckusick */ 468188604Smckusick if (dqbuf.dqb_bsoftlimit && id != 0 && 469188604Smckusick dqbuf.dqb_curblocks < dqbuf.dqb_bsoftlimit && 470188604Smckusick dqbuf.dqb_curblocks >= dqb->dqb_bsoftlimit) 471188604Smckusick dqb->dqb_btime = 0; 472188604Smckusick if (dqbuf.dqb_bsoftlimit == 0 && id != 0 && 473188604Smckusick dqb->dqb_bsoftlimit > 0 && 474188604Smckusick dqbuf.dqb_curblocks >= dqb->dqb_bsoftlimit) 475188604Smckusick dqb->dqb_btime = 0; 476188604Smckusick if (dqbuf.dqb_isoftlimit && id != 0 && 477188604Smckusick dqbuf.dqb_curinodes < dqbuf.dqb_isoftlimit && 478188604Smckusick dqbuf.dqb_curinodes >= dqb->dqb_isoftlimit) 479188604Smckusick dqb->dqb_itime = 0; 480188604Smckusick if (dqbuf.dqb_isoftlimit == 0 && id !=0 && 481188604Smckusick dqb->dqb_isoftlimit > 0 && 482188604Smckusick dqbuf.dqb_curinodes >= dqb->dqb_isoftlimit) 483188604Smckusick dqb->dqb_itime = 0; 484188604Smckusick dqb->dqb_curinodes = dqbuf.dqb_curinodes; 485188604Smckusick dqb->dqb_curblocks = dqbuf.dqb_curblocks; 486188604Smckusick /* 487188604Smckusick * Write it back. 488188604Smckusick */ 489188604Smckusick switch (qf->wordsize) { 490188604Smckusick case 32: 491197510Sdes return (quota_write32(qf, dqb, id)); 492188604Smckusick case 64: 493197510Sdes return (quota_write64(qf, dqb, id)); 494188604Smckusick default: 495188604Smckusick errno = EINVAL; 496188604Smckusick return (-1); 497188568Smckusick } 498188604Smckusick /* not reached */ 499188568Smckusick} 500201144Smckusick 501201144Smckusick/* 502201144Smckusick * Convert a quota file from one format to another. 503201144Smckusick */ 504201144Smckusickint 505201144Smckusickquota_convert(struct quotafile *qf, int wordsize) 506201144Smckusick{ 507201144Smckusick struct quotafile *newqf; 508201144Smckusick struct dqhdr64 dqh; 509201144Smckusick struct dqblk dqblk; 510201144Smckusick struct group *grp; 511201144Smckusick int serrno, maxid, id, fd; 512201144Smckusick 513201144Smckusick /* 514201144Smckusick * Quotas must not be active and quotafile must be open 515201144Smckusick * for reading and writing. 516201144Smckusick */ 517201144Smckusick if ((qf->accmode & O_RDWR) != O_RDWR || qf->fd == -1) { 518201144Smckusick errno = EBADF; 519201144Smckusick return (-1); 520201144Smckusick } 521201144Smckusick if ((wordsize != 32 && wordsize != 64) || 522201144Smckusick wordsize == qf->wordsize) { 523201144Smckusick errno = EINVAL; 524201144Smckusick return (-1); 525201144Smckusick } 526201144Smckusick maxid = quota_maxid(qf); 527201144Smckusick if ((newqf = calloc(1, sizeof(*qf))) == NULL) { 528201144Smckusick errno = ENOMEM; 529201144Smckusick return (-1); 530201144Smckusick } 531201144Smckusick *newqf = *qf; 532201144Smckusick snprintf(newqf->qfname, MAXPATHLEN + 1, "%s_%d.orig", qf->qfname, 533201144Smckusick qf->wordsize); 534201144Smckusick if (rename(qf->qfname, newqf->qfname) < 0) { 535201144Smckusick free(newqf); 536201144Smckusick return (-1); 537201144Smckusick } 538255007Sjilles if ((newqf->fd = open(qf->qfname, O_RDWR|O_CREAT|O_TRUNC|O_CLOEXEC, 539255007Sjilles 0)) < 0) { 540201144Smckusick serrno = errno; 541201144Smckusick goto error; 542201144Smckusick } 543201144Smckusick newqf->wordsize = wordsize; 544201144Smckusick if (wordsize == 64) { 545201144Smckusick memset(&dqh, 0, sizeof(dqh)); 546201144Smckusick memcpy(dqh.dqh_magic, Q_DQHDR64_MAGIC, sizeof(dqh.dqh_magic)); 547201144Smckusick dqh.dqh_version = htobe32(Q_DQHDR64_VERSION); 548201144Smckusick dqh.dqh_hdrlen = htobe32(sizeof(struct dqhdr64)); 549201144Smckusick dqh.dqh_reclen = htobe32(sizeof(struct dqblk64)); 550201144Smckusick if (write(newqf->fd, &dqh, sizeof(dqh)) != sizeof(dqh)) { 551201144Smckusick serrno = errno; 552201144Smckusick goto error; 553201144Smckusick } 554201144Smckusick } 555201144Smckusick grp = getgrnam(QUOTAGROUP); 556201144Smckusick fchown(newqf->fd, 0, grp ? grp->gr_gid : 0); 557201144Smckusick fchmod(newqf->fd, 0640); 558201144Smckusick for (id = 0; id <= maxid; id++) { 559201144Smckusick if ((quota_read(qf, &dqblk, id)) < 0) 560201144Smckusick break; 561201144Smckusick switch (newqf->wordsize) { 562201144Smckusick case 32: 563201144Smckusick if ((quota_write32(newqf, &dqblk, id)) < 0) 564201144Smckusick break; 565201144Smckusick continue; 566201144Smckusick case 64: 567201144Smckusick if ((quota_write64(newqf, &dqblk, id)) < 0) 568201144Smckusick break; 569201144Smckusick continue; 570201144Smckusick default: 571201144Smckusick errno = EINVAL; 572201144Smckusick break; 573201144Smckusick } 574201144Smckusick } 575201144Smckusick if (id < maxid) { 576201144Smckusick serrno = errno; 577201144Smckusick goto error; 578201144Smckusick } 579201144Smckusick /* 580201144Smckusick * Update the passed in quotafile to reference the new file 581201144Smckusick * of the converted format size. 582201144Smckusick */ 583201144Smckusick fd = qf->fd; 584201144Smckusick qf->fd = newqf->fd; 585201144Smckusick newqf->fd = fd; 586201144Smckusick qf->wordsize = newqf->wordsize; 587201144Smckusick quota_close(newqf); 588201144Smckusick return (0); 589201144Smckusickerror: 590201144Smckusick /* put back the original file */ 591201144Smckusick (void) rename(newqf->qfname, qf->qfname); 592201144Smckusick quota_close(newqf); 593201144Smckusick errno = serrno; 594201144Smckusick return (-1); 595201144Smckusick} 596