diff --git a/router/router.go b/router/router.go index a8bd839..824acfa 100644 --- a/router/router.go +++ b/router/router.go @@ -87,6 +87,7 @@ func Configure() *gin.Engine { files.POST("/delete", postServerDeleteFiles) files.POST("/compress", postServerCompressFiles) files.POST("/decompress", postServerDecompressFiles) + files.POST("/chmod", postServerChmodFile) } backup := server.Group("/backup") diff --git a/router/router_server_files.go b/router/router_server_files.go index db46d23..bf8f183 100644 --- a/router/router_server_files.go +++ b/router/router_server_files.go @@ -365,6 +365,62 @@ func postServerDecompressFiles(c *gin.Context) { c.Status(http.StatusNoContent) } +type chmodFile struct { + File string `json:"file"` + Mode os.FileMode `json:"mode"` +} + +func postServerChmodFile(c *gin.Context) { + s := GetServer(c.Param("server")) + + var data struct { + Root string `json:"root"` + Files []chmodFile `json:"files"` + } + + if err := c.BindJSON(&data); err != nil { + return + } + + if len(data.Files) == 0 { + c.AbortWithStatusJSON(http.StatusUnprocessableEntity, gin.H{ + "error": "No files to chmod were provided.", + }) + return + } + + g, ctx := errgroup.WithContext(context.Background()) + + // Loop over the array of files passed in and perform the move or rename action against each. + for _, p := range data.Files { + g.Go(func() error { + select { + case <-ctx.Done(): + return ctx.Err() + default: + if err := s.Filesystem().Chmod(path.Join(data.Root, p.File), p.Mode); err != nil { + // Return nil if the error is an is not exists. + // NOTE: os.IsNotExist() does not work if the error is wrapped. + if errors.Is(err, os.ErrNotExist) { + return nil + } + + return err + } + + return nil + } + }) + } + + if err := g.Wait(); err != nil { + TrackedServerError(err, s).AbortFilesystemError(c) + return + } + + c.Status(http.StatusNoContent) +} + func postServerUploadFiles(c *gin.Context) { token := tokens.UploadPayload{} if err := tokens.ParseToken([]byte(c.Query("token")), &token); err != nil { diff --git a/server/filesystem/filesystem.go b/server/filesystem/filesystem.go index d9b8d06..f04a7f2 100644 --- a/server/filesystem/filesystem.go +++ b/server/filesystem/filesystem.go @@ -227,6 +227,23 @@ func (fs *Filesystem) Chown(path string) error { }) } +func (fs *Filesystem) Chmod(path string, mode os.FileMode) error { + cleaned, err := fs.SafePath(path) + if err != nil { + return err + } + + if fs.isTest { + return nil + } + + if err := os.Chmod(cleaned, mode); err != nil { + return err + } + + return nil +} + // Begin looping up to 50 times to try and create a unique copy file name. This will take // an input of "file.txt" and generate "file copy.txt". If that name is already taken, it will // then try to write "file copy 2.txt" and so on, until reaching 50 loops. At that point we