volser-restore-timestamp-cleanup-20040728
[openafs.git] / src / volser / vos.c
index 55093b4..5c4d4e2 100644 (file)
@@ -23,6 +23,7 @@ RCSID
 #include <sys/file.h>
 #include <netdb.h>
 #include <netinet/in.h>
+#include <arpa/inet.h>
 #endif
 #include <sys/stat.h>
 #ifdef AFS_AIX_ENV
@@ -76,9 +77,6 @@ struct tqHead {
     struct tqElem *next;
 };
 
-
-struct hostent *hostutil_GetHostByName(register char *ahost);
-
 #define COMMONPARMS     cmd_Seek(ts, 12);\
 cmd_AddParm(ts, "-cell", CMD_SINGLE, CMD_OPTIONAL, "cell name");\
 cmd_AddParm(ts, "-noauth", CMD_FLAG, CMD_OPTIONAL, "don't authenticate");\
@@ -94,29 +92,11 @@ struct rx_connection *tconn;
 afs_int32 tserver;
 extern struct ubik_client *cstruct;
 const char *confdir;
-extern struct rx_connection *UV_Bind();
-extern int UV_SetSecurity();
-extern int UV_SetVolumeInfo();
-extern int vsu_SetCrypt();
-extern VL_SetLock();
-extern VL_ReleaseLock();
-extern VL_DeleteEntry();
-extern VL_ListEntry();
-extern VL_GetAddrs();
-extern VL_GetAddrsU();
-extern VL_ChangeAddr();
-
-extern int vsu_ExtractName();
-extern PrintError();
-extern void EnumerateEntry();
-extern void SubEnumerateEntry();
-
 
 static struct tqHead busyHead, notokHead;
 
 static void
-qInit(ahead)
-     struct tqHead *ahead;
+qInit(struct tqHead *ahead)
 {
     memset((char *)ahead, 0, sizeof(struct tqHead));
     return;
@@ -124,9 +104,7 @@ qInit(ahead)
 
 
 static void
-qPut(ahead, volid)
-     struct tqHead *ahead;
-     afs_int32 volid;
+qPut(struct tqHead *ahead, afs_int32 volid)
 {
     struct tqElem *elem;
 
@@ -139,9 +117,7 @@ qPut(ahead, volid)
 }
 
 static void
-qGet(ahead, volid)
-     struct tqHead *ahead;
-     afs_int32 *volid;
+qGet(struct tqHead *ahead, afs_int32 *volid)
 {
     struct tqElem *tmp;
 
@@ -156,9 +132,8 @@ qGet(ahead, volid)
 }
 
 /* returns 1 if <filename> exists else 0 */
-static
-FileExists(filename)
-     char *filename;
+static int
+FileExists(char *filename)
 {
     usd_handle_t ufd;
     int code;
@@ -177,9 +152,8 @@ FileExists(filename)
 }
 
 /* returns 1 if <name> doesnot end in .readonly or .backup, else 0 */
-static
-VolNameOK(name)
-     char *name;
+static int
+VolNameOK(char *name)
 {
     int total;
 
@@ -195,9 +169,8 @@ VolNameOK(name)
 }
 
 /* return 1 if name is a number else 0 */
-static
-IsNumeric(name)
-     char *name;
+static int
+IsNumeric(char *name)
 {
     int result, len, i;
     char *ptr;
@@ -214,8 +187,6 @@ IsNumeric(name)
 
     }
     return result;
-
-
 }
 
 
@@ -223,8 +194,7 @@ IsNumeric(name)
  * Parse a server name/address and return the address in HOST BYTE order
  */
 afs_int32
-GetServer(aname)
-     char *aname;
+GetServer(char *aname)
 {
     register struct hostent *th;
     afs_int32 addr;
@@ -257,8 +227,7 @@ GetServer(aname)
 }
 
 afs_int32
-GetVolumeType(aname)
-     char *aname;
+GetVolumeType(char *aname)
 {
 
     if (!strcmp(aname, "ro"))
@@ -272,15 +241,11 @@ GetVolumeType(aname)
 }
 
 int
