8c0e83ba0e3ef39704475529c09e997e48159fd2
[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         if (fdP->fd_status == FD_HANDLE_CLOSING) {
329             /* The handle was open when an IH_REALLYCLOSE was issued, so we
330              * cannot reuse it; it will be closed soon. */
331             continue;
332         }
333 #ifndef HAVE_PIO
334         /*
335          * If we don't have positional i/o, don't try to share fds, since
336          * we can't do so in a threadsafe way.
337          */
338         if (fdP->fd_status == FD_HANDLE_INUSE) {
339             continue;
340         }
341         osi_Assert(fdP->fd_status == FD_HANDLE_OPEN);
342 #else /* HAVE_PIO */
343         osi_Assert(fdP->fd_status != FD_HANDLE_AVAIL);
344 #endif /* HAVE_PIO */
345
346         fdP->fd_refcnt++;
347         if (fdP->fd_status == FD_HANDLE_OPEN) {
348             fdP->fd_status = FD_HANDLE_INUSE;
349             DLL_DELETE(fdP, fdLruHead, fdLruTail, fd_next, fd_prev);
350         }
351         ihP->ih_refcnt++;
352         IH_UNLOCK;
353         return fdP;
354     }
355
356     /*
357      * Try to open the Inode, return NULL on error.
358      */
359     fdInUseCount += 1;
360     IH_UNLOCK;
361 ih_open_retry:
362     fd = OS_IOPEN(ihP);
363     IH_LOCK;
364     if (fd == INVALID_FD && (errno != EMFILE || fdLruHead == NULL) ) {
365         fdInUseCount -= 1;
366         IH_UNLOCK;
367         return NULL;
368     }
369
370     /* fdCacheSize limits the size of the descriptor cache, but
371      * we permit the number of open files to exceed fdCacheSize.
372      * We only recycle open file descriptors when the number
373      * of open files reaches the size of the cache */
374     if ((fdInUseCount > fdCacheSize || fd == INVALID_FD)  && fdLruHead != NULL) {
375         fdP = fdLruHead;
376         osi_Assert(fdP->fd_status == FD_HANDLE_OPEN);
377         DLL_DELETE(fdP, fdLruHead, fdLruTail, fd_next, fd_prev);
378         DLL_DELETE(fdP, fdP->fd_ih->ih_fdhead, fdP->fd_ih->ih_fdtail,
379                    fd_ihnext, fd_ihprev);
380         closeFd = fdP->fd_fd;
381         if (fd == INVALID_FD) {
382             fdCacheSize--;          /* reduce in order to not run into here too often */
383             DLL_INSERT_TAIL(fdP, fdAvailHead, fdAvailTail, fd_next, fd_prev);
384             fdP->fd_status = FD_HANDLE_AVAIL;
385             fdP->fd_ih = NULL;
386             fdP->fd_fd = INVALID_FD;
387             IH_UNLOCK;
388             OS_CLOSE(closeFd);
389             goto ih_open_retry;
390         }
391     } else {
392         if (fdAvailHead == NULL) {
393             fdHandleAllocateChunk();
394         }
395         fdP = fdAvailHead;
396         osi_Assert(fdP->fd_status == FD_HANDLE_AVAIL);
397         DLL_DELETE(fdP, fdAvailHead, fdAvailTail, fd_next, fd_prev);
398         closeFd = INVALID_FD;
399     }
400
401     fdP->fd_status = FD_HANDLE_INUSE;
402     fdP->fd_fd = fd;
403     fdP->fd_ih = ihP;
404     fdP->fd_refcnt++;
405
406     ihP->ih_refcnt++;
407
408     /* Add this handle to the Inode's list of open descriptors */
409     DLL_INSERT_TAIL(fdP, ihP->ih_fdhead, ihP->ih_fdtail, fd_ihnext,
410                     fd_ihprev);
411
412     if (closeFd != INVALID_FD) {
413         IH_UNLOCK;
414         OS_CLOSE(closeFd);
415         IH_LOCK;
416         fdInUseCount -= 1;
417     }
418
419     IH_UNLOCK;
420     return fdP;
421 }
422
423 /*
424  * Return a file descriptor handle to the cache
425  */
426 int
427 fd_close(FdHandle_t * fdP)
428 {
429     IHandle_t *ihP;
430
431     if (!fdP)
432         return 0;
433
434     IH_LOCK;
435     osi_Assert(ih_Inited);
436     osi_Assert(fdInUseCount > 0);
437     osi_Assert(fdP->fd_status == FD_HANDLE_INUSE ||
438                fdP->fd_status == FD_HANDLE_CLOSING);
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 (fdP->fd_status == FD_HANDLE_CLOSING ||
448         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     else
466         _ih_release_r(ihP);
467
468     IH_UNLOCK;
469
470     return 0;
471 }
472
473 /*
474  * Actually close the file descriptor handle and return it to
475  * the free list.
476  */
477 int
478 fd_reallyclose(FdHandle_t * fdP)
479 {
480     FD_t closeFd;
481     IHandle_t *ihP;
482
483     if (!fdP)
484         return 0;
485
486     IH_LOCK;
487     osi_Assert(ih_Inited);
488     osi_Assert(fdInUseCount > 0);
489     osi_Assert(fdP->fd_status == FD_HANDLE_INUSE ||
490                fdP->fd_status == FD_HANDLE_CLOSING);
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     else
526         _ih_release_r(ihP);
527
528     IH_UNLOCK;
529
530     return 0;
531 }
532
533 /* Enable buffered I/O on a file descriptor */
534 StreamHandle_t *
535 stream_fdopen(FD_t fd)
536 {
537     StreamHandle_t *streamP;
538
539     IH_LOCK;
540     if (streamAvailHead == NULL) {
541         streamHandleAllocateChunk();
542     }
543     streamP = streamAvailHead;
544     DLL_DELETE(streamP, streamAvailHead, streamAvailTail, str_next, str_prev);
545     IH_UNLOCK;
546     streamP->str_fd = fd;
547     streamP->str_buflen = 0;
548     streamP->str_bufoff = 0;
549     streamP->str_fdoff = 0;
550     streamP->str_error = 0;
551     streamP->str_eof = 0;
552     streamP->str_direction = STREAM_DIRECTION_NONE;
553     return streamP;
554 }
555
556 /* Open a file for buffered I/O */
557 StreamHandle_t *
558 stream_open(const char *filename, const char *mode)
559 {
560     FD_t fd = INVALID_FD;
561
562     if (strcmp(mode, "r") == 0) {
563         fd = OS_OPEN(filename, O_RDONLY, 0);
564     } else if (strcmp(mode, "r+") == 0) {
565         fd = OS_OPEN(filename, O_RDWR, 0);
566     } else if (strcmp(mode, "w") == 0) {
567         fd = OS_OPEN(filename, O_WRONLY | O_TRUNC | O_CREAT, 0);
568     } else if (strcmp(mode, "w+") == 0) {
569         fd = OS_OPEN(filename, O_RDWR | O_TRUNC | O_CREAT, 0);
570     } else if (strcmp(mode, "a") == 0) {
571         fd = OS_OPEN(filename, O_WRONLY | O_APPEND | O_CREAT, 0);
572     } else if (strcmp(mode, "a+") == 0) {
573         fd = OS_OPEN(filename, O_RDWR | O_APPEND | O_CREAT, 0);
574     } else {
575         osi_Assert(FALSE);              /* not implemented */
576     }
577
578     if (fd == INVALID_FD) {
579         return NULL;
580     }
581     return stream_fdopen(fd);
582 }
583
584 /* fread for buffered I/O handles */
585 afs_sfsize_t
586 stream_read(void *ptr, afs_fsize_t size, afs_fsize_t nitems,
587             StreamHandle_t * streamP)
588 {
589     afs_fsize_t nbytes, bytesRead, bytesToRead;
590     char *p;
591
592     /* Need to seek before changing direction */
593     if (streamP->str_direction == STREAM_DIRECTION_NONE) {
594         streamP->str_direction = STREAM_DIRECTION_READ;
595         streamP->str_bufoff = 0;
596         streamP->str_buflen = 0;
597     } else {
598         osi_Assert(streamP->str_direction == STREAM_DIRECTION_READ);
599     }
600
601     bytesRead = 0;
602     nbytes = size * nitems;
603     p = (char *)ptr;
604     while (nbytes > 0 && !streamP->str_eof) {
605         if (streamP->str_buflen == 0) {
606             streamP->str_bufoff = 0;
607             streamP->str_buflen =
608                 OS_PREAD(streamP->str_fd, streamP->str_buffer,
609                         STREAM_HANDLE_BUFSIZE, streamP->str_fdoff);
610             if (streamP->str_buflen < 0) {
611                 streamP->str_error = errno;
612                 streamP->str_buflen = 0;
613                 bytesRead = 0;
614                 break;
615             } else if (streamP->str_buflen == 0) {
616                 streamP->str_eof = 1;
617                 break;
618             }
619             streamP->str_fdoff += streamP->str_buflen;
620         }
621
622         bytesToRead = nbytes;
623         if (bytesToRead > streamP->str_buflen) {
624             bytesToRead = streamP->str_buflen;
625         }
626         memcpy(p, streamP->str_buffer + streamP->str_bufoff, bytesToRead);
627         p += bytesToRead;
628         streamP->str_bufoff += bytesToRead;
629         streamP->str_buflen -= bytesToRead;
630         bytesRead += bytesToRead;
631         nbytes -= bytesToRead;
632     }
633
634     return (bytesRead / size);
635 }
636
637 /* fwrite for buffered I/O handles */
638 afs_sfsize_t
639 stream_write(void *ptr, afs_fsize_t size, afs_fsize_t nitems,
640              StreamHandle_t * streamP)
641 {
642     char *p;
643     afs_sfsize_t rc;
644     afs_fsize_t nbytes, bytesWritten, bytesToWrite;
645
646     /* Need to seek before changing direction */
647     if (streamP->str_direction == STREAM_DIRECTION_NONE) {
648         streamP->str_direction = STREAM_DIRECTION_WRITE;
649         streamP->str_bufoff = 0;
650         streamP->str_buflen = STREAM_HANDLE_BUFSIZE;
651     } else {
652         osi_Assert(streamP->str_direction == STREAM_DIRECTION_WRITE);
653     }
654
655     nbytes = size * nitems;
656     bytesWritten = 0;
657     p = (char *)ptr;
658     while (nbytes > 0) {
659         if (streamP->str_buflen == 0) {
660             rc = OS_PWRITE(streamP->str_fd, streamP->str_buffer,
661                           STREAM_HANDLE_BUFSIZE, streamP->str_fdoff);
662             if (rc < 0) {
663                 streamP->str_error = errno;
664                 bytesWritten = 0;
665                 break;
666             }
667             streamP->str_fdoff += rc;
668             streamP->str_bufoff = 0;
669             streamP->str_buflen = STREAM_HANDLE_BUFSIZE;
670         }
671
672         bytesToWrite = nbytes;
673         if (bytesToWrite > streamP->str_buflen) {
674             bytesToWrite = streamP->str_buflen;
675         }
676         memcpy(streamP->str_buffer + streamP->str_bufoff, p, bytesToWrite);
677         p += bytesToWrite;
678         streamP->str_bufoff += bytesToWrite;
679         streamP->str_buflen -= bytesToWrite;
680         bytesWritten += bytesToWrite;
681         nbytes -= bytesToWrite;
682     }
683
684     return (bytesWritten / size);
685 }
686
687 /* fseek for buffered I/O handles */
688 int
689 stream_aseek(StreamHandle_t * streamP, afs_foff_t offset)
690 {
691     ssize_t rc;
692     int retval = 0;
693
694     if (streamP->str_direction == STREAM_DIRECTION_WRITE
695         && streamP->str_bufoff > 0) {
696         rc = OS_PWRITE(streamP->str_fd, streamP->str_buffer,
697                       streamP->str_bufoff, streamP->str_fdoff);
698         if (rc < 0) {
699             streamP->str_error = errno;
700             retval = -1;
701         }
702     }
703     streamP->str_fdoff = offset;
704     streamP->str_bufoff = 0;
705     streamP->str_buflen = 0;
706     streamP->str_eof = 0;
707     streamP->str_direction = STREAM_DIRECTION_NONE;
708     return retval;
709 }
710
711 /* fflush for buffered I/O handles */
712 int
713 stream_flush(StreamHandle_t * streamP)
714 {
715     ssize_t rc;
716     int retval = 0;
717
718     if (streamP->str_direction == STREAM_DIRECTION_WRITE
719         && streamP->str_bufoff > 0) {
720         rc = OS_PWRITE(streamP->str_fd, streamP->str_buffer,
721                       streamP->str_bufoff, streamP->str_fdoff);
722         if (rc < 0) {
723             streamP->str_error = errno;
724             retval = -1;
725         } else {
726             streamP->str_fdoff += rc;
727         }
728         streamP->str_bufoff = 0;
729         streamP->str_buflen = STREAM_HANDLE_BUFSIZE;
730     }
731
732     return retval;
733 }
734
735 /* Free a buffered I/O handle */
736 int
737 stream_close(StreamHandle_t * streamP, int reallyClose)
738 {
739     ssize_t rc;
740     int retval = 0;
741
742     osi_Assert(streamP != NULL);
743     if (streamP->str_direction == STREAM_DIRECTION_WRITE
744         && streamP->str_bufoff > 0) {
745         rc = OS_PWRITE(streamP->str_fd, streamP->str_buffer,
746                       streamP->str_bufoff, streamP->str_fdoff);
747         if (rc < 0) {
748             retval = -1;
749         } else {
750             streamP->str_fdoff += rc;
751         }
752     }
753     if (reallyClose) {
754         rc = OS_CLOSE(streamP->str_fd);
755         if (rc < 0) {
756             retval = -1;
757         }
758     }
759     streamP->str_fd = INVALID_FD;
760
761     IH_LOCK;
762     DLL_INSERT_TAIL(streamP, streamAvailHead, streamAvailTail,
763                     str_next, str_prev);
764     IH_UNLOCK;
765     return retval;
766 }
767
768 /* Close all unused file descriptors associated with the inode
769  * handle. Called with IH_LOCK held. May drop and reacquire
770  * IH_LOCK. Sets the IH_REALLY_CLOSED flag in the inode handle
771  * if it fails to close all file handles.
772  */
773 static int
774 ih_fdclose(IHandle_t * ihP)
775 {
776     int closeCount, closedAll;
777     FdHandle_t *fdP, *head, *tail, *next;
778
779     osi_Assert(ihP->ih_refcnt > 0);
780
781     closedAll = 1;
782     DLL_INIT_LIST(head, tail);
783     ihP->ih_flags &= ~IH_REALLY_CLOSED;
784
785     /*
786      * Remove the file descriptors for this Inode from the LRU queue
787      * and the IHandle queue and put them on a temporary queue so we
788      * can drop the lock before we close the files.
789      */
790     for (fdP = ihP->ih_fdhead; fdP != NULL; fdP = next) {
791         next = fdP->fd_ihnext;
792         osi_Assert(fdP->fd_ih == ihP);
793         osi_Assert(fdP->fd_status == FD_HANDLE_OPEN
794                || fdP->fd_status == FD_HANDLE_INUSE
795                || fdP->fd_status == FD_HANDLE_CLOSING);
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             fdP->fd_status = FD_HANDLE_CLOSING;
804             ihP->ih_flags |= IH_REALLY_CLOSED;
805         }
806     }
807
808     /* If the ihandle reference count is 1, we should have
809      * closed all file descriptors.
810      */
811     if (ihP->ih_refcnt == 1 || closedAll) {
812         osi_Assert(closedAll);
813         osi_Assert(!ihP->ih_fdhead);
814         osi_Assert(!ihP->ih_fdtail);
815     }
816
817     if (head == NULL) {
818         return 0;               /* No file descriptors closed */
819     }
820
821     IH_UNLOCK;
822     /*
823      * Close the file descriptors
824      */
825     closeCount = 0;
826     for (fdP = head; fdP != NULL; fdP = fdP->fd_next) {
827         OS_CLOSE(fdP->fd_fd);
828         fdP->fd_status = FD_HANDLE_AVAIL;
829         fdP->fd_refcnt = 0;
830         fdP->fd_fd = INVALID_FD;
831         fdP->fd_ih = NULL;
832         closeCount++;
833     }
834
835     IH_LOCK;
836     osi_Assert(fdInUseCount >= closeCount);
837     fdInUseCount -= closeCount;
838
839     /*
840      * Append the temporary queue to the list of available descriptors
841      */
842     if (fdAvailHead == NULL) {
843         fdAvailHead = head;
844         fdAvailTail = tail;
845     } else {
846         fdAvailTail->fd_next = head;
847         head->fd_prev = fdAvailTail;
848         fdAvailTail = tail;
849     }
850
851     return 0;
852 }
853
854 /* Close all cached file descriptors for this inode. */
855 int
856 ih_reallyclose(IHandle_t * ihP)
857 {
858     if (!ihP)
859         return 0;
860
861     IH_LOCK;
862     ihP->ih_refcnt++;   /* must not disappear over unlock */
863     if (ihP->ih_synced) {
864         FdHandle_t *fdP;
865         ihP->ih_synced = 0;
866         IH_UNLOCK;
867
868         fdP = IH_OPEN(ihP);
869         if (fdP) {
870             OS_SYNC(fdP->fd_fd);
871             FDH_CLOSE(fdP);
872         }
873
874         IH_LOCK;
875     }
876
877     osi_Assert(ihP->ih_refcnt > 0);
878
879     ih_fdclose(ihP);
880
881     if (ihP->ih_refcnt > 1)
882         ihP->ih_refcnt--;
883     else
884         _ih_release_r(ihP);
885
886     IH_UNLOCK;
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 static int
894 _ih_release_r(IHandle_t * ihP)
895 {
896     int ihash;
897
898     if (!ihP)
899         return 0;
900
901     osi_Assert(ihP->ih_refcnt > 0);
902
903     if (ihP->ih_refcnt > 1) {
904         ihP->ih_refcnt--;
905         return 0;
906     }
907
908     ihash = IH_HASH(ihP->ih_dev, ihP->ih_vid, ihP->ih_ino);
909     DLL_DELETE(ihP, ihashTable[ihash].ihash_head,
910                ihashTable[ihash].ihash_tail, ih_next, ih_prev);
911
912     ih_fdclose(ihP);
913
914     ihP->ih_refcnt--;
915
916     DLL_INSERT_TAIL(ihP, ihAvailHead, ihAvailTail, ih_next, ih_prev);
917
918     return 0;
919 }
920
921 /* Release an Inode handle. All cached file descriptors for this
922  * inode are closed when the last reference to this handle is released
923  */
924 int
925 ih_release(IHandle_t * ihP)
926 {
927     int ret;
928
929     if (!ihP)
930         return 0;
931
932     IH_LOCK;
933     ret = _ih_release_r(ihP);
934     IH_UNLOCK;
935     return ret;
936 }
937
938 /* Sync an inode to disk if its handle isn't NULL */
939 int
940 ih_condsync(IHandle_t * ihP)
941 {
942     int code;
943     FdHandle_t *fdP;
944
945     if (!ihP)
946         return 0;
947
948     fdP = IH_OPEN(ihP);
949     if (fdP == NULL)
950         return -1;
951
952     code = FDH_SYNC(fdP);
953     FDH_CLOSE(fdP);
954
955     return code;
956 }
957
958 void
959 ih_sync_all(void) {
960
961     int ihash;
962
963     IH_LOCK;
964     for (ihash = 0; ihash < I_HANDLE_HASH_SIZE; ihash++) {
965         IHandle_t *ihP, *ihPnext;
966
967         ihP = ihashTable[ihash].ihash_head;
968         if (ihP)
969             ihP->ih_refcnt++;   /* must not disappear over unlock */
970         for (; ihP; ihP = ihPnext) {
971
972             if (ihP->ih_synced) {
973                 FdHandle_t *fdP;
974
975                 ihP->ih_synced = 0;
976                 IH_UNLOCK;
977
978                 fdP = IH_OPEN(ihP);
979                 if (fdP) {
980                     OS_SYNC(fdP->fd_fd);
981                     FDH_CLOSE(fdP);
982                 }
983
984                 IH_LOCK;
985             }
986
987             /* when decrementing the refcount, the ihandle might disappear
988                and we might not even be able to proceed to the next one.
989                Hence the gymnastics putting a hold on the next one already */
990             ihPnext = ihP->ih_next;
991             if (ihPnext) ihPnext->ih_refcnt++;
992
993             if (ihP->ih_refcnt > 1)
994                 ihP->ih_refcnt--;
995             else
996                 _ih_release_r(ihP);
997         }
998     }
999     IH_UNLOCK;
1000 }
1001
1002 void *
1003 ih_sync_thread(void *dummy) {
1004     afs_pthread_setname_self("ih_syncer");
1005     while(1) {
1006
1007 #ifdef AFS_PTHREAD_ENV
1008         sleep(10);
1009 #else /* AFS_PTHREAD_ENV */
1010         IOMGR_Sleep(60);
1011 #endif /* AFS_PTHREAD_ENV */
1012
1013         ih_sync_all();
1014     }
1015     return NULL;
1016 }
1017
1018
1019 /*************************************************************************
1020  * OS specific support routines.
1021  *************************************************************************/
1022 #ifndef AFS_NAMEI_ENV
1023 Inode
1024 ih_icreate(IHandle_t * ih, int dev, char *part, Inode nI, int p1, int p2,
1025            int p3, int p4)
1026 {
1027     Inode ino;
1028 #ifdef  AFS_3DISPARES
1029     /* See viceinode.h */
1030     if (p2 == INODESPECIAL) {
1031         int tp = p3;
1032         p3 = p4;
1033         p4 = tp;
1034     }
1035 #endif
1036     ino = ICREATE(dev, part, nI, p1, p2, p3, p4);
1037     return ino;
1038 }
1039 #endif /* AFS_NAMEI_ENV */
1040
1041 afs_sfsize_t
1042 ih_size(FD_t fd)
1043 {
1044 #ifdef AFS_NT40_ENV
1045     LARGE_INTEGER size;
1046     if (!GetFileSizeEx(fd, &size))
1047         return -1;
1048     return size.QuadPart;
1049 #else
1050     struct afs_stat_st status;
1051     if (afs_fstat(fd, &status) < 0)
1052         return -1;
1053     return status.st_size;
1054 #endif
1055 }
1056
1057 #ifndef HAVE_PIO
1058 ssize_t
1059 ih_pread(int fd, 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_READ(fd, buf, count);
1066 }
1067
1068 ssize_t
1069 ih_pwrite(int fd, const void * buf, size_t count, afs_foff_t offset)
1070 {
1071         afs_foff_t code;
1072         code = OS_SEEK(fd, offset, 0);
1073         if (code < 0)
1074             return code;
1075         return OS_WRITE(fd, buf, count);
1076 }
1077 #endif /* !HAVE_PIO */