/* * openrd.c - Open or create ramdisk image file * * Copyright (C) 1996-2003 Gero Kuhlmann * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Id: openrd.c,v 1.7 2003/03/09 00:43:09 gkminix Exp $ */ #define NEED_BINARY 1 #define NEED_DIR 1 #define NEED_TIME 1 #include #include #include "mknbi.h" #include "dir.h" #ifndef _MKNBI_H_DOS_ #error Included wrong header file #endif /* * List of possible output file formats */ struct disklayout { unsigned long size; /* size of floppy in kB */ unsigned int diskid; /* disk ID byte */ unsigned int clustsize; /* size of one cluster in bytes */ unsigned int maxcluster; /* maximum cluster number */ unsigned int fatsects; /* number of sector per FAT */ unsigned int rootentries; /* number of entries in root dir */ unsigned int spt; /* sectors per track */ unsigned int heads; /* number of heads */ unsigned int tracks; /* number of tracks */ unsigned int fat16; /* TRUE if FAT16 */ }; static struct disklayout floppylist[] = { /* * List of floppy formats. This list has to be organized from lowest * to highest size. All formats assume two FATs. The number of sectors * per FAT have to be so that it is possible to include enough * clusters when just one FAT is requested. */ { 320L, 0xFF, SECTSIZE*2, 315, 1, 112, 8, 2, 40, FALSE }, { 360L, 0xFD, SECTSIZE*2, 354, 2, 112, 9, 2, 40, FALSE }, { 720L, 0xF9, SECTSIZE*2, 713, 3, 112, 9, 2, 80, FALSE }, { 1200L, 0xF9, SECTSIZE, 2371, 7, 224, 15, 2, 80, FALSE }, { 1440L, 0xF0, SECTSIZE, 2847, 9, 224, 18, 2, 80, FALSE }, { 2880L, 0xF0, SECTSIZE*2, 2863, 9, 224, 36, 2, 80, FALSE }, { 0L, 0, 0, 0, 0, 0, 0, 0, 0, FALSE } }; static struct disklayout hdlist[] = { /* * These values are the max available when simulating a hard disk. * They have to be adjusted before the actual image is constructed. */ { 4096L, 0xF8, SECTSIZE*2, 3659, 12, 256, 32, 2, 128, FALSE }, { 8192L, 0xF8, SECTSIZE*4, 3672, 12, 256, 32, 2, 256, FALSE }, { 16384L, 0xF8, SECTSIZE*8, 3677, 12, 512, 32, 2, 512, FALSE }, { 32736L, 0xF8, SECTSIZE*16, 3677, 12, 512, 48, 4, 341, FALSE }, { 65472L, 0xF8, SECTSIZE*16, 7359, 32, 512, 48, 4, 682, TRUE }, { 130944L, 0xF8, SECTSIZE*16, 14723, 64, 512, 48, 8, 682, TRUE }, { 262080L, 0xF8, SECTSIZE*16, 29469, 128, 1024, 52, 16, 630, TRUE }, { 0L, 0, 0, 0, 0, 0, 0, 0, 0, FALSE } }; static struct disklayout *curformat = NULL; #define DEFAULT_FORMAT 4 /* Default format if none specified */ #define MAX_CLUST_SIZE SECTSIZE*16 /* Maximum cluster size in table */ /*************************************************************************** Miscellaneous routines ***************************************************************************/ /* Boot block buffer (global) */ __u8 *boot_block = NULL; unsigned int boot_size = 0; /* Pointer to IO.SYS file */ static struct file_struct *io_sys = NULL; static unsigned int io_sys_sects; static unsigned int io_sys_seg; /* * Write a simple partition table into the ramdisk image file */ static void putpart(outfile) int outfile; { static __u8 part_buf[SECTSIZE]; struct partition *part; unsigned int sector, cylinder; unsigned long sectnum; int i; /* Setup the partition table */ memset(part_buf, 0, SECTSIZE); assign(*((__u16 *)&(part_buf[BOOT_SIG_OFF])), BOOT_SIGNATURE); part = (struct partition *)&(part_buf[BOOT_PART_OFF]); part->status = BOOT_ACTIVE; part->type = curformat->fat16 ? (curformat->size > 32767 ? BOOT_TYPE_16_LARGE : BOOT_TYPE_16_SMALL) : BOOT_TYPE_12; sectnum = curformat->size * SECTS_PER_KB - curformat->spt; assign(part->number_sectors.low, htot(low_word(sectnum))); assign(part->number_sectors.high, htot(high_word(sectnum))); assign(part->first_abs_sector.low, htot(low_word(curformat->spt))); assign(part->first_abs_sector.high, htot(high_word(curformat->spt))); /* Determine starting sector of partition */ part->first_sector = 1; part->first_track = 0; part->first_head = 1; /* Determine last sector of partition */ sector = (curformat->spt + sectnum - 1) % curformat->spt; cylinder = (curformat->spt + sectnum - 1) / curformat->spt; part->last_sector = ((sector + 1) & 0x003f) | (((cylinder / curformat->heads) & 0x0300) << 6); part->last_track = (cylinder / curformat->heads) & 0x00ff; part->last_head = (cylinder % curformat->heads) & 0x00ff; /* Write partition table */ (void)nbwrite(part_buf, SECTSIZE, outfile); /* Insert all hidden sectors */ memset(part_buf, 0, SECTSIZE); for (i = 0; i < (curformat->spt - 1); i++) (void)nbwrite(part_buf, SECTSIZE, outfile); } /* * Setup boot record with parameters from disk format table */ static void set_boot_block() { struct boot_record *bp = (struct boot_record *)boot_block; unsigned long l; /* * Setup the disk parameter block within boot sector with the disk * geometry values. */ assert((bp != NULL) && ((boot_size % SECTSIZE) == 0)); assign(bp->bytes_per_sect, htot(SECTSIZE)); assign(bp->dir_num, htot(curformat->rootentries)); assign(bp->sect_per_fat, htot(curformat->fatsects)); assign(bp->sect_per_track, htot(curformat->spt)); assign(bp->head_num, htot(curformat->heads)); bp->sect_per_cluster = (__u8)((curformat->clustsize / SECTSIZE) & 0xff); bp->boot_id = (__u8)(usehd ? BOOT_DEV_HD : BOOT_DEV_FD); bp->media_id = (__u8)(curformat->diskid); bp->fat_num = (singlefat ? 1 : 2); l = curformat->size * SECTS_PER_KB - (usehd ? curformat->spt : 0); assign(bp->sect_num, htot(l & 0xffff)); assign(bp->sect_num_32.low, htot(low_word(l))); assign(bp->sect_num_32.high, htot(high_word(l))); if (usehd) { assign(bp->hidden_num.low, htot(low_word(curformat->spt))); assign(bp->hidden_num.high, htot(high_word(curformat->spt))); } else { assign(bp->hidden_num.low, htot(0)); assign(bp->hidden_num.high, htot(0)); } if (curformat->fat16) bytecpy(BOOT_FAT16_NAME, bp->fat_name, sizeof(bp->fat_name)); else bytecpy(BOOT_FAT12_NAME, bp->fat_name, sizeof(bp->fat_name)); if (volumename == NULL) bytecpy(BOOT_VOL_NAME, bp->vol_name, sizeof(bp->vol_name)); else bytecpy(volumename, bp->vol_name, sizeof(bp->vol_name)); /* * If we use our own private boot sector, we have to set some additional * values, for example the number of sectors occupied by IO.SYS and the * loading segment. */ if (getval(*((__u16 *)&(boot_block[BOOT_SIG_OFF]))) == htot(BOOT_PRIVSIG)) { struct priv_boot *pbp; pbp = (struct priv_boot *)boot_block; if (io_sys != NULL) { assign(pbp->io_sys_sects, htot(io_sys_sects)); assign(pbp->io_sys_ofs, htot(io_sys_seg * 16)); } assign(*((__u16 *)&(boot_block[BOOT_SIG_OFF])), htot(BOOT_SIGNATURE)); } } /*************************************************************************** Directory handling routines ***************************************************************************/ /* Pointer to root directory (global) */ struct dir_struct *root_dir = NULL; /* * Determine required number of root directory sectors. First compute * the multiple of the disk format root dir entry number which holds * all root dir entries, and then compute the number of sectors required * to hold the root directory. * Note that this routine gets used quite often. */ static unsigned int get_root_sects(diskformat) struct disklayout *diskformat; { unsigned int rootentries; rootentries = roundup((DIR_ENTRIES(root_dir) + 1), diskformat->rootentries); return(howmany(rootentries * sizeof(struct dos_dir), SECTSIZE)); } /* * Move the given file into the first position in the file list. This * is required for the MS-DOS system files. */ static void movefirst(dsp, fsp) struct dir_struct *dsp; struct file_struct *fsp; { struct file_struct *cur = dsp->files; struct file_struct *prev = NULL; while (cur != NULL && cur != fsp) { prev = cur; cur = cur->next; } if (cur == fsp && prev != NULL) { prev->next = fsp->next; fsp->next = dsp->files; dsp->files = fsp; } } /* * Delete a file entry */ static void delfile(dsp, fsp) struct dir_struct *dsp; struct file_struct *fsp; { struct file_struct *cur = dsp->files; struct file_struct *prev = NULL; while (cur != NULL && cur != fsp) { prev = cur; cur = cur->next; } if (cur == fsp && prev != NULL) { prev->next = fsp->next; if (fsp->name.lfn_name != NULL) free(fsp->name.lfn_name); free(fsp); } } /* * Delete full directory tree */ static void deltree(dsp) struct dir_struct *dsp; { struct dir_struct *tmpdsp; struct file_struct *fsp; /* Check if there is anything to do */ if (dsp == NULL) return; /* Delete all files in current directory entry */ while (dsp->files != NULL) { fsp = dsp->files; dsp->files = fsp->next; if (fsp->name.lfn_name != NULL) free(fsp->name.lfn_name); free(fsp); } /* Delete all subdirectories */ while (dsp->next != NULL) { tmpdsp = dsp->next; dsp->next = tmpdsp->next; if (dsp->name.lfn_name != NULL) free(dsp->name.lfn_name); deltree(dsp->subdirs); free(tmpdsp); } } /*************************************************************************** Routines to handle DOS configuration files ***************************************************************************/ /* List of configuration files to check for */ static struct { char *name; /* File name */ char *ext; /* File extension */ } const config_files[] = { { "CONFIG ", "SYS" }, { "AUTOEXEC", "BAT" }, { "MSDOS ", "SYS" }, { NULL , NULL } }; /* * Search configuration files within root directory */ static void do_config_files() { struct file_struct *fsp[3]; int i; /* * Search for various DOS configuration files, and if specific versions * exist for the type of ramdisk, remove all other files and make the * specific file current by copying the extension. Note that we have to * use memcpy here because the extension name in the file structure has * a fixed length and does not contain a trailing zero. */ for (i = 0; config_files[i].name != NULL; i++) { fsp[0] = findfile(root_dir, FALSE, config_files[i].name, config_files[i].ext); fsp[1] = findfile(root_dir, FALSE, config_files[i].name, "$$A"); fsp[2] = findfile(root_dir, FALSE, config_files[i].name, "$$C"); if (usehd && (fsp[2] != NULL)) { if (fsp[1] != NULL) delfile(root_dir, fsp[1]); if (fsp[0] != NULL) delfile(root_dir, fsp[0]); memcpy(fsp[2]->name.ext, config_files[i].ext, sizeof(fsp[2]->name.ext)); } else if (!usehd && (fsp[1] != NULL)) { if (fsp[2] != NULL) delfile(root_dir, fsp[2]); if (fsp[0] != NULL) delfile(root_dir, fsp[0]); memcpy(fsp[1]->name.ext, config_files[i].ext, sizeof(fsp[1]->name.ext)); } else { if (fsp[1] != NULL) delfile(root_dir, fsp[1]); if (fsp[2] != NULL) delfile(root_dir, fsp[2]); } } } /*************************************************************************** Routines to handle DOS system files ***************************************************************************/ /* List of DOS system files */ static struct { char *bio_name; /* Name and extension of BIOS file */ char *bio_ext; char *dos_name; /* Name and extension of DOS file */ char *dos_ext; char *cmd_name; /* Name and extension of COMMAND.COM */ char *cmd_ext; char *himem_name; /* Name and extension of XMS driver */ char *himem_ext; unsigned long rpl_size; /* Size for old non-RPL BIOS file */ unsigned int sys_seg; /* Load segment of BIOS file */ int use_size; /* TRUE if to load full BIOS file */ int flags; /* Flags for primary boot loader */ } const system_files[] = { /* PC-DOS or OpenDOS / Caldera-DOS / DR-DOS */ { "IBMBIO ", "COM", "IBMDOS ", "COM", "COMMAND ", "COM", "HIMEM ", "SYS", 24800, 0x0070, TRUE, 0 }, /* MS-DOS */ { "IO ", "SYS", "MSDOS ", "SYS", "COMMAND ", "COM", "HIMEM ", "SYS", 28500, 0x0070, FALSE, 0 }, /* Early DR-DOS */ { "DRBIOS ", "SYS", "DRBDOS ", "SYS", "COMMAND ", "COM", NULL , NULL , 0, 0x0070, FALSE, FLAG_NORPL | FLAG_INT15 }, /* FreeDOS */ { "KERNEL ", "SYS", NULL , NULL , "COMMAND ", "COM", "HIMEM ", "EXE", 0, 0x0060, TRUE, FLAG_NORPL | FLAG_INT15 }, /* PTS-DOS */ { "PTSBIO ", "SYS", "PTSDOS ", "SYS", "COMMAND ", "COM", "HIMEM ", "SYS", 0, 0x0280, FALSE, FLAG_NORPL }, /* End marker */ { NULL , NULL , NULL , NULL , NULL , NULL , NULL , NULL , 0, 0x0000, FALSE, 0 } }; /* * Handle DOS system files */ static void do_system_files(loadflags) int *loadflags; { #define FILE_BIO 0 #define FILE_DOS 1 #define FILE_CMD 2 #define FILE_HIMEM 3 #define FILE_NUM 4 #define FILE_NOT_NEEDED ((struct file_struct *)(NULL + 1)) struct file_struct *fsp[FILE_NUM]; int i; /* * Search for various DOS system files */ for (i = 0; system_files[i].bio_name != NULL; i++) { fsp[FILE_BIO] = findfile(root_dir, FALSE, system_files[i].bio_name, system_files[i].bio_ext); if (system_files[i].dos_name != NULL) fsp[FILE_DOS] = findfile(root_dir, FALSE, system_files[i].dos_name, system_files[i].dos_ext); else fsp[FILE_DOS] = FILE_NOT_NEEDED; if (system_files[i].cmd_name != NULL) fsp[FILE_CMD] = findfile(root_dir, TRUE, system_files[i].cmd_name, system_files[i].cmd_ext); else fsp[FILE_CMD] = FILE_NOT_NEEDED; if (system_files[i].himem_name != NULL) fsp[FILE_HIMEM] = findfile(root_dir, TRUE, system_files[i].himem_name, system_files[i].himem_ext); else fsp[FILE_HIMEM] = NULL; if ((fsp[FILE_BIO] != NULL) && (fsp[FILE_DOS] != NULL) && (fsp[FILE_CMD] != NULL)) break; } if ((fsp[FILE_BIO] == NULL) || (fsp[FILE_DOS] == NULL) || (fsp[FILE_CMD] == NULL)) { prnerr0("one of the DOS system files is missing"); exit(EXIT_DOS_INVSYS); } /* * Move them within the root directory so that IO.SYS comes first, then * MSDOS.SYS and finally COMMAND.COM. */ if (fsp[FILE_CMD] != FILE_NOT_NEEDED) movefirst(root_dir, fsp[FILE_CMD]); if (fsp[FILE_DOS] != FILE_NOT_NEEDED) movefirst(root_dir, fsp[FILE_DOS]); movefirst(root_dir, fsp[FILE_BIO]); /* * Handle various system flags. The NORPL flag has some magic: * Various types of DOS don't support the RPL mechanism of protecting * the memory occupied by the ramdisk driver. These are FreeDOS and * PTS-DOS. However, also older versions of MS-DOS and IBM's PC-DOS * don't support it. We try to identify those old DOS versions by * checking the size of IO.SYS or IBMBIO.COM against some magic value * which is a little bit below the size of IO.SYS in the first version * which does support RPL protection. Unfortunately, Caldera's OpenDOS * does support RPL, but it's IBMBIO.COM file is a lot smaller than * that of IBM's PC-DOS. I hope that the values selected in the table * above are at least a little bit reasonable. * The INT15 flag will be used when it is not possible to protect the * ramdisk via HIMEM.SYS. The XMS driver which comes with FreeDOS does * not work properly, so we set this flag automatically for FreeDOS. Also, * if we can't find an XMS driver in one of the ramdisk directories, we * can't use it. Of course even if we find an XMS driver it doesn't mean * that it gets called by CONFIG.SYS, but it is at least a little bit of * error protection. */ *loadflags = system_files[i].flags | (useint15 ? FLAG_INT15 : 0) | (norpl ? FLAG_NORPL : 0) | (fsp[FILE_HIMEM] == NULL ? FLAG_INT15 : 0) | (fsp[FILE_BIO]->size < system_files[i].rpl_size ? FLAG_NORPL : 0); /* * Set some boot sector information about IO.SYS. Some operating systems * require to load IO.SYS (or equiv.) completely by the boot sector, so we * have to set the total number of sectors occupied by IO.SYS in this case. * Otherwise only the first 4 sectors of IO.SYS needs to get loaded. */ io_sys = fsp[FILE_BIO]; io_sys_seg = system_files[i].sys_seg; if (system_files[i].use_size) io_sys_sects = howmany(io_sys->size, SECTSIZE); else io_sys_sects = 4; } /*************************************************************************** Routines to generate the FAT ***************************************************************************/ /* Static variables for generating the FAT */ static unsigned short cur_cluster; /* current cluster number */ static __u8 *fat_buf; /* FAT buffer */ /* * Generate 12 or 16 bit FAT entries for a given cluster range */ static void genclusters(start, length) unsigned short start; unsigned short length; { unsigned short cur = start; unsigned short value; unsigned int i; while (cur < (start + length)) { if (cur > curformat->maxcluster) { prnerr0("not enough space on ramdisk"); exit(EXIT_DOS_RDSPACE); } if (curformat->fat16) { value = (cur == (start + length - 1)) ? 0xffff : cur + 1; i = cur * 2; fat_buf[i] = (unsigned char)(value & 0x00ff); fat_buf[i+1] = (unsigned char)((value & 0xff00) >> 8); } else { value = (cur == (start + length - 1)) ? 0xfff : cur + 1; i = (cur * 3) / 2; if ((cur * 3) % 2 == 0) { fat_buf[i] = (unsigned char)(value & 0x0ff); fat_buf[i+1] |= (unsigned char)((value & 0xf00) >> 8); } else { fat_buf[i] |= (unsigned char)((value & 0x00f) << 4); fat_buf[i+1] = (unsigned char)((value & 0xff0) >> 4); } } cur++; } } /* * Scan through the directory tree to generate the FAT */ static void genfatdir(dsp) struct dir_struct *dsp; { struct file_struct *fsp; /* First compute the number of clusters for current directory */ if (dsp != root_dir) { dsp->clustnum = howmany(DIR_ENTRIES(dsp) * sizeof(struct dos_dir), curformat->clustsize); dsp->cluster = cur_cluster; cur_cluster += dsp->clustnum; genclusters(dsp->cluster, dsp->clustnum); } /* Determine the number of clusters for each file in current directory */ fsp = dsp->files; while (fsp != NULL) { fsp->clustnum = howmany(fsp->size, curformat->clustsize); fsp->cluster = cur_cluster; cur_cluster += fsp->clustnum; genclusters(fsp->cluster, fsp->clustnum); fsp = fsp->next; } /* Scan through all subdirectories recursively */ dsp = dsp->subdirs; while (dsp != NULL) { genfatdir(dsp); dsp = dsp->next; } } /* * Generate the FAT for the files and directories in the tree. */ static void genfat() { if (verbose > 1) printf("Generating FATs\n"); fat_buf = (__u8 *)nbmalloc(curformat->fatsects * SECTSIZE); fat_buf[0] = (__u8)(curformat->diskid & 0xff); fat_buf[1] = 0xff; /* filler byte */ fat_buf[2] = 0xff; if (curformat->fat16) fat_buf[3] = 0xff; cur_cluster = FIRST_CLUSTER; genfatdir(root_dir); } /*************************************************************************** Write directories and files into output file ***************************************************************************/ /* * Write a long file name into the directory buffer */ static void writelfn(dirp, namep) struct dos_dir **dirp; struct fname_struct *namep; { struct lfn_dir *lfnp = (struct lfn_dir *)(*dirp); utf16_t *cp; int chksum, i, j; /* Check if we have anything to do at all */ if (namep->lfn_num == 0 || namep->lfn_name == NULL) return; /* Compute checksum of short file name */ chksum = 0; for (i = 0; i < 8; i++) { chksum = (chksum >> 1) | ((chksum & 0x01) << 7); chksum = (chksum + totarget(namep->name[i])) & 0xff; } for (i = 0; i < 3; i++) { chksum = (chksum >> 1) | ((chksum & 0x01) << 7); chksum = (chksum + totarget(namep->ext[i])) & 0xff; } /* Now create all long file name directory entries */ i = 1; j = 0; cp = namep->lfn_name; lfnp->sequence = (i & LFN_SEQ_MASK); lfnp->checksum = (chksum & 0xff); lfnp->attrib = ATTR_LFN; while (i < (namep->lfn_num + 1)) { if (j < 5) assign(lfnp->first_part[j], htot(*cp)); else if (j < 11) assign(lfnp->second_part[j - 5], htot(*cp)); else assign(lfnp->third_part[j - 11], htot(*cp)); j++; cp++; if (j >= LFN_CHARS) { i++; if (i < (namep->lfn_num + 1)) { lfnp++; lfnp->sequence = (i & LFN_SEQ_MASK); lfnp->checksum = (chksum & 0xff); lfnp->attrib = ATTR_LFN; j = 0; } } } lfnp->sequence |= LFN_SEQ_END; /* Set pointer of next free directory entry */ *dirp = (struct dos_dir *)(++lfnp); } /* * Recursively write each subdirectory */ static void writedir(handle, dsp, parent, fatfs) int handle; struct dir_struct *dsp; struct dir_struct *parent; int fatfs; { struct dos_dir *dirp; struct dir_struct *subdsp; struct file_struct *fsp; __u8 *buf; size_t size; /* Allocate memory for directory */ if (parent == NULL) size = curformat->rootentries * sizeof(struct dos_dir); else size = dsp->clustnum * curformat->clustsize; buf = (__u8 *)nbmalloc(size); /* Generate the entries for current and parent directories and volume name */ dirp = (struct dos_dir *)buf; if (parent != NULL) { bytecpy(". ", dirp->name, sizeof(dirp->name)); bytecpy(" ", dirp->ext, sizeof(dirp->ext)); dirp->attrib = dsp->attrib; assign(dirp->time, htot(dsp->time)); assign(dirp->date, htot(dsp->date)); assign(dirp->cluster, htot(dsp->cluster)); assign(dirp->size.low, htot(0)); assign(dirp->size.high, htot(0)); dirp++; bytecpy(".. ", dirp->name, sizeof(dirp->name)); bytecpy(" ", dirp->ext, sizeof(dirp->ext)); dirp->attrib = parent->attrib; assign(dirp->time, htot(parent->time)); assign(dirp->date, htot(parent->date)); assign(dirp->cluster, htot(parent->cluster)); assign(dirp->size.low, htot(0)); assign(dirp->size.high, htot(0)); dirp++; } else if (volumename != NULL) { bytecpy(&(volumename[0]), dirp->name, sizeof(dirp->name)); bytecpy(&(volumename[8]), dirp->ext, sizeof(dirp->ext)); dirp->attrib = ATTR_LABEL; dirp++; } /* Write the file entries into directory. The files have to come first! */ fsp = dsp->files; while (fsp != NULL) { writelfn(&dirp, &(fsp->name)); bytecpy(fsp->name.name, dirp->name, sizeof(dirp->name)); bytecpy(fsp->name.ext, dirp->ext, sizeof(dirp->ext)); dirp->attrib = fsp->attrib; assign(dirp->time, htot(fsp->time)); assign(dirp->date, htot(fsp->date)); assign(dirp->cluster, htot(fsp->cluster)); assign(dirp->size.low, htot(low_word(fsp->size))); assign(dirp->size.high, htot(high_word(fsp->size))); dirp++; fsp = fsp->next; } /* Write the subdir entries into directory */ subdsp = dsp->subdirs; while (subdsp != NULL) { writelfn(&dirp, &(subdsp->name)); bytecpy(subdsp->name.name, dirp->name, sizeof(dirp->name)); bytecpy(subdsp->name.ext, dirp->ext, sizeof(dirp->ext)); dirp->attrib = subdsp->attrib; assign(dirp->time, htot(subdsp->time)); assign(dirp->date, htot(subdsp->date)); assign(dirp->cluster, htot(subdsp->cluster)); assign(dirp->size.low, htot(0)); assign(dirp->size.high, htot(0)); dirp++; subdsp = subdsp->next; } /* Write the directory into output file and free directory memory */ (void)nbwrite(buf, size, handle); free(buf); /* Write all files in the same order as setup in the FAT */ fsp = dsp->files; while (fsp != NULL) { if (fatfs) fatcopy(curformat->clustsize, handle, fsp); else hostcopy(curformat->clustsize, handle, fsp); fsp = fsp->next; } /* Finally write all subdirectories into the output file */ subdsp = dsp->subdirs; while (subdsp != NULL) { writedir(handle, subdsp, dsp, fatfs); subdsp = subdsp->next; } } /*************************************************************************** Create new ramdisk image from directory ***************************************************************************/ /* * When creating a hard disk image, adjust the format parameters to * match the actual ramdisk size. */ static struct disklayout *adjust_format(oldformat, rdsize) struct disklayout *oldformat; unsigned long rdsize; { struct disklayout *newformat; unsigned int rootsects; unsigned int fatsects; unsigned long l, m; /* * Allocate memory for new disk geometry record */ if (verbose > 1) printf("Adjusting image parameters\n"); newformat = (struct disklayout *)nbmalloc(sizeof(struct disklayout)); *newformat = *oldformat; /* * From requested ramdisk size compute the disk geometry. Note that * we use the next largest number of tracks needed for the requested * ramdisk size, even if the ramdisk image in memory isn't large * enough. The runtime module will check if the maximum size gets * exceeded in a request. However, the ramdisk size in the format * record has to be the exact size, so that the partition table * gets constructed correctly. */ newformat->size = rdsize; newformat->tracks = howmany(rdsize * SECTS_PER_KB, (oldformat->heads * oldformat->spt)); assert(newformat->tracks <= MAX_CYLS); /* * Determine the number of root directory entries, and from this the * number of sectors required for the root directory. */ rootsects = get_root_sects(oldformat); newformat->rootentries = (rootsects * SECTSIZE) / sizeof(struct dos_dir); /* * Determine the number of sectors per FAT and the maximum cluster number. * For this computation we use the real ramdisk size, e.g. the size of the * ramdisk "partition". */ l = (rdsize * SECTS_PER_KB) - newformat->spt - rootsects - (boot_size / SECTSIZE); fatsects = 0; do { fatsects++; if (newformat->fat16) m = ((fatsects * SECTSIZE * 8) / 16 - FIRST_CLUSTER - 16) * (newformat->clustsize / SECTSIZE); else m = ((fatsects * SECTSIZE * 8) / 12 - FIRST_CLUSTER - 16) * (newformat->clustsize / SECTSIZE); m += fatsects * (singlefat ? 1 : 2); } while (m < l); assert(fatsects <= newformat->fatsects); l -= fatsects * (singlefat ? 1 : 2); newformat->fatsects = fatsects; newformat->maxcluster = (l + FIRST_CLUSTER) / (newformat->clustsize / SECTSIZE); /* * Let the user know what we got */ if (verbose > 2) { printf("New image values:\n"); printf(" Total size in kB: %lu\n", newformat->size); printf(" Cluster size in Bytes: %u\n", newformat->clustsize); printf(" Maximum cluster number: %u\n", newformat->maxcluster); printf(" Number of FAT sectors: %u\n", newformat->fatsects); printf(" Number of sectors per track: %u\n", newformat->spt); printf(" Number of cylinders: %u\n", newformat->tracks); printf(" Number of heads: %u\n", newformat->heads); } return(newformat); } /* * Read all directories and create a new ramdisk image with a 12 or * 16 bit FAT. */ static int rdcreate(name, rdsize, loadflags, fatfs) char *name; unsigned long rdsize; int *loadflags; int fatfs; { FILE *tmprd; unsigned long l, m; int rdfile, i; /* * Read the root directory and setup the directory tree */ if (verbose > 0) printf("Scanning directory tree of %s\n", name); if (fatfs) fatopen(name); else hostopen(name); /* * Handle configuration files */ do_config_files(); /* * Handle DOS system files */ do_system_files(loadflags); /* * Determine the size of the image in the temporary file. For a hard * disk select the format which leaves at least 20% space, if a size * hasn't been specified already. */ l = root_dir->totsize * SECTS_PER_KB; if (usehd) { if (rdsize == 0) { m = 0; for (i = 0; hdlist[i].size != 0; i++) { m = ((l * 12L) / 10L) + hdlist[i].spt + ((singlefat ? 1 : 2) * hdlist[i].fatsects) + get_root_sects(&hdlist[i]) + (boot_size / SECTSIZE); if (hdlist[i].size >= (m / SECTS_PER_KB)) break; } if (hdlist[i].size == 0) { prnerr0("too much contents for ramdisk"); exit(EXIT_DOS_RDSIZE); } rdsize = m / SECTS_PER_KB; } else { if (rdsize < MIN_RDSIZE) { prnerr0("requested ramdisk size too small"); exit(EXIT_DOS_RDSIZE); } for (i = 0; hdlist[i].size != 0; i++) if (hdlist[i].size >= rdsize) break; if (hdlist[i].size == 0) { prnerr0("specified ramdisk size too large"); exit(EXIT_DOS_RDSIZE); } m = l + hdlist[i].spt + ((singlefat ? 1 : 2) * hdlist[i].fatsects) + get_root_sects(&hdlist[i]) + (boot_size / SECTSIZE); if (rdsize < (m / SECTS_PER_KB)) { prnerr0("too much contents for specified ramdisk size"); exit(EXIT_DOS_RDSIZE); } } curformat = adjust_format(&hdlist[i], rdsize); } else { if (rdsize == 0) { i = DEFAULT_FORMAT; rdsize = floppylist[i].size; } else { for (i = 0; floppylist[i].size != 0; i++) if (floppylist[i].size == rdsize) break; if (floppylist[i].size == 0) { prnerr0("invalid ramdisk size given"); exit(EXIT_DOS_RDSIZE); } } /* Floppies have a fixed number of root dir entries */ if ((DIR_ENTRIES(root_dir) + 1) > floppylist[i].rootentries) { prnerr0("too many root directory entries"); exit(EXIT_DOS_RDSIZE); } m = l + ((singlefat ? 1 : 2) * floppylist[i].fatsects) + get_root_sects(&floppylist[i]) + (boot_size / SECTSIZE) + 1; if (rdsize < (m / SECTS_PER_KB)) { prnerr0("too much contents for ramdisk"); exit(EXIT_DOS_RDSIZE); } /* Adjust format if necessary */ curformat = (struct disklayout *)nbmalloc(sizeof(struct disklayout)); *curformat = floppylist[i]; if (singlefat) curformat->maxcluster += curformat->fatsects / (curformat->clustsize / SECTSIZE); } if (verbose > 0) printf("Generating %lu kB ramdisk image\n", curformat->size); /* * Generate the FAT. */ genfat(); /* * Create a temporary file to receive the ramdisk image. */ if ((tmprd = tmpfile()) == NULL) { prnerr0("unable to create temporary file"); exit(EXIT_CREATE); } rdfile = fileno(tmprd); /* * If creating a hard disk image, write out the partition table first. */ if (usehd) { if (verbose > 0) printf("Writing partition table into temporary ramdisk image\n"); putpart(rdfile); } /* * Setup the floppy boot sector. */ set_boot_block(); /* * Write the floppy boot sector, both FATs and the directory trees and * files. */ if (verbose > 0) printf("Writing temporary ramdisk image file\n"); (void)nbwrite(boot_block, boot_size, rdfile); (void)nbwrite(fat_buf, curformat->fatsects * SECTSIZE, rdfile); if (!singlefat) (void)nbwrite(fat_buf, curformat->fatsects * SECTSIZE, rdfile); writedir(rdfile, root_dir, NULL, fatfs); /* * Close source image/directory. This will delete all intermediate * structures including the boot block buffer and the directory tree. */ if (fatfs) fatclose(); else hostclose(); deltree(root_dir); root_dir = NULL; return(rdfile); } /*************************************************************************** Main routine to open/create ramdisk image file ***************************************************************************/ /* * Open ram disk image file. If it's not a file but a directory, generate * an image file using this directory. */ int openrd(name, rdsize, geom, loadflags) char *name; unsigned long rdsize; struct disk_geometry *geom; int *loadflags; { struct stat sbuf; unsigned long sectnum; int rdfile; /* * Check whether we have a block device, an image file or a * directory. */ if (stat(name, &sbuf) != 0) { prnerr1("unable to stat %s", name); exit(EXIT_DOS_RDSTAT); } if (S_ISREG(sbuf.st_mode) || S_ISBLK(sbuf.st_mode)) rdfile = rdcreate(name, rdsize, loadflags, TRUE); else if (S_ISDIR(sbuf.st_mode)) rdfile = rdcreate(name, rdsize, loadflags, FALSE); else { prnerr1("%s is not a file, block device or directory", name); exit(EXIT_DOS_INVRD); } /* * Return the new ramdisk geometry */ sectnum = curformat->size * SECTS_PER_KB; assign(geom->num_sectors.low, htot(low_word(sectnum))); assign(geom->num_sectors.high, htot(high_word(sectnum))); assign(geom->sect_per_track, htot(curformat->spt)); assign(geom->cylinders, htot(curformat->tracks)); assign(geom->num_heads, htot(curformat->heads)); geom->no_hd_flag = nohd & 0xff; if (usehd) geom->boot_drive = BOOT_DEV_HD; else geom->boot_drive = BOOT_DEV_FD; if (curformat != NULL) free(curformat); /* * Rewind output file and return its file handle. */ if (lseek(rdfile, 0L, 0) != 0) { prnerr0("unable to seek to beginning of ramdisk file"); exit(EXIT_SEEK); } return(rdfile); }