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