diff options
Diffstat (limited to 'stand/libsa/dosfs.c')
-rw-r--r-- | stand/libsa/dosfs.c | 227 |
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)) |