Status WaitUntilComplete(aiocb& cb, size_t queueDepth) { #if CONFIG2_FILE_ENABLE_AIO if(queueDepth > 1) { aiocb* const cbs = &cb; timespec* const timeout = 0; // infinite SUSPEND_AGAIN: errno = 0; const int ret = aio_suspend(&cbs, 1, timeout); if(ret != 0) { if(errno == EINTR) // interrupted by signal goto SUSPEND_AGAIN; WARN_RETURN(StatusFromErrno()); } const int err = aio_error(&cb); ENSURE(err != EINPROGRESS); // else aio_return is undefined ssize_t bytesTransferred = aio_return(&cb); if(bytesTransferred == -1) // transfer failed { errno = err; WARN_RETURN(StatusFromErrno()); } cb.aio_nbytes = (size_t)bytesTransferred; } #else UNUSED2(cb); UNUSED2(queueDepth); #endif return INFO::OK; }
Status Issue(aiocb& cb, size_t queueDepth) { #if CONFIG2_FILE_ENABLE_AIO if(queueDepth > 1) { const int ret = (cb.aio_lio_opcode == LIO_WRITE)? aio_write(&cb): aio_read(&cb); if(ret != 0) WARN_RETURN(StatusFromErrno()); } else #else UNUSED2(queueDepth); #endif { ENSURE(lseek(cb.aio_fildes, cb.aio_offset, SEEK_SET) == cb.aio_offset); void* buf = (void*)cb.aio_buf; // cast from volatile void* const ssize_t bytesTransferred = (cb.aio_lio_opcode == LIO_WRITE)? write(cb.aio_fildes, buf, cb.aio_nbytes) : read(cb.aio_fildes, buf, cb.aio_nbytes); if(bytesTransferred < 0) WARN_RETURN(StatusFromErrno()); cb.aio_nbytes = (size_t)bytesTransferred; } return INFO::OK; }
Status CreateDirectories(const OsPath& path, mode_t mode, bool breakpoint) { if(path.empty()) return INFO::OK; struct stat s; if(wstat(path, &s) == 0) { if(!S_ISDIR(s.st_mode)) // encountered a file WARN_RETURN(ERR::FAIL); return INFO::OK; } // If we were passed a path ending with '/', strip the '/' now so that // we can consistently use Parent to find parent directory names if(path.IsDirectory()) return CreateDirectories(path.Parent(), mode, breakpoint); RETURN_STATUS_IF_ERR(CreateDirectories(path.Parent(), mode)); errno = 0; if(wmkdir(path, mode) != 0) { debug_printf("CreateDirectories: failed to mkdir %s (mode %d)\n", path.string8().c_str(), mode); if (breakpoint) WARN_RETURN(StatusFromErrno()); else return StatusFromErrno(); } return INFO::OK; }
Status DeleteDirectory(const OsPath& path) { // note: we have to recursively empty the directory before it can // be deleted (required by Windows and POSIX rmdir()). CFileInfos files; DirectoryNames subdirectoryNames; RETURN_STATUS_IF_ERR(GetDirectoryEntries(path, &files, &subdirectoryNames)); // delete files for(size_t i = 0; i < files.size(); i++) { const OsPath pathname = path / files[i].Name(); errno = 0; if(wunlink(pathname) != 0) WARN_RETURN(StatusFromErrno()); } // recurse over subdirectoryNames for(size_t i = 0; i < subdirectoryNames.size(); i++) RETURN_STATUS_IF_ERR(DeleteDirectory(path / subdirectoryNames[i])); errno = 0; if(wrmdir(path) != 0) WARN_RETURN(StatusFromErrno()); return INFO::OK; }
// be careful not to use other tex_* APIs here because they call us. Status tex_validate(const Tex* t) { if(t->flags & TEX_UNDEFINED_FLAGS) WARN_RETURN(ERR::_1); // pixel data (only check validity if the image is still in memory; // ogl_tex frees the data after uploading to GL) if(t->data) { // file size smaller than header+pixels. // possible causes: texture file header is invalid, // or file wasn't loaded completely. if(t->dataSize < t->ofs + t->w*t->h*t->bpp/8) WARN_RETURN(ERR::_2); } // bits per pixel // (we don't bother checking all values; a sanity check is enough) if(t->bpp % 4 || t->bpp > 32) WARN_RETURN(ERR::_3); // flags // .. DXT value const size_t dxt = t->flags & TEX_DXT; if(dxt != 0 && dxt != 1 && dxt != DXT1A && dxt != 3 && dxt != 5) WARN_RETURN(ERR::_4); // .. orientation const size_t orientation = t->flags & TEX_ORIENTATION; if(orientation == (TEX_BOTTOM_UP|TEX_TOP_DOWN)) WARN_RETURN(ERR::_5); return INFO::OK; }
static Status Init() { HRESULT hr; hr = CoInitialize(0); ENSURE(hr == S_OK || hr == S_FALSE); // S_FALSE => already initialized hr = CoInitializeSecurity(0, -1, 0, 0, RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_IMP_LEVEL_IMPERSONATE, 0, EOAC_NONE, 0); if(FAILED(hr)) WARN_RETURN(ERR::_2); { IWbemLocatorPtr pLoc = 0; hr = CoCreateInstance(CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (void**)&pLoc); if(FAILED(hr)) WARN_RETURN(ERR::_3); hr = pLoc->ConnectServer(_bstr_t(L"ROOT\\CIMV2"), 0, 0, 0, 0, 0, 0, &pSvc); if(FAILED(hr)) return ERR::_4; // NOWARN (happens if WMI service is disabled) } hr = CoSetProxyBlanket(pSvc, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, 0, RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, 0, EOAC_NONE); if(FAILED(hr)) WARN_RETURN(ERR::_5); return INFO::OK; }
// check if the given texture format is acceptable: 8bpp grey, // 24bpp color or 32bpp color+alpha (BGR / upside down are permitted). // basically, this is the "plain" format understood by all codecs and // tex_codec_plain_transform. Status tex_validate_plain_format(size_t bpp, size_t flags) { const bool alpha = (flags & TEX_ALPHA ) != 0; const bool grey = (flags & TEX_GREY ) != 0; const bool dxt = (flags & TEX_DXT ) != 0; const bool mipmaps = (flags & TEX_MIPMAPS) != 0; if(dxt || mipmaps) WARN_RETURN(ERR::TEX_FMT_INVALID); // grey must be 8bpp without alpha, or it's invalid. if(grey) { if(bpp == 8 && !alpha) return INFO::OK; WARN_RETURN(ERR::TEX_FMT_INVALID); } if(bpp == 24 && !alpha) return INFO::OK; if(bpp == 32 && alpha) return INFO::OK; WARN_RETURN(ERR::TEX_FMT_INVALID); }
// @return INFO::OK iff text has been assigned a pointer (which the // caller must free via sys_clipboard_free) to the clipboard text. static Status GetClipboardText(wchar_t*& text) { // NB: Windows NT/2000+ auto convert CF_UNICODETEXT <-> CF_TEXT. if(!IsClipboardFormatAvailable(CF_UNICODETEXT)) return INFO::CANNOT_HANDLE; HGLOBAL hMem = GetClipboardData(CF_UNICODETEXT); if(!hMem) WARN_RETURN(ERR::FAIL); const wchar_t* lockedText = (const wchar_t*)GlobalLock(hMem); if(!lockedText) WARN_RETURN(ERR::NO_MEM); const size_t size = GlobalSize(hMem); text = (wchar_t*)malloc(size); if(!text) WARN_RETURN(ERR::NO_MEM); wcscpy_s(text, size/sizeof(wchar_t), lockedText); (void)GlobalUnlock(hMem); return INFO::OK; }
Status validate() const { const GLint A = 128; // no cursor is expected to get this big if(w > A || h > A || hotspotx > A || hotspoty > A) WARN_RETURN(ERR::_1); if(ht < 0) WARN_RETURN(ERR::_2); return INFO::OK; }
static Status type_validate(H_Type type) { if(!type) WARN_RETURN(ERR::INVALID_PARAM); if(type->user_size > HDATA_USER_SIZE) WARN_RETURN(ERR::LIMIT); if(type->name == 0) WARN_RETURN(ERR::INVALID_PARAM); return INFO::OK; }
Status mem_Release(u8* p, size_t size) { errno = 0; if(munmap(p, size) != 0) WARN_RETURN(StatusFromErrno()); return 0; }
static Status load_sys_cursor(const PIVFS& vfs, const VfsPath& pathname, int hx, int hy, sys_cursor* cursor) { #if !ALLOW_SYS_CURSOR UNUSED2(vfs); UNUSED2(pathname); UNUSED2(hx); UNUSED2(hy); UNUSED2(cursor); return ERR::FAIL; #else shared_ptr<u8> file; size_t fileSize; RETURN_STATUS_IF_ERR(vfs->LoadFile(pathname, file, fileSize)); Tex t; RETURN_STATUS_IF_ERR(t.decode(file, fileSize)); // convert to required BGRA format. const size_t flags = (t.m_Flags | TEX_BGR) & ~TEX_DXT; RETURN_STATUS_IF_ERR(t.transform_to(flags)); void* bgra_img = t.get_data(); if(!bgra_img) WARN_RETURN(ERR::FAIL); RETURN_STATUS_IF_ERR(sys_cursor_create((int)t.m_Width, (int)t.m_Height, bgra_img, hx, hy, cursor)); return INFO::OK; #endif }
static Handle key_find(uintptr_t key, H_Type type, KeyRemoveFlag remove_option = KEY_NOREMOVE) { Key2Idx* key2idx = key2idx_wrapper.get(); if(!key2idx) WARN_RETURN(ERR::NO_MEM); // initial return value: "not found at all, or it's of the // wrong type". the latter happens when called by h_alloc to // check if e.g. a Tex object already exists; at that time, // only the corresponding VFile exists. Handle ret = -1; std::pair<It, It> range = key2idx->equal_range(key); for(It it = range.first; it != range.second; ++it) { ssize_t idx = it->second; HDATA* hd; if(h_data_from_idx(idx, hd) != INFO::OK) continue; if(hd->type != type || hd->key != key) continue; // found a match if(remove_option == KEY_REMOVE) key2idx->erase(it); ret = hd->h; break; } key2idx_wrapper.lock(); return ret; }
static Status sys_cursor_create_common(int w, int h, void* bgra_img, void* mask_img, int hx, int hy, sys_cursor* cursor) { *cursor = 0; // MSDN says selecting this HBITMAP into a DC is slower since we use // CreateBitmap; bpp/format must be checked against those of the DC. // this is the simplest way and we don't care about slight performance // differences because this is typically only called once. HBITMAP hbmColor = CreateBitmap(w, h, 1, 32, bgra_img); // CreateIconIndirect doesn't access this; we just need to pass // an empty bitmap. HBITMAP hbmMask = CreateBitmap(w, h, 1, 1, mask_img); // create the cursor (really an icon; they differ only in // fIcon and the hotspot definitions). ICONINFO ii; ii.fIcon = FALSE; // cursor ii.xHotspot = (DWORD)hx; ii.yHotspot = (DWORD)hy; ii.hbmMask = hbmMask; ii.hbmColor = hbmColor; HICON hIcon = CreateIconIndirect(&ii); // CreateIconIndirect makes copies, so we no longer need these. DeleteObject(hbmMask); DeleteObject(hbmColor); if(!wutil_IsValidHandle(hIcon)) WARN_RETURN(ERR::FAIL); *cursor = cursor_from_HICON(hIcon); return INFO::OK; }
// split out of png_decode to simplify resource cleanup and avoid // "dtor / setjmp interaction" warning. static Status png_decode_impl(DynArray* da, png_structp png_ptr, png_infop info_ptr, Tex* t) { png_set_read_fn(png_ptr, da, io_read); // read header and determine format png_read_info(png_ptr, info_ptr); png_uint_32 w, h; int bit_depth, colour_type; png_get_IHDR(png_ptr, info_ptr, &w, &h, &bit_depth, &colour_type, 0, 0, 0); const size_t pitch = png_get_rowbytes(png_ptr, info_ptr); const u32 bpp = (u32)(pitch/w * 8); size_t flags = 0; if(bpp == 32) flags |= TEX_ALPHA; if(colour_type == PNG_COLOR_TYPE_GRAY) flags |= TEX_GREY; // make sure format is acceptable if(bit_depth != 8) WARN_RETURN(ERR::TEX_NOT_8BIT_PRECISION); if(colour_type & PNG_COLOR_MASK_PALETTE) WARN_RETURN(ERR::TEX_INVALID_COLOR_TYPE); const size_t img_size = pitch * h; shared_ptr<u8> data; AllocateAligned(data, img_size, pageSize); std::vector<RowPtr> rows = tex_codec_alloc_rows(data.get(), h, pitch, TEX_TOP_DOWN, 0); png_read_image(png_ptr, (png_bytepp)&rows[0]); png_read_end(png_ptr, info_ptr); // success; make sure all data was consumed. ENSURE(da->pos == da->cur_size); // store image info t->data = data; t->dataSize = img_size; t->ofs = 0; t->w = w; t->h = h; t->bpp = bpp; t->flags = flags; return INFO::OK; }
Status mem_Protect(u8* p, size_t size, int prot) { errno = 0; if(mprotect(p, size, prot) != 0) WARN_RETURN(StatusFromErrno()); return 0; }
// get HDATA for the given handle. // also verifies the tag field. // used by functions callable for any handle type, e.g. h_filename. static inline Status h_data_tag(Handle h, HDATA*& hd) { RETURN_STATUS_IF_ERR(h_data_no_tag(h, hd)); if(hd->key == 0) // HDATA was wiped out and hd->h overwritten by pool_free { if(ignoreDoubleFree) return ERR::H_ALREADY_FREED; // NOWARN (see ignoreDoubleFree) else WARN_RETURN(ERR::H_ALREADY_FREED); } if(h != hd->h) WARN_RETURN(ERR::H_TAG_MISMATCH); return INFO::OK; }
Status Open() { ov_callbacks callbacks; callbacks.read_func = Adapter::Read; callbacks.close_func = Adapter::Close; callbacks.seek_func = Adapter::Seek; callbacks.tell_func = Adapter::Tell; const int ret = ov_open_callbacks(&adapter, &vf, 0, 0, callbacks); if(ret != 0) WARN_RETURN(LibErrorFromVorbis(ret)); const int link = -1; // retrieve info for current bitstream info = ov_info(&vf, link); if(!info) WARN_RETURN(ERR::INVALID_HANDLE); return INFO::OK; }
Status GetDirectoryEntries(const OsPath& path, CFileInfos* files, DirectoryNames* subdirectoryNames) { // open directory errno = 0; WDIR* pDir = wopendir(path); if(!pDir) return StatusFromErrno(); // NOWARN shared_ptr<WDIR> osDir(pDir, DirDeleter()); for(;;) { errno = 0; struct wdirent* osEnt = wreaddir(osDir.get()); if(!osEnt) { // no error, just no more entries to return if(!errno) return INFO::OK; WARN_RETURN(StatusFromErrno()); } for(size_t i = 0; osEnt->d_name[i] != '\0'; i++) RETURN_STATUS_IF_ERR(Path::Validate(osEnt->d_name[i])); const OsPath name(osEnt->d_name); // get file information (mode, size, mtime) struct stat s; #if OS_WIN // .. return wdirent directly (much faster than calling stat). RETURN_STATUS_IF_ERR(wreaddir_stat_np(osDir.get(), &s)); #else // .. call regular stat(). errno = 0; const OsPath pathname = path / name; if(wstat(pathname, &s) != 0) WARN_RETURN(StatusFromErrno()); #endif if(files && S_ISREG(s.st_mode)) files->push_back(CFileInfo(name, s.st_size, s.st_mtime)); else if(subdirectoryNames && S_ISDIR(s.st_mode) && name != L"." && name != L"..") subdirectoryNames->push_back(name); } }
Status sys_generate_random_bytes(u8* buf, size_t count) { FILE* f = fopen("/dev/urandom", "rb"); if (!f) WARN_RETURN(ERR::FAIL); while (count) { size_t numread = fread(buf, 1, count, f); if (numread == 0) WARN_RETURN(ERR::FAIL); buf += numread; count -= numread; } fclose(f); return INFO::OK; }
Status mem_Commit(u8* p, size_t size, int prot) { // avoid misinterpretation by mmap. if(prot == PROT_NONE) WARN_RETURN(ERR::INVALID_PARAM); errno = 0; void* ret = mmap(p, size, prot, mmap_flags|MAP_FIXED, -1, 0); return StatusFromMap(ret); }
// get HDATA for the given handle. // only uses (and checks) the index field. // used by h_force_close (which must work regardless of tag). static inline Status h_data_no_tag(const Handle h, HDATA*& hd) { ssize_t idx = (ssize_t)h_idx(h); RETURN_STATUS_IF_ERR(h_data_from_idx(idx, hd)); // need to verify it's in range - h_data_from_idx can only verify that // it's < maximum allowable index. if(uintptr_t(hd) > uintptr_t(hpool.da.base)+hpool.da.pos) WARN_RETURN(ERR::H_IDX_UNUSED); return INFO::OK; }
// caller is responsible for freeing hMem. static Status SetClipboardText(const wchar_t* text, HGLOBAL& hMem) { const size_t numChars = wcslen(text); hMem = GlobalAlloc(GMEM_MOVEABLE|GMEM_ZEROINIT, (numChars + 1) * sizeof(wchar_t)); if(!hMem) WARN_RETURN(ERR::NO_MEM); wchar_t* lockedText = (wchar_t*)GlobalLock(hMem); if(!lockedText) WARN_RETURN(ERR::NO_MEM); wcscpy_s(lockedText, numChars+1, text); GlobalUnlock(hMem); HANDLE hData = SetClipboardData(CF_UNICODETEXT, hMem); if(!hData) // failed WARN_RETURN(ERR::FAIL); return INFO::OK; }
Status GetFileInfo(const OsPath& pathname, CFileInfo* pPtrInfo) { errno = 0; struct stat s; memset(&s, 0, sizeof(s)); if(wstat(pathname, &s) != 0) WARN_RETURN(StatusFromErrno()); *pPtrInfo = CFileInfo(pathname.Filename(), s.st_size, s.st_mtime); return INFO::OK; }
Status wmi_GetClass(const wchar_t* className, WmiMap& wmiMap) { RETURN_STATUS_IF_ERR(ModuleInit(&initState, Init)); IEnumWbemClassObjectPtr pEnum = 0; wchar_t query[200]; swprintf_s(query, ARRAY_SIZE(query), L"SELECT * FROM %ls", className); HRESULT hr = pSvc->ExecQuery(L"WQL", _bstr_t(query), WBEM_FLAG_FORWARD_ONLY|WBEM_FLAG_RETURN_IMMEDIATELY, 0, &pEnum); if(FAILED(hr)) WARN_RETURN(ERR::FAIL); ENSURE(pEnum); for(;;) { IWbemClassObjectPtr pObj = 0; ULONG numReturned = 0; hr = pEnum->Next((LONG)WBEM_INFINITE, 1, &pObj, &numReturned); if(FAILED(hr)) WARN_RETURN(ERR::FAIL); if(numReturned == 0) break; ENSURE(pEnum); pObj->BeginEnumeration(WBEM_FLAG_NONSYSTEM_ONLY); for(;;) { BSTR name; VARIANT value; VariantInit(&value); if(pObj->Next(0, &name, &value, 0, 0) != WBEM_S_NO_ERROR) { SysFreeString(name); break; } wmiMap[name] = value; SysFreeString(name); } } return INFO::OK; }
// get a (possibly new) array entry. // // fails if idx is out of bounds. static Status h_data_from_idx(ssize_t idx, HDATA*& hd) { // don't check if idx is beyond the current high-water mark, because // we might be allocating a new entry. subsequent tag checks protect // against using unallocated entries. if(size_t(idx) >= size_t(hdata_cap)) // also detects negative idx WARN_RETURN(ERR::H_IDX_INVALID); hd = (HDATA*)(hpool.da.base + idx*hpool.el_size); hd->num_derefs++; return INFO::OK; }
static Status InitStructures() { #if OS_WIN wfirmware::Table table; RETURN_STATUS_IF_ERR(GetTable(table)); #else std::vector<u8> table; return ERR::NOT_SUPPORTED; #endif // (instead of counting the total string size, just use the // SMBIOS size - typically 1-2 KB - as an upper bound.) stringStoragePos = stringStorage = (char*)calloc(table.size(), sizeof(char)); // freed in Cleanup if(!stringStorage) WARN_RETURN(ERR::NO_MEM); atexit(Cleanup); const Header* header = (const Header*)&table[0]; const Header* const end = (const Header*)(&table[0] + table.size()); for(;;) { if(header+1 > end) { debug_printf("SMBIOS: table not terminated\n"); break; } if(header->id == 127) // end break; if(header->length < sizeof(Header)) return ERR::_3; // NOWARN (happens on some unknown BIOS, see http://trac.wildfiregames.com/ticket/2985) const Header* next; const Strings strings = ExtractStrings(header, (const char*)end, next); switch(header->id) { #define STRUCTURE(name, id) case id: AddStructure(header, strings, structures.name##_); break; STRUCTURES #undef STRUCTURE default: if(32 < header->id && header->id < 126) // only mention non-proprietary structures of which we are not aware debug_printf("SMBIOS: unknown structure type %d\n", header->id); break; } header = next; } return INFO::OK; }
// get HDATA for the given handle. // also verifies the type. // used by most functions accessing handle data. static Status h_data_tag_type(const Handle h, const H_Type type, HDATA*& hd) { RETURN_STATUS_IF_ERR(h_data_tag(h, hd)); // h_alloc makes sure type isn't 0, so no need to check that here. if(hd->type != type) { debug_printf(L"h_mgr: expected type %ls, got %ls\n", hd->type->name, type->name); WARN_RETURN(ERR::H_TYPE_MISMATCH); } return INFO::OK; }
static Status ReadVersionString(const OsPath& modulePathname, wchar_t* out_ver, size_t out_ver_len) { WinScopedPreserveLastError s; // GetFileVersion*, Ver* // determine size of and allocate memory for version information. DWORD unused; const DWORD ver_size = GetFileVersionInfoSizeW(OsString(modulePathname).c_str(), &unused); // [bytes] if(!ver_size) { // check if the failure is due to not finding modulePathname // (necessary since GetFileVersionInfoSize doesn't SetLastError) HMODULE hModule = LoadLibraryExW(OsString(modulePathname).c_str(), 0, LOAD_LIBRARY_AS_DATAFILE); if(!hModule) return ERR::FAIL; // NOWARN (file not found - due to FS redirection?) FreeLibrary(hModule); return ERR::NOT_SUPPORTED; // NOWARN (module apparently lacks version information) } shared_ptr<u8> mem = Allocate(ver_size); if(!GetFileVersionInfoW(OsString(modulePathname).c_str(), 0, ver_size, mem.get())) WARN_RETURN(ERR::_3); u16* lang; // -> 16 bit language ID, 16 bit codepage UINT lang_len; const BOOL ok = VerQueryValueW(mem.get(), L"\\VarFileInfo\\Translation", (void**)&lang, &lang_len); if(!ok || !lang || lang_len != 4) WARN_RETURN(ERR::_4); wchar_t subblock[64]; swprintf_s(subblock, ARRAY_SIZE(subblock), L"\\StringFileInfo\\%04X%04X\\FileVersion", lang[0], lang[1]); const wchar_t* in_ver; UINT in_ver_len; if(!VerQueryValueW(mem.get(), subblock, (void**)&in_ver, &in_ver_len)) WARN_RETURN(ERR::_5); wcscpy_s(out_ver, out_ver_len, in_ver); return INFO::OK; }
static Status OpenFile(const OsPath& pathname, int oflag, HANDLE& hFile) { WinScopedPreserveLastError s; const DWORD access = DesiredAccess(oflag); const DWORD share = ShareMode(oflag); const DWORD create = CreationDisposition(oflag); const DWORD flags = FlagsAndAttributes(); hFile = CreateFileW(OsString(pathname).c_str(), access, share, 0, create, flags, 0); if(hFile == INVALID_HANDLE_VALUE) WARN_RETURN(StatusFromWin()); return INFO::OK; }