/* Free a buffered I/O handle */ int stream_close(StreamHandle_t * streamP, int reallyClose) { ssize_t rc; int retval = 0; osi_Assert(streamP != NULL); if (streamP->str_direction == STREAM_DIRECTION_WRITE && streamP->str_bufoff > 0) { rc = OS_PWRITE(streamP->str_fd, streamP->str_buffer, streamP->str_bufoff, streamP->str_fdoff); if (rc < 0) { retval = -1; } else { streamP->str_fdoff += rc; } } if (reallyClose) { rc = OS_CLOSE(streamP->str_fd); if (rc < 0) { retval = -1; } } streamP->str_fd = INVALID_FD; IH_LOCK; DLL_INSERT_TAIL(streamP, streamAvailHead, streamAvailTail, str_next, str_prev); IH_UNLOCK; return retval; }
/* * Actually close the file descriptor handle and return it to * the free list. */ int fd_reallyclose(FdHandle_t * fdP) { FD_t closeFd; IHandle_t *ihP; if (!fdP) return 0; IH_LOCK; osi_Assert(ih_Inited); osi_Assert(fdInUseCount > 0); osi_Assert(fdP->fd_status == FD_HANDLE_INUSE || fdP->fd_status == FD_HANDLE_CLOSING); ihP = fdP->fd_ih; closeFd = fdP->fd_fd; fdP->fd_refcnt--; if (fdP->fd_refcnt == 0) { DLL_DELETE(fdP, ihP->ih_fdhead, ihP->ih_fdtail, fd_ihnext, fd_ihprev); DLL_INSERT_TAIL(fdP, fdAvailHead, fdAvailTail, fd_next, fd_prev); fdP->fd_status = FD_HANDLE_AVAIL; fdP->fd_refcnt = 0; fdP->fd_ih = NULL; fdP->fd_fd = INVALID_FD; } /* All the file descriptor handles have been closed; reset * the IH_REALLY_CLOSED flag indicating that ih_reallyclose * has completed its job. */ if (!ihP->ih_fdhead) { ihP->ih_flags &= ~IH_REALLY_CLOSED; } if (fdP->fd_refcnt == 0) { IH_UNLOCK; OS_CLOSE(closeFd); IH_LOCK; fdInUseCount -= 1; } /* If this is not the only reference to the Inode then we can decrement * the reference count, otherwise we need to call ih_release. */ if (ihP->ih_refcnt > 1) ihP->ih_refcnt--; else _ih_release_r(ihP); IH_UNLOCK; return 0; }
/* Close all unused file descriptors associated with the inode * handle. Called with IH_LOCK held. May drop and reacquire * IH_LOCK. Sets the IH_REALLY_CLOSED flag in the inode handle * if it fails to close all file handles. */ static int ih_fdclose(IHandle_t * ihP) { int closeCount, closedAll; FdHandle_t *fdP, *head, *tail, *next; osi_Assert(ihP->ih_refcnt > 0); closedAll = 1; DLL_INIT_LIST(head, tail); ihP->ih_flags &= ~IH_REALLY_CLOSED; /* * Remove the file descriptors for this Inode from the LRU queue * and the IHandle queue and put them on a temporary queue so we * can drop the lock before we close the files. */ for (fdP = ihP->ih_fdhead; fdP != NULL; fdP = next) { next = fdP->fd_ihnext; osi_Assert(fdP->fd_ih == ihP); osi_Assert(fdP->fd_status == FD_HANDLE_OPEN || fdP->fd_status == FD_HANDLE_INUSE || fdP->fd_status == FD_HANDLE_CLOSING); if (fdP->fd_status == FD_HANDLE_OPEN) { /* Note that FdHandle_t's do not count against the parent * IHandle_t ref count when they are FD_HANDLE_OPEN. So, we don't * need to dec the parent IHandle_t ref count for each one we pull * off here. */ DLL_DELETE(fdP, ihP->ih_fdhead, ihP->ih_fdtail, fd_ihnext, fd_ihprev); DLL_DELETE(fdP, fdLruHead, fdLruTail, fd_next, fd_prev); DLL_INSERT_TAIL(fdP, head, tail, fd_next, fd_prev); } else { closedAll = 0; fdP->fd_status = FD_HANDLE_CLOSING; ihP->ih_flags |= IH_REALLY_CLOSED; } } /* If the ihandle reference count is 1, we should have * closed all file descriptors. */ if (ihP->ih_refcnt == 1 || closedAll) { osi_Assert(closedAll); osi_Assert(!ihP->ih_fdhead); osi_Assert(!ihP->ih_fdtail); } if (head == NULL) { return 0; /* No file descriptors closed */ } IH_UNLOCK; /* * Close the file descriptors */ closeCount = 0; for (fdP = head; fdP != NULL; fdP = fdP->fd_next) { OS_CLOSE(fdP->fd_fd); fdP->fd_status = FD_HANDLE_AVAIL; fdP->fd_refcnt = 0; fdP->fd_fd = INVALID_FD; fdP->fd_ih = NULL; closeCount++; } IH_LOCK; osi_Assert(fdInUseCount >= closeCount); fdInUseCount -= closeCount; /* * Append the temporary queue to the list of available descriptors */ if (fdAvailHead == NULL) { fdAvailHead = head; fdAvailTail = tail; } else { fdAvailTail->fd_next = head; head->fd_prev = fdAvailTail; fdAvailTail = tail; } return 0; }
/* * Get a file descriptor handle given an Inode handle */ FdHandle_t * ih_open(IHandle_t * ihP) { FdHandle_t *fdP; FD_t fd; FD_t closeFd; if (!ihP) /* XXX should log here in the fileserver */ return NULL; IH_LOCK; /* Do we already have an open file handle for this Inode? */ for (fdP = ihP->ih_fdtail; fdP != NULL; fdP = fdP->fd_ihprev) { if (fdP->fd_status == FD_HANDLE_CLOSING) { /* The handle was open when an IH_REALLYCLOSE was issued, so we * cannot reuse it; it will be closed soon. */ continue; } #ifndef HAVE_PIO /* * If we don't have positional i/o, don't try to share fds, since * we can't do so in a threadsafe way. */ if (fdP->fd_status == FD_HANDLE_INUSE) { continue; } osi_Assert(fdP->fd_status == FD_HANDLE_OPEN); #else /* HAVE_PIO */ osi_Assert(fdP->fd_status != FD_HANDLE_AVAIL); #endif /* HAVE_PIO */ fdP->fd_refcnt++; if (fdP->fd_status == FD_HANDLE_OPEN) { fdP->fd_status = FD_HANDLE_INUSE; DLL_DELETE(fdP, fdLruHead, fdLruTail, fd_next, fd_prev); } ihP->ih_refcnt++; IH_UNLOCK; return fdP; } /* * Try to open the Inode, return NULL on error. */ fdInUseCount += 1; IH_UNLOCK; ih_open_retry: fd = OS_IOPEN(ihP); IH_LOCK; if (fd == INVALID_FD && (errno != EMFILE || fdLruHead == NULL) ) { fdInUseCount -= 1; IH_UNLOCK; return NULL; } /* fdCacheSize limits the size of the descriptor cache, but * we permit the number of open files to exceed fdCacheSize. * We only recycle open file descriptors when the number * of open files reaches the size of the cache */ if ((fdInUseCount > fdCacheSize || fd == INVALID_FD) && fdLruHead != NULL) { fdP = fdLruHead; osi_Assert(fdP->fd_status == FD_HANDLE_OPEN); DLL_DELETE(fdP, fdLruHead, fdLruTail, fd_next, fd_prev); DLL_DELETE(fdP, fdP->fd_ih->ih_fdhead, fdP->fd_ih->ih_fdtail, fd_ihnext, fd_ihprev); closeFd = fdP->fd_fd; if (fd == INVALID_FD) { fdCacheSize--; /* reduce in order to not run into here too often */ DLL_INSERT_TAIL(fdP, fdAvailHead, fdAvailTail, fd_next, fd_prev); fdP->fd_status = FD_HANDLE_AVAIL; fdP->fd_ih = NULL; fdP->fd_fd = INVALID_FD; IH_UNLOCK; OS_CLOSE(closeFd); goto ih_open_retry; } } else { if (fdAvailHead == NULL) { fdHandleAllocateChunk(); } fdP = fdAvailHead; osi_Assert(fdP->fd_status == FD_HANDLE_AVAIL); DLL_DELETE(fdP, fdAvailHead, fdAvailTail, fd_next, fd_prev); closeFd = INVALID_FD; } fdP->fd_status = FD_HANDLE_INUSE; fdP->fd_fd = fd; fdP->fd_ih = ihP; fdP->fd_refcnt++; ihP->ih_refcnt++; /* Add this handle to the Inode's list of open descriptors */ DLL_INSERT_TAIL(fdP, ihP->ih_fdhead, ihP->ih_fdtail, fd_ihnext, fd_ihprev); if (closeFd != INVALID_FD) { IH_UNLOCK; OS_CLOSE(closeFd); IH_LOCK; fdInUseCount -= 1; } IH_UNLOCK; return fdP; }
/** * handle a single vol header as part of VWalkVolumeHeaders. * * @param[in] dp disk partition * @param[in] volfunc function to call when a vol header is successfully read * @param[in] name full path name to the .vol header * @param[out] hdr header data read in from the .vol header * @param[in] locked 1 if the partition headers are locked, 0 otherwise * @param[in] rock the rock to pass to volfunc * * @return operation status * @retval 0 success * @retval -1 fatal error, stop scanning * @retval 1 failed to read header * @retval 2 volfunc callback indicated error after header read */ static int _VHandleVolumeHeader(struct DiskPartition64 *dp, VWalkVolFunc volfunc, const char *name, struct VolumeDiskHeader *hdr, int locked, void *rock) { int error = 0; FD_t fd; if ((fd = OS_OPEN(name, O_RDONLY, 0)) == INVALID_FD || OS_READ(fd, hdr, sizeof(*hdr)) != sizeof(*hdr) || hdr->stamp.magic != VOLUMEHEADERMAGIC) { error = 1; } if (fd != INVALID_FD) { OS_CLOSE(fd); } #ifdef AFSFS_DEMAND_ATTACH_FS if (locked) { VPartHeaderUnlock(dp); } #endif /* AFS_DEMAND_ATTACH_FS */ if (!error && volfunc) { /* the volume header seems fine; call the caller-supplied * 'we-found-a-volume-header' function */ int last = 1; #ifdef AFS_DEMAND_ATTACH_FS if (!locked) { last = 0; } #endif /* AFS_DEMAND_ATTACH_FS */ error = (*volfunc) (dp, name, hdr, last, rock); if (error < 0) { return -1; } if (error) { error = 2; } } #ifdef AFS_DEMAND_ATTACH_FS if (error && !locked) { int code; /* retry reading the volume header under the partition * header lock, just to be safe and ensure we're not * racing something rewriting the vol header */ code = VPartHeaderLock(dp, WRITE_LOCK); if (code) { Log("Error acquiring partition write lock when " "looking at header %s\n", name); return -1; } return _VHandleVolumeHeader(dp, volfunc, name, hdr, 1, rock); } #endif /* AFS_DEMAND_ATTACH_FS */ return error; }
/* * Get a file descriptor handle given an Inode handle */ FdHandle_t * ih_open(IHandle_t * ihP) { FdHandle_t *fdP; FD_t fd; FD_t closeFd; if (!ihP) /* XXX should log here in the fileserver */ return NULL; IH_LOCK; /* Do we already have an open file handle for this Inode? */ for (fdP = ihP->ih_fdtail; fdP != NULL; fdP = fdP->fd_ihprev) { if (fdP->fd_status != FD_HANDLE_INUSE) { assert(fdP->fd_status == FD_HANDLE_OPEN); fdP->fd_status = FD_HANDLE_INUSE; DLL_DELETE(fdP, fdLruHead, fdLruTail, fd_next, fd_prev); ihP->ih_refcnt++; IH_UNLOCK; (void)FDH_SEEK(fdP, 0, SEEK_SET); return fdP; } } /* * Try to open the Inode, return NULL on error. */ fdInUseCount += 1; IH_UNLOCK; ih_open_retry: fd = OS_IOPEN(ihP); IH_LOCK; if (fd == INVALID_FD && (errno != EMFILE || fdLruHead == NULL) ) { fdInUseCount -= 1; IH_UNLOCK; return NULL; } /* fdCacheSize limits the size of the descriptor cache, but * we permit the number of open files to exceed fdCacheSize. * We only recycle open file descriptors when the number * of open files reaches the size of the cache */ if ((fdInUseCount > fdCacheSize || fd == INVALID_FD) && fdLruHead != NULL) { fdP = fdLruHead; assert(fdP->fd_status == FD_HANDLE_OPEN); DLL_DELETE(fdP, fdLruHead, fdLruTail, fd_next, fd_prev); DLL_DELETE(fdP, fdP->fd_ih->ih_fdhead, fdP->fd_ih->ih_fdtail, fd_ihnext, fd_ihprev); closeFd = fdP->fd_fd; if (fd == INVALID_FD) { fdCacheSize--; /* reduce in order to not run into here too often */ DLL_INSERT_TAIL(fdP, fdAvailHead, fdAvailTail, fd_next, fd_prev); fdP->fd_status = FD_HANDLE_AVAIL; fdP->fd_ih = NULL; fdP->fd_fd = INVALID_FD; IH_UNLOCK; OS_CLOSE(closeFd); goto ih_open_retry; } } else { if (fdAvailHead == NULL) { fdHandleAllocateChunk(); } fdP = fdAvailHead; assert(fdP->fd_status == FD_HANDLE_AVAIL); DLL_DELETE(fdP, fdAvailHead, fdAvailTail, fd_next, fd_prev); closeFd = INVALID_FD; } fdP->fd_status = FD_HANDLE_INUSE; fdP->fd_fd = fd; fdP->fd_ih = ihP; ihP->ih_refcnt++; /* Add this handle to the Inode's list of open descriptors */ DLL_INSERT_TAIL(fdP, ihP->ih_fdhead, ihP->ih_fdtail, fd_ihnext, fd_ihprev); if (closeFd != INVALID_FD) { IH_UNLOCK; OS_CLOSE(closeFd); IH_LOCK; fdInUseCount -= 1; } IH_UNLOCK; return fdP; }