diff options
author | H. Peter Anvin <hpa@zytor.com> | 2010-02-26 12:11:18 -0800 |
---|---|---|
committer | H. Peter Anvin <hpa@zytor.com> | 2010-02-26 12:11:18 -0800 |
commit | f3e92c92c0a9e16bec3aa3b1c546a01054056861 (patch) | |
tree | 495bc168c1b7242f916dbd2173a4b6ee36faff09 | |
parent | df88057cf111bfb00f27da51b53591742239a974 (diff) | |
download | syslinux-f3e92c92c0a9e16bec3aa3b1c546a01054056861.tar.gz |
fs: add generic getfssec
Add a generic getfssec method which operate on cached extents. This
should avoid the need to each filesystem to implement its own getfssec
loop.
Signed-off-by: H. Peter Anvin <hpa@zytor.com>
-rw-r--r-- | core/fs/getfssec.c | 156 | ||||
-rw-r--r-- | core/include/fs.h | 27 |
2 files changed, 181 insertions, 2 deletions
diff --git a/core/fs/getfssec.c b/core/fs/getfssec.c new file mode 100644 index 00000000..e8ae62f4 --- /dev/null +++ b/core/fs/getfssec.c @@ -0,0 +1,156 @@ +/* ----------------------------------------------------------------------- * + * + * 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 populate_next_extent method. + * + * The expected semantics of populate_next_extent are as follows: + * + * inode->next_extent.lstart will contain the initial sector number to + * be mapped. The routine is expected to populate inode->next_extent.pstart + * and inode->next_extent.len. If inode->prev_extent.pstart != EXTENT_VOID + * then the routine is allowed to assume inode->prev_extent contains valid + * data. + * + * 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->prev_extent at searchdir/iget time, and leave + * populate_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 <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); +} + +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; + + lsector = file->offset >> SECTOR_SHIFT(fs); + + if (lsector < inode->this_extent.lstart || + lsector >= inode->this_extent.lstart + inode->this_extent.len) { + /* inode->this_extent unusable, maybe prev_extent is... */ + inode->this_extent = inode->prev_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); + } + + while (sectors) { + uint32_t chunk; + size_t len; + + if (!inode->this_extent.len && inode->next_extent.len && + inode->this_extent.lstart == inode->next_extent.lstart) + inode->this_extent = inode->next_extent; + + while (sectors > inode->this_extent.len) { + /* Whatever we had before... */ + inode->prev_extent = inode->next_extent; + + /* Dummy information to make failure returns easier */ + inode->next_extent.len = 0; + + fs->fs_ops->populate_next_extent(inode); + + 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; + } + } + + chunk = min(sectors, inode->this_extent.len); + len = chunk << SECTOR_SHIFT(fs); + + 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; +} diff --git a/core/include/fs.h b/core/include/fs.h index 25219d96..1870de79 100644 --- a/core/include/fs.h +++ b/core/include/fs.h @@ -69,10 +69,28 @@ struct fs_ops { /* the _dir_ stuff */ struct dirent * (*readdir)(struct file *); + + void (*populate_next_extent)(struct inode *); }; enum inode_mode {I_FILE, I_DIR, I_SYMLINK}; +/* + * Extent structure: contains the mapping of some chunk of a file + * that is contiguous on disk. + */ +struct extent { + sector_t pstart; /* Physical start sector */ + uint32_t lstart; /* Logical start sector */ + uint32_t len; /* Number of contiguous sectors */ +}; + +/* Special sector numbers used for struct extent.pstart */ +#define EXTENT_ZERO ((sector_t)-1) /* All-zero extent */ +#define EXTENT_VOID ((sector_t)-2) /* Invalid information */ + +#define EXTENT_SPECIAL(x) ((x) >= EXTENT_VOID) + /* * The inode structure, including the detail file information */ @@ -81,14 +99,15 @@ struct inode { int refcnt; int mode; /* FILE , DIR or SYMLINK */ uint32_t size; + uint32_t blocks; /* How many blocks the file take */ uint32_t ino; /* Inode number */ uint32_t atime; /* Access time */ uint32_t mtime; /* Modify time */ uint32_t ctime; /* Create time */ uint32_t dtime; /* Delete time */ - int blocks; /* How many blocks the file take */ uint32_t flags; uint32_t file_acl; + struct extent this_extent, prev_extent, next_extent; char pvt[0]; /* Private filesystem data */ }; @@ -104,7 +123,7 @@ struct file { uint32_t offset; /* for next read */ }; - /* For the old searhdir method */ + /* For the old searchdir method */ struct { struct open_file_t *open_file;/* The fs-specific open file struct */ }; @@ -208,4 +227,8 @@ int generic_load_config(void); /* close.c */ void generic_close_file(struct file *file); +/* getfssec.c */ +uint32_t generic_getfssec(struct file *file, char *buf, + int sectors, bool *have_more); + #endif /* FS_H */ |