changeset 5121:c926e7bdc887

PSARC 2007/044 Support for timezone mount option in PCFS PSARC 2007/415 PCFS mount options "noatime"/"atime" 6213409 pcfs alternate FAT #1 corrupted: doesn't say which device 6258345 Some of the contents of /tmp/SUNWut/mnt/<Username>/ shows wrong date 6338334 crash in pc_getcluster - filesystem believes it has 29-bit cluster count 6459843 fix for 1181439 (enabling "hidden") failed to remove old comment in cmd/fs.d/pcfs/mount/mount.c 6490418 mount option for timezone is required in pcfs 6503577 PCFS updates access time too often - "noatime" mount option required 6539301 PCFS FAT type detection / device detection still insufficient 6391828 A pcfs with 2k sector size cannot be mounted on Solaris: read fails with EIO 6391838 A pcfs with 2k sector size cannot be mounted on Solaris: read data corruption 6393753 A pcfs with 2k sector size cannot be mounted on Solaris: FAT signature error 6540349 PCFS endlessly loops on reading dirs if encountering LFN entry with LDIR_Ord == {0xc0|0x80|0x40} Contributed by Juergen Keil <jk@tools.de> Contributed by Alexander Zakharov
author frankho
date Mon, 24 Sep 2007 06:23:24 -0700
parents d3d745ca6aaf
children 3f4be3102a6a
files usr/src/cmd/fs.d/pcfs/mkfs/mkfs.c usr/src/cmd/fs.d/pcfs/mount/mount.c usr/src/uts/common/fs/pcfs/pc_alloc.c usr/src/uts/common/fs/pcfs/pc_node.c usr/src/uts/common/fs/pcfs/pc_vfsops.c usr/src/uts/common/fs/pcfs/pc_vnops.c usr/src/uts/common/sys/fs/pc_dir.h usr/src/uts/common/sys/fs/pc_fs.h usr/src/uts/common/sys/fs/pc_node.h
diffstat 9 files changed, 2514 insertions(+), 1766 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/cmd/fs.d/pcfs/mkfs/mkfs.c	Sun Sep 23 11:47:40 2007 -0700
+++ b/usr/src/cmd/fs.d/pcfs/mkfs/mkfs.c	Mon Sep 24 06:23:24 2007 -0700
@@ -112,7 +112,7 @@
 
 static uchar_t *build_rootdir(bpb_t *wbpb, char *ffn, int fffd,
 	ulong_t ffsize, pc_cluster32_t ffstart, ulong_t *rdirsize);
-static uchar_t *build_fat(bpb_t *wbpb, struct fat32_boot_fsinfo *fsinfop,
+static uchar_t *build_fat(bpb_t *wbpb, struct fat_od_fsi *fsinfop,
 	ulong_t bootblksize, ulong_t *fatsize, char *ffn, int *fffd,
 	ulong_t *ffsize, pc_cluster32_t *ffstartclust);
 
@@ -126,7 +126,7 @@
 	bpb_t *wbpb);
 static void compute_file_area_size(bpb_t *wbpb);
 static void write_fat32_bootstuff(int fd, boot_sector_t *bsp,
-	struct fat32_boot_fsinfo *fsinfop, off64_t seekto);
+	struct fat_od_fsi *fsinfop, off64_t seekto);
 static void sanity_check_options(int argc, int optind);
 static void compute_cluster_size(bpb_t *wbpb);
 static void find_fixed_details(int fd, bpb_t *wbpb);
@@ -140,7 +140,7 @@
 static void parse_suboptions(char *optsstr);
 static void header_for_dump(void);
 static void write_bootsects(int fd, boot_sector_t *bsp, bpb_t *wbpb,
-	struct fat32_boot_fsinfo *fsinfop, off64_t seekto);
+	struct fat_od_fsi *fsinfop, off64_t seekto);
 static void fill_bpb_sizes(bpb_t *wbpb, struct ipart part[],
 	int partno, off64_t offset);
 static void set_fat_string(bpb_t *wbpb, int fatsize);
@@ -189,28 +189,28 @@
 {
 	(void) fprintf(stderr,
 	    gettext("pcfs usage: mkfs [-F FSType] [-V] [-m] "
-		"[-o specific_options] special\n"));
+	    "[-o specific_options] special\n"));
 
 	(void) fprintf(stderr,
 	    gettext(" -V: print this command line and return\n"
-		" -m: dump command line used to create a FAT on this media\n"
-		"\t(other options are ignored if this option is chosen).\n"
-		" -o: pcfs_specific_options:\n"
-		"\t'pcfs_specific_options' is a comma separated list\n"
-		"\tincluding one or more of the following options:\n"
-		"\t    N,v,r,h,s,b=label,B=filename,i=filename,\n"
-		"\t    spc=n,fat=n,nsect=n,ntrack=n,nofdisk,size=n,\n"
-		"\t    reserve=n,hidden=n\n\n"));
+	    " -m: dump command line used to create a FAT on this media\n"
+	    "\t(other options are ignored if this option is chosen).\n"
+	    " -o: pcfs_specific_options:\n"
+	    "\t'pcfs_specific_options' is a comma separated list\n"
+	    "\tincluding one or more of the following options:\n"
+	    "\t    N,v,r,h,s,b=label,B=filename,i=filename,\n"
+	    "\t    spc=n,fat=n,nsect=n,ntrack=n,nofdisk,size=n,\n"
+	    "\t    reserve=n,hidden=n\n\n"));
 
 	(void) fprintf(stderr,
 	    gettext("'Special' should specify a raw diskette "
-			"or raw fixed disk device.  \"Fixed\"\n"
-			"disks (which include high-capacity removable "
-			"media such as Zip disks)\n"
-			"may be further qualified with a logical "
-			"drive specifier.\n"
-			"Examples are: /dev/rdiskette and "
-			"/dev/rdsk/c0t0d0p0:c\n"));
+	    "or raw fixed disk device.  \"Fixed\"\n"
+	    "disks (which include high-capacity removable "
+	    "media such as Zip disks)\n"
+	    "may be further qualified with a logical "
+	    "drive specifier.\n"
+	    "Examples are: /dev/rdiskette and "
+	    "/dev/rdsk/c0t0d0p0:c\n"));
 	exit(1);
 }
 
@@ -352,8 +352,8 @@
 		}
 		for (bl = 0; bl < BPL; bl++) {
 			if ((cu+bl < n) &&
-				((b[cu+bl] >= ' ') && (b[cu+bl] <= '~')))
-					(void) printf("%c", b[cu+bl]);
+			    ((b[cu+bl] >= ' ') && (b[cu+bl] <= '~')))
+				(void) printf("%c", b[cu+bl]);
 			else
 				(void) printf(".");
 		}
@@ -489,15 +489,15 @@
 	 */
 	if (Notreally || !isatty(fileno(stdin))) {
 		(void) printf(gettext("WARNING: User supplied %s is %d,"
-			"\nbut value obtained from the %s is %d.\n"
-			"Using user supplied value.\n"),
-			desc, assigned, src, expect);
+		    "\nbut value obtained from the %s is %d.\n"
+		    "Using user supplied value.\n"),
+		    desc, assigned, src, expect);
 		return (assigned);
 	}
 
 	(void) printf(gettext("User supplied %s is %d."
-		"\nThe value obtained from the %s is %d.\n"),
-		desc, assigned, src, expect);
+	    "\nThe value obtained from the %s is %d.\n"),
+	    desc, assigned, src, expect);
 
 	(void) printf(
 	    gettext("Continue with value given on command line (y/n)? "));
@@ -562,8 +562,8 @@
 	if (GetFsParams || GetSize) {
 		usesize = ltohi(part[partno].numsect);
 		if (Verbose) {
-		    (void) printf(
-			gettext("Partition size (from FDISK table) "
+			(void) printf(
+			    gettext("Partition size (from FDISK table) "
 			    "= %d sectors.\n"), usesize);
 		}
 	} else {
@@ -808,9 +808,9 @@
 			 */
 			if (lseek64(fd, nextseek * BPSEC, SEEK_SET) < 0 ||
 			    read(fd, &extmboot, sizeof (extmboot)) !=
-				sizeof (extmboot)) {
+			    sizeof (extmboot)) {
 				perror(gettext("Unable to read extended "
-					"partition record"));
+				    "partition record"));
 				return (PART_NOT_FOUND);
 			}
 			(void) memcpy(part, extmboot.parts, sizeof (part));
@@ -925,7 +925,7 @@
 		fill_bpb_sizes(wbpb, part, extraDrives[driveIndex], *seekto);
 		*seekto *= BPSEC;
 		FdiskFATsize =
-			lookup_FAT_size(part[extraDrives[driveIndex]].systid);
+		    lookup_FAT_size(part[extraDrives[driveIndex]].systid);
 		if (Verbose)
 			(void) printf(gettext("Partition's offset: "
 			    "Sector %x.\n"), *seekto/BPSEC);
@@ -987,13 +987,13 @@
 {
 	if (fatsize == 12) {
 		(void) strncpy((char *)wbpb->ebpb.type, FAT12_TYPE_STRING,
-			strlen(FAT12_TYPE_STRING));
+		    strlen(FAT12_TYPE_STRING));
 	} else if (fatsize == 16) {
 		(void) strncpy((char *)wbpb->ebpb.type, FAT16_TYPE_STRING,
-			strlen(FAT16_TYPE_STRING));
+		    strlen(FAT16_TYPE_STRING));
 	} else {
 		(void) strncpy((char *)wbpb->ebpb.type, FAT32_TYPE_STRING,
-			strlen(FAT32_TYPE_STRING));
+		    strlen(FAT32_TYPE_STRING));
 	}
 }
 
@@ -1082,17 +1082,17 @@
 partn_lecture(char *dn)
 {
 	(void) fprintf(stderr,
-		gettext("\nDevice %s was assumed to be a diskette.\n"
-		    "A diskette specific operation failed on this device.\n"
-		    "If the device is a hard disk, provide the name of "
-		    "the full physical disk,\n"
-		    "and qualify that name with a logical drive specifier.\n\n"
-		    "Hint: the device is usually something similar to\n\n"
-		    "/dev/rdsk/c0d0p0 or /dev/rdsk/c0t0d0p0 (x86)\n"
-		    "/dev/rdsk/c0t5d0s2 (sparc)\n\n"
-		    "The drive specifier is appended to the device name."
-		    " For example:\n\n"
-		    "/dev/rdsk/c0t5d0s2:c or /dev/rdsk/c0d0p0:boot\n\n"), dn);
+	    gettext("\nDevice %s was assumed to be a diskette.\n"
+	    "A diskette specific operation failed on this device.\n"
+	    "If the device is a hard disk, provide the name of "
+	    "the full physical disk,\n"
+	    "and qualify that name with a logical drive specifier.\n\n"
+	    "Hint: the device is usually something similar to\n\n"
+	    "/dev/rdsk/c0d0p0 or /dev/rdsk/c0t0d0p0 (x86)\n"
+	    "/dev/rdsk/c0t5d0s2 (sparc)\n\n"
+	    "The drive specifier is appended to the device name."
+	    " For example:\n\n"
+	    "/dev/rdsk/c0t5d0s2:c or /dev/rdsk/c0d0p0:boot\n\n"), dn);
 }
 
 static
@@ -1101,7 +1101,7 @@
 {
 	(void) fprintf(stderr,
 	    gettext("Use the 'nofdisk' option to create file systems\n"
-		    "on non-standard floppies.\n\n"));
+	    "on non-standard floppies.\n\n"));
 	exit(4);
 }
 
@@ -1111,8 +1111,8 @@
 {
 	(void) fprintf(stderr,
 	    gettext("Non-standard FAT size requested for floppy.\n"
-		    "The 'nofdisk' option must be used to\n"
-		    "override the 12 bit floppy default.\n\n"));
+	    "The 'nofdisk' option must be used to\n"
+	    "override the 12 bit floppy default.\n\n"));
 	exit(4);
 }
 
@@ -1146,8 +1146,8 @@
 			default:
 				(void) fprintf(stderr,
 				    gettext("Unknown diskette parameters!  "
-					"3.5'' diskette with %d heads "
-					"and %d sectors/track.\n"), hds, spt);
+				    "3.5'' diskette with %d heads "
+				    "and %d sectors/track.\n"), hds, spt);
 				warn_funky_floppy();
 			}
 			break;
@@ -1155,7 +1155,7 @@
 		default:
 			(void) fprintf(stderr,
 			    gettext("Unknown diskette parameters!  "
-				"3.5'' diskette with %d heads "), hds);
+			    "3.5'' diskette with %d heads "), hds);
 			warn_funky_floppy();
 		}
 		break;
@@ -1184,8 +1184,8 @@
 			default:
 				(void) fprintf(stderr,
 				    gettext("Unknown diskette parameters!  "
-					"5.25'' diskette with %d heads "
-					"and %d sectors/track.\n"), hds, spt);
+				    "5.25'' diskette with %d heads "
+				    "and %d sectors/track.\n"), hds, spt);
 				warn_funky_floppy();
 			}
 			break;
@@ -1206,22 +1206,22 @@
 			default:
 				(void) fprintf(stderr,
 				    gettext("Unknown diskette parameters! "
-					"5.25'' diskette with %d heads "
-					"and %d sectors/track.\n"), hds, spt);
+				    "5.25'' diskette with %d heads "
+				    "and %d sectors/track.\n"), hds, spt);
 				warn_funky_floppy();
 			}
 			break;
 		default:
 			(void) fprintf(stderr,
 			    gettext("Unknown diskette parameters! "
-				"5.25'' diskette with %d heads."), hds);
+			    "5.25'' diskette with %d heads."), hds);
 			warn_funky_floppy();
 		}
 		break;
 	default:
 		(void) fprintf(stderr,
 		    gettext("\nUnknown diskette type.  Only know about "
-			"5.25'' and 3.5'' diskettes.\n"));
+		    "5.25'' and 3.5'' diskettes.\n"));
 		warn_funky_floppy();
 	}
 }
@@ -1253,20 +1253,20 @@
 		wbpb->bpb.sectors_in_logical_volume = tsize;
 	} else {
 		wbpb->bpb.sectors_in_logical_volume =
-			warn_mismatch(
-			    gettext("length of partition (in sectors)"),
-			    gettext("FDIOGCHAR call"), tsize, TotSize);
+		    warn_mismatch(
+		    gettext("length of partition (in sectors)"),
+		    gettext("FDIOGCHAR call"), tsize, TotSize);
 	}
 	wbpb->bpb.sectors_in_volume =
-		(short)wbpb->bpb.sectors_in_logical_volume;
+	    (short)wbpb->bpb.sectors_in_logical_volume;
 
 	if (GetSPT) {
 		wbpb->bpb.sectors_per_track = spt;
 	} else {
 		wbpb->bpb.sectors_per_track =
-			warn_mismatch(
-			    gettext("sectors per track"),
-			    gettext("FDIOGCHAR call"), spt, SecPerTrk);
+		    warn_mismatch(
+		    gettext("sectors per track"),
+		    gettext("FDIOGCHAR call"), spt, SecPerTrk);
 		spt = wbpb->bpb.sectors_per_track;
 	}
 
@@ -1274,9 +1274,9 @@
 		wbpb->bpb.heads = hds;
 	} else {
 		wbpb->bpb.heads =
-			warn_mismatch(
-			    gettext("number of heads"),
-			    gettext("FDIOGCHAR call"), hds, TrkPerCyl);
+		    warn_mismatch(
+		    gettext("number of heads"),
+		    gettext("FDIOGCHAR call"), hds, TrkPerCyl);
 		hds = wbpb->bpb.heads;
 	}
 
@@ -1317,7 +1317,6 @@
 	ulong_t spc;
 	ulong_t rds, tmpval1, tmpval2;
 	ulong_t fatsz;
-	ulong_t bps = wbpb->bpb.bytes_sector;
 	int newfat = 16;
 
 #define	FAT12_MAX_CLUSTERS	0x0FF4
@@ -1327,7 +1326,7 @@
 
 	/* compute volume size in sectors. */
 	volsize = wbpb->bpb.sectors_in_volume ? wbpb->bpb.sectors_in_volume :
-		wbpb->bpb.sectors_in_logical_volume;
+	    wbpb->bpb.sectors_in_logical_volume;
 	volsize -= wbpb->bpb.resv_sectors;
 
 	if (GetSPC) {
@@ -1340,8 +1339,8 @@
 			/* volsize is in sectors */
 			if (volsize < FAT12_MAX_CLUSTERS) {
 				(void) fprintf(stderr,
-					gettext("Requested size is too "
-						"small for FAT16.\n"));
+				    gettext("Requested size is too "
+				    "small for FAT16.\n"));
 				exit(4);
 			}
 			/* SPC must be a power of 2 */
@@ -1351,16 +1350,16 @@
 			}
 			if (volsize > (spc * FAT16_MAX_CLUSTERS)) {
 				(void) fprintf(stderr,
-					gettext("Requested size is too "
-						"large for FAT16.\n"));
+				    gettext("Requested size is too "
+				    "large for FAT16.\n"));
 				exit(4);
 			}
 		} else { /* FAT32 */
 			/* volsize is in sectors */
 			if (volsize < FAT16_MAX_CLUSTERS) {
 				(void) fprintf(stderr,
-					gettext("Requested size is too "
-						"small for FAT32.\n"));
+				    gettext("Requested size is too "
+				    "small for FAT32.\n"));
 				exit(4);
 			}
 			/* SPC must be a power of 2 */
@@ -1370,8 +1369,8 @@
 			}
 			if (volsize > (spc * FAT32_MAX_CLUSTERS)) {
 				(void) fprintf(stderr,
-					gettext("Requested size is too "
-						"large for FAT32.\n"));
+				    gettext("Requested size is too "
+				    "large for FAT32.\n"));
 				exit(4);
 			}
 		}
@@ -1387,9 +1386,8 @@
 		nclust = volsize / spc;
 
 		if (nclust <= FAT16_MAX_CLUSTERS && MakeFAT32) {
-			(void) fprintf(stderr,
-				gettext("Requested size is too "
-					"small for FAT32.\n"));
+			(void) fprintf(stderr, gettext("Requested size is too "
+			    "small for FAT32.\n"));
 			exit(4);
 		}
 		if (!MakeFAT32) {
@@ -1400,8 +1398,8 @@
 				newfat = 16;
 			else {
 				(void) fprintf(stderr,
-					gettext("Requested size is too "
-						"small for FAT32.\n"));
+				    gettext("Requested size is too "
+				    "small for FAT32.\n"));
 				exit(4);
 			}
 		}
@@ -1412,8 +1410,7 @@
 	 *	(BPB_BytsPerSec  1)) / BPB_BytsPerSec;
 	 */
 	rds = ((wbpb->bpb.num_root_entries * 32) +
-		(wbpb->bpb.bytes_sector - 1)) /
-		wbpb->bpb.bytes_sector;
+	    (wbpb->bpb.bytes_sector - 1)) / wbpb->bpb.bytes_sector;
 
 	if (GetBPF) {
 		if (MakeFAT32)
@@ -1424,7 +1421,7 @@
 		Fatentsize = BitsPerFAT;
 
 		if (Fatentsize == 12 &&
-			(volsize - rds) >= DOS_F12MAXC * spc) {
+		    (volsize - rds) >= DOS_F12MAXC * spc) {
 			/*
 			 * If we don't have an input TTY, or we aren't
 			 * really doing anything, then don't ask
@@ -1432,21 +1429,21 @@
 			 * questions we would ask.
 			 */
 			if (Notreally || !isatty(fileno(stdin))) {
-			    (void) printf(
+				(void) printf(
 				gettext("Volume too large for 12 bit FAT,"
-				" increasing to 16 bit FAT size.\n"));
-			    (void) fflush(stdout);
-			    Fatentsize = 16;
+				    " increasing to 16 bit FAT size.\n"));
+				(void) fflush(stdout);
+				Fatentsize = 16;
 			} else {
-			    (void) printf(
+				(void) printf(
 				gettext("Volume too large for a 12 bit FAT.\n"
-					"Increase to 16 bit FAT "
-					"and continue (y/n)? "));
-			    (void) fflush(stdout);
-			    if (yes())
-				Fatentsize = 16;
-			    else
-				exit(5);
+				    "Increase to 16 bit FAT "
+				    "and continue (y/n)? "));
+				(void) fflush(stdout);
+				if (yes())
+					Fatentsize = 16;
+				else
+					exit(5);
 			}
 		}
 	}
@@ -1455,19 +1452,19 @@
 	if (!GetFsParams && FdiskFATsize < 0) {
 		(void) printf(
 		    gettext("Cannot verify chosen/computed FAT "
-			"entry size (%d bits) with FDISK table.\n"
-			"FDISK table has an unknown file system "
-			"type for this device.  Giving up...\n"),
-			Fatentsize, Fatentsize);
+		    "entry size (%d bits) with FDISK table.\n"
+		    "FDISK table has an unknown file system "
+		    "type for this device.  Giving up...\n"),
+		    Fatentsize, Fatentsize);
 		exit(6);
 	} else if (!GetFsParams && FdiskFATsize && FdiskFATsize != Fatentsize) {
 		(void) printf(
 		    gettext("Chosen/computed FAT entry size (%d bits) "
-			"does not match FDISK table (%d bits).\n"),
-			Fatentsize, FdiskFATsize);
+		    "does not match FDISK table (%d bits).\n"),
+		    Fatentsize, FdiskFATsize);
 		(void) printf(
 		    gettext("Use -o fat=%d to build a FAT "
-			"that matches the FDISK entry.\n"), FdiskFATsize);
+		    "that matches the FDISK entry.\n"), FdiskFATsize);
 		exit(6);
 	}
 	set_fat_string(wbpb, Fatentsize);
@@ -1491,8 +1488,7 @@
 	 */
 	tmpval1 = volsize - (wbpb->bpb.resv_sectors + rds);
 
-	tmpval2 = (256 * wbpb->bpb.sectors_per_cluster) +
-		wbpb->bpb.num_fats;
+	tmpval2 = (256 * wbpb->bpb.sectors_per_cluster) + wbpb->bpb.num_fats;
 
 	if (Fatentsize == 32)
 		tmpval2 = tmpval2 / 2;
@@ -1505,16 +1501,15 @@
 		wbpb->bpb.sectors_per_fat = 0;
 		wbpb->bpb32.big_sectors_per_fat = fatsz;
 		if (Verbose)
-		    (void) printf("compute_cluster_size: Sectors per "
-				"FAT32 = %d\n",
-				wbpb->bpb32.big_sectors_per_fat);
+			(void) printf("compute_cluster_size: Sectors per "
+			    "FAT32 = %d\n", wbpb->bpb32.big_sectors_per_fat);
 		break;
 	case 12:
 	default:	/* 16 bit FAT */
 		wbpb->bpb.sectors_per_fat = (ushort_t)(fatsz & 0x0000FFFF);
 		if (Verbose)
-		    (void) printf("compute_cluster_size: Sectors per "
-			"FAT16 = %d\n", wbpb->bpb.sectors_per_fat);
+			(void) printf("compute_cluster_size: Sectors per "
+			    "FAT16 = %d\n", wbpb->bpb.sectors_per_fat);
 		break;
 	}
 }
@@ -1530,16 +1525,14 @@
 	 *  that is specific to the hard drive using a disk ioctl.
 	 */
 	if (GetSPT || GetTPC) {
-		if ((ioctl(fd, DKIOCG_VIRTGEOM, &dginfo)) == -1) {
-		    if ((ioctl(fd, DKIOCG_PHYGEOM, &dginfo)) == -1) {
-			if ((ioctl(fd, DKIOCGGEOM, &dginfo)) == -1) {
-			    (void) close(fd);
-			    perror(
-				gettext("Drive geometry lookup (need "
-				    "tracks/cylinder and/or sectors/track"));
-			    exit(2);
-			}
-		    }
+		if (ioctl(fd, DKIOCG_VIRTGEOM, &dginfo) == -1 &&
+		    ioctl(fd, DKIOCG_PHYGEOM, &dginfo) == -1 &&
+		    ioctl(fd, DKIOCGGEOM, &dginfo) == -1) {
+			(void) close(fd);
+			perror(
+			    gettext("Drive geometry lookup (need "
+			    "tracks/cylinder and/or sectors/track"));
+			exit(2);
 		}
 	}
 
@@ -1548,14 +1541,14 @@
 
 	if (Verbose) {
 		if (GetTPC) {
-		    (void) printf(
-			gettext("DKIOCG determined number of heads = %d\n"),
-			dginfo.dkg_nhead);
+			(void) printf(
+			    gettext("DKIOCG determined number of heads = %d\n"),
+			    dginfo.dkg_nhead);
 		}
 		if (GetSPT) {
-		    (void) printf(
-			gettext("DKIOCG determined sectors per track = %d\n"),
-			dginfo.dkg_nsect);
+			(void) printf(
+			    gettext("DKIOCG determined sectors per track"
+			    " = %d\n"), dginfo.dkg_nsect);
 		}
 	}
 
@@ -1607,9 +1600,9 @@
 	int FATSz;
 	int TotSec;
 	int DataSec;
-	int RootDirSectors = ((wbpb->bpb.num_root_entries * 32) +
-			(wbpb->bpb.bytes_sector - 1)) /
-			wbpb->bpb.bytes_sector;
+	int RootDirSectors =
+	    ((wbpb->bpb.num_root_entries * 32) + (wbpb->bpb.bytes_sector - 1)) /
+	    wbpb->bpb.bytes_sector;
 
 	if (wbpb->bpb.sectors_per_fat) {
 		/*
@@ -1625,9 +1618,9 @@
 		TotSec = wbpb->bpb.sectors_in_logical_volume;
 	}
 
-	DataSec = TotSec - (wbpb->bpb.resv_sectors +
-			(wbpb->bpb.num_fats * FATSz) +
-			RootDirSectors);
+	DataSec = TotSec -
+	    (wbpb->bpb.resv_sectors + (wbpb->bpb.num_fats * FATSz) +
+	    RootDirSectors);
 
 
 	/*
@@ -1839,8 +1832,8 @@
 dashm_bail(int fd)
 {
 	(void) fprintf(stderr,
-		gettext("This media does not appear to be "
-			"formatted with a FAT file system.\n"));
+	    gettext("This media does not appear to be "
+	    "formatted with a FAT file system.\n"));
 	(void) close(fd);
 	exit(6);
 }
@@ -1860,7 +1853,7 @@
 
 	if (read(fd, ubpb.buf, BPSEC) < BPSEC) {
 		perror(gettext("Read BIOS parameter block "
-			"from previously formatted media"));
+		    "from previously formatted media"));
 		(void) close(fd);
 		exit(6);
 	}
@@ -1871,7 +1864,7 @@
 
 #ifdef i386
 	(void) memcpy(&(wbpb->bpb), &(ubpb.bs.bs_front.bs_bpb),
-		sizeof (wbpb->bpb));
+	    sizeof (wbpb->bpb));
 	(void) memcpy(&(wbpb->ebpb), &(ubpb.bs.bs_ebpb), sizeof (wbpb->ebpb));
 #else
 	swap_pack_grabbpb(wbpb, &(ubpb.bs));
@@ -1879,7 +1872,7 @@
 	if (SunBPBfields) {
 #ifdef i386
 		(void) memcpy(&(wbpb->sunbpb), &(ubpb.bs.bs_sebpb),
-			sizeof (wbpb->sunbpb));
+		    sizeof (wbpb->sunbpb));
 #else
 		swap_pack_grabsebpb(wbpb, &(ubpb.bs));
 #endif
@@ -1890,16 +1883,16 @@
 		if (!powerofx_le_y(2, BPSEC * 8, wbpb->bpb.bytes_sector)) {
 			(void) fprintf(stderr,
 			    gettext("The device name may be missing a "
-				    "logical drive specifier.\n"));
+			    "logical drive specifier.\n"));
 			(void) close(fd);
 			exit(6);
 		} else {
 			(void) fprintf(stderr,
 			    gettext("Do not know how to build FATs with a\n"
-				    "non-standard sector size. Standard "
-				    "size is %d bytes,\nyour sector size "
-				    "is %d bytes.\n"), BPSEC,
-				    wbpb->bpb.bytes_sector);
+			    "non-standard sector size. Standard "
+			    "size is %d bytes,\nyour sector size "
+			    "is %d bytes.\n"), BPSEC,
+			    wbpb->bpb.bytes_sector);
 			(void) close(fd);
 			exit(6);
 		}
@@ -1909,7 +1902,7 @@
 		    gettext("Bogus sectors per cluster value.\n"));
 		(void) fprintf(stderr,
 		    gettext("The device name may be missing a "
-			"logical drive specifier.\n"));
+		    "logical drive specifier.\n"));
 		(void) close(fd);
 		exit(6);
 	}
@@ -1917,7 +1910,7 @@
 	if (wbpb->bpb.sectors_per_fat == 0) {
 #ifdef i386
 		(void) memcpy(&(wbpb->bpb32), &(ubpb.bs32.bs_bpb32),
-			sizeof (wbpb->bpb32));
+		    sizeof (wbpb->bpb32));
 #else
 		swap_pack_grab32bpb(wbpb, &(ubpb.bs));
 #endif
@@ -1997,14 +1990,12 @@
 			(*prtsize)++;
 			*dashos += 2;
 		}
-		if ((ioctl(fd, DKIOCG_VIRTGEOM, &dginfo)) == -1) {
-		    if ((ioctl(fd, DKIOCG_PHYGEOM, &dginfo)) == -1) {
-			if ((ioctl(fd, DKIOCGGEOM, &dginfo)) == -1) {
-				*prtnsect = *prtntrk = 1;
-				*dashos += 2;
-				dk_ioctl_worked = 0;
-			}
-		    }
+		if (ioctl(fd, DKIOCG_VIRTGEOM, &dginfo) == -1 &&
+		    ioctl(fd, DKIOCG_PHYGEOM, &dginfo) == -1 &&
+		    ioctl(fd, DKIOCGGEOM, &dginfo) == -1) {
+			*prtnsect = *prtntrk = 1;
+			*dashos += 2;
+			dk_ioctl_worked = 0;
 		}
 		if (dk_ioctl_worked) {
 			if (dginfo.dkg_nhead != wbpb->bpb.heads) {
@@ -2022,7 +2013,7 @@
 	}
 
 	if (!*prtfdisk && TotSize != wbpb->bpb.sectors_in_volume &&
-		TotSize != wbpb->bpb.sectors_in_logical_volume) {
+	    TotSize != wbpb->bpb.sectors_in_logical_volume) {
 		(*dashos)++;
 		(*prtsize)++;
 	}
@@ -2097,7 +2088,8 @@
 	 * any label info.
 	 */
 	i = ll;
-	while (wbpb->ebpb.volume_label[--i] == ' ');
+	while (wbpb->ebpb.volume_label[--i] == ' ')
+		;
 	ll = i;
 
 	if (ll == strlen(DEFAULT_LABEL) - 1) {
@@ -2212,7 +2204,7 @@
 	if (!(S_ISCHR(di.st_mode))) {
 		(void) fprintf(stderr,
 		    gettext("\n%s: device name must be a "
-			"character special device.\n"), actualdisk);
+		    "character special device.\n"), actualdisk);
 		exit(2);
 	} else if ((fd = open(actualdisk, O_RDWR)) < 0) {
 		perror(actualdisk);
@@ -2299,8 +2291,8 @@
 	if (DontUseFdisk && suffix) {
 		(void) fprintf(stderr,
 		    gettext("Using 'nofdisk' option precludes "
-			    "appending logical drive\nspecifier "
-			    "to the device name.\n"));
+		    "appending logical drive\nspecifier "
+		    "to the device name.\n"));
 		exit(2);
 	}
 
