blob: d4802d8ecb0e72e90a78df412e12e8728be1be88 [file] [log] [blame]
Jihoon Kangbd7afdc2024-12-19 02:06:22 +00001#!/usr/bin/env python3
2#
3# Copyright (C) 2024 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16#
17"""A tool for calculating the hash of a directory based on file contents and metadata."""
18
19import argparse
20import hashlib
21import os
22import stat
23
24def calculate_hash(directory: str) -> str:
25 """
26 Calculates the hash of a directory, including file contents and metadata.
27
28 Following informations are taken into consideration:
29 * Name: The file or directory name.
30 * File Type: Whether it's a regular file, directory, symbolic link, etc.
31 * Size: The size of the file in bytes.
32 * Permissions: The file's access permissions (read, write, execute).
33 * Content Hash (for files): The SHA-1 hash of the file's content.
34 """
35
36 output = []
37 for root, _, files in os.walk(directory):
38 for file in files:
39 filepath = os.path.join(root, file)
40 file_stat = os.lstat(filepath)
41 stat_info = f"{filepath} {stat.filemode(file_stat.st_mode)} {file_stat.st_size}"
42
43 if os.path.islink(filepath):
44 stat_info += os.readlink(filepath)
45 elif os.path.isfile(filepath):
46 with open(filepath, "rb") as f:
47 file_hash = hashlib.sha1(f.read()).hexdigest()
48 stat_info += f" {file_hash}"
49
50 output.append(stat_info)
51
52 return hashlib.sha1("\n".join(sorted(output)).encode()).hexdigest()
53
54if __name__ == "__main__":
55 parser = argparse.ArgumentParser(description="Calculate the hash of a directory.")
56 parser.add_argument("directory", help="Path to the directory")
57 parser.add_argument("output_file", help="Path to the output file")
58 args = parser.parse_args()
59
60 hash_value = calculate_hash(args.directory)
61 with open(args.output_file, "w") as f:
62 f.write(hash_value)