remap-badblocks 0.8__py3-none-any.whl → 0.8.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. remap_badblocks/__init__.py +1 -1
  2. remap_badblocks-0.8.1.dist-info/METADATA +107 -0
  3. remap_badblocks-0.8.1.dist-info/RECORD +36 -0
  4. __init__.py +0 -0
  5. cli/__init__.py +0 -0
  6. cli/__main__.py +0 -239
  7. cli/commands/__init__.py +0 -8
  8. cli/commands/add.py +0 -91
  9. cli/commands/apply.py +0 -81
  10. cli/commands/get.py +0 -28
  11. cli/commands/remove.py +0 -45
  12. cli/commands/update.py +0 -208
  13. cli/commands/version.py +0 -15
  14. remap_badblocks-0.8.dist-info/METADATA +0 -130
  15. remap_badblocks-0.8.dist-info/RECORD +0 -66
  16. src/badblocks/_compute_good_ranges.py +0 -43
  17. src/badblocks/_find_badblocks.py +0 -76
  18. src/badblocks/_mapping_generation.py +0 -12
  19. src/badblocks/_remap_badblocks.py +0 -114
  20. src/badblocks/badblocks.py +0 -40
  21. src/devices/__init__.py +0 -0
  22. src/devices/device_config.py +0 -62
  23. src/devices/devices_config.py +0 -300
  24. src/devices/exceptions.py +0 -22
  25. src/devices_config_constants.py +0 -3
  26. src/mapping.py +0 -109
  27. src/remappers/_check_applied_devices.py +0 -10
  28. src/remappers/_generate_dm_table.py +0 -27
  29. src/test_utils.py +0 -18
  30. src/utils/__init__.py +0 -0
  31. src/utils/_get_device_info.py +0 -43
  32. src/utils/_iterable_bytes_converter.py +0 -19
  33. src/utils/_parse_inputs.py +0 -84
  34. src/utils/_run_command.py +0 -76
  35. src/utils/_sort_devices.py +0 -26
  36. {remap_badblocks-0.8.dist-info → remap_badblocks-0.8.1.dist-info}/WHEEL +0 -0
  37. {remap_badblocks-0.8.dist-info → remap_badblocks-0.8.1.dist-info}/entry_points.txt +0 -0
  38. {remap_badblocks-0.8.dist-info → remap_badblocks-0.8.1.dist-info}/licenses/LICENSE +0 -0
  39. {remap_badblocks-0.8.dist-info → remap_badblocks-0.8.1.dist-info}/top_level.txt +0 -0
