⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 repos.c

📁 linux subdivision ying gai ke yi le ba
💻 C
📖 第 1 页 / 共 3 页
字号:
      APR_EOL_STR
      APR_EOL_STR
      "Visit http://subversion.tigris.org/ for more information."
      APR_EOL_STR;

    SVN_ERR_W (svn_io_file_create (readme_file_name, readme_contents, pool),
               "Creating readme file");
  }

  /* Write the top-level FORMAT file. */
  SVN_ERR (svn_io_write_version_file 
           (svn_path_join (path, SVN_REPOS__FORMAT, pool),
            SVN_REPOS__VERSION, pool));

  return SVN_NO_ERROR;
}


svn_error_t *
svn_repos_create (svn_repos_t **repos_p,
                  const char *path,
                  const char *unused_1,
                  const char *unused_2,
                  apr_hash_t *config,
                  apr_hash_t *fs_config,
                  apr_pool_t *pool)
{
  svn_repos_t *repos;

  /* Allocate a repository object. */
  repos = apr_pcalloc (pool, sizeof (*repos));

  /* Initialize the repository paths. */
  init_repos_dirs (repos, path, pool);

  /* Create the various files and subdirectories for the repository. */
  SVN_ERR_W (create_repos_structure (repos, path, pool),
             "Repository creation failed");
  
  /* Create a Berkeley DB environment for the filesystem. */
  SVN_ERR (svn_fs_create (&repos->fs, repos->db_path, fs_config, pool));

  *repos_p = repos;
  return SVN_NO_ERROR;
}


/* Check if @a path is the root of a repository by checking if the
 * path contains the expected files and directories.  Return TRUE
 * on errors (which would be permission errors, probably) so that
 * we the user will see them after we try to open the repository
 * for real.  */
static svn_boolean_t
check_repos_path (const char *path,
                  apr_pool_t *pool)
{
  svn_node_kind_t kind;
  svn_error_t *err;

  err = svn_io_check_path (svn_path_join (path, SVN_REPOS__FORMAT, pool),
                           &kind, pool);
  if (err)
    {
      svn_error_clear (err);
      return TRUE;
    }
  if (kind != svn_node_file)
    return FALSE;

  /* Check the db/ subdir, but allow it to be a symlink (Subversion
     works just fine if it's a symlink). */
  err = svn_io_check_resolved_path
    (svn_path_join (path, SVN_REPOS__DB_DIR, pool), &kind, pool);
  if (err)
    {
      svn_error_clear (err);
      return TRUE;
    }
  if (kind != svn_node_dir)
    return FALSE;

  return TRUE;
}


/* Verify that the repository's 'format' file is a suitable version. */
static svn_error_t *
check_repos_version (const char *path,
                     apr_pool_t *pool)
{
  int version;
  const char *format_path;

  format_path = svn_path_join (path, SVN_REPOS__FORMAT, pool);
  SVN_ERR (svn_io_read_version_file (&version, format_path, pool));

  if (version != SVN_REPOS__VERSION)
    return svn_error_createf 
      (SVN_ERR_REPOS_UNSUPPORTED_VERSION, NULL,
       "Expected version '%d' of repository; found version '%d'", 
       SVN_REPOS__VERSION, version);

  return SVN_NO_ERROR;
}


/* Set *REPOS_P to a repository at PATH which has been opened with
   some kind of lock.  LOCKTYPE is one of APR_FLOCK_SHARED (for
   standard readers/writers), or APR_FLOCK_EXCLUSIVE (for processes
   that need exclusive access, like db_recover.)  OPEN_FS indicates
   whether the database should be opened and placed into repos->fs.

   Do all allocation in POOL.  When POOL is destroyed, the lock will
   be released as well. */
static svn_error_t *
get_repos (svn_repos_t **repos_p,
           const char *path,
           svn_boolean_t exclusive,
           svn_boolean_t nonblocking,
           svn_boolean_t open_fs,
           apr_pool_t *pool)
{
  svn_repos_t *repos;

  /* Verify the validity of our repository format. */
  SVN_ERR (check_repos_version (path, pool));

  /* Allocate a repository object. */
  repos = apr_pcalloc (pool, sizeof (*repos));

  /* Initialize the repository paths. */
  init_repos_dirs (repos, path, pool);

  /* Locking. */
  {
    const char *lockfile_path;
    svn_error_t *err;

    /* Get a filehandle for the repository's db lockfile. */
    lockfile_path = svn_repos_db_lockfile (repos, pool);

    err = svn_io_file_lock2 (lockfile_path, exclusive, nonblocking, pool);
    if (err != NULL && APR_STATUS_IS_EAGAIN (err->apr_err))
      return err;
    SVN_ERR_W (err, "Error opening db lockfile");
  }

  /* Open up the Berkeley filesystem only after obtaining the lock. */
  if (open_fs)
    SVN_ERR (svn_fs_open (&repos->fs, repos->db_path, NULL, pool));

  *repos_p = repos;
  return SVN_NO_ERROR;
}



