Add the source code for fdisk, quotacheck, and mount_pcfs to sbin
authorJulian Elischer <julian@dialix.oz.au>
Sun, 4 Apr 1993 00:00:00 +0000 (00:00 +0000)
committerJulian Elischer <julian@dialix.oz.au>
Sun, 4 Apr 1993 00:00:00 +0000 (00:00 +0000)
This patch adds the source code for the fdisk program that Julian
Elischer wrote.  Includes manual pages and lattest bug fixes as of 4/4/93.

This patch adds the source code for the quotacheck program from
the Net/2 release tape.  There are 2 uninitialized variables in the Net/2
code.  dev_bsize is initialized in the wrong place; this bug only shows
itself if quotacheck is run on more than one disk partition.

This patch adds the source code for the mount_pcfs program that
is part of the pcfs package by Pual Popelka.

AUTHOR: Julian Elischer (???) (fdisk)
AUTHOR: Rodney W. Grimes (rgrimes@agora.rain.com) (Net/2 quotacheck)
AUTHOR: Paul Popelka (paulp@uts.amdahl.com) (mount_pcfs)
AUTHOR: Kent Talarico (kent@shipwreck.tsoft.net) (Net/2 quotacheck fixes)
386BSD-Patchkit: patch00111

usr/src/sbin/Makefile
usr/src/sbin/fdisk/Makefile [new file with mode: 0644]
usr/src/sbin/fdisk/fdisk.8 [new file with mode: 0644]
usr/src/sbin/fdisk/fdisk.c [new file with mode: 0644]
usr/src/sbin/mount_pcfs/Makefile [new file with mode: 0644]
usr/src/sbin/mount_pcfs/mount_pcfs.c [new file with mode: 0644]
usr/src/sbin/quotacheck/Makefile [new file with mode: 0644]
usr/src/sbin/quotacheck/quotacheck.8 [new file with mode: 0644]
usr/src/sbin/quotacheck/quotacheck.c [new file with mode: 0644]

index ef2a675..73d3352 100644 (file)
@@ -1,8 +1,20 @@
 #      @(#)Makefile    5.4.1.1 (Berkeley) 5/7/91
 #      @(#)Makefile    5.4.1.1 (Berkeley) 5/7/91
+#
+# PATCHES MAGIC                LEVEL   PATCH THAT GOT US HERE
+# --------------------         -----   ----------------------
+# CURRENT PATCH LEVEL:         1       00111
+# --------------------         -----   ----------------------
+#
+# 04 Apr 93    Rodney W. Grimes        Add fdisk, mount_pcfs, quotacheck
+#                                      Comments about not ported and missing
+#
 
 
-SUBDIR=        badsect clri disklabel dmesg dump dumpfs fastboot fsck \
-       halt ifconfig init mknod mount mount_isofs mountd newfs \
-       nfsd nfsiod ping reboot restore route routed savecore \
+SUBDIR=        badsect clri disklabel dmesg dump dumpfs fastboot fdisk fsck \
+       halt ifconfig init mknod mount mount_isofs mount_pcfs mountd newfs \
+       nfsd nfsiod ping quotacheck reboot restore route routed savecore \
        shutdown slattach swapon tunefs umount
 
        shutdown slattach swapon tunefs umount
 
+# Not ported: XNSrouted enpload
+# Missing: icheck ncheck
+
 .include <bsd.subdir.mk>
 .include <bsd.subdir.mk>
