/* ----------------------------------------------------------------------- * * * Copyright 2010 Intel Corporation; author: H. Peter Anvin * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom * the Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall * be included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. * * ----------------------------------------------------------------------- */ /* * getfssec.c * * Generic getfssec implementation for disk-based filesystems, which * support the next_extent() method. * * The expected semantics of next_extent are as follows: * * The second argument will contain the initial sector number to be * mapped. The routine is expected to populate * inode->next_extent.pstart and inode->next_extent.len (the caller * will store the initial sector number into inode->next_extent.lstart * on return.) * * If inode->next_extent.pstart is EXTENT_ZERO, then no disk I/O is * performed, and the data in the extent is all zero. * * If inode->next_extent.len != 0 on entry then the routine is allowed * to assume inode->next_extent contains valid data from the previous * usage, which can be used for optimization purposes. * * If the filesystem can map the entire file as a single extent * (e.g. iso9660), then the filesystem can simply insert the extent * information into inode->next_extent at searchdir/iget time, and point * next_extent() to the generic function no_next_extent(). * * Note: the filesystem driver is not required to do extent coalescing, * if that is difficult to do; this routine will perform extent lookahead * and coalescing. However, if the filesystem can do extent coalescing * very cheaply by using filesystem-specific knowledge, then that is * preferred (e.g. FAT). */ #include #include #include "fs.h" static inline sector_t next_psector(sector_t psector, uint32_t skip) { if (EXTENT_SPECIAL(psector)) return psector; else return psector + skip; } static inline sector_t next_pstart(const struct extent *e) { return next_psector(e->pstart, e->len); } static void get_next_extent(struct inode *inode) { /* The logical start address that we care about... */ uint32_t lstart = inode->this_extent.lstart + inode->this_extent.len; if (inode->fs->fs_ops->next_extent(inode, lstart)) inode->next_extent.len = 0; /* ERROR */ inode->next_extent.lstart = lstart; dprintf("Extent: inode %p @ %u start %llu len %u\n", inode, inode->next_extent.lstart, inode->next_extent.pstart, inode->next_extent.len); } uint32_t generic_getfssec(struct file *file, char *buf, int sectors, bool *have_more) { struct inode *inode = file->inode; struct fs_info *fs = file->fs; struct disk *disk = fs->fs_dev->disk; uint32_t bytes_read = 0; uint32_t bytes_left = inode->size - file->offset; uint32_t sectors_left = (bytes_left + SECTOR_SIZE(fs) - 1) >> SECTOR_SHIFT(fs); uint32_t lsector; if (sectors > sectors_left) sectors = sectors_left; if (!sectors) return 0; lsector = file->offset >> SECTOR_SHIFT(fs); dprintf("Offset: %u lsector: %u\n", file->offset, lsector); if (lsector < inode->this_extent.lstart || lsector >= inode->this_extent.lstart + inode->this_extent.len) { /* inode->this_extent unusable, maybe next_extent is... */ inode->this_extent = inode->next_extent; } if (lsector < inode->this_extent.lstart || lsector >= inode->this_extent.lstart + inode->this_extent.len) { /* Still nothing useful... */ inode->this_extent.lstart = lsector; inode->this_extent.len = 0; } else { /* We have some usable information */ uint32_t delta = lsector - inode->this_extent.lstart; inode->this_extent.lstart = lsector; inode->this_extent.len -= delta; inode->this_extent.pstart = next_psector(inode->this_extent.pstart, delta); } dprintf("this_extent: lstart %u pstart %llu len %u\n", inode->this_extent.lstart, inode->this_extent.pstart, inode->this_extent.len); while (sectors) { uint32_t chunk; size_t len; while (sectors > inode->this_extent.len) { if (!inode->next_extent.len || inode->next_extent.lstart != inode->this_extent.lstart + inode->this_extent.len) get_next_extent(inode); if (!inode->this_extent.len) { /* Doesn't matter if it's contiguous... */ inode->this_extent = inode->next_extent; if (!inode->next_extent.len) { sectors = 0; /* Failed to get anything... we're dead */ break; } } else if (inode->next_extent.len && inode->next_extent.pstart == next_pstart(&inode->this_extent)) { /* Coalesce extents and loop */ inode->this_extent.len += inode->next_extent.len; } else { /* Discontiguous extents */ break; } } dprintf("this_extent: lstart %u pstart %llu len %u\n", inode->this_extent.lstart, inode->this_extent.pstart, inode->this_extent.len); chunk = min(sectors, inode->this_extent.len); len = chunk << SECTOR_SHIFT(fs); dprintf(" I/O: inode %p @ %u start %llu len %u\n", inode, inode->this_extent.lstart, inode->this_extent.pstart, chunk); if (inode->this_extent.pstart == EXTENT_ZERO) { memset(buf, 0, len); } else { disk->rdwr_sectors(disk, buf, inode->this_extent.pstart, chunk, 0); inode->this_extent.pstart += chunk; } buf += len; sectors -= chunk; bytes_read += len; inode->this_extent.lstart += chunk; inode->this_extent.len -= chunk; } bytes_read = min(bytes_read, bytes_left); file->offset += bytes_read; if (have_more) *have_more = bytes_read < bytes_left; return bytes_read; }