/*===========================================================================* * do_read * *===========================================================================*/ ssize_t do_read(ino_t ino_nr, struct fsdriver_data *data, size_t count, off_t pos, int call) { /* Read data from a file. */ struct inode *ino; size_t size, off; char *ptr; int r, chunk; if ((ino = find_inode(ino_nr)) == NULL) return EINVAL; if (IS_DIR(ino)) return EISDIR; if ((r = get_handle(ino)) != OK) return r; assert(count > 0); /* Use the buffer from below to eliminate extra copying. */ size = sffs_table->t_readbuf(&ptr); off = 0; while (count > 0) { chunk = MIN(count, size); if ((r = sffs_table->t_read(ino->i_file, ptr, chunk, pos)) <= 0) break; chunk = r; if ((r = fsdriver_copyout(data, off, ptr, chunk)) != OK) break; count -= chunk; off += chunk; pos += chunk; } if (r < 0) return r; return off; }
/*===========================================================================* * fs_read * *===========================================================================*/ ssize_t fs_read(ino_t ino_nr, struct fsdriver_data *data, size_t bytes, off_t pos, int __unused call) { /* Read from a file. */ struct inode *node; size_t len; char *ptr; int r; /* Try to get inode by its inode number. */ if ((node = find_inode(ino_nr)) == NULL) return EINVAL; /* Check whether the node is a regular file. */ if (!S_ISREG(node->i_stat.mode)) return EINVAL; /* Call the read hook, if any. */ if (!is_inode_deleted(node) && vtreefs_hooks->read_hook != NULL) { len = bytes; /* On success, the read hook provides us with a pointer to the * resulting data. This avoids copying overhead. */ r = vtreefs_hooks->read_hook(node, pos, &ptr, &len, get_inode_cbdata(node)); assert(len <= bytes); /* Copy the resulting data to user space. */ if (r == OK && len > 0) r = fsdriver_copyout(data, 0, ptr, len); } else { /* Feign an empty file. */ r = OK; len = 0; } return (r != OK) ? r : len; }
/*===========================================================================* * fs_rdlink * *===========================================================================*/ ssize_t fs_rdlink(ino_t ino_nr, struct fsdriver_data *data, size_t bytes) { struct buf *bp = NULL; /* buffer containing link text */ char* link_text; /* either bp->b_data or rip->i_block */ register struct inode *rip; /* target inode */ register int r; /* return value */ /* Temporarily open the file. */ if( (rip = get_inode(fs_dev, ino_nr)) == NULL) return(EINVAL); if (rip->i_size >= MAX_FAST_SYMLINK_LENGTH) { /* normal symlink */ if(!(bp = get_block_map(rip, 0))) { r = EIO; } else { link_text = b_data(bp); r = OK; } } else { /* fast symlink, stored in inode */ link_text = (char*) rip->i_block; r = OK; } if (r == OK) { /* Passed all checks */ if (bytes > rip->i_size) bytes = rip->i_size; r = fsdriver_copyout(data, 0, link_text, bytes); put_block(bp, DIRECTORY_BLOCK); if (r == OK) r = bytes; } put_inode(rip); return(r); }
/* * Perform block I/O, on "dev", starting from offset "pos", for a total of * "bytes" bytes. Reading, writing, and peeking are highly similar, and thus, * this function implements all of them. The "call" parameter indicates the * call type (one of FSC_READ, FSC_WRITE, FSC_PEEK). For read and write calls, * "data" will identify the user buffer to use; for peek calls, "data" is set * to NULL. In all cases, this function returns the number of bytes * successfully transferred, 0 on end-of-file conditions, and a negative error * code if no bytes could be transferred due to an error. Dirty data is not * flushed immediately, and thus, a successful write only indicates that the * data have been taken in by the cache (for immediate I/O, a character device * would have to be used, but MINIX3 no longer supports this), which may be * follwed later by silent failures, including undetected end-of-file cases. * In particular, write requests may or may not return 0 (EOF) immediately when * writing at or beyond the block device's size. i Since block I/O takes place * at block granularity, block-unaligned writes have to read a block from disk * before updating it, and that is the only possible source of actual I/O * errors for write calls. * TODO: reconsider the buffering-only approach, or see if we can at least * somehow throw accurate EOF errors without reading in each block first. */ ssize_t lmfs_bio(dev_t dev, struct fsdriver_data * data, size_t bytes, off_t pos, int call) { block_t block, blocks_left; size_t block_size, off, block_off, chunk; struct buf *bp; int r, write, how; if (dev == NO_DEV) return EINVAL; block_size = lmfs_fs_block_size(); write = (call == FSC_WRITE); assert(block_size > 0); /* FIXME: block_t is 32-bit, so we have to impose a limit here. */ if (pos < 0 || pos / block_size > UINT32_MAX || bytes > SSIZE_MAX) return EINVAL; off = 0; block = pos / block_size; block_off = (size_t)(pos % block_size); blocks_left = howmany(block_off + bytes, block_size); lmfs_reset_rdwt_err(); r = OK; for (off = 0; off < bytes; off += chunk) { chunk = block_size - block_off; if (chunk > bytes - off) chunk = bytes - off; /* * For read requests, help the block driver form larger I/O * requests. */ if (!write) block_prefetch(dev, block, blocks_left); /* * Do not read the block from disk if we will end up * overwriting all of its contents. */ how = (write && chunk == block_size) ? NO_READ : NORMAL; bp = lmfs_get_block(dev, block, how); assert(bp); r = lmfs_rdwt_err(); if (r == OK && data != NULL) { assert(lmfs_dev(bp) != NO_DEV); if (write) { r = fsdriver_copyin(data, off, (char *)bp->data + block_off, chunk); /* * Mark the block as dirty even if the copy * failed, since the copy may in fact have * succeeded partially. This is an interface * issue that should be resolved at some point, * but for now we do not want the cache to be * desynchronized from the disk contents. */ lmfs_markdirty(bp); } else r = fsdriver_copyout(data, off, (char *)bp->data + block_off, chunk); } lmfs_put_block(bp, FULL_DATA_BLOCK); if (r != OK) break; block++; block_off = 0; blocks_left--; } /* * If we were not able to do any I/O, return the error (or EOF, even * for writes). Otherwise, return how many bytes we did manage to * transfer. */ if (r != OK && off == 0) return (r == END_OF_FILE) ? 0 : r; return off; }
ssize_t fs_read(ino_t ino_nr, struct fsdriver_data *data, size_t bytes, off_t pos, int __unused call) { size_t off, chunk, block_size, cum_io; off_t f_size; struct inode *i_node; struct buf *bp; int r; /* Try to get inode according to its index. */ if ((i_node = find_inode(ino_nr)) == NULL) return EINVAL; /* No inode found. */ f_size = i_node->i_stat.st_size; if (pos >= f_size) return 0; /* EOF */ /* Limit the request to the remainder of the file size. */ if ((off_t)bytes > f_size - pos) bytes = (size_t)(f_size - pos); block_size = v_pri.logical_block_size_l; cum_io = 0; lmfs_reset_rdwt_err(); r = OK; /* Split the transfer into chunks that don't span two blocks. */ while (bytes > 0) { off = pos % block_size; chunk = block_size - off; if (chunk > bytes) chunk = bytes; /* Read 'chunk' bytes. */ bp = read_extent_block(i_node->extent, pos / block_size); if (bp == NULL) panic("bp not valid in rw_chunk; this can't happen"); r = fsdriver_copyout(data, cum_io, b_data(bp)+off, chunk); lmfs_put_block(bp, FULL_DATA_BLOCK); if (r != OK) break; /* EOF reached. */ if (lmfs_rdwt_err() < 0) break; /* Update counters and pointers. */ bytes -= chunk; /* Bytes yet to be read. */ cum_io += chunk; /* Bytes read so far. */ pos += chunk; /* Position within the file. */ } if (lmfs_rdwt_err() != OK) r = lmfs_rdwt_err(); /* Check for disk error. */ if (lmfs_rdwt_err() == END_OF_FILE) r = OK; return (r == OK) ? cum_io : r; }