]> git.cameronkatri.com Git - apple_cmds.git/blob - diskdev_cmds/quotacheck.tproj/quotacheck.c
md5: Don't symlink non working bins, setuid appropriate bins
[apple_cmds.git] / diskdev_cmds / quotacheck.tproj / quotacheck.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
60 /*
61 * Fix up / report on disk quotas & usage
62 */
63 #include <sys/param.h>
64 #include <sys/stat.h>
65 #include <sys/queue.h>
66 #ifdef __APPLE__
67 #include <sys/mount.h>
68 #endif /* __APPLE__ */
69
70 #include <sys/quota.h>
71
72 #include <fcntl.h>
73 #include <fstab.h>
74 #include <pwd.h>
75 #include <grp.h>
76 #include <errno.h>
77 #include <unistd.h>
78 #include <stdio.h>
79 #include <stdlib.h>
80 #include <string.h>
81 #include <err.h>
82
83 #ifdef __APPLE__
84 #include <libkern/OSByteOrder.h>
85 #endif /* __APPLE__ */
86
87 #include "quotacheck.h"
88
89 char *qfname = QUOTAFILENAME;
90 char *qfextension[] = INITQFNAMES;
91 char *quotagroup = QUOTAGROUP;
92
93 #ifdef __APPLE__
94 u_int32_t quotamagic[MAXQUOTAS] = INITQMAGICS;
95 #endif /* __APPLE__ */
96
97
98 #define FUHASH 1024 /* must be power of two */
99 struct fileusage *fuhead[MAXQUOTAS][FUHASH];
100
101 int aflag; /* all file systems */
102 int gflag; /* check group quotas */
103 int uflag; /* check user quotas */
104 int vflag; /* verbose */
105
106 #ifdef __APPLE__
107 int maxentries; /* maximum entries in disk quota file */
108 #else
109 u_long highid[MAXQUOTAS]; /* highest addid()'ed identifier per type */
110 #endif /* __APPLE__ */
111
112
113 #ifdef __APPLE__
114 char * blockcheck(char *);
115 int chkquota(char *, char *, char *, struct quotaname *);
116 int getquotagid(void);
117 int hasquota(struct statfs *, int, char **);
118 struct fileusage *
119 lookup(u_long, int);
120 void * needchk(struct statfs *);
121 int oneof(char *, char*[], int);
122 int qfinsert(FILE *, struct dqblk *, int, int);
123 int qfmaxentries(char *, int);
124 void usage(void);
125 void dqbuftohost(struct dqblk *dqbp);
126
127 #else
128 struct fileusage *
129 addid __P((uid_t, int, char *));
130 char *blockcheck __P((char *));
131 int chkquota __P((char *, char *, struct quotaname *));
132 int getquotagid __P((void));
133 int hasquota __P((struct fstab *, int, char **));
134 struct fileusage *
135 lookup __P((u_long, int));
136 void *needchk __P((struct fstab *));
137 int oneof __P((char *, char*[], int));
138 int update __P((char *, char *, int));
139 void usage __P((void));
140 #endif /* __APPLE__ */
141
142
143 int
144 main(argc, argv)
145 int argc;
146 char *argv[];
147 {
148 #ifndef __APPLE__
149 register struct fstab *fs;
150 register struct passwd *pw;
151 register struct group *gr;
152 #endif /* !__APPLE__ */
153 struct quotaname *auxdata;
154 int i, argnum, maxrun, errs;
155 long done = 0;
156 char ch, *name;
157
158 #ifdef __APPLE__
159 int nfst;
160 struct statfs *fst;
161 #endif /* __APPLE__ */
162
163 errs = maxrun = 0;
164 while ((ch = getopt(argc, argv, "aguvl:")) != EOF) {
165 switch(ch) {
166 case 'a':
167 aflag++;
168 break;
169 case 'g':
170 gflag++;
171 break;
172 case 'u':
173 uflag++;
174 break;
175 case 'v':
176 vflag++;
177 break;
178 case 'l':
179 maxrun = atoi(optarg);
180 break;
181 default:
182 usage();
183 }
184 }
185 argc -= optind;
186 argv += optind;
187 if ((argc == 0 && !aflag) || (argc > 0 && aflag))
188 usage();
189 if (!gflag && !uflag) {
190 gflag++;
191 uflag++;
192 }
193
194 #ifdef __APPLE__
195 nfst = getmntinfo(&fst, MNT_WAIT);
196 if (nfst==0) {
197 fprintf(stderr, "quotacheck: no mounted filesystems\n");
198 exit(1);
199 }
200
201 for (i=0; i<nfst; i++) {
202 if(strcmp(fst[i].f_fstypename, "hfs")) {
203 continue;
204 }
205
206 if (aflag) {
207 if ((auxdata = needchk(&fst[i])) &&
208 (name = blockcheck(fst[i].f_mntfromname))) {
209 errs += chkquota(fst[i].f_fstypename, name, fst[i].f_mntonname, auxdata);
210 }
211
212 if (i+1 == nfst)
213 exit (errs);
214 else
215 continue;
216 }
217
218 if (((argnum = oneof(fst[i].f_mntonname, argv, argc)) >= 0 ||
219 (argnum = oneof(fst[i].f_mntfromname, argv, argc)) >= 0) &&
220 (auxdata = needchk(&fst[i])) &&
221 (name = blockcheck(fst[i].f_mntfromname))) {
222 done |= 1 << argnum;
223 errs += chkquota(fst[i].f_fstypename, name, fst[i].f_mntonname, auxdata);
224 }
225 } /* end for loop */
226
227 for (i = 0; i < argc; i++)
228 if ((done & (1 << i)) == 0)
229 fprintf(stderr, "%s not identified as a quota filesystem\n",
230 argv[i]);
231
232 exit(errs);
233 #else
234 if (gflag) {
235 setgrent();
236 while ((gr = getgrent()) != 0)
237 (void) addid((u_long)gr->gr_gid, GRPQUOTA, gr->gr_name);
238 endgrent();
239 }
240 if (uflag) {
241 setpwent();
242 while ((pw = getpwent()) != 0)
243 (void) addid((u_long)pw->pw_uid, USRQUOTA, pw->pw_name);
244 endpwent();
245 }
246 if (aflag)
247 exit(checkfstab(1, maxrun, needchk, chkquota));
248 if (setfsent() == 0)
249 err(1, "%s: can't open", FSTAB);
250 while ((fs = getfsent()) != NULL) {
251 if (((argnum = oneof(fs->fs_file, argv, argc)) >= 0 ||
252 (argnum = oneof(fs->fs_spec, argv, argc)) >= 0) &&
253 (auxdata = needchk(fs)) &&
254 (name = blockcheck(fs->fs_spec))) {
255 done |= 1 << argnum;
256 errs += chkquota(name, fs->fs_file, auxdata);
257 }
258 }
259 endfsent();
260 for (i = 0; i < argc; i++)
261 if ((done & (1 << i)) == 0)
262 fprintf(stderr, "%s not found in %s\n",
263 argv[i], FSTAB);
264 exit(errs);
265 #endif /* __APPLE__ */
266 }
267
268 void
269 usage()
270 {
271 (void)fprintf(stderr, "usage:\t%s\n\t%s\n",
272 "quotacheck -a [-guv]",
273 "quotacheck [-guv] filesys ...");
274 exit(1);
275 }
276
277 #ifdef __APPLE__
278 void *
279 needchk(fst)
280 struct statfs *fst;
281 {
282 register struct quotaname *qnp;
283 char *qfnp;
284
285 if(strcmp(fst->f_fstypename, "hfs")) {
286 return(NULL);
287 }
288 if(fst->f_flags & MNT_RDONLY)
289 return (NULL);
290 if ((qnp = malloc(sizeof(*qnp))) == NULL)
291 err(1, "%s", strerror(errno));
292 qnp->flags = 0;
293 if (gflag && hasquota(fst, GRPQUOTA, &qfnp)) {
294 strlcpy(qnp->grpqfname, qfnp, sizeof(qnp->grpqfname));
295 qnp->flags |= HASGRP;
296 }
297 if (uflag && hasquota(fst, USRQUOTA, &qfnp)) {
298 strlcpy(qnp->usrqfname, qfnp, sizeof(qnp->usrqfname));
299 qnp->flags |= HASUSR;
300 }
301 if (qnp->flags)
302 return (qnp);
303 free(qnp);
304 return (NULL);
305 }
306 #else
307 void *
308 needchk(fs)
309 register struct fstab *fs;
310 {
311 register struct quotaname *qnp;
312 char *qfnp;
313
314 if (strcmp(fs->fs_type, FSTAB_RW))
315 return (NULL);
316
317 if ((qnp = malloc(sizeof(*qnp))) == NULL)
318 err(1, "%s", strerror(errno));
319 qnp->flags = 0;
320 if (gflag && hasquota(fs, GRPQUOTA, &qfnp)) {
321 strlcpy(qnp->grpqfname, qfnp, sizeof(qnp->grpqfname));
322 qnp->flags |= HASGRP;
323 }
324 if (uflag && hasquota(fs, USRQUOTA, &qfnp)) {
325 strlcpy(qnp->usrqfname, qfnp, sizeof(qnp->usrqfname));
326 qnp->flags |= HASUSR;
327 }
328 if (qnp->flags)
329 return (qnp);
330 free(qnp);
331 return (NULL);
332 }
333 #endif /* __APPLE__ */
334
335 /*
336 * Scan the specified filesystem to check quota(s) present on it.
337 */
338 int
339 chkquota(fstype, fsname, mntpt, qnp)
340 char *fstype, *fsname, *mntpt;
341 register struct quotaname *qnp;
342 {
343 int errs = 1;
344
345 if (vflag) {
346 fprintf(stdout, "*** Checking ");
347 if (qnp->flags & HASUSR)
348 fprintf(stdout, "%s%s", qfextension[USRQUOTA],
349 (qnp->flags & HASGRP) ? " and " : "");
350 if (qnp->flags & HASGRP)
351 fprintf(stdout, "%s", qfextension[GRPQUOTA]);
352 fprintf(stdout, " quotas for %s (%s)\n", fsname, mntpt);
353 }
354
355 if(strcmp(fstype, "hfs") == 0)
356 errs = chkquota_hfs(fsname, mntpt, qnp);
357
358 return (errs);
359 }
360
361 /*
362 * Update a specified quota file.
363 */
364 #ifdef __APPLE__
365 int
366 update(fsname, quotafile, type)
367 char *fsname, *quotafile;
368 register int type;
369 {
370 register struct fileusage *fup;
371 register FILE *qfi, *qfo;
372 register u_long i;
373 struct dqblk dqbuf;
374 struct dqfilehdr dqhdr = {0};
375 int m, shift;
376 int idcnt = 0;
377 static int warned = 0;
378 static struct dqblk zerodqbuf;
379 static struct fileusage zerofileusage;
380
381 if ((qfo = fopen(quotafile, "r+")) == NULL) {
382 if (errno == ENOENT)
383 qfo = fopen(quotafile, "w+");
384 if (qfo) {
385 fprintf(stderr,
386 "quotacheck: creating quota file %s\n", quotafile);
387 #define MODE (S_IRUSR|S_IWUSR|S_IRGRP)
388 (void) fchown(fileno(qfo), getuid(), getquotagid());
389 (void) fchmod(fileno(qfo), MODE);
390 } else {
391 fprintf(stderr,
392 "quotacheck: %s: %s\n", quotafile, strerror(errno));
393 return (1);
394 }
395 }
396 if ((qfi = fopen(quotafile, "r")) == NULL) {
397 fprintf(stderr,
398 "quotacheck: %s: %s\n", quotafile, strerror(errno));
399 (void) fclose(qfo);
400 return (1);
401 }
402 if (quotactl(fsname, QCMD(Q_SYNC, type), 0, 0) < 0 &&
403 errno == ENOTSUP && !warned && vflag) {
404 warned++;
405 fprintf(stdout, "*** Warning: %s\n",
406 "Quotas are not compiled into this kernel");
407 }
408
409 /* Read in the quota file header. */
410 if (fread((char *)&dqhdr, sizeof(struct dqfilehdr), 1, qfi) > 0) {
411 /* Check for reverse endian file. */
412 if (OSSwapBigToHostInt32(dqhdr.dqh_magic) != quotamagic[type] &&
413 OSSwapInt32(dqhdr.dqh_magic) == quotamagic[type]) {
414 fprintf(stderr,
415 "quotacheck: %s: not in big endian byte order\n",
416 quotafile);
417 (void) fclose(qfo);
418 (void) fclose(qfi);
419 return (1);
420 }
421 /* Sanity check the quota file header. */
422 if ((OSSwapBigToHostInt32(dqhdr.dqh_magic) != quotamagic[type]) ||
423 (OSSwapBigToHostInt32(dqhdr.dqh_version) > QF_VERSION) ||
424 (!powerof2(OSSwapBigToHostInt32(dqhdr.dqh_maxentries)))) {
425 fprintf(stderr,
426 "quotacheck: %s: not a valid quota file\n",
427 quotafile);
428 (void) fclose(qfo);
429 (void) fclose(qfi);
430 return (1);
431 }
432 m = OSSwapBigToHostInt32(dqhdr.dqh_maxentries);
433 } else /* empty file */ {
434 if (maxentries)
435 m = maxentries;
436 else
437 m = qfmaxentries(fsname, type);
438
439 ftruncate(fileno(qfo), (off_t)((m + 1) * sizeof(struct dqblk)));
440
441 /* Initialize file header in big endian. */
442 dqhdr.dqh_magic = OSSwapHostToBigInt32(quotamagic[type]);
443 dqhdr.dqh_version = OSSwapHostToBigConstInt32(QF_VERSION);
444 dqhdr.dqh_maxentries = OSSwapHostToBigInt32(m);
445 dqhdr.dqh_btime = OSSwapHostToBigConstInt32(MAX_DQ_TIME);
446 dqhdr.dqh_itime = OSSwapHostToBigConstInt32(MAX_IQ_TIME);
447 memmove(dqhdr.dqh_string, QF_STRING_TAG, strlen(QF_STRING_TAG));
448 goto orphans; /* just insert all new records */
449 }
450
451 /* Examine all the entries in the quota file. */
452 for (i = 0; i < m; i++) {
453 if (fread((char *)&dqbuf, sizeof(struct dqblk), 1, qfi) == 0) {
454 fprintf(stderr,
455 "quotacheck: problem reading at index %ld\n", i);
456 continue;
457 }
458 if (dqbuf.dqb_id == 0)
459 continue;
460
461 ++idcnt;
462 if ((fup = lookup(OSSwapBigToHostInt32(dqbuf.dqb_id), type)) == 0)
463 fup = &zerofileusage;
464 else
465 fup->fu_checked = 1;
466
467 if (OSSwapBigToHostInt32(dqbuf.dqb_curinodes) == fup->fu_curinodes &&
468 OSSwapBigToHostInt64(dqbuf.dqb_curbytes) == fup->fu_curbytes) {
469 fup->fu_curinodes = 0;
470 fup->fu_curbytes = 0;
471 continue;
472 }
473 if (vflag) {
474 if (aflag)
475 fprintf(stdout, "%s: ", fsname);
476 fprintf(stdout, "%-12s fixed:", fup->fu_name);
477 if (OSSwapBigToHostInt32(dqbuf.dqb_curinodes) != fup->fu_curinodes)
478 fprintf(stdout, "\tinodes %u -> %u",
479 OSSwapBigToHostInt32(dqbuf.dqb_curinodes), fup->fu_curinodes);
480 if (OSSwapBigToHostInt64(dqbuf.dqb_curbytes) != fup->fu_curbytes)
481 fprintf(stdout, "\t1K blocks %qd -> %qd",
482 (OSSwapBigToHostInt64(dqbuf.dqb_curbytes)/1024), (fup->fu_curbytes/1024));
483 fprintf(stdout, "\n");
484 }
485 /*
486 * Reset time limit if have a soft limit and were
487 * previously under it, but are now over it.
488 */
489 if (dqbuf.dqb_bsoftlimit != 0 &&
490 OSSwapBigToHostInt64(dqbuf.dqb_curbytes) < OSSwapBigToHostInt64(dqbuf.dqb_bsoftlimit) &&
491 fup->fu_curbytes >= OSSwapBigToHostInt64(dqbuf.dqb_bsoftlimit))
492 dqbuf.dqb_btime = 0;
493 if (dqbuf.dqb_isoftlimit != 0 &&
494 OSSwapBigToHostInt32(dqbuf.dqb_curinodes) < OSSwapBigToHostInt32(dqbuf.dqb_isoftlimit) &&
495 fup->fu_curinodes >= OSSwapBigToHostInt32(dqbuf.dqb_isoftlimit))
496 dqbuf.dqb_itime = 0;
497 dqbuf.dqb_curinodes = OSSwapHostToBigInt32(fup->fu_curinodes);
498 dqbuf.dqb_curbytes = OSSwapHostToBigInt64(fup->fu_curbytes);
499
500 /* Write dqblk in big endian. */
501 fseek(qfo, dqoffset(i), SEEK_SET);
502 fwrite((char *)&dqbuf, sizeof(struct dqblk), 1, qfo);
503
504 dqbuftohost(&dqbuf);
505 (void) quotactl(fsname, QCMD(Q_SETUSE, type), dqbuf.dqb_id,
506 (caddr_t)&dqbuf);
507 fup->fu_curinodes = 0;
508 fup->fu_curbytes = 0;
509 }
510 orphans:
511 /* Look for any fileusage orphans */
512
513 shift = dqhashshift(m);
514 for (i = 0; i < FUHASH; ++i) {
515 for (fup = fuhead[type][i]; fup != 0; fup = fup->fu_next) {
516 if (fup->fu_checked || fup->fu_id == 0)
517 continue;
518 if (vflag) {
519 if (aflag)
520 fprintf(stdout, "%s: ", fsname);
521 fprintf(stdout,
522 "%-12s added:\tinodes %u\t1K blocks %qd\n",
523 fup->fu_name, fup->fu_curinodes,
524 (fup->fu_curbytes/1024));
525 }
526 /* Initialize dqbuf in big endian. */
527 dqbuf = zerodqbuf;
528 dqbuf.dqb_id = OSSwapHostToBigInt32(fup->fu_id);
529 dqbuf.dqb_curinodes = OSSwapHostToBigInt32(fup->fu_curinodes);
530 dqbuf.dqb_curbytes = OSSwapHostToBigInt64(fup->fu_curbytes);
531 /* insert this dqb */
532 if (qfinsert(qfo, &dqbuf, m, shift)) {
533 i = FUHASH;
534 break;
535 }
536
537 dqbuftohost(&dqbuf);
538 (void) quotactl(fsname, QCMD(Q_SETUSE, type),
539 dqbuf.dqb_id, (caddr_t)&dqbuf);
540 ++idcnt;
541 }
542 }
543
544 /* Write the quota file header */
545 dqhdr.dqh_entrycnt = OSSwapHostToBigInt32(idcnt);
546 fseek(qfo, (long)0, SEEK_SET);
547 fwrite((char *)&dqhdr, sizeof(struct dqfilehdr), 1, qfo);
548
549 fclose(qfi);
550 fflush(qfo);
551 fclose(qfo);
552 return (0);
553 }
554
555 /* Convert a dqblk to host native byte order. */
556 void
557 dqbuftohost(struct dqblk *dqbp)
558 {
559 dqbp->dqb_bhardlimit = OSSwapBigToHostInt64(dqbp->dqb_bhardlimit);
560 dqbp->dqb_bsoftlimit = OSSwapBigToHostInt64(dqbp->dqb_bsoftlimit);
561 dqbp->dqb_curbytes = OSSwapBigToHostInt64(dqbp->dqb_curbytes);
562 dqbp->dqb_ihardlimit = OSSwapBigToHostInt32(dqbp->dqb_ihardlimit);
563 dqbp->dqb_isoftlimit = OSSwapBigToHostInt32(dqbp->dqb_isoftlimit);
564 dqbp->dqb_curinodes = OSSwapBigToHostInt32(dqbp->dqb_curinodes);
565 dqbp->dqb_btime = OSSwapBigToHostInt32(dqbp->dqb_btime);
566 dqbp->dqb_itime = OSSwapBigToHostInt32(dqbp->dqb_itime);
567 dqbp->dqb_id = OSSwapBigToHostInt32(dqbp->dqb_id);
568 }
569
570 #else
571 int
572 update(fsname, quotafile, type)
573 char *fsname, *quotafile;
574 register int type;
575 {
576 register struct fileusage *fup;
577 register FILE *qfi, *qfo;
578 register u_long id, lastid;
579 struct dqblk dqbuf;
580 static int warned = 0;
581 static struct dqblk zerodqbuf;
582 static struct fileusage zerofileusage;
583
584 if ((qfo = fopen(quotafile, "r+")) == NULL) {
585 if (errno == ENOENT)
586 qfo = fopen(quotafile, "w+");
587 if (qfo) {
588 (void) fprintf(stderr,
589 "quotacheck: creating quota file %s\n", quotafile);
590 #define MODE (S_IRUSR|S_IWUSR|S_IRGRP)
591 (void) fchown(fileno(qfo), getuid(), getquotagid());
592 (void) fchmod(fileno(qfo), MODE);
593 } else {
594 (void) fprintf(stderr,
595 "quotacheck: %s: %s\n", quotafile, strerror(errno));
596 return (1);
597 }
598 }
599 if ((qfi = fopen(quotafile, "r")) == NULL) {
600 (void) fprintf(stderr,
601 "quotacheck: %s: %s\n", quotafile, strerror(errno));
602 (void) fclose(qfo);
603 return (1);
604 }
605 if (quotactl(fsname, QCMD(Q_SYNC, type), (u_long)0, (caddr_t)0) < 0 &&
606 errno == ENOTSUP && !warned && vflag) {
607 warned++;
608 (void)printf("*** Warning: %s\n",
609 "Quotas are not compiled into this kernel");
610 }
611 for (lastid = highid[type], id = 0; id <= lastid; id++) {
612 if (fread((char *)&dqbuf, sizeof(struct dqblk), 1, qfi) == 0)
613 dqbuf = zerodqbuf;
614 if ((fup = lookup(id, type)) == 0)
615 fup = &zerofileusage;
616 if (dqbuf.dqb_curinodes == fup->fu_curinodes &&
617 dqbuf.dqb_curblocks == fup->fu_curblocks) {
618 fup->fu_curinodes = 0;
619 fup->fu_curblocks = 0;
620 fseek(qfo, (long)sizeof(struct dqblk), 1);
621 continue;
622 }
623 if (vflag) {
624 if (aflag)
625 printf("%s: ", fsname);
626 printf("%-8s fixed:", fup->fu_name);
627 if (dqbuf.dqb_curinodes != fup->fu_curinodes)
628 (void)printf("\tinodes %d -> %d",
629 dqbuf.dqb_curinodes, fup->fu_curinodes);
630 if (dqbuf.dqb_curblocks != fup->fu_curblocks)
631 (void)printf("\tblocks %d -> %d",
632 dqbuf.dqb_curblocks, fup->fu_curblocks);
633 (void)printf("\n");
634 }
635 /*
636 * Reset time limit if have a soft limit and were
637 * previously under it, but are now over it.
638 */
639 if (dqbuf.dqb_bsoftlimit &&
640 dqbuf.dqb_curblocks < dqbuf.dqb_bsoftlimit &&
641 fup->fu_curblocks >= dqbuf.dqb_bsoftlimit)
642 dqbuf.dqb_btime = 0;
643 if (dqbuf.dqb_isoftlimit &&
644 dqbuf.dqb_curblocks < dqbuf.dqb_isoftlimit &&
645 fup->fu_curblocks >= dqbuf.dqb_isoftlimit)
646 dqbuf.dqb_itime = 0;
647 dqbuf.dqb_curinodes = fup->fu_curinodes;
648 dqbuf.dqb_curblocks = fup->fu_curblocks;
649 fwrite((char *)&dqbuf, sizeof(struct dqblk), 1, qfo);
650 (void) quotactl(fsname, QCMD(Q_SETUSE, type), id,
651 (caddr_t)&dqbuf);
652 fup->fu_curinodes = 0;
653 fup->fu_curblocks = 0;
654 }
655 fclose(qfi);
656 fflush(qfo);
657 ftruncate(fileno(qfo),
658 (off_t)((highid[type] + 1) * sizeof(struct dqblk)));
659 fclose(qfo);
660 return (0);
661 }
662 #endif /* __APPLE__ */
663
664 #ifdef __APPLE__
665 /*
666 * Insert an entry into a quota file.
667 *
668 * The dqblk pointed to by dqbp is in big endian.
669 */
670 int
671 qfinsert(file, dqbp, maxentries, shift)
672 FILE *file;
673 struct dqblk *dqbp;
674 int maxentries, shift;
675 {
676 struct dqblk dqbuf;
677 int i, skip, last;
678 u_int32_t mask;
679 u_int32_t id;
680 off_t offset;
681
682 id = OSSwapBigToHostInt32(dqbp->dqb_id);
683 if (id == 0)
684 return (0);
685 mask = maxentries - 1;
686 i = dqhash1(id, dqhashshift(maxentries), mask);
687 skip = dqhash2(id, mask);
688
689 for (last = (i + (maxentries-1) * skip) & mask;
690 i != last;
691 i = (i + skip) & mask) {
692 offset = dqoffset(i);
693 fseek(file, (long)offset, SEEK_SET);
694 if (fread((char *)&dqbuf, sizeof(struct dqblk), 1, file) == 0) {
695 fprintf(stderr, "quotacheck: read error at index %d\n", i);
696 return (EIO);
697 }
698 /*
699 * Stop when an empty entry is found
700 * or we encounter a matching id.
701 */
702 if (dqbuf.dqb_id == 0 || dqbuf.dqb_id == dqbp->dqb_id) {
703 dqbuf = *dqbp;
704 fseek(file, (long)offset, SEEK_SET);
705 fwrite((char *)&dqbuf, sizeof(struct dqblk), 1, file);
706 return (0);
707 }
708 }
709 fprintf(stderr, "quotacheck: exceeded maximum entries (%d)\n", maxentries);
710 return (ENOSPC);
711 }
712
713 #define ONEGIGABYTE (1024*1024*1024)
714
715 /*
716 * Calculate the size of the hash table from the size of
717 * the file system. The open addressing hashing used on
718 * the quota file assumes that this table will never be
719 * more than 90% full.
720 */
721 int
722 qfmaxentries(mntpt, type)
723 char *mntpt;
724 int type;
725 {
726 struct statfs fs_stat;
727 u_int64_t fs_size;
728 int max = 0;
729
730 if (statfs(mntpt, &fs_stat)) {
731 fprintf(stderr, "quotacheck: %s: %s\n",
732 mntpt, strerror(errno));
733 return (0);
734 }
735 fs_size = (u_int64_t)fs_stat.f_blocks * (u_int64_t)fs_stat.f_bsize;
736
737 if (type == USRQUOTA) {
738 max = QF_USERS_PER_GB * (fs_size / ONEGIGABYTE);
739
740 if (max < QF_MIN_USERS)
741 max = QF_MIN_USERS;
742 else if (max > QF_MAX_USERS)
743 max = QF_MAX_USERS;
744 } else if (type == GRPQUOTA) {
745 max = QF_GROUPS_PER_GB * (fs_size / ONEGIGABYTE);
746
747 if (max < QF_MIN_GROUPS)
748 max = QF_MIN_GROUPS;
749 else if (max > QF_MAX_GROUPS)
750 max = QF_MAX_GROUPS;
751 }
752 /* Round up to a power of 2 */
753 if (max && !powerof2(max)) {
754 int x = max;
755 max = 4;
756 while (x>>1 != 1) {
757 x = x >> 1;
758 max = max << 1;
759 }
760 }
761 return (max);
762 }
763 #endif /* __APPLE__ */
764
765 /*
766 * Check to see if target appears in list of size cnt.
767 */
768 int
769 oneof(target, list, cnt)
770 register char *target, *list[];
771 int cnt;
772 {
773 register int i;
774
775 for (i = 0; i < cnt; i++)
776 if (strcmp(target, list[i]) == 0)
777 return (i);
778 return (-1);
779 }
780
781 /*
782 * Determine the group identifier for quota files.
783 */
784 int
785 getquotagid()
786 {
787 struct group *gr;
788
789 if ((gr = getgrnam(quotagroup)))
790 return (gr->gr_gid);
791 return (-1);
792 }
793
794 /*
795 * Check to see if a particular quota is to be enabled.
796 */
797 #ifdef __APPLE__
798 int
799 hasquota(fst, type, qfnamep)
800 struct statfs *fst;
801 int type;
802 char **qfnamep;
803 {
804 struct stat sb;
805 static char initname, usrname[100], grpname[100];
806 static char buf[BUFSIZ];
807
808 if (!initname) {
809 (void)snprintf(usrname, sizeof(usrname),
810 "%s%s", qfextension[USRQUOTA], qfname);
811 (void)snprintf(grpname, sizeof(grpname),
812 "%s%s", qfextension[GRPQUOTA], qfname);
813 initname = 1;
814 }
815
816 /*
817 We only support the default path to the
818 on disk quota files.
819 */
820
821 (void)snprintf(buf, sizeof(buf), "%s/%s.%s", fst->f_mntonname,
822 QUOTAOPSNAME, qfextension[type] );
823 if (stat(buf, &sb) != 0) {
824 /* There appears to be no mount option file */
825 return(0);
826 }
827
828 (void)snprintf(buf, sizeof(buf),
829 "%s/%s.%s", fst->f_mntonname, qfname, qfextension[type]);
830 *qfnamep = buf;
831
832 return (1);
833 }
834 #else
835 int
836 hasquota(fs, type, qfnamep)
837 register struct fstab *fs;
838 int type;
839 char **qfnamep;
840 {
841 register char *opt;
842 char *cp;
843 static char initname, usrname[100], grpname[100];
844 static char buf[BUFSIZ];
845
846 if (!initname) {
847 (void)snprintf(usrname, sizeof(usrname),
848 "%s%s", qfextension[USRQUOTA], qfname);
849 (void)snprintf(grpname, sizeof(grpname),
850 "%s%s", qfextension[GRPQUOTA], qfname);
851 initname = 1;
852 }
853 strlcpy(buf, fs->fs_mntops, sizeof(buf));
854 for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) {
855 if (cp = strchr(opt, '='))
856 *cp++ = '\0';
857 if (type == USRQUOTA && strcmp(opt, usrname) == 0)
858 break;
859 if (type == GRPQUOTA && strcmp(opt, grpname) == 0)
860 break;
861 }
862 if (!opt)
863 return (0);
864 if (cp)
865 *qfnamep = cp;
866 else {
867 (void)snprintf(buf, sizeof(buf),
868 "%s/%s.%s", fs->fs_file, qfname, qfextension[type]);
869 *qfnamep = buf;
870 }
871 return (1);
872 }
873 #endif /* __APPLE__ */
874
875 /*
876 * Routines to manage the file usage table.
877 *
878 * Lookup an id of a specific type.
879 */
880 struct fileusage *
881 lookup(id, type)
882 u_long id;
883 int type;
884 {
885 register struct fileusage *fup;
886
887 for (fup = fuhead[type][id & (FUHASH-1)]; fup != 0; fup = fup->fu_next)
888 if (fup->fu_id == id)
889 return (fup);
890 return (NULL);
891 }
892
893 /*
894 * Add a new file usage id if it does not already exist.
895 */
896 #ifdef __APPLE__
897 struct fileusage *
898 addid(id, type)
899 uid_t id;
900 int type;
901 {
902 struct fileusage *fup, **fhp;
903 struct passwd *pw;
904 struct group *gr;
905 char *name;
906 size_t len;
907
908 if ((fup = lookup(id, type)))
909 return (fup);
910
911 name = NULL;
912 len = 10;
913 switch (type) {
914 case USRQUOTA:
915 if ((pw = getpwuid(id)) != 0) {
916 name = pw->pw_name;
917 len = strlen(name);
918 }
919 break;
920 case GRPQUOTA:
921 if ((gr = getgrgid(id)) != 0) {
922 name = gr->gr_name;
923 len = strlen(name);
924 }
925 break;
926 }
927
928 if ((fup = calloc(1, sizeof(*fup) + len)) == NULL)
929 err(1, "%s", strerror(errno));
930 fhp = &fuhead[type][id & (FUHASH - 1)];
931 fup->fu_next = *fhp;
932 *fhp = fup;
933 fup->fu_id = id;
934 if (name)
935 memmove(fup->fu_name, name, len + 1);
936 else
937 (void)snprintf(fup->fu_name, len + 1, "%u", (unsigned int)id);
938
939 return (fup);
940 }
941 #else
942 struct fileusage *
943 addid(id, type, name)
944 u_long id;
945 int type;
946 char *name;
947 {
948 struct fileusage *fup, **fhp;
949 int len;
950
951 if (fup = lookup(id, type))
952 return (fup);
953 if (name)
954 len = strlen(name);
955 else
956 len = 10;
957 if ((fup = calloc(1, sizeof(*fup) + len)) == NULL)
958 err(1, "%s", strerror(errno));
959 fhp = &fuhead[type][id & (FUHASH - 1)];
960 fup->fu_next = *fhp;
961 *fhp = fup;
962 fup->fu_id = id;
963 if (id > highid[type])
964 highid[type] = id;
965 if (name)
966 memmove(fup->fu_name, name, len + 1);
967 else
968 (void)snprintf(fup->fu_name, len + 1, "%u", id);
969 return (fup);
970 }
971 #endif /* __APPLE__ */
972