Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add fdisk utility #18

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
all: install test

TARGETS = echo cat
TARGETS = echo cat fdisk

test:
go test -v ./...

install:
@for cmd in $(TARGETS); do \
rm $(GOPATH)/bin/$$cmd; \
rm -f $(GOPATH)/bin/$$cmd; \
done; \
go install ./cmd/...
@for cmd in $(TARGETS); do \
Expand Down
95 changes: 95 additions & 0 deletions cmd/fdisk/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package main

import (
"flag"
"fmt"
"io"
"os"
)

const (
// Classical MBR structure
CPart1 = 0x1be // 16 bytes each
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

offset of first partition entry info.

CPart2 = 0x1ce
CPart3 = 0x1de
CPart4 = 0x1ee

PEntrySZ = 16

Magic1Off = 0x1fe
Magic2Off = 0x1ff

// MBR magic numbers
Magic1 = 0x55
Magic2 = 0xaa
)

func diskinfo(fname string) error {
file, err := os.Open(fname)

if err != nil {
return err
}

var mbr [512]byte
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MBR resides in the first 512 bytes of disk. For compatibility, GPT has a MBR-compatible structure in the first sector too.


_, err = file.Read(mbr[:])

if err != nil && err != io.EOF {
return err
}

if mbr[Magic1Off] != Magic1 &&
mbr[Magic2Off] != Magic2 {
return fmt.Errorf("no MBR found\n")
}

var part *partition
var empty bool

if part, err, empty = NewPartition(mbr[CPart1 : CPart1+PEntrySZ]); err != nil {
return err
} else if !empty {
fmt.Printf("%s\n", part)
}

if part, err, empty = NewPartition(mbr[CPart2 : CPart2+PEntrySZ]); err != nil {
return err
} else if !empty {
fmt.Printf("%s\n", part)
}

if part, err, empty = NewPartition(mbr[CPart3 : CPart3+PEntrySZ]); err != nil {
return err
} else if !empty {
fmt.Printf("%s\n", part)
}

if part, err, empty = NewPartition(mbr[CPart4 : CPart4+PEntrySZ]); err != nil {
return err
} else if !empty {
fmt.Printf("%s\n", part)
}

return nil
}

func main() {
flag.Parse()
diskpaths := flag.Args()

if len(diskpaths) == 0 {
fmt.Fprintf(os.Stderr, "Usage: %s dev1 dev2 ...\n", os.Args[0])
os.Exit(1)
}

for _, disk := range diskpaths {
err := diskinfo(disk)

if err != nil {
fmt.Fprintf(os.Stderr, "Failed to get MBR/GPT info: %s\n", err)
os.Exit(1)
}
}

}
110 changes: 110 additions & 0 deletions cmd/fdisk/partition.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
package main

import (
"bytes"
"encoding/binary"
"fmt"
)

type (
// cylinder-head-sector
chs struct {
head, sector uint8
cylinder uint16
}

status byte
typ byte

partition struct {
number uint8
status status
begin chs
typ typ
end chs
lba int32
nsectors int32
}
)

func (chs chs) String() string {
return fmt.Sprintf("%d/%d/%d", chs.cylinder, chs.head, chs.sector)
}

func (t typ) String() string {
switch t {
case 0x83:
return fmt.Sprintf("%x (Linux)", int(t))
}
return "unknown"

}

func (st status) String() string {
err := ""
if st&0x80 != 0 && st&0x80 != 0x80 {
err += " (wrong)"
}

switch st >> 7 {
case 1:
return "active" + err
}

return "inactive" + err
}

func (p partition) String() string {
format := `Partition #%d
Status: %s
FS type: %s
First C/H/S: %s
Last C/H/S: %s
LBA: %d
Number of sectors: %d
`
return fmt.Sprintf(format, p.number, p.status, p.typ, p.begin, p.end, p.lba, p.nsectors)
}

func NewPartition(entry []byte) (*partition, error, bool) {
part := &partition{}

if len(entry) != 16 {
return nil, fmt.Errorf("Invalid partition entry: %v", entry), false
}

if isPartEmpty(entry) {
return nil, nil, true
}

part.status = status(entry[0])
part.begin.head = uint8(entry[1])
part.begin.sector = uint8(entry[2])
part.begin.cylinder = uint16(entry[3])
part.typ = typ(entry[4])
part.end.head = uint8(entry[5])
part.end.sector = uint8(entry[6] & 0x3f) // sector in bits 5-0
part.end.cylinder = uint16(entry[6]&0xc0)<<2 | uint16(entry[7])

buf := bytes.NewBuffer(entry[8:12])
err := binary.Read(buf, binary.LittleEndian, &part.lba)

if err != nil {
return nil, err, false
}

buf = bytes.NewBuffer(entry[12:16])
err = binary.Read(buf, binary.LittleEndian, &part.nsectors)

return part, err, false
}

func isPartEmpty(buf []byte) bool {
for i := 0; i < 16; i++ {
if buf[i] != 0 {
return false
}
}

return true
}