195 lines
6.1 KiB
C
195 lines
6.1 KiB
C
/* ----------------------------------------------------------------------- *
|
|
*
|
|
* 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.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 leave
|
|
* next_extent() as NULL.
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
#include <dprintf.h>
|
|
#include <minmax.h>
|
|
#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;
|
|
}
|