summaryrefslogtreecommitdiffstats
path: root/contrib/syslinux-4.02/core/fs/getfssec.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/syslinux-4.02/core/fs/getfssec.c')
-rw-r--r--contrib/syslinux-4.02/core/fs/getfssec.c194
1 files changed, 194 insertions, 0 deletions
diff --git a/contrib/syslinux-4.02/core/fs/getfssec.c b/contrib/syslinux-4.02/core/fs/getfssec.c
new file mode 100644
index 0000000..e099b64
--- /dev/null
+++ b/contrib/syslinux-4.02/core/fs/getfssec.c
@@ -0,0 +1,194 @@
+/* ----------------------------------------------------------------------- *
+ *
+ * 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;
+}