File:  [MW Coherent from dump] / coherent / d / bin / dos / dos1.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs
Wed May 29 04:56:39 2019 UTC (7 years, 2 months ago) by root
Branches: MarkWilliams, MAIN
CVS tags: relic, HEAD
coherent

/* dos1.c */
/* Globals and functions common to the "dos" and "dosshrink" commands. */

#include "dos0.h"

/* Diskette parameters. */
BPB	d8floppy  = { 512, 2, 1, 2, 112,  640, 0xFF, 1,  8, 2, 0 };
BPB	d9floppy  = { 512, 2, 1, 2, 112,  720, 0xFD, 2,  9, 2, 0 };
BPB	d15floppy = { 512, 1, 1, 2, 224, 2400, 0xF9, 7, 15, 2, 0 };
BPB	d18floppy = { 512, 1, 1, 2, 224, 2880, 0xF0, 9, 18, 2, 0 };
BPB	q9floppy  = { 512, 2, 1, 2, 112, 1440, 0xF9, 3,  9, 2, 0 };
BPB	s8floppy  = { 512, 1, 1, 2,  64,  320, 0xFE, 1,  8, 1, 0 };
BPB	s9floppy  = { 512, 1, 1, 2,  64,  360, 0xFC, 2,  9, 1, 0 };

/* Globals. */
char		*argv0;			/* Command name			*/
unsigned char	bootb[BBSIZE];		/* Boot block			*/
BPB		*bpb;			/* Disk parameters		*/
short		cflag;			/* Readonly			*/
unsigned char	*clbuf;			/* One cluster buffer		*/
unsigned short	clsize;			/* Sectors per cluster		*/
unsigned short	dirbase;		/* First root directory sector	*/
unsigned short	dirsize;		/* Root dir size in sectors	*/
unsigned short	fatbase;		/* First FAT sector		*/
unsigned short	fatbytes;		/* Bytes per FAT entry (1 means 1.5)*/
unsigned short	*fatcache;		/* File allocation table cache	*/
unsigned short	fatccount;		/* Sectors in FAT cache		*/
unsigned short	fatcfirst;		/* First sector in FAT cache	*/
short		fatcflag;		/* FAT must be written		*/
unsigned short	fatcmax;		/* Max cluster in FAT cache	*/
unsigned short	fatcmin;		/* Min cluster in FAT cache	*/
unsigned short	fatsize;		/* FAT size in sectors		*/
unsigned short	filebase;		/* First disk file data sector	*/
short		fsfd;			/* File system file descriptor	*/
unsigned short	heads;			/* Heads			*/
unsigned short	maxcluster;		/* Max cluster number		*/
unsigned short	mdirsize;		/* MDIRs per cluster		*/
unsigned short	nspt;			/* Sectors per track		*/
unsigned short	sectors;		/* Sectors			*/
unsigned short	ssize;			/* Sector size			*/
char		*usagemsg;		/* Usage message		*/
short		vflag;			/* Verbose			*/

/*
 * Convert cluster number n to a block number.
 * Complain if the cluster number is out of range.
 */
unsigned long
cltosec(n) register unsigned short n;
{
	if (n < 2 || n > maxcluster)
		fatal("cluster number %u out of range", n);
	return ((long)(n - 2) * (long)clsize + filebase);
}

/*
 * Decode or encode a FAT with 1.5-byte entries.
 * The FAT is already in the FAT cache and the cache is big enough
 * for the expanded version with 2-byte entries.
 * The flag is 0 to decode after read, 1 to encode before write.
 * This lazy code uses short pointers on char boundaries.
 */
void
decodefat(flag) short flag;
{
	register unsigned short i;
	register unsigned short u, *ip;
	register unsigned char *cp;

	cp = fatcache;
	if (flag == 0) {
		/* Expand 1.5-byte entries to unsigned shorts. */
		for (i = maxcluster; ; i--) {
			ip = &cp[i*3/2];
			fatcache[i] = *ip;
			if (i & 1)
				fatcache[i] >>= 4;
			else
				fatcache[i] &= FATMASK;
			if (fatcache[i] > (CLMAX & FATMASK))
				fatcache[i] |= 0xF000;
			if (i == 0)
				break;
		}
	} else {
		/* Compress the FAT table to 1.5-byte entries. */
		for (i = 0; i <= maxcluster; i++) {
			u = fatcache[i] & FATMASK;
			ip = &cp[i*3/2];
			if (i & 1) {
				*ip &= ~(FATMASK << 4);
				*ip |= u << 4;
			} else {
				*ip &= ~FATMASK;
				*ip |= u;
			}
		}
	}
}

/*
 * Read size sectors starting at block n into buf.
 * Failure is fatal.
 */
void
diskread(buf, n, size, msg) char *buf; unsigned long n; short size; char *msg;
{
	diskseek(n);
	while (size-- > 0) {
		if (read(fsfd, buf, ssize) != ssize)
			fatal("%s read error", msg);
		buf += ssize;
	}
}

/*
 * Seek to the specified sector on the disk.
 */
void
diskseek(n) unsigned long n;
{
	if (lseek(fsfd, partseek + n * ssize, 0) == -1L)
		fatal("seek failed 0x%lx", n * ssize);
}

/*
 * Write size sectors starting at block n from buf.
 * Failure is fatal.
 */
void
diskwrite(buf, n, size, msg) char *buf; unsigned long n; short size; char *msg;
{
	if (cflag)
		return;
	diskseek(n);
	while (size--) {
		if (write(fsfd, buf, ssize) != ssize)
			fatal("%s write error", msg);
		buf += ssize;
	}
}

/*
 * Cry and die.
 * Uses the nonportable "%r" format.
 */
void
fatal(x) char *x;
{
	fflush(stdout);
	fprintf(stderr, "%s: %r\n", argv0, &x);
	rm_lock();
	exit(1);
}

void
fatal1(x) char *x;
{
	fflush(stdout);
	fprintf(stderr, "%s: %r\n", argv0, &x);
	exit(1);
}

/*
 * Flush the FAT cache.
 */
void
fatcflush()
{
	register unsigned short i, n;

	if (fatcflag)
		for (n = fatbase + fatcfirst, i = 1;
		     i <= bpb->b_fats;
		     n += fatsize, i++)
			diskwrite(fatcache, (long)n, fatccount, "FAT cache");
	fatcflag = 0;
}

/*
 * Read segment of FAT including cluster n into the FAT cache.
 */
void
fatcread(n) register unsigned short n;
{
	fatcflush();				/* flush previous contents */
	fatcflag = 0;				/* clear dirty flag */
	fatcfirst = (n/FATCCOUNT) * FATCSECS;	/* first FAT sector to read */
	fatccount = FATCSECS;			/* sectors to read */
	if (fatcfirst + fatccount > fatsize)
		fatccount = fatsize - fatcfirst;	/* read less at end */
	fatcmin = fatcfirst * FATCNPSEC;	/* min cluster in cache */
	fatcmax = (fatcfirst + fatccount) * FATCNPSEC - 1;	/* max cl */
/*
printf("n=%d,fatbase=%d,fatcfirst=%d,(long)(fatbase+fatcfirst)=%d,fatccount=%d\n",
n,fatbase,fatcfirst,(long)(fatbase + fatcfirst),fatccount);
*/
	diskread(fatcache, (long)(fatbase + fatcfirst),fatccount,"FAT cache");
}

/*
 * Get the FAT entry for cluster n.
 */
unsigned short
getcluster(n) register unsigned short n;
{
	if (n < 2 || n > maxcluster)
		fatal("getcluster: bad cluster number %u", n);
	if (n < fatcmin || n > fatcmax)
		fatcread(n);
	return fatcache[n - fatcmin];
}

/*
 * Convert up to n characters from src (an MS-DOS filename or extension)
 * to dst (a COHERENT filename buffer),
 * mapping to lower case and stopping at ' '.
 * Return a pointer to the NUL terminator.
 */
char *
lcname(dst, src, n) unsigned char *dst, *src; register short n;
{
	register short c;

	while (n--) {
		if ((c = *src++) == ' ')
			break;
		else if (isupper(c))
			c = tolower(c);
		*dst++ = c;
	}
	*dst = '\0';
	return dst;
}

/*
 * Put val in the FAT entry for cluster n.
 */
void
putcluster(n, val) unsigned short n; register short val;
{
	if (n < 2 || n > maxcluster)
		fatal("putcluster: bad cluster number %u", n);
	if (n < fatcmin || n > fatcmax)
		fatcread(n);
	fatcache[n - fatcmin] = val;
	fatcflag = 1;
}

/*
 * Read the file allocation table.
 * Try the rational approach:
 * read the boot block and use the boot block BPB info.
 * This being MS-DOS, the rational approach sometimes loses,
 * because the BPB information is not always there;
 * try e.g. DOS 3.2 "format a: /8".
 * Hence, the somewhat backhanded approach below.
 */
