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