I'm trying to get a data structure that describes the allocation and location on disk of a specific file on python using ctypes.windll.kernel32.DeviceIoControl call.
import os
import json
import shutil
import ctypes
import ctypes.wintypes as wintypes
import struct
import winioctlcon
import json
import subprocess
FILE_ATTRIBUTE_NORMAL = 0x00000080
#FILE_READ_ATTRIBUTES = 0x80000000
FILE_READ_ATTRIBUTES = (0x0080)
#FILE_SHARE_READ = 1
#FILE_SHARE_WRITE = 2
#FILE_SHARE_DELETE = 4
FILE_SHARE_READ = 0x00000001
FILE_SHARE_WRITE = 0x00000002
FILE_SHARE_DELETE = 0x00000004
GENERIC_READ = 0x80000000
GENERIC_WRITE = 0x40000000
FILE_FLAG_NO_BUFFERING = 0x20000000
FILE_FLAG_RANDOM_ACCESS = 0x10000000
FILE_FLAG_WRITE_THROUGH = 0x80000000
SYNCHRONIZE = 0x00100000
OPEN_EXISTING = 3
OPEN_ALWAYS = 4
#FILE_FLAG_BACKUP_SEMANTICS = 0x02000000
FILE_FLAG_BACKUP_SEMANTICS = 0x02000000
#FILE_FLAG_OPEN_REPARSE_POINT = 0x00200000
FILE_FLAG_OPEN_REPARSE_POINT = 0x00200000
#FILE_FLAG_OPEN_NO_RECALL = 0x00100000
FILE_FLAG_OPEN_NO_RECALL = 0x00100000
class FileChunkInfo(ctypes.Structure):
_fields_ = [
("fileOffset", ctypes.c_ulonglong),
("chunkSize", ctypes.c_ulonglong),
("volumeOffset", ctypes.c_ulonglong)
]
class LARGE_INTEGER(ctypes.Structure):
_fields_ = [
('LowPart', ctypes.c_ulong),
('HighPart', ctypes.c_long),
('QuadPart', ctypes.c_longlong),
]
class STARTING_VCN_INPUT_BUFFER(ctypes.Structure):
_fields_ = [("StartingVcn", LARGE_INTEGER)]
class RETRIEVAL_POINTERS_EXTENT(ctypes.Structure):
_fields_ = [
('NextVcn', LARGE_INTEGER),
('Lcn', LARGE_INTEGER),
]
class RETRIEVAL_POINTERS_BUFFER(ctypes.Structure):
_fields_ = [
('ExtentCount', wintypes.DWORD),
('StartingVcn', LARGE_INTEGER),
('Extents', RETRIEVAL_POINTERS_EXTENT * 1), # Adjust the array size as needed
]
def get_file_info(full_path, bytes_per_cluster):
file_size = 0
file_parts = {}
full_path_wchar = ctypes.c_wchar_p(full_path)
# Retrieve handle to a file
h_file = ctypes.windll.kernel32.CreateFileW(
full_path_wchar,
FILE_READ_ATTRIBUTES,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
None,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_OPEN_NO_RECALL,
None,
)
if h_file == -1:
print("Invalid handle. Error: {}", ctypes.windll.kernel32.GetLastError())
return False
if h_file != -1:
file_size = LARGE_INTEGER(0)
res = ctypes.windll.kernel32.GetFileSize(h_file, ctypes.byref(file_size ))
print("fileSize{}".format(res))
# Retrieve file extents using DeviceIoControl with FSCTL_GET_RETRIEVAL_POINTERS
start_buffer = STARTING_VCN_INPUT_BUFFER()
p_start_buffer = ctypes.pointer(start_buffer)
extents = RETRIEVAL_POINTERS_BUFFER()
p_extents = ctypes.pointer(extents)
bytes_returned = wintypes.DWORD(0)
try:
res = ctypes.windll.kernel32.DeviceIoControl(
h_file,
winioctlcon.FSCTL_GET_RETRIEVAL_POINTERS, #0x90083C, # FSCTL_GET_RETRIEVAL_POINTERS
p_start_buffer,
ctypes.sizeof(STARTING_VCN_INPUT_BUFFER),
p_extents,
ctypes.sizeof(RETRIEVAL_POINTERS_BUFFER),
ctypes.byref(bytes_returned),
None
)
print(ctypes.windll.kernel32.GetLastError())
print("Res: {}".format(res))
#print("Extents count: {}".format(extents.ExtentCount))
return
except Exception as err:
print(err)
return False
ctypes.windll.kernel32.CloseHandle(h_file)
return True
For some reasons DeviceIoControl returns 0 which means the call failed and GetLastError() returns error code 5 which is access denied. Cna you help me to understand what did I do wrong?
Also, I tried this example: https://gist.github.com/santa4nt/11068180. It works fine for Geometry. But my specific case also fails with code 5 using an approach provided in this sources.
Furthemore, code 5 returns even if file is not opened by anyone.
My shell was opened as Administrator but I'm sure the reason something else.
I tried to rewrite the DeviceIoControl call using this approach: https://gist.github.com/santa4nt/11068180. Didn't help.