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