const char *
svn_repos_find_root_path (const char *path,
                          apr_pool_t *pool)
{
  const char *candidate = path;

  while (1)
    {
      if (check_repos_path (candidate, pool))
        break;
      if (candidate[0] == '\0' || strcmp(candidate, "/") == 0)
        return NULL;
      candidate = svn_path_dirname (candidate, pool);
    }

  return candidate;
}


svn_error_t *
svn_repos_open (svn_repos_t **repos_p,
                const char *path,
                apr_pool_t *pool)
{
  /* Fetch a repository object initialized with a shared read/write
     lock on the database. */

  SVN_ERR (get_repos (repos_p, path, FALSE, FALSE, TRUE, pool));

  return SVN_NO_ERROR;
}


svn_error_t *
svn_repos_delete (const char *path, 
                  apr_pool_t *pool)
{
  const char *db_path = svn_path_join (path, SVN_REPOS__DB_DIR, pool);

  /* Delete the Berkeley environment... */
  SVN_ERR (svn_fs_delete_fs (db_path, pool));

  /* ...then blow away everything else.  */
  SVN_ERR (svn_io_remove_dir (path, pool));

  return SVN_NO_ERROR;
}


svn_fs_t *
svn_repos_fs (svn_repos_t *repos)
{
  if (! repos)
    return NULL;
  return repos->fs;
}


/* This code uses repository locking, which is motivated by the
 * need to support DB_RUN_RECOVERY.  Here's how it works:
 *
 * Every accessor of a repository's database takes out a shared lock
 * on the repository -- both readers and writers get shared locks, and
 * there can be an unlimited number of shared locks simultaneously.
 *
 * Sometimes, a db access returns the error DB_RUN_RECOVERY.  When
 * this happens, we need to run svn_fs_berkeley_recover() on the db
 * with no other accessors present.  So we take out an exclusive lock
 * on the repository.  From the moment we request the exclusive lock,
 * no more shared locks are granted, and when the last shared lock
 * disappears, the exclusive lock is granted.  As soon as we get it,
 * we can run recovery.
 *
 * We assume that once any berkeley call returns DB_RUN_RECOVERY, they
 * all do, until recovery is run.
 */

svn_error_t *
svn_repos_recover2 (const char *path,
                    svn_boolean_t nonblocking,
                    svn_error_t *(*start_callback) (void *baton),
                    void *start_callback_baton,
                    apr_pool_t *pool)
{
  svn_repos_t *repos;
  apr_pool_t *subpool = svn_pool_create (pool);

  /* Fetch a repository object initialized with an EXCLUSIVE lock on
     the database.   This will at least prevent others from trying to
     read or write to it while we run recovery. */
  SVN_ERR (get_repos (&repos, path, TRUE, nonblocking,
                      FALSE,    /* don't try to open the db yet. */
                      subpool));

  if (start_callback)
    SVN_ERR (start_callback (start_callback_baton));

  /* Recover the database to a consistent state. */
  SVN_ERR (svn_fs_berkeley_recover (repos->db_path, subpool));

  /* Close shop and free the subpool, to release the exclusive lock. */
  svn_pool_destroy (subpool);

  return SVN_NO_ERROR;
}


svn_error_t *
svn_repos_recover (const char *path,
                   apr_pool_t *pool)
{
    return svn_repos_recover2 (path, FALSE, NULL, NULL, pool);
}

svn_error_t *svn_repos_db_logfiles (apr_array_header_t **logfiles,
                                    const char *path,
                                    svn_boolean_t only_unused,
                                    apr_pool_t *pool)
{
  svn_repos_t *repos;
  int i;

  SVN_ERR (get_repos (&repos, path,
                      FALSE, FALSE,
                      FALSE,     /* Do not open fs. */
                      pool));

  SVN_ERR (svn_fs_berkeley_logfiles (logfiles,
                                    svn_repos_db_env (repos, pool),
                                    only_unused,
                                    pool));

  /* Loop, printing log files. */
  for (i = 0; i < (*logfiles)->nelts; i++)
    {
      const char ** log_file = &(APR_ARRAY_IDX (*logfiles, i, const char *));
      *log_file = svn_path_join(SVN_REPOS__DB_DIR, *log_file, pool);
    }

  return SVN_NO_ERROR;
}

/** Hot copy structure copy context.
 */
struct hotcopy_ctx_t {
  const char *dest;     /* target location to construct */
  unsigned int src_len; /* len of the source path*/
};

