930 lines
17 KiB
C
930 lines
17 KiB
C
/*
|
|
* This file is part of the UCB release of Plan 9. It is subject to the license
|
|
* terms in the LICENSE file found in the top-level directory of this
|
|
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
|
|
* part of the UCB release of Plan 9, including this file, may be copied,
|
|
* modified, propagated, or distributed except according to the terms contained
|
|
* in the LICENSE file.
|
|
*/
|
|
|
|
#include <u.h>
|
|
#include <libc.h>
|
|
#include <auth.h>
|
|
#include <9P2000.h>
|
|
#include "iotrack.h"
|
|
#include "dat.h"
|
|
#include "dosfs.h"
|
|
#include "fns.h"
|
|
|
|
extern int putlongname(Xfs *xf, Dosptr *ndp, char *name, char sname[13]);
|
|
|
|
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 = jehanne_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 = jehanne_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);
|
|
jehanne_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;
|
|
jehanne_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(jehanne_strcmp(req->wname[rep->nwqid], ".") == 0){
|
|
;
|
|
}else if(jehanne_strcmp(req->wname[rep->nwqid], "..") == 0){
|
|
if(f->qid.path != f->xf->rootqid.path){
|
|
r = walkup(f, dp);
|
|
if(r < 0)
|
|
goto error;
|
|
jehanne_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;
|
|
jehanne_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;
|
|
jehanne_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 & NP_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 & NP_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 NP_OREAD:
|
|
case NP_OEXEC:
|
|
omode |= Oread;
|
|
break;
|
|
case NP_ORDWR:
|
|
omode |= Oread;
|
|
/* fall through */
|
|
case NP_OWRITE:
|
|
omode |= Owrite;
|
|
if(attr & DRONLY){
|
|
errno = Eperm;
|
|
goto out;
|
|
}
|
|
break;
|
|
default:
|
|
errno = Eio;
|
|
goto out;
|
|
}
|
|
if(req->mode & NP_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(jehanne_strcmp(name, ".") == 0 || jehanne_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.
|
|
*/
|
|
jehanne_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,
|
|
int32_t start, int32_t 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;
|
|
jehanne_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];
|
|
int32_t 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 & NP_ORCLOSE)
|
|
omode |= Orclose;
|
|
switch(req->mode & 7){
|
|
case NP_OREAD:
|
|
case NP_OEXEC:
|
|
omode |= Oread;
|
|
break;
|
|
case NP_ORDWR:
|
|
omode |= Oread;
|
|
/* fall through */
|
|
case NP_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 = jehanne_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){
|
|
jehanne_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){
|
|
jehanne_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);
|
|
jehanne_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];
|
|
jehanne_memmove(xd, ndp->d, DOSDIRSIZE);
|
|
jehanne_memset(xd->name, ' ', sizeof xd->name+sizeof xd->ext);
|
|
xd->name[0] = '.';
|
|
xd = (Dosdir *)&xp->iobuf[DOSDIRSIZE];
|
|
if(pd)
|
|
jehanne_memmove(xd, pd, DOSDIRSIZE);
|
|
else{
|
|
jehanne_memset(xd, 0, DOSDIRSIZE);
|
|
puttime(xd, 0);
|
|
xd->attr = DDIR;
|
|
}
|
|
jehanne_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);
|
|
jehanne_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)){
|
|
jehanne_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))
|
|
jehanne_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 = jehanne_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];
|
|
uint32_t oaddr, ooffset;
|
|
int32_t 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(jehanne_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' && jehanne_strcmp(dir.uid, wdir.uid) != 0
|
|
|| wdir.gid[0] != '\0' && jehanne_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' && jehanne_strcmp(dir.name, wdir.name) != 0){
|
|
if(jehanne_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;
|
|
jehanne_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();
|
|
}
|