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