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