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