-IsPartValid(partId, server, code)
-     afs_int32 server, partId, *code;
-
+IsPartValid(afs_int32 partId, afs_int32 server, afs_int32 *code)
 {
     struct partList dummyPartList;
     int i, success, cnt;
 
-
-
     success = 0;
     *code = 0;
 
@@ -299,10 +264,8 @@ IsPartValid(partId, server, code)
 
  /*sends the contents of file associated with <fd> and <blksize>  to Rx Stream 
   * associated  with <call> */
-SendFile(ufd, call, blksize)
-     usd_handle_t ufd;
-     register struct rx_call *call;
-     long blksize;
+int 
+SendFile(usd_handle_t ufd, register struct rx_call *call, long blksize)
 {
     char *buffer = (char *)0;
     afs_int32 error = 0;
@@ -345,9 +308,7 @@ SendFile(ufd, call, blksize)
 /* function invoked by UV_RestoreVolume, reads the data from rx_trx_stream and
  * writes it out to the volume. */
 afs_int32
-WriteData(call, rock)
-     struct rx_call *call;
-     char *rock;
+WriteData(struct rx_call *call, char *rock)
 {
     char *filename;
     usd_handle_t ufd;
@@ -396,10 +357,7 @@ WriteData(call, rock)
  * with <fd> <blksize>
  */
 int
-ReceiveFile(ufd, call, blksize)
-     usd_handle_t ufd;
-     struct rx_call *call;
-     long blksize;
+ReceiveFile(usd_handle_t ufd, struct rx_call *call, long blksize)
 {
     char *buffer = NULL;
     afs_int32 bytesread;
@@ -437,9 +395,7 @@ ReceiveFile(ufd, call, blksize)
 }
 
 afs_int32
-DumpFunction(call, filename)
-     struct rx_call *call;
-     char *filename;
+DumpFunction(struct rx_call *call, char *filename)
 {
     usd_handle_t ufd;          /* default is to stdout */
     afs_int32 error = 0, code;
@@ -543,12 +499,8 @@ DisplayFormat(pntr, server, part, totalOK, totalNotOK, totalBusy, fast,
                fprintf(STDOUT, "    Last Access %s",
                        ctime((time_t *) & pntr->accessDate));
 #endif
-           if (pntr->updateDate < pntr->creationDate)
-               fprintf(STDOUT, "    Last Update %s",
-                       ctime((time_t *) & pntr->creationDate));
-           else
-               fprintf(STDOUT, "    Last Update %s",
-                       ctime((time_t *) & pntr->updateDate));
+           fprintf(STDOUT, "    Last Update %s",
+                   ctime((time_t *) & pntr->updateDate));
            fprintf(STDOUT,
                    "    %d accesses in the past day (i.e., vnode references)\n",
                    pntr->dayUse);
@@ -700,12 +652,8 @@ XDisplayFormat(a_xInfoP, a_servID, a_partID, a_totalOKP, a_totalNotOKP,
                fprintf(STDOUT, "    Last Access %s",
                        ctime((time_t *) & a_xInfoP->accessDate));
 #endif
-           if (a_xInfoP->updateDate < a_xInfoP->creationDate)
-               fprintf(STDOUT, "    Last Update %s",
-                       ctime((time_t *) & a_xInfoP->creationDate));
-           else
-               fprintf(STDOUT, "    Last Update %s",
-                       ctime((time_t *) & a_xInfoP->updateDate));
+           fprintf(STDOUT, "    Last Update %s",
+                   ctime((time_t *) & a_xInfoP->updateDate));
            fprintf(STDOUT,
                    "    %d accesses in the past day (i.e., vnode references)\n",
                    a_xInfoP->dayUse);
@@ -858,6 +806,8 @@ DisplayFormat2(server, partition, pntr)
     if (partition != partition_cache) {
        MapPartIdIntoName(partition, pname);
        partition_cache = partition;
+    } else {
+        pname[0] = '\0';
     }
     fprintf(STDOUT, "name\t\t%s\n", pntr->name);
     fprintf(STDOUT, "id\t\t%lu\n", pntr->volid);
@@ -1174,7 +1124,6 @@ VolumeStats(pntr, entry, server, part, voltype)
      afs_int32 server, part;
 {
     int totalOK, totalNotOK, totalBusy;
-    afs_int32 vcode, vcode2;
 
     DisplayFormat(pntr, server, part, &totalOK, &totalNotOK, &totalBusy, 0, 1,
                  1);
@@ -1602,7 +1551,7 @@ volOffline(register struct cmd_syndesc *as)
 static int
 CreateVolume(register struct cmd_syndesc *as)
 {
-    afs_int32 pname;
+    afs_int32 pnum;
     char part[10];
     afs_int32 volid, code;
     struct nvldbentry entry;
@@ -1616,13 +1565,13 @@ CreateVolume(register struct cmd_syndesc *as)
                as->parms[0].items->data);
        return ENOENT;
     }
-    pname = volutil_GetPartitionID(as->parms[1].items->data);
-    if (pname < 0) {
+    pnum = volutil_GetPartitionID(as->parms[1].items->data);
+    if (pnum < 0) {
        fprintf(STDERR, "vos: could not interpret partition name '%s'\n",
                as->parms[1].items->data);
        return ENOENT;
     }
-    if (!IsPartValid(pname, tserver, &code)) { /*check for validity of the partition */
+    if (!IsPartValid(pnum, tserver, &code)) {  /*check for validity of the partition */
        if (code)
            PrintError("", code);
        else
@@ -1671,13 +1620,13 @@ CreateVolume(register struct cmd_syndesc *as)
     }
 
     code =
-       UV_CreateVolume2(tserver, pname, as->parms[2].items->data, quota, 0,
+       UV_CreateVolume2(tserver, pnum, as->parms[2].items->data, quota, 0,
                         0, 0, 0, &volid);
     if (code) {
        PrintDiagnostics("create", code);
        return code;
     }
-    MapPartIdIntoName(pname, part);
+    MapPartIdIntoName(pnum, part);
     fprintf(STDOUT, "Volume %lu created on partition %s of %s\n",
            (unsigned long)volid, part, as->parms[0].items->data);
 
@@ -1834,7 +1783,8 @@ MoveVolume(as)
      register struct cmd_syndesc *as;
 {
 
-    afs_int32 volid, fromserver, toserver, frompart, topart, code, err;
+    afs_int32 volid, fromserver, toserver, frompart, topart;
+    afs_int32 flags, code, err;
     char fromPartName[10], toPartName[10];
 
     struct diskPartition partition;    /* for space check */
@@ -1892,6 +1842,9 @@ MoveVolume(as)
        return ENOENT;
     }
 
+    flags = 0;
+    if (as->parms[5].items) flags |= RV_NOCLONE;
+
     /*
      * check source partition for space to clone volume
      */
@@ -1939,7 +1892,8 @@ MoveVolume(as)
 
     /* successful move still not guaranteed but shoot for it */
 
-    code = UV_MoveVolume(volid, fromserver, frompart, toserver, topart);
+    code =
+       UV_MoveVolume2(volid, fromserver, frompart, toserver, topart, flags);
     if (code) {
        PrintDiagnostics("move", code);
        return code;
@@ -1957,7 +1911,7 @@ static
 CopyVolume(as)
      register struct cmd_syndesc *as;
 {
-    afs_int32 volid, fromserver, toserver, frompart, topart, code, err;
+    afs_int32 volid, fromserver, toserver, frompart, topart, code, err, flags;
     char fromPartName[10], toPartName[10], *tovolume;
     struct nvldbentry entry;
     struct diskPartition partition;    /* for space check */
@@ -1982,7 +1936,7 @@ CopyVolume(as)
     toserver = GetServer(as->parms[4].items->data);
     if (toserver == 0) {
        fprintf(STDERR, "vos: server '%s' not found in host table\n",
-               as->parms[3].items->data);
+               as->parms[4].items->data);
        return ENOENT;
     }
 
@@ -2030,7 +1984,7 @@ CopyVolume(as)
     topart = volutil_GetPartitionID(as->parms[5].items->data);
     if (topart < 0) {
        fprintf(STDERR, "vos: could not interpret partition name '%s'\n",
-               as->parms[4].items->data);
+               as->parms[5].items->data);
        return EINVAL;
     }
     if (!IsPartValid(topart, toserver, &code)) {       /*check for validity of the partition */
@@ -2039,13 +1993,14 @@ CopyVolume(as)
        else
            fprintf(STDERR,
                    "vos : partition %s does not exist on the server\n",
-                   as->parms[4].items->data);
+                   as->parms[5].items->data);
        return ENOENT;
     }
 
-    /*
-     * check source partition for space to clone volume
-     */
+    flags = 0;
+    if (as->parms[6].items) flags |= RV_OFFLINE;
+    if (as->parms[7].items) flags |= RV_RDONLY;
+    if (as->parms[8].items) flags |= RV_NOCLONE;
 
     MapPartIdIntoName(topart, toPartName);
     MapPartIdIntoName(frompart, fromPartName);
@@ -2084,8 +2039,8 @@ CopyVolume(as)
     /* successful copy still not guaranteed but shoot for it */
 
     code =
-       UV_CopyVolume(volid, fromserver, frompart, tovolume, toserver,
-                     topart);
+       UV_CopyVolume2(volid, fromserver, frompart, tovolume, toserver,
+                      topart, 0, flags);
     if (code) {
        PrintDiagnostics("copy", code);
        return code;
@@ -2101,6 +2056,311 @@ CopyVolume(as)
 
 
 static
+ShadowVolume(as)
+     register struct cmd_syndesc *as;
+{
+    afs_int32 volid, fromserver, toserver, frompart, topart, tovolid;
+    afs_int32 code, err, flags;
+    char fromPartName[10], toPartName[10], toVolName[32], *tovolume;
+    struct nvldbentry entry;
+    struct diskPartition partition;    /* for space check */
+    volintInfo *p, *q;
+
+    p = (volintInfo *) 0;
+    q = (volintInfo *) 0;
+
+    volid = vsu_GetVolumeID(as->parms[0].items->data, cstruct, &err);
+    if (volid == 0) {
+       if (err)
+           PrintError("", err);
+       else
+           fprintf(STDERR, "vos: can't find volume ID or name '%s'\n",
+                   as->parms[0].items->data);
+       return ENOENT;
+    }
+    fromserver = GetServer(as->parms[1].items->data);
+    if (fromserver == 0) {
+       fprintf(STDERR, "vos: server '%s' not found in host table\n",
+               as->parms[1].items->data);
+       return ENOENT;
+    }
+
+    toserver = GetServer(as->parms[3].items->data);
+    if (toserver == 0) {
+       fprintf(STDERR, "vos: server '%s' not found in host table\n",
+               as->parms[3].items->data);
+       return ENOENT;
+    }
+
+    frompart = volutil_GetPartitionID(as->parms[2].items->data);
+    if (frompart < 0) {
+       fprintf(STDERR, "vos: could not interpret partition name '%s'\n",
+               as->parms[2].items->data);
+       return EINVAL;
+    }
+    if (!IsPartValid(frompart, fromserver, &code)) {   /*check for validity of the partition */
+       if (code)
+           PrintError("", code);
+       else
+           fprintf(STDERR,
+                   "vos : partition %s does not exist on the server\n",
+                   as->parms[2].items->data);
+       return ENOENT;
+    }
+
+    topart = volutil_GetPartitionID(as->parms[4].items->data);
+    if (topart < 0) {
+       fprintf(STDERR, "vos: could not interpret partition name '%s'\n",
+               as->parms[4].items->data);
+       return EINVAL;
+    }
+    if (!IsPartValid(topart, toserver, &code)) {       /*check for validity of the partition */
+       if (code)
+           PrintError("", code);
+       else
+           fprintf(STDERR,
+                   "vos : partition %s does not exist on the server\n",
+                   as->parms[4].items->data);
+       return ENOENT;
+    }
+
+    if (as->parms[5].items) {
+       tovolume = as->parms[5].items->data;
+       if (!ISNAMEVALID(tovolume)) {
+           fprintf(STDERR,
+               "vos: the name of the root volume %s exceeds the size limit of %d\n",
+               tovolume, VOLSER_OLDMAXVOLNAME - 10);
+           return E2BIG;
+       }
+       if (!VolNameOK(tovolume)) {
+           fprintf(STDERR,
+               "Illegal volume name %s, should not end in .readonly or .backup\n",
+               tovolume);
+           return EINVAL;
+       }
+       if (IsNumeric(tovolume)) {
+           fprintf(STDERR,
+               "Illegal volume name %s, should not be a number\n",
+               tovolume);
+           return EINVAL;
+       }
+    } else {
+       /* use actual name of source volume */
+       code = UV_ListOneVolume(fromserver, frompart, volid, &p);
+       if (code) {
+           fprintf(STDERR, "vos:cannot access volume %lu\n",
+               (unsigned long)volid);
+           exit(1);
+       }
+       strcpy(toVolName, p->name);
+       tovolume = toVolName;
+       /* save p for size checks later */
+    }
+
+    if (as->parms[6].items) {
+       tovolid = vsu_GetVolumeID(as->parms[6].items->data, cstruct, &err);
+       if (tovolid == 0) {
+           if (err)
+               PrintError("", err);
+           else
+               fprintf(STDERR, "vos: can't find volume ID or name '%s'\n",
+                       as->parms[6].items->data);
+           if (p)
+               free(p);
+           return ENOENT;
+       }
+    } else {
+       tovolid = vsu_GetVolumeID(tovolume, cstruct, &err);
+       if (tovolid == 0) {
+           if (err)
+               PrintError("", err);
+           else
+               fprintf(STDERR, "vos: can't find volume ID or name '%s'\n",
+                       tovolume);
+           if (p)
+               free(p);
+           return ENOENT;
+       }
+    }
+
+    flags = RV_NOVLDB;
+    if (as->parms[7].items) flags |= RV_OFFLINE;
+    if (as->parms[8].items) flags |= RV_RDONLY;
+    if (as->parms[9].items) flags |= RV_NOCLONE;
+    if (as->parms[10].items) flags |= RV_CPINCR;
+
+    MapPartIdIntoName(topart, toPartName);
+    MapPartIdIntoName(frompart, fromPartName);
+
+    /*
+     * check target partition for space to move volume
+     */
+
+    code = UV_PartitionInfo(toserver, toPartName, &partition);
+    if (code) {
+       fprintf(STDERR, "vos: cannot access partition %s\n", toPartName);
+       exit(1);
+    }
+    if (TESTM)
+       fprintf(STDOUT, "target partition %s free space %d\n", toPartName,
+               partition.free);
+
+    /* Don't do this again if we did it above */
+    if (!p) {
+       code = UV_ListOneVolume(fromserver, frompart, volid, &p);
+       if (code) {
+           fprintf(STDERR, "vos:cannot access volume %lu\n",
+               (unsigned long)volid);
+           exit(1);
+       }
+    }
+
+    /* OK if this fails */
+    code = UV_ListOneVolume(toserver, topart, tovolid, &q);
+
+    /* Treat existing volume size as "free" */
+    if (q)
+       p->size = (q->size < p->size) ? p->size - q->size : 0;
+
+    if (partition.free <= p->size) {
+       fprintf(STDERR,
+               "vos: no space on target partition %s to copy volume %lu\n",
+               toPartName, (unsigned long)volid);
+       free(p);
+       if (q) free(q);
+       exit(1);
+    }
+    free(p);
+    if (q) free(q);
+
+    /* successful copy still not guaranteed but shoot for it */
+
+    code =
+       UV_CopyVolume2(volid, fromserver, frompart, tovolume, toserver,
+                      topart, tovolid, flags);
+    if (code) {
+       PrintDiagnostics("shadow", code);
+       return code;
+    }
+    MapPartIdIntoName(topart, toPartName);
+    MapPartIdIntoName(frompart, fromPartName);
+    fprintf(STDOUT, "Volume %lu shadowed from %s %s to %s %s \n",
+           (unsigned long)volid, as->parms[1].items->data, fromPartName,
+           as->parms[4].items->data, toPartName);
+
+    return 0;
+}
+
+
+static
+CloneVolume(as)
+     register struct cmd_syndesc *as;
+{
+    afs_int32 server, part, volid, cloneid, voltype;
+    char partName[10], *volname;
+    afs_int32 code, err, flags;
+    struct nvldbentry entry;
+
+    volid = vsu_GetVolumeID(as->parms[0].items->data, cstruct, &err);
+    if (volid == 0) {
+       if (err)
+           PrintError("", err);
+       else
+           fprintf(STDERR, "vos: can't find volume ID or name '%s'\n",
+                   as->parms[0].items->data);
+       return ENOENT;
+    }
+
+    if (as->parms[1].items || as->parms[2].items) {
+       if (!as->parms[1].items || !as->parms[2].items) {
+           fprintf(STDERR,
+                   "Must specify both -server and -partition options\n");
+           return -1;
+       }
+       server = GetServer(as->parms[1].items->data);
+       if (server == 0) {
+           fprintf(STDERR, "vos: server '%s' not found in host table\n",
+                   as->parms[1].items->data);
+           return ENOENT;
+       }
+       part = volutil_GetPartitionID(as->parms[2].items->data);
+       if (part < 0) {
+           fprintf(STDERR, "vos: could not interpret partition name '%s'\n",
+                   as->parms[2].items->data);
+           return EINVAL;
+       }
+       if (!IsPartValid(part, server, &code)) {        /*check for validity of the partition */
+           if (code)
+               PrintError("", code);
+           else
+               fprintf(STDERR,
+                   "vos : partition %s does not exist on the server\n",
+                   as->parms[2].items->data);
+           return ENOENT;
+        }
+    } else {
+       code = GetVolumeInfo(volid, &server, &part, &voltype, &entry);
+       if (code)
+           return code;
+    }
+
+    volname = 0;
+    if (as->parms[3].items) {
+       volname = as->parms[3].items->data;
+       if (strlen(volname) > VOLSER_OLDMAXVOLNAME - 1) {
+           fprintf(STDERR,
+               "vos: the name of the root volume %s exceeds the size limit of %d\n",
+               volname, VOLSER_OLDMAXVOLNAME - 1);
+           return E2BIG;
+       }
+       if (!VolNameOK(volname)) {
+           fprintf(STDERR,
+               "Illegal volume name %s, should not end in .readonly or .backup\n",
+               volname);
+           return EINVAL;
+       }
+       if (IsNumeric(volname)) {
+           fprintf(STDERR,
+               "Illegal volume name %s, should not be a number\n",
+               volname);
+           return EINVAL;
+       }
+    }
+
+    cloneid = 0;
+    if (as->parms[4].items) {
+       cloneid = vsu_GetVolumeID(as->parms[4].items->data, cstruct, &err);
+       if (cloneid == 0) {
+           if (err)
+               PrintError("", err);
+           else
+               fprintf(STDERR, "vos: can't find volume ID or name '%s'\n",
+                       as->parms[4].items->data);
+           return ENOENT;
+       }
+    }
+
+    flags = 0;
+    if (as->parms[5].items) flags |= RV_OFFLINE;
+    if (as->parms[6].items) flags |= RV_RDONLY;
+
+
+    code = 
+       UV_CloneVolume(server, part, volid, cloneid, volname, flags);
+
+    if (code) {
+       PrintDiagnostics("clone", code);
+       return code;
+    }
+    MapPartIdIntoName(part, partName);
+    fprintf(STDOUT, "Created clone for volume %lu\n",
+           as->parms[0].items->data);
+
+    return 0;
+}
+
+
+static
 BackupVolume(as)
      register struct cmd_syndesc *as;
 {
@@ -2109,9 +2369,6 @@ BackupVolume(as)
 
     afs_int32 buvolid, buserver, bupart, butype;
     struct nvldbentry buentry;
-    struct rx_connection *conn;
-    volEntries volInfo;
-    struct nvldbentry store;
 
     avolid = vsu_GetVolumeID(as->parms[0].items->data, cstruct, &err);
     if (avolid == 0) {
@@ -2310,6 +2567,10 @@ DumpVolume(as)
 #define FULL  2
 #define INC   3
 
+#define TS_DUMP        1
+#define TS_KEEP        2
+#define TS_NEW 3
+
 static
 RestoreVolume(as)
      register struct cmd_syndesc *as;
@@ -2317,6 +2578,7 @@ RestoreVolume(as)
 {
     afs_int32 avolid, aserver, apart, code, vcode, err;
     afs_int32 aoverwrite = ASK;
+    afs_int32 acreation = 0, alastupdate = 0;
     int restoreflags, readonly = 0, offline = 0, voltype = RWVOL;
     char prompt;
     char afilename[NameLen], avolname[VOLSER_MAXVOLNAME + 1], apartName[10];
@@ -2363,6 +2625,40 @@ RestoreVolume(as)
        voltype = ROVOL;
     }
 
+    if (as->parms[8].items) {
+       if ((strcmp(as->parms[8].items->data, "d") == 0)
+           || (strcmp(as->parms[8].items->data, "dump") == 0)) {
+           acreation = TS_DUMP;
+       } else if ((strcmp(as->parms[8].items->data, "k") == 0)
+           || (strcmp(as->parms[8].items->data, "keep") == 0)) {
+           acreation = TS_KEEP;
+       } else if ((strcmp(as->parms[8].items->data, "n") == 0)
+           || (strcmp(as->parms[8].items->data, "new") == 0)) {
+           acreation = TS_NEW;
+       } else {
+           fprintf(STDERR, "vos: %s is not a valid argument to -creation\n",
+                   as->parms[8].items->data);
+           exit(1);
+       }
+    }
+
+    if (as->parms[9].items) {
+       if ((strcmp(as->parms[9].items->data, "d") == 0)
+           || (strcmp(as->parms[9].items->data, "dump") == 0)) {
+           alastupdate = TS_DUMP;
+       } else if ((strcmp(as->parms[9].items->data, "k") == 0)
+           || (strcmp(as->parms[9].items->data, "keep") == 0)) {
+           alastupdate = TS_KEEP;
+       } else if ((strcmp(as->parms[9].items->data, "n") == 0)
+           || (strcmp(as->parms[9].items->data, "new") == 0)) {
+           alastupdate = TS_NEW;
+       } else {
+           fprintf(STDERR, "vos: %s is not a valid argument to -lastupdate\n",
+                   as->parms[9].items->data);
+           exit(1);
+       }
+    }
+
     aserver = GetServer(as->parms[0].items->data);
     if (aserver == 0) {
        fprintf(STDERR, "vos: server '%s' not found in host table\n",
@@ -2514,6 +2810,38 @@ RestoreVolume(as)
        restoreflags |= RV_OFFLINE;
     if (readonly)
        restoreflags |= RV_RDONLY;
+
+    switch (acreation) {
+       case TS_DUMP:
+           restoreflags |= RV_CRDUMP;
+           break;
+       case TS_KEEP:
+           restoreflags |= RV_CRKEEP;
+           break;
+       case TS_NEW:
+           restoreflags |= RV_CRNEW;
+           break;
+       default:
+           if (aoverwrite == FULL)
+               restoreflags |= RV_CRNEW;
+           else
+               restoreflags |= RV_CRKEEP;
+    }
+
+    switch (alastupdate) {
+       case TS_DUMP:
+           restoreflags |= RV_LUDUMP;
+           break;
+       case TS_KEEP:
+           restoreflags |= RV_LUKEEP;
+           break;
+       case TS_NEW:
+           restoreflags |= RV_LUNEW;
+           break;
+       default:
+           restoreflags |= RV_LUKEEP;
+    }
+
     code =
        UV_RestoreVolume(aserver, apart, avolid, avolname, restoreflags,
                         WriteData, afilename);
@@ -3028,7 +3356,7 @@ static
 SyncVldb(as)
      register struct cmd_syndesc *as;
 {
-    afs_int32 pname, code;     /* part name */
+    afs_int32 pnum, code;      /* part name */
     char part[10];
     int flags = 0;
     char *volname = 0;
@@ -3044,13 +3372,13 @@ SyncVldb(as)
     }
 
     if (as->parms[1].items) {
-       pname = volutil_GetPartitionID(as->parms[1].items->data);
-       if (pname < 0) {
+       pnum = volutil_GetPartitionID(as->parms[1].items->data);
+       if (pnum < 0) {
            fprintf(STDERR, "vos: could not interpret partition name '%s'\n",
                    as->parms[1].items->data);
            exit(1);
        }
-       if (!IsPartValid(pname, tserver, &code)) {      /*check for validity of the partition */
+       if (!IsPartValid(pnum, tserver, &code)) {       /*check for validity of the partition */
            if (code)
                PrintError("", code);
            else
@@ -3071,14 +3399,14 @@ SyncVldb(as)
     if (as->parms[2].items) {
        /* Synchronize an individual volume */
        volname = as->parms[2].items->data;
-       code = UV_SyncVolume(tserver, pname, volname, flags);
+       code = UV_SyncVolume(tserver, pnum, volname, flags);
     } else {
        if (!tserver) {
            fprintf(STDERR,
                    "Without a -volume option, the -server option is required\n");
            exit(1);
        }
-       code = UV_SyncVldb(tserver, pname, flags, 0 /*unused */ );
+       code = UV_SyncVldb(tserver, pnum, flags, 0 /*unused */ );
     }
 
     if (code) {
@@ -3095,7 +3423,7 @@ SyncVldb(as)
        fprintf(STDOUT, " with state of server %s", as->parms[0].items->data);
     }
     if (flags) {
-       MapPartIdIntoName(pname, part);
+       MapPartIdIntoName(pnum, part);
        fprintf(STDOUT, " partition %s\n", part);
     }
     fprintf(STDOUT, "\n");
@@ -3108,7 +3436,7 @@ SyncServer(as)
      register struct cmd_syndesc *as;
 
 {
-    afs_int32 pname, code;     /* part name */
+    afs_int32 pnum, code;      /* part name */
     char part[10];
 
     int flags = 0;
@@ -3120,13 +3448,13 @@ SyncServer(as)
        exit(1);
     }
     if (as->parms[1].items) {
-       pname = volutil_GetPartitionID(as->parms[1].items->data);
-       if (pname < 0) {
+       pnum = volutil_GetPartitionID(as->parms[1].items->data);
+       if (pnum < 0) {
            fprintf(STDERR, "vos: could not interpret partition name '%s'\n",
                    as->parms[1].items->data);
            exit(1);
        }
-       if (!IsPartValid(pname, tserver, &code)) {      /*check for validity of the partition */
+       if (!IsPartValid(pnum, tserver, &code)) {       /*check for validity of the partition */
            if (code)
                PrintError("", code);
            else
@@ -3136,15 +3464,17 @@ SyncServer(as)
            exit(1);
        }
        flags = 1;
+    } else {
+        pnum = -1;
     }
 
-    code = UV_SyncServer(tserver, pname, flags, 0 /*unused */ );
+    code = UV_SyncServer(tserver, pnum, flags, 0 /*unused */ );
     if (code) {
        PrintDiagnostics("syncserv", code);
        exit(1);
     }
     if (flags) {
-       MapPartIdIntoName(pname, part);
+       MapPartIdIntoName(pnum, part);
        fprintf(STDOUT, "Server %s partition %s synchronized with VLDB\n",
                as->parms[0].items->data, part);
     } else
@@ -3477,6 +3807,10 @@ GetVolumeInfo(volid, server, part, voltype, rentry)
        *part = rentry->serverPartition[index];
        return 0;
     }
+    fprintf(STDERR,
+            "unexpected volume type for volume %lu\n",
+            (unsigned long)volid);
+    return -1;
 }
 
 static
@@ -3893,7 +4227,6 @@ BackSys(as)
     afs_int32 totalFail = 0;
     int previdx = -1, error, same;
     int comp = 0;
-    char compstr[50];
     struct cmd_item *ti;
     char *ccode;
     int match;
@@ -3936,7 +4269,7 @@ BackSys(as)
                regex_t re;
                char errbuf[256];
 
-               code = regcomp(&re, ti->data, REG_BASIC | REG_NOSUB);
+               code = regcomp(&re, ti->data, REG_NOSUB);
                if (code != 0) {
                    regerror(code, &re, errbuf, sizeof errbuf);
                    fprintf(STDERR,
@@ -3964,7 +4297,7 @@ BackSys(as)
                regex_t re;
                char errbuf[256];
 
-               code = regcomp(&re, ti->data, REG_BASIC | REG_NOSUB);
+               code = regcomp(&re, ti->data, REG_NOSUB);
                if (code != 0) {
                    regerror(code, &re, errbuf, sizeof errbuf);
                    fprintf(STDERR,
@@ -4051,7 +4384,7 @@ BackSys(as)
                    char errbuf[256];
 
                    /* XXX -- should just do the compile once! */
-                   code = regcomp(&re, ti->data, REG_BASIC | REG_NOSUB);
+                   code = regcomp(&re, ti->data, REG_NOSUB);
                    if (code != 0) {
                        regerror(code, &re, errbuf, sizeof errbuf);
                        fprintf(STDERR,
@@ -4096,7 +4429,7 @@ BackSys(as)
                    char errbuf[256];
 
                    /* XXX -- should just do the compile once! */
-                   code = regcomp(&re, ti->data, REG_BASIC | REG_NOSUB);
+                   code = regcomp(&re, ti->data, REG_NOSUB);
                    if (code != 0) {
                        regerror(code, &re, errbuf, sizeof errbuf);
                        fprintf(STDERR,
@@ -4441,11 +4774,11 @@ print_addrs(const bulkaddrs * addrs, const afsUUID * m_uuid, int nentries,
 {
     afs_int32 vcode;
     afs_int32 i, j;
-    struct VLCallBack unused;
+    struct VLCallBack vlcb;
     afs_int32 *addrp;
     bulkaddrs m_addrs;
     ListAddrByAttributes m_attrs;
-    afs_int32 m_unique, m_nentries, *m_addrp;
+    afs_int32 m_nentries, *m_addrp;
     afs_int32 base, index;
     char buf[1024];
 
@@ -4475,7 +4808,7 @@ print_addrs(const bulkaddrs * addrs, const afsUUID * m_uuid, int nentries,
                m_addrs.bulkaddrs_len = 0;
                vcode =
                    ubik_Call(VL_GetAddrsU, cstruct, 0, &m_attrs, &m_uuid,
-                             &m_unique, &m_nentries, &m_addrs);
+                             &vlcb, &m_nentries, &m_addrs);
                if (vcode) {
                    fprintf(STDERR,
                            "vos: could not list the multi-homed server addresses\n");
@@ -4526,14 +4859,13 @@ ListAddrs(as)
      register struct cmd_syndesc *as;
 {
     afs_int32 vcode;
-    afs_int32 i, j, noresolve = 0, printuuid = 0;
-    struct VLCallBack unused;
-    afs_int32 nentries, *addrp;
-    bulkaddrs addrs, m_addrs;
+    afs_int32 i, noresolve = 0, printuuid = 0;
+    struct VLCallBack vlcb;
+    afs_int32 nentries;
+    bulkaddrs m_addrs;
     ListAddrByAttributes m_attrs;
     afsUUID m_uuid, askuuid;
-    afs_int32 m_unique, m_nentries, *m_addrp;
-    afs_int32 base, index;
+    afs_int32 m_nentries;
 
     memset(&m_attrs, 0, sizeof(struct ListAddrByAttributes));
     m_attrs.Mask = VLADDR_INDEX;
@@ -4571,7 +4903,7 @@ ListAddrs(as)
     m_addrs.bulkaddrs_len = 0;
 
     vcode =
-       ubik_Call_New(VL_GetAddrs, cstruct, 0, 0, 0, &m_unique, &nentries,
+       ubik_Call_New(VL_GetAddrs, cstruct, 0, 0, 0, &vlcb, &nentries,
                      &m_addrs);
     if (vcode) {
        fprintf(STDERR, "vos: could not list the server addresses\n");
@@ -4588,7 +4920,7 @@ ListAddrs(as)
 
        vcode =
            ubik_Call_New(VL_GetAddrsU, cstruct, 0, &m_attrs, &m_uuid,
-                         &m_unique, &m_nentries, &m_addrs);
+                         &vlcb, &m_nentries, &m_addrs);
        if (vcode == VL_NOENT) {
            i++;
            nentries++;
@@ -5010,6 +5342,8 @@ main(argc, argv)
                "machine name on destination");
     cmd_AddParm(ts, "-topartition", CMD_SINGLE, 0,
                "partition name on destination");
+    cmd_AddParm(ts, "-live", CMD_FLAG, CMD_OPTIONAL,
+               "copy live volume without cloning");
     COMMONPARMS;
 
     ts = cmd_CreateSyntax("copy", CopyVolume, 0, "copy a volume");
@@ -5022,6 +5356,36 @@ main(argc, argv)
                "machine name on destination");
     cmd_AddParm(ts, "-topartition", CMD_SINGLE, 0,
                "partition name on destination");
+    cmd_AddParm(ts, "-offline", CMD_FLAG, CMD_OPTIONAL,
+               "leave new volume offline");
+    cmd_AddParm(ts, "-readonly", CMD_FLAG, CMD_OPTIONAL,
+               "make new volume read-only");
+    cmd_AddParm(ts, "-live", CMD_FLAG, CMD_OPTIONAL,
+               "copy live volume without cloning");
+    COMMONPARMS;
+
+    ts = cmd_CreateSyntax("shadow", ShadowVolume, 0,
+                         "make or update a shadow volume");
+    cmd_AddParm(ts, "-id", CMD_SINGLE, 0, "volume name or ID on source");
+    cmd_AddParm(ts, "-fromserver", CMD_SINGLE, 0, "machine name on source");
+    cmd_AddParm(ts, "-frompartition", CMD_SINGLE, 0,
+               "partition name on source");
+    cmd_AddParm(ts, "-toserver", CMD_SINGLE, 0,
+               "machine name on destination");
+    cmd_AddParm(ts, "-topartition", CMD_SINGLE, 0,
+               "partition name on destination");
+    cmd_AddParm(ts, "-toname", CMD_SINGLE, CMD_OPTIONAL,
+               "volume name on destination");
+    cmd_AddParm(ts, "-toid", CMD_SINGLE, CMD_OPTIONAL,
+               "volume ID on destination");
+    cmd_AddParm(ts, "-offline", CMD_FLAG, CMD_OPTIONAL,
+               "leave shadow volume offline");
+    cmd_AddParm(ts, "-readonly", CMD_FLAG, CMD_OPTIONAL,
+               "make shadow volume read-only");
+    cmd_AddParm(ts, "-live", CMD_FLAG, CMD_OPTIONAL,
+               "copy live volume without cloning");
+    cmd_AddParm(ts, "-incremental", CMD_FLAG, CMD_OPTIONAL,
+               "do incremental update if target exists");
     COMMONPARMS;
 
     ts = cmd_CreateSyntax("backup", BackupVolume, 0,
@@ -5029,6 +5393,21 @@ main(argc, argv)
     cmd_AddParm(ts, "-id", CMD_SINGLE, 0, "volume name or ID");
     COMMONPARMS;
 
+    ts = cmd_CreateSyntax("clone", CloneVolume, 0,
+                         "make clone of a volume");
+    cmd_AddParm(ts, "-id", CMD_SINGLE, 0, "volume name or ID");
+    cmd_AddParm(ts, "-server", CMD_SINGLE, CMD_OPTIONAL, "server");
+    cmd_AddParm(ts, "-partition", CMD_SINGLE, CMD_OPTIONAL, "partition");
+    cmd_AddParm(ts, "-toname", CMD_SINGLE, CMD_OPTIONAL,
+               "volume name on destination");
+    cmd_AddParm(ts, "-toid", CMD_SINGLE, CMD_OPTIONAL,
+               "volume ID on destination");
+    cmd_AddParm(ts, "-offline", CMD_FLAG, CMD_OPTIONAL,
+               "leave clone volume offline");
+    cmd_AddParm(ts, "-readonly", CMD_FLAG, CMD_OPTIONAL,
+               "make clone volume read-only, not readwrite");
+    COMMONPARMS;
+
     ts = cmd_CreateSyntax("release", ReleaseVolume, 0, "release a volume");
     cmd_AddParm(ts, "-id", CMD_SINGLE, 0, "volume name or ID");
     cmd_AddParm(ts, "-force", CMD_FLAG, CMD_OPTIONAL,
@@ -5057,6 +5436,10 @@ main(argc, argv)
                "leave restored volume offline");
     cmd_AddParm(ts, "-readonly", CMD_FLAG, CMD_OPTIONAL,
                "make restored volume read-only");
+    cmd_AddParm(ts, "-creation", CMD_SINGLE, CMD_OPTIONAL,
+               "dump | keep | new");
+    cmd_AddParm(ts, "-lastupdate", CMD_SINGLE, CMD_OPTIONAL,
+               "dump | keep | new");
     COMMONPARMS;
 
     ts = cmd_CreateSyntax("unlock", LockReleaseCmd, 0,