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