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