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
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 * ----------------------------------------------------------------------- */
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
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
60 static inline sector_t
next_psector(sector_t psector
, uint32_t skip
)
62 if (EXTENT_SPECIAL(psector
))
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
);
100 if (sectors
> sectors_left
)
101 sectors
= sectors_left
;
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;
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
);
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 */
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
;
156 /* Discontiguous extents */
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
) {
176 disk
->rdwr_sectors(disk
, buf
, inode
->this_extent
.pstart
, chunk
, 0);
177 inode
->this_extent
.pstart
+= chunk
;
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
;
191 *have_more
= bytes_read
< bytes_left
;