Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
6 changes: 3 additions & 3 deletions components/execd/pkg/web/controller/filesystem_upload.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ func (c *FilesystemController) processUploadPair(metadataHeader, fileHeader *mul
return uerr
}

resolvedPath, uerr := resolveUploadTarget(meta.Path)
resolvedPath, uerr := resolveUploadTarget(meta.Path, meta.Permission)
if uerr != nil {
return uerr
}
Expand Down Expand Up @@ -137,7 +137,7 @@ func parseUploadMetadata(header *multipart.FileHeader) (*model.FileMetadata, *up
return &meta, nil
}

func resolveUploadTarget(targetPath string) (string, *uploadError) {
func resolveUploadTarget(targetPath string, perm model.Permission) (string, *uploadError) {
resolvedPath, err := pathutil.ExpandPath(targetPath)
if err != nil {
return "", newUploadError(
Expand All @@ -147,7 +147,7 @@ func resolveUploadTarget(targetPath string) (string, *uploadError) {
)
}
targetDir := filepath.Dir(resolvedPath)
if err := os.MkdirAll(targetDir, os.ModePerm); err != nil {
if err := MkdirAllWithOwnership(targetDir, os.ModePerm, perm.Owner, perm.Group); err != nil {
Comment thread
Pangjiping marked this conversation as resolved.
return "", newUploadError(
http.StatusInternalServerError,
model.ErrorCodeRuntimeError,
Expand Down
61 changes: 57 additions & 4 deletions components/execd/pkg/web/controller/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,56 @@ func RenameFile(item model.RenameFileItem) error {
return nil
}

// MkdirAllWithOwnership creates targetDir and any missing parents, then applies
// owner/group only to the directories that were actually created (not pre-existing ones).
func MkdirAllWithOwnership(targetDir string, dirPerm os.FileMode, owner, group string) error {
// Walk up to find the first directory that needs to be created.
firstNew := targetDir
for {
parent := filepath.Dir(firstNew)
if parent == firstNew {
break
Comment thread
Pangjiping marked this conversation as resolved.
Outdated
}
if _, err := os.Stat(firstNew); err == nil {
firstNew = ""
break
}
if _, err := os.Stat(parent); err == nil {
break
}
firstNew = parent
}

if err := os.MkdirAll(targetDir, dirPerm); err != nil {
return err
}

if firstNew == "" || (owner == "" && group == "") {
return nil
}

// Apply ownership to every newly created directory from firstNew down to targetDir.
rel, err := filepath.Rel(firstNew, targetDir)
if err != nil {
return err
}
parts := strings.Split(rel, string(filepath.Separator))
cur := firstNew
if err := SetFileOwnership(cur, owner, group); err != nil {
return err
}
for _, p := range parts {
if p == "." {
continue
}
cur = filepath.Join(cur, p)
if err := SetFileOwnership(cur, owner, group); err != nil {
return err
}
}
return nil
}

func MakeDir(dir string, perm model.Permission) error {
abs, err := pathutil.ExpandAbsPath(dir)
if err != nil {
Expand All @@ -153,13 +203,16 @@ func MakeDir(dir string, perm model.Permission) error {
_, statErr := os.Stat(abs)
existed := statErr == nil

err = os.MkdirAll(abs, os.ModePerm)
if err != nil {
if err := MkdirAllWithOwnership(abs, os.ModePerm, perm.Owner, perm.Group); err != nil {
return err
}

if !existed {
return ChmodFile(abs, perm)
if !existed && perm.Mode != 0 {
mode, err := strconv.ParseUint(strconv.Itoa(perm.Mode), 8, 32)
if err != nil {
return err
}
return os.Chmod(abs, os.FileMode(mode))
}
return nil
}
Expand Down
45 changes: 45 additions & 0 deletions components/execd/pkg/web/controller/utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,51 @@ func TestMakeDir_NewDir(t *testing.T) {
}
}

func TestMkdirAllWithOwnership_NewNestedDirs(t *testing.T) {
tmp := t.TempDir()
nested := filepath.Join(tmp, "a", "b", "c")

err := MkdirAllWithOwnership(nested, 0o755, "", "")
require.NoError(t, err)

for _, seg := range []string{"a", "a/b", "a/b/c"} {
info, err := os.Stat(filepath.Join(tmp, seg))
require.NoError(t, err)
require.True(t, info.IsDir())
}
}

func TestMkdirAllWithOwnership_PreExistingParent(t *testing.T) {
tmp := t.TempDir()
existing := filepath.Join(tmp, "existing")
require.NoError(t, os.Mkdir(existing, 0o755))

origInfo, err := os.Stat(existing)
require.NoError(t, err)
origMode := origInfo.Mode().Perm()

nested := filepath.Join(existing, "new-child", "deep")
err = MkdirAllWithOwnership(nested, 0o755, "", "")
require.NoError(t, err)

afterInfo, err := os.Stat(existing)
require.NoError(t, err)
require.Equal(t, origMode, afterInfo.Mode().Perm(), "pre-existing dir should be unchanged")

info, err := os.Stat(nested)
require.NoError(t, err)
require.True(t, info.IsDir())
}

func TestMkdirAllWithOwnership_AllExist(t *testing.T) {
tmp := t.TempDir()
existing := filepath.Join(tmp, "already")
require.NoError(t, os.MkdirAll(existing, 0o755))

err := MkdirAllWithOwnership(existing, 0o755, "", "")
require.NoError(t, err, "all dirs exist — should be no-op")
}

func TestSetFileOwnership_EmptyOwnerGroup(t *testing.T) {
tmp := t.TempDir()
file := filepath.Join(tmp, "test.txt")
Expand Down
Loading