/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * Copyright (c) 2011 Gary Mills * Copyright 2024 MNX Cloud, Inc. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "getresponse.h" #include "pcfs_bpb.h" #include "pcfs_common.h" /* Drop gettext for debug build, so we can catch format errors. */ #ifdef DEBUG #define gettext(x) x #endif /* * mkfs (for pcfs) * * Install a boot block, FAT, and (if desired) the first resident * of the new fs. * * XXX -- floppy opens need O_NDELAY? */ #define IN_RANGE(n, x, y) (((n) >= (x)) && ((n) <= (y))) #define DEFAULT_LABEL "NONAME" /* * Extended boot signature. This byte indicates that VOLID, VOLLAB and * FILSYSTYPE fields are present. [fatgen103, pages 11 and 12]. */ #define BOOTSIG 0x29 /* Exit codes. */ #define ERR_USAGE 1 #define ERR_OS 2 /* open fail, stat fail etc */ #define ERR_INVALID 3 /* Validation failed */ #define ERR_FAIL 4 /* IO error, no memory etc */ #define ERR_USER 5 /* User input */ #define ERR_INVAL 6 /* Invalid data */ static char *BootBlkFn = "/boot/pmbr"; static char *DiskName = NULL; static char *FirstFn = NULL; static char *Label = DEFAULT_LABEL; static char Firstfileattr = 0x20; static int Outputtofile = 0; static int SunBPBfields = 0; static int GetFsParams = 0; static int Fatentsize = 0; static int Imagesize = 3; static int Notreally = 0; static int Verbose = 0; static int MakeFAT32 = 0; /* * If there is an FDISK entry for the device where we're about to * make the file system, we ought to make a file system that has the * same size FAT as the FDISK table claims. We track the size FDISK * thinks in this variable. */ static int FdiskFATsize = 0; static int GetSize = 1; /* Unless we're given as arg, must look it up */ static ulong_t TotSize; /* Total size of FS in # of sectors */ static int GetSPC = 1; /* Unless we're given as arg, must calculate */ static ulong_t SecPerClust; /* # of sectors per cluster */ static int GetOffset = 1; /* Unless we're given as arg, must look it up */ static ulong_t RelOffset; /* Relative start sector (hidden sectors) */ static int GetSPT = 1; /* Unless we're given as arg, must look it up */ static ushort_t SecPerTrk; /* # of sectors per track */ static int GetTPC = 1; /* Unless we're given as arg, must look it up */ static ushort_t TrkPerCyl; /* # of tracks per cylinder */ static int GetResrvd = 1; /* Unless we're given as arg, must calculate */ static int Resrvd; /* Number of reserved sectors */ static int GetBPF = 1; /* Unless we're given as arg, must calculate */ static int BitsPerFAT; /* Total size of FS in # of sectors */ static ulong_t TotalClusters; /* Computed total number of clusters */ /* * Unless we are told otherwise, we should use fdisk table for non-diskettes. */ static int DontUseFdisk = 0; /* * Function prototypes */ #ifdef _BIG_ENDIAN static void swap_pack_grabsebpb(bpb_t *wbpb, struct _boot_sector *bsp); static void swap_pack_bpb32cpy(struct _boot_sector32 *bsp, bpb_t *wbpb); static void swap_pack_sebpbcpy(struct _boot_sector *bsp, bpb_t *wbpb); static void swap_pack_bpbcpy(struct _boot_sector *bsp, bpb_t *wbpb); #endif 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 fat_od_fsi *fsinfop, ulong_t *fatsize, char *ffn, int *fffd, ulong_t *ffsize, pc_cluster32_t *ffstartclust); static void compare_existing_with_computed(int fd, char *suffix, bpb_t *wbpb, int *prtsize, int *prtspc, int *prtbpf, int *prtnsect, int *prtntrk, int *prtfdisk, int *prthidden, int *prtrsrvd, int *dashos); static void print_reproducing_command(int fd, char *actualdisk, char *suffix, bpb_t *wbpb); static void compute_file_area_size(bpb_t *wbpb); static void write_fat32_bootstuff(int fd, boot_sector_t *bsp, bpb_t *wbpb, 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); static void dirent_fname_fill(struct pcdir *dep, char *fn); static void floppy_bpb_fillin(bpb_t *wbpb, int diam, int hds, int spt); static void read_existing_bpb(int fd, bpb_t *wbpb); static void warn_funky_fatsize(void); static void warn_funky_floppy(void); static void dirent_time_fill(struct pcdir *dep); static void parse_suboptions(char *optsstr); static void write_bootsects(int fd, boot_sector_t *bsp, bpb_t *wbpb, 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); static void partn_lecture(char *dn); static void lookup_floppy(struct fd_char *fdchar, bpb_t *wbpb); static void label_volume(char *lbl, bpb_t *wbpb); static void mark_cluster(uchar_t *fatp, pc_cluster32_t clustnum, uint32_t value); static void dashm_bail(int fd); static void write_rest(bpb_t *wbpb, char *efn, int dfd, int sfd, int remaining); static void write_fat(int fd, off64_t seekto, char *fn, char *lbl, char *ffn, bpb_t *wbpb); static int prepare_image_file(const char *fn, bpb_t *wbpb); static int verify_bootblkfile(char *fn, boot_sector_t *bs); static int open_and_examine(char *dn, bpb_t *wbpb); static int verify_firstfile(char *fn, ulong_t *filesize); static int lookup_FAT_size(uchar_t partid); static int open_and_seek(const char *dn, bpb_t *wbpb, off64_t *seekto); static int warn_mismatch(char *desc, char *src, int expect, int assigned); static void copy_bootblk(char *fn, boot_sector_t *bootsect); static int parse_drvnum(char *pn); static bool seek_nofdisk(int fd, bpb_t *wbpb, off64_t *seekto); static bool ask_nicely(int bits, char *special); static bool seek_partn(int fd, char *pn, bpb_t *wbpb, off64_t *seekto); /* * usage * * Display usage message and exit. */ void usage(void) { (void) fprintf(stderr, gettext("pcfs usage: mkfs [-F FSType] [-V] [-m] " "[-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")); (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")); exit(ERR_USAGE); } static bool ask_nicely(int bits, char *special) { /* * 4228473 - No way to non-interactively make a pcfs filesystem * * If we don't have an input TTY, or we aren't really doing * anything, then don't ask questions. Assume a yes answer * to any questions we would ask. */ if (Notreally || !isatty(fileno(stdin))) return (true); (void) printf( gettext("Construct a new FAT%d file system on %s: (y/n)? "), bits, special); (void) fflush(stdout); return (yes()); } /* * parse_drvnum * Convert a partition name into a drive number. */ static int parse_drvnum(char *pn) { int drvnum; /* * Determine logical drive to seek after. */ if (strlen(pn) == 1 && *pn >= 'c' && *pn <= 'z') { drvnum = *pn - 'c' + 1; } else if (*pn >= '0' && *pn <= '9') { char *d; int v, m, c; v = 0; d = pn; while (*d && *d >= '0' && *d <= '9') { c = strlen(d); m = 1; while (--c) m *= 10; v += m * (*d - '0'); d++; } if (*d || v > 24) { (void) fprintf(stderr, gettext("%s: bogus logical drive specification.\n"), pn); return (-1); } drvnum = v; } else if (strcmp(pn, "boot") == 0) { drvnum = 99; } else { (void) fprintf(stderr, gettext("%s: bogus logical drive specification.\n"), pn); return (-1); } return (drvnum); } /* * Define some special logical drives we use. */ #define BOOT_PARTITION_DRIVE 99 #define PRIMARY_DOS_DRIVE 1 /* * 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); } static int warn_mismatch(char *desc, char *src, int expect, int assigned) { if (expect == assigned) return (assigned); /* * 4228473 - No way to non-interactively make a pcfs filesystem * * If we don't have an input TTY, or we aren't really doing * anything, then don't ask questions. Assume a yes answer * to any questions we would ask. */ 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); return (assigned); } (void) printf(gettext("User supplied %s is %d." "\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)? ")); (void) fflush(stdout); if (yes()) return (assigned); else exit(ERR_USER); /*NOTREACHED*/ } static void fill_fat32_bpb(bpb_t *wbpb) { /* * ExtFlags means (according to MSDN BPB (FAT32) document) * * Bit 8 indicates info written to the active FAT is written * to all copies of the FAT. (I think they mean bit 7, with * numbering starting at 0) * * Lowest 4 bits of field are the 0 based FAT number of the * Active FAT. (only meaningful if bit 8 is set) * * Field contains combination of these values: * * VALUE DESCRIPTION * BGBPB_F_ActiveFATMsk Mask for low four bits * (0x000F) * BGBPB_F_NoFATMirror If set FAT mirroring disabled. * (0x0080) If clear, FAT mirroring enabled. * * We set the value based on what I've seen on all the FAT32 drives * I've seen created by Windows. * */ wbpb->bpb32.ext_flags = 0x0; /* * No real explanation of the fs_vers file in the BPB doc. The * high byte is supposed to be the major version and the low the * minor version. Again I set according to what I've seen on Windows. */ wbpb->bpb32.fs_vers_lo = '\0'; wbpb->bpb32.fs_vers_hi = '\0'; /* * The convention appears to be to place the fs info sector * immediately after the boot sector, and that the backup boot * sector should be at sector 6. (based on what I see with * Windows) */ wbpb->bpb32.fsinfosec = 1; wbpb->bpb32.backupboot = 6; } static void fill_bpb_sizes(bpb_t *wbpb, struct ipart part[], int partno, off64_t offset) { ulong_t usesize; if (GetFsParams || GetSize) { usesize = ltohi(part[partno].numsect); if (Verbose) { (void) printf( gettext("Partition size (from FDISK table) " "= %lu sectors.\n"), usesize); } } else { usesize = warn_mismatch( gettext("length of partition (in sectors)"), gettext("FDISK table"), ltohi(part[partno].numsect), TotSize); } if (GetFsParams) { TotSize = usesize; } else { if (usesize > 0xffff) wbpb->bpb.sectors_in_volume = 0; else wbpb->bpb.sectors_in_volume = usesize; wbpb->bpb.sectors_in_logical_volume = usesize; } wbpb->bpb.hidden_sectors = offset; if (GetFsParams) { RelOffset = offset; } else { wbpb->sunbpb.bs_offset_high = offset >> 16; wbpb->sunbpb.bs_offset_low = offset & 0xFFFF; } } /* * lookup_FAT_size * * Given the FDISK partition file system identifier, return the * expected FAT size for the partition. */ static int lookup_FAT_size(uchar_t partid) { int rval; switch (partid) { case DOSOS12: rval = 12; break; case DOSOS16: case DOSHUGE: case FDISK_FAT95: case X86BOOT: rval = 16; break; case FDISK_WINDOWS: case FDISK_EXT_WIN: rval = 32; break; case EXTDOS: case FDISK_EXTLBA: default: rval = -1; break; } return (rval); } /* * seek_partn * * Seek to the beginning of the partition where we need to install * the new FAT. Zero return for any error, but print error * messages here. */ static bool seek_partn(int fd, char *pn, bpb_t *wbpb, off64_t *seekto) { struct ipart part[FD_NUMPART]; struct mboot extmboot; struct mboot mb; diskaddr_t xstartsect; off64_t nextseek = 0; off64_t lastseek = 0; int logicalDriveCount = 0; int extendedPart = -1; int primaryPart = -1; int bootPart = -1; uint32_t xnumsect = 0; int drvnum; int driveIndex; 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; if ((drvnum = parse_drvnum(pn)) < 0) return (false); if (read(fd, &mb, sizeof (mb)) != sizeof (mb)) { (void) fprintf(stderr, gettext("Couldn't read a Master Boot Record?!\n")); return (false); } if (ltohs(mb.signature) != BOOTSECSIG) { (void) fprintf(stderr, gettext("Bad Sig on master boot record!\n")); return (false); } *seekto = 0; /* * Copy partition table into memory */ (void) memcpy(part, mb.parts, sizeof (part)); /* * 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++) { if (isDosDrive(part[i].systid)) { if (primaryPart < 0) { logicalDriveCount++; primaryPart = i; } else { extraDrives[numExtraDrives++] = i; } continue; } if ((extendedPart < 0) && isDosExtended(part[i].systid)) { extendedPart = i; continue; } if ((bootPart < 0) && isBootPart(part[i].systid)) { bootPart = i; continue; } } if (drvnum == BOOT_PARTITION_DRIVE) { if (bootPart < 0) { (void) fprintf(stderr, gettext("No boot partition found on drive\n")); return (false); } if ((*seekto = ltohi(part[bootPart].relsect)) == 0) { (void) fprintf(stderr, gettext("Bogus FDISK entry? " "A boot partition starting\nat sector 0 would " "collide with the FDISK table!\n")); return (false); } fill_bpb_sizes(wbpb, part, bootPart, *seekto); *seekto *= wbpb->bpb.bytes_per_sector; FdiskFATsize = lookup_FAT_size(part[bootPart].systid); if (Verbose) (void) printf(gettext("Boot partition's offset: " "Sector %llx.\n"), *seekto / wbpb->bpb.bytes_per_sector); if (lseek64(fd, *seekto, SEEK_SET) < 0) { (void) fprintf(stderr, gettext("Partition %s: "), pn); perror(""); return (false); } return (true); } if (drvnum == PRIMARY_DOS_DRIVE && primaryPart >= 0) { if ((*seekto = ltohi(part[primaryPart].relsect)) == 0) { (void) fprintf(stderr, gettext("Bogus FDISK entry? " "A partition starting\nat sector 0 would " "collide with the FDISK table!\n")); return (false); } fill_bpb_sizes(wbpb, part, primaryPart, *seekto); *seekto *= wbpb->bpb.bytes_per_sector; FdiskFATsize = lookup_FAT_size(part[primaryPart].systid); if (Verbose) (void) printf(gettext("Partition's offset: " "Sector %llx.\n"), *seekto / wbpb->bpb.bytes_per_sector); if (lseek64(fd, *seekto, SEEK_SET) < 0) { (void) fprintf(stderr, gettext("Partition %s: "), pn); perror(""); return (false); } return (true); } /* * We are not looking for the C: drive (or there was no primary * drive found), so we had better have an extended partition or * extra drives in the Master FDISK table. */ if ((extendedPart < 0) && (numExtraDrives == 0)) { (void) fprintf(stderr, gettext("No such logical drive " "(missing extended partition entry)\n")); return (false); } if (extendedPart >= 0) { nextseek = xstartsect = ltohi(part[extendedPart].relsect); xnumsect = ltohi(part[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 (nextseek == lastseek) break; logicalDriveCount += numDrives; /* * Seek the next extended partition, and find * logical drives within it. */ if (lseek64(fd, nextseek * wbpb->bpb.bytes_per_sector, SEEK_SET) < 0 || read(fd, &extmboot, sizeof (extmboot)) != sizeof (extmboot)) { perror(gettext("Unable to read extended " "partition record")); return (false); } (void) memcpy(part, extmboot.parts, sizeof (part)); lastseek = nextseek; if (ltohs(extmboot.signature) != MBB_MAGIC) { (void) fprintf(stderr, gettext("Bad signature on " "extended partition\n")); return (false); } /* * 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(part[i].systid)) { extndDrives[numDrives++] = i; continue; } else if (isDosExtended(part[i].systid)) { if (nextseek != lastseek) { /* * Already found an extended * partition in this table. */ (void) fprintf(stderr, gettext("WARNING: " "Ignoring unexpected " "additional extended " "partition")); continue; } nextseek = xstartsect + ltohi(part[i].relsect); continue; } } } while (drvnum > logicalDriveCount + numDrives); if (drvnum <= 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 - drvnum; *seekto = ltohi(part[extndDrives[driveIndex]].relsect) + lastseek; if (*seekto == lastseek) { (void) fprintf(stderr, gettext("Bogus FDISK entry? A logical " "drive starting at\nsector 0x%llx would " "collide with the\nFDISK information in " "that sector.\n"), *seekto); return (false); } else if (*seekto <= xstartsect || *seekto >= (xstartsect + xnumsect)) { (void) fprintf(stderr, gettext("Bogus FDISK entry? " "Logical drive start sector (0x%llx)\n" "not within extended partition! " "(Expected in range 0x%llx - 0x%llx)\n"), *seekto, xstartsect + 1, xstartsect + xnumsect - 1); return (false); } fill_bpb_sizes(wbpb, part, extndDrives[driveIndex], *seekto); *seekto *= wbpb->bpb.bytes_per_sector; FdiskFATsize = lookup_FAT_size( part[extndDrives[driveIndex]].systid); if (Verbose) (void) printf(gettext("Partition's offset: " "Sector 0x%llx.\n"), *seekto/wbpb->bpb.bytes_per_sector); if (lseek64(fd, *seekto, SEEK_SET) < 0) { (void) fprintf(stderr, gettext("Partition %s: "), pn); perror(""); return (false); } return (true); } 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; (void) memcpy(part, mb.parts, sizeof (part)); } } /* * Still haven't found the drive, is it an extra * drive defined in the main FDISK table? */ if (drvnum <= logicalDriveCount + numExtraDrives) { driveIndex = logicalDriveCount + numExtraDrives - drvnum; *seekto = ltohi(part[extraDrives[driveIndex]].relsect); if (*seekto == 0) { (void) fprintf(stderr, gettext("Bogus FDISK entry? " "A partition starting\nat sector 0 would " "collide with the FDISK table!\n")); return (false); } fill_bpb_sizes(wbpb, part, extraDrives[driveIndex], *seekto); *seekto *= wbpb->bpb.bytes_per_sector; FdiskFATsize = lookup_FAT_size(part[extraDrives[driveIndex]].systid); if (Verbose) (void) printf(gettext("Partition's offset: " "Sector %llx.\n"), *seekto / wbpb->bpb.bytes_per_sector); if (lseek64(fd, *seekto, SEEK_SET) < 0) { (void) fprintf(stderr, gettext("Partition %s: "), pn); perror(""); return (false); } return (true); } (void) fprintf(stderr, gettext("No such logical drive\n")); return (false); } /* * seek_nofdisk * * User is asking us to trust them that they know best. * We basically won't do much seeking here, the only seeking we'll do * is if the 'hidden' parameter was given. */ static bool seek_nofdisk(int fd, bpb_t *wbpb, off64_t *seekto) { if (TotSize > 0xffff) wbpb->bpb.sectors_in_volume = 0; else wbpb->bpb.sectors_in_volume = (short)TotSize; wbpb->bpb.sectors_in_logical_volume = TotSize; *seekto = RelOffset * wbpb->bpb.bytes_per_sector; wbpb->bpb.hidden_sectors = RelOffset; wbpb->sunbpb.bs_offset_high = RelOffset >> 16; wbpb->sunbpb.bs_offset_low = RelOffset & 0xFFFF; if (Verbose) (void) printf(gettext("Requested offset: Sector %llx.\n"), *seekto/wbpb->bpb.bytes_per_sector); if (lseek64(fd, *seekto, SEEK_SET) < 0) { (void) fprintf(stderr, gettext("User specified start sector %lu"), RelOffset); perror(""); return (false); } return (true); } /* * set_fat_string * * Fill in the type string of the FAT */ static void set_fat_string(bpb_t *wbpb, int fatsize) { if (fatsize == 12) { (void) strncpy((char *)wbpb->ebpb.type, FAT12_TYPE_STRING, strlen(FAT12_TYPE_STRING)); } else if (fatsize == 16) { (void) strncpy((char *)wbpb->ebpb.type, FAT16_TYPE_STRING, strlen(FAT16_TYPE_STRING)); } else { (void) strncpy((char *)wbpb->ebpb.type, FAT32_TYPE_STRING, strlen(FAT32_TYPE_STRING)); } } /* * prepare_image_file * * Open the file that will hold the image (as opposed to the image * being written to the boot sector of an actual disk). */ static int prepare_image_file(const char *fn, bpb_t *wbpb) { int fd; char zerobyte = '\0'; if ((fd = open(fn, O_RDWR | O_CREAT | O_EXCL, 0666)) < 0) { perror(fn); exit(ERR_OS); } if (Imagesize == 5) { /* Disk image of a 1.2M floppy */ wbpb->bpb.sectors_in_volume = 2 * 80 * 15; wbpb->bpb.sectors_in_logical_volume = 2 * 80 * 15; wbpb->bpb.sectors_per_track = 15; wbpb->bpb.heads = 2; wbpb->bpb.media = 0xF9; wbpb->bpb.num_root_entries = 224; wbpb->bpb.sectors_per_cluster = 1; wbpb->bpb.sectors_per_fat = 7; } else { /* Disk image of a 1.44M floppy */ wbpb->bpb.sectors_in_volume = 2 * 80 * 18; wbpb->bpb.sectors_in_logical_volume = 2 * 80 * 18; wbpb->bpb.sectors_per_track = 18; wbpb->bpb.heads = 2; wbpb->bpb.media = 0xF0; wbpb->bpb.num_root_entries = 224; wbpb->bpb.sectors_per_cluster = 1; wbpb->bpb.sectors_per_fat = 9; } /* * Make a holey file, with length the exact * size of the floppy image. */ if (lseek(fd, (wbpb->bpb.sectors_in_volume * MINBPS)-1, SEEK_SET) < 0) { (void) close(fd); perror(fn); exit(ERR_OS); } if (write(fd, &zerobyte, 1) != 1) { (void) close(fd); perror(fn); exit(ERR_OS); } if (lseek(fd, 0, SEEK_SET) < 0) { (void) close(fd); perror(fn); exit(ERR_OS); } Fatentsize = 12; /* Size of fat entry in bits */ set_fat_string(wbpb, Fatentsize); wbpb->ebpb.phys_drive_num = 0; wbpb->sunbpb.bs_offset_high = 0; wbpb->sunbpb.bs_offset_low = 0; return (fd); } /* * partn_lecture * * Give a brief sermon on dev_name user should pass to * the program from the command line. * */ static void 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); } static void warn_funky_floppy(void) { (void) fprintf(stderr, gettext("Use the 'nofdisk' option to create file systems\n" "on non-standard floppies.\n\n")); exit(ERR_FAIL); } static void warn_funky_fatsize(void) { (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")); exit(ERR_FAIL); } static void floppy_bpb_fillin(bpb_t *wbpb, int diam, int hds, int spt) { switch (diam) { case 3: switch (hds) { case 2: switch (spt) { case 9: wbpb->bpb.media = 0xF9; wbpb->bpb.num_root_entries = 112; wbpb->bpb.sectors_per_cluster = 2; wbpb->bpb.sectors_per_fat = 3; break; case 18: wbpb->bpb.media = 0xF0; wbpb->bpb.num_root_entries = 224; wbpb->bpb.sectors_per_cluster = 1; wbpb->bpb.sectors_per_fat = 9; break; case 36: wbpb->bpb.media = 0xF0; wbpb->bpb.num_root_entries = 240; wbpb->bpb.sectors_per_cluster = 2; wbpb->bpb.sectors_per_fat = 9; break; default: (void) fprintf(stderr, gettext("Unknown diskette parameters! " "3.5'' diskette with %d heads " "and %d sectors/track.\n"), hds, spt); warn_funky_floppy(); } break; case 1: default: (void) fprintf(stderr, gettext("Unknown diskette parameters! " "3.5'' diskette with %d heads "), hds); warn_funky_floppy(); } break; case 5: switch (hds) { case 2: switch (spt) { case 15: wbpb->bpb.media = 0xF9; wbpb->bpb.num_root_entries = 224; wbpb->bpb.sectors_per_cluster = 1; wbpb->bpb.sectors_per_fat = 7; break; case 9: wbpb->bpb.media = 0xFD; wbpb->bpb.num_root_entries = 112; wbpb->bpb.sectors_per_cluster = 2; wbpb->bpb.sectors_per_fat = 2; break; case 8: wbpb->bpb.media = 0xFF; wbpb->bpb.num_root_entries = 112; wbpb->bpb.sectors_per_cluster = 1; wbpb->bpb.sectors_per_fat = 2; break; default: (void) fprintf(stderr, gettext("Unknown diskette parameters! " "5.25'' diskette with %d heads " "and %d sectors/track.\n"), hds, spt); warn_funky_floppy(); } break; case 1: switch (spt) { case 9: wbpb->bpb.media = 0xFC; wbpb->bpb.num_root_entries = 64; wbpb->bpb.sectors_per_cluster = 1; wbpb->bpb.sectors_per_fat = 2; break; case 8: wbpb->bpb.media = 0xFE; wbpb->bpb.num_root_entries = 64; wbpb->bpb.sectors_per_cluster = 1; wbpb->bpb.sectors_per_fat = 1; break; default: (void) fprintf(stderr, gettext("Unknown diskette parameters! " "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); warn_funky_floppy(); } break; default: (void) fprintf(stderr, gettext("\nUnknown diskette type. Only know about " "5.25'' and 3.5'' diskettes.\n")); warn_funky_floppy(); } } /* * lookup_floppy * * Look up a media descriptor byte and other crucial BPB values * based on floppy characteristics. */ static void lookup_floppy(struct fd_char *fdchar, bpb_t *wbpb) { ulong_t tsize; ulong_t cyls, spt, hds, diam; cyls = fdchar->fdc_ncyl; diam = fdchar->fdc_medium; spt = fdchar->fdc_secptrack; hds = fdchar->fdc_nhead; tsize = cyls * hds * spt; if (GetFsParams) TotSize = tsize; if (GetSize) { 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); } wbpb->bpb.sectors_in_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); spt = wbpb->bpb.sectors_per_track; } if (GetTPC) { wbpb->bpb.heads = hds; } else { wbpb->bpb.heads = warn_mismatch( gettext("number of heads"), gettext("FDIOGCHAR call"), hds, TrkPerCyl); hds = wbpb->bpb.heads; } Fatentsize = 12; /* Size of fat entry in bits */ if (!GetBPF && BitsPerFAT != Fatentsize) { warn_funky_fatsize(); } set_fat_string(wbpb, Fatentsize); wbpb->ebpb.phys_drive_num = 0; wbpb->bpb.hidden_sectors = 0; wbpb->sunbpb.bs_offset_high = 0; wbpb->sunbpb.bs_offset_low = 0; floppy_bpb_fillin(wbpb, diam, hds, spt); } /* * compute_cluster_size * * Compute an acceptable sectors/cluster value. * * Based on values from the Hardware White Paper * from Microsoft. * "Microsoft Extensible Firmware Initiative * FAT32 File System Specification * FAT: General Overview of On-Disk Format" * * Version 1.03, December 6, 2000 * */ static void compute_cluster_size(bpb_t *wbpb) { ulong_t volsize; ulong_t spc; ulong_t rds, scale, tmpval1, tmpval2; ulong_t fatsz; int newfat = 16; #define FAT12_MAX_CLUSTERS 0x0FF4 #define FAT16_MAX_CLUSTERS 0xFFF4 #define FAT32_MAX_CLUSTERS 0x0FFFFFF0 #define FAT32_SUGGESTED_NCLUST 0x400000 /* compute volume size in sectors. */ volsize = wbpb->bpb.sectors_in_volume ? wbpb->bpb.sectors_in_volume : wbpb->bpb.sectors_in_logical_volume; volsize -= wbpb->bpb.resv_sectors; if (GetSPC) { /* * User indicated what sort of FAT to create, * make sure it is valid with the given size * and compute an SPC value. */ if (!MakeFAT32) { /* FAT16 */ /* volsize is in sectors */ if (volsize < FAT12_MAX_CLUSTERS) { (void) fprintf(stderr, gettext("Requested size is too " "small for FAT16.\n")); exit(ERR_FAIL); } /* SPC must be a power of 2 */ for (spc = 1; spc <= 64; spc = spc * 2) { if (volsize < spc * FAT16_MAX_CLUSTERS) break; } if (volsize > (spc * FAT16_MAX_CLUSTERS)) { (void) fprintf(stderr, gettext("Requested size is too " "large for FAT16.\n")); exit(ERR_FAIL); } } else { /* FAT32 */ /* volsize is in sectors */ if (volsize <= FAT16_MAX_CLUSTERS) { (void) fprintf(stderr, gettext("Requested size is too " "small for FAT32.\n")); exit(ERR_FAIL); } /* SPC must be a power of 2 */ for (spc = 1; spc <= 64; spc = spc * 2) { if (volsize < (spc * FAT32_SUGGESTED_NCLUST)) break; } if (volsize > (spc * FAT32_MAX_CLUSTERS)) { (void) fprintf(stderr, gettext("Requested size is too " "large for FAT32.\n")); exit(ERR_FAIL); } } } else { /* * User gave the SPC as an explicit option, * make sure it will work with the requested * volume size. */ int nclust; spc = SecPerClust; nclust = volsize / spc; if (nclust <= FAT16_MAX_CLUSTERS && MakeFAT32) { (void) fprintf(stderr, gettext("Requested size is too " "small for FAT32.\n")); exit(ERR_FAIL); } if (!MakeFAT32) { /* Determine if FAT12 or FAT16 */ if (nclust < FAT12_MAX_CLUSTERS) newfat = 12; else if (nclust < FAT16_MAX_CLUSTERS) newfat = 16; else { (void) fprintf(stderr, gettext("Requested size is too " "small for FAT32.\n")); exit(ERR_FAIL); } } } /* * RootDirSectors = ((BPB_RootEntCnt * 32) + * (BPB_BytsPerSec - 1)) / BPB_BytsPerSec; */ rds = ((wbpb->bpb.num_root_entries * 32) + (wbpb->bpb.bytes_per_sector - 1)) / wbpb->bpb.bytes_per_sector; if (GetBPF) { if (MakeFAT32) Fatentsize = 32; else Fatentsize = newfat; } else { Fatentsize = BitsPerFAT; if (Fatentsize == 12 && (volsize - rds) >= DOS_F12MAXC * spc) { /* * If we don't have an input TTY, or we aren't * really doing anything, then don't ask * questions. Assume a yes answer to any * questions we would ask. */ if (Notreally || !isatty(fileno(stdin))) { (void) printf( gettext("Volume too large for 12 bit FAT," " increasing to 16 bit FAT size.\n")); (void) fflush(stdout); Fatentsize = 16; } else { (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(ERR_USER); } } } wbpb->bpb.sectors_per_cluster = spc; 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); exit(ERR_INVAL); } 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); (void) printf( gettext("Use -o fat=%d to build a FAT " "that matches the FDISK entry.\n"), FdiskFATsize); exit(ERR_INVAL); } set_fat_string(wbpb, Fatentsize); /* * Compute the FAT sizes according to algorithm from Microsoft: * * RootDirSectors = ((BPB_RootEntCnt * 32) + * (BPB_BytsPerSec - 1)) / BPB_BytsPerSec; * TmpVal1 = DskSize - (BPB_ResvdSecCnt + RootDirSectors); * TmpVal2 = (256 * BPB_SecPerClus) + BPB_NumFATs; * If (FATType == FAT32) * TmpVal2 = TmpVal2 / 2; * FATSz = (TMPVal1 + (TmpVal2 - 1)) / TmpVal2; * If (FATType == FAT32) { * BPB_FATSz16 = 0; * BPB_FATSz32 = FATSz; * } else { * BPB_FATSz16 = LOWORD(FATSz); * // there is no BPB_FATSz32 in a FAT16 BPB * } * * The comment from Microsoft [fatgen103, page 21] is that we should * not think too much about this algorithm and that it works. * However, they neglected to mention, it does work with a 512B sector * size. When using different sector sizes we need to change the * scale factor from 256. Apparently the scale factor is actually * meant to be half of the sector size. */ scale = wbpb->bpb.bytes_per_sector / 2; tmpval1 = volsize - (wbpb->bpb.resv_sectors + rds); tmpval2 = (scale * wbpb->bpb.sectors_per_cluster) + wbpb->bpb.num_fats; if (Fatentsize == 32) tmpval2 = tmpval2 / 2; fatsz = (tmpval1 + (tmpval2 - 1)) / tmpval2; /* Compute a sector/fat figure */ switch (Fatentsize) { case 32: wbpb->bpb.sectors_per_fat = 0; wbpb->bpb32.big_sectors_per_fat = fatsz; if (Verbose) (void) printf("%s: Sectors per FAT32 = %d\n", __func__, 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("%s: Sectors per FAT16 = %d\n", __func__, wbpb->bpb.sectors_per_fat); break; } } static void find_fixed_details(int fd, bpb_t *wbpb) { struct dk_geom dginfo; /* * Look up the last remaining bits of info we need * that is specific to the hard drive using a disk ioctl. */ if (GetSPT || GetTPC) { 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(ERR_OS); } } wbpb->bpb.heads = (GetTPC ? dginfo.dkg_nhead : TrkPerCyl); wbpb->bpb.sectors_per_track = (GetSPT ? dginfo.dkg_nsect : SecPerTrk); if (Verbose) { if (GetTPC) { (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); } } /* * XXX - MAY need an additional flag (or flags) to set media * and physical drive number fields. That in the case of weird * floppies that have to go through 'nofdisk' route for formatting. */ wbpb->bpb.media = 0xF8; if (MakeFAT32) wbpb->bpb.num_root_entries = 0; else wbpb->bpb.num_root_entries = 512; wbpb->ebpb.phys_drive_num = 0x80; compute_cluster_size(wbpb); } static void compute_file_area_size(bpb_t *wbpb) { int FATSz; int TotSec; int DataSec; int RootDirSectors = ((wbpb->bpb.num_root_entries * 32) + (wbpb->bpb.bytes_per_sector - 1)) / wbpb->bpb.bytes_per_sector; if (wbpb->bpb.sectors_per_fat) { /* * Good old FAT12 or FAT16 */ FATSz = wbpb->bpb.sectors_per_fat; TotSec = wbpb->bpb.sectors_in_volume; } else { /* * FAT32 */ FATSz = wbpb->bpb32.big_sectors_per_fat; TotSec = wbpb->bpb.sectors_in_logical_volume; } DataSec = TotSec - (wbpb->bpb.resv_sectors + (wbpb->bpb.num_fats * FATSz) + RootDirSectors); /* * Now change sectors to clusters */ TotalClusters = DataSec / wbpb->bpb.sectors_per_cluster; if (Verbose) (void) printf(gettext("Disk has a file area of %lu " "allocation units,\neach with %d sectors = %lu " "bytes.\n"), TotalClusters, wbpb->bpb.sectors_per_cluster, TotalClusters * wbpb->bpb.sectors_per_cluster * wbpb->bpb.bytes_per_sector); } #ifdef _BIG_ENDIAN /* * swap_pack_{bpb,bpb32,sebpb}cpy * * If not on an x86 we assume the structures making up the bpb * were not packed and that longs and shorts need to be byte swapped * (we've kept everything in host order up until now). A new architecture * might not need to swap or might not need to pack, in which case * new routines will have to be written. Of course if an architecture * supports both packing and little-endian host order, it can follow the * same path as the x86 code. */ static void swap_pack_bpbcpy(struct _boot_sector *bsp, bpb_t *wbpb) { uchar_t *fillp; fillp = (uchar_t *)&(bsp->bs_filler[ORIG_BPB_START_INDEX]); store_16_bits(&fillp, wbpb->bpb.bytes_per_sector); *fillp++ = wbpb->bpb.sectors_per_cluster; store_16_bits(&fillp, wbpb->bpb.resv_sectors); *fillp++ = wbpb->bpb.num_fats; store_16_bits(&fillp, wbpb->bpb.num_root_entries); store_16_bits(&fillp, wbpb->bpb.sectors_in_volume); *fillp++ = wbpb->bpb.media; store_16_bits(&fillp, wbpb->bpb.sectors_per_fat); store_16_bits(&fillp, wbpb->bpb.sectors_per_track); store_16_bits(&fillp, wbpb->bpb.heads); store_32_bits(&fillp, wbpb->bpb.hidden_sectors); store_32_bits(&fillp, wbpb->bpb.sectors_in_logical_volume); *fillp++ = wbpb->ebpb.phys_drive_num; *fillp++ = wbpb->ebpb.reserved; *fillp++ = wbpb->ebpb.ext_signature; store_32_bits(&fillp, wbpb->ebpb.volume_id); (void) strncpy((char *)fillp, (char *)wbpb->ebpb.volume_label, 11); fillp += 11; (void) strncpy((char *)fillp, (char *)wbpb->ebpb.type, 8); } static void swap_pack_bpb32cpy(struct _boot_sector32 *bsp, bpb_t *wbpb) { uchar_t *fillp; int r; fillp = (uchar_t *)&(bsp->bs_filler[ORIG_BPB_START_INDEX]); store_16_bits(&fillp, wbpb->bpb.bytes_per_sector); *fillp++ = wbpb->bpb.sectors_per_cluster; store_16_bits(&fillp, wbpb->bpb.resv_sectors); *fillp++ = wbpb->bpb.num_fats; store_16_bits(&fillp, wbpb->bpb.num_root_entries); store_16_bits(&fillp, wbpb->bpb.sectors_in_volume); *fillp++ = wbpb->bpb.media; store_16_bits(&fillp, wbpb->bpb.sectors_per_fat); store_16_bits(&fillp, wbpb->bpb.sectors_per_track); store_16_bits(&fillp, wbpb->bpb.heads); store_32_bits(&fillp, wbpb->bpb.hidden_sectors); store_32_bits(&fillp, wbpb->bpb.sectors_in_logical_volume); store_32_bits(&fillp, wbpb->bpb32.big_sectors_per_fat); store_16_bits(&fillp, wbpb->bpb32.ext_flags); *fillp++ = wbpb->bpb32.fs_vers_lo; *fillp++ = wbpb->bpb32.fs_vers_hi; store_32_bits(&fillp, wbpb->bpb32.root_dir_clust); store_16_bits(&fillp, wbpb->bpb32.fsinfosec); store_16_bits(&fillp, wbpb->bpb32.backupboot); for (r = 0; r < 6; r++) store_16_bits(&fillp, wbpb->bpb32.reserved[r]); *fillp++ = wbpb->ebpb.phys_drive_num; *fillp++ = wbpb->ebpb.reserved; *fillp++ = wbpb->ebpb.ext_signature; store_32_bits(&fillp, wbpb->ebpb.volume_id); (void) strncpy((char *)fillp, (char *)wbpb->ebpb.volume_label, 11); fillp += 11; (void) strncpy((char *)fillp, (char *)wbpb->ebpb.type, 8); } static void swap_pack_sebpbcpy(struct _boot_sector *bsp, bpb_t *wbpb) { uchar_t *fillp; fillp = bsp->bs_sun_bpb; store_16_bits(&fillp, wbpb->sunbpb.bs_offset_high); store_16_bits(&fillp, wbpb->sunbpb.bs_offset_low); } static void swap_pack_grabsebpb(bpb_t *wbpb, struct _boot_sector *bsp) { uchar_t *grabp; grabp = bsp->bs_sun_bpb; ((uchar_t *)&(wbpb->sunbpb.bs_offset_high))[1] = *grabp++; ((uchar_t *)&(wbpb->sunbpb.bs_offset_high))[0] = *grabp++; ((uchar_t *)&(wbpb->sunbpb.bs_offset_low))[1] = *grabp++; ((uchar_t *)&(wbpb->sunbpb.bs_offset_low))[0] = *grabp++; } #endif /* ! _BIG_ENDIAN */ static void dashm_bail(int fd) { (void) fprintf(stderr, gettext("This media does not appear to be " "formatted with a FAT file system.\n")); (void) close(fd); exit(ERR_INVAL); } /* * read_existing_bpb * * Grab the first sector, which we think is a bios parameter block. * If it looks bad, bail. Otherwise fill in the parameter struct * fields that matter. */ static void read_existing_bpb(int fd, bpb_t *wbpb) { boot_sector_t ubpb; size_t bps; bps = wbpb->bpb.bytes_per_sector; if (read(fd, ubpb.buf, bps) < (ssize_t)bps) { perror(gettext("Read BIOS parameter block " "from previously formatted media")); (void) close(fd); exit(ERR_INVAL); } if (ltohs(ubpb.mb.signature) != BOOTSECSIG) { dashm_bail(fd); } #ifdef _LITTLE_ENDIAN (void) memcpy(&(wbpb->bpb), &(ubpb.bs.bs_front.bs_bpb), sizeof (wbpb->bpb)); (void) memcpy(&(wbpb->ebpb), &(ubpb.bs.bs_ebpb), sizeof (wbpb->ebpb)); #else swap_pack_grabbpb(wbpb, &(ubpb.bs)); #endif if (SunBPBfields) { #ifdef _LITTLE_ENDIAN (void) memcpy(&(wbpb->sunbpb), &(ubpb.bs.bs_sebpb), sizeof (wbpb->sunbpb)); #else swap_pack_grabsebpb(wbpb, &(ubpb.bs)); #endif } if (!is_sector_size_valid(wbpb->bpb.bytes_per_sector)) { (void) close(fd); err(ERR_INVAL, gettext("Invalid bytes/sector (%u): must be 512, 1024, " "2048 or 4096\n"), wbpb->bpb.bytes_per_sector); } bps = wbpb->bpb.bytes_per_sector; if (!(ISP2(wbpb->bpb.sectors_per_cluster) && IN_RANGE(wbpb->bpb.sectors_per_cluster, 1, 128))) { (void) fprintf(stderr, gettext("Bogus sectors per cluster value.\n")); (void) fprintf(stderr, gettext("The device name may be missing a " "logical drive specifier.\n")); (void) close(fd); exit(ERR_INVAL); } if (wbpb->bpb.sectors_per_fat == 0) { #ifdef _LITTLE_ENDIAN (void) memcpy(&(wbpb->bpb32), &(ubpb.bs32.bs_bpb32), sizeof (wbpb->bpb32)); #else swap_pack_grab32bpb(wbpb, &(ubpb.bs)); #endif compute_file_area_size(wbpb); if ((wbpb->bpb32.big_sectors_per_fat * bps / 4) >= TotalClusters) { MakeFAT32 = 1; } else { dashm_bail(fd); } } else { compute_file_area_size(wbpb); } } /* * compare_existing_with_computed * * We use this function when we the user specifies the -m option. * We compute and look up things like we would if they had asked * us to make the fs, and compare that to what's already layed down * in the existing fs. If there's a difference we can tell them what * options to specify in order to reproduce their existing layout. * Note that they still may not get an exact duplicate, because we * don't, for example, preserve their existing boot code. We think * we've got all the fields that matter covered, though. * * XXX - We're basically ignoring sbpb at this point. I'm unsure * if we'll ever care about those fields, in terms of the -m option. */ static void compare_existing_with_computed(int fd, char *suffix, bpb_t *wbpb, int *prtsize, int *prtspc, int *prtbpf, int *prtnsect, int *prtntrk, int *prtfdisk, int *prthidden, int *prtrsrvd, int *dashos) { struct dk_geom dginfo; struct fd_char fdchar; bpb_t compare; int fd_ioctl_worked = 0; int fatents; /* * For all non-floppy cases we expect to find a 16-bit FAT */ int expectfatsize = 16; compare = *wbpb; if (!suffix) { if (ioctl(fd, FDIOGCHAR, &fdchar) != -1) { expectfatsize = 12; fd_ioctl_worked++; } } if (fd_ioctl_worked) { #ifdef sparc fdchar.fdc_medium = 3; #endif GetSize = GetSPT = GetSPC = GetTPC = GetBPF = 1; lookup_floppy(&fdchar, &compare); if (compare.bpb.heads != wbpb->bpb.heads) { (*prtntrk)++; (*dashos)++; } if (compare.bpb.sectors_per_track != wbpb->bpb.sectors_per_track) { (*prtnsect)++; (*dashos)++; } } else { int dk_ioctl_worked = 1; if (!suffix) { (*prtfdisk)++; (*prtsize)++; *dashos += 2; } 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) { (*prtntrk)++; (*dashos)++; } if (dginfo.dkg_nsect != wbpb->bpb.sectors_per_track) { (*prtnsect)++; (*dashos)++; } } GetBPF = GetSPC = 1; compute_cluster_size(&compare); } if (!*prtfdisk && TotSize != wbpb->bpb.sectors_in_volume && TotSize != wbpb->bpb.sectors_in_logical_volume) { (*dashos)++; (*prtsize)++; } if (compare.bpb.sectors_per_cluster != wbpb->bpb.sectors_per_cluster) { (*dashos)++; (*prtspc)++; } if (compare.bpb.hidden_sectors != wbpb->bpb.hidden_sectors) { (*dashos)++; (*prthidden)++; } if (compare.bpb.resv_sectors != wbpb->bpb.resv_sectors) { (*dashos)++; (*prtrsrvd)++; } /* * Compute approximate Fatentsize. It's approximate because the * size of the FAT may not be exactly a multiple of the number of * clusters. It should be close, though. */ if (MakeFAT32) { Fatentsize = 32; (*dashos)++; (*prtbpf)++; } else { fatents = wbpb->bpb.sectors_per_fat * wbpb->bpb.bytes_per_sector * 2 / 3; if (fatents >= TotalClusters && wbpb->ebpb.type[4] == '2') Fatentsize = 12; else Fatentsize = 16; if (Fatentsize != expectfatsize) { (*dashos)++; (*prtbpf)++; } } } static void print_reproducing_command(int fd, char *actualdisk, char *suffix, bpb_t *wbpb) { int needcomma = 0; int prthidden = 0; int prtrsrvd = 0; int prtfdisk = 0; int prtnsect = 0; int prtntrk = 0; int prtsize = 0; int prtbpf = 0; int prtspc = 0; int dashos = 0; int ll, i; compare_existing_with_computed(fd, suffix, wbpb, &prtsize, &prtspc, &prtbpf, &prtnsect, &prtntrk, &prtfdisk, &prthidden, &prtrsrvd, &dashos); /* * Print out the command line they can use to reproduce the * file system. */ (void) printf("mkfs -F pcfs"); ll = MIN(11, (int)strlen((char *)wbpb->ebpb.volume_label)); /* * First, eliminate trailing spaces. Now compare the name against * our default label. If there's a match we don't need to print * any label info. */ i = ll; while (wbpb->ebpb.volume_label[--i] == ' ') ; ll = i; if (ll == strlen(DEFAULT_LABEL) - 1) { char cmpbuf[11]; (void) strcpy(cmpbuf, DEFAULT_LABEL); for (i = ll; i >= 0; i--) { if (cmpbuf[i] != toupper((int)(wbpb->ebpb.volume_label[i]))) { break; } } if (i < 0) ll = i; } if (ll >= 0) { (void) printf(" -o "); (void) printf("b=\""); for (i = 0; i <= ll; i++) { (void) printf("%c", wbpb->ebpb.volume_label[i]); } (void) printf("\""); needcomma++; } else if (dashos) { (void) printf(" -o "); } #define NEXT_DASH_O dashos--; needcomma++; continue while (dashos) { if (needcomma) { (void) printf(","); needcomma = 0; } if (prtfdisk) { (void) printf("nofdisk"); prtfdisk--; NEXT_DASH_O; } if (prtsize) { (void) printf("size=%u", wbpb->bpb.sectors_in_volume ? wbpb->bpb.sectors_in_volume : wbpb->bpb.sectors_in_logical_volume); prtsize--; NEXT_DASH_O; } if (prtnsect) { (void) printf("nsect=%d", wbpb->bpb.sectors_per_track); prtnsect--; NEXT_DASH_O; } if (prtspc) { (void) printf("spc=%d", wbpb->bpb.sectors_per_cluster); prtspc--; NEXT_DASH_O; } if (prtntrk) { (void) printf("ntrack=%d", wbpb->bpb.heads); prtntrk--; NEXT_DASH_O; } if (prtbpf) { (void) printf("fat=%d", Fatentsize); prtbpf--; NEXT_DASH_O; } if (prthidden) { (void) printf("hidden=%u", wbpb->bpb.hidden_sectors); prthidden--; NEXT_DASH_O; } if (prtrsrvd) { (void) printf("reserve=%d", wbpb->bpb.resv_sectors); prtrsrvd--; NEXT_DASH_O; } } (void) printf(" %s%c%c\n", actualdisk, suffix ? ':' : '\0', suffix ? *suffix : '\0'); } /* * open_and_examine * * Open the requested 'dev_name'. Seek to point where * we'd expect to find boot sectors, etc., based on any ':partition' * attachments to the dev_name. * * Examine the fields of any existing boot sector and display best * approximation of how this fs could be reproduced with this command. */ static int open_and_examine(char *dn, bpb_t *wbpb) { struct stat di; off64_t ignored; char *actualdisk = NULL; char *suffix = NULL; int fd, rv; size_t ssize; if (Verbose) (void) printf(gettext("Opening destination device/file.\n")); actualdisk = stat_actual_disk(dn, &di, &suffix); /* * Destination exists, now find more about it. */ if (!(S_ISCHR(di.st_mode))) { (void) fprintf(stderr, gettext("\n%s: device name must be a " "character special device.\n"), actualdisk); exit(ERR_OS); } else if ((fd = open(actualdisk, O_RDWR)) < 0) { perror(actualdisk); exit(ERR_OS); } /* * Get the media sector size. */ rv = get_media_sector_size(fd, &ssize); if (rv != 0) { int e = errno; (void) close(fd); errc(ERR_OS, e, gettext("failed to obtain sector size for %s"), actualdisk); } if (!is_sector_size_valid(ssize)) { (void) close(fd); err(ERR_OS, gettext("Invalid bytes/sector (%zu): must be 512, 1024, " "2048 or 4096\n"), ssize); } wbpb->bpb.bytes_per_sector = ssize; /* * Find appropriate partition if we were requested to do so. */ if (suffix && !(seek_partn(fd, suffix, wbpb, &ignored))) { (void) close(fd); exit(ERR_OS); } read_existing_bpb(fd, wbpb); print_reproducing_command(fd, actualdisk, suffix, wbpb); return (fd); } /* * getdiskinfo * * Extracts information about disk path in dn. We need to return both a * file descriptor and the device's suffix. * Secondarily, we need to detect the FAT type and size when dealing with * GPT partitions. */ static void getdiskinfo(const char *dn, char **actualdisk, char **suffix) { struct stat di; int rv, fd, reserved; dk_gpt_t *gpt = NULL; *actualdisk = stat_actual_disk(dn, &di, suffix); /* * Destination exists, now find more about it. */ if (!(S_ISCHR(di.st_mode))) { (void) fprintf(stderr, gettext("Device name must indicate a " "character special device: %s\n"), *actualdisk); exit(ERR_OS); } else if ((fd = open(*actualdisk, O_RDWR)) < 0) { err(ERR_OS, "%s: failed to open disk device %s", __func__, *actualdisk); } rv = efi_alloc_and_read(fd, &gpt); /* * We should see only VT_EINVAL, VT_EIO and VT_ERROR. * VT_EINVAL is for the case there is no GPT label. * VT_ERROR will happen if device does no support the ioctl, so * we will exit only in case of VT_EIO and unknown value of rv. */ if (rv < 0 && rv != VT_EINVAL && rv != VT_ERROR) { switch (rv) { case VT_EIO: (void) fprintf(stderr, gettext("IO Error reading EFI label\n")); break; default: (void) fprintf(stderr, gettext("Unknown Error %d reading EFI label\n"), rv); break; } (void) close(fd); exit(ERR_OS); } if (rv >= 0) { DontUseFdisk = 1; if (*suffix != NULL) { (void) fprintf(stderr, gettext("Can not use drive specifier \"%s\" with " "GPT partitioning.\n"), *suffix); efi_free(gpt); (void) close(fd); exit(ERR_OS); } /* Can not use whole disk, 7 is GPT minor node "wd" */ if (rv == 7) { (void) fprintf(stderr, gettext("Device name must indicate a " "partition: %s\n"), *actualdisk); efi_free(gpt); (void) close(fd); exit(ERR_OS); } if (GetSize == 1) { TotSize = gpt->efi_parts[rv].p_size; GetSize = 0; } if (GetBPF == 1) { if (GetResrvd == 1) { /* FAT32 has 32 reserved sectors */ reserved = 32; } else { reserved = Resrvd; } /* * The type of FAT is determined by the size of * the partition - reserved sectors. * The calculation is based on logic used in * compute_cluster_size() and therefore we will not * get into error situation when * compute_cluster_size() will be called. */ if (TotSize - reserved < FAT16_MAX_CLUSTERS) { if (GetResrvd == 1) reserved = 1; if (TotSize - reserved < FAT12_MAX_CLUSTERS) { int spc; MakeFAT32 = 0; Fatentsize = 12; /* * compute sectors per cluster * for fat12 */ for (spc = 1; spc <= 64; spc = spc * 2) { if (TotSize - reserved < spc * FAT12_MAX_CLUSTERS) break; } if (GetSPC == 1) { GetSPC = 0; SecPerClust = spc; } } else { MakeFAT32 = 0; Fatentsize = 16; } } else { MakeFAT32 = 1; Fatentsize = 32; Resrvd = reserved; GetResrvd = 0; } } efi_free(gpt); } (void) close(fd); } static void prepare_wbpb(const char *dn, bpb_t *wbpb, char **actualdisk, char **suffix) { /* * We hold these truths to be self evident, all BPBs we create * will have these values in these fields. */ wbpb->bpb.num_fats = 2; /* Set value for prepare_image_file() */ wbpb->bpb.bytes_per_sector = MINBPS; /* Collect info about device */ if (!Outputtofile) getdiskinfo(dn, actualdisk, suffix); /* * Assign or use supplied numbers for hidden and * reserved sectors in the file system. */ if (GetResrvd) if (MakeFAT32) wbpb->bpb.resv_sectors = 32; else wbpb->bpb.resv_sectors = 1; else wbpb->bpb.resv_sectors = Resrvd; wbpb->ebpb.ext_signature = BOOTSIG; /* Magic number for modern format */ wbpb->ebpb.volume_id = 0; if (MakeFAT32) fill_fat32_bpb(wbpb); } /* * open_and_seek * * Open the requested 'dev_name'. Seek to point where * we'll write boot sectors, etc., based on any ':partition' * attachments to the dev_name. * * By the time we are finished here, the entire BPB will be * filled in, excepting the volume label. */ static int open_and_seek(const char *dn, bpb_t *wbpb, off64_t *seekto) { struct fd_char fdchar; struct dk_geom dg; char *actualdisk = NULL; char *suffix = NULL; size_t size = 0; int fd, rv; if (Verbose) (void) printf(gettext("Opening destination device/file.\n")); prepare_wbpb(dn, wbpb, &actualdisk, &suffix); /* * If all output goes to a simple file, call a routine to setup * that scenario. Otherwise, try to find the device. */ if (Outputtofile) return (prepare_image_file(dn, wbpb)); fd = open(actualdisk, O_RDWR); if (fd < 0) { err(ERR_OS, "Failed to open disk device %s", actualdisk); } /* * Check the media sector size */ rv = get_media_sector_size(fd, &size); if (rv != 0) { int e = errno; (void) close(fd); errc(ERR_OS, e, gettext("Failed to obtain sector size for %s"), actualdisk); } if (!is_sector_size_valid(size)) { (void) close(fd); err(ERR_OS, gettext("Invalid bytes/sector (%zu): must be 512, 1024, " "2048 or 4096\n"), size); } /* record sector size */ wbpb->bpb.bytes_per_sector = size; /* * Sanity check. If we've been provided a partition-specifying * suffix, we shouldn't also have been told to ignore the * fdisk table. */ if (DontUseFdisk && suffix) { (void) fprintf(stderr, gettext("Using 'nofdisk' option precludes " "appending logical drive\nspecifier " "to the device name.\n")); goto err_out; } /* * Find appropriate partition if we were requested to do so. */ if (suffix != NULL && !(seek_partn(fd, suffix, wbpb, seekto))) goto err_out; if (suffix == NULL) { /* * We have one of two possibilities. Chances are we have * a floppy drive. But the user may be trying to format * some weird drive that we don't know about and is supplying * all the important values. In that case, they should have set * the 'nofdisk' flag. * * If 'nofdisk' isn't set, do a floppy-specific ioctl to * get the remainder of our info. If the ioctl fails, we have * a good idea that they aren't really on a floppy. In that * case, they should have given us a partition specifier. */ if (DontUseFdisk) { if (!(seek_nofdisk(fd, wbpb, seekto))) goto err_out; find_fixed_details(fd, wbpb); } else if (ioctl(fd, FDIOGCHAR, &fdchar) == -1) { /* * It is possible that we are trying to use floppy * specific FDIOGCHAR ioctl on USB floppy. Since sd * driver, by which USB floppy is handled, doesn't * support it, we can try to use disk DKIOCGGEOM ioctl * to retrieve data we need. sd driver itself * determines floppy disk by number of blocks * (<=0x1000), then it sets geometry to 80 cylinders, * 2 heads. * * Note that DKIOCGGEOM cannot supply us with type * of media (e.g. 3.5" or 5.25"). We will set it to * 3 (3.5") which is most probable value. */ if (errno == ENOTTY) { if (ioctl(fd, DKIOCGGEOM, &dg) != -1 && dg.dkg_ncyl == 80 && dg.dkg_nhead == 2) { fdchar.fdc_ncyl = dg.dkg_ncyl; fdchar.fdc_medium = 3; fdchar.fdc_secptrack = dg.dkg_nsect; fdchar.fdc_nhead = dg.dkg_nhead; lookup_floppy(&fdchar, wbpb); } else { partn_lecture(actualdisk); goto err_out; } } } else { #ifdef sparc fdchar.fdc_medium = 3; #endif lookup_floppy(&fdchar, wbpb); } } else { find_fixed_details(fd, wbpb); } return (fd); err_out: (void) close(fd); exit(ERR_OS); } /* * verify_bootblkfile * * We were provided with the name of a file containing the bootblk * to install. Verify it has a valid boot sector as best we can. Any * errors and we return a bad file descriptor. Otherwise we fill up the * provided buffer with the boot sector, return the file * descriptor for later use and leave the file pointer just * past the boot sector part of the boot block file. */ static int verify_bootblkfile(char *fn, boot_sector_t *bs) { struct stat fi; int bsfd = -1; if (stat(fn, &fi)) { perror(fn); } else if (fi.st_size != MINBPS) { (void) fprintf(stderr, gettext("%s: File size does not fit for a boot sector.\n"), fn); } else if ((bsfd = open(fn, O_RDONLY)) < 0) { perror(fn); } else if (read(bsfd, bs->buf, MINBPS) < MINBPS) { (void) close(bsfd); 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 _LITTLE_ENDIAN (bs->bs.bs_front.bs_jump_code[0] != OPCODE1 && bs->bs.bs_front.bs_jump_code[0] != OPCODE2) #else (bs->bs.bs_jump_code[0] != OPCODE1 && bs->bs.bs_jump_code[0] != OPCODE2) #endif /* CSTYLED */ ) { (void) close(bsfd); bsfd = -1; (void) fprintf(stderr, gettext("Boot block (%s) bogus.\n"), fn); } bs->bs.bs_front.bs_oem_name[0] = 'M'; bs->bs.bs_front.bs_oem_name[1] = 'S'; bs->bs.bs_front.bs_oem_name[2] = 'W'; bs->bs.bs_front.bs_oem_name[3] = 'I'; bs->bs.bs_front.bs_oem_name[4] = 'N'; bs->bs.bs_front.bs_oem_name[5] = '4'; bs->bs.bs_front.bs_oem_name[6] = '.'; bs->bs.bs_front.bs_oem_name[7] = '1'; /* * As we are storing Partition Boot Record, unset * pmbr built in stage2 lba and size. * We do this to stop mdb disk_label module to * try to interpret it. */ if (*((uint64_t *)(bs->buf + STAGE1_STAGE2_LBA)) == 256 && *((uint16_t *)(bs->buf + STAGE1_STAGE2_SIZE)) == 1) { *((uint64_t *)(bs->buf + STAGE1_STAGE2_LBA)) = 0; *((uint16_t *)(bs->buf + STAGE1_STAGE2_SIZE)) = 0; } } return (bsfd); } /* * verify_firstfile * * We were provided with the name of a file to be the first file * installed on the disk. We just need to verify it exists and * find out how big it is. If it doesn't exist, we print a warning * message about how the file wasn't found. We don't exit fatally, * though, rather we return a size of 0 and the FAT will be built * without installing any first file. They can then presumably * install the correct first file by hand. */ static int verify_firstfile(char *fn, ulong_t *filesize) { struct stat fi; int fd = -1; *filesize = 0; if (stat(fn, &fi) || (fd = open(fn, O_RDONLY)) < 0) { perror(fn); (void) fprintf(stderr, gettext("Could not access requested file. It will not\n" "be installed in the new file system.\n")); } else { *filesize = fi.st_size; } return (fd); } /* * label_volume * * Fill in BPB with volume label. */ static void label_volume(char *lbl, bpb_t *wbpb) { int ll, i; /* Put a volume label into our BPB. */ if (!lbl) lbl = DEFAULT_LABEL; ll = MIN(11, (int)strlen(lbl)); for (i = 0; i < ll; i++) { wbpb->ebpb.volume_label[i] = toupper(lbl[i]); } for (; i < 11; i++) { wbpb->ebpb.volume_label[i] = ' '; } } static void copy_bootblk(char *fn, boot_sector_t *bootsect) { int bsfd = -1; if (Verbose) (void) printf(gettext("Request to install boot " "block file %s.\n"), fn); /* * Sanity check that block. */ bsfd = verify_bootblkfile(fn, bootsect); if (bsfd < 0) { exit(ERR_INVALID); } (void) close(bsfd); } /* * mark_cluster * * This routine fills a FAT entry with the value supplied to it as an * argument. The fatp argument is assumed to be a pointer to the FAT's * 0th entry. The clustnum is the cluster entry that should be updated. * The value is the new value for the entry. */ static void mark_cluster(uchar_t *fatp, pc_cluster32_t clustnum, uint32_t value) { uchar_t *ep; ulong_t idx; idx = (Fatentsize == 32) ? clustnum * 4 : (Fatentsize == 16) ? clustnum * 2 : clustnum + clustnum/2; ep = fatp + idx; if (Fatentsize == 32) { store_32_bits(&ep, value); } else if (Fatentsize == 16) { store_16_bits(&ep, value); } else { if (clustnum & 1) { *ep = (*ep & 0x0f) | ((value << 4) & 0xf0); ep++; *ep = (value >> 4) & 0xff; } else { *ep++ = value & 0xff; *ep = (*ep & 0xf0) | ((value >> 8) & 0x0f); } } } static uchar_t * build_fat(bpb_t *wbpb, struct fat_od_fsi *fsinfop, ulong_t *fatsize, char *ffn, int *fffd, ulong_t *ffsize, pc_cluster32_t *ffstartclust) { pc_cluster32_t nextfree, ci; uchar_t *fatp; ushort_t numclust, numsect; int remclust; /* Alloc space for a FAT and then null it out. */ 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); } if (MakeFAT32) { *fatsize = wbpb->bpb.bytes_per_sector * wbpb->bpb32.big_sectors_per_fat; } else { *fatsize = wbpb->bpb.bytes_per_sector * wbpb->bpb.sectors_per_fat; } fatp = calloc(1, *fatsize); if (fatp == NULL) { perror(gettext("FAT table alloc")); exit(ERR_FAIL); } /* Build in-memory FAT */ *fatp = wbpb->bpb.media; *(fatp + 1) = 0xFF; *(fatp + 2) = 0xFF; if (Fatentsize == 16) { *(fatp + 3) = 0xFF; } else if (Fatentsize == 32) { *(fatp + 3) = 0x0F; *(fatp + 4) = 0xFF; *(fatp + 5) = 0xFF; *(fatp + 6) = 0xFF; *(fatp + 7) = 0x0F; } /* * Keep track of clusters used. */ remclust = TotalClusters; nextfree = 2; /* * Get info on first file to install, if any. */ if (ffn) *fffd = verify_firstfile(ffn, ffsize); /* * Reserve a cluster for the root directory on a FAT32. */ if (MakeFAT32) { mark_cluster(fatp, nextfree, PCF_LASTCLUSTER32); wbpb->bpb32.root_dir_clust = nextfree++; remclust--; } /* * Compute and preserve number of clusters for first file. */ if (*fffd >= 0) { *ffstartclust = nextfree; numsect = idivceil(*ffsize, wbpb->bpb.bytes_per_sector); numclust = idivceil(numsect, wbpb->bpb.sectors_per_cluster); if (numclust > remclust) { (void) fprintf(stderr, gettext("Requested first file too large to be\n" "installed in the new file system.\n")); (void) close(*fffd); *fffd = -1; goto finish; } if (Verbose) (void) printf(gettext("Reserving %d first file " "cluster(s).\n"), numclust); 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); remclust -= numclust; } finish: if (Verbose) { (void) printf(gettext("First sector of FAT")); header_for_dump(); dump_bytes(fatp, wbpb->bpb.bytes_per_sector); } (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); } static void dirent_time_fill(struct pcdir *dep) { struct timeval tv; struct tm *tp; ushort_t dostime; ushort_t dosday; (void) gettimeofday(&tv, (struct timezone *)0); tp = localtime(&tv.tv_sec); /* get the time & day into DOS format */ dostime = tp->tm_sec / 2; dostime |= tp->tm_min << 5; dostime |= tp->tm_hour << 11; dosday = tp->tm_mday; dosday |= (tp->tm_mon + 1) << 5; dosday |= (tp->tm_year - 80) << 9; dep->pcd_mtime.pct_time = htols(dostime); dep->pcd_mtime.pct_date = htols(dosday); } static void dirent_label_fill(struct pcdir *dep, char *fn) { int nl, i; /* * We spread the volume label across both the NAME and EXT fields */ nl = MIN(PCFNAMESIZE, strlen(fn)); for (i = 0; i < nl; i++) { dep->pcd_filename[i] = toupper(fn[i]); } if (i < PCFNAMESIZE) { for (; i < PCFNAMESIZE; i++) dep->pcd_filename[i] = ' '; for (i = 0; i < PCFEXTSIZE; i++) dep->pcd_ext[i] = ' '; return; } nl = MIN(PCFEXTSIZE, strlen(fn) - PCFNAMESIZE); for (i = 0; i < nl; i++) dep->pcd_ext[i] = toupper(fn[i + PCFNAMESIZE]); if (i < PCFEXTSIZE) { for (; i < PCFEXTSIZE; i++) dep->pcd_ext[i] = ' '; } } static void dirent_fname_fill(struct pcdir *dep, char *fn) { char *fname, *fext; int nl, i; if ((fname = strrchr(fn, '/')) != NULL) { fname++; } else { fname = fn; } if ((fext = strrchr(fname, '.')) != NULL) { fext++; } else { fext = ""; } fname = strtok(fname, "."); nl = MIN(PCFNAMESIZE, (int)strlen(fname)); for (i = 0; i < nl; i++) { dep->pcd_filename[i] = toupper(fname[i]); } for (; i < PCFNAMESIZE; i++) { dep->pcd_filename[i] = ' '; } nl = MIN(PCFEXTSIZE, (int)strlen(fext)); for (i = 0; i < nl; i++) { dep->pcd_ext[i] = toupper(fext[i]); } for (; i < PCFEXTSIZE; i++) { dep->pcd_ext[i] = ' '; } } static uchar_t * build_rootdir(bpb_t *wbpb, char *ffn, int fffd, ulong_t ffsize, pc_cluster32_t ffstart, ulong_t *rdirsize) { struct pcdir *rootdirp; struct pcdir *entry; /* * Build a root directory. It will have at least one entry, * the volume label and a second if the first file was defined. */ if (MakeFAT32) { /* * We devote an entire cluster to the root * directory on FAT32. */ *rdirsize = wbpb->bpb.sectors_per_cluster * wbpb->bpb.bytes_per_sector; } else { *rdirsize = wbpb->bpb.num_root_entries * sizeof (struct pcdir); } if ((rootdirp = (struct pcdir *)malloc(*rdirsize)) == NULL) { perror(gettext("Root directory allocation")); exit(ERR_FAIL); } else { entry = rootdirp; (void) memset((char *)rootdirp, 0, *rdirsize); } /* Create directory entry for first file, if there is one */ if (fffd >= 0) { dirent_fname_fill(entry, ffn); entry->pcd_attr = Firstfileattr; dirent_time_fill(entry); entry->pcd_scluster_lo = htols(ffstart); if (MakeFAT32) { ffstart = ffstart >> 16; entry->un.pcd_scluster_hi = htols(ffstart); } entry->pcd_size = htoli(ffsize); entry++; } /* Create directory entry for volume label, if there is one */ if (Label != NULL) { dirent_label_fill(entry, Label); entry->pcd_attr = PCA_ARCH | PCA_LABEL; dirent_time_fill(entry); entry->pcd_scluster_lo = 0; if (MakeFAT32) { entry->un.pcd_scluster_hi = 0; } entry->pcd_size = 0; entry++; } if (Verbose) { (void) printf(gettext("First two directory entries")); header_for_dump(); dump_bytes((uchar_t *)rootdirp, 2 * sizeof (struct pcdir)); } return ((uchar_t *)rootdirp); } /* * write_rest * * Write all the bytes from the current file pointer to end of file * in the source file out to the destination file. The writes should * be padded to whole clusters with 0's if necessary. */ static void write_rest(bpb_t *wbpb, char *efn, int dfd, int sfd, int remaining) { char *buf; ushort_t numsect, numclust; ushort_t wnumsect, s; int doneread = 0; int rstat; size_t size; size = wbpb->bpb.bytes_per_sector; buf = malloc(size); if (buf == NULL) { perror(efn); return; } /* * Compute number of clusters required to contain remaining bytes. */ numsect = idivceil(remaining, size); numclust = idivceil(numsect, wbpb->bpb.sectors_per_cluster); wnumsect = numclust * wbpb->bpb.sectors_per_cluster; for (s = 0; s < wnumsect; s++) { if (!doneread) { if ((rstat = read(sfd, buf, size)) < 0) { perror(efn); doneread = 1; rstat = 0; } else if (rstat == 0) { doneread = 1; } (void) memset(&(buf[rstat]), 0, size - rstat); } if (write(dfd, buf, size) != (ssize_t)size) { (void) fprintf(stderr, gettext("Copying ")); perror(efn); } } free(buf); } static void write_fat32_bootstuff(int fd, boot_sector_t *bsp, bpb_t *wbpb, struct fat_od_fsi *fsinfop, off64_t seekto) { char *buf = NULL; size_t size = wbpb->bpb.bytes_per_sector; if (size != MINBPS && !Notreally) { buf = calloc(1, size); if (buf == NULL) { perror(gettext("FS info buffer alloc")); exit(ERR_FAIL); } (void) memcpy(buf, fsinfop, sizeof (*fsinfop)); } else { buf = (char *)fsinfop; } if (Verbose) { (void) printf(gettext("Dump of the fs info sector")); header_for_dump(); dump_bytes((uchar_t *)fsinfop, sizeof (*fsinfop)); } if (!Notreally) { /* * 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, buf, size) != (ssize_t)size) { perror(gettext("FS info sector write")); exit(ERR_FAIL); } if (lseek64(fd, seekto + wbpb->bpb32.backupboot * size, SEEK_SET) < 0) { (void) close(fd); perror(gettext("Boot sector backup seek")); exit(ERR_FAIL); } if (write(fd, bsp->buf, size) != (ssize_t)size) { perror(gettext("Boot sector backup write")); exit(ERR_FAIL); } } /* * Second copy of fs info sector is modified to have "don't know" * as the number of free clusters */ fsinfop = (struct fat_od_fsi *)buf; 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((uchar_t *)fsinfop, sizeof (*fsinfop)); } if (!Notreally) { if (write(fd, buf, size) != (ssize_t)size) { perror(gettext("FS info sector backup write")); exit(ERR_FAIL); } } if (size != MINBPS && !Notreally) free(buf); } static void write_bootsects(int fd, boot_sector_t *bsp, bpb_t *wbpb, struct fat_od_fsi *fsinfop, off64_t seekto) { if (MakeFAT32) { /* Copy our BPB into bootsec structure */ #ifdef _LITTLE_ENDIAN (void) memcpy(&(bsp->bs32.bs_front.bs_bpb), &(wbpb->bpb), sizeof (wbpb->bpb)); (void) memcpy(&(bsp->bs32.bs_bpb32), &(wbpb->bpb32), sizeof (wbpb->bpb32)); (void) memcpy(&(bsp->bs32.bs_ebpb), &(wbpb->ebpb), sizeof (wbpb->ebpb)); #else swap_pack_bpb32cpy(&(bsp->bs32), wbpb); #endif } else { /* Copy our BPB into bootsec structure */ #ifdef _LITTLE_ENDIAN (void) memcpy(&(bsp->bs.bs_front.bs_bpb), &(wbpb->bpb), sizeof (wbpb->bpb)); (void) memcpy(&(bsp->bs.bs_ebpb), &(wbpb->ebpb), sizeof (wbpb->ebpb)); #else swap_pack_bpbcpy(&(bsp->bs), wbpb); #endif /* Copy SUN BPB extensions into bootsec structure */ if (SunBPBfields) { #ifdef _LITTLE_ENDIAN (void) memcpy(&(bsp->bs.bs_sebpb), &(wbpb->sunbpb), sizeof (wbpb->sunbpb)); #else swap_pack_sebpbcpy(&(bsp->bs), wbpb); #endif } } /* Write boot sector */ if (!Notreally && write(fd, bsp->buf, wbpb->bpb.bytes_per_sector) != (ssize_t)wbpb->bpb.bytes_per_sector) { perror(gettext("Boot sector write")); exit(ERR_FAIL); } if (Verbose) { (void) printf(gettext("Dump of the boot sector")); header_for_dump(); dump_bytes(bsp->buf, MINBPS); } if (MakeFAT32) write_fat32_bootstuff(fd, bsp, wbpb, fsinfop, seekto); } static void write_fat(int fd, off64_t seekto, char *fn, char *lbl, char *ffn, bpb_t *wbpb) { struct fat_od_fsi fsinfo; pc_cluster32_t ffsc; boot_sector_t bootsect; uchar_t *fatp, *rdirp; ulong_t fatsize, rdirsize, ffsize; int fffd = -1; compute_file_area_size(wbpb); /* boot sector structure size is always 512B */ copy_bootblk(fn, &bootsect); label_volume(lbl, wbpb); if (Verbose) (void) printf(gettext("Building FAT.\n")); fatp = build_fat(wbpb, &fsinfo, &fatsize, ffn, &fffd, &ffsize, &ffsc); write_bootsects(fd, &bootsect, wbpb, &fsinfo, seekto); if (lseek64(fd, seekto + (wbpb->bpb.bytes_per_sector * wbpb->bpb.resv_sectors), SEEK_SET) < 0) { (void) close(fd); perror(gettext("Seek to end of reserved sectors")); exit(ERR_FAIL); } /* Write FAT */ if (Verbose) (void) printf(gettext("Writing FAT(s). %lu bytes times %u.\n"), fatsize, wbpb->bpb.num_fats); if (!Notreally) { for (uint_t nf = 0; nf < wbpb->bpb.num_fats; nf++) { ssize_t wb; wb = write(fd, fatp, fatsize); if (wb != (ssize_t)fatsize) { perror(gettext("FAT write")); exit(ERR_FAIL); } else { if (Verbose) (void) printf( gettext("Wrote %zd bytes\n"), wb); } } } free(fatp); if (Verbose) (void) printf(gettext("Building root directory.\n")); rdirp = build_rootdir(wbpb, ffn, fffd, ffsize, ffsc, &rdirsize); /* * In non FAT32, root directory exists outside of the file area */ if (Verbose) (void) printf(gettext("Writing root directory. %lu bytes.\n"), rdirsize); if (MakeFAT32) { if (lseek64(fd, seekto + wbpb->bpb.bytes_per_sector * wbpb->bpb.resv_sectors + wbpb->bpb.num_fats * fatsize + wbpb->bpb.bytes_per_sector * wbpb->bpb.sectors_per_cluster * (wbpb->bpb32.root_dir_clust - 2), SEEK_SET) < 0) { (void) close(fd); perror(gettext("Seek to end of reserved sectors")); exit(ERR_FAIL); } } if (!Notreally) { if (write(fd, rdirp, rdirsize) != rdirsize) { perror(gettext("Root directory write")); exit(ERR_FAIL); } } free(rdirp); /* * Now write anything that needs to be in the file space. */ if (fffd >= 0) { if (Verbose) (void) printf(gettext("Writing first file.\n")); if (!Notreally) write_rest(wbpb, ffn, fd, fffd, ffsize); } } static char *LegalOpts[] = { #define NFLAG 0 "N", #define VFLAG 1 "v", #define RFLAG 2 "r", #define HFLAG 3 "h", #define SFLAG 4 "s", #define SUNFLAG 5 "S", #define LABFLAG 6 "b", #define BTRFLAG 7 "B", #define INITFLAG 8 "i", #define SZFLAG 9 "size", #define SECTFLAG 10 "nsect", #define TRKFLAG 11 "ntrack", #define SPCFLAG 12 "spc", #define BPFFLAG 13 "fat", #define FFLAG 14 "f", #define DFLAG 15 "d", #define NOFDISKFLAG 16 "nofdisk", #define RESRVFLAG 17 "reserve", #define HIDDENFLAG 18 "hidden", NULL }; static void parse_suboptions(char *optsstr) { char *value; int c; while (*optsstr != '\0') { switch (c = getsubopt(&optsstr, LegalOpts, &value)) { case NFLAG: Notreally++; break; case VFLAG: Verbose++; break; case RFLAG: Firstfileattr |= 0x01; break; case HFLAG: Firstfileattr |= 0x02; break; case SFLAG: Firstfileattr |= 0x04; break; case SUNFLAG: SunBPBfields = 1; break; case LABFLAG: if (value == NULL) { missing_arg(LegalOpts[c]); } else { Label = value; } break; case BTRFLAG: if (value == NULL) { missing_arg(LegalOpts[c]); } else { BootBlkFn = value; } break; case INITFLAG: if (value == NULL) { missing_arg(LegalOpts[c]); } else { FirstFn = value; } break; case SZFLAG: if (value == NULL) { missing_arg(LegalOpts[c]); } else { TotSize = atoi(value); GetSize = 0; } break; case SECTFLAG: if (value == NULL) { missing_arg(LegalOpts[c]); } else { SecPerTrk = atoi(value); GetSPT = 0; } break; case TRKFLAG: if (value == NULL) { missing_arg(LegalOpts[c]); } else { TrkPerCyl = atoi(value); GetTPC = 0; } break; case SPCFLAG: if (value == NULL) { missing_arg(LegalOpts[c]); } else { SecPerClust = atoi(value); GetSPC = 0; } break; case BPFFLAG: if (value == NULL) { missing_arg(LegalOpts[c]); } else { BitsPerFAT = atoi(value); GetBPF = 0; } break; case NOFDISKFLAG: DontUseFdisk = 1; break; case RESRVFLAG: if (value == NULL) { missing_arg(LegalOpts[c]); } else { Resrvd = atoi(value); GetResrvd = 0; } break; case HIDDENFLAG: if (value == NULL) { missing_arg(LegalOpts[c]); } else { RelOffset = atoi(value); GetOffset = 0; } break; case FFLAG: if (value == NULL) { missing_arg(LegalOpts[c]); } else { DiskName = value; Outputtofile = 1; } break; case DFLAG: if (value == NULL) { missing_arg(LegalOpts[c]); } else { Imagesize = atoi(value); } break; default: bad_arg(value); break; } } } static void sanity_check_options(int argc, int optind) { if (GetFsParams) { if (argc - optind != 1) usage(); return; } if (DontUseFdisk && GetOffset) { /* Set default relative offset of zero */ RelOffset = 0; } if (BitsPerFAT == 32) MakeFAT32 = 1; if (Outputtofile && (argc - optind)) { usage(); } else if (Outputtofile && !DiskName) { usage(); } else if (!Outputtofile && (argc - optind != 1)) { usage(); } else if (SunBPBfields && !BootBlkFn) { (void) fprintf(stderr, gettext("Use of the 'S' option requires that\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")); 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")); 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")); 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")); exit(ERR_OS); } else if (!GetSPC && !(ISP2(SecPerClust) && IN_RANGE(SecPerClust, 1, 128))) { (void) fprintf(stderr, gettext("Invalid Sectors/Cluster value. Must be a " "power of 2 between 1 and 128.\n")); exit(ERR_OS); } 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.")); exit(ERR_OS); } else if (!GetResrvd && MakeFAT32 && (Resrvd < 32 || Resrvd > 0xffff)) { (void) fprintf(stderr, gettext("Invalid number of reserved sectors. " "Must be at least 32 but\nno larger than 65535.")); exit(ERR_OS); } else if (Imagesize != 3 && Imagesize != 5) { usage(); } } int main(int argc, char **argv) { off64_t AbsBootSect = 0; bpb_t dskparamblk; char *string; int fd; int c; (void) setlocale(LC_ALL, ""); #if !defined(TEXT_DOMAIN) #define TEXT_DOMAIN "SYS_TEST" #endif (void) textdomain(TEXT_DOMAIN); if (init_yes() < 0) errx(ERR_OS, gettext(ERR_MSG_INIT_YES), strerror(errno)); while ((c = getopt(argc, argv, "F:Vmo:")) != EOF) { switch (c) { case 'F': string = optarg; if (strcmp(string, "pcfs") != 0) usage(); break; case 'V': { char *opt_text; int opt_count; (void) fprintf(stdout, gettext("mkfs -F pcfs ")); for (opt_count = 1; opt_count < argc; opt_count++) { opt_text = argv[opt_count]; if (opt_text) (void) fprintf(stdout, " %s ", opt_text); } (void) fprintf(stdout, "\n"); } break; case 'm': GetFsParams++; break; case 'o': string = optarg; parse_suboptions(string); break; } } sanity_check_options(argc, optind); if (!Outputtofile) DiskName = argv[optind]; (void) memset(&dskparamblk, 0, sizeof (dskparamblk)); if (GetFsParams) { fd = open_and_examine(DiskName, &dskparamblk); } else { fd = open_and_seek(DiskName, &dskparamblk, &AbsBootSect); if (ask_nicely(Fatentsize, DiskName)) write_fat(fd, AbsBootSect, BootBlkFn, Label, FirstFn, &dskparamblk); } (void) close(fd); fini_yes(); return (0); }