1 /*- 2 * Copyright (c) 2008 Dag-Erling Coïdan Smørgrav 3 * Copyright (c) 2008 Marshall Kirk McKusick 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer 11 * in this position and unchanged. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 * 28 * $FreeBSD$ 29 */ 30 31 #include <sys/types.h> 32 #include <sys/endian.h> 33 #include <sys/mount.h> 34 #include <sys/stat.h> 35 36 #include <ufs/ufs/quota.h> 37 38 #include <errno.h> 39 #include <fcntl.h> 40 #include <fstab.h> 41 #include <grp.h> 42 #include <pwd.h> 43 #include <libutil.h> 44 #include <stdint.h> 45 #include <stdio.h> 46 #include <stdlib.h> 47 #include <string.h> 48 #include <unistd.h> 49 50 struct quotafile { 51 int fd; /* -1 means using quotactl for access */ 52 int accmode; /* access mode */ 53 int wordsize; /* 32-bit or 64-bit limits */ 54 int quotatype; /* USRQUOTA or GRPQUOTA */ 55 dev_t dev; /* device */ 56 char fsname[MAXPATHLEN + 1]; /* mount point of filesystem */ 57 char qfname[MAXPATHLEN + 1]; /* quota file if not using quotactl */ 58 }; 59 60 static const char *qfextension[] = INITQFNAMES; 61 62 /* 63 * Check to see if a particular quota is to be enabled. 64 */ 65 static int 66 hasquota(struct fstab *fs, int type, char *qfnamep, int qfbufsize) 67 { 68 char *opt; 69 char *cp; 70 struct statfs sfb; 71 char buf[BUFSIZ]; 72 static char initname, usrname[100], grpname[100]; 73 74 /* 75 * 1) we only need one of these 76 * 2) fstab may specify a different filename 77 */ 78 if (!initname) { 79 (void)snprintf(usrname, sizeof(usrname), "%s%s", 80 qfextension[USRQUOTA], QUOTAFILENAME); 81 (void)snprintf(grpname, sizeof(grpname), "%s%s", 82 qfextension[GRPQUOTA], QUOTAFILENAME); 83 initname = 1; 84 } 85 strcpy(buf, fs->fs_mntops); 86 for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) { 87 if ((cp = index(opt, '='))) 88 *cp++ = '\0'; 89 if (type == USRQUOTA && strcmp(opt, usrname) == 0) 90 break; 91 if (type == GRPQUOTA && strcmp(opt, grpname) == 0) 92 break; 93 } 94 if (!opt) 95 return (0); 96 /* 97 * Ensure that the filesystem is mounted. 98 */ 99 if (statfs(fs->fs_file, &sfb) != 0 || 100 strcmp(fs->fs_file, sfb.f_mntonname)) { 101 return (0); 102 } 103 if (cp) { 104 strncpy(qfnamep, cp, qfbufsize); 105 } else { 106 (void)snprintf(qfnamep, qfbufsize, "%s/%s.%s", fs->fs_file, 107 QUOTAFILENAME, qfextension[type]); 108 } 109 return (1); 110 } 111 112 struct quotafile * 113 quota_open(struct fstab *fs, int quotatype, int openflags) 114 { 115 struct quotafile *qf; 116 struct dqhdr64 dqh; 117 struct group *grp; 118 struct stat st; 119 int qcmd, serrno; 120 121 if (strcmp(fs->fs_vfstype, "ufs")) 122 return (NULL); 123 if ((qf = calloc(1, sizeof(*qf))) == NULL) 124 return (NULL); 125 qf->fd = -1; 126 qf->quotatype = quotatype; 127 strncpy(qf->fsname, fs->fs_file, sizeof(qf->fsname)); 128 if (stat(qf->fsname, &st) != 0) 129 goto error; 130 qf->dev = st.st_dev; 131 serrno = hasquota(fs, quotatype, qf->qfname, sizeof(qf->qfname)); 132 qcmd = QCMD(Q_GETQUOTASIZE, quotatype); 133 if (quotactl(qf->fsname, qcmd, 0, &qf->wordsize) == 0) 134 return (qf); 135 if (serrno == 0) { 136 errno = EOPNOTSUPP; 137 goto error; 138 } 139 qf->accmode = openflags & O_ACCMODE; 140 if ((qf->fd = open(qf->qfname, qf->accmode)) < 0 && 141 (openflags & O_CREAT) != O_CREAT) 142 goto error; 143 /* File open worked, so process it */ 144 if (qf->fd != -1) { 145 qf->wordsize = 32; 146 switch (read(qf->fd, &dqh, sizeof(dqh))) { 147 case -1: 148 goto error; 149 case sizeof(dqh): 150 if (strcmp(dqh.dqh_magic, Q_DQHDR64_MAGIC) != 0) { 151 /* no magic, assume 32 bits */ 152 qf->wordsize = 32; 153 return (qf); 154 } 155 if (be32toh(dqh.dqh_version) != Q_DQHDR64_VERSION || 156 be32toh(dqh.dqh_hdrlen) != sizeof(struct dqhdr64) || 157 be32toh(dqh.dqh_reclen) != sizeof(struct dqblk64)) { 158 /* correct magic, wrong version / lengths */ 159 errno = EINVAL; 160 goto error; 161 } 162 qf->wordsize = 64; 163 return (qf); 164 default: 165 qf->wordsize = 32; 166 return (qf); 167 } 168 /* not reached */ 169 } 170 /* open failed, but O_CREAT was specified, so create a new file */ 171 if ((qf->fd = open(qf->qfname, O_RDWR|O_CREAT|O_TRUNC, 0)) < 0) 172 goto error; 173 qf->wordsize = 64; 174 memset(&dqh, 0, sizeof(dqh)); 175 memcpy(dqh.dqh_magic, Q_DQHDR64_MAGIC, sizeof(dqh.dqh_magic)); 176 dqh.dqh_version = htobe32(Q_DQHDR64_VERSION); 177 dqh.dqh_hdrlen = htobe32(sizeof(struct dqhdr64)); 178 dqh.dqh_reclen = htobe32(sizeof(struct dqblk64)); 179 if (write(qf->fd, &dqh, sizeof(dqh)) != sizeof(dqh)) { 180 /* it was one we created ourselves */ 181 unlink(qf->qfname); 182 goto error; 183 } 184 grp = getgrnam(QUOTAGROUP); 185 fchown(qf->fd, 0, grp ? grp->gr_gid : 0); 186 fchmod(qf->fd, 0640); 187 return (qf); 188 error: 189 serrno = errno; 190 /* did we have an open file? */ 191 if (qf->fd != -1) 192 close(qf->fd); 193 free(qf); 194 errno = serrno; 195 return (NULL); 196 } 197 198 void 199 quota_close(struct quotafile *qf) 200 { 201 202 if (qf->fd != -1) 203 close(qf->fd); 204 free(qf); 205 } 206 207 int 208 quota_on(struct quotafile *qf) 209 { 210 int qcmd; 211 212 qcmd = QCMD(Q_QUOTAON, qf->quotatype); 213 return (quotactl(qf->fsname, qcmd, 0, qf->qfname)); 214 } 215 216 int 217 quota_off(struct quotafile *qf) 218 { 219 220 return (quotactl(qf->fsname, QCMD(Q_QUOTAOFF, qf->quotatype), 0, 0)); 221 } 222 223 const char * 224 quota_fsname(const struct quotafile *qf) 225 { 226 227 return (qf->fsname); 228 } 229 230 const char * 231 quota_qfname(const struct quotafile *qf) 232 { 233 234 return (qf->qfname); 235 } 236 237 int 238 quota_check_path(const struct quotafile *qf, const char *path) 239 { 240 struct stat st; 241 242 if (stat(path, &st) == -1) 243 return (-1); 244 return (st.st_dev == qf->dev); 245 } 246 247 int 248 quota_maxid(struct quotafile *qf) 249 { 250 struct stat st; 251 int maxid; 252 253 if (stat(qf->qfname, &st) < 0) 254 return (0); 255 switch (qf->wordsize) { 256 case 32: 257 maxid = st.st_size / sizeof(struct dqblk32) - 1; 258 break; 259 case 64: 260 maxid = st.st_size / sizeof(struct dqblk64) - 2; 261 break; 262 default: 263 maxid = 0; 264 break; 265 } 266 return (maxid > 0 ? maxid : 0); 267 } 268 269 static int 270 quota_read32(struct quotafile *qf, struct dqblk *dqb, int id) 271 { 272 struct dqblk32 dqb32; 273 off_t off; 274 275 off = id * sizeof(struct dqblk32); 276 if (lseek(qf->fd, off, SEEK_SET) == -1) 277 return (-1); 278 switch (read(qf->fd, &dqb32, sizeof(dqb32))) { 279 case 0: 280 memset(dqb, 0, sizeof(*dqb)); 281 return (0); 282 case sizeof(dqb32): 283 dqb->dqb_bhardlimit = dqb32.dqb_bhardlimit; 284 dqb->dqb_bsoftlimit = dqb32.dqb_bsoftlimit; 285 dqb->dqb_curblocks = dqb32.dqb_curblocks; 286 dqb->dqb_ihardlimit = dqb32.dqb_ihardlimit; 287 dqb->dqb_isoftlimit = dqb32.dqb_isoftlimit; 288 dqb->dqb_curinodes = dqb32.dqb_curinodes; 289 dqb->dqb_btime = dqb32.dqb_btime; 290 dqb->dqb_itime = dqb32.dqb_itime; 291 return (0); 292 default: 293 return (-1); 294 } 295 } 296 297 static int 298 quota_read64(struct quotafile *qf, struct dqblk *dqb, int id) 299 { 300 struct dqblk64 dqb64; 301 off_t off; 302 303 off = sizeof(struct dqhdr64) + id * sizeof(struct dqblk64); 304 if (lseek(qf->fd, off, SEEK_SET) == -1) 305 return (-1); 306 switch (read(qf->fd, &dqb64, sizeof(dqb64))) { 307 case 0: 308 memset(dqb, 0, sizeof(*dqb)); 309 return (0); 310 case sizeof(dqb64): 311 dqb->dqb_bhardlimit = be64toh(dqb64.dqb_bhardlimit); 312 dqb->dqb_bsoftlimit = be64toh(dqb64.dqb_bsoftlimit); 313 dqb->dqb_curblocks = be64toh(dqb64.dqb_curblocks); 314 dqb->dqb_ihardlimit = be64toh(dqb64.dqb_ihardlimit); 315 dqb->dqb_isoftlimit = be64toh(dqb64.dqb_isoftlimit); 316 dqb->dqb_curinodes = be64toh(dqb64.dqb_curinodes); 317 dqb->dqb_btime = be64toh(dqb64.dqb_btime); 318 dqb->dqb_itime = be64toh(dqb64.dqb_itime); 319 return (0); 320 default: 321 return (-1); 322 } 323 } 324 325 int 326 quota_read(struct quotafile *qf, struct dqblk *dqb, int id) 327 { 328 int qcmd; 329 330 if (qf->fd == -1) { 331 qcmd = QCMD(Q_GETQUOTA, qf->quotatype); 332 return (quotactl(qf->fsname, qcmd, id, dqb)); 333 } 334 switch (qf->wordsize) { 335 case 32: 336 return (quota_read32(qf, dqb, id)); 337 case 64: 338 return (quota_read64(qf, dqb, id)); 339 default: 340 errno = EINVAL; 341 return (-1); 342 } 343 /* not reached */ 344 } 345 346 #define CLIP32(u64) ((u64) > UINT32_MAX ? UINT32_MAX : (uint32_t)(u64)) 347 348 static int 349 quota_write32(struct quotafile *qf, const struct dqblk *dqb, int id) 350 { 351 struct dqblk32 dqb32; 352 off_t off; 353 354 dqb32.dqb_bhardlimit = CLIP32(dqb->dqb_bhardlimit); 355 dqb32.dqb_bsoftlimit = CLIP32(dqb->dqb_bsoftlimit); 356 dqb32.dqb_curblocks = CLIP32(dqb->dqb_curblocks); 357 dqb32.dqb_ihardlimit = CLIP32(dqb->dqb_ihardlimit); 358 dqb32.dqb_isoftlimit = CLIP32(dqb->dqb_isoftlimit); 359 dqb32.dqb_curinodes = CLIP32(dqb->dqb_curinodes); 360 dqb32.dqb_btime = CLIP32(dqb->dqb_btime); 361 dqb32.dqb_itime = CLIP32(dqb->dqb_itime); 362 363 off = id * sizeof(struct dqblk32); 364 if (lseek(qf->fd, off, SEEK_SET) == -1) 365 return (-1); 366 if (write(qf->fd, &dqb32, sizeof(dqb32)) == sizeof(dqb32)) 367 return (0); 368 return (-1); 369 } 370 371 static int 372 quota_write64(struct quotafile *qf, const struct dqblk *dqb, int id) 373 { 374 struct dqblk64 dqb64; 375 off_t off; 376 377 dqb64.dqb_bhardlimit = htobe64(dqb->dqb_bhardlimit); 378 dqb64.dqb_bsoftlimit = htobe64(dqb->dqb_bsoftlimit); 379 dqb64.dqb_curblocks = htobe64(dqb->dqb_curblocks); 380 dqb64.dqb_ihardlimit = htobe64(dqb->dqb_ihardlimit); 381 dqb64.dqb_isoftlimit = htobe64(dqb->dqb_isoftlimit); 382 dqb64.dqb_curinodes = htobe64(dqb->dqb_curinodes); 383 dqb64.dqb_btime = htobe64(dqb->dqb_btime); 384 dqb64.dqb_itime = htobe64(dqb->dqb_itime); 385 386 off = sizeof(struct dqhdr64) + id * sizeof(struct dqblk64); 387 if (lseek(qf->fd, off, SEEK_SET) == -1) 388 return (-1); 389 if (write(qf->fd, &dqb64, sizeof(dqb64)) == sizeof(dqb64)) 390 return (0); 391 return (-1); 392 } 393 394 int 395 quota_write_usage(struct quotafile *qf, struct dqblk *dqb, int id) 396 { 397 struct dqblk dqbuf; 398 int qcmd; 399 400 if (qf->fd == -1) { 401 qcmd = QCMD(Q_SETUSE, qf->quotatype); 402 return (quotactl(qf->fsname, qcmd, id, dqb)); 403 } 404 /* 405 * Have to do read-modify-write of quota in file. 406 */ 407 if ((qf->accmode & O_RDWR) != O_RDWR) { 408 errno = EBADF; 409 return (-1); 410 } 411 if (quota_read(qf, &dqbuf, id) != 0) 412 return (-1); 413 /* 414 * Reset time limit if have a soft limit and were 415 * previously under it, but are now over it. 416 */ 417 if (dqbuf.dqb_bsoftlimit && id != 0 && 418 dqbuf.dqb_curblocks < dqbuf.dqb_bsoftlimit && 419 dqb->dqb_curblocks >= dqbuf.dqb_bsoftlimit) 420 dqbuf.dqb_btime = 0; 421 if (dqbuf.dqb_isoftlimit && id != 0 && 422 dqbuf.dqb_curinodes < dqbuf.dqb_isoftlimit && 423 dqb->dqb_curinodes >= dqbuf.dqb_isoftlimit) 424 dqbuf.dqb_itime = 0; 425 dqbuf.dqb_curinodes = dqb->dqb_curinodes; 426 dqbuf.dqb_curblocks = dqb->dqb_curblocks; 427 /* 428 * Write it back. 429 */ 430 switch (qf->wordsize) { 431 case 32: 432 return (quota_write32(qf, &dqbuf, id)); 433 case 64: 434 return (quota_write64(qf, &dqbuf, id)); 435 default: 436 errno = EINVAL; 437 return (-1); 438 } 439 /* not reached */ 440 } 441 442 int 443 quota_write_limits(struct quotafile *qf, struct dqblk *dqb, int id) 444 { 445 struct dqblk dqbuf; 446 int qcmd; 447 448 if (qf->fd == -1) { 449 qcmd = QCMD(Q_SETQUOTA, qf->quotatype); 450 return (quotactl(qf->fsname, qcmd, id, dqb)); 451 } 452 /* 453 * Have to do read-modify-write of quota in file. 454 */ 455 if ((qf->accmode & O_RDWR) != O_RDWR) { 456 errno = EBADF; 457 return (-1); 458 } 459 if (quota_read(qf, &dqbuf, id) != 0) 460 return (-1); 461 /* 462 * Reset time limit if have a soft limit and were 463 * previously under it, but are now over it 464 * or if there previously was no soft limit, but 465 * now have one and are over it. 466 */ 467 if (dqbuf.dqb_bsoftlimit && id != 0 && 468 dqbuf.dqb_curblocks < dqbuf.dqb_bsoftlimit && 469 dqbuf.dqb_curblocks >= dqb->dqb_bsoftlimit) 470 dqb->dqb_btime = 0; 471 if (dqbuf.dqb_bsoftlimit == 0 && id != 0 && 472 dqb->dqb_bsoftlimit > 0 && 473 dqbuf.dqb_curblocks >= dqb->dqb_bsoftlimit) 474 dqb->dqb_btime = 0; 475 if (dqbuf.dqb_isoftlimit && id != 0 && 476 dqbuf.dqb_curinodes < dqbuf.dqb_isoftlimit && 477 dqbuf.dqb_curinodes >= dqb->dqb_isoftlimit) 478 dqb->dqb_itime = 0; 479 if (dqbuf.dqb_isoftlimit == 0 && id !=0 && 480 dqb->dqb_isoftlimit > 0 && 481 dqbuf.dqb_curinodes >= dqb->dqb_isoftlimit) 482 dqb->dqb_itime = 0; 483 dqb->dqb_curinodes = dqbuf.dqb_curinodes; 484 dqb->dqb_curblocks = dqbuf.dqb_curblocks; 485 /* 486 * Write it back. 487 */ 488 switch (qf->wordsize) { 489 case 32: 490 return (quota_write32(qf, dqb, id)); 491 case 64: 492 return (quota_write64(qf, dqb, id)); 493 default: 494 errno = EINVAL; 495 return (-1); 496 } 497 /* not reached */ 498 } 499 500 /* 501 * Convert a quota file from one format to another. 502 */ 503 int 504 quota_convert(struct quotafile *qf, int wordsize) 505 { 506 struct quotafile *newqf; 507 struct dqhdr64 dqh; 508 struct dqblk dqblk; 509 struct group *grp; 510 int serrno, maxid, id, fd; 511 512 /* 513 * Quotas must not be active and quotafile must be open 514 * for reading and writing. 515 */ 516 if ((qf->accmode & O_RDWR) != O_RDWR || qf->fd == -1) { 517 errno = EBADF; 518 return (-1); 519 } 520 if ((wordsize != 32 && wordsize != 64) || 521 wordsize == qf->wordsize) { 522 errno = EINVAL; 523 return (-1); 524 } 525 maxid = quota_maxid(qf); 526 if ((newqf = calloc(1, sizeof(*qf))) == NULL) { 527 errno = ENOMEM; 528 return (-1); 529 } 530 *newqf = *qf; 531 snprintf(newqf->qfname, MAXPATHLEN + 1, "%s_%d.orig", qf->qfname, 532 qf->wordsize); 533 if (rename(qf->qfname, newqf->qfname) < 0) { 534 free(newqf); 535 return (-1); 536 } 537 if ((newqf->fd = open(qf->qfname, O_RDWR|O_CREAT|O_TRUNC, 0)) < 0) { 538 serrno = errno; 539 goto error; 540 } 541 newqf->wordsize = wordsize; 542 if (wordsize == 64) { 543 memset(&dqh, 0, sizeof(dqh)); 544 memcpy(dqh.dqh_magic, Q_DQHDR64_MAGIC, sizeof(dqh.dqh_magic)); 545 dqh.dqh_version = htobe32(Q_DQHDR64_VERSION); 546 dqh.dqh_hdrlen = htobe32(sizeof(struct dqhdr64)); 547 dqh.dqh_reclen = htobe32(sizeof(struct dqblk64)); 548 if (write(newqf->fd, &dqh, sizeof(dqh)) != sizeof(dqh)) { 549 serrno = errno; 550 goto error; 551 } 552 } 553 grp = getgrnam(QUOTAGROUP); 554 fchown(newqf->fd, 0, grp ? grp->gr_gid : 0); 555 fchmod(newqf->fd, 0640); 556 for (id = 0; id <= maxid; id++) { 557 if ((quota_read(qf, &dqblk, id)) < 0) 558 break; 559 switch (newqf->wordsize) { 560 case 32: 561 if ((quota_write32(newqf, &dqblk, id)) < 0) 562 break; 563 continue; 564 case 64: 565 if ((quota_write64(newqf, &dqblk, id)) < 0) 566 break; 567 continue; 568 default: 569 errno = EINVAL; 570 break; 571 } 572 } 573 if (id < maxid) { 574 serrno = errno; 575 goto error; 576 } 577 /* 578 * Update the passed in quotafile to reference the new file 579 * of the converted format size. 580 */ 581 fd = qf->fd; 582 qf->fd = newqf->fd; 583 newqf->fd = fd; 584 qf->wordsize = newqf->wordsize; 585 quota_close(newqf); 586 return (0); 587 error: 588 /* put back the original file */ 589 (void) rename(newqf->qfname, qf->qfname); 590 quota_close(newqf); 591 errno = serrno; 592 return (-1); 593 } 594