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 extern char *vol_DevName();
223 static char *TimeStamp(time_t clock, int precision);
225 #define ORPH_IGNORE 0
226 #define ORPH_REMOVE 1
227 #define ORPH_ATTACH 2
230 int debug; /* -d flag */
231 int Testing = 0; /* -n flag */
232 int ListInodeOption; /* -i flag */
233 int ShowRootFiles; /* -r flag */
234 int RebuildDirs; /* -sal flag */
235 int Parallel = 4; /* -para X flag */
236 int PartsPerDisk = 8; /* Salvage up to 8 partitions on same disk sequentially */
237 int forceR = 0; /* -b flag */
238 int ShowLog = 0; /* -showlog flag */
239 int ShowSuid = 0; /* -showsuid flag */
240 int ShowMounts = 0; /* -showmounts flag */
241 int orphans = ORPH_IGNORE; /* -orphans option */
245 int useSyslog = 0; /* -syslog flag */
246 int useSyslogFacility = LOG_DAEMON; /* -syslogfacility option */
249 #define MAXPARALLEL 32
251 int OKToZap; /* -o flag */
252 int ForceSalvage; /* If salvage should occur despite the DONT_SALVAGE flag
253 * in the volume header */
255 static FILE *logFile = 0; /* one of {/usr/afs/logs,/vice/file}/SalvageLog */
257 #define ROOTINODE 2 /* Root inode of a 4.2 Unix file system
259 Device fileSysDevice; /* The device number of the current
260 * partition being salvaged */
264 char *fileSysPath; /* The path of the mounted partition currently
265 * being salvaged, i.e. the directory
266 * containing the volume headers */
268 char *fileSysPathName; /* NT needs this to make name pretty in log. */
269 IHandle_t *VGLinkH; /* Link handle for current volume group. */
270 int VGLinkH_cnt; /* # of references to lnk handle. */
271 struct DiskPartition *fileSysPartition; /* Partition being salvaged */
273 char *fileSysDeviceName; /* The block device where the file system
274 * being salvaged was mounted */
275 char *filesysfulldev;
277 int VolumeChanged; /* Set by any routine which would change the volume in
278 * a way which would require callback is to be broken if the
279 * volume was put back on line by an active file server */
281 VolumeDiskData VolInfo; /* A copy of the last good or salvaged volume header dealt with */
283 struct InodeSummary { /* Inode summary file--an entry for each
284 * volume in the inode file for a partition */
285 VolId volumeId; /* Volume id */
286 VolId RWvolumeId; /* RW volume associated */
287 int index; /* index into inode file (0, 1, 2 ...) */
288 int nInodes; /* Number of inodes for this volume */
289 int nSpecialInodes; /* Number of special inodes, i.e. volume
290 * header, index, etc. These are all
291 * marked (viceinode.h) and will all be sorted
292 * to the beginning of the information for
293 * this volume. Read-only volumes should
294 * ONLY have special inodes (all the other
295 * inodes look as if they belong to the
296 * original RW volume). */
297 Unique maxUniquifier; /* The maximum uniquifier found in all the inodes.
298 * This is only useful for RW volumes and is used
299 * to compute a new volume uniquifier in the event
300 * that the header needs to be recreated. The inode
301 * uniquifier may be a truncated version of vnode
302 * uniquifier (AFS_3DISPARES). The real maxUniquifer
303 * is from the vnodes and later calcuated from it */
304 struct VolumeSummary *volSummary;
305 /* Either a pointer to the original volume
306 * header summary, or constructed summary
309 #define readOnly(isp) ((isp)->volumeId != (isp)->RWvolumeId)
310 int nVolumesInInodeFile; /* Number of read-write volumes summarized */
311 int inodeFd; /* File descriptor for inode file */
314 struct VolumeSummary { /* Volume summary an entry for each
315 * volume in a volume directory.
316 * Assumption: one volume directory per
318 char *fileName; /* File name on the partition for the volume
320 struct VolumeHeader header;
321 /* volume number, rw volume number, inode
322 * numbers of each major component of
324 IHandle_t *volumeInfoHandle;
325 byte wouldNeedCallback; /* set if the file server should issue
326 * call backs for all the files in this volume when
327 * the volume goes back on line */
331 IHandle_t *handle; /* Inode containing this index */
332 int nVnodes; /* Total number of vnodes in index */
333 int nAllocatedVnodes; /* Total number actually used */
334 int volumeBlockCount; /* Total number of blocks used by volume */
335 Inode *inodes; /* Directory only */
336 struct VnodeEssence {
337 short count; /* Number of references to vnode; MUST BE SIGNED */
338 unsigned claimed:1; /* Set when a parent directory containing an entry
339 * referencing this vnode is found. The claim
340 * is that the parent in "parent" can point to
341 * this vnode, and no other */
342 unsigned changed:1; /* Set if any parameters (other than the count)
343 * in the vnode change. It is determined if the
344 * link count has changed by noting whether it is
345 * 0 after scanning all directories */
346 unsigned salvaged:1; /* Set if this directory vnode has already been salvaged. */
347 unsigned todelete:1; /* Set if this vnode is to be deleted (should not be claimed) */
348 afs_fsize_t blockCount;
349 /* Number of blocks (1K) used by this vnode,
351 VnodeId parent; /* parent in vnode */
352 Unique unique; /* Must match entry! */
353 char *name; /* Name of directory entry */
354 int modeBits; /* File mode bits */
355 Inode InodeNumber; /* file's inode */
356 int type; /* File type */
357 int author; /* File author */
358 int owner; /* File owner */
359 int group; /* File group */
361 } vnodeInfo[nVNODECLASSES];
364 struct DirHandle dirHandle;
367 unsigned haveDot, haveDotDot;
369 int copied; /* If the copy-on-write stuff has been applied */
377 struct VolumeSummary *volumeSummaryp; /* Holds all the volumes in a part */
378 int nVolumes; /* Number of volumes (read-write and read-only)
379 * in volume summary */
382 /* For NT, we can fork the per partition salvagers to gain the required
383 * safety against Aborts. But there's too many complex data structures at
384 * the per volume salvager layer to easilty copy the data across.
385 * childJobNumber is resset from -1 to the job number if this is a
386 * per partition child of the main salvager. This information is passed
387 * out-of-band in the extra data area setup for the now unused parent/child
390 #define SALVAGER_MAGIC 0x00BBaaDD
391 #define NOT_CHILD -1 /* job numbers start at 0 */
392 /* If new options need to be passed to child, add them here. */
399 /* Child job this process is running. */
400 childJob_t myjob = { SALVAGER_MAGIC, NOT_CHILD };
402 int nt_SalvagePartition(char *partName, int jobn);
403 int nt_SetupPartitionSalvage(void *datap, int len);
406 struct InodeSummary *svgp_inodeSummaryp;
416 /* Forward declarations */
417 /*@printflike@*/ void Log(const char *format, ...);
418 /*@printflike@*/ void Abort(const char *format, ...);
421 int Wait(char *prog);
422 char *ToString(char *s);
423 void AskOffline(VolumeId volumeId);
424 void AskOnline(VolumeId volumeId, char *partition);
425 void CheckLogFile(void);
427 void TimeStampLogFile(void);
429 void ClearROInUseBit(struct VolumeSummary *summary);
430 void CopyAndSalvage(register struct DirSummary *dir);
431 int CopyInode(Device device, Inode inode1, Inode inode2, int rwvolume);
432 void CopyOnWrite(register struct DirSummary *dir);
433 void CountVolumeInodes(register struct ViceInodeInfo *ip, int maxInodes,
434 register struct InodeSummary *summary);
435 void DeleteExtraVolumeHeaderFile(register struct VolumeSummary *vsp);
436 void DistilVnodeEssence(VolumeId vid, VnodeClass class, Inode ino,
438 int GetInodeSummary(char *path, VolumeId singleVolumeNumber);
439 void GetVolumeSummary(VolumeId singleVolumeNumber);
440 void JudgeEntry(struct DirSummary *dir, char *name, VnodeId vnodeNumber,
442 void MaybeZapVolume(register struct InodeSummary *isp, char *message,
443 int deleteMe, int check);
444 void ObtainSalvageLock(void);
445 void PrintInodeList(void);
446 void PrintInodeSummary(void);
447 void PrintVolumeSummary(void);
448 int QuickCheck(register struct InodeSummary *isp, int nVols);
449 void RemoveTheForce(char *path);
450 void SalvageDir(char *name, VolumeId rwVid, struct VnodeInfo *dirVnodeInfo,
451 IHandle_t * alinkH, int i, struct DirSummary *rootdir,
453 void SalvageFileSysParallel(struct DiskPartition *partP);
454 void SalvageFileSys(struct DiskPartition *partP, VolumeId singleVolumeNumber);
455 void SalvageFileSys1(struct DiskPartition *partP,
456 VolumeId singleVolumeNumber);
457 int SalvageHeader(register struct stuff *sp, struct InodeSummary *isp,
458 int check, int *deleteMe);
459 int SalvageIndex(Inode ino, VnodeClass class, int RW,
460 register struct ViceInodeInfo *ip, int nInodes,
461 struct VolumeSummary *volSummary, int check);
462 int SalvageVnodes(register struct InodeSummary *rwIsp,
463 register struct InodeSummary *thisIsp,
464 register struct ViceInodeInfo *inodes, int check);
465 int SalvageVolume(register struct InodeSummary *rwIsp, IHandle_t * alinkH);
466 void DoSalvageVolumeGroup(register struct InodeSummary *isp, int nVols);
468 void SalvageVolumeGroup(register struct InodeSummary *isp, int nVols);
470 #define SalvageVolumeGroup DoSalvageVolumeGroup
472 int SalvageVolumeHeaderFile(register struct InodeSummary *isp,
473 register struct ViceInodeInfo *inodes, int RW,
474 int check, int *deleteMe);
476 int UseTheForceLuke(char *path);
478 static int IsVnodeOrphaned(VnodeId vnode);
480 /* Uniquifier stored in the Inode */
485 return (u & 0x3fffff);
487 #if defined(AFS_SGI_EXMAG)
488 return (u & SGI_UNIQMASK);
491 #endif /* AFS_SGI_EXMAG */
496 BadError(register int aerror)
498 if (aerror == EPERM || aerror == ENXIO || aerror == ENOENT)
500 return 0; /* otherwise may be transient, e.g. EMFILE */
506 handleit(struct cmd_syndesc *as)
508 register struct cmd_item *ti;
509 char pname[100], *temp;
510 afs_int32 seenpart = 0, seenvol = 0, vid = 0, seenany = 0;
511 struct DiskPartition *partP;
513 #ifdef AFS_SGI_VNODE_GLUE
514 if (afs_init_kernel_config(-1) < 0) {
516 ("Can't determine NUMA configuration, not starting salvager.\n");
524 for (i = 0; i < CMD_MAXPARMS; i++) {
525 if (as->parms[i].items) {
533 "Exiting immediately without salvage. Look into the FileLog to find volumes which really need to be salvaged!";
542 #endif /* FAST_RESTART */
543 if ((ti = as->parms[0].items)) { /* -partition */
545 strncpy(pname, ti->data, 100);
547 if ((ti = as->parms[1].items)) { /* -volumeid */
550 ("You must also specify '-partition' option with the '-volumeid' option\n");
554 vid = atoi(ti->data);
556 if (as->parms[2].items) /* -debug */
558 if (as->parms[3].items) /* -nowrite */
560 if (as->parms[4].items) /* -inodes */
562 if (as->parms[5].items) /* -force */
564 if (as->parms[6].items) /* -oktozap */
566 if (as->parms[7].items) /* -rootinodes */
568 if (as->parms[8].items) /* -RebuildDirs */
570 if (as->parms[9].items) /* -ForceReads */
572 if ((ti = as->parms[10].items)) { /* -Parallel # */
574 if (strncmp(temp, "all", 3) == 0) {
578 if (strlen(temp) != 0) {
579 Parallel = atoi(temp);
582 if (Parallel > MAXPARALLEL) {
583 printf("Setting parallel salvages to maximum of %d \n",
585 Parallel = MAXPARALLEL;
589 if ((ti = as->parms[11].items)) { /* -tmpdir */
593 dirp = opendir(tmpdir);
596 ("Can't open temporary placeholder dir %s; using current partition \n",
602 if ((ti = as->parms[12].items)) /* -showlog */
604 if ((ti = as->parms[13].items)) { /* -log */
609 if ((ti = as->parms[14].items)) { /* -showmounts */
614 if ((ti = as->parms[15].items)) { /* -orphans */
616 orphans = ORPH_IGNORE;
617 else if (strcmp(ti->data, "remove") == 0
618 || strcmp(ti->data, "r") == 0)
619 orphans = ORPH_REMOVE;
620 else if (strcmp(ti->data, "attach") == 0
621 || strcmp(ti->data, "a") == 0)
622 orphans = ORPH_ATTACH;
624 #ifndef AFS_NT40_ENV /* ignore options on NT */
625 if ((ti = as->parms[16].items)) { /* -syslog */
629 if ((ti = as->parms[17].items)) { /* -syslogfacility */
630 useSyslogFacility = atoi(ti->data);
633 if ((ti = as->parms[18].items)) { /* -datelogs */
639 if (ti = as->parms[19].items) { /* -DontSalvage */
641 "Exiting immediately without salvage. Look into the FileLog to find volumes which really need to be salvaged!";
649 #endif /* FAST_RESTART */
651 /* Note: if seemvol we initialize this as a standard volume utility: this has the
652 * implication that the file server may be running; negotations have to be made with
653 * the file server in this case to take the read write volume and associated read-only
654 * volumes off line before salvaging */
657 if (afs_winsockInit() < 0) {
658 ReportErrorEventAlt(AFSEVT_SVR_WINSOCK_INIT_FAILED, 0,
659 AFSDIR_SALVAGER_FILE, 0);
660 Log("Failed to initailize winsock, exiting.\n");
665 VInitVolumePackage(seenvol ? volumeUtility : salvager, 5, 5,
669 if (myjob.cj_number != NOT_CHILD) {
672 (void)strcpy(pname, myjob.cj_part);
677 for (partP = DiskPartitionList; partP; partP = partP->next) {
678 SalvageFileSysParallel(partP);
680 SalvageFileSysParallel(0);
682 partP = VGetPartition(pname, 0);
684 Log("salvage: Unknown or unmounted partition %s; salvage aborted\n", pname);
688 SalvageFileSys(partP, 0);
690 /* Salvage individual volume */
692 Log("salvage: invalid volume id specified; salvage aborted\n");
695 SalvageFileSys(partP, vid);
703 #include "AFS_component_version_number.c"
707 char *save_args[MAX_ARGS];
709 pthread_t main_thread;
713 main(int argc, char **argv)
715 struct cmd_syndesc *ts;
717 char commandLine[150];
720 extern char cml_version_number[];
724 * The following signal action for AIX is necessary so that in case of a
725 * crash (i.e. core is generated) we can include the user's data section
726 * in the core dump. Unfortunately, by default, only a partial core is
727 * generated which, in many cases, isn't too useful.
729 struct sigaction nsa;
731 sigemptyset(&nsa.sa_mask);
732 nsa.sa_handler = SIG_DFL;
733 nsa.sa_flags = SA_FULLDUMP;
734 sigaction(SIGABRT, &nsa, NULL);
735 sigaction(SIGSEGV, &nsa, NULL);
738 /* Initialize directory paths */
739 if (!(initAFSDirPath() & AFSDIR_SERVER_PATHS_OK)) {
741 ReportErrorEventAlt(AFSEVT_SVR_NO_INSTALL_DIR, 0, argv[0], 0);
743 fprintf(stderr, "%s: Unable to obtain AFS server directory.\n",
748 main_thread = pthread_self();
749 if (spawnDatap && spawnDataLen) {
750 /* This is a child per partition salvager. Don't setup log or
751 * try to lock the salvager lock.
753 if (nt_SetupPartitionSalvage(spawnDatap, spawnDataLen) < 0)
757 for (commandLine[0] = '\0', i = 0; i < argc; i++) {
759 strcat(commandLine, " ");
760 strcat(commandLine, argv[i]);
763 /* All entries to the log will be appended. Useful if there are
764 * multiple salvagers appending to the log.
769 #ifdef AFS_LINUX20_ENV
770 fcntl(fileno(logFile), F_SETFL, O_APPEND); /* Isn't this redundant? */
772 fcntl(fileno(logFile), F_SETFL, FAPPEND); /* Isn't this redundant? */
778 if (geteuid() != 0) {
779 printf("Salvager must be run as root.\n");
785 /* bad for normal help flag processing, but can do nada */
787 fprintf(logFile, "%s\n", cml_version_number);
788 Log("STARTING AFS SALVAGER %s (%s)\n", SalvageVersion, commandLine);
790 /* Get and hold a lock for the duration of the salvage to make sure
791 * that no other salvage runs at the same time. The routine
792 * VInitVolumePackage (called below) makes sure that a file server or
793 * other volume utilities don't interfere with the salvage.
800 ts = cmd_CreateSyntax("initcmd", handleit, 0, "initialize the program");
801 cmd_AddParm(ts, "-partition", CMD_SINGLE, CMD_OPTIONAL,
802 "Name of partition to salvage");
803 cmd_AddParm(ts, "-volumeid", CMD_SINGLE, CMD_OPTIONAL,
804 "Volume Id to salvage");
805 cmd_AddParm(ts, "-debug", CMD_FLAG, CMD_OPTIONAL,
806 "Run in Debugging mode");
807 cmd_AddParm(ts, "-nowrite", CMD_FLAG, CMD_OPTIONAL,
808 "Run readonly/test mode");
809 cmd_AddParm(ts, "-inodes", CMD_FLAG, CMD_OPTIONAL,
810 "Just list affected afs inodes - debugging flag");
811 cmd_AddParm(ts, "-force", CMD_FLAG, CMD_OPTIONAL, "Force full salvaging");
812 cmd_AddParm(ts, "-oktozap", CMD_FLAG, CMD_OPTIONAL,
813 "Give permission to destroy bogus inodes/volumes - debugging flag");
814 cmd_AddParm(ts, "-rootinodes", CMD_FLAG, CMD_OPTIONAL,
815 "Show inodes owned by root - debugging flag");
816 cmd_AddParm(ts, "-salvagedirs", CMD_FLAG, CMD_OPTIONAL,
817 "Force rebuild/salvage of all directories");
818 cmd_AddParm(ts, "-blockreads", CMD_FLAG, CMD_OPTIONAL,
819 "Read smaller blocks to handle IO/bad blocks");
820 cmd_AddParm(ts, "-parallel", CMD_SINGLE, CMD_OPTIONAL,
821 "# of max parallel partition salvaging");
822 cmd_AddParm(ts, "-tmpdir", CMD_SINGLE, CMD_OPTIONAL,
823 "Name of dir to place tmp files ");
824 cmd_AddParm(ts, "-showlog", CMD_FLAG, CMD_OPTIONAL,
825 "Show log file upon completion");
826 cmd_AddParm(ts, "-showsuid", CMD_FLAG, CMD_OPTIONAL,
827 "Report on suid/sgid files");
828 cmd_AddParm(ts, "-showmounts", CMD_FLAG, CMD_OPTIONAL,
829 "Report on mountpoints");
830 cmd_AddParm(ts, "-orphans", CMD_SINGLE, CMD_OPTIONAL,
831 "ignore | remove | attach");
833 /* note - syslog isn't avail on NT, but if we make it conditional, have
834 * to deal with screwy offsets for cmd params */
835 cmd_AddParm(ts, "-syslog", CMD_FLAG, CMD_OPTIONAL,
836 "Write salvage log to syslogs");
837 cmd_AddParm(ts, "-syslogfacility", CMD_SINGLE, CMD_OPTIONAL,
838 "Syslog facility number to use");
839 cmd_AddParm(ts, "-datelogs", CMD_FLAG, CMD_OPTIONAL,
840 "Include timestamp in logfile filename");
843 cmd_AddParm(ts, "-DontSalvage", CMD_FLAG, CMD_OPTIONAL,
844 "Don't salvage. This my be set in BosConfig to let the fileserver restart immediately after a crash. Bad volumes will be taken offline");
845 #endif /* FAST_RESTART */
846 err = cmd_Dispatch(argc, argv);
850 /* Get the salvage lock if not already held. Hold until process exits. */
852 ObtainSalvageLock(void)
858 (int)CreateFile(AFSDIR_SERVER_SLVGLOCK_FILEPATH, 0, 0, NULL,
859 OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
860 if (salvageLock == (int)INVALID_HANDLE_VALUE) {
862 "salvager: There appears to be another salvager running! Aborted.\n");
867 afs_open(AFSDIR_SERVER_SLVGLOCK_FILEPATH, O_CREAT | O_RDWR, 0666);
868 assert(salvageLock >= 0);
869 #ifdef AFS_DARWIN_ENV
870 if (flock(salvageLock, LOCK_EX) == -1) {
872 if (lockf(salvageLock, F_LOCK, 0) == -1) {
875 "salvager: There appears to be another salvager running! Aborted.\n");
882 #ifdef AFS_SGI_XFS_IOPS_ENV
883 /* Check if the given partition is mounted. For XFS, the root inode is not a
884 * constant. So we check the hard way.
887 IsPartitionMounted(char *part)
890 struct mntent *mntent;
892 assert(mntfp = setmntent(MOUNTED, "r"));
893 while (mntent = getmntent(mntfp)) {
894 if (!strcmp(part, mntent->mnt_dir))
899 return mntent ? 1 : 1;
902 /* Check if the given inode is the root of the filesystem. */
903 #ifndef AFS_SGI_XFS_IOPS_ENV
905 IsRootInode(struct afs_stat *status)
908 * The root inode is not a fixed value in XFS partitions. So we need to
909 * see if the partition is in the list of mounted partitions. This only
910 * affects the SalvageFileSys path, so we check there.
912 return (status->st_ino == ROOTINODE);
917 #ifndef AFS_NAMEI_ENV
918 /* We don't want to salvage big files filesystems, since we can't put volumes on
922 CheckIfBigFilesFS(char *mountPoint, char *devName)
924 struct superblock fs;
927 if (strncmp(devName, "/dev/", 5)) {
928 (void)sprintf(name, "/dev/%s", devName);
930 (void)strcpy(name, devName);
933 if (ReadSuper(&fs, name) < 0) {
934 Log("Unable to read superblock. Not salvaging partition %s.\n",
938 if (IsBigFilesFileSystem(&fs)) {
939 Log("Partition %s is a big files filesystem, not salvaging.\n",
949 #define HDSTR "\\Device\\Harddisk"
950 #define HDLEN (sizeof(HDSTR)-1) /* Length of "\Device\Harddisk" */
952 SameDisk(struct DiskPartition *p1, struct DiskPartition *p2)
957 static int dowarn = 1;
959 if (!QueryDosDevice(p1->devName, res, RES_LEN - 1))
961 if (strncmp(res, HDSTR, HDLEN)) {
964 Log("WARNING: QueryDosDevice is returning %s, not %s for %s\n",
965 res, HDSTR, p1->devName);
969 d1 = atoi(&res[HDLEN]);
971 if (!QueryDosDevice(p2->devName, res, RES_LEN - 1))
973 if (strncmp(res, HDSTR, HDLEN)) {
976 Log("WARNING: QueryDosDevice is returning %s, not %s for %s\n",
977 res, HDSTR, p2->devName);
981 d2 = atoi(&res[HDLEN]);
986 #define SameDisk(P1, P2) ((P1)->device/PartsPerDisk == (P2)->device/PartsPerDisk)
989 /* This assumes that two partitions with the same device number divided by
990 * PartsPerDisk are on the same disk.
993 SalvageFileSysParallel(struct DiskPartition *partP)
996 struct DiskPartition *partP;
997 int pid; /* Pid for this job */
998 int jobnumb; /* Log file job number */
999 struct job *nextjob; /* Next partition on disk to salvage */
1001 static struct job *jobs[MAXPARALLEL] = { 0 }; /* Need to zero this */
1002 struct job *thisjob = 0;
1003 static int numjobs = 0;
1004 static int jobcount = 0;
1010 char logFileName[256];
1014 /* We have a partition to salvage. Copy it into thisjob */
1015 thisjob = (struct job *)malloc(sizeof(struct job));
1017 Log("Can't salvage '%s'. Not enough memory\n", partP->name);
1020 memset(thisjob, 0, sizeof(struct job));
1021 thisjob->partP = partP;
1022 thisjob->jobnumb = jobcount;
1024 } else if (jobcount == 0) {
1025 /* We are asking to wait for all jobs (partp == 0), yet we never
1028 Log("No file system partitions named %s* found; not salvaged\n",
1029 VICE_PARTITION_PREFIX);
1033 if (debug || Parallel == 1) {
1035 SalvageFileSys(thisjob->partP, 0);
1042 /* Check to see if thisjob is for a disk that we are already
1043 * salvaging. If it is, link it in as the next job to do. The
1044 * jobs array has 1 entry per disk being salvages. numjobs is
1045 * the total number of disks currently being salvaged. In
1046 * order to keep thejobs array compact, when a disk is
1047 * completed, the hightest element in the jobs array is moved
1048 * down to now open slot.
1050 for (j = 0; j < numjobs; j++) {
1051 if (SameDisk(jobs[j]->partP, thisjob->partP)) {
1052 /* On same disk, add it to this list and return */
1053 thisjob->nextjob = jobs[j]->nextjob;
1054 jobs[j]->nextjob = thisjob;
1061 /* Loop until we start thisjob or until all existing jobs are finished */
1062 while (thisjob || (!partP && (numjobs > 0))) {
1063 startjob = -1; /* No new job to start */
1065 if ((numjobs >= Parallel) || (!partP && (numjobs > 0))) {
1066 /* Either the max jobs are running or we have to wait for all
1067 * the jobs to finish. In either case, we wait for at least one
1068 * job to finish. When it's done, clean up after it.
1070 pid = wait(&wstatus);
1072 for (j = 0; j < numjobs; j++) { /* Find which job it is */
1073 if (pid == jobs[j]->pid)
1076 assert(j < numjobs);
1077 if (WCOREDUMP(wstatus)) { /* Say if the job core dumped */
1078 Log("Salvage of %s core dumped!\n", jobs[j]->partP->name);
1081 numjobs--; /* job no longer running */
1082 oldjob = jobs[j]; /* remember */
1083 jobs[j] = jobs[j]->nextjob; /* Step to next part on same disk */
1084 free(oldjob); /* free the old job */
1086 /* If there is another partition on the disk to salvage, then
1087 * say we will start it (startjob). If not, then put thisjob there
1088 * and say we will start it.
1090 if (jobs[j]) { /* Another partitions to salvage */
1091 startjob = j; /* Will start it */
1092 } else { /* There is not another partition to salvage */
1094 jobs[j] = thisjob; /* Add thisjob */
1096 startjob = j; /* Will start it */
1098 jobs[j] = jobs[numjobs]; /* Move last job up to this slot */
1099 startjob = -1; /* Don't start it - already running */
1103 /* We don't have to wait for a job to complete */
1105 jobs[numjobs] = thisjob; /* Add this job */
1107 startjob = numjobs; /* Will start it */
1111 /* Start up a new salvage job on a partition in job slot "startjob" */
1112 if (startjob != -1) {
1114 Log("Starting salvage of file system partition %s\n",
1115 jobs[startjob]->partP->name);
1117 /* For NT, we not only fork, but re-exec the salvager. Pass in the
1118 * commands and pass the child job number via the data path.
1121 nt_SalvagePartition(jobs[startjob]->partP->name,
1122 jobs[startjob]->jobnumb);
1123 jobs[startjob]->pid = pid;
1128 jobs[startjob]->pid = pid;
1134 for (fd = 0; fd < 16; fd++)
1139 #ifndef AFS_NT40_ENV
1141 openlog("salvager", LOG_PID, useSyslogFacility);
1145 (void)afs_snprintf(logFileName, sizeof logFileName,
1147 AFSDIR_SERVER_SLVGLOG_FILEPATH,
1148 jobs[startjob]->jobnumb);
1149 logFile = afs_fopen(logFileName, "w");
1154 SalvageFileSys1(jobs[startjob]->partP, 0);
1159 } /* while ( thisjob || (!partP && numjobs > 0) ) */
1161 /* If waited for all jobs to complete, now collect log files and return */
1162 #ifndef AFS_NT40_ENV
1163 if (!useSyslog) /* if syslogging - no need to collect */
1166 for (i = 0; i < jobcount; i++) {
1167 (void)afs_snprintf(logFileName, sizeof logFileName, "%s.%d",
1168 AFSDIR_SERVER_SLVGLOG_FILEPATH, i);
1169 if ((passLog = afs_fopen(logFileName, "r"))) {
1170 while (fgets(buf, sizeof(buf), passLog)) {
1171 fputs(buf, logFile);
1175 (void)unlink(logFileName);
1184 SalvageFileSys(struct DiskPartition *partP, VolumeId singleVolumeNumber)
1186 if (!canfork || debug || Fork() == 0) {
1187 SalvageFileSys1(partP, singleVolumeNumber);
1188 if (canfork && !debug) {
1193 Wait("SalvageFileSys");
1197 get_DevName(char *pbuffer, char *wpath)
1199 char pbuf[128], *ptr;
1200 strcpy(pbuf, pbuffer);
1201 ptr = (char *)strrchr(pbuf, '/');
1204 strcpy(wpath, pbuf);
1207 ptr = (char *)strrchr(pbuffer, '/');
1209 strcpy(pbuffer, ptr + 1);
1216 SalvageFileSys1(struct DiskPartition *partP, VolumeId singleVolumeNumber)
1219 char inodeListPath[256];
1220 static char tmpDevName[100];
1221 static char wpath[100];
1222 struct VolumeSummary *vsp, *esp;
1225 fileSysPartition = partP;
1226 fileSysDevice = fileSysPartition->device;
1227 fileSysPathName = VPartitionPath(fileSysPartition);
1230 /* Opendir can fail on "C:" but not on "C:\" if C is empty! */
1231 (void)sprintf(fileSysPath, "%s\\", fileSysPathName);
1232 name = partP->devName;
1234 fileSysPath = fileSysPathName;
1235 strcpy(tmpDevName, partP->devName);
1236 name = get_DevName(tmpDevName, wpath);
1237 fileSysDeviceName = name;
1238 filesysfulldev = wpath;
1241 VLockPartition(partP->name);
1242 if (singleVolumeNumber || ForceSalvage)
1245 ForceSalvage = UseTheForceLuke(fileSysPath);
1247 if (singleVolumeNumber) {
1248 if (!VConnectFS()) {
1249 Abort("Couldn't connect to file server\n");
1251 AskOffline(singleVolumeNumber);
1254 Log("SALVAGING FILE SYSTEM PARTITION %s (device=%s%s)\n",
1255 partP->name, name, (Testing ? "(READONLY mode)" : ""));
1257 Log("***Forced salvage of all volumes on this partition***\n");
1262 * Remove any leftover /vicepa/salvage.inodes.* or /vicepa/salvage.temp.*
1269 assert((dirp = opendir(fileSysPath)) != NULL);
1270 while ((dp = readdir(dirp))) {
1271 if (!strncmp(dp->d_name, "salvage.inodes.", 15)
1272 || !strncmp(dp->d_name, "salvage.temp.", 13)) {
1274 Log("Removing old salvager temp files %s\n", dp->d_name);
1275 strcpy(npath, fileSysPath);
1277 strcat(npath, dp->d_name);
1283 tdir = (tmpdir ? tmpdir : fileSysPath);
1285 (void)_putenv("TMP="); /* If "TMP" is set, then that overrides tdir. */
1286 (void)strncpy(inodeListPath, _tempnam(tdir, "salvage.inodes."), 255);
1288 snprintf(inodeListPath, 255, "%s/salvage.inodes.%s.%d", tdir, name,
1291 if (GetInodeSummary(inodeListPath, singleVolumeNumber) < 0) {
1292 unlink(inodeListPath);
1296 /* Using nt_unlink here since we're really using the delete on close
1297 * semantics of unlink. In most places in the salvager, we really do
1298 * mean to unlink the file at that point. Those places have been
1299 * modified to actually do that so that the NT crt can be used there.
1302 _open_osfhandle((long)nt_open(inodeListPath, O_RDWR, 0), O_RDWR);
1303 nt_unlink(inodeListPath); /* NT's crt unlink won't if file is open. */
1305 inodeFd = afs_open(inodeListPath, O_RDONLY);
1306 unlink(inodeListPath);
1309 Abort("Temporary file %s is missing...\n", inodeListPath);
1310 if (ListInodeOption) {
1314 /* enumerate volumes in the partition.
1315 * figure out sets of read-only + rw volumes.
1316 * salvage each set, read-only volumes first, then read-write.
1317 * Fix up inodes on last volume in set (whether it is read-write
1320 GetVolumeSummary(singleVolumeNumber);
1322 for (i = j = 0, vsp = volumeSummaryp, esp = vsp + nVolumes;
1323 i < nVolumesInInodeFile; i = j) {
1324 VolumeId rwvid = inodeSummary[i].RWvolumeId;
1326 j < nVolumesInInodeFile && inodeSummary[j].RWvolumeId == rwvid;
1328 VolumeId vid = inodeSummary[j].volumeId;
1329 struct VolumeSummary *tsp;
1330 /* Scan volume list (from partition root directory) looking for the
1331 * current rw volume number in the volume list from the inode scan.
1332 * If there is one here that is not in the inode volume list,
1334 for (; vsp < esp && (vsp->header.parent < rwvid); vsp++) {
1336 DeleteExtraVolumeHeaderFile(vsp);
1338 /* Now match up the volume summary info from the root directory with the
1339 * entry in the volume list obtained from scanning inodes */
1340 inodeSummary[j].volSummary = NULL;
1341 for (tsp = vsp; tsp < esp && (tsp->header.parent == rwvid); tsp++) {
1342 if (tsp->header.id == vid) {
1343 inodeSummary[j].volSummary = tsp;
1349 /* Salvage the group of volumes (several read-only + 1 read/write)
1350 * starting with the current read-only volume we're looking at.
1352 SalvageVolumeGroup(&inodeSummary[i], j - i);
1355 /* Delete any additional volumes that were listed in the partition but which didn't have any corresponding inodes */
1356 for (; vsp < esp; vsp++) {
1358 DeleteExtraVolumeHeaderFile(vsp);
1361 if (!singleVolumeNumber) /* Remove the FORCESALVAGE file */
1362 RemoveTheForce(fileSysPath);
1364 if (!Testing && singleVolumeNumber) {
1365 AskOnline(singleVolumeNumber, fileSysPartition->name);
1367 /* Step through the volumeSummary list and set all volumes on-line.
1368 * The volumes were taken off-line in GetVolumeSummary.
1370 for (j = 0; j < nVolumes; j++) {
1371 AskOnline(volumeSummaryp[j].header.id, fileSysPartition->name);
1375 Log("SALVAGING OF PARTITION %s%s COMPLETED\n",
1376 fileSysPartition->name, (Testing ? " (READONLY mode)" : ""));
1379 close(inodeFd); /* SalvageVolumeGroup was the last which needed it. */
1383 DeleteExtraVolumeHeaderFile(register struct VolumeSummary *vsp)
1386 Log("The volume header file %s is not associated with any actual data (%sdeleted)\n", vsp->fileName, (Testing ? "would have been " : ""));
1388 unlink(vsp->fileName);
1392 CompareInodes(const void *_p1, const void *_p2)
1394 register const struct ViceInodeInfo *p1 = _p1;
1395 register const struct ViceInodeInfo *p2 = _p2;
1396 if (p1->u.vnode.vnodeNumber == INODESPECIAL
1397 || p2->u.vnode.vnodeNumber == INODESPECIAL) {
1398 VolumeId p1rwid, p2rwid;
1400 (p1->u.vnode.vnodeNumber ==
1401 INODESPECIAL ? p1->u.special.parentId : p1->u.vnode.volumeId);
1403 (p2->u.vnode.vnodeNumber ==
1404 INODESPECIAL ? p2->u.special.parentId : p2->u.vnode.volumeId);
1405 if (p1rwid < p2rwid)
1407 if (p1rwid > p2rwid)
1409 if (p1->u.vnode.vnodeNumber == INODESPECIAL
1410 && p2->u.vnode.vnodeNumber == INODESPECIAL) {
1411 if (p1->u.vnode.volumeId == p2->u.vnode.volumeId)
1412 return (p1->u.special.type < p2->u.special.type ? -1 : 1);
1413 if (p1->u.vnode.volumeId == p1rwid)
1415 if (p2->u.vnode.volumeId == p2rwid)
1417 return (p1->u.vnode.volumeId < p2->u.vnode.volumeId ? -1 : 1);
1419 if (p1->u.vnode.vnodeNumber != INODESPECIAL)
1420 return (p2->u.vnode.volumeId == p2rwid ? 1 : -1);
1421 return (p1->u.vnode.volumeId == p1rwid ? -1 : 1);
1423 if (p1->u.vnode.volumeId < p2->u.vnode.volumeId)
1425 if (p1->u.vnode.volumeId > p2->u.vnode.volumeId)
1427 if (p1->u.vnode.vnodeNumber < p2->u.vnode.vnodeNumber)
1429 if (p1->u.vnode.vnodeNumber > p2->u.vnode.vnodeNumber)
1431 /* The following tests are reversed, so that the most desirable
1432 * of several similar inodes comes first */
1433 if (p1->u.vnode.vnodeUniquifier > p2->u.vnode.vnodeUniquifier) {
1434 #ifdef AFS_3DISPARES
1435 if (p1->u.vnode.vnodeUniquifier > 3775414 /* 90% of 4.2M */ &&
1436 p2->u.vnode.vnodeUniquifier < 419490 /* 10% of 4.2M */ )
1439 #ifdef AFS_SGI_EXMAG
1440 if (p1->u.vnode.vnodeUniquifier > 15099494 /* 90% of 16M */ &&
1441 p2->u.vnode.vnodeUniquifier < 1677721 /* 10% of 16M */ )
1446 if (p1->u.vnode.vnodeUniquifier < p2->u.vnode.vnodeUniquifier) {
1447 #ifdef AFS_3DISPARES
1448 if (p2->u.vnode.vnodeUniquifier > 3775414 /* 90% of 4.2M */ &&
1449 p1->u.vnode.vnodeUniquifier < 419490 /* 10% of 4.2M */ )
1452 #ifdef AFS_SGI_EXMAG
1453 if (p2->u.vnode.vnodeUniquifier > 15099494 /* 90% of 16M */ &&
1454 p1->u.vnode.vnodeUniquifier < 1677721 /* 10% of 16M */ )
1459 if (p1->u.vnode.inodeDataVersion > p2->u.vnode.inodeDataVersion) {
1460 #ifdef AFS_3DISPARES
1461 if (p1->u.vnode.inodeDataVersion > 1887437 /* 90% of 2.1M */ &&
1462 p2->u.vnode.inodeDataVersion < 209716 /* 10% of 2.1M */ )
1465 #ifdef AFS_SGI_EXMAG
1466 if (p1->u.vnode.inodeDataVersion > 15099494 /* 90% of 16M */ &&
1467 p2->u.vnode.inodeDataVersion < 1677721 /* 10% of 16M */ )
1472 if (p1->u.vnode.inodeDataVersion < p2->u.vnode.inodeDataVersion) {
1473 #ifdef AFS_3DISPARES
1474 if (p2->u.vnode.inodeDataVersion > 1887437 /* 90% of 2.1M */ &&
1475 p1->u.vnode.inodeDataVersion < 209716 /* 10% of 2.1M */ )
1478 #ifdef AFS_SGI_EXMAG
1479 if (p2->u.vnode.inodeDataVersion > 15099494 /* 90% of 16M */ &&
1480 p1->u.vnode.inodeDataVersion < 1677721 /* 10% of 16M */ )
1489 CountVolumeInodes(register struct ViceInodeInfo *ip, int maxInodes,
1490 register struct InodeSummary *summary)
1492 int volume = ip->u.vnode.volumeId;
1493 int rwvolume = volume;
1494 register n, nSpecial;
1495 register Unique maxunique;
1498 while (maxInodes-- && volume == ip->u.vnode.volumeId) {
1500 if (ip->u.vnode.vnodeNumber == INODESPECIAL) {
1502 rwvolume = ip->u.special.parentId;
1503 /* This isn't quite right, as there could (in error) be different
1504 * parent inodes in different special vnodes */
1506 if (maxunique < ip->u.vnode.vnodeUniquifier)
1507 maxunique = ip->u.vnode.vnodeUniquifier;
1511 summary->volumeId = volume;
1512 summary->RWvolumeId = rwvolume;
1513 summary->nInodes = n;
1514 summary->nSpecialInodes = nSpecial;
1515 summary->maxUniquifier = maxunique;
1519 OnlyOneVolume(struct ViceInodeInfo *inodeinfo, VolumeId singleVolumeNumber)
1521 if (inodeinfo->u.vnode.vnodeNumber == INODESPECIAL)
1522 return (inodeinfo->u.special.parentId == singleVolumeNumber);
1523 return (inodeinfo->u.vnode.volumeId == singleVolumeNumber);
1528 * Collect list of inodes in file named by path. If a truly fatal error,
1529 * unlink the file and abort. For lessor errors, return -1. The file will
1530 * be unlinked by the caller.
1533 GetInodeSummary(char *path, VolumeId singleVolumeNumber)
1535 struct afs_stat status;
1537 struct ViceInodeInfo *ip;
1538 struct InodeSummary summary;
1539 char summaryFileName[50];
1542 char *dev = fileSysPath;
1543 char *wpath = fileSysPath;
1545 char *dev = fileSysDeviceName;
1546 char *wpath = filesysfulldev;
1548 char *part = fileSysPath;
1551 /* This file used to come from vfsck; cobble it up ourselves now... */
1553 ListViceInodes(dev, fileSysPath, path,
1554 singleVolumeNumber ? OnlyOneVolume : 0,
1555 singleVolumeNumber, &forceSal, forceR, wpath)) < 0) {
1557 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);
1561 Abort("Unable to get inodes for \"%s\"; not salvaged\n", dev);
1563 if (forceSal && !ForceSalvage) {
1564 Log("***Forced salvage of all volumes on this partition***\n");
1567 inodeFd = afs_open(path, O_RDWR);
1568 if (inodeFd == -1 || afs_fstat(inodeFd, &status) == -1) {
1570 Abort("No inode description file for \"%s\"; not salvaged\n", dev);
1572 tdir = (tmpdir ? tmpdir : part);
1574 (void)_putenv("TMP="); /* If "TMP" is set, then that overrides tdir. */
1575 (void)strcpy(summaryFileName, _tempnam(tdir, "salvage.temp"));
1577 (void)afs_snprintf(summaryFileName, sizeof summaryFileName,
1578 "%s/salvage.temp.%d", tdir, getpid());
1580 summaryFile = afs_fopen(summaryFileName, "a+");
1581 if (summaryFile == NULL) {
1584 Abort("Unable to create inode summary file\n");
1586 if (!canfork || debug || Fork() == 0) {
1588 nInodes = status.st_size / sizeof(struct ViceInodeInfo);
1590 fclose(summaryFile);
1592 unlink(summaryFileName);
1593 if (!singleVolumeNumber) /* Remove the FORCESALVAGE file */
1594 RemoveTheForce(fileSysPath);
1595 Log("%s vice inodes on %s; not salvaged\n",
1596 singleVolumeNumber ? "No applicable" : "No", dev);
1599 ip = (struct ViceInodeInfo *)malloc(status.st_size);
1601 fclose(summaryFile);
1604 unlink(summaryFileName);
1606 ("Unable to allocate enough space to read inode table; %s not salvaged\n",
1609 if (read(inodeFd, ip, status.st_size) != status.st_size) {
1610 fclose(summaryFile);
1613 unlink(summaryFileName);
1614 Abort("Unable to read inode table; %s not salvaged\n", dev);
1616 qsort(ip, nInodes, sizeof(struct ViceInodeInfo), CompareInodes);
1617 if (afs_lseek(inodeFd, 0, SEEK_SET) == -1
1618 || write(inodeFd, ip, status.st_size) != status.st_size) {
1619 fclose(summaryFile);
1622 unlink(summaryFileName);
1623 Abort("Unable to rewrite inode table; %s not salvaged\n", dev);
1627 CountVolumeInodes(ip, nInodes, &summary);
1628 if (fwrite(&summary, sizeof(summary), 1, summaryFile) != 1) {
1629 Log("Difficulty writing summary file (errno = %d); %s not salvaged\n", errno, dev);
1630 fclose(summaryFile);
1634 summary.index += (summary.nInodes);
1635 nInodes -= summary.nInodes;
1636 ip += summary.nInodes;
1638 /* Following fflush is not fclose, because if it was debug mode would not work */
1639 if (fflush(summaryFile) == EOF || fsync(fileno(summaryFile)) == -1) {
1640 Log("Unable to write summary file (errno = %d); %s not salvaged\n", errno, dev);
1641 fclose(summaryFile);
1645 if (canfork && !debug) {
1650 if (Wait("Inode summary") == -1) {
1651 fclose(summaryFile);
1654 unlink(summaryFileName);
1655 Exit(1); /* salvage of this partition aborted */
1658 assert(afs_fstat(fileno(summaryFile), &status) != -1);
1659 if (status.st_size != 0) {
1661 inodeSummary = (struct InodeSummary *)malloc(status.st_size);
1662 assert(inodeSummary != NULL);
1663 /* For GNU we need to do lseek to get the file pointer moved. */
1664 assert(afs_lseek(fileno(summaryFile), 0, SEEK_SET) == 0);
1665 ret = read(fileno(summaryFile), inodeSummary, status.st_size);
1666 assert(ret == status.st_size);
1668 nVolumesInInodeFile = status.st_size / sizeof(struct InodeSummary);
1669 fclose(summaryFile);
1671 unlink(summaryFileName);
1675 /* Comparison routine for volume sort.
1676 This is setup so that a read-write volume comes immediately before
1677 any read-only clones of that volume */
1679 CompareVolumes(const void *_p1, const void *_p2)
1681 register const struct VolumeSummary *p1 = _p1;
1682 register const struct VolumeSummary *p2 = _p2;
1683 if (p1->header.parent != p2->header.parent)
1684 return p1->header.parent < p2->header.parent ? -1 : 1;
1685 if (p1->header.id == p1->header.parent) /* p1 is rw volume */
1687 if (p2->header.id == p2->header.parent) /* p2 is rw volume */
1689 return p1->header.id < p2->header.id ? -1 : 1; /* Both read-only */
1693 GetVolumeSummary(VolumeId singleVolumeNumber)
1696 afs_int32 nvols = 0;
1697 struct VolumeSummary *vsp, vs;
1698 struct VolumeDiskHeader diskHeader;
1701 /* Get headers from volume directory */
1702 if (chdir(fileSysPath) == -1 || (dirp = opendir(".")) == NULL)
1703 Abort("Can't read directory %s; not salvaged\n", fileSysPath);
1704 if (!singleVolumeNumber) {
1705 while ((dp = readdir(dirp))) {
1706 char *p = dp->d_name;
1707 p = strrchr(dp->d_name, '.');
1708 if (p != NULL && strcmp(p, VHDREXT) == 0) {
1710 if ((fd = afs_open(dp->d_name, O_RDONLY)) != -1
1711 && read(fd, (char *)&diskHeader, sizeof(diskHeader))
1712 == sizeof(diskHeader)
1713 && diskHeader.stamp.magic == VOLUMEHEADERMAGIC) {
1714 DiskToVolumeHeader(&vs.header, &diskHeader);
1722 dirp = opendir("."); /* No rewinddir for NT */
1729 (struct VolumeSummary *)malloc(nvols *
1730 sizeof(struct VolumeSummary));
1733 (struct VolumeSummary *)malloc(20 * sizeof(struct VolumeSummary));
1734 assert(volumeSummaryp != NULL);
1737 vsp = volumeSummaryp;
1738 while ((dp = readdir(dirp))) {
1739 char *p = dp->d_name;
1740 p = strrchr(dp->d_name, '.');
1741 if (p != NULL && strcmp(p, VHDREXT) == 0) {
1744 if ((fd = afs_open(dp->d_name, O_RDONLY)) == -1
1745 || read(fd, &diskHeader, sizeof(diskHeader))
1746 != sizeof(diskHeader)
1747 || diskHeader.stamp.magic != VOLUMEHEADERMAGIC) {
1752 if (!singleVolumeNumber) {
1754 Log("%s/%s is not a legitimate volume header file; %sdeleted\n", fileSysPathName, dp->d_name, (Testing ? "it would have been " : ""));
1759 char nameShouldBe[64];
1760 DiskToVolumeHeader(&vsp->header, &diskHeader);
1761 if (singleVolumeNumber && vsp->header.id == singleVolumeNumber
1762 && vsp->header.parent != singleVolumeNumber) {
1763 Log("%u is a read-only volume; not salvaged\n",
1764 singleVolumeNumber);
1767 if (!singleVolumeNumber
1768 || (vsp->header.id == singleVolumeNumber
1769 || vsp->header.parent == singleVolumeNumber)) {
1770 (void)afs_snprintf(nameShouldBe, sizeof nameShouldBe,
1771 VFORMAT, vsp->header.id);
1772 if (singleVolumeNumber)
1773 AskOffline(vsp->header.id);
1774 if (strcmp(nameShouldBe, dp->d_name)) {
1776 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 " : ""));
1780 vsp->fileName = ToString(dp->d_name);
1790 qsort(volumeSummaryp, nVolumes, sizeof(struct VolumeSummary),
1794 /* Find the link table. This should be associated with the RW volume or, if
1795 * a RO only site, then the RO volume. For now, be cautious and hunt carefully.
1798 FindLinkHandle(register struct InodeSummary *isp, int nVols,
1799 struct ViceInodeInfo *allInodes)
1802 struct ViceInodeInfo *ip;
1804 for (i = 0; i < nVols; i++) {
1805 ip = allInodes + isp[i].index;
1806 for (j = 0; j < isp[i].nSpecialInodes; j++) {
1807 if (ip[j].u.special.type == VI_LINKTABLE)
1808 return ip[j].inodeNumber;
1815 CreateLinkTable(register struct InodeSummary *isp, Inode ino)
1817 struct versionStamp version;
1820 if (!VALID_INO(ino))
1822 IH_CREATE(NULL, fileSysDevice, fileSysPath, 0, isp->volumeId,
1823 INODESPECIAL, VI_LINKTABLE, isp->RWvolumeId);
1824 if (!VALID_INO(ino))
1826 ("Unable to allocate link table inode for volume %u (error = %d)\n",
1827 isp->RWvolumeId, errno);
1828 IH_INIT(VGLinkH, fileSysDevice, isp->RWvolumeId, ino);
1829 fdP = IH_OPEN(VGLinkH);
1831 Abort("Can't open link table for volume %u (error = %d)\n",
1832 isp->RWvolumeId, errno);
1834 if (FDH_TRUNC(fdP, 0) < 0)
1835 Abort("Can't truncate link table for volume %u (error = %d)\n",
1836 isp->RWvolumeId, errno);
1838 version.magic = LINKTABLEMAGIC;
1839 version.version = LINKTABLEVERSION;
1841 if (FDH_WRITE(fdP, (char *)&version, sizeof(version))
1843 Abort("Can't truncate link table for volume %u (error = %d)\n",
1844 isp->RWvolumeId, errno);
1846 FDH_REALLYCLOSE(fdP);
1848 /* If the volume summary exits (i.e., the V*.vol header file exists),
1849 * then set this inode there as well.
1851 if (isp->volSummary)
1852 isp->volSummary->header.linkTable = ino;
1861 SVGParms_t *parms = (SVGParms_t *) arg;
1862 DoSalvageVolumeGroup(parms->svgp_inodeSummaryp, parms->svgp_count);
1867 SalvageVolumeGroup(register struct InodeSummary *isp, int nVols)
1870 pthread_attr_t tattr;
1874 /* Initialize per volume global variables, even if later code does so */
1878 memset(&VolInfo, 0, sizeof(VolInfo));
1880 parms.svgp_inodeSummaryp = isp;
1881 parms.svgp_count = nVols;
1882 code = pthread_attr_init(&tattr);
1884 Log("Failed to salvage volume group %u: pthread_attr_init()\n",
1888 code = pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_JOINABLE);
1890 Log("Failed to salvage volume group %u: pthread_attr_setdetachstate()\n", isp->RWvolumeId);
1893 code = pthread_create(&tid, &tattr, nt_SVG, &parms);
1895 Log("Failed to create thread to salvage volume group %u\n",
1899 (void)pthread_join(tid, NULL);
1901 #endif /* AFS_NT40_ENV */
1904 DoSalvageVolumeGroup(register struct InodeSummary *isp, int nVols)
1906 struct ViceInodeInfo *inodes, *allInodes, *ip;
1907 int i, totalInodes, size, salvageTo;
1911 int dec_VGLinkH = 0;
1913 FdHandle_t *fdP = NULL;
1916 haveRWvolume = (isp->volumeId == isp->RWvolumeId
1917 && isp->nSpecialInodes > 0);
1918 if ((!ShowMounts) || (ShowMounts && !haveRWvolume)) {
1919 if (!ForceSalvage && QuickCheck(isp, nVols))
1922 if (ShowMounts && !haveRWvolume)
1924 if (canfork && !debug && Fork() != 0) {
1925 (void)Wait("Salvage volume group");
1928 for (i = 0, totalInodes = 0; i < nVols; i++)
1929 totalInodes += isp[i].nInodes;
1930 size = totalInodes * sizeof(struct ViceInodeInfo);
1931 inodes = (struct ViceInodeInfo *)malloc(size);
1932 allInodes = inodes - isp->index; /* this would the base of all the inodes
1933 * for the partition, if all the inodes
1934 * had been read into memory */
1936 (inodeFd, isp->index * sizeof(struct ViceInodeInfo),
1938 assert(read(inodeFd, inodes, size) == size);
1940 /* Don't try to salvage a read write volume if there isn't one on this
1942 salvageTo = haveRWvolume ? 0 : 1;
1944 #ifdef AFS_NAMEI_ENV
1945 ino = FindLinkHandle(isp, nVols, allInodes);
1946 if (VALID_INO(ino)) {
1947 IH_INIT(VGLinkH, fileSysDevice, isp->RWvolumeId, ino);
1948 fdP = IH_OPEN(VGLinkH);
1950 if (!VALID_INO(ino) || fdP == NULL) {
1951 Log("%s link table for volume %u.\n",
1952 Testing ? "Would have recreated" : "Recreating", isp->RWvolumeId);
1954 IH_INIT(VGLinkH, fileSysDevice, -1, -1);
1956 CreateLinkTable(isp, ino);
1960 FDH_REALLYCLOSE(fdP);
1962 IH_INIT(VGLinkH, fileSysDevice, -1, -1);
1965 /* Salvage in reverse order--read/write volume last; this way any
1966 * Inodes not referenced by the time we salvage the read/write volume
1967 * can be picked up by the read/write volume */
1968 /* ACTUALLY, that's not done right now--the inodes just vanish */
1969 for (i = nVols - 1; i >= salvageTo; i--) {
1971 struct InodeSummary *lisp = &isp[i];
1972 #ifdef AFS_NAMEI_ENV
1973 /* If only the RO is present on this partition, the link table
1974 * shows up as a RW volume special file. Need to make sure the
1975 * salvager doesn't try to salvage the non-existent RW.
1977 if (rw && nVols > 1 && isp[i].nSpecialInodes == 1) {
1978 /* If this only special inode is the link table, continue */
1979 if (inodes->u.special.type == VI_LINKTABLE) {
1986 Log("%s VOLUME %u%s.\n", rw ? "SALVAGING" : "CHECKING CLONED",
1987 lisp->volumeId, (Testing ? "(READONLY mode)" : ""));
1988 /* Check inodes twice. The second time do things seriously. This
1989 * way the whole RO volume can be deleted, below, if anything goes wrong */
1990 for (check = 1; check >= 0; check--) {
1992 if (SalvageVolumeHeaderFile(lisp, allInodes, rw, check, &deleteMe)
1994 MaybeZapVolume(lisp, "Volume header", deleteMe, check);
1995 if (rw && deleteMe) {
1996 haveRWvolume = 0; /* This will cause its inodes to be deleted--since salvage
1997 * volume won't be called */
2003 if (rw && check == 1)
2005 if (SalvageVnodes(isp, lisp, allInodes, check) == -1) {
2006 MaybeZapVolume(lisp, "Vnode index", 0, check);
2012 /* Fix actual inode counts */
2014 for (ip = inodes; totalInodes; ip++, totalInodes--) {
2015 static int TraceBadLinkCounts = 0;
2016 #ifdef AFS_NAMEI_ENV
2017 if (VGLinkH->ih_ino == ip->inodeNumber) {
2018 dec_VGLinkH = ip->linkCount - VGLinkH_cnt;
2019 VGLinkH_p1 = ip->u.param[0];
2020 continue; /* Deal with this last. */
2023 if (ip->linkCount != 0 && TraceBadLinkCounts) {
2024 TraceBadLinkCounts--; /* Limit reports, per volume */
2025 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]);
2027 while (ip->linkCount > 0) {
2028 /* below used to assert, not break */
2030 if (IH_DEC(VGLinkH, ip->inodeNumber, ip->u.param[0])) {
2031 Log("idec failed. inode %s errno %d\n",
2032 PrintInode(NULL, ip->inodeNumber), errno);
2038 while (ip->linkCount < 0) {
2039 /* these used to be asserts */
2041 if (IH_INC(VGLinkH, ip->inodeNumber, ip->u.param[0])) {
2042 Log("iinc failed. inode %s errno %d\n",
2043 PrintInode(NULL, ip->inodeNumber), errno);
2050 #ifdef AFS_NAMEI_ENV
2051 while (dec_VGLinkH > 0) {
2052 if (IH_DEC(VGLinkH, VGLinkH->ih_ino, VGLinkH_p1) < 0) {
2053 Log("idec failed on link table, errno = %d\n", errno);
2057 while (dec_VGLinkH < 0) {
2058 if (IH_INC(VGLinkH, VGLinkH->ih_ino, VGLinkH_p1) < 0) {
2059 Log("iinc failed on link table, errno = %d\n", errno);
2066 /* Directory consistency checks on the rw volume */
2068 SalvageVolume(isp, VGLinkH);
2069 IH_RELEASE(VGLinkH);
2071 if (canfork && !debug) {
2078 QuickCheck(register struct InodeSummary *isp, int nVols)
2080 /* Check headers BEFORE forking */
2084 for (i = 0; i < nVols; i++) {
2085 struct VolumeSummary *vs = isp[i].volSummary;
2086 VolumeDiskData volHeader;
2088 /* Don't salvage just because phantom rw volume is there... */
2089 /* (If a read-only volume exists, read/write inodes must also exist) */
2090 if (i == 0 && isp->nSpecialInodes == 0 && nVols > 1)
2094 IH_INIT(h, fileSysDevice, vs->header.parent, vs->header.volumeInfo);
2095 if (IH_IREAD(h, 0, (char *)&volHeader, sizeof(volHeader))
2096 == sizeof(volHeader)
2097 && volHeader.stamp.magic == VOLUMEINFOMAGIC
2098 && volHeader.dontSalvage == DONT_SALVAGE
2099 && volHeader.needsSalvaged == 0 && volHeader.destroyMe == 0) {
2100 if (volHeader.inUse == 1) {
2101 volHeader.inUse = 0;
2102 volHeader.inService = 1;
2104 if (IH_IWRITE(h, 0, (char *)&volHeader, sizeof(volHeader))
2105 != sizeof(volHeader)) {
2121 /* SalvageVolumeHeaderFile
2123 * Salvage the top level V*.vol header file. Make sure the special files
2124 * exist and that there are no duplicates.
2126 * Calls SalvageHeader for each possible type of volume special file.
2130 SalvageVolumeHeaderFile(register struct InodeSummary *isp,
2131 register struct ViceInodeInfo *inodes, int RW,
2132 int check, int *deleteMe)
2136 register struct ViceInodeInfo *ip;
2137 int allinodesobsolete = 1;
2138 struct VolumeDiskHeader diskHeader;
2142 memset(&tempHeader, 0, sizeof(tempHeader));
2143 tempHeader.stamp.magic = VOLUMEHEADERMAGIC;
2144 tempHeader.stamp.version = VOLUMEHEADERVERSION;
2145 tempHeader.id = isp->volumeId;
2146 tempHeader.parent = isp->RWvolumeId;
2147 /* Check for duplicates (inodes are sorted by type field) */
2148 for (i = 0; i < isp->nSpecialInodes - 1; i++) {
2149 ip = &inodes[isp->index + i];
2150 if (ip->u.special.type == (ip + 1)->u.special.type) {
2152 Log("Duplicate special inodes in volume header; salvage of volume %u aborted\n", isp->volumeId);
2156 for (i = 0; i < isp->nSpecialInodes; i++) {
2157 ip = &inodes[isp->index + i];
2158 if (ip->u.special.type <= 0 || ip->u.special.type > MAXINODETYPE) {
2160 Log("Rubbish header inode\n");
2163 Log("Rubbish header inode; deleted\n");
2164 } else if (!stuff[ip->u.special.type - 1].obsolete) {
2165 *(stuff[ip->u.special.type - 1].inode) = ip->inodeNumber;
2166 if (!check && ip->u.special.type != VI_LINKTABLE)
2167 ip->linkCount--; /* Keep the inode around */
2168 allinodesobsolete = 0;
2172 if (allinodesobsolete) {
2179 VGLinkH_cnt++; /* one for every header. */
2181 if (!RW && !check && isp->volSummary) {
2182 ClearROInUseBit(isp->volSummary);
2186 for (i = 0; i < MAXINODETYPE; i++) {
2187 if (stuff[i].inodeType == VI_LINKTABLE) {
2188 /* Gross hack: SalvageHeader does a bcmp on the volume header.
2189 * And we may have recreated the link table earlier, so set the
2190 * RW header as well.
2192 if (VALID_INO(VGLinkH->ih_ino)) {
2193 *stuff[i].inode = VGLinkH->ih_ino;
2197 if (SalvageHeader(&stuff[i], isp, check, deleteMe) == -1 && check)
2201 if (isp->volSummary == NULL) {
2203 (void)afs_snprintf(name, sizeof name, VFORMAT, isp->volumeId);
2205 Log("No header file for volume %u\n", isp->volumeId);
2209 Log("No header file for volume %u; %screating %s/%s\n",
2210 isp->volumeId, (Testing ? "it would have been " : ""),
2211 fileSysPathName, name);
2212 headerFd = afs_open(name, O_RDWR | O_CREAT | O_TRUNC, 0644);
2213 assert(headerFd != -1);
2214 isp->volSummary = (struct VolumeSummary *)
2215 malloc(sizeof(struct VolumeSummary));
2216 isp->volSummary->fileName = ToString(name);
2219 /* hack: these two fields are obsolete... */
2220 isp->volSummary->header.volumeAcl = 0;
2221 isp->volSummary->header.volumeMountTable = 0;
2224 (&isp->volSummary->header, &tempHeader,
2225 sizeof(struct VolumeHeader))) {
2226 /* We often remove the name before calling us, so we make a fake one up */
2227 if (isp->volSummary->fileName) {
2228 strcpy(name, isp->volSummary->fileName);
2230 (void)afs_snprintf(name, sizeof name, VFORMAT, isp->volumeId);
2231 isp->volSummary->fileName = ToString(name);
2234 Log("Header file %s is damaged or no longer valid%s\n", name,
2235 (check ? "" : "; repairing"));
2239 headerFd = afs_open(name, O_RDWR | O_TRUNC, 0644);
2240 assert(headerFd != -1);
2244 memcpy(&isp->volSummary->header, &tempHeader,
2245 sizeof(struct VolumeHeader));
2248 Log("It would have written a new header file for volume %u\n",
2251 VolumeHeaderToDisk(&diskHeader, &tempHeader);
2252 if (write(headerFd, &diskHeader, sizeof(struct VolumeDiskHeader))
2253 != sizeof(struct VolumeDiskHeader)) {
2254 Log("Couldn't rewrite volume header file!\n");
2261 IH_INIT(isp->volSummary->volumeInfoHandle, fileSysDevice, isp->RWvolumeId,
2262 isp->volSummary->header.volumeInfo);
2267 SalvageHeader(register struct stuff *sp, struct InodeSummary *isp, int check,
2271 VolumeDiskData volumeInfo;
2272 struct versionStamp fileHeader;
2281 #ifndef AFS_NAMEI_ENV
2282 if (sp->inodeType == VI_LINKTABLE)
2285 if (*(sp->inode) == 0) {
2287 Log("Missing inode in volume header (%s)\n", sp->description);
2291 Log("Missing inode in volume header (%s); %s\n", sp->description,
2292 (Testing ? "it would have recreated it" : "recreating"));
2295 IH_CREATE(NULL, fileSysDevice, fileSysPath, 0, isp->volumeId,
2296 INODESPECIAL, sp->inodeType, isp->RWvolumeId);
2297 if (!VALID_INO(*(sp->inode)))
2299 ("Unable to allocate inode (%s) for volume header (error = %d)\n",
2300 sp->description, errno);
2305 IH_INIT(specH, fileSysDevice, isp->RWvolumeId, *(sp->inode));
2306 fdP = IH_OPEN(specH);
2307 if (OKToZap && (fdP == NULL) && BadError(errno)) {
2308 /* bail out early and destroy the volume */
2310 Log("Still can't open volume header inode (%s), destroying volume\n", sp->description);
2317 Abort("Unable to open inode (%s) of volume header (error = %d)\n",
2318 sp->description, errno);
2321 && (FDH_READ(fdP, (char *)&header, sp->size) != sp->size
2322 || header.fileHeader.magic != sp->stamp.magic)) {
2324 Log("Part of the header (%s) is corrupted\n", sp->description);
2325 FDH_REALLYCLOSE(fdP);
2329 Log("Part of the header (%s) is corrupted; recreating\n",
2333 if (sp->inodeType == VI_VOLINFO
2334 && header.volumeInfo.destroyMe == DESTROY_ME) {
2337 FDH_REALLYCLOSE(fdP);
2341 if (recreate && !Testing) {
2344 ("Internal error: recreating volume header (%s) in check mode\n",
2346 code = FDH_TRUNC(fdP, 0);
2348 Abort("Unable to truncate volume header file (%s) (error = %d)\n",
2349 sp->description, errno);
2351 /* The following code should be moved into vutil.c */
2352 if (sp->inodeType == VI_VOLINFO) {
2354 memset(&header.volumeInfo, 0, sizeof(header.volumeInfo));
2355 header.volumeInfo.stamp = sp->stamp;
2356 header.volumeInfo.id = isp->volumeId;
2357 header.volumeInfo.parentId = isp->RWvolumeId;
2358 sprintf(header.volumeInfo.name, "bogus.%u", isp->volumeId);
2359 Log("Warning: the name of volume %u is now \"bogus.%u\"\n",
2360 isp->volumeId, isp->volumeId);
2361 header.volumeInfo.inService = 0;
2362 header.volumeInfo.blessed = 0;
2363 /* The + 1000 is a hack in case there are any files out in venus caches */
2364 header.volumeInfo.uniquifier = (isp->maxUniquifier + 1) + 1000;
2365 header.volumeInfo.type = (isp->volumeId == isp->RWvolumeId ? readwriteVolume : readonlyVolume); /* XXXX */
2366 header.volumeInfo.needsCallback = 0;
2367 gettimeofday(&tp, 0);
2368 header.volumeInfo.creationDate = tp.tv_sec;
2369 if (FDH_SEEK(fdP, 0, SEEK_SET) < 0) {
2371 ("Unable to seek to beginning of volume header file (%s) (errno = %d)\n",
2372 sp->description, errno);
2375 FDH_WRITE(fdP, (char *)&header.volumeInfo,
2376 sizeof(header.volumeInfo));
2377 if (code != sizeof(header.volumeInfo)) {
2380 ("Unable to write volume header file (%s) (errno = %d)\n",
2381 sp->description, errno);
2382 Abort("Unable to write entire volume header file (%s)\n",
2386 if (FDH_SEEK(fdP, 0, SEEK_SET) < 0) {
2388 ("Unable to seek to beginning of volume header file (%s) (errno = %d)\n",
2389 sp->description, errno);
2391 code = FDH_WRITE(fdP, (char *)&sp->stamp, sizeof(sp->stamp));
2392 if (code != sizeof(sp->stamp)) {
2395 ("Unable to write version stamp in volume header file (%s) (errno = %d)\n",
2396 sp->description, errno);
2398 ("Unable to write entire version stamp in volume header file (%s)\n",
2403 FDH_REALLYCLOSE(fdP);
2405 if (sp->inodeType == VI_VOLINFO) {
2406 VolInfo = header.volumeInfo;
2409 if (VolInfo.updateDate) {
2410 strcpy(update, TimeStamp(VolInfo.updateDate, 0));
2412 Log("%s (%u) %supdated %s\n", VolInfo.name, VolInfo.id,
2413 (Testing ? "it would have been " : ""), update);
2415 strcpy(update, TimeStamp(VolInfo.creationDate, 0));
2417 Log("%s (%u) not updated (created %s)\n", VolInfo.name,
2418 VolInfo.id, update);
2428 SalvageVnodes(register struct InodeSummary *rwIsp,
2429 register struct InodeSummary *thisIsp,
2430 register struct ViceInodeInfo *inodes, int check)
2432 int ilarge, ismall, ioffset, RW, nInodes;
2433 ioffset = rwIsp->index + rwIsp->nSpecialInodes; /* first inode */
2436 RW = (rwIsp == thisIsp);
2437 nInodes = (rwIsp->nInodes - rwIsp->nSpecialInodes);
2439 SalvageIndex(thisIsp->volSummary->header.smallVnodeIndex, vSmall, RW,
2440 &inodes[ioffset], nInodes, thisIsp->volSummary, check);
2441 if (check && ismall == -1)
2444 SalvageIndex(thisIsp->volSummary->header.largeVnodeIndex, vLarge, RW,
2445 &inodes[ioffset], nInodes, thisIsp->volSummary, check);
2446 return (ilarge == 0 && ismall == 0 ? 0 : -1);
2450 SalvageIndex(Inode ino, VnodeClass class, int RW,
2451 register struct ViceInodeInfo *ip, int nInodes,
2452 struct VolumeSummary *volSummary, int check)
2454 VolumeId volumeNumber;
2455 char buf[SIZEOF_LARGEDISKVNODE];
2456 struct VnodeDiskObject *vnode = (struct VnodeDiskObject *)buf;
2458 StreamHandle_t *file;
2459 struct VnodeClassInfo *vcp;
2461 afs_fsize_t vnodeLength;
2462 int vnodeIndex, nVnodes;
2463 afs_ino_str_t stmp1, stmp2;
2467 volumeNumber = volSummary->header.id;
2468 IH_INIT(handle, fileSysDevice, volSummary->header.parent, ino);
2469 fdP = IH_OPEN(handle);
2470 assert(fdP != NULL);
2471 file = FDH_FDOPEN(fdP, "r+");
2472 assert(file != NULL);
2473 vcp = &VnodeClassInfo[class];
2474 size = OS_SIZE(fdP->fd_fd);
2476 nVnodes = (size / vcp->diskSize) - 1;
2478 assert((nVnodes + 1) * vcp->diskSize == size);
2479 assert(STREAM_SEEK(file, vcp->diskSize, 0) == 0);
2483 for (vnodeIndex = 0;
2484 nVnodes && STREAM_READ(vnode, vcp->diskSize, 1, file) == 1;
2485 nVnodes--, vnodeIndex++) {
2486 if (vnode->type != vNull) {
2487 int vnodeChanged = 0;
2488 int vnodeNumber = bitNumberToVnodeNumber(vnodeIndex, class);
2489 /* Log programs that belong to root (potentially suid root);
2490 * don't bother for read-only or backup volumes */
2491 #ifdef notdef /* This is done elsewhere */
2492 if (ShowRootFiles && RW && vnode->owner == 0 && vnodeNumber != 1)
2493 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);
2495 if (VNDISK_GET_INO(vnode) == 0) {
2497 /* Log("### DEBUG ### Deleted Vnode with 0 inode (vnode %d)\n", vnodeNumber); */
2498 memset(vnode, 0, vcp->diskSize);
2502 if (vcp->magic != vnode->vnodeMagic) {
2503 /* bad magic #, probably partially created vnode */
2504 Log("Partially allocated vnode %d deleted.\n",
2506 memset(vnode, 0, vcp->diskSize);
2510 /* ****** Should do a bit more salvage here: e.g. make sure
2511 * vnode type matches what it should be given the index */
2512 while (nInodes && ip->u.vnode.vnodeNumber < vnodeNumber) {
2513 /* if (vnodeIdToClass(ip->u.vnode.vnodeNumber) == class && RW) {
2514 * Log("Inode %d: says it belongs to non-existing vnode %d\n",
2515 * ip->inodeNumber, ip->u.vnode.vnodeNumber);
2522 while (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2523 /* The following doesn't work, because the version number
2524 * is not maintained correctly by the file server */
2525 /*if (vnode->uniquifier == ip->u.vnode.vnodeUniquifier &&
2526 * vnode->dataVersion == ip->u.vnode.inodeDataVersion)
2528 if (VNDISK_GET_INO(vnode) == ip->inodeNumber)
2534 /* For RW volume, look for vnode with matching inode number;
2535 * if no such match, take the first determined by our sort
2537 register struct ViceInodeInfo *lip = ip;
2538 register lnInodes = nInodes;
2540 && lip->u.vnode.vnodeNumber == vnodeNumber) {
2541 if (VNDISK_GET_INO(vnode) == lip->inodeNumber) {
2550 if (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2551 /* "Matching" inode */
2555 vu = vnode->uniquifier;
2556 iu = ip->u.vnode.vnodeUniquifier;
2557 vd = vnode->dataVersion;
2558 id = ip->u.vnode.inodeDataVersion;
2560 * Because of the possibility of the uniquifier overflows (> 4M)
2561 * we compare them modulo the low 22-bits; we shouldn't worry
2562 * about mismatching since they shouldn't to many old
2563 * uniquifiers of the same vnode...
2565 if (IUnique(vu) != IUnique(iu)) {
2567 Log("Vnode %u: vnode.unique, %u, does not match inode unique, %u; fixed, but status will be wrong\n", vnodeNumber, IUnique(vu), IUnique(iu));
2570 vnode->uniquifier = iu;
2571 #ifdef AFS_3DISPARES
2572 vnode->dataVersion = (id >= vd ?
2575 1887437 ? vd : id) :
2578 1887437 ? id : vd));
2580 #if defined(AFS_SGI_EXMAG)
2581 vnode->dataVersion = (id >= vd ?
2584 15099494 ? vd : id) :
2587 15099494 ? id : vd));
2589 vnode->dataVersion = (id > vd ? id : vd);
2590 #endif /* AFS_SGI_EXMAG */
2591 #endif /* AFS_3DISPARES */
2594 /* don't bother checking for vd > id any more, since
2595 * partial file transfers always result in this state,
2596 * and you can't do much else anyway (you've already
2597 * found the best data you can) */
2598 #ifdef AFS_3DISPARES
2599 if (!vnodeIsDirectory(vnodeNumber)
2600 && ((vd < id && (id - vd) < 1887437)
2601 || ((vd > id && (vd - id) > 1887437)))) {
2603 #if defined(AFS_SGI_EXMAG)
2604 if (!vnodeIsDirectory(vnodeNumber)
2605 && ((vd < id && (id - vd) < 15099494)
2606 || ((vd > id && (vd - id) > 15099494)))) {
2608 if (!vnodeIsDirectory(vnodeNumber) && vd < id) {
2609 #endif /* AFS_SGI_EXMAG */
2612 Log("Vnode %d: version < inode version; fixed (old status)\n", vnodeNumber);
2613 vnode->dataVersion = id;
2618 if (ip->inodeNumber != VNDISK_GET_INO(vnode)) {
2621 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);
2623 VNDISK_SET_INO(vnode, ip->inodeNumber);
2628 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);
2630 VNDISK_SET_INO(vnode, ip->inodeNumber);
2633 VNDISK_GET_LEN(vnodeLength, vnode);
2634 if (ip->byteCount != vnodeLength) {
2637 Log("Vnode %d: length incorrect; (is %llu should be %llu)\n", vnodeNumber, (afs_uintmax_t) vnodeLength, (afs_uintmax_t) ip->byteCount);
2642 Log("Vnode %d: length incorrect; changed from %llu to %llu\n", vnodeNumber, (afs_uintmax_t) vnodeLength, (afs_uintmax_t) ip->byteCount);
2643 VNDISK_SET_LEN(vnode, ip->byteCount);
2647 ip->linkCount--; /* Keep the inode around */
2650 } else { /* no matching inode */
2651 if (VNDISK_GET_INO(vnode) != 0
2652 || vnode->type == vDirectory) {
2653 /* No matching inode--get rid of the vnode */
2655 if (VNDISK_GET_INO(vnode)) {
2657 Log("Vnode %d (unique %u): corresponding inode %s is missing\n", vnodeNumber, vnode->uniquifier, PrintInode(NULL, VNDISK_GET_INO(vnode)));
2661 Log("Vnode %d (unique %u): bad directory vnode (no inode number listed)\n", vnodeNumber, vnode->uniquifier);
2666 if (VNDISK_GET_INO(vnode)) {
2668 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)));
2672 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)));
2674 memset(vnode, 0, vcp->diskSize);
2677 /* Should not reach here becuase we checked for
2678 * (inodeNumber == 0) above. And where we zero the vnode,
2679 * we also goto vnodeDone.
2683 while (nInodes && ip->u.vnode.vnodeNumber == vnodeNumber) {
2687 } /* VNDISK_GET_INO(vnode) != 0 */
2689 assert(!(vnodeChanged && check));
2690 if (vnodeChanged && !Testing) {
2692 (handle, vnodeIndexOffset(vcp, vnodeNumber),
2693 (char *)vnode, vcp->diskSize)
2695 VolumeChanged = 1; /* For break call back */
2706 struct VnodeEssence *
2707 CheckVnodeNumber(VnodeId vnodeNumber)
2710 struct VnodeInfo *vip;
2713 class = vnodeIdToClass(vnodeNumber);
2714 vip = &vnodeInfo[class];
2715 offset = vnodeIdToBitNumber(vnodeNumber);
2716 return (offset >= vip->nVnodes ? NULL : &vip->vnodes[offset]);
2720 CopyOnWrite(register struct DirSummary *dir)
2722 /* Copy the directory unconditionally if we are going to change it:
2723 * not just if was cloned.
2725 struct VnodeDiskObject vnode;
2726 struct VnodeClassInfo *vcp = &VnodeClassInfo[vLarge];
2727 Inode oldinode, newinode;
2730 if (dir->copied || Testing)
2732 DFlush(); /* Well justified paranoia... */
2735 IH_IREAD(vnodeInfo[vLarge].handle,
2736 vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2738 assert(code == sizeof(vnode));
2739 oldinode = VNDISK_GET_INO(&vnode);
2740 /* Increment the version number by a whole lot to avoid problems with
2741 * clients that were promised new version numbers--but the file server
2742 * crashed before the versions were written to disk.
2745 IH_CREATE(dir->ds_linkH, fileSysDevice, fileSysPath, 0, dir->rwVid,
2746 dir->vnodeNumber, vnode.uniquifier, vnode.dataVersion +=
2748 assert(VALID_INO(newinode));
2749 assert(CopyInode(fileSysDevice, oldinode, newinode, dir->rwVid) == 0);
2751 VNDISK_SET_INO(&vnode, newinode);
2753 IH_IWRITE(vnodeInfo[vLarge].handle,
2754 vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2756 assert(code == sizeof(vnode));
2758 SetSalvageDirHandle(&dir->dirHandle, dir->dirHandle.dirh_handle->ih_vid,
2759 fileSysDevice, newinode);
2760 /* Don't delete the original inode right away, because the directory is
2761 * still being scanned.
2767 * This function should either successfully create a new dir, or give up
2768 * and leave things the way they were. In particular, if it fails to write
2769 * the new dir properly, it should return w/o changing the reference to the
2773 CopyAndSalvage(register struct DirSummary *dir)
2775 struct VnodeDiskObject vnode;
2776 struct VnodeClassInfo *vcp = &VnodeClassInfo[vLarge];
2777 Inode oldinode, newinode;
2779 register afs_int32 code;
2780 afs_int32 parentUnique = 1;
2781 struct VnodeEssence *vnodeEssence;
2785 Log("Salvaging directory %u...\n", dir->vnodeNumber);
2787 IH_IREAD(vnodeInfo[vLarge].handle,
2788 vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2790 assert(code == sizeof(vnode));
2791 oldinode = VNDISK_GET_INO(&vnode);
2792 /* Increment the version number by a whole lot to avoid problems with
2793 * clients that were promised new version numbers--but the file server
2794 * crashed before the versions were written to disk.
2797 IH_CREATE(dir->ds_linkH, fileSysDevice, fileSysPath, 0, dir->rwVid,
2798 dir->vnodeNumber, vnode.uniquifier, vnode.dataVersion +=
2800 assert(VALID_INO(newinode));
2801 SetSalvageDirHandle(&newdir, dir->rwVid, fileSysDevice, newinode);
2803 /* Assign . and .. vnode numbers from dir and vnode.parent.
2804 * The uniquifier for . is in the vnode.
2805 * The uniquifier for .. might be set to a bogus value of 1 and
2806 * the salvager will later clean it up.
2808 if (vnode.parent && (vnodeEssence = CheckVnodeNumber(vnode.parent))) {
2809 parentUnique = (vnodeEssence->unique ? vnodeEssence->unique : 1);
2812 DirSalvage(&dir->dirHandle, &newdir, dir->vnodeNumber,
2814 (vnode.parent ? vnode.parent : dir->vnodeNumber),
2819 /* didn't really build the new directory properly, let's just give up. */
2820 code = IH_DEC(dir->ds_linkH, newinode, dir->rwVid);
2822 Log("Directory salvage returned code %d, continuing.\n", code);
2825 Log("Checking the results of the directory salvage...\n");
2826 if (!DirOK(&newdir)) {
2827 Log("Directory salvage failed!!!; restoring old version of the directory.\n");
2828 code = IH_DEC(dir->ds_linkH, newinode, dir->rwVid);
2833 VNDISK_SET_INO(&vnode, newinode);
2834 VNDISK_SET_LEN(&vnode, Length(&newdir));
2836 IH_IWRITE(vnodeInfo[vLarge].handle,
2837 vnodeIndexOffset(vcp, dir->vnodeNumber), (char *)&vnode,
2839 assert(code == sizeof(vnode));
2841 nt_sync(fileSysDevice);
2843 sync(); /* this is slow, but hopefully rarely called. We don't have
2844 * an open FD on the file itself to fsync.
2847 code = IH_DEC(dir->ds_linkH, oldinode, dir->rwVid);
2849 dir->dirHandle = newdir;
2853 JudgeEntry(struct DirSummary *dir, char *name, VnodeId vnodeNumber,
2856 struct VnodeEssence *vnodeEssence;
2857 afs_int32 dirOrphaned, todelete;
2859 dirOrphaned = IsVnodeOrphaned(dir->vnodeNumber);
2861 vnodeEssence = CheckVnodeNumber(vnodeNumber);
2862 if (vnodeEssence == NULL) {
2864 Log("dir vnode %u: invalid entry deleted: %s/%s (vnode %u, unique %u)\n", dir->vnodeNumber, dir->name ? dir->name : "??", name, vnodeNumber, unique);
2868 assert(Delete(&dir->dirHandle, name) == 0);
2873 #ifndef AFS_NAMEI_ENV
2874 /* On AIX machines, don't allow entries to point to inode 0. That is a special
2875 * mount inode for the partition. If this inode were deleted, it would crash
2878 if (vnodeEssence->InodeNumber == 0) {
2879 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"));
2882 assert(Delete(&dir->dirHandle, name) == 0);
2889 if (!(vnodeNumber & 1) && !Showmode
2890 && !(vnodeEssence->count || vnodeEssence->unique
2891 || vnodeEssence->modeBits)) {
2892 Log("dir vnode %u: invalid entry: %s/%s (vnode %u, unique %u)%s\n",
2893 dir->vnodeNumber, (dir->name ? dir->name : "??"), name,
2894 vnodeNumber, unique,
2895 ((!unique) ? (Testing ? "-- would have deleted" : " -- deleted") :
2900 assert(Delete(&dir->dirHandle, name) == 0);
2906 /* Check if the Uniquifiers match. If not, change the directory entry
2907 * so its unique matches the vnode unique. Delete if the unique is zero
2908 * or if the directory is orphaned.
2910 if (!vnodeEssence->unique || (vnodeEssence->unique) != unique) {
2911 if (!vnodeEssence->unique
2912 && ((strcmp(name, "..") == 0) || (strcmp(name, ".") == 0))) {
2913 /* This is an orphaned directory. Don't delete the . or ..
2914 * entry. Otherwise, it will get created in the next
2915 * salvage and deleted again here. So Just skip it.
2920 todelete = ((!vnodeEssence->unique || dirOrphaned) ? 1 : 0);
2923 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")));
2927 fid.Vnode = vnodeNumber;
2928 fid.Unique = vnodeEssence->unique;
2930 assert(Delete(&dir->dirHandle, name) == 0);
2932 assert(Create(&dir->dirHandle, name, &fid) == 0);
2935 return; /* no need to continue */
2938 if (strcmp(name, ".") == 0) {
2939 if (dir->vnodeNumber != vnodeNumber || (dir->unique != unique)) {
2942 Log("directory vnode %u.%u: bad '.' entry (was %u.%u); fixed\n", dir->vnodeNumber, dir->unique, vnodeNumber, unique);
2945 assert(Delete(&dir->dirHandle, ".") == 0);
2946 fid.Vnode = dir->vnodeNumber;
2947 fid.Unique = dir->unique;
2948 assert(Create(&dir->dirHandle, ".", &fid) == 0);
2951 vnodeNumber = fid.Vnode; /* Get the new Essence */
2952 unique = fid.Unique;
2953 vnodeEssence = CheckVnodeNumber(vnodeNumber);
2956 } else if (strcmp(name, "..") == 0) {
2959 struct VnodeEssence *dotdot;
2960 pa.Vnode = dir->parent;
2961 dotdot = CheckVnodeNumber(pa.Vnode);
2962 assert(dotdot != NULL); /* XXX Should not be assert */
2963 pa.Unique = dotdot->unique;
2965 pa.Vnode = dir->vnodeNumber;
2966 pa.Unique = dir->unique;
2968 if ((pa.Vnode != vnodeNumber) || (pa.Unique != unique)) {
2970 Log("directory vnode %u.%u: bad '..' entry (was %u.%u); fixed\n", dir->vnodeNumber, dir->unique, vnodeNumber, unique);
2973 assert(Delete(&dir->dirHandle, "..") == 0);
2974 assert(Create(&dir->dirHandle, "..", &pa) == 0);
2977 vnodeNumber = pa.Vnode; /* Get the new Essence */
2979 vnodeEssence = CheckVnodeNumber(vnodeNumber);
2981 dir->haveDotDot = 1;
2982 } else if (strncmp(name, ".__afs", 6) == 0) {
2984 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);
2988 assert(Delete(&dir->dirHandle, name) == 0);
2990 vnodeEssence->claimed = 0; /* Not claimed: Orphaned */
2991 vnodeEssence->todelete = 1; /* Will later delete vnode and decr inode */
2994 if (ShowSuid && (vnodeEssence->modeBits & 06000))
2995 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);
2996 if (ShowMounts && (vnodeEssence->type == vSymlink)
2997 && !(vnodeEssence->modeBits & 0111)) {
3003 IH_INIT(ihP, fileSysDevice, dir->dirHandle.dirh_handle->ih_vid,
3004 vnodeEssence->InodeNumber);
3006 assert(fdP != NULL);
3007 size = FDH_SIZE(fdP);
3009 memset(buf, 0, 1024);
3012 code = FDH_READ(fdP, buf, size);
3013 assert(code == size);
3014 Log("In volume %u (%s) found mountpoint %s/%s to '%s'\n",
3015 dir->dirHandle.dirh_handle->ih_vid, dir->vname,
3016 dir->name ? dir->name : "??", name, buf);
3017 FDH_REALLYCLOSE(fdP);
3020 if (ShowRootFiles && vnodeEssence->owner == 0 && vnodeNumber != 1)
3021 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);
3022 if (vnodeIdToClass(vnodeNumber) == vLarge
3023 && vnodeEssence->name == NULL) {
3025 if ((n = (char *)malloc(strlen(name) + 1)))
3027 vnodeEssence->name = n;
3030 /* The directory entry points to the vnode. Check to see if the
3031 * vnode points back to the directory. If not, then let the
3032 * directory claim it (else it might end up orphaned). Vnodes
3033 * already claimed by another directory are deleted from this
3034 * directory: hardlinks to the same vnode are not allowed
3035 * from different directories.
3037 if (vnodeEssence->parent != dir->vnodeNumber) {
3038 if (!vnodeEssence->claimed && !dirOrphaned) {
3039 /* Vnode does not point back to this directory.
3040 * Orphaned dirs cannot claim a file (it may belong to
3041 * another non-orphaned dir).
3044 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);
3046 vnodeEssence->parent = dir->vnodeNumber;
3047 vnodeEssence->changed = 1;
3049 /* Vnode was claimed by another directory */
3052 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 " : ""));
3054 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 " : ""));
3059 assert(Delete(&dir->dirHandle, name) == 0);
3064 /* This directory claims the vnode */
3065 vnodeEssence->claimed = 1;
3067 vnodeEssence->count--;
3071 DistilVnodeEssence(VolumeId rwVId, VnodeClass class, Inode ino, Unique * maxu)
3073 register struct VnodeInfo *vip = &vnodeInfo[class];
3074 struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
3075 char buf[SIZEOF_LARGEDISKVNODE];
3076 struct VnodeDiskObject *vnode = (struct VnodeDiskObject *)buf;
3078 StreamHandle_t *file;
3083 IH_INIT(vip->handle, fileSysDevice, rwVId, ino);
3084 fdP = IH_OPEN(vip->handle);
3085 assert(fdP != NULL);
3086 file = FDH_FDOPEN(fdP, "r+");
3087 assert(file != NULL);
3088 size = OS_SIZE(fdP->fd_fd);
3090 vip->nVnodes = (size / vcp->diskSize) - 1;
3091 if (vip->nVnodes > 0) {
3092 assert((vip->nVnodes + 1) * vcp->diskSize == size);
3093 assert(STREAM_SEEK(file, vcp->diskSize, 0) == 0);
3094 assert((vip->vnodes = (struct VnodeEssence *)
3095 calloc(vip->nVnodes, sizeof(struct VnodeEssence))) != NULL);
3096 if (class == vLarge) {
3097 assert((vip->inodes = (Inode *)
3098 calloc(vip->nVnodes, sizeof(Inode))) != NULL);
3107 vip->volumeBlockCount = vip->nAllocatedVnodes = 0;
3108 for (vnodeIndex = 0, nVnodes = vip->nVnodes;
3109 nVnodes && STREAM_READ(vnode, vcp->diskSize, 1, file) == 1;
3110 nVnodes--, vnodeIndex++) {
3111 if (vnode->type != vNull) {
3112 register struct VnodeEssence *vep = &vip->vnodes[vnodeIndex];
3113 afs_fsize_t vnodeLength;
3114 vip->nAllocatedVnodes++;
3115 vep->count = vnode->linkCount;
3116 VNDISK_GET_LEN(vnodeLength, vnode);
3117 vep->blockCount = nBlocks(vnodeLength);
3118 vip->volumeBlockCount += vep->blockCount;
3119 vep->parent = vnode->parent;
3120 vep->unique = vnode->uniquifier;
3121 if (*maxu < vnode->uniquifier)
3122 *maxu = vnode->uniquifier;
3123 vep->modeBits = vnode->modeBits;
3124 vep->InodeNumber = VNDISK_GET_INO(vnode);
3125 vep->type = vnode->type;
3126 vep->author = vnode->author;
3127 vep->owner = vnode->owner;
3128 vep->group = vnode->group;
3129 if (vnode->type == vDirectory) {
3130 assert(class == vLarge);
3131 vip->inodes[vnodeIndex] = VNDISK_GET_INO(vnode);
3140 GetDirName(VnodeId vnode, struct VnodeEssence *vp, char *path)
3142 struct VnodeEssence *parentvp;
3148 if (vp->parent && vp->name && (parentvp = CheckVnodeNumber(vp->parent))
3149 && GetDirName(vp->parent, parentvp, path)) {
3151 strcat(path, vp->name);
3157 /* To determine if a vnode is orhpaned or not, the vnode and all its parent
3158 * vnodes must be "claimed". The vep->claimed flag is set in JudgeEntry().
3161 IsVnodeOrphaned(VnodeId vnode)
3163 struct VnodeEssence *vep;
3166 return (1); /* Vnode zero does not exist */
3168 return (0); /* The root dir vnode is always claimed */
3169 vep = CheckVnodeNumber(vnode); /* Get the vnode essence */
3170 if (!vep || !vep->claimed)
3171 return (1); /* Vnode is not claimed - it is orphaned */
3173 return (IsVnodeOrphaned(vep->parent));
3177 SalvageDir(char *name, VolumeId rwVid, struct VnodeInfo *dirVnodeInfo,
3178 IHandle_t * alinkH, int i, struct DirSummary *rootdir,
3181 static struct DirSummary dir;
3182 static struct DirHandle dirHandle;
3183 struct VnodeEssence *parent;
3184 static char path[MAXPATHLEN];
3187 if (dirVnodeInfo->vnodes[i].salvaged)
3188 return; /* already salvaged */
3191 dirVnodeInfo->vnodes[i].salvaged = 1;
3193 if (dirVnodeInfo->inodes[i] == 0)
3194 return; /* Not allocated to a directory */
3196 parent = CheckVnodeNumber(dirVnodeInfo->vnodes[i].parent);
3197 if (parent && parent->salvaged == 0)
3198 SalvageDir(name, rwVid, dirVnodeInfo, alinkH,
3199 vnodeIdToBitNumber(dirVnodeInfo->vnodes[i].parent),
3200 rootdir, rootdirfound);
3201 dir.vnodeNumber = bitNumberToVnodeNumber(i, vLarge);
3202 dir.unique = dirVnodeInfo->vnodes[i].unique;
3205 dir.parent = dirVnodeInfo->vnodes[i].parent;
3206 dir.haveDot = dir.haveDotDot = 0;
3207 dir.ds_linkH = alinkH;
3208 SetSalvageDirHandle(&dir.dirHandle, dir.rwVid, fileSysDevice,
3209 dirVnodeInfo->inodes[i]);
3211 dirok = ((RebuildDirs && !Testing) ? 0 : DirOK(&dir.dirHandle));
3214 Log("Directory bad, vnode %u; %s...\n", dir.vnodeNumber,
3215 (Testing ? "skipping" : "salvaging"));
3218 CopyAndSalvage(&dir);
3222 dirHandle = dir.dirHandle;
3225 GetDirName(bitNumberToVnodeNumber(i, vLarge),
3226 &dirVnodeInfo->vnodes[i], path);
3229 /* If enumeration failed for random reasons, we will probably delete
3230 * too much stuff, so we guard against this instead.
3232 assert(EnumerateDir(&dirHandle, JudgeEntry, &dir) == 0);
3235 /* Delete the old directory if it was copied in order to salvage.
3236 * CopyOnWrite has written the new inode # to the disk, but we still
3237 * have the old one in our local structure here. Thus, we idec the
3241 if (dir.copied && !Testing) {
3242 code = IH_DEC(dir.ds_linkH, dirHandle.dirh_handle->ih_ino, rwVid);
3244 dirVnodeInfo->inodes[i] = dir.dirHandle.dirh_inode;
3247 /* Remember rootdir DirSummary _after_ it has been judged */
3248 if (dir.vnodeNumber == 1 && dir.unique == 1) {
3249 memcpy(rootdir, &dir, sizeof(struct DirSummary));
3257 SalvageVolume(register struct InodeSummary *rwIsp, IHandle_t * alinkH)
3259 /* This routine, for now, will only be called for read-write volumes */
3261 int BlocksInVolume = 0, FilesInVolume = 0;
3262 register VnodeClass class;
3263 struct DirSummary rootdir, oldrootdir;
3264 struct VnodeInfo *dirVnodeInfo;
3265 struct VnodeDiskObject vnode;
3266 VolumeDiskData volHeader;
3268 int orphaned, rootdirfound = 0;
3269 Unique maxunique = 0; /* the maxUniquifier from the vnodes */
3270 afs_int32 ofiles = 0, oblocks = 0; /* Number of orphaned files/blocks */
3271 struct VnodeEssence *vep;
3276 VnodeId LFVnode, ThisVnode;
3277 Unique LFUnique, ThisUnique;
3280 vid = rwIsp->volSummary->header.id;
3281 IH_INIT(h, fileSysDevice, vid, rwIsp->volSummary->header.volumeInfo);
3282 nBytes = IH_IREAD(h, 0, (char *)&volHeader, sizeof(volHeader));
3283 assert(nBytes == sizeof(volHeader));
3284 assert(volHeader.stamp.magic == VOLUMEINFOMAGIC);
3285 assert(volHeader.destroyMe != DESTROY_ME);
3286 /* (should not have gotten this far with DESTROY_ME flag still set!) */
3288 DistilVnodeEssence(vid, vLarge, rwIsp->volSummary->header.largeVnodeIndex,
3290 DistilVnodeEssence(vid, vSmall, rwIsp->volSummary->header.smallVnodeIndex,
3293 dirVnodeInfo = &vnodeInfo[vLarge];
3294 for (i = 0; i < dirVnodeInfo->nVnodes; i++) {
3295 SalvageDir(volHeader.name, vid, dirVnodeInfo, alinkH, i, &rootdir,
3303 /* Parse each vnode looking for orphaned vnodes and
3304 * connect them to the tree as orphaned (if requested).
3306 oldrootdir = rootdir;
3307 for (class = 0; class < nVNODECLASSES; class++) {
3308 for (v = 0; v < vnodeInfo[class].nVnodes; v++) {
3309 vep = &(vnodeInfo[class].vnodes[v]);
3310 ThisVnode = bitNumberToVnodeNumber(v, class);
3311 ThisUnique = vep->unique;
3313 if ((vep->type == 0) || vep->claimed || ThisVnode == 1)
3314 continue; /* Ignore unused, claimed, and root vnodes */
3316 /* This vnode is orphaned. If it is a directory vnode, then the '..'
3317 * entry in this vnode had incremented the parent link count (In
3318 * JudgeEntry()). We need to go to the parent and decrement that
3319 * link count. But if the parent's unique is zero, then the parent
3320 * link count was not incremented in JudgeEntry().
3322 if (class == vLarge) { /* directory vnode */
3323 pv = vnodeIdToBitNumber(vep->parent);
3324 if (vnodeInfo[vLarge].vnodes[pv].unique != 0)
3325 vnodeInfo[vLarge].vnodes[pv].count++;
3329 continue; /* If no rootdir, can't attach orphaned files */
3331 /* Here we attach orphaned files and directories into the
3332 * root directory, LVVnode, making sure link counts stay correct.
3334 if ((orphans == ORPH_ATTACH) && !vep->todelete && !Testing) {
3335 LFVnode = rootdir.vnodeNumber; /* Lost+Found vnode number */
3336 LFUnique = rootdir.unique; /* Lost+Found uniquifier */
3338 /* Update this orphaned vnode's info. Its parent info and
3339 * link count (do for orphaned directories and files).
3341 vep->parent = LFVnode; /* Parent is the root dir */
3342 vep->unique = LFUnique;
3345 vep->count--; /* Inc link count (root dir will pt to it) */
3347 /* If this orphaned vnode is a directory, change '..'.
3348 * The name of the orphaned dir/file is unknown, so we
3349 * build a unique name. No need to CopyOnWrite the directory
3350 * since it is not connected to tree in BK or RO volume and
3351 * won't be visible there.
3353 if (class == vLarge) {
3357 /* Remove and recreate the ".." entry in this orphaned directory */
3358 SetSalvageDirHandle(&dh, vid, fileSysDevice,
3359 vnodeInfo[class].inodes[v]);
3361 pa.Unique = LFUnique;
3362 assert(Delete(&dh, "..") == 0);
3363 assert(Create(&dh, "..", &pa) == 0);
3365 /* The original parent's link count was decremented above.
3366 * Here we increment the new parent's link count.
3368 pv = vnodeIdToBitNumber(LFVnode);
3369 vnodeInfo[vLarge].vnodes[pv].count--;
3373 /* Go to the root dir and add this entry. The link count of the
3374 * root dir was incremented when ".." was created. Try 10 times.
3376 for (j = 0; j < 10; j++) {
3377 pa.Vnode = ThisVnode;
3378 pa.Unique = ThisUnique;
3380 (void)afs_snprintf(npath, sizeof npath, "%s.%u.%u",
3382 vLarge) ? "__ORPHANDIR__" :
3383 "__ORPHANFILE__"), ThisVnode,
3386 CopyOnWrite(&rootdir);
3387 code = Create(&rootdir.dirHandle, npath, &pa);
3391 ThisUnique += 50; /* Try creating a different file */
3394 Log("Attaching orphaned %s to volume's root dir as %s\n",
3395 ((class == vLarge) ? "directory" : "file"), npath);
3397 } /* for each vnode in the class */
3398 } /* for each class of vnode */
3400 /* Delete the old rootinode directory if the rootdir was CopyOnWrite */
3402 if (!oldrootdir.copied && rootdir.copied) {
3404 IH_DEC(oldrootdir.ds_linkH, oldrootdir.dirHandle.dirh_inode,
3407 /* dirVnodeInfo->inodes[?] is not updated with new inode number */
3410 DFlush(); /* Flush the changes */
3411 if (!rootdirfound && (orphans == ORPH_ATTACH)) {
3412 Log("Cannot attach orphaned files and directories: Root directory not found\n");
3413 orphans = ORPH_IGNORE;
3416 /* Write out all changed vnodes. Orphaned files and directories
3417 * will get removed here also (if requested).
3419 for (class = 0; class < nVNODECLASSES; class++) {
3420 int nVnodes = vnodeInfo[class].nVnodes;
3421 struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
3422 struct VnodeEssence *vnodes = vnodeInfo[class].vnodes;
3423 FilesInVolume += vnodeInfo[class].nAllocatedVnodes;
3424 BlocksInVolume += vnodeInfo[class].volumeBlockCount;
3425 for (i = 0; i < nVnodes; i++) {
3426 register struct VnodeEssence *vnp = &vnodes[i];
3427 VnodeId vnodeNumber = bitNumberToVnodeNumber(i, class);
3429 /* If the vnode is good but is unclaimed (not listed in
3430 * any directory entries), then it is orphaned.
3433 if ((vnp->type != 0) && (orphaned = IsVnodeOrphaned(vnodeNumber))) {
3434 vnp->claimed = 0; /* Makes IsVnodeOrphaned calls faster */
3438 if (vnp->changed || vnp->count) {
3442 IH_IREAD(vnodeInfo[class].handle,
3443 vnodeIndexOffset(vcp, vnodeNumber),
3444 (char *)&vnode, sizeof(vnode));
3445 assert(nBytes == sizeof(vnode));
3447 vnode.parent = vnp->parent;
3448 oldCount = vnode.linkCount;
3449 vnode.linkCount = vnode.linkCount - vnp->count;
3452 orphaned = IsVnodeOrphaned(vnodeNumber);
3454 if (!vnp->todelete) {
3455 /* Orphans should have already been attached (if requested) */
3456 assert(orphans != ORPH_ATTACH);
3457 oblocks += vnp->blockCount;
3460 if (((orphans == ORPH_REMOVE) || vnp->todelete)
3462 BlocksInVolume -= vnp->blockCount;
3464 if (VNDISK_GET_INO(&vnode)) {
3466 IH_DEC(alinkH, VNDISK_GET_INO(&vnode), vid);
3469 memset(&vnode, 0, sizeof(vnode));
3471 } else if (vnp->count) {
3473 Log("Vnode %u: link count incorrect (was %d, %s %d)\n", vnodeNumber, oldCount, (Testing ? "would have changed to" : "now"), vnode.linkCount);
3477 vnode.dataVersion++;
3480 IH_IWRITE(vnodeInfo[class].handle,
3481 vnodeIndexOffset(vcp, vnodeNumber),
3482 (char *)&vnode, sizeof(vnode));
3483 assert(nBytes == sizeof(vnode));
3489 if (!Showmode && ofiles) {
3490 Log("%s %d orphaned files and directories (approx. %u KB)\n",
3492 && (orphans == ORPH_REMOVE)) ? "Removed" : "Found", ofiles,
3496 for (class = 0; class < nVNODECLASSES; class++) {
3497 register struct VnodeInfo *vip = &vnodeInfo[class];
3498 for (i = 0; i < vip->nVnodes; i++)
3499 if (vip->vnodes[i].name)
3500 free(vip->vnodes[i].name);
3507 /* Set correct resource utilization statistics */
3508 volHeader.filecount = FilesInVolume;
3509 volHeader.diskused = BlocksInVolume;
3511 /* Make sure the uniquifer is big enough: maxunique is the real maxUniquifier */
3512 if (volHeader.uniquifier < (maxunique + 1)) {
3514 Log("Volume uniquifier is too low; fixed\n");
3515 /* Plus 2,000 in case there are workstations out there with
3516 * cached vnodes that have since been deleted
3518 volHeader.uniquifier = (maxunique + 1 + 2000);
3521 /* Turn off the inUse bit; the volume's been salvaged! */
3522 volHeader.inUse = 0; /* clear flag indicating inUse@last crash */
3523 volHeader.needsSalvaged = 0; /* clear 'damaged' flag */
3524 volHeader.inService = 1; /* allow service again */
3525 volHeader.needsCallback = (VolumeChanged != 0);
3526 volHeader.dontSalvage = DONT_SALVAGE;
3529 nBytes = IH_IWRITE(h, 0, (char *)&volHeader, sizeof(volHeader));
3530 assert(nBytes == sizeof(volHeader));
3533 Log("%sSalvaged %s (%u): %d files, %d blocks\n",
3534 (Testing ? "It would have " : ""), volHeader.name, volHeader.id,
3535 FilesInVolume, BlocksInVolume);
3537 IH_RELEASE(vnodeInfo[vSmall].handle);
3538 IH_RELEASE(vnodeInfo[vLarge].handle);
3544 ClearROInUseBit(struct VolumeSummary *summary)
3546 IHandle_t *h = summary->volumeInfoHandle;
3549 VolumeDiskData volHeader;
3551 nBytes = IH_IREAD(h, 0, (char *)&volHeader, sizeof(volHeader));
3552 assert(nBytes == sizeof(volHeader));
3553 assert(volHeader.stamp.magic == VOLUMEINFOMAGIC);
3554 volHeader.inUse = 0;
3555 volHeader.needsSalvaged = 0;
3556 volHeader.inService = 1;
3557 volHeader.dontSalvage = DONT_SALVAGE;
3559 nBytes = IH_IREAD(h, 0, (char *)&volHeader, sizeof(volHeader));
3560 assert(nBytes == sizeof(volHeader));
3565 * Possible delete the volume.
3567 * deleteMe - Always do so, only a partial volume.
3570 MaybeZapVolume(register struct InodeSummary *isp, char *message, int deleteMe,
3573 if (readOnly(isp) || deleteMe) {
3574 if (isp->volSummary && isp->volSummary->fileName) {
3577 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);
3579 Log("It will be deleted on this server (you may find it elsewhere)\n");
3582 Log("Volume %u needs to be salvaged. Since it is read-only, however,\n", isp->volumeId);
3584 Log("it will be deleted instead. It should be recloned.\n");
3587 unlink(isp->volSummary->fileName);
3589 } else if (!check) {
3590 Log("%s salvage was unsuccessful: read-write volume %u\n", message,
3592 Abort("Salvage of volume %u aborted\n", isp->volumeId);
3598 AskOffline(VolumeId volumeId)
3600 if (FSYNC_askfs(volumeId, NULL, FSYNC_OFF, FSYNC_SALVAGE) == FSYNC_DENIED) {
3601 Log("AskOffline: file server denied offline request; a general salvage is required.\n");
3602 Abort("Salvage aborted\n");
3607 AskOnline(VolumeId volumeId, char *partition)
3609 if (FSYNC_askfs(volumeId, partition, FSYNC_ON, 0) == FSYNC_DENIED) {
3610 Log("AskOnline: file server denied online request to volume %u partition %s\n", volumeId, partition);
3615 CopyInode(Device device, Inode inode1, Inode inode2, int rwvolume)
3617 /* Volume parameter is passed in case iopen is upgraded in future to
3618 * require a volume Id to be passed
3621 IHandle_t *srcH, *destH;
3622 FdHandle_t *srcFdP, *destFdP;
3625 IH_INIT(srcH, device, rwvolume, inode1);
3626 srcFdP = IH_OPEN(srcH);
3627 assert(srcFdP != NULL);
3628 IH_INIT(destH, device, rwvolume, inode2);
3629 destFdP = IH_OPEN(destH);
3631 while ((n = FDH_READ(srcFdP, buf, sizeof(buf))) > 0)
3632 assert(FDH_WRITE(destFdP, buf, n) == n);
3634 FDH_REALLYCLOSE(srcFdP);
3635 FDH_REALLYCLOSE(destFdP);
3642 PrintInodeList(void)
3644 register struct ViceInodeInfo *ip;
3645 struct ViceInodeInfo *buf;
3646 struct afs_stat status;
3649 assert(afs_fstat(inodeFd, &status) == 0);
3650 buf = (struct ViceInodeInfo *)malloc(status.st_size);
3651 assert(buf != NULL);
3652 nInodes = status.st_size / sizeof(struct ViceInodeInfo);
3653 assert(read(inodeFd, buf, status.st_size) == status.st_size);
3654 for (ip = buf; nInodes--; ip++) {
3655 Log("Inode:%s, linkCount=%d, size=%#llx, p=(%u,%u,%u,%u)\n",
3656 PrintInode(NULL, ip->inodeNumber), ip->linkCount,
3657 (afs_uintmax_t) ip->byteCount, ip->u.param[0], ip->u.param[1],
3658 ip->u.param[2], ip->u.param[3]);
3664 PrintInodeSummary(void)
3667 struct InodeSummary *isp;
3669 for (i = 0; i < nVolumesInInodeFile; i++) {
3670 isp = &inodeSummary[i];
3671 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);
3676 PrintVolumeSummary(void)
3679 struct VolumeSummary *vsp;
3681 for (i = 0, vsp = volumeSummaryp; i < nVolumes; vsp++, i++) {
3682 Log("fileName:%s, header, wouldNeedCallback\n", vsp->fileName);
3692 assert(0); /* Fork is never executed in the NT code path */
3707 if (main_thread != pthread_self())
3708 pthread_exit((void *)code);
3721 pid = wait(&status);
3723 if (WCOREDUMP(status))
3724 Log("\"%s\" core dumped!\n", prog);
3725 if (WIFSIGNALED(status) != 0 || WEXITSTATUS(status) != 0)
3731 TimeStamp(time_t clock, int precision)
3734 static char timestamp[20];
3735 lt = localtime(&clock);
3737 (void)strftime(timestamp, 20, "%m/%d/%Y %T", lt);
3739 (void)strftime(timestamp, 20, "%m/%d/%Y %H:%M", lt);
3746 char oldSlvgLog[AFSDIR_PATH_MAX];
3748 #ifndef AFS_NT40_ENV
3755 strcpy(oldSlvgLog, AFSDIR_SERVER_SLVGLOG_FILEPATH);
3756 strcat(oldSlvgLog, ".old");
3758 renamefile(AFSDIR_SERVER_SLVGLOG_FILEPATH, oldSlvgLog);
3759 logFile = afs_fopen(AFSDIR_SERVER_SLVGLOG_FILEPATH, "a");
3761 if (!logFile) { /* still nothing, use stdout */
3765 #ifndef AFS_NAMEI_ENV
3766 AFS_DEBUG_IOPS_LOG(logFile);
3771 #ifndef AFS_NT40_ENV
3773 TimeStampLogFile(void)
3775 char stampSlvgLog[AFSDIR_PATH_MAX];
3780 lt = localtime(&now);
3781 (void)afs_snprintf(stampSlvgLog, sizeof stampSlvgLog,
3782 "%s.%04d-%02d-%02d.%02d:%02d:%02d",
3783 AFSDIR_SERVER_SLVGLOG_FILEPATH, lt->tm_year + 1900,
3784 lt->tm_mon + 1, lt->tm_mday, lt->tm_hour, lt->tm_min,
3787 /* try to link the logfile to a timestamped filename */
3788 /* if it fails, oh well, nothing we can do */
3789 link(AFSDIR_SERVER_SLVGLOG_FILEPATH, stampSlvgLog);
3798 #ifndef AFS_NT40_ENV
3800 printf("Can't show log since using syslog.\n");
3809 logFile = afs_fopen(AFSDIR_SERVER_SLVGLOG_FILEPATH, "r");
3812 printf("Can't read %s, exiting\n", AFSDIR_SERVER_SLVGLOG_FILEPATH);
3815 while (fgets(line, sizeof(line), logFile))
3822 Log(const char *format, ...)
3828 va_start(args, format);
3829 (void)afs_vsnprintf(tmp, sizeof tmp, format, args);
3831 #ifndef AFS_NT40_ENV
3833 syslog(LOG_INFO, "%s", tmp);
3837 gettimeofday(&now, 0);
3838 fprintf(logFile, "%s %s", TimeStamp(now.tv_sec, 1), tmp);
3844 Abort(const char *format, ...)
3849 va_start(args, format);
3850 (void)afs_vsnprintf(tmp, sizeof tmp, format, args);
3852 #ifndef AFS_NT40_ENV
3854 syslog(LOG_INFO, "%s", tmp);
3858 fprintf(logFile, "%s", tmp);
3873 p = (char *)malloc(strlen(s) + 1);
3880 /* Remove the FORCESALVAGE file */
3882 RemoveTheForce(char *path)
3884 if (!Testing && ForceSalvage) {
3885 if (chdir(path) == 0)
3886 unlink("FORCESALVAGE");
3890 #ifndef AFS_AIX32_ENV
3892 * UseTheForceLuke - see if we can use the force
3895 UseTheForceLuke(char *path)
3897 struct afs_stat force;
3899 assert(chdir(path) != -1);
3901 return (afs_stat("FORCESALVAGE", &force) == 0);
3905 * UseTheForceLuke - see if we can use the force
3908 * The VRMIX fsck will not muck with the filesystem it is supposedly
3909 * fixing and create a "FORCESAVAGE" file (by design). Instead, we
3910 * muck directly with the root inode, which is within the normal
3912 * ListViceInodes() has a side effect of setting ForceSalvage if
3913 * it detects a need, based on root inode examination.
3916 UseTheForceLuke(char *path)
3919 return 0; /* sorry OB1 */
3924 /* NT support routines */
3926 static char execpathname[MAX_PATH];
3928 nt_SalvagePartition(char *partName, int jobn)
3933 if (!*execpathname) {
3934 n = GetModuleFileName(NULL, execpathname, MAX_PATH - 1);
3935 if (!n || n == 1023)
3938 job.cj_magic = SALVAGER_MAGIC;
3939 job.cj_number = jobn;
3940 (void)strcpy(job.cj_part, partName);
3941 pid = (int)spawnprocveb(execpathname, save_args, NULL, &job, sizeof(job));
3946 nt_SetupPartitionSalvage(void *datap, int len)
3948 childJob_t *jobp = (childJob_t *) datap;
3949 char logname[AFSDIR_PATH_MAX];
3951 if (len != sizeof(childJob_t))
3953 if (jobp->cj_magic != SALVAGER_MAGIC)
3958 (void)sprintf(logname, "%s.%d", AFSDIR_SERVER_SLVGLOG_FILEPATH,
3960 logFile = afs_fopen(logname, "w");
3968 #endif /* AFS_NT40_ENV */