diff --git a/src/osal.c b/src/osal.c index 80728283..655f104f 100644 --- a/src/osal.c +++ b/src/osal.c @@ -104,6 +104,35 @@ extern NTSTATUS NTAPI NtFreeVirtualMemory(IN HANDLE ProcessHandle, IN OUT PULONG RegionSize, IN ULONG FreeType); +typedef struct _IO_STATUS_BLOCK { + union { + NTSTATUS Status; + PVOID Pointer; + }; + ULONG_PTR Information; +} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK; + +typedef struct _FILE_PROVIDER_EXTERNAL_INFO_V1 { + ULONG Version; + ULONG Algorithm; + ULONG Flags; +} FILE_PROVIDER_EXTERNAL_INFO_V1, *PFILE_PROVIDER_EXTERNAL_INFO_V1; + +#ifndef STATUS_OBJECT_NOT_EXTERNALLY_BACKED +#define STATUS_OBJECT_NOT_EXTERNALLY_BACKED ((NTSTATUS)0xC000046DL) +#endif +#ifndef STATUS_INVALID_DEVICE_REQUEST +#define STATUS_INVALID_DEVICE_REQUEST ((NTSTATUS)0xC0000010L) +#endif + +extern NTSTATUS +NtFsControlFile(IN HANDLE FileHandle, IN OUT HANDLE Event, + IN OUT PVOID /* PIO_APC_ROUTINE */ ApcRoutine, + IN OUT PVOID ApcContext, OUT PIO_STATUS_BLOCK IoStatusBlock, + IN ULONG FsControlCode, IN OUT PVOID InputBuffer, + IN ULONG InputBufferLength, OUT OPTIONAL PVOID OutputBuffer, + IN ULONG OutputBufferLength); + #endif /* _WIN32 || _WIN64 */ /*----------------------------------------------------------------------------*/ @@ -746,7 +775,89 @@ int mdbx_msync(mdbx_mmap_t *map, size_t offset, size_t length, int async) { int mdbx_mmap(int flags, mdbx_mmap_t *map, size_t length, size_t limit) { #if defined(_WIN32) || defined(_WIN64) - NTSTATUS rc = NtCreateSection( + map->section = 0; + map->address = MAP_FAILED; + + if (GetFileType(map->fd) != FILE_TYPE_DISK) + return ERROR_FILE_OFFLINE; + + FILE_REMOTE_PROTOCOL_INFO RemoteProtocolInfo; + if (GetFileInformationByHandleEx(map->fd, FileRemoteProtocolInfo, + &RemoteProtocolInfo, + sizeof(RemoteProtocolInfo))) { + if ((RemoteProtocolInfo.Flags & (REMOTE_PROTOCOL_INFO_FLAG_LOOPBACK | + REMOTE_PROTOCOL_INFO_FLAG_OFFLINE)) != + REMOTE_PROTOCOL_INFO_FLAG_LOOPBACK) + return ERROR_FILE_OFFLINE; + } + + NTSTATUS rc; +#ifdef _WIN64 + struct { + WOF_EXTERNAL_INFO wof_info; + union { + WIM_PROVIDER_EXTERNAL_INFO wim_info; + FILE_PROVIDER_EXTERNAL_INFO_V1 file_info; + }; + size_t reserved_for_microsoft_madness[42]; + } GetExternalBacking_OutputBuffer; + IO_STATUS_BLOCK StatusBlock; + rc = NtFsControlFile(map->fd, NULL, NULL, NULL, &StatusBlock, + FSCTL_GET_EXTERNAL_BACKING, NULL, 0, + &GetExternalBacking_OutputBuffer, + sizeof(GetExternalBacking_OutputBuffer)); + if (rc != STATUS_OBJECT_NOT_EXTERNALLY_BACKED && + rc != STATUS_INVALID_DEVICE_REQUEST) + return NT_SUCCESS(rc) ? ERROR_FILE_OFFLINE : ntstatus2errcode(rc); +#endif + + WCHAR PathBuffer[INT16_MAX]; + DWORD VolumeSerialNumber, FileSystemFlags; + if (!GetVolumeInformationByHandleW(map->fd, PathBuffer, INT16_MAX, + &VolumeSerialNumber, NULL, + &FileSystemFlags, NULL, 0)) + return GetLastError(); + + if ((flags & MDBX_RDONLY) == 0) { + if (FileSystemFlags & (FILE_SEQUENTIAL_WRITE_ONCE | FILE_READ_ONLY_VOLUME | + FILE_VOLUME_IS_COMPRESSED)) + return ERROR_FILE_OFFLINE; + } + + if (!GetFinalPathNameByHandleW(map->fd, PathBuffer, INT16_MAX, + FILE_NAME_NORMALIZED | VOLUME_NAME_NT)) + return GetLastError(); + + if (_wcsnicmp(PathBuffer, L"\\Device\\Mup\\", 12) == 0) + return ERROR_FILE_OFFLINE; + + if (GetFinalPathNameByHandleW(map->fd, PathBuffer, INT16_MAX, + FILE_NAME_NORMALIZED | VOLUME_NAME_DOS)) { + UINT DriveType = GetDriveTypeW(PathBuffer); + if (DriveType == DRIVE_NO_ROOT_DIR && + wcsncmp(PathBuffer, L"\\\\?\\", 4) == 0 && + wcsncmp(PathBuffer + 5, L":\\", 2) == 0) { + PathBuffer[7] = 0; + DriveType = GetDriveTypeW(PathBuffer + 4); + } + switch (DriveType) { + case DRIVE_CDROM: + if (flags & MDBX_RDONLY) + break; + // fall through + case DRIVE_UNKNOWN: + case DRIVE_NO_ROOT_DIR: + case DRIVE_REMOTE: + default: + return ERROR_FILE_OFFLINE; + case DRIVE_REMOVABLE: + case DRIVE_FIXED: + case DRIVE_RAMDISK: + break; + } + } + + rc = NtCreateSection( &map->section, /* DesiredAccess */ SECTION_MAP_READ | SECTION_EXTEND_SIZE | ((flags & MDBX_WRITEMAP) ? SECTION_MAP_WRITE : 0), @@ -755,11 +866,8 @@ int mdbx_mmap(int flags, mdbx_mmap_t *map, size_t length, size_t limit) { : PAGE_READWRITE, /* AllocationAttributes */ SEC_RESERVE, map->fd); - if (!NT_SUCCESS(rc)) { - map->section = 0; - map->address = MAP_FAILED; + if (!NT_SUCCESS(rc)) return ntstatus2errcode(rc); - } map->address = NULL; SIZE_T ViewSize = limit;