Feat: conditional permissions behavior (#28)

* Fix: don't need sudo if we're root + other aesthetics

* Heavy refactoring, see PR #28

* Fix: avoid silent fatalities

demo: https://tcp.ac/i/JMSUc.gif

* Fix: Inverse check on `IsRoot`

* D.R.Y: check for permissions error in `common.ErrorCheck`

Reduce cognitive complexity.

* Fix: Issue with copying

* Resolve https://github.com/HikariKnight/quickpassthrough/pull/28#discussion_r1646535918

* Resolve https://github.com/HikariKnight/quickpassthrough/pull/28#discussion_r1646606680 and https://github.com/HikariKnight/quickpassthrough/pull/28#discussion_r1646594105

* Revert "Resolve https://github.com/HikariKnight/quickpassthrough/pull/28#discussion_r1646606680 and https://github.com/HikariKnight/quickpassthrough/pull/28#discussion_r1646594105"

This reverts commit ce15213009.

* Resolve https://github.com/HikariKnight/quickpassthrough/pull/28#discussion_r1646730751
This commit is contained in:
kayos 2024-07-27 08:38:39 -07:00 committed by GitHub
parent 4d0086df41
commit 6c48a35180
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
21 changed files with 604 additions and 255 deletions

View file

@ -1,16 +1,20 @@
package configs
import (
"errors"
"fmt"
"os"
"path/filepath"
"regexp"
"strings"
"github.com/HikariKnight/ls-iommu/pkg/errorcheck"
"github.com/klauspost/cpuid/v2"
"github.com/HikariKnight/quickpassthrough/internal/common"
"github.com/HikariKnight/quickpassthrough/internal/logger"
"github.com/HikariKnight/quickpassthrough/pkg/command"
"github.com/HikariKnight/quickpassthrough/pkg/fileio"
"github.com/HikariKnight/quickpassthrough/pkg/uname"
"github.com/klauspost/cpuid/v2"
)
type Path struct {
@ -30,9 +34,10 @@ type Config struct {
Path *Path
Gpu_Group string
Gpu_IDs []string
IsRoot bool
}
// Gets the path to all the config files
// GetConfigPaths retrieves the path to all the config files.
func GetConfigPaths() *Path {
Paths := &Path{
CMDLINE: "config/kernel_args",
@ -48,7 +53,7 @@ func GetConfigPaths() *Path {
return Paths
}
// Gets all the configs and returns the struct
// GetConfig retrieves all the configs and returns the struct.
func GetConfig() *Config {
config := &Config{
Bootloader: "unknown",
@ -64,7 +69,7 @@ func GetConfig() *Config {
return config
}
// Constructs the empty config files and folders based on what exists on the system
// InitConfigs constructs the empty config files and folders based on what exists on the system
func InitConfigs() {
config := GetConfig()
@ -77,10 +82,17 @@ func InitConfigs() {
}
// Remove old config
os.RemoveAll("config")
if err := os.RemoveAll("config"); err != nil && !errors.Is(err, os.ErrNotExist) {
// won't be called if the error is ErrNotExist
common.ErrorCheck(err, "\nError removing old config")
}
// Make the config folder
os.Mkdir("config", os.ModePerm)
if err := os.Mkdir("config", os.ModePerm); err != nil && !errors.Is(err, os.ErrExist) {
// won't be called if the error is ErrExist
common.ErrorCheck(err, "\nError making config folder")
}
// Make a regex to get the system path instead of the config path
syspath_re := regexp.MustCompile(`^config`)
@ -90,8 +102,16 @@ func InitConfigs() {
// Get the system path
syspath := syspath_re.ReplaceAllString(confpath, "")
exists, err := fileio.FileExist(syspath)
// If we received an error that is not ErrNotExist
if err != nil {
common.ErrorCheck(err, "\nError checking for directory: "+syspath)
continue // note: unreachable due to ErrorCheck calling fatal
}
// If the path exists
if fileio.FileExist(syspath) {
if exists {
// Write to log
logger.Printf(
"%s found on the system\n"+
@ -104,8 +124,10 @@ func InitConfigs() {
makeBackupDir(syspath)
// Create the directories for our configs
err := os.MkdirAll(confpath, os.ModePerm)
errorcheck.ErrorCheck(err)
if err = os.MkdirAll(confpath, os.ModePerm); err != nil && !errors.Is(err, os.ErrExist) {
common.ErrorCheck(err, "\nError making directory: "+confpath)
return // note: unreachable due to ErrorCheck calling fatal
}
}
}
@ -128,7 +150,15 @@ func InitConfigs() {
sysfile := syspath_re.ReplaceAllString(conffile, "")
// If the file exists
if fileio.FileExist(sysfile) {
exists, err := fileio.FileExist(sysfile)
// If we received an error that is not ErrNotExist
if err != nil {
common.ErrorCheck(err, "\nError checking for file: "+sysfile)
continue // note: unreachable due to ErrorCheck calling fatal
}
if exists {
// Write to log
logger.Printf(
"%s found on the system\n"+
@ -139,16 +169,22 @@ func InitConfigs() {
// Create the directories for our configs
file, err := os.Create(conffile)
errorcheck.ErrorCheck(err)
common.ErrorCheck(err)
// Close the file so we can edit it
file.Close()
_ = file.Close()
// Backup the sysfile if we do not have a backup
backupFile(sysfile)
}
exists, err = fileio.FileExist(conffile)
if err != nil {
common.ErrorCheck(err, "\nError checking for file: "+conffile)
continue // note: unreachable
}
// If we now have a config that exists
if fileio.FileExist(conffile) {
if exists {
switch conffile {
case config.Path.ETCMODULES:
// Write to logger
@ -204,14 +240,28 @@ func backupFile(source string) {
// Make a destination path
dest := fmt.Sprintf("backup%s", source)
configExists, configFileError := fileio.FileExist(fmt.Sprintf("config%s", source))
sysExists, sysFileError := fileio.FileExist(source)
destExists, destFileError := fileio.FileExist(dest)
// If we received an error that is not ErrNotExist on any of the files
for _, err := range []error{configFileError, sysFileError, destFileError} {
if err != nil {
common.ErrorCheck(configFileError, "\nError checking for file: "+source)
return // note: unreachable
}
}
switch {
// If the file exists in the config but not on the system it is a file we make
if fileio.FileExist(fmt.Sprintf("config%s", source)) && !fileio.FileExist(source) {
case configExists && !sysExists:
// Create the blank file so that a copy of the backup folder to /etc
file, err := os.Create(dest)
errorcheck.ErrorCheck(err, "Error creating file %s\n", dest)
file.Close()
} else if !fileio.FileExist(dest) {
common.ErrorCheck(err, "Error creating file %s\n", dest)
_ = file.Close()
// If a backup of the file does not exist
case sysExists && !destExists:
// Write to the logger
logger.Printf("No first time backup of %s detected.\nCreating a backup at %s\n", source, dest)
@ -223,29 +273,67 @@ func backupFile(source string) {
func makeBackupDir(dest string) {
// If a backup directory does not exist
if !fileio.FileExist("backup/") {
exists, err := fileio.FileExist("backup/")
if err != nil {
// If we received an error that is not ErrNotExist
common.ErrorCheck(err, "Error checking for backup/ folder")
return // note: unreachable
}
if !exists {
// Write to the logger
logger.Printf("Backup directory does not exist!\nCreating backup directory for first run backup")
}
// Make the empty directories
err := os.MkdirAll(fmt.Sprintf("backup/%s", dest), os.ModePerm)
errorcheck.ErrorCheck(err, "Error making backup/ folder")
if err = os.MkdirAll(fmt.Sprintf("backup/%s", dest), os.ModePerm); errors.Is(err, os.ErrExist) {
// ignore if the directory already exists
err = nil
}
// will return without incident if there's no error
common.ErrorCheck(err, "Error making backup/ folder")
}
// Copy a file to the system, make sure you have run command.Elevate() recently
func CopyToSystem(conffile, sysfile string) string {
// CopyToSystem copies a file to the system.
func CopyToSystem(isRoot bool, conffile, sysfile string) {
// Since we should be elevated with our sudo token we will copy with cp
// (using built in functions will not work as we are running as the normal user)
output, _ := command.Run("sudo", "cp", "-v", conffile, sysfile)
// Clean the output
clean_re := regexp.MustCompile(`\n`)
clean_output := clean_re.ReplaceAllString(output[0], "")
// ExecAndLogSudo will write to the logger, so just print here
fmt.Printf("Copying: %s to %s\n", conffile, sysfile)
// Write output to logger
logger.Printf("%s\n", clean_output)
if isRoot {
logger.Printf("Copying %s to %s\n", conffile, sysfile)
fmt.Printf("Copying %s to %s\n", conffile, sysfile)
fDat, err := os.ReadFile(conffile)
common.ErrorCheck(err, fmt.Sprintf("Failed to read %s", conffile))
err = os.WriteFile(sysfile, fDat, 0644)
common.ErrorCheck(err, fmt.Sprintf("Failed to write %s", sysfile))
logger.Printf("Copied %s to %s\n", conffile, sysfile)
return
}
// Return the output
return fmt.Sprintf("Copying: %s", clean_output)
if !filepath.IsAbs(conffile) {
conffile, _ = filepath.Abs(conffile)
}
conffile = strings.ReplaceAll(conffile, " ", "\\ ")
cmd := fmt.Sprintf("cp -v %s %s", conffile, sysfile)
err := command.ExecAndLogSudo(isRoot, false, cmd)
errMsg := ""
if err != nil {
errMsg = err.Error()
}
// [command.ExecAndLogSudo] will log the command's output
common.ErrorCheck(err, fmt.Sprintf("Failed to copy %s to %s:\n%s", conffile, sysfile, errMsg))
// ---------------------------------------------------------------------------------
// note that if we failed the error check, the following will not appear in the log!
// this is because the [common.ErrorCheck] function will call [log.Fatalf] and exit
// ---------------------------------------------------------------------------------
logger.Printf("Copied %s to %s\n", conffile, sysfile)
}