Index: subversion/libsvn_ra_svn/client.c =================================================================== --- subversion/libsvn_ra_svn/client.c (revision 16180) +++ subversion/libsvn_ra_svn/client.c (working copy) @@ -1416,7 +1416,7 @@ return SVN_NO_ERROR; } -/* For each path in @a path_revs, send a 'lock' command to the server. +/* For each path in path_revs, send a 'lock' command to the server. Used with 1.2.x series servers which support locking, but of only one path at a time. ra_svn_lock(), which supports 'lock-many' is now the default. See svn_ra_lock() docstring for interface details. */ @@ -1481,10 +1481,11 @@ return SVN_NO_ERROR; } -/* For each path in @path_tokens, send an 'unlock' command to the server. - Used with 1.2.x series servers which support unlocking, but of only - one path at a time. ra_svn_unlock(), which supports 'unlock-many' is - now the default. See svn_ra_unlock() docstring for interface details. */ +/* For each path_token in path_tokens, send an 'unlock' command to the + server. Used with 1.2.x series servers which support unlocking, + but of only one path at a time. ra_svn_unlock(), which supports + 'unlock-many' is now the default. See svn_ra_unlock() docstring for + interface details. */ static svn_error_t *ra_svn_unlock_compat(svn_ra_session_t *session, apr_hash_t *path_tokens, svn_boolean_t break_lock, @@ -1541,7 +1542,7 @@ return SVN_NO_ERROR; } -/* Tell the server to lock all paths in @a path_revs. +/* Tell the server to lock all paths in path_revs. See svn_ra_lock() for interface details. */ static svn_error_t *ra_svn_lock(svn_ra_session_t *session, apr_hash_t *path_revs, @@ -1552,18 +1553,18 @@ apr_pool_t *pool) { ra_svn_session_baton_t *sess = session->priv; - svn_ra_svn_conn_t* conn = sess->conn; - apr_array_header_t *list; + svn_ra_svn_conn_t *conn = sess->conn; apr_hash_index_t *hi; int i; svn_ra_svn_item_t *elt; svn_error_t *err, *callback_err = NULL; apr_pool_t *subpool = svn_pool_create(pool); - const char *status; apr_array_header_t *condensed_tgt_paths; - condensed_tgt_paths = apr_array_make(pool, 1, sizeof(const char*)); + const char *condensed_tgt_path; + svn_lock_t *lock; - /* (lock-many (?c) b ( (c(?r)) ...) ) */ + condensed_tgt_paths = apr_array_make(pool, 1, sizeof(const char *)); + SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "w((?c)b(!", "lock-many", comment, steal_lock)); @@ -1577,7 +1578,7 @@ svn_pool_clear(subpool); apr_hash_this(hi, &key, NULL, &val); path = key; - APR_ARRAY_PUSH(condensed_tgt_paths, const char*) = path; + APR_ARRAY_PUSH(condensed_tgt_paths, const char *) = path; revnum = val; SVN_ERR(svn_ra_svn_write_tuple(conn, subpool, "c(?r)", @@ -1591,56 +1592,62 @@ /* Pre-1.3 servers don't support 'lock-many'. If that fails, fall back * to 'lock'. */ if (err && err->apr_err == SVN_ERR_RA_SVN_UNKNOWN_CMD) - return ra_svn_lock_compat(session, path_revs, comment, steal_lock, - lock_func, lock_baton, pool); + { + svn_error_clear(err); + return ra_svn_lock_compat(session, path_revs, comment, steal_lock, + lock_func, lock_baton, pool); + } - /* Unknown error */ if (err) return err; - /* svn_ra_svn_read_cmd_response() is unusable as it parses the params, - * instead of returning a list over which to iterate. This means - * failure status must be handled explicitly. */ - err = svn_ra_svn_read_tuple(conn, pool, "wl", &status, &list); - if (strcmp(status, "failure") == 0) - return svn_ra_svn__handle_failure_status(list, pool); - - if (err && !SVN_ERR_IS_LOCK_ERROR(err)) - return err; - - for (i = 0; i < list->nelts; ++i) + /* Loop over responses to get the lock tokens. */ + svn_boolean_t is_done = FALSE; + i = 0; + while (!is_done) { - svn_lock_t *lock; - const char *condensed_tgt_path; - svn_pool_clear(subpool); - condensed_tgt_path = APR_ARRAY_IDX(condensed_tgt_paths, i, const char *); - elt = &APR_ARRAY_IDX(list, i, svn_ra_svn_item_t); + SVN_ERR(svn_ra_svn_read_item(conn, pool, &elt)); - if (elt->kind != SVN_RA_SVN_LIST) + if (elt->kind == SVN_RA_SVN_WORD && strcmp(elt->u.word, "done") == 0) + { + is_done = 1; + break; + } + else if (elt->kind != SVN_RA_SVN_LIST) return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, - _("Lock element not a list")); + _("Location entry not a list")); + else + { + err = parse_lock(elt->u.list, subpool, &lock); + if (err && !SVN_ERR_IS_LOCK_ERROR(err)) + return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, err, + _("Unable to parse lock data")); - err = parse_lock(elt->u.list, subpool, &lock); - if (err) - return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, err, - _("Unable to parse lock data")); + condensed_tgt_path = APR_ARRAY_IDX(condensed_tgt_paths, i, + const char *); + if (lock_func) + callback_err = lock_func(lock_baton, condensed_tgt_path, TRUE, + lock, err, subpool); - if (lock_func) - callback_err = lock_func(lock_baton, condensed_tgt_path, TRUE, - err ? NULL : lock, - err, subpool); + svn_error_clear(err); - if (callback_err) - return callback_err; + if (callback_err) + return callback_err; + } + i++; } + /* Read the response. This is so the server would have a chance to + * report an error. */ + SVN_ERR(svn_ra_svn_read_cmd_response(conn, pool, "")); + svn_pool_destroy(subpool); return SVN_NO_ERROR; } -/* Tell the server to lock all paths in @a path_tokens. +/* Tell the server to unlock all paths in path_tokens. See svn_ra_unlock() for interface details. */ static svn_error_t *ra_svn_unlock(svn_ra_session_t *session, apr_hash_t *path_tokens, @@ -1650,12 +1657,12 @@ apr_pool_t *pool) { ra_svn_session_baton_t *sess = session->priv; - svn_ra_svn_conn_t* conn = sess->conn; + svn_ra_svn_conn_t *conn = sess->conn; apr_hash_index_t *hi; apr_pool_t *subpool = svn_pool_create(pool); svn_error_t *err, *callback_err = NULL; + svn_ra_svn_item_t *elt; - /* (unlock-many (b ( (c(?c)) ...) ) ) */ SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "w(b(!", "unlock-many", break_lock)); @@ -1675,7 +1682,7 @@ else token = NULL; - SVN_ERR(svn_ra_svn_write_tuple(conn, subpool, "c(?c)", path,token)); + SVN_ERR(svn_ra_svn_write_tuple(conn, subpool, "c(?c)", path, token)); } SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "!))")); @@ -1686,34 +1693,52 @@ * to 'unlock'. */ if (err && err->apr_err == SVN_ERR_RA_SVN_UNKNOWN_CMD) - return ra_svn_unlock_compat(session, path_tokens, break_lock, lock_func, - lock_baton, pool); + { + svn_error_clear(err); + return ra_svn_unlock_compat(session, path_tokens, break_lock, lock_func, + lock_baton, pool); + } - /* Unknown error */ if (err) - return err; + { + if (!SVN_ERR_IS_UNLOCK_ERROR(err)) + return err; + else + svn_error_clear(err); + } - err = svn_ra_svn_read_cmd_response(conn, pool, ""); - - if (err && !SVN_ERR_IS_UNLOCK_ERROR(err)) - return err; - - for (hi = apr_hash_first(pool, path_tokens); hi; hi = apr_hash_next(hi)) + /* Loop over responses to unlock the files. */ + svn_boolean_t is_done = FALSE; + hi = apr_hash_first(pool, path_tokens); + while (!is_done) { - const void *key; - const char *path; - svn_pool_clear(subpool); - apr_hash_this(hi, &key, NULL, NULL); - path = key; + SVN_ERR(svn_ra_svn_read_item(conn, pool, &elt)); - if (lock_func) - callback_err = lock_func(lock_baton, path, FALSE, NULL, err, subpool); + if (elt->kind == SVN_RA_SVN_WORD && (strcmp(elt->u.word, "done") == 0)) + { + is_done = 1; + break; + } + else if (elt->kind != SVN_RA_SVN_LIST) + return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, + _("Unlocked path not parseable")); + else + { + const char *path; + SVN_ERR(svn_ra_svn_parse_tuple(elt->u.list, pool, "c", &path)); + if (lock_func) + callback_err = lock_func(lock_baton, path, FALSE, NULL, err, + subpool); + svn_error_clear(err); - if (callback_err) - return callback_err; + if (callback_err) + return callback_err; + } } + SVN_ERR(svn_ra_svn_read_cmd_response(conn, pool, "")); + svn_pool_destroy(subpool); return SVN_NO_ERROR; Index: subversion/libsvn_ra_svn/protocol =================================================================== --- subversion/libsvn_ra_svn/protocol (revision 16180) +++ subversion/libsvn_ra_svn/protocol (working copy) @@ -337,7 +337,9 @@ lock-many params: ( [ comment:string ] steal-lock:bool ( (path:string [ current-rev:number ] ) ... ) ) - response: ( ( lock:lockdesc ) ... ) + Before sending response, server sends lock tokens, ending with "done". + lock-token: ( lock:lockdesc ) | done + response: ( ) unlock params: ( path:string [ token:string ] break-lock:bool ) @@ -345,6 +347,8 @@ unlock-many params: ( break-lock:bool ( ( path:string [ token:string ] ) ... ) ) + Before sending response, server sends unlocked paths, ending with "done". + pre-response: ( path:string ) | done response: ( ) get-lock Index: subversion/svnserve/serve.c =================================================================== --- subversion/svnserve/serve.c (revision 16180) +++ subversion/svnserve/serve.c (working copy) @@ -1594,23 +1594,21 @@ apr_array_header_t *params, void *baton) { server_baton_t *b = baton; - apr_array_header_t *locks, *lock_cmds; + apr_array_header_t *locks; const char *comment; svn_boolean_t steal_lock; int i; apr_pool_t *subpool; - struct lock_cmd { - const char *path; - const char *full_path; - svn_revnum_t current_rev; - svn_lock_t *l; - }; + const char *path; + const char *full_path; + svn_revnum_t current_rev; + svn_lock_t *l = NULL; + svn_error_t *err; SVN_ERR(svn_ra_svn_parse_tuple(params, pool, "(?c)bl", &comment, &steal_lock, &locks)); subpool = svn_pool_create(pool); - lock_cmds = apr_array_make(pool, locks->nelts, sizeof(struct lock_cmd)); /* Because we can only send a single auth reply per request, we send a reply before parsing the lock commands. This means an authz @@ -1621,53 +1619,48 @@ /* Loop through the lock commands. */ for (i = 0; i < locks->nelts; ++i) { - struct lock_cmd *cmd = apr_pcalloc(pool, sizeof(struct lock_cmd)); + svn_pool_clear(subpool); svn_ra_svn_item_t *item = &APR_ARRAY_IDX(locks, i, svn_ra_svn_item_t); if (item->kind != SVN_RA_SVN_LIST) return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, - "Lock commands should be list of lists\n"); + "Lock commands should be list of lists"); - SVN_ERR(svn_ra_svn_parse_tuple(item->u.list, pool, "c(?r)", - &cmd->path, &cmd->current_rev)); + SVN_ERR(svn_ra_svn_parse_tuple(item->u.list, pool, "c(?r)", &path, + ¤t_rev)); - cmd->full_path = svn_path_join(b->fs_path, - svn_path_canonicalize(cmd->path, subpool), - pool); + full_path = svn_path_join(b->fs_path, + svn_path_canonicalize(path, subpool), + pool); - if (! lookup_access(pool, b, svn_authz_write, cmd->full_path, TRUE)) + if (! lookup_access(pool, b, svn_authz_write, full_path, TRUE)) return svn_error_create(SVN_ERR_RA_SVN_CMD_ERR, svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED, NULL, NULL), NULL); - APR_ARRAY_PUSH(lock_cmds, struct lock_cmd) = *cmd; - } + err = svn_repos_fs_lock(&l, b->repos, full_path, + NULL, comment, 0, + 0, /* No expiration time. */ + current_rev, + steal_lock, pool); - /* Loop through each path to be locked. */ - for (i = 0; i < lock_cmds->nelts; i++) - { - struct lock_cmd *cmd = &APR_ARRAY_IDX(lock_cmds, i, struct lock_cmd); - - SVN_CMD_ERR(svn_repos_fs_lock(&cmd->l, b->repos, cmd->full_path, - NULL, comment, 0, - 0, /* No expiration time. */ - cmd->current_rev, - steal_lock, pool)); + if (err) + { + if (SVN_ERR_IS_LOCK_ERROR(err)) + { + SVN_ERR(svn_ra_svn_write_cmd_failure(conn, subpool, err)); + svn_error_clear(err); + } + else + SVN_CMD_ERR(err); + } + else + SVN_ERR(write_lock(conn, subpool, l)); } - /* (success( (ccc(?c)c(?c) ... )) */ - SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "w(!", "success")); + SVN_ERR(svn_ra_svn_write_word(conn, pool, "done")); + SVN_ERR(svn_ra_svn_write_cmd_response(conn, pool, "")); - for (i = 0; i < lock_cmds->nelts; i++) - { - struct lock_cmd *cmd = &APR_ARRAY_IDX(lock_cmds, i, struct lock_cmd); - - svn_pool_clear(subpool); - SVN_ERR(write_lock(conn, subpool, cmd->l)); - } - - SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "!)")); - svn_pool_destroy(subpool); return SVN_NO_ERROR; @@ -1686,7 +1679,7 @@ full_path = svn_path_join(b->fs_path, svn_path_canonicalize(path, pool), pool); - /* Username required unless force was specified. */ + /* Username required unless break_lock was specified. */ SVN_ERR(must_have_access(conn, pool, b, svn_authz_write, full_path, ! break_lock)); @@ -1703,29 +1696,25 @@ { server_baton_t *b = baton; svn_boolean_t break_lock; - apr_array_header_t *unlock_tokens, *unlock_cmds; + apr_array_header_t *unlock_tokens; int i; apr_pool_t *subpool; - struct unlock_cmd { - const char *path; - const char *full_path; - const char *token; - }; + const char *path; + const char *full_path; + const char *token; + svn_error_t *err; SVN_ERR(svn_ra_svn_parse_tuple(params, pool, "bl", &break_lock, &unlock_tokens)); - unlock_cmds = - apr_array_make(pool, unlock_tokens->nelts, sizeof(struct unlock_cmd)); - - /* Username required unless force was specified. */ + /* Username required unless break_lock was specified. */ SVN_ERR(must_have_access(conn, pool, b, svn_authz_write, NULL, ! break_lock)); subpool = svn_pool_create(pool); + /* Loop through the unlock commands. */ for (i = 0; i < unlock_tokens->nelts; i++) { - struct unlock_cmd *cmd = apr_pcalloc(pool, sizeof(struct unlock_cmd)); svn_ra_svn_item_t *item = &APR_ARRAY_IDX(unlock_tokens, i, svn_ra_svn_item_t); @@ -1733,39 +1722,40 @@ if (item->kind != SVN_RA_SVN_LIST) return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, - "Unlock command should be a list of lists\n"); + "Unlock command should be a list of lists"); - SVN_ERR(svn_ra_svn_parse_tuple(item->u.list, subpool, "c(?c)", - &cmd->path, &cmd->token)); + SVN_ERR(svn_ra_svn_parse_tuple(item->u.list, subpool, "c(?c)", &path, + &token)); - cmd->full_path = svn_path_join(b->fs_path, - svn_path_canonicalize(cmd->path, subpool), - pool); + full_path = svn_path_join(b->fs_path, + svn_path_canonicalize(path, subpool), + subpool); - if (! lookup_access(pool, b, svn_authz_write, cmd->full_path, + if (! lookup_access(subpool, b, svn_authz_write, full_path, ! break_lock)) return svn_error_create(SVN_ERR_RA_SVN_CMD_ERR, svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED, NULL, NULL), NULL); - APR_ARRAY_PUSH(unlock_cmds, struct unlock_cmd) = *cmd; + err = svn_repos_fs_unlock(b->repos, full_path, token, break_lock, + subpool); + if (err) + { + if (SVN_ERR_IS_UNLOCK_ERROR(err)) + svn_error_clear(err); + else + SVN_CMD_ERR(err); + } + else + /* Inform client of unlocked path. */ + SVN_CMD_ERR(svn_ra_svn_write_tuple(conn, subpool, "c", path)); } - /* Loop through each path to be unlocked. */ - for (i = 0; i < unlock_cmds->nelts; i++) - { - struct unlock_cmd *cmd = &APR_ARRAY_IDX(unlock_cmds, i, - struct unlock_cmd); + svn_pool_destroy(subpool); - svn_pool_clear(subpool); - SVN_CMD_ERR(svn_repos_fs_unlock(b->repos, cmd->full_path, - cmd->token, break_lock, subpool)); - } - + SVN_ERR(svn_ra_svn_write_word(conn, pool, "done")); SVN_ERR(svn_ra_svn_write_cmd_response(conn, pool, "")); - svn_pool_destroy(subpool); - return SVN_NO_ERROR; }