Disallowed files/dirs can be completly hidden. This may cause performance issues for large directories
Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
66 | 66 | return nil, c.GetPermissionDeniedError() |
|
67 | 67 | } |
|
68 | 68 | ||
69 | - | fi, err := c.DoStat(name, mode) |
|
69 | + | fi, err := c.DoStat(name, mode, true) |
|
70 | 70 | if err != nil { |
|
71 | 71 | return nil, err |
|
72 | 72 | } |
89 | 89 | return nil, c.GetPermissionDeniedError() |
|
90 | 90 | } |
|
91 | 91 | ||
92 | - | if !c.User.IsFileAllowed(name) { |
|
92 | + | if ok, policy := c.User.IsFileAllowed(name); !ok { |
|
93 | 93 | c.Log(logger.LevelWarn, "reading file %#v is not allowed", name) |
|
94 | - | return nil, c.GetPermissionDeniedError() |
|
94 | + | return nil, c.GetErrorForDeniedFile(policy) |
|
95 | 95 | } |
|
96 | 96 | ||
97 | 97 | fs, p, err := c.GetFsAndResolvedPath(name) |
120 | 120 | func (c *Connection) getFileWriter(name string) (io.WriteCloser, error) { |
|
121 | 121 | c.UpdateLastActivity() |
|
122 | 122 | ||
123 | - | if !c.User.IsFileAllowed(name) { |
|
123 | + | if ok, _ := c.User.IsFileAllowed(name); !ok { |
|
124 | 124 | c.Log(logger.LevelWarn, "writing file %#v is not allowed", name) |
|
125 | 125 | return nil, c.GetPermissionDeniedError() |
|
126 | 126 | } |
62 | 62 | c.UpdateLastActivity() |
|
63 | 63 | ||
64 | 64 | name = util.CleanPath(name) |
|
65 | - | return c.CreateDir(name) |
|
65 | + | return c.CreateDir(name, true) |
|
66 | 66 | } |
|
67 | 67 | ||
68 | 68 | // Rename renames a file or a directory |
85 | 85 | return nil, c.GetPermissionDeniedError() |
|
86 | 86 | } |
|
87 | 87 | ||
88 | - | fi, err := c.DoStat(name, 0) |
|
88 | + | fi, err := c.DoStat(name, 0, true) |
|
89 | 89 | if err != nil { |
|
90 | 90 | return nil, err |
|
91 | 91 | } |
156 | 156 | } |
|
157 | 157 | ||
158 | 158 | func (c *Connection) putFile(fs vfs.Fs, fsPath, virtualPath string) (webdav.File, error) { |
|
159 | - | if !c.User.IsFileAllowed(virtualPath) { |
|
159 | + | if ok, _ := c.User.IsFileAllowed(virtualPath); !ok { |
|
160 | 160 | c.Log(logger.LevelWarn, "writing file %#v is not allowed", virtualPath) |
|
161 | 161 | return nil, c.GetPermissionDeniedError() |
|
162 | 162 | } |
161 | 161 | return c.sendErrorResponse(err) |
|
162 | 162 | } |
|
163 | 163 | } else if fi.Mode().IsRegular() { |
|
164 | - | if !c.connection.User.IsFileAllowed(sshDestPath) { |
|
164 | + | if ok, _ := c.connection.User.IsFileAllowed(sshDestPath); !ok { |
|
165 | 165 | err := errors.New("unsupported copy destination: this file is not allowed") |
|
166 | 166 | return c.sendErrorResponse(err) |
|
167 | 167 | } |
282 | 282 | response = fmt.Sprintf("%x -\n", h.Sum(nil)) |
|
283 | 283 | } else { |
|
284 | 284 | sshPath := c.getDestPath() |
|
285 | - | if !c.connection.User.IsFileAllowed(sshPath) { |
|
285 | + | if ok, policy := c.connection.User.IsFileAllowed(sshPath); !ok { |
|
286 | 286 | c.connection.Log(logger.LevelInfo, "hash not allowed for file %#v", sshPath) |
|
287 | - | return c.sendErrorResponse(c.connection.GetPermissionDeniedError()) |
|
287 | + | return c.sendErrorResponse(c.connection.GetErrorForDeniedFile(policy)) |
|
288 | 288 | } |
|
289 | 289 | fs, fsPath, err := c.connection.GetFsAndResolvedPath(sshPath) |
|
290 | 290 | if err != nil { |
63 | 63 | return nil, sftp.ErrSSHFxPermissionDenied |
|
64 | 64 | } |
|
65 | 65 | ||
66 | - | if !c.User.IsFileAllowed(request.Filepath) { |
|
66 | + | if ok, policy := c.User.IsFileAllowed(request.Filepath); !ok { |
|
67 | 67 | c.Log(logger.LevelWarn, "reading file %#v is not allowed", request.Filepath) |
|
68 | - | return nil, sftp.ErrSSHFxPermissionDenied |
|
68 | + | return nil, c.GetErrorForDeniedFile(policy) |
|
69 | 69 | } |
|
70 | 70 | ||
71 | 71 | fs, p, err := c.GetFsAndResolvedPath(request.Filepath) |
104 | 104 | func (c *Connection) handleFilewrite(request *sftp.Request) (sftp.WriterAtReaderAt, error) { |
|
105 | 105 | c.UpdateLastActivity() |
|
106 | 106 | ||
107 | - | if !c.User.IsFileAllowed(request.Filepath) { |
|
107 | + | if ok, _ := c.User.IsFileAllowed(request.Filepath); !ok { |
|
108 | 108 | c.Log(logger.LevelWarn, "writing file %#v is not allowed", request.Filepath) |
|
109 | - | return nil, sftp.ErrSSHFxPermissionDenied |
|
109 | + | return nil, c.GetPermissionDeniedError() |
|
110 | 110 | } |
|
111 | 111 | ||
112 | 112 | fs, p, err := c.GetFsAndResolvedPath(request.Filepath) |
175 | 175 | case "Rmdir": |
|
176 | 176 | return c.RemoveDir(request.Filepath) |
|
177 | 177 | case "Mkdir": |
|
178 | - | err := c.CreateDir(request.Filepath) |
|
178 | + | err := c.CreateDir(request.Filepath, true) |
|
179 | 179 | if err != nil { |
|
180 | 180 | return err |
|
181 | 181 | } |
214 | 214 | return nil, sftp.ErrSSHFxPermissionDenied |
|
215 | 215 | } |
|
216 | 216 | ||
217 | - | s, err := c.DoStat(request.Filepath, 0) |
|
217 | + | s, err := c.DoStat(request.Filepath, 0, true) |
|
218 | 218 | if err != nil { |
|
219 | 219 | return nil, err |
|
220 | 220 | } |
255 | 255 | return nil, sftp.ErrSSHFxPermissionDenied |
|
256 | 256 | } |
|
257 | 257 | ||
258 | - | s, err := c.DoStat(request.Filepath, 1) |
|
258 | + | s, err := c.DoStat(request.Filepath, 1, true) |
|
259 | 259 | if err != nil { |
|
260 | 260 | return nil, err |
|
261 | 261 | } |
797 | 797 | return result, nil |
|
798 | 798 | } |
|
799 | 799 | ||
800 | + | func getPatterDenyPolicyFromString(policy string) int { |
|
801 | + | denyPolicy := sdk.DenyPolicyDefault |
|
802 | + | if policy == "1" { |
|
803 | + | denyPolicy = sdk.DenyPolicyHide |
|
804 | + | } |
|
805 | + | return denyPolicy |
|
806 | + | } |
|
807 | + | ||
800 | 808 | func getFilePatternsFromPostField(r *http.Request) []sdk.PatternsFilter { |
|
801 | 809 | var result []sdk.PatternsFilter |
|
802 | 810 | ||
803 | 811 | allowedPatterns := make(map[string][]string) |
|
804 | 812 | deniedPatterns := make(map[string][]string) |
|
813 | + | patternPolicies := make(map[string]string) |
|
805 | 814 | ||
806 | 815 | for k := range r.Form { |
|
807 | 816 | if strings.HasPrefix(k, "pattern_path") { |
810 | 819 | filters := strings.TrimSpace(r.Form.Get(fmt.Sprintf("patterns%v", idx))) |
|
811 | 820 | filters = strings.ReplaceAll(filters, " ", "") |
|
812 | 821 | patternType := r.Form.Get(fmt.Sprintf("pattern_type%v", idx)) |
|
822 | + | patternPolicy := r.Form.Get(fmt.Sprintf("pattern_policy%v", idx)) |
|
813 | 823 | if p != "" && filters != "" { |
|
814 | 824 | if patternType == "allowed" { |
|
815 | 825 | allowedPatterns[p] = append(allowedPatterns[p], strings.Split(filters, ",")...) |
|
816 | 826 | } else { |
|
817 | 827 | deniedPatterns[p] = append(deniedPatterns[p], strings.Split(filters, ",")...) |
|
818 | 828 | } |
|
829 | + | if patternPolicy != "" && patternPolicy != "0" { |
|
830 | + | patternPolicies[p] = patternPolicy |
|
831 | + | } |
|
819 | 832 | } |
|
820 | 833 | } |
|
821 | 834 | } |
823 | 836 | for dirAllowed, allowPatterns := range allowedPatterns { |
|
824 | 837 | filter := sdk.PatternsFilter{ |
|
825 | 838 | Path: dirAllowed, |
|
826 | - | AllowedPatterns: util.RemoveDuplicates(allowPatterns), |
|
839 | + | AllowedPatterns: allowPatterns, |
|
840 | + | DenyPolicy: getPatterDenyPolicyFromString(patternPolicies[dirAllowed]), |
|
827 | 841 | } |
|
828 | 842 | for dirDenied, denPatterns := range deniedPatterns { |
|
829 | 843 | if dirAllowed == dirDenied { |
|
830 | - | filter.DeniedPatterns = util.RemoveDuplicates(denPatterns) |
|
844 | + | filter.DeniedPatterns = denPatterns |
|
831 | 845 | break |
|
832 | 846 | } |
|
833 | 847 | } |
845 | 859 | result = append(result, sdk.PatternsFilter{ |
|
846 | 860 | Path: dirDenied, |
|
847 | 861 | DeniedPatterns: denPatterns, |
|
862 | + | DenyPolicy: getPatterDenyPolicyFromString(patternPolicies[dirDenied]), |
|
848 | 863 | }) |
|
849 | 864 | } |
|
850 | 865 | } |
148 | 148 | return common.ErrPermissionDenied |
|
149 | 149 | } |
|
150 | 150 | ||
151 | - | info, err := c.connection.DoStat(dirPath, 1) |
|
151 | + | info, err := c.connection.DoStat(dirPath, 1, true) |
|
152 | 152 | if err == nil && info.IsDir() { |
|
153 | 153 | return nil |
|
154 | 154 | } |
276 | 276 | return err |
|
277 | 277 | } |
|
278 | 278 | ||
279 | - | if !c.connection.User.IsFileAllowed(uploadFilePath) { |
|
279 | + | if ok, _ := c.connection.User.IsFileAllowed(uploadFilePath); !ok { |
|
280 | 280 | c.connection.Log(logger.LevelWarn, "writing file %#v is not allowed", uploadFilePath) |
|
281 | - | c.sendErrorMessage(fs, common.ErrPermissionDenied) |
|
281 | + | c.sendErrorMessage(fs, c.connection.GetPermissionDeniedError()) |
|
282 | 282 | return common.ErrPermissionDenied |
|
283 | 283 | } |
|
284 | 284 |
372 | 372 | c.sendErrorMessage(fs, err) |
|
373 | 373 | return err |
|
374 | 374 | } |
|
375 | - | files = c.connection.User.AddVirtualDirs(files, fs.GetRelativePath(dirPath)) |
|
375 | + | files = c.connection.User.FilterListDir(files, fs.GetRelativePath(dirPath)) |
|
376 | 376 | var dirs []string |
|
377 | 377 | for _, file := range files { |
|
378 | 378 | filePath := fs.GetRelativePath(fs.Join(dirPath, file.Name())) |
509 | 509 | return common.ErrPermissionDenied |
|
510 | 510 | } |
|
511 | 511 | ||
512 | - | if !c.connection.User.IsFileAllowed(filePath) { |
|
512 | + | if ok, policy := c.connection.User.IsFileAllowed(filePath); !ok { |
|
513 | 513 | c.connection.Log(logger.LevelWarn, "reading file %#v is not allowed", filePath) |
|
514 | - | c.sendErrorMessage(fs, common.ErrPermissionDenied) |
|
514 | + | c.sendErrorMessage(fs, c.connection.GetErrorForDeniedFile(policy)) |
|
515 | 515 | return common.ErrPermissionDenied |
|
516 | 516 | } |
|
517 | 517 |
143 | 143 | return 0, f.Connection.GetPermissionDeniedError() |
|
144 | 144 | } |
|
145 | 145 | ||
146 | - | if !f.Connection.User.IsFileAllowed(f.GetVirtualPath()) { |
|
146 | + | if ok, policy := f.Connection.User.IsFileAllowed(f.GetVirtualPath()); !ok { |
|
147 | 147 | f.Connection.Log(logger.LevelWarn, "reading file %#v is not allowed", f.GetVirtualPath()) |
|
148 | - | return 0, f.Connection.GetPermissionDeniedError() |
|
148 | + | return 0, f.Connection.GetErrorForDeniedFile(policy) |
|
149 | 149 | } |
|
150 | 150 | err := common.ExecutePreAction(f.Connection, common.OperationPreDownload, f.GetFsPath(), f.GetVirtualPath(), 0, 0) |
|
151 | 151 | if err != nil { |
242 | 242 | c.Log(logger.LevelDebug, "error listing directory: %+v", err) |
|
243 | 243 | return nil, c.GetFsError(fs, err) |
|
244 | 244 | } |
|
245 | - | return c.User.AddVirtualDirs(files, virtualPath), nil |
|
245 | + | return c.User.FilterListDir(files, virtualPath), nil |
|
246 | 246 | } |
|
247 | 247 | ||
248 | 248 | // CheckParentDirs tries to create the specified directory and any missing parent dirs |
254 | 254 | if fs.HasVirtualFolders() { |
|
255 | 255 | return nil |
|
256 | 256 | } |
|
257 | - | if _, err := c.DoStat(virtualPath, 0); !c.IsNotExistError(err) { |
|
257 | + | if _, err := c.DoStat(virtualPath, 0, false); !c.IsNotExistError(err) { |
|
258 | 258 | return err |
|
259 | 259 | } |
|
260 | 260 | dirs := util.GetDirsForVirtualPath(virtualPath) |
275 | 275 | } |
|
276 | 276 | ||
277 | 277 | // CreateDir creates a new directory at the specified fsPath |
|
278 | - | func (c *BaseConnection) CreateDir(virtualPath string) error { |
|
278 | + | func (c *BaseConnection) CreateDir(virtualPath string, checkFilePatterns bool) error { |
|
279 | 279 | if !c.User.HasPerm(dataprovider.PermCreateDirs, path.Dir(virtualPath)) { |
|
280 | 280 | return c.GetPermissionDeniedError() |
|
281 | 281 | } |
|
282 | + | if checkFilePatterns { |
|
283 | + | if ok, _ := c.User.IsFileAllowed(virtualPath); !ok { |
|
284 | + | return c.GetPermissionDeniedError() |
|
285 | + | } |
|
286 | + | } |
|
282 | 287 | if c.User.IsVirtualFolder(virtualPath) { |
|
283 | 288 | c.Log(logger.LevelWarn, "mkdir not allowed %#v is a virtual folder", virtualPath) |
|
284 | 289 | return c.GetPermissionDeniedError() |
304 | 309 | if !c.User.HasAnyPerm([]string{dataprovider.PermDeleteFiles, dataprovider.PermDelete}, path.Dir(virtualPath)) { |
|
305 | 310 | return c.GetPermissionDeniedError() |
|
306 | 311 | } |
|
307 | - | if !c.User.IsFileAllowed(virtualPath) { |
|
312 | + | if ok, policy := c.User.IsFileAllowed(virtualPath); !ok { |
|
308 | 313 | c.Log(logger.LevelDebug, "removing file %#v is not allowed", virtualPath) |
|
309 | - | return c.GetPermissionDeniedError() |
|
314 | + | return c.GetErrorForDeniedFile(policy) |
|
310 | 315 | } |
|
311 | 316 | return nil |
|
312 | 317 | } |
368 | 373 | if !c.User.HasAnyPerm([]string{dataprovider.PermDeleteDirs, dataprovider.PermDelete}, path.Dir(virtualPath)) { |
|
369 | 374 | return c.GetPermissionDeniedError() |
|
370 | 375 | } |
|
376 | + | if ok, policy := c.User.IsFileAllowed(virtualPath); !ok { |
|
377 | + | c.Log(logger.LevelDebug, "removing directory %#v is not allowed", virtualPath) |
|
378 | + | return c.GetErrorForDeniedFile(policy) |
|
379 | + | } |
|
371 | 380 | return nil |
|
372 | 381 | } |
|
373 | 382 |
488 | 497 | return c.GetFsError(fs, err) |
|
489 | 498 | } |
|
490 | 499 | if fs.GetRelativePath(fsSourcePath) == "/" { |
|
491 | - | c.Log(logger.LevelWarn, "symlinking root dir is not allowed") |
|
500 | + | c.Log(logger.LevelError, "symlinking root dir is not allowed") |
|
492 | 501 | return c.GetPermissionDeniedError() |
|
493 | 502 | } |
|
494 | 503 | if fs.GetRelativePath(fsTargetPath) == "/" { |
|
495 | - | c.Log(logger.LevelWarn, "symlinking to root dir is not allowed") |
|
504 | + | c.Log(logger.LevelError, "symlinking to root dir is not allowed") |
|
496 | 505 | return c.GetPermissionDeniedError() |
|
497 | 506 | } |
|
498 | 507 | if !c.User.HasPerm(dataprovider.PermCreateSymlinks, path.Dir(virtualTargetPath)) { |
|
499 | 508 | return c.GetPermissionDeniedError() |
|
500 | 509 | } |
|
510 | + | ok, policy := c.User.IsFileAllowed(virtualSourcePath) |
|
511 | + | if !ok && policy == sdk.DenyPolicyHide { |
|
512 | + | c.Log(logger.LevelError, "symlink source path %#v is not allowed", virtualSourcePath) |
|
513 | + | return c.GetNotExistError() |
|
514 | + | } |
|
515 | + | if ok, _ = c.User.IsFileAllowed(virtualTargetPath); !ok { |
|
516 | + | c.Log(logger.LevelError, "symlink target path %#v is not allowed", virtualTargetPath) |
|
517 | + | return c.GetPermissionDeniedError() |
|
518 | + | } |
|
501 | 519 | if err := fs.Symlink(fsSourcePath, fsTargetPath); err != nil { |
|
502 | 520 | c.Log(logger.LevelError, "failed to create symlink %#v -> %#v: %+v", fsSourcePath, fsTargetPath, err) |
|
503 | 521 | return c.GetFsError(fs, err) |
518 | 536 | } |
|
519 | 537 | ||
520 | 538 | // DoStat execute a Stat if mode = 0, Lstat if mode = 1 |
|
521 | - | func (c *BaseConnection) DoStat(virtualPath string, mode int) (os.FileInfo, error) { |
|
539 | + | func (c *BaseConnection) DoStat(virtualPath string, mode int, checkFilePatterns bool) (os.FileInfo, error) { |
|
522 | 540 | // for some vfs we don't create intermediary folders so we cannot simply check |
|
523 | 541 | // if virtualPath is a virtual folder |
|
524 | 542 | vfolders := c.User.GetVirtualFoldersInPath(path.Dir(virtualPath)) |
|
525 | 543 | if _, ok := vfolders[virtualPath]; ok { |
|
526 | 544 | return vfs.NewFileInfo(virtualPath, true, 0, time.Now(), false), nil |
|
527 | 545 | } |
|
546 | + | if checkFilePatterns { |
|
547 | + | ok, policy := c.User.IsFileAllowed(virtualPath) |
|
548 | + | if !ok && policy == sdk.DenyPolicyHide { |
|
549 | + | return nil, c.GetNotExistError() |
|
550 | + | } |
|
551 | + | } |
|
528 | 552 | ||
529 | 553 | var info os.FileInfo |
|
530 | 554 |
549 | 573 | } |
|
550 | 574 | ||
551 | 575 | func (c *BaseConnection) createDirIfMissing(name string) error { |
|
552 | - | _, err := c.DoStat(name, 0) |
|
576 | + | _, err := c.DoStat(name, 0, false) |
|
553 | 577 | if c.IsNotExistError(err) { |
|
554 | - | return c.CreateDir(name) |
|
578 | + | return c.CreateDir(name, false) |
|
555 | 579 | } |
|
556 | 580 | return err |
|
557 | 581 | } |
625 | 649 | ||
626 | 650 | // SetStat set StatAttributes for the specified fsPath |
|
627 | 651 | func (c *BaseConnection) SetStat(virtualPath string, attributes *StatAttributes) error { |
|
652 | + | if ok, policy := c.User.IsFileAllowed(virtualPath); !ok { |
|
653 | + | return c.GetErrorForDeniedFile(policy) |
|
654 | + | } |
|
628 | 655 | fs, fsPath, err := c.GetFsAndResolvedPath(virtualPath) |
|
629 | 656 | if err != nil { |
|
630 | 657 | return err |
799 | 826 | c.Log(logger.LevelWarn, "renaming a virtual folder is not allowed") |
|
800 | 827 | return false |
|
801 | 828 | } |
|
802 | - | if !c.User.IsFileAllowed(virtualSourcePath) || !c.User.IsFileAllowed(virtualTargetPath) { |
|
803 | - | if fi != nil && fi.Mode().IsRegular() { |
|
804 | - | c.Log(logger.LevelDebug, "renaming file is not allowed, source: %#v target: %#v", |
|
805 | - | virtualSourcePath, virtualTargetPath) |
|
806 | - | return false |
|
807 | - | } |
|
829 | + | isSrcAllowed, _ := c.User.IsFileAllowed(virtualSourcePath) |
|
830 | + | isDstAllowed, _ := c.User.IsFileAllowed(virtualTargetPath) |
|
831 | + | if !isSrcAllowed || !isDstAllowed { |
|
832 | + | c.Log(logger.LevelDebug, "renaming source: %#v to target: %#v not allowed", virtualSourcePath, |
|
833 | + | virtualTargetPath) |
|
834 | + | return false |
|
808 | 835 | } |
|
809 | 836 | return c.hasRenamePerms(virtualSourcePath, virtualTargetPath, fi) |
|
810 | 837 | } |
1141 | 1168 | } |
|
1142 | 1169 | } |
|
1143 | 1170 | ||
1171 | + | // GetErrorForDeniedFile return permission denied or not exist error based on the specified policy |
|
1172 | + | func (c *BaseConnection) GetErrorForDeniedFile(policy int) error { |
|
1173 | + | switch policy { |
|
1174 | + | case sdk.DenyPolicyHide: |
|
1175 | + | return c.GetNotExistError() |
|
1176 | + | default: |
|
1177 | + | return c.GetPermissionDeniedError() |
|
1178 | + | } |
|
1179 | + | } |
|
1180 | + | ||
1144 | 1181 | // GetPermissionDeniedError returns an appropriate permission denied error for the connection protocol |
|
1145 | 1182 | func (c *BaseConnection) GetPermissionDeniedError() error { |
|
1146 | 1183 | switch c.protocol { |
83 | 83 | func (c *Connection) Mkdir(name string, perm os.FileMode) error { |
|
84 | 84 | c.UpdateLastActivity() |
|
85 | 85 | ||
86 | - | return c.CreateDir(name) |
|
86 | + | return c.CreateDir(name, true) |
|
87 | 87 | } |
|
88 | 88 | ||
89 | 89 | // MkdirAll is not implemented, we don't need it |
145 | 145 | return nil, c.GetPermissionDeniedError() |
|
146 | 146 | } |
|
147 | 147 | ||
148 | - | fi, err := c.DoStat(name, 0) |
|
148 | + | fi, err := c.DoStat(name, 0, true) |
|
149 | 149 | if err != nil { |
|
150 | 150 | return nil, err |
|
151 | 151 | } |
319 | 319 | return nil, c.GetPermissionDeniedError() |
|
320 | 320 | } |
|
321 | 321 | ||
322 | - | if !c.User.IsFileAllowed(ftpPath) { |
|
322 | + | if ok, policy := c.User.IsFileAllowed(ftpPath); !ok { |
|
323 | 323 | c.Log(logger.LevelWarn, "reading file %#v is not allowed", ftpPath) |
|
324 | - | return nil, c.GetPermissionDeniedError() |
|
324 | + | return nil, c.GetErrorForDeniedFile(policy) |
|
325 | 325 | } |
|
326 | 326 | ||
327 | 327 | if err := common.ExecutePreAction(c.BaseConnection, common.OperationPreDownload, fsPath, ftpPath, 0, 0); err != nil { |
344 | 344 | } |
|
345 | 345 | ||
346 | 346 | func (c *Connection) uploadFile(fs vfs.Fs, fsPath, ftpPath string, flags int) (ftpserver.FileTransfer, error) { |
|
347 | - | if !c.User.IsFileAllowed(ftpPath) { |
|
347 | + | if ok, _ := c.User.IsFileAllowed(ftpPath); !ok { |
|
348 | 348 | c.Log(logger.LevelWarn, "writing file %#v is not allowed", ftpPath) |
|
349 | 349 | return nil, ftpserver.ErrFileNameNotAllowed |
|
350 | 350 | } |