/*
* openrd.c - Open or create ramdisk image file
*
* Copyright (C) 1996-2003 Gero Kuhlmann <gero@gkminix.han.de>
*
* 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 <common.h>
#include <nblib.h>
#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);
}
syntax highlighted by Code2HTML, v. 0.9.1