Skip to content

Commit

Permalink
[Normal] Update Some Codes
Browse files Browse the repository at this point in the history
  • Loading branch information
ColdWindScholar committed Mar 24, 2024
1 parent f0ebb1f commit cba769c
Show file tree
Hide file tree
Showing 2 changed files with 275 additions and 11 deletions.
37 changes: 26 additions & 11 deletions imgextractor.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,15 @@
from timeit import default_timer as dti
from utils import simg2img

try:
from pycase import ensure_dir_case_sensitive
except ImportError:
ensure_dir_case_sensitive = lambda *x: ...


class Extractor:
def __init__(self):
self.CONFING_DIR = None
self.CONFIG_DIR = None
self.FileName = ""
self.OUTPUT_IMAGE_FILE = ""
self.EXTRACT_DIR = ""
Expand Down Expand Up @@ -121,7 +126,7 @@ def scan_dir(root_inode, root_path=""):
link_target = root_inode.volume.read(link_target_block * root_inode.volume.block_size,
entry_inode.inode.i_size).decode("utf8")
if tmp_path.find(' ', 1, len(tmp_path)) > 0:
self.__append(tmp_path, os.path.join(self.CONFING_DIR, self.FileName + '_space.txt'))
self.__append(tmp_path, os.path.join(self.CONFIG_DIR, self.FileName + '_space.txt'))
self.fs_config.append(
f"{tmp_path.replace(' ', '_')} {uid} {gid} {mode}{cap} {link_target}")
else:
Expand All @@ -133,6 +138,11 @@ def scan_dir(root_inode, root_path=""):
dir_target = dir_target[:-1]
if not os.path.isdir(dir_target):
os.makedirs(dir_target)
if os.name == 'nt' and windll.shell32.IsUserAnAdmin():
try:
ensure_dir_case_sensitive(dir_target)
except (Exception, BaseException):
...
if os.name == 'posix' and os.geteuid() == 0:
os.chmod(dir_target, int(mode, 8))
os.chown(dir_target, uid, gid)
Expand All @@ -143,7 +153,7 @@ def scan_dir(root_inode, root_path=""):
with open(file_target, 'wb') as out:
out.write(entry_inode.open_read().read())
except Exception and BaseException as e:
print(f'[E] Cannot Write {file_target}, Because of {e}')
print(f'[E] Cannot Write to {file_target}, Reason: {e}')
if os.name == 'posix' and os.geteuid() == 0:
os.chmod(file_target, int(mode, 8))
os.chown(file_target, uid, gid)
Expand Down Expand Up @@ -181,16 +191,16 @@ def scan_dir(root_inode, root_path=""):
finally:
...

if not os.path.isdir(self.CONFING_DIR):
os.makedirs(self.CONFING_DIR)
self.__append(os.path.getsize(self.OUTPUT_IMAGE_FILE), self.CONFING_DIR + os.sep + self.FileName + '_size.txt')
if not os.path.isdir(self.CONFIG_DIR):
os.makedirs(self.CONFIG_DIR)
self.__append(os.path.getsize(self.OUTPUT_IMAGE_FILE), self.CONFIG_DIR + os.sep + self.FileName + '_size.txt')
with open(self.OUTPUT_IMAGE_FILE, 'rb') as file:
dir_r = self.FileName
scan_dir(ext4.Volume(file).root)
self.fs_config.insert(0, '/ 0 2000 0755' if dir_r == 'vendor' else '/ 0 0 0755')
self.fs_config.insert(1, f'{dir_r} 0 2000 0755' if dir_r == 'vendor' else '/lost+found 0 0 0700')
self.fs_config.insert(2 if dir_r == 'system' else 1, f'{dir_r} 0 0 0755')
self.__append('\n'.join(self.fs_config), self.CONFING_DIR + os.sep + self.FileName + '_fs_config')
self.__append('\n'.join(self.fs_config), self.CONFIG_DIR + os.sep + self.FileName + '_fs_config')
p1 = p2 = 0
if self.context:
self.context.sort()
Expand All @@ -207,7 +217,7 @@ def scan_dir(root_inode, root_path=""):
p2 = 1
if p1 == p2 == 1:
break
self.__append('\n'.join(self.context), self.CONFING_DIR + os.sep + self.FileName + "_file_contexts")
self.__append('\n'.join(self.context), self.CONFIG_DIR + os.sep + self.FileName + "_file_contexts")

@staticmethod
def fix_moto(input_file):
Expand Down Expand Up @@ -250,24 +260,29 @@ def fix_size(self):
t = ext4.Volume(file)
real_size = t.get_block_count * t.block_size
if orig_size < real_size:
print(f"......Wrong Size!Fixing.......\nShould:{real_size}\nYours:{orig_size}")
print(
f"......Your image is smaller than expected! Expanding the file.......\n"
f"Expected:{real_size}\nGot:{orig_size}")
file.truncate(real_size)

