]>
Commit | Line | Data |
---|---|---|
da4458bd DH |
1 | /* RomFS storage access routines |
2 | * | |
3 | * Copyright © 2007 Red Hat, Inc. All Rights Reserved. | |
4 | * Written by David Howells ([email protected]) | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or | |
7 | * modify it under the terms of the GNU General Public License | |
8 | * as published by the Free Software Foundation; either version | |
9 | * 2 of the License, or (at your option) any later version. | |
10 | */ | |
11 | ||
12 | #include <linux/fs.h> | |
13 | #include <linux/mtd/super.h> | |
14 | #include <linux/buffer_head.h> | |
15 | #include "internal.h" | |
16 | ||
17 | #if !defined(CONFIG_ROMFS_ON_MTD) && !defined(CONFIG_ROMFS_ON_BLOCK) | |
18 | #error no ROMFS backing store interface configured | |
19 | #endif | |
20 | ||
21 | #ifdef CONFIG_ROMFS_ON_MTD | |
c382fb43 | 22 | #define ROMFS_MTD_READ(sb, ...) mtd_read((sb)->s_mtd, ##__VA_ARGS__) |
da4458bd DH |
23 | |
24 | /* | |
25 | * read data from an romfs image on an MTD device | |
26 | */ | |
27 | static int romfs_mtd_read(struct super_block *sb, unsigned long pos, | |
28 | void *buf, size_t buflen) | |
29 | { | |
30 | size_t rlen; | |
31 | int ret; | |
32 | ||
33 | ret = ROMFS_MTD_READ(sb, pos, buflen, &rlen, buf); | |
34 | return (ret < 0 || rlen != buflen) ? -EIO : 0; | |
35 | } | |
36 | ||
37 | /* | |
38 | * determine the length of a string in a romfs image on an MTD device | |
39 | */ | |
40 | static ssize_t romfs_mtd_strnlen(struct super_block *sb, | |
41 | unsigned long pos, size_t maxlen) | |
42 | { | |
43 | ssize_t n = 0; | |
44 | size_t segment; | |
45 | u_char buf[16], *p; | |
46 | size_t len; | |
47 | int ret; | |
48 | ||
49 | /* scan the string up to 16 bytes at a time */ | |
50 | while (maxlen > 0) { | |
51 | segment = min_t(size_t, maxlen, 16); | |
52 | ret = ROMFS_MTD_READ(sb, pos, segment, &len, buf); | |
53 | if (ret < 0) | |
54 | return ret; | |
55 | p = memchr(buf, 0, len); | |
56 | if (p) | |
57 | return n + (p - buf); | |
58 | maxlen -= len; | |
59 | pos += len; | |
60 | n += len; | |
61 | } | |
62 | ||
63 | return n; | |
64 | } | |
65 | ||
66 | /* | |
67 | * compare a string to one in a romfs image on MTD | |
68 | * - return 1 if matched, 0 if differ, -ve if error | |
69 | */ | |
84baf74b DH |
70 | static int romfs_mtd_strcmp(struct super_block *sb, unsigned long pos, |
71 | const char *str, size_t size) | |
da4458bd | 72 | { |
84baf74b | 73 | u_char buf[17]; |
da4458bd DH |
74 | size_t len, segment; |
75 | int ret; | |
76 | ||
84baf74b DH |
77 | /* scan the string up to 16 bytes at a time, and attempt to grab the |
78 | * trailing NUL whilst we're at it */ | |
79 | buf[0] = 0xff; | |
80 | ||
da4458bd | 81 | while (size > 0) { |
84baf74b | 82 | segment = min_t(size_t, size + 1, 17); |
da4458bd DH |
83 | ret = ROMFS_MTD_READ(sb, pos, segment, &len, buf); |
84 | if (ret < 0) | |
85 | return ret; | |
84baf74b | 86 | len--; |
da4458bd DH |
87 | if (memcmp(buf, str, len) != 0) |
88 | return 0; | |
84baf74b | 89 | buf[0] = buf[len]; |
da4458bd DH |
90 | size -= len; |
91 | pos += len; | |
92 | str += len; | |
93 | } | |
94 | ||
84baf74b DH |
95 | /* check the trailing NUL was */ |
96 | if (buf[0]) | |
97 | return 0; | |
98 | ||
da4458bd DH |
99 | return 1; |
100 | } | |
101 | #endif /* CONFIG_ROMFS_ON_MTD */ | |
102 | ||
103 | #ifdef CONFIG_ROMFS_ON_BLOCK | |
104 | /* | |
105 | * read data from an romfs image on a block device | |
106 | */ | |
107 | static int romfs_blk_read(struct super_block *sb, unsigned long pos, | |
108 | void *buf, size_t buflen) | |
109 | { | |
110 | struct buffer_head *bh; | |
111 | unsigned long offset; | |
112 | size_t segment; | |
113 | ||
114 | /* copy the string up to blocksize bytes at a time */ | |
115 | while (buflen > 0) { | |
116 | offset = pos & (ROMBSIZE - 1); | |
117 | segment = min_t(size_t, buflen, ROMBSIZE - offset); | |
118 | bh = sb_bread(sb, pos >> ROMBSBITS); | |
119 | if (!bh) | |
120 | return -EIO; | |
121 | memcpy(buf, bh->b_data + offset, segment); | |
122 | brelse(bh); | |
4b2b0b97 | 123 | buf += segment; |
da4458bd DH |
124 | buflen -= segment; |
125 | pos += segment; | |
126 | } | |
127 | ||
128 | return 0; | |
129 | } | |
130 | ||
131 | /* | |
132 | * determine the length of a string in romfs on a block device | |
133 | */ | |
134 | static ssize_t romfs_blk_strnlen(struct super_block *sb, | |
135 | unsigned long pos, size_t limit) | |
136 | { | |
137 | struct buffer_head *bh; | |
138 | unsigned long offset; | |
139 | ssize_t n = 0; | |
140 | size_t segment; | |
141 | u_char *buf, *p; | |
142 | ||
143 | /* scan the string up to blocksize bytes at a time */ | |
144 | while (limit > 0) { | |
145 | offset = pos & (ROMBSIZE - 1); | |
146 | segment = min_t(size_t, limit, ROMBSIZE - offset); | |
147 | bh = sb_bread(sb, pos >> ROMBSBITS); | |
148 | if (!bh) | |
149 | return -EIO; | |
150 | buf = bh->b_data + offset; | |
151 | p = memchr(buf, 0, segment); | |
152 | brelse(bh); | |
153 | if (p) | |
154 | return n + (p - buf); | |
155 | limit -= segment; | |
156 | pos += segment; | |
157 | n += segment; | |
158 | } | |
159 | ||
160 | return n; | |
161 | } | |
162 | ||
163 | /* | |
164 | * compare a string to one in a romfs image on a block device | |
165 | * - return 1 if matched, 0 if differ, -ve if error | |
166 | */ | |
84baf74b DH |
167 | static int romfs_blk_strcmp(struct super_block *sb, unsigned long pos, |
168 | const char *str, size_t size) | |
da4458bd DH |
169 | { |
170 | struct buffer_head *bh; | |
171 | unsigned long offset; | |
172 | size_t segment; | |
84baf74b | 173 | bool matched, terminated = false; |
da4458bd | 174 | |
84baf74b | 175 | /* compare string up to a block at a time */ |
da4458bd DH |
176 | while (size > 0) { |
177 | offset = pos & (ROMBSIZE - 1); | |
178 | segment = min_t(size_t, size, ROMBSIZE - offset); | |
179 | bh = sb_bread(sb, pos >> ROMBSBITS); | |
180 | if (!bh) | |
181 | return -EIO; | |
84baf74b DH |
182 | matched = (memcmp(bh->b_data + offset, str, segment) == 0); |
183 | ||
da4458bd DH |
184 | size -= segment; |
185 | pos += segment; | |
186 | str += segment; | |
84baf74b DH |
187 | if (matched && size == 0 && offset + segment < ROMBSIZE) { |
188 | if (!bh->b_data[offset + segment]) | |
189 | terminated = true; | |
190 | else | |
191 | matched = false; | |
192 | } | |
193 | brelse(bh); | |
194 | if (!matched) | |
195 | return 0; | |
196 | } | |
197 | ||
198 | if (!terminated) { | |
199 | /* the terminating NUL must be on the first byte of the next | |
200 | * block */ | |
201 | BUG_ON((pos & (ROMBSIZE - 1)) != 0); | |
202 | bh = sb_bread(sb, pos >> ROMBSBITS); | |
203 | if (!bh) | |
204 | return -EIO; | |
205 | matched = !bh->b_data[0]; | |
206 | brelse(bh); | |
207 | if (!matched) | |
208 | return 0; | |
da4458bd DH |
209 | } |
210 | ||
211 | return 1; | |
212 | } | |
213 | #endif /* CONFIG_ROMFS_ON_BLOCK */ | |
214 | ||
215 | /* | |
216 | * read data from the romfs image | |
217 | */ | |
218 | int romfs_dev_read(struct super_block *sb, unsigned long pos, | |
219 | void *buf, size_t buflen) | |
220 | { | |
221 | size_t limit; | |
222 | ||
223 | limit = romfs_maxsize(sb); | |
224 | if (pos >= limit) | |
225 | return -EIO; | |
226 | if (buflen > limit - pos) | |
227 | buflen = limit - pos; | |
228 | ||
229 | #ifdef CONFIG_ROMFS_ON_MTD | |
230 | if (sb->s_mtd) | |
231 | return romfs_mtd_read(sb, pos, buf, buflen); | |
232 | #endif | |
233 | #ifdef CONFIG_ROMFS_ON_BLOCK | |
234 | if (sb->s_bdev) | |
235 | return romfs_blk_read(sb, pos, buf, buflen); | |
236 | #endif | |
237 | return -EIO; | |
238 | } | |
239 | ||
240 | /* | |
241 | * determine the length of a string in romfs | |
242 | */ | |
243 | ssize_t romfs_dev_strnlen(struct super_block *sb, | |
244 | unsigned long pos, size_t maxlen) | |
245 | { | |
246 | size_t limit; | |
247 | ||
248 | limit = romfs_maxsize(sb); | |
249 | if (pos >= limit) | |
250 | return -EIO; | |
251 | if (maxlen > limit - pos) | |
252 | maxlen = limit - pos; | |
253 | ||
254 | #ifdef CONFIG_ROMFS_ON_MTD | |
255 | if (sb->s_mtd) | |
ef1f7a7e | 256 | return romfs_mtd_strnlen(sb, pos, maxlen); |
da4458bd DH |
257 | #endif |
258 | #ifdef CONFIG_ROMFS_ON_BLOCK | |
259 | if (sb->s_bdev) | |
ef1f7a7e | 260 | return romfs_blk_strnlen(sb, pos, maxlen); |
da4458bd DH |
261 | #endif |
262 | return -EIO; | |
263 | } | |
264 | ||
265 | /* | |
266 | * compare a string to one in romfs | |
84baf74b DH |
267 | * - the string to be compared to, str, may not be NUL-terminated; instead the |
268 | * string is of the specified size | |
da4458bd DH |
269 | * - return 1 if matched, 0 if differ, -ve if error |
270 | */ | |
84baf74b DH |
271 | int romfs_dev_strcmp(struct super_block *sb, unsigned long pos, |
272 | const char *str, size_t size) | |
da4458bd DH |
273 | { |
274 | size_t limit; | |
275 | ||
276 | limit = romfs_maxsize(sb); | |
277 | if (pos >= limit) | |
278 | return -EIO; | |
279 | if (size > ROMFS_MAXFN) | |
280 | return -ENAMETOOLONG; | |
84baf74b | 281 | if (size + 1 > limit - pos) |
da4458bd DH |
282 | return -EIO; |
283 | ||
284 | #ifdef CONFIG_ROMFS_ON_MTD | |
285 | if (sb->s_mtd) | |
84baf74b | 286 | return romfs_mtd_strcmp(sb, pos, str, size); |
da4458bd DH |
287 | #endif |
288 | #ifdef CONFIG_ROMFS_ON_BLOCK | |
289 | if (sb->s_bdev) | |
84baf74b | 290 | return romfs_blk_strcmp(sb, pos, str, size); |
da4458bd DH |
291 | #endif |
292 | return -EIO; | |
293 | } |