vos-new-repsite-cleanup-20040601
[openafs.git] / src / volser / vsprocs.c
index 6837023..c1d0cca 100644 (file)
@@ -64,17 +64,8 @@ RCSID
 
 #include <volser_prototypes.h>
 
-afs_int32 VolumeExists(), CheckVldbRWBK(), CheckVldb();
-
 struct ubik_client *cstruct;
 int verbose = 0;
-extern int VL_GetNewVolumeId();
-extern int VL_SetLock();
-extern int VL_ReleaseLock();
-extern int VL_DeleteEntry();
-
-void MapNetworkToHost();
-void MapHostToNetwork();
 
 struct release {
     afs_int32 time;
@@ -424,6 +415,7 @@ UV_SetSecurity(register struct rx_securityClass *as, afs_int32 aindex)
 {
     uvindex = aindex;
     uvclass = as;
+    return 0;
 }
 
 /* bind to volser on <port> <aserver> */
@@ -540,9 +532,10 @@ SubEnumerateEntry(struct nvldbentry *entry)
            fprintf(STDOUT, "RO Site ");
        if (isMixed) {
            if (entry->serverFlags[i] & NEW_REPSITE)
-               fprintf(STDOUT, " -- New release");
+               fprintf(STDOUT," -- New release");
            else
-               fprintf(STDOUT, " -- Old release");
+               if (!(entry->serverFlags[i] & ITSRWVOL))
+                   fprintf(STDOUT," -- Old release");
        } else {
            if (entry->serverFlags[i] & RO_DONTUSE)
                fprintf(STDOUT, " -- Not released");
@@ -1022,12 +1015,15 @@ sigint_handler(int x)
 }
 
 /* Move volume <afromvol> on <afromserver> <afrompart> to <atoserver>
- * <atopart>. The operation is almost idempotent 
+ * <atopart>.  The operation is almost idempotent.  The following
+ * flags are recognized:
+ * 
+ *     RV_NOCLONE - don't use a copy clone
  */
 
 int
-UV_MoveVolume(afs_int32 afromvol, afs_int32 afromserver, afs_int32 afrompart,
-             afs_int32 atoserver, afs_int32 atopart)
+UV_MoveVolume2(afs_int32 afromvol, afs_int32 afromserver, afs_int32 afrompart,
+              afs_int32 atoserver, afs_int32 atopart, int flags)
 {
     struct rx_connection *toconn, *fromconn;
     afs_int32 fromtid, totid, clonetid;
@@ -1251,26 +1247,32 @@ UV_MoveVolume(afs_int32 afromvol, afs_int32 afromserver, afs_int32 afrompart,
           afromvol);
     VDONE;
 
-    /* Get a clone id */
-    VPRINT1("Allocating new volume id for clone of volume %u ...", afromvol);
-    newVol = 0;
-    vcode = ubik_Call(VL_GetNewVolumeId, cstruct, 0, 1, &newVol);
-    EGOTO1(mfail, vcode,
-          "Could not get an ID for the clone of volume %u from the VLDB\n",
-          afromvol);
-    VDONE;
+    if (!(flags & RV_NOCLONE)) {
+       /* Get a clone id */
+       VPRINT1("Allocating new volume id for clone of volume %u ...",
+               afromvol);
+       newVol = 0;
+       vcode = ubik_Call(VL_GetNewVolumeId, cstruct, 0, 1, &newVol);
+       EGOTO1(mfail, vcode,
+              "Could not get an ID for the clone of volume %u from the VLDB\n",
+              afromvol);
+       VDONE;
 
-    /* Do the clone. Default flags on clone are set to delete on salvage and out of service */
-    VPRINT1("Cloning source volume %u ...", afromvol);
-    strcpy(vname, "move-clone-temp");
-    code = AFSVolClone(fromconn, fromtid, 0, readonlyVolume, vname, &newVol);
-    EGOTO1(mfail, code, "Failed to clone the source volume %u\n", afromvol);
-    VDONE;
+       /* Do the clone. Default flags on clone are set to delete on salvage and out of service */
+       VPRINT1("Cloning source volume %u ...", afromvol);
+       strcpy(vname, "move-clone-temp");
+       code =
+           AFSVolClone(fromconn, fromtid, 0, readonlyVolume, vname, &newVol);
+       EGOTO1(mfail, code, "Failed to clone the source volume %u\n",
+              afromvol);
+       VDONE;
+    }
 
     /* lookup the name of the volume we just cloned */
     volid = afromvol;
     code = AFSVolGetName(fromconn, fromtid, &volName);
-    EGOTO1(mfail, code, "Failed to get the name of the volume %u\n", newVol);
+    EGOTO1(mfail, code, "Failed to get the name of the volume %u\n",
+          afromvol);
 
     VPRINT1("Ending the transaction on the source volume %u ...", afromvol);
     rcode = 0;
@@ -1287,27 +1289,39 @@ UV_MoveVolume(afs_int32 afromvol, afs_int32 afromserver, afs_int32 afrompart,
      * Create the destination volume
      * ***/
 
-    VPRINT1("Starting transaction on the cloned volume %u ...", newVol);
-    code =
-       AFSVolTransCreate(fromconn, newVol, afrompart, ITOffline, &clonetid);
-    EGOTO1(mfail, code,
-          "Failed to start a transaction on the cloned volume%u\n", newVol);
-    VDONE;
+    if (!(flags & RV_NOCLONE)) {
+       /* All of this is to get the fromDate */
+       VPRINT1("Starting transaction on the cloned volume %u ...", newVol);
+       code =
+           AFSVolTransCreate(fromconn, newVol, afrompart, ITOffline,
+                             &clonetid);
+       EGOTO1(mfail, code,
+              "Failed to start a transaction on the cloned volume%u\n",
+              newVol);
+       VDONE;
 
-    VPRINT1("Setting flags on cloned volume %u ...", newVol);
-    code = AFSVolSetFlags(fromconn, clonetid, VTDeleteOnSalvage | VTOutOfService);     /*redundant */
-    EGOTO1(mfail, code, "Could not set falgs on the cloned volume %u\n",
-          newVol);
-    VDONE;
+       VPRINT1("Setting flags on cloned volume %u ...", newVol);
+       code =
+           AFSVolSetFlags(fromconn, clonetid,
+                          VTDeleteOnSalvage | VTOutOfService); /*redundant */
+       EGOTO1(mfail, code, "Could not set flags on the cloned volume %u\n",
+              newVol);
+       VDONE;
 
-    /* remember time from which we've dumped the volume */
-    VPRINT1("Getting status of cloned volume %u ...", newVol);
-    code = AFSVolGetStatus(fromconn, clonetid, &tstatus);
-    EGOTO1(mfail, code, "Failed to get the status of the cloned volume %u\n",
-          newVol);
-    VDONE;
+       /* remember time from which we've dumped the volume */
+       VPRINT1("Getting status of cloned volume %u ...", newVol);
+       code = AFSVolGetStatus(fromconn, clonetid, &tstatus);
+       EGOTO1(mfail, code,
+              "Failed to get the status of the cloned volume %u\n",
+              newVol);
+       VDONE;
+
+       fromDate = tstatus.creationDate - CLOCKSKEW;
+    } else {
+       /* With RV_NOCLONE, just do a full copy from the source */
+       fromDate = 0;
+    }
 
-    fromDate = tstatus.creationDate - CLOCKSKEW;
 
 #ifdef ENABLE_BUGFIX_1165
     /*
@@ -1382,25 +1396,31 @@ UV_MoveVolume(afs_int32 afromvol, afs_int32 afromserver, afs_int32 afrompart,
     destination.destPort = AFSCONF_VOLUMEPORT;
     destination.destSSID = 1;
 
-    /* Copy the clone to the new volume */
-    VPRINT2("Dumping from clone %u on source to volume %u on destination ...",
-           newVol, afromvol);
     strncpy(cookie.name, tmpName, VOLSER_OLDMAXVOLNAME);
     cookie.type = RWVOL;
     cookie.parent = entry.volumeId[RWVOL];
     cookie.clone = 0;
-    code = AFSVolForward(fromconn, clonetid, 0, &destination, totid, &cookie);
-    EGOTO1(mfail, code, "Failed to move data for the volume %u\n", volid);
-    VDONE;
 
-    VPRINT1("Ending transaction on cloned volume %u ...", newVol);
-    code = AFSVolEndTrans(fromconn, clonetid, &rcode);
-    if (!code)
-       code = rcode;
-    clonetid = 0;
-    EGOTO1(mfail, code,
-          "Failed to end the transaction on the cloned volume %u\n", newVol);
-    VDONE;
+    if (!(flags & RV_NOCLONE)) {
+       /* Copy the clone to the new volume */
+       VPRINT2("Dumping from clone %u on source to volume %u on destination ...",
+               newVol, afromvol);
+       code =
+           AFSVolForward(fromconn, clonetid, 0, &destination, totid,
+                         &cookie);
+       EGOTO1(mfail, code, "Failed to move data for the volume %u\n", volid);
+       VDONE;
+
+       VPRINT1("Ending transaction on cloned volume %u ...", newVol);
+       code = AFSVolEndTrans(fromconn, clonetid, &rcode);
+       if (!code)
+           code = rcode;
+       clonetid = 0;
+       EGOTO1(mfail, code,
+              "Failed to end the transaction on the cloned volume %u\n",
+              newVol);
+       VDONE;
+    }
 
     /* ***
      * reattach to the main-line volume, and incrementally dump it.
@@ -1414,14 +1434,16 @@ UV_MoveVolume(afs_int32 afromvol, afs_int32 afromserver, afs_int32 afrompart,
     VDONE;
 
     /* now do the incremental */
-    VPRINT1
-       ("Doing the incremental dump from source to destination for volume %u ... ",
+    VPRINT2
+       ("Doing the%s dump from source to destination for volume %u ... ",
+        (flags & RV_NOCLONE) ? "" : " incremental",
         afromvol);
     code =
        AFSVolForward(fromconn, fromtid, fromDate, &destination, totid,
                      &cookie);
-    EGOTO(mfail, code,
-         "Failed to do the incremental dump from rw volume on old site to rw volume on newsite\n");
+    EGOTO1(mfail, code,
+          "Failed to do the%s dump from rw volume on old site to rw volume on newsite\n",
+         (flags & RV_NOCLONE) ? "" : " incremental");
     VDONE;
 
     /* now adjust the flags so that the new volume becomes official */
@@ -1590,27 +1612,33 @@ UV_MoveVolume(afs_int32 afromvol, afs_int32 afromserver, afs_int32 afrompart,
        code = 0;               /* no backup volume? that's okay */
 
     fromtid = 0;
-    VPRINT1("Starting transaction on the cloned volume %u ...", newVol);
-    code =
-       AFSVolTransCreate(fromconn, newVol, afrompart, ITOffline, &clonetid);
-    EGOTO1(mfail, code,
-          "Failed to start a transaction on the cloned volume%u\n", newVol);
-    VDONE;
+    if (!(flags & RV_NOCLONE)) {
+       VPRINT1("Starting transaction on the cloned volume %u ...", newVol);
+       code =
+           AFSVolTransCreate(fromconn, newVol, afrompart, ITOffline,
+                             &clonetid);
+       EGOTO1(mfail, code,
+              "Failed to start a transaction on the cloned volume%u\n",
+              newVol);
+       VDONE;
 
-    /* now delete the clone */
-    VPRINT1("Deleting the cloned volume %u ...", newVol);
-    code = AFSVolDeleteVolume(fromconn, clonetid);
-    EGOTO1(mfail, code, "Failed to delete the cloned volume %u\n", newVol);
-    VDONE;
+       /* now delete the clone */
+       VPRINT1("Deleting the cloned volume %u ...", newVol);
+       code = AFSVolDeleteVolume(fromconn, clonetid);
+       EGOTO1(mfail, code, "Failed to delete the cloned volume %u\n",
+              newVol);
+       VDONE;
 
-    VPRINT1("Ending transaction on cloned volume %u ...", newVol);
-    code = AFSVolEndTrans(fromconn, clonetid, &rcode);
-    if (!code)
-       code = rcode;
-    clonetid = 0;
-    EGOTO1(mfail, code,
-          "Failed to end the transaction on the cloned volume %u\n", newVol);
-    VDONE;
+       VPRINT1("Ending transaction on cloned volume %u ...", newVol);
+       code = AFSVolEndTrans(fromconn, clonetid, &rcode);
+       if (!code)
+           code = rcode;
+       clonetid = 0;
+       EGOTO1(mfail, code,
+              "Failed to end the transaction on the cloned volume %u\n",
+              newVol);
+       VDONE;
+    }
 
     /* fall through */
     /* END OF MOVE */
@@ -1660,8 +1688,8 @@ UV_MoveVolume(afs_int32 afromvol, afs_int32 afromserver, afs_int32 afrompart,
        if (code || rcode) {
            VPRINT("\n");
            fprintf(STDERR,
-                   "Could not end transaction on the source's clone volume %lu\n",
-                   (unsigned long)newVol);
+                   "Could not end transaction on the source volume %lu\n",
+                   (unsigned long)afromvol);
            if (!error)
                error = (code ? code : rcode);
        }
@@ -1956,31 +1984,45 @@ UV_MoveVolume(afs_int32 afromvol, afs_int32 afromserver, afs_int32 afrompart,
 }
 
 
-/* Move volume <afromvol> on <afromserver> <afrompart> to <atoserver>
- * <atopart>. The operation is almost idempotent 
- */
+int
+UV_MoveVolume(afs_int32 afromvol, afs_int32 afromserver, afs_int32 afrompart,
+             afs_int32 atoserver, afs_int32 atopart)
+{
+    return UV_MoveVolume2(afromvol, afromserver, afrompart,
+                         atoserver, atopart, 0);
+}
 
+
+/* Copy volume <afromvol> from <afromserver> <afrompart> to <atoserver>
+ * <atopart>.  The new volume is named by <atovolname>.  The new volume
+ * has ID <atovolid> if that is nonzero; otherwise a new ID is allocated
+ * from the VLDB.  the following flags are supported:
+ * 
+ *     RV_RDONLY  - target volume is RO
+ *     RV_OFFLINE - leave target volume offline
+ *     RV_CPINCR  - do incremental dump if target exists
+ *     RV_NOVLDB  - don't create/update VLDB entry
+ *     RV_NOCLONE - don't use a copy clone
+ */
 int
-UV_CopyVolume(afs_int32 afromvol, afs_int32 afromserver, afs_int32 afrompart,
-             char *atovolname, afs_int32 atoserver, afs_int32 atopart)
+UV_CopyVolume2(afs_int32 afromvol, afs_int32 afromserver, afs_int32 afrompart,
+              char *atovolname, afs_int32 atoserver, afs_int32 atopart,
+              afs_int32 atovolid, int flags)
 {
     struct rx_connection *toconn, *fromconn;
     afs_int32 fromtid, totid, clonetid;
     char vname[64];
-    char tmpName[VOLSER_MAXVOLNAME + 1];
     afs_int32 rcode;
-    afs_int32 fromDate;
+    afs_int32 fromDate, cloneFromDate;
     struct restoreCookie cookie;
     register afs_int32 vcode, code;
-    afs_int32 cloneVol, newVol;
+    afs_int32 cloneVol, newVol, volflag;
     struct volser_status tstatus;
     struct destServer destination;
 
     struct nvldbentry entry, newentry, storeEntry;
-    int i, islocked, pntg;
+    int islocked, pntg;
     afs_int32 error;
-    char in, lf;               /* for test code */
-    int same;
     int justclone = 0;
 
     islocked = 0;
@@ -2019,80 +2061,121 @@ UV_CopyVolume(afs_int32 afromvol, afs_int32 afromserver, afs_int32 afrompart,
      * clone the read/write volume locally.
      * ***/
 
-    VPRINT1("Starting transaction on source volume %u ...", afromvol);
-    code = AFSVolTransCreate(fromconn, afromvol, afrompart, ITBusy, &fromtid);
-    EGOTO1(mfail, code, "Failed to create transaction on the volume %u\n",
-          afromvol);
-    VDONE;
+    cloneVol = 0;
+    if (!(flags & RV_NOCLONE)) {
+       VPRINT1("Starting transaction on source volume %u ...", afromvol);
+       code = AFSVolTransCreate(fromconn, afromvol, afrompart, ITBusy,
+                                &fromtid);
+       EGOTO1(mfail, code, "Failed to create transaction on the volume %u\n",
+              afromvol);
+       VDONE;
 
-    /* Get a clone id */
-    VPRINT1("Allocating new volume id for clone of volume %u ...", afromvol);
-    newVol = 0;
-    vcode = ubik_Call(VL_GetNewVolumeId, cstruct, 0, 1, &cloneVol);
-    EGOTO1(mfail, vcode,
+       /* Get a clone id */
+       VPRINT1("Allocating new volume id for clone of volume %u ...",
+               afromvol);
+       cloneVol = 0;
+       vcode = ubik_Call(VL_GetNewVolumeId, cstruct, 0, 1, &cloneVol);
+       EGOTO1(mfail, vcode,
           "Could not get an ID for the clone of volume %u from the VLDB\n",
           afromvol);
-    VDONE;
+       VDONE;
+    }
 
-    /* Get a new volume id */
-    VPRINT1("Allocating new volume id for copy of volume %u ...", afromvol);
-    newVol = 0;
-    vcode = ubik_Call(VL_GetNewVolumeId, cstruct, 0, 1, &newVol);
-    EGOTO1(mfail, vcode,
-          "Could not get an ID for the copy of volume %u from the VLDB\n",
-          afromvol);
-    VDONE;
+    if (atovolid) {
+       newVol = atovolid;
+    } else {
+       /* Get a new volume id */
+       VPRINT1("Allocating new volume id for copy of volume %u ...", afromvol);
+       newVol = 0;
+       vcode = ubik_Call(VL_GetNewVolumeId, cstruct, 0, 1, &newVol);
+       EGOTO1(mfail, vcode,
+              "Could not get an ID for the copy of volume %u from the VLDB\n",
+              afromvol);
+       VDONE;
+    }
 
-    /* Do the clone. Default flags on clone are set to delete on salvage and out of service */
-    VPRINT1("Cloning source volume %u ...", afromvol);
-    strcpy(vname, "copy-clone-temp");
-    code =
-       AFSVolClone(fromconn, fromtid, 0, readonlyVolume, vname, &cloneVol);
-    EGOTO1(mfail, code, "Failed to clone the source volume %u\n", afromvol);
-    VDONE;
+    if (!(flags & RV_NOCLONE)) {
+       /* Do the clone. Default flags on clone are set to delete on salvage and out of service */
+       VPRINT1("Cloning source volume %u ...", afromvol);
+       strcpy(vname, "copy-clone-temp");
+       code =
+           AFSVolClone(fromconn, fromtid, 0, readonlyVolume, vname,
+                       &cloneVol);
+       EGOTO1(mfail, code, "Failed to clone the source volume %u\n",
+              afromvol);
+       VDONE;
 
-    VPRINT1("Ending the transaction on the source volume %u ...", afromvol);
-    rcode = 0;
-    code = AFSVolEndTrans(fromconn, fromtid, &rcode);
-    fromtid = 0;
-    if (!code)
-       code = rcode;
-    EGOTO1(mfail, code,
-          "Failed to end the transaction on the source volume %u\n",
-          afromvol);
-    VDONE;
+       VPRINT1("Ending the transaction on the source volume %u ...", afromvol);
+       rcode = 0;
+       code = AFSVolEndTrans(fromconn, fromtid, &rcode);
+       fromtid = 0;
+       if (!code)
+           code = rcode;
+       EGOTO1(mfail, code,
+              "Failed to end the transaction on the source volume %u\n",
+              afromvol);
+       VDONE;
+    }
 
     /* ***
      * Create the destination volume
      * ***/
 
-    VPRINT1("Starting transaction on the cloned volume %u ...", cloneVol);
-    code =
-       AFSVolTransCreate(fromconn, cloneVol, afrompart, ITOffline,
+    if (!(flags & RV_NOCLONE)) {
+       VPRINT1("Starting transaction on the cloned volume %u ...", cloneVol);
+       code =
+           AFSVolTransCreate(fromconn, cloneVol, afrompart, ITOffline,
                          &clonetid);
-    EGOTO1(mfail, code,
-          "Failed to start a transaction on the cloned volume%u\n",
-          cloneVol);
-    VDONE;
+       EGOTO1(mfail, code,
+              "Failed to start a transaction on the cloned volume%u\n",
+              cloneVol);
+       VDONE;
 
-    VPRINT1("Setting flags on cloned volume %u ...", cloneVol);
-    code = AFSVolSetFlags(fromconn, clonetid, VTDeleteOnSalvage | VTOutOfService);     /*redundant */
-    EGOTO1(mfail, code, "Could not set falgs on the cloned volume %u\n",
-          cloneVol);
-    VDONE;
+       VPRINT1("Setting flags on cloned volume %u ...", cloneVol);
+       code =
+           AFSVolSetFlags(fromconn, clonetid,
+                          VTDeleteOnSalvage | VTOutOfService); /*redundant */
+       EGOTO1(mfail, code, "Could not set flags on the cloned volume %u\n",
+              cloneVol);
+       VDONE;
 
-    /* remember time from which we've dumped the volume */
-    VPRINT1("Getting status of cloned volume %u ...", cloneVol);
-    code = AFSVolGetStatus(fromconn, clonetid, &tstatus);
-    EGOTO1(mfail, code, "Failed to get the status of the cloned volume %u\n",
-          cloneVol);
-    VDONE;
+       /* remember time from which we've dumped the volume */
+       VPRINT1("Getting status of cloned volume %u ...", cloneVol);
+       code = AFSVolGetStatus(fromconn, clonetid, &tstatus);
+       EGOTO1(mfail, code,
+              "Failed to get the status of the cloned volume %u\n",
+              cloneVol);
+       VDONE;
 
-    fromDate = tstatus.creationDate - CLOCKSKEW;
+       fromDate = tstatus.creationDate - CLOCKSKEW;
+    } else {
+       fromDate = 0;
+    }
 
     /* create a volume on the target machine */
+    cloneFromDate = 0;
     code = AFSVolTransCreate(toconn, newVol, atopart, ITOffline, &totid);
     if (!code) {
+       if ((flags & RV_CPINCR)) {
+           VPRINT1("Getting status of pre-existing volume %u ...", newVol);
+           code = AFSVolGetStatus(toconn, totid, &tstatus);
+           EGOTO1(mfail, code,
+                  "Failed to get the status of the pre-existing volume %u\n",
+                  newVol);
+           VDONE;
+
+           /* Using the update date should be OK here, but add some fudge */
+           cloneFromDate = tstatus.updateDate - CLOCKSKEW;
+           if ((flags & RV_NOCLONE))
+               fromDate = cloneFromDate;
+
+           /* XXX We should check that the source volume's creationDate is
+            * XXX not newer than the existing target volume, and if not,
+            * XXX throw away the existing target and do a full dump. */
+
+           goto cpincr;
+       }
+
        /* Delete the existing volume.
         * While we are deleting the volume in these steps, the transaction
         * we started against the cloned volume (clonetid above) will be
@@ -2120,14 +2203,13 @@ UV_CopyVolume(afs_int32 afromvol, afs_int32 afromserver, afs_int32 afrompart,
 
     VPRINT1("Creating the destination volume %u ...", newVol);
     code =
-       AFSVolCreateVolume(toconn, atopart, atovolname, volser_RW, newVol,
-                          &newVol, &totid);
+       AFSVolCreateVolume(toconn, atopart, atovolname,
+                          (flags & RV_RDONLY) ? volser_RO : volser_RW,
+                          newVol, &newVol, &totid);
     EGOTO1(mfail, code, "Failed to create the destination volume %u\n",
           newVol);
     VDONE;
 
-    strncpy(tmpName, atovolname, VOLSER_OLDMAXVOLNAME);
-
     VPRINT1("Setting volume flags on destination volume %u ...", newVol);
     code =
        AFSVolSetFlags(toconn, totid, (VTDeleteOnSalvage | VTOutOfService));
@@ -2135,38 +2217,46 @@ UV_CopyVolume(afs_int32 afromvol, afs_int32 afromserver, afs_int32 afrompart,
           "Failed to set the flags on the destination volume %u\n", newVol);
     VDONE;
 
-    /***
-     * Now dump the clone to the new volume
-     ***/
+cpincr:
 
     destination.destHost = ntohl(atoserver);
     destination.destPort = AFSCONF_VOLUMEPORT;
     destination.destSSID = 1;
 
+    strncpy(cookie.name, atovolname, VOLSER_OLDMAXVOLNAME);
+    cookie.type = (flags & RV_RDONLY) ? ROVOL : RWVOL;
+    cookie.parent = 0;
+    cookie.clone = 0;
+
+    /***
+     * Now dump the clone to the new volume
+     ***/
 
-/* probably should have some code here that checks to see if we are copying to same server
-and partition - if so, just use a clone to save disk space */
+    if (!(flags & RV_NOCLONE)) {
+       /* XXX probably should have some code here that checks to see if
+        * XXX we are copying to same server and partition - if so, just
+        * XXX use a clone to save disk space */
 
-    /* Copy the clone to the new volume */
-    VPRINT2("Dumping from clone %u on source to volume %u on destination ...",
+       /* Copy the clone to the new volume */
+       VPRINT2("Dumping from clone %u on source to volume %u on destination ...",
            cloneVol, newVol);
-    strncpy(cookie.name, tmpName, VOLSER_OLDMAXVOLNAME);
-    cookie.type = RWVOL;
-    cookie.parent = 0;
-    cookie.clone = 0;
-    code = AFSVolForward(fromconn, clonetid, 0, &destination, totid, &cookie);
-    EGOTO1(mfail, code, "Failed to move data for the volume %u\n", newVol);
-    VDONE;
+       code =
+           AFSVolForward(fromconn, clonetid, cloneFromDate, &destination,
+                         totid, &cookie);
+       EGOTO1(mfail, code, "Failed to move data for the volume %u\n",
+              newVol);
+       VDONE;
 
-    VPRINT1("Ending transaction on cloned volume %u ...", cloneVol);
-    code = AFSVolEndTrans(fromconn, clonetid, &rcode);
-    if (!code)
-       code = rcode;
-    clonetid = 0;
-    EGOTO1(mfail, code,
-          "Failed to end the transaction on the cloned volume %u\n",
-          cloneVol);
-    VDONE;
+       VPRINT1("Ending transaction on cloned volume %u ...", cloneVol);
+       code = AFSVolEndTrans(fromconn, clonetid, &rcode);
+       if (!code)
+           code = rcode;
+       clonetid = 0;
+       EGOTO1(mfail, code,
+              "Failed to end the transaction on the cloned volume %u\n",
+              cloneVol);
+       VDONE;
+    }
 
     /* ***
      * reattach to the main-line volume, and incrementally dump it.
@@ -2180,18 +2270,21 @@ and partition - if so, just use a clone to save disk space */
     VDONE;
 
     /* now do the incremental */
-    VPRINT1
-       ("Doing the incremental dump from source to destination for volume %u ... ",
+    VPRINT2
+       ("Doing the%s dump from source to destination for volume %u ... ",
+        (flags & RV_NOCLONE) ? "" : " incremental",
         afromvol);
     code =
        AFSVolForward(fromconn, fromtid, fromDate, &destination, totid,
                      &cookie);
-    EGOTO(mfail, code,
-         "Failed to do the incremental dump from rw volume on old site to rw volume on newsite\n");
+    EGOTO1(mfail, code,
+          "Failed to do the%s dump from old site to new site\n",
+          afromvol);
     VDONE;
 
     VPRINT1("Setting volume flags on destination volume %u ...", newVol);
-    code = AFSVolSetFlags(toconn, totid, 0);
+    volflag = ((flags & RV_OFFLINE) ? VTOutOfService : 0);     /* off or on-line */
+    code = AFSVolSetFlags(toconn, totid, volflag);
     EGOTO(mfail, code,
          "Failed to set the flags to make destination volume online\n");
     VDONE;
@@ -2218,59 +2311,65 @@ and partition - if so, just use a clone to save disk space */
     VDONE;
 
     fromtid = 0;
-    VPRINT1("Starting transaction on the cloned volume %u ...", cloneVol);
-    code =
-       AFSVolTransCreate(fromconn, cloneVol, afrompart, ITOffline,
-                         &clonetid);
-    EGOTO1(mfail, code,
-          "Failed to start a transaction on the cloned volume%u\n",
-          cloneVol);
-    VDONE;
 
-    /* now delete the clone */
-    VPRINT1("Deleting the cloned volume %u ...", cloneVol);
-    code = AFSVolDeleteVolume(fromconn, clonetid);
-    EGOTO1(mfail, code, "Failed to delete the cloned volume %u\n", cloneVol);
-    VDONE;
+    if (!(flags & RV_NOCLONE)) {
+       VPRINT1("Starting transaction on the cloned volume %u ...", cloneVol);
+       code =
+           AFSVolTransCreate(fromconn, cloneVol, afrompart, ITOffline,
+                             &clonetid);
+       EGOTO1(mfail, code,
+              "Failed to start a transaction on the cloned volume%u\n",
+              cloneVol);
+       VDONE;
 
-    VPRINT1("Ending transaction on cloned volume %u ...", cloneVol);
-    code = AFSVolEndTrans(fromconn, clonetid, &rcode);
-    if (!code)
-       code = rcode;
-    clonetid = 0;
-    EGOTO1(mfail, code,
-          "Failed to end the transaction on the cloned volume %u\n",
-          cloneVol);
-    VDONE;
+       /* now delete the clone */
+       VPRINT1("Deleting the cloned volume %u ...", cloneVol);
+       code = AFSVolDeleteVolume(fromconn, clonetid);
+       EGOTO1(mfail, code, "Failed to delete the cloned volume %u\n",
+              cloneVol);
+       VDONE;
 
-    /* create the vldb entry for the copied volume */
-    strncpy(newentry.name, atovolname, VOLSER_OLDMAXVOLNAME);
-    newentry.nServers = 1;
-    newentry.serverNumber[0] = atoserver;
-    newentry.serverPartition[0] = atopart;
-    newentry.flags = RW_EXISTS;        /* this records that rw volume exists */
-    newentry.serverFlags[0] = ITSRWVOL;        /*this rep site has rw vol */
-    newentry.volumeId[RWVOL] = newVol;
-    newentry.volumeId[ROVOL] = 0;
-    newentry.volumeId[BACKVOL] = 0;
-    newentry.cloneId = 0;
-    /*map into right byte order, before passing to xdr, the stuff has to be in host
-     * byte order. Xdr converts it into network order */
-    MapNetworkToHost(&newentry, &storeEntry);
-    /* create the vldb entry */
-    vcode = VLDB_CreateEntry(&storeEntry);
-    if (vcode) {
-       fprintf(STDERR,
-               "Could not create a VLDB entry for the volume %s %lu\n",
-               atovolname, (unsigned long)newVol);
-       /*destroy the created volume */
-       VPRINT1("Deleting the newly created volume %u\n", newVol);
-       AFSVolDeleteVolume(toconn, totid);
-       error = vcode;
-       goto mfail;
+       VPRINT1("Ending transaction on cloned volume %u ...", cloneVol);
+       code = AFSVolEndTrans(fromconn, clonetid, &rcode);
+       if (!code)
+           code = rcode;
+       clonetid = 0;
+       EGOTO1(mfail, code,
+              "Failed to end the transaction on the cloned volume %u\n",
+              cloneVol);
+       VDONE;
+    }
+
+    if (!(flags & RV_NOVLDB)) {
+       /* create the vldb entry for the copied volume */
+       strncpy(newentry.name, atovolname, VOLSER_OLDMAXVOLNAME);
+       newentry.nServers = 1;
+       newentry.serverNumber[0] = atoserver;
+       newentry.serverPartition[0] = atopart;
+       newentry.flags = (flags & RV_RDONLY) ? RO_EXISTS : RW_EXISTS;
+       newentry.serverFlags[0] = (flags & RV_RDONLY) ? ITSROVOL : ITSRWVOL;
+       newentry.volumeId[RWVOL] = newVol;
+       newentry.volumeId[ROVOL] = (flags & RV_RDONLY) ? newVol : 0;
+       newentry.volumeId[BACKVOL] = 0;
+       newentry.cloneId = 0;
+       /*map into right byte order, before passing to xdr, the stuff has to be in host
+        * byte order. Xdr converts it into network order */
+       MapNetworkToHost(&newentry, &storeEntry);
+       /* create the vldb entry */
+       vcode = VLDB_CreateEntry(&storeEntry);
+       if (vcode) {
+           fprintf(STDERR,
+                   "Could not create a VLDB entry for the volume %s %lu\n",
+                   atovolname, (unsigned long)newVol);
+           /*destroy the created volume */
+           VPRINT1("Deleting the newly created volume %u\n", newVol);
+           AFSVolDeleteVolume(toconn, totid);
+           error = vcode;
+           goto mfail;
+       }
+       VPRINT2("Created the VLDB entry for the volume %s %u\n", atovolname,
+               newVol);
     }
-    VPRINT2("Created the VLDB entry for the volume %s %u\n", atovolname,
-           newVol);
 
     /* normal cleanup code */
 
@@ -2399,6 +2498,13 @@ and partition - if so, just use a clone to save disk space */
 }
 
 
+int
+UV_CopyVolume(afs_int32 afromvol, afs_int32 afromserver, afs_int32 afrompart,
+             char *atovolname, afs_int32 atoserver, afs_int32 atopart)
+{
+    return UV_CopyVolume2(afromvol, afromserver, afrompart,
+                          atovolname, atoserver, atopart, 0, 0);
+}
 
 
 
@@ -2657,6 +2763,196 @@ UV_BackupVolume(afs_int32 aserver, afs_int32 apart, afs_int32 avolid)
     return error;
 }
 
+/* Make a new clone of volume <avolid> on <aserver> and <apart> 
+ * using volume ID <acloneid>, or a new ID allocated from the VLDB.
+ * The new volume is named by <aname>, or by appending ".clone" to
+ * the existing name if <aname> is NULL.  The following flags are
+ * supported:
+ * 
+ *     RV_RDONLY  - target volume is RO
+ *     RV_OFFLINE - leave target volume offline
+ */
+
+int
+UV_CloneVolume(afs_int32 aserver, afs_int32 apart, afs_int32 avolid,
+              afs_int32 acloneid, char *aname, int flags)
+{
+    struct rx_connection *aconn = (struct rx_connection *)0;
+    afs_int32 ttid = 0, btid = 0;
+    afs_int32 code = 0, rcode = 0;
+    char vname[VOLSER_MAXVOLNAME + 1];
+    afs_int32 error = 0;
+    int backexists = 1;
+    volEntries volumeInfo;
+
+    aconn = UV_Bind(aserver, AFSCONF_VOLUMEPORT);
+
+    if (!aname) {
+       volumeInfo.volEntries_val = (volintInfo *) 0;
+       volumeInfo.volEntries_len = 0;
+       code = AFSVolListOneVolume(aconn, apart, avolid, &volumeInfo);
+       if (code) {
+           fprintf(stderr, "Could not get info for volume %lu\n",
+                   (unsigned long)avolid);
+           error = code;
+           goto bfail;
+       }
+       strncpy(vname, volumeInfo.volEntries_val[0].name,
+               VOLSER_OLDMAXVOLNAME - 7);
+       vname[VOLSER_OLDMAXVOLNAME - 7] = 0;
+       strcat(vname, ".clone");
+       aname = vname;
+       if (volumeInfo.volEntries_val)
+           free(volumeInfo.volEntries_val);
+    }
+
+    if (!acloneid) {
+       /* Get a clone id */
+       VPRINT1("Allocating new volume id for clone of volume %u ...",
+               avolid);
+       code = ubik_Call(VL_GetNewVolumeId, cstruct, 0, 1, &acloneid);
+       EGOTO1(bfail, code,
+          "Could not get an ID for the clone of volume %u from the VLDB\n",
+          avolid);
+       VDONE;
+    }
+
+    /* Test to see if the clone volume exists by trying to create
+     * a transaction on the clone volume. We've assumed the clone exists.
+     */
+    /* XXX I wonder what happens if the clone has some other parent... */
+    code = AFSVolTransCreate(aconn, acloneid, apart, ITOffline, &btid);
+    if (code) {
+       if (code != VNOVOL) {
+           fprintf(STDERR, "Could not reach the clone volume %lu\n",
+                   (unsigned long)acloneid);
+           error = code;
+           goto bfail;
+       }
+       backexists = 0;         /* backup volume does not exist */
+    }
+    if (btid) {
+       code = AFSVolEndTrans(aconn, btid, &rcode);
+       btid = 0;
+       if (code || rcode) {
+           fprintf(STDERR,
+                   "Could not end transaction on the previous clone volume %lu\n",
+                   (unsigned long)acloneid);
+           error = (code ? code : rcode);
+           goto bfail;
+       }
+    }
+
+    /* Now go ahead and try to clone the RW volume.
+     * First start a transaction on the RW volume 
+     */
+    code = AFSVolTransCreate(aconn, avolid, apart, ITBusy, &ttid);
+    if (code) {
+       fprintf(STDERR, "Could not start a transaction on the volume %lu\n",
+               (unsigned long)avolid);
+       error = code;
+       goto bfail;
+    }
+
+    /* Clone or reclone the volume, depending on whether the backup 
+     * volume exists or not
+     */
+    if (backexists) {
+       VPRINT1("Re-cloning clone volume %u ...", acloneid);
+
+       code = AFSVolReClone(aconn, ttid, acloneid);
+       if (code) {
+           fprintf(STDERR, "Could not re-clone backup volume %lu\n",
+                   (unsigned long)acloneid);
+           error = code;
+           goto bfail;
+       }
+    } else {
+       VPRINT1("Creating a new clone %u ...", acloneid);
+
+       code = AFSVolClone(aconn, ttid, 0,
+                          (flags & RV_RDONLY) ? readonlyVolume : backupVolume,
+                          aname, &acloneid);
+       if (code) {
+           fprintf(STDERR, "Failed to clone the volume %lu\n",
+                   (unsigned long)avolid);
+           error = code;
+           goto bfail;
+       }
+    }
+
+    /* End the transaction on the RW volume */
+    code = AFSVolEndTrans(aconn, ttid, &rcode);
+    ttid = 0;
+    if (code || rcode) {
+       fprintf(STDERR,
+               "Failed to end the transaction on the rw volume %lu\n",
+               (unsigned long)avolid);
+       error = (code ? code : rcode);
+       goto bfail;
+    }
+
+    /* Now go back to the backup volume and bring it on line */
+    if (!(flags & RV_OFFLINE)) {
+       code = AFSVolTransCreate(aconn, acloneid, apart, ITOffline, &btid);
+       if (code) {
+           fprintf(STDERR,
+                   "Failed to start a transaction on the clone volume %lu\n",
+                   (unsigned long)acloneid);
+           error = code;
+           goto bfail;
+       }
+
+       code = AFSVolSetFlags(aconn, btid, 0);
+       if (code) {
+           fprintf(STDERR, "Could not mark the clone volume %lu on line \n",
+                   (unsigned long)acloneid);
+           error = code;
+           goto bfail;
+       }
+
+       code = AFSVolEndTrans(aconn, btid, &rcode);
+       btid = 0;
+       if (code || rcode) {
+           fprintf(STDERR,
+                   "Failed to end the transaction on the clone volume %lu\n",
+                   (unsigned long)acloneid);
+           error = (code ? code : rcode);
+           goto bfail;
+       }
+    }
+
+    VDONE;
+
+  bfail:
+    if (ttid) {
+       code = AFSVolEndTrans(aconn, ttid, &rcode);
+       if (code || rcode) {
+           fprintf(STDERR, "Could not end transaction on the volume %lu\n",
+                   (unsigned long)avolid);
+           if (!error)
+               error = (code ? code : rcode);
+       }
+    }
+
+    if (btid) {
+       code = AFSVolEndTrans(aconn, btid, &rcode);
+       if (code || rcode) {
+           fprintf(STDERR,
+                   "Could not end transaction on the clone volume %lu\n",
+                   (unsigned long)acloneid);
+           if (!error)
+               error = (code ? code : rcode);
+       }
+    }
+
+    if (aconn)
+       rx_DestroyConnection(aconn);
+
+    PrintError("", error);
+    return error;
+}
+
 static int
 DelVol(struct rx_connection *conn, afs_int32 vid, afs_int32 part,
        afs_int32 flags)
@@ -2855,7 +3151,7 @@ UV_ReleaseVolume(afs_int32 afromvol, afs_int32 afromserver,
     manyDests tr;
     manyResults results;
     int rwindex, roindex, roclone, roexists;
-    afs_int32 rwcrdate;
+    afs_int32 rwcrdate, clcrdate;
     struct rtime {
        int validtime;
        afs_uint32 time;
@@ -2918,11 +3214,13 @@ UV_ReleaseVolume(afs_int32 afromvol, afs_int32 afromserver,
     }
 
     /* Will we be completing a previously unfinished release. -force overrides */
-    for (fullrelease = 1, i = 0; (fullrelease && (i < entry.nServers)); i++) {
-       if (entry.serverFlags[i] & NEW_REPSITE)
-           fullrelease = 0;
+    for (s = 0, m = 0, fullrelease=0, i=0; (i<entry.nServers); i++) {
+       if (entry.serverFlags[i] & ITSROVOL) {
+           m++;
+           if (entry.serverFlags[i] & NEW_REPSITE) s++;
+       }
     }
-    if (forceflag && !fullrelease)
+    if ((forceflag && !fullrelease) || (s == m) || (s == 0))
        fullrelease = 1;
 
     /* Determine which volume id to use and see if it exists */
@@ -2931,23 +3229,75 @@ UV_ReleaseVolume(afs_int32 afromvol, afs_int32 afromserver,
          || (entry.cloneId == 0)) ? entry.volumeId[ROVOL] : entry.cloneId);
     code = VolumeExists(afromserver, afrompart, cloneVolId);
     roexists = ((code == ENODEV) ? 0 : 1);
-    if (!roexists && !fullrelease)
-       fullrelease = 1;        /* Do a full release if RO clone does not exist */
-
-    if (verbose) {
-       if (fullrelease) {
-           fprintf(STDOUT, "This is a complete release of the volume %lu\n",
-                   (unsigned long)afromvol);
-       } else {
-           fprintf(STDOUT, "This is a completion of the previous release\n");
-       }
-    }
 
     fromconn = UV_Bind(afromserver, AFSCONF_VOLUMEPORT);
     if (!fromconn)
        ONERROR(-1, afromserver,
                "Cannot establish connection with server 0x%x\n");
 
+    if (!fullrelease) {
+       if (!roexists)
+           fullrelease = 1;    /* Do a full release if RO clone does not exist */
+       else {
+           /* Begin transaction on RW and mark it busy while we query it */
+           code = AFSVolTransCreate(
+                       fromconn, afromvol, afrompart, ITBusy, &fromtid
+                  );
+           ONERROR(code, afromvol,
+                   "Failed to start transaction on RW volume %u\n");
+
+           /* Query the creation date for the RW */
+           code = AFSVolGetStatus(fromconn, fromtid, &volstatus);
+           ONERROR(code, afromvol,
+                   "Failed to get the status of RW volume %u\n");
+           rwcrdate = volstatus.creationDate;
+
+           /* End transaction on RW */
+           code = AFSVolEndTrans(fromconn, fromtid, &rcode);
+           fromtid = 0;
+           ONERROR((code ? code : rcode), afromvol,
+                   "Failed to end transaction on RW volume %u\n");
+
+           /* Begin transaction on clone and mark it busy while we query it */
+           code = AFSVolTransCreate(
+                       fromconn, cloneVolId, afrompart, ITBusy, &clonetid
+                  );
+           ONERROR(code, cloneVolId,
+                   "Failed to start transaction on RW clone %u\n");
+
+           /* Query the creation date for the clone */
+           code = AFSVolGetStatus(fromconn, clonetid, &volstatus);
+           ONERROR(code, cloneVolId,
+                   "Failed to get the status of RW clone %u\n");
+           clcrdate = volstatus.creationDate;
+
+           /* End transaction on RW */
+           code = AFSVolEndTrans(fromconn, clonetid, &rcode);
+           clonetid = 0;
+           ONERROR((code ? code : rcode), cloneVolId,
+                   "Failed to end transaction on RW volume %u\n");
+
+           if (rwcrdate > clcrdate)
+               fullrelease = 2;/* Do a full release if RO clone older than RW */
+       }
+    }
+
+    if (verbose) {
+       switch (fullrelease) {
+           case 2:
+               fprintf(STDOUT, "RW %lu changed, doing a complete release\n",
+                       (unsigned long)afromvol);
+               break;
+           case 1:
+               fprintf(STDOUT, "This is a complete release of volume %lu\n",
+                       (unsigned long)afromvol);
+               break;
+           case 0:
+               fprintf(STDOUT, "This is a completion of a previous release\n");
+               break;
+       }
+    }
+
     if (fullrelease) {
        /* If the RO clone exists, then if the clone is a temporary
         * clone, delete it. Or if the RO clone is marked RO_DONTUSE
@@ -3408,7 +3758,7 @@ UV_ReleaseVolume(afs_int32 afromvol, afs_int32 afromserver,
            replicas[i].trans = 0;
            if (code) {
                fprintf(STDERR,
-                       "Failed to end transaction on ro volume %u at server 0x%x\n",
+                       "Failed to end transaction on ro volume %u at server %s\n",
                        entry.volumeId[ROVOL],
                        hostutil_GetNameByINet(htonl
                                               (replicas[i].server.
@@ -3800,11 +4150,18 @@ UV_RestoreVolume(afs_int32 toserver, afs_int32 topart, afs_int32 tovolid,
                AFSVolCreateVolume(toconn, topart, tovolname, volsertype, 0,
                                   &pvolid, &totid);
            EGOTO1(refail, code, "Could not create new volume %u\n", pvolid);
+
+           newDate = 0;
        } else {
            code =
                AFSVolTransCreate(toconn, pvolid, topart, ITOffline, &totid);
            EGOTO1(refail, code, "Failed to start transaction on %u\n",
                   pvolid);
+
+           code = AFSVolGetStatus(toconn, totid, &tstatus);
+           EGOTO1(refail, code, "Could not get timestamp from volume %u\n",
+                  pvolid);
+           newDate = tstatus.creationDate;
        }
     }
     cookie.parent = pvolid;
@@ -3847,7 +4204,8 @@ UV_RestoreVolume(afs_int32 toserver, afs_int32 topart, afs_int32 tovolid,
        error = code;
        goto refail;
     }
-    newDate = time(0);
+    if (!newDate)
+       newDate = time(0);
     code = AFSVolSetDate(toconn, totid, newDate);
     if (code) {
        fprintf(STDERR, "Could not set the date on %lu\n",
@@ -4413,7 +4771,8 @@ UV_ListPartitions(afs_int32 aserver, struct partList *ptrPartList,
        }
        free(partEnts.partEntries_val);
     }
-  out:
+
+   /* out: */
     if (code)
        fprintf(STDERR,
                "Could not fetch the list of partitions from the server\n");