Skip to content

Commit 78c06ec

Browse files
committed
Add way to get the info to open an entry independently of the store
1 parent 56b0695 commit 78c06ec

File tree

5 files changed

+154
-19
lines changed

5 files changed

+154
-19
lines changed

src/store/bao_file.rs

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -189,16 +189,7 @@ impl FileStorage {
189189
}
190190

191191
fn current_size(&self) -> io::Result<u64> {
192-
let len = self.sizes.metadata()?.len();
193-
if len < 8 {
194-
Ok(0)
195-
} else {
196-
// todo: use the last full u64 in case the sizes file is not a multiple of 8
197-
// bytes. Not sure how that would happen, but we should handle it.
198-
let mut buf = [0u8; 8];
199-
self.sizes.read_exact_at(len - 8, &mut buf)?;
200-
Ok(u64::from_le_bytes(buf))
201-
}
192+
read_current_size(&self.sizes)
202193
}
203194

204195
fn write_batch(&mut self, size: u64, batch: &[BaoContentItem]) -> io::Result<()> {
@@ -470,6 +461,18 @@ impl AsyncSliceReader for OutboardReader {
470461
}
471462
}
472463

464+
pub fn read_current_size(sizes: &File) -> io::Result<u64> {
465+
let len = sizes.metadata()?.len();
466+
if len < 8 {
467+
Ok(0)
468+
} else {
469+
let len = len & !7;
470+
let mut buf = [0u8; 8];
471+
sizes.read_exact_at(len - 8, &mut buf)?;
472+
Ok(u64::from_le_bytes(buf))
473+
}
474+
}
475+
473476
enum HandleChange {
474477
None,
475478
MemToFile,

src/store/fs.rs

Lines changed: 83 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -98,9 +98,9 @@ use tables::{ReadOnlyTables, ReadableTables, Tables};
9898

9999
use self::{tables::DeleteSet, test_support::EntryData, util::PeekableFlumeReceiver};
100100
use super::{
101-
bao_file::{BaoFileConfig, BaoFileHandle, BaoFileHandleWeak, CreateCb},
102-
temp_name, BaoBatchWriter, BaoBlobSize, ConsistencyCheckProgress, EntryStatus, ExportMode,
103-
ExportProgressCb, ImportMode, ImportProgress, Map, ReadableStore, TempCounterMap,
101+
bao_file::{read_current_size, BaoFileConfig, BaoFileHandle, BaoFileHandleWeak, CreateCb},
102+
temp_name, BaoBatchWriter, BaoBlobSize, ConsistencyCheckProgress, EntryPathOrData, EntryStatus,
103+
ExportMode, ExportProgressCb, ImportMode, ImportProgress, Map, ReadableStore, TempCounterMap,
104104
};
105105
use crate::{
106106
store::{
@@ -532,6 +532,10 @@ pub(crate) enum ActorMessage {
532532
hash: Hash,
533533
tx: oneshot::Sender<ActorResult<EntryStatus>>,
534534
},
535+
EntryPathOrData {
536+
hash: Hash,
537+
tx: oneshot::Sender<ActorResult<Option<EntryPathOrData>>>,
538+
},
535539
#[cfg(test)]
536540
/// Query method: get the full entry state for a hash, both in memory and in redb.
537541
/// This is everything we got about the entry, including the actual inline outboard and data.
@@ -664,6 +668,7 @@ impl ActorMessage {
664668
| Self::Tags { .. }
665669
| Self::GcStart { .. }
666670
| Self::GetFullEntryState { .. }
671+
| Self::EntryPathOrData { .. }
667672
| Self::Dump => MessageCategory::ReadOnly,
668673
Self::Import { .. }
669674
| Self::Export { .. }
@@ -870,6 +875,14 @@ impl StoreInner {
870875
Ok(tags)
871876
}
872877

878+
async fn entry_path_or_data(&self, hash: Hash) -> OuterResult<Option<EntryPathOrData>> {
879+
let (tx, rx) = oneshot::channel();
880+
self.tx
881+
.send(ActorMessage::EntryPathOrData { hash, tx })
882+
.await?;
883+
Ok(rx.await??)
884+
}
885+
873886
async fn set_tag(&self, tag: Tag, value: Option<HashAndFormat>) -> OuterResult<()> {
874887
let (tx, rx) = oneshot::channel();
875888
self.tx
@@ -1371,6 +1384,10 @@ impl super::Store for Store {
13711384
.await??)
13721385
}
13731386

1387+
async fn entry_path_or_data(&self, hash: Hash) -> io::Result<Option<EntryPathOrData>> {
1388+
Ok(self.0.entry_path_or_data(hash).await?)
1389+
}
1390+
13741391
async fn set_tag(&self, name: Tag, hash: Option<HashAndFormat>) -> io::Result<()> {
13751392
Ok(self.0.set_tag(name, hash).await?)
13761393
}
@@ -2266,6 +2283,65 @@ impl ActorState {
22662283
Ok(())
22672284
}
22682285

2286+
fn entry_path_or_data(
2287+
&mut self,
2288+
tables: &impl ReadableTables,
2289+
hash: Hash,
2290+
) -> ActorResult<Option<EntryPathOrData>> {
2291+
let data_path = || self.options.path.owned_data_path(&hash);
2292+
let outboard_path = || self.options.path.owned_outboard_path(&hash);
2293+
let sizes_path = || self.options.path.owned_sizes_path(&hash);
2294+
Ok(match tables.blobs().get(hash)? {
2295+
Some(guard) => match guard.value() {
2296+
EntryState::Complete {
2297+
data_location,
2298+
outboard_location,
2299+
} => {
2300+
let data = match data_location {
2301+
DataLocation::External(paths, size) => {
2302+
let path = paths.first().ok_or_else(|| {
2303+
ActorError::Inconsistent("external data missing".to_owned())
2304+
})?;
2305+
MemOrFile::File((path.clone(), size))
2306+
}
2307+
DataLocation::Owned(size) => MemOrFile::File((data_path(), size)),
2308+
DataLocation::Inline(_) => {
2309+
let data = tables.inline_data().get(hash)?.ok_or_else(|| {
2310+
ActorError::Inconsistent("inline data missing".to_owned())
2311+
})?;
2312+
MemOrFile::Mem(data.value().to_vec().into())
2313+
}
2314+
};
2315+
let outboard = match outboard_location {
2316+
OutboardLocation::Owned => MemOrFile::File(outboard_path()),
2317+
OutboardLocation::Inline(_) => MemOrFile::Mem(
2318+
tables
2319+
.inline_outboard()
2320+
.get(hash)?
2321+
.ok_or_else(|| {
2322+
ActorError::Inconsistent("inline outboard missing".to_owned())
2323+
})?
2324+
.value()
2325+
.to_vec()
2326+
.into(),
2327+
),
2328+
OutboardLocation::NotNeeded => MemOrFile::Mem(Bytes::new()),
2329+
};
2330+
Some(EntryPathOrData { data, outboard })
2331+
}
2332+
EntryState::Partial { .. } => {
2333+
let sizes = std::fs::File::open(sizes_path())?;
2334+
let size = read_current_size(&sizes)?;
2335+
Some(EntryPathOrData {
2336+
data: MemOrFile::File((data_path(), size)),
2337+
outboard: MemOrFile::File(outboard_path()),
2338+
})
2339+
}
2340+
},
2341+
None => None,
2342+
})
2343+
}
2344+
22692345
fn handle_toplevel(&mut self, db: &redb::Database, msg: ActorMessage) -> ActorResult<()> {
22702346
match msg {
22712347
ActorMessage::UpdateInlineOptions {
@@ -2339,6 +2415,10 @@ impl ActorState {
23392415
let res = self.get_full_entry_state(tables, hash);
23402416
tx.send(res).ok();
23412417
}
2418+
ActorMessage::EntryPathOrData { hash, tx } => {
2419+
let res = self.entry_path_or_data(tables, hash);
2420+
tx.send(res).ok();
2421+
}
23422422
x => return Ok(Err(x)),
23432423
}
23442424
Ok(Ok(()))

src/store/mem.rs

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,16 @@ use futures_lite::{Stream, StreamExt};
1919
use iroh_io::AsyncSliceReader;
2020

2121
use super::{
22-
temp_name, BaoBatchWriter, ConsistencyCheckProgress, ExportMode, ExportProgressCb, ImportMode,
23-
ImportProgress, Map, TempCounterMap,
22+
temp_name, BaoBatchWriter, ConsistencyCheckProgress, EntryPathOrData, ExportMode,
23+
ExportProgressCb, ImportMode, ImportProgress, Map, TempCounterMap,
2424
};
2525
use crate::{
2626
store::{
2727
mutable_mem_storage::MutableMemStorage, BaoBlobSize, MapEntry, MapEntryMut, ReadableStore,
2828
},
2929
util::{
3030
progress::{BoxedProgressSender, IdGenerator, IgnoreProgressSender, ProgressSender},
31-
TagCounter, TagDrop,
31+
MemOrFile, TagCounter, TagDrop,
3232
},
3333
BlobFormat, Hash, HashAndFormat, Tag, TempTag, IROH_BLOCK_SIZE,
3434
};
@@ -246,6 +246,22 @@ impl super::Store for Store {
246246
Ok(())
247247
}
248248

249+
async fn entry_path_or_data(&self, hash: Hash) -> io::Result<Option<EntryPathOrData>> {
250+
let state = self.read_lock();
251+
match state.entries.get(&hash) {
252+
Some(entry) => {
253+
let inner = entry.inner.data.read().unwrap();
254+
let data = inner.data.to_vec().into();
255+
let outboard = inner.outboard.to_vec().into();
256+
Ok(Some(EntryPathOrData {
257+
data: MemOrFile::Mem(data),
258+
outboard: MemOrFile::Mem(outboard),
259+
}))
260+
}
261+
None => Ok(None),
262+
}
263+
}
264+
249265
async fn shutdown(&self) {}
250266

251267
async fn sync(&self) -> io::Result<()> {

src/store/readonly_mem.rs

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,18 @@ use futures_lite::Stream;
1818
use iroh_io::AsyncSliceReader;
1919
use tokio::io::AsyncWriteExt;
2020

21-
use super::{BaoBatchWriter, BaoBlobSize, ConsistencyCheckProgress, DbIter, ExportProgressCb};
21+
use super::{
22+
BaoBatchWriter, BaoBlobSize, ConsistencyCheckProgress, DbIter, EntryPathOrData,
23+
ExportProgressCb,
24+
};
2225
use crate::{
2326
store::{
2427
EntryStatus, ExportMode, ImportMode, ImportProgress, Map, MapEntry, MapEntryMut,
2528
ReadableStore,
2629
},
2730
util::{
2831
progress::{BoxedProgressSender, IdGenerator, ProgressSender},
29-
Tag,
32+
MemOrFile, Tag,
3033
},
3134
BlobFormat, Hash, HashAndFormat, TempTag, IROH_BLOCK_SIZE,
3235
};
@@ -321,6 +324,24 @@ impl super::Store for Store {
321324
Err(io::Error::new(io::ErrorKind::Other, "not implemented"))
322325
}
323326

327+
fn entry_path_or_data(
328+
&self,
329+
hash: Hash,
330+
) -> impl Future<Output = io::Result<Option<super::EntryPathOrData>>> + Send {
331+
let res = match self.0.get(&hash) {
332+
Some((outboard, data)) => {
333+
let outboard = outboard.data.clone();
334+
let data = data.clone();
335+
Ok(Some(EntryPathOrData {
336+
outboard: MemOrFile::Mem(outboard),
337+
data: MemOrFile::Mem(data),
338+
}))
339+
}
340+
None => Ok(None),
341+
};
342+
futures_lite::future::ready(res)
343+
}
344+
324345
fn temp_tag(&self, inner: HashAndFormat) -> TempTag {
325346
TempTag::new(inner, None)
326347
}

src/store/traits.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use crate::{
2020
util::{
2121
local_pool::{self, LocalPool},
2222
progress::{BoxedProgressSender, IdGenerator, ProgressSender},
23-
Tag,
23+
MemOrFile, Tag,
2424
},
2525
BlobFormat, Hash, HashAndFormat, TempTag, IROH_BLOCK_SIZE,
2626
};
@@ -42,6 +42,15 @@ pub enum EntryStatus {
4242
NotFound,
4343
}
4444

45+
/// Get the path or data for an entry
46+
#[derive(Debug)]
47+
pub struct EntryPathOrData {
48+
/// The path to the data file or the inline data
49+
pub data: MemOrFile<Bytes, (PathBuf, u64)>,
50+
/// The path to the outboard file, or the inline outboard
51+
pub outboard: MemOrFile<Bytes, PathBuf>,
52+
}
53+
4554
/// The size of a bao file
4655
#[derive(Debug, Clone, Copy, Serialize, Deserialize, Eq, PartialEq)]
4756
pub enum BaoBlobSize {
@@ -384,6 +393,12 @@ pub trait Store: ReadableStore + MapMut + std::fmt::Debug {
384393
) -> impl Future<Output = io::Result<()>> + Send {
385394
validate_impl(self, repair, tx)
386395
}
396+
397+
/// Get the info needed to open an entry independently of the store.
398+
fn entry_path_or_data(
399+
&self,
400+
hash: Hash,
401+
) -> impl Future<Output = io::Result<Option<EntryPathOrData>>> + Send;
387402
}
388403

389404
async fn validate_impl(

0 commit comments

Comments
 (0)