Update NEWS for 1.6.22
[pkg-k5-afs_openafs.git] / src / vol / ihandle.c
blobf1e0ae4fce943dae8932a8c2981fe94e0c3755b2
1 /*
2 * Copyright 2000, International Business Machines Corporation and others.
3 * All Rights Reserved.
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 */
10 /* ihandle.c - file descriptor cacheing for Inode handles. */
11 /* */
12 /************************************************************************/
14 #include <afsconfig.h>
15 #include <afs/param.h>
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
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>
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 */
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 */
59 /* Linked list of available inode handles */
60 IHandle_t *ihAvailHead;
61 IHandle_t *ihAvailTail;
63 /* Linked list of available file descriptor handles */
64 FdHandle_t *fdAvailHead;
65 FdHandle_t *fdAvailTail;
67 /* Linked list of available stream descriptor handles */
68 StreamHandle_t *streamAvailHead;
69 StreamHandle_t *streamAvailTail;
71 /* LRU list for file descriptor handles */
72 FdHandle_t *fdLruHead;
73 FdHandle_t *fdLruTail;
75 int ih_Inited = 0;
76 int ih_PkgDefaultsSet = 0;
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. */
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
86 int fdMaxCacheSize = 0;
87 int fdCacheSize = 0;
89 /* Number of in use file descriptors */
90 int fdInUseCount = 0;
92 /* Hash table for inode handles */
93 IHashBucket_t ihashTable[I_HANDLE_HASH_SIZE];
95 void *ih_sync_thread(void *);
97 /* start-time configurable I/O limits */
98 ih_init_params vol_io_params;
100 void ih_PkgDefaults(void)
102 /* once */
103 ih_PkgDefaultsSet = 1;
105 /* default to well-known values */
106 vol_io_params.fd_handle_setaside = FD_HANDLE_SETASIDE;
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;
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;
118 vol_io_params.sync_behavior = IH_SYNC_ONCLOSE;
122 ih_SetSyncBehavior(const char *behavior)
124 int val;
126 if (strcmp(behavior, "always") == 0) {
127 val = IH_SYNC_ALWAYS;
129 } else if (strcmp(behavior, "delayed") == 0) {
130 val = IH_SYNC_DELAYED;
132 } else if (strcmp(behavior, "onclose") == 0) {
133 val = IH_SYNC_ONCLOSE;
135 } else if (strcmp(behavior, "never") == 0) {
136 val = IH_SYNC_NEVER;
138 } else {
139 /* invalid behavior name */
140 return -1;
143 vol_io_params.sync_behavior = val;
144 return 0;
147 #ifdef AFS_PTHREAD_ENV
148 /* Initialize the global ihandle mutex */
149 void
150 ih_glock_init(void)
152 MUTEX_INIT(&ih_glock_mutex, "ih glock", MUTEX_DEFAULT, 0);
154 #endif /* AFS_PTHREAD_ENV */
156 /* Initialize the file descriptor cache */
157 void
158 ih_Initialize(void)
160 int i;
161 osi_Assert(!ih_Inited);
162 ih_Inited = 1;
163 DLL_INIT_LIST(ihAvailHead, ihAvailTail);
164 DLL_INIT_LIST(fdAvailHead, fdAvailTail);
165 DLL_INIT_LIST(fdLruHead, fdLruTail);
166 for (i = 0; i < I_HANDLE_HASH_SIZE; i++) {
167 DLL_INIT_LIST(ihashTable[i].ihash_head, ihashTable[i].ihash_tail);
169 #if defined(AFS_NT40_ENV)
170 fdMaxCacheSize = vol_io_params.fd_max_cachesize;
171 #elif defined(AFS_SUN5_ENV) || defined(AFS_NBSD_ENV)
173 struct rlimit rlim;
174 osi_Assert(getrlimit(RLIMIT_NOFILE, &rlim) == 0);
175 rlim.rlim_cur = rlim.rlim_max;
176 osi_Assert(setrlimit(RLIMIT_NOFILE, &rlim) == 0);
177 fdMaxCacheSize = rlim.rlim_cur - vol_io_params.fd_handle_setaside;
178 #ifdef AFS_NBSD_ENV
179 /* XXX this is to avoid using up all system fd netbsd is
180 * somewhat broken and have set maximum fd for a root process
181 * to the same as system fd that is avaible, so if the
182 * fileserver uses all up process fds, all system fd will be
183 * used up too !
185 * Check for this better
187 fdMaxCacheSize /= 4;
188 #endif
189 fdMaxCacheSize = MIN(fdMaxCacheSize, vol_io_params.fd_max_cachesize);
190 osi_Assert(fdMaxCacheSize > 0);
192 #elif defined(AFS_HPUX_ENV)
193 /* Avoid problems with "UFSOpen: igetinode failed" panics on HPUX 11.0 */
194 fdMaxCacheSize = 0;
195 #else
197 long fdMax = MAX(sysconf(_SC_OPEN_MAX) - vol_io_params.fd_handle_setaside,
199 fdMaxCacheSize = (int)MIN(fdMax, vol_io_params.fd_max_cachesize);
201 #endif
202 fdCacheSize = MIN(fdMaxCacheSize, vol_io_params.fd_initial_cachesize);
204 if (vol_io_params.sync_behavior == IH_SYNC_DELAYED) {
205 #ifdef AFS_PTHREAD_ENV
206 pthread_t syncer;
207 pthread_attr_t tattr;
209 pthread_attr_init(&tattr);
210 pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
212 pthread_create(&syncer, &tattr, ih_sync_thread, NULL);
213 #else /* AFS_PTHREAD_ENV */
214 PROCESS syncer;
215 LWP_CreateProcess(ih_sync_thread, 16*1024, LWP_MAX_PRIORITY - 2,
216 NULL, "ih_syncer", &syncer);
217 #endif /* AFS_PTHREAD_ENV */
222 /* Make the file descriptor cache as big as possible. Don't this call
223 * if the program uses fopen or fdopen, if fd_max_cachesize cannot be
224 * represented in the fileno member of the system FILE structure (or
225 * equivalent).
227 void
228 ih_UseLargeCache(void)
230 IH_LOCK;
232 if (!ih_PkgDefaultsSet) {
233 ih_PkgDefaults();
236 if (!ih_Inited) {
237 ih_Initialize();
240 fdCacheSize = fdMaxCacheSize;
242 IH_UNLOCK;
245 /* Allocate a chunk of inode handles */
246 void
247 iHandleAllocateChunk(void)
249 int i;
250 IHandle_t *ihP;
252 osi_Assert(ihAvailHead == NULL);
253 ihP = (IHandle_t *) malloc(I_HANDLE_MALLOCSIZE * sizeof(IHandle_t));
254 osi_Assert(ihP != NULL);
255 for (i = 0; i < I_HANDLE_MALLOCSIZE; i++) {
256 ihP[i].ih_refcnt = 0;
257 DLL_INSERT_TAIL(&ihP[i], ihAvailHead, ihAvailTail, ih_next, ih_prev);
261 /* Initialize an inode handle */
262 IHandle_t *
263 ih_init(int dev, int vid, Inode ino)
265 int ihash = IH_HASH(dev, vid, ino);
266 IHandle_t *ihP;
268 if (!ih_PkgDefaultsSet) {
269 ih_PkgDefaults();
272 IH_LOCK;
273 if (!ih_Inited) {
274 ih_Initialize();
277 /* Do we already have a handle for this Inode? */
278 for (ihP = ihashTable[ihash].ihash_head; ihP; ihP = ihP->ih_next) {
279 if (ihP->ih_ino == ino && ihP->ih_vid == vid && ihP->ih_dev == dev) {
280 ihP->ih_refcnt++;
281 IH_UNLOCK;
282 return ihP;
286 /* Allocate and initialize a new Inode handle */
287 if (ihAvailHead == NULL) {
288 iHandleAllocateChunk();
290 ihP = ihAvailHead;
291 osi_Assert(ihP->ih_refcnt == 0);
292 DLL_DELETE(ihP, ihAvailHead, ihAvailTail, ih_next, ih_prev);
293 ihP->ih_dev = dev;
294 ihP->ih_vid = vid;
295 ihP->ih_ino = ino;
296 ihP->ih_flags = 0;
297 ihP->ih_synced = 0;
298 ihP->ih_refcnt = 1;
299 DLL_INIT_LIST(ihP->ih_fdhead, ihP->ih_fdtail);
300 DLL_INSERT_TAIL(ihP, ihashTable[ihash].ihash_head,
301 ihashTable[ihash].ihash_tail, ih_next, ih_prev);
302 IH_UNLOCK;
303 return ihP;
306 /* Copy an inode handle */
307 IHandle_t *
308 ih_copy(IHandle_t * ihP)
310 IH_LOCK;
311 osi_Assert(ih_Inited);
312 osi_Assert(ihP->ih_refcnt > 0);
313 ihP->ih_refcnt++;
314 IH_UNLOCK;
315 return ihP;
318 /* Allocate a chunk of file descriptor handles */
319 void
320 fdHandleAllocateChunk(void)
322 int i;
323 FdHandle_t *fdP;
325 osi_Assert(fdAvailHead == NULL);
326 fdP = (FdHandle_t *) malloc(FD_HANDLE_MALLOCSIZE * sizeof(FdHandle_t));
327 osi_Assert(fdP != NULL);
328 for (i = 0; i < FD_HANDLE_MALLOCSIZE; i++) {
329 fdP[i].fd_status = FD_HANDLE_AVAIL;
330 fdP[i].fd_refcnt = 0;
331 fdP[i].fd_ih = NULL;
332 fdP[i].fd_fd = INVALID_FD;
333 fdP[i].fd_ihnext = NULL;
334 fdP[i].fd_ihprev = NULL;
335 DLL_INSERT_TAIL(&fdP[i], fdAvailHead, fdAvailTail, fd_next, fd_prev);
339 /* Allocate a chunk of stream handles */
340 void
341 streamHandleAllocateChunk(void)
343 int i;
344 StreamHandle_t *streamP;
346 osi_Assert(streamAvailHead == NULL);
347 streamP = (StreamHandle_t *)
348 malloc(STREAM_HANDLE_MALLOCSIZE * sizeof(StreamHandle_t));
349 osi_Assert(streamP != NULL);
350 for (i = 0; i < STREAM_HANDLE_MALLOCSIZE; i++) {
351 streamP[i].str_fd = INVALID_FD;
352 DLL_INSERT_TAIL(&streamP[i], streamAvailHead, streamAvailTail,
353 str_next, str_prev);
358 * Get a file descriptor handle given an Inode handle
359 * Takes the given file descriptor, and creates a new FdHandle_t for it,
360 * attached to the given IHandle_t. fd can be INVALID_FD, indicating that the
361 * caller failed to open the relevant file because we had too many FDs open;
362 * ih_attachfd_r will then just evict/close an existing fd in the cache, and
363 * return NULL.
365 static FdHandle_t *
366 ih_attachfd_r(IHandle_t *ihP, FD_t fd)
368 FD_t closeFd;
369 FdHandle_t *fdP;
371 /* fdCacheSize limits the size of the descriptor cache, but
372 * we permit the number of open files to exceed fdCacheSize.
373 * We only recycle open file descriptors when the number
374 * of open files reaches the size of the cache */
375 if ((fdInUseCount > fdCacheSize || fd == INVALID_FD) && fdLruHead != NULL) {
376 fdP = fdLruHead;
377 osi_Assert(fdP->fd_status == FD_HANDLE_OPEN);
378 DLL_DELETE(fdP, fdLruHead, fdLruTail, fd_next, fd_prev);
379 DLL_DELETE(fdP, fdP->fd_ih->ih_fdhead, fdP->fd_ih->ih_fdtail,
380 fd_ihnext, fd_ihprev);
381 closeFd = fdP->fd_fd;
382 if (fd == INVALID_FD) {
383 fdCacheSize--; /* reduce in order to not run into here too often */
384 DLL_INSERT_TAIL(fdP, fdAvailHead, fdAvailTail, fd_next, fd_prev);
385 fdP->fd_status = FD_HANDLE_AVAIL;
386 fdP->fd_ih = NULL;
387 fdP->fd_fd = INVALID_FD;
388 IH_UNLOCK;
389 OS_CLOSE(closeFd);
390 IH_LOCK;
391 fdInUseCount -= 1;
392 return NULL;
394 } else {
395 if (fdAvailHead == NULL) {
396 fdHandleAllocateChunk();
398 fdP = fdAvailHead;
399 osi_Assert(fdP->fd_status == FD_HANDLE_AVAIL);
400 DLL_DELETE(fdP, fdAvailHead, fdAvailTail, fd_next, fd_prev);
401 closeFd = INVALID_FD;
404 fdP->fd_status = FD_HANDLE_INUSE;
405 fdP->fd_fd = fd;
406 fdP->fd_ih = ihP;
407 fdP->fd_refcnt++;
409 ihP->ih_refcnt++;
411 /* Add this handle to the Inode's list of open descriptors */
412 DLL_INSERT_TAIL(fdP, ihP->ih_fdhead, ihP->ih_fdtail, fd_ihnext,
413 fd_ihprev);
415 if (closeFd != INVALID_FD) {
416 IH_UNLOCK;
417 OS_CLOSE(closeFd);
418 IH_LOCK;
419 fdInUseCount -= 1;
422 return fdP;
425 FdHandle_t *
426 ih_attachfd(IHandle_t *ihP, FD_t fd)
428 FdHandle_t *fdP;
430 IH_LOCK;
432 fdInUseCount += 1;
434 fdP = ih_attachfd_r(ihP, fd);
435 if (!fdP) {
436 fdInUseCount -= 1;
439 IH_UNLOCK;
441 return fdP;
445 * Get a file descriptor handle given an Inode handle
447 FdHandle_t *
448 ih_open(IHandle_t * ihP)
450 FdHandle_t *fdP;
451 FD_t fd;
453 if (!ihP) /* XXX should log here in the fileserver */
454 return NULL;
456 IH_LOCK;
458 /* Do we already have an open file handle for this Inode? */
459 for (fdP = ihP->ih_fdtail; fdP != NULL; fdP = fdP->fd_ihprev) {
460 if (fdP->fd_status == FD_HANDLE_CLOSING) {
461 /* The handle was open when an IH_REALLYCLOSE was issued, so we
462 * cannot reuse it; it will be closed soon. */
463 continue;
465 #ifndef HAVE_PIO
467 * If we don't have positional i/o, don't try to share fds, since
468 * we can't do so in a threadsafe way.
470 if (fdP->fd_status == FD_HANDLE_INUSE) {
471 continue;
473 osi_Assert(fdP->fd_status == FD_HANDLE_OPEN);
474 #else /* HAVE_PIO */
475 osi_Assert(fdP->fd_status != FD_HANDLE_AVAIL);
476 #endif /* HAVE_PIO */
478 fdP->fd_refcnt++;
479 if (fdP->fd_status == FD_HANDLE_OPEN) {
480 fdP->fd_status = FD_HANDLE_INUSE;
481 DLL_DELETE(fdP, fdLruHead, fdLruTail, fd_next, fd_prev);
483 ihP->ih_refcnt++;
484 IH_UNLOCK;
485 return fdP;
489 * Try to open the Inode, return NULL on error.
491 fdInUseCount += 1;
492 IH_UNLOCK;
493 ih_open_retry:
494 fd = OS_IOPEN(ihP);
495 IH_LOCK;
496 if (fd == INVALID_FD && (errno != EMFILE || fdLruHead == NULL) ) {
497 fdInUseCount -= 1;
498 IH_UNLOCK;
499 return NULL;
502 fdP = ih_attachfd_r(ihP, fd);
503 if (!fdP) {
504 osi_Assert(fd == INVALID_FD);
505 IH_UNLOCK;
506 goto ih_open_retry;
509 IH_UNLOCK;
511 return fdP;
515 * Return a file descriptor handle to the cache
518 fd_close(FdHandle_t * fdP)
520 IHandle_t *ihP;
522 if (!fdP)
523 return 0;
525 IH_LOCK;
526 osi_Assert(ih_Inited);
527 osi_Assert(fdInUseCount > 0);
528 osi_Assert(fdP->fd_status == FD_HANDLE_INUSE ||
529 fdP->fd_status == FD_HANDLE_CLOSING);
531 ihP = fdP->fd_ih;
533 /* Call fd_reallyclose to really close the unused file handles if
534 * the previous attempt to close (ih_reallyclose()) all file handles
535 * failed (this is determined by checking the ihandle for the flag
536 * IH_REALLY_CLOSED) or we have too many open files.
538 if (fdP->fd_status == FD_HANDLE_CLOSING ||
539 ihP->ih_flags & IH_REALLY_CLOSED || fdInUseCount > fdCacheSize) {
540 IH_UNLOCK;
541 return fd_reallyclose(fdP);
544 fdP->fd_refcnt--;
545 if (fdP->fd_refcnt == 0) {
546 /* Put this descriptor back into the cache */
547 fdP->fd_status = FD_HANDLE_OPEN;
548 DLL_INSERT_TAIL(fdP, fdLruHead, fdLruTail, fd_next, fd_prev);
551 /* If this is not the only reference to the Inode then we can decrement
552 * the reference count, otherwise we need to call ih_release.
554 if (ihP->ih_refcnt > 1) {
555 ihP->ih_refcnt--;
556 IH_UNLOCK;
557 } else {
558 IH_UNLOCK;
559 ih_release(ihP);
562 return 0;
566 * Actually close the file descriptor handle and return it to
567 * the free list.
570 fd_reallyclose(FdHandle_t * fdP)
572 FD_t closeFd;
573 IHandle_t *ihP;
575 if (!fdP)
576 return 0;
578 IH_LOCK;
579 osi_Assert(ih_Inited);
580 osi_Assert(fdInUseCount > 0);
581 osi_Assert(fdP->fd_status == FD_HANDLE_INUSE ||
582 fdP->fd_status == FD_HANDLE_CLOSING);
584 ihP = fdP->fd_ih;
585 closeFd = fdP->fd_fd;
586 fdP->fd_refcnt--;
588 if (fdP->fd_refcnt == 0) {
589 DLL_DELETE(fdP, ihP->ih_fdhead, ihP->ih_fdtail, fd_ihnext, fd_ihprev);
590 DLL_INSERT_TAIL(fdP, fdAvailHead, fdAvailTail, fd_next, fd_prev);
592 fdP->fd_status = FD_HANDLE_AVAIL;
593 fdP->fd_refcnt = 0;
594 fdP->fd_ih = NULL;
595 fdP->fd_fd = INVALID_FD;
598 /* All the file descriptor handles have been closed; reset
599 * the IH_REALLY_CLOSED flag indicating that ih_reallyclose
600 * has completed its job.
602 if (!ihP->ih_fdhead) {
603 ihP->ih_flags &= ~IH_REALLY_CLOSED;
604 } else {
605 FdHandle_t *lfdP, *next;
606 int clear = 1;
607 for (lfdP = ihP->ih_fdhead; lfdP != NULL; lfdP = next) {
608 next = lfdP->fd_ihnext;
609 osi_Assert(lfdP->fd_ih == ihP);
610 if (lfdP->fd_status != FD_HANDLE_CLOSING) {
611 clear = 0;
612 break;
615 /* no *future* fd should be subjected to this */
616 if (clear)
617 ihP->ih_flags &= ~IH_REALLY_CLOSED;
620 if (fdP->fd_refcnt == 0) {
621 IH_UNLOCK;
622 OS_CLOSE(closeFd);
623 IH_LOCK;
624 fdInUseCount -= 1;
627 /* If this is not the only reference to the Inode then we can decrement
628 * the reference count, otherwise we need to call ih_release. */
629 if (ihP->ih_refcnt > 1) {
630 ihP->ih_refcnt--;
631 IH_UNLOCK;
632 } else {
633 IH_UNLOCK;
634 ih_release(ihP);
637 return 0;
640 /* Enable buffered I/O on a file descriptor */
641 StreamHandle_t *
642 stream_fdopen(FD_t fd)
644 StreamHandle_t *streamP;
646 IH_LOCK;
647 if (streamAvailHead == NULL) {
648 streamHandleAllocateChunk();
650 streamP = streamAvailHead;
651 DLL_DELETE(streamP, streamAvailHead, streamAvailTail, str_next, str_prev);
652 IH_UNLOCK;
653 streamP->str_fd = fd;
654 streamP->str_buflen = 0;
655 streamP->str_bufoff = 0;
656 streamP->str_fdoff = 0;
657 streamP->str_error = 0;
658 streamP->str_eof = 0;
659 streamP->str_direction = STREAM_DIRECTION_NONE;
660 return streamP;
663 /* Open a file for buffered I/O */
664 StreamHandle_t *
665 stream_open(const char *filename, const char *mode)
667 FD_t fd = INVALID_FD;
669 if (strcmp(mode, "r") == 0) {
670 fd = OS_OPEN(filename, O_RDONLY, 0);
671 } else if (strcmp(mode, "r+") == 0) {
672 fd = OS_OPEN(filename, O_RDWR, 0);
673 } else if (strcmp(mode, "w") == 0) {
674 fd = OS_OPEN(filename, O_WRONLY | O_TRUNC | O_CREAT, 0);
675 } else if (strcmp(mode, "w+") == 0) {
676 fd = OS_OPEN(filename, O_RDWR | O_TRUNC | O_CREAT, 0);
677 } else if (strcmp(mode, "a") == 0) {
678 fd = OS_OPEN(filename, O_WRONLY | O_APPEND | O_CREAT, 0);
679 } else if (strcmp(mode, "a+") == 0) {
680 fd = OS_OPEN(filename, O_RDWR | O_APPEND | O_CREAT, 0);
681 } else {
682 osi_Assert(FALSE); /* not implemented */
685 if (fd == INVALID_FD) {
686 return NULL;
688 return stream_fdopen(fd);
691 /* fread for buffered I/O handles */
692 afs_sfsize_t
693 stream_read(void *ptr, afs_fsize_t size, afs_fsize_t nitems,
694 StreamHandle_t * streamP)
696 afs_fsize_t nbytes, bytesRead, bytesToRead;
697 char *p;
699 /* Need to seek before changing direction */
700 if (streamP->str_direction == STREAM_DIRECTION_NONE) {
701 streamP->str_direction = STREAM_DIRECTION_READ;
702 streamP->str_bufoff = 0;
703 streamP->str_buflen = 0;
704 } else {
705 osi_Assert(streamP->str_direction == STREAM_DIRECTION_READ);
708 bytesRead = 0;
709 nbytes = size * nitems;
710 p = (char *)ptr;
711 while (nbytes > 0 && !streamP->str_eof) {
712 if (streamP->str_buflen == 0) {
713 streamP->str_bufoff = 0;
714 streamP->str_buflen =
715 OS_PREAD(streamP->str_fd, streamP->str_buffer,
716 STREAM_HANDLE_BUFSIZE, streamP->str_fdoff);
717 if (streamP->str_buflen < 0) {
718 streamP->str_error = errno;
719 streamP->str_buflen = 0;
720 bytesRead = 0;
721 break;
722 } else if (streamP->str_buflen == 0) {
723 streamP->str_eof = 1;
724 break;
726 streamP->str_fdoff += streamP->str_buflen;
729 bytesToRead = nbytes;
730 if (bytesToRead > streamP->str_buflen) {
731 bytesToRead = streamP->str_buflen;
733 memcpy(p, streamP->str_buffer + streamP->str_bufoff, bytesToRead);
734 p += bytesToRead;
735 streamP->str_bufoff += bytesToRead;
736 streamP->str_buflen -= bytesToRead;
737 bytesRead += bytesToRead;
738 nbytes -= bytesToRead;
741 return (bytesRead / size);
744 /* fwrite for buffered I/O handles */
745 afs_sfsize_t
746 stream_write(void *ptr, afs_fsize_t size, afs_fsize_t nitems,
747 StreamHandle_t * streamP)
749 char *p;
750 afs_sfsize_t rc;
751 afs_fsize_t nbytes, bytesWritten, bytesToWrite;
753 /* Need to seek before changing direction */
754 if (streamP->str_direction == STREAM_DIRECTION_NONE) {
755 streamP->str_direction = STREAM_DIRECTION_WRITE;
756 streamP->str_bufoff = 0;
757 streamP->str_buflen = STREAM_HANDLE_BUFSIZE;
758 } else {
759 osi_Assert(streamP->str_direction == STREAM_DIRECTION_WRITE);
762 nbytes = size * nitems;
763 bytesWritten = 0;
764 p = (char *)ptr;
765 while (nbytes > 0) {
766 if (streamP->str_buflen == 0) {
767 rc = OS_PWRITE(streamP->str_fd, streamP->str_buffer,
768 STREAM_HANDLE_BUFSIZE, streamP->str_fdoff);
769 if (rc < 0) {
770 streamP->str_error = errno;
771 bytesWritten = 0;
772 break;
774 streamP->str_fdoff += rc;
775 streamP->str_bufoff = 0;
776 streamP->str_buflen = STREAM_HANDLE_BUFSIZE;
779 bytesToWrite = nbytes;
780 if (bytesToWrite > streamP->str_buflen) {
781 bytesToWrite = streamP->str_buflen;
783 memcpy(streamP->str_buffer + streamP->str_bufoff, p, bytesToWrite);
784 p += bytesToWrite;
785 streamP->str_bufoff += bytesToWrite;
786 streamP->str_buflen -= bytesToWrite;
787 bytesWritten += bytesToWrite;
788 nbytes -= bytesToWrite;
791 return (bytesWritten / size);
794 /* fseek for buffered I/O handles */
796 stream_aseek(StreamHandle_t * streamP, afs_foff_t offset)
798 ssize_t rc;
799 int retval = 0;
801 if (streamP->str_direction == STREAM_DIRECTION_WRITE
802 && streamP->str_bufoff > 0) {
803 rc = OS_PWRITE(streamP->str_fd, streamP->str_buffer,
804 streamP->str_bufoff, streamP->str_fdoff);
805 if (rc < 0) {
806 streamP->str_error = errno;
807 retval = -1;
810 streamP->str_fdoff = offset;
811 streamP->str_bufoff = 0;
812 streamP->str_buflen = 0;
813 streamP->str_eof = 0;
814 streamP->str_direction = STREAM_DIRECTION_NONE;
815 return retval;
818 /* fflush for buffered I/O handles */
820 stream_flush(StreamHandle_t * streamP)
822 ssize_t rc;
823 int retval = 0;
825 if (streamP->str_direction == STREAM_DIRECTION_WRITE
826 && streamP->str_bufoff > 0) {
827 rc = OS_PWRITE(streamP->str_fd, streamP->str_buffer,
828 streamP->str_bufoff, streamP->str_fdoff);
829 if (rc < 0) {
830 streamP->str_error = errno;
831 retval = -1;
832 } else {
833 streamP->str_fdoff += rc;
835 streamP->str_bufoff = 0;
836 streamP->str_buflen = STREAM_HANDLE_BUFSIZE;
839 return retval;
842 /* Free a buffered I/O handle */
844 stream_close(StreamHandle_t * streamP, int reallyClose)
846 ssize_t rc;
847 int retval = 0;
849 osi_Assert(streamP != NULL);
850 if (streamP->str_direction == STREAM_DIRECTION_WRITE
851 && streamP->str_bufoff > 0) {
852 rc = OS_PWRITE(streamP->str_fd, streamP->str_buffer,
853 streamP->str_bufoff, streamP->str_fdoff);
854 if (rc < 0) {
855 retval = -1;
856 } else {
857 streamP->str_fdoff += rc;
860 if (reallyClose) {
861 rc = OS_CLOSE(streamP->str_fd);
862 if (rc < 0) {
863 retval = -1;
866 streamP->str_fd = INVALID_FD;
868 IH_LOCK;
869 DLL_INSERT_TAIL(streamP, streamAvailHead, streamAvailTail,
870 str_next, str_prev);
871 IH_UNLOCK;
872 return retval;
875 /* Close all unused file descriptors associated with the inode
876 * handle. Called with IH_LOCK held. May drop and reacquire
877 * IH_LOCK. Sets the IH_REALLY_CLOSED flag in the inode handle
878 * if it fails to close all file handles.
880 static int
881 ih_fdclose(IHandle_t * ihP)
883 int closeCount, closedAll;
884 FdHandle_t *fdP, *head, *tail, *next;
886 osi_Assert(ihP->ih_refcnt > 0);
888 closedAll = 1;
889 DLL_INIT_LIST(head, tail);
890 ihP->ih_flags &= ~IH_REALLY_CLOSED;
893 * Remove the file descriptors for this Inode from the LRU queue
894 * and the IHandle queue and put them on a temporary queue so we
895 * can drop the lock before we close the files.
897 for (fdP = ihP->ih_fdhead; fdP != NULL; fdP = next) {
898 next = fdP->fd_ihnext;
899 osi_Assert(fdP->fd_ih == ihP);
900 osi_Assert(fdP->fd_status == FD_HANDLE_OPEN
901 || fdP->fd_status == FD_HANDLE_INUSE
902 || fdP->fd_status == FD_HANDLE_CLOSING);
903 if (fdP->fd_status == FD_HANDLE_OPEN) {
904 /* Note that FdHandle_t's do not count against the parent
905 * IHandle_t ref count when they are FD_HANDLE_OPEN. So, we don't
906 * need to dec the parent IHandle_t ref count for each one we pull
907 * off here. */
908 DLL_DELETE(fdP, ihP->ih_fdhead, ihP->ih_fdtail, fd_ihnext,
909 fd_ihprev);
910 DLL_DELETE(fdP, fdLruHead, fdLruTail, fd_next, fd_prev);
911 DLL_INSERT_TAIL(fdP, head, tail, fd_next, fd_prev);
912 } else {
913 closedAll = 0;
914 fdP->fd_status = FD_HANDLE_CLOSING;
915 ihP->ih_flags |= IH_REALLY_CLOSED;
919 /* If the ihandle reference count is 1, we should have
920 * closed all file descriptors.
922 if (ihP->ih_refcnt == 1 || closedAll) {
923 osi_Assert(closedAll);
924 osi_Assert(!ihP->ih_fdhead);
925 osi_Assert(!ihP->ih_fdtail);
928 if (head == NULL) {
929 return 0; /* No file descriptors closed */
932 IH_UNLOCK;
934 * Close the file descriptors
936 closeCount = 0;
937 for (fdP = head; fdP != NULL; fdP = fdP->fd_next) {
938 OS_CLOSE(fdP->fd_fd);
939 fdP->fd_status = FD_HANDLE_AVAIL;
940 fdP->fd_refcnt = 0;
941 fdP->fd_fd = INVALID_FD;
942 fdP->fd_ih = NULL;
943 closeCount++;
946 IH_LOCK;
947 osi_Assert(fdInUseCount >= closeCount);
948 fdInUseCount -= closeCount;
951 * Append the temporary queue to the list of available descriptors
953 if (fdAvailHead == NULL) {
954 fdAvailHead = head;
955 fdAvailTail = tail;
956 } else {
957 fdAvailTail->fd_next = head;
958 head->fd_prev = fdAvailTail;
959 fdAvailTail = tail;
962 return 0;
965 /* Close all cached file descriptors for this inode. */
967 ih_reallyclose(IHandle_t * ihP)
969 if (!ihP)
970 return 0;
972 IH_LOCK;
973 ihP->ih_refcnt++; /* must not disappear over unlock */
974 if (ihP->ih_synced) {
975 FdHandle_t *fdP;
976 osi_Assert(vol_io_params.sync_behavior != IH_SYNC_ALWAYS);
977 osi_Assert(vol_io_params.sync_behavior != IH_SYNC_NEVER);
978 ihP->ih_synced = 0;
979 IH_UNLOCK;
981 fdP = IH_OPEN(ihP);
982 if (fdP) {
983 OS_SYNC(fdP->fd_fd);
984 FDH_CLOSE(fdP);
987 IH_LOCK;
990 osi_Assert(ihP->ih_refcnt > 0);
992 ih_fdclose(ihP);
994 if (ihP->ih_refcnt > 1) {
995 ihP->ih_refcnt--;
996 IH_UNLOCK;
997 } else {
998 IH_UNLOCK;
999 ih_release(ihP);
1001 return 0;
1004 /* Release an Inode handle. All cached file descriptors for this
1005 * inode are closed when the last reference to this handle is released
1008 ih_release(IHandle_t * ihP)
1010 int ihash;
1012 if (!ihP)
1013 return 0;
1015 IH_LOCK;
1016 osi_Assert(ihP->ih_refcnt > 0);
1018 if (ihP->ih_refcnt > 1) {
1019 ihP->ih_refcnt--;
1020 IH_UNLOCK;
1021 return 0;
1024 ihash = IH_HASH(ihP->ih_dev, ihP->ih_vid, ihP->ih_ino);
1025 DLL_DELETE(ihP, ihashTable[ihash].ihash_head,
1026 ihashTable[ihash].ihash_tail, ih_next, ih_prev);
1028 ih_fdclose(ihP);
1030 ihP->ih_refcnt--;
1032 DLL_INSERT_TAIL(ihP, ihAvailHead, ihAvailTail, ih_next, ih_prev);
1034 IH_UNLOCK;
1035 return 0;
1038 /* Sync an inode to disk if its handle isn't NULL */
1040 ih_condsync(IHandle_t * ihP)
1042 int code;
1043 FdHandle_t *fdP;
1045 if (!ihP)
1046 return 0;
1048 fdP = IH_OPEN(ihP);
1049 if (fdP == NULL)
1050 return -1;
1052 code = FDH_SYNC(fdP);
1053 FDH_CLOSE(fdP);
1055 return code;
1058 void
1059 ih_sync_all(void) {
1061 int ihash;
1063 IH_LOCK;
1064 for (ihash = 0; ihash < I_HANDLE_HASH_SIZE; ihash++) {
1065 IHandle_t *ihP, *ihPnext;
1067 ihP = ihashTable[ihash].ihash_head;
1068 if (ihP)
1069 ihP->ih_refcnt++; /* must not disappear over unlock */
1070 for (; ihP; ihP = ihPnext) {
1072 if (ihP->ih_synced) {
1073 FD_t fd;
1075 ihP->ih_synced = 0;
1076 IH_UNLOCK;
1078 fd = OS_IOPEN(ihP);
1079 if (fd != INVALID_FD) {
1080 OS_SYNC(fd);
1081 OS_CLOSE(fd);
1084 IH_LOCK;
1087 /* when decrementing the refcount, the ihandle might disappear
1088 and we might not even be able to proceed to the next one.
1089 Hence the gymnastics putting a hold on the next one already */
1090 ihPnext = ihP->ih_next;
1091 if (ihPnext) ihPnext->ih_refcnt++;
1093 if (ihP->ih_refcnt > 1) {
1094 ihP->ih_refcnt--;
1095 } else {
1096 IH_UNLOCK;
1097 ih_release(ihP);
1098 IH_LOCK;
1103 IH_UNLOCK;
1106 void *
1107 ih_sync_thread(void *dummy) {
1108 while(1) {
1110 #ifdef AFS_PTHREAD_ENV
1111 sleep(10);
1112 #else /* AFS_PTHREAD_ENV */
1113 IOMGR_Sleep(60);
1114 #endif /* AFS_PTHREAD_ENV */
1116 ih_sync_all();
1118 return NULL;
1122 /*************************************************************************
1123 * OS specific support routines.
1124 *************************************************************************/
1125 #ifndef AFS_NAMEI_ENV
1126 Inode
1127 ih_icreate(IHandle_t * ih, int dev, char *part, Inode nI, int p1, int p2,
1128 int p3, int p4)
1130 Inode ino;
1131 #ifdef AFS_3DISPARES
1132 /* See viceinode.h */
1133 if (p2 == INODESPECIAL) {
1134 int tp = p3;
1135 p3 = p4;
1136 p4 = tp;
1138 #endif
1139 ino = ICREATE(dev, part, nI, p1, p2, p3, p4);
1140 return ino;
1142 #endif /* AFS_NAMEI_ENV */
1144 #if defined(AFS_NT40_ENV) || !defined(AFS_NAMEI_ENV)
1145 /* Unix namei implements its own more efficient IH_CREATE_INIT; this wrapper
1146 * is for everyone else */
1147 IHandle_t *
1148 ih_icreate_init(IHandle_t *lh, int dev, char *part, Inode nearInode,
1149 afs_uint32 p1, afs_uint32 p2, afs_uint32 p3, afs_uint32 p4)
1151 IHandle_t *ihP;
1152 Inode ino = IH_CREATE(lh, dev, part, nearInode, p1, p2, p3, p4);
1153 if (!VALID_INO(ino)) {
1154 return NULL;
1156 IH_INIT(ihP, dev, p1, ino);
1157 return ihP;
1159 #endif
1161 afs_sfsize_t
1162 ih_size(FD_t fd)
1164 #ifdef AFS_NT40_ENV
1165 LARGE_INTEGER size;
1166 if (!GetFileSizeEx(fd, &size))
1167 return -1;
1168 return size.QuadPart;
1169 #else
1170 struct afs_stat status;
1171 if (afs_fstat(fd, &status) < 0)
1172 return -1;
1173 return status.st_size;
1174 #endif
1177 #ifndef HAVE_PIO
1178 ssize_t
1179 ih_pread(int fd, void * buf, size_t count, afs_foff_t offset)
1181 afs_foff_t code;
1182 code = OS_SEEK(fd, offset, 0);
1183 if (code < 0)
1184 return code;
1185 return OS_READ(fd, buf, count);
1188 ssize_t
1189 ih_pwrite(int fd, const void * buf, size_t count, afs_foff_t offset)
1191 afs_foff_t code;
1192 code = OS_SEEK(fd, offset, 0);
1193 if (code < 0)
1194 return code;
1195 return OS_WRITE(fd, buf, count);
1197 #endif /* !HAVE_PIO */
1199 #ifndef AFS_NT40_ENV
1201 ih_isunlinked(int fd)
1203 struct afs_stat status;
1204 if (afs_fstat(fd, &status) < 0) {
1205 return -1;
1207 if (status.st_nlink < 1) {
1208 return 1;
1210 return 0;
1212 #endif /* !AFS_NT40_ENV */
1215 ih_fdsync(FdHandle_t *fdP)
1217 switch (vol_io_params.sync_behavior) {
1218 case IH_SYNC_ALWAYS:
1219 return OS_SYNC(fdP->fd_fd);
1220 case IH_SYNC_DELAYED:
1221 case IH_SYNC_ONCLOSE:
1222 if (fdP->fd_ih) {
1223 fdP->fd_ih->ih_synced = 1;
1224 return 0;
1226 return 1;
1227 case IH_SYNC_NEVER:
1228 return 0;
1229 default:
1230 osi_Assert(0);