# define INCLUDE_FILE_IO
# include "dgd.h"
# include "swap.h"
# define BSET(map, bit) (map[(bit) >> 3] |= (1 << ((bit) & 7)))
# define BCLR(map, bit) (map[(bit) >> 3] &= ~(1 << ((bit) & 7)))
# define BTST(map, bit) (map[(bit) >> 3] & (1 << ((bit) & 7)))
typedef struct _header_ { /* swap slot header */
struct _header_ *prev; /* previous in swap slot list */
struct _header_ *next; /* next in swap slot list */
sector sec; /* the sector that uses this slot */
sector swap; /* the swap sector (if any) */
bool dirty; /* has the swap slot been written to? */
} header;
static char *swapfile; /* swap file name */
static int swap; /* swap file descriptor */
static int dump; /* dump file descriptor */
static int restore; /* restore file descriptor */
static char *mem; /* swap slots in memory */
static sector *map, *smap; /* sector map, swap free map */
static sector mfree, sfree; /* free sector lists */
static char *bmap; /* sector bitmap */
static char *cbuf; /* sector buffer */
static sector cached; /* sector currently cached in cbuf */
static header *first, *last; /* first and last swap slot */
static header *lfree; /* free swap slot list */
static long slotsize; /* sizeof(header) + size of sector */
static unsigned int sectorsize; /* size of sector */
static unsigned int restoresecsize; /* size of sector in restore file */
static sector swapsize, cachesize; /* # of sectors in swap and cache */
static sector nsectors; /* total swap sectors */
static sector nfree; /* # free sectors */
static sector ssectors; /* sectors actually in swap file */
static sector dsectors, dcursec; /* dump sectors */
/*
* NAME: swap->init()
* DESCRIPTION: initialize the swap device
*/
void sw_init(file, total, cache, secsize)
char *file;
register unsigned int total, cache;
unsigned int secsize;
{
register header *h;
register sector i;
/* allocate and initialize all tables */
swapfile = file;
swapsize = total;
cachesize = cache;
sectorsize = secsize;
slotsize = sizeof(header) + secsize;
mem = ALLOC(char, slotsize * cache);
map = ALLOC(sector, total);
smap = ALLOC(sector, total);
bmap = ALLOC(char, (total + 7) >> 3);
cbuf = ALLOC(char, secsize);
cached = SW_UNUSED;
/* 0 sectors allocated */
nsectors = 0;
ssectors = 0;
nfree = 0;
dsectors = 0;
/* init free sector maps */
mfree = SW_UNUSED;
sfree = SW_UNUSED;
lfree = h = (header *) mem;
for (i = cache - 1; i > 0; --i) {
h->sec = SW_UNUSED;
h->next = (header *) ((char *) h + slotsize);
h = h->next;
}
h->sec = SW_UNUSED;
h->next = (header *) NULL;
/* no swap slots in use yet */
first = (header *) NULL;
last = (header *) NULL;
swap = dump = -1;
}
/*
* NAME: swap->finish()
* DESCRIPTION: clean up swapfile
*/
void sw_finish()
{
if (swap >= 0) {
close(swap);
unlink(swapfile);
}
if (dump >= 0) {
close(dump);
}
}
/*
* NAME: create()
* DESCRIPTION: create the swap file
*/
static void sw_create()
{
memset(cbuf, '\0', sectorsize);
swap = open(swapfile, O_RDWR | O_CREAT | O_TRUNC | O_BINARY, 0600);
if (swap < 0 || write(swap, cbuf, sectorsize) < 0) {
fatal("cannot create swap file \"%s\"", swapfile);
}
}
/*
* NAME: copy()
* DESCRIPTION: copy sectors from dump to swap
*/
static void copy(sec, n)
register sector sec, n;
{
dsectors -= n;
if (swap < 0) {
sw_create();
}
while (n > 0) {
while (!BTST(bmap, dcursec)) {
dcursec++;
}
lseek(dump, (map[dcursec] + 1L) * sectorsize, SEEK_SET);
if (read(dump, cbuf, sectorsize) <= 0) {
fatal("cannot read dump file");
}
lseek(swap, (sec + 1L) * sectorsize, SEEK_SET);
if (write(swap, cbuf, sectorsize) < 0) {
fatal("cannot write swap file");
}
BCLR(bmap, dcursec);
map[dcursec++] = sec++;
--n;
}
}
/*
* NAME: swap->newv()
* DESCRIPTION: initialize a new vector of sectors
*/
void sw_newv(vec, size)
register sector *vec;
register unsigned int size;
{
while (mfree != SW_UNUSED) {
/* reuse a previously deleted sector */
if (size == 0) {
return;
}
mfree = map[*vec = mfree];
map[*vec++] = SW_UNUSED;
--nfree;
--size;
}
while (size > 0) {
/* allocate a new sector */
if (nsectors == swapsize) {
fatal("out of sectors");
}
BCLR(bmap, nsectors);
map[*vec++ = nsectors++] = SW_UNUSED;
--size;
}
}
/*
* NAME: swap->wipev()
* DESCRIPTION: wipe a vector of sectors
*/
void sw_wipev(vec, size)
register sector *vec;
register unsigned int size;
{
register sector sec, i;
register header *h;
while (dsectors > 0) {
if (size == 0) {
return;
}
sec = *vec++;
i = map[sec];
if (i < cachesize && (h=(header *) (mem + i * slotsize))->sec == sec) {
i = h->swap;
h->swap = SW_UNUSED;
} else {
map[sec] = SW_UNUSED;
}
if (i != SW_UNUSED) {
if (BTST(bmap, sec)) {
/*
* free sector in dump file
*/
BCLR(bmap, sec);
--dsectors;
} else {
/*
* replace by sector from dump file
*/
copy(i, (sector) 1);
}
}
--size;
}
vec += size;
while (size > 0) {
sec = *--vec;
i = map[sec];
if (i < cachesize && (h=(header *) (mem + i * slotsize))->sec == sec) {
i = h->swap;
h->swap = SW_UNUSED;
} else {
map[sec] = SW_UNUSED;
}
if (i != SW_UNUSED) {
/*
* free sector in swap file
*/
smap[i] = sfree;
sfree = i;
}
--size;
}
}
/*
* NAME: swap->delv()
* DESCRIPTION: delete a vector of swap sectors
*/
void sw_delv(vec, size)
register sector *vec;
register unsigned int size;
{
register sector sec, i;
register header *h;
/*
* note: sectors must have been wiped before being deleted!
*/
vec += size;
while (size > 0) {
sec = *--vec;
i = map[sec];
if (i < cachesize && (h=(header *) (mem + i * slotsize))->sec == sec) {
/*
* remove the swap slot from the first-last list
*/
if (h != first) {
h->prev->next = h->next;
} else {
first = h->next;
if (first != (header *) NULL) {
first->prev = (header *) NULL;
}
}
if (h != last) {
h->next->prev = h->prev;
} else {
last = h->prev;
if (last != (header *) NULL) {
last->next = (header *) NULL;
}
}
/*
* put the cache slot in the free cache slot list
*/
h->sec = SW_UNUSED;
h->next = lfree;
lfree = h;
}
/*
* put sec in free sector list
*/
map[sec] = mfree;
mfree = sec;
nfree++;
--size;
}
}
/*
* NAME: swap->load()
* DESCRIPTION: reserve a swap slot for sector sec. If fill == TRUE, load it
* from the swap file if appropriate.
*/
static header *sw_load(sec, fill)
sector sec;
bool fill;
{
register header *h;
register sector load, save;
load = map[sec];
if (load >= cachesize ||
(h=(header *) (mem + load * slotsize))->sec != sec) {
/*
* the sector is either unused or in the swap file
*/
if (lfree != (header *) NULL) {
/*
* get swap slot from the free swap slot list
*/
h = lfree;
lfree = h->next;
} else {
/*
* No free slot available, use the last one in the swap slot list
* instead.
*/
h = last;
last = h->prev;
if (last != (header *) NULL) {
last->next = (header *) NULL;
}
save = h->swap;
if (h->dirty) {
/*
* Dump the sector to swap file
*/
if (save == SW_UNUSED) {
/*
* allocate new sector in swap file
*/
if (sfree == SW_UNUSED) {
save = ssectors++;
} else {
save = sfree;
sfree = smap[save];
}
}
if (swap < 0) {
sw_create();
}
lseek(swap, (save + 1L) * sectorsize, SEEK_SET);
if (write(swap, (char *) (h + 1), sectorsize) < 0) {
fatal("cannot write swap file");
}
}
map[h->sec] = save;
}
h->sec = sec;
h->swap = load;
h->dirty = FALSE;
/*
* The slot has been reserved. Update map.
*/
map[sec] = ((long) h - (long) mem) / slotsize;
if (load != SW_UNUSED) {
if (dsectors > 0 && BTST(bmap, sec)) {
if (fill) {
/*
* load the sector from the dump file
*/
lseek(dump, (load + 1L) * sectorsize, SEEK_SET);
if (read(dump, (char *) (h + 1), sectorsize) <= 0) {
fatal("cannot read dump file");
}
}
BCLR(bmap, sec);
--dsectors;
h->swap = SW_UNUSED;
h->dirty = TRUE;
} else if (fill) {
/*
* load the sector from the swap file
*/
lseek(swap, (load + 1L) * sectorsize, SEEK_SET);
if (read(swap, (char *) (h + 1), sectorsize) <= 0) {
fatal("cannot read swap file");
}
}
} else if (fill) {
/* zero-fill new sector */
memset(h + 1, '\0', sectorsize);
}
} else {
/*
* The sector already had a slot. Remove it from the first-last list.
*/
if (h != first) {
h->prev->next = h->next;
} else {
first = h->next;
}
if (h != last) {
h->next->prev = h->prev;
} else {
last = h->prev;
if (last != (header *) NULL) {
last->next = (header *) NULL;
}
}
}
/*
* put the sector at the head of the first-last list
*/
h->prev = (header *) NULL;
h->next = first;
if (first != (header *) NULL) {
first->prev = h;
} else {
last = h; /* last was NULL too */
}
first = h;
return h;
}
/*
* NAME: swap->readv()
* DESCRIPTION: read bytes from a vector of sectors
*/
void sw_readv(m, vec, size, idx)
register char *m;
register sector *vec;
register Uint size, idx;
{
register unsigned int len;
vec += idx / sectorsize;
idx %= sectorsize;
do {
len = (size > sectorsize - idx) ? sectorsize - idx : size;
memcpy(m, (char *) (sw_load(*vec++, TRUE) + 1) + idx, len);
idx = 0;
m += len;
} while ((size -= len) > 0);
}
/*
* NAME: swap->writev()
* DESCRIPTION: write bytes to a vector of sectors
*/
void sw_writev(m, vec, size, idx)
register char *m;
register sector *vec;
register Uint size, idx;
{
register header *h;
register unsigned int len;
vec += idx / sectorsize;
idx %= sectorsize;
do {
len = (size > sectorsize - idx) ? sectorsize - idx : size;
h = sw_load(*vec++, (len != sectorsize));
h->dirty = TRUE;
memcpy((char *) (h + 1) + idx, m, len);
idx = 0;
m += len;
} while ((size -= len) > 0);
}
/*
* NAME: swap->dreadv()
* DESCRIPTION: restore bytes from a vector of sectors in dump file
*/
void sw_dreadv(m, vec, size, idx)
register char *m;
register sector *vec;
register Uint size, idx;
{
register unsigned int len;
vec += idx / restoresecsize;
idx %= restoresecsize;
do {
len = (size > restoresecsize - idx) ? restoresecsize - idx : size;
if (*vec != cached) {
lseek(restore, (smap[*vec] + 1L) * restoresecsize, SEEK_SET);
if (read(restore, cbuf, restoresecsize) <= 0) {
fatal("cannot read dump file");
}
cached = *vec;
}
vec++;
memcpy(m, cbuf + idx, len);
idx = 0;
m += len;
} while ((size -= len) > 0);
}
/*
* NAME: swap->mapsize()
* DESCRIPTION: count the number of sectors required for size bytes + a map
*/
sector sw_mapsize(size)
unsigned int size;
{
register sector i, n;
/* calculate the number of sectors required */
n = 0;
for (;;) {
i = (size + n * sizeof(sector) + sectorsize - 1) / sectorsize;
if (n == i) {
return n;
}
n = i;
}
}
/*
* NAME: swap->count()
* DESCRIPTION: return the number of sectors presently in use
*/
sector sw_count()
{
return nsectors - nfree;
}
typedef struct {
Uint secsize; /* size of swap sector */
sector nsectors; /* # sectors */
sector ssectors; /* # swap sectors */
sector nfree; /* # free sectors */
sector mfree; /* free sector list */
} dump_header;
static char dh_layout[] = "idddd";
# define CHUNKSZ 32
/*
* NAME: swap->copy()
* DESCRIPTION: copy sectors from dumpfile to swapfile
*/
void sw_copy()
{
if (dump >= 0) {
if (dsectors > 0) {
register sector n;
n = CHUNKSZ;
if (n > dsectors) {
n = dsectors;
}
copy(ssectors, n);
ssectors += n;
}
if (dsectors == 0) {
close(dump);
dump = -1;
}
}
}
/*
* NAME: swap->dump()
* DESCRIPTION: dump swap file
*/
int sw_dump(dumpfile)
char *dumpfile;
{
register header *h;
register sector sec;
char buffer[STRINGSZ + 4];
register sector n;
dump_header dh;
if (dump >= 0) {
if (dsectors > 0) {
/* copy remaining dump sectors */
n = dsectors;
copy(ssectors, n);
ssectors += n;
}
close(dump);
}
sprintf(buffer, "%s.old", dumpfile);
unlink(buffer);
rename(dumpfile, buffer);
if (swap < 0) {
sw_create();
}
/* flush the cache and adjust sector map */
for (h = last; h != (header *) NULL; h = h->prev) {
sec = h->swap;
if (h->dirty) {
/*
* Dump the sector to swap file
*/
if (sec == SW_UNUSED) {
/*
* allocate new sector in swap file
*/
if (sfree == SW_UNUSED) {
sec = ssectors++;
} else {
sec = sfree;
sfree = smap[sec];
}
h->swap = sec;
}
lseek(swap, (sec + 1L) * sectorsize, SEEK_SET);
if (write(swap, (char *) (h + 1), sectorsize) < 0) {
fatal("cannot write swap file");
}
}
map[h->sec] = sec;
}
/* move to dumpfile */
close(swap);
if (rename(swapfile, dumpfile) < 0) {
/*
* The rename failed. Attempt to copy the dumpfile instead.
* This will take a long, long while, so keep the swapfile and
* dumpfile on the same file system if at all possible.
*/
swap = open(swapfile, O_RDWR | O_BINARY, 0);
dump = open(dumpfile, O_RDWR | O_CREAT | O_TRUNC | O_BINARY, 0600);
if (swap < 0 || dump < 0) {
fatal("cannot move swap file");
}
/* copy initial sector */
if (read(swap, cbuf, sectorsize) <= 0) {
fatal("cannot read swap file");
}
if (write(dump, cbuf, sectorsize) < 0) {
fatal("cannot write dump file");
}
/* copy swap sectors */
for (n = ssectors; n > 0; --n) {
if (read(swap, cbuf, sectorsize) <= 0) {
fatal("cannot read swap file");
}
if (write(dump, cbuf, sectorsize) < 0) {
fatal("cannot write dump file");
}
}
} else {
/*
* The rename succeeded; reopen the new dumpfile.
*/
dump = open(dumpfile, O_RDWR | O_BINARY, 0);
if (dump < 0) {
fatal("cannot reopen dump file");
}
swap = -1;
}
/* write header */
dh.secsize = sectorsize;
dh.nsectors = nsectors;
dh.ssectors = ssectors;
dh.nfree = nfree;
dh.mfree = mfree;
lseek(dump, sectorsize - (long) sizeof(dump_header), SEEK_SET);
if (write(dump, (char *) &dh, sizeof(dump_header)) < 0) {
fatal("cannot write swap header to dump file");
}
/* write map */
lseek(dump, (ssectors + 1L) * sectorsize, SEEK_SET);
if (write(dump, (char *) map, nsectors * sizeof(sector)) < 0) {
fatal("cannot write sector map to dump file");
}
if (swap < 0) {
/*
* the swapfile was moved
*/
/* create bitmap */
dsectors = nsectors;
dcursec = 0;
memset(bmap, -1, (dsectors + 7) >> 3);
for (sec = mfree; sec != SW_UNUSED; sec = map[sec]) {
BCLR(bmap, sec);
--dsectors;
}
/* fix the sector map and bitmap */
for (h = last; h != (header *) NULL; h = h->prev) {
map[h->sec] = ((long) h - (long) mem) / slotsize;
h->swap = SW_UNUSED;
h->dirty = TRUE;
BCLR(bmap, h->sec);
--dsectors;
}
ssectors = 0;
sfree = SW_UNUSED;
} else {
/*
* the swapfile was copied
*/
/* fix the sector map */
for (h = last; h != (header *) NULL; h = h->prev) {
map[h->sec] = ((long) h - (long) mem) / slotsize;
h->dirty = FALSE;
}
}
return dump;
}
/*
* NAME: swap->restore()
* DESCRIPTION: restore dump file
*/
void sw_restore(fd, secsize)
int fd;
unsigned int secsize;
{
dump_header dh;
/* restore swap header */
lseek(fd, (long) secsize - (conf_dsize(dh_layout) & 0xff), SEEK_SET);
conf_dread(fd, (char *) &dh, dh_layout, (Uint) 1);
if (dh.secsize != secsize || dh.nsectors > swapsize) {
error("Wrong sector size or too many sectors in restore file");
}
restoresecsize = secsize;
/* seek beyond swap sectors */
lseek(fd, (dh.ssectors + 1L) * secsize, SEEK_SET);
/* restore swap map */
conf_dread(fd, (char *) smap, "d", (Uint) dh.nsectors);
restore = fd;
}