diff --git a/assets b/assets index 3ffab03..e2d4f13 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 3ffab03068e88d627825710798b5cc0d5ce8df20 +Subproject commit e2d4f13a54dfd424cfbc129664772e104ccf97fc diff --git a/pkg/filesystem/driver/local/handler.go b/pkg/filesystem/driver/local/handler.go index 96e7b90..a2cdcc2 100644 --- a/pkg/filesystem/driver/local/handler.go +++ b/pkg/filesystem/driver/local/handler.go @@ -100,6 +100,14 @@ func (handler Driver) Put(ctx context.Context, file io.ReadCloser, dst string, s defer file.Close() dst = util.RelativePath(filepath.FromSlash(dst)) + // 如果禁止了 Overwrite,则检查是否有重名冲突 + if ctx.Value(fsctx.DisableOverwrite) != nil { + if util.Exists(dst) { + util.Log().Warning("物理同名文件已存在或不可用: %s", dst) + return errors.New("物理同名文件已存在或不可用") + } + } + // 如果目标目录不存在,创建 basePath := filepath.Dir(dst) if !util.Exists(basePath) { diff --git a/pkg/filesystem/driver/local/handler_test.go b/pkg/filesystem/driver/local/handler_test.go index 2256db7..ed0bc2b 100644 --- a/pkg/filesystem/driver/local/handler_test.go +++ b/pkg/filesystem/driver/local/handler_test.go @@ -21,7 +21,8 @@ import ( func TestHandler_Put(t *testing.T) { asserts := assert.New(t) handler := Driver{} - ctx := context.Background() + ctx := context.WithValue(context.Background(), fsctx.DisableOverwrite, true) + os.Remove(util.RelativePath("test/test/txt")) testCases := []struct { file io.ReadCloser @@ -33,6 +34,11 @@ func TestHandler_Put(t *testing.T) { dst: "test/test/txt", err: false, }, + { + file: ioutil.NopCloser(strings.NewReader("test input file")), + dst: "test/test/txt", + err: true, + }, { file: ioutil.NopCloser(strings.NewReader("test input file")), dst: "/notexist:/S.TXT", diff --git a/pkg/filesystem/driver/oss/handler.go b/pkg/filesystem/driver/oss/handler.go index fc4f9a3..45945ba 100644 --- a/pkg/filesystem/driver/oss/handler.go +++ b/pkg/filesystem/driver/oss/handler.go @@ -235,8 +235,15 @@ func (handler Driver) Put(ctx context.Context, file io.ReadCloser, dst string, s // 凭证有效期 credentialTTL := model.GetIntSetting("upload_credential_timeout", 3600) + // 是否允许覆盖 + overwrite := true + if ctx.Value(fsctx.DisableOverwrite) != nil { + overwrite = false + } + options := []oss.Option{ oss.Expires(time.Now().Add(time.Duration(credentialTTL) * time.Second)), + oss.ForbidOverWrite(!overwrite), } // 上传文件 diff --git a/pkg/filesystem/driver/oss/handler_test.go b/pkg/filesystem/driver/oss/handler_test.go index 3758bba..5be01f2 100644 --- a/pkg/filesystem/driver/oss/handler_test.go +++ b/pkg/filesystem/driver/oss/handler_test.go @@ -265,10 +265,11 @@ func TestDriver_Put(t *testing.T) { }, } cache.Set("setting_upload_credential_timeout", "3600", 0) + ctx := context.WithValue(context.Background(), fsctx.DisableOverwrite, true) // 失败 { - err := handler.Put(context.Background(), ioutil.NopCloser(strings.NewReader("123")), "/123.txt", 3) + err := handler.Put(ctx, ioutil.NopCloser(strings.NewReader("123")), "/123.txt", 3) asserts.Error(err) } } diff --git a/pkg/filesystem/driver/remote/handler.go b/pkg/filesystem/driver/remote/handler.go index c80e24d..5b9b965 100644 --- a/pkg/filesystem/driver/remote/handler.go +++ b/pkg/filesystem/driver/remote/handler.go @@ -155,6 +155,13 @@ func (handler Driver) Put(ctx context.Context, file io.ReadCloser, dst string, s if err != nil { return err } + + // 决定是否要禁用文件覆盖 + overwrite := "true" + if ctx.Value(fsctx.DisableOverwrite) != nil { + overwrite = "false" + } + // 上传文件 resp, err := handler.Client.Request( "POST", @@ -164,6 +171,7 @@ func (handler Driver) Put(ctx context.Context, file io.ReadCloser, dst string, s "Authorization": {credential.Token}, "X-Policy": {credential.Policy}, "X-FileName": {fileName}, + "X-Overwrite": {overwrite}, }), request.WithContentLength(int64(size)), request.WithTimeout(time.Duration(0)), @@ -321,7 +329,8 @@ func (handler Driver) getUploadCredential(ctx context.Context, policy serializer // 签名上传策略 uploadRequest, _ := http.NewRequest("POST", "/api/v3/slave/upload", nil) uploadRequest.Header = map[string][]string{ - "X-Policy": {policyEncoded}, + "X-Policy": {policyEncoded}, + "X-Overwrite": {"false"}, } auth.SignRequest(handler.AuthInstance, uploadRequest, TTL) diff --git a/pkg/filesystem/driver/remote/handler_test.go b/pkg/filesystem/driver/remote/handler_test.go index 7ec91dc..a1dc698 100644 --- a/pkg/filesystem/driver/remote/handler_test.go +++ b/pkg/filesystem/driver/remote/handler_test.go @@ -34,7 +34,7 @@ func TestHandler_Token(t *testing.T) { }, AuthInstance: auth.HMACAuth{}, } - ctx := context.Background() + ctx := context.WithValue(context.Background(), fsctx.DisableOverwrite, true) auth.General = auth.HMACAuth{SecretKey: []byte("test")} // 成功 @@ -49,6 +49,7 @@ func TestHandler_Token(t *testing.T) { asserts.Equal(true, policy.AutoRename) asserts.Equal("dir", policy.SavePath) asserts.Equal("file", policy.FileName) + asserts.Equal("file", policy.FileName) asserts.Equal([]string{"txt"}, policy.AllowedExtension) } diff --git a/pkg/filesystem/fsctx/context.go b/pkg/filesystem/fsctx/context.go index 0056043..28a2653 100644 --- a/pkg/filesystem/fsctx/context.go +++ b/pkg/filesystem/fsctx/context.go @@ -29,8 +29,8 @@ const ( ShareKeyCtx // LimitParentCtx 限制父目录 LimitParentCtx - // IgnoreConflictCtx 忽略重名冲突 - IgnoreConflictCtx + // IgnoreDirectoryConflictCtx 忽略目录重名冲突 + IgnoreDirectoryConflictCtx // RetryCtx 失败重试次数 RetryCtx // ForceUsePublicEndpointCtx 强制使用公网 Endpoint @@ -39,4 +39,6 @@ const ( CancelFuncCtx // ValidateCapacityOnceCtx 限定归还容量的操作只執行一次 ValidateCapacityOnceCtx + // 禁止上传时同名覆盖操作 + DisableOverwrite ) diff --git a/pkg/filesystem/manage.go b/pkg/filesystem/manage.go index ba99571..91100a5 100644 --- a/pkg/filesystem/manage.go +++ b/pkg/filesystem/manage.go @@ -403,8 +403,8 @@ func (fs *FileSystem) CreateDirectory(ctx context.Context, fullPath string) (*mo isExist, parent := fs.IsPathExist(base) if !isExist { // 递归创建父目录 - if _, ok := ctx.Value(fsctx.IgnoreConflictCtx).(bool); !ok { - ctx = context.WithValue(ctx, fsctx.IgnoreConflictCtx, true) + if _, ok := ctx.Value(fsctx.IgnoreDirectoryConflictCtx).(bool); !ok { + ctx = context.WithValue(ctx, fsctx.IgnoreDirectoryConflictCtx, true) } newParent, err := fs.CreateDirectory(ctx, base) if err != nil { @@ -427,7 +427,7 @@ func (fs *FileSystem) CreateDirectory(ctx context.Context, fullPath string) (*mo _, err := newFolder.Create() if err != nil { - if _, ok := ctx.Value(fsctx.IgnoreConflictCtx).(bool); !ok { + if _, ok := ctx.Value(fsctx.IgnoreDirectoryConflictCtx).(bool); !ok { return nil, ErrFolderExisted } diff --git a/pkg/task/decompress.go b/pkg/task/decompress.go index 5a75871..2a06d6a 100644 --- a/pkg/task/decompress.go +++ b/pkg/task/decompress.go @@ -6,6 +6,7 @@ import ( model "github.com/cloudreve/Cloudreve/v3/models" "github.com/cloudreve/Cloudreve/v3/pkg/filesystem" + "github.com/cloudreve/Cloudreve/v3/pkg/filesystem/fsctx" ) // DecompressTask 文件压缩任务 @@ -81,7 +82,12 @@ func (job *DecompressTask) Do() { } job.TaskModel.SetProgress(DecompressingProgress) - err = fs.Decompress(context.Background(), job.TaskProps.Src, job.TaskProps.Dst) + + // 禁止重名覆盖 + ctx := context.Background() + ctx = context.WithValue(ctx, fsctx.DisableOverwrite, true) + + err = fs.Decompress(ctx, job.TaskProps.Src, job.TaskProps.Dst) if err != nil { job.SetErrorMsg("解压缩失败", err) return diff --git a/pkg/task/import.go b/pkg/task/import.go index 45941b1..7a94d67 100644 --- a/pkg/task/import.go +++ b/pkg/task/import.go @@ -102,7 +102,7 @@ func (job *ImportTask) Do() { // 列取目录、对象 job.TaskModel.SetProgress(ListingProgress) - coxIgnoreConflict := context.WithValue(context.Background(), fsctx.IgnoreConflictCtx, + coxIgnoreConflict := context.WithValue(context.Background(), fsctx.IgnoreDirectoryConflictCtx, true) objects, err := fs.Handler.List(ctx, job.TaskProps.Src, job.TaskProps.Recursive) if err != nil { diff --git a/pkg/task/tranfer.go b/pkg/task/tranfer.go index 79e84ac..8cdc247 100644 --- a/pkg/task/tranfer.go +++ b/pkg/task/tranfer.go @@ -10,6 +10,7 @@ import ( model "github.com/cloudreve/Cloudreve/v3/models" "github.com/cloudreve/Cloudreve/v3/pkg/filesystem" + "github.com/cloudreve/Cloudreve/v3/pkg/filesystem/fsctx" "github.com/cloudreve/Cloudreve/v3/pkg/util" ) @@ -102,7 +103,8 @@ func (job *TransferTask) Do() { dst = path.Join(job.TaskProps.Dst, strings.TrimPrefix(src, trim)) } - err = fs.UploadFromPath(context.Background(), file, dst) + ctx := context.WithValue(context.Background(), fsctx.DisableOverwrite, true) + err = fs.UploadFromPath(ctx, file, dst) if err != nil { job.SetErrorMsg("文件转存失败", err) } diff --git a/pkg/webdav/webdav.go b/pkg/webdav/webdav.go index dbfbdd3..e0879f0 100644 --- a/pkg/webdav/webdav.go +++ b/pkg/webdav/webdav.go @@ -373,6 +373,9 @@ func (h *Handler) handlePut(w http.ResponseWriter, r *http.Request, fs *filesyst fs.Use("AfterUploadFailed", filesystem.HookGiveBackCapacity) } + // 禁止覆盖 + ctx = context.WithValue(ctx, fsctx.DisableOverwrite, true) + // 执行上传 err = fs.Upload(ctx, fileData) if err != nil { @@ -407,8 +410,8 @@ func (h *Handler) handleMkcol(w http.ResponseWriter, r *http.Request, fs *filesy return http.StatusUnsupportedMediaType, nil } if strings.Contains(r.UserAgent(), "rclone") { - if _, ok := ctx.Value(fsctx.IgnoreConflictCtx).(bool); !ok { - ctx = context.WithValue(ctx, fsctx.IgnoreConflictCtx, true) + if _, ok := ctx.Value(fsctx.IgnoreDirectoryConflictCtx).(bool); !ok { + ctx = context.WithValue(ctx, fsctx.IgnoreDirectoryConflictCtx, true) } } if _, err := fs.CreateDirectory(ctx, reqPath); err != nil { diff --git a/routers/controllers/file.go b/routers/controllers/file.go index c914ed5..ff10547 100644 --- a/routers/controllers/file.go +++ b/routers/controllers/file.go @@ -319,6 +319,7 @@ func FileUploadStream(c *gin.Context) { // 执行上传 ctx = context.WithValue(ctx, fsctx.ValidateCapacityOnceCtx, &sync.Once{}) + ctx = context.WithValue(ctx, fsctx.DisableOverwrite, true) uploadCtx := context.WithValue(ctx, fsctx.GinCtx, c) err = fs.Upload(uploadCtx, fileData) if err != nil { diff --git a/routers/controllers/slave.go b/routers/controllers/slave.go index 4f7a98a..e10e2b0 100644 --- a/routers/controllers/slave.go +++ b/routers/controllers/slave.go @@ -71,6 +71,11 @@ func SlaveUpload(c *gin.Context) { fs.Use("AfterUpload", filesystem.SlaveAfterUpload) fs.Use("AfterValidateFailed", filesystem.HookDeleteTempFile) + // 是否允许覆盖 + if c.Request.Header.Get("X-Overwrite") == "false" { + ctx = context.WithValue(ctx, fsctx.DisableOverwrite, true) + } + // 执行上传 err = fs.Upload(ctx, fileData) if err != nil { diff --git a/service/explorer/file.go b/service/explorer/file.go index 1015b98..33146ac 100644 --- a/service/explorer/file.go +++ b/service/explorer/file.go @@ -78,6 +78,7 @@ func (service *SingleFileService) Create(c *gin.Context) serializer.Response { // 上下文 ctx, cancel := context.WithCancel(context.Background()) defer cancel() + ctx = context.WithValue(ctx, fsctx.DisableOverwrite, true) // 给文件系统分配钩子 fs.Use("BeforeUpload", filesystem.HookValidateFile)