static svn_error_t * open_root(void *edit_baton, svn_revnum_t base_revision, apr_pool_t *pool, void **root_baton) { struct dir_baton *dirb; struct edit_baton *eb = edit_baton; svn_revnum_t youngest; /* Ignore BASE_REVISION. We always build our transaction against HEAD. However, we will keep it in our dir baton for out of dateness checks. */ SVN_ERR(svn_fs_youngest_rev(&youngest, eb->fs, eb->pool)); if (base_revision > youngest) return svn_error_createf(SVN_ERR_FS_NO_SUCH_REVISION, NULL, _("No such revision %ld (HEAD is %ld)"), base_revision, youngest); /* Unless we've been instructed to use a specific transaction, we'll make our own. */ if (eb->txn_owner) { SVN_ERR(svn_repos_fs_begin_txn_for_commit2(&(eb->txn), eb->repos, youngest, eb->revprop_table, eb->pool)); } else /* Even if we aren't the owner of the transaction, we might have been instructed to set some properties. */ { apr_array_header_t *props = svn_prop_hash_to_array(eb->revprop_table, pool); SVN_ERR(svn_repos_fs_change_txn_props(eb->txn, props, pool)); } SVN_ERR(svn_fs_txn_name(&(eb->txn_name), eb->txn, eb->pool)); SVN_ERR(svn_fs_txn_root(&(eb->txn_root), eb->txn, eb->pool)); /* Create a root dir baton. The `base_path' field is an -absolute- path in the filesystem, upon which all further editor paths are based. */ dirb = apr_pcalloc(pool, sizeof(*dirb)); dirb->edit_baton = edit_baton; dirb->parent = NULL; dirb->pool = pool; dirb->was_copied = FALSE; dirb->path = apr_pstrdup(pool, eb->base_path); dirb->base_rev = base_revision; *root_baton = dirb; return SVN_NO_ERROR; }
dav_error * dav_svn__create_txn(dav_svn_repos *repos, const char **ptxn_name, apr_hash_t *revprops, apr_pool_t *pool) { svn_revnum_t rev; svn_fs_txn_t *txn; svn_error_t *serr; if (! revprops) { revprops = apr_hash_make(pool); } if (repos->username) { svn_hash_sets(revprops, SVN_PROP_REVISION_AUTHOR, svn_string_create(repos->username, pool)); } serr = dav_svn__get_youngest_rev(&rev, repos, pool); if (serr != NULL) { return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, "could not determine youngest revision", repos->pool); } serr = svn_repos_fs_begin_txn_for_commit2(&txn, repos->repos, rev, revprops, repos->pool); if (serr != NULL) { return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, "could not begin a transaction", repos->pool); } serr = svn_fs_txn_name(ptxn_name, txn, pool); if (serr != NULL) { return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, "could not fetch transaction name", repos->pool); } return NULL; }
svn_error_t * svn_repos_fs_begin_txn_for_commit(svn_fs_txn_t **txn_p, svn_repos_t *repos, svn_revnum_t rev, const char *author, const char *log_msg, apr_pool_t *pool) { apr_hash_t *revprop_table = apr_hash_make(pool); if (author) svn_hash_sets(revprop_table, SVN_PROP_REVISION_AUTHOR, svn_string_create(author, pool)); if (log_msg) svn_hash_sets(revprop_table, SVN_PROP_REVISION_LOG, svn_string_create(log_msg, pool)); return svn_repos_fs_begin_txn_for_commit2(txn_p, repos, rev, revprop_table, pool); }
/* ** 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; }