Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 65 additions & 47 deletions drivers/strm/hook.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"io"
"os"
stdpath "path"
"path/filepath"
"strings"

"github.com/OpenListTeam/OpenList/v4/internal/model"
Expand All @@ -26,6 +27,10 @@ func UpdateLocalStrm(ctx context.Context, path string, objs []model.Obj) {
updateLocal := func(driver *Strm, basePath string, objs []model.Obj) {
relParent := strings.TrimPrefix(basePath, utils.GetActualMountPath(driver.MountPath))
localParentPath := stdpath.Join(driver.SaveStrmLocalPath, relParent)
if err := createLocalDirectory(localParentPath); err != nil {
log.Warnf("failed to create local strm directory %s: %v", localParentPath, err)
return
}
for _, obj := range objs {
localPath := stdpath.Join(localParentPath, obj.GetName())
generateStrm(ctx, driver, obj, localPath)
Expand Down Expand Up @@ -92,55 +97,68 @@ func RemoveStrm(dstPath string, d *Strm) {
}

func generateStrm(ctx context.Context, driver *Strm, obj model.Obj, localPath string) {
if !obj.IsDir() {
if utils.Exists(localPath) && driver.SaveLocalMode == SaveLocalInsertMode {
return
}
link, err := driver.Link(ctx, obj, model.LinkArgs{})
if err != nil {
log.Warnf("failed to generate strm of obj %s: failed to link: %v", localPath, err)
return
}
defer link.Close()
size := link.ContentLength
if size <= 0 {
size = obj.GetSize()
}
rrf, err := stream.GetRangeReaderFromLink(size, link)
if err != nil {
log.Warnf("failed to generate strm of obj %s: failed to get range reader: %v", localPath, err)
return
}
rc, err := rrf.RangeRead(ctx, http_range.Range{Length: -1})
if err != nil {
log.Warnf("failed to generate strm of obj %s: failed to read range: %v", localPath, err)
return
}
defer rc.Close()
same, err := isSameContent(localPath, size, rc)
if err != nil {
log.Warnf("failed to compare content of obj %s: %v", localPath, err)
return
}
if same {
return
}
rc, err = rrf.RangeRead(ctx, http_range.Range{Length: -1})
if err != nil {
log.Warnf("failed to generate strm of obj %s: failed to reread range: %v", localPath, err)
return
}
defer rc.Close()
file, err := utils.CreateNestedFile(localPath)
if err != nil {
log.Warnf("failed to generate strm of obj %s: failed to create local file: %v", localPath, err)
return
}
defer file.Close()
if _, err := utils.CopyWithBuffer(file, rc); err != nil {
log.Warnf("failed to generate strm of obj %s: copy failed: %v", localPath, err)
if obj.IsDir() {
if err := createLocalDirectory(localPath); err != nil {
log.Warnf("failed to create local strm directory %s: %v", localPath, err)
}
return
}

if utils.Exists(localPath) && driver.SaveLocalMode == SaveLocalInsertMode {
return
}
link, err := driver.Link(ctx, obj, model.LinkArgs{})
if err != nil {
log.Warnf("failed to generate strm of obj %s: failed to link: %v", localPath, err)
return
}
defer link.Close()
size := link.ContentLength
if size <= 0 {
size = obj.GetSize()
}
rrf, err := stream.GetRangeReaderFromLink(size, link)
if err != nil {
log.Warnf("failed to generate strm of obj %s: failed to get range reader: %v", localPath, err)
return
}
rc, err := rrf.RangeRead(ctx, http_range.Range{Length: -1})
if err != nil {
log.Warnf("failed to generate strm of obj %s: failed to read range: %v", localPath, err)
return
}
defer rc.Close()
same, err := isSameContent(localPath, size, rc)
if err != nil {
log.Warnf("failed to compare content of obj %s: %v", localPath, err)
return
}
if same {
return
}
rc, err = rrf.RangeRead(ctx, http_range.Range{Length: -1})
if err != nil {
log.Warnf("failed to generate strm of obj %s: failed to reread range: %v", localPath, err)
return
}
defer rc.Close()
if err := createLocalDirectory(filepath.Dir(localPath)); err != nil {
log.Warnf("failed to generate strm of obj %s: failed to create parent directory: %v", localPath, err)
return
}
file, err := os.Create(localPath)
if err != nil {
log.Warnf("failed to generate strm of obj %s: failed to create local file: %v", localPath, err)
return
}
defer file.Close()
if _, err := utils.CopyWithBuffer(file, rc); err != nil {
log.Warnf("failed to generate strm of obj %s: copy failed: %v", localPath, err)
}
}

func createLocalDirectory(path string) error {
return os.MkdirAll(path, 0o777)
}

func isSameContent(localPath string, size int64, rc io.Reader) (bool, error) {
Expand Down
43 changes: 43 additions & 0 deletions drivers/strm/hook_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package strm

import (
"os"
"path/filepath"
"runtime"
"testing"
)

func TestCreateLocalDirectory(t *testing.T) {
root := t.TempDir()
nested := filepath.Join(root, "library", "movie")
if err := createLocalDirectory(nested); err != nil {
t.Fatalf("createLocalDirectory() error = %v", err)
}

if _, err := os.Stat(nested); err != nil {
t.Fatalf("stat created directory: %v", err)
}
}

func TestCreateLocalDirectoryPreservesExistingPermissions(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("directory permission bits are not supported on Windows")
}

root := t.TempDir()
nested := filepath.Join(root, "library", "movie")
if err := os.MkdirAll(nested, 0o700); err != nil {
t.Fatalf("create existing directory: %v", err)
}
if err := createLocalDirectory(nested); err != nil {
t.Fatalf("createLocalDirectory() error = %v", err)
}

info, err := os.Stat(nested)
if err != nil {
t.Fatalf("stat existing directory: %v", err)
}
if got := info.Mode().Perm(); got != 0o700 {
t.Errorf("existing directory permissions = %#o, want %#o", got, 0o700)
}
}
30 changes: 30 additions & 0 deletions drivers/strm/hook_umask_unix_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris

package strm

import (
"os"
"path/filepath"
"syscall"
"testing"
)

func TestCreateLocalDirectoryRespectsUmask(t *testing.T) {
root := t.TempDir()
nested := filepath.Join(root, "library", "movie")

originalUmask := syscall.Umask(0o027)
defer syscall.Umask(originalUmask)

if err := createLocalDirectory(nested); err != nil {
t.Fatalf("createLocalDirectory() error = %v", err)
}

info, err := os.Stat(nested)
if err != nil {
t.Fatalf("stat created directory: %v", err)
}
if got := info.Mode().Perm(); got != 0o750 {
t.Errorf("created directory permissions = %#o, want %#o", got, 0o750)
}
}