diff options
-rw-r--r-- | ChangeLog | 6 | ||||
-rw-r--r-- | PROTOCOL | 16 | ||||
-rw-r--r-- | sftp-client.c | 81 | ||||
-rw-r--r-- | sftp-client.h | 14 | ||||
-rw-r--r-- | sftp-server.c | 24 | ||||
-rw-r--r-- | sftp.1 | 29 | ||||
-rw-r--r-- | sftp.c | 65 |
7 files changed, 182 insertions, 53 deletions
@@ -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 | ||
27 | 20131015 | 33 | 20131015 |
28 | - (djm) OpenBSD CVS Sync | 34 | - (djm) OpenBSD CVS Sync |
@@ -331,4 +331,18 @@ link(oldpath, newpath) and will respond with a SSH_FXP_STATUS message. | |||
331 | This extension is advertised in the SSH_FXP_VERSION hello with version | 331 | This 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 $ | 334 | 10. sftp: Extension request "fsync@openssh.com" |
335 | |||
336 | This 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 | |||
342 | One receiving this request, a server will call fsync(handle_fd) and will | ||
343 | respond with a SSH_FXP_STATUS message. | ||
344 | |||
345 | This 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 | ||
877 | int | ||
878 | do_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 |
873 | char * | 908 | char * |
874 | do_readlink(struct sftp_conn *conn, char *path) | 909 | do_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 | ||
992 | int | 1027 | int |
993 | do_download(struct sftp_conn *conn, char *remote_path, char *local_path, | 1028 | do_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 | ||
1262 | static int | 1303 | static int |
1263 | download_dir_internal(struct sftp_conn *conn, char *src, char *dst, int depth, | 1304 | download_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 | ||
1352 | int | 1395 | int |
1353 | download_dir(struct sftp_conn *conn, char *src, char *dst, | 1396 | download_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 | ||
1370 | int | 1414 | int |
1371 | do_upload(struct sftp_conn *conn, char *local_path, char *remote_path, | 1415 | do_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 | ||
1555 | static int | 1602 | static int |
1556 | upload_dir_internal(struct sftp_conn *conn, char *src, char *dst, int depth, | 1603 | upload_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 | ||
1647 | int | 1695 | int |
1648 | upload_dir(struct sftp_conn *conn, char *src, char *dst, int preserve_flag, | 1696 | upload_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' */ |
101 | int do_symlink(struct sftp_conn *, char *, char *); | 101 | int do_symlink(struct sftp_conn *, char *, char *); |
102 | 102 | ||
103 | /* Call fsync() on open file 'handle' */ | ||
104 | int 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 | */ |
107 | int do_download(struct sftp_conn *, char *, char *, Attrib *, int, int); | 110 | int 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 | */ |
113 | int download_dir(struct sftp_conn *, char *, char *, Attrib *, int, int, int); | 116 | int 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 | */ |
119 | int do_upload(struct sftp_conn *, char *, char *, int); | 123 | int 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 | */ |
125 | int upload_dir(struct sftp_conn *, char *, char *, int, int); | 129 | int 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. */ |
128 | char *path_append(char *, char *); | 132 | char *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); | |||
112 | static void process_extended_statvfs(u_int32_t id); | 112 | static void process_extended_statvfs(u_int32_t id); |
113 | static void process_extended_fstatvfs(u_int32_t id); | 113 | static void process_extended_fstatvfs(u_int32_t id); |
114 | static void process_extended_hardlink(u_int32_t id); | 114 | static void process_extended_hardlink(u_int32_t id); |
115 | static void process_extended_fsync(u_int32_t id); | ||
115 | static void process_extended(u_int32_t id); | 116 | static void process_extended(u_int32_t id); |
116 | 117 | ||
117 | struct sftp_handler { | 118 | struct 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 | ||
1300 | static void | 1305 | static void |
1306 | process_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 | |||
1322 | static void | ||
1301 | process_extended(u_int32_t id) | 1323 | process_extended(u_int32_t id) |
1302 | { | 1324 | { |
1303 | char *request; | 1325 | char *request; |
@@ -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 . |
165 | This option is directly passed to | 165 | This option is directly passed to |
166 | .Xr ssh 1 . | 166 | .Xr ssh 1 . |
167 | .It Fl f | ||
168 | Requests that files be flushed to disk immediately after transfer. | ||
169 | When uploading files, this feature is only enabled if the server | ||
170 | implements the "fsync@openssh.com" extension. | ||
167 | .It Fl i Ar identity_file | 171 | .It Fl i Ar identity_file |
168 | Selects the file from which the identity (private key) for public key | 172 | Selects the file from which the identity (private key) for public key |
169 | authentication is read. | 173 | authentication is read. |
@@ -348,7 +352,7 @@ extension. | |||
348 | Quit | 352 | Quit |
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. | |||
376 | If the remote file contents differ from the partial local copy then the | 380 | If the remote file contents differ from the partial local copy then the |
377 | resultant file is likely to be corrupt. | 381 | resultant file is likely to be corrupt. |
378 | .Pp | 382 | .Pp |
383 | If the | ||
384 | .Fl f | ||
385 | flag is specified, then | ||
386 | .Xr fsync 2 | ||
387 | will ba called after the file transfer has completed to flush the file | ||
388 | to disk. | ||
389 | .Pp | ||
379 | If either the | 390 | If either the |
380 | .Fl P | 391 | .Fl P |
381 | or | 392 | or |
@@ -479,7 +490,7 @@ Create remote directory specified by | |||
479 | .It Ic progress | 490 | .It Ic progress |
480 | Toggle display of progress meter. | 491 | Toggle 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 |
499 | must specify a directory. | 510 | must specify a directory. |
500 | .Pp | 511 | .Pp |
512 | If the | ||
513 | .Fl f | ||
514 | flag is specified, then a request will be sent to the server to call | ||
515 | .Xr fsync 2 | ||
516 | after the file has been transferred. | ||
517 | Note that this is only supported by servers that implement | ||
518 | the "fsync@openssh.com" extension. | ||
519 | .Pp | ||
501 | If either the | 520 | If either the |
502 | .Fl P | 521 | .Fl P |
503 | or | 522 | or |
@@ -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 */ |
95 | int global_pflag = 0; | 95 | int global_pflag = 0; |
96 | 96 | ||
97 | /* When this option is set, transfers will have fsync() called on each file */ | ||
98 | int global_fflag = 0; | ||
99 | |||
97 | /* SIGINT received during command processing */ | 100 | /* SIGINT received during command processing */ |
98 | volatile sig_atomic_t interrupted = 0; | 101 | volatile sig_atomic_t interrupted = 0; |
99 | 102 | ||
@@ -359,7 +362,7 @@ make_absolute(char *p, char *pwd) | |||
359 | 362 | ||
360 | static int | 363 | static int |
361 | parse_getput_flags(const char *cmd, char **argv, int argc, | 364 | parse_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 | ||
575 | static int | 581 | static int |
576 | process_get(struct sftp_conn *conn, char *src, char *dst, char *pwd, | 582 | process_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 | ||
653 | static int | 661 | static int |
654 | process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd, | 662 | process_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 | ||
1178 | static int | 1188 | static int |
1179 | parse_args(const char **cpp, int *aflag, int *hflag, int *iflag, int *lflag, | 1189 | parse_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; |