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