Mercurial > illumos > illumos-gate
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 *);