1 /* 2 * Copyright (c) 1980 Regents of the University of California. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms are permitted 6 * provided that the above copyright notice and this paragraph are 7 * duplicated in all such forms and that any documentation, 8 * advertising materials, and other materials related to such 9 * distribution and use acknowledge that the software was developed 10 * by the University of California, Berkeley. The name of the 11 * University may not be used to endorse or promote products derived 12 * from this software without specific prior written permission. 13 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 14 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 15 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 16 */ 17 18 #ifndef lint 19 char copyright[] = 20 "@(#) Copyright (c) 1980 Regents of the University of California.\n\ 21 All rights reserved.\n"; 22 #endif /* not lint */ 23 24 #ifndef lint 25 static char sccsid[] = "@(#)edquota.c 5.7 (Berkeley) 03/05/89"; 26 #endif /* not lint */ 27 28 /* 29 * Disk quota editor. 30 */ 31 #include <sys/param.h> 32 #include <sys/stat.h> 33 #include <sys/file.h> 34 #include <sys/quota.h> 35 #include <signal.h> 36 #include <errno.h> 37 #include <fstab.h> 38 #include <pwd.h> 39 #include <ctype.h> 40 #include <stdio.h> 41 42 #define DEFEDITOR "/usr/ucb/vi" 43 44 struct dquot dq[NMOUNT]; 45 struct dquot odq[NMOUNT]; 46 char dqf[NMOUNT][MAXPATHLEN + 1]; 47 char odqf[NMOUNT][MAXPATHLEN + 1]; 48 49 char tmpfil[] = "/tmp/EdP.aXXXXX"; 50 char *qfname = "quotas"; 51 char *getenv(); 52 53 main(argc, argv) 54 char **argv; 55 { 56 int uid; 57 char *arg0; 58 59 mktemp(tmpfil); 60 close(creat(tmpfil, 0600)); 61 chown(tmpfil, getuid(), getgid()); 62 arg0 = *argv++; 63 if (argc < 2) { 64 fprintf(stderr, "Usage: %s [-p username] username ...\n", arg0); 65 unlink(tmpfil); 66 exit(1); 67 } 68 --argc; 69 if (getuid()) { 70 fprintf(stderr, "%s: permission denied\n", arg0); 71 unlink(tmpfil); 72 exit(1); 73 } 74 if (argc > 2 && strcmp(*argv, "-p") == 0) { 75 argc--, argv++; 76 uid = getentry(*argv++); 77 if (uid < 0) { 78 unlink(tmpfil); 79 exit(1); 80 } 81 getprivs(uid); 82 argc--; 83 while (argc-- > 0) { 84 uid = getentry(*argv++); 85 if (uid < 0) 86 continue; 87 getdiscq(uid, odq, odqf); 88 putprivs(uid); 89 } 90 unlink(tmpfil); 91 exit(0); 92 } 93 while (--argc >= 0) { 94 uid = getentry(*argv++); 95 if (uid < 0) 96 continue; 97 getprivs(uid); 98 if (editit()) 99 putprivs(uid); 100 } 101 unlink(tmpfil); 102 exit(0); 103 } 104 105 getentry(name) 106 char *name; 107 { 108 struct passwd *pw; 109 int uid; 110 111 if (alldigits(name)) 112 uid = atoi(name); 113 else if (pw = getpwnam(name)) 114 uid = pw->pw_uid; 115 else { 116 fprintf(stderr, "%s: no such user\n", name); 117 sleep(1); 118 return (-1); 119 } 120 return (uid); 121 } 122 123 editit() 124 { 125 register int pid, xpid; 126 long omask; 127 int stat; 128 129 omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGHUP)); 130 top: 131 if ((pid = fork()) < 0) { 132 extern errno; 133 134 if (errno == EPROCLIM) { 135 fprintf(stderr, "You have too many processes\n"); 136 return(0); 137 } 138 if (errno == EAGAIN) { 139 sleep(1); 140 goto top; 141 } 142 perror("fork"); 143 return (0); 144 } 145 if (pid == 0) { 146 register char *ed; 147 148 sigsetmask(omask); 149 setgid(getgid()); 150 setuid(getuid()); 151 if ((ed = getenv("EDITOR")) == (char *)0) 152 ed = DEFEDITOR; 153 execlp(ed, ed, tmpfil, 0); 154 perror(ed); 155 exit(1); 156 } 157 while ((xpid = wait(&stat)) >= 0) 158 if (xpid == pid) 159 break; 160 sigsetmask(omask); 161 return (!stat); 162 } 163 164 getprivs(uid) 165 register uid; 166 { 167 register i; 168 FILE *fd; 169 170 getdiscq(uid, dq, dqf); 171 for (i = 0; i < NMOUNT; i++) { 172 odq[i] = dq[i]; 173 strcpy(odqf[i], dqf[i]); 174 } 175 if ((fd = fopen(tmpfil, "w")) == NULL) { 176 fprintf(stderr, "edquota: "); 177 perror(tmpfil); 178 exit(1); 179 } 180 for (i = 0; i < NMOUNT; i++) { 181 if (*dqf[i] == '\0') 182 continue; 183 fprintf(fd, 184 "fs %s blocks (soft = %d, hard = %d) inodes (soft = %d, hard = %d)\n" 185 , dqf[i] 186 , dbtob(dq[i].dq_bsoftlimit) / 1024 187 , dbtob(dq[i].dq_bhardlimit) / 1024 188 , dq[i].dq_isoftlimit 189 , dq[i].dq_ihardlimit 190 ); 191 } 192 fclose(fd); 193 } 194 195 putprivs(uid) 196 register uid; 197 { 198 register i, j; 199 int n; 200 FILE *fd; 201 char line[BUFSIZ]; 202 203 fd = fopen(tmpfil, "r"); 204 if (fd == NULL) { 205 fprintf(stderr, "Can't re-read temp file!!\n"); 206 return; 207 } 208 for (i = 0; i < NMOUNT; i++) { 209 char *cp, *dp, *next(); 210 211 if (fgets(line, sizeof (line), fd) == NULL) 212 break; 213 cp = next(line, " \t"); 214 if (cp == NULL) 215 break; 216 *cp++ = '\0'; 217 while (*cp && *cp == '\t' && *cp == ' ') 218 cp++; 219 dp = cp, cp = next(cp, " \t"); 220 if (cp == NULL) 221 break; 222 *cp++ = '\0'; 223 while (*cp && *cp == '\t' && *cp == ' ') 224 cp++; 225 strcpy(dqf[i], dp); 226 n = sscanf(cp, 227 "blocks (soft = %d, hard = %d) inodes (soft = %hd, hard = %hd)\n" 228 , &dq[i].dq_bsoftlimit 229 , &dq[i].dq_bhardlimit 230 , &dq[i].dq_isoftlimit 231 , &dq[i].dq_ihardlimit 232 ); 233 if (n != 4) { 234 fprintf(stderr, "%s: bad format\n", cp); 235 continue; 236 } 237 dq[i].dq_bsoftlimit = btodb(dq[i].dq_bsoftlimit * 1024); 238 dq[i].dq_bhardlimit = btodb(dq[i].dq_bhardlimit * 1024); 239 } 240 fclose(fd); 241 n = i; 242 for (i = 0; i < n; i++) { 243 if (*dqf[i] == '\0') 244 break; 245 for (j = 0; j < NMOUNT; j++) { 246 if (strcmp(dqf[i], odqf[j]) == 0) 247 break; 248 } 249 if (j >= NMOUNT) 250 continue; 251 *odqf[j] = '\0'; 252 /* 253 * This isn't really good enough, it is quite likely 254 * to have changed while we have been away editing, 255 * but it's not important enough to worry about at 256 * the minute. 257 */ 258 dq[i].dq_curblocks = odq[j].dq_curblocks; 259 dq[i].dq_curinodes = odq[j].dq_curinodes; 260 /* 261 * If we've upped the inode or disk block limits 262 * and the guy is out of warnings, reinitialize. 263 */ 264 if (dq[i].dq_bsoftlimit > odq[j].dq_bsoftlimit && 265 dq[i].dq_bwarn == 0) 266 dq[i].dq_bwarn = MAX_DQ_WARN; 267 if (dq[i].dq_isoftlimit > odq[j].dq_isoftlimit && 268 dq[i].dq_iwarn == 0) 269 dq[i].dq_iwarn = MAX_IQ_WARN; 270 } 271 if (i < NMOUNT) { 272 for (j = 0; j < NMOUNT; j++) { 273 if (*odqf[j] == '\0') 274 continue; 275 strcpy(dqf[i], odqf[j]); 276 dq[i].dq_isoftlimit = 0; 277 dq[i].dq_ihardlimit = 0; 278 dq[i].dq_bsoftlimit = 0; 279 dq[i].dq_bhardlimit = 0; 280 /* 281 * Same applies as just above 282 * but matters not at all, as we are just 283 * turning quota'ing off for this filesys. 284 */ 285 dq[i].dq_curblocks = odq[j].dq_curblocks; 286 dq[i].dq_curinodes = odq[j].dq_curinodes; 287 if (++i >= NMOUNT) 288 break; 289 } 290 } 291 if (*dqf[0]) 292 putdiscq(uid, dq, dqf); 293 } 294 295 char * 296 next(cp, match) 297 register char *cp; 298 char *match; 299 { 300 register char *dp; 301 302 while (cp && *cp) { 303 for (dp = match; dp && *dp; dp++) 304 if (*dp == *cp) 305 return (cp); 306 cp++; 307 } 308 return ((char *)0); 309 } 310 311 alldigits(s) 312 register char *s; 313 { 314 register c; 315 316 c = *s++; 317 do { 318 if (!isdigit(c)) 319 return (0); 320 } while (c = *s++); 321 return (1); 322 } 323 324 getdiscq(uid, dq, dqf) 325 register uid; 326 register struct dquot *dq; 327 register char (*dqf)[MAXPATHLEN + 1]; 328 { 329 register struct fstab *fs; 330 char qfilename[MAXPATHLEN + 1]; 331 struct stat statb; 332 struct dqblk dqblk; 333 dev_t fsdev; 334 int fd; 335 static int warned = 0; 336 extern int errno; 337 338 setfsent(); 339 while (fs = getfsent()) { 340 if (stat(fs->fs_spec, &statb) < 0) 341 continue; 342 fsdev = statb.st_rdev; 343 sprintf(qfilename, "%s/%s", fs->fs_file, qfname); 344 if (stat(qfilename, &statb) < 0 || statb.st_dev != fsdev) 345 continue; 346 if (quota(Q_GETDLIM, uid, fsdev, &dqblk) != 0) { 347 if (errno == EINVAL && !warned) { 348 warned++; 349 fprintf(stderr, "Warning: %s\n", 350 "Quotas are not compiled into this kernel"); 351 sleep(3); 352 } 353 fd = open(qfilename, O_RDONLY); 354 if (fd < 0) 355 continue; 356 lseek(fd, (long)(uid * sizeof dqblk), L_SET); 357 switch (read(fd, &dqblk, sizeof dqblk)) { 358 case 0: /* EOF */ 359 /* 360 * Convert implicit 0 quota (EOF) 361 * into an explicit one (zero'ed dqblk) 362 */ 363 bzero((caddr_t)&dqblk, sizeof dqblk); 364 break; 365 366 case sizeof dqblk: /* OK */ 367 break; 368 369 default: /* ERROR */ 370 fprintf(stderr, "edquota: read error in "); 371 perror(qfilename); 372 close(fd); 373 continue; 374 } 375 close(fd); 376 } 377 dq->dq_dqb = dqblk; 378 dq->dq_dev = fsdev; 379 strcpy(*dqf, fs->fs_file); 380 dq++, dqf++; 381 } 382 endfsent(); 383 **dqf = '\0'; 384 } 385 386 putdiscq(uid, dq, dqf) 387 register uid; 388 register struct dquot *dq; 389 register char (*dqf)[MAXPATHLEN + 1]; 390 { 391 register fd, cnt; 392 struct stat sb; 393 struct fstab *fs; 394 395 cnt = 0; 396 for (cnt = 0; ++cnt <= NMOUNT && **dqf; dq++, dqf++) { 397 fs = getfsfile(*dqf); 398 if (fs == NULL) { 399 fprintf(stderr, "%s: not in /etc/fstab\n", *dqf); 400 continue; 401 } 402 strcat(*dqf, "/"); 403 strcat(*dqf, qfname); 404 if (stat(*dqf, &sb) >= 0) 405 quota(Q_SETDLIM, uid, sb.st_dev, &dq->dq_dqb); 406 if ((fd = open(*dqf, 1)) < 0) { 407 perror(*dqf); 408 } else { 409 lseek(fd, (long)uid * (long)sizeof (struct dqblk), 0); 410 if (write(fd, &dq->dq_dqb, sizeof (struct dqblk)) != 411 sizeof (struct dqblk)) { 412 fprintf(stderr, "edquota: "); 413 perror(*dqf); 414 } 415 close(fd); 416 } 417 } 418 } 419