d828dcf72e36cb82891de963a84d11f967ce93ff
[openafs.git] / src / vol / ntops.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 Windows NT platforms. */
11
12 #include <afsconfig.h>
13 #include <afs/param.h>
14
15 RCSID
16     ("$Header$");
17
18 #ifdef AFS_NT40_ENV
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <direct.h>
24 #include <io.h>
25 #include <fcntl.h>
26 #include <sys/stat.h>
27 #include <windows.h>
28 #include <winnt.h>
29 #include <winbase.h>
30 #include <lock.h>
31 #include <afs/afsutil.h>
32 #include "nfs.h"
33 #include <afs/afsint.h>
34 #include "ihandle.h"
35 #include "vnode.h"
36 #include "volume.h"
37 #include "viceinode.h"
38 #include <dirent.h>
39 #include <afs/assert.h>
40 #include <afs/errmap_nt.h>
41
42 #define BASEFILEATTRIBUTE FILE_ATTRIBUTE_NORMAL
43
44 /* nt_unlink - unlink a case sensitive name.
45  *
46  * nt_unlink supports the nt_dec call.
47  *
48  * This nt_unlink has the delete on last close semantics of the Unix unlink
49  * with a minor twist. Subsequent CreateFile calls on this file can succeed
50  * if they open for delete. It's also unclear what happens if a CreateFile
51  * call tries to create a new file with the same name. Fortunately, neither
52  * case should occur as part of nt_dec.
53  */
54 int
55 nt_unlink(char *name)
56 {
57     HANDLE fh;
58
59     fh = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
60                     FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
61                     NULL, OPEN_EXISTING,
62                     BASEFILEATTRIBUTE | FILE_FLAG_DELETE_ON_CLOSE |
63                     FILE_FLAG_POSIX_SEMANTICS, NULL);
64     if (fh != INVALID_HANDLE_VALUE)
65         CloseHandle(fh);
66     else {
67         errno = nterr_nt2unix(GetLastError(), ENOENT);
68         return -1;
69     }
70     return 0;
71 }
72
73 /* nt_open - open an NT handle for a file.
74  *
75  * Return Value:
76  *      the handle or -1 on error.
77  */
78 FD_t
79 nt_open(char *name, int flags, int mode)
80 {
81     HANDLE fh;
82     DWORD nt_access = 0;
83     DWORD nt_share = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
84     DWORD nt_create = 0;
85     /* Really use the sequential one for data files, random for meta data. */
86     DWORD FandA = BASEFILEATTRIBUTE | FILE_FLAG_SEQUENTIAL_SCAN;
87
88     /* set access */
89     if ((flags & O_RDWR) || (flags & O_WRONLY))
90         nt_access |= GENERIC_WRITE;
91     if ((flags & O_RDWR) || (flags == O_RDONLY))
92         nt_access |= GENERIC_READ;
93
94     /* set creation */
95     switch (flags & (O_CREAT | O_EXCL | O_TRUNC)) {
96     case 0:
97         nt_create = OPEN_EXISTING;
98         break;
99     case O_CREAT:
100         nt_create = OPEN_ALWAYS;
101         break;
102     case O_CREAT | O_TRUNC:
103         nt_create = CREATE_ALWAYS;
104         break;
105     case O_CREAT | O_EXCL:
106     case O_CREAT | O_EXCL | O_TRUNC:
107         nt_create = CREATE_NEW;
108         break;
109     case O_TRUNC:
110         nt_create = TRUNCATE_EXISTING;
111         break;
112     case O_TRUNC | O_EXCL:
113     case O_EXCL:
114     default:
115         errno = EINVAL;
116         return INVALID_FD;
117         break;
118     }
119
120     fh = CreateFile(name, nt_access, nt_share, NULL, nt_create, FandA, NULL);
121
122     if (fh == INVALID_HANDLE_VALUE) {
123         fh = INVALID_FD;
124         errno = nterr_nt2unix(GetLastError(), EBADF);
125     }
126     return fh;
127 }
128
129 int
130 nt_close(FD_t fd)
131 {
132     BOOL code;
133
134     code = CloseHandle(fd);
135     if (!code) {
136         errno = nterr_nt2unix(GetLastError(), EBADF);
137         return -1;
138     }
139     return 0;
140 }
141
142 int
143 nt_write(FD_t fd, char *buf, size_t size)
144 {
145     BOOL code;
146     DWORD nbytes;
147
148     code = WriteFile((HANDLE) fd, (void *)buf, (DWORD) size, &nbytes, NULL);
149
150     if (!code) {
151         errno = nterr_nt2unix(GetLastError(), EBADF);
152         return -1;
153     }
154     return (int)nbytes;
155 }
156
157 int
158 nt_read(FD_t fd, char *buf, size_t size)
159 {
160     BOOL code;
161     DWORD nbytes;
162
163     code = ReadFile((HANDLE) fd, (void *)buf, (DWORD) size, &nbytes, NULL);
164
165     if (!code) {
166         errno = nterr_nt2unix(GetLastError(), EBADF);
167         return -1;
168     }
169     return (int)nbytes;
170 }
171
172 int
173 nt_iread(IHandle_t * h, int offset, char *buf, int size)
174 {
175     int nBytes;
176     FdHandle_t *fdP;
177
178     fdP = IH_OPEN(h);
179     if (fdP == NULL)
180         return -1;
181
182     if (FDH_SEEK(fdP, offset, SEEK_SET) < 0) {
183         FDH_REALLYCLOSE(fdP);
184         return -1;
185     }
186
187     nBytes = FDH_READ(fdP, buf, size);
188     FDH_CLOSE(fdP);
189     return nBytes;
190 }
191
192 int
193 nt_iwrite(IHandle_t * h, int offset, char *buf, int size)
194 {
195     int nBytes;
196     FdHandle_t *fdP;
197
198     fdP = IH_OPEN(h);
199     if (fdP == NULL)
200         return -1;
201
202     if (FDH_SEEK(fdP, offset, SEEK_SET) < 0) {
203         FDH_REALLYCLOSE(fdP);
204         return -1;
205     }
206     nBytes = FDH_WRITE(fdP, buf, size);
207     FDH_CLOSE(fdP);
208     return nBytes;
209 }
210
211
212 int
213 nt_size(FD_t fd)
214 {
215     BY_HANDLE_FILE_INFORMATION finfo;
216
217     if (!GetFileInformationByHandle(fd, &finfo))
218         return -1;
219
220     return finfo.nFileSizeLow;
221 }
222
223
224 int
225 nt_getFileCreationTime(FD_t fd, FILETIME * ftime)
226 {
227     BY_HANDLE_FILE_INFORMATION finfo;
228
229     if (!GetFileInformationByHandle(fd, &finfo))
230         return -1;
231
232     *ftime = finfo.ftCreationTime;
233
234     return 0;
235 }
236
237 int
238 nt_setFileCreationTime(FD_t fd, FILETIME * ftime)
239 {
240     return !SetFileTime(fd, ftime, NULL, NULL);
241 }
242
243 int
244 nt_sync(int cdrive)
245 {
246     FD_t drive_fd;
247     char sdrive[32];
248     int n;
249
250     n = cdrive;
251     if (n <= 26) {
252         cdrive = 'A' + (n - 1);
253     }
254
255     cdrive = _toupper(cdrive);
256
257     (void)sprintf(sdrive, "\\\\.\\%c:", cdrive);
258     drive_fd = nt_open(sdrive, O_RDWR, 0666);
259     if (drive_fd == INVALID_FD) {
260         return -1;
261     }
262
263     if (!FlushFileBuffers((HANDLE) drive_fd)) {
264         errno = nterr_nt2unix(GetLastError(), EBADF);
265         nt_close(drive_fd);
266         return -1;
267     }
268     nt_close(drive_fd);
269     return 0;
270 }
271
272
273 /* Currently nt_ftruncate only tested to shrink a file. */
274 int
275 nt_ftruncate(FD_t fd, int len)
276 {
277     if (SetFilePointer(fd, (LONG) len, NULL, FILE_BEGIN)
278         == 0xffffffff) {
279         errno = nterr_nt2unix(GetLastError(), EBADF);
280         return -1;
281     }
282     if (!SetEndOfFile(fd)) {
283         errno = nterr_nt2unix(GetLastError(), EBADF);
284         return -1;
285     }
286     return 0;
287 }
288
289
290 int
291 nt_fsync(FD_t fd)
292 {
293     int code = FlushFileBuffers(fd);
294     return code == 0 ? -1 : 0;
295 }
296
297
298 int
299 nt_seek(FD_t fd, int off, int where)
300 {
301     int code = SetFilePointer(fd, off, NULL, where);
302     return code;
303 }
304
305
306 /* Inode number format:
307  * low 32 bits - if a regular file or directory, the vnode. Else the type.
308  * 32-36 - unquifier tag and index into counts array for this vnode. Only
309  *         two of the available bits are currently used. The rest are
310  *         present in case we ever increase the number of types of volumes
311  *         in the volume grou.
312  * bit 37 : 1  == special, 0 == regular
313  */
314 #define NT_VNODEMASK    0x00ffffffff
315 /* While the TAGMASK is 7, note that we are leaving 1 more bit available. */
316 #define NT_TAGMASK      0x7
317 #define NT_TAGSHIFT     32
318 #define NT_INODESPECIAL 0x2000000000
319
320 #define NT_MAXVOLS 5            /* Maximum supported number of volumes per volume
321                                  * group, not counting temporary (move) volumes.
322                                  * This is the number of separate files, all having
323                                  * the same vnode number, which can occur in a volume
324                                  * group at once.
325                                  */
326
327
328 int nt_SetLinkCount(FdHandle_t * h, Inode ino, int count, int locked);
329
330
331 /* nt_DevToDrive 
332  * converts a device number (2-25) into a drive letter name.
333  *
334  * Arguments:
335  * drive - assumes drive is a pointer to a string at least 3 bytes long.
336  * dev   - drive number 2-25, since A-C already in use.
337  *
338  * Return Value:
339  * Returns pointer to end of drive if successful, else NULL.
340  *
341  */
342 char *
343 nt_DevToDrive(char *drive, int dev)
344 {
345     if (dev < 2 || dev > 25) {
346         errno = EINVAL;
347         return NULL;            /* Invalid drive */
348     }
349     drive[0] = (char)('A' + dev);
350     drive[1] = ':';
351     drive[2] = '\0';
352
353     return drive + 2;
354
355 }
356
357 /* Returns pointer to end of name if successful, else NULL. */
358 char *
359 nt_HandleToVolDir(char *name, IHandle_t * h)
360 {
361     b32_string_t str1;
362
363     if (!(name = nt_DevToDrive(name, h->ih_dev)))
364         return NULL;
365
366     (void)memcpy(name, "\\Vol_", 5);
367     name += 5;
368     (void)strcpy(name, int_to_base32(str1, h->ih_vid));
369     name += strlen(name);
370     memcpy(name, ".data", 5);
371     name += 5;
372     *name = '\0';
373
374     return name;
375 }
376
377 /* nt_HandleToName
378  *
379  * Constructs a file name for the fully qualified handle.
380  */
381 int
382 nt_HandleToName(char *name, IHandle_t * h)
383 {
384     b32_string_t str1;
385     int tag = (int)((h->ih_ino >> NT_TAGSHIFT) & NT_TAGMASK);
386     int vno = (int)(h->ih_ino & NT_VNODEMASK);
387
388     if (!(name = nt_HandleToVolDir(name, h)))
389         return -1;
390
391     str1[0] = '\\';
392     if (h->ih_ino & NT_INODESPECIAL)
393         str1[1] = 'R';
394     else {
395         if (vno & 0x1)
396             str1[1] = 'Q';
397         else
398             str1[1] = ((vno & 0x1f) >> 1) + 'A';
399     }
400
401     memcpy(name, str1, 2);
402     name += 2;
403     (void)memcpy(name, "\\V_", 3);
404     name += 3;
405     (void)strcpy(name, int_to_base32(str1, vno));
406     name += strlen(name);
407     *(name++) = '.';
408     (void)strcpy(name, int_to_base32(str1, tag));
409     name += strlen(name);
410     *name = '\0';
411
412     return 0;
413 }
414
415 /* nt_CreateDataDirectories
416  *
417  * The data for each volume is in a separate directory. The name of the
418  * volume is of the form: Vol_NNNNNN.data, where NNNNNN is a base 32
419  * representation of the RW volume ID (even where the RO is the only volume
420  * on the partition). Below that are separate subdirectories for the 
421  * AFS directories and special files. There are also 16 directories for files,
422  * hashed on the low 5 bits (recall bit0 is always 0) of the vnode number.
423  * These directories are named:
424  * A - P - 16 file directories.
425  * Q ----- data directory
426  * R ----- special files directory
427  */
428 static int
429 nt_CreateDataDirectories(IHandle_t * h, int *created)
430 {
431     char name[128];
432     char *s;
433     int i;
434
435     if (!(s = nt_HandleToVolDir(name, h)))
436         return -1;
437
438     if (mkdir(name) < 0) {
439         if (errno != EEXIST)
440             return -1;
441     } else
442         *created = 1;
443
444     *s++ = '\\';
445     *(s + 1) = '\0';
446     for (i = 'A'; i <= 'R'; i++) {
447         *s = (char)i;
448         if (mkdir(name) < 0 && errno != EEXIST)
449             return -1;
450     }
451     return 0;
452 }
453
454 /* nt_RemoveDataDirectories
455  *
456  * Returns -1 on error. Typically, callers ignore this error bcause we
457  * can continue running if the removes fail. The salvage process will
458  * finish tidying up for us.
459  */
460 static int
461 nt_RemoveDataDirectories(IHandle_t * h)
462 {
463     char name[128];
464     char *s;
465     int i;
466
467     if (!(s = nt_HandleToVolDir(name, h)))
468         return -1;
469
470     *s++ = '\\';
471     *(s + 1) = '\0';
472     for (i = 'A'; i <= 'R'; i++) {
473         *s = (char)i;
474         if (rmdir(name) < 0 && errno != ENOENT)
475             return -1;
476     }
477
478     /* Delete the Vol_NNNNNN.data directory. */
479     s--;
480     *s = '\0';
481     if (rmdir(name) < 0 && errno != ENOENT) {
482         return -1;
483     }
484
485     return 0;
486 }
487
488
489 /* Create the file in the name space.
490  *
491  * Parameters stored as follows:
492  * Regular files:
493  * p1 - volid - implied in containing directory.
494  * p2 - vnode - name is <vnode>.<tag> where tag is a file name unqiquifier.
495  * p3 - uniq -- creation time - dwHighDateTime
496  * p4 - dv ---- creation time - dwLowDateTime
497  * Special files:
498  * p1 - volid - creation time - dwHighDateTime
499  * p2 - vnode - -1 means special, file goes in "S" subdirectory.
500  * p3 - type -- name is <type>.<tag> where tag is a file name unqiquifier.
501  * p4 - parid - parent volume id - implied in containing directory.
502  *
503  * Return value is the inode number or (Inode)-1 if error.
504  * We "know" there is only one link table, so return EEXIST if there already
505  * is a link table. It's up to the calling code to test errno and increment
506  * the link count.
507  */
508
509 /* nt_MakeSpecIno
510  *
511  * This function is called by VCreateVolume to hide the implementation
512  * details of the inode numbers.
513  */
514 Inode
515 nt_MakeSpecIno(int type)
516 {
517     return ((Inode) type | (Inode) NT_INODESPECIAL);
518 }
519
520 Inode
521 nt_icreate(IHandle_t * h, char *part, int p1, int p2, int p3, int p4)
522 {
523     char filename[128];
524     b32_string_t str1;
525     char *p;
526     int i;
527     FD_t fd;
528     int created_dir = 0;
529     int code = 0;
530     FILETIME ftime;
531     IHandle_t tmp;
532     FdHandle_t *fdP;
533     FdHandle_t tfd;
534     int save_errno;
535
536     memset((void *)&tmp, 0, sizeof(IHandle_t));
537
538
539     tmp.ih_dev = tolower(*part) - 'a';
540
541     if (p2 == -1) {
542         tmp.ih_vid = p4;        /* Use parent volume id, where this file will be. */
543
544         if (nt_CreateDataDirectories(&tmp, &created_dir) < 0)
545             goto bad;
546
547         tmp.ih_ino = nt_MakeSpecIno(p3);
548         ftime.dwHighDateTime = p1;
549         ftime.dwLowDateTime = p2;
550     } else {
551         /* Regular file or directory.
552          * Encoding: p1 -> dir,  p2 -> name, p3,p4 -> Create time
553          */
554         tmp.ih_ino = (Inode) p2;
555         tmp.ih_vid = p1;
556
557         ftime.dwHighDateTime = p3;
558         ftime.dwLowDateTime = p4;
559     }
560
561     /* Now create file. */
562     if ((code = nt_HandleToName(filename, &tmp)) < 0)
563         goto bad;
564
565     p = filename + strlen(filename);
566     p--;
567     for (i = 0; i < NT_MAXVOLS; i++) {
568         *p = *int_to_base32(str1, i);
569         fd = nt_open(filename, O_CREAT | O_RDWR | O_TRUNC | O_EXCL, 0666);
570         if (fd != INVALID_FD)
571             break;
572         if (p2 == -1 && p3 == VI_LINKTABLE)
573             break;
574     }
575     if (fd == INVALID_FD) {
576         code = -1;
577         goto bad;
578     }
579
580     tmp.ih_ino &= ~((Inode) NT_TAGMASK << NT_TAGSHIFT);
581     tmp.ih_ino |= ((Inode) i << NT_TAGSHIFT);
582
583     if (!code) {
584         if (!SetFileTime((HANDLE) fd, &ftime, NULL, NULL)) {
585             errno = EBADF;
586             code = -1;
587         }
588     }
589
590     if (!code) {
591         if (p2 != -1) {
592             if (fd == INVALID_FD) {
593                 errno = ENOENT;
594                 code = nt_unlink(filename);
595                 if (code == -1) {
596                 }
597                 code = -1;
598                 goto bad;
599             }
600             fdP = IH_OPEN(h);
601             if (fdP == NULL) {
602                 code = -1;
603                 goto bad;
604             }
605             code = nt_SetLinkCount(fdP, tmp.ih_ino, 1, 0);
606             FDH_CLOSE(fdP);
607         } else if (p2 == -1 && p3 == VI_LINKTABLE) {
608             if (fd == INVALID_FD)
609                 goto bad;
610             /* hack at tmp to setup for set link count call. */
611             tfd.fd_fd = fd;
612             code = nt_SetLinkCount(&tfd, (Inode) 0, 1, 0);
613         }
614     }
615
616   bad:
617     if (fd != INVALID_FD)
618         nt_close(fd);
619
620     if (code && created_dir) {
621         save_errno = errno;
622         nt_RemoveDataDirectories(&tmp);
623         errno = save_errno;
624     }
625     return code ? (Inode) - 1 : tmp.ih_ino;
626 }
627
628
629 FD_t
630 nt_iopen(IHandle_t * h)
631 {
632     FD_t fd;
633     char name[128];
634
635     /* Convert handle to file name. */
636     if (nt_HandleToName(name, h) < 0)
637         return INVALID_FD;
638
639     fd = nt_open(name, O_RDWR, 0666);
640     return fd;
641 }
642
643 /* Need to detect vol special file and just unlink. In those cases, the
644  * handle passed in _is_ for the inode. We only check p1 for the special
645  * files.
646  */
647 int
648 nt_dec(IHandle_t * h, Inode ino, int p1)
649 {
650     int count = 0;
651     char name[128];
652     int code = 0;
653     FdHandle_t *fdP;
654
655     if (ino & NT_INODESPECIAL) {
656         IHandle_t *tmp;
657         int was_closed = 0;
658         WIN32_FIND_DATA info;
659         HANDLE dirH;
660
661         /* Verify this is the right file. */
662         IH_INIT(tmp, h->ih_dev, h->ih_vid, ino);
663
664         if (nt_HandleToName(name, tmp) < 0) {
665             IH_RELEASE(tmp);
666             errno = EINVAL;
667             return -1;
668         }
669
670         dirH =
671             FindFirstFileEx(name, FindExInfoStandard, &info,
672                             FindExSearchNameMatch, NULL,
673                             FIND_FIRST_EX_CASE_SENSITIVE);
674         if (!dirH) {
675             IH_RELEASE(tmp);
676             errno = ENOENT;
677             return -1;          /* Can't get info, leave alone */
678         }
679
680         FindClose(dirH);
681         if (info.ftCreationTime.dwHighDateTime != (unsigned int)p1) {
682             IH_RELEASE(tmp);
683             return -1;
684         }
685
686         /* If it's the link table itself, decrement the link count. */
687         if ((ino & NT_VNODEMASK) == VI_LINKTABLE) {
688             fdP = IH_OPEN(tmp);
689             if (fdP == NULL) {
690                 IH_RELEASE(tmp);
691                 return -1;
692             }
693
694             if ((count = nt_GetLinkCount(fdP, (Inode) 0, 1)) < 0) {
695                 FDH_REALLYCLOSE(fdP);
696                 IH_RELEASE(tmp);
697                 return -1;
698             }
699
700             count--;
701             if (nt_SetLinkCount(fdP, (Inode) 0, count < 0 ? 0 : count, 1) < 0) {
702                 FDH_REALLYCLOSE(fdP);
703                 IH_RELEASE(tmp);
704                 return -1;
705             }
706
707             FDH_REALLYCLOSE(fdP);
708             if (count > 0) {
709                 IH_RELEASE(tmp);
710                 return 0;
711             }
712         }
713
714         if ((code = nt_unlink(name)) == 0) {
715             if ((ino & NT_VNODEMASK) == VI_LINKTABLE) {
716                 /* Try to remove directory. If it fails, that's ok.
717                  * Salvage will clean up.
718                  */
719                 (void)nt_RemoveDataDirectories(tmp);
720             }
721         }
722
723         IH_RELEASE(tmp);
724     } else {
725         /* Get a file descriptor handle for this Inode */
726         fdP = IH_OPEN(h);
727         if (fdP == NULL) {
728             return -1;
729         }
730
731         if ((count = nt_GetLinkCount(fdP, ino, 1)) < 0) {
732             FDH_REALLYCLOSE(fdP);
733             return -1;
734         }
735
736         count--;
737         if (count >= 0) {
738             if (nt_SetLinkCount(fdP, ino, count, 1) < 0) {
739                 FDH_REALLYCLOSE(fdP);
740                 return -1;
741             }
742         }
743         if (count == 0) {
744             IHandle_t th = *h;
745             th.ih_ino = ino;
746             nt_HandleToName(name, &th);
747             code = nt_unlink(name);
748         }
749         FDH_CLOSE(fdP);
750     }
751
752     return code;
753 }
754
755 int
756 nt_inc(IHandle_t * h, Inode ino, int p1)
757 {
758     int count;
759     int code = 0;
760     FdHandle_t *fdP;
761
762     if (ino & NT_INODESPECIAL) {
763         if ((ino & NT_VNODEMASK) != VI_LINKTABLE)
764             return 0;
765         ino = (Inode) 0;
766     }
767
768     /* Get a file descriptor handle for this Inode */
769     fdP = IH_OPEN(h);
770     if (fdP == NULL) {
771         return -1;
772     }
773
774     if ((count = nt_GetLinkCount(fdP, ino, 1)) < 0)
775         code = -1;
776     else {
777         count++;
778         if (count > 7) {
779             errno = EINVAL;
780             code = -1;
781             count = 7;
782         }
783         if (nt_SetLinkCount(fdP, ino, count, 1) < 0)
784             code = -1;
785     }
786     if (code) {
787         FDH_REALLYCLOSE(fdP);
788     } else {
789         FDH_CLOSE(fdP);
790     }
791     return code;
792 }
793
794
795
796 /************************************************************************
797  *  Link Table Organization
798  ************************************************************************
799  *
800  * The link table volume special file is used to hold the link counts that
801  * are held in the inodes in inode based AFS vice filesystems. Since NTFS
802  * doesn't provide us that access, the link counts are being kept in a separate
803  * volume special file. The file begins with the usual version stamp
804  * information and is then followed by one row per vnode number. vnode 0
805  * is used to hold the link count of the link table itself. That is because
806  * the same link table is shared among all the volumes of the volume group
807  * and is deleted only when the last volume of a volume group is deleted.
808  *
809  * Within each row, the columns are 3 bits wide. They can each hold a 0 based
810  * link count from 0 through 7. Each colume represents a unique instance of
811  * that vnode. Say we have a file shared between the RW and a RO and a
812  * different version of the file (or a different uniquifer) for the BU volume.
813  * Then one column would be holding the link count of 2 for the RW and RO
814  * and a different column would hold the link count of 1 for the BU volume.
815  * The column used is determined for NT by the uiquifier tag applied to
816  * generate a unique file name in the NTFS namespace. The file name is
817  * of the form "V_<vno>.<tag>" . And the <tag> is also the column number
818  * in the link table.
819  */
820 #define LINKTABLE_WIDTH 2
821 #define LINKTABLE_SHIFT 1       /* log 2 = 1 */
822
823 static void
824 nt_GetLCOffsetAndIndexFromIno(Inode ino, int *offset, int *index)
825 {
826     int toff = (int)(ino & NT_VNODEMASK);
827     int tindex = (int)((ino >> NT_TAGSHIFT) & NT_TAGMASK);
828
829     *offset = (toff << LINKTABLE_SHIFT) + 8;    /* *2 + sizeof stamp */
830     *index = (tindex << 1) + tindex;
831 }
832
833
834 /* nt_GetLinkCount
835  * If lockit is set, lock the file and leave it locked upon a successful
836  * return.
837  */
838 int
839 nt_GetLinkCount(FdHandle_t * h, Inode ino, int lockit)
840 {
841     unsigned short row = 0;
842     int junk;
843     int offset, index;
844
845     nt_GetLCOffsetAndIndexFromIno(ino, &offset, &index);
846
847     if (lockit) {
848         if (!LockFile(h->fd_fd, offset, 0, 2, 0))
849             return -1;
850     }
851
852     if (!SetFilePointer(h->fd_fd, (LONG) offset, NULL, FILE_BEGIN))
853         goto bad_getLinkByte;
854
855     if (!ReadFile(h->fd_fd, (void *)&row, 2, &junk, NULL)) {
856         goto bad_getLinkByte;
857     }
858
859     return (int)((row >> index) & NT_TAGMASK);
860
861   bad_getLinkByte:
862     if (lockit)
863         UnlockFile(h->fd_fd, offset, 0, 2, 0);
864     return -1;
865 }
866
867
868
869
870 /* nt_SetLinkCount
871  * If locked is set, assume file is locked. Otherwise, lock file before
872  * proceeding to modify it.
873  */
874 int
875 nt_SetLinkCount(FdHandle_t * h, Inode ino, int count, int locked)
876 {
877     int offset, index;
878     unsigned short row;
879     int junk;
880     int code = -1;
881
882     nt_GetLCOffsetAndIndexFromIno(ino, &offset, &index);
883
884
885     if (!locked) {
886         if (!LockFile(h->fd_fd, offset, 0, 2, 0)) {
887             errno = nterr_nt2unix(GetLastError(), EBADF);
888             return -1;
889         }
890     }
891     if (!SetFilePointer(h->fd_fd, (LONG) offset, NULL, FILE_BEGIN)) {
892         errno = nterr_nt2unix(GetLastError(), EBADF);
893         goto bad_SetLinkCount;
894     }
895
896
897     if (!ReadFile(h->fd_fd, (void *)&row, 2, &junk, NULL)) {
898         errno = nterr_nt2unix(GetLastError(), EBADF);
899         goto bad_SetLinkCount;
900     }
901     if (junk == 0)
902         row = 0;
903
904     junk = 7 << index;
905     count <<= index;
906     row &= (unsigned short)~junk;
907     row |= (unsigned short)count;
908
909     if (!SetFilePointer(h->fd_fd, (LONG) offset, NULL, FILE_BEGIN)) {
910         errno = nterr_nt2unix(GetLastError(), EBADF);
911         goto bad_SetLinkCount;
912     }
913
914     if (!WriteFile(h->fd_fd, (void *)&row, 2, &junk, NULL)) {
915         errno = nterr_nt2unix(GetLastError(), EBADF);
916         goto bad_SetLinkCount;
917     }
918
919     code = 0;
920
921
922   bad_SetLinkCount:
923     UnlockFile(h->fd_fd, offset, 0, 2, 0);
924
925     return code;
926 }
927
928
929 /* ListViceInodes - write inode data to a results file. */
930 static int DecodeInodeName(char *name, int *p1, int *p2);
931 static int DecodeVolumeName(char *name, int *vid);
932 static int nt_ListAFSSubDirs(IHandle_t * dirIH,
933                              int (*write_fun) (FILE *, struct ViceInodeInfo *,
934                                                char *, char *), FILE * fp,
935                              int (*judgeFun) (struct ViceInodeInfo *,
936                                               int vid),
937                              int singleVolumeNumber);
938
939
940 /* WriteInodeInfo
941  *
942  * Write the inode data to the results file. 
943  *
944  * Returns -2 on error, 0 on success.
945  *
946  * This is written as a callback simply so that other listing routines
947  * can use the same inode reading code.
948  */
949 static int
950 WriteInodeInfo(FILE * fp, struct ViceInodeInfo *info, char *dir, char *name)
951 {
952     int n;
953     n = fwrite(info, sizeof(*info), 1, fp);
954     return (n == 1) ? 0 : -2;
955 }
956
957
958 /* ListViceInodes
959  * Fill the results file with the requested inode information.
960  *
961  * Return values:
962  *  0 - success
963  * -1 - complete failure, salvage should terminate.
964  * -2 - not enough space on partition, salvager has error message for this.
965  *
966  * This code optimizes single volume salvages by just looking at that one
967  * volume's directory. 
968  *
969  * If the resultFile is NULL, then don't call the write routine.
970  */
971 int
972 ListViceInodes(char *devname, char *mountedOn, char *resultFile,
973                int (*judgeInode) (struct ViceInodeInfo * info, int vid),
974                int singleVolumeNumber, int *forcep, int forceR, char *wpath)
975 {
976     FILE *fp = (FILE *) - 1;
977     int ninodes;
978     struct stat status;
979
980     if (resultFile) {
981         fp = fopen(resultFile, "w");
982         if (!fp) {
983             Log("Unable to create inode description file %s\n", resultFile);
984             return -1;
985         }
986     }
987     ninodes =
988         nt_ListAFSFiles(wpath, WriteInodeInfo, fp, judgeInode,
989                         singleVolumeNumber);
990
991     if (!resultFile)
992         return ninodes;
993
994     if (ninodes < 0) {
995         fclose(fp);
996         return ninodes;
997     }
998
999     if (fflush(fp) == EOF) {
1000         Log("Unable to successfully flush inode file for %s\n", mountedOn);
1001         fclose(fp);
1002         return -2;
1003     }
1004     if (fsync(fileno(fp)) == -1) {
1005         Log("Unable to successfully fsync inode file for %s\n", mountedOn);
1006         fclose(fp);
1007         return -2;
1008     }
1009     if (fclose(fp) == EOF) {
1010         Log("Unable to successfully close inode file for %s\n", mountedOn);
1011         return -2;
1012     }
1013
1014     /*
1015      * Paranoia:  check that the file is really the right size
1016      */
1017     if (stat(resultFile, &status) == -1) {
1018         Log("Unable to successfully stat inode file for %s\n", mountedOn);
1019         return -2;
1020     }
1021     if (status.st_size != ninodes * sizeof(struct ViceInodeInfo)) {
1022         Log("Wrong size (%d instead of %d) in inode file for %s\n",
1023             status.st_size, ninodes * sizeof(struct ViceInodeInfo),
1024             mountedOn);
1025         return -2;
1026     }
1027     return 0;
1028 }
1029
1030
1031 /* nt_ListAFSFiles
1032  *
1033  * Collect all the matching AFS files on the drive.
1034  * If singleVolumeNumber is non-zero, just return files for that volume.
1035  *
1036  * Returns <0 on error, else number of files found to match.
1037  */
1038 int
1039 nt_ListAFSFiles(char *dev,
1040                 int (*writeFun) (FILE *, struct ViceInodeInfo *, char *,
1041                                  char *), FILE * fp,
1042                 int (*judgeFun) (struct ViceInodeInfo *, int),
1043                 int singleVolumeNumber)
1044 {
1045     IHandle_t h;
1046     char name[MAX_PATH];
1047     int ninodes = 0;
1048     DIR *dirp;
1049     struct dirent *dp;
1050     static void FreeZLCList(void);
1051
1052     memset((void *)&h, 0, sizeof(IHandle_t));
1053     h.ih_dev = toupper(*dev) - 'A';
1054
1055     if (singleVolumeNumber) {
1056         h.ih_vid = singleVolumeNumber;
1057         if (!nt_HandleToVolDir(name, &h))
1058             return -1;
1059         ninodes =
1060             nt_ListAFSSubDirs(&h, writeFun, fp, judgeFun, singleVolumeNumber);
1061         if (ninodes < 0)
1062             return ninodes;
1063     } else {
1064         /* Find all Vol_*.data directories and descend through them. */
1065         if (!nt_DevToDrive(name, h.ih_dev))
1066             return -1;
1067         ninodes = 0;
1068         dirp = opendir(name);
1069         if (!dirp)
1070             return -1;
1071         while (dp = readdir(dirp)) {
1072             if (!DecodeVolumeName(dp->d_name, &h.ih_vid)) {
1073                 ninodes += nt_ListAFSSubDirs(&h, writeFun, fp, judgeFun, 0);
1074             }
1075         }
1076     }
1077     FreeZLCList();
1078     return ninodes;
1079 }
1080
1081
1082
1083 /* nt_ListAFSSubDirs
1084  *
1085  * List the S, F, and D subdirectories of this volume's directory.
1086  *
1087  * Return values:
1088  * < 0 - an error
1089  * > = 0 - number of AFS files found.
1090  */
1091 static int
1092 nt_ListAFSSubDirs(IHandle_t * dirIH,
1093                   int (*writeFun) (FILE *, struct ViceInodeInfo *, char *,
1094                                    char *), FILE * fp,
1095                   int (*judgeFun) (struct ViceInodeInfo *, int),
1096                   int singleVolumeNumber)
1097 {
1098     int i;
1099     IHandle_t myIH = *dirIH;
1100     HANDLE dirH;
1101     WIN32_FIND_DATA data;
1102     char path[1024];
1103     char basePath[1024];
1104     char *s;
1105     char findPath[1024];
1106     struct ViceInodeInfo info;
1107     int tag, vno;
1108     FdHandle_t linkHandle;
1109     int ninodes = 0;
1110     static void AddToZLCDeleteList(char dir, char *name);
1111     static void DeleteZLCFiles(char *path);
1112
1113     s = nt_HandleToVolDir(path, &myIH);
1114     strcpy(basePath, path);
1115     if (!s)
1116         return -1;
1117     *s = '\\';
1118     s++;
1119     *(s + 1) = '\0';
1120
1121     /* Do the directory containing the special files first to pick up link
1122      * counts.
1123      */
1124     for (i = 'R'; i >= 'A'; i--) {
1125         *s = (char)i;
1126         (void)strcpy(findPath, path);
1127         (void)strcat(findPath, "\\*");
1128         dirH =
1129             FindFirstFileEx(findPath, FindExInfoStandard, &data,
1130                             FindExSearchNameMatch, NULL,
1131                             FIND_FIRST_EX_CASE_SENSITIVE);
1132         if (dirH == INVALID_HANDLE_VALUE)
1133             continue;
1134         while (1) {
1135             /* Store the vice info. */
1136             memset((void *)&info, 0, sizeof(info));
1137             if (*data.cFileName == '.')
1138                 goto next_file;
1139             if (DecodeInodeName(data.cFileName, &vno, &tag) < 0) {
1140                 Log("Error parsing %s\\%s\n", path, data.cFileName);
1141             } else {
1142                 info.inodeNumber = (Inode) tag << NT_TAGSHIFT;
1143                 info.inodeNumber |= (Inode) vno;
1144                 info.byteCount = data.nFileSizeLow;
1145
1146                 if (i == 'R') { /* Special inode. */
1147                     info.inodeNumber |= NT_INODESPECIAL;
1148                     info.u.param[0] = data.ftCreationTime.dwHighDateTime;
1149                     info.u.param[1] = data.ftCreationTime.dwLowDateTime;
1150                     info.u.param[2] = vno;
1151                     info.u.param[3] = dirIH->ih_vid;
1152                     if (info.u.param[2] != VI_LINKTABLE) {
1153                         info.linkCount = 1;
1154                     } else {
1155                         /* Open this handle */
1156                         char lpath[1024];
1157                         (void)sprintf(lpath, "%s\\%s", path, data.cFileName);
1158                         linkHandle.fd_fd = nt_open(lpath, O_RDONLY, 0666);
1159                         info.linkCount =
1160                             nt_GetLinkCount(&linkHandle, (Inode) 0, 0);
1161                     }
1162                 } else {        /* regular file. */
1163                     info.linkCount =
1164                         nt_GetLinkCount(&linkHandle, info.inodeNumber, 0);
1165                     if (info.linkCount == 0) {
1166 #ifdef notdef
1167                         Log("Found 0 link count file %s\\%s, deleting it.\n",
1168                             path, data.cFileName);
1169                         AddToZLCDeleteList((char)i, data.cFileName);
1170 #else
1171                         Log("Found 0 link count file %s\\%s.\n", path,
1172                             data.cFileName);
1173 #endif
1174                         goto next_file;
1175                     }
1176                     info.u.param[0] = dirIH->ih_vid;
1177                     info.u.param[1] = vno;
1178                     info.u.param[2] = data.ftCreationTime.dwHighDateTime;
1179                     info.u.param[3] = data.ftCreationTime.dwLowDateTime;
1180                 }
1181                 if (judgeFun && !(*judgeFun) (&info, singleVolumeNumber))
1182                     goto next_file;
1183                 if ((*writeFun) (fp, &info, path, data.cFileName) < 0) {
1184                     nt_close(linkHandle.fd_fd);
1185                     FindClose(dirH);
1186                     return -1;
1187                 }
1188                 ninodes++;
1189             }
1190           next_file:
1191             if (!FindNextFile(dirH, &data)) {
1192                 break;
1193             }
1194         }
1195         FindClose(dirH);
1196     }
1197     nt_close(linkHandle.fd_fd);
1198     DeleteZLCFiles(basePath);
1199     if (!ninodes) {
1200         /* Then why does this directory exist? Blow it away. */
1201         nt_RemoveDataDirectories(dirIH);
1202     }
1203
1204     return ninodes;
1205 }
1206
1207 /* The name begins with "Vol_" and ends with .data.  See nt_HandleToVolDir() */
1208 static int
1209 DecodeVolumeName(char *name, int *vid)
1210 {
1211     char stmp[32];
1212     int len;
1213
1214     len = strlen(name);
1215     if (len <= 9)
1216         return -1;
1217     if (strncmp(name, "Vol_", 4))
1218         return -1;
1219     if (strcmp(name + len - 5, ".data"))
1220         return -1;
1221     strcpy(stmp, name);
1222     stmp[len - 5] = '\0';
1223     *vid = base32_to_int(stmp + 4);
1224     return 0;
1225 }
1226
1227 /* Recall that the name beings with a "V_" */
1228 static int
1229 DecodeInodeName(char *name, int *p1, int *p2)
1230 {
1231     char *s, *t;
1232     char stmp[16];
1233
1234     (void)strcpy(stmp, name);
1235     s = strrchr(stmp, '_');
1236     if (!s)
1237         return -1;
1238     s++;
1239     t = strrchr(s, '.');
1240     if (!t)
1241         return -1;
1242
1243     *t = '\0';
1244     *p1 = base32_to_int(s);
1245     *p2 = base32_to_int(t + 1);
1246     return 0;
1247 }
1248
1249
1250 /* PrintInode
1251  *
1252  * returns a static string used to print either 32 or 64 bit inode numbers.
1253  */
1254 char *
1255 PrintInode(char *s, Inode ino)
1256 {
1257     static afs_ino_str_t result;
1258     if (!s)
1259         s = result;
1260
1261     (void)sprintf((char *)s, "%I64u", ino);
1262
1263     return (char *)s;
1264 }
1265
1266
1267 /* Routines to facilitate removing zero link count files. */
1268 #define MAX_ZLC_NAMES 32
1269 #define MAX_ZLC_NAMELEN 16
1270 typedef struct zlcList_s {
1271     struct zlcList_s *zlc_next;
1272     int zlc_n;
1273     char zlc_names[MAX_ZLC_NAMES][MAX_ZLC_NAMELEN];
1274 } zlcList_t;
1275
1276 static zlcList_t *zlcAnchor = NULL;
1277 static zlcList_t *zlcCur = NULL;
1278
1279 static void
1280 AddToZLCDeleteList(char dir, char *name)
1281 {
1282     assert(strlen(name) <= MAX_ZLC_NAMELEN - 3);
1283
1284     if (!zlcCur || zlcCur->zlc_n >= MAX_ZLC_NAMES) {
1285         if (zlcCur && zlcCur->zlc_next)
1286             zlcCur = zlcCur->zlc_next;
1287         else {
1288             zlcList_t *tmp = (zlcList_t *) malloc(sizeof(zlcList_t));
1289             if (!tmp)
1290                 return;
1291             if (!zlcAnchor) {
1292                 zlcAnchor = tmp;
1293             } else {
1294                 zlcCur->zlc_next = tmp;
1295             }
1296             zlcCur = tmp;
1297             zlcCur->zlc_n = 0;
1298             zlcCur->zlc_next = NULL;
1299         }
1300     }
1301
1302     (void)sprintf(zlcCur->zlc_names[zlcCur->zlc_n], "%c\\%s", dir, name);
1303     zlcCur->zlc_n++;
1304 }
1305
1306 static void
1307 DeleteZLCFiles(char *path)
1308 {
1309     zlcList_t *z;
1310     int i;
1311     char fname[1024];
1312
1313     for (z = zlcAnchor; z; z = z->zlc_next) {
1314         for (i = 0; i < z->zlc_n; i++) {
1315             (void)sprintf(fname, "%s\\%s", path, z->zlc_names[i]);
1316             if (nt_unlink(fname) < 0) {
1317                 Log("ZLC: Can't unlink %s, dos error = %d\n", fname,
1318                     GetLastError());
1319             }
1320         }
1321         z->zlc_n = 0;           /* Can reuse space. */
1322     }
1323     zlcCur = zlcAnchor;
1324 }
1325
1326 static void
1327 FreeZLCList(void)
1328 {
1329     zlcList_t *tnext;
1330     zlcList_t *i;
1331
1332     i = zlcAnchor;
1333     while (i) {
1334         tnext = i->zlc_next;
1335         free(i);
1336         i = tnext;
1337     }
1338     zlcCur = zlcAnchor = NULL;
1339 }
1340
1341 #endif /* AFS_NT40_ENV */