def main(self, target: str, output_dir: str, work: str, target_type: str = 'img'):
self.EXTRACT_DIR = os.path.realpath(os.path.dirname(output_dir)) + os.sep + self.__out_name(
os.path.basename(output_dir))
self.OUTPUT_IMAGE_FILE = (os.path.realpath(os.path.dirname(target)) + os.sep) + os.path.basename(target)
self.FileName = self.__out_name(os.path.basename(target), out=0)
self.CONFING_DIR = work + os.sep + 'config'
self.CONFIG_DIR = work + os.sep + 'config'
with open(self.OUTPUT_IMAGE_FILE, 'rb+') as file:
mount = ext4.Volume(file).get_mount_point
if mount[:1] == '/':
mount = mount[1:]
if '/' in mount:
mount = mount.split('/')
mount = mount[len(mount) - 1]
if [True for i in [".", "@", "#"] if i in mount]:
mount = ""
if self.__out_name(os.path.basename(output_dir)) != mount and mount and self.FileName != 'mi_ext':
print(f"[N]:Your File Name Not Right , We will Extract {self.OUTPUT_IMAGE_FILE} to {mount}")
print(
f"[N]:Filename appears to be wrong , We will Extract {self.OUTPUT_IMAGE_FILE} to {mount}")
self.EXTRACT_DIR = os.path.realpath(os.path.dirname(output_dir)) + os.sep + mount
self.FileName = mount
if target_type == 's_img':
Expand Down
249 changes: 249 additions & 0 deletions pycase.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,249 @@
#!/usr/bin/env python3
"""
set directory case sensitive on Windows directly with ctypes
**requires WSL to be enabled**
thus for Windows 10 1903 and up
"""

from ctypes import (
WinDLL,
c_void_p,
get_last_error,
WinError,
c_int,
Structure,
sizeof,
byref
)
from ctypes.wintypes import (
HANDLE,
LPCSTR,
DWORD,
BOOL,
LPVOID,
ULONG
)
try:
from enum import IntEnum
except ImportError:
IntEnum = int
import os.path

# ==================================================================
# ======================== WinApi Bindings =========================
# ==================================================================

# https://stackoverflow.com/questions/29847679/get-error-message-from-ctypes-windll
kernel32 = WinDLL('kernel32', use_last_error=True)


def _check_handle(h, *_):
if h == INVALID_HANDLE_VALUE:
raise WinError(get_last_error())

return h


def _expect_nonzero(res, *_):
if not res:
raise WinError(get_last_error())


# https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea
FILE_FLAG_BACKUP_SEMANTICS = 0x02000000
FILE_FLAG_POSIX_SEMANTICS = 0x01000000

OPEN_EXISTING = 3

FILE_SHARE_READ = 0x00000001
FILE_SHARE_WRITE = 0x00000002
FILE_SHARE_DELETE = 0x00000004
FILE_SHARE_VALID_FLAGS = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE

"""
HANDLE CreateFileA(
[in] LPCSTR lpFileName,
[in] DWORD dwDesiredAccess,
[in] DWORD dwShareMode,
[in, optional] LPSECURITY_ATTRIBUTES lpSecurityAttributes,
[in] DWORD dwCreationDisposition,
[in] DWORD dwFlagsAndAttributes,
[in, optional] HANDLE hTemplateFile
);
"""
_CreateFileA = kernel32.CreateFileA
_CreateFileA.argtypes = [
LPCSTR, DWORD, DWORD, c_void_p, DWORD, DWORD, HANDLE
]
_CreateFileA.restype = HANDLE
_CreateFileA.errcheck = _check_handle

# https://learn.microsoft.com/en-us/windows/win32/secauthz/generic-access-rights
GENERIC_READ = 0x80000000
GENERIC_WRITE = 0x40000000

# https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-findfirstfilenamew
INVALID_HANDLE_VALUE = HANDLE(-1).value

# https://learn.microsoft.com/en-us/windows/win32/api/handleapi/nf-handleapi-closehandle
"""
BOOL CloseHandle(
[in] HANDLE hObject
);
"""
_CloseHandle = kernel32.CloseHandle
_CloseHandle.argtypes = [HANDLE]
_CloseHandle.restype = BOOL
_CloseHandle.errcheck = _expect_nonzero

