Skip to content

Commit b9aa64d

Browse files
committed
fix: Index partition read/write mismatch causing data loss after tape reload
CRITICAL BUG FIX: - Fixed FileMark location mismatch between read (FM1) and write (FM3) operations - Investigation of LTFSCopyGUI source confirmed both read/write use FileMark 3 - Write: LTFSWriter.vb:2418 - Read: LTFSWriter.vb:4549 - Updated read.rs to use FileMark 3 for index partition (matching LTFSCopyGUI) - Improved sync.rs logging to show recursive total file count - Bumped version to 0.1.2 This bug caused indexes written to FM3 to be unreadable after tape reload, as the read logic was looking at FM1 where old/invalid data existed. Symptoms: 100% null data when reading index after tape reload.
1 parent 3aa3bc0 commit b9aa64d

4 files changed

Lines changed: 34 additions & 17 deletions

File tree

Cargo.lock

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

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "rust-ltfs"
3-
version = "0.1.1"
3+
version = "0.1.2"
44
edition = "2021"
55
authors = ["<oplancelot@gmail.com>"]
66
description = "A Rust CLI tool for IBM tape direct read/write operations"

src/tape_ops/index_io/read.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -450,12 +450,13 @@ impl super::super::TapeOperations {
450450
}
451451

452452
// Step 3: 根据分区类型确定目标FileMark
453-
// 🔧 修复:索引分区(P0)使用固定的FileMark 1(LTFS标准位置)
453+
// 🔧 FIX: 索引分区(P0)使用FileMark 3(与LTFSCopyGUI一致)
454+
// Reference: LTFSWriter.vb line 4549 - TapeUtils.Locate(driveHandle, 3UL, IndexPartition, TapeUtils.LocateDestType.FileMark)
454455
// 数据分区(P1)使用FM-1策略(最新索引在EOD之前)
455456
let target_filemark = if partition == 0 {
456-
// 索引分区:LTFS标准索引位置在FileMark 1之后
457-
info!("Index partition (P0): using standard LTFS location FileMark 1");
458-
1
457+
// 索引分区:使用FileMark 3(LTFSCopyGUI兼容)
458+
info!("Index partition (P0): using FileMark 3 (LTFSCopyGUI compatible)");
459+
3
459460
} else {
460461
// 数据分区:最新索引在最后一个FileMark之前
461462
info!("Data partition (P{}): using FM-1 strategy", partition);

src/tape_ops/index_io/sync.rs

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,15 @@ fn get_current_ltfs_timestamp() -> String {
2121
format_ltfs_timestamp(chrono::Utc::now())
2222
}
2323

24+
/// Helper function to count files recursively in directory tree
25+
fn count_files_recursive(dir: &crate::ltfs_index::Directory) -> usize {
26+
let mut count = dir.contents.files.len();
27+
for subdir in &dir.contents.directories {
28+
count += count_files_recursive(subdir);
29+
}
30+
count
31+
}
32+
2433
impl TapeOperations {
2534
/// Update index on tape with force option (corresponds to VB.NET WriteCurrentIndex + RefreshIndexPartition)
2635
pub async fn update_index_on_tape_with_options_dual_partition(&mut self, force_index: bool) -> Result<()> {
@@ -32,12 +41,14 @@ impl TapeOperations {
3241
// 优先使用 self.index (包含最新的文件状态),回退到 self.schema
3342
let mut current_index = match &self.index {
3443
Some(idx) => {
35-
info!("Using self.index with {} files", idx.root_directory.contents.files.len());
44+
let total_files = count_files_recursive(&idx.root_directory);
45+
info!("Using self.index with {} total files (gen {})", total_files, idx.generationnumber);
3646
idx.clone()
3747
},
3848
None => match &self.schema {
3949
Some(idx) => {
40-
info!("Fallback to self.schema with {} files", idx.root_directory.contents.files.len());
50+
let total_files = count_files_recursive(&idx.root_directory);
51+
info!("Fallback to self.schema with {} total files (gen {})", total_files, idx.generationnumber);
4152
idx.clone()
4253
},
4354
None => {
@@ -204,24 +215,28 @@ impl TapeOperations {
204215
}
205216

206217
/// RefreshIndexPartition: Sync index to index partition (对应LTFSCopyGUI RefreshIndexPartition)
218+
///
219+
/// 🔧 LTFSCopyGUI compatible: Uses FileMark 3 for index partition
220+
/// Reference: LTFSWriter.vb line 2418 - TapeUtils.Locate(driveHandle, 3UL, IndexPartition, TapeUtils.LocateDestType.FileMark)
221+
/// Reference: LTFSWriter.vb line 4549 - same location used for reading
207222
async fn refresh_index_partition(&mut self, current_index: &mut LtfsIndex) -> Result<()> {
208223
info!("=== RefreshIndexPartition: Syncing to Index Partition ===");
209224

210225
let logical_index_partition = 0u8; // IndexPartition = 0 (Partition A)
211226
let index_partition = self.get_target_partition(logical_index_partition);
212227

213-
// 精确对应LTFSCopyGUI:TapeUtils.Locate(driveHandle, 3UL, IndexPartition, TapeUtils.LocateDestType.FileMark)
214-
debug!("Locating to index partition {} at 3rd filemark",
215-
index_partition);
228+
// LTFSCopyGUI uses FileMark 3 for index partition (line 2418 & 4549)
229+
let target_filemark = 3u64;
230+
debug!("Locating to index partition {} at FileMark {} (LTFSCopyGUI compatible)",
231+
index_partition, target_filemark);
216232

217-
// 使用LTFSCopyGUI的精确参数:3UL (第3个文件标记)
218-
self.scsi.locate_to_filemark(3, index_partition)?;
233+
self.scsi.locate_to_filemark(target_filemark, index_partition)?;
219234

220235
let locate_position = self.scsi.read_position()?;
221236
debug!("Located to position: partition={}, block={}",
222237
locate_position.partition, locate_position.block_number);
223238

224-
// Write filemark (对应LTFSCopyGUI WriteFileMark)
239+
// Write filemark (对应LTFSCopyGUI WriteFileMark at line 2421)
225240
debug!("Writing filemark at index partition");
226241
self.scsi.write_filemarks(1)?;
227242

@@ -233,6 +248,7 @@ impl TapeOperations {
233248
});
234249
}
235250

251+
// LTFSCopyGUI: schema.location.startblock = p.BlockNumber + 1 (line 2427)
236252
let write_position = self.scsi.read_position()?;
237253
current_index.location.startblock = write_position.block_number;
238254
current_index.location.partition = "a".to_string(); // Index partition
@@ -245,15 +261,15 @@ impl TapeOperations {
245261

246262
let index_xml = current_index.to_xml()?;
247263

248-
debug!("Writing index to index partition...");
264+
debug!("Writing index to index partition ({} bytes)...", index_xml.len());
249265
self.write_xml_to_tape(&index_xml).await?;
250266

251267
// Write filemark after index
252268
self.scsi.write_filemarks(1)?;
253269

254270
let final_position = self.scsi.read_position()?;
255-
debug!("Index partition write completed at position: partition={}, block={}",
256-
final_position.partition, final_position.block_number);
271+
info!("Index partition write completed: partition={}, block={}, index_size={} bytes",
272+
final_position.partition, final_position.block_number, index_xml.len());
257273

258274
// Write VCI (Volume Coherency Information) - 对应LTFSCopyGUI WriteVCI
259275
debug!("Writing VCI (Volume Coherency Information)");

0 commit comments

Comments
 (0)