char * tr_sys_path_resolve (const char * path, tr_error ** error) { char * ret = NULL; wchar_t * wide_path; wchar_t * wide_ret = NULL; HANDLE handle; DWORD wide_ret_size; assert (path != NULL); wide_path = tr_win32_utf8_to_native (path, -1); if (wide_path == NULL) goto fail; handle = CreateFileW (wide_path, FILE_READ_EA, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); if (handle == INVALID_HANDLE_VALUE) goto fail; wide_ret_size = GetFinalPathNameByHandleW (handle, NULL, 0, 0); if (wide_ret_size == 0) goto fail; wide_ret = tr_new (wchar_t, wide_ret_size); if (GetFinalPathNameByHandleW (handle, wide_ret, wide_ret_size, 0) != wide_ret_size - 1) goto fail; /* Resolved path always begins with "\\?\", so skip those first four chars. */ ret = tr_win32_native_to_utf8 (wide_ret + 4, -1); if (ret != NULL) goto cleanup; fail: set_system_error (error, GetLastError ()); tr_free (ret); ret = NULL; cleanup: tr_free (wide_ret); tr_free (wide_path); if (handle != INVALID_HANDLE_VALUE) CloseHandle (handle); return ret; }
/* Convert an utf-8 file name to NT file name, return converted name length in characters, no NULL terminator is appended */ static int filename_to_nt_pathname(const char *filename, WCHAR *buf, int buf_size) { HANDLE basedir_handle = CreateFileW(L".", 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); if (basedir_handle == INVALID_HANDLE_VALUE) return 0; WCHAR basedir[PATH_MAX]; DWORD basedir_len = GetFinalPathNameByHandleW(basedir_handle, basedir, PATH_MAX, FILE_NAME_NORMALIZED); CloseHandle(basedir_handle); if (basedir_len > PATH_MAX) return 0; basedir[1] = L'?'; if (buf_size < basedir_len) return 0; memcpy(buf, basedir, basedir_len * sizeof(WCHAR)); buf += basedir_len; int out_size = basedir_len; buf_size -= basedir_len; if (filename[0] == 0) return out_size; if (buf_size < 1) return 0; *buf++ = L'\\'; out_size++; buf_size--; int fl = utf8_to_utf16_filename(filename, strlen(filename), buf, buf_size); if (fl == 0) return 0; return out_size + fl; }
static posix_errno_t internal_read_link(const efile_path_t *path, efile_path_t *result) { DWORD required_length, actual_length; HANDLE link_handle; DWORD last_error; link_handle = CreateFileW((WCHAR*)path->data, GENERIC_READ, FILE_SHARE_FLAGS, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); last_error = GetLastError(); if(link_handle == INVALID_HANDLE_VALUE) { return windows_to_posix_errno(last_error); } required_length = GetFinalPathNameByHandleW(link_handle, NULL, 0, 0); last_error = GetLastError(); if(required_length <= 0) { CloseHandle(link_handle); return windows_to_posix_errno(last_error); } /* Unlike many other path functions (eg. GetFullPathNameW), this one * includes the NUL terminator in its required length. */ if(!enif_alloc_binary(required_length * sizeof(WCHAR), result)) { CloseHandle(link_handle); return ENOMEM; } actual_length = GetFinalPathNameByHandleW(link_handle, (WCHAR*)result->data, required_length, 0); last_error = GetLastError(); CloseHandle(link_handle); if(actual_length == 0 || actual_length >= required_length) { enif_release_binary(result); return windows_to_posix_errno(last_error); } /* GetFinalPathNameByHandle always prepends with "\\?\" and NUL-terminates, * so we never have to touch-up the resulting path. */ ASSERT_PATH_FORMAT(result); return 0; }
/* * @implemented */ DWORD WINAPI GetFinalPathNameByHandleA(IN HANDLE hFile, OUT LPSTR lpszFilePath, IN DWORD cchFilePath, IN DWORD dwFlags) { WCHAR FilePathW[MAX_PATH]; UNICODE_STRING FilePathU; DWORD PrevLastError; DWORD Ret = 0; if (cchFilePath != 0 && cchFilePath > sizeof(FilePathW) / sizeof(FilePathW[0])) { FilePathU.Length = 0; FilePathU.MaximumLength = (USHORT)cchFilePath * sizeof(WCHAR); FilePathU.Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, FilePathU.MaximumLength); if (FilePathU.Buffer == NULL) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); return 0; } } else { FilePathU.Length = 0; FilePathU.MaximumLength = sizeof(FilePathW); FilePathU.Buffer = FilePathW; } /* save the last error code */ PrevLastError = GetLastError(); SetLastError(ERROR_SUCCESS); /* call the unicode version that does all the work */ Ret = GetFinalPathNameByHandleW(hFile, FilePathU.Buffer, cchFilePath, dwFlags); if (GetLastError() == ERROR_SUCCESS) { /* no error, restore the last error code and convert the string */ SetLastError(PrevLastError); Ret = FilenameU2A_FitOrFail(lpszFilePath, cchFilePath, &FilePathU); } /* free allocated memory if necessary */ if (FilePathU.Buffer != FilePathW) { RtlFreeHeap(RtlGetProcessHeap(), 0, FilePathU.Buffer); } return Ret; }
static int _g_win32_stat_utf16_no_trailing_slashes (const gunichar2 *filename, int fd, GWin32PrivateStat *buf, gboolean for_symlink) { HANDLE file_handle; gboolean succeeded_so_far; DWORD error_code; struct __stat64 statbuf; BY_HANDLE_FILE_INFORMATION handle_info; FILE_STANDARD_INFO std_info; WIN32_FIND_DATAW finddata; DWORD immediate_attributes; gboolean is_symlink = FALSE; gboolean is_directory; DWORD open_flags; wchar_t *filename_target = NULL; int result; if (fd < 0) { immediate_attributes = GetFileAttributesW (filename); if (immediate_attributes == INVALID_FILE_ATTRIBUTES) { error_code = GetLastError (); errno = w32_error_to_errno (error_code); return -1; } is_symlink = (immediate_attributes & FILE_ATTRIBUTE_REPARSE_POINT) == FILE_ATTRIBUTE_REPARSE_POINT; is_directory = (immediate_attributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY; open_flags = FILE_ATTRIBUTE_NORMAL; if (for_symlink && is_symlink) open_flags |= FILE_FLAG_OPEN_REPARSE_POINT; if (is_directory) open_flags |= FILE_FLAG_BACKUP_SEMANTICS; file_handle = CreateFileW (filename, FILE_READ_ATTRIBUTES, FILE_SHARE_READ, NULL, OPEN_EXISTING, open_flags, NULL); if (file_handle == INVALID_HANDLE_VALUE) { error_code = GetLastError (); errno = w32_error_to_errno (error_code); return -1; } } else { file_handle = (HANDLE) _get_osfhandle (fd); if (file_handle == INVALID_HANDLE_VALUE) return -1; } succeeded_so_far = GetFileInformationByHandle (file_handle, &handle_info); error_code = GetLastError (); if (succeeded_so_far) { succeeded_so_far = GetFileInformationByHandleEx (file_handle, FileStandardInfo, &std_info, sizeof (std_info)); error_code = GetLastError (); } if (!succeeded_so_far) { if (fd < 0) CloseHandle (file_handle); errno = w32_error_to_errno (error_code); return -1; } /* It's tempting to use GetFileInformationByHandleEx(FileAttributeTagInfo), * but it always reports that the ReparseTag is 0. */ if (fd < 0) { memset (&finddata, 0, sizeof (finddata)); if (handle_info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) { HANDLE tmp = FindFirstFileW (filename, &finddata); if (tmp == INVALID_HANDLE_VALUE) { error_code = GetLastError (); errno = w32_error_to_errno (error_code); CloseHandle (file_handle); return -1; } FindClose (tmp); } if (is_symlink && !for_symlink) { /* If filename is a symlink, but we need the target. * To get information about the target we need to resolve * the symlink first. */ DWORD filename_target_len; DWORD new_len; /* Just in case, give it a real memory location instead of NULL */ new_len = GetFinalPathNameByHandleW (file_handle, (wchar_t *) &filename_target_len, 0, FILE_NAME_NORMALIZED); #define SANE_LIMIT 1024 * 10 if (new_len >= SANE_LIMIT) #undef SANE_LIMIT { new_len = 0; error_code = ERROR_BUFFER_OVERFLOW; } else if (new_len == 0) { error_code = GetLastError (); } if (new_len > 0) { /* Pretend that new_len doesn't count the terminating NUL char, * and ask for a bit more space than is needed, and allocate even more. */ filename_target_len = new_len + 3; filename_target = g_malloc ((filename_target_len + 1) * sizeof (wchar_t)); new_len = GetFinalPathNameByHandleW (file_handle, filename_target, filename_target_len, FILE_NAME_NORMALIZED); /* filename_target_len is already larger than needed, * new_len should be smaller than that, even if the size * is off by 1 for some reason. */ if (new_len >= filename_target_len - 1) { new_len = 0; error_code = ERROR_BUFFER_OVERFLOW; g_clear_pointer (&filename_target, g_free); } else if (new_len == 0) { g_clear_pointer (&filename_target, g_free); } /* GetFinalPathNameByHandle() is documented to return extended paths, * strip the extended prefix, if it is followed by a drive letter * and a colon. Otherwise keep it (the path could be * \\\\?\\Volume{GUID}\\ - it's only usable in extended form). */ else if (new_len > 0) { gsize len = new_len; /* Account for NUL-terminator maybe not being counted. * This is why we overallocated earlier. */ if (filename_target[len] != L'\0') { len++; filename_target[len] = L'\0'; } _g_win32_strip_extended_ntobjm_prefix (filename_target, &len); new_len = len; } } if (new_len == 0) succeeded_so_far = FALSE; } CloseHandle (file_handle); } /* else if fd >= 0 the file_handle was obtained via _get_osfhandle() * and must not be closed, it is owned by fd. */ if (!succeeded_so_far) { errno = w32_error_to_errno (error_code); return -1; } /* * We can't use _wstat64() here, because with UCRT it now gives * information about the target, even if we want information about * the link itself (unlike MSVCRT, which gave information about * the link, and if we needed information about the target we were * able to resolve it by ourselves prior to calling _wstat64()). */ if (fd < 0) result = _g_win32_fill_statbuf_from_handle_info (filename, filename_target, &handle_info, &statbuf); else result = _fstat64 (fd, &statbuf); if (result != 0) { int errsv = errno; g_free (filename_target); errno = errsv; return -1; } g_free (filename_target); buf->st_dev = statbuf.st_dev; buf->st_mode = statbuf.st_mode; buf->volume_serial = handle_info.dwVolumeSerialNumber; buf->file_index = (((guint64) handle_info.nFileIndexHigh) << 32) | handle_info.nFileIndexLow; /* Note that immediate_attributes is for the symlink * (if it's a symlink), while handle_info contains info * about the symlink or the target, depending on the flags * we used earlier. */ buf->attributes = handle_info.dwFileAttributes; buf->st_nlink = handle_info.nNumberOfLinks; buf->st_size = (((guint64) handle_info.nFileSizeHigh) << 32) | handle_info.nFileSizeLow; buf->allocated_size = std_info.AllocationSize.QuadPart; if (fd < 0 && buf->attributes & FILE_ATTRIBUTE_REPARSE_POINT) buf->reparse_tag = finddata.dwReserved0; else buf->reparse_tag = 0; buf->st_ctime = statbuf.st_ctime; buf->st_atime = statbuf.st_atime; buf->st_mtime = statbuf.st_mtime; return 0; }
w_string FileDescriptor::getOpenedPath() const { #if defined(F_GETPATH) // macOS. The kernel interface only allows MAXPATHLEN char buf[MAXPATHLEN + 1]; if (fcntl(fd_, F_GETPATH, buf) == -1) { throw std::system_error(errno, std::generic_category(), "fcntl for getOpenedPath"); } return w_string(buf); #elif defined(__linux__) || defined(__sun) char procpath[1024]; #if defined(__linux__) snprintf(procpath, sizeof(procpath), "/proc/%d/fd/%d", getpid(), fd_); #elif defined(__sun) snprintf(procpath, sizeof(procpath), "/proc/%d/path/%d", getpid(), fd_); #endif // Avoid an extra stat by speculatively attempting to read into // a reasonably sized buffer. char buf[WATCHMAN_NAME_MAX]; auto len = readlink(procpath, buf, sizeof(buf)); if (len == sizeof(buf)) { len = -1; // We need to stat it to discover the required length errno = ENAMETOOLONG; } if (len >= 0) { return w_string(buf, len); } if (errno == ENOENT) { // For this path to not exist must mean that /proc is not mounted. // Report this with an actionable message throw std::system_error(ENOSYS, std::generic_category(), "getOpenedPath: need /proc to be mounted!"); } if (errno != ENAMETOOLONG) { throw std::system_error(errno, std::generic_category(), "readlink for getOpenedPath"); } // Figure out how much space we need struct stat st; if (fstat(fd_, &st)) { throw std::system_error(errno, std::generic_category(), "fstat for getOpenedPath"); } std::string result; result.resize(st.st_size + 1, 0); len = readlink(procpath, &result[0], result.size()); if (len == int(result.size())) { // It's longer than we expected; TOCTOU detected! throw std::system_error( ENAMETOOLONG, std::generic_category(), "readlinkat: link contents grew while examining file"); } if (len >= 0) { return w_string(&result[0], len); } throw std::system_error(errno, std::generic_category(), "readlink for getOpenedPath"); #elif defined(_WIN32) std::wstring wchar; wchar.resize(WATCHMAN_NAME_MAX); auto len = GetFinalPathNameByHandleW( (HANDLE)fd_, &wchar[0], wchar.size(), FILE_NAME_NORMALIZED | VOLUME_NAME_DOS); auto err = GetLastError(); if (len >= wchar.size()) { // Grow it wchar.resize(len); len = GetFinalPathNameByHandleW( (HANDLE)fd_, &wchar[0], len, FILE_NAME_NORMALIZED | VOLUME_NAME_DOS); err = GetLastError(); } if (len == 0) { throw std::system_error( GetLastError(), std::system_category(), "GetFinalPathNameByHandleW"); } return w_string(wchar.data(), len); #else throw std::system_error(ENOSYS, std::generic_category(), "getOpenedPath not implemented on this platform"); #endif }
static NTSTATUS PtfsCreate(PWSTR Path, PWSTR VolumePrefix, PWSTR MountPoint, UINT32 DebugFlags, PTFS **PPtfs) { WCHAR FullPath[MAX_PATH]; ULONG Length; HANDLE Handle; FILETIME CreationTime; DWORD LastError; FSP_FSCTL_VOLUME_PARAMS VolumeParams; PTFS *Ptfs = 0; NTSTATUS Result; *PPtfs = 0; Handle = CreateFileW( Path, FILE_READ_ATTRIBUTES, 0, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0); if (INVALID_HANDLE_VALUE == Handle) return FspNtStatusFromWin32(GetLastError()); Length = GetFinalPathNameByHandleW(Handle, FullPath, FULLPATH_SIZE - 1, 0); if (0 == Length) { LastError = GetLastError(); CloseHandle(Handle); return FspNtStatusFromWin32(LastError); } if (L'\\' == FullPath[Length - 1]) FullPath[--Length] = L'\0'; if (!GetFileTime(Handle, &CreationTime, 0, 0)) { LastError = GetLastError(); CloseHandle(Handle); return FspNtStatusFromWin32(LastError); } CloseHandle(Handle); /* from now on we must goto exit on failure */ Ptfs = malloc(sizeof *Ptfs); if (0 == Ptfs) { Result = STATUS_INSUFFICIENT_RESOURCES; goto exit; } memset(Ptfs, 0, sizeof *Ptfs); Length = (Length + 1) * sizeof(WCHAR); Ptfs->Path = malloc(Length); if (0 == Ptfs->Path) { Result = STATUS_INSUFFICIENT_RESOURCES; goto exit; } memcpy(Ptfs->Path, FullPath, Length); memset(&VolumeParams, 0, sizeof VolumeParams); VolumeParams.SectorSize = ALLOCATION_UNIT; VolumeParams.SectorsPerAllocationUnit = 1; VolumeParams.VolumeCreationTime = ((PLARGE_INTEGER)&CreationTime)->QuadPart; VolumeParams.VolumeSerialNumber = 0; VolumeParams.FileInfoTimeout = 1000; VolumeParams.CaseSensitiveSearch = 0; VolumeParams.CasePreservedNames = 1; VolumeParams.UnicodeOnDisk = 1; VolumeParams.PersistentAcls = 1; VolumeParams.PostCleanupWhenModifiedOnly = 1; VolumeParams.PassQueryDirectoryPattern = 1; VolumeParams.FlushAndPurgeOnCleanup = 1; VolumeParams.UmFileContextIsUserContext2 = 1; if (0 != VolumePrefix) wcscpy_s(VolumeParams.Prefix, sizeof VolumeParams.Prefix / sizeof(WCHAR), VolumePrefix); wcscpy_s(VolumeParams.FileSystemName, sizeof VolumeParams.FileSystemName / sizeof(WCHAR), L"" PROGNAME); Result = FspFileSystemCreate( VolumeParams.Prefix[0] ? L"" FSP_FSCTL_NET_DEVICE_NAME : L"" FSP_FSCTL_DISK_DEVICE_NAME, &VolumeParams, &PtfsInterface, &Ptfs->FileSystem); if (!NT_SUCCESS(Result)) goto exit; Ptfs->FileSystem->UserContext = Ptfs; Result = FspFileSystemSetMountPoint(Ptfs->FileSystem, MountPoint); if (!NT_SUCCESS(Result)) goto exit; FspFileSystemSetDebugLog(Ptfs->FileSystem, DebugFlags); Result = STATUS_SUCCESS; exit: if (NT_SUCCESS(Result)) *PPtfs = Ptfs; else if (0 != Ptfs) PtfsDelete(Ptfs); return Result; }
static NTSTATUS ReadDirectory(FSP_FILE_SYSTEM *FileSystem, PVOID FileContext0, PWSTR Pattern, PWSTR Marker, PVOID Buffer, ULONG BufferLength, PULONG PBytesTransferred) { PTFS *Ptfs = (PTFS *)FileSystem->UserContext; PTFS_FILE_CONTEXT *FileContext = FileContext0; HANDLE Handle = HandleFromContext(FileContext); WCHAR FullPath[FULLPATH_SIZE]; ULONG Length, PatternLength; HANDLE FindHandle; WIN32_FIND_DATAW FindData; union { UINT8 B[FIELD_OFFSET(FSP_FSCTL_DIR_INFO, FileNameBuf) + MAX_PATH * sizeof(WCHAR)]; FSP_FSCTL_DIR_INFO D; } DirInfoBuf; FSP_FSCTL_DIR_INFO *DirInfo = &DirInfoBuf.D; NTSTATUS DirBufferResult; DirBufferResult = STATUS_SUCCESS; if (FspFileSystemAcquireDirectoryBuffer(&FileContext->DirBuffer, 0 == Marker, &DirBufferResult)) { if (0 == Pattern) Pattern = L"*"; PatternLength = (ULONG)wcslen(Pattern); Length = GetFinalPathNameByHandleW(Handle, FullPath, FULLPATH_SIZE - 1, 0); if (0 == Length) DirBufferResult = FspNtStatusFromWin32(GetLastError()); else if (Length + 1 + PatternLength >= FULLPATH_SIZE) DirBufferResult = STATUS_OBJECT_NAME_INVALID; if (!NT_SUCCESS(DirBufferResult)) { FspFileSystemReleaseDirectoryBuffer(&FileContext->DirBuffer); return DirBufferResult; } if (L'\\' != FullPath[Length - 1]) FullPath[Length++] = L'\\'; memcpy(FullPath + Length, Pattern, PatternLength * sizeof(WCHAR)); FullPath[Length + PatternLength] = L'\0'; FindHandle = FindFirstFileW(FullPath, &FindData); if (INVALID_HANDLE_VALUE != FindHandle) { do { memset(DirInfo, 0, sizeof *DirInfo); Length = (ULONG)wcslen(FindData.cFileName); DirInfo->Size = (UINT16)(FIELD_OFFSET(FSP_FSCTL_DIR_INFO, FileNameBuf) + Length * sizeof(WCHAR)); DirInfo->FileInfo.FileAttributes = FindData.dwFileAttributes; DirInfo->FileInfo.ReparseTag = 0; DirInfo->FileInfo.FileSize = ((UINT64)FindData.nFileSizeHigh << 32) | (UINT64)FindData.nFileSizeLow; DirInfo->FileInfo.AllocationSize = (DirInfo->FileInfo.FileSize + ALLOCATION_UNIT - 1) / ALLOCATION_UNIT * ALLOCATION_UNIT; DirInfo->FileInfo.CreationTime = ((PLARGE_INTEGER)&FindData.ftCreationTime)->QuadPart; DirInfo->FileInfo.LastAccessTime = ((PLARGE_INTEGER)&FindData.ftLastAccessTime)->QuadPart; DirInfo->FileInfo.LastWriteTime = ((PLARGE_INTEGER)&FindData.ftLastWriteTime)->QuadPart; DirInfo->FileInfo.ChangeTime = DirInfo->FileInfo.LastWriteTime; DirInfo->FileInfo.IndexNumber = 0; DirInfo->FileInfo.HardLinks = 0; memcpy(DirInfo->FileNameBuf, FindData.cFileName, Length * sizeof(WCHAR)); if (!FspFileSystemFillDirectoryBuffer(&FileContext->DirBuffer, DirInfo, &DirBufferResult)) break; } while (FindNextFileW(FindHandle, &FindData)); FindClose(FindHandle); } FspFileSystemReleaseDirectoryBuffer(&FileContext->DirBuffer); } if (!NT_SUCCESS(DirBufferResult)) return DirBufferResult; FspFileSystemReadDirectoryBuffer(&FileContext->DirBuffer, Marker, Buffer, BufferLength, PBytesTransferred); return STATUS_SUCCESS; }
std::string os_get_final_path(std::string path) { #if (_WIN32_WINNT >= 0x0600) std::wstring wret; if(path.size()<3 && path.find(":")==std::string::npos) { path+=":"; } if(path.find("\\")==std::string::npos) { path+="\\"; } HANDLE hFile = CreateFileW(ConvertToWchar(path).c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS, NULL); if( hFile==INVALID_HANDLE_VALUE ) { Log("Could not open path in os_get_final_path for \""+path+"\"", LL_ERROR); return path; } DWORD dwBufsize = GetFinalPathNameByHandleW( hFile, NULL, 0, VOLUME_NAME_DOS ); if(dwBufsize==0) { Log("Error getting path size in in os_get_final_path error="+convert((int)GetLastError())+" for \""+path+"\"", LL_ERROR); CloseHandle(hFile); return path; } wret.resize(dwBufsize+1); DWORD dwRet = GetFinalPathNameByHandleW( hFile, (LPWSTR)wret.c_str(), dwBufsize, VOLUME_NAME_DOS ); CloseHandle(hFile); if(dwRet==0) { Log("Error getting path in in os_get_final_path error="+convert((int)GetLastError()), LL_ERROR); } else if(dwRet<wret.size()) { wret.resize(dwRet); if(wret.find(L"\\\\?\\UNC")==0) { wret.erase(0, 7); wret=L"\\"+wret; } if(wret.find(L"\\\\?\\")==0) { wret.erase(0,4); } /*if(wret.size()>=2 && wret[wret.size()-2]=='.' && ret[wret.size()-1]=='.' ) { wret.resize(ret.size()-2); }*/ return ConvertFromWchar(wret); } else { Log("Error getting path (buffer too small) in in os_get_final_path error="+convert((int)GetLastError()), LL_ERROR); } return path; #else return path; #endif }