mirror of
https://git.hardenedbsd.org/hardenedbsd/HardenedBSD.git
synced 2024-12-27 21:44:34 +01:00
675 lines
16 KiB
C
675 lines
16 KiB
C
|
/*
|
||
|
* Copyright (c) 1996 Robert Nordier
|
||
|
* All rights reserved.
|
||
|
*
|
||
|
* Redistribution and use in source and binary forms, with or without
|
||
|
* modification, are permitted provided that the following conditions
|
||
|
* are met:
|
||
|
* 1. Redistributions of source code must retain the above copyright
|
||
|
* notice, this list of conditions and the following disclaimer.
|
||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||
|
* notice, this list of conditions and the following disclaimer in the
|
||
|
* documentation and/or other materials provided with the distribution.
|
||
|
*
|
||
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS
|
||
|
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT,
|
||
|
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||
|
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||
|
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||
|
* POSSIBILITY OF SUCH DAMAGE.
|
||
|
*/
|
||
|
|
||
|
#include <sys/types.h>
|
||
|
#include <sys/stat.h>
|
||
|
#include <unistd.h>
|
||
|
#include <fcntl.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <stdio.h>
|
||
|
#include <errno.h>
|
||
|
#include <ctype.h>
|
||
|
#include <string.h>
|
||
|
#include "dosio.h"
|
||
|
|
||
|
#define SECSIZ 512 /* sector size */
|
||
|
#define SSHIFT 9 /* SECSIZ shift */
|
||
|
#define DEPSEC 16 /* directory entries per sector */
|
||
|
#define DSHIFT 4 /* DEPSEC shift */
|
||
|
#define NFATS 2 /* number of FATs */
|
||
|
#define DENMSZ 8 /* DE name size */
|
||
|
#define DEXTSZ 3 /* DE extension size */
|
||
|
#define DENXSZ 11 /* DE name + extension size */
|
||
|
#define LOCLUS 2 /* lowest cluster number */
|
||
|
|
||
|
/* DOS "BIOS Parameter Block" */
|
||
|
typedef struct {
|
||
|
u_char secsiz[2]; /* sector size */
|
||
|
u_char spc; /* sectors per cluster */
|
||
|
u_char ressec[2]; /* reserved sectors */
|
||
|
u_char fats; /* FATs */
|
||
|
u_char dirents[2]; /* root directory entries */
|
||
|
u_char secs[2]; /* total sectors */
|
||
|
u_char media; /* media descriptor */
|
||
|
u_char spf[2]; /* sectors per FAT */
|
||
|
u_char spt[2]; /* sectors per track */
|
||
|
u_char heads[2]; /* drive heads */
|
||
|
u_char hidsec[4]; /* hidden sectors */
|
||
|
u_char lsecs[4]; /* huge sectors */
|
||
|
} DOS_BPB;
|
||
|
|
||
|
/* Fixed portion of DOS boot sector */
|
||
|
typedef struct {
|
||
|
u_char jmp[3]; /* usually 80x86 'jmp' opcode */
|
||
|
u_char oem[8]; /* OEM name and version */
|
||
|
DOS_BPB bpb; /* BPB */
|
||
|
u_char drive; /* drive number */
|
||
|
u_char reserved; /* reserved */
|
||
|
u_char extsig; /* extended boot signature */
|
||
|
u_char volid[4]; /* volume ID */
|
||
|
u_char label[11]; /* volume label */
|
||
|
u_char fstype[8]; /* file system type */
|
||
|
} DOS_BS;
|
||
|
|
||
|
/* Supply missing "." and ".." root directory entries */
|
||
|
static DOS_DE dot[2] = {
|
||
|
{". ", " ", FA_DIR, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||
|
{0, 0}, {0x21, 0}, {0, 0}, {0, 0, 0, 0} },
|
||
|
{".. ", " ", FA_DIR, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||
|
{0, 0}, {0x21, 0}, {0, 0}, {0, 0, 0, 0} }
|
||
|
};
|
||
|
|
||
|
/* I/O error handler address */
|
||
|
int (*dos_ioerr)(int op) = NULL;
|
||
|
|
||
|
/* The usual conversion macros to avoid multiplication and division */
|
||
|
#define bytsec(n) ((n) >> (SSHIFT))
|
||
|
#define secbyt(s) ((u_long)(s) << (SSHIFT))
|
||
|
#define entsec(e) ((e) >> (DSHIFT))
|
||
|
#define bytblk(fs, n) ((n) >> (fs)->bshift)
|
||
|
#define blkbyt(fs, b) ((u_long)(b) << (fs)->bshift)
|
||
|
#define blksec(fs, b) ((u_long)(b) << ((fs)->bshift - (SSHIFT)))
|
||
|
|
||
|
/* Convert cluster number to offset within filesystem */
|
||
|
#define blkoff(fs, b) secbyt((fs)->lsndta) + blkbyt(fs, (b) - (LOCLUS))
|
||
|
|
||
|
/* Convert cluster number to logical sector number */
|
||
|
#define blklsn(fs, b) ((fs)->lsndta + blksec(fs, (b) - (LOCLUS)))
|
||
|
|
||
|
/* Convert cluster number to offset within FAT */
|
||
|
#define fatoff(fat12, c) ((u_long)(c) + ((fat12) ? (c) >> 1 : (c)))
|
||
|
|
||
|
/* Does cluster number reference a valid data cluster? */
|
||
|
#define okclus(fs, c) ((c) >= (LOCLUS) && (c) <= (fs)->xclus)
|
||
|
|
||
|
/* Return on error */
|
||
|
#define RETERR(err) { \
|
||
|
errno = err; \
|
||
|
return -1; \
|
||
|
}
|
||
|
|
||
|
static int dosunmount(DOS_FS *fs);
|
||
|
static int dosstat(DOS_FS *fs, DOS_DE *de, struct stat *sb);
|
||
|
static int parsebs(DOS_FS *fs, DOS_BS *bs);
|
||
|
static int namede(DOS_FS *fs, const char *path, DOS_DE **dep);
|
||
|
static DOS_DE *lookup(DOS_FS *fs, unsigned c, const u_char *nx, int *err);
|
||
|
static off_t fsize(DOS_FS *fs, DOS_DE *de);
|
||
|
static u_short fatget(DOS_FS *fs, u_short c);
|
||
|
static int fatend(int fat12, u_short c);
|
||
|
static int fatcnt(DOS_FS *fs, u_short c);
|
||
|
static int ioread(int fd, off_t offset, void *buf, size_t nbytes);
|
||
|
static int ioget(int fd, u_long lsec, void *buf, u_int nsec);
|
||
|
static u_char *nxname(const char *name, char **endptr);
|
||
|
static int sepchar(int c);
|
||
|
static int wildchar(int c);
|
||
|
static int doschar(int c);
|
||
|
|
||
|
/*
|
||
|
* Mount DOS filesystem
|
||
|
*/
|
||
|
int
|
||
|
dos_mount(DOS_FS *fs, const char *devname)
|
||
|
{
|
||
|
char buf[SECSIZ];
|
||
|
int err;
|
||
|
|
||
|
memset(fs, 0, sizeof(DOS_FS));
|
||
|
if ((fs->fd = open(devname, O_RDONLY)) == -1)
|
||
|
RETERR(errno);
|
||
|
if (!(err = ioget(fs->fd, 0, buf, 1)) &&
|
||
|
!(err = parsebs(fs, (DOS_BS *)buf)))
|
||
|
if (!(fs->fat = malloc(secbyt(fs->spf))))
|
||
|
err = errno;
|
||
|
else
|
||
|
err = ioget(fs->fd, fs->lsnfat, fs->fat, fs->spf);
|
||
|
if (err) {
|
||
|
dosunmount(fs);
|
||
|
RETERR(err);
|
||
|
}
|
||
|
fs->bsize = secbyt(fs->spc);
|
||
|
fs->bshift = ffs(fs->bsize) - 1;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Unmount mounted filesystem
|
||
|
*/
|
||
|
int
|
||
|
dos_unmount(DOS_FS *fs)
|
||
|
{
|
||
|
int err;
|
||
|
|
||
|
if (fs->links)
|
||
|
RETERR(EBUSY);
|
||
|
if ((err = dosunmount(fs)))
|
||
|
RETERR(err);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Common code shared by dos_mount() and dos_unmount()
|
||
|
*/
|
||
|
static
|
||
|
int dosunmount(DOS_FS *fs)
|
||
|
{
|
||
|
if (fs->fat)
|
||
|
free(fs->fat);
|
||
|
return close(fs->fd) ? errno : 0;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Determine free data space in filesystem (in bytes)
|
||
|
*/
|
||
|
u_long
|
||
|
dos_free(DOS_FS *fs)
|
||
|
{
|
||
|
unsigned n, c;
|
||
|
|
||
|
n = 0;
|
||
|
for (c = LOCLUS; c <= fs->xclus; c++)
|
||
|
if (!fatget(fs, c))
|
||
|
n++;
|
||
|
return blkbyt(fs, n);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Close open file
|
||
|
*/
|
||
|
int
|
||
|
dos_close(void *v)
|
||
|
{
|
||
|
DOS_FILE *f = v;
|
||
|
|
||
|
f->fs->links--;
|
||
|
free(f);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Reposition with file
|
||
|
*/
|
||
|
fpos_t
|
||
|
dos_seek(void *v, fpos_t offset, int whence)
|
||
|
{
|
||
|
off_t off;
|
||
|
u_long size;
|
||
|
DOS_FILE *f = v;
|
||
|
|
||
|
size = cv4(f->de.size);
|
||
|
switch (whence) {
|
||
|
case SEEK_SET:
|
||
|
off = 0;
|
||
|
break;
|
||
|
case SEEK_CUR:
|
||
|
off = f->offset;
|
||
|
break;
|
||
|
case SEEK_END:
|
||
|
off = size;
|
||
|
break;
|
||
|
default:
|
||
|
RETERR(EINVAL);
|
||
|
}
|
||
|
off += offset;
|
||
|
if (off < 0 || off > size)
|
||
|
RETERR(EINVAL);
|
||
|
f->offset = off;
|
||
|
f->c = 0;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Read from file
|
||
|
*/
|
||
|
int
|
||
|
dos_read(void *v, char *buf, int nbytes)
|
||
|
{
|
||
|
off_t size;
|
||
|
u_long off, cnt, n;
|
||
|
unsigned clus, c;
|
||
|
int err;
|
||
|
DOS_FILE *f = v;
|
||
|
|
||
|
if ((size = fsize(f->fs, &f->de)) == -1)
|
||
|
RETERR(EBADFS);
|
||
|
if (nbytes > (n = size - f->offset))
|
||
|
nbytes = n;
|
||
|
off = f->offset;
|
||
|
if ((clus = cv2(f->de.clus)))
|
||
|
off &= f->fs->bsize - 1;
|
||
|
c = f->c;
|
||
|
cnt = nbytes;
|
||
|
while (cnt) {
|
||
|
n = 0;
|
||
|
if (!c) {
|
||
|
if ((c = clus))
|
||
|
n = bytblk(f->fs, f->offset);
|
||
|
} else if (!off)
|
||
|
n++;
|
||
|
while (n--) {
|
||
|
c = fatget(f->fs, c);
|
||
|
if (!okclus(f->fs, c))
|
||
|
RETERR(EBADFS);
|
||
|
}
|
||
|
if (!clus || (n = f->fs->bsize - off) > cnt)
|
||
|
n = cnt;
|
||
|
if ((err = ioread(f->fs->fd, (c ? blkoff(f->fs, c) :
|
||
|
secbyt(f->fs->lsndir)) + off,
|
||
|
buf, n)))
|
||
|
RETERR(err);
|
||
|
f->offset += n;
|
||
|
f->c = c;
|
||
|
off = 0;
|
||
|
buf += n;
|
||
|
cnt -= n;
|
||
|
}
|
||
|
return nbytes;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Get file status
|
||
|
*/
|
||
|
int
|
||
|
dos_stat(DOS_FS *fs, const char *path, struct stat *sb)
|
||
|
{
|
||
|
DOS_DE *de;
|
||
|
int err;
|
||
|
|
||
|
if ((err = namede(fs, path, &de)) || (err = dosstat(fs, de, sb)))
|
||
|
RETERR(err);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Get file status of open file
|
||
|
*/
|
||
|
int
|
||
|
dos_fstat(DOS_FILE *f, struct stat *sb)
|
||
|
{
|
||
|
int err;
|
||
|
|
||
|
if ((err = dosstat(f->fs, &f->de, sb)))
|
||
|
RETERR(err);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* File status primitive
|
||
|
*/
|
||
|
static int
|
||
|
dosstat(DOS_FS *fs, DOS_DE *de, struct stat *sb)
|
||
|
{
|
||
|
|
||
|
memset(sb, 0, sizeof(struct stat));
|
||
|
sb->st_mode = (de->attr & FA_DIR) ? S_IFDIR | 0777 : S_IFREG | 0666;
|
||
|
if (de->attr & FA_RDONLY)
|
||
|
sb->st_mode &= ~0222;
|
||
|
if (de->attr & FA_HIDDEN)
|
||
|
sb->st_mode &= ~0007;
|
||
|
if (de->attr & FA_SYSTEM)
|
||
|
sb->st_mode &= ~0077;
|
||
|
sb->st_nlink = 1;
|
||
|
dos_cvtime(&sb->st_atime, cv2(de->date), cv2(de->time));
|
||
|
sb->st_mtime = sb->st_atime;
|
||
|
sb->st_ctime = sb->st_atime;
|
||
|
if ((sb->st_size = fsize(fs, de)) == -1)
|
||
|
return EBADFS;
|
||
|
if (!(de->attr & FA_DIR) || cv2(de->clus))
|
||
|
sb->st_blocks = bytblk(fs, sb->st_size + fs->bsize - 1);
|
||
|
sb->st_blksize = fs->bsize;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Convert from DOS date and time
|
||
|
*/
|
||
|
void
|
||
|
dos_cvtime(time_t *timer, u_short ddate, u_short dtime)
|
||
|
{
|
||
|
struct tm tm;
|
||
|
|
||
|
memset(&tm, 0, sizeof(tm));
|
||
|
tm.tm_sec = (dtime & 0x1f) << 1;
|
||
|
tm.tm_min = dtime >> 5 & 0x3f;
|
||
|
tm.tm_hour = dtime >> 11;
|
||
|
tm.tm_mday = ddate & 0x1f;
|
||
|
tm.tm_mon = (ddate >> 5 & 0xf) - 1;
|
||
|
tm.tm_year = 80 + (ddate >> 9);
|
||
|
*timer = mktime(&tm);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Open DOS file
|
||
|
*/
|
||
|
FILE *
|
||
|
dos_open(DOS_FS *fs, const char *path)
|
||
|
{
|
||
|
DOS_DE *de;
|
||
|
DOS_FILE *f;
|
||
|
u_long size;
|
||
|
u_int clus;
|
||
|
int err;
|
||
|
FILE *fp;
|
||
|
|
||
|
if ((err = namede(fs, path, &de)))
|
||
|
return NULL;
|
||
|
clus = cv2(de->clus);
|
||
|
size = cv4(de->size);
|
||
|
if ((clus && (!okclus(fs, clus) || (!(de->attr & FA_DIR) && !size))) ||
|
||
|
(!clus && !(de->attr & FA_DIR) && size))
|
||
|
return NULL;
|
||
|
f = (DOS_FILE *)malloc(sizeof(DOS_FILE));
|
||
|
memset(f, 0, sizeof(DOS_FILE));
|
||
|
f->fs = fs;
|
||
|
fs->links++;
|
||
|
f->de = *de;
|
||
|
fp = funopen(f, dos_read, NULL, dos_seek, dos_close);
|
||
|
return fp;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Parse DOS boot sector
|
||
|
*/
|
||
|
static int
|
||
|
parsebs(DOS_FS *fs, DOS_BS *bs)
|
||
|
{
|
||
|
u_long sc;
|
||
|
|
||
|
if ((bs->jmp[0] != 0xe9 && (bs->jmp[0] != 0xeb || bs->jmp[2] != 0x90)) ||
|
||
|
bs->bpb.media < 0xf0 ||
|
||
|
cv2(bs->bpb.secsiz) != SECSIZ ||
|
||
|
!bs->bpb.spc || (bs->bpb.spc ^ (bs->bpb.spc - 1)) < bs->bpb.spc)
|
||
|
return EINVAL;
|
||
|
fs->spf = cv2(bs->bpb.spf);
|
||
|
fs->dirents = cv2(bs->bpb.dirents);
|
||
|
fs->spc = bs->bpb.spc;
|
||
|
sc = cv2(bs->bpb.secs);
|
||
|
if (!sc && bs->extsig == 0x29)
|
||
|
sc = cv4(bs->bpb.lsecs);
|
||
|
if (!sc || bs->bpb.fats != NFATS || bs->bpb.spc > 64)
|
||
|
return EINVAL;
|
||
|
if (!fs->dirents || fs->dirents & (DEPSEC - 1))
|
||
|
return EINVAL;
|
||
|
fs->lsnfat = cv2(bs->bpb.ressec);
|
||
|
fs->lsndir = fs->lsnfat + (u_long)fs->spf * NFATS;
|
||
|
fs->lsndta = fs->lsndir + entsec(fs->dirents);
|
||
|
if (fs->lsndta > sc || !(sc = (sc - fs->lsndta) / fs->spc) || sc >= 0xfff6)
|
||
|
return EINVAL;
|
||
|
fs->fat12 = sc < 0xff6;
|
||
|
fs->xclus = sc + 1;
|
||
|
if (fs->spf < bytsec(fatoff(fs->fat12, fs->xclus) + SECSIZ))
|
||
|
return EINVAL;
|
||
|
if (bs->extsig == 0x29)
|
||
|
fs->volid = cv4(bs->volid);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Return directory entry from path
|
||
|
*/
|
||
|
static int
|
||
|
namede(DOS_FS *fs, const char *path, DOS_DE **dep)
|
||
|
{
|
||
|
DOS_DE *de;
|
||
|
u_char *nx;
|
||
|
int err;
|
||
|
|
||
|
err = 0;
|
||
|
de = dot;
|
||
|
if (*path == '/')
|
||
|
path++;
|
||
|
while (*path) {
|
||
|
if (!(nx = nxname(path, (char **)&path)))
|
||
|
return EINVAL;
|
||
|
if (!(de->attr & FA_DIR))
|
||
|
return ENOTDIR;
|
||
|
if (!(de = lookup(fs, cv2(de->clus), nx, &err)))
|
||
|
return err ? err : ENOENT;
|
||
|
if (*path == '/')
|
||
|
path++;
|
||
|
}
|
||
|
*dep = de;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Lookup path segment
|
||
|
*/
|
||
|
static DOS_DE *
|
||
|
lookup(DOS_FS *fs, unsigned c, const u_char *nx, int *err)
|
||
|
{
|
||
|
static DOS_DE dir[DEPSEC];
|
||
|
u_long lsec;
|
||
|
u_int nsec;
|
||
|
int s, e;
|
||
|
|
||
|
if (!c)
|
||
|
for (e = 0; e < 2; e++)
|
||
|
if (!memcmp(dot + e, nx, DENXSZ))
|
||
|
return dot + e;
|
||
|
nsec = !c ? entsec(fs->dirents) : fs->spc;
|
||
|
lsec = 0;
|
||
|
do {
|
||
|
if (!c && !lsec)
|
||
|
lsec = fs->lsndir;
|
||
|
else if okclus(fs, c)
|
||
|
lsec = blklsn(fs, c);
|
||
|
else {
|
||
|
*err = EBADFS;
|
||
|
return NULL;
|
||
|
}
|
||
|
for (s = 0; s < nsec; s++) {
|
||
|
if ((e = ioget(fs->fd, lsec + s, dir, 1))) {
|
||
|
*err = e;
|
||
|
return NULL;
|
||
|
}
|
||
|
for (e = 0; e < DEPSEC; e++) {
|
||
|
if (!*dir[e].name)
|
||
|
return NULL;
|
||
|
if (*dir[e].name == 0xe5 || dir[e].attr & FA_LABEL)
|
||
|
continue;
|
||
|
if (!memcmp(dir + e, nx, DENXSZ))
|
||
|
return dir + e;
|
||
|
}
|
||
|
}
|
||
|
} while (c && !fatend(fs->fat12, c = fatget(fs, c)));
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Return size of file in bytes
|
||
|
*/
|
||
|
static off_t
|
||
|
fsize(DOS_FS *fs, DOS_DE *de)
|
||
|
{
|
||
|
u_long size;
|
||
|
u_int c;
|
||
|
int n;
|
||
|
|
||
|
if (!(size = cv4(de->size)) && de->attr & FA_DIR)
|
||
|
if (!(c = cv2(de->clus)))
|
||
|
size = fs->dirents * sizeof(DOS_DE);
|
||
|
else {
|
||
|
if ((n = fatcnt(fs, c)) == -1)
|
||
|
return n;
|
||
|
size = blkbyt(fs, n);
|
||
|
}
|
||
|
return size;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Return next cluster in cluster chain
|
||
|
*/
|
||
|
static u_short
|
||
|
fatget(DOS_FS *fs, u_short c)
|
||
|
{
|
||
|
u_short x;
|
||
|
|
||
|
x = cv2(fs->fat + fatoff(fs->fat12, c));
|
||
|
return fs->fat12 ? c & 1 ? x >> 4 : x & 0xfff : x;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Count number of clusters in chain
|
||
|
*/
|
||
|
static int
|
||
|
fatcnt(DOS_FS *fs, u_short c)
|
||
|
{
|
||
|
int n;
|
||
|
|
||
|
for (n = 0; okclus(fs, c); n++)
|
||
|
c = fatget(fs, c);
|
||
|
return fatend(fs->fat12, c) ? n : -1;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Is cluster an end-of-chain marker?
|
||
|
*/
|
||
|
static int
|
||
|
fatend(int fat12, u_short c)
|
||
|
{
|
||
|
return c > (fat12 ? 0xff7 : 0xfff7) || c == 0xfff0;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Offset-based I/O primitive
|
||
|
*/
|
||
|
static int
|
||
|
ioread(int fd, off_t offset, void *buf, size_t nbytes)
|
||
|
{
|
||
|
char tmp[SECSIZ];
|
||
|
u_int off, n;
|
||
|
int err;
|
||
|
|
||
|
if ((off = offset & (SECSIZ - 1))) {
|
||
|
offset -= off;
|
||
|
if ((err = ioget(fd, bytsec(offset), tmp, 1)))
|
||
|
return err;
|
||
|
offset += SECSIZ;
|
||
|
if ((n = SECSIZ - off) > nbytes)
|
||
|
n = nbytes;
|
||
|
memcpy(buf, tmp + off, n);
|
||
|
buf += n;
|
||
|
nbytes -= n;
|
||
|
}
|
||
|
n = nbytes & (SECSIZ - 1);
|
||
|
if (nbytes -= n) {
|
||
|
if ((err = ioget(fd, bytsec(offset), buf, bytsec(nbytes))))
|
||
|
return err;
|
||
|
offset += nbytes;
|
||
|
buf += nbytes;
|
||
|
}
|
||
|
if (n) {
|
||
|
if ((err = ioget(fd, bytsec(offset), tmp, 1)))
|
||
|
return err;
|
||
|
memcpy(buf, tmp, n);
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Sector-based I/O primitive
|
||
|
*/
|
||
|
static int
|
||
|
ioget(int fd, u_long lsec, void *buf, u_int nsec)
|
||
|
{
|
||
|
size_t nbytes;
|
||
|
ssize_t n;
|
||
|
|
||
|
nbytes = secbyt(nsec);
|
||
|
do {
|
||
|
if (lseek(fd, secbyt(lsec), SEEK_SET) == -1)
|
||
|
return errno;
|
||
|
n = read(fd, buf, nbytes);
|
||
|
} while (n == -1 && errno == EIO && dos_ioerr && dos_ioerr(0));
|
||
|
if (n != nbytes)
|
||
|
return n == -1 ? errno : EIO;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Convert name to DOS directory (name + extension) format
|
||
|
*/
|
||
|
static u_char *
|
||
|
nxname(const char *name, char **endptr)
|
||
|
{
|
||
|
static u_char nx[DENXSZ];
|
||
|
int i;
|
||
|
|
||
|
memset(nx, ' ', sizeof(nx));
|
||
|
for (i = 0; i < DENMSZ && doschar(*name); i++)
|
||
|
nx[i] = toupper(*name++);
|
||
|
if (i) {
|
||
|
if (i == DENMSZ)
|
||
|
while (!sepchar(*name))
|
||
|
name++;
|
||
|
if (*name == '.') {
|
||
|
name++;
|
||
|
for (i = 0; i < DEXTSZ && doschar(*name); i++)
|
||
|
nx[DENMSZ + i] = toupper(*name++);
|
||
|
if (i == DEXTSZ)
|
||
|
while(!sepchar(*name))
|
||
|
name++;
|
||
|
}
|
||
|
} else if (*name == '.') {
|
||
|
nx[0] = *name++;
|
||
|
if (*name == '.')
|
||
|
nx[1] = *name++;
|
||
|
}
|
||
|
if ((*name && *name != '/') || *nx == ' ')
|
||
|
return NULL;
|
||
|
if (*nx == 0xe5)
|
||
|
*nx = 5;
|
||
|
*endptr = (char *)name;
|
||
|
return nx;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Is character a path-separator?
|
||
|
*/
|
||
|
static int
|
||
|
sepchar(int c)
|
||
|
{
|
||
|
return !wildchar(c) && !doschar(c);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Is character a wildcard?
|
||
|
*/
|
||
|
static int
|
||
|
wildchar(int c)
|
||
|
{
|
||
|
return c == '*' || c == '?';
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Is character valid in a DOS name?
|
||
|
*/
|
||
|
static int
|
||
|
doschar(int c)
|
||
|
{
|
||
|
return c & 0x80 || (c >= ' ' && !strchr("\"*+,./:;<=>?[\\]|", c));
|
||
|
}
|