diff --git a/usr/src/sbin/fdisk/Makefile b/usr/src/sbin/fdisk/Makefile
new file mode 100644 (file)
index 0000000..38e3fad
--- /dev/null
@@ -0,0 +1,9 @@
+#      @(#)Makefile    1.1 (Julian Elischer)   3/28/93
+#
+#
+
+PROG=  fdisk 
+SRCS=  fdisk.c
+MAN8=  fdisk.0
+
+.include <bsd.prog.mk>
diff --git a/usr/src/sbin/fdisk/fdisk.8 b/usr/src/sbin/fdisk/fdisk.8
new file mode 100644 (file)
index 0000000..078ae5e
--- /dev/null
@@ -0,0 +1,177 @@
+.Dd April 4, 1993
+.Dt FDISK 8
+.\".Os BSD 4
+.Sh NAME
+.Nm fdisk
+.Nd DOS partition maintainance program
+.Sh SYNOPSIS
+.Nm
+.Op Fl i
+.Op Fl u
+.Bl -tag -width time
+.It Fl i
+Initializes sector 0 of the disk.
+.It Fl u
+Is used for updating (editing) sector 0 of the disk.
+.El
+.Sh PROLOGUE
+In order for the BIOS to boot the kernel,
+certain conventions must be adhered to.
+Sector 0 of the disk must contain boot code,
+a partition table,
+and a magic number.
+BIOS partitions can be used to break the disk up into several pieces.
+The BIOS brings in sector 0
+(does it really use the code?)
+and verifies the magic number.
+It then searches the 4 BIOS partitions described by sector 0
+to determine which of them is
+.Em active.
+This boot then brings in the secondary boot block from the
+.Em active
+partition and runs it.
+Under DOS,
+you could have one or more partitions with one
+.Em active.
+The DOS
+.Nm
+program can be used to divide space on the disk into partitions and set one
+.Em active.
+.Sh DESCRIPTION
+The 386bsd program
+.Nm
+serves a similar purpose to the DOS program.
+When called with no arguments, it prints the sector 0 partition table.
+An example follows:
+
+.Bd -literal
+       ******* Working on device /dev/rwd0d *******
+       parameters extracted from in-core disklabel are:
+       cylinders=769 heads=15 sectors/track=33 (495 blks/cyl)
+
+       parameters to be used for BIOS calculations are:
+       cylinders=769 heads=15 sectors/track=33 (495 blks/cyl)
+               
+       Warning: BIOS sector numbering starts with sector 1
+       Information from DOS bootblock is:
+       The data for partition 0 is:
+       sysid 165,(386BSD)
+           start 495, size 380160 (185 Meg), flag 0
+               beg: cyl 1/ sector 1/ head 0;
+               end: cyl 768/ sector 33/ head 14
+       The data for partition 1 is:
+       sysid 164,(unknown)
+           start 378180, size 2475 (1 Meg), flag 0
+               beg: cyl 764/ sector 1/ head 0;
+               end: cyl 768/ sector 33/ head 14
+       The data for partition 2 is:
+       <UNUSED>
+       The data for partition 3 is:
+       sysid 99,(ISC UNIX, other System V/386, GNU HURD or Mach)
+           start 380656, size 224234 (109 Meg), flag 80
+               beg: cyl 769/ sector 2/ head 0;
+               end: cyl 197/ sector 33/ head 14
+.Ed
+.Pp
+The disk is divided into three parititions that happen to fill the disk.
+The second partition overlaps the end of the first.
+(Used for debugging purposes)
+.Bl -tag -width "cyl, sector and head"
+.It Em "sysid"
+is used to label the partition.  386bsd reserves the
+magic number 165 decimal (A5 in hex).
+.It Em "start and size"
+fields provide the start address
+and size of a parition in sectors.
+.It Em "flag 80"
+specifies that this is the active partition.
+.It Em "cyl, sector and head"
+fields are used to specify the beginning address
+and end address for the parititon.
+.It Em "Note:"
+these numbers are calculated using BIOS's understanding of the disk geometry
+and saved in the bootblock.
+.El
+.Pp
+The flags
+.Fl i
+or
+.Fl u
+are used to indicate that the paritition data is to be updated.
+The
+.Nm
+program will enter a conversational mode.
+This mode is designed not to change any data unless you explicitly tell it to.
+.Nm
+selects defaults for its questions to guarantee the above behaviour.
+.Pp
+It displays each partition
+and ask if you want to edit it.
+If you say yes,
+it will step through each field showing the old value
+and asking for a new one.
+When you are done with a partition,
+.Nm
+will display it and ask if it is correct.
+.Nm
+will then procede to the next entry.
+.Pp
+Getting the
+.Em cyl, sector,
+and
+.Em head
+fields correct is tricky.
+So by default,
+they will be calculated for you;
+you can specify them if you choose.
+.Pp
+After all the partitions are processed,
+you are given the option to change the
+.Em active
+partition.
+Finally,
+when the all the data for the first sector has been accumulated,
+you are asked if you really want to rewrite sector 0.
+Only if you answer yes,
+will the data be written to disk.
+.Pp
+The difference between the
+.Fl u
+flag and
+.Fl i
+flag is that
+the
+.Fl u
+flag just edits the fields as they appear on the disk.
+While the
+.Fl i
+flag is used to "initialize" sector 0;
+it will setup the last BIOS partition to use the whole disk for 386bsd;
+and make it active.
+.Sh NOTES
+.Pp
+The automatic calculation of starting cylinder etc. uses
+a set of figures that represent what the BIOS thinks is the
+geometry of the drive.
+These figures are by default taken from the incore disklabel,
+but the program initially gives you an oportunity to change them.
+This allows the user to create a bootblock that can work with drives
+that use geometry translation under the BIOS.
+.Pp
+If you hand craft your disk layout,
+please make sure that the 386bsd partition starts on a cylinder boundary.
+A number of decisions made later may assume this.
+(This might not be necessary later.)
+.Pp
+Editing an existing partition will most likely cause you to
+lose all the data in that partition.
+.Pp
+You should run this program interactively once or twice to see how it works.
+This is completely safe as long as you answer the last question in the negative.
+There are subtleties
+that the program detects
+that are not fully explained in this manual page.
+.Sh SEE ALSO
+.Xr disklabel 8
+.Sh BUGS
+One less now, but probably more
diff --git a/usr/src/sbin/fdisk/fdisk.c b/usr/src/sbin/fdisk/fdisk.c
new file mode 100644 (file)
index 0000000..7fb728d
--- /dev/null
@@ -0,0 +1,686 @@
+/* 
+ * Mach Operating System
+ * Copyright (c) 1992 Carnegie Mellon University
+ * All Rights Reserved.
+ * 
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ * 
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ * 
+ * Carnegie Mellon requests users of this software to return to
+ * 
+ *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
+ *  School of Computer Science
+ *  Carnegie Mellon University
+ *  Pittsburgh PA 15213-3890
+ * 
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+
+#include <sys/types.h>
+#include <sys/disklabel.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+
+int iotest;
+
+#define LBUF 100
+static char lbuf[LBUF];
+
+/*
+ *
+ * Ported to 386bsd by Julian Elischer  Thu Oct 15 20:26:46 PDT 1992
+ *
+ * 14-Dec-89  Robert Baron (rvb) at Carnegie-Mellon University
+ *     Copyright (c) 1989      Robert. V. Baron
+ *     Created.
+ */
+
+#define Decimal(str, ans, tmp) if (decimal(str, &tmp, ans)) ans = tmp
+#define Hex(str, ans, tmp) if (hex(str, &tmp, ans)) ans = tmp
+#define String(str, ans, len) {char *z = ans; char **dflt = &z; if (string(str, dflt)) strncpy(ans, *dflt, len); }
+
+#define RoundCyl(x) ((((x) + cylsecs - 1) / cylsecs) * cylsecs)
+
+#define SECSIZE 512
+
+char *disk = "/dev/rwd0d";
+char *name;
+
+struct disklabel disklabel;            /* disk parameters */
+
+int cyls, sectors, heads, cylsecs, disksecs;
+
+struct mboot
+{
+       unsigned char padding[2]; /* force the longs to be long alligned */
+       unsigned char bootinst[DOSPARTOFF];
+       struct  dos_partition parts[4];
+       unsigned short int      signature;
+};
+struct mboot mboot;
+
+#define ACTIVE 0x80
+#define BOOT_MAGIC 0xAA55
+
+int dos_cyls;
+int dos_heads;
+int dos_sectors;
+int dos_cylsecs;
+
+#define DOSSECT(s,c) ((s & 0x3f) | ((c >> 2) & 0xc0))
+#define DOSCYL(c)      (c & 0xff)
+static int dos();
+char *get_type();
+static int partition = -1;
+
+
+static int a_flag  = 0;                /* set active partition */
+static int i_flag  = 0;                /* replace partition data */
+static int u_flag  = 0;                /* update partition data */
+
+static unsigned char bootcode[] = {
+0x33, 0xc0, 0xfa, 0x8e, 0xd0, 0xbc, 0x00, 0x7c, 0x8e, 0xc0, 0x8e, 0xd8, 0xfb, 0x8b, 0xf4, 0xbf, 
+0x00, 0x06, 0xb9, 0x00, 0x02, 0xfc, 0xf3, 0xa4, 0xea, 0x1d, 0x06, 0x00, 0x00, 0xb0, 0x04, 0xbe, 
+0xbe, 0x07, 0x80, 0x3c, 0x80, 0x74, 0x0c, 0x83, 0xc6, 0x10, 0xfe, 0xc8, 0x75, 0xf4, 0xbe, 0xbd, 
+0x06, 0xeb, 0x43, 0x8b, 0xfe, 0x8b, 0x14, 0x8b, 0x4c, 0x02, 0x83, 0xc6, 0x10, 0xfe, 0xc8, 0x74, 
+0x0a, 0x80, 0x3c, 0x80, 0x75, 0xf4, 0xbe, 0xbd, 0x06, 0xeb, 0x2b, 0xbd, 0x05, 0x00, 0xbb, 0x00, 
+0x7c, 0xb8, 0x01, 0x02, 0xcd, 0x13, 0x73, 0x0c, 0x33, 0xc0, 0xcd, 0x13, 0x4d, 0x75, 0xef, 0xbe, 
+0x9e, 0x06, 0xeb, 0x12, 0x81, 0x3e, 0xfe, 0x7d, 0x55, 0xaa, 0x75, 0x07, 0x8b, 0xf7, 0xea, 0x00, 
+0x7c, 0x00, 0x00, 0xbe, 0x85, 0x06, 0x2e, 0xac, 0x0a, 0xc0, 0x74, 0x06, 0xb4, 0x0e, 0xcd, 0x10, 
+0xeb, 0xf4, 0xfb, 0xeb, 0xfe,
+'M', 'i', 's', 's', 'i', 'n', 'g', ' ',
+       'o', 'p', 'e', 'r', 'a', 't', 'i', 'n', 'g', ' ', 's', 'y', 's', 't', 'e', 'm', 0,
+'E', 'r', 'r', 'o', 'r', ' ', 'l', 'o', 'a', 'd', 'i', 'n', 'g', ' ', 
+       'o', 'p', 'e', 'r', 'a', 't', 'i', 'n', 'g', ' ', 's', 'y', 's', 't', 'e', 'm', 0,
+'I', 'n', 'v', 'a', 'l', 'i', 'd', ' ',
+       'p', 'a', 'r', 't', 'i', 't', 'i', 'o', 'n', ' ', 't', 'a', 'b', 'l', 'e', 0,
+'A', 'u', 't', 'h', 'o', 'r', ' ', '-', ' ',
+       'S', 'i', 'e', 'g', 'm', 'a', 'r', ' ', 'S', 'c', 'h', 'm', 'i', 'd', 't', 0,0,0, 
+
+  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
+  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
+  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
+  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
+  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
+  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
+  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
+  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
+  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
+  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
+  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
+  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
+  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0 
+};
+\f
+struct part_type
+{
+ unsigned char type;
+ char *name;
+}part_types[] =
+{
+        {0x00, "unused"}   
+       ,{0x01, "Primary DOS with 12 bit FAT"}   
+       ,{0x02, "XENIX / filesystem"}  
+       ,{0x03, "XENIX /usr filesystem"}  
+       ,{0x04, "Primary DOS with 16 bit FAT"}   
+       ,{0x05, "Extended DOS"}   
+       ,{0x06, "Primary 'big' DOS (> 32MB)"}   
+       ,{0x07, "OS/2 HPFS, QNX or Advanced UNIX"}  
+       ,{0x08, "AIX filesystem"}   
+       ,{0x09, "AIX boot partition or Coherent"}  
+       ,{0x0A, "OS/2 Boot Manager or OPUS"}  
+       ,{0x10, "OPUS"}   
+       ,{0x40, "VENIX 286"}  
+       ,{0x50, "DM"}   
+       ,{0x51, "DM"}   
+       ,{0x52, "CP/M or Microport SysV/AT"}  
+       ,{0x56, "GB"}   
+       ,{0x61, "Speed"}   
+       ,{0x63, "ISC UNIX, other System V/386, GNU HURD or Mach"}  
+       ,{0x64, "Novell Netware 2.xx"}      
+       ,{0x65, "Novell Netware 3.xx"} 
+       ,{0x75, "PCIX"}  
+       ,{0x80, "Minix 1.1 ... 1.4a"} 
+       ,{0x81, "Minix 1.4b ... 1.5.10"}   
+       ,{0x82, "Linux"}   
+       ,{0x93, "Amoeba filesystem"} 
+       ,{0x94, "Amoeba bad block table"} 
+       ,{0xA5, "386BSD"} 
+       ,{0xB7, "BSDI BSD/386 filesystem"} 
+       ,{0xB8, "BSDI BSD/386 swap"} 
+       ,{0xDB, "Concurrent CPM or C.DOS or CTOS"}  
+       ,{0xE1, "Speed"}   
+       ,{0xE3, "Speed"}   
+       ,{0xE4, "Speed"}   
+       ,{0xF1, "Speed"}   
+       ,{0xF2, "DOS 3.3+ Secondary"}   
+       ,{0xF4, "Speed"}   
+       ,{0xFF, "BBT (Bad Blocks Table)"}  
+};
+
+
+main(argc, argv)
+char **argv;
+{
+int    i;
+
+       name = *argv;
+       {register char *cp = name;
+               while (*cp) if (*cp++ == '/') name = cp;
+       }
+
+       for ( argv++ ; --argc ; argv++ ) { register char *token = *argv;
+               if (*token++ != '-' || !*token)
+                       break;
+               else { register int flag;
+                       for ( ; flag = *token++ ; ) {
+                               switch (flag) {
+                               case '0':
+                                       partition = 0;
+                                       break;
+                               case '1':
+                                       partition = 1;
+                                       break;
+                               case '2':
+                                       partition = 2;
+                                       break;
+                               case '3':
+                                       partition = 3;
+                                       break;
+                               case 'a':
+                                       a_flag = 1;
+                                       break;
+                               case 'i':
+                                       i_flag = 1;
+                               case 'u':
+                                       u_flag = 1;
+                                       break;
+                               default:
+                                       goto usage;
+                               }
+                       }
+               }
+       }
+
+       if (argc > 0)
+               disk = argv[0];
+       
+       if (open_disk(u_flag) < 0)
+               exit(1);
+
+       printf("******* Working on device %s *******\n",disk);
+       if(u_flag)
+       {
+               get_params_to_use();
+       }
+       else
+       {
+               print_params();
+       }
+
+       if (read_s0())
+               init_sector0(1);
+
+       printf("Warning: BIOS sector numbering starts with sector 1\n");
+       printf("Information from DOS bootblock is:\n");
+       if (partition == -1)
+               for (i = 0; i < NDOSPART; i++)
+                       change_part(i);
+       else
+               change_part(partition);
+
+       if (u_flag || a_flag)
+               change_active(partition);
+
+       if (u_flag || a_flag) {
+               printf("\nWe haven't changed the partition table yet.  ");
+               printf("This is your last chance.\n");
+               print_s0(-1);
+               if (ok("Should we write new partition table?"))
+                       write_s0();
+       }
+
+       exit(0);
+
+usage:
+       printf("fdisk {-a|-i|-r} {disk}\n");
+}
+
+print_s0(which)
+{
+int    i;
+
+       print_params();
+       printf("Information from DOS bootblock is:\n");
+       if (which == -1)
+               for (i = 0; i < NDOSPART; i++)
+                       printf("%d: ", i), print_part(i);
+       else
+               print_part(which);
+}
+
+static struct dos_partition mtpart = { 0 };
+
+print_part(i)
+{
+struct dos_partition *partp = ((struct dos_partition *) &mboot.parts) + i;
+
+
+       if (!bcmp(partp, &mtpart, sizeof (struct dos_partition))) {
+               printf("<UNUSED>\n");
+               return;
+       }
+       printf("sysid %d,(%s)\n", partp->dp_typ, get_type(partp->dp_typ));
+       printf("    start %d, size %d (%d Meg), flag %x\n",
+               partp->dp_start,
+               partp->dp_size, partp->dp_size * 512 / (1024 * 1024),
+               partp->dp_flag);
+       printf("\tbeg: cyl %d/ sector %d/ head %d;\n\tend: cyl %d/ sector %d/ head %d\n"
+               ,DPCYL(partp->dp_scyl, partp->dp_ssect)
+               ,DPSECT(partp->dp_ssect)
+               ,partp->dp_shd
+               ,DPCYL(partp->dp_ecyl, partp->dp_esect)
+               ,DPSECT(partp->dp_esect)
+               ,partp->dp_ehd);
+}
+
+init_sector0(start)
+{
+struct dos_partition *partp = (struct dos_partition *) (&mboot.parts[3]);
+int size = disksecs - start;
+int rest;
+
+       memcpy(mboot.bootinst, bootcode, sizeof(bootcode)); 
+       mboot.signature = BOOT_MAGIC;
+
+       partp->dp_typ = DOSPTYP_386BSD;
+       partp->dp_flag = ACTIVE;
+       partp->dp_start = start;
+       partp->dp_size = size;
+
+       dos(partp->dp_start, &partp->dp_scyl, &partp->dp_ssect, &partp->dp_shd);
+       dos(partp->dp_start+partp->dp_size, &partp->dp_ecyl, &partp->dp_esect, &partp->dp_ehd);
+}
+
+change_part(i)
+{
+struct dos_partition *partp = ((struct dos_partition *) &mboot.parts) + i;
+
+    printf("The data for partition %d is:\n", i);
+    print_part(i);
+
+    if (u_flag && ok("Do you want to change it?")) {
+       int tmp;
+
+       if (i_flag) {
+               bzero((char *)partp, sizeof (struct dos_partition));
+               if (i == 3) {
+                       init_sector0(1);
+                       printf("\nThe static data for the DOS partition 3 has been reinitialized to:\n");
+                       print_part(i);
+               }
+       }
+
+       do {
+               Decimal("sysid", partp->dp_typ, tmp);
+               Decimal("start", partp->dp_start, tmp);
+               Decimal("size", partp->dp_size, tmp);
+
+               if (ok("Explicitly specifiy beg/end address ?"))
+               {
+                       int     tsec,tcyl,thd;
+                       tcyl = DPCYL(partp->dp_scyl,partp->dp_ssect);
+                       thd = partp->dp_shd;
+                       tsec = DPSECT(partp->dp_ssect);
+                       Decimal("beginning cylinder", tcyl, tmp);
+                       Decimal("beginning head", thd, tmp);
+                       Decimal("beginning sector", tsec, tmp);
+                       partp->dp_scyl = DOSCYL(tcyl);
+                       partp->dp_ssect = DOSSECT(tsec,tcyl);
+                       partp->dp_shd = thd;
+
+                       tcyl = DPCYL(partp->dp_ecyl,partp->dp_esect);
+                       thd = partp->dp_ehd;
+                       tsec = DPSECT(partp->dp_esect);
+                       Decimal("ending cylinder", tcyl, tmp);
+                       Decimal("ending head", thd, tmp);
+                       Decimal("ending sector", tsec, tmp);
+                       partp->dp_ecyl = DOSCYL(tcyl);
+                       partp->dp_esect = DOSSECT(tsec,tcyl);
+                       partp->dp_ehd = thd;
+               } else {
+                       dos(partp->dp_start,
+                               &partp->dp_scyl, &partp->dp_ssect, &partp->dp_shd);
+                       dos(partp->dp_start+partp->dp_size - 1,
+                               &partp->dp_ecyl, &partp->dp_esect, &partp->dp_ehd);
+               }
+           
+               print_part(i);
+       } while (!ok("Are we happy with this entry?"));
+    }
+}
+
+print_params()
+{
+       printf("parameters extracted from in-core disklabel are:\n");
+       printf("cylinders=%d heads=%d sectors/track=%d (%d blks/cyl)\n\n"
+                       ,cyls,heads,sectors,cylsecs);
+       if((dos_sectors > 63) || (dos_cyls > 1023) || (dos_heads > 255))
+               printf(" Figures below won't work with BIOS for partitions not in cyl 1\n");
+       printf("parameters to be used for BIOS calculations are:\n");
+       printf("cylinders=%d heads=%d sectors/track=%d (%d blks/cyl)\n\n"
+               ,dos_cyls,dos_heads,dos_sectors,dos_cylsecs);
+}
+
+change_active(which)
+{
+int i;
+int active = 3, tmp;
+struct dos_partition *partp = ((struct dos_partition *) &mboot.parts);
+
+       if (a_flag && which != -1)
+               active = which;
+       if (ok("Do you want to change the active partition?")) {
+               do
+                       Decimal("active partition", active, tmp);
+               while(!ok("Are you happy with this choice"));
+       }
+       for (i = 0; i < NDOSPART; i++)
+               partp[i].dp_flag = 0;
+       partp[active].dp_flag = ACTIVE;
+}
+
+get_params_to_use()
+{
+       int     tmp;
+       print_params();
+       if (ok("Do you want to change our idea of what BIOS thinks ?"))
+       {
+               do
+               {
+                       Decimal("BIOS's idea of #cylinders", dos_cyls, tmp);
+                       Decimal("BIOS's idea of #heads", dos_heads, tmp);
+                       Decimal("BIOS's idea of #sectors", dos_sectors, tmp);
+                       dos_cylsecs = dos_heads * dos_sectors;
+                       print_params();
+               }
+               while(!ok("Are you happy with this choice"));
+       }
+}
+
+/***********************************************\
+* Change real numbers into strange dos numbers *
+\***********************************************/
+static
+dos(sec, c, s, h)
+int sec;
+unsigned char *c, *s, *h;
+{
+int cy;
+int hd;
+
+       cy = sec / ( dos_cylsecs );
+       sec = sec - cy * ( dos_cylsecs );
+
+       hd = sec / dos_sectors;
+       sec = (sec - hd * dos_sectors) + 1;
+
+       *h = hd;
+       *c = cy & 0xff;
+       *s = (sec & 0x3f) | ( (cy & 0x300) >> 2);
+}
+
+int fd;
+
+       /* Getting device status */
+
+open_disk(u_flag)
+{
+struct stat    st;
+
+       if (stat(disk, &st) == -1) {            
+               fprintf(stderr, "%s: Can't get file status of %s\n",
+                       name, disk);
+               return -1;
+       } else if ( !(st.st_mode & S_IFCHR) ) {
+               fprintf(stderr,"%s: Device %s is not character special\n",
+                       name, disk);
+               return -1;
+       }
+       if ((fd = open(disk, u_flag?O_RDWR:O_RDONLY)) == -1) {
+               fprintf(stderr,"%s: Can't open device %s\n", name, disk);
+               return -1;
+       }
+       if (get_params(0) == -1) {
+               fprintf(stderr, "%s: Can't get disk parameters on %s\n",
+                       name, disk);
+               return -1;
+       }
+       return fd;
+}
+
+
+read_disk(sector, buf)
+{
+       lseek(fd,(sector * 512), 0);
+       return read(fd, buf, 512);
+}
+
+write_disk(sector, buf)
+{
+       lseek(fd,(sector * 512), 0);
+       return write(fd, buf, 512);
+}
+
+get_params(verbose)
+{
+
+    if (ioctl(fd, DIOCGDINFO, &disklabel) == -1) {
+       return -1;
+    }
+
+    dos_cyls = cyls = disklabel.d_ncylinders;
+    dos_heads = heads = disklabel.d_ntracks;
+    dos_sectors = sectors = disklabel.d_nsectors;
+    dos_cylsecs = cylsecs = heads * sectors;
+    disksecs = cyls * heads * sectors;
+
+    return (disksecs);
+}
+\f
+
+read_s0()
+{
+       if (read_disk(0, (char *) mboot.bootinst) == -1) { 
+               fprintf(stderr, "%s: Can't read fdisk partition table\n", name);
+               return -1;
+       }
+       if (mboot.signature != BOOT_MAGIC) {
+               fprintf(stderr, "%s: Invalid fdisk partition table found\n",
+                       name);
+               /* So should we initialize things */
+               return -1;
+       }
+       return 0;
+}
+
+write_s0()
+{
+       int     flag;
+       if (iotest) {
+               print_s0(-1);
+               return 0;
+       }
+       /*
+        * write enable label sector before write (if necessary),
+        * disable after writing.
+        * needed if the disklabel protected area also protects
+        * sector 0. (e.g. empty disk)
+        */
+       flag = 1;
+       if (ioctl(fd, DIOCWLABEL, &flag) < 0)
+               perror("ioctl DIOCWLABEL");
+       if (write_disk(0, (char *) mboot.bootinst) == -1) { 
+               fprintf(stderr, "%s: Can't write fdisk partition table\n",
+                       name);
+               return -1;
+       flag = 0;
+       (void) ioctl(fd, DIOCWLABEL, &flag);
+       }
+}
+
+
+
+ok(str)
+char *str;
+{
+       printf("%s [n] ", str);
+       fgets(lbuf, LBUF, stdin);
+       lbuf[strlen(lbuf)-1] = 0;
+
+       if (*lbuf &&
+               (!strcmp(lbuf, "yes") || !strcmp(lbuf, "YES") ||
+                !strcmp(lbuf, "y") || !strcmp(lbuf, "Y")))
+               return 1;
+       else
+               return 0;
+}
+
+decimal(str, num, deflt)
+char *str;
+int *num;
+{
+int acc = 0, c;
+char *cp;
+
+       while (1) {
+               printf("Supply a decimal value for \"%s\" [%d] ", str, deflt);
+               fgets(lbuf, LBUF, stdin);
+               lbuf[strlen(lbuf)-1] = 0;
+
+               if (!*lbuf)
+                       return 0;
+
+               cp = lbuf;
+               while ((c = *cp) && (c == ' ' || c == '\t')) cp++;
+               if (!c)
+                       return 0;
+               while (c = *cp++) {
+                       if (c <= '9' && c >= '0')
+                               acc = acc * 10 + c - '0';
+                       else
+                               break;
+               }
+               if (c == ' ' || c == '\t')
+                       while ((c = *cp) && (c == ' ' || c == '\t')) cp++;
+               if (!c) {
+                       *num = acc;
+                       return 1;
+               } else
+                       printf("%s is an invalid decimal number.  Try again\n",
+                               lbuf);
+       }
+
+}
+
+hex(str, num, deflt)
+char *str;
+int *num;
+{
+int acc = 0, c;
+char *cp;
+
+       while (1) {
+               printf("Supply a hex value for \"%s\" [%x] ", str, deflt);
+               fgets(lbuf, LBUF, stdin);
+               lbuf[strlen(lbuf)-1] = 0;
+
+               if (!*lbuf)
+                       return 0;
+
+               cp = lbuf;
+               while ((c = *cp) && (c == ' ' || c == '\t')) cp++;
+               if (!c)
+                       return 0;
+               while (c = *cp++) {
+                       if (c <= '9' && c >= '0')
+                               acc = (acc << 4) + c - '0';
+                       else if (c <= 'f' && c >= 'a')
+                               acc = (acc << 4) + c - 'a' + 10;
+                       else if (c <= 'F' && c >= 'A')
+                               acc = (acc << 4) + c - 'A' + 10;
+                       else
+                               break;
+               }
+               if (c == ' ' || c == '\t')
+                       while ((c = *cp) && (c == ' ' || c == '\t')) cp++;
+               if (!c) {
+                       *num = acc;
+                       return 1;
+               } else
+                       printf("%s is an invalid hex number.  Try again\n",
+                               lbuf);
+       }
+
+}
+
+string(str, ans)
+char *str;
+char **ans;
+{
+int c;
+char *cp = lbuf;
+
+       while (1) {
+               printf("Supply a string value for \"%s\" [%s] ", str, *ans);
+               fgets(lbuf, LBUF, stdin);
+               lbuf[strlen(lbuf)-1] = 0;
+
+               if (!*lbuf)
+                       return 0;
+
+               while ((c = *cp) && (c == ' ' || c == '\t')) cp++;
+               if (c == '"') {
+                       c = *++cp;
+                       *ans = cp;
+                       while ((c = *cp) && c != '"') cp++;
+               } else {
+                       *ans = cp;
+                       while ((c = *cp) && c != ' ' && c != '\t') cp++;
+               }
+
+               if (c)
+                       *cp = 0;
+               return 1;
+       }
+}
+
+char *get_type(type)
+int    type;
+{
+       int     numentries = (sizeof(part_types)/sizeof(struct part_type));
+       int     counter = 0;
+       struct  part_type *ptr = part_types;
+
+       
+       while(counter < numentries)
+       {
+               if(ptr->type == type)
+               {
+                       return(ptr->name);
+               }
+               ptr++;
+               counter++;
+       }
+       return("unknown");
+}
diff --git a/usr/src/sbin/mount_pcfs/Makefile b/usr/src/sbin/mount_pcfs/Makefile
new file mode 100644 (file)
index 0000000..ea9fe0f
--- /dev/null
@@ -0,0 +1,5 @@
+PROG = mount_pcfs
+CFLAGS += -DPCFS
+NOMAN = noman
+
+.include <bsd.prog.mk>
diff --git a/usr/src/sbin/mount_pcfs/mount_pcfs.c b/usr/src/sbin/mount_pcfs/mount_pcfs.c
new file mode 100644 (file)
index 0000000..847ff69
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * 06 Apr 93, Rodney W. Grimes, changed MOUNT_PCFS to MOUNT_MSDOS, whole
+ * thing should be renamed msdosfs...
+ *
+ * No copyright as supplied to the patchkit???
+ * Who wrote this???
+ *
+ */
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/mount.h>
+
+char *progname;
+
+void
+usage ()
+{
+       fprintf (stderr, "usage: %s bdev dir\n", progname);
+       exit (1);
+}
+               
+int
+main (argc, argv)
+int argc;
+char **argv;
+{
+       char *dev;
+       char *dir;
+       struct pcfs_args args;
+       int c;
+       extern char *optarg;
+       extern int optind;
+       int opts;
+
+       progname = argv[0];
+
+       opts = 0;
+
+       while ((c = getopt (argc, argv, "F:")) != EOF) {
+               switch (c) {
+               case 'F':
+                       opts |= atoi (optarg);
+                       break;
+               default:
+                       usage ();
+               }
+       }
+
+       if (optind + 2 != argc)
+               usage ();
+
+       dev = argv[optind];
+       dir = argv[optind + 1];
+
+       args.fspec = dev;
+       args.exflags = 0;
+       args.exroot = 0;
+
+       if (mount (MOUNT_MSDOS, dir, opts, &args) < 0) {
+               perror ("mount");
+               exit (1);
+       }
+
+       exit (0);
+}
diff --git a/usr/src/sbin/quotacheck/Makefile b/usr/src/sbin/quotacheck/Makefile
new file mode 100644 (file)
index 0000000..fa74932
--- /dev/null
@@ -0,0 +1,8 @@
+#      @(#)Makefile    5.3 (Berkeley) 5/11/90
+
+PROG=  quotacheck
+SRCS=  quotacheck.c preen.c
+MAN8=  quotacheck.0
+.PATH: ${.CURDIR}/../fsck
+
+.include <bsd.prog.mk>
diff --git a/usr/src/sbin/quotacheck/quotacheck.8 b/usr/src/sbin/quotacheck/quotacheck.8
new file mode 100644 (file)
index 0000000..dca7216
--- /dev/null
@@ -0,0 +1,157 @@
+.\" Copyright (c) 1983, 1990, 1991 Regents of the University of California.
+.\" All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" Robert Elz at The University of Melbourne.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\"    must display the following acknowledgement:
+.\"    This product includes software developed by the University of
+.\"    California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\"    may be used to endorse or promote products derived from this software
+.\"    without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\"     @(#)quotacheck.8       6.7 (Berkeley) 3/16/91
+.\"
+.Dd March 16, 1991
+.Dt QUOTACHECK 8
+.Os BSD 4.2
+.Sh NAME
+.Nm quotacheck
+.Nd filesystem quota consistency checker
+.Sh SYNOPSIS
+.Nm quotacheck
+.Op Fl g
+.Op Fl u
+.Op Fl v
+.Ar filesystem Ar ...
+.Nm quotacheck
+.Op Fl g
+.Op Fl u
+.Op Fl v
+.Fl a
+.Sh DESCRIPTION
+.Nm Quotacheck
+examines each filesystem,
+builds a table of current disk usage,
+and compares this table against that recorded
+in the disk quota file for the filesystem.
+If any inconsistencies are detected, both the
+quota file and the current system copy of the
+incorrect quotas are updated (the latter only
+occurs if an active filesystem is checked).
+By default both user and group quotas are checked.
+.Pp
+Available options:
+.Bl -tag -width Ds
+.It Fl a
+If the
+.Fl a
+flag is supplied in place of any filesystem names,
+.Nm quotacheck
+will check all the filesystems indicated in
+.Pa /etc/fstab
+to be read-write with disk quotas.
+By default only the types of quotas listed in
+.Pa /etc/fstab
+are checked.
+.It Fl g
+Only group quotas listed in
+.Pa /etc/fstab
+are to be checked.
+.It Fl u
+Only user quotas listed in
+.Pa /etc/fstab
+are to be checked.
+.It Fl v
+.Nm quotacheck
+reports discrepancies between the
+calculated and recorded disk quotas.
+.El
+.Pp
+Specifying both
+.Fl g
+and
+.Fl u
+is equivalent to the default.
+Parallel passes are run on the filesystems required,
+using the pass numbers in
+.Pa /etc/fstab
+in an identical fashion to
+.Xr fsck 8 .
+.Pp
+Normally
+.Nm quotacheck
+operates silently.
+.Pp
+.Nm Quotacheck
+expects each filesystem to be checked to have a
+quota files named
+.Pa quota.user
+and
+.Pa quota.group
+which are located at the root of the associated file system.
+These defaults may be overridden in
+.Pa /etc/fstab .
+If a file is not present, 
+.Nm quotacheck
+will create it.
+.Pp
+.Nm Quotacheck
+is normally run at boot time from the
+.Pa /etc/rc.local
+file, see
+.Xr rc 8 ,
+before enabling disk quotas with
+.Xr quotaon 8 .
+.Pp
+.Nm Quotacheck
+accesses the raw device in calculating the actual
+disk usage for each user.
+Thus, the filesystems
+checked should be quiescent while
+.Nm quotacheck
+is running.
+.Sh FILES
+.Bl -tag -width quota.group -compact
+.It Pa quota.user
+at the filesystem root with user quotas
+.It Pa quota.group
+at the filesystem root with group quotas
+.It Pa /etc/fstab
+default filesystems
+.El
+.Sh SEE ALSO
+.Xr quota 1 ,
+.Xr quotactl 2 ,
+.Xr fstab 5 ,
+.Xr edquota 8 ,
+.Xr fsck 8 ,
+.Xr quotaon 8 ,
+.Xr repquota 8
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.2 .
diff --git a/usr/src/sbin/quotacheck/quotacheck.c b/usr/src/sbin/quotacheck/quotacheck.c
new file mode 100644 (file)
index 0000000..5d6dafa
--- /dev/null
@@ -0,0 +1,595 @@
+/*
+ * Copyright (c) 1980, 1990 Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Robert Elz at The University of Melbourne.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+char copyright[] =
+"@(#) Copyright (c) 1980, 1990 Regents of the University of California.\n\
+ All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)quotacheck.c       5.16 (Berkeley) 2/28/91";
+#endif /* not lint */
+
+/*
+ * Fix up / report on disk quotas & usage
+ */
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <ufs/dinode.h>
+#include <ufs/fs.h>
+#include <ufs/quota.h>
+#include <fcntl.h>
+#include <fstab.h>
+#include <pwd.h>
+#include <grp.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+char *qfname = QUOTAFILENAME;
+char *qfextension[] = INITQFNAMES;
+char *quotagroup = QUOTAGROUP;
+
+union {
+       struct  fs      sblk;
+       char    dummy[MAXBSIZE];
+} un;
+#define        sblock  un.sblk
+long dev_bsize;
+long maxino;
+
+struct quotaname {
+       long    flags;
+       char    grpqfname[MAXPATHLEN + 1];
+       char    usrqfname[MAXPATHLEN + 1];
+};
+#define        HASUSR  1
+#define        HASGRP  2
+
+struct fileusage {
+       struct  fileusage *fu_next;
+       u_long  fu_curinodes;
+       u_long  fu_curblocks;
+       u_long  fu_id;
+       char    fu_name[1];
+       /* actually bigger */
+};
+#define FUHASH 1024    /* must be power of two */
+struct fileusage *fuhead[MAXQUOTAS][FUHASH];
+struct fileusage *lookup();
+struct fileusage *addid();
+struct dinode *getnextinode();
+
+int    aflag;                  /* all file systems */
+int    gflag;                  /* check group quotas */
+int    uflag;                  /* check user quotas */
+int    vflag;                  /* verbose */
+int    fi;                     /* open disk file descriptor */
+u_long highid[MAXQUOTAS];      /* highest addid()'ed identifier per type */
+
+main(argc, argv)
+       int argc;
+       char **argv;
+{
+       register struct fstab *fs;
+       register struct passwd *pw;
+       register struct group *gr;
+       int i, argnum, maxrun = 0, errs = 0;
+       long auxdata, done = 0;
+       char ch, *name, *blockcheck();
+       int needchk(), chkquota();
+       extern char *optarg;
+       extern int optind;
+
+       while ((ch = getopt(argc, argv, "aguvl:")) != EOF) {
+               switch(ch) {
+               case 'a':
+                       aflag++;
+                       break;
+               case 'g':
+                       gflag++;
+                       break;
+               case 'u':
+                       uflag++;
+                       break;
+               case 'v':
+                       vflag++;
+                       break;
+               case 'l':
+                       maxrun = atoi(optarg);
+                       break;
+               default:
+                       usage();
+               }
+       }
+       argc -= optind;
+       argv += optind;
+       if ((argc == 0 && !aflag) || (argc > 0 && aflag))
+               usage();
+       if (!gflag && !uflag) {
+               gflag++;
+               uflag++;
+       }
+       if (gflag) {
+               setgrent();
+               while ((gr = getgrent()) != 0)
+                       (void) addid((u_long)gr->gr_gid, GRPQUOTA, gr->gr_name);
+               endgrent();
+       }
+       if (uflag) {
+               setpwent();
+               while ((pw = getpwent()) != 0)
+                       (void) addid((u_long)pw->pw_uid, USRQUOTA, pw->pw_name);
+               endpwent();
+       }
+       if (aflag)
+               exit(checkfstab(1, maxrun, needchk, chkquota));
+       if (setfsent() == 0) {
+               fprintf(stderr, "Can't open ");
+               perror(FSTAB);
+               exit(8);
+       }
+       while ((fs = getfsent()) != NULL) {
+               if (((argnum = oneof(fs->fs_file, argv, argc)) >= 0 ||
+                   (argnum = oneof(fs->fs_spec, argv, argc)) >= 0) &&
+                   (auxdata = needchk(fs)) &&
+                   (name = blockcheck(fs->fs_spec))) {
+                       done |= 1 << argnum;
+                       errs += chkquota(name, fs->fs_file, auxdata);
+               }
+       }
+       endfsent();
+       for (i = 0; i < argc; i++)
+               if ((done & (1 << i)) == 0)
+                       fprintf(stderr, "%s not found in %s\n",
+                               argv[i], FSTAB);
+       exit(errs);
+}
+
+usage()
+{
+       (void) fprintf(stderr, "usage:\t%s\n\t%s\n",
+               "quotacheck -a [-guv]",
+               "quotacheck [-guv] filesys ...");
+       exit(1);
+}
+
+needchk(fs)
+       register struct fstab *fs;
+{
+       register struct quotaname *qnp;
+       char *qfnp;
+
+       if (strcmp(fs->fs_vfstype, "ufs") ||
+           strcmp(fs->fs_type, FSTAB_RW))
+               return (0);
+       if ((qnp = (struct quotaname *)malloc(sizeof *qnp)) == 0) {
+               fprintf(stderr, "out of memory for quota structures\n");
+               exit(1);
+       }
+       qnp->flags = 0;
+       if (gflag && hasquota(fs, GRPQUOTA, &qfnp)) {
+               strcpy(qnp->grpqfname, qfnp);
+               qnp->flags |= HASGRP;
+       }
+       if (uflag && hasquota(fs, USRQUOTA, &qfnp)) {
+               strcpy(qnp->usrqfname, qfnp);
+               qnp->flags |= HASUSR;
+       }
+       if (qnp->flags)
+               return ((int)qnp);
+       free((char *)qnp);
+       return (0);
+}
+
+/*
+ * Scan the specified filesystem to check quota(s) present on it.
+ */
+chkquota(fsname, mntpt, qnp)
+       char *fsname, *mntpt;
+       register struct quotaname *qnp;
+{
+       register struct fileusage *fup;
+       register struct dinode *dp;
+       int cg, i, mode, errs = 0;
+       ino_t ino;
+
+       if ((fi = open(fsname, 0)) < 0) {
+               perror(fsname);
+               return (1);
+       }
+       if (vflag) {
+               fprintf(stdout, "*** Checking ");
+               if (qnp->flags & HASUSR)
+                       fprintf(stdout, "%s%s", qfextension[USRQUOTA],
+                           (qnp->flags & HASGRP) ? " and " : "");
+               if (qnp->flags & HASGRP)
+                       fprintf(stdout, "%s", qfextension[GRPQUOTA]);
+               fprintf(stdout, " quotas for %s (%s)\n", fsname, mntpt);
+       }
+       sync();
+       dev_bsize = 1;
+       bread(SBOFF, (char *)&sblock, (long)SBSIZE);
+       dev_bsize = sblock.fs_fsize / fsbtodb(&sblock, 1);
+       maxino = sblock.fs_ncg * sblock.fs_ipg;
+       resetinodebuf();
+       for (ino = 0, cg = 0; cg < sblock.fs_ncg; cg++) {
+               for (i = 0; i < sblock.fs_ipg; i++, ino++) {
+                       if (ino < ROOTINO)
+                               continue;
+                       if ((dp = getnextinode(ino)) == NULL)
+                               continue;
+                       if ((mode = dp->di_mode & IFMT) == 0)
+                               continue;
+                       if (qnp->flags & HASGRP) {
+                               fup = addid((u_long)dp->di_gid, GRPQUOTA,
+                                   (char *)0);
+                               fup->fu_curinodes++;
+                               if (mode == IFREG || mode == IFDIR ||
+                                   mode == IFLNK)
+                                       fup->fu_curblocks += dp->di_blocks;
+                       }
+                       if (qnp->flags & HASUSR) {
+                               fup = addid((u_long)dp->di_uid, USRQUOTA,
+                                   (char *)0);
+                               fup->fu_curinodes++;
+                               if (mode == IFREG || mode == IFDIR ||
+                                   mode == IFLNK)
+                                       fup->fu_curblocks += dp->di_blocks;
+                       }
+               }
+       }
+       freeinodebuf();
+       if (qnp->flags & HASUSR)
+               errs += update(mntpt, qnp->usrqfname, USRQUOTA);
+       if (qnp->flags & HASGRP)
+               errs += update(mntpt, qnp->grpqfname, GRPQUOTA);
+       close(fi);
+       return (errs);
+}
+
+/*
+ * Update a specified quota file.
+ */
+update(fsname, quotafile, type)
+       char *fsname, *quotafile;
+       register int type;
+{
+       register struct fileusage *fup;
+       register FILE *qfi, *qfo;
+       register u_long id, lastid;
+       struct dqblk dqbuf;
+       static int warned = 0;
+       static struct dqblk zerodqbuf;
+       static struct fileusage zerofileusage;
+
+       if ((qfo = fopen(quotafile, "r+")) == NULL) {
+               if (errno == ENOENT)
+                       qfo = fopen(quotafile, "w+");
+               if (qfo) {
+                       (void) fprintf(stderr,
+                           "quotacheck: creating quota file %s\n", quotafile);
+#define        MODE    (S_IRUSR|S_IWUSR|S_IRGRP)
+                       (void) fchown(fileno(qfo), getuid(), getquotagid());
+                       (void) fchmod(fileno(qfo), MODE);
+               } else {
+                       (void) fprintf(stderr,
+                           "quotacheck: %s: %s\n", quotafile, strerror(errno));
+                       return (1);
+               }
+       }
+       if ((qfi = fopen(quotafile, "r")) == NULL) {
+               (void) fprintf(stderr,
+                   "quotacheck: %s: %s\n", quotafile, strerror(errno));
+               (void) fclose(qfo);
+               return (1);
+       }
+       if (quotactl(fsname, QCMD(Q_SYNC, type), (u_long)0, (caddr_t)0) < 0 &&
+           errno == EOPNOTSUPP && !warned && vflag) {
+               warned++;
+               fprintf(stdout, "*** Warning: %s\n",
+                   "Quotas are not compiled into this kernel");
+       }
+       for (lastid = highid[type], id = 0; id <= lastid; id++) {
+               if (fread((char *)&dqbuf, sizeof(struct dqblk), 1, qfi) == 0)
+                       dqbuf = zerodqbuf;
+               if ((fup = lookup(id, type)) == 0)
+                       fup = &zerofileusage;
+               if (dqbuf.dqb_curinodes == fup->fu_curinodes &&
+                   dqbuf.dqb_curblocks == fup->fu_curblocks) {
+                       fup->fu_curinodes = 0;
+                       fup->fu_curblocks = 0;
+                       fseek(qfo, (long)sizeof(struct dqblk), 1);
+                       continue;
+               }
+               if (vflag) {
+                       if (aflag)
+                               printf("%s: ", fsname);
+                       printf("%-8s fixed:", fup->fu_name);
+                       if (dqbuf.dqb_curinodes != fup->fu_curinodes)
+                               fprintf(stdout, "\tinodes %d -> %d",
+                                       dqbuf.dqb_curinodes, fup->fu_curinodes);
+                       if (dqbuf.dqb_curblocks != fup->fu_curblocks)
+                               fprintf(stdout, "\tblocks %d -> %d",
+                                       dqbuf.dqb_curblocks, fup->fu_curblocks);
+                       fprintf(stdout, "\n");
+               }
+               /*
+                * Reset time limit if have a soft limit and were
+                * previously under it, but are now over it.
+                */
+               if (dqbuf.dqb_bsoftlimit &&
+                   dqbuf.dqb_curblocks < dqbuf.dqb_bsoftlimit &&
+                   fup->fu_curblocks >= dqbuf.dqb_bsoftlimit)
+                       dqbuf.dqb_btime = 0;
+               if (dqbuf.dqb_isoftlimit &&
+                   dqbuf.dqb_curblocks < dqbuf.dqb_isoftlimit &&
+                   fup->fu_curblocks >= dqbuf.dqb_isoftlimit)
+                       dqbuf.dqb_itime = 0;
+               dqbuf.dqb_curinodes = fup->fu_curinodes;
+               dqbuf.dqb_curblocks = fup->fu_curblocks;
+               fwrite((char *)&dqbuf, sizeof(struct dqblk), 1, qfo);
+               (void) quotactl(fsname, QCMD(Q_SETUSE, type), id,
+                   (caddr_t)&dqbuf);
+               fup->fu_curinodes = 0;
+               fup->fu_curblocks = 0;
+       }
+       fclose(qfi);
+       fflush(qfo);
+       ftruncate(fileno(qfo),
+           (off_t)((highid[type] + 1) * sizeof(struct dqblk)));
+       fclose(qfo);
+       return (0);
+}
+
+/*
+ * Check to see if target appears in list of size cnt.
+ */
+oneof(target, list, cnt)
+       register char *target, *list[];
+       int cnt;
+{
+       register int i;
+
+       for (i = 0; i < cnt; i++)
+               if (strcmp(target, list[i]) == 0)
+                       return (i);
+       return (-1);
+}
+
+/*
+ * Determine the group identifier for quota files.
+ */
+getquotagid()
+{
+       struct group *gr;
+
+       if (gr = getgrnam(quotagroup))
+               return (gr->gr_gid);
+       return (-1);
+}
+
+/*
+ * Check to see if a particular quota is to be enabled.
+ */
+hasquota(fs, type, qfnamep)
+       register struct fstab *fs;
+       int type;
+       char **qfnamep;
+{
+       register char *opt;
+       char *cp, *index(), *strtok();
+       static char initname, usrname[100], grpname[100];
+       static char buf[BUFSIZ];
+
+       if (!initname) {
+               sprintf(usrname, "%s%s", qfextension[USRQUOTA], qfname);
+               sprintf(grpname, "%s%s", qfextension[GRPQUOTA], qfname);
+               initname = 1;
+       }
+       strcpy(buf, fs->fs_mntops);
+       for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) {
+               if (cp = index(opt, '='))
+                       *cp++ = '\0';
+               if (type == USRQUOTA && strcmp(opt, usrname) == 0)
+                       break;
+               if (type == GRPQUOTA && strcmp(opt, grpname) == 0)
+                       break;
+       }
+       if (!opt)
+               return (0);
+       if (cp) {
+               *qfnamep = cp;
+               return (1);
+       }
+       (void) sprintf(buf, "%s/%s.%s", fs->fs_file, qfname, qfextension[type]);
+       *qfnamep = buf;
+       return (1);
+}
+
+/*
+ * Routines to manage the file usage table.
+ *
+ * Lookup an id of a specific type.
+ */
+struct fileusage *
+lookup(id, type)
+       u_long id;
+       int type;
+{
+       register struct fileusage *fup;
+
+       for (fup = fuhead[type][id & (FUHASH-1)]; fup != 0; fup = fup->fu_next)
+               if (fup->fu_id == id)
+                       return (fup);
+       return ((struct fileusage *)0);
+}
+
+/*
+ * Add a new file usage id if it does not already exist.
+ */
+struct fileusage *
+addid(id, type, name)
+       u_long id;
+       int type;
+       char *name;
+{
+       struct fileusage *fup, **fhp;
+       int len;
+
+       if (fup = lookup(id, type))
+               return (fup);
+       if (name)
+               len = strlen(name);
+       else
+               len = 10;
+       if ((fup = (struct fileusage *)calloc(1, sizeof(*fup) + len)) == NULL) {
+               fprintf(stderr, "out of memory for fileusage structures\n");
+               exit(1);
+       }
+       fhp = &fuhead[type][id & (FUHASH - 1)];
+       fup->fu_next = *fhp;
+       *fhp = fup;
+       fup->fu_id = id;
+       if (id > highid[type])
+               highid[type] = id;
+       if (name) {
+               bcopy(name, fup->fu_name, len + 1);
+       } else {
+               sprintf(fup->fu_name, "%u", id);
+       }
+       return (fup);
+}
+
+/*
+ * Special purpose version of ginode used to optimize pass
+ * over all the inodes in numerical order.
+ */
+ino_t nextino, lastinum;
+long readcnt, readpercg, fullcnt, inobufsize, partialcnt, partialsize;
+struct dinode *inodebuf;
+#define        INOBUFSIZE      56*1024 /* size of buffer to read inodes */
+
+struct dinode *
+getnextinode(inumber)
+       ino_t inumber;
+{
+       long size;
+       daddr_t dblk;
+       static struct dinode *dp;
+
+       if (inumber != nextino++ || inumber > maxino) {
+               fprintf(stderr, "bad inode number %d to nextinode\n", inumber);
+               exit(1);
+       }
+       if (inumber >= lastinum) {
+               readcnt++;
+               dblk = fsbtodb(&sblock, itod(&sblock, lastinum));
+               if (readcnt % readpercg == 0) {
+                       size = partialsize;
+                       lastinum += partialcnt;
+               } else {
+                       size = inobufsize;
+                       lastinum += fullcnt;
+               }
+               bread(dblk, (char *)inodebuf, size);
+               dp = inodebuf;
+       }
+       return (dp++);
+}
+
+/*
+ * Prepare to scan a set of inodes.
+ */
+resetinodebuf()
+{
+
+       nextino = 0;
+       lastinum = 0;
+       readcnt = 0;
+       inobufsize = blkroundup(&sblock, INOBUFSIZE);
+       fullcnt = inobufsize / sizeof(struct dinode);
+       readpercg = sblock.fs_ipg / fullcnt;
+       partialcnt = sblock.fs_ipg % fullcnt;
+       partialsize = partialcnt * sizeof(struct dinode);
+       if (partialcnt != 0) {
+               readpercg++;
+       } else {
+               partialcnt = fullcnt;
+               partialsize = inobufsize;
+       }
+       if (inodebuf == NULL &&
+          (inodebuf = (struct dinode *)malloc((unsigned)inobufsize)) == NULL) {
+               fprintf(stderr, "Cannot allocate space for inode buffer\n");
+               exit(1);
+       }
+       while (nextino < ROOTINO)
+               getnextinode(nextino);
+}
+
+/*
+ * Free up data structures used to scan inodes.
+ */
+freeinodebuf()
+{
+
+       if (inodebuf != NULL)
+               free((char *)inodebuf);
+       inodebuf = NULL;
+}
+
+/*
+ * Read specified disk blocks.
+ */
+bread(bno, buf, cnt)
+       daddr_t bno;
+       char *buf;
+       long cnt;
+{
+
+       if (lseek(fi, bno * dev_bsize, 0) < 0) {
+               perror("lseek");
+               exit(1);
+       }
+
+       if (read(fi, buf, cnt) != cnt) {
+               perror("read");
+               exit(1);
+       }
+}