1/*- 2 * See the file LICENSE for redistribution information. 3 * 4 * Copyright (c) 2004,2008 Oracle. All rights reserved. 5 * 6 * $Id: os_truncate.c,v 12.19 2008/02/18 19:34:22 bostic Exp $ 7 */ 8 9#include "db_config.h" 10 11#include "db_int.h" 12 13/* 14 * __os_truncate -- 15 * Truncate the file. 16 */ 17int 18__os_truncate(env, fhp, pgno, pgsize) 19 ENV *env; 20 DB_FH *fhp; 21 db_pgno_t pgno; 22 u_int32_t pgsize; 23{ 24 /* Yes, this really is how Microsoft have designed their API */ 25 union { 26 __int64 bigint; 27 struct { 28 unsigned long low; 29 long high; 30 }; 31 } off; 32 DB_ENV *dbenv; 33 off_t offset; 34 int ret; 35 36 dbenv = env == NULL ? NULL : env->dbenv; 37 offset = (off_t)pgsize * pgno; 38 ret = 0; 39 40 if (dbenv != NULL && 41 FLD_ISSET(dbenv->verbose, DB_VERB_FILEOPS | DB_VERB_FILEOPS_ALL)) 42 __db_msg(env, 43 "fileops: truncate %s to %lu", fhp->name, (u_long)offset); 44 45#ifdef HAVE_FILESYSTEM_NOTZERO 46 /* 47 * If the filesystem doesn't zero fill, it isn't safe to extend the 48 * file, or we end up with junk blocks. Just return in that case. 49 */ 50 if (__os_fs_notzero()) { 51 off_t stat_offset; 52 u_int32_t mbytes, bytes; 53 54 /* Stat the file. */ 55 if ((ret = 56 __os_ioinfo(env, NULL, fhp, &mbytes, &bytes, NULL)) != 0) 57 return (ret); 58 stat_offset = (off_t)mbytes * MEGABYTE + bytes; 59 60 if (offset > stat_offset) 61 return (0); 62 } 63#endif 64 65 LAST_PANIC_CHECK_BEFORE_IO(env); 66 67 /* 68 * Windows doesn't provide truncate directly. Instead, it has 69 * SetEndOfFile, which truncates to the current position. To 70 * deal with that, we open a duplicate file handle for truncating. 71 * 72 * We want to retry the truncate call, which involves a SetFilePointer 73 * and a SetEndOfFile, but there are several complications: 74 * 75 * 1) since the Windows API deals in 32-bit values, it's possible that 76 * the return from SetFilePointer (the low 32-bits) is 77 * INVALID_SET_FILE_POINTER even when the call has succeeded. So we 78 * have to also check whether GetLastError() returns NO_ERROR. 79 * 80 * 2) when it returns, SetFilePointer overwrites the high bits of the 81 * offset, so if we need to retry, we have to reset the offset each 82 * time. 83 * 84 * We can't switch to SetFilePointerEx, which knows about 64-bit 85 * offsets, because it isn't supported on Win9x/ME. 86 */ 87 RETRY_CHK((off.bigint = (__int64)pgsize * pgno, 88 (SetFilePointer(fhp->trunc_handle, off.low, &off.high, FILE_BEGIN) 89 == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR) || 90 !SetEndOfFile(fhp->trunc_handle)), ret); 91 92 if (ret != 0) { 93 __db_syserr(env, ret, "SetFilePointer: %lu", pgno * pgsize); 94 ret = __os_posix_err(ret); 95 } 96 97 return (ret); 98} 99