openafs-string-header-cleanup-20071030
[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
97 #ifdef AFS_PTHREAD_ENV
98 /* Initialize the global ihandle mutex */
99 void
100 ih_glock_init()
101 {
102     assert(pthread_mutex_init(&ih_glock_mutex, NULL) == 0);
103 }
104 #endif /* AFS_PTHREAD_ENV */
105
106 /* Initialize the file descriptor cache */
107 void
108 ih_Initialize(void)
109 {
110     int i;
111     assert(!ih_Inited);
112     ih_Inited = 1;
113     DLL_INIT_LIST(ihAvailHead, ihAvailTail);
114     DLL_INIT_LIST(fdAvailHead, fdAvailTail);
115     DLL_INIT_LIST(fdLruHead, fdLruTail);
116     for (i = 0; i < I_HANDLE_HASH_SIZE; i++) {
117         DLL_INIT_LIST(ihashTable[i].ihash_head, ihashTable[i].ihash_tail);
118     }
119 #if defined(AFS_NT40_ENV)
120     fdMaxCacheSize = FD_MAX_CACHESIZE;
121 #elif defined(AFS_SUN5_ENV) || defined(AFS_NBSD_ENV)
122     {
123         struct rlimit rlim;
124         assert(getrlimit(RLIMIT_NOFILE, &rlim) == 0);
125         rlim.rlim_cur = rlim.rlim_max;
126         assert(setrlimit(RLIMIT_NOFILE, &rlim) == 0);
127         fdMaxCacheSize = rlim.rlim_cur - FD_HANDLE_SETASIDE;
128 #ifdef AFS_NBSD_ENV
129         /* XXX this is to avoid using up all system fd netbsd is
130          * somewhat broken and have set maximum fd for a root process
131          * to the same as system fd that is avaible, so if the
132          * fileserver uses all up process fds, all system fd will be
133          * used up too !
134          *
135          * Check for this better
136          */
137         fdMaxCacheSize /= 4;
138 #endif
139         fdMaxCacheSize = MIN(fdMaxCacheSize, FD_MAX_CACHESIZE);
140         assert(fdMaxCacheSize > 0);
141     }
142 #elif defined(AFS_HPUX_ENV)
143     /* Avoid problems with "UFSOpen: igetinode failed" panics on HPUX 11.0 */
144     fdMaxCacheSize = 0;
145 #else
146     {
147         long fdMax = MAX(sysconf(_SC_OPEN_MAX) - FD_HANDLE_SETASIDE, 0);
148         fdMaxCacheSize = (int)MIN(fdMax, FD_MAX_CACHESIZE);
149     }
150 #endif
151     fdCacheSize = MIN(fdMaxCacheSize, FD_DEFAULT_CACHESIZE);
152
153     {
154         void *ih_sync_thread();
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     fd = OS_IOPEN(ihP);
324     IH_LOCK;
325     if (fd == INVALID_FD) {
326         fdInUseCount -= 1;
327         IH_UNLOCK;
328         return NULL;
329     }
330
331     /* fdCacheSize limits the size of the descriptor cache, but
332      * we permit the number of open files to exceed fdCacheSize.
333      * We only recycle open file descriptors when the number
334      * of open files reaches the size of the cache */
335     if (fdInUseCount > fdCacheSize && fdLruHead != NULL) {
336         fdP = fdLruHead;
337         assert(fdP->fd_status == FD_HANDLE_OPEN);
338         DLL_DELETE(fdP, fdLruHead, fdLruTail, fd_next, fd_prev);
339         DLL_DELETE(fdP, fdP->fd_ih->ih_fdhead, fdP->fd_ih->ih_fdtail,
340                    fd_ihnext, fd_ihprev);
341         closeFd = fdP->fd_fd;
342     } else {
343         if (fdAvailHead == NULL) {
344             fdHandleAllocateChunk();
345         }
346         fdP = fdAvailHead;
347         assert(fdP->fd_status == FD_HANDLE_AVAIL);
348         DLL_DELETE(fdP, fdAvailHead, fdAvailTail, fd_next, fd_prev);
349         closeFd = INVALID_FD;
350     }
351
352     fdP->fd_status = FD_HANDLE_INUSE;
353     fdP->fd_fd = fd;
354     fdP->fd_ih = ihP;
355
356     ihP->ih_refcnt++;
357
358     /* Add this handle to the Inode's list of open descriptors */
359     DLL_INSERT_TAIL(fdP, ihP->ih_fdhead, ihP->ih_fdtail, fd_ihnext,
360                     fd_ihprev);
361
362     if (closeFd != INVALID_FD) {
363         IH_UNLOCK;
364         OS_CLOSE(closeFd);
365         IH_LOCK;
366         fdInUseCount -= 1;
367     }
368
369     IH_UNLOCK;
370     return fdP;
371 }
372
373 /*
374  * Return a file descriptor handle to the cache
375  */
376 int
377 fd_close(FdHandle_t * fdP)
378 {
379     IHandle_t *ihP;
380
381     if (!fdP)
382         return 0;
383
384     IH_LOCK;
385     assert(ih_Inited);
386     assert(fdInUseCount > 0);
387     assert(fdP->fd_status == FD_HANDLE_INUSE);
388
389     ihP = fdP->fd_ih;
390
391     /* Call fd_reallyclose to really close the unused file handles if
392      * the previous attempt to close (ih_reallyclose()) all file handles
393      * failed (this is determined by checking the ihandle for the flag
394      * IH_REALLY_CLOSED) or we have too many open files.
395      */
396     if (ihP->ih_flags & IH_REALLY_CLOSED || fdInUseCount > fdCacheSize) {
397         IH_UNLOCK;
398         return fd_reallyclose(fdP);
399     }
400
401     /* Put this descriptor back into the cache */
402     fdP->fd_status = FD_HANDLE_OPEN;
403     DLL_INSERT_TAIL(fdP, fdLruHead, fdLruTail, fd_next, fd_prev);
404
405     /* If this is not the only reference to the Inode then we can decrement
406      * the reference count, otherwise we need to call ih_release.
407      */
408     if (ihP->ih_refcnt > 1) {
409         ihP->ih_refcnt--;
410         IH_UNLOCK;
411     } else {
412         IH_UNLOCK;
413         ih_release(ihP);
414     }
415
416     return 0;
417 }
418
419 /*
420  * Actually close the file descriptor handle and return it to
421  * the free list.
422  */
423 int
424 fd_reallyclose(FdHandle_t * fdP)
425 {
426     FD_t closeFd;
427     IHandle_t *ihP;
428
429     if (!fdP)
430         return 0;
431
432     IH_LOCK;
433     assert(ih_Inited);
434     assert(fdInUseCount > 0);
435     assert(fdP->fd_status == FD_HANDLE_INUSE);
436
437     ihP = fdP->fd_ih;
438     closeFd = fdP->fd_fd;
439
440     DLL_DELETE(fdP, ihP->ih_fdhead, ihP->ih_fdtail, fd_ihnext, fd_ihprev);
441     DLL_INSERT_TAIL(fdP, fdAvailHead, fdAvailTail, fd_next, fd_prev);
442
443     fdP->fd_status = FD_HANDLE_AVAIL;
444     fdP->fd_ih = NULL;
445     fdP->fd_fd = INVALID_FD;
446
447     /* All the file descriptor handles have been closed; reset
448      * the IH_REALLY_CLOSED flag indicating that ih_reallyclose
449      * has completed its job.
450      */
451     if (!ihP->ih_fdhead) {
452         ihP->ih_flags &= ~IH_REALLY_CLOSED;
453     }
454
455     IH_UNLOCK;
456     OS_CLOSE(closeFd);
457     IH_LOCK;
458     fdInUseCount -= 1;
459
460     /* If this is not the only reference to the Inode then we can decrement
461      * the reference count, otherwise we need to call ih_release. */
462     if (ihP->ih_refcnt > 1) {
463         ihP->ih_refcnt--;
464         IH_UNLOCK;
465     } else {
466         IH_UNLOCK;
467         ih_release(ihP);
468     }
469
470     return 0;
471 }
472
473 /* Enable buffered I/O on a file descriptor */
474 StreamHandle_t *
475 stream_fdopen(FD_t fd)
476 {
477     StreamHandle_t *streamP;
478
479     IH_LOCK;
480     if (streamAvailHead == NULL) {
481         streamHandleAllocateChunk();
482     }
483     streamP = streamAvailHead;
484     DLL_DELETE(streamP, streamAvailHead, streamAvailTail, str_next, str_prev);
485     IH_UNLOCK;
486     streamP->str_fd = fd;
487     streamP->str_buflen = 0;
488     streamP->str_bufoff = 0;
489     streamP->str_error = 0;
490     streamP->str_eof = 0;
491     streamP->str_direction = STREAM_DIRECTION_NONE;
492     return streamP;
493 }
494
495 /* Open a file for buffered I/O */
496 StreamHandle_t *
497 stream_open(const char *filename, const char *mode)
498 {
499     FD_t fd;
500
501     if (strcmp(mode, "r") == 0) {
502         fd = OS_OPEN(filename, O_RDONLY, 0);
503     } else if (strcmp(mode, "r+") == 0) {
504         fd = OS_OPEN(filename, O_RDWR, 0);
505     } else if (strcmp(mode, "w") == 0) {
506         fd = OS_OPEN(filename, O_WRONLY | O_TRUNC | O_CREAT, 0);
507     } else if (strcmp(mode, "w+") == 0) {
508         fd = OS_OPEN(filename, O_RDWR | O_TRUNC | O_CREAT, 0);
509     } else if (strcmp(mode, "a") == 0) {
510         fd = OS_OPEN(filename, O_WRONLY | O_APPEND | O_CREAT, 0);
511     } else if (strcmp(mode, "a+") == 0) {
512         fd = OS_OPEN(filename, O_RDWR | O_APPEND | O_CREAT, 0);
513     } else {
514         assert(FALSE);          /* not implemented */
515     }
516
517     if (fd == INVALID_FD) {
518         return NULL;
519     }
520     return stream_fdopen(fd);
521 }
522
523 /* fread for buffered I/O handles */
524 afs_sfsize_t
525 stream_read(void *ptr, afs_fsize_t size, afs_fsize_t nitems,
526             StreamHandle_t * streamP)
527 {
528     afs_fsize_t nbytes, bytesRead, bytesToRead;
529     char *p;
530
531     /* Need to seek before changing direction */
532     if (streamP->str_direction == STREAM_DIRECTION_NONE) {
533         streamP->str_direction = STREAM_DIRECTION_READ;
534         streamP->str_bufoff = 0;
535         streamP->str_buflen = 0;
536     } else {
537         assert(streamP->str_direction == STREAM_DIRECTION_READ);
538     }
539
540     bytesRead = 0;
541     nbytes = size * nitems;
542     p = (char *)ptr;
543     while (nbytes > 0 && !streamP->str_eof) {
544         if (streamP->str_buflen == 0) {
545             streamP->str_bufoff = 0;
546             streamP->str_buflen =
547                 OS_READ(streamP->str_fd, streamP->str_buffer,
548                         STREAM_HANDLE_BUFSIZE);
549             if (streamP->str_buflen < 0) {
550                 streamP->str_error = errno;
551                 streamP->str_buflen = 0;
552                 bytesRead = 0;
553                 break;
554             } else if (streamP->str_buflen == 0) {
555                 streamP->str_eof = 1;
556                 break;
557             }
558         }
559
560         bytesToRead = nbytes;
561         if (bytesToRead > streamP->str_buflen) {
562             bytesToRead = streamP->str_buflen;
563         }
564         memcpy(p, streamP->str_buffer + streamP->str_bufoff, bytesToRead);
565         p += bytesToRead;
566         streamP->str_bufoff += bytesToRead;
567         streamP->str_buflen -= bytesToRead;
568         bytesRead += bytesToRead;
569         nbytes -= bytesToRead;
570     }
571
572     return (bytesRead / size);
573 }
574
575 /* fwrite for buffered I/O handles */
576 afs_sfsize_t
577 stream_write(void *ptr, afs_fsize_t size, afs_fsize_t nitems,
578              StreamHandle_t * streamP)
579 {
580     char *p;
581     afs_sfsize_t rc;
582     afs_fsize_t nbytes, bytesWritten, bytesToWrite;
583
584     /* Need to seek before changing direction */
585     if (streamP->str_direction == STREAM_DIRECTION_NONE) {
586         streamP->str_direction = STREAM_DIRECTION_WRITE;
587         streamP->str_bufoff = 0;
588         streamP->str_buflen = STREAM_HANDLE_BUFSIZE;
589     } else {
590         assert(streamP->str_direction == STREAM_DIRECTION_WRITE);
591     }
592
593     nbytes = size * nitems;
594     bytesWritten = 0;
595     p = (char *)ptr;
596     while (nbytes > 0) {
597         if (streamP->str_buflen == 0) {
598             rc = OS_WRITE(streamP->str_fd, streamP->str_buffer,
599                           STREAM_HANDLE_BUFSIZE);
600             if (rc < 0) {
601                 streamP->str_error = errno;
602                 bytesWritten = 0;
603                 break;
604             }
605             streamP->str_bufoff = 0;
606             streamP->str_buflen = STREAM_HANDLE_BUFSIZE;
607         }
608
609         bytesToWrite = nbytes;
610         if (bytesToWrite > streamP->str_buflen) {
611             bytesToWrite = streamP->str_buflen;
612         }
613         memcpy(streamP->str_buffer + streamP->str_bufoff, p, bytesToWrite);
614         p += bytesToWrite;
615         streamP->str_bufoff += bytesToWrite;
616         streamP->str_buflen -= bytesToWrite;
617         bytesWritten += bytesToWrite;
618         nbytes -= bytesToWrite;
619     }
620
621     return (bytesWritten / size);
622 }
623
624 /* fseek for buffered I/O handles */
625 int
626 stream_seek(StreamHandle_t * streamP, afs_foff_t offset, int whence)
627 {
628     int rc;
629     int retval = 0;
630
631     if (streamP->str_direction == STREAM_DIRECTION_WRITE
632         && streamP->str_bufoff > 0) {
633         rc = OS_WRITE(streamP->str_fd, streamP->str_buffer,
634                       streamP->str_bufoff);
635         if (rc < 0) {
636             streamP->str_error = errno;
637             retval = -1;
638         }
639     }
640     streamP->str_bufoff = 0;
641     streamP->str_buflen = 0;
642     streamP->str_eof = 0;
643     streamP->str_direction = STREAM_DIRECTION_NONE;
644     if (OS_SEEK(streamP->str_fd, offset, whence) < 0) {
645         streamP->str_error = errno;
646         retval = -1;
647     }
648     return retval;
649 }
650
651 /* fflush for buffered I/O handles */
652 int
653 stream_flush(StreamHandle_t * streamP)
654 {
655     int rc;
656     int retval = 0;
657
658     if (streamP->str_direction == STREAM_DIRECTION_WRITE
659         && streamP->str_bufoff > 0) {
660         rc = OS_WRITE(streamP->str_fd, streamP->str_buffer,
661                       streamP->str_bufoff);
662         if (rc < 0) {
663             streamP->str_error = errno;
664             retval = -1;
665         }
666         streamP->str_bufoff = 0;
667         streamP->str_buflen = STREAM_HANDLE_BUFSIZE;
668     }
669
670     return retval;
671 }
672
673 /* Free a buffered I/O handle */
674 int
675 stream_close(StreamHandle_t * streamP, int reallyClose)
676 {
677     int rc;
678     int retval = 0;
679
680     assert(streamP != NULL);
681     if (streamP->str_direction == STREAM_DIRECTION_WRITE
682         && streamP->str_bufoff > 0) {
683         rc = OS_WRITE(streamP->str_fd, streamP->str_buffer,
684                       streamP->str_bufoff);
685         if (rc < 0) {
686             retval = -1;
687         }
688     }
689     if (reallyClose) {
690         rc = OS_CLOSE(streamP->str_fd);
691         if (rc < 0) {
692             retval = -1;
693         }
694     }
695     streamP->str_fd = INVALID_FD;
696
697     IH_LOCK;
698     DLL_INSERT_TAIL(streamP, streamAvailHead, streamAvailTail,
699                     str_next, str_prev);
700     IH_UNLOCK;
701     return retval;
702 }
703
704 /* Close all unused file descriptors associated with the inode
705  * handle. Called with IH_LOCK held. May drop and reacquire
706  * IH_LOCK. Sets the IH_REALLY_CLOSED flag in the inode handle
707  * if it fails to close all file handles.
708  */
709 static int
710 ih_fdclose(IHandle_t * ihP)
711 {
712     int closeCount, closedAll;
713     FdHandle_t *fdP, *head, *tail, *next;
714
715     assert(ihP->ih_refcnt > 0);
716
717     closedAll = 1;
718     DLL_INIT_LIST(head, tail);
719     ihP->ih_flags &= ~IH_REALLY_CLOSED;
720
721     /*
722      * Remove the file descriptors for this Inode from the LRU queue
723      * and the IHandle queue and put them on a temporary queue so we
724      * can drop the lock before we close the files.
725      */
726     for (fdP = ihP->ih_fdhead; fdP != NULL; fdP = next) {
727         next = fdP->fd_ihnext;
728         assert(fdP->fd_ih == ihP);
729         assert(fdP->fd_status == FD_HANDLE_OPEN
730                || fdP->fd_status == FD_HANDLE_INUSE);
731         if (fdP->fd_status == FD_HANDLE_OPEN) {
732             DLL_DELETE(fdP, ihP->ih_fdhead, ihP->ih_fdtail, fd_ihnext,
733                        fd_ihprev);
734             DLL_DELETE(fdP, fdLruHead, fdLruTail, fd_next, fd_prev);
735             DLL_INSERT_TAIL(fdP, head, tail, fd_next, fd_prev);
736         } else {
737             closedAll = 0;
738             ihP->ih_flags |= IH_REALLY_CLOSED;
739         }
740     }
741
742     /* If the ihandle reference count is 1, we should have
743      * closed all file descriptors.
744      */
745     if (ihP->ih_refcnt == 1 || closedAll) {
746         assert(closedAll);
747         assert(!ihP->ih_fdhead);
748         assert(!ihP->ih_fdtail);
749     }
750
751     if (head == NULL) {
752         return 0;               /* No file descriptors closed */
753     }
754
755     IH_UNLOCK;
756     /*
757      * Close the file descriptors
758      */
759     closeCount = 0;
760     for (fdP = head; fdP != NULL; fdP = fdP->fd_next) {
761         OS_CLOSE(fdP->fd_fd);
762         fdP->fd_status = FD_HANDLE_AVAIL;
763         fdP->fd_fd = INVALID_FD;
764         fdP->fd_ih = NULL;
765         closeCount++;
766     }
767
768     IH_LOCK;
769     assert(fdInUseCount >= closeCount);
770     fdInUseCount -= closeCount;
771
772     /*
773      * Append the temporary queue to the list of available descriptors
774      */
775     if (fdAvailHead == NULL) {
776         fdAvailHead = head;
777         fdAvailTail = tail;
778     } else {
779         fdAvailTail->fd_next = head;
780         head->fd_prev = fdAvailTail;
781         fdAvailTail = tail;
782     }
783
784     return 0;
785 }
786
787 /* Close all cached file descriptors for this inode. */
788 int
789 ih_reallyclose(IHandle_t * ihP)
790 {
791     if (!ihP)
792         return 0;
793
794     IH_LOCK;
795     assert(ihP->ih_refcnt > 0);
796     ih_fdclose(ihP);
797
798     IH_UNLOCK;
799     return 0;
800 }
801
802 /* Release an Inode handle. All cached file descriptors for this
803  * inode are closed when the last reference to this handle is released
804  */
805 int
806 ih_release(IHandle_t * ihP)
807 {
808     int ihash;
809
810     if (!ihP)
811         return 0;
812
813     IH_LOCK;
814     assert(ihP->ih_refcnt > 0);
815
816     if (ihP->ih_refcnt > 1) {
817         ihP->ih_refcnt--;
818         IH_UNLOCK;
819         return 0;
820     }
821
822     ihash = IH_HASH(ihP->ih_dev, ihP->ih_vid, ihP->ih_ino);
823     DLL_DELETE(ihP, ihashTable[ihash].ihash_head,
824                ihashTable[ihash].ihash_tail, ih_next, ih_prev);
825
826     ih_fdclose(ihP);
827
828     ihP->ih_refcnt--;
829
830     DLL_INSERT_TAIL(ihP, ihAvailHead, ihAvailTail, ih_next, ih_prev);
831
832     IH_UNLOCK;
833     return 0;
834 }
835
836 /* Sync an inode to disk if its handle isn't NULL */
837 int
838 ih_condsync(IHandle_t * ihP)
839 {
840     int code;
841     FdHandle_t *fdP;
842
843     if (!ihP)
844         return 0;
845
846     fdP = IH_OPEN(ihP);
847     if (fdP == NULL)
848         return -1;
849
850     code = FDH_SYNC(fdP);
851     FDH_CLOSE(fdP);
852
853     return code;
854 }
855
856 void
857 ih_sync_all() {
858     int ihash;
859
860     IH_LOCK;
861     for (ihash = 0; ihash < I_HANDLE_HASH_SIZE; ihash++) {
862         IHandle_t *ihP, *ihPnext;
863
864         ihP = ihashTable[ihash].ihash_head;
865         if (ihP)
866             ihP->ih_refcnt++;   /* must not disappear over unlock */
867         for (; ihP; ihP = ihPnext) {
868             
869             if (ihP->ih_synced) {
870                 FdHandle_t *fdP;
871
872                 ihP->ih_synced = 0;
873                 IH_UNLOCK;
874
875                 fdP = IH_OPEN(ihP);
876                 if (fdP) OS_SYNC(fdP->fd_fd);
877                 FDH_CLOSE(fdP);
878
879                 IH_LOCK;
880             }
881
882             /* when decrementing the refcount, the ihandle might disappear
883                and we might not even be able to proceed to the next one.
884                Hence the gymnastics putting a hold on the next one already */
885             ihPnext = ihP->ih_next;
886             if (ihPnext) ihPnext->ih_refcnt++;
887
888             if (ihP->ih_refcnt > 1) {
889                 ihP->ih_refcnt--;
890             } else {
891                 IH_UNLOCK;
892                 ih_release(ihP);
893                 IH_LOCK;
894             }
895
896         }
897     }
898     IH_UNLOCK;
899 }
900
901 void *
902 ih_sync_thread() {
903     while(1) {
904
905 #ifdef AFS_PTHREAD_ENV
906         sleep(10);
907 #else /* AFS_PTHREAD_ENV */
908         IOMGR_Sleep(60);
909 #endif /* AFS_PTHREAD_ENV */
910
911 #ifndef AFS_NT40_ENV
912         sync();
913 #endif
914         ih_sync_all();
915     }
916 }
917
918
919 /*************************************************************************
920  * OS specific support routines.
921  *************************************************************************/
922 #ifndef AFS_NAMEI_ENV
923 Inode
924 ih_icreate(IHandle_t * ih, int dev, char *part, Inode nI, int p1, int p2,
925            int p3, int p4)
926 {
927     Inode ino;
928 #ifdef  AFS_3DISPARES
929     /* See viceinode.h */
930     if (p2 == INODESPECIAL) {
931         int tp = p3;
932         p3 = p4;
933         p4 = tp;
934     }
935 #endif
936     ino = ICREATE(dev, part, nI, p1, p2, p3, p4);
937     return ino;
938 }
939 #endif /* AFS_NAMEI_ENV */
940
941
942 #ifndef AFS_NT40_ENV
943 afs_sfsize_t
944 ih_size(int fd)
945 {
946     struct afs_stat status;
947     if (afs_fstat(fd, &status) < 0)
948         return -1;
949     return status.st_size;
950 }
951 #endif