Exemple #1
0
/* If the file at PATH is read-only, attempt to make it writable.  The
 * original state will be restored with RESULT_POOL gets cleaned up.
 * SCRATCH_POOL is for temporary allocations. */
static svn_error_t *
auto_make_writable(const char *path,
                   apr_pool_t *result_pool,
                   apr_pool_t *scratch_pool)
{
  svn_boolean_t is_read_only;
  apr_finfo_t finfo;

  SVN_ERR(svn_io_stat(&finfo, path, SVN__APR_FINFO_READONLY, scratch_pool));
  SVN_ERR(svn_io__is_finfo_read_only(&is_read_only, &finfo, scratch_pool));

  if (is_read_only)
    {
      /* Tell the pool to restore the r/o state upon cleanup
         (assuming the file will still exist, failing silently otherwise). */
      set_read_only_baton_t *baton = apr_pcalloc(result_pool,
                                                  sizeof(*baton));
      baton->pool = result_pool;
      baton->file_path = apr_pstrdup(result_pool, path);
      apr_pool_cleanup_register(result_pool, baton,
                                set_read_only, apr_pool_cleanup_null);

      /* Finally, allow write access (undoing it has already been scheduled
         and is idempotent). */
      SVN_ERR(svn_io_set_file_read_write(path, FALSE, scratch_pool));
    }

  return SVN_NO_ERROR;
}
Exemple #2
0
/* Remove file PATH, if it exists - even if it is read-only.
 * Use POOL for temporary allocations. */
static svn_error_t *
hotcopy_remove_file(const char *path,
                    apr_pool_t *pool)
{
  /* Make the rev file writable and remove it. */
  SVN_ERR(svn_io_set_file_read_write(path, TRUE, pool));
  SVN_ERR(svn_io_remove_file2(path, TRUE, pool));

  return SVN_NO_ERROR;
}
/* Write the format number and maximum number of files per directory
   to a new format file in PATH, overwriting a previously existing file.

   Use POOL for temporary allocation.

   This implementation is largely stolen from libsvn_fs_fs/fs_fs.c. */
