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, ",")) { 87188604Smckusick if ((cp = index(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; 140197532Sdes if ((qf->fd = open(qf->qfname, qf->accmode)) < 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 */ 171197532Sdes if ((qf->fd = open(qf->qfname, O_RDWR|O_CREAT|O_TRUNC, 0)) < 0) 172197532Sdes goto error; 173188604Smckusick qf->wordsize = 64; 174187914Sdes memset(&dqh, 0, sizeof(dqh)); 175187914Sdes memcpy(dqh.dqh_magic, Q_DQHDR64_MAGIC, sizeof(dqh.dqh_magic)); 176187914Sdes dqh.dqh_version = htobe32(Q_DQHDR64_VERSION); 177187914Sdes dqh.dqh_hdrlen = htobe32(sizeof(struct dqhdr64)); 178187914Sdes dqh.dqh_reclen = htobe32(sizeof(struct dqblk64)); 179201037Smckusick if (write(qf->fd, &dqh, sizeof(dqh)) != sizeof(dqh)) { 180201037Smckusick /* it was one we created ourselves */ 181201037Smckusick unlink(qf->qfname); 182197532Sdes goto error; 183201037Smckusick } 184187914Sdes grp = getgrnam(QUOTAGROUP); 185187914Sdes fchown(qf->fd, 0, grp ? grp->gr_gid : 0); 186187914Sdes fchmod(qf->fd, 0640); 187187914Sdes return (qf); 188197532Sdeserror: 189197532Sdes serrno = errno; 190197532Sdes /* did we have an open file? */ 191201037Smckusick if (qf->fd != -1) 192197532Sdes close(qf->fd); 193197532Sdes free(qf); 194197532Sdes errno = serrno; 195197532Sdes return (NULL); 196187914Sdes} 197187914Sdes 198187914Sdesvoid 199187914Sdesquota_close(struct quotafile *qf) 200187914Sdes{ 201187914Sdes 202188604Smckusick if (qf->fd != -1) 203188604Smckusick close(qf->fd); 204187914Sdes free(qf); 205187914Sdes} 206187914Sdes 207199328Smckusickint 208199328Smckusickquota_on(struct quotafile *qf) 209199328Smckusick{ 210199328Smckusick int qcmd; 211199328Smckusick 212199328Smckusick qcmd = QCMD(Q_QUOTAON, qf->quotatype); 213199328Smckusick return (quotactl(qf->fsname, qcmd, 0, qf->qfname)); 214199328Smckusick} 215199328Smckusick 216199328Smckusickint 217199328Smckusickquota_off(struct quotafile *qf) 218199328Smckusick{ 219199328Smckusick 220199328Smckusick return (quotactl(qf->fsname, QCMD(Q_QUOTAOFF, qf->quotatype), 0, 0)); 221199328Smckusick} 222199328Smckusick 223197532Sdesconst char * 224197532Sdesquota_fsname(const struct quotafile *qf) 225197532Sdes{ 226197532Sdes 227197532Sdes return (qf->fsname); 228197532Sdes} 229197532Sdes 230197532Sdesconst char * 231197532Sdesquota_qfname(const struct quotafile *qf) 232197532Sdes{ 233197532Sdes 234197532Sdes return (qf->qfname); 235197532Sdes} 236197532Sdes 237197532Sdesint 238197532Sdesquota_check_path(const struct quotafile *qf, const char *path) 239197532Sdes{ 240197532Sdes struct stat st; 241197532Sdes 242197532Sdes if (stat(path, &st) == -1) 243197532Sdes return (-1); 244197532Sdes return (st.st_dev == qf->dev); 245197532Sdes} 246197532Sdes 247198265Smckusickint 248198265Smckusickquota_maxid(struct quotafile *qf) 249198265Smckusick{ 250198265Smckusick struct stat st; 251205207Smckusick int maxid; 252198265Smckusick 253198265Smckusick if (stat(qf->qfname, &st) < 0) 254198265Smckusick return (0); 255198265Smckusick switch (qf->wordsize) { 256198265Smckusick case 32: 257205207Smckusick maxid = st.st_size / sizeof(struct dqblk32) - 1; 258205207Smckusick break; 259198265Smckusick case 64: 260205207Smckusick maxid = st.st_size / sizeof(struct dqblk64) - 2; 261205207Smckusick break; 262198265Smckusick default: 263205207Smckusick maxid = 0; 264205207Smckusick break; 265198265Smckusick } 266205207Smckusick return (maxid > 0 ? maxid : 0); 267198265Smckusick} 268198265Smckusick 269187914Sdesstatic int 270187914Sdesquota_read32(struct quotafile *qf, struct dqblk *dqb, int id) 271187914Sdes{ 272187914Sdes struct dqblk32 dqb32; 273187914Sdes off_t off; 274187914Sdes 275187914Sdes off = id * sizeof(struct dqblk32); 276187914Sdes if (lseek(qf->fd, off, SEEK_SET) == -1) 277187914Sdes return (-1); 278187914Sdes switch (read(qf->fd, &dqb32, sizeof(dqb32))) { 279187914Sdes case 0: 280198265Smckusick memset(dqb, 0, sizeof(*dqb)); 281187914Sdes return (0); 282187914Sdes case sizeof(dqb32): 283187914Sdes dqb->dqb_bhardlimit = dqb32.dqb_bhardlimit; 284187914Sdes dqb->dqb_bsoftlimit = dqb32.dqb_bsoftlimit; 285187914Sdes dqb->dqb_curblocks = dqb32.dqb_curblocks; 286187914Sdes dqb->dqb_ihardlimit = dqb32.dqb_ihardlimit; 287187914Sdes dqb->dqb_isoftlimit = dqb32.dqb_isoftlimit; 288187914Sdes dqb->dqb_curinodes = dqb32.dqb_curinodes; 289187914Sdes dqb->dqb_btime = dqb32.dqb_btime; 290187914Sdes dqb->dqb_itime = dqb32.dqb_itime; 291187914Sdes return (0); 292187914Sdes default: 293187914Sdes return (-1); 294187914Sdes } 295187914Sdes} 296187914Sdes 297187914Sdesstatic int 298187914Sdesquota_read64(struct quotafile *qf, struct dqblk *dqb, int id) 299187914Sdes{ 300187914Sdes struct dqblk64 dqb64; 301187914Sdes off_t off; 302187914Sdes 303187914Sdes off = sizeof(struct dqhdr64) + id * sizeof(struct dqblk64); 304187914Sdes if (lseek(qf->fd, off, SEEK_SET) == -1) 305187914Sdes return (-1); 306187914Sdes switch (read(qf->fd, &dqb64, sizeof(dqb64))) { 307187914Sdes case 0: 308198265Smckusick memset(dqb, 0, sizeof(*dqb)); 309187914Sdes return (0); 310187914Sdes case sizeof(dqb64): 311187914Sdes dqb->dqb_bhardlimit = be64toh(dqb64.dqb_bhardlimit); 312187914Sdes dqb->dqb_bsoftlimit = be64toh(dqb64.dqb_bsoftlimit); 313187914Sdes dqb->dqb_curblocks = be64toh(dqb64.dqb_curblocks); 314187914Sdes dqb->dqb_ihardlimit = be64toh(dqb64.dqb_ihardlimit); 315187914Sdes dqb->dqb_isoftlimit = be64toh(dqb64.dqb_isoftlimit); 316187914Sdes dqb->dqb_curinodes = be64toh(dqb64.dqb_curinodes); 317187914Sdes dqb->dqb_btime = be64toh(dqb64.dqb_btime); 318187914Sdes dqb->dqb_itime = be64toh(dqb64.dqb_itime); 319187914Sdes return (0); 320187914Sdes default: 321187914Sdes return (-1); 322187914Sdes } 323187914Sdes} 324187914Sdes 325187914Sdesint 326187914Sdesquota_read(struct quotafile *qf, struct dqblk *dqb, int id) 327187914Sdes{ 328188604Smckusick int qcmd; 329187914Sdes 330188604Smckusick if (qf->fd == -1) { 331188604Smckusick qcmd = QCMD(Q_GETQUOTA, qf->quotatype); 332188604Smckusick return (quotactl(qf->fsname, qcmd, id, dqb)); 333188604Smckusick } 334188604Smckusick switch (qf->wordsize) { 335187914Sdes case 32: 336197510Sdes return (quota_read32(qf, dqb, id)); 337187914Sdes case 64: 338197510Sdes return (quota_read64(qf, dqb, id)); 339187914Sdes default: 340187914Sdes errno = EINVAL; 341187914Sdes return (-1); 342187914Sdes } 343187914Sdes /* not reached */ 344187914Sdes} 345187914Sdes 346187914Sdes#define CLIP32(u64) ((u64) > UINT32_MAX ? UINT32_MAX : (uint32_t)(u64)) 347187914Sdes 348187914Sdesstatic int 349187914Sdesquota_write32(struct quotafile *qf, const struct dqblk *dqb, int id) 350187914Sdes{ 351187914Sdes struct dqblk32 dqb32; 352187914Sdes off_t off; 353187914Sdes 354187914Sdes dqb32.dqb_bhardlimit = CLIP32(dqb->dqb_bhardlimit); 355187914Sdes dqb32.dqb_bsoftlimit = CLIP32(dqb->dqb_bsoftlimit); 356187914Sdes dqb32.dqb_curblocks = CLIP32(dqb->dqb_curblocks); 357187914Sdes dqb32.dqb_ihardlimit = CLIP32(dqb->dqb_ihardlimit); 358187914Sdes dqb32.dqb_isoftlimit = CLIP32(dqb->dqb_isoftlimit); 359187914Sdes dqb32.dqb_curinodes = CLIP32(dqb->dqb_curinodes); 360187914Sdes dqb32.dqb_btime = CLIP32(dqb->dqb_btime); 361187914Sdes dqb32.dqb_itime = CLIP32(dqb->dqb_itime); 362187914Sdes 363187914Sdes off = id * sizeof(struct dqblk32); 364187914Sdes if (lseek(qf->fd, off, SEEK_SET) == -1) 365187914Sdes return (-1); 366188604Smckusick if (write(qf->fd, &dqb32, sizeof(dqb32)) == sizeof(dqb32)) 367188604Smckusick return (0); 368188604Smckusick return (-1); 369187914Sdes} 370187914Sdes 371187914Sdesstatic int 372187914Sdesquota_write64(struct quotafile *qf, const struct dqblk *dqb, int id) 373187914Sdes{ 374187914Sdes struct dqblk64 dqb64; 375187914Sdes off_t off; 376187914Sdes 377187914Sdes dqb64.dqb_bhardlimit = htobe64(dqb->dqb_bhardlimit); 378187914Sdes dqb64.dqb_bsoftlimit = htobe64(dqb->dqb_bsoftlimit); 379187914Sdes dqb64.dqb_curblocks = htobe64(dqb->dqb_curblocks); 380187914Sdes dqb64.dqb_ihardlimit = htobe64(dqb->dqb_ihardlimit); 381187914Sdes dqb64.dqb_isoftlimit = htobe64(dqb->dqb_isoftlimit); 382187914Sdes dqb64.dqb_curinodes = htobe64(dqb->dqb_curinodes); 383187914Sdes dqb64.dqb_btime = htobe64(dqb->dqb_btime); 384187914Sdes dqb64.dqb_itime = htobe64(dqb->dqb_itime); 385187914Sdes 386187914Sdes off = sizeof(struct dqhdr64) + id * sizeof(struct dqblk64); 387187914Sdes if (lseek(qf->fd, off, SEEK_SET) == -1) 388187914Sdes return (-1); 389188604Smckusick if (write(qf->fd, &dqb64, sizeof(dqb64)) == sizeof(dqb64)) 390188604Smckusick return (0); 391188604Smckusick return (-1); 392187914Sdes} 393187914Sdes 394187914Sdesint 395188604Smckusickquota_write_usage(struct quotafile *qf, struct dqblk *dqb, int id) 396187914Sdes{ 397188604Smckusick struct dqblk dqbuf; 398188604Smckusick int qcmd; 399187914Sdes 400188604Smckusick if (qf->fd == -1) { 401188604Smckusick qcmd = QCMD(Q_SETUSE, qf->quotatype); 402188604Smckusick return (quotactl(qf->fsname, qcmd, id, dqb)); 403188604Smckusick } 404188604Smckusick /* 405188604Smckusick * Have to do read-modify-write of quota in file. 406188604Smckusick */ 407205207Smckusick if ((qf->accmode & O_RDWR) != O_RDWR) { 408205207Smckusick errno = EBADF; 409205207Smckusick return (-1); 410205207Smckusick } 411188604Smckusick if (quota_read(qf, &dqbuf, id) != 0) 412188604Smckusick return (-1); 413188604Smckusick /* 414188604Smckusick * Reset time limit if have a soft limit and were 415188604Smckusick * previously under it, but are now over it. 416188604Smckusick */ 417188604Smckusick if (dqbuf.dqb_bsoftlimit && id != 0 && 418188604Smckusick dqbuf.dqb_curblocks < dqbuf.dqb_bsoftlimit && 419188604Smckusick dqb->dqb_curblocks >= dqbuf.dqb_bsoftlimit) 420188604Smckusick dqbuf.dqb_btime = 0; 421188604Smckusick if (dqbuf.dqb_isoftlimit && id != 0 && 422188604Smckusick dqbuf.dqb_curinodes < dqbuf.dqb_isoftlimit && 423188604Smckusick dqb->dqb_curinodes >= dqbuf.dqb_isoftlimit) 424188604Smckusick dqbuf.dqb_itime = 0; 425188604Smckusick dqbuf.dqb_curinodes = dqb->dqb_curinodes; 426188604Smckusick dqbuf.dqb_curblocks = dqb->dqb_curblocks; 427188604Smckusick /* 428188604Smckusick * Write it back. 429188604Smckusick */ 430188604Smckusick switch (qf->wordsize) { 431187914Sdes case 32: 432197510Sdes return (quota_write32(qf, &dqbuf, id)); 433187914Sdes case 64: 434197510Sdes return (quota_write64(qf, &dqbuf, id)); 435187914Sdes default: 436187914Sdes errno = EINVAL; 437187914Sdes return (-1); 438187914Sdes } 439187914Sdes /* not reached */ 440187914Sdes} 441188568Smckusick 442188568Smckusickint 443188604Smckusickquota_write_limits(struct quotafile *qf, struct dqblk *dqb, int id) 444188568Smckusick{ 445188604Smckusick struct dqblk dqbuf; 446188604Smckusick int qcmd; 447188568Smckusick 448188604Smckusick if (qf->fd == -1) { 449188604Smckusick qcmd = QCMD(Q_SETQUOTA, qf->quotatype); 450188604Smckusick return (quotactl(qf->fsname, qcmd, id, dqb)); 451188568Smckusick } 452188568Smckusick /* 453188604Smckusick * Have to do read-modify-write of quota in file. 454188568Smckusick */ 455205207Smckusick if ((qf->accmode & O_RDWR) != O_RDWR) { 456205207Smckusick errno = EBADF; 457205207Smckusick return (-1); 458205207Smckusick } 459188604Smckusick if (quota_read(qf, &dqbuf, id) != 0) 460188604Smckusick return (-1); 461188604Smckusick /* 462188604Smckusick * Reset time limit if have a soft limit and were 463188604Smckusick * previously under it, but are now over it 464188604Smckusick * or if there previously was no soft limit, but 465188604Smckusick * now have one and are over it. 466188604Smckusick */ 467188604Smckusick if (dqbuf.dqb_bsoftlimit && id != 0 && 468188604Smckusick dqbuf.dqb_curblocks < dqbuf.dqb_bsoftlimit && 469188604Smckusick dqbuf.dqb_curblocks >= dqb->dqb_bsoftlimit) 470188604Smckusick dqb->dqb_btime = 0; 471188604Smckusick if (dqbuf.dqb_bsoftlimit == 0 && id != 0 && 472188604Smckusick dqb->dqb_bsoftlimit > 0 && 473188604Smckusick dqbuf.dqb_curblocks >= dqb->dqb_bsoftlimit) 474188604Smckusick dqb->dqb_btime = 0; 475188604Smckusick if (dqbuf.dqb_isoftlimit && id != 0 && 476188604Smckusick dqbuf.dqb_curinodes < dqbuf.dqb_isoftlimit && 477188604Smckusick dqbuf.dqb_curinodes >= dqb->dqb_isoftlimit) 478188604Smckusick dqb->dqb_itime = 0; 479188604Smckusick if (dqbuf.dqb_isoftlimit == 0 && id !=0 && 480188604Smckusick dqb->dqb_isoftlimit > 0 && 481188604Smckusick dqbuf.dqb_curinodes >= dqb->dqb_isoftlimit) 482188604Smckusick dqb->dqb_itime = 0; 483188604Smckusick dqb->dqb_curinodes = dqbuf.dqb_curinodes; 484188604Smckusick dqb->dqb_curblocks = dqbuf.dqb_curblocks; 485188604Smckusick /* 486188604Smckusick * Write it back. 487188604Smckusick */ 488188604Smckusick switch (qf->wordsize) { 489188604Smckusick case 32: 490197510Sdes return (quota_write32(qf, dqb, id)); 491188604Smckusick case 64: 492197510Sdes return (quota_write64(qf, dqb, id)); 493188604Smckusick default: 494188604Smckusick errno = EINVAL; 495188604Smckusick return (-1); 496188568Smckusick } 497188604Smckusick /* not reached */ 498188568Smckusick} 499201144Smckusick 500201144Smckusick/* 501201144Smckusick * Convert a quota file from one format to another. 502201144Smckusick */ 503201144Smckusickint 504201144Smckusickquota_convert(struct quotafile *qf, int wordsize) 505201144Smckusick{ 506201144Smckusick struct quotafile *newqf; 507201144Smckusick struct dqhdr64 dqh; 508201144Smckusick struct dqblk dqblk; 509201144Smckusick struct group *grp; 510201144Smckusick int serrno, maxid, id, fd; 511201144Smckusick 512201144Smckusick /* 513201144Smckusick * Quotas must not be active and quotafile must be open 514201144Smckusick * for reading and writing. 515201144Smckusick */ 516201144Smckusick if ((qf->accmode & O_RDWR) != O_RDWR || qf->fd == -1) { 517201144Smckusick errno = EBADF; 518201144Smckusick return (-1); 519201144Smckusick } 520201144Smckusick if ((wordsize != 32 && wordsize != 64) || 521201144Smckusick wordsize == qf->wordsize) { 522201144Smckusick errno = EINVAL; 523201144Smckusick return (-1); 524201144Smckusick } 525201144Smckusick maxid = quota_maxid(qf); 526201144Smckusick if ((newqf = calloc(1, sizeof(*qf))) == NULL) { 527201144Smckusick errno = ENOMEM; 528201144Smckusick return (-1); 529201144Smckusick } 530201144Smckusick *newqf = *qf; 531201144Smckusick snprintf(newqf->qfname, MAXPATHLEN + 1, "%s_%d.orig", qf->qfname, 532201144Smckusick qf->wordsize); 533201144Smckusick if (rename(qf->qfname, newqf->qfname) < 0) { 534201144Smckusick free(newqf); 535201144Smckusick return (-1); 536201144Smckusick } 537201144Smckusick if ((newqf->fd = open(qf->qfname, O_RDWR|O_CREAT|O_TRUNC, 0)) < 0) { 538201144Smckusick serrno = errno; 539201144Smckusick goto error; 540201144Smckusick } 541201144Smckusick newqf->wordsize = wordsize; 542201144Smckusick if (wordsize == 64) { 543201144Smckusick memset(&dqh, 0, sizeof(dqh)); 544201144Smckusick memcpy(dqh.dqh_magic, Q_DQHDR64_MAGIC, sizeof(dqh.dqh_magic)); 545201144Smckusick dqh.dqh_version = htobe32(Q_DQHDR64_VERSION); 546201144Smckusick dqh.dqh_hdrlen = htobe32(sizeof(struct dqhdr64)); 547201144Smckusick dqh.dqh_reclen = htobe32(sizeof(struct dqblk64)); 548201144Smckusick if (write(newqf->fd, &dqh, sizeof(dqh)) != sizeof(dqh)) { 549201144Smckusick serrno = errno; 550201144Smckusick goto error; 551201144Smckusick } 552201144Smckusick } 553201144Smckusick grp = getgrnam(QUOTAGROUP); 554201144Smckusick fchown(newqf->fd, 0, grp ? grp->gr_gid : 0); 555201144Smckusick fchmod(newqf->fd, 0640); 556201144Smckusick for (id = 0; id <= maxid; id++) { 557201144Smckusick if ((quota_read(qf, &dqblk, id)) < 0) 558201144Smckusick break; 559201144Smckusick switch (newqf->wordsize) { 560201144Smckusick case 32: 561201144Smckusick if ((quota_write32(newqf, &dqblk, id)) < 0) 562201144Smckusick break; 563201144Smckusick continue; 564201144Smckusick case 64: 565201144Smckusick if ((quota_write64(newqf, &dqblk, id)) < 0) 566201144Smckusick break; 567201144Smckusick continue; 568201144Smckusick default: 569201144Smckusick errno = EINVAL; 570201144Smckusick break; 571201144Smckusick } 572201144Smckusick } 573201144Smckusick if (id < maxid) { 574201144Smckusick serrno = errno; 575201144Smckusick goto error; 576201144Smckusick } 577201144Smckusick /* 578201144Smckusick * Update the passed in quotafile to reference the new file 579201144Smckusick * of the converted format size. 580201144Smckusick */ 581201144Smckusick fd = qf->fd; 582201144Smckusick qf->fd = newqf->fd; 583201144Smckusick newqf->fd = fd; 584201144Smckusick qf->wordsize = newqf->wordsize; 585201144Smckusick quota_close(newqf); 586201144Smckusick return (0); 587201144Smckusickerror: 588201144Smckusick /* put back the original file */ 589201144Smckusick (void) rename(newqf->qfname, qf->qfname); 590201144Smckusick quota_close(newqf); 591201144Smckusick errno = serrno; 592201144Smckusick return (-1); 593201144Smckusick} 594