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