static INLINE bool_t SI_close_win_handles(FSFileHandle *self) { // Close both standard handle and mapping handle. if (self->win_maphandle) { if (!CloseHandle(self->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; } self->win_maphandle = NULL; } if (self->win_fhandle) { if (!CloseHandle(self->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; } self->win_fhandle = NULL; } return true; }
static INLINE bool_t SI_init_read_only(FSFileHandle *self) { LARGE_INTEGER large_int; char *filepath = (char*)CB_Get_Ptr8(self->path); SYSTEM_INFO sys_info; // Get system page size. GetSystemInfo(&sys_info); self->page_size = sys_info.dwAllocationGranularity; // Open. self->win_fhandle = CreateFile( filepath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY | FILE_FLAG_OVERLAPPED, NULL ); if (self->win_fhandle == INVALID_HANDLE_VALUE) { char *win_error = Err_win_error(); Err_set_error(Err_new(CB_newf("CreateFile for %o failed: %s", self->path, win_error))); FREEMEM(win_error); return false; } // Derive len. GetFileSizeEx(self->win_fhandle, &large_int); self->len = large_int.QuadPart; if (self->len < 0) { Err_set_error(Err_new(CB_newf( "GetFileSizeEx for %o returned a negative length: '%i64'", self->path, self->len))); return false; } // Init mapping handle. self->buf = NULL; if (self->len) { self->win_maphandle = CreateFileMapping(self->win_fhandle, NULL, PAGE_READONLY, 0, 0, NULL); if (self->win_maphandle == NULL) { char *win_error = Err_win_error(); Err_set_error(Err_new(CB_newf( "CreateFileMapping for %o failed: %s", self->path, win_error))); FREEMEM(win_error); return false; } } return true; }
static INLINE bool SI_init_read_only(FSFileHandle *self, FSFileHandleIVARS *ivars) { char *filepath = (char*)CB_Get_Ptr8(ivars->path); SYSTEM_INFO sys_info; // Get system page size. GetSystemInfo(&sys_info); ivars->page_size = sys_info.dwAllocationGranularity; // Open. ivars->win_fhandle = CreateFile( filepath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY | FILE_FLAG_OVERLAPPED, NULL ); if (ivars->win_fhandle == INVALID_HANDLE_VALUE) { char *win_error = Err_win_error(); Err_set_error(Err_new(CB_newf("CreateFile for %o failed: %s", ivars->path, win_error))); FREEMEM(win_error); return false; } // Derive len. DWORD file_size_hi; DWORD file_size_lo = GetFileSize(ivars->win_fhandle, &file_size_hi); if (file_size_lo == INVALID_FILE_SIZE && GetLastError() != NO_ERROR) { Err_set_error(Err_new(CB_newf("GetFileSize for %o failed", ivars->path))); return false; } ivars->len = ((uint64_t)file_size_hi << 32) | file_size_lo; // Init mapping handle. ivars->buf = NULL; if (ivars->len) { ivars->win_maphandle = CreateFileMapping(ivars->win_fhandle, NULL, PAGE_READONLY, 0, 0, NULL); if (ivars->win_maphandle == NULL) { char *win_error = Err_win_error(); Err_set_error(Err_new(CB_newf("CreateFileMapping for %o failed: %s", ivars->path, win_error))); FREEMEM(win_error); return false; } } return true; }
static INLINE void* SI_map(FSFileHandle *self, int64_t offset, int64_t len) { void *buf = NULL; if (len) { // Read-only memory map. uint64_t offs = (uint64_t)offset; DWORD file_offset_hi = offs >> 32; DWORD file_offset_lo = offs & 0xFFFFFFFF; size_t amount = (size_t)len; buf = MapViewOfFile( self->win_maphandle, FILE_MAP_READ, file_offset_hi, file_offset_lo, amount ); if (buf == NULL) { char *win_error = Err_win_error(); Err_set_error(Err_new(CB_newf( "MapViewOfFile for %o failed: %s", self->path, win_error))); FREEMEM(win_error); } } return buf; }
bool FSDH_close(FSDirHandle *self) { FSDirHandleIVARS *const ivars = FSDH_IVARS(self); if (ivars->sys_dirhandle && ivars->sys_dirhandle != INVALID_HANDLE_VALUE) { HANDLE dirhandle = (HANDLE)ivars->sys_dirhandle; ivars->sys_dirhandle = NULL; if (dirhandle != INVALID_HANDLE_VALUE && !FindClose(dirhandle)) { if (!ivars->saved_error) { char *win_error = Err_win_error(); ivars->saved_error = Err_new(CB_newf("Error while closing directory: %s", win_error)); FREEMEM(win_error); } } } if (ivars->sys_dir_entry) { FREEMEM(ivars->sys_dir_entry); ivars->sys_dir_entry = NULL; } // If we encountered an error condition previously, report it now. if (ivars->saved_error) { Err_set_error((Err*)CFISH_INCREF(ivars->saved_error)); return false; } else { return true; } }
static INLINE bool_t SI_unmap(FSFileHandle *self, char *buf, 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", self->path, win_error))); FREEMEM(win_error); return false; } } return true; }
static bool S_hard_link(char *from8, char *to8) { if (CreateHardLink(to8, from8, NULL)) { return true; } else { char *win_error = Err_win_error(); Err_set_error(Err_new(Str_newf("CreateHardLink for new file '%s' from '%s' failed: %s", to8, from8, win_error))); FREEMEM(win_error); return false; } }
bool_t FSFH_read(FSFileHandle *self, char *dest, int64_t offset, size_t len) { 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 > U32_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(self->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(self->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; }
void ErrMsg_set_with_win_error(const char *fmt, ...) { char *win_error = Err_win_error(); CharBuf *buf = CB_new(0); va_list args; va_start(args, fmt); CB_VCatF(buf, fmt, args); va_end(args); CB_Cat_Trusted_Utf8(buf, ": ", 2); CB_Cat_Utf8(buf, win_error, strlen(win_error)); Err_set_error(Err_new(CB_Yield_String(buf))); DECREF(buf); FREEMEM(win_error); }
bool FSDH_next(FSDirHandle *self) { FSDirHandleIVARS *const ivars = FSDH_IVARS(self); HANDLE dirhandle = (HANDLE)ivars->sys_dirhandle; WIN32_FIND_DATA *find_data = (WIN32_FIND_DATA*)ivars->sys_dir_entry; // Attempt to move forward or absorb cached iter. if (!dirhandle || dirhandle == INVALID_HANDLE_VALUE) { return false; } else if (ivars->delayed_iter) { ivars->delayed_iter = false; } else if ((FindNextFile(dirhandle, find_data) == 0)) { // Iterator exhausted. Verify that no errors were encountered. CB_Set_Size(ivars->entry, 0); if (GetLastError() != ERROR_NO_MORE_FILES) { char *win_error = Err_win_error(); ivars->saved_error = Err_new(CB_newf("Error while traversing directory: %s", win_error)); FREEMEM(win_error); } return false; } // Process the results of the iteration. size_t len = strlen(find_data->cFileName); if (SI_is_updir(find_data->cFileName, len)) { return FSDH_Next(self); } else { CB_Mimic_Str(ivars->entry, find_data->cFileName, len); return true; } }