extlinux: add --device option to override device detect
[syslinux/sherbszt.git] / core / fs / getfssec.c
blobe099b64e5c00cf9c29c28807636a15f3d67d70ea
1 /* ----------------------------------------------------------------------- *
3 * Copyright 2010 Intel Corporation; author: H. Peter Anvin
5 * Permission is hereby granted, free of charge, to any person
6 * obtaining a copy of this software and associated documentation
7 * files (the "Software"), to deal in the Software without
8 * restriction, including without limitation the rights to use,
9 * copy, modify, merge, publish, distribute, sublicense, and/or
10 * sell copies of the Software, and to permit persons to whom
11 * the Software is furnished to do so, subject to the following
12 * conditions:
14 * The above copyright notice and this permission notice shall
15 * be included in all copies or substantial portions of the Software.
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
19 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
21 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
22 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
24 * OTHER DEALINGS IN THE SOFTWARE.
26 * ----------------------------------------------------------------------- */
29 * getfssec.c
31 * Generic getfssec implementation for disk-based filesystems, which
32 * support the next_extent() method.
34 * The expected semantics of next_extent are as follows:
36 * The second argument will contain the initial sector number to be
37 * mapped. The routine is expected to populate
38 * inode->next_extent.pstart and inode->next_extent.len (the caller
39 * will store the initial sector number into inode->next_extent.lstart
40 * on return.)
42 * If inode->next_extent.len != 0 on entry then the routine is allowed
43 * to assume inode->next_extent contains valid data from the previous
44 * usage, which can be used for optimization purposes.
46 * If the filesystem can map the entire file as a single extent
47 * (e.g. iso9660), then the filesystem can simply insert the extent
48 * information into inode->next_extent at searchdir/iget time, and leave
49 * next_extent() as NULL.
51 * Note: the filesystem driver is not required to do extent coalescing,
52 * if that is difficult to do; this routine will perform extent lookahead
53 * and coalescing.
56 #include <dprintf.h>
57 #include <minmax.h>
58 #include "fs.h"
60 static inline sector_t next_psector(sector_t psector, uint32_t skip)
62 if (EXTENT_SPECIAL(psector))
63 return psector;
64 else
65 return psector + skip;
68 static inline sector_t next_pstart(const struct extent *e)
70 return next_psector(e->pstart, e->len);
74 static void get_next_extent(struct inode *inode)
76 /* The logical start address that we care about... */
77 uint32_t lstart = inode->this_extent.lstart + inode->this_extent.len;
79 if (inode->fs->fs_ops->next_extent(inode, lstart))
80 inode->next_extent.len = 0; /* ERROR */
81 inode->next_extent.lstart = lstart;
83 dprintf("Extent: inode %p @ %u start %llu len %u\n",
84 inode, inode->next_extent.lstart,
85 inode->next_extent.pstart, inode->next_extent.len);
88 uint32_t generic_getfssec(struct file *file, char *buf,
89 int sectors, bool *have_more)
91 struct inode *inode = file->inode;
92 struct fs_info *fs = file->fs;
93 struct disk *disk = fs->fs_dev->disk;
94 uint32_t bytes_read = 0;
95 uint32_t bytes_left = inode->size - file->offset;
96 uint32_t sectors_left =
97 (bytes_left + SECTOR_SIZE(fs) - 1) >> SECTOR_SHIFT(fs);
98 uint32_t lsector;
100 if (sectors > sectors_left)
101 sectors = sectors_left;
103 if (!sectors)
104 return 0;
106 lsector = file->offset >> SECTOR_SHIFT(fs);
107 dprintf("Offset: %u lsector: %u\n", file->offset, lsector);
109 if (lsector < inode->this_extent.lstart ||
110 lsector >= inode->this_extent.lstart + inode->this_extent.len) {
111 /* inode->this_extent unusable, maybe next_extent is... */
112 inode->this_extent = inode->next_extent;
115 if (lsector < inode->this_extent.lstart ||
116 lsector >= inode->this_extent.lstart + inode->this_extent.len) {
117 /* Still nothing useful... */
118 inode->this_extent.lstart = lsector;
119 inode->this_extent.len = 0;
120 } else {
121 /* We have some usable information */
122 uint32_t delta = lsector - inode->this_extent.lstart;
123 inode->this_extent.lstart = lsector;
124 inode->this_extent.len -= delta;
125 inode->this_extent.pstart
126 = next_psector(inode->this_extent.pstart, delta);
129 dprintf("this_extent: lstart %u pstart %llu len %u\n",
130 inode->this_extent.lstart,
131 inode->this_extent.pstart,
132 inode->this_extent.len);
134 while (sectors) {
135 uint32_t chunk;
136 size_t len;
138 while (sectors > inode->this_extent.len) {
139 if (!inode->next_extent.len ||
140 inode->next_extent.lstart !=
141 inode->this_extent.lstart + inode->this_extent.len)
142 get_next_extent(inode);
144 if (!inode->this_extent.len) {
145 /* Doesn't matter if it's contiguous... */
146 inode->this_extent = inode->next_extent;
147 if (!inode->next_extent.len) {
148 sectors = 0; /* Failed to get anything... we're dead */
149 break;
151 } else if (inode->next_extent.len &&
152 inode->next_extent.pstart == next_pstart(&inode->this_extent)) {
153 /* Coalesce extents and loop */
154 inode->this_extent.len += inode->next_extent.len;
155 } else {
156 /* Discontiguous extents */
157 break;
161 dprintf("this_extent: lstart %u pstart %llu len %u\n",
162 inode->this_extent.lstart,
163 inode->this_extent.pstart,
164 inode->this_extent.len);
166 chunk = min(sectors, inode->this_extent.len);
167 len = chunk << SECTOR_SHIFT(fs);
169 dprintf(" I/O: inode %p @ %u start %llu len %u\n",
170 inode, inode->this_extent.lstart,
171 inode->this_extent.pstart, chunk);
173 if (inode->this_extent.pstart == EXTENT_ZERO) {
174 memset(buf, 0, len);
175 } else {
176 disk->rdwr_sectors(disk, buf, inode->this_extent.pstart, chunk, 0);
177 inode->this_extent.pstart += chunk;
180 buf += len;
181 sectors -= chunk;
182 bytes_read += len;
183 inode->this_extent.lstart += chunk;
184 inode->this_extent.len -= chunk;
187 bytes_read = min(bytes_read, bytes_left);
188 file->offset += bytes_read;
190 if (have_more)
191 *have_more = bytes_read < bytes_left;
193 return bytes_read;