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