diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d9bbafc --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +mkfstab diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..c6ffb1a --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Madonuko, Ultramarine Project, Fyra Labs, Et al. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..2e382e0 --- /dev/null +++ b/README.md @@ -0,0 +1,29 @@ +# mkfstab + +An alternative to [`genfstab` from Arch Linux](https://wiki.archlinux.org/title/Genfstab). +This is a dead simple but faster implementation of `genfstab`. + +## ❓ Usage + +(Last update: version 0.1.0) +```sh +$ mkfstab -h +Usage: + mkfstab [optional-params] [System root for detecting mountpoints] +An alternative to genfstab: generate output suitable for addition to /etc/fstab +Options: + -h, --help print this cligen-erated help + --help-syntax advanced: prepend,plurals,.. + -o=, --output= string "" Path for output file (default is stdout) + -P, --includepseudo bool false Include pseudofs mounts + -v=, --verbosity= Level lvlNotice set the logging verbosity: {lvlAll, lvlDebug, lvlInfo, lvlNotice, lvlWarn, lvlError, lvlFatal, lvlNone} +``` + +## 🏗️ Building + +1. Install [Nim](https://nim-lang.org) + - if you are on [Terra](https://terra.fyralabs.com) / Ultramarine Linux: `sudo dnf in nim` +2. `nimble build` + +## 🗒️ Todos +- [ ] Make it compatible with genfstab: https://man.archlinux.org/man/genfstab.8 diff --git a/mkfstab.nimble b/mkfstab.nimble new file mode 100644 index 0000000..c4da386 --- /dev/null +++ b/mkfstab.nimble @@ -0,0 +1,15 @@ +# Package + +version = "0.1.0" +author = "madomado" +description = "An alternative to genfstab: generate output suitable for addition to /etc/fstab" +license = "MIT" +srcDir = "src" +bin = @["mkfstab"] + + +# Dependencies + +requires "nim >= 2.1.1" +requires "cligen" +requires "sweet" diff --git a/src/log.nim b/src/log.nim new file mode 100644 index 0000000..ad5de49 --- /dev/null +++ b/src/log.nim @@ -0,0 +1,6 @@ +import std/logging + +var logger = newConsoleLogger(fmtStr="mkfstab: $levelname: ", useStderr=true) +addHandler logger + +export debug, error, fatal, info, log, notice, warn diff --git a/src/mkfstab.nim b/src/mkfstab.nim new file mode 100644 index 0000000..9bab2bf --- /dev/null +++ b/src/mkfstab.nim @@ -0,0 +1,69 @@ +import std/[strutils, strformat, sequtils, sugar, symlinks, paths, dirs, enumerate, logging] +import cligen, sweet +import log, mounts + + +proc get_mps(root: string, includepseudo: bool): seq[Mount] = + let mps = getMounts() + if !mps.len: + fatal "Fail to get mountpoints from /proc/mounts" + fatal "The file either does not exist, is empty or unparseable" + fatal "You should run mkfstab on a system with /proc/mounts" + fatal "If you are inside a chroot, exit the chroot" + quit 1 + mps.filter(mp => includepseudo || (mp.device.startsWith('/') && mp.mountpoint.startsWith(root))) + +func pad(s: string, max: int): string = + if max > s.len: + s & ' '.repeat(max - s.len) + else: + s + +template `:<`(s: string, max: int): string = + s.pad max + +proc mkfstab(root: seq[string], output = "", includepseudo = false, verbosity = lvlNotice) = + ## An alternative to genfstab: generate output suitable for addition to /etc/fstab + setLogFilter verbosity + if root.len > 1: + fatal "Specifying multiple roots makes no sense" + fatal "You are supposed to specify 1 root or default to `/`" + quit 1 + let root = + if !root.len: + warn "Defaulting to `/` as root is not specified" + "/" + elif root[0].endsWith '/': root[0] + else: root[0] & '/' + debug fmt"root={root}" + var mps = get_mps(root, includepseudo) + var lengths: array[0..3, int] = [0, 0, 0, 0] # uuid, mp, fstype, fsopts + # turn into UUID= + for (_, uuid) in walkDir(Path("/dev/disk/by-uuid"), checkDir=true): + var dev = absolutePath(expandSymlink(uuid), "/dev/disk/by-uuid".Path) + debug &"Found {uuid.string:<54} → {dev.string}" + for i, mp in enumerate(mps): + if mp.device.Path == dev: + mps[i].device = fmt"UUID={uuid.string}" + # check lengths for pretty fstab + for mp in mps: + lengths = [ + max(mp.device.len, lengths[0]), + max(mp.mountpoint.len, lengths[1]), + max(mp.fstype.len, lengths[2]), + max(mp.mountopts.len, lengths[3]), + ] + let outfd = if output == "": stdout else: open(output, fmWrite) + for mp in mps: + let dev = mp.device: