sftpd: make file/dir removal and creation more standard

- remove a non empty directory. Before: the directory contents were
removed recursively. Now: removing a non empty directory fails.

- make a directory in a non existent path: Before: any necessary parents
were created. Now: it fails.

- remove a file. Before: files, directories and symlinks were removed.
Now: only files and symlink are removed, removing a directory using "Remove"
instead of "Rmdir" fails.

Upload a file in a non existent directory. Before: any necessary parents
were created. Now: it fails.

Now SFTPGo behaves as OpenSSH.
This commit is contained in:
Nicola Murino
2019-10-16 07:48:22 +02:00
parent f98a29a1e0
commit 8682ae4a54
2 changed files with 92 additions and 70 deletions

View File

@@ -279,21 +279,23 @@ func (c Connection) handleSFTPRmdir(path string) error {
return sftp.ErrSSHFxPermissionDenied
}
numFiles, size, fileList, err := utils.ScanDirContents(path)
if err != nil {
c.Log(logger.LevelError, logSender, "failed to remove directory %#v, scanning error: %v", path, err)
var fi os.FileInfo
var err error
if fi, err = os.Lstat(path); err != nil {
c.Log(logger.LevelError, logSender, "failed to remove a dir %#v: stat error: %v", path, err)
return sftp.ErrSSHFxFailure
}
if err := os.RemoveAll(path); err != nil {
if !fi.IsDir() || fi.Mode()&os.ModeSymlink == os.ModeSymlink {
c.Log(logger.LevelDebug, logSender, "cannot remove %#v is not a directory", path)
return sftp.ErrSSHFxFailure
}
if err = os.Remove(path); err != nil {
c.Log(logger.LevelError, logSender, "failed to remove directory %#v: %v", path, err)
return sftp.ErrSSHFxFailure
}
logger.CommandLog(rmdirLogSender, path, "", c.User.Username, c.ID, c.protocol)
dataprovider.UpdateUserQuota(dataProvider, c.User, -numFiles, -size, false)
for _, p := range fileList {
executeAction(operationDelete, c.User.Username, p, "")
}
return sftp.ErrSSHFxOk
}
@@ -314,11 +316,12 @@ func (c Connection) handleSFTPMkdir(path string) error {
if !c.User.HasPerm(dataprovider.PermCreateDirs) {
return sftp.ErrSSHFxPermissionDenied
}
if err := c.createMissingDirs(filepath.Join(path, "testfile")); err != nil {
c.Log(logger.LevelError, logSender, "error making missing dir for path %#v: %v", path, err)
if err := os.Mkdir(path, 0777); err != nil {
c.Log(logger.LevelError, logSender, "error creating missing dir: %#v error: %v", path, err)
return sftp.ErrSSHFxFailure
}
utils.SetPathPermissions(path, c.User.GetUID(), c.User.GetGID())
logger.CommandLog(mkdirLogSender, path, "", c.User.Username, c.ID, c.protocol)
return nil
}
@@ -335,6 +338,10 @@ func (c Connection) handleSFTPRemove(path string) error {
c.Log(logger.LevelError, logSender, "failed to remove a file %#v: stat error: %v", path, err)
return sftp.ErrSSHFxFailure
}
if fi.IsDir() && fi.Mode()&os.ModeSymlink != os.ModeSymlink {
c.Log(logger.LevelDebug, logSender, "cannot remove %#v is not a file/symlink", path)
return sftp.ErrSSHFxFailure
}
size = fi.Size()
if err := os.Remove(path); err != nil {
c.Log(logger.LevelError, logSender, "failed to remove a file/symlink %#v: %v", path, err)
@@ -356,18 +363,6 @@ func (c Connection) handleSFTPUploadToNewFile(requestPath, filePath string) (io.
return nil, sftp.ErrSSHFxFailure
}
if _, err := os.Stat(filepath.Dir(requestPath)); os.IsNotExist(err) {
if !c.User.HasPerm(dataprovider.PermCreateDirs) {
return nil, sftp.ErrSSHFxPermissionDenied
}
}
err := c.createMissingDirs(requestPath)
if err != nil {
c.Log(logger.LevelError, logSender, "error making missing dir for path %#v: %v", requestPath, err)
return nil, sftp.ErrSSHFxFailure
}
file, err := os.Create(filePath)
if err != nil {
c.Log(logger.LevelError, logSender, "error creating file %#v: %v", requestPath, err)
@@ -569,23 +564,6 @@ func (c Connection) isSubDir(sub string) error {
return nil
}
func (c Connection) createMissingDirs(filePath string) error {
dirsToCreate, err := c.findNonexistentDirs(filePath)
if err != nil {
return err
}
last := len(dirsToCreate) - 1
for i := range dirsToCreate {
d := dirsToCreate[last-i]
if err := os.Mkdir(d, 0777); err != nil {
c.Log(logger.LevelError, logSender, "error creating missing dir: %#v", d)
return err
}
utils.SetPathPermissions(d, c.User.GetUID(), c.User.GetGID())
}
return nil
}
func (c Connection) close() error {
if c.channel != nil {
err := c.channel.Close()