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, void *rock)
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, NULL)) < 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 unsigned long st_size=(unsigned long) status.st_size;
1593 nInodes = st_size / sizeof(struct ViceInodeInfo);
1595 fclose(summaryFile);
1597 unlink(summaryFileName);
1598 if (!singleVolumeNumber) /* Remove the FORCESALVAGE file */
1599 RemoveTheForce(fileSysPath);
1601 struct VolumeSummary *vsp;
1604 GetVolumeSummary(singleVolumeNumber);
1606 for (i = 0, vsp = volumeSummaryp; i < nVolumes; i++) {
1608 DeleteExtraVolumeHeaderFile(vsp);
1611 Log("%s vice inodes on %s; not salvaged\n",
1612 singleVolumeNumber ? "No applicable" : "No", dev);
1615 ip = (struct ViceInodeInfo *)malloc(nInodes*sizeof(struct ViceInodeInfo));
1617 fclose(summaryFile);
1620 unlink(summaryFileName);
1622 ("Unable to allocate enough space to read inode table; %s not salvaged\n",
1625 if (read(inodeFd, ip, st_size) != st_size) {
1626 fclose(summaryFile);
1629 unlink(summaryFileName);
1630 Abort("Unable to read inode table; %s not salvaged\n", dev);
1632 qsort(ip, nInodes, sizeof(struct ViceInodeInfo), CompareInodes);
1633 if (afs_lseek(inodeFd, 0, SEEK_SET) == -1
1634 || write(inodeFd, ip, st_size) != st_size) {
1635 fclose(summaryFile);
1638 unlink(summaryFileName);
1639 Abort("Unable to rewrite inode table; %s not salvaged\n", dev);
1643 CountVolumeInodes(ip, nInodes, &summary);
1644 if (fwrite(&summary, sizeof(summary), 1, summaryFile) != 1) {
1645 Log("Difficulty writing summary file (errno = %d); %s not salvaged\n", errno, dev);
1646 fclose(summaryFile);
1650 summary.index += (summary.nInodes);
1651 nInodes -= summary.nInodes;
1652 ip += summary.nInodes;
1654 /* Following fflush is not fclose, because if it was debug mode would not work */
1655 if (fflush(summaryFile) == EOF || fsync(fileno(summaryFile)) == -1) {
1656 Log("Unable to write summary file (errno = %d); %s not salvaged\n", errno, dev);
1657 fclose(summaryFile);
1661 if (canfork && !debug) {
1666 if (Wait("Inode summary") == -1) {
1667 fclose(summaryFile);
1670 unlink(summaryFileName);
1671 Exit(1); /* salvage of this partition aborted */
1674 assert(afs_fstat(fileno(summaryFile), &status) != -1);
1675 if (status.st_size != 0) {
1677 unsigned long st_status=(unsigned long)status.st_size;
1678 inodeSummary = (struct InodeSummary *)malloc(st_status);
1679 assert(inodeSummary != NULL);
1680 /* For GNU we need to do lseek to get the file pointer moved. */
1681 assert(afs_lseek(fileno(summaryFile), 0, SEEK_SET) == 0);
1682 ret = read(fileno(summaryFile), inodeSummary, st_status);
1683 assert(ret == st_status);
1685 nVolumesInInodeFile =(unsigned long)(status.st_size) / sizeof(struct InodeSummary);
1686 Log("%d nVolumesInInodeFile %d \n",nVolumesInInodeFile,(unsigned long)(status.st_size));
1687 fclose(summaryFile);
1689 unlink(summaryFileName);
1693 /* Comparison routine for volume sort.
1694 This is setup so that a read-write volume comes immediately before
1695 any read-only clones of that volume */
1697 CompareVolumes(const void *_p1, const void *_p2)
1699 register const struct VolumeSummary *p1 = _p1;
1700 register const struct VolumeSummary *p2 = _p2;
1701 if (p1->header.parent != p2->header.parent)
1702 return p1->header.parent < p2->header.parent ? -1 : 1;
1703 if (p1->header.id == p1->header.parent) /* p1 is rw volume */
1705 if (p2->header.id == p2->header.parent) /* p2 is rw volume */
1707 return p1->header.id < p2->header.id ? -1 : 1; /* Both read-only */
1711 GetVolumeSummary(VolumeId singleVolumeNumber)
1714 afs_int32 nvols = 0;
1715 struct VolumeSummary *vsp, vs;
1716 struct VolumeDiskHeader diskHeader;
1719 /* Get headers from volume directory */
1720 if (chdir(fileSysPath) == -1 || (dirp = opendir(".")) == NULL)
1721 Abort("Can't read directory %s; not salvaged\n", fileSysPath);
1722 if (!singleVolumeNumber) {
1723 while ((dp = readdir(dirp))) {
1724 char *p = dp->d_name;
1725 p = strrchr(dp->d_name, '.');
1726 if (p != NULL && strcmp(p, VHDREXT) == 0) {
1728 if ((fd = afs_open(dp->d_name, O_RDONLY)) != -1
1729 && read(fd, (char *)&diskHeader, sizeof(diskHeader))
1730 == sizeof(diskHeader)
1731 && diskHeader.stamp.magic == VOLUMEHEADERMAGIC) {
1732 DiskToVolumeHeader(&vs.header, &diskHeader);
1740 dirp = opendir("."); /* No rewinddir for NT */
1747 (struct VolumeSummary *)malloc(nvols *
1748 sizeof(struct VolumeSummary));
1751 (struct VolumeSummary *)malloc(20 * sizeof(struct VolumeSummary));
1752 assert(volumeSummaryp != NULL);
1755 vsp = volumeSummaryp;
1756 while ((dp = readdir(dirp))) {
1757 char *p = dp->d_name;
1758 p = strrchr(dp->d_name, '.');
1759 if (p != NULL && strcmp(p, VHDREXT) == 0) {
1762 if ((fd = afs_open(dp->d_name, O_RDONLY)) == -1
1763 || read(fd, &diskHeader, sizeof(diskHeader))
1764 != sizeof(diskHeader)
1765 || diskHeader.stamp.magic != VOLUMEHEADERMAGIC) {
1770 if (!singleVolumeNumber) {
1772 Log("%s/%s is not a legitimate volume header file; %sdeleted\n", fileSysPathName, dp->d_name, (Testing ? "it would have been " : ""));
1777 char nameShouldBe[64];
1778 DiskToVolumeHeader(&vsp->header, &diskHeader);
1779 if (singleVolumeNumber && vsp->header.id == singleVolumeNumber
1780 && vsp->header.parent != singleVolumeNumber) {
1781 Log("%u is a read-only volume; not salvaged\n",
1782 singleVolumeNumber);
1785 if (!singleVolumeNumber
1786 || (vsp->header.id == singleVolumeNumber
1787 || vsp->header.parent == singleVolumeNumber)) {
1788 (void)afs_snprintf(nameShouldBe, sizeof nameShouldBe,
1789 VFORMAT, vsp->header.id);
1790 if (singleVolumeNumber)
1791 AskOffline(vsp->header.id);
1792 if (strcmp(nameShouldBe, dp->d_name)) {
1794 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 " : ""));
1798 vsp->fileName = ToString(dp->d_name);
1808 qsort(volumeSummaryp, nVolumes, sizeof(struct VolumeSummary),
1812 /* Find the link table. This should be associated with the RW volume or, if
1813 * a RO only site, then the RO volume. For now, be cautious and hunt carefully.
1816 FindLinkHandle(register struct InodeSummary *isp, int nVols,
1817 struct ViceInodeInfo *allInodes)
1820 struct ViceInodeInfo *ip;
1822 for (i = 0; i < nVols; i++) {
1823 ip = allInodes + isp[i].index;
1824 for (j = 0; j < isp[i].nSpecialInodes; j++) {
1825 if (ip[j].u.special.type == VI_LINKTABLE)
1826 return ip[j].inodeNumber;
1833 CreateLinkTable(register struct InodeSummary *isp, Inode ino)
1835 struct versionStamp version;
1838 if (!VALID_INO(ino))
1840 IH_CREATE(NULL, fileSysDevice, fileSysPath, 0, isp->volumeId,
1841 INODESPECIAL, VI_LINKTABLE, isp->RWvolumeId);
1842 if (!VALID_INO(ino))
1844 ("Unable to allocate link table inode for volume %u (error = %d)\n",
1845 isp->RWvolumeId, errno);
1846 IH_INIT(VGLinkH, fileSysDevice, isp->RWvolumeId, ino);
1847 fdP = IH_OPEN(VGLinkH);
1849 Abort("Can't open link table for volume %u (error = %d)\n",
1850 isp->RWvolumeId, errno);
1852 if (FDH_TRUNC(fdP, 0) < 0)
1853 Abort("Can't truncate link table for volume %u (error = %d)\n",
1854 isp->RWvolumeId, errno);
1856 version.magic = LINKTABLEMAGIC;
1857 version.version = LINKTABLEVERSION;
1859 if (FDH_WRITE(fdP, (char *)&version, sizeof(version))
1861 Abort("Can't truncate link table for volume %u (error = %d)\n",
1862 isp->RWvolumeId, errno);
1864 FDH_REALLYCLOSE(fdP);
1866 /* If the volume summary exits (i.e., the V*.vol header file exists),
1867 * then set this inode there as well.
1869 if (isp->volSummary)
1870 isp->volSummary->header.linkTable = ino;
1879 SVGParms_t *parms = (SVGParms_t *) arg;
1880 DoSalvageVolumeGroup(parms->svgp_inodeSummaryp, parms->svgp_count);
1885 SalvageVolumeGroup(register struct InodeSummary *isp, int nVols)
1888 pthread_attr_t tattr;
1892 /* Initialize per volume global variables, even if later code does so */
1896 memset(&VolInfo, 0, sizeof(VolInfo));
1898 parms.svgp_inodeSummaryp = isp;
1899 parms.svgp_count = nVols;
1900 code = pthread_attr_init(&tattr);
1902 Log("Failed to salvage volume group %u: pthread_attr_init()\n",
1906 code = pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_JOINABLE);
1908 Log("Failed to salvage volume group %u: pthread_attr_setdetachstate()\n", isp->RWvolumeId);
1911 code = pthread_create(&tid, &tattr, nt_SVG, &parms);
1913 Log("Failed to create thread to salvage volume group %u\n",
1917 (void)pthread_join(tid, NULL);
1919 #endif /* AFS_NT40_ENV */
1922 DoSalvageVolumeGroup(register struct InodeSummary *isp, int nVols)
1924 struct ViceInodeInfo *inodes, *allInodes, *ip;
1925 int i, totalInodes, size, salvageTo;
1929 int dec_VGLinkH = 0;
1931 FdHandle_t *fdP = NULL;
1934 haveRWvolume = (isp->volumeId == isp->RWvolumeId
1935 && isp->nSpecialInodes > 0);
1936 if ((!ShowMounts) || (ShowMounts && !haveRWvolume)) {
1937 if (!ForceSalvage && QuickCheck(isp, nVols))
1940 if (ShowMounts && !haveRWvolume)
1942 if (canfork && !debug && Fork() != 0) {
1943 (void)Wait("Salvage volume group");
1946 for (i = 0, totalInodes = 0; i < nVols; i++)
1947 totalInodes += isp[i].nInodes;
1948 size = totalInodes * sizeof(struct ViceInodeInfo);
1949 inodes = (struct ViceInodeInfo *)malloc(size);
1950 allInodes = inodes - isp->index; /* this would the base of all the inodes
1951 * for the partition, if all the inodes
1952 * had been read into memory */
1954 (inodeFd, isp->index * sizeof(struct ViceInodeInfo),
1956 assert(read(inodeFd, inodes, size) == size);
1958 /* Don't try to salvage a read write volume if there isn't one on this
1960 salvageTo = haveRWvolume ? 0 : 1;
1962 #ifdef AFS_NAMEI_ENV
1963 ino = FindLinkHandle(isp, nVols, allInodes);
1964 if (VALID_INO(ino)) {
1965 IH_INIT(VGLinkH, fileSysDevice, isp->RWvolumeId, ino);
1966 fdP = IH_OPEN(VGLinkH);
1968 if (!VALID_INO(ino) || fdP == NULL) {
1969 Log("%s link table for volume %u.\n",
1970 Testing ? "Would have recreated" : "Recreating", isp->RWvolumeId);
1972 IH_INIT(VGLinkH, fileSysDevice, -1, -1);
1974 CreateLinkTable(isp, ino);
1978 FDH_REALLYCLOSE(fdP);
1980 IH_INIT(VGLinkH, fileSysDevice, -1, -1);
1983 /* Salvage in reverse order--read/write volume last; this way any
1984 * Inodes not referenced by the time we salvage the read/write volume
1985 * can be picked up by the read/write volume */
1986 /* ACTUALLY, that's not done right now--the inodes just vanish */
1987 for (i = nVols - 1; i >= salvageTo; i--) {
1989 struct InodeSummary *lisp = &isp[i];
1990 #ifdef AFS_NAMEI_ENV
1991 /* If only the RO is present on this partition, the link table
1992 * shows up as a RW volume special file. Need to make sure the
1993 * salvager doesn't try to salvage the non-existent RW.
1995 if (rw && nVols > 1 && isp[i].nSpecialInodes == 1) {
1996 /* If this only special inode is the link table, continue */
1997 if (inodes->u.special.type == VI_LINKTABLE) {
2004 Log("%s VOLUME %u%s.\n", rw ? "SALVAGING" : "CHECKING CLONED",
2005 lisp->volumeId, (Testing ? "(READONLY mode)" : ""));
2006 /* Check inodes twice. The second time do things seriously. This
2007 * way the whole RO volume can be deleted, below, if anything goes wrong */
2008 for (check = 1; check >= 0; check--) {
2010 if (SalvageVolumeHeaderFile(lisp, allInodes, rw, check, &deleteMe)
2012 MaybeZapVolume(lisp, "Volume header", deleteMe, check);
2013 if (rw && deleteMe) {
2014 haveRWvolume = 0; /* This will cause its inodes to be deleted--since salvage
2015 * volume won't be called */
2021 if (rw && check == 1)
2023 if (SalvageVnodes(isp, lisp, allInodes, check) == -1) {
2024 MaybeZapVolume(lisp, "Vnode index", 0, check);
2030 /* Fix actual inode counts */
2032 Log("totalInodes %d\n",totalInodes);
2033 for (ip = inodes; totalInodes; ip++, totalInodes--) {
2034 static int TraceBadLinkCounts = 0;
2035 #ifdef AFS_NAMEI_ENV
2036 if (VGLinkH->ih_ino == ip->inodeNumber) {
2037 dec_VGLinkH = ip->linkCount - VGLinkH_cnt;
2038 VGLinkH_p1 = ip->u.param[0];
2039 continue; /* Deal with this last. */
2042 if (ip->linkCount != 0 && TraceBadLinkCounts) {
2043 TraceBadLinkCounts--; /* Limit reports, per volume */
2044 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]);
2046 while (ip->linkCount > 0) {
2047 /* below used to assert, not break */
2049 if (IH_DEC(VGLinkH, ip->inodeNumber, ip->u.param[0])) {
2050 Log("idec failed. inode %s errno %d\n",
2051 PrintInode(NULL, ip->inodeNumber), errno);
2057 while (ip->linkCount < 0) {
2058 /* these used to be asserts */
2060 if (IH_INC(VGLinkH, ip->inodeNumber, ip->u.param[0])) {
2061 Log("iinc failed. inode %s errno %d\n",
2062 PrintInode(NULL, ip->inodeNumber), errno);
2069 if (totalInodes % 10000 == 0)
2070 Log("%d inodes to process\n",totalInodes);
2072 #ifdef AFS_NAMEI_ENV
2073 while (dec_VGLinkH > 0) {
2074 if (IH_DEC(VGLinkH, VGLinkH->ih_ino, VGLinkH_p1) < 0) {
2075 Log("idec failed on link table, errno = %d\n", errno);
2079 while (dec_VGLinkH < 0) {
2080 if (IH_INC(VGLinkH, VGLinkH->ih_ino, VGLinkH_p1) < 0) {
2081 Log("iinc failed on link table, errno = %d\n", errno);
2088 /* Directory consistency checks on the rw volume */
2090 SalvageVolume(isp, VGLinkH);
2091 IH_RELEASE(VGLinkH);
2093 if (canfork && !debug) {
2100 QuickCheck(register struct InodeSummary *isp, int nVols)
2102 /* Check headers BEFORE forking */
2106 for (i = 0; i < nVols; i++) {
2107 struct VolumeSummary *vs = isp[i].volSummary;
2108 VolumeDiskData volHeader;
2110 /* Don't salvage just because phantom rw volume is there... */
2111 /* (If a read-only volume exists, read/write inodes must also exist) */
2112 if (i == 0 && isp->nSpecialInodes == 0 && nVols > 1)
2116 IH_INIT(h, fileSysDevice, vs->header.parent, vs->header.volumeInfo);
2117 if (IH_IREAD(h, 0, (char *)&volHeader, sizeof(volHeader))
2118 == sizeof(volHeader)
2119 && volHeader.stamp.magic == VOLUMEINFOMAGIC
2120 && volHeader.dontSalvage == DONT_SALVAGE
2121 && volHeader.needsSalvaged == 0 && volHeader.destroyMe == 0) {
2122 if (volHeader.inUse == 1) {
2123 volHeader.inUse = 0;
2124 volHeader.inService = 1;
2126 if (IH_IWRITE(h, 0, (char *)&volHeader, sizeof(volHeader))
2127 != sizeof(volHeader)) {
2143 /* SalvageVolumeHeaderFile
2145 * Salvage the top level V*.vol header file. Make sure the special files
2146 * exist and that there are no duplicates.
2148 * Calls SalvageHeader for each possible type of volume special file.
2152 SalvageVolumeHeaderFile(register struct InodeSummary *isp,
2153 register struct ViceInodeInfo *inodes, int RW,
2154 int check, int *deleteMe)
2158 register struct ViceInodeInfo *ip;
2159 int allinodesobsolete = 1;
2160 struct VolumeDiskHeader diskHeader;
2164 memset(&tempHeader, 0, sizeof(tempHeader));
2165 tempHeader.stamp.magic = VOLUMEHEADERMAGIC;
2166 tempHeader.stamp.version = VOLUMEHEADERVERSION;
2167 tempHeader.id = isp->volumeId;
2168 tempHeader.parent = isp->RWvolumeId;
2169 /* Check for duplicates (inodes are sorted by type field) */
2170 for (i = 0; i < isp->nSpecialInodes - 1; i++) {
2171 ip = &inodes[isp->index + i];
2172 if (ip->u.special.type == (ip + 1)->u.special.type) {
2174 Log("Duplicate special inodes in volume header; salvage of volume %u aborted\n", isp->volumeId);
2178 for (i = 0; i < isp->nSpecialInodes; i++) {
2179 ip = &inodes[isp->index + i];
2180 if (ip->u.special.type <= 0 || ip->u.special.type > MAXINODETYPE) {
2182 Log("Rubbish header inode\n");
2185 Log("Rubbish header inode; deleted\n");
2186 } else if (!stuff[ip->u.special.type - 1].obsolete) {
2187 *(stuff[ip->u.special.type - 1].inode) = ip->inodeNumber;
2188 if (!check && ip->u.special.type != VI_LINKTABLE)
2189 ip->linkCount--; /* Keep the inode around */
2190 allinodesobsolete = 0;
2194 if (allinodesobsolete) {
2201 VGLinkH_cnt++; /* one for every header. */
2203 if (!RW && !check && isp->volSummary) {
2204 ClearROInUseBit(isp->volSummary);
2208 for (i = 0; i < MAXINODETYPE; i++) {
2209 if (stuff[i].inodeType == VI_LINKTABLE) {
2210 /* Gross hack: SalvageHeader does a bcmp on the volume header.
2211 * And we may have recreated the link table earlier, so set the
2212 * RW header as well.
2214 if (VALID_INO(VGLinkH->ih_ino)) {
2215 *stuff[i].inode = VGLinkH->ih_ino;
2219 if (SalvageHeader(&stuff[i], isp, check, deleteMe) == -1 && check)
2223 if (isp->volSummary == NULL) {
2225 (void)afs_snprintf(name, sizeof name, VFORMAT, isp->volumeId);
2227 Log("No header file for volume %u\n", isp->volumeId);
2231 Log("No header file for volume %u; %screating %s/%s\n",
2232 isp->volumeId, (Testing ? "it would have been " : ""),
2233 fileSysPathName, name);
2234 headerFd = afs_open(name, O_RDWR | O_CREAT | O_TRUNC, 0644);
2235 assert(headerFd != -1);
2236 isp->volSummary = (struct VolumeSummary *)
2237 malloc(sizeof(struct VolumeSummary));
2238 isp->volSummary->fileName = ToString(name);
2241 /* hack: these two fields are obsolete... */
2242 isp->volSummary->header.volumeAcl = 0;
2243 isp->volSummary->header.volumeMountTable = 0;
2246 (&isp->volSummary->header, &tempHeader,
2247 sizeof(struct VolumeHeader))) {
2248 /* We often remove the name before calling us, so we make a fake one up */
2249 if (isp->volSummary->fileName) {
2250 strcpy(name, isp->volSummary->fileName);
2252 (void)afs_snprintf(name, sizeof name, VFORMAT, isp->volumeId);
2253 isp->volSummary->fileName = ToString(name);
2256 Log("Header file %s is damaged or no longer valid%s\n", name,
2257 (check ? "" : "; repairing"));
2261 headerFd = afs_open(name, O_RDWR | O_TRUNC, 0644);
2262 assert(headerFd != -1);
2266 memcpy(&isp->volSummary->header, &tempHeader,
2267 sizeof(struct VolumeHeader));
2270 Log("It would have written a new header file for volume %u\n",
2273 VolumeHeaderToDisk(&diskHeader, &tempHeader);
2274 if (write(headerFd, &diskHeader, sizeof(struct VolumeDiskHeader))
2275 != sizeof(struct VolumeDiskHeader)) {
2276 Log("Couldn't rewrite volume header file!\n");
2283 IH_INIT(isp->volSummary->volumeInfoHandle, fileSysDevice, isp->RWvolumeId,
2284 isp->volSummary->header.volumeInfo);
2289 SalvageHeader(register struct stuff *sp, struct InodeSummary *isp, int check,
2293 VolumeDiskData volumeInfo;
2294 struct versionStamp fileHeader;
2303 #ifndef AFS_NAMEI_ENV
2304 if (sp->inodeType == VI_LINKTABLE)
2307 if (*(sp->inode) == 0) {
2309 Log("Missing inode in volume header (%s)\n", sp->description);
2313 Log("Missing inode in volume header (%s); %s\n", sp->description,
2314 (Testing ? "it would have recreated it" : "recreating"));
2317 IH_CREATE(NULL, fileSysDevice, fileSysPath, 0, isp->volumeId,
2318 INODESPECIAL, sp->inodeType, isp->RWvolumeId);
2319 if (!VALID_INO(*(sp->inode)))
2321 ("Unable to allocate inode (%s) for volume header (error = %d)\n",
2322 sp->description, errno);
2327 IH_INIT(specH, fileSysDevice, isp->RWvolumeId, *(sp->inode));
2328 fdP = IH_OPEN(specH);
2329 if (OKToZap && (fdP == NULL) && BadError(errno)) {
2330 /* bail out early and destroy the volume */
2332 Log("Still can't open volume header inode (%s), destroying volume\n", sp->description);
2339 Abort("Unable to open inode (%s) of volume header (error = %d)\n",
2340 sp->description, errno);
2343 && (FDH_READ(fdP, (char *)&header, sp->size) != sp->size
2344 || header.fileHeader.magic != sp->stamp.magic)) {
2346 Log("Part of the header (%s) is corrupted\n", sp->description);
2347 FDH_REALLYCLOSE(fdP);
2351 Log("Part of the header (%s) is corrupted; recreating\n",
2355 if (sp->inodeType == VI_VOLINFO
2356 && header.volumeInfo.destroyMe == DESTROY_ME) {
2359 FDH_REALLYCLOSE(fdP);
2363 if (recreate && !Testing) {
2366 ("Internal error: recreating volume header (%s) in check mode\n",
2368 code = FDH_TRUNC(fdP, 0);
2370 Abort("Unable to truncate volume header file (%s) (error = %d)\n",
2371 sp->description, errno);
2373 /* The following code should be moved into vutil.c */
2374 if (sp->inodeType == VI_VOLINFO) {
2376 memset(&header.volumeInfo, 0, sizeof(header.volumeInfo));
2377 header.volumeInfo.stamp = sp->stamp;
2378 header.volumeInfo.id = isp->volumeId;
2379 header.volumeInfo.parentId = isp->RWvolumeId;
2380 sprintf(header.volumeInfo.name, "bogus.%u", isp->volumeId);
2381 Log("Warning: the name of volume %u is now \"bogus.%u\"\n",
2382 isp->volumeId, isp->volumeId);
2383 header.volumeInfo.inService = 0;
2384 header.volumeInfo.blessed = 0;
2385 /* The + 1000 is a hack in case there are any files out in venus caches */
2386 header.volumeInfo.uniquifier = (isp->maxUniquifier + 1) + 1000;
2387 header.volumeInfo.type = (isp->volumeId == isp->RWvolumeId ? readwriteVolume : readonlyVolume); /* XXXX */
2388 header.volumeInfo.needsCallback = 0;
2389 gettimeofday(&tp, 0);
2390 header.volumeInfo.creationDate = tp.tv_sec;
2391 if (FDH_SEEK(fdP, 0, SEEK_SET) < 0) {
2393 ("Unable to seek to beginning of volume header file (%s) (errno = %d)\n",
2394 sp->description, errno);
2397 FDH_WRITE(fdP, (char *)&header.volumeInfo,
2398 sizeof(header.volumeInfo));
2399 if (code != sizeof(header.volumeInfo)) {
2402 ("Unable to write volume header file (%s) (errno = %d)\n",
2403 sp->description, errno);
2404 Abort("Unable to write entire volume header file (%s)\n",
2408 if (FDH_SEEK(fdP, 0, SEEK_SET) < 0) {
2410 ("Unable to seek to beginning of volume header file (%s) (errno = %d)\n",
2411 sp->description, errno);
2413 code = FDH_WRITE(fdP, (char *)&sp->stamp, sizeof(sp->stamp));
2414 if (code != sizeof(sp->stamp)) {
2417 ("Unable to write version stamp in volume header file (%s) (errno = %d)\n",
2418 sp->description, errno);
2420 ("Unable to write entire version stamp in volume header file (%s)\n",
2425 FDH_REALLYCLOSE(fdP);
2427 if (sp->inodeType == VI_VOLINFO) {
2428 VolInfo = header.volumeInfo;
2431 if (VolInfo.updateDate) {
2432 strcpy(update, TimeStamp(VolInfo.updateDate, 0));
2434 Log("%s (%u) %supdated %s\n", VolInfo.name, VolInfo.id,
2435 (Testing ? "it would have been " : ""), update);
2437 strcpy(update, TimeStamp(VolInfo.creationDate, 0));
2439 Log("%s (%u) not updated (created %s)\n", VolInfo.name,
2440 VolInfo.id, update);
2450 SalvageVnodes(register struct InodeSummary *rwIsp,
2451 register struct InodeSummary *thisIsp,
2452 register struct ViceInodeInfo *inodes, int check)
2454 int ilarge, ismall, ioffset, RW, nInodes;
2455 ioffset = rwIsp->index + rwIsp->nSpecialInodes; /* first inode */
2458 RW = (rwIsp == thisIsp);
2459 nInodes = (rwIsp->nInodes - rwIsp->nSpecialInodes);
2461 SalvageIndex(thisIsp->volSummary->header.smallVnodeIndex, vSmall, RW,
2462 &inodes[ioffset], nInodes, thisIsp->volSummary, check);
2463 if (check && ismall == -1)
2466 SalvageIndex(thisIsp->volSummary->header.largeVnodeIndex, vLarge, RW,
2467 &inodes[ioffset], nInodes, thisIsp->volSummary, check);
2468 return (ilarge == 0 && ismall == 0 ? 0 : -1);
2472 SalvageIndex(Inode ino, VnodeClass class, int RW,
2473 register struct ViceInodeInfo *ip, int nInodes,
2474 struct VolumeSummary *volSummary, int check)
2476 VolumeId volumeNumber;
2477 char buf[SIZEOF_LARGEDISKVNODE];
2478 struct VnodeDiskObject *vnode = (struct VnodeDiskObject *)buf;
2480 StreamHandle_t *file;
2481 struct VnodeClassInfo *vcp;
2483 afs_fsize_t vnodeLength;
2484 int vnodeIndex, nVnodes;
2485 afs_ino_str_t stmp1, stmp2;
2489 volumeNumber = volSummary->header.id;
2490 IH_INIT(handle, fileSysDevice, volSummary->header.parent, ino);
2491 fdP = IH_OPEN(handle);
2492 assert(fdP != NULL);
2493 file = FDH_FDOPEN(fdP, "r+");
2494 assert(file != NULL);
2495 vcp = &VnodeClassInfo[class];
2496 size = OS_SIZE(fdP->fd_fd);
2498 nVnodes = (size / vcp->diskSize) - 1;
2500 assert((nVnodes + 1) * vcp->diskSize == size);
2501 assert(STREAM_SEEK(file, vcp->diskSize, 0) == 0);
2505 for (vnodeIndex = 0;
2506 nVnodes && STREAM_READ(vnode, vcp->diskSize, 1, file) == 1;
2507 nVnodes--, vnodeIndex++) {
2508 if (vnode->type != vNull) {
2509 int vnodeChanged = 0;
2510 int vnodeNumber = bitNumberToVnodeNumber(vnodeIndex, class);
2511 /* Log programs that belong to root (potentially suid root);
2512 * don't bother for read-only or backup volumes */
2513 #ifdef notdef /* This is done elsewhere */
2514 if (ShowRootFiles && RW && vnode->owner == 0 && vnodeNumber != 1)
2515 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);
2517 if (VNDISK_GET_INO(vnode) == 0) {
2519 /* Log("### DEBUG ### Deleted Vnode with 0 inode (vnode %d)\n", vnodeNumber); */
2520 memset(vnode, 0, vcp->diskSize);
2524 if (vcp->magic != vnode->vnodeMagic) {
2525 /* bad magic #, probably partially created vnode */
2526 Log("Partially allocated vnode %d deleted.\n",
2528 memset(vnode, 0, vcp->diskSize);
2532 /* ****** Should do a bit more salvage here: e.g. make sure
2533 * vnode type matches what it should be given the index */
2534 while (nInodes && ip->u.vnode.vnodeNumber < vnodeNumber) {
2535 /* if (vnodeIdToClass(ip->u.vnode.vnodeNumber) == class && RW) {
2536 * Log("Inode %d: says it belongs to non-existing vnode %d\n",
2537 * ip->inodeNumber, ip->u.vnode.vnodeNumber);
2544 while (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2545 /* The following doesn't work, because the version number
2546 * is not maintained correctly by the file server */
2547 /*if (vnode->uniquifier == ip->u.vnode.vnodeUniquifier &&
2548 * vnode->dataVersion == ip->u.vnode.inodeDataVersion)
2550 if (VNDISK_GET_INO(vnode) == ip->inodeNumber)
2556 /* For RW volume, look for vnode with matching inode number;
2557 * if no such match, take the first determined by our sort
2559 register struct ViceInodeInfo *lip = ip;
2560 register lnInodes = nInodes;
2562 && lip->u.vnode.vnodeNumber == vnodeNumber) {
2563 if (VNDISK_GET_INO(vnode) == lip->inodeNumber) {
2572 if (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2573 /* "Matching" inode */
2577 vu = vnode->uniquifier;
2578 iu = ip->u.vnode.vnodeUniquifier;
2579 vd = vnode->dataVersion;
2580 id = ip->u.vnode.inodeDataVersion;
2582 * Because of the possibility of the uniquifier overflows (> 4M)
2583 * we compare them modulo the low 22-bits; we shouldn't worry
2584 * about mismatching since they shouldn't to many old
2585 * uniquifiers of the same vnode...
2587 if (IUnique(vu) != IUnique(iu)) {
2589 Log("Vnode %u: vnode.unique, %u, does not match inode unique, %u; fixed, but status will be wrong\n", vnodeNumber, IUnique(vu), IUnique(iu));
2592 vnode->uniquifier = iu;
2593 #ifdef AFS_3DISPARES
2594 vnode->dataVersion = (id >= vd ?
2597 1887437 ? vd : id) :
2600 1887437 ? id : vd));
2602 #if defined(AFS_SGI_EXMAG)
2603 vnode->dataVersion = (id >= vd ?
2606 15099494 ? vd : id) :
2609 15099494 ? id : vd));
2611 vnode->dataVersion = (id > vd ? id : vd);
2612 #endif /* AFS_SGI_EXMAG */
2613 #endif /* AFS_3DISPARES */
2616 /* don't bother checking for vd > id any more, since
2617 * partial file transfers always result in this state,
2618 * and you can't do much else anyway (you've already
2619 * found the best data you can) */
2620 #ifdef AFS_3DISPARES
2621 if (!vnodeIsDirectory(vnodeNumber)
2622 && ((vd < id && (id - vd) < 1887437)
2623 || ((vd > id && (vd - id) > 1887437)))) {
2625 #if defined(AFS_SGI_EXMAG)
2626 if (!vnodeIsDirectory(vnodeNumber)
2627 && ((vd < id && (id - vd) < 15099494)
2628 || ((vd > id && (vd - id) > 15099494)))) {
2630 if (!vnodeIsDirectory(vnodeNumber) && vd < id) {
2631 #endif /* AFS_SGI_EXMAG */
2634 Log("Vnode %d: version < inode version; fixed (old status)\n", vnodeNumber);
2635 vnode->dataVersion = id;
2640 if (ip->inodeNumber != VNDISK_GET_INO(vnode)) {
2643 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);
2645 VNDISK_SET_INO(vnode, ip->inodeNumber);
2650 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);
2652 VNDISK_SET_INO(vnode, ip->inodeNumber);
2655 VNDISK_GET_LEN(vnodeLength, vnode);
2656 if (ip->byteCount != vnodeLength) {
2659 Log("Vnode %d: length incorrect; (is %llu should be %llu)\n", vnodeNumber, (afs_uintmax_t) vnodeLength, (afs_uintmax_t) ip->byteCount);
2664 Log("Vnode %d: length incorrect; changed from %llu to %llu\n", vnodeNumber, (afs_uintmax_t) vnodeLength, (afs_uintmax_t) ip->byteCount);
2665 VNDISK_SET_LEN(vnode, ip->byteCount);
2669 ip->linkCount--; /* Keep the inode around */
2672 } else { /* no matching inode */
2673 if (VNDISK_GET_INO(vnode) != 0
2674 || vnode->type == vDirectory) {
2675 /* No matching inode--get rid of the vnode */
2677 if (VNDISK_GET_INO(vnode)) {
2679 Log("Vnode %d (unique %u): corresponding inode %s is missing\n", vnodeNumber, vnode->uniquifier, PrintInode(NULL, VNDISK_GET_INO(vnode)));
2683 Log("Vnode %d (unique %u): bad directory vnode (no inode number listed)\n", vnodeNumber, vnode->uniquifier);
2688 if (VNDISK_GET_INO(vnode)) {
2690 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)));
2694 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)));
2696 memset(vnode, 0, vcp->diskSize);
2699 /* Should not reach here becuase we checked for
2700 * (inodeNumber == 0) above. And where we zero the vnode,
2701 * we also goto vnodeDone.
2705 while (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2709 } /* VNDISK_GET_INO(vnode) != 0 */
2711 assert(!(vnodeChanged && check));
2712 if (vnodeChanged && !Testing) {
2714 (handle, vnodeIndexOffset(vcp, vnodeNumber),
2715 (char *)vnode, vcp->diskSize)
2717 VolumeChanged = 1; /* For break call back */
2728 struct VnodeEssence *
2729 CheckVnodeNumber(VnodeId vnodeNumber)
2732 struct VnodeInfo *vip;
2735 class = vnodeIdToClass(vnodeNumber);
2736 vip = &vnodeInfo[class];
2737 offset = vnodeIdToBitNumber(vnodeNumber);
2738 return (offset >= vip->nVnodes ? NULL : &vip->vnodes[offset]);
2742 CopyOnWrite(register struct DirSummary *dir)
2744 /* Copy the directory unconditionally if we are going to change it:
2745 * not just if was cloned.
2747 struct VnodeDiskObject vnode;
2748 struct VnodeClassInfo *vcp = &VnodeClassInfo[vLarge];
2749 Inode oldinode, newinode;
2752 if (dir->copied || Testing)
2754 DFlush(); /* Well justified paranoia... */
2757 IH_IREAD(vnodeInfo[vLarge].handle,
2758 vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2760 assert(code == sizeof(vnode));
2761 oldinode = VNDISK_GET_INO(&vnode);
2762 /* Increment the version number by a whole lot to avoid problems with
2763 * clients that were promised new version numbers--but the file server
2764 * crashed before the versions were written to disk.
2767 IH_CREATE(dir->ds_linkH, fileSysDevice, fileSysPath, 0, dir->rwVid,
2768 dir->vnodeNumber, vnode.uniquifier, vnode.dataVersion +=
2770 assert(VALID_INO(newinode));
2771 assert(CopyInode(fileSysDevice, oldinode, newinode, dir->rwVid) == 0);
2773 VNDISK_SET_INO(&vnode, newinode);
2775 IH_IWRITE(vnodeInfo[vLarge].handle,
2776 vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2778 assert(code == sizeof(vnode));
2780 SetSalvageDirHandle(&dir->dirHandle, dir->dirHandle.dirh_handle->ih_vid,
2781 fileSysDevice, newinode);
2782 /* Don't delete the original inode right away, because the directory is
2783 * still being scanned.
2789 * This function should either successfully create a new dir, or give up
2790 * and leave things the way they were. In particular, if it fails to write
2791 * the new dir properly, it should return w/o changing the reference to the
2795 CopyAndSalvage(register struct DirSummary *dir)
2797 struct VnodeDiskObject vnode;
2798 struct VnodeClassInfo *vcp = &VnodeClassInfo[vLarge];
2799 Inode oldinode, newinode;
2801 register afs_int32 code;
2802 afs_int32 parentUnique = 1;
2803 struct VnodeEssence *vnodeEssence;
2807 Log("Salvaging directory %u...\n", dir->vnodeNumber);
2809 IH_IREAD(vnodeInfo[vLarge].handle,
2810 vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2812 assert(code == sizeof(vnode));
2813 oldinode = VNDISK_GET_INO(&vnode);
2814 /* Increment the version number by a whole lot to avoid problems with
2815 * clients that were promised new version numbers--but the file server
2816 * crashed before the versions were written to disk.
2819 IH_CREATE(dir->ds_linkH, fileSysDevice, fileSysPath, 0, dir->rwVid,
2820 dir->vnodeNumber, vnode.uniquifier, vnode.dataVersion +=
2822 assert(VALID_INO(newinode));
2823 SetSalvageDirHandle(&newdir, dir->rwVid, fileSysDevice, newinode);
2825 /* Assign . and .. vnode numbers from dir and vnode.parent.
2826 * The uniquifier for . is in the vnode.
2827 * The uniquifier for .. might be set to a bogus value of 1 and
2828 * the salvager will later clean it up.
2830 if (vnode.parent && (vnodeEssence = CheckVnodeNumber(vnode.parent))) {
2831 parentUnique = (vnodeEssence->unique ? vnodeEssence->unique : 1);
2834 DirSalvage(&dir->dirHandle, &newdir, dir->vnodeNumber,
2836 (vnode.parent ? vnode.parent : dir->vnodeNumber),
2841 /* didn't really build the new directory properly, let's just give up. */
2842 code = IH_DEC(dir->ds_linkH, newinode, dir->rwVid);
2844 Log("Directory salvage returned code %d, continuing.\n", code);
2847 Log("Checking the results of the directory salvage...\n");
2848 if (!DirOK(&newdir)) {
2849 Log("Directory salvage failed!!!; restoring old version of the directory.\n");
2850 code = IH_DEC(dir->ds_linkH, newinode, dir->rwVid);
2855 VNDISK_SET_INO(&vnode, newinode);
2856 VNDISK_SET_LEN(&vnode, Length(&newdir));
2858 IH_IWRITE(vnodeInfo[vLarge].handle,
2859 vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2861 assert(code == sizeof(vnode));
2863 nt_sync(fileSysDevice);
2865 sync(); /* this is slow, but hopefully rarely called. We don't have
2866 * an open FD on the file itself to fsync.
2869 code = IH_DEC(dir->ds_linkH, oldinode, dir->rwVid);
2871 dir->dirHandle = newdir;
2875 JudgeEntry(struct DirSummary *dir, char *name, VnodeId vnodeNumber,
2878 struct VnodeEssence *vnodeEssence;
2879 afs_int32 dirOrphaned, todelete;
2881 dirOrphaned = IsVnodeOrphaned(dir->vnodeNumber);
2883 vnodeEssence = CheckVnodeNumber(vnodeNumber);
2884 if (vnodeEssence == NULL) {
2886 Log("dir vnode %u: invalid entry deleted: %s/%s (vnode %u, unique %u)\n", dir->vnodeNumber, dir->name ? dir->name : "??", name, vnodeNumber, unique);
2890 assert(Delete(&dir->dirHandle, name) == 0);
2895 #ifndef AFS_NAMEI_ENV
2896 /* On AIX machines, don't allow entries to point to inode 0. That is a special
2897 * mount inode for the partition. If this inode were deleted, it would crash
2900 if (vnodeEssence->InodeNumber == 0) {
2901 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"));
2904 assert(Delete(&dir->dirHandle, name) == 0);
2911 if (!(vnodeNumber & 1) && !Showmode
2912 && !(vnodeEssence->count || vnodeEssence->unique
2913 || vnodeEssence->modeBits)) {
2914 Log("dir vnode %u: invalid entry: %s/%s (vnode %u, unique %u)%s\n",
2915 dir->vnodeNumber, (dir->name ? dir->name : "??"), name,
2916 vnodeNumber, unique,
2917 ((!unique) ? (Testing ? "-- would have deleted" : " -- deleted") :
2922 assert(Delete(&dir->dirHandle, name) == 0);
2928 /* Check if the Uniquifiers match. If not, change the directory entry
2929 * so its unique matches the vnode unique. Delete if the unique is zero
2930 * or if the directory is orphaned.
2932 if (!vnodeEssence->unique || (vnodeEssence->unique) != unique) {
2933 if (!vnodeEssence->unique
2934 && ((strcmp(name, "..") == 0) || (strcmp(name, ".") == 0))) {
2935 /* This is an orphaned directory. Don't delete the . or ..
2936 * entry. Otherwise, it will get created in the next
2937 * salvage and deleted again here. So Just skip it.
2942 todelete = ((!vnodeEssence->unique || dirOrphaned) ? 1 : 0);
2945 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")));
2949 fid.Vnode = vnodeNumber;
2950 fid.Unique = vnodeEssence->unique;
2952 assert(Delete(&dir->dirHandle, name) == 0);
2954 assert(Create(&dir->dirHandle, name, &fid) == 0);
2957 return; /* no need to continue */
2960 if (strcmp(name, ".") == 0) {
2961 if (dir->vnodeNumber != vnodeNumber || (dir->unique != unique)) {
2964 Log("directory vnode %u.%u: bad '.' entry (was %u.%u); fixed\n", dir->vnodeNumber, dir->unique, vnodeNumber, unique);
2967 assert(Delete(&dir->dirHandle, ".") == 0);
2968 fid.Vnode = dir->vnodeNumber;
2969 fid.Unique = dir->unique;
2970 assert(Create(&dir->dirHandle, ".", &fid) == 0);
2973 vnodeNumber = fid.Vnode; /* Get the new Essence */
2974 unique = fid.Unique;
2975 vnodeEssence = CheckVnodeNumber(vnodeNumber);
2978 } else if (strcmp(name, "..") == 0) {
2981 struct VnodeEssence *dotdot;
2982 pa.Vnode = dir->parent;
2983 dotdot = CheckVnodeNumber(pa.Vnode);
2984 assert(dotdot != NULL); /* XXX Should not be assert */
2985 pa.Unique = dotdot->unique;
2987 pa.Vnode = dir->vnodeNumber;
2988 pa.Unique = dir->unique;
2990 if ((pa.Vnode != vnodeNumber) || (pa.Unique != unique)) {
2992 Log("directory vnode %u.%u: bad '..' entry (was %u.%u); fixed\n", dir->vnodeNumber, dir->unique, vnodeNumber, unique);
2995 assert(Delete(&dir->dirHandle, "..") == 0);
2996 assert(Create(&dir->dirHandle, "..", &pa) == 0);
2999 vnodeNumber = pa.Vnode; /* Get the new Essence */
3001 vnodeEssence = CheckVnodeNumber(vnodeNumber);
3003 dir->haveDotDot = 1;
3004 } else if (strncmp(name, ".__afs", 6) == 0) {
3006 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);
3010 assert(Delete(&dir->dirHandle, name) == 0);
3012 vnodeEssence->claimed = 0; /* Not claimed: Orphaned */
3013 vnodeEssence->todelete = 1; /* Will later delete vnode and decr inode */
3016 if (ShowSuid && (vnodeEssence->modeBits & 06000))
3017 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);
3018 if (ShowMounts && (vnodeEssence->type == vSymlink)
3019 && !(vnodeEssence->modeBits & 0111)) {
3025 IH_INIT(ihP, fileSysDevice, dir->dirHandle.dirh_handle->ih_vid,
3026 vnodeEssence->InodeNumber);
3028 assert(fdP != NULL);
3029 size = FDH_SIZE(fdP);
3031 memset(buf, 0, 1024);
3034 code = FDH_READ(fdP, buf, size);
3035 assert(code == size);
3036 Log("In volume %u (%s) found mountpoint %s/%s to '%s'\n",
3037 dir->dirHandle.dirh_handle->ih_vid, dir->vname,
3038 dir->name ? dir->name : "??", name, buf);
3039 FDH_REALLYCLOSE(fdP);
3042 if (ShowRootFiles && vnodeEssence->owner == 0 && vnodeNumber != 1)
3043 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);
3044 if (vnodeIdToClass(vnodeNumber) == vLarge
3045 && vnodeEssence->name == NULL) {
3047 if ((n = (char *)malloc(strlen(name) + 1)))
3049 vnodeEssence->name = n;
3052 /* The directory entry points to the vnode. Check to see if the
3053 * vnode points back to the directory. If not, then let the
3054 * directory claim it (else it might end up orphaned). Vnodes
3055 * already claimed by another directory are deleted from this
3056 * directory: hardlinks to the same vnode are not allowed
3057 * from different directories.
3059 if (vnodeEssence->parent != dir->vnodeNumber) {
3060 if (!vnodeEssence->claimed && !dirOrphaned && vnodeNumber != 1) {
3061 /* Vnode does not point back to this directory.
3062 * Orphaned dirs cannot claim a file (it may belong to
3063 * another non-orphaned dir).
3066 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);
3068 vnodeEssence->parent = dir->vnodeNumber;
3069 vnodeEssence->changed = 1;
3071 /* Vnode was claimed by another directory */
3074 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 " : ""));
3075 } else if (vnodeNumber == 1) {
3076 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 " : ""));
3078 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 " : ""));
3083 assert(Delete(&dir->dirHandle, name) == 0);
3088 /* This directory claims the vnode */
3089 vnodeEssence->claimed = 1;
3091 vnodeEssence->count--;
3095 DistilVnodeEssence(VolumeId rwVId, VnodeClass class, Inode ino, Unique * maxu)
3097 register struct VnodeInfo *vip = &vnodeInfo[class];
3098 struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
3099 char buf[SIZEOF_LARGEDISKVNODE];
3100 struct VnodeDiskObject *vnode = (struct VnodeDiskObject *)buf;
3102 StreamHandle_t *file;
3107 IH_INIT(vip->handle, fileSysDevice, rwVId, ino);
3108 fdP = IH_OPEN(vip->handle);
3109 assert(fdP != NULL);
3110 file = FDH_FDOPEN(fdP, "r+");
3111 assert(file != NULL);
3112 size = OS_SIZE(fdP->fd_fd);
3114 vip->nVnodes = (size / vcp->diskSize) - 1;
3115 if (vip->nVnodes > 0) {
3116 assert((vip->nVnodes + 1) * vcp->diskSize == size);
3117 assert(STREAM_SEEK(file, vcp->diskSize, 0) == 0);
3118 assert((vip->vnodes = (struct VnodeEssence *)
3119 calloc(vip->nVnodes, sizeof(struct VnodeEssence))) != NULL);
3120 if (class == vLarge) {
3121 assert((vip->inodes = (Inode *)
3122 calloc(vip->nVnodes, sizeof(Inode))) != NULL);
3131 vip->volumeBlockCount = vip->nAllocatedVnodes = 0;
3132 for (vnodeIndex = 0, nVnodes = vip->nVnodes;
3133 nVnodes && STREAM_READ(vnode, vcp->diskSize, 1, file) == 1;
3134 nVnodes--, vnodeIndex++) {
3135 if (vnode->type != vNull) {
3136 register struct VnodeEssence *vep = &vip->vnodes[vnodeIndex];
3137 afs_fsize_t vnodeLength;
3138 vip->nAllocatedVnodes++;
3139 vep->count = vnode->linkCount;
3140 VNDISK_GET_LEN(vnodeLength, vnode);
3141 vep->blockCount = nBlocks(vnodeLength);
3142 vip->volumeBlockCount += vep->blockCount;
3143 vep->parent = vnode->parent;
3144 vep->unique = vnode->uniquifier;
3145 if (*maxu < vnode->uniquifier)
3146 *maxu = vnode->uniquifier;
3147 vep->modeBits = vnode->modeBits;
3148 vep->InodeNumber = VNDISK_GET_INO(vnode);
3149 vep->type = vnode->type;
3150 vep->author = vnode->author;
3151 vep->owner = vnode->owner;
3152 vep->group = vnode->group;
3153 if (vnode->type == vDirectory) {
3154 assert(class == vLarge);
3155 vip->inodes[vnodeIndex] = VNDISK_GET_INO(vnode);
3164 GetDirName(VnodeId vnode, struct VnodeEssence *vp, char *path)
3166 struct VnodeEssence *parentvp;
3172 if (vp->parent && vp->name && (parentvp = CheckVnodeNumber(vp->parent))
3173 && GetDirName(vp->parent, parentvp, path)) {
3175 strcat(path, vp->name);
3181 /* To determine if a vnode is orhpaned or not, the vnode and all its parent
3182 * vnodes must be "claimed". The vep->claimed flag is set in JudgeEntry().
3185 IsVnodeOrphaned(VnodeId vnode)
3187 struct VnodeEssence *vep;
3190 return (1); /* Vnode zero does not exist */
3192 return (0); /* The root dir vnode is always claimed */
3193 vep = CheckVnodeNumber(vnode); /* Get the vnode essence */
3194 if (!vep || !vep->claimed)
3195 return (1); /* Vnode is not claimed - it is orphaned */
3197 return (IsVnodeOrphaned(vep->parent));
3201 SalvageDir(char *name, VolumeId rwVid, struct VnodeInfo *dirVnodeInfo,
3202 IHandle_t * alinkH, int i, struct DirSummary *rootdir,
3205 static struct DirSummary dir;
3206 static struct DirHandle dirHandle;
3207 struct VnodeEssence *parent;
3208 static char path[MAXPATHLEN];
3211 if (dirVnodeInfo->vnodes[i].salvaged)
3212 return; /* already salvaged */
3215 dirVnodeInfo->vnodes[i].salvaged = 1;
3217 if (dirVnodeInfo->inodes[i] == 0)
3218 return; /* Not allocated to a directory */
3220 if (bitNumberToVnodeNumber(i, vLarge) == 1) {
3221 if (dirVnodeInfo->vnodes[i].parent) {
3222 Log("Bad parent, vnode 1; %s...\n",
3223 (Testing ? "skipping" : "salvaging"));
3224 dirVnodeInfo->vnodes[i].parent = 0;
3225 dirVnodeInfo->vnodes[i].changed = 1;
3228 parent = CheckVnodeNumber(dirVnodeInfo->vnodes[i].parent);
3229 if (parent && parent->salvaged == 0)
3230 SalvageDir(name, rwVid, dirVnodeInfo, alinkH,
3231 vnodeIdToBitNumber(dirVnodeInfo->vnodes[i].parent),
3232 rootdir, rootdirfound);
3235 dir.vnodeNumber = bitNumberToVnodeNumber(i, vLarge);
3236 dir.unique = dirVnodeInfo->vnodes[i].unique;
3239 dir.parent = dirVnodeInfo->vnodes[i].parent;
3240 dir.haveDot = dir.haveDotDot = 0;
3241 dir.ds_linkH = alinkH;
3242 SetSalvageDirHandle(&dir.dirHandle, dir.rwVid, fileSysDevice,
3243 dirVnodeInfo->inodes[i]);
3245 dirok = ((RebuildDirs && !Testing) ? 0 : DirOK(&dir.dirHandle));
3248 Log("Directory bad, vnode %u; %s...\n", dir.vnodeNumber,
3249 (Testing ? "skipping" : "salvaging"));
3252 CopyAndSalvage(&dir);
3256 dirHandle = dir.dirHandle;
3259 GetDirName(bitNumberToVnodeNumber(i, vLarge),
3260 &dirVnodeInfo->vnodes[i], path);
3263 /* If enumeration failed for random reasons, we will probably delete
3264 * too much stuff, so we guard against this instead.
3266 assert(EnumerateDir(&dirHandle, JudgeEntry, &dir) == 0);
3269 /* Delete the old directory if it was copied in order to salvage.
3270 * CopyOnWrite has written the new inode # to the disk, but we still
3271 * have the old one in our local structure here. Thus, we idec the
3275 if (dir.copied && !Testing) {
3276 code = IH_DEC(dir.ds_linkH, dirHandle.dirh_handle->ih_ino, rwVid);
3278 dirVnodeInfo->inodes[i] = dir.dirHandle.dirh_inode;
3281 /* Remember rootdir DirSummary _after_ it has been judged */
3282 if (dir.vnodeNumber == 1 && dir.unique == 1) {
3283 memcpy(rootdir, &dir, sizeof(struct DirSummary));
3291 SalvageVolume(register struct InodeSummary *rwIsp, IHandle_t * alinkH)
3293 /* This routine, for now, will only be called for read-write volumes */
3295 int BlocksInVolume = 0, FilesInVolume = 0;
3296 register VnodeClass class;
3297 struct DirSummary rootdir, oldrootdir;
3298 struct VnodeInfo *dirVnodeInfo;
3299 struct VnodeDiskObject vnode;
3300 VolumeDiskData volHeader;
3302 int orphaned, rootdirfound = 0;
3303 Unique maxunique = 0; /* the maxUniquifier from the vnodes */
3304 afs_int32 ofiles = 0, oblocks = 0; /* Number of orphaned files/blocks */
3305 struct VnodeEssence *vep;
3310 VnodeId LFVnode, ThisVnode;
3311 Unique LFUnique, ThisUnique;
3314 vid = rwIsp->volSummary->header.id;
3315 IH_INIT(h, fileSysDevice, vid, rwIsp->volSummary->header.volumeInfo);
3316 nBytes = IH_IREAD(h, 0, (char *)&volHeader, sizeof(volHeader));
3317 assert(nBytes == sizeof(volHeader));
3318 assert(volHeader.stamp.magic == VOLUMEINFOMAGIC);
3319 assert(volHeader.destroyMe != DESTROY_ME);
3320 /* (should not have gotten this far with DESTROY_ME flag still set!) */
3322 DistilVnodeEssence(vid, vLarge, rwIsp->volSummary->header.largeVnodeIndex,
3324 DistilVnodeEssence(vid, vSmall, rwIsp->volSummary->header.smallVnodeIndex,
3327 dirVnodeInfo = &vnodeInfo[vLarge];
3328 for (i = 0; i < dirVnodeInfo->nVnodes; i++) {
3329 SalvageDir(volHeader.name, vid, dirVnodeInfo, alinkH, i, &rootdir,
3337 /* Parse each vnode looking for orphaned vnodes and
3338 * connect them to the tree as orphaned (if requested).
3340 oldrootdir = rootdir;
3341 for (class = 0; class < nVNODECLASSES; class++) {
3342 for (v = 0; v < vnodeInfo[class].nVnodes; v++) {
3343 vep = &(vnodeInfo[class].vnodes[v]);
3344 ThisVnode = bitNumberToVnodeNumber(v, class);
3345 ThisUnique = vep->unique;
3347 if ((vep->type == 0) || vep->claimed || ThisVnode == 1)
3348 continue; /* Ignore unused, claimed, and root vnodes */
3350 /* This vnode is orphaned. If it is a directory vnode, then the '..'
3351 * entry in this vnode had incremented the parent link count (In
3352 * JudgeEntry()). We need to go to the parent and decrement that
3353 * link count. But if the parent's unique is zero, then the parent
3354 * link count was not incremented in JudgeEntry().
3356 if (class == vLarge) { /* directory vnode */
3357 pv = vnodeIdToBitNumber(vep->parent);
3358 if (vnodeInfo[vLarge].vnodes[pv].unique != 0)
3359 vnodeInfo[vLarge].vnodes[pv].count++;
3363 continue; /* If no rootdir, can't attach orphaned files */
3365 /* Here we attach orphaned files and directories into the
3366 * root directory, LVVnode, making sure link counts stay correct.
3368 if ((orphans == ORPH_ATTACH) && !vep->todelete && !Testing) {
3369 LFVnode = rootdir.vnodeNumber; /* Lost+Found vnode number */
3370 LFUnique = rootdir.unique; /* Lost+Found uniquifier */
3372 /* Update this orphaned vnode's info. Its parent info and
3373 * link count (do for orphaned directories and files).
3375 vep->parent = LFVnode; /* Parent is the root dir */
3376 vep->unique = LFUnique;
3379 vep->count--; /* Inc link count (root dir will pt to it) */
3381 /* If this orphaned vnode is a directory, change '..'.
3382 * The name of the orphaned dir/file is unknown, so we
3383 * build a unique name. No need to CopyOnWrite the directory
3384 * since it is not connected to tree in BK or RO volume and
3385 * won't be visible there.
3387 if (class == vLarge) {
3391 /* Remove and recreate the ".." entry in this orphaned directory */
3392 SetSalvageDirHandle(&dh, vid, fileSysDevice,
3393 vnodeInfo[class].inodes[v]);
3395 pa.Unique = LFUnique;
3396 assert(Delete(&dh, "..") == 0);
3397 assert(Create(&dh, "..", &pa) == 0);
3399 /* The original parent's link count was decremented above.
3400 * Here we increment the new parent's link count.
3402 pv = vnodeIdToBitNumber(LFVnode);
3403 vnodeInfo[vLarge].vnodes[pv].count--;
3407 /* Go to the root dir and add this entry. The link count of the
3408 * root dir was incremented when ".." was created. Try 10 times.
3410 for (j = 0; j < 10; j++) {
3411 pa.Vnode = ThisVnode;
3412 pa.Unique = ThisUnique;
3414 (void)afs_snprintf(npath, sizeof npath, "%s.%u.%u",
3416 vLarge) ? "__ORPHANDIR__" :
3417 "__ORPHANFILE__"), ThisVnode,
3420 CopyOnWrite(&rootdir);
3421 code = Create(&rootdir.dirHandle, npath, &pa);
3425 ThisUnique += 50; /* Try creating a different file */
3428 Log("Attaching orphaned %s to volume's root dir as %s\n",
3429 ((class == vLarge) ? "directory" : "file"), npath);
3431 } /* for each vnode in the class */
3432 } /* for each class of vnode */
3434 /* Delete the old rootinode directory if the rootdir was CopyOnWrite */
3436 if (!oldrootdir.copied && rootdir.copied) {
3438 IH_DEC(oldrootdir.ds_linkH, oldrootdir.dirHandle.dirh_inode,
3441 /* dirVnodeInfo->inodes[?] is not updated with new inode number */
3444 DFlush(); /* Flush the changes */
3445 if (!rootdirfound && (orphans == ORPH_ATTACH)) {
3446 Log("Cannot attach orphaned files and directories: Root directory not found\n");
3447 orphans = ORPH_IGNORE;
3450 /* Write out all changed vnodes. Orphaned files and directories
3451 * will get removed here also (if requested).
3453 for (class = 0; class < nVNODECLASSES; class++) {
3454 int nVnodes = vnodeInfo[class].nVnodes;
3455 struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
3456 struct VnodeEssence *vnodes = vnodeInfo[class].vnodes;
3457 FilesInVolume += vnodeInfo[class].nAllocatedVnodes;
3458 BlocksInVolume += vnodeInfo[class].volumeBlockCount;
3459 for (i = 0; i < nVnodes; i++) {
3460 register struct VnodeEssence *vnp = &vnodes[i];
3461 VnodeId vnodeNumber = bitNumberToVnodeNumber(i, class);
3463 /* If the vnode is good but is unclaimed (not listed in
3464 * any directory entries), then it is orphaned.
3467 if ((vnp->type != 0) && (orphaned = IsVnodeOrphaned(vnodeNumber))) {
3468 vnp->claimed = 0; /* Makes IsVnodeOrphaned calls faster */
3472 if (vnp->changed || vnp->count) {
3476 IH_IREAD(vnodeInfo[class].handle,
3477 vnodeIndexOffset(vcp, vnodeNumber),
3478 (char *)&vnode, sizeof(vnode));
3479 assert(nBytes == sizeof(vnode));
3481 vnode.parent = vnp->parent;
3482 oldCount = vnode.linkCount;
3483 vnode.linkCount = vnode.linkCount - vnp->count;
3486 orphaned = IsVnodeOrphaned(vnodeNumber);
3488 if (!vnp->todelete) {
3489 /* Orphans should have already been attached (if requested) */
3490 assert(orphans != ORPH_ATTACH);
3491 oblocks += vnp->blockCount;
3494 if (((orphans == ORPH_REMOVE) || vnp->todelete)
3496 BlocksInVolume -= vnp->blockCount;
3498 if (VNDISK_GET_INO(&vnode)) {
3500 IH_DEC(alinkH, VNDISK_GET_INO(&vnode), vid);
3503 memset(&vnode, 0, sizeof(vnode));
3505 } else if (vnp->count) {
3507 Log("Vnode %u: link count incorrect (was %d, %s %d)\n", vnodeNumber, oldCount, (Testing ? "would have changed to" : "now"), vnode.linkCount);
3511 vnode.dataVersion++;
3514 IH_IWRITE(vnodeInfo[class].handle,
3515 vnodeIndexOffset(vcp, vnodeNumber),
3516 (char *)&vnode, sizeof(vnode));
3517 assert(nBytes == sizeof(vnode));
3523 if (!Showmode && ofiles) {
3524 Log("%s %d orphaned files and directories (approx. %u KB)\n",
3526 && (orphans == ORPH_REMOVE)) ? "Removed" : "Found", ofiles,
3530 for (class = 0; class < nVNODECLASSES; class++) {
3531 register struct VnodeInfo *vip = &vnodeInfo[class];
3532 for (i = 0; i < vip->nVnodes; i++)
3533 if (vip->vnodes[i].name)
3534 free(vip->vnodes[i].name);
3541 /* Set correct resource utilization statistics */
3542 volHeader.filecount = FilesInVolume;
3543 volHeader.diskused = BlocksInVolume;
3545 /* Make sure the uniquifer is big enough: maxunique is the real maxUniquifier */
3546 if (volHeader.uniquifier < (maxunique + 1)) {
3548 Log("Volume uniquifier is too low; fixed\n");
3549 /* Plus 2,000 in case there are workstations out there with
3550 * cached vnodes that have since been deleted
3552 volHeader.uniquifier = (maxunique + 1 + 2000);
3555 /* Turn off the inUse bit; the volume's been salvaged! */
3556 volHeader.inUse = 0; /* clear flag indicating inUse@last crash */
3557 volHeader.needsSalvaged = 0; /* clear 'damaged' flag */
3558 volHeader.inService = 1; /* allow service again */
3559 volHeader.needsCallback = (VolumeChanged != 0);
3560 volHeader.dontSalvage = DONT_SALVAGE;
3563 nBytes = IH_IWRITE(h, 0, (char *)&volHeader, sizeof(volHeader));
3564 assert(nBytes == sizeof(volHeader));
3567 Log("%sSalvaged %s (%u): %d files, %d blocks\n",
3568 (Testing ? "It would have " : ""), volHeader.name, volHeader.id,
3569 FilesInVolume, BlocksInVolume);
3571 IH_RELEASE(vnodeInfo[vSmall].handle);
3572 IH_RELEASE(vnodeInfo[vLarge].handle);
3578 ClearROInUseBit(struct VolumeSummary *summary)
3580 IHandle_t *h = summary->volumeInfoHandle;
3583 VolumeDiskData volHeader;
3585 nBytes = IH_IREAD(h, 0, (char *)&volHeader, sizeof(volHeader));
3586 assert(nBytes == sizeof(volHeader));
3587 assert(volHeader.stamp.magic == VOLUMEINFOMAGIC);
3588 volHeader.inUse = 0;
3589 volHeader.needsSalvaged = 0;
3590 volHeader.inService = 1;
3591 volHeader.dontSalvage = DONT_SALVAGE;
3593 nBytes = IH_IREAD(h, 0, (char *)&volHeader, sizeof(volHeader));
3594 assert(nBytes == sizeof(volHeader));
3599 * Possible delete the volume.
3601 * deleteMe - Always do so, only a partial volume.
3604 MaybeZapVolume(register struct InodeSummary *isp, char *message, int deleteMe,
3607 if (readOnly(isp) || deleteMe) {
3608 if (isp->volSummary && isp->volSummary->fileName) {
3611 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);
3613 Log("It will be deleted on this server (you may find it elsewhere)\n");
3616 Log("Volume %u needs to be salvaged. Since it is read-only, however,\n", isp->volumeId);
3618 Log("it will be deleted instead. It should be recloned.\n");
3621 unlink(isp->volSummary->fileName);
3623 } else if (!check) {
3624 Log("%s salvage was unsuccessful: read-write volume %u\n", message,
3626 Abort("Salvage of volume %u aborted\n", isp->volumeId);
3632 AskOffline(VolumeId volumeId)
3634 if (FSYNC_askfs(volumeId, NULL, FSYNC_OFF, FSYNC_SALVAGE) == FSYNC_DENIED) {
3635 Log("AskOffline: file server denied offline request; a general salvage is required.\n");
3636 Abort("Salvage aborted\n");
3641 AskOnline(VolumeId volumeId, char *partition)
3643 if (FSYNC_askfs(volumeId, partition, FSYNC_ON, 0) == FSYNC_DENIED) {
3644 Log("AskOnline: file server denied online request to volume %u partition %s\n", volumeId, partition);
3649 CopyInode(Device device, Inode inode1, Inode inode2, int rwvolume)
3651 /* Volume parameter is passed in case iopen is upgraded in future to
3652 * require a volume Id to be passed
3655 IHandle_t *srcH, *destH;
3656 FdHandle_t *srcFdP, *destFdP;
3659 IH_INIT(srcH, device, rwvolume, inode1);
3660 srcFdP = IH_OPEN(srcH);
3661 assert(srcFdP != NULL);
3662 IH_INIT(destH, device, rwvolume, inode2);
3663 destFdP = IH_OPEN(destH);
3665 while ((n = FDH_READ(srcFdP, buf, sizeof(buf))) > 0)
3666 assert(FDH_WRITE(destFdP, buf, n) == n);
3668 FDH_REALLYCLOSE(srcFdP);
3669 FDH_REALLYCLOSE(destFdP);
3676 PrintInodeList(void)
3678 register struct ViceInodeInfo *ip;
3679 struct ViceInodeInfo *buf;
3680 struct afs_stat status;
3683 assert(afs_fstat(inodeFd, &status) == 0);
3684 buf = (struct ViceInodeInfo *)malloc(status.st_size);
3685 assert(buf != NULL);
3686 nInodes = status.st_size / sizeof(struct ViceInodeInfo);
3687 assert(read(inodeFd, buf, status.st_size) == status.st_size);
3688 for (ip = buf; nInodes--; ip++) {
3689 Log("Inode:%s, linkCount=%d, size=%#llx, p=(%u,%u,%u,%u)\n",
3690 PrintInode(NULL, ip->inodeNumber), ip->linkCount,
3691 (afs_uintmax_t) ip->byteCount, ip->u.param[0], ip->u.param[1],
3692 ip->u.param[2], ip->u.param[3]);
3698 PrintInodeSummary(void)
3701 struct InodeSummary *isp;
3703 for (i = 0; i < nVolumesInInodeFile; i++) {
3704 isp = &inodeSummary[i];
3705 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);
3710 PrintVolumeSummary(void)
3713 struct VolumeSummary *vsp;
3715 for (i = 0, vsp = volumeSummaryp; i < nVolumes; vsp++, i++) {
3716 Log("fileName:%s, header, wouldNeedCallback\n", vsp->fileName);
3726 assert(0); /* Fork is never executed in the NT code path */
3741 if (main_thread != pthread_self())
3742 pthread_exit((void *)code);
3755 pid = wait(&status);
3757 if (WCOREDUMP(status))
3758 Log("\"%s\" core dumped!\n", prog);
3759 if (WIFSIGNALED(status) != 0 || WEXITSTATUS(status) != 0)
3765 TimeStamp(time_t clock, int precision)
3768 static char timestamp[20];
3769 lt = localtime(&clock);
3771 (void)strftime(timestamp, 20, "%m/%d/%Y %T", lt);
3773 (void)strftime(timestamp, 20, "%m/%d/%Y %H:%M", lt);
3780 char oldSlvgLog[AFSDIR_PATH_MAX];
3782 #ifndef AFS_NT40_ENV
3789 strcpy(oldSlvgLog, AFSDIR_SERVER_SLVGLOG_FILEPATH);
3790 strcat(oldSlvgLog, ".old");
3792 renamefile(AFSDIR_SERVER_SLVGLOG_FILEPATH, oldSlvgLog);
3793 logFile = afs_fopen(AFSDIR_SERVER_SLVGLOG_FILEPATH, "a");
3795 if (!logFile) { /* still nothing, use stdout */
3799 #ifndef AFS_NAMEI_ENV
3800 AFS_DEBUG_IOPS_LOG(logFile);
3805 #ifndef AFS_NT40_ENV
3807 TimeStampLogFile(void)
3809 char stampSlvgLog[AFSDIR_PATH_MAX];
3814 lt = localtime(&now);
3815 (void)afs_snprintf(stampSlvgLog, sizeof stampSlvgLog,
3816 "%s.%04d-%02d-%02d.%02d:%02d:%02d",
3817 AFSDIR_SERVER_SLVGLOG_FILEPATH, lt->tm_year + 1900,
3818 lt->tm_mon + 1, lt->tm_mday, lt->tm_hour, lt->tm_min,
3821 /* try to link the logfile to a timestamped filename */
3822 /* if it fails, oh well, nothing we can do */
3823 link(AFSDIR_SERVER_SLVGLOG_FILEPATH, stampSlvgLog);
3832 #ifndef AFS_NT40_ENV
3834 printf("Can't show log since using syslog.\n");
3843 logFile = afs_fopen(AFSDIR_SERVER_SLVGLOG_FILEPATH, "r");
3846 printf("Can't read %s, exiting\n", AFSDIR_SERVER_SLVGLOG_FILEPATH);
3849 while (fgets(line, sizeof(line), logFile))
3856 Log(const char *format, ...)
3862 va_start(args, format);
3863 (void)afs_vsnprintf(tmp, sizeof tmp, format, args);
3865 #ifndef AFS_NT40_ENV
3867 syslog(LOG_INFO, "%s", tmp);
3871 gettimeofday(&now, 0);
3872 fprintf(logFile, "%s %s", TimeStamp(now.tv_sec, 1), tmp);
3878 Abort(const char *format, ...)
3883 va_start(args, format);
3884 (void)afs_vsnprintf(tmp, sizeof tmp, format, args);
3886 #ifndef AFS_NT40_ENV
3888 syslog(LOG_INFO, "%s", tmp);
3892 fprintf(logFile, "%s", tmp);
3907 p = (char *)malloc(strlen(s) + 1);
3914 /* Remove the FORCESALVAGE file */
3916 RemoveTheForce(char *path)
3918 if (!Testing && ForceSalvage) {
3919 if (chdir(path) == 0)
3920 unlink("FORCESALVAGE");
3924 #ifndef AFS_AIX32_ENV
3926 * UseTheForceLuke - see if we can use the force
3929 UseTheForceLuke(char *path)
3931 struct afs_stat force;
3933 assert(chdir(path) != -1);
3935 return (afs_stat("FORCESALVAGE", &force) == 0);
3939 * UseTheForceLuke - see if we can use the force
3942 * The VRMIX fsck will not muck with the filesystem it is supposedly
3943 * fixing and create a "FORCESAVAGE" file (by design). Instead, we
3944 * muck directly with the root inode, which is within the normal
3946 * ListViceInodes() has a side effect of setting ForceSalvage if
3947 * it detects a need, based on root inode examination.
3950 UseTheForceLuke(char *path)
3953 return 0; /* sorry OB1 */
3958 /* NT support routines */
3960 static char execpathname[MAX_PATH];
3962 nt_SalvagePartition(char *partName, int jobn)
3967 if (!*execpathname) {
3968 n = GetModuleFileName(NULL, execpathname, MAX_PATH - 1);
3969 if (!n || n == 1023)
3972 job.cj_magic = SALVAGER_MAGIC;
3973 job.cj_number = jobn;
3974 (void)strcpy(job.cj_part, partName);
3975 pid = (int)spawnprocveb(execpathname, save_args, NULL, &job, sizeof(job));
3980 nt_SetupPartitionSalvage(void *datap, int len)
3982 childJob_t *jobp = (childJob_t *) datap;
3983 char logname[AFSDIR_PATH_MAX];
3985 if (len != sizeof(childJob_t))
3987 if (jobp->cj_magic != SALVAGER_MAGIC)
3992 (void)sprintf(logname, "%s.%d", AFSDIR_SERVER_SLVGLOG_FILEPATH,
3994 logFile = afs_fopen(logname, "w");
4002 #endif /* AFS_NT40_ENV */