1 /* ----------------------------------------------------------------------- * 2 * 3 * Copyright 2010 Intel Corporation; author: H. Peter Anvin 4 * 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: 13 * 14 * The above copyright notice and this permission notice shall 15 * be included in all copies or substantial portions of the Software. 16 * 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. 25 * 26 * ----------------------------------------------------------------------- */ 27 28 /* 29 * getfssec.c 30 * 31 * Generic getfssec implementation for disk-based filesystems, which 32 * support the next_extent() method. 33 * 34 * The expected semantics of next_extent are as follows: 35 * 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.) 41 * 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. 45 * 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. 50 * 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. 54 */ 55 56 #include <dprintf.h> 57 #include <minmax.h> 58 #include "fs.h" 59 60 static inline sector_t next_psector(sector_t psector, uint32_t skip) 61 { 62 if (EXTENT_SPECIAL(psector)) 63 return psector; 64 else 65 return psector + skip; 66 } 67 68 static inline sector_t next_pstart(const struct extent *e) 69 { 70 return next_psector(e->pstart, e->len); 71 } 72 73 74 static void get_next_extent(struct inode *inode) 75 { 76 /* The logical start address that we care about... */ 77 uint32_t lstart = inode->this_extent.lstart + inode->this_extent.len; 78 79 if (inode->fs->fs_ops->next_extent(inode, lstart)) 80 inode->next_extent.len = 0; /* ERROR */ 81 inode->next_extent.lstart = lstart; 82 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); 86 } 87 88 uint32_t generic_getfssec(struct file *file, char *buf, 89 int sectors, bool *have_more) 90 { 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; 99 100 if (sectors > sectors_left) 101 sectors = sectors_left; 102 103 if (!sectors) 104 return 0; 105 106 lsector = file->offset >> SECTOR_SHIFT(fs); 107 dprintf("Offset: %u lsector: %u\n", file->offset, lsector); 108 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; 113 } 114 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); 127 } 128 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); 133 134 while (sectors) { 135 uint32_t chunk; 136 size_t len; 137 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); 143 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; 150 } 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; 158 } 159 } 160 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); 165 166 chunk = min(sectors, inode->this_extent.len); 167 len = chunk << SECTOR_SHIFT(fs); 168 169 dprintf(" I/O: inode %p @ %u start %llu len %u\n", 170 inode, inode->this_extent.lstart, 171 inode->this_extent.pstart, chunk); 172 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; 178 } 179 180 buf += len; 181 sectors -= chunk; 182 bytes_read += len; 183 inode->this_extent.lstart += chunk; 184 inode->this_extent.len -= chunk; 185 } 186 187 bytes_read = min(bytes_read, bytes_left); 188 file->offset += bytes_read; 189 190 if (have_more) 191 *have_more = bytes_read < bytes_left; 192 193 return bytes_read; 194 } 195