pull-prototypes-to-head-20020821
[openafs.git] / src / bucoord / ubik_db_if.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 /* Interface and supporting routines for the backup system's ubik database */
11
12 #include <afsconfig.h>
13 #include <afs/param.h>
14
15 RCSID("$Header$");
16
17 #include <stdio.h>
18 #include <sys/types.h>
19 #include <fcntl.h>
20 #ifdef AFS_NT40_ENV
21 #include <winsock2.h>
22 #elif defined(AFS_SUN5_ENV)
23 #include <netdb.h>
24 #else
25 #include <sys/param.h>                  /* for hostnames etc */
26 #endif
27 #include <afs/auth.h>
28 #include <afs/cellconfig.h>
29 #include <ubik.h>
30 #include <afs/volser.h>
31 #include <afs/afsutil.h>
32 #include <afs/bubasics.h>
33 #include <afs/budb_client.h>
34 #include <afs/budb.h>
35 #include <errno.h>
36
37 #include "bc.h"
38 #include "error_macros.h"
39
40 extern char *whoami;
41
42 /* -------------------------------------
43  * Globals 
44  * -------------------------------------
45  */
46
47 struct udbHandleS udbHandle;
48
49 /* -------------------------------------
50  * interface routines (alphabetic)
51  * -------------------------------------
52  */
53
54 bcdb_AddVolume(veptr)
55      register struct budb_volumeEntry *veptr;
56 {
57     afs_int32 code;
58
59     code = ubik_Call( BUDB_AddVolume,  udbHandle.uh_client, 0, veptr);
60     return(code);
61 }
62
63 bcdb_AddVolumes(veptr, count)
64   register struct budb_volumeEntry *veptr;
65   afs_int32 count;
66 {
67   struct budb_volumeList volumeList;
68   afs_int32 code;
69
70   volumeList.budb_volumeList_len = count;
71   volumeList.budb_volumeList_val = veptr;
72   code = ubik_Call(BUDB_AddVolumes,  udbHandle.uh_client, 0, &volumeList);
73   return(code);
74 }
75
76
77 bcdb_CreateDump(deptr)
78      register struct budb_dumpEntry *deptr;
79 {
80     afs_int32 code;
81
82     code = ubik_Call(BUDB_CreateDump, udbHandle.uh_client, 0, deptr);
83     return(code);    
84 }
85
86 bcdb_deleteDump(dumpID, fromTime, toTime, dumps)
87     afs_int32 dumpID;
88     afs_int32 fromTime;
89     afs_int32 toTime;
90     budb_dumpsList *dumps;
91 {
92     afs_int32 code;
93     budb_dumpsList dumpsList, *dumpsPtr;
94
95     dumpsList.budb_dumpsList_len = 0;
96     dumpsList.budb_dumpsList_val = 0;
97     dumpsPtr = ( dumps ? dumps : &dumpsList );
98
99     code = ubik_Call(BUDB_DeleteDump, udbHandle.uh_client, 0, dumpID, fromTime, toTime, dumpsPtr);
100     if (dumpsList.budb_dumpsList_val) free(dumpsList.budb_dumpsList_val);
101     return(code);    
102 }
103
104 bcdb_listDumps(sflags, groupId, fromTime, toTime, dumps, flags)
105     afs_int32 groupId;
106     afs_int32 fromTime;
107     afs_int32 toTime;
108     budb_dumpsList *dumps;
109     budb_dumpsList *flags;
110 {
111     afs_int32 code, sflag=0;
112     budb_dumpsList dumpsList, *dumpsPtr;
113     budb_dumpsList flagsList, *flagsPtr;
114
115     dumpsList.budb_dumpsList_len = 0;
116     dumpsList.budb_dumpsList_val = 0;
117     dumpsPtr = (dumps ? dumps : &dumpsList);
118
119     flagsList.budb_dumpsList_len = 0;
120     flagsList.budb_dumpsList_val = 0;
121     flagsPtr = (flags ? flags : &flagsList);
122
123     code = ubik_Call(BUDB_ListDumps, udbHandle.uh_client, 0, 
124                      sflags, "", groupId, fromTime, toTime, dumpsPtr, flagsPtr);
125
126     if (dumpsList.budb_dumpsList_val) free(dumpsList.budb_dumpsList_val);
127     if (flagsList.budb_dumpsList_val) free(flagsList.budb_dumpsList_val);
128     return(code);    
129 }
130
131
132 bcdb_DeleteVDP(dumpSetName, dumpPath, dumpID)
133      char *dumpSetName;
134      char *dumpPath;
135      afs_int32 dumpID;
136 {
137     afs_int32 code;
138
139     code = ubik_Call( BUDB_DeleteVDP,  udbHandle.uh_client, 0, dumpSetName, dumpPath, dumpID);
140     return(code);    
141 }
142
143 /* bcdb_FindClone
144  *      Returns the clone time of a volume by going up the parent chain.
145  *      If no clone time is found, a clone time of 0 is returned, forcing
146  *      a full dump.
147  * entry:
148  *      dumpID - of the first dump to examine.
149  *      volName - name of the volume for whom a clone time is required
150  *      clonetime - ptr to vbl for returning result
151  * exit:
152  *      0 - clonetime set appropriately
153  *      -1 - error occured in traversing chain, clone time set to 0.
154  *      -2 - no clone times found, clone time set to 0
155  */
156
157 afs_int32
158 bcdb_FindClone(dumpID, volName, clonetime)
159      afs_int32 dumpID;
160      char *volName;
161      afs_int32 *clonetime;
162 {
163     afs_int32 code;
164     code = ubik_Call( BUDB_FindClone,  udbHandle.uh_client, 0, dumpID, volName, clonetime);
165     return(code);    
166 }
167
168 /* bcdb_FindDump
169  *      scan entire database for latest volume dump before adate.  Optimize
170  *      further by reading only the first line of the dump and if it is older
171  *      than the oldest acceptable dump we've found so far, we don't bother
172  *      scanning the dump file we've just opened
173  *
174  *      Used by restore code when restoring a user requested volume(s)
175  * entry:
176  *      volumeName - name of volume to match on
177  *      beforeDate - look for dumps older than this date
178  * exit:
179  *      deptr - desciptor of most recent dump
180  * notes:
181  *      should be able to implement this in a single call rather than
182  *      the current multiple bcdb_ call algorithm.
183  */
184
185 bcdb_FindDump(volumeName, beforeDate, deptr)
186      char *volumeName;
187      afs_int32 beforeDate;
188      struct budb_dumpEntry *deptr;
189 {
190     afs_int32 code;
191     code = ubik_Call( BUDB_FindDump,  udbHandle.uh_client, 0, volumeName, beforeDate, deptr);
192     return(code);    
193 }
194
195 /* bcdb_FindDumpByID
196  *      find a dump by id. Currently insists on a single return value.
197  * entry:
198  *      dumpID - id to lookup
199  * exit:
200  */
201 bcdb_FindDumpByID(dumpID, deptr)
202      afs_int32 dumpID;
203      register struct budb_dumpEntry *deptr;
204 {
205     register afs_int32 code;
206     afs_int32   nextindex;
207     afs_int32   dbTime;
208     budb_dumpList dl;
209
210     /* initialize the dump list */
211     dl.budb_dumpList_len = 0;
212     dl.budb_dumpList_val = 0;
213
214     /* outline algorithm */
215     code = ubik_Call (BUDB_GetDumps,  udbHandle.uh_client, 0,
216                       BUDB_MAJORVERSION,
217                       BUDB_OP_DUMPID,
218                       "",                       /* no name */
219                       dumpID,                   /* start */
220                       0,                        /* end */
221                       0,                        /* index */
222                       &nextindex,
223                       &dbTime,
224                       &dl);
225
226     if ( (code != 0)
227     ||   (dl.budb_dumpList_len != 1)            /* single retn val expected */
228        )
229     {
230 /*      printf("bcdb_FindDumpByID: code %d, nvalues %d\n",
231                code, dl.budb_dumpList_len); */
232         if ( code == 0 )
233                 code = 1;                       /* multiple id's */
234         goto error;
235     }
236
237     memcpy(deptr, dl.budb_dumpList_val, sizeof(*deptr));
238
239 exit:
240     if ( dl.budb_dumpList_val )
241     {
242         /* free any allocated structures */
243         free(dl.budb_dumpList_val);
244     }
245     return(code);
246
247 error:
248     memset(deptr, 0, sizeof(*deptr));
249     goto exit;
250 }
251
252 /* bcdb_FindLastVolClone
253  *      Returns the clone time, from the most recent dump of volName, when
254  *      dumped in the volume set volSetName, with dump schedule dumpName.
255  *      The clone time can be used to check if the volume has been correctly
256  *      re-cloned, and also is used as the time from which to do the current
257  *      incremental dump.
258  * entry:
259  *      volSetName - name of volume set
260  *      dumpName - full path of dump node
261  *      volName - name of volume for whom a clone time is required
262  *      clonetime - ptr to vbl for result
263  * exit:
264  *      0 - clonetime set appropriately
265  * notes:
266  *      used only for warning generation. Suggest that this be omitted.
267  */
268
269 afs_int32
270 bcdb_FindLastVolClone(volSetName, dumpName, volName, clonetime)
271      char *volSetName;
272      char *dumpName;
273      char *volName;
274      afs_int32 *clonetime;
275 {
276     /* server notes
277        search by dumpName
278        match on volumeset and dump path
279        search for the volume name
280      */
281     return(0);
282 }
283
284 /* bcdb_FindLatestDump
285  *      find the latest dump with volume set component avname and the
286  *      specified dump pathname. Used to find a dump, relative to which an
287  *      incremental dump can be done. Defines the parent <-> child relations
288  *      for restores.
289  * entry:
290  *      avname: volume set name
291  *      dumpPath: full path of dump node
292  * exit:
293  *      0:  adentry: dump entry structure filled in.
294  *      -1: probably an internal error
295  *      2: no such entry
296  * Notes for 4.0:
297  *      Need to store volumeset name in dump in order to implement this.
298  *      Need new routine since params are two strings
299  */
300
301 bcdb_FindLatestDump(volSetName, dumpPath, deptr)
302      char *volSetName;
303      char *dumpPath;
304      struct budb_dumpEntry *deptr;
305 {
306     afs_int32 code;
307     code = ubik_Call( BUDB_FindLatestDump,  udbHandle.uh_client, 0,
308                       volSetName, dumpPath, deptr);
309     return(code);
310 }
311
312
313 /* bcdb_FindTape
314  *      find a tape
315  * entry:
316  *      dumpid: dump id to which tape beint32s
317  *      tapeName: name of tape
318  */
319
320 bcdb_FindTape(dumpid, tapeName, teptr)
321      afs_int32 dumpid;
322      char *tapeName;
323      struct budb_tapeEntry *teptr;
324 {
325     budb_tapeList tl;
326     afs_int32 next;
327     afs_int32 dbTime;
328     afs_int32 code = 0;
329
330     memset(teptr, 0, sizeof(*teptr));
331     tl.budb_tapeList_len = 0;
332     tl.budb_tapeList_val = 0;
333
334     code = ubik_Call( BUDB_GetTapes, udbHandle.uh_client, 0,
335                       BUDB_MAJORVERSION,
336                       BUDB_OP_TAPENAME|BUDB_OP_DUMPID,
337                       tapeName, dumpid, 0, 0, &next, &dbTime, &tl);
338
339     if (code) ERROR(code);
340
341     if (tl.budb_tapeList_len != 1)
342         ERROR(BC_NOTUNIQUE);    /* expecting a single descriptor */
343
344     memcpy(teptr, tl.budb_tapeList_val, sizeof(*teptr));
345
346 error_exit:                  
347     if ( tl.budb_tapeList_val )
348         free(tl.budb_tapeList_val);
349     return(code);
350 }
351
352 bcdb_FindTapeSeq(dumpid, tapeSeq, teptr)
353      afs_int32 dumpid;
354      afs_int32 tapeSeq;
355      struct budb_tapeEntry *teptr;
356 {
357     budb_tapeList tl;
358     afs_int32 next;
359     afs_int32 dbTime;
360     afs_int32 code = 0;
361
362     memset(teptr, 0, sizeof(*teptr));
363     tl.budb_tapeList_len = 0;
364     tl.budb_tapeList_val = 0;
365
366     code = ubik_Call( BUDB_GetTapes, udbHandle.uh_client, 0,
367                       BUDB_MAJORVERSION,
368                       BUDB_OP_TAPESEQ|BUDB_OP_DUMPID,
369                       "", dumpid, tapeSeq, 0, &next, &dbTime, &tl);
370     if (code) ERROR(code);
371
372     if (tl.budb_tapeList_len != 1)
373         ERROR(BC_NOTUNIQUE);    /* expecting a single descriptor */
374
375     memcpy(teptr, tl.budb_tapeList_val, sizeof(*teptr));
376
377 error_exit:                  
378     if ( tl.budb_tapeList_val )
379         free(tl.budb_tapeList_val);
380     return(code);
381 }
382
383 /* bcdb_FindVolumes
384  * notes: 
385  *      - this is part of dblookup. The existing semantics will not work since
386  *      they do lookups based on dump id.
387  *      - in the restore code, it uses this to extract information about
388  *      the volume. Need current semantics. Could filter the output, selecting
389  *      on the dumpid.
390  *      - Suggest that the lookup be based on volume name only, with optional
391  *      match on backup, and readonly volumes.
392  *      - Further, need to check if the volume structure returns enough
393  *      information
394  */
395
396 afs_int32
397 bcdb_FindVolumes(dumpID, volumeName, returnArray, last, next, maxa, nEntries)
398      afs_int32 dumpID;
399      char *volumeName;
400      struct budb_volumeEntry *returnArray;
401      afs_int32 last;
402      afs_int32 *next;
403      afs_int32 maxa;
404      afs_int32 *nEntries;
405 {
406     budb_volumeList vl;
407     afs_int32 dbTime;
408     afs_int32 code;
409
410     vl.budb_volumeList_len = maxa;
411     vl.budb_volumeList_val = returnArray;
412
413     /* outline algorithm */
414     code = ubik_Call (BUDB_GetVolumes,  udbHandle.uh_client, 0,
415                       BUDB_MAJORVERSION,
416                       BUDB_OP_VOLUMENAME|BUDB_OP_DUMPID,
417                       volumeName,               /* name */
418                       dumpID,                   /* start */
419                       0,                        /* end */
420                       last,                     /* index */
421                       next,                     /* nextindex */
422                       &dbTime,
423                       &vl);
424
425     *nEntries  = vl.budb_volumeList_len;
426     return(code);
427 }
428
429
430 bcdb_FinishDump(deptr)
431      register struct budb_dumpEntry *deptr;
432 {
433     afs_int32 code;     
434     code = ubik_Call (BUDB_FinishDump,  udbHandle.uh_client, 0, deptr);
435     return(code);    
436 }
437
438 bcdb_FinishTape(teptr)
439      register struct budb_tapeEntry *teptr;
440 {
441     afs_int32 code;     
442     code = ubik_Call (BUDB_FinishTape,  udbHandle.uh_client, 0, teptr);
443     return(code);    
444     
445 }
446
447 /* bcdb_LookupVolumes
448  */
449
450 afs_int32
451 bcdb_LookupVolume(volumeName, returnArray, last, next, maxa, nEntries)
452      char *volumeName;
453      struct budb_volumeEntry *returnArray;
454      afs_int32 last;
455      afs_int32 *next;
456      afs_int32 maxa;
457      afs_int32 *nEntries;
458 {
459     budb_volumeList vl;
460     afs_int32 dbTime;
461     afs_int32 code;
462
463     vl.budb_volumeList_len = maxa;
464     vl.budb_volumeList_val = returnArray;
465
466     /* outline algorithm */
467     code = ubik_Call (BUDB_GetVolumes,  udbHandle.uh_client, 0,
468                       BUDB_MAJORVERSION,
469                       BUDB_OP_VOLUMENAME,
470                       volumeName,               /* name */
471                       0,                        /* start */
472                       0,                        /* end */
473                       last,                     /* index */
474                       next,                     /* nextindex */
475                       &dbTime,
476                       &vl);
477     if ( code )
478     {
479         *nEntries = 0;
480         return(code);
481     }
482     *nEntries = vl.budb_volumeList_len;
483     return(0);
484 }
485
486
487 bcdb_UseTape(teptr, newFlag)
488      register struct budb_tapeEntry *teptr;
489      afs_int32 *newFlag;
490 {
491     afs_int32 code;     
492     code = ubik_Call (BUDB_UseTape,  udbHandle.uh_client, 0, teptr, newFlag);
493     return(code);    
494 }
495
496
497 /* ---- text configuration handling routines ----
498  * 
499  * notes: 
500  *      The caller should pass in/out a fid for an unlinked, open file to prevent
501  *      tampering with the files contents; 
502  */
503  
504 /* bcdb_GetTextFile
505  *      extract the specified textType and put it in a temporary, local
506  *      file.
507  * entry:
508  *      ctPtr - ptr to client structure with all the required information
509  */
510
511 bcdb_GetTextFile(register udbClientTextP ctPtr)
512 {
513     afs_int32 bufferSize;
514     afs_int32 offset, nextOffset, chunkSize; 
515     charListT charList;
516     afs_int32 code = 0;
517
518     /* Initialize charlistT_val. We try to deallocate this structure based on
519      * this */
520     memset((void *)&charList, 0, sizeof(charList));
521
522     /* check params and cleanup any previous state */
523     if ( ctPtr->lockHandle == 0 )
524         ERROR(BUDB_INTERNALERROR);
525
526     if ( ctPtr->textStream == NULL ) /* Should have an open stream */
527         ERROR(BUDB_INTERNALERROR);
528
529     /* allocate a buffer */
530     bufferSize = 1024;
531     charList.charListT_val = (char *) malloc(bufferSize);
532     if ( charList.charListT_val == 0 )
533         ERROR(BUDB_INTERNALERROR);
534     charList.charListT_len = bufferSize;
535
536     offset = 0;
537     nextOffset = 0;
538     ctPtr->textSize = 0;
539     while ( nextOffset != -1 )
540     {
541         offset = nextOffset;
542         charList.charListT_len = bufferSize;
543         code = ubik_Call(BUDB_GetText,  udbHandle.uh_client, 0,
544                          ctPtr->lockHandle, ctPtr->textType, bufferSize,
545                          offset, &nextOffset, &charList);
546
547         if ( code )
548                 ERROR(code);
549
550         code = fwrite(charList.charListT_val, sizeof(char), 
551                       charList.charListT_len, ctPtr->textStream);
552         if ( ferror(ctPtr->textStream) )
553                 ERROR(BUDB_INTERNALERROR);
554
555         ctPtr->textSize += charList.charListT_len;
556     }
557
558     /* get text version */
559     code = ubik_Call(BUDB_GetTextVersion, udbHandle.uh_client, 0,
560                      ctPtr->textType, &ctPtr->textVersion);
561     if ( code )
562         ERROR(code);
563
564 normal_exit:
565     fflush(ctPtr->textStream);                          /* debug */
566
567     /* exit, leaving the configuration text file open */
568     if ( charList.charListT_val )
569         free(charList.charListT_val);
570     return(code);
571
572 error_exit:
573     if ( ctPtr->textStream != NULL )
574     {
575         fclose(ctPtr->textStream);
576         ctPtr->textStream = NULL;
577     }
578     goto normal_exit;
579 }
580     
581
582 /* bcdb_SaveTextFile
583  *      save the text file in ubik database
584  * entry:
585  *      textType - identifies type of configuration file
586  *      filename - where to get the text from
587  */
588
589 bcdb_SaveTextFile(ctPtr)
590      udbClientTextP ctPtr;
591 {
592     afs_int32 bufferSize;
593     afs_int32 offset, chunkSize, fileSize;
594     charListT charList;
595     afs_int32 code = 0;
596     afs_int32 filesize();
597
598     /* allocate a buffer */
599     bufferSize = 1024;
600     charList.charListT_val = (char *) malloc(bufferSize);
601     if ( charList.charListT_val == 0 )
602         ERROR(BUDB_INTERNALERROR);
603     charList.charListT_len = bufferSize;
604
605     if ( ctPtr->textStream == NULL )
606         ERROR(BUDB_INTERNALERROR);
607     rewind(ctPtr->textStream); 
608
609     fileSize = (afs_int32) filesize(ctPtr->textStream);
610
611     dprintf(("filesize is %d\n", fileSize));
612
613     rewind(ctPtr->textStream);
614
615     /* special case empty files */
616     if ( fileSize == 0 )
617     {
618         charList.charListT_len = 0;     
619         code = ubik_Call(BUDB_SaveText,  udbHandle.uh_client, 0,
620                          ctPtr->lockHandle, ctPtr->textType, 0,
621                          BUDB_TEXT_COMPLETE, &charList);
622         goto error_exit;
623     }
624
625     offset = 0;
626     while ( fileSize != 0 )
627     {
628         chunkSize = MIN(fileSize, bufferSize);
629         code = fread(charList.charListT_val, sizeof(char), chunkSize,
630                      ctPtr->textStream);
631
632         if ( code != chunkSize )
633                 printf("code = %d\n", code );
634         if ( ferror(ctPtr->textStream ) )
635                 ERROR(BUDB_INTERNALERROR);
636
637         charList.charListT_len = chunkSize;
638         code = ubik_Call(BUDB_SaveText,  udbHandle.uh_client, 0,
639                          ctPtr->lockHandle, ctPtr->textType, offset, 
640                          (chunkSize == fileSize) ? BUDB_TEXT_COMPLETE: 0,
641                          &charList);
642         if ( code )
643                 ERROR(code);
644
645         fileSize -= chunkSize;
646         offset += chunkSize;
647     }
648     
649 error_exit:
650     /* if ( ctPtr->textStream >= 0 )
651         close(ctPtr->textStream); */
652     if ( charList.charListT_val )
653         free(charList.charListT_val);
654     return(code);
655 }
656
657 bcdb_FindLastTape(dumpID, dumpEntry, tapeEntry, volEntry)
658     afs_int32                    dumpID;
659     struct budb_dumpEntry   *dumpEntry;
660     struct budb_tapeEntry   *tapeEntry;
661     struct budb_volumeEntry *volEntry;
662 {
663     return( ubik_Call(BUDB_FindLastTape,udbHandle.uh_client,0,
664                       dumpID,dumpEntry,tapeEntry,volEntry) );
665 }
666
667 bcdb_MakeDumpAppended(appendedDumpID, initialDumpID, startTapeSeq)
668     afs_int32 appendedDumpID;
669     afs_int32 initialDumpID;
670     afs_int32 startTapeSeq;
671 {
672     return( ubik_Call(BUDB_MakeDumpAppended,udbHandle.uh_client,0,
673                       appendedDumpID,initialDumpID,startTapeSeq) );
674 }
675
676
677 /* -------------------------------------
678  * misc. support routines
679  * -------------------------------------
680  */
681
682 afs_int32
683 filesize(stream)
684      FILE *stream;
685 {
686     afs_int32 offset;
687     afs_int32 size;
688
689     offset = ftell(stream);
690     fseek(stream, (afs_int32) 0, 2);            /* end of file */
691     size = ftell(stream);
692     fseek(stream, offset, 0);
693     return(size);
694 }
695
696
697 /* ------------------------------------
698  * misc. support routines - general text management
699  * ------------------------------------
700  */
701
702
703 /* bc_LockText
704  *      locks the text described by the ctPtr
705  * entry:
706  *      ctptr - client text ptr
707  * exit:
708  *      0 - success
709  *      n - fail
710  */
711
712 bc_LockText(ctPtr)
713      register udbClientTextP ctPtr;
714 {
715     afs_int32 code;
716     afs_int32 timeout, j = 0;
717
718     if (ctPtr->lockHandle != 0) return(1);      /* already locked */
719
720     timeout = ( (ctPtr->textSize == 0) ? 30 : ((ctPtr->textSize/50000) + 10) );
721
722     while (1) {
723         code = ubik_Call(BUDB_GetLock,  udbHandle.uh_client, 0,
724                          udbHandle.uh_instanceId, ctPtr->textType, timeout,
725                          &ctPtr->lockHandle);
726         if ( (code != BUDB_LOCKED) && (code != BUDB_SELFLOCKED) ) {
727             break;
728         }
729
730         /* Mention something every 30 seconds */
731         if (++j >= 30) {
732             com_err(whoami, code,"; Waiting for db configuration text unlock");
733             j = 0;
734         }
735 #ifdef AFS_PTHREAD_ENV
736         sleep(1);
737 #else
738         IOMGR_Sleep(1);
739 #endif
740     }
741
742     /* cleanup */
743     if (code) ctPtr->lockHandle = 0;
744     return(code);
745 }
746
747 /* bc_UnlockText
748  *      unlocks the text described by the ctPtr
749  * entry:
750  *      ctptr - client text ptr
751  * exit:
752  *      0 - success
753  *      n - fail
754  */
755
756 bc_UnlockText(ctPtr)
757      register udbClientTextP ctPtr;
758 {
759     afs_int32 code = 0;
760
761     if ( ctPtr->lockHandle == 0 )
762         return(0);
763
764     code = ubik_Call(BUDB_FreeLock, udbHandle.uh_client, 0, ctPtr->lockHandle);
765     ctPtr->lockHandle = 0;
766     
767     /* Don't try to analyse the error. Let the lock timeout */
768     return(code);
769 }
770
771 /* bc_CheckTextVersion
772  * exit:
773  *      0 - version # ok
774  *      n - out of date or error
775  */
776
777 bc_CheckTextVersion(ctPtr)
778      register udbClientTextP ctPtr;
779 {
780     afs_int32 code;
781     afs_uint32 tversion;
782
783     if ( ctPtr->textVersion == -1 )
784         return(BC_VERSIONMISMATCH);
785
786     code = ubik_Call(BUDB_GetTextVersion,  udbHandle.uh_client, 0, 
787                      ctPtr->textType, &tversion);
788     if ( code )
789         return(code);
790     if ( tversion != ctPtr->textVersion )
791         return(BC_VERSIONMISMATCH);
792     return(0);
793 }
794      
795 /* -------------------------------------
796  * initialization routines
797  * -------------------------------------
798  */
799
800 /* vldbClientInit 
801  *      Initialize a client for the vl ubik database.
802  */
803 vldbClientInit(noAuthFlag, localauth, cellName, cstruct, ttoken)
804      int                noAuthFlag;
805      int                localauth;
806      char               *cellName;
807      struct ubik_client **cstruct;
808      struct ktc_token   *ttoken;
809 {
810     afs_int32                   code = 0;
811     struct afsconf_dir      *acdir;
812     struct rc_securityClass *sc;
813     afs_int32                   i, scIndex = 0;     /* Index of Rx security object - noauth */
814     struct afsconf_cell     info;
815     struct ktc_principal    sname;
816     struct rx_connection    *serverconns[VLDB_MAXSERVERS];
817
818
819     /* Find out about the given cell */
820     acdir = afsconf_Open((localauth ? AFSDIR_SERVER_ETC_DIRPATH  :
821                           AFSDIR_CLIENT_ETC_DIRPATH));
822     if (!acdir) 
823     {
824         com_err(whoami,0,"Can't open configuration directory '%s'", 
825                 (localauth ? AFSDIR_SERVER_ETC_DIRPATH  : 
826                  AFSDIR_CLIENT_ETC_DIRPATH));
827         ERROR(BC_NOCELLCONFIG);
828     }
829
830     if (!cellName[0])
831     {
832         char cname[64];
833
834         code = afsconf_GetLocalCell(acdir, cname, sizeof(cname));
835         if (code) 
836         {
837             com_err(whoami, code, "; Can't get the local cell name - check %s/%s",  (localauth ? AFSDIR_SERVER_ETC_DIRPATH : AFSDIR_CLIENT_ETC_DIRPATH),
838                     AFSDIR_THISCELL_FILE);
839             ERROR(code);
840         }
841         strcpy(cellName, cname);
842     }
843
844     code = afsconf_GetCellInfo(acdir, cellName, AFSCONF_VLDBSERVICE, &info);
845     if (code) 
846     {
847       com_err(whoami, code, "; Can't find cell %s's hosts in %s/%s", 
848               cellName, (localauth ? AFSDIR_SERVER_ETC_DIRPATH  :
849                          AFSDIR_CLIENT_ETC_DIRPATH),
850               AFSDIR_CELLSERVDB_FILE);
851       ERROR(BC_NOCELLCONFIG);
852     }
853
854     /*
855      * Grab tickets if we care about authentication.
856      */
857     ttoken->endTime = 0;
858     if (localauth)
859     {
860         code = afsconf_GetLatestKey (acdir, 0, 0);
861         if (code)
862         {
863             com_err(whoami, code, "; Can't get key from local key file");
864             ERROR(code);
865         }
866         else
867         {
868             code = afsconf_ClientAuth(acdir, &sc, &scIndex);
869             if (code) 
870             {
871                 com_err(whoami, code, "; Calling ClientAuth");
872                 ERROR(code);
873             }
874
875             ttoken->endTime = NEVERDATE;
876         }
877     }
878     else
879     {
880         if (!noAuthFlag)
881         {
882             strcpy(sname.cell, info.name);
883             sname.instance[0] = 0;
884             strcpy(sname.name, "afs");
885
886             code = ktc_GetToken(&sname, ttoken, sizeof(struct ktc_token), NULL);
887             if (code) 
888             {
889                 com_err(whoami,code, 0,"; Can't get AFS tokens - running unauthenticated");
890             }
891             else 
892             {
893                 if ( (ttoken->kvno < 0) || (ttoken->kvno > 255) )
894                   com_err(whoami, 0, "Funny kvno (%d) in ticket, proceeding", ttoken->kvno);
895                 
896                 scIndex = 2;
897             }
898         }
899     
900         switch (scIndex) 
901         {
902             case 0:
903               sc = rxnull_NewClientSecurityObject();
904               break;
905             case 2:
906               sc = (struct rx_securityClass *) 
907                 rxkad_NewClientSecurityObject(rxkad_clear, &ttoken->sessionKey, 
908                                               ttoken->kvno, ttoken->ticketLen, 
909                                               ttoken->ticket);
910               break;
911             default:
912               com_err(whoami, 0, "Unsupported authentication type %d", scIndex);
913               ERROR(-1);
914               break;
915         }
916     }
917
918     if (!sc)
919     {
920         com_err(whoami, 0, "Can't create a security object with security index %d",
921                 scIndex);
922         ERROR(-1);
923     }
924
925     /* tell UV module about default authentication */
926     UV_SetSecurity(sc, scIndex);
927
928     if (info.numServers > VLDB_MAXSERVERS) 
929     {
930         com_err(whoami, 0, 
931                 "Warning: %d VLDB servers exist for cell '%s', can only remember the first %d", 
932                 info.numServers, cellName, VLDB_MAXSERVERS);
933         info.numServers = VLDB_MAXSERVERS;
934     }
935
936     for (i=0; i<info.numServers; i++) 
937         serverconns[i] = rx_NewConnection(info.hostAddr[i].sin_addr.s_addr, 
938                                           info.hostAddr[i].sin_port, 
939                                           USER_SERVICE_ID, sc, scIndex);
940     serverconns[i] = 0;
941
942     *cstruct = 0;
943     code = ubik_ClientInit(serverconns, cstruct);
944     if (code) 
945     {
946         com_err(whoami,code,"; Can't initialize ubik connection to vldb");
947         ERROR(code);
948     }
949
950 error_exit:
951     if (acdir) afsconf_Close(acdir);
952     return(code);
953 }
954
955 /* udbClientInit
956  *      initialize a client for the backup systems ubik database.
957  */
958
959 afs_int32
960 udbClientInit(noAuthFlag, localauth, cellName)
961      int  noAuthFlag;
962      int  localauth;
963      char *cellName;
964 {
965     afs_int32 serverList[MAXSERVERS];
966     char hostname[256];
967     struct ktc_principal principal;
968     struct ktc_token token;
969     struct afsconf_cell info;
970     struct afsconf_dir *acdir;
971     int i;
972     afs_int32  code = 0;
973     
974     acdir = afsconf_Open((localauth ? AFSDIR_SERVER_ETC_DIRPATH  :
975                           AFSDIR_CLIENT_ETC_DIRPATH));   
976     if (!acdir) 
977     {
978       com_err(whoami,0,"Can't open configuration directory '%s'", 
979               (localauth ? AFSDIR_SERVER_ETC_DIRPATH  :
980                AFSDIR_CLIENT_ETC_DIRPATH));
981       ERROR(BC_NOCELLCONFIG);
982     }
983     
984     if (!cellName[0])
985     {
986         char cname[64];
987
988         code = afsconf_GetLocalCell(acdir, cname, sizeof(cname));
989         if (code) 
990         {
991           com_err(whoami, code, "; Can't get the local cell name - check %s/%s",    (localauth ? AFSDIR_SERVER_ETC_DIRPATH : AFSDIR_CLIENT_ETC_DIRPATH),
992                   AFSDIR_THISCELL_FILE);
993           ERROR(code);
994         }
995         strcpy(cellName, cname);
996     }
997
998     code = afsconf_GetCellInfo(acdir, cellName, 0, &info);
999     if (code) 
1000     {
1001       com_err(whoami, code, "; Can't find cell %s's hosts in %s/%s", 
1002               cellName, (localauth ? AFSDIR_SERVER_ETC_DIRPATH :
1003                          AFSDIR_CLIENT_ETC_DIRPATH),
1004               AFSDIR_CELLSERVDB_FILE);
1005         ERROR(BC_NOCELLCONFIG);
1006     }
1007         
1008     udbHandle.uh_scIndex = RX_SCINDEX_NULL;
1009
1010     if (localauth)
1011     {
1012         code = afsconf_GetLatestKey (acdir, 0, 0);
1013         if (code)
1014         {
1015             com_err(whoami, code, "; Can't get key from local key file");
1016             ERROR(-1);
1017         }
1018         else
1019         {
1020             code = afsconf_ClientAuth(acdir, &udbHandle.uh_secobj, &udbHandle.uh_scIndex);
1021             if (code) 
1022             {
1023                 com_err(whoami, code, "; Calling ClientAuth");
1024                 ERROR(-1);
1025             }
1026         }
1027     }
1028     else
1029     {
1030         if (!noAuthFlag)
1031         {
1032             /* setup principal */
1033             strcpy(principal.cell, info.name);
1034             principal.instance[0] = 0;
1035             strcpy(principal.name, "afs");
1036         
1037             /* get token */
1038             code = ktc_GetToken(&principal, &token, sizeof(token), NULL);
1039             if (code)
1040             {
1041                 com_err(whoami, code, "; Can't get tokens - running unauthenticated");
1042             }
1043             else
1044             {
1045                 if ((token.kvno < 0) || (token.kvno > 255))
1046                     com_err(whoami, 0, "Unexpected kvno (%d) in ticket - proceeding",
1047                             token.kvno);
1048                 udbHandle.uh_scIndex = RX_SCINDEX_KAD;          /* Kerberos */
1049             }
1050         }
1051
1052         switch (udbHandle.uh_scIndex)
1053         {
1054             case 0:
1055               udbHandle.uh_secobj = rxnull_NewClientSecurityObject();
1056               break;
1057         
1058             case 2:
1059               udbHandle.uh_secobj = (struct rx_securityClass *)
1060                 rxkad_NewClientSecurityObject(rxkad_clear, &token.sessionKey,
1061                                               token.kvno, token.ticketLen,
1062                                               token.ticket);
1063               break;
1064
1065             default:
1066               com_err(whoami, 0, "Unsupported authentication type %d", udbHandle.uh_scIndex );
1067               ERROR(-1);
1068               break;
1069         }
1070     }
1071
1072     if (!udbHandle.uh_secobj)
1073     {   
1074         com_err(whoami, 0, "Can't create a security object with security index %d",
1075                 udbHandle.uh_secobj);
1076         ERROR(-1);
1077     }
1078
1079     if (info.numServers > MAXSERVERS)
1080     {
1081         com_err(whoami,0,
1082                 "Warning: %d BDB servers exist for cell '%s', can only remember the first %d", 
1083                 info.numServers, cellName, MAXSERVERS);
1084         info.numServers = MAXSERVERS;
1085     }
1086
1087     /* establish connections to the servers. Check for failed connections? */
1088     for (i = 0; i < info.numServers; i++) {
1089         udbHandle.uh_serverConn[i] = rx_NewConnection(info.hostAddr[i].sin_addr.s_addr,
1090                                                    htons(AFSCONF_BUDBPORT),
1091                                                    BUDB_SERVICE, 
1092                                                    udbHandle.uh_secobj, 
1093                                                    udbHandle.uh_scIndex);
1094     }
1095     udbHandle.uh_serverConn[i] = 0;
1096
1097     code = ubik_ClientInit(udbHandle.uh_serverConn, &udbHandle.uh_client);
1098     if (code) {
1099         com_err(whoami,code,"; Can't initialize ubik connection to backup database");
1100         ERROR(code);
1101     }
1102
1103     /* Try to quickly find a good site by setting deadtime low */
1104     for (i = 0; i < info.numServers; i++)
1105         rx_SetConnDeadTime(udbHandle.uh_client->conns[i], 1);
1106     code = ubik_Call(BUDB_GetInstanceId, udbHandle.uh_client, 0, &udbHandle.uh_instanceId);
1107
1108     /* Reset dead time back up to default */
1109     for (i = 0; i < info.numServers; i++)
1110        rx_SetConnDeadTime(udbHandle.uh_client->conns[i], 60);
1111
1112     /* If did not find a site on first quick pass, try again */
1113     if (code == -1)
1114        code = ubik_Call(BUDB_GetInstanceId, udbHandle.uh_client, 0, &udbHandle.uh_instanceId);
1115     if (code) {
1116         com_err(whoami,code,"; Can't access backup database");
1117         ERROR(code);
1118     }
1119
1120 error_exit:
1121     if (acdir) afsconf_Close(acdir);
1122     return(code);
1123 }
1124
1125 /* -------------------------------------
1126  * specialized ubik support
1127  * -------------------------------------
1128  */
1129
1130 #include <rx/xdr.h>
1131 #include <rx/rx.h>
1132 #include <lock.h>
1133
1134 /* notes
1135  *      1) first call with SINGLESERVER set, record the server to be used.
1136  *      2) subsequent calls use that server. If a failure is encountered,
1137  *         the state is cleaned up and the error returned back to the caller.
1138  *      3) upon completion, the user must make a dummy call with
1139  *         END_SINGLESERVER set, to clean up state.
1140  *      4) if the vanilla ubik_Call is ever modified, the END_SINGLESERVER
1141  *         flag can be discarded. The first call without SINGLESERVER set
1142  *         can clean up the state.
1143  */ 
1144
1145 struct ubikCallState
1146 {
1147     afs_int32 ucs_flags;                        /* state flags */
1148     afs_int32 ucs_selectedServer;               /* which server selected */
1149 };
1150
1151 static struct ubikCallState uServer;
1152
1153 /* ubik_Call_SingleServer
1154  *      variant of ubik_Call. This is used by the backup system to initiate
1155  *      a series of calls to a single ubik server. The first call sets up
1156  *      process state (not recorded in ubik) that requires all further calls
1157  *      of that group to be made to the same server process.
1158  *
1159  *      call this instead of stub and we'll guarantee to find a host that's up.
1160  *      in the future, we should also put in a protocol to find the sync site
1161  */
1162
1163 afs_int32
1164 ubik_Call_SingleServer(aproc, aclient, aflags, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, p16)
1165     register struct ubik_client *aclient;
1166     int (*aproc)();
1167     afs_int32 aflags;
1168     char *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8, *p9, *p10, *p11, *p12, *p13, *p14, *p15, *p16;
1169 {
1170     register afs_int32 code;
1171     afs_int32 someCode, newHost, thisHost;
1172     register afs_int32 i;
1173     register afs_int32 count;
1174     int chaseCount;
1175     int pass;
1176     struct rx_connection *tc;
1177     struct rx_peer *rxp;
1178
1179     if ( (aflags & (UF_SINGLESERVER | UF_END_SINGLESERVER)) != 0 )
1180     {
1181         if ( ((aflags & UF_SINGLESERVER) != 0 )
1182         &&   ((uServer.ucs_flags & UF_SINGLESERVER ) != 0)
1183            )
1184         {
1185             
1186             /* have a selected server */
1187             tc = aclient->conns[uServer.ucs_selectedServer];
1188             
1189             code = (*aproc)(tc, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11,
1190                             p12, p13, p14, p15, p16);
1191             if ( code )
1192             {
1193                 /* error. Clean up single server state */
1194                 memset(&uServer, 0, sizeof(uServer));
1195             }
1196             return(code);
1197         }
1198         else
1199         if ( (aflags & UF_END_SINGLESERVER) != 0 )
1200         {
1201             memset(&uServer, 0, sizeof(uServer));
1202             return(0);
1203         }
1204     }
1205
1206     someCode = UNOSERVERS;
1207     chaseCount = 0;
1208     pass = 0;
1209     count = 0;
1210     while(1)
1211     { /*w*/
1212
1213         /* tc is the next conn to try */
1214         tc = aclient->conns[count];
1215         if ( tc == 0 )
1216         {
1217             if (pass == 0) {
1218                 pass = 1;       /* in pass 1, we look at down hosts, too */
1219                 count = 0;
1220                 continue;
1221             }
1222             else break;                 /* nothing left to try */
1223         }
1224         if (pass == 0 && (aclient->states[count] & CFLastFailed)) {
1225             count++;
1226             continue;   /* this guy's down, try someone else first */
1227         }
1228
1229         code = (*aproc)(tc, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, p16);
1230
1231         /* note that getting a UNOTSYNC error code back does *not* guarantee
1232          * that there is a sync site yet elected.  However, if there is a
1233          * sync site out there somewhere, and you're trying an operation that
1234          * requires a sync site, ubik will return UNOTSYNC, indicating the
1235          * operation won't work until you find a sync site
1236          */
1237         if (code == UNOTSYNC) 
1238         { /*ns*/
1239             /* means that this requires a sync site to work */
1240             someCode = code;    /* remember an error, if this fails */
1241
1242             /* now see if we can find the sync site host */
1243             code = VOTE_GetSyncSite(tc, &newHost);
1244             if (code == 0 && newHost != 0) {
1245                 newHost = htonl(newHost); /* convert back to network order */
1246
1247                 /* position count at the appropriate slot in the client
1248                  * structure and retry. If we can't find in slot, we'll just
1249                  * continue through the whole list
1250                  */
1251                 for(i=0;i<MAXSERVERS;i++)
1252                 { /*f*/
1253                     rxp = rx_PeerOf(aclient->conns[i]);
1254                     if (!(thisHost = rx_HostOf(rxp))) {
1255                         count++;    /* host not found, try the next dude */
1256                         break;
1257                     }
1258                     if (thisHost == newHost) 
1259                     {   
1260                         /* avoid asking in a loop */
1261                         if (chaseCount++ > 2) 
1262                             break; 
1263                         count = i;  /* we were told to use this one */
1264                         break;
1265                     }
1266                 } /*f*/
1267             }
1268             else count++;   /* not directed, keep looking for a sync site */
1269             continue;
1270         } /*ns*/
1271         else if (code == UNOQUORUM) {   /* this guy is still recovering */
1272             someCode = code;
1273             count++;
1274             continue;
1275         }
1276         else if (code < 0) {    /* network errors */
1277             someCode = code;
1278             aclient->states[count] |= CFLastFailed;
1279             count++;
1280             continue;
1281         }
1282         else
1283         {
1284             /* ok, operation worked */
1285             aclient->states[count] &= ~CFLastFailed;
1286             /* either misc ubik code, or misc application code (incl success)
1287              */
1288
1289             /* if the call succeeded, setup connection state for subsequent
1290              * calls
1291              */
1292             if ( (code == 0)
1293             &&   (aflags & UF_SINGLESERVER != 0)
1294                )
1295             {
1296                 /* need to save state */
1297                 uServer.ucs_flags = UF_SINGLESERVER;
1298                 uServer.ucs_selectedServer = count;
1299             }
1300                
1301             return code;
1302         } 
1303     } /*w*/
1304     return someCode;
1305 }
1306
1307      
1308 /* -------------------------------------
1309  * debug and test routines 
1310  * -------------------------------------
1311  */
1312
1313 /* udbLocalInit
1314  *      For testing only. Open a connect to the database server running on
1315  *      the local host
1316  * exit:
1317  *      0 - ubik connection established in the global udbHandle structure
1318  *      n - error.
1319  */
1320
1321 udbLocalInit()
1322 {
1323     afs_int32 serverList[MAXSERVERS];
1324     char hostname[256];
1325     char *args[3];
1326     int i;
1327     afs_int32 code;
1328
1329     /* get our host name */
1330     gethostname(hostname, sizeof(hostname));
1331     /* strcpy(hostname, "hops"); */
1332     
1333     args[0] = "";
1334     args[1] = "-servers";
1335     args[2] = hostname;
1336
1337     code = ubik_ParseClientList(3, args, serverList);
1338     if ( code )
1339     {
1340         com_err(whoami, code, "; udbLocalInit: parsing ubik server list");
1341         return(-1);
1342     }
1343
1344     udbHandle.uh_scIndex = RX_SCINDEX_NULL;
1345     udbHandle.uh_secobj = (struct rx_securityClass *) 
1346                                         rxnull_NewClientSecurityObject();
1347
1348     for ( i = 0; serverList[i] != 0; i++ )
1349     {
1350         udbHandle.uh_serverConn[i] = rx_NewConnection (serverList[i], 
1351                                                     htons(AFSCONF_BUDBPORT),
1352                                                     BUDB_SERVICE,
1353                                                     udbHandle.uh_secobj,
1354                                                     udbHandle.uh_scIndex);
1355         if ( udbHandle.uh_serverConn[i] == 0 )
1356         {
1357             com_err(whoami, 0, "connection %d failed", i);
1358             continue;
1359         }
1360     }
1361     udbHandle.uh_serverConn[i] = 0;
1362     code = ubik_ClientInit(udbHandle.uh_serverConn, &udbHandle.uh_client);
1363     if ( code )
1364     {
1365         com_err(whoami, code, "; in ubik_ClientInit");
1366         return(code);
1367     }
1368
1369     code = ubik_Call(BUDB_GetInstanceId,  udbHandle.uh_client, 0, &udbHandle.uh_instanceId);
1370     if ( code )
1371     {
1372         com_err(whoami, code, "; Can't estblish instance Id");
1373         return(code);
1374     }
1375
1376 abort:
1377     return(0);
1378 }
1379
1380 /* bc_openTextFile - This function opens a temp file to read in the
1381  * config text recd from the bu server. On Unix, an unlink() is done on
1382  * the file as soon as it is opened, so when the program exits, the file will
1383  * be removed automatically, while being invisible while in use.
1384  * On NT, however, the file must be explicitly deleted after use with an unlink()
1385  * Input:
1386  *  Pointer to a udhClientTextP struct. The open stream ptr is stored in
1387  *           the udbClientTextP.textStream member.
1388  * Output: The temp file name is returned in tmpFileName. This should be used
1389  *   to delete the file when done with it.
1390  * Return Values: 
1391  *     !0: error code
1392  *     0: Success.
1393  */
1394 int bc_openTextFile(udbClientTextP ctPtr, char *tmpFileName)
1395 {
1396   int code = 0;
1397
1398   if ( ctPtr->textStream != NULL )
1399     fclose(ctPtr->textStream);
1400   
1401   sprintf(tmpFileName, "%s/bu_XXXXXX", gettmpdir());
1402 #ifdef AFS_LINUX20_ENV
1403   mkstemp(tmpFileName);
1404 #else
1405   mktemp(tmpFileName);
1406 #endif
1407   ctPtr->textStream = fopen(tmpFileName, "w+");
1408   if ( ctPtr->textStream == NULL )
1409     ERROR(BUDB_INTERNALERROR);
1410   
1411 #ifndef AFS_NT40_ENV /* This can't be done on NT */
1412   /* make the file invisible to others */
1413   code = unlink(tmpFileName);
1414   if ( code )
1415     ERROR(errno);
1416 #endif
1417  
1418   dprintf(("file is %s\n", tmpFileName));
1419
1420 normal_exit:
1421   return code;
1422
1423 error_exit:
1424   if ( ctPtr->textStream != NULL )
1425     {
1426       fclose(ctPtr->textStream);
1427       ctPtr->textStream = NULL;
1428     }
1429   goto normal_exit;
1430 }
1431
1432
1433 /* bc_closeTextFile: This function closes any actual temp files associated with
1434  * a udbClientText structure. 
1435  * Input: ctPtr->textStream - stream to close
1436  *        tmpFileName - temp file name to delete
1437  * RetVal: 
1438  *    0  - Success
1439  *    !0 - error code
1440  */
1441 int bc_closeTextFile(udbClientTextP ctPtr, char *tmpFileName)
1442 {
1443   int code = 0;
1444
1445   if (ctPtr->textStream)
1446     fclose(ctPtr->textStream);
1447
1448   if (*tmpFileName) { /* we have a valid name */
1449     code = unlink(tmpFileName);
1450     if (code < 0)
1451       code = errno;
1452   }
1453   return code;
1454 }