16d2332313f43c2997af63acfe2db0ef5ff2acb1
[openafs.git] / src / vol / ihandle.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 /*  ihandle.c   - file descriptor cacheing for Inode handles.           */
11 /*                                                                      */
12 /************************************************************************/
13
14 #include <afsconfig.h>
15 #include <afs/param.h>
16
17 #include <roken.h>
18
19 #include <limits.h>
20
21 #ifdef HAVE_SYS_RESOURCE_H
22 #include <sys/resource.h>
23 #endif
24
25 #include <afs/opr.h>
26 #ifdef AFS_PTHREAD_ENV
27 # include <opr/lock.h>
28 #endif
29 #include <afs/afsint.h>
30 #include <afs/afssyscalls.h>
31 #include <afs/afsutil.h>
32
33 #include "nfs.h"
34 #include "ihandle.h"
35 #include "viceinode.h"
36
37 #ifdef AFS_PTHREAD_ENV
38 pthread_once_t ih_glock_once = PTHREAD_ONCE_INIT;
39 pthread_mutex_t ih_glock_mutex;
40 #endif /* AFS_PTHREAD_ENV */
41
42 /* Linked list of available inode handles */
43 IHandle_t *ihAvailHead;
44 IHandle_t *ihAvailTail;
45
46 /* Linked list of available file descriptor handles */
47 FdHandle_t *fdAvailHead;
48 FdHandle_t *fdAvailTail;
49
50 /* Linked list of available stream descriptor handles */
51 StreamHandle_t *streamAvailHead;
52 StreamHandle_t *streamAvailTail;
53
54 /* LRU list for file descriptor handles */
55 FdHandle_t *fdLruHead;
56 FdHandle_t *fdLruTail;
57
58 int ih_Inited = 0;
59 int ih_PkgDefaultsSet = 0;
60
61 /* Most of the servers use fopen/fdopen. Since the FILE structure
62  * only has eight bits for the file descriptor, the cache size
63  * has to be less than 256. The cache can be made larger as long
64  * as you are sure you don't need fopen/fdopen. */
65
66 /* As noted in ihandle.h, the fileno member of FILE on most platforms
67  * in 2008 is a 16- or 32-bit signed int. -Matt
68  */
69 int fdMaxCacheSize = 0;
70 int fdCacheSize = 0;
71
72 /* Number of in use file descriptors */
73 int fdInUseCount = 0;
74
75 /* Hash table for inode handles */
76 IHashBucket_t ihashTable[I_HANDLE_HASH_SIZE];
77
78 static int _ih_release_r(IHandle_t * ihP);
79
80 /* start-time configurable I/O limits */
81 ih_init_params vol_io_params;
82
83 void ih_PkgDefaults(void)
84 {
85     /* once */
86     ih_PkgDefaultsSet = 1;
87
88     /* default to well-known values */
89     vol_io_params.fd_handle_setaside = FD_HANDLE_SETASIDE;
90
91     /* initial fd cachesize.  the only one that will be used if
92      * the application does not call ih_UseLargeCache().  set this
93      * to a value representable in fileno member of the system's
94      * FILE structure (or equivalent). */
95     vol_io_params.fd_initial_cachesize = FD_DEFAULT_CACHESIZE;
96
97     /* fd cache size that will be used if/when ih_UseLargeCache()
98      * is called */
99     vol_io_params.fd_max_cachesize = FD_MAX_CACHESIZE;
100
101     vol_io_params.sync_behavior = IH_SYNC_ONCLOSE;
102 }
103
104 int
105 ih_SetSyncBehavior(const char *behavior)
106 {
107     int val;
108
109     if (strcmp(behavior, "always") == 0) {
110         val = IH_SYNC_ALWAYS;
111
112     } else if (strcmp(behavior, "onclose") == 0) {
113         val = IH_SYNC_ONCLOSE;
114
115     } else if (strcmp(behavior, "never") == 0) {
116         val = IH_SYNC_NEVER;
117
118     } else {
119         /* invalid behavior name */
120         return -1;
121     }
122
123     vol_io_params.sync_behavior = val;
124     return 0;
125 }
126
127 #ifdef AFS_PTHREAD_ENV
128 /* Initialize the global ihandle mutex */
129 void
130 ih_glock_init(void)
131 {
132     opr_mutex_init(&ih_glock_mutex);
133 }
134 #endif /* AFS_PTHREAD_ENV */
135
136 /* Initialize the file descriptor cache */
137 void
138 ih_Initialize(void)
139 {
140     int i;
141     opr_Assert(!ih_Inited);
142     ih_Inited = 1;
143     DLL_INIT_LIST(ihAvailHead, ihAvailTail);
144     DLL_INIT_LIST(fdAvailHead, fdAvailTail);
145     DLL_INIT_LIST(fdLruHead, fdLruTail);
146     for (i = 0; i < I_HANDLE_HASH_SIZE; i++) {
147         DLL_INIT_LIST(ihashTable[i].ihash_head, ihashTable[i].ihash_tail);
148     }
149 #if defined(AFS_NT40_ENV)
150     fdMaxCacheSize = vol_io_params.fd_max_cachesize;
151 #elif defined(AFS_SUN5_ENV) || defined(AFS_NBSD_ENV)
152     {
153         struct rlimit rlim;
154         opr_Verify(getrlimit(RLIMIT_NOFILE, &rlim) == 0);
155         rlim.rlim_cur = rlim.rlim_max;
156         opr_Verify(setrlimit(RLIMIT_NOFILE, &rlim) == 0);
157         fdMaxCacheSize = rlim.rlim_cur - vol_io_params.fd_handle_setaside;
158 #ifdef AFS_NBSD_ENV
159         /* XXX this is to avoid using up all system fd netbsd is
160          * somewhat broken and have set maximum fd for a root process
161          * to the same as system fd that is avaible, so if the
162          * fileserver uses all up process fds, all system fd will be
163          * used up too !
164          *
165          * Check for this better
166          */
167         fdMaxCacheSize /= 4;
168 #endif
169         fdMaxCacheSize = min(fdMaxCacheSize, vol_io_params.fd_max_cachesize);
170         opr_Assert(fdMaxCacheSize > 0);
171     }
172 #elif defined(AFS_HPUX_ENV)
173     /* Avoid problems with "UFSOpen: igetinode failed" panics on HPUX 11.0 */
174     fdMaxCacheSize = 0;
175 #else
176     {
177         long fdMax = max(sysconf(_SC_OPEN_MAX) - vol_io_params.fd_handle_setaside,
178                                          0);
179         fdMaxCacheSize = (int)min(fdMax, vol_io_params.fd_max_cachesize);
180     }
181 #endif
182     fdCacheSize = min(fdMaxCacheSize, vol_io_params.fd_initial_cachesize);
183 }
184
185 /* Make the file descriptor cache as big as possible. Don't this call
186  * if the program uses fopen or fdopen, if fd_max_cachesize cannot be
187  * represented in the fileno member of the system FILE structure (or
188  * equivalent).
189  */
190 void
191 ih_UseLargeCache(void)
192 {
193     IH_LOCK;
194
195     if (!ih_PkgDefaultsSet) {
196         ih_PkgDefaults();
197     }
198
199     if (!ih_Inited) {
200         ih_Initialize();
201     }
202
203     fdCacheSize = fdMaxCacheSize;
204
205     IH_UNLOCK;
206 }
207
208 /* Allocate a chunk of inode handles */
209 void
210 iHandleAllocateChunk(void)
211 {
212     int i;
213     IHandle_t *ihP;
214
215     opr_Assert(ihAvailHead == NULL);
216     ihP = malloc(I_HANDLE_MALLOCSIZE * sizeof(IHandle_t));
217     opr_Assert(ihP != NULL);
218     for (i = 0; i < I_HANDLE_MALLOCSIZE; i++) {
219         ihP[i].ih_refcnt = 0;
220         DLL_INSERT_TAIL(&ihP[i], ihAvailHead, ihAvailTail, ih_next, ih_prev);
221     }
222 }
223
224 /* Initialize an inode handle */
225 IHandle_t *
226 ih_init(int dev, int vid, Inode ino)
227 {
228     int ihash = IH_HASH(dev, vid, ino);
229     IHandle_t *ihP;
230
231     if (!ih_PkgDefaultsSet) {
232         ih_PkgDefaults();
233     }
234
235     IH_LOCK;
236     if (!ih_Inited) {
237         ih_Initialize();
238     }
239
240     /* Do we already have a handle for this Inode? */
241     for (ihP = ihashTable[ihash].ihash_head; ihP; ihP = ihP->ih_next) {
242         if (ihP->ih_ino == ino && ihP->ih_vid == vid && ihP->ih_dev == dev) {
243             ihP->ih_refcnt++;
244             IH_UNLOCK;
245             return ihP;
246         }
247     }
248
249     /* Allocate and initialize a new Inode handle */
250     if (ihAvailHead == NULL) {
251         iHandleAllocateChunk();
252     }
253     ihP = ihAvailHead;
254     opr_Assert(ihP->ih_refcnt == 0);
255     DLL_DELETE(ihP, ihAvailHead, ihAvailTail, ih_next, ih_prev);
256     ihP->ih_dev = dev;
257     ihP->ih_vid = vid;
258     ihP->ih_ino = ino;
259     ihP->ih_flags = 0;
260     ihP->ih_synced = 0;
261     ihP->ih_refcnt = 1;
262     DLL_INIT_LIST(ihP->ih_fdhead, ihP->ih_fdtail);
263     DLL_INSERT_TAIL(ihP, ihashTable[ihash].ihash_head,
264                     ihashTable[ihash].ihash_tail, ih_next, ih_prev);
265     IH_UNLOCK;
266     return ihP;
267 }
268
269 /* Copy an inode handle */
270 IHandle_t *
271 ih_copy(IHandle_t * ihP)
272 {
273     IH_LOCK;
274     opr_Assert(ih_Inited);
275     opr_Assert(ihP->ih_refcnt > 0);
276     ihP->ih_refcnt++;
277     IH_UNLOCK;
278     return ihP;
279 }
280
281 /* Allocate a chunk of file descriptor handles */
282 void
283 fdHandleAllocateChunk(void)
284 {
285     int i;
286     FdHandle_t *fdP;
287
288     opr_Assert(fdAvailHead == NULL);
289     fdP = malloc(FD_HANDLE_MALLOCSIZE * sizeof(FdHandle_t));
290     opr_Assert(fdP != NULL);
291     for (i = 0; i < FD_HANDLE_MALLOCSIZE; i++) {
292         fdP[i].fd_status = FD_HANDLE_AVAIL;
293         fdP[i].fd_refcnt = 0;
294         fdP[i].fd_ih = NULL;
295         fdP[i].fd_fd = INVALID_FD;
296         fdP[i].fd_ihnext = NULL;
297         fdP[i].fd_ihprev = NULL;
298         DLL_INSERT_TAIL(&fdP[i], fdAvailHead, fdAvailTail, fd_next, fd_prev);
299     }
300 }
301
302 /* Allocate a chunk of stream handles */
303 void
304 streamHandleAllocateChunk(void)
305 {
306     int i;
307     StreamHandle_t *streamP;
308
309     opr_Assert(streamAvailHead == NULL);
310     streamP = (StreamHandle_t *)
311         malloc(STREAM_HANDLE_MALLOCSIZE * sizeof(StreamHandle_t));
312     opr_Assert(streamP != NULL);
313     for (i = 0; i < STREAM_HANDLE_MALLOCSIZE; i++) {
314         streamP[i].str_fd = INVALID_FD;
315         DLL_INSERT_TAIL(&streamP[i], streamAvailHead, streamAvailTail,
316                         str_next, str_prev);
317     }
318 }
319
320 /*
321  * Get a file descriptor handle given an Inode handle
322  * Takes the given file descriptor, and creates a new FdHandle_t for it,
323  * attached to the given IHandle_t. fd can be INVALID_FD, indicating that the
324  * caller failed to open the relevant file because we had too many FDs open;
325  * ih_attachfd_r will then just evict/close an existing fd in the cache, and
326  * return NULL.
327  */
328 static FdHandle_t *
329 ih_attachfd_r(IHandle_t *ihP, FD_t fd)
330 {
331     FD_t closeFd;
332     FdHandle_t *fdP;
333
334     /* fdCacheSize limits the size of the descriptor cache, but
335      * we permit the number of open files to exceed fdCacheSize.
336      * We only recycle open file descriptors when the number
337      * of open files reaches the size of the cache */
338     if ((fdInUseCount > fdCacheSize || fd == INVALID_FD)  && fdLruHead != NULL) {
339         fdP = fdLruHead;
340         opr_Assert(fdP->fd_status == FD_HANDLE_OPEN);
341         DLL_DELETE(fdP, fdLruHead, fdLruTail, fd_next, fd_prev);
342         DLL_DELETE(fdP, fdP->fd_ih->ih_fdhead, fdP->fd_ih->ih_fdtail,
343                    fd_ihnext, fd_ihprev);
344         closeFd = fdP->fd_fd;
345         if (fd == INVALID_FD) {
346             fdCacheSize--;          /* reduce in order to not run into here too often */
347             DLL_INSERT_TAIL(fdP, fdAvailHead, fdAvailTail, fd_next, fd_prev);
348             fdP->fd_status = FD_HANDLE_AVAIL;
349             fdP->fd_ih = NULL;
350             fdP->fd_fd = INVALID_FD;
351             IH_UNLOCK;
352             OS_CLOSE(closeFd);
353             IH_LOCK;
354             fdInUseCount -= 1;
355             return NULL;
356         }
357     } else {
358         if (fdAvailHead == NULL) {
359             fdHandleAllocateChunk();
360         }
361         fdP = fdAvailHead;
362         opr_Assert(fdP->fd_status == FD_HANDLE_AVAIL);
363         DLL_DELETE(fdP, fdAvailHead, fdAvailTail, fd_next, fd_prev);
364         closeFd = INVALID_FD;
365     }
366
367     fdP->fd_status = FD_HANDLE_INUSE;
368     fdP->fd_fd = fd;
369     fdP->fd_ih = ihP;
370     fdP->fd_refcnt++;
371
372     ihP->ih_refcnt++;
373
374     /* Add this handle to the Inode's list of open descriptors */
375     DLL_INSERT_TAIL(fdP, ihP->ih_fdhead, ihP->ih_fdtail, fd_ihnext,
376                     fd_ihprev);
377
378     if (closeFd != INVALID_FD) {
379         IH_UNLOCK;
380         OS_CLOSE(closeFd);
381         IH_LOCK;
382         fdInUseCount -= 1;
383     }
384
385     return fdP;
386 }
387
388 FdHandle_t *
389 ih_attachfd(IHandle_t *ihP, FD_t fd)
390 {
391     FdHandle_t *fdP;
392
393     IH_LOCK;
394
395     fdInUseCount += 1;
396
397     fdP = ih_attachfd_r(ihP, fd);
398     if (!fdP) {
399         fdInUseCount -= 1;
400     }
401
402     IH_UNLOCK;
403
404     return fdP;
405 }
406
407 /*
408  * Get a file descriptor handle given an Inode handle
409  */
410 FdHandle_t *
411 ih_open(IHandle_t * ihP)
412 {
413     FdHandle_t *fdP;
414     FD_t fd;
415
416     if (!ihP)                   /* XXX should log here in the fileserver */
417         return NULL;
418
419     IH_LOCK;
420
421     /* Do we already have an open file handle for this Inode? */
422     for (fdP = ihP->ih_fdtail; fdP != NULL; fdP = fdP->fd_ihprev) {
423         if (fdP->fd_status == FD_HANDLE_CLOSING) {
424             /* The handle was open when an IH_REALLYCLOSE was issued, so we
425              * cannot reuse it; it will be closed soon. */
426             continue;
427         }
428 #ifndef HAVE_PIO
429         /*
430          * If we don't have positional i/o, don't try to share fds, since
431          * we can't do so in a threadsafe way.
432          */
433         if (fdP->fd_status == FD_HANDLE_INUSE) {
434             continue;
435         }
436         opr_Assert(fdP->fd_status == FD_HANDLE_OPEN);
437 #else /* HAVE_PIO */
438         opr_Assert(fdP->fd_status != FD_HANDLE_AVAIL);
439 #endif /* HAVE_PIO */
440
441         fdP->fd_refcnt++;
442         if (fdP->fd_status == FD_HANDLE_OPEN) {
443             fdP->fd_status = FD_HANDLE_INUSE;
444             DLL_DELETE(fdP, fdLruHead, fdLruTail, fd_next, fd_prev);
445         }
446         ihP->ih_refcnt++;
447         IH_UNLOCK;
448         return fdP;
449     }
450
451     /*
452      * Try to open the Inode, return NULL on error.
453      */
454     fdInUseCount += 1;
455     IH_UNLOCK;
456 ih_open_retry:
457     fd = OS_IOPEN(ihP);
458     IH_LOCK;
459     if (fd == INVALID_FD && (errno != EMFILE || fdLruHead == NULL) ) {
460         fdInUseCount -= 1;
461         IH_UNLOCK;
462         return NULL;
463     }
464
465     fdP = ih_attachfd_r(ihP, fd);
466     if (!fdP) {
467         opr_Assert(fd == INVALID_FD);
468         IH_UNLOCK;
469         goto ih_open_retry;
470     }
471
472     IH_UNLOCK;
473
474     return fdP;
475 }
476
477 /*
478  * Return a file descriptor handle to the cache
479  */
480 int
481 fd_close(FdHandle_t * fdP)
482 {
483     IHandle_t *ihP;
484
485     if (!fdP)
486         return 0;
487
488     IH_LOCK;
489     opr_Assert(ih_Inited);
490     opr_Assert(fdInUseCount > 0);
491     opr_Assert(fdP->fd_status == FD_HANDLE_INUSE ||
492                fdP->fd_status == FD_HANDLE_CLOSING);
493
494     ihP = fdP->fd_ih;
495
496     /* Call fd_reallyclose to really close the unused file handles if
497      * the previous attempt to close (ih_reallyclose()) all file handles
498      * failed (this is determined by checking the ihandle for the flag
499      * IH_REALLY_CLOSED) or we have too many open files.
500      */
501     if (fdP->fd_status == FD_HANDLE_CLOSING ||
502         ihP->ih_flags & IH_REALLY_CLOSED || fdInUseCount > fdCacheSize) {
503         IH_UNLOCK;
504         return fd_reallyclose(fdP);
505     }
506
507     fdP->fd_refcnt--;
508     if (fdP->fd_refcnt == 0) {
509         /* Put this descriptor back into the cache */
510         fdP->fd_status = FD_HANDLE_OPEN;
511         DLL_INSERT_TAIL(fdP, fdLruHead, fdLruTail, fd_next, fd_prev);
512     }
513
514     /* If this is not the only reference to the Inode then we can decrement
515      * the reference count, otherwise we need to call ih_release.
516      */
517     if (ihP->ih_refcnt > 1)
518         ihP->ih_refcnt--;
519     else
520         _ih_release_r(ihP);
521
522     IH_UNLOCK;
523
524     return 0;
525 }
526
527 /*
528  * Actually close the file descriptor handle and return it to
529  * the free list.
530  */
531 int
532 fd_reallyclose(FdHandle_t * fdP)
533 {
534     FD_t closeFd;
535     IHandle_t *ihP;
536
537     if (!fdP)
538         return 0;
539
540     IH_LOCK;
541     opr_Assert(ih_Inited);
542     opr_Assert(fdInUseCount > 0);
543     opr_Assert(fdP->fd_status == FD_HANDLE_INUSE ||
544                fdP->fd_status == FD_HANDLE_CLOSING);
545
546     ihP = fdP->fd_ih;
547     closeFd = fdP->fd_fd;
548     fdP->fd_refcnt--;
549
550     if (fdP->fd_refcnt == 0) {
551         DLL_DELETE(fdP, ihP->ih_fdhead, ihP->ih_fdtail, fd_ihnext, fd_ihprev);
552         DLL_INSERT_TAIL(fdP, fdAvailHead, fdAvailTail, fd_next, fd_prev);
553
554         fdP->fd_status = FD_HANDLE_AVAIL;
555         fdP->fd_refcnt = 0;
556         fdP->fd_ih = NULL;
557         fdP->fd_fd = INVALID_FD;
558     }
559
560     /* All the file descriptor handles have been closed; reset
561      * the IH_REALLY_CLOSED flag indicating that ih_reallyclose
562      * has completed its job.
563      */
564     if (!ihP->ih_fdhead) {
565         ihP->ih_flags &= ~IH_REALLY_CLOSED;
566     } else {
567         FdHandle_t *lfdP, *next;
568         int clear = 1;
569         for (lfdP = ihP->ih_fdhead; lfdP != NULL; lfdP = next) {
570             next = lfdP->fd_ihnext;
571             osi_Assert(lfdP->fd_ih == ihP);
572             if (lfdP->fd_status != FD_HANDLE_CLOSING) {
573                 clear = 0;
574                 break;
575             }
576         }
577         /* no *future* fd should be subjected to this */
578         if (clear)
579             ihP->ih_flags &= ~IH_REALLY_CLOSED;
580     }
581
582     if (fdP->fd_refcnt == 0) {
583         IH_UNLOCK;
584         OS_CLOSE(closeFd);
585         IH_LOCK;
586         fdInUseCount -= 1;
587     }
588
589     /* If this is not the only reference to the Inode then we can decrement
590      * the reference count, otherwise we need to call ih_release. */
591     if (ihP->ih_refcnt > 1)
592         ihP->ih_refcnt--;
593     else
594         _ih_release_r(ihP);
595
596     IH_UNLOCK;
597
598     return 0;
599 }
600
601 /* Enable buffered I/O on a file descriptor */
602 StreamHandle_t *
603 stream_fdopen(FD_t fd)
604 {
605     StreamHandle_t *streamP;
606
607     IH_LOCK;
608     if (streamAvailHead == NULL) {
609         streamHandleAllocateChunk();
610     }
611     streamP = streamAvailHead;
612     DLL_DELETE(streamP, streamAvailHead, streamAvailTail, str_next, str_prev);
613     IH_UNLOCK;
614     streamP->str_fd = fd;
615     streamP->str_buflen = 0;
616     streamP->str_bufoff = 0;
617     streamP->str_fdoff = 0;
618     streamP->str_error = 0;
619     streamP->str_eof = 0;
620     streamP->str_direction = STREAM_DIRECTION_NONE;
621     return streamP;
622 }
623
624 /* Open a file for buffered I/O */
625 StreamHandle_t *
626 stream_open(const char *filename, const char *mode)
627 {
628     FD_t fd = INVALID_FD;
629
630     if (strcmp(mode, "r") == 0) {
631         fd = OS_OPEN(filename, O_RDONLY, 0);
632     } else if (strcmp(mode, "r+") == 0) {
633         fd = OS_OPEN(filename, O_RDWR, 0);
634     } else if (strcmp(mode, "w") == 0) {
635         fd = OS_OPEN(filename, O_WRONLY | O_TRUNC | O_CREAT, 0);
636     } else if (strcmp(mode, "w+") == 0) {
637         fd = OS_OPEN(filename, O_RDWR | O_TRUNC | O_CREAT, 0);
638     } else if (strcmp(mode, "a") == 0) {
639         fd = OS_OPEN(filename, O_WRONLY | O_APPEND | O_CREAT, 0);
640     } else if (strcmp(mode, "a+") == 0) {
641         fd = OS_OPEN(filename, O_RDWR | O_APPEND | O_CREAT, 0);
642     } else {
643         opr_abort();            /* not implemented */
644     }
645
646     if (fd == INVALID_FD) {
647         return NULL;
648     }
649     return stream_fdopen(fd);
650 }
651
652 /* fread for buffered I/O handles */
653 afs_sfsize_t
654 stream_read(void *ptr, afs_fsize_t size, afs_fsize_t nitems,
655             StreamHandle_t * streamP)
656 {
657     afs_fsize_t nbytes, bytesRead, bytesToRead;
658     char *p;
659
660     /* Need to seek before changing direction */
661     if (streamP->str_direction == STREAM_DIRECTION_NONE) {
662         streamP->str_direction = STREAM_DIRECTION_READ;
663         streamP->str_bufoff = 0;
664         streamP->str_buflen = 0;
665     } else {
666         opr_Assert(streamP->str_direction == STREAM_DIRECTION_READ);
667     }
668
669     bytesRead = 0;
670     nbytes = size * nitems;
671     p = (char *)ptr;
672     while (nbytes > 0 && !streamP->str_eof) {
673         if (streamP->str_buflen == 0) {
674             streamP->str_bufoff = 0;
675             streamP->str_buflen =
676                 OS_PREAD(streamP->str_fd, streamP->str_buffer,
677                         STREAM_HANDLE_BUFSIZE, streamP->str_fdoff);
678             if (streamP->str_buflen < 0) {
679                 streamP->str_error = errno;
680                 streamP->str_buflen = 0;
681                 bytesRead = 0;
682                 break;
683             } else if (streamP->str_buflen == 0) {
684                 streamP->str_eof = 1;
685                 break;
686             }
687             streamP->str_fdoff += streamP->str_buflen;
688         }
689
690         bytesToRead = nbytes;
691         if (bytesToRead > streamP->str_buflen) {
692             bytesToRead = streamP->str_buflen;
693         }
694         memcpy(p, streamP->str_buffer + streamP->str_bufoff, bytesToRead);
695         p += bytesToRead;
696         streamP->str_bufoff += bytesToRead;
697         streamP->str_buflen -= bytesToRead;
698         bytesRead += bytesToRead;
699         nbytes -= bytesToRead;
700     }
701
702     return (bytesRead / size);
703 }
704
705 /* fwrite for buffered I/O handles */
706 afs_sfsize_t
707 stream_write(void *ptr, afs_fsize_t size, afs_fsize_t nitems,
708              StreamHandle_t * streamP)
709 {
710     char *p;
711     afs_sfsize_t rc;
712     afs_fsize_t nbytes, bytesWritten, bytesToWrite;
713
714     /* Need to seek before changing direction */
715     if (streamP->str_direction == STREAM_DIRECTION_NONE) {
716         streamP->str_direction = STREAM_DIRECTION_WRITE;
717         streamP->str_bufoff = 0;
718         streamP->str_buflen = STREAM_HANDLE_BUFSIZE;
719     } else {
720         opr_Assert(streamP->str_direction == STREAM_DIRECTION_WRITE);
721     }
722
723     nbytes = size * nitems;
724     bytesWritten = 0;
725     p = (char *)ptr;
726     while (nbytes > 0) {
727         if (streamP->str_buflen == 0) {
728             rc = OS_PWRITE(streamP->str_fd, streamP->str_buffer,
729                           STREAM_HANDLE_BUFSIZE, streamP->str_fdoff);
730             if (rc < 0) {
731                 streamP->str_error = errno;
732                 bytesWritten = 0;
733                 break;
734             }
735             streamP->str_fdoff += rc;
736             streamP->str_bufoff = 0;
737             streamP->str_buflen = STREAM_HANDLE_BUFSIZE;
738         }
739
740         bytesToWrite = nbytes;
741         if (bytesToWrite > streamP->str_buflen) {
742             bytesToWrite = streamP->str_buflen;
743         }
744         memcpy(streamP->str_buffer + streamP->str_bufoff, p, bytesToWrite);
745         p += bytesToWrite;
746         streamP->str_bufoff += bytesToWrite;
747         streamP->str_buflen -= bytesToWrite;
748         bytesWritten += bytesToWrite;
749         nbytes -= bytesToWrite;
750     }
751
752     return (bytesWritten / size);
753 }
754
755 /* fseek for buffered I/O handles */
756 int
757 stream_aseek(StreamHandle_t * streamP, afs_foff_t offset)
758 {
759     ssize_t rc;
760     int retval = 0;
761
762     if (streamP->str_direction == STREAM_DIRECTION_WRITE
763         && streamP->str_bufoff > 0) {
764         rc = OS_PWRITE(streamP->str_fd, streamP->str_buffer,
765                       streamP->str_bufoff, streamP->str_fdoff);
766         if (rc < 0) {
767             streamP->str_error = errno;
768             retval = -1;
769         }
770     }
771     streamP->str_fdoff = offset;
772     streamP->str_bufoff = 0;
773     streamP->str_buflen = 0;
774     streamP->str_eof = 0;
775     streamP->str_direction = STREAM_DIRECTION_NONE;
776     return retval;
777 }
778
779 /* fflush for buffered I/O handles */
780 int
781 stream_flush(StreamHandle_t * streamP)
782 {
783     ssize_t rc;
784     int retval = 0;
785
786     if (streamP->str_direction == STREAM_DIRECTION_WRITE
787         && streamP->str_bufoff > 0) {
788         rc = OS_PWRITE(streamP->str_fd, streamP->str_buffer,
789                       streamP->str_bufoff, streamP->str_fdoff);
790         if (rc < 0) {
791             streamP->str_error = errno;
792             retval = -1;
793         } else {
794             streamP->str_fdoff += rc;
795         }
796         streamP->str_bufoff = 0;
797         streamP->str_buflen = STREAM_HANDLE_BUFSIZE;
798     }
799
800     return retval;
801 }
802
803 /* Free a buffered I/O handle */
804 int
805 stream_close(StreamHandle_t * streamP, int reallyClose)
806 {
807     ssize_t rc;
808     int retval = 0;
809
810     opr_Assert(streamP != NULL);
811     if (streamP->str_direction == STREAM_DIRECTION_WRITE
812         && streamP->str_bufoff > 0) {
813         rc = OS_PWRITE(streamP->str_fd, streamP->str_buffer,
814                       streamP->str_bufoff, streamP->str_fdoff);
815         if (rc < 0) {
816             retval = -1;
817         } else {
818             streamP->str_fdoff += rc;
819         }
820     }
821     if (reallyClose) {
822         rc = OS_CLOSE(streamP->str_fd);
823         if (rc < 0) {
824             retval = -1;
825         }
826     }
827     streamP->str_fd = INVALID_FD;
828
829     IH_LOCK;
830     DLL_INSERT_TAIL(streamP, streamAvailHead, streamAvailTail,
831                     str_next, str_prev);
832     IH_UNLOCK;
833     return retval;
834 }
835
836 /* Close all unused file descriptors associated with the inode
837  * handle. Called with IH_LOCK held. May drop and reacquire
838  * IH_LOCK. Sets the IH_REALLY_CLOSED flag in the inode handle
839  * if it fails to close all file handles.
840  */
841 static int
842 ih_fdclose(IHandle_t * ihP)
843 {
844     int closeCount, closedAll;
845     FdHandle_t *fdP, *head, *tail, *next;
846
847     opr_Assert(ihP->ih_refcnt > 0);
848
849     closedAll = 1;
850     DLL_INIT_LIST(head, tail);
851     ihP->ih_flags &= ~IH_REALLY_CLOSED;
852
853     /*
854      * Remove the file descriptors for this Inode from the LRU queue
855      * and the IHandle queue and put them on a temporary queue so we
856      * can drop the lock before we close the files.
857      */
858     for (fdP = ihP->ih_fdhead; fdP != NULL; fdP = next) {
859         next = fdP->fd_ihnext;
860         opr_Assert(fdP->fd_ih == ihP);
861         opr_Assert(fdP->fd_status == FD_HANDLE_OPEN
862                || fdP->fd_status == FD_HANDLE_INUSE
863                || fdP->fd_status == FD_HANDLE_CLOSING);
864         if (fdP->fd_status == FD_HANDLE_OPEN) {
865             /* Note that FdHandle_t's do not count against the parent
866              * IHandle_t ref count when they are FD_HANDLE_OPEN. So, we don't
867              * need to dec the parent IHandle_t ref count for each one we pull
868              * off here. */
869             DLL_DELETE(fdP, ihP->ih_fdhead, ihP->ih_fdtail, fd_ihnext,
870                        fd_ihprev);
871             DLL_DELETE(fdP, fdLruHead, fdLruTail, fd_next, fd_prev);
872             DLL_INSERT_TAIL(fdP, head, tail, fd_next, fd_prev);
873         } else {
874             closedAll = 0;
875             fdP->fd_status = FD_HANDLE_CLOSING;
876             ihP->ih_flags |= IH_REALLY_CLOSED;
877         }
878     }
879
880     /* If the ihandle reference count is 1, we should have
881      * closed all file descriptors.
882      */
883     if (ihP->ih_refcnt == 1 || closedAll) {
884         opr_Assert(closedAll);
885         opr_Assert(!ihP->ih_fdhead);
886         opr_Assert(!ihP->ih_fdtail);
887     }
888
889     if (head == NULL) {
890         return 0;               /* No file descriptors closed */
891     }
892
893     IH_UNLOCK;
894     /*
895      * Close the file descriptors
896      */
897     closeCount = 0;
898     for (fdP = head; fdP != NULL; fdP = fdP->fd_next) {
899         OS_CLOSE(fdP->fd_fd);
900         fdP->fd_status = FD_HANDLE_AVAIL;
901         fdP->fd_refcnt = 0;
902         fdP->fd_fd = INVALID_FD;
903         fdP->fd_ih = NULL;
904         closeCount++;
905     }
906
907     IH_LOCK;
908     opr_Assert(fdInUseCount >= closeCount);
909     fdInUseCount -= closeCount;
910
911     /*
912      * Append the temporary queue to the list of available descriptors
913      */
914     if (fdAvailHead == NULL) {
915         fdAvailHead = head;
916         fdAvailTail = tail;
917     } else {
918         fdAvailTail->fd_next = head;
919         head->fd_prev = fdAvailTail;
920         fdAvailTail = tail;
921     }
922
923     return 0;
924 }
925
926 /* Close all cached file descriptors for this inode. */
927 int
928 ih_reallyclose(IHandle_t * ihP)
929 {
930     if (!ihP)
931         return 0;
932
933     IH_LOCK;
934     ihP->ih_refcnt++;   /* must not disappear over unlock */
935     if (ihP->ih_synced) {
936         FdHandle_t *fdP;
937         opr_Assert(vol_io_params.sync_behavior != IH_SYNC_ALWAYS);
938         opr_Assert(vol_io_params.sync_behavior != IH_SYNC_NEVER);
939         ihP->ih_synced = 0;
940         IH_UNLOCK;
941
942         fdP = IH_OPEN(ihP);
943         if (fdP) {
944             OS_SYNC(fdP->fd_fd);
945             FDH_CLOSE(fdP);
946         }
947
948         IH_LOCK;
949     }
950
951     opr_Assert(ihP->ih_refcnt > 0);
952
953     ih_fdclose(ihP);
954
955     if (ihP->ih_refcnt > 1)
956         ihP->ih_refcnt--;
957     else
958         _ih_release_r(ihP);
959
960     IH_UNLOCK;
961     return 0;
962 }
963
964 /* Release an Inode handle. All cached file descriptors for this
965  * inode are closed when the last reference to this handle is released
966  */
967 static int
968 _ih_release_r(IHandle_t * ihP)
969 {
970     int ihash;
971
972     if (!ihP)
973         return 0;
974
975     opr_Assert(ihP->ih_refcnt > 0);
976
977     if (ihP->ih_refcnt > 1) {
978         ihP->ih_refcnt--;
979         return 0;
980     }
981
982     ihash = IH_HASH(ihP->ih_dev, ihP->ih_vid, ihP->ih_ino);
983     DLL_DELETE(ihP, ihashTable[ihash].ihash_head,
984                ihashTable[ihash].ihash_tail, ih_next, ih_prev);
985
986     ih_fdclose(ihP);
987
988     ihP->ih_refcnt--;
989
990     DLL_INSERT_TAIL(ihP, ihAvailHead, ihAvailTail, ih_next, ih_prev);
991
992     return 0;
993 }
994
995 /* Release an Inode handle. All cached file descriptors for this
996  * inode are closed when the last reference to this handle is released
997  */
998 int
999 ih_release(IHandle_t * ihP)
1000 {
1001     int ret;
1002
1003     if (!ihP)
1004         return 0;
1005
1006     IH_LOCK;
1007     ret = _ih_release_r(ihP);
1008     IH_UNLOCK;
1009     return ret;
1010 }
1011
1012 /* Sync an inode to disk if its handle isn't NULL */
1013 int
1014 ih_condsync(IHandle_t * ihP)
1015 {
1016     int code;
1017     FdHandle_t *fdP;
1018
1019     if (!ihP)
1020         return 0;
1021
1022     fdP = IH_OPEN(ihP);
1023     if (fdP == NULL)
1024         return -1;
1025
1026     code = FDH_SYNC(fdP);
1027     FDH_CLOSE(fdP);
1028
1029     return code;
1030 }
1031
1032 /*************************************************************************
1033  * OS specific support routines.
1034  *************************************************************************/
1035 #ifndef AFS_NAMEI_ENV
1036 Inode
1037 ih_icreate(IHandle_t * ih, int dev, char *part, Inode nI, int p1, int p2,
1038            int p3, int p4)
1039 {
1040     Inode ino;
1041 #ifdef  AFS_3DISPARES
1042     /* See viceinode.h */
1043     if (p2 == INODESPECIAL) {
1044         int tp = p3;
1045         p3 = p4;
1046         p4 = tp;
1047     }
1048 #endif
1049     ino = ICREATE(dev, part, nI, p1, p2, p3, p4);
1050     return ino;
1051 }
1052 #endif /* AFS_NAMEI_ENV */
1053
1054 #if defined(AFS_NT40_ENV) || !defined(AFS_NAMEI_ENV)
1055 /* Unix namei implements its own more efficient IH_CREATE_INIT; this wrapper
1056  * is for everyone else */
1057 IHandle_t *
1058 ih_icreate_init(IHandle_t *lh, int dev, char *part, Inode nearInode,
1059                 afs_uint32 p1, afs_uint32 p2, afs_uint32 p3, afs_uint32 p4)
1060 {
1061     IHandle_t *ihP;
1062     Inode ino = IH_CREATE(lh, dev, part, nearInode, p1, p2, p3, p4);
1063     if (!VALID_INO(ino)) {
1064         return NULL;
1065     }
1066     IH_INIT(ihP, dev, p1, ino);
1067     return ihP;
1068 }
1069 #endif
1070
1071 afs_sfsize_t
1072 ih_size(FD_t fd)
1073 {
1074 #ifdef AFS_NT40_ENV
1075     LARGE_INTEGER size;
1076     if (!GetFileSizeEx(fd, &size))
1077         return -1;
1078     return size.QuadPart;
1079 #else
1080     struct afs_stat_st status;
1081     if (afs_fstat(fd, &status) < 0)
1082         return -1;
1083     return status.st_size;
1084 #endif
1085 }
1086
1087 #ifndef HAVE_PIO
1088 ssize_t
1089 ih_pread(int fd, void * buf, size_t count, afs_foff_t offset)
1090 {
1091         afs_foff_t code;
1092         code = OS_SEEK(fd, offset, 0);
1093         if (code < 0)
1094             return code;
1095         return OS_READ(fd, buf, count);
1096 }
1097
1098 ssize_t
1099 ih_pwrite(int fd, const void * buf, size_t count, afs_foff_t offset)
1100 {
1101         afs_foff_t code;
1102         code = OS_SEEK(fd, offset, 0);
1103         if (code < 0)
1104             return code;
1105         return OS_WRITE(fd, buf, count);
1106 }
1107 #endif /* !HAVE_PIO */
1108
1109 #ifndef AFS_NT40_ENV
1110 int
1111 ih_isunlinked(int fd)
1112 {
1113     struct afs_stat_st status;
1114     if (afs_fstat(fd, &status) < 0) {
1115         return -1;
1116     }
1117     if (status.st_nlink < 1) {
1118         return 1;
1119     }
1120     return 0;
1121 }
1122 #endif /* !AFS_NT40_ENV */
1123
1124 int
1125 ih_fdsync(FdHandle_t *fdP)
1126 {
1127     switch (vol_io_params.sync_behavior) {
1128     case IH_SYNC_ALWAYS:
1129         return OS_SYNC(fdP->fd_fd);
1130     case IH_SYNC_ONCLOSE:
1131         if (fdP->fd_ih) {
1132             fdP->fd_ih->ih_synced = 1;
1133             return 0;
1134         }
1135         return 1;
1136     case IH_SYNC_NEVER:
1137         return 0;
1138     default:
1139         opr_Assert(0);
1140     }
1141 }