static INLINE bool SI_close_win_handles(FSFileHandle *self) { FSFileHandleIVARS *ivars = FSFH_IVARS(self); // Close both standard handle and mapping handle. if (ivars->win_maphandle) { if (!CloseHandle(ivars->win_maphandle)) { char *win_error = Err_win_error(); Err_set_error(Err_new(CB_newf("Failed to close file mapping handle: %s", win_error))); FREEMEM(win_error); return false; } ivars->win_maphandle = NULL; } if (ivars->win_fhandle) { if (!CloseHandle(ivars->win_fhandle)) { char *win_error = Err_win_error(); Err_set_error(Err_new(CB_newf("Failed to close file handle: %s", win_error))); FREEMEM(win_error); return false; } ivars->win_fhandle = NULL; } return true; }
bool FSFH_read(FSFileHandle *self, char *dest, int64_t offset, size_t len) { FSFileHandleIVARS *const ivars = FSFH_IVARS(self); int64_t check_val; // Sanity check. if (offset < 0) { Err_set_error(Err_new(CB_newf("Can't read from an offset less than 0 (%i64)", offset))); return false; } // Read. check_val = pread64(ivars->fd, dest, len, offset); if (check_val != (int64_t)len) { if (check_val == -1) { Err_set_error(Err_new(CB_newf("Tried to read %u64 bytes, got %i64: %s", (uint64_t)len, check_val, strerror(errno)))); } else { Err_set_error(Err_new(CB_newf("Tried to read %u64 bytes, got %i64", (uint64_t)len, check_val))); } return false; } return true; }
bool FSFH_close(FSFileHandle *self) { FSFileHandleIVARS *const ivars = FSFH_IVARS(self); // On 64-bit systems, cancel the whole-file mapping. if (IS_64_BIT && (ivars->flags & FH_READ_ONLY) && ivars->buf != NULL) { if (!SI_unmap(self, ivars->buf, ivars->len)) { return false; } ivars->buf = NULL; } // Close system-specific handles. if (ivars->fd) { if (close(ivars->fd)) { Err_set_error(Err_new(CB_newf("Failed to close file: %s", strerror(errno)))); return false; } ivars->fd = 0; } #if (defined(CHY_HAS_WINDOWS_H) && !defined(CHY_HAS_SYS_MMAN_H)) if (ivars->win_fhandle) { if (!SI_close_win_handles(self)) { return false; } } #endif return true; }
static INLINE bool SI_unmap(FSFileHandle *self, char *buf, int64_t len) { if (buf != NULL) { if (munmap(buf, len)) { Err_set_error(Err_new(CB_newf("Failed to munmap '%o': %s", FSFH_IVARS(self)->path, strerror(errno)))); return false; } } return true; }
static INLINE bool SI_unmap(FSFileHandle *self, char *ptr, int64_t len) { if (buf != NULL) { if (!UnmapViewOfFile(buf)) { char *win_error = Err_win_error(); Err_set_error(Err_new(CB_newf("Failed to unmap '%o': %s", FSFH_IVARS(self)->path, win_error))); FREEMEM(win_error); return false; } } return true; }
static void test_Close(TestBatchRunner *runner) { String *test_filename = (String*)SSTR_WRAP_UTF8("_fstest", 7); FSFileHandle *fh; remove(Str_Get_Ptr8(test_filename)); fh = FSFH_open(test_filename, FH_CREATE | FH_WRITE_ONLY | FH_EXCLUSIVE); TEST_TRUE(runner, FSFH_Close(fh), "Close returns true for write-only"); DECREF(fh); // Simulate an OS error when closing the file descriptor. This // approximates what would happen if, say, we run out of disk space. remove(Str_Get_Ptr8(test_filename)); fh = FSFH_open(test_filename, FH_CREATE | FH_WRITE_ONLY | FH_EXCLUSIVE); #ifdef _MSC_VER SKIP(runner, "LUCY-155"); SKIP(runner, "LUCY-155"); #else int saved_fd = FSFH_IVARS(fh)->fd; FSFH_IVARS(fh)->fd = -1; Err_set_error(NULL); bool result = FSFH_Close(fh); TEST_FALSE(runner, result, "Failed Close() returns false"); TEST_TRUE(runner, Err_get_error() != NULL, "Failed Close() sets Err_error"); FSFH_IVARS(fh)->fd = saved_fd; #endif /* _MSC_VER */ DECREF(fh); fh = FSFH_open(test_filename, FH_READ_ONLY); TEST_TRUE(runner, FSFH_Close(fh), "Close returns true for read-only"); DECREF(fh); remove(Str_Get_Ptr8(test_filename)); }
bool FSFH_read(FSFileHandle *self, char *dest, int64_t offset, size_t len) { FSFileHandleIVARS *const ivars = FSFH_IVARS(self); BOOL check_val; DWORD got; OVERLAPPED read_op_state; uint64_t offs = (uint64_t)offset; read_op_state.hEvent = NULL; read_op_state.OffsetHigh = offs >> 32; read_op_state.Offset = offs & 0xFFFFFFFF; // Sanity check. if (offset < 0) { Err_set_error(Err_new(CB_newf("Can't read from an offset less than 0 (%i64)", offset))); return false; } // ReadFile() takes a DWORD (unsigned 32-bit integer) as a length // argument, so throw a sensible error rather than wrap around. if (len > UINT32_MAX) { Err_set_error(Err_new(CB_newf("Can't read more than 4 GB (%u64)", (uint64_t)len))); return false; } // Read. check_val = ReadFile(ivars->win_fhandle, dest, len, &got, &read_op_state); if (!check_val && GetLastError() == ERROR_IO_PENDING) { // Read has been queued by the OS and will soon complete. Wait for // it, since this is a blocking IO call from the point of the rest of // the library. check_val = GetOverlappedResult(ivars->win_fhandle, &read_op_state, &got, TRUE); } // Verify that the read has succeeded by now. if (!check_val) { char *win_error = Err_win_error(); Err_set_error(Err_new(CB_newf("Failed to read %u64 bytes: %s", (uint64_t)len, win_error))); FREEMEM(win_error); return false; } return true; }
bool FSFH_read(FSFileHandle *self, char *dest, int64_t offset, size_t len) { FSFileHandleIVARS *const ivars = FSFH_IVARS(self); const int64_t end = offset + len; if (ivars->flags & FH_WRITE_ONLY) { Err_set_error(Err_new(CB_newf("Can't read from write-only filehandle"))); return false; } if (offset < 0) { Err_set_error(Err_new(CB_newf("Can't read from an offset less than 0 (%i64)", offset))); return false; } else if (end > ivars->len) { Err_set_error(Err_new(CB_newf("Tried to read past EOF: offset %i64 + request %u64 > len %i64", offset, (uint64_t)len, ivars->len))); return false; } memcpy(dest, ivars->buf + offset, len); return true; }
bool FSFH_window(FSFileHandle *self, FileWindow *window, int64_t offset, int64_t len) { FSFileHandleIVARS *const ivars = FSFH_IVARS(self); const int64_t end = offset + len; if (!(ivars->flags & FH_READ_ONLY)) { Err_set_error(Err_new(CB_newf("Can't read from write-only handle"))); return false; } else if (offset < 0) { Err_set_error(Err_new(CB_newf("Can't read from negative offset %i64", offset))); return false; } else if (end > ivars->len) { Err_set_error(Err_new(CB_newf("Tried to read past EOF: offset %i64 + request %i64 > len %i64", offset, len, ivars->len))); return false; } else { return SI_window(self, ivars, window, offset, len); } }
bool FSFH_write(FSFileHandle *self, const void *data, size_t len) { FSFileHandleIVARS *const ivars = FSFH_IVARS(self); if (len) { // Write data, track file length, check for errors. int64_t check_val = write(ivars->fd, data, len); ivars->len += check_val; if ((size_t)check_val != len) { if (check_val == -1) { Err_set_error(Err_new(CB_newf("Error when writing %u64 bytes: %s", (uint64_t)len, strerror(errno)))); } else { Err_set_error(Err_new(CB_newf("Attempted to write %u64 bytes, but wrote %i64", (uint64_t)len, check_val))); } return false; } } return true; }
FSFileHandle* FSFH_do_open(FSFileHandle *self, const CharBuf *path, uint32_t flags) { FH_do_open((FileHandle*)self, path, flags); FSFileHandleIVARS *const ivars = FSFH_IVARS(self); if (!path || !CB_Get_Size(path)) { Err_set_error(Err_new(CB_newf("Missing required param 'path'"))); CFISH_DECREF(self); return NULL; } // Attempt to open file. if (flags & FH_WRITE_ONLY) { ivars->fd = open((char*)CB_Get_Ptr8(path), SI_posix_flags(flags), 0666); if (ivars->fd == -1) { ivars->fd = 0; Err_set_error(Err_new(CB_newf("Attempt to open '%o' failed: %s", path, strerror(errno)))); CFISH_DECREF(self); return NULL; } if (flags & FH_EXCLUSIVE) { ivars->len = 0; } else { // Derive length. ivars->len = lseek64(ivars->fd, INT64_C(0), SEEK_END); if (ivars->len == -1) { Err_set_error(Err_new(CB_newf("lseek64 on %o failed: %s", ivars->path, strerror(errno)))); CFISH_DECREF(self); return NULL; } else { int64_t check_val = lseek64(ivars->fd, INT64_C(0), SEEK_SET); if (check_val == -1) { Err_set_error(Err_new(CB_newf("lseek64 on %o failed: %s", ivars->path, strerror(errno)))); CFISH_DECREF(self); return NULL; } } } } else if (flags & FH_READ_ONLY) { if (SI_init_read_only(self, ivars)) { // On 64-bit systems, map the whole file up-front. if (IS_64_BIT && ivars->len) { ivars->buf = (char*)SI_map(self, ivars, 0, ivars->len); if (!ivars->buf) { // An error occurred during SI_map, which has set // Err_error for us already. CFISH_DECREF(self); return NULL; } } } else { CFISH_DECREF(self); return NULL; } } else { Err_set_error(Err_new(CB_newf("Must specify FH_READ_ONLY or FH_WRITE_ONLY to open '%o'", path))); CFISH_DECREF(self); return NULL; } return self; }
int64_t FSFH_length(FSFileHandle *self) { return FSFH_IVARS(self)->len; }