cli/commands/update.py DELETED
@@ -1,208 +0,0 @@
1
- from argparse import Namespace
2
- from pathlib import Path
3
- from tempfile import NamedTemporaryFile
4
- from typing import Iterable, Literal, Optional, TypedDict
5
-
6
- from remap_badblocks.src.badblocks._compute_good_ranges import (
7
- compute_good_ranges, reserve_space_from_good_ranges)
8
- from remap_badblocks.src.badblocks._find_badblocks import (
9
- get_all_badblocks, read_known_badblocks)
10
- from remap_badblocks.src.badblocks._mapping_generation import generate_mapping
11
- from remap_badblocks.src.badblocks._remap_badblocks import (
12
- count_used_spare_sectors, iter_all_spare_sectors, iter_free_spare_sectors,
13
- remap_badblocks, simplify_mapping)
14
- from remap_badblocks.src.devices.device_config import DeviceConfig
15
- from remap_badblocks.src.devices.devices_config import DevicesConfig
16
- from remap_badblocks.src.mapping import Mapping, MappingElement
17
- from remap_badblocks.src.utils._parse_inputs import (
18
- parse_bytes_to_sectors, parse_memory_number_to_bytes,
19
- parse_memory_range_to_sectors)
20
- from remap_badblocks.src.utils._run_command import pipe_lines_to_file
21
-
22
-
23
- class UpdateArgs(TypedDict):
24
- device_id: Optional[int]
25
- device_name: Optional[str]
26
- mode: Literal["read", "write", "skip"]
27
- output: Optional[Path]
28
- block_range: str
29
- spare_space: Optional[str]
30
- reset_mapping: bool
31
- external_known_badblocks_file: Optional[Path]
32
-
33
-
34
- def parse_args(args: Namespace) -> UpdateArgs:
35
- return {
36
- "device_id": args.id,
37
- "device_name": args.name,
38
- "mode": args.mode,
39
- "output": args.output,
40
- "block_range": args.block_range,
41
- "spare_space": (None if args.spare_space is None else args.spare_space.strip()),
42
- "reset_mapping": args.reset_mapping,
43
- "external_known_badblocks_file": args.known_badblocks_file,
44
- }
45
-
46
-
47
- def get_device_from_args(args: UpdateArgs, dc: DevicesConfig) -> DeviceConfig:
48
- if args["device_id"] is None:
49
- if args["device_name"] is not None:
50
- return dc.get_device(name=args["device_name"])
51
- else:
52
- raise ValueError(
53
- "Either --id or --name must be provided to update a device in the database."
54
- )
55
-
56
- return dc.get_device(id=args["device_id"])
57
-
58
-
59
- def get_update_block_range_from_args(
60
- args: UpdateArgs, device: DeviceConfig
61
- ) -> tuple[int, int]:
62
- start_block, end_block = parse_memory_range_to_sectors(
63
- args["block_range"],
64
- sector_size=device.sector_size,
65
- )
66
-
67
- if (start_block is not None and start_block < device.logical_range[0]) or (
68
- end_block is not None and end_block > device.logical_range[1]
69
- ):
70
- raise ValueError(
71
- f"Block range {start_block}-{end_block} must be contained"
72
- f" within the logical range {device.logical_range[0]}-{device.logical_range[1]}."
73
- )
74
-
75
- if start_block is None:
76
- start_block = device.logical_range[0]
77
- if end_block is None:
78
- end_block = device.logical_range[1]
79
-
80
- return start_block, end_block
81
-
82
-
83
- def get_n_spare_sectors_from_args(args: UpdateArgs, device: DeviceConfig) -> int:
84
- sector_size = device.sector_size
85
-
86
- if args["spare_space"] is None:
87
- spare_blocks = None
88
- else:
89
- spare_blocks = parse_bytes_to_sectors(
90
- parse_memory_number_to_bytes(args["spare_space"], sector_size=sector_size),
91
- sector_size,
92
- )
93
-
94
- if spare_blocks is not None:
95
- return spare_blocks
96
- else:
97
- if device.spare_sectors is None:
98
- raise ValueError(
99
- "Should specify spare_sectors the first time you map badblocks"
100
- )
101
- return device.spare_sectors
102
-
103
-
104
- def get_starting_badblocks(args: UpdateArgs, device: DeviceConfig) -> set[int]:
105
- known_badblocks: set[int] = set(device.badblocks)
106
-
107
- # If known badblocks file is passed, merge it into our known_badblocks
108
- if args["external_known_badblocks_file"]:
109
- known_badblocks.update(
110
- read_known_badblocks(
111
- known_badblocks_file=args["external_known_badblocks_file"]
112
- )
113
- )
114
-
115
- return known_badblocks
116
-
117
-
118
- def get_current_badblocks(
119
- args: UpdateArgs,
120
- known_badblocks: set[int],
121
- device: DeviceConfig,
122
- block_range: tuple[int, int],
123
- ) -> set[int]:
124
- if args["mode"] == "skip":
125
- return known_badblocks
126
-
127
- with NamedTemporaryFile() as known_badblocks_file:
128
- known_badblocks_file.write("\n".join(map(str, known_badblocks)).encode("utf-8"))
129
- known_badblocks_file.flush()
130
-
131
- badblocks: set[int] = set(
132
- get_all_badblocks(
133
- device.path,
134
- device.sector_size,
135
- mode=args["mode"],
136
- known_badblocks_file=Path(known_badblocks_file.name),
137
- block_range=block_range,
138
- )
139
- )
140
-
141
- if args["output"]:
142
- pipe_lines_to_file(badblocks, args["output"])
143
-
144
- return badblocks
145
-
146
-
147
- def update_mapping(
148
- args: UpdateArgs,
149
- device: DeviceConfig,
150
- badblocks: Iterable[int],
151
- n_spare_sectors: int,
152
- ) -> Mapping:
153
- new_mapping: Iterable[tuple[int, int, int]]
154
-
155
- if (device.mapping is None) or (args["reset_mapping"]):
156
- good_ranges = compute_good_ranges(
157
- badblocks, available_range=device.logical_range
158
- )
159
- good_ranges = reserve_space_from_good_ranges(good_ranges, n_spare_sectors)
160
- good_ranges = reserve_space_from_good_ranges(
161
- good_ranges,
162
- int(100 * 1024 / device.sector_size),
163
- )
164
- new_mapping = generate_mapping(
165
- good_ranges,
166
- )
167
- else:
168
- spare_sectors = iter_all_spare_sectors(n_spare_sectors, device.logical_range[0])
169
- n_used_spare_sectors = count_used_spare_sectors(device.mapping, spare_sectors)
170
- current_mapping = map(lambda x: x.to_tuple(), device.mapping)
171
- new_mapping = remap_badblocks(
172
- current_mapping,
173
- badblocks,
174
- spare_sectors=iter_free_spare_sectors(spare_sectors, n_used_spare_sectors),
175
- )
176
-
177
- new_mapping = simplify_mapping(list(new_mapping))
178
- return Mapping(set(map(MappingElement.from_tuple, new_mapping)))
179
-
180
-
181
- def update(dc: DevicesConfig, _args: Namespace) -> None:
182
- """
183
- Update the mapping of a device in the database.
184
- """
185
- args = parse_args(_args)
186
-
187
- device: DeviceConfig = get_device_from_args(args, dc)
188
- block_range = get_update_block_range_from_args(args, device)
189
- n_spare_sectors = get_n_spare_sectors_from_args(args, device)
190
-
191
- known_badblocks = get_starting_badblocks(args, device)
192
- badblocks = get_current_badblocks(args, known_badblocks, device, block_range)
193
-
194
- print(
195
- f"Badblocks computed. Badblocks went from {len(known_badblocks)} to {len(badblocks)}."
196
- )
197
-
198
- dc.update_device(
199
- id=device.id,
200
- badblocks=badblocks,
201
- )
202
-
203
- print("Badblocks list updated.")
204
-
205
- new_mapping = update_mapping(args, device, badblocks, n_spare_sectors)
206
- dc.update_device(id=device.id, mapping=new_mapping, spare_sectors=n_spare_sectors)
207
-
208
- print("Mapping updated.")
cli/commands/version.py DELETED
@@ -1,15 +0,0 @@
1
- import os
2
- from tomllib import load
3
-
4
-
5
- def get_version() -> str:
6
-
7
- pyproject = os.path.join(
8
- os.path.dirname(os.path.abspath(__name__)), "pyproject.toml"
9
- )
10
- with open(pyproject, "rb") as f:
11
- return load(f)["project"]["version"]
12
-
13
-
14
- def version():
15
- print(f"remap-badblocks version v{get_version()}")
@@ -1,130 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: remap_badblocks
3
- Version: 0.8
4
- Summary: CLI tool for remapping bad sectors on Linux disks using device-mapper
5
- Author-email: Luigi Privitera <priviteraluigi98@gmail.com>
6
- License-Expression: GPL-3.0-only
7
- Project-URL: Repository, https://gitlab.com/Luigi-98/remap_badblocks.git
8
- Project-URL: Issues, https://gitlab.com/Luigi-98/remap_badblocks/-/issues
9
- Requires-Python: >=3.10
10
- Description-Content-Type: text/markdown
11
- License-File: LICENSE
12
- Provides-Extra: dev
13
- Requires-Dist: mypy; extra == "dev"
14
- Requires-Dist: flake8; extra == "dev"
15
- Requires-Dist: ruff; extra == "dev"
16
- Requires-Dist: pytest>=7.0; extra == "dev"
17
- Requires-Dist: pre-commit; extra == "dev"
18
- Requires-Dist: black; extra == "dev"
19
- Requires-Dist: isort; extra == "dev"
20
- Dynamic: license-file
21
-
22
- # remap\_badblocks
23
-
24
- `remap_badblocks` is a Linux utility that lets you safely create a virtual block device which skips bad sectors and transparently remaps new bad sectors to healthy spare sectors elsewhere on the same disk. It is useful for squeezing safe, stable usage out of partially failing disks.
25
-
26
- ## 🔧 How It Works
27
-
28
- * Stores devices configurations in a persistent database (this is crucial for accessing the remapped disks)
29
- * badblocks
30
- * mapping between real and virtual blocks
31
- * number of spare sectors in the device
32
- * It can either create a new mapping from scratch or update the existing one to remap new badblocks by keeping everything intact
33
- * It can generate device-mapper devices that follow the stored mappings
34
-
35
- ## 🚀 Usage
36
-
37
- ```bash
38
- remap-badblocks [-h] [-P DB_PATH] {add,get,update,apply} ...
39
- ```
40
-
41
- ### Global Options
42
-
43
- * `-P, --db-path` — path to the configuration database (default is internal)
44
-
45
- ### Commands:
46
-
47
- #### `add`
48
-
49
- Register a new device in the remap database.
50
-
51
- ```bash
52
- remap-badblocks add [--wwn WWN] [--path PATH] name
53
- ```
54
-
55
- * `name`: required alias for the device
56
- * `--wwn`: optional WWN of the device
57
- * `--path`: optional device path (e.g., `/dev/sdX`)
58
-
59
- #### `get`
60
-
61
- Retrieve registered devices or a specific one:
62
-
63
- ```bash
64
- remap-badblocks get [--id ID]
65
- ```
66
-
67
- #### `update`
68
-
69
- Compute and store badblocks, update the mapping:
70
-
71
- ```bash
72
- remap-badblocks update [--mode {read,write,skip}] [--block-range RANGE] [--output OUTPUT] [--spare-space N] id
73
- ```
74
-
75
- * `--mode`: how badblocks should operate (default: `read`)
76
- * `--block-range`: e.g., `0-100000` or `1573-`
77
- * `--output`: also save badblocks to a file
78
- * `--spare-space`: reserve N good sectors for future remapping
79
-
80
- #### `apply`
81
-
82
- Apply the device-mapper table to create a virtual remapped device:
83
-
84
- ```bash
85
- remap-badblocks apply [--id ID] [--method device-mapper]
86
- ```
87
-
88
- ## 🛠️ Example Workflow
89
-
90
- 1. Register the device:
91
-
92
- ```bash
93
- remap-badblocks add --path /dev/disk/by-id/myid mydrive
94
- ```
95
-
96
- 2. Run badblocks scan and create the mapping:
97
-
98
- ```bash
99
- remap-badblocks update --mode read --spare-space 512MB --id 1
100
- ```
101
-
102
- 3. Apply the remapping to create a safe virtual block device:
103
-
104
- ```bash
105
- remap-badblocks apply --id 1
106
- ```
107
-
108
- 4. Use `/dev/mapper/mydrive` as your new clean device
109
-
110
- ## ⚠️ Warnings
111
-
112
- * This tool **does not recover corrupted data**, it only prevents future reads/writes from known bad sectors.
113
- * Only usable on non-boot drives.
114
- * Always keep backups.
115
- * Devices are not persistent between boots, you have to manually apply them at startup (`remap-badblocks apply`), but this will be changed in the future.
116
-
117
- ## 📆 Future Plans
118
-
119
- ### Done
120
- - [x] Manage dependencies between different devices
121
- - [x] Empty db column for "apply at startup"
122
- - [x] Leave free space in disks for possible future metadata (around 4-8B per badblock + 12-24B per mapping, let's consider around 1k badblocks + 1k mappings = 16-32kB => maybe 100kB is good)
123
- - [x] Check already applied devices
124
- - [x] Add version option for getting version
125
-
126
- ## 🤝 Contributions Welcome
127
-
128
- Feel free to submit improvements or suggestions.
129
-
130
- For issues or feature requests, please [open a card on GitLab](https://gitlab.com/Luigi-98/remap_badblocks/-/issues).
@@ -1,66 +0,0 @@
1
- __init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
- cli/__main__.py,sha256=9p8yZNyu012JYMfAkQVcVkhShaoyre44jcYzK0ow4NY,7132
4
- cli/commands/__init__.py,sha256=tGo7_yWZsDpvIIcA9y-4AlLB6grVlK2zaJ6VUKdpONo,216
5
- cli/commands/add.py,sha256=XRNtzUlwRmFD-5VM2us-odvx5smUcIBM6MBF7786p84,2693
6
- cli/commands/apply.py,sha256=uwLqEGYz4HVV_5V5Fe3dDDpmS9pt6VUmdZBsVyCnwyk,2534
7
- cli/commands/get.py,sha256=qDb_KP4YZA9wiXMlXk2i19TDRGaHasPQKcnkWiVv2dA,740
8
- cli/commands/remove.py,sha256=C3eBGmujtRktBJlGCtvdCRHBVWjRaTDx0o-LQpLEKB8,1186
9
- cli/commands/update.py,sha256=MA2Ka3tVEC7xg1rx8_dvzThd6huPyZOE0czSsEIoNM8,7019
10
- cli/commands/version.py,sha256=dAZdvgVLx5V2dMuRCLiEHFP4onIFcvUJ3iT6Bxg-1NE,323
11
- remap_badblocks/__init__.py,sha256=RBxHP7TNTzbZGK9jUdRbwkQQz4RsI_7AIusux77Kjmg,68
12
- remap_badblocks/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
- remap_badblocks/cli/__main__.py,sha256=9p8yZNyu012JYMfAkQVcVkhShaoyre44jcYzK0ow4NY,7132
14
- remap_badblocks/cli/commands/__init__.py,sha256=tGo7_yWZsDpvIIcA9y-4AlLB6grVlK2zaJ6VUKdpONo,216
15
- remap_badblocks/cli/commands/add.py,sha256=XRNtzUlwRmFD-5VM2us-odvx5smUcIBM6MBF7786p84,2693
16
- remap_badblocks/cli/commands/apply.py,sha256=uwLqEGYz4HVV_5V5Fe3dDDpmS9pt6VUmdZBsVyCnwyk,2534
17
- remap_badblocks/cli/commands/get.py,sha256=qDb_KP4YZA9wiXMlXk2i19TDRGaHasPQKcnkWiVv2dA,740
18
- remap_badblocks/cli/commands/remove.py,sha256=C3eBGmujtRktBJlGCtvdCRHBVWjRaTDx0o-LQpLEKB8,1186
19
- remap_badblocks/cli/commands/update.py,sha256=h-PJNWNPSuobOV1byl4nlOtjCFuWG2zGjt2fc8O55RA,7109
20
- remap_badblocks/cli/commands/version.py,sha256=uJIZLqK8zlNEgXlx-V7-yhhvsZfBKAE1tlF76t_Fg2I,168
21
- remap_badblocks/src/devices_config_constants.py,sha256=spRtelXdR9kWw-MzFI-CWfmZ6QM0gXvVHbk4J1CeKcM,103
22
- remap_badblocks/src/mapping.py,sha256=o_r6fiPkzwmsUqzspVkWY7PjWGdRMgxjE5n4w1lEAls,3222
23
- remap_badblocks/src/test_utils.py,sha256=6kzIHJ04VJMQFYLYNw6wLX0Zy-i_aWDRmWc-zFsiw7Y,437
24
- remap_badblocks/src/badblocks/_compute_good_ranges.py,sha256=hMNiDurMvtxyt9VxJYTqF_i-8S0_zBLvzKybC6rA9Js,1425
25
- remap_badblocks/src/badblocks/_find_badblocks.py,sha256=aKgCsUpMj0KDPh_WhAXM5-sxTouVgy61ZS2UyPvcdfQ,2263
26
- remap_badblocks/src/badblocks/_mapping_generation.py,sha256=M9ENd3fol_C0PA2Pi7AcZ9KRPpUqiVAq8-8n7u_I0Ec,381
27
- remap_badblocks/src/badblocks/_remap_badblocks.py,sha256=G9ZAzGJmG_hGUrKAcGHjn6I3XGYU_BPJWPzryZY7RBk,3963
28
- remap_badblocks/src/badblocks/badblocks.py,sha256=AYuh0VRG6vY7of_fqqTCeoe2METVJ-xy7ZvMZw7VqTI,1046
29
- remap_badblocks/src/devices/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
30
- remap_badblocks/src/devices/device_config.py,sha256=5ZkCxvObgxGf1zVOgzy8GlEX4BfHp8KlD7XzdkJI4zs,1943
31
- remap_badblocks/src/devices/devices_config.py,sha256=wxOuLfpAHqEHPTZz3LafqZxISGvQWaBlI0kR0Qhl1fk,10380
32
- remap_badblocks/src/devices/exceptions.py,sha256=2RHEZAHQj_bl4B5KHGuqURZswHOMV0GelXXVN7EugdY,825
33
- remap_badblocks/src/remappers/_check_applied_devices.py,sha256=3Wv5TEoKtszKhcyNNL2pzcB6BuYLmQIJ16mlpOqlw34,314
34
- remap_badblocks/src/remappers/_generate_dm_table.py,sha256=7BJnqc7Ijgd8SDqdgZvEz6gDJeFokf0hDtm5UjDHlgg,961
35
- remap_badblocks/src/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
36
- remap_badblocks/src/utils/_get_device_info.py,sha256=HSERimNgOpi_0VIc9LYWLjWBnLRISo2na-pcxbA2icU,1479
37
- remap_badblocks/src/utils/_iterable_bytes_converter.py,sha256=H0PDLANQLTaakNBHMD_ZZ0Js3seRgXZgs1lxFrqrYAw,586
38
- remap_badblocks/src/utils/_parse_inputs.py,sha256=KggQ80y97LIAolbhS1OsBMVdt-XDXDO3w4glJQPd4Tw,2772
39
- remap_badblocks/src/utils/_run_command.py,sha256=3EXIPcjx7MFsXGSDDfnS2qBwXfV-98Efj_enfQS1C_Q,2435
40
- remap_badblocks/src/utils/_sort_devices.py,sha256=f9w3DoMbRdkDjjaoSrz8qnmDzfaOvk2I_mq4-wwS65A,876
41
- remap_badblocks-0.8.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
42
- src/devices_config_constants.py,sha256=spRtelXdR9kWw-MzFI-CWfmZ6QM0gXvVHbk4J1CeKcM,103
43
- src/mapping.py,sha256=o_r6fiPkzwmsUqzspVkWY7PjWGdRMgxjE5n4w1lEAls,3222
44
- src/test_utils.py,sha256=6kzIHJ04VJMQFYLYNw6wLX0Zy-i_aWDRmWc-zFsiw7Y,437
45
- src/badblocks/_compute_good_ranges.py,sha256=hMNiDurMvtxyt9VxJYTqF_i-8S0_zBLvzKybC6rA9Js,1425
46
- src/badblocks/_find_badblocks.py,sha256=aKgCsUpMj0KDPh_WhAXM5-sxTouVgy61ZS2UyPvcdfQ,2263
47
- src/badblocks/_mapping_generation.py,sha256=M9ENd3fol_C0PA2Pi7AcZ9KRPpUqiVAq8-8n7u_I0Ec,381
48
- src/badblocks/_remap_badblocks.py,sha256=G9ZAzGJmG_hGUrKAcGHjn6I3XGYU_BPJWPzryZY7RBk,3963
49
- src/badblocks/badblocks.py,sha256=AYuh0VRG6vY7of_fqqTCeoe2METVJ-xy7ZvMZw7VqTI,1046
50
- src/devices/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
51
- src/devices/device_config.py,sha256=5ZkCxvObgxGf1zVOgzy8GlEX4BfHp8KlD7XzdkJI4zs,1943
52
- src/devices/devices_config.py,sha256=KT0mjop4z3qodHSeJPi5StYzdcUE8WU9te7LIro2CPc,10363
53
- src/devices/exceptions.py,sha256=2RHEZAHQj_bl4B5KHGuqURZswHOMV0GelXXVN7EugdY,825
54
- src/remappers/_check_applied_devices.py,sha256=3Wv5TEoKtszKhcyNNL2pzcB6BuYLmQIJ16mlpOqlw34,314
55
- src/remappers/_generate_dm_table.py,sha256=7BJnqc7Ijgd8SDqdgZvEz6gDJeFokf0hDtm5UjDHlgg,961
56
- src/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
57
- src/utils/_get_device_info.py,sha256=HSERimNgOpi_0VIc9LYWLjWBnLRISo2na-pcxbA2icU,1479
58
- src/utils/_iterable_bytes_converter.py,sha256=H0PDLANQLTaakNBHMD_ZZ0Js3seRgXZgs1lxFrqrYAw,586
59
- src/utils/_parse_inputs.py,sha256=KggQ80y97LIAolbhS1OsBMVdt-XDXDO3w4glJQPd4Tw,2772
60
- src/utils/_run_command.py,sha256=3EXIPcjx7MFsXGSDDfnS2qBwXfV-98Efj_enfQS1C_Q,2435
61
- src/utils/_sort_devices.py,sha256=f9w3DoMbRdkDjjaoSrz8qnmDzfaOvk2I_mq4-wwS65A,876
62
- remap_badblocks-0.8.dist-info/METADATA,sha256=K6CWLMMCnTS-RzcfcLT4nYt_4rnw2SY0wWctr9VUm-w,3974
63
- remap_badblocks-0.8.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
64
- remap_badblocks-0.8.dist-info/entry_points.txt,sha256=JmLHnqW16bljetWHJrYYAuP0vBrxfO0QY0mFdaWqOKg,70
65
- remap_badblocks-0.8.dist-info/top_level.txt,sha256=FqRo65stYjF_GyJQCLGIQN1Tj0YQcaiinI5bvzd0OpY,16
66
- remap_badblocks-0.8.dist-info/RECORD,,
@@ -1,43 +0,0 @@
1
- from typing import Generator, Iterable
2
-
3
-
4
- def compute_good_ranges(
5
- bad_sectors: Iterable[int], available_range: tuple[int, int]
6
- ) -> Generator[tuple[int, int], None, None]:
7
- """
8
- Compute good sector ranges, excluding bad sectors.
9
- available_range is expected to be a range that is right-open
10
- result is a range that is right-open
11
- """
12
- bad_sectors = sorted(set(bad_sectors)) # Ensure bad_sectors is sorted and unique
13
- current_start = available_range[0]
14
- last_sector = available_range[1] - 1
15
-
16
- for bad_sector in bad_sectors:
17
- if bad_sector > last_sector:
18
- break
19
- if current_start < bad_sector:
20
- yield (current_start, bad_sector)
21
- current_start = bad_sector + 1
22
-
23
- if current_start <= last_sector:
24
- yield (current_start, last_sector + 1)
25
-
26
-
27
- def reserve_space_from_good_ranges(
28
- good_ranges: Iterable[tuple[int, int]], spare_sectors: int
29
- ) -> Generator[tuple[int, int], None, None]:
30
- """
31
- Return good sector ranges, skipping the first `spare_sectors` good sectors as reserved space.
32
- Both input and output ranges are right-open.
33
- """
34
- to_reserve = spare_sectors
35
- for start, end in good_ranges:
36
- length = end - start
37
- if to_reserve >= length:
38
- to_reserve -= length
39
- continue
40
- elif to_reserve > 0:
41
- start += to_reserve
42
- to_reserve = 0
43
- yield (start, end)
@@ -1,76 +0,0 @@
1
- from itertools import chain
2
- from pathlib import Path
3
- from typing import Iterable, Optional, Union
4
-
5
- from remap_badblocks.src.utils._run_command import run_command_realtime
6
-
7
-
8
- def parse_badblocks(lines: Iterable[str]) -> Iterable[int]:
9
- """Parse badblocks output and return a set of bad sector numbers."""
10
- lines = map(lambda line: line.strip(), lines)
11
- lines = filter(lambda line: line.isdigit(), lines)
12
- return map(int, lines)
13
-
14
-
15
- def read_known_badblocks(known_badblocks_file: Union[Path, str]) -> Iterable[int]:
16
- """Merge badblocks with known badblocks from a file."""
17
- with open(known_badblocks_file, "r") as f:
18
- known_badblocks = parse_badblocks(f.readlines())
19
- return known_badblocks
20
-
21
-
22
- def build_badblocks_command(
23
- device: Union[Path, str],
24
- sector_size: int,
25
- mode: str,
26
- known_badblocks_file: Optional[Union[Path, str]],
27
- blocks_range: Optional[tuple[int, int]],
28
- ) -> list[str]:
29
- """
30
- Build the badblocks command based on the mode and optional known badblocks file.
31
- blocks_range is a right-open interval
32
- """
33
- if mode == "read":
34
- cmd = ["badblocks", "-sv"]
35
- elif mode == "write":
36
- cmd = ["badblocks", "-wsv"]
37
- else:
38
- raise ValueError("Invalid mode. Use 'read' or 'write'.")
39
-
40
- cmd += ["-b", str(sector_size)]
41
-
42
- if known_badblocks_file:
43
- cmd += ["-i", str(known_badblocks_file)]
44
-
45
- cmd += [str(device)]
46
-
47
- if blocks_range:
48
- start_block, end_block = blocks_range
49
- cmd += [str(end_block), str(start_block)]
50
-
51
- return cmd
52
-
53
-
54
- def get_all_badblocks(
55
- device: Path,
56
- sector_size: int,
57
- mode: str = "read",
58
- known_badblocks_file: Optional[Path] = None,
59
- block_range: Optional[tuple[int, int]] = None,
60
- ) -> Iterable[int]:
61
- """
62
- Run badblocks on a device and return the results as a set of bad sectors.
63
- block_range is a right-open interval
64
- """
65
- badblocks: Iterable[int] = set()
66
-
67
- if known_badblocks_file:
68
- badblocks = read_known_badblocks(known_badblocks_file)
69
-
70
- cmd = build_badblocks_command(
71
- device, sector_size, mode, known_badblocks_file, block_range
72
- )
73
- badblocks_lines = run_command_realtime(cmd)
74
- badblocks = chain(badblocks, parse_badblocks(badblocks_lines))
75
-
76
- return badblocks
@@ -1,12 +0,0 @@
1
- from typing import Generator, Iterable
2
-
3
-
4
- def generate_mapping(
5
- ranges: Iterable[tuple[int, int]],
6
- ) -> Generator[tuple[int, int, int], None, None]:
7
- """Generate a mapping from good ranges."""
8
- offset = 0
9
- for start, end in ranges:
10
- length = end - start
11
- yield (offset, start, length) # (start_id_virtual, start_id_real, length)
12
- offset += length
@@ -1,114 +0,0 @@
1
- import itertools
2
- from typing import Iterable, Iterator, Optional, Union
3
-
4
- from remap_badblocks.src.devices.devices_config import Mapping
5
-
6
-
7
- def remap_badblocks(
8
- mapping: Iterable[tuple[int, int, int]],
9
- badblocks: Iterable[int],
10
- spare_sectors: Iterator[int],
11
- ) -> Iterable[tuple[int, int, int]]:
12
- sorted_badblocks = sorted(badblocks)
13
-
14
- for start_virtual, start_real, length in mapping:
15
- end_real = start_real + length - 1
16
- sorted_to_remap: list[int] = []
17
- sorted_to_remap_virtual: list[int] = []
18
- for badblock in sorted_badblocks:
19
- if start_real <= badblock <= end_real:
20
- sorted_to_remap.append(badblock)
21
- if not sorted_to_remap:
22
- # nothing to remap
23
- yield start_virtual, start_real, length
24
- else:
25
- current_start_virtual = start_virtual
26
- current_start_real = start_real
27
- for badblock in sorted_to_remap:
28
- if current_start_real < badblock:
29
- # pieces to keep
30
- yield current_start_virtual, current_start_real, badblock - current_start_real
31
- step = badblock - current_start_real + 1
32
- current_start_virtual += step
33
- current_start_real += step
34
- sorted_to_remap_virtual.append(current_start_virtual - 1)
35
- if current_start_real < end_real:
36
- # pieces to keep
37
- yield current_start_virtual, current_start_real, end_real - current_start_real + 1
38
- for virt_to_remap in sorted_to_remap_virtual:
39
- try:
40
- while (spare_sector := next(spare_sectors)) in badblocks:
41
- pass
42
- except StopIteration:
43
- raise RuntimeError
44
- # remap
45
- yield virt_to_remap, spare_sector, 1
46
-
47
-
48
- def identify_simplifiable_couple_in_mapping(
49
- mapping: list[tuple[int, int, int]],
50
- ) -> Optional[tuple[int, int]]:
51
- for i, (start_virtual_0, start_real_0, length_0) in enumerate(mapping[:-1]):
52
- end_virtual_0 = start_virtual_0 + length_0
53
- end_real_0 = start_real_0 + length_0
54
- for _j, (start_virtual_1, start_real_1, length_1) in enumerate(
55
- mapping[i + 1 :]
56
- ):
57
- j = _j + i + 1
58
- end_virtual_1 = start_virtual_1 + length_1
59
- end_real_1 = start_real_1 + length_1
60
- if (start_virtual_0 == end_virtual_1) and (start_real_0 == end_real_1):
61
- return j, i
62
- if (start_virtual_1 == end_virtual_0) and (start_real_1 == end_real_0):
63
- return i, j
64
- return None
65
-
66
-
67
- def simplify_mapping(
68
- mapping: list[tuple[int, int, int]],
69
- ) -> Iterable[tuple[int, int, int]]:
70
- while (to_simplify := identify_simplifiable_couple_in_mapping(mapping)) is not None:
71
- i, j = to_simplify
72
- element_i = mapping[i]
73
- element_j = mapping[j]
74
-
75
- simplified_element = element_i[0], element_i[1], element_i[2] + element_j[2]
76
-
77
- _i = min(i, j)
78
- _j = max(i, j)
79
-
80
- mapping = (
81
- mapping[:_i]
82
- + mapping[_i + 1 : _j]
83
- + mapping[_j + 1 :]
84
- + [simplified_element]
85
- )
86
-
87
- return mapping
88
-
89
-
90
- def iter_all_spare_sectors(
91
- n_spare_sectors: int, first_spare_sector: int
92
- ) -> Iterator[int]:
93
- return iter(range(first_spare_sector, first_spare_sector + n_spare_sectors))
94
-
95
-
96
- def iter_free_spare_sectors(
97
- spare_sectors: Iterable[int], n_used_spare_sectors: int
98
- ) -> Iterator[int]:
99
- return itertools.islice(spare_sectors, n_used_spare_sectors, None)
100
-
101
-
102
- def count_used_spare_sectors(
103
- mapping: Union[Iterable[tuple[int, int, int]], Mapping],
104
- spare_sectors: Iterable[int],
105
- ) -> int:
106
- spare_sectors = set(spare_sectors)
107
- used = 0
108
- for _, start, length in mapping:
109
- end = start + length
110
- for s in spare_sectors:
111
- if start <= s < end:
112
- used += 1
113
-
114
- return used