static svn_error_t *
write_format(const char *path,
             int format,
             int max_files_per_dir,
             apr_pool_t *pool)
{
  const char *contents;

  path = svn_path_join(path, "format", pool);

  if (format >= SVN_FS_FS__MIN_LAYOUT_FORMAT_OPTION_FORMAT)
    {
      if (max_files_per_dir)
        contents = apr_psprintf(pool,
                                "%d\n"
                                "layout sharded %d\n",
                                format, max_files_per_dir);
      else
        contents = apr_psprintf(pool,
                                "%d\n"
                                "layout linear",
                                format);
    }
  else
    {
      contents = apr_psprintf(pool, "%d\n", format);
    }

    {
      const char *path_tmp;

      SVN_ERR(svn_io_write_unique(&path_tmp,
                                  svn_path_dirname(path, pool),
                                  contents, strlen(contents),
                                  svn_io_file_del_none, pool));

#ifdef WIN32
      /* make the destination writable, but only on Windows, because
         Windows does not let us replace read-only files. */
      SVN_ERR(svn_io_set_file_read_write(path, TRUE, pool));
#endif /* WIN32 */

      /* rename the temp file as the real destination */
      SVN_ERR(svn_io_file_rename(path_tmp, path, pool));
    }

  /* And set the perms to make it read only */
  return svn_io_set_file_read_only(path, FALSE, pool);
}
Exemple #4
0
static svn_error_t *
test_readonly(apr_pool_t *pool)
{
  const char *path;
  apr_finfo_t finfo;
  svn_boolean_t read_only;
  apr_int32_t wanted = APR_FINFO_SIZE | APR_FINFO_MTIME | APR_FINFO_TYPE
                        | APR_FINFO_LINK | APR_FINFO_PROT;


  SVN_ERR(svn_io_open_unique_file3(NULL, &path, NULL,
                                   svn_io_file_del_on_pool_cleanup,
                                   pool, pool));

  /* File should be writable */
  SVN_ERR(svn_io_stat(&finfo, path, wanted, pool));
  SVN_ERR(svn_io__is_finfo_read_only(&read_only, &finfo, pool));
  SVN_TEST_ASSERT(!read_only);

  /* Set read only */
  SVN_ERR(svn_io_set_file_read_only(path, FALSE, pool));

  /* File should be read only */
  SVN_ERR(svn_io_stat(&finfo, path, wanted, pool));
  SVN_ERR(svn_io__is_finfo_read_only(&read_only, &finfo, pool));
  SVN_TEST_ASSERT(read_only);

  /* Set writable */
  SVN_ERR(svn_io_set_file_read_write(path, FALSE, pool));

  /* File should be writable */
  SVN_ERR(svn_io_stat(&finfo, path, wanted, pool));
  SVN_ERR(svn_io__is_finfo_read_only(&read_only, &finfo, pool));
  SVN_TEST_ASSERT(!read_only);

  return SVN_NO_ERROR;
}
Exemple #5
0
svn_error_t *
svn_wc__sync_flags_with_props(svn_boolean_t *did_set,
                              svn_wc__db_t *db,
                              const char *local_abspath,
                              apr_pool_t *scratch_pool)
{
  svn_wc__db_status_t status;
  svn_node_kind_t kind;
  svn_wc__db_lock_t *lock;
  apr_hash_t *props = NULL;
  svn_boolean_t had_props;
  svn_boolean_t props_mod;

  if (did_set)
    *did_set = FALSE;

  /* ### We'll consolidate these info gathering statements in a future
         commit. */

  SVN_ERR(svn_wc__db_read_info(&status, &kind, NULL, NULL, NULL, NULL, NULL,
                               NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
                               NULL, &lock, NULL, NULL, NULL, NULL, NULL,
                               &had_props, &props_mod, NULL, NULL, NULL,
                               db, local_abspath,
                               scratch_pool, scratch_pool));

  /* We actually only care about the following flags on files, so just
     early-out for all other types.

     Also bail if there is no in-wc representation of the file. */
  if (kind != svn_node_file
      || (status != svn_wc__db_status_normal
          && status != svn_wc__db_status_added))
    return SVN_NO_ERROR;

  if (props_mod || had_props)
    SVN_ERR(svn_wc__db_read_props(&props, db, local_abspath, scratch_pool,
                                  scratch_pool));
  else
    props = NULL;

  /* If we get this far, we're going to change *something*, so just set
     the flag appropriately. */
  if (did_set)
    *did_set = TRUE;

  /* Handle the read-write bit. */
  if (status != svn_wc__db_status_normal
      || props == NULL
      || ! svn_hash_gets(props, SVN_PROP_NEEDS_LOCK)
      || lock)
    {
      SVN_ERR(svn_io_set_file_read_write(local_abspath, FALSE, scratch_pool));
    }
  else
    {
      /* Special case: If we have an uncommitted svn:needs-lock, we don't
         set the file read_only just yet.  That happens upon commit. */
      apr_hash_t *pristine_props;

      if (! props_mod)
        pristine_props = props;
      else if (had_props)
        SVN_ERR(svn_wc__db_read_pristine_props(&pristine_props, db, local_abspath,
                                                scratch_pool, scratch_pool));
      else
        pristine_props = NULL;

      if (pristine_props
            && svn_hash_gets(pristine_props, SVN_PROP_NEEDS_LOCK) )
            /*&& props
            && apr_hash_get(props, SVN_PROP_NEEDS_LOCK, APR_HASH_KEY_STRING) )*/
        SVN_ERR(svn_io_set_file_read_only(local_abspath, FALSE, scratch_pool));
    }

/* Windows doesn't care about the execute bit. */
#ifndef WIN32

  if (props == NULL
      || ! svn_hash_gets(props, SVN_PROP_EXECUTABLE))
    {
      /* Turn off the execute bit */
      SVN_ERR(svn_io_set_file_executable(local_abspath, FALSE, FALSE,
                                         scratch_pool));
    }
  else
    SVN_ERR(svn_io_set_file_executable(local_abspath, TRUE, FALSE,
                                       scratch_pool));
#endif

  return SVN_NO_ERROR;
}
Exemple #6
0
/* Perform a hotcopy, either normal or incremental.
 *
 * Normal hotcopy assumes that the destination exists as an empty
 * directory. It behaves like an incremental hotcopy except that
 * none of the copied files already exist in the destination.
 *
 * An incremental hotcopy copies only changed or new files to the destination,
 * and removes files from the destination no longer present in the source.
 * While the incremental hotcopy is running, readers should still be able
 * to access the destintation repository without error and should not see
 * revisions currently in progress of being copied. Readers are able to see
 * new fully copied revisions even if the entire incremental hotcopy procedure
 * has not yet completed.
 *
 * Writers are blocked out completely during the entire incremental hotcopy
 * process to ensure consistency. This function assumes that the repository
 * write-lock is held.
 */
