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.
6 * Kern Sibbald, April MMIII
12 Bacula® - The Network Backup Solution
14 Copyright (C) 2003-2007 Free Software Foundation Europe e.V.
16 The main author of Bacula is Kern Sibbald, with contributions from
17 many others, a complete list can be found in the file AUTHORS.
18 This program is Free Software; you can redistribute it and/or
19 modify it under the terms of version two of the GNU General Public
20 License as published by the Free Software Foundation and included
23 This program is distributed in the hope that it will be useful, but
24 WITHOUT ANY WARRANTY; without even the implied warranty of
25 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
26 General Public License for more details.
28 You should have received a copy of the GNU General Public License
29 along with this program; if not, write to the Free Software
30 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
33 Bacula® is a registered trademark of John Walker.
34 The licensor of Bacula is the Free Software Foundation Europe
35 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
36 Switzerland, email:ftf@fsfeurope.org.
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_GZIP_DATA:
82 return _("GZIP data");
83 case STREAM_SPARSE_GZIP_DATA:
84 return _("GZIP sparse data");
85 case STREAM_WIN32_DATA:
86 return _("Win32 data");
87 case STREAM_WIN32_GZIP_DATA:
88 return _("Win32 GZIP data");
89 case STREAM_UNIX_ATTRIBUTES:
90 return _("File attributes");
91 case STREAM_FILE_DATA:
92 return _("File data");
93 case STREAM_MD5_DIGEST:
94 return _("MD5 digest");
95 case STREAM_UNIX_ATTRIBUTES_EX:
96 return _("Extended attributes");
97 case STREAM_SPARSE_DATA:
98 return _("Sparse data");
99 case STREAM_PROGRAM_NAMES:
100 return _("Program names");
101 case STREAM_PROGRAM_DATA:
102 return _("Program data");
103 case STREAM_SHA1_DIGEST:
104 return _("SHA1 digest");
105 case STREAM_MACOS_FORK_DATA:
106 return _("HFS+ resource fork");
107 case STREAM_HFSPLUS_ATTRIBUTES:
108 return _("HFS+ Finder Info");
109 case STREAM_SHA256_DIGEST:
110 return _("SHA256 digest");
111 case STREAM_SHA512_DIGEST:
112 return _("SHA512 digest");
113 case STREAM_SIGNED_DIGEST:
114 return _("Signed digest");
115 case STREAM_ENCRYPTED_FILE_DATA:
116 return _("Encrypted File data");
117 case STREAM_ENCRYPTED_FILE_GZIP_DATA:
118 return _("Encrypted GZIP data");
119 case STREAM_ENCRYPTED_WIN32_DATA:
120 return _("Encrypted Win32 data");
121 case STREAM_ENCRYPTED_WIN32_GZIP_DATA:
122 return _("Encrypted Win32 GZIP data");
123 case STREAM_ENCRYPTED_MACOS_FORK_DATA:
124 return _("Encrypted HFS+ resource fork");
126 sprintf(buf, "%d", stream);
127 return (const char *)buf;
132 void int64_LE2BE(int64_t* pBE, const int64_t v)
134 /* convert little endian to big endian */
135 if (htonl(1) != 1L) { /* no work if on little endian machine */
136 memcpy(pBE, &v, sizeof(int64_t));
139 uint8_t rv[sizeof(int64_t)];
140 uint8_t *pv = (uint8_t *) &v;
142 for (i = 0; i < 8; i++) {
145 memcpy(pBE, &rv, sizeof(int64_t));
150 void int32_LE2BE(int32_t* pBE, const int32_t v)
152 /* convert little endian to big endian */
153 if (htonl(1) != 1L) { /* no work if on little endian machine */
154 memcpy(pBE, &v, sizeof(int32_t));
157 uint8_t rv[sizeof(int32_t)];
158 uint8_t *pv = (uint8_t *) &v;
160 for (i = 0; i < 4; i++) {
163 memcpy(pBE, &rv, sizeof(int32_t));
168 bool processWin32BackupAPIBlock (BFILE *bfd, void *pBuffer, ssize_t dwSize)
170 /* pByte contains the buffer
171 dwSize the len to be processed. function assumes to be
172 called in successive incremental order over the complete
173 BackupRead stream beginning at pos 0 and ending at the end.
176 PROCESS_WIN32_BACKUPAPIBLOCK_CONTEXT* pContext = &(bfd->win32DecompContext);
177 bool bContinue = false;
178 int64_t dwDataOffset = 0;
181 /* Win32 Stream Header size without name of stream.
182 * = sizeof (WIN32_STREAM_ID)- sizeof(WCHAR*);
184 int32_t dwSizeHeader = 20;
187 if (pContext->liNextHeader >= dwSize) {
188 dwDataLen = dwSize-dwDataOffset;
189 bContinue = false; /* 1 iteration is enough */
191 dwDataLen = pContext->liNextHeader-dwDataOffset;
192 bContinue = true; /* multiple iterations may be necessary */
196 /* copy block of real DATA */
197 if (pContext->bIsInData) {
198 if (bwrite(bfd, ((char *)pBuffer)+dwDataOffset, dwDataLen) != (ssize_t)dwDataLen)
202 if (pContext->liNextHeader < dwSize) {/* is a header in this block ? */
203 int32_t dwOffsetTarget;
204 int32_t dwOffsetSource;
206 if (pContext->liNextHeader < 0) {
207 /* start of header was before this block, so we
208 * continue with the part in the current block
210 dwOffsetTarget = -pContext->liNextHeader;
213 /* start of header is inside of this block */
215 dwOffsetSource = pContext->liNextHeader;
218 int32_t dwHeaderPartLen = dwSizeHeader-dwOffsetTarget;
219 bool bHeaderIsComplete;
221 if (dwHeaderPartLen <= dwSize-dwOffsetSource) {
222 /* header (or rest of header) is completely available
225 bHeaderIsComplete = true;
227 /* header will continue in next block */
228 bHeaderIsComplete = false;
229 dwHeaderPartLen = dwSize-dwOffsetSource;
232 /* copy the available portion of header to persistent copy */
233 memcpy(((char *)&pContext->header_stream)+dwOffsetTarget, ((char *)pBuffer)+dwOffsetSource, dwHeaderPartLen);
235 /* recalculate position of next header */
236 if (bHeaderIsComplete) {
237 /* convert stream name size (32 bit little endian) to machine type */
239 int32_LE2BE (&dwNameSize, pContext->header_stream.dwStreamNameSize);
240 dwDataOffset = dwNameSize+pContext->liNextHeader+dwSizeHeader;
242 /* convert stream size (64 bit little endian) to machine type */
243 int64_LE2BE (&(pContext->liNextHeader), pContext->header_stream.Size);
244 pContext->liNextHeader += dwDataOffset;
246 pContext->bIsInData = pContext->header_stream.dwStreamId == WIN32_BACKUP_DATA;
247 if (dwDataOffset == dwSize)
250 /* stop and continue with next block */
252 pContext->bIsInData = false;
257 /* set "NextHeader" relative to the beginning of the next block */
258 pContext->liNextHeader-= dwSize;
265 /* ===============================================================
269 * ===============================================================
272 #if defined(HAVE_WIN32)
274 void unix_name_to_win32(POOLMEM **win32_name, char *name);
275 extern "C" HANDLE get_osfhandle(int fd);
278 void binit(BFILE *bfd)
280 memset(bfd, 0, sizeof(BFILE));
282 bfd->mode = BF_CLOSED;
283 bfd->use_backup_api = have_win32_api();
287 * Enables using the Backup API (win32_data).
288 * Returns 1 if function worked
289 * Returns 0 if failed (i.e. do not have Backup API on this machine)
291 bool set_win32_backup(BFILE *bfd)
293 /* We enable if possible here */
294 bfd->use_backup_api = have_win32_api();
295 return bfd->use_backup_api;
299 bool set_portable_backup(BFILE *bfd)
301 bfd->use_backup_api = false;
305 bool set_prog(BFILE *bfd, char *prog, JCR *jcr)
313 * Return 1 if we are NOT using Win32 BackupWrite()
316 bool is_portable_backup(BFILE *bfd)
318 return !bfd->use_backup_api;
321 bool have_win32_api()
323 return p_BackupRead && p_BackupWrite;
328 * Return true if we support the stream
329 * false if we do not support the stream
331 * This code is running under Win32, so we
332 * do not need #ifdef on MACOS ...
334 bool is_restore_stream_supported(int stream)
338 /* Streams known not to be supported */
340 case STREAM_GZIP_DATA:
341 case STREAM_SPARSE_GZIP_DATA:
342 case STREAM_WIN32_GZIP_DATA:
344 case STREAM_MACOS_FORK_DATA:
345 case STREAM_HFSPLUS_ATTRIBUTES:
346 case STREAM_ENCRYPTED_MACOS_FORK_DATA:
351 case STREAM_GZIP_DATA:
352 case STREAM_SPARSE_GZIP_DATA:
353 case STREAM_WIN32_GZIP_DATA:
355 case STREAM_WIN32_DATA:
356 case STREAM_UNIX_ATTRIBUTES:
357 case STREAM_FILE_DATA:
358 case STREAM_MD5_DIGEST:
359 case STREAM_UNIX_ATTRIBUTES_EX:
360 case STREAM_SPARSE_DATA:
361 case STREAM_PROGRAM_NAMES:
362 case STREAM_PROGRAM_DATA:
363 case STREAM_SHA1_DIGEST:
365 case STREAM_SHA256_DIGEST:
366 case STREAM_SHA512_DIGEST:
369 case STREAM_SIGNED_DIGEST:
370 case STREAM_ENCRYPTED_FILE_DATA:
371 case STREAM_ENCRYPTED_FILE_GZIP_DATA:
372 case STREAM_ENCRYPTED_WIN32_DATA:
373 case STREAM_ENCRYPTED_WIN32_GZIP_DATA:
375 case 0: /* compatibility with old tapes */
381 HANDLE bget_handle(BFILE *bfd)
386 int bopen(BFILE *bfd, const char *fname, int flags, mode_t mode)
388 POOLMEM *win32_fname;
389 POOLMEM *win32_fname_wchar;
391 DWORD dwaccess, dwflags, dwshare;
393 /* Convert to Windows path format */
394 win32_fname = get_pool_memory(PM_FNAME);
395 win32_fname_wchar = get_pool_memory(PM_FNAME);
397 unix_name_to_win32(&win32_fname, (char *)fname);
399 if (!(p_CreateFileA || p_CreateFileW))
402 if (p_CreateFileW && p_MultiByteToWideChar)
403 make_win32_path_UTF8_2_wchar(&win32_fname_wchar, fname);
405 if (flags & O_CREAT) { /* Create */
406 if (bfd->use_backup_api) {
407 dwaccess = GENERIC_WRITE|FILE_ALL_ACCESS|WRITE_OWNER|WRITE_DAC|ACCESS_SYSTEM_SECURITY;
408 dwflags = FILE_FLAG_BACKUP_SEMANTICS;
410 dwaccess = GENERIC_WRITE;
414 // unicode or ascii open for create write
415 if (p_CreateFileW && p_MultiByteToWideChar) {
416 bfd->fh = p_CreateFileW((LPCWSTR)win32_fname_wchar,
417 dwaccess, /* Requested access */
419 NULL, /* SecurityAttributes */
420 CREATE_ALWAYS, /* CreationDisposition */
421 dwflags, /* Flags and attributes */
422 NULL); /* TemplateFile */
424 bfd->fh = p_CreateFileA(win32_fname,
425 dwaccess, /* Requested access */
427 NULL, /* SecurityAttributes */
428 CREATE_ALWAYS, /* CreationDisposition */
429 dwflags, /* Flags and attributes */
430 NULL); /* TemplateFile */
433 bfd->mode = BF_WRITE;
435 } else if (flags & O_WRONLY) { /* Open existing for write */
436 if (bfd->use_backup_api) {
437 dwaccess = GENERIC_WRITE|WRITE_OWNER|WRITE_DAC;
438 dwflags = FILE_FLAG_BACKUP_SEMANTICS;
440 dwaccess = GENERIC_WRITE;
444 // unicode or ascii open for open existing write
445 if (p_CreateFileW && p_MultiByteToWideChar) {
446 bfd->fh = p_CreateFileW((LPCWSTR)win32_fname_wchar,
447 dwaccess, /* Requested access */
449 NULL, /* SecurityAttributes */
450 OPEN_EXISTING, /* CreationDisposition */
451 dwflags, /* Flags and attributes */
452 NULL); /* TemplateFile */
454 bfd->fh = p_CreateFileA(win32_fname,
455 dwaccess, /* Requested access */
457 NULL, /* SecurityAttributes */
458 OPEN_EXISTING, /* CreationDisposition */
459 dwflags, /* Flags and attributes */
460 NULL); /* TemplateFile */
464 bfd->mode = BF_WRITE;
467 if (bfd->use_backup_api) {
468 dwaccess = GENERIC_READ|READ_CONTROL|ACCESS_SYSTEM_SECURITY;
469 dwflags = FILE_FLAG_BACKUP_SEMANTICS;
470 dwshare = FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE;
472 dwaccess = GENERIC_READ;
474 dwshare = FILE_SHARE_READ|FILE_SHARE_WRITE;
477 // unicode or ascii open for open existing read
478 if (p_CreateFileW && p_MultiByteToWideChar) {
479 bfd->fh = p_CreateFileW((LPCWSTR)win32_fname_wchar,
480 dwaccess, /* Requested access */
481 dwshare, /* Share modes */
482 NULL, /* SecurityAttributes */
483 OPEN_EXISTING, /* CreationDisposition */
484 dwflags, /* Flags and attributes */
485 NULL); /* TemplateFile */
487 bfd->fh = p_CreateFileA(win32_fname,
488 dwaccess, /* Requested access */
489 dwshare, /* Share modes */
490 NULL, /* SecurityAttributes */
491 OPEN_EXISTING, /* CreationDisposition */
492 dwflags, /* Flags and attributes */
493 NULL); /* TemplateFile */
499 if (bfd->fh == INVALID_HANDLE_VALUE) {
500 bfd->lerror = GetLastError();
501 bfd->berrno = b_errno_win32;
502 errno = b_errno_win32;
503 bfd->mode = BF_CLOSED;
506 bfd->lpContext = NULL;
507 bfd->win32DecompContext.bIsInData = false;
508 bfd->win32DecompContext.liNextHeader = 0;
509 free_pool_memory(win32_fname_wchar);
510 free_pool_memory(win32_fname);
511 return bfd->mode == BF_CLOSED ? -1 : 1;
515 * Returns 0 on success
518 int bclose(BFILE *bfd)
523 free_pool_memory(bfd->errmsg);
526 if (bfd->mode == BF_CLOSED) {
529 if (bfd->use_backup_api && bfd->mode == BF_READ) {
531 if (!bfd->lpContext && !p_BackupRead(bfd->fh,
533 (DWORD)0, /* bytes to read */
534 &bfd->rw_bytes, /* bytes read */
536 1, /* ProcessSecurity */
537 &bfd->lpContext)) { /* Read context */
538 errno = b_errno_win32;
541 } else if (bfd->use_backup_api && bfd->mode == BF_WRITE) {
543 if (!bfd->lpContext && !p_BackupWrite(bfd->fh,
545 (DWORD)0, /* bytes to read */
546 &bfd->rw_bytes, /* bytes written */
548 1, /* ProcessSecurity */
549 &bfd->lpContext)) { /* Write context */
550 errno = b_errno_win32;
554 if (!CloseHandle(bfd->fh)) {
556 errno = b_errno_win32;
558 bfd->mode = BF_CLOSED;
559 bfd->lpContext = NULL;
563 /* Returns: bytes read on success
567 ssize_t bread(BFILE *bfd, void *buf, size_t count)
571 if (bfd->use_backup_api) {
572 if (!p_BackupRead(bfd->fh,
577 1, /* Process Security */
578 &bfd->lpContext)) { /* Context */
579 bfd->lerror = GetLastError();
580 bfd->berrno = b_errno_win32;
581 errno = b_errno_win32;
585 if (!ReadFile(bfd->fh,
590 bfd->lerror = GetLastError();
591 bfd->berrno = b_errno_win32;
592 errno = b_errno_win32;
597 return (ssize_t)bfd->rw_bytes;
600 ssize_t bwrite(BFILE *bfd, void *buf, size_t count)
604 if (bfd->use_backup_api) {
605 if (!p_BackupWrite(bfd->fh,
610 1, /* Process Security */
611 &bfd->lpContext)) { /* Context */
612 bfd->lerror = GetLastError();
613 bfd->berrno = b_errno_win32;
614 errno = b_errno_win32;
618 if (!WriteFile(bfd->fh,
623 bfd->lerror = GetLastError();
624 bfd->berrno = b_errno_win32;
625 errno = b_errno_win32;
629 return (ssize_t)bfd->rw_bytes;
632 bool is_bopen(BFILE *bfd)
634 return bfd->mode != BF_CLOSED;
637 boffset_t blseek(BFILE *bfd, boffset_t offset, int whence)
639 LONG offset_low = (LONG)offset;
640 LONG offset_high = (LONG)(offset >> 32);
643 dwResult = SetFilePointer(bfd->fh, offset_low, &offset_high, whence);
645 if (dwResult == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR) {
646 return (boffset_t)-1;
649 return ((boffset_t)offset_high << 32) | dwResult;
652 #else /* Unix systems */
654 /* ===============================================================
658 * ===============================================================
660 void binit(BFILE *bfd)
662 memset(bfd, 0, sizeof(BFILE));
666 bool have_win32_api()
668 return false; /* no can do */
672 * Enables using the Backup API (win32_data).
673 * Returns true if function worked
674 * Returns false if failed (i.e. do not have Backup API on this machine)
676 bool set_win32_backup(BFILE *bfd)
678 return false; /* no can do */
682 bool set_portable_backup(BFILE *bfd)
684 return true; /* no problem */
688 * Return true if we are writing in portable format
689 * return false if not
691 bool is_portable_backup(BFILE *bfd)
693 return true; /* portable by definition */
696 bool set_prog(BFILE *bfd, char *prog, JCR *jcr)
699 if (bfd->prog && strcmp(prog, bfd->prog) == 0) {
700 return true; /* already setup */
703 if (python_set_prog(jcr, prog)) {
704 Dmsg1(000, "Set prog=%s\n", prog);
710 Dmsg0(000, "No prog set\n");
717 * This code is running on a non-Win32 machine
719 bool is_restore_stream_supported(int stream)
721 /* No Win32 backup on this machine */
724 case STREAM_GZIP_DATA:
725 case STREAM_SPARSE_GZIP_DATA:
726 case STREAM_WIN32_GZIP_DATA:
728 #ifndef HAVE_DARWIN_OS
729 case STREAM_MACOS_FORK_DATA:
730 case STREAM_HFSPLUS_ATTRIBUTES:
736 case STREAM_GZIP_DATA:
737 case STREAM_SPARSE_GZIP_DATA:
738 case STREAM_WIN32_GZIP_DATA:
740 case STREAM_WIN32_DATA:
741 case STREAM_UNIX_ATTRIBUTES:
742 case STREAM_FILE_DATA:
743 case STREAM_MD5_DIGEST:
744 case STREAM_UNIX_ATTRIBUTES_EX:
745 case STREAM_SPARSE_DATA:
746 case STREAM_PROGRAM_NAMES:
747 case STREAM_PROGRAM_DATA:
748 case STREAM_SHA1_DIGEST:
750 case STREAM_SHA256_DIGEST:
751 case STREAM_SHA512_DIGEST:
754 case STREAM_SIGNED_DIGEST:
755 case STREAM_ENCRYPTED_FILE_DATA:
756 case STREAM_ENCRYPTED_FILE_GZIP_DATA:
757 case STREAM_ENCRYPTED_WIN32_DATA:
758 case STREAM_ENCRYPTED_WIN32_GZIP_DATA:
760 #ifdef HAVE_DARWIN_OS
761 case STREAM_MACOS_FORK_DATA:
762 case STREAM_HFSPLUS_ATTRIBUTES:
764 case STREAM_ENCRYPTED_MACOS_FORK_DATA:
765 #endif /* HAVE_CRYPTO */
766 #endif /* HAVE_DARWIN_OS */
767 case 0: /* compatibility with old tapes */
774 int bopen(BFILE *bfd, const char *fname, int flags, mode_t mode)
776 /* Open reader/writer program */
778 Dmsg1(000, "Open file %d\n", bfd->fid);
779 return python_open(bfd, fname, flags, mode);
782 /* Normal file open */
783 Dmsg1(400, "open file %s\n", fname);
784 /* We use fnctl to set O_NOATIME if requested to avoid open error */
785 bfd->fid = open(fname, flags & ~O_NOATIME, mode);
786 /* Set O_NOATIME if possible */
787 if (bfd->fid != -1 && flags & O_NOATIME) {
788 int oldflags = fcntl(bfd->fid, F_GETFL, 0);
789 if (oldflags == -1) {
794 int ret = fcntl(bfd->fid, F_SETFL, oldflags | O_NOATIME);
795 /* EPERM means setting O_NOATIME was not allowed */
796 if (ret == -1 && errno != EPERM) {
804 bfd->m_flags = flags;
805 Dmsg1(400, "Open file %d\n", bfd->fid);
808 bfd->win32DecompContext.bIsInData = false;
809 bfd->win32DecompContext.liNextHeader = 0;
811 #if defined(HAVE_POSIX_FADVISE) && defined(POSIX_FADV_WILLNEED)
812 if (bfd->fid != -1 && flags & O_RDONLY) {
813 int stat = posix_fadvise(bfd->fid, 0, 0, POSIX_FADV_WILLNEED);
814 Dmsg2(400, "Did posix_fadvise on %s stat=%d\n", fname, stat);
821 #ifdef HAVE_DARWIN_OS
822 /* Open the resource fork of a file. */
823 int bopen_rsrc(BFILE *bfd, const char *fname, int flags, mode_t mode)
827 rsrc_fname = get_pool_memory(PM_FNAME);
828 pm_strcpy(rsrc_fname, fname);
829 pm_strcat(rsrc_fname, _PATH_RSRCFORKSPEC);
830 bopen(bfd, rsrc_fname, flags, mode);
831 free_pool_memory(rsrc_fname);
837 int bclose(BFILE *bfd)
841 Dmsg1(400, "Close file %d\n", bfd->fid);
843 /* Close reader/writer program */
845 return python_close(bfd);
848 if (bfd->fid == -1) {
851 #if defined(HAVE_POSIX_FADVISE) && defined(POSIX_FADV_DONTNEED)
852 if (bfd->m_flags & O_RDONLY) {
853 fdatasync(bfd->fid); /* sync the file */
854 /* Tell OS we don't need it any more */
855 posix_fadvise(bfd->fid, 0, 0, POSIX_FADV_DONTNEED);
859 /* Close normal file */
860 stat = close(bfd->fid);
866 ssize_t bread(BFILE *bfd, void *buf, size_t count)
871 return python_read(bfd, buf, count);
873 stat = read(bfd->fid, buf, count);
878 ssize_t bwrite(BFILE *bfd, void *buf, size_t count)
883 return python_write(bfd, buf, count);
885 stat = write(bfd->fid, buf, count);
890 bool is_bopen(BFILE *bfd)
892 return bfd->fid >= 0;
895 boffset_t blseek(BFILE *bfd, boffset_t offset, int whence)
898 pos = (boffset_t)lseek(bfd->fid, (off_t)offset, whence);