pre-upload action: add file open flags

Reading the flags the hook receiver can detect if the client wants to
truncate the target file
This commit is contained in:
Nicola Murino
2021-05-31 22:33:23 +02:00
parent c63b923ec3
commit c1239fbf59
10 changed files with 33 additions and 26 deletions

View File

@@ -50,7 +50,7 @@ func InitializeActionHandler(handler ActionHandler) {
} }
// ExecutePreAction executes a pre-* action and returns the result // ExecutePreAction executes a pre-* action and returns the result
func ExecutePreAction(user *dataprovider.User, operation, filePath, virtualPath, protocol string, fileSize int64) error { func ExecutePreAction(user *dataprovider.User, operation, filePath, virtualPath, protocol string, fileSize int64, openFlags int) error {
if !utils.IsStringInSlice(operation, Config.Actions.ExecuteOn) { if !utils.IsStringInSlice(operation, Config.Actions.ExecuteOn) {
// for pre-delete we execute the internal handling on error, so we must return errUnconfiguredAction. // for pre-delete we execute the internal handling on error, so we must return errUnconfiguredAction.
// Other pre action will deny the operation on error so if we have no configuration we must return // Other pre action will deny the operation on error so if we have no configuration we must return
@@ -60,13 +60,13 @@ func ExecutePreAction(user *dataprovider.User, operation, filePath, virtualPath,
} }
return nil return nil
} }
notification := newActionNotification(user, operation, filePath, virtualPath, "", "", protocol, fileSize, nil) notification := newActionNotification(user, operation, filePath, virtualPath, "", "", protocol, fileSize, openFlags, nil)
return actionHandler.Handle(notification) return actionHandler.Handle(notification)
} }
// ExecuteActionNotification executes the defined hook, if any, for the specified action // ExecuteActionNotification executes the defined hook, if any, for the specified action
func ExecuteActionNotification(user *dataprovider.User, operation, filePath, virtualPath, target, sshCmd, protocol string, fileSize int64, err error) { func ExecuteActionNotification(user *dataprovider.User, operation, filePath, virtualPath, target, sshCmd, protocol string, fileSize int64, err error) {
notification := newActionNotification(user, operation, filePath, virtualPath, target, sshCmd, protocol, fileSize, err) notification := newActionNotification(user, operation, filePath, virtualPath, target, sshCmd, protocol, fileSize, 0, err)
if utils.IsStringInSlice(operation, Config.Actions.ExecuteSync) { if utils.IsStringInSlice(operation, Config.Actions.ExecuteSync) {
actionHandler.Handle(notification) //nolint:errcheck actionHandler.Handle(notification) //nolint:errcheck
@@ -94,12 +94,14 @@ type ActionNotification struct {
Endpoint string `json:"endpoint,omitempty"` Endpoint string `json:"endpoint,omitempty"`
Status int `json:"status"` Status int `json:"status"`
Protocol string `json:"protocol"` Protocol string `json:"protocol"`
OpenFlags int `json:"open_flags,omitempty"`
} }
func newActionNotification( func newActionNotification(
user *dataprovider.User, user *dataprovider.User,
operation, filePath, virtualPath, target, sshCmd, protocol string, operation, filePath, virtualPath, target, sshCmd, protocol string,
fileSize int64, fileSize int64,
openFlags int,
err error, err error,
) *ActionNotification { ) *ActionNotification {
var bucket, endpoint string var bucket, endpoint string
@@ -142,6 +144,7 @@ func newActionNotification(
Endpoint: endpoint, Endpoint: endpoint,
Status: status, Status: status,
Protocol: protocol, Protocol: protocol,
OpenFlags: openFlags,
} }
} }
@@ -230,5 +233,6 @@ func notificationAsEnvVars(notification *ActionNotification) []string {
fmt.Sprintf("SFTPGO_ACTION_ENDPOINT=%v", notification.Endpoint), fmt.Sprintf("SFTPGO_ACTION_ENDPOINT=%v", notification.Endpoint),
fmt.Sprintf("SFTPGO_ACTION_STATUS=%v", notification.Status), fmt.Sprintf("SFTPGO_ACTION_STATUS=%v", notification.Status),
fmt.Sprintf("SFTPGO_ACTION_PROTOCOL=%v", notification.Protocol), fmt.Sprintf("SFTPGO_ACTION_PROTOCOL=%v", notification.Protocol),
fmt.Sprintf("SFTPGO_ACTION_OPEN_FLAGS=%v", notification.OpenFlags),
} }
} }

