mirror of
git://anongit.mindrot.org/openssh.git
synced 2025-01-03 00:02:05 +00:00
upstream: arrange for scp, when in sftp mode, to not ftruncate(3) files
early previous behavious of unconditionally truncating the destination file would cause "scp ~/foo localhost:" and "scp localhost:foo ~/" to delete all the contents of their destination. spotted by solene@ sthen@, also bz3431; ok dtucker@ OpenBSD-Commit-ID: ca39fdd39e0ec1466b9666f15cbcfddea6aaa179
This commit is contained in:
parent
fbcef70c28
commit
56a0697fe0
10
scp.c
10
scp.c
@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: scp.c,v 1.247 2022/03/20 08:52:17 djm Exp $ */
|
/* $OpenBSD: scp.c,v 1.248 2022/05/13 06:31:50 djm Exp $ */
|
||||||
/*
|
/*
|
||||||
* scp - secure remote copy. This is basically patched BSD rcp which
|
* scp - secure remote copy. This is basically patched BSD rcp which
|
||||||
* uses ssh to do the data transfer (instead of using rcmd).
|
* uses ssh to do the data transfer (instead of using rcmd).
|
||||||
@ -1311,11 +1311,11 @@ source_sftp(int argc, char *src, char *targ, struct sftp_conn *conn)
|
|||||||
|
|
||||||
if (src_is_dir && iamrecursive) {
|
if (src_is_dir && iamrecursive) {
|
||||||
if (upload_dir(conn, src, abs_dst, pflag,
|
if (upload_dir(conn, src, abs_dst, pflag,
|
||||||
SFTP_PROGRESS_ONLY, 0, 0, 1) != 0) {
|
SFTP_PROGRESS_ONLY, 0, 0, 1, 1) != 0) {
|
||||||
error("failed to upload directory %s to %s", src, targ);
|
error("failed to upload directory %s to %s", src, targ);
|
||||||
errs = 1;
|
errs = 1;
|
||||||
}
|
}
|
||||||
} else if (do_upload(conn, src, abs_dst, pflag, 0, 0) != 0) {
|
} else if (do_upload(conn, src, abs_dst, pflag, 0, 0, 1) != 0) {
|
||||||
error("failed to upload file %s to %s", src, targ);
|
error("failed to upload file %s to %s", src, targ);
|
||||||
errs = 1;
|
errs = 1;
|
||||||
}
|
}
|
||||||
@ -1552,11 +1552,11 @@ sink_sftp(int argc, char *dst, const char *src, struct sftp_conn *conn)
|
|||||||
debug("Fetching %s to %s\n", g.gl_pathv[i], abs_dst);
|
debug("Fetching %s to %s\n", g.gl_pathv[i], abs_dst);
|
||||||
if (globpath_is_dir(g.gl_pathv[i]) && iamrecursive) {
|
if (globpath_is_dir(g.gl_pathv[i]) && iamrecursive) {
|
||||||
if (download_dir(conn, g.gl_pathv[i], abs_dst, NULL,
|
if (download_dir(conn, g.gl_pathv[i], abs_dst, NULL,
|
||||||
pflag, SFTP_PROGRESS_ONLY, 0, 0, 1) == -1)
|
pflag, SFTP_PROGRESS_ONLY, 0, 0, 1, 1) == -1)
|
||||||
err = -1;
|
err = -1;
|
||||||
} else {
|
} else {
|
||||||
if (do_download(conn, g.gl_pathv[i], abs_dst, NULL,
|
if (do_download(conn, g.gl_pathv[i], abs_dst, NULL,
|
||||||
pflag, 0, 0) == -1)
|
pflag, 0, 0, 1) == -1)
|
||||||
err = -1;
|
err = -1;
|
||||||
}
|
}
|
||||||
free(abs_dst);
|
free(abs_dst);
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: sftp-client.c,v 1.162 2022/03/31 03:07:03 djm Exp $ */
|
/* $OpenBSD: sftp-client.c,v 1.163 2022/05/13 06:31:50 djm Exp $ */
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
|
* Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
|
||||||
*
|
*
|
||||||
@ -1580,7 +1580,7 @@ progress_meter_path(const char *path)
|
|||||||
int
|
int
|
||||||
do_download(struct sftp_conn *conn, const char *remote_path,
|
do_download(struct sftp_conn *conn, const char *remote_path,
|
||||||
const char *local_path, Attrib *a, int preserve_flag, int resume_flag,
|
const char *local_path, Attrib *a, int preserve_flag, int resume_flag,
|
||||||
int fsync_flag)
|
int fsync_flag, int inplace_flag)
|
||||||
{
|
{
|
||||||
struct sshbuf *msg;
|
struct sshbuf *msg;
|
||||||
u_char *handle;
|
u_char *handle;
|
||||||
@ -1627,8 +1627,8 @@ do_download(struct sftp_conn *conn, const char *remote_path,
|
|||||||
&handle, &handle_len) != 0)
|
&handle, &handle_len) != 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
local_fd = open(local_path,
|
local_fd = open(local_path, O_WRONLY | O_CREAT |
|
||||||
O_WRONLY | O_CREAT | (resume_flag ? 0 : O_TRUNC), mode | S_IWUSR);
|
((resume_flag || inplace_flag) ? 0 : O_TRUNC), mode | S_IWUSR);
|
||||||
if (local_fd == -1) {
|
if (local_fd == -1) {
|
||||||
error("open local \"%s\": %s", local_path, strerror(errno));
|
error("open local \"%s\": %s", local_path, strerror(errno));
|
||||||
goto fail;
|
goto fail;
|
||||||
@ -1851,7 +1851,7 @@ do_download(struct sftp_conn *conn, const char *remote_path,
|
|||||||
static int
|
static int
|
||||||
download_dir_internal(struct sftp_conn *conn, const char *src, const char *dst,
|
download_dir_internal(struct sftp_conn *conn, const char *src, const char *dst,
|
||||||
int depth, Attrib *dirattrib, int preserve_flag, int print_flag,
|
int depth, Attrib *dirattrib, int preserve_flag, int print_flag,
|
||||||
int resume_flag, int fsync_flag, int follow_link_flag)
|
int resume_flag, int fsync_flag, int follow_link_flag, int inplace_flag)
|
||||||
{
|
{
|
||||||
int i, ret = 0;
|
int i, ret = 0;
|
||||||
SFTP_DIRENT **dir_entries;
|
SFTP_DIRENT **dir_entries;
|
||||||
@ -1910,7 +1910,7 @@ download_dir_internal(struct sftp_conn *conn, const char *src, const char *dst,
|
|||||||
if (download_dir_internal(conn, new_src, new_dst,
|
if (download_dir_internal(conn, new_src, new_dst,
|
||||||
depth + 1, &(dir_entries[i]->a), preserve_flag,
|
depth + 1, &(dir_entries[i]->a), preserve_flag,
|
||||||
print_flag, resume_flag,
|
print_flag, resume_flag,
|
||||||
fsync_flag, follow_link_flag) == -1)
|
fsync_flag, follow_link_flag, inplace_flag) == -1)
|
||||||
ret = -1;
|
ret = -1;
|
||||||
} else if (S_ISREG(dir_entries[i]->a.perm) ||
|
} else if (S_ISREG(dir_entries[i]->a.perm) ||
|
||||||
(follow_link_flag && S_ISLNK(dir_entries[i]->a.perm))) {
|
(follow_link_flag && S_ISLNK(dir_entries[i]->a.perm))) {
|
||||||
@ -1922,7 +1922,8 @@ download_dir_internal(struct sftp_conn *conn, const char *src, const char *dst,
|
|||||||
if (do_download(conn, new_src, new_dst,
|
if (do_download(conn, new_src, new_dst,
|
||||||
S_ISLNK(dir_entries[i]->a.perm) ? NULL :
|
S_ISLNK(dir_entries[i]->a.perm) ? NULL :
|
||||||
&(dir_entries[i]->a),
|
&(dir_entries[i]->a),
|
||||||
preserve_flag, resume_flag, fsync_flag) == -1) {
|
preserve_flag, resume_flag, fsync_flag,
|
||||||
|
inplace_flag) == -1) {
|
||||||
error("Download of file %s to %s failed",
|
error("Download of file %s to %s failed",
|
||||||
new_src, new_dst);
|
new_src, new_dst);
|
||||||
ret = -1;
|
ret = -1;
|
||||||
@ -1960,7 +1961,7 @@ download_dir_internal(struct sftp_conn *conn, const char *src, const char *dst,
|
|||||||
int
|
int
|
||||||
download_dir(struct sftp_conn *conn, const char *src, const char *dst,
|
download_dir(struct sftp_conn *conn, const char *src, const char *dst,
|
||||||
Attrib *dirattrib, int preserve_flag, int print_flag, int resume_flag,
|
Attrib *dirattrib, int preserve_flag, int print_flag, int resume_flag,
|
||||||
int fsync_flag, int follow_link_flag)
|
int fsync_flag, int follow_link_flag, int inplace_flag)
|
||||||
{
|
{
|
||||||
char *src_canon;
|
char *src_canon;
|
||||||
int ret;
|
int ret;
|
||||||
@ -1972,26 +1973,25 @@ download_dir(struct sftp_conn *conn, const char *src, const char *dst,
|
|||||||
|
|
||||||
ret = download_dir_internal(conn, src_canon, dst, 0,
|
ret = download_dir_internal(conn, src_canon, dst, 0,
|
||||||
dirattrib, preserve_flag, print_flag, resume_flag, fsync_flag,
|
dirattrib, preserve_flag, print_flag, resume_flag, fsync_flag,
|
||||||
follow_link_flag);
|
follow_link_flag, inplace_flag);
|
||||||
free(src_canon);
|
free(src_canon);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
do_upload(struct sftp_conn *conn, const char *local_path,
|
do_upload(struct sftp_conn *conn, const char *local_path,
|
||||||
const char *remote_path, int preserve_flag, int resume, int fsync_flag)
|
const char *remote_path, int preserve_flag, int resume,
|
||||||
|
int fsync_flag, int inplace_flag)
|
||||||
{
|
{
|
||||||
int r, local_fd;
|
int r, local_fd;
|
||||||
u_int status = SSH2_FX_OK;
|
u_int openmode, id, status = SSH2_FX_OK, reordered = 0;
|
||||||
u_int id;
|
|
||||||
u_char type;
|
|
||||||
off_t offset, progress_counter;
|
off_t offset, progress_counter;
|
||||||
u_char *handle, *data;
|
u_char type, *handle, *data;
|
||||||
struct sshbuf *msg;
|
struct sshbuf *msg;
|
||||||
struct stat sb;
|
struct stat sb;
|
||||||
Attrib a, *c = NULL;
|
Attrib a, t, *c = NULL;
|
||||||
u_int32_t startid;
|
u_int32_t startid, ackid;
|
||||||
u_int32_t ackid;
|
u_int64_t highwater = 0;
|
||||||
struct request *ack = NULL;
|
struct request *ack = NULL;
|
||||||
struct requests acks;
|
struct requests acks;
|
||||||
size_t handle_len;
|
size_t handle_len;
|
||||||
@ -2043,10 +2043,15 @@ do_upload(struct sftp_conn *conn, const char *local_path,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
openmode = SSH2_FXF_WRITE|SSH2_FXF_CREAT;
|
||||||
|
if (resume)
|
||||||
|
openmode |= SSH2_FXF_APPEND;
|
||||||
|
else if (!inplace_flag)
|
||||||
|
openmode |= SSH2_FXF_TRUNC;
|
||||||
|
|
||||||
/* Send open request */
|
/* Send open request */
|
||||||
if (send_open(conn, remote_path, "dest", SSH2_FXF_WRITE|SSH2_FXF_CREAT|
|
if (send_open(conn, remote_path, "dest", openmode, &a,
|
||||||
(resume ? SSH2_FXF_APPEND : SSH2_FXF_TRUNC),
|
&handle, &handle_len) != 0) {
|
||||||
&a, &handle, &handle_len) != 0) {
|
|
||||||
close(local_fd);
|
close(local_fd);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@ -2128,6 +2133,12 @@ do_upload(struct sftp_conn *conn, const char *local_path,
|
|||||||
ack->id, ack->len, (unsigned long long)ack->offset);
|
ack->id, ack->len, (unsigned long long)ack->offset);
|
||||||
++ackid;
|
++ackid;
|
||||||
progress_counter += ack->len;
|
progress_counter += ack->len;
|
||||||
|
if (!reordered && ack->offset <= highwater)
|
||||||
|
highwater = ack->offset + ack->len;
|
||||||
|
else if (!reordered && ack->offset > highwater) {
|
||||||
|
debug3_f("server reordered ACKs");
|
||||||
|
reordered = 1;
|
||||||
|
}
|
||||||
free(ack);
|
free(ack);
|
||||||
}
|
}
|
||||||
offset += len;
|
offset += len;
|
||||||
@ -2145,6 +2156,14 @@ do_upload(struct sftp_conn *conn, const char *local_path,
|
|||||||
status = SSH2_FX_FAILURE;
|
status = SSH2_FX_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((resume || inplace_flag) && (status != SSH2_FX_OK || interrupted)) {
|
||||||
|
debug("truncating at %llu", (unsigned long long)highwater);
|
||||||
|
attrib_clear(&t);
|
||||||
|
t.flags = SSH2_FILEXFER_ATTR_SIZE;
|
||||||
|
t.size = highwater;
|
||||||
|
do_fsetstat(conn, handle, handle_len, &a);
|
||||||
|
}
|
||||||
|
|
||||||
if (close(local_fd) == -1) {
|
if (close(local_fd) == -1) {
|
||||||
error("close local \"%s\": %s", local_path, strerror(errno));
|
error("close local \"%s\": %s", local_path, strerror(errno));
|
||||||
status = SSH2_FX_FAILURE;
|
status = SSH2_FX_FAILURE;
|
||||||
@ -2168,7 +2187,7 @@ do_upload(struct sftp_conn *conn, const char *local_path,
|
|||||||
static int
|
static int
|
||||||
upload_dir_internal(struct sftp_conn *conn, const char *src, const char *dst,
|
upload_dir_internal(struct sftp_conn *conn, const char *src, const char *dst,
|
||||||
int depth, int preserve_flag, int print_flag, int resume, int fsync_flag,
|
int depth, int preserve_flag, int print_flag, int resume, int fsync_flag,
|
||||||
int follow_link_flag)
|
int follow_link_flag, int inplace_flag)
|
||||||
{
|
{
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
DIR *dirp;
|
DIR *dirp;
|
||||||
@ -2246,12 +2265,13 @@ upload_dir_internal(struct sftp_conn *conn, const char *src, const char *dst,
|
|||||||
|
|
||||||
if (upload_dir_internal(conn, new_src, new_dst,
|
if (upload_dir_internal(conn, new_src, new_dst,
|
||||||
depth + 1, preserve_flag, print_flag, resume,
|
depth + 1, preserve_flag, print_flag, resume,
|
||||||
fsync_flag, follow_link_flag) == -1)
|
fsync_flag, follow_link_flag, inplace_flag) == -1)
|
||||||
ret = -1;
|
ret = -1;
|
||||||
} else if (S_ISREG(sb.st_mode) ||
|
} else if (S_ISREG(sb.st_mode) ||
|
||||||
(follow_link_flag && S_ISLNK(sb.st_mode))) {
|
(follow_link_flag && S_ISLNK(sb.st_mode))) {
|
||||||
if (do_upload(conn, new_src, new_dst,
|
if (do_upload(conn, new_src, new_dst,
|
||||||
preserve_flag, resume, fsync_flag) == -1) {
|
preserve_flag, resume, fsync_flag,
|
||||||
|
inplace_flag) == -1) {
|
||||||
error("upload \"%s\" to \"%s\" failed",
|
error("upload \"%s\" to \"%s\" failed",
|
||||||
new_src, new_dst);
|
new_src, new_dst);
|
||||||
ret = -1;
|
ret = -1;
|
||||||
@ -2271,7 +2291,7 @@ upload_dir_internal(struct sftp_conn *conn, const char *src, const char *dst,
|
|||||||
int
|
int
|
||||||
upload_dir(struct sftp_conn *conn, const char *src, const char *dst,
|
upload_dir(struct sftp_conn *conn, const char *src, const char *dst,
|
||||||
int preserve_flag, int print_flag, int resume, int fsync_flag,
|
int preserve_flag, int print_flag, int resume, int fsync_flag,
|
||||||
int follow_link_flag)
|
int follow_link_flag, int inplace_flag)
|
||||||
{
|
{
|
||||||
char *dst_canon;
|
char *dst_canon;
|
||||||
int ret;
|
int ret;
|
||||||
@ -2282,7 +2302,7 @@ upload_dir(struct sftp_conn *conn, const char *src, const char *dst,
|
|||||||
}
|
}
|
||||||
|
|
||||||
ret = upload_dir_internal(conn, src, dst_canon, 0, preserve_flag,
|
ret = upload_dir_internal(conn, src, dst_canon, 0, preserve_flag,
|
||||||
print_flag, resume, fsync_flag, follow_link_flag);
|
print_flag, resume, fsync_flag, follow_link_flag, inplace_flag);
|
||||||
|
|
||||||
free(dst_canon);
|
free(dst_canon);
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: sftp-client.h,v 1.36 2022/03/31 03:07:03 djm Exp $ */
|
/* $OpenBSD: sftp-client.h,v 1.37 2022/05/13 06:31:50 djm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
|
* Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
|
||||||
@ -141,28 +141,29 @@ int do_fsync(struct sftp_conn *conn, u_char *, u_int);
|
|||||||
* Download 'remote_path' to 'local_path'. Preserve permissions and times
|
* Download 'remote_path' to 'local_path'. Preserve permissions and times
|
||||||
* if 'pflag' is set
|
* if 'pflag' is set
|
||||||
*/
|
*/
|
||||||
int do_download(struct sftp_conn *, const char *, const char *,
|
int do_download(struct sftp_conn *, const char *, const char *, Attrib *,
|
||||||
Attrib *, int, int, int);
|
int, int, int, int);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Recursively download 'remote_directory' to 'local_directory'. Preserve
|
* Recursively download 'remote_directory' to 'local_directory'. Preserve
|
||||||
* times if 'pflag' is set
|
* times if 'pflag' is set
|
||||||
*/
|
*/
|
||||||
int download_dir(struct sftp_conn *, const char *, const char *,
|
int download_dir(struct sftp_conn *, const char *, const char *, Attrib *,
|
||||||
Attrib *, int, int, int, int, int);
|
int, int, int, int, int, int);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Upload 'local_path' to 'remote_path'. Preserve permissions and times
|
* Upload 'local_path' to 'remote_path'. Preserve permissions and times
|
||||||
* if 'pflag' is set
|
* if 'pflag' is set
|
||||||
*/
|
*/
|
||||||
int do_upload(struct sftp_conn *, const char *, const char *, int, int, int);
|
int do_upload(struct sftp_conn *, const char *, const char *,
|
||||||
|
int, int, int, int);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Recursively upload 'local_directory' to 'remote_directory'. Preserve
|
* Recursively upload 'local_directory' to 'remote_directory'. Preserve
|
||||||
* times if 'pflag' is set
|
* times if 'pflag' is set
|
||||||
*/
|
*/
|
||||||
int upload_dir(struct sftp_conn *, const char *, const char *, int, int, int,
|
int upload_dir(struct sftp_conn *, const char *, const char *,
|
||||||
int, int);
|
int, int, int, int, int, int);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Download a 'from_path' from the 'from' connection and upload it to
|
* Download a 'from_path' from the 'from' connection and upload it to
|
||||||
|
10
sftp.c
10
sftp.c
@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: sftp.c,v 1.215 2022/05/08 22:32:36 djm Exp $ */
|
/* $OpenBSD: sftp.c,v 1.216 2022/05/13 06:31:50 djm Exp $ */
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
|
* Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
|
||||||
*
|
*
|
||||||
@ -697,12 +697,12 @@ process_get(struct sftp_conn *conn, const char *src, const char *dst,
|
|||||||
if (globpath_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
|
if (globpath_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
|
||||||
if (download_dir(conn, g.gl_pathv[i], abs_dst, NULL,
|
if (download_dir(conn, g.gl_pathv[i], abs_dst, NULL,
|
||||||
pflag || global_pflag, 1, resume,
|
pflag || global_pflag, 1, resume,
|
||||||
fflag || global_fflag, 0) == -1)
|
fflag || global_fflag, 0, 0) == -1)
|
||||||
err = -1;
|
err = -1;
|
||||||
} else {
|
} else {
|
||||||
if (do_download(conn, g.gl_pathv[i], abs_dst, NULL,
|
if (do_download(conn, g.gl_pathv[i], abs_dst, NULL,
|
||||||
pflag || global_pflag, resume,
|
pflag || global_pflag, resume,
|
||||||
fflag || global_fflag) == -1)
|
fflag || global_fflag, 0) == -1)
|
||||||
err = -1;
|
err = -1;
|
||||||
}
|
}
|
||||||
free(abs_dst);
|
free(abs_dst);
|
||||||
@ -791,12 +791,12 @@ process_put(struct sftp_conn *conn, const char *src, const char *dst,
|
|||||||
if (globpath_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
|
if (globpath_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
|
||||||
if (upload_dir(conn, g.gl_pathv[i], abs_dst,
|
if (upload_dir(conn, g.gl_pathv[i], abs_dst,
|
||||||
pflag || global_pflag, 1, resume,
|
pflag || global_pflag, 1, resume,
|
||||||
fflag || global_fflag, 0) == -1)
|
fflag || global_fflag, 0, 0) == -1)
|
||||||
err = -1;
|
err = -1;
|
||||||
} else {
|
} else {
|
||||||
if (do_upload(conn, g.gl_pathv[i], abs_dst,
|
if (do_upload(conn, g.gl_pathv[i], abs_dst,
|
||||||
pflag || global_pflag, resume,
|
pflag || global_pflag, resume,
|
||||||
fflag || global_fflag) == -1)
|
fflag || global_fflag, 0) == -1)
|
||||||
err = -1;
|
err = -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user