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