View File

@@ -35,38 +35,39 @@ func TestNewActionNotification(t *testing.T) {
user.FsConfig.SFTPConfig = vfs.SFTPFsConfig{ user.FsConfig.SFTPConfig = vfs.SFTPFsConfig{
Endpoint: "sftpendpoint", Endpoint: "sftpendpoint",
} }
a := newActionNotification(user, operationDownload, "path", "vpath", "target", "", ProtocolSFTP, 123, errors.New("fake error")) a := newActionNotification(user, operationDownload, "path", "vpath", "target", "", ProtocolSFTP, 123, 0, errors.New("fake error"))
assert.Equal(t, user.Username, a.Username) assert.Equal(t, user.Username, a.Username)
assert.Equal(t, 0, len(a.Bucket)) assert.Equal(t, 0, len(a.Bucket))
assert.Equal(t, 0, len(a.Endpoint)) assert.Equal(t, 0, len(a.Endpoint))
assert.Equal(t, 0, a.Status) assert.Equal(t, 0, a.Status)
user.FsConfig.Provider = vfs.S3FilesystemProvider user.FsConfig.Provider = vfs.S3FilesystemProvider
a = newActionNotification(user, operationDownload, "path", "vpath", "target", "", ProtocolSSH, 123, nil) a = newActionNotification(user, operationDownload, "path", "vpath", "target", "", ProtocolSSH, 123, 0, nil)
assert.Equal(t, "s3bucket", a.Bucket) assert.Equal(t, "s3bucket", a.Bucket)
assert.Equal(t, "endpoint", a.Endpoint) assert.Equal(t, "endpoint", a.Endpoint)
assert.Equal(t, 1, a.Status) assert.Equal(t, 1, a.Status)
user.FsConfig.Provider = vfs.GCSFilesystemProvider user.FsConfig.Provider = vfs.GCSFilesystemProvider
a = newActionNotification(user, operationDownload, "path", "vpath", "target", "", ProtocolSCP, 123, ErrQuotaExceeded) a = newActionNotification(user, operationDownload, "path", "vpath", "target", "", ProtocolSCP, 123, 0, ErrQuotaExceeded)
assert.Equal(t, "gcsbucket", a.Bucket) assert.Equal(t, "gcsbucket", a.Bucket)
assert.Equal(t, 0, len(a.Endpoint)) assert.Equal(t, 0, len(a.Endpoint))
assert.Equal(t, 2, a.Status) assert.Equal(t, 2, a.Status)
user.FsConfig.Provider = vfs.AzureBlobFilesystemProvider user.FsConfig.Provider = vfs.AzureBlobFilesystemProvider
a = newActionNotification(user, operationDownload, "path", "vpath", "target", "", ProtocolSCP, 123, nil) a = newActionNotification(user, operationDownload, "path", "vpath", "target", "", ProtocolSCP, 123, 0, nil)
assert.Equal(t, "azcontainer", a.Bucket) assert.Equal(t, "azcontainer", a.Bucket)
assert.Equal(t, "azsasurl", a.Endpoint) assert.Equal(t, "azsasurl", a.Endpoint)
assert.Equal(t, 1, a.Status) assert.Equal(t, 1, a.Status)
user.FsConfig.AzBlobConfig.SASURL = "" user.FsConfig.AzBlobConfig.SASURL = ""
a = newActionNotification(user, operationDownload, "path", "vpath", "target", "", ProtocolSCP, 123, nil) a = newActionNotification(user, operationDownload, "path", "vpath", "target", "", ProtocolSCP, 123, os.O_APPEND, nil)
assert.Equal(t, "azcontainer", a.Bucket) assert.Equal(t, "azcontainer", a.Bucket)
assert.Equal(t, "azendpoint", a.Endpoint) assert.Equal(t, "azendpoint", a.Endpoint)
assert.Equal(t, 1, a.Status) assert.Equal(t, 1, a.Status)
assert.Equal(t, os.O_APPEND, a.OpenFlags)
user.FsConfig.Provider = vfs.SFTPFilesystemProvider user.FsConfig.Provider = vfs.SFTPFilesystemProvider
a = newActionNotification(user, operationDownload, "path", "vpath", "target", "", ProtocolSFTP, 123, nil) a = newActionNotification(user, operationDownload, "path", "vpath", "target", "", ProtocolSFTP, 123, 0, nil)
assert.Equal(t, "sftpendpoint", a.Endpoint) assert.Equal(t, "sftpendpoint", a.Endpoint)
} }
@@ -80,7 +81,7 @@ func TestActionHTTP(t *testing.T) {
user := &dataprovider.User{ user := &dataprovider.User{
Username: "username", Username: "username",
} }
a := newActionNotification(user, operationDownload, "path", "vpath", "target", "", ProtocolSFTP, 123, nil) a := newActionNotification(user, operationDownload, "path", "vpath", "target", "", ProtocolSFTP, 123, 0, nil)
err := actionHandler.Handle(a) err := actionHandler.Handle(a)
assert.NoError(t, err) assert.NoError(t, err)
@@ -113,7 +114,7 @@ func TestActionCMD(t *testing.T) {
user := &dataprovider.User{ user := &dataprovider.User{
Username: "username", Username: "username",
} }
a := newActionNotification(user, operationDownload, "path", "vpath", "target", "", ProtocolSFTP, 123, nil) a := newActionNotification(user, operationDownload, "path", "vpath", "target", "", ProtocolSFTP, 123, 0, nil)
err = actionHandler.Handle(a) err = actionHandler.Handle(a)
assert.NoError(t, err) assert.NoError(t, err)
@@ -137,7 +138,7 @@ func TestWrongActions(t *testing.T) {
Username: "username", Username: "username",
} }
a := newActionNotification(user, operationUpload, "", "", "", "", ProtocolSFTP, 123, nil) a := newActionNotification(user, operationUpload, "", "", "", "", ProtocolSFTP, 123, 0, nil)
err := actionHandler.Handle(a) err := actionHandler.Handle(a)
assert.Error(t, err, "action with bad command must fail") assert.Error(t, err, "action with bad command must fail")

View File

@@ -261,7 +261,7 @@ func (c *BaseConnection) RemoveFile(fs vfs.Fs, fsPath, virtualPath string, info
} }
size := info.Size() size := info.Size()
actionErr := ExecutePreAction(&c.User, operationPreDelete, fsPath, virtualPath, c.protocol, size) actionErr := ExecutePreAction(&c.User, operationPreDelete, fsPath, virtualPath, c.protocol, size, 0)
if actionErr == nil { if actionErr == nil {
c.Log(logger.LevelDebug, "remove for path %#v handled by pre-delete action", fsPath) c.Log(logger.LevelDebug, "remove for path %#v handled by pre-delete action", fsPath)
} else { } else {

View File

@@ -43,6 +43,7 @@ The external program can also read the following environment variables:
- `SFTPGO_ACTION_ENDPOINT`, non-empty for S3, SFTP and Azure backend if configured. For Azure this is the SAS URL, if configured otherwise the endpoint - `SFTPGO_ACTION_ENDPOINT`, non-empty for S3, SFTP and Azure backend if configured. For Azure this is the SAS URL, if configured otherwise the endpoint
- `SFTPGO_ACTION_STATUS`, integer. Status for `upload`, `download` and `ssh_cmd` actions. 0 means a generic error occurred. 1 means no error, 2 means quota exceeded error - `SFTPGO_ACTION_STATUS`, integer. Status for `upload`, `download` and `ssh_cmd` actions. 0 means a generic error occurred. 1 means no error, 2 means quota exceeded error
- `SFTPGO_ACTION_PROTOCOL`, string. Possible values are `SSH`, `SFTP`, `SCP`, `FTP`, `DAV`, `HTTP` - `SFTPGO_ACTION_PROTOCOL`, string. Possible values are `SSH`, `SFTP`, `SCP`, `FTP`, `DAV`, `HTTP`
- `SFTPGO_ACTION_OPEN_FLAGS`, integer. File open flags, can be non-zero for `pre-upload` action. If `SFTPGO_ACTION_FILE_SIZE` is greater than zero and `SFTPGO_ACTION_OPEN_FLAGS&512 == 0` the target file will not be truncated
Previous global environment variables aren't cleared when the script is called. Previous global environment variables aren't cleared when the script is called.
The program must finish within 30 seconds. The program must finish within 30 seconds.
@@ -60,6 +61,7 @@ If the `hook` defines an HTTP URL then this URL will be invoked as HTTP POST. Th
- `endpoint`, included for S3, SFTP and Azure backend if configured. For Azure this is the SAS URL, if configured, otherwise the endpoint - `endpoint`, included for S3, SFTP and Azure backend if configured. For Azure this is the SAS URL, if configured, otherwise the endpoint
- `status`, integer. Status for `upload`, `download` and `ssh_cmd` actions. 0 means a generic error occurred. 1 means no error, 2 means quota exceeded error - `status`, integer. Status for `upload`, `download` and `ssh_cmd` actions. 0 means a generic error occurred. 1 means no error, 2 means quota exceeded error
- `protocol`, string. Possible values are `SSH`, `SFTP`, `SCP`, `FTP`, `DAV`, `HTTP` - `protocol`, string. Possible values are `SSH`, `SFTP`, `SCP`, `FTP`, `DAV`, `HTTP`
- `open_flags`, integer, File open flags, can be non-zero for `pre-upload` action. If `file_size` is greater than zero and `file_size&512 == 0` the target file will not be truncated
The HTTP hook will use the global configuration for HTTP clients and will respect the retry configurations. The HTTP hook will use the global configuration for HTTP clients and will respect the retry configurations.

View File

@@ -297,7 +297,7 @@ func (c *Connection) downloadFile(fs vfs.Fs, fsPath, ftpPath string, offset int6
return nil, c.GetPermissionDeniedError() return nil, c.GetPermissionDeniedError()
} }
if err := common.ExecutePreAction(&c.User, common.OperationPreDownload, fsPath, ftpPath, c.GetProtocol(), 0); err != nil { if err := common.ExecutePreAction(&c.User, common.OperationPreDownload, fsPath, ftpPath, c.GetProtocol(), 0, 0); err != nil {
c.Log(logger.LevelDebug, "download for file %#v denied by pre action: %v", ftpPath, err) c.Log(logger.LevelDebug, "download for file %#v denied by pre action: %v", ftpPath, err)
return nil, c.GetPermissionDeniedError() return nil, c.GetPermissionDeniedError()
} }
@@ -358,7 +358,7 @@ func (c *Connection) handleFTPUploadToNewFile(fs vfs.Fs, resolvedPath, filePath,
c.Log(logger.LevelInfo, "denying file write due to quota limits") c.Log(logger.LevelInfo, "denying file write due to quota limits")
return nil, common.ErrQuotaExceeded return nil, common.ErrQuotaExceeded
} }
if err := common.ExecutePreAction(&c.User, common.OperationPreUpload, resolvedPath, requestPath, c.GetProtocol(), 0); err != nil { if err := common.ExecutePreAction(&c.User, common.OperationPreUpload, resolvedPath, requestPath, c.GetProtocol(), 0, 0); err != nil {
c.Log(logger.LevelDebug, "upload for file %#v denied by pre action: %v", requestPath, err) c.Log(logger.LevelDebug, "upload for file %#v denied by pre action: %v", requestPath, err)
return nil, c.GetPermissionDeniedError() return nil, c.GetPermissionDeniedError()
} }
@@ -388,7 +388,7 @@ func (c *Connection) handleFTPUploadToExistingFile(fs vfs.Fs, flags int, resolve
c.Log(logger.LevelInfo, "denying file write due to quota limits") c.Log(logger.LevelInfo, "denying file write due to quota limits")
return nil, common.ErrQuotaExceeded return nil, common.ErrQuotaExceeded
} }
if err := common.ExecutePreAction(&c.User, common.OperationPreUpload, resolvedPath, requestPath, c.GetProtocol(), fileSize); err != nil { if err := common.ExecutePreAction(&c.User, common.OperationPreUpload, resolvedPath, requestPath, c.GetProtocol(), fileSize, flags); err != nil {
c.Log(logger.LevelDebug, "upload for file %#v denied by pre action: %v", requestPath, err) c.Log(logger.LevelDebug, "upload for file %#v denied by pre action: %v", requestPath, err)
return nil, c.GetPermissionDeniedError() return nil, c.GetPermissionDeniedError()
} }

