1#!/usr/bin/env python3 2# Copyright (C) 2023 The Android Open Source Project 3# 4# Licensed under the Apache License, Version 2.0 (the "License"); 5# you may not use this file except in compliance with the License. 6# You may obtain a copy of the License at 7# 8# http://www.apache.org/licenses/LICENSE-2.0 9# 10# Unless required by applicable law or agreed to in writing, software 11# distributed under the License is distributed on an "AS IS" BASIS, 12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13# See the License for the specific language governing permissions and 14# limitations under the License. 15 16''' 17Partition inspector creates a text file that describes a partition, for diffing it against another 18partition. Currently it just lists the files in the partition, but should be expanded to include 19selinux information and avb information. 20''' 21 22import argparse 23import difflib 24import hashlib 25import os 26import subprocess 27import sys 28 29def run_debugfs(command): 30 p = subprocess.run(command, check=True, text=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 31 if len(p.stderr.splitlines()) > 1: 32 # debugfs unfortunately doesn't exit with a nonzero status code on most errors. 33 # Instead, check if it had more than 1 line of stderr output. 34 # It always outputs its version number as the first line. 35 sys.exit(p.stderr) 36 return p.stdout 37 38def tree(debugfs, image, path='/', depth=0): 39 result = '' 40 for line in run_debugfs([debugfs, '-R', 'ls -p '+path, image]).splitlines(): 41 line = line.strip() 42 if not line: 43 continue 44 _, _ino, mode, uid, gid, name, size, _ = line.split('/') 45 if name == '.' or name == '..': 46 continue 47 is_dir = not size 48 49 child = os.path.join(path, name) 50 51 # The selinux contexts are stored in the extended attributes 52 extended_attributes = ' '.join(run_debugfs([debugfs, '-R' 'ea_list '+child, image]).strip().split()) 53 54 result += ' '*depth 55 result += f'{mode} {uid} {gid} {name} ({extended_attributes}){":" if is_dir else ""}\n' 56 if not size: 57 result += tree(debugfs, image, child, depth+1) 58 return result 59 60 61def main(): 62 parser = argparse.ArgumentParser() 63 parser.add_argument('--debugfs-path', default='debugfs') 64 parser.add_argument('image') 65 args = parser.parse_args() 66 67 # debugfs doesn't exit with an error if the image doesn't exist 68 if not os.path.isfile(args.image): 69 sys.exit(f"{args.image} was not found or was a directory") 70 71 print(tree(args.debugfs_path, args.image)) 72 73 74 75if __name__ == "__main__": 76 main() 77