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