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