/** * @file * @brief Wrappers for sys/libc calls, mostly for charset purposes. **/ #include "AppHdr.h" #ifdef TARGET_OS_WINDOWS # ifdef TARGET_COMPILER_VC # include # endif # define WIN32_LEAN_AND_MEAN # include # include # include #else # include # include # include # include # include #endif #include "files.h" #include "random.h" #include "syscalls.h" #include "unicode.h" bool lock_file(int fd, bool write, bool wait) { #ifdef TARGET_OS_WINDOWS OVERLAPPED pos; pos.hEvent = 0; pos.Offset = 0; pos.OffsetHigh = 0; return !!LockFileEx((HANDLE)_get_osfhandle(fd), (write ? LOCKFILE_EXCLUSIVE_LOCK : 0) | (wait ? 0 : LOCKFILE_FAIL_IMMEDIATELY), 0, -1, -1, &pos); #else struct flock fl; fl.l_type = write ? F_WRLCK : F_RDLCK; fl.l_whence = SEEK_SET; fl.l_start = 0; fl.l_len = 0; return !fcntl(fd, wait ? F_SETLKW : F_SETLK, &fl); #endif } bool unlock_file(int fd) { #ifdef TARGET_OS_WINDOWS return !!UnlockFile((HANDLE)_get_osfhandle(fd), 0, 0, -1, -1); #else struct flock fl; fl.l_type = F_UNLCK; fl.l_whence = SEEK_SET; fl.l_start = 0; fl.l_len = 0; return !fcntl(fd, F_SETLK, &fl); #endif } bool read_urandom(char *buf, int len) { #ifdef TARGET_OS_WINDOWS HCRYPTPROV hProvider = 0; if (!CryptAcquireContextW(&hProvider, 0, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) { return false; } if (!CryptGenRandom(hProvider, len, (BYTE*)buf)) { CryptReleaseContext(hProvider, 0); return false; } CryptReleaseContext(hProvider, 0); return true; #else /* Try opening from various system provided (hopefully) CSPRNGs */ FILE* seed_f = fopen("/dev/urandom", "rb"); if (!seed_f) seed_f = fopen("/dev/random", "rb"); if (!seed_f) seed_f = fopen("/dev/srandom", "rb"); if (!seed_f) seed_f = fopen("/dev/arandom", "rb"); if (seed_f) { int res = fread(buf, 1, len, seed_f); fclose(seed_f); return res == len; } return false; #endif } #ifdef TARGET_OS_WINDOWS # ifndef UNIX // should check the presence of alarm() instead static void CALLBACK _abortion(PVOID dummy, BOOLEAN timedout) { TerminateProcess(GetCurrentProcess(), 0); } void alarm(unsigned int seconds) { HANDLE dummy; CreateTimerQueueTimer(&dummy, 0, _abortion, 0, seconds * 1000, 0, 0); } # endif # ifndef CRAWL_HAVE_FDATASYNC // implementation by Richard W.M. Jones // He claims this is the equivalent to fsync(), reading the MSDN doesn't seem // to show that vital metadata is indeed flushed, others report that at least // non-vital isn't. int fdatasync(int fd) { HANDLE h = (HANDLE)_get_osfhandle(fd); if (h == INVALID_HANDLE_VALUE) { errno = EBADF; return -1; } if (!FlushFileBuffers(h)) { /* Translate some Windows errors into rough approximations of Unix * errors. MSDN is useless as usual - in this case it doesn't * document the full range of errors. */ switch (GetLastError()) { /* eg. Trying to fsync a tty. */ case ERROR_INVALID_HANDLE: errno = EINVAL; break; default: errno = EIO; } return -1; } return 0; } # endif int mkstemp(char *dummy) { HANDLE fh; for (int tries = 0; tries < 100; tries++) { wchar_t filename[MAX_PATH]; int len = GetTempPathW(MAX_PATH - 8, filename); ASSERT(len); for (int i = 0; i < 6; i++) filename[len + i] = 'a' + random2(26); filename[len + 6] = 0; fh = CreateFileW(filename, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_FLAG_DELETE_ON_CLOSE | FILE_ATTRIBUTE_TEMPORARY, NULL); if (fh != INVALID_HANDLE_VALUE) return _open_osfhandle((intptr_t)fh, 0); } die("can't create temporary file in %%TMPDIR%%"); } #else // non-Windows # ifndef CRAWL_HAVE_FDATASYNC // At least MacOS X 10.6 has it (as required by Posix) but present only // as a symbol in the libraries without a proper header. int fdatasync(int fd) { # ifdef F_FULLFSYNC // On MacOS X, fsync() doesn't even try to actually do what it was asked. // Sane systems might have this problem only on disks that do write caching // but ignore flush requests. fsync() should never return before the disk // claims the flush completed, but this is not the case on OS X. // // Except, this is the case for internal drives only. For "external" ones, // F_FULLFSYNC is said to fail (at least on some versions of OS X), while // fsync() actually works. Thus, we need to try both. return fcntl(fd, F_FULLFSYNC, 0) && fsync(fd); # else return fsync(fd); # endif } # endif #endif // The old school way of doing short delays via low level I/O sync. // Good for systems like old versions of Solaris that don't have usleep. #ifdef NEED_USLEEP # ifdef TARGET_OS_WINDOWS void usleep(unsigned long time) { ASSERT(time > 0); ASSERT(!(time % 1000)); Sleep(time/1000); } # else #include #include #include void usleep(unsigned long time) { struct timeval timer; timer.tv_sec = (time / 1000000L); timer.tv_usec = (time % 1000000L); select(0, NULL, NULL, NULL, &timer); } # endif #endif bool file_exists(const string &name) { #ifdef TARGET_OS_WINDOWS DWORD lAttr = GetFileAttributesW(OUTW(name)); return lAttr != INVALID_FILE_ATTRIBUTES && !(lAttr & FILE_ATTRIBUTE_DIRECTORY); #else struct stat st; const int err = ::stat(OUTS(name), &st); return !err && S_ISREG(st.st_mode); #endif } // Low-tech existence check. bool dir_exists(const string &dir) { #ifdef TARGET_OS_WINDOWS DWORD lAttr = GetFileAttributesW(OUTW(dir)); return lAttr != INVALID_FILE_ATTRIBUTES && (lAttr & FILE_ATTRIBUTE_DIRECTORY); #elif defined(HAVE_STAT) struct stat st; const int err = ::stat(OUTS(dir), &st); return !err && S_ISDIR(st.st_mode); #else DIR *d = opendir(OUTS(dir)); const bool exists = !!d; if (d) closedir(d); return exists; #endif } static inline bool _is_good_filename(const string &s) { return s != "." && s != ".."; } // Returns the names of all files in the given directory. Note that the // filenames returned are relative to the directory. vector get_dir_files(const string &dirname) { vector files; #ifdef TARGET_OS_WINDOWS WIN32_FIND_DATAW lData; string dir = dirname; if (!dir.empty() && dir[dir.length() - 1] != FILE_SEPARATOR) dir += FILE_SEPARATOR; dir += "*"; HANDLE hFind = FindFirstFileW(OUTW(dir), &lData); if (hFind != INVALID_HANDLE_VALUE) { do { if (_is_good_filename(utf16_to_8(lData.cFileName))) files.push_back(utf16_to_8(lData.cFileName)); } while (FindNextFileW(hFind, &lData)); FindClose(hFind); } #else DIR *dir = opendir(OUTS(dirname)); if (!dir) return files; while (dirent *entry = readdir(dir)) { string name = mb_to_utf8(entry->d_name); if (_is_good_filename(name)) files.push_back(name); } closedir(dir); #endif return files; } int rename_u(const char *oldpath, const char *newpath) { #ifdef TARGET_OS_WINDOWS return !MoveFileExW(OUTW(oldpath), OUTW(newpath), MOVEFILE_REPLACE_EXISTING); #else return rename(OUTS(oldpath), OUTS(newpath)); #endif } int unlink_u(const char *pathname) { #ifdef TARGET_OS_WINDOWS return _wunlink(OUTW(pathname)); #else return unlink(OUTS(pathname)); #endif } FILE *fopen_u(const char *path, const char *mode) { #ifdef TARGET_OS_WINDOWS // Why it wants the mode string as double-byte is beyond me. return _wfopen(OUTW(path), OUTW(mode)); #else return fopen(OUTS(path), mode); #endif } int mkdir_u(const char *pathname, mode_t mode) { #ifdef TARGET_OS_WINDOWS return _wmkdir(OUTW(pathname)); #else return mkdir(OUTS(pathname), mode); #endif } int open_u(const char *pathname, int flags, mode_t mode) { #ifdef TARGET_OS_WINDOWS return _wopen(OUTW(pathname), flags, mode); #else return open(OUTS(pathname), flags, mode); #endif }