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/dosfs.c |
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/cmd/dossrv/dosfs.c')
-rwxr-xr-x | sys/src/cmd/dossrv/dosfs.c | 916 |
1 files changed, 916 insertions, 0 deletions
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(); +} |