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