void
readfat()
{
	register unsigned short i;
	unsigned char id[3];
	char *s;

	if (xpart != 0)
		xpartition();

	/* Read the boot block and hope its BPB is rational. */
	lseek(fsfd, partseek, 0);
	if (read(fsfd, bootb, BBSIZE) != BBSIZE)
		fatal("boot block read error");
	bpb = &bootb[BPBOFF];
	if (is_media_id(bpb->b_media))
		s = "Found";
	else {
		/*
		 * The boot block BPB is missing,
		 * check for media id at start of block 1.
		 */
		lseek(fsfd, (long)BBSIZE, 0);
		if (read(fsfd, id, 3) != 3)
			fatal("media id read error");
		i = id[0];
		if ((i!=0xFC && i!=0xFD && i!=0xFE && i!=0xFF)
		  || id[1]!=0xFF || id[2]==0xFF)
			fatal("Probably not a DOS disk (media descriptor 0x%02x)", id[0]);
		/* Known diskette format. */
		s = "Known";
		switch (i) {
		case 0xFC:	bpb = &s9floppy;	break;
		case 0xFD:	bpb = &d9floppy;	break;
		case 0xFE:	bpb = &s8floppy;	break;
		case 0xFF:	bpb = &d8floppy;	break;
		}
	}
	setglobals();
#if	DEBUG
	printf("%s BPB: %u %u %u %u %u %u 0x%x %u %u %u %u\n",
		s,
		ssize,
		clsize,
		fatbase,
		bpb->b_fats,
		bpb->b_files,
		sectors,
		bpb->b_media,
		fatsize,
		nspt,
		heads,
		bpb->b_hidden);
#endif
	/*
	 * Allocate space for a FAT cache.
	 * If the FAT contains 1.5-byte entries, the cache must be big
	 * enough for the entire FAT table.
	 * Read start of FAT for sanity check below.
	 */
	if ((fatcache = malloc(FATCSECS * ssize)) == NULL)
		fatal("FAT allocation failed");
	if (fatbytes == 1) {		/* 1.5-byte FAT entries */
		if (fatsize > FATCSECS)
			fatal("FAT cache botch fatsize=%d", fatsize);
		fatcread(0);
		fatcmax = (fatsize * ssize * 2 / 3) - 1;
		if (fatcmax < maxcluster)
			fatal("FAT cache botch max=%d", fatcmax);
		decodefat(0);		/* expand entries to two bytes each */
	} else if (fatbytes == 2)	/* 2-byte FAT entries */
		fatcread(0);
	else				/* 4-byte FAT entries, presumably */
		fatal("fatbytes=%u", fatbytes);

	/* Sanity check. */
	if (fatcache[0] != (0xFF00 | bpb->b_media) || fatcache[1] != 0xFFFF)
		fatal("bad MS-DOS diskette format 0x%x 0x%x", fatcache[0], fatcache[1]);
}

/*
 * Set globals based on values in BPB.
 * This is mostly for efficiency.
 */
void
setglobals()
{
	ssize = bpb->b_ssize;
	clsize = bpb->b_clsize;
	fatbase = bpb->b_reserved;
	sectors = bpb->b_sectors;
	fatsize = bpb->b_fatsize;
	nspt = bpb->b_tracks;
	heads = bpb->b_heads;

	/* This program is untested for sector size != 512. */
	/* It might work, it might not; let's be prudent. */
	if (ssize != BBSIZE)
		fatal("ssize=%u BBSIZE=%u", ssize, BBSIZE);

	/* Compute base file sector, number of clusters, etc. */
	dirbase = fatbase + fatsize * bpb->b_fats;
	dirsize = (bpb->b_files * sizeof(MDIR) + ssize - 1) / ssize;
	filebase = dirbase + dirsize;
	mdirsize = clsize * ssize / sizeof(MDIR);

	if (sectors == 0) {
		fatbytes = 2;
		maxcluster=1+(unsigned short)((bpb->b_bigsectors-filebase)/clsize);
	}
	else {
		fatbytes = (sectors / clsize > (FATMASK & CLMAX)) ? 2 : 1;
		maxcluster = 1 + (sectors - filebase) / clsize;
	}

	dbprintf(("dirbase=%u dirsize=%u fatbytes=%u filebase=%u maxcluster=%u mdirsize=%u\n", dirbase, dirsize, fatbytes, filebase, maxcluster, mdirsize));
}

/*
 * Print usage message and die.
 */
void
usage(msg) char *msg;
{
	fprintf(stderr, usagemsg);
	if (msg)
		fatal(msg);
	else
		exit(1);
}

/*
 * Write the file allocation table.
 * If fatbytes==1, the FAT is compressed into 1.5-byte entries;
 * it is not directly usable after compression.
 */
void
writefat()
{
	if (fatbytes == 1)
		decodefat(1);
	fatcflush();
}

/*
 * If the desired MS-DOS filesystem is an extended MS-DOS partition,
 * find the seek into the COHERENT partition
 * which specifies the base of the actual MS-DOS filessytem.
 */
void
xpartition()
{
	register short part;
	HDISK_S hd;
	FDISK_S *p1, *p2;

	partseek = 0L;
	for (part = 1; part <= xpart; ++part) {
		if (lseek(fsfd, partseek, 0) == -1L)
			fatal("extended partition lseek failed part=%d seek=%lx",
				part, partseek);
		if (read(fsfd, &hd, sizeof hd) != sizeof hd)
			fatal("extended partition table %d read failed", part);
		if (hd.hd_sig != HDSIG)
			fatal("no signature found on extended partition table %d",
				part);
		p1 = &(hd.hd_partn[0]);
		p2 = &(hd.hd_partn[1]);
		if (part == xpart) {
			partseek += p1->p_base * BBSIZE;
			break;
		} else if (p2->p_sys == SYS_DOS_XP)
			partseek = p2->p_base * BBSIZE;
		else
			fatal("extended MS-DOS partition does not contain %d drives",
				xpart);
	}
}

/* end of dos1.c */


unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.