Daniel Shahaf wrote on Sat, Nov 13, 2010 at 14:08:12 +0200:
> Philip Martin wrote on Fri, Nov 12, 2010 at 11:02:14 +0000:
> > Daniel Shahaf <d...@daniel.shahaf.name> writes:
> > > 3. What's a reasonable strategy to hotcopy the locks?
> > >
> > >    I've started on a patch that uses walk_digest_files() (I modified
> > >    that function today), but currently that patch simply does a walk
> > >    starting at the digest file md5("/"), and consequently it misses
> > >    copying the unreferenced digest file md5("/trunk").
> > 
> > It appears you have to read/copy md5("/") and get a list of locks, then
> > read/copy each lock file that still exists, then reconstruct the
> > intermediate files for the locks that were copied.
> > 
> 
> So, for example:
> 
>     - read md5("/")
>       * read md5("/trunk/A/mu")
>         + compute md5("/trunk/A")
>         + compute md5("/trunk")
>       * read md5("/trunk/iota")
>         + compute md5("/trunk")
> 
> where the 'compute()' steps follow those of normal lock creation: read
> the digest file, update list of children, write it back.

Attached patch implements this.  Comments?

http://people.apache.org/~danielsh/hot.logmsg
http://people.apache.org/~danielsh/hot.diff

[[[
FSFS: teach 'hotcopy' the semantics of the locks/ directory.

Found by: philip

* notes/fsfs:
    Document another limitation of rsync-style backup, which 'hotcopy' lacks.

* subversion/libsvn_fs_fs/fs_fs.c
  (svn_fs_fs__hotcopy):
    Use svn_fs_fs__hotcopy_locks() instead of svn_io_copy_dir_recursively().

* subversion/libsvn_fs_fs/lock.c
  (svn_fs_fs__hotcopy_locks):  New declaration.

* subversion/libsvn_fs_fs/lock.c
  (digest_dir_path_from_digest):
    New, like svn_dirent_dirname(digest_path_from_digest()).
  (hotcopy_walker):  New callback.
  (svn_fs_fs__hotcopy_locks):  New definition.
]]]

