]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/findlib/bfile.c
simplify is_stream_supported
[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 1 if we support the stream
252  *        0 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 /*#ifndef HAVE_LIBZ
259    case STREAM_GZIP_DATA:
260    case STREAM_SPARSE_GZIP_DATA:
261       return 0;
262 #endif*/
263    case STREAM_WIN32_DATA:
264    case STREAM_WIN32_GZIP_DATA:
265 #ifdef USE_WIN32STREAMEXTRACTION
266       return true;
267 #else
268       return have_win32_api();      
269 #endif
270
271 /*
272    case STREAM_MACOS_FORK_DATA:
273    case STREAM_HFSPLUS_ATTRIBUTES:
274       return false;
275 */
276    /* Known streams */
277 #ifdef HAVE_LIBZ
278    case STREAM_GZIP_DATA:
279    case STREAM_SPARSE_GZIP_DATA:
280 #endif
281    case STREAM_UNIX_ATTRIBUTES:
282    case STREAM_FILE_DATA:
283    case STREAM_MD5_SIGNATURE:
284    case STREAM_UNIX_ATTRIBUTES_EX:
285    case STREAM_SPARSE_DATA:
286    case STREAM_PROGRAM_NAMES:
287    case STREAM_PROGRAM_DATA:
288    case STREAM_SHA1_SIGNATURE:
289    case 0:                            /* compatibility with old tapes */
290       return true;
291    }
292    return false;
293 }
294
295 HANDLE bget_handle(BFILE *bfd)
296 {
297    return bfd->fh;
298 }
299
300 int bopen(BFILE *bfd, const char *fname, int flags, mode_t mode)
301 {
302    POOLMEM *win32_fname;
303    POOLMEM *win32_fname_wchar;
304
305    DWORD dwaccess, dwflags, dwshare;
306
307    /* Convert to Windows path format */
308    win32_fname = get_pool_memory(PM_FNAME);
309    win32_fname_wchar = get_pool_memory(PM_FNAME);
310    
311    unix_name_to_win32(&win32_fname, (char *)fname);
312
313    if (!(p_CreateFileA || p_CreateFileW))
314       return 0;
315
316    if (p_CreateFileW && p_MultiByteToWideChar)               
317       UTF8_2_wchar(&win32_fname_wchar, win32_fname);
318
319    if (flags & O_CREAT) {             /* Create */
320       if (bfd->use_backup_api) {
321          dwaccess = GENERIC_WRITE|FILE_ALL_ACCESS|WRITE_OWNER|WRITE_DAC|ACCESS_SYSTEM_SECURITY;
322          dwflags = FILE_FLAG_BACKUP_SEMANTICS;
323       } else {
324          dwaccess = GENERIC_WRITE;
325          dwflags = 0;
326       }
327
328    // unicode or ansii open for create write
329    if (p_CreateFileW && p_MultiByteToWideChar) {   
330       bfd->fh = p_CreateFileW((LPCWSTR)win32_fname_wchar,
331              dwaccess,                /* Requested access */
332              0,                       /* Shared mode */
333              NULL,                    /* SecurityAttributes */
334              CREATE_ALWAYS,           /* CreationDisposition */
335              dwflags,                 /* Flags and attributes */
336              NULL);                   /* TemplateFile */
337    }
338    else {
339       bfd->fh = p_CreateFileA(win32_fname,
340              dwaccess,                /* Requested access */
341              0,                       /* Shared mode */
342              NULL,                    /* SecurityAttributes */
343              CREATE_ALWAYS,           /* CreationDisposition */
344              dwflags,                 /* Flags and attributes */
345              NULL);                   /* TemplateFile */
346    }
347
348
349       bfd->mode = BF_WRITE;
350
351    } else if (flags & O_WRONLY) {     /* Open existing for write */
352       if (bfd->use_backup_api) {
353          dwaccess = GENERIC_WRITE|WRITE_OWNER|WRITE_DAC;
354          dwflags = FILE_FLAG_BACKUP_SEMANTICS;
355       } else {
356          dwaccess = GENERIC_WRITE;
357          dwflags = 0;
358       }
359
360    // unicode or ansii open for open existing write
361    if (p_CreateFileW && p_MultiByteToWideChar) {   
362       bfd->fh = p_CreateFileW((LPCWSTR)win32_fname_wchar,
363              dwaccess,                /* Requested access */
364              0,                       /* Shared mode */
365              NULL,                    /* SecurityAttributes */
366              OPEN_EXISTING,           /* CreationDisposition */
367              dwflags,                 /* Flags and attributes */
368              NULL);                   /* TemplateFile */
369    }
370    else {
371       bfd->fh = p_CreateFileA(win32_fname,
372              dwaccess,                /* Requested access */
373              0,                       /* Shared mode */
374              NULL,                    /* SecurityAttributes */
375              OPEN_EXISTING,           /* CreationDisposition */
376              dwflags,                 /* Flags and attributes */
377              NULL);                   /* TemplateFile */
378
379    }
380
381       bfd->mode = BF_WRITE;
382
383    } else {                           /* Read */
384       if (bfd->use_backup_api) {
385          dwaccess = GENERIC_READ|READ_CONTROL|ACCESS_SYSTEM_SECURITY;
386          dwflags = FILE_FLAG_BACKUP_SEMANTICS;
387          dwshare = FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE;
388       } else {
389          dwaccess = GENERIC_READ;
390          dwflags = 0;
391          dwshare = FILE_SHARE_READ|FILE_SHARE_WRITE;
392       }
393
394       // unicode or ansii open for open existing read
395    if (p_CreateFileW && p_MultiByteToWideChar) {   
396       bfd->fh = p_CreateFileW((LPCWSTR)win32_fname_wchar,
397              dwaccess,                /* Requested access */
398              dwshare,                 /* Share modes */
399              NULL,                    /* SecurityAttributes */
400              OPEN_EXISTING,           /* CreationDisposition */
401              dwflags,                 /* Flags and attributes */
402              NULL);                   /* TemplateFile */
403    }
404    else {
405       bfd->fh = p_CreateFileA(win32_fname,
406              dwaccess,                /* Requested access */
407              dwshare,                 /* Share modes */
408              NULL,                    /* SecurityAttributes */
409              OPEN_EXISTING,           /* CreationDisposition */
410              dwflags,                 /* Flags and attributes */
411              NULL);                   /* TemplateFile */
412    }
413
414       bfd->mode = BF_READ;
415    }
416
417    if (bfd->fh == INVALID_HANDLE_VALUE) {
418       bfd->lerror = GetLastError();
419       bfd->berrno = b_errno_win32;
420       errno = b_errno_win32;
421       bfd->mode = BF_CLOSED;
422    }
423    bfd->errmsg = NULL;
424    bfd->lpContext = NULL;
425 #ifdef USE_WIN32STREAMEXTRACTION
426    bfd->win32DecompContext.bIsInData = FALSE;
427    bfd->win32DecompContext.liNextHeader = 0;
428 #endif
429    free_pool_memory(win32_fname_wchar);
430    free_pool_memory(win32_fname);
431    return bfd->mode == BF_CLOSED ? -1 : 1;
432 }
433
434 /*
435  * Returns  0 on success
436  *         -1 on error
437  */
438 int bclose(BFILE *bfd)
439 {
440    int stat = 0;
441
442    if (bfd->errmsg) {
443       free_pool_memory(bfd->errmsg);
444       bfd->errmsg = NULL;
445    }
446    if (bfd->mode == BF_CLOSED) {
447       return 0;
448    }
449    if (bfd->use_backup_api && bfd->mode == BF_READ) {
450       BYTE buf[10];
451       if (!bfd->lpContext && !p_BackupRead(bfd->fh,
452               buf,                    /* buffer */
453               (DWORD)0,               /* bytes to read */
454               &bfd->rw_bytes,         /* bytes read */
455               1,                      /* Abort */
456               1,                      /* ProcessSecurity */
457               &bfd->lpContext)) {     /* Read context */
458          errno = b_errno_win32;
459          stat = -1;
460       }
461    } else if (bfd->use_backup_api && bfd->mode == BF_WRITE) {
462       BYTE buf[10];
463       if (!bfd->lpContext && !p_BackupWrite(bfd->fh,
464               buf,                    /* buffer */
465               (DWORD)0,               /* bytes to read */
466               &bfd->rw_bytes,         /* bytes written */
467               1,                      /* Abort */
468               1,                      /* ProcessSecurity */
469               &bfd->lpContext)) {     /* Write context */
470          errno = b_errno_win32;
471          stat = -1;
472       }
473    }
474    if (!CloseHandle(bfd->fh)) {
475       stat = -1;
476       errno = b_errno_win32;
477    }
478    bfd->mode = BF_CLOSED;
479    bfd->lpContext = NULL;
480    return stat;
481 }
482
483 /* Returns: bytes read on success
484  *           0         on EOF
485  *          -1         on error
486  */
487 ssize_t bread(BFILE *bfd, void *buf, size_t count)
488 {
489    bfd->rw_bytes = 0;
490
491    if (bfd->use_backup_api) {
492       if (!p_BackupRead(bfd->fh,
493            (BYTE *)buf,
494            count,
495            &bfd->rw_bytes,
496            0,                           /* no Abort */
497            1,                           /* Process Security */
498            &bfd->lpContext)) {          /* Context */
499          bfd->lerror = GetLastError();
500          bfd->berrno = b_errno_win32;
501          errno = b_errno_win32;
502          return -1;
503       }
504    } else {
505       if (!ReadFile(bfd->fh,
506            buf,
507            count,
508            &bfd->rw_bytes,
509            NULL)) {
510          bfd->lerror = GetLastError();
511          bfd->berrno = b_errno_win32;
512          errno = b_errno_win32;
513          return -1;
514       }
515    }
516
517    return (ssize_t)bfd->rw_bytes;
518 }
519
520 ssize_t bwrite(BFILE *bfd, void *buf, size_t count)
521 {
522    bfd->rw_bytes = 0;
523
524    if (bfd->use_backup_api) {
525       if (!p_BackupWrite(bfd->fh,
526            (BYTE *)buf,
527            count,
528            &bfd->rw_bytes,
529            0,                           /* No abort */
530            1,                           /* Process Security */
531            &bfd->lpContext)) {          /* Context */
532          bfd->lerror = GetLastError();
533          bfd->berrno = b_errno_win32;
534          errno = b_errno_win32;
535          return -1;
536       }
537    } else {
538       if (!WriteFile(bfd->fh,
539            buf,
540            count,
541            &bfd->rw_bytes,
542            NULL)) {
543          bfd->lerror = GetLastError();
544          bfd->berrno = b_errno_win32;
545          errno = b_errno_win32;
546          return -1;
547       }
548    }
549    return (ssize_t)bfd->rw_bytes;
550 }
551
552 bool is_bopen(BFILE *bfd)
553 {
554    return bfd->mode != BF_CLOSED;
555 }
556
557 off_t blseek(BFILE *bfd, off_t offset, int whence)
558 {
559    /* ****FIXME**** this must be implemented if we want to read Win32 Archives */
560    return -1;
561 }
562
563 #else  /* Unix systems */
564
565 /* ===============================================================
566  *
567  *            U N I X
568  *
569  * ===============================================================
570  */
571 void binit(BFILE *bfd)
572 {
573    memset(bfd, 0, sizeof(BFILE));
574    bfd->fid = -1;
575 }
576
577 bool have_win32_api()
578 {
579    return false;                       /* no can do */
580 }
581
582 /*
583  * Enables using the Backup API (win32_data).
584  *   Returns true  if function worked
585  *   Returns false if failed (i.e. do not have Backup API on this machine)
586  */
587 bool set_win32_backup(BFILE *bfd)
588 {
589    return false;                       /* no can do */
590 }
591
592
593 bool set_portable_backup(BFILE *bfd)
594 {
595    return true;                        /* no problem */
596 }
597
598 /*
599  * Return true  if we are writing in portable format
600  * return false if not
601  */
602 bool is_portable_backup(BFILE *bfd)
603 {
604    return true;                       /* portable by definition */
605 }
606
607 bool set_prog(BFILE *bfd, char *prog, JCR *jcr)
608 {
609 #ifdef HAVE_PYTHON
610    if (bfd->prog && strcmp(prog, bfd->prog) == 0) {
611       return true;                    /* already setup */
612    }
613
614    if (python_set_prog(jcr, prog)) {
615       Dmsg1(000, "Set prog=%s\n", prog);
616       bfd->prog = prog;
617       bfd->jcr = jcr;
618       return true;
619    }
620 #endif
621    Dmsg0(000, "No prog set\n");
622    bfd->prog = NULL;
623    return false;
624
625 }
626
627
628 bool is_stream_supported(int stream)
629 {
630    /* No Win32 backup on this machine */
631 /*   switch (stream) {
632 #ifndef HAVE_LIBZ
633    case STREAM_GZIP_DATA:
634    case STREAM_SPARSE_GZIP_DATA:
635    case STREAM_WIN32_GZIP_DATA:    
636 #endif
637 #ifndef USE_WIN32STREAMEXTRACTION
638    case STREAM_WIN32_DATA:
639 #endif
640 #ifndef HAVE_DARWIN_OS
641    case STREAM_MACOS_FORK_DATA:
642    case STREAM_HFSPLUS_ATTRIBUTES:
643 #endif
644       return false;
645 */
646
647    /* Known streams */
648 #ifdef HAVE_LIBZ
649    case STREAM_GZIP_DATA:
650    case STREAM_SPARSE_GZIP_DATA:
651 #endif
652 #ifdef USE_WIN32STREAMEXTRACTION
653    case STREAM_WIN32_DATA:
654 # ifdef HAVE_LIBZ 
655    case STREAM_WIN32_GZIP_DATA:    
656 # endif
657 #endif
658    case STREAM_UNIX_ATTRIBUTES:
659    case STREAM_FILE_DATA:
660    case STREAM_MD5_SIGNATURE:
661    case STREAM_UNIX_ATTRIBUTES_EX:
662    case STREAM_SPARSE_DATA:
663    case STREAM_PROGRAM_NAMES:
664    case STREAM_PROGRAM_DATA:
665    case STREAM_SHA1_SIGNATURE:
666 #ifdef HAVE_DARWIN_OS
667    case STREAM_MACOS_FORK_DATA:
668    case STREAM_HFSPLUS_ATTRIBUTES:
669 #endif
670    case 0:   /* compatibility with old tapes */
671       return true;
672
673    }
674    return false;
675 }
676
677 int bopen(BFILE *bfd, const char *fname, int flags, mode_t mode)
678 {
679    /* Open reader/writer program */
680    if (bfd->prog) {
681       Dmsg1(000, "Open file %d\n", bfd->fid);
682       return python_open(bfd, fname, flags, mode);
683    }
684
685    /* Normal file open */
686    bfd->fid = open(fname, flags, mode);
687    bfd->berrno = errno;
688    Dmsg1(400, "Open file %d\n", bfd->fid);
689    errno = bfd->berrno;
690
691 #ifdef USE_WIN32STREAMEXTRACTION
692    bfd->win32DecompContext.bIsInData = FALSE;
693    bfd->win32DecompContext.liNextHeader = 0;
694 #endif
695
696    return bfd->fid;
697 }
698
699 #ifdef HAVE_DARWIN_OS
700 /* Open the resource fork of a file. */
701 int bopen_rsrc(BFILE *bfd, const char *fname, int flags, mode_t mode)
702 {
703    POOLMEM *rsrc_fname;
704    size_t fname_len;
705
706    fname_len = strlen(fname);
707    rsrc_fname = get_pool_memory(PM_FNAME);
708    bstrncpy(rsrc_fname, fname, fname_len + 1);
709    bstrncpy(rsrc_fname + fname_len, _PATH_RSRCFORKSPEC,
710       strlen(_PATH_RSRCFORKSPEC) + 1);
711    bopen(bfd, rsrc_fname, flags, mode);
712    free_pool_memory(rsrc_fname);
713    return bfd->fid;
714 }
715 #endif
716
717
718 int bclose(BFILE *bfd)
719 {
720    int stat;
721
722    Dmsg1(400, "Close file %d\n", bfd->fid);
723
724    /* Close reader/writer program */
725    if (bfd->prog) {
726       return python_close(bfd);
727    }
728
729    if (bfd->fid == -1) {
730       return 0;
731    }
732
733    /* Close normal file */
734    stat = close(bfd->fid);
735    bfd->berrno = errno;
736    bfd->fid = -1;
737    return stat;
738 }
739
740 ssize_t bread(BFILE *bfd, void *buf, size_t count)
741 {
742    ssize_t stat;
743
744    if (bfd->prog) {
745       return python_read(bfd, buf, count);
746    }
747    stat = read(bfd->fid, buf, count);
748    bfd->berrno = errno;
749    return stat;
750 }
751
752 ssize_t bwrite(BFILE *bfd, void *buf, size_t count)
753 {
754    ssize_t stat;
755
756    if (bfd->prog) {
757       return python_write(bfd, buf, count);
758    }
759    stat = write(bfd->fid, buf, count);
760    bfd->berrno = errno;
761    return stat;
762 }
763
764 bool is_bopen(BFILE *bfd)
765 {
766    return bfd->fid >= 0;
767 }
768
769 off_t blseek(BFILE *bfd, off_t offset, int whence)
770 {
771     off_t pos;
772     pos = lseek(bfd->fid, offset, whence);
773     bfd->berrno = errno;
774     return pos;
775 }
776
777 #endif