Initial IBM OpenAFS 1.0 tree
[openafs.git] / src / budb / procs.c
1 /* Copyright (C) 1990, 1989 Transarc Corporation - All rights reserved */
2 /*
3  * (C) COPYRIGHT IBM CORPORATION 1989
4  * LICENSED MATERIALS - PROPERTY OF IBM
5  */
6
7 /* TBD
8  *      ht_lookupEntry - tape ids
9  *      Truncate Tape - tape id's
10  *      usetape - tape id's
11  */
12
13 #include <afs/param.h>
14 #ifdef AFS_NT40_ENV
15 #include <winsock2.h>
16 #else
17 #include <netinet/in.h>
18 #include <sys/file.h>
19 #include <sys/param.h>
20 #include <sys/time.h>
21 #include <sys/resource.h>
22 #include <strings.h>
23 #endif
24 #include <sys/types.h>
25 #include <afs/stds.h>
26 #include <afs/bubasics.h>
27 #include <stdio.h>
28 #include <lock.h>
29 #include <ubik.h>
30 #include <lwp.h>
31 #include <rx/xdr.h>
32 #include <rx/rx.h>
33 #include <rx/rxkad.h>
34 #include <des.h>
35 #include <afs/cellconfig.h>
36 #include <afs/auth.h>
37 #include <errno.h>
38 #include "budb.h"
39 #include "budb_errs.h"
40 #include "database.h"
41 #include "error_macros.h"
42 #include "globals.h"
43 #include "afs/audit.h"
44 #include <afs/afsutil.h>
45
46 #undef min
47 #undef max
48
49
50 extern struct ubik_dbase *BU_dbase;
51 extern struct afsconf_dir *BU_conf;     /* for getting cell info */
52 extern afs_int32 myHost;
53 extern struct memoryDB db;              /* incore copies of db structures */
54
55 afs_int32 AddVolume(), AddVolumes(), CreateDump(), DoDeleteDump(), DoDeleteTape(), ListDumps();
56 afs_int32 DeleteVDP(), FindClone(), FindDump(), FindLatestDump();
57 afs_int32 FinishDump(), FinishTape(), GetDumps(), getExpiration(), T_DumpDatabase();
58 afs_int32 makeAppended(), MakeDumpAppended(), FindLastTape(), GetTapes();
59 afs_int32 GetVolumes(), UseTape(), T_DumpHashTable(), T_GetVersion();
60
61 /* Text block management */
62
63 struct memTextBlock
64 {
65     struct memTextBlock *mtb_next;      /* next in chain */
66     afs_int32                   mtb_nbytes;     /* # of bytes in this block */
67     struct blockHeader  mtb_blkHeader;  /* in memory header */
68     dbadr               mtb_addr;       /* disk address of block */
69 };
70
71 typedef struct memTextBlock     memTextBlockT;
72 typedef memTextBlockT           *memTextBlockP;
73
74 /* These variable are for returning debugging info about the state of the
75    server.  If they get trashed during multi-threaded operation it doesn't
76    matter. */
77
78 /* This is global so COUNT_REQ in krb_udp.c can refer to it. */
79 char *lastOperation;                    /* name of last operation */
80 static Date  lastTrans;                 /* time of last transaction */
81
82 /* procsInited is sort of a lock: during a transaction only one process runs
83    while procsInited is false. */
84
85 static int procsInited = 0;
86
87 /* This variable is protected by the procsInited flag. */
88
89 static int (*rebuildDatabase)();
90
91 /* AwaitInitialization 
92  * Wait unitl budb has initialized (InitProcs). If it hasn't
93  * within 5 seconds, then return no quorum.
94  */ 
95 afs_int32
96 AwaitInitialization() 
97
98   afs_int32 start = 0;
99
100   while (!procsInited) 
101   {
102       if (!start) start = time(0);
103       else if (time(0)-start > 5) return UNOQUORUM;
104       IOMGR_Sleep (1);
105   } 
106   return 0; 
107 }
108
109 /* tailCompPtr
110  *      name is a pathname style name, determine trailing name and return
111  *      pointer to it
112  */
113
114 char *
115 tailCompPtr(pathNamePtr)
116      char *pathNamePtr;
117 {
118     char *ptr;
119     ptr = rindex(pathNamePtr, '/');
120     if ( ptr == 0 )
121     {
122         /* this should never happen */
123         LogError(0, "tailCompPtr: could not find / in name(%s)\n",
124                pathNamePtr);
125         return(pathNamePtr);
126     }
127     else
128         ptr++;                                  /* skip the / */
129     return(ptr);
130 }
131
132 /* callPermitted
133  *      Check to see if the caller is a SuperUser.
134  * exit:
135  *      0 - no
136  *      1 - yes
137  */
138
139 callPermitted(call)
140     struct rx_call *call;
141 {
142     int permitted = 0;
143     struct afsconf_dir *acdir;
144
145     acdir = afsconf_Open(AFSDIR_SERVER_ETC_DIRPATH);
146     if (!acdir) return 0;
147
148      if ( afsconf_SuperUser(acdir, call, (char *)0) )
149          permitted = 1;
150
151   exit:
152     if (acdir) afsconf_Close(acdir);
153     return(permitted);
154 }
155
156 /* InitRPC
157  *      This is called by every RPC interface to create a Ubik transaction
158  *      and read the database header into core
159  * entry:
160  *      ut
161  *      lock
162  *      this_op
163  * notes:
164  *      sets a lock on byte 1 of the database. Looks like it enforces
165  *      single threading by use of the lock.
166  */
167
168 afs_int32 
169 InitRPC (ut, lock, this_op)
170   struct ubik_trans **ut;
171   int   lock;                           /* indicate read/write transaction */
172   int   this_op;                        /* opcode of RCP, for COUNT_ABO */
173 {   
174     int   code;
175     float wait = 0.91;                  /* start waiting for 1 second */
176
177 start:
178     /* wait for server initialization to finish if this is not InitProcs calling */
179     if (this_op) 
180         if (code = AwaitInitialization()) 
181             return code;
182
183     for (code = UNOQUORUM; code == UNOQUORUM; )
184     {
185         code = ubik_BeginTrans(BU_dbase, 
186                                ((lock == LOCKREAD) ? UBIK_READTRANS : UBIK_WRITETRANS), 
187                                ut);
188         if (code == UNOQUORUM)
189         {                                            /* no quorum elected */
190             if (wait < 1) Log("Waiting for quorum election\n");
191             if (wait < 15.0) wait *= 1.1;
192             IOMGR_Sleep ((int)wait);
193         }
194     }
195     if (code) return code;
196     if (wait > 1) Log("Have established quorum\n");
197
198     /* set lock at posiion 1, for 1 byte of type lock */
199     if (code = ubik_SetLock (*ut, 1, 1, lock))
200     {
201         ubik_AbortTrans (*ut);
202         return code;
203     }
204
205     /* check that dbase is initialized and setup cheader */
206     if (lock == LOCKREAD)
207     {
208         /* init but don't fix because this is read only */
209         if ( code = CheckInit(*ut, 0) )
210         {
211             ubik_AbortTrans(*ut);
212             if ( code = InitRPC(ut, LOCKWRITE, 0) )         /* Now fix the database */
213             {
214                 LogError(code, "InitRPC: InitRPC failed\n");
215                 return code;
216             }
217             if ( code = ubik_EndTrans(*ut) )
218             {
219                 LogError(code, "InitRPC: ubik_EndTrans failed\n");
220                 return code;
221             }
222             goto start;                                     /* now redo the read transaction */
223         }
224     }
225     else 
226     {
227         if (code = CheckInit(*ut, rebuildDatabase)) 
228         {
229             ubik_AbortTrans(*ut);
230             return code;
231         }
232     }
233     lastTrans = time(0);
234     return 0;
235 }
236
237 /* This is called to initialize a newly created database */
238 static int initialize_database (ut)
239   struct ubik_trans *ut;
240 {
241     return 0;
242 }
243
244 static int noAuthenticationRequired;    /* global state */
245 static int recheckNoAuth;               /* global state */
246
247 afs_int32
248 InitProcs()
249 {
250     struct ubik_trans  *ut;
251     afs_int32 code = 0;
252
253     procsInited = 0;
254
255     if ( (globalConfPtr->myHost == 0) || (BU_conf == 0) )
256         ERROR(BUDB_INTERNALERROR);
257
258     recheckNoAuth = 1;
259
260     if ( globalConfPtr->debugFlags & DF_NOAUTH )
261         noAuthenticationRequired = 1;
262
263     if ( globalConfPtr->debugFlags & DF_RECHECKNOAUTH )
264         recheckNoAuth = 0;
265
266     if (recheckNoAuth)
267         noAuthenticationRequired = afsconf_GetNoAuthFlag(BU_conf);
268
269     if (noAuthenticationRequired) 
270         LogError(0, "Running server with security disabled\n");
271
272     InitDB();
273
274     rebuildDatabase = initialize_database;
275
276     if (code = InitRPC (&ut, LOCKREAD, 0)) 
277     {
278         LogError(code, "InitProcs: InitRPC failed\n");
279         return code;
280     }
281     code = ubik_EndTrans(ut);
282     if (code) 
283     {
284         LogError(code, "InitProcs: ubik_EndTrans failed\n");
285         return code;
286     }
287
288     rebuildDatabase = 0;                /* only do this during init */
289     procsInited = 1;
290
291   error_exit:
292     return(code);
293 }
294
295 struct returnList {
296     int nElements;                      /* number in list */
297     int allocSize;                      /* number of elements allocated */
298     dbadr *elements;                    /* array of addresses */
299 };
300
301 static void InitReturnList (list)
302   struct returnList *list;
303 {
304     list->nElements   = 0;
305     list->allocSize   = 0;
306     list->elements    = (dbadr *)0;
307 }
308
309 static void FreeReturnList (list)
310    struct returnList *list;
311 {
312    if (list->elements) free(list->elements);
313    list->elements  = (dbadr *)0;
314    list->nElements = 0;
315 }
316
317 /* As entries are collected, they are added to a return list. Once all
318  * entries have been collected, it is then placed in the return buffer
319  * with SendReturnList(). The first *to_skipP are not recorded.
320  */
321 static afs_int32 AddToReturnList (list, a, to_skipP)
322   struct returnList *list;
323   dbadr a;
324   afs_int32 *to_skipP;
325 {
326    char  *tmp;
327    afs_int32 size;
328
329     if (a == 0) return 0;
330     if (*to_skipP > 0) {
331        (*to_skipP)--;
332        return 0;
333     }
334
335     /* Up to 5 plus a maximum so SendReturnList() knows if we 
336      * need to come back for more.
337      */
338     if (list->nElements >= BUDB_MAX_RETURN_LIST+5)
339        return BUDB_LIST2BIG;
340
341     if (list->nElements >= list->allocSize) {
342         if (list->elements == 0) {
343             size = 10;
344             tmp = (char *) malloc (sizeof(dbadr) * size);
345         } else {
346             size = list->allocSize + 10;
347             tmp = (char *) realloc (list->elements, sizeof(dbadr) * size);
348         }
349         if (!tmp) return BUDB_NOMEM;
350         list->elements = (dbadr *) tmp;
351         list->allocSize = size;
352     }
353
354     list->elements[list->nElements] = a;
355     list->nElements++;
356     return 0;
357 }
358
359 afs_int32 FillVolEntry(ut, va, vol)
360   struct ubik_trans       *ut;
361   dbadr                   va;
362   struct budb_volumeEntry *vol;
363 {
364     struct dump        d;
365     struct tape        t;
366     struct volInfo     vi;
367     struct volFragment vf;
368
369     if (dbread (ut, va,             &vf, sizeof(vf))) return BUDB_IO;   /* The volFrag */
370     if (dbread (ut, ntohl(vf.vol),  &vi, sizeof(vi))) return BUDB_IO;   /* The volInfo */
371     if (dbread (ut, ntohl(vf.tape), &t,  sizeof(t)))  return BUDB_IO;   /* The tape */
372     if (dbread (ut, ntohl(t.dump),  &d,  sizeof(d)))  return BUDB_IO;   /* The dump */
373
374     strcpy (vol->name,   vi.name);
375     strcpy (vol->server, vi.server);
376     strcpy (vol->tape,   t.name);
377     vol->tapeSeq   = ntohl(t.seq);
378     vol->dump      = ntohl(d.id);
379     vol->position  = ntohl(vf.position);
380     vol->clone     = ntohl(vf.clone);
381     vol->incTime   = ntohl(vf.incTime);
382     vol->nBytes    = ntohl(vf.nBytes);
383     vol->startByte = ntohl(vf.startByte);
384     vol->flags     = (ntohs(vf.flags) & VOLFRAGMENTFLAGS); /* low  16 bits here */
385     vol->flags    |= (ntohl(vi.flags) & VOLINFOFLAGS);     /* high 16 bits here */
386     vol->seq       = ntohs(vf.sequence);
387     vol->id        = ntohl(vi.id);
388     vol->partition = ntohl(vi.partition);
389
390     return 0;
391 }
392
393 afs_int32 FillDumpEntry (ut, da, dump)
394   struct ubik_trans *ut;
395   dbadr da;
396   struct budb_dumpEntry *dump;
397 {
398     struct dump d, ad;
399
400     if (dbread (ut, da, &d, sizeof(d))) return BUDB_IO;
401     dump->id = ntohl(d.id);
402     dump->flags = ntohl(d.flags);
403     dump->created = ntohl(d.created);
404     strncpy (dump->name, d.dumpName, sizeof(dump->name));
405     strncpy (dump->dumpPath, d.dumpPath, sizeof(dump->dumpPath));
406     strncpy (dump->volumeSetName, d.volumeSet, sizeof(dump->volumeSetName));
407
408     dump->parent = ntohl(d.parent);
409     dump->level = ntohl(d.level);
410     dump->nVolumes = ntohl(d.nVolumes);
411
412     tapeSet_ntoh(&d.tapes, &dump->tapes);
413
414     if (strlen(d.dumper.name) < sizeof(dump->dumper.name))
415         strcpy (dump->dumper.name, d.dumper.name);
416     if (strlen(d.dumper.instance) < sizeof(dump->dumper.instance))
417         strcpy (dump->dumper.instance, d.dumper.instance);
418     if (strlen(d.dumper.cell) < sizeof(dump->dumper.cell))
419         strcpy (dump->dumper.cell, d.dumper.cell);
420
421     /* Get the initial dumpid and the appended dump id */
422     dump->initialDumpID = ntohl(d.initialDumpID);
423     if (d.appendedDumpChain)
424     {
425         if (dbread (ut, ntohl(d.appendedDumpChain), &ad, sizeof(ad))) return BUDB_IO;
426         dump->appendedDumpID = ntohl(ad.id);
427     }
428     else
429         dump->appendedDumpID = 0;
430
431     /* printf("dump name %s, parent %d, level %d\n",
432         d.dumpName, ntohl(d.parent), ntohl(d.level)); */
433
434     return 0;
435 }
436
437 afs_int32 FillTapeEntry (ut, ta, tape)
438   struct ubik_trans *ut;
439   dbadr ta;
440   struct budb_tapeEntry *tape;
441 {
442     struct tape t;
443     struct dump d;
444     afs_int32 code;
445
446     if ( dbread (ut, ta, &t, sizeof(t)) )
447         return BUDB_IO;
448
449     /* Get the tape's expiration date */
450     if ( code = getExpiration(ut,&t) )
451         return(code);
452
453     strcpy (tape->name, t.name);
454     tape->flags = ntohl(t.flags);
455     tape->written = ntohl(t.written);
456     tape->expires = ntohl(t.expires);
457     tape->nMBytes = ntohl(t.nMBytes);
458     tape->nBytes = ntohl(t.nBytes);
459     tape->nFiles = ntohl(t.nFiles);
460     tape->nVolumes = ntohl(t.nVolumes);
461     tape->seq = ntohl(t.seq);
462     tape->labelpos = ntohl(t.labelpos);
463     tape->useCount = ntohl(t.useCount);
464     tape->useKBytes = ntohl(t.useKBytes);
465
466     if (dbread (ut, ntohl(t.dump), &d, sizeof(d))) return BUDB_IO;
467     tape->dump = ntohl(d.id);
468     return 0;
469 }
470
471 #define returnList_t budb_dumpList *
472
473 /* SendReturnList
474  *      A list of elements of size e_size is in list, collected
475  *      with AddToReturnList(). We will move this to a correspoding
476  *      return list, eList, via FillProc(). nextInodeP tells us
477  *      if there are more and how many to skip on the next request.
478  */
479 static afs_int32
480 SendReturnList (ut, list, FillProc, e_size, index, nextIndexP, dbTimeP, eList)
481   struct ubik_trans *ut;
482   struct returnList *list;              /* list of elements to return */
483   afs_int32 (*FillProc)();                      /* proc to fill entry */
484   int   e_size;                         /* size of each element */
485   afs_int32  index;                             /* index from previous call */
486   afs_int32 *nextIndexP;                        /* if more elements are available */
487   afs_int32 *dbTimeP;                   /* time of last db update */
488   budb_dumpList *eList;                 /* rxgen list structure (e.g.) */
489 {
490     afs_int32 code;
491     int  to_return;
492     int  i;
493     char *e;
494
495     *nextIndexP = -1;
496     *dbTimeP = ntohl(db.h.lastUpdate);
497
498     /* Calculate how many to return. Don't let if go over
499      * BUDB_MAX_RETURN_LIST nor the size of our return list.
500      */
501     to_return = list->nElements;
502     if (to_return > BUDB_MAX_RETURN_LIST)
503        to_return = BUDB_MAX_RETURN_LIST;
504     if (eList->budb_dumpList_len && (to_return > eList->budb_dumpList_len))
505        to_return = eList->budb_dumpList_len;
506
507     /* Allocate space for the return values if needed and zero it */
508     if (eList->budb_dumpList_val == 0) {
509        eList->budb_dumpList_val = (struct budb_dumpEntry *)malloc (e_size * to_return);
510        if (!eList->budb_dumpList_val) return(BUDB_NOMEM);
511     }
512     bzero(eList->budb_dumpList_val, e_size * to_return);
513     eList->budb_dumpList_len = to_return;
514
515     e = (char *)(eList->budb_dumpList_val);
516     for (i=0; i<to_return; i++, e += e_size) {
517         code = (*FillProc) (ut, list->elements[i], e);
518         if (code) return code;
519     }
520
521     if (list->nElements > i)
522        *nextIndexP = index + i;
523     return 0;
524 }
525
526 /* Come here to delete a volInfo structure. */
527
528 static afs_int32 DeleteVolInfo (ut, via, vi)
529   struct ubik_trans *ut;
530   dbadr via;
531   struct volInfo *vi;
532 {   afs_int32 code;
533     dbadr hvia;
534     struct volInfo hvi;
535
536     if (vi->firstFragment) return 0;    /* still some frags, don't free yet */
537     if (vi->sameNameHead == 0) { /* this is the head */
538         if (vi->sameNameChain) return 0; /* empty head, some non-heads left */
539
540         code = ht_HashOut (ut, &db.volName, via, vi);
541         if (code) return code;
542         code = FreeStructure (ut, volInfo_BLOCK, via);
543         return code;
544     }
545     hvia = ntohl(vi->sameNameHead);
546     if (dbread (ut, hvia, &hvi, sizeof(hvi))) return BUDB_IO;
547     code = RemoveFromList (ut, hvia, &hvi, &hvi.sameNameChain, via, vi, &vi->sameNameChain);
548     if (code == -1) return BUDB_DATABASEINCONSISTENT;
549     if (code == 0) code = FreeStructure (ut, volInfo_BLOCK, via);
550     return code;
551 }
552
553 /* Detach a volume fragment from its volInfo structure.  Its tape chain is
554    already freed.  This routine frees the structure and the caller must not
555    write it out. */
556
557 static afs_int32 DeleteVolFragment (ut, va, v)
558   struct ubik_trans *ut;
559   dbadr va;
560   struct volFragment *v;
561 {   afs_int32 code;
562     struct volInfo vi;
563     dbadr via;
564
565     via = ntohl(v->vol);
566     if (dbread (ut, via, &vi, sizeof(vi))) return BUDB_IO;
567     code = RemoveFromList (ut, via, &vi, &vi.firstFragment, va, v, &v->sameNameChain);
568     if (code == -1) return BUDB_DATABASEINCONSISTENT;
569     if (code) return code;
570     if (vi.firstFragment == 0)
571         if (code = DeleteVolInfo (ut, via, &vi)) return code;
572     if (code = FreeStructure (ut, volFragment_BLOCK, va)) return code;
573
574     /* decrement frag counter */
575     code = set_word_addr (ut, via, &vi, &vi.nFrags, htonl(ntohl(vi.nFrags)-1));
576     if (code) return code;
577     return 0;
578 }
579
580 /* DeleteTape - by freeing all its volumes and removing it from its dump chain.
581  * The caller will remove it from the hash table if necessary.  The caller is
582  * also responsible for writing the tape out if necessary. */
583
584 static afs_int32 DeleteTape (ut, ta, t)
585   struct ubik_trans *ut;
586   dbadr ta;
587   struct tape *t;
588 {   afs_int32 code;
589     struct dump d;
590     dbadr da;
591
592     da = ntohl(t->dump);
593     if (da == 0) return BUDB_DATABASEINCONSISTENT;
594     if (dbread (ut, da, &d, sizeof(d))) return BUDB_IO;
595     if (d.firstTape == 0) return BUDB_DATABASEINCONSISTENT;
596
597     code = RemoveFromList (ut, da, &d, &d.firstTape, ta, t, &t->nextTape);
598     if (code == -1) return BUDB_DATABASEINCONSISTENT;
599     if (code) return code;
600
601     /* Since the tape should have been truncated there should never be any
602      * volumes in the tape. */
603     if (t->firstVol || t->nVolumes) return BUDB_DATABASEINCONSISTENT;
604
605     return 0;
606 }
607
608 static afs_int32
609 DeleteDump (ut, da, d)
610      struct ubik_trans *ut;
611      dbadr da;
612      struct dump *d;
613 {  
614     afs_int32  code = 0;
615
616     code = ht_HashOut (ut, &db.dumpIden, da, d);
617     if (code) ERROR(code);
618
619     code = ht_HashOut (ut, &db.dumpName, da, d);
620     if (code) ERROR(code);
621
622     /* Since the tape should have been truncated this should never happen. */
623     if (d->firstTape || d->nVolumes) ERROR(BUDB_DATABASEINCONSISTENT);
624
625     code = FreeStructure (ut, dump_BLOCK, da);
626     if (code) ERROR(code);
627
628   error_exit:
629     return code;
630 }
631
632 /*
633  * VolInfoMatch()
634  *
635  * This is called with a volumeEntry and a volInfo structure and compares
636  * them.  It returns non-zero if they are equal.  It is used by GetVolInfo to
637  * search volInfo structures once it has the head volInfo structure from the
638  * volName hash table.
639  *
640  * When called from GetVolInfo the name compare is redundant.
641  * Always AND the flags with VOLINFOFLAGS for backwards compatability (3.3).
642  */
643
644 static int VolInfoMatch (vol, vi)
645   struct budb_volumeEntry *vol;
646   struct volInfo *vi;
647 {
648     return ( (strcmp (vol->name, vi->name) == 0)      &&        /* same volume name */
649              (vol->id        == ntohl(vi->id))        &&        /* same volume id */
650              ((vol->flags       & VOLINFOFLAGS) == 
651               (ntohl(vi->flags) & VOLINFOFLAGS))      &&        /* same flags */
652              (vol->partition == ntohl(vi->partition)) &&        /* same partition (N/A)*/
653              (strcmp (vol->server, vi->server) == 0) );         /* same server (N/A) */
654 }
655
656 /*
657  * GetVolInfo()
658  *     This routine takes a volumeEntry structure from an RPC interface and
659  * returns the corresponding volInfo structure, creating it if necessary.
660  *
661  *     The caller must write the entry out.
662  */
663
664 static afs_int32 GetVolInfo (ut, volP, viaP, viP)
665   struct ubik_trans       *ut;
666   struct budb_volumeEntry *volP;
667   dbadr                   *viaP;
668   struct volInfo          *viP;
669 {
670     dbadr          hvia, via;
671     struct volInfo hvi;
672     afs_int32          eval, code = 0;
673
674     eval = ht_LookupEntry (ut, &db.volName, volP->name, &via, viP);
675     if (eval) ERROR(eval);
676
677     if (!via)
678     {
679         /* allocate a new volinfo structure */
680         eval = AllocStructure (ut, volInfo_BLOCK, 0, &via, viP);
681         if (eval) ERROR(eval);
682
683         strcpy (viP->name,   volP->name);
684         strcpy (viP->server, volP->server);
685         viP->sameNameHead  = 0;             /* The head of same name chain */
686         viP->sameNameChain = 0;             /* Same name chain is empty */
687         viP->firstFragment = 0;
688         viP->nFrags        = 0;
689         viP->id            = htonl(volP->id);
690         viP->partition     = htonl(volP->partition);
691         viP->flags         = htonl(volP->flags & VOLINFOFLAGS);
692
693         /* Chain onto volname hash table */
694         eval = ht_HashIn (ut, &db.volName, via, viP);
695         if (eval) ERROR(eval);
696
697         LogDebug(4, "volume Info for %s placed at %d\n", db.volName, via);
698     }
699
700     else if ( !VolInfoMatch(volP,viP) )         /* Not the head volinfo struct */
701     {
702         hvia = via;                             /* remember the head volinfo struct */
703         bcopy(viP, &hvi, sizeof(hvi));
704
705         /* Search the same name chain for the correct volinfo structure */
706         for (via=ntohl(viP->sameNameChain); via; via=ntohl(viP->sameNameChain))
707         {
708             eval = dbread (ut, via, viP, sizeof(*viP));
709             if (eval) ERROR(eval);
710
711             if ( VolInfoMatch(volP,viP) ) break;      /* found the one */
712         }
713         
714         /* if the correct volinfo struct isn't found, create one */
715         if (!via)
716         {
717             eval = AllocStructure (ut, volInfo_BLOCK, 0, &via, viP);
718             if (eval) ERROR(eval);
719
720             strcpy (viP->name,   volP->name);
721             strcpy (viP->server, volP->server);
722             viP->nameHashChain = 0;                  /* not in hash table */
723             viP->sameNameHead  = htonl(hvia);        /* chain to head of sameNameChain */
724             viP->sameNameChain = hvi.sameNameChain;
725             viP->firstFragment = 0;
726             viP->nFrags        = 0;
727             viP->id            = htonl(volP->id);
728             viP->partition     = htonl(volP->partition);
729             viP->flags         = htonl(volP->flags & VOLINFOFLAGS);
730
731             /* write the head entry's sameNameChain link */
732             eval = set_word_addr (ut, hvia, &hvi, &hvi.sameNameChain, htonl(via));
733             if (eval) ERROR(eval);
734         }
735     }
736
737     *viaP = via;
738
739   error_exit:
740     return code;
741 }
742
743 /* deletesomevolumesfromtape
744  *      Deletes a specified number of volumes from a tape. The tape 
745  *      and dump are modified to reflect the smaller number of volumes.
746  *      The transaction is not terminated, it is up to the caller to
747  *      finish the transaction and start a new one (if desired).
748  * entry:
749  *      maxvolumestodelete - don't delete more than this many volumes
750  */
751
752 afs_int32
753 deleteSomeVolumesFromTape(ut, tapeAddr, tapePtr, maxVolumesToDelete)
754      struct ubik_trans *ut;
755      dbadr tapeAddr;
756      struct tape *tapePtr;
757      int maxVolumesToDelete;
758 {
759     dbadr              volFragAddr, nextVolFragAddr, dumpAddr;
760     struct volFragment volFrag;
761     struct dump        dump;
762     int                volumesDeleted = 0;
763     afs_int32              eval, code = 0;
764
765     if (!tapePtr) ERROR(0);
766
767     for (volFragAddr=ntohl(tapePtr->firstVol); (volFragAddr && (maxVolumesToDelete > 0));
768          volFragAddr=nextVolFragAddr)
769     {
770         eval = dbread(ut, volFragAddr, &volFrag, sizeof(volFrag));
771         if (eval) ERROR(eval);
772
773         nextVolFragAddr = ntohl(volFrag.sameTapeChain);
774
775         eval = DeleteVolFragment(ut, volFragAddr, &volFrag);
776         if (eval) ERROR(eval);
777
778         maxVolumesToDelete--;
779         volumesDeleted++;
780     }
781
782     /* reset the volume fragment pointer in the tape */
783     tapePtr->firstVol = htonl(volFragAddr);
784     
785     /* diminish the tape's volume count */
786     tapePtr->nVolumes = htonl(ntohl(tapePtr->nVolumes) - volumesDeleted);
787
788     eval = dbwrite(ut, tapeAddr, tapePtr, sizeof(*tapePtr));
789     if (eval) ERROR(eval);
790
791     /* diminish the dump's volume count */
792     dumpAddr = ntohl(tapePtr->dump);
793     eval = dbread(ut, dumpAddr, &dump, sizeof(dump));
794     if (eval) ERROR(eval);
795
796     dump.nVolumes = htonl(ntohl(dump.nVolumes) - volumesDeleted);       
797     eval = dbwrite(ut, dumpAddr, &dump, sizeof(dump));
798     if (eval) ERROR(eval);
799
800   error_exit:
801     return(code);
802 }
803
804 /* deleteDump
805  *      deletes a dump in stages, by repeatedly deleting a small number of
806  *      volumes from the dump until none are left. The dump is then deleted.
807  *
808  *      In the case where multiple calls are made to delete the same
809  *      dump, the operation will succeed but contention for structures
810  *      will result in someone getting back an error.
811  *
812  * entry:
813  *      id - id of dump to delete
814  */
815
816 afs_int32
817 deleteDump(call, id, dumps)
818   struct rx_call *call;
819   dumpId id;
820   budb_dumpsList *dumps;
821 {
822     struct ubik_trans *ut;
823     dbadr             dumpAddr, tapeAddr, appendedDump;
824     struct dump       dump;
825     struct tape       tape;
826     dumpId            dumpid;
827     int               firstdump;
828     afs_int32             eval, code = 0;
829     int               partialDel;
830
831     /* iterate until the dump is truly deleted */
832
833     dumpid = id;
834     while (dumpid)
835     {
836         partialDel = 0;
837         while (1)
838         {
839             eval = InitRPC(&ut, LOCKWRITE, 1);
840             if (eval) ERROR(eval);                      /* can't start transaction */
841
842             eval = ht_LookupEntry (ut, &db.dumpIden, &dumpid, &dumpAddr, &dump);
843             if (eval) ABORT(eval);
844             if (!dumpAddr) ABORT(BUDB_NOENT);               /* can't find dump */
845
846             if ( (dumpid == id) && (dump.initialDumpID) )   /* can't be an appended dump */
847                 ABORT(BUDB_NOTINITIALDUMP);
848
849             tapeAddr = ntohl(dump.firstTape);
850             if (tapeAddr == 0) break;
851             
852             /* there is a tape to delete */
853             eval = dbread(ut, tapeAddr, &tape, sizeof(tape));
854             if (eval) ABORT(eval);
855             
856             if ( ntohl(tape.nVolumes) )
857             {
858                 /* tape is not empty */
859                 eval = deleteSomeVolumesFromTape(ut, tapeAddr, &tape, 10);
860                 if (eval) ABORT(eval);
861             }
862             
863             if ( ntohl(tape.nVolumes) == 0 )
864             {
865                 /* tape is now empty, delete it */
866                 eval = DeleteTape(ut, tapeAddr, &tape);
867                 if (eval) ABORT(eval);
868                 eval = ht_HashOut(ut, &db.tapeName, tapeAddr, &tape);
869                 if (eval) ABORT(eval);
870                 eval = FreeStructure(ut, tape_BLOCK, tapeAddr);
871                 if (eval) ABORT(eval);
872             }
873
874             eval = ubik_EndTrans(ut);
875             if (eval) ERROR(eval);
876             partialDel = 1;
877         }                            /* next deletion portion */
878
879         /* Record the dump just deleted */
880         if (dumps && (dumps->budb_dumpsList_len < BUDB_MAX_RETURN_LIST))
881         {
882            if (dumps->budb_dumpsList_len == 0)
883              dumps->budb_dumpsList_val = (afs_int32 *) malloc(sizeof(afs_int32));
884            else
885              dumps->budb_dumpsList_val = 
886                (afs_int32 *) realloc(dumps->budb_dumpsList_val, 
887                                 (dumps->budb_dumpsList_len+1)*sizeof(afs_int32));
888
889            if ( !dumps->budb_dumpsList_val ) ABORT(BUDB_NOMEM);
890            
891            dumps->budb_dumpsList_val[dumps->budb_dumpsList_len] = dumpid;
892            dumps->budb_dumpsList_len++;
893         }
894
895         appendedDump = ntohl(dump.appendedDumpChain);
896
897         /* finally done. No more tapes left in the dump. Delete the dump itself */
898         eval = DeleteDump(ut, dumpAddr, &dump);
899         if (eval) ABORT(eval);
900
901         /* Now delete the appended dump too */
902         if ( appendedDump )
903         {
904             eval = dbread(ut, appendedDump, &dump, sizeof(dump));
905             if (eval) ABORT(eval);
906
907             dumpid = ntohl(dump.id);
908         }
909         else
910             dumpid = 0;
911           
912         eval = ubik_EndTrans(ut);
913         if (eval) ERROR(eval);
914
915         Log("Delete dump %s (DumpID %u), path %s\n",
916             dump.dumpName, ntohl(dump.id), dump.dumpPath);
917     }
918
919   error_exit:
920     if (code && partialDel) {
921        Log("Delete dump %s (DumpID %u), path %s - INCOMPLETE (code = %u)\n",
922            dump.dumpName, ntohl(dump.id), dump.dumpPath, code);
923     }
924     return(code);
925
926   abort_exit:
927     ubik_AbortTrans(ut);
928     goto error_exit;
929 }
930
931 /* --------------
932  * dump selection routines - used by BUDB_GetDumps
933  * --------------
934  */
935
936 /* most recent dump selection */
937
938 struct chosenDump
939 {
940     struct chosenDump *next;
941     dbadr addr;
942     afs_uint32  date;
943 };
944
945 struct wantDumpRock
946 {
947     int maxDumps;                       /* max wanted */
948     int ndumps;                         /* actual in chain */
949     struct chosenDump *chain;
950 };
951
952
953 wantDump(dumpAddrParam, dumpParam, dumpListPtrParam)
954      char *dumpAddrParam;
955      char *dumpParam;
956      char *dumpListPtrParam;
957 {
958     dbadr dumpAddr;
959     struct dump *dumpPtr;
960     struct wantDumpRock *rockPtr;
961
962     dumpAddr = (dbadr) dumpAddrParam;
963     dumpPtr = (struct dump *) dumpParam;
964     rockPtr = (struct wantDumpRock *) dumpListPtrParam;
965
966     /* if we don't have our full complement, just add another */
967     if ( rockPtr->ndumps < rockPtr->maxDumps )
968         return(1);
969
970     /* got the number we need, select based on date */
971     if (  (afs_uint32) ntohl(dumpPtr->created) > rockPtr->chain->date )
972         return(1);
973
974     return(0);
975 }
976
977 rememberDump(dumpAddrParam, dumpParam,  dumpListPtrParam)
978      char *dumpAddrParam;
979      char *dumpParam;
980      char *dumpListPtrParam;
981 {
982     dbadr dumpAddr;
983     struct dump *dumpPtr;
984     struct wantDumpRock *rockPtr;
985     struct chosenDump *ptr, *deletedPtr, **nextPtr;
986
987     dumpAddr = (dbadr) dumpAddrParam;
988     dumpPtr = (struct dump *) dumpParam;
989     rockPtr = (struct wantDumpRock *) dumpListPtrParam;
990
991     ptr = (struct chosenDump *) malloc(sizeof(*ptr));
992     if (!ptr)
993        return(0);
994     bzero(ptr, sizeof(*ptr));
995     ptr->addr = dumpAddr;
996     ptr->date = (afs_uint32) ntohl(dumpPtr->created);    
997
998     /* Don't overflow the max */
999     while (rockPtr->ndumps >= rockPtr->maxDumps) {
1000        /* have to drop one */
1001        deletedPtr = rockPtr->chain;
1002        rockPtr->chain = deletedPtr->next;
1003        free(deletedPtr);
1004        rockPtr->ndumps--;
1005     }
1006
1007     /* now insert in the right place */
1008     for (nextPtr = &rockPtr->chain; *nextPtr; nextPtr = &((*nextPtr)->next)) {
1009        if (ptr->date < (*nextPtr)->date)
1010           break;
1011     }
1012     ptr->next = *nextPtr;
1013     *nextPtr  = ptr;
1014     rockPtr->ndumps++;
1015
1016     return(0);
1017 }
1018
1019
1020 /* ---------------------------------------------
1021  * general interface routines - alphabetic
1022  * ---------------------------------------------
1023  */
1024
1025 afs_int32 BUDB_AddVolume (call, vol)
1026   struct rx_call *call;
1027   struct budb_volumeEntry *vol;
1028 {
1029   afs_int32 code;
1030
1031   code = AddVolume (call, vol);
1032   osi_auditU (call, BUDB_AddVolEvent, code, AUD_LONG, (vol ? vol->id : 0), AUD_END);
1033   return code;
1034 }
1035
1036 afs_int32 AddVolume (call, vol)
1037   struct rx_call *call;
1038   struct budb_volumeEntry *vol;
1039 {   
1040     struct ubik_trans  *ut;
1041     dbadr              da, ta, via, va;
1042     struct dump        d;
1043     struct tape        t;
1044     struct volInfo     vi;
1045     struct volFragment v;
1046     afs_uint32            bytes;
1047     afs_int32              eval, code = 0;
1048
1049     if ( !callPermitted(call) )
1050         return BUDB_NOTPERMITTED;
1051
1052     if ( ( strlen(vol->name)   >= sizeof(vi.name)   ) ||
1053          ( strlen(vol->server) >= sizeof(vi.server) ) ||
1054          ( strlen(vol->tape)  >= sizeof(t.name)    ) )
1055         return BUDB_BADARGUMENT;
1056
1057     eval = InitRPC (&ut, LOCKWRITE, 1);
1058     if (eval) return eval;
1059
1060     /* Find the dump in dumpid hash table */
1061     eval = ht_LookupEntry (ut, &db.dumpIden, &vol->dump, &da, &d);
1062     if (eval) ABORT(eval);
1063     if (!da)  ABORT(BUDB_NODUMPID);
1064
1065     /* search for the right tape in the dump */
1066     for (ta=ntohl(d.firstTape); ta; ta=ntohl(t.nextTape))
1067     {
1068         /* read the tape entry */
1069         eval = dbread(ut, ta, &t, sizeof(t));
1070         if (eval) ABORT(eval);
1071
1072         /* Check if the right tape name */
1073         if ( strcmp(t.name, vol->tape) == 0 )
1074             break;
1075     }
1076     if (!ta) ABORT(BUDB_NOTAPENAME);
1077
1078     if ( (t.dump != htonl(da))                            || /* tape must belong to dump   */
1079          ((ntohl(t.flags) & BUDB_TAPE_BEINGWRITTEN) == 0) || /* tape must be being written */
1080          ((ntohl(d.flags) & BUDB_DUMP_INPROGRESS)   == 0) )  /* dump must be in progress   */
1081         ABORT(BUDB_BADPROTOCOL);
1082
1083     /* find or create a volume info structure */
1084     eval = GetVolInfo(ut, vol, &via, &vi);
1085     if (eval) ABORT(eval);
1086
1087     /* Create a volume fragment */
1088     eval = AllocStructure (ut, volFragment_BLOCK, 0, &va, &v);
1089     if (eval) ABORT(eval);
1090
1091     v.vol            = htonl(via);               /* vol frag points to vol info */
1092     v.sameNameChain  = vi.firstFragment;         /* vol frag is chained to vol info */
1093     vi.firstFragment = htonl(va);
1094     vi.nFrags        = htonl(ntohl(vi.nFrags)+1);
1095
1096     eval = dbwrite(ut, via, &vi, sizeof(vi));    /* write the vol info struct */
1097     if (eval) ABORT(eval);
1098
1099     v.tape          = htonl(ta);                 /* vol frag points to tape */
1100     v.sameTapeChain = t.firstVol;                /* vol frag is chained to tape info */
1101     t.firstVol      = htonl(va);
1102     t.nVolumes      = htonl(ntohl(t.nVolumes) + 1);
1103     bytes           = ntohl(t.nBytes) + vol->nBytes;     /* update bytes on tape */
1104     t.nMBytes       = htonl(ntohl(t.nMBytes) + bytes/(1024*1024));
1105     t.nBytes        = htonl(bytes % (1024*1024));
1106
1107     eval = dbwrite(ut, ta, &t, sizeof(t));       /* write the tape structure */
1108     if (eval) ABORT(eval);
1109
1110     d.nVolumes = htonl(ntohl(d.nVolumes) + 1);   /* one more volume on dump */
1111
1112     eval = dbwrite(ut, da, &d, sizeof(d));       /* write out the dump structure */
1113     if (eval) ABORT(eval);
1114
1115     v.position  = htonl(vol->position);          /* vol frag info */
1116     v.clone     = htonl(vol->clone);
1117     v.incTime   = htonl(vol->incTime);
1118     v.startByte = htonl(vol->startByte);
1119     v.nBytes    = htonl(vol->nBytes);
1120     v.flags     = htons(vol->flags & VOLFRAGMENTFLAGS);
1121     v.sequence  = htons(vol->seq);
1122
1123     eval = dbwrite(ut, va, &v, sizeof(v));       /* write out the vol frag struct */
1124     if (eval) ABORT(eval);
1125
1126     eval = set_header_word(ut, lastUpdate, htonl(time(0)));
1127     if (eval) ABORT(eval);
1128
1129     LogDebug(4, "added volume %s at %d\n", vol->name, va);
1130
1131     code = ubik_EndTrans(ut);
1132     return code;
1133
1134   abort_exit:
1135     ubik_AbortTrans(ut);
1136     return code;
1137 }
1138
1139
1140 afs_int32 BUDB_AddVolumes (call, vols)
1141   struct rx_call *call;
1142   struct budb_volumeList *vols;
1143 {
1144   afs_int32 code;
1145
1146   code = AddVolumes (call, vols);
1147   osi_auditU (call, BUDB_AddVolEvent, code, AUD_LONG, 0, AUD_END);
1148   return code;
1149 }
1150
1151 afs_int32 AddVolumes (call, vols)
1152   struct rx_call *call;
1153   struct budb_volumeList *vols;
1154 {   
1155     struct budb_volumeEntry *vol, *vol1;
1156     struct ubik_trans  *ut;
1157     dbadr              da, ta, via, va;
1158     struct dump        d;
1159     struct tape        t;
1160     struct volInfo     vi;
1161     struct volFragment v;
1162     afs_uint32            bytes;
1163     afs_int32              eval, e, code = 0;
1164
1165     if ( !callPermitted(call) )
1166        return BUDB_NOTPERMITTED;
1167
1168     if (!vols || (vols->budb_volumeList_len <= 0) || !vols->budb_volumeList_val)
1169        return BUDB_BADARGUMENT;
1170     
1171     /* The first volume in the list of volumes to add */
1172     vol1 = (struct budb_volumeEntry *)vols->budb_volumeList_val;
1173
1174     eval = InitRPC (&ut, LOCKWRITE, 1);
1175     if (eval) return eval;
1176
1177     /* Find the dump in dumpid hash table */
1178     eval = ht_LookupEntry (ut, &db.dumpIden, &vol1->dump, &da, &d);
1179     if (eval) ABORT(eval);
1180     if (!da)  ABORT(BUDB_NODUMPID);
1181
1182     /* search for the right tape in the dump */
1183     for (ta=ntohl(d.firstTape); ta; ta=ntohl(t.nextTape)) {
1184        /* read the tape entry */
1185        eval = dbread(ut, ta, &t, sizeof(t));
1186        if (eval) ABORT(eval);
1187
1188        /* Check if the right tape name */
1189        if ( strcmp(t.name, vol1->tape) == 0 )
1190           break;
1191     }
1192     if (!ta) ABORT(BUDB_NOTAPENAME);
1193
1194     if ( (t.dump != htonl(da))                            || /* tape must belong to dump   */
1195          ((ntohl(t.flags) & BUDB_TAPE_BEINGWRITTEN) == 0) || /* tape must be being written */
1196          ((ntohl(d.flags) & BUDB_DUMP_INPROGRESS)   == 0) )  /* dump must be in progress   */
1197        ABORT(BUDB_BADPROTOCOL);
1198
1199     for (vol=vol1,  e=0; e < vols->budb_volumeList_len; vol++, e++) {
1200        /*v*/
1201        if ( ( strlen(vol->name)   >= sizeof(vi.name)   ) ||
1202             ( strcmp(vol->name, "") == 0               ) ||   /* no null volnames */
1203             ( strlen(vol->server) >= sizeof(vi.server) ) ||
1204             ( strlen(vol->tape)   >= sizeof(t.name)    ) ||
1205             ( strcmp(vol->tape, vol1->tape) != 0       ) ) {
1206           Log("Volume '%s' %u, tape '%s', dumpID %u is an invalid entry - not added\n",
1207               vol->name, vol->id, vol->tape, vol->dump);
1208           continue;
1209        }
1210
1211        /* find or create a volume info structure */
1212        eval = GetVolInfo(ut, vol, &via, &vi);
1213        if (eval) ABORT(eval);
1214        if (*(afs_int32 *)(&vi) == 0) {
1215           Log("Volume '%s', tape '%s', dumpID %u is an invalid entry - aborted\n",
1216               vol->name, vol->tape, vol->dump);
1217           ABORT(BUDB_BADARGUMENT);
1218        }
1219
1220        /* Create a volume fragment */
1221        eval = AllocStructure (ut, volFragment_BLOCK, 0, &va, &v);
1222        if (eval) ABORT(eval);
1223
1224        v.vol            = htonl(via);               /* vol frag points to vol info */
1225        v.sameNameChain  = vi.firstFragment;         /* vol frag is chained to vol info */
1226        vi.firstFragment = htonl(va);
1227        vi.nFrags        = htonl(ntohl(vi.nFrags)+1);
1228        eval = dbwrite(ut, via, &vi, sizeof(vi));    /* write the vol info struct */
1229        if (eval) ABORT(eval);
1230
1231        v.tape          = htonl(ta);                 /* vol frag points to tape */
1232        v.sameTapeChain = t.firstVol;                /* vol frag is chained to tape info */
1233        t.firstVol      = htonl(va);
1234        t.nVolumes      = htonl(ntohl(t.nVolumes) + 1);
1235        bytes           = ntohl(t.nBytes) + vol->nBytes;     /* update bytes on tape */
1236        t.nMBytes       = htonl(ntohl(t.nMBytes) + bytes/(1024*1024));
1237        t.nBytes        = htonl(bytes % (1024*1024));
1238
1239        d.nVolumes = htonl(ntohl(d.nVolumes) + 1);   /* one more volume on dump */
1240
1241        v.position  = htonl(vol->position);          /* vol frag info */
1242        v.clone     = htonl(vol->clone);
1243        v.incTime   = htonl(vol->incTime);
1244        v.startByte = htonl(vol->startByte);
1245        v.nBytes    = htonl(vol->nBytes);
1246        v.flags     = htons(vol->flags & VOLFRAGMENTFLAGS);
1247        v.sequence  = htons(vol->seq);
1248
1249        eval = dbwrite(ut, va, &v, sizeof(v));       /* write out the vol frag struct */
1250        if (eval) ABORT(eval);
1251        
1252        LogDebug(4, "added volume %s at %d\n", vol->name, va);
1253     } /*v*/
1254
1255     eval = dbwrite(ut, ta, &t, sizeof(t));       /* write the tape structure */
1256     if (eval) ABORT(eval);
1257
1258     eval = dbwrite(ut, da, &d, sizeof(d));       /* write out the dump structure */
1259     if (eval) ABORT(eval);
1260
1261     eval = set_header_word(ut, lastUpdate, htonl(time(0)));
1262     if (eval) ABORT(eval);
1263
1264     code = ubik_EndTrans(ut);
1265     return code;
1266
1267   abort_exit:
1268     ubik_AbortTrans(ut);
1269     return code;
1270 }
1271
1272
1273 /* BUDB_CreateDump
1274  *      records the existence of a dump in the database. This creates only
1275  *      the dump record, to which one must attach tape and volume records.
1276  * TBD
1277  *      1) record the volume set
1278  */
1279
1280 afs_int32 BUDB_CreateDump(call, dump)
1281      struct rx_call *call;
1282      struct budb_dumpEntry *dump;
1283 {
1284   afs_int32 code;
1285
1286   code = CreateDump(call, dump);
1287   osi_auditU (call, BUDB_CrDmpEvent, code, AUD_DATE, (dump ? dump->id : 0), AUD_END);
1288   if (dump && !code) {
1289      Log("Create dump %s (DumpID %u), path %s\n",
1290          dump->name, dump->id, dump->dumpPath);
1291   }
1292   return code;
1293 }
1294
1295 afs_int32 CreateDump(call, dump)
1296      struct rx_call *call;
1297      struct budb_dumpEntry *dump;
1298 {  
1299     struct ubik_trans *ut;
1300     dbadr             findDumpAddr, da;
1301     struct dump       findDump, d;
1302     afs_int32             eval, code = 0;
1303
1304     rxkad_level          level;
1305     afs_int32            kvno;
1306     Date                 expiration;    /* checked by Security Module */
1307     struct ktc_principal principal;
1308
1309     if ( !callPermitted(call) )
1310         return BUDB_NOTPERMITTED;
1311
1312     if (strlen(dump->name) >= sizeof(d.dumpName))
1313         return BUDB_BADARGUMENT;
1314
1315     eval = InitRPC (&ut, LOCKWRITE, 1);
1316     if (eval) return eval;
1317
1318     eval = rxkad_GetServerInfo( rx_ConnectionOf(call),
1319                                 &level, &expiration, 
1320                                 principal.name,
1321                                 principal.instance, 
1322                                 principal.cell,
1323                                 &kvno );
1324
1325     if (eval)
1326     {
1327         if (eval != RXKADNOAUTH) ABORT(eval);
1328
1329         strcpy(principal.name,     "");
1330         strcpy(principal.instance, "");
1331         strcpy(principal.cell,     "");
1332         expiration = 0;
1333     }
1334     else
1335     {
1336         /* authenticated. Take user supplied principal information */
1337         if ( strcmp(dump->dumper.name, "") != 0 )
1338             strncpy(principal.name, dump->dumper.name, sizeof(principal.name));
1339
1340         if ( strcmp(dump->dumper.instance, "") != 0 )
1341             strncpy(principal.instance, dump->dumper.instance, sizeof(principal.instance));
1342
1343         if ( strcmp(dump->dumper.cell, "") != 0 )
1344             strncpy(principal.cell, dump->dumper.cell, sizeof(principal.cell));
1345     }
1346
1347     /* dump id's are time stamps */
1348     if (!dump->id)
1349     {
1350         while (1)        /* allocate a unique dump id */
1351         { /*w*/
1352             dump->id = time(0);
1353
1354             /* ensure it is unique - seach for dumpid in hash table */
1355             eval = ht_LookupEntry(ut, &db.dumpIden, &dump->id, &findDumpAddr, &findDump);
1356             if (eval) ABORT(eval);
1357
1358             if (!findDumpAddr)         /* dumpid not in use */
1359             {
1360                 /* update the last dump id allocated */
1361                 eval = set_header_word(ut, lastDumpId, htonl(dump->id));
1362                 if (eval) ABORT(eval);
1363                 break;
1364             }
1365
1366             /* dump id is in use - wait a while */
1367             IOMGR_Sleep(1);
1368         } /*w*/
1369     }
1370     else
1371     {
1372         /* dump id supplied (e.g. for database restore) */
1373         eval = ht_LookupEntry(ut, &db.dumpIden, &dump->id, &findDumpAddr, &findDump);
1374         if (eval) ABORT(eval);
1375         
1376         /* Dump id must not already exist */
1377         if (findDumpAddr) ABORT(BUDB_DUMPIDEXISTS);
1378     }
1379             
1380     /* Allocate a dump structure */
1381     bzero (&d, sizeof(d));
1382     eval = AllocStructure (ut, dump_BLOCK, 0, &da, &d);
1383     if (eval) ABORT(eval);
1384
1385     strcpy(d.dumpName,  dump->name);            /* volset.dumpname */
1386     strcpy(d.dumpPath,  dump->dumpPath);        /* dump node path */
1387     strcpy(d.volumeSet, dump->volumeSetName);   /* volume set */
1388     d.id     = htonl(dump->id);
1389     d.parent = htonl(dump->parent);             /* parent id */
1390     d.level  = htonl(dump->level);
1391
1392     LogDebug(4, "dump name %s, parent %d level %d\n", dump->name, dump->parent, dump->level);
1393
1394     /* if creation time specified, use that.  Else use the dumpid time */
1395     if (dump->created == 0) dump->created = dump->id;
1396     d.created = htonl(dump->created);
1397
1398     principal_hton(&principal, &d.dumper);
1399     tapeSet_hton(&dump->tapes, &d.tapes);
1400
1401     d.flags = htonl(dump->flags | BUDB_DUMP_INPROGRESS);
1402
1403     eval = ht_HashIn (ut, &db.dumpName, da, &d);     /* Into dump name hash table */
1404     if (eval) ABORT(eval);
1405
1406     eval = ht_HashIn (ut, &db.dumpIden, da, &d);     /* Into dumpid hash table */
1407     if (eval) ABORT(eval);
1408
1409     eval = dbwrite (ut, da, (char *)&d, sizeof(d));  /* Write the dump structure */
1410     if (eval) ABORT(eval);
1411
1412     eval = set_header_word (ut, lastUpdate, htonl(time(0)));
1413     if (eval) ABORT(eval);
1414
1415     /* If to append this dump, then append it - will write the appended dump */
1416     eval = makeAppended(ut, dump->id, dump->initialDumpID, dump->tapes.b);
1417     if (eval) ABORT(eval);
1418
1419     code = ubik_EndTrans(ut);
1420     LogDebug(5, "made dump %s, path %s\n", d.dumpName, d.dumpPath);
1421     return code;
1422
1423   abort_exit:
1424     ubik_AbortTrans(ut);
1425     return code;
1426 }
1427
1428 afs_int32 BUDB_DeleteDump (call, id, fromTime, toTime, dumps)
1429      struct rx_call *call;
1430      dumpId id;
1431      Date   fromTime;
1432      Date   toTime;
1433      budb_dumpsList *dumps;
1434 {
1435   afs_int32 code;
1436
1437   code = DoDeleteDump (call, id, fromTime, toTime, dumps);
1438   osi_auditU (call, BUDB_DelDmpEvent, code, AUD_DATE, id, AUD_END);
1439   return code;
1440 }
1441
1442 #define MAXOFFS 30
1443
1444 afs_int32 DoDeleteDump (call, id, fromTime, toTime, dumps)
1445     struct rx_call *call;
1446     dumpId id;
1447     Date   fromTime;
1448     Date   toTime;
1449     budb_dumpsList *dumps;
1450 {
1451     afs_int32                   code = 0;
1452
1453     if ( !callPermitted(call) )
1454         return BUDB_NOTPERMITTED;
1455
1456     if (id) code = deleteDump(call, id, dumps);
1457     return(code);
1458 }
1459
1460 afs_int32 BUDB_ListDumps (call, sflags, name, groupid, fromTime, toTime, dumps, flags)
1461    struct rx_call *call;
1462    afs_int32 sflags, groupid;
1463    char *name;
1464    Date   fromTime, toTime;
1465    budb_dumpsList *dumps, *flags;
1466 {
1467   afs_int32 code;
1468
1469   code = ListDumps(call, sflags, groupid, fromTime, toTime, dumps, flags);
1470   osi_auditU (call, BUDB_LstDmpEvent, code, AUD_LONG, flags, AUD_END);
1471   return code;
1472 }
1473
1474 afs_int32 ListDumps (call, sflags, groupid, fromTime, toTime, dumps, flags)
1475    struct rx_call *call;
1476    afs_int32 sflags, groupid;
1477    Date   fromTime, toTime;
1478    budb_dumpsList *dumps, *flags;
1479 {
1480     struct ubik_trans      *ut;
1481     struct memoryHashTable *mht;
1482     struct dump            diskDump, appDiskDump;
1483     dbadr                  dbAddr, dbAppAddr;
1484
1485     afs_int32              eval, code = 0;
1486     int                    old, hash, length, entrySize, j, k, count=0;
1487     afs_uint32             toList, toFlag;
1488
1489     if ( !callPermitted(call) )
1490         return BUDB_NOTPERMITTED;
1491
1492     eval= InitRPC (&ut, LOCKREAD, 1);
1493     if (eval) return(eval);
1494
1495     /* Search the database */
1496     mht = ht_GetType(HT_dumpIden_FUNCTION, &entrySize);
1497     if (!mht) return(BUDB_BADARGUMENT);
1498
1499     for (old=0; old<=1; old++) { /*o*/                /* old and new hash tables */
1500        length = (old ? mht->oldLength : mht->length);
1501        if (length == 0) continue;
1502
1503        for (hash=0; hash<length; hash++) { /*h*/            /* for each hash bucket */
1504           for (dbAddr = ht_LookupBucket(ut,mht,hash,old); dbAddr;
1505                dbAddr = ntohl(diskDump.idHashChain)) { /*d*/
1506
1507              /* read the entry */
1508              eval = dbread (ut, dbAddr, &diskDump, sizeof(diskDump));
1509              if (eval) ABORT(eval);
1510                     
1511              /* Skip appended dumps */
1512              if (ntohl(diskDump.initialDumpID) != 0) {
1513                 continue;
1514              }
1515
1516              /* Skip dumps with different goup id */
1517              if ((sflags & BUDB_OP_GROUPID) && (ntohl(diskDump.tapes.id) != groupid)) {
1518                 continue; /*nope*/
1519              }
1520
1521              /* Look at this dump to see if it meets the criteria for listing */
1522              if (sflags & BUDB_OP_DATES) {
1523                 /* This and each appended dump should be in time */
1524                 for (dbAppAddr=dbAddr; dbAppAddr; dbAppAddr=ntohl(appDiskDump.appendedDumpChain)) {
1525                    eval = dbread (ut, dbAppAddr, &appDiskDump, sizeof(appDiskDump));
1526                    if (eval) ABORT(eval);
1527
1528                    if ((ntohl(appDiskDump.id) < fromTime) || (ntohl(appDiskDump.id) > toTime))
1529                       break; /*nope*/
1530                 }
1531                 if (dbAppAddr) continue; /*nope*/
1532              }
1533
1534              /* Add it and each of its appended dump to our list to return */
1535              for (dbAppAddr=dbAddr; dbAppAddr; dbAppAddr=ntohl(appDiskDump.appendedDumpChain)) {
1536                 eval = dbread (ut, dbAppAddr, &appDiskDump, sizeof(appDiskDump));
1537                 if (eval) ABORT(eval);
1538
1539                 /* Make sure we have space to list it */
1540                 if (dumps->budb_dumpsList_len >= count) {
1541                    count += 10;
1542                    if (count == 10) {
1543                       dumps->budb_dumpsList_val = (afs_int32 *)malloc(count * sizeof(afs_int32));
1544                       flags->budb_dumpsList_val = (afs_int32 *)malloc(count * sizeof(afs_int32));
1545                    } else {
1546                       dumps->budb_dumpsList_val = 
1547                         (afs_int32 *)realloc(dumps->budb_dumpsList_val, count*sizeof(afs_int32));
1548                       flags->budb_dumpsList_val = 
1549                         (afs_int32 *)realloc(flags->budb_dumpsList_val, count*sizeof(afs_int32));
1550                    }
1551                    if (!dumps->budb_dumpsList_val || !dumps->budb_dumpsList_val)
1552                      ABORT(BUDB_NOMEM);
1553                 }
1554
1555                 /* Add it to our list */
1556                 dumps->budb_dumpsList_val[dumps->budb_dumpsList_len] = ntohl(appDiskDump.id);
1557                 flags->budb_dumpsList_val[flags->budb_dumpsList_len] = 0;
1558                 if ( ntohl(appDiskDump.initialDumpID) != 0 ) {
1559                    flags->budb_dumpsList_val[flags->budb_dumpsList_len] |= BUDB_OP_APPDUMP;
1560                 }
1561                 if (strcmp(appDiskDump.dumpName,DUMP_TAPE_NAME) == 0) {
1562                    flags->budb_dumpsList_val[flags->budb_dumpsList_len] |= BUDB_OP_DBDUMP;
1563                 }
1564                 dumps->budb_dumpsList_len++;
1565                 flags->budb_dumpsList_len++;
1566              }
1567           } /*d*/
1568        } /*h*/
1569     } /*o*/
1570         
1571     code = ubik_EndTrans(ut);
1572     return(code);
1573
1574   abort_exit:
1575     ubik_AbortTrans(ut);
1576     return(code);
1577 }
1578
1579 afs_int32 BUDB_DeleteTape (call, tape)
1580   struct rx_call *call;
1581   struct budb_tapeEntry *tape;          /* tape info */
1582 {
1583   afs_int32 code;
1584
1585   code = DoDeleteTape (call, tape);
1586   osi_auditU (call, BUDB_DelTpeEvent, code, AUD_DATE, (tape ? tape->dump : 0), AUD_END);
1587   return code;
1588 }
1589
1590 afs_int32 DoDeleteTape (call, tape)
1591   struct rx_call *call;
1592   struct budb_tapeEntry *tape;          /* tape info */
1593 {
1594     struct ubik_trans *ut;
1595     struct tape       t;
1596     dbadr             a;
1597     afs_int32             eval, code;
1598
1599     if ( !callPermitted(call) )
1600         return BUDB_NOTPERMITTED;
1601
1602     eval = InitRPC (&ut, LOCKWRITE, 1);
1603     if (eval) return eval;
1604
1605     eval = ht_LookupEntry (ut, &db.tapeName, tape->name, &a, &t);
1606     if (eval) ABORT(eval);
1607
1608     eval = DeleteTape (ut, a, &t);
1609     if (eval) ABORT(eval);
1610
1611     eval = FreeStructure (ut, tape_BLOCK, a);
1612     if (eval) ABORT(eval);
1613
1614     eval = set_header_word (ut, lastUpdate, htonl(time(0)));
1615     if (eval) ABORT(eval);
1616
1617     code = ubik_EndTrans(ut);
1618     return code;
1619
1620   abort_exit:
1621     ubik_AbortTrans(ut);
1622     return code;
1623 }
1624
1625 /* BUDB_DeleteVDP
1626  *      Deletes old information from the database for a particular dump path
1627  *      and volumset. This supercedes the old policy implemented in
1628  *      UseTape, which simply matched on the volumeset.dump. Consequently
1629  *      it was unable to handle name re-use.
1630  * entry:
1631  *      dsname - dumpset name, i.e. volumeset.dumpname
1632  *      dumpPath - full path of dump node
1633  *      curDumpID - current dump in progress - so that is may be excluded
1634  * exit:
1635  *      0 - ok
1636  *      n - some error. May or may not have deleted information.
1637  */
1638
1639 afs_int32 BUDB_DeleteVDP (call, dsname, dumpPath, curDumpId)
1640      struct rx_call *call;
1641      char *dsname;
1642      char *dumpPath;
1643      afs_int32 curDumpId;
1644 {
1645   afs_int32 code;
1646
1647   code = DeleteVDP (call, dsname, dumpPath, curDumpId);
1648   osi_auditU (call, BUDB_DelVDPEvent, code, AUD_STR, dsname, AUD_END);
1649   return code;
1650 }
1651
1652 afs_int32 DeleteVDP (call, dsname, dumpPath, curDumpId)
1653      struct rx_call *call;
1654      char *dsname;
1655      char *dumpPath;
1656      afs_int32 curDumpId;
1657 {
1658     struct dump dump;
1659     dbadr dumpAddr;
1660     
1661     struct ubik_trans *ut;
1662     afs_int32             eval, code = 0;
1663
1664     if ( !callPermitted(call) )
1665         return BUDB_NOTPERMITTED;
1666
1667     while (1)
1668     {
1669         eval = InitRPC (&ut, LOCKREAD, 1);
1670         if (eval) return(eval);
1671
1672         eval = ht_LookupEntry(ut, &db.dumpName, dsname, &dumpAddr, &dump);
1673         if (eval) ABORT(eval);
1674
1675         while ( dumpAddr != 0 )
1676         { /*wd*/
1677             if ( (strcmp(dump.dumpName, dsname)   == 0) &&
1678                  (strcmp(dump.dumpPath, dumpPath) == 0) &&
1679                  (ntohl(dump.id) != curDumpId) )
1680             {
1681                 eval = ubik_EndTrans(ut);
1682                 if (eval) return(eval);
1683
1684                 eval = deleteDump(call, ntohl(dump.id), 0);
1685                 if (eval) return(eval);
1686
1687                 /* start the traversal over since the various chains may
1688                  * have changed
1689                  */
1690                 break;
1691             }
1692
1693             dumpAddr = ntohl(dump.nameHashChain);
1694             if (dumpAddr)
1695             {
1696                 eval = dbread(ut, dumpAddr, &dump, sizeof(dump));
1697                 if (eval) ABORT(eval);
1698             }
1699         } /*wd*/
1700
1701         /* check if all the dumps have been examined - can terminate */
1702         if (!dumpAddr)
1703         {
1704             eval = ubik_EndTrans(ut);
1705             return(eval);
1706         }
1707     }
1708
1709   abort_exit:
1710     ubik_AbortTrans(ut);
1711     return(code);
1712 }
1713
1714 /* BUDB_FindClone
1715  * notes:
1716  *      Given a volume name, and a dumpID, find the volume in that dump and 
1717  *      return the clone date of the volume (this is the clone date of the 
1718  *      volume at the time it was dumped).
1719  *   
1720  *      Hashes on the volume name and traverses the fragments. Will need to read 
1721  *      the volumes tape entry to determine if it belongs to the dump. If the
1722  *      volume is not found in the dump, then look for it in its parent dump.
1723  */
1724
1725 afs_int32 BUDB_FindClone(call, dumpID, volName, clonetime)
1726      struct rx_call *call;
1727      afs_int32 dumpID;
1728      char *volName;
1729      afs_int32 *clonetime;
1730 {
1731   afs_int32 code;
1732
1733   code = FindClone (call, dumpID, volName, clonetime);
1734   osi_auditU (call, BUDB_FndClnEvent, code, AUD_STR, volName, AUD_END);
1735   return code;
1736 }
1737
1738 afs_int32 FindClone (call, dumpID, volName, clonetime)
1739      struct rx_call *call;
1740      afs_int32 dumpID;
1741      char *volName;
1742      afs_int32 *clonetime;
1743 {
1744     struct ubik_trans  *ut;
1745     dbadr              da, ta, hvia, via, vfa;
1746     struct dump        d;
1747     struct tape        t;
1748     struct volFragment vf;
1749     struct volInfo     vi;
1750     int                rvi;              /* read the volInfo struct */
1751     afs_int32              eval, code = 0;
1752
1753     if ( !callPermitted(call) )
1754         return BUDB_NOTPERMITTED;
1755
1756     eval = InitRPC (&ut, LOCKREAD, 1);
1757     if (eval) return(eval);
1758
1759     *clonetime = 0;
1760
1761     /* Search for the volume by name */
1762     eval = ht_LookupEntry (ut, &db.volName, volName, &hvia, &vi);
1763     if (eval) ABORT(eval);
1764     if (!hvia) ABORT(BUDB_NOVOLUMENAME);
1765     rvi = 0;
1766
1767     /* Follw the dump levels up */
1768     for (; dumpID; dumpID = ntohl(d.parent))
1769     { /*d*/
1770         /* Get the dump entry */
1771         eval = ht_LookupEntry (ut, &db.dumpIden, &dumpID, &da, &d);
1772         if (eval) ABORT(eval);
1773         if (!da) ABORT(BUDB_NODUMPID);
1774         
1775         /* seach all the volInfo entries on the sameNameChain */
1776         for (via=hvia; via; via=ntohl(vi.sameNameChain))
1777         { /*via*/
1778             if (rvi)           /* Read the volInfo entry - except first time */
1779             {
1780                 eval = dbread(ut, via, &vi, sizeof(vi));    
1781                 if (eval) ABORT(eval);
1782             }
1783             rvi = 1;
1784
1785             /* search all the volFrag entries on the volFrag */
1786             for (vfa=ntohl(vi.firstFragment); vfa; vfa=ntohl(vf.sameNameChain))
1787             { /*vfa*/
1788                 eval = dbread(ut, vfa, &vf, sizeof(vf));    /* Read the volFrag entry */
1789                 if (eval) ABORT(eval);
1790
1791                 eval = dbread(ut, ntohl(vf.tape), &t, sizeof(t));   /* Read the tape */
1792                 if (eval) ABORT(eval);
1793
1794                 /* Now check to see if this fragment belongs to the dump we have */
1795                 if (ntohl(t.dump) == da) 
1796                 {
1797                     *clonetime = ntohl(vf.clone); /* return the clone */
1798                     ERROR(0);
1799                 }
1800             } /*vfa*/
1801         } /*via*/
1802     } /*d*/
1803
1804   error_exit:
1805     code = ubik_EndTrans(ut);
1806     return(code);
1807
1808   abort_exit:
1809     ubik_EndTrans(ut);
1810     return(code);
1811 }
1812
1813 #ifdef notdef
1814 /*
1815  *      Searches each tape and each volume in the dump until the volume is found.
1816  *      If the volume is not in the dump, then we search it's parent dump.
1817  *
1818  *      Re-write to do lookups by volume name.
1819  */
1820 afs_int32 FindClone (call, dumpID, volName, clonetime)
1821      struct rx_call *call;
1822      afs_int32 dumpID;
1823      char *volName;
1824      afs_int32 *clonetime;
1825 {
1826     struct ubik_trans  *ut;
1827     dbadr              diskAddr, tapeAddr, volFragmentAddr;
1828     struct dump        dump;
1829     struct tape        tape;
1830     struct volFragment volFragment;
1831     struct volInfo     volInfo;
1832     afs_int32              eval, code = 0;
1833
1834     if ( !callPermitted(call) )
1835         return BUDB_NOTPERMITTED;
1836
1837     eval = InitRPC (&ut, LOCKREAD, 1);
1838     if (eval) return(eval);
1839
1840     *clonetime = 0;
1841
1842     for (; dumpID; dumpID = ntohl(dump.parent))
1843     { /*d*/
1844         /* Get the dump entry */
1845         eval = ht_LookupEntry (ut, &db.dumpIden, &dumpID, &diskAddr, &dump);
1846         if (eval) ABORT(eval);
1847         if (!diskAddr) ABORT(BUDB_NODUMPID);
1848         
1849         /* just to be sure */
1850         if (ntohl(dump.id) != dumpID)
1851         {
1852             LogDebug(4, "BUDB_FindClone: requested %d, found %d\n", dumpID, ntohl(dump.id));
1853             ABORT(BUDB_INTERNALERROR);
1854         }
1855         
1856         /* search all the tapes in this dump */
1857         for (tapeAddr=ntohl(dump.firstTape); tapeAddr; tapeAddr=ntohl(tape.nextTape))
1858         { /*t*/
1859             /* Get the tape entry */
1860             eval = dbread(ut, tapeAddr, &tape, sizeof(tape));
1861             if (eval) ABORT(eval);
1862             
1863             /* search all the volume fragments on this tape */
1864             for (volFragmentAddr=ntohl(tape.firstVol);  volFragmentAddr; 
1865                  volFragmentAddr=ntohl(volFragment.sameTapeChain))
1866             { /*vf*/
1867                 /* Get the volume fragment entry */
1868                 eval = dbread(ut, volFragmentAddr, &volFragment, sizeof(volFragment));
1869                 if (eval) ABORT(eval);
1870
1871                 /* Get the volume info entry */
1872                 eval = dbread(ut, ntohl(volFragment.vol), &volInfo, sizeof(volInfo));
1873                 if (eval) ABORT(eval);
1874
1875                 /* check if this volume is the one we want */
1876                 if ( strcmp(volInfo.name,volName) == 0 )
1877                 {
1878                     *clonetime = ntohl(volFragment.clone);
1879                     ERROR(0);
1880                 }
1881             } /*vf*/
1882         } /*t*/
1883     } /*d*/
1884
1885   error_exit:
1886     code = ubik_EndTrans(ut);
1887     return(code);
1888
1889   abort_exit:
1890     ubik_EndTrans(ut);
1891     return(code);
1892 }
1893 #endif
1894
1895 /* BUDB_FindDump
1896  *      Find latest volume dump before adate.
1897  *      Used by restore code when restoring a user requested volume(s)
1898  * entry:
1899  *      volumeName - name of volume to match on
1900  *      beforeDate - look for dumps older than this date
1901  * exit:
1902  *      deptr - descriptor of most recent dump
1903  */
1904
1905 afs_int32 BUDB_FindDump (call, volumeName, beforeDate, deptr)
1906      struct rx_call *call;
1907      char *volumeName;
1908      afs_int32 beforeDate;
1909      struct budb_dumpEntry *deptr;
1910 {
1911   afs_int32 code;
1912
1913   code = FindDump (call, volumeName, beforeDate, deptr);
1914   osi_auditU (call, BUDB_FndDmpEvent, code, AUD_STR, volumeName, AUD_END);
1915   return code;
1916 }
1917
1918 afs_int32 FindDump (call, volumeName, beforeDate, deptr)
1919      struct rx_call *call;
1920      char *volumeName;
1921      afs_int32 beforeDate;
1922      struct budb_dumpEntry *deptr;
1923 {
1924     struct ubik_trans  *ut;
1925     dbadr              volInfoAddr, volFragmentAddr;
1926     struct tape        tape;
1927     struct volInfo     volInfo;
1928     struct volFragment volFragment;
1929
1930     dbadr selectedDumpAddr = 0;
1931     afs_int32 selectedDate     = 0;
1932     afs_int32 volCloned;
1933     int   rvoli;
1934     afs_int32 eval, code = 0;
1935
1936     if ( !callPermitted(call) )
1937         return BUDB_NOTPERMITTED;
1938
1939     eval = InitRPC (&ut, LOCKREAD, 1);
1940     if (eval) return eval;
1941
1942     /* Find volinfo struct for volume name in hash table */
1943     eval = ht_LookupEntry (ut, &db.volName, volumeName, &volInfoAddr, &volInfo);
1944     if (eval) ABORT(eval);
1945     if (!volInfoAddr) ABORT(BUDB_NOVOLUMENAME);
1946
1947     /* Step through all the volinfo structures on the same name chain.
1948      * No need to read the first - we read it above.
1949      */
1950     for (rvoli=0; volInfoAddr; rvoli=1, volInfoAddr=ntohl(volInfo.sameNameChain))
1951     {
1952         if (rvoli)    /* read the volinfo structure */
1953         {
1954             eval = dbread(ut, volInfoAddr, &volInfo, sizeof(volInfo));
1955             if (eval) ABORT(eval);
1956         }
1957       
1958         /* step through the volfrag structures */
1959         for (volFragmentAddr=ntohl(volInfo.firstFragment); volFragmentAddr;
1960              volFragmentAddr=ntohl(volFragment.sameNameChain))
1961         {
1962             /* read the volfrag struct */
1963             eval = dbread(ut, volFragmentAddr, &volFragment, sizeof(volFragment));
1964             if (eval) ABORT(eval);
1965
1966             volCloned = ntohl(volFragment.clone);
1967
1968             /* now we can examine the date for most recent dump */
1969             if ( (volCloned > selectedDate) && (volCloned < beforeDate) )
1970             {
1971                 /* from the volfrag struct, read the tape struct */
1972                 eval = dbread(ut, ntohl(volFragment.tape), &tape, sizeof(tape));
1973                 if (eval) ABORT(eval);
1974
1975                 selectedDate     = volCloned;
1976                 selectedDumpAddr = ntohl(tape.dump);
1977             }
1978         }
1979     }
1980
1981     if (!selectedDumpAddr) ABORT(BUDB_NOENT);
1982
1983     eval = FillDumpEntry(ut, selectedDumpAddr, deptr);
1984     if (eval) ABORT(eval);
1985
1986     code = ubik_EndTrans(ut);
1987     return(code);
1988
1989   abort_exit:
1990     ubik_EndTrans(ut);
1991     return(code);
1992 }
1993
1994 /* BUDB_FindLatestDump
1995  *      Find the latest dump of volumeset vsname with dump name dname.
1996  * entry:
1997  *      vsname - volumeset name
1998  *      dname - dumpname
1999  */
2000
2001 afs_int32 BUDB_FindLatestDump (call, vsname, dumpPath, dumpentry)
2002      struct rx_call *call;
2003      char       *vsname, *dumpPath;
2004      struct     budb_dumpEntry *dumpentry;
2005 {
2006   afs_int32 code;
2007
2008   code = FindLatestDump (call, vsname, dumpPath, dumpentry);
2009   osi_auditU (call, BUDB_FndLaDEvent, code, AUD_STR, vsname, AUD_END);
2010   return code;
2011 }
2012
2013 afs_int32 FindLatestDump (call, vsname, dumpPath, dumpentry)
2014      struct rx_call *call;
2015      char       *vsname, *dumpPath;
2016      struct     budb_dumpEntry *dumpentry;
2017 {
2018     struct ubik_trans *ut;
2019     dbadr             curdbaddr, retdbaddr, firstdbaddr;
2020     struct dump       d;
2021     Date              latest;
2022     char              dumpName[BU_MAXNAMELEN+2];
2023     afs_int32             eval, code = 0;
2024
2025     if ( !callPermitted(call) )
2026         return BUDB_NOTPERMITTED;
2027
2028     eval = InitRPC(&ut, LOCKREAD, 1);
2029     if (eval) return(eval);
2030
2031     if ( (strcmp(vsname,"") == 0) && (strcmp(dumpPath,"") == 0) )
2032     {
2033         /* Construct a database dump name */
2034         strcpy(dumpName, DUMP_TAPE_NAME);
2035     }
2036     else if (index(dumpPath,'/') == 0) {
2037         int                    level, old, length, hash;
2038         struct dump            hostDump, diskDump;
2039         struct memoryHashTable *mht;
2040         int                    entrySize;
2041         dbadr                  dbAddr;
2042         afs_uint32                bestDumpId=0;
2043
2044         level = atoi(dumpPath);
2045         if (level < 0) {
2046            ABORT(BUDB_BADARGUMENT);
2047         }
2048         
2049         /* Brute force search of all the dumps in the database - yuck! */
2050
2051         retdbaddr = 0;
2052         mht = ht_GetType(HT_dumpIden_FUNCTION, &entrySize);
2053         if (!mht) ABORT(BUDB_BADARGUMENT);
2054
2055         for (old=0; old <= 1; old++) { /*fo*/
2056             length = (old ? mht->oldLength : mht->length);
2057             if (!length) continue;
2058
2059             for (hash=0; hash<length; hash++) { 
2060                 /*f*/
2061                 for (dbAddr = ht_LookupBucket(ut,mht,hash,old); dbAddr;
2062                      dbAddr = hostDump.idHashChain) { 
2063                     /*w*/
2064                     eval = dbread (ut, dbAddr, &diskDump, sizeof(diskDump));
2065                     if (eval) ABORT(eval);
2066                     dump_ntoh(&diskDump, &hostDump);
2067
2068                     if ( (strcmp(hostDump.volumeSet,vsname) == 0)  &&    /* the volumeset */
2069                          (hostDump.level == level)                 &&    /* same level */
2070                          (hostDump.id > bestDumpId) )               {    /* more recent */
2071                         bestDumpId = hostDump.id;
2072                         retdbaddr  = dbAddr;
2073                     }
2074                 } /*w*/
2075             } /*f*/
2076         } /*fo*/
2077         if (!retdbaddr)
2078            ABORT(BUDB_NODUMPNAME);
2079
2080         goto finished;
2081     }
2082     else
2083     {
2084         /* construct the name of the dump */
2085         if ( (strlen(vsname) + strlen(tailCompPtr(dumpPath))) > BU_MAXNAMELEN )
2086             ABORT(BUDB_NODUMPNAME);
2087
2088         strcpy(dumpName, vsname);
2089         strcat(dumpName, ".");
2090         strcat(dumpName, tailCompPtr(dumpPath));
2091     }
2092     
2093     LogDebug(5, "lookup on :%s:\n", dumpName);
2094     
2095     /* Lookup on dumpname in hash table */
2096     eval = ht_LookupEntry(ut, &db.dumpName, dumpName, &firstdbaddr, &d);
2097     if (eval) ABORT(eval);
2098     
2099     latest    = 0;
2100     retdbaddr = 0;
2101
2102     /* folow remaining dumps in hash chain, looking for most latest dump */
2103     for (curdbaddr=firstdbaddr; curdbaddr; curdbaddr=ntohl(d.nameHashChain))
2104     {
2105         if (curdbaddr != firstdbaddr) {
2106            eval = dbread(ut, curdbaddr, &d, sizeof(d));
2107            if (eval) ABORT(eval);
2108         }
2109
2110         if ( (strcmp(d.dumpPath, dumpPath) == 0) &&        /* Same dumppath */
2111              (strcmp(d.dumpName, dumpName) == 0) &&        /* Same dumpname */
2112              (ntohl(d.created) > latest) )                 /* most recent */
2113         {
2114             latest    = ntohl(d.created);
2115             retdbaddr = curdbaddr;
2116         }
2117     }
2118     if (!retdbaddr) ABORT(BUDB_NODUMPNAME);
2119
2120   finished:
2121     /* return the dump found */
2122     FillDumpEntry(ut, retdbaddr, dumpentry);
2123
2124     code = ubik_EndTrans(ut);
2125     return(code);
2126
2127   abort_exit:
2128     ubik_AbortTrans(ut);
2129     return(code);
2130 }
2131
2132
2133 afs_int32 BUDB_FinishDump (call, dump)
2134   struct rx_call *call;
2135   struct budb_dumpEntry *dump;
2136 {
2137   afs_int32 code;
2138
2139   code = FinishDump (call, dump);
2140   osi_auditU (call, BUDB_FinDmpEvent, code, AUD_DATE, (dump ? dump->id : 0), AUD_END);
2141   return code;
2142 }
2143
2144 afs_int32 FinishDump (call, dump)
2145   struct rx_call *call;
2146   struct budb_dumpEntry *dump;
2147 {
2148     struct ubik_trans *ut;
2149     dbadr             a;
2150     struct dump       d;
2151     afs_int32             eval, code = 0;
2152
2153     if ( !callPermitted(call) )
2154         return BUDB_NOTPERMITTED;
2155
2156     eval = InitRPC(&ut, LOCKWRITE, 1);
2157     if (eval) return eval;
2158
2159     eval = ht_LookupEntry (ut, &db.dumpIden, &dump->id, &a, &d);
2160     if (eval) ABORT(eval);
2161     if (!a) ABORT(BUDB_NODUMPID);
2162
2163     if ((ntohl(d.flags) & BUDB_DUMP_INPROGRESS) == 0)
2164         ABORT(BUDB_DUMPNOTINUSE);
2165
2166     d.flags = htonl(dump->flags & ~BUDB_DUMP_INPROGRESS);
2167
2168     /* if creation time specified set it */
2169     if (dump->created) d.created = htonl(dump->created);
2170     dump->created = ntohl(d.created);
2171
2172     /* Write the dump entry out */
2173     eval = dbwrite(ut, a, &d, sizeof(d));
2174     if (eval) ABORT(eval);
2175
2176     eval = set_header_word(ut, lastUpdate, htonl(time(0)));
2177     if (eval) ABORT(eval);
2178
2179     code = ubik_EndTrans(ut);
2180     return code;
2181
2182   abort_exit:
2183     ubik_AbortTrans(ut);
2184     return code;
2185 }
2186
2187 afs_int32 BUDB_FinishTape (call, tape)
2188   struct rx_call *call;
2189   struct budb_tapeEntry *tape;
2190 {
2191   afs_int32 code;
2192
2193   code = FinishTape (call, tape);
2194   osi_auditU (call, BUDB_FinTpeEvent, code, AUD_DATE, (tape ? tape->dump : 0), AUD_END);
2195   return code;
2196 }
2197
2198 afs_int32 FinishTape (call, tape)
2199   struct rx_call *call;
2200   struct budb_tapeEntry *tape;
2201 {
2202     struct ubik_trans *ut;
2203     dbadr             a;
2204     struct tape       t;
2205     struct dump       d;
2206     afs_int32             eval, code = 0;
2207
2208     if ( !callPermitted(call) )
2209         return BUDB_NOTPERMITTED;
2210
2211     eval = InitRPC (&ut, LOCKWRITE, 1);
2212     if (eval) return eval;
2213
2214     /* find the tape struct in the tapename hash chain */
2215     eval = ht_LookupEntry (ut, &db.tapeName, tape->name, &a, &t);
2216     if (eval) ABORT(eval);
2217     if (!a) ABORT(BUDB_NOTAPENAME);
2218
2219     /* Read the dump structure */
2220     eval  = dbread(ut, ntohl(t.dump), &d, sizeof(d));
2221     if (eval) ABORT(eval);
2222
2223     /* search for the right tape on the rest of the chain */
2224     while (ntohl(d.id) != tape->dump)
2225     {
2226         a = ntohl(t.nameHashChain);
2227         if (!a) ABORT(BUDB_NOTAPENAME);
2228
2229         eval = dbread(ut, a, &t, sizeof(t));
2230         if (eval) ABORT(eval);
2231         
2232         eval = dbread(ut, ntohl(t.dump), &d, sizeof(d));
2233         if (eval) ABORT(eval);
2234     }
2235
2236     if ( (ntohl(t.flags) & BUDB_TAPE_BEINGWRITTEN) == 0 )
2237         ABORT(BUDB_TAPENOTINUSE);
2238
2239     /* t.nBytes = htonl(tape->nBytes); */
2240     t.nFiles    = htonl(tape->nFiles);
2241     t.useKBytes = htonl(tape->useKBytes);
2242     t.flags     = htonl(tape->flags & ~BUDB_TAPE_BEINGWRITTEN);
2243
2244     eval = dbwrite(ut, a, &t, sizeof(t));
2245     if (eval) ABORT(BUDB_IO);
2246
2247     eval = set_header_word (ut, lastUpdate, htonl(time(0)));
2248     if (eval) ABORT(eval);
2249
2250     code = ubik_EndTrans(ut);
2251     return code;
2252
2253   abort_exit:
2254     ubik_AbortTrans(ut);
2255     return code;
2256 }
2257     
2258 /* BUDB_GetDumps
2259  *      return a set of dumps that match the specified criteria
2260  * entry:
2261  *      call - rx call
2262  *      majorVersion - version of interface structures. Permits compatibility
2263  *              checks to be made
2264  *      flags - for search and select operations. Broken down into flags
2265  *              for name, start point, end point and time.
2266  *      name - name to search for. Interpretation based on flags
2267  *      end 
2268  *      index
2269  *      nextIndexP
2270  *      dbTimeP
2271  * exit:
2272  *      nextIndexP 
2273  *      dbTimeP - time at which the database was last modified. Up to
2274  *              caller (client) to take appropriate action if database
2275  *              modified between successive calls
2276  *      dumps - list of matching dumps
2277  * notes:
2278  *      currently supported are:
2279  *      BUDB_OP_DUMPNAME
2280  *      BUDB_OP_DUMPID
2281  */
2282
2283 afs_int32 BUDB_GetDumps (call, majorVersion, flags, name, start, end,
2284                     index, nextIndexP, dbTimeP, dumps)
2285   struct rx_call *call;
2286   int majorVersion;                     /* version of interface structures */
2287   afs_int32  flags;                             /* search & select controls */
2288   char *name;                           /* s&s parameters */
2289   afs_int32  start;
2290   afs_int32  end;
2291   afs_int32  index;                             /* start index of returned entries */
2292   afs_int32 *nextIndexP;                        /* output index for next call */
2293   afs_int32 *dbTimeP;
2294   budb_dumpList *dumps;                 /* pointer to buffer */
2295 {
2296   afs_int32 code;
2297
2298   code = GetDumps (call, majorVersion, flags, name, start, end,
2299                              index, nextIndexP, dbTimeP, dumps);
2300   osi_auditU (call, BUDB_GetDmpEvent, code, AUD_END);
2301   return code;
2302 }
2303
2304 afs_int32 GetDumps (call, majorVersion, flags, name, start, end,
2305                     index, nextIndexP, dbTimeP, dumps)
2306   struct rx_call *call;
2307   int majorVersion;                     /* version of interface structures */
2308   afs_int32  flags;                             /* search & select controls */
2309   char *name;                           /* s&s parameters */
2310   afs_int32  start;
2311   afs_int32  end;
2312   afs_int32  index;                             /* start index of returned entries */
2313   afs_int32 *nextIndexP;                        /* output index for next call */
2314   afs_int32 *dbTimeP;
2315   budb_dumpList *dumps;                 /* pointer to buffer */
2316 {   
2317     struct ubik_trans *ut;
2318     dbadr             da;
2319     struct dump       d;
2320     afs_int32             nameFlags, startFlags, endFlags, timeFlags;
2321     afs_int32             eval, code = 0;
2322     afs_int32             toskip;
2323     struct returnList list;
2324
2325     /* Don't check permissions when we look up a specific dump id */
2326     if ( ((flags & BUDB_OP_STARTS) != BUDB_OP_DUMPID) && !callPermitted(call) )
2327         return BUDB_NOTPERMITTED;
2328
2329     if (majorVersion != BUDB_MAJORVERSION) return BUDB_OLDINTERFACE;
2330     if (index < 0) return BUDB_ENDOFLIST;
2331
2332     eval = InitRPC (&ut, LOCKREAD, 1);
2333     if (eval) return eval;
2334
2335     nameFlags  = flags & BUDB_OP_NAMES;
2336     startFlags = flags & BUDB_OP_STARTS;
2337     endFlags   = flags & BUDB_OP_ENDS;
2338     timeFlags  = flags & BUDB_OP_TIMES;
2339
2340     InitReturnList (&list);
2341     toskip = index;
2342
2343     if (nameFlags == BUDB_OP_DUMPNAME) 
2344     {
2345         /* not yet implemented */
2346         if (startFlags || endFlags || timeFlags) ABORT(BUDB_BADFLAGS);
2347
2348         eval = ht_LookupEntry (ut, &db.dumpName, name, &da, &d);
2349         if (eval) ABORT(eval);
2350         if (!da) ABORT(BUDB_NODUMPNAME);
2351
2352         while (1) 
2353         {
2354             if (strcmp (d.dumpName, name) == 0) 
2355             {
2356                 eval = AddToReturnList (&list, da, &toskip);
2357                 if (eval == BUDB_LIST2BIG) break;
2358                 if (eval) ABORT(eval);
2359             }
2360
2361             da = ntohl(d.nameHashChain);        /* get next dump w/ name */
2362             if (!da) break;
2363
2364             eval = dbread (ut, da, &d, sizeof(d));
2365             if (eval) ABORT(eval);
2366         }
2367     }
2368     else
2369     if ( nameFlags == BUDB_OP_VOLUMENAME ) 
2370     {
2371 #ifdef PA
2372         struct volInfo vi;
2373         
2374         LogError(0, "NYI, BUDB_OP_VOLUMENAME\n");
2375         ABORT(BUDB_BADFLAGS);
2376
2377
2378         if (startFlags != BUDB_OP_STARTTIME) ABORT(BUDB_BADFLAGS);
2379
2380         /* lookup a dump by volumename and time stamp. Find the most recent
2381          * dump of the specified volumename, that occured before the supplied
2382          * time
2383          */
2384
2385         /* get us a volInfo for name */
2386         eval = ht_LookupEntry(ut, &db.volName, name, &da, &vi);
2387         if (eval) ABORT(eval);
2388
2389         while (1)
2390         {
2391             /* now iterate over all the entries of this name */
2392             for ( va = vi.firstFragment; va != 0; va = v.sameNameChain )
2393             {
2394                 va = ntohl(va);
2395                 eval = dbread(ut, va, &v, sizeof(v));
2396                 if (eval) ABORT(eval);
2397                 
2398                 if date on fragment > date
2399                 ignore it - too recent;
2400                 
2401                 if ( date on fragment < date && date on fragment > bestfound )
2402                 bestfound = date on fragment;
2403                 
2404             } /* for va */
2405
2406             da = vi.sameNameChain;
2407             if ( da == 0 )
2408                 break;
2409             da = ntohl(da);
2410             eval = dbread(ut, da, &vi, sizeof(vi));
2411             if (eval) ABORT(eval);
2412         }
2413
2414         if nothing found
2415                 return error
2416
2417         from saved volfragment address, compute dump.
2418         otherwise, return dump found
2419 #endif /* PA */
2420         
2421     }
2422     else if (startFlags == BUDB_OP_DUMPID) 
2423     {
2424         if (endFlags || timeFlags) ABORT(BUDB_BADFLAGS);
2425         if (nameFlags) ABORT(BUDB_BADFLAGS);                     /* NYI */
2426
2427         eval = ht_LookupEntry (ut, &db.dumpIden, &start, &da, &d);
2428         if (eval) ABORT(eval);
2429         if (!da) ABORT(BUDB_NODUMPID);
2430
2431         eval = AddToReturnList (&list, da, &toskip);
2432         if (eval) ABORT(eval);
2433     } 
2434     else if (endFlags == BUDB_OP_NPREVIOUS)
2435     {
2436         struct wantDumpRock rock;
2437         struct chosenDump *ptr, *nextPtr;
2438
2439         extern wantDump(), rememberDump();
2440
2441         /* no other flags should be set */
2442
2443         /* end specifies how many dumps */
2444         if (!end) ABORT(BUDB_BADFLAGS);
2445
2446         bzero(&rock, sizeof(rock));
2447         rock.maxDumps = end;
2448
2449         scanHashTable(ut, &db.dumpName, wantDump, rememberDump, (char *) &rock);
2450
2451         for (ptr=rock.chain; ptr; ptr=nextPtr)
2452         {
2453             nextPtr = ptr->next;
2454             AddToReturnList (&list, ptr->addr, &toskip);  /* ignore error for free */
2455             free(ptr);
2456         }
2457     }
2458     else
2459     {
2460         ABORT(BUDB_BADFLAGS);
2461     }
2462
2463     eval = SendReturnList (ut, &list, FillDumpEntry, sizeof(struct budb_dumpEntry),
2464                            index, nextIndexP, dbTimeP, (returnList_t)dumps);
2465     if (eval) ABORT(eval);
2466
2467   error_exit:
2468     FreeReturnList(&list);
2469     code = ubik_EndTrans(ut);
2470     return code;
2471
2472   abort_exit:
2473     FreeReturnList(&list);
2474     ubik_AbortTrans(ut);
2475     return code;
2476 }
2477
2478 /* 
2479  * Get the expiration of a tape.  Since the dump could have appended dumps,
2480  * we should use the most recent expiration date. Put the most recent 
2481  * expiration tape into the given tape structure.
2482  */
2483 afs_int32 getExpiration (ut, tapePtr)
2484      struct ubik_trans *ut;
2485      struct tape       *tapePtr;
2486 {
2487     dbadr       ad;
2488     struct dump d;
2489     struct tape t;
2490     afs_int32        initDump;
2491     afs_int32        eval, code = 0;
2492
2493     if (!tapePtr) ERROR(0);
2494
2495     /* Get the dump for this tape */
2496     ad = ntohl(tapePtr->dump);
2497     eval = dbread(ut,ad,&d,sizeof(d));
2498     if (eval) ERROR(eval);
2499     
2500     /* If not an initial dump, get the initial dump */
2501     if (d.initialDumpID)
2502     {
2503         initDump = ntohl(d.initialDumpID);
2504         eval = ht_LookupEntry (ut, &db.dumpIden, &initDump, &ad, &d);
2505         if (eval) ERROR(eval);
2506     }
2507
2508     /* Cycle through the dumps and appended dumps */
2509     while (ad)
2510     {
2511         /* Get the first tape in this dump. No need to check the rest of the tapes */
2512         /* for this dump since they will all have the same expiration date */
2513         eval = dbread (ut, ntohl(d.firstTape), &t, sizeof(t));
2514         if (eval) ERROR(eval);
2515         
2516         /* Take the greater of the expiration dates */
2517         if ( ntohl(tapePtr->expires) < ntohl(t.expires) )
2518             tapePtr->expires = t.expires;
2519
2520         /* Step to and read the next appended dump */
2521         if ( ad = ntohl(d.appendedDumpChain) )      
2522         {
2523             eval = dbread(ut,ad,&d,sizeof(d));
2524             if (eval) ERROR(eval);
2525         }
2526     }
2527
2528   error_exit:
2529     return(code);
2530 }
2531
2532 /* Mark the following dump as appended to another, intial dump */
2533 afs_int32 makeAppended (ut, appendedDumpID, initialDumpID, startTapeSeq)
2534      struct ubik_trans *ut;
2535      afs_int32   appendedDumpID;
2536      afs_int32   initialDumpID;
2537      afs_int32   startTapeSeq;
2538 {
2539     dbadr       ada, da, lastDumpAddr;
2540     struct dump ad,  d;
2541     afs_int32       eval, code = 0;
2542
2543     if (!initialDumpID)
2544        ERROR(0);
2545     if (appendedDumpID == initialDumpID)
2546        ERROR(BUDB_INTERNALERROR);
2547
2548     /* If there is an initial dump, append this dump to it */
2549     /* Find the appended dump via its id */
2550     eval = ht_LookupEntry(ut,&db.dumpIden,&appendedDumpID,&ada,&ad);
2551     if (eval) ERROR(eval);
2552
2553     /* If the dump is already marked as appended,
2554      * then we have an internal error.
2555      */
2556     if (ad.initialDumpID) {
2557        if (ntohl(ad.initialDumpID) != initialDumpID)
2558           ERROR(BUDB_INTERNALERROR);
2559     }
2560
2561     /* Update the appended dump to point to the initial dump */
2562     ad.initialDumpID = htonl(initialDumpID);
2563     ad.tapes.b       = htonl(startTapeSeq);
2564
2565     /* find the initial dump via its id */
2566     eval = ht_LookupEntry(ut,&db.dumpIden,&initialDumpID,&da,&d);
2567     if (eval) ERROR(eval);
2568
2569     /* Update the appended dump's tape format with that of the initial */
2570     strcpy(ad.tapes.format, d.tapes.format);
2571
2572     /* starting with the initial dump step through its appended dumps till 
2573      * we reach the last appended dump. 
2574      */
2575     lastDumpAddr = da;
2576     while (d.appendedDumpChain) {
2577        lastDumpAddr = ntohl(d.appendedDumpChain);
2578        if (lastDumpAddr == ada) ERROR(0);               /* Already appended */
2579        eval = dbread(ut, lastDumpAddr, &d, sizeof(d));
2580        if (eval) ERROR(eval);
2581     }
2582       
2583     /* Update the last dump to point to our new appended dump.
2584      * The appended dump is the last one in the dump chain.
2585      */
2586     d.appendedDumpChain  = htonl(ada);
2587     ad.appendedDumpChain = 0;
2588
2589     /* Write the appended dump and the initial dump */
2590     eval = dbwrite(ut,ada,(char *)&ad,sizeof(ad));
2591     if (eval) ERROR(eval);
2592
2593     eval = dbwrite (ut, lastDumpAddr, (char *)&d, sizeof(d));
2594     if (eval) ERROR(eval);
2595
2596     eval = set_header_word (ut, lastUpdate, htonl(time(0)));
2597     if (eval) ERROR(eval);
2598
2599   error_exit:
2600     return(code);
2601 }
2602
2603 afs_int32 BUDB_MakeDumpAppended (call, appendedDumpID, initialDumpID, startTapeSeq)
2604      struct rx_call *call;
2605      afs_int32           appendedDumpID;
2606      afs_int32           initialDumpID;
2607      afs_int32           startTapeSeq;
2608 {
2609   afs_int32 code;
2610
2611   code = MakeDumpAppended (call, appendedDumpID, initialDumpID, startTapeSeq);
2612   osi_auditU (call, BUDB_AppDmpEvent, code, AUD_LONG, appendedDumpID, AUD_END);
2613   return code;
2614 }
2615
2616 afs_int32 MakeDumpAppended (call, appendedDumpID, initialDumpID, startTapeSeq)
2617      struct rx_call *call;
2618      afs_int32           appendedDumpID;
2619      afs_int32           initialDumpID;
2620      afs_int32           startTapeSeq;
2621 {
2622      struct ubik_trans *ut;
2623      afs_int32             eval, code = 0;
2624
2625      if ( !callPermitted(call) )
2626          return BUDB_NOTPERMITTED;
2627
2628      eval = InitRPC (&ut, LOCKWRITE, 1);
2629      if (eval) return(eval);
2630
2631      eval = makeAppended(ut,appendedDumpID,initialDumpID,startTapeSeq);
2632      if (eval) ABORT(eval);
2633
2634      code = ubik_EndTrans(ut);
2635      return (code);
2636
2637    abort_exit:
2638      ubik_AbortTrans(ut);
2639      return(code);
2640 }
2641
2642 /* Find the last tape of a dump-set. This includes any appended dumps */
2643 afs_int32 BUDB_FindLastTape (call, dumpID, dumpEntry, tapeEntry, volEntry)
2644      struct rx_call          *call;
2645      afs_int32                    dumpID;
2646      struct budb_dumpEntry   *dumpEntry;
2647      struct budb_tapeEntry   *tapeEntry;
2648      struct budb_volumeEntry *volEntry;
2649 {
2650   afs_int32 code;
2651
2652   code = FindLastTape (call, dumpID, dumpEntry, tapeEntry, volEntry);
2653   osi_auditU (call, BUDB_FndLTpeEvent, code, AUD_LONG, dumpID, AUD_END);
2654   return code;
2655 }
2656
2657 afs_int32 FindLastTape (call, dumpID, dumpEntry, tapeEntry, volEntry)
2658      struct rx_call          *call;
2659      afs_int32                    dumpID;
2660      struct budb_dumpEntry   *dumpEntry;
2661      struct budb_tapeEntry   *tapeEntry;
2662      struct budb_volumeEntry *volEntry;
2663 {
2664      struct ubik_trans *ut;
2665      struct dump        d;
2666      dbadr              lastDump;
2667      struct tape        t;
2668      dbadr              lastTape, thisTape;
2669      afs_int32              lastTapeSeq;
2670      struct volFragment vf;
2671      dbadr              lastVol, thisVol;
2672      afs_int32              lastVolPos;
2673      afs_int32              eval, code = 0;
2674
2675      if ( !callPermitted(call) )
2676          return BUDB_NOTPERMITTED;
2677
2678      if (!dumpID) return(BUDB_BADARGUMENT);
2679
2680      eval = InitRPC (&ut, LOCKREAD, 1);
2681      if (eval) return(eval);
2682
2683      /* find and read its initial dump via its id */
2684      eval = ht_LookupEntry (ut, &db.dumpIden, &dumpID, &lastDump, &d);
2685      if (eval) ABORT(eval);
2686      if (!lastDump) ABORT(BUDB_NODUMPID);
2687
2688      /* Follow the append dumps link chain until we reach the last dump */
2689      while (d.appendedDumpChain)
2690      {
2691          lastDump = ntohl(d.appendedDumpChain);
2692          eval = dbread(ut,lastDump,&d,sizeof(d));
2693          if (eval) ABORT(eval);
2694      }
2695
2696      /* We now have the last dump of the last appended dump */
2697      /* Copy this into our return structure */
2698      eval = FillDumpEntry(ut,lastDump,dumpEntry);
2699      if (eval) ABORT(eval);
2700
2701      /* Fail if the last dump has no tapes */
2702      if (!d.firstTape) ABORT(BUDB_NOTAPENAME);
2703
2704      /* Follow the tapes in this dump until we reach the last tape */
2705      eval = dbread (ut, ntohl(d.firstTape), &t, sizeof(t));
2706      if (eval) ABORT(eval);     
2707
2708      lastTape    = ntohl(d.firstTape);
2709      lastTapeSeq = ntohl(t.seq);
2710      lastVol     = ntohl(t.firstVol);
2711      
2712      while (t.nextTape)
2713      {
2714          thisTape = ntohl(t.nextTape);
2715          eval = dbread(ut,thisTape,&t,sizeof(t));
2716          if (eval) ABORT(eval);
2717
2718          if (ntohl(t.seq) > lastTapeSeq)
2719          {
2720              lastTape    = thisTape;
2721              lastTapeSeq = ntohl(t.seq);
2722              lastVol     = ntohl(t.firstVol);
2723          }
2724      }
2725
2726      /* We now have the last tape of the last appended dump */
2727      /* Copy this into our return structure */
2728      eval = FillTapeEntry(ut,lastTape,tapeEntry);
2729      if (eval) ABORT(eval);
2730
2731      /* Zero volume entry if the last tape has no volumes */
2732      if (!lastVol) {
2733         bzero(volEntry, sizeof(*volEntry));
2734      } else {
2735         /* Follow the volumes until we reach the last volume */
2736         eval = dbread (ut,lastVol,&vf,sizeof(vf));
2737         if (eval) ABORT(eval);
2738
2739         lastVolPos = vf.position;
2740
2741         while (vf.sameTapeChain) {
2742            thisVol = ntohl(vf.sameTapeChain);
2743            eval = dbread(ut,thisVol,&vf,sizeof(vf));
2744            if (eval) ABORT(eval);
2745
2746            if (vf.position > lastVolPos) {
2747               lastVol    = thisVol;
2748               lastVolPos = vf.position;
2749            }
2750         }
2751
2752         /* We now have the last volume of this tape */
2753         /* Copy this into our return structure */
2754         eval = FillVolEntry(ut,lastVol,volEntry);
2755         if (eval) ABORT(eval);
2756      }
2757
2758      eval = ubik_EndTrans(ut);
2759      if (!code) code = eval;
2760      return (code);
2761
2762    abort_exit:
2763      ubik_AbortTrans(ut);
2764      return(code);
2765 }
2766
2767
2768 afs_int32 BUDB_GetTapes (call, majorVersion, flags, name, start, end, index, nextIndexP,
2769                dbTimeP, tapes)
2770      struct rx_call *call;
2771      int   majorVersion;                /* version of interface structures */
2772      afs_int32  flags;                  /* search & select controls */
2773      char *name;                        /* s&s parameters */
2774      afs_int32  start;
2775      afs_int32  end;                            /* reserved: MBZ */
2776      afs_int32  index;                  /* start index of returned entries */
2777      afs_int32 *nextIndexP;                     /* output index for next call */
2778      afs_int32 *dbTimeP;
2779      budb_tapeList *tapes;              /* pointer to buffer */
2780 {
2781   afs_int32 code;
2782
2783   code = GetTapes (call, majorVersion, flags, name, start, end, 
2784                              index, nextIndexP, dbTimeP, tapes);
2785   osi_auditU (call, BUDB_GetTpeEvent, code, AUD_END);
2786   return code;
2787 }
2788
2789 afs_int32 GetTapes (call, majorVersion, flags, name, start, end, 
2790                          index, nextIndexP, dbTimeP, tapes)
2791      struct rx_call *call;
2792      int   majorVersion;                /* version of interface structures */
2793      afs_int32  flags;                  /* search & select controls */
2794      char *name;                        /* s&s parameters */
2795      afs_int32  start;
2796      afs_int32  end;                            /* reserved: MBZ */
2797      afs_int32  index;                  /* start index of returned entries */
2798      afs_int32 *nextIndexP;                     /* output index for next call */
2799      afs_int32 *dbTimeP;
2800      budb_tapeList *tapes;              /* pointer to buffer */
2801 {
2802     struct ubik_trans *ut;
2803     dbadr             da, ta;
2804     struct dump       d;
2805     struct tape       t;
2806     afs_int32             nameFlags, startFlags, endFlags, timeFlags;
2807     struct returnList list;
2808     afs_int32             eval, code = 0;
2809     afs_int32             toskip;
2810
2811     if ( !callPermitted(call) )
2812         return BUDB_NOTPERMITTED;
2813
2814     if (majorVersion != BUDB_MAJORVERSION)
2815         return BUDB_OLDINTERFACE;
2816
2817     if (index < 0) return BUDB_ENDOFLIST;
2818
2819     eval = InitRPC (&ut, LOCKREAD, 1);
2820     if (eval) return eval;
2821
2822     nameFlags  = flags & BUDB_OP_NAMES;
2823     startFlags = flags & BUDB_OP_STARTS;
2824     endFlags   = flags & BUDB_OP_ENDS;
2825     timeFlags  = flags & BUDB_OP_TIMES;
2826
2827     InitReturnList (&list);
2828     toskip = index;
2829
2830     if (nameFlags == BUDB_OP_TAPENAME)
2831     { /*it*/
2832         eval = ht_LookupEntry (ut, &db.tapeName, name, &ta, &t);
2833         if (eval) ABORT(eval);
2834         if (!ta) ABORT(BUDB_NOTAPENAME);
2835         
2836         /* NYI */
2837         if ( (startFlags & ~BUDB_OP_DUMPID) || endFlags || timeFlags ) ABORT(BUDB_BADFLAGS);
2838
2839         /* follow the hash chain to the end */
2840         while (ta)
2841         { /*w*/
2842             if (startFlags & BUDB_OP_DUMPID)
2843             {
2844                 /* read in the dump */
2845                 eval = dbread(ut, ntohl(t.dump), &d, sizeof(d));
2846                 if (eval) ABORT(eval);
2847
2848                 /* check if both name and dump id match */
2849                 if ( (strcmp(name, t.name) == 0) && (ntohl(d.id) == start) )
2850                 {
2851                     eval = AddToReturnList (&list, ta, &toskip);
2852                     if (eval && (eval != BUDB_LIST2BIG)) ABORT(eval);
2853                     break;
2854                 }
2855             }
2856             else
2857             {
2858                 /* Add to return list and continue search */
2859                 if ( strcmp(name, t.name) == 0 ) 
2860                 {
2861                     eval = AddToReturnList (&list, ta, &toskip);
2862                     if (eval == BUDB_LIST2BIG) break;
2863                     if (eval) ABORT(eval);                  
2864                 }
2865             }
2866
2867             ta = ntohl(t.nameHashChain);
2868             if (ta) dbread(ut, ta, &t, sizeof(t));
2869         } /*w*/
2870     } /*it*/
2871     else if (nameFlags == BUDB_OP_TAPESEQ)
2872     {
2873         eval = ht_LookupEntry(ut,&db.dumpIden, &start, &da, &d);
2874         if (eval) ABORT(eval);
2875         if (!da) ABORT(BUDB_NODUMPNAME);
2876         
2877         /* search for the right tape */
2878         ta = ntohl(d.firstTape);
2879         for (ta = ntohl(d.firstTape); ta; ta = ntohl(t.nextTape))
2880         {
2881             eval = dbread(ut, ta, &t, sizeof(t));
2882             if (eval) ABORT(eval);
2883
2884             if (ntohl(t.seq) == end)
2885             {
2886                 eval = AddToReturnList (&list, ta, &toskip);
2887                 if (eval && (eval != BUDB_LIST2BIG)) ABORT(eval);
2888                 break;
2889             }
2890         }
2891     }
2892     else
2893     {
2894         ABORT (BUDB_BADFLAGS);
2895     }
2896
2897     eval = SendReturnList (ut, &list, FillTapeEntry,
2898                            sizeof(struct budb_tapeEntry),
2899                            index, nextIndexP, dbTimeP, (returnList_t)tapes);
2900     if (eval) ABORT(eval);
2901
2902     FreeReturnList(&list);
2903     code = ubik_EndTrans(ut);
2904     return code;    
2905     
2906   abort_exit:
2907     FreeReturnList(&list);
2908     ubik_AbortTrans(ut);
2909     return(code);
2910 }
2911
2912 /* BUDB_GetVolumes
2913  *      get a set of volumes according to the specified criteria.
2914  *      See BUDB_GetDumps for general information on parameters
2915  *      Currently supports:
2916  *      1) volume match - returns volumes based on volume name only.
2917  *      2) flags = BUDB_OP_DUMPID in which case name is a volume name
2918  *              and start is a dumpid. Returns all volumes of the specified
2919  *              name on the selected dumpid.
2920  */
2921
2922 afs_int32 BUDB_GetVolumes (call, majorVersion, flags, name, start, end,
2923                       index, nextIndexP, dbTimeP, volumes)
2924   struct rx_call *call;
2925   int   majorVersion;                   /* version of interface structures */
2926   afs_int32  flags;                             /* search & select controls */
2927   char *name;                           /*  - parameters for search */
2928   afs_int32  start;                             /*  - usage depends which BUDP_OP_* */
2929   afs_int32  end;                               /*  - bits are set */
2930   afs_int32  index;                             /* start index of returned entries */
2931   afs_int32 *nextIndexP;                        /* output index for next call */
2932   afs_int32 *dbTimeP;
2933   budb_volumeList *volumes;             /* pointer to buffer */
2934 {
2935   afs_int32 code;
2936
2937   code = GetVolumes (call, majorVersion, flags, name, start, end,
2938                                index, nextIndexP, dbTimeP, volumes);
2939   osi_auditU (call, BUDB_GetVolEvent, code, AUD_END);
2940   return code;
2941 }
2942
2943 afs_int32 GetVolumes (call, majorVersion, flags, name, start, end,
2944                       index, nextIndexP, dbTimeP, volumes)
2945   struct rx_call *call;
2946   int   majorVersion;                   /* version of interface structures */
2947   afs_int32  flags;                             /* search & select controls */
2948   char *name;                           /*  - parameters for search */
2949   afs_int32  start;                             /*  - usage depends which BUDP_OP_* */
2950   afs_int32  end;                               /*  - bits are set */
2951   afs_int32  index;                             /* start index of returned entries */
2952   afs_int32 *nextIndexP;                        /* output index for next call */
2953   afs_int32 *dbTimeP;
2954   budb_volumeList *volumes;             /* pointer to buffer */
2955 {
2956     struct ubik_trans *ut;
2957     dbadr             via;
2958     struct volInfo    vi;
2959     afs_int32             nameFlags, startFlags, endFlags, timeFlags;
2960     afs_int32             eval, code = 0;
2961     struct returnList vollist;
2962     afs_int32             toskip;
2963
2964     /* Don't check permissions when we look up a specific volume name */
2965     if ( ((flags & BUDB_OP_NAMES) != BUDB_OP_VOLUMENAME) && !callPermitted(call) )
2966         return BUDB_NOTPERMITTED;
2967
2968     if (majorVersion != BUDB_MAJORVERSION) return BUDB_OLDINTERFACE;
2969     if (index < 0) return BUDB_ENDOFLIST;
2970
2971     eval = InitRPC (&ut, LOCKREAD, 1);
2972     if (eval) return eval;
2973
2974     nameFlags  = flags & BUDB_OP_NAMES;
2975     startFlags = flags & BUDB_OP_STARTS;
2976     endFlags   = flags & BUDB_OP_ENDS;
2977     timeFlags  = flags & BUDB_OP_TIMES;
2978
2979     InitReturnList (&vollist);
2980     toskip = index;
2981
2982     /* lookup a the volume (specified by name) in the dump (specified by id) */
2983     if (nameFlags == BUDB_OP_VOLUMENAME)
2984     {
2985         /* dumpid permissible, all others off */
2986         if ( ((startFlags & ~BUDB_OP_DUMPID) != 0) || endFlags || timeFlags )
2987             ABORT(BUDB_BADFLAGS);
2988
2989         /* returns ptr to volinfo of requested name */
2990         eval = ht_LookupEntry (ut, &db.volName, name, &via, &vi);
2991         if (eval) ABORT(eval);
2992         if (!via) ABORT(BUDB_NOVOLUMENAME);
2993
2994         /* Iterate over all volume fragments with this name */
2995         while (1) 
2996         {
2997             struct volFragment v;
2998             afs_int32 va;
2999
3000             /* traverse all the volume fragments for this volume info structure */
3001             for (va=vi.firstFragment; va; va=v.sameNameChain) 
3002             {
3003                 va = ntohl(va);
3004                 eval = dbread (ut, va, &v, sizeof(v));
3005                 if (eval) ABORT(eval);
3006
3007                 if ( startFlags & BUDB_OP_DUMPID )
3008                 {
3009                     struct tape atape;
3010                     struct dump adump;
3011
3012                     /* get the dump id for this fragment */
3013                     eval = dbread(ut, ntohl(v.tape), &atape, sizeof(atape));
3014                     if (eval) ABORT(eval);
3015
3016                     eval = dbread(ut, ntohl(atape.dump), &adump, sizeof(adump));
3017                     if (eval) ABORT(BUDB_IO);
3018
3019                     /* dump id does not match */
3020                     if ( ntohl(adump.id) != start )
3021                         continue;
3022                 }
3023
3024                 eval = AddToReturnList (&vollist, va, &toskip);
3025                 if (eval == BUDB_LIST2BIG) break;
3026                 if (eval) ABORT(eval);
3027             }
3028             if (eval == BUDB_LIST2BIG) break;
3029
3030             via = vi.sameNameChain;
3031             if (via == 0) break;
3032             via = ntohl(via);
3033
3034             eval = dbread (ut, via, &vi, sizeof(vi));
3035             if (eval) ABORT(eval);
3036         }
3037     }
3038     else if ( ((nameFlags == 0) || (nameFlags == BUDB_OP_TAPENAME)) &&
3039               (startFlags == BUDB_OP_DUMPID) )
3040     {
3041         struct dump dump;
3042         dbadr dumpAddr;
3043         struct tape tape;
3044         dbadr tapeAddr;
3045         struct volFragment volFrag;
3046         dbadr volFragAddr;
3047
3048         /* lookup all volumes for a specified dump id */
3049
3050         /* no other flags should be set */
3051         if (endFlags || timeFlags) ABORT(BUDB_BADFLAGS);
3052
3053         /* find the dump */
3054         eval = ht_LookupEntry(ut, &db.dumpIden, &start, &dumpAddr, &dump);
3055         if (eval) ABORT(eval);
3056
3057         /* traverse all the tapes */
3058         for (tapeAddr=ntohl(dump.firstTape); tapeAddr; tapeAddr=ntohl(tape.nextTape))
3059         { /*w*/
3060             eval = dbread(ut, tapeAddr, &tape, sizeof(tape));
3061             if (eval) ABORT(eval);
3062
3063             if ( ( nameFlags != BUDB_OP_TAPENAME ) ||
3064                  ((nameFlags == BUDB_OP_TAPENAME) && (strcmp(tape.name,name) == 0)) )
3065             {
3066                 /* now return all the volumes */
3067                 for (volFragAddr=ntohl(tape.firstVol); volFragAddr; 
3068                      volFragAddr=ntohl(volFrag.sameTapeChain))
3069                 {
3070                     eval = dbread(ut, volFragAddr, &volFrag, sizeof(volFrag));
3071                     if (eval) ABORT(eval);
3072
3073                     eval = AddToReturnList(&vollist, volFragAddr, &toskip);
3074                     if (eval == BUDB_LIST2BIG) break;
3075                     if (eval) ABORT(eval);
3076                 }
3077             }
3078             if (eval == BUDB_LIST2BIG) break;
3079         } /*w*/
3080     }
3081     else
3082     {
3083         ABORT(BUDB_BADFLAGS);
3084     }
3085
3086     eval = SendReturnList(ut, &vollist, FillVolEntry, sizeof(struct budb_volumeEntry),
3087                            index, nextIndexP, dbTimeP, (returnList_t)volumes);
3088     if (eval) ABORT(eval);
3089
3090   error_exit:
3091     FreeReturnList(&vollist);
3092     code = ubik_EndTrans(ut);
3093     return code;
3094
3095   abort_exit:
3096     FreeReturnList(&vollist);
3097     ubik_AbortTrans(ut);
3098     return code;
3099 }
3100
3101 afs_int32 BUDB_UseTape (call, tape, new)
3102   struct rx_call *call;
3103   struct budb_tapeEntry *tape;          /* tape info */
3104   int  *new;                            /* set if tape is new */
3105 {
3106   afs_int32 code;
3107
3108   code = UseTape (call, tape, new);
3109   osi_auditU (call, BUDB_UseTpeEvent, code, AUD_DATE, (tape ? tape->dump : 0), AUD_END);
3110   return code;
3111 }
3112
3113 afs_int32 UseTape (call, tape, new)
3114   struct rx_call *call;
3115   struct budb_tapeEntry *tape;          /* tape info */
3116   int  *new;                            /* set if tape is new */
3117 {
3118     struct ubik_trans *ut;
3119     dbadr             da, a;
3120     struct dump       d;
3121     struct tape       t;
3122     afs_int32             eval, code;
3123
3124     if ( !callPermitted(call) )
3125         return BUDB_NOTPERMITTED;
3126
3127     if (strlen (tape->name) >= sizeof(t.name)) return BUDB_BADARGUMENT;
3128
3129     eval = InitRPC (&ut, LOCKWRITE, 1);
3130     if (eval) return eval;
3131
3132     *new = 0;
3133
3134     bzero (&t, sizeof(t));
3135     eval = AllocStructure (ut, tape_BLOCK, 0, &a, &t);
3136     if (eval) ABORT(eval);
3137
3138     strcpy (t.name, tape->name);
3139
3140     eval = ht_HashIn (ut, &db.tapeName, a, &t);
3141     if (eval) ABORT(eval);
3142
3143     *new = 1;
3144
3145     /* Since deleting a tape may change the dump (if its the same one), read in
3146        the dump after the call to DeleteTape. */
3147
3148     eval = ht_LookupEntry (ut, &db.dumpIden, &tape->dump, &da, &d);
3149     if (eval) ABORT(eval);
3150     if (!da) ABORT(BUDB_NODUMPID);
3151
3152     if (!tape->written) tape->written = time(0);    /* fill in tape struct */
3153     t.written   = htonl(tape->written);
3154     t.expires   = htonl(tape->expires);
3155     t.dump      = htonl(da);
3156     t.seq       = htonl(tape->seq);
3157     t.useCount  = htonl(tape->useCount);
3158     t.labelpos  = htonl(tape->labelpos);
3159     t.useKBytes = 0;
3160     t.flags     = htonl(tape->flags | BUDB_TAPE_BEINGWRITTEN);
3161
3162     t.nextTape = d.firstTape;           /* Chain the tape to the dump */
3163     d.firstTape = htonl(a);
3164
3165     if (tape->seq >= ntohl(d.tapes.maxTapes))    /* inc # tapes in the dump */
3166         d.tapes.maxTapes = htonl(tape->seq);
3167
3168     eval = dbwrite (ut, a, &t, sizeof(t));         /* write tape struct */
3169     if (eval) ABORT(eval);
3170
3171     eval = dbwrite (ut, da, &d, sizeof(d));        /* write the dump struct */
3172     if (eval) ABORT(eval);
3173
3174     eval = set_header_word (ut, lastUpdate, htonl(time(0)));
3175     if (eval) ABORT(eval);
3176
3177     LogDebug(5, "added tape %s\n", tape->name);
3178
3179     code = ubik_EndTrans(ut);
3180     return code;
3181
3182   abort_exit:
3183     ubik_AbortTrans(ut);
3184     return code;
3185
3186 }
3187
3188
3189 /* ---------------------------------------------
3190  * debug interface routines
3191  * ---------------------------------------------
3192  */
3193
3194 afs_int32 BUDB_T_DumpHashTable (call, type, filename)
3195   struct rx_call *call;
3196   int   type;
3197   char *filename;
3198 {
3199   afs_int32 code;
3200
3201   code = T_DumpHashTable (call, type, filename);
3202   osi_auditU (call, BUDB_TDmpHaEvent, code, AUD_STR, filename, AUD_END);
3203   return code;
3204 }
3205
3206 afs_int32 T_DumpHashTable (call, type, filename)
3207   struct rx_call *call;
3208   int   type;
3209   char *filename;
3210 {
3211     struct ubik_trans      *ut;
3212     struct memoryHashTable *mht;
3213     int                    ent;
3214     afs_int32                  eval, code = 0;
3215     char                   path[64];
3216     FILE                   *DUMP;
3217
3218     int                     length;
3219     afs_uint32                 hash;
3220     dbadr                   a, first_a;
3221     char                    e[sizeof(struct block)];    /* unnecessarily conservative */
3222     int                     e_size;
3223     int                     old;
3224
3225     if ( !callPermitted(call) )
3226         return BUDB_NOTPERMITTED;
3227
3228     if (strlen (filename) >= sizeof(path)-5) return BUDB_BADARGUMENT;
3229
3230     eval = InitRPC (&ut, LOCKWRITE, 1);
3231     if (eval) return eval;
3232
3233     if ((mht = ht_GetType (type, &e_size)) == 0) return BUDB_BADARGUMENT;
3234
3235     sprintf(path, "%s/%s", gettmpdir(), filename);
3236
3237     DUMP = fopen (path, "w");
3238     if (!DUMP) ABORT(BUDB_BADARGUMENT);
3239
3240     ent = 0;
3241     for (old=0; ; old++) 
3242     {
3243         length = (old ? mht->oldLength : mht->length);
3244         if (length) fprintf (DUMP, "Dumping %sHash Table:\n", (old?"Old ":""));
3245
3246         for (hash=0; hash<length; hash++) 
3247         {
3248             a = ht_LookupBucket (ut, mht, hash, old);
3249             first_a = a;
3250             while (a) 
3251             {
3252                 eval = dbread (ut, a, e, e_size);
3253                 if (eval) ABORT(eval);
3254
3255                 ent++;
3256                 if (a == first_a) fprintf (DUMP, "  in bucket %d at %d is ", hash, a);
3257                 else fprintf (DUMP, "    at %d is ", a);
3258                 switch (type) {
3259                   case HT_dumpIden_FUNCTION:
3260                     fprintf (DUMP, "%d\n", ntohl(((struct dump *)e)->id));
3261                     break;
3262                   case HT_dumpName_FUNCTION:
3263                     fprintf (DUMP, "%s\n", ((struct dump *)e)->dumpName);
3264                     break;
3265                   case HT_tapeName_FUNCTION:
3266                     fprintf (DUMP, "%s\n", ((struct tape *)e)->name);
3267                     break;
3268                   case HT_volName_FUNCTION:
3269                     fprintf (DUMP, "%s\n", ((struct volInfo *)e)->name);
3270                     break;
3271                 }
3272                 if ((ht_HashEntry(mht,e) % length) != hash) ABORT(BUDB_DATABASEINCONSISTENT);
3273                 a = ntohl(*(dbadr *)(e + mht->threadOffset));
3274             }
3275         }
3276         if (old) break;
3277     }
3278
3279     fprintf (DUMP, "%d entries found\n", ent);
3280     if (ntohl(mht->ht->entries) != ent) ABORT(BUDB_DATABASEINCONSISTENT);
3281
3282     code = ubik_EndTrans(ut);
3283     if (DUMP) fclose (DUMP);
3284     return code;
3285   
3286   abort_exit:
3287     ubik_AbortTrans(ut);
3288     if (DUMP) fclose (DUMP);
3289     return code;
3290 }
3291
3292 afs_int32 BUDB_T_GetVersion (call, majorVersion)
3293   struct rx_call *call;
3294   int *majorVersion;
3295 {
3296   afs_int32 code;
3297
3298   code = T_GetVersion (call, majorVersion);
3299   osi_auditU (call, BUDB_TGetVrEvent, code, AUD_END);
3300   return code;
3301 }
3302
3303 afs_int32 T_GetVersion (call, majorVersion)
3304   struct rx_call *call;
3305   int *majorVersion;
3306 {
3307     struct ubik_trans *ut;
3308     afs_int32 code;
3309     
3310     code = InitRPC (&ut, LOCKREAD, 0);
3311     if (code) return(code);
3312     
3313     *majorVersion = BUDB_MAJORVERSION;
3314     
3315     code = ubik_EndTrans(ut);
3316     return(code);
3317 }
3318
3319 /* BUDB_T_DumpDatabase
3320  *      dump as much of the database as possible int /tmp/<filename>
3321  */
3322
3323 afs_int32 BUDB_T_DumpDatabase (call, filename)
3324      struct rx_call *call;
3325      char *filename;
3326 {
3327   afs_int32 code;
3328
3329   code = T_DumpDatabase (call, filename);
3330   osi_auditU (call, BUDB_TDmpDBEvent, code, AUD_STR, filename, AUD_END);
3331   return code;
3332 }
3333
3334 afs_int32 T_DumpDatabase (call, filename)
3335      struct rx_call *call;
3336      char *filename;
3337 {  
3338     FILE *dumpfid;
3339     int entrySize;
3340     struct ubik_trans *ut;
3341     char *path = 0;
3342     dbadr dbAddr;
3343     int type, old, length, hash;
3344     int block, index;
3345     struct memoryHashTable *mht;
3346     afs_int32                  eval, code = 0;
3347
3348     if ( !callPermitted(call) )
3349         return BUDB_NOTPERMITTED;
3350
3351     path = (char *) malloc(strlen(gettmpdir())+1+strlen(filename)+1);
3352     if (!path) return(BUDB_INTERNALERROR);
3353
3354     sprintf(path, "%s/%s", gettmpdir(), filename);
3355
3356     dumpfid = fopen(path, "w");
3357     if (!dumpfid) return(BUDB_BADARGUMENT);
3358
3359     eval = InitRPC (&ut, LOCKWRITE, 1);
3360     if (eval) return(eval);
3361
3362    /* dump all items in the database */
3363     for ( type=1; type<=HT_MAX_FUNCTION; type++ )
3364     { /*ft*/
3365         mht = ht_GetType (type, &entrySize);
3366         if (!mht) ERROR(BUDB_BADARGUMENT);
3367
3368         for ( old =0; old <= 1; old++ )
3369         { /*fo*/
3370             length = ( old ? mht->oldLength : mht->length);
3371             if (!length) continue;
3372
3373             fprintf (dumpfid, "Dumping %s Hash Table:\n", (old ? "Old ":""));
3374             
3375             for ( hash = 0; hash < length; hash++ )
3376             { /*f*/
3377                 dbAddr = ht_LookupBucket (ut, mht, hash, old);
3378
3379                 while ( dbAddr )
3380                 { /*w*/
3381                     switch (type)
3382                     { /*s*/
3383                       case HT_dumpIden_FUNCTION:
3384                         {
3385                             struct dump hostDump, diskDump;
3386
3387                             eval = cdbread (ut, dump_BLOCK, dbAddr, &diskDump, sizeof(diskDump));
3388                             if (eval) ERROR(eval);
3389
3390                             fprintf(dumpfid, "\ndumpId hash %d, entry at %u: block %d, index %d\n",
3391                                     hash, dbAddr, block, index);
3392                             fprintf(dumpfid, "----------------------------\n");
3393                             dump_ntoh(&diskDump, &hostDump);
3394                             printDump(dumpfid, &hostDump);
3395                             dbAddr = hostDump.idHashChain;
3396                         }
3397                         break;
3398                     
3399                       case HT_dumpName_FUNCTION:
3400                         {
3401                             struct dump hostDump, diskDump;
3402
3403                             eval = cdbread (ut, dump_BLOCK, dbAddr, &diskDump, sizeof(diskDump));
3404                             if (eval) ERROR(eval);
3405
3406                             fprintf(dumpfid,
3407                                     "\ndumpname hash %d, entry at %u: block %d, index %d\n",
3408                                     hash, dbAddr, block, index);
3409                             fprintf(dumpfid, "----------------------------\n");
3410                             dump_ntoh(&diskDump, &hostDump);
3411                             printDump(dumpfid, &hostDump);
3412                             dbAddr = hostDump.nameHashChain;
3413                         }
3414                         break;
3415
3416                       case HT_tapeName_FUNCTION:
3417                         {
3418                             struct tape hostTape, diskTape;
3419
3420                             eval = cdbread (ut, tape_BLOCK, dbAddr, &diskTape, sizeof(diskTape));
3421                             if (eval) ERROR(eval);
3422
3423                             fprintf(dumpfid, 
3424                                     "\ntapename hash %d, entry at %u: block %d, index %d\n",
3425                                     hash, dbAddr, block, index);
3426                             fprintf(dumpfid, "----------------------------\n");
3427                             tape_ntoh(&diskTape, &hostTape);
3428                             printTape(dumpfid, &hostTape);
3429                             dbAddr = hostTape.nameHashChain;
3430                         }
3431                         break;
3432
3433                       case HT_volName_FUNCTION:
3434                         {
3435                             struct volInfo hostVolInfo, diskVolInfo;
3436
3437                             eval = cdbread (ut, volInfo_BLOCK, dbAddr, &diskVolInfo, sizeof(diskVolInfo));
3438                             if (eval) ERROR(eval);
3439
3440                             fprintf(dumpfid, 
3441                                     "\nvolname hash %d, entry at %u: block %d, index %d\n",
3442                                     hash, dbAddr, block, index);
3443                             fprintf(dumpfid, "----------------------------\n");
3444                             volInfo_ntoh(&diskVolInfo, &hostVolInfo);
3445                             printVolInfo(dumpfid, &hostVolInfo);
3446                             dbAddr = hostVolInfo.nameHashChain;
3447
3448                             volFragsDump(ut, dumpfid,
3449                                          hostVolInfo.firstFragment);
3450                         }
3451                         break;
3452                         
3453                       default:
3454                         fprintf(dumpfid, "unknown type %d\n", type);
3455                         break;
3456                     
3457                     } /*s*/
3458                 } /*w*/
3459             } /*f*/
3460         } /*fo*/
3461     } /*ft*/
3462
3463   error_exit:
3464     code = ubik_EndTrans(ut);           /* is this safe if no ut started ?*/
3465     if (dumpfid) fclose(dumpfid);
3466     if (path)    free(path);
3467     return(code);
3468 }
3469
3470 volFragsDump(ut, dumpfid, dbAddr)
3471     struct ubik_trans *ut;
3472     FILE *dumpfid;
3473     dbadr dbAddr;
3474 {
3475     struct volFragment hostVolFragment, diskVolFragment;
3476     afs_int32 code;
3477     int block, index;
3478     
3479     while (dbAddr)
3480     {
3481         code = cdbread (ut, volFragment_BLOCK, dbAddr, &diskVolFragment, sizeof(diskVolFragment));
3482         if (code) {                                 /* don't be fussy about errors */
3483             fprintf(dumpfid, "volFragsDump: Error reading database\n");
3484             return(0);
3485         }
3486         
3487         fprintf(dumpfid, "\nvolfragment entry at %u: block %d, index %d\n",
3488                 dbAddr, block, index);
3489         fprintf(dumpfid, "----------------------------\n");
3490         volFragment_ntoh(&diskVolFragment, &hostVolFragment);
3491         printVolFragment(dumpfid, &hostVolFragment);
3492         dbAddr = hostVolFragment.sameNameChain;
3493     }
3494     return(0);
3495 }
3496
3497 #ifdef notdef
3498 /* utilities - network to host conversion
3499  *      currently used for debug only
3500  */
3501
3502 volFragmentDiskToHost(diskVfPtr, hostVfPtr)
3503      struct volFragment *diskVfPtr, *hostVfPtr;
3504 {
3505     hostVfPtr->vol = ntohl(diskVfPtr->vol);
3506     hostVfPtr->sameNameChain = ntohl(diskVfPtr->sameNameChain);
3507     hostVfPtr->tape = ntohl(diskVfPtr->tape);
3508     hostVfPtr->sameTapeChain = ntohl(diskVfPtr->sameTapeChain);
3509     hostVfPtr->position = ntohl(diskVfPtr->position);
3510     hostVfPtr->clone = ntohl(diskVfPtr->clone);
3511     hostVfPtr->incTime = ntohl(diskVfPtr->incTime);
3512     hostVfPtr->startByte = ntohl(diskVfPtr->startByte);
3513     hostVfPtr->nBytes = ntohl(diskVfPtr->nBytes);
3514     hostVfPtr->flags = ntohs(diskVfPtr->flags);
3515     hostVfPtr->sequence = ntohs(diskVfPtr->sequence);
3516 }
3517
3518 volInfoDiskToHost(diskViPtr, hostViPtr)
3519      struct volInfo *diskViPtr, *hostViPtr;
3520 {
3521     strcpy(hostViPtr->name, diskViPtr->name);
3522     hostViPtr->nameHashChain = ntohl(diskViPtr->nameHashChain);
3523     hostViPtr->id = ntohl(diskViPtr->id);
3524     strcpy(hostViPtr->server, diskViPtr->server);
3525     hostViPtr->partition = ntohl(diskViPtr->partition);
3526     hostViPtr->flags = ntohl(diskViPtr->flags);
3527     hostViPtr->sameNameHead = ntohl(diskViPtr->sameNameHead);
3528     hostViPtr->sameNameChain = ntohl(diskViPtr->sameNameChain);
3529     hostViPtr->firstFragment = ntohl(diskViPtr->firstFragment);
3530     hostViPtr->nFrags = ntohl(diskViPtr->nFrags);
3531 }
3532
3533 tapeDiskToHost(diskTapePtr, hostTapePtr)
3534      struct tape *diskTapePtr, *hostTapePtr;
3535 {
3536     strcpy(hostTapePtr->name, diskTapePtr->name);
3537     hostTapePtr->nameHashChain = ntohl(diskTapePtr->nameHashChain);
3538     hostTapePtr->flags = ntohl(diskTapePtr->flags);
3539
3540     /* tape id conversion here */
3541     hostTapePtr->written = ntohl(diskTapePtr->written);
3542     hostTapePtr->nBytes = ntohl(diskTapePtr->nBytes);
3543     hostTapePtr->nFiles = ntohl(diskTapePtr->nFiles);
3544     hostTapePtr->nVolumes = ntohl(diskTapePtr->nVolumes);
3545     hostTapePtr->seq = ntohl(diskTapePtr->seq);
3546     hostTapePtr->dump = ntohl(diskTapePtr->dump);
3547     hostTapePtr->nextTape = ntohl(diskTapePtr->nextTape);
3548     hostTapePtr->firstVol = ntohl(diskTapePtr->firstVol);
3549     hostTapePtr->useCount = ntohl(diskTapePtr->useCount);
3550 }
3551
3552 dumpDiskToHost(diskDumpPtr, hostDumpPtr)
3553      struct dump *diskDumpPtr, *hostDumpPtr;
3554 {
3555     hostDumpPtr->id = ntohl(diskDumpPtr->id);    
3556     hostDumpPtr->idHashChain = ntohl(diskDumpPtr->idHashChain);
3557     strcpy(hostDumpPtr->dumpName, diskDumpPtr->dumpName);
3558     strcpy(hostDumpPtr->dumpPath, diskDumpPtr->dumpPath);
3559     strcpy(hostDumpPtr->volumeSet, diskDumpPtr->volumeSet);
3560     hostDumpPtr->nameHashChain = ntohl(diskDumpPtr->nameHashChain);
3561     hostDumpPtr->flags = ntohl(diskDumpPtr->flags);
3562     hostDumpPtr->parent = ntohl(diskDumpPtr->parent);
3563     hostDumpPtr->created = ntohl(diskDumpPtr->created);
3564 /*  hostDumpPtr->incTime = ntohl(diskDumpPtr->incTime); */
3565     hostDumpPtr->nVolumes = ntohl(diskDumpPtr->nVolumes);
3566
3567     /* tapeset conversion here */
3568
3569     hostDumpPtr->firstTape = ntohl(diskDumpPtr->firstTape);
3570
3571     /* principal conversion here */
3572 }
3573
3574 #endif /* notdef */
3575
3576 checkHash(ut,hashType)
3577      struct ubik_trans *ut;
3578      int hashType;
3579 {
3580     struct memoryHashTable *mhtPtr;
3581     int entrySize, hashTableLength;
3582     int bucket;
3583     int old;
3584     afs_int32 code;
3585
3586     mhtPtr = ht_GetType(hashType, &entrySize);
3587     if ( mhtPtr == 0 )
3588         ERROR(-1);
3589
3590     for ( old = 0; old < 1; old++)
3591     {
3592         LogDebug(5, "\nold = %d\n", old);
3593         printMemoryHashTable(stdout, mhtPtr);
3594         LogDebug(5, "\n");
3595         hashTableLength = ( old ? mhtPtr->oldLength : mhtPtr->length);
3596
3597         for ( bucket = 0; bucket < hashTableLength; bucket++ )
3598         {
3599             dbadr entryAddr;
3600
3601             entryAddr = ht_LookupBucket (ut, mhtPtr, bucket, old);
3602             while (entryAddr != 0) 
3603             {
3604                 LogDebug(6, "bucket %d has disk addr %d\n", bucket, entryAddr);
3605                 switch (hashType)
3606                 {
3607                   case HT_dumpIden_FUNCTION:
3608                   {
3609                       struct dump diskDump, hostDump;
3610
3611                       code = dbread(ut, entryAddr, &diskDump, entrySize);
3612                       if (code) ERROR(-1);
3613                       
3614                       dump_ntoh(&diskDump, &hostDump);
3615                       printDump(stdout, &hostDump);
3616                       entryAddr = hostDump.idHashChain;
3617                   }
3618                   break;
3619
3620                   case HT_dumpName_FUNCTION:
3621                     break;
3622
3623                   case HT_tapeName_FUNCTION:
3624                     break;
3625
3626                   case HT_volName_FUNCTION:
3627                   {
3628                       struct volInfo diskVolInfo, hostVolInfo;
3629
3630                       code = dbread(ut, entryAddr, &diskVolInfo, entrySize);
3631                       if (code) ERROR(-1);
3632
3633                       volInfo_ntoh(&diskVolInfo, &hostVolInfo);
3634                       printVolInfo(stdout, &hostVolInfo);
3635                       entryAddr = hostVolInfo.nameHashChain;
3636                     break;
3637                   }
3638                 }
3639             }
3640         }
3641     }
3642   error_exit:
3643     return(code);
3644 }