22# -*- coding: utf-8 -*-
33
44import os
5+ import re
56import sys
67
7- # Key không có dấu "/" ở đầu để khớp hoàn chỉnh với cấu trúc sinh ra từ scan_dir
8- fix_permission = {
9- "vendor/bin/hw/android.hardware.wifi@1.0" : ["u:object_r:hal_wifi_default_exec:s0" ],
10- "system/bin/init" : ["u:object_r:init_exec:s0" ],
8+ # Khai báo quy tắc gán context cứng (hỗ trợ cả đường dẫn tuyệt đối lẫn tên file/thư mục đặc biệt)
9+ FIX_PERMISSIONS = {
1110 "lost+found" : ["u:object_r:rootfs:s0" ],
11+ "system/bin/init" : ["u:object_r:init_exec:s0" ],
1212 "system/bin/idmap" : ["u:object_r:idmap_exec:s0" ],
1313 "system/bin/fsck" : ["u:object_r:fsck_exec:s0" ],
1414 "system/bin/e2fsck" : ["u:object_r:fsck_exec:s0" ],
15- "system/bin/logcat" : ["u:object_r:logcat_exec:s0" ]
15+ "system/bin/logcat" : ["u:object_r:logcat_exec:s0" ],
16+ "vendor/bin/hw/android.hardware.wifi@1.0" : ["u:object_r:hal_wifi_default_exec:s0" ]
1617}
1718
18- def scan_context (file ) -> tuple :
19- """Đọc file context, giữ nguyên cấu trúc Regex gốc và bảo toàn thứ tự dòng."""
19+ def escape_regex_path (path : str ) -> str :
20+ """
21+ Escape các ký tự đặc biệt Regex (+, .) cho file_contexts của Android,
22+ giữ nguyên dấu / và @ để make_ext4fs đọc chuẩn.
23+ """
24+ # Escape dấu '+' chưa được escape trước đó
25+ path_escaped = re .sub (r'(?<!\\)\+' , r'\+' , path )
26+ return path_escaped
27+
28+ def scan_context (file_path : str ) -> tuple :
29+ """Đọc file context, lưu trữ dưới dạng dict và bảo toàn thứ tự dòng gốc."""
2030 context = {}
2131 original_order = []
2232
23- with open (file , "r" , encoding = 'utf-8' ) as file_ :
24- for i in file_ :
25- line = i .strip ()
26- if not line or line .startswith ('#' ):
33+ with open (file_path , "r" , encoding = 'utf-8' ) as f :
34+ for line in f :
35+ line_str = line .strip ()
36+ if not line_str or line_str .startswith ('#' ):
2737 continue
2838
29- parts = line .split ()
39+ parts = line_str .split ()
3040 if not parts :
3141 continue
3242
33- filepath , * other = parts
34- filepath = filepath .replace (r'\@' , '@' )
35- context [filepath ] = other
36- original_order .append (filepath )
43+ filepath = parts [0 ]
44+ # Chuẩn hóa đường dẫn bỏ escape @ nếu có
45+ filepath_clean = filepath .replace (r'\@' , '@' )
46+
47+ context [filepath_clean ] = parts [1 :]
48+ original_order .append (filepath_clean )
3749
3850 return context , original_order
3951
40- def scan_dir (folder ) -> list :
52+ def scan_dir (folder : str ) -> list :
4153 """
42- Quét thư mục thực tế và chuyển đổi sang định dạng path chuẩn của Android.
43- HỖ TRỢ DYNAMIC PARTITIONS .
54+ Quét thư mục thực tế và chuyển đổi sang định dạng path chuẩn của Android SELinux .
55+ Hỗ trợ Dynamic Partitions (system, vendor, product, system_ext, cust...) .
4456 """
4557 folder_abs = os .path .abspath (folder ).replace ('\\ ' , '/' )
4658 part_name = os .path .basename (os .path .normpath (folder ))
47-
48- # Xây dựng các root path chuẩn
49- allfiles = ['/' , f'/{ part_name } /lost+found' , f'/{ part_name } ' , f'/{ part_name } /' ]
59+
60+ allfiles = [
61+ '/' ,
62+ f'/{ part_name } ' ,
63+ f'/{ part_name } /' ,
64+ f'/{ part_name } /lost+found'
65+ ]
5066
5167 for root , dirs , files in os .walk (folder , topdown = True ):
52- # Chuẩn hóa root path hiện tại về dạng gạch chéo xuôi
5368 root_clean = os .path .abspath (root ).replace ('\\ ' , '/' )
5469
55- for dir_ in dirs :
56- full_path = f"{ root_clean } /{ dir_ } "
70+ for dir_name in dirs :
71+ full_path = f"{ root_clean } /{ dir_name } "
5772 target_path = full_path .replace (folder_abs , '/' + part_name )
5873 allfiles .append (target_path )
5974
60- for file in files :
61- full_path = f"{ root_clean } /{ file } "
75+ for file_name in files :
76+ full_path = f"{ root_clean } /{ file_name } "
6277 target_path = full_path .replace (folder_abs , '/' + part_name )
6378 allfiles .append (target_path )
6479
6580 return sorted (set (allfiles ), key = allfiles .index )
6681
67- def context_patch (fs_file , filename ) -> tuple :
68- """Vá context thông minh dựa trên luật cứng hoặc thừa kế từ thư mục cha."""
82+ def context_patch (fs_file : dict , filename_list : list ) -> tuple :
83+ """Vá context thông minh dựa trên luật cứng, quy tắc tương thích hoặc thừa kế thư mục cha."""
6984 new_fs = {}
7085 added_keys = []
7186
72- permission_d = ['u:object_r:system_file:s0' ]
87+ # Context mặc định cho file/folder không xác định
88+ default_permission = ['u:object_r:system_file:s0' ]
7389 if fs_file :
74- permission_d = fs_file .get (next (iter (fs_file )), permission_d )
90+ default_permission = fs_file .get (next (iter (fs_file )), default_permission )
7591
76- for i in filename :
77- actual_i = i if i .isprintable () else '' .join (c if c .isprintable () else '*' for c in i )
92+ for path in filename_list :
93+ actual_path = path if path .isprintable () else '' .join (c if c .isprintable () else '*' for c in path )
7894
79- if fs_file .get (actual_i ):
80- new_fs [actual_i ] = fs_file [actual_i ]
95+ # 1. Khớp chính xác với file_contexts gốc
96+ if actual_path in fs_file :
97+ new_fs [actual_path ] = fs_file [actual_path ]
8198 continue
8299
83- if actual_i in new_fs :
100+ if actual_path in new_fs :
84101 continue
85102
86- permission = permission_d
87- clean_i = actual_i .lstrip ('/' )
88-
89- if clean_i in fix_permission :
90- permission = fix_permission [clean_i ]
91- elif actual_i in fix_permission :
92- permission = fix_permission [actual_i ]
103+ permission = None
104+ clean_path = actual_path .lstrip ('/' )
105+ base_name = os .path .basename (actual_path )
106+
107+ # 2. Khớp theo FIX_PERMISSIONS (Tên file/thư mục hoặc đuôi path)
108+ if clean_path in FIX_PERMISSIONS :
109+ permission = FIX_PERMISSIONS [clean_path ]
110+ elif base_name in FIX_PERMISSIONS :
111+ permission = FIX_PERMISSIONS [base_name ]
93112 else :
94- # Thuật toán tìm ngược lên thư mục cha có thêm điều kiện chặn lặp vô hạn
95- tmp_path = os .path .dirname (actual_i )
96- while tmp_path and tmp_path != "/" :
113+ # 3. Thừa kế context từ thư mục cha gần nhất
114+ tmp_path = os .path .dirname (actual_path )
115+ while tmp_path :
97116 if tmp_path in fs_file :
98117 permission = fs_file [tmp_path ]
99118 break
119+ if tmp_path in new_fs :
120+ permission = new_fs [tmp_path ]
121+ break
122+ if tmp_path in ('/' , '' ):
123+ break
124+
100125 prev_path = tmp_path
101126 tmp_path = os .path .dirname (tmp_path )
102- if tmp_path == prev_path : # Chặn lỗi lặp cấu trúc lạ
127+ if tmp_path == prev_path :
103128 break
104129
105- print (f"ADD [{ actual_i } :{ permission } ]" )
106- new_fs [actual_i ] = permission
107- added_keys .append (actual_i )
130+ # Nếu vẫn chưa tìm thấy context -> Dùng default
131+ if not permission :
132+ permission = default_permission
133+
134+ print (f"ADD [{ actual_path } : { ' ' .join (permission )} ]" )
135+ new_fs [actual_path ] = permission
136+ added_keys .append (actual_path )
108137
109138 return new_fs , added_keys
110139
111- def main (dir_path , fs_config ) -> None :
140+ def main (dir_path : str , fs_config : str ) -> None :
112141 origin , orig_order = scan_context (os .path .abspath (fs_config ))
113142 allfiles = scan_dir (os .path .abspath (dir_path ))
114143
115144 new_fs , added_keys = context_patch (origin , allfiles )
116145
117146 with open (fs_config , "w" , encoding = 'utf-8' , newline = '\n ' ) as f :
118- # Ghi lại các key cũ theo đúng thứ tự ban đầu
147+ written_keys = set ()
148+
149+ # Ghi các key cũ theo đúng thứ tự gốc
119150 for key in orig_order :
120151 if key in new_fs :
121- f .write (f"{ key } { ' ' .join (new_fs [key ])} \n " )
122- del new_fs [key ]
152+ formatted_key = escape_regex_path (key )
153+ f .write (f"{ formatted_key } { ' ' .join (new_fs [key ])} \n " )
154+ written_keys .add (key )
123155
124- # Ghi các key mới được thêm vào cuối file
156+ # Ghi các key mới được bổ sung
125157 for key in sorted (added_keys ):
126- if key in new_fs :
127- f .write (f"{ key } { ' ' .join (new_fs [key ])} \n " )
158+ if key in new_fs and key not in written_keys :
159+ formatted_key = escape_regex_path (key )
160+ f .write (f"{ formatted_key } { ' ' .join (new_fs [key ])} \n " )
161+ written_keys .add (key )
128162
129- print ("Load origin %d entries" % (len (origin .keys ())))
130- print ("Detect total %d entries" % (len (allfiles )))
131- print ("Add %d entries thành công" % (len (added_keys )))
163+ print ("---" )
164+ print (f"Load origin: { len (origin )} entries" )
165+ print (f"Detect total: { len (allfiles )} entries" )
166+ print (f"Added new: { len (added_keys )} entries successfully" )
132167
133168if __name__ == "__main__" :
134169 if len (sys .argv ) < 3 :
135- print ("Insufficient parameters " )
170+ print ("Usage: python script.py <dir_path> <file_contexts_path> " )
136171 sys .exit (1 )
137- if not os .path .exists (sys .argv [1 ]) or not os .path .exists (sys .argv [2 ]):
138- print ("File does not exist" )
172+
173+ dir_arg , context_arg = sys .argv [1 ], sys .argv [2 ]
174+
175+ if not os .path .exists (dir_arg ) or not os .path .exists (context_arg ):
176+ print ("Error: Target directory or file_contexts path does not exist." )
139177 sys .exit (1 )
140- main (sys .argv [1 ], sys .argv [2 ])
178+
179+ main (dir_arg , context_arg )
0 commit comments