/** Called by (svn_io_dir_walk).
 * Copies the repository structure with exception of
 * @c SVN_REPOS__DB_DIR and @c SVN_REPOS__LOCK_DIR.
 * Those directories are handled separetly.
 * @a baton is a pointer to (struct hotcopy_ctx_t) specifying
 * destination path to copy to and the length of the source path.
 *  
 * @copydoc svn_io_dir_walk()
 */
static svn_error_t *hotcopy_structure (void *baton,
                                       const char *path,
                                       const apr_finfo_t *finfo,
                                       apr_pool_t *pool)
{
  const struct hotcopy_ctx_t *ctx = ((struct hotcopy_ctx_t *) baton);
  const char *sub_path;
  const char *target;

  if (strlen (path) == ctx->src_len)
    {
      sub_path = "";
    } 
  else
    {
      sub_path = &path[ctx->src_len+1];

      /* Check if we are inside db directory and if so skip it */
      if (svn_path_compare_paths(
            svn_path_get_longest_ancestor (SVN_REPOS__DB_DIR, sub_path, pool), 
            SVN_REPOS__DB_DIR) == 0)
        return SVN_NO_ERROR;

      if (svn_path_compare_paths(
            svn_path_get_longest_ancestor (SVN_REPOS__LOCK_DIR, 
                                           sub_path, pool),
            SVN_REPOS__LOCK_DIR) == 0)
        return SVN_NO_ERROR;
    }

  target = svn_path_join (ctx->dest, sub_path, pool);

  if (finfo->filetype == APR_DIR)
    {
      SVN_ERR (create_repos_dir (target, pool));
    } 
  else if (finfo->filetype == APR_REG)
    {
    
      SVN_ERR(svn_io_copy_file(path, target, TRUE, pool));
    }

  return SVN_NO_ERROR;
}


/** Obtain a lock on db logs lock file. Create one if it does not exist.
 */
static svn_error_t *
lock_db_logs_file (svn_repos_t *repos,
                   svn_boolean_t exclusive,
                   apr_pool_t *pool)
{
  const char * lock_file = svn_repos_db_logs_lockfile (repos, pool);

  /* Try to create a lock file, in case if it is missing. As in case of the
     repositories created before hotcopy functionality.  */
  svn_error_clear (create_db_logs_lock (repos, pool));

  SVN_ERR (svn_io_file_lock2 (lock_file, exclusive, FALSE, pool));

  return SVN_NO_ERROR;
}


/* Make a copy of a repository with hot backup of fs. */
svn_error_t *
svn_repos_hotcopy (const char *src_path,
                   const char *dst_path,
                   svn_boolean_t clean_logs,
                   apr_pool_t *pool)
{
  svn_repos_t *src_repos;
  svn_repos_t *dst_repos;
  struct hotcopy_ctx_t hotcopy_context;
  
  /* Try to open original repository */
  SVN_ERR (get_repos (&src_repos, src_path,
                      FALSE, FALSE,
                      FALSE,    /* don't try to open the db yet. */
                      pool));

  /* If we are going to clean logs, then get an exclusive lock on
     db-logs.lock, to ensure that no one else will work with logs.

     If we are just copying, then get a shared lock to ensure that 
     no one else will clean logs while we copying them */
  
  SVN_ERR (lock_db_logs_file (src_repos, clean_logs, pool));

  /* Copy the repository to a new path, with exception of 
     specially handled directories */

  hotcopy_context.dest = dst_path;
  hotcopy_context.src_len = strlen (src_path);
  SVN_ERR (svn_io_dir_walk (src_path,
                            0,
                            hotcopy_structure,
                            &hotcopy_context,
                            pool));

  /* Prepare dst_repos object so that we may create locks,
     so that we may open repository */

  dst_repos = apr_pcalloc (pool, sizeof (*dst_repos));

  init_repos_dirs (dst_repos, dst_path, pool);

  SVN_ERR (create_locks (dst_repos, pool));

  SVN_ERR (svn_io_dir_make_sgid (dst_repos->db_path, APR_OS_DEFAULT, pool));

  /* Open repository, since before we only initialized the directories. 
     Above is a work around because lock creation functions expect a
     pointer to (svn_repos_t) with initialized paths. */

  /* Exclusively lock the new repository.  
     No one should be accessing it at the moment */ 
  SVN_ERR (get_repos (&dst_repos, dst_path,
                      TRUE, FALSE,
                      FALSE,    /* don't try to open the db yet. */
                      pool));


  SVN_ERR (svn_fs_hotcopy (src_repos->db_path, dst_repos->db_path,
                           clean_logs, pool));

  return SVN_NO_ERROR;
}

/* Return the library version number. */
const svn_version_t *
svn_repos_version (void)
{
  SVN_VERSION_BODY;
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -