diff --git a/CHANGES b/CHANGES index 2ad7ca185..43756b60f 100644 --- a/CHANGES +++ b/CHANGES @@ -6,6 +6,20 @@ Changelog +Daniel Stenberg (17 Feb 2009) +- CURLOPT_FTP_CREATE_MISSING_DIRS can now be set to 2 in addition to 1 for + plain FTP connections, and it will then allow MKD to fail once and retry the + CWD afterwards. This is especially useful if you're doing many simultanoes + connections against the same server and they all have this option enabled, + as then CWD may first fail but then another connection does MKD before this + connection and thus MKD fails but trying CWD works! The numbers can + (should?) now be set with the convenience enums now called + CURLFTP_CREATE_DIR and CURLFTP_CREATE_DIR_RETRY. + + Tests has proven that if you're making an application that uploads a set of + files to an ftp server, you will get a noticable gain in speed if you're + using multiple connections and this option will be then be very useful. + Daniel Stenberg (14 Feb 2009) - Andre Guibert de Bruet found and fixed a memory leak in the content encoding code, which could happen on libz errors. diff --git a/RELEASE-NOTES b/RELEASE-NOTES index 59e442635..ebd3c3a42 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -21,6 +21,8 @@ This release includes the following changes: o Added docs/libcurl/symbols-in-versions o Added CURLINFO_CONDITION_UNMET o Added support for Digest and NTLM authentication using GnuTLS + o CURLOPT_FTP_CREATE_MISSING_DIRS can now be set to 2 to retry the CWD even + when MKD fails This release includes the following bugfixes: diff --git a/docs/libcurl/curl_easy_setopt.3 b/docs/libcurl/curl_easy_setopt.3 index e5e35d3d0..59a277d0d 100644 --- a/docs/libcurl/curl_easy_setopt.3 +++ b/docs/libcurl/curl_easy_setopt.3 @@ -1103,6 +1103,17 @@ This setting also applies to SFTP-connections. curl will attempt to create the remote directory if it can't obtain a handle to the target-location. The creation will fail if a file of the same name as the directory to create already exists or lack of permissions prevents creation. (Added in 7.16.3) + +Starting with 7.19.4, you can also set this value to 2, which will make +libcurl retry the CWD command again if the subsequent MKD command fails. This +is especially useful if you're doing many simultanoes connections against the +same server and they all have this option enabled, as then CWD may first fail +but then another connection does MKD before this connection and thus MKD fails +but trying CWD works! 7.19.4 also introduced the \fICURLFTP_CREATE_DIR\fP and +\fICURLFTP_CREATE_DIR_RETRY\fP enum names for these arguments. + +Before version 7.19.4, libcurl will simply ignore arguments set to 2 and act +as if 1 was selected. .IP CURLOPT_FTP_RESPONSE_TIMEOUT Pass a long. Causes curl to set a timeout period (in seconds) on the amount of time that the server is allowed to take in order to generate a response diff --git a/include/curl/curl.h b/include/curl/curl.h index 3cbf69f7c..69fe45757 100644 --- a/include/curl/curl.h +++ b/include/curl/curl.h @@ -530,6 +530,17 @@ typedef enum { CURLFTPAUTH_LAST /* not an option, never use */ } curl_ftpauth; +/* parameter for the CURLOPT_FTP_CREATE_MISSING_DIRS option */ +typedef enum { + CURLFTP_CREATE_DIR_NONE, /* do NOT create missing dirs! */ + CURLFTP_CREATE_DIR, /* (FTP/SFTP) if CWD fails, try MKD and then CWD + again if MKD succeeded, for SFTP this does + similar magic */ + CURLFTP_CREATE_DIR_RETRY, /* (FTP only) if CWD fails, try MKD and then CWD + again even if MKD failed! */ + CURLFTP_CREATE_DIR_LAST /* not an option, never use */ +} curl_ftpcreatedir; + /* parameter for the CURLOPT_FTP_FILEMETHOD option */ typedef enum { CURLFTPMETHOD_DEFAULT, /* let libcurl pick */ @@ -936,7 +947,10 @@ typedef enum { argument */ CINIT(SSL_CTX_DATA, OBJECTPOINT, 109), - /* FTP Option that causes missing dirs to be created on the remote server */ + /* FTP Option that causes missing dirs to be created on the remote server. + In 7.19.4 we introduced the convenience enums for this option using the + CURLFTP_CREATE_DIR prefix. + */ CINIT(FTP_CREATE_MISSING_DIRS, LONG, 110), /* Set this to a bitmask value to enable the particular authentications diff --git a/lib/ftp.c b/lib/ftp.c index f76868f0d..08a55a0a4 100644 --- a/lib/ftp.c +++ b/lib/ftp.c @@ -828,7 +828,13 @@ static CURLcode ftp_state_cwd(struct connectdata *conn) /* already done and fine */ result = ftp_state_post_cwd(conn); else { - ftpc->count2 = 0; + ftpc->count2 = 0; /* count2 counts failed CWDs */ + + /* count3 is set to allow a MKD to fail once. In the case when first CWD + fails and then MKD fails (due to another session raced it to create the + dir) this then allows for a second try to CWD to it */ + ftpc->count3 = (conn->data->set.ftp_create_missing_dirs==2)?1:0; + if(conn->bits.reuse && ftpc->entrypath) { /* This is a re-used connection. Since we change directory to where the transfer is taking place, we must first get back to the original dir @@ -2834,7 +2840,7 @@ static CURLcode ftp_statemach_act(struct connectdata *conn) break; case FTP_MKD: - if(ftpcode/100 != 2) { + if((ftpcode/100 != 2) && !ftpc->count3--) { /* failure to MKD the dir */ failf(data, "Failed to MKD dir: %03d", ftpcode); return CURLE_REMOTE_ACCESS_DENIED; diff --git a/lib/url.c b/lib/url.c index 6d5bf4207..c16ad97f3 100644 --- a/lib/url.c +++ b/lib/url.c @@ -922,7 +922,7 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, * An FTP option that modifies an upload to create missing directories on * the server. */ - data->set.ftp_create_missing_dirs = (bool)(0 != va_arg(param, long)); + data->set.ftp_create_missing_dirs = (int)va_arg(param, long); break; case CURLOPT_FTP_RESPONSE_TIMEOUT: /* diff --git a/lib/urldata.h b/lib/urldata.h index be3f06191..d1851066e 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -1474,6 +1474,10 @@ struct UserDefined { curl_ftpfile ftp_filemethod; /* how to get to a file when FTP is used */ + int ftp_create_missing_dirs; /* 1 - create directories that don't exist + 2 - the same but also allow MKD to fail once + */ + /* Here follows boolean settings that define how to behave during this session. They are STATIC, set by libcurl users or at least initially and they don't change during operations. */ @@ -1484,7 +1488,6 @@ struct UserDefined { bool prefer_ascii; /* ASCII rather than binary */ bool ftp_append; /* append, not overwrite, on upload */ bool ftp_list_only; /* switch FTP command for listing directories */ - bool ftp_create_missing_dirs; /* create directories that don't exist */ bool ftp_use_port; /* use the FTP PORT command */ bool hide_progress; /* don't use the progress meter */ bool http_fail_on_error; /* fail on HTTP error codes >= 300 */ diff --git a/src/main.c b/src/main.c index 16abdfb7b..958c8b514 100644 --- a/src/main.c +++ b/src/main.c @@ -4753,11 +4753,11 @@ operate(struct Configurable *config, int argc, argv_item_t argv[]) /* new in curl 7.10 */ my_setopt(curl, CURLOPT_ENCODING, - (config->encoding) ? "" : NULL); + (config->encoding) ? "" : NULL); - /* new in curl 7.10.7 */ + /* new in curl 7.10.7, extended in 7.19.4 but this only sets 0 or 1 */ my_setopt(curl, CURLOPT_FTP_CREATE_MISSING_DIRS, - config->ftp_create_dirs); + config->ftp_create_dirs); if(config->proxyanyauth) my_setopt(curl, CURLOPT_PROXYAUTH, CURLAUTH_ANY); else if(config->proxynegotiate) @@ -4925,17 +4925,13 @@ operate(struct Configurable *config, int argc, argv_item_t argv[]) } if(retry) { - static const char * const m[]={NULL, - "timeout", - "HTTP error", - "FTP error" + static const char * const m[]={ + NULL, "timeout", "HTTP error", "FTP error" }; warnf(config, "Transient problem: %s " "Will retry in %ld seconds. " "%ld retries left.\n", - m[retry], - retry_sleep/1000, - retry_numretries); + m[retry], retry_sleep/1000, retry_numretries); go_sleep(retry_sleep); retry_numretries--; @@ -4977,15 +4973,13 @@ operate(struct Configurable *config, int argc, argv_item_t argv[]) } while(1); if((config->progressmode == CURL_PROGRESS_BAR) && - progressbar.calls) { + progressbar.calls) /* if the custom progress bar has been displayed, we output a newline here */ fputs("\n", progressbar.out); - } - if(config->writeout) { + if(config->writeout) ourWriteOut(curl, config->writeout); - } #ifdef USE_ENVIRONMENT if (config->writeenv) ourWriteEnv(curl);