From: Jeffrey Hutzelman Date: Thu, 8 Jan 2004 21:54:10 +0000 (+0000) Subject: vos-move-copy-improvements-20040108 X-Git-Tag: openafs-devel-1_3_60~109 X-Git-Url: https://git.michaelhowe.org/gitweb/?a=commitdiff_plain;h=c4f94dd3a5c1d15273bc54e4f42fc9069c3a3613;p=packages%2Fo%2Fopenafs.git vos-move-copy-improvements-20040108 FIXES 2815 add switches to vos move and vos copy. add vos clone. --- diff --git a/src/volser/volser.p.h b/src/volser/volser.p.h index 03af7491b..091b523b8 100644 --- a/src/volser/volser.p.h +++ b/src/volser/volser.p.h @@ -156,9 +156,13 @@ struct partList { /*used by the backup system */ #define VOK 0x02 /* Values for the UV_RestoreVolume flags parameter */ +/* Also used for UV_CopyVolume and UV_CloneVolume */ #define RV_FULLRST 0x1 #define RV_OFFLINE 0x2 #define RV_RDONLY 0x10000 +#define RV_CPINCR 0x20000 +#define RV_NOVLDB 0x40000 +#define RV_NOCLONE 0x80000 extern afs_uint32 vsu_GetVolumeID(char *astring, struct ubik_client *acstruct, afs_int32 *errp); extern int vsu_ExtractName(char rname[], char name[]); diff --git a/src/volser/vos.c b/src/volser/vos.c index 93a0dd51c..9015b50d2 100644 --- a/src/volser/vos.c +++ b/src/volser/vos.c @@ -1789,7 +1789,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 */ @@ -1847,6 +1848,9 @@ MoveVolume(as) return ENOENT; } + flags = 0; + if (as->parms[5].items) flags |= RV_NOCLONE; + /* * check source partition for space to clone volume */ @@ -1894,7 +1898,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; @@ -1912,7 +1917,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 */ @@ -1937,7 +1942,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; } @@ -1985,7 +1990,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 */ @@ -1994,13 +1999,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); @@ -2039,8 +2045,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; @@ -2055,6 +2061,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; @@ -4962,6 +5273,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"); @@ -4974,6 +5287,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, @@ -4981,6 +5324,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, diff --git a/src/volser/vsprocs.c b/src/volser/vsprocs.c index fe03d06a0..6ca2273f4 100644 --- a/src/volser/vsprocs.c +++ b/src/volser/vsprocs.c @@ -1014,12 +1014,15 @@ sigint_handler(int x) } /* Move volume on to - * . The operation is almost idempotent + * . 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; @@ -1243,26 +1246,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; @@ -1279,27 +1288,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 /* @@ -1374,25 +1395,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. @@ -1406,14 +1433,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 */ @@ -1582,27 +1611,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 */ @@ -1652,8 +1687,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); } @@ -1948,23 +1983,39 @@ UV_MoveVolume(afs_int32 afromvol, afs_int32 afromserver, afs_int32 afrompart, } -/* Move volume on to - * . 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 from to + * . The new volume is named by . The new volume + * has ID 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; @@ -2009,80 +2060,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 @@ -2110,14 +2202,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)); @@ -2125,38 +2216,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; -/* 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 */ + /*** + * Now dump the clone to the new volume + ***/ - /* Copy the clone to the new volume */ - VPRINT2("Dumping from clone %u on source to volume %u on destination ...", + 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 ...", 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. @@ -2170,18 +2269,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; @@ -2208,59 +2310,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 */ @@ -2389,6 +2497,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); +} @@ -2647,6 +2762,196 @@ UV_BackupVolume(afs_int32 aserver, afs_int32 apart, afs_int32 avolid) return error; } +/* Make a new clone of volume on and + * using volume ID , or a new ID allocated from the VLDB. + * The new volume is named by , or by appending ".clone" to + * the existing name if 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)