2011-03-30 12:46:40 +00:00
|
|
|
#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);
|
2018-12-23 21:43:29 +00:00
|
|
|
if(p == nil)
|
2011-03-30 12:46:40 +00:00
|
|
|
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);
|
2018-12-24 00:21:47 +00:00
|
|
|
chat("fat info: %ld free clusters, next free %ld\n",
|
|
|
|
bp->freeclusters, bp->freeptr);
|
2011-03-30 12:46:40 +00:00
|
|
|
}
|
|
|
|
putsect(p1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}else{
|
|
|
|
if(chatty)
|
|
|
|
bootdump(2, b);
|
|
|
|
bp->rootaddr = bp->fataddr + bp->nfats*bp->fatsize;
|
|
|
|
bp->rootstart = 0;
|
2018-12-24 00:21:47 +00:00
|
|
|
bp->dataaddr = bp->rootaddr + (bp->rootsize*DOSDIRSIZE + bp->sectsize-1)/bp->sectsize;
|
2011-03-30 12:46:40 +00:00
|
|
|
bp->freeptr = FATRESRV;
|
|
|
|
}
|
2018-12-24 00:21:47 +00:00
|
|
|
bp->fatclusters = FATRESRV + (bp->volsize - bp->dataaddr)/bp->clustsize;
|
2011-03-30 12:46:40 +00:00
|
|
|
|
|
|
|
if(xf->isfat32)
|
|
|
|
bp->fatbits = 32;
|
|
|
|
else if(bp->fatclusters < 4087)
|
|
|
|
bp->fatbits = 12;
|
|
|
|
else
|
|
|
|
bp->fatbits = 16;
|
|
|
|
|
2018-12-22 19:49:24 +00:00
|
|
|
chat("fatbits=%d (%ld clusters)...", bp->fatbits, bp->fatclusters);
|
2011-03-30 12:46:40 +00:00
|
|
|
for(i=0; i<b->nfats; i++)
|
2018-12-22 19:49:24 +00:00
|
|
|
chat("fat %d: %lld...", i, bp->fataddr+i*bp->fatsize);
|
|
|
|
chat("root: %lld...", bp->rootaddr);
|
|
|
|
chat("data: %lld...", bp->dataaddr);
|
2011-03-30 12:46:40 +00:00
|
|
|
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
|
2018-12-22 19:49:24 +00:00
|
|
|
isroot(vlong addr)
|
2011-03-30 12:46:40 +00:00
|
|
|
{
|
|
|
|
return addr == 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
getfile(Xfile *f)
|
|
|
|
{
|
|
|
|
Dosptr *dp;
|
|
|
|
Iosect *p;
|
|
|
|
|
|
|
|
dp = f->ptr;
|
2018-12-24 00:21:47 +00:00
|
|
|
if(dp->p != nil)
|
2011-03-30 12:46:40 +00:00
|
|
|
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)){
|
2018-12-22 19:49:24 +00:00
|
|
|
chat("qid mismatch f=%#llux d=%#llux...", f->qid.path, QIDPATH(dp));
|
2011-03-30 12:46:40 +00:00
|
|
|
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;
|
2018-12-24 00:21:47 +00:00
|
|
|
if(dp->p == nil)
|
2011-03-30 12:46:40 +00:00
|
|
|
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
|
|
|
|
*/
|
2018-12-22 19:49:24 +00:00
|
|
|
vlong
|
2011-03-30 12:46:40 +00:00
|
|
|
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;
|
2018-12-22 19:49:24 +00:00
|
|
|
int isect, o, o1, islong, have, need, sum;
|
|
|
|
vlong addr, addr1, addr2, prevaddr, prevaddr1;
|
2011-03-30 12:46:40 +00:00
|
|
|
|
|
|
|
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);
|
2018-12-23 21:43:29 +00:00
|
|
|
if(p == nil)
|
2011-03-30 12:46:40 +00:00
|
|
|
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;
|
2018-12-22 19:49:24 +00:00
|
|
|
int isect, o;
|
|
|
|
vlong addr;
|
2011-03-30 12:46:40 +00:00
|
|
|
Iosect *p;
|
|
|
|
Dosdir *d;
|
|
|
|
|
|
|
|
for(isect=0;; isect++){
|
|
|
|
addr = fileaddr(f, isect, 0);
|
|
|
|
if(addr < 0)
|
|
|
|
break;
|
|
|
|
p = getsect(xf, addr);
|
2018-12-23 21:43:29 +00:00
|
|
|
if(p == nil)
|
2011-03-30 12:46:40 +00:00
|
|
|
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
|
2015-10-05 02:31:31 +00:00
|
|
|
readdir(Xfile *f, void *vbuf, vlong offset, long count)
|
2011-03-30 12:46:40 +00:00
|
|
|
{
|
|
|
|
Xfs *xf;
|
|
|
|
Dosbpb *bp;
|
|
|
|
Dir dir;
|
2018-12-22 19:49:24 +00:00
|
|
|
int isect, o, islong, sum;
|
|
|
|
vlong addr;
|
2011-03-30 12:46:40 +00:00
|
|
|
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);
|
2018-12-23 21:43:29 +00:00
|
|
|
if(p == nil)
|
2011-03-30 12:46:40 +00:00
|
|
|
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;
|
2018-12-22 19:49:24 +00:00
|
|
|
long o, so, start, pstart, ppstart, st, ppclust;
|
|
|
|
vlong k;
|
2011-03-30 12:46:40 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
|
2018-12-22 19:49:24 +00:00
|
|
|
chat("walkup: paddr=%#llx...", dp->paddr);
|
2011-03-30 12:46:40 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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));
|
2018-12-23 21:43:29 +00:00
|
|
|
if(p == nil){
|
2011-03-30 12:46:40 +00:00
|
|
|
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){
|
2018-12-22 19:49:24 +00:00
|
|
|
chat("getsect %lld failed\n", k);
|
2011-03-30 12:46:40 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
2018-12-24 00:21:47 +00:00
|
|
|
k = clust2sect(bp, ppclust) + so%bp->clustsize;
|
2011-03-30 12:46:40 +00:00
|
|
|
}else{
|
|
|
|
if(so*bp->sectsize >= bp->rootsize*DOSDIRSIZE)
|
|
|
|
goto error;
|
|
|
|
k = bp->rootaddr + so;
|
|
|
|
}
|
|
|
|
putsect(p);
|
|
|
|
p = getsect(f->xf, k);
|
2018-12-23 21:43:29 +00:00
|
|
|
if(p == nil){
|
2018-12-22 19:49:24 +00:00
|
|
|
chat("getsect %lld failed\n", k);
|
2011-03-30 12:46:40 +00:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
out:
|
|
|
|
putsect(p);
|
|
|
|
ndp->paddr = k;
|
|
|
|
ndp->poffset = o;
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
error:
|
2018-12-24 00:21:47 +00:00
|
|
|
if(p != nil)
|
2011-03-30 12:46:40 +00:00
|
|
|
putsect(p);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
long
|
2015-10-05 02:31:31 +00:00
|
|
|
readfile(Xfile *f, void *vbuf, vlong offset, long count)
|
2011-03-30 12:46:40 +00:00
|
|
|
{
|
|
|
|
Xfs *xf = f->xf;
|
|
|
|
Dosbpb *bp = xf->ptr;
|
|
|
|
Dosptr *dp = f->ptr;
|
|
|
|
Dosdir *d = dp->d;
|
2018-12-22 19:49:24 +00:00
|
|
|
int isect, o, c;
|
|
|
|
vlong addr;
|
2011-03-30 12:46:40 +00:00
|
|
|
Iosect *p;
|
|
|
|
uchar *buf;
|
2015-10-05 02:31:31 +00:00
|
|
|
ulong length, rcnt;
|
|
|
|
|
|
|
|
if(offset >= MAXFILELEN)
|
|
|
|
return 0;
|
|
|
|
if(offset+count > MAXFILELEN)
|
|
|
|
count = MAXFILELEN - offset;
|
2011-03-30 12:46:40 +00:00
|
|
|
|
|
|
|
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);
|
2018-12-23 21:43:29 +00:00
|
|
|
if(p == nil)
|
2011-03-30 12:46:40 +00:00
|
|
|
return -1;
|
|
|
|
memmove(&buf[rcnt], &p->iobuf[o], c);
|
|
|
|
putsect(p);
|
|
|
|
count -= c;
|
|
|
|
rcnt += c;
|
|
|
|
o = 0;
|
|
|
|
}
|
|
|
|
return rcnt;
|
|
|
|
}
|
|
|
|
|
|
|
|
long
|
2015-10-05 02:31:31 +00:00
|
|
|
writefile(Xfile *f, void *vbuf, vlong offset, long count)
|
2011-03-30 12:46:40 +00:00
|
|
|
{
|
|
|
|
Xfs *xf = f->xf;
|
|
|
|
Dosbpb *bp = xf->ptr;
|
|
|
|
Dosptr *dp = f->ptr;
|
|
|
|
Dosdir *d = dp->d;
|
2018-12-22 19:49:24 +00:00
|
|
|
int isect, o, c;
|
|
|
|
vlong addr;
|
2011-03-30 12:46:40 +00:00
|
|
|
Iosect *p;
|
|
|
|
uchar *buf;
|
2015-10-05 02:31:31 +00:00
|
|
|
ulong length, rcnt, dlen;
|
|
|
|
|
|
|
|
if(offset >= MAXFILELEN)
|
|
|
|
return 0;
|
|
|
|
if(offset+count > MAXFILELEN)
|
|
|
|
count = MAXFILELEN - offset;
|
2011-03-30 12:46:40 +00:00
|
|
|
|
2015-10-05 02:31:31 +00:00
|
|
|
rcnt = 0;
|
|
|
|
addr = 0;
|
2011-03-30 12:46:40 +00:00
|
|
|
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;
|
|
|
|
}
|
2015-10-05 02:31:31 +00:00
|
|
|
if(addr < 0)
|
2011-03-30 12:46:40 +00:00
|
|
|
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)
|
2018-12-22 19:49:24 +00:00
|
|
|
length = (ulong)c*dp->iclust;
|
2011-03-30 12:46:40 +00:00
|
|
|
}
|
|
|
|
if(length > dlen)
|
|
|
|
PLONG(d->length, length);
|
|
|
|
puttime(d, 0);
|
|
|
|
dp->p->flags |= BMOD;
|
|
|
|
return rcnt;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2015-10-05 02:31:31 +00:00
|
|
|
truncfile(Xfile *f, vlong length)
|
2011-03-30 12:46:40 +00:00
|
|
|
{
|
|
|
|
Xfs *xf = f->xf;
|
|
|
|
Dosbpb *bp = xf->ptr;
|
|
|
|
Dosptr *dp = f->ptr;
|
|
|
|
Dosdir *d = dp->d;
|
2015-10-05 02:31:31 +00:00
|
|
|
long clust, next;
|
|
|
|
vlong n;
|
|
|
|
|
|
|
|
if(length > MAXFILELEN)
|
|
|
|
return -1;
|
2011-03-30 12:46:40 +00:00
|
|
|
|
|
|
|
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
|
2018-12-22 19:49:24 +00:00
|
|
|
getdir(Xfs *xfs, Dir *dp, Dosdir *d, vlong addr, int offset)
|
2011-03-30 12:46:40 +00:00
|
|
|
{
|
|
|
|
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
|
2018-12-23 21:43:29 +00:00
|
|
|
dp->length = GLONG(d->length);
|
2011-03-30 12:46:40 +00:00
|
|
|
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;
|
2012-04-14 11:21:31 +00:00
|
|
|
*p++ = tolower(c);
|
2011-03-30 12:46:40 +00:00
|
|
|
}
|
|
|
|
for(i=0; i<3; i++){
|
|
|
|
c = d->ext[i];
|
|
|
|
if(c == '\0' || c == ' ')
|
|
|
|
break;
|
|
|
|
if(i == 0)
|
|
|
|
*p++ = '.';
|
2012-04-14 11:21:31 +00:00
|
|
|
*p++ = tolower(c);
|
2011-03-30 12:46:40 +00:00
|
|
|
}
|
|
|
|
*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
|
2018-12-22 19:49:24 +00:00
|
|
|
getfat(Xfs *xf, long n)
|
2011-03-30 12:46:40 +00:00
|
|
|
{
|
|
|
|
Dosbpb *bp = xf->ptr;
|
|
|
|
Iosect *p;
|
2018-12-22 19:49:24 +00:00
|
|
|
ulong k, o;
|
|
|
|
vlong sect;
|
|
|
|
int fb;
|
2011-03-30 12:46:40 +00:00
|
|
|
|
|
|
|
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)
|
2018-12-22 19:49:24 +00:00
|
|
|
chat("fat(%#lx)=%#lux...", n, k);
|
2011-03-30 12:46:40 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
2018-12-22 19:49:24 +00:00
|
|
|
if(k >= (1UL << fb) - 8)
|
2011-03-30 12:46:40 +00:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
return k;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
putfat(Xfs *xf, int n, ulong val)
|
|
|
|
{
|
|
|
|
Fatinfo *fi;
|
|
|
|
Dosbpb *bp;
|
|
|
|
Iosect *p;
|
2018-12-22 19:49:24 +00:00
|
|
|
ulong k;
|
|
|
|
vlong sect, esect;
|
2011-03-30 12:46:40 +00:00
|
|
|
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;
|
2018-12-22 19:49:24 +00:00
|
|
|
int isok, i, n, nclust, nrun;
|
|
|
|
vlong rs, ws;
|
2011-03-30 12:46:40 +00:00
|
|
|
|
|
|
|
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);
|
2020-04-25 17:57:17 +00:00
|
|
|
assert(wp != nil);
|
2011-03-30 12:46:40 +00:00
|
|
|
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;
|
2020-04-05 01:46:47 +00:00
|
|
|
int n, i;
|
|
|
|
vlong k;
|
2011-03-30 12:46:40 +00:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2018-12-22 19:49:24 +00:00
|
|
|
vlong
|
2011-03-30 12:46:40 +00:00
|
|
|
clust2sect(Dosbpb *bp, long clust)
|
|
|
|
{
|
2018-12-22 19:49:24 +00:00
|
|
|
return bp->dataaddr + ((vlong)(clust - FATRESRV) * bp->clustsize);
|
2011-03-30 12:46:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
long
|
2018-12-22 19:49:24 +00:00
|
|
|
sect2clust(Dosbpb *bp, vlong sect)
|
2011-03-30 12:46:40 +00:00
|
|
|
{
|
2018-12-22 19:49:24 +00:00
|
|
|
return ((sect - bp->dataaddr) / bp->clustsize) + FATRESRV;
|
2011-03-30 12:46:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
2015-06-04 02:34:08 +00:00
|
|
|
PSHORT(d->adate, x);
|
2011-03-30 12:46:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
long
|
|
|
|
gtime(Dosdir *dp)
|
|
|
|
{
|
|
|
|
Tm tm;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
i = GSHORT(dp->time);
|
2019-12-03 07:22:02 +00:00
|
|
|
|
|
|
|
memset(&tm, 0, sizeof(tm));
|
2011-03-30 12:46:40 +00:00
|
|
|
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));
|
2018-12-23 21:43:29 +00:00
|
|
|
Bprint(&bp, "nhidden: %lud\n", GLONG(b->nhidden));
|
|
|
|
Bprint(&bp, "bigvolsize: %lud\n", GLONG(b->bigvolsize));
|
2011-03-30 12:46:40 +00:00
|
|
|
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));
|
2018-12-23 21:43:29 +00:00
|
|
|
Bprint(&bp, "nhidden: %lud\n", GLONG(b->nhidden));
|
|
|
|
Bprint(&bp, "bigvolsize: %lud\n", GLONG(b->bigvolsize));
|
|
|
|
Bprint(&bp, "fatsize32: %lud\n", GLONG(b->fatsize32));
|
2011-03-30 12:46:40 +00:00
|
|
|
Bprint(&bp, "extflags: %d\n", GSHORT(b->extflags));
|
|
|
|
Bprint(&bp, "version: %d\n", GSHORT(b->version1));
|
2018-12-23 21:43:29 +00:00
|
|
|
Bprint(&bp, "rootstart: %lud\n", GLONG(b->rootstart));
|
2011-03-30 12:46:40 +00:00
|
|
|
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);
|
2015-06-04 02:34:08 +00:00
|
|
|
if(bsec != 0 && bsec != 0xffff){
|
|
|
|
if(bsec >= res)
|
|
|
|
fprint(fd, "bad backup boot sector: %d reserved %d\n", bsec, res);
|
|
|
|
else {
|
|
|
|
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);
|
|
|
|
}
|
2011-03-30 12:46:40 +00:00
|
|
|
}
|
2015-06-04 02:34:08 +00:00
|
|
|
}
|
2011-03-30 12:46:40 +00:00
|
|
|
fisec = GSHORT(b32->infospec);
|
2015-06-04 02:34:08 +00:00
|
|
|
if(fisec != 0 && fisec != 0xffff){
|
|
|
|
if(fisec >= res)
|
|
|
|
fprint(2, "bad fat info sector: %d reserved %d\n", fisec, res);
|
|
|
|
else {
|
|
|
|
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);
|
|
|
|
}
|
2011-03-30 12:46:40 +00:00
|
|
|
}
|
2015-06-04 02:34:08 +00:00
|
|
|
}
|
2011-03-30 12:46:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
2015-06-04 02:34:08 +00:00
|
|
|
if((d->attr & 0xf) == 0xf){
|
2011-03-30 12:46:40 +00:00
|
|
|
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);
|
2015-06-04 02:34:08 +00:00
|
|
|
s = seprint(s, ebuf, " %2.2d:%2.2d:%2.2d+%d", i>>11, (i>>5)&63, (i&31)<<1, (int)d->ctimetenth);
|
2011-03-30 12:46:40 +00:00
|
|
|
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);
|
|
|
|
|
2018-12-23 21:43:29 +00:00
|
|
|
seprint(s, ebuf, " %d %lud", GSHORT(d->start), GLONG(d->length));
|
2011-03-30 12:46:40 +00:00
|
|
|
}
|
|
|
|
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;
|
|
|
|
}
|