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