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
12 * Module: vol-salvage.c
13 * Institution: The Information Technology Center, Carnegie-Mellon University
17 Correct handling of bad "." and ".." entries.
18 Message if volume has "destroyMe" flag set--but doesn't delete yet.
19 Link count bug fixed--bug was that vnodeEssence link count was unsigned
20 14 bits. Needs to be signed.
23 Change to DirHandle stuff to make sure that cache entries are reused at the
24 right time (this parallels the file server change, but is not identical).
26 Added calls to directory salvager routines; doesn't salvage dir unless debug=1.
29 Fixed bug which was causing inode link counts to go bad (thus leaking
31 Vnodes with 0 inode pointers in RW volumes are now deleted.
32 An inode with a matching inode number to the vnode is preferred to an
33 inode with a higer data version.
34 Bug is probably fixed that was causing data version to remain wrong,
35 despite assurances from the salvager to the contrary.
38 Added limited salvaging: unless ForceSalvage is on, then the volume will
39 not be salvaged if the dontSalvage flag is set in the Volume Header.
40 The ForceSalvage flag is turned on if an individual volume is salvaged or
41 if the file FORCESALVAGE exists in the partition header of the file system
42 being salvaged. This isn't used for anything but could be set by vfsck.
43 A -f flag was also added to force salvage.
46 It now deletes obsolete volume inodes without complaining
49 Repairs rw volume headers (again).
52 Correlates volume headers & inodes correctly, thus preventing occasional deletion
53 of read-only volumes...
54 No longer forces a directory salvage for volume 144 (which may be a good volume
56 Some of the messages are cleaned up or made more explicit. One or two added.
58 A bug was fixed which forced salvage of read-only volumes without a corresponding
62 When a volume header is recreated, the new name will be "bogus.volume#"
65 Directory salvaging turned on!!!
68 Prints warning messages for setuid programs.
71 Logs missing inode numbers.
74 Increments directory version number by 200 (rather than by 1) when it is salvaged, in order to prevent problems due to the fact that a version number can be promised to a workstation before it is written to disk. If the server crashes, it may have an older version. Salvaging it could bring the version number up to the same version the workstation believed it already had a call back on.
77 Locks the file /vice/vol/salvage.lock before starting. Aborts if it can't acquire the lock.
78 Time stamps on log entries.
79 Fcntl on stdout to cause all entries to be appended.
80 Problems writing to temporary files are now all detected.
81 Inode summary files are now dynamically named (so that multiple salvagers wouldn't conflict).
82 Some cleanup of error messages.
86 #define SalvageVersion "2.4"
88 /* Main program file. Define globals. */
91 #include <afsconfig.h>
92 #include <afs/param.h>
101 #include <sys/stat.h>
106 #include <WINNT/afsevent.h>
108 #include <sys/param.h>
109 #include <sys/file.h>
111 #include <sys/time.h>
112 #endif /* ITIMER_REAL */
114 #if defined(AFS_AIX_ENV) || defined(AFS_SUN4_ENV)
115 #define WCOREDUMP(x) (x & 0200)
118 #include <afs/afsint.h>
119 #include <afs/assert.h>
120 #if !defined(AFS_SGI_ENV) && !defined(AFS_NT40_ENV)
121 #if defined(AFS_VFSINCL_ENV)
122 #include <sys/vnode.h>
124 #include <sys/fs/ufs_inode.h>
126 #if defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
127 #include <ufs/ufs/dinode.h>
128 #include <ufs/ffs/fs.h>
130 #include <ufs/inode.h>
133 #else /* AFS_VFSINCL_ENV */
135 #include <ufs/inode.h>
136 #else /* AFS_OSF_ENV */
137 #if !defined(AFS_LINUX20_ENV) && !defined(AFS_XBSD_ENV)
138 #include <sys/inode.h>
141 #endif /* AFS_VFSINCL_ENV */
142 #endif /* AFS_SGI_ENV */
145 #include <sys/lockf.h>
149 #include <checklist.h>
151 #if defined(AFS_SGI_ENV)
156 #if defined(AFS_SUN_ENV) || defined(AFS_SUN5_ENV)
159 #include <sys/mnttab.h>
160 #include <sys/mntent.h>
165 #endif /* AFS_SGI_ENV */
166 #endif /* AFS_HPUX_ENV */
171 #include <afs/osi_inode.h>
174 #include <afs/afsutil.h>
175 #include <afs/fileutil.h>
176 #include <afs/procmgmt.h> /* signal(), kill(), wait(), etc. */
184 #include <afs/afssyscalls.h>
188 #include "partition.h"
190 #include "viceinode.h"
192 #include "volinodes.h" /* header magic number, etc. stuff */
197 /*@+fcnmacros +macrofcndecl@*/
200 extern off64_t afs_lseek(int FD, off64_t O, int F);
201 #endif /*S_SPLINT_S */
202 #define afs_lseek(FD, O, F) lseek64(FD, (off64_t) (O), F)
203 #define afs_stat stat64
204 #define afs_fstat fstat64
205 #define afs_open open64
206 #define afs_fopen fopen64
207 #else /* !O_LARGEFILE */
209 extern off_t afs_lseek(int FD, off_t O, int F);
210 #endif /*S_SPLINT_S */
211 #define afs_lseek(FD, O, F) lseek(FD, (off_t) (O), F)
212 #define afs_stat stat
213 #define afs_fstat fstat
214 #define afs_open open
215 #define afs_fopen fopen
216 #endif /* !O_LARGEFILE */
217 /*@=fcnmacros =macrofcndecl@*/
220 extern void *calloc();
222 static char *TimeStamp(time_t clock, int precision);
224 #define ORPH_IGNORE 0
225 #define ORPH_REMOVE 1
226 #define ORPH_ATTACH 2
229 int debug; /* -d flag */
230 int Testing = 0; /* -n flag */
231 int ListInodeOption; /* -i flag */
232 int ShowRootFiles; /* -r flag */
233 int RebuildDirs; /* -sal flag */
234 int Parallel = 4; /* -para X flag */
235 int PartsPerDisk = 8; /* Salvage up to 8 partitions on same disk sequentially */
236 int forceR = 0; /* -b flag */
237 int ShowLog = 0; /* -showlog flag */
238 int ShowSuid = 0; /* -showsuid flag */
239 int ShowMounts = 0; /* -showmounts flag */
240 int orphans = ORPH_IGNORE; /* -orphans option */
244 int useSyslog = 0; /* -syslog flag */
245 int useSyslogFacility = LOG_DAEMON; /* -syslogfacility option */
248 #define MAXPARALLEL 32
250 int OKToZap; /* -o flag */
251 int ForceSalvage; /* If salvage should occur despite the DONT_SALVAGE flag
252 * in the volume header */
254 static FILE *logFile = 0; /* one of {/usr/afs/logs,/vice/file}/SalvageLog */
256 #define ROOTINODE 2 /* Root inode of a 4.2 Unix file system
258 Device fileSysDevice; /* The device number of the current
259 * partition being salvaged */
263 char *fileSysPath; /* The path of the mounted partition currently
264 * being salvaged, i.e. the directory
265 * containing the volume headers */
267 char *fileSysPathName; /* NT needs this to make name pretty in log. */
268 IHandle_t *VGLinkH; /* Link handle for current volume group. */
269 int VGLinkH_cnt; /* # of references to lnk handle. */
270 struct DiskPartition *fileSysPartition; /* Partition being salvaged */
272 char *fileSysDeviceName; /* The block device where the file system
273 * being salvaged was mounted */
274 char *filesysfulldev;
276 int VolumeChanged; /* Set by any routine which would change the volume in
277 * a way which would require callback is to be broken if the
278 * volume was put back on line by an active file server */
280 VolumeDiskData VolInfo; /* A copy of the last good or salvaged volume header dealt with */
282 struct InodeSummary { /* Inode summary file--an entry for each
283 * volume in the inode file for a partition */
284 VolId volumeId; /* Volume id */
285 VolId RWvolumeId; /* RW volume associated */
286 int index; /* index into inode file (0, 1, 2 ...) */
287 int nInodes; /* Number of inodes for this volume */
288 int nSpecialInodes; /* Number of special inodes, i.e. volume
289 * header, index, etc. These are all
290 * marked (viceinode.h) and will all be sorted
291 * to the beginning of the information for
292 * this volume. Read-only volumes should
293 * ONLY have special inodes (all the other
294 * inodes look as if they belong to the
295 * original RW volume). */
296 Unique maxUniquifier; /* The maximum uniquifier found in all the inodes.
297 * This is only useful for RW volumes and is used
298 * to compute a new volume uniquifier in the event
299 * that the header needs to be recreated. The inode
300 * uniquifier may be a truncated version of vnode
301 * uniquifier (AFS_3DISPARES). The real maxUniquifer
302 * is from the vnodes and later calcuated from it */
303 struct VolumeSummary *volSummary;
304 /* Either a pointer to the original volume
305 * header summary, or constructed summary
308 #define readOnly(isp) ((isp)->volumeId != (isp)->RWvolumeId)
309 int nVolumesInInodeFile; /* Number of read-write volumes summarized */
310 int inodeFd; /* File descriptor for inode file */
313 struct VolumeSummary { /* Volume summary an entry for each
314 * volume in a volume directory.
315 * Assumption: one volume directory per
317 char *fileName; /* File name on the partition for the volume
319 struct VolumeHeader header;
320 /* volume number, rw volume number, inode
321 * numbers of each major component of
323 IHandle_t *volumeInfoHandle;
324 byte wouldNeedCallback; /* set if the file server should issue
325 * call backs for all the files in this volume when
326 * the volume goes back on line */
330 IHandle_t *handle; /* Inode containing this index */
331 int nVnodes; /* Total number of vnodes in index */
332 int nAllocatedVnodes; /* Total number actually used */
333 int volumeBlockCount; /* Total number of blocks used by volume */
334 Inode *inodes; /* Directory only */
335 struct VnodeEssence {
336 short count; /* Number of references to vnode; MUST BE SIGNED */
337 unsigned claimed:1; /* Set when a parent directory containing an entry
338 * referencing this vnode is found. The claim
339 * is that the parent in "parent" can point to
340 * this vnode, and no other */
341 unsigned changed:1; /* Set if any parameters (other than the count)
342 * in the vnode change. It is determined if the
343 * link count has changed by noting whether it is
344 * 0 after scanning all directories */
345 unsigned salvaged:1; /* Set if this directory vnode has already been salvaged. */
346 unsigned todelete:1; /* Set if this vnode is to be deleted (should not be claimed) */
347 afs_fsize_t blockCount;
348 /* Number of blocks (1K) used by this vnode,
350 VnodeId parent; /* parent in vnode */
351 Unique unique; /* Must match entry! */
352 char *name; /* Name of directory entry */
353 int modeBits; /* File mode bits */
354 Inode InodeNumber; /* file's inode */
355 int type; /* File type */
356 int author; /* File author */
357 int owner; /* File owner */
358 int group; /* File group */
360 } vnodeInfo[nVNODECLASSES];
363 struct DirHandle dirHandle;
366 unsigned haveDot, haveDotDot;
368 int copied; /* If the copy-on-write stuff has been applied */
376 struct VolumeSummary *volumeSummaryp; /* Holds all the volumes in a part */
377 int nVolumes; /* Number of volumes (read-write and read-only)
378 * in volume summary */
381 /* For NT, we can fork the per partition salvagers to gain the required
382 * safety against Aborts. But there's too many complex data structures at
383 * the per volume salvager layer to easilty copy the data across.
384 * childJobNumber is resset from -1 to the job number if this is a
385 * per partition child of the main salvager. This information is passed
386 * out-of-band in the extra data area setup for the now unused parent/child
389 #define SALVAGER_MAGIC 0x00BBaaDD
390 #define NOT_CHILD -1 /* job numbers start at 0 */
391 /* If new options need to be passed to child, add them here. */
398 /* Child job this process is running. */
399 childJob_t myjob = { SALVAGER_MAGIC, NOT_CHILD, "" };
401 int nt_SalvagePartition(char *partName, int jobn);
402 int nt_SetupPartitionSalvage(void *datap, int len);
405 struct InodeSummary *svgp_inodeSummaryp;
415 /* Forward declarations */
416 /*@printflike@*/ void Log(const char *format, ...);
417 /*@printflike@*/ void Abort(const char *format, ...);
420 int Wait(char *prog);
421 char *ToString(char *s);
422 void AskOffline(VolumeId volumeId);
423 void AskOnline(VolumeId volumeId, char *partition);
424 void CheckLogFile(void);
426 void TimeStampLogFile(void);
428 void ClearROInUseBit(struct VolumeSummary *summary);
429 void CopyAndSalvage(register struct DirSummary *dir);
430 int CopyInode(Device device, Inode inode1, Inode inode2, int rwvolume);
431 void CopyOnWrite(register struct DirSummary *dir);
432 void CountVolumeInodes(register struct ViceInodeInfo *ip, int maxInodes,
433 register struct InodeSummary *summary);
434 void DeleteExtraVolumeHeaderFile(register struct VolumeSummary *vsp);
435 void DistilVnodeEssence(VolumeId vid, VnodeClass class, Inode ino,
437 int GetInodeSummary(char *path, VolumeId singleVolumeNumber);
438 void GetVolumeSummary(VolumeId singleVolumeNumber);
439 void JudgeEntry(struct DirSummary *dir, char *name, VnodeId vnodeNumber,
441 void MaybeZapVolume(register struct InodeSummary *isp, char *message,
442 int deleteMe, int check);
443 void ObtainSalvageLock(void);
444 void PrintInodeList(void);
445 void PrintInodeSummary(void);
446 void PrintVolumeSummary(void);
447 int QuickCheck(register struct InodeSummary *isp, int nVols);
448 void RemoveTheForce(char *path);
449 void SalvageDir(char *name, VolumeId rwVid, struct VnodeInfo *dirVnodeInfo,
450 IHandle_t * alinkH, int i, struct DirSummary *rootdir,
452 void SalvageFileSysParallel(struct DiskPartition *partP);
453 void SalvageFileSys(struct DiskPartition *partP, VolumeId singleVolumeNumber);
454 void SalvageFileSys1(struct DiskPartition *partP,
455 VolumeId singleVolumeNumber);
456 int SalvageHeader(register struct stuff *sp, struct InodeSummary *isp,
457 int check, int *deleteMe);
458 int SalvageIndex(Inode ino, VnodeClass class, int RW,
459 register struct ViceInodeInfo *ip, int nInodes,
460 struct VolumeSummary *volSummary, int check);
461 int SalvageVnodes(register struct InodeSummary *rwIsp,
462 register struct InodeSummary *thisIsp,
463 register struct ViceInodeInfo *inodes, int check);
464 int SalvageVolume(register struct InodeSummary *rwIsp, IHandle_t * alinkH);
465 void DoSalvageVolumeGroup(register struct InodeSummary *isp, int nVols);
467 void SalvageVolumeGroup(register struct InodeSummary *isp, int nVols);
469 #define SalvageVolumeGroup DoSalvageVolumeGroup
471 int SalvageVolumeHeaderFile(register struct InodeSummary *isp,
472 register struct ViceInodeInfo *inodes, int RW,
473 int check, int *deleteMe);
475 int UseTheForceLuke(char *path);
477 static int IsVnodeOrphaned(VnodeId vnode);
479 /* Uniquifier stored in the Inode */
484 return (u & 0x3fffff);
486 #if defined(AFS_SGI_EXMAG)
487 return (u & SGI_UNIQMASK);
490 #endif /* AFS_SGI_EXMAG */
495 BadError(register int aerror)
497 if (aerror == EPERM || aerror == ENXIO || aerror == ENOENT)
499 return 0; /* otherwise may be transient, e.g. EMFILE */
505 handleit(struct cmd_syndesc *as)
507 register struct cmd_item *ti;
508 char pname[100], *temp;
509 afs_int32 seenpart = 0, seenvol = 0, vid = 0, seenany = 0;
510 struct DiskPartition *partP;
512 #ifdef AFS_SGI_VNODE_GLUE
513 if (afs_init_kernel_config(-1) < 0) {
515 ("Can't determine NUMA configuration, not starting salvager.\n");
523 for (i = 0; i < CMD_MAXPARMS; i++) {
524 if (as->parms[i].items) {
532 "Exiting immediately without salvage. Look into the FileLog to find volumes which really need to be salvaged!";
541 #endif /* FAST_RESTART */
542 if ((ti = as->parms[0].items)) { /* -partition */
544 strncpy(pname, ti->data, 100);
546 if ((ti = as->parms[1].items)) { /* -volumeid */
549 ("You must also specify '-partition' option with the '-volumeid' option\n");
553 vid = atoi(ti->data);
555 if (as->parms[2].items) /* -debug */
557 if (as->parms[3].items) /* -nowrite */
559 if (as->parms[4].items) /* -inodes */
561 if (as->parms[5].items) /* -force */
563 if (as->parms[6].items) /* -oktozap */
565 if (as->parms[7].items) /* -rootinodes */
567 if (as->parms[8].items) /* -RebuildDirs */
569 if (as->parms[9].items) /* -ForceReads */
571 if ((ti = as->parms[10].items)) { /* -Parallel # */
573 if (strncmp(temp, "all", 3) == 0) {
577 if (strlen(temp) != 0) {
578 Parallel = atoi(temp);
581 if (Parallel > MAXPARALLEL) {
582 printf("Setting parallel salvages to maximum of %d \n",
584 Parallel = MAXPARALLEL;
588 if ((ti = as->parms[11].items)) { /* -tmpdir */
592 dirp = opendir(tmpdir);
595 ("Can't open temporary placeholder dir %s; using current partition \n",
601 if ((ti = as->parms[12].items)) /* -showlog */
603 if ((ti = as->parms[13].items)) { /* -log */
608 if ((ti = as->parms[14].items)) { /* -showmounts */
613 if ((ti = as->parms[15].items)) { /* -orphans */
615 orphans = ORPH_IGNORE;
616 else if (strcmp(ti->data, "remove") == 0
617 || strcmp(ti->data, "r") == 0)
618 orphans = ORPH_REMOVE;
619 else if (strcmp(ti->data, "attach") == 0
620 || strcmp(ti->data, "a") == 0)
621 orphans = ORPH_ATTACH;
623 #ifndef AFS_NT40_ENV /* ignore options on NT */
624 if ((ti = as->parms[16].items)) { /* -syslog */
628 if ((ti = as->parms[17].items)) { /* -syslogfacility */
629 useSyslogFacility = atoi(ti->data);
632 if ((ti = as->parms[18].items)) { /* -datelogs */
638 if (ti = as->parms[19].items) { /* -DontSalvage */
640 "Exiting immediately without salvage. Look into the FileLog to find volumes which really need to be salvaged!";
648 #endif /* FAST_RESTART */
650 /* Note: if seemvol we initialize this as a standard volume utility: this has the
651 * implication that the file server may be running; negotations have to be made with
652 * the file server in this case to take the read write volume and associated read-only
653 * volumes off line before salvaging */
656 if (afs_winsockInit() < 0) {
657 ReportErrorEventAlt(AFSEVT_SVR_WINSOCK_INIT_FAILED, 0,
658 AFSDIR_SALVAGER_FILE, 0);
659 Log("Failed to initailize winsock, exiting.\n");
664 VInitVolumePackage(seenvol ? volumeUtility : salvager, 5, 5,
668 if (myjob.cj_number != NOT_CHILD) {
671 (void)strcpy(pname, myjob.cj_part);
676 for (partP = DiskPartitionList; partP; partP = partP->next) {
677 SalvageFileSysParallel(partP);
679 SalvageFileSysParallel(0);
681 partP = VGetPartition(pname, 0);
683 Log("salvage: Unknown or unmounted partition %s; salvage aborted\n", pname);
687 SalvageFileSys(partP, 0);
689 /* Salvage individual volume */
691 Log("salvage: invalid volume id specified; salvage aborted\n");
694 SalvageFileSys(partP, vid);
702 #include "AFS_component_version_number.c"
706 char *save_args[MAX_ARGS];
708 pthread_t main_thread;
712 main(int argc, char **argv)
714 struct cmd_syndesc *ts;
716 char commandLine[150];
719 extern char cml_version_number[];
723 * The following signal action for AIX is necessary so that in case of a
724 * crash (i.e. core is generated) we can include the user's data section
725 * in the core dump. Unfortunately, by default, only a partial core is
726 * generated which, in many cases, isn't too useful.
728 struct sigaction nsa;
730 sigemptyset(&nsa.sa_mask);
731 nsa.sa_handler = SIG_DFL;
732 nsa.sa_flags = SA_FULLDUMP;
733 sigaction(SIGABRT, &nsa, NULL);
734 sigaction(SIGSEGV, &nsa, NULL);
737 /* Initialize directory paths */
738 if (!(initAFSDirPath() & AFSDIR_SERVER_PATHS_OK)) {
740 ReportErrorEventAlt(AFSEVT_SVR_NO_INSTALL_DIR, 0, argv[0], 0);
742 fprintf(stderr, "%s: Unable to obtain AFS server directory.\n",
747 main_thread = pthread_self();
748 if (spawnDatap && spawnDataLen) {
749 /* This is a child per partition salvager. Don't setup log or
750 * try to lock the salvager lock.
752 if (nt_SetupPartitionSalvage(spawnDatap, spawnDataLen) < 0)
756 for (commandLine[0] = '\0', i = 0; i < argc; i++) {
758 strcat(commandLine, " ");
759 strcat(commandLine, argv[i]);
762 /* All entries to the log will be appended. Useful if there are
763 * multiple salvagers appending to the log.
768 #ifdef AFS_LINUX20_ENV
769 fcntl(fileno(logFile), F_SETFL, O_APPEND); /* Isn't this redundant? */
771 fcntl(fileno(logFile), F_SETFL, FAPPEND); /* Isn't this redundant? */
777 if (geteuid() != 0) {
778 printf("Salvager must be run as root.\n");
784 /* bad for normal help flag processing, but can do nada */
786 fprintf(logFile, "%s\n", cml_version_number);
787 Log("STARTING AFS SALVAGER %s (%s)\n", SalvageVersion, commandLine);
789 /* Get and hold a lock for the duration of the salvage to make sure
790 * that no other salvage runs at the same time. The routine
791 * VInitVolumePackage (called below) makes sure that a file server or
792 * other volume utilities don't interfere with the salvage.
799 ts = cmd_CreateSyntax("initcmd", handleit, 0, "initialize the program");
800 cmd_AddParm(ts, "-partition", CMD_SINGLE, CMD_OPTIONAL,
801 "Name of partition to salvage");
802 cmd_AddParm(ts, "-volumeid", CMD_SINGLE, CMD_OPTIONAL,
803 "Volume Id to salvage");
804 cmd_AddParm(ts, "-debug", CMD_FLAG, CMD_OPTIONAL,
805 "Run in Debugging mode");
806 cmd_AddParm(ts, "-nowrite", CMD_FLAG, CMD_OPTIONAL,
807 "Run readonly/test mode");
808 cmd_AddParm(ts, "-inodes", CMD_FLAG, CMD_OPTIONAL,
809 "Just list affected afs inodes - debugging flag");
810 cmd_AddParm(ts, "-force", CMD_FLAG, CMD_OPTIONAL, "Force full salvaging");
811 cmd_AddParm(ts, "-oktozap", CMD_FLAG, CMD_OPTIONAL,
812 "Give permission to destroy bogus inodes/volumes - debugging flag");
813 cmd_AddParm(ts, "-rootinodes", CMD_FLAG, CMD_OPTIONAL,
814 "Show inodes owned by root - debugging flag");
815 cmd_AddParm(ts, "-salvagedirs", CMD_FLAG, CMD_OPTIONAL,
816 "Force rebuild/salvage of all directories");
817 cmd_AddParm(ts, "-blockreads", CMD_FLAG, CMD_OPTIONAL,
818 "Read smaller blocks to handle IO/bad blocks");
819 cmd_AddParm(ts, "-parallel", CMD_SINGLE, CMD_OPTIONAL,
820 "# of max parallel partition salvaging");
821 cmd_AddParm(ts, "-tmpdir", CMD_SINGLE, CMD_OPTIONAL,
822 "Name of dir to place tmp files ");
823 cmd_AddParm(ts, "-showlog", CMD_FLAG, CMD_OPTIONAL,
824 "Show log file upon completion");
825 cmd_AddParm(ts, "-showsuid", CMD_FLAG, CMD_OPTIONAL,
826 "Report on suid/sgid files");
827 cmd_AddParm(ts, "-showmounts", CMD_FLAG, CMD_OPTIONAL,
828 "Report on mountpoints");
829 cmd_AddParm(ts, "-orphans", CMD_SINGLE, CMD_OPTIONAL,
830 "ignore | remove | attach");
832 /* note - syslog isn't avail on NT, but if we make it conditional, have
833 * to deal with screwy offsets for cmd params */
834 cmd_AddParm(ts, "-syslog", CMD_FLAG, CMD_OPTIONAL,
835 "Write salvage log to syslogs");
836 cmd_AddParm(ts, "-syslogfacility", CMD_SINGLE, CMD_OPTIONAL,
837 "Syslog facility number to use");
838 cmd_AddParm(ts, "-datelogs", CMD_FLAG, CMD_OPTIONAL,
839 "Include timestamp in logfile filename");
842 cmd_AddParm(ts, "-DontSalvage", CMD_FLAG, CMD_OPTIONAL,
843 "Don't salvage. This my be set in BosConfig to let the fileserver restart immediately after a crash. Bad volumes will be taken offline");
844 #endif /* FAST_RESTART */
845 err = cmd_Dispatch(argc, argv);
849 /* Get the salvage lock if not already held. Hold until process exits. */
851 ObtainSalvageLock(void)
857 (int)CreateFile(AFSDIR_SERVER_SLVGLOCK_FILEPATH, 0, 0, NULL,
858 OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
859 if (salvageLock == (int)INVALID_HANDLE_VALUE) {
861 "salvager: There appears to be another salvager running! Aborted.\n");
866 afs_open(AFSDIR_SERVER_SLVGLOCK_FILEPATH, O_CREAT | O_RDWR, 0666);
867 if (salvageLock < 0) {
869 "salvager: can't open salvage lock file %s, aborting\n",
870 AFSDIR_SERVER_SLVGLOCK_FILEPATH);
873 #ifdef AFS_DARWIN_ENV
874 if (flock(salvageLock, LOCK_EX) == -1) {
876 if (lockf(salvageLock, F_LOCK, 0) == -1) {
879 "salvager: There appears to be another salvager running! Aborted.\n");
886 #ifdef AFS_SGI_XFS_IOPS_ENV
887 /* Check if the given partition is mounted. For XFS, the root inode is not a
888 * constant. So we check the hard way.
891 IsPartitionMounted(char *part)
894 struct mntent *mntent;
896 assert(mntfp = setmntent(MOUNTED, "r"));
897 while (mntent = getmntent(mntfp)) {
898 if (!strcmp(part, mntent->mnt_dir))
903 return mntent ? 1 : 1;
906 /* Check if the given inode is the root of the filesystem. */
907 #ifndef AFS_SGI_XFS_IOPS_ENV
909 IsRootInode(struct afs_stat *status)
912 * The root inode is not a fixed value in XFS partitions. So we need to
913 * see if the partition is in the list of mounted partitions. This only
914 * affects the SalvageFileSys path, so we check there.
916 return (status->st_ino == ROOTINODE);
921 #ifndef AFS_NAMEI_ENV
922 /* We don't want to salvage big files filesystems, since we can't put volumes on
926 CheckIfBigFilesFS(char *mountPoint, char *devName)
928 struct superblock fs;
931 if (strncmp(devName, "/dev/", 5)) {
932 (void)sprintf(name, "/dev/%s", devName);
934 (void)strcpy(name, devName);
937 if (ReadSuper(&fs, name) < 0) {
938 Log("Unable to read superblock. Not salvaging partition %s.\n",
942 if (IsBigFilesFileSystem(&fs)) {
943 Log("Partition %s is a big files filesystem, not salvaging.\n",
953 #define HDSTR "\\Device\\Harddisk"
954 #define HDLEN (sizeof(HDSTR)-1) /* Length of "\Device\Harddisk" */
956 SameDisk(struct DiskPartition *p1, struct DiskPartition *p2)
961 static int dowarn = 1;
963 if (!QueryDosDevice(p1->devName, res, RES_LEN - 1))
965 if (strncmp(res, HDSTR, HDLEN)) {
968 Log("WARNING: QueryDosDevice is returning %s, not %s for %s\n",
969 res, HDSTR, p1->devName);
973 d1 = atoi(&res[HDLEN]);
975 if (!QueryDosDevice(p2->devName, res, RES_LEN - 1))
977 if (strncmp(res, HDSTR, HDLEN)) {
980 Log("WARNING: QueryDosDevice is returning %s, not %s for %s\n",
981 res, HDSTR, p2->devName);
985 d2 = atoi(&res[HDLEN]);
990 #define SameDisk(P1, P2) ((P1)->device/PartsPerDisk == (P2)->device/PartsPerDisk)
993 /* This assumes that two partitions with the same device number divided by
994 * PartsPerDisk are on the same disk.
997 SalvageFileSysParallel(struct DiskPartition *partP)
1000 struct DiskPartition *partP;
1001 int pid; /* Pid for this job */
1002 int jobnumb; /* Log file job number */
1003 struct job *nextjob; /* Next partition on disk to salvage */
1005 static struct job *jobs[MAXPARALLEL] = { 0 }; /* Need to zero this */
1006 struct job *thisjob = 0;
1007 static int numjobs = 0;
1008 static int jobcount = 0;
1014 char logFileName[256];
1018 /* We have a partition to salvage. Copy it into thisjob */
1019 thisjob = (struct job *)malloc(sizeof(struct job));
1021 Log("Can't salvage '%s'. Not enough memory\n", partP->name);
1024 memset(thisjob, 0, sizeof(struct job));
1025 thisjob->partP = partP;
1026 thisjob->jobnumb = jobcount;
1028 } else if (jobcount == 0) {
1029 /* We are asking to wait for all jobs (partp == 0), yet we never
1032 Log("No file system partitions named %s* found; not salvaged\n",
1033 VICE_PARTITION_PREFIX);
1037 if (debug || Parallel == 1) {
1039 SalvageFileSys(thisjob->partP, 0);
1046 /* Check to see if thisjob is for a disk that we are already
1047 * salvaging. If it is, link it in as the next job to do. The
1048 * jobs array has 1 entry per disk being salvages. numjobs is
1049 * the total number of disks currently being salvaged. In
1050 * order to keep thejobs array compact, when a disk is
1051 * completed, the hightest element in the jobs array is moved
1052 * down to now open slot.
1054 for (j = 0; j < numjobs; j++) {
1055 if (SameDisk(jobs[j]->partP, thisjob->partP)) {
1056 /* On same disk, add it to this list and return */
1057 thisjob->nextjob = jobs[j]->nextjob;
1058 jobs[j]->nextjob = thisjob;
1065 /* Loop until we start thisjob or until all existing jobs are finished */
1066 while (thisjob || (!partP && (numjobs > 0))) {
1067 startjob = -1; /* No new job to start */
1069 if ((numjobs >= Parallel) || (!partP && (numjobs > 0))) {
1070 /* Either the max jobs are running or we have to wait for all
1071 * the jobs to finish. In either case, we wait for at least one
1072 * job to finish. When it's done, clean up after it.
1074 pid = wait(&wstatus);
1076 for (j = 0; j < numjobs; j++) { /* Find which job it is */
1077 if (pid == jobs[j]->pid)
1080 assert(j < numjobs);
1081 if (WCOREDUMP(wstatus)) { /* Say if the job core dumped */
1082 Log("Salvage of %s core dumped!\n", jobs[j]->partP->name);
1085 numjobs--; /* job no longer running */
1086 oldjob = jobs[j]; /* remember */
1087 jobs[j] = jobs[j]->nextjob; /* Step to next part on same disk */
1088 free(oldjob); /* free the old job */
1090 /* If there is another partition on the disk to salvage, then
1091 * say we will start it (startjob). If not, then put thisjob there
1092 * and say we will start it.
1094 if (jobs[j]) { /* Another partitions to salvage */
1095 startjob = j; /* Will start it */
1096 } else { /* There is not another partition to salvage */
1098 jobs[j] = thisjob; /* Add thisjob */
1100 startjob = j; /* Will start it */
1102 jobs[j] = jobs[numjobs]; /* Move last job up to this slot */
1103 startjob = -1; /* Don't start it - already running */
1107 /* We don't have to wait for a job to complete */
1109 jobs[numjobs] = thisjob; /* Add this job */
1111 startjob = numjobs; /* Will start it */
1115 /* Start up a new salvage job on a partition in job slot "startjob" */
1116 if (startjob != -1) {
1118 Log("Starting salvage of file system partition %s\n",
1119 jobs[startjob]->partP->name);
1121 /* For NT, we not only fork, but re-exec the salvager. Pass in the
1122 * commands and pass the child job number via the data path.
1125 nt_SalvagePartition(jobs[startjob]->partP->name,
1126 jobs[startjob]->jobnumb);
1127 jobs[startjob]->pid = pid;
1132 jobs[startjob]->pid = pid;
1138 for (fd = 0; fd < 16; fd++)
1143 #ifndef AFS_NT40_ENV
1145 openlog("salvager", LOG_PID, useSyslogFacility);
1149 (void)afs_snprintf(logFileName, sizeof logFileName,
1151 AFSDIR_SERVER_SLVGLOG_FILEPATH,
1152 jobs[startjob]->jobnumb);
1153 logFile = afs_fopen(logFileName, "w");
1158 SalvageFileSys1(jobs[startjob]->partP, 0);
1163 } /* while ( thisjob || (!partP && numjobs > 0) ) */
1165 /* If waited for all jobs to complete, now collect log files and return */
1166 #ifndef AFS_NT40_ENV
1167 if (!useSyslog) /* if syslogging - no need to collect */
1170 for (i = 0; i < jobcount; i++) {
1171 (void)afs_snprintf(logFileName, sizeof logFileName, "%s.%d",
1172 AFSDIR_SERVER_SLVGLOG_FILEPATH, i);
1173 if ((passLog = afs_fopen(logFileName, "r"))) {
1174 while (fgets(buf, sizeof(buf), passLog)) {
1175 fputs(buf, logFile);
1179 (void)unlink(logFileName);
1188 SalvageFileSys(struct DiskPartition *partP, VolumeId singleVolumeNumber)
1190 if (!canfork || debug || Fork() == 0) {
1191 SalvageFileSys1(partP, singleVolumeNumber);
1192 if (canfork && !debug) {
1197 Wait("SalvageFileSys");
1201 get_DevName(char *pbuffer, char *wpath)
1203 char pbuf[128], *ptr;
1204 strcpy(pbuf, pbuffer);
1205 ptr = (char *)strrchr(pbuf, '/');
1208 strcpy(wpath, pbuf);
1211 ptr = (char *)strrchr(pbuffer, '/');
1213 strcpy(pbuffer, ptr + 1);
1220 SalvageFileSys1(struct DiskPartition *partP, VolumeId singleVolumeNumber)
1223 char inodeListPath[256];
1224 static char tmpDevName[100];
1225 static char wpath[100];
1226 struct VolumeSummary *vsp, *esp;
1229 fileSysPartition = partP;
1230 fileSysDevice = fileSysPartition->device;
1231 fileSysPathName = VPartitionPath(fileSysPartition);
1234 /* Opendir can fail on "C:" but not on "C:\" if C is empty! */
1235 (void)sprintf(fileSysPath, "%s\\", fileSysPathName);
1236 name = partP->devName;
1238 fileSysPath = fileSysPathName;
1239 strcpy(tmpDevName, partP->devName);
1240 name = get_DevName(tmpDevName, wpath);
1241 fileSysDeviceName = name;
1242 filesysfulldev = wpath;
1245 VLockPartition(partP->name);
1246 if (singleVolumeNumber || ForceSalvage)
1249 ForceSalvage = UseTheForceLuke(fileSysPath);
1251 if (singleVolumeNumber) {
1252 if (!VConnectFS()) {
1253 Abort("Couldn't connect to file server\n");
1255 AskOffline(singleVolumeNumber);
1258 Log("SALVAGING FILE SYSTEM PARTITION %s (device=%s%s)\n",
1259 partP->name, name, (Testing ? "(READONLY mode)" : ""));
1261 Log("***Forced salvage of all volumes on this partition***\n");
1266 * Remove any leftover /vicepa/salvage.inodes.* or /vicepa/salvage.temp.*
1273 assert((dirp = opendir(fileSysPath)) != NULL);
1274 while ((dp = readdir(dirp))) {
1275 if (!strncmp(dp->d_name, "salvage.inodes.", 15)
1276 || !strncmp(dp->d_name, "salvage.temp.", 13)) {
1278 Log("Removing old salvager temp files %s\n", dp->d_name);
1279 strcpy(npath, fileSysPath);
1281 strcat(npath, dp->d_name);
1287 tdir = (tmpdir ? tmpdir : fileSysPath);
1289 (void)_putenv("TMP="); /* If "TMP" is set, then that overrides tdir. */
1290 (void)strncpy(inodeListPath, _tempnam(tdir, "salvage.inodes."), 255);
1292 snprintf(inodeListPath, 255, "%s/salvage.inodes.%s.%d", tdir, name,
1295 if (GetInodeSummary(inodeListPath, singleVolumeNumber) < 0) {
1296 unlink(inodeListPath);
1300 /* Using nt_unlink here since we're really using the delete on close
1301 * semantics of unlink. In most places in the salvager, we really do
1302 * mean to unlink the file at that point. Those places have been
1303 * modified to actually do that so that the NT crt can be used there.
1306 _open_osfhandle((long)nt_open(inodeListPath, O_RDWR, 0), O_RDWR);
1307 nt_unlink(inodeListPath); /* NT's crt unlink won't if file is open. */
1309 inodeFd = afs_open(inodeListPath, O_RDONLY);
1310 unlink(inodeListPath);
1313 Abort("Temporary file %s is missing...\n", inodeListPath);
1314 if (ListInodeOption) {
1318 /* enumerate volumes in the partition.
1319 * figure out sets of read-only + rw volumes.
1320 * salvage each set, read-only volumes first, then read-write.
1321 * Fix up inodes on last volume in set (whether it is read-write
1324 GetVolumeSummary(singleVolumeNumber);
1326 for (i = j = 0, vsp = volumeSummaryp, esp = vsp + nVolumes;
1327 i < nVolumesInInodeFile; i = j) {
1328 VolumeId rwvid = inodeSummary[i].RWvolumeId;
1330 j < nVolumesInInodeFile && inodeSummary[j].RWvolumeId == rwvid;
1332 VolumeId vid = inodeSummary[j].volumeId;
1333 struct VolumeSummary *tsp;
1334 /* Scan volume list (from partition root directory) looking for the
1335 * current rw volume number in the volume list from the inode scan.
1336 * If there is one here that is not in the inode volume list,
1338 for (; vsp < esp && (vsp->header.parent < rwvid); vsp++) {
1340 DeleteExtraVolumeHeaderFile(vsp);
1342 /* Now match up the volume summary info from the root directory with the
1343 * entry in the volume list obtained from scanning inodes */
1344 inodeSummary[j].volSummary = NULL;
1345 for (tsp = vsp; tsp < esp && (tsp->header.parent == rwvid); tsp++) {
1346 if (tsp->header.id == vid) {
1347 inodeSummary[j].volSummary = tsp;
1353 /* Salvage the group of volumes (several read-only + 1 read/write)
1354 * starting with the current read-only volume we're looking at.
1356 SalvageVolumeGroup(&inodeSummary[i], j - i);
1359 /* Delete any additional volumes that were listed in the partition but which didn't have any corresponding inodes */
1360 for (; vsp < esp; vsp++) {
1362 DeleteExtraVolumeHeaderFile(vsp);
1365 if (!singleVolumeNumber) /* Remove the FORCESALVAGE file */
1366 RemoveTheForce(fileSysPath);
1368 if (!Testing && singleVolumeNumber) {
1369 AskOnline(singleVolumeNumber, fileSysPartition->name);
1371 /* Step through the volumeSummary list and set all volumes on-line.
1372 * The volumes were taken off-line in GetVolumeSummary.
1374 for (j = 0; j < nVolumes; j++) {
1375 AskOnline(volumeSummaryp[j].header.id, fileSysPartition->name);
1379 Log("SALVAGING OF PARTITION %s%s COMPLETED\n",
1380 fileSysPartition->name, (Testing ? " (READONLY mode)" : ""));
1383 close(inodeFd); /* SalvageVolumeGroup was the last which needed it. */
1387 DeleteExtraVolumeHeaderFile(register struct VolumeSummary *vsp)
1390 Log("The volume header file %s is not associated with any actual data (%sdeleted)\n", vsp->fileName, (Testing ? "would have been " : ""));
1392 unlink(vsp->fileName);
1396 CompareInodes(const void *_p1, const void *_p2)
1398 register const struct ViceInodeInfo *p1 = _p1;
1399 register const struct ViceInodeInfo *p2 = _p2;
1400 if (p1->u.vnode.vnodeNumber == INODESPECIAL
1401 || p2->u.vnode.vnodeNumber == INODESPECIAL) {
1402 VolumeId p1rwid, p2rwid;
1404 (p1->u.vnode.vnodeNumber ==
1405 INODESPECIAL ? p1->u.special.parentId : p1->u.vnode.volumeId);
1407 (p2->u.vnode.vnodeNumber ==
1408 INODESPECIAL ? p2->u.special.parentId : p2->u.vnode.volumeId);
1409 if (p1rwid < p2rwid)
1411 if (p1rwid > p2rwid)
1413 if (p1->u.vnode.vnodeNumber == INODESPECIAL
1414 && p2->u.vnode.vnodeNumber == INODESPECIAL) {
1415 if (p1->u.vnode.volumeId == p2->u.vnode.volumeId)
1416 return (p1->u.special.type < p2->u.special.type ? -1 : 1);
1417 if (p1->u.vnode.volumeId == p1rwid)
1419 if (p2->u.vnode.volumeId == p2rwid)
1421 return (p1->u.vnode.volumeId < p2->u.vnode.volumeId ? -1 : 1);
1423 if (p1->u.vnode.vnodeNumber != INODESPECIAL)
1424 return (p2->u.vnode.volumeId == p2rwid ? 1 : -1);
1425 return (p1->u.vnode.volumeId == p1rwid ? -1 : 1);
1427 if (p1->u.vnode.volumeId < p2->u.vnode.volumeId)
1429 if (p1->u.vnode.volumeId > p2->u.vnode.volumeId)
1431 if (p1->u.vnode.vnodeNumber < p2->u.vnode.vnodeNumber)
1433 if (p1->u.vnode.vnodeNumber > p2->u.vnode.vnodeNumber)
1435 /* The following tests are reversed, so that the most desirable
1436 * of several similar inodes comes first */
1437 if (p1->u.vnode.vnodeUniquifier > p2->u.vnode.vnodeUniquifier) {
1438 #ifdef AFS_3DISPARES
1439 if (p1->u.vnode.vnodeUniquifier > 3775414 /* 90% of 4.2M */ &&
1440 p2->u.vnode.vnodeUniquifier < 419490 /* 10% of 4.2M */ )
1443 #ifdef AFS_SGI_EXMAG
1444 if (p1->u.vnode.vnodeUniquifier > 15099494 /* 90% of 16M */ &&
1445 p2->u.vnode.vnodeUniquifier < 1677721 /* 10% of 16M */ )
1450 if (p1->u.vnode.vnodeUniquifier < p2->u.vnode.vnodeUniquifier) {
1451 #ifdef AFS_3DISPARES
1452 if (p2->u.vnode.vnodeUniquifier > 3775414 /* 90% of 4.2M */ &&
1453 p1->u.vnode.vnodeUniquifier < 419490 /* 10% of 4.2M */ )
1456 #ifdef AFS_SGI_EXMAG
1457 if (p2->u.vnode.vnodeUniquifier > 15099494 /* 90% of 16M */ &&
1458 p1->u.vnode.vnodeUniquifier < 1677721 /* 10% of 16M */ )
1463 if (p1->u.vnode.inodeDataVersion > p2->u.vnode.inodeDataVersion) {
1464 #ifdef AFS_3DISPARES
1465 if (p1->u.vnode.inodeDataVersion > 1887437 /* 90% of 2.1M */ &&
1466 p2->u.vnode.inodeDataVersion < 209716 /* 10% of 2.1M */ )
1469 #ifdef AFS_SGI_EXMAG
1470 if (p1->u.vnode.inodeDataVersion > 15099494 /* 90% of 16M */ &&
1471 p2->u.vnode.inodeDataVersion < 1677721 /* 10% of 16M */ )
1476 if (p1->u.vnode.inodeDataVersion < p2->u.vnode.inodeDataVersion) {
1477 #ifdef AFS_3DISPARES
1478 if (p2->u.vnode.inodeDataVersion > 1887437 /* 90% of 2.1M */ &&
1479 p1->u.vnode.inodeDataVersion < 209716 /* 10% of 2.1M */ )
1482 #ifdef AFS_SGI_EXMAG
1483 if (p2->u.vnode.inodeDataVersion > 15099494 /* 90% of 16M */ &&
1484 p1->u.vnode.inodeDataVersion < 1677721 /* 10% of 16M */ )
1493 CountVolumeInodes(register struct ViceInodeInfo *ip, int maxInodes,
1494 register struct InodeSummary *summary)
1496 int volume = ip->u.vnode.volumeId;
1497 int rwvolume = volume;
1498 register n, nSpecial;
1499 register Unique maxunique;
1502 while (maxInodes-- && volume == ip->u.vnode.volumeId) {
1504 if (ip->u.vnode.vnodeNumber == INODESPECIAL) {
1506 rwvolume = ip->u.special.parentId;
1507 /* This isn't quite right, as there could (in error) be different
1508 * parent inodes in different special vnodes */
1510 if (maxunique < ip->u.vnode.vnodeUniquifier)
1511 maxunique = ip->u.vnode.vnodeUniquifier;
1515 summary->volumeId = volume;
1516 summary->RWvolumeId = rwvolume;
1517 summary->nInodes = n;
1518 summary->nSpecialInodes = nSpecial;
1519 summary->maxUniquifier = maxunique;
1523 OnlyOneVolume(struct ViceInodeInfo *inodeinfo, VolumeId singleVolumeNumber)
1525 if (inodeinfo->u.vnode.vnodeNumber == INODESPECIAL)
1526 return (inodeinfo->u.special.parentId == singleVolumeNumber);
1527 return (inodeinfo->u.vnode.volumeId == singleVolumeNumber);
1532 * Collect list of inodes in file named by path. If a truly fatal error,
1533 * unlink the file and abort. For lessor errors, return -1. The file will
1534 * be unlinked by the caller.
1537 GetInodeSummary(char *path, VolumeId singleVolumeNumber)
1539 struct afs_stat status;
1541 struct ViceInodeInfo *ip;
1542 struct InodeSummary summary;
1543 char summaryFileName[50];
1546 char *dev = fileSysPath;
1547 char *wpath = fileSysPath;
1549 char *dev = fileSysDeviceName;
1550 char *wpath = filesysfulldev;
1552 char *part = fileSysPath;
1555 /* This file used to come from vfsck; cobble it up ourselves now... */
1557 ListViceInodes(dev, fileSysPath, path,
1558 singleVolumeNumber ? OnlyOneVolume : 0,
1559 singleVolumeNumber, &forceSal, forceR, wpath)) < 0) {
1561 Log("*** I/O error %d when writing a tmp inode file %s; Not salvaged %s ***\nIncrease space on partition or use '-tmpdir'\n", errno, path, dev);
1565 Abort("Unable to get inodes for \"%s\"; not salvaged\n", dev);
1567 if (forceSal && !ForceSalvage) {
1568 Log("***Forced salvage of all volumes on this partition***\n");
1571 inodeFd = afs_open(path, O_RDWR);
1572 if (inodeFd == -1 || afs_fstat(inodeFd, &status) == -1) {
1574 Abort("No inode description file for \"%s\"; not salvaged\n", dev);
1576 tdir = (tmpdir ? tmpdir : part);
1578 (void)_putenv("TMP="); /* If "TMP" is set, then that overrides tdir. */
1579 (void)strcpy(summaryFileName, _tempnam(tdir, "salvage.temp"));
1581 (void)afs_snprintf(summaryFileName, sizeof summaryFileName,
1582 "%s/salvage.temp.%d", tdir, getpid());
1584 summaryFile = afs_fopen(summaryFileName, "a+");
1585 if (summaryFile == NULL) {
1588 Abort("Unable to create inode summary file\n");
1590 if (!canfork || debug || Fork() == 0) {
1592 nInodes = status.st_size / sizeof(struct ViceInodeInfo);
1594 fclose(summaryFile);
1596 unlink(summaryFileName);
1597 if (!singleVolumeNumber) /* Remove the FORCESALVAGE file */
1598 RemoveTheForce(fileSysPath);
1600 struct VolumeSummary *vsp;
1603 GetVolumeSummary(singleVolumeNumber);
1605 for (i = 0,vsp = volumeSummaryp; i < nVolumes; i++) {
1607 DeleteExtraVolumeHeaderFile(vsp);
1610 Log("%s vice inodes on %s; not salvaged\n",
1611 singleVolumeNumber ? "No applicable" : "No", dev);
1614 ip = (struct ViceInodeInfo *)malloc(status.st_size);
1616 fclose(summaryFile);
1619 unlink(summaryFileName);
1621 ("Unable to allocate enough space to read inode table; %s not salvaged\n",
1624 if (read(inodeFd, ip, status.st_size) != status.st_size) {
1625 fclose(summaryFile);
1628 unlink(summaryFileName);
1629 Abort("Unable to read inode table; %s not salvaged\n", dev);
1631 qsort(ip, nInodes, sizeof(struct ViceInodeInfo), CompareInodes);
1632 if (afs_lseek(inodeFd, 0, SEEK_SET) == -1
1633 || write(inodeFd, ip, status.st_size) != status.st_size) {
1634 fclose(summaryFile);
1637 unlink(summaryFileName);
1638 Abort("Unable to rewrite inode table; %s not salvaged\n", dev);
1642 CountVolumeInodes(ip, nInodes, &summary);
1643 if (fwrite(&summary, sizeof(summary), 1, summaryFile) != 1) {
1644 Log("Difficulty writing summary file (errno = %d); %s not salvaged\n", errno, dev);
1645 fclose(summaryFile);
1649 summary.index += (summary.nInodes);
1650 nInodes -= summary.nInodes;
1651 ip += summary.nInodes;
1653 /* Following fflush is not fclose, because if it was debug mode would not work */
1654 if (fflush(summaryFile) == EOF || fsync(fileno(summaryFile)) == -1) {
1655 Log("Unable to write summary file (errno = %d); %s not salvaged\n", errno, dev);
1656 fclose(summaryFile);
1660 if (canfork && !debug) {
1665 if (Wait("Inode summary") == -1) {
1666 fclose(summaryFile);
1669 unlink(summaryFileName);
1670 Exit(1); /* salvage of this partition aborted */
1673 assert(afs_fstat(fileno(summaryFile), &status) != -1);
1674 if (status.st_size != 0) {
1676 inodeSummary = (struct InodeSummary *)malloc(status.st_size);
1677 assert(inodeSummary != NULL);
1678 /* For GNU we need to do lseek to get the file pointer moved. */
1679 assert(afs_lseek(fileno(summaryFile), 0, SEEK_SET) == 0);
1680 ret = read(fileno(summaryFile), inodeSummary, status.st_size);
1681 assert(ret == status.st_size);
1683 nVolumesInInodeFile = status.st_size / sizeof(struct InodeSummary);
1684 fclose(summaryFile);
1686 unlink(summaryFileName);
1690 /* Comparison routine for volume sort.
1691 This is setup so that a read-write volume comes immediately before
1692 any read-only clones of that volume */
1694 CompareVolumes(const void *_p1, const void *_p2)
1696 register const struct VolumeSummary *p1 = _p1;
1697 register const struct VolumeSummary *p2 = _p2;
1698 if (p1->header.parent != p2->header.parent)
1699 return p1->header.parent < p2->header.parent ? -1 : 1;
1700 if (p1->header.id == p1->header.parent) /* p1 is rw volume */
1702 if (p2->header.id == p2->header.parent) /* p2 is rw volume */
1704 return p1->header.id < p2->header.id ? -1 : 1; /* Both read-only */
1708 GetVolumeSummary(VolumeId singleVolumeNumber)
1711 afs_int32 nvols = 0;
1712 struct VolumeSummary *vsp, vs;
1713 struct VolumeDiskHeader diskHeader;
1716 /* Get headers from volume directory */
1717 if (chdir(fileSysPath) == -1 || (dirp = opendir(".")) == NULL)
1718 Abort("Can't read directory %s; not salvaged\n", fileSysPath);
1719 if (!singleVolumeNumber) {
1720 while ((dp = readdir(dirp))) {
1721 char *p = dp->d_name;
1722 p = strrchr(dp->d_name, '.');
1723 if (p != NULL && strcmp(p, VHDREXT) == 0) {
1725 if ((fd = afs_open(dp->d_name, O_RDONLY)) != -1
1726 && read(fd, (char *)&diskHeader, sizeof(diskHeader))
1727 == sizeof(diskHeader)
1728 && diskHeader.stamp.magic == VOLUMEHEADERMAGIC) {
1729 DiskToVolumeHeader(&vs.header, &diskHeader);
1737 dirp = opendir("."); /* No rewinddir for NT */
1744 (struct VolumeSummary *)malloc(nvols *
1745 sizeof(struct VolumeSummary));
1748 (struct VolumeSummary *)malloc(20 * sizeof(struct VolumeSummary));
1749 assert(volumeSummaryp != NULL);
1752 vsp = volumeSummaryp;
1753 while ((dp = readdir(dirp))) {
1754 char *p = dp->d_name;
1755 p = strrchr(dp->d_name, '.');
1756 if (p != NULL && strcmp(p, VHDREXT) == 0) {
1759 if ((fd = afs_open(dp->d_name, O_RDONLY)) == -1
1760 || read(fd, &diskHeader, sizeof(diskHeader))
1761 != sizeof(diskHeader)
1762 || diskHeader.stamp.magic != VOLUMEHEADERMAGIC) {
1767 if (!singleVolumeNumber) {
1769 Log("%s/%s is not a legitimate volume header file; %sdeleted\n", fileSysPathName, dp->d_name, (Testing ? "it would have been " : ""));
1774 char nameShouldBe[64];
1775 DiskToVolumeHeader(&vsp->header, &diskHeader);
1776 if (singleVolumeNumber && vsp->header.id == singleVolumeNumber
1777 && vsp->header.parent != singleVolumeNumber) {
1778 Log("%u is a read-only volume; not salvaged\n",
1779 singleVolumeNumber);
1782 if (!singleVolumeNumber
1783 || (vsp->header.id == singleVolumeNumber
1784 || vsp->header.parent == singleVolumeNumber)) {
1785 (void)afs_snprintf(nameShouldBe, sizeof nameShouldBe,
1786 VFORMAT, vsp->header.id);
1787 if (singleVolumeNumber)
1788 AskOffline(vsp->header.id);
1789 if (strcmp(nameShouldBe, dp->d_name)) {
1791 Log("Volume header file %s is incorrectly named; %sdeleted (it will be recreated later, if necessary)\n", dp->d_name, (Testing ? "it would have been " : ""));
1795 vsp->fileName = ToString(dp->d_name);
1805 qsort(volumeSummaryp, nVolumes, sizeof(struct VolumeSummary),
1809 /* Find the link table. This should be associated with the RW volume or, if
1810 * a RO only site, then the RO volume. For now, be cautious and hunt carefully.
1813 FindLinkHandle(register struct InodeSummary *isp, int nVols,
1814 struct ViceInodeInfo *allInodes)
1817 struct ViceInodeInfo *ip;
1819 for (i = 0; i < nVols; i++) {
1820 ip = allInodes + isp[i].index;
1821 for (j = 0; j < isp[i].nSpecialInodes; j++) {
1822 if (ip[j].u.special.type == VI_LINKTABLE)
1823 return ip[j].inodeNumber;
1830 CreateLinkTable(register struct InodeSummary *isp, Inode ino)
1832 struct versionStamp version;
1835 if (!VALID_INO(ino))
1837 IH_CREATE(NULL, fileSysDevice, fileSysPath, 0, isp->volumeId,
1838 INODESPECIAL, VI_LINKTABLE, isp->RWvolumeId);
1839 if (!VALID_INO(ino))
1841 ("Unable to allocate link table inode for volume %u (error = %d)\n",
1842 isp->RWvolumeId, errno);
1843 IH_INIT(VGLinkH, fileSysDevice, isp->RWvolumeId, ino);
1844 fdP = IH_OPEN(VGLinkH);
1846 Abort("Can't open link table for volume %u (error = %d)\n",
1847 isp->RWvolumeId, errno);
1849 if (FDH_TRUNC(fdP, 0) < 0)
1850 Abort("Can't truncate link table for volume %u (error = %d)\n",
1851 isp->RWvolumeId, errno);
1853 version.magic = LINKTABLEMAGIC;
1854 version.version = LINKTABLEVERSION;
1856 if (FDH_WRITE(fdP, (char *)&version, sizeof(version))
1858 Abort("Can't truncate link table for volume %u (error = %d)\n",
1859 isp->RWvolumeId, errno);
1861 FDH_REALLYCLOSE(fdP);
1863 /* If the volume summary exits (i.e., the V*.vol header file exists),
1864 * then set this inode there as well.
1866 if (isp->volSummary)
1867 isp->volSummary->header.linkTable = ino;
1876 SVGParms_t *parms = (SVGParms_t *) arg;
1877 DoSalvageVolumeGroup(parms->svgp_inodeSummaryp, parms->svgp_count);
1882 SalvageVolumeGroup(register struct InodeSummary *isp, int nVols)
1885 pthread_attr_t tattr;
1889 /* Initialize per volume global variables, even if later code does so */
1893 memset(&VolInfo, 0, sizeof(VolInfo));
1895 parms.svgp_inodeSummaryp = isp;
1896 parms.svgp_count = nVols;
1897 code = pthread_attr_init(&tattr);
1899 Log("Failed to salvage volume group %u: pthread_attr_init()\n",
1903 code = pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_JOINABLE);
1905 Log("Failed to salvage volume group %u: pthread_attr_setdetachstate()\n", isp->RWvolumeId);
1908 code = pthread_create(&tid, &tattr, nt_SVG, &parms);
1910 Log("Failed to create thread to salvage volume group %u\n",
1914 (void)pthread_join(tid, NULL);
1916 #endif /* AFS_NT40_ENV */
1919 DoSalvageVolumeGroup(register struct InodeSummary *isp, int nVols)
1921 struct ViceInodeInfo *inodes, *allInodes, *ip;
1922 int i, totalInodes, size, salvageTo;
1926 int dec_VGLinkH = 0;
1928 FdHandle_t *fdP = NULL;
1931 haveRWvolume = (isp->volumeId == isp->RWvolumeId
1932 && isp->nSpecialInodes > 0);
1933 if ((!ShowMounts) || (ShowMounts && !haveRWvolume)) {
1934 if (!ForceSalvage && QuickCheck(isp, nVols))
1937 if (ShowMounts && !haveRWvolume)
1939 if (canfork && !debug && Fork() != 0) {
1940 (void)Wait("Salvage volume group");
1943 for (i = 0, totalInodes = 0; i < nVols; i++)
1944 totalInodes += isp[i].nInodes;
1945 size = totalInodes * sizeof(struct ViceInodeInfo);
1946 inodes = (struct ViceInodeInfo *)malloc(size);
1947 allInodes = inodes - isp->index; /* this would the base of all the inodes
1948 * for the partition, if all the inodes
1949 * had been read into memory */
1951 (inodeFd, isp->index * sizeof(struct ViceInodeInfo),
1953 assert(read(inodeFd, inodes, size) == size);
1955 /* Don't try to salvage a read write volume if there isn't one on this
1957 salvageTo = haveRWvolume ? 0 : 1;
1959 #ifdef AFS_NAMEI_ENV
1960 ino = FindLinkHandle(isp, nVols, allInodes);
1961 if (VALID_INO(ino)) {
1962 IH_INIT(VGLinkH, fileSysDevice, isp->RWvolumeId, ino);
1963 fdP = IH_OPEN(VGLinkH);
1965 if (!VALID_INO(ino) || fdP == NULL) {
1966 Log("%s link table for volume %u.\n",
1967 Testing ? "Would have recreated" : "Recreating", isp->RWvolumeId);
1969 IH_INIT(VGLinkH, fileSysDevice, -1, -1);
1971 CreateLinkTable(isp, ino);
1975 FDH_REALLYCLOSE(fdP);
1977 IH_INIT(VGLinkH, fileSysDevice, -1, -1);
1980 /* Salvage in reverse order--read/write volume last; this way any
1981 * Inodes not referenced by the time we salvage the read/write volume
1982 * can be picked up by the read/write volume */
1983 /* ACTUALLY, that's not done right now--the inodes just vanish */
1984 for (i = nVols - 1; i >= salvageTo; i--) {
1986 struct InodeSummary *lisp = &isp[i];
1987 #ifdef AFS_NAMEI_ENV
1988 /* If only the RO is present on this partition, the link table
1989 * shows up as a RW volume special file. Need to make sure the
1990 * salvager doesn't try to salvage the non-existent RW.
1992 if (rw && nVols > 1 && isp[i].nSpecialInodes == 1) {
1993 /* If this only special inode is the link table, continue */
1994 if (inodes->u.special.type == VI_LINKTABLE) {
2001 Log("%s VOLUME %u%s.\n", rw ? "SALVAGING" : "CHECKING CLONED",
2002 lisp->volumeId, (Testing ? "(READONLY mode)" : ""));
2003 /* Check inodes twice. The second time do things seriously. This
2004 * way the whole RO volume can be deleted, below, if anything goes wrong */
2005 for (check = 1; check >= 0; check--) {
2007 if (SalvageVolumeHeaderFile(lisp, allInodes, rw, check, &deleteMe)
2009 MaybeZapVolume(lisp, "Volume header", deleteMe, check);
2010 if (rw && deleteMe) {
2011 haveRWvolume = 0; /* This will cause its inodes to be deleted--since salvage
2012 * volume won't be called */
2018 if (rw && check == 1)
2020 if (SalvageVnodes(isp, lisp, allInodes, check) == -1) {
2021 MaybeZapVolume(lisp, "Vnode index", 0, check);
2027 /* Fix actual inode counts */
2029 for (ip = inodes; totalInodes; ip++, totalInodes--) {
2030 static int TraceBadLinkCounts = 0;
2031 #ifdef AFS_NAMEI_ENV
2032 if (VGLinkH->ih_ino == ip->inodeNumber) {
2033 dec_VGLinkH = ip->linkCount - VGLinkH_cnt;
2034 VGLinkH_p1 = ip->u.param[0];
2035 continue; /* Deal with this last. */
2038 if (ip->linkCount != 0 && TraceBadLinkCounts) {
2039 TraceBadLinkCounts--; /* Limit reports, per volume */
2040 Log("#### DEBUG #### Link count incorrect by %d; inode %s, size %llu, p=(%u,%u,%u,%u)\n", ip->linkCount, PrintInode(NULL, ip->inodeNumber), (afs_uintmax_t) ip->byteCount, ip->u.param[0], ip->u.param[1], ip->u.param[2], ip->u.param[3]);
2042 while (ip->linkCount > 0) {
2043 /* below used to assert, not break */
2045 if (IH_DEC(VGLinkH, ip->inodeNumber, ip->u.param[0])) {
2046 Log("idec failed. inode %s errno %d\n",
2047 PrintInode(NULL, ip->inodeNumber), errno);
2053 while (ip->linkCount < 0) {
2054 /* these used to be asserts */
2056 if (IH_INC(VGLinkH, ip->inodeNumber, ip->u.param[0])) {
2057 Log("iinc failed. inode %s errno %d\n",
2058 PrintInode(NULL, ip->inodeNumber), errno);
2065 #ifdef AFS_NAMEI_ENV
2066 while (dec_VGLinkH > 0) {
2067 if (IH_DEC(VGLinkH, VGLinkH->ih_ino, VGLinkH_p1) < 0) {
2068 Log("idec failed on link table, errno = %d\n", errno);
2072 while (dec_VGLinkH < 0) {
2073 if (IH_INC(VGLinkH, VGLinkH->ih_ino, VGLinkH_p1) < 0) {
2074 Log("iinc failed on link table, errno = %d\n", errno);
2081 /* Directory consistency checks on the rw volume */
2083 SalvageVolume(isp, VGLinkH);
2084 IH_RELEASE(VGLinkH);
2086 if (canfork && !debug) {
2093 QuickCheck(register struct InodeSummary *isp, int nVols)
2095 /* Check headers BEFORE forking */
2099 for (i = 0; i < nVols; i++) {
2100 struct VolumeSummary *vs = isp[i].volSummary;
2101 VolumeDiskData volHeader;
2103 /* Don't salvage just because phantom rw volume is there... */
2104 /* (If a read-only volume exists, read/write inodes must also exist) */
2105 if (i == 0 && isp->nSpecialInodes == 0 && nVols > 1)
2109 IH_INIT(h, fileSysDevice, vs->header.parent, vs->header.volumeInfo);
2110 if (IH_IREAD(h, 0, (char *)&volHeader, sizeof(volHeader))
2111 == sizeof(volHeader)
2112 && volHeader.stamp.magic == VOLUMEINFOMAGIC
2113 && volHeader.dontSalvage == DONT_SALVAGE
2114 && volHeader.needsSalvaged == 0 && volHeader.destroyMe == 0) {
2115 if (volHeader.inUse == 1) {
2116 volHeader.inUse = 0;
2117 volHeader.inService = 1;
2119 if (IH_IWRITE(h, 0, (char *)&volHeader, sizeof(volHeader))
2120 != sizeof(volHeader)) {
2136 /* SalvageVolumeHeaderFile
2138 * Salvage the top level V*.vol header file. Make sure the special files
2139 * exist and that there are no duplicates.
2141 * Calls SalvageHeader for each possible type of volume special file.
2145 SalvageVolumeHeaderFile(register struct InodeSummary *isp,
2146 register struct ViceInodeInfo *inodes, int RW,
2147 int check, int *deleteMe)
2151 register struct ViceInodeInfo *ip;
2152 int allinodesobsolete = 1;
2153 struct VolumeDiskHeader diskHeader;
2157 memset(&tempHeader, 0, sizeof(tempHeader));
2158 tempHeader.stamp.magic = VOLUMEHEADERMAGIC;
2159 tempHeader.stamp.version = VOLUMEHEADERVERSION;
2160 tempHeader.id = isp->volumeId;
2161 tempHeader.parent = isp->RWvolumeId;
2162 /* Check for duplicates (inodes are sorted by type field) */
2163 for (i = 0; i < isp->nSpecialInodes - 1; i++) {
2164 ip = &inodes[isp->index + i];
2165 if (ip->u.special.type == (ip + 1)->u.special.type) {
2167 Log("Duplicate special inodes in volume header; salvage of volume %u aborted\n", isp->volumeId);
2171 for (i = 0; i < isp->nSpecialInodes; i++) {
2172 ip = &inodes[isp->index + i];
2173 if (ip->u.special.type <= 0 || ip->u.special.type > MAXINODETYPE) {
2175 Log("Rubbish header inode\n");
2178 Log("Rubbish header inode; deleted\n");
2179 } else if (!stuff[ip->u.special.type - 1].obsolete) {
2180 *(stuff[ip->u.special.type - 1].inode) = ip->inodeNumber;
2181 if (!check && ip->u.special.type != VI_LINKTABLE)
2182 ip->linkCount--; /* Keep the inode around */
2183 allinodesobsolete = 0;
2187 if (allinodesobsolete) {
2194 VGLinkH_cnt++; /* one for every header. */
2196 if (!RW && !check && isp->volSummary) {
2197 ClearROInUseBit(isp->volSummary);
2201 for (i = 0; i < MAXINODETYPE; i++) {
2202 if (stuff[i].inodeType == VI_LINKTABLE) {
2203 /* Gross hack: SalvageHeader does a bcmp on the volume header.
2204 * And we may have recreated the link table earlier, so set the
2205 * RW header as well.
2207 if (VALID_INO(VGLinkH->ih_ino)) {
2208 *stuff[i].inode = VGLinkH->ih_ino;
2212 if (SalvageHeader(&stuff[i], isp, check, deleteMe) == -1 && check)
2216 if (isp->volSummary == NULL) {
2218 (void)afs_snprintf(name, sizeof name, VFORMAT, isp->volumeId);
2220 Log("No header file for volume %u\n", isp->volumeId);
2224 Log("No header file for volume %u; %screating %s/%s\n",
2225 isp->volumeId, (Testing ? "it would have been " : ""),
2226 fileSysPathName, name);
2227 headerFd = afs_open(name, O_RDWR | O_CREAT | O_TRUNC, 0644);
2228 assert(headerFd != -1);
2229 isp->volSummary = (struct VolumeSummary *)
2230 malloc(sizeof(struct VolumeSummary));
2231 isp->volSummary->fileName = ToString(name);
2234 /* hack: these two fields are obsolete... */
2235 isp->volSummary->header.volumeAcl = 0;
2236 isp->volSummary->header.volumeMountTable = 0;
2239 (&isp->volSummary->header, &tempHeader,
2240 sizeof(struct VolumeHeader))) {
2241 /* We often remove the name before calling us, so we make a fake one up */
2242 if (isp->volSummary->fileName) {
2243 strcpy(name, isp->volSummary->fileName);
2245 (void)afs_snprintf(name, sizeof name, VFORMAT, isp->volumeId);
2246 isp->volSummary->fileName = ToString(name);
2249 Log("Header file %s is damaged or no longer valid%s\n", name,
2250 (check ? "" : "; repairing"));
2254 headerFd = afs_open(name, O_RDWR | O_TRUNC, 0644);
2255 assert(headerFd != -1);
2259 memcpy(&isp->volSummary->header, &tempHeader,
2260 sizeof(struct VolumeHeader));
2263 Log("It would have written a new header file for volume %u\n",
2266 VolumeHeaderToDisk(&diskHeader, &tempHeader);
2267 if (write(headerFd, &diskHeader, sizeof(struct VolumeDiskHeader))
2268 != sizeof(struct VolumeDiskHeader)) {
2269 Log("Couldn't rewrite volume header file!\n");
2276 IH_INIT(isp->volSummary->volumeInfoHandle, fileSysDevice, isp->RWvolumeId,
2277 isp->volSummary->header.volumeInfo);
2282 SalvageHeader(register struct stuff *sp, struct InodeSummary *isp, int check,
2286 VolumeDiskData volumeInfo;
2287 struct versionStamp fileHeader;
2296 #ifndef AFS_NAMEI_ENV
2297 if (sp->inodeType == VI_LINKTABLE)
2300 if (*(sp->inode) == 0) {
2302 Log("Missing inode in volume header (%s)\n", sp->description);
2306 Log("Missing inode in volume header (%s); %s\n", sp->description,
2307 (Testing ? "it would have recreated it" : "recreating"));
2310 IH_CREATE(NULL, fileSysDevice, fileSysPath, 0, isp->volumeId,
2311 INODESPECIAL, sp->inodeType, isp->RWvolumeId);
2312 if (!VALID_INO(*(sp->inode)))
2314 ("Unable to allocate inode (%s) for volume header (error = %d)\n",
2315 sp->description, errno);
2320 IH_INIT(specH, fileSysDevice, isp->RWvolumeId, *(sp->inode));
2321 fdP = IH_OPEN(specH);
2322 if (OKToZap && (fdP == NULL) && BadError(errno)) {
2323 /* bail out early and destroy the volume */
2325 Log("Still can't open volume header inode (%s), destroying volume\n", sp->description);
2332 Abort("Unable to open inode (%s) of volume header (error = %d)\n",
2333 sp->description, errno);
2336 && (FDH_READ(fdP, (char *)&header, sp->size) != sp->size
2337 || header.fileHeader.magic != sp->stamp.magic)) {
2339 Log("Part of the header (%s) is corrupted\n", sp->description);
2340 FDH_REALLYCLOSE(fdP);
2344 Log("Part of the header (%s) is corrupted; recreating\n",
2348 if (sp->inodeType == VI_VOLINFO
2349 && header.volumeInfo.destroyMe == DESTROY_ME) {
2352 FDH_REALLYCLOSE(fdP);
2356 if (recreate && !Testing) {
2359 ("Internal error: recreating volume header (%s) in check mode\n",
2361 code = FDH_TRUNC(fdP, 0);
2363 Abort("Unable to truncate volume header file (%s) (error = %d)\n",
2364 sp->description, errno);
2366 /* The following code should be moved into vutil.c */
2367 if (sp->inodeType == VI_VOLINFO) {
2369 memset(&header.volumeInfo, 0, sizeof(header.volumeInfo));
2370 header.volumeInfo.stamp = sp->stamp;
2371 header.volumeInfo.id = isp->volumeId;
2372 header.volumeInfo.parentId = isp->RWvolumeId;
2373 sprintf(header.volumeInfo.name, "bogus.%u", isp->volumeId);
2374 Log("Warning: the name of volume %u is now \"bogus.%u\"\n",
2375 isp->volumeId, isp->volumeId);
2376 header.volumeInfo.inService = 0;
2377 header.volumeInfo.blessed = 0;
2378 /* The + 1000 is a hack in case there are any files out in venus caches */
2379 header.volumeInfo.uniquifier = (isp->maxUniquifier + 1) + 1000;
2380 header.volumeInfo.type = (isp->volumeId == isp->RWvolumeId ? readwriteVolume : readonlyVolume); /* XXXX */
2381 header.volumeInfo.needsCallback = 0;
2382 gettimeofday(&tp, 0);
2383 header.volumeInfo.creationDate = tp.tv_sec;
2384 if (FDH_SEEK(fdP, 0, SEEK_SET) < 0) {
2386 ("Unable to seek to beginning of volume header file (%s) (errno = %d)\n",
2387 sp->description, errno);
2390 FDH_WRITE(fdP, (char *)&header.volumeInfo,
2391 sizeof(header.volumeInfo));
2392 if (code != sizeof(header.volumeInfo)) {
2395 ("Unable to write volume header file (%s) (errno = %d)\n",
2396 sp->description, errno);
2397 Abort("Unable to write entire volume header file (%s)\n",
2401 if (FDH_SEEK(fdP, 0, SEEK_SET) < 0) {
2403 ("Unable to seek to beginning of volume header file (%s) (errno = %d)\n",
2404 sp->description, errno);
2406 code = FDH_WRITE(fdP, (char *)&sp->stamp, sizeof(sp->stamp));
2407 if (code != sizeof(sp->stamp)) {
2410 ("Unable to write version stamp in volume header file (%s) (errno = %d)\n",
2411 sp->description, errno);
2413 ("Unable to write entire version stamp in volume header file (%s)\n",
2418 FDH_REALLYCLOSE(fdP);
2420 if (sp->inodeType == VI_VOLINFO) {
2421 VolInfo = header.volumeInfo;
2424 if (VolInfo.updateDate) {
2425 strcpy(update, TimeStamp(VolInfo.updateDate, 0));
2427 Log("%s (%u) %supdated %s\n", VolInfo.name, VolInfo.id,
2428 (Testing ? "it would have been " : ""), update);
2430 strcpy(update, TimeStamp(VolInfo.creationDate, 0));
2432 Log("%s (%u) not updated (created %s)\n", VolInfo.name,
2433 VolInfo.id, update);
2443 SalvageVnodes(register struct InodeSummary *rwIsp,
2444 register struct InodeSummary *thisIsp,
2445 register struct ViceInodeInfo *inodes, int check)
2447 int ilarge, ismall, ioffset, RW, nInodes;
2448 ioffset = rwIsp->index + rwIsp->nSpecialInodes; /* first inode */
2451 RW = (rwIsp == thisIsp);
2452 nInodes = (rwIsp->nInodes - rwIsp->nSpecialInodes);
2454 SalvageIndex(thisIsp->volSummary->header.smallVnodeIndex, vSmall, RW,
2455 &inodes[ioffset], nInodes, thisIsp->volSummary, check);
2456 if (check && ismall == -1)
2459 SalvageIndex(thisIsp->volSummary->header.largeVnodeIndex, vLarge, RW,
2460 &inodes[ioffset], nInodes, thisIsp->volSummary, check);
2461 return (ilarge == 0 && ismall == 0 ? 0 : -1);
2465 SalvageIndex(Inode ino, VnodeClass class, int RW,
2466 register struct ViceInodeInfo *ip, int nInodes,
2467 struct VolumeSummary *volSummary, int check)
2469 VolumeId volumeNumber;
2470 char buf[SIZEOF_LARGEDISKVNODE];
2471 struct VnodeDiskObject *vnode = (struct VnodeDiskObject *)buf;
2473 StreamHandle_t *file;
2474 struct VnodeClassInfo *vcp;
2476 afs_fsize_t vnodeLength;
2477 int vnodeIndex, nVnodes;
2478 afs_ino_str_t stmp1, stmp2;
2482 volumeNumber = volSummary->header.id;
2483 IH_INIT(handle, fileSysDevice, volSummary->header.parent, ino);
2484 fdP = IH_OPEN(handle);
2485 assert(fdP != NULL);
2486 file = FDH_FDOPEN(fdP, "r+");
2487 assert(file != NULL);
2488 vcp = &VnodeClassInfo[class];
2489 size = OS_SIZE(fdP->fd_fd);
2491 nVnodes = (size / vcp->diskSize) - 1;
2493 assert((nVnodes + 1) * vcp->diskSize == size);
2494 assert(STREAM_SEEK(file, vcp->diskSize, 0) == 0);
2498 for (vnodeIndex = 0;
2499 nVnodes && STREAM_READ(vnode, vcp->diskSize, 1, file) == 1;
2500 nVnodes--, vnodeIndex++) {
2501 if (vnode->type != vNull) {
2502 int vnodeChanged = 0;
2503 int vnodeNumber = bitNumberToVnodeNumber(vnodeIndex, class);
2504 /* Log programs that belong to root (potentially suid root);
2505 * don't bother for read-only or backup volumes */
2506 #ifdef notdef /* This is done elsewhere */
2507 if (ShowRootFiles && RW && vnode->owner == 0 && vnodeNumber != 1)
2508 Log("OWNER IS ROOT %s %u dir %u vnode %u author %u owner %u mode %o\n", VolInfo.name, volumeNumber, vnode->parent, vnodeNumber, vnode->author, vnode->owner, vnode->modeBits);
2510 if (VNDISK_GET_INO(vnode) == 0) {
2512 /* Log("### DEBUG ### Deleted Vnode with 0 inode (vnode %d)\n", vnodeNumber); */
2513 memset(vnode, 0, vcp->diskSize);
2517 if (vcp->magic != vnode->vnodeMagic) {
2518 /* bad magic #, probably partially created vnode */
2519 Log("Partially allocated vnode %d deleted.\n",
2521 memset(vnode, 0, vcp->diskSize);
2525 /* ****** Should do a bit more salvage here: e.g. make sure
2526 * vnode type matches what it should be given the index */
2527 while (nInodes && ip->u.vnode.vnodeNumber < vnodeNumber) {
2528 /* if (vnodeIdToClass(ip->u.vnode.vnodeNumber) == class && RW) {
2529 * Log("Inode %d: says it belongs to non-existing vnode %d\n",
2530 * ip->inodeNumber, ip->u.vnode.vnodeNumber);
2537 while (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2538 /* The following doesn't work, because the version number
2539 * is not maintained correctly by the file server */
2540 /*if (vnode->uniquifier == ip->u.vnode.vnodeUniquifier &&
2541 * vnode->dataVersion == ip->u.vnode.inodeDataVersion)
2543 if (VNDISK_GET_INO(vnode) == ip->inodeNumber)
2549 /* For RW volume, look for vnode with matching inode number;
2550 * if no such match, take the first determined by our sort
2552 register struct ViceInodeInfo *lip = ip;
2553 register lnInodes = nInodes;
2555 && lip->u.vnode.vnodeNumber == vnodeNumber) {
2556 if (VNDISK_GET_INO(vnode) == lip->inodeNumber) {
2565 if (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2566 /* "Matching" inode */
2570 vu = vnode->uniquifier;
2571 iu = ip->u.vnode.vnodeUniquifier;
2572 vd = vnode->dataVersion;
2573 id = ip->u.vnode.inodeDataVersion;
2575 * Because of the possibility of the uniquifier overflows (> 4M)
2576 * we compare them modulo the low 22-bits; we shouldn't worry
2577 * about mismatching since they shouldn't to many old
2578 * uniquifiers of the same vnode...
2580 if (IUnique(vu) != IUnique(iu)) {
2582 Log("Vnode %u: vnode.unique, %u, does not match inode unique, %u; fixed, but status will be wrong\n", vnodeNumber, IUnique(vu), IUnique(iu));
2585 vnode->uniquifier = iu;
2586 #ifdef AFS_3DISPARES
2587 vnode->dataVersion = (id >= vd ?
2590 1887437 ? vd : id) :
2593 1887437 ? id : vd));
2595 #if defined(AFS_SGI_EXMAG)
2596 vnode->dataVersion = (id >= vd ?
2599 15099494 ? vd : id) :
2602 15099494 ? id : vd));
2604 vnode->dataVersion = (id > vd ? id : vd);
2605 #endif /* AFS_SGI_EXMAG */
2606 #endif /* AFS_3DISPARES */
2609 /* don't bother checking for vd > id any more, since
2610 * partial file transfers always result in this state,
2611 * and you can't do much else anyway (you've already
2612 * found the best data you can) */
2613 #ifdef AFS_3DISPARES
2614 if (!vnodeIsDirectory(vnodeNumber)
2615 && ((vd < id && (id - vd) < 1887437)
2616 || ((vd > id && (vd - id) > 1887437)))) {
2618 #if defined(AFS_SGI_EXMAG)
2619 if (!vnodeIsDirectory(vnodeNumber)
2620 && ((vd < id && (id - vd) < 15099494)
2621 || ((vd > id && (vd - id) > 15099494)))) {
2623 if (!vnodeIsDirectory(vnodeNumber) && vd < id) {
2624 #endif /* AFS_SGI_EXMAG */
2627 Log("Vnode %d: version < inode version; fixed (old status)\n", vnodeNumber);
2628 vnode->dataVersion = id;
2633 if (ip->inodeNumber != VNDISK_GET_INO(vnode)) {
2636 Log("Vnode %d: inode number incorrect (is %s should be %s). FileSize=%llu\n", vnodeNumber, PrintInode(stmp1, VNDISK_GET_INO(vnode)), PrintInode(stmp2, ip->inodeNumber), (afs_uintmax_t) ip->byteCount);
2638 VNDISK_SET_INO(vnode, ip->inodeNumber);
2643 Log("Vnode %d: inode number incorrect; changed from %s to %s. FileSize=%llu\n", vnodeNumber, PrintInode(stmp1, VNDISK_GET_INO(vnode)), PrintInode(stmp2, ip->inodeNumber), (afs_uintmax_t) ip->byteCount);
2645 VNDISK_SET_INO(vnode, ip->inodeNumber);
2648 VNDISK_GET_LEN(vnodeLength, vnode);
2649 if (ip->byteCount != vnodeLength) {
2652 Log("Vnode %d: length incorrect; (is %llu should be %llu)\n", vnodeNumber, (afs_uintmax_t) vnodeLength, (afs_uintmax_t) ip->byteCount);
2657 Log("Vnode %d: length incorrect; changed from %llu to %llu\n", vnodeNumber, (afs_uintmax_t) vnodeLength, (afs_uintmax_t) ip->byteCount);
2658 VNDISK_SET_LEN(vnode, ip->byteCount);
2662 ip->linkCount--; /* Keep the inode around */
2665 } else { /* no matching inode */
2666 if (VNDISK_GET_INO(vnode) != 0
2667 || vnode->type == vDirectory) {
2668 /* No matching inode--get rid of the vnode */
2670 if (VNDISK_GET_INO(vnode)) {
2672 Log("Vnode %d (unique %u): corresponding inode %s is missing\n", vnodeNumber, vnode->uniquifier, PrintInode(NULL, VNDISK_GET_INO(vnode)));
2676 Log("Vnode %d (unique %u): bad directory vnode (no inode number listed)\n", vnodeNumber, vnode->uniquifier);
2681 if (VNDISK_GET_INO(vnode)) {
2683 Log("Vnode %d (unique %u): corresponding inode %s is missing; vnode deleted, vnode mod time=%s", vnodeNumber, vnode->uniquifier, PrintInode(NULL, VNDISK_GET_INO(vnode)), ctime((time_t *) & (vnode->serverModifyTime)));
2687 Log("Vnode %d (unique %u): bad directory vnode (no inode number listed); vnode deleted, vnode mod time=%s", vnodeNumber, vnode->uniquifier, ctime((time_t *) & (vnode->serverModifyTime)));
2689 memset(vnode, 0, vcp->diskSize);
2692 /* Should not reach here becuase we checked for
2693 * (inodeNumber == 0) above. And where we zero the vnode,
2694 * we also goto vnodeDone.
2698 while (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2702 } /* VNDISK_GET_INO(vnode) != 0 */
2704 assert(!(vnodeChanged && check));
2705 if (vnodeChanged && !Testing) {
2707 (handle, vnodeIndexOffset(vcp, vnodeNumber),
2708 (char *)vnode, vcp->diskSize)
2710 VolumeChanged = 1; /* For break call back */
2721 struct VnodeEssence *
2722 CheckVnodeNumber(VnodeId vnodeNumber)
2725 struct VnodeInfo *vip;
2728 class = vnodeIdToClass(vnodeNumber);
2729 vip = &vnodeInfo[class];
2730 offset = vnodeIdToBitNumber(vnodeNumber);
2731 return (offset >= vip->nVnodes ? NULL : &vip->vnodes[offset]);
2735 CopyOnWrite(register struct DirSummary *dir)
2737 /* Copy the directory unconditionally if we are going to change it:
2738 * not just if was cloned.
2740 struct VnodeDiskObject vnode;
2741 struct VnodeClassInfo *vcp = &VnodeClassInfo[vLarge];
2742 Inode oldinode, newinode;
2745 if (dir->copied || Testing)
2747 DFlush(); /* Well justified paranoia... */
2750 IH_IREAD(vnodeInfo[vLarge].handle,
2751 vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2753 assert(code == sizeof(vnode));
2754 oldinode = VNDISK_GET_INO(&vnode);
2755 /* Increment the version number by a whole lot to avoid problems with
2756 * clients that were promised new version numbers--but the file server
2757 * crashed before the versions were written to disk.
2760 IH_CREATE(dir->ds_linkH, fileSysDevice, fileSysPath, 0, dir->rwVid,
2761 dir->vnodeNumber, vnode.uniquifier, vnode.dataVersion +=
2763 assert(VALID_INO(newinode));
2764 assert(CopyInode(fileSysDevice, oldinode, newinode, dir->rwVid) == 0);
2766 VNDISK_SET_INO(&vnode, newinode);
2768 IH_IWRITE(vnodeInfo[vLarge].handle,
2769 vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2771 assert(code == sizeof(vnode));
2773 SetSalvageDirHandle(&dir->dirHandle, dir->dirHandle.dirh_handle->ih_vid,
2774 fileSysDevice, newinode);
2775 /* Don't delete the original inode right away, because the directory is
2776 * still being scanned.
2782 * This function should either successfully create a new dir, or give up
2783 * and leave things the way they were. In particular, if it fails to write
2784 * the new dir properly, it should return w/o changing the reference to the
2788 CopyAndSalvage(register struct DirSummary *dir)
2790 struct VnodeDiskObject vnode;
2791 struct VnodeClassInfo *vcp = &VnodeClassInfo[vLarge];
2792 Inode oldinode, newinode;
2794 register afs_int32 code;
2795 afs_int32 parentUnique = 1;
2796 struct VnodeEssence *vnodeEssence;
2800 Log("Salvaging directory %u...\n", dir->vnodeNumber);
2802 IH_IREAD(vnodeInfo[vLarge].handle,
2803 vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2805 assert(code == sizeof(vnode));
2806 oldinode = VNDISK_GET_INO(&vnode);
2807 /* Increment the version number by a whole lot to avoid problems with
2808 * clients that were promised new version numbers--but the file server
2809 * crashed before the versions were written to disk.
2812 IH_CREATE(dir->ds_linkH, fileSysDevice, fileSysPath, 0, dir->rwVid,
2813 dir->vnodeNumber, vnode.uniquifier, vnode.dataVersion +=
2815 assert(VALID_INO(newinode));
2816 SetSalvageDirHandle(&newdir, dir->rwVid, fileSysDevice, newinode);
2818 /* Assign . and .. vnode numbers from dir and vnode.parent.
2819 * The uniquifier for . is in the vnode.
2820 * The uniquifier for .. might be set to a bogus value of 1 and
2821 * the salvager will later clean it up.
2823 if (vnode.parent && (vnodeEssence = CheckVnodeNumber(vnode.parent))) {
2824 parentUnique = (vnodeEssence->unique ? vnodeEssence->unique : 1);
2827 DirSalvage(&dir->dirHandle, &newdir, dir->vnodeNumber,
2829 (vnode.parent ? vnode.parent : dir->vnodeNumber),
2834 /* didn't really build the new directory properly, let's just give up. */
2835 code = IH_DEC(dir->ds_linkH, newinode, dir->rwVid);
2837 Log("Directory salvage returned code %d, continuing.\n", code);
2840 Log("Checking the results of the directory salvage...\n");
2841 if (!DirOK(&newdir)) {
2842 Log("Directory salvage failed!!!; restoring old version of the directory.\n");
2843 code = IH_DEC(dir->ds_linkH, newinode, dir->rwVid);
2848 VNDISK_SET_INO(&vnode, newinode);
2849 VNDISK_SET_LEN(&vnode, Length(&newdir));
2851 IH_IWRITE(vnodeInfo[vLarge].handle,
2852 vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2854 assert(code == sizeof(vnode));
2856 nt_sync(fileSysDevice);
2858 sync(); /* this is slow, but hopefully rarely called. We don't have
2859 * an open FD on the file itself to fsync.
2862 code = IH_DEC(dir->ds_linkH, oldinode, dir->rwVid);
2864 dir->dirHandle = newdir;
2868 JudgeEntry(struct DirSummary *dir, char *name, VnodeId vnodeNumber,
2871 struct VnodeEssence *vnodeEssence;
2872 afs_int32 dirOrphaned, todelete;
2874 dirOrphaned = IsVnodeOrphaned(dir->vnodeNumber);
2876 vnodeEssence = CheckVnodeNumber(vnodeNumber);
2877 if (vnodeEssence == NULL) {
2879 Log("dir vnode %u: invalid entry deleted: %s/%s (vnode %u, unique %u)\n", dir->vnodeNumber, dir->name ? dir->name : "??", name, vnodeNumber, unique);
2883 assert(Delete(&dir->dirHandle, name) == 0);
2888 #ifndef AFS_NAMEI_ENV
2889 /* On AIX machines, don't allow entries to point to inode 0. That is a special
2890 * mount inode for the partition. If this inode were deleted, it would crash
2893 if (vnodeEssence->InodeNumber == 0) {
2894 Log("dir vnode %d: invalid entry: %s/%s has no inode (vnode %d, unique %d)%s\n", dir->vnodeNumber, (dir->name ? dir->name : "??"), name, vnodeNumber, unique, (Testing ? "-- would have deleted" : " -- deleted"));
2897 assert(Delete(&dir->dirHandle, name) == 0);
2904 if (!(vnodeNumber & 1) && !Showmode
2905 && !(vnodeEssence->count || vnodeEssence->unique
2906 || vnodeEssence->modeBits)) {
2907 Log("dir vnode %u: invalid entry: %s/%s (vnode %u, unique %u)%s\n",
2908 dir->vnodeNumber, (dir->name ? dir->name : "??"), name,
2909 vnodeNumber, unique,
2910 ((!unique) ? (Testing ? "-- would have deleted" : " -- deleted") :
2915 assert(Delete(&dir->dirHandle, name) == 0);
2921 /* Check if the Uniquifiers match. If not, change the directory entry
2922 * so its unique matches the vnode unique. Delete if the unique is zero
2923 * or if the directory is orphaned.
2925 if (!vnodeEssence->unique || (vnodeEssence->unique) != unique) {
2926 if (!vnodeEssence->unique
2927 && ((strcmp(name, "..") == 0) || (strcmp(name, ".") == 0))) {
2928 /* This is an orphaned directory. Don't delete the . or ..
2929 * entry. Otherwise, it will get created in the next
2930 * salvage and deleted again here. So Just skip it.
2935 todelete = ((!vnodeEssence->unique || dirOrphaned) ? 1 : 0);
2938 Log("dir vnode %u: %s/%s (vnode %u): unique changed from %u to %u %s\n", dir->vnodeNumber, (dir->name ? dir->name : "??"), name, vnodeNumber, unique, vnodeEssence->unique, (!todelete ? "" : (Testing ? "-- would have deleted" : "-- deleted")));
2942 fid.Vnode = vnodeNumber;
2943 fid.Unique = vnodeEssence->unique;
2945 assert(Delete(&dir->dirHandle, name) == 0);
2947 assert(Create(&dir->dirHandle, name, &fid) == 0);
2950 return; /* no need to continue */
2953 if (strcmp(name, ".") == 0) {
2954 if (dir->vnodeNumber != vnodeNumber || (dir->unique != unique)) {
2957 Log("directory vnode %u.%u: bad '.' entry (was %u.%u); fixed\n", dir->vnodeNumber, dir->unique, vnodeNumber, unique);
2960 assert(Delete(&dir->dirHandle, ".") == 0);
2961 fid.Vnode = dir->vnodeNumber;
2962 fid.Unique = dir->unique;
2963 assert(Create(&dir->dirHandle, ".", &fid) == 0);
2966 vnodeNumber = fid.Vnode; /* Get the new Essence */
2967 unique = fid.Unique;
2968 vnodeEssence = CheckVnodeNumber(vnodeNumber);
2971 } else if (strcmp(name, "..") == 0) {
2974 struct VnodeEssence *dotdot;
2975 pa.Vnode = dir->parent;
2976 dotdot = CheckVnodeNumber(pa.Vnode);
2977 assert(dotdot != NULL); /* XXX Should not be assert */
2978 pa.Unique = dotdot->unique;
2980 pa.Vnode = dir->vnodeNumber;
2981 pa.Unique = dir->unique;
2983 if ((pa.Vnode != vnodeNumber) || (pa.Unique != unique)) {
2985 Log("directory vnode %u.%u: bad '..' entry (was %u.%u); fixed\n", dir->vnodeNumber, dir->unique, vnodeNumber, unique);
2988 assert(Delete(&dir->dirHandle, "..") == 0);
2989 assert(Create(&dir->dirHandle, "..", &pa) == 0);
2992 vnodeNumber = pa.Vnode; /* Get the new Essence */
2994 vnodeEssence = CheckVnodeNumber(vnodeNumber);
2996 dir->haveDotDot = 1;
2997 } else if (strncmp(name, ".__afs", 6) == 0) {
2999 Log("dir vnode %u: special old unlink-while-referenced file %s %s deleted (vnode %u)\n", dir->vnodeNumber, name, (Testing ? "would have been" : "is"), vnodeNumber);
3003 assert(Delete(&dir->dirHandle, name) == 0);
3005 vnodeEssence->claimed = 0; /* Not claimed: Orphaned */
3006 vnodeEssence->todelete = 1; /* Will later delete vnode and decr inode */
3009 if (ShowSuid && (vnodeEssence->modeBits & 06000))
3010 Log("FOUND suid/sgid file: %s/%s (%u.%u %05o) author %u (vnode %u dir %u)\n", dir->name ? dir->name : "??", name, vnodeEssence->owner, vnodeEssence->group, vnodeEssence->modeBits, vnodeEssence->author, vnodeNumber, dir->vnodeNumber);
3011 if (ShowMounts && (vnodeEssence->type == vSymlink)
3012 && !(vnodeEssence->modeBits & 0111)) {
3018 IH_INIT(ihP, fileSysDevice, dir->dirHandle.dirh_handle->ih_vid,
3019 vnodeEssence->InodeNumber);
3021 assert(fdP != NULL);
3022 size = FDH_SIZE(fdP);
3024 memset(buf, 0, 1024);
3027 code = FDH_READ(fdP, buf, size);
3028 assert(code == size);
3029 Log("In volume %u (%s) found mountpoint %s/%s to '%s'\n",
3030 dir->dirHandle.dirh_handle->ih_vid, dir->vname,
3031 dir->name ? dir->name : "??", name, buf);
3032 FDH_REALLYCLOSE(fdP);
3035 if (ShowRootFiles && vnodeEssence->owner == 0 && vnodeNumber != 1)
3036 Log("FOUND root file: %s/%s (%u.%u %05o) author %u (vnode %u dir %u)\n", dir->name ? dir->name : "??", name, vnodeEssence->owner, vnodeEssence->group, vnodeEssence->modeBits, vnodeEssence->author, vnodeNumber, dir->vnodeNumber);
3037 if (vnodeIdToClass(vnodeNumber) == vLarge
3038 && vnodeEssence->name == NULL) {
3040 if ((n = (char *)malloc(strlen(name) + 1)))
3042 vnodeEssence->name = n;
3045 /* The directory entry points to the vnode. Check to see if the
3046 * vnode points back to the directory. If not, then let the
3047 * directory claim it (else it might end up orphaned). Vnodes
3048 * already claimed by another directory are deleted from this
3049 * directory: hardlinks to the same vnode are not allowed
3050 * from different directories.
3052 if (vnodeEssence->parent != dir->vnodeNumber) {
3053 if (!vnodeEssence->claimed && !dirOrphaned && vnodeNumber != 1) {
3054 /* Vnode does not point back to this directory.
3055 * Orphaned dirs cannot claim a file (it may belong to
3056 * another non-orphaned dir).
3059 Log("dir vnode %u: %s/%s (vnode %u, unique %u) -- parent vnode %schanged from %u to %u\n", dir->vnodeNumber, (dir->name ? dir->name : "??"), name, vnodeNumber, unique, (Testing ? "would have been " : ""), vnodeEssence->parent, dir->vnodeNumber);
3061 vnodeEssence->parent = dir->vnodeNumber;
3062 vnodeEssence->changed = 1;
3064 /* Vnode was claimed by another directory */
3067 Log("dir vnode %u: %s/%s parent vnode is %u (vnode %u, unique %u) -- %sdeleted\n", dir->vnodeNumber, (dir->name ? dir->name : "??"), name, vnodeEssence->parent, vnodeNumber, unique, (Testing ? "would have been " : ""));
3068 } else if (vnodeNumber == 1) {
3069 Log("dir vnode %d: %s/%s is invalid (vnode %d, unique %d) -- %sdeleted\n", dir->vnodeNumber, (dir->name ? dir->name : "??"), name, vnodeNumber, unique, (Testing?"would have been ":""));
3071 Log("dir vnode %u: %s/%s already claimed by directory vnode %u (vnode %u, unique %u) -- %sdeleted\n", dir->vnodeNumber, (dir->name ? dir->name : "??"), name, vnodeEssence->parent, vnodeNumber, unique, (Testing ? "would have been " : ""));
3076 assert(Delete(&dir->dirHandle, name) == 0);
3081 /* This directory claims the vnode */
3082 vnodeEssence->claimed = 1;
3084 vnodeEssence->count--;
3088 DistilVnodeEssence(VolumeId rwVId, VnodeClass class, Inode ino, Unique * maxu)
3090 register struct VnodeInfo *vip = &vnodeInfo[class];
3091 struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
3092 char buf[SIZEOF_LARGEDISKVNODE];
3093 struct VnodeDiskObject *vnode = (struct VnodeDiskObject *)buf;
3095 StreamHandle_t *file;
3100 IH_INIT(vip->handle, fileSysDevice, rwVId, ino);
3101 fdP = IH_OPEN(vip->handle);
3102 assert(fdP != NULL);
3103 file = FDH_FDOPEN(fdP, "r+");
3104 assert(file != NULL);
3105 size = OS_SIZE(fdP->fd_fd);
3107 vip->nVnodes = (size / vcp->diskSize) - 1;
3108 if (vip->nVnodes > 0) {
3109 assert((vip->nVnodes + 1) * vcp->diskSize == size);
3110 assert(STREAM_SEEK(file, vcp->diskSize, 0) == 0);
3111 assert((vip->vnodes = (struct VnodeEssence *)
3112 calloc(vip->nVnodes, sizeof(struct VnodeEssence))) != NULL);
3113 if (class == vLarge) {
3114 assert((vip->inodes = (Inode *)
3115 calloc(vip->nVnodes, sizeof(Inode))) != NULL);
3124 vip->volumeBlockCount = vip->nAllocatedVnodes = 0;
3125 for (vnodeIndex = 0, nVnodes = vip->nVnodes;
3126 nVnodes && STREAM_READ(vnode, vcp->diskSize, 1, file) == 1;
3127 nVnodes--, vnodeIndex++) {
3128 if (vnode->type != vNull) {
3129 register struct VnodeEssence *vep = &vip->vnodes[vnodeIndex];
3130 afs_fsize_t vnodeLength;
3131 vip->nAllocatedVnodes++;
3132 vep->count = vnode->linkCount;
3133 VNDISK_GET_LEN(vnodeLength, vnode);
3134 vep->blockCount = nBlocks(vnodeLength);
3135 vip->volumeBlockCount += vep->blockCount;
3136 vep->parent = vnode->parent;
3137 vep->unique = vnode->uniquifier;
3138 if (*maxu < vnode->uniquifier)
3139 *maxu = vnode->uniquifier;
3140 vep->modeBits = vnode->modeBits;
3141 vep->InodeNumber = VNDISK_GET_INO(vnode);
3142 vep->type = vnode->type;
3143 vep->author = vnode->author;
3144 vep->owner = vnode->owner;
3145 vep->group = vnode->group;
3146 if (vnode->type == vDirectory) {
3147 assert(class == vLarge);
3148 vip->inodes[vnodeIndex] = VNDISK_GET_INO(vnode);
3157 GetDirName(VnodeId vnode, struct VnodeEssence *vp, char *path)
3159 struct VnodeEssence *parentvp;
3165 if (vp->parent && vp->name && (parentvp = CheckVnodeNumber(vp->parent))
3166 && GetDirName(vp->parent, parentvp, path)) {
3168 strcat(path, vp->name);
3174 /* To determine if a vnode is orhpaned or not, the vnode and all its parent
3175 * vnodes must be "claimed". The vep->claimed flag is set in JudgeEntry().
3178 IsVnodeOrphaned(VnodeId vnode)
3180 struct VnodeEssence *vep;
3183 return (1); /* Vnode zero does not exist */
3185 return (0); /* The root dir vnode is always claimed */
3186 vep = CheckVnodeNumber(vnode); /* Get the vnode essence */
3187 if (!vep || !vep->claimed)
3188 return (1); /* Vnode is not claimed - it is orphaned */
3190 return (IsVnodeOrphaned(vep->parent));
3194 SalvageDir(char *name, VolumeId rwVid, struct VnodeInfo *dirVnodeInfo,
3195 IHandle_t * alinkH, int i, struct DirSummary *rootdir,
3198 static struct DirSummary dir;
3199 static struct DirHandle dirHandle;
3200 struct VnodeEssence *parent;
3201 static char path[MAXPATHLEN];
3204 if (dirVnodeInfo->vnodes[i].salvaged)
3205 return; /* already salvaged */
3208 dirVnodeInfo->vnodes[i].salvaged = 1;
3210 if (dirVnodeInfo->inodes[i] == 0)
3211 return; /* Not allocated to a directory */
3213 if (bitNumberToVnodeNumber(i, vLarge) == 1) {
3214 if (dirVnodeInfo->vnodes[i].parent) {
3215 Log("Bad parent, vnode 1; %s...\n",
3216 (Testing ? "skipping" : "salvaging"));
3217 dirVnodeInfo->vnodes[i].parent = 0;
3218 dirVnodeInfo->vnodes[i].changed = 1;
3221 parent = CheckVnodeNumber(dirVnodeInfo->vnodes[i].parent);
3222 if (parent && parent->salvaged == 0)
3223 SalvageDir(name, rwVid, dirVnodeInfo, alinkH,
3224 vnodeIdToBitNumber(dirVnodeInfo->vnodes[i].parent),
3225 rootdir, rootdirfound);
3228 dir.vnodeNumber = bitNumberToVnodeNumber(i, vLarge);
3229 dir.unique = dirVnodeInfo->vnodes[i].unique;
3232 dir.parent = dirVnodeInfo->vnodes[i].parent;
3233 dir.haveDot = dir.haveDotDot = 0;
3234 dir.ds_linkH = alinkH;
3235 SetSalvageDirHandle(&dir.dirHandle, dir.rwVid, fileSysDevice,
3236 dirVnodeInfo->inodes[i]);
3238 dirok = ((RebuildDirs && !Testing) ? 0 : DirOK(&dir.dirHandle));
3241 Log("Directory bad, vnode %u; %s...\n", dir.vnodeNumber,
3242 (Testing ? "skipping" : "salvaging"));
3245 CopyAndSalvage(&dir);
3249 dirHandle = dir.dirHandle;
3252 GetDirName(bitNumberToVnodeNumber(i, vLarge),
3253 &dirVnodeInfo->vnodes[i], path);
3256 /* If enumeration failed for random reasons, we will probably delete
3257 * too much stuff, so we guard against this instead.
3259 assert(EnumerateDir(&dirHandle, JudgeEntry, &dir) == 0);
3262 /* Delete the old directory if it was copied in order to salvage.
3263 * CopyOnWrite has written the new inode # to the disk, but we still
3264 * have the old one in our local structure here. Thus, we idec the
3268 if (dir.copied && !Testing) {
3269 code = IH_DEC(dir.ds_linkH, dirHandle.dirh_handle->ih_ino, rwVid);
3271 dirVnodeInfo->inodes[i] = dir.dirHandle.dirh_inode;
3274 /* Remember rootdir DirSummary _after_ it has been judged */
3275 if (dir.vnodeNumber == 1 && dir.unique == 1) {
3276 memcpy(rootdir, &dir, sizeof(struct DirSummary));
3284 SalvageVolume(register struct InodeSummary *rwIsp, IHandle_t * alinkH)
3286 /* This routine, for now, will only be called for read-write volumes */
3288 int BlocksInVolume = 0, FilesInVolume = 0;
3289 register VnodeClass class;
3290 struct DirSummary rootdir, oldrootdir;
3291 struct VnodeInfo *dirVnodeInfo;
3292 struct VnodeDiskObject vnode;
3293 VolumeDiskData volHeader;
3295 int orphaned, rootdirfound = 0;
3296 Unique maxunique = 0; /* the maxUniquifier from the vnodes */
3297 afs_int32 ofiles = 0, oblocks = 0; /* Number of orphaned files/blocks */
3298 struct VnodeEssence *vep;
3303 VnodeId LFVnode, ThisVnode;
3304 Unique LFUnique, ThisUnique;
3307 vid = rwIsp->volSummary->header.id;
3308 IH_INIT(h, fileSysDevice, vid, rwIsp->volSummary->header.volumeInfo);
3309 nBytes = IH_IREAD(h, 0, (char *)&volHeader, sizeof(volHeader));
3310 assert(nBytes == sizeof(volHeader));
3311 assert(volHeader.stamp.magic == VOLUMEINFOMAGIC);
3312 assert(volHeader.destroyMe != DESTROY_ME);
3313 /* (should not have gotten this far with DESTROY_ME flag still set!) */
3315 DistilVnodeEssence(vid, vLarge, rwIsp->volSummary->header.largeVnodeIndex,
3317 DistilVnodeEssence(vid, vSmall, rwIsp->volSummary->header.smallVnodeIndex,
3320 dirVnodeInfo = &vnodeInfo[vLarge];
3321 for (i = 0; i < dirVnodeInfo->nVnodes; i++) {
3322 SalvageDir(volHeader.name, vid, dirVnodeInfo, alinkH, i, &rootdir,
3330 /* Parse each vnode looking for orphaned vnodes and
3331 * connect them to the tree as orphaned (if requested).
3333 oldrootdir = rootdir;
3334 for (class = 0; class < nVNODECLASSES; class++) {
3335 for (v = 0; v < vnodeInfo[class].nVnodes; v++) {
3336 vep = &(vnodeInfo[class].vnodes[v]);
3337 ThisVnode = bitNumberToVnodeNumber(v, class);
3338 ThisUnique = vep->unique;
3340 if ((vep->type == 0) || vep->claimed || ThisVnode == 1)
3341 continue; /* Ignore unused, claimed, and root vnodes */
3343 /* This vnode is orphaned. If it is a directory vnode, then the '..'
3344 * entry in this vnode had incremented the parent link count (In
3345 * JudgeEntry()). We need to go to the parent and decrement that
3346 * link count. But if the parent's unique is zero, then the parent
3347 * link count was not incremented in JudgeEntry().
3349 if (class == vLarge) { /* directory vnode */
3350 pv = vnodeIdToBitNumber(vep->parent);
3351 if (vnodeInfo[vLarge].vnodes[pv].unique != 0)
3352 vnodeInfo[vLarge].vnodes[pv].count++;
3356 continue; /* If no rootdir, can't attach orphaned files */
3358 /* Here we attach orphaned files and directories into the
3359 * root directory, LVVnode, making sure link counts stay correct.
3361 if ((orphans == ORPH_ATTACH) && !vep->todelete && !Testing) {
3362 LFVnode = rootdir.vnodeNumber; /* Lost+Found vnode number */
3363 LFUnique = rootdir.unique; /* Lost+Found uniquifier */
3365 /* Update this orphaned vnode's info. Its parent info and
3366 * link count (do for orphaned directories and files).
3368 vep->parent = LFVnode; /* Parent is the root dir */
3369 vep->unique = LFUnique;
3372 vep->count--; /* Inc link count (root dir will pt to it) */
3374 /* If this orphaned vnode is a directory, change '..'.
3375 * The name of the orphaned dir/file is unknown, so we
3376 * build a unique name. No need to CopyOnWrite the directory
3377 * since it is not connected to tree in BK or RO volume and
3378 * won't be visible there.
3380 if (class == vLarge) {
3384 /* Remove and recreate the ".." entry in this orphaned directory */
3385 SetSalvageDirHandle(&dh, vid, fileSysDevice,
3386 vnodeInfo[class].inodes[v]);
3388 pa.Unique = LFUnique;
3389 assert(Delete(&dh, "..") == 0);
3390 assert(Create(&dh, "..", &pa) == 0);
3392 /* The original parent's link count was decremented above.
3393 * Here we increment the new parent's link count.
3395 pv = vnodeIdToBitNumber(LFVnode);
3396 vnodeInfo[vLarge].vnodes[pv].count--;
3400 /* Go to the root dir and add this entry. The link count of the
3401 * root dir was incremented when ".." was created. Try 10 times.
3403 for (j = 0; j < 10; j++) {
3404 pa.Vnode = ThisVnode;
3405 pa.Unique = ThisUnique;
3407 (void)afs_snprintf(npath, sizeof npath, "%s.%u.%u",
3409 vLarge) ? "__ORPHANDIR__" :
3410 "__ORPHANFILE__"), ThisVnode,
3413 CopyOnWrite(&rootdir);
3414 code = Create(&rootdir.dirHandle, npath, &pa);
3418 ThisUnique += 50; /* Try creating a different file */
3421 Log("Attaching orphaned %s to volume's root dir as %s\n",
3422 ((class == vLarge) ? "directory" : "file"), npath);
3424 } /* for each vnode in the class */
3425 } /* for each class of vnode */
3427 /* Delete the old rootinode directory if the rootdir was CopyOnWrite */
3429 if (!oldrootdir.copied && rootdir.copied) {
3431 IH_DEC(oldrootdir.ds_linkH, oldrootdir.dirHandle.dirh_inode,
3434 /* dirVnodeInfo->inodes[?] is not updated with new inode number */
3437 DFlush(); /* Flush the changes */
3438 if (!rootdirfound && (orphans == ORPH_ATTACH)) {
3439 Log("Cannot attach orphaned files and directories: Root directory not found\n");
3440 orphans = ORPH_IGNORE;
3443 /* Write out all changed vnodes. Orphaned files and directories
3444 * will get removed here also (if requested).
3446 for (class = 0; class < nVNODECLASSES; class++) {
3447 int nVnodes = vnodeInfo[class].nVnodes;
3448 struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
3449 struct VnodeEssence *vnodes = vnodeInfo[class].vnodes;
3450 FilesInVolume += vnodeInfo[class].nAllocatedVnodes;
3451 BlocksInVolume += vnodeInfo[class].volumeBlockCount;
3452 for (i = 0; i < nVnodes; i++) {
3453 register struct VnodeEssence *vnp = &vnodes[i];
3454 VnodeId vnodeNumber = bitNumberToVnodeNumber(i, class);
3456 /* If the vnode is good but is unclaimed (not listed in
3457 * any directory entries), then it is orphaned.
3460 if ((vnp->type != 0) && (orphaned = IsVnodeOrphaned(vnodeNumber))) {
3461 vnp->claimed = 0; /* Makes IsVnodeOrphaned calls faster */
3465 if (vnp->changed || vnp->count) {
3469 IH_IREAD(vnodeInfo[class].handle,
3470 vnodeIndexOffset(vcp, vnodeNumber),
3471 (char *)&vnode, sizeof(vnode));
3472 assert(nBytes == sizeof(vnode));
3474 vnode.parent = vnp->parent;
3475 oldCount = vnode.linkCount;
3476 vnode.linkCount = vnode.linkCount - vnp->count;
3479 orphaned = IsVnodeOrphaned(vnodeNumber);
3481 if (!vnp->todelete) {
3482 /* Orphans should have already been attached (if requested) */
3483 assert(orphans != ORPH_ATTACH);
3484 oblocks += vnp->blockCount;
3487 if (((orphans == ORPH_REMOVE) || vnp->todelete)
3489 BlocksInVolume -= vnp->blockCount;
3491 if (VNDISK_GET_INO(&vnode)) {
3493 IH_DEC(alinkH, VNDISK_GET_INO(&vnode), vid);
3496 memset(&vnode, 0, sizeof(vnode));
3498 } else if (vnp->count) {
3500 Log("Vnode %u: link count incorrect (was %d, %s %d)\n", vnodeNumber, oldCount, (Testing ? "would have changed to" : "now"), vnode.linkCount);
3504 vnode.dataVersion++;
3507 IH_IWRITE(vnodeInfo[class].handle,
3508 vnodeIndexOffset(vcp, vnodeNumber),
3509 (char *)&vnode, sizeof(vnode));
3510 assert(nBytes == sizeof(vnode));
3516 if (!Showmode && ofiles) {
3517 Log("%s %d orphaned files and directories (approx. %u KB)\n",
3519 && (orphans == ORPH_REMOVE)) ? "Removed" : "Found", ofiles,
3523 for (class = 0; class < nVNODECLASSES; class++) {
3524 register struct VnodeInfo *vip = &vnodeInfo[class];
3525 for (i = 0; i < vip->nVnodes; i++)
3526 if (vip->vnodes[i].name)
3527 free(vip->vnodes[i].name);
3534 /* Set correct resource utilization statistics */
3535 volHeader.filecount = FilesInVolume;
3536 volHeader.diskused = BlocksInVolume;
3538 /* Make sure the uniquifer is big enough: maxunique is the real maxUniquifier */
3539 if (volHeader.uniquifier < (maxunique + 1)) {
3541 Log("Volume uniquifier is too low; fixed\n");
3542 /* Plus 2,000 in case there are workstations out there with
3543 * cached vnodes that have since been deleted
3545 volHeader.uniquifier = (maxunique + 1 + 2000);
3548 /* Turn off the inUse bit; the volume's been salvaged! */
3549 volHeader.inUse = 0; /* clear flag indicating inUse@last crash */
3550 volHeader.needsSalvaged = 0; /* clear 'damaged' flag */
3551 volHeader.inService = 1; /* allow service again */
3552 volHeader.needsCallback = (VolumeChanged != 0);
3553 volHeader.dontSalvage = DONT_SALVAGE;
3556 nBytes = IH_IWRITE(h, 0, (char *)&volHeader, sizeof(volHeader));
3557 assert(nBytes == sizeof(volHeader));
3560 Log("%sSalvaged %s (%u): %d files, %d blocks\n",
3561 (Testing ? "It would have " : ""), volHeader.name, volHeader.id,
3562 FilesInVolume, BlocksInVolume);
3564 IH_RELEASE(vnodeInfo[vSmall].handle);
3565 IH_RELEASE(vnodeInfo[vLarge].handle);
3571 ClearROInUseBit(struct VolumeSummary *summary)
3573 IHandle_t *h = summary->volumeInfoHandle;
3576 VolumeDiskData volHeader;
3578 nBytes = IH_IREAD(h, 0, (char *)&volHeader, sizeof(volHeader));
3579 assert(nBytes == sizeof(volHeader));
3580 assert(volHeader.stamp.magic == VOLUMEINFOMAGIC);
3581 volHeader.inUse = 0;
3582 volHeader.needsSalvaged = 0;
3583 volHeader.inService = 1;
3584 volHeader.dontSalvage = DONT_SALVAGE;
3586 nBytes = IH_IREAD(h, 0, (char *)&volHeader, sizeof(volHeader));
3587 assert(nBytes == sizeof(volHeader));
3592 * Possible delete the volume.
3594 * deleteMe - Always do so, only a partial volume.
3597 MaybeZapVolume(register struct InodeSummary *isp, char *message, int deleteMe,
3600 if (readOnly(isp) || deleteMe) {
3601 if (isp->volSummary && isp->volSummary->fileName) {
3604 Log("Volume %u (is only a partial volume--probably an attempt was made to move/restore it when a machine crash occured.\n", isp->volumeId);
3606 Log("It will be deleted on this server (you may find it elsewhere)\n");
3609 Log("Volume %u needs to be salvaged. Since it is read-only, however,\n", isp->volumeId);
3611 Log("it will be deleted instead. It should be recloned.\n");
3614 unlink(isp->volSummary->fileName);
3616 } else if (!check) {
3617 Log("%s salvage was unsuccessful: read-write volume %u\n", message,
3619 Abort("Salvage of volume %u aborted\n", isp->volumeId);
3625 AskOffline(VolumeId volumeId)
3627 if (FSYNC_askfs(volumeId, NULL, FSYNC_OFF, FSYNC_SALVAGE) == FSYNC_DENIED) {
3628 Log("AskOffline: file server denied offline request; a general salvage is required.\n");
3629 Abort("Salvage aborted\n");
3634 AskOnline(VolumeId volumeId, char *partition)
3636 if (FSYNC_askfs(volumeId, partition, FSYNC_ON, 0) == FSYNC_DENIED) {
3637 Log("AskOnline: file server denied online request to volume %u partition %s\n", volumeId, partition);
3642 CopyInode(Device device, Inode inode1, Inode inode2, int rwvolume)
3644 /* Volume parameter is passed in case iopen is upgraded in future to
3645 * require a volume Id to be passed
3648 IHandle_t *srcH, *destH;
3649 FdHandle_t *srcFdP, *destFdP;
3652 IH_INIT(srcH, device, rwvolume, inode1);
3653 srcFdP = IH_OPEN(srcH);
3654 assert(srcFdP != NULL);
3655 IH_INIT(destH, device, rwvolume, inode2);
3656 destFdP = IH_OPEN(destH);
3658 while ((n = FDH_READ(srcFdP, buf, sizeof(buf))) > 0)
3659 assert(FDH_WRITE(destFdP, buf, n) == n);
3661 FDH_REALLYCLOSE(srcFdP);
3662 FDH_REALLYCLOSE(destFdP);
3669 PrintInodeList(void)
3671 register struct ViceInodeInfo *ip;
3672 struct ViceInodeInfo *buf;
3673 struct afs_stat status;
3676 assert(afs_fstat(inodeFd, &status) == 0);
3677 buf = (struct ViceInodeInfo *)malloc(status.st_size);
3678 assert(buf != NULL);
3679 nInodes = status.st_size / sizeof(struct ViceInodeInfo);
3680 assert(read(inodeFd, buf, status.st_size) == status.st_size);
3681 for (ip = buf; nInodes--; ip++) {
3682 Log("Inode:%s, linkCount=%d, size=%#llx, p=(%u,%u,%u,%u)\n",
3683 PrintInode(NULL, ip->inodeNumber), ip->linkCount,
3684 (afs_uintmax_t) ip->byteCount, ip->u.param[0], ip->u.param[1],
3685 ip->u.param[2], ip->u.param[3]);
3691 PrintInodeSummary(void)
3694 struct InodeSummary *isp;
3696 for (i = 0; i < nVolumesInInodeFile; i++) {
3697 isp = &inodeSummary[i];
3698 Log("VID:%u, RW:%u, index:%d, nInodes:%d, nSpecialInodes:%d, maxUniquifier:%u, volSummary\n", isp->volumeId, isp->RWvolumeId, isp->index, isp->nInodes, isp->nSpecialInodes, isp->maxUniquifier);
3703 PrintVolumeSummary(void)
3706 struct VolumeSummary *vsp;
3708 for (i = 0, vsp = volumeSummaryp; i < nVolumes; vsp++, i++) {
3709 Log("fileName:%s, header, wouldNeedCallback\n", vsp->fileName);
3719 assert(0); /* Fork is never executed in the NT code path */
3734 if (main_thread != pthread_self())
3735 pthread_exit((void *)code);
3748 pid = wait(&status);
3750 if (WCOREDUMP(status))
3751 Log("\"%s\" core dumped!\n", prog);
3752 if (WIFSIGNALED(status) != 0 || WEXITSTATUS(status) != 0)
3758 TimeStamp(time_t clock, int precision)
3761 static char timestamp[20];
3762 lt = localtime(&clock);
3764 (void)strftime(timestamp, 20, "%m/%d/%Y %T", lt);
3766 (void)strftime(timestamp, 20, "%m/%d/%Y %H:%M", lt);
3773 char oldSlvgLog[AFSDIR_PATH_MAX];
3775 #ifndef AFS_NT40_ENV
3782 strcpy(oldSlvgLog, AFSDIR_SERVER_SLVGLOG_FILEPATH);
3783 strcat(oldSlvgLog, ".old");
3785 renamefile(AFSDIR_SERVER_SLVGLOG_FILEPATH, oldSlvgLog);
3786 logFile = afs_fopen(AFSDIR_SERVER_SLVGLOG_FILEPATH, "a");
3788 if (!logFile) { /* still nothing, use stdout */
3792 #ifndef AFS_NAMEI_ENV
3793 AFS_DEBUG_IOPS_LOG(logFile);
3798 #ifndef AFS_NT40_ENV
3800 TimeStampLogFile(void)
3802 char stampSlvgLog[AFSDIR_PATH_MAX];
3807 lt = localtime(&now);
3808 (void)afs_snprintf(stampSlvgLog, sizeof stampSlvgLog,
3809 "%s.%04d-%02d-%02d.%02d:%02d:%02d",
3810 AFSDIR_SERVER_SLVGLOG_FILEPATH, lt->tm_year + 1900,
3811 lt->tm_mon + 1, lt->tm_mday, lt->tm_hour, lt->tm_min,
3814 /* try to link the logfile to a timestamped filename */
3815 /* if it fails, oh well, nothing we can do */
3816 link(AFSDIR_SERVER_SLVGLOG_FILEPATH, stampSlvgLog);
3825 #ifndef AFS_NT40_ENV
3827 printf("Can't show log since using syslog.\n");
3836 logFile = afs_fopen(AFSDIR_SERVER_SLVGLOG_FILEPATH, "r");
3839 printf("Can't read %s, exiting\n", AFSDIR_SERVER_SLVGLOG_FILEPATH);
3842 while (fgets(line, sizeof(line), logFile))
3849 Log(const char *format, ...)
3855 va_start(args, format);
3856 (void)afs_vsnprintf(tmp, sizeof tmp, format, args);
3858 #ifndef AFS_NT40_ENV
3860 syslog(LOG_INFO, "%s", tmp);
3864 gettimeofday(&now, 0);
3865 fprintf(logFile, "%s %s", TimeStamp(now.tv_sec, 1), tmp);
3871 Abort(const char *format, ...)
3876 va_start(args, format);
3877 (void)afs_vsnprintf(tmp, sizeof tmp, format, args);
3879 #ifndef AFS_NT40_ENV
3881 syslog(LOG_INFO, "%s", tmp);
3885 fprintf(logFile, "%s", tmp);
3900 p = (char *)malloc(strlen(s) + 1);
3907 /* Remove the FORCESALVAGE file */
3909 RemoveTheForce(char *path)
3911 if (!Testing && ForceSalvage) {
3912 if (chdir(path) == 0)
3913 unlink("FORCESALVAGE");
3917 #ifndef AFS_AIX32_ENV
3919 * UseTheForceLuke - see if we can use the force
3922 UseTheForceLuke(char *path)
3924 struct afs_stat force;
3926 assert(chdir(path) != -1);
3928 return (afs_stat("FORCESALVAGE", &force) == 0);
3932 * UseTheForceLuke - see if we can use the force
3935 * The VRMIX fsck will not muck with the filesystem it is supposedly
3936 * fixing and create a "FORCESAVAGE" file (by design). Instead, we
3937 * muck directly with the root inode, which is within the normal
3939 * ListViceInodes() has a side effect of setting ForceSalvage if
3940 * it detects a need, based on root inode examination.
3943 UseTheForceLuke(char *path)
3946 return 0; /* sorry OB1 */
3951 /* NT support routines */
3953 static char execpathname[MAX_PATH];
3955 nt_SalvagePartition(char *partName, int jobn)
3960 if (!*execpathname) {
3961 n = GetModuleFileName(NULL, execpathname, MAX_PATH - 1);
3962 if (!n || n == 1023)
3965 job.cj_magic = SALVAGER_MAGIC;
3966 job.cj_number = jobn;
3967 (void)strcpy(job.cj_part, partName);
3968 pid = (int)spawnprocveb(execpathname, save_args, NULL, &job, sizeof(job));
3973 nt_SetupPartitionSalvage(void *datap, int len)
3975 childJob_t *jobp = (childJob_t *) datap;
3976 char logname[AFSDIR_PATH_MAX];
3978 if (len != sizeof(childJob_t))
3980 if (jobp->cj_magic != SALVAGER_MAGIC)
3985 (void)sprintf(logname, "%s.%d", AFSDIR_SERVER_SLVGLOG_FILEPATH,
3987 logFile = afs_fopen(logname, "w");
3995 #endif /* AFS_NT40_ENV */