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