@@ -2310,7 +2302,7 @@
 	if (!(S_ISCHR(di.st_mode))) {
 		(void) fprintf(stderr,
 		    gettext("\n%s: device name must indicate a "
-			"character special device.\n"), actualdisk);
+		    "character special device.\n"), actualdisk);
 		exit(2);
 	} else if ((fd = open(actualdisk, O_RDWR)) < 0) {
 		perror(actualdisk);
@@ -2500,15 +2492,16 @@
 		bsfd = -1;
 		perror(gettext("Boot block read"));
 	} else {
+		if ((bs->bs.bs_signature[0] != (BOOTSECSIG & 0xFF) &&
+		    bs->bs.bs_signature[1] != ((BOOTSECSIG >> 8) & 0xFF)) ||
 #ifdef i386
-		if ((bs->bs.bs_front.bs_jump_code[0] != OPCODE1 &&
-		    bs->bs.bs_front.bs_jump_code[0] != OPCODE2) ||
+		    (bs->bs.bs_front.bs_jump_code[0] != OPCODE1 &&
+		    bs->bs.bs_front.bs_jump_code[0] != OPCODE2)
 #else
-		if ((bs->bs.bs_jump_code[0] != OPCODE1 &&
-		    bs->bs.bs_jump_code[0] != OPCODE2) ||
+		    (bs->bs.bs_jump_code[0] != OPCODE1 &&
+		    bs->bs.bs_jump_code[0] != OPCODE2)
 #endif
-		    (bs->bs.bs_signature[0] != (BOOTSECSIG & 0xFF) &&
-		    bs->bs.bs_signature[1] != ((BOOTSECSIG >> 8) & 0xFF))) {
+		    ) {
 			(void) close(bsfd);
 			bsfd = -1;
 			(void) fprintf(stderr,
@@ -2542,7 +2535,7 @@
 		perror(fn);
 		(void) fprintf(stderr,
 		    gettext("Could not access requested file.  It will not\n"
-			    "be installed in the new file system.\n"));
+		    "be installed in the new file system.\n"));
 	} else {
 		*filesize = fi.st_size;
 	}
@@ -2620,7 +2613,7 @@
 	ulong_t idx;
 
 	idx = (Fatentsize == 32) ? clustnum * 4 :
-		(Fatentsize == 16) ? clustnum * 2 : clustnum + clustnum/2;
+	    (Fatentsize == 16) ? clustnum * 2 : clustnum + clustnum/2;
 	ep = fatp + idx;
 
 	if (Fatentsize == 32) {
@@ -2641,7 +2634,7 @@
 
 static
 uchar_t *
-build_fat(bpb_t *wbpb, struct fat32_boot_fsinfo *fsinfop, ulong_t bootblksize,
+build_fat(bpb_t *wbpb, struct fat_od_fsi *fsinfop, ulong_t bootblksize,
     ulong_t *fatsize, char *ffn, int *fffd, ulong_t *ffsize,
     pc_cluster32_t *ffstartclust)
 {
@@ -2654,7 +2647,7 @@
 	if (Verbose) {
 		(void) printf(gettext("BUILD FAT.\n%d sectors per fat.\n"),
 		    wbpb->bpb.sectors_per_fat ? wbpb->bpb.sectors_per_fat :
-			wbpb->bpb32.big_sectors_per_fat);
+		    wbpb->bpb32.big_sectors_per_fat);
 	}
 
 	if (MakeFAT32) {
@@ -2712,7 +2705,7 @@
 		    numclust);
 	for (ci = 0; ci < numclust; ci++)
 		mark_cluster(fatp, nextfree++,
-			MakeFAT32 ? PCF_BADCLUSTER32 : PCF_BADCLUSTER);
+		    MakeFAT32 ? PCF_BADCLUSTER32 : PCF_BADCLUSTER);
 	remclust -= numclust;
 
 	/*
@@ -2734,8 +2727,8 @@
 
 		if (numclust > remclust) {
 			(void) fprintf(stderr,
-				gettext("Requested first file too large to be\n"
-					"installed in the new file system.\n"));
+			    gettext("Requested first file too large to be\n"
+			    "installed in the new file system.\n"));
 			(void) close(*fffd);
 			*fffd = -1;
 			goto finish;
@@ -2747,7 +2740,7 @@
 		for (ci = 0; (int)ci < (int)(numclust-1); ci++, nextfree++)
 			mark_cluster(fatp, nextfree, nextfree + 1);
 		mark_cluster(fatp, nextfree++,
-			MakeFAT32 ? PCF_LASTCLUSTER32 : PCF_LASTCLUSTER);
+		    MakeFAT32 ? PCF_LASTCLUSTER32 : PCF_LASTCLUSTER);
 		remclust -= numclust;
 	}
 
@@ -2758,9 +2751,12 @@
 		dump_bytes(fatp, BPSEC);
 	}
 
-	fsinfop->fs_signature = FAT32_FS_SIGN;
-	fsinfop->fs_free_clusters = remclust;
-	fsinfop->fs_next_cluster = nextfree;
+	(void) memset(fsinfop, 0, sizeof (*fsinfop));
+	fsinfop->fsi_leadsig = LE_32(FSI_LEADSIG);
+	fsinfop->fsi_strucsig = LE_32(FSI_STRUCSIG);
+	fsinfop->fsi_trailsig = LE_32(FSI_TRAILSIG);
+	fsinfop->fsi_incore.fs_free_clusters = LE_32(remclust);
+	fsinfop->fsi_incore.fs_next_free = LE_32(nextfree);
 	return (fatp);
 }
 
@@ -2963,42 +2959,12 @@
 static
 void
 write_fat32_bootstuff(int fd, boot_sector_t *bsp,
-	struct fat32_boot_fsinfo *fsinfop, off64_t seekto)
+	struct fat_od_fsi *fsinfop, off64_t seekto)
 {
-	uchar_t fsinfobuf[BPSEC];
-	uchar_t *fp;
-
-	/*
-	 * Construct the fsinfo buf
-	 */
-	(void) memset(fsinfobuf, 0, BPSEC);
-	/*
-	 * Not sure if this magic signature at the beginning of the
-	 * sector is necessary for us to create or not.  For now, I'm
-	 * going to assume it is, since I haven't seen any Windows FAT32s
-	 * without it.
-	 */
-	fsinfobuf[0] = 'R';
-	fsinfobuf[1] = 'R';
-	fsinfobuf[2] = 'a';
-	fsinfobuf[3] = 'A';
-	/*
-	 * We also appear to want the magic Boot sector signature at the
-	 * end of the sector.
-	 */
-	fsinfobuf[BPSEC - 2] = BOOTSECSIG & 0xFF;
-	fsinfobuf[BPSEC - 1] = (BOOTSECSIG >> 8) & 0xFF;
-
-	fp = &(fsinfobuf[FAT32_BOOT_FSINFO_OFF]);
-	fp += 4;	/* skip first reserved field */
-	store_32_bits(&fp, fsinfop->fs_signature);
-	store_32_bits(&fp, fsinfop->fs_free_clusters);
-	store_32_bits(&fp, fsinfop->fs_next_cluster);
-
 	if (Verbose) {
 		(void) printf(gettext("Dump of the fs info sector"));
 		header_for_dump();
-		dump_bytes(fsinfobuf, sizeof (fsinfobuf));
+		dump_bytes((uchar_t *)fsinfop, sizeof (*fsinfop));
 	}
 
 	if (!Notreally) {
@@ -3006,7 +2972,7 @@
 		 * FAT32's have an FS info sector, then a backup of the boot
 		 * sector, and a modified backup of the FS Info sector.
 		 */
-		if (write(fd, fsinfobuf, sizeof (fsinfobuf)) != BPSEC) {
+		if (write(fd, fsinfop, sizeof (*fsinfop)) != BPSEC) {
 			perror(gettext("FS info sector write"));
 			exit(4);
 		}
@@ -3025,18 +2991,16 @@
 	 * Second copy of fs info sector is modified to have "don't know"
 	 * as the number of free clusters
 	 */
-	fp = &(fsinfobuf[FAT32_BOOT_FSINFO_OFF]);
-	fp += 8;	/* skip first reserved field and signature */
-	store_32_bits(&fp, -1);
+	fsinfop->fsi_incore.fs_next_free = LE_32(FSINFO_UNKNOWN);
 
 	if (Verbose) {
 		(void) printf(gettext("Dump of the backup fs info sector"));
 		header_for_dump();
-		dump_bytes(fsinfobuf, sizeof (fsinfobuf));
+		dump_bytes((uchar_t *)fsinfop, sizeof (*fsinfop));
 	}
 
 	if (!Notreally) {
-		if (write(fd, fsinfobuf, sizeof (fsinfobuf)) != BPSEC) {
+		if (write(fd, fsinfop, sizeof (*fsinfop)) != BPSEC) {
 			perror(gettext("FS info sector backup write"));
 			exit(4);
 		}
@@ -3046,17 +3010,17 @@
 static
 void
 write_bootsects(int fd, boot_sector_t *bsp, bpb_t *wbpb,
-	struct fat32_boot_fsinfo *fsinfop, off64_t seekto)
+	struct fat_od_fsi *fsinfop, off64_t seekto)
 {
 	if (MakeFAT32) {
 		/* Copy our BPB into bootsec structure */
 #ifdef i386
 		(void) memcpy(&(bsp->bs32.bs_front.bs_bpb), &(wbpb->bpb),
-			    sizeof (wbpb->bpb));
+		    sizeof (wbpb->bpb));
 		(void) memcpy(&(bsp->bs32.bs_bpb32), &(wbpb->bpb32),
-			    sizeof (wbpb->bpb32));
+		    sizeof (wbpb->bpb32));
 		(void) memcpy(&(bsp->bs32.bs_ebpb), &(wbpb->ebpb),
-			    sizeof (wbpb->ebpb));
+		    sizeof (wbpb->ebpb));
 #else
 		swap_pack_bpb32cpy(&(bsp->bs32), wbpb);
 #endif
@@ -3064,9 +3028,9 @@
 		/* Copy our BPB into bootsec structure */
 #ifdef i386
 		(void) memcpy(&(bsp->bs.bs_front.bs_bpb), &(wbpb->bpb),
-			    sizeof (wbpb->bpb));
+		    sizeof (wbpb->bpb));
 		(void) memcpy(&(bsp->bs.bs_ebpb), &(wbpb->ebpb),
-			    sizeof (wbpb->ebpb));
+		    sizeof (wbpb->ebpb));
 #else
 		swap_pack_bpbcpy(&(bsp->bs), wbpb);
 #endif
@@ -3075,7 +3039,7 @@
 		if (SunBPBfields) {
 #ifdef i386
 			(void) memcpy(&(bsp->bs.bs_sebpb), &(wbpb->sunbpb),
-				sizeof (wbpb->sunbpb));
+			    sizeof (wbpb->sunbpb));
 #else
 			swap_pack_sebpbcpy(&(bsp->bs), wbpb);
 #endif
@@ -3102,7 +3066,7 @@
 void
 write_fat(int fd, off64_t seekto, char *fn, char *lbl, char *ffn, bpb_t *wbpb)
 {
-	struct fat32_boot_fsinfo fsinfo;
+	struct fat_od_fsi fsinfo;
 	pc_cluster32_t ffsc;
 	boot_sector_t bootsect;
 	uchar_t *fatp, *rdirp;
@@ -3156,9 +3120,8 @@
 	 */
 	if (!MakeFAT32) {
 		if (Verbose)
-		    (void) printf(
-			gettext("Writing root directory. "
-				"%d bytes.\n"), rdirsize);
+			(void) printf(gettext("Writing root directory. "
+			    "%d bytes.\n"), rdirsize);
 		if (!Notreally) {
 			if (write(fd, rdirp, rdirsize) != rdirsize) {
 				perror(gettext("Root directory write"));
@@ -3174,16 +3137,15 @@
 	if (bootblksize > BPSEC) {
 		if (Verbose)
 			(void) printf(gettext("Writing remainder of "
-				"boot block.\n"));
+			    "boot block.\n"));
 		if (!Notreally)
 			write_rest(wbpb, fn, fd, bsfd, bootblksize - BPSEC);
 	}
 
 	if (MakeFAT32) {
 		if (Verbose)
-		    (void) printf(
-			gettext("Writing root directory. "
-				"%d bytes.\n"), rdirsize);
+			(void) printf(gettext("Writing root directory. "
+			    "%d bytes.\n"), rdirsize);
 		if (!Notreally) {
 			if (write(fd, rdirp, rdirsize) != rdirsize) {
 				perror(gettext("Root directory write"));
@@ -3249,7 +3211,7 @@
 bad_arg(char *option)
 {
 	(void) fprintf(stderr,
-		gettext("Unrecognized option %s.\n"), option);
+	    gettext("Unrecognized option %s.\n"), option);
 	usage();
 	exit(2);
 }
@@ -3259,7 +3221,7 @@
 missing_arg(char *option)
 {
 	(void) fprintf(stderr,
-		gettext("Option %s requires a value.\n"), option);
+	    gettext("Option %s requires a value.\n"), option);
 	usage();
 	exit(3);
 }
@@ -3420,44 +3382,43 @@
 	} else if (SunBPBfields && !BootBlkFn) {
 		(void) fprintf(stderr,
 		    gettext("Use of the 'S' option requires that\n"
-			    "the 'B=' option also be used.\n\n"));
+		    "the 'B=' option also be used.\n\n"));
 		usage();
 	} else if (Firstfileattr != 0x20 && !FirstFn) {
 		(void) fprintf(stderr,
 		    gettext("Use of the 'r', 'h', or 's' options requires\n"
-			    "that the 'i=' option also be used.\n\n"));
+		    "that the 'i=' option also be used.\n\n"));
 		usage();
 	} else if (!GetOffset && !DontUseFdisk) {
 		(void) fprintf(stderr,
 		    gettext("Use of the 'hidden' option requires that\n"
-			    "the 'nofdisk' option also be used.\n\n"));
+		    "the 'nofdisk' option also be used.\n\n"));
 		usage();
 	} else if (DontUseFdisk && GetSize) {
 		(void) fprintf(stderr,
 		    gettext("Use of the 'nofdisk' option requires that\n"
-			    "the 'size=' option also be used.\n\n"));
+		    "the 'size=' option also be used.\n\n"));
 		usage();
 	} else if (!GetBPF &&
-		    BitsPerFAT != 12 && BitsPerFAT != 16 && BitsPerFAT != 32) {
-		(void) fprintf(stderr,
-		    gettext("Invalid Bits/Fat value."
-			    "  Must be 12, 16 or 32.\n"));
+	    BitsPerFAT != 12 && BitsPerFAT != 16 && BitsPerFAT != 32) {
+		(void) fprintf(stderr, gettext("Invalid Bits/Fat value."
+		    " Must be 12, 16 or 32.\n"));
 		exit(2);
 	} else if (!GetSPC && !powerofx_le_y(2, 128, SecPerClust)) {
 		(void) fprintf(stderr,
 		    gettext("Invalid Sectors/Cluster value.  Must be a "
-			    "power of 2 between 1 and 128.\n"));
+		    "power of 2 between 1 and 128.\n"));
 		exit(2);
 	} else if (!GetResrvd && (Resrvd < 1 || Resrvd > 0xffff)) {
 		(void) fprintf(stderr,
 		    gettext("Invalid number of reserved sectors.  "
-			"Must be at least 1 but\nno larger than 65535."));
+		    "Must be at least 1 but\nno larger than 65535."));
 		exit(2);
 	} else if (!GetResrvd && MakeFAT32 &&
-		    (Resrvd < 32 || Resrvd > 0xffff)) {
+	    (Resrvd < 32 || Resrvd > 0xffff)) {
 		(void) fprintf(stderr,
 		    gettext("Invalid number of reserved sectors.  "
-			"Must be at least 32 but\nno larger than 65535."));
+		    "Must be at least 32 but\nno larger than 65535."));
 		exit(2);
 	} else if (Imagesize != 3 && Imagesize != 5) {
 		usage();
@@ -3495,11 +3456,11 @@
 				(void) fprintf(stdout,
 				    gettext("mkfs -F pcfs "));
 				for (opt_count = 1; opt_count < argc;
-								opt_count++) {
+				    opt_count++) {
 					opt_text = argv[opt_count];
 					if (opt_text)
-					    (void) fprintf(stdout, " %s ",
-								opt_text);
+						(void) fprintf(stdout,
+						    " %s ", opt_text);
 				}
 				(void) fprintf(stdout, "\n");
 			}
--- a/usr/src/cmd/fs.d/pcfs/mount/mount.c	Sun Sep 23 11:47:40 2007 -0700
+++ b/usr/src/cmd/fs.d/pcfs/mount/mount.c	Mon Sep 24 06:23:24 2007 -0700
@@ -21,7 +21,7 @@
 #pragma ident	"%Z%%M%	%I%	%E% SMI"
 
 /*
- * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -42,15 +42,33 @@
 
 extern int	daylight;
 
-/*
- * The "hidden/nohidden" option is private. It is not approved for
- * use (see PSARC/1996/443), which is why it is not in the usage message.
- */
 static int roflag = 0;
 static char optbuf[MAX_MNTOPT_STR] = { '\0', };
 static int optsize = 0;
 
-static struct pcfs_args tz;
+
+/*
+ * Since the format/value expected for the mount options listed below
+ * differs between what the user mount command expects and what the
+ * kernel module can grok, we transmogrify the mount option string
+ * for such options. Others are copied through as-is.
+ */
+static char *pcfs_opts[] = { MNTOPT_PCFS_TIMEZONE, NULL };
+#define	ARG_PCFS_TIMEZONE	0
+
+/*
+ * While constructing the mount option string, we need to append
+ * comma separators if there have been previous options copied over
+ * from the input string. This takes care of it.
+ */
+static int
+append_opt(char *str, int strsz, char *opt)
+{
+	if (str[0] != '\0' && strlcat(str, ",", strsz) >= strsz)
+		return (0);
+
+	return (strlcat(str, opt, strsz) < strsz);
+}
 
 int
 main(int argc, char *argv[])
@@ -60,14 +78,18 @@
 	int c;
 	char *myname;
 	char typename[64];
-	char *savedoptbuf;
+	char tzstr[100];
+	char *tzval;
+	char *savedoptbuf = NULL, *savedoptarg = NULL;
+	char *in_arg, *val, *curarg;
 	extern int optind;
 	extern char *optarg;
 	int error = 0;
 	int verbose = 0;
-	int mflg = 0;
+	int mflg = MS_OPTIONSTR;	/* we always pass mount options */
 	int qflg = 0;
 	int optcnt = 0;
+	int tzdone = 0;
 
 	myname = strrchr(argv[0], '/');
 	myname = myname ? myname + 1 : argv[0];
@@ -91,14 +113,39 @@
 			mflg |= MS_NOMNTTAB;
 			break;
 		case 'o':
-			if (strlcpy(optbuf, optarg, sizeof (optbuf)) >=
-			    sizeof (optbuf)) {
+			in_arg = optarg;
+			if ((savedoptarg = strdup(optarg)) == NULL) {
 				(void) fprintf(stderr,
-				    gettext("%s: Invalid argument: %s\n"),
-				    myname, optarg);
-				return (2);
+				    gettext("%s: out of memory\n"), myname);
+				exit(2);
 			}
-			optsize = strlen(optbuf);
+			while (*in_arg != '\0') {
+				curarg = in_arg;
+
+				switch (getsubopt(&in_arg, pcfs_opts, &val)) {
+				case ARG_PCFS_TIMEZONE:
+					if (tzdone || val == NULL)
+						goto invalarg;
+					tzval = val;
+					(void) snprintf(tzstr, 100,
+					    "TZ=%s", tzval);
+					tzstr[99] = '\0';
+					(void) putenv(tzstr);
+					tzdone = 1;
+					break;
+				default:
+					/*
+					 * Remove empty suboptions
+					 * (happens on sequences of commas)
+					 */
+					if (*curarg == '\0')
+						break;
+
+					if (append_opt(optbuf, sizeof (optbuf),
+					    curarg) == 0)
+						goto invalarg;
+				}
+			}
 			break;
 		case 'O':
 			mflg |= MS_OVERLAY;
@@ -131,17 +178,41 @@
 		(void) fprintf(stderr, gettext(
 		    "\tpcfs-specific suboptions are:\n"
 		    "\t     clamptime,noclamptime\n"
-		    "\t     foldcase,nofoldcase\n"));
+		    "\t     hidden,nohidden\n"
+		    "\t     atime,noatime\n"
+		    "\t     foldcase,nofoldcase\n"
+		    "\t     timezone=<valid TZ string>"));
 		exit(32);
 	}
 
 	mnt_special = argv[optind++];
 	mnt_mountp = argv[optind++];
 
-	(void) tzset();
-	tz.secondswest = timezone;
-	tz.dsttime = daylight;
-	mflg |= MS_DATA;
+	/*
+	 * Pass timezone information to the kernel module so that
+	 * FAT timestamps, as per spec, can be recorded in local time.
+	 */
+	tzset();
+	/*
+	 * We perform this validation only in case the user of
+	 * mount(1m) specified the "timezone=..." option. That's
+	 * because we don't want PCFS mounts to fail due to a
+	 * botched $TZ environment variable. If the admin's
+	 * environment contains garbage, it'll just parse as
+	 * GMT (timezone=0).
+	 */
+	if (tzdone && timezone == 0 && altzone == 0 && daylight == 0 &&
+	    strcmp(tzname[0], tzval) &&
+	    strspn(tzname[1], " ") == strlen(tzname[1])) {
+		goto invalarg;
+	}
+	(void) snprintf(tzstr, 100, "timezone=%d", timezone);
+	tzstr[99] = '\0';
+	if (append_opt(optbuf, sizeof (optbuf), tzstr) == 0)
+		goto invalarg;
+
+	optsize = strlen(optbuf);
+
 	if (roflag)
 		mflg |= MS_RDONLY;
 
@@ -158,8 +229,8 @@
 		(void) fprintf(stderr, "mount(%s, \"%s\", %d, %s",
 		    mnt_special, mnt_mountp, mflg, MNTTYPE_PCFS);
 	}