static svn_error_t *
hotcopy_body(void *baton, apr_pool_t *pool)
{
  struct hotcopy_body_baton *hbb = baton;
  svn_fs_t *src_fs = hbb->src_fs;
  fs_fs_data_t *src_ffd = src_fs->fsap_data;
  svn_fs_t *dst_fs = hbb->dst_fs;
  fs_fs_data_t *dst_ffd = dst_fs->fsap_data;
  svn_boolean_t incremental = hbb->incremental;
  svn_fs_hotcopy_notify_t notify_func = hbb->notify_func;
  void* notify_baton = hbb->notify_baton;
  svn_cancel_func_t cancel_func = hbb->cancel_func;
  void* cancel_baton = hbb->cancel_baton;
  svn_revnum_t src_youngest;
  apr_uint64_t src_next_node_id;
  apr_uint64_t src_next_copy_id;
  svn_revnum_t dst_youngest;
  const char *src_revprops_dir;
  const char *dst_revprops_dir;
  const char *src_revs_dir;
  const char *dst_revs_dir;
  const char *src_subdir;
  const char *dst_subdir;
  svn_node_kind_t kind;

  /* Try to copy the config.
   *
   * ### We try copying the config file before doing anything else,
   * ### because higher layers will abort the hotcopy if we throw
   * ### an error from this function, and that renders the hotcopy
   * ### unusable anyway. */
  if (src_ffd->format >= SVN_FS_FS__MIN_CONFIG_FILE)
    {
      svn_error_t *err;

      err = svn_io_dir_file_copy(src_fs->path, dst_fs->path, PATH_CONFIG,
                                 pool);
      if (err)
        {
          if (APR_STATUS_IS_ENOENT(err->apr_err))
            {
              /* 1.6.0 to 1.6.11 did not copy the configuration file during
               * hotcopy. So if we're hotcopying a repository which has been
               * created as a hotcopy itself, it's possible that fsfs.conf
               * does not exist. Ask the user to re-create it.
               *
               * ### It would be nice to make this a non-fatal error,
               * ### but this function does not get an svn_fs_t object
               * ### so we have no way of just printing a warning via
               * ### the fs->warning() callback. */

              const char *src_abspath;
              const char *dst_abspath;
              const char *config_relpath;
              svn_error_t *err2;

              config_relpath = svn_dirent_join(src_fs->path, PATH_CONFIG, pool);
              err2 = svn_dirent_get_absolute(&src_abspath, src_fs->path, pool);
              if (err2)
                return svn_error_trace(svn_error_compose_create(err, err2));
              err2 = svn_dirent_get_absolute(&dst_abspath, dst_fs->path, pool);
              if (err2)
                return svn_error_trace(svn_error_compose_create(err, err2));

              /* ### hack: strip off the 'db/' directory from paths so
               * ### they make sense to the user */
              src_abspath = svn_dirent_dirname(src_abspath, pool);
              dst_abspath = svn_dirent_dirname(dst_abspath, pool);

              return svn_error_quick_wrapf(err,
                                 _("Failed to create hotcopy at '%s'. "
                                   "The file '%s' is missing from the source "
                                   "repository. Please create this file, for "
                                   "instance by running 'svnadmin upgrade %s'"),
                                 dst_abspath, config_relpath, src_abspath);
            }
          else
            return svn_error_trace(err);
        }
    }

  if (cancel_func)
    SVN_ERR(cancel_func(cancel_baton));

  /* Find the youngest revision in the source and destination.
   * We only support hotcopies from sources with an equal or greater amount
   * of revisions than the destination.
   * This also catches the case where users accidentally swap the
   * source and destination arguments. */
  SVN_ERR(svn_fs_fs__read_current(&src_youngest, &src_next_node_id,
                                  &src_next_copy_id, src_fs, pool));
  if (incremental)
    {
      SVN_ERR(svn_fs_fs__youngest_rev(&dst_youngest, dst_fs, pool));
      if (src_youngest < dst_youngest)
        return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
                 _("The hotcopy destination already contains more revisions "
                   "(%lu) than the hotcopy source contains (%lu); are source "
                   "and destination swapped?"),
                   dst_youngest, src_youngest);
    }
  else
    dst_youngest = 0;

  src_revs_dir = svn_dirent_join(src_fs->path, PATH_REVS_DIR, pool);
  dst_revs_dir = svn_dirent_join(dst_fs->path, PATH_REVS_DIR, pool);
  src_revprops_dir = svn_dirent_join(src_fs->path, PATH_REVPROPS_DIR, pool);
  dst_revprops_dir = svn_dirent_join(dst_fs->path, PATH_REVPROPS_DIR, pool);

  /* Ensure that the required folders exist in the destination
   * before actually copying the revisions and revprops. */
  SVN_ERR(svn_io_make_dir_recursively(dst_revs_dir, pool));
  SVN_ERR(svn_io_make_dir_recursively(dst_revprops_dir, pool));

  if (cancel_func)
    SVN_ERR(cancel_func(cancel_baton));

  /* Split the logic for new and old FS formats. The latter is much simpler
   * due to the absense of sharding and packing. However, it requires special
   * care when updating the 'current' file (which contains not just the
   * revision number, but also the next-ID counters). */
  if (src_ffd->format >= SVN_FS_FS__MIN_NO_GLOBAL_IDS_FORMAT)
    {
      SVN_ERR(hotcopy_revisions(src_fs, dst_fs, src_youngest, dst_youngest,
                                incremental, src_revs_dir, dst_revs_dir,
                                src_revprops_dir, dst_revprops_dir,
                                notify_func, notify_baton,
                                cancel_func, cancel_baton, pool));
      SVN_ERR(svn_fs_fs__write_current(dst_fs, src_youngest, 0, 0, pool));
    }
  else
    {
      SVN_ERR(hotcopy_revisions_old(src_fs, dst_fs, src_youngest,
                                    src_revs_dir, dst_revs_dir,
                                    src_revprops_dir, dst_revprops_dir,
                                    notify_func, notify_baton,
                                    cancel_func, cancel_baton, pool));
      SVN_ERR(svn_fs_fs__write_current(dst_fs, src_youngest, src_next_node_id,
                                       src_next_copy_id, pool));
    }

  /* Replace the locks tree.
   * This is racy in case readers are currently trying to list locks in
   * the destination. However, we need to get rid of stale locks.
   * This is the simplest way of doing this, so we accept this small race. */
  dst_subdir = svn_dirent_join(dst_fs->path, PATH_LOCKS_DIR, pool);
  SVN_ERR(svn_io_remove_dir2(dst_subdir, TRUE, cancel_func, cancel_baton,
                             pool));
  src_subdir = svn_dirent_join(src_fs->path, PATH_LOCKS_DIR, pool);
  SVN_ERR(svn_io_check_path(src_subdir, &kind, pool));
  if (kind == svn_node_dir)
    SVN_ERR(svn_io_copy_dir_recursively(src_subdir, dst_fs->path,
                                        PATH_LOCKS_DIR, TRUE,
                                        cancel_func, cancel_baton, pool));

  /* Now copy the node-origins cache tree. */
  src_subdir = svn_dirent_join(src_fs->path, PATH_NODE_ORIGINS_DIR, pool);
  SVN_ERR(svn_io_check_path(src_subdir, &kind, pool));
  if (kind == svn_node_dir)
    SVN_ERR(hotcopy_io_copy_dir_recursively(NULL, src_subdir, dst_fs->path,
                                            PATH_NODE_ORIGINS_DIR, TRUE,
                                            cancel_func, cancel_baton, pool));

  /*
   * NB: Data copied below is only read by writers, not readers.
   *     Writers are still locked out at this point.
   */

  if (dst_ffd->format >= SVN_FS_FS__MIN_REP_SHARING_FORMAT)
    {
      /* Copy the rep cache and then remove entries for revisions
       * that did not make it into the destination. */
      src_subdir = svn_dirent_join(src_fs->path, REP_CACHE_DB_NAME, pool);
      dst_subdir = svn_dirent_join(dst_fs->path, REP_CACHE_DB_NAME, pool);
      SVN_ERR(svn_io_check_path(src_subdir, &kind, pool));
      if (kind == svn_node_file)
        {
          SVN_ERR(svn_sqlite__hotcopy(src_subdir, dst_subdir, pool));

          /* The source might have r/o flags set on it - which would be
             carried over to the copy. */
          SVN_ERR(svn_io_set_file_read_write(dst_subdir, FALSE, pool));
          SVN_ERR(svn_fs_fs__del_rep_reference(dst_fs, src_youngest, pool));
        }
    }

  /* Copy the txn-current file. */
  if (dst_ffd->format >= SVN_FS_FS__MIN_TXN_CURRENT_FORMAT)
    SVN_ERR(svn_io_dir_file_copy(src_fs->path, dst_fs->path,
                                 PATH_TXN_CURRENT, pool));

  return SVN_NO_ERROR;
}
Exemple #7
0
/* Make a copy of the filesystem node (or tree if RECURSIVE) at
   SRC_ABSPATH under a temporary name in the directory
   TMPDIR_ABSPATH and return the absolute path of the copy in
   *DST_ABSPATH.  Return the node kind of SRC_ABSPATH in *KIND.  If
   SRC_ABSPATH doesn't exist then set *DST_ABSPATH to NULL to indicate
   that no copy was made. */
