2 * Copyright 2000, International Business Machines Corporation and others.
5 * This software has been released under the terms of the IBM Public
6 * License. For details, see the LICENSE file in the top-level source
7 * directory or online at http://www.openafs.org/dl/license10.html
13 Institution: The Information Technology Center, Carnegie-Mellon University
17 #include <afsconfig.h>
18 #include <afs/param.h>
24 #ifdef HAVE_SYS_FILE_H
30 #include <afs/afsint.h>
31 #include <afs/errors.h>
32 #include <opr/queue.h>
39 #include "partition.h"
42 #include "AFS_component_version_number.c"
45 static const char *progname = "volinfo";
47 /* Command line options */
64 /* Vnode details for vnode handling procedures */
68 VnodeDiskObject *vnode;
75 static int DumpInfo = 0; /**< Dump volume information */
76 static int DumpHeader = 0; /**< Dump volume header files info */
77 static int DumpVnodes = 0; /**< Dump vnode info */
78 static int DumpInodeNumber = 0; /**< Dump inode numbers with vnodes */
79 static int DumpDate = 0; /**< Dump vnode date (server modify date) with vnode */
80 static int InodeTimes = 0; /**< Dump some of the dates associated with inodes */
81 #if defined(AFS_NAMEI_ENV)
82 static int PrintFileNames = 0; /**< Dump vnode and special file name filenames */
84 static int ShowOrphaned = 0; /**< Show "orphaned" vnodes (vnodes with parent of 0) */
85 static int ShowSizes = 0; /**< Show volume size summary */
86 static int SaveInodes = 0; /**< Save vnode data to files */
87 static int FixHeader = 0; /**< Repair header files magic and version fields. */
90 * Volume size running totals
93 afs_uint64 diskused_k; /**< volume size from disk data file, in kilobytes */
94 afs_uint64 auxsize; /**< size of header files, in bytes */
95 afs_uint64 auxsize_k; /**< size of header files, in kilobytes */
96 afs_uint64 vnodesize; /**< size of the large and small vnodes, in bytes */
97 afs_uint64 vnodesize_k; /**< size of the large and small vnodes, in kilobytes */
98 afs_uint64 size_k; /**< size of headers and vnodes, in kilobytes */
101 static struct sizeTotals volumeTotals = { 0, 0, 0, 0, 0, 0 };
102 static struct sizeTotals partitionTotals = { 0, 0, 0, 0, 0, 0 };
103 static struct sizeTotals serverTotals = { 0, 0, 0, 0, 0, 0 };
104 static int PrintingVolumeSizes = 0; /*print volume size lines */
107 * List of procedures to call when scanning vnodes.
109 struct VnodeScanProc {
110 struct opr_queue link;
112 void (*proc) (struct VnodeDetails * vdp);
114 static struct opr_queue VnodeScanLists[nVNODECLASSES];
116 /* Forward Declarations */
117 void PrintHeader(Volume * vp);
118 void HandleAllPart(void);
119 void HandlePart(struct DiskPartition64 *partP);
120 void HandleVolume(struct DiskPartition64 *partP, char *name);
121 struct DiskPartition64 *FindCurrentPartition(void);
122 Volume *AttachVolume(struct DiskPartition64 *dp, char *volname,
123 struct VolumeHeader *header);
124 void HandleVnodes(Volume * vp, VnodeClass class);
125 static void AddVnodeToSizeTotals(struct VnodeDetails *vdp);
126 static void SaveInode(struct VnodeDetails *vdp);
127 static void PrintVnode(struct VnodeDetails *vdp);
130 * Format time as a timestamp string
132 * @param[in] date time value to format
134 * @return timestamp string in the form YYYY/MM/DD.hh:mm:ss
136 * @note A static array of 8 strings are stored by this
137 * function. The array slots are overwritten, so the
138 * caller must not reference the returned string after
139 * seven additional calls to this function.
144 #define MAX_DATE_RESULT 100
145 static char results[8][MAX_DATE_RESULT];
147 struct tm *tm = localtime(&date);
150 (void)strftime(buf, 32, "%Y/%m/%d.%H:%M:%S", tm); /* NT does not have %T */
151 snprintf(results[next = (next + 1) & 7], MAX_DATE_RESULT,
152 "%lu (%s)", (unsigned long)date, buf);
153 return results[next];
158 * Format file time as a timestamp string
160 * @param[in] ft file time
162 * @return timestamp string in the form YYYY/MM/DD.hh:mm:ss
164 * @note A static array of 8 strings are stored by this
165 * function. The array slots are overwritten, so the
166 * caller must not reference the returned string after
167 * seven additional calls to this function.
170 NT_date(FILETIME * ft)
172 static char result[8][64];
177 if (!FileTimeToLocalFileTime(ft, &lft)
178 || !FileTimeToSystemTime(&lft, &st)) {
179 fprintf(stderr, "%s: Time conversion failed.\n", progname);
182 sprintf(result[next = ((next + 1) & 7)], "%4d/%02d/%02d.%2d:%2d:%2d",
183 st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond);
189 * Add vnode size to the running volume totals.
191 * @param[in] vdp vnode details object
196 AddVnodeToSizeTotals(struct VnodeDetails *vdp)
198 afs_fsize_t fileLength;
200 VNDISK_GET_LEN(fileLength, vdp->vnode);
201 if (fileLength > 0) {
202 volumeTotals.vnodesize += fileLength;
203 volumeTotals.vnodesize_k += fileLength / 1024;
208 * Print the volume size table heading line, if needed.
213 PrintVolumeSizeHeading(void)
215 if (!PrintingVolumeSizes) {
217 ("Volume-Id\t Volsize Auxsize Inodesize AVolsize SizeDiff (VolName)\n");
218 PrintingVolumeSizes = 1;
228 AddSizeTotals(struct sizeTotals *a, struct sizeTotals *b)
230 a->diskused_k += b->diskused_k;
231 a->auxsize += b->auxsize;
232 a->auxsize_k += b->auxsize_k;
233 a->vnodesize += b->vnodesize;
234 a->vnodesize_k += b->vnodesize_k;
235 a->size_k += b->size_k;
239 * Print the sizes for a volume.
244 PrintVolumeSizes(Volume * vp)
246 afs_int64 diff_k = volumeTotals.size_k - volumeTotals.diskused_k;
248 PrintVolumeSizeHeading();
249 printf("%u\t%9llu%9llu%10llu%10llu%9lld\t%24s\n",
251 volumeTotals.diskused_k,
252 volumeTotals.auxsize_k, volumeTotals.vnodesize_k,
253 volumeTotals.size_k, diff_k, V_name(vp));
257 * Print the size totals for the partition.
262 PrintPartitionTotals(afs_uint64 nvols)
264 afs_int64 diff_k = partitionTotals.size_k - partitionTotals.diskused_k;
266 PrintVolumeSizeHeading();
267 printf("\nPart Totals %12llu%9llu%10llu%10llu%9lld (%llu volumes)\n\n",
268 partitionTotals.diskused_k, partitionTotals.auxsize,
269 partitionTotals.vnodesize, partitionTotals.size_k, diff_k, nvols);
270 PrintingVolumeSizes = 0;
274 * Print the size totals for all the partitions.
279 PrintServerTotals(void)
281 afs_int64 diff_k = serverTotals.size_k - serverTotals.diskused_k;
283 printf("\nServer Totals%12lld%9lld%10lld%10lld%9lld\n",
284 serverTotals.diskused_k, serverTotals.auxsize,
285 serverTotals.vnodesize, serverTotals.size_k, diff_k);
289 * Read a volume header file
291 * @param[in] ih ihandle of the header file
292 * @param[in] to destination
293 * @param[in] size expected header size
294 * @param[in] magic expected header magic number
295 * @param[in] version expected header version number
299 * @retval -1 failed to read file
302 ReadHdr1(IHandle_t * ih, char *to, int size, u_int magic, u_int version)
304 struct versionStamp *vsn;
308 vsn = (struct versionStamp *)to;
310 code = IH_IREAD(ih, 0, to, size);
314 if (vsn->magic != magic) {
316 fprintf(stderr, "%s: Inode %s: Bad magic %x (%x): IGNORED\n",
317 progname, PrintInode(NULL, ih->ih_ino), vsn->magic, magic);
320 /* Check is conditional, in case caller wants to inspect version himself */
321 if (version && vsn->version != version) {
323 fprintf(stderr, "%s: Inode %s: Bad version %x (%x): IGNORED\n",
325 PrintInode(NULL, ih->ih_ino), vsn->version, version);
327 if (bad && FixHeader) {
329 vsn->version = version;
330 printf("Special index inode %s has a bad header. Reconstructing...\n",
331 PrintInode(NULL, ih->ih_ino));
332 code = IH_IWRITE(ih, 0, to, size);
335 "%s: Write failed for inode %s; header left in damaged state\n",
336 progname, PrintInode(NULL, ih->ih_ino));
339 if (!bad && DumpInfo) {
340 printf("Inode %s: Good magic %x and version %x\n",
341 PrintInode(NULL, ih->ih_ino), magic, version);
347 * Simplified attach volume
349 * param[in] dp vice disk partition object
350 * param[in] volname volume header file name
351 * param[in] header volume header object
353 * @return volume object or null on error
356 AttachVolume(struct DiskPartition64 * dp, char *volname,
357 struct VolumeHeader * header)
362 vp = (Volume *) calloc(1, sizeof(Volume));
364 fprintf(stderr, "%s: Failed to allocate volume object.\n", progname);
367 vp->specialStatus = 0;
368 vp->device = dp->device;
370 IH_INIT(vp->vnodeIndex[vLarge].handle, dp->device, header->parent,
371 header->largeVnodeIndex);
372 IH_INIT(vp->vnodeIndex[vSmall].handle, dp->device, header->parent,
373 header->smallVnodeIndex);
374 IH_INIT(vp->diskDataHandle, dp->device, header->parent,
376 IH_INIT(V_linkHandle(vp), dp->device, header->parent, header->linkTable);
377 vp->cacheCheck = 0; /* XXXX */
378 vp->shuttingDown = 0;
379 vp->goingOffline = 0;
381 vp->header = (struct volHeader *)calloc(1, sizeof(*vp->header));
383 fprintf(stderr, "%s: Failed to allocate volume header.\n", progname);
387 ec = ReadHdr1(V_diskDataHandle(vp), (char *)&V_disk(vp),
388 sizeof(V_disk(vp)), VOLUMEINFOMAGIC, VOLUMEINFOVERSION);
390 struct IndexFileHeader iHead;
391 ec = ReadHdr1(vp->vnodeIndex[vSmall].handle, (char *)&iHead,
392 sizeof(iHead), SMALLINDEXMAGIC, SMALLINDEXVERSION);
395 struct IndexFileHeader iHead;
396 ec = ReadHdr1(vp->vnodeIndex[vLarge].handle, (char *)&iHead,
397 sizeof(iHead), LARGEINDEXMAGIC, LARGEINDEXVERSION);
401 struct versionStamp stamp;
402 ec = ReadHdr1(V_linkHandle(vp), (char *)&stamp, sizeof(stamp),
403 LINKTABLEMAGIC, LINKTABLEVERSION);
412 * Simplified detach volume
414 * param[in] vp volume object from AttachVolume
419 DetachVolume(Volume * vp)
421 IH_RELEASE(vp->vnodeIndex[vLarge].handle);
422 IH_RELEASE(vp->vnodeIndex[vSmall].handle);
423 IH_RELEASE(vp->diskDataHandle);
424 IH_RELEASE(V_linkHandle(vp));
430 * Convert the partition device number into a partition name.
432 * @param[in] partId partition number, 0 to 254
433 * @param[out] partName buffer to hold partition name (e.g. /vicepa)
434 * @param[in] partNameSize size of partName buffer
438 * @retval -1 error, partId is out of range
439 * @retval -2 error, partition name exceeds partNameSize
442 GetPartitionName(afs_uint32 partId, char *partName, afs_size_t partNameSize)
444 const int OLD_NUM_DEVICES = 26; /* a..z */
446 if (partId < OLD_NUM_DEVICES) {
447 if (partNameSize < 8) {
450 strlcpy(partName, "/vicep", partNameSize);
451 partName[6] = partId + 'a';
455 if (partId < VOLMAXPARTS) {
456 if (partNameSize < 9) {
459 strlcpy(partName, "/vicep", partNameSize);
460 partId -= OLD_NUM_DEVICES;
461 partName[6] = 'a' + (partId / OLD_NUM_DEVICES);
462 partName[7] = 'a' + (partId % OLD_NUM_DEVICES);
470 * Scan the volumes in the partitions
472 * Scan the specified volume in the specified partition if both
473 * are given. Scan all the volumes in the specified partition if
474 * only the partition is given. If neither a partition nor volume
475 * is given, scan all the volumes in all the partitions. If only
476 * the volume is given, attempt to find it in the current working
479 * @param[in] partNameOrId partition name or id to be scannned
480 * @param[in] volumeId volume id to be scanned
482 * @return 0 if successful
485 ScanPartitions(char *partNameOrId, afs_uint32 volumeId)
488 char partName[64] = "";
489 struct DiskPartition64 *partP = NULL;
492 if (geteuid() != 0) {
493 fprintf(stderr, "%s: Must be run as root; sorry\n", progname);
500 /* Allow user to specify partition by name or id. */
502 afs_uint32 partId = volutil_GetPartitionID(partNameOrId);
504 fprintf(stderr, "%s: Could not parse '%s' as a partition name.\n",
505 progname, partNameOrId);
508 if (GetPartitionName(partId, partName, sizeof(partName))) {
510 "%s: Could not format '%s' as a partition name.\n",
511 progname, partNameOrId);
516 err = VAttachPartitions();
518 fprintf(stderr, "%s: %d partitions had errors during attach.\n",
524 partP = VGetPartition(partName, 0);
527 "%s: %s is not an AFS partition name on this server.\n",
543 partP = FindCurrentPartition();
546 "%s: Current partition is not a vice partition.\n",
551 snprintf(name1, sizeof name1, VFORMAT,
552 afs_printable_uint32_lu(volumeId));
553 HandleVolume(partP, name1);
560 * Add a vnode scanning procedure
562 * @param[in] class vnode class for this handler
563 * @param[in] proc handler proc to be called by HandleVnodes
564 * @param[in] heading optional string to pring before scanning vnodes
569 AddVnodeHandler(VnodeClass class, void (*proc) (struct VnodeDetails * vdp),
572 struct VnodeScanProc *entry = malloc(sizeof(struct VnodeScanProc));
574 entry->heading = heading;
575 opr_queue_Append(&VnodeScanLists[class], (struct opr_queue *)entry);
579 * Process command line options and start scanning
581 * @param[in] as command syntax object
582 * @param[in] arock opaque object; not used
587 handleit(struct cmd_syndesc *as, void *arock)
590 afs_uint32 volumeId = 0;
591 char *partNameOrId = NULL;
593 DumpInfo = 1; /* volinfo default mode */
595 if (as->parms[P_ONLINE].items) {
596 fprintf(stderr, "%s: -online not supported\n", progname);
599 if (as->parms[P_VNODE].items) {
602 if (as->parms[P_DATE].items) {
605 if (as->parms[P_INODE].items) {
608 if (as->parms[P_ITIME].items) {
611 if ((ti = as->parms[P_PART].items)) {
612 partNameOrId = ti->data;
614 if ((ti = as->parms[P_VOLUMEID].items)) {
615 volumeId = strtoul(ti->data, NULL, 10);
617 if (as->parms[P_HEADER].items) {
620 if (as->parms[P_SIZEONLY].items) {
623 if (as->parms[P_FIXHEADER].items) {
626 if (as->parms[P_SAVEINODES].items) {
629 if (as->parms[P_ORPHANED].items) {
632 #if defined(AFS_NAMEI_ENV)
633 if (as->parms[P_FILENAMES].items) {
638 /* -saveinodes and -sizeOnly override the default mode.
639 * For compatibility with old versions of volinfo, -orphaned
640 * and -filename options imply -vnodes */
641 if (SaveInodes || ShowSizes) {
647 } else if (ShowOrphaned) {
648 DumpVnodes = 1; /* implied */
650 } else if (PrintFileNames) {
651 DumpVnodes = 1; /* implied */
656 AddVnodeHandler(vSmall, SaveInode,
657 "Saving all volume files to current directory ...\n");
660 AddVnodeHandler(vLarge, AddVnodeToSizeTotals, NULL);
661 AddVnodeHandler(vSmall, AddVnodeToSizeTotals, NULL);
664 AddVnodeHandler(vLarge, PrintVnode, "\nLarge vnodes (directories)\n");
665 AddVnodeHandler(vSmall, PrintVnode,
666 "\nSmall vnodes(files, symbolic links)\n");
669 return ScanPartitions(partNameOrId, volumeId);
673 * Determine if the current directory is a vice partition
675 * @return disk partition object
679 struct DiskPartition64 *
680 FindCurrentPartition(void)
682 int dr = _getdrive();
683 struct DiskPartition64 *dp;
686 for (dp = DiskPartitionList; dp; dp = dp->next) {
687 if (*dp->devName - 'A' == dr)
691 fprintf(stderr, "%s: Current drive is not a valid vice partition.\n",
697 struct DiskPartition64 *
698 FindCurrentPartition(void)
703 struct DiskPartition64 *dp;
705 if (!getcwd(partName, 1023)) {
706 fprintf(stderr, "%s: Failed to get current working directory: ",
711 p = strchr(&partName[1], OS_DIRSEPC);
716 if (!(dp = VGetPartition(partName, 0))) {
719 fprintf(stderr, "%s: %s is not a valid vice partition.\n", progname,
728 * Scan and handle all the partitions detected on this server
735 struct DiskPartition64 *partP;
738 for (partP = DiskPartitionList; partP; partP = partP->next) {
739 if (DumpInfo || SaveInodes || ShowSizes) {
740 printf("Processing Partition %s:\n", partP->name);
744 AddSizeTotals(&serverTotals, &partitionTotals);
754 * Scan the partition and handle volumes
756 * @param[in] partP disk partition to scan
761 HandlePart(struct DiskPartition64 *partP)
769 (void)sprintf(pname, "%s\\", VPartitionPath(partP));
771 char *p = VPartitionPath(partP);
774 if ((dirp = opendir(p)) == NULL) {
775 fprintf(stderr, "%s: Can't read directory %s; giving up\n", progname,
779 while ((dp = readdir(dirp))) {
780 p = (char *)strrchr(dp->d_name, '.');
781 if (p != NULL && strcmp(p, VHDREXT) == 0) {
782 HandleVolume(partP, dp->d_name);
785 AddSizeTotals(&partitionTotals, &volumeTotals);
791 PrintPartitionTotals(nvols);
796 * Inspect a volume header special file.
798 * @param[in] name descriptive name of the type of header special file
799 * @param[in] dp partition object for this volume
800 * @param[in] header header object for this volume
801 * @param[in] inode fileserver inode number for this header special file
802 * @param[out] psize total of the header special file
807 HandleSpecialFile(const char *name, struct DiskPartition64 *dp,
808 struct VolumeHeader *header, Inode inode,
809 afs_sfsize_t * psize)
811 afs_sfsize_t size = 0;
812 IHandle_t *ih = NULL;
813 FdHandle_t *fdP = NULL;
816 #endif /* AFS_NAMEI_ENV */
818 IH_INIT(ih, dp->device, header->parent, inode);
822 "%s: Error opening header file '%s' for volume %u", progname,
827 size = FDH_SIZE(fdP);
830 "%s: Error getting size of header file '%s' for volume %u",
831 progname, name, header->id);
836 printf("\t%s inode\t= %s (size = %lld)\n",
837 name, PrintInode(NULL, inode), size);
839 namei_HandleToName(&filename, ih);
840 printf("\t%s namei\t= %s\n", name, filename.n_path);
841 #endif /* AFS_NAMEI_ENV */
847 FDH_REALLYCLOSE(fdP);
855 * Inspect this volume header files.
857 * @param[in] dp partition object for this volume
858 * @param[in] header_fd volume header file descriptor
859 * @param[in] header volume header object
860 * @param[out] psize total of the header special file
865 HandleHeaderFiles(struct DiskPartition64 *dp, FD_t header_fd,
866 struct VolumeHeader *header)
868 afs_sfsize_t size = 0;
871 size = OS_SIZE(header_fd);
872 printf("Volume header (size = %lld):\n", size);
873 printf("\tstamp\t= 0x%x\n", header->stamp.version);
874 printf("\tVolId\t= %u\n", header->id);
875 printf("\tparent\t= %u\n", header->parent);
878 HandleSpecialFile("Info", dp, header, header->volumeInfo, &size);
879 HandleSpecialFile("Small", dp, header, header->smallVnodeIndex,
881 HandleSpecialFile("Large", dp, header, header->largeVnodeIndex,
884 HandleSpecialFile("Link", dp, header, header->linkTable, &size);
885 #endif /* AFS_NAMEI_ENV */
888 printf("Total aux volume size = %lld\n\n", size);
892 volumeTotals.auxsize = size;
893 volumeTotals.auxsize_k = size / 1024;
898 * Attach and scan the volume and handle the header and vnodes
900 * Print the volume header and vnode information, depending on the
903 * @param[in] dp vice partition object for this volume
904 * @param[in] name volume header file name
909 HandleVolume(struct DiskPartition64 *dp, char *name)
911 struct VolumeHeader header;
912 struct VolumeDiskHeader diskHeader;
913 FD_t fd = INVALID_FD;
915 char headerName[1024];
918 snprintf(headerName, sizeof headerName, "%s" OS_DIRSEP "%s",
919 VPartitionPath(dp), name);
920 if ((fd = OS_OPEN(headerName, O_RDONLY, 0666)) == INVALID_FD) {
921 fprintf(stderr, "%s: Cannot open volume header %s\n", progname, name);
924 if (OS_SIZE(fd) < 0) {
925 fprintf(stderr, "%s: Cannot read volume header %s\n", progname, name);
928 n = OS_READ(fd, &diskHeader, sizeof(diskHeader));
929 if (n != sizeof(diskHeader)
930 || diskHeader.stamp.magic != VOLUMEHEADERMAGIC) {
931 fprintf(stderr, "%s: Error reading volume header %s\n", progname,
935 if (diskHeader.stamp.version != VOLUMEHEADERVERSION) {
937 "%s: Volume %s, version number is incorrect; volume needs to be salvaged\n",
942 DiskToVolumeHeader(&header, &diskHeader);
943 if (DumpHeader || ShowSizes) {
944 HandleHeaderFiles(dp, fd, &header);
947 vp = AttachVolume(dp, name, &header);
949 fprintf(stderr, "%s: Error attaching volume header %s\n",
958 HandleVnodes(vp, vLarge);
959 HandleVnodes(vp, vSmall);
962 volumeTotals.diskused_k = V_diskused(vp);
963 volumeTotals.size_k =
964 volumeTotals.auxsize_k + volumeTotals.vnodesize_k;
966 PrintingVolumeSizes = 0; /* print heading again */
968 PrintVolumeSizes(vp);
972 if (fd != INVALID_FD) {
981 * volinfo program entry
984 main(int argc, char **argv)
986 struct cmd_syndesc *ts;
989 opr_queue_Init(&VnodeScanLists[vLarge]);
990 opr_queue_Init(&VnodeScanLists[vSmall]);
992 ts = cmd_CreateSyntax(NULL, handleit, NULL,
993 "Dump volume's internal state");
994 cmd_AddParmAtOffset(ts, P_ONLINE, "-online", CMD_FLAG, CMD_OPTIONAL,
995 "Get info from running fileserver");
996 cmd_AddParmAtOffset(ts, P_VNODE, "-vnode", CMD_FLAG, CMD_OPTIONAL,
998 cmd_AddParmAtOffset(ts, P_DATE, "-date", CMD_FLAG, CMD_OPTIONAL,
999 "Also dump vnode's mod date");
1000 cmd_AddParmAtOffset(ts, P_INODE, "-inode", CMD_FLAG, CMD_OPTIONAL,
1001 "Also dump vnode's inode number");
1002 cmd_AddParmAtOffset(ts, P_ITIME, "-itime", CMD_FLAG, CMD_OPTIONAL,
1003 "Dump special inode's mod times");
1004 cmd_AddParmAtOffset(ts, P_PART, "-part", CMD_LIST, CMD_OPTIONAL,
1005 "AFS partition name or id (default current partition)");
1006 cmd_AddParmAtOffset(ts, P_VOLUMEID, "-volumeid", CMD_LIST, CMD_OPTIONAL,
1008 cmd_AddParmAtOffset(ts, P_HEADER, "-header", CMD_FLAG, CMD_OPTIONAL,
1009 "Dump volume's header info");
1010 cmd_AddParmAtOffset(ts, P_SIZEONLY, "-sizeonly", CMD_FLAG, CMD_OPTIONAL,
1011 "Dump volume's size");
1012 cmd_AddParmAtOffset(ts, P_FIXHEADER, "-fixheader", CMD_FLAG,
1013 CMD_OPTIONAL, "Try to fix header");
1014 cmd_AddParmAtOffset(ts, P_SAVEINODES, "-saveinodes", CMD_FLAG,
1015 CMD_OPTIONAL, "Try to save all inodes");
1016 cmd_AddParmAtOffset(ts, P_ORPHANED, "-orphaned", CMD_FLAG, CMD_OPTIONAL,
1017 "List all dir/files without a parent");
1018 #if defined(AFS_NAMEI_ENV)
1019 cmd_AddParmAtOffset(ts, P_FILENAMES, "-filenames", CMD_FLAG,
1020 CMD_OPTIONAL, "Also dump vnode's namei filename");
1023 /* For compatibility with older versions. */
1024 cmd_AddParmAlias(ts, P_SIZEONLY, "-sizeOnly");
1026 code = cmd_Dispatch(argc, argv);
1031 * Return a display string for the volume type.
1033 * @param[in] type volume type
1035 * @return volume type description string
1037 static_inline char *
1038 volumeTypeString(int type)
1041 (type == RWVOL ? "read/write" :
1042 (type == ROVOL ? "readonly" :
1043 (type == BACKVOL ? "backup" : "unknown")));
1047 * Print the volume header information
1049 * @param[in] volume object
1054 PrintHeader(Volume * vp)
1056 printf("Volume header for volume %u (%s)\n", V_id(vp), V_name(vp));
1057 printf("stamp.magic = %x, stamp.version = %u\n", V_stamp(vp).magic,
1058 V_stamp(vp).version);
1060 ("inUse = %d, inService = %d, blessed = %d, needsSalvaged = %d, dontSalvage = %d\n",
1061 V_inUse(vp), V_inService(vp), V_blessed(vp), V_needsSalvaged(vp),
1064 ("type = %d (%s), uniquifier = %u, needsCallback = %d, destroyMe = %x\n",
1065 V_type(vp), volumeTypeString(V_type(vp)), V_uniquifier(vp),
1066 V_needsCallback(vp), V_destroyMe(vp));
1068 ("id = %u, parentId = %u, cloneId = %u, backupId = %u, restoredFromId = %u\n",
1069 V_id(vp), V_parentId(vp), V_cloneId(vp), V_backupId(vp),
1070 V_restoredFromId(vp));
1072 ("maxquota = %d, minquota = %d, maxfiles = %d, filecount = %d, diskused = %d\n",
1073 V_maxquota(vp), V_minquota(vp), V_maxfiles(vp), V_filecount(vp),
1075 printf("creationDate = %s, copyDate = %s\n", date(V_creationDate(vp)),
1076 date(V_copyDate(vp)));
1077 printf("backupDate = %s, expirationDate = %s\n", date(V_backupDate(vp)),
1078 date(V_expirationDate(vp)));
1079 printf("accessDate = %s, updateDate = %s\n", date(V_accessDate(vp)),
1080 date(V_updateDate(vp)));
1081 printf("owner = %u, accountNumber = %u\n", V_owner(vp),
1082 V_accountNumber(vp));
1084 ("dayUse = %u; week = (%u, %u, %u, %u, %u, %u, %u), dayUseDate = %s\n",
1085 V_dayUse(vp), V_weekUse(vp)[0], V_weekUse(vp)[1], V_weekUse(vp)[2],
1086 V_weekUse(vp)[3], V_weekUse(vp)[4], V_weekUse(vp)[5],
1087 V_weekUse(vp)[6], date(V_dayUseDate(vp)));
1088 printf("volUpdateCounter = %u\n", V_volUpCounter(vp));
1092 * Get the size and times of a file
1094 * @param[in] fd file descriptor of file to stat
1095 * @param[out] size size of the file
1096 * @param[out] ctime ctime of file as a formatted string
1097 * @param[out] mtime mtime of file as a formatted string
1098 * @param[out] atime atime of file as a formatted string
1100 * @return error code
1102 * @retval -1 failed to retrieve file information
1105 GetFileInfo(FD_t fd, afs_sfsize_t * size, char **ctime, char **mtime,
1109 BY_HANDLE_FILE_INFORMATION fi;
1110 LARGE_INTEGER fsize;
1111 if (!GetFileInformationByHandle(fd, &fi)) {
1112 fprintf(stderr, "%s: GetFileInformationByHandle failed\n", progname);
1115 if (!GetFileSizeEx(fd, &fsize)) {
1116 fprintf(stderr, "%s: GetFileSizeEx failed\n", progname);
1119 *size = fsize.QuadPart;
1121 *mtime = NT_date(&fi.ftLastWriteTime);
1122 *atime = NT_date(&fi.ftLastAccessTime);
1124 struct afs_stat_st status;
1125 if (afs_fstat(fd, &status) == -1) {
1126 fprintf(stderr, "%s: fstat failed %d\n", progname, errno);
1129 *size = status.st_size;
1130 *ctime = date(status.st_ctime);
1131 *mtime = date(status.st_mtime);
1132 *atime = date(status.st_atime);
1138 * Copy the inode data to a file in the current directory.
1140 * @param[in] vdp vnode details object
1145 SaveInode(struct VnodeDetails *vdp)
1149 char nfile[50], buffer[256];
1153 Inode ino = VNDISK_GET_INO(vdp->vnode);
1155 if (!VALID_INO(ino)) {
1159 IH_INIT(ih, V_device(vdp->vp), V_parentId(vdp->vp), ino);
1163 "%s: Can't open inode %s error %d (ignored)\n",
1164 progname, PrintInode(NULL, ino), errno);
1167 snprintf(nfile, sizeof nfile, "TmpInode.%s", PrintInode(NULL, ino));
1168 ofd = afs_open(nfile, O_CREAT | O_RDWR | O_TRUNC, 0600);
1171 "%s: Can't create file %s; error %d (ignored)\n",
1172 progname, nfile, errno);
1174 FDH_REALLYCLOSE(fdP);
1181 len = FDH_PREAD(fdP, buffer, sizeof(buffer), total);
1183 FDH_REALLYCLOSE(fdP);
1188 "%s: Error while reading from inode %s (%d)\n",
1189 progname, PrintInode(NULL, ino), errno);
1193 break; /* No more input */
1194 nBytes = write(ofd, buffer, len);
1195 if (nBytes != len) {
1196 FDH_REALLYCLOSE(fdP);
1201 "%s: Error while writing to \"%s\" (%d - ignored)\n",
1202 progname, nfile, errno);
1208 FDH_REALLYCLOSE(fdP);
1211 printf("... Copied inode %s to file %s (%lu bytes)\n",
1212 PrintInode(NULL, ino), nfile, (unsigned long)total);
1216 * Scan a volume index and handle each vnode
1218 * @param[in] vp volume object
1219 * @param[in] class which index to scan
1224 HandleVnodes(Volume * vp, VnodeClass class)
1226 afs_int32 diskSize =
1227 (class == vSmall ? SIZEOF_SMALLDISKVNODE : SIZEOF_LARGEDISKVNODE);
1228 char buf[SIZEOF_LARGEDISKVNODE];
1229 struct VnodeDiskObject *vnode = (struct VnodeDiskObject *)buf;
1230 StreamHandle_t *file = NULL;
1232 afs_sfsize_t nVnodes;
1233 afs_foff_t offset = 0;
1234 IHandle_t *ih = vp->vnodeIndex[class].handle;
1235 FdHandle_t *fdP = NULL;
1237 char *ctime, *atime, *mtime;
1238 struct opr_queue *scanList = &VnodeScanLists[class];
1239 struct opr_queue *cursor;
1241 if (opr_queue_IsEmpty(scanList)) {
1245 for (opr_queue_Scan(scanList, cursor)) {
1246 struct VnodeScanProc *entry = (struct VnodeScanProc *)cursor;
1247 if (entry->heading) {
1248 printf(entry->heading);
1254 fprintf(stderr, "%s: open failed: ", progname);
1258 file = FDH_FDOPEN(fdP, "r");
1260 fprintf(stderr, "%s: fdopen failed\n", progname);
1264 if (GetFileInfo(fdP->fd_fd, &size, &ctime, &atime, &mtime) != 0) {
1268 printf("ichanged : %s\nimodified: %s\niaccessed: %s\n\n", ctime,
1272 nVnodes = (size / diskSize) - 1;
1274 STREAM_ASEEK(file, diskSize);
1278 for (vnodeIndex = 0;
1279 nVnodes && STREAM_READ(vnode, diskSize, 1, file) == 1;
1280 nVnodes--, vnodeIndex++, offset += diskSize) {
1282 struct VnodeDetails vnodeDetails;
1284 vnodeDetails.vp = vp;
1285 vnodeDetails.class = class;
1286 vnodeDetails.vnode = vnode;
1287 vnodeDetails.vnodeNumber = bitNumberToVnodeNumber(vnodeIndex, class);
1288 vnodeDetails.offset = offset;
1289 vnodeDetails.index = vnodeIndex;
1291 for (opr_queue_Scan(scanList, cursor)) {
1292 struct VnodeScanProc *entry = (struct VnodeScanProc *)cursor;
1294 (*entry->proc) (&vnodeDetails);
1309 * Print vnode information
1311 * @param[in] vdp vnode details object
1316 PrintVnode(struct VnodeDetails *vdp)
1318 #if defined(AFS_NAMEI_ENV)
1322 afs_foff_t offset = vdp->offset;
1323 VnodeDiskObject *vnode = vdp->vnode;
1324 afs_fsize_t fileLength;
1327 ino = VNDISK_GET_INO(vnode);
1328 VNDISK_GET_LEN(fileLength, vnode);
1330 /* The check for orphaned vnodes is currently limited to non-empty
1331 * vnodes with a parent of zero (and which are not the first entry
1333 if (ShowOrphaned && (fileLength == 0 || vnode->parent || !offset))
1337 ("%10lld Vnode %u.%u.%u cloned: %u, length: %llu linkCount: %d parent: %u",
1338 (long long)offset, vdp->vnodeNumber, vnode->uniquifier,
1339 vnode->dataVersion, vnode->cloned, (afs_uintmax_t) fileLength,
1340 vnode->linkCount, vnode->parent);
1341 if (DumpInodeNumber)
1342 printf(" inode: %s", PrintInode(NULL, ino));
1344 printf(" ServerModTime: %s", date(vnode->serverModifyTime));
1345 #if defined(AFS_NAMEI_ENV)
1346 if (PrintFileNames) {
1347 IH_INIT(ihtmpp, V_device(vdp->vp), V_parentId(vdp->vp), ino);
1348 namei_HandleToName(&filename, ihtmpp);
1349 #if !defined(AFS_NT40_ENV)
1350 printf(" UFS-Filename: %s", filename.n_path);
1352 printf(" NTFS-Filename: %s", filename.n_path);