/*
* 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);
}
abort();
return -1;
}
/*
* 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;
}
/*
* 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;
}
}
/*
* 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;