Changeset View
Changeset View
Standalone View
Standalone View
lib/libstand/dosfs.c
Show First 20 Lines • Show All 125 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 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 *, u_int); | ||||
static int iobuf(DOS_FS *, u_int); | static int ioget(struct open_file *, daddr_t, size_t, void *, u_int); | ||||
static int ioget(struct open_file *, u_int, void *, u_int); | |||||
static void | |||||
dos_read_fat(DOS_FS *fs, struct open_file *fd) | |||||
{ | |||||
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; | |||||
} | |||||
} | |||||
if (fat.buf == NULL) | |||||
fat.buf = malloc(secbyt(fs->spf)); | |||||
if (fat.buf != NULL) { | |||||
if (ioget(fd, fs->lsnfat, 0, 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; | |||||
bzero(fs, sizeof(DOS_FS)); | bzero(fs, sizeof(DOS_FS)); | ||||
fs->fd = fd; | fs->fd = fd; | ||||
if ((err = !(fs->buf = malloc(SECSIZ)) ? errno : 0) || | |||||
(err = ioget(fs->fd, 0, fs->buf, 1)) || | if ((err = !(buf = malloc(secbyt(1))) ? errno : 0) || | ||||
(err = parsebs(fs, (DOS_BS *)fs->buf))) { | (err = ioget(fs->fd, 0, 0, buf, secbyt(1))) || | ||||
(err = parsebs(fs, (DOS_BS *)buf))) { | |||||
if (buf != NULL) | |||||
free(buf); | |||||
(void)dosunmount(fs); | (void)dosunmount(fs); | ||||
return(err); | return(err); | ||||
} | } | ||||
free(buf); | |||||
if (fat.buf == NULL || fat.unit != dd->d_unit) | |||||
dos_read_fat(fs, fd); | |||||
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; | ||||
} | } | ||||
Show All 16 Lines | |||||
} | } | ||||
/* | /* | ||||
* Common code shared by dos_mount() and dos_unmount() | * Common code shared by dos_mount() and dos_unmount() | ||||
*/ | */ | ||||
static int | static int | ||||
dosunmount(DOS_FS *fs) | dosunmount(DOS_FS *fs) | ||||
{ | { | ||||
if (fs->buf) | |||||
free(fs->buf); | |||||
free(fs); | free(fs); | ||||
return(0); | return(0); | ||||
} | } | ||||
/* | /* | ||||
* Open DOS file | * Open DOS file | ||||
*/ | */ | ||||
static int | static int | ||||
Show All 40 Lines | |||||
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) | ||||
{ | { | ||||
off_t size; | off_t size; | ||||
u_int nb, off, clus, c, cnt, n; | u_int nb, off, clus, c, cnt, n; | ||||
DOS_FILE *f = (DOS_FILE *)fd->f_fsdata; | DOS_FILE *f = (DOS_FILE *)fd->f_fsdata; | ||||
int err = 0; | int err = 0; | ||||
/* | |||||
* as ioget() can be called *a lot*, use twiddle here. | |||||
* also 4 seems to be good value not to slow loading down too much: | |||||
* with 270MB file (~540k ioget() calls, twiddle can easily waste 4-5sec. | |||||
*/ | |||||
twiddle(4); | |||||
nb = (u_int)nbyte; | nb = (u_int)nbyte; | ||||
if ((size = fsize(f->fs, &f->de)) == -1) | if ((size = fsize(f->fs, &f->de)) == -1) | ||||
return EINVAL; | return EINVAL; | ||||
if (nb > (n = size - f->offset)) | if (nb > (n = size - f->offset)) | ||||
nb = n; | nb = n; | ||||
off = f->offset; | off = f->offset; | ||||
if ((clus = stclus(f->fs->fatsz, &f->de))) | if ((clus = stclus(f->fs->fatsz, &f->de))) | ||||
off &= f->fs->bsize - 1; | off &= f->fs->bsize - 1; | ||||
c = f->c; | c = f->c; | ||||
cnt = nb; | cnt = nb; | ||||
while (cnt) { | while (cnt) { | ||||
n = 0; | n = 0; | ||||
if (!c) { | if (!c) { | ||||
if ((c = clus)) | if ((c = clus)) | ||||
n = bytblk(f->fs, f->offset); | n = bytblk(f->fs, f->offset); | ||||
} else if (!off) | } else if (!off) | ||||
n++; | n++; | ||||
while (n--) { | while (n--) { | ||||
if ((err = fatget(f->fs, &c))) | if ((err = fatget(f->fs, &c))) | ||||
goto out; | goto out; | ||||
if (!okclus(f->fs, c)) { | if (!okclus(f->fs, c)) { | ||||
err = EINVAL; | err = EINVAL; | ||||
goto out; | goto out; | ||||
} | } | ||||
} | } | ||||
if (!clus || (n = f->fs->bsize - off) > cnt) | if (!clus || (n = f->fs->bsize - off) > cnt) | ||||
n = cnt; | n = cnt; | ||||
if ((err = ioread(f->fs, (c ? blkoff(f->fs, c) : | if ((err = ioread(f->fs, (c ? blkoff(f->fs, c) : | ||||
secbyt(f->fs->lsndir)) + off, | secbyt(f->fs->lsndir)) + off, buf, n))) | ||||
buf, n))) | |||||
goto out; | goto out; | ||||
f->offset += n; | f->offset += n; | ||||
f->c = c; | f->c = c; | ||||
off = 0; | off = 0; | ||||
buf = (char *)buf + n; | buf = (char *)buf + n; | ||||
cnt -= n; | cnt -= n; | ||||
} | } | ||||
out: | out: | ||||
if (resid) | if (resid) | ||||
*resid = nbyte - nb + cnt; | *resid = nbyte - nb + cnt; | ||||
return(err); | return(err); | ||||
} | } | ||||
/* | /* | ||||
▲ Show 20 Lines • Show All 60 Lines • ▼ Show 20 Lines | dos_stat(struct open_file *fd, struct stat *sb) | ||||
sb->st_uid = 0; | sb->st_uid = 0; | ||||
sb->st_gid = 0; | sb->st_gid = 0; | ||||
if ((sb->st_size = fsize(f->fs, &f->de)) == -1) | if ((sb->st_size = fsize(f->fs, &f->de)) == -1) | ||||
return EINVAL; | return EINVAL; | ||||
return (0); | return (0); | ||||
} | } | ||||
static int | static int | ||||
dos_checksum(char *name, char *ext) | |||||
{ | |||||
int x, i; | |||||
char buf[11]; | |||||
bcopy(name, buf, 8); | |||||
bcopy(ext, buf+8, 3); | |||||
x = 0; | |||||
for (i = 0; i < 11; i++) { | |||||
x = ((x & 1) << 7) | (x >> 1); | |||||
x += buf[i]; | |||||
x &= 0xff; | |||||
} | |||||
return (x); | |||||
} | |||||
static int | |||||
dos_readdir(struct open_file *fd, struct dirent *d) | dos_readdir(struct open_file *fd, struct dirent *d) | ||||
{ | { | ||||
/* DOS_FILE *f = (DOS_FILE *)fd->f_fsdata; */ | /* DOS_FILE *f = (DOS_FILE *)fd->f_fsdata; */ | ||||
u_char fn[261]; | u_char fn[261]; | ||||
DOS_DIR dd; | DOS_DIR dd; | ||||
size_t res; | size_t res; | ||||
u_int chk, i, x, xdn; | u_int chk, i, x, xdn; | ||||
int err; | int err; | ||||
Show All 37 Lines | if (dd.de.attr & FA_LABEL) { | ||||
} | } | ||||
cp_xdnm(fn, &dd.xde); | cp_xdnm(fn, &dd.xde); | ||||
} else { | } else { | ||||
/* skip only volume label entries */ | /* skip only volume label entries */ | ||||
continue; | continue; | ||||
} | } | ||||
} else { | } else { | ||||
if (xdn == 1) { | if (xdn == 1) { | ||||
x = 0; | x = dos_checksum(dd.de.name, dd.de.ext); | ||||
for (i = 0; i < 11; i++) { | |||||
x = ((x & 1) << 7) | (x >> 1); | |||||
x += dd.de.name[i]; | |||||
x &= 0xff; | |||||
} | |||||
if (x == chk) | if (x == chk) | ||||
break; | break; | ||||
} else { | } else { | ||||
cp_sfn(fn, &dd.de); | cp_sfn(fn, &dd.de); | ||||
break; | break; | ||||
} | } | ||||
x = 0; | x = 0; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 116 Lines • ▼ Show 20 Lines | lookup(DOS_FS *fs, u_int clus, const char *name, DOS_DE **dep) | ||||
for (;;) { | for (;;) { | ||||
if (!clus && !lsec) | if (!clus && !lsec) | ||||
lsec = fs->lsndir; | lsec = fs->lsndir; | ||||
else if (okclus(fs, clus)) | else if (okclus(fs, clus)) | ||||
lsec = blklsn(fs, clus); | lsec = blklsn(fs, clus); | ||||
else | else | ||||
return EINVAL; | return EINVAL; | ||||
for (sec = 0; sec < nsec; sec++) { | for (sec = 0; sec < nsec; sec++) { | ||||
if ((err = ioget(fs->fd, lsec + sec, dir, 1))) | if ((err = ioget(fs->fd, lsec + sec, 0, dir, secbyt(1)))) | ||||
return err; | return err; | ||||
for (ent = 0; ent < DEPSEC; ent++) { | for (ent = 0; ent < DEPSEC; ent++) { | ||||
if (!*dir[ent].de.name) | if (!*dir[ent].de.name) | ||||
return ENOENT; | return ENOENT; | ||||
if (*dir[ent].de.name != 0xe5) { | if (*dir[ent].de.name != 0xe5) { | ||||
if ((dir[ent].de.attr & FA_MASK) == FA_XDE) { | if ((dir[ent].de.attr & FA_MASK) == FA_XDE) { | ||||
x = dir[ent].xde.seq; | x = dir[ent].xde.seq; | ||||
if (x & 0x40 || (x + 1 == xdn && | if (x & 0x40 || (x + 1 == xdn && | ||||
dir[ent].xde.chk == chk)) { | dir[ent].xde.chk == chk)) { | ||||
if (x & 0x40) { | if (x & 0x40) { | ||||
chk = dir[ent].xde.chk; | chk = dir[ent].xde.chk; | ||||
x &= ~0x40; | x &= ~0x40; | ||||
} | } | ||||
if (x >= 1 && x <= 20) { | if (x >= 1 && x <= 20) { | ||||
cp_xdnm(lfn, &dir[ent].xde); | cp_xdnm(lfn, &dir[ent].xde); | ||||
xdn = x; | xdn = x; | ||||
continue; | continue; | ||||
} | } | ||||
} | } | ||||
} else if (!(dir[ent].de.attr & FA_LABEL)) { | } else if (!(dir[ent].de.attr & FA_LABEL)) { | ||||
if ((ok = xdn == 1)) { | if ((ok = xdn == 1)) { | ||||
for (x = 0, i = 0; i < 11; i++) | x = dos_checksum(dir[ent].de.name, dir[ent].de.ext); | ||||
x = ((((x & 1) << 7) | (x >> 1)) + | |||||
dir[ent].de.name[i]) & 0xff; | |||||
ok = chk == x && | ok = chk == x && | ||||
!strcasecmp(name, (const char *)lfn); | !strcasecmp(name, (const char *)lfn); | ||||
} | } | ||||
if (!ok) { | if (!ok) { | ||||
cp_sfn(sfn, &dir[ent].de); | cp_sfn(sfn, &dir[ent].de); | ||||
ok = !strcasecmp(name, (const char *)sfn); | ok = !strcasecmp(name, (const char *)sfn); | ||||
} | } | ||||
if (ok) { | if (ok) { | ||||
▲ Show 20 Lines • Show All 103 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 | * Get next cluster in cluster chain. Use in core fat cache unless another | ||||
* device replaced it. | |||||
*/ | */ | ||||
static int | static int | ||||
fatget(DOS_FS *fs, u_int *c) | fatget(DOS_FS *fs, u_int *c) | ||||
{ | { | ||||
u_char buf[4]; | u_char buf[4]; | ||||
u_int x; | u_char *s; | ||||
int err; | u_int x, offset, off, n, nbyte, lsec; | ||||
struct devdesc *dd = fs->fd->f_devdata; | |||||
int err = 0; | |||||
if (fat.unit != dd->d_unit) { | |||||
/* fat cache was changed to another device, dont use it */ | |||||
err = ioread(fs, secbyt(fs->lsnfat) + fatoff(fs->fatsz, *c), buf, | err = ioread(fs, secbyt(fs->lsnfat) + fatoff(fs->fatsz, *c), buf, | ||||
fs->fatsz != 32 ? 2 : 4); | fs->fatsz != 32 ? 2 : 4); | ||||
if (err) | if (err) | ||||
return err; | return err; | ||||
} else { | |||||
offset = fatoff(fs->fatsz, *c); | |||||
nbyte = fs->fatsz != 32 ? 2 : 4; | |||||
s = buf; | |||||
if ((off = offset & (SECSIZ - 1))) { | |||||
offset -= off; | |||||
lsec = bytsec(offset); | |||||
offset += SECSIZ; | |||||
if ((n = SECSIZ - off) > nbyte) | |||||
n = nbyte; | |||||
memcpy(s, fat.buf + secbyt(lsec) + off, n); | |||||
s += n; | |||||
nbyte -= n; | |||||
} | |||||
n = nbyte & (SECSIZ - 1); | |||||
if (nbyte -= n) { | |||||
memcpy(s, fat.buf + secbyt(bytsec(offset)), nbyte); | |||||
offset += nbyte; | |||||
s += nbyte; | |||||
} | |||||
if (n) | |||||
memcpy(s, fat.buf + secbyt(bytsec(offset)), n); | |||||
} | |||||
x = fs->fatsz != 32 ? cv2(buf) : cv4(buf); | x = fs->fatsz != 32 ? cv2(buf) : cv4(buf); | ||||
*c = fs->fatsz == 12 ? *c & 1 ? x >> 4 : x & 0xfff : x; | *c = fs->fatsz == 12 ? *c & 1 ? x >> 4 : x & 0xfff : x; | ||||
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, u_int nbyte) | ||||
{ | { | ||||
char *s; | char *s; | ||||
u_int off, n; | u_int off, n; | ||||
int err; | int err; | ||||
s = buf; | s = buf; | ||||
if ((off = offset & (SECSIZ - 1))) { | if ((off = offset & (SECSIZ - 1))) { | ||||
offset -= off; | offset -= off; | ||||
if ((err = iobuf(fs, bytsec(offset)))) | |||||
return err; | |||||
offset += SECSIZ; | |||||
if ((n = SECSIZ - off) > nbyte) | if ((n = SECSIZ - off) > nbyte) | ||||
n = nbyte; | n = nbyte; | ||||
memcpy(s, fs->buf + off, n); | if ((err = ioget(fs->fd, bytsec(offset), off, s, n))) | ||||
return err; | |||||
offset += SECSIZ; | |||||
s += n; | s += n; | ||||
nbyte -= n; | nbyte -= n; | ||||
} | } | ||||
n = nbyte & (SECSIZ - 1); | n = nbyte & (SECSIZ - 1); | ||||
if (nbyte -= n) { | if (nbyte -= n) { | ||||
if ((err = ioget(fs->fd, bytsec(offset), s, bytsec(nbyte)))) | if ((err = ioget(fs->fd, bytsec(offset), 0, s, nbyte))) | ||||
return err; | return err; | ||||
offset += nbyte; | offset += nbyte; | ||||
s += nbyte; | s += nbyte; | ||||
} | } | ||||
if (n) { | if (n) { | ||||
if ((err = iobuf(fs, bytsec(offset)))) | if ((err = ioget(fs->fd, bytsec(offset), 0, s, n))) | ||||
return err; | return err; | ||||
memcpy(s, fs->buf, n); | |||||
} | } | ||||
return 0; | return 0; | ||||
} | } | ||||
/* | /* | ||||
* Buffered sector-based I/O primitive | |||||
*/ | |||||
static int | |||||
iobuf(DOS_FS *fs, u_int lsec) | |||||
{ | |||||
int err; | |||||
if (fs->bufsec != lsec) { | |||||
if ((err = ioget(fs->fd, lsec, fs->buf, 1))) | |||||
return err; | |||||
fs->bufsec = lsec; | |||||
} | |||||
return 0; | |||||
} | |||||
/* | |||||
* Sector-based I/O primitive | * Sector-based I/O primitive | ||||
*/ | */ | ||||
static int | static int | ||||
ioget(struct open_file *fd, u_int lsec, void *buf, u_int nsec) | ioget(struct open_file *fd, daddr_t lsec, size_t offset, void *buf, u_int size) | ||||
{ | { | ||||
int err; | return ((fd->f_dev->dv_strategy)(fd->f_devdata, F_READ, lsec, offset, | ||||
size, buf, NULL)); | |||||
twiddle(1); | |||||
if ((err = (fd->f_dev->dv_strategy)(fd->f_devdata, F_READ, lsec, | |||||
secbyt(nsec), buf, NULL))) | |||||
return(err); | |||||
return(0); | |||||
} | } |