diff --git a/cmd/stfs-seek-file/main.go b/cmd/stfs-seek-file/main.go deleted file mode 100644 index ee3613f..0000000 --- a/cmd/stfs-seek-file/main.go +++ /dev/null @@ -1,54 +0,0 @@ -package main - -import ( - "archive/tar" - "flag" - "io" - "log" - "os" -) - -const ( - blockSize = 512 -) - -func main() { - file := flag.String("file", "test.tar", "Tar file to open") - recordSize := flag.Int("recordSize", 20, "Amount of 512-bit blocks per record") - record := flag.Int("record", 0, "Record to seek too") - block := flag.Int("block", 0, "Block in record to seek too") - - flag.Parse() - - bytesToSeek := (*recordSize * blockSize * *record) + *block*blockSize - - f, err := os.Open(*file) - if err != nil { - panic(err) - } - defer f.Close() - - if _, err := f.Seek(int64(bytesToSeek), 0); err != nil { - panic(err) - } - - tr := tar.NewReader(f) - - for { - hdr, err := tr.Next() - if err != nil { - panic(err) - } - - log.Println(hdr) - - curr, err := f.Seek(0, io.SeekCurrent) - if err != nil { - panic(err) - } - - if currentRecord := ((curr + hdr.Size) / blockSize) / int64(*recordSize); currentRecord > int64(*record) { - break - } - } -} diff --git a/cmd/stfs-seek/main.go b/cmd/stfs-seek/main.go new file mode 100644 index 0000000..29eb810 --- /dev/null +++ b/cmd/stfs-seek/main.go @@ -0,0 +1,140 @@ +package main + +import ( + "archive/tar" + "bufio" + "flag" + "io" + "log" + "os" + "syscall" + "unsafe" +) + +// See https://github.com/benmcclelland/mtio +const ( + MTIOCTOP = 0x40086d01 // Do magnetic tape operation + MTSEEK = 22 // Seek to block + MTIOCPOS = 0x80086d03 // Get tape position + + blockSize = 512 +) + +// Operation is struct for MTIOCTOP +type Operation struct { + Op int16 // Operation ID + Pad int16 // Padding to match C structures + Count int32 // Operation count +} + +// Position is struct for MTIOCPOS +type Position struct { + BlkNo int64 // Current block number +} + +func main() { + file := flag.String("file", "/dev/nst0", "File (tape drive or tar file) to open") + recordSize := flag.Int("recordSize", 20, "Amount of 512-bit blocks per record") + record := flag.Int("record", 0, "Record to seek too") + block := flag.Int("block", 0, "Block in record to seek too") + + flag.Parse() + + fileDescription, err := os.Stat(*file) + if err != nil { + panic(err) + } + + var f *os.File + if fileDescription.Mode().IsRegular() { + f, err = os.Open(*file) + if err != nil { + panic(err) + } + + // Seek to record & block + if _, err := f.Seek(int64((*recordSize*blockSize**record)+*block*blockSize), 0); err != nil { + panic(err) + } + } else { + f, err = os.OpenFile(*file, os.O_RDONLY, os.ModeCharDevice) + if err != nil { + panic(err) + } + + // Seek to record (we can't seek to block on tape) + // TODO: Seek to next header here too; currently this only works for the start of each archive/after a filemark + if err := seekToRecordOnTape(f, int32(*record)); err != nil { + panic(err) + } + } + defer f.Close() + + var tr *tar.Reader + if fileDescription.Mode().IsRegular() { + tr = tar.NewReader(f) + } else { + br := bufio.NewReaderSize(f, blockSize**recordSize) + tr = tar.NewReader(br) + } + + for { + hdr, err := tr.Next() + if err != nil { + panic(err) + } + + log.Println(hdr) + + currentRecord := int64(0) + if fileDescription.Mode().IsRegular() { + curr, err := f.Seek(0, io.SeekCurrent) + if err != nil { + panic(err) + } + + currentRecord = ((curr + hdr.Size) / blockSize) / int64(*recordSize) + } else { + currentRecord, err = getCurrentRecordFromTape(f) + if err != nil { + panic(err) + } + } + + if currentRecord > int64(*record) { + break + } + } +} + +func seekToRecordOnTape(f *os.File, record int32) error { + if _, _, err := syscall.Syscall( + syscall.SYS_IOCTL, + f.Fd(), + MTIOCTOP, + uintptr(unsafe.Pointer( + &Operation{ + Op: MTSEEK, + Count: record, + }, + )), + ); err != 0 { + return err + } + + return nil +} + +func getCurrentRecordFromTape(f *os.File) (int64, error) { + pos := &Position{} + if _, _, err := syscall.Syscall( + syscall.SYS_IOCTL, + f.Fd(), + MTIOCPOS, + uintptr(unsafe.Pointer(pos)), + ); err != 0 { + return 0, err + } + + return pos.BlkNo, nil +} diff --git a/cmd/stfs-tvf-simple/main.go b/cmd/stfs-tvf-simple/main.go index 882d1dc..bd833a2 100644 --- a/cmd/stfs-tvf-simple/main.go +++ b/cmd/stfs-tvf-simple/main.go @@ -122,7 +122,6 @@ func main() { tr := tar.NewReader(br) record := int64(0) - for { hdr, err := tr.Next() if err == io.EOF { @@ -147,12 +146,22 @@ func main() { panic(err) } - log.Println("Record:", record, "Block:", 0, "Header:", hdr) + if record == 0 { + log.Println("Record:", 0, "Block:", 0, "Header:", hdr) + } else { + log.Println("Record:", record, "Block:", 0, "Header:", hdr) + } - record, err = getCurrentRecordFromTape(f) + curr, err := getCurrentRecordFromTape(f) if err != nil { panic(err) } + + if record == 0 { + record = ((curr * int64(*recordSize) * blockSize) + hdr.Size) / (int64(*recordSize) * blockSize) // For the first record of the file or archive, the offset of one is not needed + } else { + record = ((curr*int64(*recordSize)*blockSize)+hdr.Size)/(int64(*recordSize)*blockSize) + 2 // +2 because we need to start reading right after the last block + } } } }