]> git.cameronkatri.com Git - apple_cmds.git/blob - diskdev_cmds/repquota.tproj/repquota.c
Update README.md
[apple_cmds.git] / diskdev_cmds / repquota.tproj / repquota.c
1 /*
2 * Copyright (c) 2002-2005 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23 /*
24 * Copyright (c) 1980, 1990, 1993
25 * The Regents of the University of California. All rights reserved.
26 *
27 * This code is derived from software contributed to Berkeley by
28 * Robert Elz at The University of Melbourne.
29 *
30 * Redistribution and use in source and binary forms, with or without
31 * modification, are permitted provided that the following conditions
32 * are met:
33 * 1. Redistributions of source code must retain the above copyright
34 * notice, this list of conditions and the following disclaimer.
35 * 2. Redistributions in binary form must reproduce the above copyright
36 * notice, this list of conditions and the following disclaimer in the
37 * documentation and/or other materials provided with the distribution.
38 * 3. All advertising materials mentioning features or use of this software
39 * must display the following acknowledgement:
40 * This product includes software developed by the University of
41 * California, Berkeley and its contributors.
42 * 4. Neither the name of the University nor the names of its contributors
43 * may be used to endorse or promote products derived from this software
44 * without specific prior written permission.
45 *
46 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
47 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
48 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
49 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
50 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
51 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
52 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
53 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
54 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
55 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
56 * SUCH DAMAGE.
57 */
58
59 #include <sys/cdefs.h>
60
61 #ifndef lint
62 __unused static char copyright[] =
63 "@(#) Copyright (c) 1980, 1990, 1993\n\
64 The Regents of the University of California. All rights reserved.\n";
65 #endif /* not lint */
66
67 #ifndef lint
68 __unused static char sccsid[] = "@(#)repquota.c 8.2 (Berkeley) 11/22/94";
69 #endif /* not lint */
70
71 /*
72 * Quota report
73 */
74 #include <sys/param.h>
75 #include <sys/stat.h>
76 #include <sys/queue.h>
77 #include <sys/quota.h>
78
79 #ifdef __APPLE__
80 #include <sys/mount.h>
81 #endif /* __APPLE__ */
82 #include <errno.h>
83 #include <fstab.h>
84 #include <grp.h>
85 #include <pwd.h>
86 #include <stdio.h>
87 #include <stdlib.h>
88 #include <string.h>
89 #include <unistd.h>
90 #ifdef __APPLE__
91 #include <libkern/OSByteOrder.h>
92 #endif /* __APPLE__ */
93
94 char *qfname = QUOTAFILENAME;
95 char *qfextension[] = INITQFNAMES;
96 #ifdef __APPLE__
97 u_int32_t quotamagic[MAXQUOTAS] = INITQMAGICS;
98 #endif /* __APPLE__ */
99
100 #ifndef __APPLE__
101 struct fileusage {
102 struct fileusage *fu_next;
103 struct dqblk fu_dqblk;
104 u_long fu_id;
105 char fu_name[1];
106 /* actually bigger */
107 };
108 struct fileusage * addid(u_long, int);
109 struct fileusage * lookup(u_long, int);
110
111 #define FUHASH 1024 /* must be power of two */
112 struct fileusage *fuhead[MAXQUOTAS][FUHASH];
113 u_long highid[MAXQUOTAS]; /* highest addid()'ed identifier per type */
114 #endif /* NOT __APPLE__ */
115
116 int vflag; /* verbose */
117 int aflag; /* all file systems */
118
119 int hasquota(struct statfs *, int, char **);
120 int repquota(struct statfs *, int, char *);
121 int oneof(char *, char **, int);
122 void usage(void);
123
124 int
125 main(argc, argv)
126 int argc;
127 char **argv;
128 {
129 #ifndef __APPLE__
130 register struct fstab *fs;
131 register struct passwd *pw;
132 register struct group *gr;
133 #endif /* __APPLE__ */
134 int gflag = 0, uflag = 0, errs = 0;
135 long i, argnum, done = 0;
136 #ifdef __APPLE__
137 int nfst;
138 struct statfs *fst;
139 #endif /* __APPLE__ */
140 char ch, *qfnp;
141
142 while ((ch = getopt(argc, argv, "aguv")) != EOF) {
143 switch(ch) {
144 case 'a':
145 aflag++;
146 break;
147 case 'g':
148 gflag++;
149 break;
150 case 'u':
151 uflag++;
152 break;
153 case 'v':
154 vflag++;
155 break;
156 default:
157 usage();
158 }
159 }
160 argc -= optind;
161 argv += optind;
162 if (argc == 0 && !aflag)
163 usage();
164 if (!gflag && !uflag) {
165 if (aflag)
166 gflag++;
167 uflag++;
168 }
169
170 #ifdef __APPLE__
171 nfst = getmntinfo(&fst, MNT_WAIT);
172 if (nfst==0) {
173 fprintf(stderr, "repquota: no filesystems mounted");
174 return(1);
175 }
176
177 for (i=0; i<nfst; i++) {
178 if(strcmp(fst[i].f_fstypename, "hfs")) {
179 continue;
180 }
181 if (aflag) {
182 if (gflag && hasquota(&fst[i], GRPQUOTA, &qfnp))
183 errs += repquota(&fst[i], GRPQUOTA, qfnp);
184 if (uflag && hasquota(&fst[i], USRQUOTA, &qfnp))
185 errs += repquota(&fst[i], USRQUOTA, qfnp);
186 continue;
187 }
188 if ((argnum = oneof(fst[i].f_mntonname, argv, argc)) >= 0 ||
189 (argnum = oneof(fst[i].f_mntfromname, argv, argc)) >= 0) {
190 done |= 1 << argnum;
191 if (gflag && hasquota(&fst[i], GRPQUOTA, &qfnp))
192 errs += repquota(&fst[i], GRPQUOTA, qfnp);
193 if (uflag && hasquota(&fst[i], USRQUOTA, &qfnp))
194 errs += repquota(&fst[i], USRQUOTA, qfnp);
195 }
196 }
197 #else
198 if (gflag) {
199 setgrent();
200 while ((gr = getgrent()) != 0)
201 (void) addid((u_long)gr->gr_gid, GRPQUOTA, gr->gr_name);
202 endgrent();
203 }
204 if (uflag) {
205 setpwent();
206 while ((pw = getpwent()) != 0)
207 (void) addid((u_long)pw->pw_uid, USRQUOTA, pw->pw_name);
208 endpwent();
209 }
210
211 setfsent();
212 while ((fs = getfsent()) != NULL) {
213 if (strcmp(fs->fs_vfstype, "ufs"))
214 continue;
215 if (aflag) {
216 if (gflag && hasquota(fs, GRPQUOTA, &qfnp))
217 errs += repquota(fs, GRPQUOTA, qfnp);
218 if (uflag && hasquota(fs, USRQUOTA, &qfnp))
219 errs += repquota(fs, USRQUOTA, qfnp);
220 continue;
221 }
222 if ((argnum = oneof(fs->fs_file, argv, argc)) >= 0 ||
223 (argnum = oneof(fs->fs_spec, argv, argc)) >= 0) {
224 done |= 1 << argnum;
225 if (gflag && hasquota(fs, GRPQUOTA, &qfnp))
226 errs += repquota(fs, GRPQUOTA, qfnp);
227 if (uflag && hasquota(fs, USRQUOTA, &qfnp))
228 errs += repquota(fs, USRQUOTA, qfnp);
229 }
230 }
231 endfsent();
232 #endif /* __APPLE */
233 for (i = 0; i < argc; i++)
234 if ((done & (1 << i)) == 0)
235 fprintf(stderr, "%s not found in fstab\n", argv[i]);
236 exit(errs);
237 }
238
239 void
240 usage()
241 {
242 fprintf(stderr, "Usage:\n\t%s\n\t%s\n",
243 "repquota [-v] [-g] [-u] -a",
244 "repquota [-v] [-g] [-u] filesys ...");
245 exit(1);
246 }
247
248 #ifdef __APPLE__
249 int
250 repquota(fst, type, qfpathname)
251 struct statfs *fst;
252 int type;
253 char *qfpathname;
254 {
255 FILE *qf;
256 uid_t id;
257 struct dqblk dqbuf;
258 char *timeprt();
259 char *name;
260 struct dqfilehdr dqhdr;
261 static int warned = 0;
262 static int multiple = 0;
263 extern int errno;
264 int i;
265 struct passwd *pw;
266 struct group *gr;
267 int maxentries;
268 u_int64_t bsoftlimit;
269 u_int32_t isoftlimit;
270 u_int64_t curbytes;
271 u_int32_t curinodes;
272
273
274 if (quotactl(fst->f_mntonname, QCMD(Q_SYNC, type), 0, 0) < 0 &&
275 errno == ENOTSUP && !warned && vflag) {
276 warned++;
277 fprintf(stdout,
278 "*** Warning: Quotas are not compiled into this kernel\n");
279 }
280 if (multiple++)
281 printf("\n");
282 if (vflag)
283 fprintf(stdout, "*** Report for %s quotas on %s (%s)\n",
284 qfextension[type], fst->f_mntonname, fst->f_mntfromname);
285 if ((qf = fopen(qfpathname, "r")) == NULL) {
286 perror(qfpathname);
287 return (1);
288 }
289
290 /* Read in the quota file header. */
291 if (fread((char *)&dqhdr, sizeof(struct dqfilehdr), 1, qf) > 0) {
292 /* Check for non big endian file. */
293 if (OSSwapBigToHostInt32(dqhdr.dqh_magic) != quotamagic[type] &&
294 OSSwapInt32(dqhdr.dqh_magic) == quotamagic[type]) {
295 (void) fprintf(stderr,
296 "*** Error: %s: not in big endian byte order\n", qfpathname);
297 (void) fclose(qf);
298 return (1);
299 }
300 /* Sanity check the quota file header. */
301 if ((OSSwapBigToHostInt32(dqhdr.dqh_magic) != quotamagic[type]) ||
302 (OSSwapBigToHostInt32(dqhdr.dqh_version) > QF_VERSION) ||
303 (!powerof2(OSSwapBigToHostInt32(dqhdr.dqh_maxentries)))) {
304 (void) fprintf(stderr,
305 "repquota: %s: not a valid quota file\n", qfpathname);
306 (void) fclose(qf);
307 return (1);
308 }
309 }
310
311 printf(" 1K Block limits File limits\n");
312 printf("User used soft hard grace used soft hard grace\n");
313
314 maxentries = OSSwapBigToHostInt32(dqhdr.dqh_maxentries);
315
316 /* Read the entries in the quota file. */
317 for (i = 0; i < maxentries; i++) {
318 if (fread(&dqbuf, sizeof(struct dqblk), 1, qf) == 0 && feof(qf))
319 break;
320 id = OSSwapBigToHostInt32(dqbuf.dqb_id);
321 if (id == 0)
322 continue;
323 if (dqbuf.dqb_curinodes == 0 && dqbuf.dqb_curbytes == 0LL)
324 continue;
325 name = NULL;
326 switch (type) {
327 case USRQUOTA:
328 if ((pw = getpwuid(id)) != 0)
329 name = pw->pw_name;
330 break;
331 case GRPQUOTA:
332 if ((gr = getgrgid(id)) != 0)
333 name = gr->gr_name;
334 break;
335 }
336 if (name)
337 printf("%-10s", name);
338 else
339 printf("%-10u", (unsigned int)id);
340
341 bsoftlimit = OSSwapBigToHostInt64( dqbuf.dqb_bsoftlimit );
342 isoftlimit = OSSwapBigToHostInt32( dqbuf.dqb_isoftlimit );
343 curbytes = OSSwapBigToHostInt64( dqbuf.dqb_curbytes );
344 curinodes = OSSwapBigToHostInt32( dqbuf.dqb_curinodes );
345
346 printf("%c%c%12qd%12qd%12qd%7s",
347 bsoftlimit &&
348 curbytes >=
349 bsoftlimit ? '+' : '-',
350 isoftlimit &&
351 curinodes >=
352 isoftlimit ? '+' : '-',
353 curbytes / 1024,
354 bsoftlimit / 1024,
355 OSSwapBigToHostInt64( dqbuf.dqb_bhardlimit ) / 1024,
356 bsoftlimit &&
357 curbytes >=
358 bsoftlimit ?
359 timeprt(OSSwapBigToHostInt32(dqbuf.dqb_btime)) : "");
360 printf(" %6d%6d%6d%7s\n",
361 curinodes,
362 isoftlimit,
363 OSSwapBigToHostInt32( dqbuf.dqb_ihardlimit ),
364 isoftlimit &&
365 curinodes >=
366 isoftlimit ?
367 timeprt(OSSwapBigToHostInt32(dqbuf.dqb_itime)) : "");
368 }
369 fclose(qf);
370
371 return (0);
372 }
373 #else
374 repquota(fs, type, qfpathname)
375 register struct fstab *fs;
376 int type;
377 char *qfpathname;
378 {
379 register struct fileusage *fup;
380 FILE *qf;
381 u_long id;
382 struct dqblk dqbuf;
383 char *timeprt();
384 static struct dqblk zerodqblk;
385 static int warned = 0;
386 static int multiple = 0;
387 extern int errno;
388
389 if (quotactl(fs->fs_file, QCMD(Q_SYNC, type), 0, 0) < 0 &&
390 errno == ENOTSUP && !warned && vflag) {
391 warned++;
392 fprintf(stdout,
393 "*** Warning: Quotas are not compiled into this kernel\n");
394 }
395 if (multiple++)
396 printf("\n");
397 if (vflag)
398 fprintf(stdout, "*** Report for %s quotas on %s (%s)\n",
399 qfextension[type], fs->fs_file, fs->fs_spec);
400 if ((qf = fopen(qfpathname, "r")) == NULL) {
401 perror(qfpathname);
402 return (1);
403 }
404 for (id = 0; ; id++) {
405 fread(&dqbuf, sizeof(struct dqblk), 1, qf);
406 if (feof(qf))
407 break;
408 if (dqbuf.dqb_curinodes == 0 && dqbuf.dqb_curblocks == 0)
409 continue;
410 if ((fup = lookup(id, type)) == 0)
411 fup = addid(id, type, (char *)0);
412 fup->fu_dqblk = dqbuf;
413 }
414 fclose(qf);
415 printf(" Block limits File limits\n");
416 printf("User used soft hard grace used soft hard grace\n");
417
418 for (id = 0; id <= highid[type]; id++) {
419 fup = lookup(id, type);
420 if (fup == 0)
421 continue;
422 if (fup->fu_dqblk.dqb_curinodes == 0 &&
423 fup->fu_dqblk.dqb_curblocks == 0)
424 continue;
425 printf("%-10s", fup->fu_name);
426 printf("%c%c%8d%8d%8d%7s",
427 fup->fu_dqblk.dqb_bsoftlimit &&
428 fup->fu_dqblk.dqb_curblocks >=
429 fup->fu_dqblk.dqb_bsoftlimit ? '+' : '-',
430 fup->fu_dqblk.dqb_isoftlimit &&
431 fup->fu_dqblk.dqb_curinodes >=
432 fup->fu_dqblk.dqb_isoftlimit ? '+' : '-',
433 dbtob(fup->fu_dqblk.dqb_curblocks) / 1024,
434 dbtob(fup->fu_dqblk.dqb_bsoftlimit) / 1024,
435 dbtob(fup->fu_dqblk.dqb_bhardlimit) / 1024,
436 fup->fu_dqblk.dqb_bsoftlimit &&
437 fup->fu_dqblk.dqb_curblocks >=
438 fup->fu_dqblk.dqb_bsoftlimit ?
439 timeprt(fup->fu_dqblk.dqb_btime) : "");
440 printf(" %6d%6d%6d%7s\n",
441 fup->fu_dqblk.dqb_curinodes,
442 fup->fu_dqblk.dqb_isoftlimit,
443 fup->fu_dqblk.dqb_ihardlimit,
444 fup->fu_dqblk.dqb_isoftlimit &&
445 fup->fu_dqblk.dqb_curinodes >=
446 fup->fu_dqblk.dqb_isoftlimit ?
447 timeprt(fup->fu_dqblk.dqb_itime) : "");
448 fup->fu_dqblk = zerodqblk;
449 }
450 return (0);
451 }
452 #endif /* __APPLE */
453
454 /*
455 * Check to see if target appears in list of size cnt.
456 */
457 int
458 oneof(target, list, cnt)
459 register char *target, **list;
460 int cnt;
461 {
462 register int i;
463
464 for (i = 0; i < cnt; i++)
465 if (strcmp(target, list[i]) == 0)
466 return (i);
467 return (-1);
468 }
469
470 /*
471 * Check to see if a particular quota is to be enabled.
472 */
473 #ifdef __APPLE__
474 int
475 hasquota(fst, type, qfnamep)
476 register struct statfs *fst;
477 int type;
478 char **qfnamep;
479 {
480 struct stat sb;
481 static char initname, usrname[100], grpname[100];
482 static char buf[BUFSIZ];
483
484 if (!initname) {
485 snprintf(usrname, sizeof(usrname), "%s%s", qfextension[USRQUOTA], qfname);
486 snprintf(grpname, sizeof(grpname), "%s%s", qfextension[GRPQUOTA], qfname);
487 initname = 1;
488 }
489
490 /*
491 We only support the default path to the
492 on disk quota files.
493 */
494
495 (void)snprintf(buf, sizeof(buf), "%s/%s.%s", fst->f_mntonname,
496 QUOTAOPSNAME, qfextension[type] );
497 if (stat(buf, &sb) != 0) {
498 /* There appears to be no mount option file */
499 return(0);
500 }
501
502 (void) snprintf(buf, sizeof(buf), "%s/%s.%s", fst->f_mntonname, qfname, qfextension[type]);
503 *qfnamep = buf;
504 return (1);
505 }
506 #else
507 hasquota(fs, type, qfnamep)
508 register struct fstab *fs;
509 int type;
510 char **qfnamep;
511 {
512 register char *opt;
513 char *cp, *index(), *strtok();
514 static char initname, usrname[100], grpname[100];
515 static char buf[BUFSIZ];
516
517 if (!initname) {
518 snprintf(usrname, sizoef(usrname), "%s%s", qfextension[USRQUOTA], qfname);
519 snprintf(grpname, sizeof(grpname), "%s%s", qfextension[GRPQUOTA], qfname);
520 initname = 1;
521 }
522 strlcpy(buf, fs->fs_mntops, sizeof(buf));
523 for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) {
524 if (cp = index(opt, '='))
525 *cp++ = '\0';
526 if (type == USRQUOTA && strcmp(opt, usrname) == 0)
527 break;
528 if (type == GRPQUOTA && strcmp(opt, grpname) == 0)
529 break;
530 }
531 if (!opt)
532 return (0);
533 if (cp) {
534 *qfnamep = cp;
535 return (1);
536 }
537 (void) snprintf(buf, sizeof(buf), "%s/%s.%s", fs->fs_file, qfname, qfextension[type]);
538 *qfnamep = buf;
539 return (1);
540 }
541 #endif /* __APPLE__ */
542
543
544 #ifndef __APPLE__
545
546 /*
547 * Routines to manage the file usage table.
548 *
549 * Lookup an id of a specific type.
550 */
551 struct fileusage *
552 lookup(id, type)
553 u_long id;
554 int type;
555 {
556 register struct fileusage *fup;
557
558 for (fup = fuhead[type][id & (FUHASH-1)]; fup != 0; fup = fup->fu_next)
559 if (fup->fu_id == id)
560 return (fup);
561 return ((struct fileusage *)0);
562 }
563
564 /*
565 * Add a new file usage id if it does not already exist.
566 */
567 struct fileusage *
568 addid(id, type, name)
569 u_long id;
570 int type;
571 char *name;
572 {
573 struct fileusage *fup, **fhp;
574 int len;
575 extern char *calloc();
576
577 if (fup = lookup(id, type))
578 return (fup);
579 if (name)
580 len = strlen(name);
581 else
582 len = 10;
583 if ((fup = (struct fileusage *)calloc(1, sizeof(*fup) + len)) == NULL) {
584 fprintf(stderr, "out of memory for fileusage structures\n");
585 exit(1);
586 }
587 fhp = &fuhead[type][id & (FUHASH - 1)];
588 fup->fu_next = *fhp;
589 *fhp = fup;
590 fup->fu_id = id;
591 if (id > highid[type])
592 highid[type] = id;
593 if (name) {
594 bcopy(name, fup->fu_name, len + 1);
595 } else {
596 snprintf(fup->fu_name, len + 1, "%u", id);
597 }
598 return (fup);
599 }
600 #endif /* !__APPLE__ */
601
602 /*
603 * Calculate the grace period and return a printable string for it.
604 */
605 char *
606 timeprt(seconds)
607 time_t seconds;
608 {
609 time_t hours, minutes;
610 static char buf[20];
611 static time_t now;
612
613 if (now == 0)
614 time(&now);
615 if (now > seconds)
616 return ("none");
617 seconds -= now;
618 minutes = (seconds + 30) / 60;
619 hours = (minutes + 30) / 60;
620 if (hours >= 36) {
621 snprintf(buf, sizeof(buf), "%lddays", (hours + 12) / 24);
622 return (buf);
623 }
624 if (minutes >= 60) {
625 snprintf(buf, sizeof(buf), "%2ld:%ld", minutes / 60, minutes % 60);
626 return (buf);
627 }
628 snprintf(buf, sizeof(buf), "%2ld", minutes);
629 return (buf);
630 }