From cef8c47161eb14ed74684e3904d8e7ca9f4f38db Mon Sep 17 00:00:00 2001 From: Randall Winkhart Date: Tue, 9 Jul 2024 23:18:13 -0400 Subject: [PATCH] Implement synchronized renaming --- src/backend/edit.go | 24 ---------------- src/sync/common.go | 67 +++++++++++++++++++++++++++++++++++++-------- 2 files changed, 55 insertions(+), 36 deletions(-) diff --git a/src/backend/edit.go b/src/backend/edit.go index add05e7..ac9a938 100644 --- a/src/backend/edit.go +++ b/src/backend/edit.go @@ -1,10 +1,5 @@ package backend -import ( - "fmt" - "os" -) - // GetOldEntryData decrypts and returns old entry data (with all required lines present) func GetOldEntryData(targetLocation string, field int) []string { // ensure targetLocation exists @@ -21,25 +16,6 @@ func GetOldEntryData(targetLocation string, field int) []string { } } -// Rename renames oldLocation to newLocation -func Rename(oldLocation, newLocation string) { - // ensure newLocation does not exist - _, isAccessible := TargetIsFile(newLocation, false, 0) - if isAccessible { - fmt.Println(AnsiError + "\"" + newLocation + "\" already exists" + AnsiReset) - os.Exit(1) - } - - // rename oldLocation to newLocation - err := os.Rename(oldLocation, newLocation) - if err != nil { - fmt.Println(AnsiError + "Failed to rename - does the target containing directory exists?" + AnsiReset) - } - - // TODO implement synced renaming - Exit(0) -} - // EnsureSliceLength ensures slice is long enough to contain the specified index func EnsureSliceLength(slice []string, index int) []string { for len(slice) <= index { diff --git a/src/sync/common.go b/src/sync/common.go index a494cbb..58aec5c 100644 --- a/src/sync/common.go +++ b/src/sync/common.go @@ -3,12 +3,13 @@ package sync import ( "fmt" "github.com/rwinkhart/MUTN/src/backend" + "io/fs" "os" "strings" ) +// getModTimes returns a list of all entry modification times func getModTimes(entryList []string) []int64 { - // get a list of all entry modification times var modList []int64 for _, file := range entryList { modTime, _ := os.Stat(backend.TargetLocationFormat(file)) @@ -18,6 +19,53 @@ func getModTimes(entryList []string) []int64 { return modList } +// genDeviceIDList returns a pointer to a slice of all registered device IDs +func genDeviceIDList() *[]fs.DirEntry { + // create a slice of all registered devices + deviceIDList, err := os.ReadDir(backend.ConfigDir + backend.PathSeparator + "devices") + if err != nil { + fmt.Println(backend.AnsiError + "Failed to read the devices directory: " + err.Error() + backend.AnsiReset) + os.Exit(1) + } + return &deviceIDList +} + +// Rename renames oldLocation to newLocation +// if running on the client, also calls the server to rename the entry and add the old entry name to the deletions list +func Rename(oldLocationIncomplete, newLocationIncomplete string, onServer bool) { + // get full paths for both locations + oldLocation := backend.TargetLocationFormat(oldLocationIncomplete) + newLocation := backend.TargetLocationFormat(newLocationIncomplete) + + // ensure oldLocation exists + backend.TargetIsFile(oldLocation, true, 0) + + // ensure newLocation does not exist + _, isAccessible := backend.TargetIsFile(newLocation, false, 0) + if isAccessible { + fmt.Println(backend.AnsiError + "\"" + newLocation + "\" already exists" + backend.AnsiReset) + os.Exit(1) + } + + // rename oldLocation to newLocation + err := os.Rename(oldLocation, newLocation) + if err != nil { + fmt.Println(backend.AnsiError + "Failed to rename - does the target containing directory exist?" + backend.AnsiReset) + } + + // call server to complete rename + if !onServer { + deviceIDList := genDeviceIDList() + if len(*deviceIDList) > 0 { // ensure a device ID exists (online mode) + GetSSHOutput("libmuttonserver rename", + (*deviceIDList)[0].Name()+"\n"+ + strings.ReplaceAll(oldLocationIncomplete, backend.PathSeparator, "\x1d")+"\n"+ + strings.ReplaceAll(newLocationIncomplete, backend.PathSeparator, "\x1d"), false) + } + backend.Exit(0) // do not exit program on server, as fallthrough is used to add the old entry name to the deletions list + } +} + // ShearLocal removes the target file or directory from the local system // returns: deviceID (on client), for use in ShearRemoteFromClient // if the local system is a server, it will also add the target to the deletions list for all clients (except the requesting client) @@ -29,18 +77,13 @@ func ShearLocal(targetLocationIncomplete, clientDeviceID string) string { onServer = true } - // create a slice of all registered devices - deviceIDList, err := os.ReadDir(backend.ConfigDir + backend.PathSeparator + "devices") - if err != nil { - fmt.Println(backend.AnsiError + "Failed to read the devices directory: " + err.Error() + backend.AnsiReset) - os.Exit(1) - } + deviceIDList := genDeviceIDList() // add the sheared target (incomplete, vanity) to the deletions list (if running on a server) if onServer { - for _, device := range deviceIDList { + for _, device := range *deviceIDList { if device.Name() != clientDeviceID { - _, err = os.Create(backend.ConfigDir + backend.PathSeparator + "deletions" + backend.PathSeparator + device.Name() + "\x1e" + strings.ReplaceAll(targetLocationIncomplete, "/", "\x1d")) + _, err := os.Create(backend.ConfigDir + backend.PathSeparator + "deletions" + backend.PathSeparator + device.Name() + "\x1e" + strings.ReplaceAll(targetLocationIncomplete, "/", "\x1d")) if err != nil { // do not print error as there is currently no way of seeing server-side errors // failure to add the target to the deletions list will exit the program and result in a client re-uploading the target (non-critical) @@ -55,14 +98,14 @@ func ShearLocal(targetLocationIncomplete, clientDeviceID string) string { if !onServer { // error if target does not exist on client, needed because os.RemoveAll does not return an error if target does not exist backend.TargetIsFile(targetLocationComplete, true, 0) } - err = os.RemoveAll(targetLocationComplete) + err := os.RemoveAll(targetLocationComplete) if err != nil { fmt.Println(backend.AnsiError + "Failed to remove local target: " + err.Error() + backend.AnsiReset) os.Exit(1) } - if !onServer && len(deviceIDList) > 0 { // return the device ID if running on the client and a device ID exists (online mode) - return deviceIDList[0].Name() + if !onServer && len(*deviceIDList) > 0 { // return the device ID if running on the client and a device ID exists (online mode) + return (*deviceIDList)[0].Name() } return ""