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