]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/findlib/bfile.c
3c6e611874fcdc66a95f14c9f4ccfe876b753758
[bacula/bacula] / bacula / src / findlib / bfile.c
1 /*
2    Bacula(R) - The Network Backup Solution
3
4    Copyright (C) 2000-2015 Kern Sibbald
5
6    The original author of Bacula is Kern Sibbald, with contributions
7    from many others, a complete list can be found in the file AUTHORS.
8
9    You may use this file and others of this release according to the
10    license defined in the LICENSE file, which includes the Affero General
11    Public License, v3.0 ("AGPLv3") and some additional permissions and
12    terms pursuant to its AGPLv3 Section 7.
13
14    This notice must be preserved when any source code is 
15    conveyed and/or propagated.
16
17    Bacula(R) is a registered trademark of Kern Sibbald.
18 */
19 /*
20  *  Bacula low level File I/O routines.  This routine simulates
21  *    open(), read(), write(), and close(), but using native routines.
22  *    I.e. on Windows, we use Windows APIs.
23  *
24  *    Kern Sibbald, April MMIII
25  *
26  */
27
28 #include "bacula.h"
29 #include "find.h"
30
31 const int dbglvl = 200;
32
33 int       (*plugin_bopen)(BFILE *bfd, const char *fname, uint64_t flags, mode_t mode) = NULL;
34 int       (*plugin_bclose)(BFILE *bfd) = NULL;
35 ssize_t   (*plugin_bread)(BFILE *bfd, void *buf, size_t count) = NULL;
36 ssize_t   (*plugin_bwrite)(BFILE *bfd, void *buf, size_t count) = NULL;
37 boffset_t (*plugin_blseek)(BFILE *bfd, boffset_t offset, int whence) = NULL;
38
39
40 #ifdef HAVE_DARWIN_OS
41 #include <sys/paths.h>
42 #endif
43
44 #if !defined(HAVE_FDATASYNC)
45 #define fdatasync(fd)
46 #endif
47
48 #ifdef HAVE_WIN32
49 void pause_msg(const char *file, const char *func, int line, const char *msg)
50 {
51    char buf[1000];
52    if (msg) {
53       bsnprintf(buf, sizeof(buf), "%s:%s:%d %s", file, func, line, msg);
54    } else {
55       bsnprintf(buf, sizeof(buf), "%s:%s:%d", file, func, line);
56    }
57    MessageBox(NULL, buf, "Pause", MB_OK);
58 }
59 #endif
60
61 /* ===============================================================
62  *
63  *            U N I X   AND   W I N D O W S
64  *
65  * ===============================================================
66  */
67
68 bool is_win32_stream(int stream)
69 {
70    switch (stream) {
71    case STREAM_WIN32_DATA:
72    case STREAM_WIN32_GZIP_DATA:
73    case STREAM_WIN32_COMPRESSED_DATA:
74    case STREAM_ENCRYPTED_WIN32_DATA:
75    case STREAM_ENCRYPTED_WIN32_GZIP_DATA:
76    case STREAM_ENCRYPTED_WIN32_COMPRESSED_DATA:
77       return true;
78    }
79    return false;
80 }
81
82 const char *stream_to_ascii(int stream)
83 {
84    static char buf[20];
85
86    switch (stream & STREAMMASK_TYPE) {
87       case STREAM_UNIX_ATTRIBUTES:
88          return _("Unix attributes");
89       case STREAM_FILE_DATA:
90          return _("File data");
91       case STREAM_MD5_DIGEST:
92          return _("MD5 digest");
93       case STREAM_GZIP_DATA:
94          return _("GZIP data");
95       case STREAM_COMPRESSED_DATA:
96          return _("Compressed data");
97       case STREAM_UNIX_ATTRIBUTES_EX:
98          return _("Extended attributes");
99       case STREAM_SPARSE_DATA:
100          return _("Sparse data");
101       case STREAM_SPARSE_GZIP_DATA:
102          return _("GZIP sparse data");
103       case STREAM_SPARSE_COMPRESSED_DATA:
104          return _("Compressed sparse data");
105       case STREAM_PROGRAM_NAMES:
106          return _("Program names");
107       case STREAM_PROGRAM_DATA:
108          return _("Program data");
109       case STREAM_SHA1_DIGEST:
110          return _("SHA1 digest");
111       case STREAM_WIN32_DATA:
112          return _("Win32 data");
113       case STREAM_WIN32_GZIP_DATA:
114          return _("Win32 GZIP data");
115       case STREAM_WIN32_COMPRESSED_DATA:
116          return _("Win32 compressed data");
117       case STREAM_MACOS_FORK_DATA:
118          return _("MacOS Fork data");
119       case STREAM_HFSPLUS_ATTRIBUTES:
120          return _("HFS+ attribs");
121       case STREAM_UNIX_ACCESS_ACL:
122          return _("Standard Unix ACL attribs");
123       case STREAM_UNIX_DEFAULT_ACL:
124          return _("Default Unix ACL attribs");
125       case STREAM_SHA256_DIGEST:
126          return _("SHA256 digest");
127       case STREAM_SHA512_DIGEST:
128          return _("SHA512 digest");
129       case STREAM_SIGNED_DIGEST:
130          return _("Signed digest");
131       case STREAM_ENCRYPTED_FILE_DATA:
132          return _("Encrypted File data");
133       case STREAM_ENCRYPTED_WIN32_DATA:
134          return _("Encrypted Win32 data");
135       case STREAM_ENCRYPTED_SESSION_DATA:
136          return _("Encrypted session data");
137       case STREAM_ENCRYPTED_FILE_GZIP_DATA:
138          return _("Encrypted GZIP data");
139       case STREAM_ENCRYPTED_FILE_COMPRESSED_DATA:
140          return _("Encrypted compressed data");
141       case STREAM_ENCRYPTED_WIN32_GZIP_DATA:
142          return _("Encrypted Win32 GZIP data");
143       case STREAM_ENCRYPTED_WIN32_COMPRESSED_DATA:
144          return _("Encrypted Win32 Compressed data");
145       case STREAM_ENCRYPTED_MACOS_FORK_DATA:
146          return _("Encrypted MacOS fork data");
147       case STREAM_PLUGIN_NAME:
148          return _("Plugin Name");
149       case STREAM_PLUGIN_DATA:
150          return _("Plugin Data");
151       case STREAM_RESTORE_OBJECT:
152          return _("Restore Object");
153       case STREAM_XACL_AIX_TEXT:
154          return _("AIX ACL attribs");
155       case STREAM_XACL_DARWIN_ACCESS:
156          return _("Darwin ACL attribs");
157       case STREAM_XACL_FREEBSD_DEFAULT:
158          return _("FreeBSD Default ACL attribs");
159       case STREAM_XACL_FREEBSD_ACCESS:
160          return _("FreeBSD Access ACL attribs");
161       case STREAM_XACL_HPUX_ACL_ENTRY:
162          return _("HPUX ACL attribs");
163       case STREAM_XACL_IRIX_DEFAULT:
164          return _("Irix Default ACL attribs");
165       case STREAM_XACL_IRIX_ACCESS:
166          return _("Irix Access ACL attribs");
167       case STREAM_XACL_LINUX_DEFAULT:
168          return _("Linux Default ACL attribs");
169       case STREAM_XACL_LINUX_ACCESS:
170          return _("Linux Access ACL attribs");
171       case STREAM_XACL_TRU64_DEFAULT:
172          return _("TRU64 Default ACL attribs");
173       case STREAM_XACL_TRU64_ACCESS:
174          return _("TRU64 Access ACL attribs");
175       case STREAM_XACL_SOLARIS_POSIX:
176          return _("Solaris POSIX ACL attribs");
177       case STREAM_XACL_SOLARIS_NFS4:
178          return _("Solaris NFSv4/ZFS ACL attribs");
179       case STREAM_XACL_AFS_TEXT:
180          return _("AFS ACL attribs");
181       case STREAM_XACL_AIX_AIXC:
182          return _("AIX POSIX ACL attribs");
183       case STREAM_XACL_AIX_NFS4:
184          return _("AIX NFSv4 ACL attribs");
185       case STREAM_XACL_FREEBSD_NFS4:
186          return _("FreeBSD NFSv4/ZFS ACL attribs");
187       case STREAM_XACL_HURD_DEFAULT:
188          return _("GNU Hurd Default ACL attribs");
189       case STREAM_XACL_HURD_ACCESS:
190          return _("GNU Hurd Access ACL attribs");
191       case STREAM_XACL_HURD_XATTR:
192          return _("GNU Hurd Extended attribs");
193       case STREAM_XACL_IRIX_XATTR:
194          return _("IRIX Extended attribs");
195       case STREAM_XACL_TRU64_XATTR:
196          return _("TRU64 Extended attribs");
197       case STREAM_XACL_AIX_XATTR:
198          return _("AIX Extended attribs");
199       case STREAM_XACL_OPENBSD_XATTR:
200          return _("OpenBSD Extended attribs");
201       case STREAM_XACL_SOLARIS_SYS_XATTR:
202          return _("Solaris Extensible attribs or System Extended attribs");
203       case STREAM_XACL_SOLARIS_XATTR:
204          return _("Solaris Extended attribs");
205       case STREAM_XACL_DARWIN_XATTR:
206          return _("Darwin Extended attribs");
207       case STREAM_XACL_FREEBSD_XATTR:
208          return _("FreeBSD Extended attribs");
209       case STREAM_XACL_LINUX_XATTR:
210          return _("Linux Extended attribs");
211       case STREAM_XACL_NETBSD_XATTR:
212          return _("NetBSD Extended attribs");
213       default:
214          sprintf(buf, "%d", stream);
215          return (const char *)buf;
216       }
217 }
218
219 /**
220  *  Convert a 64 bit little endian to a big endian
221  */
222 void int64_LE2BE(int64_t* pBE, const int64_t v)
223 {
224    /* convert little endian to big endian */
225    if (htonl(1) != 1L) { /* no work if on little endian machine */
226       memcpy(pBE, &v, sizeof(int64_t));
227    } else {
228       int i;
229       uint8_t rv[sizeof(int64_t)];
230       uint8_t *pv = (uint8_t *) &v;
231
232       for (i = 0; i < 8; i++) {
233          rv[i] = pv[7 - i];
234       }
235       memcpy(pBE, &rv, sizeof(int64_t));
236    }
237 }
238
239 /**
240  *  Convert a 32 bit little endian to a big endian
241  */
242 void int32_LE2BE(int32_t* pBE, const int32_t v)
243 {
244    /* convert little endian to big endian */
245    if (htonl(1) != 1L) { /* no work if on little endian machine */
246       memcpy(pBE, &v, sizeof(int32_t));
247    } else {
248       int i;
249       uint8_t rv[sizeof(int32_t)];
250       uint8_t *pv = (uint8_t *) &v;
251
252       for (i = 0; i < 4; i++) {
253          rv[i] = pv[3 - i];
254       }
255       memcpy(pBE, &rv, sizeof(int32_t));
256    }
257 }
258
259
260 /**
261  *  Read a BackupRead block and pull out the file data
262  */
263 bool processWin32BackupAPIBlock (BFILE *bfd, void *pBuffer, ssize_t dwSize)
264 {
265    /* pByte contains the buffer
266       dwSize the len to be processed.  function assumes to be
267       called in successive incremental order over the complete
268       BackupRead stream beginning at pos 0 and ending at the end.
269     */
270
271    PROCESS_WIN32_BACKUPAPIBLOCK_CONTEXT* pContext = &(bfd->win32DecompContext);
272    bool bContinue = false;
273    int64_t dwDataOffset = 0;
274    int64_t dwDataLen;
275
276    /* Win32 Stream Header size without name of stream.
277     * = sizeof (WIN32_STREAM_ID)- sizeof(WCHAR*);
278     */
279    int32_t dwSizeHeader = 20;
280
281    do {
282       if (pContext->liNextHeader >= dwSize) {
283          dwDataLen = dwSize-dwDataOffset;
284          bContinue = false; /* 1 iteration is enough */
285       } else {
286          dwDataLen = pContext->liNextHeader-dwDataOffset;
287          bContinue = true; /* multiple iterations may be necessary */
288       }
289
290       /* flush */
291       /* copy block of real DATA */
292       if (pContext->bIsInData) {
293          if (bwrite(bfd, ((char *)pBuffer)+dwDataOffset, dwDataLen) != (ssize_t)dwDataLen)
294             return false;
295       }
296
297       if (pContext->liNextHeader < dwSize) {/* is a header in this block ? */
298          int32_t dwOffsetTarget;
299          int32_t dwOffsetSource;
300
301          if (pContext->liNextHeader < 0) {
302             /* start of header was before this block, so we
303              * continue with the part in the current block
304              */
305             dwOffsetTarget = -pContext->liNextHeader;
306             dwOffsetSource = 0;
307          } else {
308             /* start of header is inside of this block */
309             dwOffsetTarget = 0;
310             dwOffsetSource = pContext->liNextHeader;
311          }
312
313          int32_t dwHeaderPartLen = dwSizeHeader-dwOffsetTarget;
314          bool bHeaderIsComplete;
315
316          if (dwHeaderPartLen <= dwSize-dwOffsetSource) {
317             /* header (or rest of header) is completely available
318                in current block
319              */
320             bHeaderIsComplete = true;
321          } else {
322             /* header will continue in next block */
323             bHeaderIsComplete = false;
324             dwHeaderPartLen = dwSize-dwOffsetSource;
325          }
326
327          /* copy the available portion of header to persistent copy */
328          memcpy(((char *)&pContext->header_stream)+dwOffsetTarget, ((char *)pBuffer)+dwOffsetSource, dwHeaderPartLen);
329
330          /* recalculate position of next header */
331          if (bHeaderIsComplete) {
332             /* convert stream name size (32 bit little endian) to machine type */
333             int32_t dwNameSize;
334             int32_LE2BE (&dwNameSize, pContext->header_stream.dwStreamNameSize);
335             dwDataOffset = dwNameSize+pContext->liNextHeader+dwSizeHeader;
336
337             /* convert stream size (64 bit little endian) to machine type */
338             int64_LE2BE (&(pContext->liNextHeader), pContext->header_stream.Size);
339             pContext->liNextHeader += dwDataOffset;
340
341             pContext->bIsInData = pContext->header_stream.dwStreamId == WIN32_BACKUP_DATA;
342             if (dwDataOffset == dwSize)
343                bContinue = false;
344          } else {
345             /* stop and continue with next block */
346             bContinue = false;
347             pContext->bIsInData = false;
348          }
349       }
350    } while (bContinue);
351
352    /* set "NextHeader" relative to the beginning of the next block */
353    pContext->liNextHeader-= dwSize;
354
355    return TRUE;
356 }
357
358
359
360
361 /* ===============================================================
362  *
363  *            U N I X
364  *
365  * ===============================================================
366  */
367 /* Unix */
368 void binit(BFILE *bfd)
369 {
370    memset(bfd, 0, sizeof(BFILE));
371    bfd->fid = -1;
372 }
373
374 /* Unix */
375 bool have_win32_api()
376 {
377    return false;                       /* no can do */
378 }
379
380 /*
381  * Enables using the Backup API (win32_data).
382  *   Returns true  if function worked
383  *   Returns false if failed (i.e. do not have Backup API on this machine)
384  */
385 /* Unix */
386 bool set_win32_backup(BFILE *bfd)
387 {
388    return false;                       /* no can do */
389 }
390
391
392 /* Unix */
393 bool set_portable_backup(BFILE *bfd)
394 {
395    return true;                        /* no problem */
396 }
397
398 /*
399  * Return true  if we are writing in portable format
400  * return false if not
401  */
402 /* Unix */
403 bool is_portable_backup(BFILE *bfd)
404 {
405    return true;                       /* portable by definition */
406 }
407
408 /* Unix */
409 bool set_prog(BFILE *bfd, char *prog, JCR *jcr)
410 {
411    return false;
412 }
413
414 /* Unix */
415 bool set_cmd_plugin(BFILE *bfd, JCR *jcr)
416 {
417    bfd->cmd_plugin = true;
418    bfd->jcr = jcr;
419    return true;
420 }
421
422 /*
423  * This code is running on a non-Win32 machine
424  */
425 /* Unix */
426 bool is_restore_stream_supported(int stream)
427 {
428    /* No Win32 backup on this machine */
429      switch (stream) {
430 #ifndef HAVE_LIBZ
431    case STREAM_GZIP_DATA:
432    case STREAM_SPARSE_GZIP_DATA:
433    case STREAM_WIN32_GZIP_DATA:
434 #endif
435 #ifndef HAVE_LZO
436    case STREAM_COMPRESSED_DATA:
437    case STREAM_SPARSE_COMPRESSED_DATA:
438    case STREAM_WIN32_COMPRESSED_DATA:
439    case STREAM_ENCRYPTED_FILE_COMPRESSED_DATA:
440    case STREAM_ENCRYPTED_WIN32_COMPRESSED_DATA:
441 #endif
442 #ifndef HAVE_DARWIN_OS
443    case STREAM_MACOS_FORK_DATA:
444    case STREAM_HFSPLUS_ATTRIBUTES:
445 #endif
446       return false;
447
448    /* Known streams */
449 #ifdef HAVE_LIBZ
450    case STREAM_GZIP_DATA:
451    case STREAM_SPARSE_GZIP_DATA:
452    case STREAM_WIN32_GZIP_DATA:
453 #endif
454 #ifdef HAVE_LZO
455    case STREAM_COMPRESSED_DATA:
456    case STREAM_SPARSE_COMPRESSED_DATA:
457    case STREAM_WIN32_COMPRESSED_DATA:
458    case STREAM_ENCRYPTED_FILE_COMPRESSED_DATA:
459    case STREAM_ENCRYPTED_WIN32_COMPRESSED_DATA:
460 #endif
461    case STREAM_WIN32_DATA:
462    case STREAM_UNIX_ATTRIBUTES:
463    case STREAM_FILE_DATA:
464    case STREAM_MD5_DIGEST:
465    case STREAM_UNIX_ATTRIBUTES_EX:
466    case STREAM_SPARSE_DATA:
467    case STREAM_PROGRAM_NAMES:
468    case STREAM_PROGRAM_DATA:
469    case STREAM_SHA1_DIGEST:
470 #ifdef HAVE_SHA2
471    case STREAM_SHA256_DIGEST:
472    case STREAM_SHA512_DIGEST:
473 #endif
474 #ifdef HAVE_CRYPTO
475    case STREAM_SIGNED_DIGEST:
476    case STREAM_ENCRYPTED_FILE_DATA:
477    case STREAM_ENCRYPTED_FILE_GZIP_DATA:
478    case STREAM_ENCRYPTED_WIN32_DATA:
479    case STREAM_ENCRYPTED_WIN32_GZIP_DATA:
480 #endif
481 #ifdef HAVE_DARWIN_OS
482    case STREAM_MACOS_FORK_DATA:
483    case STREAM_HFSPLUS_ATTRIBUTES:
484 #ifdef HAVE_CRYPTO
485    case STREAM_ENCRYPTED_MACOS_FORK_DATA:
486 #endif /* HAVE_CRYPTO */
487 #endif /* HAVE_DARWIN_OS */
488    case 0:   /* compatibility with old tapes */
489       return true;
490
491    }
492    return false;
493 }
494
495 /* Unix */
496 int bopen(BFILE *bfd, const char *fname, uint64_t flags, mode_t mode)
497 {
498    if (bfd->cmd_plugin && plugin_bopen) {
499       Dmsg1(400, "call plugin_bopen fname=%s\n", fname);
500       bfd->fid = plugin_bopen(bfd, fname, flags, mode);
501       Dmsg2(400, "Plugin bopen fid=%d file=%s\n", bfd->fid, fname);
502       return bfd->fid;
503    }
504
505    /* Normal file open */
506    Dmsg1(dbglvl, "open file %s\n", fname);
507
508    /* We use fnctl to set O_NOATIME if requested to avoid open error */
509    bfd->fid = open(fname, (flags | O_CLOEXEC) & ~O_NOATIME, mode);
510
511    /* Set O_NOATIME if possible */
512    if (bfd->fid != -1 && flags & O_NOATIME) {
513       int oldflags = fcntl(bfd->fid, F_GETFL, 0);
514       if (oldflags == -1) {
515          bfd->berrno = errno;
516          close(bfd->fid);
517          bfd->fid = -1;
518       } else {
519          int ret = fcntl(bfd->fid, F_SETFL, oldflags | O_NOATIME);
520         /* EPERM means setting O_NOATIME was not allowed  */
521          if (ret == -1 && errno != EPERM) {
522             bfd->berrno = errno;
523             close(bfd->fid);
524             bfd->fid = -1;
525          }
526       }
527    }
528    bfd->berrno = errno;
529    bfd->m_flags = flags;
530    bfd->block = 0;
531    bfd->total_bytes = 0;
532    Dmsg1(400, "Open file %d\n", bfd->fid);
533    errno = bfd->berrno;
534
535    bfd->win32DecompContext.bIsInData = false;
536    bfd->win32DecompContext.liNextHeader = 0;
537
538 #if defined(HAVE_POSIX_FADVISE) && defined(POSIX_FADV_WILLNEED)
539    /* If not RDWR or WRONLY must be Read Only */
540    if (bfd->fid != -1 && !(flags & (O_RDWR|O_WRONLY))) {
541       int stat = posix_fadvise(bfd->fid, 0, 0, POSIX_FADV_WILLNEED);
542       Dmsg3(400, "Did posix_fadvise WILLNEED on %s fid=%d stat=%d\n", fname, bfd->fid, stat);
543    }
544 #endif
545
546    return bfd->fid;
547 }
548
549 #ifdef HAVE_DARWIN_OS
550 /* Open the resource fork of a file. */
551 int bopen_rsrc(BFILE *bfd, const char *fname, uint64_t flags, mode_t mode)
552 {
553    POOLMEM *rsrc_fname;
554
555    rsrc_fname = get_pool_memory(PM_FNAME);
556    pm_strcpy(rsrc_fname, fname);
557    pm_strcat(rsrc_fname, _PATH_RSRCFORKSPEC);
558    bopen(bfd, rsrc_fname, flags, mode);
559    free_pool_memory(rsrc_fname);
560    return bfd->fid;
561 }
562 #else /* Unix */
563
564 /* Unix */
565 int bopen_rsrc(BFILE *bfd, const char *fname, uint64_t flags, mode_t mode)
566     { return -1; }
567
568 #endif
569
570
571 /* Unix */
572 int bclose(BFILE *bfd)
573 {
574    int stat;
575
576    Dmsg2(400, "Close bfd=%p file %d\n", bfd, bfd->fid);
577
578    if (bfd->fid == -1) {
579       return 0;
580    }
581    if (bfd->cmd_plugin && plugin_bclose) {
582       stat = plugin_bclose(bfd);
583       bfd->fid = -1;
584       bfd->cmd_plugin = false;
585       return stat;
586    }
587
588 #if defined(HAVE_POSIX_FADVISE) && defined(POSIX_FADV_DONTNEED)
589    /* If not RDWR or WRONLY must be Read Only */
590    if (!(bfd->m_flags & (O_RDWR|O_WRONLY))) {
591       fdatasync(bfd->fid);            /* sync the file */
592       /* Tell OS we don't need it any more */
593       posix_fadvise(bfd->fid, 0, 0, POSIX_FADV_DONTNEED);
594       Dmsg1(400, "Did posix_fadvise DONTNEED on fid=%d\n", bfd->fid);
595    }
596 #endif
597
598    /* Close normal file */
599    stat = close(bfd->fid);
600    bfd->berrno = errno;
601    bfd->fid = -1;
602    bfd->cmd_plugin = false;
603    return stat;
604 }
605
606 /* Unix */
607 ssize_t bread(BFILE *bfd, void *buf, size_t count)
608 {
609    ssize_t stat;
610
611    if (bfd->cmd_plugin && plugin_bread) {
612       return plugin_bread(bfd, buf, count);
613    }
614
615    stat = read(bfd->fid, buf, count);
616    bfd->berrno = errno;
617    bfd->block++;
618    if (stat > 0) {
619       bfd->total_bytes += stat;
620    }
621    return stat;
622 }
623
624 /* Unix */
625 ssize_t bwrite(BFILE *bfd, void *buf, size_t count)
626 {
627    ssize_t stat;
628
629    if (bfd->cmd_plugin && plugin_bwrite) {
630       stat = plugin_bwrite(bfd, buf, count);
631
632    } else {
633       stat = write(bfd->fid, buf, count);
634       bfd->berrno = errno;
635    }
636
637    bfd->block++;
638    if (stat > 0) {
639       bfd->total_bytes += stat;
640    }
641    return stat;
642 }
643
644 /* Unix */
645 bool is_bopen(BFILE *bfd)
646 {
647    return bfd->fid >= 0;
648 }
649
650 /* Unix */
651 boffset_t blseek(BFILE *bfd, boffset_t offset, int whence)
652 {
653    boffset_t pos;
654
655    if (bfd->cmd_plugin && plugin_bwrite) {
656       return plugin_blseek(bfd, offset, whence);
657    }
658    pos = (boffset_t)lseek(bfd->fid, offset, whence);
659    bfd->berrno = errno;
660    return pos;
661 }