View File

@@ -93,7 +93,7 @@ func (c *Connection) getFileReader(name string, offset int64, method string) (io
} }
if method != http.MethodHead { if method != http.MethodHead {
if err := common.ExecutePreAction(&c.User, common.OperationPreDownload, p, name, c.GetProtocol(), 0); err != nil { if err := common.ExecutePreAction(&c.User, common.OperationPreDownload, p, name, c.GetProtocol(), 0, 0); err != nil {
c.Log(logger.LevelDebug, "download for file %#v denied by pre action: %v", name, err) c.Log(logger.LevelDebug, "download for file %#v denied by pre action: %v", name, err)
return nil, c.GetPermissionDeniedError() return nil, c.GetPermissionDeniedError()
} }

View File

@@ -59,7 +59,7 @@ func (c *Connection) Fileread(request *sftp.Request) (io.ReaderAt, error) {
return nil, err return nil, err
} }
if err := common.ExecutePreAction(&c.User, common.OperationPreDownload, p, request.Filepath, c.GetProtocol(), 0); err != nil { if err := common.ExecutePreAction(&c.User, common.OperationPreDownload, p, request.Filepath, c.GetProtocol(), 0, 0); err != nil {
c.Log(logger.LevelDebug, "download for file %#v denied by pre action: %v", request.Filepath, err) c.Log(logger.LevelDebug, "download for file %#v denied by pre action: %v", request.Filepath, err)
return nil, c.GetPermissionDeniedError() return nil, c.GetPermissionDeniedError()
} }
@@ -330,7 +330,7 @@ func (c *Connection) handleSFTPUploadToNewFile(fs vfs.Fs, resolvedPath, filePath
return nil, sftp.ErrSSHFxFailure return nil, sftp.ErrSSHFxFailure
} }
if err := common.ExecutePreAction(&c.User, common.OperationPreUpload, resolvedPath, requestPath, c.GetProtocol(), 0); err != nil { if err := common.ExecutePreAction(&c.User, common.OperationPreUpload, resolvedPath, requestPath, c.GetProtocol(), 0, 0); err != nil {
c.Log(logger.LevelDebug, "upload for file %#v denied by pre action: %v", requestPath, err) c.Log(logger.LevelDebug, "upload for file %#v denied by pre action: %v", requestPath, err)
return nil, c.GetPermissionDeniedError() return nil, c.GetPermissionDeniedError()
} }
@@ -361,13 +361,13 @@ func (c *Connection) handleSFTPUploadToExistingFile(fs vfs.Fs, pflags sftp.FileO
c.Log(logger.LevelInfo, "denying file write due to quota limits") c.Log(logger.LevelInfo, "denying file write due to quota limits")
return nil, sftp.ErrSSHFxFailure return nil, sftp.ErrSSHFxFailure
} }
if err := common.ExecutePreAction(&c.User, common.OperationPreUpload, resolvedPath, requestPath, c.GetProtocol(), fileSize); err != nil { osFlags := getOSOpenFlags(pflags)
if err := common.ExecutePreAction(&c.User, common.OperationPreUpload, resolvedPath, requestPath, c.GetProtocol(), fileSize, osFlags); err != nil {
c.Log(logger.LevelDebug, "upload for file %#v denied by pre action: %v", requestPath, err) c.Log(logger.LevelDebug, "upload for file %#v denied by pre action: %v", requestPath, err)
return nil, c.GetPermissionDeniedError() return nil, c.GetPermissionDeniedError()
} }
minWriteOffset := int64(0) minWriteOffset := int64(0)
osFlags := getOSOpenFlags(pflags)
isTruncate := osFlags&os.O_TRUNC != 0 isTruncate := osFlags&os.O_TRUNC != 0
// for upload resumes OpenSSH sets the APPEND flag while WinSCP does not set it, // for upload resumes OpenSSH sets the APPEND flag while WinSCP does not set it,
// so we suppose this is an upload resume if the TRUNCATE flag is not set // so we suppose this is an upload resume if the TRUNCATE flag is not set