[[[
Index: notes/fsfs
===================================================================
--- notes/fsfs  (revision 1034745)
+++ notes/fsfs  (working copy)
@@ -248,6 +248,11 @@ manually removed.  "svnadmin hotcopy" does not cop
 transactions from an FSFS repository, although that might need to
 change if Subversion starts making use of long-lived transactions.
 
+Naively copying an FSFS repository might also copy an inconsistent view
+of the locks directory, if locks are being added or removed as the copy
+operation is running.  "svnadmin hotcopy" copies and generates the locks
+digest files properly.
+
 So, if you are using standard backup tools to make backups of a FSFS
 repository, configure the software to copy the "current" file before
 the numbered revision files, if possible, and configure it not to copy
Index: subversion/libsvn_fs_fs/fs_fs.c
===================================================================
--- subversion/libsvn_fs_fs/fs_fs.c     (revision 1034745)
+++ subversion/libsvn_fs_fs/fs_fs.c     (working copy)
@@ -1767,9 +1767,7 @@ svn_fs_fs__hotcopy(const char *src_path,
   src_subdir = svn_dirent_join(src_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_path,
-                                        PATH_LOCKS_DIR, TRUE, NULL,
-                                        NULL, pool));
+    SVN_ERR(svn_fs_fs__hotcopy_locks(src_path, dst_path, pool));
 
   /* Now copy the node-origins cache tree. */
   src_subdir = svn_dirent_join(src_path, PATH_NODE_ORIGINS_DIR, pool);
Index: subversion/libsvn_fs_fs/lock.c
===================================================================
--- subversion/libsvn_fs_fs/lock.c      (revision 1034756)
+++ subversion/libsvn_fs_fs/lock.c      (working copy)
@@ -121,6 +121,19 @@ err_corrupt_lockfile(const char *fs_path, const ch
 
 /*** Digest file handling functions. ***/
 
+/* Return the path of the lock/entries directory for which DIGEST is the
+   hashed repository relative path. */
+static const char *
+digest_dir_path_from_digest(const char *fs_path,
+                            const char *digest,
+                            apr_pool_t *pool)
+{
+  return svn_dirent_join_many(pool, fs_path, PATH_LOCKS_DIR,
+                              apr_pstrmemdup(pool, digest, DIGEST_SUBDIR_LEN),
+                              NULL);
+}
+
+
 /* Return the path of the lock/entries file for which DIGEST is the
    hashed repository relative path. */
 static const char *
@@ -893,6 +906,65 @@ unlock_body(void *baton, apr_pool_t *pool)
 }
 
 
+/*** Hotcopying locks. ***/
+
+/* Implements walk_digests_callback_t. */
+static svn_error_t *
+hotcopy_walker(void *baton,
+               const char *fs_path,
+               const char *digest_path,
+               apr_hash_t *children /* ignored */,
+               svn_lock_t *lock /* ignored */,
+               svn_boolean_t have_write_lock,
+               apr_pool_t *pool)
+{
+  const char *dst_fs_path = baton;
+  const char *digest;
+  const char *src_digest_dir, *dst_digest_dir;
+  const char *perms_reference = digest_path;
+  
+  digest = svn_dirent_basename(digest_path, NULL);
+  src_digest_dir = svn_dirent_dirname(digest_path, pool);
+  dst_digest_dir = digest_dir_path_from_digest(dst_fs_path, digest, pool);
+
+  SVN_DBG(("Hotcopying fs='%s' dest='%s' digest='%s'\n", 
+           fs_path, dst_fs_path, digest));
+
+  /* Copy the digest file. */
+  SVN_ERR(svn_fs_fs__ensure_dir_exists(dst_digest_dir, dst_fs_path, pool));
+  SVN_ERR(svn_io_dir_file_copy(src_digest_dir, dst_digest_dir, digest, pool));
+
+  /* Generate the ancestor digest files (e.g., /trunk/A and /trunk, if we
+     locked /trunk/A/mu).  It will re-write DST_FS_PATH's digest_path, too.
+   */
+  if (lock)
+    SVN_ERR(set_lock(dst_fs_path, lock, perms_reference, pool));
+
+  return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_fs_fs__hotcopy_locks(const char *fs_path,
+                         const char *dst_fs_path,
+                         apr_pool_t *pool)
+{
+  /* md5("/") == 6666cd76f96956469e7be39d750cc7d9 */
+  const char *md5_root = make_digest("/", pool);
+
+  SVN_ERR(svn_fs_fs__ensure_dir_exists(
+            svn_dirent_join(dst_fs_path, PATH_LOCKS_DIR, pool),
+            dst_fs_path,
+            pool));
+
+  SVN_ERR(walk_digest_files(fs_path,
+                            digest_path_from_digest(fs_path, md5_root, pool),
+                            hotcopy_walker, (void *)dst_fs_path,
+                            FALSE /* have_write_lock */,
+                            pool));
+  return SVN_NO_ERROR;
+}
+
+
 /*** Public API implementations ***/
 
 svn_error_t *
Index: subversion/libsvn_fs_fs/lock.h
===================================================================
--- subversion/libsvn_fs_fs/lock.h      (revision 1034745)
+++ subversion/libsvn_fs_fs/lock.h      (working copy)
@@ -96,6 +96,12 @@ svn_error_t *svn_fs_fs__allow_locked_operation(con
                                                svn_boolean_t have_write_lock,
                                                apr_pool_t *pool);
 
+/* Copy the locks hierarchy from the live FS at FS_PATH to DST_SUBDIR. */
+svn_error_t *
+svn_fs_fs__hotcopy_locks(const char *fs_path,
+                         const char *dst_subdir,
+                         apr_pool_t *scratch_pool);
+
 #ifdef __cplusplus
 }
 #endif /* __cplusplus */
]]]

Reply via email to