static svn_error_t * svn_ra_local__lock(svn_ra_session_t *session, apr_hash_t *path_revs, const char *comment, 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 lock a path. */ SVN_ERR(get_username(session, pool)); for (hi = apr_hash_first(pool, path_revs); hi; hi = apr_hash_next(hi)) { svn_lock_t *lock; const void *key; const char *path; void *val; svn_revnum_t *revnum; const char *abs_path; svn_error_t *err, *callback_err = NULL; svn_pool_clear(iterpool); apr_hash_this(hi, &key, NULL, &val); path = key; revnum = val; abs_path = svn_path_join(sess->fs_path->data, path, iterpool); /* This wrapper will call pre- and post-lock hooks. */ err = svn_repos_fs_lock(&lock, sess->repos, abs_path, NULL, comment, FALSE /* not DAV comment */, 0 /* no expiration */, *revnum, force, iterpool); if (err && !SVN_ERR_IS_LOCK_ERROR(err)) return err; if (lock_func) callback_err = lock_func(lock_baton, path, TRUE, err ? NULL : lock, err, iterpool); svn_error_clear(err); if (callback_err) return callback_err; } svn_pool_destroy(iterpool); return SVN_NO_ERROR; }
/* ** Refresh all locks, found on the specified resource, which has a ** locktoken in the provided list. ** ** If the lock is indirect, then the direct lock is referenced and ** refreshed. ** ** Each lock that is updated is returned in the <locks> argument. ** Note that the locks will be fully resolved. */ static dav_error * refresh_locks(dav_lockdb *lockdb, const dav_resource *resource, const dav_locktoken_list *ltl, time_t new_time, dav_lock **locks) { /* We're not looping over a list of locks, since we only support one lock per resource. */ dav_locktoken *token = ltl->locktoken; svn_error_t *serr; svn_lock_t *slock; dav_lock *dlock; /* If the resource's fs path is unreadable, we don't want to say anything about locks attached to 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."); /* Convert the path into an svn_lock_t. */ 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, "Token doesn't point to a lock.", resource->pool); /* Sanity check: does the incoming token actually represent the current lock on the incoming resource? */ if ((! slock) || (strcmp(token->uuid_str, slock->token) != 0)) return dav_svn__new_error(resource->pool, HTTP_UNAUTHORIZED, DAV_ERR_LOCK_SAVE_LOCK, "Lock refresh request doesn't match existing " "lock."); /* Now use the tweaked svn_lock_t to 'refresh' the existing lock. */ serr = svn_repos_fs_lock(&slock, resource->info->repos->repos, slock->path, slock->token, slock->comment, slock->is_dav_comment, (new_time == DAV_TIMEOUT_INFINITE) ? 0 : (apr_time_t)new_time * APR_USEC_PER_SEC, SVN_INVALID_REVNUM, TRUE, /* forcibly steal existing lock */ 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 refreshing is not allowed."); } else if (serr) return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, "Failed to refresh existing lock.", resource->pool); /* Convert the refreshed lock into a dav_lock and return it. */ svn_lock_to_dav_lock(&dlock, slock, FALSE, resource->exists, resource->pool); *locks = dlock; return 0; }
/* ** Append the specified lock(s) to the set of locks on this resource. ** ** If "make_indirect" is true (non-zero), then the specified lock(s) ** should be converted to an indirect lock (if it is a direct lock) ** before appending. Note that the conversion to an indirect lock does ** not alter the passed-in lock -- the change is internal the ** append_locks function. ** ** Multiple locks are specified using the lock->next links. */ static dav_error * append_locks(dav_lockdb *lockdb, const dav_resource *resource, int make_indirect, const dav_lock *lock) { dav_lockdb_private *info = lockdb->info; svn_lock_t *slock; svn_error_t *serr; dav_error *derr; /* If the resource's fs path is unreadable, we don't allow a lock to be created on 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 (lock->next) return dav_svn__new_error(resource->pool, HTTP_BAD_REQUEST, DAV_ERR_LOCK_SAVE_LOCK, "Tried to attach multiple locks to a resource."); /* RFC2518bis (section 7.4) doesn't require us to support 'lock-null' resources at all. Instead, it asks that we treat 'LOCK nonexistentURL' as a PUT (followed by a LOCK) of a 0-byte file. */ if (! resource->exists) { svn_revnum_t rev, new_rev; svn_fs_txn_t *txn; svn_fs_root_t *txn_root; const char *conflict_msg; dav_svn_repos *repos = resource->info->repos; apr_hash_t *revprop_table = apr_hash_make(resource->pool); apr_hash_set(revprop_table, SVN_PROP_REVISION_AUTHOR, APR_HASH_KEY_STRING, svn_string_create(repos->username, resource->pool)); if (resource->info->repos->is_svn_client) return dav_svn__new_error(resource->pool, HTTP_METHOD_NOT_ALLOWED, DAV_ERR_LOCK_SAVE_LOCK, "Subversion clients may not lock " "nonexistent paths."); else if (! resource->info->repos->autoversioning) return dav_svn__new_error(resource->pool, HTTP_METHOD_NOT_ALLOWED, DAV_ERR_LOCK_SAVE_LOCK, "Attempted to lock non-existent path; " "turn on autoversioning first."); /* Commit a 0-byte file: */ if ((serr = svn_fs_youngest_rev(&rev, repos->fs, resource->pool))) return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, "Could not determine youngest revision", resource->pool); if ((serr = svn_repos_fs_begin_txn_for_commit2(&txn, repos->repos, rev, revprop_table, resource->pool))) return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, "Could not begin a transaction", resource->pool); if ((serr = svn_fs_txn_root(&txn_root, txn, resource->pool))) return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, "Could not begin a transaction", resource->pool); if ((serr = svn_fs_make_file(txn_root, resource->info->repos_path, resource->pool))) return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, "Could not create empty file.", resource->pool); if ((serr = dav_svn__attach_auto_revprops(txn, resource->info->repos_path, resource->pool))) return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, "Could not create empty file.", resource->pool); serr = svn_repos_fs_commit_txn(&conflict_msg, repos->repos, &new_rev, txn, resource->pool); if (SVN_IS_VALID_REVNUM(new_rev)) { /* ### Log an error in post commit FS processing? */ svn_error_clear(serr); } else { svn_error_clear(svn_fs_abort_txn(txn, resource->pool)); if (serr) return dav_svn__convert_err(serr, HTTP_CONFLICT, apr_psprintf(resource->pool, "Conflict when " "committing '%s'.", conflict_msg), resource->pool); else return dav_svn__new_error(resource->pool, HTTP_INTERNAL_SERVER_ERROR, 0, "Commit failed but there was no error " "provided."); } } /* Convert the dav_lock into an svn_lock_t. */ derr = dav_lock_to_svn_lock(&slock, lock, resource->info->repos_path, info, resource->info->repos->is_svn_client, resource->pool); if (derr) return derr; /* Now use the svn_lock_t to actually perform the lock. */ serr = svn_repos_fs_lock(&slock, resource->info->repos->repos, slock->path, slock->token, slock->comment, slock->is_dav_comment, slock->expiration_date, info->working_revnum, info->lock_steal, 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 creation is not allowed."); } else if (serr) return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, "Failed to create new lock.", resource->pool); /* A standard webdav LOCK response doesn't include any information about the creation date. We send it in a custom header, so that svn clients can fill in svn_lock_t->creation_date. A generic DAV client should just ignore the header. */ apr_table_setn(info->r->headers_out, SVN_DAV_CREATIONDATE_HEADER, svn_time_to_cstring(slock->creation_date, resource->pool)); /* A standard webdav LOCK response doesn't include any information about the owner of the lock. ('DAV:owner' has nothing to do with authorization, it's just a comment that we map to svn_lock_t->comment.) We send the owner in a custom header, so that svn clients can fill in svn_lock_t->owner. A generic DAV client should just ignore the header. */ apr_table_setn(info->r->headers_out, SVN_DAV_LOCK_OWNER_HEADER, slock->owner); /* Log the locking as a 'high-level' action. */ dav_svn__operational_log(resource->info, svn_log__lock_one_path(slock->path, info->lock_steal, resource->info->r->pool)); return 0; }