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