aboutsummaryrefslogtreecommitdiff
path: root/stand/libsa/dosfs.c
diff options
context:
space:
mode:
Diffstat (limited to 'stand/libsa/dosfs.c')
-rw-r--r--stand/libsa/dosfs.c227
1 files changed, 147 insertions, 80 deletions
diff --git a/stand/libsa/dosfs.c b/stand/libsa/dosfs.c
index 452a79ae12dc..38610d917007 100644
--- a/stand/libsa/dosfs.c
+++ b/stand/libsa/dosfs.c
@@ -1,6 +1,7 @@
/*
* Copyright (c) 1996, 1998 Robert Nordier
* All rights reserved.
+ * Copyright 2024 MNX Cloud, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -25,20 +26,17 @@
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
/*
* Readonly filesystem for Microsoft FAT12/FAT16/FAT32 filesystems,
* also supports VFAT.
*/
#include <sys/types.h>
+#include <sys/disk.h>
#include <string.h>
#include <stddef.h>
#include "stand.h"
-#include "disk.h"
#include "dosfs.h"
@@ -63,6 +61,7 @@ static int dos_unmount(const char *dev, void *data);
struct fs_ops dosfs_fsops = {
.fs_name = "dosfs",
+ .fs_flags = 0,
.fo_open = dos_open,
.fo_close = dos_close,
.fo_read = dos_read,
@@ -74,10 +73,6 @@ struct fs_ops dosfs_fsops = {
.fo_unmount = dos_unmount
};
-#define SECSIZ 512 /* sector size */
-#define SSHIFT 9 /* SECSIZ shift */
-#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 */
@@ -95,14 +90,43 @@ typedef struct {
u_char heads[2]; /* drive heads */
u_char hidsec[4]; /* hidden sectors */
u_char lsecs[4]; /* huge sectors */
- u_char lspf[4]; /* huge sectors per FAT */
- u_char xflg[2]; /* flags */
- u_char vers[2]; /* filesystem version */
- u_char rdcl[4]; /* root directory start cluster */
- u_char infs[2]; /* filesystem info sector */
- u_char bkbs[2]; /* backup boot sector */
+ union {
+ struct {
+ u_char drvnum; /* Int 13 drive number */
+ u_char rsvd1; /* Reserved */
+ u_char bootsig; /* Boot signature (0x29) */
+ u_char volid[4]; /* Volume serial number */
+ u_char vollab[11]; /* Volume label */
+ u_char fstype[8]; /* Informational */
+ } f12_f16;
+ struct {
+ u_char lspf[4]; /* huge sectors per FAT */
+ u_char xflg[2]; /* flags */
+ u_char vers[2]; /* filesystem version */
+ u_char rdcl[4]; /* root directory cluster */
+ u_char infs[2]; /* filesystem info sector */
+ u_char bkbs[2]; /* backup boot sector */
+ u_char reserved[12]; /* Reserved */
+ u_char drvnum; /* Int 13 drive number */
+ u_char rsvd1; /* Reserved */
+ u_char bootsig; /* Boot signature (0x29) */
+ u_char volid[4]; /* Volume serial number */
+ u_char vollab[11]; /* Volume label */
+ u_char fstype[8]; /* Informational */
+ } f32;
+ } fstype;
} DOS_BPB;
+typedef struct {
+ u_char fsi_leadsig[4]; /* Value 0x41615252 */
+ u_char fsi_reserved1[480];
+ u_char fsi_structsig[4]; /* Value 0x61417272 */
+ u_char fsi_free_count[4]; /* Last known free cluster count */
+ u_char fsi_next_free[4]; /* First free cluster */
+ u_char fsi_reserved2[12];
+ u_char fsi_trailsig[4]; /* Value 0xAA550000 */
+} DOS_FSINFO;
+
/* Initial portion of DOS boot sector */
typedef struct {
u_char jmp[3]; /* usually 80x86 'jmp' opcode */
@@ -120,16 +144,18 @@ static DOS_DE dot[2] = {
};
/* The usual conversion macros to avoid multiplication and division */
-#define bytsec(n) ((n) >> SSHIFT)
-#define secbyt(s) ((s) << SSHIFT)
-#define entsec(e) ((e) >> DSHIFT)
-#define bytblk(fs, n) ((n) >> (fs)->bshift)
-#define blkbyt(fs, b) ((b) << (fs)->bshift)
-#define secblk(fs, s) ((s) >> ((fs)->bshift - SSHIFT))
-#define blksec(fs, b) ((b) << ((fs)->bshift - SSHIFT))
+#define bytsec(fs, n) ((n) >> (fs)->sshift)
+#define secbyt(fs, s) ((s) << (fs)->sshift)
+#define depsec(fs) (1 << (fs)->dshift)
+#define entsec(fs, e) ((e) >> (fs)->dshift)
+#define bytblk(fs, n) ((n) >> (fs)->bshift)
+#define blkbyt(fs, b) ((b) << (fs)->bshift)
+#define secblk(fs, s) ((s) >> ((fs)->bshift - (fs)->sshift))
+#define blksec(fs, b) ((b) << ((fs)->bshift - (fs)->sshift))
/* Convert cluster number to offset within filesystem */
-#define blkoff(fs, b) (secbyt((fs)->lsndta) + blkbyt(fs, (b) - LOCLUS))
+#define blkoff(fs, b) (secbyt(fs, (fs)->lsndta) + \
+ blkbyt(fs, (b) - LOCLUS))
/* Convert cluster number to logical sector number */
#define blklsn(fs, b) ((fs)->lsndta + blksec(fs, (b) - LOCLUS))
@@ -143,8 +169,8 @@ static DOS_DE dot[2] = {
#define okclus(fs, c) ((c) >= LOCLUS && (c) <= (fs)->xclus)
/* Get start cluster from directory entry */
-#define stclus(sz, de) ((sz) != 32 ? cv2((de)->clus) : \
- ((u_int)cv2((de)->dex.h_clus) << 16) | \
+#define stclus(sz, de) ((sz) != 32 ? (u_int)cv2((de)->clus) : \
+ ((u_int)cv2((de)->dex.h_clus) << 16) | \
cv2((de)->clus))
static int parsebs(DOS_FS *, DOS_BS *);
@@ -156,18 +182,18 @@ 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 *, size_t);
-static int ioget(struct open_file *, daddr_t, void *, size_t);
+static int ioread(DOS_FS *, uint64_t, void *, size_t);
+static int ioget(DOS_FS *, daddr_t, void *, size_t);
static int
-dos_read_fatblk(DOS_FS *fs, struct open_file *fd, u_int blknum)
+dos_read_fatblk(DOS_FS *fs, u_int blknum)
{
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);
+ max_offset_in_fat = secbyt(fs, (daddr_t)fs->spf);
io_size = FATBLKSZ;
if (offset_in_fat > max_offset_in_fat)
offset_in_fat = max_offset_in_fat;
@@ -175,7 +201,7 @@ dos_read_fatblk(DOS_FS *fs, struct open_file *fd, u_int blknum)
io_size = ((size_t)(max_offset_in_fat - offset_in_fat));
if (io_size != 0) {
- err = ioget(fd, fs->lsnfat + bytsec(offset_in_fat),
+ err = ioget(fs, fs->lsnfat + bytsec(fs, offset_in_fat),
fs->fatbuf, io_size);
if (err != 0) {
fs->fatbuf_blknum = ((u_int)(-1));
@@ -196,23 +222,34 @@ static int
dos_mount_impl(DOS_FS *fs, struct open_file *fd)
{
int err;
+ unsigned secsz;
u_char *buf;
fs->fd = fd;
- if ((buf = malloc(secbyt(1))) == NULL)
+ err = ioctl(fd->f_id, DIOCGSECTORSIZE, &secsz);
+ if (err != 0) {
+ return (err);
+ }
+
+ buf = malloc(secsz);
+ if (buf == NULL)
return (errno);
- if ((err = ioget(fs->fd, 0, buf, secbyt(1))) ||
+
+ if ((err = ioget(fs, 0, buf, secsz)) ||
(err = parsebs(fs, (DOS_BS *)buf))) {
free(buf);
return (err);
}
- free(buf);
+ fs->secbuf = buf;
- if ((fs->fatbuf = malloc(FATBLKSZ)) == NULL)
+ if ((fs->fatbuf = malloc(FATBLKSZ)) == NULL) {
+ free(buf);
return (errno);
- err = dos_read_fatblk(fs, fd, 0);
+ }
+ err = dos_read_fatblk(fs, 0);
if (err != 0) {
+ free(buf);
free(fs->fatbuf);
return (err);
}
@@ -295,6 +332,7 @@ dos_unmount_impl(DOS_FS *fs)
{
if (fs->links)
return (EBUSY);
+ free(fs->secbuf);
free(fs->fatbuf);
free(fs);
return (0);
@@ -308,13 +346,13 @@ dos_open(const char *path, struct open_file *fd)
{
DOS_DE *de;
DOS_FILE *f;
- DOS_FS *fs;
+ DOS_FS *fs = NULL;
dos_mnt_t *mnt;
const char *dev;
u_int size, clus;
int err;
- dev = disk_fmtdev(fd->f_devdata);
+ dev = devformat((struct devdesc *)fd->f_devdata);
STAILQ_FOREACH(mnt, &mnt_list, dos_link) {
if (strcmp(dev, mnt->dos_dev) == 0)
break;
@@ -322,7 +360,7 @@ dos_open(const char *path, struct open_file *fd)
if (mnt == NULL) {
/* Allocate mount structure, associate with open */
- if ((fs = malloc(sizeof(DOS_FS))) == NULL)
+ if ((fs = calloc(1, sizeof(DOS_FS))) == NULL)
return (errno);
if ((err = dos_mount_impl(fs, fd))) {
free(fs);
@@ -368,7 +406,9 @@ static int
dos_read(struct open_file *fd, void *buf, size_t nbyte, size_t *resid)
{
off_t size;
- u_int nb, off, clus, c, cnt, n;
+ uint64_t off;
+ size_t nb;
+ u_int clus, c, cnt, n;
DOS_FILE *f = (DOS_FILE *)fd->f_fsdata;
int err = 0;
@@ -379,7 +419,7 @@ dos_read(struct open_file *fd, void *buf, size_t nbyte, size_t *resid)
* 4-5 sec.
*/
twiddle(4);
- nb = (u_int)nbyte;
+ nb = nbyte;
if ((size = fsize(f->fs, &f->de)) == -1)
return (EINVAL);
if (nb > (n = size - f->offset))
@@ -406,8 +446,12 @@ dos_read(struct open_file *fd, void *buf, size_t nbyte, size_t *resid)
}
if (!clus || (n = f->fs->bsize - off) > cnt)
n = cnt;
- if ((err = ioread(f->fs, (c ? blkoff(f->fs, c) :
- secbyt(f->fs->lsndir)) + off, buf, n)))
+ if (c != 0)
+ off += blkoff(f->fs, (uint64_t)c);
+ else
+ off += secbyt(f->fs, f->fs->lsndir);
+ err = ioread(f->fs, off, buf, n);
+ if (err != 0)
goto out;
f->offset += n;
f->c = c;
@@ -585,36 +629,54 @@ dos_readdir(struct open_file *fd, struct dirent *d)
static int
parsebs(DOS_FS *fs, DOS_BS *bs)
{
- u_int sc;
+ u_int sc, RootDirSectors;
- if ((bs->jmp[0] != 0x69 &&
- bs->jmp[0] != 0xe9 &&
- (bs->jmp[0] != 0xeb || bs->jmp[2] != 0x90)) ||
- bs->bpb.media < 0xf0)
+ if (bs->bpb.media < 0xf0)
return (EINVAL);
- if (cv2(bs->bpb.secsiz) != SECSIZ)
+
+ /* Check supported sector sizes */
+ switch (cv2(bs->bpb.secsiz)) {
+ case 512:
+ case 1024:
+ case 2048:
+ case 4096:
+ fs->sshift = ffs(cv2(bs->bpb.secsiz)) - 1;
+ break;
+
+ default:
return (EINVAL);
+ }
+
if (!(fs->spc = bs->bpb.spc) || fs->spc & (fs->spc - 1))
return (EINVAL);
- fs->bsize = secbyt(fs->spc);
+ fs->bsize = secbyt(fs, fs->spc);
fs->bshift = ffs(fs->bsize) - 1;
- if ((fs->spf = cv2(bs->bpb.spf))) {
+ fs->dshift = ffs(secbyt(fs, 1) / sizeof (DOS_DE)) - 1;
+ fs->dirents = cv2(bs->bpb.dirents);
+ fs->spf = cv2(bs->bpb.spf);
+ fs->lsnfat = cv2(bs->bpb.ressec);
+
+ if (fs->spf != 0) {
if (bs->bpb.fats != 2)
return (EINVAL);
- if (!(fs->dirents = cv2(bs->bpb.dirents)))
+ if (fs->dirents == 0)
return (EINVAL);
} else {
- if (!(fs->spf = cv4(bs->bpb.lspf)))
+ fs->spf = cv4(bs->bpb.fstype.f32.lspf);
+ if (fs->spf == 0)
return (EINVAL);
- if (!bs->bpb.fats || bs->bpb.fats > 16)
+ if (bs->bpb.fats == 0 || bs->bpb.fats > 16)
return (EINVAL);
- if ((fs->rdcl = cv4(bs->bpb.rdcl)) < LOCLUS)
+ fs->rdcl = cv4(bs->bpb.fstype.f32.rdcl);
+ if (fs->rdcl < LOCLUS)
return (EINVAL);
}
- if (!(fs->lsnfat = cv2(bs->bpb.ressec)))
- return (EINVAL);
+
+ RootDirSectors = ((fs->dirents * sizeof (DOS_DE)) +
+ (secbyt(fs, 1) - 1)) / secbyt(fs, 1);
+
fs->lsndir = fs->lsnfat + fs->spf * bs->bpb.fats;
- fs->lsndta = fs->lsndir + entsec(fs->dirents);
+ fs->lsndta = fs->lsndir + RootDirSectors;
if (!(sc = cv2(bs->bpb.secs)) && !(sc = cv4(bs->bpb.lsecs)))
return (EINVAL);
if (fs->lsndta > sc)
@@ -622,7 +684,7 @@ parsebs(DOS_FS *fs, DOS_BS *bs)
if ((fs->xclus = secblk(fs, sc - fs->lsndta) + 1) < LOCLUS)
return (EINVAL);
fs->fatsz = fs->dirents ? fs->xclus < 0xff6 ? 12 : 16 : 32;
- sc = (secbyt(fs->spf) << 1) / (fs->fatsz >> 2) - 1;
+ sc = (secbyt(fs, fs->spf) << 1) / (fs->fatsz >> 2) - 1;
if (fs->xclus > sc)
fs->xclus = sc;
return (0);
@@ -669,7 +731,7 @@ namede(DOS_FS *fs, const char *path, DOS_DE **dep)
static int
lookup(DOS_FS *fs, u_int clus, const char *name, DOS_DE **dep)
{
- static DOS_DIR dir[DEPSEC];
+ DOS_DIR *dir;
u_char lfn[261];
u_char sfn[13];
u_int nsec, lsec, xdn, chk, sec, ent, x;
@@ -683,9 +745,10 @@ lookup(DOS_FS *fs, u_int clus, const char *name, DOS_DE **dep)
}
if (!clus && fs->fatsz == 32)
clus = fs->rdcl;
- nsec = !clus ? entsec(fs->dirents) : fs->spc;
+ nsec = !clus ? entsec(fs, fs->dirents) : fs->spc;
lsec = 0;
xdn = chk = 0;
+ dir = (DOS_DIR *)fs->secbuf;
for (;;) {
if (!clus && !lsec)
lsec = fs->lsndir;
@@ -694,9 +757,10 @@ lookup(DOS_FS *fs, u_int clus, const char *name, DOS_DE **dep)
else
return (EINVAL);
for (sec = 0; sec < nsec; sec++) {
- if ((err = ioget(fs->fd, lsec + sec, dir, secbyt(1))))
+ if ((err = ioget(fs, lsec + sec, dir,
+ secbyt(fs, 1))))
return (err);
- for (ent = 0; ent < DEPSEC; ent++) {
+ for (ent = 0; ent < depsec(fs); ent++) {
if (!*dir[ent].de.name)
return (ENOENT);
if (*dir[ent].de.name != 0xe5) {
@@ -819,7 +883,7 @@ fsize(DOS_FS *fs, DOS_DE *de)
int n;
if (!(size = cv4(de->size)) && de->attr & FA_DIR) {
- if (!(c = cv2(de->clus))) {
+ if (!(c = stclus(fs->fatsz, de))) {
size = fs->dirents * sizeof(DOS_DE);
} else {
if ((n = fatcnt(fs, c)) == -1)
@@ -863,14 +927,14 @@ fatget(DOS_FS *fs, u_int *c)
/* 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))
+ if (offset + nbyte > secbyt(fs, 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);
+ err = dos_read_fatblk(fs, blknum);
if (err != 0)
return (err);
}
@@ -912,55 +976,58 @@ fatend(u_int sz, u_int c)
* Offset-based I/O primitive
*/
static int
-ioread(DOS_FS *fs, u_int offset, void *buf, size_t nbyte)
+ioread(DOS_FS *fs, uint64_t offset, void *buf, size_t nbyte)
{
char *s;
- u_int off, n;
+ size_t n, secsiz;
int err;
- u_char local_buf[SECSIZ];
+ uint64_t off;
+ secsiz = secbyt(fs, 1);
s = buf;
- if ((off = offset & (SECSIZ - 1))) {
+ if ((off = offset & (secsiz - 1))) {
offset -= off;
- if ((n = SECSIZ - off) > nbyte)
+ if ((n = secsiz - off) > nbyte)
n = nbyte;
- err = ioget(fs->fd, bytsec(offset), local_buf,
- sizeof(local_buf));
+ err = ioget(fs, bytsec(fs, offset), fs->secbuf, secsiz);
if (err != 0)
return (err);
- memcpy(s, local_buf + off, n);
- offset += SECSIZ;
+ memcpy(s, fs->secbuf + off, n);
+ offset += secsiz;
s += n;
nbyte -= n;
}
- n = nbyte & (SECSIZ - 1);
+ n = nbyte & (secsiz - 1);
if (nbyte -= n) {
- if ((err = ioget(fs->fd, bytsec(offset), s, nbyte)))
+ if ((err = ioget(fs, bytsec(fs, offset), s, nbyte)))
return (err);
offset += nbyte;
s += nbyte;
}
if (n != 0) {
- err = ioget(fs->fd, bytsec(offset), local_buf,
- sizeof(local_buf));
+ err = ioget(fs, bytsec(fs, offset), fs->secbuf, secsiz);
if (err != 0)
return (err);
- memcpy(s, local_buf, n);
+ memcpy(s, fs->secbuf, n);
}
return (0);
}
/*
- * Sector-based I/O primitive
+ * Sector-based I/O primitive. Note, since strategy functions are operating
+ * in terms of 512B sectors, we need to do necessary conversion here.
*/
static int
-ioget(struct open_file *fd, daddr_t lsec, void *buf, size_t size)
+ioget(DOS_FS *fs, daddr_t lsec, void *buf, size_t size)
{
size_t rsize;
int rv;
+ struct open_file *fd = fs->fd;
/* Make sure we get full read or error. */
rsize = 0;
+ /* convert native sector number to 512B sector number. */
+ lsec = secbyt(fs, lsec) >> 9;
rv = (fd->f_dev->dv_strategy)(fd->f_devdata, F_READ, lsec,
size, buf, &rsize);
if ((rv == 0) && (size != rsize))