diff options
author | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
---|---|---|
committer | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
commit | e5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch) | |
tree | d8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/cmd/dossrv |
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/cmd/dossrv')
-rwxr-xr-x | sys/src/cmd/dossrv/chat.c | 36 | ||||
-rwxr-xr-x | sys/src/cmd/dossrv/dat.h | 255 | ||||
-rwxr-xr-x | sys/src/cmd/dossrv/devio.c | 65 | ||||
-rwxr-xr-x | sys/src/cmd/dossrv/dosfs.c | 916 | ||||
-rwxr-xr-x | sys/src/cmd/dossrv/dosfs.h | 10 | ||||
-rwxr-xr-x | sys/src/cmd/dossrv/dossubs.c | 1940 | ||||
-rwxr-xr-x | sys/src/cmd/dossrv/errstr.h | 17 | ||||
-rwxr-xr-x | sys/src/cmd/dossrv/fns.h | 70 | ||||
-rwxr-xr-x | sys/src/cmd/dossrv/iotrack.c | 316 | ||||
-rwxr-xr-x | sys/src/cmd/dossrv/iotrack.h | 69 | ||||
-rwxr-xr-x | sys/src/cmd/dossrv/lock.c | 38 | ||||
-rwxr-xr-x | sys/src/cmd/dossrv/mkfile | 29 | ||||
-rwxr-xr-x | sys/src/cmd/dossrv/xfile.c | 255 | ||||
-rwxr-xr-x | sys/src/cmd/dossrv/xfssrv.c | 204 |
14 files changed, 4220 insertions, 0 deletions
diff --git a/sys/src/cmd/dossrv/chat.c b/sys/src/cmd/dossrv/chat.c new file mode 100755 index 000000000..7f1174307 --- /dev/null +++ b/sys/src/cmd/dossrv/chat.c @@ -0,0 +1,36 @@ +#include <u.h> +#include <libc.h> +#include "iotrack.h" +#include "dat.h" +#include "fns.h" + +#define SIZE 1024 +int chatty; +extern int doabort; + +void +chat(char *fmt, ...) +{ + va_list arg; + + if (!chatty) + return; + va_start(arg, fmt); + vfprint(2, fmt, arg); + va_end(arg); +} + +void +panic(char *fmt, ...) +{ + va_list arg; + + fprint(2, "%s %d: panic ", argv0, getpid()); + va_start(arg, fmt); + vfprint(2, fmt, arg); + va_end(arg); + fprint(2, ": %r\n"); + if(doabort) + abort(); + exits("panic"); +} diff --git a/sys/src/cmd/dossrv/dat.h b/sys/src/cmd/dossrv/dat.h new file mode 100755 index 000000000..45202c69a --- /dev/null +++ b/sys/src/cmd/dossrv/dat.h @@ -0,0 +1,255 @@ +typedef struct Dosboot Dosboot; +typedef struct Dosboot32 Dosboot32; +typedef struct Dosbpb Dosbpb; +typedef struct Dosdir Dosdir; +typedef struct Dospart Dospart; +typedef struct Dosptr Dosptr; +typedef struct Fatinfo Fatinfo; +typedef struct Xfs Xfs; +typedef struct Xfile Xfile; + +struct Dospart{ + uchar active; + uchar hstart; + uchar cylstart[2]; + uchar type; + uchar hend; + uchar cylend[2]; + uchar start[4]; + uchar length[4]; +}; + +enum +{ + /* + * dos partition types + */ + FAT12 = 0x01, + FAT16 = 0x04, /* partitions smaller than 32MB */ + FATHUGE = 0x06, /* fat16 partitions larger than 32MB */ + FAT32 = 0x0b, + FAT32X = 0x0c, + FATHUGEX = 0x0e, + DMDDO = 0x54, + + FATRESRV = 2, /* number of reserved fat entries */ +}; + +/* + * dos boot sector, the start of every dos partition + */ +struct Dosboot{ + uchar magic[3]; + uchar version[8]; + uchar sectsize[2]; + uchar clustsize; + uchar nresrv[2]; + uchar nfats; + uchar rootsize[2]; + uchar volsize[2]; + uchar mediadesc; + uchar fatsize[2]; + uchar trksize[2]; + uchar nheads[2]; + uchar nhidden[4]; + uchar bigvolsize[4]; /* same as Dosboot32 up to here */ + uchar driveno; + uchar reserved0; + uchar bootsig; + uchar volid[4]; + uchar label[11]; + uchar reserved1[8]; +}; + +/* + * dos boot sector for FAT32 + */ +enum +{ + NOFATMIRROR = 0x0080, /* masks for extflags */ + ACTFATMASK = 0x000f, +}; + +struct Dosboot32{ + uchar magic[3]; + uchar version[8]; + uchar sectsize[2]; + uchar clustsize; + uchar nresrv[2]; + uchar nfats; + uchar rootsize[2]; + uchar volsize[2]; + uchar mediadesc; + uchar fatsize[2]; + uchar trksize[2]; + uchar nheads[2]; + uchar nhidden[4]; + uchar bigvolsize[4]; /* same as Dosboot up to here */ + uchar fatsize32[4]; /* sectors per fat */ + uchar extflags[2]; /* active fat flags */ + uchar version1[2]; /* fat32 version; major & minor bytes */ + uchar rootstart[4]; /* starting cluster of root dir */ + uchar infospec[2]; /* fat allocation info sector */ + uchar backupboot[2]; /* backup boot sector */ + uchar reserved[12]; +}; + +/* + * optional FAT32 info sector + */ +enum +{ + FATINFOSIG1 = 0x41615252UL, + FATINFOSIG = 0x61417272UL, +}; + +struct Fatinfo +{ + uchar sig1[4]; + uchar pad[480]; + uchar sig[4]; + uchar freeclust[4]; /* num frre clusters; -1 is unknown */ + uchar nextfree[4]; /* most recently allocated cluster */ + uchar resrv[4*3]; +}; + +/* + * BIOS paramater block + */ +struct Dosbpb{ + MLock; /* access to fat */ + int sectsize; /* in bytes */ + int clustsize; /* in sectors */ + int nresrv; /* sectors */ + int nfats; /* usually 2; modified to 1 if fat mirroring disabled */ + int rootsize; /* number of entries, for fat12 and fat16 */ + long volsize; /* in sectors */ + int mediadesc; + long fatsize; /* in sectors */ + int fatclusters; + int fatbits; /* 12, 16, or 32 */ + long fataddr; /* sector number of first valid fat entry */ + long rootaddr; /* for fat16 or fat12, sector of root dir */ + long rootstart; /* for fat32, cluster of root dir */ + long dataaddr; /* initial sector of data clusters */ + long freeptr; /* next free cluster candidate */ + long freeclusters; /* count of free clusters, for fat32 */ + int fatinfo; /* fat info sector location; 0 => none */ +}; + +enum +{ + DOSDIRSIZE = 32, + DOSEMPTY = 0xe5, /* first char in name if entry is unused */ + DOSRUNE = 13, /* runes per dosdir in a long file name */ + DOSNAMELEN = 261 /* max dos file name length */ +}; + +struct Dosdir{ + uchar name[8]; + uchar ext[3]; + uchar attr; + uchar reserved[1]; + uchar ctime[3]; /* creation time */ + uchar cdate[2]; /* creation date */ + uchar adate[2]; /* last access date */ + uchar hstart[2]; /* high bits of start for fat32 */ + uchar time[2]; /* last modified time */ + uchar date[2]; /* last modified date */ + uchar start[2]; + uchar length[4]; +}; + +enum +{ + DRONLY = 0x01, + DHIDDEN = 0x02, + DSYSTEM = 0x04, + DVLABEL = 0x08, + DDIR = 0x10, + DARCH = 0x20, +}; + +#define GSHORT(p) (((p)[0])|(p)[1]<<8) +#define GLONG(p) (((long)(p)[0])|(p)[1]<<8|(p)[2]<<16|(p)[3]<<24) +#define PSHORT(p,v) ((p)[0]=(v),(p)[1]=(v)>>8) +#define PLONG(p,v) ((p)[0]=(v),(p)[1]=(v)>>8,(p)[2]=(v)>>16,(p)[3]=(v)>>24) + +struct Dosptr{ + ulong addr; /* sector & entry within of file's directory entry */ + ulong offset; + ulong paddr; /* of parent's directory entry */ + ulong poffset; + ulong iclust; /* ordinal within file */ + ulong clust; + ulong naddr; /* next block in directory (for writing multi entry elements) */ + ulong prevaddr; + Iosect *p; + Dosdir *d; +}; + +#define QIDPATH(p) ((p)->addr*(Sectorsize/DOSDIRSIZE) + \ + (p)->offset/DOSDIRSIZE) + +struct Xfs{ + Xfs *next; + int omode; /* of file containing external fs */ + char *name; /* of file containing external f.s. */ + Qid qid; /* of file containing external f.s. */ + long ref; /* attach count */ + Qid rootqid; /* of plan9 constructed root directory */ + uchar isfat32; /* is a fat 32 file system? */ + short dev; + short fmt; + long offset; + void *ptr; +}; + +struct Xfile{ + Xfile *next; /* in hash bucket */ + long fid; + ulong flags; + Qid qid; + Xfs *xf; + Dosptr *ptr; +}; + +enum{ + Asis, Clean, Clunk +}; + +enum{ + Invalid, Short, ShortLower, Long +}; + +enum{ /* Xfile flags */ + Oread = 1, + Owrite = 2, + Orclose = 4, + Omodes = 3, +}; + +enum{ + Enevermind, + Eformat, + Eio, + Enoauth, + Enomem, + Enonexist, + Eperm, + Enofilsys, + Eauth, + Econtig, + Ebadfcall, + Ebadstat, + Eversion, + Etoolong, + Eerrstr, + ESIZE +}; + +extern int chatty; +extern int errno; +extern int readonly; +extern char *deffile; +extern int trspaces;
\ No newline at end of file diff --git a/sys/src/cmd/dossrv/devio.c b/sys/src/cmd/dossrv/devio.c new file mode 100755 index 000000000..0ec1681ef --- /dev/null +++ b/sys/src/cmd/dossrv/devio.c @@ -0,0 +1,65 @@ +#include <u.h> +#include <libc.h> +#include "iotrack.h" +#include "dat.h" +#include "fns.h" + +int readonly; + +static int +deverror(char *name, Xfs *xf, long addr, long n, long nret) +{ + errno = Eio; + if(nret < 0){ + chat("%s errstr=\"%r\"...", name); + close(xf->dev); + xf->dev = -1; + return -1; + } + fprint(2, "dev %d sector %ld, %s: %ld, should be %ld\n", xf->dev, addr, name, nret, n); + return -1; +} + +int +devread(Xfs *xf, long addr, void *buf, long n) +{ + long nread; + + if(xf->dev < 0) + return -1; + nread = pread(xf->dev, buf, n, xf->offset+(vlong)addr*Sectorsize); + if (nread == n) + return 0; + return deverror("read", xf, addr, n, nread); +} + +int +devwrite(Xfs *xf, long addr, void *buf, long n) +{ + long nwrite; + + if(xf->omode==OREAD) + return -1; + + if(xf->dev < 0) + return -1; + nwrite = pwrite(xf->dev, buf, n, xf->offset+(vlong)addr*Sectorsize); + if (nwrite == n) + return 0; + return deverror("write", xf, addr, n, nwrite); +} + +int +devcheck(Xfs *xf) +{ + char buf[Sectorsize]; + + if(xf->dev < 0) + return -1; + if(pread(xf->dev, buf, Sectorsize, 0) != Sectorsize){ + close(xf->dev); + xf->dev = -1; + return -1; + } + return 0; +} diff --git a/sys/src/cmd/dossrv/dosfs.c b/sys/src/cmd/dossrv/dosfs.c new file mode 100755 index 000000000..f30d1ab8a --- /dev/null +++ b/sys/src/cmd/dossrv/dosfs.c @@ -0,0 +1,916 @@ +#include <u.h> +#include <libc.h> +#include <auth.h> +#include <fcall.h> +#include "iotrack.h" +#include "dat.h" +#include "dosfs.h" +#include "fns.h" + +void +rversion(void) +{ + if(req->msize > Maxiosize) + rep->msize = Maxiosize; + else + rep->msize = req->msize; + rep->version = "9P2000"; +} + +void +rauth(void) +{ + errno = Enoauth; +} + +void +rflush(void) +{ +} + +void +rattach(void) +{ + Xfs *xf; + Xfile *root; + Dosptr *dp; + + root = xfile(req->fid, Clean); + if(!root){ + errno = Enomem; + goto error; + } + root->xf = xf = getxfs(req->uname, req->aname); + if(!xf) + goto error; + if(xf->fmt == 0 && dosfs(xf) < 0){ + errno = Eformat; + goto error; + } + root->qid.type = QTDIR; + root->qid.path = 0; + root->qid.vers = 0; + root->xf->rootqid = root->qid; + dp = malloc(sizeof(Dosptr)); + if(dp == nil){ + errno = Enomem; + goto error; + } + root->ptr = dp; + rootfile(root); + rep->qid = root->qid; + return; +error: + if(root) + xfile(req->fid, Clunk); +} + +Xfile* +doclone(Xfile *of, int newfid) +{ + Xfile *nf, *next; + Dosptr *dp; + + nf = xfile(newfid, Clean); + if(!nf){ + errno = Enomem; + return nil; + } + dp = malloc(sizeof(Dosptr)); + if(dp == nil){ + errno = Enomem; + return nil; + } + next = nf->next; + *nf = *of; + nf->next = next; + nf->fid = req->newfid; + nf->ptr = dp; + refxfs(nf->xf, 1); + memmove(dp, of->ptr, sizeof(Dosptr)); + dp->p = nil; + dp->d = nil; + return nf; +} + +void +rwalk(void) +{ + Xfile *f, *nf; + Dosptr dp[1], savedp[1]; + int r, longtype; + Qid saveqid; + + rep->nwqid = 0; + nf = nil; + f = xfile(req->fid, Asis); + if(f == nil){ + chat("\tno xfile\n"); + goto error2; + } + if(req->fid != req->newfid){ + nf = doclone(f, req->newfid); + if(nf == nil){ + chat("\tclone failed\n"); + goto error2; + } + f = nf; + } + + saveqid = f->qid; + memmove(savedp, f->ptr, sizeof(Dosptr)); + for(; rep->nwqid < req->nwname && rep->nwqid < MAXWELEM; rep->nwqid++){ + chat("\twalking %s\n", req->wname[rep->nwqid]); + if(!(f->qid.type & QTDIR)){ + chat("\tnot dir: type=%#x\n", f->qid.type); + goto error; + } + if(strcmp(req->wname[rep->nwqid], ".") == 0){ + ; + }else if(strcmp(req->wname[rep->nwqid], "..") == 0){ + if(f->qid.path != f->xf->rootqid.path){ + r = walkup(f, dp); + if(r < 0) + goto error; + memmove(f->ptr, dp, sizeof(Dosptr)); + if(isroot(dp->addr)) + f->qid.path = f->xf->rootqid.path; + else + f->qid.path = QIDPATH(dp); + } + }else{ + fixname(req->wname[rep->nwqid]); + longtype = classifyname(req->wname[rep->nwqid]); + if(longtype==Invalid || getfile(f) < 0) + goto error; + + /* + * always do a search for the long name, + * because it could be filed as such + */ + r = searchdir(f, req->wname[rep->nwqid], dp, 0, longtype); + putfile(f); + if(r < 0) + goto error; + memmove(f->ptr, dp, sizeof(Dosptr)); + f->qid.path = QIDPATH(dp); + f->qid.type = QTFILE; + if(isroot(dp->addr)) + f->qid.path = f->xf->rootqid.path; + else if(dp->d->attr & DDIR) + f->qid.type = QTDIR; + else if(dp->d->attr & DSYSTEM){ + f->qid.type |= QTEXCL; + if(iscontig(f->xf, dp->d)) + f->qid.type |= QTAPPEND; + } +//ZZZ maybe use other bits than qtexcl & qtapppend + putfile(f); + } + rep->wqid[rep->nwqid] = f->qid; + } + return; +error: + f->qid = saveqid; + memmove(f->ptr, savedp, sizeof(Dosptr)); + if(nf != nil) + xfile(req->newfid, Clunk); +error2: + if(!errno && !rep->nwqid) + errno = Enonexist; +} + +void +ropen(void) +{ + Xfile *f; + Iosect *p; + Dosptr *dp; + int attr, omode; + + f = xfile(req->fid, Asis); + if(!f || (f->flags&Omodes)){ + errno = Eio; + return; + } + dp = f->ptr; + omode = 0; + if(!isroot(dp->paddr) && (req->mode & ORCLOSE)){ + /* + * check on parent directory of file to be deleted + */ + p = getsect(f->xf, dp->paddr); + if(p == nil){ + errno = Eio; + return; + } + attr = ((Dosdir *)&p->iobuf[dp->poffset])->attr; + putsect(p); + if(attr & DRONLY){ + errno = Eperm; + return; + } + omode |= Orclose; + }else if(req->mode & ORCLOSE) + omode |= Orclose; + if(getfile(f) < 0){ + errno = Enonexist; + return; + } + if(!isroot(dp->addr)) + attr = dp->d->attr; + else + attr = DDIR; + switch(req->mode & 7){ + case OREAD: + case OEXEC: + omode |= Oread; + break; + case ORDWR: + omode |= Oread; + /* fall through */ + case OWRITE: + omode |= Owrite; + if(attr & DRONLY){ + errno = Eperm; + goto out; + } + break; + default: + errno = Eio; + goto out; + } + if(req->mode & OTRUNC){ + if(attr & DDIR || attr & DRONLY){ + errno = Eperm; + goto out; + } + if(truncfile(f, 0) < 0){ + errno = Eio; + goto out; + } + } + f->flags |= omode; + rep->qid = f->qid; + rep->iounit = 0; +out: + putfile(f); +} + +static int +mk8dot3name(Xfile *f, Dosptr *ndp, char *name, char *sname) +{ + Dosptr tmpdp; + int i, longtype; + + if(strcmp(name, ".") == 0 || strcmp(name, "..") == 0) + return Invalid; + + /* + * always do a search for the long name, + * because it could be filed as such + */ + fixname(name); + longtype = classifyname(name); + if(longtype==Invalid || searchdir(f, name, ndp, 1, longtype) < 0) + return Invalid; + + if(longtype==Short) + return Short; + + if(longtype==ShortLower){ + /* + * alias is the upper-case version, which we + * already know does not exist. + */ + strcpy(sname, name); + for(i=0; sname[i]; i++) + if('a' <= sname[i] && sname[i] <= 'z') + sname[i] += 'A'-'a'; + return ShortLower; + } + + /* + * find alias for the long name + */ + for(i=1;; i++){ + mkalias(name, sname, i); + if(searchdir(f, sname, &tmpdp, 0, 0) < 0) + return Long; + putsect(tmpdp.p); + } +} + +/* + * fill in a directory entry for a new file + */ +static int +mkdentry(Xfs *xf, Dosptr *ndp, char *name, char *sname, int longtype, int nattr, long start, long length) +{ + Dosdir *nd; + + /* + * fill in the entry + */ + ndp->p = getsect(xf, ndp->addr); + if(ndp->p == nil + || longtype!=Short && putlongname(xf, ndp, name, sname) < 0){ + errno = Eio; + return -1; + } + + ndp->d = (Dosdir *)&ndp->p->iobuf[ndp->offset]; + nd = ndp->d; + memset(nd, 0, DOSDIRSIZE); + + if(longtype!=Short) + name = sname; + putname(name, nd); + + nd->attr = nattr; + puttime(nd, 0); + putstart(xf, nd, start); + nd->length[0] = length; + nd->length[1] = length>>8; + nd->length[2] = length>>16; + nd->length[3] = length>>24; + + ndp->p->flags |= BMOD; + + return 0; +} + +void +rcreate(void) +{ + Dosbpb *bp; + Xfile *f; + Dosptr *pdp, *ndp; + Iosect *xp; + Dosdir *pd, *xd; + char sname[13]; + long start; + int longtype, attr, omode, nattr; + + f = xfile(req->fid, Asis); + if(!f || (f->flags&Omodes) || getfile(f)<0){ + errno = Eio; + return; + } + pdp = f->ptr; + pd = pdp->d; + /* + * perm check + */ + if(isroot(pdp->addr) && pd != nil) + panic("root pd != nil"); + attr = pd ? pd->attr : DDIR; + if(!(attr & DDIR) || (attr & DRONLY)){ +badperm: + putfile(f); + errno = Eperm; + return; + } + omode = 0; + if(req->mode & ORCLOSE) + omode |= Orclose; + switch(req->mode & 7){ + case OREAD: + case OEXEC: + omode |= Oread; + break; + case ORDWR: + omode |= Oread; + /* fall through */ + case OWRITE: + omode |= Owrite; + if(req->perm & DMDIR) + goto badperm; + break; + default: + goto badperm; + } + + /* + * check the name, find the slot for the dentry, + * and find a good alias for a long name + */ + ndp = malloc(sizeof(Dosptr)); + if(ndp == nil){ + putfile(f); + errno = Enomem; + return; + } + longtype = mk8dot3name(f, ndp, req->name, sname); + chat("rcreate %s longtype %d...\n", req->name, longtype); + if(longtype == Invalid){ + free(ndp); + goto badperm; + } + + /* + * allocate first cluster, if making directory + */ + start = 0; + bp = nil; + if(req->perm & DMDIR){ + bp = f->xf->ptr; + mlock(bp); + start = falloc(f->xf); + unmlock(bp); + if(start <= 0){ + free(ndp); + putfile(f); + errno = Eio; + return; + } + } + + /* + * make the entry + */ + nattr = 0; + if((req->perm & 0222) == 0) + nattr |= DRONLY; + if(req->perm & DMDIR) + nattr |= DDIR; + + if(mkdentry(f->xf, ndp, req->name, sname, longtype, nattr, start, 0) < 0){ + if(ndp->p != nil) + putsect(ndp->p); + free(ndp); + if(start > 0) + ffree(f->xf, start); + putfile(f); + return; + } + + if(pd != nil){ + puttime(pd, 0); + pdp->p->flags |= BMOD; + } + + /* + * fix up the fid + */ + f->ptr = ndp; + f->qid.type = QTFILE; + f->qid.path = QIDPATH(ndp); + +//ZZZ set type for excl, append? + if(req->perm & DMDIR){ + f->qid.type = QTDIR; + xp = getsect(f->xf, clust2sect(bp, start)); + if(xp == nil){ + errno = Eio; + goto badio; + } + xd = (Dosdir *)&xp->iobuf[0]; + memmove(xd, ndp->d, DOSDIRSIZE); + memset(xd->name, ' ', sizeof xd->name+sizeof xd->ext); + xd->name[0] = '.'; + xd = (Dosdir *)&xp->iobuf[DOSDIRSIZE]; + if(pd) + memmove(xd, pd, DOSDIRSIZE); + else{ + memset(xd, 0, DOSDIRSIZE); + puttime(xd, 0); + xd->attr = DDIR; + } + memset(xd->name, ' ', sizeof xd->name+sizeof xd->ext); + xd->name[0] = '.'; + xd->name[1] = '.'; + xp->flags |= BMOD; + putsect(xp); + } + + f->flags |= omode; + rep->qid = f->qid; + rep->iounit = 0; + +badio: + putfile(f); + putsect(pdp->p); + free(pdp); +} + +void +rread(void) +{ + Xfile *f; + int r; + + if (!(f=xfile(req->fid, Asis)) || !(f->flags&Oread)) + goto error; + if(req->count > sizeof repdata) + req->count = sizeof repdata; + if(f->qid.type & QTDIR){ + if(getfile(f) < 0) + goto error; + r = readdir(f, repdata, req->offset, req->count); + }else{ + if(getfile(f) < 0) + goto error; + r = readfile(f, repdata, req->offset, req->count); + } + putfile(f); + if(r < 0){ +error: + errno = Eio; + }else{ + rep->count = r; + rep->data = (char*)repdata; + } +} + +void +rwrite(void) +{ + Xfile *f; + int r; + + if (!(f=xfile(req->fid, Asis)) || !(f->flags&Owrite)) + goto error; + if(getfile(f) < 0) + goto error; + r = writefile(f, req->data, req->offset, req->count); + putfile(f); + if(r < 0){ +error: + errno = Eio; + }else{ + rep->count = r; + } +} + +void +rclunk(void) +{ + xfile(req->fid, Clunk); + sync(); +} + +/* + * wipe out a dos directory entry + */ +static void +doremove(Xfs *xf, Dosptr *dp) +{ + Iosect *p; + int prevdo; + + dp->p->iobuf[dp->offset] = DOSEMPTY; + dp->p->flags |= BMOD; + for(prevdo = dp->offset-DOSDIRSIZE; prevdo >= 0; prevdo -= DOSDIRSIZE){ + if(dp->p->iobuf[prevdo+11] != 0xf) + break; + dp->p->iobuf[prevdo] = DOSEMPTY; + } + if(prevdo < 0 && dp->prevaddr != -1){ + p = getsect(xf, dp->prevaddr); + for(prevdo = ((Dosbpb*)xf->ptr)->sectsize-DOSDIRSIZE; prevdo >= 0; prevdo -= DOSDIRSIZE){ + if(p->iobuf[prevdo+11] != 0xf) + break; + p->iobuf[prevdo] = DOSEMPTY; + p->flags |= BMOD; + } + putsect(p); + } +} + +void +rremove(void) +{ + Xfile *f; + Dosptr *dp; + Iosect *parp; + Dosdir *pard; + + f = xfile(req->fid, Asis); + parp = nil; + if(f == nil){ + errno = Eio; + goto out; + } + dp = f->ptr; + if(isroot(dp->addr)){ + errno = Eperm; + goto out; + } + + /* + * can't remove if parent is read only, + * it's a non-empty directory, + * or it's a read only file in the root directory + */ + parp = getsect(f->xf, dp->paddr); + if(parp == nil + || getfile(f) < 0){ + errno = Eio; + goto out; + } + pard = (Dosdir *)&parp->iobuf[dp->poffset]; + if(!isroot(dp->paddr) && (pard->attr & DRONLY) + || (dp->d->attr & DDIR) && emptydir(f) < 0 + || isroot(dp->paddr) && (dp->d->attr&DRONLY)){ + errno = Eperm; + goto out; + } + if(truncfile(f, 0) < 0){ + errno = Eio; + goto out; + } + doremove(f->xf, f->ptr); + if(!isroot(dp->paddr)){ + puttime(pard, 0); + parp->flags |= BMOD; + } +out: + if(parp != nil) + putsect(parp); + if(f != nil) + putfile(f); + xfile(req->fid, Clunk); + sync(); +} + +static void +dostat(Xfile *f, Dir *d) +{ + Dosptr *dp; + Iosect *p; + char *name, namebuf[DOSNAMELEN]; + int islong, sum, prevdo; + + dp = f->ptr; + if(isroot(dp->addr)){ + memset(d, 0, sizeof(Dir)); + d->name = "/"; + d->qid.type = QTDIR; + d->qid.path = f->xf->rootqid.path; + d->mode = DMDIR|0777; + d->uid = "bill"; + d->muid = "bill"; + d->gid = "trog"; + }else{ + /* + * assemble any long file name + */ + sum = aliassum(dp->d); + islong = 0; + name = namebuf; + for(prevdo = dp->offset-DOSDIRSIZE; prevdo >= 0; prevdo -= DOSDIRSIZE){ + if(dp->p->iobuf[prevdo+11] != 0xf) + break; + name = getnamesect(namebuf, name, &dp->p->iobuf[prevdo], &islong, &sum, -1); + } + if(prevdo < 0 && dp->prevaddr != -1){ + p = getsect(f->xf, dp->prevaddr); + for(prevdo = ((Dosbpb*)f->xf->ptr)->sectsize-DOSDIRSIZE; prevdo >= 0; prevdo -= DOSDIRSIZE){ + if(p->iobuf[prevdo+11] != 0xf) + break; + name = getnamesect(namebuf, name, &p->iobuf[prevdo], &islong, &sum, -1); + } + putsect(p); + } + getdir(f->xf, d, dp->d, dp->addr, dp->offset); + if(islong && sum == -1 && nameok(namebuf)) + strcpy(d->name, namebuf); + } +} + +void +rstat(void) +{ + Dir dir; + Xfile *f; + + f = xfile(req->fid, Asis); + if(!f || getfile(f) < 0){ + errno = Eio; + return; + } + + dir.name = repdata; + dostat(f, &dir); + + rep->nstat = convD2M(&dir, statbuf, sizeof statbuf); + rep->stat = statbuf; + putfile(f); +} + +void +rwstat(void) +{ + Dir dir, wdir; + Xfile *f, pf; + Dosptr *dp, ndp, pdp; + Iosect *parp; + Dosdir *pard, *d, od; + char sname[13]; + ulong oaddr, ooffset; + long start, length; + int i, longtype, changes, attr; + + f = xfile(req->fid, Asis); + if(!f || getfile(f) < 0){ + errno = Eio; + return; + } + dp = f->ptr; + + if(isroot(dp->addr)){ + errno = Eperm; + goto out; + } + + changes = 0; + dir.name = repdata; + dostat(f, &dir); + if(convM2D(req->stat, req->nstat, &wdir, (char*)statbuf) != req->nstat){ + errno = Ebadstat; + goto out; + } + + /* + * To change length, must have write permission on file. + * we only allow truncates for now. + */ + if(wdir.length!=~0 && wdir.length!=dir.length){ + if(wdir.length > dir.length || !dir.mode & 0222){ + errno = Eperm; + goto out; + } + } + + /* + * no chown or chgrp + */ + if(wdir.uid[0] != '\0' && strcmp(dir.uid, wdir.uid) != 0 + || wdir.gid[0] != '\0' && strcmp(dir.gid, wdir.gid) != 0){ + errno = Eperm; + goto out; + } + + /* + * mode/mtime allowed + */ + if(wdir.mtime != ~0 && dir.mtime != wdir.mtime) + changes = 1; + + /* + * Setting DMAPPEND (make system file contiguous) + * requires setting DMEXCL (system file). + */ + if(wdir.mode != ~0){ + if((wdir.mode & 7) != ((wdir.mode >> 3) & 7) + || (wdir.mode & 7) != ((wdir.mode >> 6) & 7)){ + errno = Eperm; + goto out; + } + if((dir.mode^wdir.mode) & (DMEXCL|DMAPPEND|0777)) + changes = 1; + if((dir.mode^wdir.mode) & DMAPPEND) { + if((wdir.mode & (DMEXCL|DMAPPEND)) == DMAPPEND) { + errno = Eperm; + goto out; + } + if((wdir.mode & DMAPPEND) && makecontig(f, 0) < 0) { + errno = Econtig; + goto out; + } + } + } + + + /* + * to rename: + * 1) make up a fake clone + * 2) walk to parent + * 3) remove the old entry + * 4) create entry with new name + * 5) write correct mode/mtime info + * we need to remove the old entry before creating the new one + * to avoid a lock loop. + */ + if(wdir.name[0] != '\0' && strcmp(dir.name, wdir.name) != 0){ + if(utflen(wdir.name) >= DOSNAMELEN){ + errno = Etoolong; + goto out; + } + + /* + * grab parent directory of file to be changed and check for write perm + * rename also disallowed for read-only files in root directory + */ + parp = getsect(f->xf, dp->paddr); + if(parp == nil){ + errno = Eio; + goto out; + } + pard = (Dosdir *)&parp->iobuf[dp->poffset]; + if(!isroot(dp->paddr) && (pard->attr & DRONLY) + || isroot(dp->paddr) && (dp->d->attr&DRONLY)){ + putsect(parp); + errno = Eperm; + goto out; + } + + /* + * retrieve info from old entry + */ + oaddr = dp->addr; + ooffset = dp->offset; + d = dp->d; + od = *d; + start = getstart(f->xf, d); + length = GLONG(d->length); + attr = d->attr; + + /* + * temporarily release file to allow other directory ops: + * walk to parent, validate new name + * then remove old entry + */ + putfile(f); + pf = *f; + memset(&pdp, 0, sizeof(Dosptr)); + pdp.prevaddr = -1; + pdp.naddr = -1; + pdp.addr = dp->paddr; + pdp.offset = dp->poffset; + pdp.p = parp; + if(!isroot(pdp.addr)) + pdp.d = (Dosdir *)&parp->iobuf[pdp.offset]; + pf.ptr = &pdp; + longtype = mk8dot3name(&pf, &ndp, wdir.name, sname); + if(longtype==Invalid){ + putsect(parp); + errno = Eperm; + return; + } + if(getfile(f) < 0){ + putsect(parp); + errno = Eio; + return; + } + doremove(f->xf, dp); + putfile(f); + + /* + * search for dir entry again, since we may be able to use the old slot, + * and we need to set up the naddr field if a long name spans the block. + * create new entry. + */ + if(searchdir(&pf, wdir.name, dp, 1, longtype) < 0 + || mkdentry(pf.xf, dp, wdir.name, sname, longtype, attr, start, length) < 0){ + putsect(parp); + errno = Eio; + goto out; + } + + /* + * copy invisible fields + */ + d = dp->d; + for(i = 0; i < 2; i++) + d->ctime[i] = od.ctime[i]; + for(i = 0; i < nelem(od.cdate); i++) + d->cdate[i] = od.cdate[i]; + for(i = 0; i < nelem(od.adate); i++) + d->adate[i] = od.adate[i]; + + putsect(parp); + + /* + * relocate up other fids to the same file, if it moved + */ + f->qid.path = QIDPATH(dp); + if(oaddr != dp->addr || ooffset != dp->offset) + dosptrreloc(f, dp, oaddr, ooffset); + + /* + * copy fields that are not supposed to change + */ + if(wdir.mtime == ~0) + wdir.mtime = dir.mtime; + if(wdir.mode == ~0) + wdir.mode = dir.mode; + changes = 1; + } + + /* + * do the actual truncate + */ + if(wdir.length != ~0 && wdir.length != dir.length && truncfile(f, wdir.length) < 0) + errno = Eio; + + if(changes){ + putdir(dp->d, &wdir); + dp->p->flags |= BMOD; + } + +out: + putfile(f); + sync(); +} diff --git a/sys/src/cmd/dossrv/dosfs.h b/sys/src/cmd/dossrv/dosfs.h new file mode 100755 index 000000000..3f933039b --- /dev/null +++ b/sys/src/cmd/dossrv/dosfs.h @@ -0,0 +1,10 @@ +enum +{ + Maxfdata = 8192, + Maxiosize = IOHDRSZ+Maxfdata, +}; + +extern Fcall *req; +extern Fcall *rep; +extern char repdata[Maxfdata]; +extern uchar statbuf[STATMAX]; diff --git a/sys/src/cmd/dossrv/dossubs.c b/sys/src/cmd/dossrv/dossubs.c new file mode 100755 index 000000000..86d95674c --- /dev/null +++ b/sys/src/cmd/dossrv/dossubs.c @@ -0,0 +1,1940 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <auth.h> +#include <fcall.h> +#include "iotrack.h" +#include "dat.h" +#include "fns.h" + +static uchar isdos[256]; + +int +isdosfs(uchar *buf) +{ + /* + * When dynamic disc managers move the disc partition, + * they make it start with 0xE9. + */ + if(buf[0] == 0xE9) + return 1; + + /* + * Check if the jump displacement (magic[1]) is too short for a FAT. + * + * check now omitted due to digital cameras that use a 0 jump. + * the ecma-107 standard says this is okay and that interoperable fat + * implementations shouldn't assume this: + * http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-107.pdf, + * page 11. + */ + if(buf[0] == 0xEB && buf[2] == 0x90 /* && buf[1] >= 0x30 */) + return 1; + if(chatty) + fprint(2, "bad sig %.2ux %.2ux %.2uxn", buf[0], buf[1], buf[2]); + + return 0; +} + +int +dosfs(Xfs *xf) +{ + Iosect *p, *p1; + Dosboot *b; + Fatinfo *fi; + Dosboot32 *b32; + Dosbpb *bp; + long fisec, extflags; + int i; + + if(!isdos['a']){ + for(i = 'a'; i <= 'z'; i++) + isdos[i] = 1; + for(i = 'A'; i <= 'Z'; i++) + isdos[i] = 1; + for(i = '0'; i <= '9'; i++) + isdos[i] = 1; + isdos['$'] = 1; + isdos['%'] = 1; + isdos['''] = 1; + isdos['-'] = 1; + isdos['_'] = 1; + isdos['@'] = 1; + isdos['~'] = 1; + isdos['`'] = 1; + isdos['!'] = 1; + isdos['('] = 1; + isdos[')'] = 1; + isdos['{'] = 1; + isdos['}'] = 1; + isdos['^'] = 1; + isdos['#'] = 1; + isdos['&'] = 1; + } + + p = getsect(xf, 0); + if(p == 0) + return -1; + + b = (Dosboot*)p->iobuf; + if(b->clustsize == 0 || isdosfs(p->iobuf) == 0){ + putsect(p); + return -1; + } + + bp = malloc(sizeof(Dosbpb)); + memset(bp, 0, sizeof(Dosbpb)); /* clear lock */ + xf->ptr = bp; + xf->fmt = 1; + + bp->sectsize = GSHORT(b->sectsize); + bp->clustsize = b->clustsize; + bp->nresrv = GSHORT(b->nresrv); + bp->nfats = b->nfats; + bp->rootsize = GSHORT(b->rootsize); + bp->volsize = GSHORT(b->volsize); + if(bp->volsize == 0) + bp->volsize = GLONG(b->bigvolsize); + bp->mediadesc = b->mediadesc; + bp->fatsize = GSHORT(b->fatsize); + bp->fataddr = GSHORT(b->nresrv); + + bp->fatinfo = 0; + + if(bp->fatsize == 0){ /* is FAT32 */ + if(chatty) + bootsecdump32(2, xf, (Dosboot32*)b); + xf->isfat32 = 1; + b32 = (Dosboot32*)b; + bp->fatsize = GLONG(b32->fatsize32); + if(bp->fatsize == 0){ + putsect(p); + if(chatty) + fprint(2, "fatsize 0\n"); + return -1; + } + bp->dataaddr = bp->fataddr + bp->nfats*bp->fatsize; + bp->rootaddr = 0; + bp->rootstart = GLONG(b32->rootstart); + + /* + * disable fat mirroring? + */ + extflags = GSHORT(b32->extflags); + if(extflags & 0x0080){ + for(i = 0; i < 4; i++){ + if(extflags & (1 << i)){ + bp->fataddr += i * bp->fatsize; + bp->nfats = 1; + break; + } + } + } + + /* + * fat free list info + */ + bp->freeptr = FATRESRV; + fisec = GSHORT(b32->infospec); + if(fisec != 0 && fisec < GSHORT(b32->nresrv)){ + p1 = getsect(xf, fisec); + if(p1 != nil){ + fi = (Fatinfo*)p1->iobuf; + if(GLONG(fi->sig1) == FATINFOSIG1 && GLONG(fi->sig) == FATINFOSIG){ + bp->fatinfo = fisec; + bp->freeptr = GLONG(fi->nextfree); + bp->freeclusters = GLONG(fi->freeclust); + chat("fat info: %ld free clusters, next free %ld\n", bp->freeclusters, bp->freeptr); + } + putsect(p1); + } + } + }else{ + if(chatty) + bootdump(2, b); + bp->rootaddr = bp->fataddr + bp->nfats*bp->fatsize; + bp->rootstart = 0; + i = bp->rootsize*DOSDIRSIZE + bp->sectsize-1; + i /= bp->sectsize; + bp->dataaddr = bp->rootaddr + i; + bp->freeptr = FATRESRV; + } + bp->fatclusters = FATRESRV+(bp->volsize - bp->dataaddr)/bp->clustsize; + + if(xf->isfat32) + bp->fatbits = 32; + else if(bp->fatclusters < 4087) + bp->fatbits = 12; + else + bp->fatbits = 16; + + chat("fatbits=%d (%d clusters)...", bp->fatbits, bp->fatclusters); + for(i=0; i<b->nfats; i++) + chat("fat %d: %ld...", i, bp->fataddr+i*bp->fatsize); + chat("root: %ld...", bp->rootaddr); + chat("data: %ld...", bp->dataaddr); + putsect(p); + return 0; +} + +/* + * initialize f to the root directory + * this file has no Dosdir entry, + * so we special case it all over. + */ +void +rootfile(Xfile *f) +{ + Dosptr *dp; + + dp = f->ptr; + memset(dp, 0, sizeof(Dosptr)); + dp->prevaddr = -1; +} + +int +isroot(ulong addr) +{ + return addr == 0; +} + +int +getfile(Xfile *f) +{ + Dosptr *dp; + Iosect *p; + + dp = f->ptr; + if(dp->p) + panic("getfile"); + p = getsect(f->xf, dp->addr); + if(p == nil) + return -1; + + /* + * we could also make up a Dosdir for the root + */ + dp->d = nil; + if(!isroot(dp->addr)){ + if(f->qid.path != QIDPATH(dp)){ + chat("qid mismatch f=%#llux d=%#lux...", f->qid.path, QIDPATH(dp)); + putsect(p); + errno = Enonexist; + return -1; + } + dp->d = (Dosdir *)&p->iobuf[dp->offset]; + } + dp->p = p; + return 0; +} + +void +putfile(Xfile *f) +{ + Dosptr *dp; + + dp = f->ptr; + if(!dp->p) + panic("putfile"); + putsect(dp->p); + dp->p = nil; + dp->d = nil; +} + +long +getstart(Xfs *xf, Dosdir *d) +{ + long start; + + start = GSHORT(d->start); + if(xf->isfat32) + start |= GSHORT(d->hstart)<<16; + return start; +} + +void +putstart(Xfs *xf, Dosdir *d, long start) +{ + PSHORT(d->start, start); + if(xf->isfat32) + PSHORT(d->hstart, start>>16); +} + +/* + * return the disk cluster for the iclust cluster in f + */ +long +fileclust(Xfile *f, long iclust, int cflag) +{ + Dosbpb *bp; + Dosptr *dp; + Dosdir *d; + long start, clust, nskip, next; + + bp = f->xf->ptr; + dp = f->ptr; + d = dp->d; + next = 0; + + /* + * asking for the cluster of the root directory + * is not a well-formed question, since the root directory + * does not begin on a cluster boundary. + */ + if(!f->xf->isfat32 && isroot(dp->addr)) + return -1; + + if(f->xf->isfat32 && isroot(dp->addr)){ + start = bp->rootstart; + }else{ + start = getstart(f->xf, d); + if(start == 0){ + if(!cflag) + return -1; + mlock(bp); + start = falloc(f->xf); + unmlock(bp); + if(start <= 0) + return -1; + puttime(d, 0); + putstart(f->xf, d, start); + dp->p->flags |= BMOD; + dp->clust = 0; + } + } + if(dp->clust == 0 || iclust < dp->iclust){ + clust = start; + nskip = iclust; + }else{ + clust = dp->clust; + nskip = iclust - dp->iclust; + } + if(chatty > 1 && nskip > 0) + chat("clust %#lx, skip %ld...", clust, nskip); + if(clust <= 0) + return -1; + if(nskip > 0){ + mlock(bp); + while(--nskip >= 0){ + next = getfat(f->xf, clust); + if(chatty > 1) + chat("->%#lx", next); + if(next > 0){ + clust = next; + continue; + }else if(!cflag) + break; + if(d && (d->attr&DSYSTEM)){ + next = cfalloc(f); + if(next < 0) + break; + /* cfalloc will call putfat for us, since clust may change */ + } else { + next = falloc(f->xf); + if(next < 0) + break; + putfat(f->xf, clust, next); + } + clust = next; + } + unmlock(bp); + if(next <= 0) + return -1; + dp->clust = clust; + dp->iclust = iclust; + } + if(chatty > 1) + chat(" clust(%#lx)=%#lx...", iclust, clust); + return clust; +} + +/* + * return the disk sector for the isect disk sector in f + */ +long +fileaddr(Xfile *f, long isect, int cflag) +{ + Dosbpb *bp; + Dosptr *dp; + long clust; + + bp = f->xf->ptr; + dp = f->ptr; + if(!f->xf->isfat32 && isroot(dp->addr)){ + if(isect*bp->sectsize >= bp->rootsize*DOSDIRSIZE) + return -1; + return bp->rootaddr + isect; + } + clust = fileclust(f, isect/bp->clustsize, cflag); + if(clust < 0) + return -1; + + return clust2sect(bp, clust) + isect%bp->clustsize; +} + +/* + * translate names + */ +void +fixname(char *buf) +{ + int c; + char *p; + + p = buf; + while(c = *p){ + if(c == ':' && trspaces) + *p = ' '; + p++; + } +} + +/* + * classify the file name as one of + * Invalid - contains a bad character + * Short - short valid 8.3 name, no lowercase letters + * ShortLower - short valid 8.3 name except for lowercase letters + * Long - long name + */ +int +classifyname(char *buf) +{ + char *p, *dot; + int c, isextended, is8dot3, islower, ndot; + + p = buf; + isextended = 0; + islower = 0; + dot = nil; + ndot = 0; + while(c = (uchar)*p){ + if(c&0x80) /* UTF8 */ + isextended = 1; + else if(c == '.'){ + dot = p; + ndot++; + }else if(strchr("+,:;=[] ", c)) + isextended = 1; + else if(!isdos[c]) + return Invalid; + if('a' <= c && c <= 'z') + islower = 1; + p++; + } + + is8dot3 = (ndot==0 && p-buf <= 8) || (ndot==1 && dot-buf <= 8 && p-(dot+1) <= 3); + + if(!isextended && is8dot3){ + if(islower) + return ShortLower; + return Short; + } + return Long; +} + +/* + * make an alias for a valid long file name + */ +void +mkalias(char *name, char *sname, int id) +{ + Rune r; + char *s, *e, sid[10]; + int i, esuf, v; + + e = strrchr(name, '.'); + if(e == nil) + e = strchr(name, '\0'); + + s = name; + i = 0; + while(s < e && i < 6){ + if(isdos[(uchar)*s]) + sname[i++] = *s++; + else + s += chartorune(&r, s); + } + + v = snprint(sid, 10, "%d", id); + if(i + 1 + v > 8) + i = 8 - 1 - v; + sname[i++] = '~'; + strcpy(&sname[i], sid); + i += v; + + sname[i++] = '.'; + esuf = i + 3; + if(esuf > 12) + panic("bad mkalias"); + while(*e && i < esuf){ + if(isdos[(uchar)*e]) + sname[i++] = *e++; + else + e += chartorune(&r, e); + } + if(sname[i-1] == '.') + i--; + sname[i] = '\0'; +} + +/* + * check for valid plan 9 names, + * rewrite ' ' to ':' + */ +char isfrog[256]={ + /*NUL*/ 1, 1, 1, 1, 1, 1, 1, 1, + /*BKS*/ 1, 1, 1, 1, 1, 1, 1, 1, + /*DLE*/ 1, 1, 1, 1, 1, 1, 1, 1, + /*CAN*/ 1, 1, 1, 1, 1, 1, 1, 1, +/* [' '] 1, let's try this -rsc */ + ['/'] 1, + [0x7f] 1, +}; + +int +nameok(char *elem) +{ + while(*elem) { + if(*elem == ' ' && trspaces) + *elem = ':'; + if(isfrog[*(uchar*)elem]) + return 0; + elem++; + } + return 1; +} + +/* + * look for a directory entry matching name + * always searches for long names which match a short name + */ +int +searchdir(Xfile *f, char *name, Dosptr *dp, int cflag, int longtype) +{ + Xfs *xf; + Iosect *p; + Dosbpb *bp; + Dosdir *d; + char buf[261], *bname; + int isect, addr, o, addr1, addr2, prevaddr, prevaddr1, o1, islong, have, need, sum; + + xf = f->xf; + bp = xf->ptr; + addr1 = -1; + addr2 = -1; + prevaddr1 = -1; + o1 = 0; + islong = 0; + sum = -1; + + need = 1; + if(longtype!=Short && cflag) + need += (utflen(name) + DOSRUNE-1) / DOSRUNE; + + memset(dp, 0, sizeof(Dosptr)); + dp->prevaddr = -1; + dp->naddr = -1; + dp->paddr = ((Dosptr *)f->ptr)->addr; + dp->poffset = ((Dosptr *)f->ptr)->offset; + + have = 0; + addr = -1; + bname = nil; + for(isect=0;; isect++){ + prevaddr = addr; + addr = fileaddr(f, isect, cflag); + if(addr < 0) + break; + p = getsect(xf, addr); + if(p == 0) + break; + for(o=0; o<bp->sectsize; o+=DOSDIRSIZE){ + d = (Dosdir *)&p->iobuf[o]; + if(d->name[0] == 0x00){ + chat("end dir(0)..."); + putsect(p); + if(!cflag) + return -1; + + /* + * addr1 & o1 are the start of the dirs + * addr2 is the optional second cluster used if the long name + * entry does not fit within the addr1 cluster + * + * have tells us the number of contiguous free dirs + * starting at addr1.o1; need are necessary to hold the long name. + */ + if(addr1 < 0){ + addr1 = addr; + prevaddr1 = prevaddr; + o1 = o; + } + if(addr2 < 0 && (bp->sectsize-o)/DOSDIRSIZE + have < need){ + addr2 = fileaddr(f, isect+1, cflag); + if(addr2 < 0) + goto breakout; + }else if(addr2 < 0) + addr2 = addr; + if(addr2 == addr1) + addr2 = -1; + dp->addr = addr1; + dp->offset = o1; + dp->prevaddr = prevaddr1; + dp->naddr = addr2; + return 0; + } + if(d->name[0] == DOSEMPTY){ + if(chatty) + fprint(2, "empty dir\n"); + + have++; + if(addr1 == -1){ + addr1 = addr; + o1 = o; + prevaddr1 = prevaddr; + } + if(addr2 == -1 && have >= need) + addr2 = addr; + continue; + } + have = 0; + if(addr2 == -1) + addr1 = -1; + + dirdump(d); + if((d->attr & 0xf) == 0xf){ + bname = getnamesect(buf, bname, p->iobuf + o, &islong, &sum, 1); + continue; + } + if(d->attr & DVLABEL){ + islong = 0; + continue; + } + if(islong != 1 || sum != aliassum(d) || cistrcmp(bname, name) != 0){ + bname = buf; + getname(buf, d); + } + islong = 0; + if(cistrcmp(bname, name) != 0) + continue; + if(chatty) + fprint(2, "found\n"); + if(cflag){ + putsect(p); + return -1; + } + dp->addr = addr; + dp->prevaddr = prevaddr; + dp->offset = o; + dp->p = p; + dp->d = d; + return 0; + } + putsect(p); + } +breakout: + chat("end dir(1)..."); + return -1; +} + +int +emptydir(Xfile *f) +{ + Xfs *xf = f->xf; + Dosbpb *bp = xf->ptr; + int isect, addr, o; + Iosect *p; + Dosdir *d; + + for(isect=0;; isect++){ + addr = fileaddr(f, isect, 0); + if(addr < 0) + break; + p = getsect(xf, addr); + if(p == 0) + return -1; + for(o=0; o<bp->sectsize; o+=DOSDIRSIZE){ + d = (Dosdir *)&p->iobuf[o]; + if(d->name[0] == 0x00){ + putsect(p); + return 0; + } + if(d->name[0] == DOSEMPTY) + continue; + if(d->name[0] == '.') + continue; + if(d->attr&DVLABEL) + continue; + putsect(p); + return -1; + } + putsect(p); + } + return 0; +} + +long +readdir(Xfile *f, void *vbuf, long offset, long count) +{ + Xfs *xf; + Dosbpb *bp; + Dir dir; + int isect, addr, o, islong, sum; + Iosect *p; + Dosdir *d; + long rcnt, n; + char *name, snamebuf[8+1+3+1], namebuf[DOSNAMELEN]; + uchar *buf; + + buf = vbuf; + rcnt = 0; + xf = f->xf; + bp = xf->ptr; + if(count <= 0) + return 0; + islong = 0; + sum = -1; + name = nil; + for(isect=0;; isect++){ + addr = fileaddr(f, isect, 0); + if(addr < 0) + break; + p = getsect(xf, addr); + if(p == 0) + return -1; + for(o=0; o<bp->sectsize; o+=DOSDIRSIZE){ + d = (Dosdir *)&p->iobuf[o]; + if(d->name[0] == 0x00){ + putsect(p); + return rcnt; + } + if(d->name[0] == DOSEMPTY) + continue; + dirdump(d); + if(d->name[0] == '.'){ + if(d->name[1] == ' ' || d->name[1] == 0) + continue; + if(d->name[1] == '.' && + (d->name[2] == ' ' || d->name[2] == 0)) + continue; + } + if((d->attr & 0xf) == 0xf){ + name = getnamesect(namebuf, name, p->iobuf+o, &islong, &sum, 1); + continue; + } + if(d->attr & DVLABEL){ + islong = 0; + continue; + } + dir.name = snamebuf; + getdir(xf, &dir, d, addr, o); + if(islong == 1 && nameok(name) && sum == aliassum(d)) + dir.name = name; + islong = 0; + n = convD2M(&dir, &buf[rcnt], count - rcnt); + name = nil; + if(n <= BIT16SZ){ /* no room for next entry */ + putsect(p); + return rcnt; + } + rcnt += n; + if(offset > 0){ + offset -= rcnt; + rcnt = 0; + islong = 0; + continue; + } + if(rcnt == count){ + putsect(p); + return rcnt; + } + } + putsect(p); + } + return rcnt; +} + +/* + * set up ndp for a directory's parent + * the hardest part is setting up paddr + */ +int +walkup(Xfile *f, Dosptr *ndp) +{ + Dosbpb *bp; + Dosptr *dp; + Dosdir *xd; + Iosect *p; + long k, o, so, start, pstart, ppstart, st, ppclust; + + bp = f->xf->ptr; + dp = f->ptr; + memset(ndp, 0, sizeof(Dosptr)); + ndp->prevaddr = -1; + ndp->naddr = -1; + ndp->addr = dp->paddr; + ndp->offset = dp->poffset; + + chat("walkup: paddr=%#lx...", dp->paddr); + + /* + * root's paddr is always itself + */ + if(isroot(dp->paddr)) + return 0; + + /* + * find the start of our parent's directory + */ + p = getsect(f->xf, dp->paddr); + if(p == nil) + goto error; + xd = (Dosdir *)&p->iobuf[dp->poffset]; + dirdump(xd); + start = getstart(f->xf, xd); + chat("start=%#lx...", start); + if(start == 0) + goto error; + putsect(p); + + /* + * verify that parent's . points to itself + */ + p = getsect(f->xf, clust2sect(bp, start)); + if(p == nil) + goto error; + xd = (Dosdir *)p->iobuf; + dirdump(xd); + st = getstart(f->xf, xd); + if(xd->name[0]!='.' || xd->name[1]!=' ' || start!=st) + goto error; + + /* + * parent's .. is the next entry, and has start of parent's parent + */ + xd++; + dirdump(xd); + if(xd->name[0] != '.' || xd->name[1] != '.') + goto error; + pstart = getstart(f->xf, xd); + putsect(p); + + /* + * we're done if parent is root + */ + if(pstart == 0 || f->xf->isfat32 && pstart == bp->rootstart) + return 0; + + /* + * verify that parent's . points to itself + */ + p = getsect(f->xf, clust2sect(bp, pstart)); + if(p == 0){ + chat("getsect %ld failed\n", pstart); + goto error; + } + xd = (Dosdir *)p->iobuf; + dirdump(xd); + st = getstart(f->xf, xd); + if(xd->name[0]!='.' || xd->name[1]!=' ' || pstart!=st) + goto error; + + /* + * parent's parent's .. is the next entry, and has start of parent's parent's parent + */ + xd++; + dirdump(xd); + if(xd->name[0] != '.' || xd->name[1] != '.') + goto error; + ppstart = getstart(f->xf, xd); + putsect(p); + + /* + * open parent's parent's parent, and walk through it until parent's parent is found + * need this to find parent's parent's addr and offset + */ + ppclust = ppstart; + if(f->xf->isfat32 && ppclust == 0){ + ppclust = bp->rootstart; + chat("ppclust 0, resetting to rootstart\n"); + } + k = ppclust ? clust2sect(bp, ppclust) : bp->rootaddr; + p = getsect(f->xf, k); + if(p == nil){ + chat("getsect %ld failed\n", k); + goto error; + } + xd = (Dosdir *)p->iobuf; + dirdump(xd); + if(ppstart){ + st = getstart(f->xf, xd); + if(xd->name[0]!='.' || xd->name[1]!=' ' || ppstart!=st) + goto error; + } + for(so=1;; so++){ + for(o=0; o<bp->sectsize; o+=DOSDIRSIZE){ + xd = (Dosdir *)&p->iobuf[o]; + if(xd->name[0] == 0x00){ + chat("end dir\n"); + goto error; + } + if(xd->name[0] == DOSEMPTY) + continue; + st = getstart(f->xf, xd); + if(st == pstart) + goto out; + } + if(ppclust){ + if(so%bp->clustsize == 0){ + mlock(bp); + ppclust = getfat(f->xf, ppclust); + unmlock(bp); + if(ppclust < 0){ + chat("getfat %ld failed\n", ppclust); + goto error; + } + } + k = clust2sect(bp, ppclust) + + so%bp->clustsize; + }else{ + if(so*bp->sectsize >= bp->rootsize*DOSDIRSIZE) + goto error; + k = bp->rootaddr + so; + } + putsect(p); + p = getsect(f->xf, k); + if(p == 0){ + chat("getsect %ld failed\n", k); + goto error; + } + } +out: + putsect(p); + ndp->paddr = k; + ndp->poffset = o; + return 0; + +error: + if(p) + putsect(p); + return -1; +} + +long +readfile(Xfile *f, void *vbuf, long offset, long count) +{ + Xfs *xf = f->xf; + Dosbpb *bp = xf->ptr; + Dosptr *dp = f->ptr; + Dosdir *d = dp->d; + int isect, addr, o, c; + Iosect *p; + uchar *buf; + long length, rcnt; + + rcnt = 0; + length = GLONG(d->length); + buf = vbuf; + if(offset >= length) + return 0; + if(offset+count >= length) + count = length - offset; + isect = offset/bp->sectsize; + o = offset%bp->sectsize; + while(count > 0){ + addr = fileaddr(f, isect++, 0); + if(addr < 0) + break; + c = bp->sectsize - o; + if(c > count) + c = count; + p = getsect(xf, addr); + if(p == 0) + return -1; + memmove(&buf[rcnt], &p->iobuf[o], c); + putsect(p); + count -= c; + rcnt += c; + o = 0; + } + return rcnt; +} + +long +writefile(Xfile *f, void *vbuf, long offset, long count) +{ + Xfs *xf = f->xf; + Dosbpb *bp = xf->ptr; + Dosptr *dp = f->ptr; + Dosdir *d = dp->d; + int isect, addr = 0, o, c; + Iosect *p; + uchar *buf; + long length, rcnt = 0, dlen; + + buf = vbuf; + isect = offset/bp->sectsize; + o = offset%bp->sectsize; + while(count > 0){ + addr = fileaddr(f, isect++, 1); + if(addr < 0) + break; + c = bp->sectsize - o; + if(c > count) + c = count; + if(c == bp->sectsize){ + p = getosect(xf, addr); + p->flags = 0; + }else{ + p = getsect(xf, addr); + if(p == nil) + return -1; + } + memmove(&p->iobuf[o], &buf[rcnt], c); + p->flags |= BMOD; + putsect(p); + count -= c; + rcnt += c; + o = 0; + } + if(rcnt <= 0 && addr < 0) + return -1; + length = 0; + dlen = GLONG(d->length); + if(rcnt > 0) + length = offset+rcnt; + else if(dp->addr && dp->clust){ + c = bp->clustsize*bp->sectsize; + if(dp->iclust > (dlen+c-1)/c) + length = c*dp->iclust; + } + if(length > dlen) + PLONG(d->length, length); + puttime(d, 0); + dp->p->flags |= BMOD; + return rcnt; +} + +int +truncfile(Xfile *f, long length) +{ + Xfs *xf = f->xf; + Dosbpb *bp = xf->ptr; + Dosptr *dp = f->ptr; + Dosdir *d = dp->d; + long clust, next, n; + + mlock(bp); + clust = getstart(f->xf, d); + n = length; + if(n <= 0) + putstart(f->xf, d, 0); + else + n -= bp->sectsize; + while(clust > 0){ + next = getfat(xf, clust); + if(n <= 0) + putfat(xf, clust, 0); + else + n -= bp->clustsize*bp->sectsize; + clust = next; + } + unmlock(bp); + PLONG(d->length, length); + dp->iclust = 0; + dp->clust = 0; + dp->p->flags |= BMOD; + return 0; +} + +void +putdir(Dosdir *d, Dir *dp) +{ + if(dp->mode != ~0){ + if(dp->mode & 2) + d->attr &= ~DRONLY; + else + d->attr |= DRONLY; + if(dp->mode & DMEXCL) + d->attr |= DSYSTEM; + else + d->attr &= ~DSYSTEM; + } + if(dp->mtime != ~0) + puttime(d, dp->mtime); +} + +/* + * should extend this to deal with + * creation and access dates + */ +void +getdir(Xfs *xfs, Dir *dp, Dosdir *d, int addr, int offset) +{ + if(d == nil || addr == 0) + panic("getdir on root"); + dp->type = 0; + dp->dev = 0; + getname(dp->name, d); + + dp->qid.path = addr*(Sectorsize/DOSDIRSIZE) + + offset/DOSDIRSIZE; + dp->qid.vers = 0; + + if(d->attr & DRONLY) + dp->mode = 0444; + else + dp->mode = 0666; + dp->atime = gtime(d); + dp->mtime = dp->atime; + dp->qid.type = QTFILE; + if(d->attr & DDIR){ + dp->qid.type = QTDIR; + dp->mode |= DMDIR|0111; + dp->length = 0; + }else + dp->length = GLONG(d->length); + if(d->attr & DSYSTEM){ + dp->mode |= DMEXCL; + if(iscontig(xfs, d)) + dp->mode |= DMAPPEND; + } + + dp->uid = "bill"; + dp->muid = "bill"; + dp->gid = "trog"; +} + +void +getname(char *p, Dosdir *d) +{ + int c, i; + + for(i=0; i<8; i++){ + c = d->name[i]; + if(c == '\0' || c == ' ') + break; + if(i == 0 && c == 0x05) + c = 0xe5; + *p++ = c; + } + for(i=0; i<3; i++){ + c = d->ext[i]; + if(c == '\0' || c == ' ') + break; + if(i == 0) + *p++ = '.'; + *p++ = c; + } + *p = 0; +} + +static char* +getnamerunes(char *dst, uchar *buf, int step) +{ + int i; + Rune r; + char dbuf[DOSRUNE * UTFmax + 1], *d; + + d = dbuf; + r = 1; + for(i = 1; r && i < 11; i += 2){ + r = buf[i] | (buf[i+1] << 8); + d += runetochar(d, &r); + } + for(i = 14; r && i < 26; i += 2){ + r = buf[i] | (buf[i+1] << 8); + d += runetochar(d, &r); + } + for(i = 28; r && i < 32; i += 2){ + r = buf[i] | (buf[i+1] << 8); + d += runetochar(d, &r); + } + + if(step == 1) + dst -= d - dbuf; + + memmove(dst, dbuf, d - dbuf); + + if(step == -1){ + dst += d - dbuf; + *dst = '\0'; + } + + return dst; +} + +char* +getnamesect(char *dbuf, char *d, uchar *buf, int *islong, int *sum, int step) +{ + /* + * validation checks to make sure we're + * making up a consistent name + */ + if(buf[11] != 0xf || buf[12] != 0){ + *islong = 0; + return nil; + } + if(step == 1){ + if((buf[0] & 0xc0) == 0x40){ + *islong = buf[0] & 0x3f; + *sum = buf[13]; + d = dbuf + DOSNAMELEN; + *--d = '\0'; + }else if(*islong && *islong == buf[0] + 1 && *sum == buf[13]){ + *islong = buf[0]; + }else{ + *islong = 0; + return nil; + } + }else{ + if(*islong + 1 == (buf[0] & 0xbf) && *sum == buf[13]){ + *islong = buf[0] & 0x3f; + if(buf[0] & 0x40) + *sum = -1; + }else{ + *islong = 0; + *sum = -1; + return nil; + } + } + if(*islong > 20){ + *islong = 0; + *sum = -1; + return nil; + } + + return getnamerunes(d, buf, step); +} + +void +putname(char *p, Dosdir *d) +{ + int i; + + memset(d->name, ' ', sizeof d->name+sizeof d->ext); + for(i=0; i<sizeof d->name; i++){ + if(*p == 0 || *p == '.') + break; + d->name[i] = toupper(*p++); + } + p = strrchr(p, '.'); + if(p){ + for(i=0; i<sizeof d->ext; i++){ + if(*++p == 0) + break; + d->ext[i] = toupper(*p); + } + } +} + +static void +putnamesect(uchar *slot, Rune *longname, int curslot, int first, int sum) +{ + Rune r; + Dosdir ds; + int i, j; + + memset(&ds, 0xff, sizeof ds); + ds.attr = 0xf; + ds.reserved[0] = 0; + ds.reserved[1] = sum; + ds.start[0] = 0; + ds.start[1] = 0; + if(first) + ds.name[0] = 0x40 | curslot; + else + ds.name[0] = curslot; + memmove(slot, &ds, sizeof ds); + + j = (curslot-1) * DOSRUNE; + + for(i = 1; i < 11; i += 2){ + r = longname[j++]; + slot[i] = r; + slot[i+1] = r >> 8; + if(r == 0) + return; + } + for(i = 14; i < 26; i += 2){ + r = longname[j++]; + slot[i] = r; + slot[i+1] = r >> 8; + if(r == 0) + return; + } + for(i = 28; i < 32; i += 2){ + r = longname[j++]; + slot[i] = r; + slot[i+1] = r >> 8; + if(r == 0) + return; + } +} + +int +aliassum(Dosdir *d) +{ + int i, sum; + + if(d == nil) + return -1; + sum = 0; + for(i = 0; i < 11; i++) + sum = (((sum&1)<<7) | ((sum&0xfe)>>1)) + d->name[i]; + return sum & 0xff; +} + +int +putlongname(Xfs *xf, Dosptr *ndp, char *name, char sname[13]) +{ + Dosbpb *bp; + Dosdir tmpd; + Rune longname[DOSNAMELEN+1]; + int i, first, sum, nds, len; + + /* calculate checksum */ + putname(sname, &tmpd); + sum = aliassum(&tmpd); + + bp = xf->ptr; + first = 1; + len = utftorunes(longname, name, DOSNAMELEN); + if(chatty){ + chat("utftorunes %s =", name); + for(i=0; i<len; i++) + chat(" %.4X", longname[i]); + chat("\n"); + } + for(nds = (len + DOSRUNE-1) / DOSRUNE; nds > 0; nds--){ + putnamesect(&ndp->p->iobuf[ndp->offset], longname, nds, first, sum); + first = 0; + ndp->offset += 32; + if(ndp->offset == bp->sectsize){ + chat("long name moving over sector boundary\n"); + ndp->p->flags |= BMOD; + putsect(ndp->p); + ndp->p = nil; + + /* + * switch to the next cluster for a long entry + * naddr should be set up correctly by searchdir + */ + ndp->prevaddr = ndp->addr; + ndp->addr = ndp->naddr; + ndp->naddr = -1; + if(ndp->addr == -1) + return -1; + ndp->p = getsect(xf, ndp->addr); + if(ndp->p == nil) + return -1; + ndp->offset = 0; + ndp->d = (Dosdir *)&ndp->p->iobuf[ndp->offset]; + } + } + return 0; +} + +long +getfat(Xfs *xf, int n) +{ + Dosbpb *bp = xf->ptr; + Iosect *p; + ulong k, sect; + int o, fb; + + if(n < FATRESRV || n >= bp->fatclusters) + return -1; + fb = bp->fatbits; + k = (fb * n) >> 3; + if(k >= bp->fatsize*bp->sectsize) + panic("getfat"); + sect = k/bp->sectsize + bp->fataddr; + o = k%bp->sectsize; + p = getsect(xf, sect); + if(p == nil) + return -1; + k = p->iobuf[o++]; + if(o >= bp->sectsize){ + putsect(p); + p = getsect(xf, sect+1); + if(p == nil) + return -1; + o = 0; + } + k |= p->iobuf[o++]<<8; + if(fb == 32){ + /* fat32 is really fat28 */ + k |= p->iobuf[o++] << 16; + k |= (p->iobuf[o] & 0x0f) << 24; + fb = 28; + } + putsect(p); + if(fb == 12){ + if(n&1) + k >>= 4; + else + k &= 0xfff; + } + if(chatty > 1) + chat("fat(%#x)=%#lx...", n, k); + + /* + * This is a very strange check for out of range. + * As a concrete example, for a 16-bit FAT, + * FFF8 through FFFF all signify ``end of cluster chain.'' + * This generalizes to other-sized FATs. + */ + if(k >= (1 << fb) - 8) + return -1; + + return k; +} + +void +putfat(Xfs *xf, int n, ulong val) +{ + Fatinfo *fi; + Dosbpb *bp; + Iosect *p; + ulong k, sect, esect; + int o; + + bp = xf->ptr; + if(n < FATRESRV || n >= bp->fatclusters) + panic("putfat n=%d", n); + k = (bp->fatbits * n) >> 3; + if(k >= bp->fatsize*bp->sectsize) + panic("putfat"); + sect = k/bp->sectsize + bp->fataddr; + esect = sect + bp->nfats * bp->fatsize; + for(; sect<esect; sect+=bp->fatsize){ + o = k%bp->sectsize; + p = getsect(xf, sect); + if(p == nil) + continue; + switch(bp->fatbits){ + case 12: + if(n&1){ + p->iobuf[o] &= 0x0f; + p->iobuf[o++] |= val<<4; + if(o >= bp->sectsize){ + p->flags |= BMOD; + putsect(p); + p = getsect(xf, sect+1); + if(p == nil) + continue; + o = 0; + } + p->iobuf[o] = val>>4; + }else{ + p->iobuf[o++] = val; + if(o >= bp->sectsize){ + p->flags |= BMOD; + putsect(p); + p = getsect(xf, sect+1); + if(p == nil) + continue; + o = 0; + } + p->iobuf[o] &= 0xf0; + p->iobuf[o] |= (val>>8) & 0x0f; + } + break; + case 16: + p->iobuf[o++] = val; + p->iobuf[o] = val>>8; + break; + case 32: /* fat32 is really fat28 */ + p->iobuf[o++] = val; + p->iobuf[o++] = val>>8; + p->iobuf[o++] = val>>16; + p->iobuf[o] = (p->iobuf[o] & 0xf0) | ((val>>24) & 0x0f); + break; + default: + panic("putfat fatbits"); + } + p->flags |= BMOD; + putsect(p); + } + + if(val == 0) + bp->freeclusters++; + else + bp->freeclusters--; + + if(bp->fatinfo){ + p = getsect(xf, bp->fatinfo); + if(p != nil){ + fi = (Fatinfo*)p->iobuf; + PLONG(fi->nextfree, bp->freeptr); + PLONG(fi->freeclust, bp->freeclusters); + p->flags |= BMOD; + putsect(p); + } + } +} + +/* + * Contiguous falloc; if we can, use lastclust+1. + * Otherwise, move the file to get some space. + * If there just isn't enough contiguous space + * anywhere on disk, fail. + */ +int +cfalloc(Xfile *f) +{ + int l; + + if((l=makecontig(f, 8)) >= 0) + return l; + return makecontig(f, 1); +} + +/* + * Check whether a file is contiguous. + */ +int +iscontig(Xfs *xf, Dosdir *d) +{ + long clust, next; + + clust = getstart(xf, d); + if(clust <= 0) + return 1; + + for(;;) { + next = getfat(xf, clust); + if(next < 0) + return 1; + if(next != clust+1) + return 0; + clust = next; + } +} + +/* + * Make a file contiguous, with nextra clusters of + * free space after it for later expansion. + * Return the number of the first new cluster. + */ +int +makecontig(Xfile *f, int nextra) +{ + Dosbpb *bp; + Dosdir *d; + Dosptr *dp; + Xfs *xf; + Iosect *wp, *rp; + long clust, next, last, start, rclust, wclust, eclust, ostart; + int isok, i, n, nclust, nrun, rs, ws; + + xf = f->xf; + bp = xf->ptr; + dp = f->ptr; + d = dp->d; + + isok = 1; + nclust = 0; + clust = fileclust(f, 0, 0); + chat("clust %#lux", clust); + if(clust != -1) { + for(;;) { + nclust++; + chat("."); + next = getfat(xf, clust); + if(next <= 0) + break; + if(next != clust+1) + isok = 0; + clust = next; + } + } + chat("nclust %d\n", nclust); + + if(isok && clust != -1) { + eclust = clust+1; /* eclust = first cluster past file */ + assert(eclust == fileclust(f, 0, 0)+nclust); + for(i=0; i<nextra; i++) + if(getfat(xf, eclust+i) != 0) + break; + if(i == nextra) { /* they were all free */ + chat("eclust=%#lx, getfat eclust-1 = %#lux\n", eclust, getfat(xf, eclust-1)); + assert(getfat(xf, eclust-1) == 0xffffffff); + putfat(xf, eclust-1, eclust); + putfat(xf, eclust, 0xffffffff); + bp->freeptr = clust+1; /* to help keep the blocks free */ + return eclust; + } + } + + /* need to search for nclust+nextra contiguous free blocks */ + last = -1; + n = bp->freeptr; + nrun = 0; + for(;;){ + if(getfat(xf, n) == 0) { + if(last+1 == n) + nrun++; + else + nrun = 1; + if(nrun >= nclust+nextra) + break; + last = n; + } + if(++n >= bp->fatclusters) + n = FATRESRV; + if(n == bp->freeptr) { + errno = Econtig; + return -1; + } + } + bp->freeptr = n+1; + + /* copy old data over */ + start = n+1 - nrun; + + /* sanity check */ + for(i=0; i<nclust+nextra; i++) + assert(getfat(xf, start+i) == 0); + + chat("relocate chain %lux -> 0x%lux len %d\n", fileclust(f, 0, 0), start, nclust); + + wclust = start; + for(rclust = fileclust(f, 0, 0); rclust > 0; rclust = next){ + rs = clust2sect(bp, rclust); + ws = clust2sect(bp, wclust); + for(i=0; i<bp->clustsize; i++, rs++, ws++){ + rp = getsect(xf, rs); + if(rp == nil) + return -1; + wp = getosect(xf, ws); + assert(wp != nil); + memmove(wp->iobuf, rp->iobuf, bp->sectsize); + wp->flags = BMOD; + putsect(rp); + putsect(wp); + } + chat("move cluster %#lx -> %#lx...", rclust, wclust); + next = getfat(xf, rclust); + putfat(xf, wclust, wclust+1); + wclust++; + } + + /* now wclust points at the first new cluster; chain it in */ + chat("wclust 0x%lux start 0x%lux (fat->0x%lux) nclust %d\n", wclust, start, getfat(xf, start), nclust); + assert(wclust == start+nclust); + putfat(xf, wclust, 0xffffffff); /* end of file */ + + /* update directory entry to point at new start */ + ostart = fileclust(f, 0, 0); + putstart(xf, d, start); + + /* check our work */ + i = 0; + clust = fileclust(f, 0, 0); + if(clust != -1) { + for(;;) { + i++; + next = getfat(xf, clust); + if(next <= 0) + break; + assert(next == clust+1); + clust = next; + } + } + chat("chain check: len %d\n", i); + assert(i == nclust+1); + + /* succeeded; remove old chain. */ + for(rclust = ostart; rclust > 0; rclust = next){ + next = getfat(xf, rclust); + putfat(xf, rclust, 0); /* free cluster */ + } + + return start+nclust; +} + +int +falloc(Xfs *xf) +{ + Dosbpb *bp = xf->ptr; + Iosect *p; + int n, i, k; + + n = bp->freeptr; + for(;;){ + if(getfat(xf, n) == 0) + break; + if(++n >= bp->fatclusters) + n = FATRESRV; + if(n == bp->freeptr) + return -1; + } + bp->freeptr = n+1; + if(bp->freeptr >= bp->fatclusters) + bp->freeptr = FATRESRV; + putfat(xf, n, 0xffffffff); + k = clust2sect(bp, n); + for(i=0; i<bp->clustsize; i++){ + p = getosect(xf, k+i); + memset(p->iobuf, 0, bp->sectsize); + p->flags = BMOD; + putsect(p); + } + return n; +} + +void +ffree(Xfs *xf, long start) +{ + putfat(xf, start, 0); +} + +long +clust2sect(Dosbpb *bp, long clust) +{ + return bp->dataaddr + (clust - FATRESRV) * bp->clustsize; +} + +long +sect2clust(Dosbpb *bp, long sect) +{ + long c; + + c = (sect - bp->dataaddr) / bp->clustsize + FATRESRV; + assert(sect == clust2sect(bp, c)); + return c; +} + +void +puttime(Dosdir *d, long s) +{ + Tm *t; + ushort x; + + if(s == 0) + s = time(0); + t = localtime(s); + + x = (t->hour<<11) | (t->min<<5) | (t->sec>>1); + PSHORT(d->time, x); + x = ((t->year-80)<<9) | ((t->mon+1)<<5) | t->mday; + PSHORT(d->date, x); +} + +long +gtime(Dosdir *dp) +{ + Tm tm; + int i; + + i = GSHORT(dp->time); + tm.hour = i >> 11; + tm.min = (i >> 5) & 63; + tm.sec = (i & 31) << 1; + i = GSHORT(dp->date); + tm.year = 80 + (i >> 9); + tm.mon = ((i >> 5) & 15) - 1; + tm.mday = i & 31; + tm.zone[0] = '\0'; + tm.tzoff = 0; + tm.yday = 0; + + return tm2sec(&tm); +} + +/* + * structure dumps for debugging + */ +void +bootdump(int fd, Dosboot *b) +{ + Biobuf bp; + + Binit(&bp, fd, OWRITE); + Bprint(&bp, "magic: 0x%2.2x 0x%2.2x 0x%2.2x\n", + b->magic[0], b->magic[1], b->magic[2]); + Bprint(&bp, "version: \"%8.8s\"\n", (char*)b->version); + Bprint(&bp, "sectsize: %d\n", GSHORT(b->sectsize)); + Bprint(&bp, "clustsize: %d\n", b->clustsize); + Bprint(&bp, "nresrv: %d\n", GSHORT(b->nresrv)); + Bprint(&bp, "nfats: %d\n", b->nfats); + Bprint(&bp, "rootsize: %d\n", GSHORT(b->rootsize)); + Bprint(&bp, "volsize: %d\n", GSHORT(b->volsize)); + Bprint(&bp, "mediadesc: 0x%2.2x\n", b->mediadesc); + Bprint(&bp, "fatsize: %d\n", GSHORT(b->fatsize)); + Bprint(&bp, "trksize: %d\n", GSHORT(b->trksize)); + Bprint(&bp, "nheads: %d\n", GSHORT(b->nheads)); + Bprint(&bp, "nhidden: %ld\n", GLONG(b->nhidden)); + Bprint(&bp, "bigvolsize: %ld\n", GLONG(b->bigvolsize)); + Bprint(&bp, "driveno: %d\n", b->driveno); + Bprint(&bp, "reserved0: 0x%2.2x\n", b->reserved0); + Bprint(&bp, "bootsig: 0x%2.2x\n", b->bootsig); + Bprint(&bp, "volid: 0x%8.8lux\n", GLONG(b->volid)); + Bprint(&bp, "label: \"%11.11s\"\n", (char*)b->label); + Bterm(&bp); +} + +void +bootdump32(int fd, Dosboot32 *b) +{ + Biobuf bp; + + Binit(&bp, fd, OWRITE); + Bprint(&bp, "magic: 0x%2.2x 0x%2.2x 0x%2.2x\n", + b->magic[0], b->magic[1], b->magic[2]); + Bprint(&bp, "version: \"%8.8s\"\n", (char*)b->version); + Bprint(&bp, "sectsize: %d\n", GSHORT(b->sectsize)); + Bprint(&bp, "clustsize: %d\n", b->clustsize); + Bprint(&bp, "nresrv: %d\n", GSHORT(b->nresrv)); + Bprint(&bp, "nfats: %d\n", b->nfats); + Bprint(&bp, "rootsize: %d\n", GSHORT(b->rootsize)); + Bprint(&bp, "volsize: %d\n", GSHORT(b->volsize)); + Bprint(&bp, "mediadesc: 0x%2.2x\n", b->mediadesc); + Bprint(&bp, "fatsize: %d\n", GSHORT(b->fatsize)); + Bprint(&bp, "trksize: %d\n", GSHORT(b->trksize)); + Bprint(&bp, "nheads: %d\n", GSHORT(b->nheads)); + Bprint(&bp, "nhidden: %ld\n", GLONG(b->nhidden)); + Bprint(&bp, "bigvolsize: %ld\n", GLONG(b->bigvolsize)); + Bprint(&bp, "fatsize32: %ld\n", GLONG(b->fatsize32)); + Bprint(&bp, "extflags: %d\n", GSHORT(b->extflags)); + Bprint(&bp, "version: %d\n", GSHORT(b->version1)); + Bprint(&bp, "rootstart: %ld\n", GLONG(b->rootstart)); + Bprint(&bp, "infospec: %d\n", GSHORT(b->infospec)); + Bprint(&bp, "backupboot: %d\n", GSHORT(b->backupboot)); + Bprint(&bp, "reserved: %d %d %d %d %d %d %d %d %d %d %d %d\n", + b->reserved[0], b->reserved[1], b->reserved[2], b->reserved[3], + b->reserved[4], b->reserved[5], b->reserved[6], b->reserved[7], + b->reserved[8], b->reserved[9], b->reserved[10], b->reserved[11]); + Bterm(&bp); +} + +void +bootsecdump32(int fd, Xfs *xf, Dosboot32 *b32) +{ + Fatinfo *fi; + Iosect *p1; + int fisec, bsec, res; + + fprint(fd, "\nfat32\n"); + bootdump32(fd, b32); + res = GSHORT(b32->nresrv); + bsec = GSHORT(b32->backupboot); + if(bsec < res && bsec != 0){ + p1 = getsect(xf, bsec); + if(p1 == nil) + fprint(fd, "\ncouldn't get backup boot sector: %r\n"); + else{ + fprint(fd, "\nbackup boot\n"); + bootdump32(fd, (Dosboot32*)p1->iobuf); + putsect(p1); + } + }else if(bsec != 0xffff) + fprint(fd, "bad backup boot sector: %d reserved %d\n", bsec, res); + fisec = GSHORT(b32->infospec); + if(fisec < res && fisec != 0){ + p1 = getsect(xf, fisec); + if(p1 == nil) + fprint(fd, "\ncouldn't get fat info sector: %r\n"); + else{ + fprint(fd, "\nfat info %d\n", fisec); + fi = (Fatinfo*)p1->iobuf; + fprint(fd, "sig1: 0x%lux sb 0x%lux\n", GLONG(fi->sig1), FATINFOSIG1); + fprint(fd, "sig: 0x%lux sb 0x%lux\n", GLONG(fi->sig), FATINFOSIG); + fprint(fd, "freeclust: %lud\n", GLONG(fi->freeclust)); + fprint(fd, "nextfree: %lud\n", GLONG(fi->nextfree)); + fprint(fd, "reserved: %lud %lud %lud\n", GLONG(fi->resrv), GLONG(fi->resrv+4), GLONG(fi->resrv+8)); + putsect(p1); + } + }else if(fisec != 0xffff) + fprint(2, "bad fat info sector: %d reserved %d\n", bsec, res); +} + +void +dirdump(void *vdbuf) +{ + static char attrchar[] = "rhsvda67"; + Dosdir *d; + char *name, namebuf[DOSNAMELEN]; + char buf[128], *s, *ebuf; + uchar *dbuf; + int i; + + if(!chatty) + return; + + d = vdbuf; + + ebuf = buf + sizeof(buf); + if(d->attr == 0xf){ + dbuf = vdbuf; + name = namebuf + DOSNAMELEN; + *--name = '\0'; + name = getnamerunes(name, dbuf, 1); + seprint(buf, ebuf, "\"%s\" %2.2x %2.2ux %2.2ux %d", name, dbuf[0], dbuf[12], dbuf[13], GSHORT(d->start)); + }else{ + s = seprint(buf, ebuf, "\"%.8s.%.3s\" ", (char*)d->name, (char*)d->ext); + for(i=7; i>=0; i--) + *s++ = d->attr&(1<<i) ? attrchar[i] : '-'; + + i = GSHORT(d->time); + s = seprint(s, ebuf, " %2.2d:%2.2d:%2.2d", i>>11, (i>>5)&63, (i&31)<<1); + i = GSHORT(d->date); + s = seprint(s, ebuf, " %2.2d.%2.2d.%2.2d", 80+(i>>9), (i>>5)&15, i&31); + + i = GSHORT(d->ctime); + s = seprint(s, ebuf, " %2.2d:%2.2d:%2.2d", i>>11, (i>>5)&63, (i&31)<<1); + i = GSHORT(d->cdate); + s = seprint(s, ebuf, " %2.2d.%2.2d.%2.2d", 80+(i>>9), (i>>5)&15, i&31); + + i = GSHORT(d->adate); + s = seprint(s, ebuf, " %2.2d.%2.2d.%2.2d", 80+(i>>9), (i>>5)&15, i&31); + + seprint(s, ebuf, " %d %d", GSHORT(d->start), GSHORT(d->length)); + } + chat("%s\n", buf); +} + +int +cistrcmp(char *s1, char *s2) +{ + int c1, c2; + + while(*s1){ + c1 = *s1++; + c2 = *s2++; + + if(c1 >= 'A' && c1 <= 'Z') + c1 -= 'A' - 'a'; + + if(c2 >= 'A' && c2 <= 'Z') + c2 -= 'A' - 'a'; + + if(c1 != c2) + return c1 - c2; + } + return -*s2; +} + +int +utftorunes(Rune *rr, char *s, int n) +{ + Rune *r, *re; + int c; + + r = rr; + re = r + n - 1; + while(c = (uchar)*s){ + if(c < Runeself){ + *r = c; + s++; + }else + s += chartorune(r, s); + r++; + if(r >= re) + break; + } + *r = 0; + return r - rr; +} diff --git a/sys/src/cmd/dossrv/errstr.h b/sys/src/cmd/dossrv/errstr.h new file mode 100755 index 000000000..951e55bc0 --- /dev/null +++ b/sys/src/cmd/dossrv/errstr.h @@ -0,0 +1,17 @@ +char *errmsg[ESIZE] = { + [Enevermind] "never mind", + [Eformat] "unknown format", + [Eio] "I/O error", + [Enoauth] "dossrv: authentication not required", + [Enomem] "server out of memory", + [Enonexist] "file does not exist", + [Eperm] "permission denied", + [Enofilsys] "no file system device specified", + [Eauth] "authentication failed", + [Econtig] "out of contiguous disk space", + [Ebadfcall] "bad fcall type", + [Ebadstat] "bad stat format", + [Etoolong] "file name too long", + [Eversion] "unknown 9P version", + [Eerrstr] "system call error", +}; diff --git a/sys/src/cmd/dossrv/fns.h b/sys/src/cmd/dossrv/fns.h new file mode 100755 index 000000000..181d10564 --- /dev/null +++ b/sys/src/cmd/dossrv/fns.h @@ -0,0 +1,70 @@ +int aliassum(Dosdir*); +void bootdump32(int, Dosboot32*); +void bootdump(int, Dosboot*); +void bootsecdump32(int fd, Xfs *xf, Dosboot32 *b32); +int cfalloc(Xfile*); +void chat(char*, ...); +#pragma varargck argpos chat 1 +int cistrcmp(char*, char*); +int classifyname(char*); +Xfile *clean(Xfile*); +long clust2sect(Dosbpb*, long); +void dirdump(void*); +int dosfs(Xfs*); +void dosptrreloc(Xfile *f, Dosptr *dp, ulong addr, ulong offset); +int emptydir(Xfile*); +int eqqid(Qid, Qid); +int falloc(Xfs*); +void ffree(Xfs *xf, long start); +long fileaddr(Xfile*, long, int); +void fixname(char*); +void getdir(Xfs*, Dir*, Dosdir*, int, int); +long getfat(Xfs*, int); +int getfile(Xfile*); +void getname(char*, Dosdir*); +char *getnamesect(char*, char*, uchar*, int*, int*, int); +long getstart(Xfs *xf, Dosdir *d); +Xfs *getxfs(char*, char*); +long gtime(Dosdir *d); +void io(int srvfd); +int iscontig(Xfs *xf, Dosdir *d); +int isroot(ulong addr); +int makecontig(Xfile*, int); +void mkalias(char*, char*, int); +int nameok(char*); +void panic(char*, ...); +#pragma varargck argpos panic 1 +void putdir(Dosdir*, Dir*); +void putfat(Xfs*, int, ulong); +void putfile(Xfile*); +int putlongname(Xfs *f, Dosptr *ndp, char *name, char sname[13]); +void putname(char*, Dosdir*); +void putstart(Xfs *xf, Dosdir *d, long start); +void puttime(Dosdir*, long); +void rattach(void); +void rauth(void); +void rclone(void); +void rclunk(void); +void rcreate(void); +long readdir(Xfile*, void*, long, long); +long readfile(Xfile*, void*, long, long); +void refxfs(Xfs*, int); +void rflush(void); +void rootfile(Xfile*); +void ropen(void); +void rread(void); +void rremove(void); +void rstat(void); +void rwalk(void); +void rwrite(void); +void rwstat(void); +void rversion(void); +int searchdir(Xfile*, char*, Dosptr*, int, int); +long sect2clust(Dosbpb*, long); +int truncfile(Xfile*, long length); +int utftorunes(Rune*, char*, int); +int walkup(Xfile*, Dosptr*); +long writefile(Xfile*, void*, long, long); +char *xerrstr(int); +Xfile *xfile(int, int); +int xfspurge(void); diff --git a/sys/src/cmd/dossrv/iotrack.c b/sys/src/cmd/dossrv/iotrack.c new file mode 100755 index 000000000..2815fa8c7 --- /dev/null +++ b/sys/src/cmd/dossrv/iotrack.c @@ -0,0 +1,316 @@ +#include <u.h> +#include <libc.h> +#include "iotrack.h" +#include "dat.h" +#include "fns.h" + +#define HIOB 31 /* a prime */ +#define NIOBUF 80 + +static Iotrack hiob[HIOB+1]; /* hash buckets + lru list */ +static Iotrack iobuf[NIOBUF]; /* the real ones */ + +#define UNLINK(p, nx, pr) ((p)->pr->nx = (p)->nx, (p)->nx->pr = (p)->pr) + +#define LINK(h, p, nx, pr) ((p)->nx = (h)->nx, (p)->pr = (h), \ + (h)->nx->pr = (p), (h)->nx = (p)) + +#define HTOFRONT(h, p) ((h)->hnext != (p) && (UNLINK(p,hnext,hprev), LINK(h,p,hnext,hprev))) + +#define TOFRONT(h, p) ((h)->next != (p) && (UNLINK(p, next, prev), LINK(h,p, next, prev))) + +Iosect * +getsect(Xfs *xf, long addr) +{ + return getiosect(xf, addr, 1); +} + +Iosect * +getosect(Xfs *xf, long addr) +{ + return getiosect(xf, addr, 0); +} + +Iosect * +getiosect(Xfs *xf, long addr, int rflag) +{ + Iotrack *t; + long taddr; + int toff; + Iosect *p; + + toff = addr % Sect2trk; + taddr = addr - toff; + t = getiotrack(xf, taddr); + if(rflag && (t->flags&BSTALE)){ + if(tread(t) < 0){ + unmlock(&t->lock); + return 0; + } + t->flags &= ~BSTALE; + } + t->ref++; + p = t->tp->p[toff]; + if(p == 0){ + p = newsect(); + t->tp->p[toff] = p; + p->flags = t->flags&BSTALE; + p->lock.key = 0; + p->t = t; + p->iobuf = t->tp->buf[toff]; + } + unmlock(&t->lock); + mlock(&p->lock); + return p; +} + +void +putsect(Iosect *p) +{ + Iotrack *t; + + if(canmlock(&p->lock)) + panic("putsect"); + t = p->t; + mlock(&t->lock); + t->flags |= p->flags; + p->flags = 0; + t->ref--; + if(t->flags & BIMM){ + if(t->flags & BMOD) + twrite(t); + t->flags &= ~(BMOD|BIMM); + } + unmlock(&t->lock); + unmlock(&p->lock); +} + +Iotrack * +getiotrack(Xfs *xf, long addr) +{ + Iotrack *hp, *p; + Iotrack *mp = &hiob[HIOB]; + long h; +/* + * chat("iotrack %d,%d...", dev, addr); + */ + h = (xf->dev<<24) ^ addr; + if(h < 0) + h = ~h; + h %= HIOB; + hp = &hiob[h]; + +loop: + +/* + * look for it in the active list + */ + mlock(&hp->lock); + for(p=hp->hnext; p != hp; p=p->hnext){ + if(p->addr != addr || p->xf != xf) + continue; + unmlock(&hp->lock); + mlock(&p->lock); + if(p->addr == addr && p->xf == xf) + goto out; + unmlock(&p->lock); + goto loop; + } + unmlock(&hp->lock); +/* + * not found + * take oldest unref'd entry + */ + mlock(&mp->lock); + for(p=mp->prev; p != mp; p=p->prev) + if(p->ref == 0 && canmlock(&p->lock)){ + if(p->ref == 0) + break; + unmlock(&p->lock); + } + unmlock(&mp->lock); + if(p == mp){ + print("iotrack all ref'd\n"); + goto loop; + } + if(p->flags & BMOD){ + twrite(p); + p->flags &= ~(BMOD|BIMM); + unmlock(&p->lock); + goto loop; + } + purgetrack(p); + p->addr = addr; + p->xf = xf; + p->flags = BSTALE; +out: + mlock(&hp->lock); + HTOFRONT(hp, p); + unmlock(&hp->lock); + mlock(&mp->lock); + TOFRONT(mp, p); + unmlock(&mp->lock); + return p; +} + +void +purgetrack(Iotrack *t) +{ + int i, ref = Sect2trk; + Iosect *p; + + for(i=0; i<Sect2trk; i++){ + p = t->tp->p[i]; + if(p == 0){ + --ref; + continue; + } + if(canmlock(&p->lock)){ + freesect(p); + --ref; + t->tp->p[i] = 0; + } + } + if(t->ref != ref) + panic("purgetrack"); +} + +int +twrite(Iotrack *t) +{ + int i, ref; + + chat("[twrite %ld...", t->addr); + if(t->flags & BSTALE){ + for(ref=0,i=0; i<Sect2trk; i++) + if(t->tp->p[i]) + ++ref; + if(ref < Sect2trk){ + if(tread(t) < 0){ + chat("error]"); + return -1; + } + }else + t->flags &= ~BSTALE; + } + if(devwrite(t->xf, t->addr, t->tp->buf, Trksize) < 0){ + chat("error]"); + return -1; + } + chat(" done]"); + return 0; +} + +int +tread(Iotrack *t) +{ + int i, ref = 0; + uchar buf[Sect2trk][Sectorsize]; + + for(i=0; i<Sect2trk; i++) + if(t->tp->p[i]) + ++ref; + chat("[tread %ld+%ld...", t->addr, t->xf->offset); + if(ref == 0){ + if(devread(t->xf, t->addr, t->tp->buf, Trksize) < 0){ + chat("error]"); + return -1; + } + chat("done]"); + t->flags &= ~BSTALE; + return 0; + } + if(devread(t->xf, t->addr, buf, Trksize) < 0){ + chat("error]"); + return -1; + } + for(i=0; i<Sect2trk; i++) + if(t->tp->p[i] == 0){ + memmove(t->tp->buf[i], buf[i], Sectorsize); + chat("%d ", i); + } + chat("done]"); + t->flags &= ~BSTALE; + return 0; +} + +void +purgebuf(Xfs *xf) +{ + Iotrack *p; + + for(p=&iobuf[0]; p<&iobuf[NIOBUF]; p++){ + if(p->xf != xf) + continue; + mlock(&p->lock); + if(p->xf == xf){ + if(p->flags & BMOD) + twrite(p); + p->flags = BSTALE; + purgetrack(p); + } + unmlock(&p->lock); + } +} + +void +sync(void) +{ + Iotrack *p; + + for(p=&iobuf[0]; p<&iobuf[NIOBUF]; p++){ + if(!(p->flags & BMOD)) + continue; + mlock(&p->lock); + if(p->flags & BMOD){ + twrite(p); + p->flags &= ~(BMOD|BIMM); + } + unmlock(&p->lock); + } +} + +void +iotrack_init(void) +{ + Iotrack *mp, *p; + + for (mp=&hiob[0]; mp<&hiob[HIOB]; mp++) + mp->hprev = mp->hnext = mp; + mp->prev = mp->next = mp; + + for (p=&iobuf[0]; p<&iobuf[NIOBUF]; p++) { + p->hprev = p->hnext = p; + p->prev = p->next = p; + TOFRONT(mp, p); + p->tp = sbrk(sizeof(Track)); + memset(p->tp->p, 0, sizeof p->tp->p); + } +} + +static MLock freelock; +static Iosect * freelist; + +Iosect * +newsect(void) +{ + Iosect *p; + + mlock(&freelock); + if(p = freelist) /* assign = */ + freelist = p->next; + else + p = malloc(sizeof(Iosect)); + unmlock(&freelock); + p->next = 0; + return p; +} + +void +freesect(Iosect *p) +{ + mlock(&freelock); + p->next = freelist; + freelist = p; + unmlock(&freelock); +} diff --git a/sys/src/cmd/dossrv/iotrack.h b/sys/src/cmd/dossrv/iotrack.h new file mode 100755 index 000000000..39b39c306 --- /dev/null +++ b/sys/src/cmd/dossrv/iotrack.h @@ -0,0 +1,69 @@ +typedef struct MLock MLock; +typedef struct Iosect Iosect; +typedef struct Iotrack Iotrack; +typedef struct Track Track; +typedef struct Xfs Xfs; + +struct MLock +{ + char key; +}; + +struct Iosect +{ + Iosect *next; + short flags; + MLock lock; + Iotrack *t; + uchar * iobuf; +}; + +struct Iotrack +{ + short flags; + Xfs * xf; + long addr; + Iotrack *next; /* in lru list */ + Iotrack *prev; + Iotrack *hnext; /* in hash list */ + Iotrack *hprev; + MLock lock; + int ref; + Track *tp; +}; + +enum{ + Sectorsize = 512, + Sect2trk = 9, + Trksize = Sectorsize*Sect2trk +}; + +struct Track +{ + Iosect *p[Sect2trk]; + uchar buf[Sect2trk][Sectorsize]; +}; + +#define BMOD (1<<0) +#define BIMM (1<<1) +#define BSTALE (1<<2) + +Iosect* getiosect(Xfs*, long, int); +Iosect* getosect(Xfs*, long); +Iosect* getsect(Xfs*, long); +Iosect* newsect(void); +Iotrack* getiotrack(Xfs*, long); +int canmlock(MLock*); +int devcheck(Xfs*); +int devread(Xfs*, long, void*, long); +int devwrite(Xfs*, long, void*, long); +int tread(Iotrack*); +int twrite(Iotrack*); +void freesect(Iosect*); +void iotrack_init(void); +void mlock(MLock*); +void purgebuf(Xfs*); +void purgetrack(Iotrack*); +void putsect(Iosect*); +void sync(void); +void unmlock(MLock*); diff --git a/sys/src/cmd/dossrv/lock.c b/sys/src/cmd/dossrv/lock.c new file mode 100755 index 000000000..20d77ccd6 --- /dev/null +++ b/sys/src/cmd/dossrv/lock.c @@ -0,0 +1,38 @@ +#include <u.h> +#include <libc.h> +#include "iotrack.h" +#include "dat.h" +#include "fns.h" + +void +mlock(MLock *l) +{ + + if(l->key != 0 && l->key != 1) + panic("uninitialized lock"); + if (l->key) + panic("lock"); + l->key = 1; +} + +void +unmlock(MLock *l) +{ + + if(l->key != 0 && l->key != 1) + panic("uninitialized unlock"); + if (!l->key) + panic("unlock"); + l->key = 0; +} + +int +canmlock(MLock *l) +{ + if(l->key != 0 && l->key != 1) + panic("uninitialized canlock"); + if (l->key) + return 0; + l->key = 1; + return 1; +} diff --git a/sys/src/cmd/dossrv/mkfile b/sys/src/cmd/dossrv/mkfile new file mode 100755 index 000000000..932018319 --- /dev/null +++ b/sys/src/cmd/dossrv/mkfile @@ -0,0 +1,29 @@ +</$objtype/mkfile + +TARG=dossrv +OFILES=\ + xfssrv.$O\ + xfile.$O\ + dosfs.$O\ + dossubs.$O\ + iotrack.$O\ + lock.$O\ + chat.$O\ + devio.$O\ + +HFILES=dat.h\ + dosfs.h\ + fns.h\ + iotrack.h\ + +BIN=/$objtype/bin + +UPDATE=\ + mkfile\ + $HFILES\ + ${OFILES:%.$O=%.c}\ + ${TARG:%=/386/bin/%}\ + +</sys/src/cmd/mkone + +xfssrv.$O: errstr.h diff --git a/sys/src/cmd/dossrv/xfile.c b/sys/src/cmd/dossrv/xfile.c new file mode 100755 index 000000000..6dbfb40d4 --- /dev/null +++ b/sys/src/cmd/dossrv/xfile.c @@ -0,0 +1,255 @@ +#include <u.h> +#include <libc.h> +#include "iotrack.h" +#include "dat.h" +#include "fns.h" + +#define FIDMOD 127 /* prime */ + +static Xfs *xhead; +static Xfile *xfiles[FIDMOD], *freelist; +static MLock xlock, xlocks[FIDMOD], freelock; + +static int +okmode(int omode, int fmode) +{ + if(omode == OREAD) + return fmode & 4; + /* else ORDWR */ + return (fmode & 6) == 6; +} + +Xfs * +getxfs(char *user, char *name) +{ + Xfs *xf, *fxf; + Dir *dir; + Qid dqid; + char *p, *q; + long offset; + int fd, omode; + + USED(user); + if(name==nil || name[0]==0) + name = deffile; + if(name == nil){ + errno = Enofilsys; + return 0; + } + + /* + * If the name passed is of the form 'name:offset' then + * offset is used to prime xf->offset. This allows accessing + * a FAT-based filesystem anywhere within a partition. + * Typical use would be to mount a filesystem in the presence + * of a boot manager programme at the beginning of the disc. + */ + offset = 0; + if(p = strrchr(name, ':')){ + *p++ = 0; + offset = strtol(p, &q, 0); + chat("name %s, offset %ld\n", p, offset); + if(offset < 0 || p == q){ + errno = Enofilsys; + return 0; + } + offset *= Sectorsize; + } + + if(readonly) + omode = OREAD; + else + omode = ORDWR; + fd = open(name, omode); + + if(fd < 0 && omode==ORDWR){ + omode = OREAD; + fd = open(name, omode); + } + + if(fd < 0){ + chat("can't open %s: %r\n", name); + errno = Eerrstr; + return 0; + } + dir = dirfstat(fd); + if(dir == nil){ + errno = Eio; + close(fd); + return 0; + } + + dqid = dir->qid; + free(dir); + mlock(&xlock); + for(fxf=0,xf=xhead; xf; xf=xf->next){ + if(xf->ref == 0){ + if(fxf == 0) + fxf = xf; + continue; + } + if(!eqqid(xf->qid, dqid)) + continue; + if(strcmp(xf->name, name) != 0 || xf->dev < 0) + continue; + if(devcheck(xf) < 0) /* look for media change */ + continue; + if(offset && xf->offset != offset) + continue; + chat("incref \"%s\", dev=%d...", xf->name, xf->dev); + ++xf->ref; + unmlock(&xlock); + close(fd); + return xf; + } + if(fxf == nil){ + fxf = malloc(sizeof(Xfs)); + if(fxf == nil){ + unmlock(&xlock); + close(fd); + errno = Enomem; + return nil; + } + fxf->next = xhead; + xhead = fxf; + } + chat("alloc \"%s\", dev=%d...", name, fd); + fxf->name = strdup(name); + fxf->ref = 1; + fxf->qid = dqid; + fxf->dev = fd; + fxf->fmt = 0; + fxf->offset = offset; + fxf->ptr = nil; + fxf->isfat32 = 0; + fxf->omode = omode; + unmlock(&xlock); + return fxf; +} + +void +refxfs(Xfs *xf, int delta) +{ + mlock(&xlock); + xf->ref += delta; + if(xf->ref == 0){ + chat("free \"%s\", dev=%d...", xf->name, xf->dev); + free(xf->name); + free(xf->ptr); + purgebuf(xf); + if(xf->dev >= 0){ + close(xf->dev); + xf->dev = -1; + } + } + unmlock(&xlock); +} + +Xfile * +xfile(int fid, int flag) +{ + Xfile **hp, *f, *pf; + int k; + + k = ((ulong)fid) % FIDMOD; + hp = &xfiles[k]; + mlock(&xlocks[k]); + pf = nil; + for(f=*hp; f; f=f->next){ + if(f->fid == fid) + break; + pf = f; + } + if(f && pf){ + pf->next = f->next; + f->next = *hp; + *hp = f; + } + switch(flag){ + default: + panic("xfile"); + case Asis: + unmlock(&xlocks[k]); + return (f && f->xf && f->xf->dev < 0) ? nil : f; + case Clean: + break; + case Clunk: + if(f){ + *hp = f->next; + unmlock(&xlocks[k]); + clean(f); + mlock(&freelock); + f->next = freelist; + freelist = f; + unmlock(&freelock); + } else + unmlock(&xlocks[k]); + return nil; + } + unmlock(&xlocks[k]); + if(f) + return clean(f); + mlock(&freelock); + if(f = freelist){ /* assign = */ + freelist = f->next; + unmlock(&freelock); + } else { + unmlock(&freelock); + f = malloc(sizeof(Xfile)); + if(f == nil){ + errno = Enomem; + return nil; + } + } + mlock(&xlocks[k]); + f->next = *hp; + *hp = f; + unmlock(&xlocks[k]); + f->fid = fid; + f->flags = 0; + f->qid = (Qid){0,0,0}; + f->xf = nil; + f->ptr = nil; + return f; +} + +Xfile * +clean(Xfile *f) +{ + if(f->ptr){ + free(f->ptr); + f->ptr = nil; + } + if(f->xf){ + refxfs(f->xf, -1); + f->xf = nil; + } + f->flags = 0; + f->qid = (Qid){0,0,0}; + return f; +} + +/* + * the file at <addr, offset> has moved + * relocate the dos entries of all fids in the same file + */ +void +dosptrreloc(Xfile *f, Dosptr *dp, ulong addr, ulong offset) +{ + int i; + Xfile *p; + Dosptr *xdp; + + for(i=0; i < FIDMOD; i++){ + for(p = xfiles[i]; p != nil; p = p->next){ + xdp = p->ptr; + if(p != f && p->xf == f->xf + && xdp != nil && xdp->addr == addr && xdp->offset == offset){ + memmove(xdp, dp, sizeof(Dosptr)); + xdp->p = nil; + xdp->d = nil; + p->qid.path = QIDPATH(xdp); + } + } + } +} diff --git a/sys/src/cmd/dossrv/xfssrv.c b/sys/src/cmd/dossrv/xfssrv.c new file mode 100755 index 000000000..3766a170d --- /dev/null +++ b/sys/src/cmd/dossrv/xfssrv.c @@ -0,0 +1,204 @@ +#include <u.h> +#include <libc.h> +#include <auth.h> +#include <fcall.h> +#include "iotrack.h" +#include "dat.h" +#include "dosfs.h" +#include "fns.h" + +#include "errstr.h" + +#define Reqsize (sizeof(Fcall)+Maxfdata) +Fcall *req; +Fcall *rep; + +uchar mdata[Maxiosize]; +char repdata[Maxfdata]; +uchar statbuf[STATMAX]; +int errno; +char errbuf[ERRMAX]; +void rmservice(void); +char srvfile[64]; +char *deffile; +int doabort; +int trspaces; + +void (*fcalls[])(void) = { + [Tversion] rversion, + [Tflush] rflush, + [Tauth] rauth, + [Tattach] rattach, + [Twalk] rwalk, + [Topen] ropen, + [Tcreate] rcreate, + [Tread] rread, + [Twrite] rwrite, + [Tclunk] rclunk, + [Tremove] rremove, + [Tstat] rstat, + [Twstat] rwstat, +}; + +void +usage(void) +{ + fprint(2, "usage: %s [-v] [-s] [-f devicefile] [srvname]\n", argv0); + exits("usage"); +} + +void +main(int argc, char **argv) +{ + int stdio, srvfd, pipefd[2]; + + rep = malloc(sizeof(Fcall)); + req = malloc(Reqsize); + if(rep == nil || req == nil) + panic("out of memory"); + stdio = 0; + ARGBEGIN{ + case ':': + trspaces = 1; + break; + case 'r': + readonly = 1; + break; + case 'v': + ++chatty; + break; + case 'f': + deffile = ARGF(); + break; + case 's': + stdio = 1; + break; + case 'p': + doabort = 1; + break; + default: + usage(); + }ARGEND + + if(argc == 0) + strcpy(srvfile, "#s/dos"); + else if(argc == 1) + snprint(srvfile, sizeof srvfile, "#s/%s", argv[0]); + else + usage(); + + if(stdio){ + pipefd[0] = 0; + pipefd[1] = 1; + }else{ + close(0); + close(1); + open("/dev/null", OREAD); + open("/dev/null", OWRITE); + if(pipe(pipefd) < 0) + panic("pipe"); + srvfd = create(srvfile, OWRITE|ORCLOSE, 0600); + if(srvfd < 0) + panic(srvfile); + fprint(srvfd, "%d", pipefd[0]); + close(pipefd[0]); + atexit(rmservice); + fprint(2, "%s: serving %s\n", argv0, srvfile); + } + srvfd = pipefd[1]; + + switch(rfork(RFNOWAIT|RFNOTEG|RFFDG|RFPROC|RFNAMEG)){ + case -1: + panic("fork"); + default: + _exits(0); + case 0: + break; + } + + iotrack_init(); + + if(!chatty){ + close(2); + open("#c/cons", OWRITE); + } + + io(srvfd); + exits(0); +} + +void +io(int srvfd) +{ + int n, pid; + + pid = getpid(); + + fmtinstall('F', fcallfmt); + for(;;){ + /* + * reading from a pipe or a network device + * will give an error after a few eof reads. + * however, we cannot tell the difference + * between a zero-length read and an interrupt + * on the processes writing to us, + * so we wait for the error. + */ + n = read9pmsg(srvfd, mdata, sizeof mdata); + if(n < 0) + break; + if(n == 0) + continue; + if(convM2S(mdata, n, req) == 0) + continue; + + if(chatty) + fprint(2, "dossrv %d:<-%F\n", pid, req); + + errno = 0; + if(!fcalls[req->type]) + errno = Ebadfcall; + else + (*fcalls[req->type])(); + if(errno){ + rep->type = Rerror; + rep->ename = xerrstr(errno); + }else{ + rep->type = req->type + 1; + rep->fid = req->fid; + } + rep->tag = req->tag; + if(chatty) + fprint(2, "dossrv %d:->%F\n", pid, rep); + n = convS2M(rep, mdata, sizeof mdata); + if(n == 0) + panic("convS2M error on write"); + if(write(srvfd, mdata, n) != n) + panic("mount write"); + } + chat("server shut down"); +} + +void +rmservice(void) +{ + remove(srvfile); +} + +char * +xerrstr(int e) +{ + if (e < 0 || e >= sizeof errmsg/sizeof errmsg[0]) + return "no such error"; + if(e == Eerrstr){ + errstr(errbuf, sizeof errbuf); + return errbuf; + } + return errmsg[e]; +} + +int +eqqid(Qid q1, Qid q2) +{ + return q1.path == q2.path && q1.type == q2.type && q1.vers == q2.vers; +} |