Skip to content

Commit de90c5f

Browse files
feat(ubireader_list_files): support recursive listing
The default output of ubireader_list_files remains the same, but when you add '--recursive' it will recursively list all the inodes under the listpath. It then also displays the absolute path to the files instead of just the name. Co-authored-by: Krisztián Fekete <1246751+e3krisztian@users.noreply.github.com>
1 parent 2f213ef commit de90c5f

2 files changed

Lines changed: 55 additions & 15 deletions

File tree

ubireader/scripts/ubireader_list_files.py

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,12 @@
1818
# along with this program. If not, see <http://www.gnu.org/licenses/>.
1919
#############################################################
2020

21+
from __future__ import annotations
2122
import os
2223
import sys
2324
import time
2425
import argparse
26+
from typing import Protocol, cast
2527

2628
from ubireader import settings
2729
from ubireader.ubi import ubi
@@ -33,6 +35,23 @@
3335
from ubireader.debug import error, log
3436
from ubireader.utils import guess_filetype, guess_start_offset, guess_leb_size, guess_peb_size
3537

38+
class _Args(Protocol):
39+
log: bool
40+
verbose: bool
41+
block_size: int | None
42+
start_offset: int | None
43+
end_offset: int | None
44+
guess_offset: int | None
45+
warn_only_block_read_errors: bool
46+
ignore_block_header_errors: bool
47+
uboot_fix: bool
48+
listpath: str | None
49+
copyfile: str | None
50+
copyfiledest: str | None
51+
master_key: str | None
52+
recursive: bool
53+
filepath: str
54+
3655
def main():
3756
start = time.time()
3857
description = 'List and Extract files of a UBI or UBIFS image.'
@@ -81,13 +100,16 @@ def main():
81100
parser.add_argument('-K', '--master-key', dest='master_key',
82101
help='Master key file, given with fscryptctl e.g. to encrypt the UBIFS (support limited to fscrypt v1 policies)')
83102

103+
parser.add_argument('-r', '--recursive', action='store_true',
104+
help='List files recursively and show absolute paths.')
105+
84106
parser.add_argument('filepath', help='UBI/UBIFS image file.')
85107

86108
if len(sys.argv) == 1:
87109
parser.print_help()
88110
sys.exit(1)
89111

90-
args = parser.parse_args()
112+
args = cast(_Args, parser.parse_args())
91113

92114
settings.logging_on = args.log
93115

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

100122
settings.uboot_fix = args.uboot_fix
101123

124+
if args.recursive and not args.listpath:
125+
parser.error("Recursive option needs a path to start with.")
126+
102127
if args.master_key:
103128
path = args.master_key
104129
if not os.path.exists(path):
@@ -173,7 +198,7 @@ def main():
173198
ubifs_obj = ubifs(lebv_file, master_key=master_key)
174199

175200
if args.listpath:
176-
list_files(ubifs_obj, args.listpath)
201+
list_files(ubifs_obj, args.listpath, recursive=args.recursive)
177202
if args.copyfile and args.copyfiledest:
178203
copy_file(ubifs_obj, args.copyfile, args.copyfiledest)
179204

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

184209
if args.listpath:
185-
list_files(ubifs_obj, args.listpath)
210+
list_files(ubifs_obj, args.listpath, recursive=args.recursive)
186211
if args.copyfile and args.copyfiledest:
187212
copy_file(ubifs_obj, args.copyfile, args.copyfiledest)
188213

ubireader/ubifs/list.py

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
from __future__ import annotations
2121
import os
22+
from pathlib import PurePath
2223
import time
2324
from typing import TYPE_CHECKING
2425
from ubireader.ubifs.decrypt import decrypt_symlink_target
@@ -32,12 +33,9 @@
3233
from ubireader.ubifs import ubifs as Ubifs, nodes
3334
from ubireader.ubifs.walk import Inode
3435

35-
def list_files(ubifs: Ubifs, list_path: str) -> None:
36-
pathnames = list_path.split("/")
37-
pnames: list[str] = []
38-
for i in pathnames:
39-
if len(i) > 0:
40-
pnames.append(i)
36+
def list_files(ubifs: Ubifs, list_path: PurePath | str, *, recursive: bool = False) -> None:
37+
list_path = PurePath(list_path)
38+
pnames = [part for part in list_path.parts if part != '/']
4139
try:
4240
inodes: dict[int, Inode] = {}
4341
bad_blocks: list[int] = []
@@ -56,8 +54,11 @@ def list_files(ubifs: Ubifs, list_path: str) -> None:
5654
return
5755

5856
for dent in inodes[inum]['dent']:
59-
print_dent(ubifs, inodes, dent, longts=False)
60-
57+
if recursive:
58+
print_dent_recursive(ubifs, inodes, dent, longts=False, dent_path=list_path / dent.name)
59+
else:
60+
print_dent(ubifs, inodes, dent, longts=False)
61+
6162
if len(bad_blocks):
6263
error(list_files, 'Warn', 'Data may be missing or corrupted, bad blocks, LEB [%s]' % ','.join(map(str, bad_blocks)))
6364

@@ -113,9 +114,11 @@ def find_dir(inodes: Mapping[int, Inode], inum: int, names: list[str], idx: int)
113114
return find_dir(inodes, dent.inum, names, idx+1)
114115
return None
115116

116-
117-
def print_dent(ubifs: Ubifs, inodes: Mapping[int, Inode], dent_node: nodes.dent_node, long: bool = True, longts: bool = False) -> None:
117+
def print_dent(ubifs: Ubifs, inodes: Mapping[int, Inode], dent_node: nodes.dent_node, long: bool = True, longts: bool = False, *, dent_path: PurePath | None = None) -> None:
118118
inode = inodes[dent_node.inum]
119+
# Display the full path if path is set, otherwise just the name.
120+
display_path = str(dent_path) if dent_path is not None else dent_node.name
121+
119122
if long:
120123
fl = file_leng(ubifs, inode)
121124

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

131-
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))
134+
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))
132135
else:
133-
print(dent_node.name)
136+
print(display_path)
137+
138+
139+
def print_dent_recursive(ubifs: Ubifs, inodes: Mapping[int, Inode], dent_node: nodes.dent_node, long: bool, longts: bool, *, dent_path: PurePath) -> None:
140+
inode = inodes[dent_node.inum]
141+
142+
print_dent(ubifs, inodes, dent_node, long=long, longts=longts, dent_path=dent_path)
143+
144+
if dent_node.type != UBIFS_ITYPE_DIR:
145+
return
146+
147+
for dnode in inode.get('dent', []):
148+
print_dent_recursive(ubifs, inodes, dnode, long=long, longts=longts, dent_path=dent_path / dnode.name)
134149

135150

136151
def file_leng(ubifs: Ubifs, inode: Inode) -> int:

0 commit comments

Comments
 (0)