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