remap-badblocks 0.7__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.
- remap_badblocks/__init__.py +3 -0
- remap_badblocks/cli/commands/update.py +5 -1
- remap_badblocks/cli/commands/version.py +2 -8
- remap_badblocks/src/devices/devices_config.py +1 -1
- remap_badblocks-0.8.1.dist-info/METADATA +107 -0
- remap_badblocks-0.8.1.dist-info/RECORD +36 -0
- __init__.py +0 -0
- cli/__init__.py +0 -0
- cli/__main__.py +0 -239
- cli/commands/__init__.py +0 -8
- cli/commands/add.py +0 -91
- cli/commands/apply.py +0 -81
- cli/commands/get.py +0 -28
- cli/commands/remove.py +0 -45
- cli/commands/update.py +0 -208
- cli/commands/version.py +0 -15
- remap_badblocks-0.7.dist-info/METADATA +0 -130
- remap_badblocks-0.7.dist-info/RECORD +0 -66
- src/badblocks/_compute_good_ranges.py +0 -43
- src/badblocks/_find_badblocks.py +0 -76
- src/badblocks/_mapping_generation.py +0 -12
- src/badblocks/_remap_badblocks.py +0 -114
- src/badblocks/badblocks.py +0 -40
- src/devices/__init__.py +0 -0
- src/devices/device_config.py +0 -62
- src/devices/devices_config.py +0 -300
- src/devices/exceptions.py +0 -22
- src/devices_config_constants.py +0 -3
- src/mapping.py +0 -109
- src/remappers/_check_applied_devices.py +0 -10
- src/remappers/_generate_dm_table.py +0 -27
- src/test_utils.py +0 -18
- src/utils/__init__.py +0 -0
- src/utils/_get_device_info.py +0 -43
- src/utils/_iterable_bytes_converter.py +0 -19
- src/utils/_parse_inputs.py +0 -84
- src/utils/_run_command.py +0 -76
- src/utils/_sort_devices.py +0 -26
- {remap_badblocks-0.7.dist-info → remap_badblocks-0.8.1.dist-info}/WHEEL +0 -0
- {remap_badblocks-0.7.dist-info → remap_badblocks-0.8.1.dist-info}/entry_points.txt +0 -0
- {remap_badblocks-0.7.dist-info → remap_badblocks-0.8.1.dist-info}/licenses/LICENSE +0 -0
- {remap_badblocks-0.7.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.7
|
|
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=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
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=MA2Ka3tVEC7xg1rx8_dvzThd6huPyZOE0czSsEIoNM8,7019
|
|
20
|
-
remap_badblocks/cli/commands/version.py,sha256=dAZdvgVLx5V2dMuRCLiEHFP4onIFcvUJ3iT6Bxg-1NE,323
|
|
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=KT0mjop4z3qodHSeJPi5StYzdcUE8WU9te7LIro2CPc,10363
|
|
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.7.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.7.dist-info/METADATA,sha256=w9kicxxd5oFt4oo1K5UrzpnSu31Pava6E4G9h5IAkMc,3974
|
|
63
|
-
remap_badblocks-0.7.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
64
|
-
remap_badblocks-0.7.dist-info/entry_points.txt,sha256=JmLHnqW16bljetWHJrYYAuP0vBrxfO0QY0mFdaWqOKg,70
|
|
65
|
-
remap_badblocks-0.7.dist-info/top_level.txt,sha256=FqRo65stYjF_GyJQCLGIQN1Tj0YQcaiinI5bvzd0OpY,16
|
|
66
|
-
remap_badblocks-0.7.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)
|
src/badblocks/_find_badblocks.py
DELETED
|
@@ -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
|