diff --git a/Makefile.in b/Makefile.in index 364456eabc..5ac9f2877a 100644 --- a/Makefile.in +++ b/Makefile.in @@ -215,9 +215,6 @@ tviced: project viced vlserver libafsrpc libafsauthent volser: project tviced usd kauth audit ${COMPILE_PART1} volser ${COMPILE_PART2} -tools: volser - ${COMPILE_PART1} tools ${COMPILE_PART2} - venus: project volser ptserver ${COMPILE_PART1} venus ${COMPILE_PART2} ${COMPILE_PART1} venus/test ${COMPILE_PART2} @@ -439,13 +436,13 @@ libadmin: libafsauthent bozo finale: project cmd comerr afsd allrcmds butc tbutc @ENABLE_KERNEL_MODULE@ libuafs audit kauth log package \ ptserver scout bu_utils ubik uss bozo vfsck volser \ - venus update xstat afsmonitor dauth tools rxdebug libafsrpc \ + venus update xstat afsmonitor dauth rxdebug libafsrpc \ libafsauthent libadmin ${COMPILE_PART1} finale ${COMPILE_PART2} finale_nolibafs: project cmd comerr afsd allrcmds butc tbutc libuafs audit kauth log package \ ptserver scout bu_utils ubik uss bozo vfsck volser \ - venus update xstat afsmonitor dauth tools rxdebug libafsrpc \ + venus update xstat afsmonitor dauth rxdebug libafsrpc \ libafsauthent libadmin ${COMPILE_PART1} finale ${COMPILE_PART2} @@ -536,7 +533,7 @@ clean2: -${COMPILE_PART1} bucoord ${COMPILE_CLEAN} -${COMPILE_PART1} xstat ${COMPILE_CLEAN} -${COMPILE_PART1} afsmonitor ${COMPILE_CLEAN} - -${COMPILE_PART1} tools ${COMPILE_CLEAN} + -${COMPILE_PART1} tests ${COMPILE_CLEAN} -${COMPILE_PART1} rxdebug ${COMPILE_CLEAN} -${COMPILE_PART1} libafsrpc ${COMPILE_CLEAN} -${COMPILE_PART1} libafsauthent ${COMPILE_CLEAN} @@ -665,7 +662,8 @@ distclean: clean src/sia/Makefile \ src/sys/Makefile \ src/tbutc/Makefile \ - src/tools/Makefile \ + src/tests/Makefile \ + src/tests/Dirpath.pm \ src/tsm41/Makefile \ src/tviced/Makefile \ src/ubik/Makefile \ diff --git a/configure.in b/configure.in index 10615e2eb1..f5672fd734 100644 --- a/configure.in +++ b/configure.in @@ -107,7 +107,6 @@ src/sys/Makefile \ src/tbutc/Makefile \ src/tests/Makefile \ src/tests/Dirpath.pm \ -src/tools/Makefile \ src/tsm41/Makefile \ src/tviced/Makefile \ src/ubik/Makefile \ diff --git a/src/tests/Makefile.in b/src/tests/Makefile.in index b4021891d8..b1ae44d2b3 100644 --- a/src/tests/Makefile.in +++ b/src/tests/Makefile.in @@ -6,6 +6,105 @@ SHELL = /bin/sh CFLAGS = -I. -I${srcdir} ${DBG} ${OPTMZ} -I${TOP_OBJDIR}/src/config -I${TOP_INCDIR} ${XCFLAGS} LDFLAGS=${DBG} ${OPTMZ} ${XLDFLAGS} +INCDIRS=-I${TOP_OBJDIR}/src/config -I${TOP_INCDIR}/afs -I${TOP_INCDIR} +INCLIBS=-L${SRCDIR}/lib/afs -L${TOP_LIBDIR} + +LIBS=\ + ${TOP_LIBDIR}/libdumpscan.a \ + ${TOP_LIBDIR}/libxfiles.a \ + ${TOP_LIBDIR}/libauth.a \ + ${TOP_LIBDIR}/libaudit.a \ + ${TOP_LIBDIR}/libvolser.a \ + ${TOP_LIBDIR}/libvldb.a \ + ${TOP_LIBDIR}/libubik.a \ + ${TOP_LIBDIR}/librxkad.a \ + ${TOP_LIBDIR}/libsys.a \ + ${TOP_LIBDIR}/librx.a \ + ${TOP_LIBDIR}/liblwp.a \ + ${TOP_LIBDIR}/util.a \ + ${TOP_LIBDIR}/libcom_err.a \ + ${XLIBS} + +OBJS_afsdump_scan = afsdump_scan.o repair.o +OBJS_afsdump_xsed = afsdump_xsed.o repair.o +OBJS_libxfiles.a = xfiles.o xf_errs.o xf_printf.o int64.o \ + xf_files.o xf_rxcall.o xf_profile.o +OBJS_libdumpscan.a = primitive.o util.o dumpscan_errs.o parsetag.o \ + parsedump.o parsevol.o parsevnode.o dump.o \ + directory.o pathname.o backuphdr.o stagehdr.o + +TARGETS = libxfiles.a libdumpscan.a \ + afsdump_scan afsdump_dirlist afsdump_extract dumptool + +afsdump_scan: libxfiles.a libdumpscan.a $(OBJS_afsdump_scan) + $(CC) $(CFLAGS) $(LDFLAGS) -o afsdump_scan $(OBJS_afsdump_scan) $(LIBS) + +afsdump_xsed: libxfiles.a libdumpscan.a $(OBJS_afsdump_xsed) + $(CC) $(CFLAGS) $(LDFLAGS) -o afsdump_xsed $(OBJS_afsdump_xsed) $(LIBS) + +afsdump_dirlist: libxfiles.a libdumpscan.a afsdump_dirlist.o + $(CC) $(CFLAGS) $(LDFLAGS) -o afsdump_dirlist afsdump_dirlist.o $(LIBS) + +afsdump_extract: libxfiles.a libdumpscan.a afsdump_extract.o + $(CC) $(CFLAGS) $(LDFLAGS) -o afsdump_extract afsdump_extract.o $(LIBS) + +null-search: libxfiles.a libdumpscan.a null-search.c + $(CC) $(CFLAGS) $(LDFLAGS) -o null-search null-search.c $(LIBS) + +dumptool: dumptool.c + $(CC) $(CFLAGS) $(LDFLAGS) -o dumptool dumptool.c + +libxfiles.a: $(OBJS_libxfiles.a) + -rm -f libxfiles.a + $(AR) r libxfiles.a $(OBJS_libxfiles.a) + $(RANLIB) libxfiles.a + +libdumpscan.a: $(OBJS_libdumpscan.a) + -rm -f libdumpscan.a + $(AR) r libdumpscan.a $(OBJS_libdumpscan.a) + $(RANLIB) libdumpscan.a + +xf_errs.c xf_errs.h: xf_errs.et + $(COMPILE_ET) xf_errs.et + +dumpscan_errs.c dumpscan_errs.h: dumpscan_errs.et + $(COMPILE_ET) dumpscan_errs.et + +util.o xfiles.o xf_files.o: xf_errs.h +backuphdr.o directory.o parsedump.o parsetag.o: dumpscan_errs.h +parsevnode.o parsevol.o pathname.o repair.o: dumpscan_errs.h +stagehdr.o util.o: dumpscan_errs.h + +${DEST}/etc/afsdump_scan: afsdump_scan + ${INSTALL} $? $@ + +${DEST}/etc/afsdump_dirlist: afsdump_dirlist + ${INSTALL} $? $@ + +${DEST}/etc/afsdump_extract: afsdump_extract + ${INSTALL} $? $@ + +${DEST}/etc/dumptool: dumptool + ${INSTALL} $? $@ + +${DESTDIR}${sbindir}/afsdump_scan: afsdump_scan + ${INSTALL} $? $@ + +${DESTDIR}${sbindir}/afsdump_dirlist: afsdump_dirlist + ${INSTALL} $? $@ + +${DESTDIR}${sbindir}/afsdump_extract: afsdump_extract + ${INSTALL} $? $@ + +${DESTDIR}${sbindir}/dumptool: dumptool + ${INSTALL} $? $@ + +${TOP_LIBDIR}/libxfiles.a: libxfiles.a + ${INSTALL} $? $@ + +${TOP_LIBDIR}/libdumpscan.a: libdumpscan.a + ${INSTALL} $? $@ + SYS_LIBS = ${TOP_LIBDIR}/libsys.a ${TOP_LIBDIR}/librx.a ${TOP_LIBDIR}/liblwp.a ${TOP_LIBDIR}/util.a AUTH_LIBS = ${TOP_LIBDIR}/libauth.a ${SYS_LIBS} @@ -65,8 +164,6 @@ TEST_SRCS = write-ro-file.c read-vs-mmap.c read-vs-mmap2.c \ EXTRA_OBJS = err.o errx.o warn.o warnx.o -all: run-tests $(TEST_PROGRAMS) OS.pm - OS.pm: OS-$(MKAFS_OSTYPE).pm $(CP) OS-$(MKAFS_OSTYPE).pm OS.pm @@ -256,13 +353,21 @@ mountpoint: mountpoint.in sed -e "s!%bindir%!$(bindir)!" $(srcdir)/mountpoint.in > $@ chmod +x mountpoint -clean: - rm -f run-tests $(TEST_PROGRAMS) *.o *~ OS.pm +dest: install: uninstall: +all: run-tests $(TEST_PROGRAMS) OS.pm ${TOP_LIBDIR}/libxfiles.a \ + ${TOP_LIBDIR}/libdumpscan.a \ + afsdump_scan afsdump_dirlist afsdump_extract dumptool + +clean: + -rm xf_errs.c xf_errs.h dumpscan_errs.c dumpscan_errs.h *.o \ + $(TARGETS) run-tests $(TEST_PROGRAMS) OS.pm + +include ../config/Makefile.version TAGS: $(TEST_SRCS) etags $(TEST_SRCS) diff --git a/src/tests/README.dumptool b/src/tests/README.dumptool new file mode 100644 index 0000000000..69c28ddcb8 --- /dev/null +++ b/src/tests/README.dumptool @@ -0,0 +1,152 @@ +$Id$ + +This is the README for dumptool, a program to interactively restore +AFS volume dump files. + + +INTRODUCTION + +Dumptool arose out of a need here at NRL to perform maintenance of +MR-AFS (Multi-Resident AFS) volumes. After it was written, we found +that it worked great on standard AFS volumes as well, and relatively +few changes were required to make it compile with a standard AFS +installation. + +Dumptool provides an interface similar to the interactive Unix restore; +given a dump file, a user can navigate through the filesystem inside +the dump using familiar commands such as "cd" and "ls". Also provided +is a "cp" command to copy individual files out of the dump into a +normal filesystem space. This eliminates the need to restore an +entire volume just to retrieve a single file. + +Dumptool was written at the Naval Research Laboratory by Ken Hornstein +. The latest and greatest version of dumptool +can always be found in the AFS contrib directory at: + +/afs/transarc.com/public/afs-contrib/dumptool/ + + +INSTALLATION & OPERATION + +The standard Makefile target will build a dumptool for a vanilla AFS +installation. The "mrafs" target will build a dumptool that can +operate on MR-AFS dumps. In either case, you may need to change some +of the Makefile variables to reflect your site; see the Makefile for +more information. + +Once dumptool is built successfully, you can run it on any AFS dump +file. Without any additional arguments, dumptool will scan the dump file, +build indexes of all listed vnodes, and present a prompt (">") that +accepts the following commands: + + ls Lists files in the current directory. Filename globbing + (e.g., wildcards such as * ?) are supported via the system + fnmatch() function. Accepts the following flags: + + -l Generates a "long" listing, similar to the -l switch for + the Unix ls. Displays Unix mode mask, owner, group, + and file size. + -i Displays volume, vnode, and uniquifier for each matching + file in the format volume.vnode.uniquifier. Note that + the volume displayed is that of the _parent_ volume, + which in the case of a backup volume is the _original_ + volume from which it was generated. + -F Append / to filenames for directories, @ for symlinks, + and * for files which have the execute bits set. + -R Recurse through all subdirectories. + + cd Change the current directory + + cp Copy a file from the dump. Note that globbing is NOT + supported, and you must give a filename (the Unix + idiom of just giving a destination directory for the + second argument to cp will NOT work). + + vcp Copy a file from the dump, by the vnode. The first + argument is the vnode number, optionally followed by + the uniqifier. E.g: + + vcp 126278 /tmp/file1 + vcp 126278.43289 /tmp/file2 + + quit Exits dumptool. + exit + + +ADDITIONAL OPTIONS TO DUMPTOOL + +Dumptool supports a number of command-line options. They are detailed +below: + + -v Verbose mode. Output additional information during dump + processing. Each -v will increate output. + + -f Force dump processing. Attempt to continue processing + a dump even when some errors are detected. Not completely + tested. + + -i Inode dump. Dump a list of all files in the volume, + with their volume/vnode/uniqifier information. + + +SUPPORT FOR MR-AFS + +Dumptool also supports the extra information in MR-AFS dumps, and +provides some extra commands/options for dealing with MR-AFS dumps: + +Additional command line options: + + -d Dump all residency filenames in the dump file to standard + output. + + -t Residency tag information. Allows a user to specify the + tag of a residency if the rsserver is not available. + Format of option is Residency/Tag + + -r Residency filesystem information. Allows a user to specify + the residency filesystem information if dumptool is not + run on the same host as the residency in the dump. Format + of option is Residency/Type/Size/Algorithm. + +Additional commands: + + file Displays to standard output the residency filename of a + given dump filename. All residencies available are shown. + + +CAVEATS + +The user interface needs some work. "ls" doesn't support nearly as many +options as the standard Unix one. "cp" also doesn't have all of the features +found in common Unix variants. + +Right now two passes are done through the dump file to scan all vnodes. +With some clever work this could be sped up somewhat and changed to only +do a single scan. + + +MODIFYING DUMPTOOL + +I welcome changes to dumptool, but I have some guidelines. + +First, I DEMAND that the changes be sent in context diff format. I prefer +unidiff (diff -u), but standard context diffs are okay. It's extremely +difficult to deal with new code in any other way. + +If the changes you want to do require some significant +rearchitecturing, it might be a good idea to contact me first. That +way we can coordinate the modifications in a meaningful way (I might +have made changes since the last released version). + +If you're making MR-AFS specific changes, please follow the example +I've set and protect them with #ifdef RESIDENCY. + +And please ... follow my code style, okay? I always try to follow +whatever wacky code style I find in source code that I modify, and I +think it's only polite for you to do the same. + + +CONTACT INFORMATION + +As always, please send comments, suggestions, or new features to me, +Ken Hornstein . Shar and enjoy. diff --git a/src/tests/TEMPLATE b/src/tests/TEMPLATE new file mode 100644 index 0000000000..644875009e --- /dev/null +++ b/src/tests/TEMPLATE @@ -0,0 +1,27 @@ +/* + * CMUCS AFStools + * dumpscan - routines for scanning and manipulating AFS volume dumps + * + * Copyright (c) 1998 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software_Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ diff --git a/src/tests/afsdump_dirlist.c b/src/tests/afsdump_dirlist.c new file mode 100644 index 0000000000..78d603757a --- /dev/null +++ b/src/tests/afsdump_dirlist.c @@ -0,0 +1,140 @@ +/* + * CMUCS AFStools + * dumpscan - routines for scanning and manipulating AFS volume dumps + * + * Copyright (c) 1998 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software_Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ + +/* afsdump_dirlist.c - List an AFS directory file */ + +#include +#include +#include +#include + +#include "dumpscan.h" + +extern int optind; +extern char *optarg; + +char *argv0; +static char *input_path; +static int quiet, verbose, error_count; + +static path_hashinfo phi; +static dump_parser dp; + + +/* Print a usage message and exit */ +static void usage(int status, char *msg) +{ + if (msg) fprintf(stderr, "%s: %s\n", argv0, msg); + fprintf(stderr, "Usage: %s [options] [file]\n", argv0); + fprintf(stderr, " -h Print this help message\n"); + fprintf(stderr, " -q Quiet mode (don't print errors)\n"); + fprintf(stderr, " -v Verbose mode\n"); + exit(status); +} + + +/* Parse the command-line options */ +static void parse_options(int argc, char **argv) +{ + int c; + + /* Set the program name */ + if (argv0 = strrchr(argv[0], '/')) argv0++; + else argv0 = argv[0]; + + /* Initialize options */ + input_path = 0; + quiet = verbose = 0; + + /* Initialize other stuff */ + error_count = 0; + + /* Parse the options */ + while ((c = getopt(argc, argv, "hqv")) != EOF) { + switch (c) { + case 'q': quiet = 1; continue; + case 'v': verbose = 1; continue; + case 'h': usage(0, 0); + default: usage(1, "Invalid option!"); + } + } + + if (quiet && verbose) usage(1, "Can't specify both -q and -v"); + + /* Parse non-option arguments */ + if (argc - optind > 1) usage(1, "Too many arguments!"); + input_path = (argc == optind) ? "-" : argv[optind]; +} + + +/* A callback to count and print errors */ +static afs_uint32 my_error_cb(afs_uint32 code, int fatal, void *ref, char *msg, ...) +{ + va_list alist; + + error_count++; + if (!quiet) { + va_start(alist, msg); + com_err_va(argv0, code, msg, alist); + va_end(alist); + } +} + + +/* Main program */ +void main(int argc, char **argv) +{ + XFILE input_file; + afs_uint32 r; + + parse_options(argc, argv); + initialize_acfg_error_table(); + initialize_AVds_error_table(); + initialize_rxk_error_table(); + initialize_u_error_table(); + initialize_vl_error_table(); + initialize_vols_error_table(); + initialize_xFil_error_table(); + r = xfopen(&input_file, O_RDONLY, input_path); + if (r) { + com_err(argv0, r, "opening %s", input_path); + exit(2); + } + + memset(&dp, 0, sizeof(dp)); + dp.cb_error = my_error_cb; + dp.print_flags = DSPRINT_DIR; + if (input_file.is_seekable) dp.flags |= DSFLAG_SEEK; + + r = ParseDirectory(&input_file, &dp, 0, 1); + xfclose(&input_file); + + if (verbose && error_count) fprintf(stderr, "*** %d errors\n", error_count); + if (r && !quiet) fprintf(stderr, "*** FAILED: %s\n", error_message(r)); + exit(0); +} diff --git a/src/tests/afsdump_extract.c b/src/tests/afsdump_extract.c new file mode 100644 index 0000000000..e92c66e197 --- /dev/null +++ b/src/tests/afsdump_extract.c @@ -0,0 +1,526 @@ +/* + * CMUCS AFStools + * dumpscan - routines for scanning and manipulating AFS volume dumps + * + * Copyright (c) 1998 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software_Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ + +/* afsdump_extract.c - Extract files from an AFS dump */ + +#include +#include +#include +#include +#include +#include + +#include "dumpscan.h" +#include "dumpscan_errs.h" + +#define COPYBUFSIZE (256*1024) + +extern int optind; +extern char *optarg; + +char *argv0; +static char **file_names; +static int *file_vnums, name_count, vnum_count; + +static char *input_path, *target; +static int quiet, verbose, error_count, dirs_done, extract_all; +static int nomode, use_realpath, use_vnum; +static int do_acls, do_headers; + +static path_hashinfo phi; +static dump_parser dp; + +/* Print a usage message and exit */ +static void usage(int status, char *msg) +{ + if (msg) fprintf(stderr, "%s: %s\n", argv0, msg); + fprintf(stderr, "Usage: %s [options] dumpfile [dest [files...]]\n", argv0); + fprintf(stderr, " -A Save ACL's\n"); + fprintf(stderr, " -H Save headers\n"); + fprintf(stderr, " -h Print this help message\n"); + fprintf(stderr, " -i Use vnode numbers\n"); + fprintf(stderr, " -n Don't actually create files\n"); + fprintf(stderr, " -p Use real pathnames internally\n"); + fprintf(stderr, " -q Quiet mode (don't print errors)\n"); + fprintf(stderr, " -v Verbose mode\n"); + fprintf(stderr, "The destination directory defaults to .\n"); + fprintf(stderr, "Files may be vnode numbers or volume-relative paths;\n"); + fprintf(stderr, "If vnode numbers are used, files will be extracted\n"); + fprintf(stderr, "a name generated from the vnode number and uniqifier.\n"); + fprintf(stderr, "If paths are used, -p is implied and files will be\n"); + fprintf(stderr, "into correctly-named files.\n"); + exit(status); +} + + +/* Parse the command-line options */ +static void parse_options(int argc, char **argv) +{ + int c, i, i_name, i_vnum; + + /* Set the program name */ + if (argv0 = strrchr(argv[0], '/')) argv0++; + else argv0 = argv[0]; + + /* Initialize options */ + input_path = 0; + quiet = verbose = nomode = 0; + use_realpath = use_vnum = do_acls = do_headers = extract_all = 0; + + /* Initialize other stuff */ + error_count = 0; + + /* Parse the options */ + while ((c = getopt(argc, argv, "AHhinpqv")) != EOF) { + switch (c) { + case 'A': do_acls = 1; continue; + case 'H': do_headers = 1; continue; + case 'i': use_vnum = 1; continue; + case 'n': nomode = 1; continue; + case 'p': use_realpath = 1; continue; + case 'q': quiet = 1; continue; + case 'v': verbose = 1; continue; + case 'h': usage(0, 0); + default: usage(1, "Invalid option!"); + } + } + + if (quiet && verbose) usage(1, "Can't specify both -q and -v"); + + /* Parse non-option arguments */ + if (argc - optind < 1) usage(1, "Dumpfile name required!"); + input_path = argv[optind]; + + if (argc - optind < 2) target = "."; + target = argv[optind + 1]; + + vnum_count = name_count = 0; + if (argc - optind < 3) extract_all = 1; + else { + argv += optind + 2; + argc -= optind + 2; + for (i = 0; i < argc; i++) { + if (argv[i][0] == '/') name_count++; + else vnum_count++; + } + file_names = (char **)malloc(name_count + sizeof(char *)); + file_vnums = (afs_uint32 *)malloc(vnum_count + sizeof(afs_uint32)); + if (name_count) use_realpath = 1; + + i_name = i_vnum = 0; + for (i = 0; i < argc; i++) { + if (argv[i][0] == '/') file_names[i_name++] = argv[i]; + else file_vnums[i_vnum++] = strtol(argv[i], 0, 0); + } + } +} + + +static int mkdirp(char *path) +{ + char *x = path, slash; + struct stat statbuf; + + for (;;) { + while (*x && *x != '/') x++; + slash = *x; + *x = 0; + + if (stat(path, &statbuf)) { + if (errno == ENOENT) { + if (verbose) printf("> mkdir %s\n", path); + if (!mkdir(path, 0755)) errno = 0; + } + } + if (!slash) break; + *x++ = '/'; + if (errno) return errno; + } + + return 0; +} + + +static char *modestr(int mode) +{ + static char str[10]; + int i; + + strcpy(str, "rwxrwxrwx"); + for (i = 0; i < 9; i++) { + if (!(mode & (1 << i))) + str[8 - i] = '-'; + } + if (mode & 01000) str[8] = (str[8] == '-') ? 'T' : 't'; + if (mode & 02000) str[5] = (str[5] == '-') ? 'S' : 's'; + if (mode & 04000) str[2] = (str[2] == '-') ? 'S' : 's'; + return str; +} + + +static char *month[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; +static char *datestr(time_t date) +{ + static char str[13]; + time_t clock = time(0); + struct tm *now, *then; + int diff; + + now = localtime(&clock); + then = localtime(&date); + + diff = now->tm_mon - then->tm_mon; + if (then->tm_year == now->tm_year - 1) diff += 12; + if (then->tm_year == now->tm_year + 1) diff -= 12; + + if (diff < 5 || diff > 5) + sprintf(str, "%3s %2d %4d", month[then->tm_mon], then->tm_mday, + then->tm_year + 1900); + else + sprintf(str, "%3s %2d %2d:%2d", month[then->tm_mon], then->tm_mday, + then->tm_hour, then->tm_min); + return str; +} + + +/* Should we use this vnode? + * Return 0 if no, non-0 if yes + */ +static int usevnode(XFILE *X, afs_uint32 vnum, char *vnodepath) +{ + int vl, vpl, r, i; + + /* Special case */ + if (extract_all || !strcmp(vnodepath, "/")) + return 1; + + for (i = 0; i < vnum_count; i++) + if (vnum == file_vnums[i]) return 2; + + vl = strlen(vnodepath); +/*fprintf(stderr, "++ checking %s\n", vnodepath);*/ + for (i = 0; i < name_count; i++) { + vpl = strlen(file_names[i]); +/* fprintf(stderr, " %s\n", file_names[i]);*/ + + if (vl > vpl) { + r = !strncmp(file_names[i], vnodepath, vpl) && vnodepath[vpl] == '/'; + } else if (vl < vpl) { + r = !strncmp(file_names[i], vnodepath, vl) && file_names[i][vl] == '/'; + } else { + r = !strcmp(file_names[i], vnodepath); + } + if (r) return 1; + } + return 0; +} + + +static int copyfile(XFILE *in, XFILE *out, int size) +{ + static char buf[COPYBUFSIZE]; + int nr, nw, r; + + while (size) { + nr = (size > COPYBUFSIZE) ? COPYBUFSIZE : size; + if (r = xfread(in, buf, nr)) return r; + if (r = xfwrite(out, buf, nr)) return r; + size -= nr; + } + return 0; +} + + +/* A callback to count and print errors */ +static afs_uint32 my_error_cb(afs_uint32 code, int fatal, void *ref, char *msg, ...) +{ + va_list alist; + + error_count++; + if (!quiet) { + va_start(alist, msg); + com_err_va(argv0, code, msg, alist); + va_end(alist); + } +} + + +static afs_uint32 dumphdr_cb(afs_dump_header *hdr, XFILE *X, void *refcon) +{ + return 0; +} + + +static afs_uint32 volhdr_cb(afs_vol_header *hdr, XFILE *X, void *refcon) +{ + return 0; +} + + +static afs_uint32 directory_cb(afs_vnode *v, XFILE *X, void *refcon) +{ + char *vnodepath; + int r, use; + + /* Should we even use this? */ + if (!use_vnum) { + if (r = Path_Build(X, &phi, v->vnode, &vnodepath, !use_realpath)) + return r; + if (!(use = usevnode(X, v->vnode, vnodepath))) { + free(vnodepath); + return 0; + } + } + + /* Print it out */ + if (verbose) { + if (use_vnum) + printf("d%s %3d %-11d %11d %s #%d:%d\n", + modestr(v->mode), v->nlinks, v->owner, v->size, + datestr(v->server_date), v->vnode, v->vuniq); + else + printf("d%s %3d %-11d %11d %s %s\n", + modestr(v->mode), v->nlinks, v->owner, v->size, + datestr(v->server_date), vnodepath); + } + else if (!quiet && !use_vnum) + printf("%s\n", vnodepath); + + /* Make the directory, if needed */ + if (!nomode && !use_vnum && use != 2) { + if (strcmp(vnodepath, "/") + && (r = mkdirp(vnodepath + 1))) { + free(vnodepath); + return r; + } + if (do_acls) { + /* XXX do ACL's later */ + } + } + if (!use_vnum) free(vnodepath); + return 0; +} + + +static afs_uint32 file_cb(afs_vnode *v, XFILE *X, void *refcon) +{ + char *vnodepath, vnpx[30]; + u_int64 where; + XFILE OX; + int r, use; + + if (!dirs_done) { + dirs_done = 1; + if (verbose) printf("* Extracting files...\n"); + } + + /* Should we even use this? */ + if (!use_vnum) { + if (r = Path_Build(X, &phi, v->vnode, &vnodepath, !use_realpath)) + return r; + if (!(use = usevnode(X, v->vnode, vnodepath))) { + free(vnodepath); + return 0; + } + if (use == 2) { + free(vnodepath); + sprintf(vnpx, "#%d:%d", v->vnode, v->vuniq); + vnodepath = vnpx; + } + } else { + sprintf(vnpx, "#%d:%d", v->vnode, v->vuniq); + vnodepath = vnpx; + } + + /* Print it out */ + if (verbose) { + printf("-%s %3d %-11d %11d %s %s\n", + modestr(v->mode), v->nlinks, v->owner, v->size, + datestr(v->server_date), vnodepath); + } else if (!quiet) { + printf("%s\n", vnodepath); + } + + if (!nomode) { + if ((r = xftell(X, &where)) + || (r = xfseek(X, &v->d_offset)) + || (r = xfopen_path(&OX, O_RDWR|O_CREAT|O_TRUNC, vnodepath + 1, 0644))) { + if (!use_vnum) free(vnodepath); + return r; + } + r = copyfile(X, &OX, v->size); + xfclose(&OX); + xfseek(X, &where); + } else r = 0; + + if (!use_vnum && use != 2) free(vnodepath); + return r; +} + + +static afs_uint32 symlink_cb(afs_vnode *v, XFILE *X, void *refcon) +{ + char *vnodepath, *linktarget, vnpx[30]; + u_int64 where; + int r, use; + + if (!dirs_done) { + dirs_done = 1; + if (verbose) printf("* Extracting files...\n"); + } + + /* Should we even use this? */ + if (!use_vnum) { + if (r = Path_Build(X, &phi, v->vnode, &vnodepath, !use_realpath)) + return r; + if (!(use = usevnode(X, v->vnode, vnodepath))) { + free(vnodepath); + return 0; + } + if (use == 2) { + free(vnodepath); + sprintf(vnpx, "#%d:%d", v->vnode, v->vuniq); + vnodepath = vnpx; + } + } else { + sprintf(vnpx, "#%d:%d", v->vnode, v->vuniq); + vnodepath = vnpx; + } + + if (!(linktarget = (char *)malloc(v->size + 1))) { + if (!use_vnum && use != 2) free(vnodepath); + return DSERR_MEM; + } + if ((r = xftell(X, &where)) + || (r = xfseek(X, &v->d_offset)) + || (r = xfread(X, linktarget, v->size))) { + if (!use_vnum && use != 2) free(vnodepath); + free(linktarget); + return r; + } + xfseek(X, &where); + linktarget[v->size] = 0; + + /* Print it out */ + if (verbose) + printf("l%s %3d %-11d %11d %s %s -> %s\n", + modestr(v->mode), v->nlinks, v->owner, v->size, + datestr(v->server_date), vnodepath, linktarget); + else if (!quiet) + printf("%s\n", vnodepath); + + r = 0; + if (!nomode) { + if (symlink(linktarget, vnodepath + 1)) + r = errno; + } + + free(linktarget); + if (!use_vnum && use != 2) free(vnodepath); + return r; +} + + +static afs_uint32 lose_cb(afs_vnode *v, XFILE *F, void *refcon) +{ + int r; + + if (!dirs_done) { + dirs_done = 1; + if (verbose) printf("* Extracting files...\n"); + } + + return 0; +} + + +/* Main program */ +void main(int argc, char **argv) +{ + XFILE input_file; + afs_uint32 r; + + parse_options(argc, argv); + initialize_acfg_error_table(); + initialize_AVds_error_table(); + initialize_rxk_error_table(); + initialize_u_error_table(); + initialize_vl_error_table(); + initialize_vols_error_table(); + initialize_xFil_error_table(); + r = xfopen(&input_file, O_RDONLY, input_path); + if (r) { + com_err(argv0, r, "opening %s", input_path); + exit(2); + } + + memset(&dp, 0, sizeof(dp)); + dp.cb_error = my_error_cb; + if (input_file.is_seekable) dp.flags |= DSFLAG_SEEK; + dirs_done = 0; + + if (!use_vnum) { + u_int64 where; + + memset(&phi, 0, sizeof(phi)); + phi.p = &dp; + + if (verbose) printf("* Building pathname info...\n"); + if ((r = xftell(&input_file, &where)) + || (r = Path_PreScan(&input_file, &phi, 1)) + || (r = xfseek(&input_file, &where))) { + com_err(argv0, r, "- path initialization failed"); + xfclose(&input_file); + exit(1); + } + } + + dp.cb_vnode_dir = directory_cb; + dp.cb_vnode_file = file_cb; + dp.cb_vnode_link = symlink_cb; + dp.cb_vnode_empty = lose_cb; + dp.cb_vnode_wierd = lose_cb; + if (do_headers) { + dp.cb_dumphdr = dumphdr_cb; + dp.cb_volhdr = volhdr_cb; + } + + if (!nomode) { + mkdir(target, 0755); + if (chdir(target)) { + fprintf(stderr, "chdir %s failed: %s\n", target, strerror(errno)); + exit(1); + } + } + r = ParseDumpFile(&input_file, &dp); + + if (verbose && error_count) fprintf(stderr, "*** %d errors\n", error_count); + if (r && !quiet) fprintf(stderr, "*** FAILED: %s\n", error_message(r)); + exit(0); +} diff --git a/src/tests/afsdump_scan.c b/src/tests/afsdump_scan.c new file mode 100644 index 0000000000..ae95d8b391 --- /dev/null +++ b/src/tests/afsdump_scan.c @@ -0,0 +1,293 @@ +/* + * CMUCS AFStools + * dumpscan - routines for scanning and manipulating AFS volume dumps + * + * Copyright (c) 1998 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software_Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ + +/* afsdump_scan.c - General-purpose dump scanner */ + +#include +#include +#include +#include + +#include "dumpscan.h" + +extern int optind; +extern char *optarg; + +extern XFILE repair_output; +extern afs_uint32 repair_dumphdr_cb(afs_dump_header *, XFILE *, void *); +extern afs_uint32 repair_volhdr_cb(afs_vol_header *, XFILE *, void *); +extern afs_uint32 repair_vnode_cb(afs_vnode *, XFILE *, void *); + +char *argv0; +static char *input_path, *gendump_path; +static afs_uint32 printflags, repairflags; +static int quiet, verbose, error_count; + +static path_hashinfo phi; +static dump_parser dp; + + +/* Print a usage message and exit */ +static void usage(int status, char *msg) +{ + if (msg) fprintf(stderr, "%s: %s\n", argv0, msg); + fprintf(stderr, "Usage: %s [options] [file]\n", argv0); + fprintf(stderr, " -Pxxx Set print options:\n"); + fprintf(stderr, " B = Print backup system header (if any)\n"); + fprintf(stderr, " H = Print AFS dump header\n"); + fprintf(stderr, " V = Print AFS volume header\n"); + fprintf(stderr, " v = List vnodes\n"); + fprintf(stderr, " p = Include path to each vnode\n"); + fprintf(stderr, " i = Include info for each vnode\n"); + fprintf(stderr, " d = List directory contents\n"); + fprintf(stderr, " a = List access control lists\n"); + fprintf(stderr, " g = Print debugging info\n"); + fprintf(stderr, " -Rxxx Set repair options:\n"); + fprintf(stderr, " 0 = Skip null tags\n"); + fprintf(stderr, " b = Seek backward to find skipped tags\n"); + fprintf(stderr, " d = Resync after vnode data\n"); + fprintf(stderr, " v = Resync after corrupted vnodes\n"); + fprintf(stderr, " -h Print this help message\n"); + fprintf(stderr, " -gxxx Generate a new dump in file xxx\n"); + fprintf(stderr, " -q Quiet mode (don't print errors)\n"); + fprintf(stderr, " -v Verbose mode\n"); + exit(status); +} + + +/* Parse the argument given to the -P option. + * Returns the resulting * dumpscan print flags (DSPRINT_*). + * If an unrecognized flag is used, prints an error message and exits. + */ +static afs_uint32 parse_printflags(char *flags) +{ + afs_uint32 result = 0; + char *x; + + for (x = flags; *x; x++) switch (*x) { + case 'B': result |= DSPRINT_BCKHDR; continue; + case 'H': result |= DSPRINT_DUMPHDR; continue; + case 'V': result |= DSPRINT_VOLHDR; continue; + case 'v': result |= DSPRINT_ITEM; continue; + case 'p': result |= DSPRINT_PATH; continue; + case 'i': result |= DSPRINT_VNODE; continue; + case 'd': result |= DSPRINT_DIR; continue; + case 'a': result |= DSPRINT_ACL; continue; + case 'g': result |= DSPRINT_DEBUG; continue; + default: usage(1, "Invalid print options!"); + } + return result; +} + + +/* Parse the argument given to the -R option. + * Returns the resulting * dumpscan repair flags (DSFIX_*). + * If an unrecognized flag is used, prints an error message and exits. + */ +static afs_uint32 parse_repairflags(char *flags) +{ + afs_uint32 result = 0; + char *x; + + for (x = flags; *x; x++) switch (*x) { + case '0': result |= DSFIX_SKIP; continue; + case 'b': result |= DSFIX_RSKIP; continue; + case 'd': result |= DSFIX_VDSYNC; continue; + case 'v': result |= DSFIX_VFSYNC; continue; + default: usage(1, "Invalid repair options!"); + } + return result; +} + + +/* Parse the command-line options */ +static void parse_options(int argc, char **argv) +{ + int c; + + /* Set the program name */ + if (argv0 = strrchr(argv[0], '/')) argv0++; + else argv0 = argv[0]; + + /* Initialize options */ + input_path = gendump_path = 0; + printflags = repairflags = 0; + quiet = verbose = 0; + + /* Initialize other stuff */ + error_count = 0; + + /* Parse the options */ + while ((c = getopt(argc, argv, "P:R:g:hqv")) != EOF) { + switch (c) { + case 'P': printflags = parse_printflags(optarg); continue; + case 'R': repairflags = parse_repairflags(optarg); continue; + case 'g': gendump_path = optarg; continue; + case 'q': quiet = 1; continue; + case 'v': verbose = 1; continue; + case 'h': usage(0, 0); + default: usage(1, "Invalid option!"); + } + } + + if (quiet && verbose) usage(1, "Can't specify both -q and -v"); + + /* Parse non-option arguments */ + if (argc - optind > 1) usage(1, "Too many arguments!"); + input_path = (argc == optind) ? "-" : argv[optind]; +} + + +/* A callback to count and print errors */ +static afs_uint32 my_error_cb(afs_uint32 code, int fatal, void *ref, char *msg, ...) +{ + va_list alist; + + error_count++; + if (!quiet) { + va_start(alist, msg); + com_err_va(argv0, code, msg, alist); + va_end(alist); + } +} + + +/* A callback to print the path of a vnode. */ +static afs_uint32 print_vnode_path(afs_vnode *v, XFILE *X, void *refcon) +{ + afs_uint32 r; + char *name = 0; + + /* Do repair, but only for known vnode types */ + if (gendump_path + && (!(v->field_mask & F_VNODE_TYPE) + || v->type != vFile + || v->type != vDirectory + || v->type != vSymlink)) { + r = repair_vnode_cb(v, X, refcon); + if (r) return r; + } + r = Path_Build(X, &phi, v->vnode, &name, 0); + if (!r && name) printf(" Path: %s\n", name); + if (name) free(name); + return r; +} + + +/* Setup for generating a repaired dump */ +static afs_uint32 setup_repair(void) +{ + afs_uint32 r; + + r = xfopen(&repair_output, O_RDWR|O_CREAT|O_TRUNC, gendump_path); + if (r) return r; + + dp.cb_dumphdr = repair_dumphdr_cb; + dp.cb_volhdr = repair_volhdr_cb; + dp.cb_vnode_dir = repair_vnode_cb; + dp.cb_vnode_file = repair_vnode_cb; + dp.cb_vnode_link = repair_vnode_cb; + dp.cb_vnode_empty = repair_vnode_cb; + return 0; +} + + +/* Main program */ +void main(int argc, char **argv) +{ + XFILE input_file; + afs_uint32 r; + + parse_options(argc, argv); + initialize_acfg_error_table(); + initialize_AVds_error_table(); + initialize_rxk_error_table(); + initialize_u_error_table(); + initialize_vl_error_table(); + initialize_vols_error_table(); + initialize_xFil_error_table(); + r = xfopen(&input_file, O_RDONLY, input_path); + if (r) { + com_err(argv0, r, "opening %s", input_path); + exit(2); + } + + memset(&dp, 0, sizeof(dp)); + dp.cb_error = my_error_cb; + dp.repair_flags = repairflags; + if (input_file.is_seekable) dp.flags |= DSFLAG_SEEK; + else { + if (repairflags) + fprintf(stderr, "Repair modes available only for seekable dumps\n"); + if (printflags & DSPRINT_PATH) + fprintf(stderr, "Path-printing available only for seekable dumps\n"); + if (repairflags || (printflags & DSPRINT_PATH)) + exit(1); + } + + if (gendump_path && (r = setup_repair())) { + com_err(argv0, r, "setting up repair output"); + xfclose(&input_file); + exit(2); + } + + if (printflags & DSPRINT_PATH) { + u_int64 where; + + dp.print_flags = printflags & DSPRINT_DEBUG; + memset(&phi, 0, sizeof(phi)); + phi.p = &dp; + + if ((r = xftell(&input_file, &where)) + || (r = Path_PreScan(&input_file, &phi, 0)) + || (r = xfseek(&input_file, &where))) { + com_err(argv0, r, "- path initialization failed"); + xfclose(&input_file); + exit(2); + } + + dp.cb_vnode_dir = print_vnode_path; + dp.cb_vnode_file = print_vnode_path; + dp.cb_vnode_link = print_vnode_path; + dp.cb_vnode_empty = print_vnode_path; + dp.cb_vnode_wierd = print_vnode_path; + } + + dp.print_flags = printflags; + r = ParseDumpFile(&input_file, &dp); + xfclose(&input_file); + if (gendump_path) { + if (!r) r = DumpDumpEnd(&repair_output); + if (!r) r = xfclose(&repair_output); + else xfclose(&repair_output); + } + + if (verbose && error_count) fprintf(stderr, "*** %d errors\n", error_count); + if (r && !quiet) fprintf(stderr, "*** FAILED: %s\n", error_message(r)); + exit(0); +} diff --git a/src/tests/afsdump_xsed.c b/src/tests/afsdump_xsed.c new file mode 100644 index 0000000000..f4f9e0fac2 --- /dev/null +++ b/src/tests/afsdump_xsed.c @@ -0,0 +1,333 @@ +/* + * COPYRIGHT NOTICE + * Copyright (c) 1997 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software_Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ + +/* UB - Unified Backups */ +/* methods/afs/dumpscan/afsdump_scan.c - General-purpose dump scanner */ + +#include "dumpscan.h" +#include +#include +#include +#include +#include + +extern int opterr, optind; +extern char *optarg; + +extern XFILE repair_output; +extern afs_uint32 repair_dumphdr_cb(afs_dump_header *, XFILE *, void *); +extern afs_uint32 repair_volhdr_cb(afs_vol_header *, XFILE *, void *); +extern afs_uint32 repair_vnode_cb(afs_vnode *, XFILE *, void *); + +char *argv0; +static char *input_path, *gendump_path; +static afs_uint32 printflags, repairflags, add_admin; +static int quiet, verbose, error_count; + +static path_hashinfo phi; +static dump_parser dp; + + +/* Print a usage message and exit */ +static void usage(int status, char *msg) +{ + if (msg) fprintf(stderr, "%s: %s\n", argv0, msg); + fprintf(stderr, "Usage: %s [options] [file]\n", argv0); + fprintf(stderr, " -Pxxx Set print options:\n"); + fprintf(stderr, " B = Print backup system header (if any)\n"); + fprintf(stderr, " H = Print AFS dump header\n"); + fprintf(stderr, " V = Print AFS volume header\n"); + fprintf(stderr, " v = List vnodes\n"); + fprintf(stderr, " p = Include path to each vnode\n"); + fprintf(stderr, " i = Include info for each vnode\n"); + fprintf(stderr, " d = List directory contents\n"); + fprintf(stderr, " a = List access control lists\n"); + fprintf(stderr, " g = Print debugging info\n"); + fprintf(stderr, " -Rxxx Set repair options:\n"); + fprintf(stderr, " 0 = Skip null tags\n"); + fprintf(stderr, " b = Seek backward to find skipped tags\n"); + fprintf(stderr, " d = Resync after vnode data\n"); + fprintf(stderr, " v = Resync after corrupted vnodes\n"); + fprintf(stderr, " -Annn Add all rights for ID nnn to every directory\n"); + fprintf(stderr, " -h Print this help message\n"); + fprintf(stderr, " -gxxx Generate a new dump in file xxx\n"); + fprintf(stderr, " -q Quiet mode (don't print errors)\n"); + fprintf(stderr, " -v Verbose mode\n"); + exit(status); +} + + +/* Parse the argument given to the -P option. + * Returns the resulting * dumpscan print flags (DSPRINT_*). + * If an unrecognized flag is used, prints an error message and exits. + */ +static afs_uint32 parse_printflags(char *flags) +{ + afs_uint32 result = 0; + char *x; + + for (x = flags; *x; x++) switch (*x) { + case 'B': result |= DSPRINT_BCKHDR; continue; + case 'H': result |= DSPRINT_DUMPHDR; continue; + case 'V': result |= DSPRINT_VOLHDR; continue; + case 'v': result |= DSPRINT_ITEM; continue; + case 'p': result |= DSPRINT_PATH; continue; + case 'i': result |= DSPRINT_VNODE; continue; + case 'd': result |= DSPRINT_DIR; continue; + case 'a': result |= DSPRINT_ACL; continue; + case 'g': result |= DSPRINT_DEBUG; continue; + default: usage(1, "Invalid print options!"); + } + return result; +} + + +/* Parse the argument given to the -R option. + * Returns the resulting * dumpscan repair flags (DSFIX_*). + * If an unrecognized flag is used, prints an error message and exits. + */ +static afs_uint32 parse_repairflags(char *flags) +{ + afs_uint32 result = 0; + char *x; + + for (x = flags; *x; x++) switch (*x) { + case '0': result |= DSFIX_SKIP; continue; + case 'b': result |= DSFIX_RSKIP; continue; + case 'd': result |= DSFIX_VDSYNC; continue; + case 'v': result |= DSFIX_VFSYNC; continue; + default: usage(1, "Invalid repair options!"); + } + return result; +} + + +/* Parse the command-line options */ +static void parse_options(int argc, char **argv) +{ + int c; + + /* Set the program name */ + if (argv0 = strrchr(argv[0], '/')) argv0++; + else argv0 = argv[0]; + + /* Initialize options */ + input_path = gendump_path = 0; + printflags = repairflags = add_admin = 0; + quiet = verbose = 0; + + /* Initialize other stuff */ + error_count = 0; + + /* Parse the options */ + while ((c = getopt(argc, argv, "A:P:R:g:hv")) != EOF) { + switch (c) { + case 'A': add_admin = atoi(optarg); continue; + case 'P': printflags = parse_printflags(optarg); continue; + case 'R': repairflags = parse_repairflags(optarg); continue; + case 'g': gendump_path = optarg; continue; + case 'q': quiet = 1; continue; + case 'v': verbose = 1; continue; + case 'h': usage(0, 0); + default: usage(1, "Invalid option!"); + } + } + + if (quiet && verbose) usage(1, "Can't specify both -q and -v"); + + /* Parse non-option arguments */ + if (argc - optind > 1) usage(1, "Too many arguments!"); + input_path = (argc == optind) ? "-" : argv[optind]; + if (add_admin && !gendump_path) add_admin = 0; +} + + +/* A callback to count and print errors */ +static afs_uint32 my_error_cb(afs_uint32 code, int fatal, void *ref, char *msg, ...) +{ + va_list alist; + + error_count++; + if (!quiet) { + va_start(alist, msg); + com_err_va(argv0, code, msg, alist); + va_end(alist); + } +} + + +/* A callback to print the path of a vnode. */ +static afs_uint32 print_vnode_path(afs_vnode *v, XFILE *X, void *refcon) +{ + afs_uint32 r; + char *name = 0; + + /* Do repair, but only for known vnode types */ + if (gendump_path + && (!(v->field_mask & F_VNODE_TYPE) + || v->type != vFile + || v->type != vDirectory + || v->type != vSymlink)) { + r = repair_vnode_cb(v, X, refcon); + if (r) return r; + } + r = Path_Build(X, &phi, v->vnode, &name, 0); + if (!r && name) printf(" Path: %s\n", name); + if (name) free(name); + return r; +} + + +static afs_uint32 munge_admin_acl(afs_vnode *v, XFILE *X, void *refcon) +{ + struct acl_accessList *acl; + int add_entry = 1, remove_entry = -1; + int i, o, n; + + acl = (struct acl_accessList *)(v->acl); + o = n = ntohl(acl->positive); + for (i = 0; i < n; i++) + if (ntohl(acl->entries[i].id) == add_admin) add_entry = 0; + n = ntohl(acl->negative); + for (i = o; i < n + o; i++) + if (ntohl(acl->entries[i].id) == add_admin) remove_entry = i; + + if (add_entry) { + for (i = (remove_entry < 0) ? o + n : remove_entry; i > o; i--) { + acl->entries[i].id = acl->entries[i-1].id; + acl->entries[i].rights = acl->entries[i-1].rights; + } + acl->entries[o].id = htonl(add_admin); + acl->entries[o].rights = htonl((PRSFS_READ | PRSFS_LOOKUP + | PRSFS_INSERT | PRSFS_DELETE + | PRSFS_WRITE | PRSFS_LOCK + | PRSFS_ADMINISTER)); + acl->positive = htonl(o + 1); + if (remove_entry < 0) acl->total = htonl(o + n + 1); + else acl->negative = htonl(n - 1); + } else if (remove_entry >= 0) { + for (i = remove_entry; i < o + n - 1; i++) { + acl->entries[i].id = acl->entries[i+1].id; + acl->entries[i].rights = acl->entries[i+1].rights; + } + acl->negative = htonl(n - 1); + acl->total = htonl(o + n - 1); + } + return repair_vnode_cb(v, X, refcon); +} + + +/* Setup for generating a repaired dump */ +static afs_uint32 setup_repair(void) +{ + afs_uint32 r; + + r = xfopen(&repair_output, gendump_path, O_RDWR, 0644); + if (r) return r; + + dp.cb_dumphdr = repair_dumphdr_cb; + dp.cb_volhdr = repair_volhdr_cb; + dp.cb_vnode_dir = repair_vnode_cb; + dp.cb_vnode_file = repair_vnode_cb; + dp.cb_vnode_link = repair_vnode_cb; + dp.cb_vnode_empty = repair_vnode_cb; + return 0; +} + + +/* Main program */ +void main(int argc, char **argv) +{ + XFILE *X; + afs_uint32 r; + + parse_options(argc, argv); + initialize_UB_error_table(); + initialize_UBsp_error_table(); + initialize_AVds_error_table(); + r = xfopen(&X, input_path, O_RDONLY, 0); + if (r) { + com_err(argv0, r, "opening %s", input_path); + exit(2); + } + + bzero(&dp, sizeof(dp)); + dp.cb_error = my_error_cb; + dp.repair_flags = repairflags; + if (X->is_seekable) dp.flags |= DSFLAG_SEEK; + else { + if (repairflags) + fprintf(stderr, "Repair modes available only for seekable dumps\n"); + if (printflags & DSPRINT_PATH) + fprintf(stderr, "Path-printing available only for seekable dumps\n"); + if (repairflags || (printflags & DSPRINT_PATH)) + exit(1); + } + + if (gendump_path && (r = setup_repair())) { + com_err(argv0, r, "setting up repair output"); + xfclose(X); + exit(2); + } + + if (printflags & DSPRINT_PATH) { + u_int64 where; + + dp.print_flags = printflags & DSPRINT_DEBUG; + bzero(&phi, sizeof(phi)); + phi.p = &dp; + + if ((r = xftell(X, &where)) + || (r = Path_PreScan(X, &phi, 0)) + || (r = xfseek(X, &where))) { + com_err(argv0, r, "- path initialization failed"); + xfclose(X); + exit(2); + } + + dp.cb_vnode_dir = print_vnode_path; + dp.cb_vnode_file = print_vnode_path; + dp.cb_vnode_link = print_vnode_path; + dp.cb_vnode_empty = print_vnode_path; + dp.cb_vnode_wierd = print_vnode_path; + } + + if (add_admin) { + dp.cb_vnode_dir = munge_admin_acl; + } + + dp.print_flags = printflags; + r = ParseDumpFile(X, &dp); + if (gendump_path) { + if (!r) r = DumpDumpEnd(&repair_output); + if (!r) r = xfclose(&repair_output); + else xfclose(&repair_output); + } + + if (verbose && error_count) fprintf(stderr, "*** %d errors\n", error_count); + if (r && !quiet) fprintf(stderr, "*** FAILED: %s\n", error_message(r)); + exit(0); +} diff --git a/src/tests/backuphdr.c b/src/tests/backuphdr.c new file mode 100644 index 0000000000..88c9759c1b --- /dev/null +++ b/src/tests/backuphdr.c @@ -0,0 +1,87 @@ +/* + * CMUCS AFStools + * dumpscan - routines for scanning and manipulating AFS volume dumps + * + * Copyright (c) 1998 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software_Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ + +/* backuphdr.c - Parse and print backup system headers */ + +#include + +#include "dumpscan.h" +#include "dumpscan_errs.h" +#include "stagehdr.h" + +afs_uint32 try_backuphdr(XFILE *X, char *tag, tagged_field *field, + afs_uint32 value, tag_parse_info *pi, + void *g_refcon, void *l_refcon) +{ + dump_parser *p = (dump_parser *)g_refcon; + backup_system_header bh; + u_int64 where; + afs_uint32 r; + + /* Which header should we try (if any)? */ + switch (*tag) { + case STAGE_VERSMIN: r = ParseStageHdr(X, tag, &bh); break; + default: return DSERR_MAGIC; + } + if (r) return r; + + /* Do something with it... */ + if (p->print_flags & DSPRINT_BCKHDR) PrintBackupHdr(&bh); + if (p->cb_bckhdr) { + r = xftell(X, &where); + if (!r && p->cb_bckhdr) + r = (p->cb_bckhdr)(&bh, X, p->refcon); + if (p->flags & DSFLAG_SEEK) { + if (!r) r = xfseek(X, &where); + else xfseek(X, &where); + } + } + if (bh.server) free(bh.server); + if (bh.part) free(bh.part); + if (bh.volname) free(bh.volname); + return r; +} + + +void PrintBackupHdr(backup_system_header *hdr) +{ + time_t from = hdr->from_date, to = hdr->to_date, dd = hdr->dump_date; + + printf("* BACKUP SYSTEM HEADER\n"); + printf(" Version: %d\n", hdr->version); + printf(" Volume: %s (%d)\n", hdr->volname, hdr->volid); + printf(" Location: %s %s\n", hdr->server, hdr->part); + printf(" Level: %d\n", hdr->level); + printf(" Range: %d => %d\n", hdr->from_date, hdr->to_date); + printf(" == %s", ctime(&from)); + printf(" => %s", ctime(&to)); + printf(" Dump Time: %d == %s", hdr->dump_date, ctime(&dd)); + printf(" Dump Flags: 0x%08x\n", hdr->flags); + printf(" Length: %d\n", hdr->dumplen); + printf(" File Num: %d\n", hdr->filenum); +} diff --git a/src/tests/directory.c b/src/tests/directory.c new file mode 100644 index 0000000000..f270723638 --- /dev/null +++ b/src/tests/directory.c @@ -0,0 +1,254 @@ +/* + * CMUCS AFStools + * dumpscan - routines for scanning and manipulating AFS volume dumps + * + * Copyright (c) 1998 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software_Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ + +/* directory.c - Parse an AFS directory */ +/* See the end of this file for a description of the directory format */ + +#include +#include +#include + +#include "dumpscan.h" +#include "dumpscan_errs.h" +#include "xf_errs.h" +#include "dumpfmt.h" +#include "internal.h" + +#include + +static afs_dir_page page; + +#define allocbit(x) (page.header.freebitmap[(x)>>3] & (1 << ((x) & 7))) +#define DPHE (DHE + 1) + +static void fixup(char *name, int l) +{ + name += 16; + l -= 15; + + while (l-- > 0) { + name[0] = name[4]; + name++; + } +} + +afs_uint32 parse_directory(XFILE *X, dump_parser *p, afs_vnode *v, + afs_uint32 size, int toeof) +{ + afs_dir_entry de; + int pgno, i, j, l, n; + afs_uint32 r; + u_int64 where; + + if (p->print_flags & DSPRINT_DIR) { + printf(" VNode Uniqifier Name\n"); + printf(" ========== ========== ==============================\n"); + } + if ((p->flags & DSFLAG_SEEK) && (r = xftell(X, &where))) return r; + for (pgno = 0; toeof || size; pgno++, size -= (toeof ? 0 : AFS_PAGESIZE)) { + if ((p->flags & DSFLAG_SEEK) && (r = xfseek(X, &where))) return r; + if (r = xfread(X, &page, AFS_PAGESIZE)) { + if (toeof && r == ERROR_XFILE_EOF) break; + return r; + } + if ((p->flags & DSFLAG_SEEK) && (r = xftell(X, &where))) return r; + if (page.header.tag != htons(1234)) { + if (p->cb_error) + (p->cb_error)(DSERR_MAGIC, 1, p->err_refcon, + "Invalid page tag (%d) in page %d", + ntohs(page.header.tag), pgno); + return DSERR_MAGIC; + } + for (i = (pgno ? 1 : DPHE); i < EPP; i++) { + if (!allocbit(i)) continue; + if (page.entry[i].flag != FFIRST) { + if (p->cb_error) + (p->cb_error)(DSERR_MAGIC, 0, p->err_refcon, + "Invalid entry flag %d in entry %d/%d; skipping...", + page.entry[i].flag, pgno, i); + continue; + } + n = (EPP - i - 1) * 32 + 16; + for (l = 0; n && page.entry[i].name[l]; l++, n--); + if (page.entry[i].name[l]) { + if (p->cb_error) + (p->cb_error)(DSERR_FMT, 0, p->err_refcon, + "Filename too long in entry %d/%d; skipping page", + pgno, i); + break; + } +/* fixup(page.entry[i].name, l); */ + if (pgno) de.slot = i - 1 + (pgno - 1) * (EPP - 1) + (EPP - DPHE); + else de.slot = i - DPHE; + de.name = page.entry[i].name; + de.vnode = ntohl(page.entry[i].vnode); + de.uniq = ntohl(page.entry[i].vunique); + if (p->print_flags & DSPRINT_DIR) + printf(" %10d %10d %s\n", de.vnode, de.uniq, de.name); + if (p->cb_dirent) { + r = (p->cb_dirent)(v, &de, X, p->refcon); + } + if (p->cb_dirent && (r = (p->cb_dirent)(v, &de, X, p->refcon))) + return r; + i += ((l + 16) >> 5); + } + } + if ((p->flags & DSFLAG_SEEK) && (r = xfseek(X, &where))) return r; + return 0; +} + + +afs_uint32 ParseDirectory(XFILE *X, dump_parser *p, afs_uint32 size, int toeof) +{ + afs_uint32 r; + + r = parse_directory(X, p, 0, size, toeof); +} + + +typedef struct { + char **name; + afs_uint32 *vnode; + afs_uint32 *vuniq; +} dirlookup_stat; + + +static afs_uint32 dirlookup_cb(afs_vnode *v, afs_dir_entry *de, + XFILE *X, void *refcon) +{ + dirlookup_stat *s = (dirlookup_stat *)refcon; + + if (s->name && s->name[0]) { /* Search by filename */ + if (strcmp(de->name, s->name[0])) return 0; /* Not it! */ + if (s->vnode) s->vnode[0] = de->vnode; + if (s->vuniq) s->vuniq[0] = de->uniq; + } else if (s->vnode) { /* Search by vnode */ + if (de->vnode != s->vnode[0]) return 0; /* Not it! */ + if (s->name) { + s->name[0] = (char *)malloc(strlen(de->name) + 1); + if (!s->name[0]) return ENOMEM; + strcpy(s->name[0], de->name); + } + if (s->vuniq) s->vuniq[0] = de->uniq; + } + return DSERR_DONE; +} + + +/* Look up an entry in a directory, by name or vnode. + * If *name is NULL, we are looking up by vnode. + * Otherwise, we are looking for a filename. + * In any event, any of name, vnode, vuniq that are + * neither NULL nor the search key are filled in on + * success. + * + * Call this with X pointing to the start of the directory, + * and size set to the length of the directory. + * Returns 0 on success, whether or not the entry is found. + */ +afs_uint32 DirectoryLookup(XFILE *X, dump_parser *p, afs_uint32 size, + char **name, afs_uint32 *vnode, afs_uint32 *vuniq) +{ + dump_parser my_p; + dirlookup_stat my_s; + afs_uint32 r; + + memset(&my_s, 0, sizeof(my_s)); + my_s.name = name; + my_s.vnode = vnode; + my_s.vuniq = vuniq; + + memset(&my_p, 0, sizeof(my_p)); + my_p.refcon = (void *)&my_s; + my_p.err_refcon = p->err_refcon; + my_p.cb_error = p->cb_error; + my_p.cb_dirent = dirlookup_cb; + + r = parse_directory(X, &my_p, 0, size, 0); + if (!r) r = DSERR_DONE; + return handle_return(r, X, 0, p); +} + + +/* AFS directory format: + * AFS directories are stored in volume dumps in exactly the same format + * that is used on disk, which makes them relatively easy to dump and restore, + * but means we have to do some work to interpret them. + * + * The ACL for a directory is stored on disk in the last part of a "large" + * (directory) vnode. This part of the vnode, which has fixed size + * SIZEOF_LARGEDISKVNODE - SIZEOF_SMALLDISKVNODE, is copied directly into + * the dump file with a tag of 'A' (VTAG_ACL). The structure of this + * section is described in . + * + * The name-to-vnode mappings are also stored exactly as they appear on + * disk, using the file data ('f') attribute. As usual, this attribute + * consists of a 32-bit number containing the size, immediately followed + * by the data itself. The interesting structures and constants are + * defined in + * + * A directory consists of one or more 'pages', each of which is 2K + * (AFS_PAGESIZE). Each page contains EPP (currently 64) 'entries', each + * of which is 32 bytes. The first page begins with a DirHeader, which + * is DHE entries long, and includes a PageHeader. All other pages begin + * with just a PageHeader, which is 1 entry long. Every other entry is + * a DirEntry, a DirXEntry (name extension), or unused. + * + * A Page Header contains the following elements: + * - pgcount contains a count of the number of pages in the directory, + * if the directory is new-style (>128 pages), or 0 if it is + * old-style. This field is meaningful only in the Dir Header. + * - tag a magic number, which must be 1234 + * - freecount apparently unused + * - freebitmap A bitmap of free entries. Each byte corresponds to 8 + * entries, with the least significant bit referring to the + * first of those. Each bit is set iff the corresponding + * entry is allocated. Entries used by the page and dir + * headers are considered allocated. + * + * A Dir Header consists of a Page Header, followed by an allocation map + * and hash table. The allocation map contains one byte for each of the + * first 128 pages; that byte contains the number of entries in that page + * that are allocated. Every page that actually exists has at peast one + * entry allocated (the Page Header); if a byte in this map is 0, it means + * that the page does not yet exist. + * + * Each bucket in the hash table is a linked list, using 'blob numbers' + * as pointers. A blob number is defined as (page# * EPP) + entry#. + * The head of each chain is kept in the hash table, and the next pointers + * are kept in the 'next' entry of each directory. + * + * Directory entries themselves contain the following elements: + * - flag Set to FFIRST iff this is the first blob in an entry + * (otherwise it will be a name continuation). This is + * probably not reliable. + * - length Unused + * - next Pointer to the next element in this hash chain + * - fid FileID (vnode and uniquifier) + * - name Filename (null-terminated) + */ diff --git a/src/tests/dump.c b/src/tests/dump.c new file mode 100644 index 0000000000..8d228fd44d --- /dev/null +++ b/src/tests/dump.c @@ -0,0 +1,229 @@ +/* + * CMUCS AFStools + * dumpscan - routines for scanning and manipulating AFS volume dumps + * + * Copyright (c) 1998 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software_Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ + +/* dump.c - Write out parts of a volume dump */ + +#include "dumpscan.h" +#include "dumpfmt.h" + +#define COPYBUFSIZE 65536 + +afs_uint32 DumpDumpHeader(XFILE *OX, afs_dump_header *hdr) +{ + afs_uint32 r; + + if (r = WriteTagInt32Pair(OX, TAG_DUMPHEADER, hdr->magic, hdr->version)) + return r; + + if (hdr->field_mask & F_DUMPHDR_VOLID) { + if (r = WriteTagInt32(OX, DHTAG_VOLID, hdr->volid)) return r; + } + if (hdr->field_mask & F_DUMPHDR_VOLNAME) { + if (r = WriteByte(OX, DHTAG_VOLNAME)) return r; + if (r = WriteString(OX, hdr->volname)) return r; + } + if (hdr->field_mask & (F_DUMPHDR_FROM | F_DUMPHDR_TO)) { + if (r = WriteTagInt16(OX, DHTAG_DUMPTIMES, 2)) + return r; + if (r = WriteInt32(OX, (hdr->field_mask & F_DUMPHDR_FROM) + ? hdr->from_date : 0)) + return r; + if (r = WriteInt32(OX, (hdr->field_mask & F_DUMPHDR_TO) + ? hdr->to_date : time(0))) + return r; + } + return 0; +} + + +afs_uint32 DumpVolumeHeader(XFILE *OX, afs_vol_header *hdr) +{ + afs_uint32 r; + int i; + + if (r = WriteByte(OX, TAG_VOLHEADER)) return r; + + if (hdr->field_mask & F_VOLHDR_VOLID) { + if (r = WriteTagInt32(OX, VHTAG_VOLID, hdr->volid)) return r; + } + if (hdr->field_mask & F_VOLHDR_VOLVERS) { + if (r = WriteTagInt32(OX, VHTAG_VERS, hdr->volvers)) return r; + } + if (hdr->field_mask & F_VOLHDR_VOLNAME) { + if (r = WriteByte(OX, VHTAG_VOLNAME)) return r; + if (r = WriteString(OX, hdr->volname)) return r; + } + if (hdr->field_mask & F_VOLHDR_INSERV) { + if (r = WriteTagByte(OX, VHTAG_INSERV, hdr->flag_inservice)) return r; + } + if (hdr->field_mask & F_VOLHDR_BLESSED) { + if (r = WriteTagByte(OX, VHTAG_BLESSED, hdr->flag_blessed)) return r; + } + if (hdr->field_mask & F_VOLHDR_VOLUNIQ) { + if (r = WriteTagInt32(OX, VHTAG_VUNIQ, hdr->voluniq)) return r; + } + if (hdr->field_mask & F_VOLHDR_VOLTYPE) { + if (r = WriteTagByte(OX, VHTAG_TYPE, hdr->voltype)) return r; + } + if (hdr->field_mask & F_VOLHDR_PARENT) { + if (r = WriteTagInt32(OX, VHTAG_PARENT, hdr->parent_volid)) return r; + } + if (hdr->field_mask & F_VOLHDR_CLONE) { + if (r = WriteTagInt32(OX, VHTAG_CLONE, hdr->clone_volid)) return r; + } + if (hdr->field_mask & F_VOLHDR_MAXQ) { + if (r = WriteTagInt32(OX, VHTAG_MAXQUOTA, hdr->maxquota)) return r; + } + if (hdr->field_mask & F_VOLHDR_MINQ) { + if (r = WriteTagInt32(OX, VHTAG_MINQUOTA, hdr->minquota)) return r; + } + if (hdr->field_mask & F_VOLHDR_DISKUSED) { + if (r = WriteTagInt32(OX, VHTAG_DISKUSED, hdr->diskused)) return r; + } + if (hdr->field_mask & F_VOLHDR_NFILES) { + if (r = WriteTagInt32(OX, VHTAG_FILECNT, hdr->nfiles)) return r; + } + if (hdr->field_mask & F_VOLHDR_ACCOUNT) { + if (r = WriteTagInt32(OX, VHTAG_ACCOUNT, hdr->account_no)) return r; + } + if (hdr->field_mask & F_VOLHDR_OWNER) { + if (r = WriteTagInt32(OX, VHTAG_OWNER, hdr->owner)) return r; + } + if (hdr->field_mask & F_VOLHDR_CREATE_DATE) { + if (r = WriteTagInt32(OX, VHTAG_CREAT, hdr->create_date)) return r; + } + if (hdr->field_mask & F_VOLHDR_ACCESS_DATE) { + if (r = WriteTagInt32(OX, VHTAG_ACCESS, hdr->access_date)) return r; + } + if (hdr->field_mask & F_VOLHDR_UPDATE_DATE) { + if (r = WriteTagInt32(OX, VHTAG_UPDATE, hdr->update_date)) return r; + } + if (hdr->field_mask & F_VOLHDR_EXPIRE_DATE) { + if (r = WriteTagInt32(OX, VHTAG_EXPIRE, hdr->expire_date)) return r; + } + if (hdr->field_mask & F_VOLHDR_BACKUP_DATE) { + if (r = WriteTagInt32(OX, VHTAG_BACKUP, hdr->backup_date)) return r; + } + if (hdr->field_mask & F_VOLHDR_OFFLINE_MSG) { + if (r = WriteTagInt32(OX, VHTAG_OFFLINE, hdr->offline_msg)) return r; + } + if (hdr->field_mask & F_VOLHDR_MOTD) { + if (r = WriteTagInt32(OX, VHTAG_MOTD, hdr->motd_msg)) return r; + } + if (hdr->field_mask & F_VOLHDR_WEEKUSE) { + if (r = WriteTagInt16(OX, VHTAG_WEEKUSE, 7)) return r; + for (i = 0; i < 7; i++) + if (r = WriteInt32(OX, hdr->weekuse[i])) return r; + } + if (hdr->field_mask & F_VOLHDR_DAYUSE_DATE) { + if (r = WriteTagInt32(OX, VHTAG_DUDATE, hdr->dayuse_date)) return r; + } + if (hdr->field_mask & F_VOLHDR_DAYUSE) { + if (r = WriteTagInt32(OX, VHTAG_DAYUSE, hdr->dayuse)) return r; + } + return 0; +} + + +afs_uint32 DumpVNode(XFILE *OX, afs_vnode *v) +{ + afs_uint32 r; + + if (r = WriteTagInt32Pair(OX, TAG_VNODE, v->vnode, v->vuniq)) return r; + + if (v->field_mask & F_VNODE_TYPE) { + if (r = WriteTagByte(OX, VTAG_TYPE, v->type)) return r; + } + if (v->field_mask & F_VNODE_NLINKS) { + if (r = WriteTagInt16(OX, VTAG_NLINKS, v->nlinks)) return r; + } + if (v->field_mask & F_VNODE_DVERS) { + if (r = WriteTagInt32(OX, VTAG_DVERS, v->datavers)) return r; + } + if (v->field_mask & F_VNODE_SDATE) { + if (r = WriteTagInt32(OX, VTAG_SERVER_DATE, v->server_date)) return r; + } + if (v->field_mask & F_VNODE_AUTHOR) { + if (r = WriteTagInt32(OX, VTAG_AUTHOR, v->author)) return r; + } + if (v->field_mask & F_VNODE_OWNER) { + if (r = WriteTagInt32(OX, VTAG_OWNER, v->owner)) return r; + } + if (v->field_mask & F_VNODE_GROUP) { + if (r = WriteTagInt32(OX, VTAG_GROUP, v->group)) return r; + } + if (v->field_mask & F_VNODE_MODE) { + if (r = WriteTagInt16(OX, VTAG_MODE, v->mode)) return r; + } + if (v->field_mask & F_VNODE_PARENT) { + if (r = WriteTagInt32(OX, VTAG_PARENT, v->parent)) return r; + } + if (v->field_mask & F_VNODE_CDATE) { + if (r = WriteTagInt32(OX, VTAG_CLIENT_DATE, v->client_date)) return r; + } + if (v->field_mask & F_VNODE_ACL) { + if (r = WriteByte(OX, VTAG_ACL)) return r; + if (r = xfwrite(OX, v->acl, SIZEOF_LARGEDISKVNODE - SIZEOF_SMALLDISKVNODE)) + return r; + } + return 0; +} + + +afs_uint32 DumpVNodeData(XFILE *OX, char *buf, afs_uint32 size) +{ + afs_uint32 r; + + if (r = WriteTagInt32(OX, VTAG_DATA, size)) return r; + if (r = xfwrite(OX, buf, size)) return r; + return 0; +} + + +afs_uint32 CopyVNodeData(XFILE *OX, XFILE *X, afs_uint32 size) +{ + afs_uint32 r, n; + static char buf[COPYBUFSIZE]; + + if (r = WriteTagInt32(OX, VTAG_DATA, size)) return r; + while (size) { + n = (size > COPYBUFSIZE) ? COPYBUFSIZE : size; + if (r = xfread(X, buf, n)) return r; + if (r = xfwrite(OX, buf, n)) return r; + size -= n; + } + return 0; +} + + +afs_uint32 DumpDumpEnd(XFILE *OX) { + afs_uint32 r; + + if (r = WriteTagInt32(OX, TAG_DUMPEND, DUMPENDMAGIC)) return r; + return 0; +} diff --git a/src/tests/dumpfmt.h b/src/tests/dumpfmt.h new file mode 100644 index 0000000000..766b6977f9 --- /dev/null +++ b/src/tests/dumpfmt.h @@ -0,0 +1,150 @@ +/* + * CMUCS AFStools + * dumpscan - routines for scanning and manipulating AFS volume dumps + * + * Copyright (c) 1998 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software_Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ + +/* dumpfmt.h - Description of AFS dump format */ + +#ifndef _DUMPFMT_H_ +#define _DUMPFMT_H_ + +#include "intNN.h" + +/* AFS dump file format: + * All data in AFS dumps is tagged; that is, each data item is preceeded + * by a 1-byte tag which identifies what the data item is. There is no + * explicit mention of what the data type is, but the type of each possible + * data item (and thus, each possible tag) is fixed. Usually this is + * a relatively simple, fixed amount of data (byte, short, word), but + * sometimes it is more complex. + * + * There is some amount of structure to an AFS volume dump. Basically, + * you get a dump header, followed by a volume header, followed by some + * vnodes, followed by a dump end. Each of these items (header, vnode, + * dump end) consists of a tag, a fixed amount of required information, + * and 0 or more tagged attributes (except dump-end, which has no attributes). + * + * Vnodes, in turn, are usually listed in a particular order. First, we + * list all the directory vnodes in the volume, in increasing order by + * vnode. Then, we list all the file vnodes, again in increasing order. + * Directory vnodes must have a complete set of attributes and data, but + * in an incremental dump, file vnodes may have no attributes if the vnode + * has not changed since the reference date. + * + * The primary purpose of this file is to define the tags and some magic + * numbers. There is also some information that is defined in the Transarc + * provided header files. + */ + + +/** MAGIC NUMBERS **/ +#define DUMPVERSION 1 +#define DUMPBEGINMAGIC 0xb3a11322 +#define DUMPENDMAGIC 0x3a214b6e + + +/** TOP-LEVEL TAGS **/ +#define TAG_DUMPHEADER 1 +#define TAG_VOLHEADER 2 +#define TAG_VNODE 3 +#define TAG_DUMPEND 4 + + +/** DUMP HEADER TAGS **/ +#define DHTAG_VOLNAME 'n' +#define DHTAG_VOLID 'v' +#define DHTAG_DUMPTIMES 't' + + +/** VOLUME HEADER TAGS **/ +#define VHTAG_VOLID 'i' +#define VHTAG_VERS 'v' +#define VHTAG_VOLNAME 'n' +#define VHTAG_INSERV 's' +#define VHTAG_BLESSED 'b' +#define VHTAG_VUNIQ 'u' +#define VHTAG_TYPE 't' +#define VHTAG_PARENT 'p' +#define VHTAG_CLONE 'c' +#define VHTAG_MAXQUOTA 'q' +#define VHTAG_MINQUOTA 'm' +#define VHTAG_DISKUSED 'd' +#define VHTAG_FILECNT 'f' +#define VHTAG_ACCOUNT 'a' +#define VHTAG_OWNER 'o' +#define VHTAG_CREAT 'C' +#define VHTAG_ACCESS 'A' +#define VHTAG_UPDATE 'U' +#define VHTAG_EXPIRE 'E' +#define VHTAG_BACKUP 'B' +#define VHTAG_OFFLINE 'O' +#define VHTAG_MOTD 'M' +#define VHTAG_WEEKUSE 'W' +#define VHTAG_DUDATE 'D' +#define VHTAG_DAYUSE 'Z' + + +/** VNODE TAGS **/ +#define VTAG_TYPE 't' +#define VTAG_NLINKS 'l' +#define VTAG_DVERS 'v' +#define VTAG_CLIENT_DATE 'm' +#define VTAG_AUTHOR 'a' +#define VTAG_OWNER 'o' +#define VTAG_GROUP 'g' +#define VTAG_MODE 'b' +#define VTAG_PARENT 'p' +#define VTAG_SERVER_DATE 's' +#define VTAG_ACL 'A' +#define VTAG_DATA 'f' + + +#define AFS_DIR_EPP 64 + +typedef struct { + afs_uint16 pgcount; + afs_uint16 tag; + char freecount; + char freebitmap[AFS_DIR_EPP/8]; + char padding[32 - (5 + AFS_DIR_EPP/8)]; +} afs_dir_pagehdr; + +typedef struct { + char flag; + char length; + afs_uint16 next; + afs_uint32 vnode; + afs_uint32 vunique; + char name[16]; + char padding[4]; +} afs_dir_direntry; + +typedef union { + afs_dir_pagehdr header; + afs_dir_direntry entry[AFS_DIR_EPP]; +} afs_dir_page; + +#endif /* _DUMPFMT_H_ */ diff --git a/src/tests/dumpscan.h b/src/tests/dumpscan.h new file mode 100644 index 0000000000..a6b3a24428 --- /dev/null +++ b/src/tests/dumpscan.h @@ -0,0 +1,379 @@ +/* + * CMUCS AFStools + * dumpscan - routines for scanning and manipulating AFS volume dumps + * + * Copyright (c) 1998 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software_Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ + +/* dumpscan.h - Public interface */ + +#ifndef _DUMPSCAN_H_ +#define _DUMPSCAN_H_ + +#include "intNN.h" +#include "xfiles.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Random useful types */ +typedef struct tagged_field tagged_field; +typedef struct tag_parse_info tag_parse_info; +typedef afs_uint32 (*tag_parser)(XFILE *, unsigned char *, tagged_field *, + afs_uint32, tag_parse_info *, void *, void *); + +/* Error codes used within dumpscan. + * Any of the routines declared below, or callbacks used by them, + * may signal a system error by returning the error number, or + * some other error by returning a com_err code. Note that + * ParseTaggedData does _not_ return DSERR_TAG; instead, it returns + * 0, assuming the tag will be handled at a higher level. + * + * In addition, these errors may be reported to the caller of + * ParseDumpFile using the error callback. Such reports will be + * issued whether or not error recovery is possible or attempted. + * + * NB: These errors are now in dumpscan_errs.h + */ + + +/* Backup system dump header */ +/* Right now, this looks a lot like an old stage header. Eventually, it + * should contain enough fields to fully represent headers from old or + * new stage, Transarc, or other backup systems, and the appropriate read + * functions should extract as much data as possible from the actual file + * to fill this in. */ +typedef struct { + afs_uint32 version; + afs_uint32 from_date; + afs_uint32 to_date; + afs_uint32 dump_date; + afs_uint32 filenum; + unsigned char *server; + unsigned char *part; + unsigned char *volname; + afs_uint32 volid; + afs_uint32 dumplen; + afs_uint32 level; + afs_uint32 magic; + afs_uint32 cksum; + afs_uint32 flags; +} backup_system_header; + + +/** AFS dump header **/ +#define F_DUMPHDR_VOLID 0x00000001 +#define F_DUMPHDR_VOLNAME 0x00000002 +#define F_DUMPHDR_FROM 0x00000004 +#define F_DUMPHDR_TO 0x00000008 +typedef struct { + u_int64 offset; /* Where in the file is it? */ + afs_uint32 field_mask; /* What fields are present? */ + afs_uint32 magic; /* Magic number */ + afs_uint32 version; /* Dump format version */ + afs_uint32 volid; /* VolID of volume in dump */ + unsigned char *volname; /* Name of volume in dump */ + afs_uint32 from_date; /* Reference date */ + afs_uint32 to_date; /* Date of dump */ +} afs_dump_header; + + +/** AFS volume header **/ +#define F_VOLHDR_VOLID 0x00000001 +#define F_VOLHDR_VOLVERS 0x00000002 +#define F_VOLHDR_VOLNAME 0x00000004 +#define F_VOLHDR_INSERV 0x00000008 +#define F_VOLHDR_BLESSED 0x00000010 +#define F_VOLHDR_VOLUNIQ 0x00000020 +#define F_VOLHDR_VOLTYPE 0x00000040 +#define F_VOLHDR_PARENT 0x00000080 +#define F_VOLHDR_CLONE 0x00000100 +#define F_VOLHDR_MAXQ 0x00000200 +#define F_VOLHDR_MINQ 0x00000400 +#define F_VOLHDR_DISKUSED 0x00000800 +#define F_VOLHDR_NFILES 0x00001000 +#define F_VOLHDR_ACCOUNT 0x00002000 +#define F_VOLHDR_OWNER 0x00004000 +#define F_VOLHDR_CREATE_DATE 0x00008000 +#define F_VOLHDR_ACCESS_DATE 0x00010000 +#define F_VOLHDR_UPDATE_DATE 0x00020000 +#define F_VOLHDR_EXPIRE_DATE 0x00040000 +#define F_VOLHDR_BACKUP_DATE 0x00080000 +#define F_VOLHDR_OFFLINE_MSG 0x00100000 +#define F_VOLHDR_MOTD 0x00200000 +#define F_VOLHDR_WEEKUSE 0x00400000 +#define F_VOLHDR_DAYUSE 0x00800000 +#define F_VOLHDR_DAYUSE_DATE 0x01000000 +typedef struct { + u_int64 offset; /* Where in the file is it? */ + afs_uint32 field_mask; /* What fields are present? */ + afs_uint32 volid; /* Volume ID */ + afs_uint32 volvers; /* ?? */ + unsigned char *volname; /* Volume Name */ + int flag_inservice; /* Inservice flag (0 or not) */ + int flag_blessed; /* Blessed to come online (0 or not) */ + afs_uint32 voluniq; /* Volume uniquifier */ + int voltype; /* Volume type */ + afs_uint32 parent_volid; /* Parent volume ID */ + afs_uint32 clone_volid; /* Clone volume ID */ + afs_uint32 maxquota; /* Max quota */ + afs_uint32 minquota; /* Min quota (obsolete) */ + afs_uint32 diskused; /* Disk blocks used */ + afs_uint32 nfiles; /* Number of files in volume */ + afs_uint32 account_no; /* Account number (unused) */ + afs_uint32 owner; /* Volume owner */ + afs_uint32 create_date; /* Creation date of this copy */ + afs_uint32 access_date; /* Last access */ + afs_uint32 update_date; /* Last modification */ + afs_uint32 expire_date; /* Expiration (unused) */ + afs_uint32 backup_date; /* Last backup clone */ + unsigned char *offline_msg; /* Offline message */ + unsigned char *motd_msg; /* Volume MOTD */ + afs_uint32 weekuse[7]; /* Weekuse data */ + afs_uint32 dayuse; /* # accesses in last day */ + afs_uint32 dayuse_date; /* Date for which dayuse is valid */ +} afs_vol_header; + + +/** AFS vnode **/ +#define F_VNODE_TYPE 0x00000001 +#define F_VNODE_NLINKS 0x00000002 +#define F_VNODE_PARENT 0x00000004 +#define F_VNODE_DVERS 0x00000008 +#define F_VNODE_AUTHOR 0x00000010 +#define F_VNODE_OWNER 0x00000020 +#define F_VNODE_GROUP 0x00000040 +#define F_VNODE_MODE 0x00000080 +#define F_VNODE_CDATE 0x00000100 +#define F_VNODE_SDATE 0x00000200 +#define F_VNODE_SIZE 0x00000800 +#define F_VNODE_DATA 0x00001000 +#define F_VNODE_ACL 0x00000400 +typedef struct { + u_int64 offset; /* Where in the file is it? */ + afs_uint32 field_mask; /* What fields are present? */ + afs_uint32 vnode; /* Vnode number */ + afs_uint32 vuniq; /* Uniquifier */ + int type; /* Vnode type */ + afs_uint16 nlinks; /* Number of links (should be in 1 dir!) */ + afs_uint32 parent; /* Parent vnode */ + afs_uint32 datavers; /* Data version */ + afs_uint32 author; /* Last writer */ + afs_uint32 owner; /* Owner UID */ + afs_uint32 group; /* Owning group */ + afs_uint16 mode; /* UNIX mode bits */ + afs_uint32 client_date; /* Last modified date from client */ + afs_uint32 server_date; /* Last modified date on server */ + afs_uint32 size; /* Size of data */ + u_int64 d_offset; /* Where in the file is the data? */ + unsigned char acl[SIZEOF_LARGEDISKVNODE - SIZEOF_SMALLDISKVNODE]; +} afs_vnode; + + +/** AFS directory entry **/ +typedef struct { + int slot; /* Directory slot # (info only) */ + char *name; /* Name of entry */ + afs_uint32 vnode; /* Vnode number */ + afs_uint32 uniq; /* Uniquifier */ +} afs_dir_entry; + + +/** Tagged field definitions **/ +#define DKIND_NOOP 0x00 /* No data */ +#define DKIND_BYTE 0x10 /* 1 byte - decimal */ +#define DKIND_HEX8 0x11 /* 1 byte - hex */ +#define DKIND_CHAR 0x12 /* 1 byte - character */ +#define DKIND_FLAG 0x13 /* 1 byte - true/false */ +#define DKIND_INT16 0x20 /* 2 bytes - decimal */ +#define DKIND_HEX16 0x21 /* 2 bytes - hex */ +#define DKIND_INT32 0x30 /* 4 bytes - decimal */ +#define DKIND_HEX32 0x31 /* 4 bytes - hex */ +#define DKIND_TIME 0x32 /* 4 bytes - time */ +#define DKIND_STRING 0x40 /* ASCIIZ string */ +#define DKIND_SPECIAL 0x50 /* Custom parser */ +#define DKIND_MASK (~0x0f) +struct tag_parse_info { + void *err_refcon; + afs_uint32 (*cb_error)(afs_uint32, int, void *, char *, ...); + afs_uint32 flags; +#define TPFLAG_SKIP 0x0001 +#define TPFLAG_RSKIP 0x0002 + int shift_offset; + u_int64 shift_start; +}; +struct tagged_field { + char tag; /* Tag character */ + int kind; /* Kind of object */ + char *label; /* Label to use (for debugging) */ + tag_parser func; /* Parser function (for DKIND_SPECIAL) */ + void *refptr; /* Reference pointer (for parser's use) */ + int refarg; /* Reference argument (for parser's use) */ +}; + + +/** Control structure for parsing volume dumps **/ +typedef struct { + /* Callback functions: + * Whenever a "complex" object is parsed, we call a callback function. + * The callback gets a pointer to the complex object, the file pointer + * for the dump we're parsing, and the value of refcon in this structure. + * Callbacks should return 0 if all is well, non-0 to abort the dump. + * By convention, positive numbers should be errno values, and negative + * numbers can be used for other things. It is OK to _try_ to seek anywhere + * in the file. Beware, though, that the input is not always seekable. + * Also, note that the structures passed to these callbacks are going to + * go away after the callback returns. There is no way to prevent this; + * make a copy if you want one. + */ + void *refcon; + afs_uint32 (*cb_bckhdr)(backup_system_header *, XFILE *, void *); /* Backup */ + afs_uint32 (*cb_dumphdr)(afs_dump_header *, XFILE *, void *); /* Dump hdr */ + afs_uint32 (*cb_volhdr)(afs_vol_header *, XFILE *, void *); /* Volume hdr */ + afs_uint32 (*cb_vnode_dir)(afs_vnode *, XFILE *, void *); /* Directory */ + afs_uint32 (*cb_vnode_file)(afs_vnode *, XFILE *, void *); /* File */ + afs_uint32 (*cb_vnode_link)(afs_vnode *, XFILE *, void *); /* Symlink */ + afs_uint32 (*cb_vnode_empty)(afs_vnode *, XFILE *, void *); /* vnode+uniq */ + afs_uint32 (*cb_vnode_wierd)(afs_vnode *, XFILE *, void *); /* Unknown type */ + + /* This function is called when there is an error in the dump. */ + /* (cb_error)(errno, fatal, refcon, msg_fmt, msg_args...) */ + void *err_refcon; /* If set, use instead of refcon for dir entries */ + afs_uint32 (*cb_error)(afs_uint32, int, void *, char *, ...); + + /* This function is called for each directory entry, if set */ + afs_uint32 (*cb_dirent)(afs_vnode *, afs_dir_entry *, XFILE *, void *); + + int flags; /* Flags and options */ +#define DSFLAG_SEEK 0x0001 /* Input file is seekable */ + + int print_flags; /* Flags to control what is printed */ +#define DSPRINT_BCKHDR 0x0001 /* Print backup system header */ +#define DSPRINT_DUMPHDR 0x0002 /* Print AFS dump header */ +#define DSPRINT_VOLHDR 0x0004 /* Print AFS volume header */ +#define DSPRINT_ITEM 0x0010 /* Print top-level tags */ +#define DSPRINT_VNODE 0x0020 /* Print vnode attributes */ +#define DSPRINT_ACL 0x0040 /* Print directory ACL's */ +#define DSPRINT_DIR 0x0080 /* Print directory contents */ +#define DSPRINT_DEBUG 0x0100 /* Print debugging info */ +#define DSPRINT_PATH 0x0200 /* Print vnode paths */ + + int repair_flags; /* Flags to control what is repaired. + * Most of these _require_ DSFLAG_SEEK */ +#define DSFIX_SKIP 0x0001 /* Try to skip null tags */ +#define DSFIX_RSKIP 0x0002 /* Seek back to fing skipped tags */ +#define DSFIX_VDSYNC 0x0004 /* Resync location after vnode data */ +#define DSFIX_VFSYNC 0x0008 /* Try to resync after bad vnode */ + + /** Things below this point for internal use only **/ + afs_uint32 vol_uniquifier; +} dump_parser; + + +/** Hash table and control info for pathname manipulation **/ +typedef struct vhash_ent { + struct vhash_ent *next; /* Pointer to next entry */ + afs_uint32 vnode; /* VNode number */ + afs_uint32 parent; /* Parent VNode number */ + u_int64 v_offset; /* Offset to start of vnode */ + u_int64 d_offset; /* Offset to data (0 if none) */ + afs_uint32 d_size; /* Size of data */ +} vhash_ent; +typedef struct { + afs_uint32 n_vnodes; /* Number of vnodes in volume */ + afs_uint32 n_dirs; /* Number of file vnodes */ + afs_uint32 n_files; /* Number of directory vnodes */ + int hash_size; /* Hash table size (bits) */ + vhash_ent **hash_table; /* Hash table */ + dump_parser *p; /* Dump parser to use */ +} path_hashinfo; + + +/** Function prototypes **/ +/** Only the functions declared below are public interfaces **/ +/** Maybe someday, I'll write man pages for these **/ + +/* primitive.c - I/O primitives */ +extern afs_uint32 ReadByte(XFILE *, unsigned char *); +extern afs_uint32 ReadInt16(XFILE *, afs_uint16 *); +extern afs_uint32 ReadInt32(XFILE *, afs_uint32 *); +extern afs_uint32 ReadString(XFILE *, unsigned char **); +extern afs_uint32 WriteByte(XFILE *, unsigned char); +extern afs_uint32 WriteInt16(XFILE *, afs_uint16); +extern afs_uint32 WriteInt32(XFILE *, afs_uint32); +extern afs_uint32 WriteString(XFILE *, unsigned char *); +extern afs_uint32 WriteTagByte(XFILE *, unsigned char, unsigned char); +extern afs_uint32 WriteTagInt16(XFILE *, unsigned char, afs_uint16); +extern afs_uint32 WriteTagInt32(XFILE *, unsigned char, afs_uint32); +extern afs_uint32 WriteTagInt32Pair(XFILE *, unsigned char, afs_uint32, afs_uint32); + +/* parsetag.c - Parse tagged data */ +extern afs_uint32 ParseTaggedData(XFILE *, tagged_field *, unsigned char *, + tag_parse_info *, void *, void *); + +/* stagehdr.c - Parse and dump Stage dump headers */ +extern afs_uint32 ParseStageHdr(XFILE *, unsigned char *, backup_system_header *); +extern afs_uint32 DumpStagehdr(XFILE *, backup_system_header *); + +/* backuphdr.c - Parse and print backup system headers */ +extern void PrintBackupHdr(backup_system_header *); + +/* parsedump.c - Parse all or part of a volume dump */ +extern afs_uint32 ParseDumpFile(XFILE *, dump_parser *); +extern afs_uint32 ParseDumpHeader(XFILE *, dump_parser *); +extern afs_uint32 ParseVolumeHeader(XFILE *, dump_parser *); +extern afs_uint32 ParseVNode(XFILE *, dump_parser *); + + +/* directory.c - Directory parsing and lookup */ +extern afs_uint32 ParseDirectory(XFILE *, dump_parser *, afs_uint32, int); +extern afs_uint32 DirectoryLookup(XFILE *, dump_parser *, afs_uint32, + char **, afs_uint32 *, afs_uint32 *); + +/* dump.c - Dump parts of a volume dump */ +extern afs_uint32 DumpDumpHeader(XFILE *, afs_dump_header *); +extern afs_uint32 DumpVolumeHeader(XFILE *, afs_vol_header *); +extern afs_uint32 DumpVNode(XFILE *, afs_vnode *); +extern afs_uint32 DumpVnodeData(XFILE *, char *, afs_uint32); +extern afs_uint32 CopyVnodeData(XFILE *, XFILE *, afs_uint32); + +/* pathname.c - Follow and construct pathnames */ +extern afs_uint32 Path_PreScan(XFILE *, path_hashinfo *, int); +extern void Path_FreeHashTable(path_hashinfo *); +extern afs_uint32 Path_Follow(XFILE *, path_hashinfo *, char *, vhash_ent *); +extern afs_uint32 Path_Build(XFILE *, path_hashinfo *, afs_uint32, char **, int); + +#endif diff --git a/src/tests/dumpscan_errs.et b/src/tests/dumpscan_errs.et new file mode 100644 index 0000000000..22c38c3974 --- /dev/null +++ b/src/tests/dumpscan_errs.et @@ -0,0 +1,37 @@ +# COPYRIGHT NOTICE +# Copyright (c) 1997 Carnegie Mellon University +# All Rights Reserved. +# +# Permission to use, copy, modify and distribute this software and its +# documentation is hereby granted, provided that both the copyright +# notice and this permission notice appear in all copies of the +# software, derivative works or modified versions, and any portions +# thereof, and that both notices appear in supporting documentation. +# +# CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" +# CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR +# ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. +# +# Carnegie Mellon requests users of this software to return to +# +# Software Distribution Coordinator or Software_Distribution@CS.CMU.EDU +# School of Computer Science +# Carnegie Mellon University +# Pittsburgh PA 15213-3890 +# +# any improvements or extensions that they make and grant Carnegie Mellon +# the rights to redistribute these changes. + +# UB - Unified Backups +# methods/afs/dumpscan/afsdump_errs.et - AFS dump scanner errors + +error_table AVds + ec DSERR_TAG, "Unknown tag in AFS volume dump" + ec DSERR_MAGIC, "Bad magic number in AFS volume dump" + ec DSERR_BOGUS, "Bogus value in AFS volume dump" + ec DSERR_FMT, "AFS volume dump format incorrect" + ec DSERR_KEEP, "[AFS dumpscan internal: keep string]" + ec DSERR_PANIC, "[AFS dumpscan internal: panic]" + ec DSERR_DONE, "[AFS dumpscan internal: done]" + ec DSERR_MEM, "[AFS dumpscan internal: out of memory]" +end diff --git a/src/tests/dumptool.c b/src/tests/dumptool.c new file mode 100644 index 0000000000..5f6e73b9a7 --- /dev/null +++ b/src/tests/dumptool.c @@ -0,0 +1,2640 @@ +/* + * $Id$ + * + * dumptool - A tool to manage MR-AFS dump files + * + * The dump file format ought to be documented _somewhere_, and + * this seems like a good as a place as any ... + * + * A AFS dump file is marked off into a series of sections. Each + * section marked by a dump tag. A tag is a single byte who's value + * corresponds with the next section. The sections are (in order): + * + * DUMPHEADER (tag 0x01) + * VOLUMEHEADER (tag 0x02) + * VNODE (tag 0x03) + * DUMPEND (tag 0x04) + * + * Descriptions of the sections follow. Note that in all cases, data is + * stored in the dump in network byte order. + * + * DUMPHEADER: + * + * DUMPHEADER contains two parts: the DUMPMAGIC magic number (32 bits) + * and the dump header itself. + * + * The dump header itself consists of a series of tagged values, + * each tag marking out members of the DumpHeader structure. The + * routine ReadDumpHeader explains the specifics of these tags. + * + * VOLUMEHEADER: + * + * VOLUMEHEADER is a series of tagged values corresponding to the elements + * of the VolumeDiskData structure. See ReadVolumeHeader for more + * information + * + * VNODE: + * + * The VNODE section is all vnodes contained in the volume (each vnode + * itself is marked with the VNODE tag, so it's really a sequence of + * VNODE tags, unlike other sections). + * + * Each vnode consists of three parts: the vnode number (32 bits), the + * uniqifier (32 bits), and a tagged list of elements corresponding to + * the elements of the VnodeDiskData structure. See ScanVnodes for + * more information. Note that if file data is associated with a vnode, + * it will be contained here. + * + * DUMPEND: + * + * The DUMPEND section consists of one part: the DUMPENDMAGIC magic + * number (32 bits). + * + * Notes: + * + * The tagged elements are all ASCII letters, as opposed to the section + * headers (which are 0x01, 0x02, ...). Thus, an easy way to tell when + * you've reached the end of an element sequence is to check to see if + * the next tag is a printable character (this code tests for < 20). + * + * "vos dump" dumps the large vnode index, then the small vnode index, + * so directories will appear first in the VNODE section. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#if !defined(PRE_AFS_36) && !defined(RESIDENCY) +#include +#endif /* !defined(PRE_AFS_36) && !defined(RESIDENCY) */ +#include +#include + +#ifdef RESIDENCY +#include +#include +#endif /* RESIDENCY */ + +#include + +/* + * Sigh. Linux blows it again + */ + +#ifdef linux +#include +#endif + +/* + * Stuff that is in private AFS header files, unfortunately + */ + +#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 + +struct DumpHeader { + int32_t version; + VolumeId volumeId; + char volumeName[VNAMESIZE]; + int nDumpTimes; /* Number of pairs */ + struct { + int32_t from, to; + } dumpTimes[MAXDUMPTIMES]; +}; + +/* + * Our command-line arguments + */ + +#ifdef RESIDENCY +struct { + int Algorithm; /* Conversion algorithm */ + int Size; /* Directory hierarchy size */ + int FSType; /* File system type */ + int DeviceTag; /* Device Tag */ +} rscmdlineinfo[RS_MAXRESIDENCIES]; + +/* + * This stuff comes from ufsname.c (which itself takes it from + * ufs_interfaces.c) + */ + +/* There is an assumption that all of the prefixes will have exactly one '/' */ +static char *Ufs_Prefixes[] = {"/ufs", "/slowufs", "/cdmf", "/sdmf"}; +#define MAX_ITERATIONS 10 +#define UFS_SUMMARYTREENAME "Summaries" +#define UFS_STAGINGTREENAME "Staging" +#define UFS_VOLUMEHEADERTREENAME "VolHeaders" +#define UFS_VOLUMETREENAME "Volumes" +#define UFS_ALGORITHMBASE 'A' +#define UFS_MOUNTPOINTBASE 'a' +#define UFS_ALGORITHMS 3 +#define UFS_LINK_MAX 64 /* Arbitrary. */ +#define HARD_LINKED_FILE -2 +#define TAGSTONAME(FileName, MountPoint, Sections, Level1, RWVolume, Vnode, Uniquifier, Algorithm) \ +{ \ + if (Level1) \ + sprintf(FileName,"%s/%lu/%lu/%c%lu.%lu.%lu.%lu.%lu", MountPoint, \ + (Sections)[0], (Sections)[1], UFS_ALGORITHMBASE + Algorithm, \ + (Sections)[2], (Sections)[3], RWVolume, Vnode, Uniquifier); \ + else \ + sprintf(FileName,"%s/%lu/%c%lu.%lu.%lu.%lu.%lu", MountPoint, \ + (Sections)[0], UFS_ALGORITHMBASE + Algorithm, \ + (Sections)[1], (Sections)[2], RWVolume, Vnode, Uniquifier); \ +} +#define TAGSTOSTAGINGNAME(FileName, MountPoint, RWVolume, Vnode, Uniquifier) \ + sprintf(FileName,"%s/%s/%lu.%lu.%lu", MountPoint, \ + UFS_STAGINGTREENAME, RWVolume, Vnode, Uniquifier) +#define TAGSTOVOLUMEHEADERNAME(FileName, MountPoint, FileTag1, FileTag2) \ + sprintf(FileName,"%s/%s/%lu", MountPoint, UFS_VOLUMEHEADERTREENAME, \ + FileTag1) +#define TAGSTOVOLUMEINFONAME(FileName, MountPoint, FileTag1, FileTag2) \ + sprintf(FileName,"%s/%s/%lu/%lu", MountPoint, \ + UFS_VOLUMETREENAME, FileTag2, FileTag1) +#define TAGSTOVOLUMENAME(FileName, MountPoint, FileTag1, FileTag2, RWVolume) \ + sprintf(FileName,"%s/%s/%lu/%lu.%lu", MountPoint, \ + UFS_VOLUMETREENAME, FileTag2, FileTag1, RWVolume) +#define TAGSTOSUMMARYNAME(FileName, MountPoint, FileTag1, FileTag2, SummaryRequestor, Residency) \ + sprintf(FileName,"%s/%s/%lu.%lu.%lu.%lu", MountPoint, \ + UFS_SUMMARYTREENAME, FileTag1, FileTag2, SummaryRequestor, \ + Residency) +#define DEVICETAGNUMBERTOMOUNTPOINT(MountPoint, DeviceTagNumber, FSType) \ + sprintf(MountPoint,"%s%c", Ufs_Prefixes[FSType], UFS_MOUNTPOINTBASE + \ + DeviceTagNumber) +#define MOUNTPOINTTODEVICETAGNUMBER(MountPoint) \ + MountPoint[strlen(MountPoint) - 1] - UFS_MOUNTPOINTBASE +#define DEVICETAGNUMBERTOVOLUMEHEADERTREE(TreeName, MountPoint) \ + sprintf(TreeName,"%s/%s", MountPoint, UFS_VOLUMEHEADERTREENAME) +#define UFS_RESIDENCIES_FILE "Residencies" + +/* We don't ever want to map to uid/gid -1. fchown() takes that as a + don't change flag. We know however that volume number range from + 0x2000000 to 0x20FFFFFF (see VAllocateVolumeId() in vol/vutil.c) + so we will use that to insure that -1 never appears. */ +#define RWVolumeToUid(RWVolume) ((RWVolume >> 12) & 0xFFFF) +#define RWVolumeToGid(RWVolume) ((RWVolume & 0xFFF) | \ + (((RWVolume >> 28) & 0xF) << 12)) +#define UidGidToRWVolume(Uid, Gid) ((Gid & 0xFFF) | ((Uid & 0xFFFF) << 12) | \ + ((Gid & 0xF000) << 16)) + + +/* These routines generate a file name to correspond to the given tag + numbers. */ + +/* The following entropy array contains the order of bits from highest entropy + to lowest in the numbers FileTag1 and FileTag2. Bit numbers 32 and above + correspond to FileTag2. This ordering was determined by examining all read- + write volumes in the psc.edu cell. */ +char UfsEntropy[1][64] = { + {1, 2, 3, 4, 33, 5, 6, 7, 44, 45, 46, 36, 8, 34, 42, 35, + 9, 40, 38, 32, 43, 10, 39, 37, 11, 41, 12, 13, 14, 0, + 15, 16, 61, 17, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, + 50, 49, 48, 47, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, + 21, 20, 19, 18, 62, 63}, +}; + +uint32_t Directories[3][2] = { {256, 0}, {256, 16}, {256, 256}, }; +#endif /* RESIDENCY */ + +static int verbose = 0; +static int numNoDirData = 0; +static int termsize = 0; +int Testing = 0; +#ifdef RESIDENCY +extern resid ServerRequestorId; +#endif /* RESIDENCY */ + +/* + * We use this structure to hold vnode data in our hash table. + * It's indexed by vnode number. + */ + +struct vnodeData { + struct VnodeDiskObject *vnode; /* A pointer to the disk vnode */ + int vnodeNumber; /* The vnode number */ + long dumpdata; /* File offset of dump data (if + available */ + unsigned char *filedata; /* A pointer to the actual file + data itself (if available) */ + unsigned int datalength; /* The length of the data */ +}; + +/* + * This contains the current location when we're doing a scan of a + * directory. + */ + +struct DirCursor { + int hashbucket; /* Current hash bucket */ + int entry; /* Entry within hash bucket */ +}; + +/* + * Arrays to hold vnode data + */ + +struct vnodeData **LargeVnodeIndex; +struct vnodeData **SmallVnodeIndex; +int numLargeVnodes = 0; +int numSmallVnodes = 0; + +/* + * Crap for the libraries + */ + +int ShutdownInProgress = 0; + +/* + * Our local function prototypes + */ + +static int ReadDumpHeader(FILE *, struct DumpHeader *); +static int ReadVolumeHeader(FILE *, VolumeDiskData *); +static int ScanVnodes(FILE *, VolumeDiskData *, int); +static int DumpVnodeFile(FILE *, struct VnodeDiskObject *, VolumeDiskData *); +static struct vnodeData *InsertVnode(unsigned int, struct VnodeDiskObject *); +static struct vnodeData *GetVnode(unsigned int); +static int CompareVnode(const void *, const void *); +static void InteractiveRestore(FILE *, VolumeDiskData *); +static void DirectoryList(int, char **, struct vnodeData *, VolumeDiskData *); +static void DirListInternal(struct vnodeData *, char *[], int, int, int, int, + int, int, VolumeDiskData *, char *); +static int CompareDirEntry(const void *, const void *); +static struct vnodeData *ChangeDirectory(int, char **, struct vnodeData *); +static void CopyFile(int, char **, struct vnodeData *, FILE *); +static void CopyVnode(int, char **, FILE *); +static void DumpAllFiles(int, char **, struct vnodeData *, VolumeDiskData *); +static void DumpAllResidencies(FILE *, struct vnodeData *, VolumeDiskData *); +static struct vnodeData *FindFile(struct vnodeData *, char *); +static void ResetDirCursor(struct DirCursor *, struct vnodeData *); +static struct DirEntry *ReadNextDir(struct DirCursor *, struct vnodeData *); +static void MakeArgv(char *, int *, char ***); +static char *GetToken(char *, char **, char *, char *[]); +static int ReadInt16(FILE *, uint16_t *); +static int ReadInt32(FILE *, uint32_t *); +static int ReadString(FILE *, char *, int); +static int ReadByteString(FILE *, void *, int); + +int +main(int argc, char *argv[]) +{ + int c, errflg = 0, dumpvnodes = 0, force = 0, inode = 0; + unsigned int magic; + struct DumpHeader dheader; + VolumeDiskData vol; + long offset; + int Res, Arg1, Arg2, Arg3, i; + char *p; + struct winsize win; + FILE *f; + +#ifdef RESIDENCY + for (i = 0; i < RS_MAXRESIDENCIES; i++) { + rscmdlineinfo[i].Algorithm = -1; + rscmdlineinfo[i].Size = -1; + rscmdlineinfo[i].DeviceTag = -1; + rscmdlineinfo[i].FSType = - 1; + } +#endif /* RESIDENCY */ + + /* + * Sigh, this is dumb, but we need the terminal window size + * to do intelligent things with "ls" later on. + */ + + if (isatty(STDOUT_FILENO)) { + if ((p = getenv("COLUMNS")) != NULL) + termsize = atoi(p); + else if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &win) == 0 && + win.ws_col > 0) + termsize = win.ws_col; + } + + while ((c = getopt(argc, argv, "difr:t:v")) != EOF) + switch (c) { + case 't': +#ifdef RESIDENCY + if (sscanf(optarg, "%d/%d", &Res, &Arg1) != 2) { + errflg++; + break; + } + + if (1 << (ffs(Res) - 1) != Res) { + fprintf(stderr, "Invalid residency %d\n", Res); + errflg++; + break; + } + + if (Arg1 < 0 || Arg1 > 26) { + fprintf(stderr, "Invalid device tag: %d\n", + Arg1); + errflg++; + break; + } + rscmdlineinfo[ffs(Res) - 1].DeviceTag = Arg1; +#else /* RESIDENCY */ + fprintf(stderr, "-t not supported in non-MRAFS " + "dumptool.\n"); + errflg++; +#endif /* RESIDENCY */ + break; + + case 'r': +#ifdef RESIDENCY + if (sscanf(optarg, "%d/%d/%d/%d", &Res, &Arg1, &Arg2, + &Arg3) != 4) { + errflg++; + break; + } + + if (Arg1 < 0 || Arg1 > 3) { + fprintf(stderr, "Invalid fstype: %d\n", Arg1); + errflg++; + break; + } + + if (Arg2 < 0 || Arg2 > 2) { + fprintf(stderr, "Invalid size: %d\n", Arg2); + errflg++; + break; + } + + if (Arg3 <= 0 || Arg3 > UFS_ALGORITHMS) { + fprintf(stderr, "Invalid algorithm: %d\n", + Arg3); + errflg++; + break; + } + rscmdlineinfo[ffs(Res) - 1].FSType = Arg1; + rscmdlineinfo[ffs(Res) - 1].Size = Arg2; + rscmdlineinfo[ffs(Res) - 1].Algorithm = Arg3; +#else /* RESIDENCY */ + fprintf(stderr, "-r not supported in non-MRAFS " + "dumptool.\n"); + errflg++; +#endif /* RESIDENCY */ + break; + case 'd': +#ifdef RESIDENCY + dumpvnodes++; +#else /* RESIDENCY */ + fprintf(stderr, "-d not supported in non-MRAFS " + "dumptool.\n"); + errflg++; +#endif /* RESIDENCY */ + break; + case 'v': + verbose++; + break; + case 'f': + force++; + break; + case 'i': + inode++; + break; + case '?': + default: + errflg++; + } + + if (errflg || optind == argc) { + fprintf(stderr, "Usage: %s\n\t[-v] [-f]\n\t" +#ifdef RESIDENCY + "[-t Residency/Tag]\n\t" + "[-r Residency/Type/Size/Algorithm]\n\t" + "[-d] filename [file_in_dump [file in dump ...]]\n", +#else /* RESIDENCY */ + "filename\n", +#endif /* RESIDENCY */ + argv[0]); + exit(1); + } + + /* + * Try opening the dump file + */ + + if ((f = fopen(argv[optind], "rb")) == NULL) { + fprintf(stderr, "open of dumpfile %s failed: %s\n", argv[optind], + strerror(errno)); + exit(1); + } + + if (ReadDumpHeader(f, &dheader)) { + fprintf(stderr, "Failed to read dump header!\n"); + exit(1); + } + + if (verbose) + printf("Dump is for volume %lu (%s)\n", dheader.volumeId, + dheader.volumeName); + + if (getc(f) != D_VOLUMEHEADER) { + fprintf(stderr, "Volume header is missing from dump, aborting\n"); + exit(1); + } + + if (ReadVolumeHeader(f, &vol)) { + fprintf(stderr, "Unable to read volume header\n"); + exit(1); + } + + if (verbose) { + printf("Volume information:\n"); + printf("\tid = %lu\n", vol.id); + printf("\tparent id = %lu\n", vol.parentId); + printf("\tname = %s\n", vol.name); + printf("\tflags ="); + if (vol.inUse) + printf(" inUse"); + if (vol.inService) + printf(" inService"); + if (vol.blessed) + printf(" blessed"); + if (vol.needsSalvaged) + printf(" needsSalvaged"); + printf("\n"); + printf("\tuniquifier = %lu\n", vol.uniquifier); + printf("\tCreation date = %s", ctime((time_t *) &vol.creationDate)); + printf("\tLast access date = %s", ctime((time_t *) &vol.accessDate)); + printf("\tLast update date = %s", ctime((time_t *) &vol.updateDate)); + printf("\tVolume owner = %lu\n", vol.owner); + } + + if (verbose) + printf("Scanning vnodes (this may take a while)\n"); + + /* + * We need to do two vnode scans; one to get the number of + * vnodes, the other to actually build the index. + */ + + offset = ftell(f); + + if (ScanVnodes(f, &vol, 1)) { + fprintf(stderr, "First vnode scan failed, aborting\n"); + exit(1); + } + + fseek(f, offset, SEEK_SET); + + if (ScanVnodes(f, &vol, 0)) { + fprintf(stderr, "Second vnode scan failed, aborting\n"); + exit(1); + } + + if (getc(f) != D_DUMPEND || ReadInt32(f, &magic) || + magic != DUMPENDMAGIC) { + fprintf(stderr, "Couldn't find dump postamble, "); + if (! force) { + fprintf(stderr, "aborting (use -f to override)\n"); + exit(1); + } else { + fprintf(stderr, "continuing anyway\n"); + fprintf(stderr, "WARNING: Dump may not be complete!\n"); + } + } + + /* + * If we wanted to simply dump all vnodes, do it now + */ + +#ifdef RESIDENCY + if (dumpvnodes) { + struct vnodeData *vdata; + + for (i = 0; i < numLargeVnodes; i++) { + + vdata = LargeVnodeIndex[i]; + + if (vdata->vnode->type == vFidLookup) + if (DumpVnodeFile(stdout, vdata->vnode, &vol)) { + fprintf(stderr, "DumpVnodeFile failed, " + "aborting\n"); + exit(1); + } + } + + for (i = 0; i < numSmallVnodes; i++) { + + vdata = SmallVnodeIndex[i]; + + if (vdata->vnode->type == vFidLookup) + if (DumpVnodeFile(stdout, vdata->vnode, &vol)) { + fprintf(stderr, "DumpVnodeFile failed, " + "aborting\n"); + exit(1); + } + } + + } else +#endif /* RESIDENCY */ + if (inode) { + /* + * Dump out all filenames with their corresponding FID + */ + + struct vnodeData *rootvdata; + + if ((rootvdata = GetVnode(1)) == NULL) { + fprintf(stderr, "Can't get vnode data for root " + "vnode! Aborting\n"); + exit(1); + } + + DirListInternal(rootvdata, NULL, 0, 0, 1, 0, 1, 0, &vol, ""); + + } else if (argc > optind + 1) { +#ifdef RESIDENCY + /* + * Dump out residencies of files given on the command line. + */ + + struct vnodeData *vdata, *rootvdata; + + if ((rootvdata = GetVnode(1)) == NULL) { + fprintf(stderr, "Can't get vnode data for root " + "vnode! Aborting\n"); + exit(1); + } + + for (i = optind + 1; i < argc; i++) { + + if ((vdata = FindFile(rootvdata, argv[i])) == NULL) { + fprintf(stderr, "Skipping file %s\n", + argv[i]); + continue; + } + + if (verbose) + printf("Residency locations for %s:\n", + argv[i]); + + while (vdata->vnode->NextVnodeId != 0) { + + vdata = GetVnode(vdata->vnode->NextVnodeId); + + if (vdata == NULL) { + fprintf(stderr, "We had a vnode chain " + "pointer to a vnode that " + "doesn't exist, aborting!\n"); + exit(1); + } + if (vdata->vnode->type == vFidLookup) + DumpVnodeFile(stdout, vdata->vnode, + &vol); + } + } +#else /* RESIDENCY */ + fprintf(stderr, "Extra arguments after dump filename: %s\n", + argv[optind]); + exit(1); +#endif /* RESIDENCY */ + } else { + /* + * Perform an interactive restore + */ + + InteractiveRestore(f, &vol); + } + + exit(0); +} + +/* + * Read the dump header, which is at the beginning of every dump + */ + +static int +ReadDumpHeader(FILE *f, struct DumpHeader *header) +{ + unsigned int magic; + int tag, i; + + if (getc(f) != D_DUMPHEADER || + ReadInt32(f, &magic) || ReadInt32(f, (unsigned int *) + &header->version) || + magic != DUMPBEGINMAGIC) { + if (verbose) + fprintf(stderr, "Couldn't find dump magic numbers\n"); + return -1; + } + + header->volumeId = 0; + header->nDumpTimes = 0; + + while ((tag = getc(f)) > D_MAX && tag != EOF) { + unsigned short length; + switch (tag) { + case 'v': + if (ReadInt32(f, &header->volumeId)) { + if (verbose) + fprintf(stderr, "Failed to read " + "volumeId\n"); + return -1; + } + break; + case 'n': + if (ReadString(f, header->volumeName, + sizeof(header->volumeName))) { + if (verbose) + fprintf(stderr, "Failed to read " + "volume name\n"); + return -1; + } + break; + case 't': + if (ReadInt16(f, &length)) { + if (verbose) + fprintf(stderr, "Failed to read " + "dump time array length\n"); + return -1; + } + header->nDumpTimes = (length >> 1); + for (i = 0; i < header->nDumpTimes; i++) + if (ReadInt32(f, (unsigned int *) + &header->dumpTimes[i].from) || + ReadInt32(f, (unsigned int *) + &header->dumpTimes[i].to)) { + if (verbose) + fprintf(stderr, "Failed to " + "read dump times\n"); + return -1; + } + break; + default: + if (verbose) + fprintf(stderr, "Unknown dump tag \"%c\"\n", + tag); + return -1; + } + } + + if (!header->volumeId || !header->nDumpTimes) { + if (verbose) + fprintf(stderr, "We didn't get a volume Id or " + "dump times listing\n"); + return 1; + } + + ungetc(tag, f); + return 0; +} + +/* + * Read the volume header; this is the information stored in VolumeDiskData. + * + * I'm not sure we need all of this, but read it in just in case. + */ + +static int +ReadVolumeHeader(FILE *f, VolumeDiskData *vol) +{ + int tag; + unsigned int trash; + memset((void *) vol, 0, sizeof(*vol)); + + while ((tag = getc(f)) > D_MAX && tag != EOF) { + switch (tag) { + case 'i': + if (ReadInt32(f, &vol->id)) + return -1; + break; + case 'v': + if (ReadInt32(f, &trash)) + return -1; + break; + case 'n': + if (ReadString(f, vol->name, sizeof(vol->name))) + return -1; + break; + case 's': + vol->inService = getc(f); + break; + case 'b': + vol->blessed = getc(f); + break; + case 'u': + if (ReadInt32(f, &vol->uniquifier)) + return -1; + break; + case 't': + vol->type = getc(f); + break; + case 'p': + if (ReadInt32(f, &vol->parentId)) + return -1; + break; + case 'c': + if (ReadInt32(f, &vol->cloneId)) + return -1; + break; + case 'q': + if (ReadInt32(f, (uint32_t *) &vol->maxquota)) + return -1; + break; + case 'm': + if (ReadInt32(f, (uint32_t *) &vol->minquota)) + return -1; + break; + case 'd': + if (ReadInt32(f, (uint32_t *) &vol->diskused)) + return -1; + break; + case 'f': + if (ReadInt32(f, (uint32_t *) &vol->filecount)) + return -1; + break; + case 'a': + if (ReadInt32(f, &vol->accountNumber)) + return -1; + break; + case 'o': + if (ReadInt32(f, &vol->owner)) + return -1; + break; + case 'C': + if (ReadInt32(f, &vol->creationDate)) + return -1; + break; + case 'A': + if (ReadInt32(f, &vol->accessDate)) + return -1; + break; + case 'U': + if (ReadInt32(f, &vol->updateDate)) + return -1; + break; + case 'E': + if (ReadInt32(f, &vol->expirationDate)) + return -1; + break; + case 'B': + if (ReadInt32(f, &vol->backupDate)) + return -1; + break; + case 'O': + if (ReadString(f, vol->offlineMessage, + sizeof(vol->offlineMessage))) + return -1; + break; + case 'M': + if (ReadString(f, (char *) vol->stat_reads, VMSGSIZE)) + return -1; + break; + case 'W': { + unsigned short length; + int i; + unsigned int data; + if (ReadInt16(f, &length)) + return -1; + for (i = 0; i < length; i++) { + if (ReadInt32(f, &data)) + return -1; + if (i < sizeof(vol->weekUse) / + sizeof(vol->weekUse[0])) + vol->weekUse[i] = data; + } + break; + } + case 'D': + if (ReadInt32(f, &vol->dayUseDate)) + return -1; + break; + case 'Z': + if (ReadInt32(f, (uint32_t *) &vol->dayUse)) + return -1; + break; +#ifdef RESIDENCY + case 'R': { + unsigned short length; + int i; + unsigned int data; + + if (ReadInt16(f, &length)) + return -1; + for (i = 0; i < length; i++) { + if (ReadInt32(f, &data)) + return -1; + if (i < sizeof(vol->DesiredInfo.DesiredResidencyWords) / + sizeof(vol->DesiredInfo.DesiredResidencyWords[0])) + vol->DesiredInfo.DesiredResidencyWords[i] = data; + } + break; + } + case 'S': { + unsigned short length; + int i; + unsigned int data; + + if (ReadInt16(f, &length)) + return -1; + for (i = 0; i < length; i++) { + if (ReadInt32(f, &data)) + return -1; + if (i < sizeof(vol->UnDesiredInfo.UnDesiredResidencyWords) / + sizeof(vol->UnDesiredInfo.UnDesiredResidencyWords[0])) + vol->UnDesiredInfo.UnDesiredResidencyWords[i] = data; + } + break; + } +#endif + default: + if (verbose) + fprintf(stderr, "Unknown dump tag \"%c\"\n", + tag); + return -1; + } + } + + ungetc(tag, f); + return 0; +} + +/* + * Scan all our vnode entries, and build indexing information. + */ + +static int +ScanVnodes(FILE *f, VolumeDiskData *vol, int sizescan) +{ + int vnodeNumber; + int tag; + int numFileVnodes = 0; + int numDirVnodes = 0; + unsigned char buf[SIZEOF_LARGEDISKVNODE]; + struct VnodeDiskObject *vnode = (struct VnodeDiskObject *) buf; + long offset, oldoffset; + struct vnodeData *vdata; + unsigned int length; + + tag = getc(f); + + while (tag == D_VNODE) { + + offset = 0; + length = 0; + vnode->type = -1; + vnode->length = -1; + + if (ReadInt32(f, (uint32_t *) &vnodeNumber)) + { + fprintf(stderr, "failed int32 for 'vnodenum'\n"); + return -1; + } + + if (ReadInt32(f, &vnode->uniquifier)) + { + fprintf(stderr, "failed int32 for 'uniquifier'\n"); + return -1; + } + + if (verbose > 1 && !sizescan) + printf("Got vnode %d\n", vnodeNumber); + + while ((tag = getc(f)) > D_MAX && tag != EOF) + switch (tag) { + case 't': + vnode->type = (VnodeType) getc(f); + break; + case 'l': + { + unsigned short tmp; + if (ReadInt16(f, &tmp)) + { + fprintf(stderr, "failed int16 for 'l'\n"); + return -1; + } + vnode->linkCount = tmp; + } + break; + case 'v': + if (ReadInt32(f, &vnode->dataVersion)) + { + fprintf(stderr, "failed int32 for 'v'\n"); + return -1; + } + break; + case 'm': + if (ReadInt32(f, (uint32_t *) &vnode->unixModifyTime)) + { + fprintf(stderr, "failed int32 for 'm'\n"); + return -1; + } + break; + case 's': + if (ReadInt32(f, (uint32_t *) &vnode->serverModifyTime)) + { + fprintf(stderr, "failed int32 for 's'\n"); + return -1; + } + break; + case 'a': + if (ReadInt32(f, &vnode->author)) + { + fprintf(stderr, "failed int32 for 'a'\n"); + return -1; + } + break; + case 'o': + if (ReadInt32(f, &vnode->owner)) + { + fprintf(stderr, "failed int32 for 'o'\n"); + return -1; + } + break; + case 'g': + if (ReadInt32(f, (uint32_t *) &vnode->group)) + { + fprintf(stderr, "failed int32 for 'g'\n"); + return -1; + } + break; + case 'b': { + unsigned short modeBits; + if (ReadInt16(f, &modeBits)) + return -1; + vnode->modeBits = modeBits; + break; + } + case 'p': + if (ReadInt32(f, &vnode->parent)) + { + fprintf(stderr, "failed int32 for 'p'\n"); + return -1; + } + break; +#ifdef RESIDENCY + case 'N': + if (ReadInt32(f, &vnode->NextVnodeId)) + { + fprintf(stderr, "failed int32 for 'N'\n"); + return -1; + } + break; + case 'R': + if (ReadInt32(f, &VLkp_Residencies(vnode))) + { + fprintf(stderr, "failed int32 for 'R'\n"); + return -1; + } + break; +#endif + case 'S': + if (ReadInt32(f, &vnode->length)) + { + fprintf(stderr, "failed int32 for 'S'\n"); + return -1; + } + break; + case 'F': + if (ReadInt32(f, (uint32_t *) &vnode->vn_ino_lo)) + return -1; + break; + case 'A': + if (ReadByteString(f, + (void *)VVnodeDiskACL(vnode), + VAclDiskSize(vnode))) + { + fprintf(stderr, "failed readbystring for 'A'\n"); + return -1; + } +#if 0 + acl_NtohACL(VVnodeDiskACL(vnode)); +#endif + break; +#ifdef RESIDENCY + case 'h': + if (ReadInt32(f, &vnode->length_hi)) + { + fprintf(stderr, "failed int32 for 'h'\n"); + return -1; + } +#endif + case 'f': + if (verbose > 1 && ! sizescan) + printf("We have file data!\n"); + if (ReadInt32(f, &length)) + { + fprintf(stderr, "failed int32 for 'f'\n"); + return -1; + } + vnode->length = length; + offset = ftell(f); + fseek(f, length, SEEK_CUR); + break; + default: + if (verbose) + fprintf(stderr, "Unknown dump tag \"%c\"\n", + tag); + return -1; + } + + /* + * If we're doing an incremental restore, then vnodes + * will be listed in the dump, but won't contain any + * vnode information at all (I don't know why they're + * included _at all_). If we get one of these vnodes, then + * just skip it (because we can't do anything with it. + */ + + if (vnode->type == -1) + continue; + +#ifdef RESIDENCY + if (verbose > 1 && vnode->type == vFidLookup && ! sizescan) { + printf("This is an auxiliary vnode (lookup) for vnode %d, residency %d\n", + VLkp_ParentVnodeId(vnode), + VLkp_Residencies(vnode)); + if (DumpVnodeFile(stdout, vnode, vol)) + return -1; + } + + if (verbose > 1 && vnode->type == vAccessHistory && ! sizescan) + printf("This is an auxiliary vnode (history) for vnode %d\n", + VLkp_ParentVnodeId(vnode)); +#endif + + if (vnode->type == vDirectory) + numDirVnodes++; + else + numFileVnodes++; + + /* + * We know now all we would ever know about the vnode; + * insert it into our hash table (but only if we're not + * doing a vnode scan). + */ + + if (!sizescan) { + + vdata = InsertVnode(vnodeNumber, vnode); + + if (vdata == NULL) { + if (verbose) + fprintf(stderr, "Failed to insert " + "vnode into hash table"); + return -1; + } + + vdata->dumpdata = offset; + vdata->datalength = length; + + /* + * Save directory data, since we'll need it later. + */ + + if (vnode->type == vDirectory && length) { + + vdata->filedata = malloc(length); + + if (!vdata->filedata) { + if (verbose) + fprintf(stderr, "Unable to " + "allocate space for " + "file data (%d)\n", + length); + return -1; + } + + oldoffset = ftell(f); + fseek(f, offset, SEEK_SET); + + if (fread(vdata->filedata, length, 1, f) != 1) { + if (verbose) + fprintf(stderr, "Unable to " + "read in file data!\n"); + return -1; + } + + fseek(f, oldoffset, SEEK_SET); + } else if (vnode->type == vDirectory) + /* + * Warn the user we may not have all directory + * vnodes + */ + numNoDirData++; + } + } + + ungetc(tag, f); + + if (!sizescan) { + + numLargeVnodes = numDirVnodes; + numSmallVnodes = numFileVnodes; + + } else { + LargeVnodeIndex = (struct vnodeData **) + malloc(numDirVnodes * + sizeof(struct vnodeData)); + SmallVnodeIndex = (struct vnodeData **) + malloc(numFileVnodes * + sizeof(struct vnodeData)); + + if (LargeVnodeIndex == NULL || SmallVnodeIndex == NULL) { + if (verbose) + fprintf(stderr, "Unable to allocate space " + "for vnode tables\n"); + return -1; + } + } + + if (verbose) + fprintf(stderr,"%s vnode scan completed\n", + sizescan ? "Primary" : "Secondary"); + + return 0; +} + +/* + * Perform an interactive restore + * + * Parsing the directory information is a pain, but other than that + * we just use the other tools we already have in here. + */ + +static void +InteractiveRestore(FILE *f, VolumeDiskData *vol) +{ + struct vnodeData *vdatacwd; /* Vnode data for our current dir */ + char cmdbuf[256]; + int argc; + char **argv; + + /* + * Let's see if we can at least get the data for our root directory. + * If we can't, there's no way we can do an interactive restore. + */ + + if ((vdatacwd = GetVnode(1)) == NULL) { + fprintf(stderr, "No entry for our root vnode! Aborting\n"); + return; + } + + if (! vdatacwd->filedata) { + fprintf(stderr, "There is no directory data for the root " + "vnode (1.1). An interactive\nrestore is not " + "possible.\n"); + return; + } + + /* + * If you're doing a selective dump correctly, then you should get all + * directory vnode data. But just in case you didn't, let the user + * know there may be a problem. + */ + + if (numNoDirData) + fprintf(stderr, "WARNING: %d directory vnodes had no file " + "data. An interactive restore\nmay not be possible\n", + numNoDirData); + + printf("> "); + while (fgets(cmdbuf, 256, stdin)) { + + cmdbuf[strlen(cmdbuf) - 1] = '\0'; + + if (strlen(cmdbuf) == 0) { + printf("> "); + continue; + } + + MakeArgv(cmdbuf, &argc, &argv); + + if (strcmp(argv[0], "ls") == 0) { + DirectoryList(argc, argv, vdatacwd, vol); + } else if (strcmp(argv[0], "cd") == 0) { + struct vnodeData *newvdata; + + newvdata = ChangeDirectory(argc, argv, vdatacwd); + + if (newvdata) + vdatacwd = newvdata; + } else if (strcmp(argv[0], "file") == 0) { + DumpAllFiles(argc, argv, vdatacwd, vol); + } else if (strcmp(argv[0], "cp") == 0) { + CopyFile(argc, argv, vdatacwd, f); + } else if (strcmp(argv[0], "vcp") == 0) { + CopyVnode(argc, argv, f); + } else if (strcmp(argv[0], "quit") == 0 || + strcmp(argv[0], "exit") == 0) + break; + else if (strcmp(argv[0], "?") == 0 || + strcmp(argv[0], "help") == 0) { + printf("Valid commands are:\n"); + printf("\tls\t\tList current directory\n"); + printf("\tcd\t\tChange current directory\n"); + printf("\tcp\t\tCopy file from dump\n"); + printf("\tvcp\t\tCopy file from dump (via vnode)\n"); +#ifdef RESIDENCY + printf("\tfile\t\tList residency filenames\n"); +#endif /* RESIDENCY */ + printf("\tquit | exit\tExit program\n"); + printf("\thelp | ?\tBrief help\n"); + } else + fprintf(stderr, "Unknown command, \"%s\", enter " + "\"help\" for a list of commands.\n", + argv[0]); + + printf("> "); + } + + return; +} + +/* + * Do a listing of all files in a directory. Sigh, I wish this wasn't + * so complicated. + * + * With the reorganizing, this is just a front-end to DirListInternal() + */ + +static void +DirectoryList(int argc, char **argv, struct vnodeData *vdata, + VolumeDiskData *vol) +{ + int errflg = 0, lflag = 0, iflag = 0, Fflag = 0, sflag = 0, Rflag = 0; + int c; + + optind = 1; + + while ((c = getopt(argc, argv, "liFRs")) != EOF) + switch (c) { + case 'l': + lflag++; + break; + case 'i': + iflag++; + break; + case 'F': + Fflag++; + break; + case 'R': + Rflag++; + case 's': + sflag++; + break; + case '?': + default: + errflg++; + } + + if (errflg) { + fprintf(stderr, "Usage: %s [-liFs] filename [filename ...]\n", + argv[0]); + return; + } + + DirListInternal(vdata, &(argv[optind]), argc - optind, lflag, iflag, + Fflag, Rflag, 1, vol, NULL); + + return; +} + +/* + * Function that does the REAL work in terms of directory listing + */ + +static void +DirListInternal(struct vnodeData *vdata, char *pathnames[], int numpathnames, + int lflag, int iflag, int Fflag, int Rflag, int verbose, + VolumeDiskData *vol, char *path) +{ + struct DirEntry *ep, **eplist = NULL, **eprecurse = NULL; + struct DirCursor cursor; + struct vnodeData *lvdata; + + int i, j, numentries = 0, longestname = 0, numcols, col, numrows; + int numrecurse = 0; + + if (! vdata->filedata) { + fprintf(stderr, "There is no vnode data for this " + "directory!\n"); + return; + } + + ResetDirCursor(&cursor, vdata); + + /* + * Scan through the whole directory + */ + + while ((ep = ReadNextDir(&cursor, vdata)) != NULL) { + + /* + * If we didn't get any filenames on the command line, + * get them all. + */ + + if (numpathnames == 0) { + eplist = realloc(eplist, sizeof(struct DirEntry *) * + ++numentries); + eplist[numentries - 1] = ep; + if (strlen(ep->name) > longestname) + longestname = strlen(ep->name); + if (Rflag) + if ((lvdata = GetVnode(ntohl(ep->fid.vnode))) && + lvdata->vnode->type == vDirectory && + !(strcmp(ep->name, ".") == 0 || + strcmp(ep->name, "..") == 0)) { + eprecurse = realloc(eprecurse, + sizeof(struct DirEntry *) * + ++numrecurse); + eprecurse[numrecurse - 1] = ep; + } + + } else { + /* + * Do glob matching via fnmatch() + */ + + for (i = 0; i < numpathnames; i++) + if (fnmatch(pathnames[i], ep->name, + FNM_PATHNAME) == 0) { + eplist = realloc(eplist, + sizeof(struct DirEntry *) * + ++numentries); + eplist[numentries - 1] = ep; + if (strlen(ep->name) > longestname) + longestname = strlen(ep->name); + if (Rflag) + if ((lvdata = + GetVnode(ntohl(ep->fid.vnode))) && + lvdata->vnode->type == + vDirectory && + !(strcmp(ep->name, ".") == 0 || + strcmp(ep->name, "..") == 0)) { + eprecurse = + realloc(eprecurse, + sizeof(struct DirEntry *) * + ++numrecurse); + eprecurse[numrecurse - 1] = ep; + } + break; + } + } + } + + qsort((void *) eplist, numentries, sizeof(struct DirEntry *), + CompareDirEntry); + + if (Rflag && eprecurse) + qsort((void *) eprecurse, numrecurse, + sizeof(struct DirEntry *), CompareDirEntry); + /* + * We don't have to do column printing if we have the -l or the -i + * options. Sigh, column printing is WAY TOO FUCKING COMPLICATED! + */ + + if (!lflag && !iflag) { + char c; + + if (Fflag) + longestname++; + + longestname++; + + numcols = termsize / longestname ? termsize / longestname : 1; + numrows = numentries / numcols + + (numentries % numcols ? 1 : 0); + + for (i = 0; i < numrows; i++) { + col = 0; + while (col < numcols && (i + col * numrows) < + numentries) { + ep = eplist[i + col++ * numrows]; + if (Fflag) { + if (!(lvdata = + GetVnode(ntohl(ep->fid.vnode)))) + c = ' '; + else if (lvdata->vnode->type == + vDirectory) + c = '/'; + else if (lvdata->vnode->type == + vSymlink) + c = '@'; + else if (lvdata->vnode->modeBits & + 0111 != 0) + c = '*'; + else + c = ' '; + printf("%s%-*c", ep->name, + longestname - strlen(ep->name), c); + } else + printf("%-*s", longestname, ep->name); + } + + printf("\n"); + } + } else if (iflag) + for (i = 0; i < numentries; i++) + if (!(lvdata = GetVnode(ntohl(eplist[i]->fid.vnode)))) + printf("%d.0.0\t%s\n", + vol->parentId ? vol->parentId : vol->id, + eplist[i]->name); + else + if (path) + printf("%d.%d.%d\t%s/%s\n", + vol->id, + ntohl(eplist[i]->fid.vnode), + ntohl(eplist[i]->fid.vunique), + path, eplist[i]->name); + else + printf("%d.%d.%d\t%s\n", + vol->id, + ntohl(eplist[i]->fid.vnode), + ntohl(eplist[i]->fid.vunique), + eplist[i]->name); + else if (lflag) { + for (i = 0; i < numentries; i++) + if (!(lvdata = GetVnode(ntohl(eplist[i]->fid.vnode)))) + printf("---------- 0 0 " + "0 0 %s\n", + eplist[i]->name); + else { + switch (lvdata->vnode->type) { + case vDirectory: + printf("d"); + break; + case vSymlink: + printf("l"); + break; + default: + printf("-"); + } + + for (j = 8; j >= 0; j--) { + if (lvdata->vnode->modeBits & (1 << j)) + switch (j % 3) { + case 2: printf("r"); + break; + case 1: printf("w"); + break; + case 0: printf("x"); + } + else + printf("-"); + } + + printf(" %-3d %-8d %-8d %10d %s\n", + lvdata->vnode->linkCount, + lvdata->vnode->owner, + lvdata->vnode->group, + lvdata->vnode->length, + eplist[i]->name); + } + } + + free(eplist); + + if (Rflag && eprecurse) { + char *lpath; + lpath = NULL; + for (i = 0; i < numrecurse; i++) { + if (verbose) + printf("\n%s:\n", eprecurse[i]->name); + if (path) { + lpath = malloc(strlen(path) + + strlen(eprecurse[i]->name) + 2); + if (lpath) + sprintf(lpath, "%s/%s", path, + eprecurse[i]->name); + } + DirListInternal( + GetVnode(ntohl(eprecurse[i]->fid.vnode)), + NULL, 0, lflag, iflag, Fflag, Rflag, + verbose, vol, lpath); + if (lpath) { + free(lpath); + lpath = NULL; + } + } + } + + if (eprecurse) + free(eprecurse); + + return; +} + + +/* + * Directory name comparison function, used by qsort + */ + +static int +CompareDirEntry(const void *e1, const void *e2) +{ + struct DirEntry **ep1 = (struct DirEntry **) e1; + struct DirEntry **ep2 = (struct DirEntry **) e2; + + return strcmp((*ep1)->name, (*ep2)->name); +} + +/* + * Change a directory. Return a pointer to our new vdata structure for + * this directory. + */ + +static struct vnodeData * +ChangeDirectory(int argc, char **argv, struct vnodeData *vdatacwd) +{ + struct vnodeData *newvdatacwd; + + if (argc != 2) { + fprintf(stderr, "Usage: %s directory\n", argv[0]); + return NULL; + } + + if ((newvdatacwd = FindFile(vdatacwd, argv[1])) == NULL) + return NULL; + + if (newvdatacwd->vnode->type != vDirectory) { + fprintf(stderr, "%s: Not a directory\n", argv[1]); + return NULL; + } + + if (newvdatacwd->filedata == NULL) { + fprintf(stderr, "%s: No directory data found.\n", argv[1]); + return NULL; + } + + return newvdatacwd; +} + +/* + * Copy a file from out of the dump file + */ + +#define COPYBUFSIZE 8192 + +static void +CopyFile(int argc, char **argv, struct vnodeData *vdatacwd, FILE *f) +{ + struct vnodeData *vdata; + FILE *out; + long cur = 0; + int bytes, ret; + char buffer[COPYBUFSIZE]; + + if (argc != 3) { + fprintf(stderr, "Usage: %s dumpfile destfile\n", argv[0]); + return; + } + + if ((vdata = FindFile(vdatacwd, argv[1])) == NULL) + return; + + if (vdata->dumpdata == 0) { + fprintf(stderr, "File %s has no data in dump file\n", + argv[1]); + return; + } + + if ((out = fopen(argv[2], "wb")) == NULL) { + fprintf(stderr, "Open of %s failed: %s\n", argv[2], + strerror(errno)); + return; + } + + if (fseek(f, vdata->dumpdata, SEEK_SET)) { + fprintf(stderr, "Seek failed: %s\n", strerror(errno)); + fclose(out); + return; + } + + while (cur < vdata->datalength) { + + bytes = cur + COPYBUFSIZE < vdata->datalength ? + COPYBUFSIZE : vdata->datalength - cur; + + ret = fread(buffer, sizeof(char), bytes, f); + if (ret != bytes) { + if (ret != 0) + fprintf(stderr, "Short read (expected %d, " + "got %d)\n", bytes, ret); + else + fprintf(stderr, "Error during read: %s\n", + strerror(errno)); + fclose(out); + return; + } + + ret = fwrite(buffer, sizeof(char), bytes, out); + if (ret != bytes) { + if (ret != 0) + fprintf(stderr, "Short write (expected %d, " + "got %d)\n", bytes, ret); + else + fprintf(stderr, "Error during write: %s\n", + strerror(errno)); + fclose(out); + return; + } + + cur += bytes; + } + + fclose(out); +} + +/* + * Copy a file from out of the dump file, by using the vnode + */ + +static void +CopyVnode(int argc, char *argv[], FILE *f) +{ + struct vnodeData *vdata; + FILE *out; + long cur = 0; + int bytes, ret; + char buffer[COPYBUFSIZE]; + unsigned int vnode, uniquifier = 0; + + if (argc != 3) { + fprintf(stderr, "Usage: %s vnode[.uniqifier] destfile\n", + argv[0]); + return; + } + + ret = sscanf(argv[1], "%d.%d", &vnode, &uniquifier); + + if (ret < 1) { + fprintf(stderr, "Invalid file identifier: %s\n", argv[1]); + return; + } + + if (!(vdata = GetVnode(vnode))) { + fprintf(stderr, "Vnode %d not in dump file\n", vnode); + return; + } + + if (ret == 2 && vdata->vnode->uniquifier != uniquifier) { + fprintf(stderr, "Specified uniquifier %d did not match " + "uniquifier %d found in dump file!\n", uniquifier, + vdata->vnode->uniquifier); + return; + } + + if (vdata->dumpdata == 0) { + fprintf(stderr, "File %s has no data in dump file\n", + argv[1]); + return; + } + + if ((out = fopen(argv[2], "wb")) == NULL) { + fprintf(stderr, "Open of %s failed: %s\n", argv[2], + strerror(errno)); + return; + } + + if (fseek(f, vdata->dumpdata, SEEK_SET)) { + fprintf(stderr, "Seek failed: %s\n", strerror(errno)); + fclose(out); + return; + } + + while (cur < vdata->datalength) { + + bytes = cur + COPYBUFSIZE < vdata->datalength ? + COPYBUFSIZE : vdata->datalength - cur; + + ret = fread(buffer, sizeof(char), bytes, f); + if (ret != bytes) { + if (ret != 0) + fprintf(stderr, "Short read (expected %d, " + "got %d)\n", bytes, ret); + else + fprintf(stderr, "Error during read: %s\n", + strerror(errno)); + fclose(out); + return; + } + + ret = fwrite(buffer, sizeof(char), bytes, out); + if (ret != bytes) { + if (ret != 0) + fprintf(stderr, "Short write (expected %d, " + "got %d)\n", bytes, ret); + else + fprintf(stderr, "Error during write: %s\n", + strerror(errno)); + fclose(out); + return; + } + + cur += bytes; + } + + fclose(out); +} +/* + * Dump all residency filenames associated with a file, or all files + * within a directory. + */ + +static void +DumpAllFiles(int argc, char **argv, struct vnodeData *vdatacwd, + VolumeDiskData *vol) +{ +#ifdef RESIDENCY + struct vnodeData *vdata, *nvdata; + struct DirCursor cursor; + struct DirEntry *ep; + FILE *f = stdout; + int c, i; + int dflag = 0, fflag = 0, errflg = 0; + + optind = 1; + + while ((c = getopt(argc, argv, "df:")) != EOF) + switch (c) { + case 'd': + dflag++; + break; + case 'f': + if ((f = fopen(optarg, "a")) == NULL) { + fprintf(stderr, "Cannot open \"%s\": %s\n", + optarg, strerror(errno)); + return; + } + fflag++; + break; + case 'h': + case '?': + default: + errflg++; + } + + if (errflg || argc == optind) { + fprintf(stderr, "Usage: %s [-d] [-f filename] file " + "[file ...]\n", argv[0]); + if (fflag) + fclose(f); + return; + } + + for (i = optind; i < argc; i++) { + + if ((vdata = FindFile(vdatacwd, argv[i])) == NULL) + continue; + + if (vdata->vnode->type == vDirectory && ! dflag) { + + ResetDirCursor(&cursor, vdata); + + while ((ep = ReadNextDir(&cursor, vdata)) != NULL) { + + if (!(nvdata = + GetVnode(ntohl(ep->fid.vnode)))) { + fprintf(stderr, "Cannot find vnode " + "entry for %s (%d)\n", + ep->name, ntohl(ep->fid.vnode)); + continue; + } + + + if (!fflag) { + printf("Residency locations for %s:\n", + ep->name); + + if (nvdata->dumpdata) + printf("Local disk (in dump " + "file)\n"); + } + + DumpAllResidencies(f, nvdata, vol); + + } + + } else { + if (!fflag) { + printf("Residency locations for %s:\n", + argv[i]); + + if (vdata->dumpdata) + printf("Local disk (in dump file)\n"); + } + + DumpAllResidencies(f, vdata, vol); + } + } + + if (fflag) + fclose(f); +#else /* RESIDENCY */ + fprintf(stderr, "The \"file\" command is not available in the non-" + "MRAFS version of dumptool.\n"); +#endif /* RESIDENCY */ + return; +} + +/* + * Take a vnode, traverse the vnode chain, and dump out all files on + * all residencies corresponding to that parent vnode. + */ + +#ifdef RESIDENCY +static void +DumpAllResidencies(FILE *f, struct vnodeData *vdata, struct VolumeDiskData *vol) +{ + unsigned int nextVnodeNum; + + while (nextVnodeNum = vdata->vnode->NextVnodeId) { + if ((vdata = GetVnode(nextVnodeNum)) == NULL) { + fprintf(stderr, "We had a pointer to %lu in it's " + "vnode chain, but there\nisn't a record of " + "it! The dump might be corrupt.\n", + nextVnodeNum); + return; + } + + if (vdata->vnode->type == vFidLookup) + DumpVnodeFile(f, vdata->vnode, vol); + } + + return; +} +#endif + + +/* + * Given a directory vnode and a filename, return the vnode corresponding + * to the file in that directory. + * + * We now handle pathnames with directories in them. + */ + +static struct vnodeData * +FindFile(struct vnodeData *vdatacwd, char *filename) +{ + struct DirHeader *dhp; + struct DirEntry *ep; + int i, num; + struct vnodeData *vdata; + char *c, newstr[MAXPATHLEN]; + + if (! vdatacwd->filedata) { + fprintf(stderr, "There is no vnode data for this " + "directory!\n"); + return NULL; + } + + /* + * If we have a "/" in here, look up the vnode data for the + * directory (everything before the "/") and use that as our + * current directory. We automagically handle multiple directories + * by using FindFile recursively. + */ + + if ((c = strrchr(filename, '/')) != NULL) { + + strncpy(newstr, filename, c - filename); + newstr[c - filename] = '\0'; + + if ((vdatacwd = FindFile(vdatacwd, newstr)) == NULL) + return NULL; + + if (vdatacwd->vnode->type != vDirectory) { + fprintf(stderr, "%s: Not a directory\n", newstr); + return NULL; + } + + filename = c + 1; + } + + dhp = (struct DirHeader *) vdatacwd->filedata; + + i = DirHash(filename); + + num = ntohs(dhp->hashTable[i]); + + while (num) { + ep = (struct DirEntry *) (vdatacwd->filedata + (num * 32)); + if (strcmp(ep->name, filename) == 0) + break; + num = ntohs(ep->next); + } + + if (! num) { + fprintf(stderr, "%s: No such file or directory\n", filename); + return NULL; + } + + if ((vdata = GetVnode(ntohl(ep->fid.vnode))) == NULL) { + fprintf(stderr, "%s: No vnode information for %lu found\n", + filename, ntohl(ep->fid.vnode)); + return NULL; + } + + return vdata; +} + +/* + * Reset a structure containing the current directory scan location + */ + +static void +ResetDirCursor(struct DirCursor *cursor, struct vnodeData *vdata) +{ + struct DirHeader *dhp; + + cursor->hashbucket = 0; + + dhp = (struct DirHeader *) vdata->filedata; + + cursor->entry = ntohs(dhp->hashTable[0]); +} + +/* + * Given a cursor and a directory entry, return the next entry in the + * directory. + */ + +static struct DirEntry * +ReadNextDir(struct DirCursor *cursor, struct vnodeData *vdata) +{ + struct DirHeader *dhp; + struct DirEntry *ep; + + dhp = (struct DirHeader *) vdata->filedata; + + if (cursor->entry) { + ep = (struct DirEntry *) (vdata->filedata + + (cursor->entry * 32)); + cursor->entry = ntohs(ep->next); + return ep; + } else { + while (++(cursor->hashbucket) < NHASHENT) { + cursor->entry = + ntohs(dhp->hashTable[cursor->hashbucket]); + if (cursor->entry) { + ep = (struct DirEntry *) (vdata->filedata + + (cursor->entry * 32)); + cursor->entry = ntohs(ep->next); + return ep; + } + } + } + + return NULL; +} + +/* + * Given a string, split it up into components a la Unix argc/argv. + * + * This code is most stolen from ftp. + */ + +static void +MakeArgv(char *string, int *argc, char ***argv) +{ + static char *largv[64]; + char **la = largv; + char *s = string; + static char argbuf[256]; + char *ap = argbuf; + + *argc = 0; + *argv = largv; + + while (*la++ = GetToken(s, &s, ap, &ap)) + (*argc)++; +} + +/* + * Return a pointer to the next token, and update the current string + * position. + */ + +static char * +GetToken(char *string, char **nexttoken, char argbuf[], char *nextargbuf[]) +{ + char *sp = string; + char *ap = argbuf; + int got_one = 0; + +S0: + switch (*sp) { + + case '\0': + goto OUTTOKEN; + + case ' ': + case '\t': + sp++; goto S0; + + default: + goto S1; + } + +S1: + switch (*sp) { + + case ' ': + case '\t': + case '\0': + goto OUTTOKEN; /* End of our token */ + + case '\\': + sp++; goto S2; /* Get next character */ + + case '"': + sp++; goto S3; /* Get quoted string */ + + default: + *ap++ = *sp++; /* Add a character to our token */ + got_one = 1; + goto S1; + } + +S2: + switch (*sp) { + + case '\0': + goto OUTTOKEN; + + default: + *ap++ = *sp++; + got_one = 1; + goto S1; + } + +S3: + switch (*sp) { + + case '\0': + goto OUTTOKEN; + + case '"': + sp++; goto S1; + + default: + *ap++ = *sp++; + got_one = 1; + goto S3; + } + +OUTTOKEN: + if (got_one) + *ap++ = '\0'; + *nextargbuf = ap; /* Update storage pointer */ + *nexttoken = sp; /* Update token pointer */ + + return got_one ? argbuf : NULL; +} + +/* + * Insert vnodes into our hash table. + */ + +static struct vnodeData * +InsertVnode(unsigned int vnodeNumber, struct VnodeDiskObject *vnode) +{ + struct VnodeDiskObject *nvnode; + struct vnodeData *vdata; + static int curSmallVnodeIndex = 0; + static int curLargeVnodeIndex = 0; + struct vnodeData ***vnodeIndex; + int *curIndex; + + nvnode = (struct VnodeDiskObject *) malloc(sizeof(struct VnodeDiskObject)); + + if (!nvnode) { + if (verbose) + fprintf(stderr, "Unable to allocate space for vnode\n"); + return NULL; + } + + memcpy((void *) nvnode, (void *) vnode, sizeof(struct VnodeDiskObject)); + + if (vnodeNumber & 1) { + vnodeIndex = &LargeVnodeIndex; + curIndex = &curLargeVnodeIndex; + } else { + vnodeIndex = &SmallVnodeIndex; + curIndex = &curSmallVnodeIndex; + } + + vdata = (struct vnodeData *) malloc(sizeof(struct vnodeData)); + + vdata->vnode = nvnode; + vdata->vnodeNumber = vnodeNumber; + vdata->dumpdata = 0; + vdata->filedata = 0; + vdata->datalength = 0; + + (*vnodeIndex)[(*curIndex)++] = vdata; + + return vdata; +} + +/* + * Routine to retrieve a vnode from the hash table. + */ + +static struct vnodeData * +GetVnode(unsigned int vnodeNumber) +{ + struct vnodeData vnode, *vnodep, **tmp; + + vnode.vnodeNumber = vnodeNumber; + vnodep = &vnode; + + tmp = (struct vnodeData **) + bsearch((void *) &vnodep, + vnodeNumber & 1 ? LargeVnodeIndex : SmallVnodeIndex, + vnodeNumber & 1 ? numLargeVnodes : numSmallVnodes, + sizeof(struct vnodeData *), CompareVnode); + + return tmp ? *tmp : NULL; +} + +/* + * Our comparator function for bsearch + */ + +static int +CompareVnode(const void *node1, const void *node2) +{ + struct vnodeData **vnode1 = (struct vnodeData **) node1; + struct vnodeData **vnode2 = (struct vnodeData **) node2; + + if ((*vnode1)->vnodeNumber == (*vnode2)->vnodeNumber) + return 0; + else if ((*vnode1)->vnodeNumber > (*vnode2)->vnodeNumber) + return 1; + else + return -1; +} + +#ifdef RESIDENCY +/* + * Dump out the filename corresponding to a particular vnode. + * + * This routine has the following dependancies: + * + * - Only will work on UFS filesystems at this point + * - Has to talk to the rsserver. + * - Can only determine UFS algorithm type when run on the same machine + * as the residency (unless you manually specify algorithm information) + */ + +static int +DumpVnodeFile(FILE *f, struct VnodeDiskObject *vnode, VolumeDiskData *vol) +{ + static int rscache = 0; + static rsaccessinfoList rsnlist = {0, 0}; + char MountPoint[MAXPATHLEN + 1]; + char FileName[MAXPATHLEN + 1]; + unsigned int Size, Level[4]; + unsigned int DeviceTag, Algorithm; + FileSystems *FSInfo; + int i, found, FSType, rsindex; + + /* + * Maybe we found out something about this residency via the + * command-line; check that first. + */ + + rsindex = ffs(VLkp_Residencies(vnode)) - 1; + + /* + * We need to get information from the rsserver (so we can + * find out the device tag for a given residency). If we + * haven't cached that, talk to the rsserver to get it. + * If we have info about this already, then don't talk to + * the rsserver (this lets us still do disaster recovery if + * MR-AFS is completely hosed). + */ + + if (! rscache && rscmdlineinfo[rsindex].DeviceTag == -1) { + int code; + + code = ServerInitResidencyConnection(); + + if (code) { + fprintf(stderr, "ServerInitResidencyConnection failed " + "with code %d\n", code); + return -1; + } + + code = rs_GetResidencySummary(ServerRequestorId, &rsnlist); + + if (code) { + fprintf(stderr, "rs_GetResidencySummary failed " + "with code %d\n", code); + return -1; + } + + rscache = 1; + } + + /* + * For a given residency (as specified in the vnode), + * find out it's device tag number, either via the rsserver + * or via the command line. + */ + + if (rscmdlineinfo[rsindex].DeviceTag != -1) { + DeviceTag = rscmdlineinfo[rsindex].DeviceTag; + found = 1; + } else + for (i = 0, found = 0; (i < rsnlist.rsaccessinfoList_len) && + (!found); i++) { + if (rsnlist.rsaccessinfoList_val[i].id.residency == + VLkp_Residencies(vnode)) { + found = 1; + DeviceTag = + rsnlist.rsaccessinfoList_val[i].devicetagnumber; + break; + } + } + + if (! found) { + if (verbose) + fprintf(stderr, "Unable to find residency %d in " + "rsserver database, aborting\n", + VLkp_Residencies(vnode)); + return -1; + } + + /* + * Okay, now we've got the DeviceTag ... which we can use to + * lookup the on-disk configuration information (which we + * assume is locally stored). We also need the DeviceTag to + * print out which partition we're using (but that comes later). + * + * We lookup the on-disk configuration information by calling + * Ufs_GetFSInfo() to get the configuration information on the + * filesystems specified by the given DeviceTag. + * + * Before we call Ufs_GetFSInfo, check the command-line cache; + * if we got something via the command-line, don't go to disk. + */ + + if (rscmdlineinfo[rsindex].FSType == -1 && + Ufs_GetFSInfo(&FSInfo, DeviceTag)) { + if (verbose) + fprintf(stderr, "Ufs_GetFSInfo failed for DeviceTag " + "%d, Residency %d\n", DeviceTag, + VLkp_Residencies(vnode)); + return -1; + } + + /* + * The FSInfo structure has the last two things we need: the + * FSType (ufs, slowufs, etc etc), and the usage algorithm (which + * ends up being how many directories are being used on the + * residency filesystem). + * + * With these last two parameters, use routines stolen from + * ufsname to generate the filename. + * + * (Actually, I lied - we also need the "Size" parameter, which + * we can also get from FSInfo); + */ + + if (rscmdlineinfo[rsindex].FSType != -1) { + FSType = rscmdlineinfo[rsindex].FSType; + Algorithm = rscmdlineinfo[rsindex].Algorithm; + Size = rscmdlineinfo[rsindex].Size; + } else { + FSType = FSInfo->FileSystems_u.UfsInterface.FSType; + Algorithm = FSInfo->FileSystems_u.UfsInterface.Algorithm; + if (FSInfo->FileSystems_u.UfsInterface.Directories[1] == 0) + Size = 0; + else if (FSInfo->FileSystems_u.UfsInterface.Directories[1] == 16) + Size = 1; + else if (FSInfo->FileSystems_u.UfsInterface.Directories[1] == 256) + Size = 2; + else { + if (verbose) + fprintf(stderr, "Unknown directory size %d, " + "aborting\n", + FSInfo->FileSystems_u.UfsInterface.Directories[1]); + return -1; + } + } + + /* + * First, generate our mount point from the DeviceTag and + * FSType. + */ + + DEVICETAGNUMBERTOMOUNTPOINT(MountPoint, DeviceTag, FSType); + + /* + * Then, generate the "level" (directory bitmasks) from the + * file tags, size, and algorithm + */ + + UfsTagsToLevel(VLkp_FileTag1(vnode), VLkp_FileTag2(vnode), Algorithm, + Size, Level, VLkp_ParentVnodeId(vnode), + VLkp_ParentUniquifierId(vnode)); + + /* + * Finally, take the above information and generate the + * corresponding filename (this macro ends up being a + * sprintf() call) + */ + + TAGSTONAME(FileName, MountPoint, Level, Directories[Size][1], + vol->parentId, VLkp_ParentVnodeId(vnode), + VLkp_ParentUniquifierId(vnode), Algorithm); + + fprintf(f, "%s\n", FileName); + + return 0; +} +#endif + +/* + * Read a 16 bit integer in network order + */ + +static int +ReadInt16(FILE *f, unsigned short *s) +{ + unsigned short in; + + if (fread((void *)&in, sizeof(in), 1, f) != 1) { + if (verbose) + fprintf(stderr, "ReadInt16 failed!\n"); + return -1; + } + + *s = ntohs(in); + + return 0; +} + + +/* + * Read a 32 bit integer in network order + */ + +static int +ReadInt32(FILE *f, unsigned int *i) +{ + unsigned int in; + + if (fread((void *)&in, sizeof(in), 1, f) != 1) { + if (verbose) + fprintf(stderr, "ReadInt32 failed!\n"); + return -1; + } + + *i = ntohl((unsigned long) in); + + return 0; +} + +/* + * Read a string from a dump file + */ + +static int +ReadString(FILE *f, char *string, int maxlen) +{ + int c; + + while (maxlen--) { + if ((*string++ = getc(f)) == 0) + break; + } + + /* + * I'm not sure what the _hell_ this is supposed to do ... + * but it was in the original dump code + */ + + if (string[-1]) { + while ((c = getc(f)) && c != EOF); + string[-1] = 0; + } + + return 0; +} + +static int +ReadByteString(FILE *f, void *s, int size) +{ + unsigned char *c = (unsigned char *) s; + + while (size--) + *c++ = getc(f); + + return 0; +} + +/* + * The directory hashing algorithm used by AFS + */ + +DirHash (string) + register char *string; { + /* Hash a string to a number between 0 and NHASHENT. */ + register unsigned char tc; + register int hval; + register int tval; + hval = 0; + while(tc=(*string++)) { + hval *= 173; + hval += tc; + } + tval = hval & (NHASHENT-1); +#ifdef AFS_CRAY_ENV /* actually, any > 32 bit environment */ + if (tval == 0) return tval; + else if (hval & 0x80000000) tval = NHASHENT-tval; +#else /* AFS_CRAY_ENV */ + if (tval == 0) return tval; + else if (hval < 0) tval = NHASHENT-tval; +#endif /* AFS_CRAY_ENV */ + return tval; +} + +#ifdef RESIDENCY +/* + * Sigh, we need this for the AFS libraries + */ + +int +LogErrors(int level, char *a, char *b, char *c, char *d, char *e, char *f, + char *g, char *h, char *i, char *j, char *k) +{ + if (level <= 0) { + fprintf(stderr, a, b, c, d, e, f, g, h, i, j, k); + } + return 0; +} + +/* + * These are routines taken from AFS libraries and programs. Most of + * them are from ufsname.c, but a few are from the dir library (the dir + * library has a bunch of hidden dependancies, so it's not suitable to + * include it outright). + */ + +UfsEntropiesToTags(HighEntropy,LowEntropy,Algorithm,FileTag1,FileTag2) + uint32_t HighEntropy; + uint32_t LowEntropy; + uint32_t Algorithm; + uint32_t *FileTag1; + uint32_t *FileTag2; +{ + int i; + + if ((Algorithm > UFS_ALGORITHMS) || (Algorithm <= 0)) + return -1; + *FileTag1 = 0; + *FileTag2 = 0; + for (i=0;i<32;++i) { + if (UfsEntropy[Algorithm-1][i] < 32) + *FileTag1 |= ((HighEntropy & (1 << i)) == 0) ? + 0 : 1 << UfsEntropy[Algorithm-1][i]; + else + *FileTag2 |= ((HighEntropy & (1 << i)) == 0) ? + 0 : 1 << (UfsEntropy[Algorithm-1][i] - 32); + } + for (i=32;i<64;++i) { + if (UfsEntropy[Algorithm-1][i] < 32) + *FileTag1 |=((LowEntropy & (1 << (i - 32))) == 0) ? + 0 : 1 << UfsEntropy[Algorithm-1][i]; + else + *FileTag2 |=((LowEntropy & (1 << (i - 32))) == 0) ? + 0 : 1 << (UfsEntropy[Algorithm-1][i] - 32); + } + return 0; +} + +uint32_t UfsTagsToHighEntropy(FileTag1,FileTag2,Algorithm) + uint32_t FileTag1; + uint32_t FileTag2; + uint32_t Algorithm; +{ + int i; + uint32_t Value; + + Value = 0; + for (i=0;i<32;++i) { + if (UfsEntropy[Algorithm-1][i] < 32) + Value |= ((FileTag1 & (1 << UfsEntropy[Algorithm-1][i])) + == 0) ? 0: 1 << i; + else + Value |= ((FileTag2 & (1 << (UfsEntropy[Algorithm-1][i] - + 32))) == 0) ? 0: 1 << i; + } + return Value; +} + +uint32_t UfsTagsToLowEntropy(FileTag1,FileTag2,Algorithm) + uint32_t FileTag1; + uint32_t FileTag2; + uint32_t Algorithm; +{ + int i; + uint32_t Value; + + Value = 0; + for (i=32;i<64;++i) { + if (UfsEntropy[Algorithm-1][i] < 32) + Value |= ((FileTag1 & (1 << UfsEntropy[Algorithm-1][i])) + == 0) ? 0: 1 << (i - 32); + else + Value |= ((FileTag2 & (1 << (UfsEntropy[Algorithm-1][i] - + 32))) == 0) ? 0: 1 << (i - 32) ; + } + return Value; +} + +UfsTagsToLevel(FileTag1, FileTag2, Algorithm, Size, Sections, vnode, Uniquifier) + uint32_t FileTag1; + uint32_t FileTag2; + uint32_t Algorithm; + uint32_t Size; + uint32_t Sections[4]; + uint32_t vnode; + uint32_t Uniquifier; +{ + uint32_t HighEntropy; + uint32_t LowEntropy; + + switch (Algorithm) { + case 1: + LowEntropy = UfsTagsToLowEntropy( + FileTag1, + FileTag2, + Algorithm); + HighEntropy = UfsTagsToHighEntropy( + FileTag1, + FileTag2, + Algorithm); + Sections[0] = HighEntropy % Directories[Size][0]; + HighEntropy /= Directories[Size][0]; + if (Directories[Size][1]) { + Sections[1] = HighEntropy % Directories[Size][1]; + HighEntropy /= Directories[Size][1]; + Sections[2] = HighEntropy; + Sections[3] = LowEntropy; + } else { + Sections[1] = HighEntropy; + Sections[2] = LowEntropy; + } + break; + case 2: + Sections[0] = FileTag1 & 0xff; + if (Directories[Size][1]) { + Sections[1] = Uniquifier & 0xff; + if (Directories[Size][1] == 16) Sections[1] &= 0xf; + Sections[2] = FileTag1; + Sections[3] = FileTag2; + } else { + Sections[1] = FileTag1; + Sections[2] = FileTag2; + } + break; + case 3: + Sections[0] = FileTag1 & 0xff; + if (Directories[Size][1]) { + Sections[1] = (vnode >> 1) & 0xff; + if (Directories[Size][1] == 16) Sections[1] &= 0xf; + Sections[2] = FileTag1; + Sections[3] = FileTag2; + } else { + Sections[1] = FileTag1; + Sections[2] = FileTag2; + } + break; + default: + fprintf(stderr,"UfsTagsToLevel: bad algorithm %lu!\n", Algorithm); + return -1; + } + return 0; +} + +#include +#endif /* RESIDENCY */ diff --git a/src/tests/int64.c b/src/tests/int64.c new file mode 100644 index 0000000000..4d4a68f7ca --- /dev/null +++ b/src/tests/int64.c @@ -0,0 +1,407 @@ +/* + * CMUCS AFStools + * dumpscan - routines for scanning and manipulating AFS volume dumps + * + * Copyright (c) 1998 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software_Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ + +/* int64.c - Support for 64-bit integers */ + +#include +#include +#include "intNN.h" + +char *hexify_int64(u_int64 *X, char *buf) +{ + static char mybuf[17]; + +#ifdef NATIVE_INT64 + char c, *p; + u_int64 x = *X; + + if (!buf) buf = mybuf; + p = buf + 16; + *p-- = 0; + while (x && p >= buf) { + c = x & 0xf; + c += ((c < 10) ? '0' : 'a' - 10); + *p-- = c; + x >>= 4; + } + while (p >= buf) *p-- = '0'; + +#else + if (!buf) buf = mybuf; + sprintf(buf, "%08lx%08lx", X->hi, X->lo); +#endif + + return buf; +} + + +#ifdef NATIVE_INT64 +char *decimate_int64(u_int64 *X, char *buf) +{ + static char mybuf[21]; + char *p; + u_int64 x = *X; + + if (!buf) buf = mybuf; + p = buf + 21; + *--p = 0; + while (x && p > buf) { + *--p = ((x % 10) + '0'); + x /= 10; + } + if (!*p) *--p = '0'; + return p; +} + +#else +static char bitvals[64][21] = { +/* 1 */ "00000000000000000001", +/* 2 */ "00000000000000000002", +/* 4 */ "00000000000000000004", +/* 8 */ "00000000000000000008", +/* 10 */ "00000000000000000016", +/* 20 */ "00000000000000000032", +/* 40 */ "00000000000000000064", +/* 80 */ "00000000000000000128", +/* 100 */ "00000000000000000256", +/* 200 */ "00000000000000000512", +/* 400 */ "00000000000000001024", +/* 800 */ "00000000000000002048", +/* 1000 */ "00000000000000004096", +/* 2000 */ "00000000000000008192", +/* 4000 */ "00000000000000016384", +/* 8000 */ "00000000000000032768", +/* 10000 */ "00000000000000065536", +/* 20000 */ "00000000000000131072", +/* 40000 */ "00000000000000262144", +/* 80000 */ "00000000000000524288", +/* 100000 */ "00000000000001048576", +/* 200000 */ "00000000000002097152", +/* 400000 */ "00000000000004194304", +/* 800000 */ "00000000000008388608", +/* 1000000 */ "00000000000016777216", +/* 2000000 */ "00000000000033554432", +/* 4000000 */ "00000000000067108864", +/* 8000000 */ "00000000000134217728", +/* 10000000 */ "00000000000268435456", +/* 20000000 */ "00000000000536870912", +/* 40000000 */ "00000000001073741824", +/* 80000000 */ "00000000002147483648", +/* 100000000 */ "00000000004294967296", +/* 200000000 */ "00000000008589934592", +/* 400000000 */ "00000000017179869184", +/* 800000000 */ "00000000034359738368", +/* 1000000000 */ "00000000068719476736", +/* 2000000000 */ "00000000137438953472", +/* 4000000000 */ "00000000274877906944", +/* 8000000000 */ "00000000549755813888", +/* 10000000000 */ "00000001099511627776", +/* 20000000000 */ "00000002199023255552", +/* 40000000000 */ "00000004398046511104", +/* 80000000000 */ "00000008796093022208", +/* 100000000000 */ "00000017592186044416", +/* 200000000000 */ "00000035184372088832", +/* 400000000000 */ "00000070368744177664", +/* 800000000000 */ "00000140737488355328", +/* 1000000000000 */ "00000281474976710656", +/* 2000000000000 */ "00000562949953421312", +/* 4000000000000 */ "00001125899906842624", +/* 8000000000000 */ "00002251799813685248", +/* 10000000000000 */ "00004503599627370496", +/* 20000000000000 */ "00009007199254740992", +/* 40000000000000 */ "00018014398509481984", +/* 80000000000000 */ "00036028797018963968", +/* 100000000000000 */ "00072057594037927936", +/* 200000000000000 */ "00144115188075855872", +/* 400000000000000 */ "00288230376151711744", +/* 800000000000000 */ "00576460752303423488", +/* 1000000000000000 */ "01152921504606846976", +/* 2000000000000000 */ "02305843009213693952", +/* 4000000000000000 */ "04611686018427387904", +/* 8000000000000000 */ "09223372036854775808" }; + + +static void prep_table(void) +{ + int bit, digit; + + if (bitvals[0][0] < '0') return; + for (bit = 0; bit < 64; bit++) + for (digit = 0; digit < 20; digit++) + bitvals[bit][digit] -= '0'; +} + + +static void add_bit(int bit, char *answer) +{ + int digit; + + for (digit = 19; digit >= 0; digit--) { + answer[digit] += bitvals[bit][digit]; + if (!digit) break; + while(answer[digit] > 9) { + answer[digit] -= 10; + answer[digit-1]++; + } + } +} + + +static void decimate(unsigned long hi, unsigned long lo, char *answer) +{ + unsigned long mask; + int bit, digit; + + memset(answer, 0, 21); + for (bit = 0, mask = 1; bit < 32; bit++, mask <<= 1) + if (lo&mask) add_bit(bit, answer); + for (bit = 0, mask = 1; bit < 32; bit++, mask <<= 1) + if (hi&mask) add_bit(bit + 32, answer); + + for (digit = 0; digit < 20; digit++) + answer[digit] += '0'; +} + +char *decimate_int64(u_int64 *X, char *buf) +{ + static char mybuf[21]; + char *p; + + prep_table(); + if (!buf) buf = mybuf; + decimate(X->hi, X->lo, buf); + for (p = buf; *p == '0'; p++); + return (*p) ? p : p-1; +} + +#endif /* NATIVE_INT64 */ + + +void shift_int64(u_int64 *X, int bits) +{ +#ifdef NATIVE_INT64 + if (bits < 0) *X >>= (-bits); + else *X <<= bits; +#else + if (bits < 0) { + bits = -bits; + if (bits >= 32) { + X->lo = ((X->hi & 0xffffffffL) >> (bits - 32)); + X->hi = 0; + } else { + X->lo = ((X->lo & 0xffffffffL) >> bits) + | ((X->hi & ((1 << (32 - bits)) - 1)) << (32 - bits)); + X->hi = ((X->hi & 0xffffffffL) >> bits); + } + } else { + if (bits >= 32) { + X->hi = ((X->lo & 0xffffffffL) << (bits - 32)); + X->lo = 0; + } else { + X->hi = ((X->hi & 0xffffffffL) << bits) + | ((X->lo & (((1 << bits) - 1) << (32 - bits))) >> (32 - bits)); + X->lo = ((X->lo & 0xffffffffL) << bits); + } + } +#endif +} + + +#ifdef TEST_INT64 + +/** the rest of this is for testing the int64 suite **/ + +#ifdef NATIVE_INT64 + +#define xize(x) #x +#define stringize(x) xize(x) +#define INT64_NAME stringize(unsigned NATIVE_INT64) + + +#endif /* NATIVE_INT64 */ + + +void verify_int64_size () { +#ifdef NATIVE_INT64 + signed char testchar = -1; + unsigned int testint = (unsigned char)testchar; + + printf("We think '%s' is a native 64-bit type\n", INT64_NAME); + + if (testint != 0xff) { + printf("testint = 0x%x; should be 0xff\n", testint); + fprintf(stderr, "Hmm... char's are not 8 bits. That sucks!\n"); + exit(-1); + } + printf("Looks like a char is 8 bits...\n"); + + if (sizeof(unsigned NATIVE_INT64) != 8) { + printf("sizeof(%s) = %d; should be 8\n", INT64_NAME, sizeof(unsigned NATIVE_INT64)); + fprintf(stderr, "Hey! You said a %s was 64-bits wide!\n", INT64_NAME); + exit(-1); + } + printf("Yippee! We have a native 64-bit type (%s)\n\n", INT64_NAME); + +#else /* !NATIVE_INT64 */ + + printf("Using fake 64-bit integers...\n\n"); +#endif /* NATIVE_INT64 */ +} + + +void test_int64_constructs(void) +{ + u_int64 x, y; + afs_uint32 hi, lo; + int failures = 0, pass; + char buf[17]; + + printf("Constructor/accessor tests:\n"); + printf("Setting x := %s\n", INT64_TEST_STR); + mk64(x, INT64_TEST_HI, INT64_TEST_LO); + +#ifdef NATIVE_INT64 + pass = (x == INT64_TEST_CONST); + hexify_int64(&x, buf); + printf("NATIVE mk64: x = 0x%16s %s\n", + buf, pass ? "PASSED" : "FAILED"); + if (!pass) failures++; +#else + pass = (x.hi == INT64_TEST_HI && x.lo == INT64_TEST_LO); + printf("FAKE mk64: x.hi = 0x%08lx x.lo = 0x%08lx %s\n", + x.hi, x.lo, pass ? "PASSED" : "FAILED"); + if (!pass) failures++; +#endif + + pass = (hi64(x) == INT64_TEST_HI && lo64(x) == INT64_TEST_LO); + printf("hi64/lo64: hi64(x) = 0x%08lx lo64(x) = 0x%08lx %s\n", + hi64(x), lo64(x), pass ? "PASSED" : "FAILED"); + if (!pass) failures++; + + ex64(x, hi, lo); + pass = (hi == INT64_TEST_HI && lo == INT64_TEST_LO); + printf("ex64: hi = 0x%08lx lo = 0x%08lx %s\n", + hi, lo, pass ? "PASSED" : "FAILED"); + if (!pass) failures++; + + cp64(y, x); + pass = (hi64(y) == INT64_TEST_HI && lo64(y) == INT64_TEST_LO); + printf("cp64: hi64(y) = 0x%08lx lo64(y) = 0x%08lx %s\n", + hi64(y), lo64(y), pass ? "PASSED" : "FAILED"); + if (!pass) failures++; + + if (failures) printf("%d/4 tests FAILED\n\n", failures); + else printf("All 4 tests PASSED\n\n"); +} + + +void test_int64_compares() +{ +#define NCOMPARE 9 + u_int64 control, test[NCOMPARE]; + char cbuf[17], tbuf[17]; + int i, r, result[NCOMPARE]; + int pass, failures, FAILURES = 0; + + printf("Comparison tests:\n"); + + mk64(control, 0x12345678, 0xabcdabcd); + mk64(test[0], 0x12340000, 0xabcd0000); result[0] = +1; + mk64(test[1], 0x12340000, 0xabcdabcd); result[1] = +1; + mk64(test[2], 0x12340000, 0xabcdffff); result[2] = +1; + mk64(test[3], 0x12345678, 0xabcd0000); result[3] = +1; + mk64(test[4], 0x12345678, 0xabcdabcd); result[4] = 0; + mk64(test[5], 0x12345678, 0xabcdffff); result[5] = -1; + mk64(test[6], 0x1234ffff, 0xabcd0000); result[6] = -1; + mk64(test[7], 0x1234ffff, 0xabcdabcd); result[7] = -1; + mk64(test[8], 0x1234ffff, 0xabcdffff); result[8] = -1; + + for (i = 0; i < NCOMPARE; i++) { + failures = 0; + hexify_int64(&control, cbuf); + hexify_int64(&test[i], tbuf); + + r = eq64(control, test[i]); + pass = (r == (result[i] == 0)); if (!pass) failures++; + printf("0x%s == 0x%s %s\n", cbuf, tbuf, pass ? "PASSED" : "FAILED"); + + r = ne64(control, test[i]); + pass = (r == (result[i] != 0)); if (!pass) failures++; + printf("0x%s != 0x%s %s\n", cbuf, tbuf, pass ? "PASSED" : "FAILED"); + + r = lt64(control, test[i]); + pass = (r == (result[i] < 0)); if (!pass) failures++; + printf("0x%s < 0x%s %s\n", cbuf, tbuf, pass ? "PASSED" : "FAILED"); + + r = le64(control, test[i]); + pass = (r == (result[i] <= 0)); if (!pass) failures++; + printf("0x%s <= 0x%s %s\n", cbuf, tbuf, pass ? "PASSED" : "FAILED"); + + r = gt64(control, test[i]); + pass = (r == (result[i] > 0)); if (!pass) failures++; + printf("0x%s > 0x%s %s\n", cbuf, tbuf, pass ? "PASSED" : "FAILED"); + + r = ge64(control, test[i]); + pass = (r == (result[i] >= 0)); if (!pass) failures++; + printf("0x%s >= 0x%s %s\n", cbuf, tbuf, pass ? "PASSED" : "FAILED"); + + r = zero64(test[i]); + pass = !r; if (!pass) failures++; + printf("0x%s is nonzero %s\n", + tbuf, pass ? "PASSED" : "FAILED"); + + if (failures) printf("%d/7 tests on this pair FAILED\n\n", failures); + else printf("All 7 tests on this pair PASSED\n\n"); + } + + mk64(control, 0, 0); + pass = zero64(control); if (!pass) FAILURES++; + printf("0x0000000000000000 is zero %s\n", + pass ? "PASSED" : "FAILED"); + + if (FAILURES) + printf("%d/%d comparison tests FAILED\n\n", FAILURES, 7 * NCOMPARE + 1); + else + printf("All %d comparison tests PASSED\n\n", 7 * NCOMPARE + 1); +} + + +void test_int64_arith() +{ + printf("No arithmetic tests yet!!!\n"); +} + + +void main() { + verify_int64_size(); + test_int64_constructs(); + test_int64_compares(); + test_int64_arith(); + exit(0); +} +#endif diff --git a/src/tests/intNN.h b/src/tests/intNN.h new file mode 100644 index 0000000000..964605238e --- /dev/null +++ b/src/tests/intNN.h @@ -0,0 +1,155 @@ +/* + * CMUCS AFStools + * dumpscan - routines for scanning and manipulating AFS volume dumps + * + * Copyright (c) 1998 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software_Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ + +#ifndef _INTNN_H_ +#define _INTNN_H_ + +/* intNN.h - Sized integer types */ +#include +#if 0 +typedef short afs_int16; +typedef unsigned short afs_uint16; + +typedef long afs_int32; +typedef unsigned long afs_uint32; +#endif + + +/* Support for 64-bit integers. + * Presently, only unsigned 64-bit numbers are supported. + */ +#define INT64_TEST_STR "0x12345678fedcba98" +#define INT64_TEST_HI 0x12345678 +#define INT64_TEST_LO 0xfedcba98 + + +#ifdef NATIVE_INT64 +typedef unsigned NATIVE_INT64 u_int64; + +/* construct/extract/assign */ +#define mk64(X,H,L) ((X) = ( ((u_int64)(H) << 32) \ + | ((u_int64)(L) & 0xffffffff))) +#define hi64(Y) ((afs_uint32)(((Y) >> 32) & 0xffffffff)) +#define lo64(Y) ((afs_uint32)((Y) & 0xffffffff)) +#define ex64(Y,H,L) ((H) = hi64(Y), (L) = lo64(Y)) +#define cp64(X,Y) ((X) = (Y)) +#define get64(X) (X) +#define set64(X,V) ((X) = (V)) + +/* Comparison */ +#define eq64(X,Y) ((X) == (Y)) +#define ne64(X,Y) ((X) != (Y)) +#define lt64(X,Y) ((X) < (Y)) +#define le64(X,Y) ((X) <= (Y)) +#define gt64(X,Y) ((X) > (Y)) +#define ge64(X,Y) ((X) >= (Y)) +#define zero64(X) (!(X)) + +/* Arithmetic */ +#define add64_32(X,A,B) ((X) = (A) + (u_int64)(B)) +#define add64_64(X,A,B) ((X) = (A) + (B)) +#define sub64_32(X,A,B) ((X) = (A) - (u_int64)(B)) +#define sub64_64(X,A,B) ((X) = (A) - (B)) + +/* Byte-order */ +#ifdef WORDS_BIGENDIAN +#define hton64(X,Y) cp64(X,Y) +#define ntoh64(X,Y) cp64(X,Y) +#else +#define hton64(X,Y) mk64(X,htonl(lo64(Y)),htonl(hi64(Y))) +#define ntoh64(X,Y) mk64(X,ntohl(lo64(Y)),ntohl(hi64(Y))) +#endif + +#else /* !NATIVE_INT64 */ +/** We have to provide our own 64-bit integers **/ +typedef struct { afs_uint32 hi, lo; } u_int64; + +/* construct/extract/assign */ +#define mk64(X,H,L) ((X).hi = (H), (X).lo = (L)) +#define ex64(Y,H,L) ((H) = (Y).hi, (L) = (Y).lo) +#define hi64(Y) ((Y).hi) +#define lo64(Y) ((Y).lo) +#define cp64(X,Y) ((X).hi = (Y).hi, (X).lo = (Y).lo) +#define get64(X) ((X).lo) +#define set64(X,V) ((X).hi = 0, (X).lo = (V)) + +/* Comparison */ +#define eq64(A,B) ((A).hi == (B).hi && (A).lo == (B).lo) +#define ne64(A,B) ((A).hi != (B).hi || (A).lo != (B).lo) +#define lt64(A,B) ((A).hi < (B).hi || ((A).hi == (B).hi && (A).lo < (B).lo)) +#define le64(A,B) ((A).hi < (B).hi || ((A).hi == (B).hi && (A).lo <= (B).lo)) +#define gt64(A,B) ((A).hi > (B).hi || ((A).hi == (B).hi && (A).lo > (B).lo)) +#define ge64(A,B) ((A).hi > (B).hi || ((A).hi == (B).hi && (A).lo >= (B).lo)) +#define zero64(X) ((X).hi == 0 && (X).lo == 0) + +/* Arithmetic */ +#define add64_32(X,A,B) ( \ + (X).lo = (A).lo + (B), \ + (X).hi = (A).hi + \ + (((((A).lo & 0x80000000) ^ ((B) & 0x80000000)) && !((X).lo & 0x80000000)) \ + || (((A).lo & 0x80000000) && ((B) & 0x80000000))) \ + ) +#define add64_64(X,A,B) (add64_32(X,A,(B).lo), (X).hi += (B).hi) + +#define sub64_32(X,A,B) ((X).lo = (A).lo - (B), \ + (X).hi = (A).hi - ((A).lo < (B))) +#define sub64_64(X,A,B) (sub64_32(X,A,(B).lo), (X).hi -= (B).hi) + +/* Byte-order */ +#define hton64(X,Y) mk64(X,htonl(hi64(Y)),htonl(lo64(Y))) +#define ntoh64(X,Y) mk64(X,ntohl(hi64(Y)),ntohl(lo64(Y))) + +#endif /* NATIVE_INT64 */ + + +/* The following are too complex to be macros: */ + +/* char *hexify_int64(u_int64 a, char *buf) + * Produces an ASCII representation of a in hexadecimal, and returns + * a pointer to the resulting string. If buf is non-NULL, it is taken + * to be a pointer to the buffer to be used, which must be at least 17 + * bytes long. This function is thread-safe iff buf is provided. + */ +extern char *hexify_int64(u_int64 *, char *); + +/* char *decimate_int64(u_int64 a, char *buf) + * Produces an ASCII representation of a in decimal, and returns + * a pointer to the resulting string. If buf is non-NULL, it is taken + * to be a pointer to the buffer to be used, which must be at least 21 + * bytes long. This function is thread-safe iff buf is provided. + */ +extern char *decimate_int64(u_int64 *, char *); + +/* void shift_int64(u_int64 a, int bits) + * Shifts the 64-bit integer in a by the specified number of bits. + * If bits is positive, the shift is to the left; if negative, the + * shift is to the right. + */ +extern void shift_int64(u_int64 *, int); + +#endif /* _INTNN_H_ */ diff --git a/src/tests/internal.h b/src/tests/internal.h new file mode 100644 index 0000000000..04233dbb0c --- /dev/null +++ b/src/tests/internal.h @@ -0,0 +1,55 @@ +/* + * CMUCS AFStools + * dumpscan - routines for scanning and manipulating AFS volume dumps + * + * Copyright (c) 1998 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software_Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ + +/* internal.h - Routines for internal use only */ + +#include "xfiles.h" +#include "dumpscan.h" + + +/* parsevol.c - Routines to parse volume headers */ +extern afs_uint32 parse_volhdr(XFILE *, unsigned char *, tagged_field *, afs_uint32, + tag_parse_info *, void *, void *); + +/* parsevnode.c - Routines to parse vnodes and their fields */ +extern afs_uint32 parse_vnode(XFILE *, unsigned char *, tagged_field *, afs_uint32, + tag_parse_info *, void *, void *); + +/* directory.c - Routines for parsing AFS directories */ +extern afs_uint32 parse_directory(XFILE *, dump_parser *, afs_vnode *, + afs_uint32, int); + +/* backuphdr.c - Generic support for backup system headers */ +extern afs_uint32 try_backuphdr(XFILE *X, unsigned char *tag, tagged_field *field, + afs_uint32 value, tag_parse_info *pi, + void *g_refcon, void *l_refcon); + +/* util.c - Random utilities */ +extern afs_uint32 handle_return(int, XFILE *, unsigned char, dump_parser *); +extern void prep_pi(dump_parser *, tag_parse_info *); +extern afs_uint32 match_next_vnode(XFILE *, dump_parser *, u_int64 *, afs_uint32); diff --git a/src/tests/null-search.c b/src/tests/null-search.c new file mode 100644 index 0000000000..39afb043f5 --- /dev/null +++ b/src/tests/null-search.c @@ -0,0 +1,150 @@ +#include +#include +#include +#include + +#include "dumpscan.h" + +char *argv0; +static char *input_path = 0; +static int quiet = 0, showpaths = 0, searchcount = 1; +static int error_count = 0, bad_count = 0; +static path_hashinfo phi; +static dump_parser dp; + +/* Print a usage message and exit */ +static void usage(int status, char *msg) +{ + if (msg) fprintf(stderr, "%s: %s\n", argv0, msg); + fprintf(stderr, "Usage: %s [options] [file]\n", argv0); + fprintf(stderr, " -h Print this help message\n"); + fprintf(stderr, " -p Print paths of bad vnodes\n"); + fprintf(stderr, " -q Quiet mode (don't print errors)\n"); + exit(status); +} + + +/* Parse the command-line options */ +static void parse_options(int argc, char **argv) +{ + int c; + + if (argv0 = strrchr(argv[0], '/')) argv0++; + else argv0 = argv[0]; + + /* Parse the options */ + while ((c = getopt(argc, argv, "n:hpq")) != EOF) { + switch (c) { + case 'n': searchcount = atoi(optarg); continue; + case 'p': showpaths = 1; continue; + case 'q': quiet = 1; continue; + case 'h': usage(0, 0); + default: usage(1, "Invalid option!"); + } + } + + if (argc - optind > 1) usage(1, "Too many arguments!"); + input_path = (argc == optind) ? "-" : argv[optind]; +} + + +/* A callback to count and print errors */ +static afs_uint32 my_error_cb(afs_uint32 code, int fatal, void *ref, char *msg, ...) +{ + va_list alist; + + error_count++; + if (!quiet) { + va_start(alist, msg); + com_err_va(argv0, code, msg, alist); + va_end(alist); + } +} + + +/* A callback to process file vnodes */ +static afs_uint32 my_file_cb(afs_vnode *v, XFILE *X, void *refcon) +{ + static char buf[1024]; + afs_uint32 size, nulls, cnulls, maxcnulls, n, r; + char *name = 0; + int i; + + nulls = cnulls = maxcnulls = 0; + size = v->size; + if ((r = xfseek(X, &v->d_offset))) return r; + while (size) { + n = (size > 1024) ? 1024 : size; + if (r = xfread(X, buf, n)) return r; + for (i = 0; i < n; i++) { + if (buf[i]) { + if (cnulls > maxcnulls) maxcnulls = cnulls; + cnulls = 0; + } else { + nulls++; + cnulls++; + } + } + size -= n; + } + if (maxcnulls >= searchcount) { + bad_count++; + if (showpaths) Path_Build(X, &phi, v->vnode, &name, 0); + if (name) { + printf("*** BAD %d (%s) - %d nulls, %d consecutive\n", + v->vnode, name, nulls, maxcnulls); + free(name); + } else { + printf("*** BAD %d - %d nulls, %d consecutive\n", + v->vnode, nulls, maxcnulls); + } + } + return r; +} + + +int main(int argc, char **argv) +{ + XFILE input_file; + afs_uint32 r; + + parse_options(argc, argv); + initialize_acfg_error_table(); + initialize_AVds_error_table(); + initialize_rxk_error_table(); + initialize_u_error_table(); + initialize_vl_error_table(); + initialize_vols_error_table(); + initialize_xFil_error_table(); + r = xfopen(&input_file, O_RDONLY, input_path); + if (r) { + com_err(argv0, r, "opening %s", input_path); + exit(2); + } + + memset(&dp, 0, sizeof(dp)); + dp.cb_error = my_error_cb; + if (input_file.is_seekable) dp.flags |= DSFLAG_SEEK; + if (showpaths) { + u_int64 where; + + memset(&phi, 0, sizeof(phi)); + phi.p = &dp; + + if ((r = xftell(&input_file, &where)) + || (r = Path_PreScan(&input_file, &phi, 0)) + || (r = xfseek(&input_file, &where))) { + com_err(argv0, r, "- path initialization failed"); + xfclose(&input_file); + exit(2); + } + } + + dp.cb_vnode_file = my_file_cb; + r = ParseDumpFile(&input_file, &dp); + xfclose(&input_file); + + if (error_count) printf("*** %d errors\n", error_count); + if (bad_count) printf("*** %d bad files\n", bad_count); + if (r && !quiet) printf("*** FAILED: %s\n", error_message(r)); +} diff --git a/src/tests/parsedump.c b/src/tests/parsedump.c new file mode 100644 index 0000000000..edf6903b5a --- /dev/null +++ b/src/tests/parsedump.c @@ -0,0 +1,258 @@ +/* + * CMUCS AFStools + * dumpscan - routines for scanning and manipulating AFS volume dumps + * + * Copyright (c) 1998 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software_Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ + +/* parsedump.c - Parse a volume dump file */ + +#include "dumpscan.h" +#include "dumpscan_errs.h" +#include "dumpfmt.h" +#include "internal.h" +#include "stagehdr.h" + +static afs_uint32 parse_dumphdr (XFILE *, unsigned char *, tagged_field *, + afs_uint32, tag_parse_info *, void *, void *); +static afs_uint32 parse_dumpend (XFILE *, unsigned char *, tagged_field *, + afs_uint32, tag_parse_info *, void *, void *); +static afs_uint32 store_dumphdr (XFILE *, unsigned char *, tagged_field *, + afs_uint32, tag_parse_info *, void *, void *); +static afs_uint32 parse_dumptimes(XFILE *, unsigned char *, tagged_field *, + afs_uint32, tag_parse_info *, void *, void *); + +/** Field list for top-level objects **/ +static tagged_field top_fields[] = { + { TAG_DUMPHEADER, DKIND_SPECIAL, "* DUMP HEADER", parse_dumphdr, 0, 0 }, + { TAG_VOLHEADER, DKIND_SPECIAL, "* VOLUME HEADER", parse_volhdr, 0, 0 }, + { TAG_VNODE, DKIND_SPECIAL, "* VNODE ", parse_vnode, 0, 0 }, + { TAG_DUMPEND, DKIND_INT32, "* DUMP END", parse_dumpend, 0, 0 }, + { STAGE_VERSMIN, DKIND_SPECIAL, "* STAGE HEADER", try_backuphdr, 0, 0 }, + { 0,0,0,0,0,0 }}; + + +/** Field list for dump headers **/ +static tagged_field dumphdr_fields[] = { + { DHTAG_VOLNAME, DKIND_STRING, " Volume name: ", store_dumphdr, 0, 0 }, + { DHTAG_VOLID, DKIND_INT32, " Volume ID: ", store_dumphdr, 0, 0 }, + { DHTAG_DUMPTIMES, DKIND_SPECIAL, " Dump Range: ", parse_dumptimes, 0, 0 }, + { 0,0,0,0,0,0 }}; + + +/* Parse a dump header, including its tagged attributes, and call the + * dump-header callback, if one is defined. + */ +static afs_uint32 parse_dumphdr(XFILE *X, unsigned char *tag, tagged_field *field, + afs_uint32 value, tag_parse_info *pi, + void *g_refcon, void *l_refcon) +{ + dump_parser *p = (dump_parser *)g_refcon; + afs_dump_header hdr; + u_int64 where; + afs_uint32 r; + + memset(&hdr, 0, sizeof(hdr)); + if (r = xftell(X, &where)) return r; + sub64_32(hdr.offset, where, 1); + + if (r = ReadInt32(X, &hdr.magic)) return r; + if (r = ReadInt32(X, &hdr.version)) return r; + + if (hdr.magic != DUMPBEGINMAGIC) { + if (p->cb_error) + (p->cb_error)(DSERR_MAGIC, 1, p->err_refcon, + "Invalid magic number (0x%08x) in dump header", + hdr.magic); + return DSERR_MAGIC; + } + if (hdr.version != DUMPVERSION) { + if (p->cb_error) + (p->cb_error)(DSERR_MAGIC, 1, p->err_refcon, + "Unknown dump format version (%d) in dump header", + hdr.version); + return DSERR_MAGIC; + } + + if (p->print_flags & DSPRINT_DUMPHDR) + printf("%s [%s = 0x%s]\n", field->label, + decimate_int64(&hdr.offset, 0), hexify_int64(&hdr.offset, 0)); + if (p->print_flags & DSPRINT_DUMPHDR) { + printf(" Magic number: 0x%08x\n", hdr.magic); + printf(" Version: %d\n", hdr.version); + } + r = ParseTaggedData(X, dumphdr_fields, tag, pi, g_refcon, (void *)&hdr); + + if (!r && p->cb_dumphdr) { + r = xftell(X, &where); + if (!r) r = (p->cb_dumphdr)(&hdr, X, p->refcon); + if (p->flags & DSFLAG_SEEK) { + if (!r) r = xfseek(X, &where); + else xfseek(X, &where); + } + } + if (hdr.field_mask & F_DUMPHDR_VOLNAME) + free(hdr.volname); + return r; +} + + +/* Store tagged attributes into a dump header */ +static afs_uint32 store_dumphdr(XFILE *X, unsigned char *tag, tagged_field *field, + afs_uint32 value, tag_parse_info *pi, + void *g_refcon, void *l_refcon) +{ + dump_parser *p = (dump_parser *)g_refcon; + afs_dump_header *hdr = (afs_dump_header *)l_refcon; + + switch (field->tag) { + case DHTAG_VOLID: + hdr->field_mask |= F_DUMPHDR_VOLID; + hdr->volid = value; + if (p->print_flags & DSPRINT_DUMPHDR) + printf("%s%d\n", field->label, hdr->volid); + return 0; + + case DHTAG_VOLNAME: + if (tag && tag[0]) { + hdr->field_mask |= F_DUMPHDR_VOLNAME; + hdr->volname = tag; + if (p->print_flags & DSPRINT_DUMPHDR) + printf("%s%s\n", field->label, hdr->volname); + return DSERR_KEEP; + } else return 0; + + default: + if (p->print_flags & DSPRINT_DUMPHDR) + printf("%s<<< UNKNOWN FIELD >>>\n", field->label); + return 0; + } +} + + +/* Parse and store the dump time range from a dump header */ +static afs_uint32 parse_dumptimes(XFILE *X, unsigned char *tag, + tagged_field *field, afs_uint32 value, + tag_parse_info *pi, + void *g_refcon, void *l_refcon) +{ + dump_parser *p = (dump_parser *)g_refcon; + afs_dump_header *hdr = (afs_dump_header *)l_refcon; + afs_uint16 count; + afs_uint32 r; + + if (r = ReadInt16(X, &count)) return r; + if (count != 2) { + if (p->cb_error) + (p->cb_error)(DSERR_FMT, 1, p->err_refcon, + "Incorrect array count (%d) in dump times", count); + return DSERR_FMT; + } + if (r = ReadInt32(X, &hdr->from_date)) return r; + if (r = ReadInt32(X, &hdr->to_date)) return r; + hdr->field_mask |= (F_DUMPHDR_FROM | F_DUMPHDR_TO); + if (p->print_flags & DSPRINT_DUMPHDR) + printf("%s%d => %d\n", field->label, hdr->from_date, hdr->to_date); + + return ReadByte(X, tag); +} + + +/* Parse a dump_end record */ +static afs_uint32 parse_dumpend(XFILE *X, unsigned char *tag, tagged_field *field, + afs_uint32 value, tag_parse_info *pi, + void *g_refcon, void *l_refcon) +{ + dump_parser *p = (dump_parser *)g_refcon; + afs_uint32 r; + + if (value != DUMPENDMAGIC) { + if (p->cb_error) + (p->cb_error)(DSERR_MAGIC, 1, p->err_refcon, + "Invalid magic number (0x%08x) in dump trailer", + value); + return DSERR_MAGIC; + } + if (p->print_flags & (DSPRINT_DUMPHDR | DSPRINT_ITEM)) + printf("%s\n", field->label); + return DSERR_DONE; +} + + + +afs_uint32 ParseDumpFile(XFILE *X, dump_parser *p) +{ + tag_parse_info pi; + unsigned char tag; + afs_uint32 r; + + prep_pi(p, &pi); + r = ParseTaggedData(X, top_fields, &tag, &pi, (void *)p, 0); + return handle_return(r, X, tag, p); +} + + +afs_uint32 ParseDumpHeader(XFILE *X, dump_parser *p) +{ + tag_parse_info pi; + unsigned char tag; + afs_uint32 r; + + prep_pi(p, &pi); + if (r = ReadByte(X, &tag)) return handle_return(r, X, tag, p); + if (tag != TAG_DUMPHEADER) return handle_return(0, X, tag, p); + r = parse_dumphdr(X, &tag, &top_fields[0], 0, &pi, (void *)p, 0); + if (!r && tag >= 1 && tag <= 4) r = DSERR_DONE; + return handle_return(r, X, tag, p); +} + + +afs_uint32 ParseVolumeHeader(XFILE *X, dump_parser *p) +{ + tag_parse_info pi; + unsigned char tag; + afs_uint32 r; + + prep_pi(p, &pi); + if (r = ReadByte(X, &tag)) return handle_return(r, X, tag, p); + if (tag != TAG_VOLHEADER) return handle_return(0, X, tag, p); + r = parse_volhdr(X, &tag, &top_fields[1], 0, &pi, (void *)p, 0); + if (!r && tag >= 1 && tag <= 4) r = DSERR_DONE; + return handle_return(r, X, tag, p); +} + + +afs_uint32 ParseVNode(XFILE *X, dump_parser *p) +{ + tag_parse_info pi; + unsigned char tag; + afs_uint32 r; + + prep_pi(p, &pi); + if (r = ReadByte(X, &tag)) return handle_return(r, X, tag, p); + if (tag != TAG_VNODE) return handle_return(0, X, tag, p); + r = parse_vnode(X, &tag, &top_fields[2], 0, &pi, (void *)p, 0); + if (!r && tag >= 1 && tag <= 4) r = DSERR_DONE; + return handle_return(r, X, tag, p); +} diff --git a/src/tests/parsetag.c b/src/tests/parsetag.c new file mode 100644 index 0000000000..037ea38384 --- /dev/null +++ b/src/tests/parsetag.c @@ -0,0 +1,174 @@ +/* + * CMUCS AFStools + * dumpscan - routines for scanning and manipulating AFS volume dumps + * + * Copyright (c) 1998 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software_Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ + +/* parsetag.c - Parse a tagged data stream */ + +#include "dumpscan.h" +#include "dumpscan_errs.h" + +/* If a parser function is defined, it will be called after the data value + * (if any) is read. The parser is called as follows: + * + * parser(input_file, &tag, &field_rec, value, g_refcon, l_refcon); + * + * - input_file is the FILE * for the input stream + * - field_rec is a pointer to the field record for the field just read + * - g_refcon and l_refcon are as passed in to ParseTaggedData + * - For integer types, value is the integer value + * - For DKIND_STRING, tag is a pointer to the string just read + * - For DKIND_SPEACH, tag is a pointer to the place to put the next tag. + * + * If the field type is DKIND_SPECIAL, the parser is expected to read its + * own data from the input stream, and return when ParseTaggedData is supposed + * to take over, with the next tag to process in *tag. At no other time + * should the parser read, write, or reposition the input stream. + * + * The parser routine should return 0 on success, non-0 on failure. If the + * data type is DKIND_STRING, the parser may return DSERR_KEEP to indicate + * that the memory allocated for the value should not be freed. + */ + +/* Parse a file containing tagged data and attributes **/ +afs_uint32 ParseTaggedData(XFILE *X, tagged_field *fields, unsigned char *tag, + tag_parse_info *pi, void *g_refcon, void *l_refcon) +{ + int i = -1; + afs_uint32 r, val; + afs_uint16 val16; + unsigned char val8; + unsigned char *strval; + + for (;;) { + if (i < 0 || (fields[i].kind & DKIND_MASK) != DKIND_SPECIAL) { + /* Need to read in a tag */ + if (r = ReadByte(X, tag)) return r; + } + + /* Simple error recovery - if we encounter a 0, it can never be + * a valid tag. If TPFLAG_SKIP is set, we can skip over any + * such null bytes, and process whatever tag we find beyond. + * In addition, if TPFLAG_RSKIP is set, then the next time + * we encounter a 0, try skipping backwards. That seems to + * work much of the time. + */ + if (!*tag && pi->shift_offset && (pi->flags & TPFLAG_RSKIP)) { + u_int64 where, tmp64a, tmp64b; + char buf1[21], buf2[21], buf3[21]; + char *p1, *p2, *p3; + + if (r = xftell(X, &tmp64a)) return r; + sub64_32(where, tmp64a, pi->shift_offset + 1); + if (r = xfseek(X, &where)) return r; + if (pi->cb_error){ + (pi->cb_error)(DSERR_FMT, 0, pi->err_refcon, + "Inserted %d bytes before offset %d", + pi->shift_offset, decimate_int64(&where, 0)); + add64_32(tmp64a, pi->shift_start, pi->shift_offset); + p1 = decimate_int64(&tmp64a, buf1); + sub64_64(tmp64b, where, tmp64a); + p2 = decimate_int64(&tmp64b, buf2); + p3 = decimate_int64(&pi->shift_start, buf3); + (pi->cb_error)(DSERR_FMT, 0, pi->err_refcon, + ">>> SHIFT start=%s length=%s target=%s", + p1, p2, p3); + } + pi->shift_offset = 0; + if (r = ReadByte(X, tag)) return r; + } + if (!*tag && (pi->flags & TPFLAG_SKIP)) { + int count = 0; + u_int64 where, tmp64a; + + if (r = xftell(X, &where)) return r; + + while (!*tag) { + if (r = ReadByte(X, tag)) return r; + count++; + } + pi->shift_offset += count; + cp64(pi->shift_start, where); + if (pi->cb_error) { + sub64_32(tmp64a, where, 1); + (pi->cb_error)(DSERR_FMT, 0, pi->err_refcon, + "Skipped %d bytes at offset %s", + count, decimate_int64(&tmp64a, 0)); + } + } + + for (i = 0; fields[i].tag && fields[i].tag != *tag; i++); + if (!fields[i].tag) return 0; + + switch (fields[i].kind & DKIND_MASK) { + case DKIND_NOOP: + if (fields[i].func) { + r = (fields[i].func)(X, 0, fields+i, 0, pi, g_refcon, l_refcon); + if (r) return r; + } + break; + + case DKIND_BYTE: + if (r = ReadByte(X, &val8)) return r; + if (fields[i].func) { + r = (fields[i].func)(X, 0, fields+i, val8, pi, g_refcon, l_refcon); + if (r) return r; + } + break; + + case DKIND_INT16: + if (r = ReadInt16(X, &val16)) return r; + if (fields[i].func) { + r = (fields[i].func)(X, 0, fields+i, val16, pi, g_refcon, l_refcon); + if (r) return r; + } + break; + + case DKIND_INT32: + if (r = ReadInt32(X, &val)) return r; + if (fields[i].func) { + r = (fields[i].func)(X, 0, fields+i, val, pi, g_refcon, l_refcon); + if (r) return r; + } + break; + + case DKIND_STRING: + if (r = ReadString(X, &strval)) return r; + if (fields[i].func) { + r = (fields[i].func)(X, strval, fields+i, 0, pi, g_refcon, l_refcon); + if (r != DSERR_KEEP) free(strval); + if (r && r != DSERR_KEEP) return r; + } else free(strval); + break; + + case DKIND_SPECIAL: + if (fields[i].func) { + r = (fields[i].func)(X, tag, fields+i, 0, pi, g_refcon, l_refcon); + if (r) return r; + } else i = -1; + } + } +} diff --git a/src/tests/parsevnode.c b/src/tests/parsevnode.c new file mode 100644 index 0000000000..09f86ce1da --- /dev/null +++ b/src/tests/parsevnode.c @@ -0,0 +1,427 @@ +/* + * CMUCS AFStools + * dumpscan - routines for scanning and manipulating AFS volume dumps + * + * Copyright (c) 1998 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software_Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ + +/* parsevnode.c - Parse a VNode */ + +#include +#include +#include + +#include "dumpscan.h" +#include "dumpscan_errs.h" +#include "dumpfmt.h" +#include "internal.h" + +#include +#include + +static afs_uint32 LastGoodVNode = 0; +static afs_uint32 store_vnode(XFILE *, unsigned char *, tagged_field *, afs_uint32, + tag_parse_info *, void *, void *); +static afs_uint32 parse_acl (XFILE *, unsigned char *, tagged_field *, afs_uint32, + tag_parse_info *, void *, void *); +static afs_uint32 parse_vdata(XFILE *, unsigned char *, tagged_field *, afs_uint32, + tag_parse_info *, void *, void *); + +/** Field list for vnodes **/ +static tagged_field vnode_fields[] = { + { VTAG_TYPE, DKIND_BYTE, " VNode type: ", store_vnode, 0, 0 }, + { VTAG_NLINKS, DKIND_INT16, " Link count: ", store_vnode, 0, 0 }, + { VTAG_DVERS, DKIND_INT32, " Version: ", store_vnode, 0, 0 }, + { VTAG_CLIENT_DATE, DKIND_TIME, " Server Date: ", store_vnode, 0, 0 }, + { VTAG_AUTHOR, DKIND_INT32, " Author: ", store_vnode, 0, 0 }, + { VTAG_OWNER, DKIND_INT32, " Owner: ", store_vnode, 0, 0 }, + { VTAG_GROUP, DKIND_INT32, " Group: ", store_vnode, 0, 0 }, + { VTAG_MODE, DKIND_INT16, " UNIX mode: ", store_vnode, 0, 0 }, + { VTAG_PARENT, DKIND_INT32, " Parent: ", store_vnode, 0, 0 }, + { VTAG_SERVER_DATE, DKIND_TIME, " Client Date: ", store_vnode, 0, 0 }, + { VTAG_ACL, DKIND_SPECIAL, " xxxxxxxx ACL: ", parse_acl, 0, 0 }, + { VTAG_DATA, DKIND_SPECIAL, " Contents: ", parse_vdata, 0, 0 }, + { 0,0,0,0,0,0 }}; + + +static afs_uint32 resync_vnode(XFILE *X, dump_parser *p, afs_vnode *v, + int start, int limit) +{ + u_int64 where, expected_where; + afs_uint32 r; + int i; + + if (r = xftell(X, &expected_where)) return r; + cp64(where, expected_where); + + r = match_next_vnode(X, p, &where, v->vnode); + if (r && r != DSERR_FMT) return r; + if (r) for (i = -start; i < limit; i++) { + add64_32(where, expected_where, i); + r = match_next_vnode(X, p, &where, v->vnode); + if (!r) break; + if (r != DSERR_FMT) return r; + } + if (r) { + if (p->cb_error) + (p->cb_error)(r, 1, p->err_refcon, + "Unable to resync after vnode %d [%s = 0x%s]", + v->vnode, decimate_int64(&expected_where, 0), + hexify_int64(&expected_where, 0)); + return r; + } + if (ne64(where, expected_where) && p->cb_error) { + (p->cb_error)(DSERR_FMT, 0, p->err_refcon, + "Vnode after %d not in expected location", + v->vnode); + (p->cb_error)(DSERR_FMT, 0, p->err_refcon, "Expected location: %s = 0x%s", + decimate_int64(&expected_where, 0), + hexify_int64(&expected_where, 0)); + (p->cb_error)(DSERR_FMT, 0, p->err_refcon, "Actual location: %s = 0x%s", + decimate_int64(&where, 0), hexify_int64(&where, 0)); + } + return xfseek(X, &where); +} + + +/* Parse a VNode, including any tagged attributes and data, and call the + * appropriate callback, if one is defined. + */ +afs_uint32 parse_vnode(XFILE *X, unsigned char *tag, tagged_field *field, + afs_uint32 value, tag_parse_info *pi, + void *g_refcon, void *l_refcon) +{ + dump_parser *p = (dump_parser *)g_refcon; + afs_uint32 (*cb)(afs_vnode *, XFILE *, void *); + u_int64 where, offset2k; + afs_vnode v; + afs_uint32 r; + + + if (r = xftell(X, &where)) return r; + memset(&v, 0, sizeof(v)); + sub64_32(v.offset, where, 1); + if (r = ReadInt32(X, &v.vnode)) return r; + if (r = ReadInt32(X, &v.vuniq)) return r; + + mk64(offset2k, 0, 2048); + if (!LastGoodVNode + || ((p->flags & DSFLAG_SEEK) && v.vnode == 1 + && lt64(v.offset, offset2k))) + LastGoodVNode = -1; + + if (p->print_flags & DSPRINT_ITEM) { + printf("%s %d/%d [%s = 0x%s]\n", field->label, v.vnode, v.vuniq, + decimate_int64(&where, 0), hexify_int64(&where, 0)); + } + + r = ParseTaggedData(X, vnode_fields, tag, pi, g_refcon, (void *)&v); + + /* Try to resync, if requested */ + if (!r && (p->repair_flags & DSFIX_VFSYNC)) { + afs_uint32 drop; + u_int64 xwhere; + + if (r = xftell(X, &where)) return r; + sub64_32(xwhere, where, 1); + + /* Are we at the start of a valid vnode (or dump end)? */ + r = match_next_vnode(X, p, &xwhere, v.vnode); + if (r && r != DSERR_FMT) return r; + if (r) { /* Nope. */ + /* Was _this_ a valid vnode? If so, we can keep it and search for + * the next one. Otherwise, we throw it out, and start the search + * at the starting point of this vnode. + */ + drop = r = match_next_vnode(X, p, &v.offset, LastGoodVNode); + if (r && r != DSERR_FMT) return r; + if (!r) { + add64_32(where, v.offset, 1); + if (r = xfseek(X, &v.offset)) return r; + } else { + if (r = xfseek(X, &xwhere)) return r; + } + if (r = resync_vnode(X, p, &v, 0, 1024)) return r; + if (r = ReadByte(X, tag)) return r; + if (drop) { + if (p->cb_error) + (p->cb_error)(DSERR_FMT, 0, p->err_refcon, + "Dropping vnode %d", v.vnode); + return 0; + } + } else { + if (r = xfseek(X, &where)) return r; + } + } + LastGoodVNode = v.vnode; + + if (!r) { + if (v.field_mask & F_VNODE_TYPE) + switch (v.type) { + case vFile: cb = p->cb_vnode_file; break; + case vDirectory: cb = p->cb_vnode_dir; break; + case vSymlink: cb = p->cb_vnode_link; break; + default: cb = p->cb_vnode_wierd; break; + } + else cb = p->cb_vnode_empty; + if (cb) { + u_int64 where; + + if (r = xftell(X, &where)) return r; + r = (cb)(&v, X, p->refcon); + if (p->flags & DSFLAG_SEEK) { + if (!r) r = xfseek(X, &where); + else xfseek(X, &where); + } + } + } + return r; +} + + +/* Store data in a vnode */ +static afs_uint32 store_vnode(XFILE *X, unsigned char *tag, tagged_field *field, + afs_uint32 value, tag_parse_info *pi, + void *g_refcon, void *l_refcon) +{ + dump_parser *p = (dump_parser *)g_refcon; + afs_vnode *v = (afs_vnode *)l_refcon; + time_t when; + afs_uint32 r = 0; + + switch (field->tag) { + case VTAG_TYPE: + v->field_mask |= F_VNODE_TYPE; + v->type = value; + if (p->print_flags & DSPRINT_VNODE) { + switch (value) { + case vFile: + printf("%sFile (%d)\n", field->label, value); + break; + case vDirectory: + printf("%sDirectory (%d)\n", field->label, value); + break; + case vSymlink: + printf("%sSymbolic Link (%d)\n", field->label, value); + break; + default: + printf("%s??? (%d)\n", field->label, value); + } + return r; + } + break; + + case VTAG_NLINKS: + v->field_mask |= F_VNODE_NLINKS; + v->nlinks = value; + break; + + case VTAG_DVERS: + v->field_mask |= F_VNODE_DVERS; + v->datavers = value; + break; + + case VTAG_CLIENT_DATE: + v->field_mask |= F_VNODE_CDATE; + v->client_date = value; + break; + + case VTAG_SERVER_DATE: + v->field_mask |= F_VNODE_SDATE; + v->server_date = value; + break; + + case VTAG_AUTHOR: + v->field_mask |= F_VNODE_AUTHOR; + v->author = value; + break; + + case VTAG_OWNER: + v->field_mask |= F_VNODE_OWNER; + v->owner = value; + break; + + case VTAG_GROUP: + v->field_mask |= F_VNODE_GROUP; + v->group = value; + break; + + case VTAG_MODE: + v->field_mask |= F_VNODE_MODE; + v->mode = value; + break; + + case VTAG_PARENT: + v->field_mask |= F_VNODE_PARENT; + v->parent = value; + break; + } + + if (p->print_flags & DSPRINT_VNODE) + switch (field->kind) { + case DKIND_BYTE: + case DKIND_INT16: + case DKIND_INT32: printf("%s%d\n", field->label, value); break; + case DKIND_HEX8: printf("%s0x%02x\n", field->label, value); break; + case DKIND_HEX16: printf("%s0x%04x\n", field->label, value); break; + case DKIND_HEX32: printf("%s0x%08x\n", field->label, value); break; + case DKIND_CHAR: printf("%s%c\n", field->label, value); break; + case DKIND_STRING: printf("%s%s\n", field->label, tag); break; + case DKIND_FLAG: + printf("%s%s\n", field->label, value ? "true" : "false"); + break; + case DKIND_TIME: + when = value; + printf("%s%s", field->label, ctime(&when)); + break; + } + return r; +} + + +static char *rights2str(afs_uint32 rights) +{ + static char str[16]; + char *p = str; + + if (rights & PRSFS_READ) *p++ = 'r'; + if (rights & PRSFS_LOOKUP) *p++ = 'l'; + if (rights & PRSFS_INSERT) *p++ = 'i'; + if (rights & PRSFS_DELETE) *p++ = 'd'; + if (rights & PRSFS_WRITE) *p++ = 'w'; + if (rights & PRSFS_LOCK) *p++ = 'k'; + if (rights & PRSFS_ADMINISTER) *p++ = 'a'; + if (rights & PRSFS_USR0) *p++ = 'A'; + if (rights & PRSFS_USR1) *p++ = 'B'; + if (rights & PRSFS_USR2) *p++ = 'C'; + if (rights & PRSFS_USR3) *p++ = 'D'; + if (rights & PRSFS_USR4) *p++ = 'E'; + if (rights & PRSFS_USR5) *p++ = 'F'; + if (rights & PRSFS_USR6) *p++ = 'G'; + if (rights & PRSFS_USR7) *p++ = 'H'; + + *p = 0; + if (!str[0]) strcpy(str, "none"); + return str; +} + + +/* Parse and store the ACL data from a directory vnode */ +static afs_uint32 parse_acl(XFILE *X, unsigned char *tag, tagged_field *field, + afs_uint32 value, tag_parse_info *pi, + void *g_refcon, void *l_refcon) +{ + struct acl_accessList *acl; + dump_parser *p = (dump_parser *)g_refcon; + afs_vnode *v = (afs_vnode *)l_refcon; + afs_uint32 r, i, n; + + if (r = xfread(X, v->acl, SIZEOF_LARGEDISKVNODE - SIZEOF_SMALLDISKVNODE)) + return r; + + v->field_mask |= F_VNODE_ACL; + if (p->print_flags & DSPRINT_ACL) { + acl = (struct acl_accessList *)(v->acl); + n = ntohl(acl->positive); + if (n) { + printf("Positive ACL: %d entries\n", n); + for (i = 0; i < n; i++) + printf(" %9d %s\n", + ntohl(acl->entries[i].id), + rights2str(acl->entries[i].rights)); + } + n = ntohl(acl->negative); + if (n) { + printf("Positive ACL: %d entries\n", n); + for (i = ntohl(acl->positive); i < ntohl(acl->total); i++) + printf(" %9d %s\n", + ntohl(acl->entries[i].id), + rights2str(acl->entries[i].rights)); + } + } + return ReadByte(X, tag); +} + + +/* Parse or skip over the vnode data */ +static afs_uint32 parse_vdata(XFILE *X, unsigned char *tag, tagged_field *field, + afs_uint32 value, tag_parse_info *pi, + void *g_refcon, void *l_refcon) +{ + dump_parser *p = (dump_parser *)g_refcon; + afs_vnode *v = (afs_vnode *)l_refcon; + static char *symlink_buf = 0; + static int symlink_size = 0; + afs_uint32 r; + + if (r = ReadInt32(X, &v->size)) return r; + v->field_mask |= F_VNODE_SIZE; + + if (v->size) { + v->field_mask |= F_VNODE_DATA; + if (r = xftell(X, &v->d_offset)) return r; + if (p->print_flags & DSPRINT_VNODE) + printf("%s%d (0x%08x) bytes at %s (0x%s)\n", field->label, + v->size, v->size, decimate_int64(&v->d_offset, 0), + hexify_int64(&v->d_offset, 0)); + + switch (v->type) { + case vSymlink: + if (v->size > symlink_size) { + if (symlink_buf) symlink_buf = (char *)realloc(symlink_buf, v->size + 1); + else symlink_buf = (char *)malloc(v->size + 1); + symlink_size = symlink_buf ? v->size : 0; + } + if (symlink_buf) { + if (r = xfread(X, symlink_buf, v->size)) return r; + symlink_buf[v->size] = 0; + if (p->print_flags & DSPRINT_VNODE) + printf("Target: %s\n", symlink_buf); + } else { + /* Call the callback here, because it's non-fatal */ + if (p->cb_error) + (p->cb_error)(ENOMEM, 0, p->err_refcon, + "Out of memory reading symlink"); + if (r = xfskip(X, v->size)) return r; + } + break; + + case vDirectory: + if (p->cb_dirent || (p->print_flags & DSPRINT_DIR)) { + if (r = parse_directory(X, p, v, v->size, 0)) return r; + break; + } + + default: + if (r = xfskip(X, v->size)) return r; + } + } else if (p->print_flags & DSPRINT_VNODE) { + printf("%sEmpty\n", field->label); + } + if (p->repair_flags & DSFIX_VDSYNC) { + r = resync_vnode(X, p, v, 10, 15); + if (r) return r; + } + return ReadByte(X, tag); +} diff --git a/src/tests/parsevol.c b/src/tests/parsevol.c new file mode 100644 index 0000000000..86143dd8ed --- /dev/null +++ b/src/tests/parsevol.c @@ -0,0 +1,302 @@ +/* + * CMUCS AFStools + * dumpscan - routines for scanning and manipulating AFS volume dumps + * + * Copyright (c) 1998 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software_Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ + +/* parsevol.c - Parse a volume header */ + +#include "dumpscan.h" +#include "dumpscan_errs.h" +#include "dumpfmt.h" + +static afs_uint32 store_volhdr (XFILE *, unsigned char *, tagged_field *, + afs_uint32, tag_parse_info *, void *, void *); +static afs_uint32 parse_weekuse (XFILE *, unsigned char *, tagged_field *, + afs_uint32, tag_parse_info *, void *, void *); + +/** Field list for volume headers **/ +static tagged_field volhdr_fields[] = { + { VHTAG_VOLID, DKIND_INT32, " Volume ID: ", store_volhdr, 0, 0 }, + { VHTAG_VERS, DKIND_INT32, " Version: ", store_volhdr, 0, 0 }, + { VHTAG_VOLNAME, DKIND_STRING, " Volume name: ", store_volhdr, 0, 0 }, + { VHTAG_INSERV, DKIND_FLAG, " In service? ", store_volhdr, 0, 0 }, + { VHTAG_BLESSED, DKIND_FLAG, " Blessed? ", store_volhdr, 0, 0 }, + { VHTAG_VUNIQ, DKIND_INT32, " Uniquifier: ", store_volhdr, 0, 0 }, + { VHTAG_TYPE, DKIND_BYTE, " Type: ", store_volhdr, 0, 0 }, + { VHTAG_PARENT, DKIND_INT32, " Parent ID: ", store_volhdr, 0, 0 }, + { VHTAG_CLONE, DKIND_INT32, " Clone ID: ", store_volhdr, 0, 0 }, + { VHTAG_MAXQUOTA, DKIND_INT32, " Max quota: ", store_volhdr, 0, 0 }, + { VHTAG_MINQUOTA, DKIND_INT32, " Min quota: ", store_volhdr, 0, 0 }, + { VHTAG_DISKUSED, DKIND_INT32, " Disk used: ", store_volhdr, 0, 0 }, + { VHTAG_FILECNT, DKIND_INT32, " File count: ", store_volhdr, 0, 0 }, + { VHTAG_ACCOUNT, DKIND_INT32, " Account: ", store_volhdr, 0, 0 }, + { VHTAG_OWNER, DKIND_INT32, " Owner: ", store_volhdr, 0, 0 }, + { VHTAG_CREAT, DKIND_TIME, " Created: ", store_volhdr, 0, 0 }, + { VHTAG_ACCESS, DKIND_TIME, " Accessed: ", store_volhdr, 0, 0 }, + { VHTAG_UPDATE, DKIND_TIME, " Updated: ", store_volhdr, 0, 0 }, + { VHTAG_EXPIRE, DKIND_TIME, " Expires: ", store_volhdr, 0, 0 }, + { VHTAG_BACKUP, DKIND_TIME, " Backed up: ", store_volhdr, 0, 0 }, + { VHTAG_OFFLINE, DKIND_STRING, " Offine Msg: ", store_volhdr, 0, 0 }, + { VHTAG_MOTD, DKIND_STRING, " MOTD: ", store_volhdr, 0, 0 }, + { VHTAG_WEEKUSE, DKIND_SPECIAL, " Weekuse: ", parse_weekuse, 0, 0 }, + { VHTAG_DUDATE, DKIND_TIME, " Dayuse Date: ", store_volhdr, 0, 0 }, + { VHTAG_DAYUSE, DKIND_INT32, " Daily usage: ", store_volhdr, 0, 0 }, + { 0,0,0,0,0,0 }}; + + +/* Parse a volume header, including any tagged attributes, and call the + * volume-header callback, if one is defined. + */ +afs_uint32 parse_volhdr(XFILE *X, unsigned char *tag, tagged_field *field, + afs_uint32 value, tag_parse_info *pi, + void *g_refcon, void *l_refcon) +{ + dump_parser *p = (dump_parser *)g_refcon; + afs_vol_header hdr; + u_int64 where; + afs_uint32 r; + + memset(&hdr, 0, sizeof(hdr)); + if (r = xftell(X, &where)) return r; + sub64_32(hdr.offset, where, 1); + if (p->print_flags & DSPRINT_VOLHDR) + printf("%s [%s = 0x%s]\n", field->label, + decimate_int64(&hdr.offset, 0), hexify_int64(&hdr.offset, 0)); + + r = ParseTaggedData(X, volhdr_fields, tag, pi, g_refcon, (void *)&hdr); + + if (!r && p->cb_volhdr) { + if (r = xftell(X, &where)) return r; + r = (p->cb_volhdr)(&hdr, X, p->refcon); + if (p->flags & DSFLAG_SEEK) { + if (!r) r = xfseek(X, &where); + else xfseek(X, &where); + } + } + if (hdr.field_mask & F_VOLHDR_VOLUNIQ) + p->vol_uniquifier = hdr.voluniq; + if (hdr.field_mask & F_VOLHDR_VOLNAME) + free(hdr.volname); + if (hdr.field_mask & F_VOLHDR_OFFLINE_MSG) + free(hdr.offline_msg); + if (hdr.field_mask & F_VOLHDR_MOTD) + free(hdr.motd_msg); + return r; +} + + +/* Store data in a volume header */ +static afs_uint32 store_volhdr(XFILE *X, unsigned char *tag, tagged_field *field, + afs_uint32 value, tag_parse_info *pi, + void *g_refcon, void *l_refcon) +{ + dump_parser *p = (dump_parser *)g_refcon; + afs_vol_header *hdr = (afs_vol_header *)l_refcon; + time_t when; + afs_uint32 r = 0; + + switch (field->tag) { + case VHTAG_VOLID: + hdr->field_mask |= F_VOLHDR_VOLID; + hdr->volid = value; + break; + + case VHTAG_VERS: + hdr->field_mask |= F_VOLHDR_VOLVERS; + hdr->volvers = value; + break; + + case VHTAG_VOLNAME: + if (tag && tag[0]) { + hdr->field_mask |= F_VOLHDR_VOLNAME; + hdr->volname = tag; + r = DSERR_KEEP; + } + break; + + case VHTAG_INSERV: + hdr->field_mask |= F_VOLHDR_INSERV; + hdr->flag_inservice = value; + break; + + case VHTAG_BLESSED: + hdr->field_mask |= F_VOLHDR_BLESSED; + hdr->flag_blessed = value; + break; + + case VHTAG_VUNIQ: + hdr->field_mask |= F_VOLHDR_VOLUNIQ; + hdr->voluniq = value; + break; + + case VHTAG_TYPE: + hdr->field_mask |= F_VOLHDR_VOLTYPE; + hdr->voltype = value; + break; + + case VHTAG_PARENT: + hdr->field_mask |= F_VOLHDR_PARENT; + hdr->parent_volid = value; + break; + + case VHTAG_CLONE: + hdr->field_mask |= F_VOLHDR_CLONE; + hdr->clone_volid = value; + break; + + case VHTAG_MAXQUOTA: + hdr->field_mask |= F_VOLHDR_MAXQ; + hdr->maxquota = value; + break; + + case VHTAG_MINQUOTA: + hdr->field_mask |= F_VOLHDR_MINQ; + hdr->minquota = value; + break; + + case VHTAG_DISKUSED: + hdr->field_mask |= F_VOLHDR_DISKUSED; + hdr->diskused = value; + break; + + case VHTAG_FILECNT: + hdr->field_mask |= F_VOLHDR_NFILES; + hdr->nfiles = value; + break; + + case VHTAG_ACCOUNT: + hdr->field_mask |= F_VOLHDR_ACCOUNT; + hdr->account_no = value; + break; + + case VHTAG_OWNER: + hdr->field_mask |= F_VOLHDR_OWNER; + hdr->owner = value; + break; + + case VHTAG_CREAT: + hdr->field_mask |= F_VOLHDR_CREATE_DATE; + hdr->create_date = value; + break; + + case VHTAG_ACCESS: + hdr->field_mask |= F_VOLHDR_ACCESS_DATE; + hdr->access_date = value; + break; + + case VHTAG_UPDATE: + hdr->field_mask |= F_VOLHDR_UPDATE_DATE; + hdr->update_date = value; + break; + + case VHTAG_EXPIRE: + hdr->field_mask |= F_VOLHDR_EXPIRE_DATE; + hdr->expire_date = value; + break; + + case VHTAG_BACKUP: + hdr->field_mask |= F_VOLHDR_BACKUP_DATE; + hdr->backup_date = value; + break; + + case VHTAG_OFFLINE: + if (tag && tag[0]) { + hdr->field_mask |= F_VOLHDR_OFFLINE_MSG; + hdr->offline_msg = tag; + r = DSERR_KEEP; + } + break; + + case VHTAG_MOTD: + if (tag && tag[0]) { + hdr->field_mask |= F_VOLHDR_MOTD; + hdr->motd_msg = tag; + r = DSERR_KEEP; + } + break; + + case VHTAG_DUDATE: + hdr->field_mask |= F_VOLHDR_DAYUSE_DATE; + hdr->dayuse_date = value; + break; + + case VHTAG_DAYUSE: + hdr->field_mask |= F_VOLHDR_DAYUSE; + hdr->dayuse = value; + break; + } + + if (p->print_flags & DSPRINT_VOLHDR) + switch (field->kind) { + case DKIND_BYTE: + case DKIND_INT16: + case DKIND_INT32: printf("%s%d\n", field->label, value); break; + case DKIND_HEX8: printf("%s0x%02x\n", field->label, value); break; + case DKIND_HEX16: printf("%s0x%04x\n", field->label, value); break; + case DKIND_HEX32: printf("%s0x%08x\n", field->label, value); break; + case DKIND_CHAR: printf("%s%c\n", field->label, value); break; + case DKIND_STRING: printf("%s%s\n", field->label, tag); break; + case DKIND_FLAG: + printf("%s%s\n", field->label, value ? "true" : "false"); + break; + case DKIND_TIME: + when = value; + printf("%s%s", field->label, ctime(&when)); + break; + } + return r; +} + + +/* Parse and store the week use data from a volume header */ +static afs_uint32 parse_weekuse(XFILE *X, unsigned char *tag, tagged_field *field, + afs_uint32 value, tag_parse_info *pi, + void *g_refcon, void *l_refcon) +{ + dump_parser *p = (dump_parser *)g_refcon; + afs_vol_header *hdr = (afs_vol_header *)l_refcon; + afs_uint16 count; + afs_uint32 r; + unsigned int i; + + if (r = ReadInt16(X, &count)) return r; + if (count != 7) { + if (p->cb_error) + (p->cb_error)(DSERR_FMT, 1, p->err_refcon, + "Incorrect array count (%d) in weekuse data", count); + return DSERR_FMT; + } + for (i = 0; i < count; i++) + if (r = ReadInt32(X, hdr->weekuse + i)) return r; + hdr->field_mask |= F_VOLHDR_WEEKUSE; + if (p->print_flags & DSPRINT_VOLHDR) { + printf("%s%10d %10d %10d %10d\n", field->label, + hdr->weekuse[0], hdr->weekuse[1], hdr->weekuse[2], hdr->weekuse[3]); + printf("%s%10d %10d %10d\n", field->label, + hdr->weekuse[4], hdr->weekuse[5], hdr->weekuse[6]); + } + return ReadByte(X, tag); +} diff --git a/src/tests/pathname.c b/src/tests/pathname.c new file mode 100644 index 0000000000..ee3af28f16 --- /dev/null +++ b/src/tests/pathname.c @@ -0,0 +1,376 @@ +/* + * CMUCS AFStools + * dumpscan - routines for scanning and manipulating AFS volume dumps + * + * Copyright (c) 1998 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software_Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ + +/* pathname.c - Pathname lookup and traversal */ + +#include +#include + +#include "dumpscan.h" +#include "dumpscan_errs.h" + +/* Hash function for a vnode */ +#define BUCKET_SIZE 32 +#define vnode_hash(phi,vnode) ((vnode) & ((1 << (phi)->hash_size) - 1)) + + +static vhash_ent *get_vhash_ent(path_hashinfo *phi, afs_uint32 vnode, int make) +{ + int key = vnode_hash(phi, vnode); + vhash_ent *vhe; + + for (vhe = phi->hash_table[key]; + vhe && vhe->vnode != vnode; + vhe = vhe->next); + if (make && !vhe) { + vhe = (vhash_ent *)malloc(sizeof(vhash_ent)); + if (vhe) { + memset(vhe, 0, sizeof(vhash_ent)); + vhe->vnode = vnode; + vhe->next = phi->hash_table[key]; + phi->hash_table[key] = vhe; + } + } + return vhe; +} + + +static afs_uint32 volhdr_cb(afs_vol_header *hdr, XFILE *X, void *refcon) +{ + path_hashinfo *phi = (path_hashinfo *)refcon; + int nfiles, hsize; + + if (hdr->field_mask & F_VOLHDR_NFILES) { + nfiles = phi->n_vnodes = hdr->nfiles; + for (phi->hash_size = 1; + nfiles > BUCKET_SIZE; + phi->hash_size++, nfiles >>= 1); + hsize = (1 << phi->hash_size); + phi->hash_table = (vhash_ent **)malloc(hsize * sizeof(vhash_ent *)); + if (!phi->hash_table) return ENOMEM; + memset(phi->hash_table, 0, hsize * sizeof(vhash_ent *)); + return 0; + } else { + if (phi->p->cb_error) + (phi->p->cb_error)(DSERR_FMT, 1, phi->p->err_refcon, + "File count missing from volume header"); + return DSERR_FMT; + } +} + + +static afs_uint32 vnode_keep(afs_vnode *v, XFILE *X, void *refcon) +{ + path_hashinfo *phi = (path_hashinfo *)refcon; + vhash_ent *vhe; + + if (!phi->hash_table) { + if (phi->p->cb_error) + (phi->p->cb_error)(DSERR_FMT, 1, phi->p->refcon, + "No volume header in dump???"); + return DSERR_FMT; + } + vhe = get_vhash_ent(phi, v->vnode, 1); + if (!vhe) return ENOMEM; + cp64(vhe->v_offset, v->offset); + if (v->field_mask & F_VNODE_PARENT) + vhe->parent = v->parent; + if (v->field_mask & F_VNODE_DATA) { + cp64(vhe->d_offset, v->d_offset); + vhe->d_size = v->size; + } + if ((v->field_mask & F_VNODE_TYPE) && v->type == vDirectory) + phi->n_dirs++; + else + phi->n_files++; + return 0; +} + + +static afs_uint32 vnode_stop(afs_vnode *v, XFILE *X, void *refcon) +{ + path_hashinfo *phi = (path_hashinfo *)refcon; + int r; + + /* If the file is seekable, try to position so we can pick up later... */ + if (phi->p->flags && DSFLAG_SEEK) + if (r = xfseek(X, &v->offset)) return r; + return DSERR_DONE; +} + + +static afs_uint32 dirent_cb(afs_vnode *v, afs_dir_entry *de, + XFILE *X, void *refcon) +{ + path_hashinfo *phi = (path_hashinfo *)refcon; + vhash_ent *vhe; + + if (!phi->hash_table) { + if (phi->p->cb_error) + (phi->p->cb_error)(DSERR_FMT, 1, phi->p->refcon, + "No volume header in dump???"); + return DSERR_FMT; + } + if (!strcmp(de->name, ".") || !strcmp(de->name, "..")) return 0; + vhe = get_vhash_ent(phi, de->vnode, 1); + if (!vhe) return ENOMEM; + vhe->parent = v->vnode; + return 0; +} + + +/* Prescan the vnodes in a dump file, collecting information that will + * be useful in generating and following pathnames. + */ +afs_uint32 Path_PreScan(XFILE *X, path_hashinfo *phi, int full) +{ + dump_parser my_p, *p = phi->p; + int r; + + memset(phi, 0, sizeof(path_hashinfo)); + phi->p = p; + memset(&my_p, 0, sizeof(my_p)); + my_p.refcon = (void *)phi; + my_p.cb_volhdr = volhdr_cb; + my_p.cb_vnode_dir = vnode_keep; + if (full) { + my_p.cb_vnode_file = vnode_keep; + my_p.cb_vnode_link = vnode_keep; + my_p.cb_vnode_empty = vnode_keep; + my_p.cb_vnode_wierd = vnode_keep; + } else { + my_p.cb_vnode_file = vnode_stop; + my_p.cb_vnode_link = vnode_stop; + my_p.cb_vnode_empty = vnode_stop; + my_p.cb_vnode_wierd = vnode_stop; + } + my_p.err_refcon = p->err_refcon; + my_p.cb_error = p->cb_error; + my_p.cb_dirent = dirent_cb; + my_p.flags = p->flags; + my_p.print_flags = p->print_flags; + my_p.repair_flags = p->repair_flags; + + return ParseDumpFile(X, &my_p); +} + + +/* Free the hash table in a path_hashinfo */ +void Path_FreeHashTable(path_hashinfo *phi) +{ + int i, size; + vhash_ent *vhe, *next_vhe; + + if (phi->hash_table) { + size = (1 << phi->hash_size); + for (i = 0; i < size; i++) + for (vhe = phi->hash_table[i]; vhe; vhe = next_vhe) { + next_vhe = vhe->next; + free(vhe); + } + free(phi->hash_table); + } +} + + +/* Follow a pathname to the vnode it represents */ +afs_uint32 Path_Follow(XFILE *X, path_hashinfo *phi, + char *path, vhash_ent *his_vhe) +{ + vhash_ent *vhe; + char *name; + afs_uint32 r, vnum = 1; + + if (*path == '/') path++; + name = strtok(path, "/"); + + for (name = strtok(path, "/"); name; name = strtok(0, "/")) { + if (!(vnum & 1)) { + if (phi->p->cb_error) + (phi->p->cb_error)(ENOTDIR, 1, phi->p->err_refcon, + "Not a directory vnode"); + return ENOTDIR; + } + vhe = get_vhash_ent(phi, vnum, 0); + if (!vhe) { + if (phi->p->cb_error) + (phi->p->cb_error)(DSERR_FMT, 1, phi->p->err_refcon, + "Vnode %d not found in hash table", vnum); + return DSERR_FMT; + } + if (zero64(vhe->d_offset)) { + if (phi->p->cb_error) + (phi->p->cb_error)(DSERR_FMT, 1, phi->p->err_refcon, + "Directory vnode %d is incomplete", vnum); + return DSERR_FMT; + } + if (r = xfseek(X, &vhe->d_offset)) { + if (phi->p->cb_error) + (phi->p->cb_error)(r, 1, phi->p->err_refcon, + "Unable to seek to directory %d", vnum); + return r; + } + vnum = 0; + r = DirectoryLookup(X, phi->p, vhe->d_size, &name, &vnum, 0); + if (r) return r; + if (!vnum) { + if (phi->p->cb_error) + (phi->p->cb_error)(ENOENT, 1, phi->p->err_refcon, + "No such vnode"); + return ENOENT; + } + } + vhe = get_vhash_ent(phi, vnum, 0); + if (!vhe) { + if (phi->p->cb_error) + (phi->p->cb_error)(DSERR_FMT, 1, phi->p->err_refcon, + "Vnode %d not found in hash table", vnum); + return DSERR_FMT; + } + if (his_vhe) *his_vhe = *vhe; + return 0; +} + + +afs_uint32 Path_Build(XFILE *X, path_hashinfo *phi, afs_uint32 vnode, + char **his_path, int fast) +{ + vhash_ent *vhe; + char *name, *path = 0, fastbuf[12]; + char *x, *y; + afs_uint32 parent, r; + int nl, pl = 0; + + if (vnode == 1) { + *his_path = (char *)malloc(2); + if (!his_path) { + if (phi->p->cb_error) + (phi->p->cb_error)(ENOMEM, 1, phi->p->err_refcon, + "No memory for pathname of vnode 1"); + return ENOMEM; + } + strcpy(*his_path, "/"); + return 0; + } + + *his_path = 0; + vhe = get_vhash_ent(phi, vnode, 0); + if (!vhe) { + if (phi->p->cb_error) + (phi->p->cb_error)(DSERR_FMT, 1, phi->p->err_refcon, + "Vnode %d not found in hash table", vnode); + return DSERR_FMT; + } + while (vnode != 1) { + /* Find the parent */ + if (!vhe->parent) { + if (phi->p->cb_error) + (phi->p->cb_error)(DSERR_FMT, 1, phi->p->err_refcon, + "Vnode %d has no parent?", vnode); + if (path) free(path); + return DSERR_FMT; + } + parent = vhe->parent; + vhe = get_vhash_ent(phi, parent, 0); + if (phi->p->print_flags & DSPRINT_DEBUG) + fprintf(stderr, "Searching for vnode %d in parent %d\n", vnode, parent); + if (!vhe) { + if (phi->p->cb_error) + (phi->p->cb_error)(DSERR_FMT, 1, phi->p->err_refcon, + "Vnode %d not found in hash table", parent); + if (path) free(path); + return DSERR_FMT; + } + + if (fast) { + /* Make up a path component from the vnode number */ + sprintf(fastbuf, "%d", vnode); + name = fastbuf; + } else { + /* Do a reverse-lookup in the parent directory */ + if (zero64(vhe->d_offset)) { + if (phi->p->cb_error) + (phi->p->cb_error)(DSERR_FMT, 1, phi->p->err_refcon, + "Directory vnode %d is incomplete", parent); + if (path) free(path); + return DSERR_FMT; + } + if (r = xfseek(X, &vhe->d_offset)) { + if (phi->p->cb_error) + (phi->p->cb_error)(errno, 1, phi->p->err_refcon, + "Unable to seek to directory %d", parent); + if (path) free(path); + return r; + } + + name = 0; + r = DirectoryLookup(X, phi->p, vhe->d_size, &name, &vnode, 0); + if (r) return r; + if (!name) { + if (phi->p->cb_error) + (phi->p->cb_error)(DSERR_FMT, 1, phi->p->err_refcon, + "No entry for vnode %d in directory %d", + vnode, parent); + if (path) free(path); + return ENOENT; + } + } + + nl = strlen(name); + if (path) { + path = (char *)realloc(path, nl + pl + 2); + if (!path) { + if (phi->p->cb_error) + (phi->p->cb_error)(ENOMEM, 1, phi->p->err_refcon, + "No memory for pathname of vnode 1"); + return ENOMEM; + } + x = path + pl; + y = x + nl + 1; + while (x >= path) *y-- = *x--; + path[0] = '/'; + for (x = name, y = path + 1; *x;) *y++ = *x++; + pl += nl + 1; + } else { + path = (char *)malloc(nl + 2); + if (!path) { + if (phi->p->cb_error) + (phi->p->cb_error)(ENOMEM, 1, phi->p->err_refcon, + "No memory for pathname of vnode 1"); + return ENOMEM; + } + path[0] = '/'; + strcpy(path + 1, name); + pl = nl + 1; + } + if (!fast) free(name); + vnode = parent; + } + *his_path = path; + return 0; +} diff --git a/src/tests/primitive.c b/src/tests/primitive.c new file mode 100644 index 0000000000..95dcac2b35 --- /dev/null +++ b/src/tests/primitive.c @@ -0,0 +1,165 @@ +/* + * CMUCS AFStools + * dumpscan - routines for scanning and manipulating AFS volume dumps + * + * Copyright (c) 1998 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software_Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ + +/* primitive.c - Routines for reading and writing low-level things */ + +#include +#include +#include +#include +#include + +#include "dumpscan.h" + +#define BUFSIZE 256 + + +afs_uint32 ReadByte(XFILE *X, unsigned char *val) +{ + return xfread(X, val, 1); +} + +afs_uint32 ReadInt16(XFILE *X, afs_uint16 *val) +{ + afs_uint32 r; + + if (r = xfread(X, val, 2)) return r; + *val = ntohs(*val); + return 0; +} + +afs_uint32 ReadInt32(XFILE *X, afs_uint32 *val) +{ + afs_uint32 r; + + if (r = xfread(X, val, 4)) return r; + *val = ntohl(*val); + return 0; +} + +/* Read in a NUL-terminated string. This method is kind of messy, but + * has the advantage that it reads the data stream only once, doesn't + * read anything extra, and never has to seek on the data stream. + */ +afs_uint32 ReadString(XFILE *X, unsigned char **val) +{ + static unsigned char buf[BUFSIZE]; + unsigned char *result = 0; + afs_uint32 r; + int i, l = 0; + + *val = 0; + for (;;) { + for (i = 0; i < BUFSIZE; i++) { + r = ReadByte(X, buf + i); + if (r) { + if (result) free(result); + return r; + } + if (!buf[i]) break; + } + /* iff we found a null, i < BUFSIZE and buf[i] holds the NUL */ + if (result) result = (unsigned char *)realloc(result, l + i + 1); + else result = (unsigned char *)malloc(i + 1); + if (!result) return ENOMEM; + memcpy(result + l, buf, i); + result[l+i] = 0; + l += i; + if (i < BUFSIZE) break; + } + *val = result; + return 0; +} + + +afs_uint32 WriteByte(XFILE *X, unsigned char val) +{ + return xfwrite(X, &val, 1); +} + +afs_uint32 WriteInt16(XFILE *X, afs_uint16 val) +{ + val = htons(val); + return xfwrite(X, &val, 2); +} + +afs_uint32 WriteInt32(XFILE *X, afs_uint32 val) +{ + val = htonl(val); + return xfwrite(X, &val, 4); +} + +afs_uint32 WriteString(XFILE *X, unsigned char *str) +{ + int len = strlen((char *)str) + 1; + return xfwrite(X, str, len); +} + +afs_uint32 WriteTagByte(XFILE *X, unsigned char tag, unsigned char val) +{ + char buffer[2]; + buffer[0] = tag; + buffer[1] = val; + return xfwrite(X, buffer, 2); +} + +afs_uint32 WriteTagInt16(XFILE *X, unsigned char tag, afs_uint16 val) +{ + char buffer[3]; + buffer[0] = tag; + buffer[1] = (val & 0xff00) >> 8; + buffer[2] = val & 0xff; + return xfwrite(X, buffer, 3); +} + +afs_uint32 WriteTagInt32(XFILE *X, unsigned char tag, afs_uint32 val) +{ + char buffer[5]; + buffer[0] = tag; + buffer[1] = (val & 0xff000000) >> 24; + buffer[2] = (val & 0xff0000) >> 16; + buffer[3] = (val & 0xff00) >> 8; + buffer[4] = val & 0xff; + return xfwrite(X, buffer, 5); +} + +afs_uint32 WriteTagInt32Pair(XFILE *X, unsigned char tag, + afs_uint32 val1, afs_uint32 val2) +{ + char buffer[9]; + buffer[0] = tag; + buffer[1] = (val1 & 0xff000000) >> 24; + buffer[2] = (val1 & 0xff0000) >> 16; + buffer[3] = (val1 & 0xff00) >> 8; + buffer[4] = val1 & 0xff; + buffer[5] = (val2 & 0xff000000) >> 24; + buffer[6] = (val2 & 0xff0000) >> 16; + buffer[7] = (val2 & 0xff00) >> 8; + buffer[8] = val2 & 0xff; + return xfwrite(X, buffer, 9); +} diff --git a/src/tests/repair.c b/src/tests/repair.c new file mode 100644 index 0000000000..ece84b1152 --- /dev/null +++ b/src/tests/repair.c @@ -0,0 +1,366 @@ +/* + * CMUCS AFStools + * dumpscan - routines for scanning and manipulating AFS volume dumps + * + * Copyright (c) 1998 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software_Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ + +/* repair.c - Routines to generate a repaired dump */ + +#include +#include +#include +#include +#include + +#include "dumpscan.h" +#include "dumpscan_errs.h" +#include "dumpfmt.h" + +#include +#include +#include + +XFILE repair_output; +int repair_verbose; +#define RV repair_verbose + + +/* Try to dump a dump header. Generate missing fields, if neccessary */ +afs_uint32 repair_dumphdr_cb(afs_dump_header *hdr, XFILE *X, void *refcon) +{ + afs_uint32 r, field_mask = hdr->field_mask; + char volname[22]; + + if (!(field_mask & F_DUMPHDR_VOLID)) { + if (RV) fprintf(stderr, ">>> DUMP HEADER missing volume ID\n"); + return DSERR_FMT; + } + if (!(field_mask & F_DUMPHDR_VOLNAME)) { + if (RV) { + fprintf(stderr, ">>> DUMP HEADER missing volume name\n"); + fprintf(stderr, ">>> Will use RESTORED.%d\n", hdr->volid); + } + sprintf(volname, "RESTORED.%d", hdr->volid); + hdr->volname = (unsigned char *)malloc(strlen(volname) + 1); + if (!hdr->volname) return ENOMEM; + strcpy(hdr->volname, volname); + hdr->field_mask |= F_DUMPHDR_VOLNAME; + } + if (!(field_mask & F_DUMPHDR_FROM)) { + if (RV) fprintf(stderr, ">>> DUMP HEADER missing from time (using 0)\n"); + hdr->from_date = 0; + hdr->field_mask |= F_DUMPHDR_FROM; + } + if (!(field_mask & F_DUMPHDR_TO)) { + hdr->to_date = time(0); + if (RV) fprintf(stderr, ">>> DUMP HEADER missing from time (using %d)\n", + hdr->to_date); + hdr->field_mask |= F_DUMPHDR_TO; + } + + return DumpDumpHeader(&repair_output, hdr); +} + + +/* Try to dump a volume header. Generate missing fields, if necessary */ +afs_uint32 repair_volhdr_cb(afs_vol_header *hdr, XFILE *X, void *refcon) +{ + afs_uint32 r, field_mask = hdr->field_mask; + char volname[22]; + + if (!(field_mask & F_VOLHDR_VOLID)) { + if (RV) fprintf(stderr, ">>> VOL HEADER missing volume ID\n"); + return DSERR_FMT; + } + if (!(field_mask & F_VOLHDR_VOLVERS)) { + if (RV) fprintf(stderr, ">>> VOL HEADER missing version (using 1)\n"); + hdr->volvers = 1; + hdr->field_mask |= F_VOLHDR_VOLVERS; + } else if (hdr->volvers != 1) { + if (RV) fprintf(stderr, ">>> VOL HEADER bogus version %d (using 1)\n", + hdr->volvers); + hdr->volvers = 1; + } + if (!(field_mask & F_VOLHDR_VOLNAME)) { + if (RV) { + fprintf(stderr, ">>> VOL HEADER missing volume name\n"); + fprintf(stderr, ">>> Will use RESTORED.%d\n", hdr->volid); + } + sprintf(volname, "RESTORED.%d", hdr->volid); + hdr->volname = (unsigned char *)malloc(strlen(volname) + 1); + if (!hdr->volname) return ENOMEM; + strcpy(hdr->volname, volname); + hdr->field_mask |= F_VOLHDR_VOLNAME; + } + if (!(field_mask & F_VOLHDR_INSERV)) { + if (RV) + fprintf(stderr, ">>> VOL HEADER missing in-service flag (using 1)\n"); + hdr->flag_inservice = 1; + hdr->field_mask |= F_VOLHDR_INSERV; + } + if (!(field_mask & F_VOLHDR_BLESSED)) { + if (RV) fprintf(stderr, ">>> VOL HEADER missing blessed flag (using 1)\n"); + hdr->flag_blessed = 1; + hdr->field_mask |= F_VOLHDR_BLESSED; + } + if (!(field_mask & F_VOLHDR_VOLUNIQ)) { + if (RV) fprintf(stderr, ">>> VOL HEADER missing uniquifier (using 1)\n"); + hdr->voluniq = 1; + hdr->field_mask |= F_VOLHDR_VOLUNIQ; + } + if (!(field_mask & F_VOLHDR_VOLTYPE)) { + if (RV) fprintf(stderr, ">>> VOL HEADER missing type (using 0: RW)\n"); + hdr->voltype = 0; + hdr->field_mask |= F_VOLHDR_VOLTYPE; + } else if (hdr->voltype < 0 || hdr->voltype > 2) { + if (RV) fprintf(stderr, ">>> VOL HEADER bogus type %d (using 0: RW)\n", + hdr->voltype); + hdr->voltype = 0; + } + if (!(field_mask & F_VOLHDR_PARENT)) { + if (RV) fprintf(stderr, ">>> VOL HEADER parent (using %d)\n", hdr->volid); + hdr->parent_volid = hdr->volid; + hdr->field_mask |= F_VOLHDR_PARENT; + } + if (!(field_mask & F_VOLHDR_MAXQ)) { + if (field_mask & F_VOLHDR_DISKUSED) hdr->maxquota = hdr->diskused; + else hdr->maxquota = 1; + if (RV) fprintf(stderr, ">>> VOL HEADER missing max quota (using %d)\n", + hdr->maxquota); + hdr->field_mask |= F_VOLHDR_MAXQ; + } + if (!(field_mask & F_VOLHDR_DISKUSED)) { + if (RV) fprintf(stderr, ">>> VOL HEADER missing disk used (using 2048)\n"); + hdr->diskused = 2048; + hdr->field_mask |= F_VOLHDR_DISKUSED; + } + if (!(field_mask & F_VOLHDR_NFILES)) { + if (RV) fprintf(stderr, ">>> VOL HEADER missing file count (using 1)\n"); + hdr->nfiles = 1; + hdr->field_mask |= F_VOLHDR_NFILES; + } + if (!(field_mask & F_VOLHDR_CREATE_DATE)) { + hdr->create_date = 0; + if ((field_mask & F_VOLHDR_ACCESS_DATE) + && (!hdr->create_date || hdr->access_date < hdr->create_date)) + hdr->create_date = hdr->access_date; + if ((field_mask & F_VOLHDR_UPDATE_DATE) + && (!hdr->create_date || hdr->update_date < hdr->create_date)) + hdr->create_date = hdr->update_date; + if ((field_mask & F_VOLHDR_BACKUP_DATE) + && (!hdr->create_date || hdr->backup_date < hdr->create_date)) + hdr->create_date = hdr->backup_date; + + if (RV) fprintf(stderr, ">>> VOL HEADER missing create date (using %d)\n", + hdr->create_date); + hdr->field_mask |= F_VOLHDR_CREATE_DATE; + } + if (!(field_mask & F_VOLHDR_ACCESS_DATE)) { + hdr->access_date = 0; + if ((field_mask & F_VOLHDR_CREATE_DATE) + && (!hdr->access_date || hdr->create_date > hdr->access_date)) + hdr->access_date = hdr->create_date; + if ((field_mask & F_VOLHDR_UPDATE_DATE) + && (!hdr->access_date || hdr->update_date > hdr->access_date)) + hdr->access_date = hdr->update_date; + if ((field_mask & F_VOLHDR_BACKUP_DATE) + && (!hdr->access_date || hdr->backup_date > hdr->access_date)) + hdr->access_date = hdr->backup_date; + + if (RV) fprintf(stderr, ">>> VOL HEADER missing access date (using %d)\n", + hdr->access_date); + hdr->field_mask |= F_VOLHDR_ACCESS_DATE; + } + if (!(field_mask & F_VOLHDR_UPDATE_DATE)) { + hdr->update_date = 0; + if ((field_mask & F_VOLHDR_CREATE_DATE) + && (!hdr->update_date || hdr->create_date > hdr->update_date)) + hdr->update_date = hdr->create_date; + if ((field_mask & F_VOLHDR_ACCESS_DATE) && !hdr->update_date) + hdr->update_date = hdr->access_date; + if ((field_mask & F_VOLHDR_BACKUP_DATE) && !hdr->update_date) + hdr->update_date = hdr->backup_date; + + if (RV) fprintf(stderr, ">>> VOL HEADER missing update date (using %d)\n", + hdr->update_date); + hdr->field_mask |= F_VOLHDR_UPDATE_DATE; + } + + return DumpVolumeHeader(&repair_output, hdr); +} + + +/* Try to dump a vnode. Generate missing fields, if necessary */ +afs_uint32 repair_vnode_cb(afs_vnode *v, XFILE *X, void *refcon) +{ + afs_uint32 r, field_mask = v->field_mask; + + if ((v->vnode & 1) && !field_mask) { + if (RV) fprintf(stderr, ">>> VNODE %d is directory but has no fields?\n"); + v->type = vDirectory; + v->field_mask |= F_VNODE_TYPE; + field_mask = F_VNODE_TYPE; /* Messy! */ + } + if (field_mask && !(field_mask & F_VNODE_TYPE)) { + v->type = (v->vnode & 1) ? vDirectory : vFile; + if (RV) fprintf(stderr, ">>> VNODE %d missing type (using %d)\n", + v->vnode, v->type); + v->field_mask |= F_VNODE_TYPE; + } + if (field_mask && !(field_mask & F_VNODE_NLINKS)) { + if (RV) + fprintf(stderr, ">>> VNODE %d missing link count (using 1)\n", v->vnode); + v->nlinks = 1; + v->field_mask |= F_VNODE_NLINKS; + } + if (field_mask && !(field_mask & F_VNODE_PARENT)) { + if (RV) + fprintf(stderr, ">>> VNODE %d missing parent (using 1)\n", v->vnode); + v->parent = 1; + v->field_mask |= F_VNODE_PARENT; + } + if (field_mask && !(field_mask & F_VNODE_DVERS)) { + if (RV) fprintf(stderr, ">>> VNODE %d missing data version (using 1)\n", + v->vnode); + v->datavers = 1; + v->field_mask |= F_VNODE_DVERS; + } + if (field_mask && !(field_mask & F_VNODE_AUTHOR)) { + if (field_mask & F_VNODE_OWNER) v->author = v->owner; + else v->author = 0; + if (RV) fprintf(stderr, ">>> VNODE %d missing author (using %d)\n", + v->vnode, v->author); + v->field_mask |= F_VNODE_AUTHOR; + } + if (field_mask && !(field_mask & F_VNODE_OWNER)) { + if (field_mask & F_VNODE_AUTHOR) v->owner = v->author; + else v->owner = 0; + if (RV) fprintf(stderr, ">>> VNODE %d missing owner (using %d)\n", + v->vnode, v->owner); + v->field_mask |= F_VNODE_OWNER; + } + if (field_mask && !(field_mask & F_VNODE_MODE)) { + v->mode = (v->vnode & 1) ? 0755 : 0644; + if (RV) fprintf(stderr, ">>> VNODE missing mode (using %d)\n", v->mode); + v->field_mask |= F_VNODE_MODE; + } + if (field_mask && !(field_mask & F_VNODE_CDATE)) { + if (field_mask & F_VNODE_SDATE) v->client_date = v->server_date; + else v->client_date = 0; + + if (RV) fprintf(stderr, ">>> VNODE %d missing client date (using %d)\n", + v->vnode, v->client_date); + v->field_mask |= F_VNODE_CDATE; + } + if (field_mask && !(field_mask & F_VNODE_SDATE)) { + if (field_mask & F_VNODE_CDATE) v->server_date = v->client_date; + else v->server_date = 0; + + if (RV) fprintf(stderr, ">>> VNODE %d missing server date (using %d)\n", + v->vnode, v->server_date); + v->field_mask |= F_VNODE_SDATE; + } + if (field_mask && !(field_mask & F_VNODE_SIZE)) { + if (RV) fprintf(stderr, ">>> VNODE %d has no data size (using 0)\n"); + v->size = 0; + v->field_mask |= F_VNODE_SIZE; + } + if ((field_mask & F_VNODE_DATA) && !v->size) { + if (RV) + fprintf(stderr, ">>> VNODE %d has data, but size == 0 (ignoring)\n", + v->vnode); + v->field_mask &=~ F_VNODE_DATA; + } + if (field_mask && v->type == vDirectory && !(field_mask & F_VNODE_ACL)) { + struct acl_accessList *acl = (struct acl_accessList *)v->acl; + if (RV) { + fprintf(stderr, ">>> VNODE %d is directory but has no ACL\n"); + fprintf(stderr, ">>> Will generate default ACL\n"); + } + memset(v->acl, 0, SIZEOF_LARGEDISKVNODE - SIZEOF_SMALLDISKVNODE); + acl->size = htonl(SIZEOF_LARGEDISKVNODE - SIZEOF_SMALLDISKVNODE); + acl->version = htonl(ACL_ACLVERSION); + acl->total = htonl(v->owner ? 0 : 1); + acl->positive = acl->total; + acl->negative = 0; + if (v->owner) { + acl->entries[0].id = htonl(v->owner); + acl->entries[0].rights = htonl((PRSFS_READ | PRSFS_WRITE + | PRSFS_INSERT | PRSFS_LOOKUP + | PRSFS_DELETE | PRSFS_LOCK + | PRSFS_ADMINISTER)); + } + v->field_mask |= F_VNODE_ACL; + } + + r = DumpVNode(&repair_output, v); + if (r) return r; + + if (v->size) { + if (r = xfseek(X, &v->d_offset)) return r; + r = CopyVNodeData(&repair_output, X, v->size); + } else if (v->type == vDirectory) { + afs_dir_page page; + struct DirHeader *dhp = (struct DirHeader *)&page; + int i; + + if (RV) { + fprintf(stderr, ">>> VNODE %d is directory but has no contents\n"); + fprintf(stderr, ">>> Will generate deafult directory entries\n"); + } + memset(&page, 0, sizeof(page)); + + /* Page and Directory Headers */ + page.header.tag = htons(1234); + page.header.freecount = (EPP - DHE - 3); + page.header.freebitmap[0] = 0xff; + page.header.freebitmap[1] = 0x7f; + dhp->alloMap[0] = EPP - DHE - 3; + for (i = 1; i < MAXPAGES; i++) dhp->alloMap[i] = EPP; + + /* Entry for . */ + page.entry[DHE + 1].flag = FFIRST; + page.entry[DHE + 1].length = 1; + page.entry[DHE + 1].vnode = v->vnode; + page.entry[DHE + 1].vunique = v->vuniq; + strcpy(page.entry[DHE + 1].name, "."); + dhp->hashTable[0x2e] = DHE + 1; + + /* Entry for .. */ + page.entry[DHE + 2].flag = FFIRST; + page.entry[DHE + 2].length = 1; + page.entry[DHE + 2].vnode = v->parent; + page.entry[DHE + 2].vunique = 1; /* Can't have everything! */ + strcpy(page.entry[DHE + 2].name, ".."); + dhp->hashTable[0x44] = DHE + 2; + + r = DumpVNodeData(&repair_output, (char *)&page, 2048); + } else if (field_mask) { + /* We wrote out attributes, so we should also write the 0-length data */ + r = DumpVNodeData(&repair_output, "", 0); + } + + return r; +} diff --git a/src/tests/stagehdr.c b/src/tests/stagehdr.c new file mode 100644 index 0000000000..be6342614b --- /dev/null +++ b/src/tests/stagehdr.c @@ -0,0 +1,150 @@ +/* + * CMUCS AFStools + * dumpscan - routines for scanning and manipulating AFS volume dumps + * + * Copyright (c) 1998 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software_Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ + +/* stagehdr.c - Parse and dump stage backup headers */ + +#include +#include +#include +#include + +#include "dumpscan.h" +#include "dumpscan_errs.h" +#include "xf_errs.h" +#include "stagehdr.h" + +static afs_uint32 hdr_checksum(char *buf, int size) +{ + afs_uint32 sum = 0, n = size / sizeof(afs_uint32), *words = (afs_uint32 *)buf; + + while (--n) + sum += ntohl(*words++); + return sum; +} + + +/* Parse a stage backup header. + * If tag is non-NULL, *tag should contain the first byte (already read), + * and will be filled in with the first byte after the header, if one exists. + * On success, returns 0 and leaves us positioned after the header + * On failure, returns an error and position is undefined + * Iff there is no header, returns DSERR_MAGIC and leaves us + * positioned where we started. + */ +afs_uint32 ParseStageHdr(XFILE *X, unsigned char *tag, backup_system_header *hdr) +{ + char buf[STAGE_HDRLEN]; + struct stage_header *bckhdr = (struct stage_header *)buf; + u_int64 where; + afs_uint32 r; + + if (r = xftell(X, &where)) return r; + if (hdr) memset(hdr, 0, sizeof(*hdr)); + if (tag) { + if (*tag != STAGE_VERSMIN) return DSERR_MAGIC; + buf[0] = *tag; + r = xfread(X, buf + 1, STAGE_HDRLEN - 1); + } else { + r = xfread(X, buf, STAGE_HDRLEN); + } + + if (r == ERROR_XFILE_EOF) { + r = xfseek(X, &where); + return r ? r : DSERR_MAGIC; + } else if (r) return r; + + if (bckhdr->c_vers < STAGE_VERSMIN + || ntohl(bckhdr->c_magic) != STAGE_MAGIC + || hdr_checksum(buf, STAGE_HDRLEN) != STAGE_CHECKSUM) { + r = xfseek(X, &where); + return r ? r : DSERR_MAGIC; + } + + if (hdr) { + hdr->version = bckhdr->c_vers; + hdr->from_date = ntohl(bckhdr->c_fdate); + hdr->to_date = ntohl(bckhdr->c_tdate); + hdr->dump_date = ntohl(bckhdr->c_time); + hdr->filenum = ntohl(bckhdr->c_filenum); + hdr->volid = ntohl(bckhdr->c_id); + hdr->dumplen = ntohl(bckhdr->c_length); + hdr->level = ntohl(bckhdr->c_level); + hdr->magic = ntohl(bckhdr->c_magic); + hdr->cksum = ntohl(bckhdr->c_checksum); + hdr->flags = ntohl(bckhdr->c_flags); + hdr->server = malloc(strlen(bckhdr->c_host) + 1); + hdr->part = malloc(strlen(bckhdr->c_disk) + 1); + hdr->volname = malloc(strlen(bckhdr->c_name) + 1); + + if (!hdr->server || !hdr->part || !hdr->volname) { + if (hdr->server) free(hdr->server); + if (hdr->part) free(hdr->part); + if (hdr->volname) free(hdr->volname); + return ENOMEM; + } + strcpy(hdr->server, bckhdr->c_host); + strcpy(hdr->part, bckhdr->c_disk); + strcpy(hdr->volname, bckhdr->c_name); + } + + if (tag) return ReadByte(X, tag); + else return 0; +} + + +/* Dump a stage backup header */ +afs_uint32 DumpStageHdr(XFILE *OX, backup_system_header *hdr) +{ + char buf[STAGE_HDRLEN]; + struct stage_header *bckhdr = (struct stage_header *)buf; + afs_uint32 checksum; + afs_uint32 r; + + memset(buf, 0, STAGE_HDRLEN); + bckhdr->c_vers = hdr->version; + bckhdr->c_fdate = htonl(hdr->from_date); + bckhdr->c_tdate = htonl(hdr->to_date); + bckhdr->c_filenum = htonl(hdr->filenum); + bckhdr->c_time = htonl(hdr->dump_date); + bckhdr->c_id = htonl(hdr->volid); + bckhdr->c_length = htonl(hdr->dumplen); + bckhdr->c_level = htonl(hdr->level); + bckhdr->c_magic = htonl(STAGE_MAGIC); + bckhdr->c_flags = htonl(hdr->flags); + + strcpy(bckhdr->c_host, hdr->server); + strcpy(bckhdr->c_disk, hdr->part); + strcpy(bckhdr->c_name, hdr->volname); + + /* Now, compute the checksum */ + checksum = hdr_checksum(buf, STAGE_HDRLEN); + bckhdr->c_checksum = htonl(STAGE_CHECKSUM - checksum); + + if (r = xfwrite(OX, buf, STAGE_HDRLEN)) return r; + return 0; +} diff --git a/src/tests/stagehdr.h b/src/tests/stagehdr.h new file mode 100644 index 0000000000..985fe9e598 --- /dev/null +++ b/src/tests/stagehdr.h @@ -0,0 +1,61 @@ +/* + * CMUCS AFStools + * dumpscan - routines for scanning and manipulating AFS volume dumps + * + * Copyright (c) 1998 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software_Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ + +/* stagehdr.h - (old) Stage backup header format */ + +#ifndef _STAGEHDR_H_ +#define _STAGEHDR_H_ + +#include "intNN.h" + +/* Stage-related constants */ +#define STAGE_MAGIC 0x00adf8bc /* magic number for stage header */ +#define STAGE_CHECKSUM 84446 /* checksum (same as 4.2bsd dump) */ +#define STAGE_VERSMIN 20 /* minimum version */ +#define STAGE_NAMLEN 64 /* length of host/part/vol names */ +#define STAGE_HDRLEN 1024 /* size of the header */ + +struct stage_header { + unsigned char c_vers; /* header version (starts at 20) */ + unsigned char c_notused[3]; + afs_uint32 c_fdate; /* dump "from" date */ + afs_uint32 c_tdate; /* dump "to" date */ + afs_uint32 c_filenum; /* tape file number */ + afs_uint32 c_time; /* time dump was done */ + char c_host[STAGE_NAMLEN]; /* hostname volume came from */ + char c_disk[STAGE_NAMLEN]; /* partition volume came from */ + char c_name[STAGE_NAMLEN]; /* volume name */ + afs_uint32 c_id; /* volume ID */ + afs_uint32 c_length; /* length of the dump */ + afs_uint32 c_level; /* dump level */ + afs_uint32 c_magic; /* magic number */ + afs_uint32 c_checksum; /* checksum of backup header */ + afs_uint32 c_flags; /* feature flags */ +}; + +#endif /* _STAGEHDR_H_ */ diff --git a/src/tests/util.c b/src/tests/util.c new file mode 100644 index 0000000000..7c9ac4d85f --- /dev/null +++ b/src/tests/util.c @@ -0,0 +1,164 @@ +/* + * CMUCS AFStools + * dumpscan - routines for scanning and manipulating AFS volume dumps + * + * Copyright (c) 1998 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software_Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ + +/* util.c - Useful utilities */ + +#include + +#include "xf_errs.h" +#include "dumpscan.h" +#include "dumpscan_errs.h" +#include "dumpfmt.h" + + +/* Take care of errno, ERROR_XFILE_EOF, and ENOMEM return codes. + * Call whatever callbacks are necessary, and return the code to + * actually use. If you don't want '0' to result in a DSERR_TAG, + * then you must translate it to DSERR_DONE before calling this. + */ +/*** THIS FUNCTION INTENDED FOR INTERNAL USE ONLY ***/ +int handle_return(int r, XFILE *X, unsigned char tag, dump_parser *p) +{ + u_int64 where, xwhere; + + switch (r) { + case 0: + if (p->cb_error) { + xftell(X, &where); + sub64_32(xwhere, where, 1); + (p->cb_error)(DSERR_TAG, 1, p->err_refcon, + (tag > 0x20 && tag < 0x7f) + ? "Unexpected tag '%c' at %s = 0x%s" + : "Unexpected tag 0x%02x at %s = 0x%s", + tag, decimate_int64(&xwhere, 0), hexify_int64(&xwhere, 0)); + } + return DSERR_TAG; + + case ERROR_XFILE_EOF: + if (p->cb_error) { + xftell(X, &where); + (p->cb_error)(ERROR_XFILE_EOF, 1, p->err_refcon, + "Unexpected EOF at %s = 0x%s", + decimate_int64(&where, 0), hexify_int64(&where, 0)); + } + return ERROR_XFILE_EOF; + + case ENOMEM: + if (p->cb_error) { + xftell(X, &where); + (p->cb_error)(ENOMEM, 1, p->err_refcon, + "Out of memory at %s = 0x%s", + decimate_int64(&where, 0), hexify_int64(&where, 0)); + } + return ENOMEM; + + case DSERR_DONE: + return 0; + + default: + /* For other negative valuees, the callback was already done */ + if (r > 0 && p->cb_error) + (p->cb_error)(r, 1, p->err_refcon, + "System error %d reading dump file", r); + return r; + } +} + + +/* Prepare a tag_parse_info for use by the dump parser. * +/*** THIS FUNCTION INTENDED FOR INTERNAL USE ONLY ***/ +void prep_pi(dump_parser *p, tag_parse_info *pi) +{ + memset(pi, 0, sizeof(tag_parse_info)); + pi->err_refcon = p->err_refcon; + pi->cb_error = p->cb_error; + + if (p->repair_flags & DSFIX_SKIP) + pi->flags |= TPFLAG_SKIP; + if ((p->flags & DSFLAG_SEEK) && (p->repair_flags & DSFIX_RSKIP)) + pi->flags |= TPFLAG_RSKIP; +} + + +/* Does the designated location match a vnode? + * Returns 0 if yes, DSERR_FMT if no, something else on error + */ +/*** THIS FUNCTION INTENDED FOR INTERNAL USE ONLY ***/ +int match_next_vnode(XFILE *X, dump_parser *p, u_int64 *where, afs_uint32 vnode) +{ + afs_uint32 r, x, y, z; + unsigned char tag; + + if (r = xfseek(X, where)) return r; + if (r = ReadByte(X, &tag)) return r; + switch (tag) { + case 3: /* A vnode? */ + if (r = ReadInt32(X, &x)) return r; + if (r = ReadInt32(X, &y)) return r; + if (r = ReadByte(X, &tag)) return r; + if ( !((vnode & 1) && !(x & 1) && x < vnode) + && !((vnode & 1) == (x & 1) && x > vnode)) + return DSERR_FMT; + if (x > vnode && x - vnode > 10000) return DSERR_FMT; + if (y < 0 || y > p->vol_uniquifier) return DSERR_FMT; + + /* Now, what follows the vnode/uniquifier? */ + switch (tag) { + case 3: /* Another vnode? - Only if this is a non-directory */ + if (x & 1) return DSERR_FMT; + if (r = ReadInt32(X, &z)) return r; + if ( !((x & 1) && !(z & 1) && z < x) + && !((x & 1) == (z & 1) && z > x)) + return DSERR_FMT; + return 0; + + case 4: /* Dump end - Only if this is a non-directory */ + if (x & 1) return DSERR_FMT; + if (r = ReadInt32(X, &z)) return r; + if (z != DUMPENDMAGIC) return DSERR_FMT; + return 0; + + case 't': /* Vnode type byte */ + if (r = ReadByte(X, &tag)) return r; + if ((tag == vFile || tag == vSymlink) && !(x & 1)) return 0; + if (tag == vDirectory && (x & 1)) return 0; + return DSERR_FMT; + + default: + return DSERR_FMT; + } + + case 4: /* A dump end? */ + if (r = ReadInt32(X, &x)) return r; + if (x != DUMPENDMAGIC) return DSERR_FMT; + return 0; + + default: + return DSERR_FMT; + } +} diff --git a/src/tests/xf_errs.et b/src/tests/xf_errs.et new file mode 100644 index 0000000000..03cca8fd10 --- /dev/null +++ b/src/tests/xf_errs.et @@ -0,0 +1,37 @@ +# CMUCS AFStools +# dumpscan - routines for scanning and manipulating AFS volume dumps +# +# Copyright (c) 1998 Carnegie Mellon University +# All Rights Reserved. +# +# Permission to use, copy, modify and distribute this software and its +# documentation is hereby granted, provided that both the copyright +# notice and this permission notice appear in all copies of the +# software, derivative works or modified versions, and any portions +# thereof, and that both notices appear in supporting documentation. +# +# CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" +# CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR +# ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. +# +# Carnegie Mellon requests users of this software to return to +# +# Software Distribution Coordinator or Software_Distribution@CS.CMU.EDU +# School of Computer Science +# Carnegie Mellon University +# Pittsburgh PA 15213-3890 +# +# any improvements or extensions that they make and grant Carnegie Mellon +# the rights to redistribute these changes. + +# xf_errs.et - Error table for xfiles + +error_table xFil + ec ERROR_XFILE_EOF, "EOF while reading XFILE" + ec ERROR_XFILE_WRONLY, "XFILE may not be opened write-only" + ec ERROR_XFILE_RDONLY, "XFILE is read-only" + ec ERROR_XFILE_NOSEEK, "XFILE is not seekable" + ec ERROR_XFILE_ISPASS, "XFILE passthru already set" + ec ERROR_XFILE_NOPASS, "XFILE passthru not set" + ec ERROR_XFILE_TYPE, "unknown XFILE type" +end diff --git a/src/tests/xf_files.c b/src/tests/xf_files.c new file mode 100644 index 0000000000..877e1ae0ba --- /dev/null +++ b/src/tests/xf_files.c @@ -0,0 +1,200 @@ +/* + * CMUCS AFStools + * dumpscan - routines for scanning and manipulating AFS volume dumps + * + * Copyright (c) 1998 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software_Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ + +/* xf_files.c - XFILE routines for accessing UNIX files */ + +#include +#include +#include +#include + +#include "xfiles.h" +#include "xf_errs.h" + +#define O_MODE_MASK (O_RDONLY | O_WRONLY | O_RDWR) + + +/* do_read for stdio xfiles */ +static afs_uint32 xf_FILE_do_read(XFILE *X, void *buf, afs_uint32 count) +{ + FILE *F = X->refcon; + + /* XXX: handle short and interrupted reads */ + if (fread(buf, count, 1, F) != 1) + return ferror(F) ? errno : ERROR_XFILE_EOF; + return 0; +} + + +/* do_write for stdio xfiles */ +static afs_uint32 xf_FILE_do_write(XFILE *X, void *buf, afs_uint32 count) +{ + FILE *F = X->refcon; + + /* XXX: handle interrupted writes */ + if (fwrite(buf, count, 1, F) != 1) + return errno; + return 0; +} + + +/* do_tell for stdio xfiles */ +static afs_uint32 xf_FILE_do_tell(XFILE *X, u_int64 *offset) +{ + FILE *F = X->refcon; + off_t where; + + where = ftell(F); + if (where == -1) return errno; + set64(*offset, where); + return 0; +} + + +/* do_seek for stdio xfiles */ +static afs_uint32 xf_FILE_do_seek(XFILE *X, u_int64 *offset) +{ + FILE *F = X->refcon; + off_t where = get64(*offset); + + if (fseek(F, where, SEEK_SET) == -1) return errno; + return 0; +} + + +/* do_skip for stdio xfiles */ +static afs_uint32 xf_FILE_do_skip(XFILE *X, afs_uint32 count) +{ + FILE *F = X->refcon; + + if (fseek(F, count, SEEK_CUR) == -1) return errno; + return 0; +} + + +/* do_close for stdio xfiles */ +static afs_uint32 xf_FILE_do_close(XFILE *X) +{ + FILE *F = X->refcon; + + X->refcon = 0; + if (fclose(F)) return errno; + return 0; +} + + +/* Prepare a stdio XFILE */ +static void prepare(XFILE *X, FILE *F, int xflag) +{ + struct stat st; + + memset(X, 0, sizeof(*X)); + X->do_read = xf_FILE_do_read; + X->do_write = xf_FILE_do_write; + X->do_tell = xf_FILE_do_tell; + X->do_close = xf_FILE_do_close; + X->refcon = F; + if (xflag == O_RDWR) X->is_writable = 1; + + if (!fstat(fileno(F), &st) + && ((st.st_mode & S_IFMT) == S_IFREG + || (st.st_mode & S_IFMT) == S_IFBLK)) { + X->is_seekable = 1; + X->do_seek = xf_FILE_do_seek; + X->do_skip = xf_FILE_do_skip; + } +} + + +/* Open an XFILE by path */ +afs_uint32 xfopen_path(XFILE *X, int flag, char *path, int mode) +{ + FILE *F = 0; + int fd = -1, xflag; + afs_uint32 code; + + xflag = flag & O_MODE_MASK; + if (xflag == O_WRONLY) return ERROR_XFILE_WRONLY; + + if ((fd = open(path, flag, mode)) < 0) return errno; + if (!(F = fdopen(fd, (xflag == O_RDONLY) ? "r" : "r+"))) { + code = errno; + close(fd); + return code; + } + + prepare(X, F, xflag); + return 0; +} + + +/* Open an XFILE by FILE * */ +afs_uint32 xfopen_FILE(XFILE *X, int flag, FILE *F) +{ + flag &= O_MODE_MASK; + if (flag == O_WRONLY) return ERROR_XFILE_WRONLY; + prepare(X, F, flag); + return 0; +} + + +/* Open an XFILE by file descriptor */ +afs_uint32 xfopen_fd(XFILE *X, int flag, int fd) +{ + FILE *F; + + flag &= O_MODE_MASK; + if (flag == O_WRONLY) return ERROR_XFILE_WRONLY; + if (!(F = fdopen(fd, (flag == O_RDONLY) ? "r" : "r+"))) return errno; + prepare(X, F, flag); + return 0; +} + + +/* open-by-name support for filenames */ +afs_uint32 xfon_path(XFILE *X, int flag, char *name) +{ + return xfopen_path(X, flag, name, 0644); +} + + +/* open-by-name support for file descriptors */ +afs_uint32 xfon_fd(XFILE *X, int flag, char *name) +{ + int fd = atoi(name); + return xfopen_fd(X, flag, fd); +} + + +/* open-by-name support for standard I/O */ +afs_uint32 xfon_stdio(XFILE *X, int flag) +{ + flag &= O_MODE_MASK; + if (flag == O_WRONLY) flag = O_RDWR; + return xfopen_FILE(X, flag, (flag == O_RDONLY) ? stdin : stdout); +} diff --git a/src/tests/xf_printf.c b/src/tests/xf_printf.c new file mode 100644 index 0000000000..d1060ea2ec --- /dev/null +++ b/src/tests/xf_printf.c @@ -0,0 +1,444 @@ +/* + * CMUCS AFStools + * dumpscan - routines for scanning and manipulating AFS volume dumps + * + * Copyright (c) 1998 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software_Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "xfiles.h" +#include "xf_errs.h" + +#define SPBUFLEN 40 +static char spbuf[SPBUFLEN + 1] = ""; + + +#define MAXPREC 100 + +/* Generate an ASCII representation of an integer , as follows: + * indicates the base to be used (2-36) + * is nonzero if letter digits should be uppercase + * is the minimum number of digits + * The resulting number is stored in , which must be long enough + * to receive it. The minimum length is or ceil(log{base}(val)), + * whichever is larger, plus room for a trailing NUL. + */ +static void mkint(char *buf, unsigned long val, int base, int uc, int prec) +{ + int len = 0, dig, i; + + while (val) { + dig = val % base; + val = (val - dig) / base; + if (dig < 10) dig = dig + '0'; + else if (uc) dig = dig + 'A' - 10; + else dig = dig + 'a' - 10; + buf[len++] = dig; + } + while (len < prec) buf[len++] = '0'; + for (i = 0; i < (len + 1) / 2; i++) { + dig = buf[i]; + buf[i] = buf[len - i - 1]; + buf[len - i - 1] = dig; + } + buf[len] = 0; +} + + +/* Write spaces faster than one at a time */ +static afs_uint32 wsp(XFILE *X, int count) +{ + char *x; + afs_uint32 err; + int i; + + if (!spbuf[0]) { + for (x = spbuf, i = SPBUFLEN; i; x++, i--) *x = ' '; + } + + while (count > SPBUFLEN) { + err = xfwrite(X, spbuf, SPBUFLEN); + if (err) return err; + count -= SPBUFLEN; + } + if (count > 0) return xfwrite(X, spbuf, count); + return 0; +} + + +/* This function is a mostly-complete implementation of snprintf, + * with the following features: + * + * - Actually obeys the length limit, which (unfortunately) many + * implementations of snprintf do not. + * + * - Supports all the standard format specifiers for integers + * (d, i, o, u, x, X), floating-point values (f, e, E, g, G), + * and strings and characters (c, s, %), plus a few unusual + * but useful ones described below. + * + * - Supports all the standard flags (-, 0, +, space, #). These + * flags are ignored if used when they are not appropriate. + * + * - Supports the standard size modifiers for short (h), long (h), + * and double (L) arguments. These modifiers are ignored if used + * when they are not appropriate. + * + * - Supports minimum field width and precision, where appropriate, + * including the use of '*' to specify a value given as an argument + * instead of in the format string. There is a maximum precision + * of 100 digits. + * + * - At present, the 'p' specifier for printing pointers is not + * implemented, because it is inherently non-portable and thus + * can be implemented correctly only by the compiler's run-time + * library. + * + * - Floating-point specifier (%e, %f, %g) are implemented by + * calling the standard sprintf, and thus may be unsafe. + * + * - The '%...$' notation is used primarily when the format string + * is specified by the user, who knows but cannot change the order + * of the arguments. Such usage is inherently dangerous and + * insecure; thus, it is not supported. + * + * The custom format specifier '%I' is supported. This specifier + * takes as its argument an unsigned long integer containing an + * IPv4 address in network byte order. The address is rendered + * either as a hostname or as a dotted quad, as follows: + * + * - If precision is nonzero or unspecified, a hostname lookup + * is attempted; if it is successful, the hostname is printed. + * If the hostname lookup fails, the address is printed in + * dotted-quad notation. + * + * - If precision is explicitly specified as 0, then the hostname + * lookup is skipped, and dotted-quad notation is always used. + * + * - If a hostname is to be printed: + * + The precision controls the maximum number of characters + * printed, as with %s. + * + If the '#' flag is specified, any letters in the hostname + * will be forced to lower case before printing. + * + If the '+' flag is specified, any letters in the hostname + * will be forced to upper case before printing. If both + * '#' and '+' are given, the '+' flag will be ignored. + * + The '0' and ' ' flags have no effect. + * + * - If a dotted quad is to be printed: + * + The precision has no effect; dotted quads are always + * 7 to 12 characters in length, depending on the value + * to be printed and the format flags used. + * + If the '0' flag is given, each field (byte) of the address + * will be padded with '0' on the left to three digits. + * + If the ' ' flag is given, each field (byte) of the address + * will be padded with spaces on the left to three digits. If + * both '0' and ' ' are given, the ' ' flag will be ignored. + * + The '#' and '+' flags have no effect. + */ + +afs_uint32 vxfprintf(XFILE *X, char *fmt, va_list ap) +{ + unsigned int width, precision, haveprec, len; + int ljust, plsign, spsign, altform, zfill; + int hflag, lflag, count, *countp, j; + char *x, *y, *lit = 0, xbuf[MAXPREC + 21], fbuf[20]; + struct hostent *he; + struct in_addr ia; + unsigned long UVAL; + long SVAL, *lcountp; + double FVAL; + short *hcountp; + afs_uint32 err; + + count = 0; + while (*fmt) { + if (*fmt != '%') { + if (!lit) lit = fmt; + fmt++; + count++; + continue; + } + if (lit) { + if ((err = xfwrite(X, lit, fmt - lit))) return err; + lit = 0; + } + + /** Found a format specifier **/ + ljust = plsign = spsign = altform = zfill = 0; + width = precision = haveprec = 0; + hflag = lflag = 0; + fmt++; + + /* parse format flags */ + while (*fmt) { + switch (*fmt) { + case '-': ljust = 1; fmt++; continue; /* left justify */ + case '+': plsign = 1; fmt++; continue; /* use + or - */ + case ' ': spsign = 1; fmt++; continue; /* use space or - */ + case '#': altform = 1; fmt++; continue; /* alternate form */ + case '0': zfill = 1; fmt++; continue; /* pad with 0 */ + default: break; + } + break; + } + + /* parse minimum width */ + if (*fmt == '*') { + width = va_arg(ap, int); + fmt++; + } else while (isdigit(*fmt)) { + width = (width * 10) + (*fmt - '0'); + fmt++; + } + + /* parse precision */ + if (*fmt == '.') { + fmt++; + haveprec = 1; + if (*fmt == '*') { + precision = va_arg(ap, int); + fmt++; + } else while (isdigit(*fmt)) { + precision = (precision * 10) + (*fmt - '0'); + fmt++; + } + } + + /* parse size flags */ + while (*fmt) { + switch (*fmt) { + case 'h': hflag = 1; fmt++; continue; /* short argument */ + case 'l': lflag = 1; fmt++; continue; /* long argument */ + default: break; + } + break; + } + + /* parse format specifier */ + if (!*fmt) break; + switch (*fmt++) { + case 'e': + case 'E': + case 'f': + case 'g': + case 'G': + FVAL = va_arg(ap, double); + sprintf(fbuf, "%%%s%s.*L%c", plsign ? "+" : (spsign ? " " : ""), + altform ? "#" : "", fmt[-1]); + if (!haveprec) precision = 6; + if (precision > MAXPREC) precision = MAXPREC; + sprintf(xbuf, fbuf, precision, FVAL); + x = xbuf; + len = strlen(x); + break; + + case 'i': + case 'd': /* signed decimal integer */ + if (lflag) SVAL = va_arg(ap, long); + else if (hflag) SVAL = va_arg(ap, int); + else SVAL = va_arg(ap, int); + UVAL = (SVAL < 0) ? -SVAL : SVAL; + + if (SVAL < 0) xbuf[0] = '-'; + else if (plsign) xbuf[0] = '+'; + else if (spsign) xbuf[0] = ' '; + else xbuf[0] = 0; + + if (!haveprec) { + if (zfill && !ljust) precision = width - !!xbuf[0]; + else precision = 1; + if (precision < 1 + !!xbuf[0]) precision = 1 + !!xbuf[0]; + } + if (precision > MAXPREC) precision = MAXPREC; + + mkint(xbuf + 1, UVAL, 10, 0, precision); + x = xbuf + !xbuf[0]; + len = strlen(x); + break; + + + case 'o': /* unsigned octal integer */ + if (lflag) UVAL = va_arg(ap, unsigned long); + else if (hflag) UVAL = va_arg(ap, unsigned int); + else UVAL = va_arg(ap, unsigned int); + + xbuf[0] = '0'; + + if (!haveprec) { + if (zfill && !ljust) precision = width; + else precision = 1; + } + if (precision > MAXPREC) precision = MAXPREC; + + mkint(xbuf + 1, UVAL, 8, 0, precision); + x = xbuf + (xbuf[1] == '0' || !altform); + len = strlen(x); + break; + + case 'u': /* unsigned decimal integer */ + if (lflag) UVAL = va_arg(ap, unsigned long); + else if (hflag) UVAL = va_arg(ap, unsigned int); + else UVAL = va_arg(ap, unsigned int); + + if (!haveprec) { + if (zfill && !ljust) precision = width; + else precision = 1; + } + if (precision > MAXPREC) precision = MAXPREC; + + mkint(xbuf, UVAL, 10, 0, precision); + x = xbuf; + len = strlen(x); + break; + + case 'x': + case 'X': /* unsigned hexadecimal integer */ + if (lflag) UVAL = va_arg(ap, unsigned long); + else if (hflag) UVAL = va_arg(ap, unsigned int); + else UVAL = va_arg(ap, unsigned int); + + xbuf[0] = '0'; + xbuf[1] = 'x'; + + if (!haveprec) { + if (zfill && !ljust) precision = width; + else precision = 1; + } + if (precision > MAXPREC) precision = MAXPREC; + + mkint(xbuf + 2, UVAL, 16, 0, precision); + x = xbuf + ((altform && UVAL) ? 0 : 2); + len = strlen(x); + break; + + case '%': /* literal % */ + xbuf[0] = '%'; + xbuf[1] = 0; + x = xbuf; + len = 1; + break; + + case 'c': /* character */ + xbuf[0] = va_arg(ap, int); + xbuf[1] = 0; + x = xbuf; + len = 1; + break; + + case 's': /* string */ + x = va_arg(ap, char *); + if (!x) x = ""; + len = strlen(x); + if (haveprec && precision < len) len = precision; + break; + + case 'I': /* IP address: + * value is provided as a network-order unsigned long integer + * precision specifies max hostname length, as for %s + * if precision is explicitly 0, no hostname lookup is done + * if 0fill specified, IPaddr fields are 0-filled to 3 digits + * if spsign specified, IPaddr fields are space-filled to 3 digits + */ + UVAL = va_arg(ap, unsigned long); + ia.s_addr = UVAL; + /* XXX: add support for an application-provided function + * for doing hostname lookups. We don't do it automatically + * because on some platforms that would prevent us from + * being fully statically linked. + */ + if (haveprec && !precision) he = 0; + else he = gethostbyaddr((char *)&ia, 4, AF_INET); + if (he) { + x = he->h_name; + len = strlen(x); + if (haveprec && precision < len) len = precision; + if (altform) + for (y = x; *y; y++) if (isupper(*y)) *y = tolower(*y); + else if (plsign) + for (y = x; *y; y++) if (islower(*y)) *y = toupper(*y); + } else { + UVAL = ntohl(UVAL); + if (zfill) x = "%03u.%03u.%03u.%03u"; + else if (spsign) x = "%3u.%3u.%3u.%3u"; + else x = "%u.%u.%u.%u"; + sprintf(xbuf, x, + (UVAL & 0xff000000) >> 24, (UVAL & 0x00ff0000) >> 16, + (UVAL & 0x0000ff00) >> 8, (UVAL & 0x000000ff)); + x = xbuf; + len = strlen(xbuf); + } + break; + + case 'n': /* report count so far */ + if (lflag) { + lcountp = va_arg(ap, long *); + *lcountp = count; + } else if (hflag) { + hcountp = va_arg(ap, short *); + *hcountp = count; + } else { + countp = va_arg(ap, int *); + *countp = count; + } + continue; + + default: /* unknown specifier */ + continue; + } + + /* render the results */ + if (!width) width = len; + j = width - len; + if (j > 0) count += j; + count += len; + + if (!ljust && (err = wsp(X, j))) return err; + if ((err = xfwrite(X, x, len))) return err; + if (ljust && (err = wsp(X, j))) return err; + } + if (lit && (err = xfwrite(X, lit, fmt - lit))) return err; + return 0; +lose: + return err; +} + + +afs_uint32 xfprintf(XFILE *X, char *fmt, ...) +{ + va_list ap; + afs_uint32 err; + + va_start(ap, fmt); + err = vxfprintf(X, fmt, ap); + va_end(ap); + return err; +} diff --git a/src/tests/xf_profile.c b/src/tests/xf_profile.c new file mode 100644 index 0000000000..9f9dd5037e --- /dev/null +++ b/src/tests/xf_profile.c @@ -0,0 +1,184 @@ +/* + * CMUCS AFStools + * dumpscan - routines for scanning and manipulating AFS volume dumps + * + * Copyright (c) 1998 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software_Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ + +/* xf_profile.c - XFILE routines for read/write profiling */ + +#include +#include +#include +#include +#include +#include + +#include "xfiles.h" +#include "xf_errs.h" + +#define O_MODE_MASK (O_RDONLY | O_WRONLY | O_RDWR) + +typedef struct { + XFILE content; + XFILE profile; +} PFILE; + + +/* do_read for profiled xfiles */ +static afs_uint32 xf_PROFILE_do_read(XFILE *X, void *buf, afs_uint32 count) +{ + PFILE *PF = X->refcon; + afs_uint32 err; + + err = xfread(&PF->content, buf, count); + xfprintf(&PF->profile, "R %ld =%ld\n", (long)count, (long)err); + return err; +} + + +/* do_write for profiled xfiles */ +static afs_uint32 xf_PROFILE_do_write(XFILE *X, void *buf, afs_uint32 count) +{ + PFILE *PF = X->refcon; + afs_uint32 err; + + err = xfwrite(&PF->content, buf, count); + xfprintf(&PF->profile, "W %ld =%ld\n", (long)count, (long)err); + return err; +} + + +/* do_tell for profiled xfiles */ +static afs_uint32 xf_PROFILE_do_tell(XFILE *X, u_int64 *offset) +{ + PFILE *PF = X->refcon; + afs_uint32 err; + + err = xftell(&PF->content, offset); + if (err) xfprintf(&PF->profile, "TELL ERR =%ld\n", (long)err); + else xfprintf(&PF->profile, "TELL %s =0\n", hexify_int64(offset, 0)); + return err; +} + + +/* do_seek for profiled xfiles */ +static afs_uint32 xf_PROFILE_do_seek(XFILE *X, u_int64 *offset) +{ + PFILE *PF = X->refcon; + afs_uint32 err; + + err = xfseek(&PF->content, offset); + xfprintf(&PF->profile, "SEEK %s =%ld\n", hexify_int64(offset, 0), (long)err); + return err; +} + + +/* do_skip for profiled xfiles */ +static afs_uint32 xf_PROFILE_do_skip(XFILE *X, afs_uint32 count) +{ + PFILE *PF = X->refcon; + afs_uint32 err; + + err = xfskip(&PF->content, count); + xfprintf(&PF->profile, "SKIP %ld =%ld\n", (long)count, (long)err); + return err; +} + + +/* do_close for profiled xfiles */ +static afs_uint32 xf_PROFILE_do_close(XFILE *X) +{ + PFILE *PF = X->refcon; + afs_uint32 err, err2; + + err = xfclose(&PF->content); + err2 = xfclose(&PF->profile); + free(PF); + return err ? err : err2; +} + + +/* Open a profiled XFILE */ +afs_uint32 xfopen_profile(XFILE *X, int flag, char *xname, char *profile) +{ + PFILE *PF; + afs_uint32 err; + + PF = malloc(sizeof(*PF)); + if (!PF) return ENOMEM; + memset(PF, 0, sizeof(*PF)); + + err = xfopen(&PF->profile, O_RDWR|O_CREAT|O_TRUNC, profile); + if (err) { + free(PF); + return err; + } + + err = xfopen(&PF->content, flag, xname); + if (err) { + xfclose(&PF->profile); + free(PF); + return err; + } + + memset(X, 0, sizeof(*X)); + X->refcon = PF; + X->do_read = xf_PROFILE_do_read; + X->do_write = xf_PROFILE_do_write; + X->do_tell = xf_PROFILE_do_tell; + X->do_close = xf_PROFILE_do_close; + X->is_writable = PF->content.is_writable; + if (PF->content.is_seekable) { + X->is_seekable; + X->do_seek = xf_PROFILE_do_seek; + X->do_skip = xf_PROFILE_do_skip; + } + xfprintf(&PF->profile, "OPEN %s\n", xname); + return 0; +} + + +afs_uint32 xfon_profile(XFILE *X, int flag, char *name) +{ + char *x, *profile, *xname; + afs_uint32 err; + + if (!(name = strdup(name))) return ENOMEM; + + profile = "-"; + xname = name; + for (x = name; *x; x++) { + if (x[0] == ':' && x[1] == ':') { + *x = 0; + profile = name; + xname = x + 2; + break; + } + } + if (!*name) profile = "-"; + err = xfopen_profile(X, flag, xname, profile); + free(name); + return err; +} diff --git a/src/tests/xf_rxcall.c b/src/tests/xf_rxcall.c new file mode 100644 index 0000000000..957f7abc01 --- /dev/null +++ b/src/tests/xf_rxcall.c @@ -0,0 +1,260 @@ +/* + * CMUCS AFStools + * dumpscan - routines for scanning and manipulating AFS volume dumps + * + * Copyright (c) 1998 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software_Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ + +/* xf_rxcall.c - XFILE routines for Rx bulk data transfers */ + +#include +#include +#include +#include +#include +#include +#include + +#include "xfiles.h" +#include "xf_errs.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef AFSCONF_CLIENTNAME +#include +#define AFSCONF_CLIENTNAME AFSDIR_CLIENT_ETC_DIRPATH +#endif + +#define O_MODE_MASK (O_RDONLY | O_WRONLY | O_RDWR) + +struct rxinfo { + struct rx_connection *conn; /* connection */ + struct rx_call *call; /* call */ + afs_int32 tid; /* volser transaction ID */ + afs_uint32 code; /* result code */ +}; + +static afs_uint32 xf_rxcall_do_read(XFILE *X, void *buf, afs_uint32 count) +{ + struct rxinfo *i = X->refcon; + afs_uint32 xcount; + + xcount = rx_Read(i->call, buf, count); + if (xcount == count) return 0; + i->code = rx_EndCall(i->call, 0); + i->call = 0; + return i->code ? i->code : ERROR_XFILE_RDONLY; +} + + +static afs_uint32 xf_rxcall_do_write(XFILE *X, void *buf, afs_uint32 count) +{ + struct rxinfo *i = X->refcon; + afs_uint32 xcount; + + xcount = rx_Write(i->call, buf, count); + if (xcount == count) return 0; + i->code = rx_EndCall(i->call, 0); + i->call = 0; + return i->code; +} + + +static afs_uint32 xf_rxcall_do_close(XFILE *X) +{ + struct rxinfo *i = X->refcon; + afs_uint32 code; + + if (i->call) { + code = rx_EndCall(i->call, i->code); + i->call = 0; + } else { + code = i->code; + } + free(i); + return code; +} + + +static afs_uint32 xf_voldump_do_close(XFILE *X) +{ + struct rxinfo *i = X->refcon; + struct rx_connection *conn = i->conn; + afs_uint32 code, rcode, xcode; + afs_int32 tid = i->tid; + + code = xf_rxcall_do_close(X); + xcode = AFSVolEndTrans(conn, tid, &rcode); + if (!code) code = xcode ? xcode : rcode; + return code; +} + + +afs_uint32 xfopen_rxcall(XFILE *X, int flag, struct rx_call *call) +{ + struct rxinfo *i; + + flag &= O_MODE_MASK; + if (flag == O_WRONLY) return ERROR_XFILE_WRONLY; + memset(X, 0, sizeof(*X)); + if (!(i = (struct rxinfo *)malloc(sizeof(struct rxinfo)))) return ENOMEM; + i->call = call; + i->code = 0; + X->do_read = xf_rxcall_do_read; + X->do_write = xf_rxcall_do_write; + X->do_close = xf_rxcall_do_close; + X->is_writable = (flag == O_RDWR); + X->refcon = i; + return 0; +} + + +afs_uint32 xfopen_voldump(XFILE *X, struct rx_connection *conn, + afs_int32 part, afs_int32 volid, afs_int32 date) +{ + struct rx_call *call; + struct rxinfo *i; + afs_uint32 code, rcode; + afs_int32 tid; + + if (code = AFSVolTransCreate(conn, volid, part, ITBusy, &tid)) return code; + call = rx_NewCall(conn); + if ((code = StartAFSVolDump(call, tid, date)) + || (code = xfopen_rxcall(X, O_RDONLY, call))) { + rx_EndCall(call, 0); + AFSVolEndTrans(conn, tid, &rcode); + return code; + } + + i = X->refcon; + i->conn = conn; + i->tid = tid; + X->do_close = xf_voldump_do_close; + return 0; +} + + +afs_uint32 xfon_voldump(XFILE *X, int flag, char *name) +{ + struct hostent *he; + struct rx_securityClass *class; + struct rx_connection *conn; + struct ktc_principal sname; + struct ktc_token token; + struct afsconf_dir *confdir; + afs_uint32 code, server_addr; + afs_int32 volid, partid, date; + int isnum, index; + char *x, *y; + + /* Parse out the optional date and server location */ + if (code = rx_Init(0)) return code; + if (!(name = strdup(name))) return ENOMEM; + if (x = strrchr(name, ',')) { + *x++ = 0; + date = atoi(x); + } else { + date = 0; + } + if (x = strrchr(name, '@')) { + int a, b, c, d; + + *x++ = 0; + if (!(y = strchr(x, '/'))) { + free(name); + return VL_BADPARTITION; + } + *y++ = 0; + if (sscanf(x, "%d.%d.%d.%d", &a, &b, &c, &d) == 4 + && a >= 0 && a <= 255 && b >= 0 && b <= 255 + && c >= 0 && c <= 255 && d >= 0 && d <= 255) { + server_addr = (a << 24) | (b << 16) | (c << 8) | d; + server_addr = htonl(server_addr); + } else { + he = gethostbyname(x); + if (!he) { + free(name); + return VL_BADSERVER; + } + memcpy(&server_addr, he->h_addr, sizeof(server_addr)); + } + partid = volutil_GetPartitionID(y); + if (partid < 0) { + free(name); + return VL_BADPARTITION; + } + } + + /* Get tokens and set up a security object */ + confdir = afsconf_Open(AFSCONF_CLIENTNAME); + if (!confdir) { + free(name); + return AFSCONF_NODB; + } + if (code = afsconf_GetLocalCell(confdir, sname.cell, MAXKTCNAMELEN)) { + free(name); + return code; + } + afsconf_Close(confdir); + strcpy(sname.name, "afs"); + sname.instance[0] = 0; + code = ktc_GetToken(&sname, &token, sizeof(token), 0); + if (code) { + class = rxnull_NewClientSecurityObject(); + index = 0; + } else { + class = rxkad_NewClientSecurityObject(rxkad_clear, &token.sessionKey, + token.kvno, token.ticketLen, token.ticket); + index = 2; + } + + /* Figure out the volume ID, looking it up in the VLDB if neccessary. + * Also look up the server and partition, if they were not specified. + */ + for (isnum = 1, y = name; *y; y++) + if (*y < '0' || *y > '9') isnum = 0; + if (isnum) { + volid = atoi(name); + if (!x) { + fprintf(stderr, "XXX: need to lookup volume by ID!\n"); + exit(-1); + } + } else { + fprintf(stderr, "XXX: need to lookup volume by name!\n"); + exit(-1); + } + free(name); + + /* Establish a connection and start the call */ + conn = rx_NewConnection(server_addr, htons(AFSCONF_VOLUMEPORT), + VOLSERVICE_ID, class, index); + return xfopen_voldump(X, conn, partid, volid, date); +} diff --git a/src/tests/xfiles.c b/src/tests/xfiles.c new file mode 100644 index 0000000000..6b026f598b --- /dev/null +++ b/src/tests/xfiles.c @@ -0,0 +1,219 @@ +/* + * CMUCS AFStools + * dumpscan - routines for scanning and manipulating AFS volume dumps + * + * Copyright (c) 1998 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software_Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ + +/* xfiles.c - General support routines for xfiles */ +#include +#include +#include + +#include "xfiles.h" +#include "xf_errs.h" + +#define SKIP_SIZE 65536 + +extern afs_uint32 xfon_path(XFILE *, int, char *); +extern afs_uint32 xfon_fd(XFILE *, int, char *); +extern afs_uint32 xfon_voldump(XFILE *, int, char *); +extern afs_uint32 xfon_profile(XFILE *, int, char *); +extern afs_uint32 xfon_stdio(XFILE *, int); + +struct xftype { + struct xftype *next; + char *name; + afs_uint32 (*do_on)(XFILE *, int, char *); +}; + + +static struct xftype *xftypes = 0; +static int did_register_defaults = 0; + + +afs_uint32 xfread(XFILE *X, void *buf, afs_uint32 count) +{ + afs_uint32 code; + u_int64 tmp64; + + code = (X->do_read)(X, buf, count); + if (code) return code; + + add64_32(tmp64, X->filepos, count); + cp64(X->filepos, tmp64); + if (X->passthru) return xfwrite(X->passthru, buf, count); + return 0; +} + + +afs_uint32 xfwrite(XFILE *X, void *buf, afs_uint32 count) +{ + afs_uint32 code; + u_int64 tmp64; + + if (!X->is_writable) return ERROR_XFILE_RDONLY; + code = (X->do_write)(X, buf, count); + if (code) return code; + + add64_32(tmp64, X->filepos, count); + cp64(X->filepos, tmp64); + return 0; +} + + +afs_uint32 xftell(XFILE *X, u_int64 *offset) +{ + if (X->do_tell) return (X->do_tell)(X, offset); + cp64(*offset, X->filepos); + return 0; +} + + +afs_uint32 xfseek(XFILE *X, u_int64 *offset) +{ + afs_uint32 code; + + if (!X->do_seek) return ERROR_XFILE_NOSEEK; + code = (X->do_seek)(X, offset); + if (code) return code; + cp64(X->filepos, *offset); + return 0; +} + + +afs_uint32 xfskip(XFILE *X, afs_uint32 count) +{ + afs_uint32 code; + u_int64 tmp64; + + /* Use the skip method, if there is one */ + if (X->do_skip && !X->passthru) { + code = (X->do_skip)(X, count); + if (code) return code; + add64_32(tmp64, X->filepos, count); + cp64(X->filepos, tmp64); + return 0; + } + + /* Simulate using absolute seek, if available */ + if (X->do_seek && !X->passthru) { + if (code = xftell(X, &tmp64)) return code; + add64_32(X->filepos, tmp64, count); + cp64(tmp64, X->filepos); + return xfseek(X, &tmp64); + } + + /* Do it the hard/slow way - read all the data to be skipped. + * This is done if no other method is available, or if we are + * supposed to be copying all the data to another XFILE + */ + { + char buf[SKIP_SIZE]; + afs_uint32 n; + + while (count) { + n = (count > SKIP_SIZE) ? SKIP_SIZE : count; + if (code = xfread(X, buf, n)) return code; + count -= n; + } + return 0; + } +} + + +afs_uint32 xfpass(XFILE *X, XFILE *Y) +{ + if (X->passthru) return ERROR_XFILE_ISPASS; + if (!Y->is_writable) return ERROR_XFILE_RDONLY; + X->passthru = Y; + return 0; +} + + +afs_uint32 xfunpass(XFILE *X) +{ + if (!X->passthru) return ERROR_XFILE_NOPASS; + X->passthru = 0; + return 0; +} + + +afs_uint32 xfclose(XFILE *X) +{ + int code = 0; + + if (X->do_close) code = (X->do_close)(X); + memset(X, 0, sizeof(*X)); + return 0; +} + + +afs_uint32 xfregister(char *name, afs_uint32 (*do_on)(XFILE *, int, char *)) +{ + struct xftype *x; + + if (!(x = (struct xftype *)malloc(sizeof(struct xftype)))) return ENOMEM; + memset(x, 0, sizeof(*x)); + x->next = xftypes; + x->name = name; + x->do_on = do_on; + xftypes = x; +} + + +static void register_default_types(void) +{ + xfregister("FILE", xfon_path); + xfregister("FD", xfon_fd); + xfregister("AFSDUMP", xfon_voldump); + xfregister("PROFILE", xfon_profile); + did_register_defaults = 1; +} + + +afs_uint32 xfopen(XFILE *X, int flag, char *name) +{ + struct xftype *x; + char *type, *sep; + + if (!did_register_defaults) register_default_types(); + if (!strcmp(name, "-")) return xfon_stdio(X, flag); + + for (type = name; *name && *name != ':'; name++); + if (*name) { + sep = name; + *name++ = 0; + } else { + sep = 0; + name = type; + type = "FILE"; + } + + for (x = xftypes; x; x = x->next) + if (!strcmp(type, x->name)) break; + if (sep) *sep = ':'; + if (x) return (x->do_on)(X, flag, name); + return ERROR_XFILE_TYPE; +} diff --git a/src/tests/xfiles.h b/src/tests/xfiles.h new file mode 100644 index 0000000000..661ac234c3 --- /dev/null +++ b/src/tests/xfiles.h @@ -0,0 +1,88 @@ +/* + * CMUCS AFStools + * dumpscan - routines for scanning and manipulating AFS volume dumps + * + * Copyright (c) 1998 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software_Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ + +/* xfiles.h - Type, constant, and function declarations for + * extensible file-like things */ + +#ifndef _XFILES_H_ +#define _XFILES_H_ + +#include +#include +#include "intNN.h" + +struct rx_call; +struct rx_connection; + +/* The XFILE structure */ +typedef struct XFILE XFILE; +struct XFILE { + afs_uint32 (*do_read)(XFILE *, void *, afs_uint32); /* read data */ + afs_uint32 (*do_write)(XFILE *, void *, afs_uint32); /* write data */ + afs_uint32 (*do_tell)(XFILE *, u_int64 *); /* find position */ + afs_uint32 (*do_seek)(XFILE *, u_int64 *); /* set position */ + afs_uint32 (*do_skip)(XFILE *, afs_uint32); /* skip forward */ + afs_uint32 (*do_close)(XFILE *); /* close */ + u_int64 filepos; /* position (counted) */ + int is_seekable; /* 1 if seek works */ + int is_writable; /* 1 if write works */ + XFILE *passthru; /* XFILE to pass thru to */ + void *refcon; /* type-specific data */ +}; + + +/* Functions for opening XFILEs. For these, the first two arguments are + * always a pointer to an XFILE to fill in, and the mode in which to + * open the file. O_RDONLY and O_RDWR are permitted; O_WRONLY is not. + * Other open modes may or may not be used, depending on the object type. + * Remaining arguments are a function of the object type + */ +extern afs_uint32 xfopen (XFILE *, int, char *); /* open by TYPE:name */ +extern afs_uint32 xfopen_path(XFILE *, int, char *, int); /* open by path */ +extern afs_uint32 xfopen_FILE(XFILE *, int, FILE *); /* open by FILE * */ +extern afs_uint32 xfopen_fd (XFILE *, int, int); /* open by fd */ +extern afs_uint32 xfopen_rxcall (XFILE *, int, struct rx_call *); +extern afs_uint32 xfopen_voldump(XFILE *, struct rx_connection *, + afs_int32, afs_int32, afs_int32); +extern afs_uint32 xfopen_profile(XFILE *, int, char *, char *); + +extern afs_uint32 xfregister(char *, afs_uint32 (*)(XFILE *, int, char *)); + +/* Standard operations on XFILEs */ +extern afs_uint32 xfread(XFILE *, void *, afs_uint32); /* read data */ +extern afs_uint32 xfwrite(XFILE *, void *, afs_uint32); /* write data */ +extern afs_uint32 xfprintf(XFILE *, char *, ...); /* formatted */ +extern afs_uint32 vxfprintf(XFILE *, char *, va_list); /* formatted VA */ +extern afs_uint32 xftell(XFILE *, u_int64 *); /* get position */ +extern afs_uint32 xfseek(XFILE *, u_int64 *); /* set position */ +extern afs_uint32 xfskip(XFILE *, afs_uint32); /* skip forward */ +extern afs_uint32 xfpass(XFILE *, XFILE *); /* set passthru */ +extern afs_uint32 xfunpass(XFILE *); /* unset passthru */ +extern afs_uint32 xfclose(XFILE *); /* close */ + +#endif /* _XFILES_H_ */