namei-fsync-less-20070907
[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         void *ih_sync_thread();
160 #ifdef AFS_PTHREAD_ENV
161         pthread_t syncer;
162         pthread_attr_t tattr;
163
164         pthread_attr_init(&tattr);
165         pthread_attr_setdetachstate(&tattr,PTHREAD_CREATE_DETACHED);
166
167         pthread_create(&syncer, &tattr, ih_sync_thread, NULL);
168 #else /* AFS_PTHREAD_ENV */
169         PROCESS syncer;
170         LWP_CreateProcess(ih_sync_thread, 16*1024, LWP_MAX_PRIORITY - 2,
171             NULL, "ih_syncer", &syncer);
172 #endif /* AFS_PTHREAD_ENV */
173     }
174
175 }
176
177 /* Make the file descriptor cache as big as possible. Don't this call
178  * if the program uses fopen or fdopen. */
179 void
180 ih_UseLargeCache(void)
181 {
182     IH_LOCK;
183     if (!ih_Inited) {
184         ih_Initialize();
185     }
186     fdCacheSize = fdMaxCacheSize;
187
188     IH_UNLOCK;
189 }
190
191 /* Allocate a chunk of inode handles */
192 void
193 iHandleAllocateChunk(void)
194 {
195     int i;
196     IHandle_t *ihP;
197
198     assert(ihAvailHead == NULL);
199     ihP = (IHandle_t *) malloc(I_HANDLE_MALLOCSIZE * sizeof(IHandle_t));
200     assert(ihP != NULL);
201     for (i = 0; i < I_HANDLE_MALLOCSIZE; i++) {
202         ihP[i].ih_refcnt = 0;
203         DLL_INSERT_TAIL(&ihP[i], ihAvailHead, ihAvailTail, ih_next, ih_prev);
204     }
205 }
206
207 /* Initialize an inode handle */
208 IHandle_t *
209 ih_init(int dev, int vid, Inode ino)
210 {
211     int ihash = IH_HASH(dev, vid, ino);
212     IHandle_t *ihP;
213
214     IH_LOCK;
215     if (!ih_Inited) {
216         ih_Initialize();
217     }
218
219     /* Do we already have a handle for this Inode? */
220     for (ihP = ihashTable[ihash].ihash_head; ihP; ihP = ihP->ih_next) {
221         if (ihP->ih_ino == ino && ihP->ih_vid == vid && ihP->ih_dev == dev) {
222             ihP->ih_refcnt++;
223             IH_UNLOCK;
224             return ihP;
225         }
226     }
227
228     /* Allocate and initialize a new Inode handle */
229     if (ihAvailHead == NULL) {
230         iHandleAllocateChunk();
231     }
232     ihP = ihAvailHead;
233     assert(ihP->ih_refcnt == 0);
234     DLL_DELETE(ihP, ihAvailHead, ihAvailTail, ih_next, ih_prev);
235     ihP->ih_dev = dev;
236     ihP->ih_vid = vid;
237     ihP->ih_ino = ino;
238     ihP->ih_flags = 0;
239     ihP->ih_refcnt = 1;
240     DLL_INIT_LIST(ihP->ih_fdhead, ihP->ih_fdtail);
241     DLL_INSERT_TAIL(ihP, ihashTable[ihash].ihash_head,
242                     ihashTable[ihash].ihash_tail, ih_next, ih_prev);
243     IH_UNLOCK;
244     return ihP;
245 }
246
247 /* Copy an inode handle */
248 IHandle_t *
249 ih_copy(IHandle_t * ihP)
250 {
251     IH_LOCK;
252     assert(ih_Inited);
253     assert(ihP->ih_refcnt > 0);
254     ihP->ih_refcnt++;
255     IH_UNLOCK;
256     return ihP;
257 }
258
259 /* Allocate a chunk of file descriptor handles */
260 void
261 fdHandleAllocateChunk(void)
262 {
263     int i;
264     FdHandle_t *fdP;
265
266     assert(fdAvailHead == NULL);
267     fdP = (FdHandle_t *) malloc(FD_HANDLE_MALLOCSIZE * sizeof(FdHandle_t));
268     assert(fdP != NULL);
269     for (i = 0; i < FD_HANDLE_MALLOCSIZE; i++) {
270         fdP[i].fd_status = FD_HANDLE_AVAIL;
271         fdP[i].fd_ih = NULL;
272         fdP[i].fd_fd = INVALID_FD;
273         DLL_INSERT_TAIL(&fdP[i], fdAvailHead, fdAvailTail, fd_next, fd_prev);
274     }
275 }
276
277 /* Allocate a chunk of stream handles */
278 void
279 streamHandleAllocateChunk(void)
280 {
281     int i;
282     StreamHandle_t *streamP;
283
284     assert(streamAvailHead == NULL);
285     streamP = (StreamHandle_t *)
286         malloc(STREAM_HANDLE_MALLOCSIZE * sizeof(StreamHandle_t));
287     assert(streamP != NULL);
288     for (i = 0; i < STREAM_HANDLE_MALLOCSIZE; i++) {
289         streamP[i].str_fd = INVALID_FD;
290         DLL_INSERT_TAIL(&streamP[i], streamAvailHead, streamAvailTail,
291                         str_next, str_prev);
292     }
293 }
294
295 /*
296  * Get a file descriptor handle given an Inode handle
297  */
298 FdHandle_t *
299 ih_open(IHandle_t * ihP)
300 {
301     FdHandle_t *fdP;
302     FD_t fd;
303     FD_t closeFd;
304
305     if (!ihP)                   /* XXX should log here in the fileserver */
306         return NULL;
307
308     IH_LOCK;
309
310     /* Do we already have an open file handle for this Inode? */
311     for (fdP = ihP->ih_fdtail; fdP != NULL; fdP = fdP->fd_ihprev) {
312         if (fdP->fd_status != FD_HANDLE_INUSE) {
313             assert(fdP->fd_status == FD_HANDLE_OPEN);
314             fdP->fd_status = FD_HANDLE_INUSE;
315             DLL_DELETE(fdP, fdLruHead, fdLruTail, fd_next, fd_prev);
316             ihP->ih_refcnt++;
317             IH_UNLOCK;
318             (void)FDH_SEEK(fdP, 0, SEEK_SET);
319             return fdP;
320         }
321     }
322
323     /*
324      * Try to open the Inode, return NULL on error.
325      */
326     fdInUseCount += 1;
327     IH_UNLOCK;
328     fd = OS_IOPEN(ihP);
329     IH_LOCK;
330     if (fd == INVALID_FD) {
331         fdInUseCount -= 1;
332         IH_UNLOCK;
333         return NULL;
334     }
335
336     /* fdCacheSize limits the size of the descriptor cache, but
337      * we permit the number of open files to exceed fdCacheSize.
338      * We only recycle open file descriptors when the number
339      * of open files reaches the size of the cache */
340     if (fdInUseCount > fdCacheSize && fdLruHead != NULL) {
341         fdP = fdLruHead;
342         assert(fdP->fd_status == FD_HANDLE_OPEN);
343         DLL_DELETE(fdP, fdLruHead, fdLruTail, fd_next, fd_prev);
344         DLL_DELETE(fdP, fdP->fd_ih->ih_fdhead, fdP->fd_ih->ih_fdtail,
345                    fd_ihnext, fd_ihprev);
346         closeFd = fdP->fd_fd;
347     } else {
348         if (fdAvailHead == NULL) {
349             fdHandleAllocateChunk();
350         }
351         fdP = fdAvailHead;
352         assert(fdP->fd_status == FD_HANDLE_AVAIL);
353         DLL_DELETE(fdP, fdAvailHead, fdAvailTail, fd_next, fd_prev);
354         closeFd = INVALID_FD;
355     }
356
357     fdP->fd_status = FD_HANDLE_INUSE;
358     fdP->fd_fd = fd;
359     fdP->fd_ih = ihP;
360
361     ihP->ih_refcnt++;
362
363     /* Add this handle to the Inode's list of open descriptors */
364     DLL_INSERT_TAIL(fdP, ihP->ih_fdhead, ihP->ih_fdtail, fd_ihnext,
365                     fd_ihprev);
366
367     if (closeFd != INVALID_FD) {
368         IH_UNLOCK;
369         OS_CLOSE(closeFd);
370         IH_LOCK;
371         fdInUseCount -= 1;
372     }
373
374     IH_UNLOCK;
375     return fdP;
376 }
377
378 /*
379  * Return a file descriptor handle to the cache
380  */
381 int
382 fd_close(FdHandle_t * fdP)
383 {
384     IHandle_t *ihP;
385
386     if (!fdP)
387         return 0;
388
389     IH_LOCK;
390     assert(ih_Inited);
391     assert(fdInUseCount > 0);
392     assert(fdP->fd_status == FD_HANDLE_INUSE);
393
394     ihP = fdP->fd_ih;
395
396     /* Call fd_reallyclose to really close the unused file handles if
397      * the previous attempt to close (ih_reallyclose()) all file handles
398      * failed (this is determined by checking the ihandle for the flag
399      * IH_REALLY_CLOSED) or we have too many open files.
400      */
401     if (ihP->ih_flags & IH_REALLY_CLOSED || fdInUseCount > fdCacheSize) {
402         IH_UNLOCK;
403         return fd_reallyclose(fdP);
404     }
405
406     /* Put this descriptor back into the cache */
407     fdP->fd_status = FD_HANDLE_OPEN;
408     DLL_INSERT_TAIL(fdP, fdLruHead, fdLruTail, fd_next, fd_prev);
409
410     /* If this is not the only reference to the Inode then we can decrement
411      * the reference count, otherwise we need to call ih_release.
412      */
413     if (ihP->ih_refcnt > 1) {
414         ihP->ih_refcnt--;
415         IH_UNLOCK;
416     } else {
417         IH_UNLOCK;
418         ih_release(ihP);
419     }
420
421     return 0;
422 }
423
424 /*
425  * Actually close the file descriptor handle and return it to
426  * the free list.
427  */
428 int
429 fd_reallyclose(FdHandle_t * fdP)
430 {
431     FD_t closeFd;
432     IHandle_t *ihP;
433
434     if (!fdP)
435         return 0;
436
437     IH_LOCK;
438     assert(ih_Inited);
439     assert(fdInUseCount > 0);
440     assert(fdP->fd_status == FD_HANDLE_INUSE);
441
442     ihP = fdP->fd_ih;
443     closeFd = fdP->fd_fd;
444
445     DLL_DELETE(fdP, ihP->ih_fdhead, ihP->ih_fdtail, fd_ihnext, fd_ihprev);
446     DLL_INSERT_TAIL(fdP, fdAvailHead, fdAvailTail, fd_next, fd_prev);
447
448     fdP->fd_status = FD_HANDLE_AVAIL;
449     fdP->fd_ih = NULL;
450     fdP->fd_fd = INVALID_FD;
451
452     /* All the file descriptor handles have been closed; reset
453      * the IH_REALLY_CLOSED flag indicating that ih_reallyclose
454      * has completed its job.
455      */
456     if (!ihP->ih_fdhead) {
457         ihP->ih_flags &= ~IH_REALLY_CLOSED;
458     }
459
460     IH_UNLOCK;
461     OS_CLOSE(closeFd);
462     IH_LOCK;
463     fdInUseCount -= 1;
464
465     /* If this is not the only reference to the Inode then we can decrement
466      * the reference count, otherwise we need to call ih_release. */
467     if (ihP->ih_refcnt > 1) {
468         ihP->ih_refcnt--;
469         IH_UNLOCK;
470     } else {
471         IH_UNLOCK;
472         ih_release(ihP);
473     }
474
475     return 0;
476 }
477
478 /* Enable buffered I/O on a file descriptor */
479 StreamHandle_t *
480 stream_fdopen(FD_t fd)
481 {
482     StreamHandle_t *streamP;
483
484     IH_LOCK;
485     if (streamAvailHead == NULL) {
486         streamHandleAllocateChunk();
487     }
488     streamP = streamAvailHead;
489     DLL_DELETE(streamP, streamAvailHead, streamAvailTail, str_next, str_prev);
490     IH_UNLOCK;
491     streamP->str_fd = fd;
492     streamP->str_buflen = 0;
493     streamP->str_bufoff = 0;
494     streamP->str_error = 0;
495     streamP->str_eof = 0;
496     streamP->str_direction = STREAM_DIRECTION_NONE;
497     return streamP;
498 }
499
500 /* Open a file for buffered I/O */
501 StreamHandle_t *
502 stream_open(const char *filename, const char *mode)
503 {
504     FD_t fd;
505
506     if (strcmp(mode, "r") == 0) {
507         fd = OS_OPEN(filename, O_RDONLY, 0);
508     } else if (strcmp(mode, "r+") == 0) {
509         fd = OS_OPEN(filename, O_RDWR, 0);
510     } else if (strcmp(mode, "w") == 0) {
511         fd = OS_OPEN(filename, O_WRONLY | O_TRUNC | O_CREAT, 0);
512     } else if (strcmp(mode, "w+") == 0) {
513         fd = OS_OPEN(filename, O_RDWR | O_TRUNC | O_CREAT, 0);
514     } else if (strcmp(mode, "a") == 0) {
515         fd = OS_OPEN(filename, O_WRONLY | O_APPEND | O_CREAT, 0);
516     } else if (strcmp(mode, "a+") == 0) {
517         fd = OS_OPEN(filename, O_RDWR | O_APPEND | O_CREAT, 0);
518     } else {
519         assert(FALSE);          /* not implemented */
520     }
521
522     if (fd == INVALID_FD) {
523         return NULL;
524     }
525     return stream_fdopen(fd);
526 }
527
528 /* fread for buffered I/O handles */
529 afs_sfsize_t
530 stream_read(void *ptr, afs_fsize_t size, afs_fsize_t nitems,
531             StreamHandle_t * streamP)
532 {
533     afs_fsize_t nbytes, bytesRead, bytesToRead;
534     char *p;
535
536     /* Need to seek before changing direction */
537     if (streamP->str_direction == STREAM_DIRECTION_NONE) {
538         streamP->str_direction = STREAM_DIRECTION_READ;
539         streamP->str_bufoff = 0;
540         streamP->str_buflen = 0;
541     } else {
542         assert(streamP->str_direction == STREAM_DIRECTION_READ);
543     }
544
545     bytesRead = 0;
546     nbytes = size * nitems;
547     p = (char *)ptr;
548     while (nbytes > 0 && !streamP->str_eof) {
549         if (streamP->str_buflen == 0) {
550             streamP->str_bufoff = 0;
551             streamP->str_buflen =
552                 OS_READ(streamP->str_fd, streamP->str_buffer,
553                         STREAM_HANDLE_BUFSIZE);
554             if (streamP->str_buflen < 0) {
555                 streamP->str_error = errno;
556                 streamP->str_buflen = 0;
557                 bytesRead = 0;
558                 break;
559             } else if (streamP->str_buflen == 0) {
560                 streamP->str_eof = 1;
561                 break;
562             }
563         }
564
565         bytesToRead = nbytes;
566         if (bytesToRead > streamP->str_buflen) {
567             bytesToRead = streamP->str_buflen;
568         }
569         memcpy(p, streamP->str_buffer + streamP->str_bufoff, bytesToRead);
570         p += bytesToRead;
571         streamP->str_bufoff += bytesToRead;
572         streamP->str_buflen -= bytesToRead;
573         bytesRead += bytesToRead;
574         nbytes -= bytesToRead;
575     }
576
577     return (bytesRead / size);
578 }
579
580 /* fwrite for buffered I/O handles */
581 afs_sfsize_t
582 stream_write(void *ptr, afs_fsize_t size, afs_fsize_t nitems,
583              StreamHandle_t * streamP)
584 {
585     char *p;
586     afs_sfsize_t rc;
587     afs_fsize_t nbytes, bytesWritten, bytesToWrite;
588
589     /* Need to seek before changing direction */
590     if (streamP->str_direction == STREAM_DIRECTION_NONE) {
591         streamP->str_direction = STREAM_DIRECTION_WRITE;
592         streamP->str_bufoff = 0;
593         streamP->str_buflen = STREAM_HANDLE_BUFSIZE;
594     } else {
595         assert(streamP->str_direction == STREAM_DIRECTION_WRITE);
596     }
597
598     nbytes = size * nitems;
599     bytesWritten = 0;
600     p = (char *)ptr;
601     while (nbytes > 0) {
602         if (streamP->str_buflen == 0) {
603             rc = OS_WRITE(streamP->str_fd, streamP->str_buffer,
604                           STREAM_HANDLE_BUFSIZE);
605             if (rc < 0) {
606                 streamP->str_error = errno;
607                 bytesWritten = 0;
608                 break;
609             }
610             streamP->str_bufoff = 0;
611             streamP->str_buflen = STREAM_HANDLE_BUFSIZE;
612         }
613
614         bytesToWrite = nbytes;
615         if (bytesToWrite > streamP->str_buflen) {
616             bytesToWrite = streamP->str_buflen;
617         }
618         memcpy(streamP->str_buffer + streamP->str_bufoff, p, bytesToWrite);
619         p += bytesToWrite;
620         streamP->str_bufoff += bytesToWrite;
621         streamP->str_buflen -= bytesToWrite;
622         bytesWritten += bytesToWrite;
623         nbytes -= bytesToWrite;
624     }
625
626     return (bytesWritten / size);
627 }
628
629 /* fseek for buffered I/O handles */
630 int
631 stream_seek(StreamHandle_t * streamP, afs_foff_t offset, int whence)
632 {
633     int rc;
634     int retval = 0;
635
636     if (streamP->str_direction == STREAM_DIRECTION_WRITE
637         && streamP->str_bufoff > 0) {
638         rc = OS_WRITE(streamP->str_fd, streamP->str_buffer,
639                       streamP->str_bufoff);
640         if (rc < 0) {
641             streamP->str_error = errno;
642             retval = -1;
643         }
644     }
645     streamP->str_bufoff = 0;
646     streamP->str_buflen = 0;
647     streamP->str_eof = 0;
648     streamP->str_direction = STREAM_DIRECTION_NONE;
649     if (OS_SEEK(streamP->str_fd, offset, whence) < 0) {
650         streamP->str_error = errno;
651         retval = -1;
652     }
653     return retval;
654 }
655
656 /* fflush for buffered I/O handles */
657 int
658 stream_flush(StreamHandle_t * streamP)
659 {
660     int rc;
661     int retval = 0;
662
663     if (streamP->str_direction == STREAM_DIRECTION_WRITE
664         && streamP->str_bufoff > 0) {
665         rc = OS_WRITE(streamP->str_fd, streamP->str_buffer,
666                       streamP->str_bufoff);
667         if (rc < 0) {
668             streamP->str_error = errno;
669             retval = -1;
670         }
671         streamP->str_bufoff = 0;
672         streamP->str_buflen = STREAM_HANDLE_BUFSIZE;
673     }
674
675     return retval;
676 }
677
678 /* Free a buffered I/O handle */
679 int
680 stream_close(StreamHandle_t * streamP, int reallyClose)
681 {
682     int rc;
683     int retval = 0;
684
685     assert(streamP != NULL);
686     if (streamP->str_direction == STREAM_DIRECTION_WRITE
687         && streamP->str_bufoff > 0) {
688         rc = OS_WRITE(streamP->str_fd, streamP->str_buffer,
689                       streamP->str_bufoff);
690         if (rc < 0) {
691             retval = -1;
692         }
693     }
694     if (reallyClose) {
695         rc = OS_CLOSE(streamP->str_fd);
696         if (rc < 0) {
697             retval = -1;
698         }
699     }
700     streamP->str_fd = INVALID_FD;
701
702     IH_LOCK;
703     DLL_INSERT_TAIL(streamP, streamAvailHead, streamAvailTail,
704                     str_next, str_prev);
705     IH_UNLOCK;
706     return retval;
707 }
708
709 /* Close all unused file descriptors associated with the inode
710  * handle. Called with IH_LOCK held. May drop and reacquire
711  * IH_LOCK. Sets the IH_REALLY_CLOSED flag in the inode handle
712  * if it fails to close all file handles.
713  */
714 static int
715 ih_fdclose(IHandle_t * ihP)
716 {
717     int closeCount, closedAll;
718     FdHandle_t *fdP, *head, *tail, *next;
719
720     assert(ihP->ih_refcnt > 0);
721
722     closedAll = 1;
723     DLL_INIT_LIST(head, tail);
724     ihP->ih_flags &= ~IH_REALLY_CLOSED;
725
726     /*
727      * Remove the file descriptors for this Inode from the LRU queue
728      * and the IHandle queue and put them on a temporary queue so we
729      * can drop the lock before we close the files.
730      */
731     for (fdP = ihP->ih_fdhead; fdP != NULL; fdP = next) {
732         next = fdP->fd_ihnext;
733         assert(fdP->fd_ih == ihP);
734         assert(fdP->fd_status == FD_HANDLE_OPEN
735                || fdP->fd_status == FD_HANDLE_INUSE);
736         if (fdP->fd_status == FD_HANDLE_OPEN) {
737             DLL_DELETE(fdP, ihP->ih_fdhead, ihP->ih_fdtail, fd_ihnext,
738                        fd_ihprev);
739             DLL_DELETE(fdP, fdLruHead, fdLruTail, fd_next, fd_prev);
740             DLL_INSERT_TAIL(fdP, head, tail, fd_next, fd_prev);
741         } else {
742             closedAll = 0;
743             ihP->ih_flags |= IH_REALLY_CLOSED;
744         }
745     }
746
747     /* If the ihandle reference count is 1, we should have
748      * closed all file descriptors.
749      */
750     if (ihP->ih_refcnt == 1 || closedAll) {
751         assert(closedAll);
752         assert(!ihP->ih_fdhead);
753         assert(!ihP->ih_fdtail);
754     }
755
756     if (head == NULL) {
757         return 0;               /* No file descriptors closed */
758     }
759
760     IH_UNLOCK;
761     /*
762      * Close the file descriptors
763      */
764     closeCount = 0;
765     for (fdP = head; fdP != NULL; fdP = fdP->fd_next) {
766         OS_CLOSE(fdP->fd_fd);
767         fdP->fd_status = FD_HANDLE_AVAIL;
768         fdP->fd_fd = INVALID_FD;
769         fdP->fd_ih = NULL;
770         closeCount++;
771     }
772
773     IH_LOCK;
774     assert(fdInUseCount >= closeCount);
775     fdInUseCount -= closeCount;
776
777     /*
778      * Append the temporary queue to the list of available descriptors
779      */
780     if (fdAvailHead == NULL) {
781         fdAvailHead = head;
782         fdAvailTail = tail;
783     } else {
784         fdAvailTail->fd_next = head;
785         head->fd_prev = fdAvailTail;
786         fdAvailTail = tail;
787     }
788
789     return 0;
790 }
791
792 /* Close all cached file descriptors for this inode. */
793 int
794 ih_reallyclose(IHandle_t * ihP)
795 {
796     if (!ihP)
797         return 0;
798
799     IH_LOCK;
800     assert(ihP->ih_refcnt > 0);
801     ih_fdclose(ihP);
802
803     IH_UNLOCK;
804     return 0;
805 }
806
807 /* Release an Inode handle. All cached file descriptors for this
808  * inode are closed when the last reference to this handle is released
809  */
810 int
811 ih_release(IHandle_t * ihP)
812 {
813     int ihash;
814
815     if (!ihP)
816         return 0;
817
818     IH_LOCK;
819     assert(ihP->ih_refcnt > 0);
820
821     if (ihP->ih_refcnt > 1) {
822         ihP->ih_refcnt--;
823         IH_UNLOCK;
824         return 0;
825     }
826
827     ihash = IH_HASH(ihP->ih_dev, ihP->ih_vid, ihP->ih_ino);
828     DLL_DELETE(ihP, ihashTable[ihash].ihash_head,
829                ihashTable[ihash].ihash_tail, ih_next, ih_prev);
830
831     ih_fdclose(ihP);
832
833     ihP->ih_refcnt--;
834
835     DLL_INSERT_TAIL(ihP, ihAvailHead, ihAvailTail, ih_next, ih_prev);
836
837     IH_UNLOCK;
838     return 0;
839 }
840
841 /* Sync an inode to disk if its handle isn't NULL */
842 int
843 ih_condsync(IHandle_t * ihP)
844 {
845     int code;
846     FdHandle_t *fdP;
847
848     if (!ihP)
849         return 0;
850
851     fdP = IH_OPEN(ihP);
852     if (fdP == NULL)
853         return -1;
854
855     code = FDH_SYNC(fdP);
856     FDH_CLOSE(fdP);
857
858     return code;
859 }
860
861 void
862 ih_sync_all() {
863     int ihash;
864
865     IH_LOCK;
866     for (ihash = 0; ihash < I_HANDLE_HASH_SIZE; ihash++) {
867         IHandle_t *ihP, *ihPnext;
868
869         ihP = ihashTable[ihash].ihash_head;
870         if (ihP)
871             ihP->ih_refcnt++;   /* must not disappear over unlock */
872         for (; ihP; ihP = ihPnext) {
873             
874             if (ihP->ih_synced) {
875                 FdHandle_t *fdP;
876
877                 ihP->ih_synced = 0;
878                 IH_UNLOCK;
879
880                 fdP = IH_OPEN(ihP);
881                 if (fdP) OS_SYNC(fdP->fd_fd);
882                 FDH_CLOSE(fdP);
883
884                 IH_LOCK;
885             }
886
887             /* when decrementing the refcount, the ihandle might disappear
888                and we might not even be able to proceed to the next one.
889                Hence the gymnastics putting a hold on the next one already */
890             ihPnext = ihP->ih_next;
891             if (ihPnext) ihPnext->ih_refcnt++;
892
893             if (ihP->ih_refcnt > 1) {
894                 ihP->ih_refcnt--;
895             } else {
896                 IH_UNLOCK;
897                 ih_release(ihP);
898                 IH_LOCK;
899             }
900
901         }
902     }
903     IH_UNLOCK;
904 }
905
906 void *
907 ih_sync_thread() {
908     while(1) {
909
910 #ifdef AFS_PTHREAD_ENV
911         sleep(10);
912 #else /* AFS_PTHREAD_ENV */
913         IOMGR_Sleep(60);
914 #endif /* AFS_PTHREAD_ENV */
915
916         sync();
917         ih_sync_all();
918     }
919 }
920
921
922 /*************************************************************************
923  * OS specific support routines.
924  *************************************************************************/
925 #ifndef AFS_NAMEI_ENV
926 Inode
927 ih_icreate(IHandle_t * ih, int dev, char *part, Inode nI, int p1, int p2,
928            int p3, int p4)
929 {
930     Inode ino;
931 #ifdef  AFS_3DISPARES
932     /* See viceinode.h */
933     if (p2 == INODESPECIAL) {
934         int tp = p3;
935         p3 = p4;
936         p4 = tp;
937     }
938 #endif
939     ino = ICREATE(dev, part, nI, p1, p2, p3, p4);
940     return ino;
941 }
942 #endif /* AFS_NAMEI_ENV */
943
944
945 #ifndef AFS_NT40_ENV
946 afs_sfsize_t
947 ih_size(int fd)
948 {
949     struct afs_stat status;
950     if (afs_fstat(fd, &status) < 0)
951         return -1;
952     return status.st_size;
953 }
954 #endif