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