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