mirror of
https://github.com/siderolabs/talos.git
synced 2025-10-15 01:21:32 +02:00
130 lines
3.3 KiB
Go
130 lines
3.3 KiB
Go
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
// Package blockdevice provides a library for working with block devices.
|
|
package blockdevice
|
|
|
|
import (
|
|
"bytes"
|
|
"os"
|
|
"unsafe"
|
|
|
|
"github.com/pkg/errors"
|
|
"github.com/talos-systems/talos/internal/pkg/blockdevice/table"
|
|
"github.com/talos-systems/talos/internal/pkg/blockdevice/table/gpt"
|
|
|
|
"golang.org/x/sys/unix"
|
|
)
|
|
|
|
// BlockDevice represents a block device.
|
|
type BlockDevice struct {
|
|
table table.PartitionTable
|
|
|
|
f *os.File
|
|
}
|
|
|
|
// Open initializes and returns a block device.
|
|
// TODO(andrewrynhard): Use BLKGETSIZE ioctl to get the size.
|
|
// TODO(andrewrynhard): Use BLKPBSZGET ioctl to get the physical sector size.
|
|
// TODO(andrewrynhard): Use BLKSSZGET ioctl to get the logical sector size
|
|
// and pass them into gpt as options.
|
|
func Open(devname string, setters ...Option) (bd *BlockDevice, err error) {
|
|
opts := NewDefaultOptions(setters...)
|
|
|
|
bd = &BlockDevice{}
|
|
|
|
var f *os.File
|
|
if f, err = os.OpenFile(devname, os.O_RDWR, os.ModeDevice); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
bd.f = f
|
|
|
|
defer func() {
|
|
if err != nil {
|
|
// nolint: errcheck
|
|
f.Close()
|
|
}
|
|
}()
|
|
|
|
if opts.CreateGPT {
|
|
gpt := gpt.NewGPT(devname, f)
|
|
var pt table.PartitionTable
|
|
if pt, err = gpt.New(); err != nil {
|
|
return nil, err
|
|
}
|
|
if err = pt.Write(); err != nil {
|
|
return nil, err
|
|
}
|
|
bd.table = pt
|
|
} else {
|
|
buf := make([]byte, 1)
|
|
// PMBR protective entry starts at 446. The partition type is at offset
|
|
// 4 from the start of the PMBR protective entry.
|
|
_, err = f.ReadAt(buf, 450)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
// For GPT, the partition type should be 0xee (EFI GPT).
|
|
if bytes.Equal(buf, []byte{0xee}) {
|
|
bd.table = gpt.NewGPT(devname, f)
|
|
}
|
|
}
|
|
|
|
return bd, nil
|
|
}
|
|
|
|
// Close closes the block devices's open file.
|
|
func (bd *BlockDevice) Close() error {
|
|
return bd.f.Close()
|
|
}
|
|
|
|
// PartitionTable returns the block device partition table.
|
|
func (bd *BlockDevice) PartitionTable(read bool) (table.PartitionTable, error) {
|
|
if bd.table == nil {
|
|
return nil, errors.New("missing partition table")
|
|
}
|
|
|
|
if !read {
|
|
return bd.table, nil
|
|
}
|
|
|
|
return bd.table, bd.table.Read()
|
|
}
|
|
|
|
// RereadPartitionTable invokes the BLKRRPART ioctl to have the kernel read the
|
|
// partition table.
|
|
func (bd *BlockDevice) RereadPartitionTable() error {
|
|
// Flush the file buffers.
|
|
// NOTE(andrewrynhard): I'm not entirely sure we need this, but
|
|
// figured it wouldn't hurt.
|
|
if err := bd.f.Sync(); err != nil {
|
|
return err
|
|
}
|
|
// Flush the block device buffers.
|
|
if _, _, ret := unix.Syscall(unix.SYS_IOCTL, bd.f.Fd(), unix.BLKFLSBUF, 0); ret != 0 {
|
|
return errors.Errorf("flush block device buffers: %v", ret)
|
|
}
|
|
// Reread the partition table.
|
|
if _, _, ret := unix.Syscall(unix.SYS_IOCTL, bd.f.Fd(), unix.BLKRRPART, 0); ret != 0 {
|
|
return errors.Errorf("re-read partition table: %v", ret)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Device returns the backing file for the block device
|
|
func (bd *BlockDevice) Device() *os.File {
|
|
return bd.f
|
|
}
|
|
|
|
// Size returns the size of the block device in bytes.
|
|
func (bd *BlockDevice) Size() (uint64, error) {
|
|
var devsize uint64
|
|
if _, _, errno := unix.Syscall(unix.SYS_IOCTL, bd.f.Fd(), unix.BLKGETSIZE64, uintptr(unsafe.Pointer(&devsize))); errno != 0 {
|
|
return 0, errno
|
|
}
|
|
return devsize, nil
|
|
}
|