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