static svn_error_t *
copy_to_tmpdir(svn_skel_t **work_item,
               svn_node_kind_t *kind,
               svn_wc__db_t *db,
               const char *src_abspath,
               const char *dst_abspath,
               const char *tmpdir_abspath,
               svn_boolean_t file_copy,
               svn_boolean_t unversioned,
               svn_cancel_func_t cancel_func,
               void *cancel_baton,
               apr_pool_t *result_pool,
               apr_pool_t *scratch_pool)
{
  svn_boolean_t is_special;
  svn_io_file_del_t delete_when;
  const char *dst_tmp_abspath;
  svn_node_kind_t dsk_kind;
  if (!kind)
    kind = &dsk_kind;

  *work_item = NULL;

  SVN_ERR(svn_io_check_special_path(src_abspath, kind, &is_special,
                                    scratch_pool));
  if (*kind == svn_node_none)
    {
      return SVN_NO_ERROR;
    }
  else if (*kind == svn_node_unknown)
    {
      return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
                               _("Source '%s' is unexpected kind"),
                               svn_dirent_local_style(src_abspath,
                                                      scratch_pool));
    }
  else if (*kind == svn_node_dir || is_special)
    delete_when = svn_io_file_del_on_close;
  else /* the default case: (*kind == svn_node_file) */
    delete_when = svn_io_file_del_none;

  /* ### Do we need a pool cleanup to remove the copy?  We can't use
     ### svn_io_file_del_on_pool_cleanup above because a) it won't
     ### handle the directory case and b) we need to be able to remove
     ### the cleanup before queueing the move work item. */

  if (file_copy && !unversioned)
    {
      svn_boolean_t modified;
      /* It's faster to look for mods on the source now, as
         the timestamp might match, than to examine the
         destination later as the destination timestamp will
         never match. */
      SVN_ERR(svn_wc__internal_file_modified_p(&modified,
                                               db, src_abspath,
                                               FALSE, scratch_pool));
      if (!modified)
        {
          /* Why create a temp copy if we can just reinstall from pristine? */
          SVN_ERR(svn_wc__wq_build_file_install(work_item,
                                                db, dst_abspath, NULL, FALSE,
                                                TRUE,
                                                result_pool, scratch_pool));
          return SVN_NO_ERROR;
        }
    }

  /* Set DST_TMP_ABSPATH to a temporary unique path.  If *KIND is file, leave
     a file there and then overwrite it; otherwise leave no node on disk at
     that path.  In the latter case, something else might use that path
     before we get around to using it a moment later, but never mind. */
  SVN_ERR(svn_io_open_unique_file3(NULL, &dst_tmp_abspath, tmpdir_abspath,
                                   delete_when, scratch_pool, scratch_pool));

  if (*kind == svn_node_dir)
    {
      if (file_copy)
        SVN_ERR(svn_io_copy_dir_recursively(
                           src_abspath,
                           tmpdir_abspath,
                           svn_dirent_basename(dst_tmp_abspath, scratch_pool),
                           TRUE, /* copy_perms */
                           cancel_func, cancel_baton,
                           scratch_pool));
      else
        SVN_ERR(svn_io_dir_make(dst_tmp_abspath, APR_OS_DEFAULT, scratch_pool));
    }
  else if (!is_special)
    SVN_ERR(svn_io_copy_file(src_abspath, dst_tmp_abspath,
                             TRUE /* copy_perms */,
                             scratch_pool));
  else
    SVN_ERR(svn_io_copy_link(src_abspath, dst_tmp_abspath, scratch_pool));

  if (file_copy)
    {
      /* Remove 'read-only' from the destination file; it's a local add now. */
      SVN_ERR(svn_io_set_file_read_write(dst_tmp_abspath,
                                         FALSE, scratch_pool));
    }

  SVN_ERR(svn_wc__wq_build_file_move(work_item, db, dst_abspath,
                                     dst_tmp_abspath, dst_abspath,
                                     result_pool, scratch_pool));

  return SVN_NO_ERROR;
}