main.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365
  1. /*
  2. ArOZ Online System - fsexec File System Asynchronize File Opertation Execution Instance
  3. This is a stand alone application design to replace the synchronize file operation designed since the start
  4. of the AOB project. By replacing the original file operation php script with this new script, the file explorer
  5. in AOB should be "freeze-free" while moving / copying large files.
  6. This application provide the following functions
  7. copy {from} {target}
  8. copy_folder {from} {target}
  9. delete {filename}
  10. move {from} {target}
  11. move_folder {from} {target}
  12. All the command should be stringify with JSON and encoded into base64, then pass into the command parameter of the
  13. application launching paramters.
  14. */
  15. package main
  16. import (
  17. "encoding/base64"
  18. "encoding/json"
  19. "fmt"
  20. "io"
  21. "io/ioutil"
  22. "log"
  23. "os"
  24. "path"
  25. "path/filepath"
  26. "reflect"
  27. "time"
  28. )
  29. //Commonly used functions
  30. func in_array(val interface{}, array interface{}) (exists bool, index int) {
  31. exists = false
  32. index = -1
  33. switch reflect.TypeOf(array).Kind() {
  34. case reflect.Slice:
  35. s := reflect.ValueOf(array)
  36. for i := 0; i < s.Len(); i++ {
  37. if reflect.DeepEqual(val, s.Index(i).Interface()) == true {
  38. index = i
  39. exists = true
  40. return
  41. }
  42. }
  43. }
  44. return
  45. }
  46. func file_exists(path string) bool {
  47. if _, err := os.Stat(path); os.IsNotExist(err) {
  48. return false
  49. }
  50. return true
  51. }
  52. func file_get_contents(path string) string {
  53. dat, err := ioutil.ReadFile(path)
  54. if err != nil {
  55. panic("Unable to read file: " + path)
  56. }
  57. return (string(dat))
  58. }
  59. func writeLog(filepath string, prefix string, message string) bool {
  60. f, err := os.OpenFile(filepath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
  61. if err != nil {
  62. log.Println(err)
  63. }
  64. defer f.Close()
  65. logger := log.New(f, prefix, log.LstdFlags)
  66. logger.Println(message)
  67. return true
  68. }
  69. func showHelp() {
  70. fmt.Println("<<ArOZ Online System File System Freeze Script Executer>>")
  71. fmt.Println("Usage: ./fsexec {uuid} {command_in_base64}")
  72. }
  73. func decodeBase64(base64string string) string {
  74. l, err := base64.StdEncoding.DecodeString(base64string)
  75. if err != nil {
  76. panic(err)
  77. }
  78. return string(l)
  79. }
  80. func validateCopySourceAndTarget(from string, target string) bool {
  81. if !file_exists(from) {
  82. return false
  83. }
  84. if file_exists(target) {
  85. return false
  86. }
  87. return true
  88. }
  89. func getTimestamp() string {
  90. t := time.Now()
  91. return (t.Format("2006-01-02 150405"))
  92. }
  93. func moveLogFile(logfile string, target string, uuid string) {
  94. //If then else is used to prevent invalid move target
  95. if target == "done" {
  96. os.Rename(logfile, "log/done/"+uuid+".log")
  97. os.Chmod("log/done/"+uuid+".log", 0777) //Unlock the file for read write from php
  98. } else if target == "error" {
  99. os.Rename(logfile, "log/error/"+uuid+".log")
  100. os.Chmod("log/error/"+uuid+".log", 0777) //Unlock the file for read write from php
  101. } else {
  102. //What happened?o
  103. panic("ERROR. Undefined log file sattle location.")
  104. }
  105. }
  106. func finishFileOperation(logfile string, uuid string) {
  107. //Finishing the file operation.
  108. //time.Sleep(20000 * time.Millisecond) //Added this line for debug purpose
  109. writeLog(logfile, "[done] ", "Task finished successfully")
  110. moveLogFile(logfile, "done", uuid)
  111. fmt.Println("Done")
  112. }
  113. func initChk() {
  114. //Initiation Checking for all the required directories
  115. if !file_exists("log/") {
  116. os.MkdirAll("log/", 0777)
  117. }
  118. if !file_exists("log/done/") {
  119. os.MkdirAll("log/done/", 0777)
  120. }
  121. if !file_exists("log/error/") {
  122. os.MkdirAll("log/error/", 0777)
  123. }
  124. }
  125. //Main programming logic
  126. func main() {
  127. initChk()
  128. acceptOperations := []string{"copy", "copy_folder", "delete", "move", "move_folder"}
  129. if len(os.Args) == 1 {
  130. showHelp()
  131. return
  132. }
  133. //Get the command in using base64 and required log file uuid
  134. startSettings := os.Args[1:]
  135. uuid := startSettings[0]
  136. encodedCommand := startSettings[1]
  137. decodedCommand := decodeBase64(encodedCommand)
  138. filename := uuid + ".log"
  139. var command []string
  140. //Create the log file for this task
  141. logfile := "log/" + filename
  142. //Check if the given id exists already.
  143. if file_exists(logfile) || file_exists("log/done/"+filename) || file_exists("log/error/"+filename) {
  144. panic("ERROR. Required task already exists.")
  145. }
  146. //Create the log file for this task
  147. writeLog(logfile, "[init] ", "Task created on "+getTimestamp())
  148. err := json.Unmarshal([]byte(decodedCommand), &command)
  149. if err != nil {
  150. writeLog(logfile, "[error] ", "Unable to parse JSON string.")
  151. moveLogFile(logfile, "error", uuid)
  152. panic("ERROR. Unable to parse JSON string.")
  153. }
  154. //Check if the given file operation correct or not.
  155. correctOpr, _ := in_array(string(command[0]), acceptOperations)
  156. if !correctOpr {
  157. fmt.Println(string(command[0]))
  158. writeLog(logfile, "[error] ", "Invalid file operation. "+command[0]+" given.")
  159. moveLogFile(logfile, "error", uuid)
  160. panic("ERROR. Invalid file operation. " + command[0] + " given.")
  161. }
  162. writeLog(logfile, "[info] ", decodedCommand)
  163. //Start reading the command for file operations
  164. opr := command[0]
  165. if opr == "copy" {
  166. //Copy a given file to a given location
  167. from := command[1]
  168. target := command[2]
  169. if !validateCopySourceAndTarget(from, target) {
  170. writeLog(logfile, "[error] ", "Invalid source file or target directory.")
  171. moveLogFile(logfile, "error", uuid)
  172. panic("ERROR. Invalid source file or target directory.")
  173. }
  174. //Check if the parent dir exists
  175. dirname := path.Dir(target)
  176. if !file_exists(dirname) {
  177. os.MkdirAll(dirname, 0777)
  178. }
  179. Copy(from, target)
  180. finishFileOperation(logfile, uuid)
  181. } else if opr == "copy_folder" {
  182. //Copy a folder to a given location
  183. from := command[1]
  184. target := command[2]
  185. if !validateCopySourceAndTarget(from, target) {
  186. writeLog(logfile, "[error] ", "Invalid source file or target directory.")
  187. moveLogFile(logfile, "error", uuid)
  188. panic("ERROR. Invalid source file or target directory.")
  189. }
  190. Copy(from, target)
  191. finishFileOperation(logfile, uuid)
  192. } else if opr == "move" {
  193. //Move a file
  194. from := command[1]
  195. target := command[2]
  196. if !validateCopySourceAndTarget(from, target) {
  197. writeLog(logfile, "[error] ", "Invalid source file or target directory.")
  198. moveLogFile(logfile, "error", uuid)
  199. panic("ERROR. Invalid source file or target directory.")
  200. }
  201. /*
  202. err := os.Rename(from, target)
  203. if err != nil {
  204. writeLog(logfile, "[error] ", "Unable to move file due to the following error: "+err.Error())
  205. moveLogFile(logfile, "error", uuid)
  206. panic("ERROR. Unable to move file due to unknown error.")
  207. }*/
  208. //Replacing the rename with Copy + Delete to fix the golang issue of "Unable to move file to another drive"
  209. Copy(from, target)
  210. writeLog(logfile, "[info] ", "File Copy finished. Removing the original item")
  211. os.Remove(from)
  212. writeLog(logfile, "[info] ", "Original file removed. Removed filepath: "+from)
  213. finishFileOperation(logfile, uuid)
  214. } else if opr == "move_folder" {
  215. //Move a folder
  216. from := command[1]
  217. target := command[2]
  218. if !validateCopySourceAndTarget(from, target) {
  219. writeLog(logfile, "[error] ", "Invalid source file or target directory.")
  220. moveLogFile(logfile, "error", uuid)
  221. panic("ERROR. Invalid source file or target directory.")
  222. }
  223. /*
  224. err := os.Rename(from, target)
  225. if err != nil {
  226. writeLog(logfile, "[error] ", "Unable to move folder due to to the following error: "+err.Error())
  227. moveLogFile(logfile, "error", uuid)
  228. panic("ERROR. Unable to move folder due to unknown error.")
  229. }
  230. */
  231. Copy(from, target)
  232. writeLog(logfile, "[info] ", "File Copy finished. Removing the original item")
  233. os.RemoveAll(from)
  234. writeLog(logfile, "[info] ", "Original folder removed. Removed path: "+from)
  235. finishFileOperation(logfile, uuid)
  236. }
  237. }
  238. //Program required external library. Included in main to prevent download on other building platform.
  239. //Recursive Copy Lib, not sure why it can't be imported like other module. Hence directly copy and paste in the section below.
  240. const (
  241. // tmpPermissionForDirectory makes the destination directory writable,
  242. // so that stuff can be copied recursively even if any original directory is NOT writable.
  243. // See https://github.com/otiai10/copy/pull/9 for more information.
  244. tmpPermissionForDirectory = os.FileMode(0755)
  245. )
  246. // Copy copies src to dest, doesn't matter if src is a directory or a file
  247. func Copy(src, dest string) error {
  248. info, err := os.Lstat(src)
  249. if err != nil {
  250. return err
  251. }
  252. return copy(src, dest, info)
  253. }
  254. // copy dispatches copy-funcs according to the mode.
  255. // Because this "copy" could be called recursively,
  256. // "info" MUST be given here, NOT nil.
  257. func copy(src, dest string, info os.FileInfo) error {
  258. if info.Mode()&os.ModeSymlink != 0 {
  259. return lcopy(src, dest, info)
  260. }
  261. if info.IsDir() {
  262. return dcopy(src, dest, info)
  263. }
  264. return fcopy(src, dest, info)
  265. }
  266. // fcopy is for just a file,
  267. // with considering existence of parent directory
  268. // and file permission.
  269. func fcopy(src, dest string, info os.FileInfo) error {
  270. if err := os.MkdirAll(filepath.Dir(dest), os.ModePerm); err != nil {
  271. return err
  272. }
  273. f, err := os.Create(dest)
  274. if err != nil {
  275. return err
  276. }
  277. defer f.Close()
  278. if err = os.Chmod(f.Name(), info.Mode()); err != nil {
  279. return err
  280. }
  281. s, err := os.Open(src)
  282. if err != nil {
  283. return err
  284. }
  285. defer s.Close()
  286. _, err = io.Copy(f, s)
  287. return err
  288. }
  289. // dcopy is for a directory,
  290. // with scanning contents inside the directory
  291. // and pass everything to "copy" recursively.
  292. func dcopy(srcdir, destdir string, info os.FileInfo) error {
  293. originalMode := info.Mode()
  294. // Make dest dir with 0755 so that everything writable.
  295. if err := os.MkdirAll(destdir, tmpPermissionForDirectory); err != nil {
  296. return err
  297. }
  298. // Recover dir mode with original one.
  299. defer os.Chmod(destdir, originalMode)
  300. contents, err := ioutil.ReadDir(srcdir)
  301. if err != nil {
  302. return err
  303. }
  304. for _, content := range contents {
  305. cs, cd := filepath.Join(srcdir, content.Name()), filepath.Join(destdir, content.Name())
  306. if err := copy(cs, cd, content); err != nil {
  307. // If any error, exit immediately
  308. return err
  309. }
  310. }
  311. return nil
  312. }
  313. // lcopy is for a symlink,
  314. // with just creating a new symlink by replicating src symlink.
  315. func lcopy(src, dest string, info os.FileInfo) error {
  316. src, err := os.Readlink(src)
  317. if err != nil {
  318. return err
  319. }
  320. return os.Symlink(src, dest)
  321. }