1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 * Copyright (c) 2016 by Delphix. All rights reserved. 25 */ 26 27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 28 /* All Rights Reserved */ 29 30 /* 31 * University Copyright- Copyright (c) 1982, 1986, 1988 32 * The Regents of the University of California 33 * All Rights Reserved 34 * 35 * University Acknowledgment- Portions of this document are derived from 36 * software developed by the University of California, Berkeley, and its 37 * contributors. 38 */ 39 40 /* 41 * Disk quota editor. 42 */ 43 #include <stdlib.h> 44 #include <stdio.h> 45 #include <signal.h> 46 #include <errno.h> 47 #include <pwd.h> 48 #include <ctype.h> 49 #include <fcntl.h> 50 #include <string.h> 51 #include <strings.h> 52 #include <sys/mnttab.h> 53 #include <sys/param.h> 54 #include <sys/types.h> 55 #include <sys/mntent.h> 56 #include <sys/stat.h> 57 #include <sys/file.h> 58 #include <sys/fs/ufs_quota.h> 59 #include <sys/fs/ufs_fs.h> 60 #include <sys/wait.h> 61 #include <unistd.h> 62 #include <iso/limits_iso.h> 63 64 #define DEFEDITOR "/usr/bin/vi" 65 66 #if DEV_BSIZE < 1024 67 #define dbtok(x) ((x) / (1024 / DEV_BSIZE)) 68 #define ktodb(x) ((x) * (1024 / DEV_BSIZE)) 69 #else 70 #define dbtok(x) ((x) * (DEV_BSIZE / 1024)) 71 #define ktodb(x) ((x) / (DEV_BSIZE / 1024)) 72 #endif 73 74 struct fsquot { 75 struct fsquot *fsq_next; 76 struct dqblk fsq_dqb; 77 char *fsq_fs; 78 char *fsq_dev; 79 char *fsq_qfile; 80 }; 81 82 static struct fsquot *fsqlist; 83 84 static char tmpfil[] = "/tmp/EdP.aXXXXXX"; 85 #define QFNAME "quotas" 86 87 static uid_t getentry(char *); 88 static int editit(void); 89 static void getprivs(uid_t); 90 static void putprivs(uid_t); 91 static void gettimes(uid_t); 92 static void puttimes(uid_t); 93 static char *next(char *, char *); 94 static int alldigits(char *); 95 static void fmttime(char *, ulong_t); 96 static int unfmttime(double, char *, uint32_t *); 97 static void setupfs(void); 98 static void getdiscq(uid_t); 99 static void putdiscq(uid_t); 100 static void sigsetmask(uint_t); 101 static uint_t sigblock(uint_t); 102 static void usage(void); 103 static int quotactl(int, char *, uid_t, caddr_t); 104 105 int 106 main(int argc, char **argv) 107 { 108 uid_t uid; 109 char *basename; 110 int opt; 111 int i; 112 int tmpfd = -1; 113 114 basename = argv[0]; 115 if (argc < 2) { 116 usage(); 117 } 118 if (quotactl(Q_SYNC, (char *)NULL, 0, (caddr_t)NULL) < 0 && 119 errno == EINVAL) { 120 (void) printf("Warning: " 121 "Quotas are not compiled into this kernel\n"); 122 (void) sleep(3); 123 } 124 if (getuid()) { 125 (void) fprintf(stderr, "%s: permission denied\n", basename); 126 exit(32); 127 } 128 setupfs(); 129 if (fsqlist == NULL) { 130 (void) fprintf(stderr, "%s: no UFS filesystems with %s file\n", 131 MNTTAB, QFNAME); 132 exit(32); 133 } 134 tmpfd = mkstemp(tmpfil); 135 if (tmpfd == -1 || fchown(tmpfd, getuid(), getgid()) == -1) { 136 fprintf(stderr, "failure in temporary file %s\n", tmpfil); 137 exit(32); 138 } 139 (void) close(tmpfd); 140 while ((opt = getopt(argc, argv, "p:tV")) != EOF) 141 switch (opt) { 142 case 't': 143 gettimes(0); 144 if (editit()) 145 puttimes(0); 146 (void) unlink(tmpfil); 147 exit(0); 148 /*NOTREACHED*/ 149 150 case 'p': 151 uid = getentry(optarg); 152 if (uid > MAXUID) { 153 (void) unlink(tmpfil); 154 exit(32); 155 } 156 getprivs(uid); 157 if (optind == argc) { 158 (void) unlink(tmpfil); 159 usage(); 160 } 161 for (i = optind; i < argc; i++) { 162 uid = getentry(argv[i]); 163 if (uid > MAXUID) { 164 (void) unlink(tmpfil); 165 exit(32); 166 } 167 getdiscq(uid); 168 putprivs(uid); 169 } 170 (void) unlink(tmpfil); 171 exit(0); 172 /*NOTREACHED*/ 173 174 case 'V': /* Print command line */ 175 { 176 char *optt; 177 int optc; 178 179 (void) printf("edquota -F UFS"); 180 for (optc = 1; optc < argc; optc++) { 181 optt = argv[optc]; 182 if (optt) 183 (void) printf(" %s ", optt); 184 } 185 (void) putchar('\n'); 186 } 187 break; 188 189 case '?': 190 usage(); 191 } 192 193 for (i = optind; i < argc; i++) { 194 uid = getentry(argv[i]); 195 if (uid > MAXUID) 196 continue; 197 getprivs(uid); 198 if (editit()) 199 putprivs(uid); 200 if (uid == 0) { 201 (void) printf("edquota: Note that uid 0's quotas " 202 "are used as default values for other users,\n"); 203 (void) printf("not as a limit on the uid 0 user.\n"); 204 } 205 } 206 (void) unlink(tmpfil); 207 return (0); 208 } 209 210 static uid_t 211 getentry(char *name) 212 { 213 struct passwd *pw; 214 uid_t uid; 215 216 if (alldigits(name)) { 217 errno = 0; 218 uid = strtol(name, NULL, 10); 219 if (errno == ERANGE) { 220 /* name would cause overflow in uid */ 221 (void) fprintf(stderr, "edquota: uid %s too large\n", 222 name); 223 (void) sleep(1); 224 return (-1); 225 } 226 } else if (pw = getpwnam(name)) 227 uid = pw->pw_uid; 228 else { 229 (void) fprintf(stderr, "%s: no such user\n", name); 230 (void) sleep(1); 231 return (-1); 232 } 233 return (uid); 234 } 235 236 #define RESPSZ 128 237 238 static int 239 editit(void) 240 { 241 pid_t pid, xpid; 242 char *ed; 243 char resp[RESPSZ]; 244 int status, omask; 245 246 #define mask(s) (1 << ((s) - 1)) 247 omask = sigblock(mask(SIGINT)|mask(SIGQUIT)|mask(SIGHUP)); 248 249 if ((ed = getenv("EDITOR")) == (char *)0) 250 ed = DEFEDITOR; 251 252 /*CONSTANTCONDITION*/ 253 while (1) { 254 if ((pid = fork()) < 0) { 255 if (errno == EAGAIN) { 256 (void) fprintf(stderr, 257 "You have too many processes\n"); 258 return (0); 259 } 260 perror("fork"); 261 return (0); 262 } 263 if (pid == 0) { 264 (void) sigsetmask(omask); 265 (void) setgid(getgid()); 266 (void) setuid(getuid()); 267 (void) execlp(ed, ed, tmpfil, 0); 268 (void) fprintf(stderr, 269 "Can't exec editor \"%s\": ", ed); 270 perror(""); 271 exit(32); 272 } 273 while ((xpid = wait(&status)) >= 0) 274 if (xpid == pid) 275 break; 276 277 if (!isatty(fileno(stdin))) { /* Non-interactive */ 278 break; 279 } 280 281 /* 282 * Certain editors can exit with a non-zero status even 283 * though everything is peachy. Best to ask the user what 284 * they really wants to do. (N.B.: if we're non-interactive 285 * we'll "break" the while loop before we get here.) 286 */ 287 if (WIFEXITED(status) && (WEXITSTATUS(status) != 0)) { 288 (void) printf("Non-zero return from \"%s\", ", ed); 289 (void) printf("updated file may contain errors.\n"); 290 /*CONSTANTCONDITION*/ 291 while (1) { 292 (void) printf("Edit again (e) or quit, " 293 "discarding changes (q)? "); 294 (void) fflush(stdout); 295 if (gets(resp) == NULL) { 296 return (0); 297 } 298 if ((*resp == 'e') || (*resp == 'q')) { 299 break; 300 } 301 } 302 303 if (*resp == 'e') { 304 continue; 305 } else { 306 /* 307 * Since (*resp == 'q'), then we just 308 * want to break out of here and return 309 * the failure. 310 */ 311 break; 312 } 313 } else { 314 break; /* Successful return from editor */ 315 } 316 } 317 (void) sigsetmask(omask); 318 return (!status); 319 } 320 321 static void 322 getprivs(uid_t uid) 323 { 324 struct fsquot *fsqp; 325 FILE *fd; 326 327 getdiscq(uid); 328 if ((fd = fopen64(tmpfil, "w")) == NULL) { 329 (void) fprintf(stderr, "edquota: "); 330 perror(tmpfil); 331 (void) unlink(tmpfil); 332 exit(32); 333 } 334 for (fsqp = fsqlist; fsqp; fsqp = fsqp->fsq_next) 335 (void) fprintf(fd, 336 "fs %s blocks (soft = %lu, hard = %lu) " 337 "inodes (soft = %lu, hard = %lu)\n", 338 fsqp->fsq_fs, 339 dbtok(fsqp->fsq_dqb.dqb_bsoftlimit), 340 dbtok(fsqp->fsq_dqb.dqb_bhardlimit), 341 fsqp->fsq_dqb.dqb_fsoftlimit, 342 fsqp->fsq_dqb.dqb_fhardlimit); 343 (void) fclose(fd); 344 } 345 346 static void 347 putprivs(uid_t uid) 348 { 349 FILE *fd; 350 uint64_t tmp_bsoftlimit, tmp_bhardlimit, tmp_fsoftlimit, 351 tmp_fhardlimit; 352 char line[BUFSIZ]; 353 int changed = 0; 354 uint32_t max_limit; 355 int quota_entry_printed; 356 357 fd = fopen64(tmpfil, "r"); 358 if (fd == NULL) { 359 (void) fprintf(stderr, "Can't re-read temp file!!\n"); 360 return; 361 } 362 while (fgets(line, sizeof (line), fd) != NULL) { 363 struct fsquot *fsqp; 364 char *cp, *dp; 365 int n; 366 367 cp = next(line, " \t"); 368 if (cp == NULL) 369 break; 370 *cp++ = '\0'; 371 while (*cp && *cp == '\t' && *cp == ' ') 372 cp++; 373 dp = cp, cp = next(cp, " \t"); 374 if (cp == NULL) 375 break; 376 *cp++ = '\0'; 377 for (fsqp = fsqlist; fsqp; fsqp = fsqp->fsq_next) { 378 if (strcmp(dp, fsqp->fsq_fs) == 0) 379 break; 380 } 381 if (fsqp == NULL) { 382 (void) fprintf(stderr, "%s: unknown file system\n", cp); 383 continue; 384 } 385 while (*cp && *cp == '\t' && *cp == ' ') 386 cp++; 387 388 /* 389 * At this point, dp points to the mount point of the 390 * file system and cp points to the remainder of the 391 * quota definition string. 392 */ 393 n = sscanf(cp, 394 "blocks (soft = %llu, hard = %llu) " 395 "inodes (soft = %llu, hard = %llu)\n", 396 &tmp_bsoftlimit, 397 &tmp_bhardlimit, 398 &tmp_fsoftlimit, 399 &tmp_fhardlimit); 400 401 if (n != 4) { 402 (void) fprintf(stderr, "%s: bad format\n", cp); 403 continue; 404 } 405 406 /* 407 * The values in dqb_bsoftlimit and dqb_bhardlimit 408 * are specified in 1k blocks in the edited quota 409 * file (the one we're reading), but are specified in 410 * disk blocks in the data structure passed to quotactl(). 411 * That means that the maximum allowed value for the 412 * hard and soft block limits in the edited quota file 413 * is the maximum number of disk blocks allowed in a 414 * quota (which is 2^32 - 1, since it's a 32-bit unsigned 415 * quantity), converted to 1k blocks. 416 */ 417 max_limit = dbtok(UINT_MAX); 418 419 quota_entry_printed = 0; /* only print quota entry once */ 420 421 if (tmp_bsoftlimit > max_limit) { 422 tmp_bsoftlimit = max_limit; 423 if (!quota_entry_printed) { 424 (void) fprintf(stderr, "%s %s%\n", dp, cp); 425 quota_entry_printed = 1; 426 } 427 (void) fprintf(stderr, 428 "error: soft limit for blocks exceeds maximum allowed value,\n" 429 " soft limit for blocks set to %lu\n", max_limit); 430 } 431 432 if (tmp_bhardlimit > max_limit) { 433 tmp_bhardlimit = max_limit; 434 if (!quota_entry_printed) { 435 (void) fprintf(stderr, "%s %s%\n", dp, cp); 436 quota_entry_printed = 1; 437 } 438 (void) fprintf(stderr, 439 "error: hard limit for blocks exceeds maximum allowed value,\n" 440 " hard limit for blocks set to %lu\n", max_limit); 441 } 442 443 444 /* 445 * Now check the file limits against their maximum, which 446 * is UINT_MAX (since it must fit in a uint32_t). 447 */ 448 max_limit = UINT_MAX; 449 450 if (tmp_fsoftlimit > max_limit) { 451 tmp_fsoftlimit = max_limit; 452 if (!quota_entry_printed) { 453 (void) fprintf(stderr, "%s %s%\n", dp, cp); 454 quota_entry_printed = 1; 455 } 456 (void) fprintf(stderr, 457 "error: soft limit for files exceeds maximum allowed value,\n" 458 " soft limit for files set to %lu\n", max_limit); 459 } 460 461 if (tmp_fhardlimit > max_limit) { 462 tmp_fhardlimit = max_limit; 463 if (!quota_entry_printed) { 464 (void) fprintf(stderr, "%s %s%\n", dp, cp); 465 quota_entry_printed = 1; 466 } 467 (void) fprintf(stderr, 468 "error: hard limit for files exceeds maximum allowed value,\n" 469 " hard limit for files set to %lu\n", max_limit); 470 } 471 472 changed++; 473 tmp_bsoftlimit = ktodb(tmp_bsoftlimit); 474 tmp_bhardlimit = ktodb(tmp_bhardlimit); 475 /* 476 * It we are decreasing the soft limits, set the time limits 477 * to zero, in case the user is now over quota. 478 * the time limit will be started the next time the 479 * user does an allocation. 480 */ 481 if (tmp_bsoftlimit < fsqp->fsq_dqb.dqb_bsoftlimit) 482 fsqp->fsq_dqb.dqb_btimelimit = 0; 483 if (tmp_fsoftlimit < fsqp->fsq_dqb.dqb_fsoftlimit) 484 fsqp->fsq_dqb.dqb_ftimelimit = 0; 485 fsqp->fsq_dqb.dqb_bsoftlimit = tmp_bsoftlimit; 486 fsqp->fsq_dqb.dqb_bhardlimit = tmp_bhardlimit; 487 fsqp->fsq_dqb.dqb_fsoftlimit = tmp_fsoftlimit; 488 fsqp->fsq_dqb.dqb_fhardlimit = tmp_fhardlimit; 489 } 490 (void) fclose(fd); 491 if (changed) 492 putdiscq(uid); 493 } 494 495 static void 496 gettimes(uid_t uid) 497 { 498 struct fsquot *fsqp; 499 FILE *fd; 500 char btime[80], ftime[80]; 501 502 getdiscq(uid); 503 if ((fd = fopen64(tmpfil, "w")) == NULL) { 504 (void) fprintf(stderr, "edquota: "); 505 perror(tmpfil); 506 (void) unlink(tmpfil); 507 exit(32); 508 } 509 for (fsqp = fsqlist; fsqp; fsqp = fsqp->fsq_next) { 510 fmttime(btime, fsqp->fsq_dqb.dqb_btimelimit); 511 fmttime(ftime, fsqp->fsq_dqb.dqb_ftimelimit); 512 (void) fprintf(fd, 513 "fs %s blocks time limit = %s, files time limit = %s\n", 514 fsqp->fsq_fs, btime, ftime); 515 } 516 (void) fclose(fd); 517 } 518 519 static void 520 puttimes(uid_t uid) 521 { 522 FILE *fd; 523 char line[BUFSIZ]; 524 int changed = 0; 525 double btimelimit, ftimelimit; 526 char bunits[80], funits[80]; 527 528 fd = fopen64(tmpfil, "r"); 529 if (fd == NULL) { 530 (void) fprintf(stderr, "Can't re-read temp file!!\n"); 531 return; 532 } 533 while (fgets(line, sizeof (line), fd) != NULL) { 534 struct fsquot *fsqp; 535 char *cp, *dp; 536 int n; 537 538 cp = next(line, " \t"); 539 if (cp == NULL) 540 break; 541 *cp++ = '\0'; 542 while (*cp && *cp == '\t' && *cp == ' ') 543 cp++; 544 dp = cp, cp = next(cp, " \t"); 545 if (cp == NULL) 546 break; 547 *cp++ = '\0'; 548 for (fsqp = fsqlist; fsqp; fsqp = fsqp->fsq_next) { 549 if (strcmp(dp, fsqp->fsq_fs) == 0) 550 break; 551 } 552 if (fsqp == NULL) { 553 (void) fprintf(stderr, "%s: unknown file system\n", cp); 554 continue; 555 } 556 while (*cp && *cp == '\t' && *cp == ' ') 557 cp++; 558 n = sscanf(cp, 559 "blocks time limit = %lf %[^,], " 560 "files time limit = %lf %s\n", 561 &btimelimit, bunits, &ftimelimit, funits); 562 if (n != 4 || 563 !unfmttime(btimelimit, bunits, 564 &fsqp->fsq_dqb.dqb_btimelimit) || 565 !unfmttime(ftimelimit, funits, 566 &fsqp->fsq_dqb.dqb_ftimelimit)) { 567 (void) fprintf(stderr, "%s: bad format\n", cp); 568 continue; 569 } 570 changed++; 571 } 572 (void) fclose(fd); 573 if (changed) 574 putdiscq(uid); 575 } 576 577 static char * 578 next(char *cp, char *match) 579 { 580 char *dp; 581 582 while (cp && *cp) { 583 for (dp = match; dp && *dp; dp++) 584 if (*dp == *cp) 585 return (cp); 586 cp++; 587 } 588 return ((char *)0); 589 } 590 591 static int 592 alldigits(char *s) 593 { 594 int c = *s++; 595 596 do { 597 if (!isdigit(c)) 598 return (0); 599 } while ((c = *s++) != '\0'); 600 601 return (1); 602 } 603 604 static struct { 605 int c_secs; /* conversion units in secs */ 606 char *c_str; /* unit string */ 607 } cunits [] = { 608 {60*60*24*28, "month"}, 609 {60*60*24*7, "week"}, 610 {60*60*24, "day"}, 611 {60*60, "hour"}, 612 {60, "min"}, 613 {1, "sec"} 614 }; 615 616 static void 617 fmttime(char *buf, ulong_t time) 618 { 619 double value; 620 int i; 621 622 if (time == 0) { 623 (void) strcpy(buf, "0 (default)"); 624 return; 625 } 626 for (i = 0; i < sizeof (cunits) / sizeof (cunits[0]); i++) 627 if (time >= cunits[i].c_secs) 628 break; 629 630 value = (double)time / cunits[i].c_secs; 631 (void) sprintf(buf, "%.2f %s%s", 632 value, cunits[i].c_str, value > 1.0 ? "s" : ""); 633 } 634 635 static int 636 unfmttime(double value, char *units, uint32_t *timep) 637 { 638 int i; 639 640 if (value == 0.0) { 641 *timep = 0; 642 return (1); 643 } 644 for (i = 0; i < sizeof (cunits) / sizeof (cunits[0]); i++) { 645 if (strncmp(cunits[i].c_str, units, 646 strlen(cunits[i].c_str)) == 0) 647 break; 648 } 649 if (i >= sizeof (cunits) / sizeof (cunits[0])) 650 return (0); 651 *timep = (ulong_t)(value * cunits[i].c_secs); 652 return (1); 653 } 654 655 static void 656 setupfs(void) 657 { 658 struct mnttab mntp; 659 struct fsquot *fsqp; 660 struct stat64 statb; 661 dev_t fsdev; 662 FILE *mtab; 663 char qfilename[MAXPATHLEN]; 664 665 if ((mtab = fopen(MNTTAB, "r")) == (FILE *)0) { 666 perror("/etc/mnttab"); 667 exit(31+1); 668 } 669 while (getmntent(mtab, &mntp) == 0) { 670 if (strcmp(mntp.mnt_fstype, MNTTYPE_UFS) != 0) 671 continue; 672 if (stat64(mntp.mnt_special, &statb) < 0) 673 continue; 674 if ((statb.st_mode & S_IFMT) != S_IFBLK) 675 continue; 676 fsdev = statb.st_rdev; 677 (void) snprintf(qfilename, sizeof (qfilename), "%s/%s", 678 mntp.mnt_mountp, QFNAME); 679 if (stat64(qfilename, &statb) < 0 || statb.st_dev != fsdev) 680 continue; 681 fsqp = malloc(sizeof (struct fsquot)); 682 if (fsqp == NULL) { 683 (void) fprintf(stderr, "out of memory\n"); 684 exit(31+1); 685 } 686 fsqp->fsq_next = fsqlist; 687 fsqp->fsq_fs = strdup(mntp.mnt_mountp); 688 fsqp->fsq_dev = strdup(mntp.mnt_special); 689 fsqp->fsq_qfile = strdup(qfilename); 690 if (fsqp->fsq_fs == NULL || fsqp->fsq_dev == NULL || 691 fsqp->fsq_qfile == NULL) { 692 (void) fprintf(stderr, "out of memory\n"); 693 exit(31+1); 694 } 695 fsqlist = fsqp; 696 } 697 (void) fclose(mtab); 698 } 699 700 static void 701 getdiscq(uid_t uid) 702 { 703 struct fsquot *fsqp; 704 int fd; 705 706 for (fsqp = fsqlist; fsqp; fsqp = fsqp->fsq_next) { 707 if (quotactl(Q_GETQUOTA, fsqp->fsq_dev, uid, 708 (caddr_t)&fsqp->fsq_dqb) != 0) { 709 if ((fd = open64(fsqp->fsq_qfile, O_RDONLY)) < 0) { 710 (void) fprintf(stderr, "edquota: "); 711 perror(fsqp->fsq_qfile); 712 continue; 713 } 714 (void) llseek(fd, (offset_t)dqoff(uid), L_SET); 715 switch (read(fd, (char *)&fsqp->fsq_dqb, 716 sizeof (struct dqblk))) { 717 case 0: 718 /* 719 * Convert implicit 0 quota (EOF) 720 * into an explicit one (zero'ed dqblk) 721 */ 722 bzero((caddr_t)&fsqp->fsq_dqb, 723 sizeof (struct dqblk)); 724 break; 725 726 case sizeof (struct dqblk): /* OK */ 727 break; 728 729 default: /* ERROR */ 730 (void) fprintf(stderr, 731 "edquota: read error in "); 732 perror(fsqp->fsq_qfile); 733 break; 734 } 735 (void) close(fd); 736 } 737 } 738 } 739 740 static void 741 putdiscq(uid_t uid) 742 { 743 struct fsquot *fsqp; 744 745 for (fsqp = fsqlist; fsqp; fsqp = fsqp->fsq_next) { 746 if (quotactl(Q_SETQLIM, fsqp->fsq_dev, uid, 747 (caddr_t)&fsqp->fsq_dqb) != 0) { 748 int fd; 749 750 if ((fd = open64(fsqp->fsq_qfile, O_RDWR)) < 0) { 751 (void) fprintf(stderr, "edquota: "); 752 perror(fsqp->fsq_qfile); 753 continue; 754 } 755 (void) llseek(fd, (offset_t)dqoff(uid), L_SET); 756 if (write(fd, (char *)&fsqp->fsq_dqb, 757 sizeof (struct dqblk)) != sizeof (struct dqblk)) { 758 (void) fprintf(stderr, "edquota: "); 759 perror(fsqp->fsq_qfile); 760 } 761 (void) close(fd); 762 } 763 } 764 } 765 766 static void 767 sigsetmask(uint_t omask) 768 { 769 int i; 770 771 for (i = 0; i < 32; i++) 772 if (omask & (1 << i)) { 773 if (sigignore(1 << i) == (int)SIG_ERR) { 774 (void) fprintf(stderr, 775 "Bad signal 0x%x\n", (1 << i)); 776 exit(31+1); 777 } 778 } 779 } 780 781 static uint_t 782 sigblock(uint_t omask) 783 { 784 uint_t previous = 0; 785 uint_t temp; 786 int i; 787 788 for (i = 0; i < 32; i++) 789 if (omask & (1 << i)) { 790 if ((temp = sigignore(1 << i)) == (int)SIG_ERR) { 791 (void) fprintf(stderr, 792 "Bad signal 0x%x\n", (1 << i)); 793 exit(31+1); 794 } 795 if (i == 0) 796 previous = temp; 797 } 798 799 return (previous); 800 } 801 802 static void 803 usage(void) 804 { 805 (void) fprintf(stderr, "ufs usage:\n"); 806 (void) fprintf(stderr, "\tedquota [-p username] username ...\n"); 807 (void) fprintf(stderr, "\tedquota -t\n"); 808 exit(1); 809 } 810 811 static int 812 quotactl(int cmd, char *special, uid_t uid, caddr_t addr) 813 { 814 int fd; 815 int status; 816 struct quotctl quota; 817 char qfile[MAXPATHLEN]; 818 FILE *fstab; 819 struct mnttab mntp; 820 821 if ((special == NULL) && (cmd == Q_SYNC)) { 822 cmd = Q_ALLSYNC; 823 /* 824 * need to find an acceptable fd to send this Q_ALLSYNC down 825 * on, it needs to be a ufs fd for vfs to at least call the 826 * real quotactl() in the kernel 827 * Here, try to simply find the starting mountpoint of the 828 * first mounted ufs file system 829 */ 830 } 831 832 /* 833 * Find the mount point of the special device. This is 834 * because the fcntl that implements the quotactl call has 835 * to go to a real file, and not to the block device. 836 */ 837 if ((fstab = fopen(MNTTAB, "r")) == NULL) { 838 (void) fprintf(stderr, "%s: ", MNTTAB); 839 perror("open"); 840 exit(31+1); 841 } 842 qfile[0] = '\0'; 843 while ((status = getmntent(fstab, &mntp)) == 0) { 844 /* 845 * check that it is a ufs file system 846 * for all quotactl()s except Q_ALLSYNC check that 847 * the file system is read-write since changes in the 848 * quotas file may be required 849 * for Q_ALLSYNC, this check is skipped since this option 850 * is to determine if quotas are configured into the system 851 */ 852 if (strcmp(mntp.mnt_fstype, MNTTYPE_UFS) != 0 || 853 ((cmd != Q_ALLSYNC) && hasmntopt(&mntp, MNTOPT_RO))) 854 continue; 855 if (cmd == Q_ALLSYNC) { /* implies (special==0) too */ 856 if (strlcpy(qfile, mntp.mnt_mountp, 857 sizeof (qfile)) >= sizeof (qfile)) { 858 errno = ENOENT; 859 return (-1); 860 } 861 break; 862 } 863 if (strcmp(special, mntp.mnt_special) == 0) { 864 if (strlcpy(qfile, mntp.mnt_mountp, 865 sizeof (qfile)) >= sizeof (qfile)) { 866 errno = ENOENT; 867 return (-1); 868 } 869 } 870 } 871 (void) fclose(fstab); 872 if (qfile[0] == '\0') { 873 errno = ENOENT; 874 return (-1); 875 } 876 { 877 int open_flags; 878 879 if (cmd == Q_ALLSYNC) { 880 open_flags = O_RDONLY; 881 } else { 882 if (strlcat(qfile, "/" QFNAME, sizeof (qfile)) >= 883 sizeof (qfile)) { 884 errno = ENOENT; 885 return (-1); 886 } 887 open_flags = O_RDWR; 888 } 889 890 if ((fd = open64(qfile, open_flags)) < 0) { 891 (void) fprintf(stderr, "quotactl: "); 892 perror("open"); 893 exit(31+1); 894 } 895 } 896 897 quota.op = cmd; 898 quota.uid = uid; 899 quota.addr = addr; 900 status = ioctl(fd, Q_QUOTACTL, "a); 901 (void) close(fd); 902 return (status); 903 } 904