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