/* ArOZ Online System - fsexec File System Asynchronize File Opertation Execution Instance This is a stand alone application design to replace the synchronize file operation designed since the start of the AOB project. By replacing the original file operation php script with this new script, the file explorer in AOB should be "freeze-free" while moving / copying large files. This application provide the following functions copy {from} {target} copy_folder {from} {target} delete {filename} move {from} {target} move_folder {from} {target} All the command should be stringify with JSON and encoded into base64, then pass into the command parameter of the application launching paramters. */ package main import ( "encoding/base64" "encoding/json" "fmt" "io" "io/ioutil" "log" "os" "path" "path/filepath" "reflect" "time" ) //Commonly used functions func in_array(val interface{}, array interface{}) (exists bool, index int) { exists = false index = -1 switch reflect.TypeOf(array).Kind() { case reflect.Slice: s := reflect.ValueOf(array) for i := 0; i < s.Len(); i++ { if reflect.DeepEqual(val, s.Index(i).Interface()) == true { index = i exists = true return } } } return } func file_exists(path string) bool { if _, err := os.Stat(path); os.IsNotExist(err) { return false } return true } func file_get_contents(path string) string { dat, err := ioutil.ReadFile(path) if err != nil { panic("Unable to read file: " + path) } return (string(dat)) } func writeLog(filepath string, prefix string, message string) bool { f, err := os.OpenFile(filepath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) if err != nil { log.Println(err) } defer f.Close() logger := log.New(f, prefix, log.LstdFlags) logger.Println(message) return true } func showHelp() { fmt.Println("<>") fmt.Println("Usage: ./fsexec {uuid} {command_in_base64}") } func decodeBase64(base64string string) string { l, err := base64.StdEncoding.DecodeString(base64string) if err != nil { panic(err) } return string(l) } func validateCopySourceAndTarget(from string, target string) bool { if !file_exists(from) { return false } if file_exists(target) { return false } return true } func getTimestamp() string { t := time.Now() return (t.Format("2006-01-02 150405")) } func moveLogFile(logfile string, target string, uuid string) { //If then else is used to prevent invalid move target if target == "done" { os.Rename(logfile, "log/done/"+uuid+".log") } else if target == "error" { os.Rename(logfile, "log/error/"+uuid+".log") } else { //What happened?o panic("ERROR. Undefined log file sattle location.") } } func finishFileOperation(logfile string, uuid string) { //Finishing the file operation. writeLog(logfile, "[done] ", "Task finished successfully") moveLogFile(logfile, "done", uuid) fmt.Println("Done") } func initChk() { //Initiation Checking for all the required directories if !file_exists("log/") { os.MkdirAll("log/", 0777) } if !file_exists("log/done/") { os.MkdirAll("log/done/", 0777) } if !file_exists("log/error/") { os.MkdirAll("log/error/", 0777) } } //Main programming logic func main() { initChk() acceptOperations := []string{"copy", "copy_folder", "delete", "move", "move_folder"} if len(os.Args) == 1 { showHelp() return } //Get the command in using base64 and required log file uuid startSettings := os.Args[1:] uuid := startSettings[0] encodedCommand := startSettings[1] decodedCommand := decodeBase64(encodedCommand) filename := uuid + ".log" var command []string //Create the log file for this task logfile := "log/" + filename //Check if the given id exists already. if file_exists(logfile) || file_exists("log/done/"+filename) || file_exists("log/error/"+filename) { panic("ERROR. Required task already exists.") } //Create the log file for this task writeLog(logfile, "[init] ", "Task created on "+getTimestamp()) err := json.Unmarshal([]byte(decodedCommand), &command) if err != nil { writeLog(logfile, "[error] ", "Unable to parse JSON string.") moveLogFile(logfile, "error", uuid) panic("ERROR. Unable to parse JSON string.") } //Check if the given file operation correct or not. correctOpr, _ := in_array(string(command[0]), acceptOperations) if !correctOpr { fmt.Println(string(command[0])) writeLog(logfile, "[error] ", "Invalid file operation. "+command[0]+" given.") moveLogFile(logfile, "error", uuid) panic("ERROR. Invalid file operation. " + command[0] + " given.") } writeLog(logfile, "[info] ", decodedCommand) //Start reading the command for file operations opr := command[0] if opr == "copy" { //Copy a given file to a given location from := command[1] target := command[2] if !validateCopySourceAndTarget(from, target) { writeLog(logfile, "[error] ", "Invalid source file or target directory.") moveLogFile(logfile, "error", uuid) panic("ERROR. Invalid source file or target directory.") } //Check if the parent dir exists dirname := path.Dir(target) if !file_exists(dirname) { os.MkdirAll(dirname, 0777) } Copy(from, target) finishFileOperation(logfile, uuid) } else if opr == "copy_folder" { //Copy a folder to a given location from := command[1] target := command[2] if !validateCopySourceAndTarget(from, target) { writeLog(logfile, "[error] ", "Invalid source file or target directory.") moveLogFile(logfile, "error", uuid) panic("ERROR. Invalid source file or target directory.") } Copy(from, target) finishFileOperation(logfile, uuid) } else if opr == "move" { //Move a file from := command[1] target := command[2] if !validateCopySourceAndTarget(from, target) { writeLog(logfile, "[error] ", "Invalid source file or target directory.") moveLogFile(logfile, "error", uuid) panic("ERROR. Invalid source file or target directory.") } err := os.Rename(from, target) if err != nil { writeLog(logfile, "[error] ", "Unable to move file due to unknown error.") moveLogFile(logfile, "error", uuid) panic("ERROR. Unable to move file due to unknown error.") } finishFileOperation(logfile, uuid) } else if opr == "move_folder" { //Move a folder from := command[1] target := command[2] if !validateCopySourceAndTarget(from, target) { writeLog(logfile, "[error] ", "Invalid source file or target directory.") moveLogFile(logfile, "error", uuid) panic("ERROR. Invalid source file or target directory.") } err := os.Rename(from, target) if err != nil { writeLog(logfile, "[error] ", "Unable to move folder due to unknown error.") moveLogFile(logfile, "error", uuid) panic("ERROR. Unable to move folder due to unknown error.") } finishFileOperation(logfile, uuid) } } //Program required external library. Included in main to prevent download on other building platform. //Recursive Copy Lib, not sure why it can't be imported like other module. Hence directly copy and paste in the section below. const ( // tmpPermissionForDirectory makes the destination directory writable, // so that stuff can be copied recursively even if any original directory is NOT writable. // See https://github.com/otiai10/copy/pull/9 for more information. tmpPermissionForDirectory = os.FileMode(0755) ) // Copy copies src to dest, doesn't matter if src is a directory or a file func Copy(src, dest string) error { info, err := os.Lstat(src) if err != nil { return err } return copy(src, dest, info) } // copy dispatches copy-funcs according to the mode. // Because this "copy" could be called recursively, // "info" MUST be given here, NOT nil. func copy(src, dest string, info os.FileInfo) error { if info.Mode()&os.ModeSymlink != 0 { return lcopy(src, dest, info) } if info.IsDir() { return dcopy(src, dest, info) } return fcopy(src, dest, info) } // fcopy is for just a file, // with considering existence of parent directory // and file permission. func fcopy(src, dest string, info os.FileInfo) error { if err := os.MkdirAll(filepath.Dir(dest), os.ModePerm); err != nil { return err } f, err := os.Create(dest) if err != nil { return err } defer f.Close() if err = os.Chmod(f.Name(), info.Mode()); err != nil { return err } s, err := os.Open(src) if err != nil { return err } defer s.Close() _, err = io.Copy(f, s) return err } // dcopy is for a directory, // with scanning contents inside the directory // and pass everything to "copy" recursively. func dcopy(srcdir, destdir string, info os.FileInfo) error { originalMode := info.Mode() // Make dest dir with 0755 so that everything writable. if err := os.MkdirAll(destdir, tmpPermissionForDirectory); err != nil { return err } // Recover dir mode with original one. defer os.Chmod(destdir, originalMode) contents, err := ioutil.ReadDir(srcdir) if err != nil { return err } for _, content := range contents { cs, cd := filepath.Join(srcdir, content.Name()), filepath.Join(destdir, content.Name()) if err := copy(cs, cd, content); err != nil { // If any error, exit immediately return err } } return nil } // lcopy is for a symlink, // with just creating a new symlink by replicating src symlink. func lcopy(src, dest string, info os.FileInfo) error { src, err := os.Readlink(src) if err != nil { return err } return os.Symlink(src, dest) }