/* This implements the `svn_ra_neon__startelm_cb_t' prototype. */ static svn_error_t * getlocks_start_element(int *elem, void *userdata, int parent_state, const char *ns, const char *ln, const char **atts) { get_locks_baton_t *baton = userdata; const svn_ra_neon__xml_elm_t *elm; elm = svn_ra_neon__lookup_xml_elem(getlocks_report_elements, ns, ln); /* Just skip unknown elements. */ if (!elm) { *elem = NE_XML_DECLINE; return SVN_NO_ERROR; } if (elm->id == ELEM_lock) { if (parent_state != ELEM_get_locks_report) return UNEXPECTED_ELEMENT(ns, ln); else /* allocate a new svn_lock_t in the permanent pool */ baton->current_lock = svn_lock_create(baton->pool); } else if (elm->id == ELEM_lock_path || elm->id == ELEM_lock_token || elm->id == ELEM_lock_owner || elm->id == ELEM_lock_comment || elm->id == ELEM_lock_creationdate || elm->id == ELEM_lock_expirationdate) { const char *encoding; if (parent_state != ELEM_lock) return UNEXPECTED_ELEMENT(ns, ln); /* look for any incoming encodings on these elements. */ encoding = svn_xml_get_attr_value("encoding", atts); if (encoding) baton->encoding = apr_pstrdup(baton->scratchpool, encoding); } *elem = elm->id; return SVN_NO_ERROR; }
/* Helper func: convert a dav_lock to an svn_lock_t, allocated in pool. */ static dav_error * dav_lock_to_svn_lock(svn_lock_t **slock, const dav_lock *dlock, const char *path, dav_lockdb_private *info, svn_boolean_t is_svn_client, apr_pool_t *pool) { svn_lock_t *lock; /* Sanity checks */ if (dlock->type != DAV_LOCKTYPE_WRITE) return dav_svn__new_error(pool, HTTP_BAD_REQUEST, DAV_ERR_LOCK_SAVE_LOCK, "Only 'write' locks are supported."); if (dlock->scope != DAV_LOCKSCOPE_EXCLUSIVE) return dav_svn__new_error(pool, HTTP_BAD_REQUEST, DAV_ERR_LOCK_SAVE_LOCK, "Only exclusive locks are supported."); lock = svn_lock_create(pool); lock->path = apr_pstrdup(pool, path); lock->token = apr_pstrdup(pool, dlock->locktoken->uuid_str); /* DAV has no concept of lock creationdate, so assume 'now' */ lock->creation_date = apr_time_now(); if (dlock->auth_user) lock->owner = apr_pstrdup(pool, dlock->auth_user); /* We need to be very careful about stripping the <D:owner> tag away from the cdata. It's okay to do for svn clients, but not other DAV clients! */ if (dlock->owner) { if (is_svn_client) { /* mod_dav has forcibly xml-escaped the comment before handing it to us; we need to xml-unescape it (and remove the <D:owner> wrapper) when storing in the repository, so it looks reasonable to the rest of svn. */ dav_error *derr; lock->is_dav_comment = 0; /* comment is NOT xml-wrapped. */ derr = unescape_xml(&(lock->comment), dlock->owner, pool); if (derr) return derr; } else { /* The comment comes from a non-svn client; don't touch this data at all. */ lock->comment = apr_pstrdup(pool, dlock->owner); lock->is_dav_comment = 1; /* comment IS xml-wrapped. */ } } if (dlock->timeout == DAV_TIMEOUT_INFINITE) lock->expiration_date = 0; /* never expires */ else lock->expiration_date = ((apr_time_t)dlock->timeout) * APR_USEC_PER_SEC; *slock = lock; return 0; }
static svn_error_t * txn_body_lock(void *baton, trail_t *trail) { struct lock_args *args = baton; svn_node_kind_t kind = svn_node_file; svn_lock_t *existing_lock; const char *fs_username; svn_lock_t *lock; SVN_ERR(svn_fs_base__get_path_kind(&kind, args->path, trail, trail->pool)); /* Until we implement directory locks someday, we only allow locks on files or non-existent paths. */ if (kind == svn_node_dir) return SVN_FS__ERR_NOT_FILE(trail->fs, args->path); /* While our locking implementation easily supports the locking of nonexistent paths, we deliberately choose not to allow such madness. */ if (kind == svn_node_none) return svn_error_createf(SVN_ERR_FS_NOT_FOUND, NULL, "Path '%s' doesn't exist in HEAD revision", args->path); /* There better be a username attached to the fs. */ if (!trail->fs->access_ctx || !trail->fs->access_ctx->username) return SVN_FS__ERR_NO_USER(trail->fs); else fs_username = trail->fs->access_ctx->username; /* for convenience */ /* Is the caller attempting to lock an out-of-date working file? */ if (SVN_IS_VALID_REVNUM(args->current_rev)) { svn_revnum_t created_rev; SVN_ERR(svn_fs_base__get_path_created_rev(&created_rev, args->path, trail, trail->pool)); /* SVN_INVALID_REVNUM means the path doesn't exist. So apparently somebody is trying to lock something in their working copy, but somebody else has deleted the thing from HEAD. That counts as being 'out of date'. */ if (! SVN_IS_VALID_REVNUM(created_rev)) return svn_error_createf(SVN_ERR_FS_OUT_OF_DATE, NULL, "Path '%s' doesn't exist in HEAD revision", args->path); if (args->current_rev < created_rev) return svn_error_createf(SVN_ERR_FS_OUT_OF_DATE, NULL, "Lock failed: newer version of '%s' exists", args->path); } /* If the caller provided a TOKEN, we *really* need to see if a lock already exists with that token, and if so, verify that the lock's path matches PATH. Otherwise we run the risk of breaking the 1-to-1 mapping of lock tokens to locked paths. */ if (args->token) { svn_lock_t *lock_from_token; svn_error_t *err = svn_fs_bdb__lock_get(&lock_from_token, trail->fs, args->token, trail, trail->pool); if (err && ((err->apr_err == SVN_ERR_FS_LOCK_EXPIRED) || (err->apr_err == SVN_ERR_FS_BAD_LOCK_TOKEN))) { svn_error_clear(err); } else { SVN_ERR(err); if (strcmp(lock_from_token->path, args->path) != 0) return svn_error_create(SVN_ERR_FS_BAD_LOCK_TOKEN, NULL, "Lock failed: token refers to existing " "lock with non-matching path."); } } /* Is the path already locked? Note that this next function call will automatically ignore any errors about {the path not existing as a key, the path's token not existing as a key, the lock just having been expired}. And that's totally fine. Any of these three errors are perfectly acceptable to ignore; it means that the path is now free and clear for locking, because the bdb funcs just cleared out both of the tables for us. */ SVN_ERR(svn_fs_base__get_lock_helper(&existing_lock, args->path, trail, trail->pool)); if (existing_lock) { if (! args->steal_lock) { /* Sorry, the path is already locked. */ return SVN_FS__ERR_PATH_ALREADY_LOCKED(trail->fs, existing_lock); } else { /* ARGS->steal_lock is set, so fs_username is "stealing" the lock from lock->owner. Destroy the existing lock. */ SVN_ERR(delete_lock_and_token(existing_lock->token, existing_lock->path, trail)); } } /* Create a new lock, and add it to the tables. */ lock = svn_lock_create(trail->pool); if (args->token) lock->token = apr_pstrdup(trail->pool, args->token); else SVN_ERR(svn_fs_base__generate_lock_token(&(lock->token), trail->fs, trail->pool)); lock->path = apr_pstrdup(trail->pool, args->path); lock->owner = apr_pstrdup(trail->pool, trail->fs->access_ctx->username); lock->comment = apr_pstrdup(trail->pool, args->comment); lock->is_dav_comment = args->is_dav_comment; lock->creation_date = apr_time_now(); lock->expiration_date = args->expiration_date; SVN_ERR(add_lock_and_token(lock, lock->token, args->path, trail)); *(args->lock_p) = lock; return SVN_NO_ERROR; }
/* Parse the file at DIGEST_PATH, populating the lock LOCK_P in that file (if it exists, and if *LOCK_P is non-NULL) and the hash of CHILDREN_P (if any exist, and if *CHILDREN_P is non-NULL). Use POOL for all allocations. */ static svn_error_t * read_digest_file(apr_hash_t **children_p, svn_lock_t **lock_p, const char *fs_path, const char *digest_path, apr_pool_t *pool) { svn_error_t *err = SVN_NO_ERROR; svn_lock_t *lock; apr_hash_t *hash; svn_stream_t *stream; const char *val; if (lock_p) *lock_p = NULL; if (children_p) *children_p = apr_hash_make(pool); err = svn_stream_open_readonly(&stream, digest_path, pool, pool); if (err && APR_STATUS_IS_ENOENT(err->apr_err)) { svn_error_clear(err); return SVN_NO_ERROR; } SVN_ERR(err); /* If our caller doesn't care about anything but the presence of the file... whatever. */ if (! (lock_p || children_p)) return svn_stream_close(stream); hash = apr_hash_make(pool); if ((err = svn_hash_read2(hash, stream, SVN_HASH_TERMINATOR, pool))) { svn_error_clear(svn_stream_close(stream)); return svn_error_createf(err->apr_err, err, _("Can't parse lock/entries hashfile '%s'"), svn_dirent_local_style(digest_path, pool)); } SVN_ERR(svn_stream_close(stream)); /* If our caller cares, see if we have a lock path in our hash. If so, we'll assume we have a lock here. */ val = hash_fetch(hash, PATH_KEY, pool); if (val && lock_p) { const char *path = val; /* Create our lock and load it up. */ lock = svn_lock_create(pool); lock->path = path; if (! ((lock->token = hash_fetch(hash, TOKEN_KEY, pool)))) return svn_error_trace(err_corrupt_lockfile(fs_path, path)); if (! ((lock->owner = hash_fetch(hash, OWNER_KEY, pool)))) return svn_error_trace(err_corrupt_lockfile(fs_path, path)); if (! ((val = hash_fetch(hash, IS_DAV_COMMENT_KEY, pool)))) return svn_error_trace(err_corrupt_lockfile(fs_path, path)); lock->is_dav_comment = (val[0] == '1'); if (! ((val = hash_fetch(hash, CREATION_DATE_KEY, pool)))) return svn_error_trace(err_corrupt_lockfile(fs_path, path)); SVN_ERR(svn_time_from_cstring(&(lock->creation_date), val, pool)); if ((val = hash_fetch(hash, EXPIRATION_DATE_KEY, pool))) SVN_ERR(svn_time_from_cstring(&(lock->expiration_date), val, pool)); lock->comment = hash_fetch(hash, COMMENT_KEY, pool); *lock_p = lock; } /* If our caller cares, see if we have any children for this path. */ val = hash_fetch(hash, CHILDREN_KEY, pool); if (val && children_p) { apr_array_header_t *kiddos = svn_cstring_split(val, "\n", FALSE, pool); int i; for (i = 0; i < kiddos->nelts; i++) { apr_hash_set(*children_p, APR_ARRAY_IDX(kiddos, i, const char *), APR_HASH_KEY_STRING, (void *)1); } }