osbase/blockdev: add ResizePartition

This will be used for growing the Monogon OS data partition.

Change-Id: I89fbf73f6452bd40991a182c185fc9c71e2f116b
Reviewed-on: https://review.monogon.dev/c/monogon/+/3359
Reviewed-by: Lorenz Brun <lorenz@monogon.tech>
Tested-by: Jenkins CI
diff --git a/osbase/blockdev/blockdev_linux.go b/osbase/blockdev/blockdev_linux.go
index f6d5b4c..7bc31fd 100644
--- a/osbase/blockdev/blockdev_linux.go
+++ b/osbase/blockdev/blockdev_linux.go
@@ -8,6 +8,7 @@
 	"io"
 	"math/bits"
 	"os"
+	"runtime"
 	"syscall"
 	"unsafe"
 
@@ -146,6 +147,37 @@
 	return nil
 }
 
+// ResizePartition updates the start and length of one partition in the kernel.
+// This can be used as an alternative to RefreshPartitionTable, which cannot
+// be used if any partition on this device is currently mounted.
+func (d *Device) ResizePartition(partitionNo int32, startByte, lengthBytes int64) error {
+	var ioctlPins runtime.Pinner
+	defer ioctlPins.Unpin()
+
+	partition := unix.BlkpgPartition{
+		Start:  startByte,
+		Length: lengthBytes,
+		Pno:    partitionNo,
+	}
+	ioctlPins.Pin(&partition)
+	arg := unix.BlkpgIoctlArg{
+		Op:      unix.BLKPG_RESIZE_PARTITION,
+		Datalen: int32(unsafe.Sizeof(partition)),
+		Data:    (*byte)(unsafe.Pointer(&partition)),
+	}
+
+	var err unix.Errno
+	if ctrlErr := d.rawConn.Control(func(fd uintptr) {
+		_, _, err = unix.Syscall(unix.SYS_IOCTL, fd, unix.BLKPG, uintptr(unsafe.Pointer(&arg)))
+	}); ctrlErr != nil {
+		return ctrlErr
+	}
+	if err != unix.Errno(0) {
+		return fmt.Errorf("ioctl(BLKPG): %w", err)
+	}
+	return nil
+}
+
 // Open opens a block device given a path to its inode.
 // TODO: exclusive, O_DIRECT
 func Open(path string) (*Device, error) {