aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShulhan <ms@kilabit.info>2026-02-15 13:44:39 +0700
committerShulhan <ms@kilabit.info>2026-02-15 13:45:38 +0700
commit83a162bfa6e6eba78ae439e193682c19b9cd4744 (patch)
tree8588115d422a34aeaefa22e759ef1f19d992784f
parent19d58e9a4c900aec1d63de45a655657c760b1235 (diff)
downloadawwan-83a162bfa6e6eba78ae439e193682c19b9cd4744.tar.xz
all: fix chmod/chown if destination is directory
Given the following command #put!+0644 src/file dst/ If the dst is a directory, it would cause the directory permission changes to 0644. This changes fix it by checking if the destination is a directory first. If we cannot stat the dst, skip the chmod/chown command.
-rw-r--r--CHANGELOG.adoc11
-rw-r--r--awwan_local_test.go5
-rw-r--r--awwan_sudo_test.go29
-rw-r--r--session.go11
-rw-r--r--testdata/local/put.aww2
5 files changed, 57 insertions, 1 deletions
diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc
index 7cb4910..bf40fea 100644
--- a/CHANGELOG.adoc
+++ b/CHANGELOG.adoc
@@ -34,6 +34,17 @@ Using parameters is acceptable only for 2 to 3 arguments.
The `-shutdown-idle` option set the duration when the "serve" command
will stop accepting new connections and shutting down the HTTP server.
+**🌼 all: fix chmod/chown if destination is directory**
+
+Given the following command
+
+ #put!+0644 src/file dst/
+
+If the dst is a directory, it would cause the directory permission
+changes to 0644.
+This changes fix it by checking if the destination is a directory first.
+If we cannot stat the dst, skip the chmod/chown command.
+
[#v0_13_1]
== awwan v0.13.1 (2026-02-09)
diff --git a/awwan_local_test.go b/awwan_local_test.go
index c12f3ec..b663d67 100644
--- a/awwan_local_test.go
+++ b/awwan_local_test.go
@@ -306,6 +306,11 @@ func TestAwwanLocal_Put(t *testing.T) {
t.Fatal(err)
}
+ err = os.Chmod(filepath.Join(baseDir, `tmp`), 0755)
+ if err != nil {
+ t.Fatal(err)
+ }
+
var cases = []testCase{{
desc: `With text file`,
lineRange: `1`,
diff --git a/awwan_sudo_test.go b/awwan_sudo_test.go
index eb26202..8becc0b 100644
--- a/awwan_sudo_test.go
+++ b/awwan_sudo_test.go
@@ -42,6 +42,11 @@ func TestAwwan_Local_SudoGet(t *testing.T) {
t.Fatal(err)
}
+ err = os.Chmod(filepath.Join(baseDir, `tmp`), 0755)
+ if err != nil {
+ t.Fatal(err)
+ }
+
var (
mockTerm = mock.ReadWriter{}
@@ -141,6 +146,7 @@ func TestAwwan_Local_SudoPut(t *testing.T) {
expError string
expContent string
expMode fs.FileMode
+ expDirMode fs.FileMode
}
// Load the test data output.
@@ -157,6 +163,11 @@ func TestAwwan_Local_SudoPut(t *testing.T) {
t.Fatal(err)
}
+ err = os.Chmod(filepath.Join(baseDir, `tmp`), 0755)
+ if err != nil {
+ t.Fatal(err)
+ }
+
var cases = []testCase{{
desc: `WithTextFile`,
lineRange: `7-8`,
@@ -164,6 +175,7 @@ func TestAwwan_Local_SudoPut(t *testing.T) {
fileDest: `/etc/plain.txt`,
expContent: string(tdata.Output[`tmp/plain.txt`]),
expMode: 420,
+ expDirMode: 0755,
}, {
desc: `WithMode`,
lineRange: `14`,
@@ -171,6 +183,7 @@ func TestAwwan_Local_SudoPut(t *testing.T) {
fileDest: `/etc/sudoput_with_mode.txt`,
expContent: string(tdata.Output[`tmp/plain.txt`]),
expMode: 0516,
+ expDirMode: 0755,
}, {
desc: `WithOwner`,
lineRange: `16`,
@@ -178,6 +191,15 @@ func TestAwwan_Local_SudoPut(t *testing.T) {
fileDest: `/etc/sudoput_with_owner.txt`,
expContent: string(tdata.Output[`tmp/plain.txt`]),
expMode: 420,
+ expDirMode: 0755,
+ }, {
+ desc: `WithDestinationIsDirectory`,
+ lineRange: `20`,
+ sudoPass: "awwan\nawwan\n",
+ fileDest: filepath.Join(baseDir, `tmp`, `plain.txt`),
+ expContent: string(tdata.Output[`tmp/plain.txt`]),
+ expMode: 0644,
+ expDirMode: 0755,
}}
var (
@@ -238,7 +260,12 @@ func TestAwwan_Local_SudoPut(t *testing.T) {
if err != nil {
t.Fatal(err)
}
-
test.Assert(t, `permission`, c.expMode, fi.Mode().Perm())
+
+ fi, err = os.Stat(filepath.Dir(c.fileDest))
+ if err != nil {
+ t.Fatal(err)
+ }
+ test.Assert(t, `dirMode`, c.expDirMode, fi.Mode().Perm())
}
}
diff --git a/session.go b/session.go
index 42fd0a4..2ac1c60 100644
--- a/session.go
+++ b/session.go
@@ -265,6 +265,17 @@ func (ses *Session) SudoCopy(ctx context.Context, req *ExecRequest, stmt *Statem
return fmt.Errorf("%s: %w", logp, err)
}
+ dstFileInfo, err := os.Stat(dst)
+ if err != nil {
+ log.Printf(`%s: stat fails, skip setting file mode and/or owner: %s`,
+ logp, err)
+ return nil
+ }
+ if dstFileInfo.IsDir() {
+ srcBase := filepath.Base(src)
+ dst = filepath.Join(dst, srcBase)
+ }
+
if stmt.mode != 0 {
var (
fsmode = strconv.FormatUint(uint64(stmt.mode), 8)
diff --git a/testdata/local/put.aww b/testdata/local/put.aww
index 82297e5..78bc950 100644
--- a/testdata/local/put.aww
+++ b/testdata/local/put.aww
@@ -16,3 +16,5 @@ sudo chmod 0644 /etc/plain.txt
#put!awwan:bin+0644 {{.ScriptDir}}/plain.txt /etc/sudoput_with_owner.txt
#put:$noparse {{.ScriptDir}}/plain.txt {{.ScriptDir}}/tmp/plain_noparse.txt
+
+#put!+0644 {{.ScriptDir}}/plain.txt {{.ScriptDir}}/tmp/