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