static svn_error_t * svn_ra_local__unlock(svn_ra_session_t *session, apr_hash_t *path_tokens, svn_boolean_t force, svn_ra_lock_callback_t lock_func, void *lock_baton, apr_pool_t *pool) { svn_ra_local__session_baton_t *sess = session->priv; apr_hash_index_t *hi; apr_pool_t *iterpool = svn_pool_create(pool); /* A username is absolutely required to unlock a path. */ SVN_ERR(get_username(session, pool)); for (hi = apr_hash_first(pool, path_tokens); hi; hi = apr_hash_next(hi)) { const void *key; const char *path; void *val; const char *abs_path, *token; svn_error_t *err, *callback_err = NULL; svn_pool_clear(iterpool); apr_hash_this(hi, &key, NULL, &val); path = key; /* Since we can't store NULL values in a hash, we turn "" to NULL here. */ if (strcmp(val, "") != 0) token = val; else token = NULL; abs_path = svn_path_join(sess->fs_path->data, path, iterpool); /* This wrapper will call pre- and post-unlock hooks. */ err = svn_repos_fs_unlock(sess->repos, abs_path, token, force, iterpool); if (err && !SVN_ERR_IS_UNLOCK_ERROR(err)) return err; if (lock_func) callback_err = lock_func(lock_baton, path, FALSE, NULL, err, iterpool); svn_error_clear(err); if (callback_err) return callback_err; } svn_pool_destroy(iterpool); return SVN_NO_ERROR; }
/* This implements 'svn_commit_callback_t'. Its invokes the original (wrapped) callback, but also does deltification on the new revision and possibly unlocks committed paths. BATON is 'struct deltify_etc_baton *'. */ static svn_error_t * deltify_etc(const svn_commit_info_t *commit_info, void *baton, apr_pool_t *pool) { struct deltify_etc_baton *db = baton; svn_error_t *err1, *err2; apr_hash_index_t *hi; apr_pool_t *iterpool; /* Invoke the original callback first, in case someone's waiting to know the revision number so they can go off and annotate an issue or something. */ err1 = (*db->callback)(commit_info, db->callback_baton, pool); /* Maybe unlock the paths. */ if (db->lock_tokens) { iterpool = svn_pool_create(db->pool); for (hi = apr_hash_first(db->pool, db->lock_tokens); hi; hi = apr_hash_next(hi)) { const void *rel_path; void *val; const char *abs_path, *token; svn_pool_clear(iterpool); apr_hash_this(hi, &rel_path, NULL, &val); token = val; abs_path = svn_path_join(db->fs_path, rel_path, iterpool); /* We may get errors here if the lock was broken or stolen after the commit succeeded. This is fine and should be ignored. */ svn_error_clear(svn_repos_fs_unlock(db->repos, abs_path, token, FALSE, iterpool)); } svn_pool_destroy(iterpool); } /* But, deltification shouldn't be stopped just because someone's random callback failed, so proceed unconditionally on to deltification. */ err2 = svn_fs_deltify_revision(db->fs, commit_info->revision, db->pool); /* It's more interesting if the original callback failed, so let that one dominate. */ if (err1) { svn_error_clear(err2); return err1; } return err2; }
/* ** Remove any lock that has the specified locktoken. ** ** If locktoken == NULL, then ALL locks are removed. */ static dav_error * remove_lock(dav_lockdb *lockdb, const dav_resource *resource, const dav_locktoken *locktoken) { dav_lockdb_private *info = lockdb->info; svn_error_t *serr; svn_lock_t *slock; const char *token = NULL; /* Sanity check: if the resource has no associated path in the fs, then there's nothing to do. */ if (! resource->info->repos_path) return 0; /* Another easy out: if an svn client sent a 'keep_locks' header (typically in a DELETE request, as part of 'svn commit --no-unlock'), then ignore dav_method_delete()'s attempt to unconditionally remove the lock. */ if (info->keep_locks) return 0; /* If the resource's fs path is unreadable, we don't allow a lock to be removed from it. */ if (! dav_svn__allow_read_resource(resource, SVN_INVALID_REVNUM, resource->pool)) return dav_svn__new_error(resource->pool, HTTP_FORBIDDEN, DAV_ERR_LOCK_SAVE_LOCK, "Path is not accessible."); if (locktoken == NULL) { /* Need to manually discover any lock on the resource. */ serr = svn_fs_get_lock(&slock, resource->info->repos->fs, resource->info->repos_path, resource->pool); if (serr) return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, "Failed to check path for a lock.", resource->pool); if (slock) token = slock->token; } else { token = locktoken->uuid_str; } if (token) { /* Notice that a generic DAV client is unable to forcibly 'break' a lock, because info->lock_break will always be FALSE. An svn client, however, can request a 'forced' break.*/ serr = svn_repos_fs_unlock(resource->info->repos->repos, resource->info->repos_path, token, info->lock_break, resource->pool); if (serr && serr->apr_err == SVN_ERR_FS_NO_USER) { svn_error_clear(serr); return dav_svn__new_error(resource->pool, HTTP_UNAUTHORIZED, DAV_ERR_LOCK_SAVE_LOCK, "Anonymous lock removal is not allowed."); } else if (serr) return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, "Failed to remove a lock.", resource->pool); /* Log the unlocking as a 'high-level' action. */ dav_svn__operational_log(resource->info, svn_log__unlock_one_path( resource->info->repos_path, info->lock_break, resource->info->r->pool)); } return 0; }