diff options
author | Ryo Nakamura <upa@haeena.net> | 2023-08-04 15:06:14 +0900 |
---|---|---|
committer | Ryo Nakamura <upa@haeena.net> | 2023-08-04 15:06:14 +0900 |
commit | 24c1bc9149eb8f1c383eaf16d94636b62d07c152 (patch) | |
tree | 54dd5823cb8255a493a851e4ecec74aeae675370 | |
parent | 16f2f88cc91ffa2421393b077611d55d6cd2b771 (diff) |
do not set O_TRUNC when opening destination file.
It prevents `mscp localhost:hoge ~/hoge` from truncating the source
file. See https://bugzilla.mindrot.org/show_bug.cgi?id=3431.
https://github.com/upa/mscp/issues/1
-rw-r--r-- | src/fileops.c | 17 | ||||
-rw-r--r-- | src/fileops.h | 6 | ||||
-rw-r--r-- | src/path.c | 9 | ||||
-rw-r--r-- | test/test_e2e.py | 9 |
4 files changed, 33 insertions, 8 deletions
diff --git a/src/fileops.c b/src/fileops.c index 994dd77..d9587ef 100644 --- a/src/fileops.c +++ b/src/fileops.c @@ -293,15 +293,24 @@ int mscp_lseek(mf *f, size_t off) return ret; } -int mscp_chmod(const char *path, mode_t mode, sftp_session sftp) +int mscp_setstat(const char *path, mode_t mode, size_t size, sftp_session sftp) { int ret; if (sftp) { - ret = sftp_chmod(sftp, path, mode); + struct sftp_attributes_struct attr; + memset(&attr, 0, sizeof(attr)); + attr.permissions = mode; + attr.size = size; + attr.flags = (SSH_FILEXFER_ATTR_PERMISSIONS|SSH_FILEXFER_ATTR_SIZE); + ret = sftp_setstat(sftp, path, &attr); sftp_err_to_errno(sftp); - } else - ret = chmod(path, mode); + } else { + if ((ret = chmod(path, mode)) < 0) + return ret; + if ((ret = truncate(path, size)) < 0) + return ret; + } return ret; } diff --git a/src/fileops.h b/src/fileops.h index ede9c48..8dc6e5b 100644 --- a/src/fileops.h +++ b/src/fileops.h @@ -46,7 +46,11 @@ typedef struct mf_struct mf; mf *mscp_open(const char *path, int flags, mode_t mode, sftp_session sftp); void mscp_close(mf *f); int mscp_lseek(mf *f, size_t off); -int mscp_chmod(const char *path, mode_t mode, sftp_session sftp); + +/* mscp_setstat() involves chmod and truncate. It executes both at + * once via a single SFTP command (sftp_setstat()). + */ +int mscp_setstat(const char *path, mode_t mode, size_t size, sftp_session sftp); /* remote glob */ int mscp_glob(const char *pattern, int flags, glob_t *pglob, sftp_session sftp); @@ -334,8 +334,9 @@ static int touch_dst_path(struct path *p, sftp_session sftp) *needle = '/'; } - /* open file with O_TRUNC to set file size 0 */ - f = mscp_open(p->dst_path, O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR, sftp); + /* Do not set O_TRUNC here. Instead, do mscp_setstat() at the + * end. see https://bugzilla.mindrot.org/show_bug.cgi?id=3431 */ + f = mscp_open(p->dst_path, O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR, sftp); if (!f) { mscp_set_error("mscp_open %s: %s\n", p->dst_path, strerrno()); return -1; @@ -574,7 +575,9 @@ int copy_chunk(FILE *msg_fp, struct chunk *c, if (refcnt_dec(&c->p->refcnt) == 0) { c->p->state = FILE_STATE_DONE; - mscp_chmod(c->p->dst_path, c->p->mode, dst_sftp); + if (mscp_setstat(c->p->dst_path, c->p->mode, c->p->size, dst_sftp) < 0) + mpr_err(msg_fp, "failed to chmod and truncate %s: %s\n", + c->p->path, strerrno()); mpr_info(msg_fp, "copy done: %s\n", c->p->path); } diff --git a/test/test_e2e.py b/test/test_e2e.py index 841c3be..c63a09e 100644 --- a/test/test_e2e.py +++ b/test/test_e2e.py @@ -227,6 +227,15 @@ def test_override_dst_having_larger_size(mscp, src_prefix, dst_prefix): src.cleanup() dst.cleanup() +@pytest.mark.parametrize("src_prefix, dst_prefix", param_remote_prefix) +def test_dont_truncate_dst(mscp, src_prefix, dst_prefix): + f = File("srcanddst", size = 1024 * 1024 * 128).make() + md5_before = f.md5sum() + run2ok([mscp, "-H", "-vvv", src_prefix + f.path, dst_prefix + f.path]) + md5_after = f.md5sum() + assert md5_before == md5_after + f.cleanup() + compressions = ["yes", "no", "none"] @pytest.mark.parametrize("src_prefix, dst_prefix", param_remote_prefix) @pytest.mark.parametrize("compress", compressions) |