heads/blobs/t480/deguard/mfsutil.py
notgiven by 0be89cb3a0
add dependencies and a script to download and modify essential blobs for t480
Signed-off-by: Thierry Laurion <insurgo@riseup.net>
2025-02-11 11:25:51 -05:00

174 lines
8.1 KiB
Python
Executable File

#!/usr/bin/python3
# SPDX-License-Identifier: GPL-2.0-only
# This code is based on MFSUtil by Youness Alaoui (see `doc/LICENSE.orig` for original copyright)
import sys, argparse, textwrap
from lib.mfs import MFS
from lib.cfg import CFG
import zipfile, posixpath
def main():
parser = argparse.ArgumentParser(description='MFS and CFG file manipulation utility.',
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
The default output is to stdout.
Either one of --mfs or --cfg must be specified to indicate on which \
type of file to work (MFS or CFG).
You can specify one of the mutually exclusive actions : \
--dump --zip, --extract, --add, --remove.
For the --extract, --add, --remove actions, if --mfs is specified, \
then --file-id is required, if --cfg is specified, then --file-path is required.
When adding a file to a CFG file, the --mode, --opt, --uid and --gid options can be added.
The --mode option needs to be a string in the form 'dAEIrwxrwxrwx' where \
unused bits can be either a space or a dash, like --mode ' rwx---rwx' for example.
The --opt option needs to be a string in the form '?!MF' where unused bits can be \
either a space or a dash.
When adding a directory, both the file path needs to end with a '/' character and the --mode needs to start with 'd'.
""")
parser.add_argument("-o", "--output", dest="output", default='-',
help="Output file to write", metavar="FILE")
parser.add_argument("-i", "--file-id", dest="file_id", type=int,
help="ID of the file to manipulate in the MFS file", metavar="ID")
parser.add_argument("-f", "--file-path", dest="file_path",
help="Path of the file to manipulate in the CFG file", metavar="PATH")
parser.add_argument("--mode", dest="mode", default="---rwxrwxrwx",
help="Mode for file being added to CFG", metavar="MODE")
parser.add_argument("--opt", dest="opt", default="----",
help="Deplyoment option for file being added to CFG", metavar="OPT")
parser.add_argument("--uid", dest="uid", default=0, type=int,
help="User ID for file being added to CFG", metavar="UID")
parser.add_argument("--gid", dest="gid", default=0, type=int,
help="Group ID for file being added to CFG", metavar="GID")
parser.add_argument("--recursive", dest="recursive", action="store_true",
help="Recursive deletion for a file path in CFG")
parser.add_argument("--alignment", dest="alignment", type=int, default=0,
help="Alignment type for CFG files. (default: 0).\n"
"0 : packed.\n"
"1 : align all files on chunk start.\n"
"2 : align end of files on end of chunk.")
parser.add_argument("--deoptimize", dest="optimize", action="store_false",
help="De-optimize chain sequences when adding a file to MFS.")
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument("-m", "--mfs", dest="mfs", type=argparse.FileType('rb'),
help="MFS file to read from", metavar="FILE")
group.add_argument("-c", "--cfg", dest="cfg", type=argparse.FileType('rb'),
help="CFG file to read from", metavar="FILE")
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument("-d", "--dump", dest='dump', action="store_true",
help="Dump information about the MFS file, or the CFG file")
group.add_argument("-z", "--zip", dest='zip', action="store_true",
help="Store the MFS contents to a ZIP file")
group.add_argument("-x", "--extract", dest='extract', action="store_true",
help="Extract a file from the MFS file, or a file from the CFG file")
group.add_argument("-a", "--add", dest='add', type=argparse.FileType('rb'),
help="Add a file to the MFS file or a file to the CFG file",
metavar="FILENAME")
group.add_argument("-r", "--remove", dest='remove', action="store_true",
help="Remove a file from the MFS file, or a file from the CFG file")
args = parser.parse_args()
if (args.add or args.remove or args.extract) and (args.cfg and args.file_path is None):
parser.error("--add/--remove/--extract on a --cfg file requires the --file-path option")
if (args.add or args.remove or args.extract) and (args.mfs and args.file_id is None):
parser.error("--add/--remove/--extract on a --mfs file requires the --file-id option")
if args.mfs is not None:
data = args.mfs.read()
mfs = MFS(data)
if args.dump:
with argparse.FileType("wb")(args.output) as f: f.write("%s" % mfs)
elif args.extract:
file = mfs.getSystemVolume().getFile(args.file_id)
if file:
with argparse.FileType("wb")(args.output) as f: f.write(file.data)
else:
print("File ID %d does not exist in the MFS System Volume" % args.file_id)
sys.exit(-1)
elif args.remove:
mfs.getSystemVolume().removeFile(args.file_id)
mfs.generate()
with argparse.FileType("wb")(args.output) as f: f.write(mfs.data)
elif args.add:
file = mfs.getSystemVolume().getFile(args.file_id)
if file:
print("File ID %d already exists in the MFS System Volume" % args.file_id)
sys.exit(-1)
data = args.add.read()
mfs.getSystemVolume().addFile(args.file_id, data, args.optimize)
mfs.generate()
with argparse.FileType("wb")(args.output) as f: f.write(mfs.data)
elif args.zip:
z = zipfile.ZipFile(args.output, "w", zipfile.ZIP_STORED)
for id in xrange(mfs.getSystemVolume().numFiles):
file = mfs.getSystemVolume().getFile(id)
if file:
zi = zipfile.ZipInfo("file_%d.bin" % id)
zi.external_attr = (0o644 << 16)
z.writestr(zi, file.data)
z.close()
else:
data = args.cfg.read()
cfg = CFG(data)
if args.dump:
with argparse.FileType("wb")(args.output) as f: f.write("%s" % cfg)
cfg.generate(args.alignment)
#with argparse.FileType("wb")(args.output) as f: f.write(cfg.data)
assert cfg.data == data
elif args.zip:
z = zipfile.ZipFile(args.output, "w", zipfile.ZIP_STORED)
for file in cfg.files:
path = file.path
if file.isDirectory():
path += posixpath.sep
attr = (0o40755 << 16) | 0x30
else:
attr = (0o644 << 16)
zi = zipfile.ZipInfo(path)
zi.external_attr = attr
z.writestr(zi, file.data)
z.close()
elif args.extract:
file = cfg.getFile(args.file_path)
if file is None:
print("File path '%s' does not exist in the CFG file" % args.file_path)
sys.exit(-1)
with argparse.FileType("wb")(args.output) as f: f.write(file.data)
elif args.remove:
res = cfg.removeFile(args.file_path, args.recursive)
if not res:
if cfg.getFile(args.file_path) is None:
print("File path '%s' does not exist in the CFG file" % args.file_path)
else:
print("File path '%s' is a non-empty directory in the CFG file (use --recursive)" % args.file_path)
sys.exit(-1)
cfg.generate(args.alignment)
with argparse.FileType("wb")(args.output) as f: f.write(cfg.data)
elif args.add:
file = cfg.getFile(args.file_path)
if file:
print("File path '%s' already exists in the CFG file" % args.file_path)
sys.exit(-1)
data = args.add.read()
mode = CFG.strToMode(args.mode)
opt = CFG.strToOpt(args.opt)
if args.file_path[-1] == '/':
assert mode & 0x1000 == 0x1000
else:
assert mode & 0x1000 == 0
if not cfg.addFile(args.file_path, data, mode, opt, args.uid, args.gid):
print("Error adding file to path '%s' in the CFG file " \
"(parent doesn't exist or is not a directory?)" % args.file_path)
sys.exit(-1)
cfg.generate(args.alignment)
with argparse.FileType("wb")(args.output) as f: f.write(cfg.data)
if __name__=="__main__":
main()