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