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