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