1
0
Files
irix-657m-src/eoe/cmd/mountdos/dos_fs.c
2022-09-29 17:59:04 +03:00

657 lines
18 KiB
C

/*
*-----------------------------------------------------------------------------
* DOS filesystem-level routines.
*-----------------------------------------------------------------------------
*/
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/smfd.h>
#include <sys/stat.h>
#include "dos_fs.h"
#include <sys/major.h>
#include <sys/dvh.h>
#include <sys/dkio.h>
#include <sys/conf.h>
#include <invent.h>
#include <sys/iograph.h>
#include <string.h>
#define SOFTPC_DISK 0xff00
static dfs_t *dfs_list;
dbufp_t trkcache;
/*
* NOTE: bp_sectorcount is a u_short. Type 6 partitions have sector
* counts that are 4 bytes long and have to be accomodated elsewhere.
* I'm storing them in "sectorcount", which is a global.
*/
u_int sectorcount = 0; /* sector count for partition */
u_long partsize = 0; /* Size of partition */
u_long partbgnaddr = 0; /* Beginning address of partition */
u_long partendaddr = 0; /* Ending address of partition */
u_short fat_entry_size = 0; /* Fat entry size: 12/16 */
extern int remmedinfo( int fd, int *status ); /* declared in libdisk */
dfs_t *
dos_getfs(dev_t dev)
{
dfs_t *dfs;
int status;
char devname[MAXDEVNAME];
int devnmlen = MAXDEVNAME;
for (dfs = dfs_list; dfs != NULL; dfs = dfs->dfs_next) {
if (dfs->dfs_dev == dev) {
if (dfs->dfs_dev == SOFTPC_DISK)
return dfs;
if(dev_to_drivername(dfs->dfs_dev,
devname,
&devnmlen))
if(strncmp(devname, "dksc", 4) == 0)
return dfs;
if ((ioctl(dfs->dfs_fd, SMFDMEDIA, &status) &&
remmedinfo( dfs->dfs_fd, &status))
|| status == 0) { /* no media */
dfs->dfs_flags |= DFS_STALE;
return NULL;
}
if (status & SMFDMEDIA_CHANGED
|| dfs->dfs_flags & DFS_STALE) {
/* New media -- kill this filesystem off. */
dos_removefs(dfs);
return NULL;
}
return dfs;
}
}
return NULL;
}
static void
dos_destroyfs(dfs_t *dfs)
{
close(dfs->dfs_fd);
if (dfs->dfs_fat)
free(dfs->dfs_fat);
if (dfs->dfs_root)
dos_putnode(dfs->dfs_root);
if (dfs->dfs_buf)
free(dfs->dfs_buf);
if (dfs->dfs_zerobuf)
free(dfs->dfs_zerobuf);
free(dfs);
}
int
dos_addfs(char *pathname, u_int flags, u_int partition, dfs_t **dfsp)
{
dfs_t *dfs;
int error;
int fd;
int part;
struct stat sb;
u_int status;
u_int capacity;
struct volume_header vh;
struct partition_table *pt;
if ((fd = open(pathname, O_RDWR | O_EXCL)) < 0)
return errno;
if (fstat(fd, &sb) < 0) {
close(fd);
return errno;
}
if (ioctl(fd, DIOCGETVH, &vh) == 0 && valid_vh(&vh)){
char devname[MAXDEVNAME];
char *pattern = EDGE_LBL_VOLUME"/"EDGE_LBL_CHAR;
int len1 = strlen(pattern),len2 = MAXDEVNAME;
char *str;
/* get the canonical name of this device */
dev_to_devname(sb.st_rdev,devname,&len2);
len2 = strlen(devname);
str = devname + (len2 - len1);
/* check if the volume partition is being used */
if (strcmp(pattern,str) == 0) {
part = 10; /* We have a volume partition */
pt = &vh.vh_pt[part];
}
else
part = -1;
/*
* only allow access to the whole-volume device.
*/
if (part != 10) {
close(fd);
return ENODEV;
}
if (ioctl(fd, DIOCREADCAPACITY, &capacity) != 0) {
perror("Error reading capacity");
close(fd);
return errno;
}
bzero(&vh, sizeof vh);
vh.vh_magic = VHMAGIC;
vh.vh_dp.dp_drivecap = capacity;
vh.vh_dp.dp_secbytes = SECTOR_SIZE;
pt->pt_nblks = vh.vh_dp.dp_drivecap;
pt->pt_firstlbn = 0;
pt->pt_type = PTYPE_RAW;
vh.vh_csum = -vhchksum(&vh);
(void) ioctl(fd, DIOCSETVH, &vh);
}
dfs = (dfs_t *)safe_malloc(sizeof *dfs);
bzero(dfs, sizeof *dfs);
dfs->dfs_pathname = pathname;
dfs->dfs_fd = fd;
dfs->dfs_dev = S_ISREG(sb.st_mode) ? SOFTPC_DISK : sb.st_rdev;
dfs->dfs_flags = flags;
if (error = dos_getvh(dfs, partition)) {
dos_destroyfs(dfs);
return error;
}
dfs->dfs_next = dfs_list;
dfs_list = dfs;
*dfsp = dfs;
return 0;
}
void
dos_removefs(dfs_t *dfs)
{
dfs_t **dfsp;
for (dfsp = &dfs_list; *dfsp != NULL; dfsp = &(*dfsp)->dfs_next) {
if (*dfsp == dfs) {
*dfsp = dfs->dfs_next;
dos_putnode(dfs->dfs_root);
dfs->dfs_root = NULL;
dos_purgenodes(dfs);
dos_destroyfs(dfs);
return;
}
}
panic("dos_removefs: fs %s not found", dfs->dfs_pathname);
}
int
dos_statfs(dfs_t *dfs, statfsokres *sfr)
{
sfr->tsize = NFS_MAXDATA; /* max transfer size */
sfr->bsize = dfs->dfs_clustersize; /* block size */
sfr->blocks = dfs->dfs_clustercount; /* blocks/disk */
sfr->bfree = dfs->dfs_freeclustercount; /* blocks free */
sfr->bavail = sfr->bfree; /* blocks available */
return 0;
}
int dos_getvh(dfs_t *dfs, u_int partition)
{
int i, error;
u_int pd_offset;
u_int filesarea_sectors;
u_char partition_type;
bp_t *bpp;
u_int bp_buf[SECTOR_SIZE / sizeof(u_int)];
u_char *bs = (u_char *)bp_buf;
u_int msdos_boot_version;
int rootdir_sectors;
int numclusters_by_volspace;
lseek(dfs->dfs_fd, 0, 0);
if (partition) {
u_long parttbladdr = 0;
if (partition > 4){
error = dos_find_extended(dfs, partition);
if (error){
fprintf(stderr, " Invalid logical partn\n");
return (ENXIO);
}
parttbladdr = lseek(dfs->dfs_fd, 0, SEEK_CUR);
}
if (read(dfs->dfs_fd, bs, sizeof(bp_buf)) < 0){
fprintf(stderr, " read() fail!\n");
return errno;
}
if (!MAGIC_PART(bs)){
fprintf(stderr, " Bad Magic: Partition table\n");
return (EINVAL);
}
if (partition > 4)
pd_offset = PD_OFFSET;
else pd_offset = PD_OFFSET+(partition-1)*PD_SIZE;
partition_type = bs[pd_offset + 4];
switch (partition_type){
case PARTITION_NOTUSED:
fprintf(stderr, " Partition %d is unused\n", partition);
return (ENXIO);
case PARTITION_FAT12:
fat_entry_size = 12;
break;
case PARTITION_FAT16:
case PARTITION_HUGE: /* well, since we only support FAT16 */
fat_entry_size = 16;
break;
case PARTITION_EXTENDED:
fprintf(stderr, " Cannot mount extended partn\n");
fprintf(stderr, " Please mount logical partn\n");
return (ENXIO);
default:
fprintf(stderr, " Unknown partition type\n");
return (ENXIO);
}
if (partition > 4)
partbgnaddr = parttbladdr+PARTN1_OFFSET(bs);
else partbgnaddr = PARTN_OFFSET(bs, partition);
lseek(dfs->dfs_fd, partbgnaddr, SEEK_SET);
}
else {
partbgnaddr = 0;
partition_type = PARTITION_NOTUSED;
}
/*
* OK, now read the boot sector parameter block, from wherever
* we found it, ie, in the master location or at the start of
* one of the partitions if one was specified.
*/
if (read(dfs->dfs_fd, bs, sizeof(bp_buf)) < 0){
fprintf(stderr, " read of boot sector() fail!\n");
return errno;
}
if (!MAGIC_DOS(bs)){
fprintf(stderr, " Bad Magic: DOS Boot Sector\n");
return (EINVAL);
}
bpp = &dfs->dfs_bp;
bpp->bp_magic[0] = bs[VH_MAGIC0];
bpp->bp_magic[1] = bs[VH_MAGIC1];
bpp->bp_magic[2] = bs[VH_MAGIC2];
bpp->bp_oem[0] = bs[VH_OEM0];
bpp->bp_oem[1] = bs[VH_OEM1];
bpp->bp_oem[2] = bs[VH_OEM2];
bpp->bp_oem[3] = bs[VH_OEM3];
bpp->bp_oem[4] = bs[VH_OEM4];
bpp->bp_oem[5] = bs[VH_OEM5];
bpp->bp_oem[6] = bs[VH_OEM6];
bpp->bp_oem[7] = bs[VH_OEM7];
/*
* Determine MSDOS boot sector version number right now.
* Availability of certain fields are dependent.
*/
for (i = 7; i && bpp->bp_oem[i] != '.'; i--)
;
msdos_boot_version = (isdigit(bpp->bp_oem[--i])) ? bpp->bp_oem[i] - '0' : 0;
sectorcount = SECTOR_COUNT(bs); /* good in helping determine version */
if (!msdos_boot_version) {
/*
* Well, not too unexpected, let's look for other things
* that might help us determine what the heck it is.
*/
if (!sectorcount && SECTOR_COUNT_EXTD(bs)) {
msdos_boot_version = 4;
}
if (!msdos_boot_version && MAGIC_EXTENDED_BOOT(bs)) {
msdos_boot_version = 4;
}
}
bpp->bp_sectorsize = (bs[VH_SSHI] << 8) | bs[VH_SSLO];
if (bpp->bp_sectorsize && bpp->bp_sectorsize != SECTOR_SIZE) {
fprintf(stderr, " Unsupported sector size %d\n", bpp->bp_sectorsize);
return (EINVAL);
}
else {
bpp->bp_sectorsize = SECTOR_SIZE;
}
bpp->bp_sectorspercluster = (bs[VH_CS]);
bpp->bp_reservecount = (bs[VH_RCHI] << 8) | bs[VH_RCLO];
bpp->bp_fatcount = (bs[VH_FC]);
bpp->bp_rootentries = (bs[VH_REHI] << 8) | bs[VH_RELO];
if (partition_type == 6 || (msdos_boot_version >= 4 && !sectorcount)) {
sectorcount = SECTOR_COUNT_EXTD(bs);
}
partsize = sectorcount * bpp->bp_sectorsize;
partendaddr = partbgnaddr + partsize;
bpp->bp_mediatype = (bs[VH_MT]);
bpp->bp_fatsize = (bs[VH_FSHI] << 8) | bs[VH_FSLO];
bpp->bp_sectorspertrack = (bs[VH_STHI] << 8) | bs[VH_STLO];
bpp->bp_headcount = (bs[VH_HCHI] << 8) | bs[VH_HCLO];
bpp->bp_hiddensectors = (bs[VH_HSLHI] << 8) | (bs[VH_HSLLO]);
if (msdos_boot_version >= 4) {
bpp->bp_hiddensectors |= (bs[VH_HSHHI] << 24) | (bs[VH_HSHLO] << 16);
}
if (partition) {
/*
* If we've found this boot sector as a result of an indirect through
* the partition table/MBR, make sure that the media type code represents
* a fixed disk, which seems to be a requirement in the DOS world.
*
* Ie, a partition boot sector can't contain a boot sector ordinarily
* found as the boot sector on a floppy. Just a sanity check.
*/
if (bpp->bp_mediatype != DOS_MEDIA_FIXED_DISK) {
fprintf(stderr, " Invalid DOS Boot Sector in partition %d\n",
partition);
return (EINVAL);
}
}
rootdir_sectors = (bpp->bp_rootentries * DIR_ENTRY_SIZE + bpp->bp_sectorsize - 1)
/ bpp->bp_sectorsize;
filesarea_sectors = sectorcount - bpp->bp_reservecount - rootdir_sectors -
bpp->bp_fatsize * bpp->bp_fatcount;
/*
* Fill in values into the dfs
* structure about boot/fat/root
*/
dfs->dfs_fataddr = partbgnaddr + bpp->bp_reservecount * bpp->bp_sectorsize;
dfs->dfs_fatsize = bpp->bp_fatsize * bpp->bp_sectorsize;
dfs->dfs_rootaddr = dfs->dfs_fataddr + bpp->bp_fatcount * dfs->dfs_fatsize;
dfs->dfs_rootsize = bpp->bp_rootentries * DIR_ENTRY_SIZE;
dfs->dfs_fileaddr = dfs->dfs_rootaddr + dfs->dfs_rootsize;
dfs->dfs_clustersize = bpp->bp_sectorspercluster * bpp->bp_sectorsize;
#if 0
/*
* Debug printout
*/
printf(" mount_dos: dfs_fataddr = %d\n", dfs->dfs_fataddr);
printf(" mount_dos: dfs_rootaddr = %d\n", dfs->dfs_rootaddr);
printf(" mount_dos: dfs_fileaddr = %d\n", dfs->dfs_fileaddr);
printf(" mount_dos: dfs_clustersize= %d\n", dfs->dfs_clustersize);
#endif
/*
* Allocate space for and copy in FAT, use the
* secondary copies if necessary
*/
dfs->dfs_fat = safe_malloc(dfs->dfs_fatsize);
lseek(dfs->dfs_fd, dfs->dfs_fataddr, 0);
for (i = 0; i < bpp->bp_fatcount; i++) {
if (read(dfs->dfs_fd, dfs->dfs_fat, dfs->dfs_fatsize) > 0)
break;
if (i == bpp->bp_fatcount - 1)
return errno;
lseek(dfs->dfs_fd,
dfs->dfs_fataddr + dfs->dfs_fatsize * (i + 1), 0);
}
/* Add space for . and .. and get root dnode */
dfs->dfs_rootsize += DIR_ENTRY_SIZE * 2;
bpp->bp_rootentries += 2;
if (error = dos_getnode(dfs, ROOTFNO, &dfs->dfs_root))
return error;
/* Allocate space for scratch work */
dfs->dfs_buf = safe_malloc(dfs->dfs_clustersize);
dfs->dfs_zerobuf = safe_malloc(dfs->dfs_clustersize);
bzero(dfs->dfs_zerobuf, dfs->dfs_clustersize);
numclusters_by_volspace = filesarea_sectors / bpp->bp_sectorspercluster;
if (!fat_entry_size) {
fat_entry_size = (numclusters_by_volspace > 4087) ? 16 : 12;
}
/*
* Find out number of usable entries in FAT, the first 2 entries in
* the FAT are reserved. Determine the cluster count as well by looking
* at the FAT. Then compare it against the cluster count as determined by
* looking at the number of sectors in the files area in conjunction with
* the sectors per cluster. Take the best fit.
*/
dfs->dfs_clustercount = dfs->dfs_fatsize * 8 / fat_entry_size - 2;
if (dfs->dfs_clustercount > numclusters_by_volspace) {
dfs->dfs_clustercount = numclusters_by_volspace;
}
if (fat_entry_size == 12) {
dfs->dfs_readfat = readfat12;
dfs->dfs_writefat = writefat12;
dfs->dfs_endclustermark = 0xfff;
} else {
dfs->dfs_readfat = readfat16;
dfs->dfs_writefat = writefat16;
dfs->dfs_endclustermark = 0xffff;
}
/* Find out number of free clusters on disk */
dfs->dfs_freeclustercount = 0;
for (i = 2; i < dfs->dfs_clustercount + 2; i++) {
if ((*dfs->dfs_readfat)(dfs, i) == 0)
dfs->dfs_freeclustercount++;
}
/* Initialize the cache buffer parameters */
trkcache.clustersize = bpp->bp_sectorspercluster * bpp->bp_sectorsize;
if (fat_entry_size == 12){
trkcache.trksize = bpp->bp_headcount*bpp->bp_sectorsize*
bpp->bp_sectorspertrack;
}
else {
trkcache.trksize = bpp->bp_sectorspertrack * bpp->bp_sectorsize;
trkcache.trksize = (trkcache.trksize/dfs->dfs_clustersize)*
(dfs->dfs_clustersize);
if (!trkcache.trksize)
trkcache.trksize = 5*dfs->dfs_clustersize;
}
trkcache.clusterspertrk = trkcache.trksize/trkcache.clustersize;
trkcache.trkbgnaddr = 0;
trkcache.trkendaddr = 0;
trkcache.bufp = safe_malloc(trkcache.trksize);
trkcache.dirtymaskcnts = (trkcache.clusterspertrk+(MASKBYTESZ-1)) /
MASKBYTESZ;
trkcache.dirty = (u_long *)
safe_malloc(trkcache.dirtymaskcnts*MASKBYTES);
for (i = 0; i < trkcache.dirtymaskcnts; i++)
trkcache.dirty[i] = 0;
dfs->dirtyfat = FALSE;
dfs->dirtyrootdir = FALSE;
return 0;
}
/*
*----------------------------------------------------------------------------
* dos_find_extended()
* This routine scans a hard-drive and searches for a required logical
* partition and positions the file descriptor at the very beginning.
*----------------------------------------------------------------------------
*/
int dos_find_extended(dfs_t *dfs, u_int partition)
{
int partn;
u_char p_type;
u_long extaddr, nextaddr;
u_long parttbladdr;
u_char bs[SECTOR_SIZE];
lseek(dfs->dfs_fd, 0, SEEK_SET);
read(dfs->dfs_fd, bs, SECTOR_SIZE);
for (partn = 1; partn <= 4; partn++){
p_type = PARTN_TYPE(bs, partn);
if (p_type == EXTENDED)
break;
}
if (partn == 5)
return (1);
extaddr = PARTN_OFFSET(bs, partn);
partn = 5;
parttbladdr = extaddr;
lseek(dfs->dfs_fd, parttbladdr, SEEK_SET);
read(dfs->dfs_fd, bs, SECTOR_SIZE);
while (partn != partition){
nextaddr = PARTN2_OFFSET(bs);
if (nextaddr == 0)
goto error;
partn++;
parttbladdr = extaddr+nextaddr;
lseek(dfs->dfs_fd, parttbladdr, SEEK_SET);
read(dfs->dfs_fd, bs, SECTOR_SIZE);
}
if (partn == partition){
lseek(dfs->dfs_fd, parttbladdr, SEEK_SET);
return (0);
}
error:
return (1);
}
/*
* DOS filesystem node cache.
*/
typedef struct dlist {
dnode_t *d_forw; /* hash table linkage */
dnode_t *d_back;
} dlist_t;
#define INSERT_HASH(dp, hashlist) \
((dp)->d_forw = (hashlist)->d_forw, \
(dp)->d_back = (dnode_t *)(hashlist), \
(dp)->d_forw->d_back = (dp)->d_back->d_forw = (dp))
#define REMOVE_HASH(dp) \
((dp)->d_forw->d_back = (dp)->d_back, \
(dp)->d_back->d_forw = (dp)->d_forw, \
NULL_HASH(dp))
#define NULL_HASH(dp) \
((dp)->d_forw = (dp)->d_back = (dp))
typedef struct dfreelist {
dnode_t *d_forw; /* hash table linkage */
dnode_t *d_back;
dnode_t *d_avforw; /* freelist linkage */
dnode_t *d_avback;
} dfreelist_t;
#define INSERT_FREE(dp, freelist) \
((dp)->d_avforw = (freelist)->d_avforw, \
(dp)->d_avback = (dnode_t *)(freelist), \
(dp)->d_avforw->d_avback = (dp)->d_avback->d_avforw = (dp))
#define REMOVE_FREE(dp) \
((dp)->d_avforw->d_avback = (dp)->d_avback, \
(dp)->d_avback->d_avforw = (dp)->d_avforw, \
NULL_FREE(dp))
#define NULL_FREE(dp) \
((dp)->d_avforw = (dp)->d_avback = (dp))
#define DHASHLOG2 7
#define DHASHSIZE (1<<DHASHLOG2)
#define DHASH(fno) (&dhash[(fno) & DHASHSIZE-1])
static dlist_t dhash[DHASHSIZE];
static dfreelist_t dfreelist = {
(dnode_t *)&dfreelist, (dnode_t *)&dfreelist,
(dnode_t *)&dfreelist, (dnode_t *)&dfreelist
};
#define NUMDNODES 256
static dnode_t dnodes[NUMDNODES];
void
dos_initnodes()
{
dnode_t *dp;
int i;
for (i = 0; i < DHASHSIZE; i++) {
dp = (dnode_t *) &dhash[i];
NULL_HASH(dp);
}
for (i = 0; i < NUMDNODES; i++) {
dp = &dnodes[i];
INSERT_FREE(dp, &dfreelist);
NULL_HASH(dp);
}
}
void
dos_purgenodes(dfs_t *dfs)
{
dnode_t *dp;
int i;
for (i = NUMDNODES, dp = &dnodes[0]; --i >= 0; dp++) {
if (dp->d_dfs == dfs)
dos_purgenode(dp);
}
}
void
dos_purgenode(dnode_t *dp)
{
if (dp->d_count > 1)
panic("dos_purgenode: multiple reference");
REMOVE_HASH(dp);
if (dp->d_count == 1) {
dp->d_count = 0;
INSERT_FREE(dp, &dfreelist);
}
if (dp->d_dir != NULL)
vfat_dir_write(dp);
dp->d_dfs = NULL;
dp->d_fno = 0;
}
int
dos_getnode(dfs_t *dfs, u_long fno, dnode_t **dpp)
{
dlist_t *dl;
dnode_t *dp;
int error;
dl = DHASH(fno);
for (dp = dl->d_forw; dp != (dnode_t *) dl; dp = dp->d_forw) {
if (dp->d_dfs == dfs && dp->d_fno == fno) {
dp->d_count++;
REMOVE_FREE(dp);
*dpp = dp;
return 0;
}
}
dp = dfreelist.d_avforw;
if (dp == (dnode_t *) &dfreelist)
panic("dos_getnode: out of dnodes, stuck references");
REMOVE_HASH(dp);
REMOVE_FREE(dp);
dp->d_dfs = dfs;
dp->d_fno = fno;
if (error = dos_readnode(dp)) {
INSERT_FREE(dp, &dfreelist);
return error;
}
INSERT_HASH(dp, dl);
dp->d_count = 1;
*dpp = dp;
return 0;
}
void
dos_putnode(dnode_t *dp)
{
--dp->d_count;
if (dp->d_count < 0) {
panic("dos_putnode: fs %s fno %lx reference underflow",
dp->d_dfs->dfs_pathname, dp->d_fno);
}
if (dp->d_count == 0) {
INSERT_FREE(dp, dfreelist.d_avback);
}
}