View File

@@ -219,7 +219,7 @@ func (c *scpCommand) handleUploadFile(fs vfs.Fs, resolvedPath, filePath string,
c.sendErrorMessage(fs, err) c.sendErrorMessage(fs, err)
return err return err
} }
err := common.ExecutePreAction(&c.connection.User, common.OperationPreUpload, resolvedPath, requestPath, c.connection.GetProtocol(), fileSize) err := common.ExecutePreAction(&c.connection.User, common.OperationPreUpload, resolvedPath, requestPath, c.connection.GetProtocol(), fileSize, os.O_TRUNC)
if err != nil { if err != nil {
c.connection.Log(logger.LevelDebug, "upload for file %#v denied by pre action: %v", requestPath, err) c.connection.Log(logger.LevelDebug, "upload for file %#v denied by pre action: %v", requestPath, err)
err = c.connection.GetPermissionDeniedError() err = c.connection.GetPermissionDeniedError()
@@ -514,7 +514,7 @@ func (c *scpCommand) handleDownload(filePath string) error {
return common.ErrPermissionDenied return common.ErrPermissionDenied
} }
if err := common.ExecutePreAction(&c.connection.User, common.OperationPreDownload, p, filePath, c.connection.GetProtocol(), 0); err != nil { if err := common.ExecutePreAction(&c.connection.User, common.OperationPreDownload, p, filePath, c.connection.GetProtocol(), 0, 0); err != nil {
c.connection.Log(logger.LevelDebug, "download for file %#v denied by pre action: %v", filePath, err) c.connection.Log(logger.LevelDebug, "download for file %#v denied by pre action: %v", filePath, err)
c.sendErrorMessage(fs, common.ErrPermissionDenied) c.sendErrorMessage(fs, common.ErrPermissionDenied)
return common.ErrPermissionDenied return common.ErrPermissionDenied

View File

@@ -148,7 +148,7 @@ func (f *webDavFile) Read(p []byte) (n int, err error) {
return 0, f.Connection.GetPermissionDeniedError() return 0, f.Connection.GetPermissionDeniedError()
} }
err := common.ExecutePreAction(&f.Connection.User, common.OperationPreDownload, f.GetFsPath(), f.GetVirtualPath(), err := common.ExecutePreAction(&f.Connection.User, common.OperationPreDownload, f.GetFsPath(), f.GetVirtualPath(),
f.Connection.GetProtocol(), 0) f.Connection.GetProtocol(), 0, 0)
if err != nil { if err != nil {
f.Connection.Log(logger.LevelDebug, "download for file %#v denied by pre action: %v", f.GetVirtualPath(), err) f.Connection.Log(logger.LevelDebug, "download for file %#v denied by pre action: %v", f.GetVirtualPath(), err)
return 0, f.Connection.GetPermissionDeniedError() return 0, f.Connection.GetPermissionDeniedError()

View File

@@ -194,7 +194,7 @@ func (c *Connection) handleUploadToNewFile(fs vfs.Fs, resolvedPath, filePath, re
c.Log(logger.LevelInfo, "denying file write due to quota limits") c.Log(logger.LevelInfo, "denying file write due to quota limits")
return nil, common.ErrQuotaExceeded return nil, common.ErrQuotaExceeded
} }
if err := common.ExecutePreAction(&c.User, common.OperationPreUpload, resolvedPath, requestPath, c.GetProtocol(), 0); err != nil { if err := common.ExecutePreAction(&c.User, common.OperationPreUpload, resolvedPath, requestPath, c.GetProtocol(), 0, 0); err != nil {
c.Log(logger.LevelDebug, "upload for file %#v denied by pre action: %v", requestPath, err) c.Log(logger.LevelDebug, "upload for file %#v denied by pre action: %v", requestPath, err)
return nil, c.GetPermissionDeniedError() return nil, c.GetPermissionDeniedError()
} }
@@ -223,7 +223,7 @@ func (c *Connection) handleUploadToExistingFile(fs vfs.Fs, resolvedPath, filePat
c.Log(logger.LevelInfo, "denying file write due to quota limits") c.Log(logger.LevelInfo, "denying file write due to quota limits")
return nil, common.ErrQuotaExceeded return nil, common.ErrQuotaExceeded
} }
if err := common.ExecutePreAction(&c.User, common.OperationPreUpload, resolvedPath, requestPath, c.GetProtocol(), fileSize); err != nil { if err := common.ExecutePreAction(&c.User, common.OperationPreUpload, resolvedPath, requestPath, c.GetProtocol(), fileSize, os.O_TRUNC); err != nil {
c.Log(logger.LevelDebug, "upload for file %#v denied by pre action: %v", requestPath, err) c.Log(logger.LevelDebug, "upload for file %#v denied by pre action: %v", requestPath, err)
return nil, c.GetPermissionDeniedError() return nil, c.GetPermissionDeniedError()
} }