Coverage for src/debputy/builtin_manifest_rules.py: 86%
79 statements
« prev ^ index » next coverage.py v7.2.7, created at 2024-04-07 12:14 +0200
« prev ^ index » next coverage.py v7.2.7, created at 2024-04-07 12:14 +0200
1import re
2from typing import Iterable, Tuple, Optional
4from debputy.architecture_support import DpkgArchitectureBuildProcessValuesTable
5from debputy.exceptions import PureVirtualPathError, TestPathWithNonExistentFSPathError
6from debputy.intermediate_manifest import PathType
7from debputy.manifest_parser.base_types import SymbolicMode, OctalMode, FileSystemMode
8from debputy.manifest_parser.util import AttributePath
9from debputy.packages import BinaryPackage
10from debputy.path_matcher import (
11 MATCH_ANYTHING,
12 MatchRule,
13 ExactFileSystemPath,
14 DirectoryBasedMatch,
15 MatchRuleType,
16 BasenameGlobMatch,
17)
18from debputy.substitution import Substitution
19from debputy.types import VP
20from debputy.util import _normalize_path, perl_module_dirs
22# Imported from dh_fixperms
23_PERMISSION_NORMALIZATION_SOURCE_DEFINITION = "permission normalization"
24attribute_path = AttributePath.builtin_path()[
25 _PERMISSION_NORMALIZATION_SOURCE_DEFINITION
26]
27_STD_FILE_MODE = OctalMode(0o644)
28_PATH_FILE_MODE = OctalMode(0o755)
29_HAS_BIN_SHBANG_RE = re.compile(rb"^#!\s*/(?:usr/)?s?bin", re.ASCII)
32class _UsrShareDocMatchRule(DirectoryBasedMatch):
33 def __init__(self) -> None:
34 super().__init__(
35 MatchRuleType.ANYTHING_BENEATH_DIR,
36 _normalize_path("usr/share/doc", with_prefix=True),
37 path_type=PathType.FILE,
38 )
40 def finditer(self, fs_root: VP, *, ignore_paths=None) -> Iterable[VP]:
41 doc_dir = fs_root.lookup(self._directory)
42 if doc_dir is None:
43 return
44 for path_in_doc_dir in doc_dir.iterdir:
45 if ignore_paths is not None and ignore_paths(path_in_doc_dir): 45 ↛ 46line 45 didn't jump to line 46, because the condition on line 45 was never true
46 continue
47 if path_in_doc_dir.is_file: 47 ↛ 48line 47 didn't jump to line 48, because the condition on line 47 was never true
48 yield path_in_doc_dir
49 for subpath in path_in_doc_dir.iterdir:
50 if subpath.name == "examples" and subpath.is_dir: 50 ↛ 51line 50 didn't jump to line 51, because the condition on line 50 was never true
51 continue
52 if ignore_paths is not None: 52 ↛ 59line 52 didn't jump to line 59, because the condition on line 52 was never false
53 yield from (
54 f
55 for f in subpath.all_paths()
56 if f.is_file and not ignore_paths(f)
57 )
58 else:
59 yield from (f for f in subpath.all_paths() if f.is_file)
61 def describe_match_short(self) -> str:
62 return f"All files beneath {self._directory}/ except .../<pkg>/examples"
64 def describe_match_exact(self) -> str:
65 return self.describe_match_short()
68class _ShebangScriptFiles(MatchRule):
69 def __init__(self) -> None:
70 super().__init__(MatchRuleType.GENERIC_GLOB)
72 def finditer(self, fs_root: VP, *, ignore_paths=None) -> Iterable[VP]:
73 for p in fs_root.all_paths():
74 if not p.is_file or (ignore_paths and ignore_paths(p)):
75 continue
76 try:
77 with p.open(byte_io=True) as fd:
78 c = fd.read(32)
79 except (PureVirtualPathError, TestPathWithNonExistentFSPathError):
80 continue
81 if _HAS_BIN_SHBANG_RE.match(c):
82 yield p
84 @property
85 def path_type(self) -> Optional[PathType]:
86 return PathType.FILE
88 def _full_pattern(self) -> str:
89 return "built-in - not a valid pattern"
91 def describe_match_short(self) -> str:
92 return "All scripts with a absolute #!-line for /(s)bin or /usr/(s)bin"
94 def describe_match_exact(self) -> str:
95 return self.describe_match_short()
98USR_SHARE_DOC_MATCH_RULE = _UsrShareDocMatchRule()
99SHEBANG_SCRIPTS = _ShebangScriptFiles()
100del _UsrShareDocMatchRule
101del _ShebangScriptFiles
104def builtin_mode_normalization_rules(
105 dpkg_architecture_variables: DpkgArchitectureBuildProcessValuesTable,
106 dctrl_bin: BinaryPackage,
107 substitution: Substitution,
108) -> Iterable[Tuple[MatchRule, FileSystemMode]]:
109 yield from (
110 (
111 MatchRule.from_path_or_glob(
112 x,
113 _PERMISSION_NORMALIZATION_SOURCE_DEFINITION,
114 path_type=PathType.FILE,
115 ),
116 _STD_FILE_MODE,
117 )
118 for x in (
119 "*.so.*",
120 "*.so",
121 "*.la",
122 "*.a",
123 "*.js",
124 "*.css",
125 "*.scss",
126 "*.sass",
127 "*.jpeg",
128 "*.jpg",
129 "*.png",
130 "*.gif",
131 "*.cmxs",
132 "*.node",
133 )
134 )
136 yield from (
137 (
138 MatchRule.recursive_beneath_directory(
139 x,
140 _PERMISSION_NORMALIZATION_SOURCE_DEFINITION,
141 path_type=PathType.FILE,
142 ),
143 _STD_FILE_MODE,
144 )
145 for x in (
146 "usr/share/man",
147 "usr/include",
148 "usr/share/applications",
149 "usr/share/lintian/overrides",
150 )
151 )
153 # The dh_fixperms tool recuses for these directories, but probably should not (see #1006927)
154 yield from (
155 (
156 MatchRule.from_path_or_glob(
157 f"{x}/*",
158 _PERMISSION_NORMALIZATION_SOURCE_DEFINITION,
159 path_type=PathType.FILE,
160 ),
161 _PATH_FILE_MODE,
162 )
163 for x in (
164 "usr/bin",
165 "usr/bin/mh",
166 "bin",
167 "usr/sbin",
168 "sbin",
169 "usr/games",
170 "usr/libexec",
171 "etc/init.d",
172 )
173 )
175 yield (
176 # Strictly speaking, dh_fixperms does a recursive search but in practice, it does not matter.
177 MatchRule.from_path_or_glob(
178 "etc/sudoers.d/*",
179 _PERMISSION_NORMALIZATION_SOURCE_DEFINITION,
180 path_type=PathType.FILE,
181 ),
182 OctalMode(0o440),
183 )
185 # The reportbug rule
186 yield (
187 ExactFileSystemPath(
188 substitution.substitute(
189 _normalize_path("usr/share/bug/{{PACKAGE}}"),
190 _PERMISSION_NORMALIZATION_SOURCE_DEFINITION,
191 )
192 ),
193 OctalMode(0o755),
194 )
196 yield (
197 MatchRule.recursive_beneath_directory(
198 "usr/share/bug/{{PACKAGE}}",
199 _PERMISSION_NORMALIZATION_SOURCE_DEFINITION,
200 path_type=PathType.FILE,
201 substitution=substitution,
202 ),
203 OctalMode(0o644),
204 )
206 yield (
207 ExactFileSystemPath(
208 substitution.substitute(
209 _normalize_path("usr/share/bug/{{PACKAGE}}/script"),
210 _PERMISSION_NORMALIZATION_SOURCE_DEFINITION,
211 )
212 ),
213 OctalMode(0o755),
214 )
216 yield (
217 USR_SHARE_DOC_MATCH_RULE,
218 OctalMode(0o0644),
219 )
221 yield from (
222 (
223 BasenameGlobMatch(
224 "*.pm",
225 only_when_in_directory=perl_dir,
226 path_type=PathType.FILE,
227 recursive_match=True,
228 ),
229 SymbolicMode.parse_filesystem_mode(
230 "a-x",
231 attribute_path['"*.pm'],
232 ),
233 )
234 for perl_dir in perl_module_dirs(dpkg_architecture_variables, dctrl_bin)
235 )
237 yield (
238 BasenameGlobMatch(
239 "*.ali",
240 only_when_in_directory=_normalize_path("usr/lib"),
241 path_type=PathType.FILE,
242 recursive_match=True,
243 ),
244 SymbolicMode.parse_filesystem_mode(
245 "a-w",
246 attribute_path['"*.ali"'],
247 ),
248 )
250 yield (
251 SHEBANG_SCRIPTS,
252 _PATH_FILE_MODE,
253 )
255 yield (
256 MATCH_ANYTHING,
257 SymbolicMode.parse_filesystem_mode(
258 "go=rX,u+rw,a-s",
259 attribute_path["**/*"],
260 ),
261 )