From 4958dacc2076fc935146c081b1c79849546b49b7 Mon Sep 17 00:00:00 2001 From: jyxjjj <773933146@qq.com> Date: Mon, 15 Jun 2026 13:48:51 +0800 Subject: [PATCH 1/4] fix(chunk): handle missing chunk part zero - Return ObjectNotFound when chunk directory lacks part 0 - Ignore invalid negative part indexes while scanning chunks Co-authored-by: Codex <267193182+codex@users.noreply.github.com> Signed-off-by: jyxjjj <16695261+jyxjjj@users.noreply.github.com> --- drivers/chunk/driver.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/chunk/driver.go b/drivers/chunk/driver.go index b544391dd..ab520fd48 100644 --- a/drivers/chunk/driver.go +++ b/drivers/chunk/driver.go @@ -80,7 +80,7 @@ func (d *Chunk) Get(ctx context.Context, path string) (model.Obj, error) { if err != nil { return nil, err } - var totalSize int64 = 0 + var totalSize int64 // 0号块默认为-1 以支持空文件 chunkSizes := []int64{-1} h := make(map[*utils.HashType]string) @@ -100,7 +100,7 @@ func (d *Chunk) Get(ctx context.Context, path string) (model.Obj, error) { continue } idx, err := strconv.Atoi(strings.TrimSuffix(o.GetName(), d.CustomExt)) - if err != nil { + if err != nil || idx < 0 { continue } totalSize += o.GetSize() @@ -118,6 +118,9 @@ func (d *Chunk) Get(ctx context.Context, path string) (model.Obj, error) { chunkSizes[idx] = o.GetSize() } } + if first == nil || chunkSizes[0] == -1 { + return nil, errs.NewErr(errs.ObjectNotFound, "chunk part[0] is missing") + } reqDir, _ := stdpath.Split(path) objRes := chunkObject{ Object: model.Object{ From d3470d96801d026f1f81d02538b174ebd7f98903 Mon Sep 17 00:00:00 2001 From: j2rong4cn Date: Mon, 15 Jun 2026 18:00:56 +0800 Subject: [PATCH 2/4] fix(chunk): handle missing all file chunk --- drivers/chunk/driver.go | 44 +++++++++++++++++++++++++---------------- 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/drivers/chunk/driver.go b/drivers/chunk/driver.go index ab520fd48..bae9c0d50 100644 --- a/drivers/chunk/driver.go +++ b/drivers/chunk/driver.go @@ -84,7 +84,7 @@ func (d *Chunk) Get(ctx context.Context, path string) (model.Obj, error) { // 0号块默认为-1 以支持空文件 chunkSizes := []int64{-1} h := make(map[*utils.HashType]string) - var first model.Obj + var last model.Obj for _, o := range chunkObjs { if o.IsDir() { continue @@ -100,14 +100,12 @@ func (d *Chunk) Get(ctx context.Context, path string) (model.Obj, error) { continue } idx, err := strconv.Atoi(strings.TrimSuffix(o.GetName(), d.CustomExt)) - if err != nil || idx < 0 { + if err != nil { continue } + last = o totalSize += o.GetSize() if len(chunkSizes) > idx { - if idx == 0 { - first = o - } chunkSizes[idx] = o.GetSize() } else if len(chunkSizes) == idx { chunkSizes = append(chunkSizes, o.GetSize()) @@ -118,17 +116,22 @@ func (d *Chunk) Get(ctx context.Context, path string) (model.Obj, error) { chunkSizes[idx] = o.GetSize() } } - if first == nil || chunkSizes[0] == -1 { - return nil, errs.NewErr(errs.ObjectNotFound, "chunk part[0] is missing") - } reqDir, _ := stdpath.Split(path) + if last == nil { + return &model.Object{ + Path: stdpath.Join(reqDir, chunkName), + Name: chunkName, + IsFolder: true, + Modified: d.Modified, + }, nil + } objRes := chunkObject{ Object: model.Object{ Path: stdpath.Join(reqDir, chunkName), Name: name, Size: totalSize, - Modified: first.ModTime(), - Ctime: first.CreateTime(), + Modified: last.ModTime(), + Ctime: last.CreateTime(), }, chunkSizes: chunkSizes, } @@ -172,7 +175,7 @@ func (d *Chunk) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([ } totalSize := int64(0) h := make(map[*utils.HashType]string) - first := obj + var last model.Obj for _, o := range chunkObjs { if o.IsDir() { continue @@ -187,20 +190,27 @@ func (d *Chunk) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([ continue } } - idx, err := strconv.Atoi(strings.TrimSuffix(o.GetName(), d.CustomExt)) + _, err := strconv.Atoi(strings.TrimSuffix(o.GetName(), d.CustomExt)) if err != nil { continue } - if idx == 0 { - first = o - } + last = o totalSize += o.GetSize() } + if last == nil { + result[resultIdx] = &model.Object{ + Name: rawName, + Size: obj.GetSize(), + Modified: obj.ModTime(), + IsFolder: true, + } + return nil + } objRes := model.Object{ Name: name, Size: totalSize, - Modified: first.ModTime(), - Ctime: first.CreateTime(), + Modified: last.ModTime(), + Ctime: last.CreateTime(), } if len(h) > 0 { objRes.HashInfo = utils.NewHashInfoByMap(h) From 438e956195d042f83b7196122ea8c34977ded3ad Mon Sep 17 00:00:00 2001 From: j2rong4cn Date: Mon, 15 Jun 2026 18:09:52 +0800 Subject: [PATCH 3/4] . --- drivers/chunk/driver.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/chunk/driver.go b/drivers/chunk/driver.go index bae9c0d50..0cc4e6552 100644 --- a/drivers/chunk/driver.go +++ b/drivers/chunk/driver.go @@ -53,8 +53,8 @@ func (d *Chunk) Drop(ctx context.Context) error { return nil } -func (Addition) GetRootPath() string { - return "/" +func (*Chunk) GetRootPath() string { + return "" } func (d *Chunk) Get(ctx context.Context, path string) (model.Obj, error) { From 628a5425e56cc27353ce7ab6fbbd729b77380fd9 Mon Sep 17 00:00:00 2001 From: j2rong4cn Date: Thu, 18 Jun 2026 17:00:51 +0800 Subject: [PATCH 4/4] fix(chunk): handle incomplete file chunks --- drivers/chunk/driver.go | 70 +++++++++++++++++++++++++++++++---------- 1 file changed, 54 insertions(+), 16 deletions(-) diff --git a/drivers/chunk/driver.go b/drivers/chunk/driver.go index 0cc4e6552..b0c0cd000 100644 --- a/drivers/chunk/driver.go +++ b/drivers/chunk/driver.go @@ -84,7 +84,7 @@ func (d *Chunk) Get(ctx context.Context, path string) (model.Obj, error) { // 0号块默认为-1 以支持空文件 chunkSizes := []int64{-1} h := make(map[*utils.HashType]string) - var last model.Obj + var first model.Obj for _, o := range chunkObjs { if o.IsDir() { continue @@ -103,9 +103,11 @@ func (d *Chunk) Get(ctx context.Context, path string) (model.Obj, error) { if err != nil { continue } - last = o totalSize += o.GetSize() if len(chunkSizes) > idx { + if idx == 0 { + first = o + } chunkSizes[idx] = o.GetSize() } else if len(chunkSizes) == idx { chunkSizes = append(chunkSizes, o.GetSize()) @@ -117,7 +119,8 @@ func (d *Chunk) Get(ctx context.Context, path string) (model.Obj, error) { } } reqDir, _ := stdpath.Split(path) - if last == nil { + // 文件块不完整时,返回文件夹对象 + if chunkSizes[0] == -1 { return &model.Object{ Path: stdpath.Join(reqDir, chunkName), Name: chunkName, @@ -125,13 +128,23 @@ func (d *Chunk) Get(ctx context.Context, path string) (model.Obj, error) { Modified: d.Modified, }, nil } + for i, l := 1, len(chunkSizes)-1; i < l; i++ { + if chunkSizes[i] == 0 { + return &model.Object{ + Path: stdpath.Join(reqDir, chunkName), + Name: chunkName, + IsFolder: true, + Modified: d.Modified, + }, nil + } + } objRes := chunkObject{ Object: model.Object{ Path: stdpath.Join(reqDir, chunkName), Name: name, Size: totalSize, - Modified: last.ModTime(), - Ctime: last.CreateTime(), + Modified: first.ModTime(), + Ctime: first.CreateTime(), }, chunkSizes: chunkSizes, } @@ -167,37 +180,51 @@ func (d *Chunk) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([ result = append(result, nil) listG.Go(func(ctx context.Context) error { chunkObjs, err := op.List(ctx, remoteStorage, stdpath.Join(remoteActualDir, rawName), model.ListArgs{ - ReqPath: stdpath.Join(args.ReqPath, rawName), Refresh: args.Refresh, }) if err != nil { return err } - totalSize := int64(0) + var totalSize int64 + // 0号块默认为-1 以支持空文件 + chunkSizes := []int64{-1} h := make(map[*utils.HashType]string) - var last model.Obj + var first model.Obj for _, o := range chunkObjs { if o.IsDir() { continue } - if after, ok := strings.CutPrefix(strings.TrimSuffix(o.GetName(), d.CustomExt), "hash_"); ok { - hn, value, ok := strings.Cut(after, "_") + if after, ok := strings.CutPrefix(o.GetName(), "hash_"); ok { + hn, value, ok := strings.Cut(strings.TrimSuffix(after, d.CustomExt), "_") if ok { ht, ok := utils.GetHashByName(hn) if ok { h[ht] = value } - continue } + continue } - _, err := strconv.Atoi(strings.TrimSuffix(o.GetName(), d.CustomExt)) + idx, err := strconv.Atoi(strings.TrimSuffix(o.GetName(), d.CustomExt)) if err != nil { continue } - last = o totalSize += o.GetSize() + if len(chunkSizes) > idx { + if idx == 0 { + first = o + } + chunkSizes[idx] = o.GetSize() + } else if len(chunkSizes) == idx { + chunkSizes = append(chunkSizes, o.GetSize()) + } else { + newChunkSizes := make([]int64, idx+1) + copy(newChunkSizes, chunkSizes) + chunkSizes = newChunkSizes + chunkSizes[idx] = o.GetSize() + } } - if last == nil { + // 文件块不完整时,返回文件夹对象 + if chunkSizes[0] == -1 { result[resultIdx] = &model.Object{ Name: rawName, Size: obj.GetSize(), @@ -206,11 +233,22 @@ func (d *Chunk) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([ } return nil } + for i, l := 1, len(chunkSizes)-1; i < l; i++ { + if chunkSizes[i] == 0 { + result[resultIdx] = &model.Object{ + Name: rawName, + Size: obj.GetSize(), + Modified: obj.ModTime(), + IsFolder: true, + } + return nil + } + } objRes := model.Object{ Name: name, Size: totalSize, - Modified: last.ModTime(), - Ctime: last.CreateTime(), + Modified: first.ModTime(), + Ctime: first.CreateTime(), } if len(h) > 0 { objRes.HashInfo = utils.NewHashInfoByMap(h)