-
Notifications
You must be signed in to change notification settings - Fork 117
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: Avi Deitcher <[email protected]>
- Loading branch information
Showing
26 changed files
with
4,822 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
package ext4 | ||
|
||
import "fmt" | ||
|
||
// bitmap is a structure holding a bitmap | ||
type bitmap struct { | ||
bits []byte | ||
} | ||
|
||
// bitmapFromBytes create a bitmap struct from bytes | ||
func bitmapFromBytes(b []byte) *bitmap { | ||
// just copy them over | ||
bits := make([]byte, len(b)) | ||
copy(bits, b) | ||
bm := bitmap{ | ||
bits: bits, | ||
} | ||
|
||
return &bm | ||
} | ||
|
||
// toBytes returns raw bytes ready to be written to disk | ||
func (bm *bitmap) toBytes() []byte { | ||
b := make([]byte, len(bm.bits)) | ||
copy(b, bm.bits) | ||
|
||
return b | ||
} | ||
|
||
func (bm *bitmap) checkFree(location int) (bool, error) { | ||
byteNumber, bitNumber := findBitForIndex(location) | ||
if byteNumber > len(bm.bits) { | ||
return false, fmt.Errorf("location %d is not in %d size bitmap", location, len(bm.bits)*8) | ||
} | ||
mask := byte(0x1) << bitNumber | ||
return bm.bits[byteNumber]&mask == mask, nil | ||
} | ||
|
||
func (bm *bitmap) free(location int) error { | ||
byteNumber, bitNumber := findBitForIndex(location) | ||
if byteNumber > len(bm.bits) { | ||
return fmt.Errorf("location %d is not in %d size bitmap", location, len(bm.bits)*8) | ||
} | ||
mask := byte(0x1) << bitNumber | ||
mask = ^mask | ||
bm.bits[byteNumber] &= mask | ||
return nil | ||
} | ||
|
||
func (bm *bitmap) use(location int) error { | ||
byteNumber, bitNumber := findBitForIndex(location) | ||
if byteNumber > len(bm.bits) { | ||
return fmt.Errorf("location %d is not in %d size bitmap", location, len(bm.bits)*8) | ||
} | ||
mask := byte(0x1) << bitNumber | ||
bm.bits[byteNumber] |= mask | ||
return nil | ||
} | ||
|
||
func (bm *bitmap) findFirstFree() int { | ||
var location = -1 | ||
for i, b := range bm.bits { | ||
// if all used, continue to next | ||
if b&0xff == 0xff { | ||
continue | ||
} | ||
// not all used, so find first bit set to 0 | ||
for j := uint8(0); j < 8; j++ { | ||
mask := byte(0x1) << j | ||
if b&mask != mask { | ||
location = 8*i + (8 - int(j)) | ||
break | ||
} | ||
} | ||
break | ||
} | ||
return location | ||
} | ||
|
||
//nolint:revive // params are unused as of yet, but will be used in the future | ||
func (bm *bitmap) findFirstUsed() int { | ||
var location int = -1 | ||
for i, b := range bm.bits { | ||
// if all free, continue to next | ||
if b == 0x00 { | ||
continue | ||
} | ||
// not all free, so find first bit set to 1 | ||
for j := uint8(0); j < 8; j++ { | ||
mask := byte(0x1) << j | ||
mask = ^mask | ||
if b|mask != mask { | ||
location = 8*i + (8 - int(j)) | ||
break | ||
} | ||
} | ||
break | ||
} | ||
return location | ||
} | ||
|
||
func findBitForIndex(index int) (byteNumber int, bitNumber uint8) { | ||
return index / 8, uint8(index % 8) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
package ext4 | ||
|
||
import ( | ||
"fmt" | ||
) | ||
|
||
// blockGroup is a structure holding the data about a single block group | ||
// | ||
//nolint:unused // will be used in the future, not yet | ||
type blockGroup struct { | ||
inodeBitmap *bitmap | ||
blockBitmap *bitmap | ||
blockSize int | ||
number int | ||
inodeTableSize int | ||
firstDataBlock int | ||
} | ||
|
||
// blockGroupFromBytes create a blockGroup struct from bytes | ||
// it does not load the inode table or data blocks into memory, rather holding pointers to where they are | ||
// | ||
//nolint:unused // will be used in the future, not yet | ||
func blockGroupFromBytes(b []byte, blockSize, groupNumber int) (*blockGroup, error) { | ||
expectedSize := 2 * blockSize | ||
actualSize := len(b) | ||
if actualSize != expectedSize { | ||
return nil, fmt.Errorf("expected to be passed %d bytes for 2 blocks of size %d, instead received %d", expectedSize, blockSize, actualSize) | ||
} | ||
inodeBitmap := bitmapFromBytes(b[0:blockSize]) | ||
blockBitmap := bitmapFromBytes(b[blockSize : 2*blockSize]) | ||
|
||
bg := blockGroup{ | ||
inodeBitmap: inodeBitmap, | ||
blockBitmap: blockBitmap, | ||
number: groupNumber, | ||
blockSize: blockSize, | ||
} | ||
return &bg, nil | ||
} | ||
|
||
// toBytes returns bitmaps ready to be written to disk | ||
// | ||
//nolint:unused // will be used in the future, not yet | ||
func (bg *blockGroup) toBytes() ([]byte, error) { | ||
b := make([]byte, 2*bg.blockSize) | ||
inodeBitmapBytes := bg.inodeBitmap.toBytes() | ||
blockBitmapBytes := bg.blockBitmap.toBytes() | ||
|
||
b = append(b, inodeBitmapBytes...) | ||
b = append(b, blockBitmapBytes...) | ||
|
||
return b, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
package ext4 | ||
|
||
// Directory represents a single directory in an ext4 filesystem | ||
type Directory struct { | ||
directoryEntry | ||
root bool | ||
entries []*directoryEntry | ||
} | ||
|
||
// dirEntriesFromBytes loads the directory entries from the raw bytes | ||
func (d *Directory) entriesFromBytes(b []byte) error { | ||
entries, err := parseDirEntries(b) | ||
if err != nil { | ||
return err | ||
} | ||
d.entries = entries | ||
return nil | ||
} | ||
|
||
// toBytes convert our entries to raw bytes | ||
func (d *Directory) toBytes(bytesPerBlock int) []byte { | ||
b := make([]byte, 0) | ||
for _, de := range d.entries { | ||
b2 := de.toBytes() | ||
b = append(b, b2...) | ||
} | ||
remainder := len(b) % bytesPerBlock | ||
extra := bytesPerBlock - remainder | ||
zeroes := make([]byte, extra) | ||
b = append(b, zeroes...) | ||
return b | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
package ext4 | ||
|
||
import ( | ||
"encoding/binary" | ||
"fmt" | ||
) | ||
|
||
const ( | ||
minDirEntryLength int = 12 // actually 9 for 1-byte file length, but must be multiple of 4 bytes | ||
maxDirEntryLength int = 263 | ||
) | ||
|
||
// directoryEntry is a single directory entry | ||
type directoryEntry struct { | ||
inode uint32 | ||
filename string | ||
fileType fileType | ||
} | ||
|
||
func directoryEntryFromBytes(b []byte) (*directoryEntry, error) { | ||
if len(b) < minDirEntryLength { | ||
return nil, fmt.Errorf("directory entry of length %d is less than minimum %d", len(b), minDirEntryLength) | ||
} | ||
if len(b) > maxDirEntryLength { | ||
return nil, fmt.Errorf("directory entry of length %d is greater than maximum %d", len(b), maxDirEntryLength) | ||
} | ||
|
||
//nolint:gocritic // keep this here for future reference | ||
// length := binary.LittleEndian.Uint16(b[0x4:0x6]) | ||
nameLength := b[0x6] | ||
name := b[0x8 : 0x8+nameLength] | ||
de := directoryEntry{ | ||
inode: binary.LittleEndian.Uint32(b[0x0:0x4]), | ||
fileType: fileType(b[0x7]), | ||
filename: string(name), | ||
} | ||
return &de, nil | ||
} | ||
|
||
func (de *directoryEntry) toBytes() []byte { | ||
// it must be the header length + filename length rounded up to nearest multiple of 4 | ||
nameLength := uint8(len(de.filename)) | ||
entryLength := uint16(nameLength) + 8 | ||
if leftover := entryLength % 4; leftover > 0 { | ||
entryLength += leftover | ||
} | ||
b := make([]byte, 0, entryLength) | ||
|
||
binary.LittleEndian.PutUint32(b[0x0:0x4], de.inode) | ||
binary.LittleEndian.PutUint16(b[0x4:0x6], entryLength) | ||
b[0x6] = nameLength | ||
b[0x7] = byte(de.fileType) | ||
copy(b[0x8:], de.filename) | ||
|
||
return b | ||
} | ||
|
||
// parse the data blocks to get the directory entries | ||
func parseDirEntries(b []byte) ([]*directoryEntry, error) { | ||
entries := make([]*directoryEntry, 4) | ||
count := 0 | ||
for i := 0; i < len(b); count++ { | ||
// read the length of the first entry | ||
length := binary.LittleEndian.Uint16(b[i+0x4 : i+0x6]) | ||
de, err := directoryEntryFromBytes(b[i : i+int(length)]) | ||
if err != nil { | ||
return nil, fmt.Errorf("failed to parse directory entry %d: %v", count, err) | ||
} | ||
entries = append(entries, de) | ||
i += int(length) | ||
} | ||
return entries, nil | ||
} |
Oops, something went wrong.