summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog6
-rw-r--r--PROTOCOL16
-rw-r--r--sftp-client.c81
-rw-r--r--sftp-client.h14
-rw-r--r--sftp-server.c24
-rw-r--r--sftp.129
-rw-r--r--sftp.c65
7 files changed, 182 insertions, 53 deletions
diff --git a/ChangeLog b/ChangeLog
index 0accc41e2..537f9f56b 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -23,6 +23,12 @@
23 - djm@cvs.openbsd.org 2013/10/16 22:58:01 23 - djm@cvs.openbsd.org 2013/10/16 22:58:01
24 [ssh.c ssh_config.5] 24 [ssh.c ssh_config.5]
25 one I missed in previous: s/isation/ization/ 25 one I missed in previous: s/isation/ization/
26 - djm@cvs.openbsd.org 2013/10/17 00:30:13
27 [PROTOCOL sftp-client.c sftp-client.h sftp-server.c sftp.1 sftp.c]
28 fsync@openssh.com protocol extension for sftp-server
29 client support to allow calling fsync() faster successful transfer
30 patch mostly by imorgan AT nas.nasa.gov; bz#1798
31 "fine" markus@ "grumble OK" deraadt@ "doesn't sound bad to me" millert@
26 32
2720131015 3320131015
28 - (djm) OpenBSD CVS Sync 34 - (djm) OpenBSD CVS Sync
diff --git a/PROTOCOL b/PROTOCOL
index 48b3a4400..0363314c0 100644
--- a/PROTOCOL
+++ b/PROTOCOL
@@ -331,4 +331,18 @@ link(oldpath, newpath) and will respond with a SSH_FXP_STATUS message.
331This extension is advertised in the SSH_FXP_VERSION hello with version 331This extension is advertised in the SSH_FXP_VERSION hello with version
332"1". 332"1".
333 333
334$OpenBSD: PROTOCOL,v 1.20 2013/01/08 18:49:04 markus Exp $ 33410. sftp: Extension request "fsync@openssh.com"
335
336This request asks the server to call fsync(2) on an open file handle.
337
338 uint32 id
339 string "fsync@openssh.com"
340 string handle
341
342One receiving this request, a server will call fsync(handle_fd) and will
343respond with a SSH_FXP_STATUS message.
344
345This extension is advertised in the SSH_FXP_VERSION hello with version
346"1".
347
348$OpenBSD: PROTOCOL,v 1.21 2013/10/17 00:30:13 djm Exp $
diff --git a/sftp-client.c b/sftp-client.c
index 573623b9d..91955262c 100644
--- a/sftp-client.c
+++ b/sftp-client.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: sftp-client.c,v 1.106 2013/10/11 02:52:23 djm Exp $ */ 1/* $OpenBSD: sftp-client.c,v 1.107 2013/10/17 00:30:13 djm Exp $ */
2/* 2/*
3 * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org> 3 * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
4 * 4 *
@@ -76,6 +76,7 @@ struct sftp_conn {
76#define SFTP_EXT_STATVFS 0x00000002 76#define SFTP_EXT_STATVFS 0x00000002
77#define SFTP_EXT_FSTATVFS 0x00000004 77#define SFTP_EXT_FSTATVFS 0x00000004
78#define SFTP_EXT_HARDLINK 0x00000008 78#define SFTP_EXT_HARDLINK 0x00000008
79#define SFTP_EXT_FSYNC 0x00000010
79 u_int exts; 80 u_int exts;
80 u_int64_t limit_kbps; 81 u_int64_t limit_kbps;
81 struct bwlimit bwlimit_in, bwlimit_out; 82 struct bwlimit bwlimit_in, bwlimit_out;
@@ -388,6 +389,10 @@ do_init(int fd_in, int fd_out, u_int transfer_buflen, u_int num_requests,
388 strcmp(value, "1") == 0) { 389 strcmp(value, "1") == 0) {
389 ret->exts |= SFTP_EXT_HARDLINK; 390 ret->exts |= SFTP_EXT_HARDLINK;
390 known = 1; 391 known = 1;
392 } else if (strcmp(name, "fsync@openssh.com") == 0 &&
393 strcmp(value, "1") == 0) {
394 ret->exts |= SFTP_EXT_FSYNC;
395 known = 1;
391 } 396 }
392 if (known) { 397 if (known) {
393 debug2("Server supports extension \"%s\" revision %s", 398 debug2("Server supports extension \"%s\" revision %s",
@@ -743,7 +748,7 @@ do_realpath(struct sftp_conn *conn, char *path)
743 if (type == SSH2_FXP_STATUS) { 748 if (type == SSH2_FXP_STATUS) {
744 u_int status = buffer_get_int(&msg); 749 u_int status = buffer_get_int(&msg);
745 750
746 error("Couldn't canonicalise: %s", fx2txt(status)); 751 error("Couldn't canonicalize: %s", fx2txt(status));
747 buffer_free(&msg); 752 buffer_free(&msg);
748 return NULL; 753 return NULL;
749 } else if (type != SSH2_FXP_NAME) 754 } else if (type != SSH2_FXP_NAME)
@@ -869,6 +874,36 @@ do_symlink(struct sftp_conn *conn, char *oldpath, char *newpath)
869 return(status); 874 return(status);
870} 875}
871 876
877int
878do_fsync(struct sftp_conn *conn, char *handle, u_int handle_len)
879{
880 Buffer msg;
881 u_int status, id;
882
883 /* Silently return if the extension is not supported */
884 if ((conn->exts & SFTP_EXT_FSYNC) == 0)
885 return -1;
886
887 buffer_init(&msg);
888
889 /* Send fsync request */
890 id = conn->msg_id++;
891
892 buffer_put_char(&msg, SSH2_FXP_EXTENDED);
893 buffer_put_int(&msg, id);
894 buffer_put_cstring(&msg, "fsync@openssh.com");
895 buffer_put_string(&msg, handle, handle_len);
896 send_msg(conn, &msg);
897 debug3("Sent message fsync@openssh.com I:%u", id);
898 buffer_free(&msg);
899
900 status = get_status(conn, id);
901 if (status != SSH2_FX_OK)
902 error("Couldn't sync file: %s", fx2txt(status));
903
904 return status;
905}
906
872#ifdef notyet 907#ifdef notyet
873char * 908char *
874do_readlink(struct sftp_conn *conn, char *path) 909do_readlink(struct sftp_conn *conn, char *path)
@@ -991,7 +1026,7 @@ send_read_request(struct sftp_conn *conn, u_int id, u_int64_t offset,
991 1026
992int 1027int
993do_download(struct sftp_conn *conn, char *remote_path, char *local_path, 1028do_download(struct sftp_conn *conn, char *remote_path, char *local_path,
994 Attrib *a, int preserve_flag, int resume_flag) 1029 Attrib *a, int preserve_flag, int resume_flag, int fsync_flag)
995{ 1030{
996 Attrib junk; 1031 Attrib junk;
997 Buffer msg; 1032 Buffer msg;
@@ -1251,6 +1286,12 @@ do_download(struct sftp_conn *conn, char *remote_path, char *local_path,
1251 error("Can't set times on \"%s\": %s", 1286 error("Can't set times on \"%s\": %s",
1252 local_path, strerror(errno)); 1287 local_path, strerror(errno));
1253 } 1288 }
1289 if (fsync_flag) {
1290 debug("syncing \"%s\"", local_path);
1291 if (fsync(local_fd) == -1)
1292 error("Couldn't sync file \"%s\": %s",
1293 local_path, strerror(errno));
1294 }
1254 } 1295 }
1255 close(local_fd); 1296 close(local_fd);
1256 buffer_free(&msg); 1297 buffer_free(&msg);
@@ -1261,7 +1302,8 @@ do_download(struct sftp_conn *conn, char *remote_path, char *local_path,
1261 1302
1262static int 1303static int
1263download_dir_internal(struct sftp_conn *conn, char *src, char *dst, int depth, 1304download_dir_internal(struct sftp_conn *conn, char *src, char *dst, int depth,
1264 Attrib *dirattrib, int preserve_flag, int print_flag, int resume_flag) 1305 Attrib *dirattrib, int preserve_flag, int print_flag, int resume_flag,
1306 int fsync_flag)
1265{ 1307{
1266 int i, ret = 0; 1308 int i, ret = 0;
1267 SFTP_DIRENT **dir_entries; 1309 SFTP_DIRENT **dir_entries;
@@ -1314,11 +1356,12 @@ download_dir_internal(struct sftp_conn *conn, char *src, char *dst, int depth,
1314 continue; 1356 continue;
1315 if (download_dir_internal(conn, new_src, new_dst, 1357 if (download_dir_internal(conn, new_src, new_dst,
1316 depth + 1, &(dir_entries[i]->a), preserve_flag, 1358 depth + 1, &(dir_entries[i]->a), preserve_flag,
1317 print_flag, resume_flag) == -1) 1359 print_flag, resume_flag, fsync_flag) == -1)
1318 ret = -1; 1360 ret = -1;
1319 } else if (S_ISREG(dir_entries[i]->a.perm) ) { 1361 } else if (S_ISREG(dir_entries[i]->a.perm) ) {
1320 if (do_download(conn, new_src, new_dst, 1362 if (do_download(conn, new_src, new_dst,
1321 &(dir_entries[i]->a), preserve_flag, resume_flag) == -1) { 1363 &(dir_entries[i]->a), preserve_flag,
1364 resume_flag, fsync_flag) == -1) {
1322 error("Download of file %s to %s failed", 1365 error("Download of file %s to %s failed",
1323 new_src, new_dst); 1366 new_src, new_dst);
1324 ret = -1; 1367 ret = -1;
@@ -1351,25 +1394,26 @@ download_dir_internal(struct sftp_conn *conn, char *src, char *dst, int depth,
1351 1394
1352int 1395int
1353download_dir(struct sftp_conn *conn, char *src, char *dst, 1396download_dir(struct sftp_conn *conn, char *src, char *dst,
1354 Attrib *dirattrib, int preserve_flag, int print_flag, int resume_flag) 1397 Attrib *dirattrib, int preserve_flag, int print_flag,
1398 int resume_flag, int fsync_flag)
1355{ 1399{
1356 char *src_canon; 1400 char *src_canon;
1357 int ret; 1401 int ret;
1358 1402
1359 if ((src_canon = do_realpath(conn, src)) == NULL) { 1403 if ((src_canon = do_realpath(conn, src)) == NULL) {
1360 error("Unable to canonicalise path \"%s\"", src); 1404 error("Unable to canonicalize path \"%s\"", src);
1361 return -1; 1405 return -1;
1362 } 1406 }
1363 1407
1364 ret = download_dir_internal(conn, src_canon, dst, 0, 1408 ret = download_dir_internal(conn, src_canon, dst, 0,
1365 dirattrib, preserve_flag, print_flag, resume_flag); 1409 dirattrib, preserve_flag, print_flag, resume_flag, fsync_flag);
1366 free(src_canon); 1410 free(src_canon);
1367 return ret; 1411 return ret;
1368} 1412}
1369 1413
1370int 1414int
1371do_upload(struct sftp_conn *conn, char *local_path, char *remote_path, 1415do_upload(struct sftp_conn *conn, char *local_path, char *remote_path,
1372 int preserve_flag) 1416 int preserve_flag, int fsync_flag)
1373{ 1417{
1374 int local_fd; 1418 int local_fd;
1375 int status = SSH2_FX_OK; 1419 int status = SSH2_FX_OK;
@@ -1545,6 +1589,9 @@ do_upload(struct sftp_conn *conn, char *local_path, char *remote_path,
1545 if (preserve_flag) 1589 if (preserve_flag)
1546 do_fsetstat(conn, handle, handle_len, &a); 1590 do_fsetstat(conn, handle, handle_len, &a);
1547 1591
1592 if (fsync_flag)
1593 (void)do_fsync(conn, handle, handle_len);
1594
1548 if (do_close(conn, handle, handle_len) != SSH2_FX_OK) 1595 if (do_close(conn, handle, handle_len) != SSH2_FX_OK)
1549 status = -1; 1596 status = -1;
1550 free(handle); 1597 free(handle);
@@ -1554,7 +1601,7 @@ do_upload(struct sftp_conn *conn, char *local_path, char *remote_path,
1554 1601
1555static int 1602static int
1556upload_dir_internal(struct sftp_conn *conn, char *src, char *dst, int depth, 1603upload_dir_internal(struct sftp_conn *conn, char *src, char *dst, int depth,
1557 int preserve_flag, int print_flag) 1604 int preserve_flag, int print_flag, int fsync_flag)
1558{ 1605{
1559 int ret = 0, status; 1606 int ret = 0, status;
1560 DIR *dirp; 1607 DIR *dirp;
@@ -1623,11 +1670,12 @@ upload_dir_internal(struct sftp_conn *conn, char *src, char *dst, int depth,
1623 continue; 1670 continue;
1624 1671
1625 if (upload_dir_internal(conn, new_src, new_dst, 1672 if (upload_dir_internal(conn, new_src, new_dst,
1626 depth + 1, preserve_flag, print_flag) == -1) 1673 depth + 1, preserve_flag, print_flag,
1674 fsync_flag) == -1)
1627 ret = -1; 1675 ret = -1;
1628 } else if (S_ISREG(sb.st_mode)) { 1676 } else if (S_ISREG(sb.st_mode)) {
1629 if (do_upload(conn, new_src, new_dst, 1677 if (do_upload(conn, new_src, new_dst,
1630 preserve_flag) == -1) { 1678 preserve_flag, fsync_flag) == -1) {
1631 error("Uploading of file %s to %s failed!", 1679 error("Uploading of file %s to %s failed!",
1632 new_src, new_dst); 1680 new_src, new_dst);
1633 ret = -1; 1681 ret = -1;
@@ -1646,18 +1694,19 @@ upload_dir_internal(struct sftp_conn *conn, char *src, char *dst, int depth,
1646 1694
1647int 1695int
1648upload_dir(struct sftp_conn *conn, char *src, char *dst, int preserve_flag, 1696upload_dir(struct sftp_conn *conn, char *src, char *dst, int preserve_flag,
1649 int print_flag) 1697 int print_flag, int fsync_flag)
1650{ 1698{
1651 char *dst_canon; 1699 char *dst_canon;
1652 int ret; 1700 int ret;
1653 1701
1654 if ((dst_canon = do_realpath(conn, dst)) == NULL) { 1702 if ((dst_canon = do_realpath(conn, dst)) == NULL) {
1655 error("Unable to canonicalise path \"%s\"", dst); 1703 error("Unable to canonicalize path \"%s\"", dst);
1656 return -1; 1704 return -1;
1657 } 1705 }
1658 1706
1659 ret = upload_dir_internal(conn, src, dst_canon, 0, preserve_flag, 1707 ret = upload_dir_internal(conn, src, dst_canon, 0, preserve_flag,
1660 print_flag); 1708 print_flag, fsync_flag);
1709
1661 free(dst_canon); 1710 free(dst_canon);
1662 return ret; 1711 return ret;
1663} 1712}
diff --git a/sftp-client.h b/sftp-client.h
index bcdd407c8..ba92ad01a 100644
--- a/sftp-client.h
+++ b/sftp-client.h
@@ -1,4 +1,4 @@
1/* $OpenBSD: sftp-client.h,v 1.23 2013/10/11 02:53:45 djm Exp $ */ 1/* $OpenBSD: sftp-client.h,v 1.24 2013/10/17 00:30:13 djm Exp $ */
2 2
3/* 3/*
4 * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org> 4 * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
@@ -100,29 +100,33 @@ int do_hardlink(struct sftp_conn *, char *, char *);
100/* Rename 'oldpath' to 'newpath' */ 100/* Rename 'oldpath' to 'newpath' */
101int do_symlink(struct sftp_conn *, char *, char *); 101int do_symlink(struct sftp_conn *, char *, char *);
102 102
103/* Call fsync() on open file 'handle' */
104int do_fsync(struct sftp_conn *conn, char *, u_int);
105
103/* 106/*
104 * Download 'remote_path' to 'local_path'. Preserve permissions and times 107 * Download 'remote_path' to 'local_path'. Preserve permissions and times
105 * if 'pflag' is set 108 * if 'pflag' is set
106 */ 109 */
107int do_download(struct sftp_conn *, char *, char *, Attrib *, int, int); 110int do_download(struct sftp_conn *, char *, char *, Attrib *, int, int, int);
108 111
109/* 112/*
110 * Recursively download 'remote_directory' to 'local_directory'. Preserve 113 * Recursively download 'remote_directory' to 'local_directory'. Preserve
111 * times if 'pflag' is set 114 * times if 'pflag' is set
112 */ 115 */
113int download_dir(struct sftp_conn *, char *, char *, Attrib *, int, int, int); 116int download_dir(struct sftp_conn *, char *, char *, Attrib *, int,
117 int, int, int);
114 118
115/* 119/*
116 * Upload 'local_path' to 'remote_path'. Preserve permissions and times 120 * Upload 'local_path' to 'remote_path'. Preserve permissions and times
117 * if 'pflag' is set 121 * if 'pflag' is set
118 */ 122 */
119int do_upload(struct sftp_conn *, char *, char *, int); 123int do_upload(struct sftp_conn *, char *, char *, int, int);
120 124
121/* 125/*
122 * Recursively upload 'local_directory' to 'remote_directory'. Preserve 126 * Recursively upload 'local_directory' to 'remote_directory'. Preserve
123 * times if 'pflag' is set 127 * times if 'pflag' is set
124 */ 128 */
125int upload_dir(struct sftp_conn *, char *, char *, int, int); 129int upload_dir(struct sftp_conn *, char *, char *, int, int, int);
126 130
127/* Concatenate paths, taking care of slashes. Caller must free result. */ 131/* Concatenate paths, taking care of slashes. Caller must free result. */
128char *path_append(char *, char *); 132char *path_append(char *, char *);
diff --git a/sftp-server.c b/sftp-server.c
index 3056c454e..ad158f8e2 100644
--- a/sftp-server.c
+++ b/sftp-server.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: sftp-server.c,v 1.101 2013/10/14 23:28:23 djm Exp $ */ 1/* $OpenBSD: sftp-server.c,v 1.102 2013/10/17 00:30:13 djm Exp $ */
2/* 2/*
3 * Copyright (c) 2000-2004 Markus Friedl. All rights reserved. 3 * Copyright (c) 2000-2004 Markus Friedl. All rights reserved.
4 * 4 *
@@ -112,6 +112,7 @@ static void process_extended_posix_rename(u_int32_t id);
112static void process_extended_statvfs(u_int32_t id); 112static void process_extended_statvfs(u_int32_t id);
113static void process_extended_fstatvfs(u_int32_t id); 113static void process_extended_fstatvfs(u_int32_t id);
114static void process_extended_hardlink(u_int32_t id); 114static void process_extended_hardlink(u_int32_t id);
115static void process_extended_fsync(u_int32_t id);
115static void process_extended(u_int32_t id); 116static void process_extended(u_int32_t id);
116 117
117struct sftp_handler { 118struct sftp_handler {
@@ -152,6 +153,7 @@ struct sftp_handler extended_handlers[] = {
152 { "statvfs", "statvfs@openssh.com", 0, process_extended_statvfs, 0 }, 153 { "statvfs", "statvfs@openssh.com", 0, process_extended_statvfs, 0 },
153 { "fstatvfs", "fstatvfs@openssh.com", 0, process_extended_fstatvfs, 0 }, 154 { "fstatvfs", "fstatvfs@openssh.com", 0, process_extended_fstatvfs, 0 },
154 { "hardlink", "hardlink@openssh.com", 0, process_extended_hardlink, 1 }, 155 { "hardlink", "hardlink@openssh.com", 0, process_extended_hardlink, 1 },
156 { "fsync", "fsync@openssh.com", 0, process_extended_fsync, 1 },
155 { NULL, NULL, 0, NULL, 0 } 157 { NULL, NULL, 0, NULL, 0 }
156}; 158};
157 159
@@ -652,6 +654,9 @@ process_init(void)
652 /* hardlink extension */ 654 /* hardlink extension */
653 buffer_put_cstring(&msg, "hardlink@openssh.com"); 655 buffer_put_cstring(&msg, "hardlink@openssh.com");
654 buffer_put_cstring(&msg, "1"); /* version */ 656 buffer_put_cstring(&msg, "1"); /* version */
657 /* fsync extension */
658 buffer_put_cstring(&msg, "fsync@openssh.com");
659 buffer_put_cstring(&msg, "1"); /* version */
655 send_msg(&msg); 660 send_msg(&msg);
656 buffer_free(&msg); 661 buffer_free(&msg);
657} 662}
@@ -1298,6 +1303,23 @@ process_extended_hardlink(u_int32_t id)
1298} 1303}
1299 1304
1300static void 1305static void
1306process_extended_fsync(u_int32_t id)
1307{
1308 int handle, fd, ret, status = SSH2_FX_OP_UNSUPPORTED;
1309
1310 handle = get_handle();
1311 debug3("request %u: fsync (handle %u)", id, handle);
1312 verbose("fsync \"%s\"", handle_to_name(handle));
1313 if ((fd = handle_to_fd(handle)) < 0)
1314 status = SSH2_FX_NO_SUCH_FILE;
1315 else if (handle_is_ok(handle, HANDLE_FILE)) {
1316 ret = fsync(fd);
1317 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
1318 }
1319 send_status(id, status);
1320}
1321
1322static void
1301process_extended(u_int32_t id) 1323process_extended(u_int32_t id)
1302{ 1324{
1303 char *request; 1325 char *request;
diff --git a/sftp.1 b/sftp.1
index 85d64a7fc..9809bec6f 100644
--- a/sftp.1
+++ b/sftp.1
@@ -1,4 +1,4 @@
1.\" $OpenBSD: sftp.1,v 1.94 2013/08/07 06:24:51 jmc Exp $ 1.\" $OpenBSD: sftp.1,v 1.95 2013/10/17 00:30:13 djm Exp $
2.\" 2.\"
3.\" Copyright (c) 2001 Damien Miller. All rights reserved. 3.\" Copyright (c) 2001 Damien Miller. All rights reserved.
4.\" 4.\"
@@ -22,7 +22,7 @@
22.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24.\" 24.\"
25.Dd $Mdocdate: August 7 2013 $ 25.Dd $Mdocdate: October 17 2013 $
26.Dt SFTP 1 26.Dt SFTP 1
27.Os 27.Os
28.Sh NAME 28.Sh NAME
@@ -31,7 +31,7 @@
31.Sh SYNOPSIS 31.Sh SYNOPSIS
32.Nm sftp 32.Nm sftp
33.Bk -words 33.Bk -words
34.Op Fl 1246aCpqrv 34.Op Fl 1246aCfpqrv
35.Op Fl B Ar buffer_size 35.Op Fl B Ar buffer_size
36.Op Fl b Ar batchfile 36.Op Fl b Ar batchfile
37.Op Fl c Ar cipher 37.Op Fl c Ar cipher
@@ -164,6 +164,10 @@ per-user configuration file for
164.Xr ssh 1 . 164.Xr ssh 1 .
165This option is directly passed to 165This option is directly passed to
166.Xr ssh 1 . 166.Xr ssh 1 .
167.It Fl f
168Requests that files be flushed to disk immediately after transfer.
169When uploading files, this feature is only enabled if the server
170implements the "fsync@openssh.com" extension.
167.It Fl i Ar identity_file 171.It Fl i Ar identity_file
168Selects the file from which the identity (private key) for public key 172Selects the file from which the identity (private key) for public key
169authentication is read. 173authentication is read.
@@ -348,7 +352,7 @@ extension.
348Quit 352Quit
349.Nm sftp . 353.Nm sftp .
350.It Xo Ic get 354.It Xo Ic get
351.Op Fl aPpr 355.Op Fl afPpr
352.Ar remote-path 356.Ar remote-path
353.Op Ar local-path 357.Op Ar local-path
354.Xc 358.Xc
@@ -376,6 +380,13 @@ the remote copy.
376If the remote file contents differ from the partial local copy then the 380If the remote file contents differ from the partial local copy then the
377resultant file is likely to be corrupt. 381resultant file is likely to be corrupt.
378.Pp 382.Pp
383If the
384.Fl f
385flag is specified, then
386.Xr fsync 2
387will ba called after the file transfer has completed to flush the file
388to disk.
389.Pp
379If either the 390If either the
380.Fl P 391.Fl P
381or 392or
@@ -479,7 +490,7 @@ Create remote directory specified by
479.It Ic progress 490.It Ic progress
480Toggle display of progress meter. 491Toggle display of progress meter.
481.It Xo Ic put 492.It Xo Ic put
482.Op Fl Ppr 493.Op Fl fPpr
483.Ar local-path 494.Ar local-path
484.Op Ar remote-path 495.Op Ar remote-path
485.Xc 496.Xc
@@ -498,6 +509,14 @@ is specified, then
498.Ar remote-path 509.Ar remote-path
499must specify a directory. 510must specify a directory.
500.Pp 511.Pp
512If the
513.Fl f
514flag is specified, then a request will be sent to the server to call
515.Xr fsync 2
516after the file has been transferred.
517Note that this is only supported by servers that implement
518the "fsync@openssh.com" extension.
519.Pp
501If either the 520If either the
502.Fl P 521.Fl P
503or 522or
diff --git a/sftp.c b/sftp.c
index f7b488ae5..c316e1ed4 100644
--- a/sftp.c
+++ b/sftp.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: sftp.c,v 1.155 2013/08/31 00:13:54 djm Exp $ */ 1/* $OpenBSD: sftp.c,v 1.156 2013/10/17 00:30:13 djm Exp $ */
2/* 2/*
3 * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org> 3 * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
4 * 4 *
@@ -94,6 +94,9 @@ int global_aflag = 0;
94/* When this option is set, the file transfers will always preserve times */ 94/* When this option is set, the file transfers will always preserve times */
95int global_pflag = 0; 95int global_pflag = 0;
96 96
97/* When this option is set, transfers will have fsync() called on each file */
98int global_fflag = 0;
99
97/* SIGINT received during command processing */ 100/* SIGINT received during command processing */
98volatile sig_atomic_t interrupted = 0; 101volatile sig_atomic_t interrupted = 0;
99 102
@@ -359,7 +362,7 @@ make_absolute(char *p, char *pwd)
359 362
360static int 363static int
361parse_getput_flags(const char *cmd, char **argv, int argc, 364parse_getput_flags(const char *cmd, char **argv, int argc,
362 int *aflag, int *pflag, int *rflag) 365 int *aflag, int *fflag, int *pflag, int *rflag)
363{ 366{
364 extern int opterr, optind, optopt, optreset; 367 extern int opterr, optind, optopt, optreset;
365 int ch; 368 int ch;
@@ -367,12 +370,15 @@ parse_getput_flags(const char *cmd, char **argv, int argc,
367 optind = optreset = 1; 370 optind = optreset = 1;
368 opterr = 0; 371 opterr = 0;
369 372
370 *aflag = *rflag = *pflag = 0; 373 *aflag = *fflag = *rflag = *pflag = 0;
371 while ((ch = getopt(argc, argv, "aPpRr")) != -1) { 374 while ((ch = getopt(argc, argv, "afPpRr")) != -1) {
372 switch (ch) { 375 switch (ch) {
373 case 'a': 376 case 'a':
374 *aflag = 1; 377 *aflag = 1;
375 break; 378 break;
379 case 'f':
380 *fflag = 1;
381 break;
376 case 'p': 382 case 'p':
377 case 'P': 383 case 'P':
378 *pflag = 1; 384 *pflag = 1;
@@ -574,7 +580,7 @@ pathname_is_dir(char *pathname)
574 580
575static int 581static int
576process_get(struct sftp_conn *conn, char *src, char *dst, char *pwd, 582process_get(struct sftp_conn *conn, char *src, char *dst, char *pwd,
577 int pflag, int rflag, int resume) 583 int pflag, int rflag, int resume, int fflag)
578{ 584{
579 char *abs_src = NULL; 585 char *abs_src = NULL;
580 char *abs_dst = NULL; 586 char *abs_dst = NULL;
@@ -633,11 +639,13 @@ process_get(struct sftp_conn *conn, char *src, char *dst, char *pwd,
633 printf("Fetching %s to %s\n", g.gl_pathv[i], abs_dst); 639 printf("Fetching %s to %s\n", g.gl_pathv[i], abs_dst);
634 if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) { 640 if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
635 if (download_dir(conn, g.gl_pathv[i], abs_dst, NULL, 641 if (download_dir(conn, g.gl_pathv[i], abs_dst, NULL,
636 pflag || global_pflag, 1, resume) == -1) 642 pflag || global_pflag, 1, resume,
643 fflag || global_fflag) == -1)
637 err = -1; 644 err = -1;
638 } else { 645 } else {
639 if (do_download(conn, g.gl_pathv[i], abs_dst, NULL, 646 if (do_download(conn, g.gl_pathv[i], abs_dst, NULL,
640 pflag || global_pflag, resume) == -1) 647 pflag || global_pflag, resume,
648 fflag || global_fflag) == -1)
641 err = -1; 649 err = -1;
642 } 650 }
643 free(abs_dst); 651 free(abs_dst);
@@ -652,7 +660,7 @@ out:
652 660
653static int 661static int
654process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd, 662process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd,
655 int pflag, int rflag) 663 int pflag, int rflag, int fflag)
656{ 664{
657 char *tmp_dst = NULL; 665 char *tmp_dst = NULL;
658 char *abs_dst = NULL; 666 char *abs_dst = NULL;
@@ -719,11 +727,13 @@ process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd,
719 printf("Uploading %s to %s\n", g.gl_pathv[i], abs_dst); 727 printf("Uploading %s to %s\n", g.gl_pathv[i], abs_dst);
720 if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) { 728 if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
721 if (upload_dir(conn, g.gl_pathv[i], abs_dst, 729 if (upload_dir(conn, g.gl_pathv[i], abs_dst,
722 pflag || global_pflag, 1) == -1) 730 pflag || global_pflag, 1,
731 fflag || global_fflag) == -1)
723 err = -1; 732 err = -1;
724 } else { 733 } else {
725 if (do_upload(conn, g.gl_pathv[i], abs_dst, 734 if (do_upload(conn, g.gl_pathv[i], abs_dst,
726 pflag || global_pflag) == -1) 735 pflag || global_pflag,
736 fflag || global_fflag) == -1)
727 err = -1; 737 err = -1;
728 } 738 }
729 } 739 }
@@ -1176,9 +1186,9 @@ makeargv(const char *arg, int *argcp, int sloppy, char *lastquote,
1176} 1186}
1177 1187
1178static int 1188static int
1179parse_args(const char **cpp, int *aflag, int *hflag, int *iflag, int *lflag, 1189parse_args(const char **cpp, int *ignore_errors, int *aflag, int *fflag,
1180 int *pflag, int *rflag, int *sflag, unsigned long *n_arg, 1190 int *hflag, int *iflag, int *lflag, int *pflag, int *rflag, int *sflag,
1181 char **path1, char **path2) 1191 unsigned long *n_arg, char **path1, char **path2)
1182{ 1192{
1183 const char *cmd, *cp = *cpp; 1193 const char *cmd, *cp = *cpp;
1184 char *cp2, **argv; 1194 char *cp2, **argv;
@@ -1190,9 +1200,9 @@ parse_args(const char **cpp, int *aflag, int *hflag, int *iflag, int *lflag,
1190 cp = cp + strspn(cp, WHITESPACE); 1200 cp = cp + strspn(cp, WHITESPACE);
1191 1201
1192 /* Check for leading '-' (disable error processing) */ 1202 /* Check for leading '-' (disable error processing) */
1193 *iflag = 0; 1203 *ignore_errors = 0;
1194 if (*cp == '-') { 1204 if (*cp == '-') {
1195 *iflag = 1; 1205 *ignore_errors = 1;
1196 cp++; 1206 cp++;
1197 cp = cp + strspn(cp, WHITESPACE); 1207 cp = cp + strspn(cp, WHITESPACE);
1198 } 1208 }
@@ -1222,7 +1232,8 @@ parse_args(const char **cpp, int *aflag, int *hflag, int *iflag, int *lflag,
1222 } 1232 }
1223 1233
1224 /* Get arguments and parse flags */ 1234 /* Get arguments and parse flags */
1225 *aflag = *lflag = *pflag = *rflag = *hflag = *n_arg = 0; 1235 *aflag = *fflag = *hflag = *iflag = *lflag = *pflag = 0;
1236 *rflag = *sflag = 0;
1226 *path1 = *path2 = NULL; 1237 *path1 = *path2 = NULL;
1227 optidx = 1; 1238 optidx = 1;
1228 switch (cmdnum) { 1239 switch (cmdnum) {
@@ -1230,7 +1241,7 @@ parse_args(const char **cpp, int *aflag, int *hflag, int *iflag, int *lflag,
1230 case I_REGET: 1241 case I_REGET:
1231 case I_PUT: 1242 case I_PUT:
1232 if ((optidx = parse_getput_flags(cmd, argv, argc, 1243 if ((optidx = parse_getput_flags(cmd, argv, argc,
1233 aflag, pflag, rflag)) == -1) 1244 aflag, fflag, pflag, rflag)) == -1)
1234 return -1; 1245 return -1;
1235 /* Get first pathname (mandatory) */ 1246 /* Get first pathname (mandatory) */
1236 if (argc - optidx < 1) { 1247 if (argc - optidx < 1) {
@@ -1371,8 +1382,8 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
1371 int err_abort) 1382 int err_abort)
1372{ 1383{
1373 char *path1, *path2, *tmp; 1384 char *path1, *path2, *tmp;
1374 int aflag = 0, hflag = 0, iflag = 0, lflag = 0, pflag = 0; 1385 int ignore_errors = 0, aflag = 0, fflag = 0, hflag = 0, iflag = 0;
1375 int rflag = 0, sflag = 0; 1386 int lflag = 0, pflag = 0, rflag = 0, sflag = 0;
1376 int cmdnum, i; 1387 int cmdnum, i;
1377 unsigned long n_arg = 0; 1388 unsigned long n_arg = 0;
1378 Attrib a, *aa; 1389 Attrib a, *aa;
@@ -1381,9 +1392,9 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
1381 glob_t g; 1392 glob_t g;
1382 1393
1383 path1 = path2 = NULL; 1394 path1 = path2 = NULL;
1384 cmdnum = parse_args(&cmd, &aflag, &hflag, &iflag, &lflag, &pflag, 1395 cmdnum = parse_args(&cmd, &ignore_errors, &aflag, &fflag, &hflag,
1385 &rflag, &sflag, &n_arg, &path1, &path2); 1396 &iflag, &lflag, &pflag, &rflag, &sflag, &n_arg, &path1, &path2);
1386 if (iflag != 0) 1397 if (ignore_errors != 0)
1387 err_abort = 0; 1398 err_abort = 0;
1388 1399
1389 memset(&g, 0, sizeof(g)); 1400 memset(&g, 0, sizeof(g));
@@ -1402,10 +1413,11 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
1402 /* FALLTHROUGH */ 1413 /* FALLTHROUGH */
1403 case I_GET: 1414 case I_GET:
1404 err = process_get(conn, path1, path2, *pwd, pflag, 1415 err = process_get(conn, path1, path2, *pwd, pflag,
1405 rflag, aflag); 1416 rflag, aflag, fflag);
1406 break; 1417 break;
1407 case I_PUT: 1418 case I_PUT:
1408 err = process_put(conn, path1, path2, *pwd, pflag, rflag); 1419 err = process_put(conn, path1, path2, *pwd, pflag,
1420 rflag, fflag);
1409 break; 1421 break;
1410 case I_RENAME: 1422 case I_RENAME:
1411 path1 = make_absolute(path1, *pwd); 1423 path1 = make_absolute(path1, *pwd);
@@ -2231,7 +2243,7 @@ main(int argc, char **argv)
2231 infile = stdin; 2243 infile = stdin;
2232 2244
2233 while ((ch = getopt(argc, argv, 2245 while ((ch = getopt(argc, argv,
2234 "1246ahpqrvCc:D:i:l:o:s:S:b:B:F:P:R:")) != -1) { 2246 "1246afhpqrvCc:D:i:l:o:s:S:b:B:F:P:R:")) != -1) {
2235 switch (ch) { 2247 switch (ch) {
2236 /* Passed through to ssh(1) */ 2248 /* Passed through to ssh(1) */
2237 case '4': 2249 case '4':
@@ -2291,6 +2303,9 @@ main(int argc, char **argv)
2291 quiet = batchmode = 1; 2303 quiet = batchmode = 1;
2292 addargs(&args, "-obatchmode yes"); 2304 addargs(&args, "-obatchmode yes");
2293 break; 2305 break;
2306 case 'f':
2307 global_fflag = 1;
2308 break;
2294 case 'p': 2309 case 'p':
2295 global_pflag = 1; 2310 global_pflag = 1;
2296 break; 2311 break;