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