From 992a2259df919ed7938f609c4c0094c5d7a797b4 Mon Sep 17 00:00:00 2001 From: William Wang Date: Tue, 9 Dec 2025 17:39:18 +0800 Subject: [PATCH] fix: failed to create local dir x permission denied (#2880) * fix: failed to copy absolute file: failed to create local dir x permission denied Signed-off-by: William Wang * docs: make log msg more accurate Signed-off-by: William Wang * feat: ensure mode of dir when using src as dir in copy module Signed-off-by: William Wang * refactor: change func name to ensureDestDirMode Signed-off-by: William Wang * fix: pass go lint Signed-off-by: William Wang --------- Signed-off-by: William Wang --- pkg/connector/kubernetes_connector.go | 4 +- pkg/connector/local_connector.go | 4 +- pkg/const/common.go | 7 ++++ pkg/modules/copy.go | 55 +++++++++++++++------------ 4 files changed, 42 insertions(+), 28 deletions(-) diff --git a/pkg/connector/kubernetes_connector.go b/pkg/connector/kubernetes_connector.go index 108dd3ae..2cbdfb9d 100644 --- a/pkg/connector/kubernetes_connector.go +++ b/pkg/connector/kubernetes_connector.go @@ -113,8 +113,8 @@ func (c *kubernetesConnector) PutFile(_ context.Context, src []byte, dst string, if !os.IsNotExist(err) { return errors.Wrapf(err, "failed to stat local dir %q for cluster %q", dst, c.clusterName) } - if err := os.MkdirAll(filepath.Dir(dst), mode); err != nil { - return errors.Wrapf(err, "failed to create local dir %q for cluster %q", dst, c.clusterName) + if err := os.MkdirAll(filepath.Dir(dst), _const.PermDirPublic); err != nil { + return errors.Wrapf(err, "failed to create local dir of path %q for cluster %q", dst, c.clusterName) } } if err := os.WriteFile(dst, src, mode); err != nil { diff --git a/pkg/connector/local_connector.go b/pkg/connector/local_connector.go index ca4befdb..b16c04c5 100644 --- a/pkg/connector/local_connector.go +++ b/pkg/connector/local_connector.go @@ -80,8 +80,8 @@ func (c *localConnector) PutFile(_ context.Context, src []byte, dst string, mode if !os.IsNotExist(err) { return errors.Wrapf(err, "failed to stat local dir %q", dst) } - if err := os.MkdirAll(filepath.Dir(dst), mode); err != nil { - return errors.Wrapf(err, "failed to create local dir %q", dst) + if err := os.MkdirAll(filepath.Dir(dst), _const.PermDirPublic); err != nil { + return errors.Wrapf(err, "failed to create local dir of path %q", dst) } } if err := os.WriteFile(dst, src, mode); err != nil { diff --git a/pkg/const/common.go b/pkg/const/common.go index b34ce350..be841f87 100644 --- a/pkg/const/common.go +++ b/pkg/const/common.go @@ -16,6 +16,8 @@ limitations under the License. package _const +import "os" + // variable specific key in system const ( // === From Global Parameter === // VariableLocalHost is the default local host name in inventory. @@ -123,3 +125,8 @@ const ( // SSHVerifyStatusUnreachable means host server cannot connect to target ssh SSHVerifyStatusUnreachable = "unreachable" ) + +const ( + // PermDirPublic means public permission of directory, something like os.ModePerm + PermDirPublic os.FileMode = 0755 +) diff --git a/pkg/modules/copy.go b/pkg/modules/copy.go index 9f8c3ed4..47f63be7 100644 --- a/pkg/modules/copy.go +++ b/pkg/modules/copy.go @@ -217,7 +217,10 @@ func (ca copyArgs) handleRelativePath(ctx context.Context, options ExecOptions, } // copyAbsoluteDir copies all files from an absolute directory to the remote host. -func (ca copyArgs) copyAbsoluteDir(ctx context.Context, conn connector.Connector) error { +func (ca copyArgs) copyAbsoluteDir(ctx context.Context, conn connector.Connector) (resRrr error) { + defer func() { + resRrr = ca.ensureDestDirMode(ctx, conn) + }() return filepath.WalkDir(ca.src, func(path string, d fs.DirEntry, err error) error { // Only copy files, skip directories if d.IsDir() { @@ -234,25 +237,20 @@ func (ca copyArgs) copyAbsoluteDir(ctx context.Context, conn connector.Connector } mode := info.Mode() - if ca.mode != nil { - mode = os.FileMode(*ca.mode) - } + // read file data, err := os.ReadFile(path) if err != nil { return errors.Wrapf(err, "failed to read file %q", path) } // copy file to remote - dest := ca.dest - if strings.HasSuffix(ca.dest, "/") { - rel, err := filepath.Rel(ca.src, path) - if err != nil { - return errors.Wrap(err, "failed to get relative filepath") - } - dest = filepath.Join(ca.dest, rel) + rel, err := filepath.Rel(ca.src, path) + if err != nil { + return errors.Wrap(err, "failed to get relative filepath") } + dest := filepath.Join(ca.dest, rel) - tmpDest := filepath.Join("/tmp", ca.dest) + tmpDest := filepath.Join("/tmp", dest) if err = conn.PutFile(ctx, data, tmpDest, mode); err != nil { return err @@ -265,7 +263,10 @@ func (ca copyArgs) copyAbsoluteDir(ctx context.Context, conn connector.Connector } // copyRelativeDir copies all files from a relative directory (in the project) to the remote host. -func (ca copyArgs) copyRelativeDir(ctx context.Context, pj project.Project, relPath string, conn connector.Connector) error { +func (ca copyArgs) copyRelativeDir(ctx context.Context, pj project.Project, relPath string, conn connector.Connector) (resRrr error) { + defer func() { + resRrr = ca.ensureDestDirMode(ctx, conn) + }() return pj.WalkDir(relPath, func(path string, d fs.DirEntry, err error) error { if err != nil { return err @@ -281,24 +282,19 @@ func (ca copyArgs) copyRelativeDir(ctx context.Context, pj project.Project, relP } mode := info.Mode() - if ca.mode != nil { - mode = os.FileMode(*ca.mode) - } data, err := pj.ReadFile(path) if err != nil { return errors.Wrap(err, "failed to read file") } - dest := ca.dest - if strings.HasSuffix(ca.dest, "/") { - rel, err := pj.Rel(relPath, path) - if err != nil { - return errors.Wrap(err, "failed to get relative file path") - } - dest = filepath.Join(ca.dest, rel) + rel, err := pj.Rel(relPath, path) + if err != nil { + return errors.Wrap(err, "failed to get relative file path") } - tmpDest := filepath.Join("/tmp", ca.dest) + dest := filepath.Join(ca.dest, rel) + + tmpDest := filepath.Join("/tmp", dest) err = conn.PutFile(ctx, data, tmpDest, mode) if err != nil { @@ -311,6 +307,17 @@ func (ca copyArgs) copyRelativeDir(ctx context.Context, pj project.Project, relP }) } +// ensureDestDirMode if mode args exists, ensure dest dir mode after all files copied +func (ca copyArgs) ensureDestDirMode(ctx context.Context, conn connector.Connector) error { + if ca.mode != nil { + _, _, err := conn.ExecuteCommand(ctx, fmt.Sprintf("chmod %04o %s", *ca.mode, ca.dest)) + if err != nil { + return err + } + } + return nil +} + // copyContent converts the content param and copies it to the destination file on the remote host. func (ca copyArgs) copyContent(ctx context.Context, mode fs.FileMode, conn connector.Connector) (string, string, error) { // Content must be copied to a file, not a directory