From bdc054a4de4de3378910e8ca599ab78b3dff0468 Mon Sep 17 00:00:00 2001 From: Sam Hartman Date: Fri, 21 May 2004 04:01:53 +0000 Subject: [PATCH] Copy volser in from 1.3.64 as there were merge problems and no changes needed to be saved. --- src/volser/.cvsignore | 11 + src/volser/Makefile.in | 211 + src/volser/NTMakefile | 168 + src/volser/common.c | 66 + src/volser/dump.h | 52 + src/volser/dumpstuff.c | 1637 ++++++++ src/volser/lockdata.h | 30 + src/volser/lockprocs.c | 261 ++ src/volser/physio.c | 176 + src/volser/restorevol.c | 993 +++++ src/volser/vol-dump.c | 871 ++++ src/volser/vol.h | 26 + src/volser/volerr.et | 32 + src/volser/volint.xg | 410 ++ src/volser/volmain.c | 458 +++ src/volser/volprocs.c | 2908 ++++++++++++++ src/volser/volser.p.h | 172 + src/volser/volser_prototypes.h | 102 + src/volser/volserver.rc | 17 + src/volser/voltrans.c | 228 ++ src/volser/vos.c | 5593 ++++++++++++++++++++++++++ src/volser/vos.rc | 17 + src/volser/vsprocs.c | 6880 ++++++++++++++++++++++++++++++++ src/volser/vsutils.c | 674 ++++ 24 files changed, 21993 insertions(+) create mode 100644 src/volser/.cvsignore create mode 100644 src/volser/Makefile.in create mode 100644 src/volser/NTMakefile create mode 100644 src/volser/common.c create mode 100644 src/volser/dump.h create mode 100644 src/volser/dumpstuff.c create mode 100644 src/volser/lockdata.h create mode 100644 src/volser/lockprocs.c create mode 100644 src/volser/physio.c create mode 100644 src/volser/restorevol.c create mode 100644 src/volser/vol-dump.c create mode 100644 src/volser/vol.h create mode 100644 src/volser/volerr.et create mode 100644 src/volser/volint.xg create mode 100644 src/volser/volmain.c create mode 100644 src/volser/volprocs.c create mode 100644 src/volser/volser.p.h create mode 100644 src/volser/volser_prototypes.h create mode 100644 src/volser/volserver.rc create mode 100644 src/volser/voltrans.c create mode 100644 src/volser/vos.c create mode 100644 src/volser/vos.rc create mode 100644 src/volser/vsprocs.c create mode 100644 src/volser/vsutils.c diff --git a/src/volser/.cvsignore b/src/volser/.cvsignore new file mode 100644 index 000000000..8d0ac8660 --- /dev/null +++ b/src/volser/.cvsignore @@ -0,0 +1,11 @@ +AFS_component_version_number.c +Makefile +restorevol +volerr.c +volint.cs.c +volint.h +volint.ss.c +volint.xdr.c +volser.h +volserver +vos diff --git a/src/volser/Makefile.in b/src/volser/Makefile.in new file mode 100644 index 000000000..7833d7802 --- /dev/null +++ b/src/volser/Makefile.in @@ -0,0 +1,211 @@ +# Copyright 2000, International Business Machines Corporation and others. +# All Rights Reserved. +# +# This software has been released under the terms of the IBM Public +# License. For details, see the LICENSE file in the top-level source +# directory or online at http://www.openafs.org/dl/license10.html + +srcdir=@srcdir@ +include @TOP_OBJDIR@/src/config/Makefile.config +HELPER_SPLINT=@HELPER_SPLINT@ + + +VINCLS=${TOP_INCDIR}/afs/partition.h ${TOP_INCDIR}/afs/volume.h \ + ${TOP_INCDIR}/afs/vlserver.h vol.h dump.h volser.h lockdata.h + +RINCLS=${TOP_INCDIR}/rx/rx.h ${TOP_INCDIR}/rx/xdr.h \ + ${TOP_INCDIR}/afs/keys.h ${TOP_INCDIR}/afs/cellconfig.h \ + ${TOP_INCDIR}/ubik.h ${TOP_INCDIR}/afs/cmd.h + +INTINCLS=volint.h volser.h + +LIBS=\ + ${TOP_LIBDIR}/libaudit.a \ + ${TOP_LIBDIR}/vlib.a \ + ${TOP_LIBDIR}/libacl.a \ + ${TOP_LIBDIR}/libsys.a \ + ${TOP_LIBDIR}/libvldb.a \ + ${TOP_LIBDIR}/libubik.a \ + ${TOP_LIBDIR}/libauth.a \ + ${TOP_LIBDIR}/libcmd.a \ + ${TOP_LIBDIR}/librxkad.a \ + ${TOP_LIBDIR}/libdes.a \ + ${TOP_LIBDIR}/librxstat.a \ + ${TOP_LIBDIR}/librx.a \ + ${TOP_LIBDIR}/liblwp.a \ + ${TOP_LIBDIR}/libsys.a \ + ${TOP_LIBDIR}/libcom_err.a \ + ${TOP_LIBDIR}/libkauth.a \ + ${TOP_LIBDIR}/libusd.a \ + ${TOP_LIBDIR}/util.a + +VOLDUMP_LIBS = \ + ../vol/ihandle.o \ + ../vol/physio.o \ + ../vol/vlib.a \ + ${TOP_LIBDIR}/libcmd.a \ + ${TOP_LIBDIR}/util.a \ + ${TOP_LIBDIR}/libsys.a \ + ${TOP_LIBDIR}/libdir.a \ + ${TOP_LIBDIR}/liblwp.a \ + ${TOP_LIBDIR}/libacl.a + +VSOBJS=vsprocs.o vsutils.o lockprocs.o volint.xdr.o volerr.o +SOBJS=volmain.o volprocs.o physio.o common.o voltrans.o volerr.o \ + volint.cs.o dumpstuff.o volint.ss.o volint.xdr.o + +all: volserver vos restorevol voldump \ + ${TOP_INCDIR}/afs/volser.h \ + ${TOP_INCDIR}/afs/volint.h \ + ${TOP_LIBDIR}/libvolser.a + +restorevol: restorevol.c + ${CC} ${CFLAGS} -o restorevol ${srcdir}/restorevol.c \ + ${TOP_LIBDIR}/libcmd.a ${TOP_LIBDIR}/util.a ${XLIBS} + +vos: vos.o ${VSOBJS} libvolser.a ${LIBS} + ${CC} ${LDFLAGS} -o vos vos.o $(VSOBJS) libvolser.a ${LIBS} ${XLIBS} + +volserver: $(SOBJS) $(LIBS) ${TOP_LIBDIR}/libdir.a + ${CC} ${DBUG} -o volserver $(SOBJS) ${TOP_LIBDIR}/libdir.a \ + ${LDFLAGS} $(LIBS) ${XLIBS} + +voldump: vol-dump.o ${VOLDUMP_LIBS} + ${CC} ${CFLAGS} -o voldump vol-dump.o ${VOLDUMP_LIBS} ${XLIBS} + +libvolser.a: volint.cs.o $(VSOBJS) volint.ss.o AFS_component_version_number.o + -$(RM) -f $@ + $(AR) crv $@ volint.cs.o $(VSOBJS) volint.ss.o AFS_component_version_number.o + $(RANLIB) $@ + +volser.h volerr.c: volerr.et volser.p.h + $(RM) -f volser.h volerr.c + ${COMPILE_ET} -p ${srcdir} volerr -h volser + +volint.cs.c: volint.xg + ${RXGEN} -x -C -o $@ ${srcdir}/volint.xg + +volint.ss.c: volint.xg + ${RXGEN} -x -S -o $@ ${srcdir}/volint.xg + +volint.xdr.c: volint.xg + ${RXGEN} -x -c -o $@ ${srcdir}/volint.xg + +volint.h: volint.xg + ${RXGEN} -x -h -o $@ ${srcdir}/volint.xg + +volint.cs.c: volint.h +volint.ss.c: volint.h +volint.xdr.c: volint.h + +# +# Dependencies +# +volint.cs.o: volint.cs.c ${INTINCLS} +volint.ss.o: volint.ss.c ${INTINCLS} +volint.xdr.o: volint.xdr.c ${INTINCLS} +vsutils.o: vsutils.c ${VINCLS} ${RINCLS} ${INTINCLS} +volmain.o: volmain.c ${VINCLS} ${RINCLS} AFS_component_version_number.c +volprocs.o: volprocs.c ${VINCLS} ${RINCLS} ${INTINCLS} +dumpstuff.o: dumpstuff.c ${VINCLS} ${RINCLS} ${INTINCLS} +voldump.o: voldump.c ${VINCLS} ${RINCLS} +vos.o: vos.c ${VINCLS} ${RINCLS} ${INTINCLS} AFS_component_version_number.c +vsprocs.o: vsprocs.c ${VINCLS} ${RINCLS} ${INTINCLS} +physio.o: physio.c ${VINCLS} +common.o: common.c ${VINCLS} +lockprocs.o: lockprocs.c ${VINCLS} ${INTINCLS} ${RINCLS} + +# +# Installation targets +# +install: \ + ${DESTDIR}${sbindir}/restorevol \ + ${DESTDIR}${sbindir}/voldump \ + ${DESTDIR}${includedir}/afs/volser.h \ + ${DESTDIR}${includedir}/afs/volint.h \ + ${DESTDIR}${sbindir}/vos \ + ${DESTDIR}${afssrvsbindir}/vos \ + ${DESTDIR}${afssrvlibexecdir}/volserver \ + ${DESTDIR}${libdir}/afs/libvolser.a + + +${DEST}/include/afs/volser.h: volser.h + ${INSTALL} $? $@ + +${DEST}/include/afs/volint.h: volint.h + ${INSTALL} $? $@ + +${DEST}/etc/restorevol: restorevol + ${INSTALL} $? $@ + +${DEST}/etc/voldump: voldump + ${INSTALL} $? $@ + +${DEST}/etc/vos ${DEST}/root.server/usr/afs/bin/vos: vos + ${INSTALL} $? $@ + +${DEST}/lib/afs/libvolser.a: libvolser.a + ${INSTALL} $? $@ + +${DEST}/root.server/usr/afs/bin/volserver: volserver + ${INSTALL} $? $@ + +# +# Misc targets +# +clean: + $(RM) -f *.o *.a core volserver volint.ss.c volint.cs.c volint.h \ + volint.xdr.c vos volser.h volerr.c AFS_component_version_number.c restorevol voldump + +include ../config/Makefile.version + +${DESTDIR}${sbindir}/restorevol: restorevol + ${INSTALL} $? $@ + +${DESTDIR}${sbindir}/voldump: voldump + ${INSTALL} $? $@ + +${DESTDIR}${includedir}/afs/volser.h: volser.h + ${INSTALL} $? $@ + +${TOP_INCDIR}/afs/volser.h: volser.h + ${INSTALL} $? $@ + +${DESTDIR}${includedir}/afs/volint.h: volint.h + ${INSTALL} $? $@ + +${TOP_INCDIR}/afs/volint.h: volint.h + ${INSTALL} $? $@ + +${DESTDIR}${sbindir}/vos: vos + ${INSTALL} $? $@ + +${DESTDIR}${afssrvsbindir}/vos: vos + ${INSTALL} $? $@ + +${DESTDIR}${afssrvlibexecdir}/volserver: volserver + ${INSTALL} $? $@ + +${DESTDIR}${libdir}/afs/libvolser.a: libvolser.a + ${INSTALL} $? $@ + +${TOP_LIBDIR}/libvolser.a: libvolser.a + ${INSTALL} $? $@ + +dest: \ + ${DEST}/etc/restorevol \ + ${DEST}/etc/voldump \ + ${DEST}/include/afs/volser.h \ + ${DEST}/include/afs/volint.h \ + ${DEST}/etc/vos \ + ${DEST}/root.server/usr/afs/bin/vos \ + ${DEST}/root.server/usr/afs/bin/volserver \ + ${DEST}/lib/afs/libvolser.a + + +check-splint:: + sh $(HELPER_SPLINT) $(CFLAGS) \ + vos.c restorevol.c \ + vsprocs.c vsutils.c lockprocs.c volerr.c \ + volmain.c volprocs.c physio.c common.c voltrans.c \ + dumpstuff.c diff --git a/src/volser/NTMakefile b/src/volser/NTMakefile new file mode 100644 index 000000000..ff0e91446 --- /dev/null +++ b/src/volser/NTMakefile @@ -0,0 +1,168 @@ +# Copyright 2000, International Business Machines Corporation and others. +# All Rights Reserved. +# +# This software has been released under the terms of the IBM Public +# License. For details, see the LICENSE file in the top-level source +# directory or online at http://www.openafs.org/dl/license10.html + +RELDIR=volser +!INCLUDE ..\config\NTMakefile.$(SYS_NAME) +!INCLUDE ..\config\NTMakefile.version + +############################################################################ +# Definitions for installing header files + +INCFILEDIR = $(DESTDIR)\include\afs # header file install directory + +INCFILES = \ + $(INCFILEDIR)\volser_prototypes.h \ + $(INCFILEDIR)\volser.h \ + $(INCFILEDIR)\volint.h + + +LOCAL_INCFILES = \ + volser.h \ + volint.h + +############################################################################ +# Build volser library. + +LIBFILE = $(DESTDIR)\lib\afs\afsvolser.lib + +LIBOBJS =\ + $(OUT)\lockprocs.obj \ + $(OUT)\volerr.obj \ + $(OUT)\volint.cs.obj \ + $(OUT)\volint.ss.obj \ + $(OUT)\volint.xdr.obj \ + $(OUT)\vsprocs.obj \ + $(OUT)\vsutils.obj \ + $(OUT)\AFS_component_version_number.obj + +$(LIBFILE): $(LIBOBJS) + $(LIBARCH) + + +############################################################################ +# External libraries + +EXEC_LIBS = \ + $(DESTDIR)\lib\afs\afscmd.lib \ + $(DESTDIR)\lib\afs\afsvol.lib \ + $(DESTDIR)\lib\afs\afsutil.lib \ + $(DESTDIR)\lib\afs\afsdir.lib \ + $(DESTDIR)\lib\afs\afsvol.lib \ + $(DESTDIR)\lib\afs\afsaudit.lib \ + $(DESTDIR)\lib\afs\afsauth.lib \ + $(DESTDIR)\lib\afs\afsvldb.lib \ + $(DESTDIR)\lib\afs\afskauth.lib \ + $(DESTDIR)\lib/afs/afscom_err.lib \ + $(DESTDIR)\lib\afs\afsusd.lib \ + $(DESTDIR)\lib\afsrxkad.lib \ + $(DESTDIR)\lib\afsrxstat.lib \ + $(DESTDIR)\lib\afsdes.lib \ + $(DESTDIR)\lib\afsrx.lib \ + $(DESTDIR)\lib\afslwp.lib \ + $(DESTDIR)\lib\afs\afsacl.lib \ + $(DESTDIR)\lib\afs\afsreg.lib \ + $(DESTDIR)\lib\afs\afseventlog.lib \ + $(DESTDIR)\lib\cm_dns.obj + + +############################################################################ +# Build volserver + +VOLSERVER_EXEFILE = $(DESTDIR)\root.server\usr\afs\bin\volserver.exe + +VOLSERVER_EXEOBJS = \ + $(OUT)\common.obj \ + $(OUT)\dumpstuff.obj \ + $(OUT)\physio.obj \ + $(OUT)\volerr.obj \ + $(OUT)\volint.cs.obj \ + $(OUT)\volint.ss.obj \ + $(OUT)\volint.xdr.obj \ + $(OUT)\volmain.obj \ + $(OUT)\volprocs.obj \ + $(OUT)\voltrans.obj \ + $(OUT)\volserver.res + + +VOLSERVER_EXELIBS = \ + $(DESTDIR)\lib\afs\afsdir.lib \ + $(DESTDIR)\lib\afs\afsprocmgmt.lib + +$(VOLSERVER_EXEFILE): $(VOLSERVER_EXEOBJS) $(VOLSERVER_EXELIBS) $(EXEC_LIBS) + $(EXECONLINK) + $(EXEPREP) + +############################################################################ +# Build vos + +RS_VOS_EXEFILE = $(DESTDIR)\root.server\usr\afs\bin\vos.exe +CL_VOS_EXEFILE = $(DESTDIR)\etc\vos.exe + +VOS_EXEOBJS = \ + $(OUT)\vos.obj \ + $(OUT)\vsprocs.obj \ + $(OUT)\vsutils.obj \ + $(OUT)\lockprocs.obj \ + $(OUT)\volint.xdr.obj \ + $(OUT)\volerr.obj \ + $(OUT)\vos.res + +VOS_EXELIBS = \ + $(DESTDIR)\lib\afsubik.lib \ + $(DESTDIR)\lib\afs\afsvolser.lib \ + $(DESTDIR)\lib\afs\afsprocmgmt.lib \ + $(DESTDIR)\lib\afs\afspioctl.lib + +$(RS_VOS_EXEFILE): $(VOS_EXEOBJS) $(VOS_EXELIBS) $(EXEC_LIBS) + $(EXECONLINK) + $(EXEPREP) + +$(CL_VOS_EXEFILE): $(RS_VOS_EXEFILE) + $(COPY) $** $@ + +############################################################################ +# Generate versioninfo resources +$(OUT)\volserver.res: AFS_component_version_number.h + +$(OUT)\vos.res: AFS_component_version_number.h + +############################################################################ +# Definitions for generating files via RXGEN + +$(INCFILES):$$(@F) + $(COPY) $** $(INCFILEDIR)\. + +volint.h volint.cs.c volint.ss.c volint.xdr.c: volint.xg + $(RXGEN) -x $** + + +############################################################################ +# Definitions for generating files via COMPILE_ET + +volser.h volerr.c: volerr.et volser.p.h + $(DEL) volerr.c volser.h + $(COMPILE_ET) volerr -h volser + + + + +############################################################################ +# Install target; primary makefile target + +install: $(LOCAL_INCFILES) $(LIBFILE) $(VOLSERVER_EXEFILE) $(CL_VOS_EXEFILE) \ + $(INCFILES) + + +############################################################################ +# Local clean target; augments predefined clean target + +clean:: + $(DEL) volerr.c volser.h $(INCFILES) + $(DEL) volint.cs.c volint.h volint.ss.c volint.xdr.c volser.h + +mkdir: + diff --git a/src/volser/common.c b/src/volser/common.c new file mode 100644 index 000000000..1af526e63 --- /dev/null +++ b/src/volser/common.c @@ -0,0 +1,66 @@ +/* + * Copyright 2000, International Business Machines Corporation and others. + * All Rights Reserved. + * + * This software has been released under the terms of the IBM Public + * License. For details, see the LICENSE file in the top-level source + * directory or online at http://www.openafs.org/dl/license10.html + */ + +#include +#include + +RCSID + ("$Header: /cvs/openafs/src/volser/common.c,v 1.10 2003/11/15 04:59:16 shadow Exp $"); + +#include +#include +#include + +#ifndef AFS_PTHREAD_ENV +/*@printflike@*/ void +Log(const char *format, ...) +{ + va_list args; + + va_start(args, format); + vViceLog(0, (format, args)); + va_end(args); +} +#endif + +void +LogError(afs_int32 errcode) +{ + ViceLog(0, + ("%s: %s\n", error_table_name(errcode), error_message(errcode))); +} + +#ifndef AFS_PTHREAD_ENV +/*@printflike@*/ void +Abort(const char *format, ...) +{ + va_list args; + + ViceLog(0, ("Program aborted: ")); + va_start(args, format); + vViceLog(0, (format, args)); + va_end(args); + abort(); +} +#endif + +void +InitErrTabs(void) +{ +#ifndef AFS_PTHREAD_ENV + initialize_KA_error_table(); + initialize_RXK_error_table(); + initialize_KTC_error_table(); + initialize_ACFG_error_table(); + initialize_CMD_error_table(); + initialize_VL_error_table(); + initialize_VOLS_error_table(); +#endif + return; +} diff --git a/src/volser/dump.h b/src/volser/dump.h new file mode 100644 index 000000000..349eb7247 --- /dev/null +++ b/src/volser/dump.h @@ -0,0 +1,52 @@ +/* + * Copyright 2000, International Business Machines Corporation and others. + * All Rights Reserved. + * + * This software has been released under the terms of the IBM Public + * License. For details, see the LICENSE file in the top-level source + * directory or online at http://www.openafs.org/dl/license10.html + */ + +/* + System: Volser + Module: dump.h + Institution: The Information Technology Center, Carnegie-Mellon University + + */ + +#define DUMPVERSION 1 + +#define DUMPENDMAGIC 0x3A214B6E +#define DUMPBEGINMAGIC 0xB3A11322 + +#define D_DUMPHEADER 1 +#define D_VOLUMEHEADER 2 +#define D_VNODE 3 +#define D_DUMPEND 4 + +#define D_MAX 20 + +#define MAXDUMPTIMES 50 + +/* DumpHeader: + Each {from,to} pair of time values gives a span of time covered by this dump. + Merged dumps may have multiple pairs if there are dumps missing from the merge */ + +struct DumpHeader { + afs_int32 version; + VolumeId volumeId; + char volumeName[VNAMESIZE]; + int nDumpTimes; /* Number of pairs */ + struct { + afs_int32 from, to; + } dumpTimes[MAXDUMPTIMES]; +}; + + +/* Some handshaking constants for volume move */ +#define SHAKE1 "x" +#define SHAKE2 "y" +#define SHAKE3 "z" +#define SHAKE4 "P" +#define SHAKE5 "Q" +#define SHAKE_ABORT "!" diff --git a/src/volser/dumpstuff.c b/src/volser/dumpstuff.c new file mode 100644 index 000000000..83562b200 --- /dev/null +++ b/src/volser/dumpstuff.c @@ -0,0 +1,1637 @@ +/* + * Copyright 2000, International Business Machines Corporation and others. + * All Rights Reserved. + * + * This software has been released under the terms of the IBM Public + * License. For details, see the LICENSE file in the top-level source + * directory or online at http://www.openafs.org/dl/license10.html + */ + +#include +#include + +RCSID + ("$Header: /cvs/openafs/src/volser/dumpstuff.c,v 1.25 2003/11/23 04:53:44 jaltman Exp $"); + +#include +#include +#include +#include +#ifdef AFS_NT40_ENV +#include +#else +#include +#include +#include +#include +#include +#endif +#ifdef HAVE_STRING_H +#include +#else +#ifdef HAVE_STRINGS_H +#include +#endif +#endif +#include +#ifdef AFS_PTHREAD_ENV +#include +#else /* AFS_PTHREAD_ENV */ +#include +#endif /* AFS_PTHREAD_ENV */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "dump.h" +#include +#include +#include "volser.h" +#include "volint.h" + +#ifndef AFS_NT40_ENV +#ifdef O_LARGEFILE +#define afs_stat stat64 +#define afs_fstat fstat64 +#else /* !O_LARGEFILE */ +#define afs_stat stat +#define afs_fstat fstat +#endif /* !O_LARGEFILE */ +#endif /* !AFS_NT40_ENV */ + +/*@printflike@*/ extern void Log(const char *format, ...); + +extern int DoLogging; + +/* This iod stuff is a silly little package to emulate the old qi_in stuff, which + emulated the stdio stuff. There is a big assumption here, that the + rx_Read will never be called directly, by a routine like readFile, when + there is an old character that was pushed back with iod_ungetc. This + is really bletchy, and is here for compatibility only. Eventually, + we should define a volume format that doesn't require + the pushing back of characters (i.e. characters should not double both + as an end marker and a begin marker) */ +struct iod { + struct rx_call *call; /* call to which to write, might be an array */ + int device; /* dump device ID for volume */ + int parentId; /* dump parent ID for volume */ + struct DiskPartition *dumpPartition; /* Dump partition. */ + struct rx_call **calls; /* array of pointers to calls */ + int ncalls; /* how many calls/codes in array */ + int *codes; /* one return code for each call */ + char haveOldChar; /* state for pushing back a character */ + char oldChar; +}; + + +/* Forward Declarations */ +static int DumpDumpHeader(register struct iod *iodp, register Volume * vp, + afs_int32 fromtime); +static int DumpPartial(register struct iod *iodp, register Volume * vp, + afs_int32 fromtime, int dumpAllDirs); +static int DumpVnodeIndex(register struct iod *iodp, Volume * vp, + VnodeClass class, afs_int32 fromtime, + int forcedump); +static int DumpVnode(register struct iod *iodp, struct VnodeDiskObject *v, + int volid, int vnodeNumber, int dumpEverything); +static int ReadDumpHeader(register struct iod *iodp, struct DumpHeader *hp); +static int ReadVnodes(register struct iod *iodp, Volume * vp, int incremental, + afs_int32 * Lbuf, afs_int32 s1, afs_int32 * Sbuf, + afs_int32 s2, afs_int32 delo); +static afs_fsize_t volser_WriteFile(int vn, struct iod *iodp, + FdHandle_t * handleP, int tag, + Error * status); + +static int SizeDumpDumpHeader(register struct iod *iodp, register Volume * vp, + afs_int32 fromtime, + register struct volintSize *size); +static int SizeDumpPartial(register struct iod *iodp, register Volume * vp, + afs_int32 fromtime, int dumpAllDirs, + register struct volintSize *size); +static int SizeDumpVnodeIndex(register struct iod *iodp, Volume * vp, + VnodeClass class, afs_int32 fromtime, + int forcedump, + register struct volintSize *size); +static int SizeDumpVnode(register struct iod *iodp, struct VnodeDiskObject *v, + int volid, int vnodeNumber, int dumpEverything, + register struct volintSize *size); + +static void +iod_Init(register struct iod *iodp, register struct rx_call *call) +{ + iodp->call = call; + iodp->haveOldChar = 0; + iodp->ncalls = 1; + iodp->calls = (struct rx_call **)0; +} + +static void +iod_InitMulti(struct iod *iodp, struct rx_call **calls, int ncalls, + int *codes) +{ + + iodp->calls = calls; + iodp->haveOldChar = 0; + iodp->ncalls = ncalls; + iodp->codes = codes; + iodp->call = (struct rx_call *)0; +} + +/* N.B. iod_Read doesn't check for oldchar (see previous comment) */ +#define iod_Read(iodp, buf, nbytes) rx_Read((iodp)->call, buf, nbytes) + +/* For the single dump case, it's ok to just return the "bytes written" + * that rx_Write returns, since all the callers of iod_Write abort when + * the returned value is less than they expect. For the multi dump case, + * I don't think we want half the replicas to go bad just because one + * connection timed out, but if they all time out, then we should give up. + */ +static int +iod_Write(struct iod *iodp, char *buf, int nbytes) +{ + int code, i; + int one_success = 0; + + assert((iodp->call && iodp->ncalls == 1 && !iodp->calls) + || (!iodp->call && iodp->ncalls >= 1 && iodp->calls)); + + if (iodp->call) { + code = rx_Write(iodp->call, buf, nbytes); + return code; + } + + for (i = 0; i < iodp->ncalls; i++) { + if (iodp->calls[i] && !iodp->codes[i]) { + code = rx_Write(iodp->calls[i], buf, nbytes); + if (code != nbytes) { /* everything gets merged into a single error */ + iodp->codes[i] = VOLSERDUMPERROR; /* but that's exactly what the */ + } /* standard dump does, anyways */ + else { + one_success = TRUE; + } + } + } /* for all calls */ + + if (one_success) + return nbytes; + else + return 0; +} + +static void +iod_ungetc(struct iod *iodp, int achar) +{ + iodp->oldChar = achar; + iodp->haveOldChar = 1; +} + +static int +iod_getc(register struct iod *iodp) +{ + unsigned char t; + + if (iodp->haveOldChar) { + iodp->haveOldChar = 0; + return iodp->oldChar; + } + if (iod_Read(iodp, &t, 1) == 1) + return t; + return EOF; +} + +static int +ReadShort(register struct iod *iodp, register unsigned short *sp) +{ + register b1, b0; + b1 = iod_getc(iodp); + b0 = iod_getc(iodp); + *sp = (b1 << 8) | b0; + return b0 != EOF; +} + +static int +ReadInt32(register struct iod *iodp, afs_uint32 * lp) +{ + afs_uint32 register b3, b2, b1, b0; + b3 = iod_getc(iodp); + b2 = iod_getc(iodp); + b1 = iod_getc(iodp); + b0 = iod_getc(iodp); + *lp = (((((b3 << 8) | b2) << 8) | b1) << 8) | b0; + return b0 != EOF; +} + +static void +ReadString(register struct iod *iodp, register char *to, register int maxa) +{ + register int c; + while (maxa--) { + if ((*to++ = iod_getc(iodp)) == 0) + break; + } + if (to[-1]) { + while ((c = iod_getc(iodp)) && c != EOF); + to[-1] = 0; + } +} + +static void +ReadByteString(register struct iod *iodp, register byte * to, + register int size) +{ + while (size--) + *to++ = iod_getc(iodp); +} + +static int +ReadVolumeHeader(register struct iod *iodp, VolumeDiskData * vol) +{ + register tag; + afs_uint32 trash; + memset(vol, 0, sizeof(*vol)); + while ((tag = iod_getc(iodp)) > D_MAX && tag != EOF) { + switch (tag) { + case 'i': + if (!ReadInt32(iodp, &vol->id)) + return VOLSERREAD_DUMPERROR; + break; + case 'v': + if (!ReadInt32(iodp, &trash)) + return VOLSERREAD_DUMPERROR; + break; + case 'n': + ReadString(iodp, vol->name, sizeof(vol->name)); + /*this means the name of the retsored volume could be possibly different. In conjunction with SAFSVolSignalRestore */ + break; + case 's': + vol->inService = iod_getc(iodp); + break; + case 'b': + vol->blessed = iod_getc(iodp); + break; + case 'u': + if (!ReadInt32(iodp, &vol->uniquifier)) + return VOLSERREAD_DUMPERROR; + break; + case 't': + vol->type = iod_getc(iodp); + break; + case 'p': + if (!ReadInt32(iodp, &vol->parentId)) + return VOLSERREAD_DUMPERROR; + break; + case 'c': + if (!ReadInt32(iodp, &vol->cloneId)) + return VOLSERREAD_DUMPERROR; + break; + case 'q': + if (!ReadInt32(iodp, (afs_uint32 *) & vol->maxquota)) + return VOLSERREAD_DUMPERROR; + break; + case 'm': + if (!ReadInt32(iodp, (afs_uint32 *) & vol->minquota)) + return VOLSERREAD_DUMPERROR; + break; + case 'd': + if (!ReadInt32(iodp, (afs_uint32 *) & vol->diskused)) + return VOLSERREAD_DUMPERROR; /* Bogus: should calculate this */ + break; + case 'f': + if (!ReadInt32(iodp, (afs_uint32 *) & vol->filecount)) + return VOLSERREAD_DUMPERROR; + break; + case 'a': + if (!ReadInt32(iodp, &vol->accountNumber)) + return VOLSERREAD_DUMPERROR; + break; + case 'o': + if (!ReadInt32(iodp, &vol->owner)) + return VOLSERREAD_DUMPERROR; + break; + case 'C': + if (!ReadInt32(iodp, &vol->creationDate)) + return VOLSERREAD_DUMPERROR; + break; + case 'A': + if (!ReadInt32(iodp, &vol->accessDate)) + return VOLSERREAD_DUMPERROR; + break; + case 'U': + if (!ReadInt32(iodp, &vol->updateDate)) + return VOLSERREAD_DUMPERROR; + break; + case 'E': + if (!ReadInt32(iodp, &vol->expirationDate)) + return VOLSERREAD_DUMPERROR; + break; + case 'B': + if (!ReadInt32(iodp, &vol->backupDate)) + return VOLSERREAD_DUMPERROR; + break; + case 'O': + ReadString(iodp, vol->offlineMessage, + sizeof(vol->offlineMessage)); + break; + case 'M': + /* + * Detailed volume statistics are never stored in dumps, + * so we just restore either the null string if this volume + * had already been set to store statistics, or the old motd + * contents otherwise. It doesn't matter, since this field + * will soon get initialized anyway. + */ + ReadString(iodp, (char *)(vol->stat_reads), VMSGSIZE); + break; + case 'W':{ + unsigned short length; + int i; + afs_uint32 data; + if (!ReadShort(iodp, &length)) + return VOLSERREAD_DUMPERROR; + for (i = 0; i < length; i++) { + if (!ReadInt32(iodp, &data)) + return VOLSERREAD_DUMPERROR; + if (i < sizeof(vol->weekUse) / sizeof(vol->weekUse[0])) + vol->weekUse[i] = data; + } + break; + } + case 'D': + if (!ReadInt32(iodp, &vol->dayUseDate)) + return VOLSERREAD_DUMPERROR; + break; + case 'Z': + if (!ReadInt32(iodp, (afs_uint32 *) & vol->dayUse)) + return VOLSERREAD_DUMPERROR; + break; + } + } + iod_ungetc(iodp, tag); + return 0; +} + +static int +DumpTag(register struct iod *iodp, register int tag) +{ + char p; + + p = tag; + return ((iod_Write(iodp, &p, 1) == 1) ? 0 : VOLSERDUMPERROR); + +} + +static int +DumpByte(register struct iod *iodp, char tag, byte value) +{ + char tbuffer[2]; + register byte *p = (unsigned char *)tbuffer; + *p++ = tag; + *p = value; + return ((iod_Write(iodp, tbuffer, 2) == 2) ? 0 : VOLSERDUMPERROR); +} + +#define putint32(p, v) *p++ = v>>24, *p++ = v>>16, *p++ = v>>8, *p++ = v +#define putshort(p, v) *p++ = v>>8, *p++ = v + +static int +DumpDouble(register struct iod *iodp, char tag, register afs_uint32 value1, + register afs_uint32 value2) +{ + char tbuffer[9]; + register byte *p = (unsigned char *)tbuffer; + *p++ = tag; + putint32(p, value1); + putint32(p, value2); + return ((iod_Write(iodp, tbuffer, 9) == 9) ? 0 : VOLSERDUMPERROR); +} + +static int +DumpInt32(register struct iod *iodp, char tag, register afs_uint32 value) +{ + char tbuffer[5]; + register byte *p = (unsigned char *)tbuffer; + *p++ = tag; + putint32(p, value); + return ((iod_Write(iodp, tbuffer, 5) == 5) ? 0 : VOLSERDUMPERROR); +} + +static int +DumpArrayInt32(register struct iod *iodp, char tag, + register afs_uint32 * array, register int nelem) +{ + char tbuffer[4]; + register afs_uint32 v; + int code = 0; + register byte *p = (unsigned char *)tbuffer; + *p++ = tag; + putshort(p, nelem); + code = iod_Write(iodp, tbuffer, 3); + if (code != 3) + return VOLSERDUMPERROR; + while (nelem--) { + p = (unsigned char *)tbuffer; + v = *array++; /*this was register */ + + putint32(p, v); + code = iod_Write(iodp, tbuffer, 4); + if (code != 4) + return VOLSERDUMPERROR; + } + return 0; +} + +static int +DumpShort(register struct iod *iodp, char tag, unsigned int value) +{ + char tbuffer[3]; + register byte *p = (unsigned char *)tbuffer; + *p++ = tag; + *p++ = value >> 8; + *p = value; + return ((iod_Write(iodp, tbuffer, 3) == 3) ? 0 : VOLSERDUMPERROR); +} + +static int +DumpBool(register struct iod *iodp, char tag, unsigned int value) +{ + char tbuffer[2]; + register byte *p = (unsigned char *)tbuffer; + *p++ = tag; + *p = value; + return ((iod_Write(iodp, tbuffer, 2) == 2) ? 0 : VOLSERDUMPERROR); +} + +static int +DumpString(register struct iod *iodp, char tag, register char *s) +{ + register n; + int code = 0; + code = iod_Write(iodp, &tag, 1); + if (code != 1) + return VOLSERDUMPERROR; + n = strlen(s) + 1; + code = iod_Write(iodp, s, n); + if (code != n) + return VOLSERDUMPERROR; + return 0; +} + +static int +DumpByteString(register struct iod *iodp, char tag, register byte * bs, + register int nbytes) +{ + int code = 0; + + code = iod_Write(iodp, &tag, 1); + if (code != 1) + return VOLSERDUMPERROR; + code = iod_Write(iodp, (char *)bs, nbytes); + if (code != nbytes) + return VOLSERDUMPERROR; + return 0; +} + +static int +DumpFile(struct iod *iodp, int vnode, FdHandle_t * handleP) +{ + int code = 0, lcode = 0, error = 0; + afs_int32 pad = 0, offset; + afs_sfsize_t n, nbytes, howMany, howBig; + byte *p; +#ifndef AFS_NT40_ENV + struct afs_stat status; +#endif + afs_sfsize_t size; +#ifdef AFS_AIX_ENV +#include + struct statfs tstatfs; +#endif + +#ifdef AFS_NT40_ENV + howBig = _filelength(handleP->fd_fd); + howMany = 4096; + +#else + afs_fstat(handleP->fd_fd, &status); + howBig = status.st_size; + +#ifdef AFS_AIX_ENV + /* Unfortunately in AIX valuable fields such as st_blksize are + * gone from the stat structure. + */ + fstatfs(handleP->fd_fd, &tstatfs); + howMany = tstatfs.f_bsize; +#else + howMany = status.st_blksize; +#endif /* AFS_AIX_ENV */ +#endif /* AFS_NT40_ENV */ + + + size = FDH_SIZE(handleP); +#ifdef AFS_LARGEFILE_ENV + { + afs_uint32 hi, lo; + SplitInt64(size, hi, lo); + if (hi == 0L) { + code = DumpInt32(iodp, 'f', lo); + } else { + code = DumpDouble(iodp, 'h', hi, lo); + } + } +#else /* !AFS_LARGEFILE_ENV */ + code = DumpInt32(iodp, 'f', size); +#endif /* !AFS_LARGEFILE_ENV */ + if (code) { + return VOLSERDUMPERROR; + } + + p = (unsigned char *)malloc(howMany); + if (!p) { + Log("1 Volser: DumpFile: no memory"); + return VOLSERDUMPERROR; + } + + for (nbytes = size; (nbytes && !error); nbytes -= howMany) { + if (nbytes < howMany) + howMany = nbytes; + + /* Read the data - unless we know we can't */ + n = (lcode ? 0 : FDH_READ(handleP, p, howMany)); + + /* If read any good data and we null padded previously, log the + * amount that we had null padded. + */ + if ((n > 0) && pad) { + Log("1 Volser: DumpFile: Null padding file %d bytes at offset %u\n", pad, offset); + pad = 0; + } + + /* If didn't read enough data, null padd the rest of the buffer. This + * can happen if, for instance, the media has some bad spots. We don't + * want to quit the dump, so we start null padding. + */ + if (n < howMany) { + /* Record the read error */ + if (n < 0) { + n = 0; + Log("1 Volser: DumpFile: Error %d reading inode %s for vnode %d\n", errno, PrintInode(NULL, handleP->fd_ih->ih_ino), vnode); + } else if (!pad) { + Log("1 Volser: DumpFile: Error reading inode %s for vnode %d\n", PrintInode(NULL, handleP->fd_ih->ih_ino), vnode); + } + + /* Pad the rest of the buffer with zeros. Remember offset we started + * padding. Keep total tally of padding. + */ + memset(p + n, 0, howMany - n); + if (!pad) + offset = (howBig - nbytes) + n; + pad += (howMany - n); + + /* Now seek over the data we could not get. An error here means we + * can't do the next read. + */ + lcode = FDH_SEEK(handleP, ((size - nbytes) + howMany), SEEK_SET); + if (lcode != ((size - nbytes) + howMany)) { + if (lcode < 0) { + Log("1 Volser: DumpFile: Error %d seeking in inode %s for vnode %d\n", errno, PrintInode(NULL, handleP->fd_ih->ih_ino), vnode); + } else { + Log("1 Volser: DumpFile: Error seeking in inode %s for vnode %d\n", PrintInode(NULL, handleP->fd_ih->ih_ino), vnode); + lcode = -1; + } + } else { + lcode = 0; + } + } + + /* Now write the data out */ + if (iod_Write(iodp, (char *)p, howMany) != howMany) + error = VOLSERDUMPERROR; +#ifndef AFS_PTHREAD_ENV + IOMGR_Poll(); +#endif + } + + if (pad) { /* Any padding we hadn't reported yet */ + Log("1 Volser: DumpFile: Null padding file: %d bytes at offset %u\n", + pad, offset); + } + + free(p); + return error; +} + +static int +DumpVolumeHeader(register struct iod *iodp, register Volume * vp) +{ + int code = 0; + static char nullString[1] = ""; /*The ``contents'' of motd */ + + if (!code) + code = DumpTag(iodp, D_VOLUMEHEADER); + if (!code) { + code = DumpInt32(iodp, 'i', V_id(vp)); + } + if (!code) + code = DumpInt32(iodp, 'v', V_stamp(vp).version); + if (!code) + code = DumpString(iodp, 'n', V_name(vp)); + if (!code) + code = DumpBool(iodp, 's', V_inService(vp)); + if (!code) + code = DumpBool(iodp, 'b', V_blessed(vp)); + if (!code) + code = DumpInt32(iodp, 'u', V_uniquifier(vp)); + if (!code) + code = DumpByte(iodp, 't', (byte) V_type(vp)); + if (!code) { + code = DumpInt32(iodp, 'p', V_parentId(vp)); + } + if (!code) + code = DumpInt32(iodp, 'c', V_cloneId(vp)); + if (!code) + code = DumpInt32(iodp, 'q', V_maxquota(vp)); + if (!code) + code = DumpInt32(iodp, 'm', V_minquota(vp)); + if (!code) + code = DumpInt32(iodp, 'd', V_diskused(vp)); + if (!code) + code = DumpInt32(iodp, 'f', V_filecount(vp)); + if (!code) + code = DumpInt32(iodp, 'a', V_accountNumber(vp)); + if (!code) + code = DumpInt32(iodp, 'o', V_owner(vp)); + if (!code) + code = DumpInt32(iodp, 'C', V_creationDate(vp)); /* Rw volume creation date */ + if (!code) + code = DumpInt32(iodp, 'A', V_accessDate(vp)); + if (!code) + code = DumpInt32(iodp, 'U', V_updateDate(vp)); + if (!code) + code = DumpInt32(iodp, 'E', V_expirationDate(vp)); + if (!code) + code = DumpInt32(iodp, 'B', V_backupDate(vp)); /* Rw volume backup clone date */ + if (!code) + code = DumpString(iodp, 'O', V_offlineMessage(vp)); + /* + * We do NOT dump the detailed volume statistics residing in the old + * motd field, since we cannot tell from the info in a dump whether + * statistics data has been put there. Instead, we dump a null string, + * just as if that was what the motd contained. + */ + if (!code) + code = DumpString(iodp, 'M', nullString); + if (!code) + code = + DumpArrayInt32(iodp, 'W', (afs_uint32 *) V_weekUse(vp), + sizeof(V_weekUse(vp)) / sizeof(V_weekUse(vp)[0])); + if (!code) + code = DumpInt32(iodp, 'D', V_dayUseDate(vp)); + if (!code) + code = DumpInt32(iodp, 'Z', V_dayUse(vp)); + return code; +} + +static int +DumpEnd(register struct iod *iodp) +{ + return (DumpInt32(iodp, D_DUMPEND, DUMPENDMAGIC)); +} + +/* Guts of the dump code */ + +/* Dump a whole volume */ +int +DumpVolume(register struct rx_call *call, register Volume * vp, + afs_int32 fromtime, int dumpAllDirs) +{ + struct iod iod; + int code = 0; + register struct iod *iodp = &iod; + iod_Init(iodp, call); + + if (!code) + code = DumpDumpHeader(iodp, vp, fromtime); + + if (!code) + code = DumpPartial(iodp, vp, fromtime, dumpAllDirs); + +/* hack follows. Errors should be handled quite differently in this version of dump than they used to be.*/ + if (rx_Error(iodp->call)) { + Log("1 Volser: DumpVolume: Rx call failed during dump, error %d\n", + rx_Error(iodp->call)); + return VOLSERDUMPERROR; + } + if (!code) + code = DumpEnd(iodp); + + return code; +} + +/* Dump a volume to multiple places*/ +int +DumpVolMulti(struct rx_call **calls, int ncalls, Volume * vp, + afs_int32 fromtime, int dumpAllDirs, int *codes) +{ + struct iod iod; + int code = 0; + iod_InitMulti(&iod, calls, ncalls, codes); + + if (!code) + code = DumpDumpHeader(&iod, vp, fromtime); + if (!code) + code = DumpPartial(&iod, vp, fromtime, dumpAllDirs); + if (!code) + code = DumpEnd(&iod); + return code; +} + +/* A partial dump (no dump header) */ +static int +DumpPartial(register struct iod *iodp, register Volume * vp, + afs_int32 fromtime, int dumpAllDirs) +{ + int code = 0; + if (!code) + code = DumpVolumeHeader(iodp, vp); + if (!code) + code = DumpVnodeIndex(iodp, vp, vLarge, fromtime, dumpAllDirs); + if (!code) + code = DumpVnodeIndex(iodp, vp, vSmall, fromtime, 0); + return code; +} + +static int +DumpVnodeIndex(register struct iod *iodp, Volume * vp, VnodeClass class, + afs_int32 fromtime, int forcedump) +{ + register int code = 0; + register struct VnodeClassInfo *vcp = &VnodeClassInfo[class]; + char buf[SIZEOF_LARGEDISKVNODE]; + struct VnodeDiskObject *vnode = (struct VnodeDiskObject *)buf; + StreamHandle_t *file; + FdHandle_t *fdP; + int size; + int flag; + register int vnodeIndex, nVnodes; + + fdP = IH_OPEN(vp->vnodeIndex[class].handle); + assert(fdP != NULL); + file = FDH_FDOPEN(fdP, "r+"); + assert(file != NULL); + size = OS_SIZE(fdP->fd_fd); + assert(size != -1); + nVnodes = (size / vcp->diskSize) - 1; + if (nVnodes > 0) { + assert((nVnodes + 1) * vcp->diskSize == size); + assert(STREAM_SEEK(file, vcp->diskSize, 0) == 0); + } else + nVnodes = 0; + for (vnodeIndex = 0; + nVnodes && STREAM_READ(vnode, vcp->diskSize, 1, file) == 1 && !code; + nVnodes--, vnodeIndex++) { + flag = forcedump || (vnode->serverModifyTime >= fromtime); + /* Note: the >= test is very important since some old volumes may not have + * a serverModifyTime. For an epoch dump, this results in 0>=0 test, which + * does dump the file! */ + if (!code) + code = + DumpVnode(iodp, vnode, V_id(vp), + bitNumberToVnodeNumber(vnodeIndex, class), flag); +#ifndef AFS_PTHREAD_ENV + if (!flag) + IOMGR_Poll(); /* if we dont' xfr data, but scan instead, could lose conn */ +#endif + } + STREAM_CLOSE(file); + FDH_CLOSE(fdP); + return code; +} + +static int +DumpDumpHeader(register struct iod *iodp, register Volume * vp, + afs_int32 fromtime) +{ + int code = 0; + int UseLatestReadOnlyClone = 1; + afs_int32 dumpTimes[2]; + iodp->device = vp->device; + iodp->parentId = V_parentId(vp); + iodp->dumpPartition = vp->partition; + if (!code) + code = DumpDouble(iodp, D_DUMPHEADER, DUMPBEGINMAGIC, DUMPVERSION); + if (!code) + code = + DumpInt32(iodp, 'v', + UseLatestReadOnlyClone ? V_id(vp) : V_parentId(vp)); + if (!code) + code = DumpString(iodp, 'n', V_name(vp)); + dumpTimes[0] = fromtime; + dumpTimes[1] = V_backupDate(vp); /* Until the time the clone was made */ + if (!code) + code = DumpArrayInt32(iodp, 't', (afs_uint32 *) dumpTimes, 2); + return code; +} + +static int +DumpVnode(register struct iod *iodp, struct VnodeDiskObject *v, int volid, + int vnodeNumber, int dumpEverything) +{ + int code = 0; + IHandle_t *ihP; + FdHandle_t *fdP; + + if (!v || v->type == vNull) + return code; + if (!code) + code = DumpDouble(iodp, D_VNODE, vnodeNumber, v->uniquifier); + if (!dumpEverything) + return code; + if (!code) + code = DumpByte(iodp, 't', (byte) v->type); + if (!code) + code = DumpShort(iodp, 'l', v->linkCount); /* May not need this */ + if (!code) + code = DumpInt32(iodp, 'v', v->dataVersion); + if (!code) + code = DumpInt32(iodp, 'm', v->unixModifyTime); + if (!code) + code = DumpInt32(iodp, 'a', v->author); + if (!code) + code = DumpInt32(iodp, 'o', v->owner); + if (!code && v->group) + code = DumpInt32(iodp, 'g', v->group); /* default group is 0 */ + if (!code) + code = DumpShort(iodp, 'b', v->modeBits); + if (!code) + code = DumpInt32(iodp, 'p', v->parent); + if (!code) + code = DumpInt32(iodp, 's', v->serverModifyTime); + if (v->type == vDirectory) { + acl_HtonACL(VVnodeDiskACL(v)); + if (!code) + code = + DumpByteString(iodp, 'A', (byte *) VVnodeDiskACL(v), + VAclDiskSize(v)); + } + if (VNDISK_GET_INO(v)) { + IH_INIT(ihP, iodp->device, iodp->parentId, VNDISK_GET_INO(v)); + fdP = IH_OPEN(ihP); + if (fdP == NULL) { + Log("1 Volser: DumpVnode: dump: Unable to open inode %llu for vnode %u (volume %i); not dumped, error %d\n", (afs_uintmax_t) VNDISK_GET_INO(v), vnodeNumber, volid, errno); + IH_RELEASE(ihP); + return VOLSERREAD_DUMPERROR; + } + code = DumpFile(iodp, vnodeNumber, fdP); + FDH_CLOSE(fdP); + IH_RELEASE(ihP); + } + return code; +} + + +int +ProcessIndex(Volume * vp, VnodeClass class, afs_int32 ** Bufp, int *sizep, + int del) +{ + int i, nVnodes, offset, code, index = 0; + afs_int32 *Buf; + int cnt = 0; + int size; + StreamHandle_t *afile; + FdHandle_t *fdP; + struct VnodeClassInfo *vcp = &VnodeClassInfo[class]; + char buf[SIZEOF_LARGEDISKVNODE], zero[SIZEOF_LARGEDISKVNODE]; + register struct VnodeDiskObject *vnode = (struct VnodeDiskObject *)buf; + + memset(zero, 0, sizeof(zero)); /* zero out our proto-vnode */ + fdP = IH_OPEN(vp->vnodeIndex[class].handle); + if (fdP == NULL) + return -1; + afile = FDH_FDOPEN(fdP, "r+"); + if (del) { + int cnt1 = 0; + Buf = *Bufp; + for (i = 0; i < *sizep; i++) { + if (Buf[i]) { + cnt++; + STREAM_SEEK(afile, Buf[i], 0); + code = STREAM_READ(vnode, vcp->diskSize, 1, afile); + if (code == 1) { + if (vnode->type != vNull && VNDISK_GET_INO(vnode)) { + cnt1++; + if (DoLogging) { + Log("RestoreVolume %u Cleanup: Removing old vnode=%u inode=%llu size=unknown\n", + V_id(vp), bitNumberToVnodeNumber(i, class), + (afs_uintmax_t) VNDISK_GET_INO(vnode)); + } + IH_DEC(V_linkHandle(vp), VNDISK_GET_INO(vnode), + V_parentId(vp)); + DOPOLL; + } + STREAM_SEEK(afile, Buf[i], 0); + (void)STREAM_WRITE(zero, vcp->diskSize, 1, afile); /* Zero it out */ + } + Buf[i] = 0; + } + } + if (DoLogging) { + Log("RestoreVolume Cleanup: Removed %d inodes for volume %d\n", + cnt1, V_id(vp)); + } + STREAM_FLUSH(afile); /* ensure 0s are on the disk */ + OS_SYNC(afile->str_fd); + } else { + size = OS_SIZE(fdP->fd_fd); + assert(size != -1); + nVnodes = + (size <= + vcp->diskSize ? 0 : size - vcp->diskSize) >> vcp->logSize; + if (nVnodes > 0) { + Buf = (afs_int32 *) malloc(nVnodes * sizeof(afs_int32)); + if (Buf == NULL) + return 1; + memset((char *)Buf, 0, nVnodes * sizeof(afs_int32)); + STREAM_SEEK(afile, offset = vcp->diskSize, 0); + while (1) { + code = STREAM_READ(vnode, vcp->diskSize, 1, afile); + if (code != 1) { + break; + } + if (vnode->type != vNull && VNDISK_GET_INO(vnode)) { + Buf[(offset >> vcp->logSize) - 1] = offset; + cnt++; + } + offset += vcp->diskSize; + } + *Bufp = Buf; + *sizep = nVnodes; + } + } + STREAM_CLOSE(afile); + FDH_CLOSE(fdP); + return 0; +} + + +int +RestoreVolume(register struct rx_call *call, Volume * avp, int incremental, + struct restoreCookie *cookie) +{ + VolumeDiskData vol; + struct DumpHeader header; + afs_uint32 endMagic; + Error error = 0, vupdate; + register Volume *vp; + struct iod iod; + register struct iod *iodp = &iod; + afs_int32 *b1 = 0, *b2 = 0; + int s1 = 0, s2 = 0, delo = 0, tdelo; + int tag; + + iod_Init(iodp, call); + + vp = avp; + if (!ReadDumpHeader(iodp, &header)) { + Log("1 Volser: RestoreVolume: Error reading header file for dump; aborted\n"); + return VOLSERREAD_DUMPERROR; + } + if (iod_getc(iodp) != D_VOLUMEHEADER) { + Log("1 Volser: RestoreVolume: Volume header missing from dump; not restored\n"); + return VOLSERREAD_DUMPERROR; + } + if (ReadVolumeHeader(iodp, &vol) == VOLSERREAD_DUMPERROR) + return VOLSERREAD_DUMPERROR; + + delo = ProcessIndex(vp, vLarge, &b1, &s1, 0); + if (!delo) + delo = ProcessIndex(vp, vSmall, &b2, &s2, 0); + if (delo) { + if (b1) + free((char *)b1); + if (b2) + free((char *)b2); + b1 = b2 = 0; + } + + strncpy(vol.name, cookie->name, VOLSER_OLDMAXVOLNAME); + vol.type = cookie->type; + vol.cloneId = cookie->clone; + vol.parentId = cookie->parent; + + + tdelo = delo; + while (1) { + if (ReadVnodes(iodp, vp, 0, b1, s1, b2, s2, tdelo)) { + error = VOLSERREAD_DUMPERROR; + goto clean; + } + tag = iod_getc(iodp); + if (tag != D_VOLUMEHEADER) + break; + if (ReadVolumeHeader(iodp, &vol) == VOLSERREAD_DUMPERROR) { + error = VOLSERREAD_DUMPERROR; + goto out; + } + tdelo = -1; + } + if (tag != D_DUMPEND || !ReadInt32(iodp, &endMagic) + || endMagic != DUMPENDMAGIC) { + Log("1 Volser: RestoreVolume: End of dump not found; restore aborted\n"); + error = VOLSERREAD_DUMPERROR; + goto clean; + } + + + if (iod_getc(iodp) != EOF) { + Log("1 Volser: RestoreVolume: Unrecognized postamble in dump; restore aborted\n"); + error = VOLSERREAD_DUMPERROR; + goto clean; + } + + if (!delo) { + ProcessIndex(vp, vLarge, &b1, &s1, 1); + ProcessIndex(vp, vSmall, &b2, &s2, 1); + } + + clean: + ClearVolumeStats(&vol); + CopyVolumeHeader(&vol, &V_disk(vp)); + V_destroyMe(vp) = 0; + VUpdateVolume(&vupdate, vp); + if (vupdate) { + Log("1 Volser: RestoreVolume: Unable to rewrite volume header; restore aborted\n"); + error = VOLSERREAD_DUMPERROR; + goto out; + } + out: + /* Free the malloced space above */ + if (b1) + free((char *)b1); + if (b2) + free((char *)b2); + return error; +} + +static int +ReadVnodes(register struct iod *iodp, Volume * vp, int incremental, + afs_int32 * Lbuf, afs_int32 s1, afs_int32 * Sbuf, afs_int32 s2, + afs_int32 delo) +{ + afs_int32 vnodeNumber; + char buf[SIZEOF_LARGEDISKVNODE]; + register tag; + struct VnodeDiskObject *vnode = (struct VnodeDiskObject *)buf; + struct VnodeDiskObject oldvnode; + int idx; + VnodeClass class; + struct VnodeClassInfo *vcp; + IHandle_t *tmpH; + FdHandle_t *fdP; + Inode nearInode; + + tag = iod_getc(iodp); + V_pref(vp, nearInode); + while (tag == D_VNODE) { + int haveStuff = 0; + memset(buf, 0, sizeof(buf)); + if (!ReadInt32(iodp, (afs_uint32 *) & vnodeNumber)) + break; + + ReadInt32(iodp, &vnode->uniquifier); + while ((tag = iod_getc(iodp)) > D_MAX && tag != EOF) { + haveStuff = 1; + switch (tag) { + case 't': + vnode->type = (VnodeType) iod_getc(iodp); + break; + case 'l': + { + unsigned short tlc; + ReadShort(iodp, &tlc); + vnode->linkCount = (signed int)tlc; + } + break; + case 'v': + ReadInt32(iodp, &vnode->dataVersion); + break; + case 'm': + ReadInt32(iodp, &vnode->unixModifyTime); + break; + case 's': + ReadInt32(iodp, &vnode->serverModifyTime); + break; + case 'a': + ReadInt32(iodp, &vnode->author); + break; + case 'o': + ReadInt32(iodp, &vnode->owner); + break; + case 'g': + ReadInt32(iodp, (afs_uint32 *) & vnode->group); + break; + case 'b':{ + unsigned short modeBits; + ReadShort(iodp, &modeBits); + vnode->modeBits = (unsigned int)modeBits; + break; + } + case 'p': + ReadInt32(iodp, &vnode->parent); + break; + case 'A': + ReadByteString(iodp, (byte *) VVnodeDiskACL(vnode), + VAclDiskSize(vnode)); + acl_NtohACL(VVnodeDiskACL(vnode)); + break; +#ifdef AFS_LARGEFILE_ENV + case 'h': +#endif + case 'f':{ + Inode ino; + Error error; + afs_fsize_t vnodeLength; + + ino = + IH_CREATE(V_linkHandle(vp), V_device(vp), + VPartitionPath(V_partition(vp)), nearInode, + V_parentId(vp), vnodeNumber, + vnode->uniquifier, vnode->dataVersion); + if (!VALID_INO(ino)) { + perror("unable to allocate inode"); + Log("1 Volser: ReadVnodes: Restore aborted\n"); + return VOLSERREAD_DUMPERROR; + } + nearInode = ino; + VNDISK_SET_INO(vnode, ino); + IH_INIT(tmpH, vp->device, V_parentId(vp), ino); + fdP = IH_OPEN(tmpH); + if (fdP == NULL) { + IH_RELEASE(tmpH); + return VOLSERREAD_DUMPERROR; + } + vnodeLength = + volser_WriteFile(vnodeNumber, iodp, fdP, tag, &error); + VNDISK_SET_LEN(vnode, vnodeLength); + FDH_REALLYCLOSE(fdP); + IH_RELEASE(tmpH); + if (error) { + Log("1 Volser: ReadVnodes: IDEC inode %llu\n", + (afs_uintmax_t) ino); + IH_DEC(V_linkHandle(vp), ino, V_parentId(vp)); + return VOLSERREAD_DUMPERROR; + } + break; + } + } + } + + class = vnodeIdToClass(vnodeNumber); + vcp = &VnodeClassInfo[class]; + + /* Mark this vnode as in this dump - so we don't delete it later */ + if (!delo) { + idx = (vnodeIndexOffset(vcp, vnodeNumber) >> vcp->logSize) - 1; + if (class == vLarge) { + if (Lbuf && (idx < s1)) + Lbuf[idx] = 0; + } else { + if (Sbuf && (idx < s2)) + Sbuf[idx] = 0; + } + } + + if (haveStuff) { + FdHandle_t *fdP = IH_OPEN(vp->vnodeIndex[class].handle); + if (fdP == NULL) { + Log("1 Volser: ReadVnodes: Error opening vnode index; restore aborted\n"); + return VOLSERREAD_DUMPERROR; + } + if (FDH_SEEK(fdP, vnodeIndexOffset(vcp, vnodeNumber), SEEK_SET) < + 0) { + Log("1 Volser: ReadVnodes: Error seeking into vnode index; restore aborted\n"); + FDH_REALLYCLOSE(fdP); + return VOLSERREAD_DUMPERROR; + } + if (FDH_READ(fdP, &oldvnode, sizeof(oldvnode)) == + sizeof(oldvnode)) { + if (oldvnode.type != vNull && VNDISK_GET_INO(&oldvnode)) { + IH_DEC(V_linkHandle(vp), VNDISK_GET_INO(&oldvnode), + V_parentId(vp)); + } + } + vnode->vnodeMagic = vcp->magic; + if (FDH_SEEK(fdP, vnodeIndexOffset(vcp, vnodeNumber), SEEK_SET) < + 0) { + Log("1 Volser: ReadVnodes: Error seeking into vnode index; restore aborted\n"); + FDH_REALLYCLOSE(fdP); + return VOLSERREAD_DUMPERROR; + } + if (FDH_WRITE(fdP, vnode, vcp->diskSize) != vcp->diskSize) { + Log("1 Volser: ReadVnodes: Error writing vnode index; restore aborted\n"); + FDH_REALLYCLOSE(fdP); + return VOLSERREAD_DUMPERROR; + } + FDH_CLOSE(fdP); + } + } + iod_ungetc(iodp, tag); + + + return 0; +} + + +/* called with disk file only. Note that we don't have to worry about rx_Read + * needing to read an ungetc'd character, since the ReadInt32 will have read + * it instead. + */ +static afs_fsize_t +volser_WriteFile(int vn, struct iod *iodp, FdHandle_t * handleP, int tag, + Error * status) +{ + afs_int32 code; + afs_fsize_t filesize; + afs_fsize_t written = 0; + register afs_uint32 size = 8192; + register afs_fsize_t nbytes; + unsigned char *p; + + + *status = 0; +#ifdef AFS_64BIT_ENV + { + afs_uint32 filesize_high = 0L, filesize_low = 0L; +#ifdef AFS_LARGEFILE_ENV + if (tag == 'h') { + if (!ReadInt32(iodp, &filesize_high)) { + *status = 1; + return 0; + } + } +#endif /* !AFS_LARGEFILE_ENV */ + if (!ReadInt32(iodp, &filesize_low)) { + *status = 1; + return 0; + } + FillInt64(filesize, filesize_high, filesize_low); + } +#else /* !AFS_64BIT_ENV */ + if (!ReadInt32(iodp, &filesize)) { + *status = 1; + return (0); + } +#endif /* !AFS_64BIT_ENV */ + p = (unsigned char *)malloc(size); + if (p == NULL) { + *status = 2; + return (0); + } + for (nbytes = filesize; nbytes; nbytes -= size) { + if (nbytes < size) + size = nbytes; + + if ((code = iod_Read(iodp, p, size)) != size) { + Log("1 Volser: WriteFile: Error reading dump file %d size=%llu nbytes=%u (%d of %u); restore aborted\n", vn, (afs_uintmax_t) filesize, nbytes, code, size); + *status = 3; + break; + } + code = FDH_WRITE(handleP, p, size); + if (code > 0) + written += code; + if (code != size) { + Log("1 Volser: WriteFile: Error creating file in volume; restore aborted\n"); + *status = 4; + break; + } + } + free(p); + return (written); +} + +static int +ReadDumpHeader(register struct iod *iodp, struct DumpHeader *hp) +{ + register tag; + afs_uint32 beginMagic; + if (iod_getc(iodp) != D_DUMPHEADER || !ReadInt32(iodp, &beginMagic) + || !ReadInt32(iodp, (afs_uint32 *) & hp->version) + || beginMagic != DUMPBEGINMAGIC) + return 0; + hp->volumeId = 0; + hp->nDumpTimes = 0; + while ((tag = iod_getc(iodp)) > D_MAX) { + unsigned short arrayLength; + register int i; + switch (tag) { + case 'v': + if (!ReadInt32(iodp, &hp->volumeId)) + return 0; + break; + case 'n': + ReadString(iodp, hp->volumeName, sizeof(hp->volumeName)); + break; + case 't': + if (!ReadShort(iodp, &arrayLength)) + return 0; + hp->nDumpTimes = (arrayLength >> 1); + for (i = 0; i < hp->nDumpTimes; i++) + if (!ReadInt32(iodp, (afs_uint32 *) & hp->dumpTimes[i].from) + || !ReadInt32(iodp, (afs_uint32 *) & hp->dumpTimes[i].to)) + return 0; + break; + } + } + if (!hp->volumeId || !hp->nDumpTimes) { + return 0; + } + iod_ungetc(iodp, tag); + return 1; +} + + +/* ----- Below are the calls that calculate dump size ----- */ + +static int +SizeDumpVolumeHeader(register struct iod *iodp, register Volume * vp, + register struct volintSize *v_size) +{ + int code = 0; + static char nullString[1] = ""; /*The ``contents'' of motd */ + afs_uint64 addvar; + +/* if (!code) code = DumpTag(iodp, D_VOLUMEHEADER); */ + FillInt64(addvar,0, 1); + AddUInt64(v_size->dump_size, addvar, &v_size->dump_size); +/* if (!code) {code = DumpInt32(iodp, 'i',V_id(vp));} */ + FillInt64(addvar,0, 5); + AddUInt64(v_size->dump_size, addvar, &v_size->dump_size); +/* if (!code) code = DumpInt32(iodp, 'v',V_stamp(vp).version); */ + FillInt64(addvar,0, 5); + AddUInt64(v_size->dump_size, addvar, &v_size->dump_size); +/* if (!code) code = DumpString(iodp, 'n',V_name(vp)); */ + FillInt64(addvar,0, (2 + strlen(V_name(vp)))); + AddUInt64(v_size->dump_size, addvar, &v_size->dump_size); +/* if (!code) code = DumpBool(iodp, 's',V_inService(vp)); */ + FillInt64(addvar,0, 2); + AddUInt64(v_size->dump_size, addvar, &v_size->dump_size); +/* if (!code) code = DumpBool(iodp, 'b',V_blessed(vp)); */ + FillInt64(addvar,0, 2); + AddUInt64(v_size->dump_size, addvar, &v_size->dump_size); +/* if (!code) code = DumpInt32(iodp, 'u',V_uniquifier(vp)); */ + FillInt64(addvar,0, 5); + AddUInt64(v_size->dump_size, addvar, &v_size->dump_size); +/* if (!code) code = DumpByte(iodp, 't',(byte)V_type(vp)); */ + FillInt64(addvar,0, 2); + AddUInt64(v_size->dump_size, addvar, &v_size->dump_size); +/* if (!code){ code = DumpInt32(iodp, 'p',V_parentId(vp));} */ + FillInt64(addvar,0, 5); + AddUInt64(v_size->dump_size, addvar, &v_size->dump_size); +/* if (!code) code = DumpInt32(iodp, 'c',V_cloneId(vp)); */ + FillInt64(addvar,0, 5); + AddUInt64(v_size->dump_size, addvar, &v_size->dump_size); +/* if (!code) code = DumpInt32(iodp, 'q',V_maxquota(vp)); */ + FillInt64(addvar,0, 5); + AddUInt64(v_size->dump_size, addvar, &v_size->dump_size); +/* if (!code) code = DumpInt32(iodp, 'm',V_minquota(vp)); */ + FillInt64(addvar,0, 5); + AddUInt64(v_size->dump_size, addvar, &v_size->dump_size); +/* if (!code) code = DumpInt32(iodp, 'd',V_diskused(vp)); */ + FillInt64(addvar,0, 5); + AddUInt64(v_size->dump_size, addvar, &v_size->dump_size); +/* if (!code) code = DumpInt32(iodp, 'f',V_filecount(vp)); */ + FillInt64(addvar,0, 5); + AddUInt64(v_size->dump_size, addvar, &v_size->dump_size); +/* if (!code) code = DumpInt32(iodp, 'a', V_accountNumber(vp)); */ + FillInt64(addvar,0, 5); + AddUInt64(v_size->dump_size, addvar, &v_size->dump_size); +/* if (!code) code = DumpInt32(iodp, 'o', V_owner(vp)); */ + FillInt64(addvar,0, 5); + AddUInt64(v_size->dump_size, addvar, &v_size->dump_size); +/* if (!code) code = DumpInt32(iodp, 'C',V_creationDate(vp)); /\* Rw volume creation date *\/ */ + FillInt64(addvar,0, 5); + AddUInt64(v_size->dump_size, addvar, &v_size->dump_size); +/* if (!code) code = DumpInt32(iodp, 'A',V_accessDate(vp)); */ + FillInt64(addvar,0, 5); + AddUInt64(v_size->dump_size, addvar, &v_size->dump_size); +/* if (!code) code = DumpInt32(iodp, 'U',V_updateDate(vp)); */ + FillInt64(addvar,0, 5); + AddUInt64(v_size->dump_size, addvar, &v_size->dump_size); +/* if (!code) code = DumpInt32(iodp, 'E',V_expirationDate(vp)); */ + FillInt64(addvar,0, 5); + AddUInt64(v_size->dump_size, addvar, &v_size->dump_size); +/* if (!code) code = DumpInt32(iodp, 'B',V_backupDate(vp)); /\* Rw volume backup clone date *\/ */ + FillInt64(addvar,0, 5); + AddUInt64(v_size->dump_size, addvar, &v_size->dump_size); +/* if (!code) code = DumpString(iodp, 'O',V_offlineMessage(vp)); */ + FillInt64(addvar,0, (2 + strlen(V_offlineMessage(vp)))); + AddUInt64(v_size->dump_size, addvar, &v_size->dump_size); +/* /\* */ +/* * We do NOT dump the detailed volume statistics residing in the old */ +/* * motd field, since we cannot tell from the info in a dump whether */ +/* * statistics data has been put there. Instead, we dump a null string, */ +/* * just as if that was what the motd contained. */ +/* *\/ */ +/* if (!code) code = DumpString(iodp, 'M', nullString); */ + FillInt64(addvar,0, (2 + strlen(nullString))); + AddUInt64(v_size->dump_size, addvar, &v_size->dump_size); +/* if (!code) code = DumpArrayInt32(iodp, 'W', (afs_uint32 *)V_weekUse(vp), sizeof(V_weekUse(vp))/sizeof(V_weekUse(vp)[0])); */ + FillInt64(addvar,0, (3 + 4 * (sizeof(V_weekUse(vp)) / sizeof(V_weekUse(vp)[0])))); + AddUInt64(v_size->dump_size, addvar, &v_size->dump_size); +/* if (!code) code = DumpInt32(iodp, 'D', V_dayUseDate(vp)); */ + FillInt64(addvar,0, 5); + AddUInt64(v_size->dump_size, addvar, &v_size->dump_size); +/* if (!code) code = DumpInt32(iodp, 'Z', V_dayUse(vp)); */ + FillInt64(addvar,0, 5); + AddUInt64(v_size->dump_size, addvar, &v_size->dump_size); + return code; +} + +static int +SizeDumpEnd(register struct iod *iodp, register struct volintSize *v_size) +{ + int code = 0; + afs_uint64 addvar; + FillInt64(addvar,0, 5); + AddUInt64(v_size->dump_size, addvar, &v_size->dump_size); + return code; +} + +int +SizeDumpVolume(register struct rx_call *call, register Volume * vp, + afs_int32 fromtime, int dumpAllDirs, + register struct volintSize *v_size) +{ + int code = 0; + register struct iod *iodp = (struct iod *)0; +/* iod_Init(iodp, call); */ + + if (!code) + code = SizeDumpDumpHeader(iodp, vp, fromtime, v_size); + if (!code) + code = SizeDumpPartial(iodp, vp, fromtime, dumpAllDirs, v_size); + if (!code) + code = SizeDumpEnd(iodp, v_size); + + return code; +} + +static int +SizeDumpDumpHeader(register struct iod *iodp, register Volume * vp, + afs_int32 fromtime, register struct volintSize *v_size) +{ + int code = 0; + int UseLatestReadOnlyClone = 1; +/* afs_int32 dumpTimes[2]; */ + afs_uint64 addvar; +/* iodp->device = vp->device; */ +/* iodp->parentId = V_parentId(vp); */ +/* iodp->dumpPartition = vp->partition; */ + + ZeroInt64(v_size->dump_size); /* initialize the size */ +/* if (!code) code = DumpDouble(iodp, D_DUMPHEADER, DUMPBEGINMAGIC, DUMPVERSION); */ + FillInt64(addvar,0, 9); + AddUInt64(v_size->dump_size, addvar, &v_size->dump_size); +/* if (!code) code = DumpInt32(iodp, 'v', UseLatestReadOnlyClone? V_id(vp): V_parentId(vp)); */ + FillInt64(addvar,0, 5); + AddUInt64(v_size->dump_size, addvar, &v_size->dump_size); +/* if (!code) code = DumpString(iodp, 'n',V_name(vp)); */ + FillInt64(addvar,0, (2 + strlen(V_name(vp)))); + AddUInt64(v_size->dump_size, addvar, &v_size->dump_size); +/* dumpTimes[0] = fromtime; */ +/* dumpTimes[1] = V_backupDate(vp); /\* Until the time the clone was made *\/ */ +/* if (!code) code = DumpArrayInt32(iodp, 't', (afs_uint32 *)dumpTimes, 2); */ + FillInt64(addvar,0, (3 + 4 * 2)); + AddUInt64(v_size->dump_size, addvar, &v_size->dump_size); + return code; +} + +static int +SizeDumpVnode(register struct iod *iodp, struct VnodeDiskObject *v, int volid, + int vnodeNumber, int dumpEverything, + register struct volintSize *v_size) +{ + int code = 0; + afs_uint64 addvar; + + if (!v || v->type == vNull) + return code; +/* if (!code) code = DumpDouble(iodp, D_VNODE, vnodeNumber, v->uniquifier); */ + FillInt64(addvar,0, 9); + AddUInt64(v_size->dump_size, addvar, &v_size->dump_size); + if (!dumpEverything) + return code; +/* if (!code) code = DumpByte(iodp, 't',(byte)v->type); */ + FillInt64(addvar,0, 2); + AddUInt64(v_size->dump_size, addvar, &v_size->dump_size); +/* if (!code) code = DumpShort(iodp, 'l', v->linkCount); /\* May not need this *\/ */ + FillInt64(addvar,0, 3); + AddUInt64(v_size->dump_size, addvar, &v_size->dump_size); +/* if (!code) code = DumpInt32(iodp, 'v', v->dataVersion); */ + FillInt64(addvar,0, 5); + AddUInt64(v_size->dump_size, addvar, &v_size->dump_size); +/* if (!code) code = DumpInt32(iodp, 'm', v->unixModifyTime); */ + FillInt64(addvar,0, 5); + AddUInt64(v_size->dump_size, addvar, &v_size->dump_size); +/* if (!code) code = DumpInt32(iodp, 'a', v->author); */ + FillInt64(addvar,0, 5); + AddUInt64(v_size->dump_size, addvar, &v_size->dump_size); +/* if (!code) code = DumpInt32(iodp, 'o', v->owner); */ + FillInt64(addvar,0, 5); + AddUInt64(v_size->dump_size, addvar, &v_size->dump_size); +/* if (!code && v->group) code = DumpInt32(iodp, 'g', v->group); /\* default group is 0 *\/ */ + if (v->group) { + FillInt64(addvar,0, 5); + AddUInt64(v_size->dump_size, addvar, &v_size->dump_size); + } +/* if (!code) code = DumpShort(iodp, 'b', v->modeBits); */ + FillInt64(addvar,0, 3); + AddUInt64(v_size->dump_size, addvar, &v_size->dump_size); +/* if (!code) code = DumpInt32(iodp, 'p', v->parent); */ + FillInt64(addvar,0, 5); + AddUInt64(v_size->dump_size, addvar, &v_size->dump_size); +/* if (!code) code = DumpInt32(iodp, 's', v->serverModifyTime); */ + FillInt64(addvar,0, 5); + AddUInt64(v_size->dump_size, addvar, &v_size->dump_size); + if (v->type == vDirectory) { +/* acl_HtonACL(VVnodeDiskACL(v)); */ +/* if (!code) code = DumpByteString(iodp, 'A', (byte *) VVnodeDiskACL(v), VAclDiskSize(v)); */ + FillInt64(addvar,0, (1 + VAclDiskSize(v))); + AddUInt64(v_size->dump_size, addvar, &v_size->dump_size); + } + + if (VNDISK_GET_INO(v)) { + FillInt64(addvar,0, (v->length + 5)); + AddUInt64(v_size->dump_size, addvar, &v_size->dump_size); + } + return code; +} + +/* A partial dump (no dump header) */ +static int +SizeDumpPartial(register struct iod *iodp, register Volume * vp, + afs_int32 fromtime, int dumpAllDirs, + register struct volintSize *v_size) +{ + int code = 0; + if (!code) + code = SizeDumpVolumeHeader(iodp, vp, v_size); + if (!code) + code = + SizeDumpVnodeIndex(iodp, vp, vLarge, fromtime, dumpAllDirs, + v_size); + if (!code) + code = SizeDumpVnodeIndex(iodp, vp, vSmall, fromtime, 0, v_size); + return code; +} + +static int +SizeDumpVnodeIndex(register struct iod *iodp, Volume * vp, VnodeClass class, + afs_int32 fromtime, int forcedump, + register struct volintSize *v_size) +{ + register int code = 0; + register struct VnodeClassInfo *vcp = &VnodeClassInfo[class]; + char buf[SIZEOF_LARGEDISKVNODE]; + struct VnodeDiskObject *vnode = (struct VnodeDiskObject *)buf; + StreamHandle_t *file; + FdHandle_t *fdP; + int size; + int flag; + register int vnodeIndex, nVnodes; + + fdP = IH_OPEN(vp->vnodeIndex[class].handle); + assert(fdP != NULL); + file = FDH_FDOPEN(fdP, "r+"); + assert(file != NULL); + size = OS_SIZE(fdP->fd_fd); + assert(size != -1); + nVnodes = (size / vcp->diskSize) - 1; + if (nVnodes > 0) { + assert((nVnodes + 1) * vcp->diskSize == size); + assert(STREAM_SEEK(file, vcp->diskSize, 0) == 0); + } else + nVnodes = 0; + for (vnodeIndex = 0; + nVnodes && STREAM_READ(vnode, vcp->diskSize, 1, file) == 1 && !code; + nVnodes--, vnodeIndex++) { + flag = forcedump || (vnode->serverModifyTime >= fromtime); + /* Note: the >= test is very important since some old volumes may not have + * a serverModifyTime. For an epoch dump, this results in 0>=0 test, which + * does dump the file! */ + if (!code) + code = + SizeDumpVnode(iodp, vnode, V_id(vp), + bitNumberToVnodeNumber(vnodeIndex, class), flag, + v_size); + } + STREAM_CLOSE(file); + FDH_CLOSE(fdP); + return code; +} diff --git a/src/volser/lockdata.h b/src/volser/lockdata.h new file mode 100644 index 000000000..7c8df8b8c --- /dev/null +++ b/src/volser/lockdata.h @@ -0,0 +1,30 @@ +/* + * Copyright 2000, International Business Machines Corporation and others. + * All Rights Reserved. + * + * This software has been released under the terms of the IBM Public + * License. For details, see the LICENSE file in the top-level source + * directory or online at http://www.openafs.org/dl/license10.html + */ + +#ifndef _LOCKDATA_ +#define _LOCKDATA_ 1 + +#define ZERO 2147617029 /*to be replaced by 0L */ +#define N_SECURITY_OBJECTS 1 + + +struct aqueue { + char name[VOLSER_MAXVOLNAME]; /* related to max volname allowed in the vldb */ + afs_int32 ids[3]; + afs_int32 copyDate[3]; + int isValid[3]; + struct aqueue *next; +}; + +struct qHead { + int count; + struct aqueue *next; +}; + +#endif /* _LOCKDATA_ */ diff --git a/src/volser/lockprocs.c b/src/volser/lockprocs.c new file mode 100644 index 000000000..9d175bd25 --- /dev/null +++ b/src/volser/lockprocs.c @@ -0,0 +1,261 @@ +/* + * Copyright 2000, International Business Machines Corporation and others. + * All Rights Reserved. + * + * This software has been released under the terms of the IBM Public + * License. For details, see the LICENSE file in the top-level source + * directory or online at http://www.openafs.org/dl/license10.html + */ + +/* + * Module: lockprocs.c + * System: Volser + * Instituition: ITC, CMU + * Date: December, 88 + */ + +#include +#include + +RCSID + ("$Header: /cvs/openafs/src/volser/lockprocs.c,v 1.8 2003/07/15 23:17:48 shadow Exp $"); + +#include +#ifdef AFS_NT40_ENV +#include +#else +#include +#endif +#ifdef HAVE_STRING_H +#include +#else +#ifdef HAVE_STRINGS_H +#include +#endif +#endif +#include +#include +#include +#include +#include +#include +#include "volint.h" +#include "volser.h" +#include "lockdata.h" + +/* Finds an index in VLDB entry that matches the volume type, server, and partition. + * If type is zero, will match first index of ANY type (RW, BK, or RO). + * If server is zero, will match first index of ANY server and partition + * Zero is a valid partition field. + */ +int +FindIndex(entry, server, part, type) + struct nvldbentry *entry; + afs_int32 server, part, type; +{ + int e; + afs_int32 error = 0; + + for (e = 0; (e < entry->nServers) && !error; e++) { + if (!type || (entry->serverFlags[e] & type)) { + if ((!server || (entry->serverPartition[e] == part)) + && (!server + || VLDB_IsSameAddrs(entry->serverNumber[e], server, + &error))) + break; + if (type == ITSRWVOL) + return -1; /* quit when we are looking for RW entry (there's only 1) */ + } + } + + if (error) { + fprintf(STDERR, + "Failed to get info about server's %d address(es) from vlserver (err=%d)\n", + entry->serverNumber[e], error); + return -1; + } + + if (e >= entry->nServers) + return -1; /* Didn't find it */ + + return e; /* return the index */ +} + +/* Changes the rw site only */ +void +SetAValue(entry, oserver, opart, nserver, npart, type) + struct nvldbentry *entry; + afs_int32 oserver, opart, nserver, npart, type; +{ + int e; + afs_int32 error = 0; + + e = FindIndex(entry, oserver, opart, type); + if (e == -1) + return; /* If didn't find it, just return */ + + entry->serverNumber[e] = nserver; + entry->serverPartition[e] = npart; + + /* Now move rest of entries up */ + if ((nserver == 0L) && (npart == 0L)) { + for (e++; e < entry->nServers; e++) { + entry->serverNumber[e - 1] = entry->serverNumber[e]; + entry->serverPartition[e - 1] = entry->serverPartition[e]; + entry->serverFlags[e - 1] = entry->serverFlags[e]; + } + } +} + +/* Changes the RW site only */ +Lp_SetRWValue(entry, oserver, opart, nserver, npart) + struct nvldbentry *entry; + afs_int32 oserver, opart, nserver, npart; +{ + SetAValue(entry, oserver, opart, nserver, npart, ITSRWVOL); +} + +/* Changes the RO site only */ +Lp_SetROValue(entry, oserver, opart, nserver, npart) + struct nvldbentry *entry; + afs_int32 oserver, opart, nserver, npart; +{ + SetAValue(entry, oserver, opart, nserver, npart, ITSROVOL); +} + +/* Returns success if this server and partition matches the RW entry */ +Lp_Match(server, part, entry) + afs_int32 server, part; + struct nvldbentry *entry; +{ + if (FindIndex(entry, server, part, ITSRWVOL) == -1) + return 0; + return 1; +} + +/* Return the index of the RO entry (plus 1) if it exists, else return 0 */ +Lp_ROMatch(server, part, entry) + afs_int32 server, part; + struct nvldbentry *entry; +{ + return (FindIndex(entry, server, part, ITSROVOL) + 1); +} + +/* Return the index of the RW entry if it exists, else return -1 */ +Lp_GetRwIndex(entry) + struct nvldbentry *entry; +{ + return (FindIndex(entry, 0, 0, ITSRWVOL)); +} + +/*initialize queue pointed by */ +Lp_QInit(ahead) + struct qHead *ahead; +{ + ahead->count = 0; + ahead->next = NULL; +} + +/*add in front of queue */ +Lp_QAdd(ahead, elem) + struct qHead *ahead; + struct aqueue *elem; +{ + struct aqueue *temp; + + if (ahead->count == 0) { + ahead->count += 1; + ahead->next = elem; + elem->next = NULL; + } else { + temp = ahead->next; + ahead->count += 1; + ahead->next = elem; + elem->next = temp; + } +} + +Lp_QScan(ahead, id, success, elem) + struct qHead *ahead; + struct aqueue **elem; + afs_int32 id; + int *success; +{ + struct aqueue *cptr; + + cptr = ahead->next; + while (cptr != NULL) { + if (cptr->ids[RWVOL] == id) { + *success = 1; + *elem = cptr; + return 0; + } + cptr = cptr->next; + } + *success = 0; + return 0; +} + +/*return the element in the beginning of the queue , free +*the space used by that element . indicates if enumeration was ok*/ +Lp_QEnumerate(ahead, success, elem) + struct qHead *ahead; + struct aqueue *elem; + int *success; +{ + int i; + struct aqueue *temp; + + if (ahead->count > 0) { /*more elements left */ + ahead->count -= 1; + temp = ahead->next; + ahead->next = ahead->next->next; + strncpy(elem->name, temp->name, VOLSER_OLDMAXVOLNAME); + for (i = 0; i < 3; i++) { + elem->ids[i] = temp->ids[i]; + elem->copyDate[i] = temp->copyDate[i]; + elem->isValid[i] = temp->isValid[i]; + } + elem->next = NULL; + *success = 1; + free(temp); + } else /*queue is empty */ + *success = 0; +} + +Lp_QTraverse(ahead) + struct qHead *ahead; +{ + int count; + struct aqueue *old, *new; + + old = ahead->next; + new = old->next; + count = ahead->count; + printf + ("traversing the internal queue, which groups all the related volumes on a per partition basis\n"); + while (count > 0) { + printf("---------------------------\n"); + printf("%s RW-Id %lu", old->name, (unsigned long)old->ids[RWVOL]); + if (old->isValid[RWVOL]) + printf(" valid "); + else + printf(" invalid "); + printf("RO-Id %lu", (unsigned long)old->ids[ROVOL]); + if (old->isValid[ROVOL]) + printf(" valid "); + else + printf(" invalid "); + printf("BACKUP-Id %lu", (unsigned long)old->ids[BACKVOL]); + if (old->isValid[BACKVOL]) + printf(" valid "); + else + printf(" invalid "); + printf("\n"); + printf("---------------------------\n"); + old = new; + if (count != 1) + new = new->next; + count--; + } +} diff --git a/src/volser/physio.c b/src/volser/physio.c new file mode 100644 index 000000000..f8f5a91f7 --- /dev/null +++ b/src/volser/physio.c @@ -0,0 +1,176 @@ +/* + * Copyright 2000, International Business Machines Corporation and others. + * All Rights Reserved. + * + * This software has been released under the terms of the IBM Public + * License. For details, see the LICENSE file in the top-level source + * directory or online at http://www.openafs.org/dl/license10.html + */ + +#include +#include + +RCSID + ("$Header: /cvs/openafs/src/volser/physio.c,v 1.11 2003/12/09 23:07:57 shadow Exp $"); + +#include +#ifdef AFS_NT40_ENV +#include +#else +#include +#include +#include +#endif +#ifdef HAVE_STRING_H +#include +#else +#ifdef HAVE_STRINGS_H +#include +#endif +#endif +#ifdef AFS_SUN5_ENV +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "vol.h" + +/* returns 0 on success, errno on failure */ +int +ReallyRead(DirHandle * file, int block, char *data) +{ + FdHandle_t *fdP; + int code; + errno = 0; + fdP = IH_OPEN(file->dirh_handle); + if (fdP == NULL) { + code = errno; + return code; + } + if (FDH_SEEK(fdP, block * AFS_PAGESIZE, SEEK_SET) < 0) { + code = errno; + FDH_REALLYCLOSE(fdP); + return code; + } + code = FDH_READ(fdP, data, AFS_PAGESIZE); + if (code != AFS_PAGESIZE) { + if (code < 0) + code = errno; + else + code = EIO; + FDH_REALLYCLOSE(fdP); + return code; + } + FDH_CLOSE(fdP); + return 0; +} + +/* returns 0 on success, errno on failure */ +int +ReallyWrite(DirHandle * file, int block, char *data) +{ + FdHandle_t *fdP; + extern int VolumeChanged; + int code; + + errno = 0; + + fdP = IH_OPEN(file->dirh_handle); + if (fdP == NULL) { + code = errno; + return code; + } + if (FDH_SEEK(fdP, block * AFS_PAGESIZE, SEEK_SET) < 0) { + code = errno; + FDH_REALLYCLOSE(fdP); + return code; + } + code = FDH_WRITE(fdP, data, AFS_PAGESIZE); + if (code != AFS_PAGESIZE) { + if (code < 0) + code = errno; + else + code = EIO; + FDH_REALLYCLOSE(fdP); + return code; + } + FDH_CLOSE(fdP); + VolumeChanged = 1; + return 0; +} + +/* SetSalvageDirHandle: + * Create a handle to a directory entry and reference it (IH_INIT). + * The handle needs to be dereferenced with the FidZap() routine. + */ +void +SetSalvageDirHandle(DirHandle * dir, afs_int32 volume, afs_int32 device, + Inode inode) +{ + private SalvageCacheCheck = 1; + memset(dir, 0, sizeof(DirHandle)); + + dir->dirh_volume = volume; + dir->dirh_device = device; + dir->dirh_inode = inode; + IH_INIT(dir->dirh_handle, device, volume, inode); + + /* Always re-read for a new dirhandle */ + dir->dirh_cacheCheck = SalvageCacheCheck++; +} + +void +FidZap(DirHandle * file) +{ + IH_RELEASE(file->dirh_handle); + memset(file, 0, sizeof(DirHandle)); +} + +void +FidZero(DirHandle * file) +{ + memset(file, 0, sizeof(DirHandle)); +} + +int +FidEq(DirHandle * afile, DirHandle * bfile) +{ + if (afile->dirh_volume != bfile->dirh_volume) + return 0; + if (afile->dirh_device != bfile->dirh_device) + return 0; + if (afile->dirh_cacheCheck != bfile->dirh_cacheCheck) + return 0; + if (afile->dirh_inode != bfile->dirh_inode) + return 0; + return 1; +} + +int +FidVolEq(DirHandle * afile, afs_int32 vid) +{ + if (afile->dirh_volume != vid) + return 0; + return 1; +} + +void +FidCpy(DirHandle * tofile, DirHandle * fromfile) +{ + *tofile = *fromfile; + IH_COPY(tofile->dirh_handle, fromfile->dirh_handle); +} + +void +Die(char *msg) +{ + printf("%s\n", msg); + assert(1 == 2); +} diff --git a/src/volser/restorevol.c b/src/volser/restorevol.c new file mode 100644 index 000000000..8e4782245 --- /dev/null +++ b/src/volser/restorevol.c @@ -0,0 +1,993 @@ +/* + * Copyright 2000, International Business Machines Corporation and others. + * All Rights Reserved. + * + * This software has been released under the terms of the IBM Public + * License. For details, see the LICENSE file in the top-level source + * directory or online at http://www.openafs.org/dl/license10.html + */ + +/* + * Read a vos dump and recreate the tree. + * + * restorevol [-file ] + * [-dir ] + * [-extension ] + * [-mountpoint ] + * [-umask ] + * + * 1. The dump file will be restored within the current or that specified with -dir. + * 2. Within this dir, a subdir is created. It's name is the RW volume name + * that was dumped. An extension can be appended to this directory name + * with -extension. + * 3. All mountpoints will appear as symbolic links to the volume. The + * pathname to the volume will be either that in -mountpoint, or -dir. + * Symbolic links remain untouched. + * 4. You can change your umask during the restore with -umask. Otherwise, it + * uses your current umask. Mode bits for directories are 0777 (then + * AND'ed with the umask). Mode bits for files are the owner mode bits + * duplicated accross group and user (then AND'ed with the umask). + * 5. For restores of full dumps, if a directory says it has a file and + * the file is not found, then a symbolic link "AFSFile-<#>" will + * appear in that restored tree. Restores of incremental dumps remove + * all these files at the end (expensive because it is a tree search). + * 6. If a file or directory was found in the dump but found not to be + * connected to the hierarchical tree, then the file or directory + * will be connected at the root of the tree as "__ORPHANEDIR__.<#>" + * or "__ORPHANFILE__.<#>". + * 7. ACLs are not restored. + * + */ + +#include +#include + +RCSID + ("$Header: /cvs/openafs/src/volser/restorevol.c,v 1.13 2003/12/05 08:36:06 shadow Exp $"); + +#include +#include +#include +#include +#include +#include +#include "volint.h" +#include "dump.h" +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_STRING_H +#include +#else +#ifdef HAVE_STRINGS_H +#include +#endif +#endif + + +char rootdir[MAXPATHLEN]; +char mntroot[MAXPATHLEN]; +#define ADIR "AFSDir-" +#define AFILE "AFSFile-" +#define ODIR "__ORPHANEDIR__." +#define OFILE "__ORPHANFILE__." + +int inc_dump = 0; +FILE *dumpfile; + +afs_int32 +readvalue(size) +{ + afs_int32 value, s; + int code; + char *ptr; + + value = 0; + ptr = (char *)&value; + + s = sizeof(value) - size; + if (size < 0) { + fprintf(stderr, "Too much data in afs_int32\n"); + return 0; + } + + code = fread(&ptr[s], 1, size, dumpfile); + if (code != size) + fprintf(stderr, "Code = %d; Errno = %d\n", code, errno); + + return (value); +} + +char +readchar() +{ + char value; + int code; + char *ptr; + + value = '\0'; + code = fread(&value, 1, 1, dumpfile); + if (code != 1) + fprintf(stderr, "Code = %d; Errno = %d\n", code, errno); + + return (value); +} + +#define BUFSIZE 16384 +char buf[BUFSIZE]; + +char +readdata(buffer, size) + char *buffer; + afs_sfsize_t size; +{ + int code; + afs_int32 s; + + if (!buffer) { + while (size > 0) { + s = (afs_int32) ((size > BUFSIZE) ? BUFSIZE : size); + code = fread(buf, 1, s, dumpfile); + if (code != s) + fprintf(stderr, "Code = %d; Errno = %d\n", code, errno); + size -= s; + } + } else { + code = fread(buffer, 1, size, dumpfile); + if (code != size) { + if (code < 0) + fprintf(stderr, "Code = %d; Errno = %d\n", code, errno); + else + fprintf(stderr, "Read %d bytes out of %lld\n", code, (afs_uintmax_t)size); + } + if ((code >= 0) && (code < BUFSIZE)) + buffer[size] = 0; /* Add null char at end */ + } +} + +afs_int32 +ReadDumpHeader(dh) + struct DumpHeader *dh; /* Defined in dump.h */ +{ + int code, i, done; + char tag, c; + afs_int32 magic; + +/* memset(&dh, 0, sizeof(dh)); */ + + magic = ntohl(readvalue(4)); + dh->version = ntohl(readvalue(4)); + + done = 0; + while (!done) { + tag = readchar(); + switch (tag) { + case 'v': + dh->volumeId = ntohl(readvalue(4)); + break; + + case 'n': + for (i = 0, c = 'a'; c != '\0'; i++) { + dh->volumeName[i] = c = readchar(); + } + dh->volumeName[i] = c; + break; + + case 't': + dh->nDumpTimes = ntohl(readvalue(2)) >> 1; + for (i = 0; i < dh->nDumpTimes; i++) { + dh->dumpTimes[i].from = ntohl(readvalue(4)); + dh->dumpTimes[i].to = ntohl(readvalue(4)); + } + break; + + default: + done = 1; + break; + } + } + + return ((afs_int32) tag); +} + +struct volumeHeader { + afs_int32 volumeId; + char volumeName[100]; + afs_int32 volType; + afs_int32 uniquifier; + afs_int32 parentVol; + afs_int32 cloneId; + afs_int32 maxQuota; + afs_int32 minQuota; + afs_int32 diskUsed; + afs_int32 fileCount; + afs_int32 accountNumber; + afs_int32 owner; + afs_int32 creationDate; + afs_int32 accessDate; + afs_int32 updateDate; + afs_int32 expirationDate; + afs_int32 backupDate; + afs_int32 dayUseDate; + afs_int32 dayUse; + afs_int32 weekCount; + afs_int32 weekUse[100]; /* weekCount of these */ + char motd[1024]; + int inService; + int blessed; + char message[1024]; +}; + +afs_int32 +ReadVolumeHeader(count) + afs_int32 count; +{ + struct volumeHeader vh; + int code, i, done, entries; + char tag, c; + +/* memset(&vh, 0, sizeof(vh)); */ + + done = 0; + while (!done) { + tag = readchar(); + switch (tag) { + case 'i': + vh.volumeId = ntohl(readvalue(4)); + break; + + case 'v': + ntohl(readvalue(4)); /* version stamp - ignore */ + break; + + case 'n': + for (i = 0, c = 'a'; c != '\0'; i++) { + vh.volumeName[i] = c = readchar(); + } + vh.volumeName[i] = c; + break; + + case 's': + vh.inService = ntohl(readvalue(1)); + break; + + case 'b': + vh.blessed = ntohl(readvalue(1)); + break; + + case 'u': + vh.uniquifier = ntohl(readvalue(4)); + break; + + case 't': + vh.volType = ntohl(readvalue(1)); + break; + + case 'p': + vh.parentVol = ntohl(readvalue(4)); + break; + + case 'c': + vh.cloneId = ntohl(readvalue(4)); + break; + + case 'q': + vh.maxQuota = ntohl(readvalue(4)); + break; + + case 'm': + vh.minQuota = ntohl(readvalue(4)); + break; + + case 'd': + vh.diskUsed = ntohl(readvalue(4)); + break; + + case 'f': + vh.fileCount = ntohl(readvalue(4)); + break; + + case 'a': + vh.accountNumber = ntohl(readvalue(4)); + break; + + case 'o': + vh.owner = ntohl(readvalue(4)); + break; + + case 'C': + vh.creationDate = ntohl(readvalue(4)); + break; + + case 'A': + vh.accessDate = ntohl(readvalue(4)); + break; + + case 'U': + vh.updateDate = ntohl(readvalue(4)); + break; + + case 'E': + vh.expirationDate = ntohl(readvalue(4)); + break; + + case 'B': + vh.backupDate = ntohl(readvalue(4)); + break; + + case 'O': + for (i = 0, c = 'a'; c != '\0'; i++) { + vh.message[i] = c = readchar(); + } + vh.volumeName[i] = c; + break; + + case 'W': + vh.weekCount = ntohl(readvalue(2)); + for (i = 0; i < vh.weekCount; i++) { + vh.weekUse[i] = ntohl(readvalue(4)); + } + break; + + case 'M': + for (i = 0, c = 'a'; c != '\0'; i++) { + vh.motd[i] = c = readchar(); + } + break; + + case 'D': + vh.dayUseDate = ntohl(readvalue(4)); + break; + + case 'Z': + vh.dayUse = ntohl(readvalue(4)); + break; + + default: + done = 1; + break; + } + } + + return ((afs_int32) tag); +} + +struct vNode { + afs_int32 vnode; + afs_int32 uniquifier; + afs_int32 type; + afs_int32 linkCount; + afs_int32 dataVersion; + afs_int32 unixModTime; + afs_int32 servModTime; + afs_int32 author; + afs_int32 owner; + afs_int32 group; + afs_int32 modebits; + afs_int32 parent; + char acl[192]; +#ifdef notdef + struct acl_accessList { + int size; /*size of this access list in bytes, including MySize itself */ + int version; /*to deal with upward compatibility ; <= ACL_ACLVERSION */ + int total; + int positive; /* number of positive entries */ + int negative; /* number of minus entries */ + struct acl_accessEntry { + int id; /*internally-used ID of user or group */ + int rights; /*mask */ + } entries[100]; + } acl; +#endif + afs_sfsize_t dataSize; +}; + +#define MAXNAMELEN 256 + +afs_int32 +ReadVNode(count) + afs_int32 count; +{ + struct vNode vn; + int code, i, done, entries; + char tag, c; + char dirname[MAXNAMELEN], linkname[MAXNAMELEN], lname[MAXNAMELEN]; + char parentdir[MAXNAMELEN], vflink[MAXNAMELEN]; + char filename[MAXNAMELEN], fname[MAXNAMELEN]; + int len; + afs_int32 vnode; + afs_int32 mode = 0; + +/* memset(&vn, 0, sizeof(vn)); */ + vn.dataSize = 0; + vn.vnode = 0; + vn.parent = 0; + vn.type = 0; + + vn.vnode = ntohl(readvalue(4)); + vn.uniquifier = ntohl(readvalue(4)); + + done = 0; + while (!done) { + tag = readchar(); + switch (tag) { + case 't': + vn.type = ntohl(readvalue(1)); + break; + + case 'l': + vn.linkCount = ntohl(readvalue(2)); + break; + + case 'v': + vn.dataVersion = ntohl(readvalue(4)); + break; + + case 'm': + vn.unixModTime = ntohl(readvalue(4)); + break; + + case 's': + vn.servModTime = ntohl(readvalue(4)); + break; + + case 'a': + vn.author = ntohl(readvalue(4)); + break; + + case 'o': + vn.owner = ntohl(readvalue(4)); + break; + + case 'g': + vn.group = ntohl(readvalue(4)); + break; + + case 'b': + vn.modebits = ntohl(readvalue(2)); + break; + + case 'p': + vn.parent = ntohl(readvalue(4)); + break; + + case 'A': + readdata(vn.acl, 192); /* Skip ACL data */ + break; + +#ifdef AFS_LARGEFILE_ENV + case 'h': + { + afs_uint32 hi, lo; + hi = ntohl(readvalue(4)); + lo = ntohl(readvalue(4)); + FillInt64(vn.dataSize, hi, lo); + } + goto common_vnode; +#endif /* !AFS_LARGEFILE_ENV */ + + case 'f': + vn.dataSize = ntohl(readvalue(4)); + + common_vnode: + /* parentdir is the name of this dir's vnode-file-link + * or this file's parent vnode-file-link. + * "./AFSDir-<#>". It's a symbolic link to its real dir. + * The parent dir and symbolic link to it must exist. + */ + vnode = ((vn.type == 2) ? vn.vnode : vn.parent); + if (vnode == 1) + strncpy(parentdir, rootdir, sizeof parentdir); + else { + afs_snprintf(parentdir, sizeof parentdir, "%s/%s%d", rootdir, + ADIR, vnode); + + len = readlink(parentdir, linkname, MAXNAMELEN); + if (len < 0) { + /* parentdir does not exist. So create an orphan dir. + * and then link the parentdir to the orphaned dir. + */ + afs_snprintf(linkname, sizeof linkname, "%s/%s%d", + rootdir, ODIR, vnode); + code = mkdir(linkname, 0777); + if ((code < 0) && (errno != EEXIST)) { + fprintf(stderr, + "Error creating directory %s code=%d;%d\n", + linkname, code, errno); + } + + /* Link the parentdir to it - now parentdir exists */ + afs_snprintf(linkname, sizeof linkname, "%s%d/", ODIR, + vnode); + code = symlink(linkname, parentdir); + if (code) { + fprintf(stderr, + "Error creating symlink %s -> %s code=%d;%d\n", + parentdir, linkname, code, errno); + } + } + } + + if (vn.type == 2) { + /*ITSADIR*/ + /* We read the directory entries. If the entry is a + * directory, the subdir is created and the root dir + * will contain a link to it. If its a file, we only + * create a symlink in the dir to the file name. + */ + char *buffer; + unsigned short j; + afs_int32 this_vn; + char *this_name; + + struct DirEntry { + char flag; + char length; + unsigned short next; + struct MKFid { + afs_int32 vnode; + afs_int32 vunique; + } fid; + char name[20]; + }; + + struct Pageheader { + unsigned short pgcount; + unsigned short tag; + char freecount; + char freebitmap[8]; + char padding[19]; + }; + + struct DirHeader { + struct Pageheader header; + char alloMap[128]; + unsigned short hashTable[128]; + }; + + struct Page0 { + struct DirHeader header; + struct DirEntry entry[1]; + } *page0; + + + buffer = NULL; + buffer = (char *)malloc(vn.dataSize); + + readdata(buffer, vn.dataSize); + page0 = (struct Page0 *)buffer; + + /* Step through each bucket in the hash table, i, + * and follow each element in the hash chain, j. + * This gives us each entry of the dir. + */ + for (i = 0; i < 128; i++) { + for (j = ntohs(page0->header.hashTable[i]); j; + j = ntohs(page0->entry[j].next)) { + j -= 13; + this_vn = ntohl(page0->entry[j].fid.vnode); + this_name = page0->entry[j].name; + + if ((strcmp(this_name, ".") == 0) + || (strcmp(this_name, "..") == 0)) + continue; /* Skip these */ + + /* For a directory entry, create it. Then create the + * link (from the rootdir) to this directory. + */ + if (this_vn & 1) { + /*ADIRENTRY*/ + /* dirname is the directory to create. + * vflink is what will link to it. + */ + afs_snprintf(dirname, sizeof dirname, "%s/%s", + parentdir, this_name); + afs_snprintf(vflink, sizeof vflink, "%s/%s%d", + rootdir, ADIR, this_vn); + + /* The link and directory may already exist */ + len = readlink(vflink, linkname, MAXNAMELEN); + if (len < 0) { + /* Doesn't already exist - so create the directory. + * umask will pare the mode bits down. + */ + code = mkdir(dirname, 0777); + if ((code < 0) && (errno != EEXIST)) { + fprintf(stderr, + "Error creating directory %s code=%d;%d\n", + dirname, code, errno); + } + } else { + /* Does already exist - so move the directory. + * It was created originally as orphaned. + */ + linkname[len - 1] = '\0'; /* remove '/' at end */ + afs_snprintf(lname, sizeof lname, "%s/%s", + rootdir, linkname); + code = rename(lname, dirname); + if (code) { + fprintf(stderr, + "Error renaming %s to %s code=%d;%d\n", + lname, dirname, code, errno); + } + } + + /* Now create/update the link to the new/moved directory */ + if (vn.vnode == 1) + afs_snprintf(dirname, sizeof dirname, "%s/", + this_name); + else + afs_snprintf(dirname, sizeof dirname, + "%s%d/%s/", ADIR, vn.vnode, + this_name); + unlink(vflink); + code = symlink(dirname, vflink); + if (code) { + fprintf(stderr, + "Error creating symlink %s -> %s code=%d;%d\n", + vflink, dirname, code, errno); + } + } + /*ADIRENTRY*/ + /* For a file entry, we remember the name of the file + * by creating a link within the directory. Restoring + * the file will later remove the link. + */ + else { + /*AFILEENTRY*/ afs_snprintf(vflink, + sizeof vflink, + "%s/%s%d", parentdir, + AFILE, this_vn); + + code = symlink(this_name, vflink); + if ((code < 0) && (errno != EEXIST)) { + fprintf(stderr, + "Error creating symlink %s -> %s code=%d;%d\n", + vflink, page0->entry[j].name, code, + errno); + } + } + /*AFILEENTRY*/} + } + free(buffer); + } + /*ITSADIR*/ + else if (vn.type == 1) { + /*ITSAFILE*/ + /* A file vnode. So create it into the desired directory. A + * link should exist in the directory naming the file. + */ + int fid; + int lfile; + afs_sfsize_t size, s; + + /* Check if its vnode-file-link exists. If not, + * then the file will be an orphaned file. + */ + lfile = 1; + afs_snprintf(filename, sizeof filename, "%s/%s%d", parentdir, + AFILE, vn.vnode); + len = readlink(filename, fname, MAXNAMELEN); + if (len < 0) { + afs_snprintf(filename, sizeof filename, "%s/%s%d", + rootdir, OFILE, vn.vnode); + lfile = 0; /* no longer a linked file; a direct path */ + } + + /* Create a mode for the file. Use the owner bits and + * duplicate them across group and other. The umask + * will remove what we don't want. + */ + mode = (vn.modebits >> 6) & 0x7; + mode |= (mode << 6) | (mode << 3); + + /* Write the file out */ + fid = open(filename, (O_CREAT | O_WRONLY | O_TRUNC), mode); + size = vn.dataSize; + while (size > 0) { + s = (afs_int32) ((size > BUFSIZE) ? BUFSIZE : size); + code = fread(buf, 1, s, dumpfile); + if (code > 0) { + (void)write(fid, buf, code); + size -= code; + } + if (code != s) { + if (code < 0) + fprintf(stderr, "Code = %d; Errno = %d\n", code, + errno); + else { + char tmp[100]; + (void)afs_snprintf(tmp, sizeof tmp, + "Read %llu bytes out of %llu", + (afs_uintmax_t) (vn.dataSize - + size), + (afs_uintmax_t) vn.dataSize); + fprintf(stderr, "%s\n", tmp); + } + break; + } + } + close(fid); + if (size != 0) { + fprintf(stderr, " File %s (%s) is incomplete\n", + filename, fname); + } + + /* Remove the link to the file */ + if (lfile) { + unlink(filename); + } + } + /*ITSAFILE*/ + else if (vn.type == 3) { + /*ITSASYMLINK*/ + /* A symlink vnode. So read it into the desired directory. This could + * also be a mount point. If the volume is being restored to AFS, this + * will become a mountpoint. If not, it becomes a symlink to no-where. + */ + int fid; + afs_int32 size, s; + + /* Check if its vnode-file-link exists and create pathname + * of the symbolic link. If it doesn't exist, + * then the link will be an orphaned link. + */ + afs_snprintf(linkname, sizeof linkname, "%s/%s%d", parentdir, + AFILE, vn.vnode); + len = readlink(linkname, fname, MAXNAMELEN); + if (len < 0) { + afs_snprintf(filename, sizeof filename, "%s/%s%d", + rootdir, OFILE, vn.vnode); + } else { + fname[len] = '\0'; + afs_snprintf(filename, sizeof filename, "%s/%s", + parentdir, fname); + } + + /* Read the link in, delete it, and then create it */ + readdata(buf, vn.dataSize); + + /* If a mountpoint, change its link path to mountroot */ + s = strlen(buf); + if (((buf[0] == '%') || (buf[0] == '#')) + && (buf[s - 1] == '.')) { + /* This is a symbolic link */ + buf[s - 1] = 0; /* Remove prefix '.' */ + strcpy(lname, &buf[1]); /* Remove postfix '#' or '%' */ + strcpy(buf, mntroot); + strcat(buf, lname); + } + + unlink(filename); + code = symlink(buf, filename); + if (code) { + fprintf(stderr, + "Error creating symlink %s -> %s code=%d;%d\n", + filename, buf, code, errno); + } + + /* Remove the symbolic link */ + unlink(linkname); + } + /*ITSASYMLINK*/ + else { + fprintf(stderr, "Unknown Vnode block\n"); + } + break; + + default: + done = 1; + break; + } + } + if (vn.type == 0) + inc_dump = 1; + + return ((afs_int32) tag); +} + +WorkerBee(as, arock) + struct cmd_syndesc *as; + char *arock; +{ + int code = 0, c, len; + afs_int32 type, count, vcount; + DIR *dirP, *dirQ; + struct dirent *dirE, *dirF; + char fname[MAXNAMELEN], name[MAXNAMELEN], lname[MAXNAMELEN], + mname[MAXNAMELEN]; + char thisdir[MAXPATHLEN], *t; + struct DumpHeader dh; /* Defined in dump.h */ +#if 0/*ndef HAVE_GETCWD*/ /* XXX enable when autoconf happens */ + extern char *getwd(); +#define getcwd(x,y) getwd(x) +#endif + + if (as->parms[0].items) { /* -file */ + dumpfile = fopen(as->parms[0].items->data, "r"); + if (!dumpfile) { + fprintf(stderr, "Cannot open '%s'. Code = %d\n", + as->parms[0].items->data, errno); + goto cleanup; + } + } else { + dumpfile = (FILE *) stdin; /* use stdin */ + } + + /* Read the dump header. From it we get the volume name */ + type = ntohl(readvalue(1)); + if (type != 1) { + fprintf(stderr, "Expected DumpHeader\n"); + code = -1; + goto cleanup; + } + type = ReadDumpHeader(&dh); + + /* Get the root directory we restore to */ + if (as->parms[1].items) { /* -dir */ + strcpy(rootdir, as->parms[1].items->data); + } else { + strcpy(rootdir, "."); + } + strcat(rootdir, "/"); + + /* Append the RW volume name to the root directory */ + strcat(rootdir, dh.volumeName); + len = strlen(rootdir); + if (strcmp(".backup", rootdir + len - 7) == 0) { + rootdir[len - 7] = 0; + } else if (strcmp(".readonly", rootdir + len - 9) == 0) { + rootdir[len - 9] = 0; + } + + /* Append the extension we asked for */ + if (as->parms[2].items) { + strcat(rootdir, as->parms[2].items->data); /* -extension */ + } + + /* The mountpoint root is either specifid in -mountpoint + * or -dir or the current working dir. + */ + if ((as->parms[3].items) || (as->parms[1].items)) { /* -mountpoint or -dir */ + t = (char *)getcwd(thisdir, MAXPATHLEN); /* remember current dir */ + if (!t) { + fprintf(stderr, + "Cannot get pathname of current working directory: %s\n", + thisdir); + code = -1; + goto cleanup; + } + /* Change to the mount point dir */ + code = + chdir((as->parms[3].items ? as->parms[3].items->data : as-> + parms[1].items->data)); + if (code) { + fprintf(stderr, "Mount point directory not found: Error = %d\n", + errno); + goto cleanup; + } + t = (char *)getcwd(mntroot, MAXPATHLEN); /* get its full pathname */ + if (!t) { + fprintf(stderr, + "Cannot determine pathname of mount point root directory: %s\n", + mntroot); + code = -1; + goto cleanup; + } + strcat(mntroot, "/"); /* append '/' to end of it */ + code = chdir(thisdir); /* return to original working dir */ + if (code) { + fprintf(stderr, "Cannot find working directory: Error = %d\n", + errno); + goto cleanup; + } + } else { /* use current directory */ + t = (char *)getcwd(mntroot, MAXPATHLEN); /* get full pathname of current dir */ + if (!t) { + fprintf(stderr, + "Cannot determine pathname of current working directory: %s\n", + mntroot); + code = -1; + goto cleanup; + } + } + strcat(mntroot, "/"); /* append '/' to end of it */ + + /* Set the umask for the restore */ + if (as->parms[4].items) { /* -umask */ + afs_int32 mask; + mask = strtol(as->parms[4].items->data, 0, 8); + fprintf(stderr, "Umask set to 0%03o\n", mask); + umask(mask); + } + + fprintf(stderr, "Restoring volume dump of '%s' to directory '%s'.\n", + dh.volumeName, rootdir); + code = mkdir(rootdir, 0777); + if ((code < 0) && (errno != EEXIST)) { + fprintf(stderr, "Error creating directory %s code=%d;%d\n", rootdir, + code, errno); + } + + for (count = 1; type == 2; count++) { + type = ReadVolumeHeader(count); + for (vcount = 1; type == 3; vcount++) + type = ReadVNode(vcount); + } + + if (type != 4) { + fprintf(stderr, "Expected End-of-Dump\n"); + code = -1; + goto cleanup; + } + + cleanup: + /* For incremental restores, Follow each directory link and + * remove an "AFSFile" links. + */ + if (inc_dump) { + fprintf(stderr, "An incremental dump.\n"); + dirP = opendir(rootdir); + while (dirP && (dirE = readdir(dirP))) { + if (strncmp(dirE->d_name, ADIR, strlen(ADIR)) == 0) { + afs_snprintf(name, sizeof name, "%s/%s", rootdir, + dirE->d_name); + dirQ = opendir(name); + while (dirQ && (dirF = readdir(dirQ))) { + if (strncmp(dirF->d_name, AFILE, strlen(AFILE)) == 0) { + afs_snprintf(name, sizeof name, "%s/%s/%s", rootdir, + dirE->d_name, dirF->d_name); + unlink(name); + } + } + closedir(dirQ); + } else if (strncmp(dirE->d_name, AFILE, strlen(AFILE)) == 0) { + afs_snprintf(name, sizeof name, "%s/%s", rootdir, + dirE->d_name); + unlink(name); + } + } + closedir(dirP); + } + + /* Now go through and remove all the directory links */ + dirP = opendir(rootdir); + while (dirP && (dirE = readdir(dirP))) { + if (strncmp(dirE->d_name, ADIR, strlen(ADIR)) == 0) { + afs_snprintf(name, sizeof name, "%s/%s", rootdir, dirE->d_name); + unlink(name); + } + } + closedir(dirP); + + return (code); +} + +main(argc, argv) + int argc; + char **argv; +{ + struct cmd_syndesc *ts; + struct cmd_item *ti; + + setlinebuf(stdout); + + ts = cmd_CreateSyntax(NULL, WorkerBee, NULL, "vldb check"); + cmd_AddParm(ts, "-file", CMD_SINGLE, CMD_OPTIONAL, "dump file"); + cmd_AddParm(ts, "-dir", CMD_SINGLE, CMD_OPTIONAL, "restore dir"); + cmd_AddParm(ts, "-extension", CMD_SINGLE, CMD_OPTIONAL, "name extension"); + cmd_AddParm(ts, "-mountpoint", CMD_SINGLE, CMD_OPTIONAL, + "mount point root"); + cmd_AddParm(ts, "-umask", CMD_SINGLE, CMD_OPTIONAL, "mode mask"); + + return cmd_Dispatch(argc, argv); +} diff --git a/src/volser/vol-dump.c b/src/volser/vol-dump.c new file mode 100644 index 000000000..125ada602 --- /dev/null +++ b/src/volser/vol-dump.c @@ -0,0 +1,871 @@ +/* + * Copyright 2000, International Business Machines Corporation and others. + * All Rights Reserved. + * + * This software has been released under the terms of the IBM Public + * License. For details, see the LICENSE file in the top-level source + * directory or online at http://www.openafs.org/dl/license10.html + */ + +/* + System: VICE-TWO + Module: vol-dump.c + Institution: The Information Technology Center, Carnegie-Mellon University + + */ + +#include +#include + +RCSID + ("$Header: /cvs/openafs/src/volser/vol-dump.c,v 1.1 2003/08/28 03:16:34 shadow Exp $"); + +#include +#include +#include +#include +#include +#ifdef AFS_NT40_ENV +#include +#include +#include +#else +#include +#include +#include +#endif +#include + +#include +#include +#include "nfs.h" +#include +#include "lock.h" +#include "lwp.h" +#include +#include "ihandle.h" +#include "vnode.h" +#include "volume.h" +#include "partition.h" +#include "viceinode.h" +#include +#include "acl.h" + +#ifdef HAVE_UNISTD_H +#include +#endif + +#ifdef HAVE_STDLIB_H +#include +#endif + +#ifdef HAVE_FCNTL_H +#include +#endif + +#ifdef _AIX +#include +#endif + +#include + +#include "volser/volser.h" +#include "volser/volint.h" +#include "volser/dump.h" + +#define putint32(p, v) *p++ = v>>24, *p++ = v>>16, *p++ = v>>8, *p++ = v +#define putshort(p, v) *p++ = v>>8, *p++ = v + +#ifdef O_LARGEFILE +#define afs_stat stat64 +#define afs_fstat fstat64 +#define afs_open open64 +#else /* !O_LARGEFILE */ +#define afs_stat stat +#define afs_fstat fstat +#define afs_open open +#endif /* !O_LARGEFILE */ + +int VolumeChanged; /* needed by physio - leave alone */ +int verbose = 0; + +/* Forward Declarations */ +void HandleVolume(struct DiskPartition *partP, char *name, char *filename); +Volume *AttachVolume(struct DiskPartition *dp, char *volname, + register struct VolumeHeader *header); +static void DoMyVolDump(Volume * vp, struct DiskPartition *dp, + char *dumpfile); + +#ifndef AFS_NT40_ENV +#include "AFS_component_version_number.c" +#endif + +char name[VMAXPATHLEN]; + + +int +ReadHdr1(IHandle_t * ih, char *to, int size, u_int magic, u_int version) +{ + int bad = 0; + int code; + + code = IH_IREAD(ih, 0, to, size); + if (code != size) + return -1; + + return 0; +} + + +Volume * +AttachVolume(struct DiskPartition * dp, char *volname, + register struct VolumeHeader * header) +{ + register Volume *vp; + afs_int32 ec = 0; + + vp = (Volume *) calloc(1, sizeof(Volume)); + vp->specialStatus = 0; + vp->device = dp->device; + vp->partition = dp; + IH_INIT(vp->vnodeIndex[vLarge].handle, dp->device, header->parent, + header->largeVnodeIndex); + IH_INIT(vp->vnodeIndex[vSmall].handle, dp->device, header->parent, + header->smallVnodeIndex); + IH_INIT(vp->diskDataHandle, dp->device, header->parent, + header->volumeInfo); + IH_INIT(V_linkHandle(vp), dp->device, header->parent, header->linkTable); + vp->cacheCheck = 0; /* XXXX */ + vp->shuttingDown = 0; + vp->goingOffline = 0; + vp->nUsers = 1; + vp->header = (struct volHeader *)calloc(1, sizeof(*vp->header)); + ec = ReadHdr1(V_diskDataHandle(vp), (char *)&V_disk(vp), + sizeof(V_disk(vp)), VOLUMEINFOMAGIC, VOLUMEINFOVERSION); + if (!ec) { + struct IndexFileHeader iHead; + ec = ReadHdr1(vp->vnodeIndex[vSmall].handle, (char *)&iHead, + sizeof(iHead), SMALLINDEXMAGIC, SMALLINDEXVERSION); + } + if (!ec) { + struct IndexFileHeader iHead; + ec = ReadHdr1(vp->vnodeIndex[vLarge].handle, (char *)&iHead, + sizeof(iHead), LARGEINDEXMAGIC, LARGEINDEXVERSION); + } +#ifdef AFS_NAMEI_ENV + if (!ec) { + struct versionStamp stamp; + ec = ReadHdr1(V_linkHandle(vp), (char *)&stamp, sizeof(stamp), + LINKTABLEMAGIC, LINKTABLEVERSION); + } +#endif + if (ec) + return (Volume *) 0; + return vp; +} + + +static int +handleit(struct cmd_syndesc *as, char *arock) +{ + register struct cmd_item *ti; + int err = 0; + int volumeId = 0; + char *partName = 0; + char *fileName = NULL; + struct DiskPartition *partP = NULL; + char name1[128]; + char tmpPartName[20]; + + +#ifndef AFS_NT40_ENV +#if 0 + if (geteuid() != 0) { + fprintf(stderr, "voldump must be run as root; sorry\n"); + exit(1); + } +#endif +#endif + + if ((ti = as->parms[0].items)) + partName = ti->data; + if ((ti = as->parms[1].items)) + volumeId = atoi(ti->data); + if ((ti = as->parms[2].items)) + fileName = ti->data; + if ((ti = as->parms[3].items)) + verbose = 1; + + DInit(10); + + err = VAttachPartitions(); + if (err) { + fprintf(stderr, "%d partitions had errors during attach.\n", err); + } + + if (partName) { + if (strlen(partName) == 1) { + if (partName[0] >= 'a' && partName[0] <= 'z') { + strcpy(tmpPartName, "/vicepa"); + tmpPartName[6] = partName[0]; + partP = VGetPartition(tmpPartName, 0); + } + } else { + partP = VGetPartition(partName, 0); + } + if (!partP) { + fprintf(stderr, + "%s is not an AFS partition name on this server.\n", + partName); + exit(1); + } + } + + if (!volumeId) { + fprintf(stderr, "Must specify volume id!\n"); + exit(1); + } + + if (!partP) { + fprintf(stderr, "must specify vice partition.\n"); + exit(1); + } + + (void)afs_snprintf(name1, sizeof name1, VFORMAT, (unsigned long)volumeId); + HandleVolume(partP, name1, fileName); + return 0; +} + +void +HandleVolume(struct DiskPartition *dp, char *name, char *filename) +{ + struct VolumeHeader header; + struct VolumeDiskHeader diskHeader; + struct afs_stat status, stat; + register int fd; + Volume *vp; + IHandle_t *ih; + char headerName[1024]; + + afs_int32 n; + + (void)afs_snprintf(headerName, sizeof headerName, "%s/%s", + VPartitionPath(dp), name); + if ((fd = afs_open(headerName, O_RDONLY)) == -1 + || afs_fstat(fd, &status) == -1) { + fprintf(stderr, "Cannot read volume header %s\n", name); + close(fd); + exit(1); + } + n = read(fd, &diskHeader, sizeof(diskHeader)); + + if (n != sizeof(diskHeader) + || diskHeader.stamp.magic != VOLUMEHEADERMAGIC) { + fprintf(stderr, "Error reading volume header %s\n", name); + exit(1); + } + if (diskHeader.stamp.version != VOLUMEHEADERVERSION) { + fprintf(stderr, + "Volume %s, version number is incorrect; volume needs salvage\n", + name); + exit(1); + } + DiskToVolumeHeader(&header, &diskHeader); + + close(fd); + vp = AttachVolume(dp, name, &header); + if (!vp) { + fprintf(stderr, "Error attaching volume header %s\n", name); + exit(1); + } + + DoMyVolDump(vp, dp, filename); +} + + +int +main(int argc, char **argv) +{ + register struct cmd_syndesc *ts; + afs_int32 code; + + VInitVolumePackage(volumeUtility, 5, 5, DONT_CONNECT_FS, 0); + + ts = cmd_CreateSyntax(NULL, handleit, 0, + "Dump a volume to a 'vos dump' format file without using volserver"); + cmd_AddParm(ts, "-part", CMD_LIST, CMD_OPTIONAL, "AFS partition name"); + cmd_AddParm(ts, "-volumeid", CMD_LIST, CMD_OPTIONAL, "Volume id"); + cmd_AddParm(ts, "-file", CMD_LIST, CMD_OPTIONAL, "Dump filename"); + cmd_AddParm(ts, "-verbose", CMD_FLAG, CMD_OPTIONAL, + "Trace dump progress (very verbose)"); + code = cmd_Dispatch(argc, argv); + return code; +} + + + + +static int +DumpDouble(int dumpfd, char tag, register afs_uint32 value1, + register afs_uint32 value2) +{ + int res; + char tbuffer[9]; + register byte *p = (unsigned char *)tbuffer; + *p++ = tag; + putint32(p, value1); + putint32(p, value2); + + res = write(dumpfd, tbuffer, 9); + return ((res == 9) ? 0 : VOLSERDUMPERROR); +} + +static int +DumpInt32(int dumpfd, char tag, register afs_uint32 value) +{ + char tbuffer[5]; + register byte *p = (unsigned char *)tbuffer; + *p++ = tag; + putint32(p, value); + return ((write(dumpfd, tbuffer, 5) == 5) ? 0 : VOLSERDUMPERROR); +} + +static int +DumpString(int dumpfd, char tag, register char *s) +{ + register int n; + int code = 0; + code = write(dumpfd, &tag, 1); + if (code != 1) + return VOLSERDUMPERROR; + n = strlen(s) + 1; + code = write(dumpfd, s, n); + if (code != n) + return VOLSERDUMPERROR; + return 0; +} + + +static int +DumpArrayInt32(int dumpfd, char tag, register afs_uint32 * array, + register int nelem) +{ + char tbuffer[4]; + register afs_uint32 v; + int code = 0; + register byte *p = (unsigned char *)tbuffer; + *p++ = tag; + putshort(p, nelem); + code = write(dumpfd, tbuffer, 3); + if (code != 3) + return VOLSERDUMPERROR; + while (nelem--) { + p = (unsigned char *)tbuffer; + v = *array++; /*this was register */ + + putint32(p, v); + code = write(dumpfd, tbuffer, 4); + if (code != 4) + return VOLSERDUMPERROR; + } + return 0; +} + + + + +static int +DumpDumpHeader(int dumpfd, register Volume * vp, afs_int32 fromtime) +{ + int code = 0; + afs_int32 dumpTimes[2]; + + if (verbose) + fprintf(stderr, "dumping dump header\n"); + + if (!code) + code = DumpDouble(dumpfd, D_DUMPHEADER, DUMPBEGINMAGIC, DUMPVERSION); + + if (!code) + code = DumpInt32(dumpfd, 'v', V_id(vp)); + + if (!code) + code = DumpString(dumpfd, 'n', V_name(vp)); + + dumpTimes[0] = fromtime; + dumpTimes[1] = V_backupDate(vp); /* Until the time the clone was made */ + if (!code) + code = DumpArrayInt32(dumpfd, 't', (afs_uint32 *) dumpTimes, 2); + + return code; +} + + +static int +DumpEnd(int dumpfd) +{ + return (DumpInt32(dumpfd, D_DUMPEND, DUMPENDMAGIC)); +} + +static int +DumpByte(int dumpfd, char tag, byte value) +{ + char tbuffer[2]; + register byte *p = (unsigned char *)tbuffer; + *p++ = tag; + *p = value; + return ((write(dumpfd, tbuffer, 2) == 2) ? 0 : VOLSERDUMPERROR); +} + +static int +DumpTag(int dumpfd, register int tag) +{ + char p; + + p = tag; + return ((write(dumpfd, &p, 1) == 1) ? 0 : VOLSERDUMPERROR); + +} + +static int +DumpBool(int dumpfd, char tag, unsigned int value) +{ + char tbuffer[2]; + register byte *p = (unsigned char *)tbuffer; + *p++ = tag; + *p = value; + return ((write(dumpfd, tbuffer, 2) == 2) ? 0 : VOLSERDUMPERROR); +} + + + +static int +DumpVolumeHeader(int dumpfd, register Volume * vp) +{ + int code = 0; + + if (verbose) + fprintf(stderr, "dumping volume header\n"); + + if (!code) + code = DumpTag(dumpfd, D_VOLUMEHEADER); + if (!code) + code = DumpInt32(dumpfd, 'i', V_id(vp)); + if (!code) + code = DumpInt32(dumpfd, 'v', V_stamp(vp).version); + if (!code) + code = DumpString(dumpfd, 'n', V_name(vp)); + if (!code) + code = DumpBool(dumpfd, 's', V_inService(vp)); + if (!code) + code = DumpBool(dumpfd, 'b', V_blessed(vp)); + if (!code) + code = DumpInt32(dumpfd, 'u', V_uniquifier(vp)); + if (!code) + code = DumpByte(dumpfd, 't', (byte) V_type(vp)); + if (!code) + code = DumpInt32(dumpfd, 'p', V_parentId(vp)); + if (!code) + code = DumpInt32(dumpfd, 'c', V_cloneId(vp)); + if (!code) + code = DumpInt32(dumpfd, 'q', V_maxquota(vp)); + if (!code) + code = DumpInt32(dumpfd, 'm', V_minquota(vp)); + if (!code) + code = DumpInt32(dumpfd, 'd', V_diskused(vp)); + if (!code) + code = DumpInt32(dumpfd, 'f', V_filecount(vp)); + if (!code) + code = DumpInt32(dumpfd, 'a', V_accountNumber(vp)); + if (!code) + code = DumpInt32(dumpfd, 'o', V_owner(vp)); + if (!code) + code = DumpInt32(dumpfd, 'C', V_creationDate(vp)); /* Rw volume creation date */ + if (!code) + code = DumpInt32(dumpfd, 'A', V_accessDate(vp)); + if (!code) + code = DumpInt32(dumpfd, 'U', V_updateDate(vp)); + if (!code) + code = DumpInt32(dumpfd, 'E', V_expirationDate(vp)); + if (!code) + code = DumpInt32(dumpfd, 'B', V_backupDate(vp)); /* Rw volume backup clone date */ + if (!code) + code = DumpString(dumpfd, 'O', V_offlineMessage(vp)); + + /* + * We do NOT dump the detailed volume statistics residing in the old + * motd field, since we cannot tell from the info in a dump whether + * statistics data has been put there. Instead, we dump a null string, + * just as if that was what the motd contained. + */ + if (!code) + code = DumpString(dumpfd, 'M', ""); + if (!code) + code = + DumpArrayInt32(dumpfd, 'W', (afs_uint32 *) V_weekUse(vp), + sizeof(V_weekUse(vp)) / sizeof(V_weekUse(vp)[0])); + if (!code) + code = DumpInt32(dumpfd, 'D', V_dayUseDate(vp)); + if (!code) + code = DumpInt32(dumpfd, 'Z', V_dayUse(vp)); + return code; +} + +static int +DumpShort(int dumpfd, char tag, unsigned int value) +{ + char tbuffer[3]; + register byte *p = (unsigned char *)tbuffer; + *p++ = tag; + *p++ = value >> 8; + *p = value; + return ((write(dumpfd, tbuffer, 3) == 3) ? 0 : VOLSERDUMPERROR); +} + +static int +DumpByteString(int dumpfd, char tag, register byte * bs, register int nbytes) +{ + int code = 0; + + code = write(dumpfd, &tag, 1); + if (code != 1) + return VOLSERDUMPERROR; + code = write(dumpfd, (char *)bs, nbytes); + if (code != nbytes) + return VOLSERDUMPERROR; + return 0; +} + + +static int +DumpFile(int dumpfd, int vnode, FdHandle_t * handleP, struct VnodeDiskObject *v) +{ + int code = 0, failed_seek = 0, failed_write = 0; + afs_int32 pad = 0, offset; + afs_sfsize_t n, nbytes, howMany, howBig; + byte *p; +#ifndef AFS_NT40_ENV + struct afs_stat status; +#endif + afs_sfsize_t size, tmpsize; +#ifdef AFS_AIX_ENV +#include + struct statfs tstatfs; +#endif + + if (verbose) + fprintf(stderr, "dumping file for vnode %d\n", vnode); + +#ifdef AFS_NT40_ENV + howBig = _filelength(handleP->fd_fd); + howMany = 4096; + +#else + afs_fstat(handleP->fd_fd, &status); + howBig = status.st_size; + +#ifdef AFS_AIX_ENV + /* Unfortunately in AIX valuable fields such as st_blksize are + * gone from the stat structure. + */ + fstatfs(handleP->fd_fd, &tstatfs); + howMany = tstatfs.f_bsize; +#else + howMany = status.st_blksize; +#endif /* AFS_AIX_ENV */ +#endif /* AFS_NT40_ENV */ + + + size = FDH_SIZE(handleP); + + if (verbose) + fprintf(stderr, " howBig = %u, howMany = %u, fdh size = %u\n", + howBig, howMany, size); + +#ifdef AFS_LARGEFILE_ENV + { + afs_uint32 hi, lo; + SplitInt64(size, hi, lo); + if (hi == 0L) { + code = DumpInt32(dumpfd, 'f', lo); + } else { + code = DumpDouble(dumpfd, 'h', hi, lo); + } + } +#else /* !AFS_LARGEFILE_ENV */ + code = DumpInt32(dumpfd, 'f', size); +#endif /* !AFS_LARGEFILE_ENV */ + if (code) { + return VOLSERDUMPERROR; + } + + p = (unsigned char *)malloc(howMany); + if (!p) { + fprintf(stderr, "out of memory!\n"); + return VOLSERDUMPERROR; + } + + /* loop through whole file, while we still have bytes left, and no errors, in chunks of howMany bytes */ + for (nbytes = size; (nbytes && !failed_write); nbytes -= howMany) { + if (nbytes < howMany) + howMany = nbytes; + + /* Read the data - unless we know we can't */ + n = (failed_seek ? 0 : FDH_READ(handleP, p, howMany)); + + /* If read any good data and we null padded previously, log the + * amount that we had null padded. + */ + if ((n > 0) && pad) { + fprintf(stderr, "Null padding file %d bytes at offset %u\n", pad, + offset); + pad = 0; + } + + /* If didn't read enough data, null padd the rest of the buffer. This + * can happen if, for instance, the media has some bad spots. We don't + * want to quit the dump, so we start null padding. + */ + if (n < howMany) { + + if (verbose) fprintf(stderr, " read %u instead of %u bytes.\n", n, howMany); + + /* Record the read error */ + if (n < 0) { + n = 0; + fprintf(stderr, "Error %d reading inode %s for vnode %d\n", + errno, PrintInode(NULL, handleP->fd_ih->ih_ino), + vnode); + } else if (!pad) { + fprintf(stderr, "Error reading inode %s for vnode %d\n", + PrintInode(NULL, handleP->fd_ih->ih_ino), vnode); + } + + /* Pad the rest of the buffer with zeros. Remember offset we started + * padding. Keep total tally of padding. + */ + memset(p + n, 0, howMany - n); + if (!pad) + offset = (howBig - nbytes) + n; + pad += (howMany - n); + + /* Now seek over the data we could not get. An error here means we + * can't do the next read. + */ + failed_seek = FDH_SEEK(handleP, ((size - nbytes) + howMany), SEEK_SET); + if (failed_seek != ((size - nbytes) + howMany)) { + if (failed_seek < 0) { + fprintf(stderr, + "Error %d seeking in inode %s for vnode %d\n", + errno, PrintInode(NULL, handleP->fd_ih->ih_ino), + vnode); + } else { + fprintf(stderr, + "Error seeking in inode %s for vnode %d\n", + PrintInode(NULL, handleP->fd_ih->ih_ino), vnode); + failed_seek = -1; + } + } else { + failed_seek = 0; + } + } + + /* Now write the data out */ + if (write(dumpfd, (char *)p, howMany) != howMany) + failed_write = VOLSERDUMPERROR; + } + + if (pad) { /* Any padding we hadn't reported yet */ + fprintf(stderr, "Null padding file: %d bytes at offset %u\n", pad, + offset); + } + + free(p); + return failed_write; +} + + +static int +DumpVnode(int dumpfd, struct VnodeDiskObject *v, int volid, int vnodeNumber, + int dumpEverything, struct Volume *vp) +{ + int code = 0; + IHandle_t *ihP; + FdHandle_t *fdP; + + if (verbose) + fprintf(stderr, "dumping vnode %d\n", vnodeNumber); + + if (!v || v->type == vNull) + return code; + if (!code) + code = DumpDouble(dumpfd, D_VNODE, vnodeNumber, v->uniquifier); + if (!dumpEverything) + return code; + if (!code) + code = DumpByte(dumpfd, 't', (byte) v->type); + if (!code) + code = DumpShort(dumpfd, 'l', v->linkCount); /* May not need this */ + if (!code) + code = DumpInt32(dumpfd, 'v', v->dataVersion); + if (!code) + code = DumpInt32(dumpfd, 'm', v->unixModifyTime); + if (!code) + code = DumpInt32(dumpfd, 'a', v->author); + if (!code) + code = DumpInt32(dumpfd, 'o', v->owner); + if (!code && v->group) + code = DumpInt32(dumpfd, 'g', v->group); /* default group is 0 */ + if (!code) + code = DumpShort(dumpfd, 'b', v->modeBits); + if (!code) + code = DumpInt32(dumpfd, 'p', v->parent); + if (!code) + code = DumpInt32(dumpfd, 's', v->serverModifyTime); + if (v->type == vDirectory) { + acl_HtonACL(VVnodeDiskACL(v)); + if (!code) + code = + DumpByteString(dumpfd, 'A', (byte *) VVnodeDiskACL(v), + VAclDiskSize(v)); + } + + if (VNDISK_GET_INO(v)) { + IH_INIT(ihP, V_device(vp), V_parentId(vp), VNDISK_GET_INO(v)); + fdP = IH_OPEN(ihP); + if (fdP == NULL) { + fprintf(stderr, + "Unable to open inode %s for vnode %u (volume %i); not dumped, error %d\n", + PrintInode(NULL, VNDISK_GET_INO(v)), vnodeNumber, volid, + errno); + } + else + { + if (verbose) + fprintf(stderr, "about to dump inode %s for vnode %u\n", + PrintInode(NULL, VNDISK_GET_INO(v)), vnodeNumber); + code = DumpFile(dumpfd, vnodeNumber, fdP, v); + FDH_CLOSE(fdP); + } + IH_RELEASE(ihP); + } + + if (verbose) + fprintf(stderr, "done dumping vnode %d\n", vnodeNumber); + return code; +} + + +static int +DumpVnodeIndex(int dumpfd, Volume * vp, VnodeClass class, afs_int32 fromtime, + int forcedump) +{ + register int code = 0; + register struct VnodeClassInfo *vcp = &VnodeClassInfo[class]; + char buf[SIZEOF_LARGEDISKVNODE]; + struct VnodeDiskObject *vnode = (struct VnodeDiskObject *)buf; + StreamHandle_t *file; + FdHandle_t *fdP; + int size; + int flag; + int offset = 0; + register int vnodeIndex, nVnodes = 0; + + fdP = IH_OPEN(vp->vnodeIndex[class].handle); + file = FDH_FDOPEN(fdP, "r+"); + size = OS_SIZE(fdP->fd_fd); + nVnodes = (size / vcp->diskSize) - 1; + + if (nVnodes > 0) { + STREAM_SEEK(file, vcp->diskSize, 0); + } else + nVnodes = 0; + for (vnodeIndex = 0; + nVnodes && STREAM_READ(vnode, vcp->diskSize, 1, file) == 1 && !code; + nVnodes--, vnodeIndex++, offset += vcp->diskSize) { + flag = forcedump || (vnode->serverModifyTime >= fromtime); + /* Note: the >= test is very important since some old volumes may not have + * a serverModifyTime. For an epoch dump, this results in 0>=0 test, which + * does dump the file! */ + if (verbose) + fprintf(stderr, "about to dump %s vnode %u (vnode offset = %u)\n", + class == vSmall ? "vSmall" : "vLarge", + bitNumberToVnodeNumber(vnodeIndex, class), offset); + if (!code) + code = + DumpVnode(dumpfd, vnode, V_id(vp), + bitNumberToVnodeNumber(vnodeIndex, class), flag, + vp); + } + STREAM_CLOSE(file); + FDH_CLOSE(fdP); + return code; +} + + + +/* A partial dump (no dump header) */ +static int +DumpPartial(int dumpfd, register Volume * vp, afs_int32 fromtime, + int dumpAllDirs) +{ + int code = 0; + + if (verbose) + fprintf(stderr, "about to dump the volume header\n"); + if (!code) + code = DumpVolumeHeader(dumpfd, vp); + + if (verbose) + fprintf(stderr, "about to dump the large vnode index\n"); + if (!code) + code = DumpVnodeIndex(dumpfd, vp, vLarge, fromtime, dumpAllDirs); + + if (verbose) + fprintf(stderr, "about to dump the small vnode index\n"); + if (!code) + code = DumpVnodeIndex(dumpfd, vp, vSmall, fromtime, 0); + return code; +} + + + +static void +DoMyVolDump(Volume * vp, struct DiskPartition *dp, char *dumpfile) +{ + int code = 0; + int fromtime = 0; + int dumpAllDirs = 0; + int dumpfd = 0; + + if (dumpfile) { + unlink(dumpfile); + dumpfd = + open(dumpfile, O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR); + if (dumpfd < 0) { + fprintf(stderr, "Failed to open dump file! Exiting.\n"); + exit(1); + } + } else { + dumpfd = 1; /* stdout */ + } + + if (verbose) + fprintf(stderr, "about to dump the dump header\n"); + if (!code) + code = DumpDumpHeader(dumpfd, vp, fromtime); + + if (verbose) + fprintf(stderr, "about to dump volume contents\n"); + if (!code) + code = DumpPartial(dumpfd, vp, fromtime, dumpAllDirs); + + if (verbose) + fprintf(stderr, "about to dump the dump postamble\n"); + if (!code) + code = DumpEnd(dumpfd); + + if (verbose) + fprintf(stderr, "finished dump\n"); + close(dumpfd); /* might be closing stdout, no harm */ +} diff --git a/src/volser/vol.h b/src/volser/vol.h new file mode 100644 index 000000000..522fb97ee --- /dev/null +++ b/src/volser/vol.h @@ -0,0 +1,26 @@ +/* + * Copyright 2000, International Business Machines Corporation and others. + * All Rights Reserved. + * + * This software has been released under the terms of the IBM Public + * License. For details, see the LICENSE file in the top-level source + * directory or online at http://www.openafs.org/dl/license10.html + */ + +/* + * Module: Vol.h + * System: Volser + * Instituition: ITC, CMU + * Date: December, 88 + */ + +/* pick up the declaration of Inode */ +#include + +typedef struct DirHandle { + int dirh_volume; + int dirh_device; + Inode dirh_inode; + afs_int32 dirh_cacheCheck; + IHandle_t *dirh_handle; +} DirHandle; diff --git a/src/volser/volerr.et b/src/volser/volerr.et new file mode 100644 index 000000000..16db9f0c0 --- /dev/null +++ b/src/volser/volerr.et @@ -0,0 +1,32 @@ +# Copyright 2000, International Business Machines Corporation and others. +# All Rights Reserved. +# +# This software has been released under the terms of the IBM Public +# License. For details, see the LICENSE file in the top-level source +# directory or online at http://www.openafs.org/dl/license10.html + +# */ + +# NOTE: VOLSERBAD_ACCESS value is hardcoded in audit/audit.h, so if you +# make changes here, make sure that audit/audit.h is kept up to date. + +error_table VOLS + ec VOLSERTRELE_ERROR, "internal error releasing transaction" + ec VOLSERNO_OP, "unknown internal error" + ec VOLSERREAD_DUMPERROR, "badly formatted dump" + ec VOLSERDUMPERROR, "badly formatted dump(2)" + ec VOLSERATTACH_ERROR, "could not attach volume" + ec VOLSERILLEGAL_PARTITION, "illegal partition" + ec VOLSERDETACH_ERROR, "could not detach volume" + ec VOLSERBAD_ACCESS, "insufficient privilege for volume operation" + ec VOLSERVLDB_ERROR, "error from volume location database" + ec VOLSERBADNAME, "bad volume name" + ec VOLSERVOLMOVED, "volume moved" + ec VOLSERBADOP, "illegal volume operation" + ec VOLSERBADRELEASE, "volume release failed" + ec VOLSERVOLBUSY, "volume still in use by volserver" + ec VOLSERNO_MEMORY, "out of virtual memory in volserver" + ec VOLSERNOVOL, "no such volume" + ec VOLSERMULTIRWVOL, "more than one read/write volume" + ec VOLSERFAILEDOP, "failed volume server operation" +end diff --git a/src/volser/volint.xg b/src/volser/volint.xg new file mode 100644 index 000000000..cfa515596 --- /dev/null +++ b/src/volser/volint.xg @@ -0,0 +1,410 @@ +/* + * Copyright 2000, International Business Machines Corporation and others. + * All Rights Reserved. + * + * This software has been released under the terms of the IBM Public + * License. For details, see the LICENSE file in the top-level source + * directory or online at http://www.openafs.org/dl/license10.html + */ + +/* + * Module: Volint.xg + * System: Volser + * Instituition: ITC, CMU + * Date: December, 88 + */ + +package AFSVol +prefix S +statindex 16 + +#define VOLCREATEVOLUME 100 +#define VOLDELETEVOLUME 101 +#define VOLRESTORE 102 +#define VOLFORWARD 103 +#define VOLENDTRANS 104 +#define VOLCLONE 105 +#define VOLSETFLAGS 106 +#define VOLGETFLAGS 107 +#define VOLTRANSCREATE 108 +#define VOLDUMP 109 +#define VOLGETNTHVOLUME 110 +#define VOLSETFORWARDING 111 /* defunct */ +#define VOLGETNAME 112 +#define VOLGETSTATUS 113 +#define VOLSIGRESTORE 114 +#define VOLLISTPARTITIONS 115 +#define VOLLISTVOLS 116 +#define VOLSETIDSTYPES 117 +#define VOLMONITOR 118 +#define VOLDISKPART 119 +#define VOLRECLONE 120 +#define VOLLISTONEVOL 121 +#define VOLNUKE 122 +#define VOLSETDATE 123 +#define VOLXLISTVOLS 124 +#define VOLXLISTONEVOL 125 +#define VOLSETINFO 126 +#define VOLXLISTPARTITIONS 127 +#define VOLFORWARDMULTIPLE 128 +#define VOLCONVERTRO 65536 +#define VOLGETSIZE 65537 + +const SIZE = 1024; + +struct volser_status { + afs_int32 volID; /* Volume id--unique over all systems */ + afs_int32 nextUnique; /* Next vnode uniquifier for this volume */ + int type; /* readwrite, etc. */ + afs_int32 parentID; /* Id of parent, if type==readonly or backup */ + afs_int32 cloneID; /* Latest read-only clone, if type==readwrite */ + afs_int32 backupID; /* Latest backup copy of this read write volume */ + afs_int32 restoredFromID; /* The id in a dump this volume was restored from--used simply + to make sure that an incremental dump is not restored on top + of something inappropriate: Note: this field itself is NEVER + dumped!!! */ + afs_int32 maxQuota; /* Quota maximum, 1K blocks */ + afs_int32 minQuota; /* Quota minimum, 1K blocks */ + afs_int32 owner; /* The person responsible for this volume */ + afs_int32 creationDate; /* Creation date for a read/write + volume; cloning date for original copy of + a readonly volume (replicated volumes have + the same creation date) */ + afs_int32 accessDate; /* Last access time by a user, large granularity */ + afs_int32 updateDate; /* Last modification by user */ + afs_int32 expirationDate; /* 0 if it never expires */ + afs_int32 backupDate; /* last time a backup clone was taken */ + afs_int32 copyDate; /* Time that this copy of this volume was created */ +}; + +struct destServer { + afs_int32 destHost; + afs_int32 destPort; + afs_int32 destSSID; +}; + + +struct volintInfo { +#define VNAMESIZE 32 + char name[VNAMESIZE]; + afs_int32 volid; /* volume's id */ + afs_int32 type; /* read-only, read-write, backup */ + afs_int32 backupID; + afs_int32 parentID; + afs_int32 cloneID; + afs_int32 status; + afs_int32 copyDate; + unsigned char inUse; + unsigned char needsSalvaged; + unsigned char destroyMe; + afs_int32 creationDate; + afs_int32 accessDate; + afs_int32 updateDate; + afs_int32 backupDate; + int dayUse; + int filecount; + int maxquota; + int size; + afs_int32 flags; /* used by the backup system */ + afs_int32 spare0; /* Used to hold the minquota value */ + afs_int32 spare1; /* Used to hold the weekuse value */ + afs_int32 spare2; + afs_int32 spare3; +}; + +/* + * Define some values needed for the detailed volume info structure. + */ +const VOLINT_STATS_NUM_RWINFO_FIELDS = 4; + +const VOLINT_STATS_SAME_NET = 0; /*Within same site (total)*/ +const VOLINT_STATS_SAME_NET_AUTH = 1; /*Within same site (authenticated); + (must be 1 more than above)*/ +const VOLINT_STATS_DIFF_NET = 2; /*From external site (total)*/ +const VOLINT_STATS_DIFF_NET_AUTH = 3; /*From external site (authenticated) + (must be 1 more than above)*/ + +const VOLINT_STATS_NUM_TIME_RANGES = 6; + +const VOLINT_STATS_TIME_CAP_0 = 60; /*60 seconds*/ +const VOLINT_STATS_TIME_CAP_1 = 600; /*10 minutes, in seconds*/ +const VOLINT_STATS_TIME_CAP_2 = 3600; /*1 hour, in seconds*/ +const VOLINT_STATS_TIME_CAP_3 = 86400; /*1 day, in seconds*/ +const VOLINT_STATS_TIME_CAP_4 = 604800; /*1 week, in seconds*/ + +const VOLINT_STATS_NUM_TIME_FIELDS = 6; + +const VOLINT_STATS_TIME_IDX_0 = 0; /*0 secs to 60 secs*/ +const VOLINT_STATS_TIME_IDX_1 = 1; /*1 min to 10 mins*/ +const VOLINT_STATS_TIME_IDX_2 = 2; /*10 mins to 60 mins*/ +const VOLINT_STATS_TIME_IDX_3 = 3; /*1 hr to 24 hrs*/ +const VOLINT_STATS_TIME_IDX_4 = 4; /*1 day to 7 days*/ +const VOLINT_STATS_TIME_IDX_5 = 5; /*Greater than 1 week*/ + +/* + * More detailed volume info + */ +struct volintXInfo { + char name[VNAMESIZE]; + afs_int32 volid; /*Volume's ID*/ + afs_int32 type; /*RWVOL, ROVOL, BACKVOL*/ + afs_int32 backupID; /*Backup volume ID*/ + afs_int32 parentID; /*Parent volume ID*/ + afs_int32 cloneID; /*Clone volume ID*/ + afs_int32 status; /*Volume status*/ + afs_int32 copyDate; /*Date when this volume INSTANCE created*/ + unsigned char inUse; /*In use at time of last crash?*/ + afs_int32 creationDate; /*Date when this volume was created*/ + afs_int32 accessDate; /*Date when this volume was last accessed*/ + afs_int32 updateDate; /*Date when this volume was last updated*/ + afs_int32 backupDate; /*Date when this volume was last backed up*/ + int dayUse; /*Number of accesses since midnight*/ + int filecount; /*Number of files in the volume*/ + int maxquota; /*Max volume quota, in Kbytes*/ + int size; /*Current size in Kbytes*/ + /* + * Detailed statistics for reads/writes and authorship. + */ + afs_int32 stat_reads[VOLINT_STATS_NUM_RWINFO_FIELDS]; + afs_int32 stat_writes[VOLINT_STATS_NUM_RWINFO_FIELDS]; + afs_int32 stat_fileSameAuthor[VOLINT_STATS_NUM_TIME_FIELDS]; + afs_int32 stat_fileDiffAuthor[VOLINT_STATS_NUM_TIME_FIELDS]; + afs_int32 stat_dirSameAuthor[VOLINT_STATS_NUM_TIME_FIELDS]; + afs_int32 stat_dirDiffAuthor[VOLINT_STATS_NUM_TIME_FIELDS]; +}; + +struct transDebugInfo { + afs_int32 tid; /*transaction id */ + afs_int32 time; /* time transaction was last active (for timeouts) */ + afs_int32 creationTime; /* time the transaction started */ + afs_int32 returnCode; /* transaction error code */ + afs_int32 volid; /*sequence number of the next packet to be read*/ /* open volume's id */ + afs_int32 partition; /* open volume's partition */ + short iflags; /* initial attach mode flags (IT*) */ + char vflags; /* current volume status flags (VT*) */ + char tflags; /* transaction flags (TT*) */ + char lastProcName[30]; /* name of the last procedure which used transaction */ + int callValid; /*flag which determines if following data is valid*/ + afs_int32 readNext; /*sequence number of the next packet to be read*/ + afs_int32 transmitNext; /*sequence number of the next packet to be transmitted*/ + int lastSendTime; + int lastReceiveTime; +}; + +struct pIDs { + afs_int32 partIds[26]; +}; + +struct diskPartition { + char name[32]; /* Mounted partition name */ + char devName[32]; + int lock_fd; + int totalUsable; + int free; + int minFree; + +}; + +struct restoreCookie { + char name[32]; + afs_int32 type; + afs_int32 clone; + afs_int32 parent; +}; + +struct replica { + afs_int32 trans; + struct destServer server; +}; + +/* Various size parameters of the volume */ +struct volintSize { + afs_uint64 dump_size; +}; + +typedef replica manyDests<>; +typedef afs_int32 manyResults<>; +typedef transDebugInfo transDebugEntries<>; +typedef volintInfo volEntries<>; +typedef afs_int32 partEntries<>; +typedef volintXInfo volXEntries<>; + +proc CreateVolume( + IN afs_int32 partition, + string name<>, + IN afs_int32 type, + IN afs_int32 parent, + INOUT afs_int32 *volid, + OUT afs_int32 *trans +) = VOLCREATEVOLUME; + +proc DeleteVolume( + IN afs_int32 trans +) = VOLDELETEVOLUME; + +proc Restore( + IN afs_int32 toTrans, + IN afs_int32 flags, + IN struct restoreCookie *cookie +) split = VOLRESTORE; + +proc Forward( + IN afs_int32 fromTrans, + IN afs_int32 fromDate, + IN struct destServer *destination, + IN afs_int32 destTrans, + IN struct restoreCookie *cookie +) = VOLFORWARD; + +proc EndTrans( + IN afs_int32 trans, + OUT afs_int32 *rcode +) = VOLENDTRANS; + +proc Clone( + IN afs_int32 trans, + IN afs_int32 purgeVol, + IN afs_int32 newType, + IN string newName<>, + INOUT afs_int32 *newVol +) = VOLCLONE; + +proc SetFlags( + IN afs_int32 trans, + IN afs_int32 flags +) = VOLSETFLAGS; + +proc GetFlags( + IN afs_int32 trans, + OUT afs_int32 *flags +) = VOLGETFLAGS; + +proc TransCreate( + IN afs_int32 volume, + IN afs_int32 partition, + IN afs_int32 flags, + OUT afs_int32 *trans +) = VOLTRANSCREATE; + +proc Dump( + IN afs_int32 fromTrans, + IN afs_int32 fromDate +) split = VOLDUMP; + +proc GetNthVolume( + IN afs_int32 index, + OUT afs_int32 *volume, + OUT afs_int32 *partition +) = VOLGETNTHVOLUME; + +proc SetForwarding( + IN afs_int32 tid, + IN afs_int32 newsite +) = VOLSETFORWARDING; + +proc GetName( + IN afs_int32 tid, + OUT string tname<256> +) = VOLGETNAME; + +proc GetStatus( + IN afs_int32 tid, + OUT struct volser_status *status +) = VOLGETSTATUS; + +proc SignalRestore( + IN string name<>, + int type, + afs_int32 pid, + afs_int32 cloneid +) = VOLSIGRESTORE; + +proc ListPartitions( + OUT struct pIDs *partIDs +) = VOLLISTPARTITIONS; + +proc ListVolumes( + IN afs_int32 partID, + afs_int32 flags, + OUT volEntries *resultEntries +) = VOLLISTVOLS; + +proc SetIdsTypes( + IN afs_int32 tId, + string name<>, + afs_int32 type, + afs_int32 pId, + afs_int32 cloneId, + afs_int32 backupId +) = VOLSETIDSTYPES; + +proc Monitor( + OUT transDebugEntries *result +) = VOLMONITOR; + +proc PartitionInfo( + IN string name<>, + OUT struct diskPartition *partition +) = VOLDISKPART; + +proc ReClone( + IN afs_int32 tid, + afs_int32 cloneID +) = VOLRECLONE; + +proc ListOneVolume( + IN afs_int32 partID, + afs_int32 volid, + OUT volEntries *resultEntries +) = VOLLISTONEVOL; + +proc NukeVolume( + IN afs_int32 partID, + afs_int32 volID +) = VOLNUKE; + +proc SetDate( + IN afs_int32 tid, + afs_int32 newDate +) = VOLSETDATE; + +proc XListVolumes( + IN afs_int32 partID, + afs_int32 flags, + OUT volXEntries *resultXEntriesP +) = VOLXLISTVOLS; + +proc XListOneVolume( + IN afs_int32 partID, + afs_int32 volid, + OUT volXEntries *resultXEntries +) = VOLXLISTONEVOL; + +proc SetInfo( + IN afs_int32 tid, + struct volintInfo *status +) = VOLSETINFO; + +proc XListPartitions( + OUT struct partEntries *partIDs +) = VOLXLISTPARTITIONS; + +proc ForwardMultiple( + IN afs_int32 fromTrans, + IN afs_int32 fromDate, + IN manyDests *destinations, + IN afs_int32 spare, + IN struct restoreCookie *cookie, + OUT manyResults *results +) = VOLFORWARDMULTIPLE; + +proc ConvertROtoRWvolume( + IN afs_int32 partid, + IN afs_int32 volid +) = VOLCONVERTRO; + +proc GetSize( + IN afs_int32 fromTrans, + IN afs_int32 fromDate, + OUT struct volintSize *size +) = VOLGETSIZE; diff --git a/src/volser/volmain.c b/src/volser/volmain.c new file mode 100644 index 000000000..237d32ea6 --- /dev/null +++ b/src/volser/volmain.c @@ -0,0 +1,458 @@ +/* + * Copyright 2000, International Business Machines Corporation and others. + * All Rights Reserved. + * + * This software has been released under the terms of the IBM Public + * License. For details, see the LICENSE file in the top-level source + * directory or online at http://www.openafs.org/dl/license10.html + */ + +#include +#include + +RCSID + ("$Header: /cvs/openafs/src/volser/volmain.c,v 1.18 2003/12/07 22:49:44 jaltman Exp $"); + +#include +#ifdef AFS_NT40_ENV +#include +#include +#include +#include +#else +#include +#include +#include +#endif +#ifdef HAVE_STRING_H +#include +#else +#ifdef HAVE_STRINGS_H +#include +#endif +#endif +#include +#include +#include +#include +#ifdef AFS_PTHREAD_ENV +#include +#else /* AFS_PTHREAD_ENV */ +#include +#endif /* AFS_PTHREAD_ENV */ +#include +#include +#include +#include +#include +#include +#ifdef AFS_NT40_ENV +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "volser.h" +#include +#include +#include + +/*@printflike@*/ extern void Log(const char *format, ...); +/*@printflike@*/ extern void Abort(const char *format, ...); + +#define VolserVersion "2.0" +#define N_SECURITY_OBJECTS 3 + +extern struct volser_trans *TransList(); +#ifndef AFS_PTHREAD_ENV +extern int (*vol_PollProc) (); +extern int IOMGR_Poll(); +#endif +char *GlobalNameHack = NULL; +int hackIsIn = 0; +afs_int32 GlobalVolCloneId, GlobalVolParentId; +int GlobalVolType; +int VolumeChanged; /* XXXX */ +static char busyFlags[MAXHELPERS]; +struct volser_trans *QI_GlobalWriteTrans = 0; +extern void AFSVolExecuteRequest(); +extern void RXSTATS_ExecuteRequest(); +struct afsconf_dir *tdir; +static afs_int32 runningCalls = 0; +int DoLogging = 0; +#define MAXLWP 16 +int lwps = 9; +int udpBufSize = 0; /* UDP buffer size for receive */ + +int Testing = 0; /* for ListViceInodes */ + +#define VS_EXIT(code) { \ + osi_audit(VS_ExitEvent, code, AUD_END); \ + exit(code); \ + } + + +static +MyBeforeProc(struct rx_call *acall) +{ + VTRANS_LOCK; + runningCalls++; + VTRANS_UNLOCK; + return 0; +} + +static +MyAfterProc(struct rx_call *acall, afs_int32 code) +{ + VTRANS_LOCK; + runningCalls--; + VTRANS_UNLOCK; + return 0; +} + +/* Called every GCWAKEUP seconds to try to unlock all our partitions, + * if we're idle and there are no active transactions + */ +static +TryUnlock() +{ + /* if there are no running calls, and there are no active transactions, then + * it should be safe to release any partition locks we've accumulated */ + VTRANS_LOCK; + if (runningCalls == 0 && TransList() == (struct volser_trans *)0) { + VTRANS_UNLOCK; + VPFullUnlock(); /* in volprocs.c */ + } else + VTRANS_UNLOCK; +} + +/* background daemon for timing out transactions */ +static +BKGLoop() +{ + struct timeval tv; + int loop = 0; + + while (1) { + tv.tv_sec = GCWAKEUP; + tv.tv_usec = 0; +#ifdef AFS_PTHREAD_ENV + select(0, 0, 0, 0, &tv); +#else + (void)IOMGR_Select(0, 0, 0, 0, &tv); +#endif + GCTrans(); + TryUnlock(); + loop++; + if (loop == 10) { /* reopen log every 5 minutes */ + loop = 0; + ReOpenLog(AFSDIR_SERVER_VOLSERLOG_FILEPATH); + } + } +} + +/* Background daemon for sleeping so the volserver does not become I/O bound */ +afs_int32 TTsleep, TTrun; +static +BKGSleep() +{ + struct volser_trans *tt; + + if (TTsleep) { + while (1) { +#ifdef AFS_PTHREAD_ENV + sleep(TTrun); +#else /* AFS_PTHREAD_ENV */ + IOMGR_Sleep(TTrun); +#endif + VTRANS_LOCK; + for (tt = TransList(); tt; tt = tt->next) { + if ((strcmp(tt->lastProcName, "DeleteVolume") == 0) + || (strcmp(tt->lastProcName, "Clone") == 0) + || (strcmp(tt->lastProcName, "ReClone") == 0) + || (strcmp(tt->lastProcName, "Forward") == 0) + || (strcmp(tt->lastProcName, "Restore") == 0) + || (strcmp(tt->lastProcName, "ForwardMulti") == 0)) + break; + } + if (tt) { + VTRANS_UNLOCK; + sleep(TTsleep); + } else + VTRANS_UNLOCK; + } + } +} + +#ifndef AFS_NT40_ENV +int +volser_syscall(a3, a4, a5) + afs_uint32 a3, a4; + void *a5; +{ + afs_uint32 rcode; + void (*old) (); + +#ifndef AFS_LINUX20_ENV + old = signal(SIGSYS, SIG_IGN); +#endif + rcode = + syscall(AFS_SYSCALL /* AFS_SYSCALL */ , 28 /* AFSCALL_CALL */ , a3, + a4, a5); +#ifndef AFS_LINUX20_ENV + signal(SIGSYS, old); +#endif + + return rcode; +} +#endif + + +/* check whether caller is authorized to manage RX statistics */ +int +vol_rxstat_userok(call) + struct rx_call *call; +{ + return afsconf_SuperUser(tdir, call, NULL); +} + +#include "AFS_component_version_number.c" +main(argc, argv) + int argc; + char **argv; +{ + register afs_int32 code; + struct rx_securityClass *(securityObjects[3]); + struct rx_service *service; + struct ktc_encryptionKey tkey; + int rxpackets = 100; + char commandLine[150]; + int i; + int rxJumbograms = 1; /* default is to send and receive jumbograms. */ + int bufSize = 0; /* temp variable to read in udp socket buf size */ + +#ifdef AFS_AIX32_ENV + /* + * The following signal action for AIX is necessary so that in case of a + * crash (i.e. core is generated) we can include the user's data section + * in the core dump. Unfortunately, by default, only a partial core is + * generated which, in many cases, isn't too useful. + */ + struct sigaction nsa; + + sigemptyset(&nsa.sa_mask); + nsa.sa_handler = SIG_DFL; + nsa.sa_flags = SA_FULLDUMP; + sigaction(SIGABRT, &nsa, NULL); + sigaction(SIGSEGV, &nsa, NULL); +#endif + osi_audit(VS_StartEvent, 0, AUD_END); + + /* Initialize dirpaths */ + if (!(initAFSDirPath() & AFSDIR_SERVER_PATHS_OK)) { +#ifdef AFS_NT40_ENV + ReportErrorEventAlt(AFSEVT_SVR_NO_INSTALL_DIR, 0, argv[0], 0); +#endif + fprintf(stderr, "%s: Unable to obtain AFS server directory.\n", + argv[0]); + exit(2); + } + + for (commandLine[0] = '\0', i = 0; i < argc; i++) { + if (i > 0) + strcat(commandLine, " "); + strcat(commandLine, argv[i]); + } + + TTsleep = TTrun = 0; + + /* parse cmd line */ + for (code = 1; code < argc; code++) { + if (strcmp(argv[code], "-log") == 0) { + /* set extra logging flag */ + DoLogging = 1; + } else if (strcmp(argv[code], "-help") == 0) { + goto usage; + } else if (strcmp(argv[code], "-p") == 0) { + lwps = atoi(argv[++code]); + if (lwps > MAXLWP) { + printf("Warning: '-p %d' is too big; using %d instead\n", + lwps, MAXLWP); + lwps = MAXLWP; + } + } else if (strcmp(argv[code], "-nojumbo") == 0) { + rxJumbograms = 0; + } else if (strcmp(argv[code], "-sleep") == 0) { + sscanf(argv[++code], "%d/%d", &TTsleep, &TTrun); + if ((TTsleep < 0) || (TTrun <= 0)) { + printf("Warning: '-sleep %d/%d' is incorrect; ignoring\n", + TTsleep, TTrun); + TTsleep = TTrun = 0; + } + } else if (strcmp(argv[code], "-udpsize") == 0) { + if ((code + 1) >= argc) { + printf("You have to specify -udpsize \n"); + exit(1); + } + sscanf(argv[++code], "%d", &bufSize); + if (bufSize < rx_GetMinUdpBufSize()) + printf + ("Warning:udpsize %d is less than minimum %d; ignoring\n", + bufSize, rx_GetMinUdpBufSize()); + else + udpBufSize = bufSize; + } else if (strcmp(argv[code], "-enable_peer_stats") == 0) { + rx_enablePeerRPCStats(); + } else if (strcmp(argv[code], "-enable_process_stats") == 0) { + rx_enableProcessRPCStats(); + } +#ifndef AFS_NT40_ENV + else if (strcmp(argv[code], "-syslog") == 0) { + /* set syslog logging flag */ + serverLogSyslog = 1; + } else if (strncmp(argv[code], "-syslog=", 8) == 0) { + serverLogSyslog = 1; + serverLogSyslogFacility = atoi(argv[code] + 8); + } +#endif + else { + printf("volserver: unrecognized flag '%s'\n", argv[code]); + usage: +#ifndef AFS_NT40_ENV + printf("Usage: volserver [-log] [-p ] " + "[-udpsize ] " + "[-syslog[=FACILITY]] " + "[-enable_peer_stats] [-enable_process_stats] " + "[-help]\n"); +#else + printf("Usage: volserver [-log] [-p ] " + "[-udpsize ] " + "[-enable_peer_stats] [-enable_process_stats] " + "[-help]\n"); +#endif + VS_EXIT(1); + } + } +#ifdef AFS_SGI_VNODE_GLUE + if (afs_init_kernel_config(-1) < 0) { + printf + ("Can't determine NUMA configuration, not starting volserver.\n"); + exit(1); + } +#endif + InitErrTabs(); + +#ifdef AFS_NT40_ENV + if (afs_winsockInit() < 0) { + ReportErrorEventAlt(AFSEVT_SVR_WINSOCK_INIT_FAILED, 0, argv[0], 0); + printf("Volume server unable to start winsock, exiting.\n"); + exit(1); + } +#endif + VInitVolumePackage(volumeUtility, 0, 0, CONNECT_FS, 0); + DInit(40); +#ifndef AFS_PTHREAD_ENV + vol_PollProc = IOMGR_Poll; /* tell vol pkg to poll io system periodically */ +#endif +#ifndef AFS_NT40_ENV + rxi_syscallp = volser_syscall; +#endif + rx_nPackets = rxpackets; /* set the max number of packets */ + if (udpBufSize) + rx_SetUdpBufSize(udpBufSize); /* set the UDP buffer size for receive */ + code = rx_Init((int)htons(AFSCONF_VOLUMEPORT)); + if (code) { + fprintf(stderr, "rx init failed on socket AFSCONF_VOLUMEPORT %u\n", + AFSCONF_VOLUMEPORT); + VS_EXIT(1); + } + if (!rxJumbograms) { + /* Don't allow 3.4 vos clients to send jumbograms and we don't send. */ + rx_SetNoJumbo(); + } + rx_GetIFInfo(); + rx_SetRxDeadTime(420); + memset(busyFlags, 0, sizeof(busyFlags)); + + /* Open FileLog and map stdout, stderr into it */ + OpenLog(AFSDIR_SERVER_VOLSERLOG_FILEPATH); + SetupLogSignals(); + + { +#ifdef AFS_PTHREAD_ENV + pthread_t tid; + pthread_attr_t tattr; + assert(pthread_attr_init(&tattr) == 0); + assert(pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED) == 0); + + assert(pthread_create(&tid, &tattr, (void *)BKGLoop, NULL) == 0); +#else + PROCESS pid; + LWP_CreateProcess(BKGLoop, 16*1024, 3, 0, "vol bkg daemon", &pid); + LWP_CreateProcess(BKGSleep,16*1024, 3, 0, "vol slp daemon", &pid); +#endif + } + + /* Create a single security object, in this case the null security object, for unauthenticated connections, which will be used to control security on connections made to this server */ + + tdir = afsconf_Open(AFSDIR_SERVER_ETC_DIRPATH); + if (!tdir) { + Abort("volser: could not open conf files in %s\n", + AFSDIR_SERVER_ETC_DIRPATH); + VS_EXIT(1); + } + afsconf_GetKey(tdir, 999, &tkey); + securityObjects[0] = rxnull_NewServerSecurityObject(); + securityObjects[1] = (struct rx_securityClass *)0; /* don't bother with rxvab */ + securityObjects[2] = + rxkad_NewServerSecurityObject(0, tdir, afsconf_GetKey, NULL); + if (securityObjects[0] == (struct rx_securityClass *)0) + Abort("rxnull_NewServerSecurityObject"); + service = + rx_NewService(0, VOLSERVICE_ID, "VOLSER", securityObjects, 3, + AFSVolExecuteRequest); + if (service == (struct rx_service *)0) + Abort("rx_NewService"); + rx_SetBeforeProc(service, MyBeforeProc); + rx_SetAfterProc(service, MyAfterProc); + rx_SetIdleDeadTime(service, 0); /* never timeout */ + if (lwps < 4) + lwps = 4; + rx_SetMaxProcs(service, lwps); +#ifdef AFS_SGI_ENV + rx_SetStackSize(service, 49152); +#else + rx_SetStackSize(service, 32768); +#endif + + service = + rx_NewService(0, RX_STATS_SERVICE_ID, "rpcstats", securityObjects, 3, + RXSTATS_ExecuteRequest); + if (service == (struct rx_service *)0) + Abort("rx_NewService"); + rx_SetMinProcs(service, 2); + rx_SetMaxProcs(service, 4); + + Log("Starting AFS Volserver %s (%s)\n", VolserVersion, commandLine); + if (TTsleep) { + Log("Will sleep %d second%s every %d second%s\n", TTsleep, + (TTsleep > 1) ? "s" : "", TTrun + TTsleep, + (TTrun + TTsleep > 1) ? "s" : ""); + } + + /* allow super users to manage RX statistics */ + rx_SetRxStatUserOk(vol_rxstat_userok); + + rx_StartServer(1); /* Donate this process to the server process pool */ + + osi_audit(VS_FinishEvent, (-1), AUD_END); + Abort("StartServer returned?"); +} diff --git a/src/volser/volprocs.c b/src/volser/volprocs.c new file mode 100644 index 000000000..433a7a618 --- /dev/null +++ b/src/volser/volprocs.c @@ -0,0 +1,2908 @@ +/* + * Copyright 2000, International Business Machines Corporation and others. + * All Rights Reserved. + * + * This software has been released under the terms of the IBM Public + * License. For details, see the LICENSE file in the top-level source + * directory or online at http://www.openafs.org/dl/license10.html + */ + +#include +#include + +RCSID + ("$Header: /cvs/openafs/src/volser/volprocs.c,v 1.33 2004/01/01 06:22:31 shadow Exp $"); + +#include +#include +#include +#ifdef AFS_NT40_ENV +#include +#include +#else +#include +#include +#include +#endif + +#ifdef HAVE_STRING_H +#include +#else +#ifdef HAVE_STRINGS_H +#include +#endif +#endif + +#include +#include +#include +#include +#include +#include +#include +#ifdef AFS_PTHREAD_ENV +#include +#else /* AFS_PTHREAD_ENV */ +#include +#endif /* AFS_PTHREAD_ENV */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef AFS_NT40_ENV +#include +#endif +#include +#include +#include +#include "vol.h" +#include +#include +#include "afs/audit.h" +#include + +#include "volser.h" +#include "volint.h" + +#include "volser_prototypes.h" + +extern int DoLogging; +extern struct volser_trans *FindTrans(), *NewTrans(), *TransList(); +extern struct afsconf_dir *tdir; + +/* Needed by Irix. Leave, or include a header */ +extern char *volutil_PartitionName(); + +extern void LogError(afs_int32 errcode); + +/* Forward declarations */ +static int GetPartName(afs_int32 partid, char *pname); + +#define OneDay (24*60*60) + +#ifdef AFS_NT40_ENV +#define ENOTCONN 134 +#endif + +afs_int32 localTid = 1; +afs_int32 VolPartitionInfo(), VolNukeVolume(), VolCreateVolume(), +VolDeleteVolume(), VolClone(); +afs_int32 VolReClone(), VolTransCreate(), VolGetNthVolume(), VolGetFlags(), +VolForward(), VolDump(); +afs_int32 VolRestore(), VolEndTrans(), VolSetForwarding(), VolGetStatus(), +VolSetInfo(), VolGetName(); +afs_int32 VolListPartitions(), VolListOneVolume(), +VolXListOneVolume(), VolXListVolumes(); +afs_int32 VolListVolumes(), XVolListPartitions(), VolMonitor(), +VolSetIdsTypes(), VolSetDate(), VolSetFlags(); + +/* this call unlocks all of the partition locks we've set */ +int +VPFullUnlock() +{ + register struct DiskPartition *tp; + for (tp = DiskPartitionList; tp; tp = tp->next) { + if (tp->lock_fd != -1) { + close(tp->lock_fd); /* releases flock held on this partition */ + tp->lock_fd = -1; + } + } + return 0; +} + +/* get partition id from a name */ +afs_int32 +PartitionID(char *aname) +{ + register char tc; + register int code = 0; + char ascii[3]; + + tc = *aname; + if (tc == 0) + return -1; /* unknown */ + + /* otherwise check for vicepa or /vicepa, or just plain "a" */ + ascii[2] = 0; + if (!strncmp(aname, "/vicep", 6)) { + strncpy(ascii, aname + 6, 2); + } else + return -1; /* bad partition name */ + /* now partitions are named /vicepa ... /vicepz, /vicepaa, /vicepab, .../vicepzz, and are numbered + * from 0. Do the appropriate conversion */ + if (ascii[1] == 0) { + /* one char name, 0..25 */ + if (ascii[0] < 'a' || ascii[0] > 'z') + return -1; /* wrongo */ + return ascii[0] - 'a'; + } else { + /* two char name, 26 .. */ + if (ascii[0] < 'a' || ascii[0] > 'z') + return -1; /* wrongo */ + if (ascii[1] < 'a' || ascii[1] > 'z') + return -1; /* just as bad */ + code = (ascii[0] - 'a') * 26 + (ascii[1] - 'a') + 26; + if (code > VOLMAXPARTS) + return -1; + return code; + } +} + +static int +ConvertVolume(afs_int32 avol, char *aname, afs_int32 asize) +{ + if (asize < 18) + return -1; + /* It's better using the Generic VFORMAT since otherwise we have to make changes to too many places... The 14 char limitation in names hits us again in AIX; print in field of 9 digits (still 10 for the rest), right justified with 0 padding */ + (void)afs_snprintf(aname, asize, VFORMAT, (unsigned long)avol); + return 0; +} + +static int +ConvertPartition(int apartno, char *aname, int asize) +{ + if (asize < 10) + return E2BIG; + if (apartno < 0) + return EINVAL; + strcpy(aname, "/vicep"); + if (apartno < 26) { + aname[6] = 'a' + apartno; + aname[7] = 0; + } else { + apartno -= 26; + aname[6] = 'a' + (apartno / 26); + aname[7] = 'a' + (apartno % 26); + aname[8] = 0; + } + return 0; +} + +/* the only attach function that takes a partition is "...ByName", so we use it */ +struct Volume * +XAttachVolume(afs_int32 *error, afs_int32 avolid, afs_int32 apartid, int amode) +{ + char pbuf[30], vbuf[20]; + register struct Volume *tv; + + if (ConvertPartition(apartid, pbuf, sizeof(pbuf))) { + *error = EINVAL; + return NULL; + } + if (ConvertVolume(avolid, vbuf, sizeof(vbuf))) { + *error = EINVAL; + return NULL; + } + tv = VAttachVolumeByName(error, pbuf, vbuf, amode); + return tv; +} + +/* Adapted from the file server; create a root directory for this volume */ +static int +ViceCreateRoot(Volume *vp) +{ + DirHandle dir; + struct acl_accessList *ACL; + ViceFid did; + Inode inodeNumber, nearInode; + char buf[SIZEOF_LARGEDISKVNODE]; + struct VnodeDiskObject *vnode = (struct VnodeDiskObject *)buf; + struct VnodeClassInfo *vcp = &VnodeClassInfo[vLarge]; + IHandle_t *h; + FdHandle_t *fdP; + int code; + afs_fsize_t length; + + memset(vnode, 0, SIZEOF_LARGEDISKVNODE); + + V_pref(vp, nearInode); + inodeNumber = + IH_CREATE(V_linkHandle(vp), V_device(vp), + VPartitionPath(V_partition(vp)), nearInode, V_parentId(vp), + 1, 1, 0); + assert(VALID_INO(inodeNumber)); + + SetSalvageDirHandle(&dir, V_parentId(vp), vp->device, inodeNumber); + did.Volume = V_id(vp); + did.Vnode = (VnodeId) 1; + did.Unique = 1; + + assert(!(MakeDir(&dir, &did, &did))); + DFlush(); /* flush all modified dir buffers out */ + DZap(&dir); /* Remove all buffers for this dir */ + length = Length(&dir); /* Remember size of this directory */ + + FidZap(&dir); /* Done with the dir handle obtained via SetSalvageDirHandle() */ + + /* build a single entry ACL that gives all rights to system:administrators */ + /* this section of code assumes that access list format is not going to + * change + */ + ACL = VVnodeDiskACL(vnode); + ACL->size = sizeof(struct acl_accessList); + ACL->version = ACL_ACLVERSION; + ACL->total = 1; + ACL->positive = 1; + ACL->negative = 0; + ACL->entries[0].id = -204; /* this assumes System:administrators is group -204 */ + ACL->entries[0].rights = + PRSFS_READ | PRSFS_WRITE | PRSFS_INSERT | PRSFS_LOOKUP | PRSFS_DELETE + | PRSFS_LOCK | PRSFS_ADMINISTER; + + vnode->type = vDirectory; + vnode->cloned = 0; + vnode->modeBits = 0777; + vnode->linkCount = 2; + VNDISK_SET_LEN(vnode, length); + vnode->uniquifier = 1; + V_uniquifier(vp) = vnode->uniquifier + 1; + vnode->dataVersion = 1; + VNDISK_SET_INO(vnode, inodeNumber); + vnode->unixModifyTime = vnode->serverModifyTime = V_creationDate(vp); + vnode->author = 0; + vnode->owner = 0; + vnode->parent = 0; + vnode->vnodeMagic = vcp->magic; + + IH_INIT(h, vp->device, V_parentId(vp), + vp->vnodeIndex[vLarge].handle->ih_ino); + fdP = IH_OPEN(h); + assert(fdP != NULL); + code = FDH_SEEK(fdP, vnodeIndexOffset(vcp, 1), SEEK_SET); + assert(code >= 0); + code = FDH_WRITE(fdP, vnode, SIZEOF_LARGEDISKVNODE); + assert(code == SIZEOF_LARGEDISKVNODE); + FDH_REALLYCLOSE(fdP); + IH_RELEASE(h); + VNDISK_GET_LEN(length, vnode); + V_diskused(vp) = nBlocks(length); + + return 1; +} + +afs_int32 +SAFSVolPartitionInfo(struct rx_call *acid, char *pname, struct diskPartition + *partition) +{ + afs_int32 code; + + code = VolPartitionInfo(acid, pname, partition); + osi_auditU(acid, VS_ParInfEvent, code, AUD_STR, pname, AUD_END); + return code; +} + +afs_int32 +VolPartitionInfo(struct rx_call *acid, char *pname, struct diskPartition + *partition) +{ + register struct DiskPartition *dp; + +/* + if (!afsconf_SuperUser(tdir, acid, caller)) return VOLSERBAD_ACCESS; +*/ + VResetDiskUsage(); + dp = VGetPartition(pname, 0); + if (dp) { + strncpy(partition->name, dp->name, 32); + strncpy(partition->devName, dp->devName, 32); + partition->lock_fd = dp->lock_fd; + partition->free = dp->free; + partition->minFree = dp->totalUsable; + return 0; + } else + return VOLSERILLEGAL_PARTITION; +} + +/* obliterate a volume completely, and slowly. */ +afs_int32 +SAFSVolNukeVolume(struct rx_call *acid, afs_int32 apartID, afs_int32 avolID) +{ + afs_int32 code; + + code = VolNukeVolume(acid, apartID, avolID); + osi_auditU(acid, VS_NukVolEvent, code, AUD_LONG, avolID, AUD_END); + return code; +} + +afs_int32 +VolNukeVolume(struct rx_call *acid, afs_int32 apartID, afs_int32 avolID) +{ + register char *tp; + char partName[50]; + afs_int32 error; + register afs_int32 code; + struct Volume *tvp; + char caller[MAXKTCNAMELEN]; + + /* check for access */ + if (!afsconf_SuperUser(tdir, acid, caller)) + return VOLSERBAD_ACCESS; + if (DoLogging) + Log("%s is executing VolNukeVolume %u\n", caller, avolID); + + tp = volutil_PartitionName(apartID); + if (!tp) + return VOLSERNOVOL; + strcpy(partName, tp); /* remember it for later */ + /* we first try to attach the volume in update mode, so that the file + * server doesn't try to use it (and abort) while (or after) we delete it. + * If we don't get the volume, that's fine, too. We just won't put it back. + */ + tvp = XAttachVolume(&error, avolID, apartID, V_VOLUPD); + code = nuke(partName, avolID); + if (tvp) + VDetachVolume(&error, tvp); + return code; +} + +/* create a new volume, with name aname, on the specified partition (1..n) + * and of type atype (readwriteVolume, readonlyVolume, backupVolume). + * As input, if *avolid is 0, we allocate a new volume id, otherwise we use *avolid + * for the volume id (useful for things like volume restore). + * Return the new volume id in *avolid. + */ +afs_int32 +SAFSVolCreateVolume(struct rx_call *acid, afs_int32 apart, char *aname, + afs_int32 atype, afs_int32 aparent, afs_int32 *avolid, + afs_int32 *atrans) +{ + afs_int32 code; + + code = + VolCreateVolume(acid, apart, aname, atype, aparent, avolid, atrans); + osi_auditU(acid, VS_CrVolEvent, code, AUD_LONG, *atrans, AUD_LONG, + *avolid, AUD_STR, aname, AUD_LONG, atype, AUD_LONG, aparent, + AUD_END); + return code; +} + +afs_int32 +VolCreateVolume(struct rx_call *acid, afs_int32 apart, char *aname, + afs_int32 atype, afs_int32 aparent, afs_int32 *avolid, + afs_int32 *atrans) +{ + afs_int32 error; + register Volume *vp; + afs_int32 junk; /* discardable error code */ + register afs_int32 volumeID, doCreateRoot = 1; + register struct volser_trans *tt; + char ppath[30]; + char caller[MAXKTCNAMELEN]; + + if (strlen(aname) > 31) + return VOLSERBADNAME; + if (!afsconf_SuperUser(tdir, acid, caller)) + return VOLSERBAD_ACCESS; + if (DoLogging) + Log("%s is executing CreateVolume '%s'\n", caller, aname); + if ((error = ConvertPartition(apart, ppath, sizeof(ppath)))) + return error; /*a standard unix error */ + if (atype != readwriteVolume && atype != readonlyVolume + && atype != backupVolume) + return EINVAL; + if ((volumeID = *avolid) == 0) { + + Log("1 Volser: CreateVolume: missing volume number; %s volume not created\n", aname); + return E2BIG; + + } + if ((aparent == volumeID) && (atype == readwriteVolume)) { + doCreateRoot = 0; + } + if (aparent == 0) + aparent = volumeID; + tt = NewTrans(volumeID, apart); + if (!tt) { + Log("1 createvolume: failed to create trans\n"); + return VOLSERVOLBUSY; /* volume already busy! */ + } + vp = VCreateVolume(&error, ppath, volumeID, aparent); + if (error) { + Log("1 Volser: CreateVolume: Unable to create the volume; aborted, error code %u\n", error); + LogError(error); + DeleteTrans(tt); + return EIO; + } + V_uniquifier(vp) = 1; + V_creationDate(vp) = V_copyDate(vp); + V_inService(vp) = V_blessed(vp) = 1; + V_type(vp) = atype; + AssignVolumeName(&V_disk(vp), aname, 0); + if (doCreateRoot) + ViceCreateRoot(vp); + V_destroyMe(vp) = DESTROY_ME; + V_inService(vp) = 0; + V_maxquota(vp) = 5000; /* set a quota of 5000 at init time */ + VUpdateVolume(&error, vp); + if (error) { + Log("1 Volser: create UpdateVolume failed, code %d\n", error); + LogError(error); + DeleteTrans(tt); + VDetachVolume(&junk, vp); /* rather return the real error code */ + return error; + } + tt->volume = vp; + *atrans = tt->tid; + strcpy(tt->lastProcName, "CreateVolume"); + tt->rxCallPtr = acid; + Log("1 Volser: CreateVolume: volume %u (%s) created\n", volumeID, aname); + tt->rxCallPtr = (struct rx_call *)0; + if (TRELE(tt)) + return VOLSERTRELE_ERROR; + return 0; +} + +/* delete the volume associated with this transaction */ +afs_int32 +SAFSVolDeleteVolume(struct rx_call *acid, afs_int32 atrans) +{ + afs_int32 code; + + code = VolDeleteVolume(acid, atrans); + osi_auditU(acid, VS_DelVolEvent, code, AUD_LONG, atrans, AUD_END); + return code; +} + +afs_int32 +VolDeleteVolume(struct rx_call *acid, afs_int32 atrans) +{ + register struct volser_trans *tt; + afs_int32 error; + char caller[MAXKTCNAMELEN]; + + if (!afsconf_SuperUser(tdir, acid, caller)) + return VOLSERBAD_ACCESS; + tt = FindTrans(atrans); + if (!tt) + return ENOENT; + if (tt->vflags & VTDeleted) { + Log("1 Volser: Delete: volume %u already deleted \n", tt->volid); + TRELE(tt); + return ENOENT; + } + if (DoLogging) + Log("%s is executing Delete Volume %u\n", caller, tt->volid); + strcpy(tt->lastProcName, "DeleteVolume"); + tt->rxCallPtr = acid; + VPurgeVolume(&error, tt->volume); /* don't check error code, it is not set! */ + tt->vflags |= VTDeleted; /* so we know not to do anything else to it */ + tt->rxCallPtr = (struct rx_call *)0; + if (TRELE(tt)) + return VOLSERTRELE_ERROR; + + Log("1 Volser: Delete: volume %u deleted \n", tt->volid); + return 0; /* vpurgevolume doesn't set an error code */ +} + +/* make a clone of the volume associated with atrans, possibly giving it a new + * number (allocate a new number if *newNumber==0, otherwise use *newNumber + * for the clone's id). The new clone is given the name newName. Finally, due to + * efficiency considerations, if purgeId is non-zero, we purge that volume when doing + * the clone operation. This may be useful when making new backup volumes, for instance + * since the net result of a clone and a purge generally leaves many inode ref counts + * the same, while doing them separately would result in far more iincs and idecs being + * peformed (and they are slow operations). + */ +/* for efficiency reasons, sometimes faster to piggyback a purge here */ +afs_int32 +SAFSVolClone(struct rx_call *acid, afs_int32 atrans, afs_int32 purgeId, + afs_int32 newType, char *newName, afs_int32 *newNumber) +{ + afs_int32 code; + + code = VolClone(acid, atrans, purgeId, newType, newName, newNumber); + osi_auditU(acid, VS_CloneEvent, code, AUD_LONG, atrans, AUD_LONG, purgeId, + AUD_STR, newName, AUD_LONG, newType, AUD_LONG, *newNumber, + AUD_END); + return code; +} + +afs_int32 +VolClone(struct rx_call *acid, afs_int32 atrans, afs_int32 purgeId, + afs_int32 newType, char *newName, afs_int32 *newNumber) +{ + VolumeId newId; + register struct Volume *originalvp, *purgevp, *newvp; + Error error, code; + register struct volser_trans *tt, *ttc; + char caller[MAXKTCNAMELEN]; + + if (strlen(newName) > 31) + return VOLSERBADNAME; + if (!afsconf_SuperUser(tdir, acid, caller)) + return VOLSERBAD_ACCESS; /*not a super user */ + if (DoLogging) + Log("%s is executing Clone Volume new name=%s\n", caller, newName); + error = 0; + originalvp = (Volume *) 0; + purgevp = (Volume *) 0; + newvp = (Volume *) 0; + tt = ttc = (struct volser_trans *)0; + + if (!newNumber || !*newNumber) { + Log("1 Volser: Clone: missing volume number for the clone; aborted\n"); + goto fail; + } + newId = *newNumber; + + if (newType != readonlyVolume && newType != backupVolume) + return EINVAL; + tt = FindTrans(atrans); + if (!tt) + return ENOENT; + if (tt->vflags & VTDeleted) { + Log("1 Volser: Clone: volume %u has been deleted \n", tt->volid); + TRELE(tt); + return ENOENT; + } + ttc = NewTrans(newId, tt->partition); + if (!ttc) { /* someone is messing with the clone already */ + TRELE(tt); + return VBUSY; + } + strcpy(tt->lastProcName, "Clone"); + tt->rxCallPtr = acid; + + + if (purgeId) { + purgevp = VAttachVolume(&error, purgeId, V_VOLUPD); + if (error) { + Log("1 Volser: Clone: Could not attach 'purge' volume %u; clone aborted\n", purgeId); + goto fail; + } + } else { + purgevp = NULL; + } + originalvp = tt->volume; + if ((V_type(originalvp) == backupVolume) + || (V_type(originalvp) == readonlyVolume)) { + Log("1 Volser: Clone: The volume to be cloned must be a read/write; aborted\n"); + error = EROFS; + goto fail; + } + if ((V_destroyMe(originalvp) == DESTROY_ME) || !V_inService(originalvp)) { + Log("1 Volser: Clone: Volume %d is offline and cannot be cloned\n", + V_id(originalvp)); + error = VOFFLINE; + goto fail; + } + if (purgevp) { + if (originalvp->device != purgevp->device) { + Log("1 Volser: Clone: Volumes %u and %u are on different devices\n", tt->volid, purgeId); + error = EXDEV; + goto fail; + } + if (V_type(purgevp) != readonlyVolume) { + Log("1 Volser: Clone: The \"purge\" volume must be a read only volume; aborted\n"); + error = EINVAL; + goto fail; + } + if (V_type(originalvp) == readonlyVolume + && V_parentId(originalvp) != V_parentId(purgevp)) { + Log("1 Volser: Clone: Volume %u and volume %u were not cloned from the same parent volume; aborted\n", tt->volid, purgeId); + error = EXDEV; + goto fail; + } + if (V_type(originalvp) == readwriteVolume + && tt->volid != V_parentId(purgevp)) { + Log("1 Volser: Clone: Volume %u was not originally cloned from volume %u; aborted\n", purgeId, tt->volid); + error = EXDEV; + goto fail; + } + } + + error = 0; + + newvp = + VCreateVolume(&error, originalvp->partition->name, newId, + V_parentId(originalvp)); + if (error) { + Log("1 Volser: Clone: Couldn't create new volume; clone aborted\n"); + newvp = (Volume *) 0; + goto fail; + } + if (newType == readonlyVolume) + V_cloneId(originalvp) = newId; + Log("1 Volser: Clone: Cloning volume %u to new volume %u\n", tt->volid, + newId); + if (purgevp) + Log("1 Volser: Clone: Purging old read only volume %u\n", purgeId); + CloneVolume(&error, originalvp, newvp, purgevp); + purgevp = NULL; /* clone releases it, maybe even if error */ + if (error) { + Log("1 Volser: Clone: clone operation failed with code %u\n", error); + LogError(error); + goto fail; + } + if (newType == readonlyVolume) { + AssignVolumeName(&V_disk(newvp), V_name(originalvp), ".readonly"); + V_type(newvp) = readonlyVolume; + } else if (newType == backupVolume) { + AssignVolumeName(&V_disk(newvp), V_name(originalvp), ".backup"); + V_type(newvp) = backupVolume; + V_backupId(originalvp) = newId; + } + strcpy(newvp->header->diskstuff.name, newName); + V_creationDate(newvp) = V_copyDate(newvp); + ClearVolumeStats(&V_disk(newvp)); + V_destroyMe(newvp) = DESTROY_ME; + V_inService(newvp) = 0; + if (newType == backupVolume) { + V_backupDate(originalvp) = V_copyDate(newvp); + V_backupDate(newvp) = V_copyDate(newvp); + } + V_inUse(newvp) = 0; + VUpdateVolume(&error, newvp); + if (error) { + Log("1 Volser: Clone: VUpdate failed code %u\n", error); + LogError(error); + goto fail; + } + VDetachVolume(&error, newvp); /* allow file server to get it's hands on it */ + newvp = NULL; + VUpdateVolume(&error, originalvp); + if (error) { + Log("1 Volser: Clone: original update %u\n", error); + LogError(error); + goto fail; + } + tt->rxCallPtr = (struct rx_call *)0; + if (TRELE(tt)) { + tt = (struct volser_trans *)0; + error = VOLSERTRELE_ERROR; + goto fail; + } + DeleteTrans(ttc); + return 0; + + fail: + if (purgevp) + VDetachVolume(&code, purgevp); + if (newvp) + VDetachVolume(&code, newvp); + if (tt) { + tt->rxCallPtr = (struct rx_call *)0; + TRELE(tt); + } + if (ttc) + DeleteTrans(ttc); + return error; +} + +/* reclone this volume into the specified id */ +afs_int32 +SAFSVolReClone(struct rx_call *acid, afs_int32 atrans, afs_int32 cloneId) +{ + afs_int32 code; + + code = VolReClone(acid, atrans, cloneId); + osi_auditU(acid, VS_ReCloneEvent, code, AUD_LONG, atrans, AUD_LONG, + cloneId, AUD_END); + return code; +} + +afs_int32 +VolReClone(struct rx_call *acid, afs_int32 atrans, afs_int32 cloneId) +{ + register struct Volume *originalvp, *clonevp; + Error error, code; + afs_int32 newType; + register struct volser_trans *tt, *ttc; + char caller[MAXKTCNAMELEN]; + + /*not a super user */ + if (!afsconf_SuperUser(tdir, acid, caller)) + return VOLSERBAD_ACCESS; + if (DoLogging) + Log("%s is executing Reclone Volume %u\n", caller, cloneId); + error = 0; + clonevp = originalvp = (Volume *) 0; + tt = (struct volser_trans *)0; + + tt = FindTrans(atrans); + if (!tt) + return ENOENT; + if (tt->vflags & VTDeleted) { + Log("1 Volser: VolReClone: volume %u has been deleted \n", tt->volid); + TRELE(tt); + return ENOENT; + } + ttc = NewTrans(cloneId, tt->partition); + if (!ttc) { /* someone is messing with the clone already */ + TRELE(tt); + return VBUSY; + } + strcpy(tt->lastProcName, "ReClone"); + tt->rxCallPtr = acid; + + originalvp = tt->volume; + if ((V_type(originalvp) == backupVolume) + || (V_type(originalvp) == readonlyVolume)) { + Log("1 Volser: Clone: The volume to be cloned must be a read/write; aborted\n"); + error = EROFS; + goto fail; + } + if ((V_destroyMe(originalvp) == DESTROY_ME) || !V_inService(originalvp)) { + Log("1 Volser: Clone: Volume %d is offline and cannot be cloned\n", + V_id(originalvp)); + error = VOFFLINE; + goto fail; + } + + clonevp = VAttachVolume(&error, cloneId, V_VOLUPD); + if (error) { + Log("1 Volser: can't attach clone %d\n", cloneId); + goto fail; + } + + newType = V_type(clonevp); /* type of the new volume */ + + if (originalvp->device != clonevp->device) { + Log("1 Volser: Clone: Volumes %u and %u are on different devices\n", + tt->volid, cloneId); + error = EXDEV; + goto fail; + } + if (V_type(clonevp) != readonlyVolume && V_type(clonevp) != backupVolume) { + Log("1 Volser: Clone: The \"recloned\" volume must be a read only volume; aborted\n"); + error = EINVAL; + goto fail; + } + if (V_type(originalvp) == readonlyVolume + && V_parentId(originalvp) != V_parentId(clonevp)) { + Log("1 Volser: Clone: Volume %u and volume %u were not cloned from the same parent volume; aborted\n", tt->volid, cloneId); + error = EXDEV; + goto fail; + } + if (V_type(originalvp) == readwriteVolume + && tt->volid != V_parentId(clonevp)) { + Log("1 Volser: Clone: Volume %u was not originally cloned from volume %u; aborted\n", cloneId, tt->volid); + error = EXDEV; + goto fail; + } + + error = 0; + Log("1 Volser: Clone: Recloning volume %u to volume %u\n", tt->volid, + cloneId); + CloneVolume(&error, originalvp, clonevp, clonevp); + if (error) { + Log("1 Volser: Clone: reclone operation failed with code %d\n", + error); + LogError(error); + goto fail; + } + + /* fix up volume name and type, CloneVolume just propagated RW's */ + if (newType == readonlyVolume) { + AssignVolumeName(&V_disk(clonevp), V_name(originalvp), ".readonly"); + V_type(clonevp) = readonlyVolume; + } else if (newType == backupVolume) { + AssignVolumeName(&V_disk(clonevp), V_name(originalvp), ".backup"); + V_type(clonevp) = backupVolume; + V_backupId(originalvp) = cloneId; + } + /* don't do strcpy onto diskstuff.name, it's still OK from 1st clone */ + + /* pretend recloned volume is a totally new instance */ + V_copyDate(clonevp) = time(0); + V_creationDate(clonevp) = V_copyDate(clonevp); + ClearVolumeStats(&V_disk(clonevp)); + V_destroyMe(clonevp) = 0; + V_inService(clonevp) = 0; + if (newType == backupVolume) { + V_backupDate(originalvp) = V_copyDate(clonevp); + V_backupDate(clonevp) = V_copyDate(clonevp); + } + V_inUse(clonevp) = 0; + VUpdateVolume(&error, clonevp); + if (error) { + Log("1 Volser: Clone: VUpdate failed code %u\n", error); + LogError(error); + goto fail; + } + VDetachVolume(&error, clonevp); /* allow file server to get it's hands on it */ + clonevp = NULL; + VUpdateVolume(&error, originalvp); + if (error) { + Log("1 Volser: Clone: original update %u\n", error); + LogError(error); + goto fail; + } + tt->rxCallPtr = (struct rx_call *)0; + if (TRELE(tt)) { + tt = (struct volser_trans *)0; + error = VOLSERTRELE_ERROR; + goto fail; + } + + DeleteTrans(ttc); + + { + struct DiskPartition *tpartp = originalvp->partition; + FSYNC_askfs(cloneId, tpartp->name, FSYNC_RESTOREVOLUME, 0); + } + return 0; + + fail: + if (clonevp) + VDetachVolume(&code, clonevp); + if (tt) { + tt->rxCallPtr = (struct rx_call *)0; + TRELE(tt); + } + if (ttc) + DeleteTrans(ttc); + return error; +} + +/* create a new transaction, associated with volume and partition. Type of + * volume transaction is spec'd by iflags. New trans id is returned in ttid. + * See volser.h for definition of iflags (the constants are named IT*). + */ +afs_int32 +SAFSVolTransCreate(struct rx_call *acid, afs_int32 volume, afs_int32 partition, + afs_int32 iflags, afs_int32 *ttid) +{ + afs_int32 code; + + code = VolTransCreate(acid, volume, partition, iflags, ttid); + osi_auditU(acid, VS_TransCrEvent, code, AUD_LONG, *ttid, AUD_LONG, volume, + AUD_END); + return code; +} + +afs_int32 +VolTransCreate(struct rx_call *acid, afs_int32 volume, afs_int32 partition, + afs_int32 iflags, afs_int32 *ttid) +{ + register struct volser_trans *tt; + register Volume *tv; + afs_int32 error, code; + afs_int32 mode; + char caller[MAXKTCNAMELEN]; + + if (!afsconf_SuperUser(tdir, acid, caller)) + return VOLSERBAD_ACCESS; /*not a super user */ + if (iflags & ITCreate) + mode = V_SECRETLY; + else if (iflags & ITBusy) + mode = V_CLONE; + else if (iflags & ITReadOnly) + mode = V_READONLY; + else if (iflags & ITOffline) + mode = V_VOLUPD; + else { + Log("1 Volser: TransCreate: Could not create trans, error %u\n", + EINVAL); + LogError(EINVAL); + return EINVAL; + } + tt = NewTrans(volume, partition); + if (!tt) { + /* can't create a transaction? put the volume back */ + Log("1 transcreate: can't create transaction\n"); + return VOLSERVOLBUSY; + } + tv = XAttachVolume(&error, volume, partition, mode); + if (error) { + /* give up */ + if (tv) + VDetachVolume(&code, tv); + DeleteTrans(tt); + return error; + } + tt->volume = tv; + *ttid = tt->tid; + tt->iflags = iflags; + tt->vflags = 0; + strcpy(tt->lastProcName, "TransCreate"); + if (TRELE(tt)) + return VOLSERTRELE_ERROR; + + return 0; +} + +/* using aindex as a 0-based index, return the aindex'th volume on this server + * Both the volume number and partition number (one-based) are returned. + */ +afs_int32 +SAFSVolGetNthVolume(struct rx_call *acid, afs_int32 aindex, afs_int32 *avolume, + afs_int32 *apart) +{ + afs_int32 code; + + code = VolGetNthVolume(acid, aindex, avolume, apart); + osi_auditU(acid, VS_GetNVolEvent, code, AUD_LONG, *avolume, AUD_END); + return code; +} + +afs_int32 +VolGetNthVolume(struct rx_call *acid, afs_int32 aindex, afs_int32 *avolume, + afs_int32 *apart) +{ + Log("1 Volser: GetNthVolume: Not yet implemented\n"); + return VOLSERNO_OP; +} + +/* return the volume flags (VT* constants in volser.h) associated with this + * transaction. + */ +afs_int32 +SAFSVolGetFlags(struct rx_call *acid, afs_int32 atid, afs_int32 *aflags) +{ + afs_int32 code; + + code = VolGetFlags(acid, atid, aflags); + osi_auditU(acid, VS_GetFlgsEvent, code, AUD_LONG, atid, AUD_END); + return code; +} + +afs_int32 +VolGetFlags(struct rx_call *acid, afs_int32 atid, afs_int32 *aflags) +{ + register struct volser_trans *tt; + + tt = FindTrans(atid); + if (!tt) + return ENOENT; + if (tt->vflags & VTDeleted) { + Log("1 Volser: VolGetFlags: volume %u has been deleted \n", + tt->volid); + TRELE(tt); + return ENOENT; + } + strcpy(tt->lastProcName, "GetFlags"); + tt->rxCallPtr = acid; + *aflags = tt->vflags; + tt->rxCallPtr = (struct rx_call *)0; + if (TRELE(tt)) + return VOLSERTRELE_ERROR; + + return 0; +} + +/* Change the volume flags (VT* constants in volser.h) associated with this + * transaction. Effects take place immediately on volume, although volume + * remains attached as usual by the transaction. + */ +afs_int32 +SAFSVolSetFlags(struct rx_call *acid, afs_int32 atid, afs_int32 aflags) +{ + afs_int32 code; + + code = VolSetFlags(acid, atid, aflags); + osi_auditU(acid, VS_SetFlgsEvent, code, AUD_LONG, atid, AUD_LONG, aflags, + AUD_END); + return code; +} + +afs_int32 +VolSetFlags(struct rx_call *acid, afs_int32 atid, afs_int32 aflags) +{ + register struct volser_trans *tt; + register struct Volume *vp; + afs_int32 error; + char caller[MAXKTCNAMELEN]; + + if (!afsconf_SuperUser(tdir, acid, caller)) + return VOLSERBAD_ACCESS; /*not a super user */ + /* find the trans */ + tt = FindTrans(atid); + if (!tt) + return ENOENT; + if (tt->vflags & VTDeleted) { + Log("1 Volser: VolSetFlags: volume %u has been deleted \n", + tt->volid); + TRELE(tt); + return ENOENT; + } + strcpy(tt->lastProcName, "SetFlags"); + tt->rxCallPtr = acid; + vp = tt->volume; /* pull volume out of transaction */ + + /* check if we're allowed to make any updates */ + if (tt->iflags & ITReadOnly) { + TRELE(tt); + return EROFS; + } + + /* handle delete-on-salvage flag */ + if (aflags & VTDeleteOnSalvage) { + V_destroyMe(tt->volume) = DESTROY_ME; + } else { + V_destroyMe(tt->volume) = 0; + } + + if (aflags & VTOutOfService) { + V_inService(vp) = 0; + } else { + V_inService(vp) = 1; + } + VUpdateVolume(&error, vp); + tt->vflags = aflags; + tt->rxCallPtr = (struct rx_call *)0; + if (TRELE(tt) && !error) + return VOLSERTRELE_ERROR; + + return error; +} + +/* dumpS the volume associated with a particular transaction from a particular + * date. Send the dump to a different transaction (destTrans) on the server + * specified by the destServer structure. + */ +afs_int32 +SAFSVolForward(struct rx_call *acid, afs_int32 fromTrans, afs_int32 fromDate, + struct destServer *destination, afs_int32 destTrans, + struct restoreCookie *cookie) +{ + afs_int32 code; + + code = + VolForward(acid, fromTrans, fromDate, destination, destTrans, cookie); + osi_auditU(acid, VS_ForwardEvent, code, AUD_LONG, fromTrans, AUD_HOST, + destination->destHost, AUD_LONG, destTrans, AUD_END); + return code; +} + +afs_int32 +VolForward(struct rx_call *acid, afs_int32 fromTrans, afs_int32 fromDate, + struct destServer *destination, afs_int32 destTrans, + struct restoreCookie *cookie) +{ + register struct volser_trans *tt; + register afs_int32 code; + register struct rx_connection *tcon; + struct rx_call *tcall; + register struct Volume *vp; + struct rx_securityClass *securityObject; + afs_int32 securityIndex; + char caller[MAXKTCNAMELEN]; + + if (!afsconf_SuperUser(tdir, acid, caller)) + return VOLSERBAD_ACCESS; /*not a super user */ + /* initialize things */ + tcon = (struct rx_connection *)0; + tt = (struct volser_trans *)0; + + /* find the local transaction */ + tt = FindTrans(fromTrans); + if (!tt) + return ENOENT; + if (tt->vflags & VTDeleted) { + Log("1 Volser: VolForward: volume %u has been deleted \n", tt->volid); + TRELE(tt); + return ENOENT; + } + vp = tt->volume; + strcpy(tt->lastProcName, "Forward"); + + /* get auth info for the this connection (uses afs from ticket file) */ + code = afsconf_ClientAuth(tdir, &securityObject, &securityIndex); + if (code) { + TRELE(tt); + return code; + } + + /* make an rpc connection to the other server */ + tcon = + rx_NewConnection(htonl(destination->destHost), + htons(destination->destPort), VOLSERVICE_ID, + securityObject, securityIndex); + if (!tcon) { + tt->rxCallPtr = (struct rx_call *)0; + TRELE(tt); + return ENOTCONN; + } + tcall = rx_NewCall(tcon); + tt->rxCallPtr = tcall; + /* start restore going. fromdate == 0 --> doing an incremental dump/restore */ + code = StartAFSVolRestore(tcall, destTrans, (fromDate ? 1 : 0), cookie); + if (code) { + goto fail; + } + + /* these next calls implictly call rx_Write when writing out data */ + code = DumpVolume(tcall, vp, fromDate, 0); /* last field = don't dump all dirs */ + if (code) + goto fail; + EndAFSVolRestore(tcall); /* probably doesn't do much */ + tt->rxCallPtr = (struct rx_call *)0; + code = rx_EndCall(tcall, 0); + rx_DestroyConnection(tcon); /* done with the connection */ + tcon = NULL; + if (code) + goto fail; + if (TRELE(tt)) + return VOLSERTRELE_ERROR; + + return 0; + + fail: + if (tcon) { + (void)rx_EndCall(tcall, 0); + rx_DestroyConnection(tcon); + } + if (tt) { + tt->rxCallPtr = (struct rx_call *)0; + TRELE(tt); + } + return code; +} + +/* Start a dump and send it to multiple places simultaneously. + * If this returns an error (eg, return ENOENT), it means that + * none of the releases worked. If this returns 0, that means + * that one or more of the releases worked, and the caller has + * to examine the results array to see which one(s). + * This will only do EITHER incremental or full, not both, so it's + * the caller's responsibility to be sure that all the destinations + * need just an incremental (and from the same time), if that's + * what we're doing. + */ +afs_int32 +SAFSVolForwardMultiple(struct rx_call *acid, afs_int32 fromTrans, afs_int32 + fromDate, manyDests *destinations, afs_int32 spare, + struct restoreCookie *cookie, manyResults *results) +{ + afs_int32 securityIndex; + struct rx_securityClass *securityObject; + char caller[MAXKTCNAMELEN]; + struct volser_trans *tt; + afs_int32 ec, code, *codes; + struct rx_connection **tcons; + struct rx_call **tcalls; + struct Volume *vp; + int i, is_incremental; + + if (results) + memset(results, 0, sizeof(manyResults)); + + if (!afsconf_SuperUser(tdir, acid, caller)) + return VOLSERBAD_ACCESS; /*not a super user */ + tt = FindTrans(fromTrans); + if (!tt) + return ENOENT; + if (tt->vflags & VTDeleted) { + Log("1 Volser: VolForward: volume %u has been deleted \n", tt->volid); + TRELE(tt); + return ENOENT; + } + vp = tt->volume; + strcpy(tt->lastProcName, "ForwardMulti"); + + /* (fromDate == 0) ==> incremental dump */ + is_incremental = (fromDate ? 1 : 0); + + i = results->manyResults_len = destinations->manyDests_len; + results->manyResults_val = codes = + (afs_int32 *) malloc(i * sizeof(afs_int32)); + tcons = + (struct rx_connection **)malloc(i * sizeof(struct rx_connection *)); + tcalls = (struct rx_call **)malloc(i * sizeof(struct rx_call *)); + + /* get auth info for this connection (uses afs from ticket file) */ + code = afsconf_ClientAuth(tdir, &securityObject, &securityIndex); + if (code) { + goto fail; /* in order to audit each failure */ + } + + /* make connections to all the other servers */ + for (i = 0; i < destinations->manyDests_len; i++) { + struct replica *dest = &(destinations->manyDests_val[i]); + tcons[i] = + rx_NewConnection(htonl(dest->server.destHost), + htons(dest->server.destPort), VOLSERVICE_ID, + securityObject, securityIndex); + if (!tcons[i]) { + codes[i] = ENOTCONN; + } else { + if (!(tcalls[i] = rx_NewCall(tcons[i]))) + codes[i] = ENOTCONN; + else { + codes[i] = + StartAFSVolRestore(tcalls[i], dest->trans, is_incremental, + cookie); + if (codes[i]) { + (void)rx_EndCall(tcalls[i], 0); + tcalls[i] = 0; + rx_DestroyConnection(tcons[i]); + tcons[i] = 0; + } + } + } + } + + /* these next calls implictly call rx_Write when writing out data */ + code = DumpVolMulti(tcalls, i, vp, fromDate, 0, codes); + + + fail: + for (i--; i >= 0; i--) { + struct replica *dest = &(destinations->manyDests_val[i]); + + if (!code && tcalls[i] && !codes[i]) { + EndAFSVolRestore(tcalls[i]); + } + if (tcalls[i]) { + ec = rx_EndCall(tcalls[i], 0); + if (!codes[i]) + codes[i] = ec; + } + if (tcons[i]) { + rx_DestroyConnection(tcons[i]); /* done with the connection */ + } + + osi_auditU(acid, VS_ForwardEvent, (code ? code : codes[i]), AUD_LONG, + fromTrans, AUD_HOST, dest->server.destHost, AUD_LONG, + dest->trans, AUD_END); + } + free(tcons); + free(tcalls); + + if (tt) { + tt->rxCallPtr = (struct rx_call *)0; + if (TRELE(tt) && !code) /* return the first code if it's set */ + return VOLSERTRELE_ERROR; + } + + return code; +} + +afs_int32 +SAFSVolDump(struct rx_call *acid, afs_int32 fromTrans, afs_int32 fromDate) +{ + afs_int32 code; + + code = VolDump(acid, fromTrans, fromDate); + osi_auditU(acid, VS_DumpEvent, code, AUD_LONG, fromTrans, AUD_END); + return code; +} + +afs_int32 +VolDump(struct rx_call *acid, afs_int32 fromTrans, afs_int32 fromDate) +{ + int code = 0; + register struct volser_trans *tt; + char caller[MAXKTCNAMELEN]; + + if (!afsconf_SuperUser(tdir, acid, caller)) + return VOLSERBAD_ACCESS; /*not a super user */ + tt = FindTrans(fromTrans); + if (!tt) + return ENOENT; + if (tt->vflags & VTDeleted) { + Log("1 Volser: VolDump: volume %u has been deleted \n", tt->volid); + TRELE(tt); + return ENOENT; + } + strcpy(tt->lastProcName, "Dump"); + tt->rxCallPtr = acid; + code = DumpVolume(acid, tt->volume, fromDate, 1); /* squirt out the volume's data, too */ + if (code) { + tt->rxCallPtr = (struct rx_call *)0; + TRELE(tt); + return code; + } + tt->rxCallPtr = (struct rx_call *)0; + + if (TRELE(tt)) + return VOLSERTRELE_ERROR; + + return 0; +} + +/* + * Ha! No more helper process! + */ +afs_int32 +SAFSVolRestore(struct rx_call *acid, afs_int32 atrans, afs_int32 aflags, + struct restoreCookie *cookie) +{ + afs_int32 code; + + code = VolRestore(acid, atrans, aflags, cookie); + osi_auditU(acid, VS_RestoreEvent, code, AUD_LONG, atrans, AUD_END); + return code; +} + +afs_int32 +VolRestore(struct rx_call *acid, afs_int32 atrans, afs_int32 aflags, + struct restoreCookie *cookie) +{ + register struct volser_trans *tt; + register afs_int32 code, tcode; + char caller[MAXKTCNAMELEN]; + + if (!afsconf_SuperUser(tdir, acid, caller)) + return VOLSERBAD_ACCESS; /*not a super user */ + tt = FindTrans(atrans); + if (!tt) + return ENOENT; + if (tt->vflags & VTDeleted) { + Log("1 Volser: VolRestore: volume %u has been deleted \n", tt->volid); + TRELE(tt); + return ENOENT; + } + strcpy(tt->lastProcName, "Restore"); + tt->rxCallPtr = acid; + + DFlushVolume(V_parentId(tt->volume)); /* Ensure dir buffers get dropped */ + + code = RestoreVolume(acid, tt->volume, (aflags & 1), cookie); /* last is incrementalp */ + FSYNC_askfs(tt->volid, NULL, FSYNC_RESTOREVOLUME, 0l); /*break call backs on the + * restored volume */ + tt->rxCallPtr = (struct rx_call *)0; + tcode = TRELE(tt); + + return (code ? code : tcode); +} + +/* end a transaction, returning the transaction's final error code in rcode */ +afs_int32 +SAFSVolEndTrans(struct rx_call *acid, afs_int32 destTrans, afs_int32 *rcode) +{ + afs_int32 code; + + code = VolEndTrans(acid, destTrans, rcode); + osi_auditU(acid, VS_EndTrnEvent, code, AUD_LONG, destTrans, AUD_END); + return code; +} + +afs_int32 +VolEndTrans(struct rx_call *acid, afs_int32 destTrans, afs_int32 *rcode) +{ + register struct volser_trans *tt; + char caller[MAXKTCNAMELEN]; + + if (!afsconf_SuperUser(tdir, acid, caller)) + return VOLSERBAD_ACCESS; /*not a super user */ + tt = FindTrans(destTrans); + if (!tt) { + return ENOENT; + } + *rcode = tt->returnCode; + DeleteTrans(tt); /* this does an implicit TRELE */ + + return 0; +} + +afs_int32 +SAFSVolSetForwarding(struct rx_call *acid, afs_int32 atid, afs_int32 anewsite) +{ + afs_int32 code; + + code = VolSetForwarding(acid, atid, anewsite); + osi_auditU(acid, VS_SetForwEvent, code, AUD_LONG, atid, AUD_HOST, + anewsite, AUD_END); + return code; +} + +afs_int32 +VolSetForwarding(struct rx_call *acid, afs_int32 atid, afs_int32 anewsite) +{ + register struct volser_trans *tt; + char caller[MAXKTCNAMELEN]; + + if (!afsconf_SuperUser(tdir, acid, caller)) + return VOLSERBAD_ACCESS; /*not a super user */ + tt = FindTrans(atid); + if (!tt) + return ENOENT; + if (tt->vflags & VTDeleted) { + Log("1 Volser: VolSetForwarding: volume %u has been deleted \n", + tt->volid); + TRELE(tt); + return ENOENT; + } + strcpy(tt->lastProcName, "SetForwarding"); + tt->rxCallPtr = acid; + FSYNC_askfs(tt->volid, NULL, FSYNC_MOVEVOLUME, anewsite); + tt->rxCallPtr = (struct rx_call *)0; + if (TRELE(tt)) + return VOLSERTRELE_ERROR; + + return 0; +} + +afs_int32 +SAFSVolGetStatus(struct rx_call *acid, afs_int32 atrans, + register struct volser_status *astatus) +{ + afs_int32 code; + + code = VolGetStatus(acid, atrans, astatus); + osi_auditU(acid, VS_GetStatEvent, code, AUD_LONG, atrans, AUD_END); + return code; +} + +afs_int32 +VolGetStatus(struct rx_call *acid, afs_int32 atrans, + register struct volser_status *astatus) +{ + register struct Volume *tv; + register struct VolumeDiskData *td; + struct volser_trans *tt; + + + tt = FindTrans(atrans); + if (!tt) + return ENOENT; + if (tt->vflags & VTDeleted) { + Log("1 Volser: VolGetStatus: volume %u has been deleted \n", + tt->volid); + TRELE(tt); + return ENOENT; + } + strcpy(tt->lastProcName, "GetStatus"); + tt->rxCallPtr = acid; + tv = tt->volume; + if (!tv) { + tt->rxCallPtr = (struct rx_call *)0; + TRELE(tt); + return ENOENT; + } + + td = &tv->header->diskstuff; + astatus->volID = td->id; + astatus->nextUnique = td->uniquifier; + astatus->type = td->type; + astatus->parentID = td->parentId; + astatus->cloneID = td->cloneId; + astatus->backupID = td->backupId; + astatus->restoredFromID = td->restoredFromId; + astatus->maxQuota = td->maxquota; + astatus->minQuota = td->minquota; + astatus->owner = td->owner; + astatus->creationDate = td->creationDate; + astatus->accessDate = td->accessDate; + astatus->updateDate = td->updateDate; + astatus->expirationDate = td->expirationDate; + astatus->backupDate = td->backupDate; + astatus->copyDate = td->copyDate; + tt->rxCallPtr = (struct rx_call *)0; + if (TRELE(tt)) + return VOLSERTRELE_ERROR; + + return 0; +} + +afs_int32 +SAFSVolSetInfo(struct rx_call *acid, afs_int32 atrans, + register struct volintInfo *astatus) +{ + afs_int32 code; + + code = VolSetInfo(acid, atrans, astatus); + osi_auditU(acid, VS_SetInfoEvent, code, AUD_LONG, atrans, AUD_END); + return code; +} + +afs_int32 +VolSetInfo(struct rx_call *acid, afs_int32 atrans, + register struct volintInfo *astatus) +{ + register struct Volume *tv; + register struct VolumeDiskData *td; + struct volser_trans *tt; + char caller[MAXKTCNAMELEN]; + afs_int32 error; + + if (!afsconf_SuperUser(tdir, acid, caller)) + return VOLSERBAD_ACCESS; /*not a super user */ + tt = FindTrans(atrans); + if (!tt) + return ENOENT; + if (tt->vflags & VTDeleted) { + Log("1 Volser: VolSetInfo: volume %u has been deleted \n", tt->volid); + TRELE(tt); + return ENOENT; + } + strcpy(tt->lastProcName, "SetStatus"); + tt->rxCallPtr = acid; + tv = tt->volume; + if (!tv) { + tt->rxCallPtr = (struct rx_call *)0; + TRELE(tt); + return ENOENT; + } + + td = &tv->header->diskstuff; + /* + * Add more fields as necessary + */ + if (astatus->maxquota != -1) + td->maxquota = astatus->maxquota; + if (astatus->dayUse != -1) + td->dayUse = astatus->dayUse; + VUpdateVolume(&error, tv); + tt->rxCallPtr = (struct rx_call *)0; + if (TRELE(tt)) + return VOLSERTRELE_ERROR; + return 0; +} + + +afs_int32 +SAFSVolGetName(struct rx_call *acid, afs_int32 atrans, char **aname) +{ + afs_int32 code; + + code = VolGetName(acid, atrans, aname); + osi_auditU(acid, VS_GetNameEvent, code, AUD_LONG, atrans, AUD_END); + return code; +} + +afs_int32 +VolGetName(struct rx_call *acid, afs_int32 atrans, char **aname) +{ + register struct Volume *tv; + register struct VolumeDiskData *td; + struct volser_trans *tt; + register int len; + + *aname = NULL; + tt = FindTrans(atrans); + if (!tt) + return ENOENT; + if (tt->vflags & VTDeleted) { + Log("1 Volser: VolGetName: volume %u has been deleted \n", tt->volid); + TRELE(tt); + return ENOENT; + } + strcpy(tt->lastProcName, "GetName"); + tt->rxCallPtr = acid; + tv = tt->volume; + if (!tv) { + tt->rxCallPtr = (struct rx_call *)0; + TRELE(tt); + return ENOENT; + } + + td = &tv->header->diskstuff; + len = strlen(td->name) + 1; /* don't forget the null */ + if (len >= SIZE) { + tt->rxCallPtr = (struct rx_call *)0; + TRELE(tt); + return E2BIG; + } + *aname = (char *)malloc(len); + strcpy(*aname, td->name); + tt->rxCallPtr = (struct rx_call *)0; + if (TRELE(tt)) + return VOLSERTRELE_ERROR; + + return 0; +} + +/*this is a handshake to indicate that the next call will be SAFSVolRestore + * - a noop now !*/ +afs_int32 +SAFSVolSignalRestore(struct rx_call *acid, char volname[], int volType, + afs_int32 parentId, afs_int32 cloneId) +{ + return 0; +} + + +/*return a list of all partitions on the server. The non mounted + *partitions are returned as -1 in the corresponding slot in partIds*/ +afs_int32 +SAFSVolListPartitions(struct rx_call *acid, struct pIDs *partIds) +{ + afs_int32 code; + + code = VolListPartitions(acid, partIds); + osi_auditU(acid, VS_ListParEvent, code, AUD_END); + return code; +} + +afs_int32 +VolListPartitions(struct rx_call *acid, struct pIDs *partIds) +{ + char namehead[9]; + char i; + + strcpy(namehead, "/vicep"); /*7 including null terminator */ + + /* Just return attached partitions. */ + namehead[7] = '\0'; + for (i = 0; i < 26; i++) { + namehead[6] = i + 'a'; + if (VGetPartition(namehead, 0)) + partIds->partIds[i] = VGetPartition(namehead, 0) ? i : -1; + } + + return 0; +} + +/*return a list of all partitions on the server. The non mounted + *partitions are returned as -1 in the corresponding slot in partIds*/ +afs_int32 +SAFSVolXListPartitions(struct rx_call *acid, struct partEntries *pEntries) +{ + afs_int32 code; + + code = XVolListPartitions(acid, pEntries); + osi_auditU(acid, VS_ListParEvent, code, AUD_END); + return code; +} + +afs_int32 +XVolListPartitions(struct rx_call *acid, struct partEntries *pEntries) +{ + struct stat rbuf, pbuf; + char namehead[9]; + struct partList partList; + struct DiskPartition *dp; + int i, j = 0, k; + + strcpy(namehead, "/vicep"); /*7 including null terminator */ + + /* Only report attached partitions */ + for (i = 0; i < VOLMAXPARTS; i++) { + if (i < 26) { + namehead[6] = i + 'a'; + namehead[7] = '\0'; + } else { + k = i - 26; + namehead[6] = 'a' + (k / 26); + namehead[7] = 'a' + (k % 26); + namehead[8] = '\0'; + } + dp = VGetPartition(namehead, 0); + if (dp) + partList.partId[j++] = i; + } + pEntries->partEntries_val = (afs_int32 *) malloc(j * sizeof(int)); + memcpy((char *)pEntries->partEntries_val, (char *)&partList, + j * sizeof(int)); + pEntries->partEntries_len = j; + return 0; + +} + +/*extract the volume id from string vname. Its of the form " V0*.vol "*/ +afs_int32 +ExtractVolId(char vname[]) +{ + int i; + char name[VOLSER_MAXVOLNAME + 1]; + + strcpy(name, vname); + i = 0; + while (name[i] == 'V' || name[i] == '0') + i++; + + name[11] = '\0'; /* smash the "." */ + return (atol(&name[i])); +} + +/*return the name of the next volume header in the directory associated with dirp and dp. +*the volume id is returned in volid, and volume header name is returned in volname*/ +int +GetNextVol(DIR * dirp, char *volname, afs_int32 * volid) +{ + struct dirent *dp; + + dp = readdir(dirp); /*read next entry in the directory */ + if (dp) { + if ((dp->d_name[0] == 'V') && !strcmp(&(dp->d_name[11]), VHDREXT)) { + *volid = ExtractVolId(dp->d_name); + strcpy(volname, dp->d_name); + return 0; /*return the name of the file representing a volume */ + } else { + strcpy(volname, ""); + return 0; /*volname doesnot represent a volume */ + } + } else { + strcpy(volname, "EOD"); + return 0; /*end of directory */ + } + +} + +/*return the header information about the */ +afs_int32 +SAFSVolListOneVolume(struct rx_call *acid, afs_int32 partid, afs_int32 + volumeId, volEntries *volumeInfo) +{ + afs_int32 code; + + code = VolListOneVolume(acid, partid, volumeId, volumeInfo); + osi_auditU(acid, VS_Lst1VolEvent, code, AUD_LONG, volumeId, AUD_END); + return code; +} + +afs_int32 +VolListOneVolume(struct rx_call *acid, afs_int32 partid, afs_int32 + volumeId, volEntries *volumeInfo) +{ + volintInfo *pntr; + register struct Volume *tv; + struct DiskPartition *partP; + struct volser_trans *ttc; + char pname[9], volname[20]; + afs_int32 error = 0; + DIR *dirp; + afs_int32 volid; + int found = 0; + unsigned int now; + + volumeInfo->volEntries_val = (volintInfo *) malloc(sizeof(volintInfo)); + pntr = volumeInfo->volEntries_val; + volumeInfo->volEntries_len = 1; + if (GetPartName(partid, pname)) + return VOLSERILLEGAL_PARTITION; + if (!(partP = VGetPartition(pname, 0))) + return VOLSERILLEGAL_PARTITION; + dirp = opendir(VPartitionPath(partP)); + if (dirp == NULL) + return VOLSERILLEGAL_PARTITION; + strcpy(volname, ""); + ttc = (struct volser_trans *)0; + tv = (Volume *) 0; /* volume not attached */ + + while (strcmp(volname, "EOD") && !found) { /*while there are more volumes in the partition */ + + if (!strcmp(volname, "")) { /* its not a volume, fetch next file */ + GetNextVol(dirp, volname, &volid); + continue; /*back to while loop */ + } + + if (volid == volumeId) { /*copy other things too */ + found = 1; +#ifndef AFS_PTHREAD_ENV + IOMGR_Poll(); /*make sure that the client doesnot time out */ +#endif + ttc = NewTrans(volid, partid); + if (!ttc) { + pntr->status = VBUSY; + pntr->volid = volid; + goto drop; + } + tv = VAttachVolumeByName(&error, pname, volname, V_READONLY); + if (error) { + pntr->status = 0; /*things are messed up */ + strcpy(pntr->name, volname); + pntr->volid = volid; + Log("1 Volser: ListVolumes: Could not attach volume %u (%s:%s), error=%d\n", volid, pname, volname, error); + goto drop; + } + if (tv->header->diskstuff.destroyMe == DESTROY_ME) { + /*this volume will be salvaged */ + pntr->status = 0; + strcpy(pntr->name, volname); + pntr->volid = volid; + Log("1 Volser: ListVolumes: Volume %u (%s) will be destroyed on next salvage\n", volid, volname); + goto drop; + } + + if (tv->header->diskstuff.needsSalvaged) { + /*this volume will be salvaged */ + pntr->status = 0; + strcpy(pntr->name, volname); + pntr->volid = volid; + Log("1 Volser: ListVolumes: Volume %u (%s) needs to be salvaged\n", volid, volname); + goto drop; + } + + /*read in the relevant info */ + pntr->status = VOK; /*its ok */ + pntr->volid = tv->header->diskstuff.id; + strcpy(pntr->name, tv->header->diskstuff.name); + pntr->type = tv->header->diskstuff.type; /*if ro volume */ + pntr->cloneID = tv->header->diskstuff.cloneId; /*if rw volume */ + pntr->backupID = tv->header->diskstuff.backupId; + pntr->parentID = tv->header->diskstuff.parentId; + pntr->copyDate = tv->header->diskstuff.copyDate; + pntr->inUse = tv->header->diskstuff.inUse; + pntr->size = tv->header->diskstuff.diskused; + pntr->needsSalvaged = tv->header->diskstuff.needsSalvaged; + pntr->destroyMe = tv->header->diskstuff.destroyMe; + pntr->maxquota = tv->header->diskstuff.maxquota; + pntr->filecount = tv->header->diskstuff.filecount; + now = FT_ApproxTime(); + if (now - tv->header->diskstuff.dayUseDate > OneDay) + pntr->dayUse = 0; + else + pntr->dayUse = tv->header->diskstuff.dayUse; + pntr->creationDate = tv->header->diskstuff.creationDate; + pntr->accessDate = tv->header->diskstuff.accessDate; + pntr->updateDate = tv->header->diskstuff.updateDate; + pntr->backupDate = tv->header->diskstuff.backupDate; + pntr->spare0 = tv->header->diskstuff.minquota; + pntr->spare1 = + (long)tv->header->diskstuff.weekUse[0] + + (long)tv->header->diskstuff.weekUse[1] + + (long)tv->header->diskstuff.weekUse[2] + + (long)tv->header->diskstuff.weekUse[3] + + (long)tv->header->diskstuff.weekUse[4] + + (long)tv->header->diskstuff.weekUse[5] + + (long)tv->header->diskstuff.weekUse[6]; + pntr->flags = pntr->spare2 = pntr->spare3 = (long)0; + VDetachVolume(&error, tv); /*free the volume */ + tv = (Volume *) 0; + if (error) { + pntr->status = 0; /*things are messed up */ + strcpy(pntr->name, volname); + Log("1 Volser: ListVolumes: Could not detach volume %s\n", + volname); + goto drop; + } + } + GetNextVol(dirp, volname, &volid); + } + drop: + if (tv) { + VDetachVolume(&error, tv); + tv = (Volume *) 0; + } + if (ttc) { + DeleteTrans(ttc); + ttc = (struct volser_trans *)0; + } + + closedir(dirp); + if (found) + return 0; + else + return ENODEV; +} + +/*------------------------------------------------------------------------ + * EXPORTED SAFSVolXListOneVolume + * + * Description: + * Returns extended info on volume a_volID on partition a_partID. + * + * Arguments: + * a_rxCidP : Pointer to the Rx call we're performing. + * a_partID : Partition for which we want the extended list. + * a_volID : Volume ID we wish to know about. + * a_volumeXInfoP : Ptr to the extended info blob. + * + * Returns: + * 0 Successful operation + * VOLSERILLEGAL_PARTITION if we got a bogus partition ID + * + * Environment: + * Nothing interesting. + * + * Side Effects: + * As advertised. + *------------------------------------------------------------------------*/ + +afs_int32 +SAFSVolXListOneVolume(struct rx_call *a_rxCidP, afs_int32 a_partID, + afs_int32 a_volID, volXEntries *a_volumeXInfoP) +{ + afs_int32 code; + + code = VolXListOneVolume(a_rxCidP, a_partID, a_volID, a_volumeXInfoP); + osi_auditU(a_rxCidP, VS_XLst1VlEvent, code, AUD_LONG, a_volID, AUD_END); + return code; +} + +afs_int32 +VolXListOneVolume(struct rx_call *a_rxCidP, afs_int32 a_partID, + afs_int32 a_volID, volXEntries *a_volumeXInfoP) +{ /*SAFSVolXListOneVolume */ + + volintXInfo *xInfoP; /*Ptr to the extended vol info */ + register struct Volume *tv; /*Volume ptr */ + struct volser_trans *ttc; /*Volume transaction ptr */ + struct DiskPartition *partP; /*Ptr to partition */ + char pname[9], volname[20]; /*Partition, volume names */ + afs_int32 error; /*Error code */ + afs_int32 code; /*Return code */ + DIR *dirp; /*Partition directory ptr */ + afs_int32 currVolID; /*Current volume ID */ + int found = 0; /*Did we find the volume we need? */ + struct VolumeDiskData *volDiskDataP; /*Ptr to on-disk volume data */ + int numStatBytes; /*Num stat bytes to copy per volume */ + unsigned int now; + + /* + * Set up our pointers for action, marking our structure to hold exactly + * one entry. Also, assume we'll fail in our quest. + */ + a_volumeXInfoP->volXEntries_val = + (volintXInfo *) malloc(sizeof(volintXInfo)); + xInfoP = a_volumeXInfoP->volXEntries_val; + a_volumeXInfoP->volXEntries_len = 1; + code = ENODEV; + + /* + * If the partition name we've been given is bad, bogue out. + */ + if (GetPartName(a_partID, pname)) + return (VOLSERILLEGAL_PARTITION); + + /* + * Open the directory representing the given AFS parttion. If we can't + * do that, we lose. + */ + if (!(partP = VGetPartition(pname, 0))) + return VOLSERILLEGAL_PARTITION; + dirp = opendir(VPartitionPath(partP)); + if (dirp == NULL) + return (VOLSERILLEGAL_PARTITION); + + /* + * Sweep through the partition directory, looking for the desired entry. + * First, of course, figure out how many stat bytes to copy out of each + * volume. + */ + numStatBytes = + 4 * ((2 * VOLINT_STATS_NUM_RWINFO_FIELDS) + + (4 * VOLINT_STATS_NUM_TIME_FIELDS)); + strcpy(volname, ""); + ttc = (struct volser_trans *)0; /*No transaction yet */ + tv = (Volume *) 0; /*Volume not yet attached */ + + while (strcmp(volname, "EOD") && !found) { + /* + * If this is not a volume, move on to the next entry in the + * partition's directory. + */ + if (!strcmp(volname, "")) { + GetNextVol(dirp, volname, &currVolID); + continue; + } + + if (currVolID == a_volID) { + /* + * We found the volume entry we're interested. Pull out the + * extended information, remembering to poll (so that the client + * doesn't time out) and to set up a transaction on the volume. + */ + found = 1; +#ifndef AFS_PTHREAD_ENV + IOMGR_Poll(); +#endif + ttc = NewTrans(currVolID, a_partID); + if (!ttc) { + /* + * Couldn't get a transaction on this volume; let our caller + * know it's busy. + */ + xInfoP->status = VBUSY; + xInfoP->volid = currVolID; + goto drop; + } + + /* + * Attach the volume, give up on the volume if we can't. + */ + tv = VAttachVolumeByName(&error, pname, volname, V_READONLY); + if (error) { + xInfoP->status = 0; /*things are messed up */ + strcpy(xInfoP->name, volname); + xInfoP->volid = currVolID; + Log("1 Volser: XListOneVolume: Could not attach volume %u\n", + currVolID); + goto drop; + } + + /* + * Also bag out on this volume if it's been marked as needing a + * salvage or to-be-destroyed. + */ + volDiskDataP = &(tv->header->diskstuff); + if (volDiskDataP->destroyMe == DESTROY_ME) { + xInfoP->status = 0; + strcpy(xInfoP->name, volname); + xInfoP->volid = currVolID; + Log("1 Volser: XListOneVolume: Volume %u will be destroyed on next salvage\n", currVolID); + goto drop; + } + + if (volDiskDataP->needsSalvaged) { + xInfoP->status = 0; + strcpy(xInfoP->name, volname); + xInfoP->volid = currVolID; + Log("1 Volser: XListOneVolume: Volume %u needs to be salvaged\n", currVolID); + goto drop; + } + + /* + * Pull out the desired info and stuff it into the area we'll be + * returning to our caller. + */ + strcpy(xInfoP->name, volDiskDataP->name); + xInfoP->volid = volDiskDataP->id; + xInfoP->type = volDiskDataP->type; + xInfoP->backupID = volDiskDataP->backupId; + xInfoP->parentID = volDiskDataP->parentId; + xInfoP->cloneID = volDiskDataP->cloneId; + xInfoP->status = VOK; + xInfoP->copyDate = volDiskDataP->copyDate; + xInfoP->inUse = volDiskDataP->inUse; + xInfoP->creationDate = volDiskDataP->creationDate; + xInfoP->accessDate = volDiskDataP->accessDate; + xInfoP->updateDate = volDiskDataP->updateDate; + xInfoP->backupDate = volDiskDataP->backupDate; + now = FT_ApproxTime(); + if (now - volDiskDataP->dayUseDate > OneDay) + xInfoP->dayUse = 0; + else + xInfoP->dayUse = volDiskDataP->dayUse; + xInfoP->filecount = volDiskDataP->filecount; + xInfoP->maxquota = volDiskDataP->maxquota; + xInfoP->size = volDiskDataP->diskused; + + /* + * Copy out the stat fields in a single operation. + */ + memcpy((char *)&(xInfoP->stat_reads[0]), + (char *)&(volDiskDataP->stat_reads[0]), numStatBytes); + + /* + * We're done copying. Detach the volume and iterate (at this + * point, since we found our volume, we'll then drop out of the + * loop). + */ + VDetachVolume(&error, tv); + tv = (Volume *) 0; + if (error) { + xInfoP->status = 0; + strcpy(xInfoP->name, volname); + Log("1 Volser: XListOneVolumes Couldn't detach volume %s\n", + volname); + goto drop; + } + + /* + * At this point, we're golden. + */ + code = 0; + } /*Found desired volume */ + GetNextVol(dirp, volname, &currVolID); + } + + /* + * Drop the transaction we have for this volume. + */ + drop: + if (tv) { + VDetachVolume(&error, tv); + tv = (Volume *) 0; + } + if (ttc) { + DeleteTrans(ttc); + ttc = (struct volser_trans *)0; + } + + /* + * Clean up before going to dinner: close the partition directory, + * return the proper value. + */ + closedir(dirp); + return (code); + +} /*SAFSVolXListOneVolume */ + +/*returns all the volumes on partition partid. If flags = 1 then all the +* relevant info about the volumes is also returned */ +afs_int32 +SAFSVolListVolumes(struct rx_call *acid, afs_int32 partid, afs_int32 flags, + volEntries *volumeInfo) +{ + afs_int32 code; + + code = VolListVolumes(acid, partid, flags, volumeInfo); + osi_auditU(acid, VS_ListVolEvent, code, AUD_END); + return code; +} + +afs_int32 +VolListVolumes(struct rx_call *acid, afs_int32 partid, afs_int32 flags, + volEntries *volumeInfo) +{ + volintInfo *pntr; + register struct Volume *tv; + struct DiskPartition *partP; + struct volser_trans *ttc; + afs_int32 allocSize = 1000; /*to be changed to a larger figure */ + char pname[9], volname[20]; + afs_int32 error = 0; + DIR *dirp; + afs_int32 volid; + unsigned int now; + + volumeInfo->volEntries_val = + (volintInfo *) malloc(allocSize * sizeof(volintInfo)); + pntr = volumeInfo->volEntries_val; + volumeInfo->volEntries_len = 0; + if (GetPartName(partid, pname)) + return VOLSERILLEGAL_PARTITION; + if (!(partP = VGetPartition(pname, 0))) + return VOLSERILLEGAL_PARTITION; + dirp = opendir(VPartitionPath(partP)); + if (dirp == NULL) + return VOLSERILLEGAL_PARTITION; + strcpy(volname, ""); + while (strcmp(volname, "EOD")) { /*while there are more partitions in the partition */ + ttc = (struct volser_trans *)0; /* new one for each pass */ + tv = (Volume *) 0; /* volume not attached */ + + if (!strcmp(volname, "")) { /* its not a volume, fetch next file */ + GetNextVol(dirp, volname, &volid); + continue; /*back to while loop */ + } + + if (flags) { /*copy other things too */ +#ifndef AFS_PTHREAD_ENV + IOMGR_Poll(); /*make sure that the client doesnot time out */ +#endif + ttc = NewTrans(volid, partid); + if (!ttc) { + pntr->status = VBUSY; + pntr->volid = volid; + goto drop; + } + tv = VAttachVolumeByName(&error, pname, volname, V_READONLY); + if (error) { + pntr->status = 0; /*things are messed up */ + strcpy(pntr->name, volname); + pntr->volid = volid; + Log("1 Volser: ListVolumes: Could not attach volume %u (%s) error=%d\n", volid, volname, error); + goto drop; + } + if (tv->header->diskstuff.needsSalvaged) { + /*this volume will be salvaged */ + pntr->status = 0; + strcpy(pntr->name, volname); + pntr->volid = volid; + Log("1 Volser: ListVolumes: Volume %u (%s) needs to be salvaged\n", volid, volname); + goto drop; + } + + if (tv->header->diskstuff.destroyMe == DESTROY_ME) { + /*this volume will be salvaged */ + goto drop2; + } + /*read in the relevant info */ + pntr->status = VOK; /*its ok */ + pntr->volid = tv->header->diskstuff.id; + strcpy(pntr->name, tv->header->diskstuff.name); + pntr->type = tv->header->diskstuff.type; /*if ro volume */ + pntr->cloneID = tv->header->diskstuff.cloneId; /*if rw volume */ + pntr->backupID = tv->header->diskstuff.backupId; + pntr->parentID = tv->header->diskstuff.parentId; + pntr->copyDate = tv->header->diskstuff.copyDate; + pntr->inUse = tv->header->diskstuff.inUse; + pntr->size = tv->header->diskstuff.diskused; + pntr->needsSalvaged = tv->header->diskstuff.needsSalvaged; + pntr->maxquota = tv->header->diskstuff.maxquota; + pntr->filecount = tv->header->diskstuff.filecount; + now = FT_ApproxTime(); + if (now - tv->header->diskstuff.dayUseDate > OneDay) + pntr->dayUse = 0; + else + pntr->dayUse = tv->header->diskstuff.dayUse; + pntr->creationDate = tv->header->diskstuff.creationDate; + pntr->accessDate = tv->header->diskstuff.accessDate; + pntr->updateDate = tv->header->diskstuff.updateDate; + pntr->backupDate = tv->header->diskstuff.backupDate; + pntr->spare0 = tv->header->diskstuff.minquota; + pntr->spare1 = + (long)tv->header->diskstuff.weekUse[0] + + (long)tv->header->diskstuff.weekUse[1] + + (long)tv->header->diskstuff.weekUse[2] + + (long)tv->header->diskstuff.weekUse[3] + + (long)tv->header->diskstuff.weekUse[4] + + (long)tv->header->diskstuff.weekUse[5] + + (long)tv->header->diskstuff.weekUse[6]; + pntr->flags = pntr->spare2 = pntr->spare3 = (long)0; + VDetachVolume(&error, tv); /*free the volume */ + tv = (Volume *) 0; + if (error) { + pntr->status = 0; /*things are messed up */ + strcpy(pntr->name, volname); + Log("1 Volser: ListVolumes: Could not detach volume %s\n", + volname); + goto drop; + } + } else { + pntr->volid = volid; + /*just volids are needed */ + } + + drop: + if (ttc) { + DeleteTrans(ttc); + ttc = (struct volser_trans *)0; + } + pntr++; + volumeInfo->volEntries_len += 1; + if ((allocSize - volumeInfo->volEntries_len) < 5) { + /*running out of space, allocate more space */ + allocSize = (allocSize * 3) / 2; + pntr = + (volintInfo *) realloc((char *)volumeInfo->volEntries_val, + allocSize * sizeof(volintInfo)); + if (pntr == NULL) { + if (tv) { + VDetachVolume(&error, tv); + tv = (Volume *) 0; + } + if (ttc) { + DeleteTrans(ttc); + ttc = (struct volser_trans *)0; + } + closedir(dirp); + return VOLSERNO_MEMORY; + } + volumeInfo->volEntries_val = pntr; /* point to new block */ + /* set pntr to the right position */ + pntr = volumeInfo->volEntries_val + volumeInfo->volEntries_len; + + } + + drop2: + if (tv) { + VDetachVolume(&error, tv); + tv = (Volume *) 0; + } + if (ttc) { + DeleteTrans(ttc); + ttc = (struct volser_trans *)0; + } + GetNextVol(dirp, volname, &volid); + + } + closedir(dirp); + if (ttc) + DeleteTrans(ttc); + + return 0; +} + +/*------------------------------------------------------------------------ + * EXPORTED SAFSVolXListVolumes + * + * Description: + * Returns all the volumes on partition a_partID. If a_flags + * is set to 1, then all the relevant extended volume information + * is also returned. + * + * Arguments: + * a_rxCidP : Pointer to the Rx call we're performing. + * a_partID : Partition for which we want the extended list. + * a_flags : Various flags. + * a_volumeXInfoP : Ptr to the extended info blob. + * + * Returns: + * 0 Successful operation + * VOLSERILLEGAL_PARTITION if we got a bogus partition ID + * VOLSERNO_MEMORY if we ran out of memory allocating + * our return blob + * + * Environment: + * Nothing interesting. + * + * Side Effects: + * As advertised. + *------------------------------------------------------------------------*/ + +afs_int32 +SAFSVolXListVolumes(struct rx_call *a_rxCidP, afs_int32 a_partID, + afs_int32 a_flags, volXEntries *a_volumeXInfoP) +{ + afs_int32 code; + + code = VolXListVolumes(a_rxCidP, a_partID, a_flags, a_volumeXInfoP); + osi_auditU(a_rxCidP, VS_XLstVolEvent, code, AUD_END); + return code; +} + +afs_int32 +VolXListVolumes(struct rx_call *a_rxCidP, afs_int32 a_partID, + afs_int32 a_flags, volXEntries *a_volumeXInfoP) +{ /*SAFSVolXListVolumes */ + + volintXInfo *xInfoP; /*Ptr to the extended vol info */ + register struct Volume *tv; /*Volume ptr */ + struct DiskPartition *partP; /*Ptr to partition */ + struct volser_trans *ttc; /*Volume transaction ptr */ + afs_int32 allocSize = 1000; /*To be changed to a larger figure */ + char pname[9], volname[20]; /*Partition, volume names */ + afs_int32 error = 0; /*Return code */ + DIR *dirp; /*Partition directory ptr */ + afs_int32 volid; /*Current volume ID */ + struct VolumeDiskData *volDiskDataP; /*Ptr to on-disk volume data */ + int numStatBytes; /*Num stat bytes to copy per volume */ + unsigned int now; + + /* + * Allocate a large array of extended volume info structures, then + * set it up for action. + */ + a_volumeXInfoP->volXEntries_val = + (volintXInfo *) malloc(allocSize * sizeof(volintXInfo)); + xInfoP = a_volumeXInfoP->volXEntries_val; + a_volumeXInfoP->volXEntries_len = 0; + + /* + * If the partition name we've been given is bad, bogue out. + */ + if (GetPartName(a_partID, pname)) + return (VOLSERILLEGAL_PARTITION); + + /* + * Open the directory representing the given AFS parttion. If we can't + * do that, we lose. + */ + if (!(partP = VGetPartition(pname, 0))) + return VOLSERILLEGAL_PARTITION; + dirp = opendir(VPartitionPath(partP)); + if (dirp == NULL) + return (VOLSERILLEGAL_PARTITION); + + /* + * Sweep through the partition directory, acting on each entry. First, + * of course, figure out how many stat bytes to copy out of each volume. + */ + numStatBytes = + 4 * ((2 * VOLINT_STATS_NUM_RWINFO_FIELDS) + + (4 * VOLINT_STATS_NUM_TIME_FIELDS)); + strcpy(volname, ""); + while (strcmp(volname, "EOD")) { + ttc = (struct volser_trans *)0; /*New one for each pass */ + tv = (Volume *) 0; /*Volume not yet attached */ + + /* + * If this is not a volume, move on to the next entry in the + * partition's directory. + */ + if (!strcmp(volname, "")) { + GetNextVol(dirp, volname, &volid); + continue; + } + + if (a_flags) { + /* + * Full info about the volume desired. Poll to make sure the + * client doesn't time out, then start up a new transaction. + */ +#ifndef AFS_PTHREAD_ENV + IOMGR_Poll(); +#endif + ttc = NewTrans(volid, a_partID); + if (!ttc) { + /* + * Couldn't get a transaction on this volume; let our caller + * know it's busy. + */ + xInfoP->status = VBUSY; + xInfoP->volid = volid; + goto drop; + } + + /* + * Attach the volume, give up on this volume if we can't. + */ + tv = VAttachVolumeByName(&error, pname, volname, V_READONLY); + if (error) { + xInfoP->status = 0; /*things are messed up */ + strcpy(xInfoP->name, volname); + xInfoP->volid = volid; + Log("1 Volser: XListVolumes: Could not attach volume %u\n", + volid); + goto drop; + } + + /* + * Also bag out on this volume if it's been marked as needing a + * salvage or to-be-destroyed. + */ + volDiskDataP = &(tv->header->diskstuff); + if (volDiskDataP->needsSalvaged) { + xInfoP->status = 0; + strcpy(xInfoP->name, volname); + xInfoP->volid = volid; + Log("1 Volser: XListVolumes: Volume %u needs to be salvaged\n", volid); + goto drop; + } + + if (volDiskDataP->destroyMe == DESTROY_ME) + goto drop2; + + /* + * Pull out the desired info and stuff it into the area we'll be + * returning to our caller. + */ + strcpy(xInfoP->name, volDiskDataP->name); + xInfoP->volid = volDiskDataP->id; + xInfoP->type = volDiskDataP->type; + xInfoP->backupID = volDiskDataP->backupId; + xInfoP->parentID = volDiskDataP->parentId; + xInfoP->cloneID = volDiskDataP->cloneId; + xInfoP->status = VOK; + xInfoP->copyDate = volDiskDataP->copyDate; + xInfoP->inUse = volDiskDataP->inUse; + xInfoP->creationDate = volDiskDataP->creationDate; + xInfoP->accessDate = volDiskDataP->accessDate; + xInfoP->updateDate = volDiskDataP->updateDate; + xInfoP->backupDate = volDiskDataP->backupDate; + now = FT_ApproxTime(); + if (now - volDiskDataP->dayUseDate > OneDay) + xInfoP->dayUse = 0; + else + xInfoP->dayUse = volDiskDataP->dayUse; + xInfoP->filecount = volDiskDataP->filecount; + xInfoP->maxquota = volDiskDataP->maxquota; + xInfoP->size = volDiskDataP->diskused; + + /* + * Copy out the stat fields in a single operation. + */ + memcpy((char *)&(xInfoP->stat_reads[0]), + (char *)&(volDiskDataP->stat_reads[0]), numStatBytes); + + /* + * We're done copying. Detach the volume and iterate. + */ + VDetachVolume(&error, tv); + tv = (Volume *) 0; + if (error) { + xInfoP->status = 0; + strcpy(xInfoP->name, volname); + Log("1 Volser: XListVolumes: Could not detach volume %s\n", + volname); + goto drop; + } + } /*Full contents desired */ + else + /* + * Just volume IDs are needed. + */ + xInfoP->volid = volid; + + drop: + /* + * Drop the transaction we have for this volume. + */ + if (ttc) { + DeleteTrans(ttc); + ttc = (struct volser_trans *)0; + } + + /* + * Bump the pointer in the data area we're building, along with + * the count of the number of entries it contains. + */ + xInfoP++; + (a_volumeXInfoP->volXEntries_len)++; + if ((allocSize - a_volumeXInfoP->volXEntries_len) < 5) { + /* + * We're running out of space in the area we've built. Grow it. + */ + allocSize = (allocSize * 3) / 2; + xInfoP = (volintXInfo *) + realloc((char *)a_volumeXInfoP->volXEntries_val, + (allocSize * sizeof(volintXInfo))); + if (xInfoP == NULL) { + /* + * Bummer, no memory. Bag it, tell our caller what went wrong. + */ + if (tv) { + VDetachVolume(&error, tv); + tv = (Volume *) 0; + } + if (ttc) { + DeleteTrans(ttc); + ttc = (struct volser_trans *)0; + } + closedir(dirp); + return (VOLSERNO_MEMORY); + } + + /* + * Memory reallocation worked. Correct our pointers so they + * now point to the new block and the current open position within + * the new block. + */ + a_volumeXInfoP->volXEntries_val = xInfoP; + xInfoP = + a_volumeXInfoP->volXEntries_val + + a_volumeXInfoP->volXEntries_len; + } + /*Need more space */ + drop2: + /* + * Detach our current volume and the transaction on it, then move on + * to the next volume in the partition directory. + */ + if (tv) { + VDetachVolume(&error, tv); + tv = (Volume *) 0; + } + if (ttc) { + DeleteTrans(ttc); + ttc = (struct volser_trans *)0; + } + GetNextVol(dirp, volname, &volid); + } /*Sweep through the partition directory */ + + /* + * We've examined all entries in the partition directory. Close it, + * delete our transaction (if any), and go home happy. + */ + closedir(dirp); + if (ttc) + DeleteTrans(ttc); + return (0); + +} /*SAFSVolXListVolumes */ + +/*this call is used to monitor the status of volser for debugging purposes. + *information about all the active transactions is returned in transInfo*/ +afs_int32 +SAFSVolMonitor(struct rx_call *acid, transDebugEntries *transInfo) +{ + afs_int32 code; + + code = VolMonitor(acid, transInfo); + osi_auditU(acid, VS_MonitorEvent, code, AUD_END); + return code; +} + +afs_int32 +VolMonitor(struct rx_call *acid, transDebugEntries *transInfo) +{ + transDebugInfo *pntr; + afs_int32 allocSize = 50; + struct volser_trans *tt, *allTrans; + + transInfo->transDebugEntries_val = + (transDebugInfo *) malloc(allocSize * sizeof(transDebugInfo)); + pntr = transInfo->transDebugEntries_val; + transInfo->transDebugEntries_len = 0; + allTrans = TransList(); + if (allTrans == (struct volser_trans *)0) + return 0; /*no active transactions */ + for (tt = allTrans; tt; tt = tt->next) { /*copy relevant info into pntr */ + pntr->tid = tt->tid; + pntr->time = tt->time; + pntr->creationTime = tt->creationTime; + pntr->returnCode = tt->returnCode; + pntr->volid = tt->volid; + pntr->partition = tt->partition; + pntr->iflags = tt->iflags; + pntr->vflags = tt->vflags; + pntr->tflags = tt->tflags; + strcpy(pntr->lastProcName, tt->lastProcName); + pntr->callValid = 0; + if (tt->rxCallPtr) { /*record call related info */ + pntr->callValid = 1; + pntr->readNext = tt->rxCallPtr->rnext; + pntr->transmitNext = tt->rxCallPtr->tnext; + pntr->lastSendTime = tt->rxCallPtr->lastSendTime; + pntr->lastReceiveTime = tt->rxCallPtr->lastReceiveTime; + } + pntr++; + transInfo->transDebugEntries_len += 1; + if ((allocSize - transInfo->transDebugEntries_len) < 5) { /*alloc some more space */ + allocSize = (allocSize * 3) / 2; + pntr = + (transDebugInfo *) realloc((char *)transInfo-> + transDebugEntries_val, + allocSize * + sizeof(transDebugInfo)); + transInfo->transDebugEntries_val = pntr; + pntr = + transInfo->transDebugEntries_val + + transInfo->transDebugEntries_len; + /*set pntr to right position */ + } + + } + + return 0; +} + +afs_int32 +SAFSVolSetIdsTypes(struct rx_call *acid, afs_int32 atid, char name[], afs_int32 type, afs_int32 pId, afs_int32 cloneId, afs_int32 backupId) +{ + afs_int32 code; + + code = VolSetIdsTypes(acid, atid, name, type, pId, cloneId, backupId); + osi_auditU(acid, VS_SetIdTyEvent, code, AUD_LONG, atid, AUD_STR, name, + AUD_STR, type, AUD_LONG, pId, AUD_LONG, cloneId, AUD_LONG, + backupId, AUD_END); + return code; +} + +afs_int32 +VolSetIdsTypes(struct rx_call *acid, afs_int32 atid, char name[], afs_int32 type, afs_int32 pId, afs_int32 cloneId, afs_int32 backupId) +{ + struct Volume *tv; + afs_int32 error = 0; + register struct volser_trans *tt; + char caller[MAXKTCNAMELEN]; + + if (strlen(name) > 31) + return VOLSERBADNAME; + if (!afsconf_SuperUser(tdir, acid, caller)) + return VOLSERBAD_ACCESS; /*not a super user */ + /* find the trans */ + tt = FindTrans(atid); + if (!tt) + return ENOENT; + if (tt->vflags & VTDeleted) { + Log("1 Volser: VolSetIds: volume %u has been deleted \n", tt->volid); + TRELE(tt); + return ENOENT; + } + strcpy(tt->lastProcName, "SetIdsTypes"); + tt->rxCallPtr = acid; + tv = tt->volume; + + V_type(tv) = type; + V_backupId(tv) = backupId; + V_cloneId(tv) = cloneId; + V_parentId(tv) = pId; + strcpy((&V_disk(tv))->name, name); + VUpdateVolume(&error, tv); + if (error) { + Log("1 Volser: SetIdsTypes: VUpdate failed code %d\n", error); + LogError(error); + goto fail; + } + tt->rxCallPtr = (struct rx_call *)0; + if (TRELE(tt) && !error) + return VOLSERTRELE_ERROR; + + return error; + fail: + tt->rxCallPtr = (struct rx_call *)0; + if (TRELE(tt) && !error) + return VOLSERTRELE_ERROR; + return error; +} + +afs_int32 +SAFSVolSetDate(struct rx_call *acid, afs_int32 atid, afs_int32 cdate) +{ + afs_int32 code; + + code = VolSetDate(acid, atid, cdate); + osi_auditU(acid, VS_SetDateEvent, code, AUD_LONG, atid, AUD_LONG, cdate, + AUD_END); + return code; +} + +afs_int32 +VolSetDate(struct rx_call *acid, afs_int32 atid, afs_int32 cdate) +{ + struct Volume *tv; + afs_int32 error = 0; + register struct volser_trans *tt; + char caller[MAXKTCNAMELEN]; + + if (!afsconf_SuperUser(tdir, acid, caller)) + return VOLSERBAD_ACCESS; /*not a super user */ + /* find the trans */ + tt = FindTrans(atid); + if (!tt) + return ENOENT; + if (tt->vflags & VTDeleted) { + Log("1 Volser: VolSetDate: volume %u has been deleted \n", tt->volid); + TRELE(tt); + return ENOENT; + } + strcpy(tt->lastProcName, "SetDate"); + tt->rxCallPtr = acid; + tv = tt->volume; + + V_creationDate(tv) = cdate; + VUpdateVolume(&error, tv); + if (error) { + Log("1 Volser: SetDate: VUpdate failed code %d\n", error); + LogError(error); + goto fail; + } + tt->rxCallPtr = (struct rx_call *)0; + if (TRELE(tt) && !error) + return VOLSERTRELE_ERROR; + + return error; + fail: + tt->rxCallPtr = (struct rx_call *)0; + if (TRELE(tt) && !error) + return VOLSERTRELE_ERROR; + return error; +} + +#ifdef AFS_NAMEI_ENV +/* + * Inode number format (from namei_ops.c): + * low 26 bits - vnode number - all 1's if volume special file. + * next 3 bits - tag + * next 3 bits spare (0's) + * high 32 bits - uniquifier (regular) or type if spare + */ +#define NAMEI_VNODEMASK 0x003ffffff +#define NAMEI_TAGMASK 0x7 +#define NAMEI_TAGSHIFT 26 +#define NAMEI_UNIQMASK 0xffffffff +#define NAMEI_UNIQSHIFT 32 +#define NAMEI_INODESPECIAL ((Inode)NAMEI_VNODEMASK) +#define NAMEI_VNODESPECIAL NAMEI_VNODEMASK +#endif /* AFS_NAMEI_ENV */ + +afs_int32 +SAFSVolConvertROtoRWvolume(struct rx_call *acid, afs_int32 partId, + afs_int32 volumeId) +{ +#if defined(AFS_NAMEI_ENV) && !defined(AFS_NT40_ENV) + DIR *dirp; + char pname[16]; + char volname[20]; + afs_int32 error = 0; + afs_int32 volid; + int found = 0; + char caller[MAXKTCNAMELEN]; + char headername[16]; + char opath[256]; + char npath[256]; + struct VolumeDiskHeader h; + int fd; + IHandle_t *ih; + Inode ino; + struct DiskPartition *dp; + + if (!afsconf_SuperUser(tdir, acid, caller)) + return VOLSERBAD_ACCESS; /*not a super user */ + if (GetPartName(partId, pname)) + return VOLSERILLEGAL_PARTITION; + dirp = opendir(pname); + if (dirp == NULL) + return VOLSERILLEGAL_PARTITION; + strcpy(volname, ""); + + while (strcmp(volname, "EOD") && !found) { /*while there are more volumes in the partition */ + GetNextVol(dirp, volname, &volid); + if (strcmp(volname, "")) { /* its a volume */ + if (volid == volumeId) + found = 1; + } + } + if (!found) + return ENOENT; + (void)afs_snprintf(headername, sizeof headername, VFORMAT, volumeId); + (void)afs_snprintf(opath, sizeof opath, "%s/%s", pname, headername); + fd = open(opath, O_RDONLY); + if (fd < 0) { + Log("1 SAFS_VolConvertROtoRWvolume: Couldn't open header for RO-volume %lu.\n", volumeId); + return ENOENT; + } + if (read(fd, &h, sizeof(h)) != sizeof(h)) { + Log("1 SAFS_VolConvertROtoRWvolume: Couldn't read header for RO-volume %lu.\n", volumeId); + close(fd); + return EIO; + } + close(fd); + FSYNC_askfs(volumeId, pname, FSYNC_RESTOREVOLUME, 0); + + for (dp = DiskPartitionList; dp && strcmp(dp->name, pname); + dp = dp->next); + if (!dp) { + Log("1 SAFS_VolConvertROtoRWvolume: Couldn't find DiskPartition for %s\n", pname); + return EIO; + } + ino = namei_MakeSpecIno(h.parent, VI_LINKTABLE); + IH_INIT(ih, dp->device, h.parent, ino); + + error = namei_ConvertROtoRWvolume(ih, volumeId); + if (error) + return error; + h.id = h.parent; + h.volumeInfo_hi = h.id; + h.smallVnodeIndex_hi = h.id; + h.largeVnodeIndex_hi = h.id; + h.linkTable_hi = h.id; + (void)afs_snprintf(headername, sizeof headername, VFORMAT, h.id); + (void)afs_snprintf(npath, sizeof npath, "%s/%s", pname, headername); + fd = open(npath, O_CREAT | O_EXCL | O_RDWR, 0644); + if (fd < 0) { + Log("1 SAFS_VolConvertROtoRWvolume: Couldn't create header for RW-volume %lu.\n", h.id); + return EIO; + } + if (write(fd, &h, sizeof(h)) != sizeof(h)) { + Log("1 SAFS_VolConvertROtoRWvolume: Couldn't write header for RW-volume %lu.\n", h.id); + close(fd); + return EIO; + } + close(fd); + if (unlink(opath) < 0) { + Log("1 SAFS_VolConvertROtoRWvolume: Couldn't unlink RO header, error = %d\n", error); + } + FSYNC_askfs(volumeId, pname, FSYNC_DONE, 0); + FSYNC_askfs(h.id, pname, FSYNC_ON, 0); + return 0; +#else /* AFS_NAMEI_ENV */ + return EINVAL; +#endif /* AFS_NAMEI_ENV */ +} + +afs_int32 +SAFSVolGetSize(struct rx_call *acid, afs_int32 fromTrans, afs_int32 fromDate, + register struct volintSize *size) +{ + int code = 0; + register struct volser_trans *tt; + char caller[MAXKTCNAMELEN]; + + if (!afsconf_SuperUser(tdir, acid, caller)) + return VOLSERBAD_ACCESS; /*not a super user */ + tt = FindTrans(fromTrans); + if (!tt) + return ENOENT; + if (tt->vflags & VTDeleted) { + TRELE(tt); + return ENOENT; + } + strcpy(tt->lastProcName, "GetSize"); + tt->rxCallPtr = acid; + code = SizeDumpVolume(acid, tt->volume, fromDate, 1, size); /* measure volume's data */ + tt->rxCallPtr = (struct rx_call *)0; + if (TRELE(tt)) + return VOLSERTRELE_ERROR; + +/* osi_auditU(acid, VS_DumpEvent, code, AUD_LONG, fromTrans, AUD_END); */ + return code; +} + +/* GetPartName - map partid (a decimal number) into pname (a string) + * Since for NT we actually want to return the drive name, we map through the + * partition struct. + */ +static int +GetPartName(afs_int32 partid, char *pname) +{ + if (partid < 0) + return -1; + if (partid < 26) { + strcpy(pname, "/vicep"); + pname[6] = 'a' + partid; + pname[7] = '\0'; + return 0; + } else if (partid < VOLMAXPARTS) { + strcpy(pname, "/vicep"); + partid -= 26; + pname[6] = 'a' + (partid / 26); + pname[7] = 'a' + (partid % 26); + pname[8] = '\0'; + return 0; + } else + return -1; +} diff --git a/src/volser/volser.p.h b/src/volser/volser.p.h new file mode 100644 index 000000000..091b523b8 --- /dev/null +++ b/src/volser/volser.p.h @@ -0,0 +1,172 @@ +/* + * Copyright 2000, International Business Machines Corporation and others. + * All Rights Reserved. + * + * This software has been released under the terms of the IBM Public + * License. For details, see the LICENSE file in the top-level source + * directory or online at http://www.openafs.org/dl/license10.html + */ + +#ifndef _VOLSER_ +#define _VOLSER_ 1 + +#ifdef AFS_PTHREAD_ENV +#include +#include +#endif + +/* vflags, representing state of the volume */ +#define VTDeleteOnSalvage 1 /* delete on next salvage */ +#define VTOutOfService 2 /* never put this volume online */ +#define VTDeleted 4 /* deleted, don't do anything else */ + +/* iflags, representing "attach mode" for this volume at the start of this transaction */ +#define ITOffline 1 /* volume offline on server (returns VOFFLINE) */ +#define ITBusy 2 /* volume busy on server (returns VBUSY) */ +#define ITReadOnly 8 /* volume readonly on client, readwrite on server -DO NOT USE */ +#define ITCreate 0x10 /* volume does not exist correctly yet */ +#define ITCreateVolID 0x1000 /* create volid */ + +/* tflags, representing transaction state */ +#define TTDeleted 1 /* delete transaction not yet freed due to high refCount */ + +/* other names for volumes in voldefs.h */ +#define volser_RW 0 +#define volser_RO 1 +#define volser_BACK 2 + +#define THOLD(tt) ((tt)->refCount++) + +struct volser_trans { + struct volser_trans *next; /* next ptr in active trans list */ + afs_int32 tid; /* transaction id */ + afs_int32 time; /* time transaction was last active (for timeouts) */ + afs_int32 creationTime; /* time the transaction started */ + afs_int32 returnCode; /* transaction error code */ + struct Volume *volume; /* pointer to open volume */ + afs_int32 volid; /* open volume's id */ + afs_int32 partition; /* open volume's partition */ + afs_int32 dumpTransId; /* other side's trans id during a dump */ + afs_int32 dumpSeq; /* next sequence number to use during a dump */ + short refCount; /* reference count on this structure */ + short iflags; /* initial attach mode flags (IT*) */ + char vflags; /* current volume status flags (VT*) */ + char tflags; /* transaction flags (TT*) */ + char incremental; /* do an incremental restore */ + /* the fields below are useful for debugging */ + char lastProcName[30]; /* name of the last procedure which used transaction */ + struct rx_call *rxCallPtr; /* pointer to latest associated rx_call */ + +}; + +/* This is how often the garbage collection thread wakes up and + * checks for transactions that have timed out: BKGLoop() + */ +#define GCWAKEUP 30 + +struct volser_dest { + afs_int32 destHost; + afs_int32 destPort; + afs_int32 destSSID; +}; + +#define MAXHELPERS 10 +/* flags for vol helper busyFlags array. First, VHIdle goes on when a server + * becomes idle. Next, idle flag is cleared and VHRequest goes on when + * trans is queued. Finally, VHRequest goes off (but VHIdle stays off) when + * helper is done. VHIdle goes on again when an lwp waits for work. + */ +#define VHIdle 1 /* vol helper is waiting for a request here */ +#define VHRequest 2 /* a request has been queued here */ +extern struct volser_trans *QI_GlobalWriteTrans; + +/* the stuff below is from errors.h in vol directory */ +#define VICE_SPECIAL_ERRORS 101 /* Lowest special error code */ + +#define VSALVAGE 101 /* Volume needs salvage */ +#define VNOVNODE 102 /* Bad vnode number quoted */ +#define VNOVOL 103 /* Volume not attached, doesn't exist, + * not created or not online */ +#define VVOLEXISTS 104 /* Volume already exists */ +#define VNOSERVICE 105 /* Volume is not in service (i.e. it's + * is out of funds, is obsolete, or somesuch) */ +#define VOFFLINE 106 /* Volume is off line, for the reason + * given in the offline message */ +#define VONLINE 107 /* Volume is already on line */ +#define VDISKFULL 108 /* Partition is "full", i.e. rougly within + * n% of full */ +#define VOVERQUOTA 109 /* Volume max quota exceeded */ +#define VBUSY 110 /* Volume temporarily unavailable; try again. + * The volume should be available again shortly; if + * it isn't something is wrong. Not normally to be + * propagated to the application level */ +#define VMOVED 111 /* Volume has moved to another server; do a VGetVolumeInfo + * to THIS server to find out where */ + +#define MyPort 5003 +#define NameLen 80 +#define VLDB_MAXSERVERS 10 +#define VOLSERVICE_ID 4 +#define INVALID_BID 0 +#define VOLSER_MAXVOLNAME 65 +#define VOLSER_OLDMAXVOLNAME 32 +#define VOLMAXPARTS 255 + +/*flags used for interfacing with the backup system */ +struct volDescription { /*used for interfacing with the backup system */ + char volName[VOLSER_MAXVOLNAME]; /* should be VNAMESIZE as defined in volume.h */ + afs_int32 volId; + int volSize; + afs_int32 volFlags; + afs_int32 volCloneId; +}; + +struct partList { /*used by the backup system */ + afs_int32 partId[VOLMAXPARTS]; + afs_int32 partFlags[VOLMAXPARTS]; +}; + +#define STDERR stderr +#define STDOUT stdout + +#define ISNAMEVALID(name) (strlen(name) < (VOLSER_OLDMAXVOLNAME - 9)) + +/* values for flags in struct nvldbEntry */ +#define RW_EXISTS 0x1000 +#define RO_EXISTS 0x2000 +#define BACK_EXISTS 0x4000 + +/* values for serverFlags in struct nvldbEntry */ +#define NEW_REPSITE 0x01 +#define ITSROVOL 0x02 +#define ITSRWVOL 0x04 +#define ITSBACKVOL 0x08 +#define RO_DONTUSE 0x20 + +#define VLOP_RESTORE 0x100 /*this is bogus, clashes with VLOP_DUMP */ +#define VLOP_ADDSITE 0x80 /*this is bogus, clashes with VLOP_DELETE */ +#define PARTVALID 0x01 +#define CLONEVALID 0x02 +#define CLONEZAPPED 0x04 +#define IDVALID 0x08 +#define NAMEVALID 0x10 +#define SIZEVALID 0x20 +#define ENTRYVALID 0x40 +#define REUSECLONEID 0x80 +#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[]); +extern afs_int32 vsu_ClientInit(int noAuthFlag, char *confDir, char *cellName, afs_int32 sauth, struct ubik_client **uclientp, int (*secproc)()); +extern void vsu_SetCrypt(int cryptflag); + +#endif /* _VOLSER_ */ diff --git a/src/volser/volser_prototypes.h b/src/volser/volser_prototypes.h new file mode 100644 index 000000000..6d2de8843 --- /dev/null +++ b/src/volser/volser_prototypes.h @@ -0,0 +1,102 @@ +/* + * Copyright 2000, International Business Machines Corporation and others. + * All Rights Reserved. + * + * This software has been released under the terms of the IBM Public + * License. For details, see the LICENSE file in the top-level source + * directory or online at http://www.openafs.org/dl/license10.html + */ + +#ifndef _VOLSER_PROTOTYPES_H +#define _VOLSER_PROTOTYPES_H + +/* common.c */ + +/* vsprocs.c */ +struct nvldbentry; +extern void MapPartIdIntoName(afs_int32 partId, char *partName); +extern int yesprompt(char *str); +extern int PrintError(char *msg, afs_int32 errcode); +extern int UV_SetSecurity(register struct rx_securityClass *as, + afs_int32 aindex); +extern struct rx_connection *UV_Bind(afs_int32 aserver, afs_int32 port); +extern void SubEnumerateEntry(struct nvldbentry *entry); +extern void EnumerateEntry(struct nvldbentry *entry); +extern int UV_NukeVolume(afs_int32 server, afs_int32 partid, afs_int32 volid); +extern int UV_PartitionInfo(afs_int32 server, char *pname, + struct diskPartition *partition); +extern int UV_CreateVolume(afs_int32 aserver, afs_int32 apart, char *aname, + afs_int32 * anewid); +extern int UV_CreateVolume2(afs_int32 aserver, afs_int32 apart, char *aname, + afs_int32 aquota, afs_int32 aspare1, + afs_int32 aspare2, afs_int32 aspare3, + afs_int32 aspare4, afs_int32 * anewid); +extern int UV_AddVLDBEntry(afs_int32 aserver, afs_int32 apart, char *aname, + afs_int32 aid); +extern int UV_DeleteVolume(afs_int32 aserver, afs_int32 apart, + afs_int32 avolid); +extern void sigint_handler(int x); +extern int UV_MoveVolume(afs_int32 afromvol, afs_int32 afromserver, + afs_int32 afrompart, afs_int32 atoserver, + afs_int32 atopart); +extern int UV_BackupVolume(afs_int32 aserver, afs_int32 apart, + afs_int32 avolid); +extern int UV_ReleaseVolume(afs_int32 afromvol, afs_int32 afromserver, + afs_int32 afrompart, int forceflag); +extern void dump_sig_handler(int x); +extern int UV_DumpVolume(afs_int32 afromvol, afs_int32 afromserver, + afs_int32 afrompart, afs_int32 fromdate, + afs_int32(*DumpFunction) (), char *rock); +extern int UV_RestoreVolume(afs_int32 toserver, afs_int32 topart, + afs_int32 tovolid, char tovolname[], int flags, + afs_int32(*WriteData) (), char *rock); +extern int UV_LockRelease(afs_int32 volid); +extern int UV_AddSite(afs_int32 server, afs_int32 part, afs_int32 volid); +extern int UV_RemoveSite(afs_int32 server, afs_int32 part, afs_int32 volid); +extern int UV_ChangeLocation(afs_int32 server, afs_int32 part, + afs_int32 volid); +extern int UV_ListPartitions(afs_int32 aserver, struct partList *ptrPartList, + afs_int32 * cntp); +extern int UV_ZapVolumeClones(afs_int32 aserver, afs_int32 apart, + struct volDescription *volPtr, + afs_int32 arraySize); +extern int UV_GenerateVolumeClones(afs_int32 aserver, afs_int32 apart, + struct volDescription *volPtr, + afs_int32 arraySize); +extern int UV_ListVolumes(afs_int32 aserver, afs_int32 apart, int all, + struct volintInfo **resultPtr, afs_int32 * size); +extern int UV_XListVolumes(afs_int32 a_serverID, afs_int32 a_partID, + int a_all, struct volintXInfo **a_resultPP, + afs_int32 * a_numEntsInResultP); +extern int UV_ListOneVolume(afs_int32 aserver, afs_int32 apart, + afs_int32 volid, struct volintInfo **resultPtr); +extern int UV_XListOneVolume(afs_int32 a_serverID, afs_int32 a_partID, + afs_int32 a_volID, + struct volintXInfo **a_resultPP); +extern int sortVolumes(const void *a, const void *b); +extern int UV_SyncVolume(afs_int32 aserver, afs_int32 apart, char *avolname, + int flags); +extern int UV_SyncVldb(afs_int32 aserver, afs_int32 apart, int flags, + int force); +extern afs_int32 VolumeExists(afs_int32 server, afs_int32 partition, + afs_int32 volumeid); +extern afs_int32 CheckVldbRWBK(struct nvldbentry *entry, + afs_int32 * modified); +extern int CheckVldbRO(struct nvldbentry *entry, afs_int32 * modified); +extern afs_int32 CheckVldb(struct nvldbentry *entry, afs_int32 * modified); +extern int UV_SyncServer(afs_int32 aserver, afs_int32 apart, int flags, + int force); +extern int UV_RenameVolume(struct nvldbentry *entry, char oldname[], + char newname[]); +extern int UV_VolserStatus(afs_int32 server, transDebugInfo ** rpntr, + afs_int32 * rcount); +extern int UV_VolumeZap(afs_int32 server, afs_int32 part, afs_int32 volid); +extern int UV_SetVolume(afs_int32 server, afs_int32 partition, + afs_int32 volid, afs_int32 transflag, + afs_int32 setflag, int sleeptime); +extern int UV_SetVolumeInfo(afs_int32 server, afs_int32 partition, + afs_int32 volid, volintInfo * infop); +extern void MapNetworkToHost(struct nvldbentry *old, struct nvldbentry *new); +extern void MapHostToNetwork(struct nvldbentry *entry); + +#endif diff --git a/src/volser/volserver.rc b/src/volser/volserver.rc new file mode 100644 index 000000000..074216fa8 --- /dev/null +++ b/src/volser/volserver.rc @@ -0,0 +1,17 @@ +/* + * Copyright 2000, International Business Machines Corporation and others. + * All Rights Reserved. + * + * This software has been released under the terms of the IBM Public + * License. For details, see the LICENSE file in the top-level source + * directory or online at http://www.openafs.org/dl/license10.html + */ + +/* Define VERSIONINFO resource */ + +#define AFS_VERINFO_FILE_DESCRIPTION "AFS Volume Server" +#define AFS_VERINFO_NAME "volserver" +#define AFS_VERINFO_FILENAME "volserver.exe" + +#include "AFS_component_version_number.h" +#include "..\config\NTVersioninfo.rc" diff --git a/src/volser/voltrans.c b/src/volser/voltrans.c new file mode 100644 index 000000000..4c28fbec0 --- /dev/null +++ b/src/volser/voltrans.c @@ -0,0 +1,228 @@ +/* + * Copyright 2000, International Business Machines Corporation and others. + * All Rights Reserved. + * + * This software has been released under the terms of the IBM Public + * License. For details, see the LICENSE file in the top-level source + * directory or online at http://www.openafs.org/dl/license10.html + */ + +/* + * Module: Voltrans.c + * System: Volser + * Instituition: ITC, CMU + * Date: December, 88 + */ + +#include +#include + +RCSID + ("$Header: /cvs/openafs/src/volser/voltrans.c,v 1.10 2003/11/22 02:57:04 shadow Exp $"); + +#ifdef AFS_NT40_ENV +#include +#else +#include +#endif + +#include +#include +#include +#ifdef AFS_NT40_ENV +#include +#include +#else +#include +#include +#include +#endif +#include +#include +#include +#include +#ifdef AFS_PTHREAD_ENV +#include +#else /* AFS_PTHREAD_ENV */ +#include +#endif /* AFS_PTHREAD_ENV */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef AFS_NT40_ENV +#include +#endif +#include +#include + +#ifdef HAVE_STRING_H +#include +#else +#ifdef HAVE_STRINGS_H +#include +#endif +#endif + +#include "volser.h" + +/*@printflike@*/ extern void Log(const char *format, ...); + +static struct volser_trans *allTrans = 0; +static afs_int32 transCounter = 1; + +/* create a new transaction, returning ptr to same with high ref count */ +struct volser_trans * +NewTrans(avol, apart) + afs_int32 avol; + afs_int32 apart; +{ + /* set volid, next, partition */ + register struct volser_trans *tt; + struct timeval tp; + struct timezone tzp; + + VTRANS_LOCK; + /* don't allow the same volume to be attached twice */ + for (tt = allTrans; tt; tt = tt->next) { + if ((tt->volid == avol) && (tt->partition == apart)) { + VTRANS_UNLOCK; + return (struct volser_trans *)0; /* volume busy */ + } + } + VTRANS_UNLOCK; + tt = (struct volser_trans *)malloc(sizeof(struct volser_trans)); + memset(tt, 0, sizeof(struct volser_trans)); + tt->volid = avol; + tt->partition = apart; + tt->refCount = 1; + tt->rxCallPtr = (struct rx_call *)0; + strcpy(tt->lastProcName, ""); + gettimeofday(&tp, &tzp); + tt->creationTime = tp.tv_sec; + tt->time = FT_ApproxTime(); + VTRANS_LOCK; + tt->tid = transCounter++; + tt->next = allTrans; + allTrans = tt; + VTRANS_UNLOCK; + return tt; +} + +/* find a trans, again returning with high ref count */ +struct volser_trans * +FindTrans(atrans) + register afs_int32 atrans; +{ + register struct volser_trans *tt; + VTRANS_LOCK; + for (tt = allTrans; tt; tt = tt->next) { + if (tt->tid == atrans) { + tt->time = FT_ApproxTime(); + tt->refCount++; + VTRANS_UNLOCK; + return tt; + } + } + VTRANS_UNLOCK; + return (struct volser_trans *)0; +} + +/* delete transaction if refcount == 1, otherwise queue delete for later. Does implicit TRELE */ +DeleteTrans(atrans) + register struct volser_trans *atrans; +{ + register struct volser_trans *tt, **lt; + afs_int32 error; + + if (atrans->refCount > 1) { + /* someone else is using it now */ + atrans->refCount--; + atrans->tflags |= TTDeleted; + return 0; + } + + /* otherwise we zap it ourselves */ + VTRANS_LOCK; + lt = &allTrans; + for (tt = *lt; tt; lt = &tt->next, tt = *lt) { + if (tt == atrans) { + if (tt->volume) + VDetachVolume(&error, tt->volume); + tt->volume = NULL; + *lt = tt->next; + free(tt); + VTRANS_UNLOCK; + return 0; + } + } + VTRANS_UNLOCK; + return -1; /* failed to find the transaction in the generic list */ +} + +/* THOLD is a macro defined in volser.h */ + +/* put a transaction back */ +TRELE(at) + register struct volser_trans *at; +{ + if (at->refCount == 0) { + Log("TRELE: bad refcount\n"); + return VOLSERTRELE_ERROR; + } + + at->time = FT_ApproxTime(); /* we're still using it */ + if (at->refCount == 1 && (at->tflags & TTDeleted)) { + DeleteTrans(at); + return 0; + } + /* otherwise simply drop refcount */ + at->refCount--; + return 0; +} + +/* look for old transactions and delete them */ +#define OLDTRANSTIME 600 /* seconds */ +#define OLDTRANSWARN 300 /* seconds */ +static int GCDeletes = 0; +GCTrans() +{ + register struct volser_trans *tt, *nt; + afs_int32 now; + + now = FT_ApproxTime(); + + VTRANS_LOCK; + for (tt = allTrans; tt; tt = nt) { + nt = tt->next; /* remember in case we zap it */ + if (tt->time + OLDTRANSWARN < now) { + Log("trans %u on volume %u %s than %d seconds\n", tt->tid, + tt->volid, + ((tt->refCount > 0) ? "is older" : "has been idle for more"), + (((now - tt->time) / GCWAKEUP) * GCWAKEUP)); + } + if (tt->refCount > 0) + continue; + if (tt->time + OLDTRANSTIME < now) { + Log("trans %u on volume %u has timed out\n", tt->tid, tt->volid); + tt->refCount++; /* we're using it now */ + DeleteTrans(tt); /* drops refCount or deletes it */ + GCDeletes++; + } + } + VTRANS_UNLOCK; + return 0; +} + +/*return the head of the transaction list */ +struct volser_trans * +TransList() +{ + return (allTrans); +} diff --git a/src/volser/vos.c b/src/volser/vos.c new file mode 100644 index 000000000..23689f03c --- /dev/null +++ b/src/volser/vos.c @@ -0,0 +1,5593 @@ +/* + * Copyright 2000, International Business Machines Corporation and others. + * All Rights Reserved. + * + * This software has been released under the terms of the IBM Public + * License. For details, see the LICENSE file in the top-level source + * directory or online at http://www.openafs.org/dl/license10.html + */ + +#include +#include + +RCSID + ("$Header: /cvs/openafs/src/volser/vos.c,v 1.38 2004/04/08 22:20:39 jaltman Exp $"); + +#include +#ifdef AFS_NT40_ENV +#include +#include +#include +#else +#include +#include +#include +#include +#include +#endif +#include +#ifdef AFS_AIX_ENV +#include +#endif +#include + +#ifdef HAVE_STRING_H +#include +#else +#ifdef HAVE_STRINGS_H +#include +#endif +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "volser.h" +#include "volint.h" +#include "lockdata.h" +#ifdef AFS_AIX32_ENV +#include +#endif +#include "volser_prototypes.h" + +#ifdef HAVE_POSIX_REGEX +#include +#endif + +struct tqElem { + afs_int32 volid; + struct tqElem *next; +}; + +struct tqHead { + afs_int32 count; + struct tqElem *next; +}; + +#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");\ +cmd_AddParm(ts, "-localauth",CMD_FLAG,CMD_OPTIONAL,"use server tickets");\ +cmd_AddParm(ts, "-verbose", CMD_FLAG, CMD_OPTIONAL, "verbose");\ +cmd_AddParm(ts, "-encrypt", CMD_FLAG, CMD_OPTIONAL, "encrypt commands");\ + +#define ERROR_EXIT(code) {error=(code); goto error_exit;} + +extern int verbose; +int rxInitDone = 0; +struct rx_connection *tconn; +afs_int32 tserver; +extern struct ubik_client *cstruct; +const char *confdir; + +static struct tqHead busyHead, notokHead; + +static void +qInit(struct tqHead *ahead) +{ + memset((char *)ahead, 0, sizeof(struct tqHead)); + return; +} + + +static void +qPut(struct tqHead *ahead, afs_int32 volid) +{ + struct tqElem *elem; + + elem = (struct tqElem *)malloc(sizeof(struct tqElem)); + elem->next = ahead->next; + elem->volid = volid; + ahead->next = elem; + ahead->count++; + return; +} + +static void +qGet(struct tqHead *ahead, afs_int32 *volid) +{ + struct tqElem *tmp; + + if (ahead->count <= 0) + return; + *volid = ahead->next->volid; + tmp = ahead->next; + ahead->next = tmp->next; + ahead->count--; + free(tmp); + return; +} + +/* returns 1 if exists else 0 */ +static int +FileExists(char *filename) +{ + usd_handle_t ufd; + int code; + afs_hyper_t size; + + code = usd_Open(filename, USD_OPEN_RDONLY, 0, &ufd); + if (code) { + return 0; + } + code = USD_IOCTL(ufd, USD_IOCTL_GETSIZE, &size); + USD_CLOSE(ufd); + if (code) { + return 0; + } + return 1; +} + +/* returns 1 if doesnot end in .readonly or .backup, else 0 */ +static int +VolNameOK(char *name) +{ + int total; + + + total = strlen(name); + if (!strcmp(&name[total - 9], ".readonly")) { + return 0; + } else if (!strcmp(&name[total - 7], ".backup")) { + return 0; + } else { + return 1; + } +} + +/* return 1 if name is a number else 0 */ +static int +IsNumeric(char *name) +{ + int result, len, i; + char *ptr; + + result = 1; + ptr = name; + len = strlen(name); + for (i = 0; i < len; i++) { + if (*ptr < '0' || *ptr > '9') { + result = 0; + break; + } + ptr++; + + } + return result; +} + + +/* + * Parse a server name/address and return the address in HOST BYTE order + */ +afs_int32 +GetServer(char *aname) +{ + register struct hostent *th; + afs_int32 addr; + int b1, b2, b3, b4; + register afs_int32 code; + char hostname[MAXHOSTCHARS]; + + code = sscanf(aname, "%d.%d.%d.%d", &b1, &b2, &b3, &b4); + if (code == 4) { + addr = (b1 << 24) | (b2 << 16) | (b3 << 8) | b4; + addr = ntohl(addr); /* convert to host order */ + } else { + th = gethostbyname(aname); + if (!th) + return 0; + memcpy(&addr, th->h_addr, sizeof(addr)); + } + + if (addr == htonl(0x7f000001)) { /* local host */ + code = gethostname(hostname, MAXHOSTCHARS); + if (code) + return 0; + th = gethostbyname(hostname); /* returns host byte order */ + if (!th) + return 0; + memcpy(&addr, th->h_addr, sizeof(addr)); + } + + return (addr); +} + +afs_int32 +GetVolumeType(char *aname) +{ + + if (!strcmp(aname, "ro")) + return (ROVOL); + else if (!strcmp(aname, "rw")) + return (RWVOL); + else if (!strcmp(aname, "bk")) + return (BACKVOL); + else + return (-1); +} + +int +IsPartValid(afs_int32 partId, afs_int32 server, afs_int32 *code) +{ + struct partList dummyPartList; + int i, success, cnt; + + success = 0; + *code = 0; + + *code = UV_ListPartitions(server, &dummyPartList, &cnt); + if (*code) + return success; + for (i = 0; i < cnt; i++) { + if (dummyPartList.partFlags[i] & PARTVALID) + if (dummyPartList.partId[i] == partId) + success = 1; + } + return success; +} + + + + /*sends the contents of file associated with and to Rx Stream + * associated with */ +int +SendFile(usd_handle_t ufd, register struct rx_call *call, long blksize) +{ + char *buffer = (char *)0; + afs_int32 error = 0; + int done = 0; + afs_uint32 nbytes; + + buffer = (char *)malloc(blksize); + if (!buffer) { + fprintf(STDERR, "malloc failed\n"); + return -1; + } + + while (!error && !done) { +#ifndef AFS_NT40_ENV /* NT csn't select on non-socket fd's */ + fd_set in; + FD_ZERO(&in); + FD_SET((int)(ufd->handle), &in); + /* don't timeout if read blocks */ + IOMGR_Select(((int)(ufd->handle)) + 1, &in, 0, 0, 0); +#endif + error = USD_READ(ufd, buffer, blksize, &nbytes); + if (error) { + fprintf(STDERR, "File system read failed\n"); + break; + } + if (nbytes == 0) { + done = 1; + break; + } + if (rx_Write(call, buffer, nbytes) != nbytes) { + error = -1; + break; + } + } + if (buffer) + free(buffer); + return error; +} + +/* function invoked by UV_RestoreVolume, reads the data from rx_trx_stream and + * writes it out to the volume. */ +afs_int32 +WriteData(struct rx_call *call, char *rock) +{ + char *filename; + usd_handle_t ufd; + long blksize; + afs_int32 error, code; + int ufdIsOpen = 0; + + error = 0; + + filename = rock; + if (!filename || !*filename) { + usd_StandardInput(&ufd); + blksize = 4096; + ufdIsOpen = 1; + } else { + code = usd_Open(filename, USD_OPEN_RDONLY, 0, &ufd); + if (code == 0) { + ufdIsOpen = 1; + code = USD_IOCTL(ufd, USD_IOCTL_GETBLKSIZE, &blksize); + } + if (code) { + fprintf(STDERR, "Could not access file '%s'\n", filename); + error = VOLSERBADOP; + goto wfail; + } + } + code = SendFile(ufd, call, blksize); + if (code) { + error = code; + goto wfail; + } + wfail: + if (ufdIsOpen) { + code = USD_CLOSE(ufd); + if (code) { + fprintf(STDERR, "Could not close dump file %s\n", + (filename && *filename) ? filename : "STDOUT"); + if (!error) + error = code; + } + } + return error; +} + +/* Receive data from stream into file associated + * with + */ +int +ReceiveFile(usd_handle_t ufd, struct rx_call *call, long blksize) +{ + char *buffer = NULL; + afs_int32 bytesread; + afs_uint32 bytesleft, w; + afs_int32 error = 0; + + buffer = (char *)malloc(blksize); + if (!buffer) { + fprintf(STDERR, "memory allocation failed\n"); + ERROR_EXIT(-1); + } + + while ((bytesread = rx_Read(call, buffer, blksize)) > 0) { + for (bytesleft = bytesread; bytesleft; bytesleft -= w) { +#ifndef AFS_NT40_ENV /* NT csn't select on non-socket fd's */ + fd_set out; + FD_ZERO(&out); + FD_SET((int)(ufd->handle), &out); + /* don't timeout if write blocks */ + IOMGR_Select(((int)(ufd->handle)) + 1, 0, &out, 0, 0); +#endif + error = + USD_WRITE(ufd, &buffer[bytesread - bytesleft], bytesleft, &w); + if (error) { + fprintf(STDERR, "File system write failed\n"); + ERROR_EXIT(-1); + } + } + } + + error_exit: + if (buffer) + free(buffer); + return (error); +} + +afs_int32 +DumpFunction(struct rx_call *call, char *filename) +{ + usd_handle_t ufd; /* default is to stdout */ + afs_int32 error = 0, code; + afs_hyper_t size; + long blksize; + int ufdIsOpen = 0; + + /* Open the output file */ + if (!filename || !*filename) { + usd_StandardOutput(&ufd); + blksize = 4096; + ufdIsOpen = 1; + } else { + code = + usd_Open(filename, USD_OPEN_CREATE | USD_OPEN_RDWR, 0666, &ufd); + if (code == 0) { + ufdIsOpen = 1; + hzero(size); + code = USD_IOCTL(ufd, USD_IOCTL_SETSIZE, &size); + } + if (code == 0) { + code = USD_IOCTL(ufd, USD_IOCTL_GETBLKSIZE, &blksize); + } + if (code) { + fprintf(STDERR, "Could not create file '%s'\n", filename); + ERROR_EXIT(VOLSERBADOP); + } + } + + code = ReceiveFile(ufd, call, blksize); + if (code) + ERROR_EXIT(code); + + error_exit: + /* Close the output file */ + if (ufdIsOpen) { + code = USD_CLOSE(ufd); + if (code) { + fprintf(STDERR, "Could not close dump file %s\n", + (filename && *filename) ? filename : "STDIN"); + if (!error) + error = code; + } + } + + return (error); +} + +static void +DisplayFormat(pntr, server, part, totalOK, totalNotOK, totalBusy, fast, + longlist, disp) + volintInfo *pntr; + afs_int32 server, part; + int *totalOK, *totalNotOK, *totalBusy; + int fast, longlist, disp; +{ + char pname[10]; + + if (fast) { + fprintf(STDOUT, "%-10lu\n", (unsigned long)pntr->volid); + } else if (longlist) { + if (pntr->status == VOK) { + fprintf(STDOUT, "%-32s ", pntr->name); + fprintf(STDOUT, "%10lu ", (unsigned long)pntr->volid); + if (pntr->type == 0) + fprintf(STDOUT, "RW "); + if (pntr->type == 1) + fprintf(STDOUT, "RO "); + if (pntr->type == 2) + fprintf(STDOUT, "BK "); + fprintf(STDOUT, "%10d K ", pntr->size); + if (pntr->inUse == 1) { + fprintf(STDOUT, "On-line"); + *totalOK += 1; + } else { + fprintf(STDOUT, "Off-line"); + *totalNotOK++; + } + if (pntr->needsSalvaged == 1) + fprintf(STDOUT, "**needs salvage**"); + fprintf(STDOUT, "\n"); + MapPartIdIntoName(part, pname); + fprintf(STDOUT, " %s %s \n", hostutil_GetNameByINet(server), + pname); + fprintf(STDOUT, " RWrite %10lu ROnly %10lu Backup %10lu \n", + (unsigned long)pntr->parentID, + (unsigned long)pntr->cloneID, + (unsigned long)pntr->backupID); + fprintf(STDOUT, " MaxQuota %10d K \n", pntr->maxquota); + fprintf(STDOUT, " Creation %s", + ctime((time_t *) & pntr->creationDate)); +#ifdef FULL_LISTVOL_SWITCH + fprintf(STDOUT, " Copy %s", + ctime((time_t *) & pntr->copyDate)); + if (!pntr->backupDate) + fprintf(STDOUT, " Backup Never\n"); + else + fprintf(STDOUT, " Backup %s", + ctime((time_t *) & pntr->backupDate)); + if (pntr->accessDate) + 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, + " %d accesses in the past day (i.e., vnode references)\n", + pntr->dayUse); + } else if (pntr->status == VBUSY) { + *totalBusy += 1; + qPut(&busyHead, pntr->volid); + if (disp) + fprintf(STDOUT, "**** Volume %lu is busy ****\n", + (unsigned long)pntr->volid); + } else { + *totalNotOK += 1; + qPut(¬okHead, pntr->volid); + if (disp) + fprintf(STDOUT, "**** Could not attach volume %lu ****\n", + (unsigned long)pntr->volid); + } + fprintf(STDOUT, "\n"); + } else { /* default listing */ + if (pntr->status == VOK) { + fprintf(STDOUT, "%-32s ", pntr->name); + fprintf(STDOUT, "%10lu ", (unsigned long)pntr->volid); + if (pntr->type == 0) + fprintf(STDOUT, "RW "); + if (pntr->type == 1) + fprintf(STDOUT, "RO "); + if (pntr->type == 2) + fprintf(STDOUT, "BK "); + fprintf(STDOUT, "%10d K ", pntr->size); + if (pntr->inUse == 1) { + fprintf(STDOUT, "On-line"); + *totalOK += 1; + } else { + fprintf(STDOUT, "Off-line"); + *totalNotOK += 1; + } + if (pntr->needsSalvaged == 1) + fprintf(STDOUT, "**needs salvage**"); + fprintf(STDOUT, "\n"); + } else if (pntr->status == VBUSY) { + *totalBusy += 1; + qPut(&busyHead, pntr->volid); + if (disp) + fprintf(STDOUT, "**** Volume %lu is busy ****\n", + (unsigned long)pntr->volid); + } else { + *totalNotOK += 1; + qPut(¬okHead, pntr->volid); + if (disp) + fprintf(STDOUT, "**** Could not attach volume %lu ****\n", + (unsigned long)pntr->volid); + } + } +} + +/*------------------------------------------------------------------------ + * PRIVATE XDisplayFormat + * + * Description: + * Display the contents of one extended volume info structure. + * + * Arguments: + * a_xInfoP : Ptr to extended volume info struct to print. + * a_servID : Server ID to print. + * a_partID : Partition ID to print. + * a_totalOKP : Ptr to total-OK counter. + * a_totalNotOKP : Ptr to total-screwed counter. + * a_totalBusyP : Ptr to total-busy counter. + * a_fast : Fast listing? + * a_int32 : Int32 listing? + * a_showProblems : Show volume problems? + * + * Returns: + * Nothing. + * + * Environment: + * Nothing interesting. + * + * Side Effects: + * As advertised. + *------------------------------------------------------------------------*/ + +static void +XDisplayFormat(a_xInfoP, a_servID, a_partID, a_totalOKP, a_totalNotOKP, + a_totalBusyP, a_fast, a_int32, a_showProblems) + volintXInfo *a_xInfoP; + afs_int32 a_servID; + afs_int32 a_partID; + int *a_totalOKP; + int *a_totalNotOKP; + int *a_totalBusyP; + int a_fast; + int a_int32; + int a_showProblems; + +{ /*XDisplayFormat */ + + char pname[10]; + + if (a_fast) { + /* + * Short & sweet. + */ + fprintf(STDOUT, "%-10lu\n", (unsigned long)a_xInfoP->volid); + } else if (a_int32) { + /* + * Fully-detailed listing. + */ + if (a_xInfoP->status == VOK) { + /* + * Volume's status is OK - all the fields are valid. + */ + fprintf(STDOUT, "%-32s ", a_xInfoP->name); + fprintf(STDOUT, "%10lu ", (unsigned long)a_xInfoP->volid); + if (a_xInfoP->type == 0) + fprintf(STDOUT, "RW "); + if (a_xInfoP->type == 1) + fprintf(STDOUT, "RO "); + if (a_xInfoP->type == 2) + fprintf(STDOUT, "BK "); + fprintf(STDOUT, "%10d K used ", a_xInfoP->size); + fprintf(STDOUT, "%d files ", a_xInfoP->filecount); + if (a_xInfoP->inUse == 1) { + fprintf(STDOUT, "On-line"); + (*a_totalOKP)++; + } else { + fprintf(STDOUT, "Off-line"); + (*a_totalNotOKP)++; + } + fprintf(STDOUT, "\n"); + MapPartIdIntoName(a_partID, pname); + fprintf(STDOUT, " %s %s \n", hostutil_GetNameByINet(a_servID), + pname); + fprintf(STDOUT, " RWrite %10lu ROnly %10lu Backup %10lu \n", + (unsigned long)a_xInfoP->parentID, + (unsigned long)a_xInfoP->cloneID, + (unsigned long)a_xInfoP->backupID); + fprintf(STDOUT, " MaxQuota %10d K \n", a_xInfoP->maxquota); + fprintf(STDOUT, " Creation %s", + ctime((time_t *) & a_xInfoP->creationDate)); +#ifdef FULL_LISTVOL_SWITCH + fprintf(STDOUT, " Copy %s", + ctime((time_t *) & a_xInfoP->copyDate)); + if (!a_xInfoP->backupDate) + fprintf(STDOUT, " Backup Never\n"); + else + fprintf(STDOUT, " Backup %s", + ctime((time_t *) & a_xInfoP->backupDate)); + if (a_xInfoP->accessDate) + 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, + " %d accesses in the past day (i.e., vnode references)\n", + a_xInfoP->dayUse); + + /* + * Print all the read/write and authorship stats. + */ + fprintf(STDOUT, "\n Raw Read/Write Stats\n"); + fprintf(STDOUT, + " |-------------------------------------------|\n"); + fprintf(STDOUT, + " | Same Network | Diff Network |\n"); + fprintf(STDOUT, + " |----------|----------|----------|----------|\n"); + fprintf(STDOUT, + " | Total | Auth | Total | Auth |\n"); + fprintf(STDOUT, + " |----------|----------|----------|----------|\n"); + fprintf(STDOUT, "Reads | %8d | %8d | %8d | %8d |\n", + a_xInfoP->stat_reads[VOLINT_STATS_SAME_NET], + a_xInfoP->stat_reads[VOLINT_STATS_SAME_NET_AUTH], + a_xInfoP->stat_reads[VOLINT_STATS_DIFF_NET], + a_xInfoP->stat_reads[VOLINT_STATS_DIFF_NET_AUTH]); + fprintf(STDOUT, "Writes | %8d | %8d | %8d | %8d |\n", + a_xInfoP->stat_writes[VOLINT_STATS_SAME_NET], + a_xInfoP->stat_writes[VOLINT_STATS_SAME_NET_AUTH], + a_xInfoP->stat_writes[VOLINT_STATS_DIFF_NET], + a_xInfoP->stat_writes[VOLINT_STATS_DIFF_NET_AUTH]); + fprintf(STDOUT, + " |-------------------------------------------|\n\n"); + + fprintf(STDOUT, + " Writes Affecting Authorship\n"); + fprintf(STDOUT, + " |-------------------------------------------|\n"); + fprintf(STDOUT, + " | File Authorship | Directory Authorship|\n"); + fprintf(STDOUT, + " |----------|----------|----------|----------|\n"); + fprintf(STDOUT, + " | Same | Diff | Same | Diff |\n"); + fprintf(STDOUT, + " |----------|----------|----------|----------|\n"); + fprintf(STDOUT, "0-60 sec | %8d | %8d | %8d | %8d |\n", + a_xInfoP->stat_fileSameAuthor[VOLINT_STATS_TIME_IDX_0], + a_xInfoP->stat_fileDiffAuthor[VOLINT_STATS_TIME_IDX_0], + a_xInfoP->stat_dirSameAuthor[VOLINT_STATS_TIME_IDX_0], + a_xInfoP->stat_dirDiffAuthor[VOLINT_STATS_TIME_IDX_0]); + fprintf(STDOUT, "1-10 min | %8d | %8d | %8d | %8d |\n", + a_xInfoP->stat_fileSameAuthor[VOLINT_STATS_TIME_IDX_1], + a_xInfoP->stat_fileDiffAuthor[VOLINT_STATS_TIME_IDX_1], + a_xInfoP->stat_dirSameAuthor[VOLINT_STATS_TIME_IDX_1], + a_xInfoP->stat_dirDiffAuthor[VOLINT_STATS_TIME_IDX_1]); + fprintf(STDOUT, "10min-1hr | %8d | %8d | %8d | %8d |\n", + a_xInfoP->stat_fileSameAuthor[VOLINT_STATS_TIME_IDX_2], + a_xInfoP->stat_fileDiffAuthor[VOLINT_STATS_TIME_IDX_2], + a_xInfoP->stat_dirSameAuthor[VOLINT_STATS_TIME_IDX_2], + a_xInfoP->stat_dirDiffAuthor[VOLINT_STATS_TIME_IDX_2]); + fprintf(STDOUT, "1hr-1day | %8d | %8d | %8d | %8d |\n", + a_xInfoP->stat_fileSameAuthor[VOLINT_STATS_TIME_IDX_3], + a_xInfoP->stat_fileDiffAuthor[VOLINT_STATS_TIME_IDX_3], + a_xInfoP->stat_dirSameAuthor[VOLINT_STATS_TIME_IDX_3], + a_xInfoP->stat_dirDiffAuthor[VOLINT_STATS_TIME_IDX_3]); + fprintf(STDOUT, "1day-1wk | %8d | %8d | %8d | %8d |\n", + a_xInfoP->stat_fileSameAuthor[VOLINT_STATS_TIME_IDX_4], + a_xInfoP->stat_fileDiffAuthor[VOLINT_STATS_TIME_IDX_4], + a_xInfoP->stat_dirSameAuthor[VOLINT_STATS_TIME_IDX_4], + a_xInfoP->stat_dirDiffAuthor[VOLINT_STATS_TIME_IDX_4]); + fprintf(STDOUT, "> 1wk | %8d | %8d | %8d | %8d |\n", + a_xInfoP->stat_fileSameAuthor[VOLINT_STATS_TIME_IDX_5], + a_xInfoP->stat_fileDiffAuthor[VOLINT_STATS_TIME_IDX_5], + a_xInfoP->stat_dirSameAuthor[VOLINT_STATS_TIME_IDX_5], + a_xInfoP->stat_dirDiffAuthor[VOLINT_STATS_TIME_IDX_5]); + fprintf(STDOUT, + " |-------------------------------------------|\n"); + } /*Volume status OK */ + else if (a_xInfoP->status == VBUSY) { + (*a_totalBusyP)++; + qPut(&busyHead, a_xInfoP->volid); + if (a_showProblems) + fprintf(STDOUT, "**** Volume %lu is busy ****\n", + (unsigned long)a_xInfoP->volid); + } /*Busy volume */ + else { + (*a_totalNotOKP)++; + qPut(¬okHead, a_xInfoP->volid); + if (a_showProblems) + fprintf(STDOUT, "**** Could not attach volume %lu ****\n", + (unsigned long)a_xInfoP->volid); + } /*Screwed volume */ + fprintf(STDOUT, "\n"); + } /*Long listing */ + else { + /* + * Default listing. + */ + if (a_xInfoP->status == VOK) { + fprintf(STDOUT, "%-32s ", a_xInfoP->name); + fprintf(STDOUT, "%10lu ", (unsigned long)a_xInfoP->volid); + if (a_xInfoP->type == 0) + fprintf(STDOUT, "RW "); + if (a_xInfoP->type == 1) + fprintf(STDOUT, "RO "); + if (a_xInfoP->type == 2) + fprintf(STDOUT, "BK "); + fprintf(STDOUT, "%10d K ", a_xInfoP->size); + if (a_xInfoP->inUse == 1) { + fprintf(STDOUT, "On-line"); + (*a_totalOKP)++; + } else { + fprintf(STDOUT, "Off-line"); + (*a_totalNotOKP)++; + } + fprintf(STDOUT, "\n"); + } /*Volume OK */ + else if (a_xInfoP->status == VBUSY) { + (*a_totalBusyP)++; + qPut(&busyHead, a_xInfoP->volid); + if (a_showProblems) + fprintf(STDOUT, "**** Volume %lu is busy ****\n", + (unsigned long)a_xInfoP->volid); + } /*Busy volume */ + else { + (*a_totalNotOKP)++; + qPut(¬okHead, a_xInfoP->volid); + if (a_showProblems) + fprintf(STDOUT, "**** Could not attach volume %lu ****\n", + (unsigned long)a_xInfoP->volid); + } /*Screwed volume */ + } /*Default listing */ +} /*XDisplayFormat */ + +#ifdef FULL_LISTVOL_SWITCH +static void +DisplayFormat2(server, partition, pntr) + long server, partition; + volintInfo *pntr; +{ + static long server_cache = -1, partition_cache = -1; + static char hostname[256], address[32], pname[16]; + + if (server != server_cache) { + struct in_addr s; + + s.s_addr = server; + strcpy(hostname, hostutil_GetNameByINet(server)); + strcpy(address, inet_ntoa(s)); + server_cache = server; + } + 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); + fprintf(STDOUT, "serv\t\t%s\t%s\n", address, hostname); + fprintf(STDOUT, "part\t\t%s\n", pname); + switch (pntr->status) { + case VOK: + fprintf(STDOUT, "status\t\tOK\n"); + break; + case VBUSY: + fprintf(STDOUT, "status\t\tBUSY\n"); + return; + default: + fprintf(STDOUT, "status\t\tUNATTACHABLE\n"); + return; + } + fprintf(STDOUT, "backupID\t%lu\n", pntr->backupID); + fprintf(STDOUT, "parentID\t%lu\n", pntr->parentID); + fprintf(STDOUT, "cloneID\t\t%lu\n", pntr->cloneID); + fprintf(STDOUT, "inUse\t\t%s\n", pntr->inUse ? "Y" : "N"); + fprintf(STDOUT, "needsSalvaged\t%s\n", pntr->needsSalvaged ? "Y" : "N"); + /* 0xD3 is from afs/volume.h since I had trouble including the file */ + fprintf(STDOUT, "destroyMe\t%s\n", pntr->destroyMe == 0xD3 ? "Y" : "N"); + switch (pntr->type) { + case 0: + fprintf(STDOUT, "type\t\tRW\n"); + break; + case 1: + fprintf(STDOUT, "type\t\tRO\n"); + break; + case 2: + fprintf(STDOUT, "type\t\tBK\n"); + break; + default: + fprintf(STDOUT, "type\t\t?\n"); + break; + } + fprintf(STDOUT, "creationDate\t%-9lu\t%s", pntr->creationDate, + ctime(&pntr->creationDate)); + fprintf(STDOUT, "accessDate\t%-9lu\t%s", pntr->accessDate, + ctime(&pntr->accessDate)); + fprintf(STDOUT, "updateDate\t%-9lu\t%s", pntr->updateDate, + ctime(&pntr->updateDate)); + fprintf(STDOUT, "backupDate\t%-9lu\t%s", pntr->backupDate, + ctime(&pntr->backupDate)); + fprintf(STDOUT, "copyDate\t%-9lu\t%s", pntr->copyDate, + ctime(&pntr->copyDate)); + fprintf(STDOUT, "flags\t\t%#lx\t(Optional)\n", pntr->flags); + fprintf(STDOUT, "diskused\t%u\n", pntr->size); + fprintf(STDOUT, "maxquota\t%u\n", pntr->maxquota); + fprintf(STDOUT, "minquota\t%lu\t(Optional)\n", pntr->spare0); + fprintf(STDOUT, "filecount\t%u\n", pntr->filecount); + fprintf(STDOUT, "dayUse\t\t%u\n", pntr->dayUse); + fprintf(STDOUT, "weekUse\t\t%lu\t(Optional)\n", pntr->spare1); + fprintf(STDOUT, "spare2\t\t%lu\t(Optional)\n", pntr->spare2); + fprintf(STDOUT, "spare3\t\t%lu\t(Optional)\n", pntr->spare3); + return; +} + +static void +DisplayVolumes2(server, partition, pntr, count) + volintInfo *pntr; + long server, partition, count; +{ + long i; + + for (i = 0; i < count; i++) { + fprintf(STDOUT, "BEGIN_OF_ENTRY\n"); + DisplayFormat2(server, partition, pntr); + fprintf(STDOUT, "END_OF_ENTRY\n\n"); + pntr++; + } + return; +} +#endif /* FULL_LISTVOL_SWITCH */ + +static void +DisplayVolumes(server, part, pntr, count, longlist, fast, quiet) + afs_int32 server, part; + volintInfo *pntr; + afs_int32 count, longlist, fast; + int quiet; +{ + int totalOK, totalNotOK, totalBusy, i; + afs_int32 volid; + + totalOK = 0; + totalNotOK = 0; + totalBusy = 0; + qInit(&busyHead); + qInit(¬okHead); + for (i = 0; i < count; i++) { + DisplayFormat(pntr, server, part, &totalOK, &totalNotOK, &totalBusy, + fast, longlist, 0); + pntr++; + } + if (totalBusy) { + while (busyHead.count) { + qGet(&busyHead, &volid); + fprintf(STDOUT, "**** Volume %lu is busy ****\n", + (unsigned long)volid); + } + } + if (totalNotOK) { + while (notokHead.count) { + qGet(¬okHead, &volid); + fprintf(STDOUT, "**** Could not attach volume %lu ****\n", + (unsigned long)volid); + } + } + if (!quiet) { + fprintf(STDOUT, "\n"); + if (!fast) { + fprintf(STDOUT, + "Total volumes onLine %d ; Total volumes offLine %d ; Total busy %d\n\n", + totalOK, totalNotOK, totalBusy); + } + } +} + +/*------------------------------------------------------------------------ + * PRIVATE XDisplayVolumes + * + * Description: + * Display extended volume information. + * + * Arguments: + * a_servID : Pointer to the Rx call we're performing. + * a_partID : Partition for which we want the extended list. + * a_xInfoP : Ptr to extended volume info. + * a_count : Number of volume records contained above. + * a_int32 : Int32 listing generated? + * a_fast : Fast listing generated? + * a_quiet : Quiet listing generated? + * + * Returns: + * Nothing. + * + * Environment: + * Nothing interesting. + * + * Side Effects: + * As advertised. + *------------------------------------------------------------------------*/ + +static void +XDisplayVolumes(a_servID, a_partID, a_xInfoP, a_count, a_int32, a_fast, + a_quiet) + afs_int32 a_servID; + afs_int32 a_partID; + volintXInfo *a_xInfoP; + afs_int32 a_count; + afs_int32 a_int32; + afs_int32 a_fast; + int a_quiet; + +{ /*XDisplayVolumes */ + + int totalOK; /*Total OK volumes */ + int totalNotOK; /*Total screwed volumes */ + int totalBusy; /*Total busy volumes */ + int i; /*Loop variable */ + afs_int32 volid; /*Current volume ID */ + + /* + * Initialize counters and (global!!) queues. + */ + totalOK = 0; + totalNotOK = 0; + totalBusy = 0; + qInit(&busyHead); + qInit(¬okHead); + + /* + * Display each volume in the list. + */ + for (i = 0; i < a_count; i++) { + XDisplayFormat(a_xInfoP, a_servID, a_partID, &totalOK, &totalNotOK, + &totalBusy, a_fast, a_int32, 0); + a_xInfoP++; + } + + /* + * If any volumes were found to be busy or screwed, display them. + */ + if (totalBusy) { + while (busyHead.count) { + qGet(&busyHead, &volid); + fprintf(STDOUT, "**** Volume %lu is busy ****\n", + (unsigned long)volid); + } + } + if (totalNotOK) { + while (notokHead.count) { + qGet(¬okHead, &volid); + fprintf(STDOUT, "**** Could not attach volume %lu ****\n", + (unsigned long)volid); + } + } + + if (!a_quiet) { + fprintf(STDOUT, "\n"); + if (!a_fast) { + fprintf(STDOUT, + "Total volumes: %d on-line, %d off-line, %d busyd\n\n", + totalOK, totalNotOK, totalBusy); + } + } + +} /*XDisplayVolumes */ + +/* set and to the correct values depending on + * and */ +static void +GetServerAndPart(entry, voltype, server, part, previdx) + struct nvldbentry *entry; + afs_int32 *server, *part; + int voltype; + int *previdx; +{ + int i, istart, vtype; + + *server = -1; + *part = -1; + + /* Doesn't check for non-existance of backup volume */ + if ((voltype == RWVOL) || (voltype == BACKVOL)) { + vtype = ITSRWVOL; + istart = 0; /* seach the entire entry */ + } else { + vtype = ITSROVOL; + /* Seach from beginning of entry or pick up where we left off */ + istart = ((*previdx < 0) ? 0 : *previdx + 1); + } + + for (i = istart; i < entry->nServers; i++) { + if (entry->serverFlags[i] & vtype) { + *server = entry->serverNumber[i]; + *part = entry->serverPartition[i]; + *previdx = i; + return; + } + } + + /* Didn't find any, return -1 */ + *previdx = -1; + return; +} + +static void +PostVolumeStats(entry) + struct nvldbentry *entry; +{ + SubEnumerateEntry(entry); + /* Check for VLOP_ALLOPERS */ + if (entry->flags & VLOP_ALLOPERS) + fprintf(STDOUT, " Volume is currently LOCKED \n"); + return; +} + +/*------------------------------------------------------------------------ + * PRIVATE XVolumeStats + * + * Description: + * Display extended volume information. + * + * Arguments: + * a_xInfoP : Ptr to extended volume info. + * a_entryP : Ptr to the volume's VLDB entry. + * a_srvID : Server ID. + * a_partID : Partition ID. + * a_volType : Type of volume to print. + * + * Returns: + * Nothing. + * + * Environment: + * Nothing interesting. + * + * Side Effects: + * As advertised. + *------------------------------------------------------------------------*/ + +static void +XVolumeStats(a_xInfoP, a_entryP, a_srvID, a_partID, a_volType) + volintXInfo *a_xInfoP; + struct nvldbentry *a_entryP; + afs_int32 a_srvID; + afs_int32 a_partID; + int a_volType; + +{ /*XVolumeStats */ + + int totalOK, totalNotOK, totalBusy; /*Dummies - we don't really count here */ + + XDisplayFormat(a_xInfoP, /*Ptr to extended volume info */ + a_srvID, /*Server ID to print */ + a_partID, /*Partition ID to print */ + &totalOK, /*Ptr to total-OK counter */ + &totalNotOK, /*Ptr to total-screwed counter */ + &totalBusy, /*Ptr to total-busy counter */ + 0, /*Don't do a fast listing */ + 1, /*Do a long listing */ + 1); /*Show volume problems */ + return; + +} /*XVolumeStats */ + +static void +VolumeStats(pntr, entry, server, part, voltype) + volintInfo *pntr; + struct nvldbentry *entry; + int voltype; + afs_int32 server, part; +{ + int totalOK, totalNotOK, totalBusy; + + DisplayFormat(pntr, server, part, &totalOK, &totalNotOK, &totalBusy, 0, 1, + 1); + return; +} + +/* command to forcibly remove a volume */ +static +NukeVolume(as) + register struct cmd_syndesc *as; +{ + register afs_int32 code; + afs_int32 volID, err; + afs_int32 partID; + afs_int32 server; + register char *tp; + + server = GetServer(tp = as->parms[0].items->data); + if (!server) { + fprintf(STDERR, "vos: server '%s' not found in host table\n", tp); + return 1; + } + + partID = volutil_GetPartitionID(tp = as->parms[1].items->data); + if (partID == -1) { + fprintf(STDERR, "vos: could not parse '%s' as a partition name", tp); + return 1; + } + + volID = vsu_GetVolumeID(tp = as->parms[2].items->data, cstruct, &err); + if (volID == 0) { + if (err) + PrintError("", err); + else + fprintf(STDERR, + "vos: could not parse '%s' as a numeric volume ID", tp); + return 1; + } + + fprintf(STDOUT, + "vos: forcibly removing all traces of volume %d, please wait...", + volID); + fflush(STDOUT); + code = UV_NukeVolume(server, partID, volID); + if (code == 0) + fprintf(STDOUT, "done.\n"); + else + fprintf(STDOUT, "failed with code %d.\n", code); + return code; +} + + +/*------------------------------------------------------------------------ + * PRIVATE ExamineVolume + * + * Description: + * Routine used to examine a single volume, contacting the VLDB as + * well as the Volume Server. + * + * Arguments: + * as : Ptr to parsed command line arguments. + * + * Returns: + * 0 for a successful operation, + * Otherwise, one of the ubik or VolServer error values. + * + * Environment: + * Nothing interesting. + * + * Side Effects: + * As advertised. + *------------------------------------------------------------------------ + */ +static +ExamineVolume(as) + register struct cmd_syndesc *as; +{ + struct nvldbentry entry; + afs_int32 vcode = 0; + volintInfo *pntr = (volintInfo *) 0; + volintXInfo *xInfoP = (volintXInfo *) 0; + afs_int32 volid; + afs_int32 code, err, error = 0; + int voltype, foundserv = 0, foundentry = 0; + afs_int32 aserver, apart; + int previdx = -1; + int wantExtendedInfo; /*Do we want extended vol info? */ + + wantExtendedInfo = (as->parms[1].items ? 1 : 0); /* -extended */ + + volid = vsu_GetVolumeID(as->parms[0].items->data, cstruct, &err); /* -id */ + if (volid == 0) { + if (err) + PrintError("", err); + else + fprintf(STDERR, "Unknown volume ID or name '%s'\n", + as->parms[0].items->data); + return -1; + } + + if (verbose) { + fprintf(STDOUT, "Fetching VLDB entry for %lu .. ", + (unsigned long)volid); + fflush(STDOUT); + } + vcode = VLDB_GetEntryByID(volid, -1, &entry); + if (vcode) { + fprintf(STDERR, + "Could not fetch the entry for volume number %lu from VLDB \n", + (unsigned long)volid); + return (vcode); + } + if (verbose) + fprintf(STDOUT, "done\n"); + MapHostToNetwork(&entry); + + if (entry.volumeId[RWVOL] == volid) + voltype = RWVOL; + else if (entry.volumeId[BACKVOL] == volid) + voltype = BACKVOL; + else /* (entry.volumeId[ROVOL] == volid) */ + voltype = ROVOL; + + do { /* do {...} while (voltype == ROVOL) */ + /* Get the entry for the volume. If its a RW vol, get the RW entry. + * It its a BK vol, get the RW entry (even if VLDB may say the BK doen't exist). + * If its a RO vol, get the next RO entry. + */ + GetServerAndPart(&entry, ((voltype == ROVOL) ? ROVOL : RWVOL), + &aserver, &apart, &previdx); + if (previdx == -1) { /* searched all entries */ + if (!foundentry) { + fprintf(STDERR, "Volume %s does not exist in VLDB\n\n", + as->parms[0].items->data); + error = ENOENT; + } + break; + } + foundentry = 1; + + /* Get information about the volume from the server */ + if (verbose) { + fprintf(STDOUT, "Getting volume listing from the server %s .. ", + hostutil_GetNameByINet(aserver)); + fflush(STDOUT); + } + if (wantExtendedInfo) + code = UV_XListOneVolume(aserver, apart, volid, &xInfoP); + else + code = UV_ListOneVolume(aserver, apart, volid, &pntr); + if (verbose) + fprintf(STDOUT, "done\n"); + + if (code) { + error = code; + if (code == ENODEV) { + if ((voltype == BACKVOL) && !(entry.flags & BACK_EXISTS)) { + /* The VLDB says there is no backup volume and its not on disk */ + fprintf(STDERR, "Volume %s does not exist\n", + as->parms[0].items->data); + error = ENOENT; + } else { + fprintf(STDERR, + "Volume does not exist on server %s as indicated by the VLDB\n", + hostutil_GetNameByINet(aserver)); + } + } else { + PrintDiagnostics("examine", code); + } + fprintf(STDOUT, "\n"); + } else { + foundserv = 1; + if (wantExtendedInfo) + XVolumeStats(xInfoP, &entry, aserver, apart, voltype); + else +#ifdef FULL_LISTVOL_SWITCH + if (as->parms[2].items) { + DisplayFormat2(aserver, apart, pntr); + EnumerateEntry(&entry); + } else +#endif /* FULL_LISTVOL_SWITCH */ + VolumeStats(pntr, &entry, aserver, apart, voltype); + + if ((voltype == BACKVOL) && !(entry.flags & BACK_EXISTS)) { + /* The VLDB says there is no backup volume yet we found one on disk */ + fprintf(STDERR, "Volume %s does not exist in VLDB\n", + as->parms[0].items->data); + error = ENOENT; + } + } + + if (pntr) + free(pntr); + if (xInfoP) + free(xInfoP); + } while (voltype == ROVOL); + + if (!foundserv) { + fprintf(STDERR, "Dump only information from VLDB\n\n"); + fprintf(STDOUT, "%s \n", entry.name); /* PostVolumeStats doesn't print name */ + } + PostVolumeStats(&entry); + + return (error); +} + +/*------------------------------------------------------------------------ + * PRIVATE SetFields + * + * Description: + * Routine used to change the status of a single volume. + * + * Arguments: + * as : Ptr to parsed command line arguments. + * + * Returns: + * 0 for a successful operation, + * Otherwise, one of the ubik or VolServer error values. + * + * Environment: + * Nothing interesting. + * + * Side Effects: + * As advertised. + *------------------------------------------------------------------------ + */ +static +SetFields(as) + register struct cmd_syndesc *as; +{ + struct nvldbentry entry; + afs_int32 vcode = 0; + volintInfo info; + afs_int32 volid; + afs_int32 code, err; + afs_int32 aserver, apart; + int previdx = -1; + + volid = vsu_GetVolumeID(as->parms[0].items->data, cstruct, &err); /* -id */ + if (volid == 0) { + if (err) + PrintError("", err); + else + fprintf(STDERR, "Unknown volume ID or name '%s'\n", + as->parms[0].items->data); + return -1; + } + + code = VLDB_GetEntryByID(volid, RWVOL, &entry); + if (code) { + fprintf(STDERR, + "Could not fetch the entry for volume number %lu from VLDB \n", + (unsigned long)volid); + return (code); + } + MapHostToNetwork(&entry); + + GetServerAndPart(&entry, RWVOL, &aserver, &apart, &previdx); + if (previdx == -1) { + fprintf(STDERR, "Volume %s does not exist in VLDB\n\n", + as->parms[0].items->data); + return (ENOENT); + } + + memset(&info, 0, sizeof(info)); + info.volid = volid; + info.type = RWVOL; + info.dayUse = -1; + info.maxquota = -1; + info.flags = -1; + info.spare0 = -1; + info.spare1 = -1; + info.spare2 = -1; + info.spare3 = -1; + + if (as->parms[1].items) { + /* -max */ + code = util_GetInt32(as->parms[1].items->data, &info.maxquota); + if (code) { + fprintf(STDERR, "invalid quota value\n"); + return code; + } + } + if (as->parms[2].items) { + /* -clearuse */ + info.dayUse = 0; + } + code = UV_SetVolumeInfo(aserver, apart, volid, &info); + if (code) + fprintf(STDERR, + "Could not update volume info fields for volume number %lu\n", + (unsigned long)volid); + return (code); +} + +/*------------------------------------------------------------------------ + * PRIVATE volOnline + * + * Description: + * Brings a volume online. + * + * Arguments: + * as : Ptr to parsed command line arguments. + * + * Returns: + * 0 for a successful operation, + * + * Environment: + * Nothing interesting. + * + * Side Effects: + * As advertised. + *------------------------------------------------------------------------ + */ +static +volOnline(as) + register struct cmd_syndesc *as; +{ + afs_int32 server, partition, volid; + afs_int32 code, err = 0; + + server = GetServer(as->parms[0].items->data); + if (server == 0) { + fprintf(STDERR, "vos: server '%s' not found in host table\n", + as->parms[0].items->data); + return -1; + } + + partition = volutil_GetPartitionID(as->parms[1].items->data); + if (partition < 0) { + fprintf(STDERR, "vos: could not interpret partition name '%s'\n", + as->parms[1].items->data); + return ENOENT; + } + + volid = vsu_GetVolumeID(as->parms[2].items->data, cstruct, &err); /* -id */ + if (!volid) { + if (err) + PrintError("", err); + else + fprintf(STDERR, "Unknown volume ID or name '%s'\n", + as->parms[0].items->data); + return -1; + } + + code = UV_SetVolume(server, partition, volid, ITOffline, 0 /*online */ , + 0 /*sleep */ ); + if (code) { + fprintf(STDERR, "Failed to set volume. Code = %d\n", code); + return -1; + } + + return 0; +} + +/*------------------------------------------------------------------------ + * PRIVATE volOffline + * + * Description: + * Brings a volume offline. + * + * Arguments: + * as : Ptr to parsed command line arguments. + * + * Returns: + * 0 for a successful operation, + * + * Environment: + * Nothing interesting. + * + * Side Effects: + * As advertised. + *------------------------------------------------------------------------ + */ +static int +volOffline(register struct cmd_syndesc *as) +{ + afs_int32 server, partition, volid; + afs_int32 code, err = 0; + afs_int32 transflag, sleeptime, transdone; + + server = GetServer(as->parms[0].items->data); + if (server == 0) { + fprintf(STDERR, "vos: server '%s' not found in host table\n", + as->parms[0].items->data); + return -1; + } + + partition = volutil_GetPartitionID(as->parms[1].items->data); + if (partition < 0) { + fprintf(STDERR, "vos: could not interpret partition name '%s'\n", + as->parms[1].items->data); + return ENOENT; + } + + volid = vsu_GetVolumeID(as->parms[2].items->data, cstruct, &err); /* -id */ + if (!volid) { + if (err) + PrintError("", err); + else + fprintf(STDERR, "Unknown volume ID or name '%s'\n", + as->parms[0].items->data); + return -1; + } + + transflag = (as->parms[4].items ? ITBusy : ITOffline); + sleeptime = (as->parms[3].items ? atol(as->parms[3].items->data) : 0); + transdone = (sleeptime ? 0 /*online */ : VTOutOfService); + if (as->parms[4].items && !as->parms[3].items) { + fprintf(STDERR, "-sleep option must be used with -busy flag\n"); + return -1; + } + + code = + UV_SetVolume(server, partition, volid, transflag, transdone, + sleeptime); + if (code) { + fprintf(STDERR, "Failed to set volume. Code = %d\n", code); + return -1; + } + + return 0; +} + +static int +CreateVolume(register struct cmd_syndesc *as) +{ + afs_int32 pnum; + char part[10]; + afs_int32 volid, code; + struct nvldbentry entry; + afs_int32 vcode; + afs_int32 quota; + + quota = 5000; + tserver = GetServer(as->parms[0].items->data); + if (!tserver) { + fprintf(STDERR, "vos: host '%s' not found in host table\n", + as->parms[0].items->data); + return ENOENT; + } + 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(pnum, tserver, &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[1].items->data); + return ENOENT; + } + if (!ISNAMEVALID(as->parms[2].items->data)) { + fprintf(STDERR, + "vos: the name of the root volume %s exceeds the size limit of %d\n", + as->parms[2].items->data, VOLSER_OLDMAXVOLNAME - 10); + return E2BIG; + } + if (!VolNameOK(as->parms[2].items->data)) { + fprintf(STDERR, + "Illegal volume name %s, should not end in .readonly or .backup\n", + as->parms[2].items->data); + return EINVAL; + } + if (IsNumeric(as->parms[2].items->data)) { + fprintf(STDERR, "Illegal volume name %s, should not be a number\n", + as->parms[2].items->data); + return EINVAL; + } + vcode = VLDB_GetEntryByName(as->parms[2].items->data, &entry); + if (!vcode) { + fprintf(STDERR, "Volume %s already exists\n", + as->parms[2].items->data); + PrintDiagnostics("create", code); + return EEXIST; + } + + if (as->parms[3].items) { + if (!IsNumeric(as->parms[3].items->data)) { + fprintf(STDERR, "Initial quota %s should be numeric.\n", + as->parms[3].items->data); + return EINVAL; + } + + code = util_GetInt32(as->parms[3].items->data, "a); + if (code) { + fprintf(STDERR, "vos: bad integer specified for quota.\n"); + return code; + } + } + + code = + UV_CreateVolume2(tserver, pnum, as->parms[2].items->data, quota, 0, + 0, 0, 0, &volid); + if (code) { + PrintDiagnostics("create", code); + return code; + } + MapPartIdIntoName(pnum, part); + fprintf(STDOUT, "Volume %lu created on partition %s of %s\n", + (unsigned long)volid, part, as->parms[0].items->data); + + return 0; +} + +static afs_int32 +DeleteAll(entry) + struct nvldbentry *entry; +{ + int i; + afs_int32 error, code, curserver, curpart, volid; + + MapHostToNetwork(entry); + error = 0; + for (i = 0; i < entry->nServers; i++) { + curserver = entry->serverNumber[i]; + curpart = entry->serverPartition[i]; + if (entry->serverFlags[i] & ITSROVOL) { + volid = entry->volumeId[ROVOL]; + } else { + volid = entry->volumeId[RWVOL]; + } + code = UV_DeleteVolume(curserver, curpart, volid); + if (code && !error) + error = code; + } + return error; +} + +static +DeleteVolume(as) + struct cmd_syndesc *as; +{ + afs_int32 err, code = 0; + afs_int32 server = 0, partition = -1, volid; + char pname[10]; + afs_int32 idx, j; + + if (as->parms[0].items) { + server = GetServer(as->parms[0].items->data); + if (!server) { + fprintf(STDERR, "vos: server '%s' not found in host table\n", + as->parms[0].items->data); + return ENOENT; + } + } + + if (as->parms[1].items) { + partition = volutil_GetPartitionID(as->parms[1].items->data); + if (partition < 0) { + fprintf(STDERR, "vos: could not interpret partition name '%s'\n", + as->parms[1].items->data); + return EINVAL; + } + + /* Check for validity of the partition */ + if (!IsPartValid(partition, server, &code)) { + if (code) { + PrintError("", code); + } else { + fprintf(STDERR, + "vos : partition %s does not exist on the server\n", + as->parms[1].items->data); + } + return ENOENT; + } + } + + volid = vsu_GetVolumeID(as->parms[2].items->data, cstruct, &err); + if (volid == 0) { + fprintf(STDERR, "Can't find volume name '%s' in VLDB\n", + as->parms[2].items->data); + if (err) + PrintError("", err); + return ENOENT; + } + + /* If the server or partition option are not complete, try to fill + * them in from the VLDB entry. + */ + if ((partition == -1) || !server) { + struct nvldbentry entry; + + code = VLDB_GetEntryByID(volid, -1, &entry); + if (code) { + fprintf(STDERR, + "Could not fetch the entry for volume %lu from VLDB\n", + (unsigned long)volid); + PrintError("", code); + return (code); + } + + if (((volid == entry.volumeId[RWVOL]) && (entry.flags & RW_EXISTS)) + || ((volid == entry.volumeId[BACKVOL]) + && (entry.flags & BACK_EXISTS))) { + idx = Lp_GetRwIndex(&entry); + if ((idx == -1) || (server && (server != entry.serverNumber[idx])) + || ((partition != -1) + && (partition != entry.serverPartition[idx]))) { + fprintf(STDERR, "VLDB: Volume '%s' no match\n", + as->parms[2].items->data); + return ENOENT; + } + } else if ((volid == entry.volumeId[ROVOL]) + && (entry.flags & RO_EXISTS)) { + for (idx = -1, j = 0; j < entry.nServers; j++) { + if (entry.serverFlags[j] != ITSROVOL) + continue; + + if (((server == 0) || (server == entry.serverNumber[j])) + && ((partition == -1) + || (partition == entry.serverPartition[j]))) { + if (idx != -1) { + fprintf(STDERR, + "VLDB: Volume '%s' matches more than one RO\n", + as->parms[2].items->data); + return ENOENT; + } + idx = j; + } + } + if (idx == -1) { + fprintf(STDERR, "VLDB: Volume '%s' no match\n", + as->parms[2].items->data); + return ENOENT; + } + } else { + fprintf(STDERR, "VLDB: Volume '%s' no match\n", + as->parms[2].items->data); + return ENOENT; + } + + server = htonl(entry.serverNumber[idx]); + partition = entry.serverPartition[idx]; + } + + + code = UV_DeleteVolume(server, partition, volid); + if (code) { + PrintDiagnostics("remove", code); + return code; + } + + MapPartIdIntoName(partition, pname); + fprintf(STDOUT, "Volume %lu on partition %s server %s deleted\n", + (unsigned long)volid, pname, hostutil_GetNameByINet(server)); + return 0; +} + +#define TESTM 0 /* set for move space tests, clear for production */ +static +MoveVolume(as) + register struct cmd_syndesc *as; +{ + + afs_int32 volid, fromserver, toserver, frompart, topart; + afs_int32 flags, code, err; + char fromPartName[10], toPartName[10]; + + struct diskPartition partition; /* for space check */ + volintInfo *p; + + 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; + } + + flags = 0; + if (as->parms[5].items) flags |= RV_NOCLONE; + + /* + * check source partition for space to clone volume + */ + + 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); + + p = (volintInfo *) 0; + code = UV_ListOneVolume(fromserver, frompart, volid, &p); + if (code) { + fprintf(STDERR, "vos:cannot access volume %lu\n", + (unsigned long)volid); + free(p); + exit(1); + } + if (TESTM) + fprintf(STDOUT, "volume %lu size %d\n", (unsigned long)volid, + p->size); + if (partition.free <= p->size) { + fprintf(STDERR, + "vos: no space on target partition %s to move volume %lu\n", + toPartName, (unsigned long)volid); + free(p); + exit(1); + } + free(p); + + if (TESTM) { + fprintf(STDOUT, "size test - don't do move\n"); + exit(0); + } + + /* successful move still not guaranteed but shoot for it */ + + code = + UV_MoveVolume2(volid, fromserver, frompart, toserver, topart, flags); + if (code) { + PrintDiagnostics("move", code); + return code; + } + MapPartIdIntoName(topart, toPartName); + MapPartIdIntoName(frompart, fromPartName); + fprintf(STDOUT, "Volume %lu moved from %s %s to %s %s \n", + (unsigned long)volid, as->parms[1].items->data, fromPartName, + as->parms[3].items->data, toPartName); + + return 0; +} + +static +CopyVolume(as) + register struct cmd_syndesc *as; +{ + 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 */ + volintInfo *p; + + 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[4].items->data); + if (toserver == 0) { + fprintf(STDERR, "vos: server '%s' not found in host table\n", + as->parms[4].items->data); + return ENOENT; + } + + tovolume = as->parms[3].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; + } + code = VLDB_GetEntryByName(tovolume, &entry); + if (!code) { + fprintf(STDERR, "Volume %s already exists\n", tovolume); + PrintDiagnostics("copy", code); + return EEXIST; + } + + 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[5].items->data); + if (topart < 0) { + fprintf(STDERR, "vos: could not interpret partition name '%s'\n", + as->parms[5].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[5].items->data); + return ENOENT; + } + + 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); + + /* + * 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); + + p = (volintInfo *) 0; + code = UV_ListOneVolume(fromserver, frompart, volid, &p); + if (code) { + fprintf(STDERR, "vos:cannot access volume %lu\n", + (unsigned long)volid); + free(p); + exit(1); + } + + 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); + exit(1); + } + free(p); + + /* successful copy still not guaranteed but shoot for it */ + + code = + UV_CopyVolume2(volid, fromserver, frompart, tovolume, toserver, + topart, 0, flags); + if (code) { + PrintDiagnostics("copy", code); + return code; + } + MapPartIdIntoName(topart, toPartName); + MapPartIdIntoName(frompart, fromPartName); + fprintf(STDOUT, "Volume %lu copied from %s %s to %s on %s %s \n", + (unsigned long)volid, as->parms[1].items->data, fromPartName, + tovolume, as->parms[4].items->data, toPartName); + + return 0; +} + + +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; +{ + afs_int32 avolid, aserver, apart, vtype, code, err; + struct nvldbentry entry; + + afs_int32 buvolid, buserver, bupart, butype; + struct nvldbentry buentry; + + avolid = vsu_GetVolumeID(as->parms[0].items->data, cstruct, &err); + if (avolid == 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; + } + code = GetVolumeInfo(avolid, &aserver, &apart, &vtype, &entry); + if (code) + exit(1); + + /* verify this is a readwrite volume */ + + if (vtype != RWVOL) { + fprintf(STDERR, "%s not RW volume\n", as->parms[0].items->data); + exit(1); + } + + /* is there a backup volume already? */ + + if (entry.flags & BACK_EXISTS) { + /* yep, where is it? */ + + buvolid = entry.volumeId[BACKVOL]; + code = GetVolumeInfo(buvolid, &buserver, &bupart, &butype, &buentry); + if (code) + exit(1); + + /* is it local? */ + code = VLDB_IsSameAddrs(buserver, aserver, &err); + if (err) { + fprintf(STDERR, + "Failed to get info about server's %d address(es) from vlserver; aborting call!\n", + buserver); + exit(1); + } + if (!code) { + fprintf(STDERR, + "FATAL ERROR: backup volume %lu exists on server %lu\n", + (unsigned long)buvolid, (unsigned long)buserver); + exit(1); + } + } + + /* nope, carry on */ + + code = UV_BackupVolume(aserver, apart, avolid); + + if (code) { + PrintDiagnostics("backup", code); + return code; + } + fprintf(STDOUT, "Created backup volume for %s \n", + as->parms[0].items->data); + return 0; +} + +static +ReleaseVolume(as) + register struct cmd_syndesc *as; +{ + + struct nvldbentry entry; + afs_int32 avolid, aserver, apart, vtype, code, err; + int force = 0; + + if (as->parms[1].items) + force = 1; + avolid = vsu_GetVolumeID(as->parms[0].items->data, cstruct, &err); + if (avolid == 0) { + if (err) + PrintError("", err); + else + fprintf(STDERR, "vos: can't find volume '%s'\n", + as->parms[0].items->data); + return ENOENT; + } + code = GetVolumeInfo(avolid, &aserver, &apart, &vtype, &entry); + if (code) + return code; + + if (vtype != RWVOL) { + fprintf(STDERR, "%s not a RW volume\n", as->parms[0].items->data); + return (ENOENT); + } + + if (!ISNAMEVALID(entry.name)) { + fprintf(STDERR, + "Volume name %s is too long, rename before releasing\n", + entry.name); + return E2BIG; + } + + code = UV_ReleaseVolume(avolid, aserver, apart, force); + if (code) { + PrintDiagnostics("release", code); + return code; + } + fprintf(STDOUT, "Released volume %s successfully\n", + as->parms[0].items->data); + return 0; +} + +static +DumpVolume(as) + register struct cmd_syndesc *as; + +{ + afs_int32 avolid, aserver, apart, voltype, fromdate = 0, code, err, i; + char filename[NameLen]; + struct nvldbentry entry; + + rx_SetRxDeadTime(60 * 10); + for (i = 0; i < MAXSERVERS; i++) { + struct rx_connection *rxConn = ubik_GetRPCConn(cstruct, i); + if (rxConn == 0) + break; + rx_SetConnDeadTime(rxConn, rx_connDeadTime); + if (rxConn->service) + rxConn->service->connDeadTime = rx_connDeadTime; + } + + avolid = vsu_GetVolumeID(as->parms[0].items->data, cstruct, &err); + if (avolid == 0) { + if (err) + PrintError("", err); + else + fprintf(STDERR, "vos: can't find volume '%s'\n", + as->parms[0].items->data); + return ENOENT; + } + + if (as->parms[3].items || as->parms[4].items) { + if (!as->parms[3].items || !as->parms[4].items) { + fprintf(STDERR, + "Must specify both -server and -partition options\n"); + return -1; + } + aserver = GetServer(as->parms[3].items->data); + if (aserver == 0) { + fprintf(STDERR, "Invalid server name\n"); + return -1; + } + apart = volutil_GetPartitionID(as->parms[4].items->data); + if (apart < 0) { + fprintf(STDERR, "Invalid partition name\n"); + return -1; + } + } else { + code = GetVolumeInfo(avolid, &aserver, &apart, &voltype, &entry); + if (code) + return code; + } + + if (as->parms[1].items && strcmp(as->parms[1].items->data, "0")) { + code = ktime_DateToInt32(as->parms[1].items->data, &fromdate); + if (code) { + fprintf(STDERR, "vos: failed to parse date '%s' (error=%d))\n", + as->parms[1].items->data, code); + return code; + } + } + if (as->parms[2].items) { + strcpy(filename, as->parms[2].items->data); + } else { + strcpy(filename, ""); + } + + if (as->parms[5].items) { + code = + UV_DumpClonedVolume(avolid, aserver, apart, fromdate, + DumpFunction, filename); + } else { + code = + UV_DumpVolume(avolid, aserver, apart, fromdate, DumpFunction, + filename); + } + if (code) { + PrintDiagnostics("dump", code); + return code; + } + if (strcmp(filename, "")) + fprintf(STDERR, "Dumped volume %s in file %s\n", + as->parms[0].items->data, filename); + else + fprintf(STDERR, "Dumped volume %s in stdout \n", + as->parms[0].items->data); + return 0; +} + +#define ASK 0 +#define ABORT 1 +#define FULL 2 +#define INC 3 + +static +RestoreVolume(as) + register struct cmd_syndesc *as; + +{ + afs_int32 avolid, aserver, apart, code, vcode, err; + afs_int32 aoverwrite = ASK; + int restoreflags, readonly = 0, offline = 0, voltype = RWVOL; + char prompt; + char afilename[NameLen], avolname[VOLSER_MAXVOLNAME + 1], apartName[10]; + char volname[VOLSER_MAXVOLNAME + 1]; + struct nvldbentry entry; + + prompt = 'n'; + + if (as->parms[4].items) { + avolid = vsu_GetVolumeID(as->parms[4].items->data, cstruct, &err); + if (avolid == 0) { + if (err) + PrintError("", err); + else + fprintf(STDERR, "vos: can't find volume '%s'\n", + as->parms[4].items->data); + exit(1); + } + } else + avolid = 0; + + if (as->parms[5].items) { + if ((strcmp(as->parms[5].items->data, "a") == 0) + || (strcmp(as->parms[5].items->data, "abort") == 0)) { + aoverwrite = ABORT; + } else if ((strcmp(as->parms[5].items->data, "f") == 0) + || (strcmp(as->parms[5].items->data, "full") == 0)) { + aoverwrite = FULL; + } else if ((strcmp(as->parms[5].items->data, "i") == 0) + || (strcmp(as->parms[5].items->data, "inc") == 0) + || (strcmp(as->parms[5].items->data, "increment") == 0) + || (strcmp(as->parms[5].items->data, "incremental") == 0)) { + aoverwrite = INC; + } else { + fprintf(STDERR, "vos: %s is not a valid argument to -overwrite\n", + as->parms[5].items->data); + exit(1); + } + } + if (as->parms[6].items) + offline = 1; + if (as->parms[7].items) { + readonly = 1; + voltype = ROVOL; + } + + aserver = GetServer(as->parms[0].items->data); + if (aserver == 0) { + fprintf(STDERR, "vos: server '%s' not found in host table\n", + as->parms[0].items->data); + exit(1); + } + apart = volutil_GetPartitionID(as->parms[1].items->data); + if (apart < 0) { + fprintf(STDERR, "vos: could not interpret partition name '%s'\n", + as->parms[1].items->data); + exit(1); + } + if (!IsPartValid(apart, aserver, &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[1].items->data); + exit(1); + } + strcpy(avolname, as->parms[2].items->data); + if (!ISNAMEVALID(avolname)) { + fprintf(STDERR, + "vos: the name of the volume %s exceeds the size limit\n", + avolname); + exit(1); + } + if (!VolNameOK(avolname)) { + fprintf(STDERR, + "Illegal volume name %s, should not end in .readonly or .backup\n", + avolname); + exit(1); + } + if (as->parms[3].items) { + strcpy(afilename, as->parms[3].items->data); + if (!FileExists(afilename)) { + fprintf(STDERR, "Can't access file %s\n", afilename); + exit(1); + } + } else { + strcpy(afilename, ""); + } + + /* Check if volume exists or not */ + + vsu_ExtractName(volname, avolname); + vcode = VLDB_GetEntryByName(volname, &entry); + if (vcode) { /* no volume - do a full restore */ + restoreflags = RV_FULLRST; + if ((aoverwrite == INC) || (aoverwrite == ABORT)) + fprintf(STDERR, + "Volume does not exist; Will perform a full restore\n"); + } + + else if ((!readonly && Lp_GetRwIndex(&entry) == -1) /* RW volume does not exist - do a full */ + ||(readonly && !Lp_ROMatch(0, 0, &entry))) { /* RO volume does not exist - do a full */ + restoreflags = RV_FULLRST; + if ((aoverwrite == INC) || (aoverwrite == ABORT)) + fprintf(STDERR, + "%s Volume does not exist; Will perform a full restore\n", + readonly ? "RO" : "RW"); + + if (avolid == 0) { + avolid = entry.volumeId[voltype]; + } else if (entry.volumeId[voltype] != 0 + && entry.volumeId[voltype] != avolid) { + avolid = entry.volumeId[voltype]; + } + } + + else { /* volume exists - do we do a full incremental or abort */ + int Oserver, Opart, Otype, vol_elsewhere = 0; + struct nvldbentry Oentry; + int c, dc; + + if (avolid == 0) { + avolid = entry.volumeId[voltype]; + } else if (entry.volumeId[voltype] != 0 + && entry.volumeId[voltype] != avolid) { + avolid = entry.volumeId[voltype]; + } + + /* A file name was specified - check if volume is on another partition */ + vcode = GetVolumeInfo(avolid, &Oserver, &Opart, &Otype, &Oentry); + if (vcode) + exit(1); + + vcode = VLDB_IsSameAddrs(Oserver, aserver, &err); + if (err) { + fprintf(STDERR, + "Failed to get info about server's %d address(es) from vlserver (err=%d); aborting call!\n", + Oserver, err); + exit(1); + } + if (!vcode || (Opart != apart)) + vol_elsewhere = 1; + + if (aoverwrite == ASK) { + if (strcmp(afilename, "") == 0) { /* The file is from standard in */ + fprintf(STDERR, + "Volume exists and no -overwrite option specified; Aborting restore command\n"); + exit(1); + } + + /* Ask what to do */ + if (vol_elsewhere) { + fprintf(STDERR, + "The volume %s %u already exists on a different server/part\n", + volname, entry.volumeId[voltype]); + fprintf(STDERR, + "Do you want to do a full restore or abort? [fa](a): "); + } else { + fprintf(STDERR, + "The volume %s %u already exists in the VLDB\n", + volname, entry.volumeId[voltype]); + fprintf(STDERR, + "Do you want to do a full/incremental restore or abort? [fia](a): "); + } + dc = c = getchar(); + while (!(dc == EOF || dc == '\n')) + dc = getchar(); /* goto end of line */ + if ((c == 'f') || (c == 'F')) + aoverwrite = FULL; + else if ((c == 'i') || (c == 'I')) + aoverwrite = INC; + else + aoverwrite = ABORT; + } + + if (aoverwrite == ABORT) { + fprintf(STDERR, "Volume exists; Aborting restore command\n"); + exit(1); + } else if (aoverwrite == FULL) { + restoreflags = RV_FULLRST; + fprintf(STDERR, + "Volume exists; Will delete and perform full restore\n"); + } else if (aoverwrite == INC) { + restoreflags = 0; + if (vol_elsewhere) { + fprintf(STDERR, + "%s volume %lu already exists on a different server/part; not allowed\n", + readonly ? "RO" : "RW", (unsigned long)avolid); + exit(1); + } + } + } + if (offline) + restoreflags |= RV_OFFLINE; + if (readonly) + restoreflags |= RV_RDONLY; + code = + UV_RestoreVolume(aserver, apart, avolid, avolname, restoreflags, + WriteData, afilename); + if (code) { + PrintDiagnostics("restore", code); + exit(1); + } + MapPartIdIntoName(apart, apartName); + + /* + * patch typo here - originally "parms[1]", should be "parms[0]" + */ + + fprintf(STDOUT, "Restored volume %s on %s %s\n", avolname, + as->parms[0].items->data, apartName); + return 0; +} + +static +LockReleaseCmd(as) + register struct cmd_syndesc *as; + +{ + afs_int32 avolid, code, err; + + avolid = vsu_GetVolumeID(as->parms[0].items->data, cstruct, &err); + if (avolid == 0) { + if (err) + PrintError("", err); + else + fprintf(STDERR, "vos: can't find volume '%s'\n", + as->parms[0].items->data); + exit(1); + } + + code = UV_LockRelease(avolid); + if (code) { + PrintDiagnostics("unlock", code); + exit(1); + } + fprintf(STDOUT, "Released lock on vldb entry for volume %s\n", + as->parms[0].items->data); + return 0; +} + +static +AddSite(as) + register struct cmd_syndesc *as; +{ + afs_int32 avolid, aserver, apart, code, err; + char apartName[10], avolname[VOLSER_MAXVOLNAME + 1]; + + vsu_ExtractName(avolname, as->parms[2].items->data);; + avolid = vsu_GetVolumeID(avolname, cstruct, &err); + if (avolid == 0) { + if (err) + PrintError("", err); + else + fprintf(STDERR, "vos: can't find volume '%s'\n", + as->parms[2].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", + as->parms[0].items->data); + exit(1); + } + apart = volutil_GetPartitionID(as->parms[1].items->data); + if (apart < 0) { + fprintf(STDERR, "vos: could not interpret partition name '%s'\n", + as->parms[1].items->data); + exit(1); + } + if (!IsPartValid(apart, aserver, &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[1].items->data); + exit(1); + } + code = UV_AddSite(aserver, apart, avolid); + if (code) { + PrintDiagnostics("addsite", code); + exit(1); + } + MapPartIdIntoName(apart, apartName); + fprintf(STDOUT, "Added replication site %s %s for volume %s\n", + as->parms[0].items->data, apartName, as->parms[2].items->data); + return 0; +} + +static +RemoveSite(as) + register struct cmd_syndesc *as; +{ + + afs_int32 avolid, aserver, apart, code, err; + char apartName[10], avolname[VOLSER_MAXVOLNAME + 1]; + + vsu_ExtractName(avolname, as->parms[2].items->data); + avolid = vsu_GetVolumeID(avolname, cstruct, &err); + if (avolid == 0) { + if (err) + PrintError("", err); + else + fprintf(STDERR, "vos: can't find volume '%s'\n", + as->parms[2].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", + as->parms[0].items->data); + exit(1); + } + apart = volutil_GetPartitionID(as->parms[1].items->data); + if (apart < 0) { + fprintf(STDERR, "vos: could not interpret partition name '%s'\n", + as->parms[1].items->data); + exit(1); + } +/* + *skip the partition validity check, since it is possible that the partition + *has since been decomissioned. + */ +/* + if (!IsPartValid(apart,aserver,&code)){ + if(code) PrintError("",code); + else fprintf(STDERR,"vos : partition %s does not exist on the server\n",as->parms[1].items->data); + exit(1); + } +*/ + code = UV_RemoveSite(aserver, apart, avolid); + if (code) { + PrintDiagnostics("remsite", code); + exit(1); + } + MapPartIdIntoName(apart, apartName); + fprintf(STDOUT, "Removed replication site %s %s for volume %s\n", + as->parms[0].items->data, apartName, as->parms[2].items->data); + return 0; +} + +static +ChangeLocation(as) + register struct cmd_syndesc *as; +{ + afs_int32 avolid, aserver, apart, code, err; + char apartName[10]; + + avolid = vsu_GetVolumeID(as->parms[2].items->data, cstruct, &err); + if (avolid == 0) { + if (err) + PrintError("", err); + else + fprintf(STDERR, "vos: can't find volume '%s'\n", + as->parms[2].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", + as->parms[0].items->data); + exit(1); + } + apart = volutil_GetPartitionID(as->parms[1].items->data); + if (apart < 0) { + fprintf(STDERR, "vos: could not interpret partition name '%s'\n", + as->parms[1].items->data); + exit(1); + } + if (!IsPartValid(apart, aserver, &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[1].items->data); + exit(1); + } + code = UV_ChangeLocation(aserver, apart, avolid); + if (code) { + PrintDiagnostics("addsite", code); + exit(1); + } + MapPartIdIntoName(apart, apartName); + fprintf(STDOUT, "Changed location to %s %s for volume %s\n", + as->parms[0].items->data, apartName, as->parms[2].items->data); + return 0; +} + +static +ListPartitions(as) + register struct cmd_syndesc *as; +{ + afs_int32 aserver, code; + struct partList dummyPartList; + int i; + char pname[10]; + int total, cnt; + + aserver = GetServer(as->parms[0].items->data); + if (aserver == 0) { + fprintf(STDERR, "vos: server '%s' not found in host table\n", + as->parms[0].items->data); + exit(1); + } + + + code = UV_ListPartitions(aserver, &dummyPartList, &cnt); + if (code) { + PrintDiagnostics("listpart", code); + exit(1); + } + total = 0; + fprintf(STDOUT, "The partitions on the server are:\n"); + for (i = 0; i < cnt; i++) { + if (dummyPartList.partFlags[i] & PARTVALID) { + memset(pname, 0, sizeof(pname)); + MapPartIdIntoName(dummyPartList.partId[i], pname); + fprintf(STDOUT, " %10s ", pname); + total++; + if ((i % 5) == 0 && (i != 0)) + fprintf(STDOUT, "\n"); + } + } + fprintf(STDOUT, "\n"); + fprintf(STDOUT, "Total: %d\n", total); + return 0; + +} + +static int +CompareVolName(p1, p2) + char *p1, *p2; +{ + volintInfo *arg1, *arg2; + + arg1 = (volintInfo *) p1; + arg2 = (volintInfo *) p2; + return (strcmp(arg1->name, arg2->name)); + +} + +/*------------------------------------------------------------------------ + * PRIVATE XCompareVolName + * + * Description: + * Comparison routine for volume names coming from an extended + * volume listing. + * + * Arguments: + * a_obj1P : Char ptr to first extended vol info object + * a_obj1P : Char ptr to second extended vol info object + * + * Returns: + * The value of strcmp() on the volume names within the passed + * objects (i,e., -1, 0, or 1). + * + * Environment: + * Passed to qsort() as the designated comparison routine. + * + * Side Effects: + * As advertised. + *------------------------------------------------------------------------*/ + +static int +XCompareVolName(a_obj1P, a_obj2P) + char *a_obj1P, *a_obj2P; + +{ /*XCompareVolName */ + + return (strcmp + (((struct volintXInfo *)(a_obj1P))->name, + ((struct volintXInfo *)(a_obj2P))->name)); + +} /*XCompareVolName */ + +static int +CompareVolID(p1, p2) + char *p1, *p2; +{ + volintInfo *arg1, *arg2; + + arg1 = (volintInfo *) p1; + arg2 = (volintInfo *) p2; + if (arg1->volid == arg2->volid) + return 0; + if (arg1->volid > arg2->volid) + return 1; + else + return -1; + +} + +/*------------------------------------------------------------------------ + * PRIVATE XCompareVolID + * + * Description: + * Comparison routine for volume IDs coming from an extended + * volume listing. + * + * Arguments: + * a_obj1P : Char ptr to first extended vol info object + * a_obj1P : Char ptr to second extended vol info object + * + * Returns: + * The value of strcmp() on the volume names within the passed + * objects (i,e., -1, 0, or 1). + * + * Environment: + * Passed to qsort() as the designated comparison routine. + * + * Side Effects: + * As advertised. + *------------------------------------------------------------------------*/ + +static int +XCompareVolID(a_obj1P, a_obj2P) + char *a_obj1P, *a_obj2P; + +{ /*XCompareVolID */ + + afs_int32 id1, id2; /*Volume IDs we're comparing */ + + id1 = ((struct volintXInfo *)(a_obj1P))->volid; + id2 = ((struct volintXInfo *)(a_obj2P))->volid; + if (id1 == id2) + return (0); + else if (id1 > id2) + return (1); + else + return (-1); + +} /*XCompareVolID */ + +/*------------------------------------------------------------------------ + * PRIVATE ListVolumes + * + * Description: + * Routine used to list volumes, contacting the Volume Server + * directly, bypassing the VLDB. + * + * Arguments: + * as : Ptr to parsed command line arguments. + * + * Returns: + * 0 Successful operation + * + * Environment: + * Nothing interesting. + * + * Side Effects: + * As advertised. + *------------------------------------------------------------------------*/ + +static +ListVolumes(as) + register struct cmd_syndesc *as; +{ + afs_int32 apart, int32list, fast; + afs_int32 aserver, code; + volintInfo *pntr, *oldpntr; + afs_int32 count; + int i; + char *base; + volintXInfo *xInfoP, *origxInfoP; /*Ptr to current/orig extended vol info */ + int wantExtendedInfo; /*Do we want extended vol info? */ + + char pname[10]; + struct partList dummyPartList; + int all; + int quiet, cnt; + + apart = -1; + fast = 0; + int32list = 0; + + if (as->parms[3].items) + int32list = 1; + if (as->parms[4].items) + quiet = 1; + else + quiet = 0; + if (as->parms[2].items) + fast = 1; + if (fast) + all = 0; + else + all = 1; + if (as->parms[5].items) { + /* + * We can't coexist with the fast flag. + */ + if (fast) { + fprintf(STDERR, + "vos: Can't use the -fast and -extended flags together\n"); + exit(1); + } + + /* + * We need to turn on ``long'' listings to get the full effect. + */ + wantExtendedInfo = 1; + int32list = 1; + } else + wantExtendedInfo = 0; + if (as->parms[1].items) { + apart = volutil_GetPartitionID(as->parms[1].items->data); + if (apart < 0) { + fprintf(STDERR, "vos: could not interpret partition name '%s'\n", + as->parms[1].items->data); + exit(1); + } + dummyPartList.partId[0] = apart; + dummyPartList.partFlags[0] = PARTVALID; + cnt = 1; + } + aserver = GetServer(as->parms[0].items->data); + if (aserver == 0) { + fprintf(STDERR, "vos: server '%s' not found in host table\n", + as->parms[0].items->data); + exit(1); + } + + if (apart != -1) { + if (!IsPartValid(apart, aserver, &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[1].items->data); + exit(1); + } + } else { + code = UV_ListPartitions(aserver, &dummyPartList, &cnt); + if (code) { + PrintDiagnostics("listvol", code); + exit(1); + } + } + for (i = 0; i < cnt; i++) { + if (dummyPartList.partFlags[i] & PARTVALID) { + if (wantExtendedInfo) + code = + UV_XListVolumes(aserver, dummyPartList.partId[i], all, + &xInfoP, &count); + else + code = + UV_ListVolumes(aserver, dummyPartList.partId[i], all, + &pntr, &count); + if (code) { + PrintDiagnostics("listvol", code); + if (pntr) + free(pntr); + exit(1); + } + if (wantExtendedInfo) { + origxInfoP = xInfoP; + base = (char *)xInfoP; + } else { + oldpntr = pntr; + base = (char *)pntr; + } + + if (!fast) { + if (wantExtendedInfo) + qsort(base, count, sizeof(volintXInfo), XCompareVolName); + else + qsort(base, count, sizeof(volintInfo), CompareVolName); + } else { + if (wantExtendedInfo) + qsort(base, count, sizeof(volintXInfo), XCompareVolID); + else + qsort(base, count, sizeof(volintInfo), CompareVolID); + } + MapPartIdIntoName(dummyPartList.partId[i], pname); + if (!quiet) + fprintf(STDOUT, + "Total number of volumes on server %s partition %s: %lu \n", + as->parms[0].items->data, pname, + (unsigned long)count); + if (wantExtendedInfo) { + XDisplayVolumes(aserver, dummyPartList.partId[i], origxInfoP, + count, int32list, fast, quiet); + if (xInfoP) + free(xInfoP); + xInfoP = (volintXInfo *) 0; + } else { +#ifdef FULL_LISTVOL_SWITCH + if (as->parms[6].items) + DisplayVolumes2(aserver, dummyPartList.partId[i], oldpntr, + count); + else +#endif /* FULL_LISTVOL_SWITCH */ + DisplayVolumes(aserver, dummyPartList.partId[i], oldpntr, + count, int32list, fast, quiet); + if (pntr) + free(pntr); + pntr = (volintInfo *) 0; + } + } + } + return 0; +} + +static +SyncVldb(as) + register struct cmd_syndesc *as; +{ + afs_int32 pnum, code; /* part name */ + char part[10]; + int flags = 0; + char *volname = 0; + + tserver = 0; + if (as->parms[0].items) { + tserver = GetServer(as->parms[0].items->data); + if (!tserver) { + fprintf(STDERR, "vos: host '%s' not found in host table\n", + as->parms[0].items->data); + exit(1); + } + } + + if (as->parms[1].items) { + 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(pnum, tserver, &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[1].items->data); + exit(1); + } + flags = 1; + + if (!tserver) { + fprintf(STDERR, + "The -partition option requires a -server option\n"); + exit(1); + } + } + + if (as->parms[2].items) { + /* Synchronize an individual volume */ + volname = as->parms[2].items->data; + 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, pnum, flags, 0 /*unused */ ); + } + + if (code) { + PrintDiagnostics("syncvldb", code); + exit(1); + } + + /* Print a summary of what we did */ + if (volname) + fprintf(STDOUT, "VLDB volume %s synchronized", volname); + else + fprintf(STDOUT, "VLDB synchronized"); + if (tserver) { + fprintf(STDOUT, " with state of server %s", as->parms[0].items->data); + } + if (flags) { + MapPartIdIntoName(pnum, part); + fprintf(STDOUT, " partition %s\n", part); + } + fprintf(STDOUT, "\n"); + + return 0; +} + +static +SyncServer(as) + register struct cmd_syndesc *as; + +{ + afs_int32 pnum, code; /* part name */ + char part[10]; + + int flags = 0; + + tserver = GetServer(as->parms[0].items->data); + if (!tserver) { + fprintf(STDERR, "vos: host '%s' not found in host table\n", + as->parms[0].items->data); + exit(1); + } + if (as->parms[1].items) { + 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(pnum, tserver, &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[1].items->data); + exit(1); + } + flags = 1; + } else { + pnum = -1; + } + + code = UV_SyncServer(tserver, pnum, flags, 0 /*unused */ ); + if (code) { + PrintDiagnostics("syncserv", code); + exit(1); + } + if (flags) { + MapPartIdIntoName(pnum, part); + fprintf(STDOUT, "Server %s partition %s synchronized with VLDB\n", + as->parms[0].items->data, part); + } else + fprintf(STDOUT, "Server %s synchronized with VLDB\n", + as->parms[0].items->data); + return 0; + +} + +static +VolumeInfoCmd(name) + char *name; +{ + struct nvldbentry entry; + afs_int32 vcode; + + /* The vlserver will handle names with the .readonly + * and .backup extension as well as volume ids. + */ + vcode = VLDB_GetEntryByName(name, &entry); + if (vcode) { + PrintError("", vcode); + exit(1); + } + MapHostToNetwork(&entry); + EnumerateEntry(&entry); + + /* Defect #3027: grubby check to handle locked volume. + * If VLOP_ALLOPERS is set, the entry is locked. + * Leave this routine as is, but put in correct check. + */ + if (entry.flags & VLOP_ALLOPERS) + fprintf(STDOUT, " Volume is currently LOCKED \n"); + + return 0; +} + +static +VolumeZap(as) + register struct cmd_syndesc *as; + +{ + struct nvldbentry entry; + afs_int32 volid, code, server, part, zapbackupid = 0, backupid = 0, err; + + if (as->parms[3].items) { + /* force flag is on, use the other version */ + return NukeVolume(as); + } + + if (as->parms[4].items) { + zapbackupid = 1; + } + + volid = vsu_GetVolumeID(as->parms[2].items->data, cstruct, &err); + if (volid == 0) { + if (err) + PrintError("", err); + else + fprintf(STDERR, "vos: can't find volume '%s'\n", + as->parms[2].items->data); + exit(1); + } + part = volutil_GetPartitionID(as->parms[1].items->data); + if (part < 0) { + fprintf(STDERR, "vos: could not interpret partition name '%s'\n", + as->parms[1].items->data); + exit(1); + } + server = GetServer(as->parms[0].items->data); + if (!server) { + fprintf(STDERR, "vos: host '%s' not found in host table\n", + as->parms[0].items->data); + exit(1); + } + 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[1].items->data); + exit(1); + } + code = VLDB_GetEntryByID(volid, -1, &entry); + if (!code) { + if (volid == entry.volumeId[RWVOL]) + backupid = entry.volumeId[BACKVOL]; + fprintf(STDERR, + "Warning: Entry for volume number %lu exists in VLDB (but we're zapping it anyway!)\n", + (unsigned long)volid); + } + if (zapbackupid) { + volintInfo *pntr = (volintInfo *) 0; + + if (!backupid) { + code = UV_ListOneVolume(server, part, volid, &pntr); + if (!code) { + if (volid == pntr->parentID) + backupid = pntr->backupID; + if (pntr) + free(pntr); + } + } + if (backupid) { + code = UV_VolumeZap(server, part, backupid); + if (code) { + PrintDiagnostics("zap", code); + exit(1); + } + fprintf(STDOUT, "Backup Volume %lu deleted\n", + (unsigned long)backupid); + } + } + code = UV_VolumeZap(server, part, volid); + if (code) { + PrintDiagnostics("zap", code); + exit(1); + } + fprintf(STDOUT, "Volume %lu deleted\n", (unsigned long)volid); + + return 0; +} + +static +VolserStatus(as) + register struct cmd_syndesc *as; + +{ + afs_int32 server, code; + transDebugInfo *pntr, *oldpntr; + afs_int32 count; + int i; + char pname[10]; + + server = GetServer(as->parms[0].items->data); + if (!server) { + fprintf(STDERR, "vos: host '%s' not found in host table\n", + as->parms[0].items->data); + exit(1); + } + code = UV_VolserStatus(server, &pntr, &count); + if (code) { + PrintDiagnostics("status", code); + exit(1); + } + oldpntr = pntr; + if (count == 0) + fprintf(STDOUT, "No active transactions on %s\n", + as->parms[0].items->data); + else { + fprintf(STDOUT, "Total transactions: %d\n", count); + } + for (i = 0; i < count; i++) { + /*print out the relevant info */ + fprintf(STDOUT, "--------------------------------------\n"); + fprintf(STDOUT, "transaction: %lu created: %s", + (unsigned long)pntr->tid, ctime((time_t *) & pntr->time)); + if (pntr->returnCode) { + fprintf(STDOUT, "returnCode: %lu\n", + (unsigned long)pntr->returnCode); + } + if (pntr->iflags) { + fprintf(STDOUT, "attachFlags: "); + switch (pntr->iflags) { + case ITOffline: + fprintf(STDOUT, "offline "); + break; + case ITBusy: + fprintf(STDOUT, "busy "); + break; + case ITReadOnly: + fprintf(STDOUT, "readonly "); + break; + case ITCreate: + fprintf(STDOUT, "create "); + break; + case ITCreateVolID: + fprintf(STDOUT, "create volid "); + break; + } + fprintf(STDOUT, "\n"); + } + if (pntr->vflags) { + fprintf(STDOUT, "volumeStatus: "); + switch (pntr->vflags) { + case VTDeleteOnSalvage: + fprintf(STDOUT, "deleteOnSalvage "); + case VTOutOfService: + fprintf(STDOUT, "outOfService "); + case VTDeleted: + fprintf(STDOUT, "deleted "); + } + fprintf(STDOUT, "\n"); + } + if (pntr->tflags) { + fprintf(STDOUT, "transactionFlags: "); + fprintf(STDOUT, "delete\n"); + } + MapPartIdIntoName(pntr->partition, pname); + fprintf(STDOUT, "volume: %lu partition: %s procedure: %s\n", + (unsigned long)pntr->volid, pname, pntr->lastProcName); + if (pntr->callValid) { + fprintf(STDOUT, + "packetRead: %lu lastReceiveTime: %d packetSend: %lu lastSendTime: %d\n", + (unsigned long)pntr->readNext, pntr->lastReceiveTime, + (unsigned long)pntr->transmitNext, pntr->lastSendTime); + } + pntr++; + fprintf(STDOUT, "--------------------------------------\n"); + fprintf(STDOUT, "\n"); + } + if (oldpntr) + free(oldpntr); + return 0; +} + +static +RenameVolume(as) + register struct cmd_syndesc *as; +{ + afs_int32 code1, code2, code; + struct nvldbentry entry; + + code1 = VLDB_GetEntryByName(as->parms[0].items->data, &entry); + if (code1) { + fprintf(STDERR, "vos: Could not find entry for volume %s\n", + as->parms[0].items->data); + exit(1); + } + code2 = VLDB_GetEntryByName(as->parms[1].items->data, &entry); + if ((!code1) && (!code2)) { /*the newname already exists */ + fprintf(STDERR, "vos: volume %s already exists\n", + as->parms[1].items->data); + exit(1); + } + + if (code1 && code2) { + fprintf(STDERR, "vos: Could not find entry for volume %s or %s\n", + as->parms[0].items->data, as->parms[1].items->data); + exit(1); + } + if (!VolNameOK(as->parms[0].items->data)) { + fprintf(STDERR, + "Illegal volume name %s, should not end in .readonly or .backup\n", + as->parms[0].items->data); + exit(1); + } + if (!ISNAMEVALID(as->parms[1].items->data)) { + fprintf(STDERR, + "vos: the new volume name %s exceeds the size limit of %d\n", + as->parms[1].items->data, VOLSER_OLDMAXVOLNAME - 10); + exit(1); + } + if (!VolNameOK(as->parms[1].items->data)) { + fprintf(STDERR, + "Illegal volume name %s, should not end in .readonly or .backup\n", + as->parms[1].items->data); + exit(1); + } + if (IsNumeric(as->parms[1].items->data)) { + fprintf(STDERR, "Illegal volume name %s, should not be a number\n", + as->parms[1].items->data); + exit(1); + } + MapHostToNetwork(&entry); + code = + UV_RenameVolume(&entry, as->parms[0].items->data, + as->parms[1].items->data); + if (code) { + PrintDiagnostics("rename", code); + exit(1); + } + fprintf(STDOUT, "Renamed volume %s to %s\n", as->parms[0].items->data, + as->parms[1].items->data); + return 0; +} + +GetVolumeInfo(volid, server, part, voltype, rentry) + afs_int32 *server, volid, *part, *voltype; + register struct nvldbentry *rentry; +{ + afs_int32 vcode; + int i, index = -1; + + vcode = VLDB_GetEntryByID(volid, -1, rentry); + if (vcode) { + fprintf(STDERR, + "Could not fetch the entry for volume %lu from VLDB \n", + (unsigned long)volid); + PrintError("", vcode); + return (vcode); + } + MapHostToNetwork(rentry); + if (volid == rentry->volumeId[ROVOL]) { + *voltype = ROVOL; + for (i = 0; i < rentry->nServers; i++) { + if ((index == -1) && (rentry->serverFlags[i] & ITSROVOL) + && !(rentry->serverFlags[i] & RO_DONTUSE)) + index = i; + } + if (index == -1) { + fprintf(STDERR, + "RO volume is not found in VLDB entry for volume %lu\n", + (unsigned long)volid); + return -1; + } + + *server = rentry->serverNumber[index]; + *part = rentry->serverPartition[index]; + return 0; + } + + index = Lp_GetRwIndex(rentry); + if (index == -1) { + fprintf(STDERR, + "RW Volume is not found in VLDB entry for volume %lu\n", + (unsigned long)volid); + return -1; + } + if (volid == rentry->volumeId[RWVOL]) { + *voltype = RWVOL; + *server = rentry->serverNumber[index]; + *part = rentry->serverPartition[index]; + return 0; + } + if (volid == rentry->volumeId[BACKVOL]) { + *voltype = BACKVOL; + *server = rentry->serverNumber[index]; + *part = rentry->serverPartition[index]; + return 0; + } + fprintf(STDERR, + "unexpected volume type for volume %lu\n", + (unsigned long)volid); + return -1; +} + +static +DeleteEntry(as) + register struct cmd_syndesc *as; + +{ + afs_int32 apart; + afs_int32 avolid; + afs_int32 vcode; + struct VldbListByAttributes attributes; + nbulkentries arrayEntries; + register struct nvldbentry *vllist; + struct cmd_item *itp; + afs_int32 nentries; + int j; + char prefix[VOLSER_MAXVOLNAME + 1]; + int seenprefix = 0; + afs_int32 totalBack = 0, totalFail = 0, err; + + if (as->parms[0].items) { /* -id */ + if (as->parms[1].items || as->parms[2].items || as->parms[3].items) { + fprintf(STDERR, + "You cannot use -server, -partition, or -prefix with the -id argument\n"); + exit(-2); + } + for (itp = as->parms[0].items; itp; itp = itp->next) { + avolid = vsu_GetVolumeID(itp->data, cstruct, &err); + if (avolid == 0) { + if (err) + PrintError("", err); + else + fprintf(STDERR, "vos: can't find volume '%s'\n", + itp->data); + continue; + } + if (as->parms[4].items) { /* -noexecute */ + fprintf(STDOUT, "Would have deleted VLDB entry for %s \n", + itp->data); + fflush(STDOUT); + continue; + } + vcode = ubik_Call(VL_DeleteEntry, cstruct, 0, avolid, RWVOL); + if (vcode) { + fprintf(STDERR, "Could not delete entry for volume %s\n", + itp->data); + fprintf(STDERR, + "You must specify a RW volume name or ID " + "(the entire VLDB entry will be deleted)\n"); + PrintError("", vcode); + totalFail++; + continue; + } + totalBack++; + } + fprintf(STDOUT, "Deleted %d VLDB entries\n", totalBack); + return (totalFail); + } + + if (!as->parms[1].items && !as->parms[2].items && !as->parms[3].items) { + fprintf(STDERR, "You must specify an option\n"); + exit(-2); + } + + /* Zero out search attributes */ + memset(&attributes, 0, sizeof(struct VldbListByAttributes)); + + if (as->parms[1].items) { /* -prefix */ + strncpy(prefix, as->parms[1].items->data, VOLSER_MAXVOLNAME); + seenprefix = 1; + if (!as->parms[2].items && !as->parms[3].items) { /* a single entry only */ + fprintf(STDERR, + "You must provide -server with the -prefix argument\n"); + exit(-2); + } + } + + if (as->parms[2].items) { /* -server */ + afs_int32 aserver; + aserver = GetServer(as->parms[2].items->data); + if (aserver == 0) { + fprintf(STDERR, "vos: server '%s' not found in host table\n", + as->parms[2].items->data); + exit(-1); + } + attributes.server = ntohl(aserver); + attributes.Mask |= VLLIST_SERVER; + } + + if (as->parms[3].items) { /* -partition */ + if (!as->parms[2].items) { + fprintf(STDERR, + "You must provide -server with the -partition argument\n"); + exit(-2); + } + apart = volutil_GetPartitionID(as->parms[3].items->data); + if (apart < 0) { + fprintf(STDERR, "vos: could not interpret partition name '%s'\n", + as->parms[3].items->data); + exit(-1); + } + attributes.partition = apart; + attributes.Mask |= VLLIST_PARTITION; + } + + /* Print status line of what we are doing */ + fprintf(STDOUT, "Deleting VLDB entries for "); + if (as->parms[2].items) { + fprintf(STDOUT, "server %s ", as->parms[2].items->data); + } + if (as->parms[3].items) { + char pname[10]; + MapPartIdIntoName(apart, pname); + fprintf(STDOUT, "partition %s ", pname); + } + if (seenprefix) { + fprintf(STDOUT, "which are prefixed with %s ", prefix); + } + fprintf(STDOUT, "\n"); + fflush(STDOUT); + + /* Get all the VLDB entries on a server and/or partition */ + memset(&arrayEntries, 0, sizeof(arrayEntries)); + vcode = VLDB_ListAttributes(&attributes, &nentries, &arrayEntries); + if (vcode) { + fprintf(STDERR, "Could not access the VLDB for attributes\n"); + PrintError("", vcode); + exit(-1); + } + + /* Process each entry */ + for (j = 0; j < nentries; j++) { + vllist = &arrayEntries.nbulkentries_val[j]; + if (seenprefix) { + /* It only deletes the RW volumes */ + if (strncmp(vllist->name, prefix, strlen(prefix))) { + if (verbose) { + fprintf(STDOUT, + "Omitting to delete %s due to prefix %s mismatch\n", + vllist->name, prefix); + } + fflush(STDOUT); + continue; + } + } + + if (as->parms[4].items) { /* -noexecute */ + fprintf(STDOUT, "Would have deleted VLDB entry for %s \n", + vllist->name); + fflush(STDOUT); + continue; + } + + /* Only matches the RW volume name */ + avolid = vllist->volumeId[RWVOL]; + vcode = ubik_Call(VL_DeleteEntry, cstruct, 0, avolid, RWVOL); + if (vcode) { + fprintf(STDOUT, "Could not delete VDLB entry for %s\n", + vllist->name); + totalFail++; + PrintError("", vcode); + continue; + } else { + totalBack++; + if (verbose) + fprintf(STDOUT, "Deleted VLDB entry for %s \n", vllist->name); + } + fflush(STDOUT); + } /*for */ + + fprintf(STDOUT, "----------------------\n"); + fprintf(STDOUT, + "Total VLDB entries deleted: %lu; failed to delete: %lu\n", + (unsigned long)totalBack, (unsigned long)totalFail); + if (arrayEntries.nbulkentries_val) + free(arrayEntries.nbulkentries_val); + return 0; +} + + +static int +CompareVldbEntryByName(p1, p2) + char *p1, *p2; +{ + struct nvldbentry *arg1, *arg2; + + arg1 = (struct nvldbentry *)p1; + arg2 = (struct nvldbentry *)p2; + return (strcmp(arg1->name, arg2->name)); +} + +/* +static int CompareVldbEntry(p1,p2) +char *p1,*p2; +{ + struct nvldbentry *arg1,*arg2; + int i; + int pos1, pos2; + char comp1[100],comp2[100]; + char temp1[20],temp2[20]; + + arg1 = (struct nvldbentry *)p1; + arg2 = (struct nvldbentry *)p2; + pos1 = -1; + pos2 = -1; + + for(i = 0; i < arg1->nServers; i++) + if(arg1->serverFlags[i] & ITSRWVOL) pos1 = i; + for(i = 0; i < arg2->nServers; i++) + if(arg2->serverFlags[i] & ITSRWVOL) pos2 = i; + if(pos1 == -1 || pos2 == -1){ + pos1 = 0; + pos2 = 0; + } + sprintf(comp1,"%10u",arg1->serverNumber[pos1]); + sprintf(comp2,"%10u",arg2->serverNumber[pos2]); + sprintf(temp1,"%10u",arg1->serverPartition[pos1]); + sprintf(temp2,"%10u",arg2->serverPartition[pos2]); + strcat(comp1,temp1); + strcat(comp2,temp2); + strcat(comp1,arg1->name); + strcat(comp1,arg2->name); + return(strcmp(comp1,comp2)); + +} + +*/ +static +ListVLDB(as) + struct cmd_syndesc *as; +{ + afs_int32 apart; + afs_int32 aserver, code; + afs_int32 vcode; + struct VldbListByAttributes attributes; + nbulkentries arrayEntries; + struct nvldbentry *vllist, *tarray = 0, *ttarray; + afs_int32 centries, nentries = 0, tarraysize, parraysize; + int j; + char pname[10]; + int quiet, sort, lock; + afs_int32 thisindex, nextindex; + + aserver = 0; + apart = 0; + + attributes.Mask = 0; + lock = (as->parms[3].items ? 1 : 0); /* -lock flag */ + quiet = (as->parms[4].items ? 1 : 0); /* -quit flag */ + sort = (as->parms[5].items ? 0 : 1); /* -nosort flag */ + + /* If the volume name is given, Use VolumeInfoCmd to look it up + * and not ListAttributes. + */ + if (as->parms[0].items) { + if (lock) { + fprintf(STDERR, + "vos: illegal use of '-locked' switch, need to specify server and/or partition\n"); + exit(1); + } + code = VolumeInfoCmd(as->parms[0].items->data); + if (code) { + PrintError("", code); + exit(1); + } + return 0; + } + + /* Server specified */ + if (as->parms[1].items) { + aserver = GetServer(as->parms[1].items->data); + if (aserver == 0) { + fprintf(STDERR, "vos: server '%s' not found in host table\n", + as->parms[1].items->data); + exit(1); + } + attributes.server = ntohl(aserver); + attributes.Mask |= VLLIST_SERVER; + } + + /* Partition specified */ + if (as->parms[2].items) { + apart = volutil_GetPartitionID(as->parms[2].items->data); + if (apart < 0) { + fprintf(STDERR, "vos: could not interpret partition name '%s'\n", + as->parms[2].items->data); + exit(1); + } + attributes.partition = apart; + attributes.Mask |= VLLIST_PARTITION; + } + + if (lock) { + attributes.Mask |= VLLIST_FLAG; + attributes.flag = VLOP_ALLOPERS; + } + + /* Print header information */ + if (!quiet) { + MapPartIdIntoName(apart, pname); + fprintf(STDOUT, "VLDB entries for %s %s%s%s %s\n", + (as->parms[1].items ? "server" : "all"), + (as->parms[1].items ? as->parms[1].items->data : "servers"), + (as->parms[2].items ? " partition " : ""), + (as->parms[2].items ? pname : ""), + (lock ? "which are locked:" : "")); + } + + for (thisindex = 0; (thisindex != -1); thisindex = nextindex) { + memset(&arrayEntries, 0, sizeof(arrayEntries)); + centries = 0; + nextindex = -1; + + vcode = + VLDB_ListAttributesN2(&attributes, 0, thisindex, ¢ries, + &arrayEntries, &nextindex); + if (vcode == RXGEN_OPCODE) { + /* Vlserver not running with ListAttributesN2. Fall back */ + vcode = + VLDB_ListAttributes(&attributes, ¢ries, &arrayEntries); + nextindex = -1; + } + if (vcode) { + fprintf(STDERR, "Could not access the VLDB for attributes\n"); + PrintError("", vcode); + exit(1); + } + nentries += centries; + + /* We don't sort, so just print the entries now */ + if (!sort) { + for (j = 0; j < centries; j++) { /* process each entry */ + vllist = &arrayEntries.nbulkentries_val[j]; + MapHostToNetwork(vllist); + EnumerateEntry(vllist); + + if (vllist->flags & VLOP_ALLOPERS) + fprintf(STDOUT, " Volume is currently LOCKED \n"); + } + } + + /* So we sort. First we must collect all the entries and keep + * them in memory. + */ + else if (centries > 0) { + if (!tarray) { + /* steal away the first bulk entries array */ + tarray = (struct nvldbentry *)arrayEntries.nbulkentries_val; + tarraysize = centries * sizeof(struct nvldbentry); + arrayEntries.nbulkentries_val = 0; + } else { + /* Grow the tarray to keep the extra entries */ + parraysize = (centries * sizeof(struct nvldbentry)); + ttarray = + (struct nvldbentry *)realloc(tarray, + tarraysize + parraysize); + if (!ttarray) { + fprintf(STDERR, + "Could not allocate enough space for the VLDB entries\n"); + goto bypass; + } + tarray = ttarray; + + /* Copy them in */ + memcpy(((char *)tarray) + tarraysize, + (char *)arrayEntries.nbulkentries_val, parraysize); + tarraysize += parraysize; + } + } + + /* Free the bulk array */ + if (arrayEntries.nbulkentries_val) { + free(arrayEntries.nbulkentries_val); + arrayEntries.nbulkentries_val = 0; + } + } + + /* Here is where we now sort all the entries and print them */ + if (sort && (nentries > 0)) { + qsort((char *)tarray, nentries, sizeof(struct nvldbentry), + CompareVldbEntryByName); + for (vllist = tarray, j = 0; j < nentries; j++, vllist++) { + MapHostToNetwork(vllist); + EnumerateEntry(vllist); + + if (vllist->flags & VLOP_ALLOPERS) + fprintf(STDOUT, " Volume is currently LOCKED \n"); + } + } + + bypass: + if (!quiet) + fprintf(STDOUT, "\nTotal entries: %lu\n", (unsigned long)nentries); + if (tarray) + free(tarray); + return 0; +} + +static +BackSys(as) + register struct cmd_syndesc *as; +{ + afs_int32 apart = 0, avolid; + afs_int32 aserver = 0, code, aserver1, apart1; + afs_int32 vcode; + struct VldbListByAttributes attributes; + nbulkentries arrayEntries; + register struct nvldbentry *vllist; + afs_int32 nentries; + int j; + char pname[10]; + int seenprefix, seenxprefix, exclude, ex, exp, noaction; + afs_int32 totalBack = 0; + afs_int32 totalFail = 0; + int previdx = -1, error, same; + int comp = 0; + struct cmd_item *ti; + char *ccode; + int match; + + memset(&attributes, 0, sizeof(struct VldbListByAttributes)); + attributes.Mask = 0; + + seenprefix = (as->parms[0].items ? 1 : 0); + exclude = (as->parms[3].items ? 1 : 0); + seenxprefix = (as->parms[4].items ? 1 : 0); + noaction = (as->parms[5].items ? 1 : 0); + + if (as->parms[1].items) { /* -server */ + aserver = GetServer(as->parms[1].items->data); + if (aserver == 0) { + fprintf(STDERR, "vos: server '%s' not found in host table\n", + as->parms[1].items->data); + exit(1); + } + attributes.server = ntohl(aserver); + attributes.Mask |= VLLIST_SERVER; + } + + if (as->parms[2].items) { /* -partition */ + apart = volutil_GetPartitionID(as->parms[2].items->data); + if (apart < 0) { + fprintf(STDERR, "vos: could not interpret partition name '%s'\n", + as->parms[2].items->data); + exit(1); + } + attributes.partition = apart; + attributes.Mask |= VLLIST_PARTITION; + } + + /* Check to make sure the prefix and xprefix expressions compile ok */ + if (seenprefix) { + for (ti = as->parms[0].items; ti; ti = ti->next) { + if (strncmp(ti->data, "^", 1) == 0) { +#ifdef HAVE_POSIX_REGEX + regex_t re; + char errbuf[256]; + + code = regcomp(&re, ti->data, REG_NOSUB); + if (code != 0) { + regerror(code, &re, errbuf, sizeof errbuf); + fprintf(STDERR, + "Unrecognizable -prefix regular expression: '%s': %s\n", + ti->data, errbuf); + exit(1); + } + regfree(&re); +#else + ccode = (char *)re_comp(ti->data); + if (ccode) { + fprintf(STDERR, + "Unrecognizable -prefix regular expression: '%s': %s\n", + ti->data, ccode); + exit(1); + } +#endif + } + } + } + if (seenxprefix) { + for (ti = as->parms[4].items; ti; ti = ti->next) { + if (strncmp(ti->data, "^", 1) == 0) { +#ifdef HAVE_POSIX_REGEX + regex_t re; + char errbuf[256]; + + code = regcomp(&re, ti->data, REG_NOSUB); + if (code != 0) { + regerror(code, &re, errbuf, sizeof errbuf); + fprintf(STDERR, + "Unrecognizable -xprefix regular expression: '%s': %s\n", + ti->data, errbuf); + exit(1); + } + regfree(&re); +#else + ccode = (char *)re_comp(ti->data); + if (ccode) { + fprintf(STDERR, + "Unrecognizable -xprefix regular expression: '%s': %s\n", + ti->data, ccode); + exit(1); + } +#endif + } + } + } + + memset(&arrayEntries, 0, sizeof(arrayEntries)); /* initialize to hint the stub to alloc space */ + vcode = VLDB_ListAttributes(&attributes, &nentries, &arrayEntries); + if (vcode) { + fprintf(STDERR, "Could not access the VLDB for attributes\n"); + PrintError("", vcode); + exit(1); + } + + if (as->parms[1].items || as->parms[2].items || verbose) { + fprintf(STDOUT, "%s up volumes", + (noaction ? "Would have backed" : "Backing")); + + if (as->parms[1].items) { + fprintf(STDOUT, " on server %s", as->parms[1].items->data); + } else if (as->parms[2].items) { + fprintf(STDOUT, " for all servers"); + } + + if (as->parms[2].items) { + MapPartIdIntoName(apart, pname); + fprintf(STDOUT, " partition %s", pname); + } + + if (seenprefix || (!seenprefix && seenxprefix)) { + ti = (seenprefix ? as->parms[0].items : as->parms[4].items); + ex = (seenprefix ? exclude : !exclude); + exp = (strncmp(ti->data, "^", 1) == 0); + fprintf(STDOUT, " which %smatch %s '%s'", (ex ? "do not " : ""), + (exp ? "expression" : "prefix"), ti->data); + for (ti = ti->next; ti; ti = ti->next) { + exp = (strncmp(ti->data, "^", 1) == 0); + printf(" %sor %s '%s'", (ex ? "n" : ""), + (exp ? "expression" : "prefix"), ti->data); + } + } + + if (seenprefix && seenxprefix) { + ti = as->parms[4].items; + exp = (strncmp(ti->data, "^", 1) == 0); + fprintf(STDOUT, " %swhich match %s '%s'", + (exclude ? "adding those " : "removing those "), + (exp ? "expression" : "prefix"), ti->data); + for (ti = ti->next; ti; ti = ti->next) { + exp = (strncmp(ti->data, "^", 1) == 0); + printf(" or %s '%s'", (exp ? "expression" : "prefix"), + ti->data); + } + } + fprintf(STDOUT, " .. "); + if (verbose) + fprintf(STDOUT, "\n"); + fflush(STDOUT); + } + + for (j = 0; j < nentries; j++) { /* process each vldb entry */ + vllist = &arrayEntries.nbulkentries_val[j]; + + if (seenprefix) { + for (ti = as->parms[0].items; ti; ti = ti->next) { + if (strncmp(ti->data, "^", 1) == 0) { +#ifdef HAVE_POSIX_REGEX + regex_t re; + char errbuf[256]; + + /* XXX -- should just do the compile once! */ + code = regcomp(&re, ti->data, REG_NOSUB); + if (code != 0) { + regerror(code, &re, errbuf, sizeof errbuf); + fprintf(STDERR, + "Error in -prefix regular expression: '%s': %s\n", + ti->data, errbuf); + exit(1); + } + match = (regexec(&re, vllist->name, 0, NULL, 0) == 0); + regfree(&re); +#else + ccode = (char *)re_comp(ti->data); + if (ccode) { + fprintf(STDERR, + "Error in -prefix regular expression: '%s': %s\n", + ti->data, ccode); + exit(1); + } + match = (re_exec(vllist->name) == 1); +#endif + } else { + match = + (strncmp(vllist->name, ti->data, strlen(ti->data)) == + 0); + } + if (match) + break; + } + } else { + match = 1; + } + + /* Without the -exclude flag: If it matches the prefix, then + * check if we want to exclude any from xprefix. + * With the -exclude flag: If it matches the prefix, then + * check if we want to add any from xprefix. + */ + if (match && seenxprefix) { + for (ti = as->parms[4].items; ti; ti = ti->next) { + if (strncmp(ti->data, "^", 1) == 0) { +#ifdef HAVE_POSIX_REGEX + regex_t re; + char errbuf[256]; + + /* XXX -- should just do the compile once! */ + code = regcomp(&re, ti->data, REG_NOSUB); + if (code != 0) { + regerror(code, &re, errbuf, sizeof errbuf); + fprintf(STDERR, + "Error in -xprefix regular expression: '%s': %s\n", + ti->data, errbuf); + exit(1); + } + if (regexec(&re, vllist->name, 0, NULL, 0) == 0) + match = 0; + regfree(&re); +#else + ccode = (char *)re_comp(ti->data); + if (ccode) { + fprintf(STDERR, + "Error in -xprefix regular expression: '%s': %s\n", + ti->data, ccode); + exit(1); + } + if (re_exec(vllist->name) == 1) { + match = 0; + break; + } +#endif + } else { + if (strncmp(vllist->name, ti->data, strlen(ti->data)) == + 0) { + match = 0; + break; + } + } + } + } + + if (exclude) + match = !match; /* -exclude will reverse the match */ + if (!match) + continue; /* Skip if no match */ + + /* Print list of volumes to backup */ + if (noaction) { + fprintf(STDOUT, " %s\n", vllist->name); + continue; + } + + if (!(vllist->flags & RW_EXISTS)) { + if (verbose) { + fprintf(STDOUT, + "Omitting to backup %s since RW volume does not exist \n", + vllist->name); + fprintf(STDOUT, "\n"); + } + fflush(STDOUT); + continue; + } + + avolid = vllist->volumeId[RWVOL]; + MapHostToNetwork(vllist); + GetServerAndPart(vllist, RWVOL, &aserver1, &apart1, &previdx); + if (aserver1 == -1 || apart1 == -1) { + fprintf(STDOUT, "could not backup %s, invalid VLDB entry\n", + vllist->name); + totalFail++; + continue; + } + if (aserver) { + same = VLDB_IsSameAddrs(aserver, aserver1, &error); + if (error) { + fprintf(STDERR, + "Failed to get info about server's %d address(es) from vlserver (err=%d); aborting call!\n", + aserver, error); + totalFail++; + continue; + } + } + if ((aserver && !same) || (apart && (apart != apart1))) { + if (verbose) { + fprintf(STDOUT, + "Omitting to backup %s since the RW is in a different location\n", + vllist->name); + } + continue; + } + if (verbose) { + time_t now = time(0); + fprintf(STDOUT, "Creating backup volume for %s on %s", + vllist->name, ctime(&now)); + fflush(STDOUT); + } + + code = UV_BackupVolume(aserver1, apart1, avolid); + if (code) { + fprintf(STDOUT, "Could not backup %s\n", vllist->name); + totalFail++; + } else { + totalBack++; + } + if (verbose) + fprintf(STDOUT, "\n"); + fflush(STDOUT); + } /* process each vldb entry */ + fprintf(STDOUT, "done\n"); + fprintf(STDOUT, "Total volumes backed up: %lu; failed to backup: %lu\n", + (unsigned long)totalBack, (unsigned long)totalFail); + fflush(STDOUT); + if (arrayEntries.nbulkentries_val) + free(arrayEntries.nbulkentries_val); + return 0; +} + +static +UnlockVLDB(as) + register struct cmd_syndesc *as; +{ + afs_int32 apart; + afs_int32 aserver, code; + afs_int32 vcode; + struct VldbListByAttributes attributes; + nbulkentries arrayEntries; + register struct nvldbentry *vllist; + afs_int32 nentries; + int j; + afs_int32 volid; + afs_int32 totalE; + char pname[10]; + + apart = -1; + totalE = 0; + attributes.Mask = 0; + + if (as->parms[0].items) { /* server specified */ + aserver = GetServer(as->parms[0].items->data); + if (aserver == 0) { + fprintf(STDERR, "vos: server '%s' not found in host table\n", + as->parms[0].items->data); + exit(1); + } + attributes.server = ntohl(aserver); + attributes.Mask |= VLLIST_SERVER; + } + if (as->parms[1].items) { /* partition specified */ + apart = volutil_GetPartitionID(as->parms[1].items->data); + if (apart < 0) { + fprintf(STDERR, "vos: could not interpret partition name '%s'\n", + as->parms[1].items->data); + exit(1); + } + if (!IsPartValid(apart, aserver, &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[1].items->data); + exit(1); + } + attributes.partition = apart; + attributes.Mask |= VLLIST_PARTITION; + } + attributes.flag = VLOP_ALLOPERS; + attributes.Mask |= VLLIST_FLAG; + memset(&arrayEntries, 0, sizeof(arrayEntries)); /*initialize to hint the stub to alloc space */ + vcode = VLDB_ListAttributes(&attributes, &nentries, &arrayEntries); + if (vcode) { + fprintf(STDERR, "Could not access the VLDB for attributes\n"); + PrintError("", vcode); + exit(1); + } + for (j = 0; j < nentries; j++) { /* process each entry */ + vllist = &arrayEntries.nbulkentries_val[j]; + volid = vllist->volumeId[RWVOL]; + vcode = + ubik_Call(VL_ReleaseLock, cstruct, 0, volid, -1, + LOCKREL_OPCODE | LOCKREL_AFSID | LOCKREL_TIMESTAMP); + if (vcode) { + fprintf(STDERR, "Could not unlock entry for volume %s\n", + vllist->name); + PrintError("", vcode); + totalE++; + } + + } + MapPartIdIntoName(apart, pname); + if (totalE) + fprintf(STDOUT, + "Could not lock %lu VLDB entries of %lu locked entries\n", + (unsigned long)totalE, (unsigned long)nentries); + else { + if (as->parms[0].items) { + fprintf(STDOUT, + "Unlocked all the VLDB entries for volumes on server %s ", + as->parms[0].items->data); + if (as->parms[1].items) { + MapPartIdIntoName(apart, pname); + fprintf(STDOUT, "partition %s\n", pname); + } else + fprintf(STDOUT, "\n"); + + } else if (as->parms[1].items) { + MapPartIdIntoName(apart, pname); + fprintf(STDOUT, + "Unlocked all the VLDB entries for volumes on partition %s on all servers\n", + pname); + } + } + + if (arrayEntries.nbulkentries_val) + free(arrayEntries.nbulkentries_val); + return 0; +} + +static +PartitionInfo(as) + register struct cmd_syndesc *as; +{ + afs_int32 apart; + afs_int32 aserver, code; + char pname[10]; + struct diskPartition partition; + struct partList dummyPartList; + int i, cnt; + + apart = -1; + aserver = GetServer(as->parms[0].items->data); + if (aserver == 0) { + fprintf(STDERR, "vos: server '%s' not found in host table\n", + as->parms[0].items->data); + exit(1); + } + if (as->parms[1].items) { + apart = volutil_GetPartitionID(as->parms[1].items->data); + if (apart < 0) { + fprintf(STDERR, "vos: could not interpret partition name '%s'\n", + as->parms[1].items->data); + exit(1); + } + dummyPartList.partId[0] = apart; + dummyPartList.partFlags[0] = PARTVALID; + cnt = 1; + } + if (apart != -1) { + if (!IsPartValid(apart, aserver, &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[1].items->data); + exit(1); + } + } else { + code = UV_ListPartitions(aserver, &dummyPartList, &cnt); + if (code) { + PrintDiagnostics("listpart", code); + exit(1); + } + } + for (i = 0; i < cnt; i++) { + if (dummyPartList.partFlags[i] & PARTVALID) { + MapPartIdIntoName(dummyPartList.partId[i], pname); + code = UV_PartitionInfo(aserver, pname, &partition); + if (code) { + fprintf(STDERR, "Could not get information on partition %s\n", + pname); + PrintError("", code); + exit(1); + } + fprintf(STDOUT, + "Free space on partition %s: %d K blocks out of total %d\n", + pname, partition.free, partition.minFree); + } + } + return 0; +} + +static +ChangeAddr(as) + register struct cmd_syndesc *as; + +{ + afs_int32 ip1, ip2, vcode; + int remove = 0; + + ip1 = GetServer(as->parms[0].items->data); + if (!ip1) { + fprintf(STDERR, "vos: invalid host address\n"); + return (EINVAL); + } + + if ((as->parms[1].items && as->parms[2].items) + || (!as->parms[1].items && !as->parms[2].items)) { + fprintf(STDERR, + "vos: Must specify either '-newaddr ' or '-remove' flag\n"); + return (EINVAL); + } + + if (as->parms[1].items) { + ip2 = GetServer(as->parms[1].items->data); + if (!ip2) { + fprintf(STDERR, "vos: invalid host address\n"); + return (EINVAL); + } + } else { + /* Play a trick here. If we are removing an address, ip1 will be -1 + * and ip2 will be the original address. This switch prevents an + * older revision vlserver from removing the IP address. + */ + remove = 1; + ip2 = ip1; + ip1 = 0xffffffff; + } + + vcode = ubik_Call_New(VL_ChangeAddr, cstruct, 0, ntohl(ip1), ntohl(ip2)); + if (vcode) { + if (remove) { + fprintf(STDERR, "Could not remove server %s from the VLDB\n", + as->parms[0].items->data); + if (vcode == VL_NOENT) { + fprintf(STDERR, + "vlserver does not support the remove flag or "); + } + } else { + fprintf(STDERR, "Could not change server %s to server %s\n", + as->parms[0].items->data, as->parms[1].items->data); + } + PrintError("", vcode); + return (vcode); + } + + if (remove) { + fprintf(STDOUT, "Removed server %s from the VLDB\n", + as->parms[0].items->data); + } else { + fprintf(STDOUT, "Changed server %s to server %s\n", + as->parms[0].items->data, as->parms[1].items->data); + } + return 0; +} + +static void +print_addrs(const bulkaddrs * addrs, const afsUUID * m_uuid, int nentries, + int print, int noresolve) +{ + afs_int32 vcode; + afs_int32 i, j; + struct VLCallBack vlcb; + afs_int32 *addrp; + bulkaddrs m_addrs; + ListAddrByAttributes m_attrs; + afs_int32 m_nentries, *m_addrp; + afs_int32 base, index; + char buf[1024]; + + if (print) { + afsUUID_to_string(m_uuid, buf, sizeof(buf)); + printf("UUID: %s\n", buf); + } + + /* print out the list of all the server */ + addrp = (afs_int32 *) addrs->bulkaddrs_val; + for (i = 0; i < nentries; i++, addrp++) { + /* If it is a multihomed address, then we will need to + * get the addresses for this multihomed server from + * the vlserver and print them. + */ + if (((*addrp & 0xff000000) == 0xff000000) && ((*addrp) & 0xffff)) { + /* Get the list of multihomed fileservers */ + base = (*addrp >> 16) & 0xff; + index = (*addrp) & 0xffff; + + if ((base >= 0) && (base <= VL_MAX_ADDREXTBLKS) && (index >= 1) + && (index <= VL_MHSRV_PERBLK)) { + m_attrs.Mask = VLADDR_INDEX; + m_attrs.index = (base * VL_MHSRV_PERBLK) + index; + m_nentries = 0; + m_addrs.bulkaddrs_val = 0; + m_addrs.bulkaddrs_len = 0; + vcode = + ubik_Call(VL_GetAddrsU, cstruct, 0, &m_attrs, &m_uuid, + &vlcb, &m_nentries, &m_addrs); + if (vcode) { + fprintf(STDERR, + "vos: could not list the multi-homed server addresses\n"); + PrintError("", vcode); + } + + /* Print the list */ + m_addrp = (afs_int32 *) m_addrs.bulkaddrs_val; + for (j = 0; j < m_nentries; j++, m_addrp++) { + *m_addrp = htonl(*m_addrp); + if (noresolve) { + char hoststr[16]; + printf("%s ", afs_inet_ntoa_r(*m_addrp, hoststr)); + } else { + printf("%s ", hostutil_GetNameByINet(*m_addrp)); + } + } + if (j == 0) { + printf("\n"); + } else { + printf("\n"); + } + + continue; + } + } + + /* Otherwise, it is a non-multihomed entry and contains + * the IP address of the server - print it. + */ + *addrp = htonl(*addrp); + if (noresolve) { + char hoststr[16]; + printf("%s\n", afs_inet_ntoa_r(*addrp, hoststr)); + } else { + printf("%s\n", hostutil_GetNameByINet(*addrp)); + } + } + + if (print) { + printf("\n"); + } + return; +} + +static +ListAddrs(as) + register struct cmd_syndesc *as; +{ + afs_int32 vcode; + 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_nentries; + + memset(&m_attrs, 0, sizeof(struct ListAddrByAttributes)); + m_attrs.Mask = VLADDR_INDEX; + + memset(&m_addrs, 0, sizeof(bulkaddrs)); + memset(&askuuid, 0, sizeof(afsUUID)); + if (as->parms[0].items) { + /* -uuid */ + afsUUID_from_string(as->parms[0].items->data, &askuuid); + m_attrs.Mask = VLADDR_UUID; + m_attrs.uuid = askuuid; + } + if (as->parms[1].items) { + /* -host */ + struct hostent *he; + afs_int32 saddr; + he = hostutil_GetHostByName((char *)as->parms[1].items->data); + if (he == NULL) { + fprintf(stderr, "Can't get host info for '%s'\n", + as->parms[1].items->data); + exit(-1); + } + memcpy(&saddr, he->h_addr, 4); + m_attrs.Mask = VLADDR_IPADDR; + m_attrs.ipaddr = ntohl(saddr); + } + if (as->parms[2].items) { + noresolve = 1; + } + if (as->parms[3].items) { + printuuid = 1; + } + + m_addrs.bulkaddrs_val = 0; + m_addrs.bulkaddrs_len = 0; + + vcode = + 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"); + PrintError("", vcode); + return (vcode); + } + + m_nentries = 0; + m_addrs.bulkaddrs_val = 0; + m_addrs.bulkaddrs_len = 0; + i = 1; + while (1) { + m_attrs.index = i; + + vcode = + ubik_Call_New(VL_GetAddrsU, cstruct, 0, &m_attrs, &m_uuid, + &vlcb, &m_nentries, &m_addrs); + if (vcode == VL_NOENT) { + i++; + nentries++; + continue; + } + + if (vcode == VL_INDEXERANGE) { + break; + } + + if (vcode) { + fprintf(STDERR, "vos: could not list the server addresses\n"); + PrintError("", vcode); + return (vcode); + } + + print_addrs(&m_addrs, &m_uuid, m_nentries, printuuid, noresolve); + i++; + + if ((as->parms[1].items) || (as->parms[0].items) || (i > nentries)) + break; + } + + return 0; +} + +static +LockEntry(as) + register struct cmd_syndesc *as; + +{ + afs_int32 avolid, vcode, err; + + avolid = vsu_GetVolumeID(as->parms[0].items->data, cstruct, &err); + if (avolid == 0) { + if (err) + PrintError("", err); + else + fprintf(STDERR, "vos: can't find volume '%s'\n", + as->parms[0].items->data); + exit(1); + } + vcode = ubik_Call(VL_SetLock, cstruct, 0, avolid, -1, VLOP_DELETE); + if (vcode) { + fprintf(STDERR, "Could not lock VLDB entry for volume %s\n", + as->parms[0].items->data); + PrintError("", vcode); + exit(1); + } + fprintf(STDOUT, "Locked VLDB entry for volume %s\n", + as->parms[0].items->data); + return 0; +} + +static +ConvertRO(as) + register struct cmd_syndesc *as; + +{ + afs_int32 partition = -1; + afs_int32 server, volid, code, i, same; + struct nvldbentry entry, storeEntry; + afs_int32 vcode; + afs_int32 rwindex; + afs_int32 rwserver = 0; + afs_int32 rwpartition; + afs_int32 roindex; + afs_int32 roserver = 0; + afs_int32 ropartition; + int force = 0; + struct rx_connection *aconn; + char c, dc; + + server = GetServer(as->parms[0].items->data); + if (!server) { + fprintf(STDERR, "vos: host '%s' not found in host table\n", + as->parms[0].items->data); + return ENOENT; + } + partition = volutil_GetPartitionID(as->parms[1].items->data); + if (partition < 0) { + fprintf(STDERR, "vos: could not interpret partition name '%s'\n", + as->parms[1].items->data); + return ENOENT; + } + if (!IsPartValid(partition, server, &code)) { + if (code) + PrintError("", code); + else + fprintf(STDERR, + "vos : partition %s does not exist on the server\n", + as->parms[1].items->data); + return ENOENT; + } + volid = vsu_GetVolumeID(as->parms[2].items->data, cstruct, &code); + if (volid == 0) { + if (code) + PrintError("", code); + else + fprintf(STDERR, "Unknown volume ID or name '%s'\n", + as->parms[0].items->data); + return -1; + } + if (as->parms[3].items) + force = 1; + + vcode = VLDB_GetEntryByID(volid, -1, &entry); + if (vcode) { + fprintf(STDERR, + "Could not fetch the entry for volume %lu from VLDB\n", + (unsigned long)volid); + PrintError("convertROtoRW", code); + return vcode; + } + + /* use RO volid even if user specified RW or BK volid */ + + if (volid != entry.volumeId[ROVOL]) + volid = entry.volumeId[ROVOL]; + + MapHostToNetwork(&entry); + for (i = 0; i < entry.nServers; i++) { + if (entry.serverFlags[i] & ITSRWVOL) { + rwindex = i; + rwserver = entry.serverNumber[i]; + rwpartition = entry.serverPartition[i]; + } + if (entry.serverFlags[i] & ITSROVOL) { + same = VLDB_IsSameAddrs(server, entry.serverNumber[i], &code); + if (code) { + fprintf(STDERR, + "Failed to get info about server's %d address(es) from vlserver (err=%d); aborting call!\n", + server, code); + return ENOENT; + } + if (same) { + roindex = i; + roserver = entry.serverNumber[i]; + ropartition = entry.serverPartition[i]; + break; + } + } + } + if (!roserver) { + fprintf(STDERR, "Warning: RO volume didn't exist in vldb!\n"); + } + if (ropartition != partition) { + fprintf(STDERR, + "Warning: RO volume should be in partition %d instead of %d (vldb)\n", + ropartition, partition); + } + + if (rwserver) { + fprintf(STDERR, + "VLDB indicates that a RW volume exists already on %s in partition %s.\n", + hostutil_GetNameByINet(rwserver), + volutil_PartitionName(rwpartition)); + if (!force) { + fprintf(STDERR, "Overwrite this VLDB entry? [y|n] (n)\n"); + dc = c = getchar(); + while (!(dc == EOF || dc == '\n')) + dc = getchar(); /* goto end of line */ + if ((c != 'y') && (c != 'Y')) { + fprintf(STDERR, "aborted.\n"); + return -1; + } + } + } + + vcode = + ubik_Call(VL_SetLock, cstruct, 0, entry.volumeId[RWVOL], RWVOL, + VLOP_MOVE); + aconn = UV_Bind(server, AFSCONF_VOLUMEPORT); + code = AFSVolConvertROtoRWvolume(aconn, partition, volid); + if (code) { + fprintf(STDERR, + "Converting RO volume %lu to RW volume failed with code %d\n", + (unsigned long)volid, code); + PrintError("convertROtoRW ", code); + return -1; + } + entry.serverFlags[roindex] = ITSRWVOL; + entry.flags |= RW_EXISTS; + entry.flags &= ~BACK_EXISTS; + if (rwserver) { + (entry.nServers)--; + if (rwindex != entry.nServers) { + entry.serverNumber[rwindex] = entry.serverNumber[entry.nServers]; + entry.serverPartition[rwindex] = + entry.serverPartition[entry.nServers]; + entry.serverFlags[rwindex] = entry.serverFlags[entry.nServers]; + entry.serverNumber[entry.nServers] = 0; + entry.serverPartition[entry.nServers] = 0; + entry.serverFlags[entry.nServers] = 0; + } + } + entry.flags &= ~RO_EXISTS; + for (i = 0; i < entry.nServers; i++) { + if (entry.serverFlags[i] & ITSROVOL) { + if (!(entry.serverFlags[i] & (RO_DONTUSE | NEW_REPSITE))) + entry.flags |= RO_EXISTS; + } + } + MapNetworkToHost(&entry, &storeEntry); + code = + VLDB_ReplaceEntry(entry.volumeId[RWVOL], RWVOL, &storeEntry, + (LOCKREL_OPCODE | LOCKREL_AFSID | + LOCKREL_TIMESTAMP)); + if (code) { + fprintf(STDERR, + "Warning: volume converted, but vldb update failed with code %d!\n", + code); + } + vcode = UV_LockRelease(entry.volumeId[RWVOL]); + if (vcode) { + PrintDiagnostics("unlock", vcode); + } + return code; +} + +static +Sizes(as) + register struct cmd_syndesc *as; +{ + afs_int32 avolid, aserver, apart, voltype, fromdate = 0, code, err, i; + struct nvldbentry entry; + volintSize vol_size; + + rx_SetRxDeadTime(60 * 10); + for (i = 0; i < MAXSERVERS; i++) { + struct rx_connection *rxConn = ubik_GetRPCConn(cstruct, i); + if (rxConn == 0) + break; + rx_SetConnDeadTime(rxConn, rx_connDeadTime); + if (rxConn->service) + rxConn->service->connDeadTime = rx_connDeadTime; + } + + avolid = vsu_GetVolumeID(as->parms[0].items->data, cstruct, &err); + if (avolid == 0) { + if (err) + PrintError("", err); + else + fprintf(STDERR, "vos: can't find volume '%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; + } + aserver = GetServer(as->parms[2].items->data); + if (aserver == 0) { + fprintf(STDERR, "Invalid server name\n"); + return -1; + } + apart = volutil_GetPartitionID(as->parms[1].items->data); + if (apart < 0) { + fprintf(STDERR, "Invalid partition name\n"); + return -1; + } + } else { + code = GetVolumeInfo(avolid, &aserver, &apart, &voltype, &entry); + if (code) + return code; + } + + fromdate = 0; + + if (as->parms[4].items && strcmp(as->parms[4].items->data, "0")) { + code = ktime_DateToInt32(as->parms[4].items->data, &fromdate); + if (code) { + fprintf(STDERR, "vos: failed to parse date '%s' (error=%d))\n", + as->parms[1].items->data, code); + return code; + } + } + + fprintf(STDOUT, "Volume: %s\n", as->parms[0].items->data); + + if (as->parms[3].items) { /* do the dump estimate */ +#ifdef AFS_64BIT_ENV + vol_size.dump_size = 0; +#else + FillInt64(vol_size.dump_size,0, 1); +#endif + code = UV_GetSize(avolid, aserver, apart, fromdate, &vol_size); + if (code) { + PrintDiagnostics("size", code); + return code; + } + /* presumably the size info is now gathered in pntr */ + /* now we display it */ + + fprintf(STDOUT, "dump_size: %llu\n", vol_size.dump_size); + } + + /* Display info */ + + return 0; +} + +PrintDiagnostics(astring, acode) + char *astring; + afs_int32 acode; +{ + if (acode == EACCES) { + fprintf(STDERR, + "You are not authorized to perform the 'vos %s' command (%d)\n", + astring, acode); + } else { + fprintf(STDERR, "Error in vos %s command.\n", astring); + PrintError("", acode); + } + return 0; +} + + +static +MyBeforeProc(as, arock) + struct cmd_syndesc *as; + char *arock; +{ + register char *tcell; + register afs_int32 code; + register afs_int32 sauth; + + /* Initialize the ubik_client connection */ + rx_SetRxDeadTime(90); + cstruct = (struct ubik_client *)0; + + sauth = 0; + tcell = NULL; + if (as->parms[12].items) /* if -cell specified */ + tcell = as->parms[12].items->data; + if (as->parms[14].items) /* -serverauth specified */ + sauth = 1; + if (as->parms[16].items) /* -crypt specified */ + vsu_SetCrypt(1); + if ((code = + vsu_ClientInit((as->parms[13].items != 0), confdir, tcell, sauth, + &cstruct, UV_SetSecurity))) { + fprintf(STDERR, "could not initialize VLDB library (code=%lu) \n", + (unsigned long)code); + exit(1); + } + rxInitDone = 1; + if (as->parms[15].items) /* -verbose flag set */ + verbose = 1; + else + verbose = 0; + return 0; +} + +int +osi_audit() +{ +/* this sucks but it works for now. +*/ + return 0; +} + +#include "AFS_component_version_number.c" + +main(argc, argv) + int argc; + char **argv; +{ + register afs_int32 code; + + register struct cmd_syndesc *ts; + +#ifdef AFS_AIX32_ENV + /* + * The following signal action for AIX is necessary so that in case of a + * crash (i.e. core is generated) we can include the user's data section + * in the core dump. Unfortunately, by default, only a partial core is + * generated which, in many cases, isn't too useful. + */ + struct sigaction nsa; + + sigemptyset(&nsa.sa_mask); + nsa.sa_handler = SIG_DFL; + nsa.sa_flags = SA_FULLDUMP; + sigaction(SIGSEGV, &nsa, NULL); +#endif + + confdir = AFSDIR_CLIENT_ETC_DIRPATH; + + cmd_SetBeforeProc(MyBeforeProc, NULL); + + ts = cmd_CreateSyntax("create", CreateVolume, 0, "create a new volume"); + cmd_AddParm(ts, "-server", CMD_SINGLE, 0, "machine name"); + cmd_AddParm(ts, "-partition", CMD_SINGLE, 0, "partition name"); + cmd_AddParm(ts, "-name", CMD_SINGLE, 0, "volume name"); + cmd_AddParm(ts, "-maxquota", CMD_SINGLE, CMD_OPTIONAL, + "initial quota (KB)"); +#ifdef notdef + cmd_AddParm(ts, "-minquota", CMD_SINGLE, CMD_OPTIONAL, ""); +#endif + COMMONPARMS; + + ts = cmd_CreateSyntax("remove", DeleteVolume, 0, "delete a volume"); + cmd_AddParm(ts, "-server", CMD_SINGLE, CMD_OPTIONAL, "machine name"); + cmd_AddParm(ts, "-partition", CMD_SINGLE, CMD_OPTIONAL, "partition name"); + cmd_AddParm(ts, "-id", CMD_SINGLE, 0, "volume name or ID"); + + COMMONPARMS; + + ts = cmd_CreateSyntax("move", MoveVolume, 0, "move a volume"); + cmd_AddParm(ts, "-id", CMD_SINGLE, 0, "volume name or ID"); + 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, "-live", CMD_FLAG, CMD_OPTIONAL, + "copy live volume without cloning"); + COMMONPARMS; + + ts = cmd_CreateSyntax("copy", CopyVolume, 0, "copy a 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, "-toname", CMD_SINGLE, 0, "volume name on destination"); + 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, "-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, + "make backup of a volume"); + 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, + "force a complete release"); + COMMONPARMS; + + ts = cmd_CreateSyntax("dump", DumpVolume, 0, "dump a volume"); + cmd_AddParm(ts, "-id", CMD_SINGLE, 0, "volume name or ID"); + cmd_AddParm(ts, "-time", CMD_SINGLE, CMD_OPTIONAL, "dump from time"); + cmd_AddParm(ts, "-file", CMD_SINGLE, CMD_OPTIONAL, "dump file"); + cmd_AddParm(ts, "-server", CMD_SINGLE, CMD_OPTIONAL, "server"); + cmd_AddParm(ts, "-partition", CMD_SINGLE, CMD_OPTIONAL, "partition"); + cmd_AddParm(ts, "-clone", CMD_FLAG, CMD_OPTIONAL, + "dump a clone of the volume"); + COMMONPARMS; + + ts = cmd_CreateSyntax("restore", RestoreVolume, 0, "restore a volume"); + cmd_AddParm(ts, "-server", CMD_SINGLE, 0, "machine name"); + cmd_AddParm(ts, "-partition", CMD_SINGLE, 0, "partition name"); + cmd_AddParm(ts, "-name", CMD_SINGLE, 0, "name of volume to be restored"); + cmd_AddParm(ts, "-file", CMD_SINGLE, CMD_OPTIONAL, "dump file"); + cmd_AddParm(ts, "-id", CMD_SINGLE, CMD_OPTIONAL, "volume ID"); + cmd_AddParm(ts, "-overwrite", CMD_SINGLE, CMD_OPTIONAL, + "abort | full | incremental"); + cmd_AddParm(ts, "-offline", CMD_FLAG, CMD_OPTIONAL, + "leave restored volume offline"); + cmd_AddParm(ts, "-readonly", CMD_FLAG, CMD_OPTIONAL, + "make restored volume read-only"); + COMMONPARMS; + + ts = cmd_CreateSyntax("unlock", LockReleaseCmd, 0, + "release lock on VLDB entry for a volume"); + cmd_AddParm(ts, "-id", CMD_SINGLE, 0, "volume name or ID"); + COMMONPARMS; + + ts = cmd_CreateSyntax("changeloc", ChangeLocation, 0, + "change an RW volume's location in the VLDB"); + cmd_AddParm(ts, "-server", CMD_SINGLE, 0, + "machine name for new location"); + cmd_AddParm(ts, "-partition", CMD_SINGLE, 0, + "partition name for new location"); + cmd_AddParm(ts, "-id", CMD_SINGLE, 0, "volume name or ID"); + COMMONPARMS; + + ts = cmd_CreateSyntax("addsite", AddSite, 0, "add a replication site"); + cmd_AddParm(ts, "-server", CMD_SINGLE, 0, "machine name for new site"); + cmd_AddParm(ts, "-partition", CMD_SINGLE, 0, + "partition name for new site"); + cmd_AddParm(ts, "-id", CMD_SINGLE, 0, "volume name or ID"); + COMMONPARMS; + + ts = cmd_CreateSyntax("remsite", RemoveSite, 0, + "remove a replication site"); + cmd_AddParm(ts, "-server", CMD_SINGLE, 0, "machine name"); + cmd_AddParm(ts, "-partition", CMD_SINGLE, 0, "partition name"); + cmd_AddParm(ts, "-id", CMD_SINGLE, 0, "volume name or ID"); + COMMONPARMS; + + ts = cmd_CreateSyntax("listpart", ListPartitions, 0, "list partitions"); + cmd_AddParm(ts, "-server", CMD_SINGLE, 0, "machine name"); + COMMONPARMS; + + ts = cmd_CreateSyntax("listvol", ListVolumes, 0, + "list volumes on server (bypass VLDB)"); + cmd_AddParm(ts, "-server", CMD_SINGLE, 0, "machine name"); + cmd_AddParm(ts, "-partition", CMD_SINGLE, CMD_OPTIONAL, "partition name"); + cmd_AddParm(ts, "-fast", CMD_FLAG, CMD_OPTIONAL, "minimal listing"); + cmd_AddParm(ts, "-long", CMD_FLAG, CMD_OPTIONAL, + "list all normal volume fields"); + cmd_AddParm(ts, "-quiet", CMD_FLAG, CMD_OPTIONAL, + "generate minimal information"); + cmd_AddParm(ts, "-extended", CMD_FLAG, CMD_OPTIONAL, + "list extended volume fields"); +#ifdef FULL_LISTVOL_SWITCH + cmd_AddParm(ts, "-format", CMD_FLAG, CMD_OPTIONAL, + "machine readable format"); +#endif /* FULL_LISTVOL_SWITCH */ + COMMONPARMS; + + ts = cmd_CreateSyntax("syncvldb", SyncVldb, 0, + "synchronize VLDB with server"); + cmd_AddParm(ts, "-server", CMD_SINGLE, CMD_OPTIONAL, "machine name"); + cmd_AddParm(ts, "-partition", CMD_SINGLE, CMD_OPTIONAL, "partition name"); + cmd_AddParm(ts, "-volume", CMD_SINGLE, CMD_OPTIONAL, "volume name or ID"); + COMMONPARMS; + + ts = cmd_CreateSyntax("syncserv", SyncServer, 0, + "synchronize server with VLDB"); + cmd_AddParm(ts, "-server", CMD_SINGLE, 0, "machine name"); + cmd_AddParm(ts, "-partition", CMD_SINGLE, CMD_OPTIONAL, "partition name"); + COMMONPARMS; + + ts = cmd_CreateSyntax("examine", ExamineVolume, 0, + "everything about the volume"); + cmd_AddParm(ts, "-id", CMD_SINGLE, 0, "volume name or ID"); + cmd_AddParm(ts, "-extended", CMD_FLAG, CMD_OPTIONAL, + "list extended volume fields"); +#ifdef FULL_LISTVOL_SWITCH + cmd_AddParm(ts, "-format", CMD_FLAG, CMD_OPTIONAL, + "machine readable format"); +#endif /* FULL_LISTVOL_SWITCH */ + COMMONPARMS; + cmd_CreateAlias(ts, "volinfo"); + + ts = cmd_CreateSyntax("setfields", SetFields, 0, + "change volume info fields"); + cmd_AddParm(ts, "-id", CMD_SINGLE, 0, "volume name or ID"); + cmd_AddParm(ts, "-maxquota", CMD_SINGLE, CMD_OPTIONAL, "quota (KB)"); + cmd_AddParm(ts, "-clearuse", CMD_FLAG, CMD_OPTIONAL, "clear dayUse"); + COMMONPARMS; + + ts = cmd_CreateSyntax("offline", volOffline, 0, (char *)CMD_HIDDEN); + cmd_AddParm(ts, "-server", CMD_SINGLE, 0, "server name"); + cmd_AddParm(ts, "-partition", CMD_SINGLE, 0, "partition name"); + cmd_AddParm(ts, "-id", CMD_SINGLE, 0, "volume name or ID"); + cmd_AddParm(ts, "-sleep", CMD_SINGLE, CMD_OPTIONAL, "seconds to sleep"); + cmd_AddParm(ts, "-busy", CMD_FLAG, CMD_OPTIONAL, "busy volume"); + COMMONPARMS; + + ts = cmd_CreateSyntax("online", volOnline, 0, (char *)CMD_HIDDEN); + cmd_AddParm(ts, "-server", CMD_SINGLE, 0, "server name"); + cmd_AddParm(ts, "-partition", CMD_SINGLE, 0, "partition name"); + cmd_AddParm(ts, "-id", CMD_SINGLE, 0, "volume name or ID"); + COMMONPARMS; + + ts = cmd_CreateSyntax("zap", VolumeZap, 0, + "delete the volume, don't bother with VLDB"); + cmd_AddParm(ts, "-server", CMD_SINGLE, 0, "machine name"); + cmd_AddParm(ts, "-partition", CMD_SINGLE, 0, "partition name"); + cmd_AddParm(ts, "-id", CMD_SINGLE, 0, "volume ID"); + cmd_AddParm(ts, "-force", CMD_FLAG, CMD_OPTIONAL, + "force deletion of bad volumes"); + cmd_AddParm(ts, "-backup", CMD_FLAG, CMD_OPTIONAL, + "also delete backup volume if one is found"); + COMMONPARMS; + + ts = cmd_CreateSyntax("status", VolserStatus, 0, + "report on volser status"); + cmd_AddParm(ts, "-server", CMD_SINGLE, 0, "machine name"); + COMMONPARMS; + + ts = cmd_CreateSyntax("rename", RenameVolume, 0, "rename a volume"); + cmd_AddParm(ts, "-oldname", CMD_SINGLE, 0, "old volume name "); + cmd_AddParm(ts, "-newname", CMD_SINGLE, 0, "new volume name "); + COMMONPARMS; + + ts = cmd_CreateSyntax("listvldb", ListVLDB, 0, + "list volumes in the VLDB"); + cmd_AddParm(ts, "-name", CMD_SINGLE, CMD_OPTIONAL, "volume name or ID"); + cmd_AddParm(ts, "-server", CMD_SINGLE, CMD_OPTIONAL, "machine name"); + cmd_AddParm(ts, "-partition", CMD_SINGLE, CMD_OPTIONAL, "partition name"); + cmd_AddParm(ts, "-locked", CMD_FLAG, CMD_OPTIONAL, "locked volumes only"); + cmd_AddParm(ts, "-quiet", CMD_FLAG, CMD_OPTIONAL, + "generate minimal information"); + cmd_AddParm(ts, "-nosort", CMD_FLAG, CMD_OPTIONAL, + "do not alphabetically sort the volume names"); + COMMONPARMS; + + ts = cmd_CreateSyntax("backupsys", BackSys, 0, "en masse backups"); + cmd_AddParm(ts, "-prefix", CMD_LIST, CMD_OPTIONAL, + "common prefix on volume(s)"); + cmd_AddParm(ts, "-server", CMD_SINGLE, CMD_OPTIONAL, "machine name"); + cmd_AddParm(ts, "-partition", CMD_SINGLE, CMD_OPTIONAL, "partition name"); + cmd_AddParm(ts, "-exclude", CMD_FLAG, CMD_OPTIONAL, + "exclude common prefix volumes"); + cmd_AddParm(ts, "-xprefix", CMD_LIST, CMD_OPTIONAL, + "negative prefix on volume(s)"); + cmd_AddParm(ts, "-dryrun", CMD_FLAG, CMD_OPTIONAL, "no action"); + COMMONPARMS; + + ts = cmd_CreateSyntax("delentry", DeleteEntry, 0, + "delete VLDB entry for a volume"); + cmd_AddParm(ts, "-id", CMD_LIST, CMD_OPTIONAL, "volume name or ID"); + cmd_AddParm(ts, "-prefix", CMD_SINGLE, CMD_OPTIONAL, + "prefix of the volume whose VLDB entry is to be deleted"); + cmd_AddParm(ts, "-server", CMD_SINGLE, CMD_OPTIONAL, "machine name"); + cmd_AddParm(ts, "-partition", CMD_SINGLE, CMD_OPTIONAL, "partition name"); + cmd_AddParm(ts, "-noexecute", CMD_FLAG, CMD_OPTIONAL | CMD_HIDE, + "no execute"); + COMMONPARMS; + + ts = cmd_CreateSyntax("partinfo", PartitionInfo, 0, + "list partition information"); + cmd_AddParm(ts, "-server", CMD_SINGLE, 0, "machine name"); + cmd_AddParm(ts, "-partition", CMD_SINGLE, CMD_OPTIONAL, "partition name"); + COMMONPARMS; + + ts = cmd_CreateSyntax("unlockvldb", UnlockVLDB, 0, + "unlock all the locked entries in the VLDB"); + cmd_AddParm(ts, "-server", CMD_SINGLE, CMD_OPTIONAL, "machine name"); + cmd_AddParm(ts, "-partition", CMD_SINGLE, CMD_OPTIONAL, "partition name"); + COMMONPARMS; + + ts = cmd_CreateSyntax("lock", LockEntry, 0, + "lock VLDB entry for a volume"); + cmd_AddParm(ts, "-id", CMD_SINGLE, 0, "volume name or ID"); + COMMONPARMS; + + ts = cmd_CreateSyntax("changeaddr", ChangeAddr, 0, + "change the IP address of a file server"); + cmd_AddParm(ts, "-oldaddr", CMD_SINGLE, 0, "original IP address"); + cmd_AddParm(ts, "-newaddr", CMD_SINGLE, CMD_OPTIONAL, "new IP address"); + cmd_AddParm(ts, "-remove", CMD_FLAG, CMD_OPTIONAL, + "remove the IP address from the VLDB"); + COMMONPARMS; + + ts = cmd_CreateSyntax("listaddrs", ListAddrs, 0, + "list the IP address of all file servers registered in the VLDB"); + cmd_AddParm(ts, "-uuid", CMD_SINGLE, CMD_OPTIONAL, "uuid of server"); + cmd_AddParm(ts, "-host", CMD_SINGLE, CMD_OPTIONAL, "address of host"); + cmd_AddParm(ts, "-noresolve", CMD_FLAG, CMD_OPTIONAL, + "don't resolve addresses"); + cmd_AddParm(ts, "-printuuid", CMD_FLAG, CMD_OPTIONAL, + "print uuid of hosts"); + COMMONPARMS; + + ts = cmd_CreateSyntax("convertROtoRW", ConvertRO, 0, + "convert a RO volume into a RW volume (after loss of old RW volume)"); + cmd_AddParm(ts, "-server", CMD_SINGLE, 0, "machine name"); + cmd_AddParm(ts, "-partition", CMD_SINGLE, 0, "partition name"); + cmd_AddParm(ts, "-id", CMD_SINGLE, 0, "volume name or ID"); + cmd_AddParm(ts, "-force", CMD_FLAG, CMD_OPTIONAL, "don't ask"); + COMMONPARMS; + + ts = cmd_CreateSyntax("size", Sizes, 0, + "obtain various sizes of the volume."); + cmd_AddParm(ts, "-id", CMD_SINGLE, 0, "volume name or ID"); + cmd_AddParm(ts, "-partition", CMD_SINGLE, CMD_OPTIONAL, "partition name"); + cmd_AddParm(ts, "-server", CMD_SINGLE, CMD_OPTIONAL, "machine name"); + cmd_AddParm(ts, "-dump", CMD_FLAG, CMD_OPTIONAL, + "Obtain the size of the dump"); + cmd_AddParm(ts, "-time", CMD_SINGLE, CMD_OPTIONAL, "dump from time"); + COMMONPARMS; + + code = cmd_Dispatch(argc, argv); + if (rxInitDone) { + /* Shut down the ubik_client and rx connections */ + if (cstruct) { + (void)ubik_ClientDestroy(cstruct); + cstruct = 0; + } + rx_Finalize(); + } + + exit((code ? -1 : 0)); +} diff --git a/src/volser/vos.rc b/src/volser/vos.rc new file mode 100644 index 000000000..cc39de029 --- /dev/null +++ b/src/volser/vos.rc @@ -0,0 +1,17 @@ +/* + * Copyright 2000, International Business Machines Corporation and others. + * All Rights Reserved. + * + * This software has been released under the terms of the IBM Public + * License. For details, see the LICENSE file in the top-level source + * directory or online at http://www.openafs.org/dl/license10.html + */ + +/* Define VERSIONINFO resource */ + +#define AFS_VERINFO_FILE_DESCRIPTION "AFS Volume Command" +#define AFS_VERINFO_NAME "vos" +#define AFS_VERINFO_FILENAME "vos.exe" + +#include "AFS_component_version_number.h" +#include "..\config\NTVersioninfo.rc" diff --git a/src/volser/vsprocs.c b/src/volser/vsprocs.c new file mode 100644 index 000000000..7faec4158 --- /dev/null +++ b/src/volser/vsprocs.c @@ -0,0 +1,6880 @@ +/* + * Copyright 2000, International Business Machines Corporation and others. + * All Rights Reserved. + * + * This software has been released under the terms of the IBM Public + * License. For details, see the LICENSE file in the top-level source + * directory or online at http://www.openafs.org/dl/license10.html + */ + +#include +#include + +RCSID + ("$Header: /cvs/openafs/src/volser/vsprocs.c,v 1.30 2004/01/08 21:54:10 shadow Exp $"); + +#include +#include +#include +#ifdef AFS_AIX_ENV +#include +#endif +#ifdef AFS_NT40_ENV +#include +#include +#else +#include +#include +#endif + +#ifdef HAVE_STRING_H +#include +#else +#ifdef HAVE_STRINGS_H +#include +#endif +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "volser.h" +#include "volint.h" +#include "lockdata.h" +#include +#include +#include +#include +#include +#define ERRCODE_RANGE 8 /* from error_table.h */ +#define CLOCKSKEW 2 /* not really skew, but resolution */ + +/* for UV_MoveVolume() recovery */ + +#include /* signal(), kill(), wait(), etc. */ +#include + +#include + +struct ubik_client *cstruct; +int verbose = 0; + +struct release { + afs_int32 time; + afs_int32 vldbEntryIndex; +}; + +/* Utility macros used by rest of this source file */ +#define EPRINT(ec, es) \ +do { \ + fprintf(STDERR, "\n"); \ + fprintf(STDERR, (es)); \ + PrintError(" ",ec); \ +} while (0) + +#define EPRINT1(ec, es, ep1) \ +do { \ + fprintf(STDERR, "\n"); \ + fprintf(STDERR, (es), (ep1)); \ + PrintError(" ",ec); \ +} while (0) + +#define EPRINT2(ec, es, ep1, ep2) \ +do { \ + fprintf(STDERR, "\n"); \ + fprintf(STDERR, (es), (ep1), (ep2)); \ + PrintError(" ",ec); \ +} while (0) + +#define EPRINT3(ec, es, ep1, ep2, ep3) \ +do { \ + fprintf(STDERR, "\n"); \ + fprintf(STDERR, (es), (ep1), (ep2), (ep3)); \ + PrintError(" ",ec); \ +} while (0) + +#define EGOTO(where, ec, es) \ +do { \ + if (ec) { \ + EPRINT((ec),(es)); \ + error = (ec); \ + goto where; \ + } \ +} while (0) + +#define EGOTO1(where, ec, es, ep1) \ +do { \ + if (ec) { \ + EPRINT1((ec),(es),(ep1)); \ + error = (ec); \ + goto where; \ + } \ +} while (0) + +#define EGOTO2(where, ec, es, ep1, ep2) \ +do { \ + if (ec) { \ + EPRINT2((ec),(es),(ep1),(ep2)); \ + error = (ec); \ + goto where; \ + } \ +} while (0) + +#define EGOTO3(where, ec, es, ep1, ep2, ep3) \ +do { \ + if (ec) { \ + EPRINT3((ec),(es),(ep1),(ep2),(ep3)); \ + error = (ec); \ + goto where; \ + } \ +} while (0) + +#define VPRINT(es) \ + { if (verbose) { fprintf(STDOUT, (es)); fflush(STDOUT); } } +#define VPRINT1(es, p) \ + { if (verbose) { fprintf(STDOUT, (es), (p)); fflush(STDOUT); } } +#define VPRINT2(es, p1, p2) \ + { if (verbose) { fprintf(STDOUT, (es), (p1), (p2)); fflush(STDOUT); } } +#define VPRINT3(es, p1, p2, p3) \ + { if (verbose) { fprintf(STDOUT, (es), (p1), (p2), (p3)); fflush(STDOUT); } } +#define VDONE \ + { if (verbose) { fprintf(STDOUT, " done\n"); fflush(STDOUT); } } + + + +/* getting rid of this */ +#define ERROR_EXIT(code) {error=(code); goto error_exit;} + + +/* Protos for static routines */ +static afs_int32 CheckAndDeleteVolume(struct rx_connection *aconn, + afs_int32 apart, afs_int32 okvol, + afs_int32 delvol); +static int DelVol(struct rx_connection *conn, afs_int32 vid, afs_int32 part, + afs_int32 flags); +static int GetTrans(struct nvldbentry *vldbEntryPtr, afs_int32 index, + struct rx_connection **connPtr, afs_int32 * transPtr, + afs_int32 * timePtr); +static int SimulateForwardMultiple(struct rx_connection *fromconn, + afs_int32 fromtid, afs_int32 fromdate, + manyDests * tr, afs_int32 flags, + void *cookie, manyResults * results); +static int rel_compar(struct release *r1, struct release *r2); +static afs_int32 CheckVolume(volintInfo * volumeinfo, afs_int32 aserver, + afs_int32 apart, afs_int32 * modentry, + afs_uint32 * maxvolid); + + +/*map the partition into partition name */ +void +MapPartIdIntoName(afs_int32 partId, char *partName) +{ + if (partId < 26) { /* what if partId > = 26 ? */ + strcpy(partName, "/vicep"); + partName[6] = partId + 'a'; + partName[7] = '\0'; + return; + } else if (partId < VOLMAXPARTS) { + strcpy(partName, "/vicep"); + partId -= 26; + partName[6] = 'a' + (partId / 26); + partName[7] = 'a' + (partId % 26); + partName[8] = '\0'; + return; + } +} + +int +yesprompt(char *str) +{ + int response, c; + int code; + + fprintf(STDERR, "Do you want to %s? [yn](n): ", str); + response = c = getchar(); + while (!(c == EOF || c == '\n')) + c = getchar(); /*skip to end of line */ + code = (response == 'y' || response == 'Y'); + return code; +} + + +int +PrintError(char *msg, afs_int32 errcode) +{ + fprintf(STDERR, msg); + /*replace by a big switch statement */ + switch (errcode) { + case 0: + break; + case -1: + fprintf(STDERR, "Possible communication failure\n"); + break; + case VSALVAGE: + fprintf(STDERR, "Volume needs to be salvaged\n"); + break; + case VNOVNODE: + fprintf(STDERR, "Bad vnode number quoted\n"); + break; + case VNOVOL: + fprintf(STDERR, + "Volume not attached, does not exist, or not on line\n"); + break; + case VVOLEXISTS: + fprintf(STDERR, "Volume already exists\n"); + break; + case VNOSERVICE: + fprintf(STDERR, "Volume is not in service\n"); + break; + case VOFFLINE: + fprintf(STDERR, "Volume is off line\n"); + break; + case VONLINE: + fprintf(STDERR, "Volume is already on line\n"); + break; + case VDISKFULL: + fprintf(STDERR, "Partition is full\n"); + break; + case VOVERQUOTA: + fprintf(STDERR, "Volume max quota exceeded\n"); + break; + case VBUSY: + fprintf(STDERR, "Volume temporarily unavailable\n"); + break; + case VMOVED: + fprintf(STDERR, "Volume has moved to another server\n"); + break; + case VL_IDEXIST: + fprintf(STDERR, "VLDB: volume Id exists in the vldb\n"); + break; + case VL_IO: + fprintf(STDERR, "VLDB: a read terminated too early\n"); + break; + case VL_NAMEEXIST: + fprintf(STDERR, "VLDB: volume entry exists in the vldb\n"); + break; + case VL_CREATEFAIL: + fprintf(STDERR, "VLDB: internal creation failure\n"); + break; + case VL_NOENT: + fprintf(STDERR, "VLDB: no such entry\n"); + break; + case VL_EMPTY: + fprintf(STDERR, "VLDB: vldb database is empty\n"); + break; + case VL_ENTDELETED: + fprintf(STDERR, "VLDB: entry is deleted (soft delete)\n"); + break; + case VL_BADNAME: + fprintf(STDERR, "VLDB: volume name is illegal\n"); + break; + case VL_BADINDEX: + fprintf(STDERR, "VLDB: index was out of range\n"); + break; + case VL_BADVOLTYPE: + fprintf(STDERR, "VLDB: bad volume type\n"); + break; + case VL_BADSERVER: + fprintf(STDERR, "VLDB: illegal server number (not within limits)\n"); + break; + case VL_BADPARTITION: + fprintf(STDERR, "VLDB: bad partition number\n"); + break; + case VL_REPSFULL: + fprintf(STDERR, "VLDB: run out of space for replication sites\n"); + break; + case VL_NOREPSERVER: + fprintf(STDERR, "VLDB: no such repsite server exists\n"); + break; + case VL_DUPREPSERVER: + fprintf(STDERR, "VLDB: replication site server already exists\n"); + break; + case VL_RWNOTFOUND: + fprintf(STDERR, "VLDB: parent r/w entry not found\n"); + break; + case VL_BADREFCOUNT: + fprintf(STDERR, "VLDB: illegal reference count number\n"); + break; + case VL_SIZEEXCEEDED: + fprintf(STDERR, "VLDB: vldb size for attributes exceeded\n"); + break; + case VL_BADENTRY: + fprintf(STDERR, "VLDB: bad incoming vldb entry\n"); + break; + case VL_BADVOLIDBUMP: + fprintf(STDERR, "VLDB: illegal max volid increment\n"); + break; + case VL_IDALREADYHASHED: + fprintf(STDERR, "VLDB: (RO/BACK) Id already hashed\n"); + break; + case VL_ENTRYLOCKED: + fprintf(STDERR, "VLDB: vldb entry is already locked\n"); + break; + case VL_BADVOLOPER: + fprintf(STDERR, "VLDB: bad volume operation code\n"); + break; + case VL_BADRELLOCKTYPE: + fprintf(STDERR, "VLDB: bad release lock type\n"); + break; + case VL_RERELEASE: + fprintf(STDERR, "VLDB: status report: last release was aborted\n"); + break; + case VL_BADSERVERFLAG: + fprintf(STDERR, "VLDB: invalid replication site server flag\n"); + break; + case VL_PERM: + fprintf(STDERR, "VLDB: no permission access for call\n"); + break; + case VOLSERREAD_DUMPERROR: + fprintf(STDERR, + "VOLSER: Problems encountered in reading the dump file !\n"); + break; + case VOLSERDUMPERROR: + fprintf(STDERR, "VOLSER: Problems encountered in doing the dump !\n"); + break; + case VOLSERATTACH_ERROR: + fprintf(STDERR, "VOLSER: Could not attach the volume\n"); + break; + case VOLSERDETACH_ERROR: + fprintf(STDERR, "VOLSER: Could not detach the volume\n"); + break; + case VOLSERILLEGAL_PARTITION: + fprintf(STDERR, "VOLSER: encountered illegal partition number\n"); + break; + case VOLSERBAD_ACCESS: + fprintf(STDERR, "VOLSER: permission denied, not a super user\n"); + break; + case VOLSERVLDB_ERROR: + fprintf(STDERR, "VOLSER: error detected in the VLDB\n"); + break; + case VOLSERBADNAME: + fprintf(STDERR, "VOLSER: error in volume name\n"); + break; + case VOLSERVOLMOVED: + fprintf(STDERR, "VOLSER: volume has moved\n"); + break; + case VOLSERBADOP: + fprintf(STDERR, "VOLSER: illegal operation\n"); + break; + case VOLSERBADRELEASE: + fprintf(STDERR, "VOLSER: release could not be completed\n"); + break; + case VOLSERVOLBUSY: + fprintf(STDERR, "VOLSER: volume is busy\n"); + break; + case VOLSERNO_MEMORY: + fprintf(STDERR, "VOLSER: volume server is out of memory\n"); + break; + case VOLSERNOVOL: + fprintf(STDERR, + "VOLSER: no such volume - location specified incorrectly or volume does not exist\n"); + break; + case VOLSERMULTIRWVOL: + fprintf(STDERR, + "VOLSER: multiple RW volumes with same ID, one of which should be deleted\n"); + break; + case VOLSERFAILEDOP: + fprintf(STDERR, + "VOLSER: not all entries were successfully processed\n"); + break; + default: + { + + afs_int32 offset; + + initialize_KA_error_table(); + initialize_RXK_error_table(); + initialize_KTC_error_table(); + initialize_ACFG_error_table(); + initialize_CMD_error_table(); + initialize_VL_error_table(); + + offset = errcode & ((1 << ERRCODE_RANGE) - 1); + fprintf(STDERR, "%s: %s\n", error_table_name(errcode), + error_message(errcode)); + break; + } + } + return 0; +} + + +static struct rx_securityClass *uvclass = 0; +static int uvindex = -1; +/* called by VLDBClient_Init to set the security module to be used in the RPC */ +int +UV_SetSecurity(register struct rx_securityClass *as, afs_int32 aindex) +{ + uvindex = aindex; + uvclass = as; + return 0; +} + +/* bind to volser on */ +/* takes server address in network order, port in host order. dumb */ +struct rx_connection * +UV_Bind(afs_int32 aserver, afs_int32 port) +{ + register struct rx_connection *tc; + + tc = rx_NewConnection(aserver, htons(port), VOLSERVICE_ID, uvclass, + uvindex); + return tc; +} + +/* if is allright(indicated by beibg able to + * start a transaction, delete the */ +static afs_int32 +CheckAndDeleteVolume(struct rx_connection *aconn, afs_int32 apart, + afs_int32 okvol, afs_int32 delvol) +{ + afs_int32 error, code, tid, rcode; + + error = 0; + code = 0; + + if (okvol == 0) { + code = AFSVolTransCreate(aconn, delvol, apart, ITOffline, &tid); + if (!error && code) + error = code; + code = AFSVolDeleteVolume(aconn, tid); + if (!error && code) + error = code; + code = AFSVolEndTrans(aconn, tid, &rcode); + if (!code) + code = rcode; + if (!error && code) + error = code; + return error; + } else { + code = AFSVolTransCreate(aconn, okvol, apart, ITOffline, &tid); + if (!code) { + code = AFSVolEndTrans(aconn, tid, &rcode); + if (!code) + code = rcode; + if (!error && code) + error = code; + code = AFSVolTransCreate(aconn, delvol, apart, ITOffline, &tid); + if (!error && code) + error = code; + code = AFSVolDeleteVolume(aconn, tid); + if (!error && code) + error = code; + code = AFSVolEndTrans(aconn, tid, &rcode); + if (!code) + code = rcode; + if (!error && code) + error = code; + } else + error = code; + return error; + } +} + +/* called by EmuerateEntry, show vldb entry in a reasonable format */ +void +SubEnumerateEntry(struct nvldbentry *entry) +{ + int i; + char pname[10]; + int isMixed = 0; + +#ifdef notdef + fprintf(STDOUT, " readWriteID %-10u ", entry->volumeId[RWVOL]); + if (entry->flags & RW_EXISTS) + fprintf(STDOUT, " valid \n"); + else + fprintf(STDOUT, " invalid \n"); + fprintf(STDOUT, " readOnlyID %-10u ", entry->volumeId[ROVOL]); + if (entry->flags & RO_EXISTS) + fprintf(STDOUT, " valid \n"); + else + fprintf(STDOUT, " invalid \n"); + fprintf(STDOUT, " backUpID %-10u ", entry->volumeId[BACKVOL]); + if (entry->flags & BACK_EXISTS) + fprintf(STDOUT, " valid \n"); + else + fprintf(STDOUT, " invalid \n"); + if ((entry->cloneId != 0) && (entry->flags & RO_EXISTS)) + fprintf(STDOUT, " releaseClone %-10u \n", entry->cloneId); +#else + if (entry->flags & RW_EXISTS) + fprintf(STDOUT, " RWrite: %-10u", entry->volumeId[RWVOL]); + if (entry->flags & RO_EXISTS) + fprintf(STDOUT, " ROnly: %-10u", entry->volumeId[ROVOL]); + if (entry->flags & BACK_EXISTS) + fprintf(STDOUT, " Backup: %-10u", entry->volumeId[BACKVOL]); + if ((entry->cloneId != 0) && (entry->flags & RO_EXISTS)) + fprintf(STDOUT, " RClone: %-10lu", (unsigned long)entry->cloneId); + fprintf(STDOUT, "\n"); +#endif + fprintf(STDOUT, " number of sites -> %lu\n", + (unsigned long)entry->nServers); + for (i = 0; i < entry->nServers; i++) { + if (entry->serverFlags[i] & NEW_REPSITE) + isMixed = 1; + } + for (i = 0; i < entry->nServers; i++) { + MapPartIdIntoName(entry->serverPartition[i], pname); + fprintf(STDOUT, " server %s partition %s ", + hostutil_GetNameByINet(entry->serverNumber[i]), pname); + if (entry->serverFlags[i] & ITSRWVOL) + fprintf(STDOUT, "RW Site "); + else + fprintf(STDOUT, "RO Site "); + if (isMixed) { + if (entry->serverFlags[i] & NEW_REPSITE) + fprintf(STDOUT, " -- New release"); + else + fprintf(STDOUT, " -- Old release"); + } else { + if (entry->serverFlags[i] & RO_DONTUSE) + fprintf(STDOUT, " -- Not released"); + } + fprintf(STDOUT, "\n"); + } + + return; + +} + +/*enumerate the vldb entry corresponding to */ +void +EnumerateEntry(struct nvldbentry *entry) +{ + + fprintf(STDOUT, "\n"); + fprintf(STDOUT, "%s \n", entry->name); + SubEnumerateEntry(entry); + return; +} + +/* forcibly remove a volume. Very dangerous call */ +int +UV_NukeVolume(afs_int32 server, afs_int32 partid, afs_int32 volid) +{ + register struct rx_connection *tconn; + register afs_int32 code; + + tconn = UV_Bind(server, AFSCONF_VOLUMEPORT); + if (tconn) { + code = AFSVolNukeVolume(tconn, partid, volid); + rx_DestroyConnection(tconn); + } else + code = 0; + return code; +} + +/* like df. Return usage of on in */ +int +UV_PartitionInfo(afs_int32 server, char *pname, + struct diskPartition *partition) +{ + register struct rx_connection *aconn; + afs_int32 code; + + code = 0; + aconn = (struct rx_connection *)0; + aconn = UV_Bind(server, AFSCONF_VOLUMEPORT); + code = AFSVolPartitionInfo(aconn, pname, partition); + if (code) { + fprintf(STDERR, "Could not get information on partition %s\n", pname); + PrintError("", code); + } + if (aconn) + rx_DestroyConnection(aconn); + return code; +} + +/* old interface to create volume */ +int +UV_CreateVolume(afs_int32 aserver, afs_int32 apart, char *aname, + afs_int32 * anewid) +{ + afs_int32 code; + code = UV_CreateVolume2(aserver, apart, aname, 5000, 0, 0, 0, 0, anewid); + return code; +} + +/* create a volume, given a server, partition number, volume name --> sends +* back new vol id in */ +int +UV_CreateVolume2(afs_int32 aserver, afs_int32 apart, char *aname, + afs_int32 aquota, afs_int32 aspare1, afs_int32 aspare2, + afs_int32 aspare3, afs_int32 aspare4, afs_int32 * anewid) +{ + + register struct rx_connection *aconn; + afs_int32 tid; + register afs_int32 code; + afs_int32 error; + afs_int32 rcode, vcode; + struct nvldbentry entry, storeEntry; /*the new vldb entry */ + struct volintInfo tstatus; + + tid = 0; + aconn = (struct rx_connection *)0; + error = 0; + memset(&tstatus, 0, sizeof(struct volintInfo)); + tstatus.dayUse = -1; + tstatus.maxquota = aquota; + + aconn = UV_Bind(aserver, AFSCONF_VOLUMEPORT); + /* next the next 3 available ids from the VLDB */ + vcode = ubik_Call(VL_GetNewVolumeId, cstruct, 0, 3, anewid); + EGOTO1(cfail, vcode, "Could not get an Id for volume %s\n", aname); + + code = + AFSVolCreateVolume(aconn, apart, aname, volser_RW, 0, anewid, &tid); + EGOTO2(cfail, vcode, "Failed to create the volume %s %u \n", aname, + *anewid); + + code = AFSVolSetInfo(aconn, tid, &tstatus); + if (code) + EPRINT(code, "Could not change quota (error %d), continuing...\n"); + + code = AFSVolSetFlags(aconn, tid, 0); /* bring it online (mark it InService */ + EGOTO2(cfail, vcode, "Could not bring the volume %s %u online \n", aname, + *anewid); + + VPRINT2("Volume %s %u created and brought online\n", aname, *anewid); + + /* set up the vldb entry for this volume */ + strncpy(entry.name, aname, VOLSER_OLDMAXVOLNAME); + entry.nServers = 1; + entry.serverNumber[0] = aserver; /* this should have another + * level of indirection later */ + entry.serverPartition[0] = apart; /* this should also have + * another indirection level */ + entry.flags = RW_EXISTS; /* this records that rw volume exists */ + entry.serverFlags[0] = ITSRWVOL; /*this rep site has rw vol */ + entry.volumeId[RWVOL] = *anewid; + entry.volumeId[ROVOL] = *anewid + 1; /* rw,ro, bk id are related in the default case */ + entry.volumeId[BACKVOL] = *anewid + 2; + entry.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(&entry, &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", + aname, (unsigned long)*anewid); + /*destroy the created volume */ + VPRINT1("Deleting the newly created volume %u\n", *anewid); + AFSVolDeleteVolume(aconn, tid); + error = vcode; + goto cfail; + } + VPRINT2("Created the VLDB entry for the volume %s %u\n", aname, *anewid); + /* volume created, now terminate the transaction and release the connection */ + code = AFSVolEndTrans(aconn, tid, &rcode); /*if it crashes before this + * the volume will come online anyway when transaction timesout , so if + * vldb entry exists then the volume is guaranteed to exist too wrt create */ + tid = 0; + if (code) { + fprintf(STDERR, + "Failed to end the transaction on the volume %s %lu\n", aname, + (unsigned long)*anewid); + error = code; + goto cfail; + } + + cfail: + if (tid) { + code = AFSVolEndTrans(aconn, tid, &rcode); + if (code) + fprintf(STDERR, "WARNING: could not end transaction\n"); + } + if (aconn) + rx_DestroyConnection(aconn); + PrintError("", error); + return error; + + +} + +/* create a volume, given a server, partition number, volume name --> sends +* back new vol id in */ +int +UV_AddVLDBEntry(afs_int32 aserver, afs_int32 apart, char *aname, + afs_int32 aid) +{ + register struct rx_connection *aconn; + afs_int32 error; + afs_int32 vcode; + struct nvldbentry entry, storeEntry; /*the new vldb entry */ + + aconn = (struct rx_connection *)0; + error = 0; + + /* set up the vldb entry for this volume */ + strncpy(entry.name, aname, VOLSER_OLDMAXVOLNAME); + entry.nServers = 1; + entry.serverNumber[0] = aserver; /* this should have another + * level of indirection later */ + entry.serverPartition[0] = apart; /* this should also have + * another indirection level */ + entry.flags = RW_EXISTS; /* this records that rw volume exists */ + entry.serverFlags[0] = ITSRWVOL; /*this rep site has rw vol */ + entry.volumeId[RWVOL] = aid; +#ifdef notdef + entry.volumeId[ROVOL] = anewid + 1; /* rw,ro, bk id are related in the default case */ + entry.volumeId[BACKVOL] = *anewid + 2; +#else + entry.volumeId[ROVOL] = 0; + entry.volumeId[BACKVOL] = 0; +#endif + entry.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(&entry, &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", + aname, (unsigned long)aid); + error = vcode; + goto cfail; + } + VPRINT2("Created the VLDB entry for the volume %s %u\n", aname, aid); + + cfail: + if (aconn) + rx_DestroyConnection(aconn); + PrintError("", error); + return error; +} + +/* Delete the volume on + * the physical entry gets removed from the vldb only if the ref count + * becomes zero + */ +int +UV_DeleteVolume(afs_int32 aserver, afs_int32 apart, afs_int32 avolid) +{ + struct rx_connection *aconn = (struct rx_connection *)0; + afs_int32 ttid = 0; + afs_int32 code, rcode; + afs_int32 error = 0; + struct nvldbentry entry, storeEntry; + int islocked = 0; + afs_int32 avoltype = -1, vtype; + int notondisk = 0, notinvldb = 0; + + /* Find and read bhe VLDB entry for this volume */ + code = ubik_Call(VL_SetLock, cstruct, 0, avolid, avoltype, VLOP_DELETE); + if (code) { + if (code != VL_NOENT) { + EGOTO1(error_exit, code, + "Could not lock VLDB entry for the volume %u\n", avolid); + } + notinvldb = 1; + } else { + islocked = 1; + + code = VLDB_GetEntryByID(avolid, avoltype, &entry); + EGOTO1(error_exit, code, "Could not fetch VLDB entry for volume %u\n", + avolid); + MapHostToNetwork(&entry); + + if (verbose) + EnumerateEntry(&entry); + } + + /* Whether volume is in the VLDB or not. Delete the volume on disk */ + aconn = UV_Bind(aserver, AFSCONF_VOLUMEPORT); + code = AFSVolTransCreate(aconn, avolid, apart, ITOffline, &ttid); + if (code) { + if (code == VNOVOL) { + notondisk = 1; + } else { + EGOTO1(error_exit, code, "Transaction on volume %u failed\n", + avolid); + } + } else { + VPRINT1("Trying to delete the volume %u ...", avolid); + + code = AFSVolDeleteVolume(aconn, ttid); + EGOTO1(error_exit, code, "Could not delete the volume %u \n", avolid); + + code = AFSVolEndTrans(aconn, ttid, &rcode); + code = (code ? code : rcode); + ttid = 0; + EGOTO1(error_exit, code, + "Could not end the transaction for the volume %u \n", avolid); + VDONE; + } + + /* Now update the VLDB entry. + * But first, verify we have a VLDB entry. + * Whether volume is on disk or not. Delete the volume in VLDB. + */ + if (notinvldb) + ERROR_EXIT(0); + + if (avolid == entry.volumeId[BACKVOL]) { + /* Its a backup volume, modify the VLDB entry. Check that the + * backup volume is on the server/partition we asked to delete. + */ + if (!(entry.flags & BACK_EXISTS) || !Lp_Match(aserver, apart, &entry)) { + notinvldb = 2; /* Not on this server and partition */ + ERROR_EXIT(0); + } + + VPRINT1("Marking the backup volume %u deleted in the VLDB\n", avolid); + + entry.flags &= ~BACK_EXISTS; + vtype = BACKVOL; + } + + else if (avolid == entry.volumeId[ROVOL]) { + /* Its a read-only volume, modify the VLDB entry. Check that the + * readonly volume is on the server/partition we asked to delete. + * If flags does not have RO_EIXSTS set, then this may mean the RO + * hasn't been released (and could exist in VLDB). + */ + if (!Lp_ROMatch(aserver, apart, &entry)) { + notinvldb = 2; /* Not found on this server and partition */ + ERROR_EXIT(0); + } + + if (verbose) + fprintf(STDOUT, + "Marking the readonly volume %lu deleted in the VLDB\n", + (unsigned long)avolid); + + Lp_SetROValue(&entry, aserver, apart, 0, 0); /* delete the site */ + entry.nServers--; + if (!Lp_ROMatch(0, 0, &entry)) + entry.flags &= ~RO_EXISTS; /* This was the last ro volume */ + vtype = ROVOL; + } + + else if (avolid == entry.volumeId[RWVOL]) { + /* It's a rw volume, delete the backup volume, modify the VLDB entry. + * Check that the readwrite volumes is on the server/partition we + * asked to delete. + */ + if (!(entry.flags & RW_EXISTS) || !Lp_Match(aserver, apart, &entry)) { + notinvldb = 2; /* Not found on this server and partition */ + ERROR_EXIT(0); + } + + /* Delete backup if it exists */ + code = + AFSVolTransCreate(aconn, entry.volumeId[BACKVOL], apart, + ITOffline, &ttid); + if (!code) { + if (verbose) { + fprintf(STDOUT, "Trying to delete the backup volume %u ...", + entry.volumeId[BACKVOL]); + fflush(STDOUT); + } + code = AFSVolDeleteVolume(aconn, ttid); + EGOTO1(error_exit, code, "Could not delete the volume %u \n", + entry.volumeId[BACKVOL]); + + code = AFSVolEndTrans(aconn, ttid, &rcode); + ttid = 0; + code = (code ? code : rcode); + EGOTO1(error_exit, code, + "Could not end the transaction for the volume %u \n", + entry.volumeId[BACKVOL]); + if (verbose) + fprintf(STDOUT, " done\n"); + } + + if (verbose) + fprintf(STDOUT, + "Marking the readwrite volume %lu%s deleted in the VLDB\n", + (unsigned long)avolid, + ((entry. + flags & BACK_EXISTS) ? ", and its backup volume," : + "")); + + Lp_SetRWValue(&entry, aserver, apart, 0L, 0L); + entry.nServers--; + entry.flags &= ~(BACK_EXISTS | RW_EXISTS); + vtype = RWVOL; + + if (entry.flags & RO_EXISTS) + fprintf(STDERR, "WARNING: ReadOnly copy(s) may still exist\n"); + } + + else { + notinvldb = 2; /* Not found on this server and partition */ + ERROR_EXIT(0); + } + + /* Either delete or replace the VLDB entry */ + if ((entry.nServers <= 0) || !(entry.flags & (RO_EXISTS | RW_EXISTS))) { + if (verbose) + fprintf(STDOUT, + "Last reference to the VLDB entry for %lu - deleting entry\n", + (unsigned long)avolid); + code = ubik_Call(VL_DeleteEntry, cstruct, 0, avolid, vtype); + EGOTO1(error_exit, code, + "Could not delete the VLDB entry for the volume %u \n", + avolid); + } else { + MapNetworkToHost(&entry, &storeEntry); + code = + VLDB_ReplaceEntry(avolid, vtype, &storeEntry, + (LOCKREL_OPCODE | LOCKREL_AFSID | + LOCKREL_TIMESTAMP)); + EGOTO1(error_exit, code, + "Could not update the VLDB entry for the volume %u \n", + avolid); + } + islocked = 0; + + error_exit: + if (error) + EPRINT(error, "\n"); + + if (notondisk && notinvldb) { + EPRINT2(VOLSERNOVOL, "Volume %u does not exist %s\n", avolid, + ((notinvldb == 2) ? "on server and partition" : "")); + if (!error) + error = VOLSERNOVOL; + } else if (notondisk) { + fprintf(STDERR, + "WARNING: Volume %lu did not exist on the partition\n", + (unsigned long)avolid); + } else if (notinvldb) { + fprintf(STDERR, "WARNING: Volume %lu does not exist in VLDB %s\n", + (unsigned long)avolid, + ((notinvldb == 2) ? "on server and partition" : "")); + } + + if (ttid) { + code = AFSVolEndTrans(aconn, ttid, &rcode); + code = (code ? code : rcode); + if (code) { + fprintf(STDERR, "Could not end transaction on the volume %lu\n", + (unsigned long)avolid); + PrintError("", code); + if (!error) + error = code; + } + } + + if (islocked) { + code = + ubik_Call(VL_ReleaseLock, cstruct, 0, avolid, -1, + (LOCKREL_OPCODE | LOCKREL_AFSID | LOCKREL_TIMESTAMP)); + if (code) { + EPRINT1(code, + "Could not release the lock on the VLDB entry for the volume %u \n", + avolid); + if (!error) + error = code; + } + } + + if (aconn) + rx_DestroyConnection(aconn); + return error; +} + +/* add recovery to UV_MoveVolume */ + +#define TESTC 0 /* set to test recovery code, clear for production */ + +jmp_buf env; +int interrupt = 0; + +void +sigint_handler(int x) +{ + if (interrupt) + longjmp(env, 0); + + fprintf(STDOUT, "\nSIGINT handler: vos move operation in progress\n"); + fprintf(STDOUT, + "WARNING: may leave AFS storage and metadata in indeterminate state\n"); + fprintf(STDOUT, "enter second control-c to exit\n"); + fflush(STDOUT); + + interrupt = 1; + (void)signal(SIGINT, sigint_handler); + + return; +} + +/* Move volume on to + * . The operation is almost idempotent. The following + * flags are recognized: + * + * RV_NOCLONE - don't use a copy clone + */ + +int +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; + char vname[64]; + char *volName = 0; + char tmpName[VOLSER_MAXVOLNAME + 1]; + afs_int32 rcode; + afs_int32 fromDate; + struct restoreCookie cookie; + register afs_int32 vcode, code; + afs_int32 newVol, volid, backupId; + struct volser_status tstatus; + struct destServer destination; + + struct nvldbentry entry, storeEntry; + int i, islocked, pntg; + afs_int32 error; + char in, lf; /* for test code */ + int same; + +#ifdef ENABLE_BUGFIX_1165 + volEntries volumeInfo; + struct volintInfo *infop = 0; +#endif + + islocked = 0; + fromconn = (struct rx_connection *)0; + toconn = (struct rx_connection *)0; + fromtid = 0; + totid = 0; + clonetid = 0; + error = 0; + volid = 0; + pntg = 0; + backupId = 0; + newVol = 0; + + /* support control-c processing */ + if (setjmp(env)) + goto mfail; + (void)signal(SIGINT, sigint_handler); + + if (TESTC) { + fprintf(STDOUT, + "\nThere are three tests points - verifies all code paths through recovery.\n"); + fprintf(STDOUT, "First test point - operation not started.\n"); + fprintf(STDOUT, "...test here (y, n)? "); + fflush(STDOUT); + fscanf(stdin, "%c", &in); + fscanf(stdin, "%c", &lf); /* toss away */ + if (in == 'y') { + fprintf(STDOUT, "type control-c\n"); + while (1) { + fprintf(stdout, "."); + fflush(stdout); + sleep(1); + } + } + /* or drop through */ + } + + vcode = VLDB_GetEntryByID(afromvol, -1, &entry); + EGOTO1(mfail, vcode, + "Could not fetch the entry for the volume %u from the VLDB \n", + afromvol); + + if (entry.volumeId[RWVOL] != afromvol) { + fprintf(STDERR, "Only RW volume can be moved\n"); + exit(1); + } + + vcode = ubik_Call(VL_SetLock, cstruct, 0, afromvol, RWVOL, VLOP_MOVE); + EGOTO1(mfail, vcode, "Could not lock entry for volume %u \n", afromvol); + islocked = 1; + + vcode = VLDB_GetEntryByID(afromvol, RWVOL, &entry); + EGOTO1(mfail, vcode, + "Could not fetch the entry for the volume %u from the VLDB \n", + afromvol); + + backupId = entry.volumeId[BACKVOL]; + MapHostToNetwork(&entry); + + if (!Lp_Match(afromserver, afrompart, &entry)) { + /* the from server and partition do not exist in the vldb entry corresponding to volid */ + if (!Lp_Match(atoserver, atopart, &entry)) { + /* the to server and partition do not exist in the vldb entry corresponding to volid */ + fprintf(STDERR, "The volume %lu is not on the specified site. \n", + (unsigned long)afromvol); + fprintf(STDERR, "The current site is :"); + for (i = 0; i < entry.nServers; i++) { + if (entry.serverFlags[i] == ITSRWVOL) { + char pname[10]; + MapPartIdIntoName(entry.serverPartition[i], pname); + fprintf(STDERR, " server %s partition %s \n", + hostutil_GetNameByINet(entry.serverNumber[i]), + pname); + } + } + vcode = + ubik_Call(VL_ReleaseLock, cstruct, 0, afromvol, -1, + (LOCKREL_OPCODE | LOCKREL_AFSID | + LOCKREL_TIMESTAMP)); + EGOTO1(mfail, vcode, + " Could not release lock on the VLDB entry for the volume %u \n", + afromvol); + + return VOLSERVOLMOVED; + } + + /* delete the volume afromvol on src_server */ + /* from-info does not exist but to-info does => + * we have already done the move, but the volume + * may still be existing physically on from fileserver + */ + fromconn = UV_Bind(afromserver, AFSCONF_VOLUMEPORT); + fromtid = 0; + pntg = 1; + + code = + AFSVolTransCreate(fromconn, afromvol, afrompart, ITOffline, + &fromtid); + if (!code) { /* volume exists - delete it */ + VPRINT1("Setting flags on leftover source volume %u ...", + afromvol); + code = + AFSVolSetFlags(fromconn, fromtid, + VTDeleteOnSalvage | VTOutOfService); + EGOTO1(mfail, code, + "Failed to set flags on the leftover source volume %u\n", + afromvol); + VDONE; + + VPRINT1("Deleting leftover source volume %u ...", afromvol); + code = AFSVolDeleteVolume(fromconn, fromtid); + EGOTO1(mfail, code, + "Failed to delete the leftover source volume %u\n", + afromvol); + VDONE; + + VPRINT1("Ending transaction on leftover source volume %u ...", + afromvol); + code = AFSVolEndTrans(fromconn, fromtid, &rcode); + fromtid = 0; + if (!code) + code = rcode; + EGOTO1(mfail, code, + "Could not end the transaction for the leftover source volume %u \n", + afromvol); + VDONE; + } + + /*delete the backup volume now */ + fromtid = 0; + code = + AFSVolTransCreate(fromconn, backupId, afrompart, ITOffline, + &fromtid); + if (!code) { /* backup volume exists - delete it */ + VPRINT1("Setting flags on leftover backup volume %u ...", + backupId); + code = + AFSVolSetFlags(fromconn, fromtid, + VTDeleteOnSalvage | VTOutOfService); + EGOTO1(mfail, code, + "Failed to set flags on the backup volume %u\n", backupId); + VDONE; + + VPRINT1("Deleting leftover backup volume %u ...", backupId); + code = AFSVolDeleteVolume(fromconn, fromtid); + EGOTO1(mfail, code, + "Could not delete the leftover backup volume %u\n", + backupId); + VDONE; + + VPRINT1("Ending transaction on leftover backup volume %u ...", + backupId); + code = AFSVolEndTrans(fromconn, fromtid, &rcode); + fromtid = 0; + if (!code) + code = rcode; + EGOTO1(mfail, code, + "Could not end the transaction for the leftover backup volume %u\n", + backupId); + VDONE; + } + + fromtid = 0; + error = 0; + goto mfail; + } + + /* From-info matches the vldb info about volid, + * its ok start the move operation, the backup volume + * on the old site is deleted in the process + */ + if (afrompart == atopart) { + same = VLDB_IsSameAddrs(afromserver, atoserver, &error); + EGOTO2(mfail, error, + "Failed to get info about server's %d address(es) from vlserver (err=%d); aborting call!\n", + afromserver, error); + + if (same) { + EGOTO1(mfail, VOLSERVOLMOVED, + "Warning: Moving volume %u to its home partition ignored!\n", + afromvol); + } + } + + pntg = 1; + toconn = UV_Bind(atoserver, AFSCONF_VOLUMEPORT); /* get connections to the servers */ + fromconn = UV_Bind(afromserver, AFSCONF_VOLUMEPORT); + fromtid = totid = 0; /* initialize to uncreated */ + + /* *** + * 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; + + 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; + } + + /* 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", + afromvol); + + 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 + * ***/ + + 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 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; + + fromDate = tstatus.creationDate - CLOCKSKEW; + } else { + /* With RV_NOCLONE, just do a full copy from the source */ + fromDate = 0; + } + + +#ifdef ENABLE_BUGFIX_1165 + /* + * Get the internal volume state from the source volume. We'll use such info (i.e. dayUse) + * to copy it to the new volume (via AFSSetInfo later on) so that when we move volumes we + * don't use this information... + */ + volumeInfo.volEntries_val = (volintInfo *) 0; /*this hints the stub to allocate space */ + volumeInfo.volEntries_len = 0; + code = AFSVolListOneVolume(fromconn, afrompart, afromvol, &volumeInfo); + EGOTO1(mfail, code, + "Failed to get the volint Info of the cloned volume %u\n", + afromvol); + + infop = (volintInfo *) volumeInfo.volEntries_val; + infop->maxquota = -1; /* Else it will replace the default quota */ +#endif + + /* create a volume on the target machine */ + volid = afromvol; + code = AFSVolTransCreate(toconn, volid, atopart, ITOffline, &totid); + if (!code) { + /* 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 + * sitting idle. It will get cleaned up after 600 seconds + */ + VPRINT1("Deleting pre-existing volume %u on destination ...", volid); + code = AFSVolDeleteVolume(toconn, totid); + EGOTO1(mfail, code, + "Could not delete the pre-existing volume %u on destination\n", + volid); + VDONE; + + VPRINT1 + ("Ending transaction on pre-existing volume %u on destination ...", + volid); + code = AFSVolEndTrans(toconn, totid, &rcode); + totid = 0; + if (!code) + code = rcode; + EGOTO1(mfail, code, + "Could not end the transaction on pre-existing volume %u on destination\n", + volid); + VDONE; + } + + VPRINT1("Creating the destination volume %u ...", volid); + code = + AFSVolCreateVolume(toconn, atopart, volName, volser_RW, volid, &volid, + &totid); + EGOTO1(mfail, code, "Failed to create the destination volume %u\n", + volid); + VDONE; + + strncpy(tmpName, volName, VOLSER_OLDMAXVOLNAME); + free(volName); + volName = NULL; + + VPRINT1("Setting volume flags on destination volume %u ...", volid); + code = + AFSVolSetFlags(toconn, totid, (VTDeleteOnSalvage | VTOutOfService)); + EGOTO1(mfail, code, + "Failed to set the flags on the destination volume %u\n", volid); + VDONE; + + /*** + * Now dump the clone to the new volume + ***/ + + destination.destHost = ntohl(atoserver); + destination.destPort = AFSCONF_VOLUMEPORT; + destination.destSSID = 1; + + strncpy(cookie.name, tmpName, VOLSER_OLDMAXVOLNAME); + cookie.type = RWVOL; + cookie.parent = entry.volumeId[RWVOL]; + cookie.clone = 0; + + 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. + * ***/ + + VPRINT1("Starting transaction on source volume %u ...", afromvol); + code = AFSVolTransCreate(fromconn, afromvol, afrompart, ITBusy, &fromtid); + EGOTO1(mfail, code, + "Failed to create a transaction on the source volume %u\n", + afromvol); + VDONE; + + /* now do the incremental */ + 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); + 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 */ + VPRINT1("Setting volume flags on old source volume %u ...", afromvol); + code = AFSVolSetFlags(fromconn, fromtid, VTOutOfService); + EGOTO(mfail, code, + "Failed to set the flags to make old source volume offline\n"); + VDONE; + + VPRINT1("Setting volume flags on new source volume %u ...", afromvol); + code = AFSVolSetFlags(toconn, totid, 0); + EGOTO(mfail, code, + "Failed to set the flags to make new source volume online\n"); + VDONE; + +#ifdef ENABLE_BUGFIX_1165 + VPRINT1("Setting volume status on destination volume %u ...", volid); + code = AFSVolSetInfo(toconn, totid, infop); + EGOTO1(mfail, code, + "Failed to set volume status on the destination volume %u\n", + volid); + VDONE; +#endif + + /* put new volume online */ + VPRINT1("Ending transaction on destination volume %u ...", afromvol); + code = AFSVolEndTrans(toconn, totid, &rcode); + totid = 0; + if (!code) + code = rcode; + EGOTO1(mfail, code, + "Failed to end the transaction on the volume %u on the new site\n", + afromvol); + VDONE; + + Lp_SetRWValue(&entry, afromserver, afrompart, atoserver, atopart); + MapNetworkToHost(&entry, &storeEntry); + storeEntry.flags &= ~BACK_EXISTS; + + if (TESTC) { + fprintf(STDOUT, + "Second test point - operation in progress but not complete.\n"); + fprintf(STDOUT, "...test here (y, n)? "); + fflush(STDOUT); + fscanf(stdin, "%c", &in); + fscanf(stdin, "%c", &lf); /* toss away */ + if (in == 'y') { + fprintf(STDOUT, "type control-c\n"); + while (1) { + fprintf(stdout, "."); + fflush(stdout); + sleep(1); + } + } + /* or drop through */ + } + + VPRINT1("Releasing lock on VLDB entry for volume %u ...", afromvol); + vcode = + VLDB_ReplaceEntry(afromvol, -1, &storeEntry, + (LOCKREL_OPCODE | LOCKREL_AFSID | + LOCKREL_TIMESTAMP)); + if (vcode) { + fprintf(STDERR, + " Could not release the lock on the VLDB entry for the volume %s %lu \n", + storeEntry.name, (unsigned long)afromvol); + error = vcode; + goto mfail; + } + islocked = 0; + VDONE; + + if (TESTC) { + fprintf(STDOUT, + "Third test point - operation complete but no cleanup.\n"); + fprintf(STDOUT, "...test here (y, n)? "); + fflush(STDOUT); + fscanf(stdin, "%c", &in); + fscanf(stdin, "%c", &lf); /* toss away */ + if (in == 'y') { + fprintf(STDOUT, "type control-c\n"); + while (1) { + fprintf(stdout, "."); + fflush(stdout); + sleep(1); + } + } + /* or drop through */ + } +#ifdef notdef + /* This is tricky. File server is very stupid, and if you mark the volume + * as VTOutOfService, it may mark the *good* instance (if you're moving + * between partitions on the same machine) as out of service. Since + * we're cleaning this code up in DEcorum, we're just going to kludge around + * it for now by removing this call. */ + /* already out of service, just zap it now */ + code = + AFSVolSetFlags(fromconn, fromtid, VTDeleteOnSalvage | VTOutOfService); + if (code) { + fprintf(STDERR, + "Failed to set the flags to make the old source volume offline\n"); + goto mfail; + } +#endif + if (atoserver != afromserver) { + /* set forwarding pointer for moved volumes */ + VPRINT1("Setting forwarding pointer for volume %u ...", afromvol); + code = AFSVolSetForwarding(fromconn, fromtid, atoserver); + EGOTO1(mfail, code, + "Failed to set the forwarding pointer for the volume %u\n", + afromvol); + VDONE; + } + + VPRINT1("Deleting old volume %u on source ...", afromvol); + code = AFSVolDeleteVolume(fromconn, fromtid); /* zap original volume */ + EGOTO1(mfail, code, "Failed to delete the old volume %u on source\n", + afromvol); + VDONE; + + VPRINT1("Ending transaction on old volume %u on the source ...", + afromvol); + code = AFSVolEndTrans(fromconn, fromtid, &rcode); + fromtid = 0; + if (!code) + code = rcode; + EGOTO1(mfail, code, + "Failed to end the transaction on the old volume %u on the source\n", + afromvol); + VDONE; + + /* Delete the backup volume on the original site */ + VPRINT1("Creating transaction for backup volume %u on source ...", + backupId); + code = + AFSVolTransCreate(fromconn, backupId, afrompart, ITOffline, &fromtid); + VDONE; + if (!code) { + VPRINT1("Setting flags on backup volume %u on source ...", backupId); + code = + AFSVolSetFlags(fromconn, fromtid, + VTDeleteOnSalvage | VTOutOfService); + EGOTO1(mfail, code, + "Failed to set the flags on the backup volume %u on the source\n", + backupId); + VDONE; + + VPRINT1("Deleting the backup volume %u on the source ...", backupId); + code = AFSVolDeleteVolume(fromconn, fromtid); + EGOTO1(mfail, code, + "Failed to delete the backup volume %u on the source\n", + backupId); + VDONE; + + VPRINT1("Ending transaction on backup volume %u on source ...", + backupId); + code = AFSVolEndTrans(fromconn, fromtid, &rcode); + fromtid = 0; + if (!code) + code = rcode; + EGOTO1(mfail, code, + "Failed to end the transaction on the backup volume %u on the source\n", + backupId); + VDONE; + } else + code = 0; /* no backup volume? that's okay */ + + fromtid = 0; + 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; + + 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 */ + + if (TESTC) { + fprintf(STDOUT, "Fourth test point - operation complete.\n"); + fprintf(STDOUT, "...test here (y, n)? "); + fflush(STDOUT); + fscanf(stdin, "%c", &in); + fscanf(stdin, "%c", &lf); /* toss away */ + if (in == 'y') { + fprintf(STDOUT, "type control-c\n"); + while (1) { + fprintf(stdout, "."); + fflush(stdout); + sleep(1); + } + } + /* or drop through */ + } + + /* normal cleanup code */ + + if (entry.flags & RO_EXISTS) + fprintf(STDERR, "WARNING : readOnly copies still exist \n"); + + if (islocked) { + VPRINT1("Cleanup: Releasing VLDB lock on volume %u ...", afromvol); + vcode = + ubik_Call(VL_ReleaseLock, cstruct, 0, afromvol, -1, + (LOCKREL_OPCODE | LOCKREL_AFSID | LOCKREL_TIMESTAMP)); + if (vcode) { + VPRINT("\n"); + fprintf(STDERR, + " Could not release the lock on the VLDB entry for the volume %lu \n", + (unsigned long)afromvol); + if (!error) + error = vcode; + } + VDONE; + } + + if (fromtid) { + VPRINT1("Cleanup: Ending transaction on source volume %u ...", + afromvol); + code = AFSVolEndTrans(fromconn, fromtid, &rcode); + if (code || rcode) { + VPRINT("\n"); + fprintf(STDERR, + "Could not end transaction on the source volume %lu\n", + (unsigned long)afromvol); + if (!error) + error = (code ? code : rcode); + } + VDONE; + } + + if (clonetid) { + VPRINT1("Cleanup: Ending transaction on clone volume %u ...", newVol); + code = AFSVolEndTrans(fromconn, clonetid, &rcode); + if (code || rcode) { + VPRINT("\n"); + fprintf(STDERR, + "Could not end transaction on the source's clone volume %lu\n", + (unsigned long)newVol); + if (!error) + error = (code ? code : rcode); + } + VDONE; + } + + if (totid) { + VPRINT1("Cleanup: Ending transaction on destination volume %u ...", + afromvol); + code = AFSVolEndTrans(toconn, totid, &rcode); + if (code) { + VPRINT("\n"); + fprintf(STDERR, + "Could not end transaction on destination volume %lu\n", + (unsigned long)afromvol); + if (!error) + error = (code ? code : rcode); + } + VDONE; + } + if (volName) + free(volName); +#ifdef ENABLE_BUGFIX_1165 + if (infop) + free(infop); +#endif + if (fromconn) + rx_DestroyConnection(fromconn); + if (toconn) + rx_DestroyConnection(toconn); + PrintError("", error); + return error; + + /* come here only when the sky falls */ + mfail: + + if (pntg) { + fprintf(STDOUT, + "vos move: operation interrupted, cleanup in progress...\n"); + fprintf(STDOUT, "clear transaction contexts\n"); + fflush(STDOUT); + } + + /* unlock VLDB entry */ + if (islocked) { + VPRINT1("Recovery: Releasing VLDB lock on volume %u ...", afromvol); + ubik_Call(VL_ReleaseLock, cstruct, 0, afromvol, -1, + (LOCKREL_OPCODE | LOCKREL_AFSID | LOCKREL_TIMESTAMP)); + VDONE; + } + + if (clonetid) { + VPRINT("Recovery: Ending transaction on clone volume ..."); + AFSVolEndTrans(fromconn, clonetid, &rcode); + VDONE; + } + if (totid) { + VPRINT("Recovery: Ending transaction on destination volume ..."); + AFSVolEndTrans(toconn, totid, &rcode); + VDONE; + } + if (fromtid) { /* put it on-line */ + VPRINT("Recovery: Setting volume flags on source volume ..."); + AFSVolSetFlags(fromconn, fromtid, 0); + VDONE; + + VPRINT("Recovery: Ending transaction on source volume ..."); + AFSVolEndTrans(fromconn, fromtid, &rcode); + VDONE; + } + + VPRINT("Recovery: Accessing VLDB.\n"); + vcode = VLDB_GetEntryByID(afromvol, -1, &entry); + if (vcode) { + fprintf(STDOUT, "FATAL: VLDB access error: abort cleanup\n"); + fflush(STDOUT); + goto done; + } + MapHostToNetwork(&entry); + + /* Delete either the volume on the source location or the target location. + * If the vldb entry still points to the source location, then we know the + * volume move didn't finish so we remove the volume from the target + * location. Otherwise, we remove the volume from the source location. + */ + if (Lp_Match(afromserver, afrompart, &entry)) { /* didn't move - delete target volume */ + if (pntg) { + fprintf(STDOUT, + "move incomplete - attempt cleanup of target partition - no guarantee\n"); + fflush(STDOUT); + } + + if (volid && toconn) { + VPRINT1 + ("Recovery: Creating transaction for destination volume %u ...", + volid); + code = + AFSVolTransCreate(toconn, volid, atopart, ITOffline, &totid); + + if (!code) { + VDONE; + + VPRINT1 + ("Recovery: Setting flags on destination volume %u ...", + volid); + AFSVolSetFlags(toconn, totid, + VTDeleteOnSalvage | VTOutOfService); + VDONE; + + VPRINT1("Recovery: Deleting destination volume %u ...", + volid); + AFSVolDeleteVolume(toconn, totid); + VDONE; + + VPRINT1 + ("Recovery: Ending transaction on destination volume %u ...", + volid); + AFSVolEndTrans(toconn, totid, &rcode); + VDONE; + } else { + VPRINT1 + ("\nRecovery: Unable to start transaction on destination volume %u.\n", + afromvol); + } + } + + /* put source volume on-line */ + if (fromconn) { + VPRINT1("Recovery: Creating transaction on source volume %u ...", + afromvol); + code = + AFSVolTransCreate(fromconn, afromvol, afrompart, ITBusy, + &fromtid); + if (!code) { + VDONE; + + VPRINT1("Recovery: Setting flags on source volume %u ...", + afromvol); + AFSVolSetFlags(fromconn, fromtid, 0); + VDONE; + + VPRINT1 + ("Recovery: Ending transaction on source volume %u ...", + afromvol); + AFSVolEndTrans(fromconn, fromtid, &rcode); + VDONE; + } else { + VPRINT1 + ("\nRecovery: Unable to start transaction on source volume %u.\n", + afromvol); + } + } + } else { /* yep, move complete */ + if (pntg) { + fprintf(STDOUT, + "move complete - attempt cleanup of source partition - no guarantee\n"); + fflush(STDOUT); + } + + /* delete backup volume */ + if (fromconn) { + VPRINT1("Recovery: Creating transaction on backup volume %u ...", + backupId); + code = + AFSVolTransCreate(fromconn, backupId, afrompart, ITOffline, + &fromtid); + if (!code) { + VDONE; + + VPRINT1("Recovery: Setting flags on backup volume %u ...", + backupId); + AFSVolSetFlags(fromconn, fromtid, + VTDeleteOnSalvage | VTOutOfService); + VDONE; + + VPRINT1("Recovery: Deleting backup volume %u ...", backupId); + AFSVolDeleteVolume(fromconn, fromtid); + VDONE; + + VPRINT1 + ("Recovery: Ending transaction on backup volume %u ...", + backupId); + AFSVolEndTrans(fromconn, fromtid, &rcode); + VDONE; + } else { + VPRINT1 + ("\nRecovery: Unable to start transaction on backup volume %u.\n", + backupId); + } + + /* delete source volume */ + VPRINT1("Recovery: Creating transaction on source volume %u ...", + afromvol); + code = + AFSVolTransCreate(fromconn, afromvol, afrompart, ITBusy, + &fromtid); + if (!code) { + VDONE; + + VPRINT1("Recovery: Setting flags on backup volume %u ...", + afromvol); + AFSVolSetFlags(fromconn, fromtid, + VTDeleteOnSalvage | VTOutOfService); + VDONE; + + if (atoserver != afromserver) { + VPRINT("Recovery: Setting volume forwarding pointer ..."); + AFSVolSetForwarding(fromconn, fromtid, atoserver); + VDONE; + } + + VPRINT1("Recovery: Deleting source volume %u ...", afromvol); + AFSVolDeleteVolume(fromconn, fromtid); + VDONE; + + VPRINT1 + ("Recovery: Ending transaction on source volume %u ...", + afromvol); + AFSVolEndTrans(fromconn, fromtid, &rcode); + VDONE; + } else { + VPRINT1 + ("\nRecovery: Unable to start transaction on source volume %u.\n", + afromvol); + } + } + } + + /* common cleanup - delete local clone */ + if (newVol) { + VPRINT1("Recovery: Creating transaction on clone volume %u ...", + newVol); + code = + AFSVolTransCreate(fromconn, newVol, afrompart, ITOffline, + &clonetid); + if (!code) { + VDONE; + + VPRINT1("Recovery: Deleting clone volume %u ...", newVol); + AFSVolDeleteVolume(fromconn, clonetid); + VDONE; + + VPRINT1("Recovery: Ending transaction on clone volume %u ...", + newVol); + AFSVolEndTrans(fromconn, clonetid, &rcode); + VDONE; + } else { + VPRINT1 + ("\nRecovery: Unable to start transaction on source volume %u.\n", + afromvol); + } + } + + /* unlock VLDB entry */ + VPRINT1("Recovery: Releasing lock on VLDB entry for volume %u ...", + afromvol); + ubik_Call(VL_ReleaseLock, cstruct, 0, afromvol, -1, + (LOCKREL_OPCODE | LOCKREL_AFSID | LOCKREL_TIMESTAMP)); + VDONE; + + done: /* routine cleanup */ + if (volName) + free(volName); +#ifdef ENABLE_BUGFIX_1165 + if (infop) + free(infop); +#endif + if (fromconn) + rx_DestroyConnection(fromconn); + if (toconn) + rx_DestroyConnection(toconn); + + if (pntg) { + fprintf(STDOUT, "cleanup complete - user verify desired result\n"); + fflush(STDOUT); + } + exit(1); +} + + +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_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]; + afs_int32 rcode; + afs_int32 fromDate, cloneFromDate; + struct restoreCookie cookie; + register afs_int32 vcode, code; + afs_int32 cloneVol, newVol, volflag; + struct volser_status tstatus; + struct destServer destination; + + struct nvldbentry entry, newentry, storeEntry; + int islocked, pntg; + afs_int32 error; + int justclone = 0; + + islocked = 0; + fromconn = (struct rx_connection *)0; + toconn = (struct rx_connection *)0; + fromtid = 0; + totid = 0; + clonetid = 0; + error = 0; + pntg = 0; + newVol = 0; + + /* support control-c processing */ + if (setjmp(env)) + goto mfail; + (void)signal(SIGINT, sigint_handler); + + vcode = VLDB_GetEntryByID(afromvol, -1, &entry); + EGOTO1(mfail, vcode, + "Could not fetch the entry for the volume %u from the VLDB \n", + afromvol); + MapHostToNetwork(&entry); + + pntg = 1; + toconn = UV_Bind(atoserver, AFSCONF_VOLUMEPORT); /* get connections to the servers */ + fromconn = UV_Bind(afromserver, AFSCONF_VOLUMEPORT); + fromtid = totid = 0; /* initialize to uncreated */ + + + /* check if we can shortcut and use a local clone instead of a full copy */ + if (afromserver == atoserver && afrompart == atopart) { + justclone = 1; + } + + /* *** + * clone the read/write volume locally. + * ***/ + + 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); + 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; + } + + 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; + } + + 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; + } + + /* *** + * Create the destination volume + * ***/ + + 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("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; + + 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 + * sitting idle. It will get cleaned up after 600 seconds + */ + VPRINT1("Deleting pre-existing volume %u on destination ...", newVol); + code = AFSVolDeleteVolume(toconn, totid); + EGOTO1(mfail, code, + "Could not delete the pre-existing volume %u on destination\n", + newVol); + VDONE; + + VPRINT1 + ("Ending transaction on pre-existing volume %u on destination ...", + newVol); + code = AFSVolEndTrans(toconn, totid, &rcode); + totid = 0; + if (!code) + code = rcode; + EGOTO1(mfail, code, + "Could not end the transaction on pre-existing volume %u on destination\n", + newVol); + VDONE; + } + + VPRINT1("Creating the destination volume %u ...", newVol); + code = + 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; + + VPRINT1("Setting volume flags on destination volume %u ...", newVol); + code = + AFSVolSetFlags(toconn, totid, (VTDeleteOnSalvage | VTOutOfService)); + EGOTO1(mfail, code, + "Failed to set the flags on the destination volume %u\n", newVol); + VDONE; + +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 + ***/ + + 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); + 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; + } + + /* *** + * reattach to the main-line volume, and incrementally dump it. + * ***/ + + VPRINT1("Starting transaction on source volume %u ...", afromvol); + code = AFSVolTransCreate(fromconn, afromvol, afrompart, ITBusy, &fromtid); + EGOTO1(mfail, code, + "Failed to create a transaction on the source volume %u\n", + afromvol); + VDONE; + + /* now do the incremental */ + 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); + 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); + 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; + + /* put new volume online */ + VPRINT1("Ending transaction on destination volume %u ...", newVol); + code = AFSVolEndTrans(toconn, totid, &rcode); + totid = 0; + if (!code) + code = rcode; + EGOTO1(mfail, code, + "Failed to end the transaction on the destination volume %u\n", + newVol); + VDONE; + + VPRINT1("Ending transaction on source volume %u ...", afromvol); + 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; + + fromtid = 0; + + 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; + + /* 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; + + 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); + } + + /* normal cleanup code */ + + if (fromtid) { + VPRINT1("Cleanup: Ending transaction on source volume %u ...", + afromvol); + code = AFSVolEndTrans(fromconn, fromtid, &rcode); + if (code || rcode) { + VPRINT("\n"); + fprintf(STDERR, + "Could not end transaction on the source volume %lu\n", + (unsigned long)afromvol); + if (!error) + error = (code ? code : rcode); + } + VDONE; + } + + if (clonetid) { + VPRINT1("Cleanup: Ending transaction on clone volume %u ...", + cloneVol); + code = AFSVolEndTrans(fromconn, clonetid, &rcode); + if (code || rcode) { + VPRINT("\n"); + fprintf(STDERR, + "Could not end transaction on the source's clone volume %lu\n", + (unsigned long)cloneVol); + if (!error) + error = (code ? code : rcode); + } + VDONE; + } + + if (totid) { + VPRINT1("Cleanup: Ending transaction on destination volume %u ...", + newVol); + code = AFSVolEndTrans(toconn, totid, &rcode); + if (code) { + VPRINT("\n"); + fprintf(STDERR, + "Could not end transaction on destination volume %lu\n", + (unsigned long)newVol); + if (!error) + error = (code ? code : rcode); + } + VDONE; + } + if (fromconn) + rx_DestroyConnection(fromconn); + if (toconn) + rx_DestroyConnection(toconn); + PrintError("", error); + return error; + + /* come here only when the sky falls */ + mfail: + + if (pntg) { + fprintf(STDOUT, + "vos copy: operation interrupted, cleanup in progress...\n"); + fprintf(STDOUT, "clear transaction contexts\n"); + fflush(STDOUT); + } + + if (clonetid) { + VPRINT("Recovery: Ending transaction on clone volume ..."); + AFSVolEndTrans(fromconn, clonetid, &rcode); + VDONE; + } + if (totid) { + VPRINT("Recovery: Ending transaction on destination volume ..."); + AFSVolEndTrans(toconn, totid, &rcode); + VDONE; + } + if (fromtid) { /* put it on-line */ + VPRINT("Recovery: Ending transaction on source volume ..."); + AFSVolEndTrans(fromconn, fromtid, &rcode); + VDONE; + } + + VPRINT("Recovery: Accessing VLDB.\n"); + vcode = VLDB_GetEntryByID(afromvol, -1, &entry); + if (vcode) { + fprintf(STDOUT, "FATAL: VLDB access error: abort cleanup\n"); + fflush(STDOUT); + goto done; + } + MapHostToNetwork(&entry); + + /* common cleanup - delete local clone */ + if (cloneVol) { + VPRINT1("Recovery: Creating transaction on clone volume %u ...", + cloneVol); + code = + AFSVolTransCreate(fromconn, newVol, afrompart, ITOffline, + &clonetid); + if (!code) { + VDONE; + + VPRINT1("Recovery: Deleting clone volume %u ...", cloneVol); + AFSVolDeleteVolume(fromconn, clonetid); + VDONE; + + VPRINT1("Recovery: Ending transaction on clone volume %u ...", + cloneVol); + AFSVolEndTrans(fromconn, clonetid, &rcode); + VDONE; + } else { + VPRINT1 + ("\nRecovery: Unable to start transaction on clone volume %u.\n", + cloneVol); + } + } + + done: /* routine cleanup */ + if (fromconn) + rx_DestroyConnection(fromconn); + if (toconn) + rx_DestroyConnection(toconn); + + if (pntg) { + fprintf(STDOUT, "cleanup complete - user verify desired result\n"); + fflush(STDOUT); + } + exit(1); +} + + +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); +} + + + +/* Make a new backup of volume on and + * if one already exists, update it + */ + +int +UV_BackupVolume(afs_int32 aserver, afs_int32 apart, afs_int32 avolid) +{ + struct rx_connection *aconn = (struct rx_connection *)0; + afs_int32 ttid = 0, btid = 0; + afs_int32 backupID; + afs_int32 code = 0, rcode = 0; + char vname[VOLSER_MAXVOLNAME + 1]; + struct nvldbentry entry, storeEntry; + afs_int32 error = 0; + int vldblocked = 0, vldbmod = 0, backexists = 1; + + aconn = UV_Bind(aserver, AFSCONF_VOLUMEPORT); + + /* the calls to VLDB will succeed only if avolid is a RW volume, + * since we are following the RW hash chain for searching */ + code = VLDB_GetEntryByID(avolid, RWVOL, &entry); + if (code) { + fprintf(STDERR, + "Could not fetch the entry for the volume %lu from the VLDB \n", + (unsigned long)avolid); + error = code; + goto bfail; + } + MapHostToNetwork(&entry); + + /* These operations require the VLDB be locked since it means the VLDB + * will change or the vldb is already locked. + */ + if (!(entry.flags & BACK_EXISTS) || /* backup volume doesnt exist */ + (entry.flags & VLOP_ALLOPERS) || /* vldb lock already held */ + (entry.volumeId[BACKVOL] == INVALID_BID)) { /* no assigned backup volume id */ + + code = ubik_Call(VL_SetLock, cstruct, 0, avolid, RWVOL, VLOP_BACKUP); + if (code) { + fprintf(STDERR, + "Could not lock the VLDB entry for the volume %lu\n", + (unsigned long)avolid); + error = code; + goto bfail; + } + vldblocked = 1; + + /* Reread the vldb entry */ + code = VLDB_GetEntryByID(avolid, RWVOL, &entry); + if (code) { + fprintf(STDERR, + "Could not fetch the entry for the volume %lu from the VLDB \n", + (unsigned long)avolid); + error = code; + goto bfail; + } + MapHostToNetwork(&entry); + } + + if (!ISNAMEVALID(entry.name)) { + fprintf(STDERR, "Name of the volume %s exceeds the size limit\n", + entry.name); + error = VOLSERBADNAME; + goto bfail; + } + + backupID = entry.volumeId[BACKVOL]; + if (backupID == INVALID_BID) { + /* Get a backup volume id from the VLDB and update the vldb + * entry with it. + */ + code = ubik_Call(VL_GetNewVolumeId, cstruct, 0, 1, &backupID); + if (code) { + fprintf(STDERR, + "Could not allocate ID for the backup volume of %lu from the VLDB\n", + (unsigned long)avolid); + error = code; + goto bfail; + } + entry.volumeId[BACKVOL] = backupID; + vldbmod = 1; + } + + /* Test to see if the backup volume exists by trying to create + * a transaction on the backup volume. We've assumed the backup exists. + */ + code = AFSVolTransCreate(aconn, backupID, apart, ITOffline, &btid); + if (code) { + if (code != VNOVOL) { + fprintf(STDERR, "Could not reach the backup volume %lu\n", + (unsigned long)backupID); + 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 backup volume %lu\n", + (unsigned long)backupID); + 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 backup volume %u ...", backupID); + + code = AFSVolReClone(aconn, ttid, backupID); + if (code) { + fprintf(STDERR, "Could not re-clone backup volume %lu\n", + (unsigned long)backupID); + error = code; + goto bfail; + } + } else { + VPRINT1("Creating a new backup clone %u ...", backupID); + + strcpy(vname, entry.name); + strcat(vname, ".backup"); + + code = AFSVolClone(aconn, ttid, 0, backupVolume, vname, &backupID); + 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; + } + + /* Mork vldb as backup exists */ + if (!(entry.flags & BACK_EXISTS)) { + entry.flags |= BACK_EXISTS; + vldbmod = 1; + } + + /* Now go back to the backup volume and bring it on line */ + code = AFSVolTransCreate(aconn, backupID, apart, ITOffline, &btid); + if (code) { + fprintf(STDERR, + "Failed to start a transaction on the backup volume %lu\n", + (unsigned long)backupID); + error = code; + goto bfail; + } + + code = AFSVolSetFlags(aconn, btid, 0); + if (code) { + fprintf(STDERR, "Could not mark the backup volume %lu on line \n", + (unsigned long)backupID); + error = code; + goto bfail; + } + + code = AFSVolEndTrans(aconn, btid, &rcode); + btid = 0; + if (code || rcode) { + fprintf(STDERR, + "Failed to end the transaction on the backup volume %lu\n", + (unsigned long)backupID); + error = (code ? code : rcode); + goto bfail; + } + + VDONE; + + /* Will update the vldb below */ + + 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 the backup volume %lu\n", + (unsigned long)backupID); + if (!error) + error = (code ? code : rcode); + } + } + + /* Now update the vldb - if modified */ + if (vldblocked) { + if (vldbmod) { + MapNetworkToHost(&entry, &storeEntry); + code = + VLDB_ReplaceEntry(avolid, RWVOL, &storeEntry, + (LOCKREL_OPCODE | LOCKREL_AFSID | + LOCKREL_TIMESTAMP)); + if (code) { + fprintf(STDERR, + "Could not update the VLDB entry for the volume %lu \n", + (unsigned long)avolid); + if (!error) + error = code; + } + } else { + code = + ubik_Call(VL_ReleaseLock, cstruct, 0, avolid, RWVOL, + (LOCKREL_OPCODE | LOCKREL_AFSID | + LOCKREL_TIMESTAMP)); + if (code) { + fprintf(STDERR, + "Could not unlock the VLDB entry for the volume %lu \n", + (unsigned long)avolid); + if (!error) + error = code; + } + } + } + + if (aconn) + rx_DestroyConnection(aconn); + + PrintError("", error); + 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) +{ + afs_int32 acode, ccode, rcode, tid; + ccode = rcode = tid = 0; + + acode = AFSVolTransCreate(conn, vid, part, flags, &tid); + if (!acode) { /* It really was there */ + acode = AFSVolDeleteVolume(conn, tid); + if (acode) { + fprintf(STDERR, "Failed to delete volume %lu.\n", + (unsigned long)vid); + PrintError("", acode); + } + ccode = AFSVolEndTrans(conn, tid, &rcode); + if (!ccode) + ccode = rcode; + if (ccode) { + fprintf(STDERR, "Failed to end transaction on volume %lu.\n", + (unsigned long)vid); + PrintError("", ccode); + } + } + + return acode; +} + +#define ONERROR(ec, ep, es) if (ec) { fprintf(STDERR, (es), (ep)); error = (ec); goto rfail; } +#define ERROREXIT(ec) { error = (ec); goto rfail; } + +/* Get a "transaction" on this replica. Create the volume + * if necessary. Return the time from which a dump should + * be made (0 if it's a new volume) + */ +static int +GetTrans(struct nvldbentry *vldbEntryPtr, afs_int32 index, + struct rx_connection **connPtr, afs_int32 * transPtr, + afs_int32 * timePtr) +{ + afs_int32 volid; + struct volser_status tstatus; + int code, rcode, tcode; + + *connPtr = (struct rx_connection *)0; + *timePtr = 0; + *transPtr = 0; + + /* get connection to the replication site */ + *connPtr = UV_Bind(vldbEntryPtr->serverNumber[index], AFSCONF_VOLUMEPORT); + if (!*connPtr) + goto fail; /* server is down */ + + volid = vldbEntryPtr->volumeId[ROVOL]; + if (volid) + code = + AFSVolTransCreate(*connPtr, volid, + vldbEntryPtr->serverPartition[index], ITOffline, + transPtr); + + /* If the volume does not exist, create it */ + if (!volid || code) { + char volname[64]; + + if (volid && (code != VNOVOL)) { + PrintError("Failed to start a transaction on the RO volume.\n", + code); + goto fail; + } + + strcpy(volname, vldbEntryPtr->name); + strcat(volname, ".readonly"); + + if (verbose) { + fprintf(STDOUT, + "Creating new volume %lu on replication site %s: ", + (unsigned long)volid, + hostutil_GetNameByINet(vldbEntryPtr-> + serverNumber[index])); + fflush(STDOUT); + } + + code = + AFSVolCreateVolume(*connPtr, vldbEntryPtr->serverPartition[index], + volname, volser_RO, + vldbEntryPtr->volumeId[RWVOL], &volid, + transPtr); + if (code) { + PrintError("Failed to create the ro volume: ", code); + goto fail; + } + vldbEntryPtr->volumeId[ROVOL] = volid; + + VDONE; + + /* The following is a bit redundant, since create sets these flags by default */ + code = + AFSVolSetFlags(*connPtr, *transPtr, + VTDeleteOnSalvage | VTOutOfService); + if (code) { + PrintError("Failed to set flags on the ro volume: ", code); + goto fail; + } + } + + /* Otherwise, the transaction did succeed, so get the creation date of the + * latest RO volume on the replication site + */ + else { + VPRINT2("Updating existing ro volume %u on %s ...\n", volid, + hostutil_GetNameByINet(vldbEntryPtr->serverNumber[index])); + + code = AFSVolGetStatus(*connPtr, *transPtr, &tstatus); + if (code) { + PrintError("Failed to get status of volume on destination: ", + code); + goto fail; + } + *timePtr = tstatus.creationDate - CLOCKSKEW; + } + + return 0; + + fail: + if (*transPtr) { + tcode = AFSVolEndTrans(*connPtr, *transPtr, &rcode); + *transPtr = 0; + if (!tcode) + tcode = rcode; + if (tcode) + PrintError("Could not end transaction on a ro volume: ", tcode); + } + + return code; +} + +static int +SimulateForwardMultiple(struct rx_connection *fromconn, afs_int32 fromtid, + afs_int32 fromdate, manyDests * tr, afs_int32 flags, + void *cookie, manyResults * results) +{ + int i; + + for (i = 0; i < tr->manyDests_len; i++) { + results->manyResults_val[i] = + AFSVolForward(fromconn, fromtid, fromdate, + &(tr->manyDests_val[i].server), + tr->manyDests_val[i].trans, cookie); + } + return 0; +} + + +static int +rel_compar(struct release *r1, struct release *r2) +{ + return (r1->time - r2->time); +} + +/* UV_ReleaseVolume() + * Release volume on to all + * its RO sites (full release). Unless the previous release was + * incomplete: in which case we bring the remaining incomplete + * volumes up to date with the volumes that were released + * successfully. + * forceflag: Performs a full release. + * + * Will create a clone from the RW, then dump the clone out to + * the remaining replicas. If there is more than 1 RO sites, + * ensure that the VLDB says at least one RO is available all + * the time: Influences when we write back the VLDB entry. + */ + +int +UV_ReleaseVolume(afs_int32 afromvol, afs_int32 afromserver, + afs_int32 afrompart, int forceflag) +{ + char vname[64]; + afs_int32 code, vcode, rcode, tcode; + afs_int32 cloneVolId, roVolId; + struct replica *replicas = 0; + struct nvldbentry entry, storeEntry; + int i, volcount, m, fullrelease, vldbindex; + int failure; + struct restoreCookie cookie; + struct rx_connection **toconns = 0; + struct release *times = 0; + int nservers = 0; + struct rx_connection *fromconn = (struct rx_connection *)0; + afs_int32 error = 0; + int islocked = 0; + afs_int32 clonetid = 0, onlinetid; + afs_int32 fromtid = 0; + afs_uint32 fromdate, thisdate; + int s; + manyDests tr; + manyResults results; + int rwindex, roindex, roclone, roexists; + afs_int32 rwcrdate, clcrdate; + struct rtime { + int validtime; + afs_uint32 time; + } remembertime[NMAXNSERVERS]; + int releasecount = 0; + struct volser_status volstatus; + + memset((char *)remembertime, 0, sizeof(remembertime)); + memset((char *)&results, 0, sizeof(results)); + + vcode = ubik_Call(VL_SetLock, cstruct, 0, afromvol, RWVOL, VLOP_RELEASE); + if (vcode != VL_RERELEASE) + ONERROR(vcode, afromvol, + "Could not lock the VLDB entry for the volume %u.\n"); + islocked = 1; + + /* Get the vldb entry in readable format */ + vcode = VLDB_GetEntryByID(afromvol, RWVOL, &entry); + ONERROR(vcode, afromvol, + "Could not fetch the entry for the volume %u from the VLDB.\n"); + MapHostToNetwork(&entry); + + if (verbose) + EnumerateEntry(&entry); + + if (!ISNAMEVALID(entry.name)) + ONERROR(VOLSERBADOP, entry.name, + "Volume name %s is too long, rename before releasing.\n"); + if (entry.volumeId[RWVOL] != afromvol) + ONERROR(VOLSERBADOP, afromvol, + "The volume %u being released is not a read-write volume.\n"); + if (entry.nServers <= 1) + ONERROR(VOLSERBADOP, afromvol, + "Volume %u has no replicas - release operation is meaningless!\n"); + if (strlen(entry.name) > (VOLSER_OLDMAXVOLNAME - 10)) + ONERROR(VOLSERBADOP, entry.name, + "RO volume name %s exceeds (VOLSER_OLDMAXVOLNAME - 10) character limit\n"); + + /* roclone is true if one of the RO volumes is on the same + * partition as the RW volume. In this case, we make the RO volume + * on the same partition a clone instead of a complete copy. + */ + + roindex = Lp_ROMatch(afromserver, afrompart, &entry) - 1; + roclone = ((roindex == -1) ? 0 : 1); + rwindex = Lp_GetRwIndex(&entry); + if (rwindex < 0) + ONERROR(VOLSERNOVOL, 0, "There is no RW volume \n"); + + /* Make sure we have a RO volume id to work with */ + if (entry.volumeId[ROVOL] == INVALID_BID) { + /* need to get a new RO volume id */ + vcode = ubik_Call(VL_GetNewVolumeId, cstruct, 0, 1, &roVolId); + ONERROR(vcode, entry.name, "Cant allocate ID for RO volume of %s\n"); + + entry.volumeId[ROVOL] = roVolId; + MapNetworkToHost(&entry, &storeEntry); + vcode = VLDB_ReplaceEntry(afromvol, RWVOL, &storeEntry, 0); + ONERROR(vcode, entry.name, "Could not update vldb entry for %s.\n"); + } + + /* 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; + } + if (forceflag && !fullrelease) + fullrelease = 1; + + /* Determine which volume id to use and see if it exists */ + cloneVolId = + ((fullrelease + || (entry.cloneId == 0)) ? entry.volumeId[ROVOL] : entry.cloneId); + code = VolumeExists(afromserver, afrompart, cloneVolId); + roexists = ((code == ENODEV) ? 0 : 1); + + 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 + * (it was recently added), then also delete it. We do not + * want to "reclone" a temporary RO clone. + */ + if (roexists + && (!roclone || (entry.serverFlags[roindex] & RO_DONTUSE))) { + code = DelVol(fromconn, cloneVolId, afrompart, ITOffline); + if (code && (code != VNOVOL)) + ERROREXIT(code); + roexists = 0; + } + + /* Mark all the ROs in the VLDB entry as RO_DONTUSE. We don't + * write this entry out to the vlserver until after the first + * RO volume is released (temp RO clones don't count). + */ + for (i = 0; i < entry.nServers; i++) { + entry.serverFlags[i] &= ~NEW_REPSITE; + entry.serverFlags[i] |= RO_DONTUSE; + } + entry.serverFlags[rwindex] |= NEW_REPSITE; + entry.serverFlags[rwindex] &= ~RO_DONTUSE; + + /* Begin transaction on RW and mark it busy while we clone it */ + code = + AFSVolTransCreate(fromconn, afromvol, afrompart, ITBusy, + &clonetid); + ONERROR(code, afromvol, "Failed to start transaction on volume %u\n"); + + /* Clone or reclone the volume */ + if (roexists) { + VPRINT1("Recloning RW volume %u...", cloneVolId); + code = AFSVolReClone(fromconn, clonetid, cloneVolId); + ONERROR(code, afromvol, "Failed to reclone the RW volume %u\n"); + VDONE; + } else { + if (roclone) { + strcpy(vname, entry.name); + strcat(vname, ".readonly"); + VPRINT("Cloning RW volume %u to permanent RO..."); + } else { + strcpy(vname, "readonly-clone-temp"); + VPRINT("Cloning RW volume %u to temporary RO..."); + } + code = + AFSVolClone(fromconn, clonetid, 0, readonlyVolume, vname, + &cloneVolId); + ONERROR(code, afromvol, "Failed to clone the RW volume %u\n"); + VDONE; + } + + /* Get the time the RW was created for future information */ + VPRINT1("Getting status of RW volume %u...", cloneVolId); + code = AFSVolGetStatus(fromconn, clonetid, &volstatus); + ONERROR(code, cloneVolId, + "Failed to get the status of the RW volume %u\n"); + VDONE; + rwcrdate = volstatus.creationDate; + + /* End the transaction on the RW volume */ + VPRINT1("Ending cloning transaction on RW volume %u...", cloneVolId); + code = AFSVolEndTrans(fromconn, clonetid, &rcode); + clonetid = 0; + ONERROR((code ? code : rcode), cloneVolId, + "Failed to end cloning transaction on RW %u\n"); + VDONE; + + /* Remember clone volume ID in case we fail or are interrupted */ + entry.cloneId = cloneVolId; + + if (roclone) { + /* Bring the RO clone online - though not if it's a temporary clone */ + VPRINT1("Starting transaction on RO clone volume %u...", + cloneVolId); + code = + AFSVolTransCreate(fromconn, cloneVolId, afrompart, ITOffline, + &onlinetid); + ONERROR(code, cloneVolId, + "Failed to start transaction on volume %u\n"); + VDONE; + + VPRINT1("Setting volume flags for volume %u...", cloneVolId); + tcode = AFSVolSetFlags(fromconn, onlinetid, 0); + VDONE; + + VPRINT1("Ending transaction on volume %u...", cloneVolId); + code = AFSVolEndTrans(fromconn, onlinetid, &rcode); + ONERROR((code ? code : rcode), cloneVolId, + "Failed to end transaction on RO clone %u\n"); + VDONE; + + ONERROR(tcode, cloneVolId, "Could not bring volume %u on line\n"); + + /* Sleep so that a client searching for an online volume won't + * find the clone offline and then the next RO offline while the + * release brings the clone online and the next RO offline (race). + * There is a fix in the 3.4 client that does not need this sleep + * anymore, but we don't know what clients we have. + */ + if (entry.nServers > 2) + sleep(5); + + /* Mark the RO clone in the VLDB as a good site (already released) */ + entry.serverFlags[roindex] |= NEW_REPSITE; + entry.serverFlags[roindex] &= ~RO_DONTUSE; + entry.flags |= RO_EXISTS; + + releasecount++; + + /* Write out the VLDB entry only if the clone is not a temporary + * clone. If we did this to a temporary clone then we would end + * up marking all the ROs as "old release" making the ROs + * temporarily unavailable. + */ + MapNetworkToHost(&entry, &storeEntry); + VPRINT1("Replacing VLDB entry for %s...", entry.name); + vcode = VLDB_ReplaceEntry(afromvol, RWVOL, &storeEntry, 0); + ONERROR(vcode, entry.name, + "Could not update vldb entry for %s.\n"); + VDONE; + } + } + + /* Now we will release from the clone to the remaining RO replicas. + * The first 2 ROs (counting the non-temporary RO clone) are released + * individually: releasecount. This is to reduce the race condition + * of clients trying to find an on-line RO volume. The remaining ROs + * are released in parallel but no more than half the number of ROs + * (rounded up) at a time: nservers. + */ + + strcpy(vname, entry.name); + strcat(vname, ".readonly"); + memset(&cookie, 0, sizeof(cookie)); + strncpy(cookie.name, vname, VOLSER_OLDMAXVOLNAME); + cookie.type = ROVOL; + cookie.parent = entry.volumeId[RWVOL]; + cookie.clone = 0; + + nservers = entry.nServers / 2; /* how many to do at once, excluding clone */ + replicas = + (struct replica *)malloc(sizeof(struct replica) * nservers + 1); + times = (struct release *)malloc(sizeof(struct release) * nservers + 1); + toconns = + (struct rx_connection **)malloc(sizeof(struct rx_connection *) * + nservers + 1); + results.manyResults_val = + (afs_int32 *) malloc(sizeof(afs_int32) * nservers + 1); + if (!replicas || !times || !!!results.manyResults_val || !toconns) + ONERROR(ENOMEM, 0, + "Failed to create transaction on the release clone\n"); + + memset(replicas, 0, (sizeof(struct replica) * nservers + 1)); + memset(times, 0, (sizeof(struct release) * nservers + 1)); + memset(toconns, 0, (sizeof(struct rx_connection *) * nservers + 1)); + memset(results.manyResults_val, 0, (sizeof(afs_int32) * nservers + 1)); + + /* Create a transaction on the cloned volume */ + VPRINT1("Starting transaction on cloned volume %u...", cloneVolId); + code = + AFSVolTransCreate(fromconn, cloneVolId, afrompart, ITBusy, &fromtid); + if (!fullrelease && code) + ONERROR(VOLSERNOVOL, afromvol, + "Old clone is inaccessible. Try vos release -f %u.\n"); + ONERROR(code, 0, "Failed to create transaction on the release clone\n"); + VDONE; + + /* For each index in the VLDB */ + for (vldbindex = 0; vldbindex < entry.nServers;) { + + /* Get a transaction on the replicas. Pick replacas which have an old release. */ + for (volcount = 0; + ((volcount < nservers) && (vldbindex < entry.nServers)); + vldbindex++) { + /* The first two RO volumes will be released individually. + * The rest are then released in parallel. This is a hack + * for clients not recognizing right away when a RO volume + * comes back on-line. + */ + if ((volcount == 1) && (releasecount < 2)) + break; + + if (vldbindex == roindex) + continue; /* the clone */ + if ((entry.serverFlags[vldbindex] & NEW_REPSITE) + && !(entry.serverFlags[vldbindex] & RO_DONTUSE)) + continue; + if (!(entry.serverFlags[vldbindex] & ITSROVOL)) + continue; /* not a RO vol */ + + + /* Get a Transaction on this replica. Get a new connection if + * necessary. Create the volume if necessary. Return the + * time from which the dump should be made (0 if it's a new + * volume). Each volume might have a different time. + */ + replicas[volcount].server.destHost = + ntohl(entry.serverNumber[vldbindex]); + replicas[volcount].server.destPort = AFSCONF_VOLUMEPORT; + replicas[volcount].server.destSSID = 1; + times[volcount].vldbEntryIndex = vldbindex; + + code = + GetTrans(&entry, vldbindex, &(toconns[volcount]), + &(replicas[volcount].trans), + &(times[volcount].time)); + if (code) + continue; + + /* Thisdate is the date from which we want to pick up all changes */ + if (forceflag || !fullrelease + || (rwcrdate > times[volcount].time)) { + /* If the forceflag is set, then we want to do a full dump. + * If it's not a full release, we can't be sure that the creation + * date is good (so we also do a full dump). + * If the RW volume was replaced (its creation date is newer than + * the last release), then we can't be sure what has changed (so + * we do a full dump). + */ + thisdate = 0; + } else if (remembertime[vldbindex].validtime) { + /* Trans was prev ended. Use the time from the prev trans + * because, prev trans may have created the volume. In which + * case time[volcount].time would be now instead of 0. + */ + thisdate = + (remembertime[vldbindex].time < + times[volcount].time) ? remembertime[vldbindex]. + time : times[volcount].time; + } else { + thisdate = times[volcount].time; + } + remembertime[vldbindex].validtime = 1; + remembertime[vldbindex].time = thisdate; + + if (volcount == 0) { + fromdate = thisdate; + } else { + /* Include this volume if it is within 15 minutes of the earliest */ + if (((fromdate > + thisdate) ? (fromdate - thisdate) : (thisdate - + fromdate)) > 900) { + AFSVolEndTrans(toconns[volcount], + replicas[volcount].trans, &rcode); + replicas[volcount].trans = 0; + break; + } + if (thisdate < fromdate) + fromdate = thisdate; + } + volcount++; + } + if (!volcount) + continue; + + if (verbose) { + fprintf(STDOUT, "Starting ForwardMulti from %lu to %u on %s", + (unsigned long)cloneVolId, entry.volumeId[ROVOL], + hostutil_GetNameByINet(entry. + serverNumber[times[0]. + vldbEntryIndex])); + + for (s = 1; s < volcount; s++) { + fprintf(STDOUT, " and %s", + hostutil_GetNameByINet(entry. + serverNumber[times[s]. + vldbEntryIndex])); + } + + if (fromdate == 0) + fprintf(STDOUT, " (full release)"); + fprintf(STDOUT, ".\n"); + fflush(STDOUT); + } + + /* Release the ones we have collected */ + tr.manyDests_val = &(replicas[0]); + tr.manyDests_len = results.manyResults_len = volcount; + code = + AFSVolForwardMultiple(fromconn, fromtid, fromdate, &tr, + 0 /*spare */ , &cookie, &results); + if (code == RXGEN_OPCODE) { /* RPC Interface Mismatch */ + code = + SimulateForwardMultiple(fromconn, fromtid, fromdate, &tr, + 0 /*spare */ , &cookie, &results); + nservers = 1; + } + + if (code) { + PrintError("Release failed: ", code); + } else { + for (m = 0; m < volcount; m++) { + if (results.manyResults_val[m]) { + if ((m == 0) || (results.manyResults_val[m] != ENOENT)) { + /* we retry timed out transaction. When it is + * not the first volume and the transaction wasn't found + * (assume it timed out and was garbage collected by volser). + */ + PrintError + ("Failed to dump volume from clone to a ro site: ", + results.manyResults_val[m]); + } + continue; + } + + code = + AFSVolSetIdsTypes(toconns[m], replicas[m].trans, vname, + ROVOL, entry.volumeId[RWVOL], 0, 0); + if (code) { + if ((m == 0) || (code != ENOENT)) { + PrintError("Failed to set correct names and ids: ", + code); + } + continue; + } + + /* have to clear dest. flags to ensure new vol goes online: + * because the restore (forwarded) operation copied + * the V_inService(=0) flag over to the destination. + */ + code = AFSVolSetFlags(toconns[m], replicas[m].trans, 0); + if (code) { + if ((m == 0) || (code != ENOENT)) { + PrintError("Failed to set flags on ro volume: ", + code); + } + continue; + } + + entry.serverFlags[times[m].vldbEntryIndex] |= NEW_REPSITE; + entry.serverFlags[times[m].vldbEntryIndex] &= ~RO_DONTUSE; + entry.flags |= RO_EXISTS; + releasecount++; + } + } + + /* End the transactions and destroy the connections */ + for (s = 0; s < volcount; s++) { + if (replicas[s].trans) + code = AFSVolEndTrans(toconns[s], replicas[s].trans, &rcode); + replicas[s].trans = 0; + if (!code) + code = rcode; + if (code) { + if ((s == 0) || (code != ENOENT)) { + PrintError("Could not end transaction on a ro volume: ", + code); + } else { + PrintError + ("Transaction timed out on a ro volume. Will retry.\n", + 0); + if (times[s].vldbEntryIndex < vldbindex) + vldbindex = times[s].vldbEntryIndex; + } + } + + if (toconns[s]) + rx_DestroyConnection(toconns[s]); + toconns[s] = 0; + } + + MapNetworkToHost(&entry, &storeEntry); + vcode = VLDB_ReplaceEntry(afromvol, RWVOL, &storeEntry, 0); + ONERROR(vcode, afromvol, + " Could not update VLDB entry for volume %u\n"); + } /* for each index in the vldb */ + + /* End the transaction on the cloned volume */ + code = AFSVolEndTrans(fromconn, fromtid, &rcode); + fromtid = 0; + if (!code) + code = rcode; + if (code) + PrintError("Failed to end transaction on rw volume: ", code); + + /* Figure out if any volume were not released and say so */ + for (failure = 0, i = 0; i < entry.nServers; i++) { + if (!(entry.serverFlags[i] & NEW_REPSITE)) + failure++; + } + if (failure) { + char pname[10]; + fprintf(STDERR, + "The volume %lu could not be released to the following %d sites:\n", + (unsigned long)afromvol, failure); + for (i = 0; i < entry.nServers; i++) { + if (!(entry.serverFlags[i] & NEW_REPSITE)) { + MapPartIdIntoName(entry.serverPartition[i], pname); + fprintf(STDERR, "\t%35s %s\n", + hostutil_GetNameByINet(entry.serverNumber[i]), pname); + } + } + + MapNetworkToHost(&entry, &storeEntry); + vcode = + VLDB_ReplaceEntry(afromvol, RWVOL, &storeEntry, + LOCKREL_TIMESTAMP); + ONERROR(vcode, afromvol, + " Could not update VLDB entry for volume %u\n"); + + ERROREXIT(VOLSERBADRELEASE); + } + + /* All the ROs were release successfully. Remove the temporary clone */ + if (!roclone) { + if (verbose) { + fprintf(STDOUT, "Deleting the releaseClone %lu ...", + (unsigned long)cloneVolId); + fflush(STDOUT); + } + code = DelVol(fromconn, cloneVolId, afrompart, ITOffline); + ONERROR(code, cloneVolId, "Failed to delete volume %u.\n"); + VDONE; + } + entry.cloneId = 0; + + for (i = 0; i < entry.nServers; i++) + entry.serverFlags[i] &= ~NEW_REPSITE; + + /* Update the VLDB */ + VPRINT("updating VLDB ..."); + + MapNetworkToHost(&entry, &storeEntry); + vcode = + VLDB_ReplaceEntry(afromvol, RWVOL, &storeEntry, + LOCKREL_OPCODE | LOCKREL_AFSID | LOCKREL_TIMESTAMP); + ONERROR(vcode, afromvol, " Could not update VLDB entry for volume %u\n"); + VDONE; + + rfail: + if (clonetid) { + code = AFSVolEndTrans(fromconn, clonetid, &rcode); + clonetid = 0; + if (code) { + fprintf(STDERR, + "Failed to end cloning transaction on the RW volume %lu\n", + (unsigned long)afromvol); + if (!error) + error = code; + } + } + if (fromtid) { + code = AFSVolEndTrans(fromconn, fromtid, &rcode); + fromtid = 0; + if (code) { + fprintf(STDERR, + "Failed to end transaction on the release clone %lu\n", + (unsigned long)cloneVolId); + if (!error) + error = code; + } + } + for (i = 0; i < nservers; i++) { + if (replicas && replicas[i].trans) { + code = AFSVolEndTrans(toconns[i], replicas[i].trans, &rcode); + replicas[i].trans = 0; + if (code) { + fprintf(STDERR, + "Failed to end transaction on ro volume %u at server %s\n", + entry.volumeId[ROVOL], + hostutil_GetNameByINet(htonl + (replicas[i].server. + destHost))); + if (!error) + error = code; + } + } + if (toconns && toconns[i]) { + rx_DestroyConnection(toconns[i]); + toconns[i] = 0; + } + } + if (islocked) { + vcode = + ubik_Call(VL_ReleaseLock, cstruct, 0, afromvol, RWVOL, + LOCKREL_OPCODE | LOCKREL_AFSID | LOCKREL_TIMESTAMP); + if (vcode) { + fprintf(STDERR, + "Could not release lock on the VLDB entry for volume %lu\n", + (unsigned long)afromvol); + if (!error) + error = vcode; + } + } + + PrintError("", error); + + if (fromconn) + rx_DestroyConnection(fromconn); + if (results.manyResults_val) + free(results.manyResults_val); + if (replicas) + free(replicas); + if (toconns) + free(toconns); + if (times) + free(times); + return error; +} + + +void +dump_sig_handler(int x) +{ + fprintf(STDERR, "\nSignal handler: vos dump operation\n"); + longjmp(env, 0); +} + +/* Dump the volume on and + * to starting from . + * DumpFunction does the real work behind the scenes after + * extracting parameters from the rock + */ +int +UV_DumpVolume(afs_int32 afromvol, afs_int32 afromserver, afs_int32 afrompart, + afs_int32 fromdate, afs_int32(*DumpFunction) (), char *rock) +{ + struct rx_connection *fromconn = (struct rx_connection *)0; + struct rx_call *fromcall = (struct rx_call *)0; + afs_int32 fromtid = 0, rxError = 0, rcode = 0; + afs_int32 code, error = 0; + + if (setjmp(env)) + ERROR_EXIT(EPIPE); +#ifndef AFS_NT40_ENV + (void)signal(SIGPIPE, dump_sig_handler); +#endif + (void)signal(SIGINT, dump_sig_handler); + + if (!fromdate) { + VPRINT("Full Dump ...\n"); + } else { + VPRINT1("Incremental Dump (as of %.24s)...\n", + ctime((time_t *) & fromdate)); + } + + /* get connections to the servers */ + fromconn = UV_Bind(afromserver, AFSCONF_VOLUMEPORT); + + VPRINT1("Starting transaction on volume %u...", afromvol); + code = AFSVolTransCreate(fromconn, afromvol, afrompart, ITBusy, &fromtid); + EGOTO1(error_exit, code, + "Could not start transaction on the volume %u to be dumped\n", + afromvol); + VDONE; + + fromcall = rx_NewCall(fromconn); + + VPRINT1("Starting volume dump on volume %u...", afromvol); + code = StartAFSVolDump(fromcall, fromtid, fromdate); + EGOTO(error_exit, code, "Could not start the dump process \n"); + VDONE; + + VPRINT1("Dumping volume %u...", afromvol); + code = DumpFunction(fromcall, rock); + EGOTO(error_exit, code, "Error while dumping volume \n"); + VDONE; + + error_exit: + if (fromcall) { + code = rx_EndCall(fromcall, rxError); + if (code) { + fprintf(STDERR, "Error in rx_EndCall\n"); + if (!error) + error = code; + } + } + if (fromtid) { + VPRINT1("Ending transaction on volume %u...", afromvol); + code = AFSVolEndTrans(fromconn, fromtid, &rcode); + if (code || rcode) { + fprintf(STDERR, "Could not end transaction on the volume %lu\n", + (unsigned long)afromvol); + if (!error) + error = (code ? code : rcode); + } + VDONE; + } + if (fromconn) + rx_DestroyConnection(fromconn); + + PrintError("", error); + return (error); +} + +/* Clone the volume on and + * , and then dump the clone volume to + * starting from . + * DumpFunction does the real work behind the scenes after + * extracting parameters from the rock + */ +int +UV_DumpClonedVolume(afs_int32 afromvol, afs_int32 afromserver, + afs_int32 afrompart, afs_int32 fromdate, + afs_int32(*DumpFunction) (), char *rock) +{ + struct rx_connection *fromconn = (struct rx_connection *)0; + struct rx_call *fromcall = (struct rx_call *)0; + afs_int32 fromtid = 0, rxError = 0, rcode = 0; + afs_int32 clonetid = 0; + afs_int32 code = 0, vcode = 0, error = 0; + afs_int32 clonevol = 0; + char vname[64]; + + if (setjmp(env)) + ERROR_EXIT(EPIPE); +#ifndef AFS_NT40_ENV + (void)signal(SIGPIPE, dump_sig_handler); +#endif + (void)signal(SIGINT, dump_sig_handler); + + if (!fromdate) { + VPRINT("Full Dump ...\n"); + } else { + VPRINT1("Incremental Dump (as of %.24s)...\n", + ctime((time_t *) & fromdate)); + } + + /* get connections to the servers */ + fromconn = UV_Bind(afromserver, AFSCONF_VOLUMEPORT); + + VPRINT1("Starting transaction on volume %u...", afromvol); + code = AFSVolTransCreate(fromconn, afromvol, afrompart, ITBusy, &fromtid); + EGOTO1(error_exit, code, + "Could not start transaction on the volume %u to be dumped\n", + afromvol); + VDONE; + + /* Get a clone id */ + VPRINT1("Allocating new volume id for clone of volume %u ...", afromvol); + code = ubik_Call(VL_GetNewVolumeId, cstruct, 0, 1, &clonevol); + EGOTO1(error_exit, code, + "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 */ + VPRINT2("Cloning source volume %u to clone volume %u...", afromvol, + clonevol); + strcpy(vname, "dump-clone-temp"); + code = + AFSVolClone(fromconn, fromtid, 0, readonlyVolume, vname, &clonevol); + EGOTO1(error_exit, code, "Failed to clone the source volume %u\n", + afromvol); + VDONE; + + VPRINT1("Ending the transaction on the volume %u ...", afromvol); + rcode = 0; + code = AFSVolEndTrans(fromconn, fromtid, &rcode); + fromtid = 0; + if (!code) + code = rcode; + EGOTO1(error_exit, code, + "Failed to end the transaction on the volume %u\n", afromvol); + VDONE; + + + VPRINT1("Starting transaction on the cloned volume %u ...", clonevol); + code = + AFSVolTransCreate(fromconn, clonevol, afrompart, ITOffline, + &clonetid); + EGOTO1(error_exit, 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(error_exit, code, "Could not set falgs on the cloned volume %u\n", + clonevol); + VDONE; + + + fromcall = rx_NewCall(fromconn); + + VPRINT1("Starting volume dump from cloned volume %u...", clonevol); + code = StartAFSVolDump(fromcall, clonetid, fromdate); + EGOTO(error_exit, code, "Could not start the dump process \n"); + VDONE; + + VPRINT1("Dumping volume %u...", afromvol); + code = DumpFunction(fromcall, rock); + EGOTO(error_exit, code, "Error while dumping volume \n"); + VDONE; + + error_exit: + /* now delete the clone */ + VPRINT1("Deleting the cloned volume %u ...", clonevol); + code = AFSVolDeleteVolume(fromconn, clonetid); + if (code) { + fprintf(STDERR, "Failed to delete the cloned volume %lu\n", + (unsigned long)clonevol); + } else { + VDONE; + } + + if (fromcall) { + code = rx_EndCall(fromcall, rxError); + if (code) { + fprintf(STDERR, "Error in rx_EndCall\n"); + if (!error) + error = code; + } + } + if (clonetid) { + VPRINT1("Ending transaction on cloned volume %u...", clonevol); + code = AFSVolEndTrans(fromconn, clonetid, &rcode); + if (code || rcode) { + fprintf(STDERR, + "Could not end transaction on the cloned volume %lu\n", + (unsigned long)clonevol); + if (!error) + error = (code ? code : rcode); + } + VDONE; + } + if (fromconn) + rx_DestroyConnection(fromconn); + + PrintError("", error); + return (error); +} + + + +/* + * Restore a volume on from + * the dump file . WriteData does all the real work + * after extracting params from the rock + */ +int +UV_RestoreVolume(afs_int32 toserver, afs_int32 topart, afs_int32 tovolid, + char tovolname[], int flags, afs_int32(*WriteData) (), + char *rock) +{ + struct rx_connection *toconn, *tempconn; + struct rx_call *tocall; + afs_int32 totid, code, rcode, vcode, terror = 0; + afs_int32 rxError = 0; + struct volser_status tstatus; + char partName[10]; + afs_int32 pvolid; + afs_int32 temptid; + int success; + struct nvldbentry entry, storeEntry; + afs_int32 error; + int islocked; + struct restoreCookie cookie; + int reuseID; + afs_int32 newDate, volflag, voltype, volsertype; + int index, same, errcode; + char apartName[10]; + + + memset(&cookie, 0, sizeof(cookie)); + islocked = 0; + success = 0; + error = 0; + reuseID = 1; + tocall = (struct rx_call *)0; + toconn = (struct rx_connection *)0; + tempconn = (struct rx_connection *)0; + totid = 0; + temptid = 0; + + if (flags & RV_RDONLY) { + voltype = ROVOL; + volsertype = volser_RO; + } else { + voltype = RWVOL; + volsertype = volser_RW; + } + + pvolid = tovolid; + toconn = UV_Bind(toserver, AFSCONF_VOLUMEPORT); + if (pvolid == 0) { /*alot a new id if needed */ + vcode = VLDB_GetEntryByName(tovolname, &entry); + if (vcode == VL_NOENT) { + vcode = ubik_Call(VL_GetNewVolumeId, cstruct, 0, 1, &pvolid); + if (vcode) { + fprintf(STDERR, "Could not get an Id for the volume %s\n", + tovolname); + error = vcode; + goto refail; + } + reuseID = 0; + } else if (flags & RV_RDONLY) { + if (entry.flags & RW_EXISTS) { + fprintf(STDERR, + "Entry for ReadWrite volume %s already exists!\n", + entry.name); + error = VOLSERBADOP; + goto refail; + } + if (!entry.volumeId[ROVOL]) { + fprintf(STDERR, + "Existing entry for volume %s has no ReadOnly ID\n", + tovolname); + error = VOLSERBADOP; + goto refail; + } + pvolid = entry.volumeId[ROVOL]; + } else { + pvolid = entry.volumeId[RWVOL]; + } + } + /* at this point we have a volume id to use/reuse for the volume to be restored */ + if (strlen(tovolname) > (VOLSER_OLDMAXVOLNAME - 1)) { + EGOTO1(refail, VOLSERBADOP, + "The volume name %s exceeds the maximum limit of (VOLSER_OLDMAXVOLNAME -1 ) bytes\n", + tovolname); + } + MapPartIdIntoName(topart, partName); + fprintf(STDOUT, "Restoring volume %s Id %lu on server %s partition %s ..", + tovolname, (unsigned long)pvolid, + hostutil_GetNameByINet(toserver), partName); + fflush(STDOUT); + code = + AFSVolCreateVolume(toconn, topart, tovolname, volsertype, 0, &pvolid, + &totid); + if (code) { + if (flags & RV_FULLRST) { /* full restore: delete then create anew */ + VPRINT1("Deleting the previous volume %u ...", pvolid); + + code = + AFSVolTransCreate(toconn, pvolid, topart, ITOffline, &totid); + EGOTO1(refail, code, "Failed to start transaction on %u\n", + pvolid); + + code = + AFSVolSetFlags(toconn, totid, + VTDeleteOnSalvage | VTOutOfService); + EGOTO1(refail, code, "Could not set flags on volume %u \n", + pvolid); + + code = AFSVolDeleteVolume(toconn, totid); + EGOTO1(refail, code, "Could not delete volume %u\n", pvolid); + + code = AFSVolEndTrans(toconn, totid, &rcode); + totid = 0; + if (!code) + code = rcode; + EGOTO1(refail, code, "Could not end transaction on %u\n", pvolid); + + VDONE; + + code = + 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; + cookie.type = voltype; + cookie.clone = 0; + strncpy(cookie.name, tovolname, VOLSER_OLDMAXVOLNAME); + + tocall = rx_NewCall(toconn); + terror = StartAFSVolRestore(tocall, totid, 1, &cookie); + if (terror) { + fprintf(STDERR, "Volume restore Failed \n"); + error = terror; + goto refail; + } + code = WriteData(tocall, rock); + if (code) { + fprintf(STDERR, "Could not transmit data\n"); + error = code; + goto refail; + } + terror = rx_EndCall(tocall, rxError); + tocall = (struct rx_call *)0; + if (terror) { + fprintf(STDERR, "rx_EndCall Failed \n"); + error = terror; + goto refail; + } + code = AFSVolGetStatus(toconn, totid, &tstatus); + if (code) { + fprintf(STDERR, + "Could not get status information about the volume %lu\n", + (unsigned long)pvolid); + error = code; + goto refail; + } + code = AFSVolSetIdsTypes(toconn, totid, tovolname, voltype, pvolid, 0, 0); + if (code) { + fprintf(STDERR, "Could not set the right type and ID on %lu\n", + (unsigned long)pvolid); + error = code; + goto refail; + } + if (!newDate) + newDate = time(0); + code = AFSVolSetDate(toconn, totid, newDate); + if (code) { + fprintf(STDERR, "Could not set the date on %lu\n", + (unsigned long)pvolid); + error = code; + goto refail; + } + + volflag = ((flags & RV_OFFLINE) ? VTOutOfService : 0); /* off or on-line */ + code = AFSVolSetFlags(toconn, totid, volflag); + if (code) { + fprintf(STDERR, "Could not mark %lu online\n", (unsigned long)pvolid); + error = code; + goto refail; + } + +/* It isn't handled right in refail */ + code = AFSVolEndTrans(toconn, totid, &rcode); + totid = 0; + if (!code) + code = rcode; + if (code) { + fprintf(STDERR, "Could not end transaction on %lu\n", + (unsigned long)pvolid); + error = code; + goto refail; + } + + success = 1; + fprintf(STDOUT, " done\n"); + fflush(STDOUT); + if (success && (!reuseID || (flags & RV_FULLRST))) { + /* Volume was restored on the file server, update the + * VLDB to reflect the change. + */ + vcode = VLDB_GetEntryByID(pvolid, voltype, &entry); + if (vcode && vcode != VL_NOENT && vcode != VL_ENTDELETED) { + fprintf(STDERR, + "Could not fetch the entry for volume number %lu from VLDB \n", + (unsigned long)pvolid); + error = vcode; + goto refail; + } + if (!vcode) + MapHostToNetwork(&entry); + if (vcode == VL_NOENT) { /* it doesnot exist already */ + /*make the vldb return this indication specifically */ + VPRINT("------- Creating a new VLDB entry ------- \n"); + strcpy(entry.name, tovolname); + entry.nServers = 1; + entry.serverNumber[0] = toserver; /*should be indirect */ + entry.serverPartition[0] = topart; + entry.serverFlags[0] = (flags & RV_RDONLY) ? ITSROVOL : ITSRWVOL; + entry.flags = (flags & RV_RDONLY) ? RO_EXISTS : RW_EXISTS; + if (flags & RV_RDONLY) + entry.volumeId[ROVOL] = pvolid; + else if (tstatus.cloneID != 0) { + entry.volumeId[ROVOL] = tstatus.cloneID; /*this should come from status info on the volume if non zero */ + } else + entry.volumeId[ROVOL] = INVALID_BID; + entry.volumeId[RWVOL] = pvolid; + entry.cloneId = 0; + if (tstatus.backupID != 0) { + entry.volumeId[BACKVOL] = tstatus.backupID; + /*this should come from status info on the volume if non zero */ + } else + entry.volumeId[BACKVOL] = INVALID_BID; + MapNetworkToHost(&entry, &storeEntry); + vcode = VLDB_CreateEntry(&storeEntry); + if (vcode) { + fprintf(STDERR, + "Could not create the VLDB entry for volume number %lu \n", + (unsigned long)pvolid); + error = vcode; + goto refail; + } + islocked = 0; + if (verbose) + EnumerateEntry(&entry); + } else { /*update the existing entry */ + if (verbose) { + fprintf(STDOUT, "Updating the existing VLDB entry\n"); + fprintf(STDOUT, "------- Old entry -------\n"); + EnumerateEntry(&entry); + fprintf(STDOUT, "------- New entry -------\n"); + } + vcode = + ubik_Call(VL_SetLock, cstruct, 0, pvolid, voltype, + VLOP_RESTORE); + if (vcode) { + fprintf(STDERR, + "Could not lock the entry for volume number %lu \n", + (unsigned long)pvolid); + error = vcode; + goto refail; + } + islocked = 1; + strcpy(entry.name, tovolname); + + /* Update the vlentry with the new information */ + if (flags & RV_RDONLY) + index = Lp_ROMatch(toserver, topart, &entry) - 1; + else + index = Lp_GetRwIndex(&entry); + if (index == -1) { + /* Add the new site for the volume being restored */ + entry.serverNumber[entry.nServers] = toserver; + entry.serverPartition[entry.nServers] = topart; + entry.serverFlags[entry.nServers] = + (flags & RV_RDONLY) ? ITSROVOL : ITSRWVOL; + entry.nServers++; + } else { + /* This volume should be deleted on the old site + * if its different from new site. + */ + same = + VLDB_IsSameAddrs(toserver, entry.serverNumber[index], + &errcode); + EPRINT2(errcode, + "Failed to get info about server's %d address(es) from vlserver (err=%d)\n", + toserver, errcode); + if ((!errcode && !same) + || (entry.serverPartition[index] != topart)) { + tempconn = + UV_Bind(entry.serverNumber[index], + AFSCONF_VOLUMEPORT); + + MapPartIdIntoName(entry.serverPartition[index], + apartName); + VPRINT3 + ("Deleting the previous volume %u on server %s, partition %s ...", + pvolid, + hostutil_GetNameByINet(entry.serverNumber[index]), + apartName); + code = + AFSVolTransCreate(tempconn, pvolid, + entry.serverPartition[index], + ITOffline, &temptid); + if (!code) { + code = + AFSVolSetFlags(tempconn, temptid, + VTDeleteOnSalvage | + VTOutOfService); + if (code) { + fprintf(STDERR, + "Could not set flags on volume %lu on the older site\n", + (unsigned long)pvolid); + error = code; + goto refail; + } + code = AFSVolDeleteVolume(tempconn, temptid); + if (code) { + fprintf(STDERR, + "Could not delete volume %lu on the older site\n", + (unsigned long)pvolid); + error = code; + goto refail; + } + code = AFSVolEndTrans(tempconn, temptid, &rcode); + temptid = 0; + if (!code) + code = rcode; + if (code) { + fprintf(STDERR, + "Could not end transaction on volume %lu on the older site\n", + (unsigned long)pvolid); + error = code; + goto refail; + } + VDONE; + MapPartIdIntoName(entry.serverPartition[index], + partName); + } + } + entry.serverNumber[index] = toserver; + entry.serverPartition[index] = topart; + } + + entry.flags |= (flags & RV_RDONLY) ? RO_EXISTS : RW_EXISTS; + MapNetworkToHost(&entry, &storeEntry); + vcode = + VLDB_ReplaceEntry(pvolid, voltype, &storeEntry, + LOCKREL_OPCODE | LOCKREL_AFSID | + LOCKREL_TIMESTAMP); + if (vcode) { + fprintf(STDERR, + "Could not update the entry for volume number %lu \n", + (unsigned long)pvolid); + error = vcode; + goto refail; + } + islocked = 0; + if (verbose) + EnumerateEntry(&entry); + } + + + } + refail: + if (tocall) { + code = rx_EndCall(tocall, rxError); + if (!error) + error = code; + } + if (islocked) { + vcode = + ubik_Call(VL_ReleaseLock, cstruct, 0, pvolid, voltype, + LOCKREL_OPCODE | LOCKREL_AFSID | LOCKREL_TIMESTAMP); + if (vcode) { + fprintf(STDERR, + "Could not release lock on the VLDB entry for the volume %lu\n", + (unsigned long)pvolid); + if (!error) + error = vcode; + } + } + if (totid) { + code = AFSVolEndTrans(toconn, totid, &rcode); + if (!code) + code = rcode; + if (code) { + fprintf(STDERR, "Could not end transaction on the volume %lu \n", + (unsigned long)pvolid); + if (!error) + error = code; + } + } + if (temptid) { + code = AFSVolEndTrans(toconn, temptid, &rcode); + if (!code) + code = rcode; + if (code) { + fprintf(STDERR, "Could not end transaction on the volume %lu \n", + (unsigned long)pvolid); + if (!error) + error = code; + } + } + if (tempconn) + rx_DestroyConnection(tempconn); + if (toconn) + rx_DestroyConnection(toconn); + PrintError("", error); + return error; +} + + +/*unlocks the vldb entry associated with */ +int +UV_LockRelease(afs_int32 volid) +{ + + + afs_int32 vcode; + + VPRINT("Binding to the VLDB server\n"); + vcode = + ubik_Call(VL_ReleaseLock, cstruct, 0, volid, -1, + LOCKREL_OPCODE | LOCKREL_AFSID | LOCKREL_TIMESTAMP); + if (vcode) { + fprintf(STDERR, + "Could not unlock the entry for volume number %lu in VLDB \n", + (unsigned long)volid); + PrintError("", vcode); + return (vcode); + } + VPRINT("VLDB updated\n"); + return 0; + +} + +/*adds and as a readonly replication site for +*in vldb */ +int +UV_AddSite(afs_int32 server, afs_int32 part, afs_int32 volid) +{ + int j, nro = 0, islocked = 0; + struct nvldbentry entry, storeEntry; + afs_int32 vcode, error = 0; + char apartName[10]; + + error = ubik_Call(VL_SetLock, cstruct, 0, volid, RWVOL, VLOP_ADDSITE); + if (error) { + fprintf(STDERR, + " Could not lock the VLDB entry for the volume %lu \n", + (unsigned long)volid); + goto asfail; + } + islocked = 1; + + error = VLDB_GetEntryByID(volid, RWVOL, &entry); + if (error) { + fprintf(STDERR, + "Could not fetch the VLDB entry for volume number %lu \n", + (unsigned long)volid); + goto asfail; + + } + if (!ISNAMEVALID(entry.name)) { + fprintf(STDERR, + "Volume name %s is too long, rename before adding site\n", + entry.name); + error = VOLSERBADOP; + goto asfail; + } + MapHostToNetwork(&entry); + + /* See if it's too many entries */ + if (entry.nServers >= NMAXNSERVERS) { + fprintf(STDERR, "Total number of entries will exceed %u\n", + NMAXNSERVERS); + error = VOLSERBADOP; + goto asfail; + } + + /* See if it's on the same server */ + for (j = 0; j < entry.nServers; j++) { + if (entry.serverFlags[j] & ITSROVOL) { + nro++; + if (VLDB_IsSameAddrs(server, entry.serverNumber[j], &error)) { + if (error) { + fprintf(STDERR, + "Failed to get info about server's %d address(es) from vlserver (err=%d); aborting call!\n", + server, error); + } else { + MapPartIdIntoName(entry.serverPartition[j], apartName); + fprintf(STDERR, + "RO already exists on partition %s. Multiple ROs on a single server aren't allowed\n", + apartName); + error = VOLSERBADOP; + } + goto asfail; + } + } + } + + /* See if it's too many RO sites - leave one for the RW */ + if (nro >= NMAXNSERVERS - 1) { + fprintf(STDERR, "Total number of sites will exceed %u\n", + NMAXNSERVERS - 1); + error = VOLSERBADOP; + goto asfail; + } + + VPRINT("Adding a new site ..."); + entry.serverNumber[entry.nServers] = server; + entry.serverPartition[entry.nServers] = part; + entry.serverFlags[entry.nServers] = (ITSROVOL | RO_DONTUSE); + entry.nServers++; + + MapNetworkToHost(&entry, &storeEntry); + error = + VLDB_ReplaceEntry(volid, RWVOL, &storeEntry, + LOCKREL_OPCODE | LOCKREL_AFSID | LOCKREL_TIMESTAMP); + if (error) { + fprintf(STDERR, "Could not update entry for volume %lu \n", + (unsigned long)volid); + goto asfail; + } + islocked = 0; + VDONE; + + asfail: + if (islocked) { + vcode = + ubik_Call(VL_ReleaseLock, cstruct, 0, volid, RWVOL, + LOCKREL_OPCODE | LOCKREL_AFSID | LOCKREL_TIMESTAMP); + if (vcode) { + fprintf(STDERR, + "Could not release lock on volume entry for %lu \n", + (unsigned long)volid); + PrintError("", vcode); + } + } + + PrintError("", error); + return error; +} + +/*removes as read only site for from the vldb */ +int +UV_RemoveSite(afs_int32 server, afs_int32 part, afs_int32 volid) +{ + afs_int32 vcode; + struct nvldbentry entry, storeEntry; + int islocked; + + vcode = ubik_Call(VL_SetLock, cstruct, 0, volid, RWVOL, VLOP_ADDSITE); + if (vcode) { + fprintf(STDERR, " Could not lock the VLDB entry for volume %lu \n", + (unsigned long)volid); + PrintError("", vcode); + return (vcode); + } + islocked = 1; + vcode = VLDB_GetEntryByID(volid, RWVOL, &entry); + if (vcode) { + fprintf(STDERR, + "Could not fetch the entry for volume number %lu from VLDB \n", + (unsigned long)volid); + PrintError("", vcode); + return (vcode); + } + MapHostToNetwork(&entry); + if (!Lp_ROMatch(server, part, &entry)) { + /*this site doesnot exist */ + fprintf(STDERR, "This site is not a replication site \n"); + vcode = + ubik_Call(VL_ReleaseLock, cstruct, 0, volid, RWVOL, + LOCKREL_OPCODE | LOCKREL_AFSID | LOCKREL_TIMESTAMP); + if (vcode) { + fprintf(STDERR, "Could not update entry for volume %lu \n", + (unsigned long)volid); + PrintError("", vcode); + ubik_Call(VL_ReleaseLock, cstruct, 0, volid, RWVOL, + LOCKREL_OPCODE | LOCKREL_AFSID | LOCKREL_TIMESTAMP); + return (vcode); + } + return VOLSERBADOP; + } else { /*remove the rep site */ + Lp_SetROValue(&entry, server, part, 0, 0); + entry.nServers--; + if ((entry.nServers == 1) && (entry.flags & RW_EXISTS)) + entry.flags &= ~RO_EXISTS; + if (entry.nServers < 1) { /*this is the last ref */ + VPRINT1("Deleting the VLDB entry for %u ...", volid); + fflush(STDOUT); + vcode = ubik_Call(VL_DeleteEntry, cstruct, 0, volid, ROVOL); + if (vcode) { + fprintf(STDERR, + "Could not delete VLDB entry for volume %lu \n", + (unsigned long)volid); + PrintError("", vcode); + return (vcode); + } + VDONE; + } + MapNetworkToHost(&entry, &storeEntry); + fprintf(STDOUT, "Deleting the replication site for volume %lu ...", + (unsigned long)volid); + fflush(STDOUT); + vcode = + VLDB_ReplaceEntry(volid, RWVOL, &storeEntry, + LOCKREL_OPCODE | LOCKREL_AFSID | + LOCKREL_TIMESTAMP); + if (vcode) { + fprintf(STDERR, + "Could not release lock on volume entry for %lu \n", + (unsigned long)volid); + PrintError("", vcode); + ubik_Call(VL_ReleaseLock, cstruct, 0, volid, RWVOL, + LOCKREL_OPCODE | LOCKREL_AFSID | LOCKREL_TIMESTAMP); + return (vcode); + } + VDONE; + } + return 0; +} + +/*sets as read/write site for in the vldb */ +int +UV_ChangeLocation(afs_int32 server, afs_int32 part, afs_int32 volid) +{ + afs_int32 vcode; + struct nvldbentry entry, storeEntry; + int index; + + vcode = ubik_Call(VL_SetLock, cstruct, 0, volid, RWVOL, VLOP_ADDSITE); + if (vcode) { + fprintf(STDERR, " Could not lock the VLDB entry for volume %lu \n", + (unsigned long)volid); + PrintError("", vcode); + return (vcode); + } + vcode = VLDB_GetEntryByID(volid, RWVOL, &entry); + if (vcode) { + fprintf(STDERR, + "Could not fetch the entry for volume number %lu from VLDB \n", + (unsigned long)volid); + PrintError("", vcode); + return (vcode); + } + MapHostToNetwork(&entry); + index = Lp_GetRwIndex(&entry); + if (index < 0) { + /* no RW site exists */ + fprintf(STDERR, "No existing RW site for volume %lu", + (unsigned long)volid); + vcode = + ubik_Call(VL_ReleaseLock, cstruct, 0, volid, RWVOL, + LOCKREL_OPCODE | LOCKREL_AFSID | LOCKREL_TIMESTAMP); + if (vcode) { + fprintf(STDERR, + "Could not release lock on entry for volume %lu \n", + (unsigned long)volid); + PrintError("", vcode); + return (vcode); + } + return VOLSERBADOP; + } else { /* change the RW site */ + entry.serverNumber[index] = server; + entry.serverPartition[index] = part; + MapNetworkToHost(&entry, &storeEntry); + vcode = + VLDB_ReplaceEntry(volid, RWVOL, &storeEntry, + LOCKREL_OPCODE | LOCKREL_AFSID | + LOCKREL_TIMESTAMP); + if (vcode) { + fprintf(STDERR, "Could not update entry for volume %lu \n", + (unsigned long)volid); + PrintError("", vcode); + ubik_Call(VL_ReleaseLock, cstruct, 0, volid, RWVOL, + LOCKREL_OPCODE | LOCKREL_AFSID | LOCKREL_TIMESTAMP); + return (vcode); + } + VDONE; + } + return 0; +} + +/*list all the partitions on */ +int +UV_ListPartitions(afs_int32 aserver, struct partList *ptrPartList, + afs_int32 * cntp) +{ + struct rx_connection *aconn; + struct pIDs partIds; + struct partEntries partEnts; + register int i, j = 0, code; + + *cntp = 0; + aconn = UV_Bind(aserver, AFSCONF_VOLUMEPORT); + + partEnts.partEntries_len = 0; + partEnts.partEntries_val = NULL; + code = AFSVolXListPartitions(aconn, &partEnts); /* this is available only on new servers */ + if (code == RXGEN_OPCODE) { + for (i = 0; i < 26; i++) /* try old interface */ + partIds.partIds[i] = -1; + code = AFSVolListPartitions(aconn, &partIds); + if (!code) { + for (i = 0; i < 26; i++) { + if ((partIds.partIds[i]) != -1) { + ptrPartList->partId[j] = partIds.partIds[i]; + ptrPartList->partFlags[j] = PARTVALID; + j++; + } else + ptrPartList->partFlags[i] = 0; + } + *cntp = j; + } + } else if (!code) { + *cntp = partEnts.partEntries_len; + if (*cntp > VOLMAXPARTS) { + fprintf(STDERR, + "Warning: number of partitions on the server too high %d (process only %d)\n", + *cntp, VOLMAXPARTS); + *cntp = VOLMAXPARTS; + } + for (i = 0; i < *cntp; i++) { + ptrPartList->partId[i] = partEnts.partEntries_val[i]; + ptrPartList->partFlags[i] = PARTVALID; + } + free(partEnts.partEntries_val); + } + + /* out: */ + if (code) + fprintf(STDERR, + "Could not fetch the list of partitions from the server\n"); + PrintError("", code); + if (aconn) + rx_DestroyConnection(aconn); + return code; +} + + +/*zap the list of volumes specified by volPtrArray (the volCloneId field). + This is used by the backup system */ +int +UV_ZapVolumeClones(afs_int32 aserver, afs_int32 apart, + struct volDescription *volPtr, afs_int32 arraySize) +{ + struct rx_connection *aconn; + struct volDescription *curPtr; + int curPos; + afs_int32 code = 0; + afs_int32 rcode = 0; + afs_int32 success = 1; + afs_int32 tid; + + aconn = (struct rx_connection *)0; + aconn = UV_Bind(aserver, AFSCONF_VOLUMEPORT); + curPos = 0; + for (curPtr = volPtr; curPos < arraySize; curPtr++) { + if (curPtr->volFlags & CLONEVALID) { + curPtr->volFlags &= ~CLONEZAPPED; + success = 1; + code = + AFSVolTransCreate(aconn, curPtr->volCloneId, apart, ITOffline, + &tid); + if (code) + success = 0; + else { + code = AFSVolDeleteVolume(aconn, tid); + if (code) + success = 0; + code = AFSVolEndTrans(aconn, tid, &rcode); + if (code || rcode) + success = 0; + } + if (success) + curPtr->volFlags |= CLONEZAPPED; + if (!success) + fprintf(STDERR, "Could not zap volume %lu\n", + (unsigned long)curPtr->volCloneId); + if (success) + VPRINT2("Clone of %s %u deleted\n", curPtr->volName, + curPtr->volCloneId); + curPos++; + tid = 0; + } + } + if (aconn) + rx_DestroyConnection(aconn); + return 0; +} + +/*return a list of clones of the volumes specified by volPtrArray. Used by the + backup system */ +int +UV_GenerateVolumeClones(afs_int32 aserver, afs_int32 apart, + struct volDescription *volPtr, afs_int32 arraySize) +{ + struct rx_connection *aconn; + struct volDescription *curPtr; + int curPos; + afs_int32 code = 0; + afs_int32 rcode = 0; + afs_int32 tid; + int reuseCloneId = 0; + afs_int32 curCloneId = 0; + char cloneName[256]; /*max vol name */ + + aconn = (struct rx_connection *)0; + aconn = UV_Bind(aserver, AFSCONF_VOLUMEPORT); + curPos = 0; + if ((volPtr->volFlags & REUSECLONEID) && (volPtr->volFlags & ENTRYVALID)) + reuseCloneId = 1; + else { /*get a bunch of id's from vldb */ + code = + ubik_Call(VL_GetNewVolumeId, cstruct, 0, arraySize, &curCloneId); + if (code) { + fprintf(STDERR, "Could not get ID's for the clone from VLDB\n"); + PrintError("", code); + return code; + } + } + + for (curPtr = volPtr; curPos < arraySize; curPtr++) { + if (curPtr->volFlags & ENTRYVALID) { + + curPtr->volFlags |= CLONEVALID; + /*make a clone of curParentId and record as curPtr->volCloneId */ + code = + AFSVolTransCreate(aconn, curPtr->volId, apart, ITOffline, + &tid); + if (code) + VPRINT2("Clone for volume %s %u failed \n", curPtr->volName, + curPtr->volId); + if (code) { + curPtr->volFlags &= ~CLONEVALID; /*cant clone */ + curPos++; + continue; + } + if (strlen(curPtr->volName) < (VOLSER_OLDMAXVOLNAME - 9)) { + strcpy(cloneName, curPtr->volName); + strcat(cloneName, "-tmpClone-"); + } else + strcpy(cloneName, "-tmpClone"); + if (reuseCloneId) { + curPtr->volCloneId = curCloneId; + curCloneId++; + } + + code = + AFSVolClone(aconn, tid, 0, readonlyVolume, cloneName, + &(curPtr->volCloneId)); + if (code) { + curPtr->volFlags &= ~CLONEVALID; + curPos++; + fprintf(STDERR, "Could not clone %s due to error %lu\n", + curPtr->volName, (unsigned long)code); + code = AFSVolEndTrans(aconn, tid, &rcode); + if (code) + fprintf(STDERR, "WARNING: could not end transaction\n"); + continue; + } + VPRINT2("********** Cloned %s temporary %u\n", cloneName, + curPtr->volCloneId); + code = AFSVolEndTrans(aconn, tid, &rcode); + if (code || rcode) { + curPtr->volFlags &= ~CLONEVALID; + curPos++; + continue; + } + + curPos++; + } + } + if (aconn) + rx_DestroyConnection(aconn); + return 0; +} + + +/*list all the volumes on and . If all = 1, then all the +* relevant fields of the volume are also returned. This is a heavy weight operation.*/ +int +UV_ListVolumes(afs_int32 aserver, afs_int32 apart, int all, + struct volintInfo **resultPtr, afs_int32 * size) +{ + struct rx_connection *aconn; + afs_int32 code = 0; + volEntries volumeInfo; + + code = 0; + *size = 0; + *resultPtr = (volintInfo *) 0; + volumeInfo.volEntries_val = (volintInfo *) 0; /*this hints the stub to allocate space */ + volumeInfo.volEntries_len = 0; + + aconn = UV_Bind(aserver, AFSCONF_VOLUMEPORT); + code = AFSVolListVolumes(aconn, apart, all, &volumeInfo); + if (code) { + fprintf(STDERR, + "Could not fetch the list of volumes from the server\n"); + } else { + *resultPtr = volumeInfo.volEntries_val; + *size = volumeInfo.volEntries_len; + } + + if (aconn) + rx_DestroyConnection(aconn); + PrintError("", code); + return code; +} + +/*------------------------------------------------------------------------ + * EXPORTED UV_XListVolumes + * + * Description: + * List the extended information for all the volumes on a particular + * File Server and partition. We may either return the volume's ID + * or all of its extended information. + * + * Arguments: + * a_serverID : Address of the File Server for which we want + * extended volume info. + * a_partID : Partition for which we want the extended + * volume info. + * a_all : If non-zero, fetch ALL the volume info, + * otherwise just the volume ID. + * a_resultPP : Ptr to the address of the area containing + * the returned volume info. + * a_numEntsInResultP : Ptr for the value we set for the number of + * entries returned. + * + * Returns: + * 0 on success, + * Otherise, the return value of AFSVolXListVolumes. + * + * Environment: + * This routine is closely related to UV_ListVolumes, which returns + * only the standard level of detail on AFS volumes. It is a + * heavyweight operation, zipping through all the volume entries for + * a given server/partition. + * + * Side Effects: + * As advertised. + *------------------------------------------------------------------------*/ + +int +UV_XListVolumes(afs_int32 a_serverID, afs_int32 a_partID, int a_all, + struct volintXInfo **a_resultPP, + afs_int32 * a_numEntsInResultP) +{ + struct rx_connection *rxConnP; /*Ptr to the Rx connection involved */ + afs_int32 code; /*Error code to return */ + volXEntries volumeXInfo; /*Area for returned extended vol info */ + + /* + * Set up our error code and the area for returned extended volume info. + * We set the val field to a null pointer as a hint for the stub to + * allocate space. + */ + code = 0; + *a_numEntsInResultP = 0; + *a_resultPP = (volintXInfo *) 0; + volumeXInfo.volXEntries_val = (volintXInfo *) 0; + volumeXInfo.volXEntries_len = 0; + + /* + * Bind to the Volume Server port on the File Server machine in question, + * then go for it. + */ + rxConnP = UV_Bind(a_serverID, AFSCONF_VOLUMEPORT); + code = AFSVolXListVolumes(rxConnP, a_partID, a_all, &volumeXInfo); + if (code) + fprintf(STDERR, "[UV_XListVolumes] Couldn't fetch volume list\n"); + else { + /* + * We got the info; pull out the pointer to where the results lie + * and how many entries are there. + */ + *a_resultPP = volumeXInfo.volXEntries_val; + *a_numEntsInResultP = volumeXInfo.volXEntries_len; + } + + /* + * If we got an Rx connection, throw it away. + */ + if (rxConnP) + rx_DestroyConnection(rxConnP); + + PrintError("", code); + return (code); +} /*UV_XListVolumes */ + +/* get all the information about volume on and */ +int +UV_ListOneVolume(afs_int32 aserver, afs_int32 apart, afs_int32 volid, + struct volintInfo **resultPtr) +{ + struct rx_connection *aconn; + afs_int32 code = 0; + volEntries volumeInfo; + + code = 0; + + *resultPtr = (volintInfo *) 0; + volumeInfo.volEntries_val = (volintInfo *) 0; /*this hints the stub to allocate space */ + volumeInfo.volEntries_len = 0; + + aconn = UV_Bind(aserver, AFSCONF_VOLUMEPORT); + code = AFSVolListOneVolume(aconn, apart, volid, &volumeInfo); + if (code) { + fprintf(STDERR, + "Could not fetch the information about volume %lu from the server\n", + (unsigned long)volid); + } else { + *resultPtr = volumeInfo.volEntries_val; + + } + + if (aconn) + rx_DestroyConnection(aconn); + PrintError("", code); + return code; +} + +/*------------------------------------------------------------------------ + * EXPORTED UV_XListOneVolume + * + * Description: + * List the extended information for a volume on a particular File + * Server and partition. + * + * Arguments: + * a_serverID : Address of the File Server for which we want + * extended volume info. + * a_partID : Partition for which we want the extended + * volume info. + * a_volID : Volume ID for which we want the info. + * a_resultPP : Ptr to the address of the area containing + * the returned volume info. + * + * Returns: + * 0 on success, + * Otherise, the return value of AFSVolXListOneVolume. + * + * Environment: + * This routine is closely related to UV_ListOneVolume, which returns + * only the standard level of detail on the chosen AFS volume. + * + * Side Effects: + * As advertised. + *------------------------------------------------------------------------*/ + +int +UV_XListOneVolume(afs_int32 a_serverID, afs_int32 a_partID, afs_int32 a_volID, + struct volintXInfo **a_resultPP) +{ + struct rx_connection *rxConnP; /*Rx connection to Volume Server */ + afs_int32 code; /*Error code */ + volXEntries volumeXInfo; /*Area for returned info */ + + /* + * Set up our error code, and the area we're in which we are returning + * the info. Setting the val field to a null pointer tells the stub + * to allocate space for us. + */ + code = 0; + *a_resultPP = (volintXInfo *) 0; + volumeXInfo.volXEntries_val = (volintXInfo *) 0; + volumeXInfo.volXEntries_len = 0; + + /* + * Bind to the Volume Server port on the File Server machine in question, + * then go for it. + */ + rxConnP = UV_Bind(a_serverID, AFSCONF_VOLUMEPORT); + code = AFSVolXListOneVolume(rxConnP, a_partID, a_volID, &volumeXInfo); + if (code) + fprintf(STDERR, + "[UV_XListOneVolume] Couldn't fetch the volume information\n"); + else + /* + * We got the info; pull out the pointer to where the results lie. + */ + *a_resultPP = volumeXInfo.volXEntries_val; + + /* + * If we got an Rx connection, throw it away. + */ + if (rxConnP) + rx_DestroyConnection(rxConnP); + + PrintError("", code); + return code; +} + +/* CheckVolume() + * Given a volume we read from a partition, check if it is + * represented in the VLDB correctly. + * + * The VLDB is looked up by the RW volume id (not its name). + * The RW contains the true name of the volume (BK and RO set + * the name in the VLDB only on creation of the VLDB entry). + * We want rules strict enough that when we check all volumes + * on one partition, it does not need to be done again. IE: + * two volumes on different partitions won't constantly + * change a VLDB entry away from what the other set. + * For RW and BK volumes, we will always check the VLDB to see + * if the two exist on the server/partition. May seem redundant, + * but this is an easy check of the VLDB. IE: if the VLDB entry + * says the BK exists but no BK volume is there, we will detect + * this when we check the RW volume. + * VLDB entries are locked only when a change needs to be done. + * Output changed to look a lot like the "vos syncserv" otuput. + */ +static afs_int32 +CheckVolume(volintInfo * volumeinfo, afs_int32 aserver, afs_int32 apart, + afs_int32 * modentry, afs_uint32 * maxvolid) +{ + int idx, j; + afs_int32 code, error = 0; + struct nvldbentry entry, storeEntry; + char pname[10]; + int pass = 0, islocked = 0, createentry, addvolume, modified, mod; + afs_int32 rwvolid; + + if (modentry) + *modentry = 0; + rwvolid = + ((volumeinfo->type == + RWVOL) ? volumeinfo->volid : volumeinfo->parentID); + + retry: + /* Check to see if the VLDB is ok without locking it (pass 1). + * If it will change, then lock the VLDB entry, read it again, + * then make the changes to it (pass 2). + */ + if (++pass == 2) { + code = ubik_Call(VL_SetLock, cstruct, 0, rwvolid, RWVOL, VLOP_DELETE); + if (code) { + fprintf(STDERR, "Could not lock VLDB entry for %lu\n", + (unsigned long)rwvolid); + ERROR_EXIT(code); + } + islocked = 1; + } + + createentry = 0; /* Do we need to create a VLDB entry */ + addvolume = 0; /* Add this volume to the VLDB entry */ + modified = 0; /* The VLDB entry was modified */ + + /* Read the entry from VLDB by its RW volume id */ + code = VLDB_GetEntryByID(rwvolid, RWVOL, &entry); + if (code) { + if (code != VL_NOENT) { + fprintf(STDOUT, + "Could not retreive the VLDB entry for volume %lu \n", + (unsigned long)rwvolid); + ERROR_EXIT(code); + } + + memset(&entry, 0, sizeof(entry)); + vsu_ExtractName(entry.name, volumeinfo->name); /* Store name of RW */ + + createentry = 1; + } else { + MapHostToNetwork(&entry); + } + + if (verbose && (pass == 1)) { + fprintf(STDOUT, "_______________________________\n"); + fprintf(STDOUT, "\n-- status before -- \n"); + if (createentry) { + fprintf(STDOUT, "\n**does not exist**\n"); + } else { + if ((entry.flags & RW_EXISTS) || (entry.flags & RO_EXISTS) + || (entry.flags & BACK_EXISTS)) + EnumerateEntry(&entry); + } + fprintf(STDOUT, "\n"); + } + + if (volumeinfo->type == RWVOL) { /* RW volume exists */ + if (createentry) { + idx = 0; + entry.nServers = 1; + addvolume++; + } else { + /* Check existence of RW and BK volumes */ + code = CheckVldbRWBK(&entry, &mod); + if (code) + ERROR_EXIT(code); + if (mod) + modified++; + + idx = Lp_GetRwIndex(&entry); + if (idx == -1) { /* RW index not found in the VLDB entry */ + idx = entry.nServers; /* put it into next index */ + entry.nServers++; + addvolume++; + } else { /* RW index found in the VLDB entry. */ + /* Verify if this volume's location matches where the VLDB says it is */ + if (!Lp_Match(aserver, apart, &entry)) { + if (entry.flags & RW_EXISTS) { + /* The RW volume exists elsewhere - report this one a duplicate */ + if (pass == 1) { + MapPartIdIntoName(apart, pname); + fprintf(STDERR, + "*** Warning: Orphaned RW volume %lu exists on %s %s\n", + (unsigned long)rwvolid, + hostutil_GetNameByINet(aserver), pname); + MapPartIdIntoName(entry.serverPartition[idx], + pname); + fprintf(STDERR, + " VLDB reports RW volume %lu exists on %s %s\n", + (unsigned long)rwvolid, + hostutil_GetNameByINet(entry. + serverNumber[idx]), + pname); + } + } else { + /* The RW volume does not exist - have VLDB point to this one */ + addvolume++; + + /* Check for orphaned BK volume on old partition */ + if (entry.flags & BACK_EXISTS) { + if (pass == 1) { + MapPartIdIntoName(entry.serverPartition[idx], + pname); + fprintf(STDERR, + "*** Warning: Orphaned BK volume %u exists on %s %s\n", + entry.volumeId[BACKVOL], + hostutil_GetNameByINet(entry. + serverNumber + [idx]), pname); + MapPartIdIntoName(apart, pname); + fprintf(STDERR, + " VLDB reports its RW volume %lu exists on %s %s\n", + (unsigned long)rwvolid, + hostutil_GetNameByINet(aserver), + pname); + } + } + } + } else { + /* Volume location matches the VLDB location */ + if ((volumeinfo->backupID && !entry.volumeId[BACKVOL]) + || (volumeinfo->cloneID && !entry.volumeId[ROVOL]) + || + (strncmp + (entry.name, volumeinfo->name, + VOLSER_OLDMAXVOLNAME) != 0)) { + addvolume++; + } + } + } + } + + if (addvolume) { + entry.flags |= RW_EXISTS; + entry.volumeId[RWVOL] = rwvolid; + if (!entry.volumeId[BACKVOL]) + entry.volumeId[BACKVOL] = volumeinfo->backupID; + if (!entry.volumeId[ROVOL]) + entry.volumeId[ROVOL] = volumeinfo->cloneID; + + entry.serverFlags[idx] = ITSRWVOL; + entry.serverNumber[idx] = aserver; + entry.serverPartition[idx] = apart; + strncpy(entry.name, volumeinfo->name, VOLSER_OLDMAXVOLNAME); + + modified++; + + /* One last check - to update BK if need to */ + code = CheckVldbRWBK(&entry, &mod); + if (code) + ERROR_EXIT(code); + if (mod) + modified++; + } + } + + else if (volumeinfo->type == BACKVOL) { /* A BK volume */ + if (createentry) { + idx = 0; + entry.nServers = 1; + addvolume++; + } else { + /* Check existence of RW and BK volumes */ + code = CheckVldbRWBK(&entry, &mod); + if (code) + ERROR_EXIT(code); + if (mod) + modified++; + + idx = Lp_GetRwIndex(&entry); + if (idx == -1) { /* RW index not found in the VLDB entry */ + idx = entry.nServers; /* Put it into next index */ + entry.nServers++; + addvolume++; + } else { /* RW index found in the VLDB entry */ + /* Verify if this volume's location matches where the VLDB says it is */ + if (!Lp_Match(aserver, apart, &entry)) { + /* VLDB says RW and/or BK is elsewhere - report this BK volume orphaned */ + if (pass == 1) { + MapPartIdIntoName(apart, pname); + fprintf(STDERR, + "*** Warning: Orphaned BK volume %lu exists on %s %s\n", + (unsigned long)volumeinfo->volid, + hostutil_GetNameByINet(aserver), pname); + MapPartIdIntoName(entry.serverPartition[idx], pname); + fprintf(STDERR, + " VLDB reports its RW/BK volume %lu exists on %s %s\n", + (unsigned long)rwvolid, + hostutil_GetNameByINet(entry. + serverNumber[idx]), + pname); + } + } else { + if (volumeinfo->volid != entry.volumeId[BACKVOL]) { + if (!(entry.flags & BACK_EXISTS)) { + addvolume++; + } else if (volumeinfo->volid > + entry.volumeId[BACKVOL]) { + addvolume++; + + if (pass == 1) { + MapPartIdIntoName(entry.serverPartition[idx], + pname); + fprintf(STDERR, + "*** Warning: Orphaned BK volume %u exists on %s %s\n", + entry.volumeId[BACKVOL], + hostutil_GetNameByINet(aserver), + pname); + fprintf(STDERR, + " VLDB reports its BK volume ID is %lu\n", + (unsigned long)volumeinfo->volid); + } + } else { + if (pass == 1) { + MapPartIdIntoName(entry.serverPartition[idx], + pname); + fprintf(STDERR, + "*** Warning: Orphaned BK volume %lu exists on %s %s\n", + (unsigned long)volumeinfo->volid, + hostutil_GetNameByINet(aserver), + pname); + fprintf(STDERR, + " VLDB reports its BK volume ID is %u\n", + entry.volumeId[BACKVOL]); + } + } + } else if (!entry.volumeId[BACKVOL]) { + addvolume++; + } + } + } + } + if (addvolume) { + entry.flags |= BACK_EXISTS; + entry.volumeId[RWVOL] = rwvolid; + entry.volumeId[BACKVOL] = volumeinfo->volid; + + entry.serverNumber[idx] = aserver; + entry.serverPartition[idx] = apart; + entry.serverFlags[idx] = ITSRWVOL; + + modified++; + } + } + + else if (volumeinfo->type == ROVOL) { /* A RO volume */ + if (volumeinfo->volid == entry.volumeId[ROVOL]) { + /* This is a quick check to see if the RO entry exists in the + * VLDB so we avoid the CheckVldbRO() call (which checks if each + * RO volume listed in the VLDB exists). + */ + idx = Lp_ROMatch(aserver, apart, &entry) - 1; + if (idx == -1) { + idx = entry.nServers; + entry.nServers++; + addvolume++; + } else { + if (!(entry.flags & RO_EXISTS)) { + addvolume++; + } + } + } else { + /* Before we correct the VLDB entry, make sure all the + * ROs listed in the VLDB exist. + */ + code = CheckVldbRO(&entry, &mod); + if (code) + ERROR_EXIT(code); + if (mod) + modified++; + + if (!(entry.flags & RO_EXISTS)) { + /* No RO exists in the VLDB entry - add this one */ + idx = entry.nServers; + entry.nServers++; + addvolume++; + } else if (volumeinfo->volid > entry.volumeId[ROVOL]) { + /* The volume headers's RO ID does not match that in the VLDB entry, + * and the vol hdr's ID is greater (implies more recent). So delete + * all the RO volumes listed in VLDB entry and add this volume. + */ + for (j = 0; j < entry.nServers; j++) { + if (entry.serverFlags[j] & ITSROVOL) { + /* Verify this volume exists and print message we are orphaning it */ + if (pass == 1) { + MapPartIdIntoName(apart, pname); + fprintf(STDERR, + "*** Warning: Orphaned RO volume %u exists on %s %s\n", + entry.volumeId[ROVOL], + hostutil_GetNameByINet(entry. + serverNumber[j]), + pname); + fprintf(STDERR, + " VLDB reports its RO volume ID is %lu\n", + (unsigned long)volumeinfo->volid); + } + + Lp_SetRWValue(entry, entry.serverNumber[idx], + entry.serverPartition[idx], 0L, 0L); + entry.nServers--; + modified++; + j--; + } + } + + idx = entry.nServers; + entry.nServers++; + addvolume++; + } else if (volumeinfo->volid < entry.volumeId[ROVOL]) { + /* The volume headers's RO ID does not match that in the VLDB entry, + * and the vol hdr's ID is lower (implies its older). So orphan it. + */ + if (pass == 1) { + MapPartIdIntoName(apart, pname); + fprintf(STDERR, + "*** Warning: Orphaned RO volume %lu exists on %s %s\n", + (unsigned long)volumeinfo->volid, + hostutil_GetNameByINet(aserver), pname); + fprintf(STDERR, + " VLDB reports its RO volume ID is %u\n", + entry.volumeId[ROVOL]); + } + } else { + /* The RO volume ID in the volume header match that in the VLDB entry, + * and there exist RO volumes in the VLDB entry. See if any of them + * are this one. If not, then we add it. + */ + idx = Lp_ROMatch(aserver, apart, &entry) - 1; + if (idx == -1) { + idx = entry.nServers; + entry.nServers++; + addvolume++; + } + } + } + + if (addvolume) { + entry.flags |= RO_EXISTS; + entry.volumeId[RWVOL] = rwvolid; + entry.volumeId[ROVOL] = volumeinfo->volid; + + entry.serverNumber[idx] = aserver; + entry.serverPartition[idx] = apart; + entry.serverFlags[idx] = ITSROVOL; + + modified++; + } + } + + /* Remember largest volume id */ + if (entry.volumeId[ROVOL] > *maxvolid) + *maxvolid = entry.volumeId[ROVOL]; + if (entry.volumeId[BACKVOL] > *maxvolid) + *maxvolid = entry.volumeId[BACKVOL]; + if (entry.volumeId[RWVOL] > *maxvolid) + *maxvolid = entry.volumeId[RWVOL]; + + if (modified) { + MapNetworkToHost(&entry, &storeEntry); + + if (createentry) { + code = VLDB_CreateEntry(&storeEntry); + if (code) { + fprintf(STDOUT, + "Could not create a VLDB entry for the volume %lu\n", + (unsigned long)rwvolid); + ERROR_EXIT(code); + } + } else { + if (pass == 1) + goto retry; + code = + VLDB_ReplaceEntry(rwvolid, RWVOL, &storeEntry, + LOCKREL_OPCODE | LOCKREL_AFSID | + LOCKREL_TIMESTAMP); + if (code) { + fprintf(STDERR, "Could not update entry for %lu\n", + (unsigned long)rwvolid); + ERROR_EXIT(code); + } + } + if (modentry) + *modentry = modified; + } else if (pass == 2) { + code = + ubik_Call(VL_ReleaseLock, cstruct, 0, rwvolid, RWVOL, + LOCKREL_OPCODE | LOCKREL_AFSID | LOCKREL_TIMESTAMP); + if (code) { + PrintError("Could not unlock VLDB entry ", code); + } + } + + if (verbose) { + fprintf(STDOUT, "-- status after --\n"); + if (modified) + EnumerateEntry(&entry); + else + fprintf(STDOUT, "\n**no change**\n"); + } + + error_exit: + VPRINT("\n_______________________________\n"); + return (error); +} + +int +sortVolumes(const void *a, const void *b) +{ + volintInfo *v1 = (volintInfo *) a; + volintInfo *v2 = (volintInfo *) b; + afs_int32 rwvolid1, rwvolid2; + + rwvolid1 = ((v1->type == RWVOL) ? v1->volid : v1->parentID); + rwvolid2 = ((v2->type == RWVOL) ? v2->volid : v2->parentID); + + if (rwvolid1 > rwvolid2) + return -1; /* lower RW id goes first */ + if (rwvolid1 < rwvolid2) + return 1; + + if (v1->type == RWVOL) + return -1; /* RW vols go first */ + if (v2->type == RWVOL) + return 1; + + if ((v1->type == BACKVOL) && (v2->type == ROVOL)) + return -1; /* BK vols next */ + if ((v1->type == ROVOL) && (v2->type == BACKVOL)) + return 1; + + if (v1->volid < v2->volid) + return 1; /* larger volids first */ + if (v1->volid > v2->volid) + return -1; + return 0; +} + +/* UV_SyncVolume() + * Synchronise (if flags = 1) . + * Synchronize an individual volume against a sever and partition. + * Checks the VLDB entry (similar to syncserv) as well as checks + * if the volume exists on specified servers (similar to syncvldb). + */ +int +UV_SyncVolume(afs_int32 aserver, afs_int32 apart, char *avolname, int flags) +{ + struct rx_connection *aconn = 0; + afs_int32 j, k, code, vcode, error = 0; + afs_int32 tverbose, mod, modified = 0; + struct nvldbentry vldbentry; + afs_int32 volumeid = 0; + volEntries volumeInfo; + struct partList PartList; + afs_int32 pcnt, rv; + afs_int32 maxvolid = 0; + + volumeInfo.volEntries_val = (volintInfo *) 0; + volumeInfo.volEntries_len = 0; + + if (!aserver && flags) { + /* fprintf(STDERR,"Partition option requires a server option\n"); */ + ERROR_EXIT(EINVAL); + } + + /* Turn verbose logging off and do our own verbose logging */ + tverbose = verbose; + verbose = 0; + + /* Read the VLDB entry */ + vcode = VLDB_GetEntryByName(avolname, &vldbentry); + if (vcode && (vcode != VL_NOENT)) { + fprintf(STDERR, "Could not access the VLDB for volume %s\n", + avolname); + ERROR_EXIT(vcode); + } else if (!vcode) { + MapHostToNetwork(&vldbentry); + } + + if (tverbose) { + fprintf(STDOUT, "Processing VLDB entry %s ...\n", avolname); + fprintf(STDOUT, "_______________________________\n"); + fprintf(STDOUT, "\n-- status before -- \n"); + if (vcode) { + fprintf(STDOUT, "\n**does not exist**\n"); + } else { + if ((vldbentry.flags & RW_EXISTS) || (vldbentry.flags & RO_EXISTS) + || (vldbentry.flags & BACK_EXISTS)) + EnumerateEntry(&vldbentry); + } + fprintf(STDOUT, "\n"); + } + + /* Verify that all of the VLDB entries exist on the repective servers + * and partitions (this does not require that avolname be a volume ID). + * Equivalent to a syncserv. + */ + if (!vcode) { + code = CheckVldb(&vldbentry, &mod); + if (code) { + fprintf(STDERR, "Could not process VLDB entry for volume %s\n", + vldbentry.name); + ERROR_EXIT(code); + } + if (mod) + modified++; + } + + /* If aserver is given, we will search for the desired volume on it */ + if (aserver) { + /* Generate array of partitions on the server that we will check */ + if (!flags) { + code = UV_ListPartitions(aserver, &PartList, &pcnt); + if (code) { + fprintf(STDERR, + "Could not fetch the list of partitions from the server\n"); + ERROR_EXIT(code); + } + } else { + PartList.partId[0] = apart; + pcnt = 1; + } + + aconn = UV_Bind(aserver, AFSCONF_VOLUMEPORT); + + /* If a volume ID were given, search for it on each partition */ + if ((volumeid = atol(avolname))) { + for (j = 0; j < pcnt; j++) { + code = + AFSVolListOneVolume(aconn, PartList.partId[j], volumeid, + &volumeInfo); + if (code) { + if (code != ENODEV) { + fprintf(STDERR, "Could not query server\n"); + ERROR_EXIT(code); + } + } else { + /* Found one, sync it with VLDB entry */ + code = + CheckVolume(volumeInfo.volEntries_val, aserver, + PartList.partId[j], &mod, &maxvolid); + if (code) + ERROR_EXIT(code); + if (mod) + modified++; + } + + if (volumeInfo.volEntries_val) + free(volumeInfo.volEntries_val); + volumeInfo.volEntries_val = (volintInfo *) 0; + volumeInfo.volEntries_len = 0; + } + } + + /* Check to see if the RW, BK, and RO IDs exist on any + * partitions. We get the volume IDs from the VLDB. + */ + rv = 1; /* Read the VLDB entry ? */ + for (j = 0; j < MAXTYPES; j++) { /* for RW, RO, and BK IDs */ + if (rv) { + vcode = VLDB_GetEntryByName(avolname, &vldbentry); + if (vcode) { + if (vcode == VL_NOENT) + break; + fprintf(STDERR, + "Could not access the VLDB for volume %s\n", + avolname); + ERROR_EXIT(vcode); + } + rv = 0; + } + + if (vldbentry.volumeId[j] == 0) + continue; + + for (k = 0; k < pcnt; k++) { /* For each partition */ + volumeInfo.volEntries_val = (volintInfo *) 0; + volumeInfo.volEntries_len = 0; + code = + AFSVolListOneVolume(aconn, PartList.partId[k], + vldbentry.volumeId[j], &volumeInfo); + if (code) { + if (code != ENODEV) { + fprintf(STDERR, "Could not query server\n"); + ERROR_EXIT(code); + } + } else { + /* Found one, sync it with VLDB entry */ + code = + CheckVolume(volumeInfo.volEntries_val, aserver, + PartList.partId[k], &mod, &maxvolid); + if (code) + ERROR_EXIT(code); + if (mod) + modified++, rv++; + } + + if (volumeInfo.volEntries_val) + free(volumeInfo.volEntries_val); + volumeInfo.volEntries_val = (volintInfo *) 0; + volumeInfo.volEntries_len = 0; + } + } + } + + /* if (aserver) */ + /* If verbose output, print a summary of what changed */ + if (tverbose) { + fprintf(STDOUT, "-- status after --\n"); + code = VLDB_GetEntryByName(avolname, &vldbentry); + if (code && (code != VL_NOENT)) { + fprintf(STDERR, "Could not access the VLDB for volume %s\n", + avolname); + ERROR_EXIT(code); + } + if (modified && (code == VL_NOENT)) { + fprintf(STDOUT, "\n**entry deleted**\n"); + } else if (modified) { + EnumerateEntry(&vldbentry); + } else { + fprintf(STDOUT, "\n**no change**\n"); + } + fprintf(STDOUT, "\n_______________________________\n"); + } + + error_exit: + /* Now check if the maxvolid is larger than that stored in the VLDB */ + if (maxvolid) { + afs_int32 maxvldbid = 0; + code = ubik_Call(VL_GetNewVolumeId, cstruct, 0, 0, &maxvldbid); + if (code) { + fprintf(STDERR, + "Could not get the highest allocated volume id from the VLDB\n"); + if (!error) + error = code; + } else if (maxvolid > maxvldbid) { + afs_uint32 id, nid; + id = maxvolid - maxvldbid + 1; + code = ubik_Call(VL_GetNewVolumeId, cstruct, 0, id, &nid); + if (code) { + fprintf(STDERR, + "Error in increasing highest allocated volume id in VLDB\n"); + if (!error) + error = code; + } + } + } + + verbose = tverbose; + if (verbose) { + if (error) + fprintf(STDOUT, "...error encountered"); + else + fprintf(STDOUT, "...done entry\n"); + } + if (aconn) + rx_DestroyConnection(aconn); + if (volumeInfo.volEntries_val) + free(volumeInfo.volEntries_val); + + PrintError("", error); + return error; +} + +/* UV_SyncVldb() + * Synchronise vldb with the file server and, + * optionally, . + */ +int +UV_SyncVldb(afs_int32 aserver, afs_int32 apart, int flags, int force) +{ + struct rx_connection *aconn; + afs_int32 code, error = 0; + int i, j, pfail; + volEntries volumeInfo; + struct partList PartList; + afs_int32 pcnt; + char pname[10]; + volintInfo *vi; + afs_int32 failures = 0, modifications = 0, tentries = 0; + afs_int32 modified; + afs_uint32 maxvolid = 0; + + volumeInfo.volEntries_val = (volintInfo *) 0; + volumeInfo.volEntries_len = 0; + + aconn = UV_Bind(aserver, AFSCONF_VOLUMEPORT); + + /* Generate array of partitions to check */ + if (!flags) { + code = UV_ListPartitions(aserver, &PartList, &pcnt); + if (code) { + fprintf(STDERR, + "Could not fetch the list of partitions from the server\n"); + ERROR_EXIT(code); + } + } else { + PartList.partId[0] = apart; + pcnt = 1; + } + + VPRINT("Processing volume entries ...\n"); + + /* Step through the array of partitions */ + for (i = 0; i < pcnt; i++) { + apart = PartList.partId[i]; + MapPartIdIntoName(apart, pname); + + volumeInfo.volEntries_val = (volintInfo *) 0; + volumeInfo.volEntries_len = 0; + code = AFSVolListVolumes(aconn, apart, 1, &volumeInfo); + if (code) { + fprintf(STDERR, + "Could not fetch the list of volumes from the server\n"); + ERROR_EXIT(code); + } + + /* May want to sort the entries: RW, BK (high to low), RO (high to low) */ + qsort((char *)volumeInfo.volEntries_val, volumeInfo.volEntries_len, + sizeof(volintInfo), sortVolumes); + + pfail = 0; + for (vi = volumeInfo.volEntries_val, j = 0; + j < volumeInfo.volEntries_len; j++, vi++) { + if (!vi->status) + continue; + + tentries++; + + if (verbose) { + fprintf(STDOUT, + "Processing volume entry %d: %s (%lu) on server %s %s...\n", + j + 1, vi->name, (unsigned long)vi->volid, + hostutil_GetNameByINet(aserver), pname); + fflush(STDOUT); + } + + code = CheckVolume(vi, aserver, apart, &modified, &maxvolid); + if (code) { + PrintError("", code); + failures++; + pfail++; + } else if (modified) { + modifications++; + } + + if (verbose) { + if (code) { + fprintf(STDOUT, "...error encountered\n\n"); + } else { + fprintf(STDOUT, "...done entry %d\n\n", j + 1); + } + } + } + + if (pfail) { + fprintf(STDERR, + "Could not process entries on server %s partition %s\n", + hostutil_GetNameByINet(aserver), pname); + } + if (volumeInfo.volEntries_val) { + free(volumeInfo.volEntries_val); + volumeInfo.volEntries_val = 0; + } + + } /* thru all partitions */ + + VPRINT3("Total entries: %u, Failed to process %d, Changed %d\n", tentries, + failures, modifications); + + error_exit: + /* Now check if the maxvolid is larger than that stored in the VLDB */ + if (maxvolid) { + afs_uint32 maxvldbid = 0; + code = ubik_Call(VL_GetNewVolumeId, cstruct, 0, 0, &maxvldbid); + if (code) { + fprintf(STDERR, + "Could not get the highest allocated volume id from the VLDB\n"); + if (!error) + error = code; + } else if (maxvolid > maxvldbid) { + afs_uint32 id, nid; + id = maxvolid - maxvldbid + 1; + code = ubik_Call(VL_GetNewVolumeId, cstruct, 0, id, &nid); + if (code) { + fprintf(STDERR, + "Error in increasing highest allocated volume id in VLDB\n"); + if (!error) + error = code; + } + } + } + + if (aconn) + rx_DestroyConnection(aconn); + if (volumeInfo.volEntries_val) + free(volumeInfo.volEntries_val); + PrintError("", error); + return (error); +} + +/* VolumeExists() + * Determine if a volume exists on a server and partition. + * Try creating a transaction on the volume. If we can, + * the volume exists, if not, then return the error code. + * Some error codes mean the volume is unavailable but + * still exists - so we catch these error codes. + */ +afs_int32 +VolumeExists(afs_int32 server, afs_int32 partition, afs_int32 volumeid) +{ + struct rx_connection *conn = (struct rx_connection *)0; + afs_int32 code = -1; + volEntries volumeInfo; + + conn = UV_Bind(server, AFSCONF_VOLUMEPORT); + if (conn) { + volumeInfo.volEntries_val = (volintInfo *) 0; + volumeInfo.volEntries_len = 0; + code = AFSVolListOneVolume(conn, partition, volumeid, &volumeInfo); + if (volumeInfo.volEntries_val) + free(volumeInfo.volEntries_val); + if (code == VOLSERILLEGAL_PARTITION) + code = ENODEV; + rx_DestroyConnection(conn); + } + return code; +} + +/* CheckVldbRWBK() + * + */ +afs_int32 +CheckVldbRWBK(struct nvldbentry * entry, afs_int32 * modified) +{ + int modentry = 0; + int idx; + afs_int32 code, error = 0; + char pname[10]; + + if (modified) + *modified = 0; + idx = Lp_GetRwIndex(entry); + + /* Check to see if the RW volume exists and set the RW_EXISTS + * flag accordingly. + */ + if (idx == -1) { /* Did not find a RW entry */ + if (entry->flags & RW_EXISTS) { /* ... yet entry says RW exists */ + entry->flags &= ~RW_EXISTS; /* ... so say RW does not exist */ + modentry++; + } + } else { + code = + VolumeExists(entry->serverNumber[idx], + entry->serverPartition[idx], entry->volumeId[RWVOL]); + if (code == 0) { /* RW volume exists */ + if (!(entry->flags & RW_EXISTS)) { /* ... yet entry says RW does not exist */ + entry->flags |= RW_EXISTS; /* ... so say RW does exist */ + modentry++; + } + } else if (code == ENODEV) { /* RW volume does not exist */ + if (entry->flags & RW_EXISTS) { /* ... yet entry says RW exists */ + entry->flags &= ~RW_EXISTS; /* ... so say RW does not exist */ + modentry++; + } + } else { + /* If VLDB says it didn't exist, then ignore error */ + if (entry->flags & RW_EXISTS) { + MapPartIdIntoName(entry->serverPartition[idx], pname); + fprintf(STDERR, + "Transaction call failed for RW volume %u on server %s %s\n", + entry->volumeId[RWVOL], + hostutil_GetNameByINet(entry->serverNumber[idx]), + pname); + ERROR_EXIT(code); + } + } + } + + /* Check to see if the BK volume exists and set the BACK_EXISTS + * flag accordingly. idx already ponts to the RW entry. + */ + if (idx == -1) { /* Did not find a RW entry */ + if (entry->flags & BACK_EXISTS) { /* ... yet entry says BK exists */ + entry->flags &= ~BACK_EXISTS; /* ... so say BK does not exist */ + modentry++; + } + } else { /* Found a RW entry */ + code = + VolumeExists(entry->serverNumber[idx], + entry->serverPartition[idx], + entry->volumeId[BACKVOL]); + if (code == 0) { /* BK volume exists */ + if (!(entry->flags & BACK_EXISTS)) { /* ... yet entry says BK does not exist */ + entry->flags |= BACK_EXISTS; /* ... so say BK does exist */ + modentry++; + } + } else if (code == ENODEV) { /* BK volume does not exist */ + if (entry->flags & BACK_EXISTS) { /* ... yet entry says BK exists */ + entry->flags &= ~BACK_EXISTS; /* ... so say BK does not exist */ + modentry++; + } + } else { + /* If VLDB says it didn't exist, then ignore error */ + if (entry->flags & BACK_EXISTS) { + MapPartIdIntoName(entry->serverPartition[idx], pname); + fprintf(STDERR, + "Transaction call failed for BK volume %u on server %s %s\n", + entry->volumeId[BACKVOL], + hostutil_GetNameByINet(entry->serverNumber[idx]), + pname); + ERROR_EXIT(code); + } + } + } + + /* If there is an idx but the BK and RW volumes no + * longer exist, then remove the RW entry. + */ + if ((idx != -1) && !(entry->flags & RW_EXISTS) + && !(entry->flags & BACK_EXISTS)) { + Lp_SetRWValue(entry, entry->serverNumber[idx], + entry->serverPartition[idx], 0L, 0L); + entry->nServers--; + modentry++; + } + + error_exit: + if (modified) + *modified = modentry; + return (error); +} + +int +CheckVldbRO(struct nvldbentry *entry, afs_int32 * modified) +{ + int idx; + int foundro = 0, modentry = 0; + afs_int32 code, error = 0; + char pname[10]; + + if (modified) + *modified = 0; + + /* Check to see if the RO volumes exist and set the RO_EXISTS + * flag accordingly. + */ + for (idx = 0; idx < entry->nServers; idx++) { + if (!(entry->serverFlags[idx] & ITSROVOL)) { + continue; /* not a RO */ + } + + code = + VolumeExists(entry->serverNumber[idx], + entry->serverPartition[idx], entry->volumeId[ROVOL]); + if (code == 0) { /* RO volume exists */ + foundro++; + } else if (code == ENODEV) { /* RW volume does not exist */ + Lp_SetROValue(entry, entry->serverNumber[idx], + entry->serverPartition[idx], 0L, 0L); + entry->nServers--; + idx--; + modentry++; + } else { + MapPartIdIntoName(entry->serverPartition[idx], pname); + fprintf(STDERR, + "Transaction call failed for RO %u on server %s %s\n", + entry->volumeId[ROVOL], + hostutil_GetNameByINet(entry->serverNumber[idx]), pname); + ERROR_EXIT(code); + } + } + + if (foundro) { /* A RO volume exists */ + if (!(entry->flags & RO_EXISTS)) { /* ... yet entry says RW does not exist */ + entry->flags |= RO_EXISTS; /* ... so say RW does exist */ + modentry++; + } + } else { /* A RO volume does not exist */ + if (entry->flags & RO_EXISTS) { /* ... yet entry says RO exists */ + entry->flags &= ~RO_EXISTS; /* ... so say RO does not exist */ + modentry++; + } + } + + error_exit: + if (modified) + *modified = modentry; + return (error); +} + +/* CheckVldb() + * Ensure that matches with the info on file servers + */ +afs_int32 +CheckVldb(struct nvldbentry * entry, afs_int32 * modified) +{ + afs_int32 code, error = 0; + struct nvldbentry storeEntry; + int islocked = 0, mod, modentry, delentry = 0; + int pass = 0; + + if (modified) + *modified = 0; + if (verbose) { + fprintf(STDOUT, "_______________________________\n"); + fprintf(STDOUT, "\n-- status before -- \n"); + if ((entry->flags & RW_EXISTS) || (entry->flags & RO_EXISTS) + || (entry->flags & BACK_EXISTS)) + EnumerateEntry(entry); + fprintf(STDOUT, "\n"); + } + + if (strlen(entry->name) > (VOLSER_OLDMAXVOLNAME - 10)) { + fprintf(STDERR, "Volume name %s exceeds limit of %d characters\n", + entry->name, VOLSER_OLDMAXVOLNAME - 10); + } + + retry: + /* Check to see if the VLDB is ok without locking it (pass 1). + * If it will change, then lock the VLDB entry, read it again, + * then make the changes to it (pass 2). + */ + if (++pass == 2) { + code = + ubik_Call(VL_SetLock, cstruct, 0, entry->volumeId[RWVOL], RWVOL, + VLOP_DELETE); + if (code) { + fprintf(STDERR, "Could not lock VLDB entry for %u \n", + entry->volumeId[RWVOL]); + ERROR_EXIT(code); + } + islocked = 1; + + code = VLDB_GetEntryByID(entry->volumeId[RWVOL], RWVOL, entry); + if (code) { + fprintf(STDERR, "Could not read VLDB entry for volume %s\n", + entry->name); + ERROR_EXIT(code); + } else { + MapHostToNetwork(entry); + } + } + + modentry = 0; + + /* Check if the RW and BK entries are ok */ + code = CheckVldbRWBK(entry, &mod); + if (code) + ERROR_EXIT(code); + if (mod && (pass == 1)) + goto retry; + if (mod) + modentry++; + + /* Check if the RO volumes entries are ok */ + code = CheckVldbRO(entry, &mod); + if (code) + ERROR_EXIT(code); + if (mod && (pass == 1)) + goto retry; + if (mod) + modentry++; + + /* The VLDB entry has been updated. If it as been modified, then + * write the entry back out the the VLDB. + */ + if (modentry) { + if (pass == 1) + goto retry; + + if (!(entry->flags & RW_EXISTS) && !(entry->flags & BACK_EXISTS) + && !(entry->flags & RO_EXISTS)) { + /* The RW, BK, nor RO volumes do not exist. Delete the VLDB entry */ + code = + ubik_Call(VL_DeleteEntry, cstruct, 0, entry->volumeId[RWVOL], + RWVOL); + if (code) { + fprintf(STDERR, + "Could not delete VLDB entry for volume %u \n", + entry->volumeId[RWVOL]); + ERROR_EXIT(code); + } + delentry = 1; + } else { + /* Replace old entry with our new one */ + MapNetworkToHost(entry, &storeEntry); + code = + VLDB_ReplaceEntry(entry->volumeId[RWVOL], RWVOL, &storeEntry, + (LOCKREL_OPCODE | LOCKREL_AFSID | + LOCKREL_TIMESTAMP)); + if (code) { + fprintf(STDERR, "Could not update VLDB entry for volume %u\n", + entry->volumeId[RWVOL]); + ERROR_EXIT(code); + } + } + if (modified) + *modified = 1; + islocked = 0; + } + + if (verbose) { + fprintf(STDOUT, "-- status after --\n"); + if (delentry) + fprintf(STDOUT, "\n**entry deleted**\n"); + else if (modentry) + EnumerateEntry(entry); + else + fprintf(STDOUT, "\n**no change**\n"); + } + + error_exit: + VPRINT("\n_______________________________\n"); + + if (islocked) { + code = + ubik_Call(VL_ReleaseLock, cstruct, 0, entry->volumeId[RWVOL], + RWVOL, + (LOCKREL_OPCODE | LOCKREL_AFSID | LOCKREL_TIMESTAMP)); + if (code) { + fprintf(STDERR, + "Could not release lock on VLDB entry for volume %u\n", + entry->volumeId[RWVOL]); + if (!error) + error = code; + } + } + return error; +} + +/* UV_SyncServer() + * Synchronise (if flags = 1) with the VLDB. + */ +int +UV_SyncServer(afs_int32 aserver, afs_int32 apart, int flags, int force) +{ + struct rx_connection *aconn; + afs_int32 code, error = 0; + afs_int32 nentries, tentries = 0; + struct VldbListByAttributes attributes; + nbulkentries arrayEntries; + afs_int32 failures = 0, modified, modifications = 0; + struct nvldbentry *vlentry; + afs_int32 si, nsi, j; + + aconn = UV_Bind(aserver, AFSCONF_VOLUMEPORT); + + /* Set up attributes to search VLDB */ + attributes.server = ntohl(aserver); + attributes.Mask = VLLIST_SERVER; + if (flags) { + attributes.partition = apart; + attributes.Mask |= VLLIST_PARTITION; + } + + VPRINT("Processing VLDB entries ...\n"); + + /* While we need to collect more VLDB entries */ + for (si = 0; si != -1; si = nsi) { + memset(&arrayEntries, 0, sizeof(arrayEntries)); + + /* Collect set of VLDB entries */ + code = + VLDB_ListAttributesN2(&attributes, 0, si, &nentries, + &arrayEntries, &nsi); + if (code == RXGEN_OPCODE) { + code = VLDB_ListAttributes(&attributes, &nentries, &arrayEntries); + nsi = -1; + } + if (code) { + fprintf(STDERR, "Could not access the VLDB for attributes\n"); + ERROR_EXIT(code); + } + tentries += nentries; + + for (j = 0; j < nentries; j++) { + vlentry = &arrayEntries.nbulkentries_val[j]; + MapHostToNetwork(vlentry); + + VPRINT1("Processing VLDB entry %d ...\n", j + 1); + + code = CheckVldb(vlentry, &modified); + if (code) { + PrintError("", code); + fprintf(STDERR, + "Could not process VLDB entry for volume %s\n", + vlentry->name); + failures++; + } else if (modified) { + modifications++; + } + + if (verbose) { + if (code) { + fprintf(STDOUT, "...error encountered\n\n"); + } else { + fprintf(STDOUT, "...done entry %d\n\n", j + 1); + } + } + } + + if (arrayEntries.nbulkentries_val) { + free(arrayEntries.nbulkentries_val); + arrayEntries.nbulkentries_val = 0; + } + } + + VPRINT3("Total entries: %u, Failed to process %d, Changed %d\n", tentries, + failures, modifications); + + error_exit: + if (aconn) + rx_DestroyConnection(aconn); + if (arrayEntries.nbulkentries_val) + free(arrayEntries.nbulkentries_val); + + if (failures) + error = VOLSERFAILEDOP; + return error; +} + +/*rename volume to , changing the names of the related + *readonly and backup volumes. This operation is also idempotent. + *salvager is capable of recovering from rename operation stopping halfway. + *to recover run syncserver on the affected machines,it will force renaming to completion. name clashes should have been detected before calling this proc */ +int +UV_RenameVolume(struct nvldbentry *entry, char oldname[], char newname[]) +{ + struct nvldbentry storeEntry; + afs_int32 vcode, code, rcode, error; + int i, index; + char nameBuffer[256]; + afs_int32 tid; + struct rx_connection *aconn; + int islocked; + + error = 0; + aconn = (struct rx_connection *)0; + tid = 0; + islocked = 0; + + vcode = ubik_Call(VL_SetLock, cstruct, 0, entry->volumeId[RWVOL], RWVOL, VLOP_ADDSITE); /*last param is dummy */ + if (vcode) { + fprintf(STDERR, + " Could not lock the VLDB entry for the volume %u \n", + entry->volumeId[RWVOL]); + error = vcode; + goto rvfail; + } + islocked = 1; + strncpy(entry->name, newname, VOLSER_OLDMAXVOLNAME); + MapNetworkToHost(entry, &storeEntry); + vcode = VLDB_ReplaceEntry(entry->volumeId[RWVOL], RWVOL, &storeEntry, 0); + if (vcode) { + fprintf(STDERR, "Could not update VLDB entry for %u\n", + entry->volumeId[RWVOL]); + error = vcode; + goto rvfail; + } + VPRINT1("Recorded the new name %s in VLDB\n", newname); + /*at this stage the intent to rename is recorded in the vldb, as far as the vldb + * is concerned, oldname is lost */ + if (entry->flags & RW_EXISTS) { + index = Lp_GetRwIndex(entry); + if (index == -1) { /* there is a serious discrepancy */ + fprintf(STDERR, + "There is a serious discrepancy in VLDB entry for volume %u\n", + entry->volumeId[RWVOL]); + fprintf(STDERR, "try building VLDB from scratch\n"); + error = VOLSERVLDB_ERROR; + goto rvfail; + } + aconn = UV_Bind(entry->serverNumber[index], AFSCONF_VOLUMEPORT); + code = + AFSVolTransCreate(aconn, entry->volumeId[RWVOL], + entry->serverPartition[index], ITOffline, &tid); + if (code) { /*volume doesnot exist */ + fprintf(STDERR, + "Could not start transaction on the rw volume %u\n", + entry->volumeId[RWVOL]); + error = code; + goto rvfail; + } else { /*volume exists, process it */ + + code = + AFSVolSetIdsTypes(aconn, tid, newname, RWVOL, + entry->volumeId[RWVOL], + entry->volumeId[ROVOL], + entry->volumeId[BACKVOL]); + if (!code) { + VPRINT2("Renamed rw volume %s to %s\n", oldname, newname); + code = AFSVolEndTrans(aconn, tid, &rcode); + tid = 0; + if (code) { + fprintf(STDERR, + "Could not end transaction on volume %s %u\n", + entry->name, entry->volumeId[RWVOL]); + error = code; + goto rvfail; + } + } else { + fprintf(STDERR, "Could not set parameters on volume %s %u\n", + entry->name, entry->volumeId[RWVOL]); + error = code; + goto rvfail; + } + } + if (aconn) + rx_DestroyConnection(aconn); + aconn = (struct rx_connection *)0; + } + /*end rw volume processing */ + if (entry->flags & BACK_EXISTS) { /*process the backup volume */ + index = Lp_GetRwIndex(entry); + if (index == -1) { /* there is a serious discrepancy */ + fprintf(STDERR, + "There is a serious discrepancy in the VLDB entry for the backup volume %u\n", + entry->volumeId[BACKVOL]); + fprintf(STDERR, "try building VLDB from scratch\n"); + error = VOLSERVLDB_ERROR; + goto rvfail; + } + aconn = UV_Bind(entry->serverNumber[index], AFSCONF_VOLUMEPORT); + code = + AFSVolTransCreate(aconn, entry->volumeId[BACKVOL], + entry->serverPartition[index], ITOffline, &tid); + if (code) { /*volume doesnot exist */ + fprintf(STDERR, + "Could not start transaction on the backup volume %u\n", + entry->volumeId[BACKVOL]); + error = code; + goto rvfail; + } else { /*volume exists, process it */ + if (strlen(newname) > (VOLSER_OLDMAXVOLNAME - 8)) { + fprintf(STDERR, + "Volume name %s.backup exceeds the limit of %u characters\n", + newname, VOLSER_OLDMAXVOLNAME); + error = code; + goto rvfail; + } + strcpy(nameBuffer, newname); + strcat(nameBuffer, ".backup"); + + code = + AFSVolSetIdsTypes(aconn, tid, nameBuffer, BACKVOL, + entry->volumeId[RWVOL], 0, 0); + if (!code) { + VPRINT1("Renamed backup volume to %s \n", nameBuffer); + code = AFSVolEndTrans(aconn, tid, &rcode); + tid = 0; + if (code) { + fprintf(STDERR, + "Could not end transaction on the backup volume %u\n", + entry->volumeId[BACKVOL]); + error = code; + goto rvfail; + } + } else { + fprintf(STDERR, + "Could not set parameters on the backup volume %u\n", + entry->volumeId[BACKVOL]); + error = code; + goto rvfail; + } + } + } /* end backup processing */ + if (aconn) + rx_DestroyConnection(aconn); + aconn = (struct rx_connection *)0; + if (entry->flags & RO_EXISTS) { /*process the ro volumes */ + for (i = 0; i < entry->nServers; i++) { + if (entry->serverFlags[i] & ITSROVOL) { + aconn = UV_Bind(entry->serverNumber[i], AFSCONF_VOLUMEPORT); + code = + AFSVolTransCreate(aconn, entry->volumeId[ROVOL], + entry->serverPartition[i], ITOffline, + &tid); + if (code) { /*volume doesnot exist */ + fprintf(STDERR, + "Could not start transaction on the ro volume %u\n", + entry->volumeId[ROVOL]); + error = code; + goto rvfail; + } else { /*volume exists, process it */ + strcpy(nameBuffer, newname); + strcat(nameBuffer, ".readonly"); + if (strlen(nameBuffer) > (VOLSER_OLDMAXVOLNAME - 1)) { + fprintf(STDERR, + "Volume name %s exceeds the limit of %u characters\n", + nameBuffer, VOLSER_OLDMAXVOLNAME); + error = code; + goto rvfail; + } + code = + AFSVolSetIdsTypes(aconn, tid, nameBuffer, ROVOL, + entry->volumeId[RWVOL], 0, 0); + if (!code) { + VPRINT2("Renamed RO volume %s on host %s\n", + nameBuffer, + hostutil_GetNameByINet(entry-> + serverNumber[i])); + code = AFSVolEndTrans(aconn, tid, &rcode); + tid = 0; + if (code) { + fprintf(STDERR, + "Could not end transaction on volume %u\n", + entry->volumeId[ROVOL]); + error = code; + goto rvfail; + } + } else { + fprintf(STDERR, + "Could not set parameters on the ro volume %u\n", + entry->volumeId[ROVOL]); + error = code; + goto rvfail; + } + } + if (aconn) + rx_DestroyConnection(aconn); + aconn = (struct rx_connection *)0; + } + } + } + rvfail: + if (islocked) { + vcode = + ubik_Call(VL_ReleaseLock, cstruct, 0, entry->volumeId[RWVOL], + RWVOL, + LOCKREL_OPCODE | LOCKREL_AFSID | LOCKREL_TIMESTAMP); + if (vcode) { + fprintf(STDERR, + "Could not unlock the VLDB entry for the volume %s %u\n", + entry->name, entry->volumeId[RWVOL]); + if (!error) + error = vcode; + } + } + if (tid) { + code = AFSVolEndTrans(aconn, tid, &rcode); + if (!code) + code = rcode; + if (code) { + fprintf(STDERR, "Failed to end transaction on a volume \n"); + if (!error) + error = code; + } + } + if (aconn) + rx_DestroyConnection(aconn); + PrintError("", error); + return error; + +} + +/*report on all the active transactions on volser */ +int +UV_VolserStatus(afs_int32 server, transDebugInfo ** rpntr, afs_int32 * rcount) +{ + struct rx_connection *aconn; + transDebugEntries transInfo; + afs_int32 code = 0; + + aconn = UV_Bind(server, AFSCONF_VOLUMEPORT); + transInfo.transDebugEntries_val = (transDebugInfo *) 0; + transInfo.transDebugEntries_len = 0; + code = AFSVolMonitor(aconn, &transInfo); + if (code) { + fprintf(STDERR, + "Could not access status information about the server\n"); + PrintError("", code); + if (transInfo.transDebugEntries_val) + free(transInfo.transDebugEntries_val); + if (aconn) + rx_DestroyConnection(aconn); + return code; + } else { + *rcount = transInfo.transDebugEntries_len; + *rpntr = transInfo.transDebugEntries_val; + if (aconn) + rx_DestroyConnection(aconn); + return 0; + } + + +} + +/*delete the volume without interacting with the vldb */ +int +UV_VolumeZap(afs_int32 server, afs_int32 part, afs_int32 volid) +{ + afs_int32 rcode, ttid, error, code; + struct rx_connection *aconn; + + code = 0; + error = 0; + ttid = 0; + + aconn = UV_Bind(server, AFSCONF_VOLUMEPORT); + code = AFSVolTransCreate(aconn, volid, part, ITOffline, &ttid); + if (code) { + fprintf(STDERR, "Could not start transaction on volume %lu\n", + (unsigned long)volid); + error = code; + goto zfail; + } + code = AFSVolDeleteVolume(aconn, ttid); + if (code) { + fprintf(STDERR, "Could not delete volume %lu\n", + (unsigned long)volid); + error = code; + goto zfail; + } + code = AFSVolEndTrans(aconn, ttid, &rcode); + ttid = 0; + if (!code) + code = rcode; + if (code) { + fprintf(STDERR, "Could not end transaction on volume %lu\n", + (unsigned long)volid); + error = code; + goto zfail; + } + zfail: + if (ttid) { + code = AFSVolEndTrans(aconn, ttid, &rcode); + if (!code) + code = rcode; + if (!error) + error = code; + } + PrintError("", error); + if (aconn) + rx_DestroyConnection(aconn); + return error; +} + +int +UV_SetVolume(afs_int32 server, afs_int32 partition, afs_int32 volid, + afs_int32 transflag, afs_int32 setflag, int sleeptime) +{ + struct rx_connection *conn = 0; + afs_int32 tid = 0; + afs_int32 code, error = 0, rcode; + + conn = UV_Bind(server, AFSCONF_VOLUMEPORT); + if (!conn) { + fprintf(STDERR, "SetVolumeStatus: Bind Failed"); + ERROR_EXIT(-1); + } + + code = AFSVolTransCreate(conn, volid, partition, transflag, &tid); + if (code) { + fprintf(STDERR, "SetVolumeStatus: TransCreate Failed\n"); + ERROR_EXIT(code); + } + + code = AFSVolSetFlags(conn, tid, setflag); + if (code) { + fprintf(STDERR, "SetVolumeStatus: SetFlags Failed\n"); + ERROR_EXIT(code); + } + + if (sleeptime) { +#ifdef AFS_PTHREAD_ENV + sleep(sleeptime); +#else + IOMGR_Sleep(sleeptime); +#endif + } + + error_exit: + if (tid) { + rcode = 0; + code = AFSVolEndTrans(conn, tid, &rcode); + if (code || rcode) { + fprintf(STDERR, "SetVolumeStatus: EndTrans Failed\n"); + if (!error) + error = (code ? code : rcode); + } + } + + if (conn) + rx_DestroyConnection(conn); + return (error); +} + +int +UV_SetVolumeInfo(afs_int32 server, afs_int32 partition, afs_int32 volid, + volintInfo * infop) +{ + struct rx_connection *conn = 0; + afs_int32 tid = 0; + afs_int32 code, error = 0, rcode; + + conn = UV_Bind(server, AFSCONF_VOLUMEPORT); + if (!conn) { + fprintf(STDERR, "SetVolumeInfo: Bind Failed"); + ERROR_EXIT(-1); + } + + code = AFSVolTransCreate(conn, volid, partition, ITOffline, &tid); + if (code) { + fprintf(STDERR, "SetVolumeInfo: TransCreate Failed\n"); + ERROR_EXIT(code); + } + + code = AFSVolSetInfo(conn, tid, infop); + if (code) { + fprintf(STDERR, "SetVolumeInfo: SetInfo Failed\n"); + ERROR_EXIT(code); + } + + error_exit: + if (tid) { + rcode = 0; + code = AFSVolEndTrans(conn, tid, &rcode); + if (code || rcode) { + fprintf(STDERR, "SetVolumeInfo: EndTrans Failed\n"); + if (!error) + error = (code ? code : rcode); + } + } + + if (conn) + rx_DestroyConnection(conn); + return (error); +} + +int +UV_GetSize(afs_int32 afromvol, afs_int32 afromserver, afs_int32 afrompart, + afs_int32 fromdate, struct volintSize *vol_size) +{ + struct rx_connection *aconn = (struct rx_connection *)0; + afs_int32 tid = 0, rcode = 0; + afs_int32 code, error = 0; + + + /* get connections to the servers */ + aconn = UV_Bind(afromserver, AFSCONF_VOLUMEPORT); + + VPRINT1("Starting transaction on volume %u...", afromvol); + code = AFSVolTransCreate(aconn, afromvol, afrompart, ITBusy, &tid); + EGOTO1(error_exit, code, + "Could not start transaction on the volume %u to be measured\n", + afromvol); + VDONE; + + VPRINT1("Getting size of volume on volume %u...", afromvol); + code = AFSVolGetSize(aconn, tid, fromdate, vol_size); + EGOTO(error_exit, code, "Could not start the measurement process \n"); + VDONE; + + error_exit: + if (tid) { + VPRINT1("Ending transaction on volume %u...", afromvol); + code = AFSVolEndTrans(aconn, tid, &rcode); + if (code || rcode) { + fprintf(STDERR, "Could not end transaction on the volume %u\n", + afromvol); + fprintf(STDERR, "error codes: %d and %d\n", code, rcode); + if (!error) + error = (code ? code : rcode); + } + VDONE; + } + if (aconn) + rx_DestroyConnection(aconn); + + PrintError("", error); + return (error); +} + +/*maps the host addresses in (present in network byte order) to + that in< new> (present in host byte order )*/ +void +MapNetworkToHost(struct nvldbentry *old, struct nvldbentry *new) +{ + int i, count; + + /*copy all the fields */ + strcpy(new->name, old->name); +/* new->volumeType = old->volumeType;*/ + new->nServers = old->nServers; + count = old->nServers; + if (count < NMAXNSERVERS) + count++; + for (i = 0; i < count; i++) { + new->serverNumber[i] = ntohl(old->serverNumber[i]); + new->serverPartition[i] = old->serverPartition[i]; + new->serverFlags[i] = old->serverFlags[i]; + } + new->volumeId[RWVOL] = old->volumeId[RWVOL]; + new->volumeId[ROVOL] = old->volumeId[ROVOL]; + new->volumeId[BACKVOL] = old->volumeId[BACKVOL]; + new->cloneId = old->cloneId; + new->flags = old->flags; +} + +/*maps the host entries in which are present in host byte order to network byte order */ +void +MapHostToNetwork(struct nvldbentry *entry) +{ + int i, count; + + count = entry->nServers; + if (count < NMAXNSERVERS) + count++; + for (i = 0; i < count; i++) { + entry->serverNumber[i] = htonl(entry->serverNumber[i]); + } +} diff --git a/src/volser/vsutils.c b/src/volser/vsutils.c new file mode 100644 index 000000000..0f825be9e --- /dev/null +++ b/src/volser/vsutils.c @@ -0,0 +1,674 @@ +/* + * Copyright 2000, International Business Machines Corporation and others. + * All Rights Reserved. + * + * This software has been released under the terms of the IBM Public + * License. For details, see the LICENSE file in the top-level source + * directory or online at http://www.openafs.org/dl/license10.html + */ + +#include +#include + +RCSID + ("$Header: /cvs/openafs/src/volser/vsutils.c,v 1.16 2003/12/07 22:49:46 jaltman Exp $"); + +#include +#ifdef AFS_NT40_ENV +#include +#include +#else +#include +#include +#include +#include +#endif /* AFS_NT40_ENV */ +#include +#ifdef AFS_AIX_ENV +#include +#endif + +#ifdef HAVE_STRING_H +#include +#else +#ifdef HAVE_STRINGS_H +#include +#endif +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "volser.h" +#include "volint.h" +#include "lockdata.h" + +struct ubik_client *cstruct; +static rxkad_level vsu_rxkad_level = rxkad_clear; + +static void +ovlentry_to_nvlentry(oentryp, nentryp) + struct vldbentry *oentryp; + struct nvldbentry *nentryp; +{ + register int i; + + memset(nentryp, 0, sizeof(struct nvldbentry)); + strncpy(nentryp->name, oentryp->name, sizeof(nentryp->name)); + for (i = 0; i < oentryp->nServers; i++) { + nentryp->serverNumber[i] = oentryp->serverNumber[i]; + nentryp->serverPartition[i] = oentryp->serverPartition[i]; + nentryp->serverFlags[i] = oentryp->serverFlags[i]; + } + nentryp->nServers = oentryp->nServers; + for (i = 0; i < MAXTYPES; i++) + nentryp->volumeId[i] = oentryp->volumeId[i]; + nentryp->cloneId = oentryp->cloneId; + nentryp->flags = oentryp->flags; +} + +static +nvlentry_to_ovlentry(nentryp, oentryp) + struct nvldbentry *nentryp; + struct vldbentry *oentryp; +{ + register int i; + + memset(oentryp, 0, sizeof(struct vldbentry)); + strncpy(oentryp->name, nentryp->name, sizeof(oentryp->name)); + if (nentryp->nServers > OMAXNSERVERS) { + /* + * The alternative is to store OMAXSERVERS but it's always better + * to know what's going on... + */ + return VL_BADSERVER; + } + for (i = 0; i < nentryp->nServers; i++) { + oentryp->serverNumber[i] = nentryp->serverNumber[i]; + oentryp->serverPartition[i] = nentryp->serverPartition[i]; + oentryp->serverFlags[i] = nentryp->serverFlags[i]; + } + oentryp->nServers = i; + for (i = 0; i < MAXTYPES; i++) + oentryp->volumeId[i] = nentryp->volumeId[i]; + oentryp->cloneId = nentryp->cloneId; + oentryp->flags = nentryp->flags; + return 0; +} + +static int newvlserver = 0; + +VLDB_CreateEntry(entryp) + struct nvldbentry *entryp; +{ + struct vldbentry oentry; + register int code; + + if (newvlserver == 1) { + tryold: + code = nvlentry_to_ovlentry(entryp, &oentry); + if (code) + return code; + code = ubik_Call(VL_CreateEntry, cstruct, 0, &oentry); + return code; + } + code = ubik_Call(VL_CreateEntryN, cstruct, 0, entryp); + if (!newvlserver) { + if (code == RXGEN_OPCODE) { + newvlserver = 1; /* Doesn't support new interface */ + goto tryold; + } else if (!code) { + newvlserver = 2; + } + } + return code; +} + +VLDB_GetEntryByID(volid, voltype, entryp) + afs_int32 volid, voltype; + struct nvldbentry *entryp; +{ + struct vldbentry oentry; + register int code; + + if (newvlserver == 1) { + tryold: + code = + ubik_Call(VL_GetEntryByID, cstruct, 0, volid, voltype, &oentry); + if (!code) + ovlentry_to_nvlentry(&oentry, entryp); + return code; + } + code = ubik_Call(VL_GetEntryByIDN, cstruct, 0, volid, voltype, entryp); + if (!newvlserver) { + if (code == RXGEN_OPCODE) { + newvlserver = 1; /* Doesn't support new interface */ + goto tryold; + } else if (!code) { + newvlserver = 2; + } + } + return code; +} + +VLDB_GetEntryByName(namep, entryp) + char *namep; + struct nvldbentry *entryp; +{ + struct vldbentry oentry; + register int code; + + if (newvlserver == 1) { + tryold: + code = ubik_Call(VL_GetEntryByNameO, cstruct, 0, namep, &oentry); + if (!code) + ovlentry_to_nvlentry(&oentry, entryp); + return code; + } + code = ubik_Call(VL_GetEntryByNameN, cstruct, 0, namep, entryp); + if (!newvlserver) { + if (code == RXGEN_OPCODE) { + newvlserver = 1; /* Doesn't support new interface */ + goto tryold; + } else if (!code) { + newvlserver = 2; + } + } + return code; +} + +VLDB_ReplaceEntry(volid, voltype, entryp, releasetype) + afs_int32 volid, voltype, releasetype; + struct nvldbentry *entryp; +{ + struct vldbentry oentry; + register int code; + + if (newvlserver == 1) { + tryold: + code = nvlentry_to_ovlentry(entryp, &oentry); + if (code) + return code; + code = + ubik_Call(VL_ReplaceEntry, cstruct, 0, volid, voltype, &oentry, + releasetype); + return code; + } + code = + ubik_Call(VL_ReplaceEntryN, cstruct, 0, volid, voltype, entryp, + releasetype); + if (!newvlserver) { + if (code == RXGEN_OPCODE) { + newvlserver = 1; /* Doesn't support new interface */ + goto tryold; + } else if (!code) { + newvlserver = 2; + } + } + return code; +} + + + +VLDB_ListAttributes(attrp, entriesp, blkentriesp) + VldbListByAttributes *attrp; + afs_int32 *entriesp; + nbulkentries *blkentriesp; +{ + bulkentries arrayEntries; + register int code, i; + + if (newvlserver == 1) { + tryold: + memset(&arrayEntries, 0, sizeof(arrayEntries)); /*initialize to hint the stub to alloc space */ + code = + ubik_Call(VL_ListAttributes, cstruct, 0, attrp, entriesp, + &arrayEntries); + if (!code) { + blkentriesp->nbulkentries_val = + (nvldbentry *) malloc(*entriesp * sizeof(struct nvldbentry)); + for (i = 0; i < *entriesp; i++) { /* process each entry */ + ovlentry_to_nvlentry(&arrayEntries.bulkentries_val[i], + &blkentriesp->nbulkentries_val[i]); + } + } + if (arrayEntries.bulkentries_val) + free(arrayEntries.bulkentries_val); + return code; + } + code = + ubik_Call(VL_ListAttributesN, cstruct, 0, attrp, entriesp, + blkentriesp); + if (!newvlserver) { + if (code == RXGEN_OPCODE) { + newvlserver = 1; /* Doesn't support new interface */ + goto tryold; + } else if (!code) { + newvlserver = 2; + } + } + return code; +} + +VLDB_ListAttributesN2(attrp, name, thisindex, nentriesp, blkentriesp, + nextindexp) + VldbListByAttributes *attrp; + char *name; + afs_int32 thisindex; + afs_int32 *nentriesp; + nbulkentries *blkentriesp; + afs_int32 *nextindexp; +{ + afs_int32 code; + + code = + ubik_Call(VL_ListAttributesN2, cstruct, 0, attrp, (name ? name : ""), + thisindex, nentriesp, blkentriesp, nextindexp); + return code; +} + + +static int vlserverv4 = -1; +struct cacheips { + afs_int32 server; + afs_int32 count; + afs_uint32 addrs[16]; +}; +/* + * Increase cache size. This avoids high CPU usage by the vlserver + * in environments where there are more than 16 fileservers in the + * cell. + */ +#define GETADDRUCACHESIZE 64 +struct cacheips cacheips[GETADDRUCACHESIZE]; +int cacheip_index = 0; + +VLDB_IsSameAddrs(serv1, serv2, errorp) + afs_int32 serv1, serv2, *errorp; +{ + register int code; + ListAddrByAttributes attrs; + bulkaddrs addrs; + afs_uint32 *addrp, nentries, unique, i, j, f1, f2; + afsUUID uuid; + static int initcache = 0; + + *errorp = 0; + + if (serv1 == serv2) + return 1; + if (vlserverv4 == 1) { + return 0; + } + if (!initcache) { + for (i = 0; i < GETADDRUCACHESIZE; i++) { + cacheips[i].server = cacheips[i].count = 0; + } + initcache = 1; + } + + /* See if it's cached */ + for (i = 0; i < GETADDRUCACHESIZE; i++) { + f1 = f2 = 0; + for (j = 0; j < cacheips[i].count; j++) { + if (serv1 == cacheips[i].addrs[j]) + f1 = 1; + else if (serv2 == cacheips[i].addrs[j]) + f2 = 1; + + if (f1 && f2) + return 1; + } + if (f1 || f2) + return 0; + if (cacheips[i].server == serv1) + return 0; + } + + memset(&attrs, 0, sizeof(attrs)); + attrs.Mask = VLADDR_IPADDR; + attrs.ipaddr = serv1; + memset(&addrs, 0, sizeof(addrs)); + memset(&uuid, 0, sizeof(uuid)); + code = + ubik_Call(VL_GetAddrsU, cstruct, 0, &attrs, &uuid, &unique, &nentries, + &addrs); + if (vlserverv4 == -1) { + if (code == RXGEN_OPCODE) { + vlserverv4 = 1; /* Doesn't support new interface */ + return 0; + } else if (!code) { + vlserverv4 = 2; + } + } + if (code == VL_NOENT) + return 0; + if (code) { + *errorp = code; + return 0; + } + + code = 0; + if (nentries > GETADDRUCACHESIZE) + nentries = GETADDRUCACHESIZE; /* safety check; should not happen */ + if (++cacheip_index >= GETADDRUCACHESIZE) + cacheip_index = 0; + cacheips[cacheip_index].server = serv1; + cacheips[cacheip_index].count = nentries; + addrp = addrs.bulkaddrs_val; + for (i = 0; i < nentries; i++, addrp++) { + cacheips[cacheip_index].addrs[i] = *addrp; + if (serv2 == *addrp) { + code = 1; + } + } + return code; +} + + +#ifdef notdef +afs_int32 +subik_Call(aproc, aclient, aflags, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, + p11, p12, p13, p14, p15, p16) + register struct ubik_client *aclient; + int (*aproc) (); + afs_int32 aflags; + afs_int32 p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, + p15, p16; +{ + struct vldbentry vldbentry; + register int code, (*nproc) (); + + if (newvlserver == 1) { + } + code = + ubik_Call(aproc, aclient, aflags, p1, p2, p3, p4, p5, p6, p7, p8, p9, + p10, p11, p12, p13, p14, p15, p16); + if (!newvlserver) { + if (code == RXGEN_OPCODE) { + newvlserver = 1; /* Doesn't support new interface */ + } else if (!code) { + newvlserver = 2; + } + } +} +#endif /* notdef */ + + +/* + Set encryption. If 'cryptflag' is nonzero, encrpytion is turned on + for authenticated connections; if zero, encryption is turned off. + Calling this function always results in a level of at least rxkad_auth; + to get a rxkad_clear connection, simply don't call this. +*/ +void +vsu_SetCrypt(cryptflag) + int cryptflag; +{ + if (cryptflag) { + vsu_rxkad_level = rxkad_crypt; + } else { + vsu_rxkad_level = rxkad_auth; + } +} + + +/* + Get the appropriate type of ubik client structure out from the system. +*/ +afs_int32 +vsu_ClientInit(noAuthFlag, confDir, cellName, sauth, uclientp, secproc) + int noAuthFlag; + int (*secproc) (); + char *cellName; + struct ubik_client **uclientp; + char *confDir; + afs_int32 sauth; +{ + afs_int32 code, scIndex, i; + struct afsconf_cell info; + struct afsconf_dir *tdir; + struct ktc_principal sname; + struct ktc_token ttoken; + struct rx_securityClass *sc; + static struct rx_connection *serverconns[VLDB_MAXSERVERS]; + char cellstr[64]; + + + code = rx_Init(0); + if (code) { + fprintf(STDERR, "vsu_ClientInit: could not initialize rx.\n"); + return code; + } + rx_SetRxDeadTime(90); + + if (sauth) { /* -localauth */ + tdir = afsconf_Open(AFSDIR_SERVER_ETC_DIRPATH); + if (!tdir) { + fprintf(STDERR, + "vsu_ClientInit: Could not process files in configuration directory (%s).\n", + AFSDIR_SERVER_ETC_DIRPATH); + return -1; + } + code = afsconf_ClientAuth(tdir, &sc, &scIndex); /* sets sc,scIndex */ + if (code) { + fprintf(STDERR, + "vsu_ClientInit: Could not get security object for -localAuth\n"); + return -1; + } + code = + afsconf_GetCellInfo(tdir, tdir->cellName, AFSCONF_VLDBSERVICE, + &info); + if (code) { + fprintf(STDERR, + "vsu_ClientInit: can't find cell %s's hosts in %s/%s\n", + cellName, AFSDIR_SERVER_ETC_DIRPATH, + AFSDIR_CELLSERVDB_FILE); + exit(1); + } + } else { /* not -localauth */ + tdir = afsconf_Open(confDir); + if (!tdir) { + fprintf(STDERR, + "vsu_ClientInit: Could not process files in configuration directory (%s).\n", + confDir); + return -1; + } + + if (!cellName) { + code = afsconf_GetLocalCell(tdir, cellstr, sizeof(cellstr)); + if (code) { + fprintf(STDERR, + "vsu_ClientInit: can't get local cellname, check %s/%s\n", + confDir, AFSDIR_THISCELL_FILE); + exit(1); + } + cellName = cellstr; + } + + code = + afsconf_GetCellInfo(tdir, cellName, AFSCONF_VLDBSERVICE, &info); + if (code) { + fprintf(STDERR, + "vsu_ClientInit: can't find cell %s's hosts in %s/%s\n", + cellName, confDir, AFSDIR_CELLSERVDB_FILE); + exit(1); + } + if (noAuthFlag) /* -noauth */ + scIndex = 0; + else { /* not -noauth */ + strcpy(sname.cell, info.name); + sname.instance[0] = 0; + strcpy(sname.name, "afs"); + code = ktc_GetToken(&sname, &ttoken, sizeof(ttoken), NULL); + if (code) { /* did not get ticket */ + fprintf(STDERR, + "vsu_ClientInit: Could not get afs tokens, running unauthenticated.\n"); + scIndex = 0; + } else { /* got a ticket */ + scIndex = 2; + if ((ttoken.kvno < 0) || (ttoken.kvno > 255)) { + fprintf(STDERR, + "vsu_ClientInit: funny kvno (%d) in ticket, proceeding\n", + ttoken.kvno); + } + } + } + + switch (scIndex) { + case 0: + sc = rxnull_NewClientSecurityObject(); + break; + case 2: + sc = rxkad_NewClientSecurityObject(vsu_rxkad_level, + &ttoken.sessionKey, + ttoken.kvno, ttoken.ticketLen, + ttoken.ticket); + break; + default: + fprintf(STDERR, "vsu_ClientInit: unsupported security index %d\n", + scIndex); + exit(1); + break; + } + } + + afsconf_Close(tdir); + + if (secproc) /* tell UV module about default authentication */ + (*secproc) (sc, scIndex); + if (info.numServers > VLDB_MAXSERVERS) { + fprintf(STDERR, + "vsu_ClientInit: info.numServers=%d (> VLDB_MAXSERVERS=%d)\n", + info.numServers, VLDB_MAXSERVERS); + exit(1); + } + for (i = 0; i < info.numServers; i++) { + serverconns[i] = + rx_NewConnection(info.hostAddr[i].sin_addr.s_addr, + info.hostAddr[i].sin_port, USER_SERVICE_ID, sc, + scIndex); + } + *uclientp = 0; + code = ubik_ClientInit(serverconns, uclientp); + if (code) { + fprintf(STDERR, "vsu_ClientInit: ubik client init failed.\n"); + return code; + } + return 0; +} + + +/*extract the name of volume without readonly or backup suffixes + * and return the result as . + */ +int +vsu_ExtractName(rname, name) + char rname[], name[]; +{ + char sname[VOLSER_OLDMAXVOLNAME + 1]; + int total; + + strncpy(sname, name, sizeof(sname)); + sname[sizeof(sname) - 1] = '\0'; + total = strlen(sname); + if (!strcmp(&sname[total - 9], ".readonly")) { + /*discard the last 8 chars */ + sname[total - 9] = '\0'; + strcpy(rname, sname); + return 0; + } else if (!strcmp(&sname[total - 7], ".backup")) { + /*discard last 6 chars */ + sname[total - 7] = '\0'; + strcpy(rname, sname); + return 0; + } else { + strncpy(rname, name, VOLSER_OLDMAXVOLNAME); + rname[VOLSER_OLDMAXVOLNAME] = '\0'; + return -1; + } +} + + +/* returns 0 if failed */ +afs_uint32 +vsu_GetVolumeID(astring, acstruct, errp) + struct ubik_client *acstruct; + afs_int32 *errp; + char *astring; +{ + afs_uint32 tc, value; + + char *str, *ptr, volname[VOLSER_OLDMAXVOLNAME + 1]; + int tryname, curval; + struct nvldbentry entry; + afs_int32 vcode = 0; + int total; + + *errp = 0; + total = strlen(astring); + str = astring; + ptr = astring; + tryname = 0; + while ((curval = *str++)) { + if (curval < '0' || curval > '9') + tryname = 1; + } + + if (tryname) { + vsu_ExtractName(volname, astring); + vcode = VLDB_GetEntryByName(volname, &entry); + if (!vcode) { + if (!strcmp(&astring[total - 9], ".readonly")) + return entry.volumeId[ROVOL]; + else if ((!strcmp(&astring[total - 7], ".backup"))) + return entry.volumeId[BACKVOL]; + else + return (entry.volumeId[RWVOL]); + } else { + *errp = vcode; + return 0; /* can't find volume */ + } + } + + value = 0; + while ((tc = *astring++)) { + if (tc & 0x80) { + if (!tryname) + fprintf(STDERR, "goofed in volid \n"); + else { + fprintf(STDERR, "Could not get entry from vldb for %s\n", + ptr); + PrintError("", vcode); + } + *errp = EINVAL; + return 0; + } + if (tc < '0' || tc > '9') { + if (!tryname) + fprintf(STDERR, + "internal error: out of range char in vol ID\n"); + else { + fprintf(STDERR, "Could not get entry from vldb for %s\n", + ptr); + PrintError("", vcode); + } + *errp = ERANGE; + return 0; + } + value *= 10; + value += (tc - '0'); + } + return value; +} -- 2.39.5