2 Bacula® - The Network Backup Solution
4 Copyright (C) 2003-2007 Free Software Foundation Europe e.V.
6 The main author of Bacula is Kern Sibbald, with contributions from
7 many others, a complete list can be found in the file AUTHORS.
8 This program is Free Software; you can redistribute it and/or
9 modify it under the terms of version two of the GNU General Public
10 License as published by the Free Software Foundation and included
13 This program is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23 Bacula® is a registered trademark of John Walker.
24 The licensor of Bacula is the Free Software Foundation Europe
25 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26 Switzerland, email:ftf@fsfeurope.org.
29 * Bacula low level File I/O routines. This routine simulates
30 * open(), read(), write(), and close(), but using native routines.
31 * I.e. on Windows, we use Windows APIs.
33 * Kern Sibbald, April MMIII
42 bool (*python_set_prog)(JCR *jcr, const char *prog) = NULL;
43 int (*python_open)(BFILE *bfd, const char *fname, int flags, mode_t mode) = NULL;
44 int (*python_close)(BFILE *bfd) = NULL;
45 ssize_t (*python_read)(BFILE *bfd, void *buf, size_t count) = NULL;
46 ssize_t (*python_write)(BFILE *bfd, void *buf, size_t count) = NULL;
49 #include <sys/paths.h>
52 #if !defined(HAVE_FDATASYNC)
57 /* ===============================================================
59 * U N I X AND W I N D O W S
61 * ===============================================================
64 bool is_win32_stream(int stream)
67 case STREAM_WIN32_DATA:
68 case STREAM_WIN32_GZIP_DATA:
69 case STREAM_ENCRYPTED_WIN32_DATA:
70 case STREAM_ENCRYPTED_WIN32_GZIP_DATA:
76 const char *stream_to_ascii(int stream)
81 case STREAM_UNIX_ATTRIBUTES:
82 return _("Unix attributes");
83 case STREAM_FILE_DATA:
84 return _("File data");
85 case STREAM_MD5_DIGEST:
86 return _("MD5 digest");
87 case STREAM_GZIP_DATA:
88 return _("GZIP data");
89 case STREAM_UNIX_ATTRIBUTES_EX:
90 return _("Extended attributes");
91 case STREAM_SPARSE_DATA:
92 return _("Sparse data");
93 case STREAM_SPARSE_GZIP_DATA:
94 return _("GZIP sparse data");
95 case STREAM_PROGRAM_NAMES:
96 return _("Program names");
97 case STREAM_PROGRAM_DATA:
98 return _("Program data");
99 case STREAM_SHA1_DIGEST:
100 return _("SHA1 digest");
101 case STREAM_WIN32_DATA:
102 return _("Win32 data");
103 case STREAM_WIN32_GZIP_DATA:
104 return _("Win32 GZIP data");
105 case STREAM_MACOS_FORK_DATA:
106 return _("MacOS Fork data");
107 case STREAM_HFSPLUS_ATTRIBUTES:
108 return _("HFS+ attribs");
109 case STREAM_UNIX_ATTRIBUTES_ACCESS_ACL:
110 return _("Standard Unix ACL attribs");
111 case STREAM_UNIX_ATTRIBUTES_DEFAULT_ACL:
112 return _("Default Unix ACL attribs");
113 case STREAM_SHA256_DIGEST:
114 return _("SHA256 digest");
115 case STREAM_SHA512_DIGEST:
116 return _("SHA512 digest");
117 case STREAM_SIGNED_DIGEST:
118 return _("Signed digest");
119 case STREAM_ENCRYPTED_FILE_DATA:
120 return _("Encrypted File data");
121 case STREAM_ENCRYPTED_WIN32_DATA:
122 return _("Encrypted Win32 data");
123 case STREAM_ENCRYPTED_SESSION_DATA:
124 return _("Encrypted session data");
125 case STREAM_ENCRYPTED_FILE_GZIP_DATA:
126 return _("Encrypted GZIP data");
127 case STREAM_ENCRYPTED_WIN32_GZIP_DATA:
128 return _("Encrypted Win32 GZIP data");
129 case STREAM_ENCRYPTED_MACOS_FORK_DATA:
130 return _("Encrypted MacOS fork data");
132 sprintf(buf, "%d", stream);
133 return (const char *)buf;
138 void int64_LE2BE(int64_t* pBE, const int64_t v)
140 /* convert little endian to big endian */
141 if (htonl(1) != 1L) { /* no work if on little endian machine */
142 memcpy(pBE, &v, sizeof(int64_t));
145 uint8_t rv[sizeof(int64_t)];
146 uint8_t *pv = (uint8_t *) &v;
148 for (i = 0; i < 8; i++) {
151 memcpy(pBE, &rv, sizeof(int64_t));
156 void int32_LE2BE(int32_t* pBE, const int32_t v)
158 /* convert little endian to big endian */
159 if (htonl(1) != 1L) { /* no work if on little endian machine */
160 memcpy(pBE, &v, sizeof(int32_t));
163 uint8_t rv[sizeof(int32_t)];
164 uint8_t *pv = (uint8_t *) &v;
166 for (i = 0; i < 4; i++) {
169 memcpy(pBE, &rv, sizeof(int32_t));
174 bool processWin32BackupAPIBlock (BFILE *bfd, void *pBuffer, ssize_t dwSize)
176 /* pByte contains the buffer
177 dwSize the len to be processed. function assumes to be
178 called in successive incremental order over the complete
179 BackupRead stream beginning at pos 0 and ending at the end.
182 PROCESS_WIN32_BACKUPAPIBLOCK_CONTEXT* pContext = &(bfd->win32DecompContext);
183 bool bContinue = false;
184 int64_t dwDataOffset = 0;
187 /* Win32 Stream Header size without name of stream.
188 * = sizeof (WIN32_STREAM_ID)- sizeof(WCHAR*);
190 int32_t dwSizeHeader = 20;
193 if (pContext->liNextHeader >= dwSize) {
194 dwDataLen = dwSize-dwDataOffset;
195 bContinue = false; /* 1 iteration is enough */
197 dwDataLen = pContext->liNextHeader-dwDataOffset;
198 bContinue = true; /* multiple iterations may be necessary */
202 /* copy block of real DATA */
203 if (pContext->bIsInData) {
204 if (bwrite(bfd, ((char *)pBuffer)+dwDataOffset, dwDataLen) != (ssize_t)dwDataLen)
208 if (pContext->liNextHeader < dwSize) {/* is a header in this block ? */
209 int32_t dwOffsetTarget;
210 int32_t dwOffsetSource;
212 if (pContext->liNextHeader < 0) {
213 /* start of header was before this block, so we
214 * continue with the part in the current block
216 dwOffsetTarget = -pContext->liNextHeader;
219 /* start of header is inside of this block */
221 dwOffsetSource = pContext->liNextHeader;
224 int32_t dwHeaderPartLen = dwSizeHeader-dwOffsetTarget;
225 bool bHeaderIsComplete;
227 if (dwHeaderPartLen <= dwSize-dwOffsetSource) {
228 /* header (or rest of header) is completely available
231 bHeaderIsComplete = true;
233 /* header will continue in next block */
234 bHeaderIsComplete = false;
235 dwHeaderPartLen = dwSize-dwOffsetSource;
238 /* copy the available portion of header to persistent copy */
239 memcpy(((char *)&pContext->header_stream)+dwOffsetTarget, ((char *)pBuffer)+dwOffsetSource, dwHeaderPartLen);
241 /* recalculate position of next header */
242 if (bHeaderIsComplete) {
243 /* convert stream name size (32 bit little endian) to machine type */
245 int32_LE2BE (&dwNameSize, pContext->header_stream.dwStreamNameSize);
246 dwDataOffset = dwNameSize+pContext->liNextHeader+dwSizeHeader;
248 /* convert stream size (64 bit little endian) to machine type */
249 int64_LE2BE (&(pContext->liNextHeader), pContext->header_stream.Size);
250 pContext->liNextHeader += dwDataOffset;
252 pContext->bIsInData = pContext->header_stream.dwStreamId == WIN32_BACKUP_DATA;
253 if (dwDataOffset == dwSize)
256 /* stop and continue with next block */
258 pContext->bIsInData = false;
263 /* set "NextHeader" relative to the beginning of the next block */
264 pContext->liNextHeader-= dwSize;
271 /* ===============================================================
275 * ===============================================================
278 #if defined(HAVE_WIN32)
280 void unix_name_to_win32(POOLMEM **win32_name, char *name);
281 extern "C" HANDLE get_osfhandle(int fd);
284 void binit(BFILE *bfd)
286 memset(bfd, 0, sizeof(BFILE));
288 bfd->mode = BF_CLOSED;
289 bfd->use_backup_api = have_win32_api();
293 * Enables using the Backup API (win32_data).
294 * Returns 1 if function worked
295 * Returns 0 if failed (i.e. do not have Backup API on this machine)
297 bool set_win32_backup(BFILE *bfd)
299 /* We enable if possible here */
300 bfd->use_backup_api = have_win32_api();
301 return bfd->use_backup_api;
305 bool set_portable_backup(BFILE *bfd)
307 bfd->use_backup_api = false;
311 bool set_prog(BFILE *bfd, char *prog, JCR *jcr)
319 * Return 1 if we are NOT using Win32 BackupWrite()
322 bool is_portable_backup(BFILE *bfd)
324 return !bfd->use_backup_api;
327 bool have_win32_api()
329 return p_BackupRead && p_BackupWrite;
334 * Return true if we support the stream
335 * false if we do not support the stream
337 * This code is running under Win32, so we
338 * do not need #ifdef on MACOS ...
340 bool is_restore_stream_supported(int stream)
344 /* Streams known not to be supported */
346 case STREAM_GZIP_DATA:
347 case STREAM_SPARSE_GZIP_DATA:
348 case STREAM_WIN32_GZIP_DATA:
350 case STREAM_MACOS_FORK_DATA:
351 case STREAM_HFSPLUS_ATTRIBUTES:
352 case STREAM_ENCRYPTED_MACOS_FORK_DATA:
357 case STREAM_GZIP_DATA:
358 case STREAM_SPARSE_GZIP_DATA:
359 case STREAM_WIN32_GZIP_DATA:
361 case STREAM_WIN32_DATA:
362 case STREAM_UNIX_ATTRIBUTES:
363 case STREAM_FILE_DATA:
364 case STREAM_MD5_DIGEST:
365 case STREAM_UNIX_ATTRIBUTES_EX:
366 case STREAM_SPARSE_DATA:
367 case STREAM_PROGRAM_NAMES:
368 case STREAM_PROGRAM_DATA:
369 case STREAM_SHA1_DIGEST:
371 case STREAM_SHA256_DIGEST:
372 case STREAM_SHA512_DIGEST:
375 case STREAM_SIGNED_DIGEST:
376 case STREAM_ENCRYPTED_FILE_DATA:
377 case STREAM_ENCRYPTED_FILE_GZIP_DATA:
378 case STREAM_ENCRYPTED_WIN32_DATA:
379 case STREAM_ENCRYPTED_WIN32_GZIP_DATA:
381 case 0: /* compatibility with old tapes */
387 HANDLE bget_handle(BFILE *bfd)
392 int bopen(BFILE *bfd, const char *fname, int flags, mode_t mode)
394 POOLMEM *win32_fname;
395 POOLMEM *win32_fname_wchar;
397 DWORD dwaccess, dwflags, dwshare;
399 /* Convert to Windows path format */
400 win32_fname = get_pool_memory(PM_FNAME);
401 win32_fname_wchar = get_pool_memory(PM_FNAME);
403 unix_name_to_win32(&win32_fname, (char *)fname);
405 if (!(p_CreateFileA || p_CreateFileW))
408 if (p_CreateFileW && p_MultiByteToWideChar)
409 make_win32_path_UTF8_2_wchar(&win32_fname_wchar, fname);
411 if (flags & O_CREAT) { /* Create */
412 if (bfd->use_backup_api) {
413 dwaccess = GENERIC_WRITE|FILE_ALL_ACCESS|WRITE_OWNER|WRITE_DAC|ACCESS_SYSTEM_SECURITY;
414 dwflags = FILE_FLAG_BACKUP_SEMANTICS;
416 dwaccess = GENERIC_WRITE;
420 if (p_CreateFileW && p_MultiByteToWideChar) {
421 // unicode open for create write
422 bfd->fh = p_CreateFileW((LPCWSTR)win32_fname_wchar,
423 dwaccess, /* Requested access */
425 NULL, /* SecurityAttributes */
426 CREATE_ALWAYS, /* CreationDisposition */
427 dwflags, /* Flags and attributes */
428 NULL); /* TemplateFile */
431 bfd->fh = p_CreateFileA(win32_fname,
432 dwaccess, /* Requested access */
434 NULL, /* SecurityAttributes */
435 CREATE_ALWAYS, /* CreationDisposition */
436 dwflags, /* Flags and attributes */
437 NULL); /* TemplateFile */
440 bfd->mode = BF_WRITE;
442 } else if (flags & O_WRONLY) { /* Open existing for write */
443 if (bfd->use_backup_api) {
444 dwaccess = GENERIC_WRITE|WRITE_OWNER|WRITE_DAC;
445 dwflags = FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT;
447 dwaccess = GENERIC_WRITE;
451 if (p_CreateFileW && p_MultiByteToWideChar) {
452 // unicode open for open existing write
453 bfd->fh = p_CreateFileW((LPCWSTR)win32_fname_wchar,
454 dwaccess, /* Requested access */
456 NULL, /* SecurityAttributes */
457 OPEN_EXISTING, /* CreationDisposition */
458 dwflags, /* Flags and attributes */
459 NULL); /* TemplateFile */
462 bfd->fh = p_CreateFileA(win32_fname,
463 dwaccess, /* Requested access */
465 NULL, /* SecurityAttributes */
466 OPEN_EXISTING, /* CreationDisposition */
467 dwflags, /* Flags and attributes */
468 NULL); /* TemplateFile */
472 bfd->mode = BF_WRITE;
475 if (bfd->use_backup_api) {
476 dwaccess = GENERIC_READ|READ_CONTROL|ACCESS_SYSTEM_SECURITY;
477 dwflags = FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_SEQUENTIAL_SCAN |
478 FILE_FLAG_OPEN_REPARSE_POINT;
479 dwshare = FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE;
481 dwaccess = GENERIC_READ;
483 dwshare = FILE_SHARE_READ|FILE_SHARE_WRITE;
486 if (p_CreateFileW && p_MultiByteToWideChar) {
487 // unicode open for open existing read
488 bfd->fh = p_CreateFileW((LPCWSTR)win32_fname_wchar,
489 dwaccess, /* Requested access */
490 dwshare, /* Share modes */
491 NULL, /* SecurityAttributes */
492 OPEN_EXISTING, /* CreationDisposition */
493 dwflags, /* Flags and attributes */
494 NULL); /* TemplateFile */
497 bfd->fh = p_CreateFileA(win32_fname,
498 dwaccess, /* Requested access */
499 dwshare, /* Share modes */
500 NULL, /* SecurityAttributes */
501 OPEN_EXISTING, /* CreationDisposition */
502 dwflags, /* Flags and attributes */
503 NULL); /* TemplateFile */
509 if (bfd->fh == INVALID_HANDLE_VALUE) {
510 bfd->lerror = GetLastError();
511 bfd->berrno = b_errno_win32;
512 errno = b_errno_win32;
513 bfd->mode = BF_CLOSED;
516 bfd->lpContext = NULL;
517 bfd->win32DecompContext.bIsInData = false;
518 bfd->win32DecompContext.liNextHeader = 0;
519 free_pool_memory(win32_fname_wchar);
520 free_pool_memory(win32_fname);
521 return bfd->mode == BF_CLOSED ? -1 : 1;
525 * Returns 0 on success
528 int bclose(BFILE *bfd)
533 free_pool_memory(bfd->errmsg);
536 if (bfd->mode == BF_CLOSED) {
540 * We need to tell the API to release the buffer it
541 * allocated in lpContext. We do so by calling the
542 * API one more time, but with the Abort bit set.
544 if (bfd->use_backup_api && bfd->mode == BF_READ) {
546 if (bfd->lpContext && !p_BackupRead(bfd->fh,
548 (DWORD)0, /* bytes to read */
549 &bfd->rw_bytes, /* bytes read */
551 1, /* ProcessSecurity */
552 &bfd->lpContext)) { /* Read context */
553 errno = b_errno_win32;
556 } else if (bfd->use_backup_api && bfd->mode == BF_WRITE) {
558 if (bfd->lpContext && !p_BackupWrite(bfd->fh,
560 (DWORD)0, /* bytes to read */
561 &bfd->rw_bytes, /* bytes written */
563 1, /* ProcessSecurity */
564 &bfd->lpContext)) { /* Write context */
565 errno = b_errno_win32;
569 if (!CloseHandle(bfd->fh)) {
571 errno = b_errno_win32;
573 bfd->mode = BF_CLOSED;
574 bfd->lpContext = NULL;
578 /* Returns: bytes read on success
582 ssize_t bread(BFILE *bfd, void *buf, size_t count)
586 if (bfd->use_backup_api) {
587 if (!p_BackupRead(bfd->fh,
592 1, /* Process Security */
593 &bfd->lpContext)) { /* Context */
594 bfd->lerror = GetLastError();
595 bfd->berrno = b_errno_win32;
596 errno = b_errno_win32;
600 if (!ReadFile(bfd->fh,
605 bfd->lerror = GetLastError();
606 bfd->berrno = b_errno_win32;
607 errno = b_errno_win32;
612 return (ssize_t)bfd->rw_bytes;
615 ssize_t bwrite(BFILE *bfd, void *buf, size_t count)
619 if (bfd->use_backup_api) {
620 if (!p_BackupWrite(bfd->fh,
625 1, /* Process Security */
626 &bfd->lpContext)) { /* Context */
627 bfd->lerror = GetLastError();
628 bfd->berrno = b_errno_win32;
629 errno = b_errno_win32;
633 if (!WriteFile(bfd->fh,
638 bfd->lerror = GetLastError();
639 bfd->berrno = b_errno_win32;
640 errno = b_errno_win32;
644 return (ssize_t)bfd->rw_bytes;
647 bool is_bopen(BFILE *bfd)
649 return bfd->mode != BF_CLOSED;
652 boffset_t blseek(BFILE *bfd, boffset_t offset, int whence)
654 LONG offset_low = (LONG)offset;
655 LONG offset_high = (LONG)(offset >> 32);
658 dwResult = SetFilePointer(bfd->fh, offset_low, &offset_high, whence);
660 if (dwResult == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR) {
661 return (boffset_t)-1;
664 return ((boffset_t)offset_high << 32) | dwResult;
667 #else /* Unix systems */
669 /* ===============================================================
673 * ===============================================================
675 void binit(BFILE *bfd)
677 memset(bfd, 0, sizeof(BFILE));
681 bool have_win32_api()
683 return false; /* no can do */
687 * Enables using the Backup API (win32_data).
688 * Returns true if function worked
689 * Returns false if failed (i.e. do not have Backup API on this machine)
691 bool set_win32_backup(BFILE *bfd)
693 return false; /* no can do */
697 bool set_portable_backup(BFILE *bfd)
699 return true; /* no problem */
703 * Return true if we are writing in portable format
704 * return false if not
706 bool is_portable_backup(BFILE *bfd)
708 return true; /* portable by definition */
711 bool set_prog(BFILE *bfd, char *prog, JCR *jcr)
714 if (bfd->prog && strcmp(prog, bfd->prog) == 0) {
715 return true; /* already setup */
718 if (python_set_prog(jcr, prog)) {
719 Dmsg1(000, "Set prog=%s\n", prog);
725 Dmsg0(000, "No prog set\n");
732 * This code is running on a non-Win32 machine
734 bool is_restore_stream_supported(int stream)
736 /* No Win32 backup on this machine */
739 case STREAM_GZIP_DATA:
740 case STREAM_SPARSE_GZIP_DATA:
741 case STREAM_WIN32_GZIP_DATA:
743 #ifndef HAVE_DARWIN_OS
744 case STREAM_MACOS_FORK_DATA:
745 case STREAM_HFSPLUS_ATTRIBUTES:
751 case STREAM_GZIP_DATA:
752 case STREAM_SPARSE_GZIP_DATA:
753 case STREAM_WIN32_GZIP_DATA:
755 case STREAM_WIN32_DATA:
756 case STREAM_UNIX_ATTRIBUTES:
757 case STREAM_FILE_DATA:
758 case STREAM_MD5_DIGEST:
759 case STREAM_UNIX_ATTRIBUTES_EX:
760 case STREAM_SPARSE_DATA:
761 case STREAM_PROGRAM_NAMES:
762 case STREAM_PROGRAM_DATA:
763 case STREAM_SHA1_DIGEST:
765 case STREAM_SHA256_DIGEST:
766 case STREAM_SHA512_DIGEST:
769 case STREAM_SIGNED_DIGEST:
770 case STREAM_ENCRYPTED_FILE_DATA:
771 case STREAM_ENCRYPTED_FILE_GZIP_DATA:
772 case STREAM_ENCRYPTED_WIN32_DATA:
773 case STREAM_ENCRYPTED_WIN32_GZIP_DATA:
775 #ifdef HAVE_DARWIN_OS
776 case STREAM_MACOS_FORK_DATA:
777 case STREAM_HFSPLUS_ATTRIBUTES:
779 case STREAM_ENCRYPTED_MACOS_FORK_DATA:
780 #endif /* HAVE_CRYPTO */
781 #endif /* HAVE_DARWIN_OS */
782 case 0: /* compatibility with old tapes */
789 int bopen(BFILE *bfd, const char *fname, int flags, mode_t mode)
791 /* Open reader/writer program */
793 Dmsg1(000, "Open file %d\n", bfd->fid);
794 return python_open(bfd, fname, flags, mode);
797 /* Normal file open */
798 Dmsg1(400, "open file %s\n", fname);
799 /* We use fnctl to set O_NOATIME if requested to avoid open error */
800 bfd->fid = open(fname, flags & ~O_NOATIME, mode);
801 /* Set O_NOATIME if possible */
802 if (bfd->fid != -1 && flags & O_NOATIME) {
803 int oldflags = fcntl(bfd->fid, F_GETFL, 0);
804 if (oldflags == -1) {
809 int ret = fcntl(bfd->fid, F_SETFL, oldflags | O_NOATIME);
810 /* EPERM means setting O_NOATIME was not allowed */
811 if (ret == -1 && errno != EPERM) {
819 bfd->m_flags = flags;
820 Dmsg1(400, "Open file %d\n", bfd->fid);
823 bfd->win32DecompContext.bIsInData = false;
824 bfd->win32DecompContext.liNextHeader = 0;
826 #if defined(HAVE_POSIX_FADVISE) && defined(POSIX_FADV_WILLNEED)
827 if (bfd->fid != -1 && flags & O_RDONLY) {
828 int stat = posix_fadvise(bfd->fid, 0, 0, POSIX_FADV_WILLNEED);
829 Dmsg2(400, "Did posix_fadvise on %s stat=%d\n", fname, stat);
836 #ifdef HAVE_DARWIN_OS
837 /* Open the resource fork of a file. */
838 int bopen_rsrc(BFILE *bfd, const char *fname, int flags, mode_t mode)
842 rsrc_fname = get_pool_memory(PM_FNAME);
843 pm_strcpy(rsrc_fname, fname);
844 pm_strcat(rsrc_fname, _PATH_RSRCFORKSPEC);
845 bopen(bfd, rsrc_fname, flags, mode);
846 free_pool_memory(rsrc_fname);
852 int bclose(BFILE *bfd)
856 Dmsg1(400, "Close file %d\n", bfd->fid);
858 /* Close reader/writer program */
860 return python_close(bfd);
863 if (bfd->fid == -1) {
866 #if defined(HAVE_POSIX_FADVISE) && defined(POSIX_FADV_DONTNEED)
867 if (bfd->m_flags & O_RDONLY) {
868 fdatasync(bfd->fid); /* sync the file */
869 /* Tell OS we don't need it any more */
870 posix_fadvise(bfd->fid, 0, 0, POSIX_FADV_DONTNEED);
874 /* Close normal file */
875 stat = close(bfd->fid);
881 ssize_t bread(BFILE *bfd, void *buf, size_t count)
886 return python_read(bfd, buf, count);
888 stat = read(bfd->fid, buf, count);
893 ssize_t bwrite(BFILE *bfd, void *buf, size_t count)
898 return python_write(bfd, buf, count);
900 stat = write(bfd->fid, buf, count);
905 bool is_bopen(BFILE *bfd)
907 return bfd->fid >= 0;
910 boffset_t blseek(BFILE *bfd, boffset_t offset, int whence)
913 pos = (boffset_t)lseek(bfd->fid, (off_t)offset, whence);