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