/* 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;
}
Beispiel #3
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);
        }
    }