DAFS: Avoid unnecessary preattach on FSYNC_VOL_ON
[openafs.git] / src / vol / listinodes.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  *
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
8  */
9
10 /*
11
12         System:         VICE-TWO
13         Module:         listinodes.c
14         Institution:    The Information Technology Center, Carnegie-Mellon University
15
16  */
17
18 #define ITC                     /* Required by inode.h */
19
20 #include <afsconfig.h>
21 #include <afs/param.h>
22
23 #include <roken.h>
24
25 #include <ctype.h>
26
27 #ifndef AFS_NAMEI_ENV
28 #if defined(AFS_LINUX20_ENV) || defined(AFS_SUN4_ENV)
29 /* ListViceInodes
30  *
31  * Return codes:
32  * 0 - success
33  * -1 - Unable to read the inodes.
34  * -2 - Unable to completely write temp file. Produces warning message in log.
35  */
36 int
37 ListViceInodes(char *devname, char *mountedOn, FD_t inodeFile,
38                afs_uint32 (*judgeInode) (), afs_uint32 judgeParam, int *forcep, int forceR,
39                char *wpath, void *rock)
40 {
41     Log("ListViceInodes not implemented for this platform!\n");
42     return -1;
43 }
44 #else
45 #if !defined(AFS_SGI_ENV)
46 #ifdef  AFS_OSF_ENV
47 #include <ufs/fs.h>
48 #else /* AFS_OSF_ENV */
49 #ifdef AFS_VFSINCL_ENV
50 #define VFS
51 #ifdef    AFS_SUN5_ENV
52 #include <sys/fs/ufs_fs.h>
53 #else
54 #if defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
55 #include <ufs/ufs/dinode.h>
56 #include <ufs/ffs/fs.h>
57 #define itod ino_to_fsba
58 #else
59 #include <ufs/fs.h>
60 #endif
61 #endif
62 #else /* AFS_VFSINCL_ENV */
63 #ifdef  AFS_AIX_ENV
64 #include <sys/filsys.h>
65 #else
66 #include <sys/fs.h>
67 #endif
68 #endif /* AFS_VFSINCL_ENV */
69 #endif /* AFS_OSF_ENV */
70 #ifdef AFS_VFSINCL_ENV
71 #include <sys/vnode.h>
72 #ifdef    AFS_SUN5_ENV
73 #include <sys/fs/ufs_inode.h>
74 #else
75 #if !defined(AFS_DARWIN_ENV)
76 #include <ufs/inode.h>
77 #endif
78 #endif
79 #else /* AFS_VFSINCL_ENV */
80 #ifdef  AFS_OSF_ENV
81 #include <ufs/inode.h>
82 #else /* AFS_OSF_ENV */
83 #include <sys/inode.h>
84 #endif
85 #endif /* AFS_VFSINCL_ENV */
86 #endif /* AFS_SGI_ENV */
87 #include <afs/osi_inode.h>
88 #include <sys/file.h>
89 #include <rx/xdr.h>
90 #include <afs/afsint.h>
91 #include "nfs.h"
92 #include <afs/afssyscalls.h>
93 #include "viceinode.h"
94 #if defined (AFS_AIX_ENV) || defined (AFS_HPUX_ENV)
95 #include <sys/ino.h>
96 #endif
97
98 #include "lock.h"
99 #include "ihandle.h"
100 #include "vnode.h"
101 #include "volume.h"
102 #include "volinodes.h"
103 #include "partition.h"
104 #include "fssync.h"
105 #include "volume_inline.h"
106
107 /* Notice:  parts of this module have been cribbed from vfsck.c */
108
109 #define ROOTINODE       2
110 static char *partition;
111 int Testing=0;
112 FD_t pfd;
113
114 #ifdef  AFS_AIX32_ENV
115 #include <jfs/filsys.h>
116
117 #ifndef FSBSIZE
118 #define FSBSIZE         (4096)  /* filesystem block size        */
119 #define FSBSHIFT        (12)    /* log2(FSBSIZE)                */
120 #define FSBMASK         (FSBSIZE - 1)   /* FSBSIZE mask                 */
121
122 #define MIN_FSIZE       DISKMAP_B       /* minimum fs size (FSblocks)   */
123 #define LAST_RSVD_I     15      /* last reserved inode          */
124 #endif
125
126 #ifndef INOPB
127 /*
128  * This will hopefully eventually make it into the system include files
129  */
130 #define INOPB           (FSBSIZE / sizeof (struct dinode))
131 #endif
132
133 #ifdef AFS_AIX41_ENV
134 int fragsize;
135 int iagsize;
136 int ag512;
137 int agblocks;
138 #endif /* AFS_AIX41_ENV */
139
140 /*
141  XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
142  XX This was lifted from some `com/cmd/fs/fshlpr_aix3/Fs.h', which indicated X
143  XX a longing to see it make it into a readily accessible include file. XXXXXX
144  XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
145  *
146  * itoo - inode number to offset within disk block
147  */
148 #undef itoo
149 #define itoo(x)         (int) ((unsigned)(x) % INOPB)
150
151 int Bsize = FSBSIZE;            /* block size for this system                   */
152 daddr_t fmax;                   /* total number of blocks n file system         */
153 ino_t imax, inum;               /* total number of I-nodes in file system       */
154
155 static struct superblock fs;
156 struct dinode *ginode();
157
158
159 int
160 ListViceInodes(char *devname, char *mountedOn, FD_t inodeFile,
161                int (*judgeInode) (), afs_uint32 judgeParam, int *forcep, int forceR,
162                char *wpath, void *rock)
163 {
164     char dev[50], rdev[51];
165     struct stat status;
166     struct dinode *p;
167     struct ViceInodeInfo info;
168     struct stat root_inode;
169     int ninodes = 0, err = 0;
170
171     pfd = INVALID_FD;                   /* initialize so we don't close on error output below. */
172     *forcep = 0;
173     sync();
174     sleep(1);                   /* simulate operator    */
175     sync();
176     sleep(1);
177     sync();
178     sleep(1);
179
180     partition = mountedOn;
181     sprintf(dev, "/dev/%s", devname);
182     sprintf(rdev, "/dev/r%s", devname);
183
184     if (stat(mountedOn, &root_inode) < 0) {
185         Log("cannot stat: %s\n", mountedOn);
186         return -1;
187     }
188
189     if (root_inode.st_ino != ROOTDIR_I) {
190         Log("%s is not root of a filesystem\n", mountedOn);
191         return -1;
192     }
193
194
195     /*
196      * done with the superblock, now try to read the raw device.
197      */
198     if (ReadSuper(&fs, dev) < 0)
199         return -1;
200
201     switch (fs.s_fmod) {
202     default:
203     case FM_CLEAN:              /* clean and unmounted                  */
204         Log("Most peculiar - Super blk in FM_CLEAN state!\n");
205         goto out;
206     case FM_MOUNT:              /* mounted cleanly                      */
207         break;
208
209     case FM_MDIRTY:             /* dirty when mounted or commit fail    */
210     case FM_LOGREDO:            /* log redo attempted but failed        */
211         Log("File system %s is in a bad state.\n", rdev);
212         Log("Call your IBM representative.\n");
213         return -1;
214     }
215 #ifdef AFS_AIX42_ENV
216     if (IsBigFilesFileSystem(&fs, (char *)0)) {
217         Log("%s is a big files filesystem, can't salvage.\n", mountedOn);
218         return -1;
219     }
220 #else
221     if (strncmp(fs.s_magic, fsv3magic, strlen(fsv3magic)) != 0) {
222 #ifdef  AFS_AIX41_ENV
223         if ((strncmp(fs.s_magic, fsv3pmagic, strlen(fsv3pmagic)) != 0)
224             || (fs.s_version != fsv3pvers)) {
225             Log("Super block doesn't have the problem magic (%s vs v3magic %s v3pmagic %s)\n", fs.s_magic, fsv3magic, fsv3pmagic);
226             return -1;
227         }
228 #else
229         Log("Super block doesn't have the problem magic (%s vs v3magic %s)\n",
230             fs.s_magic, fsv3magic);
231         return -1;
232 #endif
233     }
234 #endif
235
236 #ifdef AFS_AIX41_ENV
237     fragsize = (fs.s_fragsize) ? fs.s_fragsize : FSBSIZE;
238     iagsize = (fs.s_iagsize) ? fs.s_iagsize : fs.s_agsize;
239     ag512 = fragsize * fs.s_agsize / 512;
240     agblocks = fragsize * fs.s_agsize >> BSHIFT;
241 #endif /* AFS_AIX41_ENV */
242
243     fmax = fs.s_fsize / (FSBSIZE / 512);        /* first invalid blk num */
244
245     pfd = OS_OPEN(rdev, O_RDONLY, 0666);
246     if (pfd == INVALID_FD) {
247         Log("Unable to open `%s' inode for reading\n", rdev);
248         return -1;
249     }
250
251     /*
252      * calculate the maximum number of inodes possible
253      */
254 #ifdef AFS_AIX41_ENV
255     imax = iagsize * (fs.s_fsize / ag512) - 1;
256 #else /* AFS_AIX41_ENV */
257     imax =
258         ((fmax / fs.s_agsize +
259           ((fmax % fs.s_agsize) >= fs.s_agsize / INOPB ? 1 : 0))
260          * fs.s_agsize) - 1;
261 #endif /* AFS_AIX41_ENV */
262
263     /*
264      * check for "FORCESALVAGE" equivalent:
265      *      LAST_RSVD_I is a vice inode, with dead beef, and
266      *      di_nlink == 2 to indicate the FORCE.
267      */
268     osi_Assert(p = ginode(LAST_RSVD_I));
269
270     if (p->di_vicemagic == VICEMAGIC && p->di_vicep1 == 0xdeadbeef
271         && p->di_nlink == 2) {
272         *forcep = 1;
273         idec(root_inode.st_dev, LAST_RSVD_I, 0xdeadbeef);
274     }
275
276     for (inum = LAST_RSVD_I + 1; inum <= imax; ++inum) {
277         if ((p = ginode(inum)) == NULL || p->di_vicemagic != VICEMAGIC
278             || (p->di_mode & IFMT) != IFREG)
279             continue;
280
281         info.inodeNumber = inum;
282         info.byteCount = p->di_size;
283         info.linkCount = p->di_nlink;
284         info.u.param[0] = p->di_vicep1;
285         info.u.param[1] = p->di_vicep2;
286         info.u.param[2] = p->di_vicep3;
287         info.u.param[3] = p->di_vicep4;
288
289         if (judgeInode && (*judgeInode) (&info, judgeParam, rock) == 0)
290             continue;
291
292         if (inodeFile != INVALID_FD) {
293             if (OS_WRITE(inodeFile, &info, sizeof(info)) != sizeof(info)) {
294                 Log("Error writing inode file for partition %s\n", partition);
295                 goto out;
296             }
297         }
298         ++ninodes;
299     }
300
301     if (inodeFile != INVALID_FD) {
302         if (OS_SYNC(inodeFile) == -1) {
303             Log("Unable to successfully fsync inode file for %s\n", partition);
304             err = -2;
305             goto out1;
306         }
307
308         /*
309          * Paranoia:  check that the file is really the right size
310          */
311         if (OS_SIZE(inodeFile) != ninodes * sizeof(struct ViceInodeInfo)) {
312             Log("Wrong size (%d instead of %d) in inode file for %s\n",
313                 status.st_size, ninodes * sizeof(struct ViceInodeInfo),
314                 partition);
315             err = -2;
316             goto out1;
317         }
318     }
319     OS_CLOSE(pfd);
320     return 0;
321
322   out:
323     err = -1;
324   out1:
325     if (pfd != INVALID_FD)
326         OS_CLOSE(pfd);
327
328     return err;
329 }
330
331 /* Read in the superblock for devName */
332 int
333 ReadSuper(struct superblock *fs, char *devName)
334 {
335     FD_t pfd;
336
337     pfd = OS_OPEN(devName, O_RDONLY, 0666);
338     if (pfd == INVALID_FD) {
339         Log("Unable to open inode on %s for reading superblock.\n", devName);
340         return -1;
341     }
342
343     if (bread(pfd, fs, SUPER_B, sizeof(struct superblock)) < 0) {
344         Log("Unable to read superblock on %s.\n", devName);
345         return -1;
346     }
347     OS_CLOSE(pfd);
348     return (0);
349 }
350
351 #ifdef AFS_AIX42_ENV
352 /* IsBigFilesFileSystem returns 1 if it's a big files filesystem, 0 otherwise. */
353 int
354 IsBigFilesFileSystem(struct superblock *sb)
355 {
356     if ((strncmp(sb->s_magic, fsv3pmagic, 4) == 0)
357         && (sb->s_version == fsbigfile)
358         && (sb->s_bigexp))
359         return 1;
360     else
361         return 0;
362 }
363 #endif
364
365 struct dinode *
366 ginode(inum)
367 {
368     int ag;
369     daddr_t pblk;
370     struct dinode *dp;
371     static char buf[FSBSIZE];
372     static daddr_t last_blk = -1;
373
374 #ifdef AFS_AIX41_ENV
375     ag = inum / iagsize;
376     pblk =
377         (ag ==
378          0) ? INODES_B + inum / INOPB : ag * agblocks + (inum -
379                                                          ag * iagsize) /
380         INOPB;
381 #else /* AFS_AIX41_ENV */
382     ag = inum / fs.s_agsize;
383     pblk =
384         (ag ==
385          0) ? INODES_B + inum / INOPB : ag * fs.s_agsize + (inum -
386                                                             ag *
387                                                             fs.s_agsize) /
388         INOPB;
389 #endif /* AFS_AIX41_ENV */
390
391     if (last_blk != pblk) {
392         if (bread(pfd, buf, pblk, sizeof(buf)) < 0) {
393             last_blk = -1;
394             return 0;
395         }
396         last_blk = pblk;
397     }
398
399     dp = (struct dinode *)buf;
400     dp += itoo(inum);
401     return (dp);
402 }
403
404 #else /* !AFS_AIX31_ENV       */
405
406 #if defined(AFS_SGI_ENV)
407
408 /* libefs.h includes <assert.h>, which we don't want */
409 #define __ASSERT_H__
410
411 #ifdef AFS_SGI_XFS_IOPS_ENV
412 #include <dirent.h>
413 #include <afs/xfsattrs.h>
414 /* xfs_ListViceInodes
415  *
416  * xfs_ListViceInodes verifies and correct the XFS namespace as it collects
417  * the inode information. The name is required for the idec operation to work.
418  * Steps 2 and 3 below are for the AFS_XFS_NAME_VERS == 1. If the name space
419  * changes, the algorithm will need to change.
420  * 1) If the parent inode number does not match the directory's inod number,
421  *    change it in the attribute.
422  * 2) If the unqifier in the attribute does not match the name, rename the
423  *    file. This is done by doing an exclusive open, incrementing the tag
424  *    number until a file can be created. If the tag changes, then the
425  *    attribute will need updating.
426  * 3) If the tag in the attribute does not match the name, change the
427  *    attribute.
428  * 4) Verify uid = RW volume id and gid = XFS_VICEMAGIC.
429  *
430  */
431
432 /* xfs_VerifyInode
433  *
434  * Does the verifications listed above.
435  * We can't change the names until the readdir is complete, so we set the
436  * rename flag if the file needs renaming.
437  */
438 int
439 xfs_VerifyInode(char *dir, uint64_t pino, char *name, i_list_inode_t * info,
440                 int *rename)
441 {
442     char path[1024];
443     int vno;
444     int update_pino = 0;
445     int update_tag = 0;
446     int update_chown = 0;
447     int retCode = 0;
448     char tmpName[32];
449     b64_string_t stmp;
450     int tag;
451     afs_ino_str_t stmp;
452
453     *rename = 0;
454     (void)sprintf(path, "%s/%s", dir, name);
455     /* Verify uid and gid fields */
456     if (info->ili_magic != XFS_VICEMAGIC) {
457         Log("%s  magic for %s/%s (inode %s) from %d to %d\n",
458             Testing ? "Would have changed" : "Changing", dir, name,
459             PrintInode(stmp, info->ili_info.inodeNumber), info->ili_magic,
460             XFS_VICEMAGIC);
461         if (!Testing)
462             update_chown = 1;
463     }
464
465     vno = info->ili_info.param[0];
466     if (info->ili_vno != AFS_XFS_VNO_CLIP(vno)) {
467         Log("%s volume id for %s/%s (inode %s) from %d to %d\n",
468             Testing ? "Would have changed" : "Changing", dir, name,
469             PrintInode(stmp, info->ili_info.inodeNumber), info->ili_vno,
470             AFS_XFS_VNO_CLIP(vno));
471         if (!Testing)
472             update_chown = 1;
473     }
474
475     if (update_chown) {
476         if (chown(path, AFS_XFS_VNO_CLIP(vno), XFS_VICEMAGIC) < 0) {
477             Log("Can't chown %s to uid=%d, gid=0x%x\n", path,
478                 AFS_XFS_VNO_CLIP(vno), XFS_VICEMAGIC);
479             retCode = -1;
480         }
481     }
482
483     /* Check Parent inode number. */
484     if (info->ili_pino != pino) {
485         afs_ino_str_t sino, sipino, spino;
486         (void)PrintInode(sino, info->ili_info.inodeNumber);
487         (void)PrintInode(sipino, info->ili_pino);
488         (void)PrintInode(spino, pino);
489         Log("%s parent ino for %s (inode %s) from %s to %s.\n",
490             Testing ? "Would have changed" : "Changing", path, sino, sipino,
491             spino);
492         if (!Testing)
493             update_pino = 1;
494     }
495
496     /* Verify the file name. */
497     (void)strcpy(tmpName, ".");
498     (void)strcat(tmpName, int_to_base64(stmp, info->ili_info.param[2]));
499     if (strncmp(name, tmpName, strlen(tmpName))) {
500         Log("%s name %s (inode %s) in directory %s, unique=%d, tag=%d\n",
501             Testing ? "Would have returned bad" : "Bad", name,
502             PrintInode(stmp, info->ili_info.inodeNumber), dir,
503             info->ili_info.param[2], info->ili_tag);
504         if (!Testing)
505             *rename = 1;
506     }
507
508     if (!*rename) {
509         /* update the tag? */
510         (void)strcat(tmpName, ".");
511         (void)strcat(tmpName, int_to_base64(stmp, info->ili_tag));
512         if (strcmp(name, tmpName)) {
513             char *p;
514             (void)strcpy(tmpName, name);
515             p = strchr(tmpName + 1, '.');
516             if (!p) {
517                 Log("No tag found on name %s (inode %s)in directory, %s.\n",
518                     name, PrintInode(stmp, info->ili_info.inodeNumber), dir,
519                     Testing ? "would have renamed" : "will rename");
520                 if (!Testing)
521                     *rename = 1;
522             } else {
523                 tag = base64_to_int(p + 1);
524                 Log("%s the tag for %s (inode %s) from %d to %d.\n",
525                     Testing ? "Would have changed" : "Will change", path,
526                     PrintInode(stmp, info->ili_info.inodeNumber), dir, tag,
527                     info->ili_tag);
528                 if (!Testing)
529                     update_tag = 1;
530             }
531         }
532     }
533
534     if (update_pino || update_tag) {
535         afs_xfs_attr_t attrs;
536         int length;
537
538         length = SIZEOF_XFS_ATTR_T;
539         if (attr_get(path, AFS_XFS_ATTR, (char *)&attrs, &length, ATTR_ROOT) <
540             0) {
541             Log("Can't get AFS attribute for %s\n", path);
542             return -1;
543         }
544         if (update_pino)
545             attrs.at_pino = pino;
546         if (update_tag)
547             attrs.at_tag = tag;
548         if (attr_set
549             (path, AFS_XFS_ATTR, (char *)&attrs, length,
550              ATTR_ROOT | ATTR_REPLACE) < 0) {
551             Log("Can't set AFS attribute into %s\n", path);
552             retCode = -1;
553         }
554     }
555
556     return retCode;
557 }
558
559 typedef struct {
560     int uniq;
561     char name[28];
562 } xfs_Rename_t;
563
564 int
565 xfs_RenameFiles(char *dir, xfs_Rename_t * renames, int n_renames)
566 {
567     int i, j;
568     char opath[128], nbase[128], npath[128];
569     afs_xfs_attr_t attrs;
570     int length = SIZEOF_XFS_ATTR_T;
571     b64_string_t stmp;
572     int tag;
573     int fd;
574
575     for (i = 0; i < n_renames; i++) {
576         (void)sprintf(opath, "%s/%s", dir, renames[i].name);
577         (void)sprintf(nbase, "%s/.%s", dir,
578                       int_to_base64(stmp, renames[i].uniq));
579         for (tag = 2, j = 0; j < 64; tag++, j++) {
580             (void)sprintf(npath, "%s.%s", nbase, int_to_base64(stmp, tag));
581             fd = afs_open(npath, O_CREAT | O_EXCL | O_RDWR, 0);
582             if (fd > 0) {
583                 close(fd);
584                 break;
585             }
586         }
587         if (j != 64) {
588             Log("Can't find a new name for %s\n", opath);
589             return -1;
590         }
591         if (rename(opath, npath) < 0) {
592             Log("Can't rename %s to %s\n", opath, npath);
593             return -1;
594         }
595         Log("Renamed %s to %s\n", opath, npath);
596         return 0;
597     }
598 }
599
600
601 int
602 xfs_ListViceInodes(char *devname, char *mountedOn, FD_t inodeFile,
603                    int (*judgeInode) (), afs_uint32 judgeParam, int *forcep,
604                    int forceR, char *wpath, void *rock)
605 {
606     i_list_inode_t info;
607     int info_size = sizeof(i_list_inode_t);
608     int fd;
609     DIR *top_dirp;
610     dirent64_t *top_direntp;
611     DIR *vol_dirp;
612     dirent64_t *vol_direntp;
613     struct stat64 sdirbuf;
614     struct stat64 sfilebuf;
615     afs_xfs_attr_t attrs;
616     afs_xfs_dattr_t dattrs;
617     int length;
618     char vol_dirname[1024];
619     int ninodes = 0;
620     int code = 0;
621     xfs_Rename_t *renames = (xfs_Rename_t *) 0;
622     int rename;
623 #define N_RENAME_STEP 64
624     int n_renames = 0;
625     int n_avail = 0;
626     uint64_t pino;
627     int errors = 0;
628
629     *forcep = 0;
630
631     if (stat64(mountedOn, &sdirbuf) < 0) {
632         perror("xfs_ListViceInodes: stat64");
633         return -1;
634     }
635
636     if ((top_dirp = opendir(mountedOn)) == NULL) {
637         Log("Can't open directory %s to read inodes.\n", mountedOn);
638         return -1;
639     }
640
641     while (top_direntp = readdir64(top_dirp)) {
642         /* Only descend directories with the AFSDIR attribute set.
643          * Could also verify the contents of the atribute, but currently
644          * they are not used.
645          * Performance could be improved for single volume salvages by
646          * only going through the directory containing the volume's inodes.
647          * But I'm being complete as a first pass.
648          */
649         (void)sprintf(vol_dirname, "%s/%s", mountedOn, top_direntp->d_name);
650         length = SIZEOF_XFS_DATTR_T;
651         if (attr_get
652             (vol_dirname, AFS_XFS_DATTR, (char *)&dattrs, &length, ATTR_ROOT))
653             continue;
654
655         if ((vol_dirp = opendir(vol_dirname)) == NULL) {
656             if (errno == ENOTDIR)
657                 continue;
658             Log("Can't open directory %s to read inodes.\n", vol_dirname);
659             goto err1_exit;
660         }
661
662         pino = top_direntp->d_ino;
663         n_renames = 0;
664         while (vol_direntp = readdir64(vol_dirp)) {
665             if (vol_direntp->d_name[1] == '\0'
666                 || vol_direntp->d_name[1] == '.')
667                 continue;
668
669             info.ili_version = AFS_XFS_ILI_VERSION;
670             info_size = sizeof(i_list_inode_t);
671             code =
672                 ilistinode64(sdirbuf.st_dev, vol_direntp->d_ino, &info,
673                              &info_size);
674             if (code) {
675                 /* Where possible, give more explicit messages. */
676                 switch (errno) {
677                 case ENXIO:
678                 case ENOSYS:
679                     Log("%s (device id %d) is not on an XFS filesystem.\n",
680                         vol_dirname, sdirbuf.st_dev);
681                     goto err1_exit;
682                     break;
683                 case EINVAL:
684                 case E2BIG:
685                     if (info_size != sizeof(i_list_inode_t)
686                         || info.ili_version != AFS_XFS_ILI_VERSION) {
687                         Log("Version skew between kernel and salvager.\n");
688                         goto err1_exit;
689                     }
690                     break;
691                 }
692                 /* Continue, so we collect all the errors in the first pass. */
693                 Log("Error listing inode named %s/%s: %s\n", vol_dirname,
694                     vol_direntp->d_name, strerror(errno));
695                 errors++;
696                 continue;
697             }
698
699             if (info.ili_attr_version != AFS_XFS_ATTR_VERS) {
700                 Log("Unrecognized XFS attribute version %d in %s/%s. Upgrade salvager\n", info.ili_attr_version, vol_dirname, vol_direntp->d_name);
701                 goto err1_exit;
702             }
703
704             if (judgeInode && (*judgeInode) (&info.ili_info, judgeParam, rock) == 0)
705                 continue;
706
707             rename = 0;
708             if (xfs_VerifyInode
709                 (vol_dirname, pino, vol_direntp->d_name, &info,
710                  &rename) < 0) {
711                 errors++;
712             }
713
714             if (rename) {
715                 /* Add this name to the list of items to rename. */
716                 if (n_renames >= n_avail) {
717                     n_avail += N_RENAME_STEP;
718                     if (n_avail == N_RENAME_STEP)
719                         renames = (xfs_Rename_t *)
720                             malloc(n_avail * sizeof(xfs_Rename_t));
721                     else
722                         renames = (xfs_Rename_t *)
723                             realloc((char *)renames,
724                                     n_avail * sizeof(xfs_Rename_t));
725                     if (!renames) {
726                         Log("Can't %salloc %lu bytes for rename list.\n",
727                             (n_avail == N_RENAME_STEP) ? "m" : "re",
728                             n_avail * sizeof(xfs_Rename_t));
729                         goto err1_exit;
730                     }
731                 }
732                 (void)strcpy(renames[n_renames].name, vol_direntp->d_name);
733                 renames[n_renames].uniq = info.ili_info.param[2];
734                 n_renames++;
735             }
736
737             if (inodeFile != INVALID_FD) {
738                 if (OS_WRITE
739                     (inodeFile, &info.ili_info, sizeof(vice_inode_info_t))
740                     != sizeof(vice_inode_info_t)) {
741                     Log("Error writing inode file for partition %s\n", mountedOn);
742                     goto err1_exit;
743                 }
744             }
745             ninodes++;
746
747         }                       /* end while vol_direntp */
748
749         closedir(vol_dirp);
750         vol_dirp = (DIR *) 0;
751         if (n_renames) {
752             Log("Renaming files.\n");
753             if (xfs_RenameFiles(vol_dirname, renames, n_renames) < 0) {
754                 goto err1_exit;
755             }
756         }
757     }
758
759     closedir(top_dirp);
760     if (renames)
761         free((char *)renames);
762     if (inodeFile != INVALID_FD) {
763         if (OS_SYNC(inodeFile) == -1) {
764             Log("Unable to successfully fsync inode file for %s\n", mountedOn);
765             return errors ? -1 : -2;
766         }
767         /*
768          * Paranoia:  check that the file is really the right size
769          */
770         if (OS_SIZE(inodeFile) != ninodes * sizeof(struct ViceInodeInfo)) {
771             Log("Wrong size (%d instead of %d) in inode file for %s\n",
772                 OS_SIZE(inodeFile), ninodes * sizeof(struct ViceInodeInfo),
773                 partition);
774             return errors ? -1 : -2;
775         }
776     }
777
778     if (errors) {
779         Log("Errors encontered listing inodes, not salvaging partition.\n");
780         return -1;
781     }
782
783     return 0;
784
785   err1_exit:
786     if (vol_dirp)
787         closedir(vol_dirp);
788     if (top_dirp)
789         closedir(top_dirp);
790     if (renames)
791         free((char *)renames);
792     return -1;
793 }
794
795 #endif
796
797 int
798 ListViceInodes(char *devname, char *mountedOn, FD_t inodeFile,
799                int (*judgeInode) (), afs_uint32 judgeParam, int *forcep, int forceR,
800                char *wpath, void *rock)
801 {
802     char dev[50], rdev[51];
803     struct stat status;
804     struct efs_dinode *p;
805     struct ViceInodeInfo info;
806     struct stat root_inode;
807     int ninodes = 0, err = 0;
808     struct efs_dinode *dinodeBuf = NULL;
809     int last_cgno;
810     ino_t imax, inum;           /* total number of I-nodes in file system */
811
812     *forcep = 0;
813     sync();
814     sleep(1);                   /* simulate operator    */
815     sync();
816     sleep(1);
817     sync();
818     sleep(1);
819
820     if (stat(mountedOn, &root_inode) < 0) {
821         Log("cannot stat: %s\n", mountedOn);
822         return -1;
823     }
824 #ifdef AFS_SGI_XFS_IOPS_ENV
825     if (!strcmp("xfs", root_inode.st_fstype)) {
826         return xfs_ListViceInodes(devname, mountedOn, inodeFile, judgeInode,
827                                   judgeParam, forcep, forceR, wpath, rock);
828     } else
829 #endif
830     {
831         Log("%s is not root of a filesystem\n", mountedOn);
832         return -1;
833     }
834 }
835
836 #else /* AFS_SGI_ENV */
837
838 #ifdef AFS_HPUX_ENV
839 #define SPERB   (MAXBSIZE / sizeof(short))
840 #define MAXNINDIR (MAXBSIZE / sizeof(daddr_t))
841
842 struct bufarea {
843     struct bufarea *b_next;     /* must be first */
844     daddr_t b_bno;
845     int b_size;
846     union {
847         char b_buf[MAXBSIZE];   /* buffer space */
848         short b_lnks[SPERB];    /* link counts */
849         daddr_t b_indir[MAXNINDIR];     /* indirect block */
850         struct fs b_fs;         /* super block */
851         struct cg b_cg;         /* cylinder group */
852     } b_un;
853     char b_dirty;
854 };
855 typedef struct bufarea BUFAREA;
856
857 BUFAREA sblk;
858 #define sblock sblk.b_un.b_fs
859 #endif /* AFS_HPUX_ENV */
860
861 extern char *afs_rawname();
862 int
863 ListViceInodes(char *devname, char *mountedOn, FD_t inodeFile,
864                int (*judgeInode) (), afs_uint32 judgeParam, int *forcep, int forceR,
865                char *wpath, void *rock)
866 {
867     union {
868 #ifdef  AFS_AIX_ENV
869         struct filsys fs;
870         char block[BSIZE];
871 #else                           /* !AFS_AIX_ENV */
872         struct fs fs;
873         char block[SBSIZE];
874 #endif
875     } super;
876     int i, c, e, bufsize, code, err = 0;
877     char dev[50], rdev[100], err1[512], *ptr1;
878     struct dinode *inodes = NULL, *einodes, *dptr;
879     int ninodes = 0;
880     struct dinode *p;
881     struct ViceInodeInfo info;
882
883     *forcep = 0;
884     partition = mountedOn;
885     sprintf(rdev, "%s/%s", wpath, devname);
886     ptr1 = afs_rawname(rdev);
887     strcpy(rdev, ptr1);
888
889     sync();
890     /* Bletch:  this is terrible;  is there a better way to do this? Does this work? vfsck doesn't even sleep!! */
891 #ifdef  AFS_AIX_ENV
892     sleep(5);                   /* Trying a smaller one for aix */
893 #else
894     sleep(10);
895 #endif
896
897     pfd = OS_OPEN(rdev, O_RDONLY, 0666);
898     if (pfd == INVALID_FD) {
899         sprintf(err1, "Could not open device %s to get inode list\n", rdev);
900         perror(err1);
901         return -1;
902     }
903 #ifdef  AFS_AIX_ENV
904     if (bread(pfd, (char *)&super.fs, SUPERB, sizeof super.fs) == -1) {
905 #else
906 #ifdef AFS_HPUX_ENV
907     if (bread(pfd, (char *)&sblock, SBLOCK, SBSIZE) == -1) {
908 #else
909     if (bread(pfd, super.block, SBLOCK, SBSIZE) == -1) {
910 #endif /* AFS_HPUX_ENV */
911 #endif
912         Log("Unable to read superblock, partition %s\n", partition);
913         goto out;
914     }
915
916 #ifdef  AFS_AIX_ENV
917     /*
918      * char *FSlabel(), *fslabel=0;
919      * fslabel = FSlabel(&super.fs);
920      */
921     if (super.fs.s_bsize == 0)
922         super.fs.s_bsize = 512;
923     if (super.fs.s_bsize != BSIZE) {
924         Log("SuperBlk: Cluster size not %d; run vfsck\n", BSIZE);
925         goto out;
926     }
927     fmax = super.fs.s_fsize;    /* first invalid blk num */
928     imax = ((ino_t) super.fs.s_isize - (SUPERB + 1)) * INOPB;
929     if (imax == 0) {
930         Log("Size check: imax==0!\n");
931         goto out;
932     }
933     if (GetAuxInodeFile(partition, &status) == 0) {
934         Log("Can't access Aux inode file for partition %s, aborting\n",
935             partition);
936         goto out;
937     }
938     for (inum = 1; inum <= imax; inum++) {
939         struct dauxinode *auxp;
940         if ((auxp = IsAfsInode(inum)) == NULL) {
941             /* Not an afs inode, keep going */
942             continue;
943         }
944         if ((p = ginode(inum)) == NULL)
945             continue;
946         /* deleted/non-existent inode when di_mode == 0 */
947         if (!p->di_mode)
948             continue;
949         info.inodeNumber = (int)inum;
950         info.byteCount = p->di_size;
951         info.linkCount = p->di_nlink;
952         info.u.param[0] = auxp->aux_param1;
953         info.u.param[1] = auxp->aux_param2;
954         info.u.param[2] = auxp->aux_param3;
955         info.u.param[3] = auxp->aux_param4;
956         if (judgeInode && (*judgeInode) (&info, judgeParam, rock) == 0)
957             continue;
958         if (inodeFile != INVALID_FD) {
959             if (OS_WRITE(inodeFile, &info, sizeof(info)) != sizeof(info)) {
960                 Log("Error writing inode file for partition %s\n", partition);
961                 goto out;
962             }
963         }
964         ninodes++;
965     }
966 #else
967     /*
968      * run a few consistency checks of the superblock
969      * (Cribbed from vfsck)
970      */
971 #ifdef AFS_HPUX_ENV
972 #if defined(FD_FSMAGIC)
973     if ((sblock.fs_magic != FS_MAGIC) && (sblock.fs_magic != FS_MAGIC_LFN)
974         && (sblock.fs_magic != FD_FSMAGIC)
975 #if     defined(AFS_HPUX101_ENV)
976         && (sblock.fs_magic != FD_FSMAGIC_2)
977 #endif
978         ) {
979 #else
980     if ((sblock.fs_magic != FS_MAGIC) && (sblock.fs_magic != FS_MAGIC_LFN)) {
981 #endif
982         Log("There's something wrong with the superblock for partition %s; bad magic (%d) run vfsck\n", partition, sblock.fs_magic);
983         goto out;
984     }
985     if (sblock.fs_ncg < 1) {
986         Log("There's something wrong with the superblock for partition %s; NCG OUT OF RANGE (%d) run vfsck\n", partition, sblock.fs_ncg);
987         goto out;
988     }
989     if (sblock.fs_cpg < 1 || sblock.fs_cpg > MAXCPG) {
990         Log("There's something wrong with the superblock for partition %s; CPG OUT OF RANGE (%d) run vfsck\n", partition, sblock.fs_cpg);
991         goto out;
992     }
993     if (sblock.fs_ncg * sblock.fs_cpg < sblock.fs_ncyl
994         || (sblock.fs_ncg - 1) * sblock.fs_cpg >= sblock.fs_ncyl) {
995         Log("There's something wrong with the superblock for partition %s; NCYL LESS THAN NCG*CPG run vfsck\n", partition);
996         goto out;
997     }
998     if (sblock.fs_sbsize > SBSIZE) {
999         Log("There's something wrong with the superblock for partition %s; bsize too large (%d vs. %d) run vfsck\n", partition, sblock.fs_sbsize, sblock.fs_bsize);
1000         goto out;
1001     }
1002 #else
1003     if ((super.fs.fs_magic != FS_MAGIC)
1004         || (super.fs.fs_ncg < 1)
1005 #if     defined(AFS_SUN_ENV) || defined(AFS_OSF_ENV) || defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
1006         || (super.fs.fs_cpg < 1)
1007 #else
1008         || (super.fs.fs_cpg < 1 || super.fs.fs_cpg > MAXCPG)
1009 #endif
1010         || (super.fs.fs_ncg * super.fs.fs_cpg < super.fs.fs_ncyl
1011             || (super.fs.fs_ncg - 1) * super.fs.fs_cpg >= super.fs.fs_ncyl)
1012         || (super.fs.fs_sbsize > SBSIZE)) {
1013         Log("There's something wrong with the superblock for partition %s; run vfsck\n", partition);
1014         goto out;
1015     }
1016 #endif /* AFS_HPUX_ENV */
1017
1018 #ifdef AFS_HPUX_ENV
1019     bufsize = sblock.fs_ipg * sizeof(struct dinode);
1020 #else
1021     bufsize = super.fs.fs_ipg * sizeof(struct dinode);
1022 #endif /* AFS_HPUX_ENV */
1023     inodes = (struct dinode *)malloc(bufsize);
1024     einodes = (struct dinode *)(((char *)inodes) + bufsize);
1025     if (inodes == NULL) {
1026         Log("Unable to allocate enough memory to scan inodes; help!\n");
1027         goto out;
1028     }
1029     Log("Scanning inodes on device %s...\n", rdev);
1030 #ifdef AFS_HPUX_ENV
1031     for (c = 0; c < sblock.fs_ncg; c++) {
1032         i = c * sblock.fs_ipg;
1033         e = i + sblock.fs_ipg;
1034 #if     defined(AFS_HPUX102_ENV)
1035         if (OS_SEEK(pfd, dbtoo(fsbtodb(&sblock, itod(&sblock, i))), L_SET) ==
1036             -1) {
1037 #else
1038         if (OS_SEEK(pfd, dbtob(fsbtodb(&sblock, itod(&sblock, i))), L_SET) ==
1039             -1) {
1040 #endif
1041 #else
1042     for (c = 0; c < super.fs.fs_ncg; c++) {
1043         daddr_t dblk1;
1044 #if defined(AFS_SUN5_ENV) || defined(AFS_DARWIN_ENV)
1045         daddr_t f1;
1046 #if defined(AFS_DARWIN_ENV)
1047 #define offset_t off_t
1048 #define llseek lseek
1049 #endif
1050         offset_t off;
1051 #endif /* AFS_SUN5_ENV */
1052         i = c * super.fs.fs_ipg;
1053         e = i + super.fs.fs_ipg;
1054 #ifdef  AFS_OSF_ENV
1055         dblk1 = fsbtodb(&super.fs, itod(&super.fs, i));
1056         if (OS_SEEK(pfd, (off_t) ((off_t) dblk1 * DEV_BSIZE), L_SET) == -1) {
1057 #else
1058 #if defined(AFS_SUN5_ENV) || defined(AFS_DARWIN_ENV)
1059         f1 = fsbtodb(&super.fs, itod(&super.fs, i));
1060         off = (offset_t) f1 << DEV_BSHIFT;
1061         if (OS_SEEK(pfd, off, L_SET) == -1) {
1062 #else
1063         if (OS_SEEK(pfd, dbtob(fsbtodb(&super.fs, itod(&super.fs, i))), L_SET)
1064             == -1) {
1065 #endif /* AFS_SUN5_ENV */
1066 #endif /* AFS_OSF_ENV */
1067 #endif /* AFS_HPUX_ENV */
1068             Log("Error reading inodes for partition %s; run vfsck\n",
1069                 partition);
1070             goto out;
1071         }
1072         while (i < e) {
1073             if (!forceR) {
1074                 if (OS_READ(pfd, inodes, bufsize) != bufsize) {
1075                     Log("Error reading inodes for partition %s; run vfsck\n",
1076                         partition);
1077                     goto out;
1078                 }
1079             } else {
1080                 int bj, bk;
1081                 dptr = inodes;
1082                 for (bj = bk = 0; bj < bufsize; bj = bj + 512, bk++) {
1083                     if ((code = OS_READ(pfd, dptr, 512)) != 512) {
1084                         Log("Error reading inode %d? for partition %s (errno = %d); run vfsck\n", bk + i, partition, errno);
1085                         if (OS_SEEK(pfd, 512, L_SET) == -1) {
1086                             Log("OS_SEEK failed\n");
1087                             goto out;
1088                         }
1089                         dptr->di_mode = 0;
1090                         dptr++;
1091                         dptr->di_mode = 0;
1092                         dptr++;
1093                         dptr->di_mode = 0;
1094                         dptr++;
1095                         dptr->di_mode = 0;
1096                         dptr++;
1097                     } else
1098                         dptr += 4;
1099                 }
1100             }
1101             for (p = inodes; p < einodes && i < e; i++, p++) {
1102 #ifdef notdef
1103                 Log("Ino=%d, v1=%x, v2=%x, v3=%x, mode=%x size=%d, lcnt=%d\n",
1104                     i, p->di_vicep1, p->di_vicep2, p->di_vicep3, p->di_mode,
1105                     p->di_size, p->di_nlink);
1106                 printf
1107                     ("Ino=%d, v1=%x, v2=%x, v3=%x, mode=%x size=%d, lcnt=%d\n",
1108                      i, p->di_vicep1, p->di_vicep2, p->di_vicep3, p->di_mode,
1109                      p->di_size, p->di_nlink);
1110 #endif
1111 #ifdef AFS_OSF_ENV
1112 #ifdef AFS_3DISPARES
1113                 /* Check to see if this inode is a pre-"OSF1 4.0D" inode */
1114                 if ((p->di_uid || p->di_gid)
1115                     && !(p->di_flags & (IC_XUID | IC_XGID))) {
1116                     Log("Found unconverted inode %d: Use 'fs_conv_dux40D convert' on partition %s\n", i, partition);
1117                     goto out;
1118                 }
1119 #else
1120                 osi_Panic("Tru64 needs AFS_3DISPARES\n");
1121 #endif
1122 #endif
1123 #if     defined(AFS_SUN5_ENV)
1124                 /* if this is a pre-sol2.6 unconverted inode, bail out */
1125                 {
1126                     afs_uint32 p1, p2, p3, p4;
1127                     int p5;
1128                     quad *q;
1129
1130                     q = (quad *) & (p->di_ic.ic_lsize);
1131                     p1 = p->di_gen;
1132                     p2 = p->di_ic.ic_flags;
1133                     p3 = q->val[0];
1134                     p4 = p->di_ic.ic_uid;
1135                     p5 = p->di_ic.ic_gid;
1136
1137                     if ((p2 || p3) && !p4 && (p5 == -2)) {
1138                         Log("Found unconverted inode %d\n", i);
1139                         Log("You should run the AFS file conversion utility\n");
1140                         goto out;
1141                     }
1142                 }
1143 #endif
1144                 if (IS_DVICEMAGIC(p) && (p->di_mode & IFMT) == IFREG) {
1145                     afs_uint32 p2 = p->di_vicep2, p3 = DI_VICEP3(p);
1146
1147                     info.u.param[0] = p->di_vicep1;
1148 #ifdef  AFS_3DISPARES
1149                     if (((p2 >> 3) == INODESPECIAL) && (p2 & 0x3)) {
1150                         info.u.param[1] = INODESPECIAL;
1151                         info.u.param[2] = p3;
1152                         info.u.param[3] = p2 & 0x3;
1153                     } else {
1154                         info.u.param[1] = ((p2 >> 27) << 16) + (p3 & 0xffff);
1155                         info.u.param[2] = (p2 & 0x3fffff);
1156                         info.u.param[3] =
1157                             (((p2 >> 22) & 0x1f) << 16) + (p3 >> 16);
1158                     }
1159 #else
1160                     info.u.param[1] = p->di_vicep2;
1161                     info.u.param[2] = DI_VICEP3(p);
1162                     info.u.param[3] = p->di_vicep4;
1163 #endif
1164                     info.inodeNumber = i;
1165                     info.byteCount = p->di_size;
1166                     info.linkCount = p->di_nlink;
1167                     if (judgeInode && (*judgeInode) (&info, judgeParam, rock) == 0)
1168                         continue;
1169                     if (inodeFile != INVALID_FD) {
1170                         if (OS_WRITE(inodeFile, &info, sizeof(info)) != sizeof(info)) {
1171                             Log("Error writing inode file for partition %s\n",
1172                                 partition);
1173                             goto out;
1174                         }
1175                     }
1176                     ninodes++;
1177                 }
1178             }
1179         }
1180     }
1181     if (inodes)
1182         free(inodes);
1183 #endif
1184     if (inodeFile != INVALID_FD) {
1185         if (OS_SYNC(inodeFile) == -1) {
1186             Log("Unable to successfully fsync inode file for %s\n", partition);
1187             err = -2;
1188             goto out1;
1189         }
1190
1191         /*
1192          * Paranoia:  check that the file is really the right size
1193          */
1194         if (OS_SIZE(inodeFile) != ninodes * sizeof(struct ViceInodeInfo)) {
1195             Log("Wrong size (%d instead of %d) in inode file for %s\n",
1196                 OS_SIZE(inodeFile), ninodes * sizeof(struct ViceInodeInfo),
1197                 partition);
1198             err = -2;
1199             goto out1;
1200         }
1201     }
1202     OS_CLOSE(pfd);
1203     return 0;
1204
1205   out:
1206     err = -1;
1207   out1:
1208     OS_CLOSE(pfd);
1209     if (inodes)
1210         free(inodes);
1211     return err;
1212 }
1213 #endif /* !AFS_SGI_ENV */
1214 #endif /* !AFS_AIX31_ENV       */
1215
1216 #ifdef AFS_DARWIN_ENV
1217 #undef dbtob
1218 #define dbtob(db) ((unsigned)(db) << DEV_BSHIFT)
1219 #endif
1220
1221 int
1222 bread(FD_t fd, char *buf, daddr_t blk, afs_int32 size)
1223 {
1224 #ifdef  AFS_AIX_ENV
1225 #ifdef  AFS_AIX41_ENV
1226     offset_t off = (offset_t) blk << FSBSHIFT;
1227     if (OS_SEEK(fd, off, 0) < 0) {
1228         Log("Unable to seek to offset %llu for block %u\n", off, blk);
1229         return -1;
1230     }
1231 #else /* AFS_AIX41_ENV */
1232     if (OS_SEEK(fd, blk * Bsize, 0) < 0) {
1233         Log("Unable to seek to offset %u for block %u\n", blk * Bsize, blk);
1234     }
1235 #endif /* AFS_AIX41_ENV */
1236 #else
1237     if (OS_SEEK(fd, (off_t) dbtob(blk), L_SET) < 0) {
1238         Log("Unable to seek to offset %u for block %u\n", dbtob(blk), blk);
1239     }
1240 #endif
1241     if (OS_READ(fd, buf, size) != size) {
1242         Log("Unable to read block %d, partition %s\n", blk, partition);
1243         return -1;
1244     }
1245     return 0;
1246 }
1247
1248 #endif /* AFS_LINUX20_ENV */
1249 static afs_int32
1250 convertVolumeInfo(FdHandle_t *fdhr, FdHandle_t *fdhw, afs_uint32 vid)
1251 {
1252     struct VolumeDiskData vd;
1253     char *p;
1254
1255     if (FDH_PREAD(fdhr, &vd, sizeof(struct VolumeDiskData), 0) !=
1256         sizeof(struct VolumeDiskData)) {
1257         Log("1 convertiVolumeInfo: read failed for %lu with code %d\n", vid,
1258             errno);
1259         return -1;
1260     }
1261     vd.restoredFromId = vd.id;  /* remember the RO volume here */
1262     vd.cloneId = vd.id;
1263     vd.id = vd.parentId;
1264     vd.type = RWVOL;
1265     vd.dontSalvage = 0;
1266     vd.inUse = 0;
1267     vd.uniquifier += 5000;      /* just in case there are still file copies
1268                                    from the old RW volume around */
1269
1270     /* For ROs, the copyDate contains the time that the RO volume was actually
1271      * created, and the creationDate just contains the last time the RO was
1272      * copied from the RW data. So, make the new RW creationDate more accurate
1273      * by setting it to copyDate, if copyDate is older. Since, we know the
1274      * volume is at least as old as copyDate. */
1275     if (vd.copyDate < vd.creationDate) {
1276         vd.creationDate = vd.copyDate;
1277     } else {
1278         /* If copyDate is newer, just make copyDate and creationDate the same,
1279          * for consistency with other RWs */
1280         vd.copyDate = vd.creationDate;
1281     }
1282
1283     p = strrchr(vd.name, '.');
1284     if (p && !strcmp(p, ".readonly")) {
1285         memset(p, 0, 9);
1286     }
1287
1288     if (FDH_PWRITE(fdhw, &vd, sizeof(struct VolumeDiskData), 0) !=
1289         sizeof(struct VolumeDiskData)) {
1290         Log("1 convertiVolumeInfo: write failed for %lu with code %d\n", vid,
1291             errno);
1292         return -1;
1293     }
1294     return 0;
1295 }
1296
1297 struct specino {
1298     afs_int32 inodeType;
1299     Inode inodeNumber;
1300     Inode ninodeNumber;
1301 };
1302
1303
1304 int
1305 UpdateThisVolume(struct ViceInodeInfo *inodeinfo, VolumeId singleVolumeNumber,
1306                  struct specino *specinos)
1307 {
1308     struct dinode *p;
1309     if ((inodeinfo->u.vnode.vnodeNumber == INODESPECIAL) &&
1310         (inodeinfo->u.vnode.volumeId == singleVolumeNumber)) {
1311         specinos[inodeinfo->u.special.type].inodeNumber =
1312             inodeinfo->inodeNumber;
1313     }
1314     return 0; /* We aren't using a result file, we're caching */
1315 }
1316
1317 static char *
1318 getDevName(char *pbuffer, char *wpath)
1319 {
1320     char pbuf[128], *ptr;
1321     strcpy(pbuf, pbuffer);
1322     ptr = (char *)strrchr(pbuf, OS_DIRSEPC);
1323     if (ptr) {
1324         *ptr = '\0';
1325         strcpy(wpath, pbuf);
1326     } else
1327         return NULL;
1328     ptr = (char *)strrchr(pbuffer, OS_DIRSEPC);
1329     if (ptr) {
1330         strcpy(pbuffer, ptr + 1);
1331         return pbuffer;
1332     } else
1333         return NULL;
1334 }
1335
1336 #ifdef FSSYNC_BUILD_CLIENT
1337 int
1338 inode_ConvertROtoRWvolume(char *pname, afs_uint32 volumeId)
1339 {
1340     char dir_name[512], oldpath[512], newpath[512];
1341     char volname[20];
1342     char headername[16];
1343     char *name;
1344     int fd, err, forcep, j;
1345     ssize_t len, nBytes;
1346     struct dirent *dp;
1347     struct DiskPartition64 *partP;
1348     struct ViceInodeInfo info;
1349     struct VolumeDiskHeader h;
1350     IHandle_t *ih, *ih2;
1351     FdHandle_t *fdP, *fdP2;
1352     afs_foff_t offset;
1353     char wpath[100];
1354     char tmpDevName[100];
1355     char buffer[128];
1356     struct specino specinos[VI_LINKTABLE+1];
1357     Inode nearInode = 0;
1358 # ifdef AFS_DEMAND_ATTACH_FS
1359     int locktype = 0;
1360 # endif /* AFS_DEMAND_ATTACH_FS */
1361     int code = 0;
1362
1363     memset(&specinos, 0, sizeof(specinos));
1364
1365     /* now do the work */
1366
1367     for (partP = DiskPartitionList; partP && strcmp(partP->name, pname);
1368          partP = partP->next);
1369     if (!partP) {
1370         Log("1 inode_ConvertROtoRWvolume: Couldn't find DiskPartition for %s\n", pname);
1371         code = EIO;
1372         goto done;
1373     }
1374
1375 #ifdef AFS_DEMAND_ATTACH_FS
1376     locktype = VVolLockType(V_VOLUPD, 1);
1377     code = VLockVolumeByIdNB(volumeId, partP, locktype);
1378     if (code) {
1379         locktype = 0;
1380         code = EIO;
1381         goto done;
1382     }
1383 #endif
1384
1385     if (VReadVolumeDiskHeader(volumeId, partP, &h)) {
1386         Log("1 inode_ConvertROtoRWvolume: Couldn't read header for RO-volume %lu.\n",
1387             afs_printable_uint32_lu(volumeId));
1388         return EIO;
1389     }
1390
1391     FSYNC_VolOp(volumeId, pname, FSYNC_VOL_BREAKCBKS, 0, NULL);
1392
1393     strcpy(tmpDevName, partP->devName);
1394     name = getDevName(tmpDevName, wpath);
1395
1396     if ((err = ListViceInodes(name, VPartitionPath(partP),
1397                               NULL, UpdateThisVolume, volumeId,
1398                               &forcep, 0, wpath, &specinos)) < 0)
1399     {
1400         Log("1 inode_ConvertROtoRWvolume: Couldn't get special inodes\n");
1401         code = EIO;
1402         goto done;
1403     }
1404
1405 #if defined(NEARINODE_HINT)
1406     nearInodeHash(volumeId, nearInode);
1407     nearInode %= partP->f_files;
1408 #endif
1409
1410     for (j = VI_VOLINFO; j < VI_LINKTABLE+1; j++) {
1411         if (specinos[j].inodeNumber > 0) {
1412             specinos[j].ninodeNumber =
1413                 IH_CREATE(NULL, partP->device, VPartitionPath(partP),
1414                           nearInode, h.parent, INODESPECIAL, j, h.parent);
1415             IH_INIT(ih, partP->device, volumeId,
1416                     specinos[j].inodeNumber);
1417             fdP = IH_OPEN(ih);
1418             if (!fdP) {
1419                 Log("1 inode_ConvertROtoRWvolume: Couldn't find special inode %d for %d\n", j, volumeId);
1420                 code = -1;
1421                 goto done;
1422             }
1423
1424             IH_INIT(ih2, partP->device, h.parent, specinos[j].ninodeNumber);
1425             fdP2 = IH_OPEN(ih2);
1426             if (!fdP2) {
1427                 Log("1 inode_ConvertROtoRWvolume: Couldn't find special inode %d for %d\n", j, h.parent);
1428                 code = -1;
1429                 goto done;
1430             }
1431
1432             if (j == VI_VOLINFO)
1433                 convertVolumeInfo(fdP, fdP2, ih2->ih_vid);
1434             else {
1435                 offset = 0;
1436                 while (1) {
1437                     len = FDH_PREAD(fdP, buffer, sizeof(buffer), offset);
1438                     if (len < 0)
1439                         return errno;
1440                     if (len == 0)
1441                         break;
1442                     nBytes = FDH_PWRITE(fdP2, buffer, len, offset);
1443                     if (nBytes != len) {
1444                         code = -1;
1445                         goto done;
1446                     }
1447                     offset += len;
1448                 }
1449             }
1450
1451             FDH_CLOSE(fdP);
1452             FDH_CLOSE(fdP2);
1453
1454             /* Unlink the old special inode; otherwise we will get duplicate
1455              * special inodes if we recreate the RO again */
1456             if (IH_DEC(ih, specinos[j].inodeNumber, volumeId) == -1) {
1457                 afs_ino_str_t stmp;
1458                 Log("IH_DEC failed: %x, %s, %u errno %d\n", ih,
1459                     PrintInode(stmp, specinos[j].inodeNumber), volumeId, errno);
1460             }
1461
1462             IH_RELEASE(ih);
1463             IH_RELEASE(ih2);
1464         }
1465     }
1466
1467     h.id = h.parent;
1468 #ifdef AFS_64BIT_IOPS_ENV
1469     h.volumeInfo_lo = (afs_int32)specinos[VI_VOLINFO].ninodeNumber & 0xffffffff;
1470     h.volumeInfo_hi = (afs_int32)(specinos[VI_VOLINFO].ninodeNumber >> 32) && 0xffffffff;
1471     h.smallVnodeIndex_lo = (afs_int32)specinos[VI_SMALLINDEX].ninodeNumber & 0xffffffff;
1472     h.smallVnodeIndex_hi = (afs_int32)(specinos[VI_SMALLINDEX].ninodeNumber >> 32) & 0xffffffff;
1473     h.largeVnodeIndex_lo = (afs_int32)specinos[VI_LARGEINDEX].ninodeNumber & 0xffffffff;
1474     h.largeVnodeIndex_hi = (afs_int32)(specinos[VI_LARGEINDEX].ninodeNumber >> 32) & 0xffffffff;
1475     if (specinos[VI_LINKTABLE].ninodeNumber) {
1476         h.linkTable_lo = (afs_int32)specinos[VI_LINKTABLE].ninodeNumber & 0xffffffff;
1477         h.linkTable_hi = (afs_int32)specinos[VI_LINKTABLE].ninodeNumber & 0xffffffff;
1478     }
1479 #else
1480     h.volumeInfo_lo = specinos[VI_VOLINFO].ninodeNumber;
1481     h.smallVnodeIndex_lo = specinos[VI_SMALLINDEX].ninodeNumber;
1482     h.largeVnodeIndex_lo = specinos[VI_LARGEINDEX].ninodeNumber;
1483     if (specinos[VI_LINKTABLE].ninodeNumber) {
1484         h.linkTable_lo = specinos[VI_LINKTABLE].ninodeNumber;
1485     }
1486 #endif
1487
1488     if (VCreateVolumeDiskHeader(&h, partP)) {
1489         Log("1 inode_ConvertROtoRWvolume: Couldn't write header for RW-volume %lu\n",
1490             afs_printable_uint32_lu(h.id));
1491         code = EIO;
1492         goto done;
1493     }
1494
1495     if (VDestroyVolumeDiskHeader(partP, volumeId, h.parent)) {
1496         Log("1 inode_ConvertROtoRWvolume: Couldn't unlink header for RO-volume %lu\n",
1497             afs_printable_uint32_lu(volumeId));
1498     }
1499
1500     FSYNC_VolOp(volumeId, pname, FSYNC_VOL_DONE, 0, NULL);
1501     FSYNC_VolOp(h.id, pname, FSYNC_VOL_ON, 0, NULL);
1502
1503  done:
1504 # ifdef AFS_DEMAND_ATTACH_FS
1505     if (locktype) {
1506         VUnlockVolumeById(volumeId, partP);
1507     }
1508 # endif /* AFS_DEMAND_ATTACH_FS */
1509     return code;
1510 }
1511 #endif /* FSSYNC_BUILD_CLIENT */
1512 #endif /* AFS_NAMEI_ENV */