Skip to content

Instantly share code, notes, and snippets.

@hyperupcall
Created October 21, 2020 02:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hyperupcall/71a52e4d3ee92596f89a2ff43e00009c to your computer and use it in GitHub Desktop.
Save hyperupcall/71a52e4d3ee92596f89a2ff43e00009c to your computer and use it in GitHub Desktop.
fstab
// goal: to read `user-dirs.dirs` and
// generate parts of fstab for that
package main
import (
"fmt"
"strings"
)
func flags() {
}
type FstabEntry struct {
FsSpec string
FsFile string
FsVfstype string
FsMntOps string
FsFreq string
FsPassno string
}
func main() {
flags()
// SEP separates characters in
SEP := " "
// MOUNTPOINT is where the `pics`, `dls`, etc. files are actually kept
MOUNTPOINT := "/data"
var sb strings.Builder
// ROOT
{
fstabEntry := FstabEntry{
FsSpec: "UUID=" + getUuidFromPath("/"),
FsFile: "/",
FsVfstype: "ext4",
FsMntOps: "rw,suid,dev,exec,auto,nouser,async,relatime,errors=remount-ro",
FsFreq: "0",
FsPassno: "1",
}
sb.WriteString("# Root\n")
str := writeEntry(fstabEntry, SEP)
sb.WriteString(str + "\n")
}
// BOOT RELATED
{
fstabEntry := FstabEntry{
FsSpec: "UUID=" + getUuidFromPath("/efi"),
FsFile: "/efi",
FsVfstype: "vfat",
FsMntOps: "rw,suid,dev,exec,auto,nouser,async,relatime,fmask=0033,dmask=0022,iocharset=utf8,utf8,X-mount.mkdir=0755,errors=remount-ro",
FsFreq: "0",
FsPassno: "2",
}
sb.WriteString("# Boot \n")
str := writeEntry(fstabEntry, SEP)
sb.WriteString(str)
// mounting parts of /efi to /boot and /boot/efi
fstabEntry1 := FstabEntry{
FsSpec: "/efi/EFI/arch",
FsFile: "/boot",
FsVfstype: "none",
FsMntOps: "rw,bind,x-systemd.after=/boot,X-mount.mkdir=0755",
FsFreq: "0",
FsPassno: "0",
}
fstabEntry2 := FstabEntry{
FsSpec: "/efi",
FsFile: "/boot/efi",
FsVfstype: "none",
FsMntOps: "rw,bind,x-systemd.after=/boot,X-mount.mkdir=0755",
FsFreq: "0",
FsPassno: "0",
}
str1 := writeEntry(fstabEntry1, SEP)
sb.WriteString(str1)
str2 := writeEntry(fstabEntry2, SEP)
sb.WriteString(str2 + "\n")
}
// XDG DESKTOP ENTRIES
{
fstabEntry := FstabEntry{
FsSpec: "/dev/fox/data",
FsFile: MOUNTPOINT,
FsVfstype: "xfs",
FsMntOps: "rw,suid,dev,exec,auto,nouser,async,relatime,X-mount.mkdir=0755,errors=remount-ro",
FsFreq: "0",
FsPassno: "2",
}
sb.WriteString("# XDG Desktop Entries\n")
str := writeEntry(fstabEntry, SEP)
sb.WriteString(str)
xdgDirs := make(map[string]string)
// reads common default values (not from /etc/xdg/user-dirs.defaults)
xdgDirs = mergeAndOverwrite(xdgDirs, getDefaultDirsContent())
// reads ~/.config/user-dirs.dirs
xdgDirs = mergeAndOverwrite(xdgDirs, getUserDirsContent())
// destDir is a place we want to mount our folder to (in /home/$user)
for _, destDir := range xdgDirs {
srcDir := MOUNTPOINT + destDir[5:]
debug("\nsrcDir: %s\ndestDir: %s\n", srcDir, destDir)
if !fileExists(srcDir) || !fileExists(destDir) {
debug("SKIP\n")
continue
}
debug("ADD\n")
fstabEntry := FstabEntry{
FsSpec: srcDir,
FsFile: destDir,
FsVfstype: "none",
FsMntOps: "x-systemd.after=" + MOUNTPOINT + ",bind,nofail",
FsFreq: "0",
FsPassno: "0",
}
str := writeEntry(fstabEntry, SEP)
sb.WriteString(str + "\n")
}
}
// STORAGE
{
fstabEntry := FstabEntry{
FsSpec: "/dev/rodinia/vault",
FsFile: "/data/storage",
FsVfstype: "ext4",
FsMntOps: "rw,suid,dev,exec,auto,nouser,async,relatime,X-mount.mkdir=0755,errors=remount-ro",
FsFreq: "0",
FsPassno: "2",
}
sb.WriteString("# Storage\n")
str := writeEntry(fstabEntry, SEP)
sb.WriteString(str)
}
str := sb.String()
fmt.Print(str)
}
package main
import (
"fmt"
"io/ioutil"
"log"
"os"
"path"
"path/filepath"
"strings"
"github.com/mattn/go-isatty"
"github.com/shirou/gopsutil/disk"
)
func writeEntry(entry FstabEntry, SEP string) string {
var sb strings.Builder
sb.WriteString(entry.FsSpec + SEP)
sb.WriteString(entry.FsFile + SEP)
sb.WriteString(entry.FsVfstype + SEP)
sb.WriteString(entry.FsMntOps + SEP)
sb.WriteString(entry.FsFreq + SEP)
sb.WriteString(entry.FsPassno + "\n")
return sb.String()
}
// name can be '/dev/dm-0', '/dev/sda7', etc.
func getUuid(location string) string {
file, err := ioutil.ReadDir("/dev/disk/by-uuid")
if err != nil {
log.Fatalln(err)
}
// base := path.Base(location)
for _, file := range file {
uuidPath := path.Join("/dev/disk/by-uuid", file.Name())
diskPath, err := filepath.EvalSymlinks(uuidPath)
if err != nil {
log.Fatalln(err)
}
if location == diskPath {
return uuidPath
}
}
return ""
}
// mostly a function for debugging, since
// getUuidFromPath calls disk.Partitions by itself
func getDisks() {
partitionStats, err := disk.Partitions(false)
if err != nil {
log.Fatalln(err)
}
for _, partitionStat := range partitionStats {
uuid := getUuid(partitionStat.Device)
debug("device: %s\n", partitionStat.Device)
debug("mountpoint: %s\n", partitionStat.Mountpoint)
debug("fstype: %s\n", partitionStat.Fstype)
debug("opts: %s\n", partitionStat.Opts)
debug("uuid: %s\n", uuid)
debug("\n")
}
}
func getUuidFromPath(mountpoint string) string {
partitionStats, err := disk.Partitions(false)
if err != nil {
log.Fatalln(err)
}
for _, partitionStat := range partitionStats {
uuid := getUuid(partitionStat.Device)
if partitionStat.Mountpoint == mountpoint {
debug("device: %s\n", partitionStat.Device)
debug("mountpoint: %s\n", partitionStat.Mountpoint)
debug("fstype: %s\n", partitionStat.Fstype)
debug("opts: %s\n", partitionStat.Opts)
debug("uuid: %s\n", uuid)
debug("\n")
return path.Base(uuid)
}
}
return ""
}
func getDefaultDirsContent() []byte {
return []byte(`XDG_DESKTOP_DIR="$HOME/Desktop"
XDG_DOWNLOAD_DIR="$HOME/Downloads"
XDG_TEMPLATES_DIR="$HOME/Templates"
XDG_PUBLICSHARE_DIR="$HOME/Public"
XDG_DOCUMENTS_DIR="$HOME/Documents"
XDG_MUSIC_DIR="$HOME/Music"
XDG_PICTURES_DIR="$HOME/Pictures"
XDG_VIDEOS_DIR="$HOME/Videos"
`)
}
func getUserDirsContent() []byte {
homeDir := os.Getenv("HOME")
userDirsFile := path.Join(homeDir, ".config", "user-dirs.dirs")
userDirsContent, err := ioutil.ReadFile(userDirsFile)
if os.IsNotExist(err) {
fmt.Println("user-dirs.dirs file doesn't exist. that's okay")
} else if err != nil {
log.Fatalln(err)
}
return []byte(userDirsContent)
}
func mergeAndOverwrite(xdgDirs map[string]string, userDirsDotDirsContent []byte) map[string]string {
content := string(userDirsDotDirsContent)
for _, line := range strings.Split(strings.TrimSpace(content), "\n") {
line := strings.TrimSpace(line)
if strings.HasPrefix(line, "#") || line == "" {
continue
}
str := strings.Split(line, "=")
dirName := strings.TrimSpace(str[0])
dirValue := strings.TrimSpace(str[1][1 : len(str[1])-1])
if strings.HasPrefix(dirValue, "$HOME") {
homeDir := os.Getenv("HOME")
if homeDir == "" {
log.Fatalln("home dir not set")
}
dirValue = homeDir + dirValue[5:]
dirValue = strings.ReplaceAll(dirValue, "//", "/")
}
xdgDirs[dirName] = dirValue
}
return xdgDirs
}
func isColorEnabled() bool {
_, isNoColorEnabled := os.LookupEnv("NO_COLOR")
if (os.Getenv("TERM") != "dumb") && !isNoColorEnabled &&
isatty.IsTerminal(os.Stdout.Fd()) || isatty.IsCygwinTerminal(os.Stdout.Fd()) {
return true
}
return false
}
func print(text string, args ...interface{}) {
if isColorEnabled() {
fmt.Print("\033[34m")
fmt.Printf(text, args...)
fmt.Print("\033[m")
return
}
fmt.Printf(text, args...)
}
func debug(text string, args ...interface{}) {
isDebug := func() bool {
_, ok := os.LookupEnv("DEBUG")
if !ok {
return false
}
return true
}
if isDebug() {
if isColorEnabled() {
fmt.Print("\033[32m")
fmt.Printf(text, args...)
fmt.Print("\033[m")
return
}
fmt.Printf(text, args...)
}
}
func fileExists(path string) bool {
_, err := os.Stat(path)
if err == nil {
return true
}
if os.IsNotExist(err) {
return false
}
return false
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment