Index: lib/libstand/dosfs.h =================================================================== --- lib/libstand/dosfs.h +++ lib/libstand/dosfs.h @@ -96,6 +96,8 @@ typedef struct { struct open_file *fd; /* file descriptor */ + u_char *fatbuf; /* FAT cache buffer */ + u_int fatbuf_blknum; /* number of 128K block in FAT cache buffer */ u_int links; /* active links to structure */ u_int spc; /* sectors per cluster */ u_int bsize; /* cluster size in bytes */ Index: lib/libstand/dosfs.c =================================================================== --- lib/libstand/dosfs.c +++ lib/libstand/dosfs.c @@ -65,6 +65,7 @@ #define DEPSEC 16 /* directory entries per sector */ #define DSHIFT 4 /* DEPSEC shift */ #define LOCLUS 2 /* lowest cluster number */ +#define FATBLKSZ 0x20000 /* size of block in the FAT cache buffer */ /* DOS "BIOS Parameter Block" */ typedef struct { @@ -132,18 +133,6 @@ ((u_int)cv2((de)->dex.h_clus) << 16) | \ cv2((de)->clus)) -/* - * fat cache metadata - */ -struct fatcache { - int unit; /* disk unit number */ - int size; /* buffer (and fat) size in sectors */ - u_char *buf; -}; - -static struct fatcache fat; - -static int dosunmount(DOS_FS *); static int parsebs(DOS_FS *, DOS_BS *); static int namede(DOS_FS *, const char *, DOS_DE **); static int lookup(DOS_FS *, u_int, const char *, DOS_DE **); @@ -156,33 +145,34 @@ static int ioread(DOS_FS *, u_int, void *, u_int); static int ioget(struct open_file *, daddr_t, void *, u_int); -static void -dos_read_fat(DOS_FS *fs, struct open_file *fd) +static int +dos_read_fatblk(DOS_FS *fs, struct open_file *fd, u_int blknum) { - struct devdesc *dd = fd->f_devdata; + int err; + u_int io_size; + daddr_t offset_in_fat, max_offset_in_fat; - if (fat.buf != NULL) { /* can we reuse old buffer? */ - if (fat.size != fs->spf) { - free(fat.buf); /* no, free old buffer */ - fat.buf = NULL; - } + offset_in_fat = ((daddr_t)blknum) * FATBLKSZ; + max_offset_in_fat = secbyt(fs->spf); + io_size = FATBLKSZ; + if (offset_in_fat > max_offset_in_fat) + offset_in_fat = max_offset_in_fat; + if (offset_in_fat + io_size > max_offset_in_fat) + io_size = ((u_int)(max_offset_in_fat - offset_in_fat)); + + if (io_size != 0) { + err = ioget(fd, fs->lsnfat + bytsec(offset_in_fat), + fs->fatbuf, io_size); + if (err != 0) { + fs->fatbuf_blknum = ((u_int)(-1)); + return (err); + } } + if (io_size < FATBLKSZ) + memset(fs->fatbuf + io_size, 0, FATBLKSZ - io_size); - if (fat.buf == NULL) - fat.buf = malloc(secbyt(fs->spf)); - - if (fat.buf != NULL) { - if (ioget(fd, fs->lsnfat, fat.buf, secbyt(fs->spf)) == 0) { - fat.size = fs->spf; - fat.unit = dd->d_unit; - return; - } - } - if (fat.buf != NULL) /* got IO error */ - free(fat.buf); - fat.buf = NULL; - fat.unit = -1; /* impossible unit */ - fat.size = 0; + fs->fatbuf_blknum = blknum; + return (0); } /* @@ -192,24 +182,35 @@ dos_mount(DOS_FS *fs, struct open_file *fd) { int err; - struct devdesc *dd = fd->f_devdata; u_char *buf; bzero(fs, sizeof(DOS_FS)); fs->fd = fd; - if ((err = !(buf = malloc(secbyt(1))) ? errno : 0) || - (err = ioget(fs->fd, 0, buf, secbyt(1))) || + if ((buf = malloc(secbyt(1))) == NULL) { + err = errno; + free(fs); + return (err); + } + if ((err = ioget(fs->fd, 0, buf, secbyt(1))) || (err = parsebs(fs, (DOS_BS *)buf))) { - if (buf != NULL) - free(buf); - (void)dosunmount(fs); + free(buf); + free(fs); return (err); } free(buf); - if (fat.buf == NULL || fat.unit != dd->d_unit) - dos_read_fat(fs, fd); + if ((fs->fatbuf = malloc(FATBLKSZ)) == NULL) { + err = errno; + free(fs); + return (err); + } + err = dos_read_fatblk(fs, fd, 0); + if (err != 0) { + free(fs->fatbuf); + free(fs); + return (err); + } fs->root = dot[0]; fs->root.name[0] = ' '; @@ -228,21 +229,9 @@ static int dos_unmount(DOS_FS *fs) { - int err; - if (fs->links) return (EBUSY); - if ((err = dosunmount(fs))) - return (err); - return (0); -} - -/* - * Common code shared by dos_mount() and dos_unmount() - */ -static int -dosunmount(DOS_FS *fs) -{ + free(fs->fatbuf); free(fs); return (0); } @@ -761,31 +750,30 @@ } /* - * Get next cluster in cluster chain. Use in core fat cache unless another - * device replaced it. + * Get next cluster in cluster chain. Use in core fat cache unless + * the number of current 128K block in FAT has changed. */ static int fatget(DOS_FS *fs, u_int *c) { u_char buf[4]; - u_int x, offset, n, nbyte; - struct devdesc *dd = fs->fd->f_devdata; - int err = 0; - - if (fat.unit != dd->d_unit) { - /* fat cache was changed to another device, don't use it */ - err = ioread(fs, secbyt(fs->lsnfat) + fatoff(fs->fatsz, *c), buf, - fs->fatsz != 32 ? 2 : 4); - if (err) - return (err); - } else { - offset = fatoff(fs->fatsz, *c); - nbyte = fs->fatsz != 32 ? 2 : 4; + u_int x, offset, blknum, nbyte; + int err; - if (offset + nbyte > secbyt(fat.size)) - return (EINVAL); - memcpy(buf, fat.buf + offset, nbyte); + offset = fatoff(fs->fatsz, *c); + nbyte = fs->fatsz != 32 ? 2 : 4; + if (offset + nbyte > secbyt(fs->spf)) + return (EINVAL); + blknum = offset / FATBLKSZ; + offset %= FATBLKSZ; + if (offset + nbyte > FATBLKSZ) + return (EINVAL); + if (blknum != fs->fatbuf_blknum) { + err = dos_read_fatblk(fs, fs->fd, blknum); + if (err != 0) + return (err); } + memcpy(buf, fs->fatbuf + offset, nbyte); x = fs->fatsz != 32 ? cv2(buf) : cv4(buf); *c = fs->fatsz == 12 ? *c & 1 ? x >> 4 : x & 0xfff : x;