summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorToomas Soome <tsoome@FreeBSD.org>2017-04-06 20:12:39 +0000
committerToomas Soome <tsoome@FreeBSD.org>2017-04-06 20:12:39 +0000
commit3949295f36b10ee548839335040ab9b3786d0383 (patch)
tree1fcf8ca6d75f65e43acbcbde037eda53d4fa4f6e
parenta3a4eaf6fec1ff48d9efca88ae48af9f149edd79 (diff)
Notes
-rw-r--r--lib/libstand/dosfs.c210
-rw-r--r--lib/libstand/dosfs.h2
2 files changed, 116 insertions, 96 deletions
diff --git a/lib/libstand/dosfs.c b/lib/libstand/dosfs.c
index 6cf50b8ba2c6..68762842eaee 100644
--- a/lib/libstand/dosfs.c
+++ b/lib/libstand/dosfs.c
@@ -65,6 +65,7 @@ struct fs_ops dosfs_fsops = {
#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 @@ static DOS_DE dot[2] = {
((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 **);
@@ -153,36 +142,37 @@ static off_t fsize(DOS_FS *, DOS_DE *);
static int fatcnt(DOS_FS *, u_int);
static int fatget(DOS_FS *, u_int *);
static int fatend(u_int, u_int);
-static int ioread(DOS_FS *, u_int, void *, u_int);
-static int ioget(struct open_file *, daddr_t, void *, u_int);
+static int ioread(DOS_FS *, u_int, void *, size_t);
+static int ioget(struct open_file *, daddr_t, void *, size_t);
-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;
-
- if (fat.buf != NULL) { /* can we reuse old buffer? */
- if (fat.size != fs->spf) {
- free(fat.buf); /* no, free old buffer */
- fat.buf = NULL;
- }
+ int err;
+ size_t io_size;
+ daddr_t offset_in_fat, max_offset_in_fat;
+
+ 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 = ((size_t)(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,27 @@ static int
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)
+ return (errno);
+ 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);
return (err);
}
free(buf);
- if (fat.buf == NULL || fat.unit != dd->d_unit)
- dos_read_fat(fs, fd);
+ if ((fs->fatbuf = malloc(FATBLKSZ)) == NULL)
+ return (errno);
+ err = dos_read_fatblk(fs, fd, 0);
+ if (err != 0) {
+ free(fs->fatbuf);
+ return (err);
+ }
fs->root = dot[0];
fs->root.name[0] = ' ';
@@ -228,21 +221,9 @@ dos_mount(DOS_FS *fs, struct open_file *fd)
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);
}
@@ -257,16 +238,20 @@ dos_open(const char *path, struct open_file *fd)
DOS_FILE *f;
DOS_FS *fs;
u_int size, clus;
- int err = 0;
+ int err;
/* Allocate mount structure, associate with open */
- fs = malloc(sizeof(DOS_FS));
-
- if ((err = dos_mount(fs, fd)))
- goto out;
+ if ((fs = malloc(sizeof(DOS_FS))) == NULL)
+ return (errno);
+ if ((err = dos_mount(fs, fd))) {
+ free(fs);
+ return (err);
+ }
- if ((err = namede(fs, path, &de)))
- goto out;
+ if ((err = namede(fs, path, &de))) {
+ dos_unmount(fs);
+ return (err);
+ }
clus = stclus(fs->fatsz, de);
size = cv4(de->size);
@@ -274,18 +259,20 @@ dos_open(const char *path, struct open_file *fd)
if ((!(de->attr & FA_DIR) && (!clus != !size)) ||
((de->attr & FA_DIR) && size) ||
(clus && !okclus(fs, clus))) {
- err = EINVAL;
- goto out;
+ dos_unmount(fs);
+ return (EINVAL);
+ }
+ if ((f = malloc(sizeof(DOS_FILE))) == NULL) {
+ err = errno;
+ dos_unmount(fs);
+ return (err);
}
- f = malloc(sizeof(DOS_FILE));
bzero(f, sizeof(DOS_FILE));
f->fs = fs;
fs->links++;
f->de = *de;
fd->f_fsdata = (void *)f;
-
- out:
- return (err);
+ return (0);
}
/*
@@ -761,34 +748,57 @@ fatcnt(DOS_FS *fs, u_int c)
}
/*
- * 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;
+ u_int val_in, val_out, offset, blknum, nbyte;
+ const u_char *p_entry;
+ int err;
- 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;
+ /* check input value to prevent overflow in fatoff() */
+ val_in = *c;
+ if (val_in & 0xf0000000)
+ return (EINVAL);
- if (offset + nbyte > secbyt(fat.size))
- return (EINVAL);
- memcpy(buf, fat.buf + offset, nbyte);
+ /* ensure that current 128K FAT block is cached */
+ offset = fatoff(fs->fatsz, val_in);
+ 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);
}
+ p_entry = fs->fatbuf + offset;
- x = fs->fatsz != 32 ? cv2(buf) : cv4(buf);
- *c = fs->fatsz == 12 ? *c & 1 ? x >> 4 : x & 0xfff : x;
+ /* extract cluster number from FAT entry */
+ switch (fs->fatsz) {
+ case 32:
+ val_out = cv4(p_entry);
+ val_out &= 0x0fffffff;
+ break;
+ case 16:
+ val_out = cv2(p_entry);
+ break;
+ case 12:
+ val_out = cv2(p_entry);
+ if (val_in & 1)
+ val_out >>= 4;
+ else
+ val_out &= 0xfff;
+ break;
+ default:
+ return (EINVAL);
+ }
+ *c = val_out;
return (0);
}
@@ -805,7 +815,7 @@ fatend(u_int sz, u_int c)
* Offset-based I/O primitive
*/
static int
-ioread(DOS_FS *fs, u_int offset, void *buf, u_int nbyte)
+ioread(DOS_FS *fs, u_int offset, void *buf, size_t nbyte)
{
char *s;
u_int off, n;
@@ -843,8 +853,16 @@ ioread(DOS_FS *fs, u_int offset, void *buf, u_int nbyte)
* Sector-based I/O primitive
*/
static int
-ioget(struct open_file *fd, daddr_t lsec, void *buf, u_int size)
+ioget(struct open_file *fd, daddr_t lsec, void *buf, size_t size)
{
- return ((fd->f_dev->dv_strategy)(fd->f_devdata, F_READ, lsec,
- size, buf, NULL));
+ size_t rsize;
+ int rv;
+
+ /* Make sure we get full read or error. */
+ rsize = 0;
+ rv = (fd->f_dev->dv_strategy)(fd->f_devdata, F_READ, lsec,
+ size, buf, &rsize);
+ if ((rv == 0) && (size != rsize))
+ rv = EIO;
+ return (rv);
}
diff --git a/lib/libstand/dosfs.h b/lib/libstand/dosfs.h
index f2370ee502ff..5b11e0fc375a 100644
--- a/lib/libstand/dosfs.h
+++ b/lib/libstand/dosfs.h
@@ -96,6 +96,8 @@ typedef union {
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 */