vol-prototypes-20090316
[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 RCSID
18     ("$Header$");
19
20 #include <stdio.h>
21 #include <sys/types.h>
22 #include <errno.h>
23 #include <string.h>
24 #ifdef AFS_NT40_ENV
25 #include <fcntl.h>
26 #else
27 #include <sys/file.h>
28 #include <unistd.h>
29 #include <sys/stat.h>
30 #if defined(AFS_SUN5_ENV) || defined(AFS_NBSD_ENV)
31 #include <sys/fcntl.h>
32 #include <sys/resource.h>
33 #endif
34 #endif
35
36 #include <rx/xdr.h>
37 #include <afs/afsint.h>
38 #include <errno.h>
39 #include <afs/afssyscalls.h>
40 #include "nfs.h"
41 #include "ihandle.h"
42 #include "viceinode.h"
43 #ifdef AFS_PTHREAD_ENV
44 #include <assert.h>
45 #else /* AFS_PTHREAD_ENV */
46 #include "afs/assert.h"
47 #endif /* AFS_PTHREAD_ENV */
48 #include <limits.h>
49
50 #ifndef AFS_NT40_ENV
51 #ifdef O_LARGEFILE
52 #define afs_stat        stat64
53 #define afs_fstat       fstat64
54 #else /* !O_LARGEFILE */
55 #define afs_stat        stat
56 #define afs_fstat       fstat
57 #endif /* !O_LARGEFILE */
58 #endif /* AFS_NT40_ENV */
59
60 #ifdef AFS_PTHREAD_ENV
61 pthread_once_t ih_glock_once = PTHREAD_ONCE_INIT;
62 pthread_mutex_t ih_glock_mutex;
63 #endif /* AFS_PTHREAD_ENV */
64
65 /* Linked list of available inode handles */
66 IHandle_t *ihAvailHead;
67 IHandle_t *ihAvailTail;
68
69 /* Linked list of available file descriptor handles */
70 FdHandle_t *fdAvailHead;
71 FdHandle_t *fdAvailTail;
72
73 /* Linked list of available stream descriptor handles */
74 StreamHandle_t *streamAvailHead;
75 StreamHandle_t *streamAvailTail;
76
77 /* LRU list for file descriptor handles */
78 FdHandle_t *fdLruHead;
79 FdHandle_t *fdLruTail;
80
81 int ih_Inited = 0;
82
83 /* Most of the servers use fopen/fdopen. Since the FILE structure
84  * only has eight bits for the file descriptor, the cache size
85  * has to be less than 256. The cache can be made larger as long
86  * as you are sure you don't need fopen/fdopen. */
87 int fdMaxCacheSize = 0;
88 int fdCacheSize = 0;
89
90 /* Number of in use file descriptors */
91 int fdInUseCount = 0;
92
93 /* Hash table for inode handles */
94 IHashBucket_t ihashTable[I_HANDLE_HASH_SIZE];
95
96 void *ih_sync_thread(void *);
97
98 #ifdef AFS_PTHREAD_ENV
99 /* Initialize the global ihandle mutex */
100 void
101 ih_glock_init()
102 {
103     assert(pthread_mutex_init(&ih_glock_mutex, NULL) == 0);
104 }
105 #endif /* AFS_PTHREAD_ENV */
106
107 /* Initialize the file descriptor cache */
108 void
109 ih_Initialize(void)
110 {
111     int i;
112     assert(!ih_Inited);
113     ih_Inited = 1;
114     DLL_INIT_LIST(ihAvailHead, ihAvailTail);
115     DLL_INIT_LIST(fdAvailHead, fdAvailTail);
116     DLL_INIT_LIST(fdLruHead, fdLruTail);
117     for (i = 0; i < I_HANDLE_HASH_SIZE; i++) {
118         DLL_INIT_LIST(ihashTable[i].ihash_head, ihashTable[i].ihash_tail);
119     }
120 #if defined(AFS_NT40_ENV)
121     fdMaxCacheSize = FD_MAX_CACHESIZE;
122 #elif defined(AFS_SUN5_ENV) || defined(AFS_NBSD_ENV)
123     {
124         struct rlimit rlim;
125         assert(getrlimit(RLIMIT_NOFILE, &rlim) == 0);
126         rlim.rlim_cur = rlim.rlim_max;
127         assert(setrlimit(RLIMIT_NOFILE, &rlim) == 0);
128         fdMaxCacheSize = rlim.rlim_cur - FD_HANDLE_SETASIDE;
129 #ifdef AFS_NBSD_ENV
130         /* XXX this is to avoid using up all system fd netbsd is
131          * somewhat broken and have set maximum fd for a root process
132          * to the same as system fd that is avaible, so if the
133          * fileserver uses all up process fds, all system fd will be
134          * used up too !
135          *
136          * Check for this better
137          */
138         fdMaxCacheSize /= 4;
139 #endif
140         fdMaxCacheSize = MIN(fdMaxCacheSize, FD_MAX_CACHESIZE);
141         assert(fdMaxCacheSize > 0);
142     }
143 #elif defined(AFS_HPUX_ENV)
144     /* Avoid problems with "UFSOpen: igetinode failed" panics on HPUX 11.0 */
145     fdMaxCacheSize = 0;
146 #else
147     {
148         long fdMax = MAX(sysconf(_SC_OPEN_MAX) - FD_HANDLE_SETASIDE, 0);
149         fdMaxCacheSize = (int)MIN(fdMax, FD_MAX_CACHESIZE);
150     }
151 #endif
152     fdCacheSize = MIN(fdMaxCacheSize, FD_DEFAULT_CACHESIZE);
153
154     {
155 #ifdef AFS_PTHREAD_ENV
156         pthread_t syncer;
157         pthread_attr_t tattr;
158
159         pthread_attr_init(&tattr);
160         pthread_attr_setdetachstate(&tattr,PTHREAD_CREATE_DETACHED);
161
162         pthread_create(&syncer, &tattr, ih_sync_thread, NULL);
163 #else /* AFS_PTHREAD_ENV */
164         PROCESS syncer;
165         LWP_CreateProcess(ih_sync_thread, 16*1024, LWP_MAX_PRIORITY - 2,
166             NULL, "ih_syncer", &syncer);
167 #endif /* AFS_PTHREAD_ENV */
168     }
169
170 }
171
172 /* Make the file descriptor cache as big as possible. Don't this call
173  * if the program uses fopen or fdopen. */
174 void
175 ih_UseLargeCache(void)
176 {
177     IH_LOCK;
178     if (!ih_Inited) {
179         ih_Initialize();
180     }
181     fdCacheSize = fdMaxCacheSize;
182
183     IH_UNLOCK;
184 }
185
186 /* Allocate a chunk of inode handles */
187 void
188 iHandleAllocateChunk(void)
189 {
190     int i;
191     IHandle_t *ihP;
192
193     assert(ihAvailHead == NULL);
194     ihP = (IHandle_t *) malloc(I_HANDLE_MALLOCSIZE * sizeof(IHandle_t));
195     assert(ihP != NULL);
196     for (i = 0; i < I_HANDLE_MALLOCSIZE; i++) {
197         ihP[i].ih_refcnt = 0;
198         DLL_INSERT_TAIL(&ihP[i], ihAvailHead, ihAvailTail, ih_next, ih_prev);
199     }
200 }
201
202 /* Initialize an inode handle */
203 IHandle_t *
204 ih_init(int dev, int vid, Inode ino)
205 {
206     int ihash = IH_HASH(dev, vid, ino);
207     IHandle_t *ihP;
208
209     IH_LOCK;
210     if (!ih_Inited) {
211         ih_Initialize();
212     }
213
214     /* Do we already have a handle for this Inode? */
215     for (ihP = ihashTable[ihash].ihash_head; ihP; ihP = ihP->ih_next) {
216         if (ihP->ih_ino == ino && ihP->ih_vid == vid && ihP->ih_dev == dev) {
217             ihP->ih_refcnt++;
218             IH_UNLOCK;
219             return ihP;
220         }
221     }
222
223     /* Allocate and initialize a new Inode handle */
224     if (ihAvailHead == NULL) {
225         iHandleAllocateChunk();
226     }
227     ihP = ihAvailHead;
228     assert(ihP->ih_refcnt == 0);
229     DLL_DELETE(ihP, ihAvailHead, ihAvailTail, ih_next, ih_prev);
230     ihP->ih_dev = dev;
231     ihP->ih_vid = vid;
232     ihP->ih_ino = ino;
233     ihP->ih_flags = 0;
234     ihP->ih_refcnt = 1;
235     DLL_INIT_LIST(ihP->ih_fdhead, ihP->ih_fdtail);
236     DLL_INSERT_TAIL(ihP, ihashTable[ihash].ihash_head,
237                     ihashTable[ihash].ihash_tail, ih_next, ih_prev);
238     IH_UNLOCK;
239     return ihP;
240 }
241
242 /* Copy an inode handle */
243 IHandle_t *
244 ih_copy(IHandle_t * ihP)
245 {
246     IH_LOCK;
247     assert(ih_Inited);
248     assert(ihP->ih_refcnt > 0);
249     ihP->ih_refcnt++;
250     IH_UNLOCK;
251     return ihP;
252 }
253
254 /* Allocate a chunk of file descriptor handles */
255 void
256 fdHandleAllocateChunk(void)
257 {
258     int i;
259     FdHandle_t *fdP;
260
261     assert(fdAvailHead == NULL);
262     fdP = (FdHandle_t *) malloc(FD_HANDLE_MALLOCSIZE * sizeof(FdHandle_t));
263     assert(fdP != NULL);
264     for (i = 0; i < FD_HANDLE_MALLOCSIZE; i++) {
265         fdP[i].fd_status = FD_HANDLE_AVAIL;
266         fdP[i].fd_ih = NULL;
267         fdP[i].fd_fd = INVALID_FD;
268         DLL_INSERT_TAIL(&fdP[i], fdAvailHead, fdAvailTail, fd_next, fd_prev);
269     }
270 }
271
272 /* Allocate a chunk of stream handles */
273 void
274 streamHandleAllocateChunk(void)
275 {
276     int i;
277     StreamHandle_t *streamP;
278
279     assert(streamAvailHead == NULL);
280     streamP = (StreamHandle_t *)
281         malloc(STREAM_HANDLE_MALLOCSIZE * sizeof(StreamHandle_t));
282     assert(streamP != NULL);
283     for (i = 0; i < STREAM_HANDLE_MALLOCSIZE; i++) {
284         streamP[i].str_fd = INVALID_FD;
285         DLL_INSERT_TAIL(&streamP[i], streamAvailHead, streamAvailTail,
286                         str_next, str_prev);
287     }
288 }
289
290 /*
291  * Get a file descriptor handle given an Inode handle
292  */
293 FdHandle_t *
294 ih_open(IHandle_t * ihP)
295 {
296     FdHandle_t *fdP;
297     FD_t fd;
298     FD_t closeFd;
299
300     if (!ihP)                   /* XXX should log here in the fileserver */
301         return NULL;
302
303     IH_LOCK;
304
305     /* Do we already have an open file handle for this Inode? */
306     for (fdP = ihP->ih_fdtail; fdP != NULL; fdP = fdP->fd_ihprev) {
307         if (fdP->fd_status != FD_HANDLE_INUSE) {
308             assert(fdP->fd_status == FD_HANDLE_OPEN);
309             fdP->fd_status = FD_HANDLE_INUSE;
310             DLL_DELETE(fdP, fdLruHead, fdLruTail, fd_next, fd_prev);
311             ihP->ih_refcnt++;
312             IH_UNLOCK;
313             (void)FDH_SEEK(fdP, 0, SEEK_SET);
314             return fdP;
315         }
316     }
317
318     /*
319      * Try to open the Inode, return NULL on error.
320      */
321     fdInUseCount += 1;
322     IH_UNLOCK;
323 ih_open_retry:
324     fd = OS_IOPEN(ihP);
325     IH_LOCK;
326     if (fd == INVALID_FD && (errno != EMFILE || fdLruHead == NULL) ) {
327         fdInUseCount -= 1;
328         IH_UNLOCK;
329         return NULL;
330     }
331
332     /* fdCacheSize limits the size of the descriptor cache, but
333      * we permit the number of open files to exceed fdCacheSize.
334      * We only recycle open file descriptors when the number
335      * of open files reaches the size of the cache */
336     if ((fdInUseCount > fdCacheSize || fd == INVALID_FD)  && fdLruHead != NULL) {
337         fdP = fdLruHead;
338         assert(fdP->fd_status == FD_HANDLE_OPEN);
339         DLL_DELETE(fdP, fdLruHead, fdLruTail, fd_next, fd_prev);
340         DLL_DELETE(fdP, fdP->fd_ih->ih_fdhead, fdP->fd_ih->ih_fdtail,
341                    fd_ihnext, fd_ihprev);
342         closeFd = fdP->fd_fd;
343         if (fd == INVALID_FD) {
344             fdCacheSize--;          /* reduce in order to not run into here too often */
345             DLL_INSERT_TAIL(fdP, fdAvailHead, fdAvailTail, fd_next, fd_prev);
346             fdP->fd_status = FD_HANDLE_AVAIL;
347             fdP->fd_ih = NULL;
348             fdP->fd_fd = INVALID_FD;
349             IH_UNLOCK;
350             OS_CLOSE(closeFd);
351             goto ih_open_retry;
352         }
353     } else {
354         if (fdAvailHead == NULL) {
355             fdHandleAllocateChunk();
356         }
357         fdP = fdAvailHead;
358         assert(fdP->fd_status == FD_HANDLE_AVAIL);
359         DLL_DELETE(fdP, fdAvailHead, fdAvailTail, fd_next, fd_prev);
360         closeFd = INVALID_FD;
361     }
362
363     fdP->fd_status = FD_HANDLE_INUSE;
364     fdP->fd_fd = fd;
365     fdP->fd_ih = ihP;
366
367     ihP->ih_refcnt++;
368
369     /* Add this handle to the Inode's list of open descriptors */
370     DLL_INSERT_TAIL(fdP, ihP->ih_fdhead, ihP->ih_fdtail, fd_ihnext,
371                     fd_ihprev);
372
373     if (closeFd != INVALID_FD) {
374         IH_UNLOCK;
375         OS_CLOSE(closeFd);
376         IH_LOCK;
377         fdInUseCount -= 1;
378     }
379
380     IH_UNLOCK;
381     return fdP;
382 }
383
384 /*
385  * Return a file descriptor handle to the cache
386  */
387 int
388 fd_close(FdHandle_t * fdP)
389 {
390     IHandle_t *ihP;
391
392     if (!fdP)
393         return 0;
394
395     IH_LOCK;
396     assert(ih_Inited);
397     assert(fdInUseCount > 0);
398     assert(fdP->fd_status == FD_HANDLE_INUSE);
399
400     ihP = fdP->fd_ih;
401
402     /* Call fd_reallyclose to really close the unused file handles if
403      * the previous attempt to close (ih_reallyclose()) all file handles
404      * failed (this is determined by checking the ihandle for the flag
405      * IH_REALLY_CLOSED) or we have too many open files.
406      */
407     if (ihP->ih_flags & IH_REALLY_CLOSED || fdInUseCount > fdCacheSize) {
408         IH_UNLOCK;
409         return fd_reallyclose(fdP);
410     }
411
412     /* Put this descriptor back into the cache */
413     fdP->fd_status = FD_HANDLE_OPEN;
414     DLL_INSERT_TAIL(fdP, fdLruHead, fdLruTail, fd_next, fd_prev);
415
416     /* If this is not the only reference to the Inode then we can decrement
417      * the reference count, otherwise we need to call ih_release.
418      */
419     if (ihP->ih_refcnt > 1) {
420         ihP->ih_refcnt--;
421         IH_UNLOCK;
422     } else {
423         IH_UNLOCK;
424         ih_release(ihP);
425     }
426
427     return 0;
428 }
429
430 /*
431  * Actually close the file descriptor handle and return it to
432  * the free list.
433  */
434 int
435 fd_reallyclose(FdHandle_t * fdP)
436 {
437     FD_t closeFd;
438     IHandle_t *ihP;
439
440     if (!fdP)
441         return 0;
442
443     IH_LOCK;
444     assert(ih_Inited);
445     assert(fdInUseCount > 0);
446     assert(fdP->fd_status == FD_HANDLE_INUSE);
447
448     ihP = fdP->fd_ih;
449     closeFd = fdP->fd_fd;
450
451     DLL_DELETE(fdP, ihP->ih_fdhead, ihP->ih_fdtail, fd_ihnext, fd_ihprev);
452     DLL_INSERT_TAIL(fdP, fdAvailHead, fdAvailTail, fd_next, fd_prev);
453
454     fdP->fd_status = FD_HANDLE_AVAIL;
455     fdP->fd_ih = NULL;
456     fdP->fd_fd = INVALID_FD;
457
458     /* All the file descriptor handles have been closed; reset
459      * the IH_REALLY_CLOSED flag indicating that ih_reallyclose
460      * has completed its job.
461      */
462     if (!ihP->ih_fdhead) {
463         ihP->ih_flags &= ~IH_REALLY_CLOSED;
464     }
465
466     IH_UNLOCK;
467     OS_CLOSE(closeFd);
468     IH_LOCK;
469     fdInUseCount -= 1;
470
471     /* If this is not the only reference to the Inode then we can decrement
472      * the reference count, otherwise we need to call ih_release. */
473     if (ihP->ih_refcnt > 1) {
474         ihP->ih_refcnt--;
475         IH_UNLOCK;
476     } else {
477         IH_UNLOCK;
478         ih_release(ihP);
479     }
480
481     return 0;
482 }
483
484 /* Enable buffered I/O on a file descriptor */
485 StreamHandle_t *
486 stream_fdopen(FD_t fd)
487 {
488     StreamHandle_t *streamP;
489
490     IH_LOCK;
491     if (streamAvailHead == NULL) {
492         streamHandleAllocateChunk();
493     }
494     streamP = streamAvailHead;
495     DLL_DELETE(streamP, streamAvailHead, streamAvailTail, str_next, str_prev);
496     IH_UNLOCK;
497     streamP->str_fd = fd;
498     streamP->str_buflen = 0;
499     streamP->str_bufoff = 0;
500     streamP->str_error = 0;
501     streamP->str_eof = 0;
502     streamP->str_direction = STREAM_DIRECTION_NONE;
503     return streamP;
504 }
505
506 /* Open a file for buffered I/O */
507 StreamHandle_t *
508 stream_open(const char *filename, const char *mode)
509 {
510     FD_t fd = INVALID_FD;
511
512     if (strcmp(mode, "r") == 0) {
513         fd = OS_OPEN(filename, O_RDONLY, 0);
514     } else if (strcmp(mode, "r+") == 0) {
515         fd = OS_OPEN(filename, O_RDWR, 0);
516     } else if (strcmp(mode, "w") == 0) {
517         fd = OS_OPEN(filename, O_WRONLY | O_TRUNC | O_CREAT, 0);
518     } else if (strcmp(mode, "w+") == 0) {
519         fd = OS_OPEN(filename, O_RDWR | O_TRUNC | O_CREAT, 0);
520     } else if (strcmp(mode, "a") == 0) {
521         fd = OS_OPEN(filename, O_WRONLY | O_APPEND | O_CREAT, 0);
522     } else if (strcmp(mode, "a+") == 0) {
523         fd = OS_OPEN(filename, O_RDWR | O_APPEND | O_CREAT, 0);
524     } else {
525         assert(FALSE);          /* not implemented */
526     }
527
528     if (fd == INVALID_FD) {
529         return NULL;
530     }
531     return stream_fdopen(fd);
532 }
533
534 /* fread for buffered I/O handles */
535 afs_sfsize_t
536 stream_read(void *ptr, afs_fsize_t size, afs_fsize_t nitems,
537             StreamHandle_t * streamP)
538 {
539     afs_fsize_t nbytes, bytesRead, bytesToRead;
540     char *p;
541
542     /* Need to seek before changing direction */
543     if (streamP->str_direction == STREAM_DIRECTION_NONE) {
544         streamP->str_direction = STREAM_DIRECTION_READ;
545         streamP->str_bufoff = 0;
546         streamP->str_buflen = 0;
547     } else {
548         assert(streamP->str_direction == STREAM_DIRECTION_READ);
549     }
550
551     bytesRead = 0;
552     nbytes = size * nitems;
553     p = (char *)ptr;
554     while (nbytes > 0 && !streamP->str_eof) {
555         if (streamP->str_buflen == 0) {
556             streamP->str_bufoff = 0;
557             streamP->str_buflen =
558                 OS_READ(streamP->str_fd, streamP->str_buffer,
559                         STREAM_HANDLE_BUFSIZE);
560             if (streamP->str_buflen < 0) {
561                 streamP->str_error = errno;
562                 streamP->str_buflen = 0;
563                 bytesRead = 0;
564                 break;
565             } else if (streamP->str_buflen == 0) {
566                 streamP->str_eof = 1;
567                 break;
568             }
569         }
570
571         bytesToRead = nbytes;
572         if (bytesToRead > streamP->str_buflen) {
573             bytesToRead = streamP->str_buflen;
574         }
575         memcpy(p, streamP->str_buffer + streamP->str_bufoff, bytesToRead);
576         p += bytesToRead;
577         streamP->str_bufoff += bytesToRead;
578         streamP->str_buflen -= bytesToRead;
579         bytesRead += bytesToRead;
580         nbytes -= bytesToRead;
581     }
582
583     return (bytesRead / size);
584 }
585
586 /* fwrite for buffered I/O handles */
587 afs_sfsize_t
588 stream_write(void *ptr, afs_fsize_t size, afs_fsize_t nitems,
589              StreamHandle_t * streamP)
590 {
591     char *p;
592     afs_sfsize_t rc;
593     afs_fsize_t nbytes, bytesWritten, bytesToWrite;
594
595     /* Need to seek before changing direction */
596     if (streamP->str_direction == STREAM_DIRECTION_NONE) {
597         streamP->str_direction = STREAM_DIRECTION_WRITE;
598         streamP->str_bufoff = 0;
599         streamP->str_buflen = STREAM_HANDLE_BUFSIZE;
600     } else {
601         assert(streamP->str_direction == STREAM_DIRECTION_WRITE);
602     }
603
604     nbytes = size * nitems;
605     bytesWritten = 0;
606     p = (char *)ptr;
607     while (nbytes > 0) {
608         if (streamP->str_buflen == 0) {
609             rc = OS_WRITE(streamP->str_fd, streamP->str_buffer,
610                           STREAM_HANDLE_BUFSIZE);
611             if (rc < 0) {
612                 streamP->str_error = errno;
613                 bytesWritten = 0;
614                 break;
615             }
616             streamP->str_bufoff = 0;
617             streamP->str_buflen = STREAM_HANDLE_BUFSIZE;
618         }
619
620         bytesToWrite = nbytes;
621         if (bytesToWrite > streamP->str_buflen) {
622             bytesToWrite = streamP->str_buflen;
623         }
624         memcpy(streamP->str_buffer + streamP->str_bufoff, p, bytesToWrite);
625         p += bytesToWrite;
626         streamP->str_bufoff += bytesToWrite;
627         streamP->str_buflen -= bytesToWrite;
628         bytesWritten += bytesToWrite;
629         nbytes -= bytesToWrite;
630     }
631
632     return (bytesWritten / size);
633 }
634
635 /* fseek for buffered I/O handles */
636 int
637 stream_seek(StreamHandle_t * streamP, afs_foff_t offset, int whence)
638 {
639     int rc;
640     int retval = 0;
641
642     if (streamP->str_direction == STREAM_DIRECTION_WRITE
643         && streamP->str_bufoff > 0) {
644         rc = OS_WRITE(streamP->str_fd, streamP->str_buffer,
645                       streamP->str_bufoff);
646         if (rc < 0) {
647             streamP->str_error = errno;
648             retval = -1;
649         }
650     }
651     streamP->str_bufoff = 0;
652     streamP->str_buflen = 0;
653     streamP->str_eof = 0;
654     streamP->str_direction = STREAM_DIRECTION_NONE;
655     if (OS_SEEK(streamP->str_fd, offset, whence) < 0) {
656         streamP->str_error = errno;
657         retval = -1;
658     }
659     return retval;
660 }
661
662 /* fflush for buffered I/O handles */
663 int
664 stream_flush(StreamHandle_t * streamP)
665 {
666     int rc;
667     int retval = 0;
668
669     if (streamP->str_direction == STREAM_DIRECTION_WRITE
670         && streamP->str_bufoff > 0) {
671         rc = OS_WRITE(streamP->str_fd, streamP->str_buffer,
672                       streamP->str_bufoff);
673         if (rc < 0) {
674             streamP->str_error = errno;
675             retval = -1;
676         }
677         streamP->str_bufoff = 0;
678         streamP->str_buflen = STREAM_HANDLE_BUFSIZE;
679     }
680
681     return retval;
682 }
683
684 /* Free a buffered I/O handle */
685 int
686 stream_close(StreamHandle_t * streamP, int reallyClose)
687 {
688     int rc;
689     int retval = 0;
690
691     assert(streamP != NULL);
692     if (streamP->str_direction == STREAM_DIRECTION_WRITE
693         && streamP->str_bufoff > 0) {
694         rc = OS_WRITE(streamP->str_fd, streamP->str_buffer,
695                       streamP->str_bufoff);
696         if (rc < 0) {
697             retval = -1;
698         }
699     }
700     if (reallyClose) {
701         rc = OS_CLOSE(streamP->str_fd);
702         if (rc < 0) {
703             retval = -1;
704         }
705     }
706     streamP->str_fd = INVALID_FD;
707
708     IH_LOCK;
709     DLL_INSERT_TAIL(streamP, streamAvailHead, streamAvailTail,
710                     str_next, str_prev);
711     IH_UNLOCK;
712     return retval;
713 }
714
715 /* Close all unused file descriptors associated with the inode
716  * handle. Called with IH_LOCK held. May drop and reacquire
717  * IH_LOCK. Sets the IH_REALLY_CLOSED flag in the inode handle
718  * if it fails to close all file handles.
719  */
720 static int
721 ih_fdclose(IHandle_t * ihP)
722 {
723     int closeCount, closedAll;
724     FdHandle_t *fdP, *head, *tail, *next;
725
726     assert(ihP->ih_refcnt > 0);
727
728     closedAll = 1;
729     DLL_INIT_LIST(head, tail);
730     ihP->ih_flags &= ~IH_REALLY_CLOSED;
731
732     /*
733      * Remove the file descriptors for this Inode from the LRU queue
734      * and the IHandle queue and put them on a temporary queue so we
735      * can drop the lock before we close the files.
736      */
737     for (fdP = ihP->ih_fdhead; fdP != NULL; fdP = next) {
738         next = fdP->fd_ihnext;
739         assert(fdP->fd_ih == ihP);
740         assert(fdP->fd_status == FD_HANDLE_OPEN
741                || fdP->fd_status == FD_HANDLE_INUSE);
742         if (fdP->fd_status == FD_HANDLE_OPEN) {
743             DLL_DELETE(fdP, ihP->ih_fdhead, ihP->ih_fdtail, fd_ihnext,
744                        fd_ihprev);
745             DLL_DELETE(fdP, fdLruHead, fdLruTail, fd_next, fd_prev);
746             DLL_INSERT_TAIL(fdP, head, tail, fd_next, fd_prev);
747         } else {
748             closedAll = 0;
749             ihP->ih_flags |= IH_REALLY_CLOSED;
750         }
751     }
752
753     /* If the ihandle reference count is 1, we should have
754      * closed all file descriptors.
755      */
756     if (ihP->ih_refcnt == 1 || closedAll) {
757         assert(closedAll);
758         assert(!ihP->ih_fdhead);
759         assert(!ihP->ih_fdtail);
760     }
761
762     if (head == NULL) {
763         return 0;               /* No file descriptors closed */
764     }
765
766     IH_UNLOCK;
767     /*
768      * Close the file descriptors
769      */
770     closeCount = 0;
771     for (fdP = head; fdP != NULL; fdP = fdP->fd_next) {
772         OS_CLOSE(fdP->fd_fd);
773         fdP->fd_status = FD_HANDLE_AVAIL;
774         fdP->fd_fd = INVALID_FD;
775         fdP->fd_ih = NULL;
776         closeCount++;
777     }
778
779     IH_LOCK;
780     assert(fdInUseCount >= closeCount);
781     fdInUseCount -= closeCount;
782
783     /*
784      * Append the temporary queue to the list of available descriptors
785      */
786     if (fdAvailHead == NULL) {
787         fdAvailHead = head;
788         fdAvailTail = tail;
789     } else {
790         fdAvailTail->fd_next = head;
791         head->fd_prev = fdAvailTail;
792         fdAvailTail = tail;
793     }
794
795     return 0;
796 }
797
798 /* Close all cached file descriptors for this inode. */
799 int
800 ih_reallyclose(IHandle_t * ihP)
801 {
802     if (!ihP)
803         return 0;
804
805     IH_LOCK;
806     assert(ihP->ih_refcnt > 0);
807     ih_fdclose(ihP);
808
809     IH_UNLOCK;
810     return 0;
811 }
812
813 /* Release an Inode handle. All cached file descriptors for this
814  * inode are closed when the last reference to this handle is released
815  */
816 int
817 ih_release(IHandle_t * ihP)
818 {
819     int ihash;
820
821     if (!ihP)
822         return 0;
823
824     IH_LOCK;
825     assert(ihP->ih_refcnt > 0);
826
827     if (ihP->ih_refcnt > 1) {
828         ihP->ih_refcnt--;
829         IH_UNLOCK;
830         return 0;
831     }
832
833     ihash = IH_HASH(ihP->ih_dev, ihP->ih_vid, ihP->ih_ino);
834     DLL_DELETE(ihP, ihashTable[ihash].ihash_head,
835                ihashTable[ihash].ihash_tail, ih_next, ih_prev);
836
837     ih_fdclose(ihP);
838
839     ihP->ih_refcnt--;
840
841     DLL_INSERT_TAIL(ihP, ihAvailHead, ihAvailTail, ih_next, ih_prev);
842
843     IH_UNLOCK;
844     return 0;
845 }
846
847 /* Sync an inode to disk if its handle isn't NULL */
848 int
849 ih_condsync(IHandle_t * ihP)
850 {
851     int code;
852     FdHandle_t *fdP;
853
854     if (!ihP)
855         return 0;
856
857     fdP = IH_OPEN(ihP);
858     if (fdP == NULL)
859         return -1;
860
861     code = FDH_SYNC(fdP);
862     FDH_CLOSE(fdP);
863
864     return code;
865 }
866
867 void
868 ih_sync_all(void) {
869
870     int ihash;
871
872     IH_LOCK;
873     for (ihash = 0; ihash < I_HANDLE_HASH_SIZE; ihash++) {
874         IHandle_t *ihP, *ihPnext;
875
876         ihP = ihashTable[ihash].ihash_head;
877         if (ihP)
878             ihP->ih_refcnt++;   /* must not disappear over unlock */
879         for (; ihP; ihP = ihPnext) {
880             
881             if (ihP->ih_synced) {
882                 FdHandle_t *fdP;
883
884                 ihP->ih_synced = 0;
885                 IH_UNLOCK;
886
887                 fdP = IH_OPEN(ihP);
888                 if (fdP) OS_SYNC(fdP->fd_fd);
889                 FDH_CLOSE(fdP);
890
891                 IH_LOCK;
892             }
893
894             /* when decrementing the refcount, the ihandle might disappear
895                and we might not even be able to proceed to the next one.
896                Hence the gymnastics putting a hold on the next one already */
897             ihPnext = ihP->ih_next;
898             if (ihPnext) ihPnext->ih_refcnt++;
899
900             if (ihP->ih_refcnt > 1) {
901                 ihP->ih_refcnt--;
902             } else {
903                 IH_UNLOCK;
904                 ih_release(ihP);
905                 IH_LOCK;
906             }
907
908         }
909     }
910     IH_UNLOCK;
911 }
912
913 void *
914 ih_sync_thread(void *dummy) {
915     while(1) {
916
917 #ifdef AFS_PTHREAD_ENV
918         sleep(10);
919 #else /* AFS_PTHREAD_ENV */
920         IOMGR_Sleep(60);
921 #endif /* AFS_PTHREAD_ENV */
922
923 #ifndef AFS_NT40_ENV
924         sync();
925 #endif
926         ih_sync_all();
927     }
928     return NULL;
929 }
930
931
932 /*************************************************************************
933  * OS specific support routines.
934  *************************************************************************/
935 #ifndef AFS_NAMEI_ENV
936 Inode
937 ih_icreate(IHandle_t * ih, int dev, char *part, Inode nI, int p1, int p2,
938            int p3, int p4)
939 {
940     Inode ino;
941 #ifdef  AFS_3DISPARES
942     /* See viceinode.h */
943     if (p2 == INODESPECIAL) {
944         int tp = p3;
945         p3 = p4;
946         p4 = tp;
947     }
948 #endif
949     ino = ICREATE(dev, part, nI, p1, p2, p3, p4);
950     return ino;
951 }
952 #endif /* AFS_NAMEI_ENV */
953
954
955 #ifndef AFS_NT40_ENV
956 afs_sfsize_t
957 ih_size(int fd)
958 {
959     struct afs_stat status;
960     if (afs_fstat(fd, &status) < 0)
961         return -1;
962     return status.st_size;
963 }
964 #endif