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