feat: add blockd service (#172)
This commit is contained in:
parent
c8de882c67
commit
aa65101232
@ -170,6 +170,13 @@
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="sidebar-item">
|
||||
<a class="sidebar-link"
|
||||
href="https://dianemo.autonomy.io/components/blockd/" >
|
||||
blockd
|
||||
</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
|
283
docs/components/blockd/index.html
Normal file
283
docs/components/blockd/index.html
Normal file
@ -0,0 +1,283 @@
|
||||
<!DOCTYPE html>
|
||||
|
||||
<head>
|
||||
|
||||
|
||||
<meta charset="utf-8">
|
||||
<title>Autonomy</title>
|
||||
<meta name="description" content="">
|
||||
<meta name="author" content="andrew.rynhard@autonomy.io">
|
||||
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
|
||||
<link href="https://fonts.googleapis.com/css?family=Raleway|Fira+Mono|Roboto:300" rel="stylesheet">
|
||||
|
||||
|
||||
<link rel="icon" type="image/png" href="https://dianemo.autonomy.io/img/favicon.png">
|
||||
|
||||
|
||||
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/fuse.js/3.2.0/fuse.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/mark.js/8.11.1/jquery.mark.min.js"></script>
|
||||
<script src="https://dianemo.autonomy.io/js/search.js"></script>
|
||||
|
||||
|
||||
<link rel="stylesheet" href="https://dianemo.autonomy.io//css/milligram.min.css">
|
||||
<link rel="stylesheet" href="https://dianemo.autonomy.io/css/main.css">
|
||||
</head>
|
||||
<nav class="navbar">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="column column-50">
|
||||
<ul class="navbar-list navbar-left">
|
||||
<li class="navbar-item">
|
||||
<a class="navbar-link logo" href="/">
|
||||
<img src="https://dianemo.autonomy.io//img/logo.svg" class="logo">
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="column column-50">
|
||||
<ul class="navbar-list navbar-right">
|
||||
<li class="navbar-item">
|
||||
|
||||
<a class="navbar-link navbar-logo" rel="noopener noreferrer" href="https://github.com/autonomy/dianemo" target="_blank">
|
||||
<span class="octicon octicon-mark-github"></span>
|
||||
</a>
|
||||
|
||||
</li>
|
||||
<li class="navbar-item">
|
||||
|
||||
<a class="navbar-link navbar-logo" rel="noopener noreferrer" href="https://hub.docker.com/u/autonomy" target="_blank">
|
||||
<span class="fab fa-docker"></span>
|
||||
</a>
|
||||
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<script id="search-result-template" type="text/x-js-template">
|
||||
<li class="sidebar-item">
|
||||
<div id="summary-${key}">
|
||||
<a class="sidebar-link" href="${link}">${title}</a>
|
||||
<p class="search-result-item">${preview}</p>
|
||||
</div>
|
||||
</li>
|
||||
</script>
|
||||
|
||||
<nav class="sidebar">
|
||||
|
||||
<div class="row">
|
||||
<div class="column">
|
||||
<span>
|
||||
<a class="logo" href="https://dianemo.autonomy.io/">
|
||||
<img src="https://dianemo.autonomy.io//img/logo.svg" class="logo">
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
|
||||
<div class="row">
|
||||
<div class="column">
|
||||
|
||||
<div class="button-group button-group-center">
|
||||
<a class="button" href="https://github.com/autonomy/dianemo/fork">
|
||||
<span class="octicon octicon-repo-forked"></span>
|
||||
Fork
|
||||
</a>
|
||||
<a class="button" href="https://github.com/autonomy/dianemo/stargazers">
|
||||
<span class="octicon octicon-star"></span>
|
||||
Star
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
|
||||
<div class="row search-area">
|
||||
<form class="search-form" action="" onSubmit="return">
|
||||
<input class="search-box" id="search-query" name="s" type="text" placeholder="search" />
|
||||
</form>
|
||||
<ul class="sidebar-list search-results" id="search-results">
|
||||
</ul>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="column">
|
||||
<ul class="sidebar-list parent">
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="sidebar-item">
|
||||
<a class="sidebar-link sidebar-link-parent active"
|
||||
href="https://dianemo.autonomy.io/components/" >
|
||||
Components
|
||||
</a>
|
||||
<ul class="sidebar-list active">
|
||||
|
||||
<li class="sidebar-item">
|
||||
<a class="sidebar-link"
|
||||
href="https://dianemo.autonomy.io/components/kernel/" >
|
||||
kernel
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="sidebar-item">
|
||||
<a class="sidebar-link"
|
||||
href="https://dianemo.autonomy.io/components/init/" >
|
||||
init
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="sidebar-item">
|
||||
<a class="sidebar-link"
|
||||
href="https://dianemo.autonomy.io/components/kubeadm/" >
|
||||
kubeadm
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="sidebar-item">
|
||||
<a class="sidebar-link"
|
||||
href="https://dianemo.autonomy.io/components/trustd/" >
|
||||
trustd
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="sidebar-item">
|
||||
<a class="sidebar-link"
|
||||
href="https://dianemo.autonomy.io/components/proxyd/" >
|
||||
proxyd
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="sidebar-item">
|
||||
<a class="sidebar-link"
|
||||
href="https://dianemo.autonomy.io/components/osd/" >
|
||||
osd
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="sidebar-item">
|
||||
<a class="sidebar-link"
|
||||
href="https://dianemo.autonomy.io/components/osctl/" >
|
||||
osctl
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="sidebar-item">
|
||||
<a class="sidebar-link active"
|
||||
href="https://dianemo.autonomy.io/components/blockd/" >
|
||||
blockd
|
||||
</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
<li class="sidebar-item">
|
||||
<a class="sidebar-link sidebar-link-parent"
|
||||
href="https://dianemo.autonomy.io/configuration/" >
|
||||
Configuration
|
||||
</a>
|
||||
<ul class="sidebar-list">
|
||||
|
||||
<li class="sidebar-item">
|
||||
<a class="sidebar-link"
|
||||
href="https://dianemo.autonomy.io/configuration/controlplane/" >
|
||||
Control Plane
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="sidebar-item">
|
||||
<a class="sidebar-link"
|
||||
href="https://dianemo.autonomy.io/configuration/workers/" >
|
||||
Workers
|
||||
</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
<li class="sidebar-item">
|
||||
<a class="sidebar-link sidebar-link-parent"
|
||||
href="https://dianemo.autonomy.io/examples/" >
|
||||
Examples
|
||||
</a>
|
||||
<ul class="sidebar-list">
|
||||
|
||||
<li class="sidebar-item">
|
||||
<a class="sidebar-link"
|
||||
href="https://dianemo.autonomy.io/examples/aws/" >
|
||||
AWS
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="sidebar-item">
|
||||
<a class="sidebar-link"
|
||||
href="https://dianemo.autonomy.io/examples/kvm/" >
|
||||
KVM
|
||||
</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="content">
|
||||
<div class="row ">
|
||||
<div class="column column-10">
|
||||
|
||||
<a class="navigation navigation-previous" href="https://dianemo.autonomy.io/components/osctl/">
|
||||
<i class="fa fa-chevron-left"></i>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
<div class="column document">
|
||||
<section class="document">
|
||||
<h1 class="title">blockd</h1>
|
||||
<p><p>Dianemo comes with a reserved block device with three partitions:</p>
|
||||
|
||||
<ul>
|
||||
<li>an EFI System Partition (<code>ESP</code>)</li>
|
||||
<li>a <code>ROOT</code> partition mounted as read-only that contains the minimal set of binaries to operate system services</li>
|
||||
<li>and a <code>DATA</code> partion that is mounted as read/write at <code>/var/run</code></li>
|
||||
</ul>
|
||||
|
||||
<p>These partitions are reserved and cannot be modified.
|
||||
The one expection to this is that the <code>DATA</code> partition will be resized automatically in the <code>init</code> process to the maximum size possible.
|
||||
Managing any other block device can be done via the <code>blockd</code> service.</p>
|
||||
</p>
|
||||
</section>
|
||||
</div>
|
||||
<div class="column column-10">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
<div class="footer">
|
||||
<aside class="copyright">
|
||||
|
||||
© 2018 Released under Mozilla Public License 2.0
|
||||
|
||||
</aside>
|
||||
</div>
|
||||
|
@ -170,6 +170,13 @@
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="sidebar-item">
|
||||
<a class="sidebar-link"
|
||||
href="https://dianemo.autonomy.io/components/blockd/" >
|
||||
blockd
|
||||
</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
|
@ -79,5 +79,15 @@ To make this work, we needed an out-of-band tool for managing the nodes. In an i
|
||||
retrieve container logs restart a service reset a node reboot a node retrieve kernel logs generate pki resources inject data into node configuration files </description>
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<title>blockd</title>
|
||||
<link>https://dianemo.autonomy.io/components/blockd/</link>
|
||||
<pubDate>Tue, 30 Oct 2018 09:16:35 -0700</pubDate>
|
||||
|
||||
<guid>https://dianemo.autonomy.io/components/blockd/</guid>
|
||||
<description>Dianemo comes with a reserved block device with three partitions:
|
||||
an EFI System Partition (ESP) a ROOT partition mounted as read-only that contains the minimal set of binaries to operate system services and a DATA partion that is mounted as read/write at /var/run These partitions are reserved and cannot be modified. The one expection to this is that the DATA partition will be resized automatically in the init process to the maximum size possible.</description>
|
||||
</item>
|
||||
|
||||
</channel>
|
||||
</rss>
|
@ -170,6 +170,13 @@
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="sidebar-item">
|
||||
<a class="sidebar-link"
|
||||
href="https://dianemo.autonomy.io/components/blockd/" >
|
||||
blockd
|
||||
</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
|
@ -170,6 +170,13 @@
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="sidebar-item">
|
||||
<a class="sidebar-link"
|
||||
href="https://dianemo.autonomy.io/components/blockd/" >
|
||||
blockd
|
||||
</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
|
@ -170,6 +170,13 @@
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="sidebar-item">
|
||||
<a class="sidebar-link"
|
||||
href="https://dianemo.autonomy.io/components/blockd/" >
|
||||
blockd
|
||||
</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
|
@ -170,6 +170,13 @@
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="sidebar-item">
|
||||
<a class="sidebar-link"
|
||||
href="https://dianemo.autonomy.io/components/blockd/" >
|
||||
blockd
|
||||
</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
@ -261,6 +268,10 @@ With it you can do things like:</p>
|
||||
</div>
|
||||
<div class="column column-10">
|
||||
|
||||
<a class="navigation navigation-next" href="https://dianemo.autonomy.io/components/blockd/">
|
||||
<i class="fa fa-chevron-right"></i>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -170,6 +170,13 @@
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="sidebar-item">
|
||||
<a class="sidebar-link"
|
||||
href="https://dianemo.autonomy.io/components/blockd/" >
|
||||
blockd
|
||||
</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
|
@ -170,6 +170,13 @@
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="sidebar-item">
|
||||
<a class="sidebar-link"
|
||||
href="https://dianemo.autonomy.io/components/blockd/" >
|
||||
blockd
|
||||
</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
|
@ -170,6 +170,13 @@
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="sidebar-item">
|
||||
<a class="sidebar-link"
|
||||
href="https://dianemo.autonomy.io/components/blockd/" >
|
||||
blockd
|
||||
</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
|
@ -170,6 +170,13 @@
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="sidebar-item">
|
||||
<a class="sidebar-link"
|
||||
href="https://dianemo.autonomy.io/components/blockd/" >
|
||||
blockd
|
||||
</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
|
@ -170,6 +170,13 @@
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="sidebar-item">
|
||||
<a class="sidebar-link"
|
||||
href="https://dianemo.autonomy.io/components/blockd/" >
|
||||
blockd
|
||||
</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
|
@ -170,6 +170,13 @@
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="sidebar-item">
|
||||
<a class="sidebar-link"
|
||||
href="https://dianemo.autonomy.io/components/blockd/" >
|
||||
blockd
|
||||
</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
|
@ -170,6 +170,13 @@
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="sidebar-item">
|
||||
<a class="sidebar-link"
|
||||
href="https://dianemo.autonomy.io/components/blockd/" >
|
||||
blockd
|
||||
</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
|
@ -170,6 +170,13 @@
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="sidebar-item">
|
||||
<a class="sidebar-link"
|
||||
href="https://dianemo.autonomy.io/components/blockd/" >
|
||||
blockd
|
||||
</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
|
@ -170,6 +170,13 @@
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="sidebar-item">
|
||||
<a class="sidebar-link"
|
||||
href="https://dianemo.autonomy.io/components/blockd/" >
|
||||
blockd
|
||||
</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
|
@ -170,6 +170,13 @@
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="sidebar-item">
|
||||
<a class="sidebar-link"
|
||||
href="https://dianemo.autonomy.io/components/blockd/" >
|
||||
blockd
|
||||
</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
|
@ -171,6 +171,13 @@
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="sidebar-item">
|
||||
<a class="sidebar-link"
|
||||
href="https://dianemo.autonomy.io/components/blockd/" >
|
||||
blockd
|
||||
</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
|
File diff suppressed because one or more lines are too long
@ -79,6 +79,16 @@ To make this work, we needed an out-of-band tool for managing the nodes. In an i
|
||||
retrieve container logs restart a service reset a node reboot a node retrieve kernel logs generate pki resources inject data into node configuration files </description>
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<title>blockd</title>
|
||||
<link>https://dianemo.autonomy.io/components/blockd/</link>
|
||||
<pubDate>Tue, 30 Oct 2018 09:16:35 -0700</pubDate>
|
||||
|
||||
<guid>https://dianemo.autonomy.io/components/blockd/</guid>
|
||||
<description>Dianemo comes with a reserved block device with three partitions:
|
||||
an EFI System Partition (ESP) a ROOT partition mounted as read-only that contains the minimal set of binaries to operate system services and a DATA partion that is mounted as read/write at /var/run These partitions are reserved and cannot be modified. The one expection to this is that the DATA partition will be resized automatically in the init process to the maximum size possible.</description>
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<title>AWS</title>
|
||||
<link>https://dianemo.autonomy.io/examples/aws/</link>
|
||||
|
@ -37,6 +37,11 @@
|
||||
<lastmod>2018-10-29T19:40:55-07:00</lastmod>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://dianemo.autonomy.io/components/blockd/</loc>
|
||||
<lastmod>2018-10-30T09:16:35-07:00</lastmod>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://dianemo.autonomy.io/examples/aws/</loc>
|
||||
<lastmod>2018-10-29T19:40:55-07:00</lastmod>
|
||||
|
@ -170,6 +170,13 @@
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="sidebar-item">
|
||||
<a class="sidebar-link"
|
||||
href="https://dianemo.autonomy.io/components/blockd/" >
|
||||
blockd
|
||||
</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
|
19
src/docs/src/content/components/blockd.md
Normal file
19
src/docs/src/content/components/blockd.md
Normal file
@ -0,0 +1,19 @@
|
||||
---
|
||||
title: "blockd"
|
||||
date: 2018-10-30T09:16:35-07:00
|
||||
draft: false
|
||||
menu:
|
||||
main:
|
||||
parent: 'components'
|
||||
weight: 80
|
||||
---
|
||||
|
||||
Dianemo comes with a reserved block device with three partitions:
|
||||
|
||||
- an EFI System Partition (`ESP`)
|
||||
- a `ROOT` partition mounted as read-only that contains the minimal set of binaries to operate system services
|
||||
- and a `DATA` partion that is mounted as read/write at `/var/run`
|
||||
|
||||
These partitions are reserved and cannot be modified.
|
||||
The one expection to this is that the `DATA` partition will be resized automatically in the `init` process to the maximum size possible.
|
||||
Managing any other block device can be done via the `blockd` service.
|
@ -64,7 +64,7 @@ tasks:
|
||||
COPY src/packer.json /packer.json
|
||||
COPY src/entrypoint.sh /bin/entrypoint.sh
|
||||
RUN chmod +x /bin/entrypoint.sh
|
||||
{{ if .Git.IsTag }}
|
||||
{{ if and .Git.IsClean .Git.IsTag }}
|
||||
ENV VERSION {{ .Git.Tag }}
|
||||
{{ else }}
|
||||
ENV VERSION {{ .Git.SHA }}
|
||||
|
@ -11,33 +11,43 @@ function create_image() {
|
||||
|
||||
if [ "$FULL" = true ] ; then
|
||||
if [ "$RAW" = true ] ; then
|
||||
parted -s -a optimal ${RAW_IMAGE} mkpart ESP fat32 0 $((${INITRAMFS_SIZE} + 50))M
|
||||
parted -s -a optimal ${RAW_IMAGE} mkpart ROOT xfs $((${INITRAMFS_SIZE} + 50))M $((${ROOTFS_SIZE} + ${INITRAMFS_SIZE} + 100))M
|
||||
parted -s -a optimal ${RAW_IMAGE} mkpart DATA xfs $((${ROOTFS_SIZE} + ${INITRAMFS_SIZE} + 100))M 100%
|
||||
parted -s -a optimal ${RAW_IMAGE} mkpart primary fat32 0 $((${INITRAMFS_SIZE} + 50))M
|
||||
parted ${RAW_IMAGE} name 1 ESP
|
||||
parted -s -a optimal ${RAW_IMAGE} mkpart primary xfs $((${INITRAMFS_SIZE} + 50))M $((${ROOTFS_SIZE} + ${INITRAMFS_SIZE} + 100))M
|
||||
parted ${RAW_IMAGE} name 2 ROOT
|
||||
parted -s -a optimal ${RAW_IMAGE} mkpart primary xfs $((${ROOTFS_SIZE} + ${INITRAMFS_SIZE} + 100))M 100%
|
||||
parted ${RAW_IMAGE} name 3 DATA
|
||||
losetup ${DEVICE} ${RAW_IMAGE}
|
||||
partx -av ${DEVICE}
|
||||
extract_boot_partition ${DEVICE}p1
|
||||
extract_root_partition ${DEVICE}p2
|
||||
extract_data_partition ${DEVICE}p3
|
||||
else
|
||||
parted -s -a optimal ${DEVICE} mkpart ESP fat32 0 $((${INITRAMFS_SIZE} + 50))M
|
||||
parted -s -a optimal ${DEVICE} mkpart ROOT xfs $((${INITRAMFS_SIZE} + 50))M $((${ROOTFS_SIZE} + ${INITRAMFS_SIZE} + 100))M
|
||||
parted -s -a optimal ${DEVICE} mkpart DATA xfs $((${ROOTFS_SIZE} + ${INITRAMFS_SIZE} + 100))M 100%
|
||||
parted -s -a optimal ${DEVICE} mkpart primary fat32 0 $((${INITRAMFS_SIZE} + 50))M
|
||||
parted ${DEVICE} name 1 ESP
|
||||
parted -s -a optimal ${DEVICE} mkpart primary xfs $((${INITRAMFS_SIZE} + 50))M $((${ROOTFS_SIZE} + ${INITRAMFS_SIZE} + 100))M
|
||||
parted ${DEVICE} name 2 ROOT
|
||||
parted -s -a optimal ${DEVICE} mkpart primary xfs $((${ROOTFS_SIZE} + ${INITRAMFS_SIZE} + 100))M 100%
|
||||
parted ${DEVICE} name 3 DATA
|
||||
extract_boot_partition ${DEVICE}1
|
||||
extract_root_partition ${DEVICE}2
|
||||
extract_data_partition ${DEVICE}3
|
||||
fi
|
||||
else
|
||||
if [ "$RAW" = true ] ; then
|
||||
parted -s -a optimal ${RAW_IMAGE} mkpart ROOT xfs 0 $((${ROOTFS_SIZE} + 50))M
|
||||
parted -s -a optimal ${RAW_IMAGE} mkpart DATA xfs $((${ROOTFS_SIZE} + 50))M 100%
|
||||
parted -s -a optimal ${RAW_IMAGE} mkpart primary xfs 0 $((${ROOTFS_SIZE} + 50))M
|
||||
parted ${RAW_IMAGE} name 1 ROOT
|
||||
parted -s -a optimal ${RAW_IMAGE} mkpart primary xfs $((${ROOTFS_SIZE} + 50))M 100%
|
||||
parted ${RAW_IMAGE} name 2 DATA
|
||||
losetup ${DEVICE} ${RAW_IMAGE}
|
||||
partx -av ${DEVICE}
|
||||
extract_root_partition ${DEVICE}p1
|
||||
extract_data_partition ${DEVICE}p2
|
||||
else
|
||||
parted -s -a optimal ${DEVICE} mkpart ROOT xfs 0 $((${ROOTFS_SIZE} + 50))M
|
||||
parted -s -a optimal ${DEVICE} mkpart DATA xfs $((${ROOTFS_SIZE} + 50))M 100%
|
||||
parted -s -a optimal ${DEVICE} mkpart primary xfs 0 $((${ROOTFS_SIZE} + 50))M
|
||||
parted ${DEVICE} name 1 ROOT
|
||||
parted -s -a optimal ${DEVICE} mkpart primary xfs $((${ROOTFS_SIZE} + 50))M 100%
|
||||
parted ${DEVICE} name 2 DATA
|
||||
extract_root_partition ${DEVICE}1
|
||||
extract_data_partition ${DEVICE}2
|
||||
fi
|
||||
|
@ -3,6 +3,7 @@ metadata:
|
||||
variables:
|
||||
rootfs: /rootfs
|
||||
versionPath: github.com/autonomy/dianemo/src/initramfs/pkg/version
|
||||
srcXfsprogs: https://www.kernel.org/pub/linux/utils/fs/xfs/xfsprogs/xfsprogs-4.18.0.tar.xz
|
||||
script:
|
||||
template: |
|
||||
#!/bin/sh
|
||||
@ -18,6 +19,9 @@ script:
|
||||
docker tag {{ .Repository }}:proxyd autonomy/proxyd:{{ .Git.SHA }}
|
||||
docker save autonomy/proxyd:{{ .Git.SHA }} -o ../../build/proxyd.tar
|
||||
|
||||
docker tag {{ .Repository }}:blockd autonomy/blockd:{{ .Git.SHA }}
|
||||
docker save autonomy/blockd:{{ .Git.SHA }} -o ../../build/blockd.tar
|
||||
|
||||
{{ if and (.Git.IsClean) (or (.Git.IsTag) (eq .Git.Branch "master")) }}
|
||||
docker login --username=$DOCKER_USERNAME --password=$DOCKER_PASSWORD
|
||||
|
||||
@ -32,21 +36,27 @@ script:
|
||||
docker tag autonomy/proxyd:{{ .Git.SHA }} autonomy/proxyd:latest
|
||||
docker push autonomy/proxyd:{{ .Git.SHA }}
|
||||
docker push autonomy/proxyd:latest
|
||||
|
||||
docker tag autonomy/blockd:{{ .Git.SHA }} autonomy/blockd:latest
|
||||
docker push autonomy/blockd:{{ .Git.SHA }}
|
||||
docker push autonomy/blockd:latest
|
||||
{{ end }}
|
||||
pipeline:
|
||||
stages:
|
||||
- generate
|
||||
- base
|
||||
- initramfs
|
||||
- osd
|
||||
- osctl
|
||||
- trustd
|
||||
- proxyd
|
||||
- initramfs
|
||||
- blockd
|
||||
- test
|
||||
- image
|
||||
stages:
|
||||
base:
|
||||
tasks:
|
||||
- src
|
||||
- test
|
||||
osd:
|
||||
tasks:
|
||||
- osd
|
||||
@ -64,10 +74,16 @@ stages:
|
||||
proxyd:
|
||||
tasks:
|
||||
- proxyd
|
||||
blockd:
|
||||
tasks:
|
||||
- blockd
|
||||
initramfs:
|
||||
tasks:
|
||||
- xfsprogs
|
||||
- init
|
||||
- initramfs
|
||||
image:
|
||||
tasks:
|
||||
- image
|
||||
generate:
|
||||
artifacts:
|
||||
@ -75,26 +91,47 @@ stages:
|
||||
destination: ./cmd/osd
|
||||
- source: /src/github.com/autonomy/dianemo/src/initramfs/cmd/trustd/proto
|
||||
destination: ./cmd/trustd
|
||||
- source: /src/github.com/autonomy/dianemo/src/initramfs/cmd/blockd/proto
|
||||
destination: ./cmd/blockd
|
||||
tasks:
|
||||
- proto
|
||||
test:
|
||||
tasks:
|
||||
- test
|
||||
tasks:
|
||||
image:
|
||||
template: |
|
||||
FROM scratch
|
||||
WORKDIR /tmp
|
||||
COPY --from=init /initramfs/initramfs.xz initramfs.xz
|
||||
CMD false
|
||||
init:
|
||||
blockd:
|
||||
template: |
|
||||
FROM {{ .Repository }}:base AS {{ .Docker.CurrentStage }}
|
||||
WORKDIR /src/github.com/autonomy/dianemo/src/initramfs/cmd/{{ .Docker.CurrentStage }}
|
||||
{{ if and .Git.IsClean .Git.IsTag }}
|
||||
RUN GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -a \
|
||||
-ldflags "-s -w -X {{ index .Variables "versionPath" }}.Name=Blockd -X {{ index .Variables "versionPath" }}.Tag={{ .Git.Tag }} -X {{ index .Variables "versionPath" }}.SHA={{ .Git.SHA }} -X \"{{ index .Variables "versionPath" }}.Built={{ .Built }}\"" \
|
||||
-o /{{ .Docker.CurrentStage }}
|
||||
{{ else }}
|
||||
RUN GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -a \
|
||||
-ldflags "-s -w -X {{ index .Variables "versionPath" }}.Name=Blockd -X {{ index .Variables "versionPath" }}.Tag=none -X {{ index .Variables "versionPath" }}.SHA={{ .Git.SHA }}" \
|
||||
-o /{{ .Docker.CurrentStage }}
|
||||
{{ end }}
|
||||
RUN chmod +x /{{ .Docker.CurrentStage }}
|
||||
FROM scratch
|
||||
COPY --from={{ .Docker.CurrentStage }} /{{ .Docker.CurrentStage }} /{{ .Docker.CurrentStage }}
|
||||
ENTRYPOINT ["/{{ .Docker.CurrentStage }}"]
|
||||
image:
|
||||
template: |
|
||||
FROM scratch
|
||||
WORKDIR /tmp
|
||||
COPY --from=dianemo/initramfs:initramfs /initramfs/initramfs.xz initramfs.xz
|
||||
CMD false
|
||||
init:
|
||||
template: |
|
||||
WORKDIR /src/github.com/autonomy/dianemo/src/initramfs/cmd/{{ .Docker.CurrentStage }}
|
||||
{{ if and .Git.IsClean .Git.IsTag }}
|
||||
RUN GOOS=linux GOARCH=amd64 go build \
|
||||
-ldflags "-s -w -linkmode external -extldflags \"-L/lib -lblkid -luuid\" -X {{ index .Variables "versionPath" }}.Name=Dianemo -X {{ index .Variables "versionPath" }}.Tag={{ .Git.Tag }} -X {{ index .Variables "versionPath" }}.SHA={{ .Git.SHA }} -X \"{{ index .Variables "versionPath" }}.Built={{ .Built }}\"" \
|
||||
-ldflags "-s -w -X {{ index .Variables "versionPath" }}.Name=Dianemo -X {{ index .Variables "versionPath" }}.Tag={{ .Git.Tag }} -X {{ index .Variables "versionPath" }}.SHA={{ .Git.SHA }} -X \"{{ index .Variables "versionPath" }}.Built={{ .Built }}\"" \
|
||||
-o /initramfs/init
|
||||
{{ else }}
|
||||
RUN GOOS=linux GOARCH=amd64 go build \
|
||||
-ldflags "-s -w -linkmode external -extldflags \"-L/lib -lblkid -luuid\" -X {{ index .Variables "versionPath" }}.Name=Dianemo -X {{ index .Variables "versionPath" }}.Tag=none -X {{ index .Variables "versionPath" }}.SHA={{ .Git.SHA }}" \
|
||||
-ldflags "-s -w -X {{ index .Variables "versionPath" }}.Name=Dianemo -X {{ index .Variables "versionPath" }}.Tag=none -X {{ index .Variables "versionPath" }}.SHA={{ .Git.SHA }}" \
|
||||
-o /initramfs/init
|
||||
{{ end }}
|
||||
RUN chmod +x /initramfs/init
|
||||
@ -110,7 +147,7 @@ tasks:
|
||||
{{ else }}
|
||||
RUN find . 2>/dev/null | cpio -H newc -o | xz -v -C crc32 -0 -e -T 0 -z >/tmp/initramfs.xz
|
||||
{{ end }}
|
||||
RUN cp /tmp/initramfs.xz /initramfs/initramfs.xz
|
||||
RUN cp /tmp/initramfs.xz .
|
||||
osctl:
|
||||
template: |
|
||||
FROM {{ .Repository }}:base AS {{ .Docker.CurrentStage }}
|
||||
@ -166,6 +203,9 @@ tasks:
|
||||
WORKDIR /src/github.com/autonomy/dianemo/src/initramfs/cmd/trustd
|
||||
COPY ./cmd/trustd/proto ./proto
|
||||
RUN protoc -I/usr/local/include -I./proto --go_out=plugins=grpc:proto proto/api.proto
|
||||
WORKDIR /src/github.com/autonomy/dianemo/src/initramfs/cmd/blockd
|
||||
COPY ./cmd/blockd/proto ./proto
|
||||
RUN protoc -I/usr/local/include -I./proto --go_out=plugins=grpc:proto proto/api.proto
|
||||
proxyd:
|
||||
template: |
|
||||
FROM {{ .Repository }}:base AS {{ .Docker.CurrentStage }}
|
||||
@ -199,6 +239,7 @@ tasks:
|
||||
RUN go mod verify
|
||||
test:
|
||||
template: |
|
||||
FROM {{ .Repository }}:base AS {{ .Docker.CurrentStage }}
|
||||
WORKDIR /src/github.com/autonomy/dianemo/src/initramfs
|
||||
RUN chmod +x ./hack/test.sh
|
||||
RUN ./hack/test.sh --lint ./hack/golangci-lint.yaml
|
||||
@ -220,3 +261,14 @@ tasks:
|
||||
FROM scratch
|
||||
COPY --from={{ .Docker.CurrentStage }} /{{ .Docker.CurrentStage }} /{{ .Docker.CurrentStage }}
|
||||
ENTRYPOINT ["/{{ .Docker.CurrentStage }}"]
|
||||
xfsprogs:
|
||||
template: |
|
||||
FROM {{ .Repository }}:base AS {{ .Docker.CurrentStage }}
|
||||
WORKDIR /tmp/{{ .Docker.CurrentStage }}
|
||||
RUN curl -L {{index .Variables "srcXfsprogs" }} | tar -xJ --strip-components=1
|
||||
RUN make \
|
||||
DEBUG=-DNDEBUG \
|
||||
INSTALL_USER=0 \
|
||||
INSTALL_GROUP=0 \
|
||||
LOCAL_CONFIGURE_OPTIONS="--prefix=/usr"
|
||||
RUN make install DESTDIR={{ index .Variables "rootfs" }}
|
||||
|
65
src/initramfs/cmd/blockd/main.go
Normal file
65
src/initramfs/cmd/blockd/main.go
Normal file
@ -0,0 +1,65 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"log"
|
||||
|
||||
"github.com/autonomy/dianemo/src/initramfs/cmd/trustd/pkg/reg"
|
||||
"github.com/autonomy/dianemo/src/initramfs/pkg/grpc/factory"
|
||||
"github.com/autonomy/dianemo/src/initramfs/pkg/grpc/gen"
|
||||
"github.com/autonomy/dianemo/src/initramfs/pkg/grpc/tls"
|
||||
"github.com/autonomy/dianemo/src/initramfs/pkg/userdata"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials"
|
||||
)
|
||||
|
||||
var (
|
||||
dataPath *string
|
||||
generate *bool
|
||||
trustdPort *int
|
||||
)
|
||||
|
||||
func init() {
|
||||
log.SetFlags(log.Lshortfile | log.Ldate | log.Lmicroseconds | log.Ltime)
|
||||
dataPath = flag.String("userdata", "", "the path to the user data")
|
||||
trustdPort = flag.Int("trustd-port", 50001, "the trustd port")
|
||||
generate = flag.Bool("generate", false, "generate the TLS certificate using one of the Root of Trusts")
|
||||
flag.Parse()
|
||||
}
|
||||
|
||||
func main() {
|
||||
data, err := userdata.Open(*dataPath)
|
||||
if err != nil {
|
||||
log.Fatalf("open user data: %v", err)
|
||||
}
|
||||
|
||||
if *generate {
|
||||
var generator *gen.Generator
|
||||
generator, err = gen.NewGenerator(data, *trustdPort)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if err = generator.Identity(data.Security); err != nil {
|
||||
log.Fatalf("generate identity: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
config, err := tls.NewConfig(tls.Mutual, data.Security.OS)
|
||||
if err != nil {
|
||||
log.Fatalf("credentials: %v", err)
|
||||
}
|
||||
|
||||
log.Println("Starting blockd")
|
||||
err = factory.Listen(
|
||||
®.Registrator{Data: data.Security.OS},
|
||||
factory.Network("unix"),
|
||||
factory.ServerOptions(
|
||||
grpc.Creds(
|
||||
credentials.NewTLS(config),
|
||||
),
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
log.Fatalf("listen: %v", err)
|
||||
}
|
||||
}
|
26
src/initramfs/cmd/blockd/pkg/reg/reg.go
Normal file
26
src/initramfs/cmd/blockd/pkg/reg/reg.go
Normal file
@ -0,0 +1,26 @@
|
||||
package reg
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/autonomy/dianemo/src/initramfs/cmd/blockd/proto"
|
||||
"github.com/autonomy/dianemo/src/initramfs/pkg/userdata"
|
||||
"github.com/golang/protobuf/ptypes/empty"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
// Registrator is the concrete type that implements the factory.Registrator and
|
||||
// proto.BlockdServer interfaces.
|
||||
type Registrator struct {
|
||||
Data *userdata.OSSecurity
|
||||
}
|
||||
|
||||
// Register implements the factory.Registrator interface.
|
||||
func (r *Registrator) Register(s *grpc.Server) {
|
||||
proto.RegisterBlockdServer(s, r)
|
||||
}
|
||||
|
||||
// Resize implements the proto.BlockdServer interface.
|
||||
func (r *Registrator) Resize(ctx context.Context, in *proto.ResizePartitionRequest) (reply *empty.Empty, err error) {
|
||||
return nil, nil
|
||||
}
|
165
src/initramfs/cmd/blockd/proto/api.pb.go
Normal file
165
src/initramfs/cmd/blockd/proto/api.pb.go
Normal file
@ -0,0 +1,165 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// source: api.proto
|
||||
|
||||
package proto
|
||||
|
||||
import (
|
||||
fmt "fmt"
|
||||
proto "github.com/golang/protobuf/proto"
|
||||
empty "github.com/golang/protobuf/ptypes/empty"
|
||||
context "golang.org/x/net/context"
|
||||
grpc "google.golang.org/grpc"
|
||||
math "math"
|
||||
)
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
var _ = fmt.Errorf
|
||||
var _ = math.Inf
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the proto package it is being compiled against.
|
||||
// A compilation error at this line likely means your copy of the
|
||||
// proto package needs to be updated.
|
||||
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
|
||||
|
||||
// The request message containing the process name.
|
||||
type ResizePartitionRequest struct {
|
||||
Number int32 `protobuf:"varint,1,opt,name=number,proto3" json:"number,omitempty"`
|
||||
Size int64 `protobuf:"varint,2,opt,name=size,proto3" json:"size,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *ResizePartitionRequest) Reset() { *m = ResizePartitionRequest{} }
|
||||
func (m *ResizePartitionRequest) String() string { return proto.CompactTextString(m) }
|
||||
func (*ResizePartitionRequest) ProtoMessage() {}
|
||||
func (*ResizePartitionRequest) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_00212fb1f9d3bf1c, []int{0}
|
||||
}
|
||||
|
||||
func (m *ResizePartitionRequest) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_ResizePartitionRequest.Unmarshal(m, b)
|
||||
}
|
||||
func (m *ResizePartitionRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_ResizePartitionRequest.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *ResizePartitionRequest) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_ResizePartitionRequest.Merge(m, src)
|
||||
}
|
||||
func (m *ResizePartitionRequest) XXX_Size() int {
|
||||
return xxx_messageInfo_ResizePartitionRequest.Size(m)
|
||||
}
|
||||
func (m *ResizePartitionRequest) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_ResizePartitionRequest.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_ResizePartitionRequest proto.InternalMessageInfo
|
||||
|
||||
func (m *ResizePartitionRequest) GetNumber() int32 {
|
||||
if m != nil {
|
||||
return m.Number
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (m *ResizePartitionRequest) GetSize() int64 {
|
||||
if m != nil {
|
||||
return m.Size
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterType((*ResizePartitionRequest)(nil), "proto.ResizePartitionRequest")
|
||||
}
|
||||
|
||||
func init() { proto.RegisterFile("api.proto", fileDescriptor_00212fb1f9d3bf1c) }
|
||||
|
||||
var fileDescriptor_00212fb1f9d3bf1c = []byte{
|
||||
// 163 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x4c, 0x2c, 0xc8, 0xd4,
|
||||
0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x05, 0x53, 0x52, 0xd2, 0xe9, 0xf9, 0xf9, 0xe9, 0x39,
|
||||
0xa9, 0xfa, 0x60, 0x5e, 0x52, 0x69, 0x9a, 0x7e, 0x6a, 0x6e, 0x41, 0x49, 0x25, 0x44, 0x8d, 0x92,
|
||||
0x0b, 0x97, 0x58, 0x50, 0x6a, 0x71, 0x66, 0x55, 0x6a, 0x40, 0x62, 0x51, 0x49, 0x66, 0x49, 0x66,
|
||||
0x7e, 0x5e, 0x50, 0x6a, 0x61, 0x69, 0x6a, 0x71, 0x89, 0x90, 0x18, 0x17, 0x5b, 0x5e, 0x69, 0x6e,
|
||||
0x52, 0x6a, 0x91, 0x04, 0xa3, 0x02, 0xa3, 0x06, 0x6b, 0x10, 0x94, 0x27, 0x24, 0xc4, 0xc5, 0x02,
|
||||
0x52, 0x2f, 0xc1, 0xa4, 0xc0, 0xa8, 0xc1, 0x1c, 0x04, 0x66, 0x1b, 0x79, 0x73, 0xb1, 0x39, 0xe5,
|
||||
0xe4, 0x27, 0x67, 0xa7, 0x08, 0x39, 0x72, 0xb1, 0x41, 0xcc, 0x13, 0x92, 0x85, 0xd8, 0xa0, 0x87,
|
||||
0xdd, 0x78, 0x29, 0x31, 0x3d, 0x88, 0xb3, 0xf4, 0x60, 0xce, 0xd2, 0x73, 0x05, 0x39, 0x4b, 0x89,
|
||||
0x21, 0x89, 0x0d, 0x2c, 0x62, 0x0c, 0x08, 0x00, 0x00, 0xff, 0xff, 0xa6, 0xfc, 0x26, 0x27, 0xca,
|
||||
0x00, 0x00, 0x00,
|
||||
}
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ context.Context
|
||||
var _ grpc.ClientConn
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
const _ = grpc.SupportPackageIsVersion4
|
||||
|
||||
// BlockdClient is the client API for Blockd service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
|
||||
type BlockdClient interface {
|
||||
Resize(ctx context.Context, in *ResizePartitionRequest, opts ...grpc.CallOption) (*empty.Empty, error)
|
||||
}
|
||||
|
||||
type blockdClient struct {
|
||||
cc *grpc.ClientConn
|
||||
}
|
||||
|
||||
func NewBlockdClient(cc *grpc.ClientConn) BlockdClient {
|
||||
return &blockdClient{cc}
|
||||
}
|
||||
|
||||
func (c *blockdClient) Resize(ctx context.Context, in *ResizePartitionRequest, opts ...grpc.CallOption) (*empty.Empty, error) {
|
||||
out := new(empty.Empty)
|
||||
err := c.cc.Invoke(ctx, "/proto.Blockd/Resize", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// BlockdServer is the server API for Blockd service.
|
||||
type BlockdServer interface {
|
||||
Resize(context.Context, *ResizePartitionRequest) (*empty.Empty, error)
|
||||
}
|
||||
|
||||
func RegisterBlockdServer(s *grpc.Server, srv BlockdServer) {
|
||||
s.RegisterService(&_Blockd_serviceDesc, srv)
|
||||
}
|
||||
|
||||
func _Blockd_Resize_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ResizePartitionRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(BlockdServer).Resize(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/proto.Blockd/Resize",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(BlockdServer).Resize(ctx, req.(*ResizePartitionRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
var _Blockd_serviceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "proto.Blockd",
|
||||
HandlerType: (*BlockdServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "Resize",
|
||||
Handler: _Blockd_Resize_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "api.proto",
|
||||
}
|
17
src/initramfs/cmd/blockd/proto/api.proto
Normal file
17
src/initramfs/cmd/blockd/proto/api.proto
Normal file
@ -0,0 +1,17 @@
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package proto;
|
||||
|
||||
import "google/protobuf/empty.proto";
|
||||
|
||||
// The Blockd service definition.
|
||||
service Blockd {
|
||||
rpc Resize(ResizePartitionRequest) returns (google.protobuf.Empty) {}
|
||||
}
|
||||
|
||||
// The request message containing the process name.
|
||||
message ResizePartitionRequest {
|
||||
int32 number = 1;
|
||||
int64 size = 2;
|
||||
}
|
@ -104,6 +104,7 @@ func root() (err error) {
|
||||
&services.Containerd{},
|
||||
&services.CRT{},
|
||||
&services.OSD{},
|
||||
&services.Blockd{},
|
||||
&services.Kubelet{},
|
||||
&services.Kubeadm{},
|
||||
)
|
||||
|
27
src/initramfs/cmd/init/pkg/fs/xfs/util/util.go
Normal file
27
src/initramfs/cmd/init/pkg/fs/xfs/util/util.go
Normal file
@ -0,0 +1,27 @@
|
||||
package util
|
||||
|
||||
import "C"
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
func PartNo(partname string) string {
|
||||
if strings.HasPrefix(partname, "/dev/nvme") {
|
||||
idx := strings.Index(partname, "p")
|
||||
return partname[idx+1:]
|
||||
} else if strings.HasPrefix(partname, "/dev/sd") || strings.HasPrefix(partname, "/dev/hd") {
|
||||
return strings.TrimLeft(partname, "/abcdefghijklmnopqrstuvwxyz")
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func DevnameFromPartname(partname, partno string) string {
|
||||
if strings.HasPrefix(partname, "/dev/nvme") {
|
||||
return strings.TrimRight(partname, "p"+partno)
|
||||
} else if strings.HasPrefix(partname, "/dev/sd") || strings.HasPrefix(partname, "/dev/hd") {
|
||||
return strings.TrimRight(partname, partno)
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
102
src/initramfs/cmd/init/pkg/fs/xfs/util/util_test.go
Normal file
102
src/initramfs/cmd/init/pkg/fs/xfs/util/util_test.go
Normal file
@ -0,0 +1,102 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_PartNo(t *testing.T) {
|
||||
type args struct {
|
||||
devname string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "hda1",
|
||||
args: args{
|
||||
devname: "/dev/hda1",
|
||||
},
|
||||
want: "1",
|
||||
},
|
||||
{
|
||||
name: "hda10",
|
||||
args: args{
|
||||
devname: "/dev/hda10",
|
||||
},
|
||||
want: "10",
|
||||
},
|
||||
{
|
||||
name: "sda1",
|
||||
args: args{
|
||||
devname: "/dev/sda1",
|
||||
},
|
||||
want: "1",
|
||||
},
|
||||
{
|
||||
name: "sda10",
|
||||
args: args{
|
||||
devname: "/dev/sda10",
|
||||
},
|
||||
want: "10",
|
||||
},
|
||||
{
|
||||
name: "nvme1n2p2",
|
||||
args: args{
|
||||
devname: "/dev/nvme1n2p2",
|
||||
},
|
||||
want: "2",
|
||||
},
|
||||
{
|
||||
name: "nvme1n2p11",
|
||||
args: args{
|
||||
devname: "/dev/nvme1n2p11",
|
||||
},
|
||||
want: "11",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := PartNo(tt.args.devname); got != tt.want {
|
||||
t.Errorf("PartNo() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_DevnameFromPartname(t *testing.T) {
|
||||
type args struct {
|
||||
devname string
|
||||
partno string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "hda1",
|
||||
args: args{
|
||||
devname: "/dev/hda1",
|
||||
partno: PartNo("/dev/hda1"),
|
||||
},
|
||||
want: "/dev/hda",
|
||||
},
|
||||
{
|
||||
name: "nvme1n2p11",
|
||||
args: args{
|
||||
devname: "/dev/nvme1n2p11",
|
||||
partno: PartNo("/dev/nvme1n2p11"),
|
||||
},
|
||||
want: "/dev/nvme1n2",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := DevnameFromPartname(tt.args.devname, tt.args.partno); got != tt.want {
|
||||
t.Errorf("DevnameFromPartname() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
25
src/initramfs/cmd/init/pkg/fs/xfs/xfs.go
Normal file
25
src/initramfs/cmd/init/pkg/fs/xfs/xfs.go
Normal file
@ -0,0 +1,25 @@
|
||||
// Package xfs provides an interface to xfsprogs.
|
||||
package xfs
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
// GrowFS expands an XFS filesystem to the maximum possible. The partition
|
||||
// MUST be mounted, or this will fail.
|
||||
func GrowFS(partname string) error {
|
||||
return cmd("xfs_growfs", "-d", partname)
|
||||
}
|
||||
|
||||
func cmd(name string, arg ...string) error {
|
||||
cmd := exec.Command(name, arg...)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stdout
|
||||
err := cmd.Start()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return cmd.Wait()
|
||||
}
|
@ -6,10 +6,14 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/autonomy/dianemo/src/initramfs/cmd/init/pkg/constants"
|
||||
"github.com/autonomy/dianemo/src/initramfs/cmd/init/pkg/fs/xfs"
|
||||
"github.com/autonomy/dianemo/src/initramfs/cmd/init/pkg/mount/blkid"
|
||||
"github.com/autonomy/dianemo/src/initramfs/pkg/blockdevice"
|
||||
gptpartition "github.com/autonomy/dianemo/src/initramfs/pkg/blockdevice/table/gpt/partition"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
@ -62,7 +66,11 @@ func Init(s string) (err error) {
|
||||
if err = mountSpecialDevices(); err != nil {
|
||||
return
|
||||
}
|
||||
if err = mountBlockDevices(s); err != nil {
|
||||
blockdevices, err := probe()
|
||||
if err != nil {
|
||||
return fmt.Errorf("probe block devices: %s", err.Error())
|
||||
}
|
||||
if err = mountBlockDevices(blockdevices, s); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@ -168,12 +176,56 @@ func mountSpecialDevices() (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
func mountBlockDevices(s string) (err error) {
|
||||
probed, err := probe()
|
||||
if err != nil {
|
||||
return fmt.Errorf("probe block devices: %s", err.Error())
|
||||
// nolint: gocyclo
|
||||
func fixDataPartition(blockdevices []*BlockDevice) error {
|
||||
for _, b := range blockdevices {
|
||||
if b.LABEL == constants.DataPartitionLabel {
|
||||
devname := devnameFromPartname(b.dev)
|
||||
|
||||
bd, err := blockdevice.Open(devname)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// nolint: errcheck
|
||||
defer bd.Close()
|
||||
|
||||
pt, err := bd.PartitionTable()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := pt.Read(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := pt.Repair(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, partition := range pt.Partitions() {
|
||||
if partition.(*gptpartition.Partition).Name == constants.DataPartitionLabel {
|
||||
if err := pt.Resize(partition); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Rereading the partition table requires that all partitions be unmounted
|
||||
// or it will fail with EBUSY.
|
||||
if err := bd.RereadPartitionTable(devname); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, b := range probed {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func mountBlockDevices(blockdevices []*BlockDevice, s string) (err error) {
|
||||
if err = fixDataPartition(blockdevices); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, b := range blockdevices {
|
||||
mountpoint := &Point{
|
||||
source: b.dev,
|
||||
fstype: b.TYPE,
|
||||
@ -196,6 +248,13 @@ func mountBlockDevices(s string) (err error) {
|
||||
return fmt.Errorf("mount %s: %s", mountpoint.target, err.Error())
|
||||
}
|
||||
|
||||
if b.LABEL == constants.DataPartitionLabel {
|
||||
// The XFS partition MUST be mounted, or this will fail.
|
||||
if err = xfs.GrowFS(mountpoint.target); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
instance.blockdevices[b.LABEL] = mountpoint
|
||||
}
|
||||
|
||||
@ -258,3 +317,25 @@ func probeDevice(devname string) (*BlockDevice, error) {
|
||||
LABEL: LABEL,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func partNo(partname string) string {
|
||||
if strings.HasPrefix(partname, "/dev/nvme") {
|
||||
idx := strings.Index(partname, "p")
|
||||
return partname[idx+1:]
|
||||
} else if strings.HasPrefix(partname, "/dev/sd") || strings.HasPrefix(partname, "/dev/hd") {
|
||||
return strings.TrimLeft(partname, "/abcdefghijklmnopqrstuvwxyz")
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func devnameFromPartname(partname string) string {
|
||||
partno := partNo(partname)
|
||||
if strings.HasPrefix(partname, "/dev/nvme") {
|
||||
return strings.TrimRight(partname, "p"+partno)
|
||||
} else if strings.HasPrefix(partname, "/dev/sd") || strings.HasPrefix(partname, "/dev/hd") {
|
||||
return strings.TrimRight(partname, partno)
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
78
src/initramfs/cmd/init/pkg/system/services/blockd.go
Normal file
78
src/initramfs/cmd/init/pkg/system/services/blockd.go
Normal file
@ -0,0 +1,78 @@
|
||||
// nolint: dupl,golint
|
||||
package services
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/autonomy/dianemo/src/initramfs/cmd/init/pkg/constants"
|
||||
"github.com/autonomy/dianemo/src/initramfs/cmd/init/pkg/system/conditions"
|
||||
"github.com/autonomy/dianemo/src/initramfs/cmd/init/pkg/system/runner"
|
||||
"github.com/autonomy/dianemo/src/initramfs/cmd/init/pkg/system/runner/containerd"
|
||||
"github.com/autonomy/dianemo/src/initramfs/pkg/userdata"
|
||||
"github.com/autonomy/dianemo/src/initramfs/pkg/version"
|
||||
"github.com/containerd/containerd/oci"
|
||||
specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||
)
|
||||
|
||||
// Blockd implements the Service interface. It serves as the concrete type with
|
||||
// the required methods.
|
||||
type Blockd struct{}
|
||||
|
||||
// ID implements the Service interface.
|
||||
func (t *Blockd) ID(data *userdata.UserData) string {
|
||||
return "blockd"
|
||||
}
|
||||
|
||||
// PreFunc implements the Service interface.
|
||||
func (t *Blockd) PreFunc(data *userdata.UserData) error {
|
||||
return os.Mkdir("/run/blockd", os.ModeDir)
|
||||
}
|
||||
|
||||
// PostFunc implements the Service interface.
|
||||
func (t *Blockd) PostFunc(data *userdata.UserData) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ConditionFunc implements the Service interface.
|
||||
func (t *Blockd) ConditionFunc(data *userdata.UserData) conditions.ConditionFunc {
|
||||
return conditions.None()
|
||||
}
|
||||
|
||||
func (t *Blockd) Start(data *userdata.UserData) error {
|
||||
// Set the image.
|
||||
var image string
|
||||
if data.Services.Blockd != nil && data.Services.Blockd.Image != "" {
|
||||
image = data.Services.Blockd.Image
|
||||
} else {
|
||||
image = "docker.io/autonomy/blockd:" + version.SHA
|
||||
}
|
||||
|
||||
// Set the process arguments.
|
||||
args := runner.Args{
|
||||
ID: t.ID(data),
|
||||
ProcessArgs: []string{"/blockd", "--userdata=" + constants.UserDataPath},
|
||||
}
|
||||
if data.Services.Kubeadm.Init == nil {
|
||||
args.ProcessArgs = append(args.ProcessArgs, "--generate=true")
|
||||
}
|
||||
|
||||
// Set the mounts.
|
||||
mounts := []specs.Mount{
|
||||
{Type: "bind", Destination: "/dev", Source: "/dev", Options: []string{"rbind", "rshared", "rw"}},
|
||||
{Type: "bind", Destination: constants.UserDataPath, Source: constants.UserDataPath, Options: []string{"rbind", "ro"}},
|
||||
{Type: "bind", Destination: "/var/etc/kubernetes", Source: "/var/etc/kubernetes", Options: []string{"bind", "rw"}},
|
||||
{Type: "bind", Destination: "/run/factory", Source: "/run/blockd", Options: []string{"rbind", "rshared", "rw"}},
|
||||
}
|
||||
|
||||
r := containerd.Containerd{}
|
||||
|
||||
return r.Run(
|
||||
data,
|
||||
args,
|
||||
runner.WithContainerImage(image),
|
||||
runner.WithOCISpecOpts(
|
||||
containerd.WithMemoryLimit(int64(1000000*512)),
|
||||
oci.WithMounts(mounts),
|
||||
),
|
||||
)
|
||||
}
|
@ -4,10 +4,9 @@ import (
|
||||
"flag"
|
||||
"log"
|
||||
|
||||
"github.com/autonomy/dianemo/src/initramfs/cmd/osd/pkg/gen"
|
||||
"github.com/autonomy/dianemo/src/initramfs/cmd/osd/pkg/reg"
|
||||
"github.com/autonomy/dianemo/src/initramfs/pkg/grpc/factory"
|
||||
"github.com/autonomy/dianemo/src/initramfs/pkg/grpc/middleware/auth/basic"
|
||||
"github.com/autonomy/dianemo/src/initramfs/pkg/grpc/gen"
|
||||
"github.com/autonomy/dianemo/src/initramfs/pkg/grpc/tls"
|
||||
"github.com/autonomy/dianemo/src/initramfs/pkg/userdata"
|
||||
"google.golang.org/grpc"
|
||||
@ -15,48 +14,33 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
dataPath *string
|
||||
generate *bool
|
||||
port *int
|
||||
rotPort *int
|
||||
dataPath *string
|
||||
generate *bool
|
||||
port *int
|
||||
trustdPort *int
|
||||
)
|
||||
|
||||
func init() {
|
||||
log.SetFlags(log.Lshortfile | log.Ldate | log.Lmicroseconds | log.Ltime)
|
||||
dataPath = flag.String("userdata", "", "the path to the user data")
|
||||
port = flag.Int("port", 50000, "the port to listen on")
|
||||
rotPort = flag.Int("rot-port", 50001, "the port to listen on")
|
||||
trustdPort = flag.Int("trustd-port", 50001, "the trustd port")
|
||||
generate = flag.Bool("generate", false, "generate the TLS certificate using one of the Root of Trusts")
|
||||
flag.Parse()
|
||||
}
|
||||
|
||||
func main() {
|
||||
var err error
|
||||
|
||||
data, err := userdata.Open(*dataPath)
|
||||
if err != nil {
|
||||
log.Fatalf("open user data: %v", err)
|
||||
}
|
||||
|
||||
if *generate {
|
||||
if len(data.Services.Trustd.Endpoints) == 0 {
|
||||
log.Fatalf("at least one root of trust endpoint is required")
|
||||
}
|
||||
|
||||
creds := basic.NewCredentials(
|
||||
data.Security.OS.CA.Crt,
|
||||
data.Services.Trustd.Username,
|
||||
data.Services.Trustd.Password,
|
||||
)
|
||||
|
||||
// TODO: In the case of failure, attempt to generate the identity from
|
||||
// another RoT.
|
||||
var conn *grpc.ClientConn
|
||||
conn, err = basic.NewConnection(data.Services.Trustd.Endpoints[0], *rotPort, creds)
|
||||
var generator *gen.Generator
|
||||
generator, err = gen.NewGenerator(data, *trustdPort)
|
||||
if err != nil {
|
||||
return
|
||||
log.Fatal(err)
|
||||
}
|
||||
generator := gen.NewGenerator(conn)
|
||||
if err = generator.Identity(data.Security); err != nil {
|
||||
log.Fatalf("generate identity: %v", err)
|
||||
}
|
||||
@ -67,6 +51,7 @@ func main() {
|
||||
log.Fatalf("credentials: %v", err)
|
||||
}
|
||||
|
||||
log.Println("Starting osd")
|
||||
err = factory.Listen(
|
||||
®.Registrator{Data: data},
|
||||
factory.Port(*port),
|
||||
|
@ -34,6 +34,7 @@ require (
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c // indirect
|
||||
github.com/google/go-cmp v0.2.0 // indirect
|
||||
github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf // indirect
|
||||
github.com/google/uuid v1.0.0 // indirect
|
||||
github.com/googleapis/gnostic v0.2.0 // indirect
|
||||
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 // indirect
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect
|
||||
@ -69,7 +70,7 @@ require (
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225
|
||||
golang.org/x/oauth2 v0.0.0-20181003184128-c57b0facaced // indirect
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f // indirect
|
||||
golang.org/x/sys v0.0.0-20180302081741-dd2ff4accc09
|
||||
golang.org/x/sys v0.0.0-20181019160139-8e24a49d80f8
|
||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2 // indirect
|
||||
google.golang.org/appengine v1.2.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b // indirect
|
||||
|
@ -64,6 +64,8 @@ github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf h1:+RRA9JqSOZFfKrOeqr2z77+8R2RKyh8PG66dcu1V0ck=
|
||||
github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
|
||||
github.com/google/uuid v1.0.0 h1:b4Gk+7WdP/d3HZH8EJsZpvV7EtDOgaZLtnaNGIu1adA=
|
||||
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/gnostic v0.2.0 h1:l6N3VoaVzTncYYW+9yOz2LJJammFZGBO13sqgEhpy9g=
|
||||
github.com/googleapis/gnostic v0.2.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
|
||||
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWetyv/0+wjACpqVH+eVULgEjkurDLq3goeM=
|
||||
@ -136,6 +138,8 @@ golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6Zh
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180302081741-dd2ff4accc09 h1:wNPZbZUOH0tyqngVRXeF2iQm19+ssqyebJTCFBvxsow=
|
||||
golang.org/x/sys v0.0.0-20180302081741-dd2ff4accc09/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181019160139-8e24a49d80f8 h1:R91KX5nmbbvEd7w370cbVzKC+EzCTGqZq63Zad5IcLM=
|
||||
golang.org/x/sys v0.0.0-20181019160139-8e24a49d80f8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2 h1:+DCIGbF/swA92ohVg0//6X2IVY3KZs6p9mix0ziNYJM=
|
||||
|
69
src/initramfs/pkg/blockdevice/blockdevice.go
Normal file
69
src/initramfs/pkg/blockdevice/blockdevice.go
Normal file
@ -0,0 +1,69 @@
|
||||
// Package blockdevice provides a library for working with block devices.
|
||||
package blockdevice
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/autonomy/dianemo/src/initramfs/pkg/blockdevice/table"
|
||||
"github.com/autonomy/dianemo/src/initramfs/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.
|
||||
func Open(devname string) (*BlockDevice, error) {
|
||||
f, err := os.OpenFile(devname, os.O_RDWR, os.ModeDevice)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// TODO: Dynamically detect MBR/GPT.
|
||||
// TODO: Use BLKGETSIZE ioctl to get the size.
|
||||
// TODO: Use BLKPBSZGET ioctl to get the physical sector size.
|
||||
// TODO: Use BLKSSZGET ioctl to get the logical sector size.
|
||||
// and pass them into gpt as options.
|
||||
bd := &BlockDevice{
|
||||
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() (table.PartitionTable, error) {
|
||||
if bd.table == nil {
|
||||
return nil, fmt.Errorf("missing partition table")
|
||||
}
|
||||
|
||||
return bd.table, nil
|
||||
}
|
||||
|
||||
// RereadPartitionTable invokes the BLKRRPART ioctl have the kernel read the
|
||||
// partition table.
|
||||
func (bd *BlockDevice) RereadPartitionTable(devname string) error {
|
||||
f, err := os.Open(devname)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
unix.Sync()
|
||||
if _, _, ret := unix.Syscall(unix.SYS_IOCTL, f.Fd(), unix.BLKRRPART, 0); ret != 0 {
|
||||
return fmt.Errorf("re-read partition table: %v", ret)
|
||||
}
|
||||
if err := f.Sync(); err != nil {
|
||||
return err
|
||||
}
|
||||
unix.Sync()
|
||||
|
||||
return nil
|
||||
}
|
44
src/initramfs/pkg/blockdevice/pkg/lba/lba.go
Normal file
44
src/initramfs/pkg/blockdevice/pkg/lba/lba.go
Normal file
@ -0,0 +1,44 @@
|
||||
// Package lba provides a library for working with Logical Block Addresses.
|
||||
package lba
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Range represents a range of Logical Block Addresses.
|
||||
type Range struct {
|
||||
Start uint64
|
||||
End uint64
|
||||
}
|
||||
|
||||
// LogicalBlockAddresser represents Logical Block Addressing.
|
||||
type LogicalBlockAddresser struct {
|
||||
PhysicalBlockSize int
|
||||
LogicalBlockSize int
|
||||
}
|
||||
|
||||
// Make returns a slice from a source slice in the the specified range inclusively.
|
||||
func (lba *LogicalBlockAddresser) Make(size int) []byte {
|
||||
return make([]byte, lba.PhysicalBlockSize*size)
|
||||
}
|
||||
|
||||
// Copy copies from src to dst in the specified range.
|
||||
func (lba *LogicalBlockAddresser) Copy(dst []byte, src []byte, rng Range) (int, error) {
|
||||
size := uint64(lba.PhysicalBlockSize)
|
||||
n := copy(dst[size*rng.Start:size*rng.End], src)
|
||||
|
||||
if n != len(src) {
|
||||
return -1, fmt.Errorf("expected to write %d elements, wrote %d", len(src), n)
|
||||
}
|
||||
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// From returns a slice from a source slice in the the specified range inclusively.
|
||||
func (lba *LogicalBlockAddresser) From(src []byte, rng Range) ([]byte, error) {
|
||||
size := uint64(lba.PhysicalBlockSize)
|
||||
if uint64(len(src)) < size+size*rng.End {
|
||||
return nil, fmt.Errorf("cannot read LBA range (start: %d, end %d), source too small", rng.Start, rng.End)
|
||||
}
|
||||
return src[size*rng.Start : size+size*rng.End], nil
|
||||
}
|
68
src/initramfs/pkg/blockdevice/pkg/serde/serde.go
Normal file
68
src/initramfs/pkg/blockdevice/pkg/serde/serde.go
Normal file
@ -0,0 +1,68 @@
|
||||
package serde
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Serde describes a serializer/deserializer.
|
||||
type Serde interface {
|
||||
Fields() []*Field
|
||||
}
|
||||
|
||||
// FieldSerializerFunc is the func signature for serialization.
|
||||
type FieldSerializerFunc = func([]byte, interface{}) error
|
||||
|
||||
// FieldDeserializerFunc is the func signature for deserialization.
|
||||
type FieldDeserializerFunc = func(uint32, uint32, []byte, interface{}) ([]byte, error)
|
||||
|
||||
// Field represents a field in a datastructure.
|
||||
type Field struct {
|
||||
Offset uint32
|
||||
Length uint32
|
||||
Contents *[]byte
|
||||
SerializerFunc FieldSerializerFunc
|
||||
DeserializerFunc FieldDeserializerFunc
|
||||
}
|
||||
|
||||
// Ser serializes a field.
|
||||
func Ser(t Serde, data []byte, offset uint32, opts interface{}) error {
|
||||
for _, field := range t.Fields() {
|
||||
if field.SerializerFunc == nil {
|
||||
return fmt.Errorf("the field is missing the serializer function")
|
||||
}
|
||||
|
||||
if err := field.SerializerFunc(data[field.start(offset):field.end(offset)], opts); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// De deserializes a field.
|
||||
func De(t Serde, data []byte, offset uint32, opts interface{}) error {
|
||||
for _, field := range t.Fields() {
|
||||
if field.DeserializerFunc == nil {
|
||||
return fmt.Errorf("the field is missing the deserializer function")
|
||||
}
|
||||
|
||||
contents, err := field.DeserializerFunc(field.Offset, field.Length, data, opts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if n := copy(data[field.start(offset):field.end(offset)], contents); uint32(n) != field.Length {
|
||||
return fmt.Errorf("expected to write %d elements, wrote %d", field.Length, n)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (fld *Field) start(offset uint32) uint32 {
|
||||
return fld.Offset + offset
|
||||
}
|
||||
|
||||
func (fld *Field) end(offset uint32) uint32 {
|
||||
return fld.Offset + fld.Length + offset
|
||||
}
|
416
src/initramfs/pkg/blockdevice/table/gpt/gpt.go
Normal file
416
src/initramfs/pkg/blockdevice/table/gpt/gpt.go
Normal file
@ -0,0 +1,416 @@
|
||||
// Package gpt provides a library for working with GPT partitions.
|
||||
package gpt
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"github.com/autonomy/dianemo/src/initramfs/pkg/blockdevice/pkg/lba"
|
||||
"github.com/autonomy/dianemo/src/initramfs/pkg/blockdevice/pkg/serde"
|
||||
"github.com/autonomy/dianemo/src/initramfs/pkg/blockdevice/table"
|
||||
"github.com/autonomy/dianemo/src/initramfs/pkg/blockdevice/table/gpt/header"
|
||||
"github.com/autonomy/dianemo/src/initramfs/pkg/blockdevice/table/gpt/partition"
|
||||
"github.com/google/uuid"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// GPT represents the GUID partition table.
|
||||
type GPT struct {
|
||||
table table.Table
|
||||
header *header.Header
|
||||
partitions []table.Partition
|
||||
lba *lba.LogicalBlockAddresser
|
||||
|
||||
devname string
|
||||
f *os.File
|
||||
}
|
||||
|
||||
// NewGPT initializes and returns a GUID partition table.
|
||||
func NewGPT(devname string, f *os.File, setters ...interface{}) *GPT {
|
||||
opts := NewDefaultOptions(setters...)
|
||||
|
||||
lba := &lba.LogicalBlockAddresser{
|
||||
PhysicalBlockSize: opts.PhysicalBlockSize,
|
||||
LogicalBlockSize: opts.LogicalBlockSize,
|
||||
}
|
||||
|
||||
return &GPT{
|
||||
lba: lba,
|
||||
devname: devname,
|
||||
f: f,
|
||||
}
|
||||
}
|
||||
|
||||
// Bytes returns the partition table as a byte slice.
|
||||
func (gpt *GPT) Bytes() []byte {
|
||||
return gpt.table
|
||||
}
|
||||
|
||||
// Type returns the partition type.
|
||||
func (gpt *GPT) Type() table.Type {
|
||||
return table.GPT
|
||||
}
|
||||
|
||||
// Header returns the header.
|
||||
func (gpt *GPT) Header() table.Header {
|
||||
return gpt.header
|
||||
}
|
||||
|
||||
// Partitions returns the partitions.
|
||||
func (gpt *GPT) Partitions() []table.Partition {
|
||||
return gpt.partitions
|
||||
}
|
||||
|
||||
// Read performs reads the partition table.
|
||||
func (gpt *GPT) Read() error {
|
||||
primaryTable, err := gpt.readPrimary()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
serializedHeader, err := gpt.serializeHeader(primaryTable)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
serializedPartitions, err := gpt.serializePartitions(serializedHeader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
gpt.table = primaryTable
|
||||
gpt.header = serializedHeader
|
||||
gpt.partitions = serializedPartitions
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Write writes the partition table to disk.
|
||||
func (gpt *GPT) Write() error {
|
||||
partitions, err := gpt.deserializePartitions()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := gpt.writePrimary(partitions); err != nil {
|
||||
return fmt.Errorf("failed to write primary table: %v", err)
|
||||
}
|
||||
|
||||
if err := gpt.writeSecondary(partitions); err != nil {
|
||||
return fmt.Errorf("failed to write primary table: %v", err)
|
||||
}
|
||||
|
||||
return gpt.Read()
|
||||
}
|
||||
|
||||
// Write the primary table.
|
||||
func (gpt *GPT) writePrimary(partitions []byte) error {
|
||||
header, err := gpt.deserializeHeader(partitions)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
table, err := gpt.newTable(header, partitions, lba.Range{Start: 0, End: 1}, lba.Range{Start: 1, End: 33})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
written, err := gpt.f.WriteAt(table, int64(gpt.PhysicalBlockSize()))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if written != len(table) {
|
||||
return fmt.Errorf("expected a primary table write of %d bytes, got %d", len(table), written)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Write the secondary table.
|
||||
func (gpt *GPT) writeSecondary(partitions []byte) error {
|
||||
header, err := gpt.deserializeHeader(partitions, header.WithHeaderPrimary(false))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
table, err := gpt.newTable(header, partitions, lba.Range{Start: 32, End: 33}, lba.Range{Start: 0, End: 32})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
offset := int64((gpt.header.LastUsableLBA + 1))
|
||||
written, err := gpt.f.WriteAt(table, offset*int64(gpt.PhysicalBlockSize()))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if written != len(table) {
|
||||
return fmt.Errorf("expected a secondary table write of %d bytes, got %d", len(table), written)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Repair repairs the partition table.
|
||||
func (gpt *GPT) Repair() error {
|
||||
// Seek to the end to get the size.
|
||||
size, err := gpt.f.Seek(0, 2)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Reset and seek to the beginning.
|
||||
_, err = gpt.f.Seek(0, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
gpt.header.BackupLBA = uint64(size/int64(gpt.lba.PhysicalBlockSize) - 1)
|
||||
gpt.header.LastUsableLBA = gpt.header.BackupLBA - 33
|
||||
|
||||
return gpt.Write()
|
||||
}
|
||||
|
||||
// Add adds a partition.
|
||||
func (gpt *GPT) Add(size uint64, setters ...interface{}) (table.Partition, error) {
|
||||
opts := partition.NewDefaultOptions(setters...)
|
||||
|
||||
var start, end uint64
|
||||
if len(gpt.partitions) == 0 {
|
||||
start = gpt.header.FirstUsableLBA
|
||||
} else {
|
||||
previous := gpt.partitions[len(gpt.partitions)-1]
|
||||
start = previous.(*partition.Partition).LastLBA + 1
|
||||
}
|
||||
end = start + size/uint64(gpt.PhysicalBlockSize())
|
||||
|
||||
if end > gpt.header.LastUsableLBA {
|
||||
// TODO: This calculation is wrong, fix it.
|
||||
available := (gpt.header.LastUsableLBA - start) * uint64(gpt.PhysicalBlockSize())
|
||||
return nil, fmt.Errorf("requested partition size %d is too big, largest available is %d", size, available)
|
||||
}
|
||||
|
||||
uuid, err := uuid.NewUUID()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
partition := &partition.Partition{
|
||||
Type: opts.Type,
|
||||
ID: uuid,
|
||||
FirstLBA: start,
|
||||
LastLBA: end,
|
||||
// TODO: Flags should be an option.
|
||||
Flags: 0,
|
||||
Name: opts.Name,
|
||||
Number: int32(len(gpt.partitions) + 1),
|
||||
}
|
||||
|
||||
gpt.partitions = append(gpt.partitions, partition)
|
||||
|
||||
if err := gpt.Write(); err != nil {
|
||||
return nil, fmt.Errorf("failed to add partition: %v", err)
|
||||
}
|
||||
|
||||
if err := gpt.InformKernelOfAdd(gpt.devname, partition); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return partition, nil
|
||||
}
|
||||
|
||||
// Resize resizes a partition.
|
||||
// TODO: Verify that we can indeed grow this partition safely.
|
||||
func (gpt *GPT) Resize(p table.Partition) error {
|
||||
partition, ok := p.(*partition.Partition)
|
||||
if !ok {
|
||||
return fmt.Errorf("partition is not a GUID partition table partition")
|
||||
}
|
||||
|
||||
// TODO: This should be a parameter.
|
||||
partition.LastLBA = gpt.header.LastUsableLBA
|
||||
|
||||
index := partition.Number - 1
|
||||
if len(gpt.partitions) < int(index) {
|
||||
return fmt.Errorf("unknown partition %d, only %d available", partition.Number, len(gpt.partitions))
|
||||
}
|
||||
|
||||
gpt.partitions[index] = partition
|
||||
|
||||
if err := gpt.Write(); err != nil {
|
||||
return fmt.Errorf("failed to grow partitioin: %v", err)
|
||||
}
|
||||
|
||||
return gpt.InformKernelOfResize(gpt.devname, p)
|
||||
}
|
||||
|
||||
// Delete deletes a partition.
|
||||
func (gpt *GPT) Delete(partition table.Partition) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// PhysicalBlockSize returns the physical block size.
|
||||
func (gpt *GPT) PhysicalBlockSize() int {
|
||||
return gpt.lba.PhysicalBlockSize
|
||||
}
|
||||
|
||||
// TODO: Rename this func, it doesn't deserialize anything.
|
||||
func (gpt *GPT) readPrimary() ([]byte, error) {
|
||||
// LBA 34 is the first usable sector on the disk.
|
||||
table := gpt.lba.Make(34)
|
||||
read, err := gpt.f.ReadAt(table, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if read != len(table) {
|
||||
return nil, fmt.Errorf("expected a read of %d bytes, got %d", len(table), read)
|
||||
}
|
||||
|
||||
return table, nil
|
||||
}
|
||||
|
||||
func (gpt *GPT) newTable(header, partitions []byte, headerRange, paritionsRange lba.Range) ([]byte, error) {
|
||||
table := gpt.lba.Make(33)
|
||||
|
||||
if _, err := gpt.lba.Copy(table, header, headerRange); err != nil {
|
||||
return nil, fmt.Errorf("failed to copy header data: %v", err)
|
||||
}
|
||||
|
||||
if _, err := gpt.lba.Copy(table, partitions, paritionsRange); err != nil {
|
||||
return nil, fmt.Errorf("failed to copy partition data: %v", err)
|
||||
}
|
||||
|
||||
return table, nil
|
||||
}
|
||||
|
||||
func (gpt *GPT) serializeHeader(table []byte) (*header.Header, error) {
|
||||
// GPT header is in LBA 1.
|
||||
data, err := gpt.lba.From(table, lba.Range{Start: 1, End: 1})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
hdr := header.NewHeader(data, gpt.lba)
|
||||
|
||||
opts := header.NewDefaultOptions(header.WithHeaderTable(table))
|
||||
if err := serde.Ser(hdr, hdr.Bytes(), 0, opts); err != nil {
|
||||
return nil, fmt.Errorf("failed to serialize the header: %v", err)
|
||||
}
|
||||
|
||||
return hdr, nil
|
||||
}
|
||||
|
||||
func (gpt *GPT) deserializeHeader(partitions []byte, setters ...interface{}) ([]byte, error) {
|
||||
data := gpt.lba.Make(1)
|
||||
setters = append(setters, header.WithHeaderArrayBytes(partitions))
|
||||
opts := header.NewDefaultOptions(setters...)
|
||||
if err := serde.De(gpt.header, data, 0, opts); err != nil {
|
||||
return nil, fmt.Errorf("failed to deserialize the header: %v", err)
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func (gpt *GPT) serializePartitions(header *header.Header) ([]table.Partition, error) {
|
||||
partitions := make([]table.Partition, 0, header.NumberOfPartitionEntries)
|
||||
|
||||
for i := uint32(0); i < header.NumberOfPartitionEntries; i++ {
|
||||
offset := i * header.PartitionEntrySize
|
||||
data := header.ArrayBytes()[offset : offset+header.PartitionEntrySize]
|
||||
prt := partition.NewPartition(data)
|
||||
|
||||
if err := serde.Ser(prt, header.ArrayBytes(), offset, nil); err != nil {
|
||||
return nil, fmt.Errorf("failed to serialize the partitions: %v", err)
|
||||
}
|
||||
|
||||
// The first LBA of the partition cannot start before the first usable
|
||||
// LBA specified in the header.
|
||||
if prt.FirstLBA >= header.FirstUsableLBA {
|
||||
prt.Number = int32(i) + 1
|
||||
partitions = append(partitions, prt)
|
||||
}
|
||||
}
|
||||
|
||||
return partitions, nil
|
||||
}
|
||||
|
||||
func (gpt *GPT) deserializePartitions() ([]byte, error) {
|
||||
// TODO: Should this be a method on the Header struct?
|
||||
data := make([]byte, gpt.header.NumberOfPartitionEntries*gpt.header.PartitionEntrySize)
|
||||
|
||||
for j, p := range gpt.partitions {
|
||||
i := uint32(j)
|
||||
partition, ok := p.(*partition.Partition)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("partition is not a GUID partition table partition")
|
||||
}
|
||||
if err := serde.De(partition, data, i*gpt.header.PartitionEntrySize, nil); err != nil {
|
||||
return nil, fmt.Errorf("failed to deserialize the partitions: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// InformKernelOfAdd invokes the BLKPG_ADD_PARTITION ioctl.
|
||||
func (gpt *GPT) InformKernelOfAdd(devname string, partition table.Partition) error {
|
||||
f, err := os.Open(devname)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// nolint: errcheck
|
||||
defer f.Close()
|
||||
|
||||
return inform(f.Fd(), partition, unix.BLKPG_ADD_PARTITION, int64(gpt.lba.PhysicalBlockSize))
|
||||
}
|
||||
|
||||
// InformKernelOfResize invokes the BLKPG_RESIZE_PARTITION ioctl.
|
||||
func (gpt *GPT) InformKernelOfResize(devname string, partition table.Partition) error {
|
||||
f, err := os.Open(devname)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// nolint: errcheck
|
||||
defer f.Close()
|
||||
|
||||
return inform(f.Fd(), partition, unix.BLKPG_RESIZE_PARTITION, int64(gpt.lba.PhysicalBlockSize))
|
||||
}
|
||||
|
||||
// InformKernelOfDelete invokes the BLKPG_DEL_PARTITION ioctl.
|
||||
func (gpt *GPT) InformKernelOfDelete(devname string, partition table.Partition) error {
|
||||
f, err := os.Open(devname)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// nolint: errcheck
|
||||
defer f.Close()
|
||||
|
||||
return inform(f.Fd(), partition, unix.BLKPG_DEL_PARTITION, int64(gpt.lba.PhysicalBlockSize))
|
||||
}
|
||||
|
||||
func inform(fd uintptr, partition table.Partition, op int32, blocksize int64) error {
|
||||
arg := &unix.BlkpgIoctlArg{
|
||||
Op: op,
|
||||
Data: (*byte)(unsafe.Pointer(&unix.BlkpgPartition{
|
||||
Start: partition.Start() * blocksize,
|
||||
Length: partition.Length() * blocksize,
|
||||
Pno: partition.No(),
|
||||
})),
|
||||
}
|
||||
|
||||
_, _, errno := syscall.Syscall(
|
||||
syscall.SYS_IOCTL,
|
||||
fd,
|
||||
unix.BLKPG,
|
||||
uintptr(unsafe.Pointer(arg)),
|
||||
)
|
||||
|
||||
if errno != 0 {
|
||||
return errno
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
49
src/initramfs/pkg/blockdevice/table/gpt/gpt_options.go
Normal file
49
src/initramfs/pkg/blockdevice/table/gpt/gpt_options.go
Normal file
@ -0,0 +1,49 @@
|
||||
package gpt
|
||||
|
||||
// Options is the functional options struct.
|
||||
type Options struct {
|
||||
PrimaryGPT bool
|
||||
PhysicalBlockSize int
|
||||
LogicalBlockSize int
|
||||
}
|
||||
|
||||
// Option is the functional option func.
|
||||
type Option func(*Options)
|
||||
|
||||
// WithPrimaryGPT sets the contents of offset 24 in the GPT header to the location of the primary header.
|
||||
func WithPrimaryGPT(o bool) Option {
|
||||
return func(args *Options) {
|
||||
args.PrimaryGPT = o
|
||||
}
|
||||
}
|
||||
|
||||
// WithPhysicalBlockSize sets the physical block size.
|
||||
func WithPhysicalBlockSize(o int) Option {
|
||||
return func(args *Options) {
|
||||
args.PhysicalBlockSize = o
|
||||
}
|
||||
}
|
||||
|
||||
// WithLogicalBlockSize sets the logical block size.
|
||||
func WithLogicalBlockSize(o int) Option {
|
||||
return func(args *Options) {
|
||||
args.LogicalBlockSize = o
|
||||
}
|
||||
}
|
||||
|
||||
// NewDefaultOptions initializes a Options struct with default values.
|
||||
func NewDefaultOptions(setters ...interface{}) *Options {
|
||||
opts := &Options{
|
||||
PrimaryGPT: true,
|
||||
PhysicalBlockSize: 512,
|
||||
LogicalBlockSize: 512,
|
||||
}
|
||||
|
||||
for _, setter := range setters {
|
||||
if s, ok := setter.(Option); ok {
|
||||
s(opts)
|
||||
}
|
||||
}
|
||||
|
||||
return opts
|
||||
}
|
418
src/initramfs/pkg/blockdevice/table/gpt/header/header.go
Normal file
418
src/initramfs/pkg/blockdevice/table/gpt/header/header.go
Normal file
@ -0,0 +1,418 @@
|
||||
// Package header provides a library for working with GPT headers.
|
||||
package header
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"hash/crc32"
|
||||
|
||||
"github.com/autonomy/dianemo/src/initramfs/pkg/blockdevice/pkg/lba"
|
||||
"github.com/autonomy/dianemo/src/initramfs/pkg/blockdevice/pkg/serde"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
const (
|
||||
// HeaderSize is the GUID partition table header size in bytes.
|
||||
HeaderSize = 92
|
||||
)
|
||||
|
||||
// Header represents a GUID partition table.
|
||||
type Header struct {
|
||||
data []byte
|
||||
array []byte
|
||||
|
||||
Signature string // 0
|
||||
Revision uint32 // 8
|
||||
Size uint32 // 12
|
||||
CRC uint32 // 16
|
||||
Reserved uint32 // 20
|
||||
CurrentLBA uint64 // 24
|
||||
BackupLBA uint64 // 32
|
||||
FirstUsableLBA uint64 // 40
|
||||
LastUsableLBA uint64 // 48
|
||||
GUUID uuid.UUID // 56
|
||||
PartitionEntriesStartLBA uint64 // 72
|
||||
NumberOfPartitionEntries uint32 // 80
|
||||
PartitionEntrySize uint32 // 84
|
||||
PartitionsArrayCRC uint32 // 88
|
||||
TrailingBytes []byte // 92
|
||||
|
||||
*lba.LogicalBlockAddresser
|
||||
}
|
||||
|
||||
// NewHeader inializes and returns a GUID partition table header.
|
||||
func NewHeader(data []byte, lba *lba.LogicalBlockAddresser) *Header {
|
||||
return &Header{
|
||||
data: data,
|
||||
LogicalBlockAddresser: lba,
|
||||
}
|
||||
}
|
||||
|
||||
// Bytes implements the table.Header interface.
|
||||
func (hdr *Header) Bytes() []byte {
|
||||
return hdr.data
|
||||
}
|
||||
|
||||
// ArrayBytes returns the GUID partition table partitions entries array as a byte slice.
|
||||
func (hdr *Header) ArrayBytes() []byte {
|
||||
return hdr.array
|
||||
}
|
||||
|
||||
// Fields impements the serde.Serde interface.
|
||||
// nolint: gocyclo
|
||||
func (hdr *Header) Fields() []*serde.Field {
|
||||
return []*serde.Field{
|
||||
// 8 bytes Signature ("EFI PART", 45h 46h 49h 20h 50h 41h 52h 54h or 0x5452415020494645ULL on little-endian machines)
|
||||
{
|
||||
Offset: 0,
|
||||
Length: 8,
|
||||
// Contents: []byte{0x45, 0x46, 0x49, 0x20, 0x50, 0x41, 0x52, 0x54},
|
||||
DeserializerFunc: func(offset, length uint32, new []byte, opts interface{}) ([]byte, error) {
|
||||
return []byte{0x45, 0x46, 0x49, 0x20, 0x50, 0x41, 0x52, 0x54}, nil
|
||||
},
|
||||
SerializerFunc: func(contents []byte, opts interface{}) error {
|
||||
signature := string(contents)
|
||||
if signature != "EFI PART" {
|
||||
return fmt.Errorf("expected signature of \"EFI PART\", got %q", signature)
|
||||
}
|
||||
|
||||
hdr.Signature = string(contents)
|
||||
|
||||
return nil
|
||||
},
|
||||
},
|
||||
// 4 bytes Revision (for GPT version 1.0 (through at least UEFI version 2.7 (May 2017)), the value is 00h 00h 01h 00h)
|
||||
{
|
||||
Offset: 8,
|
||||
Length: 4,
|
||||
// Contents: []byte{0x00, 0x00, 0x01, 0x00},
|
||||
DeserializerFunc: func(offset, length uint32, new []byte, opts interface{}) ([]byte, error) {
|
||||
data := make([]byte, length)
|
||||
binary.LittleEndian.PutUint32(data, hdr.Revision)
|
||||
|
||||
return data, nil
|
||||
},
|
||||
SerializerFunc: func(contents []byte, opts interface{}) error {
|
||||
expected := []byte{0x00, 0x00, 0x01, 0x00}
|
||||
if !bytes.Equal(contents, expected) {
|
||||
return fmt.Errorf("expected revision of %v, got %v", expected, contents)
|
||||
}
|
||||
|
||||
hdr.Revision = binary.LittleEndian.Uint32(contents)
|
||||
|
||||
return nil
|
||||
},
|
||||
},
|
||||
// 4 bytes Header size in little endian (in bytes, usually 5Ch 00h 00h 00h or 92 bytes)
|
||||
{
|
||||
Offset: 12,
|
||||
Length: 4,
|
||||
// Contents: []byte{0x5c, 0x00, 0x00, 0x00},
|
||||
DeserializerFunc: func(offset, length uint32, new []byte, opts interface{}) ([]byte, error) {
|
||||
data := make([]byte, length)
|
||||
binary.LittleEndian.PutUint32(data, hdr.Size)
|
||||
|
||||
return data, nil
|
||||
},
|
||||
SerializerFunc: func(contents []byte, opts interface{}) error {
|
||||
hdr.Size = binary.LittleEndian.Uint32(contents)
|
||||
if hdr.Size != HeaderSize {
|
||||
return fmt.Errorf("expected GPT header size of %d, got %d", HeaderSize, hdr.Size)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
},
|
||||
// 4 bytes Reserved; must be zero
|
||||
{
|
||||
Offset: 20,
|
||||
Length: 4,
|
||||
// Contents: []byte{0x00, 0x00, 0x00, 0x00},
|
||||
DeserializerFunc: func(offset, length uint32, new []byte, opts interface{}) ([]byte, error) {
|
||||
return []byte{0x00, 0x00, 0x00, 0x00}, nil
|
||||
},
|
||||
SerializerFunc: func(contents []byte, opts interface{}) error {
|
||||
expected := []byte{0x00, 0x00, 0x00, 0x00}
|
||||
if !bytes.Equal(contents, expected) {
|
||||
return fmt.Errorf("expected reserved field to be %v, got %v", expected, contents)
|
||||
}
|
||||
|
||||
hdr.Reserved = binary.LittleEndian.Uint32(contents)
|
||||
|
||||
return nil
|
||||
},
|
||||
},
|
||||
// 8 bytes Current LBA (location of this header copy)
|
||||
// nolint: dupl
|
||||
{
|
||||
Offset: 24,
|
||||
Length: 8,
|
||||
// Contents: []byte{0x00, 0x00, 0x00, 0x00},
|
||||
DeserializerFunc: func(offset, length uint32, new []byte, opts interface{}) ([]byte, error) {
|
||||
data := make([]byte, length)
|
||||
o, ok := opts.(*Options)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("option is not a GPT header option")
|
||||
}
|
||||
if o.Primary {
|
||||
binary.LittleEndian.PutUint64(data, hdr.CurrentLBA)
|
||||
} else {
|
||||
binary.LittleEndian.PutUint64(data, hdr.BackupLBA)
|
||||
}
|
||||
|
||||
return data, nil
|
||||
},
|
||||
SerializerFunc: func(contents []byte, opts interface{}) error {
|
||||
hdr.CurrentLBA = binary.LittleEndian.Uint64(contents)
|
||||
|
||||
return nil
|
||||
},
|
||||
},
|
||||
// 8 bytes Backup LBA (location of the other header copy)
|
||||
// nolint: dupl
|
||||
{
|
||||
Offset: 32,
|
||||
Length: 8,
|
||||
// Contents: []byte{0x00, 0x00, 0x00, 0x00},
|
||||
DeserializerFunc: func(offset, length uint32, new []byte, opts interface{}) ([]byte, error) {
|
||||
data := make([]byte, length)
|
||||
o, ok := opts.(*Options)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("option is not a GPT header option")
|
||||
}
|
||||
if o.Primary {
|
||||
binary.LittleEndian.PutUint64(data, hdr.BackupLBA)
|
||||
|
||||
} else {
|
||||
binary.LittleEndian.PutUint64(data, hdr.CurrentLBA)
|
||||
}
|
||||
|
||||
return data, nil
|
||||
},
|
||||
SerializerFunc: func(contents []byte, opts interface{}) error {
|
||||
hdr.BackupLBA = binary.LittleEndian.Uint64(contents)
|
||||
|
||||
return nil
|
||||
},
|
||||
},
|
||||
// 8 bytes First usable LBA for partitions (primary partition table last LBA + 1)
|
||||
{
|
||||
Offset: 40,
|
||||
Length: 8,
|
||||
// Contents: []byte{0x00, 0x00, 0x00, 0x00},
|
||||
DeserializerFunc: func(offset, length uint32, new []byte, opts interface{}) ([]byte, error) {
|
||||
data := make([]byte, length)
|
||||
binary.LittleEndian.PutUint64(data, hdr.FirstUsableLBA)
|
||||
|
||||
return data, nil
|
||||
},
|
||||
SerializerFunc: func(contents []byte, opts interface{}) error {
|
||||
hdr.FirstUsableLBA = binary.LittleEndian.Uint64(contents)
|
||||
|
||||
return nil
|
||||
},
|
||||
},
|
||||
// 8 bytes Last usable LBA (secondary partition table first LBA - 1)
|
||||
{
|
||||
Offset: 48,
|
||||
Length: 8,
|
||||
// Contents: []byte{0x00, 0x00, 0x00, 0x00},
|
||||
DeserializerFunc: func(offset, length uint32, new []byte, opts interface{}) ([]byte, error) {
|
||||
data := make([]byte, length)
|
||||
binary.LittleEndian.PutUint64(data, hdr.LastUsableLBA)
|
||||
|
||||
return data, nil
|
||||
},
|
||||
SerializerFunc: func(contents []byte, opts interface{}) error {
|
||||
hdr.LastUsableLBA = binary.LittleEndian.Uint64(contents)
|
||||
|
||||
return nil
|
||||
},
|
||||
},
|
||||
// 16 bytes Disk GUID (also referred as UUID on UNIXes)
|
||||
{
|
||||
Offset: 56,
|
||||
Length: 16,
|
||||
// Contents: []byte{0x00},
|
||||
DeserializerFunc: func(offset, length uint32, new []byte, opts interface{}) ([]byte, error) {
|
||||
return hdr.GUUID.MarshalBinary()
|
||||
},
|
||||
SerializerFunc: func(contents []byte, opts interface{}) error {
|
||||
guid, err := uuid.FromBytes(contents)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid GUUID: %v", err)
|
||||
}
|
||||
|
||||
hdr.GUUID = guid
|
||||
|
||||
return nil
|
||||
},
|
||||
},
|
||||
// 8 bytes Starting LBA of array of partition entries (always 2 in primary copy)
|
||||
{
|
||||
Offset: 72,
|
||||
Length: 8,
|
||||
// Contents: []byte{0x00},
|
||||
DeserializerFunc: func(offset, length uint32, new []byte, opts interface{}) ([]byte, error) {
|
||||
data := make([]byte, length)
|
||||
binary.LittleEndian.PutUint64(data, hdr.PartitionEntriesStartLBA)
|
||||
|
||||
return data, nil
|
||||
},
|
||||
SerializerFunc: func(contents []byte, opts interface{}) error {
|
||||
// TODO: Should we verify it is 2 in the case of primary?
|
||||
o, ok := opts.(*Options)
|
||||
if !ok {
|
||||
return fmt.Errorf("option is not a GPT header option")
|
||||
}
|
||||
hdr.PartitionEntriesStartLBA = binary.LittleEndian.Uint64(contents)
|
||||
array, err := hdr.From(o.Table, lba.Range{Start: hdr.PartitionEntriesStartLBA, End: uint64(33)})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read starting LBA from header: %v", err)
|
||||
}
|
||||
|
||||
hdr.array = array
|
||||
|
||||
return nil
|
||||
},
|
||||
},
|
||||
// 4 bytes Number of partition entries in array
|
||||
{
|
||||
Offset: 80,
|
||||
Length: 4,
|
||||
// Contents: []byte{0x00},
|
||||
DeserializerFunc: func(offset, length uint32, new []byte, opts interface{}) ([]byte, error) {
|
||||
data := make([]byte, length)
|
||||
binary.LittleEndian.PutUint32(data, hdr.NumberOfPartitionEntries)
|
||||
|
||||
return data, nil
|
||||
},
|
||||
SerializerFunc: func(contents []byte, opts interface{}) error {
|
||||
hdr.NumberOfPartitionEntries = binary.LittleEndian.Uint32(contents)
|
||||
|
||||
return nil
|
||||
},
|
||||
},
|
||||
// 4 bytes Size of a single partition entry (usually 80h or 128)
|
||||
{
|
||||
Offset: 84,
|
||||
Length: 4,
|
||||
// Contents: []byte{0x00},
|
||||
DeserializerFunc: func(offset, length uint32, new []byte, opts interface{}) ([]byte, error) {
|
||||
data := make([]byte, length)
|
||||
binary.LittleEndian.PutUint32(data, hdr.PartitionEntrySize)
|
||||
|
||||
return data, nil
|
||||
},
|
||||
SerializerFunc: func(contents []byte, opts interface{}) error {
|
||||
length := binary.LittleEndian.Uint32(contents)
|
||||
// This field should be set to a value of: 128 x 2n where n is an integer greater than or equal to zero.
|
||||
if length%128 != 0 {
|
||||
return fmt.Errorf("expected partition entry size to be a multiple of %d, got %d", 128, length)
|
||||
}
|
||||
|
||||
hdr.PartitionEntrySize = binary.LittleEndian.Uint32(contents)
|
||||
|
||||
return nil
|
||||
},
|
||||
},
|
||||
// 4 bytes CRC32/zlib of partition array in little endian
|
||||
{
|
||||
Offset: 88,
|
||||
Length: 4,
|
||||
// Contents: []byte{0x00},
|
||||
DeserializerFunc: func(offset, length uint32, new []byte, opts interface{}) ([]byte, error) {
|
||||
o, ok := opts.(*Options)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("option is not a GPT header option")
|
||||
}
|
||||
expected := hdr.NumberOfPartitionEntries * hdr.PartitionEntrySize
|
||||
if len(o.Array) != int(expected) {
|
||||
return nil, fmt.Errorf("expected array length of %d, got %d", expected, len(o.Array))
|
||||
}
|
||||
crc := crc32.ChecksumIEEE(o.Array)
|
||||
data := make([]byte, length)
|
||||
binary.LittleEndian.PutUint32(data, crc)
|
||||
|
||||
// We should update this here and now to ensure that the field is still valid.
|
||||
hdr.PartitionsArrayCRC = crc
|
||||
|
||||
return data, nil
|
||||
},
|
||||
SerializerFunc: func(contents []byte, opts interface{}) error {
|
||||
crc := binary.LittleEndian.Uint32(contents)
|
||||
// Note that hdr.array is expected to be set in offset 72 DeserializerFunc.
|
||||
checksum := crc32.ChecksumIEEE(hdr.array)
|
||||
if crc != checksum {
|
||||
return fmt.Errorf("expected partition checksum of %v, got %v", checksum, crc)
|
||||
}
|
||||
|
||||
hdr.PartitionsArrayCRC = crc
|
||||
|
||||
return nil
|
||||
},
|
||||
},
|
||||
// Reserved; must be zeroes for the rest of the block (420 bytes for a sector size of 512 bytes; but can be more with larger sector sizes)
|
||||
{
|
||||
Offset: HeaderSize,
|
||||
Length: 420,
|
||||
// Contents: []byte{0x00},
|
||||
DeserializerFunc: func(offset, length uint32, new []byte, opts interface{}) ([]byte, error) {
|
||||
data := make([]byte, 420)
|
||||
|
||||
return data, nil
|
||||
},
|
||||
SerializerFunc: func(contents []byte, opts interface{}) error {
|
||||
expected := make([]byte, 420)
|
||||
if !bytes.Equal(contents, expected) {
|
||||
return fmt.Errorf("expected %d trailing bytes of zeroes", 420)
|
||||
}
|
||||
|
||||
hdr.TrailingBytes = contents
|
||||
|
||||
return nil
|
||||
},
|
||||
},
|
||||
// 4 bytes CRC32/zlib of header (offset +0 up to header size) in little endian, with this field zeroed during calculation
|
||||
{
|
||||
Offset: 16,
|
||||
Length: 4,
|
||||
// Contents: []byte{0x00, 0x00, 0x00, 0x00},
|
||||
DeserializerFunc: func(offset, length uint32, new []byte, opts interface{}) ([]byte, error) {
|
||||
// Copy the header into a temporary slice and to avoid modifying the original.
|
||||
header := make([]byte, HeaderSize)
|
||||
copy(header, new)
|
||||
// Zero the CRC field during the calculation.
|
||||
copy(header[16:20], []byte{0x00, 0x00, 0x00, 0x00})
|
||||
|
||||
crc := crc32.ChecksumIEEE(header)
|
||||
data := make([]byte, length)
|
||||
binary.LittleEndian.PutUint32(data, crc)
|
||||
|
||||
// We should update this here and now to ensure that the field is still valid.
|
||||
hdr.CRC = crc
|
||||
|
||||
return data, nil
|
||||
},
|
||||
SerializerFunc: func(contents []byte, opts interface{}) error {
|
||||
crc := binary.LittleEndian.Uint32(contents)
|
||||
|
||||
// Copy the header into a temporary slice and to avoid modifying the original.
|
||||
header := make([]byte, HeaderSize)
|
||||
copy(header, hdr.data)
|
||||
// Zero the CRC field during the calculation.
|
||||
copy(header[16:20], []byte{0x00, 0x00, 0x00, 0x00})
|
||||
|
||||
checksum := crc32.ChecksumIEEE(header)
|
||||
if crc != checksum {
|
||||
return fmt.Errorf("expected header checksum of %d, got %d", crc, checksum)
|
||||
}
|
||||
|
||||
hdr.CRC = crc
|
||||
|
||||
return nil
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
package header
|
||||
|
||||
// Options is the functional options struct.
|
||||
type Options struct {
|
||||
Primary bool
|
||||
Table []byte
|
||||
Array []byte
|
||||
}
|
||||
|
||||
// Option is the functional option func.
|
||||
type Option func(*Options)
|
||||
|
||||
// WithHeaderPrimary sets the primary option.
|
||||
func WithHeaderPrimary(o bool) Option {
|
||||
return func(args *Options) {
|
||||
args.Primary = o
|
||||
}
|
||||
}
|
||||
|
||||
// WithHeaderTable sets the partition type.
|
||||
func WithHeaderTable(o []byte) Option {
|
||||
return func(args *Options) {
|
||||
args.Table = o
|
||||
}
|
||||
}
|
||||
|
||||
// WithHeaderArrayBytes sets the partition type.
|
||||
func WithHeaderArrayBytes(o []byte) Option {
|
||||
return func(args *Options) {
|
||||
args.Array = o
|
||||
}
|
||||
}
|
||||
|
||||
// NewDefaultOptions initializes a Options struct with default values.
|
||||
func NewDefaultOptions(setters ...interface{}) *Options {
|
||||
opts := &Options{
|
||||
Primary: true,
|
||||
Table: []byte{},
|
||||
Array: []byte{},
|
||||
}
|
||||
|
||||
for _, setter := range setters {
|
||||
if s, ok := setter.(Option); ok {
|
||||
s(opts)
|
||||
}
|
||||
}
|
||||
|
||||
return opts
|
||||
}
|
189
src/initramfs/pkg/blockdevice/table/gpt/partition/partition.go
Normal file
189
src/initramfs/pkg/blockdevice/table/gpt/partition/partition.go
Normal file
@ -0,0 +1,189 @@
|
||||
// Package partition provides a library for working with GPT partitions.
|
||||
package partition
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
|
||||
"github.com/autonomy/dianemo/src/initramfs/pkg/blockdevice/pkg/serde"
|
||||
"github.com/google/uuid"
|
||||
"golang.org/x/text/encoding/unicode"
|
||||
)
|
||||
|
||||
// Partition represents a partition entry in a GUID partition table.
|
||||
type Partition struct {
|
||||
data []byte
|
||||
|
||||
Type uuid.UUID // 0
|
||||
ID uuid.UUID // 16
|
||||
FirstLBA uint64 // 32
|
||||
LastLBA uint64 // 40
|
||||
Flags uint64 // 48
|
||||
Name string // 56
|
||||
TrailingBytes []byte // 128
|
||||
|
||||
Number int32
|
||||
}
|
||||
|
||||
// NewPartition initializes and returns a new partition.
|
||||
func NewPartition(data []byte) *Partition {
|
||||
return &Partition{
|
||||
data: data,
|
||||
}
|
||||
}
|
||||
|
||||
// Bytes returns the partition as a byte slice.
|
||||
func (prt *Partition) Bytes() []byte {
|
||||
return prt.data
|
||||
}
|
||||
|
||||
// Start returns the partition's starting LBA..
|
||||
func (prt *Partition) Start() int64 {
|
||||
return int64(prt.FirstLBA)
|
||||
}
|
||||
|
||||
// Length returns the partition's length in LBA.
|
||||
func (prt *Partition) Length() int64 {
|
||||
return int64(prt.LastLBA)
|
||||
}
|
||||
|
||||
// No returns the partition's number.
|
||||
func (prt *Partition) No() int32 {
|
||||
return prt.Number
|
||||
}
|
||||
|
||||
// Fields implements the serder.Serde interface.
|
||||
func (prt *Partition) Fields() []*serde.Field {
|
||||
return []*serde.Field{
|
||||
// 16 bytes Partition type GUID
|
||||
{
|
||||
Offset: 0,
|
||||
Length: 16,
|
||||
DeserializerFunc: func(offset, length uint32, new []byte, opts interface{}) ([]byte, error) {
|
||||
return prt.Type.MarshalBinary()
|
||||
},
|
||||
SerializerFunc: func(contents []byte, opts interface{}) error {
|
||||
guid, err := uuid.FromBytes(contents)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid GUUID: %v", err)
|
||||
}
|
||||
|
||||
// TODO: Provide a method for getting the human readable name of the type.
|
||||
// See https://en.wikipedia.org/wiki/GUID_Partition_Table.
|
||||
prt.Type = guid
|
||||
|
||||
return nil
|
||||
},
|
||||
},
|
||||
// 16 bytes Unique partition GUID
|
||||
{
|
||||
Offset: 16,
|
||||
Length: 16,
|
||||
DeserializerFunc: func(offset, length uint32, new []byte, opts interface{}) ([]byte, error) {
|
||||
return prt.ID.MarshalBinary()
|
||||
},
|
||||
SerializerFunc: func(contents []byte, opts interface{}) error {
|
||||
guid, err := uuid.FromBytes(contents)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid GUUID: %v", err)
|
||||
}
|
||||
|
||||
prt.ID = guid
|
||||
|
||||
return nil
|
||||
},
|
||||
},
|
||||
// 8 bytes First LBA (little endian)
|
||||
{
|
||||
Offset: 32,
|
||||
Length: 8,
|
||||
DeserializerFunc: func(offset, length uint32, new []byte, opts interface{}) ([]byte, error) {
|
||||
data := make([]byte, length)
|
||||
binary.LittleEndian.PutUint64(data, prt.FirstLBA)
|
||||
|
||||
return data, nil
|
||||
},
|
||||
SerializerFunc: func(contents []byte, opts interface{}) error {
|
||||
prt.FirstLBA = binary.LittleEndian.Uint64(contents)
|
||||
|
||||
return nil
|
||||
},
|
||||
},
|
||||
// 8 bytes Last LBA (inclusive, usually odd)
|
||||
{
|
||||
Offset: 40,
|
||||
Length: 8,
|
||||
DeserializerFunc: func(offset, length uint32, new []byte, opts interface{}) ([]byte, error) {
|
||||
data := make([]byte, length)
|
||||
binary.LittleEndian.PutUint64(data, prt.LastLBA)
|
||||
|
||||
return data, nil
|
||||
},
|
||||
SerializerFunc: func(contents []byte, opts interface{}) error {
|
||||
prt.LastLBA = binary.LittleEndian.Uint64(contents)
|
||||
|
||||
return nil
|
||||
},
|
||||
},
|
||||
// 8 bytes Attribute flags (e.g. bit 60 denotes read-only)
|
||||
{
|
||||
Offset: 48,
|
||||
Length: 8,
|
||||
DeserializerFunc: func(offset, length uint32, new []byte, opts interface{}) ([]byte, error) {
|
||||
data := make([]byte, length)
|
||||
binary.LittleEndian.PutUint64(data, prt.Flags)
|
||||
|
||||
return data, nil
|
||||
},
|
||||
SerializerFunc: func(contents []byte, opts interface{}) error {
|
||||
prt.Flags = binary.LittleEndian.Uint64(contents)
|
||||
|
||||
return nil
|
||||
},
|
||||
},
|
||||
// 72 bytes Partition name (36 UTF-16LE code units)
|
||||
{
|
||||
Offset: 56,
|
||||
Length: 72,
|
||||
DeserializerFunc: func(offset, length uint32, new []byte, opts interface{}) ([]byte, error) {
|
||||
utf16 := unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM)
|
||||
name, err := utf16.NewEncoder().Bytes([]byte(prt.Name))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// TODO: Should we error if the name exceeds 72 bytes?
|
||||
data := make([]byte, 72)
|
||||
copy(data, name)
|
||||
|
||||
return data, nil
|
||||
},
|
||||
SerializerFunc: func(contents []byte, opts interface{}) error {
|
||||
utf16 := unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM)
|
||||
decoded, err := utf16.NewDecoder().Bytes(contents)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
prt.Name = string(bytes.Trim(decoded, "\x00"))
|
||||
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
Offset: 72,
|
||||
Length: 56,
|
||||
DeserializerFunc: func(offset, length uint32, new []byte, opts interface{}) ([]byte, error) {
|
||||
data := make([]byte, length)
|
||||
copy(data, prt.TrailingBytes)
|
||||
|
||||
return data, nil
|
||||
},
|
||||
SerializerFunc: func(contents []byte, opts interface{}) error {
|
||||
prt.TrailingBytes = contents
|
||||
|
||||
return nil
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
package partition
|
||||
|
||||
import (
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
// Options is the functional options struct.
|
||||
type Options struct {
|
||||
Type uuid.UUID
|
||||
Name string
|
||||
}
|
||||
|
||||
// Option is the functional option func.
|
||||
type Option func(*Options)
|
||||
|
||||
// WithPartitionType sets the partition type.
|
||||
func WithPartitionType(o [16]byte) Option {
|
||||
return func(args *Options) {
|
||||
// TODO: An Option should return an error.
|
||||
// nolint: errcheck
|
||||
guuid, _ := uuid.FromBytes(o[:])
|
||||
args.Type = guuid
|
||||
}
|
||||
}
|
||||
|
||||
// WithPartitionName sets the partition name.
|
||||
func WithPartitionName(o string) Option {
|
||||
return func(args *Options) {
|
||||
args.Name = o
|
||||
}
|
||||
}
|
||||
|
||||
// NewDefaultOptions initializes a Options struct with default values.
|
||||
func NewDefaultOptions(setters ...interface{}) *Options {
|
||||
// Default to data type "af3dc60f-8384-7247-8e79-3d69d8477de4"
|
||||
// TODO: An Option should return an error.
|
||||
// nolint: errcheck
|
||||
guuid, _ := uuid.FromBytes([]byte{0Xaf, 0X3d, 0Xc6, 0X0f, 0X83, 0X84, 0X72, 0X47, 0X8e, 0X79, 0X3d, 0X69, 0Xd8, 0X47, 0X7d, 0Xe4})
|
||||
|
||||
opts := &Options{
|
||||
Type: guuid,
|
||||
Name: "",
|
||||
}
|
||||
|
||||
for _, setter := range setters {
|
||||
if s, ok := setter.(Option); ok {
|
||||
s(opts)
|
||||
}
|
||||
}
|
||||
|
||||
return opts
|
||||
}
|
67
src/initramfs/pkg/blockdevice/table/table.go
Normal file
67
src/initramfs/pkg/blockdevice/table/table.go
Normal file
@ -0,0 +1,67 @@
|
||||
// Package table provides a library for working with block device partition tables.
|
||||
package table
|
||||
|
||||
import "github.com/autonomy/dianemo/src/initramfs/pkg/blockdevice/pkg/serde"
|
||||
|
||||
// Table represents a partition table.
|
||||
type Table = []byte
|
||||
|
||||
// PartitionTable describes a partition table.
|
||||
type PartitionTable interface {
|
||||
// Bytes returns the partition table as a byte slice.
|
||||
Bytes() Table
|
||||
// Read reades the partition table.
|
||||
Read() error
|
||||
// Write writes the partition table/.
|
||||
Write() error
|
||||
// Type returns the partition table type.
|
||||
Type() Type
|
||||
// Header returns the partition table header.
|
||||
Header() Header
|
||||
// Partitions returns a slice o partition table partitions.
|
||||
Partitions() []Partition
|
||||
// Repair repairs a partition table.
|
||||
Repair() error
|
||||
// Partitioner must be implemented by a partition table.
|
||||
Partitioner
|
||||
}
|
||||
|
||||
// Type represents a partition table type.
|
||||
type Type int
|
||||
|
||||
const (
|
||||
// MBR is the Master Boot Record artition table.
|
||||
MBR Type = iota
|
||||
// GPT is the GUID partition table.
|
||||
GPT
|
||||
)
|
||||
|
||||
// Header describes a partition table header.
|
||||
type Header interface {
|
||||
// Bytes returns the partition table header as a byte slice.
|
||||
Bytes() []byte
|
||||
serde.Serde
|
||||
}
|
||||
|
||||
// Partition describes a partition.
|
||||
type Partition interface {
|
||||
// Bytes returns the partition table partitions as a byte slice.
|
||||
Bytes() []byte
|
||||
// Start returns the partition's starting LBA.
|
||||
Start() int64
|
||||
// Length returns the partition's length in LBA.
|
||||
Length() int64
|
||||
// No returns the partition's number.
|
||||
No() int32
|
||||
serde.Serde
|
||||
}
|
||||
|
||||
// Partitioner describes actions that can be taken on a partition.
|
||||
type Partitioner interface {
|
||||
// Add adds a partition to the partition table.
|
||||
Add(uint64, ...interface{}) (Partition, error)
|
||||
// Resize resizes a partition table.
|
||||
Resize(Partition) error
|
||||
// Delete deletes a partition table.
|
||||
Delete(Partition) error
|
||||
}
|
@ -2,6 +2,7 @@ package factory
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"net"
|
||||
"strconv"
|
||||
|
||||
@ -17,6 +18,7 @@ type Registrator interface {
|
||||
// Options is the functional options struct.
|
||||
type Options struct {
|
||||
Port int
|
||||
Network string
|
||||
Config *tls.Config
|
||||
ServerOptions []grpc.ServerOption
|
||||
}
|
||||
@ -31,6 +33,13 @@ func Port(o int) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// Network sets the network type of the listener.
|
||||
func Network(o string) Option {
|
||||
return func(args *Options) {
|
||||
args.Network = o
|
||||
}
|
||||
}
|
||||
|
||||
// Config sets the listen port of the server.
|
||||
func Config(o *tls.Config) Option {
|
||||
return func(args *Options) {
|
||||
@ -48,7 +57,8 @@ func ServerOptions(o ...grpc.ServerOption) Option {
|
||||
// NewDefaultOptions initializes the Options struct with default values.
|
||||
func NewDefaultOptions(setters ...Option) *Options {
|
||||
opts := &Options{
|
||||
Port: 50000,
|
||||
Port: 50000,
|
||||
Network: "tcp",
|
||||
}
|
||||
|
||||
for _, setter := range setters {
|
||||
@ -69,11 +79,19 @@ func Listen(r Registrator, setters ...Option) (err error) {
|
||||
server := grpc.NewServer(opts.ServerOptions...)
|
||||
r.Register(server)
|
||||
|
||||
listener, err := net.Listen("tcp", ":"+strconv.Itoa(opts.Port))
|
||||
var address string
|
||||
switch opts.Network {
|
||||
case "unix":
|
||||
address = "/run/factory/factory.sock"
|
||||
case "tcp":
|
||||
address = ":" + strconv.Itoa(opts.Port)
|
||||
default:
|
||||
return fmt.Errorf("unknown network: %s", opts.Network)
|
||||
}
|
||||
listener, err := net.Listen(opts.Network, address)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = server.Serve(listener)
|
||||
if err != nil {
|
||||
return
|
||||
|
@ -11,6 +11,7 @@ import (
|
||||
|
||||
"github.com/autonomy/dianemo/src/initramfs/cmd/trustd/proto"
|
||||
"github.com/autonomy/dianemo/src/initramfs/pkg/crypto/x509"
|
||||
"github.com/autonomy/dianemo/src/initramfs/pkg/grpc/middleware/auth/basic"
|
||||
"github.com/autonomy/dianemo/src/initramfs/pkg/net"
|
||||
"github.com/autonomy/dianemo/src/initramfs/pkg/userdata"
|
||||
"google.golang.org/grpc"
|
||||
@ -22,12 +23,29 @@ type Generator struct {
|
||||
}
|
||||
|
||||
// NewGenerator initializes a Generator with a preconfigured grpc.ClientConn.
|
||||
func NewGenerator(conn *grpc.ClientConn) (g *Generator) {
|
||||
func NewGenerator(data *userdata.UserData, port int) (g *Generator, err error) {
|
||||
if len(data.Services.Trustd.Endpoints) == 0 {
|
||||
return nil, fmt.Errorf("at least one root of trust endpoint is required")
|
||||
}
|
||||
|
||||
creds := basic.NewCredentials(
|
||||
data.Security.OS.CA.Crt,
|
||||
data.Services.Trustd.Username,
|
||||
data.Services.Trustd.Password,
|
||||
)
|
||||
|
||||
// TODO: In the case of failure, attempt to generate the identity from
|
||||
// another RoT.
|
||||
var conn *grpc.ClientConn
|
||||
conn, err = basic.NewConnection(data.Services.Trustd.Endpoints[0], port, creds)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
client := proto.NewTrustdClient(conn)
|
||||
|
||||
return &Generator{
|
||||
client: client,
|
||||
}
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Certificate implements the proto.TrustdClient interface.
|
@ -51,6 +51,7 @@ type Services struct {
|
||||
Kubeadm *Kubeadm `yaml:"kubeadm"`
|
||||
Trustd *Trustd `yaml:"trustd"`
|
||||
Proxyd *Proxyd `yaml:"proxyd"`
|
||||
Blockd *Blockd `yaml:"blockd"`
|
||||
OSD *OSD `yaml:"osd"`
|
||||
CRT *CRT `yaml:"crt"`
|
||||
}
|
||||
@ -106,6 +107,11 @@ type Proxyd struct {
|
||||
Image string `yaml:"image,omitempty"`
|
||||
}
|
||||
|
||||
// Blockd describes the configuration of the blockd service.
|
||||
type Blockd struct {
|
||||
Image string `yaml:"image,omitempty"`
|
||||
}
|
||||
|
||||
// CRT describes the configuration of the container runtime service.
|
||||
type CRT struct {
|
||||
Image string `yaml:"image,omitempty"`
|
||||
|
@ -522,7 +522,7 @@ tasks:
|
||||
template: |
|
||||
WORKDIR $SRC/{{ .Docker.CurrentStage }}
|
||||
WORKDIR /tools/usr/local
|
||||
RUN curl -L {{ index .Variables "srcGo" }} | tar -C /tools/usr/local -xz
|
||||
RUN curl -L {{ index .Variables "srcGo" }} | tar -xz
|
||||
gperf:
|
||||
template: |
|
||||
WORKDIR $SRC/{{ .Docker.CurrentStage }}
|
||||
@ -840,6 +840,7 @@ tasks:
|
||||
COPY src/fsh.sh /tools/bin
|
||||
RUN chmod +x /tools/bin/fsh.sh
|
||||
RUN fsh.sh {{ index .Variables "rootfs" }}
|
||||
# WORKDIR $PREFIX/$SRC/{{ .Docker.CurrentStage }}
|
||||
WORKDIR /tools/usr/local/src/{{ .Docker.CurrentStage }}
|
||||
RUN curl -L {{index .Variables "srcGlibc" }} | tar --strip-components=1 -xJ
|
||||
RUN ln -sfv /tools/lib/gcc /usr/lib
|
||||
@ -894,6 +895,7 @@ tasks:
|
||||
--without-ncurses
|
||||
RUN make -j $(($(nproc) / 2))
|
||||
RUN make install
|
||||
RUN cp -R ../include $PREFIX/include/util-linux
|
||||
xml-parser:
|
||||
template: |
|
||||
WORKDIR $SRC/{{ .Docker.CurrentStage }}
|
||||
|
Loading…
x
Reference in New Issue
Block a user