]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/findlib/bfile.c
Rework #ifdef for ZLIB
[bacula/bacula] / bacula / src / findlib / bfile.c
1 /*
2  *  Bacula low level File I/O routines.  This routine simulates
3  *    open(), read(), write(), and close(), but using native routines.
4  *    I.e. on Windows, we use Windows APIs.
5  *
6  *    Kern Sibbald, April MMIII
7  *
8  *   Version $Id$
9  *
10  */
11 /*
12    Copyright (C) 2003-2005 Kern Sibbald
13
14    This program is free software; you can redistribute it and/or
15    modify it under the terms of the GNU General Public License
16    version 2 as amended with additional clauses defined in the
17    file LICENSE in the main source directory.
18
19    This program is distributed in the hope that it will be useful,
20    but WITHOUT ANY WARRANTY; without even the implied warranty of
21    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
22    the file LICENSE for additional details.
23
24  */
25
26 #include "bacula.h"
27 #include "find.h"
28
29 bool    (*python_set_prog)(JCR *jcr, const char *prog) = NULL;
30 int     (*python_open)(BFILE *bfd, const char *fname, int flags, mode_t mode) = NULL;
31 int     (*python_close)(BFILE *bfd) = NULL;
32 ssize_t (*python_read)(BFILE *bfd, void *buf, size_t count) = NULL;
33 ssize_t (*python_write)(BFILE *bfd, void *buf, size_t count) = NULL;
34
35 #ifdef HAVE_DARWIN_OS
36 #include <sys/paths.h>
37 #endif
38
39 /* ===============================================================
40  *
41  *            U N I X   AND   W I N D O W S
42  *
43  * ===============================================================
44  */
45
46 bool is_win32_stream(int stream)
47 {
48    switch (stream) {
49    case STREAM_WIN32_DATA:
50    case STREAM_WIN32_GZIP_DATA:
51       return true;
52    }
53    return false;
54 }
55
56 const char *stream_to_ascii(int stream)
57 {
58    static char buf[20];
59
60    switch (stream) {
61    case STREAM_GZIP_DATA:
62       return _("GZIP data");
63    case STREAM_SPARSE_GZIP_DATA:
64       return _("GZIP sparse data");
65    case STREAM_WIN32_DATA:
66       return _("Win32 data");
67    case STREAM_WIN32_GZIP_DATA:
68       return _("Win32 GZIP data");
69    case STREAM_UNIX_ATTRIBUTES:
70       return _("File attributes");
71    case STREAM_FILE_DATA:
72       return _("File data");
73    case STREAM_MD5_SIGNATURE:
74       return _("MD5 signature");
75    case STREAM_UNIX_ATTRIBUTES_EX:
76       return _("Extended attributes");
77    case STREAM_SPARSE_DATA:
78       return _("Sparse data");
79    case STREAM_PROGRAM_NAMES:
80       return _("Program names");
81    case STREAM_PROGRAM_DATA:
82       return _("Program data");
83    case STREAM_SHA1_SIGNATURE:
84       return _("SHA1 signature");
85    case STREAM_MACOS_FORK_DATA:
86       return _("HFS+ resource fork");
87    case STREAM_HFSPLUS_ATTRIBUTES:
88       return _("HFS+ Finder Info");
89    default:
90       sprintf(buf, "%d", stream);
91       return (const char *)buf;
92    }
93 }
94
95 #ifdef USE_WIN32STREAMEXTRACTION
96 BOOL processWin32BackupAPIBlock (BFILE *bfd, void *pBuffer, size_t dwSize)
97 {   
98    /* pByte contains the buffer 
99       dwSize the len to be processed. function assumes to be called in successive
100       incremental order over the complete BackupRead stream beginning at pos 0 and
101       ending at the end. */
102
103    PROCESS_WIN32_BACKUPAPIBLOCK_CONTEXT* pContext = &(bfd->win32DecompContext);
104    BOOL bContinue = FALSE;
105    LONGLONG dwDataOffset = 0;
106    LONGLONG dwDataLen;
107
108    /* Win32 Stream Header size without name of stream.
109     * = sizeof (WIN32_STREAM_ID)- sizeof(WCHAR*); 
110     */
111    DWORD dwSizeHeader = 20; 
112
113    do {               
114       if (pContext->liNextHeader >= dwSize) {                        
115          dwDataLen = dwSize-dwDataOffset;
116          bContinue = FALSE; /* 1 iteration is enough */
117       }
118       else {                        
119          dwDataLen = pContext->liNextHeader-dwDataOffset;
120          bContinue = TRUE; /* multiple iterations may be necessary */
121       }
122
123       /* flush */
124       /* copy block of real DATA */
125       if (pContext->bIsInData) {
126          if (bwrite(bfd, ((LPBYTE)pBuffer)+dwDataOffset, dwDataLen) != (ssize_t)dwDataLen)            
127             return FALSE;         
128       }
129
130       if (pContext->liNextHeader < dwSize) {/* is a header in this block ? */
131          DWORD dwOffsetTarget;
132          DWORD dwOffsetSource;
133             
134          if (pContext->liNextHeader < 0) {
135             /* start of header was before this block,
136                so we continue with the part in the current block */                                   
137             dwOffsetTarget = abs (pContext->liNextHeader);
138             dwOffsetSource = 0;                            
139          }
140          else {
141             /* start of header is inside of this block */
142             dwOffsetTarget = 0;
143             dwOffsetSource = pContext->liNextHeader;                        
144          }
145
146          DWORD dwHeaderPartLen = dwSizeHeader-dwOffsetTarget;
147          BOOL bHeaderIsComplete;
148
149          if (dwHeaderPartLen <= dwSize-dwOffsetSource) 
150             /* header (or rest of header) is completely available in current block */
151             bHeaderIsComplete = TRUE;                                                        
152          else  {
153             /* header will continue in next block */
154             bHeaderIsComplete = FALSE;
155             dwHeaderPartLen = dwSize-dwOffsetSource;
156          }
157
158          /* copy the available portion of header to persistent copy */
159          memcpy (((LPBYTE) &pContext->header_stream)+dwOffsetTarget, ((LPBYTE) pBuffer)+dwOffsetSource, dwHeaderPartLen);
160
161          /* recalculate position of next header */
162          if (bHeaderIsComplete) {
163             dwDataOffset = pContext->liNextHeader+dwSizeHeader+pContext->header_stream.dwStreamNameSize;
164             pContext->liNextHeader = dwDataOffset+pContext->header_stream.Size.QuadPart;
165             pContext->bIsInData = pContext->header_stream.dwStreamId == BACKUP_DATA;
166             if (dwDataOffset == dwSize)
167                   bContinue = FALSE;
168          }
169          else {
170             /* stop and continue with next block */
171             bContinue = FALSE;
172             pContext->bIsInData = FALSE;
173          }
174       }                
175    } while (bContinue);    
176
177    /* set "NextHeader" relative to the beginning of the next block */
178    pContext->liNextHeader-= dwSize;
179
180    return TRUE;
181 }
182 #endif
183
184
185
186 /* ===============================================================
187  *
188  *            W I N D O W S
189  *
190  * ===============================================================
191  */
192
193 #if defined(HAVE_CYGWIN) || defined(HAVE_WIN32)
194
195 void unix_name_to_win32(POOLMEM **win32_name, char *name);
196 extern "C" HANDLE get_osfhandle(int fd);
197
198
199
200 void binit(BFILE *bfd)
201 {
202    memset(bfd, 0, sizeof(BFILE));
203    bfd->fid = -1;
204    bfd->mode = BF_CLOSED;
205    bfd->use_backup_api = have_win32_api();
206 }
207
208 /*
209  * Enables using the Backup API (win32_data).
210  *   Returns 1 if function worked
211  *   Returns 0 if failed (i.e. do not have Backup API on this machine)
212  */
213 bool set_win32_backup(BFILE *bfd)
214 {
215    /* We enable if possible here */
216    bfd->use_backup_api = have_win32_api();
217    return bfd->use_backup_api;
218 }
219
220
221 bool set_portable_backup(BFILE *bfd)
222 {
223    bfd->use_backup_api = false;
224    return true;
225 }
226
227 bool set_prog(BFILE *bfd, char *prog, JCR *jcr)
228 {
229    bfd->prog = prog;
230    bfd->jcr = jcr;
231    return false;
232 }
233
234 /*
235  * Return 1 if we are NOT using Win32 BackupWrite()
236  * return 0 if are
237  */
238 bool is_portable_backup(BFILE *bfd)
239 {
240    return !bfd->use_backup_api;
241 }
242
243 bool have_win32_api()
244 {
245    return p_BackupRead && p_BackupWrite;
246 }
247
248
249
250 /*
251  * Return true  if we support the stream
252  *        false if we do not support the stream
253  */
254 bool is_stream_supported(int stream)
255 {
256    /* No Win32 backup on this machine */
257    switch (stream) {
258    case STREAM_WIN32_DATA:
259 #ifdef HAVE_ZLIB
260    case STREAM_WIN32_GZIP_DATA:
261 #endif
262 #ifdef USE_WIN32STREAMEXTRACTION
263       return true;
264 #else
265       return have_win32_api();      
266 #endif
267
268 /* Streams known not to be supported */
269 #ifndef HAVE_LIBZ
270    case STREAM_GZIP_DATA:
271    case STREAM_SPARSE_GZIP_DATA:
272    case STREAM_WIN32_GZIP_DATA:
273 #endif
274    case STREAM_MACOS_FORK_DATA:
275    case STREAM_HFSPLUS_ATTRIBUTES:
276       return false;
277
278    /* Known streams */
279 #ifdef HAVE_LIBZ
280    case STREAM_GZIP_DATA:
281    case STREAM_SPARSE_GZIP_DATA:
282 #endif
283    case STREAM_UNIX_ATTRIBUTES:
284    case STREAM_FILE_DATA:
285    case STREAM_MD5_SIGNATURE:
286    case STREAM_UNIX_ATTRIBUTES_EX:
287    case STREAM_SPARSE_DATA:
288    case STREAM_PROGRAM_NAMES:
289    case STREAM_PROGRAM_DATA:
290    case STREAM_SHA1_SIGNATURE:
291    case 0:                            /* compatibility with old tapes */
292       return true;
293    }
294    return false;
295 }
296
297 HANDLE bget_handle(BFILE *bfd)
298 {
299    return bfd->fh;
300 }
301
302 int bopen(BFILE *bfd, const char *fname, int flags, mode_t mode)
303 {
304    POOLMEM *win32_fname;
305    POOLMEM *win32_fname_wchar;
306
307    DWORD dwaccess, dwflags, dwshare;
308
309    /* Convert to Windows path format */
310    win32_fname = get_pool_memory(PM_FNAME);
311    win32_fname_wchar = get_pool_memory(PM_FNAME);
312    
313    unix_name_to_win32(&win32_fname, (char *)fname);
314
315    if (!(p_CreateFileA || p_CreateFileW))
316       return 0;
317
318    if (p_CreateFileW && p_MultiByteToWideChar)               
319       UTF8_2_wchar(&win32_fname_wchar, win32_fname);
320
321    if (flags & O_CREAT) {             /* Create */
322       if (bfd->use_backup_api) {
323          dwaccess = GENERIC_WRITE|FILE_ALL_ACCESS|WRITE_OWNER|WRITE_DAC|ACCESS_SYSTEM_SECURITY;
324          dwflags = FILE_FLAG_BACKUP_SEMANTICS;
325       } else {
326          dwaccess = GENERIC_WRITE;
327          dwflags = 0;
328       }
329
330    // unicode or ansii open for create write
331    if (p_CreateFileW && p_MultiByteToWideChar) {   
332       bfd->fh = p_CreateFileW((LPCWSTR)win32_fname_wchar,
333              dwaccess,                /* Requested access */
334              0,                       /* Shared mode */
335              NULL,                    /* SecurityAttributes */
336              CREATE_ALWAYS,           /* CreationDisposition */
337              dwflags,                 /* Flags and attributes */
338              NULL);                   /* TemplateFile */
339    }
340    else {
341       bfd->fh = p_CreateFileA(win32_fname,
342              dwaccess,                /* Requested access */
343              0,                       /* Shared mode */
344              NULL,                    /* SecurityAttributes */
345              CREATE_ALWAYS,           /* CreationDisposition */
346              dwflags,                 /* Flags and attributes */
347              NULL);                   /* TemplateFile */
348    }
349
350
351       bfd->mode = BF_WRITE;
352
353    } else if (flags & O_WRONLY) {     /* Open existing for write */
354       if (bfd->use_backup_api) {
355          dwaccess = GENERIC_WRITE|WRITE_OWNER|WRITE_DAC;
356          dwflags = FILE_FLAG_BACKUP_SEMANTICS;
357       } else {
358          dwaccess = GENERIC_WRITE;
359          dwflags = 0;
360       }
361
362    // unicode or ansii open for open existing write
363    if (p_CreateFileW && p_MultiByteToWideChar) {   
364       bfd->fh = p_CreateFileW((LPCWSTR)win32_fname_wchar,
365              dwaccess,                /* Requested access */
366              0,                       /* Shared mode */
367              NULL,                    /* SecurityAttributes */
368              OPEN_EXISTING,           /* CreationDisposition */
369              dwflags,                 /* Flags and attributes */
370              NULL);                   /* TemplateFile */
371    }
372    else {
373       bfd->fh = p_CreateFileA(win32_fname,
374              dwaccess,                /* Requested access */
375              0,                       /* Shared mode */
376              NULL,                    /* SecurityAttributes */
377              OPEN_EXISTING,           /* CreationDisposition */
378              dwflags,                 /* Flags and attributes */
379              NULL);                   /* TemplateFile */
380
381    }
382
383       bfd->mode = BF_WRITE;
384
385    } else {                           /* Read */
386       if (bfd->use_backup_api) {
387          dwaccess = GENERIC_READ|READ_CONTROL|ACCESS_SYSTEM_SECURITY;
388          dwflags = FILE_FLAG_BACKUP_SEMANTICS;
389          dwshare = FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE;
390       } else {
391          dwaccess = GENERIC_READ;
392          dwflags = 0;
393          dwshare = FILE_SHARE_READ|FILE_SHARE_WRITE;
394       }
395
396       // unicode or ansii open for open existing read
397    if (p_CreateFileW && p_MultiByteToWideChar) {   
398       bfd->fh = p_CreateFileW((LPCWSTR)win32_fname_wchar,
399              dwaccess,                /* Requested access */
400              dwshare,                 /* Share modes */
401              NULL,                    /* SecurityAttributes */
402              OPEN_EXISTING,           /* CreationDisposition */
403              dwflags,                 /* Flags and attributes */
404              NULL);                   /* TemplateFile */
405    }
406    else {
407       bfd->fh = p_CreateFileA(win32_fname,
408              dwaccess,                /* Requested access */
409              dwshare,                 /* Share modes */
410              NULL,                    /* SecurityAttributes */
411              OPEN_EXISTING,           /* CreationDisposition */
412              dwflags,                 /* Flags and attributes */
413              NULL);                   /* TemplateFile */
414    }
415
416       bfd->mode = BF_READ;
417    }
418
419    if (bfd->fh == INVALID_HANDLE_VALUE) {
420       bfd->lerror = GetLastError();
421       bfd->berrno = b_errno_win32;
422       errno = b_errno_win32;
423       bfd->mode = BF_CLOSED;
424    }
425    bfd->errmsg = NULL;
426    bfd->lpContext = NULL;
427 #ifdef USE_WIN32STREAMEXTRACTION
428    bfd->win32DecompContext.bIsInData = FALSE;
429    bfd->win32DecompContext.liNextHeader = 0;
430 #endif
431    free_pool_memory(win32_fname_wchar);
432    free_pool_memory(win32_fname);
433    return bfd->mode == BF_CLOSED ? -1 : 1;
434 }
435
436 /*
437  * Returns  0 on success
438  *         -1 on error
439  */
440 int bclose(BFILE *bfd)
441 {
442    int stat = 0;
443
444    if (bfd->errmsg) {
445       free_pool_memory(bfd->errmsg);
446       bfd->errmsg = NULL;
447    }
448    if (bfd->mode == BF_CLOSED) {
449       return 0;
450    }
451    if (bfd->use_backup_api && bfd->mode == BF_READ) {
452       BYTE buf[10];
453       if (!bfd->lpContext && !p_BackupRead(bfd->fh,
454               buf,                    /* buffer */
455               (DWORD)0,               /* bytes to read */
456               &bfd->rw_bytes,         /* bytes read */
457               1,                      /* Abort */
458               1,                      /* ProcessSecurity */
459               &bfd->lpContext)) {     /* Read context */
460          errno = b_errno_win32;
461          stat = -1;
462       }
463    } else if (bfd->use_backup_api && bfd->mode == BF_WRITE) {
464       BYTE buf[10];
465       if (!bfd->lpContext && !p_BackupWrite(bfd->fh,
466               buf,                    /* buffer */
467               (DWORD)0,               /* bytes to read */
468               &bfd->rw_bytes,         /* bytes written */
469               1,                      /* Abort */
470               1,                      /* ProcessSecurity */
471               &bfd->lpContext)) {     /* Write context */
472          errno = b_errno_win32;
473          stat = -1;
474       }
475    }
476    if (!CloseHandle(bfd->fh)) {
477       stat = -1;
478       errno = b_errno_win32;
479    }
480    bfd->mode = BF_CLOSED;
481    bfd->lpContext = NULL;
482    return stat;
483 }
484
485 /* Returns: bytes read on success
486  *           0         on EOF
487  *          -1         on error
488  */
489 ssize_t bread(BFILE *bfd, void *buf, size_t count)
490 {
491    bfd->rw_bytes = 0;
492
493    if (bfd->use_backup_api) {
494       if (!p_BackupRead(bfd->fh,
495            (BYTE *)buf,
496            count,
497            &bfd->rw_bytes,
498            0,                           /* no Abort */
499            1,                           /* Process Security */
500            &bfd->lpContext)) {          /* Context */
501          bfd->lerror = GetLastError();
502          bfd->berrno = b_errno_win32;
503          errno = b_errno_win32;
504          return -1;
505       }
506    } else {
507       if (!ReadFile(bfd->fh,
508            buf,
509            count,
510            &bfd->rw_bytes,
511            NULL)) {
512          bfd->lerror = GetLastError();
513          bfd->berrno = b_errno_win32;
514          errno = b_errno_win32;
515          return -1;
516       }
517    }
518
519    return (ssize_t)bfd->rw_bytes;
520 }
521
522 ssize_t bwrite(BFILE *bfd, void *buf, size_t count)
523 {
524    bfd->rw_bytes = 0;
525
526    if (bfd->use_backup_api) {
527       if (!p_BackupWrite(bfd->fh,
528            (BYTE *)buf,
529            count,
530            &bfd->rw_bytes,
531            0,                           /* No abort */
532            1,                           /* Process Security */
533            &bfd->lpContext)) {          /* Context */
534          bfd->lerror = GetLastError();
535          bfd->berrno = b_errno_win32;
536          errno = b_errno_win32;
537          return -1;
538       }
539    } else {
540       if (!WriteFile(bfd->fh,
541            buf,
542            count,
543            &bfd->rw_bytes,
544            NULL)) {
545          bfd->lerror = GetLastError();
546          bfd->berrno = b_errno_win32;
547          errno = b_errno_win32;
548          return -1;
549       }
550    }
551    return (ssize_t)bfd->rw_bytes;
552 }
553
554 bool is_bopen(BFILE *bfd)
555 {
556    return bfd->mode != BF_CLOSED;
557 }
558
559 off_t blseek(BFILE *bfd, off_t offset, int whence)
560 {
561    /* ****FIXME**** this must be implemented if we want to read Win32 Archives */
562    return -1;
563 }
564
565 #else  /* Unix systems */
566
567 /* ===============================================================
568  *
569  *            U N I X
570  *
571  * ===============================================================
572  */
573 void binit(BFILE *bfd)
574 {
575    memset(bfd, 0, sizeof(BFILE));
576    bfd->fid = -1;
577 }
578
579 bool have_win32_api()
580 {
581    return false;                       /* no can do */
582 }
583
584 /*
585  * Enables using the Backup API (win32_data).
586  *   Returns true  if function worked
587  *   Returns false if failed (i.e. do not have Backup API on this machine)
588  */
589 bool set_win32_backup(BFILE *bfd)
590 {
591    return false;                       /* no can do */
592 }
593
594
595 bool set_portable_backup(BFILE *bfd)
596 {
597    return true;                        /* no problem */
598 }
599
600 /*
601  * Return true  if we are writing in portable format
602  * return false if not
603  */
604 bool is_portable_backup(BFILE *bfd)
605 {
606    return true;                       /* portable by definition */
607 }
608
609 bool set_prog(BFILE *bfd, char *prog, JCR *jcr)
610 {
611 #ifdef HAVE_PYTHON
612    if (bfd->prog && strcmp(prog, bfd->prog) == 0) {
613       return true;                    /* already setup */
614    }
615
616    if (python_set_prog(jcr, prog)) {
617       Dmsg1(000, "Set prog=%s\n", prog);
618       bfd->prog = prog;
619       bfd->jcr = jcr;
620       return true;
621    }
622 #endif
623    Dmsg0(000, "No prog set\n");
624    bfd->prog = NULL;
625    return false;
626
627 }
628
629
630 bool is_stream_supported(int stream)
631 {
632    /* No Win32 backup on this machine */
633 /*   switch (stream) {
634 #ifndef HAVE_LIBZ
635    case STREAM_GZIP_DATA:
636    case STREAM_SPARSE_GZIP_DATA:
637    case STREAM_WIN32_GZIP_DATA:    
638 #endif
639 #ifndef USE_WIN32STREAMEXTRACTION
640    case STREAM_WIN32_DATA:
641 #endif
642 #ifndef HAVE_DARWIN_OS
643    case STREAM_MACOS_FORK_DATA:
644    case STREAM_HFSPLUS_ATTRIBUTES:
645 #endif
646       return false;
647 */
648
649    /* Known streams */
650 #ifdef HAVE_LIBZ
651    case STREAM_GZIP_DATA:
652    case STREAM_SPARSE_GZIP_DATA:
653 #endif
654 #ifdef USE_WIN32STREAMEXTRACTION
655    case STREAM_WIN32_DATA:
656 # ifdef HAVE_LIBZ 
657    case STREAM_WIN32_GZIP_DATA:    
658 # endif
659 #endif
660    case STREAM_UNIX_ATTRIBUTES:
661    case STREAM_FILE_DATA:
662    case STREAM_MD5_SIGNATURE:
663    case STREAM_UNIX_ATTRIBUTES_EX:
664    case STREAM_SPARSE_DATA:
665    case STREAM_PROGRAM_NAMES:
666    case STREAM_PROGRAM_DATA:
667    case STREAM_SHA1_SIGNATURE:
668 #ifdef HAVE_DARWIN_OS
669    case STREAM_MACOS_FORK_DATA:
670    case STREAM_HFSPLUS_ATTRIBUTES:
671 #endif
672    case 0:   /* compatibility with old tapes */
673       return true;
674
675    }
676    return false;
677 }
678
679 int bopen(BFILE *bfd, const char *fname, int flags, mode_t mode)
680 {
681    /* Open reader/writer program */
682    if (bfd->prog) {
683       Dmsg1(000, "Open file %d\n", bfd->fid);
684       return python_open(bfd, fname, flags, mode);
685    }
686
687    /* Normal file open */
688    bfd->fid = open(fname, flags, mode);
689    bfd->berrno = errno;
690    Dmsg1(400, "Open file %d\n", bfd->fid);
691    errno = bfd->berrno;
692
693 #ifdef USE_WIN32STREAMEXTRACTION
694    bfd->win32DecompContext.bIsInData = FALSE;
695    bfd->win32DecompContext.liNextHeader = 0;
696 #endif
697
698    return bfd->fid;
699 }
700
701 #ifdef HAVE_DARWIN_OS
702 /* Open the resource fork of a file. */
703 int bopen_rsrc(BFILE *bfd, const char *fname, int flags, mode_t mode)
704 {
705    POOLMEM *rsrc_fname;
706    size_t fname_len;
707
708    fname_len = strlen(fname);
709    rsrc_fname = get_pool_memory(PM_FNAME);
710    bstrncpy(rsrc_fname, fname, fname_len + 1);
711    bstrncpy(rsrc_fname + fname_len, _PATH_RSRCFORKSPEC,
712       strlen(_PATH_RSRCFORKSPEC) + 1);
713    bopen(bfd, rsrc_fname, flags, mode);
714    free_pool_memory(rsrc_fname);
715    return bfd->fid;
716 }
717 #endif
718
719
720 int bclose(BFILE *bfd)
721 {
722    int stat;
723
724    Dmsg1(400, "Close file %d\n", bfd->fid);
725
726    /* Close reader/writer program */
727    if (bfd->prog) {
728       return python_close(bfd);
729    }
730
731    if (bfd->fid == -1) {
732       return 0;
733    }
734
735    /* Close normal file */
736    stat = close(bfd->fid);
737    bfd->berrno = errno;
738    bfd->fid = -1;
739    return stat;
740 }
741
742 ssize_t bread(BFILE *bfd, void *buf, size_t count)
743 {
744    ssize_t stat;
745
746    if (bfd->prog) {
747       return python_read(bfd, buf, count);
748    }
749    stat = read(bfd->fid, buf, count);
750    bfd->berrno = errno;
751    return stat;
752 }
753
754 ssize_t bwrite(BFILE *bfd, void *buf, size_t count)
755 {
756    ssize_t stat;
757
758    if (bfd->prog) {
759       return python_write(bfd, buf, count);
760    }
761    stat = write(bfd->fid, buf, count);
762    bfd->berrno = errno;
763    return stat;
764 }
765
766 bool is_bopen(BFILE *bfd)
767 {
768    return bfd->fid >= 0;
769 }
770
771 off_t blseek(BFILE *bfd, off_t offset, int whence)
772 {
773     off_t pos;
774     pos = lseek(bfd->fid, offset, whence);
775     bfd->berrno = errno;
776     return pos;
777 }
778
779 #endif