1package disk
2
3import (
4 "errors"
5 "fmt"
6)
7
8// FilesystemType represents partition filesystem type
9type FilesystemType int
10
11const (
12 // FilesystemExt4 is the Linux ext4 filesystem
13 FilesystemExt4 FilesystemType = iota
14
15 // FilesystemFAT32 is the FAT32 filesystem (typically for EFI)
16 FilesystemFAT32
17
18 // FilesystemBtrfs is the Btrfs filesystem
19 FilesystemBtrfs
20)
21
22// String returns human-readable filesystem type name
23func (f FilesystemType) String() string {
24 switch f {
25 case FilesystemFAT32:
26 return "FAT32"
27 case FilesystemBtrfs:
28 return "Btrfs"
29 default:
30 return "ext4"
31 }
32}
33
34// Partition is an immutable value object representing a disk partition
35type Partition struct {
36 device string // e.g., /dev/sda1
37 size int64 // Size in megabytes
38 filesystem FilesystemType
39 mountPoint string // e.g., /, /boot, /home
40 encrypted bool
41}
42
43// NewPartition creates a new Partition value object with validation
44func NewPartition(device string, sizeMB int64, fs FilesystemType, mountPoint string, encrypted bool) (*Partition, error) {
45 // Validate device
46 if device == "" {
47 return nil, errors.New("partition device cannot be empty")
48 }
49
50 // Validate size
51 // Size must be positive and reasonable (less than 100TB)
52 if sizeMB < 1 {
53 return nil, errors.New("partition size must be at least 1MB")
54 }
55 if sizeMB > 104857600 { // 100TB in MB
56 return nil, errors.New("partition size is unreasonably large")
57 }
58
59 // Validate mount point
60 if mountPoint != "" && (mountPoint[0:1] != "/") {
61 return nil, errors.New("mount point must start with / or be empty")
62 }
63
64 // Filesystem-specific validations
65 if err := validateFilesystemMountPoint(fs, mountPoint); err != nil {
66 return nil, err
67 }
68
69 return &Partition{
70 device: device,
71 size: sizeMB,
72 filesystem: fs,
73 mountPoint: mountPoint,
74 encrypted: encrypted,
75 }, nil
76}
77
78// Device returns the device path
79func (p *Partition) Device() string {
80 return p.device
81}
82
83// SizeMB returns the partition size in megabytes
84func (p *Partition) SizeMB() int64 {
85 return p.size
86}
87
88// SizeGB returns the partition size in gigabytes (rounded)
89func (p *Partition) SizeGB() int64 {
90 return (p.size + 512) / 1024 // Round to nearest GB
91}
92
93// Filesystem returns the filesystem type
94func (p *Partition) Filesystem() FilesystemType {
95 return p.filesystem
96}
97
98// MountPoint returns the mount point
99func (p *Partition) MountPoint() string {
100 return p.mountPoint
101}
102
103// IsEncrypted returns true if partition is encrypted
104func (p *Partition) IsEncrypted() bool {
105 return p.encrypted
106}
107
108// IsBootPartition returns true if this is a boot partition
109func (p *Partition) IsBootPartition() bool {
110 return p.mountPoint == "/boot" || p.mountPoint == "/boot/efi"
111}
112
113// IsRootPartition returns true if this is the root partition
114func (p *Partition) IsRootPartition() bool {
115 return p.mountPoint == "/"
116}
117
118// IsEFI returns true if this is an EFI partition
119func (p *Partition) IsEFI() bool {
120 return p.filesystem == FilesystemFAT32 && p.mountPoint == "/boot/efi"
121}
122
123// String returns human-readable representation
124func (p *Partition) String() string {
125 return fmt.Sprintf("Partition(device=%s, size=%dGB, fs=%s, mount=%s, encrypted=%v)",
126 p.device, p.SizeGB(), p.filesystem.String(), p.mountPoint, p.encrypted)
127}
128
129// Equals checks if two partitions are equal
130func (p *Partition) Equals(other *Partition) bool {
131 if other == nil {
132 return false
133 }
134 return p.device == other.device &&
135 p.size == other.size &&
136 p.filesystem == other.filesystem &&
137 p.mountPoint == other.mountPoint &&
138 p.encrypted == other.encrypted
139}
140
141// Private helper functions
142
143func validateFilesystemMountPoint(fs FilesystemType, mountPoint string) error {
144 // FAT32 should only be used for EFI partitions
145 if fs == FilesystemFAT32 && mountPoint != "" && mountPoint != "/boot/efi" {
146 return errors.New("FAT32 filesystem should only be used for /boot/efi")
147 }
148
149 return nil
150}