rx: Use opr queues
[openafs.git] / src / vol / namei_ops.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 /* I/O operations for the Unix open by name (namei) interface. */
11
12 #include <afsconfig.h>
13 #include <afs/param.h>
14
15 #include <roken.h>
16
17
18 #ifdef AFS_NAMEI_ENV
19
20 #ifdef HAVE_SYS_FILE_H
21 # include <sys/file.h>
22 #endif
23
24 #ifdef AFS_NT40_ENV
25 #define DELETE_ZLC
26 #include <windows.h>
27 #include <winnt.h>
28 #include <winbase.h>
29 #include <direct.h>
30 #endif
31
32 #include <afs/opr.h>
33 #include <rx/rx_queue.h>
34 #include <lock.h>
35 #include <afs/afsutil.h>
36 #include <lwp.h>
37 #include "nfs.h"
38 #include <afs/afsint.h>
39 #include "ihandle.h"
40 #include "vnode.h"
41 #include "volume.h"
42 #include "viceinode.h"
43 #include "voldefs.h"
44 #include "partition.h"
45 #include "fssync.h"
46 #include "volume_inline.h"
47 #include "common.h"
48 #include <afs/errors.h>
49
50 #ifdef AFS_NT40_ENV
51 #include <afs/errmap_nt.h>
52 #endif
53
54 #ifndef LOCK_SH
55 #define   LOCK_SH   1    /* shared lock */
56 #define   LOCK_EX   2    /* exclusive lock */
57 #define   LOCK_NB   4    /* don't block when locking */
58 #define   LOCK_UN   8    /* unlock */
59 #endif
60
61 #ifdef AFS_SALSRV_ENV
62 #include <pthread.h>
63 #include <afs/work_queue.h>
64 #include <afs/thread_pool.h>
65 #include <vol/vol-salvage.h>
66 #endif
67
68 int Testing=0;
69
70
71 afs_sfsize_t
72 namei_iread(IHandle_t * h, afs_foff_t offset, char *buf, afs_fsize_t size)
73 {
74     afs_sfsize_t nBytes;
75     FdHandle_t *fdP;
76
77     fdP = IH_OPEN(h);
78     if (fdP == NULL)
79         return -1;
80
81     nBytes = FDH_PREAD(fdP, buf, size, offset);
82     if (nBytes < 0)
83         FDH_REALLYCLOSE(fdP);
84     else
85         FDH_CLOSE(fdP);
86     return nBytes;
87 }
88
89 afs_sfsize_t
90 namei_iwrite(IHandle_t * h, afs_foff_t offset, char *buf, afs_fsize_t size)
91 {
92     afs_sfsize_t nBytes;
93     FdHandle_t *fdP;
94
95     fdP = IH_OPEN(h);
96     if (fdP == NULL)
97         return -1;
98
99     nBytes = FDH_PWRITE(fdP, buf, size, offset);
100     if (nBytes < 0)
101         FDH_REALLYCLOSE(fdP);
102     else
103         FDH_CLOSE(fdP);
104     return nBytes;
105 }
106
107 #ifdef AFS_NT40_ENV
108 /* Inode number format:
109  * low 32 bits - if a regular file or directory, the vnode; else the type.
110  * 32-36 - uniquifier tag and index into counts array for this vnode. Only
111  *         two of the available bits are currently used. The rest are
112  *         present in case we ever increase the number of types of volumes
113  *         in the volume group.
114  * bit 37 : 1  == special, 0 == regular
115  */
116 # define NAMEI_VNODEMASK    0x00ffffffff
117 # define NAMEI_TAGSHIFT     32
118 # define NAMEI_INODESPECIAL 0x2000000000
119 # define NAMEI_SPECDIR "R"
120 # define NAMEI_SPECDIRC 'R'
121 #else /* !AFS_NT40_ENV */
122 /* Inode number format:
123  * low 26 bits - vnode number - all 1's if volume special file.
124  * next 3 bits - tag
125  * next 3 bits spare (0's)
126  * high 32 bits - uniquifier (regular) or type if spare
127  */
128 # define NAMEI_VNODEMASK    0x003ffffff
129 # define NAMEI_TAGSHIFT     26
130 # define NAMEI_UNIQMASK     0xffffffff
131 # define NAMEI_UNIQSHIFT    32
132 # define NAMEI_INODESPECIAL ((Inode)NAMEI_VNODEMASK)
133 /* dir1 is the high 8 bits of the 26 bit vnode */
134 # define VNO_DIR1(vno) ((vno >> 14) & 0xff)
135 /* dir2 is the next 9 bits */
136 # define VNO_DIR2(vno) ((vno >> 9) & 0x1ff)
137 /* "name" is the low 9 bits of the vnode, the 3 bit tag and the uniq */
138 # define NAMEI_SPECDIR "special"
139 #endif /* !AFS_NT40_ENV */
140 #define NAMEI_TAGMASK      0x7
141 #define NAMEI_VNODESPECIAL NAMEI_VNODEMASK
142
143 #define NAMEI_SPECDIRLEN (sizeof(NAMEI_SPECDIR)-1)
144
145 #define NAMEI_MAXVOLS 5         /* Maximum supported number of volumes per volume
146                                  * group, not counting temporary (move) volumes.
147                                  * This is the number of separate files, all having
148                                  * the same vnode number, which can occur in a volume
149                                  * group at once.
150                                  */
151
152 #ifndef AFS_NT40_ENV
153 typedef struct {
154     int ogm_owner;
155     int ogm_group;
156     int ogm_mode;
157 } namei_ogm_t;
158 #endif
159
160 static int GetFreeTag(IHandle_t * ih, int vno);
161
162 /* namei_HandleToInodeDir
163  *
164  * Construct the path name of the directory holding the inode data.
165  * Format: /<vicepx>/INODEDIR
166  *
167  */
168 #ifdef AFS_NT40_ENV
169 static void
170 namei_HandleToInodeDir(namei_t * name, IHandle_t * ih)
171 {
172     memset(name, '\0', sizeof(*name));
173     nt_DevToDrive(name->n_drive, ih->ih_dev);
174     strlcpy(name->n_path, name->n_drive, sizeof(name->n_path));
175 }
176
177 #else
178 /* Format: /<vicepx>/INODEDIR */
179 #define PNAME_BLEN 64
180 static void
181 namei_HandleToInodeDir(namei_t * name, IHandle_t * ih)
182 {
183     size_t offset;
184
185     memset(name, '\0', sizeof(*name));
186
187     /*
188      * Add the /vicepXX string to the start of name->n_base and then calculate
189      * offset as the number of bytes we know we added.
190      *
191      * FIXME: This embeds knowledge of the vice partition naming scheme and
192      * mapping from device numbers.  There needs to be an API that tells us
193      * this offset.
194      */
195     volutil_PartitionName_r(ih->ih_dev, name->n_base, sizeof(name->n_base));
196     offset = VICE_PREFIX_SIZE + (ih->ih_dev > 25 ? 2 : 1);
197     name->n_base[offset] = OS_DIRSEPC;
198     offset++;
199     strlcpy(name->n_base + offset, INODEDIR, sizeof(name->n_base) - offset);
200     strlcpy(name->n_path, name->n_base, sizeof(name->n_path));
201 }
202 #endif
203
204 #define addtoname(N, C)                                         \
205 do {                                                            \
206     if ((N)->n_path[strlen((N)->n_path)-1] != OS_DIRSEPC)       \
207         strlcat((N)->n_path, OS_DIRSEP, sizeof((N)->n_path));   \
208     strlcat((N)->n_path, (C), sizeof((N)->n_path));             \
209 } while(0)
210
211
212 #ifdef AFS_NT40_ENV
213 static void
214 namei_HandleToVolDir(namei_t * name, IHandle_t * ih)
215 {
216     /* X:\Vol_XXXXXXX.data */
217     b32_string_t str1;
218     char *namep;
219
220     namei_HandleToInodeDir(name, ih);
221     /* nt_drive added to name by namei_HandleToInodeDir() */
222     namep = name->n_voldir;
223     (void)memcpy(namep, "\\Vol_", 5);
224     namep += 5;
225     (void)strcpy(namep, int_to_base32(str1, ih->ih_vid));
226     namep += strlen(namep);
227     memcpy(namep, ".data", 5);
228     namep += 5;
229     *namep = '\0';
230     addtoname(name, name->n_voldir);
231 }
232 #else
233 static void
234 namei_HandleToVolDir(namei_t * name, IHandle_t * ih)
235 {
236     lb64_string_t tmp;
237
238     namei_HandleToInodeDir(name, ih);
239     (void)int32_to_flipbase64(tmp, (int64_t) (ih->ih_vid & 0xff));
240     strlcpy(name->n_voldir1, tmp, sizeof(name->n_voldir1));
241     addtoname(name, name->n_voldir1);
242     (void)int32_to_flipbase64(tmp, (int64_t) ih->ih_vid);
243     strlcpy(name->n_voldir2, tmp, sizeof(name->n_voldir2));
244     addtoname(name, name->n_voldir2);
245 }
246 #endif
247
248 /* namei_HandleToName
249  *
250  * Constructs a file name for the fully qualified handle.
251  */
252 #ifdef AFS_NT40_ENV
253 /* Note that special files end up in X:\Vol_XXXXXXX.data\R */
254 void
255 namei_HandleToName(namei_t * name, IHandle_t * ih)
256 {
257     int vno = (int)(ih->ih_ino & NAMEI_VNODEMASK);
258     int special = (ih->ih_ino & NAMEI_INODESPECIAL)?1:0;
259     int tag = (int)((ih->ih_ino >> NAMEI_TAGSHIFT) & NAMEI_TAGMASK);
260     b32_string_t str1;
261     char *namep;
262     namei_HandleToVolDir(name, ih);
263
264     if (special) {
265         name->n_dir[0] = NAMEI_SPECDIRC;
266     } else {
267         if (vno & 0x1)
268             name->n_dir[0] = 'Q';
269         else
270             name->n_dir[0] = ((vno & 0x1f) >> 1) + 'A';
271
272     }
273     name->n_dir[1] = '\0';
274     addtoname(name, name->n_dir);
275     /* X:\Vol_XXXXXXX.data\X\V_XXXXXXX.XXX */
276     namep = name->n_inode;
277     (void)memcpy(namep, "\\V_", 3);
278     namep += 3;
279     (void)strcpy(namep, int_to_base32(str1, vno));
280     namep += strlen(namep);
281     *(namep++) = '.';
282     (void)strcpy(namep, int_to_base32(str1, tag));
283     namep += strlen(namep);
284     addtoname(name, name->n_inode);
285 }
286 #else
287 /* Note that special files end up in /vicepX/InodeDir/Vxx/V*.data/special */
288 void
289 namei_HandleToName(namei_t * name, IHandle_t * ih)
290 {
291     int vno = (int)(ih->ih_ino & NAMEI_VNODEMASK);
292     lb64_string_t str;
293
294     namei_HandleToVolDir(name, ih);
295
296     if (vno == NAMEI_VNODESPECIAL) {
297         strlcpy(name->n_dir1, NAMEI_SPECDIR, sizeof(name->n_dir1));
298         addtoname(name, name->n_dir1);
299         name->n_dir2[0] = '\0';
300     } else {
301         (void)int32_to_flipbase64(str, VNO_DIR1(vno));
302         strlcpy(name->n_dir1, str, sizeof(name->n_dir1));
303         addtoname(name, name->n_dir1);
304         (void)int32_to_flipbase64(str, VNO_DIR2(vno));
305         strlcpy(name->n_dir2, str, sizeof(name->n_dir2));
306         addtoname(name, name->n_dir2);
307     }
308     (void)int64_to_flipbase64(str, (int64_t) ih->ih_ino);
309     strlcpy(name->n_inode, str, sizeof(name->n_inode));
310     addtoname(name, name->n_inode);
311 }
312 #endif
313
314 #ifndef AFS_NT40_ENV
315 /* The following is a warning to tell sys-admins to not muck about in this
316  * name space.
317  */
318 #define VICE_README "These files and directories are a part of the AFS \
319 namespace. Modifying them\nin any way will result in loss of AFS data,\n\
320 ownership and permissions included.\n"
321 int
322 namei_ViceREADME(char *partition)
323 {
324     char filename[32];
325     int fd;
326
327     /* Create the inode directory if we're starting for the first time */
328     snprintf(filename, sizeof filename, "%s" OS_DIRSEP "%s", partition,
329              INODEDIR);
330     mkdir(filename, 0700);
331
332     snprintf(filename, sizeof filename,
333              "%s" OS_DIRSEP "%s" OS_DIRSEP "README",
334              partition, INODEDIR);
335     fd = OS_OPEN(filename, O_WRONLY | O_CREAT | O_TRUNC, 0444);
336     if (fd != INVALID_FD) {
337         (void)OS_WRITE(fd, VICE_README, strlen(VICE_README));
338         OS_CLOSE(fd);
339     }
340     return (errno);
341 }
342 #endif
343
344 /* namei_CreateDataDirectories
345  *
346  * If creating the file failed because of ENOENT or ENOTDIR, try
347  * creating all the directories first.
348  */
349 #ifdef AFS_NT40_ENV
350 static int
351 namei_CreateDataDirectories(namei_t * name, int *created)
352 {
353     char tmp[256];
354     char *s;
355     int i;
356
357     *created = 0;
358     snprintf(tmp, 256, "%s" OS_DIRSEP "%s", name->n_drive, name->n_voldir);
359
360     if (mkdir(tmp) < 0) {
361         if (errno != EEXIST)
362             return -1;
363     } else
364         *created = 1;
365
366     s = tmp;
367     s += strlen(tmp);
368
369     *s++ = OS_DIRSEPC;
370     *(s + 1) = '\0';
371     for (i = 'A'; i <= NAMEI_SPECDIRC; i++) {
372         *s = (char)i;
373         if (mkdir(tmp) < 0 && errno != EEXIST)
374             return -1;
375     }
376     return 0;
377 }
378 #else
379 #define create_dir() \
380 do { \
381     if (mkdir(tmp, 0700)<0) { \
382         if (errno != EEXIST) \
383             return -1; \
384     } \
385     else { \
386         *created = 1; \
387     } \
388 } while (0)
389
390 #define create_nextdir(A) \
391 do { \
392          strcat(tmp, OS_DIRSEP); strcat(tmp, A); create_dir();  \
393 } while(0)
394
395 static int
396 namei_CreateDataDirectories(namei_t * name, int *created)
397 {
398     char tmp[256];
399
400     *created = 0;
401
402     strlcpy(tmp, name->n_base, sizeof(tmp));
403     create_dir();
404
405     create_nextdir(name->n_voldir1);
406     create_nextdir(name->n_voldir2);
407     create_nextdir(name->n_dir1);
408     if (name->n_dir2[0]) {
409         create_nextdir(name->n_dir2);
410     }
411     return 0;
412 }
413 #endif
414
415 #ifndef AFS_NT40_ENV
416 /* delTree(): Deletes an entire tree of directories (no files)
417  * Input:
418  *   root : Full path to the subtree. Should be big enough for PATH_MAX
419  *   tree : the subtree to be deleted is rooted here. Specifies only the
420  *          subtree beginning at tree (not the entire path). It should be
421  *          a pointer into the "root" buffer.
422  * Output:
423  *  errp : errno of the first error encountered during the directory cleanup.
424  *         *errp should have been initialized to 0.
425  *
426  * Return Values:
427  *  -1  : If errors were encountered during cleanup and error is set to
428  *        the first errno.
429  *   0  : Success.
430  *
431  * If there are errors, we try to work around them and delete as many
432  * directories as possible. We don't attempt to remove directories that still
433  * have non-dir entries in them.
434  */
435 static int
436 delTree(char *root, char *tree, int *errp)
437 {
438     char *cp;
439     DIR *ds;
440     struct dirent *dirp;
441     struct afs_stat st;
442
443     if (*tree) {
444         /* delete the children first */
445         cp = strchr(tree, OS_DIRSEPC);
446         if (cp) {
447             delTree(root, cp + 1, errp);
448             *cp = '\0';
449         } else
450             cp = tree + strlen(tree);   /* move cp to the end of string tree */
451
452         /* now delete all entries in this dir */
453         if ((ds = opendir(root)) != NULL) {
454             errno = 0;
455             while ((dirp = readdir(ds))) {
456                 /* ignore . and .. */
457                 if (!strcmp(dirp->d_name, ".") || !strcmp(dirp->d_name, ".."))
458                     continue;
459                 /* since root is big enough, we reuse the space to
460                  * concatenate the dirname to the current tree
461                  */
462                 strcat(root, OS_DIRSEP);
463                 strcat(root, dirp->d_name);
464                 if (afs_stat(root, &st) == 0 && S_ISDIR(st.st_mode)) {
465                     /* delete this subtree */
466                     delTree(root, cp + 1, errp);
467                 } else
468                     *errp = *errp ? *errp : errno;
469
470                 /* recover path to our cur tree by truncating it to
471                  * its original len
472                  */
473                 *cp = 0;
474             }
475             /* if (!errno) -- closedir not implicit if we got an error */
476             closedir(ds);
477         }
478
479         /* finally axe the current dir */
480         if (rmdir(root))
481             *errp = *errp ? *errp : errno;
482
483 #ifndef AFS_PTHREAD_ENV         /* let rx get some work done */
484         IOMGR_Poll();
485 #endif /* !AFS_PTHREAD_ENV */
486
487     }
488
489     /* if valid tree */
490     /* if we encountered errors during cleanup, we return a -1 */
491     if (*errp)
492         return -1;
493
494     return 0;
495
496 }
497 #endif
498
499 /* namei_RemoveDataDirectories
500  * Return Values:
501  * Returns 0 on success.
502  * Returns -1 on error. Typically, callers ignore this error because we
503  * can continue running if the removes fail. The salvage process will
504  * finish tidying up for us.
505  */
506
507 #ifdef AFS_NT40_ENV
508 static int
509 namei_RemoveDataDirectories(namei_t * name)
510 {
511     int code = 0;
512     char *path;
513     char tmp[256];
514     int i;
515
516     snprintf(tmp, 256, "%s" OS_DIRSEP "%s", name->n_drive, name->n_voldir);
517
518     path = tmp;
519     path += strlen(path);
520     *path++ = OS_DIRSEPC;
521     *(path + 1) = '\0';
522     for (i = 'A'; i <= NAMEI_SPECDIRC; i++) {
523         *path = (char)i;
524         if (rmdir(name->n_path) < 0 && errno != ENOENT)
525             code = -1;
526     }
527
528     if (!code) {
529         /* Delete the Vol_NNNNNN.data directory. */
530         path--;
531         *path = '\0';
532         if (rmdir(name->n_path) < 0 && errno != ENOENT) {
533             code = -1;
534         }
535     }
536     return code;
537 }
538 #else
539 /*
540  * We only use the n_base and n_voldir1 entries
541  * and only do rmdir's.
542  */
543 static int
544 namei_RemoveDataDirectories(namei_t * name)
545 {
546     int code = 0;
547     char *path;
548     int prefixlen = strlen(name->n_base), err = 0;
549     int vollen = strlen(name->n_voldir1);
550     char pbuf[MAXPATHLEN];
551
552     path = pbuf;
553
554     strlcpy(path, name->n_path, sizeof(pbuf));
555
556     /* move past the prefix and n_voldir1 */
557     path = path + prefixlen + 1 + vollen + 1;   /* skip over the trailing / */
558
559     /* now delete all dirs upto path */
560     code = delTree(pbuf, path, &err);
561
562     /* We've now deleted everything under /n_base/n_voldir1/n_voldir2 that
563      * we could. Do not delete /n_base/n_voldir1, since doing such might
564      * interrupt another thread trying to create a volume. We could introduce
565      * some locking to make this safe (or only remove it for whole-partition
566      * salvages), but by not deleting it we only leave behind a maximum of
567      * 256 empty directories. So at least for now, don't bother. */
568     return code;
569 }
570 #endif
571
572 /* Create the file in the name space.
573  *
574  * Parameters stored as follows:
575  * Regular files:
576  * p1 - volid - implied in containing directory.
577  * p2 - vnode - name is <vno:31-23>/<vno:22-15>/<vno:15-0><uniq:31-5><tag:2-0>
578  * p3 - uniq -- bits 4-0 are in mode bits 4-0
579  * p4 - dv ---- dv:15-0 in uid, dv:29-16 in gid, dv:31-30 in mode:6-5
580  * Special files:
581  * p1 - volid - creation time - dwHighDateTime
582  * p2 - vnode - -1 means special, file goes in "S" subdirectory.
583  * p3 - type -- name is <type>.<tag> where tag is a file name unqiquifier.
584  * p4 - parid - parent volume id - implied in containing directory.
585  *
586  * Return value is the inode number or (Inode)-1 if error.
587  * We "know" there is only one link table, so return EEXIST if there already
588  * is a link table. It's up to the calling code to test errno and increment
589  * the link count.
590  */
591
592 /* namei_MakeSpecIno
593  *
594  * This function is called by VCreateVolume to hide the implementation
595  * details of the inode numbers. This only allows for 7 volume special
596  * types, but if we get that far, this could should be dead by then.
597  */
598 Inode
599 namei_MakeSpecIno(int volid, int type)
600 {
601     Inode ino;
602     ino = NAMEI_INODESPECIAL;
603 #ifdef AFS_NT40_ENV
604     ino |= type;
605     /* tag is always 0 for special */
606 #else
607     type &= NAMEI_TAGMASK;
608     ino |= ((Inode) type) << NAMEI_TAGSHIFT;
609     ino |= ((Inode) volid) << NAMEI_UNIQSHIFT;
610 #endif
611     return ino;
612 }
613
614 #ifdef AFS_NT40_ENV
615 /* SetOGM */
616 static int
617 SetOGM(FD_t fd, int parm, int tag)
618 {
619     return -1;
620 }
621
622 static int
623 SetWinOGM(FD_t fd, int p1, int p2)
624 {
625     BOOL code;
626     FILETIME ftime;
627
628     ftime.dwHighDateTime = p1;
629     ftime.dwLowDateTime = p2;
630
631     code = SetFileTime(fd, &ftime, NULL /*access*/, NULL /*write*/);
632     if (!code)
633         return -1;
634     return 0;
635 }
636
637 static int
638 GetWinOGM(FD_t fd, int *p1, int *p2)
639 {
640     BOOL code;
641     FILETIME ftime;
642
643     code = GetFileTime(fd, &ftime, NULL /*access*/, NULL /*write*/);
644     if (!code)
645         return -1;
646
647     *p1 = ftime.dwHighDateTime;
648     *p2 = ftime.dwLowDateTime;
649
650     return 0;
651 }
652
653 static int
654 CheckOGM(FdHandle_t *fdP, int p1)
655 {
656     int ogm_p1, ogm_p2;
657
658     if (GetWinOGM(fdP->fd_fd, &ogm_p1, &ogm_p2)) {
659         return -1;
660     }
661
662     if (ogm_p1 != p1) {
663         return -1;
664     }
665
666     return 0;
667 }
668
669 static int
670 FixSpecialOGM(FdHandle_t *fdP, int check)
671 {
672     Inode ino = fdP->fd_ih->ih_ino;
673     VnodeId vno = NAMEI_VNODESPECIAL, ogm_vno;
674     int ogm_volid;
675
676     if (GetWinOGM(fdP->fd_fd, &ogm_volid, &ogm_vno)) {
677         return -1;
678     }
679
680     /* the only thing we can check is the vnode number; for the volid we have
681      * nothing else to compare against */
682     if (vno != ogm_vno) {
683         if (check) {
684             return -1;
685         }
686         if (SetWinOGM(fdP->fd_fd, ogm_volid, vno)) {
687             return -1;
688         }
689     }
690     return 0;
691 }
692
693 #else /* AFS_NT40_ENV */
694 /* SetOGM - set owner group and mode bits from parm and tag */
695 static int
696 SetOGM(FD_t fd, int parm, int tag)
697 {
698 /*
699  * owner - low 15 bits of parm.
700  * group - next 15 bits of parm.
701  * mode - 2 bits of parm, then lowest = 3 bits of tag.
702  */
703     int owner, group, mode;
704
705     owner = parm & 0x7fff;
706     group = (parm >> 15) & 0x7fff;
707     if (fchown(fd, owner, group) < 0)
708         return -1;
709
710     mode = (parm >> 27) & 0x18;
711     mode |= tag & 0x7;
712     if (fchmod(fd, mode) < 0)
713         return -1;
714     return 0;
715 }
716
717 /* GetOGM - get parm and tag from owner, group and mode bits. */
718 static void
719 GetOGMFromStat(struct afs_stat_st *status, int *parm, int *tag)
720 {
721     *parm = status->st_uid | (status->st_gid << 15);
722     *parm |= (status->st_mode & 0x18) << 27;
723     *tag = status->st_mode & 0x7;
724 }
725
726 static int
727 GetOGM(FdHandle_t *fdP, int *parm, int *tag)
728 {
729     struct afs_stat_st status;
730     if (afs_fstat(fdP->fd_fd, &status) < 0)
731         return -1;
732     GetOGMFromStat(&status, parm, tag);
733     return 0;
734 }
735
736 static int
737 CheckOGM(FdHandle_t *fdP, int p1)
738 {
739     int parm, tag;
740
741     if (GetOGM(fdP, &parm, &tag) < 0)
742         return -1;
743     if (parm != p1)
744         return -1;
745
746     return 0;
747 }
748
749 static int
750 FixSpecialOGM(FdHandle_t *fdP, int check)
751 {
752     int inode_volid, ogm_volid;
753     int inode_type, ogm_type;
754     Inode ino = fdP->fd_ih->ih_ino;
755
756     inode_volid = ((ino >> NAMEI_UNIQSHIFT) & NAMEI_UNIQMASK);
757     inode_type = (int)((ino >> NAMEI_TAGSHIFT) & NAMEI_TAGMASK);
758
759     if (GetOGM(fdP, &ogm_volid, &ogm_type) < 0) {
760         Log("Error retrieving OGM info\n");
761         return -1;
762     }
763
764     if (inode_volid != ogm_volid || inode_type != ogm_type) {
765         Log("%sIncorrect OGM data (ino: vol %u type %d) (ogm: vol %u type %d)\n",
766             check?"":"Fixing ", inode_volid, inode_type, ogm_volid, ogm_type);
767
768         if (check) {
769             return -1;
770         }
771
772         if (SetOGM(fdP->fd_fd, inode_volid, inode_type) < 0) {
773             Log("Error setting OGM data\n");
774             return -1;
775         }
776     }
777     return 0;
778 }
779
780 #endif /* !AFS_NT40_ENV */
781
782 /**
783  * Check/fix the OGM data for an inode
784  *
785  * @param[in] fdP   Open file handle for the inode to check
786  * @param[in] check 1 to just check the OGM data, and return an error if it
787  *                  is incorrect. 0 to fix the OGM data if it is incorrect.
788  *
789  * @pre fdP must be for a special inode
790  *
791  * @return status
792  *  @retval 0 success
793  *  @retval -1 error
794  */
795 int
796 namei_FixSpecialOGM(FdHandle_t *fdP, int check)
797 {
798     int vnode;
799     Inode ino = fdP->fd_ih->ih_ino;
800
801     vnode = (int)(ino & NAMEI_VNODEMASK);
802     if (vnode != NAMEI_VNODESPECIAL) {
803         Log("FixSpecialOGM: non-special vnode %u\n", vnode);
804         return -1;
805     }
806
807     return FixSpecialOGM(fdP, check);
808 }
809
810 int big_vno = 0;                /* Just in case we ever do 64 bit vnodes. */
811
812 /* Derive the name and create it O_EXCL. If that fails we have an error.
813  * Get the tag from a free column in the link table.
814  */
815 #ifdef AFS_NT40_ENV
816 Inode
817 namei_icreate(IHandle_t * lh, char *part, afs_uint32 p1, afs_uint32 p2, afs_uint32 p3, afs_uint32 p4)
818 {
819     namei_t name;
820     FD_t fd = INVALID_FD;
821     int code = 0;
822     int created_dir = 0;
823     IHandle_t tmp;
824     FdHandle_t *fdP;
825     FdHandle_t tfd;
826     int type, tag;
827     int ogm_p1, ogm_p2;
828     char *p;
829     b32_string_t str1;
830
831     memset((void *)&tmp, 0, sizeof(IHandle_t));
832     memset(&tfd, 0, sizeof(FdHandle_t));
833
834     tmp.ih_dev = nt_DriveToDev(part);
835     if (tmp.ih_dev == -1) {
836         errno = EINVAL;
837         return -1;
838     }
839
840     if (p2 == INODESPECIAL) {
841         /* Parameters for special file:
842          * p1 - volume id - goes into owner/group/mode
843          * p2 - vnode == INODESPECIAL
844          * p3 - type
845          * p4 - parent volume id
846          */
847         ogm_p1 = p1;
848         ogm_p2 = p2;
849         type = p3;
850         tmp.ih_vid = p4;        /* Use parent volume id, where this file will be. */
851         tmp.ih_ino = namei_MakeSpecIno(p1, p3);
852     } else {
853         int vno = p2 & NAMEI_VNODEMASK;
854         /* Parameters for regular file:
855          * p1 - volume id
856          * p2 - vnode
857          * p3 - uniq
858          * p4 - dv
859          */
860
861         if (vno != p2) {
862             big_vno++;
863             errno = EINVAL;
864             return -1;
865         }
866
867         tmp.ih_vid = p1;
868         tmp.ih_ino = (Inode) p2;
869         ogm_p1 = p3;
870         ogm_p2 = p4;
871     }
872
873     namei_HandleToName(&name, &tmp);
874     p = strrchr((char *)&name.n_path, '.');
875     p++;
876     for (tag = 0; tag < NAMEI_MAXVOLS; tag++) {
877         *p = *int_to_base32(str1, tag);
878         fd = OS_OPEN((char *)&name.n_path, O_CREAT | O_RDWR | O_EXCL, 0666);
879         if (fd == INVALID_FD) {
880             if (errno == ENOTDIR || errno == ENOENT) {
881                 if (namei_CreateDataDirectories(&name, &created_dir) == 0)
882                     fd = OS_OPEN((char *)&name.n_path, O_CREAT | O_RDWR | O_EXCL, 0666);
883             }
884         }
885
886         if (fd != INVALID_FD)
887             break;
888         if (p2 == INODESPECIAL && p3 == VI_LINKTABLE)
889             break;
890     }
891     if (fd == INVALID_FD) {
892         code = -1;
893         goto bad;
894     }
895     tmp.ih_ino &= ~(((Inode) NAMEI_TAGMASK) << NAMEI_TAGSHIFT);
896     tmp.ih_ino |= (((Inode) tag) << NAMEI_TAGSHIFT);
897
898     if (!code) {
899         if (SetWinOGM(fd, ogm_p1, ogm_p2)) {
900             errno = OS_ERROR(EBADF);
901             code = -1;
902         }
903     }
904
905     if (!code) {
906         if (p2 != INODESPECIAL) {
907             if (fd == INVALID_FD) {
908                 errno = ENOENT;
909                 code = nt_unlink((char *)&name.n_path);
910                 code = -1;
911                 goto bad;
912             }
913             fdP = IH_OPEN(lh);
914             if (fdP == NULL) {
915                 code = -1;
916                 goto bad;
917             }
918             code = namei_SetLinkCount(fdP, tmp.ih_ino, 1, 0);
919             FDH_CLOSE(fdP);
920         } else if (p2 == INODESPECIAL && p3 == VI_LINKTABLE) {
921             if (fd == INVALID_FD)
922                 goto bad;
923             /* hack at tmp to setup for set link count call. */
924             tfd.fd_fd = fd;
925             code = namei_SetLinkCount(&tfd, (Inode) 0, 1, 0);
926         }
927     }
928
929 bad:
930     if (fd != INVALID_FD)
931         OS_CLOSE(fd);
932
933     if (code || (fd == INVALID_FD)) {
934         if (p2 != INODESPECIAL) {
935             fdP = IH_OPEN(lh);
936             if (fdP) {
937                 namei_SetLinkCount(fdP, tmp.ih_ino, 0, 0);
938                 FDH_CLOSE(fdP);
939             }
940         }
941
942         if (created_dir) {
943             int save_errno = errno;
944             namei_RemoveDataDirectories(&name);
945             errno = save_errno;
946         }
947     }
948     return (code || (fd == INVALID_FD)) ? (Inode) -1 : tmp.ih_ino;
949 }
950 #else /* !AFS_NT40_ENV */
951 Inode
952 namei_icreate(IHandle_t * lh, char *part, afs_uint32 p1, afs_uint32 p2, afs_uint32 p3, afs_uint32 p4)
953 {
954     namei_t name;
955     int fd = INVALID_FD;
956     int code = 0;
957     int created_dir = 0;
958     IHandle_t tmp;
959     FdHandle_t *fdP;
960     FdHandle_t tfd;
961     int tag;
962     int ogm_parm;
963
964     memset((void *)&tmp, 0, sizeof(IHandle_t));
965     memset(&tfd, 0, sizeof(FdHandle_t));
966
967     tmp.ih_dev = volutil_GetPartitionID(part);
968     if (tmp.ih_dev == -1) {
969         errno = EINVAL;
970         return -1;
971     }
972
973     if (p2 == -1) {
974         /* Parameters for special file:
975          * p1 - volume id - goes into owner/group/mode
976          * p2 - vnode == -1
977          * p3 - type
978          * p4 - parent volume id
979          */
980         ogm_parm = p1;
981
982         tag = p3;
983         tmp.ih_vid = p4;        /* Use parent volume id, where this file will be. */
984         tmp.ih_ino = namei_MakeSpecIno(p1, p3);
985     } else {
986         int vno = p2 & NAMEI_VNODEMASK;
987         /* Parameters for regular file:
988          * p1 - volume id
989          * p2 - vnode
990          * p3 - uniq
991          * p4 - dv
992          */
993
994         if (vno != p2) {
995             big_vno++;
996             errno = EINVAL;
997             return -1;
998         }
999         /* If GetFreeTag succeeds, it atomically sets link count to 1. */
1000         tag = GetFreeTag(lh, p2);
1001         if (tag < 0)
1002             goto bad;
1003
1004         tmp.ih_vid = p1;
1005         tmp.ih_ino = (Inode) p2;
1006         /* name is <uniq(p3)><tag><vno(p2)> */
1007         tmp.ih_ino |= ((Inode) tag) << NAMEI_TAGSHIFT;
1008         tmp.ih_ino |= ((Inode) p3) << NAMEI_UNIQSHIFT;
1009
1010         ogm_parm = p4;
1011     }
1012
1013     namei_HandleToName(&name, &tmp);
1014     fd = OS_OPEN(name.n_path, O_CREAT | O_EXCL | O_RDWR, 0);
1015     if (fd == INVALID_FD) {
1016         if (errno == ENOTDIR || errno == ENOENT) {
1017             if (namei_CreateDataDirectories(&name, &created_dir) < 0)
1018                 goto bad;
1019             fd = OS_OPEN(name.n_path, O_CREAT | O_EXCL | O_RDWR,
1020                           0);
1021             if (fd == INVALID_FD)
1022                 goto bad;
1023         } else {
1024             goto bad;
1025         }
1026     }
1027     if (SetOGM(fd, ogm_parm, tag) < 0) {
1028         OS_CLOSE(fd);
1029         fd = INVALID_FD;
1030         goto bad;
1031     }
1032
1033     if (p2 == (afs_uint32)-1 && p3 == VI_LINKTABLE) {
1034         /* hack at tmp to setup for set link count call. */
1035         memset((void *)&tfd, 0, sizeof(FdHandle_t));    /* minimalistic still, but a little cleaner */
1036         tfd.fd_ih = &tmp;
1037         tfd.fd_fd = fd;
1038         code = namei_SetLinkCount(&tfd, (Inode) 0, 1, 0);
1039     }
1040
1041   bad:
1042     if (fd != INVALID_FD)
1043         OS_CLOSE(fd);
1044
1045
1046     if (code || (fd == INVALID_FD)) {
1047         if (p2 != -1) {
1048             fdP = IH_OPEN(lh);
1049             if (fdP) {
1050                 namei_SetLinkCount(fdP, tmp.ih_ino, 0, 0);
1051                 FDH_CLOSE(fdP);
1052             }
1053         }
1054     }
1055     return (code || (fd == INVALID_FD)) ? (Inode) - 1 : tmp.ih_ino;
1056 }
1057 #endif
1058
1059 /* namei_iopen */
1060 FD_t
1061 namei_iopen(IHandle_t * h)
1062 {
1063     FD_t fd;
1064     namei_t name;
1065
1066     /* Convert handle to file name. */
1067     namei_HandleToName(&name, h);
1068     fd = OS_OPEN((char *)&name.n_path, O_RDWR, 0666);
1069     return fd;
1070 }
1071
1072 /* Need to detect vol special file and just unlink. In those cases, the
1073  * handle passed in _is_ for the inode. We only check p1 for the special
1074  * files.
1075  */
1076 int
1077 namei_dec(IHandle_t * ih, Inode ino, int p1)
1078 {
1079     int count = 0;
1080     namei_t name;
1081     int code = 0;
1082     FdHandle_t *fdP;
1083
1084     if ((ino & NAMEI_INODESPECIAL) == NAMEI_INODESPECIAL) {
1085         IHandle_t *tmp;
1086         int type = (int)((ino >> NAMEI_TAGSHIFT) & NAMEI_TAGMASK);
1087
1088         /* Verify this is the right file. */
1089         IH_INIT(tmp, ih->ih_dev, ih->ih_vid, ino);
1090
1091         namei_HandleToName(&name, tmp);
1092
1093         fdP = IH_OPEN(tmp);
1094         if (fdP == NULL) {
1095             IH_RELEASE(tmp);
1096             errno = OS_ERROR(ENOENT);
1097             return -1;
1098         }
1099
1100         if (CheckOGM(fdP, p1) < 0) {
1101             FDH_REALLYCLOSE(fdP);
1102             IH_RELEASE(tmp);
1103             errno = OS_ERROR(EINVAL);
1104             return -1;
1105         }
1106
1107         /* If it's the link table itself, decrement the link count. */
1108         if (type == VI_LINKTABLE) {
1109           if ((count = namei_GetLinkCount(fdP, (Inode) 0, 1, 0, 1)) < 0) {
1110                 FDH_REALLYCLOSE(fdP);
1111                 IH_RELEASE(tmp);
1112                 return -1;
1113             }
1114
1115             count--;
1116             if (namei_SetLinkCount(fdP, (Inode) 0, count < 0 ? 0 : count, 1) <
1117                 0) {
1118                 FDH_REALLYCLOSE(fdP);
1119                 IH_RELEASE(tmp);
1120                 return -1;
1121             }
1122
1123             if (count > 0) {
1124                 FDH_CLOSE(fdP);
1125                 IH_RELEASE(tmp);
1126                 return 0;
1127             }
1128         }
1129
1130         if ((code = OS_UNLINK(name.n_path)) == 0) {
1131             if (type == VI_LINKTABLE) {
1132                 /* Try to remove directory. If it fails, that's ok.
1133                  * Salvage will clean up.
1134                  */
1135                 char *slash = strrchr(name.n_path, OS_DIRSEPC);
1136                 if (slash) {
1137                     /* avoid an rmdir() on the file we just unlinked */
1138                     *slash = '\0';
1139                 }
1140                 (void)namei_RemoveDataDirectories(&name);
1141             }
1142         }
1143         FDH_REALLYCLOSE(fdP);
1144         IH_RELEASE(tmp);
1145     } else {
1146         /* Get a file descriptor handle for this Inode */
1147         fdP = IH_OPEN(ih);
1148         if (fdP == NULL) {
1149             return -1;
1150         }
1151
1152         if ((count = namei_GetLinkCount(fdP, ino, 1, 0, 1)) < 0) {
1153             FDH_REALLYCLOSE(fdP);
1154             return -1;
1155         }
1156
1157         count--;
1158         if (count >= 0) {
1159             if (namei_SetLinkCount(fdP, ino, count, 1) < 0) {
1160                 FDH_REALLYCLOSE(fdP);
1161                 return -1;
1162             }
1163         } else {
1164             IHandle_t *th;
1165             IH_INIT(th, ih->ih_dev, ih->ih_vid, ino);
1166             Log("Warning: Lost ref on ihandle dev %d vid %d ino %lld\n",
1167                 th->ih_dev, th->ih_vid, (afs_int64)th->ih_ino);
1168             IH_RELEASE(th);
1169
1170             /* If we're less than 0, someone presumably unlinked;
1171                don't bother setting count to 0, but we need to drop a lock */
1172             if (namei_SetLinkCount(fdP, ino, 0, 1) < 0) {
1173                 FDH_REALLYCLOSE(fdP);
1174                 return -1;
1175             }
1176         }
1177         if (count == 0) {
1178             IHandle_t *th;
1179             IH_INIT(th, ih->ih_dev, ih->ih_vid, ino);
1180
1181             namei_HandleToName(&name, th);
1182             IH_RELEASE(th);
1183             code = OS_UNLINK(name.n_path);
1184         }
1185         FDH_CLOSE(fdP);
1186     }
1187
1188     return code;
1189 }
1190
1191 int
1192 namei_inc(IHandle_t * h, Inode ino, int p1)
1193 {
1194     int count;
1195     int code = 0;
1196     FdHandle_t *fdP;
1197
1198     if ((ino & NAMEI_INODESPECIAL) == NAMEI_INODESPECIAL) {
1199         int type = (int)((ino >> NAMEI_TAGSHIFT) & NAMEI_TAGMASK);
1200         if (type != VI_LINKTABLE)
1201             return 0;
1202         ino = (Inode) 0;
1203     }
1204
1205     /* Get a file descriptor handle for this Inode */
1206     fdP = IH_OPEN(h);
1207     if (fdP == NULL) {
1208         return -1;
1209     }
1210
1211     if ((count = namei_GetLinkCount(fdP, ino, 1, 0, 1)) < 0)
1212         code = -1;
1213     else {
1214         count++;
1215         if (count > 7) {
1216             errno = OS_ERROR(EINVAL);
1217             code = -1;
1218             count = 7;
1219         }
1220         if (namei_SetLinkCount(fdP, ino, count, 1) < 0)
1221             code = -1;
1222     }
1223     if (code) {
1224         FDH_REALLYCLOSE(fdP);
1225     } else {
1226         FDH_CLOSE(fdP);
1227     }
1228     return code;
1229 }
1230
1231 #ifndef AFS_NT40_ENV
1232 int
1233 namei_replace_file_by_hardlink(IHandle_t *hLink, IHandle_t *hTarget)
1234 {
1235     afs_int32 code;
1236     namei_t nameLink;
1237     namei_t nameTarget;
1238
1239     /* Convert handle to file name. */
1240     namei_HandleToName(&nameLink, hLink);
1241     namei_HandleToName(&nameTarget, hTarget);
1242
1243     OS_UNLINK(nameLink.n_path);
1244     code = link(nameTarget.n_path, nameLink.n_path);
1245     return code;
1246 }
1247
1248 int
1249 namei_copy_on_write(IHandle_t *h)
1250 {
1251     afs_int32 code = 0;
1252     FD_t fd;
1253     namei_t name;
1254     FdHandle_t *fdP;
1255     struct afs_stat_st tstat;
1256     afs_foff_t offset;
1257
1258     namei_HandleToName(&name, h);
1259     if (afs_stat(name.n_path, &tstat) < 0)
1260         return EIO;
1261     if (tstat.st_nlink > 1) {                   /* do a copy on write */
1262         char path[259];
1263         char *buf;
1264         afs_size_t size;
1265         ssize_t tlen;
1266
1267         fdP = IH_OPEN(h);
1268         if (!fdP)
1269             return EIO;
1270         snprintf(path, sizeof(path), "%s-tmp", name.n_path);
1271         fd = OS_OPEN(path, O_CREAT | O_EXCL | O_RDWR, 0);
1272         if (fd == INVALID_FD) {
1273             FDH_CLOSE(fdP);
1274             return EIO;
1275         }
1276         buf = malloc(8192);
1277         if (!buf) {
1278             OS_CLOSE(fd);
1279             OS_UNLINK(path);
1280             FDH_CLOSE(fdP);
1281             return ENOMEM;
1282         }
1283         size = tstat.st_size;
1284         offset = 0;
1285         while (size) {
1286             tlen = size > 8192 ? 8192 : size;
1287             if (FDH_PREAD(fdP, buf, tlen, offset) != tlen)
1288                 break;
1289             if (OS_WRITE(fd, buf, tlen) != tlen)
1290                 break;
1291             size -= tlen;
1292             offset += tlen;
1293         }
1294         OS_CLOSE(fd);
1295         FDH_REALLYCLOSE(fdP);
1296         free(buf);
1297         if (size)
1298             code = EIO;
1299         else {
1300             OS_UNLINK(name.n_path);
1301             code = rename(path, name.n_path);
1302         }
1303     }
1304     return code;
1305 }
1306 #endif
1307
1308 /************************************************************************
1309  * File Name Structure
1310  ************************************************************************
1311  *
1312  * Each AFS file needs a unique name and it needs to be findable with
1313  * minimal lookup time. Note that the constraint on the number of files and
1314  * directories in a volume is the size of the vnode index files and the
1315  * max file size AFS supports (for internal files) of 2^31. Since a record
1316  * in the small vnode index file is 64 bytes long, we can have at most
1317  * (2^31)/64 or 33554432 files. A record in the large index file is
1318  * 256 bytes long, giving a maximum of (2^31)/256 = 8388608 directories.
1319  * Another layout parameter is that there is roughly a 16 to 1 ratio between
1320  * the number of files and the number of directories.
1321  *
1322  * Using this information we can see that a layout of 256 directories, each
1323  * with 512 subdirectories and each of those having 512 files gives us
1324  * 256*512*512 = 67108864 AFS files and directories.
1325  *
1326  * The volume, vnode, uniquifier and data version, as well as the tag
1327  * are required, either for finding the file or for salvaging. It's best to
1328  * restrict the name to something that can be mapped into 64 bits so the
1329  * "Inode" is easily comparable (using "==") to other "Inodes". The tag
1330  * is used to distinguish between different versions of the same file
1331  * which are currently in the RW and clones of a volume. See "Link Table
1332  * Organization" below for more information on the tag. The tag is
1333  * required in the name of the file to ensure a unique name.
1334  *
1335  * ifdef AFS_NT40_ENV
1336  * The data for each volume group is in a separate directory. The name of the
1337  * volume is of the form: Vol_NNNNNN.data, where NNNNNN is a base 32
1338  * representation of the RW volume ID (even where the RO is the only volume
1339  * on the partition). Below that are separate subdirectories for the
1340  * AFS directories and special files. There are also 16 directories for files,
1341  * hashed on the low 5 bits (recall bit0 is always 0) of the vnode number.
1342  * These directories are named:
1343  * A - P - 16 file directories.
1344  * Q ----- data directory
1345  * R ----- special files directory
1346  *
1347  * The vnode is hashed into the directory using the low bits of the
1348  * vnode number.
1349  *
1350  * The format of a file name for a regular file is:
1351  * Y:\Vol_NNNNNN.data\X\V_IIIIII.J
1352  * Y - partition encoded as drive letter, starting with D
1353  * NNNNNN - base 32 encoded volume number of RW volume
1354  * X - hash directory, as above
1355  * IIIIII - base 32 encoded vnode number
1356  * J - base 32 encoded tag
1357  *
1358  * uniq is stored in the dwHighDateTime creation time field
1359  * dv is stored in the dwLowDateTime creation time field
1360  *
1361  * Special inodes are always in the R directory, as above, and are
1362  * encoded:
1363  * True child volid is stored in the dwHighDateTime creation time field
1364  * vnode number is always -1 (Special)
1365  * type is the IIIIII part of the filename
1366  * uniq is the J part of the filename
1367  * parent volume id is implied in the containing directory
1368  *
1369  * else
1370  * We can store data in the uid, gid and mode bits of the files, provided
1371  * the directories have root only access. This gives us 15 bits for each
1372  * of uid and gid (GNU chown considers 65535 to mean "don't change").
1373  * There are 9 available mode bits. Adn we need to store a total of
1374  * 32 (volume id) + 26 (vnode) + 32 (uniquifier) + 32 (data-version) + 3 (tag)
1375  * or 131 bits somewhere.
1376  *
1377  * The format of a file name for a regular file is:
1378  * /vicepX/AFSIDat/V1/V2/AA/BB/<tag><uniq><vno>
1379  * V1 - low 8 bits of RW volume id
1380  * V2 - all bits of RW volume id
1381  * AA - high 8 bits of vnode number.
1382  * BB - next 9 bits of vnode number.
1383  * <tag><uniq><vno> - file name
1384  *
1385  * Volume special files are stored in a separate directory:
1386  * /vicepX/AFSIDat/V1/V2/special/<tag><uniq><vno>
1387  *
1388  *
1389  * The vnode is hashed into the directory using the high bits of the
1390  * vnode number. This is so that consecutively created vnodes are in
1391  * roughly the same area on the disk. This will at least be optimal if
1392  * the user is creating many files in the same AFS directory. The name
1393  * should be formed so that the leading characters are different as quickly
1394  * as possible, leading to faster discards of incorrect matches in the
1395  * lookup code.
1396  *
1397  * endif
1398  *
1399  */
1400
1401
1402 /************************************************************************
1403  *  Link Table Organization
1404  ************************************************************************
1405  *
1406  * The link table volume special file is used to hold the link counts that
1407  * are held in the inodes in inode based AFS vice filesystems. For user
1408  * space access, the link counts are being kept in a separate
1409  * volume special file. The file begins with the usual version stamp
1410  * information and is then followed by one row per vnode number. vnode 0
1411  * is used to hold the link count of the link table itself. That is because
1412  * the same link table is shared among all the volumes of the volume group
1413  * and is deleted only when the last volume of a volume group is deleted.
1414  *
1415  * Within each row, the columns are 3 bits wide. They can each hold a 0 based
1416  * link count from 0 through 7. Each colume represents a unique instance of
1417  * that vnode. Say we have a file shared between the RW and a RO and a
1418  * different version of the file (or a different uniquifer) for the BU volume.
1419  * Then one column would be holding the link count of 2 for the RW and RO
1420  * and a different column would hold the link count of 1 for the BU volume.
1421  * # ifdef AFS_NT40_ENV
1422  * The column used is determined for NT by the uniquifier tag applied to
1423  * generate a unique file name in the NTFS namespace. The file name is
1424  * of the form "V_<vno>.<tag>" . And the <tag> is also the column number
1425  * in the link table.
1426  * # else
1427  * Note that we allow only 5 volumes per file, giving 15 bits used in the
1428  * short.
1429  * # endif
1430  */
1431 #define LINKTABLE_WIDTH 2
1432 #define LINKTABLE_SHIFT 1       /* log 2 = 1 */
1433
1434 /**
1435  * compute namei link table file and bit offset from inode number.
1436  *
1437  * @param[in]   ino     inode number
1438  * @param[out]  offset  link table file offset
1439  * @param[out]  index   bit offset within 2-byte record
1440  *
1441  * @internal
1442  */
1443 static void
1444 namei_GetLCOffsetAndIndexFromIno(Inode ino, afs_foff_t * offset, int *index)
1445 {
1446     int toff = (int)(ino & NAMEI_VNODEMASK);
1447     int tindex = (int)((ino >> NAMEI_TAGSHIFT) & NAMEI_TAGMASK);
1448
1449     *offset = (afs_foff_t) ((toff << LINKTABLE_SHIFT) + 8);     /* * 2 + sizeof stamp */
1450     *index = (tindex << 1) + tindex;
1451 }
1452
1453 #ifdef AFS_PTHREAD_ENV
1454 /* XXX do static initializers work for WINNT/pthread? */
1455 pthread_mutex_t _namei_glc_lock = PTHREAD_MUTEX_INITIALIZER;
1456 #define NAMEI_GLC_LOCK MUTEX_ENTER(&_namei_glc_lock)
1457 #define NAMEI_GLC_UNLOCK MUTEX_EXIT(&_namei_glc_lock)
1458 #else /* !AFS_PTHREAD_ENV */
1459 #define NAMEI_GLC_LOCK
1460 #define NAMEI_GLC_UNLOCK
1461 #endif /* !AFS_PTHREAD_ENV */
1462
1463 /**
1464  * get the link count of an inode.
1465  *
1466  * @param[in]  h        namei link count table file handle
1467  * @param[in]  ino      inode number for which we are requesting a link count
1468  * @param[in]  lockit   if asserted, return with lock held on link table file
1469  * @param[in]  fixup    if asserted, write 1 to link count when read() returns
1470  *                      zero (at EOF)
1471  * @param[in]  nowrite  return success on zero byte read or ZLC
1472  *
1473  * @post if lockit asserted and lookup was successful, will return with write
1474  *       lock on link table file descriptor
1475  *
1476  * @return link count
1477  *    @retval -1 namei link table i/o error
1478  *
1479  * @internal
1480  */
1481 int
1482 namei_GetLinkCount(FdHandle_t * h, Inode ino, int lockit, int fixup, int nowrite)
1483 {
1484     unsigned short row = 0;
1485     afs_foff_t offset;
1486     ssize_t rc;
1487     int index;
1488
1489     /* there's no linktable yet. the salvager will create one later */
1490     if (h->fd_fd == INVALID_FD && fixup)
1491        return 1;
1492     namei_GetLCOffsetAndIndexFromIno(ino, &offset, &index);
1493
1494     if (lockit) {
1495         if (FDH_LOCKFILE(h, offset) != 0)
1496             return -1;
1497     }
1498
1499     rc = FDH_PREAD(h, (char*)&row, sizeof(row), offset);
1500     if (rc == -1)
1501         goto bad_getLinkByte;
1502
1503     if ((rc == 0 || !((row >> index) & NAMEI_TAGMASK)) && fixup && nowrite)
1504         return 1;
1505     if (rc == 0 && fixup) {
1506         /*
1507          * extend link table and write a link count of 1 for ino
1508          *
1509          * in order to make MT-safe, truncation (extension really)
1510          * must happen under a mutex
1511          */
1512         NAMEI_GLC_LOCK;
1513         if (FDH_SIZE(h) >= offset+sizeof(row)) {
1514             NAMEI_GLC_UNLOCK;
1515             goto bad_getLinkByte;
1516         }
1517         FDH_TRUNC(h, offset+sizeof(row));
1518         row = 1 << index;
1519         rc = FDH_PWRITE(h, (char *)&row, sizeof(row), offset);
1520         NAMEI_GLC_UNLOCK;
1521     }
1522     if (rc != sizeof(row)) {
1523         goto bad_getLinkByte;
1524     }
1525
1526     if (fixup && !((row >> index) & NAMEI_TAGMASK)) {
1527         /*
1528          * fix up zlc
1529          *
1530          * in order to make this mt-safe, we need to do the read-modify-write
1531          * under a mutex.  thus, we repeat the read inside the lock.
1532          */
1533         NAMEI_GLC_LOCK;
1534         rc = FDH_PREAD(h, (char *)&row, sizeof(row), offset);
1535         if (rc == sizeof(row)) {
1536             row |= 1<<index;
1537             rc = FDH_PWRITE(h, (char *)&row, sizeof(row), offset);
1538         }
1539         NAMEI_GLC_UNLOCK;
1540         if (rc != sizeof(row))
1541             goto bad_getLinkByte;
1542     }
1543
1544     return (int)((row >> index) & NAMEI_TAGMASK);
1545
1546   bad_getLinkByte:
1547     if (lockit)
1548         FDH_UNLOCKFILE(h, offset);
1549     return -1;
1550 }
1551
1552 int
1553 namei_SetNonZLC(FdHandle_t * h, Inode ino)
1554 {
1555     return namei_GetLinkCount(h, ino, 0, 1, 0);
1556 }
1557
1558 /* Return a free column index for this vnode. */
1559 static int
1560 GetFreeTag(IHandle_t * ih, int vno)
1561 {
1562     FdHandle_t *fdP;
1563     afs_foff_t offset;
1564     int col;
1565     int coldata;
1566     short row;
1567     ssize_t nBytes;
1568
1569
1570     fdP = IH_OPEN(ih);
1571     if (fdP == NULL)
1572         return -1;
1573
1574     /* Only one manipulates at a time. */
1575     if (FDH_LOCKFILE(fdP, offset) != 0) {
1576         FDH_REALLYCLOSE(fdP);
1577         return -1;
1578     }
1579
1580     offset = (vno << LINKTABLE_SHIFT) + 8;      /* * 2 + sizeof stamp */
1581
1582     nBytes = FDH_PREAD(fdP, (char *)&row, sizeof(row), offset);
1583     if (nBytes != sizeof(row)) {
1584         if (nBytes != 0)
1585             goto badGetFreeTag;
1586         row = 0;
1587     }
1588
1589     /* Now find a free column in this row and claim it. */
1590     for (col = 0; col < NAMEI_MAXVOLS; col++) {
1591         coldata = 7 << (col * 3);
1592         if ((row & coldata) == 0)
1593             break;
1594     }
1595     if (col >= NAMEI_MAXVOLS) {
1596         errno = ENOSPC;
1597         goto badGetFreeTag;
1598     }
1599
1600     coldata = 1 << (col * 3);
1601     row |= coldata;
1602
1603     if (FDH_PWRITE(fdP, (char *)&row, sizeof(row), offset) != sizeof(row)) {
1604         goto badGetFreeTag;
1605     }
1606     (void)FDH_SYNC(fdP);
1607     FDH_UNLOCKFILE(fdP, offset);
1608     FDH_CLOSE(fdP);
1609     return col;
1610
1611   badGetFreeTag:
1612     FDH_UNLOCKFILE(fdP, offset);
1613     FDH_REALLYCLOSE(fdP);
1614     return -1;
1615 }
1616
1617
1618
1619 /* namei_SetLinkCount
1620  * If locked is set, assume file is locked. Otherwise, lock file before
1621  * proceeding to modify it.
1622  */
1623 int
1624 namei_SetLinkCount(FdHandle_t * fdP, Inode ino, int count, int locked)
1625 {
1626     afs_foff_t offset;
1627     int index;
1628     unsigned short row;
1629     int bytesRead;
1630     ssize_t nBytes = -1;
1631
1632     namei_GetLCOffsetAndIndexFromIno(ino, &offset, &index);
1633
1634     if (!locked) {
1635         if (FDH_LOCKFILE(fdP, offset) != 0) {
1636             return -1;
1637         }
1638     }
1639
1640     nBytes = FDH_PREAD(fdP, (char *)&row, sizeof(row), offset);
1641     if (nBytes != sizeof(row)) {
1642         if (nBytes != 0) {
1643             errno = OS_ERROR(EBADF);
1644             goto bad_SetLinkCount;
1645         }
1646         row = 0;
1647     }
1648
1649     bytesRead = 7 << index;
1650     count <<= index;
1651     row &= (unsigned short)~bytesRead;
1652     row |= (unsigned short)count;
1653
1654     if (FDH_PWRITE(fdP, (char *)&row, sizeof(short), offset) != sizeof(short)) {
1655         errno = OS_ERROR(EBADF);
1656         goto bad_SetLinkCount;
1657     }
1658     (void)FDH_SYNC(fdP);
1659
1660     nBytes = 0;
1661
1662
1663   bad_SetLinkCount:
1664     FDH_UNLOCKFILE(fdP, offset);
1665
1666     /* disallowed above 7, so... */
1667     return (int)nBytes;
1668 }
1669
1670
1671 /* ListViceInodes - write inode data to a results file. */
1672 static int DecodeInode(char *dpath, char *name, struct ViceInodeInfo *info,
1673                        unsigned int volid);
1674 static int DecodeVolumeName(char *name, unsigned int *vid);
1675 static int namei_ListAFSSubDirs(IHandle_t * dirIH,
1676                                 int (*write_fun) (FD_t,
1677                                                   struct ViceInodeInfo *,
1678                                                   char *, char *), FD_t fp,
1679                                 int (*judgeFun) (struct ViceInodeInfo *,
1680                                                  afs_uint32 vid, void *),
1681                                 afs_uint32 singleVolumeNumber, void *rock);
1682
1683
1684 /* WriteInodeInfo
1685  *
1686  * Write the inode data to the results file.
1687  *
1688  * Returns -2 on error, 0 on success.
1689  *
1690  * This is written as a callback simply so that other listing routines
1691  * can use the same inode reading code.
1692  */
1693 static int
1694 WriteInodeInfo(FD_t fp, struct ViceInodeInfo *info, char *dir, char *name)
1695 {
1696     size_t n;
1697     n = OS_WRITE(fp, info, sizeof(*info));
1698     return (n == sizeof(*info)) ? 0 : -2;
1699 }
1700
1701
1702 int mode_errors;                /* Number of errors found in mode bits on directories. */
1703 void
1704 VerifyDirPerms(char *path)
1705 {
1706     struct afs_stat_st status;
1707
1708     if (afs_stat(path, &status) < 0) {
1709         Log("Unable to stat %s. Please manually verify mode bits for this"
1710             " directory\n", path);
1711     } else {
1712         if (((status.st_mode & 0777) != 0700) || (status.st_uid != 0))
1713             mode_errors++;
1714     }
1715 }
1716
1717 /**
1718  * Fill the results file with the requested inode information.
1719  *
1720  * This code optimizes single volume salvages by just looking at that one
1721  * volume's directory.
1722  *
1723  * @param[in]   devname             device name string
1724  * @param[in]   moutnedOn           vice partition mount point
1725  * @param[in]   resultFile          result file in which to write inode
1726  *                                  metadata.  If NULL, write routine is not
1727  *                                  called.
1728  * @param[in]   judgeInode          filter function pointer.  if not NULL, only
1729  *                                  inodes for which this routine returns non-
1730  *                                  zero will be written to the results file.
1731  * @param[in]   singleVolumeNumber  volume id filter
1732  * @param[out]  forcep              always set to 0 for namei impl
1733  * @param[in]   forceR              not used by namei impl
1734  * @param[in]   wpath               not used by namei impl
1735  * @param[in]   rock                opaque pointer passed to judgeInode
1736  *
1737  * @return operation status
1738  *    @retval 0   success
1739  *    @retval -1  complete failure, salvage should terminate.
1740  *    @retval -2  not enough space on partition, salvager has error message
1741  *                for this.
1742  */
1743 int
1744 ListViceInodes(char *devname, char *mountedOn, FD_t inodeFile,
1745                int (*judgeInode) (struct ViceInodeInfo * info, afs_uint32 vid, void *rock),
1746                afs_uint32 singleVolumeNumber, int *forcep, int forceR, char *wpath,
1747                void *rock)
1748 {
1749     int ninodes;
1750
1751     *forcep = 0; /* no need to salvage until further notice */
1752
1753     /* Verify protections on directories. */
1754     mode_errors = 0;
1755     VerifyDirPerms(mountedOn);
1756
1757     ninodes =
1758         namei_ListAFSFiles(mountedOn, WriteInodeInfo, inodeFile, judgeInode,
1759                            singleVolumeNumber, rock);
1760
1761     if (inodeFile == INVALID_FD)
1762         return ninodes;
1763
1764     if (ninodes < 0) {
1765         return ninodes;
1766     }
1767
1768     if (OS_SYNC(inodeFile) == -1) {
1769         Log("Unable to successfully fsync inode file for %s\n", mountedOn);
1770         return -2;
1771     }
1772
1773     /*
1774      * Paranoia:  check that the file is really the right size
1775      */
1776     if (OS_SIZE(inodeFile) != ninodes * sizeof(struct ViceInodeInfo)) {
1777         Log("Wrong size (%d instead of %lu) in inode file for %s\n",
1778             (int) OS_SIZE(inodeFile),
1779             (long unsigned int) ninodes * sizeof(struct ViceInodeInfo),
1780             mountedOn);
1781         return -2;
1782     }
1783     return 0;
1784 }
1785
1786
1787 /**
1788  * Collect all the matching AFS files on the drive.
1789  * If singleVolumeNumber is non-zero, just return files for that volume.
1790  *
1791  * @param[in] dev                 vice partition path
1792  * @param[in] writeFun            function pointer to a function which
1793  *                                writes inode information to FILE fp
1794  * @param[in] fp                  file stream where inode metadata is sent
1795  * @param[in] judgeFun            filter function pointer.  if not NULL,
1796  *                                only entries for which a non-zero value
1797  *                                is returned are written to fp
1798  * @param[in] singleVolumeNumber  volume id filter.  if nonzero, only
1799  *                                process files for that specific volume id
1800  * @param[in] rock                opaque pointer passed into writeFun and
1801  *                                judgeFun
1802  *
1803  * @return operation status
1804  *    @retval <0 error
1805  *    @retval >=0 number of matching files found
1806  */
1807 int
1808 namei_ListAFSFiles(char *dev,
1809                    int (*writeFun) (FD_t, struct ViceInodeInfo *, char *,
1810                                     char *),
1811                    FD_t fp,
1812                    int (*judgeFun) (struct ViceInodeInfo *, afs_uint32, void *),
1813                    afs_uint32 singleVolumeNumber, void *rock)
1814 {
1815     IHandle_t ih;
1816     namei_t name;
1817     int ninodes = 0;
1818     DIR *dirp1;
1819     struct dirent *dp1;
1820 #ifndef AFS_NT40_ENV
1821     DIR *dirp2;
1822     struct dirent *dp2;
1823     char path2[512];
1824 #endif
1825 #ifdef DELETE_ZLC
1826     static void FreeZLCList(void);
1827 #endif
1828
1829     memset((void *)&ih, 0, sizeof(IHandle_t));
1830 #ifdef AFS_NT40_ENV
1831     ih.ih_dev = nt_DriveToDev(dev);
1832 #else
1833     ih.ih_dev = volutil_GetPartitionID(dev);
1834 #endif
1835
1836     if (singleVolumeNumber) {
1837         ih.ih_vid = singleVolumeNumber;
1838         namei_HandleToVolDir(&name, &ih);
1839         ninodes =
1840             namei_ListAFSSubDirs(&ih, writeFun, fp, judgeFun,
1841                                  singleVolumeNumber, rock);
1842         if (ninodes < 0)
1843             return ninodes;
1844     } else {
1845         /* Find all volume data directories and descend through them. */
1846         namei_HandleToInodeDir(&name, &ih);
1847         ninodes = 0;
1848         dirp1 = opendir(name.n_path);
1849         if (!dirp1)
1850             return 0;
1851         while ((dp1 = readdir(dirp1))) {
1852 #ifdef AFS_NT40_ENV
1853             /* Heirarchy is one level on Windows */
1854             if (!DecodeVolumeName(dp1->d_name, &ih.ih_vid)) {
1855                 ninodes +=
1856                     namei_ListAFSSubDirs(&ih, writeFun, fp, judgeFun,
1857                                          0, rock);
1858             }
1859 #else
1860             if (*dp1->d_name == '.')
1861                 continue;
1862             snprintf(path2, sizeof(path2), "%s" OS_DIRSEP "%s", name.n_path,
1863                      dp1->d_name);
1864             dirp2 = opendir(path2);
1865             if (dirp2) {
1866                 while ((dp2 = readdir(dirp2))) {
1867                     if (*dp2->d_name == '.')
1868                         continue;
1869                     if (!DecodeVolumeName(dp2->d_name, &ih.ih_vid)) {
1870                         ninodes +=
1871                             namei_ListAFSSubDirs(&ih, writeFun, fp, judgeFun,
1872                                                  0, rock);
1873                     }
1874                 }
1875                 closedir(dirp2);
1876             }
1877 #endif
1878         }
1879         closedir(dirp1);
1880     }
1881 #ifdef DELETE_ZLC
1882     FreeZLCList();
1883 #endif
1884     return ninodes;
1885 }
1886
1887 #ifdef DELETE_ZLC
1888 static void AddToZLCDeleteList(char dir, char *name);
1889 static void DeleteZLCFiles(char *path);
1890 #endif
1891
1892 /**
1893  * examine a namei volume special file.
1894  *
1895  * @param[in] path1               volume special directory path
1896  * @param[in] dname               directory entry name
1897  * @param[in] myIH                inode handle to volume directory
1898  * @param[out] linkHandle         namei link count fd handle.  if
1899  *                                the inode in question is the link
1900  *                                table, then the FdHandle is populated
1901  * @param[in] writeFun            metadata write function pointer
1902  * @param[in] fp                  file pointer where inode metadata
1903  *                                is written by (*writeFun)()
1904  * @param[in] judgeFun            inode filter function pointer.  if
1905  *                                not NULL, only inodes for which this
1906  *                                function returns non-zero are recorded
1907  *                                into fp by writeFun
1908  * @param[in] singleVolumeNumer   volume id filter.  if non-zero, only
1909  *                                inodes associated with this volume id
1910  *                                are recorded by writeFun
1911  * @param[in] rock                opaque pointer passed to writeFun and
1912  *                                judgeFun
1913  *
1914  * @return operation status
1915  *    @retval 1 count this inode
1916  *    @retval 0 don't count this inode
1917  *    @retval -1 failure
1918  *
1919  * @internal
1920  */
1921 static int
1922 _namei_examine_special(char * path1,
1923                        char * dname,
1924                        IHandle_t * myIH,
1925                        FdHandle_t * linkHandle,
1926                        int (*writeFun) (FD_t, struct ViceInodeInfo *, char *,
1927                                         char *),
1928                        FD_t fp,
1929                        int (*judgeFun) (struct ViceInodeInfo *, afs_uint32, void *),
1930                        int singleVolumeNumber,
1931                        void *rock)
1932 {
1933     int ret = 0;
1934     struct ViceInodeInfo info;
1935
1936     if (DecodeInode(path1, dname, &info, myIH->ih_vid) < 0) {
1937         ret = 0;
1938         goto error;
1939     }
1940
1941     if (info.u.param[2] != VI_LINKTABLE) {
1942         info.linkCount = 1;
1943     } else if (info.u.param[0] != myIH->ih_vid) {
1944         /* VGID encoded in linktable filename and/or OGM data isn't
1945          * consistent with VGID encoded in namei path */
1946         Log("namei_ListAFSSubDirs: warning: inconsistent linktable "
1947             "filename \"%s" OS_DIRSEP "%s\"; salvager will delete it "
1948             "(dir_vgid=%u, inode_vgid=%u)\n",
1949             path1, dname, myIH->ih_vid,
1950             info.u.param[0]);
1951     } else {
1952         char path2[512];
1953         /* Open this handle */
1954         snprintf(path2, sizeof(path2),
1955                  "%s" OS_DIRSEP "%s", path1, dname);
1956         linkHandle->fd_fd = OS_OPEN(path2, Testing ? O_RDONLY : O_RDWR, 0666);
1957         info.linkCount =
1958             namei_GetLinkCount(linkHandle, (Inode) 0, 1, 1, Testing);
1959     }
1960
1961     if (!judgeFun ||
1962         (*judgeFun) (&info, singleVolumeNumber, rock)) {
1963         ret = (*writeFun) (fp, &info, path1, dname);
1964         if (ret < 0) {
1965             Log("_namei_examine_special: writeFun returned %d\n", ret);
1966             ret = -1;
1967         } else {
1968             ret = 1;
1969         }
1970     }
1971
1972  error:
1973     return ret;
1974 }
1975
1976 /**
1977  * examine a namei file.
1978  *
1979  * @param[in] path3               volume special directory path
1980  * @param[in] dname               directory entry name
1981  * @param[in] myIH                inode handle to volume directory
1982  * @param[in] linkHandle          namei link count fd handle.
1983  * @param[in] writeFun            metadata write function pointer
1984  * @param[in] fp                  file pointer where inode metadata
1985  *                                is written by (*writeFun)()
1986  * @param[in] judgeFun            inode filter function pointer.  if
1987  *                                not NULL, only inodes for which this
1988  *                                function returns non-zero are recorded
1989  *                                into fp by writeFun
1990  * @param[in] singleVolumeNumer   volume id filter.  if non-zero, only
1991  *                                inodes associated with this volume id
1992  *                                are recorded by writeFun
1993  * @param[in] rock                opaque pointer passed to writeFun and
1994  *                                judgeFun
1995  *
1996  * @return operation status
1997  *    @retval 1 count this inode
1998  *    @retval 0 don't count this inode
1999  *    @retval -1 failure
2000  *    @retval -2 request ZLC delete
2001  *
2002  * @internal
2003  */
2004 static int
2005 _namei_examine_reg(char * path3,
2006                    char * dname,
2007                    IHandle_t * myIH,
2008                    FdHandle_t * linkHandle,
2009                    int (*writeFun) (FD_t, struct ViceInodeInfo *, char *,
2010                                     char *),
2011                    FD_t fp,
2012                    int (*judgeFun) (struct ViceInodeInfo *, afs_uint32, void *),
2013                    int singleVolumeNumber,
2014                    void *rock)
2015 {
2016     int ret = 0;
2017     struct ViceInodeInfo info;
2018 #ifdef DELETE_ZLC
2019     int dirl; /* Windows-only (one level hash dir) */
2020 #endif
2021
2022     if (DecodeInode(path3, dname, &info, myIH->ih_vid) < 0) {
2023         goto error;
2024     }
2025
2026     info.linkCount =
2027         namei_GetLinkCount(linkHandle,
2028                             info.inodeNumber, 1, 1, Testing);
2029     if (info.linkCount == 0) {
2030 #ifdef DELETE_ZLC
2031         Log("Found 0 link count file %s" OS_DIRSEP "%s, deleting it.\n", path3, dname);
2032 #ifdef AFS_SALSRV_ENV
2033         /* defer -- the AddToZLCDeleteList() interface is not MT-safe */
2034         ret = -2;
2035 #else /* !AFS_SALSRV_ENV */
2036         dirl = path3[strlen(path3)-1];
2037         AddToZLCDeleteList((char)dirl, dname);
2038 #endif /* !AFS_SALSRV_ENV */
2039 #else /* !DELETE_ZLC */
2040         Log("Found 0 link count file %s" OS_DIRSEP "%s.\n", path3,
2041             dname);
2042 #endif
2043         goto error;
2044     }
2045
2046     if (!judgeFun ||
2047         (*judgeFun) (&info, singleVolumeNumber, rock)) {
2048         ret = (*writeFun) (fp, &info, path3, dname);
2049         if (ret < 0) {
2050             Log("_namei_examine_reg: writeFun returned %d\n", ret);
2051             ret = -1;
2052         } else {
2053             ret = 1;
2054         }
2055     }
2056
2057  error:
2058     return ret;
2059 }
2060
2061 /**
2062  * listsubdirs work queue node.
2063  */
2064 struct listsubdirs_work_node {
2065 #ifdef AFS_SALSRV_ENV
2066     int *error;                         /**< *error set if an error was
2067                                          *   encountered in any listsubdirs
2068                                          *   thread. */
2069 #endif
2070
2071     IHandle_t * IH;                     /**< volume directory handle */
2072     FdHandle_t *linkHandle;             /**< namei link count fd handle. when
2073                                          *   examinining the link table special
2074                                          *   inode, this will be pointed at the
2075                                          *   link table
2076                                          */
2077     FD_t fp;                            /**< file pointer for writeFun */
2078
2079     /** function which will write inode metadata to fp */
2080     int (*writeFun) (FD_t, struct ViceInodeInfo *, char *, char *);
2081
2082     /** inode filter function */
2083     int (*judgeFun) (struct ViceInodeInfo *, afs_uint32, void *);
2084     int singleVolumeNumber;             /**< volume id filter */
2085     void * rock;                        /**< pointer passed to writeFun and judgeFun */
2086     int code;                           /**< return code from examine function */
2087     int special;                        /**< asserted when this is a volume
2088                                          *   special file */
2089 };
2090
2091 /**
2092  * simple wrapper around _namei_examine_special and _namei_examine_reg.
2093  *
2094  * @param[in] work  the struct listsubdirs_work_node for the associated
2095  *                  "list subdirs" job
2096  * @param[in] dir   the directory to examine
2097  * @param[in] filename  the filename in 'dir' to examine
2098  *
2099  * @return operation status
2100  *   @retval 1  count this inode
2101  *   @retval 0  don't count this inode
2102  *   @retval -1 failure
2103  */
2104 static_inline int
2105 _namei_examine_file(const struct listsubdirs_work_node *work, char *dir,
2106                     char *filename)
2107 {
2108     if (work->special) {
2109         return _namei_examine_special(dir, filename, work->IH,
2110                                       work->linkHandle, work->writeFun, work->fp,
2111                                       work->judgeFun, work->singleVolumeNumber,
2112                                       work->rock);
2113     } else {
2114         return _namei_examine_reg(dir, filename, work->IH,
2115                                   work->linkHandle, work->writeFun, work->fp,
2116                                   work->judgeFun, work->singleVolumeNumber,
2117                                   work->rock);
2118     }
2119 }
2120
2121
2122 #ifdef AFS_SALSRV_ENV
2123 /** @addtogroup afs_vol_salsrv_pario */
2124 /*@{*/
2125
2126 /**
2127  * arguments for the _namei_examine_file_cbk callback function.
2128  */
2129 struct listsubdirs_args {
2130     const struct listsubdirs_work_node *work; /**< arguments that are the same
2131                                                *   for all invocations of
2132                                                *   _namei_examine_file_cbk, in
2133                                                *   threads */
2134     int *result;        /**< where we can store the return code of _namei_examine_file */
2135
2136     char dir[512];      /**< directory to examine */
2137     char filename[256]; /**< filename in 'dir' to examine */
2138 };
2139
2140 /**
2141  * a node in the list of results of listsubdir jobs.
2142  */
2143 struct listsubdirs_result {
2144     struct rx_queue q;
2145     int inodes;        /**< return value from _namei_examine_file */
2146 };
2147
2148 /**
2149  * clean up a list of 'struct listsubdirs_result's and interpret the results.
2150  *
2151  * @param[in] resultlist  a list of 'struct listsubdirs_result's
2152  *
2153  * @return number of inodes found
2154  *   @retval -1  error
2155  */
2156 static int
2157 _namei_listsubdirs_cleanup_results(struct rx_queue *resultlist)
2158 {
2159     struct listsubdirs_result *res, *nres;
2160     int ret = 0;
2161
2162     for(queue_Scan(resultlist, res, nres, listsubdirs_result)) {
2163         if (ret < 0) {
2164             /* noop, retain erroneous error code */
2165         } else if (res->inodes < 0) {
2166             ret = -1;
2167         } else {
2168             ret += res->inodes;
2169         }
2170
2171         queue_Remove(res);
2172         free(res);
2173         res = NULL;
2174     }
2175
2176     return ret;
2177 }
2178
2179 /**
2180  * wait for the spawned listsubdirs jobs to finish, and return how many inodes
2181  * they found.
2182  *
2183  * @param[in] queue    queue to wait to finish
2184  * @param[in] resultlist list of 'struct listsubdirs_result's that the queued
2185  *                       jobs are storing their results in
2186  *
2187  * @return number of inodes found
2188  *   @retval -1  error
2189  */
2190 static int
2191 _namei_listsubdirs_wait(struct afs_work_queue *queue, struct rx_queue *resultlist)
2192 {
2193     int code;
2194
2195     code = afs_wq_wait_all(queue);
2196     if (code) {
2197         return -1;
2198     }
2199
2200     return _namei_listsubdirs_cleanup_results(resultlist);
2201 }
2202
2203 /**
2204  * work queue entry point for examining namei files.
2205  *
2206  * @param[in] queue        pointer to struct Vwork_queue
2207  * @param[in] node         pointer to struct Vwork_queue_node
2208  * @param[in] queue_rock   opaque pointer to struct salsrv_pool_state
2209  * @param[in] node_rock    opaque pointer to struct listsubdirs_work_node
2210  * @param[in] caller_rock  opaque pointer to struct salsrv_worker_thread_state
2211  *
2212  * @return operation status
2213  *
2214  * @see Vwork_queue_callback_func_t
2215  *
2216  * @internal
2217  */
2218 static int
2219 _namei_examine_file_cbk(struct afs_work_queue *queue,
2220                         struct afs_work_queue_node *node,
2221                         void *queue_rock,
2222                         void *node_rock,
2223                         void *caller_rock)
2224 {
2225     int code;
2226     struct listsubdirs_args *args = node_rock;
2227     const struct listsubdirs_work_node * work = args->work;
2228     char *dir = args->dir;
2229     char *filename = args->filename;
2230
2231     code = _namei_examine_file(work, dir, filename);
2232
2233     *(args->result) = code;
2234
2235     if (code < 0) {
2236         *(work->error) = 1;
2237         /* we've errored, so no point in letting more jobs continue */
2238         afs_wq_shutdown(queue);
2239     }
2240
2241     return 0;
2242 }
2243
2244 static pthread_once_t wq_once = PTHREAD_ONCE_INIT;
2245 static pthread_key_t wq_key;
2246
2247 /**
2248  * create the wq_key key for storing a work queue.
2249  */
2250 static void
2251 _namei_wq_keycreate(void)
2252 {
2253     opr_Verify(pthread_key_create(&wq_key, NULL) == 0);
2254 }
2255
2256 /**
2257  * set the work queue for this thread to use for backgrounding namei ops.
2258  *
2259  * The work queue will be used in ListAFSSubdirs (called indirectly by
2260  * ListViceInodes) to examine files in parallel.
2261  *
2262  * @param[in] wq  the work queue to use
2263  */
2264 void
2265 namei_SetWorkQueue(struct afs_work_queue *wq)
2266 {
2267     opr_Verify(pthread_once(&wq_once, _namei_wq_keycreate) == 0);
2268
2269     opr_Verify(pthread_setspecific(wq_key, wq) == 0);
2270 }
2271
2272 /**
2273  * enqueue an examine file work unit.
2274  *
2275  * @param[in] work     the _namei_examine_file arguments that are common to
2276  *                     all callers within the same ListAFSFiles operation
2277  * @param[in] dir      the specific directory to look at (string will be
2278  *                     copied; can be stack/temporary memory)
2279  * @param[in] filename the filename to look at (string will be copied; can be
2280  *                     stack/temporary memory)
2281  * @param[in] wq       work queue to enqueue this work unit to
2282  * @param[in] resultlist the list to append the 'struct listsubdirs_result' to
2283  *                       for the enqueued work unit
2284  *
2285  * @return operation status
2286  *    @retval 0 success
2287  *    @retval -1 fatal error
2288  *
2289  * @note errors MUST be indicated by a -1 error code and nothing else, to be
2290  *       compatible with _namei_examine_reg and _namei_examine_special
2291  *
2292  * @internal
2293  */
2294 static int
2295 _namei_examine_file_spawn(const struct listsubdirs_work_node *work,
2296                           const char *dir, const char *filename,
2297                           struct afs_work_queue *wq,
2298                           struct rx_queue *resultlist)
2299 {
2300     int code, ret = 0;
2301     struct listsubdirs_args *args = NULL;
2302     struct listsubdirs_result *result = NULL;
2303     struct afs_work_queue_node *node = NULL;
2304     struct afs_work_queue_add_opts opts;
2305
2306     args = malloc(sizeof(*args));
2307     if (args == NULL) {
2308         ret = -1;
2309         goto error;
2310     }
2311
2312     result = malloc(sizeof(*result));
2313     if (result == NULL) {
2314         ret = -1;
2315         goto error;
2316     }
2317
2318     code = afs_wq_node_alloc(&node);
2319     if (code) {
2320         ret = -1;
2321         goto error;
2322     }
2323     code = afs_wq_node_set_detached(node);
2324     if (code) {
2325         ret = -1;
2326         goto error;
2327     }
2328
2329     args->work = work;
2330     args->result = &result->inodes;
2331     strlcpy(args->dir, dir, sizeof(args->dir));
2332     strlcpy(args->filename, filename, sizeof(args->filename));
2333
2334     code = afs_wq_node_set_callback(node,
2335                                          &_namei_examine_file_cbk,
2336                                          args, &free);
2337     if (code) {
2338         ret = -1;
2339         goto error;
2340     }
2341     args = NULL;
2342
2343     afs_wq_add_opts_init(&opts);
2344     opts.donate = 1;
2345
2346     code = afs_wq_add(wq, node, &opts);
2347     if (code) {
2348         ret = -1;
2349         goto error;
2350     }
2351     node = NULL;
2352
2353     queue_Append(resultlist, result);
2354     result = NULL;
2355
2356  error:
2357     if (node) {
2358         afs_wq_node_put(node);
2359         node = NULL;
2360     }
2361     if (args) {
2362         free(args);
2363         args = NULL;
2364     }
2365     if (result) {
2366         free(result);
2367         result = NULL;
2368     }
2369
2370     return ret;
2371 }
2372
2373 /*@}*/
2374 #else /* !AFS_SALSRV_ENV */
2375 # define _namei_examine_file_spawn(work, dir, file, wq, resultlist) \
2376          _namei_examine_file(work, dir, file)
2377 #endif /* !AFS_SALSRV_ENV */
2378
2379 /**
2380  * traverse and check inodes.
2381  *
2382  * @param[in] dirIH               volume group directory handle
2383  * @param[in] writeFun            function pointer which will write inode
2384  *                                metadata to FILE stream fp
2385  * @param[in] fp                  file stream where inode metadata gets
2386  *                                written
2387  * @param[in] judgeFun            inode filter function.  if not NULL, only
2388  *                                inodes for which the filter returns non-zero
2389  *                                will be written out by writeFun
2390  * @param[in] singleVolumeNumber  volume id filter.  only inodes matching this
2391  *                                filter are written out by writeFun
2392  * @param[in] rock                opaque pointer passed to judgeFun and writeFun
2393  *
2394  * @return operation status
2395  *    @retval <0 error
2396  *    @retval >=0 number of matching inodes found
2397  *
2398  * @todo the salsrv implementation may consume a lot of
2399  *       memory for a large volume.  at some point we should
2400  *       probably write a background thread to asynchronously
2401  *       clean up the resultlist nodes to reduce memory footprint
2402  *
2403  * @internal
2404  */
2405 static int
2406 namei_ListAFSSubDirs(IHandle_t * dirIH,
2407                      int (*writeFun) (FD_t, struct ViceInodeInfo *, char *,
2408                                       char *),
2409                      FD_t fp,
2410                      int (*judgeFun) (struct ViceInodeInfo *, afs_uint32, void *),
2411                      afs_uint32 singleVolumeNumber, void *rock)
2412 {
2413     int code = 0, ret = 0;
2414     IHandle_t myIH = *dirIH;
2415     namei_t name;
2416     char path1[512], path3[512];
2417     DIR *dirp1, *dirp3;
2418 #ifndef AFS_NT40_ENV
2419     DIR *dirp2;
2420     struct dirent *dp2;
2421     char path2[512];
2422 #endif
2423     struct dirent *dp1, *dp3;
2424     FdHandle_t linkHandle;
2425     int ninodes = 0;
2426     struct listsubdirs_work_node work;
2427 #ifdef AFS_SALSRV_ENV
2428     int error = 0;
2429     struct afs_work_queue *wq;
2430     int wq_up = 0;
2431     struct rx_queue resultlist;
2432 #endif
2433
2434     namei_HandleToVolDir(&name, &myIH);
2435     strlcpy(path1, name.n_path, sizeof(path1));
2436
2437     /* Do the directory containing the special files first to pick up link
2438      * counts.
2439      */
2440     (void)strcat(path1, OS_DIRSEP);
2441     (void)strcat(path1, NAMEI_SPECDIR);
2442
2443     linkHandle.fd_fd = INVALID_FD;
2444 #ifdef AFS_SALSRV_ENV
2445     opr_Verify(pthread_once(&wq_once, _namei_wq_keycreate) == 0);
2446
2447     wq = pthread_getspecific(wq_key);
2448     if (!wq) {
2449         ret = -1;
2450         goto error;
2451     }
2452     wq_up = 1;
2453     queue_Init(&resultlist);
2454 #endif
2455
2456     memset(&work, 0, sizeof(work));
2457     work.linkHandle = &linkHandle;
2458     work.IH = &myIH;
2459     work.fp = fp;
2460     work.writeFun = writeFun;
2461     work.judgeFun = judgeFun;
2462     work.singleVolumeNumber = singleVolumeNumber;
2463     work.rock = rock;
2464     work.special = 1;
2465 #ifdef AFS_SALSRV_ENV
2466     work.error = &error;
2467 #endif
2468
2469     dirp1 = opendir(path1);
2470     if (dirp1) {
2471         while ((dp1 = readdir(dirp1))) {
2472             if (*dp1->d_name == '.')
2473                 continue;
2474
2475 #ifdef AFS_SALSRV_ENV
2476             if (error) {
2477                 closedir(dirp1);
2478                 ret = -1;
2479                 goto error;
2480             }
2481 #endif /* AFS_SALSRV_ENV */
2482
2483             code = _namei_examine_file_spawn(&work, path1, dp1->d_name, wq, &resultlist);
2484
2485             switch (code) {
2486             case -1:
2487                 /* fatal error */
2488                 closedir(dirp1);
2489                 ret = -1;
2490                 goto error;
2491
2492             case 1:
2493                 /* count this inode */
2494 #ifndef AFS_SALSRV_ENV
2495                 ninodes++;
2496 #endif
2497                 break;
2498             }
2499         }
2500         closedir(dirp1);
2501     }
2502
2503 #ifdef AFS_SALSRV_ENV
2504     /* effectively a barrier */
2505     code = _namei_listsubdirs_wait(wq, &resultlist);
2506     if (code < 0 || error) {
2507         ret = -1;
2508         goto error;
2509     }
2510     error = 0;
2511     ninodes += code;
2512 #endif
2513
2514     if (linkHandle.fd_fd == INVALID_FD) {
2515         Log("namei_ListAFSSubDirs: warning: VG %u does not have a link table; "
2516             "salvager will recreate it.\n", dirIH->ih_vid);
2517     }
2518
2519     /* Now run through all the other subdirs */
2520     namei_HandleToVolDir(&name, &myIH);
2521     strlcpy(path1, name.n_path, sizeof(path1));
2522
2523     work.special = 0;
2524
2525     dirp1 = opendir(path1);
2526     if (dirp1) {
2527         while ((dp1 = readdir(dirp1))) {
2528 #ifndef AFS_NT40_ENV
2529             if (*dp1->d_name == '.')
2530                 continue;
2531 #endif
2532             if (!strcmp(dp1->d_name, NAMEI_SPECDIR))
2533                 continue;
2534
2535 #ifndef AFS_NT40_ENV /* This level missing on Windows */
2536             /* Now we've got a next level subdir. */
2537             snprintf(path2, sizeof(path2), "%s" OS_DIRSEP "%s",
2538                      path1, dp1->d_name);
2539             dirp2 = opendir(path2);
2540             if (dirp2) {
2541                 while ((dp2 = readdir(dirp2))) {
2542                     if (*dp2->d_name == '.')
2543                         continue;
2544
2545                     /* Now we've got to the actual data */
2546                     snprintf(path3, sizeof(path3), "%s" OS_DIRSEP "%s",
2547                              path2, dp2->d_name);
2548 #else
2549                     /* Now we've got to the actual data */
2550                     snprintf(path3, sizeof(path3), "%s" OS_DIRSEP "%s",
2551                              path1, dp1->d_name);
2552 #endif
2553                     dirp3 = opendir(path3);
2554                     if (dirp3) {
2555                         while ((dp3 = readdir(dirp3))) {
2556 #ifndef AFS_NT40_ENV
2557                             if (*dp3->d_name == '.')
2558                                 continue;
2559 #endif
2560
2561 #ifdef AFS_SALSRV_ENV
2562                             if (error) {
2563                                 closedir(dirp3);
2564 #ifndef AFS_NT40_ENV
2565                                 closedir(dirp2);
2566 #endif
2567                                 closedir(dirp1);
2568                                 ret = -1;
2569                                 goto error;
2570                             }
2571 #endif /* AFS_SALSRV_ENV */
2572
2573                             code = _namei_examine_file_spawn(&work, path3,
2574                                                              dp3->d_name, wq,
2575                                                              &resultlist);
2576
2577                             switch (code) {
2578                             case -1:
2579                                 closedir(dirp3);
2580 #ifndef AFS_NT40_ENV
2581                                 closedir(dirp2);
2582 #endif
2583                                 closedir(dirp1);
2584                                 ret = -1;
2585                                 goto error;
2586
2587                             case 1:
2588 #ifndef AFS_SALSRV_ENV
2589                                 ninodes++;
2590 #endif
2591                                 break;
2592                             }
2593                         }
2594                         closedir(dirp3);
2595                     }
2596 #ifndef AFS_NT40_ENV /* This level missing on Windows */
2597                 }
2598                 closedir(dirp2);
2599             }
2600 #endif
2601         }
2602         closedir(dirp1);
2603     }
2604
2605 #ifdef AFS_SALSRV_ENV
2606     /* effectively a barrier */
2607     code = _namei_listsubdirs_wait(wq, &resultlist);
2608     if (code < 0 || error) {
2609         ret = -1;
2610         goto error;
2611     }
2612     error = 0;
2613     ninodes += code;
2614     wq_up = 0;
2615 #endif
2616
2617     if (!ninodes) {
2618         /* Then why does this directory exist? Blow it away. */
2619         namei_HandleToVolDir(&name, dirIH);
2620         namei_RemoveDataDirectories(&name);
2621     }
2622
2623  error:
2624 #ifdef AFS_SALSRV_ENV
2625     if (wq_up) {
2626         afs_wq_wait_all(wq);
2627     }
2628     _namei_listsubdirs_cleanup_results(&resultlist);
2629 #endif
2630     if (linkHandle.fd_fd != INVALID_FD)
2631         OS_CLOSE(linkHandle.fd_fd);
2632
2633     if (!ret) {
2634         ret = ninodes;
2635     }
2636     return ret;
2637 }
2638
2639 /*@}*/
2640
2641 #ifdef AFS_NT40_ENV
2642 static int
2643 DecodeVolumeName(char *name, unsigned int *vid)
2644 {
2645     /* Name begins with "Vol_" and ends with .data.  See nt_HandleToVolDir() */
2646     char stmp[32];
2647     size_t len;
2648
2649     len = strlen(name);
2650     if (len <= 9)
2651         return -1;
2652     if (strncmp(name, "Vol_", 4))
2653         return -1;
2654     if (strcmp(name + len - 5, ".data"))
2655         return -1;
2656     strcpy(stmp, name);
2657     stmp[len - 5] = '\0';
2658     *vid = base32_to_int(stmp + 4);
2659     return 0;
2660 }
2661 #else
2662 static int
2663 DecodeVolumeName(char *name, unsigned int *vid)
2664 {
2665     if (strlen(name) < 1)
2666         return -1;
2667     *vid = (unsigned int)flipbase64_to_int64(name);
2668     return 0;
2669 }
2670 #endif
2671
2672
2673 /* DecodeInode
2674  *
2675  * Get the inode number from the name.
2676  *
2677  */
2678 #ifdef AFS_NT40_ENV
2679 static int
2680 DecodeInode(char *dpath, char *name, struct ViceInodeInfo *info,
2681             unsigned int volid)
2682 {
2683     char fpath[512];
2684     int tag, vno;
2685     WIN32_FIND_DATA data;
2686     HANDLE dirH;
2687     char *s, *t;
2688     char stmp[16];
2689     FdHandle_t linkHandle;
2690     char dirl;
2691
2692     snprintf(fpath, sizeof(fpath), "%s" OS_DIRSEP "%s", dpath, name);
2693
2694     dirH = FindFirstFileEx(fpath, FindExInfoStandard, &data,
2695                            FindExSearchNameMatch, NULL,
2696                            FIND_FIRST_EX_CASE_SENSITIVE);
2697     if (dirH == INVALID_HANDLE_VALUE)
2698         return -1;
2699
2700     (void)strcpy(stmp, name);
2701     s = strrchr(stmp, '_');
2702     if (!s)
2703         return -1;
2704     s++;
2705     t = strrchr(s, '.');
2706     if (!t)
2707         return -1;
2708
2709     *t = '\0';
2710     vno = base32_to_int(s);     /* type for special files */
2711     tag = base32_to_int(t+1);
2712     info->inodeNumber = ((Inode) tag) << NAMEI_TAGSHIFT;
2713     info->inodeNumber |= vno;
2714     info->byteCount = data.nFileSizeLow;
2715
2716     dirl = dpath[strlen(dpath)-1];
2717     if (dirl == NAMEI_SPECDIRC) { /* Special inode. */
2718         info->inodeNumber |= NAMEI_INODESPECIAL;
2719         info->u.param[0] = data.ftCreationTime.dwHighDateTime;
2720         info->u.param[1] = data.ftCreationTime.dwLowDateTime;
2721         info->u.param[2] = vno; /* type */
2722         info->u.param[3] = volid;
2723         if (vno != VI_LINKTABLE)
2724             info->linkCount = 1;
2725         else {
2726             /* Open this handle */
2727             char lpath[1024];
2728             (void)sprintf(lpath, "%s" OS_DIRSEP "%s", fpath, data.cFileName);
2729             linkHandle.fd_fd = OS_OPEN(lpath, O_RDONLY, 0666);
2730             info->linkCount =
2731                 namei_GetLinkCount(&linkHandle, (Inode) 0, 0, 0, 0);
2732         }
2733     } else {
2734         info->linkCount =
2735             namei_GetLinkCount(&linkHandle, info->inodeNumber, 0, 0, 0);
2736         if (info->linkCount == 0) {
2737 #ifdef DELETE_ZLC
2738             Log("Found 0 link count file %s" OS_DIRSEP "%s, deleting it.\n",
2739                 fpath, data.cFileName);
2740             AddToZLCDeleteList(dirl, data.cFileName);
2741 #else
2742             Log("Found 0 link count file %s" OS_DIRSEP "%s.\n", path,
2743                 data.cFileName);
2744 #endif
2745         } else {
2746             info->u.param[2] = data.ftCreationTime.dwHighDateTime;
2747             info->u.param[3] = data.ftCreationTime.dwLowDateTime;
2748             info->u.param[1] = vno;
2749             info->u.param[0] = volid;
2750         }
2751     }
2752     return 0;
2753 }
2754 #else
2755 static int
2756 DecodeInode(char *dpath, char *name, struct ViceInodeInfo *info,
2757             unsigned int volid)
2758 {
2759     char fpath[512];
2760     struct afs_stat_st status;
2761     int parm, tag;
2762     lb64_string_t check;
2763
2764     snprintf(fpath, sizeof(fpath), "%s" OS_DIRSEP "%s", dpath, name);
2765
2766     if (afs_stat(fpath, &status) < 0) {
2767         return -1;
2768     }
2769
2770     info->byteCount = status.st_size;
2771     info->inodeNumber = (Inode) flipbase64_to_int64(name);
2772
2773     int64_to_flipbase64(check, info->inodeNumber);
2774     if (strcmp(name, check))
2775         return -1;
2776
2777     if ((info->inodeNumber & NAMEI_INODESPECIAL) == NAMEI_INODESPECIAL) {
2778         parm = ((info->inodeNumber >> NAMEI_UNIQSHIFT) & NAMEI_UNIQMASK);
2779         tag = (int)((info->inodeNumber >> NAMEI_TAGSHIFT) & NAMEI_TAGMASK);
2780
2781         /* p1 - vid, p2 - -1, p3 - type, p4 - rwvid */
2782         info->u.param[0] = parm;
2783         info->u.param[1] = -1;
2784         info->u.param[2] = tag;
2785         info->u.param[3] = volid;
2786     } else {
2787         GetOGMFromStat(&status, &parm, &tag);
2788         /* p1 - vid, p2 - vno, p3 - uniq, p4 - dv */
2789         info->u.param[0] = volid;
2790         info->u.param[1] = (int)(info->inodeNumber & NAMEI_VNODEMASK);
2791         info->u.param[2] = (int)((info->inodeNumber >> NAMEI_UNIQSHIFT)
2792                                  & (Inode) NAMEI_UNIQMASK);
2793         info->u.param[3] = parm;
2794     }
2795     return 0;
2796 }
2797 #endif
2798
2799 /*
2800  * Convert the VolumeInfo file from RO to RW
2801  * this routine is called by namei_convertROtoRWvolume()
2802  */
2803
2804 #ifdef FSSYNC_BUILD_CLIENT
2805 static afs_int32
2806 convertVolumeInfo(FD_t fdr, FD_t fdw, afs_uint32 vid)
2807 {
2808     struct VolumeDiskData vd;
2809     char *p;
2810
2811     if (OS_READ(fdr, &vd, sizeof(struct VolumeDiskData)) !=
2812         sizeof(struct VolumeDiskData)) {
2813         Log("1 convertVolumeInfo: read failed for %lu with code %d\n",
2814             afs_printable_uint32_lu(vid),
2815             errno);
2816         return -1;
2817     }
2818     vd.restoredFromId = vd.id;  /* remember the RO volume here */
2819     vd.cloneId = vd.id;
2820     vd.id = vd.parentId;
2821     vd.type = RWVOL;
2822     vd.dontSalvage = 0;
2823     vd.inUse = 0;
2824     vd.uniquifier += 5000;      /* just in case there are still file copies from
2825                                  * the old RW volume around */
2826
2827     /* For ROs, the copyDate contains the time that the RO volume was actually
2828      * created, and the creationDate just contains the last time the RO was
2829      * copied from the RW data. So, make the new RW creationDate more accurate
2830      * by setting it to copyDate, if copyDate is older. Since, we know the
2831      * volume is at least as old as copyDate. */
2832     if (vd.copyDate < vd.creationDate) {
2833         vd.creationDate = vd.copyDate;
2834     } else {
2835         /* If copyDate is newer, just make copyDate and creationDate the same,
2836          * for consistency with other RWs */
2837         vd.copyDate = vd.creationDate;
2838     }
2839
2840     p = strrchr(vd.name, '.');
2841     if (p && !strcmp(p, ".readonly")) {
2842         memset(p, 0, 9);
2843     }
2844     if (OS_WRITE(fdw, &vd, sizeof(struct VolumeDiskData)) !=
2845         sizeof(struct VolumeDiskData)) {
2846         Log("1 convertVolumeInfo: write failed for %lu with code %d\n",
2847             afs_printable_uint32_lu(vid),
2848             errno);
2849         return -1;
2850     }
2851     return 0;
2852 }
2853 #endif
2854
2855 /*
2856  * Convert a RO-volume into a RW-volume
2857  *
2858  * This function allows to recover very fast from the loss of a partition
2859  * from RO-copies if all RO-Copies exist on another partition.
2860  * Then these RO-volumes can be made to the new RW-volumes.
2861  * Backup of RW-volumes then consists in "vos release".
2862  *
2863  * We must make sure in this partition exists only the RO-volume which
2864  * is typical for remote replicas.
2865  *
2866  * Then the linktable is already ok,
2867  *      the vnode files need to be renamed
2868  *      the volinfo file needs to be replaced by another one with
2869  *                      slightly different contents and new name.
2870  * The volume header file of the RO-volume in the /vicep<x> directory
2871  * is destroyed by this call. A new header file for the RW-volume must
2872  * be created after return from this routine.
2873  */
2874
2875 int
2876 namei_ConvertROtoRWvolume(char *pname, afs_uint32 volumeId)
2877 {
2878     int code = 0;
2879 #ifdef FSSYNC_BUILD_CLIENT
2880     namei_t n;
2881     char dir_name[512], oldpath[512], newpath[512];
2882     char smallName[64];
2883     char largeName[64];
2884     char infoName[64];
2885     IHandle_t t_ih;
2886     IHandle_t *ih;
2887     char infoSeen = 0;
2888     char smallSeen = 0;
2889     char largeSeen = 0;
2890     char linkSeen = 0;
2891     FD_t fd, fd2;
2892     char *p;
2893     DIR *dirp;
2894     Inode ino;
2895     struct dirent *dp;
2896     struct DiskPartition64 *partP;
2897     struct ViceInodeInfo info;
2898     struct VolumeDiskHeader h;
2899     char *rwpart, *rwname;
2900     Error ec;
2901 # ifdef AFS_DEMAND_ATTACH_FS
2902     int locktype = 0;
2903 # endif /* AFS_DEMAND_ATTACH_FS */
2904
2905     for (partP = DiskPartitionList; partP && strcmp(partP->name, pname);
2906          partP = partP->next);
2907     if (!partP) {
2908         Log("1 namei_ConvertROtoRWvolume: Couldn't find DiskPartition for %s\n", pname);
2909         code = EIO;
2910         goto done;
2911     }
2912
2913 # ifdef AFS_DEMAND_ATTACH_FS
2914     locktype = VVolLockType(V_VOLUPD, 1);
2915     code = VLockVolumeByIdNB(volumeId, partP, locktype);
2916     if (code) {
2917         locktype = 0;
2918         code = EIO;
2919         goto done;
2920     }
2921 # endif /* AFS_DEMAND_ATTACH_FS */
2922
2923     if (VReadVolumeDiskHeader(volumeId, partP, &h)) {
2924         Log("1 namei_ConvertROtoRWvolume: Couldn't read header for RO-volume %lu.\n",
2925             afs_printable_uint32_lu(volumeId));
2926         code = EIO;
2927         goto done;
2928     }
2929
2930     /* check for existing RW on any partition on this server; */
2931     /* if found, return EXDEV - invalid cross-device link */
2932     VOL_LOCK;
2933     VGetVolumePath(&ec, h.parent, &rwpart, &rwname);
2934     if (ec == 0) {
2935         Log("1 namei_ConvertROtoRWvolume: RW volume %lu already exists on server partition %s.\n",
2936             afs_printable_uint32_lu(h.parent), rwpart);
2937         code = EXDEV;
2938         VOL_UNLOCK;
2939         goto done;
2940     }
2941     VOL_UNLOCK;
2942
2943     FSYNC_VolOp(volumeId, pname, FSYNC_VOL_BREAKCBKS, 0, NULL);
2944
2945     ino = namei_MakeSpecIno(h.parent, VI_LINKTABLE);
2946     IH_INIT(ih, partP->device, h.parent, ino);
2947
2948     namei_HandleToName(&n, ih);
2949     strlcpy(dir_name, n.n_path, sizeof(dir_name));
2950     p = strrchr(dir_name, OS_DIRSEPC);
2951     *p = 0;
2952     dirp = opendir(dir_name);
2953     if (!dirp) {
2954         Log("1 namei_ConvertROtoRWvolume: Could not opendir(%s)\n", dir_name);
2955         code = EIO;
2956         goto done;
2957     }
2958
2959     while ((dp = readdir(dirp))) {
2960         /* struct ViceInodeInfo info; */
2961 #ifndef AFS_NT40_ENV
2962         if (*dp->d_name == '.')
2963             continue;
2964 #endif
2965         if (DecodeInode(dir_name, dp->d_name, &info, ih->ih_vid) < 0) {
2966             Log("1 namei_ConvertROtoRWvolume: DecodeInode failed for %s" OS_DIRSEP "%s\n",
2967                 dir_name, dp->d_name);
2968             closedir(dirp);
2969             code = -1;
2970             goto done;
2971         }
2972         if (info.u.param[1] != -1) {
2973             Log("1 namei_ConvertROtoRWvolume: found other than volume special file %s" OS_DIRSEP "%s\n", dir_name, dp->d_name);
2974             closedir(dirp);
2975             code = -1;
2976             goto done;
2977         }
2978         if (info.u.param[0] != volumeId) {
2979             if (info.u.param[0] == ih->ih_vid) {
2980                 if (info.u.param[2] == VI_LINKTABLE) {  /* link table */
2981                     linkSeen = 1;
2982                     continue;
2983                 }
2984             }
2985             Log("1 namei_ConvertROtoRWvolume: found special file %s" OS_DIRSEP "%s"
2986                 " for volume %lu\n", dir_name, dp->d_name,
2987                 afs_printable_uint32_lu(info.u.param[0]));
2988             closedir(dirp);
2989             code = VVOLEXISTS;
2990             goto done;
2991         }
2992         if (info.u.param[2] == VI_VOLINFO) {    /* volume info file */
2993             strlcpy(infoName, dp->d_name, sizeof(infoName));
2994             infoSeen = 1;
2995         } else if (info.u.param[2] == VI_SMALLINDEX) {  /* small vnodes file */
2996             strlcpy(smallName, dp->d_name, sizeof(smallName));
2997             smallSeen = 1;
2998         } else if (info.u.param[2] == VI_LARGEINDEX) {  /* large vnodes file */
2999             strlcpy(largeName, dp->d_name, sizeof(largeName));
3000             largeSeen = 1;
3001         } else {
3002             closedir(dirp);
3003             Log("1 namei_ConvertROtoRWvolume: unknown type %d of special file found : %s" OS_DIRSEP "%s\n", info.u.param[2], dir_name, dp->d_name);
3004             code = -1;
3005             goto done;
3006         }
3007     }
3008     closedir(dirp);
3009
3010     if (!infoSeen || !smallSeen || !largeSeen || !linkSeen) {
3011         Log("1 namei_ConvertROtoRWvolume: not all special files found in %s\n", dir_name);
3012         code = -1;
3013         goto done;
3014     }
3015
3016     /*
3017      * If we come here then there was only a RO-volume and we can safely
3018      * proceed.
3019      */
3020
3021     memset(&t_ih, 0, sizeof(t_ih));
3022     t_ih.ih_dev = ih->ih_dev;
3023     t_ih.ih_vid = ih->ih_vid;
3024
3025     snprintf(oldpath, sizeof oldpath, "%s" OS_DIRSEP "%s", dir_name,
3026              infoName);
3027     fd = OS_OPEN(oldpath, O_RDWR, 0);
3028     if (fd == INVALID_FD) {
3029         Log("1 namei_ConvertROtoRWvolume: could not open RO info file: %s\n",
3030             oldpath);
3031         code = -1;
3032         goto done;
3033     }
3034     t_ih.ih_ino = namei_MakeSpecIno(ih->ih_vid, VI_VOLINFO);
3035     namei_HandleToName(&n, &t_ih);
3036     fd2 = OS_OPEN(n.n_path, O_CREAT | O_EXCL | O_RDWR, 0);
3037     if (fd2 == INVALID_FD) {
3038         Log("1 namei_ConvertROtoRWvolume: could not create RW info file: %s\n", n.n_path);
3039         OS_CLOSE(fd);
3040         code = -1;
3041         goto done;
3042     }
3043     code = convertVolumeInfo(fd, fd2, ih->ih_vid);
3044     OS_CLOSE(fd);
3045     if (code) {
3046         OS_CLOSE(fd2);
3047         OS_UNLINK(n.n_path);
3048         code = -1;
3049         goto done;
3050     }
3051     SetOGM(fd2, ih->ih_vid, 1);
3052     OS_CLOSE(fd2);
3053
3054     t_ih.ih_ino = namei_MakeSpecIno(ih->ih_vid, VI_SMALLINDEX);
3055     namei_HandleToName(&n, &t_ih);
3056     snprintf(newpath, sizeof newpath, "%s" OS_DIRSEP "%s", dir_name,
3057              smallName);
3058     fd = OS_OPEN(newpath, O_RDWR, 0);
3059     if (fd == INVALID_FD) {
3060         Log("1 namei_ConvertROtoRWvolume: could not open SmallIndex file: %s\n", newpath);
3061         code = -1;
3062         goto done;
3063     }
3064     SetOGM(fd, ih->ih_vid, 2);
3065     OS_CLOSE(fd);
3066 #ifdef AFS_NT40_ENV
3067     MoveFileEx(n.n_path, newpath, MOVEFILE_WRITE_THROUGH);
3068 #else
3069     link(newpath, n.n_path);
3070     OS_UNLINK(newpath);
3071 #endif
3072
3073     t_ih.ih_ino = namei_MakeSpecIno(ih->ih_vid, VI_LARGEINDEX);
3074     namei_HandleToName(&n, &t_ih);
3075     snprintf(newpath, sizeof newpath, "%s" OS_DIRSEP "%s", dir_name,
3076              largeName);
3077     fd = OS_OPEN(newpath, O_RDWR, 0);
3078     if (fd == INVALID_FD) {
3079         Log("1 namei_ConvertROtoRWvolume: could not open LargeIndex file: %s\n", newpath);
3080         code = -1;
3081         goto done;
3082     }
3083     SetOGM(fd, ih->ih_vid, 3);
3084     OS_CLOSE(fd);
3085 #ifdef AFS_NT40_ENV
3086     MoveFileEx(n.n_path, newpath, MOVEFILE_WRITE_THROUGH);
3087 #else
3088     link(newpath, n.n_path);
3089     OS_UNLINK(newpath);
3090 #endif
3091
3092     OS_UNLINK(oldpath);
3093
3094     h.id = h.parent;
3095     h.volumeInfo_hi = h.id;
3096     h.smallVnodeIndex_hi = h.id;
3097     h.largeVnodeIndex_hi = h.id;
3098     h.linkTable_hi = h.id;
3099
3100     if (VCreateVolumeDiskHeader(&h, partP)) {
3101         Log("1 namei_ConvertROtoRWvolume: Couldn't write header for RW-volume %lu\n",
3102             afs_printable_uint32_lu(h.id));
3103         code = EIO;
3104         goto done;
3105     }
3106
3107     if (VDestroyVolumeDiskHeader(partP, volumeId, h.parent)) {
3108         Log("1 namei_ConvertROtoRWvolume: Couldn't unlink header for RO-volume %lu\n",
3109             afs_printable_uint32_lu(volumeId));
3110     }
3111
3112     FSYNC_VolOp(volumeId, pname, FSYNC_VOL_DONE, 0, NULL);
3113     FSYNC_VolOp(h.id, pname, FSYNC_VOL_ON, 0, NULL);
3114
3115  done:
3116 # ifdef AFS_DEMAND_ATTACH_FS
3117     if (locktype) {
3118         VUnlockVolumeById(volumeId, partP);
3119     }
3120 # endif /* AFS_DEMAND_ATTACH_FS */
3121 #endif
3122
3123     return code;
3124 }
3125
3126 /* PrintInode
3127  *
3128  * returns a static string used to print either 32 or 64 bit inode numbers.
3129  */
3130 char *
3131 PrintInode(char *s, Inode ino)
3132 {
3133     static afs_ino_str_t result;
3134     if (!s)
3135         s = result;
3136
3137     snprintf(s, sizeof(afs_ino_str_t), "%llu", (afs_uintmax_t) ino);
3138
3139     return s;
3140 }
3141
3142
3143 #ifdef DELETE_ZLC
3144 /* Routines to facilitate removing zero link count files. */
3145 #define MAX_ZLC_NAMES 32
3146 #define MAX_ZLC_NAMELEN 16
3147 typedef struct zlcList_s {
3148     struct zlcList_s *zlc_next;
3149     int zlc_n;
3150     char zlc_names[MAX_ZLC_NAMES][MAX_ZLC_NAMELEN];
3151 } zlcList_t;
3152
3153 static zlcList_t *zlcAnchor = NULL;
3154 static zlcList_t *zlcCur = NULL;
3155
3156 static void
3157 AddToZLCDeleteList(char dir, char *name)
3158 {
3159     opr_Assert(strlen(name) <= MAX_ZLC_NAMELEN - 3);
3160
3161     if (!zlcCur || zlcCur->zlc_n >= MAX_ZLC_NAMES) {
3162         if (zlcCur && zlcCur->zlc_next)
3163             zlcCur = zlcCur->zlc_next;
3164         else {
3165             zlcList_t *tmp = malloc(sizeof(zlcList_t));
3166             if (!tmp)
3167                 return;
3168             if (!zlcAnchor) {
3169                 zlcAnchor = tmp;
3170             } else {
3171                 zlcCur->zlc_next = tmp;
3172             }
3173             zlcCur = tmp;
3174             zlcCur->zlc_n = 0;
3175             zlcCur->zlc_next = NULL;
3176         }
3177     }
3178
3179     if (dir)
3180         (void)sprintf(zlcCur->zlc_names[zlcCur->zlc_n], "%c" OS_DIRSEP "%s", dir, name);
3181     else
3182         (void)sprintf(zlcCur->zlc_names[zlcCur->zlc_n], "%s", name);
3183
3184     zlcCur->zlc_n++;
3185 }
3186
3187 static void
3188 DeleteZLCFiles(char *path)
3189 {
3190     zlcList_t *z;
3191     int i;
3192     char fname[1024];
3193
3194     for (z = zlcAnchor; z; z = z->zlc_next) {
3195         for (i = 0; i < z->zlc_n; i++) {
3196             if (path)
3197                 (void)sprintf(fname, "%s" OS_DIRSEP "%s", path, z->zlc_names[i]);
3198             else
3199                 (void)sprintf(fname, "%s", z->zlc_names[i]);
3200             if (namei_unlink(fname) < 0) {
3201                 Log("ZLC: Can't unlink %s, dos error = %d\n", fname,
3202                     GetLastError());
3203             }
3204         }
3205         z->zlc_n = 0;           /* Can reuse space. */
3206     }
3207     zlcCur = zlcAnchor;
3208 }
3209
3210 static void
3211 FreeZLCList(void)
3212 {
3213     zlcList_t *tnext;
3214     zlcList_t *i;
3215
3216     i = zlcAnchor;
3217     while (i) {
3218         tnext = i->zlc_next;
3219         free(i);
3220         i = tnext;
3221     }
3222     zlcCur = zlcAnchor = NULL;
3223 }
3224 #endif
3225
3226 #endif /* AFS_NAMEI_ENV */