Files
Arquivotheca.Solaris-2.5/cmd/backup/database/dbserv/instance_update.c
seta75D 7c4988eac0 Init
2021-10-11 19:38:01 -03:00

665 lines
15 KiB
C
Executable File

#ident "@(#)instance_update.c 1.11 92/03/25"
/*
* Copyright (c) 1990,1991,1992 by Sun Microsystems, Inc.
*/
#include "defs.h"
#include <sys/stat.h>
#include <sys/mman.h>
static int inst_fd = -1;
static u_long inst_maxblk = 0;
static char curhost[128];
static int transfd = -1, transsize;
/*
* also used by reclaim utility
*/
char *inst_dblockmap;
int inst_dblockmapsize;
static struct instance_record *freelisthead;
static int freelistdirty;
#ifdef __STDC__
static int get_user_recsize(void);
static void instance_alloc(void);
static void freeall(void);
static void instance_dirtyrec(u_long);
static struct instance_entry *avail_ent(struct instance_record *);
static int isdeleted(struct instance_entry *);
static int knownvalid(struct instance_entry *);
static int knowndeleted(struct instance_entry *);
static int checkstatus(struct instance_entry *);
#else
static int get_user_recsize();
static void instance_alloc();
static void freeall();
static void instance_dirtyrec();
static struct instance_entry *avail_ent();
static int isdeleted();
static int knownvalid();
static int knowndeleted();
static int checkstatus();
#endif
/*
* keep track of file mappings. We keep a read mapping and a write
* mapping. For each, we keep track of the base of the mapping, the
* first valid record in that mapping, the record number of the first
* record in the mapping and the record number of the last record in
* the mapping.
*/
static caddr_t writemap, readmap;
static struct instance_record *wmapfirst;
static struct instance_record *rmapfirst;
static u_long wmapblk1, wmapblkend;
static u_long rmapblk1, rmapblkend;
#ifdef __STDC__
instance_open(const char *host)
#else
instance_open(host)
char *host;
#endif
{
char ifilename[256];
char itransfile[256];
struct stat stbuf;
struct instance_record dummy;
int nentries;
writemap = readmap = NULL;
wmapfirst = rmapfirst = NULL_IREC;
wmapblk1 = wmapblkend = rmapblk1 = rmapblkend = transsize = 0;
(void) sprintf(ifilename, "%s/%s", host, INSTANCEFILE);
if ((inst_fd = open(ifilename, O_RDONLY)) == -1) {
/*
* get user specified default sizes
*/
if (nentries = get_user_recsize()) {
entries_perrec = nentries;
instance_recsize = COMPUTE_INST_RECSIZE(nentries);
} else {
entries_perrec = DEFAULT_INSTANCE_ENTRIES;
instance_recsize = DEFAULT_INSTANCE_RECSIZE;
}
freelisthead = (struct instance_record *)
malloc((unsigned)instance_recsize);
if (freelisthead == NULL) {
(void) fprintf(stderr,
gettext("%s: out of memory\n"), "instance_open");
return (-1);
}
(void) bzero((char *)freelisthead, instance_recsize);
freelisthead->i_entry[0].ie_dumpid = entries_perrec;
freelisthead->i_entry[0].ie_dnode_index = instance_recsize;
inst_maxblk = 0;
} else {
freelistdirty = 0;
if (fstat(inst_fd, &stbuf) == -1) {
perror("instance_open/fstat");
(void) close(inst_fd);
inst_fd = -1;
return (-1);
}
(void) bzero((char *)&dummy, sizeof (struct instance_record));
if (stbuf.st_size >= sizeof (struct instance_record)) {
if (read(inst_fd, (char *)&dummy,
sizeof (struct instance_record)) !=
sizeof (struct instance_record)) {
(void) fprintf(stderr,
gettext("%s: cannot read freelist\n"),
"instance_open");
(void) close(inst_fd);
inst_fd = -1;
return (-1);
}
}
entries_perrec = dummy.i_entry[0].ie_dumpid;
instance_recsize = dummy.i_entry[0].ie_dnode_index;
if (instance_recsize == 0) {
if (nentries = get_user_recsize()) {
entries_perrec = nentries;
instance_recsize =
COMPUTE_INST_RECSIZE(nentries);
} else {
instance_recsize = DEFAULT_INSTANCE_RECSIZE;
entries_perrec = DEFAULT_INSTANCE_ENTRIES;
}
dummy.i_entry[0].ie_dumpid = entries_perrec;
dummy.i_entry[0].ie_dnode_index = instance_recsize;
dummy.ir_next = INSTANCE_FREEREC;
freelistdirty = 1;
}
freelisthead = (struct instance_record *)
malloc((unsigned)instance_recsize);
if (freelisthead == NULL) {
(void) fprintf(stderr,
gettext("%s: out of memory\n"), "instance_open");
(void) close(inst_fd);
inst_fd = -1;
return (-1);
}
(void) bzero((char *)freelisthead, instance_recsize);
bcopy((char *)&dummy, (char *)freelisthead,
sizeof (struct instance_record));
if (stbuf.st_size % instance_recsize) {
(void) fprintf(stderr, gettext("%s: block size\n"),
"instance_open");
(void) close(inst_fd);
inst_fd = -1;
return (-1);
}
inst_maxblk = stbuf.st_size / instance_recsize;
}
(void) sprintf(itransfile, "%s/%s%s", host, INSTANCEFILE, TRANS_SUFFIX);
if ((transfd = open(itransfile, O_RDWR|O_CREAT|O_TRUNC, 0600)) == -1) {
perror("instance_open/transfile");
(void) close(inst_fd);
inst_fd = -1;
return (-1);
}
(void) strcpy(curhost, host);
instance_alloc();
return (0);
}
static int
#ifdef __STDC__
get_user_recsize(void)
#else
get_user_recsize()
#endif
{
/*
* see if users have over-ridden our default instance record
* size.
*/
FILE *fp;
int entries;
if ((fp = fopen(INSTANCECONFIG, "r")) == NULL) {
return (0);
}
if (fscanf(fp, "%d", &entries) != 1) {
(void) fclose(fp);
return (0);
}
(void) fclose(fp);
if (entries <= 0 || entries > 100)
return (0);
return (entries);
}
void
#ifdef __STDC__
instance_close(const char *host)
#else
instance_close(host)
char *host;
#endif
{
#define min(a, b) (a < b ? a : b)
char mapfile[256];
int fd;
if (writemap) {
release_map(writemap, 1);
writemap = NULL;
}
if (readmap) {
release_map(readmap, 0);
readmap = NULL;
}
if (freelistdirty) {
inst_dblockmap[INSTANCE_FREEREC] = 1;
if (lseek(transfd, (off_t)(INSTANCE_FREEREC*instance_recsize),
SEEK_SET) == -1) {
perror("instclose/freerec seek");
} else if (write(transfd, (char *)freelisthead,
instance_recsize) != instance_recsize) {
perror("instclose/freerec write");
}
}
if (ftruncate(transfd, (off_t)(inst_maxblk*instance_recsize)) == -1) {
perror("ftruncate");
(void) fprintf(stderr,
gettext("%s: cannot truncate transaction file\n"),
"instance_close");
exit(1);
}
(void) fsync(transfd);
(void) close(transfd);
transfd = -1;
inst_dblockmapsize = min(inst_maxblk, inst_dblockmapsize);
(void) sprintf(mapfile, "%s/%s%s", host, INSTANCEFILE, MAP_SUFFIX);
if ((fd = open(mapfile, O_RDWR|O_CREAT|O_TRUNC, 0600)) == -1) {
perror("open");
(void) fprintf(stderr, gettext("%s: cannot write dirty map\n"),
"instance_close");
}
if (write(fd, inst_dblockmap,
inst_dblockmapsize) != inst_dblockmapsize) {
perror("inst_dblockmap/write");
}
(void) fsync(fd);
(void) close(fd);
freeall();
if (inst_fd != -1 && close(inst_fd) == -1)
perror("instance_close/close");
inst_fd = -1;
}
#define NEWCACHESIZE 2
#define NEWENT 0
#define OLDENT 1
static struct {
u_long blknum;
struct instance_record *ir;
} newcache[NEWCACHESIZE];
static void
#ifdef __STDC__
instance_alloc(void)
#else
instance_alloc()
#endif
{
newcache[NEWENT].blknum = newcache[OLDENT].blknum = NONEXISTENT_BLOCK;
newcache[NEWENT].ir = (struct instance_record *)
malloc((unsigned)instance_recsize);
newcache[OLDENT].ir = (struct instance_record *)
malloc((unsigned)instance_recsize);
if (newcache[NEWENT].ir == NULL || newcache[OLDENT].ir == NULL) {
(void) fprintf(stderr, gettext("%s: cannot allocate cache\n"),
"instance_update");
exit(1);
}
}
static void
#ifdef __STDC__
freeall(void)
#else
freeall()
#endif
{
free((char *)newcache[OLDENT].ir);
free((char *)newcache[NEWENT].ir);
free((char *)freelisthead);
}
instance_newrec(next)
u_long next;
{
struct instance_record *ip;
u_long curblk;
if (inst_maxblk == 0) {
freelisthead->ir_next = INSTANCE_FREEREC;
freelistdirty++;
inst_maxblk++; /* freelisthead block */
}
ip = newcache[NEWENT].ir;
if (freelisthead->ir_next != INSTANCE_FREEREC) {
/*
* read from the instance file since we know that no
* records are ever freed as a result of update processing.
* If records were freed during update, we would have to
* consider reading from the transaction file here as
* well...
*
* We don't bother with any mapping or caching here
* since we assume this is a uncommon case...
*/
curblk = freelisthead->ir_next;
if (lseek(inst_fd, (off_t)(curblk*instance_recsize),
SEEK_SET) == -1) {
perror("inst_newrec/freeblk lseek");
return (NONEXISTENT_BLOCK);
}
if (read(inst_fd, (char *)ip,
instance_recsize) != instance_recsize) {
perror("inst_newrec/freeblk read");
return (NONEXISTENT_BLOCK);
}
freelistdirty++;
freelisthead->ir_next = ip->ir_next;
} else {
curblk = inst_maxblk++;
}
newcache[NEWENT].blknum = curblk;
if (next == NONEXISTENT_BLOCK)
next = curblk;
ip->ir_next = next;
(void) bzero((char *)ip->i_entry,
(sizeof (struct instance_entry) * entries_perrec));
return (curblk);
}
/*
* XXX the `reclaim' utility calls this
*/
struct instance_record *
instance_getrec(recnum)
u_long recnum;
{
int fd;
struct instance_record *irp;
if (recnum == newcache[NEWENT].blknum)
return (newcache[NEWENT].ir);
if (recnum == newcache[OLDENT].blknum)
return (newcache[OLDENT].ir);
if (inst_dblockmap && inst_dblockmapsize > recnum &&
inst_dblockmap[recnum]) {
(void) fprintf(stderr, gettext("%s: getting dirty block!\n"),
"instance_getrec");
fd = transfd;
if (lseek(fd, (off_t)(recnum*instance_recsize),
SEEK_SET) == -1) {
perror("instance_get/lseek");
return (NULL_IREC);
}
if (read(fd, (char *)newcache[OLDENT].ir,
instance_recsize) != instance_recsize) {
perror("instance_get/read");
return (NULL_IREC);
}
} else {
fd = inst_fd;
irp = (struct instance_record *)getmapblock(&readmap,
(char **)&rmapfirst, &rmapblk1, &rmapblkend, recnum,
/*LINTED*/
instance_recsize, fd, PROT_READ, 0, (int *)0);
bcopy((char *)irp, (char *)newcache[OLDENT].ir,
instance_recsize);
}
newcache[OLDENT].blknum = recnum;
return (newcache[OLDENT].ir);
}
static void
instance_dirtyrec(num)
u_long num;
{
struct instance_record *p, *dirtyp;
if (inst_dblockmap == NULL || num >= inst_dblockmapsize) {
int newsize;
char *tp;
if (inst_dblockmap) {
newsize = num+1000;
tp = (char *)realloc(inst_dblockmap, (unsigned)newsize);
if (tp == NULL) {
(void) fprintf(stderr, gettext(
"cannot extend instance map\n"));
exit(1);
}
inst_dblockmap = tp;
(void) bzero(&tp[inst_dblockmapsize],
newsize-inst_dblockmapsize);
inst_dblockmapsize = newsize;
} else {
newsize = inst_dblockmapsize = num+1000;
tp = (char *)malloc((unsigned)newsize);
if (tp == NULL) {
(void) fprintf(stderr, gettext(
"cannot allocate instance map\n"));
exit(1);
}
inst_dblockmap = tp;
(void) bzero(tp, newsize);
}
}
inst_dblockmap[num] = 1;
if (num == newcache[NEWENT].blknum)
dirtyp = newcache[NEWENT].ir;
else if (num == newcache[OLDENT].blknum)
dirtyp = newcache[OLDENT].ir;
else {
(void) fprintf(stderr,
gettext("%s: block mismatch!\n"), "instance_dirty");
return;
}
p = (struct instance_record *)getmapblock(&writemap,
(char **)&wmapfirst, &wmapblk1, &wmapblkend,
num, instance_recsize,
/*LINTED*/
transfd, PROT_READ|PROT_WRITE, 1, &transsize);
bcopy((char *)dirtyp, (char *)p, instance_recsize);
}
instance_addent(recnum, dumpid, dnode_idx)
u_long recnum;
u_long dumpid;
u_long dnode_idx;
{
struct instance_record *ip;
struct instance_entry *ep;
u_long firstrec, thisrec;
if ((ip = instance_getrec(recnum)) == NULL_IREC)
return (-1);
firstrec = thisrec = recnum;
while ((ep = avail_ent(ip)) == NULL_IENT) {
if (ip->ir_next == firstrec) {
/*
* XXX: this blows the single block cache
* strategy! However, it seems we never have
* more than 2 blocks "active" at a time, so
* a two block "cache" ought to work...
*/
ip->ir_next = instance_newrec(ip->ir_next);
instance_dirtyrec(thisrec);
}
thisrec = ip->ir_next;
if ((ip = instance_getrec(ip->ir_next)) == NULL_IREC)
return (-1);
}
ep->ie_dumpid = dumpid;
ep->ie_dnode_index = dnode_idx;
instance_dirtyrec(thisrec);
return (0);
}
static struct instance_entry *
avail_ent(rp)
struct instance_record *rp;
{
register int i;
register struct instance_entry *ep;
for (i = 0, ep = &rp->i_entry[i]; i < entries_perrec;
i++, ep = &rp->i_entry[i]) {
if (ep->ie_dumpid == 0) {
return (ep);
} else if (isdeleted(ep)) {
/*
* XXX: need to check performance hit here!
*/
(void) bzero((char *)ep,
sizeof (struct instance_entry));
return (ep);
}
}
return (NULL_IENT);
}
static int
isdeleted(ep)
struct instance_entry *ep;
{
if (knownvalid(ep))
return (0);
else if (knowndeleted(ep))
return (1);
else
return (checkstatus(ep));
}
static struct instance_list {
u_long dumpid;
struct instance_list *nxt;
} *valid, *deleted;
static int
knownvalid(ep)
struct instance_entry *ep;
{
register struct instance_list *p;
for (p = valid; p; p = p->nxt) {
if (p->dumpid == ep->ie_dumpid)
return (1);
}
return (0);
}
static int
knowndeleted(ep)
struct instance_entry *ep;
{
register struct instance_list *p;
for (p = deleted; p; p = p->nxt) {
if (p->dumpid == ep->ie_dumpid)
return (1);
}
return (0);
}
static int
checkstatus(ep)
struct instance_entry *ep;
{
struct stat stbuf;
char filename[64];
int rc;
struct instance_list *p;
if ((p = (struct instance_list *)
malloc((unsigned)sizeof (struct instance_list))) ==
(struct instance_list *)0) {
(void) fprintf(stderr,
gettext("%s: out of memory\n"), "checkstatus");
return (0);
}
p->dumpid = ep->ie_dumpid;
p->nxt = (struct instance_list *)0;
(void) sprintf(filename, "%s/%s.%lu",
curhost, DNODEFILE, ep->ie_dumpid);
if (lstat(filename, &stbuf) == -1) {
/* treat it as deleted */
p->nxt = deleted;
deleted = p;
rc = 1;
} else {
p->nxt = valid;
valid = p;
rc = 0;
}
return (rc);
}
/*
* these functions are used only by the compress utility (not during
* update).
*/
/*
* check for any valid instances at the given instance record.
* If none, free the record.
*/
unused_instance(irec, cnt)
u_long irec;
int *cnt;
{
struct instance_record *irp;
struct instance_entry *ie;
register int i;
u_long startrec, thisrec, freerec;
/*
* XXX: what about coalescing sparse instance records???
*/
if (inst_dblockmap && inst_dblockmapsize > irec &&
inst_dblockmap[irec]) {
/*
* during free space reclaim the only way for a
* record to be dirty is if it has already been freed.
*/
return (1);
}
startrec = thisrec = irec;
do {
if ((irp = instance_getrec(thisrec)) == NULL_IREC) {
(void) fprintf(stderr,
gettext("unused istance cannot get record %lu\n"),
irec);
exit(1);
}
for (i = 0; i < entries_perrec; i++) {
ie = &irp->i_entry[i];
if (ie->ie_dumpid == 0)
continue;
if (knownvalid(ie))
return (0);
else if (knowndeleted(ie))
continue;
else if (checkstatus(ie) == 0)
return (0);
}
thisrec = irp->ir_next;
} while (thisrec != startrec);
/*
* if we get here, this set of instance records does not
* point at any valid dumps.
*/
startrec = thisrec = irec;
do {
if ((irp = instance_getrec(thisrec)) == NULL_IREC) {
(void) fprintf(stderr,
gettext("unused instance cannot get record %lu\n"),
irec);
exit(1);
}
freerec = thisrec;
thisrec = irp->ir_next;
(*cnt)++;
instance_freerec(freerec, irp);
} while (thisrec != startrec);
return (1);
}
void
instance_freerec(num, irp)
u_long num;
struct instance_record *irp;
{
irp->ir_next = freelisthead->ir_next;
freelisthead->ir_next = num;
instance_dirtyrec(num);
freelistdirty = 1;
}