Skip to content

Commit 3b6819c

Browse files
authored
Add crucible-verify-raw and crucible-raw-extent packages (#1800)
This PR extracts the `crucible-verify-raw` tool from #1791. The on-disk data structures for raw extents are moved to a new `crucible-raw-extent` package, which is used by both `crucible-downstairs` and `crucible-verify-raw`.
1 parent d3c367a commit 3b6819c

File tree

10 files changed

+358
-79
lines changed

10 files changed

+358
-79
lines changed

Cargo.lock

Lines changed: 25 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ members = [
3232
"workspace-hack",
3333
"xtask",
3434
"agent-antagonist",
35+
"verify-raw",
36+
"raw-extent",
3537
]
3638
resolver = "2"
3739

@@ -155,6 +157,8 @@ crucible-pantry-api = { path = "./pantry-api" }
155157
crucible-pantry-client = { path = "./pantry-client" }
156158
crucible-pantry-types = { path = "./pantry-types" }
157159
crucible-protocol = { path = "./protocol" }
160+
crucible-raw-extent = { path = "./raw-extent" }
161+
crucible-verify-raw = { path = "./verify-raw" }
158162
crucible-smf = { path = "./smf" }
159163
dsc-client = { path = "./dsc-client" }
160164
repair-client = { path = "./repair-client" }

downstairs/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ crucible-common.workspace = true
1717
crucible-downstairs-api.workspace = true
1818
crucible-downstairs-types.workspace = true
1919
crucible-protocol.workspace = true
20+
crucible-raw-extent.workspace = true
2021
dropshot.workspace = true
2122
futures-core.workspace = true
2223
futures.workspace = true
@@ -42,7 +43,6 @@ ringbuffer.workspace = true
4243
rusqlite.workspace = true
4344
schemars.workspace = true
4445
semver.workspace = true
45-
serde.workspace = true
4646
serde_json.workspace = true
4747
sha2.workspace = true
4848
slog-async.workspace = true

downstairs/src/extent.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ use std::fs::File;
66
use anyhow::{anyhow, bail, Result};
77
use nix::unistd::{sysconf, SysconfVar};
88

9-
use serde::{Deserialize, Serialize};
109
use tracing::instrument;
1110

1211
use crate::region::JobOrReconciliationId;
@@ -135,7 +134,7 @@ pub enum ExtentState {
135134
Closed,
136135
}
137136

138-
#[derive(Debug, Deserialize, Serialize)]
137+
#[derive(Debug)]
139138
pub struct ExtentMeta {
140139
/**
141140
* Version information regarding the extent structure.
@@ -471,7 +470,8 @@ impl Extent {
471470
Box::new(inner)
472471
} else {
473472
match extent_inner_raw_common::OnDiskMeta::get_version_tag(
474-
dir, number,
473+
&extent_path(dir, number),
474+
number,
475475
)? {
476476
EXTENT_META_RAW => {
477477
Box::new(extent_inner_raw::RawInner::open(

downstairs/src/extent_inner_raw.rs

Lines changed: 7 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,13 @@ use crate::{
1010
},
1111
integrity_hash, mkdir_for_file,
1212
region::JobOrReconciliationId,
13-
Block, BlockContext, CrucibleError, ExtentReadRequest, ExtentReadResponse,
14-
ExtentWrite, JobId, RegionDefinition,
13+
Block, CrucibleError, ExtentReadRequest, ExtentReadResponse, ExtentWrite,
14+
JobId, RegionDefinition,
1515
};
1616
use crucible_common::ExtentId;
1717
use crucible_protocol::ReadBlockContext;
1818

1919
use itertools::Itertools;
20-
use serde::{Deserialize, Serialize};
2120
use slog::{error, Logger};
2221

2322
use std::collections::HashSet;
@@ -26,19 +25,10 @@ use std::io::{BufReader, Read};
2625
use std::os::fd::{AsFd, AsRawFd};
2726
use std::path::Path;
2827

29-
/// Equivalent to `DownstairsBlockContext`, but without one's own block number
30-
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
31-
struct OnDiskDownstairsBlockContext {
32-
block_context: BlockContext,
33-
on_disk_hash: u64,
34-
flush_id: u16,
35-
}
36-
37-
/// Size of backup data
38-
///
39-
/// This must be large enough to fit an `Option<OnDiskDownstairsBlockContext>`
40-
/// serialized using `bincode`.
41-
pub const BLOCK_CONTEXT_SLOT_SIZE_BYTES: u64 = 48;
28+
// Re-exports
29+
pub use crucible_raw_extent::{
30+
OnDiskDownstairsBlockContext, BLOCK_CONTEXT_SLOT_SIZE_BYTES,
31+
};
4232

4333
/// Number of extra syscalls per read / write that triggers defragmentation
4434
const DEFRAGMENT_THRESHOLD: u64 = 3;
@@ -1609,7 +1599,7 @@ mod test {
16091599
use anyhow::Result;
16101600
use bytes::{Bytes, BytesMut};
16111601
use crucible_common::BlockOffset;
1612-
use crucible_protocol::EncryptionContext;
1602+
use crucible_protocol::{BlockContext, EncryptionContext};
16131603
use tempfile::tempdir;
16141604

16151605
const IOV_MAX_TEST: usize = 1000;

downstairs/src/extent_inner_raw_common.rs

Lines changed: 2 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,62 +1,7 @@
1-
use crate::{extent::extent_path, CrucibleError};
2-
use crucible_common::ExtentId;
3-
use serde::{Deserialize, Serialize};
4-
use std::fs::OpenOptions;
5-
use std::io::{Read, Seek, SeekFrom};
61
use std::os::fd::AsFd;
7-
use std::path::Path;
82

9-
/// Equivalent to `ExtentMeta`, but ordered for efficient on-disk serialization
10-
///
11-
/// In particular, the `dirty` byte is first, so it's easy to read at a known
12-
/// offset within the file.
13-
#[derive(Debug, Clone, Serialize, Deserialize)]
14-
pub(super) struct OnDiskMeta {
15-
pub dirty: bool,
16-
pub gen_number: u64,
17-
pub flush_number: u64,
18-
pub ext_version: u32,
19-
20-
// Extra data added for debugging purposes
21-
pub bonus_sync_count: u32,
22-
pub defrag_count: u32,
23-
}
24-
25-
impl OnDiskMeta {
26-
/// Looks up the version tag
27-
///
28-
/// Across all of our raw file formats, `OnDiskMeta` is guaranteed to be
29-
/// placed at the end of the file in a `BLOCK_META_SIZE_BYTES`-length chunk,
30-
/// so we can get a tag without knowing anything else about the file.
31-
pub fn get_version_tag(
32-
dir: &Path,
33-
extent_number: ExtentId,
34-
) -> Result<u32, CrucibleError> {
35-
let path = extent_path(dir, extent_number);
36-
let mut f = OpenOptions::new()
37-
.read(true)
38-
.write(false)
39-
.open(&path)
40-
.map_err(|e| {
41-
CrucibleError::IoError(format!(
42-
"extent {extent_number}: open of {path:?} failed: {e}",
43-
))
44-
})?;
45-
46-
let mut buf = [0u8; BLOCK_META_SIZE_BYTES as usize];
47-
f.seek(SeekFrom::End(-(BLOCK_META_SIZE_BYTES as i64)))?;
48-
f.read_exact(&mut buf)?;
49-
let meta: OnDiskMeta = bincode::deserialize(&buf)
50-
.map_err(|e| CrucibleError::BadMetadata(e.to_string()))?;
51-
Ok(meta.ext_version)
52-
}
53-
}
54-
55-
/// Size of metadata region
56-
///
57-
/// This must be large enough to contain an `OnDiskMeta` serialized using
58-
/// `bincode`.
59-
pub(super) const BLOCK_META_SIZE_BYTES: u64 = 32;
3+
// Re-export from `crucible_raw_extent`
4+
pub use crucible_raw_extent::{OnDiskMeta, BLOCK_META_SIZE_BYTES};
605

616
/// Call `pread` repeatedly to read an entire buffer
627
///

raw-extent/Cargo.toml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
[package]
2+
name = "crucible-raw-extent"
3+
version = "0.1.0"
4+
edition = "2024"
5+
6+
[dependencies]
7+
serde.workspace = true
8+
crucible-common.workspace = true
9+
crucible-protocol.workspace = true
10+
bincode.workspace = true
11+
12+
crucible-workspace-hack.workspace = true

raw-extent/src/lib.rs

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
//! Data and on-disk structures for raw extent files
2+
use crucible_common::{CrucibleError, ExtentId};
3+
use crucible_protocol::BlockContext;
4+
use serde::{Deserialize, Serialize};
5+
use std::{
6+
fs::OpenOptions,
7+
io::{Read, Seek, SeekFrom},
8+
path::Path,
9+
};
10+
11+
/// Equivalent to `ExtentMeta`, but ordered for efficient on-disk serialization
12+
///
13+
/// In particular, the `dirty` byte is first, so it's easy to read at a known
14+
/// offset within the file.
15+
#[derive(Debug, Clone, Serialize, Deserialize)]
16+
pub struct OnDiskMeta {
17+
pub dirty: bool,
18+
pub gen_number: u64,
19+
pub flush_number: u64,
20+
pub ext_version: u32,
21+
22+
// Extra data added for debugging purposes
23+
pub bonus_sync_count: u32,
24+
pub defrag_count: u32,
25+
}
26+
27+
impl OnDiskMeta {
28+
/// Looks up the version tag
29+
///
30+
/// Across all of our raw file formats, `OnDiskMeta` is guaranteed to be
31+
/// placed at the end of the file in a `BLOCK_META_SIZE_BYTES`-length chunk,
32+
/// so we can get a tag without knowing anything else about the file.
33+
pub fn get_version_tag(
34+
path: &Path,
35+
extent_number: ExtentId,
36+
) -> Result<u32, CrucibleError> {
37+
let mut f = OpenOptions::new()
38+
.read(true)
39+
.write(false)
40+
.open(path)
41+
.map_err(|e| {
42+
CrucibleError::IoError(format!(
43+
"extent {extent_number}: open of {path:?} failed: {e}",
44+
))
45+
})?;
46+
47+
let mut buf = [0u8; BLOCK_META_SIZE_BYTES as usize];
48+
f.seek(SeekFrom::End(-(BLOCK_META_SIZE_BYTES as i64)))?;
49+
f.read_exact(&mut buf)?;
50+
let meta: OnDiskMeta = bincode::deserialize(&buf)
51+
.map_err(|e| CrucibleError::BadMetadata(e.to_string()))?;
52+
Ok(meta.ext_version)
53+
}
54+
}
55+
56+
/// Size of metadata region
57+
///
58+
/// This must be large enough to contain an `OnDiskMeta` serialized using
59+
/// `bincode`.
60+
pub const BLOCK_META_SIZE_BYTES: u64 = 32;
61+
62+
/// Equivalent to `DownstairsBlockContext`, but without one's own block number
63+
#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq)]
64+
pub struct OnDiskDownstairsBlockContext {
65+
pub block_context: BlockContext,
66+
pub on_disk_hash: u64,
67+
pub flush_id: u16,
68+
}
69+
70+
/// Size of backup data
71+
///
72+
/// This must be large enough to fit an `Option<OnDiskDownstairsBlockContext>`
73+
/// serialized using `bincode`.
74+
pub const BLOCK_CONTEXT_SLOT_SIZE_BYTES: u64 = 48;

verify-raw/Cargo.toml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
[package]
2+
name = "crucible-verify-raw"
3+
version = "0.1.0"
4+
edition = "2024"
5+
6+
[dependencies]
7+
anyhow.workspace = true
8+
bincode.workspace = true
9+
clap.workspace = true
10+
crucible-common.workspace = true
11+
crucible-raw-extent.workspace = true
12+
serde.workspace = true
13+
14+
crucible-workspace-hack.workspace = true

0 commit comments

Comments
 (0)