Changeset View
Changeset View
Standalone View
Standalone View
head/lib/libstand/dosfs.c
Show First 20 Lines • Show All 59 Lines • ▼ Show 20 Lines | struct fs_ops dosfs_fsops = { | ||||
dos_readdir | dos_readdir | ||||
}; | }; | ||||
#define SECSIZ 512 /* sector size */ | #define SECSIZ 512 /* sector size */ | ||||
#define SSHIFT 9 /* SECSIZ shift */ | #define SSHIFT 9 /* SECSIZ shift */ | ||||
#define DEPSEC 16 /* directory entries per sector */ | #define DEPSEC 16 /* directory entries per sector */ | ||||
#define DSHIFT 4 /* DEPSEC shift */ | #define DSHIFT 4 /* DEPSEC shift */ | ||||
#define LOCLUS 2 /* lowest cluster number */ | #define LOCLUS 2 /* lowest cluster number */ | ||||
#define FATBLKSZ 0x20000 /* size of block in the FAT cache buffer */ | |||||
/* DOS "BIOS Parameter Block" */ | /* DOS "BIOS Parameter Block" */ | ||||
typedef struct { | typedef struct { | ||||
u_char secsiz[2]; /* sector size */ | u_char secsiz[2]; /* sector size */ | ||||
u_char spc; /* sectors per cluster */ | u_char spc; /* sectors per cluster */ | ||||
u_char ressec[2]; /* reserved sectors */ | u_char ressec[2]; /* reserved sectors */ | ||||
u_char fats; /* FATs */ | u_char fats; /* FATs */ | ||||
u_char dirents[2]; /* root directory entries */ | u_char dirents[2]; /* root directory entries */ | ||||
▲ Show 20 Lines • Show All 51 Lines • ▼ Show 20 Lines | |||||
/* Does cluster number reference a valid data cluster? */ | /* Does cluster number reference a valid data cluster? */ | ||||
#define okclus(fs, c) ((c) >= LOCLUS && (c) <= (fs)->xclus) | #define okclus(fs, c) ((c) >= LOCLUS && (c) <= (fs)->xclus) | ||||
/* Get start cluster from directory entry */ | /* Get start cluster from directory entry */ | ||||
#define stclus(sz, de) ((sz) != 32 ? cv2((de)->clus) : \ | #define stclus(sz, de) ((sz) != 32 ? cv2((de)->clus) : \ | ||||
((u_int)cv2((de)->dex.h_clus) << 16) | \ | ((u_int)cv2((de)->dex.h_clus) << 16) | \ | ||||
cv2((de)->clus)) | 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 parsebs(DOS_FS *, DOS_BS *); | ||||
static int namede(DOS_FS *, const char *, DOS_DE **); | static int namede(DOS_FS *, const char *, DOS_DE **); | ||||
static int lookup(DOS_FS *, u_int, const char *, DOS_DE **); | static int lookup(DOS_FS *, u_int, const char *, DOS_DE **); | ||||
static void cp_xdnm(u_char *, DOS_XDE *); | static void cp_xdnm(u_char *, DOS_XDE *); | ||||
static void cp_sfn(u_char *, DOS_DE *); | static void cp_sfn(u_char *, DOS_DE *); | ||||
static off_t fsize(DOS_FS *, DOS_DE *); | static off_t fsize(DOS_FS *, DOS_DE *); | ||||
static int fatcnt(DOS_FS *, u_int); | static int fatcnt(DOS_FS *, u_int); | ||||
static int fatget(DOS_FS *, u_int *); | static int fatget(DOS_FS *, u_int *); | ||||
static int fatend(u_int, u_int); | static int fatend(u_int, u_int); | ||||
static int ioread(DOS_FS *, u_int, void *, u_int); | static int ioread(DOS_FS *, u_int, void *, size_t); | ||||
static int ioget(struct open_file *, daddr_t, void *, u_int); | static int ioget(struct open_file *, daddr_t, void *, size_t); | ||||
static void | static int | ||||
dos_read_fat(DOS_FS *fs, struct open_file *fd) | dos_read_fatblk(DOS_FS *fs, struct open_file *fd, u_int blknum) | ||||
{ | { | ||||
struct devdesc *dd = fd->f_devdata; | int err; | ||||
size_t io_size; | |||||
daddr_t offset_in_fat, max_offset_in_fat; | |||||
if (fat.buf != NULL) { /* can we reuse old buffer? */ | offset_in_fat = ((daddr_t)blknum) * FATBLKSZ; | ||||
if (fat.size != fs->spf) { | max_offset_in_fat = secbyt(fs->spf); | ||||
free(fat.buf); /* no, free old buffer */ | io_size = FATBLKSZ; | ||||
fat.buf = NULL; | 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) | fs->fatbuf_blknum = blknum; | ||||
fat.buf = malloc(secbyt(fs->spf)); | return (0); | ||||
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; | |||||
} | |||||
/* | /* | ||||
* Mount DOS filesystem | * Mount DOS filesystem | ||||
*/ | */ | ||||
static int | static int | ||||
dos_mount(DOS_FS *fs, struct open_file *fd) | dos_mount(DOS_FS *fs, struct open_file *fd) | ||||
{ | { | ||||
int err; | int err; | ||||
struct devdesc *dd = fd->f_devdata; | |||||
u_char *buf; | u_char *buf; | ||||
bzero(fs, sizeof(DOS_FS)); | bzero(fs, sizeof(DOS_FS)); | ||||
fs->fd = fd; | fs->fd = fd; | ||||
if ((err = !(buf = malloc(secbyt(1))) ? errno : 0) || | if ((buf = malloc(secbyt(1))) == NULL) | ||||
(err = ioget(fs->fd, 0, buf, secbyt(1))) || | return (errno); | ||||
if ((err = ioget(fs->fd, 0, buf, secbyt(1))) || | |||||
(err = parsebs(fs, (DOS_BS *)buf))) { | (err = parsebs(fs, (DOS_BS *)buf))) { | ||||
if (buf != NULL) | |||||
free(buf); | free(buf); | ||||
(void)dosunmount(fs); | |||||
return (err); | return (err); | ||||
} | } | ||||
free(buf); | free(buf); | ||||
if (fat.buf == NULL || fat.unit != dd->d_unit) | if ((fs->fatbuf = malloc(FATBLKSZ)) == NULL) | ||||
dos_read_fat(fs, fd); | return (errno); | ||||
err = dos_read_fatblk(fs, fd, 0); | |||||
if (err != 0) { | |||||
free(fs->fatbuf); | |||||
return (err); | |||||
} | |||||
fs->root = dot[0]; | fs->root = dot[0]; | ||||
fs->root.name[0] = ' '; | fs->root.name[0] = ' '; | ||||
if (fs->fatsz == 32) { | if (fs->fatsz == 32) { | ||||
fs->root.clus[0] = fs->rdcl & 0xff; | fs->root.clus[0] = fs->rdcl & 0xff; | ||||
fs->root.clus[1] = (fs->rdcl >> 8) & 0xff; | fs->root.clus[1] = (fs->rdcl >> 8) & 0xff; | ||||
fs->root.dex.h_clus[0] = (fs->rdcl >> 16) & 0xff; | fs->root.dex.h_clus[0] = (fs->rdcl >> 16) & 0xff; | ||||
fs->root.dex.h_clus[1] = (fs->rdcl >> 24) & 0xff; | fs->root.dex.h_clus[1] = (fs->rdcl >> 24) & 0xff; | ||||
} | } | ||||
return (0); | return (0); | ||||
} | } | ||||
/* | /* | ||||
* Unmount mounted filesystem | * Unmount mounted filesystem | ||||
*/ | */ | ||||
static int | static int | ||||
dos_unmount(DOS_FS *fs) | dos_unmount(DOS_FS *fs) | ||||
{ | { | ||||
int err; | |||||
if (fs->links) | if (fs->links) | ||||
return (EBUSY); | return (EBUSY); | ||||
if ((err = dosunmount(fs))) | free(fs->fatbuf); | ||||
return (err); | |||||
return (0); | |||||
} | |||||
/* | |||||
* Common code shared by dos_mount() and dos_unmount() | |||||
*/ | |||||
static int | |||||
dosunmount(DOS_FS *fs) | |||||
{ | |||||
free(fs); | free(fs); | ||||
return (0); | return (0); | ||||
} | } | ||||
/* | /* | ||||
* Open DOS file | * Open DOS file | ||||
*/ | */ | ||||
static int | static int | ||||
dos_open(const char *path, struct open_file *fd) | dos_open(const char *path, struct open_file *fd) | ||||
{ | { | ||||
DOS_DE *de; | DOS_DE *de; | ||||
DOS_FILE *f; | DOS_FILE *f; | ||||
DOS_FS *fs; | DOS_FS *fs; | ||||
u_int size, clus; | u_int size, clus; | ||||
int err = 0; | int err; | ||||
/* Allocate mount structure, associate with open */ | /* Allocate mount structure, associate with open */ | ||||
fs = malloc(sizeof(DOS_FS)); | if ((fs = malloc(sizeof(DOS_FS))) == NULL) | ||||
return (errno); | |||||
if ((err = dos_mount(fs, fd))) { | |||||
free(fs); | |||||
return (err); | |||||
} | |||||
if ((err = dos_mount(fs, fd))) | if ((err = namede(fs, path, &de))) { | ||||
goto out; | dos_unmount(fs); | ||||
return (err); | |||||
} | |||||
if ((err = namede(fs, path, &de))) | |||||
goto out; | |||||
clus = stclus(fs->fatsz, de); | clus = stclus(fs->fatsz, de); | ||||
size = cv4(de->size); | size = cv4(de->size); | ||||
if ((!(de->attr & FA_DIR) && (!clus != !size)) || | if ((!(de->attr & FA_DIR) && (!clus != !size)) || | ||||
((de->attr & FA_DIR) && size) || | ((de->attr & FA_DIR) && size) || | ||||
(clus && !okclus(fs, clus))) { | (clus && !okclus(fs, clus))) { | ||||
err = EINVAL; | dos_unmount(fs); | ||||
goto out; | return (EINVAL); | ||||
} | } | ||||
f = malloc(sizeof(DOS_FILE)); | if ((f = malloc(sizeof(DOS_FILE))) == NULL) { | ||||
err = errno; | |||||
dos_unmount(fs); | |||||
return (err); | |||||
} | |||||
bzero(f, sizeof(DOS_FILE)); | bzero(f, sizeof(DOS_FILE)); | ||||
f->fs = fs; | f->fs = fs; | ||||
fs->links++; | fs->links++; | ||||
f->de = *de; | f->de = *de; | ||||
fd->f_fsdata = (void *)f; | fd->f_fsdata = (void *)f; | ||||
return (0); | |||||
out: | |||||
return (err); | |||||
} | } | ||||
/* | /* | ||||
* Read from file | * Read from file | ||||
*/ | */ | ||||
static int | static int | ||||
dos_read(struct open_file *fd, void *buf, size_t nbyte, size_t *resid) | dos_read(struct open_file *fd, void *buf, size_t nbyte, size_t *resid) | ||||
{ | { | ||||
▲ Show 20 Lines • Show All 459 Lines • ▼ Show 20 Lines | fatcnt(DOS_FS *fs, u_int c) | ||||
for (n = 0; okclus(fs, c); n++) | for (n = 0; okclus(fs, c); n++) | ||||
if (fatget(fs, &c)) | if (fatget(fs, &c)) | ||||
return (-1); | return (-1); | ||||
return (fatend(fs->fatsz, c) ? n : -1); | return (fatend(fs->fatsz, c) ? n : -1); | ||||
} | } | ||||
/* | /* | ||||
* Get next cluster in cluster chain. Use in core fat cache unless another | * Get next cluster in cluster chain. Use in core fat cache unless | ||||
* device replaced it. | * the number of current 128K block in FAT has changed. | ||||
*/ | */ | ||||
static int | static int | ||||
fatget(DOS_FS *fs, u_int *c) | fatget(DOS_FS *fs, u_int *c) | ||||
{ | { | ||||
u_char buf[4]; | u_int val_in, val_out, offset, blknum, nbyte; | ||||
u_int x, offset, n, nbyte; | const u_char *p_entry; | ||||
struct devdesc *dd = fs->fd->f_devdata; | int err; | ||||
int err = 0; | |||||
if (fat.unit != dd->d_unit) { | /* check input value to prevent overflow in fatoff() */ | ||||
/* fat cache was changed to another device, don't use it */ | val_in = *c; | ||||
err = ioread(fs, secbyt(fs->lsnfat) + fatoff(fs->fatsz, *c), buf, | if (val_in & 0xf0000000) | ||||
fs->fatsz != 32 ? 2 : 4); | return (EINVAL); | ||||
if (err) | |||||
return (err); | |||||
} else { | |||||
offset = fatoff(fs->fatsz, *c); | |||||
nbyte = fs->fatsz != 32 ? 2 : 4; | |||||
if (offset + nbyte > secbyt(fat.size)) | /* 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); | return (EINVAL); | ||||
memcpy(buf, fat.buf + offset, nbyte); | 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); | /* extract cluster number from FAT entry */ | ||||
*c = fs->fatsz == 12 ? *c & 1 ? x >> 4 : x & 0xfff : x; | 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); | return (0); | ||||
} | } | ||||
/* | /* | ||||
* Is cluster an end-of-chain marker? | * Is cluster an end-of-chain marker? | ||||
*/ | */ | ||||
static int | static int | ||||
fatend(u_int sz, u_int c) | fatend(u_int sz, u_int c) | ||||
{ | { | ||||
return (c > (sz == 12 ? 0xff7U : sz == 16 ? 0xfff7U : 0xffffff7)); | return (c > (sz == 12 ? 0xff7U : sz == 16 ? 0xfff7U : 0xffffff7)); | ||||
} | } | ||||
/* | /* | ||||
* Offset-based I/O primitive | * Offset-based I/O primitive | ||||
*/ | */ | ||||
static int | 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; | char *s; | ||||
u_int off, n; | u_int off, n; | ||||
int err; | int err; | ||||
u_char local_buf[SECSIZ]; | u_char local_buf[SECSIZ]; | ||||
s = buf; | s = buf; | ||||
if ((off = offset & (SECSIZ - 1))) { | if ((off = offset & (SECSIZ - 1))) { | ||||
Show All 21 Lines | memcpy(s, local_buf, n); | ||||
} | } | ||||
return (0); | return (0); | ||||
} | } | ||||
/* | /* | ||||
* Sector-based I/O primitive | * Sector-based I/O primitive | ||||
*/ | */ | ||||
static int | 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_t rsize; | ||||
size, buf, NULL)); | 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); | |||||
} | } |