19 Commits
Author SHA256 Message Date
kira f388a879ea Adding ability to handle non bcachefs as root filesystem 2026-09-22 17:25:02 +02:00
kira 63cb87888b Wrapping UI rework and rollback progress addition. 2026-09-17 23:42:01 +02:00
kira e4febb1e60 Huge refactor, quicksave 2026-09-17 21:54:20 +02:00
kira d2ddceb1a2 Switching to regex_lite and s optimization level - 2x binary size reduction. 2026-09-10 00:24:07 +02:00
kira 000c8b94b5 More readme changes 2026-09-07 17:54:05 +02:00
kira 1ce2a8c5e8 Update documantation given nature of snapshots in bcachefs 2026-09-07 17:45:43 +02:00
kira 3a5d6d545c Udated some dependencies 2026-08-18 22:45:32 +02:00
kira 5ef902fc29 Added script to build initcpio image 2026-08-18 22:45:02 +02:00
kira fecd188b4d Bumping version to 0.2.1 2026-08-01 22:56:29 +02:00
kira ef16746068 Fixing /run remount on switch_root and elimenating /usr/bin/mount dependency. 2026-08-01 22:56:07 +02:00
kira 1998df9720 Bumping version to 0.2.0 2026-07-31 15:55:17 +02:00
kira 84d210d07c blkid partlabel discovery now working. Also some cleanups. 2026-07-31 15:54:54 +02:00
kira 9601cc300b Trying to fix blkid thing. Add wayting for sysfs logic. 2026-07-30 14:11:00 +02:00
kira 316ffce414 Trying to figure out blkid thing. Add --probe flag. 2026-07-29 23:16:32 +02:00
kira c132ca3ffa Bumping version to 0.1.6 2026-07-29 15:35:41 +02:00
kira 0ea1a9bb3f Using blkid utility to determine dev name by gpt partlabel. 2026-07-29 15:34:52 +02:00
kira b4127f2fe8 Removed retarded git actions. 2026-07-20 23:22:20 +02:00
kira 1bf3ef0e15 Fixed mounting of /dev /sys /proc what is needed for other stuff to work. 2026-07-20 23:21:48 +02:00
kira b90544bac5 Trying using absolute paths now for binaries executions. 2026-07-15 16:15:09 +02:00
8 changed files with 1063 additions and 345 deletions
@@ -1,43 +0,0 @@
name: "Build Release Binary"
run-name: "Build Release: ${{ inputs.tag_name }}"
on:
# Allows triggering from another workflow
workflow_call:
inputs:
tag_name:
description: 'Git tag to build (e.g., v1.0.0)'
required: true
type: string
workflow_dispatch: # Enables manual triggering
inputs:
tag_name:
description: 'Git tag to build (e.g., v1.0.0)'
required: true
type: string
jobs:
build-release:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
env:
GIT_DEFAULT_HASH: sha256
with:
# If triggered manually, use the input tag.
# If triggered by push, GITHUB_REF_NAME is the tag.
ref: ${{ inputs.tag_name || gitea.ref_name }}
- name: Setup Rust toolchain
uses: actions-rust-lang/setup-rust-toolchain@v1
- name: Build Release Binary
run: cargo build --release
- name: Upload Artifact
uses: actions/upload-artifact@v3
with:
name: nyanit-${{ inputs.tag_name || gitea.ref_name }}
path: target/release/nyanit
-40
View File
@@ -1,40 +0,0 @@
name: Create Release with Binary
on:
workflow_dispatch: # Enables manual triggering
inputs:
tag_name:
description: 'Git tag to build (e.g., v1.0.0)'
required: true
type: string
rel_description:
description: 'Release description'
required: false
type: string
jobs:
# This job calls the reusable workflow
build-binary:
uses: ./.gitea/workflows/build-release-binary.yml
with:
tag_name: "${{ inputs.tag_name }}"
create-release:
needs: build-binary # Wait for the reusable workflow to finish
runs-on: ubuntu-latest
steps:
- name: Download Artifact
uses: actions/download-artifact@v4
with:
name: nyanit-${{ inputs.tag_name }}
- name: Create Release and Attach Binary
uses: actions/release-action@main
with:
artifacts: "nyanit-${{ inputs.tag_name }}"
api_key: ${{ secrets.bzzpss_release_psszz }}
tag_name: ${{ inputs.tag_name }}
name: Release ${{ inputs.tag_name }}
body: "Automated release for ${{ inputs.tag_name }}\n${{ inputs.rel_description }}"
Generated
+95 -100
View File
@@ -2,15 +2,6 @@
# It is not intended for manual editing. # It is not intended for manual editing.
version = 4 version = 4
[[package]]
name = "aho-corasick"
version = "1.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301"
dependencies = [
"memchr",
]
[[package]] [[package]]
name = "allocator-api2" name = "allocator-api2"
version = "0.2.21" version = "0.2.21"
@@ -34,9 +25,9 @@ checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"
[[package]] [[package]]
name = "bitflags" name = "bitflags"
version = "2.13.0" version = "2.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" checksum = "3ded4057c258ba199e2d26386d3af3780957ecaee6c4ef4041c6b4b8b97c0b06"
[[package]] [[package]]
name = "by_address" name = "by_address"
@@ -55,9 +46,15 @@ dependencies = [
[[package]] [[package]]
name = "cfg-if" name = "cfg-if"
version = "1.0.4" version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" checksum = "4e7648175b45a9a48536d676f68d918270699102aa8dab5496df06904c914600"
[[package]]
name = "cfg_aliases"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f079e83a288787bcd14a6aea84cee5c87a67c5a3e660c30f557a3d24761b3527"
[[package]] [[package]]
name = "compact_str" name = "compact_str"
@@ -75,9 +72,9 @@ dependencies = [
[[package]] [[package]]
name = "darling" name = "darling"
version = "0.23.0" version = "0.24.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d" checksum = "ed17f5901b6630b993ca003def43f2f8ef4014fc13b047b57aad617ff32bc2ec"
dependencies = [ dependencies = [
"darling_core", "darling_core",
"darling_macro", "darling_macro",
@@ -85,26 +82,26 @@ dependencies = [
[[package]] [[package]]
name = "darling_core" name = "darling_core"
version = "0.23.0" version = "0.24.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9865a50f7c335f53564bb694ef660825eb8610e0a53d3e11bf1b0d3df31e03b0" checksum = "6837e2cf7485aaae18f86181d2f0e9a7ed297a025e220aeabf63fdebd3a2ddff"
dependencies = [ dependencies = [
"ident_case", "ident_case",
"proc-macro2", "proc-macro2",
"quote", "quote",
"strsim", "strsim",
"syn", "syn 3.0.5",
] ]
[[package]] [[package]]
name = "darling_macro" name = "darling_macro"
version = "0.23.0" version = "0.24.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" checksum = "2ac7135c3ef02b2f7833bbeb1be5ba7f966dcde8a87c6b87f65a778d71a02785"
dependencies = [ dependencies = [
"darling_core", "darling_core",
"quote", "quote",
"syn", "syn 3.0.5",
] ]
[[package]] [[package]]
@@ -115,9 +112,9 @@ checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c"
[[package]] [[package]]
name = "either" name = "either"
version = "1.16.0" version = "1.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" checksum = "252afb9ae5eaa683babdc6a068b3f5726eb19e05070c731f9b2a23a7c3e8ed34"
[[package]] [[package]]
name = "equivalent" name = "equivalent"
@@ -125,12 +122,6 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]]
name = "fast-srgb8"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd2e7510819d6fbf51a5545c8f922716ecfb14df168a3242f7d33e0239efe6a1"
[[package]] [[package]]
name = "foldhash" name = "foldhash"
version = "0.2.0" version = "0.2.0"
@@ -182,15 +173,15 @@ dependencies = [
[[package]] [[package]]
name = "instability" name = "instability"
version = "0.3.12" version = "0.3.13"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5eb2d60ef19920a3a9193c3e371f726ec1dafc045dac788d0fb3704272458971" checksum = "2bf84e73fa6f27f299dec58e13223cf70db80da872eb921d4f6138342a0eabc8"
dependencies = [ dependencies = [
"darling", "darling",
"indoc", "indoc",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 3.0.5",
] ]
[[package]] [[package]]
@@ -221,9 +212,9 @@ dependencies = [
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.186" version = "0.2.189"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2"
[[package]] [[package]]
name = "libm" name = "libm"
@@ -233,27 +224,33 @@ checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981"
[[package]] [[package]]
name = "line-clipping" name = "line-clipping"
version = "0.3.7" version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f50e8f47623268b5407192d26876c4d7f89d686ca130fdc53bced4814cd29f8" checksum = "e752191d037c44ad111a8caa762921926658402f01cc1253f7bef2020ece4f5e"
dependencies = [ dependencies = [
"bitflags", "bitflags",
] ]
[[package]] [[package]]
name = "lru" name = "lru"
version = "0.18.1" version = "0.18.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b6180140927ee907000b0aa540091f6ea512ead4447c92b8fc35bc72788a5a6" checksum = "ff9840bcc50b71349309900da0ce7279aa336ae71d73250b07998932c7d97c25"
dependencies = [ dependencies = [
"hashbrown 0.17.1", "hashbrown 0.17.1",
] ]
[[package]] [[package]]
name = "memchr" name = "nix"
version = "2.8.3" version = "0.31.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" checksum = "cf20d2fde8ff38632c426f1165ed7436270b44f199fc55284c38276f9db47c3d"
dependencies = [
"bitflags",
"cfg-if",
"cfg_aliases",
"libc",
]
[[package]] [[package]]
name = "num-conv" name = "num-conv"
@@ -287,43 +284,53 @@ checksum = "6aa2c4e539b869820a2b82e1aef6ff40aa85e65decdd5185e83fb4b1249cd00f"
[[package]] [[package]]
name = "nyanit" name = "nyanit"
version = "0.1.3" version = "0.2.3"
dependencies = [ dependencies = [
"nix",
"ratatui", "ratatui",
"ratatui-textarea", "ratatui-textarea",
"regex", "regex-lite",
"termion", "termion",
] ]
[[package]] [[package]]
name = "palette" name = "palette"
version = "0.7.6" version = "0.7.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4cbf71184cc5ecc2e4e1baccdb21026c20e5fc3dcf63028a086131b3ab00b6e6" checksum = "ddeed8580d347d2abf3dcf06a5f0b3dc020258338526b277847cd4248a70fc64"
dependencies = [ dependencies = [
"approx", "approx",
"fast-srgb8",
"libm", "libm",
"palette_derive", "palette_derive",
"palette_math",
] ]
[[package]] [[package]]
name = "palette_derive" name = "palette_derive"
version = "0.7.6" version = "0.7.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f5030daf005bface118c096f510ffb781fc28f9ab6a32ab224d8631be6851d30" checksum = "88537020289b719d81be994ccf1bbf4990f477e2f69ee52fe3e45f43a02e56be"
dependencies = [ dependencies = [
"by_address", "by_address",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.119",
]
[[package]]
name = "palette_math"
version = "0.7.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e6eb142958d64335fb0e345c5b9ead2ecd6fc438c307e9d7d3c4fd428dbaf12"
dependencies = [
"libm",
] ]
[[package]] [[package]]
name = "portable-atomic" name = "portable-atomic"
version = "1.13.1" version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" checksum = "05c8b63e8d9609db387f0324918f81d68fe27748f084ef092fb35954d0539a85"
[[package]] [[package]]
name = "powerfmt" name = "powerfmt"
@@ -333,18 +340,18 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.106" version = "1.0.107"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9"
dependencies = [ dependencies = [
"unicode-ident", "unicode-ident",
] ]
[[package]] [[package]]
name = "quote" name = "quote"
version = "1.0.46" version = "1.0.47"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368" checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
] ]
@@ -428,33 +435,10 @@ dependencies = [
] ]
[[package]] [[package]]
name = "regex" name = "regex-lite"
version = "1.13.0" version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a0e75113e14dc5acb068cd0786884f214f1312650a3d36d269f5c4f3cdee8a2" checksum = "cab834c73d247e67f4fae452806d17d3c7501756d98c8808d7c9c7aa7d18f973"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.4.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f388202e4b80542a0921078cc23b6333bcf1409c1e3f86404cae4766a6131db"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.8.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4"
[[package]] [[package]]
name = "rustversion" name = "rustversion"
@@ -470,9 +454,9 @@ checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f"
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.228" version = "1.0.229"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba"
dependencies = [ dependencies = [
"serde_core", "serde_core",
"serde_derive", "serde_derive",
@@ -480,22 +464,22 @@ dependencies = [
[[package]] [[package]]
name = "serde_core" name = "serde_core"
version = "1.0.228" version = "1.0.229"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48"
dependencies = [ dependencies = [
"serde_derive", "serde_derive",
] ]
[[package]] [[package]]
name = "serde_derive" name = "serde_derive"
version = "1.0.228" version = "1.0.229"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 3.0.5",
] ]
[[package]] [[package]]
@@ -528,14 +512,25 @@ dependencies = [
"heck", "heck",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.119",
] ]
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.118" version = "2.0.119"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422" checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "syn"
version = "3.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12df2e0110f65b775f769bb17ef989067a1d931b2eb822bd4346631eeada89f9"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -554,29 +549,29 @@ dependencies = [
[[package]] [[package]]
name = "thiserror" name = "thiserror"
version = "2.0.18" version = "2.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" checksum = "ec86235f5fcc2a73650310756d2ac5b138a5780bbbdfae3eeccec992c435ba4f"
dependencies = [ dependencies = [
"thiserror-impl", "thiserror-impl",
] ]
[[package]] [[package]]
name = "thiserror-impl" name = "thiserror-impl"
version = "2.0.18" version = "2.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" checksum = "bc04cd3e1236dd4a98afca4569f2deb3f120e5422a4023be2cb683f8486292af"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 3.0.5",
] ]
[[package]] [[package]]
name = "time" name = "time"
version = "0.3.53" version = "0.3.55"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18dfaaeddcb932337b5e7866ee7d0ce9b76d2fd092997146f187ec09b4558a50" checksum = "cdb87b95ec50ddfa440816d227a17b2ccbdda963a316a727fda0fc4334f7d134"
dependencies = [ dependencies = [
"deranged", "deranged",
"libc", "libc",
@@ -595,9 +590,9 @@ checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109"
[[package]] [[package]]
name = "unicode-ident" name = "unicode-ident"
version = "1.0.24" version = "1.0.25"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" checksum = "ab72a15cf68d77cb0987d3684aa8a45c5ef827e8cb49ee2f30bfd7ba2feb519f"
[[package]] [[package]]
name = "unicode-segmentation" name = "unicode-segmentation"
+7 -4
View File
@@ -1,19 +1,22 @@
[package] [package]
name = "nyanit" name = "nyanit"
version = "0.1.3" version = "0.2.3"
edition = "2024" edition = "2024"
[dependencies] [dependencies]
nix = { version = "0.31", features = ["mount"] }
ratatui = { version = "0.30", default-features = false, features = ["termion"] } ratatui = { version = "0.30", default-features = false, features = ["termion"] }
ratatui-textarea = { version = "0.9", default-features = false, features = ["termion"] } ratatui-textarea = { version = "0.9", default-features = false, features = ["termion"] }
regex = "1.1" regex-lite = "0.1"
termion = "4" termion = "4"
[profile.release] [profile.release]
lto = true lto = true
debug = false debug = false
incremental = false incremental = false
codegen-units = 1 codegen-units = 1
opt-level = 2 opt-level = "s"
strip = true strip = true
[registry]
global-min-publish-age = "14 days"
+13 -4
View File
@@ -8,14 +8,23 @@ And this partition should be formated to bcachefs.
2. Nyanit checks if bcachefs is encrypted, and asks for unlock password if it is. 2. Nyanit checks if bcachefs is encrypted, and asks for unlock password if it is.
3. Then it mounts it to **/mnt/bee-root**. 3. Then it mounts it to **/mnt/real-bee**.
4. And looks for snapshots with name pattern **/mnt/bee-root/bzz_snaps/bzz-yyyy-mm-ddTHH-MM-SS** 4. Now dealing with snapshoots. Its getting complicated because we need to have then outside root subvolume
Root subvolume should be named **bee-root**
Snapshoots made by hive-up should be placed in **bee-root/bzz-snaps** - **/mnt/real-bee/bee-root/bzz-snaps**
We need to transver snapshoot if any from **/mnt/real-bee/bee-root/bzz-snaps** to **/mnt/real-bee/bzz-snaps**
Snapshoots names should match **bzz-yyyy-mm-ddTHH-MM-SS** pattern.
5. If no snapshots are found it calls **switch_root** for **/mnt/bee-root** 4. And looks for snapshots with name pattern **/mnt/real-bee/bzz-snaps/bzz-yyyy-mm-ddTHH-MM-SS**
5. If no snapshots are found it calls **switch_root** for **/mnt/real-bee/bee-root**
6. Othervise vait for F2 for few seconds. 6. Othervise vait for F2 for few seconds.
And if F2 pressed provides list of snapshoots to boot from. And if F2 pressed provides list of snapshoots to boot from.
7. After some snapshoot or just **/mnt/bee-root** is selected, Nyanit doing **switch_root** to it. 7. After some snapshoot or just **/mnt/bee-root/real-bee** is selected, Nyanit doing **switch_root** to it.
8. TO-DO
- add cleaning of old snapshoots.
- add replacing bee-root with snapshoot
+50
View File
@@ -0,0 +1,50 @@
#!/bin/sh
echo "work dir: $1"
_tools_list=(
/usr/bin/bcachefs
/usr/bin/fsck
/usr/bin/fsck.vfat
/usr/bin/switch_root
/usr/bin/blkid
)
workdir=$1
echo "Populate initdramfs tree..."
cd $workdir
mkdir -p initramfs/{dev,proc,sys,run,mnt,mnt/bee_root,tmp,etc,usr,usr/bin,usr/lib}
echo "Create /bin /lib /lib64 simlinks"
cd $workdir/initramfs
ln -s usr/lib lib64
ln -s usr/lib lib
ln -s usr/bin bin
cd $workdir/initramfs/usr
ln -s lib lib64
echo "Install NYANIT..."
cd $workdir
lddtree --bindir / --copy-to-tree $workdir/initramfs init
echo "Copy libs and binaries"
cd $workdir
lddtree --copy-to-tree $workdir/initramfs "${_tools_list[@]}"
echo "Remove setuid flag from everything..."
chmod -R a-s $workdir/initramfs
fakeroot -- sh << EOF
echo "Create console device node"
cd $workdir/initramfs
mknod -m 622 dev/console c 5 1
# Create cpio archive
echo "Create cpio archive"
cd $workdir/initramfs
find . | cpio -ov --format=newc > ../initramfs.cpio
EOF
echo "Done!"
+307 -99
View File
@@ -18,18 +18,19 @@ use ratatui::{Frame, Terminal, backend::TermionBackend};
use ratatui::{ use ratatui::{
layout::{Constraint, Layout}, layout::{Constraint, Layout},
style::{Color, Style, Stylize}, style::{Color, Style, Stylize},
text::Line, text::{Line, Span},
widgets::{Block, Borders, List, ListState, Paragraph}, widgets::{Block, Borders, Gauge, List, ListItem, ListState, Padding, Paragraph, Wrap},
}; };
use ratatui_textarea::TextArea; use ratatui_textarea::TextArea;
use std::io::{self, Read, stdout}; use std::io::{self, stdout};
use std::process::ExitCode; use std::process::ExitCode;
use std::thread; use std::thread;
use std::time::Duration; use std::time::Duration;
use termion::event::{self, Event}; use termion::event::Key;
use termion::input::TermRead;
use termion::raw::IntoRawMode; use termion::raw::IntoRawMode;
mod nyan_system; mod nyan_system;
@@ -37,13 +38,23 @@ mod nyan_system;
use nyan_system::ExecCommandError; use nyan_system::ExecCommandError;
const BEE_CAT: &str = include_str!("bee_cat.txt"); const BEE_CAT: &str = include_str!("bee_cat.txt");
const SNAP_CHOOSE_LIST_CAPTION: &str =
"Select snapshot to rollback or \"bzz current\" to boot current system state";
const CONFIRM_TEXT1: &str = "You going to roll back system statate to ";
const CONFIRM_TEXT2: &str = "This action cannot be undone!";
const TICK_TIME_MILLS: u64 = 27u64;
const WAIT_FOR_F2_SEC: u64 = 3u64;
//const ROOT_PARTLABEL: &str = "bee-root"; //const ROOT_PARTLABEL: &str = "bee-root";
const ROOT_DEV: &str = "/dev/disk/by-partlabel/bee-root"; const ROOT_PARTLABEL: &str = "bee-root";
const TMP_ROOT: &str = "/mnt/bee_root"; const TMP_ROOT: &str = "/mnt/bee-root";
const TMP_ROOT_SNAPS: &str = "/mnt/bee-root/bzz_snaps"; const TMP_ROOT_SNAPS: &str = "/mnt/bee-root/bzz-snaps";
const SYS_INIT: &str = "/sbin/init"; const SYS_INIT: &str = "/sbin/init";
const CURRENT_BOOT_LIST_TEXT: &str = "bzz current";
// fn bee_root_is_present() -> bool { // fn bee_root_is_present() -> bool {
// nyan_system::list_gpt_labeled_devs().iter().filter(|s| s.ends_with(ROOT_PARTLABEL)).count() > 0 // nyan_system::list_gpt_labeled_devs().iter().filter(|s| s.ends_with(ROOT_PARTLABEL)).count() > 0
// } // }
@@ -63,53 +74,76 @@ const SYS_INIT: &str = "/sbin/init";
// ExitCode::SUCCESS // ExitCode::SUCCESS
// } // }
// #[derive(Debug, PartialEq)]
// struct RollbackState {
// progress: f64,
// progress_text: String,
// snapshot_date_time: String,
// roll_iter: nyan_system::RollBackIterator,
// }
// impl RollbackState {
// // Constructor function
// pub fn new(snaps_list: Vec<String>, snap_date_time: String) -> Self {
// RollbackState {
// progress: 0.0,
// progress_text: "".into(),
// snapshot_date_time: snap_date_time.clone(),
// roll_iter: nyan_system::RollBackIterator::new(snaps_list, snap_date_time)
// }
// }
// }
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
enum NyanitState { enum NyanitState {
Init, Init,
WaitForF2(Vec<String>), RollBack,
ChooseSnapshot(Vec<String>), ConfirmRollback(String),
WaitForF2,
/// list of snapshoots
ChooseSnapshot,
EnterPassword, EnterPassword,
Error(String), Error(String),
HandOff(String), HandOff,
} }
/// Mounting bee-root and chiking if there any snapshots ///
fn mount_and_check_snaps() -> Result<NyanitState, ExecCommandError> { /// Generates confirm dialog paragraph
// mount bee-root ///
nyan_system::mount_bee_root(TMP_ROOT, ROOT_DEV)?; fn gen_conf_par<'a>(snap_dt: String) -> Paragraph<'a> {
let first_line = Line::from(vec![Span::raw(CONFIRM_TEXT1), Span::raw(snap_dt).bold()]);
let confirm_line = Line::from(vec![
Span::raw("Press ("),
Span::raw("Y").bold(),
Span::raw(") to confirm or ("),
Span::raw("N").bold(),
Span::raw(") to decline."),
]);
// list snaps if any let conf_lines: Vec<Line> = vec![
let mut snaps_list = nyan_system::list_bzzpss_snaps(TMP_ROOT_SNAPS)?; first_line,
// if any snaps detected whait for user to press F2 Line::default(),
if snaps_list.len() > 0 { Line::from(CONFIRM_TEXT2).bold(),
snaps_list.insert(0, TMP_ROOT.to_string()); Line::default(),
return Ok(NyanitState::WaitForF2(snaps_list)); confirm_line,
} ];
Paragraph::new(conf_lines)
// else tell to finish boot .centered()
Ok(NyanitState::HandOff(TMP_ROOT.into())) .wrap(Wrap::default())
} .light_red()
fn init_stage() -> Result<NyanitState, ExecCommandError> {
// mount sysfss
nyan_system::prepare_env().map_err(|ex| format!("prepare_env error: {}", ex))?;
// check if device is encrypted
if nyan_system::bzzpss_dev_encrypted(ROOT_DEV).map_err(|ex| format!("Error checking if device is encrypted: {}", ex))? {
// if encrypted return EnterPassword so used get promted for pass
return Ok(NyanitState::EnterPassword);
}
// if unencrypted
mount_and_check_snaps()
} }
struct NyanitTUI<'a> { struct NyanitTUI<'a> {
exit: bool, exit: bool,
state: NyanitState, state: NyanitState,
bee_root_dev: nyan_system::BlkIdRes,
list_state: ListState, list_state: ListState,
time_counter: u32, /// time counter manly for F2 waiting in mills
time_counter: u64,
/// time counter manly for F2 waiting in seconds
time_counter_sec: u64,
textarea: Option<TextArea<'a>>, textarea: Option<TextArea<'a>>,
snaps_list: Vec<String>,
roll_state: Option<nyan_system::RollBackIterator>,
} }
impl NyanitTUI<'_> { impl NyanitTUI<'_> {
@@ -117,29 +151,91 @@ impl NyanitTUI<'_> {
Ok(Self { Ok(Self {
exit: false, exit: false,
state: NyanitState::Init, state: NyanitState::Init,
bee_root_dev: nyan_system::BlkIdRes {
fs_type: String::new(),
dev_node: String::new(),
},
list_state: ListState::default().with_selected(Some(0)), list_state: ListState::default().with_selected(Some(0)),
time_counter: 0, time_counter: 0,
time_counter_sec: 0,
textarea: None, textarea: None,
snaps_list: Vec::new(),
roll_state: None,
}) })
} }
// pub fn bzz_unwrap<T,E>(&mut self, val: Result<T,E>) -> T { /// Mounting bee-root and chiking if there any snapshots
// match val { fn mount_and_check_snaps(&mut self) -> Result<NyanitState, ExecCommandError> {
// Ok(v) => v, // mount bee-root
// } nyan_system::mount_bee_root(&self.bee_root_dev, TMP_ROOT)?;
// }
// let root_list = nyan_system::list_dir(TMP_ROOT)?;
// return Err(ExecCommandError::Other(root_list));
if self.bee_root_dev.is_bcachefs() {
// list snaps if any
self.snaps_list = nyan_system::list_bzzpss_snaps(TMP_ROOT_SNAPS)?;
// if any snaps detected check hive DB update_status
if self.snaps_list.len() > 0 {
// if pac_lock accessible and exist - rolling back
if let Ok(Some(snap_date_time)) = nyan_system::check_pac_lock()
&& self.snaps_list.contains(&snap_date_time)
{
Ok(NyanitState::ConfirmRollback(snap_date_time))
}
// else user choose what to do
else {
Ok(NyanitState::WaitForF2)
}
}
// else tell to finish boot
else {
Ok(NyanitState::HandOff)
}
} else {
Ok(NyanitState::HandOff)
}
}
fn init_stage(&mut self) -> Result<NyanitState, ExecCommandError> {
// mount sysfss
nyan_system::prepare_env().map_err(|ex| format!("prepare_env error: {}", ex))?;
// get bee-root device name aka /dev/sda1
// trying for max 3 sec
self.bee_root_dev = nyan_system::try_find_partlabel(ROOT_PARTLABEL, 3)
.ok_or_else(|| "Timed out trying to find dev by partlabel.".to_string())?;
if self.bee_root_dev.is_bcachefs() {
// check if device is encrypted
if nyan_system::bzzpss_dev_encrypted(&self.bee_root_dev.dev_node)
.map_err(|ex| format!("Error checking if device is encrypted: {}", ex))?
{
// if encrypted return EnterPassword so user get promted for pass
return Ok(NyanitState::EnterPassword);
}
}
// if unencrypted
let check_res = self.mount_and_check_snaps()?;
Ok(check_res)
}
/// runs the application's main loop until the user quits /// runs the application's main loop until the user quits
pub fn run(&mut self) -> io::Result<()> { pub fn run(&mut self) -> io::Result<()> {
self.state = match init_stage() { self.state = match self.init_stage() {
Ok(st) => st, Ok(res) => res,
Err(ex) => NyanitState::Error(format!("Error while trying to bzzz-pss things: {}", ex)), Err(ex) => {
NyanitState::Error(format!("Error while trying to bzzz-pss things:\n{}", ex))
}
}; };
// if drive unencrypted and there is no root snapshots finish boot // if drive unencrypted and there is no root snapshots finish boot
match &self.state { match &self.state {
// if drive unencrypted and there is no root snapshots finish boot // if drive unencrypted and there is no root snapshots finish boot
NyanitState::HandOff(root_mnt) => { NyanitState::HandOff => {
if let Err(ex) = nyan_system::hand_off_control(root_mnt, SYS_INIT) { if let Err(ex) = nyan_system::hand_off_control(TMP_ROOT, SYS_INIT) {
self.state = NyanitState::Error(ex.to_string()) self.state = NyanitState::Error(ex.to_string())
} }
} }
@@ -148,9 +244,13 @@ impl NyanitTUI<'_> {
let mut textarea: TextArea<'_> = TextArea::default(); let mut textarea: TextArea<'_> = TextArea::default();
textarea.set_cursor_line_style(Style::default()); textarea.set_cursor_line_style(Style::default());
textarea.set_mask_char('\u{1F41D}'); //U+2022 BULLET (•) textarea.set_mask_char('\u{1F41D}'); //U+2022 BULLET (•)
textarea.set_placeholder_text("Please enter your password"); textarea.set_placeholder_text("Please enter your disk unlock password");
textarea.set_style(Style::default().fg(Color::LightYellow)); textarea.set_style(Style::default().fg(Color::LightYellow));
textarea.set_block(Block::default().borders(Borders::ALL).title("Password")); textarea.set_block(
Block::default()
.borders(Borders::ALL)
.title(" System disk unlock password "),
);
self.textarea = Some(textarea); self.textarea = Some(textarea);
} }
_ => (), _ => (),
@@ -164,7 +264,7 @@ impl NyanitTUI<'_> {
let mut terminal = Terminal::new(backend)?; let mut terminal = Terminal::new(backend)?;
// init tty async input and start actually drawing things now! // init tty async input and start actually drawing things now!
let mut tty_input_async = termion::async_stdin().bytes(); let mut tty_input_async = termion::async_stdin().keys();
self.time_counter = 0; self.time_counter = 0;
while !self.exit { while !self.exit {
@@ -209,67 +309,151 @@ impl NyanitTUI<'_> {
match &self.state { match &self.state {
NyanitState::Error(ex) => { NyanitState::Error(ex) => {
// render big red wolf in the center with err messaghe // render big red wolf in the center with err messaghe
let err_paragraph = Paragraph::new(ex.as_str()).centered().light_red().bold(); let err_paragraph = Paragraph::new(ex.as_str())
.centered()
.wrap(Wrap::default())
.light_red()
.bold();
frame.render_widget(err_paragraph, vertical_chunks[1]); frame.render_widget(err_paragraph, vertical_chunks[1]);
} }
NyanitState::WaitForF2(_) => { NyanitState::WaitForF2 => {
// show press F2 for options text // show press F2 for options text
let f2_paragraph = Paragraph::new("Press F2 for options") let f2_lines: Vec<Line> = vec![
.centered() Line::from("Press F2 for options"),
.magenta() Line::default(),
.bold(); Line::from((WAIT_FOR_F2_SEC.saturating_sub(self.time_counter)).to_string()),
];
let f2_paragraph = Paragraph::new(f2_lines).centered().magenta().bold();
frame.render_widget(f2_paragraph, vertical_chunks[1]); frame.render_widget(f2_paragraph, vertical_chunks[1]);
} }
NyanitState::ChooseSnapshot(snaps_list) => { NyanitState::ChooseSnapshot => {
// render menu for snap choosing // 1. Split main chank to make space for description
let snap_list_chunks = Layout::vertical([
Constraint::Length(2), // Description test
Constraint::Fill(1), // to shrink unused space
])
.split(vertical_chunks[1]);
let caption_paragraph = Paragraph::new(SNAP_CHOOSE_LIST_CAPTION)
.centered()
.wrap(Wrap::default())
.light_red()
.bold();
frame.render_widget(caption_paragraph, snap_list_chunks[0]);
// construct items list to get proper text size
let mut list_items: Vec<ListItem> = vec![ListItem::new(CURRENT_BOOT_LIST_TEXT)];
self.snaps_list
.iter()
.for_each(|s| list_items.push(ListItem::new(s.as_str())));
let max_width = 2 + list_items.iter().map(|i| i.width()).max().unwrap_or(0) as u16;
let render_list = List::new(list_items)
.highlight_symbol(">>")
.highlight_style(Style::new().bold().on_light_red());
frame.render_stateful_widget( frame.render_stateful_widget(
List::new(snaps_list.iter().map(String::as_str)) render_list,
.block(Block::default().borders(Borders::ALL)) snap_list_chunks[1].centered_horizontally(Constraint::Max(max_width)),
.highlight_symbol(">>")
.highlight_style(Style::new().bold()),
vertical_chunks[1],
&mut self.list_state, &mut self.list_state,
); );
} }
NyanitState::EnterPassword => { NyanitState::ConfirmRollback(snap_dt) => {
frame.render_widget(self.textarea.as_ref().unwrap(), vertical_chunks[1]); let par1 = gen_conf_par(snap_dt.clone());
frame.render_widget(par1, vertical_chunks[1]);
} }
NyanitState::HandOff(_) => { NyanitState::RollBack => {
let ok_text = Paragraph::new("Booting\nKira Linux\n \u{1F41D} \u{1F41D} :3") let centered = vertical_chunks[1].centered_vertically(Constraint::Length(9));
.block(Block::bordered().light_green()) // 1. Create the outer block
let block = Block::default()
.padding(Padding::proportional(1))
.borders(Borders::ALL)
.light_red()
.title(" Rolling back system state ");
// 2. Get the inner area of the block
let block_inner_area = block.inner(centered);
frame.render_widget(block, centered);
// 3. Split the inner area into two parts: top for text, bottom for gauge
let chunks = Layout::vertical([
Constraint::Length(3), // gauge
Constraint::Length(1), // spacing
Constraint::Length(1), // Height of the text paragraph
])
.split(block_inner_area);
let gauge = Gauge::default()
.gauge_style(Style::default().fg(Color::LightRed))
.ratio(self.roll_state.as_ref().unwrap().progress);
frame.render_widget(gauge, chunks[0]);
let text = Paragraph::new(self.roll_state.as_ref().unwrap().progress_text.clone())
.centered();
frame.render_widget(text, chunks[2]);
}
NyanitState::EnterPassword => {
frame.render_widget(
self.textarea.as_ref().unwrap(),
vertical_chunks[1]
.centered_vertically(Constraint::Max(3))
.centered_horizontally(Constraint::Percentage(75)),
);
}
NyanitState::HandOff => {
let ok_text = Paragraph::new("Booting\nKira Linux\n\u{1F41D} \u{1F41D} :3")
.centered()
.light_green(); .light_green();
frame.render_widget(ok_text, vertical_chunks[1]); frame.render_widget(
// just some time to look at message ok_text,
vertical_chunks[1].centered_vertically(Constraint::Length(3)),
);
// just some time to look at beez
thread::sleep(Duration::from_millis(1000)) thread::sleep(Duration::from_millis(1000))
} }
_ => (), _ => (),
}; };
} }
fn handle_events(&mut self, tty_input: &mut io::Bytes<termion::AsyncReader>) -> io::Result<()> { fn handle_events(
&mut self,
tty_input: &mut termion::input::Keys<termion::AsyncReader>,
) -> io::Result<()> {
// cheking if there is any input to read // cheking if there is any input to read
if let Some(item) = tty_input.next() { if let Some(item) = tty_input.next() {
let read_byte = item?; let e = item?;
let e = event::parse_event(read_byte, tty_input)?;
match &self.state { match &self.state {
// finishing boot // finishing boot
NyanitState::HandOff(root_mnt) => { // we allways booting to mnt rootnow
if let Err(ex) = nyan_system::hand_off_control(root_mnt, SYS_INIT) { NyanitState::HandOff => {
if let Err(ex) = nyan_system::hand_off_control(TMP_ROOT, SYS_INIT) {
self.state = NyanitState::Error(ex.to_string()) self.state = NyanitState::Error(ex.to_string())
} }
} }
NyanitState::WaitForF2(vol_list) => { NyanitState::WaitForF2 => {
if e == Event::Key(event::Key::F(2)) { if e == Key::F(2) {
self.state = NyanitState::ChooseSnapshot(vol_list.clone()); self.state = NyanitState::ChooseSnapshot;
} }
} }
NyanitState::ChooseSnapshot(vol_list) => match e { NyanitState::ChooseSnapshot => match e {
Event::Key(event::Key::Down) => self.list_state.select_next(), Key::Down => self.list_state.select_next(),
Event::Key(event::Key::Up) => self.list_state.select_previous(), Key::Up => self.list_state.select_previous(),
Event::Key(event::Key::Char('\n')) => { Key::Char('\n') => {
if let Some(idx) = self.list_state.selected() { if let Some(idx) = self.list_state.selected() {
self.state = NyanitState::HandOff(vol_list[idx].clone()); // if try to boot into current state
if idx == 0 {
self.state = NyanitState::HandOff;
}
// else we going to restore selected snapshoot as current state
else {
// idx-1 because self.snaps_list do not contains current system, only snapshots date time
self.state =
NyanitState::ConfirmRollback(self.snaps_list[idx - 1].clone());
}
} }
} }
_ => (), _ => (),
@@ -277,13 +461,16 @@ impl NyanitTUI<'_> {
NyanitState::EnterPassword => { NyanitState::EnterPassword => {
match e { match e {
// try to unlock if enter pressed // try to unlock if enter pressed
Event::Key(event::Key::Char('\n')) => { Key::Char('\n') => {
let password = self.textarea.as_ref().unwrap().lines()[0].clone(); let password = self.textarea.as_ref().unwrap().lines()[0].clone();
match nyan_system::bcachefs_unlock(password, ROOT_DEV) { match nyan_system::bcachefs_unlock(
password,
&self.bee_root_dev.dev_node,
) {
Ok(unlocked) => { Ok(unlocked) => {
if unlocked { if unlocked {
// need to check for subvolumes and stuff // need to check for subvolumes and stuff
self.state = match mount_and_check_snaps() { self.state = match self.mount_and_check_snaps() {
Ok(st) => { Ok(st) => {
self.time_counter = 0; self.time_counter = 0;
st st
@@ -318,29 +505,50 @@ impl NyanitTUI<'_> {
} }
} }
} }
NyanitState::ConfirmRollback(snap_dt) => match e {
Key::Char('Y') | Key::Char('y') => {
self.roll_state = Some(nyan_system::RollBackIterator::new(
self.snaps_list.clone(),
snap_dt.clone(),
));
self.state = NyanitState::RollBack;
}
Key::Char(_) => self.state = NyanitState::ChooseSnapshot,
_ => (),
},
_ => (), _ => (),
} }
} else { } else {
// Tick time // Tick time
thread::sleep(Duration::from_millis(500)); thread::sleep(Duration::from_millis(TICK_TIME_MILLS));
match self.state {
if let NyanitState::WaitForF2(_) = self.state { NyanitState::WaitForF2 => {
self.time_counter = self.time_counter.wrapping_add(1u32); self.time_counter = self.time_counter.wrapping_add(TICK_TIME_MILLS);
// 2.5 sec wait self.time_counter_sec = self.time_counter / 1000;
if self.time_counter > 5 { // wait for F2
// boot from root if self.time_counter_sec > WAIT_FOR_F2_SEC {
self.state = NyanitState::HandOff(TMP_ROOT.into()); // boot from root
self.state = NyanitState::HandOff;
}
} }
NyanitState::RollBack => match self.roll_state.as_mut().unwrap().next() {
Some(Err(ex)) => {
self.state = NyanitState::Error(format!("ROLL BACK ERROR!: {}", ex));
}
None => {
self.state = NyanitState::HandOff;
}
_ => (),
},
_ => (),
} }
} }
Ok(()) Ok(())
} }
} }
fn main() -> ExitCode { fn main() -> ExitCode {
nyan_system::set_env_vars_unsafe(); // nyan_system::set_env_vars_unsafe();
let mut app = NyanitTUI::new().unwrap(); let mut app = NyanitTUI::new().unwrap();
app.run().unwrap(); app.run().unwrap();
// Terminal restores automatically when `stdout` (RawTerminal) is dropped // Terminal restores automatically when `stdout` (RawTerminal) is dropped
+590 -54
View File
@@ -18,8 +18,31 @@
Functions fot system interaction Functions fot system interaction
*/ */
use nix;
use std::fs; use std::fs;
use std::process::Command; use std::process::Command;
use regex_lite::Regex;
/// timeout for block deices population
const WAIT_FOR_BLK_DEVS: u64 = 5u64;
/// list of subvolumes to move from current system to snapshoot when being restored
const SBUVOL_TO_MOVE: [(&str, &str); 1] = [("var", "/tmp")];
const TMP_ROOT: &str = "/mnt/bee-root";
const TMP_ROOT_SNAPS: &str = "/mnt/bee-root/bzz-snaps";
const TMP_ROOT_USR: &str = "/mnt/bee-root/usr";
const TMP_ROOT_VAR: &str = "/mnt/bee-root/var";
/// Path where /usr subvolume got moved on snapshot restore
const TMP_ROOT_SNAPS_USR: &str = "/mnt/bee-root/bzz-snaps/tmp_usr";
/// Path where /var subvolume got moved on snapshot restore
const TMP_ROOT_SNAPS_VAR: &str = "/mnt/bee-root/bzz-snaps/tmp_var";
const TMP_ROOT_SNAPS_PREFIX: &str = "/mnt/bee-root/bzz-snaps/tmp_";
const PAC_LOCK_FILE: &str = "/var/lib/hive-up/pac-lock";
#[derive(Debug)] #[derive(Debug)]
pub struct CommandFailed { pub struct CommandFailed {
@@ -81,6 +104,12 @@ impl From<String> for ExecCommandError {
} }
} }
impl From<&str> for ExecCommandError {
fn from(err: &str) -> Self {
ExecCommandError::Other(err.to_string())
}
}
impl std::fmt::Display for ExecCommandError { impl std::fmt::Display for ExecCommandError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self { match self {
@@ -109,6 +138,91 @@ pub fn exec_command(cmd: &str, args: &Vec<&str>) -> Result<(), ExecCommandError>
} }
} }
#[derive(PartialEq)]
pub struct BlkIdRes {
pub fs_type: String,
pub dev_node: String,
}
impl BlkIdRes {
pub fn is_bcachefs(&self) -> bool {
if self.fs_type == "bcachefs" {
true
}
else {
false
}
}
}
///
/// Will block up to timeout_secs
///
pub fn try_find_partlabel(partlabel: &str, timeout_secs: u64) -> Option<BlkIdRes> {
use std::{thread, time::Duration};
let start = std::time::Instant::now();
while start.elapsed().as_secs() < timeout_secs {
if let Ok(Some(label)) = find_partlabel(partlabel) {
return Some(label);
}
thread::sleep(Duration::from_millis(100));
}
None
}
/// Gets device node aka /dev/sda1 from gpr partition label
/// blkid -s PARTLABEL --match-token PARTLABEL="bzz-primary"
pub fn find_partlabel(partlabel: &str) -> Result<Option<BlkIdRes>, ExecCommandError> {
let out = Command::new("/usr/bin/blkid")
.args([
"-s",
"TYPE",
"-s",
"PARTLABEL",
"-t",
format!("PARTLABEL=\"{}\"", partlabel).as_str(),
])
.output()?;
if out.status.success() {
let re = Regex::new(r#"^(/dev/[a-z0-9-]+): TYPE="([a-z]+)" PARTLABEL="[a-z]+"$"#)
.unwrap();
let out_text = String::from_utf8(out.stdout)?;
if let Some(captured) = re.captures(&out_text) {
// let part_no = captured[1];
// let parent_dev = captured[2];
let res = BlkIdRes{ fs_type: captured[2].to_string(), dev_node: captured[1].to_string()};
Ok(Some(res))
} else {
Err(ExecCommandError::Other(format!("Unexpected blkid output format: \"{}\"", out_text)))
}
} else {
if let Some(code) = out.status.code()
&& code == 2
{
Ok(None)
} else {
Err(ExecCommandError::Other(format!(
"Error while looking for partlabel={}: \"{}\"",
partlabel,
String::from_utf8(out.stderr).unwrap_or_else(|ex| ex.to_string())
)))
}
}
}
// pub fn list_dir(dirname: &str) -> Result<String, ExecCommandError> {
// let res: String = fs::read_dir(dirname)?
// .filter_map(|entry| entry.ok())
// .map(|entry| entry.file_name().to_string_lossy().to_string())
// .collect::<Vec<String>>()
// .join("\n");
// Ok(res)
// }
/// exec given command with arguments, returning stdout parsed to UTF-8 string /// exec given command with arguments, returning stdout parsed to UTF-8 string
/// capturing stderr in case or failure /// capturing stderr in case or failure
// pub fn exec_command_out(cmd: &str, args: &Vec<&str>) -> Result<String, ExecCommandError> { // pub fn exec_command_out(cmd: &str, args: &Vec<&str>) -> Result<String, ExecCommandError> {
@@ -120,21 +234,6 @@ pub fn exec_command(cmd: &str, args: &Vec<&str>) -> Result<(), ExecCommandError>
// } // }
// } // }
// ls /dev/disk/by-partlabel/
// here we can find gpt part labels
// pub fn list_gpt_labeled_devs() -> Result<Vec<String>, ExecCommandError> {
// use std::os::unix::fs::FileTypeExt;
// let mut res: Vec<String> = Vec::new();
// for maybe_entry in fs::read_dir("/dev/disk/by-partlabel/")? {
// let entry = maybe_entry?;
// if entry.file_type()?.is_block_device() {
// res.push(entry.path().to_str().unwrap().to_string());
// }
// }
// Ok(res)
// }
/// bcachefs unlock --file=/tmp/bzzpsspass.txt /dev/loop0somedev /// bcachefs unlock --file=/tmp/bzzpsspass.txt /dev/loop0somedev
/// Ok so. It is stupid, because bcacheutils far from ideal. /// Ok so. It is stupid, because bcacheutils far from ideal.
/// But if we provide wrong password from file or stdin this stuff plays stupid and asks for right password. /// But if we provide wrong password from file or stdin this stuff plays stupid and asks for right password.
@@ -147,18 +246,24 @@ pub fn bcachefs_unlock(pass: String, dev: &str) -> Result<bool, ExecCommandError
use std::process::Stdio; use std::process::Stdio;
// "-k", "session" is important as it stores key in sessyin keyring what allows // "-k", "session" is important as it stores key in sessyin keyring what allows
// system to mount filesystem after switch_root // system to mount filesystem after switch_root
let mut child = Command::new("bcachefs") let mut child = Command::new("/usr/bin/bcachefs")
.args(&vec!["unlock", "-k", "session", dev]) .args(&vec!["unlock", "-k", "session", dev])
.stdin(Stdio::piped()) .stdin(Stdio::piped())
.stdout(Stdio::piped()) .stdout(Stdio::piped())
.stderr(Stdio::piped()) .stderr(Stdio::piped())
.spawn()?; .spawn()?;
let mut c_stderr = child.stderr.take().ok_or_else(||{"Unable to get child process STDERR".to_string()})?; let mut c_stderr = child
.stderr
.take()
.ok_or_else(|| "Unable to get child process STDERR".to_string())?;
// Write password to STDIN in a limited scope // Write password to STDIN in a limited scope
{ {
let mut c_stdin = child.stdin.take().ok_or_else(||{"Unable to get child process STDIN".to_string()})?; let mut c_stdin = child
.stdin
.take()
.ok_or_else(|| "Unable to get child process STDIN".to_string())?;
// neccessery to use thread or we may be deadlocking // neccessery to use thread or we may be deadlocking
c_stdin.write_all(pass.as_bytes())?; c_stdin.write_all(pass.as_bytes())?;
let bytes_written = c_stdin.write(b"\n")?; let bytes_written = c_stdin.write(b"\n")?;
@@ -184,87 +289,518 @@ pub fn bcachefs_unlock(pass: String, dev: &str) -> Result<bool, ExecCommandError
/// Cheking if device is encripted /// Cheking if device is encripted
pub fn bzzpss_dev_encrypted(dev_name: &str) -> Result<bool, ExecCommandError> { pub fn bzzpss_dev_encrypted(dev_name: &str) -> Result<bool, ExecCommandError> {
match exec_command("bcachefs", &vec!["unlock", "--check", dev_name]) { match exec_command("/usr/bin/bcachefs", &vec!["unlock", "--check", dev_name]) {
Ok(_) => Ok(true), // if status OK - dev encrypted Ok(_) => Ok(true), // if status OK - dev encrypted
Err(ExecCommandError::CommandFailed(_)) => Ok(false), // if bcachefs returned error - unencrypted Err(ExecCommandError::CommandFailed(_)) => Ok(false), // if bcachefs returned error - unencrypted
Err(ex) => Err(ex), // othervice its error Err(ex) => Err(ex), // othervice its error
} }
} }
//const TMP_ROOT: &str = "/mnt/bee-root"; ///
/// Looking for snapshots in snaps_dir for usr and var
/// And return list of date-time part of filename if both snapshots exists
///
pub fn list_bzzpss_snaps(snaps_dir: &str) -> Result<Vec<String>, ExecCommandError> { pub fn list_bzzpss_snaps(snaps_dir: &str) -> Result<Vec<String>, ExecCommandError> {
use regex::Regex; use std::path::Path;
// root snapshoot in a form of bzz-yyyy-mm-ddTHH-MM-SS
let re = Regex::new(r"^bzz-[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}-[0-9]{2}-[0-9]{2}$").unwrap(); // usr or var snapshoot in a form of bzz-usr|var-yyyy-mm-ddTHH-MM-SS
let re = Regex::new(r"^bzz-(usr|var)-([0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}-[0-9]{2}-[0-9]{2})$")
.unwrap();
let mut res: Vec<String> = Vec::new(); let mut res: Vec<String> = Vec::new();
// if snaps dir path do not exist return empty list
if !Path::new(snaps_dir).exists() {
return Ok(res);
}
// contains only date-time part
let mut usr_snaps: Vec<String> = Vec::new();
let mut var_snaps: Vec<String> = Vec::new();
// collect snapshots date-time
for maybe_entry in fs::read_dir(snaps_dir)? { for maybe_entry in fs::read_dir(snaps_dir)? {
let entry = maybe_entry?; let entry = maybe_entry?;
if entry.file_type()?.is_dir() { if entry.file_type()?.is_dir() {
if let Some(dir_name) = entry.file_name().to_str() if let Some(dir_name) = entry.file_name().to_str() {
&& re.is_match(dir_name) if let Some(captured) = re.captures(&dir_name) {
{ let subvol_name = &captured[1];
res.push(dir_name.to_string()); let date_time = captured[2].to_string();
if subvol_name == "usr" {
usr_snaps.push(date_time);
} else {
var_snaps.push(date_time);
}
}
} }
} }
} }
// now we need to make sure snapshots of /usr and /var both exist
// just fools safe, may reconsider in future
usr_snaps.sort();
for snap_time in usr_snaps {
if var_snaps.contains(&snap_time) {
res.push(snap_time);
}
}
Ok(res) Ok(res)
} }
///
/// creates rw snapshot of "subvolume" and places it into "target"
/// bcachefs subvolume snapshot --rw /subvolume /target
///
pub fn snapshot_rw(subvolume: &str, target: &str) -> Result<(), ExecCommandError> {
exec_command(
"bcachefs",
&vec!["subvolume", "snapshot", "--rw", subvolume, target],
)?;
Ok(())
}
///
/// bcachefs subvolume delete shortcut
///
pub fn subvolume_delete(subvolume: &str) -> Result<(), ExecCommandError> {
exec_command("bcachefs", &vec!["subvolume", "delete", subvolume])?;
Ok(())
}
///
/// Helper function to get snapshot names in form of "bzz-(usr|var)-date-time"
/// vol: "usr" or "var"
/// dt: datetime
///
// fn snap_name_from_dt(vol: &str, dt: &str) -> String {
// format!("bzz-{}-{}", vol, dt)
// }
#[derive(Debug, PartialEq)]
enum RollbackIterStates {
Start,
RenameUsrVarAct,
ChangeToRw,
MoveSubvolums,
RemoveOldVols,
ClearSnaps,
Finish,
Err,
}
// 1. Define the iterator struct
#[derive(Debug, PartialEq)]
pub struct RollBackIterator {
pub progress: f64,
pub progress_text: String,
snaps_list: Vec<String>,
target_snap: String,
usr_ro_snap_path: String,
var_ro_snap_path: String,
state: RollbackIterStates,
}
impl RollBackIterator {
pub fn new(snaps_list: Vec<String>, target_snap: String) -> Self {
let usr_ro_snap_path = format!("{}/bzz-{}-{}", TMP_ROOT_SNAPS, "usr", &target_snap);
let var_ro_snap_path = format!("{}/bzz-{}-{}", TMP_ROOT_SNAPS, "var", &target_snap);
Self {
progress: 0.0,
progress_text: "Starting...".into(),
snaps_list: snaps_list,
target_snap: target_snap,
usr_ro_snap_path: usr_ro_snap_path,
var_ro_snap_path: var_ro_snap_path,
state: RollbackIterStates::Start,
}
}
}
// 2. Implement Iterator for the iterator struct
impl Iterator for RollBackIterator {
type Item = Result<f64, ExecCommandError>;
fn next(&mut self) -> Option<Self::Item> {
match self.state {
RollbackIterStates::Start => {
self.state = RollbackIterStates::RenameUsrVarAct;
self.progress = 0.0;
self.progress_text = "Move usr and var to temporary location".into();
Some(Ok(self.progress))
}
RollbackIterStates::RenameUsrVarAct => {
let move_res = fs::rename(TMP_ROOT_USR, TMP_ROOT_SNAPS_USR)
.and_then(|_| fs::rename(TMP_ROOT_VAR, TMP_ROOT_SNAPS_VAR));
if let Err(ex) = move_res {
self.state = RollbackIterStates::Err;
Some(Err(ex.into()))
} else {
self.state = RollbackIterStates::ChangeToRw;
self.progress_text = "Setting snapshot as current system ...".into();
self.progress = 0.1;
Some(Ok(self.progress))
}
}
RollbackIterStates::ChangeToRw => {
let rw_res = snapshot_rw(&self.usr_ro_snap_path, TMP_ROOT_USR)
.and_then(|_| snapshot_rw(&self.var_ro_snap_path, TMP_ROOT_VAR));
if let Err(ex) = rw_res {
self.state = RollbackIterStates::Err;
Some(Err(ex))
} else {
self.state = RollbackIterStates::MoveSubvolums;
self.progress_text =
"Moving subvolumes from old /usr and /var to new one".into();
self.progress = 0.2;
Some(Ok(self.progress))
}
}
RollbackIterStates::MoveSubvolums => {
self.progress = 0.3;
self.progress_text = "Removing old /usr and /var".into();
let mut mv_res = Some(Ok(self.progress));
for (vol, svol_path) in SBUVOL_TO_MOVE {
let v_from = format!("{}{}{}", TMP_ROOT_SNAPS_PREFIX, vol, svol_path);
let v_dest = format!("{}/{}{}", TMP_ROOT, vol, svol_path);
if let Err(ex) = fs::rename(v_from, v_dest) {
self.state = RollbackIterStates::Err;
mv_res = Some(Err(ex.into()));
break;
}
}
self.state = RollbackIterStates::RemoveOldVols;
mv_res
}
RollbackIterStates::RemoveOldVols => {
let rem_old_res = subvolume_delete(TMP_ROOT_SNAPS_USR)
.and_then(|_| subvolume_delete(TMP_ROOT_SNAPS_VAR));
if let Err(ex) = rem_old_res {
self.state = RollbackIterStates::Err;
Some(Err(ex))
} else {
self.state = RollbackIterStates::ClearSnaps;
self.progress_text = "Clearing snapshots...".into();
self.progress = 0.6;
Some(Ok(self.progress))
}
}
RollbackIterStates::ClearSnaps => {
self.progress = 1.0;
self.progress_text = "All done!".into();
let mut clr_snap_res = Some(Ok(self.progress));
for snap_date_time in self.snaps_list.iter().rev() {
let usr_snap = format!("{}/bzz-{}-{}", TMP_ROOT_SNAPS, "usr", snap_date_time);
let var_snap = format!("{}/bzz-{}-{}", TMP_ROOT_SNAPS, "usr", snap_date_time);
if let Err(ex) =
subvolume_delete(&usr_snap).and_then(|_| subvolume_delete(&var_snap))
{
clr_snap_res = Some(Err(ex));
self.state = RollbackIterStates::Err;
break;
}
if snap_date_time == &self.target_snap {
self.state = RollbackIterStates::Finish;
break;
}
}
clr_snap_res
}
RollbackIterStates::Finish => None,
RollbackIterStates::Err => None,
}
}
}
// pub fn roll_back_snap(
// snaps_list: Vec<String>,
// target_snap: String,
// ) -> impl Iterator<Item = Result<String, ExecCommandError>> {
// let usr_ro_snap_path = format!("{}/bzz-{}-{}", TMP_ROOT_SNAPS, "usr", target_snap);
// let var_ro_snap_path = format!("{}/bzz-{}-{}", TMP_ROOT_SNAPS, "usr", target_snap);
// let mut roll_state = RollbackStates::Start;
// std::iter::from_fn(move || match roll_state {
// RollbackStates::Start => {
// roll_state = RollbackStates::RenameUsrVarAct;
// Some(Ok("Move usr and var to temporary location".into()))
// }
// RollbackStates::RenameUsrVarAct => {
// let move_res = fs::rename(TMP_ROOT_USR, TMP_ROOT_SNAPS_USR)
// .and_then(|_| fs::rename(TMP_ROOT_VAR, TMP_ROOT_SNAPS_VAR));
// if let Err(ex) = move_res {
// roll_state = RollbackStates::Err;
// Some(Err(ex.into()))
// } else {
// roll_state = RollbackStates::ChangeToRw;
// Some(Ok("Setting snapshot as current system ...".into()))
// }
// }
// RollbackStates::ChangeToRw => {
// let rw_res = snapshot_rw(&usr_ro_snap_path, TMP_ROOT_USR)
// .and_then(|_| snapshot_rw(&var_ro_snap_path, TMP_ROOT_VAR));
// if let Err(ex) = rw_res {
// roll_state = RollbackStates::Err;
// Some(Err(ex))
// } else {
// roll_state = RollbackStates::MoveSubvolums;
// Some(Ok(
// "Moving subvolumes from old /usr and /var to new one".into()
// ))
// }
// }
// RollbackStates::MoveSubvolums => {
// let mut mv_res = Some(Ok("Removing old /usr and /var".into()));
// for (vol, svol_path) in SBUVOL_TO_MOVE {
// let v_from = format!("{}{}{}", TMP_ROOT_SNAPS_PREFIX, vol, svol_path);
// let v_dest = format!("{}/{}{}", TMP_ROOT, vol, svol_path);
// if let Err(ex) = fs::rename(v_from, v_dest) {
// roll_state = RollbackStates::Err;
// mv_res = Some(Err(ex.into()));
// break;
// }
// }
// roll_state = RollbackStates::RemoveOldVols;
// mv_res
// }
// RollbackStates::RemoveOldVols => {
// let rem_old_res = subvolume_delete(TMP_ROOT_SNAPS_USR)
// .and_then(|_| subvolume_delete(TMP_ROOT_SNAPS_VAR));
// if let Err(ex) = rem_old_res {
// roll_state = RollbackStates::Err;
// Some(Err(ex))
// } else {
// roll_state = RollbackStates::ClearSnaps;
// Some(Ok("Clearing snapshots...".into()))
// }
// }
// RollbackStates::ClearSnaps => {
// let mut clr_snap_res = Some(Ok("All done!".into()));
// for snap_date_time in snaps_list.iter().rev() {
// let usr_snap = format!("{}/bzz-{}-{}", TMP_ROOT_SNAPS, "usr", snap_date_time);
// let var_snap = format!("{}/bzz-{}-{}", TMP_ROOT_SNAPS, "usr", snap_date_time);
// if let Err(ex) =
// subvolume_delete(&usr_snap).and_then(|_| subvolume_delete(&var_snap))
// {
// clr_snap_res = Some(Err(ex));
// roll_state = RollbackStates::Err;
// break;
// }
// if snap_date_time == &target_snap {
// break;
// }
// }
// clr_snap_res
// }
// RollbackStates::Finish => None,
// RollbackStates::Err => None,
// })
// // 1. Rename usr and var to TMP_ROOT_SNAPS/tmp_var|usr
// // fs::rename(TMP_ROOT_USR, TMP_ROOT_SNAPS_USR)?;
// // fs::rename(TMP_ROOT_VAR, TMP_ROOT_SNAPS_VAR)?;
// // 2. snapshot desired snaps as RW to /usr and /var
// // snapshot_rw(&usr_ro_snap_path, TMP_ROOT_USR)?;
// // snapshot_rw(&var_ro_snap_path, TMP_ROOT_VAR)?;
// // 3. move subvolumes from old /usr and /var
// // for (vol, svol_path) in SBUVOL_TO_MOVE {
// // let v_from = format!("{}{}{}", TMP_ROOT_SNAPS_PREFIX, vol, svol_path);
// // let v_dest = format!("{}/{}{}",TMP_ROOT,vol,svol_path);
// // fs::rename(v_from, v_dest)?;
// // }
// // 4. removing tmp /usr /var
// // subvolume_delete(TMP_ROOT_SNAPS_USR)?;
// // subvolume_delete(TMP_ROOT_SNAPS_VAR)?;
// // 5. Removing all snapshoots made after one we restored, including inself
// // Iterate in reverse untill we get to target_snap in snaps list
// // for snap_date_time in snaps_list.iter().rev() {
// // let usr_snap = format!("{}/bzz-{}-{}", TMP_ROOT_SNAPS, "usr", snap_date_time);
// // let var_snap = format!("{}/bzz-{}-{}", TMP_ROOT_SNAPS, "usr", snap_date_time);
// // subvolume_delete(&usr_snap)?;
// // subvolume_delete(&var_snap)?;
// // if snap_date_time == target_snap {
// // break;
// // }
// // }
// // Ok(())
// }
///
/// Checks if pac_lock file exist and returns date-time from it if it does,
/// Otherwise return None
///
pub fn check_pac_lock() -> Result<Option<String>, ExecCommandError> {
use std::path::Path;
let pac_lock_path = Path::new(PAC_LOCK_FILE);
if pac_lock_path.exists() {
let snap_dt = fs::read_to_string(pac_lock_path)?;
Ok(Some(snap_dt.trim().into()))
} else {
Ok(None)
}
}
// pulls /sys/class/block
fn wait_for_block_devices(timeout_secs: u64) -> bool {
use std::path::Path;
use std::{thread, time::Duration};
let start = std::time::Instant::now();
while start.elapsed().as_secs() < timeout_secs {
// Check if any block device exists (e.g., sda, nvme0n1, vda)
// You can refine this to check for a specific device needed for root
if Path::new("/sys/class/block")
.read_dir()
.ok()
.and_then(|mut d| d.next().is_some().then_some(true))
.unwrap_or(false)
{
// Optional: Verify specific device needed for root exists
// if Path::new("/dev/nvme0n1").exists() { return true; }
return true;
}
thread::sleep(Duration::from_millis(100));
}
false
}
// mounting process // mounting process
//# Mount essential virtual filesystems //# Mount essential virtual filesystems
// mount -t proc none /proc // mount -t proc none /proc
// mount -t sysfs none /sys // mount -t sysfs none /sys
// modprobe bcachefs // modprobe bcachefs
pub fn prepare_env() -> Result<(), ExecCommandError> { pub fn prepare_env() -> Result<(), ExecCommandError> {
use nix::mount::{MsFlags, mount};
use std::path::Path;
// Mount proc filesystem
// mount -t proc none /proc // mount -t proc none /proc
exec_command("mount", &vec!["-t", "proc", "none", "/proc"])?; let proc_mount_flags =
MsFlags::MS_NOSUID | MsFlags::MS_NODEV | MsFlags::MS_NOEXEC | MsFlags::MS_RELATIME;
mount(
Some("proc"),
Path::new("/proc"),
Some("proc"),
proc_mount_flags,
None::<&str>,
)
.map_err(|ex| format!("Error mounting /proc: {}", ex.to_string()))?;
// mount -t sysfs none /sys // mount -t sysfs none /sys
exec_command("mount", &vec!["-t", "sysfs", "none", "/sys"])?; let sys_mount_flags =
MsFlags::MS_NOSUID | MsFlags::MS_NODEV | MsFlags::MS_NOEXEC | MsFlags::MS_RELATIME;
mount(
Some("sysfs"),
Path::new("/sys"),
Some("sysfs"),
sys_mount_flags,
None::<&str>,
)
.map_err(|ex| format!("Error mounting /sys: {}", ex.to_string()))?;
//waiting for block devs population
if wait_for_block_devices(WAIT_FOR_BLK_DEVS) == false {
return Err("Waiting for block devices to get populated timed out.".into());
}
let dev_mount_flags = MsFlags::MS_NOSUID | MsFlags::MS_RELATIME;
mount(
Some("devtmpfs"),
"/dev",
Some("devtmpfs"),
dev_mount_flags,
Some("mode=0755"),
)
.map_err(|ex| format!("Error mounting /dev: {}", ex.to_string()))?;
//mount -t tmpfs -o nosuid,nodev,noexec,mode=0755 none /run
let run_mount_flags =
MsFlags::MS_NOSUID | MsFlags::MS_NODEV | MsFlags::MS_NOEXEC | MsFlags::MS_RELATIME;
mount(
Some("run"),
"/run",
Some("tmpfs"),
run_mount_flags,
Some("mode=0755"),
)
.map_err(|ex| format!("Error mounting /run: {}", ex.to_string()))?;
// modprobe bcachefs // modprobe bcachefs
// exec_command("modprobe", &vec!["bcachefs"])?; // exec_command("modprobe", &vec!["bcachefs"])?;
// Creating dir for udev so this little shit wont hang
// fs::create_dir("/run/udev")?;
Ok(()) Ok(())
} }
/// ///
/// MUST be done before spawning any threads or async runtimes /// MUST be done before spawning any threads or async runtimes
pub fn set_env_vars_unsafe() -> () { // pub fn set_env_vars_unsafe() -> () {
use std::env; // use std::env;
// 1. Define your custom paths // // 1. Define your custom paths
let custom_bin = "/usr/bin"; // let custom_bin = "/usr/bin";
let custom_lib = "/usr/lib"; // let custom_lib = "/usr/lib";
// 2. Retrieve existing paths to append/prepend (optional but recommended) // // 2. Retrieve existing paths to append/prepend (optional but recommended)
let existing_path = env::var("PATH").unwrap_or_else(|_| String::new()); // // 3. Construct new path strings
let existing_ld_path = env::var("LD_LIBRARY_PATH").unwrap_or_else(|_| String::new()); // let new_path = match env::var("PATH") {
// Err(_) => custom_bin.to_string(),
// Ok(v) => format!("{}:{}", custom_bin, v),
// };
// 3. Construct new path strings // let new_ld_path = match env::var("LD_LIBRARY_PATH") {
// Prepend custom paths so they take precedence // Err(_) => custom_lib.to_string(),
let new_path = format!("{}:{}", custom_bin, existing_path); // Ok(v) => format!("{}:{}", custom_lib, v),
let new_ld_path = format!("{}:{}", custom_lib, existing_ld_path); // };
// 4. Set environment variables (UNSAFE in Rust 2024+) // // 4. Set environment variables (UNSAFE in Rust 2024+)
// MUST be done before spawning any threads or async runtimes // // MUST be done before spawning any threads or async runtimes
unsafe { // unsafe {
env::set_var("PATH", new_path); // env::set_var("PATH", new_path);
env::set_var("LD_LIBRARY_PATH", new_ld_path); // env::set_var("LD_LIBRARY_PATH", new_ld_path);
} // }
} // }
// # Mount the real root filesystem // # Mount the real root filesystem
pub fn mount_bee_root(mount_point: &str, dev_name: &str) -> Result<(), ExecCommandError> { pub fn mount_bee_root(device: &BlkIdRes, mount_point: &str) -> Result<(), ExecCommandError> {
exec_command("mount", &vec!["-o", "ro", dev_name, mount_point])?; use nix::mount::{MsFlags, mount};
use std::path::Path;
let root_mount_flags = MsFlags::MS_RDONLY | MsFlags::MS_RELATIME;
mount(
Some(device.dev_node.as_str()),
Path::new(mount_point),
Some(device.fs_type.as_str()),
root_mount_flags,
None::<&str>,
)
.map_err(|ex| {
format!(
"Error mounting root {} to {} :\n{}",
&device.dev_node,
mount_point,
ex.to_string()
)
})?;
// exec_command("/usr/bin/mount", &vec!["-o", "ro", dev_name, mount_point])?;
Ok(()) Ok(())
} }
// finish boot // finish boot
pub fn hand_off_control(mnt_root: &str, init_path: &str) -> Result<(), ExecCommandError> { pub fn hand_off_control(mnt_root: &str, init_path: &str) -> Result<(), ExecCommandError> {
use std::env; //use std::env;
use std::os::unix::process::CommandExt; use std::os::unix::process::CommandExt;
// 2. Change directory to the new root (Mandatory for switch_root) // 2. Change directory to the new root (Mandatory for switch_root)
env::set_current_dir(mnt_root)?; // Actually this may be a problem!
// env::set_current_dir(mnt_root)?;
// # Clean up virtual filesystems // # Clean up virtual filesystems
// !!! do not do this, switch_root handle this stuff and fail if thay not present! // !!! do not do this, switch_root handle this stuff and fail if thay not present!
@@ -276,7 +812,7 @@ pub fn hand_off_control(mnt_root: &str, init_path: &str) -> Result<(), ExecComma
// 3. Execute switch_root. // 3. Execute switch_root.
// This call replaces the current Rust process. // This call replaces the current Rust process.
// It only returns if an error occurs. // It only returns if an error occurs.
let err = Command::new("switch_root") let err = Command::new("/usr/bin/switch_root")
.arg(mnt_root) .arg(mnt_root)
.arg(init_path) .arg(init_path)
.exec(); .exec();