Add error handling (#5)

* Add error handling

* Update to 0.3.0
This commit is contained in:
Terence Boldt 2022-03-04 18:08:33 -05:00 committed by GitHub
parent 30a221c177
commit 62bb781fca
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 165 additions and 78 deletions

25
main.go
View File

@ -17,7 +17,7 @@ import (
"github.com/tjboldt/ProDOS-Utilities/prodos" "github.com/tjboldt/ProDOS-Utilities/prodos"
) )
const version = "0.2.0" const version = "0.3.0"
func main() { func main() {
var fileName string var fileName string
@ -57,11 +57,18 @@ func main() {
} }
defer file.Close() defer file.Close()
pathName = strings.ToUpper(pathName) pathName = strings.ToUpper(pathName)
volumeHeader, _, fileEntries := prodos.ReadDirectory(file, pathName) volumeHeader, _, fileEntries, err := prodos.ReadDirectory(file, pathName)
if err != nil {
fmt.Printf("Error: %s", err)
}
if len(pathName) == 0 { if len(pathName) == 0 {
pathName = "/" + volumeHeader.VolumeName pathName = "/" + volumeHeader.VolumeName
} }
volumeBitmap := prodos.ReadVolumeBitmap(file) volumeBitmap, err := prodos.ReadVolumeBitmap(file)
if err != nil {
fmt.Printf("Failed to open drive image %s:\n %s", fileName, err)
os.Exit(1)
}
freeBlocks := prodos.GetFreeBlockCount(volumeBitmap, volumeHeader.TotalBlocks) freeBlocks := prodos.GetFreeBlockCount(volumeBitmap, volumeHeader.TotalBlocks)
prodos.DumpDirectory(freeBlocks, volumeHeader.TotalBlocks, pathName, fileEntries) prodos.DumpDirectory(freeBlocks, volumeHeader.TotalBlocks, pathName, fileEntries)
case "get": case "get":
@ -109,7 +116,7 @@ func main() {
fmt.Printf("Failed to open input file %s: %s", inFileName, err) fmt.Printf("Failed to open input file %s: %s", inFileName, err)
os.Exit(1) os.Exit(1)
} }
err = prodos.WriteFile(file, file, pathName, fileType, auxType, inFile) err = prodos.WriteFile(file, pathName, fileType, auxType, inFile)
if err != nil { if err != nil {
fmt.Printf("Failed to write file %s: %s", pathName, err) fmt.Printf("Failed to write file %s: %s", pathName, err)
} }
@ -121,7 +128,11 @@ func main() {
os.Exit(1) os.Exit(1)
} }
defer file.Close() defer file.Close()
block := prodos.ReadBlock(file, blockNumber) block, err := prodos.ReadBlock(file, blockNumber)
if err != nil {
fmt.Printf("Failed to open drive image %s:\n %s", fileName, err)
os.Exit(1)
}
prodos.DumpBlock(block) prodos.DumpBlock(block)
case "writeblock": case "writeblock":
fmt.Printf("Writing block 0x%04X (%d):\n\n", blockNumber, blockNumber) fmt.Printf("Writing block 0x%04X (%d):\n\n", blockNumber, blockNumber)
@ -144,7 +155,7 @@ func main() {
return return
} }
defer file.Close() defer file.Close()
prodos.CreateVolume(file, file, volumeName, volumeSize) prodos.CreateVolume(file, volumeName, volumeSize)
case "rm": case "rm":
file, err := os.OpenFile(fileName, os.O_RDWR, 0755) file, err := os.OpenFile(fileName, os.O_RDWR, 0755)
if err != nil { if err != nil {
@ -152,7 +163,7 @@ func main() {
os.Exit(1) os.Exit(1)
} }
defer file.Close() defer file.Close()
prodos.DeleteFile(file, file, pathName) prodos.DeleteFile(file, pathName)
default: default:
fmt.Printf("Invalid command: %s\n\n", command) fmt.Printf("Invalid command: %s\n\n", command)
flag.PrintDefaults() flag.PrintDefaults()

View File

@ -12,8 +12,11 @@ import (
) )
// ReadVolumeBitmap reads the volume bitmap from a ProDOS image // ReadVolumeBitmap reads the volume bitmap from a ProDOS image
func ReadVolumeBitmap(reader io.ReaderAt) []byte { func ReadVolumeBitmap(reader io.ReaderAt) ([]byte, error) {
headerBlock := ReadBlock(reader, 2) headerBlock, err := ReadBlock(reader, 2)
if err != nil {
return nil, err
}
volumeHeader := parseVolumeHeader(headerBlock) volumeHeader := parseVolumeHeader(headerBlock)
@ -31,14 +34,17 @@ func ReadVolumeBitmap(reader io.ReaderAt) []byte {
} }
for i := 0; i < totalBitmapBlocks; i++ { for i := 0; i < totalBitmapBlocks; i++ {
bitmapBlock := ReadBlock(reader, i+volumeHeader.BitmapStartBlock) bitmapBlock, err := ReadBlock(reader, i+volumeHeader.BitmapStartBlock)
if err != nil {
return nil, err
}
for j := 0; j < 512 && i*512+j < totalBitmapBytes; j++ { for j := 0; j < 512 && i*512+j < totalBitmapBytes; j++ {
bitmap[i*512+j] = bitmapBlock[j] bitmap[i*512+j] = bitmapBlock[j]
} }
} }
return bitmap return bitmap, nil
} }
// GetFreeBlockCount gets the number of free blocks on a ProDOS image // GetFreeBlockCount gets the number of free blocks on a ProDOS image
@ -53,14 +59,17 @@ func GetFreeBlockCount(volumeBitmap []byte, totalBlocks int) int {
return freeBlockCount return freeBlockCount
} }
func writeVolumeBitmap(writer io.WriterAt, reader io.ReaderAt, bitmap []byte) { func writeVolumeBitmap(readerWriter ReaderWriterAt, bitmap []byte) error {
headerBlock := ReadBlock(reader, 2) headerBlock, err := ReadBlock(readerWriter, 2)
if err != nil {
return err
}
volumeHeader := parseVolumeHeader(headerBlock) volumeHeader := parseVolumeHeader(headerBlock)
for i := 0; i < len(bitmap)/512; i++ { for i := 0; i < len(bitmap)/512; i++ {
WriteBlock(writer, volumeHeader.BitmapStartBlock+i, bitmap[i*512:i*512+512]) WriteBlock(readerWriter, volumeHeader.BitmapStartBlock+i, bitmap[i*512:i*512+512])
} }
return nil
} }
func createVolumeBitmap(numberOfBlocks int) []byte { func createVolumeBitmap(numberOfBlocks int) []byte {

View File

@ -12,15 +12,16 @@ import (
) )
// ReadBlock reads a block from a ProDOS volume into a byte array // ReadBlock reads a block from a ProDOS volume into a byte array
func ReadBlock(reader io.ReaderAt, block int) []byte { func ReadBlock(reader io.ReaderAt, block int) ([]byte, error) {
buffer := make([]byte, 512) buffer := make([]byte, 512)
reader.ReadAt(buffer, int64(block)*512) _, err := reader.ReadAt(buffer, int64(block)*512)
return buffer return buffer, err
} }
// WriteBlock writes a block to a ProDOS volume from a byte array // WriteBlock writes a block to a ProDOS volume from a byte array
func WriteBlock(writer io.WriterAt, block int, buffer []byte) { func WriteBlock(writer io.WriterAt, block int, buffer []byte) error {
writer.WriteAt(buffer, int64(block)*512) _, err := writer.WriteAt(buffer, int64(block)*512)
return err
} }

View File

@ -74,8 +74,11 @@ type FileEntry struct {
// ReadDirectory reads the directory information from a specified path // ReadDirectory reads the directory information from a specified path
// on a ProDOS image // on a ProDOS image
func ReadDirectory(reader io.ReaderAt, path string) (VolumeHeader, DirectoryHeader, []FileEntry) { func ReadDirectory(reader io.ReaderAt, path string) (VolumeHeader, DirectoryHeader, []FileEntry, error) {
buffer := ReadBlock(reader, 2) buffer, err := ReadBlock(reader, 2)
if err != nil {
return VolumeHeader{}, DirectoryHeader{}, nil, err
}
volumeHeader := parseVolumeHeader(buffer) volumeHeader := parseVolumeHeader(buffer)
@ -86,17 +89,25 @@ func ReadDirectory(reader io.ReaderAt, path string) (VolumeHeader, DirectoryHead
path = strings.ToUpper(path) path = strings.ToUpper(path)
paths := strings.Split(path, "/") paths := strings.Split(path, "/")
directoryHeader, fileEntries := getFileEntriesInDirectory(reader, 2, 1, paths) directoryHeader, fileEntries, err := getFileEntriesInDirectory(reader, 2, 1, paths)
if err != nil {
return VolumeHeader{}, DirectoryHeader{}, nil, err
}
return volumeHeader, directoryHeader, fileEntries return volumeHeader, directoryHeader, fileEntries, nil
} }
func getFreeFileEntryInDirectory(reader io.ReaderAt, directory string) (FileEntry, error) { func getFreeFileEntryInDirectory(reader io.ReaderAt, directory string) (FileEntry, error) {
_, directoryHeader, _ := ReadDirectory(reader, directory) _, directoryHeader, _, err := ReadDirectory(reader, directory)
if err != nil {
return FileEntry{}, err
}
//DumpDirectoryHeader(directoryHeader) //DumpDirectoryHeader(directoryHeader)
blockNumber := directoryHeader.StartingBlock blockNumber := directoryHeader.StartingBlock
buffer := ReadBlock(reader, blockNumber) buffer, err := ReadBlock(reader, blockNumber)
if err != nil {
return FileEntry{}, err
}
entryOffset := 43 // start at offset after header entryOffset := 43 // start at offset after header
entryNumber := 2 // header is essentially the first entry so start at 2 entryNumber := 2 // header is essentially the first entry so start at 2
@ -109,7 +120,10 @@ func getFreeFileEntryInDirectory(reader io.ReaderAt, directory string) (FileEntr
return FileEntry{}, errors.New("No free file entries found") return FileEntry{}, errors.New("No free file entries found")
} }
// else read the next block in the directory // else read the next block in the directory
buffer = ReadBlock(reader, blockNumber) buffer, err = ReadBlock(reader, blockNumber)
if err != nil {
return FileEntry{}, nil
}
entryOffset = 4 entryOffset = 4
entryNumber = 1 entryNumber = 1
} }
@ -127,8 +141,11 @@ func getFreeFileEntryInDirectory(reader io.ReaderAt, directory string) (FileEntr
} }
} }
func getFileEntriesInDirectory(reader io.ReaderAt, blockNumber int, currentPath int, paths []string) (DirectoryHeader, []FileEntry) { func getFileEntriesInDirectory(reader io.ReaderAt, blockNumber int, currentPath int, paths []string) (DirectoryHeader, []FileEntry, error) {
buffer := ReadBlock(reader, blockNumber) buffer, err := ReadBlock(reader, blockNumber)
if err != nil {
return DirectoryHeader{}, nil, err
}
directoryHeader := parseDirectoryHeader(buffer, blockNumber) directoryHeader := parseDirectoryHeader(buffer, blockNumber)
@ -143,7 +160,7 @@ func getFileEntriesInDirectory(reader io.ReaderAt, blockNumber int, currentPath
if !matchedDirectory && (currentPath == len(paths)-1) { if !matchedDirectory && (currentPath == len(paths)-1) {
// path not matched by last path part // path not matched by last path part
return DirectoryHeader{}, nil return DirectoryHeader{}, nil, errors.New("path not matched")
} }
for { for {
@ -151,16 +168,19 @@ func getFileEntriesInDirectory(reader io.ReaderAt, blockNumber int, currentPath
entryOffset = 4 entryOffset = 4
entryNumber = 1 entryNumber = 1
if blockNumber == 0 { if blockNumber == 0 {
return DirectoryHeader{}, nil return DirectoryHeader{}, nil, nil
}
buffer, err = ReadBlock(reader, nextBlock)
if err != nil {
return DirectoryHeader{}, nil, err
} }
buffer = ReadBlock(reader, nextBlock)
nextBlock = int(buffer[2]) + int(buffer[3])*256 nextBlock = int(buffer[2]) + int(buffer[3])*256
} }
fileEntry := parseFileEntry(buffer[entryOffset:entryOffset+40], blockNumber, entryOffset) fileEntry := parseFileEntry(buffer[entryOffset:entryOffset+40], blockNumber, entryOffset)
if fileEntry.StorageType != StorageDeleted { if fileEntry.StorageType != StorageDeleted {
if matchedDirectory && activeEntries == directoryHeader.ActiveFileCount { if matchedDirectory && activeEntries == directoryHeader.ActiveFileCount {
return directoryHeader, fileEntries[0:activeEntries] return directoryHeader, fileEntries[0:activeEntries], nil
} }
if matchedDirectory { if matchedDirectory {
fileEntries[activeEntries] = fileEntry fileEntries[activeEntries] = fileEntry
@ -298,8 +318,11 @@ func parseDirectoryHeader(buffer []byte, blockNumber int) DirectoryHeader {
return directoryEntry return directoryEntry
} }
func writeDirectoryHeader(writer io.WriterAt, reader io.ReaderAt, directoryHeader DirectoryHeader) { func writeDirectoryHeader(readerWriter ReaderWriterAt, directoryHeader DirectoryHeader) error {
buffer := ReadBlock(reader, directoryHeader.StartingBlock) buffer, err := ReadBlock(readerWriter, directoryHeader.StartingBlock)
if err != nil {
return err
}
buffer[0x00] = byte(directoryHeader.PreviousBlock & 0x00FF) buffer[0x00] = byte(directoryHeader.PreviousBlock & 0x00FF)
buffer[0x01] = byte(directoryHeader.PreviousBlock >> 8) buffer[0x01] = byte(directoryHeader.PreviousBlock >> 8)
buffer[0x02] = byte(directoryHeader.NextBlock & 0x00FF) buffer[0x02] = byte(directoryHeader.NextBlock & 0x00FF)
@ -310,5 +333,7 @@ func writeDirectoryHeader(writer io.WriterAt, reader io.ReaderAt, directoryHeade
} }
buffer[0x25] = byte(directoryHeader.ActiveFileCount & 0x00FF) buffer[0x25] = byte(directoryHeader.ActiveFileCount & 0x00FF)
buffer[0x26] = byte(directoryHeader.ActiveFileCount >> 8) buffer[0x26] = byte(directoryHeader.ActiveFileCount >> 8)
WriteBlock(writer, directoryHeader.StartingBlock, buffer) WriteBlock(readerWriter, directoryHeader.StartingBlock, buffer)
return nil
} }

View File

@ -29,7 +29,10 @@ func LoadFile(reader io.ReaderAt, path string) ([]byte, error) {
buffer := make([]byte, fileEntry.EndOfFile) buffer := make([]byte, fileEntry.EndOfFile)
for i := 0; i < len(blockList); i++ { for i := 0; i < len(blockList); i++ {
block := ReadBlock(reader, blockList[i]) block, err := ReadBlock(reader, blockList[i])
if err != nil {
return nil, err
}
for j := 0; j < 512 && i*512+j < fileEntry.EndOfFile; j++ { for j := 0; j < 512 && i*512+j < fileEntry.EndOfFile; j++ {
buffer[i*512+j] = block[j] buffer[i*512+j] = block[j]
} }
@ -39,25 +42,28 @@ func LoadFile(reader io.ReaderAt, path string) ([]byte, error) {
} }
// WriteFile writes a file to a ProDOS volume from a byte array // WriteFile writes a file to a ProDOS volume from a byte array
func WriteFile(writer io.WriterAt, reader io.ReaderAt, path string, fileType int, auxType int, buffer []byte) error { func WriteFile(readerWriter ReaderWriterAt, path string, fileType int, auxType int, buffer []byte) error {
directory, fileName := GetDirectoryAndFileNameFromPath(path) directory, fileName := GetDirectoryAndFileNameFromPath(path)
existingFileEntry, _ := getFileEntry(reader, path) existingFileEntry, _ := getFileEntry(readerWriter, path)
if existingFileEntry.StorageType != StorageDeleted { if existingFileEntry.StorageType != StorageDeleted {
DeleteFile(writer, reader, path) DeleteFile(readerWriter, path)
} }
// get list of blocks to write file to // get list of blocks to write file to
blockList := createBlockList(reader, len(buffer)) blockList, err := createBlockList(readerWriter, len(buffer))
if err != nil {
return err
}
// seedling file // seedling file
if len(buffer) <= 0x200 { if len(buffer) <= 0x200 {
WriteBlock(writer, blockList[0], buffer) WriteBlock(readerWriter, blockList[0], buffer)
} }
// sapling file needs index block // sapling file needs index block
if len(buffer) > 0x200 && len(buffer) <= 0x20000 { if len(buffer) > 0x200 && len(buffer) <= 0x20000 {
writeSaplingFile(writer, buffer, blockList) writeSaplingFile(readerWriter, buffer, blockList)
} }
// TODO: add tree file // TODO: add tree file
@ -65,10 +71,10 @@ func WriteFile(writer io.WriterAt, reader io.ReaderAt, path string, fileType int
return errors.New("files > 128KB not supported yet") return errors.New("files > 128KB not supported yet")
} }
updateVolumeBitmap(writer, reader, blockList) updateVolumeBitmap(readerWriter, blockList)
// add file entry to directory // add file entry to directory
fileEntry, err := getFreeFileEntryInDirectory(reader, directory) fileEntry, err := getFreeFileEntryInDirectory(readerWriter, directory)
if err != nil { if err != nil {
return err return err
} }
@ -89,20 +95,23 @@ func WriteFile(writer io.WriterAt, reader io.ReaderAt, path string, fileType int
fileEntry.StorageType = StorageTree fileEntry.StorageType = StorageTree
} }
writeFileEntry(writer, fileEntry) writeFileEntry(readerWriter, fileEntry)
// increment file count // increment file count
directoryHeaderBlock := ReadBlock(reader, fileEntry.HeaderPointer) directoryHeaderBlock, err := ReadBlock(readerWriter, fileEntry.HeaderPointer)
if err != nil {
return err
}
directoryHeader := parseDirectoryHeader(directoryHeaderBlock, fileEntry.HeaderPointer) directoryHeader := parseDirectoryHeader(directoryHeaderBlock, fileEntry.HeaderPointer)
directoryHeader.ActiveFileCount++ directoryHeader.ActiveFileCount++
writeDirectoryHeader(writer, reader, directoryHeader) writeDirectoryHeader(readerWriter, directoryHeader)
return nil return nil
} }
// DeleteFile deletes a file from a ProDOS volume // DeleteFile deletes a file from a ProDOS volume
func DeleteFile(writer io.WriterAt, reader io.ReaderAt, path string) error { func DeleteFile(readerWriter ReaderWriterAt, path string) error {
fileEntry, err := getFileEntry(reader, path) fileEntry, err := getFileEntry(readerWriter, path)
if err != nil { if err != nil {
return errors.New("File not found") return errors.New("File not found")
} }
@ -114,27 +123,33 @@ func DeleteFile(writer io.WriterAt, reader io.ReaderAt, path string) error {
} }
// free the blocks // free the blocks
blocks, err := getBlocklist(reader, fileEntry) blocks, err := getBlocklist(readerWriter, fileEntry)
if err != nil {
return err
}
volumeBitmap, err := ReadVolumeBitmap(readerWriter)
if err != nil { if err != nil {
return err return err
} }
volumeBitmap := ReadVolumeBitmap(reader)
for i := 0; i < len(blocks); i++ { for i := 0; i < len(blocks); i++ {
freeBlockInVolumeBitmap(volumeBitmap, blocks[i]) freeBlockInVolumeBitmap(volumeBitmap, blocks[i])
} }
writeVolumeBitmap(writer, reader, volumeBitmap) writeVolumeBitmap(readerWriter, volumeBitmap)
// decrement the directory entry count // decrement the directory entry count
directoryBlock := ReadBlock(reader, fileEntry.HeaderPointer) directoryBlock, err := ReadBlock(readerWriter, fileEntry.HeaderPointer)
if err != nil {
return err
}
directoryHeader := parseDirectoryHeader(directoryBlock, fileEntry.HeaderPointer) directoryHeader := parseDirectoryHeader(directoryBlock, fileEntry.HeaderPointer)
directoryHeader.ActiveFileCount-- directoryHeader.ActiveFileCount--
writeDirectoryHeader(writer, reader, directoryHeader) writeDirectoryHeader(readerWriter, directoryHeader)
// zero out directory entry // zero out directory entry
fileEntry.StorageType = 0 fileEntry.StorageType = 0
fileEntry.FileName = "" fileEntry.FileName = ""
writeFileEntry(writer, fileEntry) writeFileEntry(readerWriter, fileEntry)
return nil return nil
} }
@ -157,12 +172,16 @@ func GetDirectoryAndFileNameFromPath(path string) (string, string) {
return directory, fileName return directory, fileName
} }
func updateVolumeBitmap(writer io.WriterAt, reader io.ReaderAt, blockList []int) { func updateVolumeBitmap(readerWriter ReaderWriterAt, blockList []int) error {
volumeBitmap := ReadVolumeBitmap(reader) volumeBitmap, err := ReadVolumeBitmap(readerWriter)
if err != nil {
return err
}
for i := 0; i < len(blockList); i++ { for i := 0; i < len(blockList); i++ {
markBlockInVolumeBitmap(volumeBitmap, blockList[i]) markBlockInVolumeBitmap(volumeBitmap, blockList[i])
} }
writeVolumeBitmap(writer, reader, volumeBitmap) writeVolumeBitmap(readerWriter, volumeBitmap)
return nil
} }
func writeSaplingFile(writer io.WriterAt, buffer []byte, blockList []int) { func writeSaplingFile(writer io.WriterAt, buffer []byte, blockList []int) {
@ -206,17 +225,26 @@ func getBlocklist(reader io.ReaderAt, fileEntry FileEntry) ([]int, error) {
blocks[0] = fileEntry.KeyPointer blocks[0] = fileEntry.KeyPointer
return blocks, nil return blocks, nil
case StorageSapling: case StorageSapling:
index := ReadBlock(reader, fileEntry.KeyPointer) index, err := ReadBlock(reader, fileEntry.KeyPointer)
if err != nil {
return nil, err
}
blocks[0] = fileEntry.KeyPointer blocks[0] = fileEntry.KeyPointer
for i := 0; i < fileEntry.BlocksUsed-1; i++ { for i := 0; i < fileEntry.BlocksUsed-1; i++ {
blocks[i+1] = int(index[i]) + int(index[i+256])*256 blocks[i+1] = int(index[i]) + int(index[i+256])*256
} }
return blocks, nil return blocks, nil
case StorageTree: case StorageTree:
masterIndex := ReadBlock(reader, fileEntry.KeyPointer) masterIndex, err := ReadBlock(reader, fileEntry.KeyPointer)
if err != nil {
return nil, err
}
blocks[0] = fileEntry.KeyPointer blocks[0] = fileEntry.KeyPointer
for i := 0; i < 128; i++ { for i := 0; i < 128; i++ {
index := ReadBlock(reader, int(masterIndex[i])+int(masterIndex[i+256])*256) index, err := ReadBlock(reader, int(masterIndex[i])+int(masterIndex[i+256])*256)
if err != nil {
return nil, err
}
for j := 0; j < 256 && i*256+j < fileEntry.BlocksUsed; j++ { for j := 0; j < 256 && i*256+j < fileEntry.BlocksUsed; j++ {
if (int(index[j]) + int(index[j+256])*256) == 0 { if (int(index[j]) + int(index[j+256])*256) == 0 {
return blocks, nil return blocks, nil
@ -237,7 +265,10 @@ func getDataBlocklist(reader io.ReaderAt, fileEntry FileEntry) ([]int, error) {
return blocks, nil return blocks, nil
case StorageSapling: case StorageSapling:
blocks := make([]int, fileEntry.BlocksUsed-1) blocks := make([]int, fileEntry.BlocksUsed-1)
index := ReadBlock(reader, fileEntry.KeyPointer) index, err := ReadBlock(reader, fileEntry.KeyPointer)
if err != nil {
return nil, err
}
for i := 0; i < fileEntry.BlocksUsed-1; i++ { for i := 0; i < fileEntry.BlocksUsed-1; i++ {
blocks[i] = int(index[i]) + int(index[i+256])*256 blocks[i] = int(index[i]) + int(index[i+256])*256
} }
@ -247,7 +278,7 @@ func getDataBlocklist(reader io.ReaderAt, fileEntry FileEntry) ([]int, error) {
return nil, errors.New("Unsupported file storage type") return nil, errors.New("Unsupported file storage type")
} }
func createBlockList(reader io.ReaderAt, fileSize int) []int { func createBlockList(reader io.ReaderAt, fileSize int) ([]int, error) {
numberOfBlocks := fileSize / 512 numberOfBlocks := fileSize / 512
if fileSize%512 > 0 { if fileSize%512 > 0 {
numberOfBlocks++ numberOfBlocks++
@ -265,15 +296,21 @@ func createBlockList(reader io.ReaderAt, fileSize int) []int {
numberOfBlocks++ numberOfBlocks++
} }
} }
volumeBitmap := ReadVolumeBitmap(reader) volumeBitmap, err := ReadVolumeBitmap(reader)
if err != nil {
return nil, err
}
blockList := findFreeBlocks(volumeBitmap, numberOfBlocks) blockList := findFreeBlocks(volumeBitmap, numberOfBlocks)
return blockList return blockList, nil
} }
func getFileEntry(reader io.ReaderAt, path string) (FileEntry, error) { func getFileEntry(reader io.ReaderAt, path string) (FileEntry, error) {
directory, fileName := GetDirectoryAndFileNameFromPath(path) directory, fileName := GetDirectoryAndFileNameFromPath(path)
_, _, fileEntries := ReadDirectory(reader, directory) _, _, fileEntries, err := ReadDirectory(reader, directory)
if err != nil {
return FileEntry{}, err
}
if fileEntries == nil || len(fileEntries) == 0 { if fileEntries == nil || len(fileEntries) == 0 {
return FileEntry{}, errors.New("File entry not found") return FileEntry{}, errors.New("File entry not found")

View File

@ -8,14 +8,13 @@ package prodos
import ( import (
"fmt" "fmt"
"io"
"strings" "strings"
"time" "time"
) )
// CreateVolume formats a new ProDOS volume including boot block, // CreateVolume formats a new ProDOS volume including boot block,
// volume bitmap and empty directory // volume bitmap and empty directory
func CreateVolume(writer io.WriterAt, reader io.ReaderAt, volumeName string, numberOfBlocks int) { func CreateVolume(readerWriter ReaderWriterAt, volumeName string, numberOfBlocks int) {
if numberOfBlocks > 65535 || numberOfBlocks < 64 { if numberOfBlocks > 65535 || numberOfBlocks < 64 {
return return
} }
@ -28,7 +27,7 @@ func CreateVolume(writer io.WriterAt, reader io.ReaderAt, volumeName string, num
blankBlock := make([]byte, 512) blankBlock := make([]byte, 512)
for i := 0; i < numberOfBlocks; i++ { for i := 0; i < numberOfBlocks; i++ {
WriteBlock(writer, i, blankBlock) WriteBlock(readerWriter, i, blankBlock)
} }
volumeHeader := [43]byte{} volumeHeader := [43]byte{}
@ -63,10 +62,10 @@ func CreateVolume(writer io.WriterAt, reader io.ReaderAt, volumeName string, num
volumeHeader[0x29] = byte(numberOfBlocks & 0xFF) volumeHeader[0x29] = byte(numberOfBlocks & 0xFF)
volumeHeader[0x2A] = byte(numberOfBlocks >> 8) volumeHeader[0x2A] = byte(numberOfBlocks >> 8)
writer.WriteAt(volumeHeader[:], 1024) readerWriter.WriteAt(volumeHeader[:], 1024)
// boot block 0 // boot block 0
WriteBlock(writer, 0, getBootBlock()) WriteBlock(readerWriter, 0, getBootBlock())
// pointers to volume directory blocks // pointers to volume directory blocks
for i := 2; i < 6; i++ { for i := 2; i < 6; i++ {
@ -83,12 +82,12 @@ func CreateVolume(writer io.WriterAt, reader io.ReaderAt, volumeName string, num
pointers[2] = byte(i + 1) pointers[2] = byte(i + 1)
} }
pointers[3] = 0x00 pointers[3] = 0x00
writer.WriteAt(pointers, int64(i*512)) readerWriter.WriteAt(pointers, int64(i*512))
} }
// volume bit map starting at block 6 // volume bit map starting at block 6
volumeBitmap := createVolumeBitmap(numberOfBlocks) volumeBitmap := createVolumeBitmap(numberOfBlocks)
writeVolumeBitmap(writer, reader, volumeBitmap) writeVolumeBitmap(readerWriter, volumeBitmap)
} }
func getBootBlock() []byte { func getBootBlock() []byte {

View File

@ -21,9 +21,9 @@ func TestCreateVolume(t *testing.T) {
t.Run(testname, func(t *testing.T) { t.Run(testname, func(t *testing.T) {
file := NewMemoryFile(0x2000000) file := NewMemoryFile(0x2000000)
CreateVolume(file, file, tt.wantVolumeName, tt.blocks) CreateVolume(file, tt.wantVolumeName, tt.blocks)
volumeHeader, _, fileEntries := ReadDirectory(file, "") volumeHeader, _, fileEntries, _ := ReadDirectory(file, "")
if volumeHeader.VolumeName != tt.wantVolumeName { if volumeHeader.VolumeName != tt.wantVolumeName {
t.Errorf("got volume name %s, want %s", volumeHeader.VolumeName, tt.wantVolumeName) t.Errorf("got volume name %s, want %s", volumeHeader.VolumeName, tt.wantVolumeName)
} }
@ -34,7 +34,7 @@ func TestCreateVolume(t *testing.T) {
t.Errorf("got files %d, want 0", len(fileEntries)) t.Errorf("got files %d, want 0", len(fileEntries))
} }
volumeBitmap := ReadVolumeBitmap(file) volumeBitmap, _ := ReadVolumeBitmap(file)
freeBlockCount := GetFreeBlockCount(volumeBitmap, tt.blocks) freeBlockCount := GetFreeBlockCount(volumeBitmap, tt.blocks)
if freeBlockCount != tt.wantFreeBlocks { if freeBlockCount != tt.wantFreeBlocks {
t.Errorf("got free blocks: %d, want %d", freeBlockCount, tt.wantFreeBlocks) t.Errorf("got free blocks: %d, want %d", freeBlockCount, tt.wantFreeBlocks)

View File

@ -12,6 +12,11 @@ type MemoryFile struct {
size int size int
} }
type ReaderWriterAt interface {
ReadAt(data []byte, offset int64) (int, error)
WriteAt(data []byte, offset int64) (int, error)
}
// NewMemoryFile creates an in-memory file of the specified size in bytes // NewMemoryFile creates an in-memory file of the specified size in bytes
func NewMemoryFile(size int) *MemoryFile { func NewMemoryFile(size int) *MemoryFile {
return &MemoryFile{make([]byte, size), size} return &MemoryFile{make([]byte, size), size}