main.go 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349
  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. } else if target == "error" {
  98. os.Rename(logfile, "log/error/"+uuid+".log")
  99. } else {
  100. //What happened?o
  101. panic("ERROR. Undefined log file sattle location.")
  102. }
  103. }
  104. func finishFileOperation(logfile string, uuid string) {
  105. //Finishing the file operation.
  106. writeLog(logfile, "[done] ", "Task finished successfully")
  107. moveLogFile(logfile, "done", uuid)
  108. fmt.Println("Done")
  109. }
  110. func initChk() {
  111. //Initiation Checking for all the required directories
  112. if !file_exists("log/") {
  113. os.MkdirAll("log/", 0777)
  114. }
  115. if !file_exists("log/done/") {
  116. os.MkdirAll("log/done/", 0777)
  117. }
  118. if !file_exists("log/error/") {
  119. os.MkdirAll("log/error/", 0777)
  120. }
  121. }
  122. //Main programming logic
  123. func main() {
  124. initChk()
  125. acceptOperations := []string{"copy", "copy_folder", "delete", "move", "move_folder"}
  126. if len(os.Args) == 1 {
  127. showHelp()
  128. return
  129. }
  130. //Get the command in using base64 and required log file uuid
  131. startSettings := os.Args[1:]
  132. uuid := startSettings[0]
  133. encodedCommand := startSettings[1]
  134. decodedCommand := decodeBase64(encodedCommand)
  135. filename := uuid + ".log"
  136. var command []string
  137. //Create the log file for this task
  138. logfile := "log/" + filename
  139. //Check if the given id exists already.
  140. if file_exists(logfile) || file_exists("log/done/"+filename) || file_exists("log/error/"+filename) {
  141. panic("ERROR. Required task already exists.")
  142. }
  143. //Create the log file for this task
  144. writeLog(logfile, "[init] ", "Task created on "+getTimestamp())
  145. err := json.Unmarshal([]byte(decodedCommand), &command)
  146. if err != nil {
  147. writeLog(logfile, "[error] ", "Unable to parse JSON string.")
  148. moveLogFile(logfile, "error", uuid)
  149. panic("ERROR. Unable to parse JSON string.")
  150. }
  151. //Check if the given file operation correct or not.
  152. correctOpr, _ := in_array(string(command[0]), acceptOperations)
  153. if !correctOpr {
  154. fmt.Println(string(command[0]))
  155. writeLog(logfile, "[error] ", "Invalid file operation. "+command[0]+" given.")
  156. moveLogFile(logfile, "error", uuid)
  157. panic("ERROR. Invalid file operation. " + command[0] + " given.")
  158. }
  159. writeLog(logfile, "[info] ", decodedCommand)
  160. //Start reading the command for file operations
  161. opr := command[0]
  162. if opr == "copy" {
  163. //Copy a given file to a given location
  164. from := command[1]
  165. target := command[2]
  166. if !validateCopySourceAndTarget(from, target) {
  167. writeLog(logfile, "[error] ", "Invalid source file or target directory.")
  168. moveLogFile(logfile, "error", uuid)
  169. panic("ERROR. Invalid source file or target directory.")
  170. }
  171. //Check if the parent dir exists
  172. dirname := path.Dir(target)
  173. if !file_exists(dirname) {
  174. os.MkdirAll(dirname, 0777)
  175. }
  176. Copy(from, target)
  177. finishFileOperation(logfile, uuid)
  178. } else if opr == "copy_folder" {
  179. //Copy a folder to a given location
  180. from := command[1]
  181. target := command[2]
  182. if !validateCopySourceAndTarget(from, target) {
  183. writeLog(logfile, "[error] ", "Invalid source file or target directory.")
  184. moveLogFile(logfile, "error", uuid)
  185. panic("ERROR. Invalid source file or target directory.")
  186. }
  187. Copy(from, target)
  188. finishFileOperation(logfile, uuid)
  189. } else if opr == "move" {
  190. //Move a file
  191. from := command[1]
  192. target := command[2]
  193. if !validateCopySourceAndTarget(from, target) {
  194. writeLog(logfile, "[error] ", "Invalid source file or target directory.")
  195. moveLogFile(logfile, "error", uuid)
  196. panic("ERROR. Invalid source file or target directory.")
  197. }
  198. err := os.Rename(from, target)
  199. if err != nil {
  200. writeLog(logfile, "[error] ", "Unable to move file due to unknown error.")
  201. moveLogFile(logfile, "error", uuid)
  202. panic("ERROR. Unable to move file due to unknown error.")
  203. }
  204. finishFileOperation(logfile, uuid)
  205. } else if opr == "move_folder" {
  206. //Move a folder
  207. from := command[1]
  208. target := command[2]
  209. if !validateCopySourceAndTarget(from, target) {
  210. writeLog(logfile, "[error] ", "Invalid source file or target directory.")
  211. moveLogFile(logfile, "error", uuid)
  212. panic("ERROR. Invalid source file or target directory.")
  213. }
  214. err := os.Rename(from, target)
  215. if err != nil {
  216. writeLog(logfile, "[error] ", "Unable to move folder due to unknown error.")
  217. moveLogFile(logfile, "error", uuid)
  218. panic("ERROR. Unable to move folder due to unknown error.")
  219. }
  220. finishFileOperation(logfile, uuid)
  221. }
  222. }
  223. //Program required external library. Included in main to prevent download on other building platform.
  224. //Recursive Copy Lib, not sure why it can't be imported like other module. Hence directly copy and paste in the section below.
  225. const (
  226. // tmpPermissionForDirectory makes the destination directory writable,
  227. // so that stuff can be copied recursively even if any original directory is NOT writable.
  228. // See https://github.com/otiai10/copy/pull/9 for more information.
  229. tmpPermissionForDirectory = os.FileMode(0755)
  230. )
  231. // Copy copies src to dest, doesn't matter if src is a directory or a file
  232. func Copy(src, dest string) error {
  233. info, err := os.Lstat(src)
  234. if err != nil {
  235. return err
  236. }
  237. return copy(src, dest, info)
  238. }
  239. // copy dispatches copy-funcs according to the mode.
  240. // Because this "copy" could be called recursively,
  241. // "info" MUST be given here, NOT nil.
  242. func copy(src, dest string, info os.FileInfo) error {
  243. if info.Mode()&os.ModeSymlink != 0 {
  244. return lcopy(src, dest, info)
  245. }
  246. if info.IsDir() {
  247. return dcopy(src, dest, info)
  248. }
  249. return fcopy(src, dest, info)
  250. }
  251. // fcopy is for just a file,
  252. // with considering existence of parent directory
  253. // and file permission.
  254. func fcopy(src, dest string, info os.FileInfo) error {
  255. if err := os.MkdirAll(filepath.Dir(dest), os.ModePerm); err != nil {
  256. return err
  257. }
  258. f, err := os.Create(dest)
  259. if err != nil {
  260. return err
  261. }
  262. defer f.Close()
  263. if err = os.Chmod(f.Name(), info.Mode()); err != nil {
  264. return err
  265. }
  266. s, err := os.Open(src)
  267. if err != nil {
  268. return err
  269. }
  270. defer s.Close()
  271. _, err = io.Copy(f, s)
  272. return err
  273. }
  274. // dcopy is for a directory,
  275. // with scanning contents inside the directory
  276. // and pass everything to "copy" recursively.
  277. func dcopy(srcdir, destdir string, info os.FileInfo) error {
  278. originalMode := info.Mode()
  279. // Make dest dir with 0755 so that everything writable.
  280. if err := os.MkdirAll(destdir, tmpPermissionForDirectory); err != nil {
  281. return err
  282. }
  283. // Recover dir mode with original one.
  284. defer os.Chmod(destdir, originalMode)
  285. contents, err := ioutil.ReadDir(srcdir)
  286. if err != nil {
  287. return err
  288. }
  289. for _, content := range contents {
  290. cs, cd := filepath.Join(srcdir, content.Name()), filepath.Join(destdir, content.Name())
  291. if err := copy(cs, cd, content); err != nil {
  292. // If any error, exit immediately
  293. return err
  294. }
  295. }
  296. return nil
  297. }
  298. // lcopy is for a symlink,
  299. // with just creating a new symlink by replicating src symlink.
  300. func lcopy(src, dest string, info os.FileInfo) error {
  301. src, err := os.Readlink(src)
  302. if err != nil {
  303. return err
  304. }
  305. return os.Symlink(src, dest)
  306. }