-	if (mount(mnt_special, mnt_mountp, mflg | MS_OPTIONSTR, MNTTYPE_PCFS,
-	    (char *)&tz, sizeof (struct pcfs_args), optbuf, MAX_MNTOPT_STR)) {
+	if (mount(mnt_special, mnt_mountp, mflg, MNTTYPE_PCFS,
+	    NULL, 0, optbuf, MAX_MNTOPT_STR)) {
 		if (errno == EBUSY) {
 			(void) fprintf(stderr, gettext(
 			    "mount: %s is already mounted or %s is busy\n"),
@@ -178,4 +249,9 @@
 		cmp_requested_to_actual_options(savedoptbuf, optbuf,
 		    mnt_special, mnt_mountp);
 	return (0);
+
+invalarg:
+	(void) fprintf(stderr,
+	    gettext("%s: Invalid mount options: %s\n"), myname, savedoptarg);
+	return (2);
 }
--- a/usr/src/uts/common/fs/pcfs/pc_alloc.c	Sun Sep 23 11:47:40 2007 -0700
+++ b/usr/src/uts/common/fs/pcfs/pc_alloc.c	Mon Sep 24 06:23:24 2007 -0700
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -63,91 +63,94 @@
 	pc_cluster32_t cn, ncn;		/* current, next cluster number */
 	daddr_t olcn = lcn;
 
-	PC_DPRINTF2(6, "pc_bmap: pcp=0x%p, lcn=%ld\n", (void *)pcp, lcn);
-
 	vp = PCTOV(pcp);
 	fsp = VFSTOPCFS(vp->v_vfsp);
+
 	if (lcn < 0)
 		return (ENOENT);
+
+	/*
+	 * FAT12 / FAT16 root directories are a continuous section on disk
+	 * before the actual data clusters. Specialcase this here.
+	 */
 	if (!IS_FAT32(fsp) && (vp->v_flag & VROOT)) {
-		daddr_t lbn, bn; /* logical (disk) block number */
+		daddr_t lbn; /* logical (disk) block number */
 
 		lbn = pc_cltodb(fsp, lcn);
 		if (lbn >= fsp->pcfs_rdirsec) {
 			PC_DPRINTF0(2, "pc_bmap: ENOENT1\n");
 			return (ENOENT);
 		}
-		bn = fsp->pcfs_rdirstart + lbn;
-		*dbnp = pc_dbdaddr(fsp, bn);
+		*dbnp = pc_dbdaddr(fsp, fsp->pcfs_rdirstart + lbn);
 		if (contigbp) {
 			ASSERT (*contigbp >= fsp->pcfs_secsize);
 			*contigbp = MIN(*contigbp,
 			    fsp->pcfs_secsize * (fsp->pcfs_rdirsec - lbn));
 		}
-	} else {
+		return (0);
+	}
+
+	if (lcn >= fsp->pcfs_ncluster) {
+		PC_DPRINTF0(2, "pc_bmap: ENOENT2\n");
+		return (ENOENT);
+	}
+	if (vp->v_type == VREG &&
+	    (pcp->pc_size == 0 ||
+	    lcn >= (daddr_t)howmany((offset_t)pcp->pc_size,
+			fsp->pcfs_clsize))) {
+		PC_DPRINTF0(2, "pc_bmap: ENOENT3\n");
+		return (ENOENT);
+	}
+	ncn = pcp->pc_scluster;
+	if (IS_FAT32(fsp) && ncn == 0)
+		ncn = fsp->pcfs_rdirstart;
 
-		if (lcn >= fsp->pcfs_ncluster) {
-			PC_DPRINTF0(2, "pc_bmap: ENOENT2\n");
-			return (ENOENT);
+	/* Do we have a cached index/cluster pair? */
+	if (pcp->pc_lindex > 0 && lcn >= pcp->pc_lindex) {
+		lcn -= pcp->pc_lindex;
+		ncn = pcp->pc_lcluster;
+	}
+	do {
+		cn = ncn;
+		if (!pc_validcl(fsp, cn)) {
+			if (IS_FAT32(fsp) && cn >= PCF_LASTCLUSTER32 &&
+			    vp->v_type == VDIR) {
+				PC_DPRINTF0(2, "pc_bmap: ENOENT4\n");
+				return (ENOENT);
+			} else if (!IS_FAT32(fsp) &&
+			    cn >= PCF_LASTCLUSTER &&
+			    vp->v_type == VDIR) {
+				PC_DPRINTF0(2, "pc_bmap: ENOENT5\n");
+				return (ENOENT);
+			} else {
+				PC_DPRINTF1(1,
+				    "pc_bmap: badfs cn=%d\n", cn);
+				(void) pc_badfs(fsp);
+				return (EIO);
+			}
 		}
-		if (vp->v_type == VREG &&
-		    (pcp->pc_size == 0 ||
-		    lcn >= (daddr_t)howmany((offset_t)pcp->pc_size,
-				fsp->pcfs_clsize))) {
-			PC_DPRINTF0(2, "pc_bmap: ENOENT3\n");
-			return (ENOENT);
-		}
-		ncn = pcp->pc_scluster;
-		if (IS_FAT32(fsp) && ncn == 0)
-			ncn = fsp->pcfs_rdirstart;
+		ncn = pc_getcluster(fsp, cn);
+	} while (lcn--);
 
-		/* Do we have a cached index/cluster pair? */
-		if (pcp->pc_lindex > 0 && lcn >= pcp->pc_lindex) {
-			lcn -= pcp->pc_lindex;
-			ncn = pcp->pc_lcluster;
-		}
-		do {
+	/*
+	 * Cache this cluster, as we'll most likely visit the
+	 * one after this next time.  Considerably improves
+	 * performance on sequential reads and writes.
+	 */
+	pcp->pc_lindex = olcn;
+	pcp->pc_lcluster = cn;
+	*dbnp = pc_cldaddr(fsp, cn);
+
+	if (contigbp && *contigbp > fsp->pcfs_clsize) {
+		uint_t count = fsp->pcfs_clsize;
+
+		while ((cn + 1) == ncn && count < *contigbp &&
+		    pc_validcl(fsp, ncn)) {
+			count += fsp->pcfs_clsize;
 			cn = ncn;
-			if (!pc_validcl(fsp, cn)) {
-				if (IS_FAT32(fsp) && cn >= PCF_LASTCLUSTER32 &&
-				    vp->v_type == VDIR) {
-					PC_DPRINTF0(2, "pc_bmap: ENOENT4\n");
-					return (ENOENT);
-				} else if (!IS_FAT32(fsp) &&
-				    cn >= PCF_LASTCLUSTER &&
-				    vp->v_type == VDIR) {
-					PC_DPRINTF0(2, "pc_bmap: ENOENT5\n");
-					return (ENOENT);
-				} else {
-					PC_DPRINTF1(1,
-					    "pc_bmap: badfs cn=%d\n", cn);
-					(void) pc_badfs(fsp);
-					return (EIO);
-				}
-			}
-			ncn = pc_getcluster(fsp, cn);
-		} while (lcn--);
-
-		/*
-		 * Cache this cluster, as we'll most likely visit the
-		 * one after this next time.  Considerably improves
-		 * performance on sequential reads and writes.
-		 */
-		pcp->pc_lindex = olcn;
-		pcp->pc_lcluster = cn;
-		*dbnp = pc_cldaddr(fsp, cn);
-
-		if (contigbp && *contigbp > fsp->pcfs_clsize) {
-			uint_t count = fsp->pcfs_clsize;
-
-			while ((cn + 1) == ncn && count < *contigbp &&
-			    pc_validcl(fsp, ncn)) {
-				count += fsp->pcfs_clsize;
-				cn = ncn;
-				ncn = pc_getcluster(fsp, ncn);
-			}
-			*contigbp = count;
+			ncn = pc_getcluster(fsp, ncn);
 		}
+		*contigbp = count;
 	}
 	return (0);
 }
@@ -165,8 +168,8 @@
 {
 	struct pcfs *fsp;	/* pcfs that file is in */
 	struct vnode *vp;
-
-	PC_DPRINTF2(5, "pc_balloc: pcp=0x%p, lcn=%ld\n", (void *)pcp, lcn);
+	pc_cluster32_t cn;	/* current cluster number */
+	pc_cluster32_t ncn;	/* next cluster number */
 
 	vp = PCTOV(pcp);
 	fsp = VFSTOPCFS(vp -> v_vfsp);
@@ -175,6 +178,9 @@
 		return (EFBIG);
 	}
 
+	/*
+	 * Again, FAT12/FAT16 root directories are not data clusters.
+	 */
 	if (!IS_FAT32(fsp) && (vp->v_flag & VROOT)) {
 		daddr_t lbn;
 
@@ -182,65 +188,64 @@
 		if (lbn >= fsp->pcfs_rdirsec)
 			return (ENOSPC);
 		*dbnp = pc_dbdaddr(fsp, fsp->pcfs_rdirstart + lbn);
-	} else {
-		pc_cluster32_t cn;	/* current cluster number */
-		pc_cluster32_t ncn;	/* next cluster number */
+		return (0);
+	}
 
-		if (lcn >= fsp->pcfs_ncluster)
+	if (lcn >= fsp->pcfs_ncluster)
+		return (ENOSPC);
+	if ((vp->v_type == VREG && pcp->pc_size == 0) ||
+	    (vp->v_type == VDIR && lcn == 0)) {
+		switch (cn = pc_alloccluster(fsp, 1)) {
+		case PCF_FREECLUSTER:
 			return (ENOSPC);
-		if ((vp->v_type == VREG && pcp->pc_size == 0) ||
-		    (vp->v_type == VDIR && lcn == 0)) {
-			switch (cn = pc_alloccluster(fsp, 1)) {
+		case PCF_ERRORCLUSTER:
+			return (EIO);
+		}
+		pcp->pc_scluster = cn;
+	} else {
+		cn = pcp->pc_scluster;
+		if (IS_FAT32(fsp) && cn == 0)
+			cn = fsp->pcfs_rdirstart;
+		if (!pc_validcl(fsp, cn)) {
+			PC_DPRINTF1(1, "pc_balloc: badfs cn=%d\n", cn);
+			(void) pc_badfs(fsp);
+			return (EIO);
+		}
+	}
+
+	if (pcp->pc_lindex > 0 && lcn > pcp->pc_lindex) {
+		lcn -= pcp->pc_lindex;
+		cn = pcp->pc_lcluster;
+	}
+	while (lcn-- > 0) {
+		ncn = pc_getcluster(fsp, cn);
+		if ((IS_FAT32(fsp) && ncn >= PCF_LASTCLUSTER32) ||
+		    (!IS_FAT32(fsp) && ncn >= PCF_LASTCLUSTER)) {
+			/*
+			 * Extend file (no holes).
+			 */
+			switch (ncn = pc_alloccluster(fsp, zwrite)) {
 			case PCF_FREECLUSTER:
 				return (ENOSPC);
 			case PCF_ERRORCLUSTER:
 				return (EIO);
 			}
-			pcp->pc_scluster = cn;
-		} else {
-			cn = pcp->pc_scluster;
-			if (IS_FAT32(fsp) && cn == 0)
-				cn = fsp->pcfs_rdirstart;
-			if (!pc_validcl(fsp, cn)) {
-				PC_DPRINTF1(1, "pc_balloc: badfs cn=%d\n", cn);
-				(void) pc_badfs(fsp);
-				return (EIO);
-			}
-		}
-
-		if (pcp->pc_lindex > 0 && lcn > pcp->pc_lindex) {
-			lcn -= pcp->pc_lindex;
-			cn = pcp->pc_lcluster;
+			pc_setcluster(fsp, cn, ncn);
+		} else if (!pc_validcl(fsp, ncn)) {
+			PC_DPRINTF1(1,
+			    "pc_balloc: badfs ncn=%d\n", ncn);
+			(void) pc_badfs(fsp);
+			return (EIO);
 		}
-		while (lcn-- > 0) {
-			ncn = pc_getcluster(fsp, cn);
-			if ((IS_FAT32(fsp) && ncn >= PCF_LASTCLUSTER32) ||
-			    (!IS_FAT32(fsp) && ncn >= PCF_LASTCLUSTER)) {
-				/*
-				 * Extend file (no holes).
-				 */
-				switch (ncn = pc_alloccluster(fsp, zwrite)) {
-				case PCF_FREECLUSTER:
-					return (ENOSPC);
-				case PCF_ERRORCLUSTER:
-					return (EIO);
-				}
-				pc_setcluster(fsp, cn, ncn);
-			} else if (!pc_validcl(fsp, ncn)) {
-				PC_DPRINTF1(1,
-				    "pc_balloc: badfs ncn=%d\n", ncn);
-				(void) pc_badfs(fsp);
-				return (EIO);
-			}
-			cn = ncn;
-		}
-		/*
-		 * Do not cache the new cluster/index values; when
-		 * extending the file we're interested in the last
-		 * written cluster and not the last cluster allocated.
-		 */
-		*dbnp = pc_cldaddr(fsp, cn);
+		cn = ncn;
 	}
+	/*
+	 * Do not cache the new cluster/index values; when
+	 * extending the file we're interested in the last
+	 * written cluster and not the last cluster allocated.
+	 */
+	*dbnp = pc_cldaddr(fsp, cn);
+
 	return (0);
 }
 
@@ -262,9 +267,6 @@
 		panic("pc_bfree");
 	}
 
-	PC_DPRINTF2(5, "pc_bfree: pcp=0x%p, after first %d clusters\n",
-	    (void *)pcp, skipcl);
-
 	if (pcp->pc_size == 0 && vp->v_type == VREG) {
 		return (0);
 	}
@@ -333,8 +335,8 @@
 	int free = 0;
 
 	if (IS_FAT32(fsp) &&
-	    fsp->fsinfo_native.fs_free_clusters != FSINFO_UNKNOWN)
-		return (fsp->fsinfo_native.fs_free_clusters);
+	    fsp->pcfs_fsinfo.fs_free_clusters != FSINFO_UNKNOWN)
+		return (fsp->pcfs_fsinfo.fs_free_clusters);
 
 	/*
 	 * make sure the FAT is in core
@@ -347,8 +349,8 @@
 	}
 
 	if (IS_FAT32(fsp)) {
-		ASSERT(fsp->fsinfo_native.fs_free_clusters == FSINFO_UNKNOWN);
-		fsp->fsinfo_native.fs_free_clusters = free;
+		ASSERT(fsp->pcfs_fsinfo.fs_free_clusters == FSINFO_UNKNOWN);
+		fsp->pcfs_fsinfo.fs_free_clusters = free;
 	}
 	return (free);
 }
@@ -367,17 +369,19 @@
 {
 	unsigned char *fp;
 
-	PC_DPRINTF1(7, "pc_getcluster: cn=%x ", cn);
 	if (fsp->pcfs_fatp == (uchar_t *)0 || !pc_validcl(fsp, cn))
 		panic("pc_getcluster");
 
-	if (IS_FAT32(fsp)) {	/* 32 bit FAT */
+	switch (fsp->pcfs_fattype) {
+	case FAT32:
 		fp = fsp->pcfs_fatp + (cn << 2);
 		cn = ltohi(*(pc_cluster32_t *)fp);
-	} else if (fsp->pcfs_flags & PCFS_FAT16) {	/* 16 bit FAT */
+		break;
+	case FAT16:
 		fp = fsp->pcfs_fatp + (cn << 1);
 		cn = ltohs(*(pc_cluster16_t *)fp);
-	} else {	/* 12 bit FAT */
+		break;
+	case FAT12:
 		fp = fsp->pcfs_fatp + (cn + (cn >> 1));
 		if (cn & 01) {
 			cn = (((unsigned int)*fp++ & 0xf0) >> 4);
@@ -388,8 +392,11 @@
 		}
 		if (cn >= PCF_12BCLUSTER)
 			cn |= PCF_RESCLUSTER;
+		break;
+	default:
+		pc_mark_irrecov(fsp);
+		cn = PCF_ERRORCLUSTER;
 	}
-	PC_DPRINTF1(7, " %x\n", cn);
 	return (cn);
 }
 
@@ -402,22 +409,23 @@
 pc_setcluster(struct pcfs *fsp, pc_cluster32_t cn, pc_cluster32_t ncn)
 {
 	unsigned char *fp;
+	pc_cluster16_t ncn16;
 
-	PC_DPRINTF2(7, "pc_setcluster: cn=%d ncn=%d\n", cn, ncn);
 	if (fsp->pcfs_fatp == (uchar_t *)0 || !pc_validcl(fsp, cn))
 		panic("pc_setcluster");
 	fsp->pcfs_flags |= PCFS_FATMOD;
 	pc_mark_fat_updated(fsp, cn);
-	if (IS_FAT32(fsp)) {	/* 32 bit FAT */
+	switch (fsp->pcfs_fattype) {
+	case FAT32:
 		fp = fsp->pcfs_fatp + (cn << 2);
 		*(pc_cluster32_t *)fp = htoli(ncn);
-	} else if (fsp->pcfs_flags & PCFS_FAT16) {	/* 16 bit FAT */
-		pc_cluster16_t	ncn16;
-
+		break;
+	case FAT16:
 		fp = fsp->pcfs_fatp + (cn << 1);
 		ncn16 = (pc_cluster16_t)ncn;
 		*(pc_cluster16_t *)fp = htols(ncn16);
-	} else {	/* 12 bit FAT */
+		break;
+	case FAT12:
 		fp = fsp->pcfs_fatp + (cn + (cn >> 1));
 		if (cn & 01) {
 			*fp = (*fp & 0x0f) | ((ncn << 4) & 0xf0);
@@ -427,13 +435,16 @@
 			*fp++ = ncn & 0xff;
 			*fp = (*fp & 0xf0) | ((ncn >> 8) & 0x0f);
 		}
+		break;
+	default:
+		pc_mark_irrecov(fsp);
 	}
 	if (ncn == PCF_FREECLUSTER) {
 		fsp->pcfs_nxfrecls = PCF_FIRSTCLUSTER;
 		if (IS_FAT32(fsp)) {
-			if (fsp->fsinfo_native.fs_free_clusters !=
+			if (fsp->pcfs_fsinfo.fs_free_clusters !=
 			    FSINFO_UNKNOWN)
-				fsp->fsinfo_native.fs_free_clusters++;
+				fsp->pcfs_fsinfo.fs_free_clusters++;
 		}
 	}
 }
@@ -459,9 +470,9 @@
 
 			if (IS_FAT32(fsp)) {
 				pc_setcluster(fsp, cn, PCF_LASTCLUSTERMARK32);
-				if (fsp->fsinfo_native.fs_free_clusters !=
+				if (fsp->pcfs_fsinfo.fs_free_clusters !=
 				    FSINFO_UNKNOWN)
-					fsp->fsinfo_native.fs_free_clusters--;
+					fsp->pcfs_fsinfo.fs_free_clusters--;
 			} else
 				pc_setcluster(fsp, cn, PCF_LASTCLUSTERMARK);
 			if (zwrite) {
@@ -477,15 +488,11 @@
 				error = geterror(bp);
 				brelse(bp);
 				if (error) {
-					PC_DPRINTF0(1,
-					    "pc_alloccluster: error\n");
 					pc_mark_irrecov(fsp);
 					return (PCF_ERRORCLUSTER);
 				}
 			}
 			fsp->pcfs_nxfrecls = cn + 1;
-			PC_DPRINTF1(5, "pc_alloccluster: new cluster = %d\n",
-			    cn);
 			return (cn);
 		}
 	}
--- a/usr/src/uts/common/fs/pcfs/pc_node.c	Sun Sep 23 11:47:40 2007 -0700
+++ b/usr/src/uts/common/fs/pcfs/pc_node.c	Mon Sep 24 06:23:24 2007 -0700
@@ -242,7 +242,7 @@
 		}
 		err = syncpcp(pcp, B_INVAL);
 		if (err) {
-			(void) syncpcp(pcp, B_INVAL|B_FORCE);
+			(void) syncpcp(pcp, B_INVAL | B_FORCE);
 		}
 	}
 	if (vn_has_cached_data(vp)) {
@@ -300,38 +300,55 @@
 /*
  * Mark a pcnode as modified with the current time.
  */
+/* ARGSUSED */
 void
-pc_mark_mod(struct pcnode *pcp)
+pc_mark_mod(struct pcfs *fsp, struct pcnode *pcp)
 {
 	timestruc_t now;
 
-	if (PCTOV(pcp)->v_type == VREG) {
-		gethrestime(&now);
-		if (pc_tvtopct(&now, &pcp->pc_entry.pcd_mtime))
-			PC_DPRINTF1(2, "pc_mark_mod failed timestamp "
-			    "conversion, curtime = %lld\n",
-			    (long long)now.tv_sec);
-		pcp->pc_flags |= PC_CHG;
-	}
+	if (PCTOV(pcp)->v_type == VDIR)
+		return;
+
+	ASSERT(PCTOV(pcp)->v_type == VREG);
+
+	gethrestime(&now);
+	if (pc_tvtopct(&now, &pcp->pc_entry.pcd_mtime))
+		PC_DPRINTF1(2, "pc_mark_mod failed timestamp "
+		    "conversion, curtime = %lld\n",
+		    (long long)now.tv_sec);
+
+	pcp->pc_flags |= PC_CHG;
 }
 
 /*
  * Mark a pcnode as accessed with the current time.
  */
 void
-pc_mark_acc(struct pcnode *pcp)
+pc_mark_acc(struct pcfs *fsp, struct pcnode *pcp)
 {
 	struct pctime pt = { 0, 0 };
 	timestruc_t now;
 
-	if (PCTOV(pcp)->v_type == VREG) {
-		gethrestime(&now);
-		if (pc_tvtopct(&now, &pt))
-			PC_DPRINTF1(2, "pc_mark_acc failed timestamp "
-			    "conversion, curtime = %lld\n",
-			    (long long)now.tv_sec);
+	if (fsp->pcfs_flags & PCFS_NOATIME || PCTOV(pcp)->v_type == VDIR)
+		return;
+
+	ASSERT(PCTOV(pcp)->v_type == VREG);
+
+	gethrestime(&now);
+	if (pc_tvtopct(&now, &pt)) {
+		PC_DPRINTF1(2, "pc_mark_acc failed timestamp "
+		    "conversion, curtime = %lld\n",
+		    (long long)now.tv_sec);
+		return;
+	}
+
+	/*
+	 * We don't really want to write the adate for every access
+	 * on flash media; make sure it really changed !
+	 */
+	if (pcp->pc_entry.pcd_ladate != pt.pct_date) {
 		pcp->pc_entry.pcd_ladate = pt.pct_date;
-		pcp->pc_flags |= PC_CHG;
+		pcp->pc_flags |= (PC_CHG | PC_ACC);
 	}
 }
 
@@ -365,7 +382,7 @@
 	 */
 	if (length > pcp->pc_size) {
 		daddr_t bno;
-		uint_t llcn;
+		uint_t llcn = howmany((offset_t)length, fsp->pcfs_clsize);
 
 		/*
 		 * We are extending a file.
@@ -373,8 +390,7 @@
 		 * since we don't need to use the block number(s).
 		 */
 		if ((daddr_t)howmany((offset_t)pcp->pc_size, fsp->pcfs_clsize) <
-		    (llcn = (daddr_t)howmany((offset_t)length,
-				fsp->pcfs_clsize))) {
+		    (daddr_t)llcn) {
 			error = pc_balloc(pcp, (daddr_t)(llcn - 1), 1, &bno);
 		}
 		if (error) {
@@ -405,7 +421,7 @@
 			 * end of the file.
 			 */
 			(void) pvn_vplist_dirty(PCTOV(pcp), (u_offset_t)length,
-				pcfs_putapage, B_INVAL | B_TRUNC, CRED());
+			    pcfs_putapage, B_INVAL | B_TRUNC, CRED());
 		} else {
 			/*
 			 * pvn_vpzero() cannot deal with more than MAXBSIZE
@@ -424,12 +440,16 @@
 			    (u_offset_t)length + nbytes,
 			    pcfs_putapage, B_INVAL | B_TRUNC, CRED());
 		}
-		error = pc_bfree(pcp,
-		    (pc_cluster32_t)howmany((offset_t)length,
-			    fsp->pcfs_clsize));
+		error = pc_bfree(pcp, (pc_cluster32_t)
+		    howmany((offset_t)length, fsp->pcfs_clsize));
 		pcp->pc_size = length;
 	}
-	pc_mark_mod(pcp);
+
+	/*
+	 * This is the only place in PCFS code where pc_mark_mod() is called
+	 * without setting PC_MOD. May be a historical artifact ...
+	 */
+	pc_mark_mod(fsp, pcp);
 	return (error);
 }
 
@@ -441,7 +461,6 @@
 {
 	struct pcfs *fsp;
 
-	PC_DPRINTF0(7, "pc_getentryblock ");
 	fsp = VFSTOPCFS(PCTOV(pcp)->v_vfsp);
 	if (pcp->pc_eblkno >= fsp->pcfs_datastart ||
 	    (pcp->pc_eblkno - fsp->pcfs_rdirstart) <
@@ -451,11 +470,10 @@
 	} else {
 		*bpp = bread(fsp->pcfs_xdev,
 		    pc_dbdaddr(fsp, pcp->pc_eblkno),
-		    (int)(fsp->pcfs_datastart-pcp->pc_eblkno) *
+		    (int)(fsp->pcfs_datastart - pcp->pc_eblkno) *
 		    fsp->pcfs_secsize);
 	}
 	if ((*bpp)->b_flags & B_ERROR) {
-		PC_DPRINTF0(1, "pc_getentryblock: error ");
 		brelse(*bpp);
 		pc_mark_irrecov(fsp);
 		return (EIO);
@@ -475,7 +493,6 @@
 	int err;
 	struct vnode *vp;
 
-	PC_DPRINTF1(7, "pc_nodesync pcp=0x%p\n", (void *)pcp);
 	vp = PCTOV(pcp);
 	fsp = VFSTOPCFS(vp->v_vfsp);
 	err = 0;
@@ -540,11 +557,9 @@
 	*((struct pcdir *)(bp->b_un.b_addr + pcp->pc_eoffset)) = pcp->pc_entry;
 	bwrite2(bp);
 	error = geterror(bp);
-	if (error)
-		error = EIO;
 	brelse(bp);
 	if (error) {
-		PC_DPRINTF0(1, "pc_nodeupdate ERROR\n");
+		error = EIO;
 		pc_mark_irrecov(VFSTOPCFS(vp->v_vfsp));
 	}
 	pcp->pc_flags &= ~(PC_CHG | PC_MOD | PC_ACC);
@@ -556,7 +571,6 @@
  * got the pcnode from.
  * MUST be called with node unlocked.
  */
-/* ARGSUSED */
 int
 pc_verify(struct pcfs *fsp)
 {
@@ -567,13 +581,25 @@
 		return (EIO);
 
 	if (!(fsp->pcfs_flags & PCFS_NOCHK) && fsp->pcfs_fatp) {
+		/*
+		 * This "has it been removed" check should better be
+		 * modified for removeable media that are not floppies.
+		 * dkio-managed devices such as USB/firewire external
+		 * disks/memory sticks/floppies (gasp) do not understand
+		 * this ioctl.
+		 */
 		PC_DPRINTF1(4, "pc_verify fsp=0x%p\n", (void *)fsp);
 		error = cdev_ioctl(fsp->pcfs_vfs->vfs_dev,
-		    FDGETCHANGE, (intptr_t)&fdstatus, FNATIVE|FKIOCTL,
+		    FDGETCHANGE, (intptr_t)&fdstatus, FNATIVE | FKIOCTL,
 		    NULL, NULL);
 
 		if (error) {
 			if (error == ENOTTY || error == ENXIO) {
+				/*
+				 * See comment above. This is a workaround
+				 * for removeable media that don't understand
+				 * floppy ioctls.
+				 */
 				error = 0;
 			} else {
 				PC_DPRINTF1(1,
@@ -607,7 +633,7 @@
 			}
 		}
 	}
-	if (!(error || fsp->pcfs_fatp)) {
+	if (error == 0 && fsp->pcfs_fatp == NULL) {
 		error = pc_getfat(fsp);
 	}
 
@@ -636,11 +662,11 @@
 
 		fsp->pcfs_flags |= PCFS_IRRECOV;
 		cmn_err(CE_WARN,
-			"Disk was changed during an update or\n"
-			"an irrecoverable error was encountered.\n"
-			"File damage is possible.  To prevent further\n"
-			"damage, this pcfs instance will now be frozen.\n"
-			"Use umount(1M) to release the instance.\n");
+		    "Disk was changed during an update or\n"
+		    "an irrecoverable error was encountered.\n"
+		    "File damage is possible.  To prevent further\n"
+		    "damage, this pcfs instance will now be frozen.\n"
+		    "Use umount(1M) to release the instance.\n");
 		(void) pc_unlockfs(fsp);
 	}
 }
--- a/usr/src/uts/common/fs/pcfs/pc_vfsops.c	Sun Sep 23 11:47:40 2007 -0700
+++ b/usr/src/uts/common/fs/pcfs/pc_vfsops.c	Mon Sep 24 06:23:24 2007 -0700
@@ -40,7 +40,6 @@
 #include <sys/file.h>
 #include <sys/uio.h>
 #include <sys/conf.h>
-#undef NFSCLIENT
 #include <sys/statvfs.h>
 #include <sys/mount.h>
 #include <sys/pathname.h>
@@ -64,6 +63,7 @@
 #include <sys/mntent.h>
 #include <sys/policy.h>
 #include <sys/atomic.h>
+#include <sys/sdt.h>
 
 /*
  * The majority of PC media use a 512 sector size, but
@@ -87,11 +87,13 @@
 static int pcfs_vget(struct vfs *vfsp, struct vnode **vpp, struct fid *fidp);
 static void pcfs_freevfs(vfs_t *vfsp);
 
-static int pc_getfattype(struct vnode *, int, daddr_t *, int *);
-static int pc_readfat(struct pcfs *fsp, uchar_t *fatp, daddr_t start,
-    size_t fatsize);
+static int pc_readfat(struct pcfs *fsp, uchar_t *fatp);
 static int pc_writefat(struct pcfs *fsp, daddr_t start);
 
+static int pc_getfattype(struct pcfs *fsp);
+static void pcfs_parse_mntopts(struct pcfs *fsp, struct mounta *uap);
+
+
 /*
  * pcfs mount options table
  */
@@ -102,6 +104,8 @@
 static char *foldcase_cancel[] = { MNTOPT_PCFS_NOFOLDCASE, NULL };
 static char *clamptime_cancel[] = { MNTOPT_PCFS_NOCLAMPTIME, NULL };
 static char *noclamptime_cancel[] = { MNTOPT_PCFS_CLAMPTIME, NULL };
+static char *atime_cancel[] = { MNTOPT_NOATIME, NULL };
+static char *noatime_cancel[] = { MNTOPT_ATIME, NULL };
 
 static mntopt_t mntopts[] = {
 /*
@@ -112,7 +116,11 @@
 	{ MNTOPT_PCFS_NOFOLDCASE, nofoldcase_cancel, NULL, MO_DEFAULT, NULL },
 	{ MNTOPT_PCFS_FOLDCASE, foldcase_cancel, NULL, 0, NULL },
 	{ MNTOPT_PCFS_CLAMPTIME, clamptime_cancel, NULL, MO_DEFAULT, NULL },
-	{ MNTOPT_PCFS_NOCLAMPTIME, noclamptime_cancel, NULL, NULL, NULL }
+	{ MNTOPT_PCFS_NOCLAMPTIME, noclamptime_cancel, NULL, NULL, NULL },
+	{ MNTOPT_NOATIME, noatime_cancel, NULL, NULL, NULL },
+	{ MNTOPT_ATIME, atime_cancel, NULL, NULL, NULL },
+	{ MNTOPT_PCFS_TIMEZONE, NULL, "+0", MO_DEFAULT | MO_HASVALUE, NULL },
+	{ MNTOPT_PCFS_SECSIZE, NULL, NULL, MO_HASVALUE, NULL }
 };
 
 static mntopts_t pcfs_mntopts = {
@@ -157,7 +165,7 @@
 
 static struct modlfs modlfs = {
 	&mod_fsops,
-	"PC filesystem v1.100",
+	"PC filesystem v1.2",
 	&vfw
 };
 
@@ -273,99 +281,42 @@
  */
 #define	BOOT_PARTITION_DRIVE	99
 #define	PRIMARY_DOS_DRIVE	1
-
-/*
- * pc_mount system call
- */
-static int
-pcfs_mount(
-	struct vfs *vfsp,
-	struct vnode *mvp,
-	struct mounta *uap,
-	struct cred *cr)
-{
-	struct pcfs *fsp;
-	struct vnode *bvp;
-	struct vnode *devvp;
-	struct pathname special;
-	daddr_t dosstart;
-	dev_t pseudodev;
-	dev_t xdev;
-	char *c;
-	char *data = uap->dataptr;
-	int datalen = uap->datalen;
-	int dos_ldrive = 0;
-	int error;
-	int fattype;
-	minor_t	minor;
-	int oflag, aflag;
-
-	if ((error = secpolicy_fs_mount(cr, mvp, vfsp)) != 0)
-		return (error);
+#define	UNPARTITIONED_DRIVE	0
 
-	PC_DPRINTF0(4, "pcfs_mount\n");
-	if (mvp->v_type != VDIR) {
-		return (ENOTDIR);
-	}
-	mutex_enter(&mvp->v_lock);
-	if ((uap->flags & MS_REMOUNT) == 0 &&
-	    (uap->flags & MS_OVERLAY) == 0 &&
-	    (mvp->v_count != 1 || (mvp->v_flag & VROOT))) {
-		mutex_exit(&mvp->v_lock);
-		return (EBUSY);
-	}
-	mutex_exit(&mvp->v_lock);
+static int
+pcfs_device_identify(
+	struct vfs *vfsp,
+	struct mounta *uap,
+	struct cred *cr,
+	int *dos_ldrive,
+	dev_t *xdev)
+{
+	struct pathname special;
+	char *c;
+	struct vnode *bvp;
+	int oflag, aflag;
+	int error;
 
 	/*
-	 * The caller is responsible for making sure to always
-	 * pass in sizeof(struct pcfs_args) (or the old one).
-	 * Doing this is the only way to know an EINVAL return
-	 * from mount(2) is due to the "not a DOS filesystem"
-	 * EINVAL that pc_verify/pc_getfattype could return.
-	 */
-	if ((datalen != sizeof (struct pcfs_args)) &&
-	    (datalen != sizeof (struct old_pcfs_args))) {
-		return (EINVAL);
-	} else {
-		struct pcfs_args tmp_tz;
-		int hidden = 0;
-		int foldcase = 0;
-		int noclamptime = 0;
-
-		tmp_tz.flags = 0;
-		if (copyin(data, &tmp_tz, datalen)) {
-			return (EFAULT);
-		}
-		if (datalen == sizeof (struct pcfs_args)) {
-			hidden = tmp_tz.flags & PCFS_MNT_HIDDEN;
-			foldcase = tmp_tz.flags & PCFS_MNT_FOLDCASE;
-			noclamptime = tmp_tz.flags & PCFS_MNT_NOCLAMPTIME;
-		}
-
-		if (hidden)
-			vfs_setmntopt(vfsp, MNTOPT_PCFS_HIDDEN,	NULL, 0);
-		if (foldcase)
-			vfs_setmntopt(vfsp, MNTOPT_PCFS_FOLDCASE, NULL, 0);
-		if (noclamptime)
-			vfs_setmntopt(vfsp, MNTOPT_PCFS_NOCLAMPTIME, NULL, 0);
-		/*
-		 * more than one pc filesystem can be mounted on x86
-		 * so the pc_tz structure is now a critical region
-		 */
-		mutex_enter(&pcfslock);
-		if (pc_mounttab == NULL)
-			bcopy(&tmp_tz, &pc_tz, sizeof (struct pcfs_args));
-		mutex_exit(&pcfslock);
-	}
-	/*
 	 * Resolve path name of special file being mounted.
 	 */
 	if (error = pn_get(uap->spec, UIO_USERSPACE, &special)) {
 		return (error);
 	}
+
+	*dos_ldrive = -1;
+
 	if (error =
 	    lookupname(special.pn_path, UIO_SYSSPACE, FOLLOW, NULLVPP, &bvp)) {
 		/*
+		 * If there's no device node, the name specified most likely
+		 * maps to a PCFS-style "partition specifier" to select a
+		 * harddisk primary/logical partition. Disable floppy-specific
+		 * checks in such cases unless an explicit :A or :B is
+		 * requested.
+		 */
+
+		/*
 		 * Split the pathname string at the last ':' separator.
 		 * If there's no ':' in the device name, or the ':' is the
 		 * last character in the string, the name is invalid and
@@ -382,12 +333,13 @@
 		 *	- "boot" to indicate the X86BOOT partition
 		 *	- a drive letter [c-z] for the "DOS logical drive"
 		 *	- a drive number 1..24 for the "DOS logical drive"
+		 *	- a "floppy name letter", 'a' or 'b' (just strip this)
 		 */
 		if (strcasecmp(c, "boot") == 0) {
 			/*
 			 * The Solaris boot partition is requested.
 			 */
-			dos_ldrive = BOOT_PARTITION_DRIVE;
+			*dos_ldrive = BOOT_PARTITION_DRIVE;
 		} else if (strspn(c, "0123456789") == strlen(c)) {
 			/*
 			 * All digits - parse the partition number.
@@ -399,21 +351,30 @@
 				 * A number alright - in the allowed range ?
 				 */
 				if (drvnum > 24 || drvnum == 0)
-					error = EINVAL;
+					error = ENXIO;
 			}
 			if (error)
 				goto devlookup_done;
-			dos_ldrive = (int)drvnum;
+			*dos_ldrive = (int)drvnum;
 		} else if (strlen(c) == 1) {
 			/*
-			 * A single trailing character - is it [c-zC-Z] ?
+			 * A single trailing character was specified.
+			 *	- [c-zC-Z] means a harddisk partition, and
+			 *	  we retrieve the partition number.
+			 *	- [abAB] means a floppy drive, so we swallow
+			 *	  the "drive specifier" and test later
+			 *	  whether the physical device is a floppy or
+			 *	  PCMCIA pseudofloppy (sram card).
 			 */
 			*c = tolower(*c);
-			if (*c < 'c' || *c > 'z') {
-				error = EINVAL;
+			if (*c == 'a' || *c == 'b') {
+				*dos_ldrive = UNPARTITIONED_DRIVE;
+			} else if (*c < 'c' || *c > 'z') {
+				error = ENXIO;
 				goto devlookup_done;
+			} else {
+				*dos_ldrive = 1 + *c - 'c';
 			}
-			dos_ldrive = 1 + *c - 'c';
 		} else {
 			/*
 			 * Can't parse this - pass through previous error.
@@ -421,21 +382,21 @@
 			goto devlookup_done;
 		}
 
-		ASSERT(dos_ldrive > 0);
 
 		error = lookupname(special.pn_path, UIO_SYSSPACE, FOLLOW,
 		    NULLVPP, &bvp);
+	} else {
+		*dos_ldrive = UNPARTITIONED_DRIVE;
 	}
 devlookup_done:
 	pn_free(&special);
 	if (error)
 		return (error);
 
-	if (bvp->v_type != VBLK) {
-		VN_RELE(bvp);
-		return (ENOTBLK);
-	}
-	xdev = bvp->v_rdev;
+	ASSERT(*dos_ldrive >= UNPARTITIONED_DRIVE);
+
+	*xdev = bvp->v_rdev;
+
 	/*
 	 * Verify caller's permission to open the device special file.
 	 */
@@ -447,30 +408,52 @@
 		oflag = FREAD | FWRITE;
 		aflag = VREAD | VWRITE;
 	}
-	if ((error = VOP_ACCESS(bvp, aflag, 0, cr)) != 0 ||
+
+	if (bvp->v_type != VBLK)
+		error = ENOTBLK;
+	else if (getmajor(*xdev) >= devcnt)
+		error = ENXIO;
+
+	if ((error != 0) ||
+	    (error = VOP_ACCESS(bvp, aflag, 0, cr)) != 0 ||
 	    (error = secpolicy_spec_open(cr, bvp, oflag)) != 0) {
 		VN_RELE(bvp);
 		return (error);
 	}
 
 	VN_RELE(bvp);
-	if (getmajor(xdev) >= devcnt) {
-		return (ENXIO);
-	}
+	return (0);
+}
+
+static int
+pcfs_device_ismounted(
+	struct vfs *vfsp,
+	int dos_ldrive,
+	dev_t xdev,
+	int *remounting,
+	dev_t *pseudodev)
+{
+	struct pcfs *fsp;
+	int remount = *remounting;
+
 	/*
 	 * Ensure that this logical drive isn't already mounted, unless
 	 * this is a REMOUNT request.
 	 * Note: The framework will perform this check if the "...:c"
 	 * PCFS-style "logical drive" syntax has not been used and an
 	 * actually existing physical device is backing this filesystem.
+	 * Once all block device drivers support PC-style partitioning,
+	 * this codeblock can be dropped.
 	 */
+	*pseudodev = xdev;
+
 	if (dos_ldrive) {
 		mutex_enter(&pcfslock);
 		for (fsp = pc_mounttab; fsp; fsp = fsp->pcfs_nxt)
 			if (fsp->pcfs_xdev == xdev &&
-			    fsp->pcfs_ldrv == dos_ldrive) {
+			    fsp->pcfs_ldrive == dos_ldrive) {
 				mutex_exit(&pcfslock);
-				if (uap->flags & MS_REMOUNT) {
+				if (remount) {
 					return (0);
 				} else {
 					return (EBUSY);
@@ -496,15 +479,15 @@
 		if (((getminor(xdev) >> 12) & 0x3F) != 0)
 			printf("whoops - upper bits used!\n");
 #endif
-		minor = ((dos_ldrive << 12) | getminor(xdev)) & MAXMIN32;
-		pseudodev = makedevice(getmajor(xdev), minor);
-		if (vfs_devmounting(pseudodev, vfsp)) {
+		*pseudodev = makedevice(getmajor(xdev),
+		    ((dos_ldrive << 12) | getminor(xdev)) & MAXMIN32);
+		if (vfs_devmounting(*pseudodev, vfsp)) {
 			mutex_exit(&pcfslock);
 			return (EBUSY);
 		}
-		if (vfs_devismounted(pseudodev)) {
+		if (vfs_devismounted(*pseudodev)) {
 			mutex_exit(&pcfslock);
-			if (uap->flags & MS_REMOUNT) {
+			if (remount) {
 				return (0);
 			} else {
 				return (EBUSY);
@@ -512,91 +495,289 @@
 		}
 		mutex_exit(&pcfslock);
 	} else {
-		if (vfs_devmounting(xdev, vfsp)) {
+		*pseudodev = xdev;
+		if (vfs_devmounting(*pseudodev, vfsp)) {
 			return (EBUSY);
 		}
-		if (vfs_devismounted(xdev))
-			if (uap->flags & MS_REMOUNT) {
+		if (vfs_devismounted(*pseudodev))
+			if (remount) {
 				return (0);
 			} else {
 				return (EBUSY);
 			}
-		pseudodev = xdev;
 	}
 
+	/*
+	 * This is not a remount. Even if MS_REMOUNT was requested,
+	 * the caller needs to proceed as it would on an ordinary
+	 * mount.
+	 */
+	*remounting = 0;
+
+	ASSERT(*pseudodev);
+	return (0);
+}
+
+/*
+ * Get the PCFS-specific mount options from the VFS framework.
+ * For "timezone" and "secsize", we need to parse the number
+ * ourselves and ensure its validity.
+ * Note: "secsize" is deliberately undocumented at this time,
+ * it's a workaround for devices (particularly: lofi image files)
+ * that don't support the DKIOCGMEDIAINFO ioctl for autodetection.
+ */
+static void
+pcfs_parse_mntopts(struct pcfs *fsp, struct mounta *uap)
+{
+	char *c;
+	char *endptr;
+	long l;
+	struct vfs *vfsp = fsp->pcfs_vfs;
+
+	ASSERT(fsp->pcfs_secondswest == 0);
+	ASSERT(fsp->pcfs_secsize == 0);
+
 	if (uap->flags & MS_RDONLY) {
 		vfsp->vfs_flag |= VFS_RDONLY;
 		vfs_setmntopt(vfsp, MNTOPT_RO, NULL, 0);
 	}
 
-	/*
-	 * Mount the filesystem
-	 */
-	devvp = makespecvp(xdev, VBLK);
-	if (IS_SWAPVP(devvp)) {
-		VN_RELE(devvp);
-		return (EBUSY);
-	}
-
-	/*
-	 * special handling for PCMCIA memory card
-	 * with pseudo floppies organization
-	 */
-	if (dos_ldrive == 0 && pcfs_pseudo_floppy(xdev)) {
-		dosstart = (daddr_t)0;
-		fattype = PCFS_PCMCIA_NO_CIS;
-	} else {
-		if (error = pc_getfattype(devvp, dos_ldrive, &dosstart,
-		    &fattype)) {
-			VN_RELE(devvp);
-			return (error);
-		}
-	}
-
-	(void) VOP_PUTPAGE(devvp, (offset_t)0, (uint_t)0, B_INVAL, cr);
-	fsp = kmem_zalloc((uint_t)sizeof (struct pcfs), KM_SLEEP);
-	fsp->pcfs_vfs = vfsp;
-	fsp->pcfs_flags = fattype;
-	fsp->pcfs_devvp = devvp;
-	fsp->pcfs_xdev = xdev;
-	fsp->pcfs_ldrv = dos_ldrive;
-	fsp->pcfs_dosstart = dosstart;
-	mutex_init(&fsp->pcfs_lock, NULL, MUTEX_DEFAULT, NULL);
-
 	if (vfs_optionisset(vfsp, MNTOPT_PCFS_HIDDEN, NULL))
 		fsp->pcfs_flags |= PCFS_HIDDEN;
 	if (vfs_optionisset(vfsp, MNTOPT_PCFS_FOLDCASE, NULL))
 		fsp->pcfs_flags |= PCFS_FOLDCASE;
 	if (vfs_optionisset(vfsp, MNTOPT_PCFS_NOCLAMPTIME, NULL))
 		fsp->pcfs_flags |= PCFS_NOCLAMPTIME;
+	if (vfs_optionisset(vfsp, MNTOPT_NOATIME, NULL))
+		fsp->pcfs_flags |= PCFS_NOATIME;
+
+	if (vfs_optionisset(vfsp, MNTOPT_PCFS_TIMEZONE, &c)) {
+		if (ddi_strtol(c, &endptr, 10, &l) == 0 &&
+		    endptr == c + strlen(c)) {
+			/*
+			 * A number alright - in the allowed range ?
+			 */
+			if (l <= -12*3600 || l >= 12*3600) {
+				cmn_err(CE_WARN, "!pcfs: invalid use of "
+				    "'timezone' mount option - %ld "
+				    "is out of range. Assuming 0.", l);
+				l = 0;
+			}
+		} else {
+			cmn_err(CE_WARN, "!pcfs: invalid use of "
+			    "'timezone' mount option - argument %s "
+			    "is not a valid number. Assuming 0.", c);
+			l = 0;
+		}
+		fsp->pcfs_secondswest = l;
+	}
+
+	/*
+	 * The "secsize=..." mount option is a workaround for the lack of
+	 * lofi(7d) support for DKIOCGMEDIAINFO. If PCFS wants to parse the
+	 * partition table of a disk image and it has been partitioned with
+	 * sector sizes other than 512 bytes, we'd fail on loopback'ed disk
+	 * images.
+	 * That should really be fixed in lofi ... this is a workaround.
+	 */
+	if (vfs_optionisset(vfsp, MNTOPT_PCFS_SECSIZE, &c)) {
+		if (ddi_strtol(c, &endptr, 10, &l) == 0 &&
+		    endptr == c + strlen(c)) {
+			/*
+			 * A number alright - a valid sector size as well ?
+			 */
+			if (!VALID_SECSIZE(l)) {
+				cmn_err(CE_WARN, "!pcfs: invalid use of "
+				    "'secsize' mount option - %ld is "
+				    "unsupported. Autodetecting.", l);
+				l = 0;
+			}
+		} else {
+			cmn_err(CE_WARN, "!pcfs: invalid use of "
+			    "'secsize' mount option - argument %s "
+			    "is not a valid number. Autodetecting.", c);
+			l = 0;
+		}
+		fsp->pcfs_secsize = l;
+		fsp->pcfs_sdshift = ddi_ffs(l / DEV_BSIZE) - 1;
+	}
+}
+
+/*
+ * vfs operations
+ */
+
+/*
+ * pcfs_mount - backend for VFS_MOUNT() on PCFS.
+ */
+static int
+pcfs_mount(
+	struct vfs *vfsp,
+	struct vnode *mvp,
+	struct mounta *uap,
+	struct cred *cr)
+{
+	struct pcfs *fsp;
+	struct vnode *devvp;
+	dev_t pseudodev;
+	dev_t xdev;
+	int dos_ldrive = 0;
+	int error;
+	int remounting;
+
+	if ((error = secpolicy_fs_mount(cr, mvp, vfsp)) != 0)
+		return (error);
+
+	if (mvp->v_type != VDIR)
+		return (ENOTDIR);
+
+	mutex_enter(&mvp->v_lock);
+	if ((uap->flags & MS_REMOUNT) == 0 &&
+	    (uap->flags & MS_OVERLAY) == 0 &&
+	    (mvp->v_count != 1 || (mvp->v_flag & VROOT))) {
+		mutex_exit(&mvp->v_lock);
+		return (EBUSY);
+	}
+	mutex_exit(&mvp->v_lock);
+
+	/*
+	 * PCFS doesn't do mount arguments anymore - everything's a mount
+	 * option these days. In order not to break existing callers, we
+	 * don't reject it yet, just warn that the data (if any) is ignored.
+	 */
+	if (uap->datalen != 0)
+		cmn_err(CE_WARN, "!pcfs: deprecated use of mount(2) with "
+		    "mount argument structures instead of mount options. "
+		    "Ignoring mount(2) 'dataptr' argument.");
+
+	/*
+	 * For most filesystems, this is just a lookupname() on the
+	 * mount pathname string. PCFS historically has to do its own
+	 * partition table parsing because not all Solaris architectures
+	 * support all styles of partitioning that PC media can have, and
+	 * hence PCFS understands "device names" that don't map to actual
+	 * physical device nodes. Parsing the "PCFS syntax" for device
+	 * names is done in pcfs_device_identify() - see there.
+	 *
+	 * Once all block device drivers that can host FAT filesystems have
+	 * been enhanced to create device nodes for all PC-style partitions,
+	 * this code can go away.
+	 */
+	if (error = pcfs_device_identify(vfsp, uap, cr, &dos_ldrive, &xdev))
+		return (error);
+
+	/*
+	 * As with looking up the actual device to mount, PCFS cannot rely
+	 * on just the checks done by vfs_ismounted() whether a given device
+	 * is mounted already. The additional check against the "PCFS syntax"
+	 * is done in  pcfs_device_ismounted().
+	 */
+	remounting = (uap->flags & MS_REMOUNT);
+
+	if (error = pcfs_device_ismounted(vfsp, dos_ldrive, xdev, &remounting,
+	    &pseudodev))
+		return (error);
+
+	if (remounting)
+		return (0);
+
+	/*
+	 * Mount the filesystem.
+	 * An instance structure is required before the attempt to locate
+	 * and parse the FAT BPB. This is because mount options may change
+	 * the behaviour of the filesystem type matching code. Precreate
+	 * it and fill it in to a degree that allows parsing the mount
+	 * options.
+	 */
+	devvp = makespecvp(xdev, VBLK);
+	if (IS_SWAPVP(devvp)) {
+		VN_RELE(devvp);
+		return (EBUSY);
+	}
+	error = VOP_OPEN(&devvp,
+	    (vfsp->vfs_flag & VFS_RDONLY) ? FREAD : FREAD | FWRITE, cr);
+	if (error) {
+		VN_RELE(devvp);
+		return (error);
+	}
+
+	fsp = kmem_zalloc(sizeof (*fsp), KM_SLEEP);
+	fsp->pcfs_vfs = vfsp;
+	fsp->pcfs_xdev = xdev;
+	fsp->pcfs_devvp = devvp;
+	fsp->pcfs_ldrive = dos_ldrive;
+	mutex_init(&fsp->pcfs_lock, NULL, MUTEX_DEFAULT, NULL);
+	vfsp->vfs_data = fsp;
 	vfsp->vfs_dev = pseudodev;
 	vfsp->vfs_fstype = pcfstype;
 	vfs_make_fsid(&vfsp->vfs_fsid, pseudodev, pcfstype);
-	vfsp->vfs_data = (caddr_t)fsp;
 	vfsp->vfs_bcount = 0;
-
-	error = pc_verify(fsp);
-	if (error) {
-		VN_RELE(devvp);
-		mutex_destroy(&fsp->pcfs_lock);
-		kmem_free(fsp, (uint_t)sizeof (struct pcfs));
-		return (error);
-	}
 	vfsp->vfs_bsize = fsp->pcfs_clsize;
 
+	pcfs_parse_mntopts(fsp, uap);
+
+	/*
+	 * This is the actual "mount" - the PCFS superblock check.
+	 *
+	 * Find the requested logical drive and the FAT BPB therein.
+	 * Check device type and flag the instance if media is removeable.
+	 *
+	 * Initializes most members of the filesystem instance structure.
+	 * Returns EINVAL if no valid BPB can be found. Other errors may
+	 * occur after I/O failures, or when invalid / unparseable partition
+	 * tables are encountered.
+	 */
+	if (error = pc_getfattype(fsp))
+		goto errout;
+
+	/*
+	 * Validate that we can access the FAT and that it is, to the
+	 * degree we can verify here, self-consistent.
+	 */
+	if (error = pc_verify(fsp))
+		goto errout;
+
+	/*
+	 * Record the time of the mount, to return as an "approximate"
+	 * timestamp for the FAT root directory. Since FAT roots don't
+	 * have timestamps, this is less confusing to the user than
+	 * claiming "zero" / Jan/01/1970.
+	 */
+	gethrestime(&fsp->pcfs_mounttime);
+
+	/*
+	 * Fix up the mount options. Because "noatime" is made default on
+	 * removeable media only, a fixed disk will have neither "atime"
+	 * nor "noatime" set. We set the options explicitly depending on
+	 * the PCFS_NOATIME flag, to inform the user of what applies.
+	 * Mount option cancellation will take care that the mutually
+	 * exclusive 'other' is cleared.
+	 */
+	vfs_setmntopt(vfsp,
+	    fsp->pcfs_flags & PCFS_NOATIME ? MNTOPT_NOATIME : MNTOPT_ATIME,
+	    NULL, 0);
+
+	/*
+	 * All clear - insert the FS instance into PCFS' list.
+	 */
 	mutex_enter(&pcfslock);
 	fsp->pcfs_nxt = pc_mounttab;
 	pc_mounttab = fsp;
 	mutex_exit(&pcfslock);
 	atomic_inc_32(&pcfs_mountcount);
 	return (0);
+
+errout:
+	(void) VOP_CLOSE(devvp,
+	    vfsp->vfs_flag & VFS_RDONLY ? FREAD : FREAD | FWRITE,
+	    1, (offset_t)0, cr);
+	VN_RELE(devvp);
+	mutex_destroy(&fsp->pcfs_lock);
+	kmem_free(fsp, sizeof (*fsp));
+	return (error);
+
 }
 
-/*
- * vfs operations
- */
-
-/* ARGSUSED */
 static int
 pcfs_unmount(
 	struct vfs *vfsp,
@@ -608,7 +789,6 @@
 	if (secpolicy_fs_unmount(cr, vfsp) != 0)
 		return (EPERM);
 
-	PC_DPRINTF0(4, "pcfs_unmount\n");
 	fsp = VFSTOPCFS(vfsp);
 
 	/*
@@ -674,8 +854,6 @@
 		return (error);
 
 	pcp = pc_getnode(fsp, (daddr_t)0, 0, (struct pcdir *)0);
-	PC_DPRINTF2(9, "pcfs_root(0x%p) pcp= 0x%p\n",
-	    (void *)vfsp, (void *)pcp);
 	pc_unlockfs(fsp);
 	*vpp = PCTOV(pcp);
 	pcp->pc_flags |= PC_EXTERNAL;
@@ -723,7 +901,6 @@
 	struct pcnode *pcp;
 	int error;
 
-	PC_DPRINTF0(7, "pcfs_syncfsnodes\n");
 	if (error = pc_lockfs(fsp, 0, 0))
 		return (error);
 
@@ -840,1000 +1017,14 @@
 	}
 }
 
-/*
- * isDosDrive()
- *	Boolean function.  Give it the systid field for an fdisk partition
- *	and it decides if that's a systid that describes a DOS drive.  We
- *	use systid values defined in sys/dktp/fdisk.h.
- */
-static int
-isDosDrive(uchar_t checkMe)
-{
-	return ((checkMe == DOSOS12) || (checkMe == DOSOS16) ||
-	    (checkMe == DOSHUGE) || (checkMe == FDISK_WINDOWS) ||
-	    (checkMe == FDISK_EXT_WIN) || (checkMe == FDISK_FAT95) ||
-	    (checkMe == DIAGPART));
-}
-
-/*
- * isDosExtended()
- *	Boolean function.  Give it the systid field for an fdisk partition
- *	and it decides if that's a systid that describes an extended DOS
- *	partition.
- */
-static int
-isDosExtended(uchar_t checkMe)
-{
-	return ((checkMe == EXTDOS) || (checkMe == FDISK_EXTLBA));
-}
-
-/*
- * isBootPart()
- *	Boolean function.  Give it the systid field for an fdisk partition
- *	and it decides if that's a systid that describes a Solaris boot
- *	partition.
- */
-static int
-isBootPart(uchar_t checkMe)
-{
-	return (checkMe == X86BOOT);
-}
-
-/*
- * noLogicalDrive()
- *	Display error message about not being able to find a logical
- *	drive.
- */
-static void
-noLogicalDrive(int requested)
-{
-	if (requested == BOOT_PARTITION_DRIVE) {
-		cmn_err(CE_NOTE, "!pcfs: no boot partition");
-	} else {
-		cmn_err(CE_NOTE, "!pcfs: no such logical drive");
-	}
-}
-
-/*
- * findTheDrive()
- *	Discover offset of the requested logical drive, and return
- *	that offset (startSector), the systid of that drive (sysid),
- *	and a buffer pointer (bp), with the buffer contents being
- *	the first sector of the logical drive (i.e., the sector that
- *	contains the BPB for that drive).
- */
-static int
-findTheDrive(dev_t dev, int ldrive, buf_t **bp,
-    daddr_t *startSector, uchar_t *sysid)
-{
-	struct ipart dosp[FD_NUMPART];	/* incore fdisk partition structure */
-	struct mboot *dosp_ptr;		/* boot structure pointer */
-	daddr_t lastseek = 0;		/* Disk block we sought previously */
-	daddr_t diskblk = 0;		/* Disk block to get */
-	daddr_t xstartsect;		/* base of Extended DOS partition */
-	int logicalDriveCount = 0;	/* Count of logical drives seen */
-	int extendedPart = -1;		/* index of extended dos partition */
-	int primaryPart = -1;		/* index of primary dos partition */
-	int bootPart = -1;		/* index of a Solaris boot partition */
-	int xnumsect = -1;		/* length of extended DOS partition */
-	int driveIndex;			/* computed FDISK table index */
-	int i;
-	/*
-	 * Count of drives in the current extended partition's
-	 * FDISK table, and indexes of the drives themselves.
-	 */
-	int extndDrives[FD_NUMPART];
-	int numDrives = 0;
-
-	/*
-	 * Count of drives (beyond primary) in master boot record's
-	 * FDISK table, and indexes of the drives themselves.
-	 */
-	int extraDrives[FD_NUMPART];
-	int numExtraDrives = 0;
-
-	/*
-	 * "ldrive == 0" should never happen, as this is a request to
-	 * mount the physical device (and ignore partitioning). The code
-	 * in pcfs_mount() should have made sure that a logical drive number
-	 * is at least 1, meaning we're looking for drive "C:". It is not
-	 * safe (and a bug in the callers of this function) to request logical
-	 * drive number 0; we could ASSERT() but a graceful EIO is a more
-	 * polite way.
-	 */
-	if (ldrive == 0) {
-		cmn_err(CE_NOTE, "!pcfs: request for logical partition zero");
-		noLogicalDrive(ldrive);
-		return (EIO);
-	}
-
-	/*
-	 *  Copy from disk block into memory aligned structure for fdisk usage.
-	 */
-	dosp_ptr = (struct mboot *)(*bp)->b_un.b_addr;
-	bcopy(dosp_ptr->parts, dosp, sizeof (struct ipart) * FD_NUMPART);
-
-	if (ltohs(dosp_ptr->signature) != MBB_MAGIC) {
-		cmn_err(CE_NOTE, "!pcfs: MBR partition table signature err");
-		return (EINVAL);
-	}
-
-	/*
-	 * Get a summary of what is in the Master FDISK table.
-	 * Normally we expect to find one partition marked as a DOS drive.
-	 * This partition is the one Windows calls the primary dos partition.
-	 * If the machine has any logical drives then we also expect
-	 * to find a partition marked as an extended DOS partition.
-	 *
-	 * Sometimes we'll find multiple partitions marked as DOS drives.
-	 * The Solaris fdisk program allows these partitions
-	 * to be created, but Windows fdisk no longer does.  We still need
-	 * to support these, though, since Windows does.  We also need to fix
-	 * our fdisk to behave like the Windows version.
-	 *
-	 * It turns out that some off-the-shelf media have *only* an
-	 * Extended partition, so we need to deal with that case as well.
-	 *
-	 * Only a single (the first) Extended or Boot Partition will
-	 * be recognized.  Any others will be ignored.
-	 */
-	for (i = 0; i < FD_NUMPART; i++) {
-		PC_DPRINTF1(2, "findTheDrive: found partition type %02x",
-			dosp[i].systid);
-
-		if (isDosDrive(dosp[i].systid)) {
-			if (primaryPart < 0) {
-				logicalDriveCount++;
-				primaryPart = i;
-			} else {
-				extraDrives[numExtraDrives++] = i;
-			}
-			continue;
-		}
-		if ((extendedPart < 0) && isDosExtended(dosp[i].systid)) {
-			extendedPart = i;
-			continue;
-		}
-		if ((bootPart < 0) && isBootPart(dosp[i].systid)) {
-			bootPart = i;
-			continue;
-		}
-	}
-
-	if (ldrive == BOOT_PARTITION_DRIVE) {
-		if (bootPart < 0) {
-			noLogicalDrive(ldrive);
-			return (EINVAL);
-		}
-		*sysid = dosp[bootPart].systid;
-		*startSector = ltohi(dosp[bootPart].relsect);
-		return (0);
-	}
-
-	if (ldrive == PRIMARY_DOS_DRIVE && primaryPart >= 0) {
-		*sysid = dosp[primaryPart].systid;
-		*startSector = ltohi(dosp[primaryPart].relsect);
-		return (0);
-	}
-
-	/*
-	 * We are not looking for the C: drive (or the primary drive
-	 * was not found), so we had better have an extended partition
-	 * or extra drives in the Master FDISK table.
-	 */
-	if ((extendedPart < 0) && (numExtraDrives == 0)) {
-		cmn_err(CE_NOTE, "!pcfs: no extended dos partition");
-		noLogicalDrive(ldrive);
-		return (EINVAL);
-	}
-
-	if (extendedPart >= 0) {
-		diskblk = xstartsect = ltohi(dosp[extendedPart].relsect);
-		xnumsect = ltohi(dosp[extendedPart].numsect);
-		do {
-			/*
-			 *  If the seek would not cause us to change
-			 *  position on the drive, then we're out of
-			 *  extended partitions to examine.
-			 */
-			if (diskblk == lastseek)
-				break;
-			logicalDriveCount += numDrives;
-			/*
-			 *  Seek the next extended partition, and find
-			 *  logical drives within it.
-			 */
-			brelse(*bp);
-			*bp = bread(dev, diskblk, PC_SAFESECSIZE);
-			if ((*bp)->b_flags & B_ERROR) {
-				PC_DPRINTF0(1, "pc_getfattype: read error\n");
-				return (EIO);
-			}
-			lastseek = diskblk;
-			dosp_ptr = (struct mboot *)(*bp)->b_un.b_addr;
-			if (ltohs(dosp_ptr->signature) != MBB_MAGIC) {
-				cmn_err(CE_NOTE, "!pcfs: "
-				    "extended partition signature err");
-				return (EINVAL);
-			}
-			bcopy(dosp_ptr->parts, dosp,
-			    sizeof (struct ipart) * FD_NUMPART);
-			/*
-			 *  Count up drives, and track where the next
-			 *  extended partition is in case we need it.  We
-			 *  are expecting only one extended partition.  If
-			 *  there is more than one we'll only go to the
-			 *  first one we see, but warn about ignoring.
-			 */
-			numDrives = 0;
-			for (i = 0; i < FD_NUMPART; i++) {
-				if (isDosDrive(dosp[i].systid)) {
-					extndDrives[numDrives++] = i;
-				} else if (isDosExtended(dosp[i].systid)) {
-					if (diskblk != lastseek) {
-						/*
-						 * Already found an extended
-						 * partition in this table.
-						 */
-						cmn_err(CE_NOTE,
-						    "!pcfs: ignoring unexpected"
-						    " additional extended"
-						    " partition");
-					} else {
-						diskblk = xstartsect +
-						    ltohi(dosp[i].relsect);
-					}
-				}
-			}
-		} while (ldrive > logicalDriveCount + numDrives);
-
-		if (ldrive <= logicalDriveCount + numDrives) {
-			/*
-			 * The number of logical drives we've found thus
-			 * far is enough to get us to the one we were
-			 * searching for.
-			 */
-			driveIndex = logicalDriveCount + numDrives - ldrive;
-			*sysid = dosp[extndDrives[driveIndex]].systid;
-			*startSector =
-			    ltohi(dosp[extndDrives[driveIndex]].relsect) +
-			    lastseek;
-			if (*startSector > (xstartsect + xnumsect)) {
-				cmn_err(CE_NOTE, "!pcfs: extended partition "
-				    "values bad");
-				return (EINVAL);
-			}
-			return (0);
-		} else {
-			/*
-			 * We ran out of extended dos partition
-			 * drives.  The only hope now is to go
-			 * back to extra drives defined in the master
-			 * fdisk table.  But we overwrote that table
-			 * already, so we must load it in again.
-			 */
-			logicalDriveCount += numDrives;
-			brelse(*bp);
-			*bp = bread(dev, (daddr_t)0, PC_SAFESECSIZE);
-			if ((*bp)->b_flags & B_ERROR) {
-				PC_DPRINTF0(1, "pc_getfattype: read error\n");
-				return (EIO);
-			}
-			dosp_ptr = (struct mboot *)(*bp)->b_un.b_addr;
-			bcopy(dosp_ptr->parts, dosp,
-			    sizeof (struct ipart) * FD_NUMPART);
-		}
-	}
-	/*
-	 *  Still haven't found the drive, is it an extra
-	 *  drive defined in the main FDISK table?
-	 */
-	if (ldrive <= logicalDriveCount + numExtraDrives) {
-		driveIndex = logicalDriveCount + numExtraDrives - ldrive;
-		ASSERT(driveIndex < MIN(numExtraDrives, FD_NUMPART));
-		*sysid = dosp[extraDrives[driveIndex]].systid;
-		*startSector = ltohi(dosp[extraDrives[driveIndex]].relsect);
-		return (0);
-	}
-	/*
-	 *  Still haven't found the drive, and there is
-	 *  nowhere else to look.
-	 */
-	noLogicalDrive(ldrive);
-	return (EINVAL);
-}
-
-/*
- * FAT12/FAT16 specific consistency checks.
- */
-static int
-check_bpb_fat16(struct bootsec *bpb)
-{
-	if (pcfsdebuglevel >= 5) {
-		PC_DPRINTF1(5, "check_bpb_fat: RootEntCount = %d",
-			ltohs(bpb->rdirents[0]));
-		PC_DPRINTF1(5, "check_bpb_fat16: TotSec16 = %d",
-			ltohs(bpb->numsect[0]));
-		PC_DPRINTF1(5, "check_bpb_fat16: FATSz16 = %d",
-			ltohs(bpb->fatsec));
-		PC_DPRINTF1(5, "check_bpb_fat16: TotSec32 = %d",
-			ltohi(bpb->totalsec));
-	}
-	return (ltohs(bpb->rdirents[0]) > 0 &&	 /* RootEntCnt > 0 */
-		((ltohs(bpb->numsect[0]) == 0 && /* TotSec16 == 0 */
-		ltohi(bpb->totalsec) > 0) ||	 /* TotSec32 > 0 */
-		ltohs(bpb->numsect[0]) > 0) && /* TotSec16 > 0 */
-		ltohs(bpb->fatsec) > 0);	 /* FatSz16 > 0 */
-}
-
-/*
- * FAT32 specific consistency checks.
- */
-static int
-check_bpb_fat32(struct fat32_bootsec *bpb)
-{
-	if (pcfsdebuglevel >= 5) {
-		PC_DPRINTF1(5, "check_bpb_fat32: RootEntCount = %d",
-			ltohs(bpb->f_bs.rdirents[0]));
-		PC_DPRINTF1(5, "check_bpb_fat32: TotSec16 = %d",
-			ltohs(bpb->f_bs.numsect[0]));
-		PC_DPRINTF1(5, "check_bpb_fat32: FATSz16 = %d",
-			ltohs(bpb->f_bs.fatsec));
-		PC_DPRINTF1(5, "check_bpb_fat32: TotSec32 = %d",
-			ltohi(bpb->f_bs.totalsec));
-		PC_DPRINTF1(5, "check_bpb_fat32: FATSz32 = %d",
-			ltohi(bpb->f_fatlength));
-	}
-	return (ltohs(bpb->f_bs.rdirents[0]) == 0 &&
-		ltohs(bpb->f_bs.numsect[0]) == 0 &&
-		ltohs(bpb->f_bs.fatsec) == 0 &&
-		ltohi(bpb->f_bs.totalsec) > 0 &&
-		ltohi(bpb->f_fatlength) > 0);
-}
-
-/*
- * Calculate the number of clusters in order to determine
- * the type of FAT we are looking at.  This is the only
- * recommended way of determining FAT type, though there
- * are other hints in the data, this is the best way.
- */
-static ulong_t
-bpb_to_numclusters(uchar_t *cp)
-{
-	struct fat32_bootsec *bpb;
-
-	ulong_t rootdirsectors;
-	ulong_t FATsz;
-	ulong_t TotSec;
-	ulong_t DataSec;
-	ulong_t CountOfClusters;
-	char FileSysType[9];
-
-	/*
-	 * Cast it to FAT32 bpb. If it turns out to be FAT12/16, its
-	 * OK, we won't try accessing the data beyond the FAT16 header
-	 * boundary.
-	 */
-	bpb = (struct fat32_bootsec *)cp;
-
-	if (pcfsdebuglevel >= 5) {
-		if (ltohs(bpb->f_bs.rdirents[0]) != 0) {
-			(void) memcpy(FileSysType, &cp[54], 8);
-			FileSysType[8] = 0;
-			PC_DPRINTF1(5, "debug_bpb: FAT12/FAT16 FileSysType = "
-				"%s", FileSysType);
-		}
-	}
-
-	rootdirsectors = ((ltohs(bpb->f_bs.rdirents[0]) * 32) +
-		(ltohs(bpb->f_bs.bps[0]) - 1)) / ltohs(bpb->f_bs.bps[0]);
-
-	if (ltohs(bpb->f_bs.fatsec) != 0)
-		FATsz = ltohs(bpb->f_bs.fatsec);
-	else
-		FATsz = ltohi(bpb->f_fatlength);
-
-	if (ltohs(bpb->f_bs.numsect[0]) != 0)
-		TotSec = ltohs(bpb->f_bs.numsect[0]);
-	else
-		TotSec = ltohi(bpb->f_bs.totalsec);
-
-	DataSec = TotSec - (ltohs(bpb->f_bs.res_sec[0]) +
-			(bpb->f_bs.nfat * FATsz) + rootdirsectors);
-
-	CountOfClusters = DataSec / bpb->f_bs.spcl;
-
-	PC_DPRINTF1(5, "debug_bpb: CountOfClusters = %ld", CountOfClusters);
-
-	return (CountOfClusters);
-
-}
-
-static int
-fattype(ulong_t CountOfClusters)
-{
-	/*
-	 * From Microsoft:
-	 * In the following example, when it says <, it does not mean <=.
-	 * Note also that the numbers are correct.  The first number for
-	 * FAT12 is 4085; the second number for FAT16 is 65525. These numbers
-	 * and the '<' signs are not wrong.
-	 */
-
-	/* Watch for edge cases */
-	if ((CountOfClusters >= 4085 && CountOfClusters <= 4095) ||
-	    (CountOfClusters >= 65525 && CountOfClusters <= 65535)) {
-		PC_DPRINTF1(5, "debug_bpb: Cannot determine FAT yet - %ld",
-			CountOfClusters);
-		return (-1); /* Cannot be determined yet */
-	} else if (CountOfClusters < 4085) {
-		/* Volume is FAT12 */
-		PC_DPRINTF0(5, "debug_bpb: This must be FAT12");
-		return (0);
-	} else if (CountOfClusters < 65525) {
-		/* Volume is FAT16 */
-		PC_DPRINTF0(5, "debug_bpb: This must be FAT16");
-		return (PCFS_FAT16);
-	} else {
-		/* Volume is FAT32 */
-		PC_DPRINTF0(5, "debug_bpb: This must be FAT32");
-		return (PCFS_FAT32);
-	}
-}
-
-#define	VALID_SECSIZE(s) (s == 512 || s == 1024 || s == 2048 || s == 4096)
-
-#define	VALID_SPCL(s) (s == 1 || s == 2 || s == 4 || s == 8 || s == 16 ||\
-	s == 32 || s == 64 || s == 128)
-
-static int
-secondaryBPBChecks(uchar_t *cp)
-{
-	struct bootsec *bpb = (struct bootsec *)cp;
-	struct fat32_bootsec *f32bpb = (struct fat32_bootsec *)cp;
-
-	/*
-	 * Perform secondary checks to try and determine what sort
-	 * of FAT partition we have based on other, less reliable,
-	 * data in the BPB header.
-	 */
-	if (ltohs(bpb->fatsec) != 0) {
-		/*
-		 * Must be FAT12 or FAT16, check the
-		 * FilSysType string (not 100% reliable).
-		 */
-		if (!memcmp((cp + PCFS_TYPESTRING_OFFSET16), "FAT12", 5)) {
-			PC_DPRINTF0(5, "secondaryBPBCheck says: FAT12");
-			return (0); /* FAT12 */
-		} else if (!memcmp((cp + PCFS_TYPESTRING_OFFSET16), "FAT16",
-			5)) {
-			PC_DPRINTF0(5, "secondaryBPBCheck says: FAT16");
-			return (PCFS_FAT16);
-		} else {
-			/*
-			 * Try to use the BPB_Media byte
-			 *
-			 *  If the media byte indicates a floppy we'll
-			 *  assume FAT12, otherwise we'll assume FAT16.
-			 */
-			switch (bpb->mediadesriptor) {
-				case SS8SPT:
-				case DS8SPT:
-				case SS9SPT:
-				case DS9SPT:
-				case DS18SPT:
-				case DS9_15SPT:
-					PC_DPRINTF0(5,
-					"secondaryBPBCheck says: FAT12");
-					return (0); /* FAT12 */
-				case MD_FIXED:
-					PC_DPRINTF0(5,
-					"secondaryBPBCheck says: FAT16");
-					return (PCFS_FAT16);
-				default:
-					cmn_err(CE_NOTE,
-						"!pcfs: unknown FAT type");
-					return (-1);
-			}
-		}
-	} else if (ltohi(f32bpb->f_fatlength) > 0) {
-		PC_DPRINTF0(5, "secondaryBPBCheck says: FAT32");
-		return (PCFS_FAT32);
-	} else {
-		/* We don't know */
-		PC_DPRINTF0(5, "secondaryBPBCheck says: unknown!!");
-		return (-1);
-	}
-}
-
-/*
- * Check to see if the BPB we found is correct.
- *
- * First, look for obvious, tell-tale signs of trouble:
- * The NumFATs value should always be 2.  Sometimes it can be a '1'
- * on FLASH memory cards and other non-disk-based media, so we
- * will allow that as well.
- *
- * We also look at the Media byte, the valid range is 0xF0, or
- * 0xF8 thru 0xFF, anything else means this is probably not a good
- * BPB.
- *
- * Finally, check the BPB Magic number at the end of the 512 byte
- * block, it must be 0xAA55.
- *
- * If that all is good, calculate the number of clusters and
- * do some final verification steps.
- *
- * If all is well, return success (1) and set the fattypep value to the
- * correct FAT value if the caller provided a pointer to store it in.
- */
-static int
-isBPB(uchar_t *cp, int *fattypep)
-{
-	struct bootsec *bpb = (struct bootsec *)cp;
-	int type;
-
-	uint_t numclusters;		/* number of clusters in file area */
-	ushort_t secsize = (int)ltohs(bpb->bps[0]);
-
-	if (pcfsdebuglevel >= 3) {
-		if (!VALID_SECSIZE(secsize))
-			PC_DPRINTF1(3, "check_bpb: invalid bps value %d",
-				secsize);
-
-		if (!VALID_SPCL(bpb->spcl))
-			PC_DPRINTF1(3, "check_bpb: invalid spcl value %d",
-				bpb->spcl);
-
-		if ((secsize * bpb->spcl) >= (32 * 1024))
-			PC_DPRINTF3(3, "check_bpb: BPC > 32K  %d x %d = %d",
-				secsize,
-				bpb->spcl,
-				secsize * bpb->spcl);
-
-		if (bpb->nfat == 0)
-			PC_DPRINTF1(3, "check_bpb: bad NumFATs value %d",
-				bpb->nfat);
-
-		if (ltohs(bpb->res_sec[0]) == 0)
-			PC_DPRINTF1(3, "check_bpb: bad RsvdSecCnt value %d",
-				ltohs(bpb->res_sec[0]));
-
-		PC_DPRINTF1(5, "check_bpb: Media byte = %02x",
-			bpb->mediadesriptor);
-
-	}
-	if ((bpb->nfat == 0) ||
-		(bpb->mediadesriptor != 0xF0 && bpb->mediadesriptor < 0xF8) ||
-		(ltohs(cp[510]) != MBB_MAGIC) ||
-		!VALID_SECSIZE(secsize) ||
-		!VALID_SPCL(bpb->spcl) ||
-		(secsize * bpb->spcl > (64 * 1024)) ||
-		!(ltohs(bpb->res_sec[0])))
-		return (0);
-
-	/*
-	 * Basic sanity checks passed so far, now try to determine which
-	 * FAT format to use.
-	 */
-	numclusters = bpb_to_numclusters(cp);
-
-	type = fattype(numclusters);
-
-	/* Do some final sanity checks for each specific type of FAT */
-	switch (type) {
-		case 0: /* FAT12 */
-		case PCFS_FAT16:
-			if (!check_bpb_fat16((struct bootsec *)cp))
-				return (0);
-			break;
-		case PCFS_FAT32:
-			if (!check_bpb_fat32((struct fat32_bootsec *)cp))
-				return (0);
-			break;
-		default: /* not sure yet */
-			type = secondaryBPBChecks(cp);
-			if (type == -1) {
-				/* Still nothing, give it up. */
-				return (0);
-			}
-			break;
-	}
-
-	if (fattypep)
-		*fattypep = type;
-
-	PC_DPRINTF0(5, "isBPB: BPB passes verification tests");
-	return (1);
-}
-
-
-/*
- * Get the FAT type for the DOS medium.
- *
- * -------------------------
- * According to Microsoft:
- *   The FAT type one of FAT12, FAT16, or FAT32 is determined by the
- * count of clusters on the volume and nothing else.
- * -------------------------
- *
- */
-static int
-pc_getfattype(
-	struct vnode *devvp,
-	int ldrive,
-	daddr_t *strtsectp,
-	int *fattypep)
-{
-	buf_t *bp = NULL;		/* Disk buffer pointer */
-	int err = 0;
-	uchar_t sysid = 0;		/* System ID character */
-	dev_t	dev = devvp->v_rdev;
-
-
-	/*
-	 * Open the device so we can check out the BPB or FDISK table,
-	 * then read in the sector.
-	 */
-	PC_DPRINTF2(5, "pc_getfattype: dev=%x  ldrive=%x  ", (int)dev, ldrive);
-	if (err = VOP_OPEN(&devvp, FREAD, CRED())) {
-		PC_DPRINTF1(1, "pc_getfattype: open error=%d\n", err);
-		return (err);
-	}
-
-	/*
-	 * Unpartitioned media (floppies and some removeable devices)
-	 * don't have a partition table, the FAT BPB is at disk block 0.
-	 * Start out by reading block 0.
-	 */
-	*strtsectp = (daddr_t)0;
-	bp = bread(dev, *strtsectp, PC_SAFESECSIZE);
-
-	if (bp->b_flags & B_ERROR) {
-		PC_DPRINTF2(1, "pc_getfattype: read error on "
-		    "device %d, disk LBA %d\n", (int)dev, (int)*strtsectp);
-		err = EIO;
-		goto out;
-	}
-
-	/*
-	 * If a logical drive number is requested, parse the partition table
-	 * and attempt to locate it. Otherwise, proceed immediately to the
-	 * BPB check. findTheDrive(), if successful, returns the disk block
-	 * number where the requested partition starts in "strtsecp".
-	 */
-	if (ldrive != 0) {
-		PC_DPRINTF0(5, "pc_getfattype: using FDISK table to find BPB");
-
-		if (err = findTheDrive(dev, ldrive, &bp, strtsectp, &sysid))
-			goto out;
-
-		brelse(bp);
-		bp = bread(dev, *strtsectp, PC_SAFESECSIZE);
-		if (bp->b_flags & B_ERROR) {
-			PC_DPRINTF2(1, "pc_getfattype: read error on "
-			    "device %d, disk LBA %d\n",
-			    (int)dev, (int)*strtsectp);
-			err = EIO;
-			goto out;
-		}
-	}
-
-	if (!isBPB((uchar_t *)bp->b_un.b_addr, fattypep)) {
-		PC_DPRINTF2(1, "pc_getfattype: No FAT BPB on device %d, "
-		    "disk LBA %d\n", (int)dev, (int)*strtsectp);
-		err = EIO;
-		goto out;
-	}
-
-out:
-	/*
-	 * Release the buffer used
-	 */
-	if (bp != NULL)
-		brelse(bp);
-	(void) VOP_CLOSE(devvp, FREAD, 1, (offset_t)0, CRED());
-	return (err);
-}
-
-
-/*
- * Get the boot parameter block and file allocation table.
- * If there is an old FAT, invalidate it.
- */
-int
-pc_getfat(struct pcfs *fsp)
-{
-	struct vfs *vfsp = PCFSTOVFS(fsp);
-	struct buf *tp = 0;
-	struct buf *bp = 0;
-	uchar_t *fatp = NULL;
-	uchar_t *fat_changemap = NULL;
-	struct bootsec *bootp;
-	struct fat32_bootsec *f32b;
-	struct vnode *devvp;
-	int error;
-	int fatsize;
-	int fat_changemapsize;
-	int flags = 0;
-	int nfat;
-	int secsize;
-	int fatsec;
-
-	PC_DPRINTF0(5, "pc_getfat\n");
-	devvp = fsp->pcfs_devvp;
-	if (fsp->pcfs_fatp) {
-		/*
-		 * There is a FAT in core.
-		 * If there are open file pcnodes or we have modified it or
-		 * it hasn't timed out yet use the in core FAT.
-		 * Otherwise invalidate it and get a new one
-		 */
-#ifdef notdef
-		if (fsp->pcfs_frefs ||
-		    (fsp->pcfs_flags & PCFS_FATMOD) ||
-		    (gethrestime_sec() < fsp->pcfs_fattime)) {
-			return (0);
-		} else {
-			mutex_enter(&pcfslock);
-			pc_invalfat(fsp);
-			mutex_exit(&pcfslock);
-		}
-#endif /* notdef */
-		return (0);
-	}
-	/*
-	 * Open block device mounted on.
-	 */
-	error = VOP_OPEN(&devvp,
-	    (vfsp->vfs_flag & VFS_RDONLY) ? FREAD : FREAD|FWRITE,
-	    CRED());
-	if (error) {
-		PC_DPRINTF1(1, "pc_getfat: open error=%d\n", error);
-		return (error);
-	}
-	/*
-	 * Get boot parameter block and check it for validity
-	 */
-	tp = bread(fsp->pcfs_xdev, fsp->pcfs_dosstart, PC_SAFESECSIZE);
-	if ((tp->b_flags & (B_ERROR | B_STALE)) ||
-	    !isBPB((uchar_t *)tp->b_un.b_addr, NULL)) {
-		PC_DPRINTF2(1, "pc_getfat: boot block error on device %d, "
-		    "disk LBA %d\n",
-		    (int)fsp->pcfs_xdev, (int)fsp->pcfs_dosstart);
-		flags = tp->b_flags & B_ERROR;
-		error = EIO;
-		goto out;
-	}
-	tp->b_flags |= B_STALE | B_AGE;
-	bootp = (struct bootsec *)tp->b_un.b_addr;
-
-
-	/* get the sector size - may be more than 512 bytes */
-	secsize = (int)ltohs(bootp->bps[0]);
-	/* check for bogus sector size - fat should be at least 1 sector */
-	if (IS_FAT32(fsp)) {
-		f32b = (struct fat32_bootsec *)bootp;
-		fatsec = ltohi(f32b->f_fatlength);
-	} else {
-		fatsec = ltohs(bootp->fatsec);
-	}
-	if (secsize < 512 || fatsec < 1 || bootp->nfat < 1) {
-		cmn_err(CE_NOTE, "!pcfs: FAT size error");
-		error = EINVAL;
-		goto out;
-	}
-
-	switch (bootp->mediadesriptor) {
-	default:
-		cmn_err(CE_NOTE, "!pcfs: media-descriptor error, 0x%x",
-		    bootp->mediadesriptor);
-		error = EINVAL;
-		goto out;
-
-	case MD_FIXED:
-		/*
-		 * PCMCIA pseudo floppy is type MD_FIXED,
-		 * but is accessed like a floppy
-		 */
-		if (!(fsp->pcfs_flags & PCFS_PCMCIA_NO_CIS)) {
-			fsp->pcfs_flags |= PCFS_NOCHK;
-		}
-		/* FALLTHRU */
-	case SS8SPT:
-	case DS8SPT:
-	case SS9SPT:
-	case DS9SPT:
-	case DS18SPT:
-	case DS9_15SPT:
-		fsp->pcfs_secsize = secsize;
-		fsp->pcfs_sdshift = secsize / DEV_BSIZE - 1;
-		fsp->pcfs_entps = secsize / sizeof (struct pcdir);
-		fsp->pcfs_spcl = (int)bootp->spcl;
-		fsp->pcfs_fatsec = fatsec;
-		fsp->pcfs_spt = (int)ltohs(bootp->spt);
-		fsp->pcfs_rdirsec = ((int)ltohs(bootp->rdirents[0])
-		    * sizeof (struct pcdir) + (secsize - 1)) / secsize;
-		fsp->pcfs_clsize = fsp->pcfs_spcl * secsize;
-		fsp->pcfs_fatstart = fsp->pcfs_dosstart +
-		    (daddr_t)ltohs(bootp->res_sec[0]);
-		fsp->pcfs_rdirstart = fsp->pcfs_fatstart +
-		    (bootp->nfat * fsp->pcfs_fatsec);
-		fsp->pcfs_datastart = fsp->pcfs_rdirstart + fsp->pcfs_rdirsec;
-		if (IS_FAT32(fsp))
-			fsp->pcfs_rdirstart = ltohi(f32b->f_rootcluster);
-		fsp->pcfs_ncluster = (((int)(ltohs(bootp->numsect[0]) ?
-		    ltohs(bootp->numsect[0]) : ltohi(bootp->totalsec))) -
-		    fsp->pcfs_datastart + fsp->pcfs_dosstart) / fsp->pcfs_spcl;
-		fsp->pcfs_numfat = (int)bootp->nfat;
-		fsp->pcfs_nxfrecls = PCF_FIRSTCLUSTER;
-		break;
-	}
-
-	/*
-	 * Get FAT and check it for validity
-	 */
-	fatsize = fsp->pcfs_fatsec * fsp->pcfs_secsize;
-	fatp = kmem_alloc(fatsize, KM_SLEEP);
-	error = pc_readfat(fsp, fatp, fsp->pcfs_fatstart, fatsize);
-	if (error) {
-		flags = B_ERROR;
-		goto out;
-	}
-	fat_changemapsize = (fatsize / fsp->pcfs_clsize) + 1;
-	fat_changemap = kmem_zalloc(fat_changemapsize, KM_SLEEP);
-
-	/*
-	 * The only definite signature check is that the
-	 * media descriptor byte should match the first byte
-	 * of the FAT block.
-	 */
-	if (fatp[0] != bootp->mediadesriptor) {
-		cmn_err(CE_NOTE, "!pcfs: FAT signature error");
-		error = EINVAL;
-		goto out;
-	}
-	/*
-	 * Checking for fatsec and number of supported clusters, should
-	 * actually determine a FAT12/FAT media.
-	 */
-	if (fsp->pcfs_flags & PCFS_FAT16) {
-		if ((fsp->pcfs_fatsec <= 12) &&
-		    ((fatsize * 2 / 3) >= fsp->pcfs_ncluster)) {
-			/*
-			 * We have a 12-bit FAT, rather than a 16-bit FAT.
-			 * Ignore what the fdisk table says.
-			 */
-			PC_DPRINTF0(2, "pc_getfattype: forcing 12-bit FAT\n");
-			fsp->pcfs_flags &= ~PCFS_FAT16;
-		}
-	}
-	/*
-	 * Sanity check our FAT is large enough for the
-	 * clusters we think we have.
-	 */
-	if ((fsp->pcfs_flags & PCFS_FAT16) &&
-	    ((fatsize / 2) < fsp->pcfs_ncluster)) {
-		cmn_err(CE_NOTE, "!pcfs: FAT too small for number of clusters");
-		error = EINVAL;
-		goto out;
-	}
-
-	/*
-	 * Get alternate FATs and check for consistency
-	 * This is an inlined version of pc_readfat().
-	 * Since we're only comparing FAT and alternate FAT,
-	 * there's no reason to let pc_readfat() copy data out
-	 * of the buf. Instead, compare in-situ, one cluster
-	 * at a time.
-	 */
-	for (nfat = 1; nfat < fsp->pcfs_numfat; nfat++) {
-		size_t startsec;
-		size_t off;
-
-		startsec = fsp->pcfs_fatstart + nfat * fsp->pcfs_fatsec;
-
-		for (off = 0; off < fatsize; off += fsp->pcfs_clsize) {
-			bp = bread(fsp->pcfs_xdev, pc_dbdaddr(fsp,
-				startsec +
-				pc_cltodb(fsp, pc_lblkno(fsp, off))),
-				MIN(fsp->pcfs_clsize, fatsize - off));
-			if (bp->b_flags & (B_ERROR | B_STALE)) {
-				cmn_err(CE_NOTE,
-					"!pcfs: alternate FAT #%d read error"
-					" at byte %ld", nfat, off);
-				flags = B_ERROR;
-				error = EIO;
-				goto out;
-			}
-			bp->b_flags |= B_STALE | B_AGE;
-			if (bcmp(bp->b_un.b_addr,
-			    fatp + off,
-			    MIN(fsp->pcfs_clsize, fatsize - off))) {
-				cmn_err(CE_NOTE,
-					"!pcfs: alternate FAT #%d corrupted"
-					" at byte %ld", nfat, off);
-				flags = B_ERROR;
-			}
-			brelse(bp);
-			bp = NULL;	/* prevent double release */
-		}
-	}
-
-	fsp->pcfs_fatsize = fatsize;
-	fsp->pcfs_fatp = fatp;
-	fsp->pcfs_fat_changemapsize = fat_changemapsize;
-	fsp->pcfs_fat_changemap = fat_changemap;
-	fsp->pcfs_fattime = gethrestime_sec() + PCFS_DISKTIMEOUT;
-	fsp->pcfs_fatjustread = 1;
-
-	brelse(tp);
-	tp = NULL;
-	if (IS_FAT32(fsp)) {
-		/* get fsinfo */
-		struct fat32_boot_fsinfo fsinfo_disk;
-
-		fsp->f32fsinfo_sector = ltohs(f32b->f_infosector);
-		tp = bread(fsp->pcfs_xdev,
-		    fsp->pcfs_dosstart + pc_dbdaddr(fsp, fsp->f32fsinfo_sector),
-		    PC_SAFESECSIZE);
-		if (tp->b_flags & (B_ERROR | B_STALE)) {
-			cmn_err(CE_NOTE, "!pcfs: error reading fat32 fsinfo");
-			flags = tp->b_flags & B_ERROR;
-			brelse(tp);
-			tp = NULL;
-			error = EIO;
-			goto out;
-		}
-		tp->b_flags |= B_STALE | B_AGE;
-		bcopy((void *)(tp->b_un.b_addr + FAT32_BOOT_FSINFO_OFF),
-		    &fsinfo_disk, sizeof (struct fat32_boot_fsinfo));
-		brelse(tp);
-		tp = NULL;
-
-		/* translated fields */
-		fsp->fsinfo_native.fs_signature =
-		    ltohi(fsinfo_disk.fs_signature);
-		fsp->fsinfo_native.fs_free_clusters =
-		    ltohi(fsinfo_disk.fs_free_clusters);
-		if (fsp->fsinfo_native.fs_signature != FAT32_FS_SIGN) {
-			cmn_err(CE_NOTE,
-			    "!pcfs: fat32 fsinfo signature mismatch.");
-			error = EINVAL;
-			goto out;
-		}
-	}
-
-	return (0);
-
-out:
-	cmn_err(CE_NOTE, "!pcfs: illegal disk format");
-	if (tp)
-		brelse(tp);
-	if (bp)
-		brelse(bp);
-	if (fatp)
-		kmem_free(fatp, fatsize);
-	if (fat_changemap)
-		kmem_free(fat_changemap, fat_changemapsize);
-
-	if (flags) {
-		pc_mark_irrecov(fsp);
-	}
-	(void) VOP_CLOSE(devvp, (vfsp->vfs_flag & VFS_RDONLY) ?
-	    FREAD : FREAD|FWRITE, 1, (offset_t)0, CRED());
-	return (error);
-}
-
 int
 pc_syncfat(struct pcfs *fsp)
 {
 	struct buf *bp;
 	int nfat;
-	int	error;
-	struct fat32_boot_fsinfo fsinfo_disk;
+	int	error = 0;
+	struct fat_od_fsi *fsinfo_disk;
 
-	PC_DPRINTF0(7, "pcfs_syncfat\n");
 	if ((fsp->pcfs_fatp == (uchar_t *)0) ||
 	    !(fsp->pcfs_flags & PCFS_FATMOD))
 		return (0);
@@ -1843,35 +1034,33 @@
 	fsp->pcfs_flags &= ~PCFS_FATMOD;
 	fsp->pcfs_fattime = gethrestime_sec() + PCFS_DISKTIMEOUT;
 	for (nfat = 0; nfat < fsp->pcfs_numfat; nfat++) {
-		error = pc_writefat(fsp,
-		    fsp->pcfs_fatstart + nfat*fsp->pcfs_fatsec);
+		error = pc_writefat(fsp, pc_dbdaddr(fsp,
+		    fsp->pcfs_fatstart + nfat * fsp->pcfs_fatsec));
 		if (error) {
 			pc_mark_irrecov(fsp);
 			return (EIO);
 		}
 	}
 	pc_clear_fatchanges(fsp);
-	PC_DPRINTF0(6, "pcfs_syncfat: wrote out FAT\n");
-	/* write out fsinfo */
+
+	/*
+	 * Write out fsinfo sector.
+	 */
 	if (IS_FAT32(fsp)) {
 		bp = bread(fsp->pcfs_xdev,
-		    fsp->pcfs_dosstart + pc_dbdaddr(fsp, fsp->f32fsinfo_sector),
-		    PC_SAFESECSIZE);
+		    pc_dbdaddr(fsp, fsp->pcfs_fsistart), fsp->pcfs_secsize);
 		if (bp->b_flags & (B_ERROR | B_STALE)) {
-			brelse(bp);
-			return (EIO);
+			error = geterror(bp);
 		}
-		bcopy((void *)(bp->b_un.b_addr + FAT32_BOOT_FSINFO_OFF),
-		    &fsinfo_disk, sizeof (struct fat32_boot_fsinfo));
-		/* translate fields */
-		fsinfo_disk.fs_free_clusters =
-		    htoli(fsp->fsinfo_native.fs_free_clusters);
-		fsinfo_disk.fs_next_cluster = (uint32_t)FSINFO_UNKNOWN;
-		bcopy(&fsinfo_disk,
-		    (void *)(bp->b_un.b_addr + FAT32_BOOT_FSINFO_OFF),
-		    sizeof (struct fat32_boot_fsinfo));
-		bwrite2(bp);
-		error = geterror(bp);
+		fsinfo_disk = (fat_od_fsi_t *)(bp->b_un.b_addr);
+		if (!error && FSISIG_OK(fsinfo_disk)) {
+			fsinfo_disk->fsi_incore.fs_free_clusters =
+			    LE_32(fsp->pcfs_fsinfo.fs_free_clusters);
+			fsinfo_disk->fsi_incore.fs_next_free =
+			    LE_32(FSINFO_UNKNOWN);
+			bwrite2(bp);
+			error = geterror(bp);
+		}
 		brelse(bp);
 		if (error) {
 			pc_mark_irrecov(fsp);
@@ -1887,13 +1076,12 @@
 	struct pcfs *xfsp;
 	int mount_cnt = 0;
 
-	PC_DPRINTF0(7, "pc_invalfat\n");
 	if (fsp->pcfs_fatp == (uchar_t *)0)
 		panic("pc_invalfat");
 	/*
 	 * Release FAT
 	 */
-	kmem_free(fsp->pcfs_fatp, fsp->pcfs_fatsize);
+	kmem_free(fsp->pcfs_fatp, fsp->pcfs_fatsec * fsp->pcfs_secsize);
 	fsp->pcfs_fatp = NULL;
 	kmem_free(fsp->pcfs_fat_changemap, fsp->pcfs_fat_changemapsize);
 	fsp->pcfs_fat_changemap = NULL;
@@ -1918,9 +1106,9 @@
 void
 pc_badfs(struct pcfs *fsp)
 {
-	cmn_err(CE_WARN, "corrupted PC file system on dev %x.%x\n",
+	cmn_err(CE_WARN, "corrupted PC file system on dev (%x.%x):%d\n",
 	    getmajor(fsp->pcfs_devvp->v_rdev),
-	    getminor(fsp->pcfs_devvp->v_rdev));
+	    getminor(fsp->pcfs_devvp->v_rdev), fsp->pcfs_ldrive);
 }
 
 /*
@@ -1965,6 +1153,7 @@
 	}
 	eblkno = pcfid->pcfid_block;
 	eoffset = pcfid->pcfid_offset;
+
 	if ((pc_dbtocl(fsp,
 	    eblkno - fsp->pcfs_dosstart) >= fsp->pcfs_ncluster) ||
 	    (eoffset > fsp->pcfs_clsize)) {
@@ -1973,11 +1162,17 @@
 		return (EINVAL);
 	}
 
-	if (eblkno >= fsp->pcfs_datastart || (eblkno-fsp->pcfs_rdirstart)
+	if (eblkno >= fsp->pcfs_datastart || (eblkno - fsp->pcfs_rdirstart)
 	    < (fsp->pcfs_rdirsec & ~(fsp->pcfs_spcl - 1))) {
-		bp = bread(fsp->pcfs_xdev, eblkno, fsp->pcfs_clsize);
+		bp = bread(fsp->pcfs_xdev, pc_dbdaddr(fsp, eblkno),
+		    fsp->pcfs_clsize);
 	} else {
-		bp = bread(fsp->pcfs_xdev, eblkno,
+		/*
+		 * This is an access "backwards" into the FAT12/FAT16
+		 * root directory. A better code structure would
+		 * significantly improve maintainability here ...
+		 */
+		bp = bread(fsp->pcfs_xdev, pc_dbdaddr(fsp, eblkno),
 		    (int)(fsp->pcfs_datastart - eblkno) * fsp->pcfs_secsize);
 	}
 	if (bp->b_flags & (B_ERROR | B_STALE)) {
@@ -2034,7 +1229,7 @@
 	if ((ep->pcd_crtime.pct_time == pcfid->pcfid_ctime) &&
 	    (ep->pcd_filename[0] != PCD_ERASED) &&
 	    (pc_validchar(ep->pcd_filename[0]) ||
-		(ep->pcd_filename[0] == '.' && ep->pcd_filename[1] == '.'))) {
+	    (ep->pcd_filename[0] == '.' && ep->pcd_filename[1] == '.'))) {
 		pcp = pc_getnode(fsp, eblkno, eoffset, ep);
 		pcp->pc_flags |= PC_EXTERNAL;
 		*vpp = PCTOV(pcp);
@@ -2048,70 +1243,27 @@
 }
 
 /*
- * if device is a PCMCIA pseudo floppy, return 1
- * otherwise, return 0
- */
-static int
-pcfs_pseudo_floppy(dev_t rdev)
-{
-	int			error, err;
-	struct dk_cinfo		info;
-	ldi_handle_t		lh;
-	ldi_ident_t		li;
-
-	err = ldi_ident_from_mod(&modlinkage, &li);
-	if (err) {
-		PC_DPRINTF1(1,
-		    "pcfs_pseudo_floppy: ldi_ident_from_mod err=%d\n", err);
-		return (0);
-	}
-
-	err = ldi_open_by_dev(&rdev, OTYP_CHR, FREAD, CRED(), &lh, li);
-	ldi_ident_release(li);
-	if (err) {
-		PC_DPRINTF1(1,
-		    "pcfs_pseudo_floppy: ldi_open err=%d\n", err);
-		return (0);
-	}
-
-	/* return value stored in err is purposfully ignored */
-	error = ldi_ioctl(lh, DKIOCINFO, (intptr_t)&info, FKIOCTL,
-	    CRED(), &err);
-
-	err = ldi_close(lh, FREAD, CRED());
-	if (err != 0) {
-		PC_DPRINTF1(1,
-		    "pcfs_pseudo_floppy: ldi_close err=%d\n", err);
-		return (0);
-	}
-
-	if ((error == 0) && (info.dki_ctype == DKC_PCMCIA_MEM) &&
-		(info.dki_flags & DKI_PCMCIA_PFD))
-		return (1);
-	else
-		return (0);
-}
-
-/*
  * Unfortunately, FAT32 fat's can be pretty big (On a 1 gig jaz drive, about
  * a meg), so we can't bread() it all in at once. This routine reads a
  * fat a chunk at a time.
  */
 static int
-pc_readfat(struct pcfs *fsp, uchar_t *fatp, daddr_t start, size_t fatsize)
+pc_readfat(struct pcfs *fsp, uchar_t *fatp)
 {
 	struct buf *bp;
 	size_t off;
 	size_t readsize;
+	daddr_t diskblk;
+	size_t fatsize = fsp->pcfs_fatsec * fsp->pcfs_secsize;
+	daddr_t start = fsp->pcfs_fatstart;
 
 	readsize = fsp->pcfs_clsize;
 	for (off = 0; off < fatsize; off += readsize, fatp += readsize) {
 		if (readsize > (fatsize - off))
 			readsize = fatsize - off;
-		bp = bread(fsp->pcfs_xdev,
-		    pc_dbdaddr(fsp, start +
-			pc_cltodb(fsp, pc_lblkno(fsp, off))),
-		    readsize);
+		diskblk = pc_dbdaddr(fsp, start +
+		    pc_cltodb(fsp, pc_lblkno(fsp, off)));
+		bp = bread(fsp->pcfs_xdev, diskblk, readsize);
 		if (bp->b_flags & (B_ERROR | B_STALE)) {
 			brelse(bp);
 			return (EIO);
@@ -2133,6 +1285,9 @@
  * the 'updated' fields here because the caller might be writing out
  * several FATs, so the caller must use pc_clear_fatchanges() after
  * all FATs have been updated.
+ * This function doesn't take "start" from fsp->pcfs_dosstart because
+ * callers can use it to write either the primary or any of the alternate
+ * FAT tables.
  */
 static int
 pc_writefat(struct pcfs *fsp, daddr_t start)
@@ -2142,7 +1297,7 @@
 	size_t writesize;
 	int	error;
 	uchar_t *fatp = fsp->pcfs_fatp;
-	size_t fatsize = fsp->pcfs_fatsize;
+	size_t fatsize = fsp->pcfs_fatsec * fsp->pcfs_secsize;
 
 	writesize = fsp->pcfs_clsize;
 	for (off = 0; off < fatsize; off += writesize, fatp += writesize) {
@@ -2223,16 +1378,1273 @@
 	struct pcfs *fsp = VFSTOPCFS(vfsp);
 
 	mutex_enter(&pcfslock);
+	/*
+	 * Purging the FAT closes the device - can't do any more
+	 * I/O after this.
+	 */
 	if (fsp->pcfs_fatp != (uchar_t *)0)
 		pc_invalfat(fsp);
 	mutex_exit(&pcfslock);
 
 	VN_RELE(fsp->pcfs_devvp);
 	mutex_destroy(&fsp->pcfs_lock);
-	kmem_free(fsp, (uint_t)sizeof (struct pcfs));
+	kmem_free(fsp, sizeof (*fsp));
 
 	/*
 	 * Allow _fini() to succeed now, if so desired.
 	 */
 	atomic_dec_32(&pcfs_mountcount);
 }
+
+
+/*
+ * PC-style partition parsing and FAT BPB identification/validation code.
+ * The partition parsers here assume:
+ *	- a FAT filesystem will be in a partition that has one of a set of
+ *	  recognized partition IDs
+ *	- the user wants the 'numbering' (C:, D:, ...) that one would get
+ *	  on MSDOS 6.x.
+ *	  That means any non-FAT partition type (NTFS, HPFS, or any Linux fs)
+ *	  will not factor in the enumeration.
+ * These days, such assumptions should be revisited. FAT is no longer the
+ * only game in 'PC town'.
+ */
+/*
+ * isDosDrive()
+ *	Boolean function.  Give it the systid field for an fdisk partition
+ *	and it decides if that's a systid that describes a DOS drive.  We
+ *	use systid values defined in sys/dktp/fdisk.h.
+ */
+static int
+isDosDrive(uchar_t checkMe)
+{
+	return ((checkMe == DOSOS12) || (checkMe == DOSOS16) ||
+	    (checkMe == DOSHUGE) || (checkMe == FDISK_WINDOWS) ||
+	    (checkMe == FDISK_EXT_WIN) || (checkMe == FDISK_FAT95) ||
+	    (checkMe == DIAGPART));
+}
+
+
+/*
+ * isDosExtended()
+ *	Boolean function.  Give it the systid field for an fdisk partition
+ *	and it decides if that's a systid that describes an extended DOS
+ *	partition.
+ */
+static int
+isDosExtended(uchar_t checkMe)
+{
+	return ((checkMe == EXTDOS) || (checkMe == FDISK_EXTLBA));
+}
+
+
+/*
+ * isBootPart()
+ *	Boolean function.  Give it the systid field for an fdisk partition
+ *	and it decides if that's a systid that describes a Solaris boot
+ *	partition.
+ */
+static int
+isBootPart(uchar_t checkMe)
+{
+	return (checkMe == X86BOOT);
+}
+
+
+/*
+ * noLogicalDrive()
+ *	Display error message about not being able to find a logical
+ *	drive.
+ */
+static void
+noLogicalDrive(int ldrive)
+{
+	if (ldrive == BOOT_PARTITION_DRIVE) {
+		cmn_err(CE_NOTE, "!pcfs: no boot partition");
+	} else {
+		cmn_err(CE_NOTE, "!pcfs: %d: no such logical drive", ldrive);
+	}
+}
+
+
+/*
+ * findTheDrive()
+ *	Discover offset of the requested logical drive, and return
+ *	that offset (startSector), the systid of that drive (sysid),
+ *	and a buffer pointer (bp), with the buffer contents being
+ *	the first sector of the logical drive (i.e., the sector that
+ *	contains the BPB for that drive).
+ *
+ * Note: this code is not capable of addressing >2TB disks, as it uses
+ *       daddr_t not diskaddr_t, some of the calculations would overflow
+ */
+#define	COPY_PTBL(mbr, ptblp)					\
+	bcopy(&(((struct mboot *)(mbr))->parts), (ptblp),	\
+	    FD_NUMPART * sizeof (struct ipart))
+
+static int
+findTheDrive(struct pcfs *fsp, buf_t **bp)
+{
+	int ldrive = fsp->pcfs_ldrive;
+	dev_t dev = fsp->pcfs_devvp->v_rdev;
+
+	struct ipart dosp[FD_NUMPART];	/* incore fdisk partition structure */
+	daddr_t lastseek = 0;		/* Disk block we sought previously */
+	daddr_t diskblk = 0;		/* Disk block to get */
+	daddr_t xstartsect;		/* base of Extended DOS partition */
+	int logicalDriveCount = 0;	/* Count of logical drives seen */
+	int extendedPart = -1;		/* index of extended dos partition */
+	int primaryPart = -1;		/* index of primary dos partition */
+	int bootPart = -1;		/* index of a Solaris boot partition */
+	int xnumsect = -1;		/* length of extended DOS partition */
+	int driveIndex;			/* computed FDISK table index */
+	daddr_t startsec;
+	len_t mediasize;
+	int i;
+	/*
+	 * Count of drives in the current extended partition's
+	 * FDISK table, and indexes of the drives themselves.
+	 */
+	int extndDrives[FD_NUMPART];
+	int numDrives = 0;
+
+	/*
+	 * Count of drives (beyond primary) in master boot record's
+	 * FDISK table, and indexes of the drives themselves.
+	 */
+	int extraDrives[FD_NUMPART];
+	int numExtraDrives = 0;
+
+	/*
+	 * "ldrive == 0" should never happen, as this is a request to
+	 * mount the physical device (and ignore partitioning). The code
+	 * in pcfs_mount() should have made sure that a logical drive number
+	 * is at least 1, meaning we're looking for drive "C:". It is not
+	 * safe (and a bug in the callers of this function) to request logical
+	 * drive number 0; we could ASSERT() but a graceful EIO is a more
+	 * polite way.
+	 */
+	if (ldrive == 0) {
+		cmn_err(CE_NOTE, "!pcfs: request for logical partition zero");
+		noLogicalDrive(ldrive);
+		return (EIO);
+	}
+
+	/*
+	 *  Copy from disk block into memory aligned structure for fdisk usage.
+	 */
+	COPY_PTBL((*bp)->b_un.b_addr, dosp);
+
+	/*
+	 * This check is ok because a FAT BPB and a master boot record (MBB)
+	 * have the same signature, in the same position within the block.
+	 */
+	if (bpb_get_BPBSig((*bp)->b_un.b_addr) != MBB_MAGIC) {
+		cmn_err(CE_NOTE, "!pcfs: MBR partition table signature err, "
+		    "device (%x.%x):%d\n",
+		    getmajor(dev), getminor(dev), ldrive);
+		return (EINVAL);
+	}
+
+	/*
+	 * Get a summary of what is in the Master FDISK table.
+	 * Normally we expect to find one partition marked as a DOS drive.
+	 * This partition is the one Windows calls the primary dos partition.
+	 * If the machine has any logical drives then we also expect
+	 * to find a partition marked as an extended DOS partition.
+	 *
+	 * Sometimes we'll find multiple partitions marked as DOS drives.
+	 * The Solaris fdisk program allows these partitions
+	 * to be created, but Windows fdisk no longer does.  We still need
+	 * to support these, though, since Windows does.  We also need to fix
+	 * our fdisk to behave like the Windows version.
+	 *
+	 * It turns out that some off-the-shelf media have *only* an
+	 * Extended partition, so we need to deal with that case as well.
+	 *
+	 * Only a single (the first) Extended or Boot Partition will
+	 * be recognized.  Any others will be ignored.
+	 */
+	for (i = 0; i < FD_NUMPART; i++) {
+		DTRACE_PROBE4(primarypart, struct pcfs *, fsp,
+		    uint_t, (uint_t)dosp[i].systid,
+		    uint_t, LE_32(dosp[i].relsect),
+		    uint_t, LE_32(dosp[i].numsect));
+
+		if (isDosDrive(dosp[i].systid)) {
+			if (primaryPart < 0) {
+				logicalDriveCount++;
+				primaryPart = i;
+			} else {
+				extraDrives[numExtraDrives++] = i;
+			}
+			continue;
+		}
+		if ((extendedPart < 0) && isDosExtended(dosp[i].systid)) {
+			extendedPart = i;
+			continue;
+		}
+		if ((bootPart < 0) && isBootPart(dosp[i].systid)) {
+			bootPart = i;
+			continue;
+		}
+	}
+
+	if (ldrive == BOOT_PARTITION_DRIVE) {
+		if (bootPart < 0) {
+			noLogicalDrive(ldrive);
+			return (EINVAL);
+		}
+		startsec = LE_32(dosp[bootPart].relsect);
+		mediasize = LE_32(dosp[bootPart].numsect);
+		goto found;
+	}
+
+	if (ldrive == PRIMARY_DOS_DRIVE && primaryPart >= 0) {
+		startsec = LE_32(dosp[primaryPart].relsect);
+		mediasize = LE_32(dosp[primaryPart].numsect);
+		goto found;
+	}
+
+	/*
+	 * We are not looking for the C: drive (or the primary drive
+	 * was not found), so we had better have an extended partition
+	 * or extra drives in the Master FDISK table.
+	 */
+	if ((extendedPart < 0) && (numExtraDrives == 0)) {
+		cmn_err(CE_NOTE, "!pcfs: no extended dos partition");
+		noLogicalDrive(ldrive);
+		return (EINVAL);
+	}
+
+	if (extendedPart >= 0) {
+		diskblk = xstartsect = LE_32(dosp[extendedPart].relsect);
+		xnumsect = LE_32(dosp[extendedPart].numsect);
+		do {
+			/*
+			 *  If the seek would not cause us to change
+			 *  position on the drive, then we're out of
+			 *  extended partitions to examine.
+			 */
+			if (diskblk == lastseek)
+				break;
+			logicalDriveCount += numDrives;
+			/*
+			 *  Seek the next extended partition, and find
+			 *  logical drives within it.
+			 */
+			brelse(*bp);
+			/*
+			 * bread() block numbers are multiples of DEV_BSIZE
+			 * but the device sector size (the unit of partitioning)
+			 * might be larger than that; pcfs_get_device_info()
+			 * has calculated the multiplicator for us.
+			 */
+			*bp = bread(dev,
+			    pc_dbdaddr(fsp, diskblk), fsp->pcfs_secsize);
+			if ((*bp)->b_flags & B_ERROR) {
+				return (EIO);
+			}
+
+			lastseek = diskblk;
+			COPY_PTBL((*bp)->b_un.b_addr, dosp);
+			if (bpb_get_BPBSig((*bp)->b_un.b_addr) != MBB_MAGIC) {
+				cmn_err(CE_NOTE, "!pcfs: "
+				    "extended partition table signature err, "
+				    "device (%x.%x):%d, LBA %u",
+				    getmajor(dev), getminor(dev), ldrive,
+				    (uint_t)pc_dbdaddr(fsp, diskblk));
+				return (EINVAL);
+			}
+			/*
+			 *  Count up drives, and track where the next
+			 *  extended partition is in case we need it.  We
+			 *  are expecting only one extended partition.  If
+			 *  there is more than one we'll only go to the
+			 *  first one we see, but warn about ignoring.
+			 */
+			numDrives = 0;
+			for (i = 0; i < FD_NUMPART; i++) {
+				DTRACE_PROBE4(extendedpart,
+				    struct pcfs *, fsp,
+				    uint_t, (uint_t)dosp[i].systid,
+				    uint_t, LE_32(dosp[i].relsect),
+				    uint_t, LE_32(dosp[i].numsect));
+				if (isDosDrive(dosp[i].systid)) {
+					extndDrives[numDrives++] = i;
+				} else if (isDosExtended(dosp[i].systid)) {
+					if (diskblk != lastseek) {
+						/*
+						 * Already found an extended
+						 * partition in this table.
+						 */
+						cmn_err(CE_NOTE,
+						    "!pcfs: ignoring unexpected"
+						    " additional extended"
+						    " partition");
+					} else {
+						diskblk = xstartsect +
+						    LE_32(dosp[i].relsect);
+					}
+				}
+			}
+		} while (ldrive > logicalDriveCount + numDrives);
+
+		ASSERT(numDrives <= FD_NUMPART);
+
+		if (ldrive <= logicalDriveCount + numDrives) {
+			/*
+			 * The number of logical drives we've found thus
+			 * far is enough to get us to the one we were
+			 * searching for.
+			 */
+			driveIndex = logicalDriveCount + numDrives - ldrive;
+			mediasize =
+			    LE_32(dosp[extndDrives[driveIndex]].numsect);
+			startsec =
+			    LE_32(dosp[extndDrives[driveIndex]].relsect) +
+			    lastseek;
+			if (startsec > (xstartsect + xnumsect)) {
+				cmn_err(CE_NOTE, "!pcfs: extended partition "
+				    "values bad");
+				return (EINVAL);
+			}
+			goto found;
+		} else {
+			/*
+			 * We ran out of extended dos partition
+			 * drives.  The only hope now is to go
+			 * back to extra drives defined in the master
+			 * fdisk table.  But we overwrote that table
+			 * already, so we must load it in again.
+			 */
+			logicalDriveCount += numDrives;
+			brelse(*bp);
+			ASSERT(fsp->pcfs_dosstart == 0);
+			*bp = bread(dev, pc_dbdaddr(fsp, fsp->pcfs_dosstart),
+			    fsp->pcfs_secsize);
+			if ((*bp)->b_flags & B_ERROR) {
+				return (EIO);
+			}
+			COPY_PTBL((*bp)->b_un.b_addr, dosp);
+		}
+	}
+	/*
+	 *  Still haven't found the drive, is it an extra
+	 *  drive defined in the main FDISK table?
+	 */
+	if (ldrive <= logicalDriveCount + numExtraDrives) {
+		driveIndex = logicalDriveCount + numExtraDrives - ldrive;
+		ASSERT(driveIndex < MIN(numExtraDrives, FD_NUMPART));
+		mediasize = LE_32(dosp[extraDrives[driveIndex]].numsect);
+		startsec = LE_32(dosp[extraDrives[driveIndex]].relsect);
+		goto found;
+	}
+	/*
+	 *  Still haven't found the drive, and there is
+	 *  nowhere else to look.
+	 */
+	noLogicalDrive(ldrive);
+	return (EINVAL);
+
+found:
+	/*
+	 * We need this value in units of sectorsize, because PCFS' internal
+	 * offset calculations go haywire for > 512Byte sectors unless all
+	 * pcfs_.*start values are in units of sectors.
+	 * So, assign before the capacity check (that's done in DEV_BSIZE)
+	 */
+	fsp->pcfs_dosstart = startsec;
+
+	/*
+	 * convert from device sectors to proper units:
+	 *	- starting sector: DEV_BSIZE (as argument to bread())
+	 *	- media size: Bytes
+	 */
+	startsec = pc_dbdaddr(fsp, startsec);
+	mediasize *= fsp->pcfs_secsize;
+
+	/*
+	 * some additional validation / warnings in case the partition table
+	 * and the actual media capacity are not in accordance ...
+	 */
+	if (fsp->pcfs_mediasize != 0) {
+		diskaddr_t startoff =
+		    (diskaddr_t)startsec * (diskaddr_t)DEV_BSIZE;
+
+		if (startoff >= fsp->pcfs_mediasize ||
+		    startoff + mediasize > fsp->pcfs_mediasize) {
+			cmn_err(CE_WARN,
+			    "!pcfs: partition size (LBA start %u, %lld bytes, "
+			    "device (%x.%x):%d) smaller than "
+			    "mediasize (%lld bytes).\n"
+			    "filesystem may be truncated, access errors "
+			    "may result.\n",
+			    (uint_t)startsec, (long long)mediasize,
+			    getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev),
+			    fsp->pcfs_ldrive, (long long)fsp->pcfs_mediasize);
+		}
+	} else {
+		fsp->pcfs_mediasize = mediasize;
+	}
+
+	return (0);
+}
+
+
+static fattype_t
+secondaryBPBChecks(struct pcfs *fsp, uchar_t *bpb, size_t secsize)
+{
+	uint32_t ncl = fsp->pcfs_ncluster;
+
+	if (ncl <= 4096) {
+		if (bpb_get_FatSz16(bpb) == 0)
+			return (FAT_UNKNOWN);
+
+		if (bpb_get_FatSz16(bpb) * secsize < ncl * 2 &&
+		    bpb_get_FatSz16(bpb) * secsize >= (3 * ncl / 2))
+			return (FAT12);
+		if (bcmp(bpb_FilSysType16(bpb), "FAT12", 5) == 0)
+			return (FAT12);
+		if (bcmp(bpb_FilSysType16(bpb), "FAT16", 5) == 0)
+			return (FAT16);
+
+		switch (bpb_get_Media(bpb)) {
+			case SS8SPT:
+			case DS8SPT:
+			case SS9SPT:
+			case DS9SPT:
+			case DS18SPT:
+			case DS9_15SPT:
+				/*
+				 * Is this reliable - all floppies are FAT12 ?
+				 */
+				return (FAT12);
+			case MD_FIXED:
+				/*
+				 * Is this reliable - disks are always FAT16 ?
+				 */
+				return (FAT16);
+			default:
+				break;
+		}
+	} else if (ncl <= 65536) {
+		if (bpb_get_FatSz16(bpb) == 0 && bpb_get_FatSz32(bpb) > 0)
+			return (FAT32);
+		if (VALID_BOOTSIG(bpb_get_BootSig32(bpb)))
+			return (FAT32);
+		if (VALID_FSTYPSTR32(bpb_FilSysType32(bpb)))
+			return (FAT32);
+
+		if (VALID_BOOTSIG(bpb_get_BootSig16(bpb)))
+			return (FAT16);
+		if (bpb_get_FatSz16(bpb) * secsize < ncl * 4)
+			return (FAT16);
+	}
+
+	/*
+	 * We don't know
+	 */
+	return (FAT_UNKNOWN);
+}
+
+/*
+ * Check to see if the BPB we found is correct.
+ *
+ * This looks far more complicated that it needs to be for pure structural
+ * validation. The reason for this is that parseBPB() is also used for
+ * debugging purposes (mdb dcmd) and we therefore want a bitmap of which
+ * BPB fields have 'known good' values, even if we do not reject the BPB
+ * when attempting to mount the filesystem.
+ */
+static int
+parseBPB(struct pcfs *fsp, uchar_t *bpb, int *valid)
+{
+	fattype_t type;
+
+	uint32_t	ncl;	/* number of clusters in file area */
+	uint32_t	rec;
+	uint32_t	reserved;
+	uint32_t	fsisec, bkbootsec;
+	blkcnt_t	totsec, totsec16, totsec32, datasec;
+	size_t		fatsec, fatsec16, fatsec32, rdirsec;
+	size_t		secsize;
+	len_t		mediasize;
+	uint64_t	validflags = 0;
+
+	if (VALID_BPBSIG(bpb_get_BPBSig(bpb)))
+		validflags |= BPB_BPBSIG_OK;
+
+	rec = bpb_get_RootEntCnt(bpb);
+	reserved = bpb_get_RsvdSecCnt(bpb);
+	fsisec = bpb_get_FSInfo32(bpb);
+	bkbootsec = bpb_get_BkBootSec32(bpb);
+	totsec16 = (blkcnt_t)bpb_get_TotSec16(bpb);
+	totsec32 = (blkcnt_t)bpb_get_TotSec32(bpb);
+	fatsec16 = bpb_get_FatSz16(bpb);
+	fatsec32 = bpb_get_FatSz32(bpb);
+
+	totsec = totsec16 ? totsec16 : totsec32;
+	fatsec = fatsec16 ? fatsec16 : fatsec32;
+
+	secsize = bpb_get_BytesPerSec(bpb);
+	if (!VALID_SECSIZE(secsize))
+		secsize = fsp->pcfs_secsize;
+	if (secsize != fsp->pcfs_secsize) {
+		PC_DPRINTF3(3, "!pcfs: parseBPB, device (%x.%x):%d:\n",
+		    getmajor(fsp->pcfs_xdev),
+		    getminor(fsp->pcfs_xdev), fsp->pcfs_ldrive);
+		PC_DPRINTF2(3, "!BPB secsize %d != "
+		    "autodetected media block size %d\n",
+		    (int)secsize, (int)fsp->pcfs_secsize);
+		if (fsp->pcfs_ldrive) {
+			/*
+			 * We've already attempted to parse the partition
+			 * table. If the block size used for that don't match
+			 * the PCFS sector size, we're hosed one way or the
+			 * other. Just try what happens.
+			 */
+			secsize = fsp->pcfs_secsize;
+			PC_DPRINTF1(3,
+			    "!pcfs: Using autodetected secsize %d\n",
+			    (int)secsize);
+		} else {
+			/*
+			 * This allows mounting lofi images of PCFS partitions
+			 * with sectorsize != DEV_BSIZE. We can't parse the
+			 * partition table on whole-disk images unless the
+			 * (undocumented) "secsize=..." mount option is used,
+			 * but at least this allows us to mount if we have
+			 * an image of a partition.
+			 */
+			PC_DPRINTF1(3,
+			    "!pcfs: Using BPB secsize %d\n", (int)secsize);
+		}
+	}
+
+	if (fsp->pcfs_mediasize == 0) {
+		mediasize = (len_t)totsec * (len_t)secsize;
+		PC_DPRINTF4(3, "!pcfs: parseBPB: mediasize autodetect failed "
+		    "on device (%x.%x):%d, trusting BPB totsec (%lld Bytes)\n",
+		    getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev),
+		    fsp->pcfs_ldrive, (long long)fsp->pcfs_mediasize);
+	} else if ((len_t)totsec * (len_t)secsize > fsp->pcfs_mediasize) {
+		cmn_err(CE_WARN,
+		    "!pcfs: autodetected mediasize (%lld Bytes) smaller than "
+		    "FAT BPB mediasize (%lld Bytes).\n"
+		    "truncated filesystem on device (%x.%x):%d, access errors "
+		    "possible.\n",
+		    (long long)fsp->pcfs_mediasize,
+		    (long long)(totsec * (blkcnt_t)secsize),
+		    getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev),
+		    fsp->pcfs_ldrive);
+		mediasize = fsp->pcfs_mediasize;
+	} else {
+		/*
+		 * This is actually ok. A FAT needs not occupy the maximum
+		 * space available in its partition, it can be shorter.
+		 */
+		mediasize = (len_t)totsec * (len_t)secsize;
+	}
+
+	/*
+	 * Since we let just about anything pass through this function,
+	 * fence against divide-by-zero here.
+	 */
+	if (secsize)
+		rdirsec = roundup(rec * 32, secsize) / secsize;
+	else
+		rdirsec = 0;
+
+	/*
+	 * This assignment is necessary before pc_dbdaddr() can first be
+	 * used. Must initialize the value here.
+	 */
+	fsp->pcfs_secsize = secsize;
+	fsp->pcfs_sdshift = ddi_ffs(secsize / DEV_BSIZE) - 1;
+
+	fsp->pcfs_mediasize = mediasize;
+
+	fsp->pcfs_spcl = bpb_get_SecPerClus(bpb);
+	fsp->pcfs_numfat = bpb_get_NumFATs(bpb);
+	fsp->pcfs_mediadesc = bpb_get_Media(bpb);
+	fsp->pcfs_clsize = secsize * fsp->pcfs_spcl;
+	fsp->pcfs_rdirsec = rdirsec;
+
+	/*
+	 * Remember: All PCFS offset calculations in sectors. Before I/O
+	 * is done, convert to DEV_BSIZE units via pc_dbdaddr(). This is
+	 * necessary so that media with > 512Byte sector sizes work correctly.
+	 */
+	fsp->pcfs_fatstart = fsp->pcfs_dosstart + reserved;
+	fsp->pcfs_rdirstart = fsp->pcfs_fatstart + fsp->pcfs_numfat * fatsec;
+	fsp->pcfs_datastart = fsp->pcfs_rdirstart + rdirsec;
+	datasec = totsec -
+	    (blkcnt_t)fatsec * fsp->pcfs_numfat -
+	    (blkcnt_t)rdirsec -
+	    (blkcnt_t)reserved;
+
+	DTRACE_PROBE4(fatgeometry,
+	    blkcnt_t, totsec, size_t, fatsec,
+	    size_t, rdirsec, blkcnt_t, datasec);
+
+	/*
+	 * UINT32_MAX is an underflow check - we calculate in "blkcnt_t" which
+	 * is 64bit in order to be able to catch "impossible" sector counts.
+	 * A sector count in FAT must fit 32bit unsigned int.
+	 */
+	if (totsec != 0 &&
+	    (totsec16 == totsec32 || totsec16 == 0 || totsec32 == 0) &&
+	    (len_t)totsec * (len_t)secsize <= mediasize &&
+	    datasec < totsec && datasec <= UINT32_MAX)
+		validflags |= BPB_TOTSEC_OK;
+
+	if (mediasize >= (len_t)datasec * (len_t)secsize)
+		validflags |= BPB_MEDIASZ_OK;
+
+	if (VALID_SECSIZE(secsize))
+		validflags |= BPB_SECSIZE_OK;
+	if (VALID_SPCL(fsp->pcfs_spcl))
+		validflags |= BPB_SECPERCLUS_OK;
+	if (VALID_CLSIZE(fsp->pcfs_clsize))
+		validflags |= BPB_CLSIZE_OK;
+	if (VALID_NUMFATS(fsp->pcfs_numfat))
+		validflags |= BPB_NUMFAT_OK;
+	if (VALID_RSVDSEC(reserved) && reserved < totsec)
+		validflags |= BPB_RSVDSECCNT_OK;
+	if (VALID_MEDIA(fsp->pcfs_mediadesc))
+		validflags |= BPB_MEDIADESC_OK;
+	if (VALID_BOOTSIG(bpb_get_BootSig16(bpb)))
+		validflags |= BPB_BOOTSIG16_OK;
+	if (VALID_BOOTSIG(bpb_get_BootSig32(bpb)))
+		validflags |= BPB_BOOTSIG32_OK;
+	if (VALID_FSTYPSTR16(bpb_FilSysType16(bpb)))
+		validflags |= BPB_FSTYPSTR16_OK;
+	if (VALID_FSTYPSTR32(bpb_FilSysType32(bpb)))
+		validflags |= BPB_FSTYPSTR32_OK;
+	if (VALID_OEMNAME(bpb_OEMName(bpb)))
+		validflags |= BPB_OEMNAME_OK;
+	if (bkbootsec > 0 && bkbootsec <= reserved && fsisec != bkbootsec)
+		validflags |= BPB_BKBOOTSEC_OK;
+	if (fsisec > 0 && fsisec <= reserved)
+		validflags |= BPB_FSISEC_OK;
+	if (VALID_JMPBOOT(bpb_jmpBoot(bpb)))
+		validflags |= BPB_JMPBOOT_OK;
+	if (VALID_FSVER32(bpb_get_FSVer32(bpb)))
+		validflags |= BPB_FSVER_OK;
+	if (VALID_VOLLAB(bpb_VolLab16(bpb)))
+		validflags |= BPB_VOLLAB16_OK;
+	if (VALID_VOLLAB(bpb_VolLab32(bpb)))
+		validflags |= BPB_VOLLAB32_OK;
+	if (VALID_EXTFLAGS(bpb_get_ExtFlags32(bpb)))
+		validflags |= BPB_EXTFLAGS_OK;
+
+	/*
+	 * Try to determine which FAT format to use.
+	 *
+	 * Calculate the number of clusters in order to determine
+	 * the type of FAT we are looking at.  This is the only
+	 * recommended way of determining FAT type, though there
+	 * are other hints in the data, this is the best way.
+	 *
+	 * Since we let just about "anything" pass through this function
+	 * without early exits, fence against divide-by-zero here.
+	 *
+	 * datasec was already validated against UINT32_MAX so we know
+	 * the result will not overflow the 32bit calculation.
+	 */
+	if (fsp->pcfs_spcl)
+		ncl = (uint32_t)datasec / fsp->pcfs_spcl;
+	else
+		ncl = 0;
+
+	fsp->pcfs_ncluster = ncl;
+
+	/*
+	 * From the Microsoft FAT specification:
+	 * In the following example, when it says <, it does not mean <=.
+	 * Note also that the numbers are correct.  The first number for
+	 * FAT12 is 4085; the second number for FAT16 is 65525. These numbers
+	 * and the '<' signs are not wrong.
+	 *
+	 * We "specialdetect" the corner cases, and use at least one "extra"
+	 * criterion to decide whether it's FAT16 or FAT32 if the cluster
+	 * count is dangerously close to the boundaries.
+	 */
+
+	if (ncl <= PCF_FIRSTCLUSTER) {
+		type = FAT_UNKNOWN;
+	} else if (ncl < 4085) {
+		type = FAT12;
+	} else if (ncl <= 4096) {
+		type = FAT_QUESTIONABLE;
+	} else if (ncl < 65525) {
+		type = FAT16;
+	} else if (ncl <= 65536) {
+		type = FAT_QUESTIONABLE;
+	} else if (ncl < PCF_LASTCLUSTER32) {
+		type = FAT32;
+	} else {
+		type = FAT_UNKNOWN;
+	}
+
+	DTRACE_PROBE4(parseBPB__initial,
+	    struct pcfs *, fsp, unsigned char *, bpb,
+	    int, validflags, fattype_t, type);
+
+recheck:
+	fsp->pcfs_fatsec = fatsec;
+
+	/* Do some final sanity checks for each specific type of FAT */
+	switch (type) {
+		case FAT12:
+			if (rec != 0)
+				validflags |= BPB_ROOTENTCNT_OK;
+			if ((blkcnt_t)bpb_get_TotSec16(bpb) == totsec ||
+			    bpb_get_TotSec16(bpb) == 0)
+				validflags |= BPB_TOTSEC16_OK;
+			if ((blkcnt_t)bpb_get_TotSec32(bpb) == totsec ||
+			    bpb_get_TotSec32(bpb) == 0)
+				validflags |= BPB_TOTSEC32_OK;
+			if (bpb_get_FatSz16(bpb) == fatsec)
+				validflags |= BPB_FATSZ16_OK;
+			if (fatsec * secsize >= ncl * 3 / 2)
+				validflags |= BPB_FATSZ_OK;
+			if (ncl < 4085)
+				validflags |= BPB_NCLUSTERS_OK;
+
+			fsp->pcfs_lastclmark = (PCF_LASTCLUSTER & 0xfff);
+			fsp->pcfs_rootblksize =
+			    fsp->pcfs_rdirsec * secsize;
+			fsp->pcfs_fsistart = 0;
+
+			if ((validflags & FAT12_VALIDMSK) != FAT12_VALIDMSK)
+				type = FAT_UNKNOWN;
+			break;
+		case FAT16:
+			if (rec != 0)
+				validflags |= BPB_ROOTENTCNT_OK;
+			if ((blkcnt_t)bpb_get_TotSec16(bpb) == totsec ||
+			    bpb_get_TotSec16(bpb) == 0)
+				validflags |= BPB_TOTSEC16_OK;
+			if ((blkcnt_t)bpb_get_TotSec32(bpb) == totsec ||
+			    bpb_get_TotSec32(bpb) == 0)
+				validflags |= BPB_TOTSEC32_OK;
+			if (bpb_get_FatSz16(bpb) == fatsec)
+				validflags |= BPB_FATSZ16_OK;
+			if (fatsec * secsize >= ncl * 2)
+				validflags |= BPB_FATSZ_OK;
+			if (ncl >= 4085 && ncl < 65525)
+				validflags |= BPB_NCLUSTERS_OK;
+
+			fsp->pcfs_lastclmark = PCF_LASTCLUSTER;
+			fsp->pcfs_rootblksize =
+			    fsp->pcfs_rdirsec * secsize;
+			fsp->pcfs_fsistart = 0;
+
+			if ((validflags & FAT16_VALIDMSK) != FAT16_VALIDMSK)
+				type = FAT_UNKNOWN;
+			break;
+		case FAT32:
+			if (rec == 0)
+				validflags |= BPB_ROOTENTCNT_OK;
+			if (bpb_get_TotSec16(bpb) == 0)
+				validflags |= BPB_TOTSEC16_OK;
+			if ((blkcnt_t)bpb_get_TotSec32(bpb) == totsec)
+				validflags |= BPB_TOTSEC32_OK;
+			if (bpb_get_FatSz16(bpb) == 0)
+				validflags |= BPB_FATSZ16_OK;
+			if (bpb_get_FatSz32(bpb) == fatsec)
+				validflags |= BPB_FATSZ32_OK;
+			if (fatsec * secsize >= ncl * 4)
+				validflags |= BPB_FATSZ_OK;
+			if (ncl >= 65525 && ncl < PCF_LASTCLUSTER32)
+				validflags |= BPB_NCLUSTERS_OK;
+
+			fsp->pcfs_lastclmark = PCF_LASTCLUSTER32;
+			fsp->pcfs_rootblksize = fsp->pcfs_clsize;
+			fsp->pcfs_fsistart = fsp->pcfs_dosstart + fsisec;
+			if (validflags & BPB_FSISEC_OK)
+				fsp->pcfs_flags |= PCFS_FSINFO_OK;
+			fsp->pcfs_rootclnum = bpb_get_RootClus32(bpb);
+			if (pc_validcl(fsp, fsp->pcfs_rootclnum))
+				validflags |= BPB_ROOTCLUSTER_OK;
+
+			/*
+			 * Current PCFS code only works if 'pcfs_rdirstart'
+			 * contains the root cluster number on FAT32.
+			 * That's a mis-use and would better be changed.
+			 */
+			fsp->pcfs_rdirstart = (daddr_t)fsp->pcfs_rootclnum;
+
+			if ((validflags & FAT32_VALIDMSK) != FAT32_VALIDMSK)
+				type = FAT_UNKNOWN;
+			break;
+		case FAT_QUESTIONABLE:
+			type = secondaryBPBChecks(fsp, bpb, secsize);
+			goto recheck;
+		default:
+			ASSERT(type == FAT_UNKNOWN);
+			break;
+	}
+
+	ASSERT(type != FAT_QUESTIONABLE);
+
+	fsp->pcfs_fattype = type;
+
+	if (valid)
+		*valid = validflags;
+
+	DTRACE_PROBE4(parseBPB__final,
+	    struct pcfs *, fsp, unsigned char *, bpb,
+	    int, validflags, fattype_t, type);
+
+	if (type != FAT_UNKNOWN) {
+		ASSERT((secsize & (DEV_BSIZE - 1)) == 0);
+		ASSERT(ISP2(secsize / DEV_BSIZE));
+		return (1);
+	}
+
+	return (0);
+}
+
+
+/*
+ * Detect the device's native block size (sector size).
+ *
+ * Test whether the device is:
+ *	- a floppy device from a known controller type via DKIOCINFO
+ *	- a real floppy using the fd(7d) driver and capable of fdio(7I) ioctls
+ *	- a PCMCIA sram memory card (pseudofloppy) using pcram(7d)
+ *	- a USB floppy drive (identified by drive geometry)
+ *
+ * Detecting a floppy will make PCFS metadata updates on such media synchronous,
+ * to minimize risks due to slow I/O and user hotplugging / device ejection.
+ *
+ * This might be a bit wasteful on kernel stack space; if anyone's
+ * bothered by this, kmem_alloc/kmem_free the ioctl arguments...
+ */
+static void
+pcfs_device_getinfo(struct pcfs *fsp)
+{
+	dev_t			rdev = fsp->pcfs_xdev;
+	int			error;
+	union {
+		struct dk_minfo		mi;
+		struct dk_cinfo		ci;
+		struct dk_geom		gi;
+		struct fd_char		fc;
+	} arg;				/* save stackspace ... */
+	intptr_t argp = (intptr_t)&arg;
+	ldi_handle_t		lh;
+	ldi_ident_t		li;
+	int isfloppy, isremoveable, ishotpluggable;
+	cred_t			*cr = CRED();
+
+	if (ldi_ident_from_dev(rdev, &li))
+		goto out;
+
+	error = ldi_open_by_dev(&rdev, OTYP_CHR, FREAD, cr, &lh, li);
+	ldi_ident_release(li);
+	if (error)
+		goto out;
+
+	/*
+	 * Not sure if this could possibly happen. It'd be a bit like
+	 * VOP_OPEN() changing the passed-in vnode ptr. We're just not
+	 * expecting it, needs some thought if triggered ...
+	 */
+	ASSERT(fsp->pcfs_xdev == rdev);
+
+	/*
+	 * Check for removeable/hotpluggable media.
+	 */
+	if (ldi_ioctl(lh, DKIOCREMOVABLE,
+	    (intptr_t)&isremoveable, FKIOCTL, cr, NULL)) {
+		isremoveable = 0;
+	}
+	if (ldi_ioctl(lh, DKIOCHOTPLUGGABLE,
+	    (intptr_t)&ishotpluggable, FKIOCTL, cr, NULL)) {
+		ishotpluggable = 0;
+	}
+
+	/*
+	 * Make sure we don't use "half-initialized" values if the ioctls fail.
+	 */
+	if (ldi_ioctl(lh, DKIOCGMEDIAINFO, argp, FKIOCTL, cr, NULL)) {
+		bzero(&arg, sizeof (arg));
+		fsp->pcfs_mediasize = 0;
+	} else {
+		fsp->pcfs_mediasize =
+		    (len_t)arg.mi.dki_lbsize *
+		    (len_t)arg.mi.dki_capacity;
+	}
+
+	if (VALID_SECSIZE(arg.mi.dki_lbsize)) {
+		if (fsp->pcfs_secsize == 0) {
+			fsp->pcfs_secsize = arg.mi.dki_lbsize;
+			fsp->pcfs_sdshift =
+			    ddi_ffs(arg.mi.dki_lbsize / DEV_BSIZE) - 1;
+		} else {
+			PC_DPRINTF4(1, "!pcfs: autodetected media block size "
+			    "%d, device (%x.%x), different from user-provided "
+			    "%d. User override - ignoring autodetect result.\n",
+			    arg.mi.dki_lbsize,
+			    getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev),
+			    fsp->pcfs_secsize);
+		}
+	} else if (arg.mi.dki_lbsize) {
+		PC_DPRINTF3(1, "!pcfs: autodetected media block size "
+		    "%d, device (%x.%x), invalid (not 512, 1024, 2048, 4096). "
+		    "Ignoring autodetect result.\n",
+		    arg.mi.dki_lbsize,
+		    getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev));
+	}
+
+	/*
+	 * We treat the following media types as a floppy by default.
+	 */
+	isfloppy =
+	    (arg.mi.dki_media_type == DK_FLOPPY ||
+	    arg.mi.dki_media_type == DK_ZIP ||
+	    arg.mi.dki_media_type == DK_JAZ);
+
+	/*
+	 * if this device understands fdio(7I) requests it's
+	 * obviously a floppy drive.
+	 */
+	if (!isfloppy &&
+	    !ldi_ioctl(lh, FDIOGCHAR, argp, FKIOCTL, cr, NULL))
+		isfloppy = 1;
+
+	/*
+	 * some devices (PCMCIA pseudofloppies) we like to treat
+	 * as floppies, but they don't understand fdio(7I) requests.
+	 */
+	if (!isfloppy &&
+	    !ldi_ioctl(lh, DKIOCINFO, argp, FKIOCTL, cr, NULL) &&
+	    (arg.ci.dki_ctype == DKC_WDC2880 ||
+	    arg.ci.dki_ctype == DKC_NCRFLOPPY ||
+	    arg.ci.dki_ctype == DKC_SMSFLOPPY ||
+	    arg.ci.dki_ctype == DKC_INTEL82077 ||
+	    (arg.ci.dki_ctype == DKC_PCMCIA_MEM &&
+	    arg.ci.dki_flags & DKI_PCMCIA_PFD)))
+		isfloppy = 1;
+
+	/*
+	 * This is the "final fallback" test - media with
+	 * 2 heads and 80 cylinders are assumed to be floppies.
+	 * This is normally true for USB floppy drives ...
+	 */
+	if (!isfloppy &&
+	    !ldi_ioctl(lh, DKIOCGGEOM, argp, FKIOCTL, cr, NULL) &&
+	    (arg.gi.dkg_ncyl == 80 && arg.gi.dkg_nhead == 2))
+		isfloppy = 1;
+
+	/*
+	 * This is similar to the "old" PCFS code that sets this flag
+	 * just based on the media descriptor being 0xf8 (MD_FIXED).
+	 * Should be re-worked. We really need some specialcasing for
+	 * removeable media.
+	 */
+	if (!isfloppy) {
+		fsp->pcfs_flags |= PCFS_NOCHK;
+	}
+
+	/*
+	 * We automatically disable access time updates if the medium is
+	 * removeable and/or hotpluggable, and the admin did not explicitly
+	 * request access time updates (via the "atime" mount option).
+	 * The majority of flash-based media should fit this category.
+	 * Minimizing write access extends the lifetime of your memory stick !
+	 */
+	if (!vfs_optionisset(fsp->pcfs_vfs, MNTOPT_ATIME, NULL) &&
+	    (isremoveable || ishotpluggable | isfloppy)) {
+		fsp->pcfs_flags |= PCFS_NOATIME;
+	}
+
+	(void) ldi_close(lh, FREAD, cr);
+out:
+	if (fsp->pcfs_secsize == 0) {
+		PC_DPRINTF3(1, "!pcfs: media block size autodetection "
+		    "device (%x.%x) failed, no user-provided fallback. "
+		    "Using %d bytes.\n",
+		    getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev),
+		    DEV_BSIZE);
+		fsp->pcfs_secsize = DEV_BSIZE;
+		fsp->pcfs_sdshift = 0;
+	}
+	ASSERT(fsp->pcfs_secsize % DEV_BSIZE == 0);
+	ASSERT(VALID_SECSIZE(fsp->pcfs_secsize));
+}
+
+/*
+ * Get the FAT type for the DOS medium.
+ *
+ * -------------------------
+ * According to Microsoft:
+ *   The FAT type one of FAT12, FAT16, or FAT32 is determined by the
+ * count of clusters on the volume and nothing else.
+ * -------------------------
+ *
+ */
+static int
+pc_getfattype(struct pcfs *fsp)
+{
+	int error = 0;
+	buf_t *bp = NULL;
+	struct vnode *devvp = fsp->pcfs_devvp;
+	dev_t	dev = devvp->v_rdev;
+
+	/*
+	 * Detect the native block size of the medium, and attempt to
+	 * detect whether the medium is removeable.
+	 * We do treat removeable media (floppies, PCMCIA memory cards,
+	 * USB and FireWire disks) differently wrt. to the frequency
+	 * and synchronicity of FAT updates.
+	 * We need to know the media block size in order to be able to
+	 * parse the partition table.
+	 */
+	pcfs_device_getinfo(fsp);
+
+	/*
+	 * Unpartitioned media (floppies and some removeable devices)
+	 * don't have a partition table, the FAT BPB is at disk block 0.
+	 * Start out by reading block 0.
+	 */
+	fsp->pcfs_dosstart = 0;
+	bp = bread(dev, pc_dbdaddr(fsp, fsp->pcfs_dosstart), fsp->pcfs_secsize);
+
+	if (error = geterror(bp))
+		goto out;
+
+	/*
+	 * If a logical drive number is requested, parse the partition table
+	 * and attempt to locate it. Otherwise, proceed immediately to the
+	 * BPB check. findTheDrive(), if successful, returns the disk block
+	 * number where the requested partition starts in "startsec".
+	 */
+	if (fsp->pcfs_ldrive != 0) {
+		PC_DPRINTF3(5, "!pcfs: pc_getfattype: using FDISK table on "
+		    "device (%x,%x):%d to find BPB\n",
+		    getmajor(dev), getminor(dev), fsp->pcfs_ldrive);
+
+		if (error = findTheDrive(fsp, &bp))
+			goto out;
+
+		ASSERT(fsp->pcfs_dosstart != 0);
+
+		brelse(bp);
+		bp = bread(dev, pc_dbdaddr(fsp, fsp->pcfs_dosstart),
+		    fsp->pcfs_secsize);
+		if (error = geterror(bp))
+			goto out;
+	}
+
+	/*
+	 * Validate the BPB and fill in the instance structure.
+	 */
+	if (!parseBPB(fsp, (uchar_t *)bp->b_un.b_addr, NULL)) {
+		PC_DPRINTF4(1, "!pcfs: pc_getfattype: No FAT BPB on "
+		    "device (%x.%x):%d, disk LBA %u\n",
+		    getmajor(dev), getminor(dev), fsp->pcfs_ldrive,
+		    (uint_t)pc_dbdaddr(fsp, fsp->pcfs_dosstart));
+		error = EINVAL;
+		goto out;
+	}
+
+	ASSERT(fsp->pcfs_fattype != FAT_UNKNOWN);
+
+out:
+	/*
+	 * Release the buffer used
+	 */
+	if (bp != NULL)
+		brelse(bp);
+	return (error);
+}
+
+
+/*
+ * Get the file allocation table.
+ * If there is an old FAT, invalidate it.
+ */
+int
+pc_getfat(struct pcfs *fsp)
+{
+	struct buf *bp = NULL;
+	uchar_t *fatp = NULL;
+	uchar_t *fat_changemap = NULL;
+	int error;
+	int fat_changemapsize;
+	int flags = 0;
+	int nfat;
+	int altfat_mustmatch = 0;
+	int fatsize = fsp->pcfs_fatsec * fsp->pcfs_secsize;
+
+	if (fsp->pcfs_fatp) {
+		/*
+		 * There is a FAT in core.
+		 * If there are open file pcnodes or we have modified it or
+		 * it hasn't timed out yet use the in core FAT.
+		 * Otherwise invalidate it and get a new one
+		 */
+#ifdef notdef
+		if (fsp->pcfs_frefs ||
+		    (fsp->pcfs_flags & PCFS_FATMOD) ||
+		    (gethrestime_sec() < fsp->pcfs_fattime)) {
+			return (0);
+		} else {
+			mutex_enter(&pcfslock);
+			pc_invalfat(fsp);
+			mutex_exit(&pcfslock);
+		}
+#endif /* notdef */
+		return (0);
+	}
+
+	/*
+	 * Get FAT and check it for validity
+	 */
+	fatp = kmem_alloc(fatsize, KM_SLEEP);
+	error = pc_readfat(fsp, fatp);
+	if (error) {
+		flags = B_ERROR;
+		goto out;
+	}
+	fat_changemapsize = (fatsize / fsp->pcfs_clsize) + 1;
+	fat_changemap = kmem_zalloc(fat_changemapsize, KM_SLEEP);
+	fsp->pcfs_fatp = fatp;
+	fsp->pcfs_fat_changemapsize = fat_changemapsize;
+	fsp->pcfs_fat_changemap = fat_changemap;
+
+	/*
+	 * The only definite signature check is that the
+	 * media descriptor byte should match the first byte
+	 * of the FAT block.
+	 */
+	if (fatp[0] != fsp->pcfs_mediadesc) {
+		cmn_err(CE_NOTE, "!pcfs: FAT signature mismatch, "
+		    "media descriptor %x, FAT[0] lowbyte %x\n",
+		    (uint32_t)fsp->pcfs_mediadesc, (uint32_t)fatp[0]);
+		cmn_err(CE_NOTE, "!pcfs: Enforcing alternate FAT validation\n");
+		altfat_mustmatch = 1;
+	}
+
+	/*
+	 * Get alternate FATs and check for consistency
+	 * This is an inlined version of pc_readfat().
+	 * Since we're only comparing FAT and alternate FAT,
+	 * there's no reason to let pc_readfat() copy data out
+	 * of the buf. Instead, compare in-situ, one cluster
+	 * at a time.
+	 */
+	for (nfat = 1; nfat < fsp->pcfs_numfat; nfat++) {
+		size_t startsec;
+		size_t off;
+
+		startsec = pc_dbdaddr(fsp,
+		    fsp->pcfs_fatstart + nfat * fsp->pcfs_fatsec);
+
+		for (off = 0; off < fatsize; off += fsp->pcfs_clsize) {
+			daddr_t fatblk = startsec + pc_dbdaddr(fsp,
+			    pc_cltodb(fsp, pc_lblkno(fsp, off)));
+
+			bp = bread(fsp->pcfs_xdev, fatblk,
+			    MIN(fsp->pcfs_clsize, fatsize - off));
+			if (bp->b_flags & (B_ERROR | B_STALE)) {
+				cmn_err(CE_NOTE,
+				    "!pcfs: alternate FAT #%d (start LBA %p)"
+				    " read error at offset %ld on device"
+				    " (%x.%x):%d",
+				    nfat, (void *)(uintptr_t)startsec, off,
+				    getmajor(fsp->pcfs_xdev),
+				    getminor(fsp->pcfs_xdev),
+				    fsp->pcfs_ldrive);
+				flags = B_ERROR;
+				error = EIO;
+				goto out;
+			}
+			bp->b_flags |= B_STALE | B_AGE;
+			if (bcmp(bp->b_un.b_addr, fatp + off,
+			    MIN(fsp->pcfs_clsize, fatsize - off))) {
+				cmn_err(CE_NOTE,
+				    "!pcfs: alternate FAT #%d (start LBA %p)"
+				    " corrupted at offset %ld on device"
+				    " (%x.%x):%d",
+				    nfat, (void *)(uintptr_t)startsec, off,
+				    getmajor(fsp->pcfs_xdev),
+				    getminor(fsp->pcfs_xdev),
+				    fsp->pcfs_ldrive);
+				if (altfat_mustmatch) {
+					flags = B_ERROR;
+					error = EIO;
+					goto out;
+				}
+			}
+			brelse(bp);
+			bp = NULL;	/* prevent double release */
+		}
+	}
+
+	fsp->pcfs_fattime = gethrestime_sec() + PCFS_DISKTIMEOUT;
+	fsp->pcfs_fatjustread = 1;
+
+	/*
+	 * Retrieve FAT32 fsinfo sector.
+	 * A failure to read this is not fatal to accessing the volume.
+	 * It simply means operations that count or search free blocks
+	 * will have to do a full FAT walk, vs. a possibly quicker lookup
+	 * of the summary information.
+	 * Hence, we log a message but return success overall after this point.
+	 */
+	if (IS_FAT32(fsp) && (fsp->pcfs_flags & PCFS_FSINFO_OK)) {
+		struct fat_od_fsi *fsinfo_disk;
+
+		bp = bread(fsp->pcfs_xdev,
+		    pc_dbdaddr(fsp, fsp->pcfs_fsistart), fsp->pcfs_secsize);
+		fsinfo_disk = (struct fat_od_fsi *)bp->b_un.b_addr;
+		if (bp->b_flags & (B_ERROR | B_STALE) ||
+		    !FSISIG_OK(fsinfo_disk)) {
+			cmn_err(CE_NOTE,
+			    "!pcfs: error reading fat32 fsinfo from "
+			    "device (%x.%x):%d, block %lld",
+			    getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev),
+			    fsp->pcfs_ldrive,
+			    (long long)pc_dbdaddr(fsp, fsp->pcfs_fsistart));
+			fsp->pcfs_flags &= ~PCFS_FSINFO_OK;
+			fsp->pcfs_fsinfo.fs_free_clusters = FSINFO_UNKNOWN;
+			fsp->pcfs_fsinfo.fs_next_free = FSINFO_UNKNOWN;
+		} else {
+			bp->b_flags |= B_STALE | B_AGE;
+			fsinfo_disk = (fat_od_fsi_t *)(bp->b_un.b_addr);
+			fsp->pcfs_fsinfo.fs_free_clusters =
+			    LE_32(fsinfo_disk->fsi_incore.fs_free_clusters);
+			fsp->pcfs_fsinfo.fs_next_free =
+			    LE_32(fsinfo_disk->fsi_incore.fs_next_free);
+		}
+		brelse(bp);
+		bp = NULL;
+	}
+
+	if (pc_validcl(fsp, (pc_cluster32_t)fsp->pcfs_fsinfo.fs_next_free))
+		fsp->pcfs_nxfrecls = fsp->pcfs_fsinfo.fs_next_free;
+	else
+		fsp->pcfs_nxfrecls = PCF_FIRSTCLUSTER;
+
+	return (0);
+
+out:
+	cmn_err(CE_NOTE, "!pcfs: illegal disk format");
+	if (bp)
+		brelse(bp);
+	if (fatp)
+		kmem_free(fatp, fatsize);
+	if (fat_changemap)
+		kmem_free(fat_changemap, fat_changemapsize);
+
+	if (flags) {
+		pc_mark_irrecov(fsp);
+	}
+	return (error);
+}
--- a/usr/src/uts/common/fs/pcfs/pc_vnops.c	Sun Sep 23 11:47:40 2007 -0700
+++ b/usr/src/uts/common/fs/pcfs/pc_vnops.c	Mon Sep 24 06:23:24 2007 -0700
@@ -221,8 +221,7 @@
 	}
 	error = rwpcp(pcp, uiop, UIO_READ, ioflag);
 	if ((fsp->pcfs_vfs->vfs_flag & VFS_RDONLY) == 0) {
-		pcp->pc_flags |= PC_ACC;
-		pc_mark_acc(pcp);
+		pc_mark_acc(fsp, pcp);
 	}
 	pc_unlockfs(fsp);
 	if (error) {
@@ -262,7 +261,7 @@
 	}
 	error = rwpcp(pcp, uiop, UIO_WRITE, ioflag);
 	pcp->pc_flags |= PC_MOD;
-	pc_mark_mod(pcp);
+	pc_mark_mod(fsp, pcp);
 	if (ioflag & (FSYNC|FDSYNC))
 		(void) pc_nodeupdate(pcp);
 
@@ -492,9 +491,10 @@
 		 * Unlock the pages which have been allocated by
 		 * page_create_va() in segmap_pagecreate().
 		 */
-		if (newpage)
+		if (newpage) {
 			segmap_pageunlock(segkmap, base, (size_t)n,
 			    rw == UIO_WRITE ? S_WRITE : S_READ);
+		}
 
 		if (error) {
 			PC_DPRINTF1(1, "rwpcp error2=%d\n", error);
@@ -593,9 +593,26 @@
 	vap->va_fsid = vp->v_vfsp->vfs_dev;
 	vap->va_nodeid = (ino64_t)pc_makenodeid(pcp->pc_eblkno,
 	    pcp->pc_eoffset, pcp->pc_entry.pcd_attr,
-	    pc_getstartcluster(fsp, &pcp->pc_entry), fsp->pcfs_entps);
+	    pc_getstartcluster(fsp, &pcp->pc_entry), pc_direntpersec(fsp));
 	vap->va_nlink = 1;
 	vap->va_size = (u_offset_t)pcp->pc_size;
+	vap->va_rdev = 0;
+	vap->va_nblocks =
+	    (fsblkcnt64_t)howmany((offset_t)pcp->pc_size, DEV_BSIZE);
+	vap->va_blksize = fsp->pcfs_clsize;
+
+	/*
+	 * FAT root directories have no timestamps. In order not to return
+	 * "time zero" (1/1/1970), we record the time of the mount and give
+	 * that. This breaks less expectations.
+	 */
+	if (vp->v_flag & VROOT) {
+		vap->va_mtime = fsp->pcfs_mounttime;
+		vap->va_atime = fsp->pcfs_mounttime;
+		vap->va_ctime = fsp->pcfs_mounttime;
+		pc_unlockfs(fsp);
+		return (0);
+	}
 
 	pc_pcttotv(&pcp->pc_entry.pcd_mtime, &unixtime);
 	if ((fsp->pcfs_flags & PCFS_NOCLAMPTIME) == 0) {
@@ -643,10 +660,6 @@
 	vap->va_atime.tv_sec = (time_t)unixtime;
 	vap->va_atime.tv_nsec = 0;
 
-	vap->va_rdev = 0;
-	vap->va_nblocks = (fsblkcnt64_t)howmany((offset_t)pcp->pc_size,
-	    DEV_BSIZE);
-	vap->va_blksize = fsp->pcfs_clsize;
 	pc_unlockfs(fsp);
 	return (0);
 }
@@ -1411,8 +1424,7 @@
 	 */
 	if ((pcp->pc_flags & PC_ACC) == 0 &&
 	    ((fsp->pcfs_vfs->vfs_flag & VFS_RDONLY) == 0)) {
-		pcp->pc_flags |= PC_ACC;
-		pc_mark_acc(pcp);
+		pc_mark_acc(fsp, pcp);
 	}
 reread:
 	if ((pagefound = page_exists(vp, off)) == NULL) {
@@ -1459,10 +1471,7 @@
 			bp = pageio_setup(pp, xfersize, devvp, B_READ);
 			bp->b_edev = devvp->v_rdev;
 			bp->b_dev = cmpdev(devvp->v_rdev);
-			bp->b_blkno = bn +
-			    /* add a sector offset within the cluster */
-			    /* when the clustersize > PAGESIZE */
-			    (xferoffset - lbnoff) / fsp->pcfs_secsize;
+			bp->b_blkno = bn + btodt(xferoffset - lbnoff);
 			bp->b_un.b_addr = (caddr_t)(uintptr_t)pgoff;
 			bp->b_file = vp;
 			bp->b_offset = (offset_t)(off + pgoff);
@@ -1722,7 +1731,7 @@
 	 */
 	if ((pcp->pc_flags & PC_MOD) == 0 || (flags & B_FORCE)) {
 		pcp->pc_flags |= PC_MOD;
-		pc_mark_mod(pcp);
+		pc_mark_mod(fsp, pcp);
 	}
 	pp = pvn_write_kluster(vp, pp, &io_off, &io_len, pp->p_offset,
 	    PAGESIZE, flags);
@@ -1757,10 +1766,7 @@
 		bp = pageio_setup(pp, xfersize, devvp, B_WRITE | flags);
 		bp->b_edev = devvp->v_rdev;
 		bp->b_dev = cmpdev(devvp->v_rdev);
-		bp->b_blkno = bn +
-		    /* add a sector offset within the cluster */
-		    /* when the clustersize > PAGESIZE */
-		    (xferoffset - lbnoff) / fsp->pcfs_secsize;
+		bp->b_blkno = bn + btodt(xferoffset - lbnoff);
 		bp->b_un.b_addr = (caddr_t)(uintptr_t)pgoff;
 		bp->b_file = vp;
 		bp->b_offset = (offset_t)(io_off + pgoff);
@@ -2162,13 +2168,13 @@
 	char	*lfn_base;
 	int	boff;
 	int	i, cs;
-	char 	*buf;
+	char	*buf;
 	uchar_t	cksum;
-	int 	detached = 0;
+	int	detached = 0;
 	int	error = 0;
 	int	foldcase;
 	int	count = 0;
-	size_t u16l = 0, u8l = 0;
+	size_t	u16l = 0, u8l = 0;
 
 	foldcase = (fsp->pcfs_flags & PCFS_FOLDCASE);
 	lfn_base = kmem_alloc(PCMAXNAM_UTF16, KM_SLEEP);
@@ -2311,7 +2317,7 @@
 	ld->d_off = uiop->uio_loffset + sizeof (struct pcdir);
 	ld->d_ino = pc_makenodeid(pc_daddrdb(fsp, (*bp)->b_blkno),
 	    pc_blkoff(fsp, *offset), ep->pcd_attr,
-	    pc_getstartcluster(fsp, ep), fsp->pcfs_entps);
+	    pc_getstartcluster(fsp, ep), pc_direntpersec(fsp));
 	(void) uiomove((caddr_t)ld, ld->d_reclen, UIO_READ, uiop);
 	uiop->uio_loffset = ld->d_off;
 	*offset += sizeof (struct pcdir);
@@ -2342,7 +2348,8 @@
 		return (0);
 	}
 	ld->d_ino = (ino64_t)pc_makenodeid(pc_daddrdb(fsp, (*bp)->b_blkno),
-	    boff, ep->pcd_attr, pc_getstartcluster(fsp, ep), fsp->pcfs_entps);
+	    boff, ep->pcd_attr, pc_getstartcluster(fsp, ep),
+	    pc_direntpersec(fsp));
 	foldcase = (fsp->pcfs_flags & PCFS_FOLDCASE);
 	error = pc_fname_ext_to_name(&ld->d_name[0], &ep->pcd_filename[0],
 	    &ep->pcd_ext[0], foldcase);
--- a/usr/src/uts/common/sys/fs/pc_dir.h	Sun Sep 23 11:47:40 2007 -0700
+++ b/usr/src/uts/common/sys/fs/pc_dir.h	Mon Sep 24 06:23:24 2007 -0700
@@ -172,10 +172,24 @@
 	uchar_t pcdl_thirdfilename[PCLF_THIRDNAMESIZE];
 };
 
+/*
+ * FAT LFN entries are consecutively numbered downwards, and the last
+ * entry of a LFN chain will have the 0x40 'termination' marker logically
+ * or'ed in. The entry immediately preceeding the short name has number 1,
+ * consecutively increasing. Since the filename length limit on FAT is
+ * 255 unicode characters and every LFN entry contributes 13 characters,
+ * the maximum sequence number is 255/13 + 1 == 20.
+ */
 #define	PCDL_IS_LAST_LFN(x) ((x->pcdl_ordinal) & 0x40)
 #define	PCDL_LFN_BITS (PCA_RDONLY | PCA_HIDDEN | PCA_SYSTEM | PCA_LABEL)
-#define	PCDL_IS_LFN(x) (enable_long_filenames && \
-			    (((x)->pcd_attr & PCDL_LFN_BITS) == PCDL_LFN_BITS))
+#define	PCDL_LFN_MASK (PCDL_LFN_BITS | PCA_DIR | PCA_ARCH)
+#define	PCDL_LFN_VALID_ORD(x)						\
+	(((((struct pcdir_lfn *)(x))->pcdl_ordinal & ~0x40) > 0) &&	\
+	((((struct pcdir_lfn *)(x))->pcdl_ordinal & ~0x40) <= 20))
+#define	PCDL_IS_LFN(x)							\
+	(enable_long_filenames &&					\
+	(((x)->pcd_attr & PCDL_LFN_MASK) == PCDL_LFN_BITS) &&		\
+	PCDL_LFN_VALID_ORD((x)))
 
 /*
  * The first char of the file name has special meaning as follows:
--- a/usr/src/uts/common/sys/fs/pc_fs.h	Sun Sep 23 11:47:40 2007 -0700
+++ b/usr/src/uts/common/sys/fs/pc_fs.h	Mon Sep 24 06:23:24 2007 -0700
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -29,6 +29,9 @@
 #pragma ident	"%Z%%M%	%I%	%E% SMI"
 
 #include <sys/thread.h>
+#include <sys/ksynch.h>
+#include <sys/sysmacros.h>
+#include <sys/byteorder.h>
 
 #ifdef	__cplusplus
 extern "C" {
@@ -95,8 +98,17 @@
  * bread blocks forever. So now we read the FAT in chunks.
  */
 
+
 /*
- * pre-FAT32 boot sector.
+ * The FAT bootsector uses little-endian multibyte values not aligned at
+ * a 'native' wordsize. Instead of defining a strange data structure and
+ * odd accessor methods for some members while using standard C accesses
+ * for others, we don't bother and just define the structure offsets, and
+ * a common set of misaligned-littleendian accessor macros.
+ *
+ * The "bootsec" and "fat32_bootsec" structures are only provided for
+ * compatibility with old code including <sys/fs/pc_fs.h> but not used
+ * by the PCFS kernel driver anymore.
  */
 struct bootsec {
 	uchar_t	instr[3];
@@ -131,27 +143,255 @@
 	uint16_t	f_reserved2[6];
 };
 
-#define	FAT32_FS_SIGN	0x61417272
-#define	FAT32_BOOT_FSINFO_OFF	0x1e0
+
+#define	OFF_JMPBOOT	0
+#define	OFF_OEMNAME	3
+#define	OFF_BYTESPERSEC	11
+#define	OFF_SECPERCLUS	13
+#define	OFF_RSVDSECCNT	14
+#define	OFF_NUMFATS	16
+#define	OFF_ROOTENTCNT	17
+#define	OFF_TOTSEC16	19
+#define	OFF_MEDIA	21
+#define	OFF_FATSZ16	22
+#define	OFF_SECPERTRK	24
+#define	OFF_NUMHEADS	26
+#define	OFF_HIDDSEC	28
+#define	OFF_TOTSEC32	32
+#define	OFF_BPBSIG	510
+
+#define	OFF_DRVNUM16	36
+#define	OFF_BOOTSIG16	38
+#define	OFF_VOLID16	39
+#define	OFF_VOLLAB16	43
+#define	OFF_FILSYSTYP16	54
+
+#define	OFF_FATSZ32	36
+#define	OFF_EXTFLAGS32	40
+#define	OFF_FSVER32	42
+#define	OFF_ROOTCLUS32	44
+#define	OFF_FSINFO32	48
+#define	OFF_BKBOOTSEC32	50
+#define	OFF_DRVNUM32	64
+#define	OFF_BOOTSIG32	66
+#define	OFF_VOLID32	67
+#define	OFF_VOLLAB32	71
+#define	OFF_FILSYSTYP32	82
+
+#define	LE_16_NA(addr)					\
+	(((uint16_t)*((uint8_t *)(addr))) +		\
+	((uint16_t)*((uint8_t *)(addr) + 1) << 8))
+
+#define	LE_32_NA(addr)					\
+	(((uint32_t)*((uint8_t *)(addr))) +		\
+	((uint32_t)*((uint8_t *)(addr) + 1) << 8) +	\
+	((uint32_t)*((uint8_t *)(addr) + 2) << 16) +	\
+	((uint32_t)*((uint8_t *)(addr) + 3) << 24))
+
+/*
+ * Generic FAT BPB fields
+ */
+#define	bpb_jmpBoot(bpb)		((unsigned char *)(bpb))
+#define	bpb_OEMName(bpb)		((char *)(bpb) + OFF_OEMNAME)
+#define	bpb_get_BytesPerSec(bpb)	LE_16_NA((bpb) + OFF_BYTESPERSEC)
+#define	bpb_get_SecPerClus(bpb)		(((uint8_t *)(bpb))[OFF_SECPERCLUS])
+#define	bpb_get_RsvdSecCnt(bpb)		LE_16_NA((bpb) + OFF_RSVDSECCNT)
+#define	bpb_get_NumFATs(bpb)		(((uint8_t *)(bpb))[OFF_NUMFATS])
+#define	bpb_get_RootEntCnt(bpb)		LE_16_NA((bpb) + OFF_ROOTENTCNT)
+#define	bpb_get_TotSec16(bpb)		LE_16_NA((bpb) + OFF_TOTSEC16)
+#define	bpb_get_Media(bpb)		(((uint8_t *)(bpb))[OFF_MEDIA])
+#define	bpb_get_FatSz16(bpb)		LE_16_NA((bpb) + OFF_FATSZ16)
+#define	bpb_get_SecPerTrk(bpb)		LE_16_NA((bpb) + OFF_SECPERTRK)
+#define	bpb_get_NumHeads(bpb)		LE_16_NA((bpb) + OFF_NUMHEADS)
+#define	bpb_get_HiddSec(bpb)		LE_32_NA((bpb) + OFF_HIDDSEC)
+#define	bpb_get_TotSec32(bpb)		LE_32_NA((bpb) + OFF_TOTSEC32)
+#define	bpb_get_BPBSig(bpb)		LE_16_NA((bpb) + OFF_BPBSIG)
+
+/*
+ * FAT12/16 extended BPB fields
+ */
+#define	bpb_get_DrvNum16(bpb)		(((uint8_t *)(bpb))[OFF_DRVNUM16])
+#define	bpb_get_BootSig16(bpb)		(((uint8_t *)(bpb))[OFF_BOOTSIG16])
+#define	bpb_VolLab16(bpb)		((char *)(bpb) + OFF_VOLLAB16)
+#define	bpb_FilSysType16(bpb)		((char *)(bpb) + OFF_FILSYSTYP16)
+#define	bpb_get_VolID16(bpb)		LE_32_NA((bpb) + OFF_VOLID16)
+
+/*
+ * FAT32 extended BPB fields
+ */
+#define	bpb_get_FatSz32(bpb)		LE_32_NA((bpb) + OFF_FATSZ32)
+#define	bpb_get_ExtFlags32(bpb)		LE_16_NA((bpb) + OFF_EXTFLAGS32)
+#define	bpb_get_FSVer32(bpb)		LE_16_NA((bpb) + OFF_FSVER32)
+#define	bpb_get_RootClus32(bpb)		LE_32_NA((bpb) + OFF_ROOTCLUS32)
+#define	bpb_get_FSInfo32(bpb)		LE_16_NA((bpb) + OFF_FSINFO32)
+#define	bpb_get_BkBootSec32(bpb)	LE_16_NA((bpb) + OFF_BKBOOTSEC32)
+#define	bpb_get_DrvNum32(bpb)		(((uint8_t *)(bpb))[OFF_DRVNUM32])
+#define	bpb_get_BootSig32(bpb)		(((uint8_t *)(bpb))[OFF_BOOTSIG32])
+#define	bpb_get_VolID32(bpb)		LE_32_NA((bpb) + OFF_VOLID32)
+#define	bpb_VolLab32(bpb)		((char *)(bpb) + OFF_VOLLAB32)
+#define	bpb_FilSysType32(bpb)		((char *)(bpb) + OFF_FILSYSTYP32)
+
+/*
+ * Validators
+ */
+#define	VALID_SECSIZE(s)	\
+	(s == 512 || s == 1024 || s == 2048 || s == 4096)
+#define	VALID_SPCL(s)		(ISP2((s)) && (unsigned int)(s) <= 128)
+#define	VALID_CLSIZE(s)		(ISP2((s)) && (unsigned int)(s) <= (64 * 1024))
+#define	VALID_NUMFATS(n)	((n) > 0 && (n) < 8)
+#define	VALID_RSVDSEC(s)	((s) > 0)
+#define	VALID_BPBSIG(sig)	((sig) == MBB_MAGIC)
+#define	VALID_BOOTSIG(sig)	((sig) == 0x29)
+#define	VALID_MEDIA(m)		((m) == 0xF0 || ((m) >= 0xF8 && (m) <= 0xFF))
+
+/*
+ * this might require a change for codepage support. In particular,
+ * pc_validchar() cannot be a macro anymore if codepages get involved.
+ */
+#define	VALID_VOLLAB(l)		(			\
+	pc_validchar((l)[0]) && pc_validchar((l)[1]) && \
+	pc_validchar((l)[2]) &&	pc_validchar((l)[3]) && \
+	pc_validchar((l)[4]) && pc_validchar((l)[5]) && \
+	pc_validchar((l)[6]) && pc_validchar((l)[7]) && \
+	pc_validchar((l)[8]) && pc_validchar((l)[9]) && \
+	pc_validchar((l)[10]))
 
 /*
- * summary information for fat32 volumes. We need to maintain fs_free_clusters
- * or Microsoft Scandisk will be upset.
+ * We might actually use the 'validchar' checks as well; it only needs
+ * to be printable. Should this ever caused failed media recognition,
+ * we can change it. Many ISVs put different strings into the "oemname"
+ * field.
+ */
+#define	VALID_OEMNAME(nm)	(			\
+	bcmp((nm), "MSDOS", 5) == 0 || bcmp((nm), "MSWIN", 5) == 0)
+#define	VALID_FSTYPSTR16(typ)	(bcmp((typ), "FAT", 3) == 0)
+#define	VALID_FSTYPSTR32(typ)	(bcmp((typ), "FAT32", 5) == 0)
+#define	VALID_JMPBOOT(b)	(			\
+	((b)[0] == 0xeb && (b)[2] == 0x90) || (b)[0] == 0xe9)
+#define	VALID_FSVER32(v)	((v) == PCFS_SUPPORTED_FSVER)
+/*
+ * Can we check this properly somehow ? There should be a better way.
+ * The FAT spec doesn't mention reserved bits need to be zero ...
+ */
+#define	VALID_EXTFLAGS(flags)	(((flags) & 0x8f) == (flags))
+
+/*
+ * Validation results
  */
-struct fat32_boot_fsinfo {
-	uint32_t	fs_reserved1;
-	uint32_t	fs_signature;	/* 0x61417272 */
-	uint32_t	fs_free_clusters;  /* # free clusters. -1 if unknown */
-	uint32_t	fs_next_cluster;   /* unused by pcfs */
-	uint32_t	fs_reserved2[4];
-};
+#define	BPB_SECSIZE_OK		(1 << 0)	/* ok: 512/1024/2048/4096 */
+#define	BPB_OEMNAME_OK		(1 << 1)	/* "MSDOS" or "MSWIN" */
+#define	BPB_JMPBOOT_OK		(1 << 2)	/* 16bit "jmp" / "call" */
+#define	BPB_SECPERCLUS_OK	(1 << 3)	/* power of 2, [1 .. 128] */
+#define	BPB_RSVDSECCNT_OK	(1 << 4)	/* cannot be zero */
+#define	BPB_NUMFAT_OK		(1 << 5)	/* >= 1, <= 8 */
+#define	BPB_ROOTENTCNT_OK	(1 << 6)	/* 0 on FAT32, != 0 else */
+#define	BPB_TOTSEC_OK		(1 << 7)	/* smaller than volume */
+#define	BPB_TOTSEC16_OK		(1 << 8)	/* 0 on FAT32, != 0 on FAT12 */
+#define	BPB_TOTSEC32_OK		(1 << 9)	/* 0 on FAT12, != 0 on FAT32 */
+#define	BPB_MEDIADESC_OK	(1 << 10)	/* 0xf0 or 0xf8..0xff */
+#define	BPB_FATSZ_OK		(1 << 11)	/* [nclusters], no smaller */
+#define	BPB_FATSZ16_OK		(1 << 12)	/* 0 on FAT32, != 0 else */
+#define	BPB_FATSZ32_OK		(1 << 13)	/* non-zero on FAT32 */
+#define	BPB_BPBSIG_OK		(1 << 14)	/* 0x55, 0xAA */
+#define	BPB_BOOTSIG16_OK	(1 << 15)	/* 0x29 - if present */
+#define	BPB_BOOTSIG32_OK	(1 << 16)	/* 0x29 - unless SYSLINUX2.x */
+#define	BPB_FSTYPSTR16_OK	(1 << 17)	/* At least "FAT" */
+#define	BPB_FSTYPSTR32_OK	(1 << 18)	/* "FAT32" */
+#define	BPB_EXTFLAGS_OK		(1 << 19)	/* reserved bits should be 0 */
+#define	BPB_FSVER_OK		(1 << 20)	/* must be 0 */
+#define	BPB_ROOTCLUSTER_OK	(1 << 21)	/* must be != 0 and valid */
+#define	BPB_FSISEC_OK		(1 << 22)	/* != 0, <= reserved */
+#define	BPB_BKBOOTSEC_OK	(1 << 23)	/* != 0, <= reserved, != fsi */
+#define	BPB_VOLLAB16_OK		(1 << 24)	/* passes pc_validchar() */
+#define	BPB_VOLLAB32_OK		(1 << 25)	/* passes pc_validchar() */
+#define	BPB_NCLUSTERS_OK	(1 << 26)	/* from FAT spec */
+#define	BPB_CLSIZE_OK		(1 << 27)	/* cluster size */
+#define	BPB_MEDIASZ_OK		(1 << 28)	/* filesystem fits on device */
+
+#define	FAT12_VALIDMSK							\
+	(BPB_SECSIZE_OK | BPB_SECPERCLUS_OK | BPB_CLSIZE_OK |		\
+	BPB_RSVDSECCNT_OK | BPB_NUMFAT_OK | BPB_ROOTENTCNT_OK |		\
+	BPB_TOTSEC_OK | BPB_TOTSEC16_OK |				\
+	BPB_FATSZ_OK | BPB_FATSZ16_OK |	BPB_BPBSIG_OK)
+
+#define	FAT16_VALIDMSK							\
+	(BPB_SECSIZE_OK | BPB_SECPERCLUS_OK | BPB_CLSIZE_OK |		\
+	BPB_RSVDSECCNT_OK | BPB_NUMFAT_OK | BPB_ROOTENTCNT_OK |		\
+	BPB_TOTSEC_OK | BPB_TOTSEC16_OK | BPB_TOTSEC32_OK | 		\
+	BPB_FATSZ_OK | BPB_FATSZ16_OK | BPB_BPBSIG_OK)
 
-#define	FSINFO_UNKNOWN	(-1)
+/*
+ * A note on FAT32: According to the FAT spec, FAT32 _must_ have a valid
+ * extended BPB and therefore, as a proof of its existance, the FAT32
+ * boot signature (offset 66) must be valid as well. Why don't we check
+ * for BPB_BOOTSIG32_OK  then ?
+ *
+ * We don't test for this here first-pass, because there are media out
+ * there that are valid FAT32 structurally but don't have a valid sig.
+ * This happens if older versions of the SYSLINUX bootloader (below 3.x)
+ * are installed on a media with a FAT32 on it. SYSLINUX 2.x and lower
+ * overwrite the BPB past the end of the FAT12/16 extension with its
+ * bootloader code - and the FAT16 extended BPB is 62 Bytes...
+ * All structurally relevant fields of the FAT32 BPB are within the first
+ * 52 Bytes, so the filesystem is accessible - but the signature check
+ * would reject it.
+ */
+#define	FAT32_VALIDMSK							\
+	(BPB_SECSIZE_OK | BPB_SECPERCLUS_OK | BPB_CLSIZE_OK |		\
+	BPB_RSVDSECCNT_OK | BPB_NUMFAT_OK | BPB_ROOTENTCNT_OK |		\
+	BPB_TOTSEC_OK | BPB_TOTSEC16_OK | BPB_TOTSEC32_OK | 		\
+	BPB_FATSZ_OK | BPB_FATSZ16_OK |	BPB_FATSZ32_OK |		\
+	BPB_EXTFLAGS_OK | BPB_FSVER_OK | BPB_ROOTCLUSTER_OK |		\
+	BPB_BPBSIG_OK)
+
+/*
+ * FAT32 BPB allows 'versioning' via FSVer32. We follow the 'NULL' spec.
+ */
+#define	PCFS_SUPPORTED_FSVER	0
+
+
+/*
+ * Filesystem summary information (introduced originally for FAT32 volumes).
+ * We need to maintain fs_free_clusters or Microsoft Scandisk will be upset.
+ * We keep these values in-core even for FAT12/FAT16 but will never attempt
+ * to write them out to disk then.
+ */
+typedef struct fat_fsinfo {
+	uint32_t fs_free_clusters;	/* # free clusters. -1 if unknown */
+	uint32_t fs_next_free;		/* search next free after this cn */
+} fat_fsi_t;
+
+/*
+ * On-disk FSI. All values in little endian. Only FAT32 has this.
+ */
+typedef struct fat_od_fsi {
+	uint32_t	fsi_leadsig;		/* 0x41615252 */
+	char		fsi_reserved1[480];
+	uint32_t	fsi_strucsig;		/* 0x61417272 */
+	fat_fsi_t	fsi_incore;		/* free/nextfree */
+	char		fsi_reserved2[12];
+	uint32_t	fsi_trailsig;		/* 0xaa550000 */
+} fat_od_fsi_t;
+
+#define	FSI_LEADSIG	LE_32(0x41615252)
+#define	FSI_STRUCSIG	LE_32(0x61417272)
+#define	FSI_TRAILSIG	LE_32(0xaa550000)	/* same as MBB_MAGIC */
+
+#define	FSISIG_OK(fsi)	(						\
+	((fat_od_fsi_t *)(fsi))->fsi_leadsig == FSI_LEADSIG &&		\
+	((fat_od_fsi_t *)(fsi))->fsi_strucsig == FSI_STRUCSIG &&	\
+	((fat_od_fsi_t *)(fsi))->fsi_trailsig == FSI_TRAILSIG)
+
+#define	FSINFO_UNKNOWN	((uint32_t)(-1))	/* free/next not valid */
+
+typedef enum { FAT12, FAT16, FAT32, FAT_UNKNOWN, FAT_QUESTIONABLE } fattype_t;
+
 
 struct pcfs {
 	struct vfs *pcfs_vfs;		/* vfs for this fs */
 	int pcfs_flags;			/* flags */
-	int pcfs_ldrv;			/* logical DOS drive number */
+	int pcfs_ldrive;		/* logical DOS drive number */
+	fattype_t pcfs_fattype;
 	dev_t pcfs_xdev;		/* actual device that is mounted */
 	struct vnode *pcfs_devvp;	/*   and a vnode for it */
 	int pcfs_secsize;		/* sector size in bytes */
@@ -165,29 +405,34 @@
 	int pcfs_numfat;		/* number of FAT copies */
 	int pcfs_rdirsec;		/* number of sec in root dir */
 	daddr_t pcfs_dosstart;		/* start blkno of DOS partition */
+	daddr_t pcfs_fsistart;		/* start blkno of FSI sector */
 	daddr_t pcfs_fatstart;		/* start blkno of first FAT */
 	daddr_t pcfs_rdirstart;		/* start blkno of root dir */
 	daddr_t pcfs_datastart;		/* start blkno of data area */
 	int pcfs_clsize;		/* cluster size in bytes */
 	int pcfs_ncluster;		/* number of clusters in fs */
-	int pcfs_entps;			/* number of dir entry per sector */
 	int pcfs_nrefs;			/* number of active pcnodes */
 	int pcfs_frefs;			/* number of active file pcnodes */
 	int pcfs_nxfrecls;		/* next free cluster */
 	uchar_t *pcfs_fatp;		/* ptr to FAT data */
 	uchar_t *pcfs_fat_changemap;	/* map of changed fat data */
-	int pcfs_fatsize;		/* size of FAT data */
 	int pcfs_fat_changemapsize;	/* size of FAT changemap */
 	time_t pcfs_fattime;		/* time FAT becomes invalid */
 	time_t pcfs_verifytime;		/* time to reverify disk */
 	kmutex_t	pcfs_lock;		/* per filesystem lock */
 	kthread_id_t pcfs_owner;		/* id of thread locking pcfs */
 	int pcfs_count;			/* # of pcfs locks for pcfs_owner */
-	struct fat32_boot_fsinfo fsinfo_native; /* native fsinfo for fat32 */
-	uint32_t	f32fsinfo_sector; /* where to read/write fsinfo */
+	struct fat_fsinfo pcfs_fsinfo;	/* in-core fsinfo */
 	struct pcfs *pcfs_nxt;		/* linked list of all mounts */
 	int pcfs_fatjustread;		/* Used to flag a freshly found FAT */
 	struct vnode *pcfs_root;	/* vnode for the root dir of the fs */
+	int pcfs_secondswest;		/* recording timezone for this fs */
+	len_t pcfs_mediasize;
+	int pcfs_rootblksize;
+	int pcfs_mediadesc;		/* media descriptor */
+	pc_cluster32_t pcfs_lastclmark;
+	pc_cluster32_t pcfs_rootclnum;
+	timestruc_t pcfs_mounttime;	/* timestamp for "/" */
 };
 
 /*
@@ -196,15 +441,19 @@
 #define	PCFS_FATMOD		0x01	/* FAT has been modified */
 #define	PCFS_LOCKED		0x02	/* fs is locked */
 #define	PCFS_WANTED		0x04	/* locked fs is wanted */
-#define	PCFS_FAT16		0x400	/* 16 bit FAT */
 #define	PCFS_NOCHK		0x800	/* don't resync fat on error */
 #define	PCFS_BOOTPART		0x1000	/* boot partition type */
 #define	PCFS_HIDDEN		0x2000	/* show hidden files */
 #define	PCFS_PCMCIA_NO_CIS	0x4000	/* PCMCIA psuedo floppy */
 #define	PCFS_FOLDCASE		0x8000	/* fold filenames to lowercase */
-#define	PCFS_FAT32		0x10000	/* 32 bit FAT */
+#define	PCFS_FSINFO_OK		0x10000	/* valid FAT32 fsinfo sector */
 #define	PCFS_IRRECOV		0x20000	/* FS was messed with during write */
 #define	PCFS_NOCLAMPTIME	0x40000	/* expose full FAT timestamp range */
+#define	PCFS_NOATIME		0x80000	/* disable atime updates */
+
+#define	IS_FAT12(PCFS)	((PCFS)->pcfs_fattype == FAT12)
+#define	IS_FAT16(PCFS)	((PCFS)->pcfs_fattype == FAT16)
+#define	IS_FAT32(PCFS)	((PCFS)->pcfs_fattype == FAT32)
 
 /* for compatibility */
 struct old_pcfs_args {
@@ -219,16 +468,6 @@
 };
 
 /*
- * flags for the pcfs_args 'flags' field.
- *
- * Note that these two macros are obsolete - do not use them.
- */
-#define	PCFS_MNT_HIDDEN		0x01	/* show hidden files */
-#define	PCFS_MNT_FOLDCASE	0x02	/* fold all names from media to */
-					/* lowercase */
-#define	PCFS_MNT_NOCLAMPTIME	0x04	/* expose full FAT timestamp range */
-
-/*
  * pcfs mount options.
  */
 #define	MNTOPT_PCFS_HIDDEN	"hidden"
@@ -237,6 +476,8 @@
 #define	MNTOPT_PCFS_NOFOLDCASE	"nofoldcase"
 #define	MNTOPT_PCFS_CLAMPTIME	"clamptime"
 #define	MNTOPT_PCFS_NOCLAMPTIME	"noclamptime"
+#define	MNTOPT_PCFS_TIMEZONE	"timezone"
+#define	MNTOPT_PCFS_SECSIZE	"secsize"
 
 /*
  * Disk timeout value in sec.
@@ -245,6 +486,8 @@
  */
 #define	PCFS_DISKTIMEOUT	2
 
+#define	PCFS_MAXOFFSET_T	UINT32_MAX	/* PCFS max file size */
+
 #define	VFSTOPCFS(VFSP)		((struct pcfs *)((VFSP)->vfs_data))
 #define	PCFSTOVFS(FSP)		((FSP)->pcfs_vfs)
 
@@ -273,19 +516,10 @@
  * file system parameter macros
  */
 
-#define	IS_FAT32(PCFS) \
-	(((PCFS)->pcfs_flags & PCFS_FAT32) == PCFS_FAT32)
-
-#define	IS_FAT16(PCFS) \
-	(((PCFS)->pcfs_flags & PCFS_FAT16) == PCFS_FAT16)
-
-#define	IS_FAT12(PCFS) \
-	(((PCFS)->pcfs_flags & (PCFS_FAT16 | PCFS_FAT32)) == 0)
-
 #define	pc_clear_fatchanges(PCFS) \
 	bzero((PCFS)->pcfs_fat_changemap, (PCFS)->pcfs_fat_changemapsize)
 
-#define	pc_blksize(PCFS, PCP, OFF)	/* file system block size */ \
+#define	pc_blksize(PCFS, PCP, OFF)	/* file system block size */	\
 	(((PCTOV(PCP)->v_flag & VROOT) && !IS_FAT32(PCFS)) ? \
 	    ((OFF) >= \
 	    ((PCFS)->pcfs_rdirsec & \
@@ -307,21 +541,32 @@
 #define	pc_cltodb(PCFS, CL)		/* clusters to disk blks */ \
 	((daddr_t)((CL) * (PCFS)->pcfs_spcl))
 
-#define	pc_cldaddr(PCFS, CL)	/* DEV_BSIZE "sector" addr for cluster */ \
-	(((daddr_t)((PCFS)->pcfs_datastart + \
-	    ((CL) - PCF_FIRSTCLUSTER) * (PCFS)->pcfs_spcl)) << \
-	    (PCFS)->pcfs_sdshift)
-
-#define	pc_daddrcl(PCFS, DADDR)		/* cluster for disk address */ \
-	((int)(((((DADDR) >> (PCFS)->pcfs_sdshift) - (PCFS)->pcfs_datastart) / \
-	(PCFS)->pcfs_spcl) + 2))
-
 #define	pc_dbdaddr(PCFS, DB)	/* sector to DEV_BSIZE "sector" addr */ \
 	((DB) << (PCFS)->pcfs_sdshift)
 
 #define	pc_daddrdb(PCFS, DADDR)	/* DEV_BSIZE "sector" addr to sector addr */ \
 	((DADDR) >> (PCFS)->pcfs_sdshift)
 
+#define	pc_cldaddr(PCFS, CL)	/* DEV_BSIZE "sector" addr for cluster */ \
+	pc_dbdaddr(PCFS, ((daddr_t)((PCFS)->pcfs_datastart +	\
+	pc_cltodb(PCFS, (CL) - PCF_FIRSTCLUSTER))))
+
+#define	pc_daddrcl(PCFS, DADDR)		/* cluster for disk address */	\
+	((int)(PCF_FIRSTCLUSTER +					\
+	pc_dbtocl(pc_daddrdb(PCFS, DADDR) - (PCFS)->pcfs_datastart)))
+
+/*
+ * Number of directory entries per sector / cluster
+ */
+#define	pc_direntpersec(PCFS)						\
+	((int)((PCFS)->pcfs_secsize / sizeof (struct pcdir)))
+
+#define	pc_direntpercl(PCFS)						\
+	((int)((PCFS)->pcfs_clsize  / sizeof (struct pcdir)))
+
+/*
+ * out-of-range check for cluster numbers.
+ */
 #define	pc_validcl(PCFS, CL)		/* check that cluster no is legit */ \
 	((int)(CL) >= PCF_FIRSTCLUSTER && \
 	    (int)(CL) <= (PCFS)->pcfs_ncluster)
--- a/usr/src/uts/common/sys/fs/pc_node.h	Sun Sep 23 11:47:40 2007 -0700
+++ b/usr/src/uts/common/sys/fs/pc_node.h	Mon Sep 24 06:23:24 2007 -0700
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -114,8 +114,8 @@
 extern void pc_init(void);
 extern struct pcnode *pc_getnode(struct pcfs *, daddr_t, int, struct pcdir *);
 extern void pc_rele(struct pcnode *);
-extern void pc_mark_mod(struct pcnode *);
-extern void pc_mark_acc(struct pcnode *);
+extern void pc_mark_mod(struct pcfs *, struct pcnode *);
+extern void pc_mark_acc(struct pcfs *, struct pcnode *);
 extern int pc_nodesync(struct pcnode *);
 extern int pc_nodeupdate(struct pcnode *);
 extern int pc_bmap(struct pcnode *, daddr_t, daddr_t *, uint_t *);