Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 28 additions & 3 deletions ubireader/scripts/ubireader_list_files.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,12 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#############################################################

from __future__ import annotations
import os
import sys
import time
import argparse
from typing import Protocol, cast

from ubireader import settings
from ubireader.ubi import ubi
Expand All @@ -33,6 +35,23 @@
from ubireader.debug import error, log
from ubireader.utils import guess_filetype, guess_start_offset, guess_leb_size, guess_peb_size

class _Args(Protocol):
log: bool
verbose: bool
block_size: int | None
start_offset: int | None
end_offset: int | None
guess_offset: int | None
warn_only_block_read_errors: bool
ignore_block_header_errors: bool
uboot_fix: bool
listpath: str | None
copyfile: str | None
copyfiledest: str | None
master_key: str | None
recursive: bool
filepath: str

def main():
start = time.time()
description = 'List and Extract files of a UBI or UBIFS image.'
Expand Down Expand Up @@ -81,13 +100,16 @@ def main():
parser.add_argument('-K', '--master-key', dest='master_key',
help='Master key file, given with fscryptctl e.g. to encrypt the UBIFS (support limited to fscrypt v1 policies)')

parser.add_argument('-r', '--recursive', action='store_true',
help='List files recursively and show absolute paths.')

parser.add_argument('filepath', help='UBI/UBIFS image file.')

if len(sys.argv) == 1:
parser.print_help()
sys.exit(1)

args = parser.parse_args()
args = cast(_Args, parser.parse_args())

settings.logging_on = args.log

Expand All @@ -99,6 +121,9 @@ def main():

settings.uboot_fix = args.uboot_fix

if args.recursive and not args.listpath:
parser.error("Recursive option needs a path to start with.")

if args.master_key:
path = args.master_key
if not os.path.exists(path):
Expand Down Expand Up @@ -173,7 +198,7 @@ def main():
ubifs_obj = ubifs(lebv_file, master_key=master_key)

if args.listpath:
list_files(ubifs_obj, args.listpath)
list_files(ubifs_obj, args.listpath, recursive=args.recursive)
if args.copyfile and args.copyfiledest:
copy_file(ubifs_obj, args.copyfile, args.copyfiledest)

Expand All @@ -182,7 +207,7 @@ def main():
ubifs_obj = ubifs(ufile_obj, master_key=master_key)

if args.listpath:
list_files(ubifs_obj, args.listpath)
list_files(ubifs_obj, args.listpath, recursive=args.recursive)
if args.copyfile and args.copyfiledest:
copy_file(ubifs_obj, args.copyfile, args.copyfiledest)

Expand Down
52 changes: 41 additions & 11 deletions ubireader/ubifs/list.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

from __future__ import annotations
import os
from pathlib import PurePath
import time
from typing import TYPE_CHECKING
from ubireader.ubifs.decrypt import decrypt_symlink_target
Expand All @@ -32,12 +33,9 @@
from ubireader.ubifs import ubifs as Ubifs, nodes
from ubireader.ubifs.walk import Inode

def list_files(ubifs: Ubifs, list_path: str) -> None:
pathnames = list_path.split("/")
pnames: list[str] = []
for i in pathnames:
if len(i) > 0:
pnames.append(i)
def list_files(ubifs: Ubifs, list_path: PurePath | str, *, recursive: bool = False) -> None:
list_path = PurePath(list_path)
pnames = [part for part in list_path.parts if part != '/']
try:
inodes: dict[int, Inode] = {}
bad_blocks: list[int] = []
Expand All @@ -56,7 +54,15 @@ def list_files(ubifs: Ubifs, list_path: str) -> None:
return

for dent in inodes[inum]['dent']:
print_dent(ubifs, inodes, dent, longts=False)
print_dent(
ubifs,
inodes,
dent,
longts=False,
recursive=recursive,
# Only show absolute paths if recursive
dent_path=(list_path / dent.name) if recursive else None,
)

if len(bad_blocks):
error(list_files, 'Warn', 'Data may be missing or corrupted, bad blocks, LEB [%s]' % ','.join(map(str, bad_blocks)))
Expand Down Expand Up @@ -114,8 +120,21 @@ def find_dir(inodes: Mapping[int, Inode], inum: int, names: list[str], idx: int)
return None


def print_dent(ubifs: Ubifs, inodes: Mapping[int, Inode], dent_node: nodes.dent_node, long: bool = True, longts: bool = False) -> None:
def print_dent(
ubifs: Ubifs,
inodes: Mapping[int, Inode],
dent_node: nodes.dent_node,
long: bool = True,
longts: bool = False,
*,
recursive: bool = False,
# Path of the directory entry
dent_path: PurePath | None = None,
) -> None:
inode = inodes[dent_node.inum]
# Display the full path if path is set, otherwise just the name.
display_path = str(dent_path) if dent_path is not None else dent_node.name

if long:
fl = file_leng(ubifs, inode)

Expand All @@ -128,10 +147,21 @@ def print_dent(ubifs: Ubifs, inodes: Mapping[int, Inode], dent_node: nodes.dent_
else:
mtime = time.strftime("%b %d %H:%M", time.gmtime(inode['ino'].mtime_sec))

print('%6o %2d %s %s %7d %s %s%s' % (inode['ino'].mode, inode['ino'].nlink, inode['ino'].uid, inode['ino'].gid, fl, mtime, dent_node.name, lnk))
print('%6o %2d %s %s %7d %s %s%s' % (inode['ino'].mode, inode['ino'].nlink, inode['ino'].uid, inode['ino'].gid, fl, mtime, display_path, lnk))
else:
print(dent_node.name)

print(display_path)

if recursive and dent_node.type == UBIFS_ITYPE_DIR:
for dnode in inode.get('dent', []):
print_dent(
ubifs,
inodes,
dnode,
long=long,
longts=longts,
recursive=recursive,
dent_path=dent_path / dnode.name if dent_path is not None else None,
)

def file_leng(ubifs: Ubifs, inode: Inode) -> int:
fl = 0
Expand Down