# https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-getfileinformationbyhandleex
"""
BOOL GetFileInformationByHandleEx(
[in] HANDLE hFile,
[in] FILE_INFO_BY_HANDLE_CLASS FileInformationClass,
[out] LPVOID lpFileInformation,
[in] DWORD dwBufferSize
);
"""
_GetFileInformationByHandleEx = kernel32.GetFileInformationByHandleEx
_GetFileInformationByHandleEx.argtypes = [HANDLE, c_int, LPVOID, DWORD]
_GetFileInformationByHandleEx.restype = BOOL
_GetFileInformationByHandleEx.errcheck = _expect_nonzero


# ===== The start of the undocumented craps... =====

# /mingw64/include/winbase.h
class FILE_CASE_SENSITIVE_INFO(Structure):
_fields_ = [
('Flags', ULONG)
]


FILE_INFO_BY_HANDLE = FILE_CASE_SENSITIVE_INFO


# https://learn.microsoft.com/en-us/windows/win32/api/minwinbase/ne-minwinbase-file_info_by_handle_class
class FILE_INFO_BY_HANDLE_CLASS(IntEnum):
FileCaseSensitiveInfo = 23


# /mingw64/include/winnt.h
# https://github.com/DDoSolitary/LxRunOffline/blob/bdc6d7d77f886c6dcdab3b4c9c136557ca6694c4/src/lib/fs.cpp#L89
FILE_CS_FLAG_CASE_SENSITIVE_DIR = 0x00000001

# https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-setfileinformationbyhandle
"""
BOOL SetFileInformationByHandle(
[in] HANDLE hFile,
[in] FILE_INFO_BY_HANDLE_CLASS FileInformationClass,
[in] LPVOID lpFileInformation,
[in] DWORD dwBufferSize
);
"""
_SetFileInformationByHandle = kernel32.SetFileInformationByHandle
_SetFileInformationByHandle.argtypes = [HANDLE, c_int, LPVOID, DWORD]
_SetFileInformationByHandle.restype = BOOL
_SetFileInformationByHandle.errcheck = _expect_nonzero


# ==================================================================
# ======================= Wrappers functions =======================
# ==================================================================

def CreateFileA(path: str, access: int, share: int,
oflag: int, flags: int) -> HANDLE:
return _CreateFileA(
path.encode(encoding='utf-8'),
access, share,
None, # default
oflag, flags,
None # unused
)


def CloseHandle(h: HANDLE):
_CloseHandle(h)


def GetFileInformationByHandleEx(
h: HANDLE,
kind: FILE_INFO_BY_HANDLE_CLASS
) -> FILE_INFO_BY_HANDLE:
if kind == FILE_INFO_BY_HANDLE_CLASS.FileCaseSensitiveInfo:
dtype = FILE_CASE_SENSITIVE_INFO
else:
raise ValueError('Invalid file info class')

data = dtype()

_GetFileInformationByHandleEx(h, kind,
byref(data), sizeof(dtype))

return data


def SetFileInformationByHandle(
h: HANDLE,
kind: FILE_INFO_BY_HANDLE_CLASS,
data: FILE_INFO_BY_HANDLE
):
if kind == FILE_INFO_BY_HANDLE_CLASS.FileCaseSensitiveInfo:
dtype = FILE_CASE_SENSITIVE_INFO
else:
raise ValueError('Invalid file info class')

_SetFileInformationByHandle(h, kind,
byref(data), sizeof(dtype))


# ==================================================================
# ======================== Helper functions ========================
# ==================================================================

def open_dir_handle(path: str, access: int) -> HANDLE:
return CreateFileA(
path, access,
FILE_SHARE_VALID_FLAGS, OPEN_EXISTING,
FILE_FLAG_POSIX_SEMANTICS | FILE_FLAG_BACKUP_SEMANTICS
)


# ==================================================================
# ======================= Exported functions ========================
# ==================================================================

def ensure_dir_case_sensitive(path: str):
if not os.path.isdir(path):
raise NotADirectoryError(
f'Cannot set case sensitive for non-directory: {path}'
)

h = open_dir_handle(path, GENERIC_READ)
try:
info: FILE_CASE_SENSITIVE_INFO = GetFileInformationByHandleEx(
h, FILE_INFO_BY_HANDLE_CLASS.FileCaseSensitiveInfo
)

if info.Flags & FILE_CS_FLAG_CASE_SENSITIVE_DIR:
print(f'{path!r} is already case sensitive')
else:
h2 = open_dir_handle(path, GENERIC_WRITE)

try:
info.Flags |= FILE_CS_FLAG_CASE_SENSITIVE_DIR

SetFileInformationByHandle(
h2,
FILE_INFO_BY_HANDLE_CLASS.FileCaseSensitiveInfo,
info
)

print(f'set case sensitive state for {path!r}')
finally:
CloseHandle(h2)
finally:
CloseHandle(h)

0 comments on commit cba769c

Please sign in to comment.