mirror of
https://github.com/open-simh/simtools.git
synced 2026-02-02 14:52:29 +00:00
file systems. It supports reading and writing as well as a number
of other operations, such as octal dump, file system initialize,
and file system check ("clean").
This was originally maintained as a Subversion repository at
svn://akdesign.dyndns.org/flx/branches/V2.6.
as suggested by Timothe Litt on the SIMH mailing list.
1545 lines
42 KiB
C
1545 lines
42 KiB
C
/* rsts file processing subroutines */
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
|
|
#include "flx.h"
|
|
#include "fldef.h"
|
|
#include "fip.h"
|
|
#include "diskio.h"
|
|
#include "rtime.h"
|
|
#include "filename.h"
|
|
|
|
byte fibuf[BLKSIZE]; /* buffer for directories */
|
|
byte *sattbufp; /* pointer to SATT buffer */
|
|
int sattsize; /* size of SATT.SYS file in bytes */
|
|
long pcns; /* number of pack clusters on this disk */
|
|
long sattlbn; /* start LBN of SATT */
|
|
long satptr; /* current allocation pointer (PCN) */
|
|
int womsat; /* TRUE if SATT needs to be written back */
|
|
long fiblk; /* current block in FIBUF */
|
|
int fiblkw; /* TRUE if FIBUF needs to be written back */
|
|
|
|
void fbwrite (void) /* write current block from fibuf */
|
|
{
|
|
rwrite (fiblk, BLKSIZE, fibuf);
|
|
fiblkw = FALSE;
|
|
}
|
|
|
|
void checkwrite (void)
|
|
{
|
|
if (fiblkw) fbwrite ();
|
|
}
|
|
|
|
void fbread (long block) /* read block into fibuf if needed */
|
|
{
|
|
if (fiblk != block)
|
|
{
|
|
checkwrite ();
|
|
rread (block, BLKSIZE, fibuf);
|
|
fiblk = block;
|
|
}
|
|
}
|
|
|
|
void readdcn (long dcn)
|
|
{
|
|
fbread (dcntolbn(dcn));
|
|
}
|
|
|
|
long i,k; /* current directory entry pointers */
|
|
|
|
/* ulk unpacks a rsts directory link, returning the LBN in i and
|
|
* the byte offset in k.
|
|
* it returns 0 if ok, BADLINK if not. bad" means block offset out
|
|
* of range (should be less than cluster size), cluster number 7,
|
|
* or byte offset 760 octal (belongs to fdcm).
|
|
*/
|
|
|
|
int ulk (word link)
|
|
{
|
|
int clu, blk;
|
|
|
|
k = (link & ul_eno); /* k = byte offset to entry */
|
|
clu = (link & ul_clo) >> sl_clo; /* cluster number */
|
|
blk = (link & ul_blo) >> sl_blo; /* block in cluster */
|
|
if (sw.debug != NULL)
|
|
printf ("ulk(%o), k=%lo, clu=%d, blk=%d, clumap=%d\n",
|
|
link, k, clu, blk, clumap->uent[clu]);
|
|
if (blk >= clumap->uclus ||
|
|
clu > 6 ||
|
|
k == 0760 ||
|
|
clumap->uent[clu] == 0)
|
|
return (BADLINK);
|
|
i = blk + dcntolbn(clumap->uent[clu]); /* LBN of entry */
|
|
return (0); /* ok */
|
|
}
|
|
|
|
/* readlk reads the directory block pointed to by the supplied link.
|
|
* i and k are set as for ulk. If the link is null, this routine
|
|
* returns FALSE; otherwise it returns TRUE. readlk2 works similarly,
|
|
* but it unconditionally reads what the link points to; thus it can be
|
|
* used to read the label blockette of a directory (and readlk cannot!!!)
|
|
* readlktbl is similar to readlk, except that it is meant to be called
|
|
* when the block currently in fibuf is the GFD name entry link table --
|
|
* which doesn't have a cluster map. curgfd must be set for it to work.
|
|
*/
|
|
|
|
void readlk2 (word link)
|
|
{
|
|
if (ulk (link))
|
|
rabort(CORRUPT); /* unpack the link */
|
|
fbread (i); /* read the directory block */
|
|
}
|
|
|
|
int readlk (word link)
|
|
{
|
|
if (NULLINK(link))
|
|
return (FALSE); /* reject null link */
|
|
readlk2 (link); /* otherwise read it */
|
|
return (TRUE);
|
|
}
|
|
|
|
int readlktbl (word link)
|
|
{
|
|
if (NULLINK(link))
|
|
return (FALSE); /* null link, exit now */
|
|
if (link & ul_clo)
|
|
fbread (curgfd); /* get gfd block with fdcm */
|
|
else
|
|
fbread (curgfd + ((link & ul_blo) >> sl_blo));
|
|
return (readlk (link)); /* now do the actual read */
|
|
}
|
|
|
|
/* utility routines for getclu */
|
|
|
|
/* both of these scan the satt from "start" through "last", looking
|
|
* to allocate a chunk of "clucount" clusters, with clustersize
|
|
* "clusiz" (i.e., aligned on "clusiz" boundary) -- both of the latter
|
|
* being expressed as a count of pack clusters.
|
|
* scanbytes works for clusiz >= 8, where the scan is for whole bytes of
|
|
* zero. scanbits works for smaller clusiz values, and looks for fields
|
|
* of zero bits. In both cases, if a spot is found, it is allocated,
|
|
* satptr is set to the next cluster after the allocated area, and the
|
|
* start DCN returned; zero means failure (and SATT and satptr are
|
|
* unchanged).
|
|
*/
|
|
|
|
long scanbytes (long start, long last, int clusiz, long clucount)
|
|
{
|
|
long clusizbyt, clu, cluoff, clucountbyt, found;
|
|
byte *s;
|
|
|
|
clusizbyt = clusiz / 8; /* byte alignment needed */
|
|
clucountbyt = clucount / 8; /* allocation size in bytes */
|
|
s = sattbufp + (start / 8); /* byte pointer to start of scan */
|
|
for (clu = start; clu <= last; clu += clusiz, s+= clusizbyt)
|
|
{
|
|
if (*s != 0)
|
|
continue; /* if not free, keep scanning */
|
|
found = TRUE; /* found something... */
|
|
for (cluoff = 1; cluoff < clucountbyt; cluoff++)
|
|
if (*(s + cluoff) != 0)
|
|
{
|
|
cluoff = DOWN(cluoff,clusizbyt);
|
|
s += cluoff;
|
|
clu += cluoff * 8;
|
|
found = FALSE; /* never mind... */
|
|
break;
|
|
}
|
|
if (!found)
|
|
continue; /* keep going if no luck */
|
|
for (cluoff = 0; cluoff < clucountbyt; cluoff++) *s++ = 0xff;
|
|
satptr = clu + clucount; /* update satptr */
|
|
MARKS; /* SATT is dirty */
|
|
return (pcntodcn(clu));
|
|
}
|
|
return (0); /* nothing found */
|
|
}
|
|
|
|
long scanbits (long start, long last, int clusiz, long clucount)
|
|
{
|
|
long mask, mask1, mask2, cluoff, clu, found;
|
|
byte *s, *s2;
|
|
|
|
clucount /= clusiz; /* make it count of file clusters */
|
|
mask1 = (1 << clusiz) - 1; /* mask to match on */
|
|
s = sattbufp + start / 8; /* byte pointer to start of scan */
|
|
mask = mask1 << (start % 8); /* form starting bit field */
|
|
for (clu = start; clu <= last; clu += clusiz)
|
|
{
|
|
if ((*s & mask) != 0) /* not all free, keep looking... */
|
|
{
|
|
if (mask < 0x80)
|
|
mask <<= clusiz;
|
|
else
|
|
{
|
|
mask = mask1;
|
|
s++;
|
|
}
|
|
continue;
|
|
}
|
|
found = TRUE; /* found something... */
|
|
mask2 = mask; /* now look for contiguous piece */
|
|
s2 = s;
|
|
for (cluoff = 0; cluoff < clucount; cluoff++)
|
|
{
|
|
if ((*s2 & mask2) != 0) /* not all free, keep looking... */
|
|
{
|
|
clu += cluoff;
|
|
if (mask2 < 0x80)
|
|
mask2 <<= clusiz;
|
|
else
|
|
{
|
|
mask2 = mask1;
|
|
s2++;
|
|
}
|
|
s = s2;
|
|
mask = mask2; /* continue just past here */
|
|
found = FALSE; /* never mind... */
|
|
break;
|
|
}
|
|
if (mask2 < 0x80)
|
|
mask2 <<= clusiz;
|
|
else
|
|
{
|
|
mask2 = mask1;
|
|
s2++;
|
|
}
|
|
}
|
|
if (!found)
|
|
continue; /* keep going if no luck */
|
|
for (cluoff = 0; cluoff < clucount; cluoff++)
|
|
{
|
|
*s |= mask;
|
|
if (mask < 0x80)
|
|
mask <<= clusiz;
|
|
else
|
|
{
|
|
mask = mask1;
|
|
s++;
|
|
}
|
|
}
|
|
satptr = clu + clucount; /* update satptr */
|
|
MARKS; /* SATT is dirty */
|
|
return (pcntodcn(clu));
|
|
}
|
|
return (0); /* nothing found */
|
|
}
|
|
|
|
/* getclu allocates clusters of the specified size, for a file of specified
|
|
* total size. before calling this routine, the pack should be mounted
|
|
* read/write to ensure a rebuild is forced if things abort after this point.
|
|
* If there is no room, zero is returned. Otherwise the starting DCN
|
|
* is returned.
|
|
* A single allocation is done, so the "size" argument should be equal to
|
|
* the clustersize unless a contiguous allocation is being done.
|
|
*/
|
|
|
|
int pcs; /* pack clustersize */
|
|
|
|
long getclu (int clusiz, long size)
|
|
{
|
|
long start, alloc, clucount;
|
|
|
|
if (sw.debug != NULL)
|
|
printf ("getclu(%d,%ld)\n", clusiz, size);
|
|
if (sattsize == 0)
|
|
rabort(INTERNAL);
|
|
clusiz /= pcs; /* fcs as count of clusters */
|
|
clucount = size / pcs; /* ditto for total size wanted */
|
|
if (clusiz <= 0)
|
|
rabort(INTERNAL);
|
|
if (clucount <= 0)
|
|
rabort(INTERNAL);
|
|
start = UP(satptr,clusiz); /* align start of scan */
|
|
if (clusiz < 8) /* scanning bitwise */
|
|
{
|
|
alloc = scanbits (start, pcns - clucount, clusiz, clucount);
|
|
if (alloc)
|
|
return (alloc); /* found it in rest of satt */
|
|
return (scanbits (0, start - clusiz, clusiz, clucount));
|
|
}
|
|
else /* scanning whole bytes */
|
|
{
|
|
alloc = scanbytes (start, pcns - clucount, clusiz, clucount);
|
|
if (alloc)
|
|
return (alloc); /* found it in rest of satt */
|
|
return (scanbytes (0, start - clusiz, clusiz, clucount));
|
|
}
|
|
}
|
|
|
|
/* retclu returns a single cluster. "pos" is the DCN of the cluster,
|
|
* (i.e., as found in retrieval entries); "clusiz" is the file clustersize.
|
|
*/
|
|
|
|
void retclu (long pos, int clusiz)
|
|
{
|
|
long m, n;
|
|
byte *s;
|
|
|
|
if (sw.debug != NULL)
|
|
printf ("retclu(%lo,%d)\n", pos, clusiz);
|
|
if (sattsize == 0)
|
|
rabort(INTERNAL);
|
|
pos = dcntopcn(pos); /* convert to pcn */
|
|
clusiz /= pcs; /* fcs as count of clusters */
|
|
if (clusiz <= 0)
|
|
rabort(INTERNAL);
|
|
s = sattbufp + pos / 8; /* byte pointer to start of cluster */
|
|
if (clusiz < 8) /* scanning bitwise */
|
|
{
|
|
m = (1 << clusiz) - 1; /* mask to match on */
|
|
m <<= (pos % 8); /* form starting bit field */
|
|
*s &= ~m; /* free this cluster */
|
|
}
|
|
else /* scanning whole bytes */
|
|
{
|
|
clusiz /= 8; /* change to count of bytes to free */
|
|
for (n = 0; n < clusiz; n++) *s++ = 0;
|
|
}
|
|
MARKS; /* mark SATT dirty */
|
|
}
|
|
|
|
int pflags; /* pack flags */
|
|
int plevel; /* pack structure revision level */
|
|
long mfddcn; /* DCN of start of MFD */
|
|
long mfdlbn; /* LBN of start of MFD */
|
|
char pname[7]; /* pack ID in ascii */
|
|
|
|
void readlabel (void)
|
|
{
|
|
packlabel *p;
|
|
|
|
readdcn (1); /* get pack label */
|
|
p = use(packlabel,0);
|
|
pcs = p->ppcs; /* get PCS */
|
|
pflags = p->pstat; /* get pack flags */
|
|
if (pflags & uc_new)
|
|
{
|
|
plevel = p->plvl; /* get RDS level */
|
|
mfddcn = p->mdcn; /* get MFD pointer */
|
|
}
|
|
else
|
|
{
|
|
plevel = 0; /* set to 0 if old pack */
|
|
mfddcn = 1; /* MFD is at 1 for old format */
|
|
}
|
|
mfdlbn = dcntolbn(mfddcn); /* for convenience, LBN also */
|
|
r50toascii2 (p->pckid, pname, FALSE); /* translate pack ID */
|
|
}
|
|
|
|
long curgfd; /* LBN of start of current GFD */
|
|
long curqblbn; /* LBN where current ppn quota block lives */
|
|
word curqb; /* and link pointing to it */
|
|
int entptr; /* current directory entry allocation pointer */
|
|
word nextlink; /* link to next file */
|
|
word prevlink; /* link to prececessor of file */
|
|
word nextppnlink; /* link to next PPN for RDS 0 */
|
|
word prevppnlink; /* link to prececessor of PPN for RDS 0*/
|
|
|
|
void setppn (firqb *f, int proj, int prog, word ppnent, int which)
|
|
{
|
|
f->cproj = proj;
|
|
f->cprog = prog;
|
|
curqb = 0; /* haven't found quota block yet */
|
|
if (which == gfddcntbl) /* looking for UFD */
|
|
{
|
|
ppnent = dcntolbn(ppnent);
|
|
fbread (ppnent); /* read it */
|
|
prevlink = 0;
|
|
nextlink = use(ufdlabel,0)->ulnk;
|
|
entptr = sizeof (ufdlabel);
|
|
}
|
|
else readlktbl (ppnent);
|
|
}
|
|
|
|
word nextppn (firqb *f, int which) /* find next ppn */
|
|
{
|
|
int firstproj, firstprog, lastproj, lastprog;
|
|
int j, n;
|
|
word ppnent;
|
|
word curlink;
|
|
gfdne *d;
|
|
|
|
nextlink = 0; /* assume nothing found */
|
|
if (plevel == RDS0) /* if old pack */
|
|
{
|
|
readlktbl (nextppnlink); /* read next MFD NE */
|
|
while (!NULLINK(nextppnlink))
|
|
{
|
|
readlk (nextppnlink); /* read it */
|
|
curlink = nextppnlink;
|
|
d = use(gfdne,k);
|
|
nextppnlink = d->ulnk; /* point to next one */
|
|
if ((d->ustat & (us_del | us_ufd)) == us_ufd
|
|
&& (f->proj == 255 || f->proj == (d->unam[0] >> 8))
|
|
&& (f->prog == 255 || f->prog == (d->unam[0] & 0xff)))
|
|
{
|
|
f->cproj = d->unam[0] >> 8;
|
|
f->cprog = d->unam[0] & 0xff;
|
|
if (which == gfddcntbl) /* looking for UFD */
|
|
{
|
|
ppnent = dcntolbn(d->uar);
|
|
if (ppnent == 0)
|
|
/* PPN without UFD, skip */
|
|
continue;
|
|
fbread (ppnent); /* read it */
|
|
prevlink = 0;
|
|
nextlink = use(ufdlabel,0)->ulnk;
|
|
entptr = sizeof (ufdlabel);
|
|
}
|
|
else ppnent = curlink;
|
|
return (ppnent);
|
|
}
|
|
prevppnlink = curlink;
|
|
}
|
|
return (0);
|
|
}
|
|
firstproj = f->cproj;
|
|
if (f->proj == 255) lastproj = 254;
|
|
else lastproj = f->proj;
|
|
if (f->prog == 255)
|
|
{
|
|
firstprog = 0;
|
|
lastprog = 254;
|
|
}
|
|
else firstprog = lastprog = f->prog;
|
|
n = f->cprog + 1; /* next prog number to try */
|
|
for (j = firstproj; j <= lastproj; j++)
|
|
{
|
|
fbread (mfdlbn + gfddcntbl);
|
|
if ((curgfd = dcntolbn(fibufw[j])) == 0)
|
|
continue;
|
|
fbread (curgfd + which);
|
|
for ( ; n <= lastprog; n++)
|
|
{
|
|
if ((ppnent = fibufw[n]) == 0)
|
|
continue;
|
|
setppn (f, j, n, ppnent, which);
|
|
return (ppnent);
|
|
}
|
|
n = firstprog; /* next project, start at firstprog */
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
word initfilescan (firqb *f, int which) /* setup file scan to the beginning */
|
|
{
|
|
int firstproj, firstprog, lastproj, lastprog;
|
|
int j, n;
|
|
word ppnent;
|
|
|
|
nextlink = prevlink = 0; /* assume nothing found */
|
|
if (plevel == RDS0) /* if old pack */
|
|
{
|
|
prevppnlink = 0;
|
|
curgfd = mfdlbn; /* pretent MFD is also GFD */
|
|
fbread (mfdlbn); /* read start of MFD */
|
|
nextppnlink = use(packlabel,0)->ulnk;
|
|
return (nextppn (f, which)); /* and look for first match */
|
|
}
|
|
if ((f->flags & f_name) == 0)
|
|
{
|
|
f->cproj = f->proj;
|
|
f->cprog = f->prog;
|
|
if (f->proj == 255)
|
|
{
|
|
fbread (mfdlbn);
|
|
return (mfddcn);
|
|
}
|
|
if (f->prog == 255)
|
|
{
|
|
fbread (mfdlbn + gfddcntbl);
|
|
if ((ppnent = fibufw[f->proj]) == 0)
|
|
return (0);
|
|
curgfd = dcntolbn (ppnent);
|
|
fbread (curgfd);
|
|
return (ppnent);
|
|
}
|
|
firstproj = lastproj = f->proj;
|
|
firstprog = lastprog = f->prog;
|
|
}
|
|
else
|
|
{
|
|
if (f->proj == 255)
|
|
{
|
|
firstproj = 0;
|
|
lastproj = 254;
|
|
}
|
|
else firstproj = lastproj = f->proj;
|
|
if (f->prog == 255)
|
|
{
|
|
firstprog = 0;
|
|
lastprog = 254;
|
|
}
|
|
else firstprog = lastprog = f->prog;
|
|
}
|
|
for (j = firstproj; j <= lastproj; j++)
|
|
{
|
|
fbread (mfdlbn + gfddcntbl);
|
|
if ((curgfd = dcntolbn(fibufw[j])) == 0)
|
|
continue;
|
|
fbread (curgfd + which);
|
|
for (n = firstprog; n <= lastprog; n++)
|
|
{
|
|
if (n == 0 && j == 0)
|
|
continue;
|
|
if ((ppnent = fibufw[n]) == 0)
|
|
continue;
|
|
setppn (f, j, n, ppnent, which);
|
|
return (ppnent);
|
|
}
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
int wmatch (const char *wn, const char *n) /* wildcard match */
|
|
{
|
|
while (*wn != '\0')
|
|
{
|
|
if (*wn != *n && *wn != '?')
|
|
return (FALSE);
|
|
wn++;
|
|
n++;
|
|
}
|
|
return (TRUE);
|
|
}
|
|
|
|
/* nextfileindir looks for a file in the current directory. If found, it
|
|
* updates the file informationin the supplied firqb (name, links, status,
|
|
* protection code, size, clustersize).
|
|
* A return of TRUE means match, FALSE means nothing found.
|
|
* Special case: if the filename is null, the indicated director is opened.
|
|
* In that case, "prevlink" is set non-zero to indicate that this is the
|
|
* only "match" on this directory. "Indicated directory" can be the GFD
|
|
* or MFD if the proj and prog were wild.
|
|
*/
|
|
|
|
int nextfileindir (firqb *f)
|
|
{
|
|
ufdne *n;
|
|
ufdae *a;
|
|
ufdrms1 *r;
|
|
int ent;
|
|
|
|
if ((f->flags & f_name) == 0) /* null name, open UFD */
|
|
{
|
|
if (prevlink)
|
|
return (FALSE);
|
|
prevlink++; /* return no match next time */
|
|
if (f->prog == 255)
|
|
{
|
|
if (f->proj == 255)
|
|
fbread (mfdlbn);
|
|
else
|
|
fbread (curgfd);
|
|
}
|
|
f->stat = us_ufd | us_nok;
|
|
f->prot = 63;
|
|
f->clusiz = clumap->uclus;
|
|
f->size = 0;
|
|
for (ent = 0; ent < 7; ent++)
|
|
if (clumap->uent[ent])
|
|
f->size += clumap->uclus;
|
|
f->rmslink = 0;
|
|
f->recfmt = rf_stm; /* default to stream */
|
|
f->eofblk = f->size;
|
|
f->eofbyte = 0;
|
|
sprintf (f->cname, "%03d%03d.dir", f->cproj, f->cprog);
|
|
return (TRUE);
|
|
}
|
|
while (readlk (nextlink)) /* read next entry, if any */
|
|
{
|
|
n = use(ufdne,k);
|
|
f->nlink = nextlink; /* save this link */
|
|
nextlink = n->ulnk; /* link to next entry */
|
|
if ((n->ustat & (us_ufd | us_del)) == 0)
|
|
{
|
|
r50filename (n->unam, f->cname, TRUE);
|
|
if (wmatch (f->name, f->cname))
|
|
{
|
|
f->stat = n->ustat;
|
|
f->prot = n->uprot;
|
|
f->rlink = n->uar;
|
|
f->alink = n->uaa;
|
|
if (!readlk (f->alink))
|
|
rabort(CORRUPT);
|
|
a = use(ufdae,k);
|
|
f->size = a->usiz;
|
|
f->clusiz = a->uclus;
|
|
f->rmslink = a->ulnk;
|
|
if (a->urts[0] == 0)
|
|
f->size += a->urts[1] << 16;
|
|
if (NULLINK(f->rmslink))
|
|
{
|
|
f->recfmt = rf_stm; /* default to stream */
|
|
f->eofblk = f->size;
|
|
f->eofbyte = 0;
|
|
}
|
|
else
|
|
{
|
|
readlk (f->rmslink);
|
|
r = use(ufdrms1,k);
|
|
f->recfmt = r->fa_typ;
|
|
f->recsiz = r->fa_rsz;
|
|
f->eofblk = ((long)(r->fa_eof[0]) << 16) + r->fa_eof[1];
|
|
f->eofbyte = r->fa_eofb;
|
|
if (f->eofbyte == BLKSIZE)
|
|
{
|
|
f->eofbyte = 0;
|
|
f->eofblk++;
|
|
}
|
|
if ((r->fa_typ & fa_rfm) == rf_vfc)
|
|
{
|
|
if (NULLINK(r->ulnk))
|
|
f->rechdrsiz = 0;
|
|
else
|
|
{
|
|
readlk(r->ulnk);
|
|
f->rechdrsiz = use(ufdrms2,k)->fa_hsz;
|
|
}
|
|
}
|
|
}
|
|
return (TRUE);
|
|
}
|
|
}
|
|
prevlink = f->nlink; /* save link to predecessor */
|
|
}
|
|
return (FALSE); /* not found */
|
|
}
|
|
|
|
/* nextfile looks for the next matching filespec, going across directories
|
|
* as needed. If a match is found, it returns TRUE and loads the supplied
|
|
* firqb with information about the file. Otherwise, FALSE is returned.
|
|
* Special case: if the filename is null, the current UFD is opened.
|
|
*/
|
|
|
|
int nextfile (firqb *f) /* find next match for this filespec */
|
|
{
|
|
for (;;)
|
|
{
|
|
if (nextfileindir (f))
|
|
{
|
|
return (TRUE);
|
|
}
|
|
if (nextppn (f, gfddcntbl) == 0)
|
|
return (FALSE);
|
|
}
|
|
}
|
|
|
|
int findfile (firqb *f, const char *name) /* find a single file by name */
|
|
{
|
|
parse (name, f);
|
|
initfilescan (f, gfddcntbl);
|
|
return (nextfile (f));
|
|
}
|
|
|
|
int findqb (const firqb *f) /* find quota block for current ppn */
|
|
{
|
|
word link;
|
|
ua_quo *a;
|
|
|
|
if (plevel < RDS11)
|
|
rabort(INTERNAL);
|
|
if (curgfd == 0)
|
|
rabort(INTERNAL);
|
|
fbread (curgfd + gfdatrtbl); /* read attribute link table */
|
|
link = fibufw[f->cprog]; /* fetch appropriate link */
|
|
if (link & ul_clo) fbread (curgfd); /* get gfd block with fdcm */
|
|
else fbread (curgfd + ((link & ul_blo) >> sl_blo));
|
|
if (!readlk (link)) rabort(INTERNAL); /* read dir NE */
|
|
link = use(gfdne,k)->ulnk; /* get link to first attribute */
|
|
while (link)
|
|
{
|
|
readlk (link); /* read an attribute entry */
|
|
a = use(ua_quo,k);
|
|
if (a->uatyp == aa_quo) return (link); /* found it */
|
|
link = a->ulnk; /* follow the link */
|
|
}
|
|
rabort(CORRUPT); /* bogus -- no quota block */
|
|
return (0); /* to make the compiler happy */
|
|
}
|
|
|
|
void updqb (const firqb *f, long delta) /* adjust quota by delta blocks */
|
|
{
|
|
long quo, savefiblk;
|
|
ua_quo *q;
|
|
|
|
if (sw.debug != NULL)
|
|
printf ("updqb(,%ld), RDS %d.%d\n",
|
|
delta, plevel >> 8, plevel & 0xff);
|
|
if (plevel < RDS12)
|
|
return; /* NOP if not RDS 1.2 */
|
|
if (f->cproj == 0 && f->cprog == 1)
|
|
return; /* NOP for [0,1] */
|
|
savefiblk = fiblk; /* remember current block */
|
|
if (curqb == 0) /* if we haven't been here yet */
|
|
{
|
|
curqb = findqb (f); /* find it */
|
|
curqblbn = fiblk; /* and remember LBN also */
|
|
}
|
|
else
|
|
{
|
|
fbread (curqblbn); /* read block where it lives */
|
|
ulk (curqb); /* and set up "k" */
|
|
}
|
|
q = use(ua_quo,k); /* point to it */
|
|
quo = (q->aq_crm << 16) + q->aq_crl;
|
|
quo += delta; /* adjust the usage */
|
|
q->aq_crl = quo & 0xffff; /* update low order */
|
|
q->aq_crm = quo >> 16; /* and high order */
|
|
MARKF; /* mark FIBUF for write */
|
|
fbread (savefiblk); /* restore caller's FIBUF content */
|
|
}
|
|
|
|
/* upddlw updates the dlw/dla field; it is called when the file has
|
|
* been changed. note that we don't implement date of last access
|
|
* recording; this avoids having to write to disks that would otherwise
|
|
* only be read.
|
|
*/
|
|
|
|
void upddlw (const firqb *f) /* update date of last write */
|
|
{
|
|
if (sw.debug != NULL)
|
|
printf ("upddlw( )\n");
|
|
if (f->stat & us_ufd)
|
|
return; /* nop on directories! */
|
|
if (!readlk (f->alink))
|
|
rabort(INTERNAL);
|
|
use(ufdae,k)->udla = curdate ();
|
|
MARKF;
|
|
}
|
|
|
|
/* extdir extends the current directory by one cluster, if possible.
|
|
* it returns the DCN of the cluster allocated, if successful, or 0
|
|
* if allocation failed. If it succeeded, all directory clustermaps
|
|
* have been updated, and the new cluster has been otherwise set to zero.
|
|
* This routine works for any kind of directory, MFD and GFD included.
|
|
* On completion, some directory block is in FIBUF. If successful,
|
|
* it is the FIRST block of the directory (NOT any of the allocated
|
|
* cluster!).
|
|
*
|
|
* extdir2 either creates the first cluster of a directory or extends
|
|
* a directory by one cluster. It is called from extdir (and also
|
|
* directly in processing the "init" command). It is passed the clustersize,
|
|
* clustermap entry offset, clustermap flags, and directory label data
|
|
* to be used. (If a cluster other than first is being allocated, only
|
|
* entry offset, flags, and clustersize arguments are used.) The flags
|
|
* argument is used to control whether an mfd/gfd vs. a ufd is being
|
|
* extended. Since this affects which blocks have to be updated, it
|
|
* is important to pass the correct value.
|
|
*/
|
|
|
|
int extdir2 (int newcm, int clusiz, byte flags, const ufdlabel *newl)
|
|
{
|
|
long clu, lbn; /* new cluster and its lbn */
|
|
int cm, off; /* clustermap entry and offset */
|
|
int realclu; /* clustersize for getclu */
|
|
ufdlabel *u;
|
|
|
|
if (sw.debug != NULL)
|
|
{
|
|
printf ("extdir2(%d,%d,%o)\n", newcm, clusiz, flags);
|
|
printf (" old map: %03o %03o %06o %06o %06o %06o %06o %06o %06o\n",
|
|
clumap->uclus, clumap->uflag, clumap->uent[0],
|
|
clumap->uent[1], clumap->uent[2],
|
|
clumap->uent[3], clumap->uent[4],
|
|
clumap->uent[5], clumap->uent[6]);
|
|
}
|
|
checkwrite (); /* make sure pending write is done */
|
|
realclu = clusiz;
|
|
if (clusiz < pcs)
|
|
realclu = pcs; /* for big disks, if pcs > 16 */
|
|
clu = getclu (realclu, realclu); /* get one cluster that size */
|
|
if (clu == 0)
|
|
return (FALSE); /* sorry, nothing available */
|
|
lbn = dcntolbn(clu); /* get corresponding start LBN */
|
|
for (cm = 0; cm < newcm; cm++) /* update old clusters, if any */
|
|
{
|
|
for (off = 0; off < clusiz; off++)
|
|
{
|
|
if (cm == 0) /* first cluster needs some checks */
|
|
{
|
|
if (off == 0)
|
|
continue; /* do this last */
|
|
if ((flags & fd_new) && off <= gfdatrtbl)
|
|
continue; /* skip tables */
|
|
}
|
|
fbread (dcntolbn(clumap->uent[cm]) + off);
|
|
clumap->uent[newcm] = clu;
|
|
fbwrite (); /* write updated data */
|
|
}
|
|
}
|
|
|
|
if (newcm == 0) /* creating the first cluster */
|
|
{
|
|
off = 1; /* Assume updating a UFD */
|
|
if (flags & fd_new)
|
|
off = gfdatrtbl + 1; /* Adjust if MFD/GFD */
|
|
memset (fibuf, 0, BLKSIZE); /* zero entire block */
|
|
if (flags & fd_new) /* erase table blocks if MFD/GFD */
|
|
{
|
|
rwrite (lbn + gfddcntbl, BLKSIZE, fibuf);
|
|
rwrite (lbn + gfdatrtbl, BLKSIZE, fibuf);
|
|
}
|
|
clumap->uclus = clusiz; /* set up fixed fields in cluster map */
|
|
clumap->uflag = flags; /* flags too */
|
|
clumap->uent[0] = clu; /* this is the only cluster */
|
|
}
|
|
else
|
|
{
|
|
/* Special case: if we're creating the second cluster of a
|
|
* directory of cluster size 1, then the above code does
|
|
* nothing. So we need to read cluster 0 explicitly to get
|
|
* the old cluster map.
|
|
*/
|
|
if (newcm == 1 && clusiz == 1)
|
|
{
|
|
readdcn (clumap->uent[0]);
|
|
clumap->uent[newcm] = clu;
|
|
}
|
|
memset (fibuf, 0, BLKSIZE - sizeof (fdcm));
|
|
off = 0; /* updating entire new cluster */
|
|
}
|
|
for ( ; off < clusiz; off++)
|
|
rwrite (lbn + off, BLKSIZE, fibuf);
|
|
if (newcm == 0) /* initialize directory label */
|
|
{
|
|
fiblk = lbn; /* we're building this block */
|
|
u = use(ufdlabel,0);
|
|
memcpy (u, newl, sizeof (ufdlabel));
|
|
}
|
|
else
|
|
{
|
|
fiblk = -1; /* nothing in fibuf */
|
|
readdcn (clumap->uent[0]); /* read first block of dir */
|
|
clumap->uent[newcm] = clu; /* update that last */
|
|
}
|
|
fbwrite (); /* write updated data */
|
|
if (sw.debug != NULL)
|
|
printf (" new map: %03o %03o %06o %06o %06o %06o %06o %06o %06o\n",
|
|
clumap->uclus, clumap->uflag, clumap->uent[0],
|
|
clumap->uent[1], clumap->uent[2],
|
|
clumap->uent[3], clumap->uent[4],
|
|
clumap->uent[5], clumap->uent[6]);
|
|
return (clu); /* it worked! return new DCN */
|
|
}
|
|
|
|
int extdir (void) /* extend current directory */
|
|
{
|
|
int clusiz; /* directory clustersize */
|
|
int newcm; /* clustermap entry to update */
|
|
|
|
if (sw.debug != NULL)
|
|
printf ("extdir()\n");
|
|
if (clumap->uent[6])
|
|
return (0); /* already max length dir */
|
|
for (newcm = 0; ; newcm++) if (clumap->uent[newcm] == 0) break;
|
|
clusiz = clumap->uclus; /* get dir clustersize */
|
|
return (extdir2 (newcm, clusiz, clumap->uflag, NULL));
|
|
}
|
|
|
|
/* Check if the UFD has been allocated yet for a PPN; if not, allocate
|
|
* the first cluster. Returns 0 on failure, or start LBN of the UFD
|
|
* otherwise (including if the UFD already exists).
|
|
*/
|
|
|
|
/* the 255,255 is overwritten with the PPN */
|
|
|
|
ufdlabel newulabel = { 0, 0177777, {0, 0, 0, 0}, {255, 255}, UFD };
|
|
|
|
int allocufd (word dirne, firqb *f)
|
|
{
|
|
long savegfdlbn, savelbn;
|
|
word dirae;
|
|
gfdne *d;
|
|
gfdae *a;
|
|
|
|
if (sw.debug != NULL)
|
|
printf ("allocufd (%d) for [%d,%d]\n",
|
|
dirne, f->cproj, f->cprog);
|
|
readlk (dirne); /* read the NE */
|
|
d = use(gfdne,k);
|
|
if ((savelbn = dcntolbn(d->uar)) == 0) /* no UFD allocated yet */
|
|
{
|
|
savegfdlbn = fiblk; /* remember where NE is */
|
|
dirae = d->uaa; /* read AE for PPN */
|
|
if (!readlk (dirae))
|
|
rabort(CORRUPT);
|
|
a = use(gfdae,k);
|
|
newulabel.lppn[1] = f->cproj; /* fill in the label */
|
|
newulabel.lppn[0] = f->cprog;
|
|
if (!extdir2 (0, a->uclus, 0, &newulabel))
|
|
return (0);
|
|
savelbn = clumap->uent[0]; /* pick up DCN of UFD */
|
|
fbread (savegfdlbn); /* re-read GFD */
|
|
readlk (dirne); /* and set up for NE */
|
|
use(gfdne,k)->uar = savelbn; /* set DCN of UFD */
|
|
MARKF;
|
|
fbread (curgfd + gfddcntbl); /* read UFD pointer block */
|
|
fibufw[f->cprog] = savelbn; /* set the new pointer (DCN) */
|
|
MARKF;
|
|
savelbn = dcntolbn(savelbn); /* now make it an LBN */
|
|
entptr = sizeof (ufdlabel); /* initialize the entry allocator */
|
|
}
|
|
return (savelbn);
|
|
}
|
|
/* free entry search runs from entptr through end of directory. entptr
|
|
* initially points to start of dir. Anytime an entry is freed, entptr
|
|
* should be backed up to that entry if necessary.
|
|
* The value returned is the link to the entry, or 0 if none is available.
|
|
* The directory is extended if necessary, so 0 will be returned only if
|
|
* the disk is full or the directory already has 7 clusters, all full.
|
|
* In all cases, fibuf is left with a valid directory block in it.
|
|
* If the allocation succeeded, is it the block with the free entry, and
|
|
* k has the offset to it. If the allocation failed, it is some unspecified
|
|
* block of the directory.
|
|
* The entry is completely zeroed, and entptr points to it (not to the
|
|
* entry after it). So a second call to getent before any changes are
|
|
* made will find the same entry again!
|
|
*/
|
|
|
|
int getent (void) /* get a free directory blockette */
|
|
{
|
|
int clu, blk, n; /* cluster and block being scanned */
|
|
lword32 *l;
|
|
|
|
if (sw.debug != NULL)
|
|
printf ("getent()\n");
|
|
readlk2 (entptr); /* read starting point */
|
|
clu = (entptr & ul_clo) >> sl_clo; /* convert entptr */
|
|
blk = (entptr & ul_blo) >> sl_blo;
|
|
for (;;)
|
|
{
|
|
if (*use(lword,k) == 0) /* found an entry */
|
|
{
|
|
entptr = k + (clu << sl_clo) + (blk << sl_blo);
|
|
readlk2 (entptr); /* read it */
|
|
l = use (lword32, k); /* point to it */
|
|
*l++ = 0; /* and clear it out */
|
|
*l++ = 0;
|
|
*l++ = 0;
|
|
*l++ = 0;
|
|
MARKF; /* and mark it */
|
|
return (entptr); /* this is what we found */
|
|
}
|
|
if ((k += sizeof (ufdne)) == BLKSIZE - sizeof (fdcm))
|
|
{
|
|
k = 0;
|
|
if (++blk < clumap->uclus)
|
|
{
|
|
if ((clumap->uflag & fd_new)
|
|
&& clu == 0 && blk == gfddcntbl)
|
|
blk = gfdatrtbl + 1;
|
|
fbread (dcntolbn(clumap->uent[clu]) + blk);
|
|
}
|
|
else
|
|
{
|
|
blk = 0;
|
|
if (++clu > 6) return (0); /* we're FULL */
|
|
if (clumap->uent[clu])
|
|
readdcn (clumap->uent[clu]);
|
|
else
|
|
{
|
|
if ((n = extdir ()) != 0)
|
|
{
|
|
readdcn (n);
|
|
entptr = clu << sl_clo;
|
|
readlk2 (entptr); /* read it */
|
|
return (entptr);
|
|
}
|
|
else
|
|
return (0); /* no room */
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Release a directory entry. entptr is updated if necessary.
|
|
* The entire entry is cleared out.
|
|
*/
|
|
|
|
void retent (word link)
|
|
{
|
|
lword32 *l;
|
|
|
|
if (sw.debug != NULL) printf ("retent(%o)\n", link);
|
|
if (!readlk (link)) rabort(INTERNAL); /* read the entry */
|
|
l = use (lword32, k); /* point to it */
|
|
*l++ = 0; /* and mark it as free */
|
|
*l++ = 0;
|
|
*l++ = 0;
|
|
*l++ = 0;
|
|
MARKF;
|
|
if ((link & ul_clo) < (entptr & ul_clo) ||
|
|
((link & ul_clo) == (entptr & ul_clo) && link < entptr))
|
|
entptr = link; /* move entptr back if needed */
|
|
}
|
|
|
|
/* Release the directory entries and allocated clusters for a file.
|
|
* This routine does not unlink it from the directory linked list;
|
|
* the caller has to do that. Nor does it make any protection checks.
|
|
*/
|
|
|
|
void retfile (word nlink)
|
|
{
|
|
word alink, rlink, rlink2, rmslink, clu, clusiz;
|
|
ufdne *n;
|
|
ufdae *a;
|
|
ufdrms1 *rms;
|
|
ufdre *r;
|
|
|
|
if (sw.debug != NULL)
|
|
printf ("retfile(%o)\n", nlink);
|
|
if (!readlk (nlink))
|
|
rabort(INTERNAL); /* read the NE */
|
|
n = use(ufdne,k);
|
|
alink = n->uaa;
|
|
rlink = n->uar; /* save the links */
|
|
retent (nlink); /* release the NE */
|
|
if (!readlk (alink))
|
|
rabort(CORRUPT); /* read the AE */
|
|
a = use(ufdae,k);
|
|
clusiz = a->uclus; /* save fcs */
|
|
rmslink = a->ulnk;
|
|
retent (alink); /* release the AE */
|
|
if (!NULLINK(rmslink)) /* if RMS attributes present */
|
|
{
|
|
readlk (rmslink); /* read the first */
|
|
rms = use(ufdrms1,k);
|
|
if (!NULLINK(rms->ulnk))
|
|
retent (rms->ulnk);
|
|
retent (rmslink);
|
|
}
|
|
while (!NULLINK(rlink)) /* release all RE's */
|
|
{
|
|
readlk (rlink); /* read this one */
|
|
r = use(ufdre,k);
|
|
rlink2 = r->ulnk; /* save link to next */
|
|
for (clu = 0; clu < 7; clu++)
|
|
if (r->uent[clu])
|
|
retclu (r->uent[clu], clusiz);
|
|
retent (rlink); /* release the RE */
|
|
rlink = rlink2;
|
|
}
|
|
}
|
|
|
|
void delfile (firqb *f)
|
|
{
|
|
readlk2 (prevlink);
|
|
use(ufdne,k)->ulnk = nextlink; /* link previous to next */
|
|
MARKF;
|
|
retfile (f->nlink); /* deallocate this file */
|
|
updqb (f, -UP(f->size,f->clusiz)); /* adjust quota block */
|
|
f->nlink = 0; /* zap link to former file */
|
|
}
|
|
|
|
/* Create a file given a filled-in firqb. The file is initially null,
|
|
* unless it is to be contiguous; in that case, it is pre-extended to
|
|
* the given size. The rts name to be assigned is also passed. The third
|
|
* and fourth arguments are used to supply RMS attribute block data if
|
|
* desired; if these are passed as NULL, no attribute blockettes are
|
|
* allocated for the file.
|
|
* prevlink must be set on entry (normally from an earlier call to nextfile).
|
|
* Return is TRUE if success, FALSE if failure. On failure, everything is
|
|
* released with the exception of any directory extension that may have been
|
|
* done.
|
|
* NOTE: this routine assumes that the file does NOT currently exist.
|
|
* The caller must verify this.
|
|
*/
|
|
|
|
int crefile (firqb *f, const word16 *rtsname,
|
|
const ufdrms1 *rms1, const ufdrms2 *rms2)
|
|
{
|
|
long dcn, startdcn, clu, clucount;
|
|
int dcnperfcs, reoff;
|
|
word re, prevre;
|
|
word rmslink2 = 0;
|
|
ufdne *n;
|
|
ufdae *a;
|
|
ufdrms1 *a1;
|
|
ufdrms2 *a2;
|
|
|
|
if (sw.debug != NULL)
|
|
printf ("crefile()\n");
|
|
if (pflags & uc_top)
|
|
{
|
|
prevlink = 0; /* link new file first */
|
|
readlk2 (0); /* find current first */
|
|
nextlink = use(ufdlabel,0)->ulnk; /* that will be next */
|
|
}
|
|
else
|
|
nextlink = 0; /* otherwise no next */
|
|
dcnperfcs = f->clusiz / dcs; /* # dev clu per file clu */
|
|
if ((f->stat & us_nox) == 0)
|
|
f->size = 0; /* if not contiguous */
|
|
f->rmslink = 0;
|
|
if (rms1 != NULL) /* if allocating attributes */
|
|
{
|
|
if (rms2 != NULL) /* two of them! */
|
|
{
|
|
if ((rmslink2 = getent ()) == 0)
|
|
return (FALSE);
|
|
readlk (rmslink2);
|
|
if (sw.debug != NULL)
|
|
printf ("crefile() rms2 at %o\n", rmslink2);
|
|
a2 = use(ufdrms2,k);
|
|
memcpy (a2, rms2, sizeof (ufdrms2));
|
|
a2->ulnk = ul_use; /* mark in use */
|
|
MARKF;
|
|
}
|
|
if ((f->rmslink = getent ()) == 0)
|
|
{
|
|
if (rmslink2)
|
|
retent (rmslink2);
|
|
return (FALSE);
|
|
}
|
|
readlk (f->rmslink);
|
|
if (sw.debug != NULL)
|
|
printf ("crefile() rms1 at %o\n", f->rmslink);
|
|
a1 = use(ufdrms1,k);
|
|
memcpy (a1, rms1, sizeof(ufdrms1));
|
|
a1->ulnk = rmslink2 | ul_use;
|
|
MARKF;
|
|
}
|
|
if ((f->alink = getent ()) == 0)
|
|
{
|
|
if (f->rmslink)
|
|
retent (f->rmslink);
|
|
if (rmslink2)
|
|
retent (rmslink2);
|
|
return (FALSE);
|
|
}
|
|
readlk (f->alink); /* read new AE */
|
|
if (sw.debug != NULL)
|
|
printf ("crefile() ae at %o\n", f->alink);
|
|
a = use(ufdae,k);
|
|
a->ulnk = f->rmslink | ul_use; /* mark in use */
|
|
a->usiz = f->size & 0xffff; /* set low order size */
|
|
a->udla = a->udc = curdate (); /* set dates */
|
|
a->utc = curtime (); /* and creation time */
|
|
a->uclus = f->clusiz; /* and clustersize */
|
|
if (f->size >> 16) /* if large file */
|
|
a->urts[1] = f->size >> 16; /* set high order size */
|
|
else
|
|
{
|
|
a->urts[0] = rtsname[0]; /* set rts name */
|
|
a->urts[1] = rtsname[1];
|
|
}
|
|
MARKF;
|
|
if ((f->nlink = getent ()) == 0) /* try to get NE */
|
|
{
|
|
if (f->rmslink) retent (f->rmslink);
|
|
if (rmslink2) retent (rmslink2);
|
|
retent (f->alink); /* no go, release AE */
|
|
return (FALSE);
|
|
}
|
|
readlk (f->nlink);
|
|
if (sw.debug != NULL)
|
|
printf ("crefile() ne at %o\n", f->nlink);
|
|
n = use(ufdne,k);
|
|
cvtnameexttor50 (f->cname, n->unam); /* set file name.ext */
|
|
n->uaa = f->alink; /* link AE to NE */
|
|
n->ustat = f->stat;
|
|
n->uprot = f->newprot; /* set status and protection */
|
|
MARKF;
|
|
f->rlink = 0; /* no RE's allocated yet */
|
|
if (f->size) /* do contiguous extend */
|
|
{
|
|
clucount = UP(f->size,f->clusiz); /* round size up to cluster */
|
|
startdcn = getclu (f->clusiz, clucount);
|
|
clucount /= f->clusiz; /* now get cluster count */
|
|
if (startdcn == 0) /* if it failed... */
|
|
{
|
|
retfile (f->nlink); /* make file go away */
|
|
return (FALSE); /* and exit */
|
|
}
|
|
if (sw.debug != NULL) printf ("crefile() dcn %lo\n", startdcn);
|
|
prevre = 0; /* working on first RE */
|
|
reoff = 0;
|
|
dcn = startdcn; /* start filling in RE's here */
|
|
for (clu = 0; clu < clucount; clu++)
|
|
{
|
|
if (reoff == 0) /* time to get another RE */
|
|
{
|
|
if ((re = getent ()) == 0)
|
|
{
|
|
for (clu = 0; clu < clucount; clu++)
|
|
retclu (startdcn + (clu * dcnperfcs), f->clusiz);
|
|
retfile (f->nlink);
|
|
return (FALSE);
|
|
}
|
|
if (sw.debug != NULL)
|
|
printf ("crefile() re at %o\n", re);
|
|
if (prevre)
|
|
{
|
|
readlk (prevre);
|
|
use(ufdre,k)->ulnk = re;
|
|
}
|
|
else
|
|
{
|
|
readlk (f->nlink);
|
|
use(ufdne,k)->uar = re;
|
|
f->rlink = re; /* save in firqb */
|
|
}
|
|
MARKF;
|
|
prevre = re; /* next RE will link to this */
|
|
readlk (re); /* read it */
|
|
}
|
|
use(ufdre,k)->uent[reoff++] = dcn;
|
|
MARKF;
|
|
dcn += dcnperfcs; /* point to next file cluster */
|
|
if (reoff > 6) reoff = 0; /* check for end of RE */
|
|
}
|
|
}
|
|
readlk (f->nlink); /* read NE again */
|
|
use(ufdne,k)->ulnk = nextlink; /* link it to next */
|
|
MARKF;
|
|
readlk2 (prevlink); /* read previous */
|
|
use(ufdne,k)->ulnk = f->nlink; /* link it to new file */
|
|
MARKF;
|
|
prevlink = f->nlink; /* this is new previous */
|
|
if (f->size)
|
|
updqb (f, UP(f->size,f->clusiz));
|
|
return (TRUE);
|
|
}
|
|
|
|
/* This routine checks whether a file is protected. It returns TRUE if so.
|
|
* It must be called after a call to nextfile or nextfileindir; the
|
|
* file information in the firqb is used to control the decisions.
|
|
*/
|
|
|
|
int protfile (firqb *f)
|
|
{
|
|
if (f->stat & us_nok)
|
|
{
|
|
printf ("File ");
|
|
printcurname (f);
|
|
printf (" is marked no-delete\n");
|
|
return (TRUE);
|
|
}
|
|
if (sw.overwrite == NULL && f->prot & up_wpo)
|
|
{
|
|
printf ("File ");
|
|
printcurname (f);
|
|
printf (" is protected\n");
|
|
return (TRUE);
|
|
}
|
|
return (FALSE);
|
|
}
|
|
|
|
/* Makedir creates a new account. It allocates the GFD, if needed, and
|
|
* creates the accounting data for the specified PPN. It does not actually
|
|
* allocate a UFD; this will happen when a file is created there, or can
|
|
* be done at any time by calling allocufd.
|
|
* It returns TRUE on success, FALSE on failure. On failure, an explanatory
|
|
* message is also printed.
|
|
* The caller must verify that the account does not currently exist. curgfd
|
|
* must be set up to point to the gfd, if it exists. "initfilescan" can be
|
|
* used to do both these things.
|
|
*/
|
|
|
|
/* the second (high order) 255 is overwritten with the project number */
|
|
|
|
ufdlabel newglabel = { 1, 0177777, {0, 0, 0, 0}, {255, 255}, GFD };
|
|
|
|
int makedir (firqb *f, int newclu)
|
|
{
|
|
gfdne *n;
|
|
gfdae *a;
|
|
ua_quo *q;
|
|
ua_dat *d;
|
|
word ne, ae, quo, dat;
|
|
int gfdclu;
|
|
|
|
/* note: when processing an RDS 0.0 disk, curgfd = mfdlbn = the start lbn
|
|
* of the MFD (DCN 1)
|
|
*/
|
|
|
|
if (sw.debug != NULL)
|
|
{
|
|
printf ("makedir() for");
|
|
printcurname (f);
|
|
printf ("\n");
|
|
}
|
|
if (plevel == RDS0 && f->cproj == 0)
|
|
{
|
|
printf ("Invalid PPN [%d,%d] for RDS 0.0 format disk\n", f->cproj, f->cprog);
|
|
return (FALSE);
|
|
}
|
|
if (curgfd == 0) /* must create GFD */
|
|
{
|
|
gfdclu = 4;
|
|
if (gfdclu < pcs)
|
|
gfdclu = pcs;
|
|
if (gfdclu > 16)
|
|
gfdclu = 16;
|
|
newglabel.lppn[1] = f->cproj;
|
|
if (!extdir2 (0, gfdclu, fd_new, &newglabel))
|
|
{
|
|
printf ("No room to allocate GFD for [%d,*]\n", f->cproj);
|
|
return (FALSE);
|
|
}
|
|
curgfd = clumap->uent[0]; /* remember its DCN */
|
|
fbread (mfdlbn + gfddcntbl);
|
|
fibufw[f->cproj] = curgfd; /* set new GFD pointer */
|
|
MARKF;
|
|
curgfd = dcntolbn(curgfd); /* and remember the LBN */
|
|
}
|
|
fbread (curgfd); /* read first block of GFD */
|
|
entptr = sizeof (gfdlabel); /* initialize getent search */
|
|
if ((ae = getent ()) == 0)
|
|
{
|
|
printf ("No room to create [%d,%d]\n", f->cproj, f->cprog);
|
|
return (FALSE);
|
|
}
|
|
readlk (ae);
|
|
a = use(gfdae,k);
|
|
a->ulnk = ul_use;
|
|
a->uclus = newclu;
|
|
MARKF;
|
|
if (plevel > RDS0)
|
|
{
|
|
if ((dat = getent ()) == 0)
|
|
{
|
|
printf ("No room to create [%d,%d]\n", f->cproj, f->cprog);
|
|
retent (ae);
|
|
return (FALSE);
|
|
}
|
|
readlk (dat);
|
|
d = use(ua_dat,k);
|
|
d->ulnk = ul_use; /* mark in use */
|
|
d->uatyp = aa_dat; /* set type code */
|
|
d->at_lti = at_npw; /* in case it's set to /user */
|
|
d->at_cda = d->at_pda = curdate ();
|
|
d->at_pti = curtime () | at_nlk; /* set no-lookup flag */
|
|
if (plevel >= RDS12)
|
|
d->at_exp = 65535U;
|
|
MARKF;
|
|
if ((quo = getent ()) == 0)
|
|
{
|
|
printf ("No room to create [%d,%d]\n", f->cproj, f->cprog);
|
|
retent (ae);
|
|
retent (dat);
|
|
return (FALSE);
|
|
}
|
|
readlk (quo);
|
|
q = use(ua_quo,k);
|
|
q->ulnk = dat; /* link to date/time */
|
|
q->uatyp = aa_quo; /* set type code */
|
|
q->aq_lol = 65535U; /* set disk quotas unlimited */
|
|
q->aq_lil = 65535U;
|
|
q->aq_lom = 255U;
|
|
q->aq_lim = 255U;
|
|
MARKF;
|
|
}
|
|
if ((ne = getent ()) == 0)
|
|
{
|
|
printf ("No room to create [%d,%d]\n", f->cproj, f->cprog);
|
|
retent (ae);
|
|
if (plevel > RDS0)
|
|
{
|
|
retent (dat);
|
|
retent (quo);
|
|
}
|
|
return (FALSE);
|
|
}
|
|
readlk (ne);
|
|
n = use(gfdne,k);
|
|
n->unam[0] = (f->cproj << 8) + f->cprog;
|
|
n->ustat = us_nok | us_ufd;
|
|
n->uprot = 60;
|
|
n->uaa = ae;
|
|
MARKF;
|
|
if (plevel > RDS0)
|
|
{
|
|
n->ulnk = quo; /* link to quotas */
|
|
fbread (curgfd + gfdatrtbl); /* read GFD table block */
|
|
fibufw[f->cprog] = ne; /* set NE link */
|
|
}
|
|
else
|
|
{
|
|
n->ulnk = nextppnlink; /* link this one to next */
|
|
readlk2 (prevppnlink); /* read previous MFD NE */
|
|
use(gfdne,k)->ulnk = ne; /* and link it to new NE */
|
|
prevppnlink = ne; /* this is previous next time */
|
|
}
|
|
MARKF;
|
|
return (TRUE);
|
|
}
|
|
|
|
/* remdir removes a directory, or prints a message explaining why
|
|
* it can't. Returns TRUE on success, FALSE on failure. The name entry
|
|
* link for the PPN is supplied by the caller.
|
|
*
|
|
*/
|
|
|
|
int remdir (firqb *f, word ne)
|
|
{
|
|
word ae, attr, a2;
|
|
long savelbn, ufdclu;
|
|
gfdne *n;
|
|
int j;
|
|
|
|
if (f->cproj == 0 && f->cprog == 1)
|
|
{
|
|
printf ("Directory [0,1] cannot be deleted\n");
|
|
return (FALSE);
|
|
}
|
|
readlk (ne); /* read its NE */
|
|
n = use(gfdne,k);
|
|
if (n->uar) /* it has a UFD */
|
|
{
|
|
savelbn = fiblk;
|
|
readdcn(n->uar); /* read the UFD */
|
|
if (!NULLINK(use(ufdlabel,0)->ulnk))
|
|
{
|
|
printf ("Directory [%d,%d] is not empty\n",
|
|
f->cproj, f->cprog);
|
|
fbread (savelbn);
|
|
return (FALSE);
|
|
}
|
|
ufdclu = clumap->uclus; /* get clustersize */
|
|
if (ufdclu < pcs)
|
|
ufdclu = pcs; /* for big disks */
|
|
for (j = 0; j < 7; j++)
|
|
if (clumap->uent[j])
|
|
retclu (clumap->uent[j], ufdclu);
|
|
fbread (curgfd + gfddcntbl); /* read UFD pointer table */
|
|
fibufw[f->cprog] = 0; /* no more UFD */
|
|
MARKF;
|
|
fbread (savelbn); /* restore GFD block */
|
|
}
|
|
ae = n->uaa;
|
|
attr = n->ulnk;
|
|
if (NULLINK(ae))
|
|
rabort(CORRUPT);
|
|
retent (ne); /* release NE */
|
|
retent (ae); /* and AE */
|
|
if (plevel > RDS0)
|
|
{
|
|
while (!NULLINK (attr))
|
|
{
|
|
readlk (attr);
|
|
a2 = use(uattr,k)->ulnk; /* remember next link */
|
|
retent (attr); /* release it */
|
|
attr = a2;
|
|
}
|
|
fbread (curgfd + gfdatrtbl);
|
|
fibufw[f->cprog] = 0; /* no more PPN */
|
|
}
|
|
else
|
|
{
|
|
readlk2 (prevppnlink); /* read previous MFD NE */
|
|
use(gfdne,k)->ulnk = nextppnlink; /* and unlink this one */
|
|
}
|
|
MARKF;
|
|
return (TRUE);
|
|
}
|
|
|
|
/* Set up the SATT parameters by searching for [0,1]satt.sys if that
|
|
* hasn't been done yet.
|
|
*/
|
|
|
|
void findsat (void)
|
|
{
|
|
firqb f;
|
|
|
|
if (sattsize) return; /* don't do it twice */
|
|
if (!findfile (&f, "[0,1]satt.sys"))
|
|
{
|
|
printf ("SATT.SYS not found\n");
|
|
rabort(CORRUPT);
|
|
}
|
|
if ((f.stat & us_nox) == 0)
|
|
{
|
|
printf ("SATT.SYS is not contiguous\n");
|
|
rabort(CORRUPT);
|
|
}
|
|
pcns = (diskblocks - dcs)/ pcs; /* pack size in pack clusters */
|
|
sattsize = UP(pcns,BLKSIZE*8); /* round up to block boundary */
|
|
sattsize /= BLKSIZE * 8; /* and change bits to blocks */
|
|
if (f.size != sattsize)
|
|
{
|
|
printf ("Expected SATT.SYS size is %d, actual size is %ld\n",
|
|
sattsize, f.size);
|
|
rabort(CORRUPT);
|
|
}
|
|
sattsize *= BLKSIZE; /* now size in bytes */
|
|
if ((sattbufp = (byte *) malloc (sattsize)) == NULL) rabort(NOMEM);
|
|
readlk (f.rlink);
|
|
sattlbn = dcntolbn(use(ufdre,k)->uent[0]);
|
|
rread (sattlbn, sattsize, sattbufp);
|
|
satptr = 0; /* nothing allocated yet */
|
|
}
|
|
|
|
void rmount (void) /* "mount" the rsts pack */
|
|
{
|
|
ropen (DREADMODE); /* open it first */
|
|
readlabel (); /* read and remember label data */
|
|
if (pflags & uc_mnt)
|
|
printf ("** warning: disk was not properly dismounted **\n");
|
|
sattsize = 0; /* SATT not looked up yet */
|
|
womsat = FALSE; /* and SATT is clean */
|
|
fiblkw = FALSE; /* FIBUF ditto */
|
|
}
|
|
|
|
void rmountrw (void) /* mount the RSTS pack read/write */
|
|
{
|
|
packlabel *p;
|
|
|
|
if (sw.debug != NULL)
|
|
printf ("rmountrw()\n");
|
|
ropen (DWRITEMODE); /* open it first */
|
|
readlabel (); /* read and remember label data */
|
|
if (pflags & uc_mnt)
|
|
rabort(DIRTY);
|
|
if ((pflags & uc_ro) && (sw.overwrite == NULL))
|
|
rabort(ROPACK);
|
|
sattsize = 0; /* SATT not looked up yet */
|
|
womsat = FALSE; /* and SATT is clean */
|
|
fiblkw = FALSE; /* FIBUF ditto */
|
|
findsat (); /* make sure we know where satt is */
|
|
readdcn (1); /* read the pack label */
|
|
p = use(packlabel,0);
|
|
p->pstat |= uc_mnt; /* mark mounted */
|
|
if (plevel >= RDS12) /* if new pack */
|
|
{
|
|
p->mntdat = curdate (); /* update timestamps */
|
|
p->mnttim = curtime ();
|
|
}
|
|
fbwrite (); /* write it back */
|
|
}
|
|
|
|
void rumount (void) /* dismount for users of rmount() */
|
|
{
|
|
rclose (); /* close the device */
|
|
}
|
|
|
|
void rumountrw (void) /* dismount (no longer read/write) */
|
|
{
|
|
packlabel *p;
|
|
|
|
if (sw.debug != NULL)
|
|
printf ("rumount()\n");
|
|
readdcn (1); /* read the pack label */
|
|
p = use(packlabel,0);
|
|
if ((p->pstat & uc_mnt) == 0)
|
|
rabort(INTERNAL);
|
|
if (womsat)
|
|
rwrite (sattlbn, sattsize, sattbufp);
|
|
womsat = FALSE; /* SATT written out if needed */
|
|
free (sattbufp); /* release satt memory copy */
|
|
p->pstat &= ~uc_mnt; /* mark no longer mounted */
|
|
if (plevel >= RDS12) /* if new pack */
|
|
{
|
|
p->mntdat = curdate (); /* update timestamps */
|
|
p->mnttim = curtime ();
|
|
}
|
|
fbwrite (); /* write it back */
|
|
/* this leaves FIBUF clean */
|
|
rumount (); /* now do common dismount */
|
|
}
|