From a22d768e0f5024d4990559f51e5455f257b3570a Mon Sep 17 00:00:00 2001 From: njl Date: Sun, 22 Apr 2007 22:33:29 +0000 Subject: Add back the original behavior of changing the entire directory path at once (CWD a/b/c vs. 3 CWDs). If an error occurs, we fall back to the default method of a single CWD per directory element. Since this is technically a violation of the basic FTP RFC, this behavior is under a compile-time option FTP_COMBINE_CWDS and is off by default. It should work with most Unix-based FTP daemons and can save latency. MFC after: 2 weeks --- lib/libfetch/fetch.3 | 12 +++++++++++- lib/libfetch/ftp.c | 24 +++++++++++++++++++++++- 2 files changed, 34 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/libfetch/fetch.3 b/lib/libfetch/fetch.3 index 4c399b7..b145bb0 100644 --- a/lib/libfetch/fetch.3 +++ b/lib/libfetch/fetch.3 @@ -25,7 +25,7 @@ .\" .\" $FreeBSD$ .\" -.Dd July 1, 1998 +.Dd April 22, 2007 .Dt FETCH 3 .Os .Sh NAME @@ -334,6 +334,16 @@ If no user name or password is given, the .Nm fetch library will attempt an anonymous login, with user name "anonymous" and password "anonymous@". +.Pp +By default, +.Nm fetch +changes directories one at a time for the best compatibility with a wide +variety of servers. +If it is compiled with the +.Ql FTP_COMBINE_CWDS +flag, it will first try a single change directory command for the entire +path and fall back to the default scheme if an error occurred. +This can save latency when used with a slow network link. .Sh HTTP SCHEME The .Fn fetchXGetHTTP , diff --git a/lib/libfetch/ftp.c b/lib/libfetch/ftp.c index 92c51ed..3613c4c 100644 --- a/lib/libfetch/ftp.c +++ b/lib/libfetch/ftp.c @@ -267,6 +267,7 @@ _ftp_cwd(conn_t *conn, const char *file) char pwd[PATH_MAX]; int e, i, len; + /* If no slashes in name, no need to change dirs. */ if ((end = strrchr(file, '/')) == NULL) return (0); if ((e = _ftp_cmd(conn, "PWD")) != FTP_WORKING_DIRECTORY || @@ -276,7 +277,8 @@ _ftp_cwd(conn_t *conn, const char *file) } for (;;) { len = strlen(pwd); - /* look for a common prefix */ + + /* Look for a common prefix between PWD and dir to fetch. */ for (i = 0; i <= len && i <= end - file; ++i) if (pwd[i] != file[i]) break; @@ -284,6 +286,7 @@ _ftp_cwd(conn_t *conn, const char *file) DEBUG(fprintf(stderr, "have: [%.*s|%s]\n", i, pwd, pwd + i)); DEBUG(fprintf(stderr, "want: [%.*s|%s]\n", i, file, file + i)); #endif + /* Keep going up a dir until we have a matching prefix. */ if (pwd[i] == '\0' && (file[i - 1] == '/' || file[i] == '/')) break; if ((e = _ftp_cmd(conn, "CDUP")) != FTP_FILE_ACTION_OK || @@ -293,6 +296,23 @@ _ftp_cwd(conn_t *conn, const char *file) return (-1); } } + +#ifdef FTP_COMBINE_CWDS + /* Skip leading slashes, even "////". */ + for (beg = file + i; beg < end && *beg == '/'; ++beg, ++i) + /* nothing */ ; + + /* If there is no trailing dir, we're already there. */ + if (beg >= end) + return (0); + + /* Change to the directory all in one chunk (e.g., foo/bar/baz). */ + e = _ftp_cmd(conn, "CWD %.*s", (int)(end - beg), beg); + if (e == FTP_FILE_ACTION_OK) + return (0); +#endif /* FTP_COMBINE_CWDS */ + + /* That didn't work so go back to legacy behavior (multiple CWDs). */ for (beg = file + i; beg < end; beg = file + i + 1) { while (*beg == '/') ++beg, ++i; @@ -966,6 +986,8 @@ _ftp_connect(struct url *url, struct url *purl, const char *flags) if ((e = _ftp_authenticate(conn, url, purl)) != FTP_LOGGED_IN) goto fouch; + /* TODO: Request extended features supported, if any (RFC 3659). */ + /* done */ return (conn); -- cgit v1.1