bool_t FSFH_read(FSFileHandle *self, char *dest, int64_t offset, size_t len) { 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(self->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; }
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; }
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_errno(const char *fmt, ...) { int cur_errno = errno; 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); const char *msg = ErrMsg_strerror(cur_errno); if (msg != NULL) { CB_Cat_Trusted_Utf8(buf, msg, strlen(msg)); } else { CB_catf(buf, "Unknown error: %i32", (int32_t)cur_errno); } Err_set_error(Err_new(CB_Yield_String(buf))); DECREF(buf); }
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; }
CharBuf* Json_to_json(Obj *dump) { // Validate object type, only allowing hashes and arrays per JSON spec. if (!dump || !(Obj_Is_A(dump, HASH) || Obj_Is_A(dump, VARRAY))) { if (!tolerant) { CharBuf *class_name = dump ? Obj_Get_Class_Name(dump) : NULL; CharBuf *mess = MAKE_MESS("Illegal top-level object type: %o", class_name); Err_set_error(Err_new(mess)); return NULL; } } // Encode. CharBuf *json = CB_new(31); if (!S_to_json(dump, json, 0)) { DECREF(json); ERR_ADD_FRAME(Err_get_error()); json = NULL; } else { // Append newline. CB_Cat_Trusted_Str(json, "\n", 1); } return json; }
InStream* CFReader_Local_Open_In_IMP(CompoundFileReader *self, String *name) { CompoundFileReaderIVARS *const ivars = CFReader_IVARS(self); Hash *entry = (Hash*)Hash_Fetch(ivars->records, name); if (!entry) { InStream *instream = Folder_Local_Open_In(ivars->real_folder, name); if (!instream) { ERR_ADD_FRAME(Err_get_error()); } return instream; } else { Obj *len = Hash_Fetch_Utf8(entry, "length", 6); Obj *offset = Hash_Fetch_Utf8(entry, "offset", 6); if (!len || !offset) { Err_set_error(Err_new(Str_newf("Malformed entry for '%o' in '%o'", name, Folder_Get_Path(ivars->real_folder)))); return NULL; } else if (Str_Get_Size(ivars->path)) { String *fullpath = Str_newf("%o/%o", ivars->path, name); InStream *instream = InStream_Reopen(ivars->instream, fullpath, Obj_To_I64(offset), Obj_To_I64(len)); DECREF(fullpath); return instream; } else { return InStream_Reopen(ivars->instream, name, Obj_To_I64(offset), Obj_To_I64(len)); } } }
InStream* CFReader_local_open_in(CompoundFileReader *self, const CharBuf *name) { Hash *entry = (Hash*)Hash_Fetch(self->records, (Obj*)name); if (!entry) { InStream *instream = Folder_Local_Open_In(self->real_folder, name); if (!instream) { ERR_ADD_FRAME(Err_get_error()); } return instream; } else { Obj *len = Hash_Fetch_Str(entry, "length", 6); Obj *offset = Hash_Fetch_Str(entry, "offset", 6); if (!len || !offset) { Err_set_error(Err_new(CB_newf("Malformed entry for '%o' in '%o'", name, Folder_Get_Path(self->real_folder)))); return NULL; } else if (CB_Get_Size(self->path)) { CharBuf *fullpath = CB_newf("%o/%o", self->path, name); InStream *instream = InStream_Reopen(self->instream, fullpath, Obj_To_I64(offset), Obj_To_I64(len)); DECREF(fullpath); return instream; } else { return InStream_Reopen(self->instream, name, Obj_To_I64(offset), Obj_To_I64(len)); } } }
bool_t FSFH_close(FSFileHandle *self) { // On 64-bit systems, cancel the whole-file mapping. if (IS_64_BIT && (self->flags & FH_READ_ONLY) && self->buf != NULL) { if (!SI_unmap(self, self->buf, self->len)) { return false; } self->buf = NULL; } // Close system-specific handles. if (self->fd) { if (close(self->fd)) { Err_set_error(Err_new(CB_newf("Failed to close file: %s", strerror(errno)))); return false; } self->fd = 0; } #if (defined(CHY_HAS_WINDOWS_H) && !defined(CHY_HAS_SYS_MMAN_H)) if (self->win_fhandle) { if (!SI_close_win_handles(self)) { return false; } } #endif return true; }
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; } }
FSDirHandle* FSDH_do_open(FSDirHandle *self, const CharBuf *dir) { size_t dir_path_size = CB_Get_Size(dir); char *dir_path_ptr = (char*)CB_Get_Ptr8(dir); char search_string[MAX_PATH + 1]; char *path_ptr = search_string; DH_init((DirHandle*)self, dir); FSDirHandleIVARS *const ivars = FSDH_IVARS(self); ivars->sys_dir_entry = MALLOCATE(sizeof(WIN32_FIND_DATA)); ivars->sys_dirhandle = INVALID_HANDLE_VALUE; ivars->saved_error = NULL; if (dir_path_size >= MAX_PATH - 2) { // Deal with Windows ceiling on file path lengths. Err_set_error(Err_new(CB_newf("Directory path is too long: %o", dir))); CFISH_DECREF(self); return NULL; } // Append trailing wildcard so Windows lists dir contents rather than just // the dir name itself. memcpy(path_ptr, dir_path_ptr, dir_path_size); memcpy(path_ptr + dir_path_size, "\\*\0", 3); ivars->sys_dirhandle = FindFirstFile(search_string, (WIN32_FIND_DATA*)ivars->sys_dir_entry); if (INVALID_HANDLE_VALUE == ivars->sys_dirhandle) { // Directory inaccessible or doesn't exist. Err_set_error(Err_new(CB_newf("Failed to open dir '%o'", dir))); CFISH_DECREF(self); return NULL; } else { // Compensate for the fact that FindFirstFile has already returned the // first entry but DirHandle's API requires that you call Next() to // start the iterator. ivars->delayed_iter = true; } return self; }
FileHandle* RAMFolder_local_open_filehandle(RAMFolder *self, const CharBuf *name, uint32_t flags) { RAMFileHandle *fh; CharBuf *fullpath = S_fullpath(self, name); RAMFile *file = (RAMFile*)Hash_Fetch(self->entries, (Obj*)name); bool_t can_create = (flags & (FH_WRITE_ONLY | FH_CREATE)) == (FH_WRITE_ONLY | FH_CREATE) ? true : false; // Make sure the filepath isn't a directory, and that it either exists // or we have permission to create it. if (file) { if (!RAMFile_Is_A(file, RAMFILE)) { Err_set_error(Err_new(CB_newf("Not a file: '%o'", fullpath))); DECREF(fullpath); return NULL; } } else if (!can_create) { Err_set_error(Err_new(CB_newf("File not found: '%o'", fullpath))); DECREF(fullpath); return NULL; } // Open the file and store it if it was just created. fh = RAMFH_open(fullpath, flags, file); if (fh) { if (!file) { file = RAMFH_Get_File(fh); Hash_Store(self->entries, (Obj*)name, INCREF(file)); } } else { Err *error = Err_get_error(); ERR_ADD_FRAME(error); } DECREF(fullpath); return (FileHandle*)fh; }
static bool S_hard_link(char *from8, char *to8) { if (-1 == link(from8, to8)) { Err_set_error(Err_new(Str_newf("hard link for new file '%s' from '%s' failed: %s", to8, from8, strerror(errno)))); return false; } else { return true; } }
static void* S_err_thread(void *arg) { TestBatchRunner *runner = (TestBatchRunner*)arg; TEST_TRUE(runner, Err_get_error() == NULL, "global error in thread initialized to null"); Err_set_error(Err_new(Str_newf("thread"))); return NULL; }
static INLINE bool_t 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", self->path, strerror(errno)))); return false; } } return true; }
static INLINE bool SI_init_read_only(FSFileHandle *self, FSFileHandleIVARS *ivars) { UNUSED_VAR(self); // Open. ivars->fd = open((char*)CB_Get_Ptr8(ivars->path), SI_posix_flags(ivars->flags), 0666); if (ivars->fd == -1) { ivars->fd = 0; Err_set_error(Err_new(CB_newf("Can't open '%o': %s", ivars->path, strerror(errno)))); return false; } // Derive len. 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)))); return false; } 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)))); return false; } } // Get system page size. #if defined(_SC_PAGESIZE) ivars->page_size = sysconf(_SC_PAGESIZE); #elif defined(_SC_PAGE_SIZE) ivars->page_size = sysconf(_SC_PAGE_SIZE); #else #error "Can't determine system memory page size" #endif return true; }
static bool S_create_dir(String *path) { bool retval = true; char *path_ptr = Str_To_Utf8(path); if (-1 == chy_makedir(path_ptr, 0777)) { Err_set_error(Err_new(Str_newf("Couldn't create directory '%o': %s", path, strerror(errno)))); retval = false; } FREEMEM(path_ptr); return retval; }
bool_t FSFH_read(FSFileHandle *self, char *dest, int64_t offset, size_t len) { const int64_t end = offset + len; if (self->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 > self->len) { Err_set_error(Err_new(CB_newf("Tried to read past EOF: offset %i64 + request %u64 > len %i64", offset, (uint64_t)len, self->len))); return false; } memcpy(dest, self->buf + offset, len); return true; }
bool_t FSFH_write(FSFileHandle *self, const void *data, size_t len) { if (len) { // Write data, track file length, check for errors. int64_t check_val = write(self->fd, data, len); self->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; }
bool_t CFReader_local_mkdir(CompoundFileReader *self, const CharBuf *name) { if (Hash_Fetch(self->records, (Obj*)name)) { Err_set_error(Err_new(CB_newf("Can't MkDir: '%o' exists", name))); return false; } else { bool_t result = Folder_Local_MkDir(self->real_folder, name); if (!result) { ERR_ADD_FRAME(Err_get_error()); } return result; } }
static Obj* S_parse_json(char *text, size_t size) { void *json_parser = LucyParseJsonAlloc(lucy_Memory_wrapped_malloc); if (json_parser == NULL) { CharBuf *mess = MAKE_MESS("Failed to allocate JSON parser"); Err_set_error(Err_new(mess)); return NULL; } Obj *dump = S_do_parse_json(json_parser, text, size); LucyParseJsonFree(json_parser, lucy_Memory_wrapped_free); return dump; }
void ErrMsg_set(const char *fmt, ...) { CharBuf *buf = CB_new(0); va_list args; va_start(args, fmt); CB_VCatF(buf, fmt, args); va_end(args); Err_set_error(Err_new(CB_Yield_String(buf))); DECREF(buf); }
bool Folder_mkdir(Folder *self, const CharBuf *path) { Folder *enclosing_folder = Folder_Enclosing_Folder(self, path); bool result = false; if (!CB_Get_Size(path)) { Err_set_error(Err_new(CB_newf("Invalid path: '%o'", path))); } else if (!enclosing_folder) { Err_set_error(Err_new(CB_newf("Can't recursively create dir %o", path))); } else { ZombieCharBuf *name = IxFileNames_local_part(path, ZCB_BLANK()); result = Folder_Local_MkDir(enclosing_folder, (CharBuf*)name); if (!result) { ERR_ADD_FRAME(Err_get_error()); } } return result; }
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; }
bool_t FSFH_window(FSFileHandle *self, FileWindow *window, int64_t offset, int64_t len) { const int64_t end = offset + len; if (!(self->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 > self->len) { Err_set_error(Err_new(CB_newf("Tried to read past EOF: offset %i64 + request %i64 > len %i64", offset, len, self->len))); return false; } else { return SI_window(self, window, offset, len); } }
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 CFReader_Local_MkDir_IMP(CompoundFileReader *self, String *name) { CompoundFileReaderIVARS *const ivars = CFReader_IVARS(self); if (Hash_Fetch(ivars->records, name)) { Err_set_error(Err_new(Str_newf("Can't MkDir: '%o' exists", name))); return false; } else { bool result = Folder_Local_MkDir(ivars->real_folder, name); if (!result) { ERR_ADD_FRAME(Err_get_error()); } return result; } }
bool FSFolder_Rename_IMP(FSFolder *self, String* from, String *to) { char *from_path = S_fullpath_ptr(self, from); char *to_path = S_fullpath_ptr(self, to); bool retval = !rename(from_path, to_path); if (!retval) { Err_set_error(Err_new(Str_newf("rename from '%s' to '%s' failed: %s", from_path, to_path, strerror(errno)))); } FREEMEM(from_path); FREEMEM(to_path); return retval; }