]>
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 (dhowells@redhat.com) | |
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 | |
22 | #define ROMFS_MTD_READ(sb, ...) ((sb)->s_mtd->read((sb)->s_mtd, ##__VA_ARGS__)) | |
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 | */ | |
70 | static int romfs_mtd_strncmp(struct super_block *sb, unsigned long pos, | |
71 | const char *str, size_t size) | |
72 | { | |
73 | u_char buf[16]; | |
74 | size_t len, segment; | |
75 | int ret; | |
76 | ||
77 | /* scan the string up to 16 bytes at a time */ | |
78 | while (size > 0) { | |
79 | segment = min_t(size_t, size, 16); | |
80 | ret = ROMFS_MTD_READ(sb, pos, segment, &len, buf); | |
81 | if (ret < 0) | |
82 | return ret; | |
83 | if (memcmp(buf, str, len) != 0) | |
84 | return 0; | |
85 | size -= len; | |
86 | pos += len; | |
87 | str += len; | |
88 | } | |
89 | ||
90 | return 1; | |
91 | } | |
92 | #endif /* CONFIG_ROMFS_ON_MTD */ | |
93 | ||
94 | #ifdef CONFIG_ROMFS_ON_BLOCK | |
95 | /* | |
96 | * read data from an romfs image on a block device | |
97 | */ | |
98 | static int romfs_blk_read(struct super_block *sb, unsigned long pos, | |
99 | void *buf, size_t buflen) | |
100 | { | |
101 | struct buffer_head *bh; | |
102 | unsigned long offset; | |
103 | size_t segment; | |
104 | ||
105 | /* copy the string up to blocksize bytes at a time */ | |
106 | while (buflen > 0) { | |
107 | offset = pos & (ROMBSIZE - 1); | |
108 | segment = min_t(size_t, buflen, ROMBSIZE - offset); | |
109 | bh = sb_bread(sb, pos >> ROMBSBITS); | |
110 | if (!bh) | |
111 | return -EIO; | |
112 | memcpy(buf, bh->b_data + offset, segment); | |
113 | brelse(bh); | |
114 | buflen -= segment; | |
115 | pos += segment; | |
116 | } | |
117 | ||
118 | return 0; | |
119 | } | |
120 | ||
121 | /* | |
122 | * determine the length of a string in romfs on a block device | |
123 | */ | |
124 | static ssize_t romfs_blk_strnlen(struct super_block *sb, | |
125 | unsigned long pos, size_t limit) | |
126 | { | |
127 | struct buffer_head *bh; | |
128 | unsigned long offset; | |
129 | ssize_t n = 0; | |
130 | size_t segment; | |
131 | u_char *buf, *p; | |
132 | ||
133 | /* scan the string up to blocksize bytes at a time */ | |
134 | while (limit > 0) { | |
135 | offset = pos & (ROMBSIZE - 1); | |
136 | segment = min_t(size_t, limit, ROMBSIZE - offset); | |
137 | bh = sb_bread(sb, pos >> ROMBSBITS); | |
138 | if (!bh) | |
139 | return -EIO; | |
140 | buf = bh->b_data + offset; | |
141 | p = memchr(buf, 0, segment); | |
142 | brelse(bh); | |
143 | if (p) | |
144 | return n + (p - buf); | |
145 | limit -= segment; | |
146 | pos += segment; | |
147 | n += segment; | |
148 | } | |
149 | ||
150 | return n; | |
151 | } | |
152 | ||
153 | /* | |
154 | * compare a string to one in a romfs image on a block device | |
155 | * - return 1 if matched, 0 if differ, -ve if error | |
156 | */ | |
157 | static int romfs_blk_strncmp(struct super_block *sb, unsigned long pos, | |
158 | const char *str, size_t size) | |
159 | { | |
160 | struct buffer_head *bh; | |
161 | unsigned long offset; | |
162 | size_t segment; | |
163 | bool x; | |
164 | ||
165 | /* scan the string up to 16 bytes at a time */ | |
166 | while (size > 0) { | |
167 | offset = pos & (ROMBSIZE - 1); | |
168 | segment = min_t(size_t, size, ROMBSIZE - offset); | |
169 | bh = sb_bread(sb, pos >> ROMBSBITS); | |
170 | if (!bh) | |
171 | return -EIO; | |
172 | x = (memcmp(bh->b_data + offset, str, segment) != 0); | |
173 | brelse(bh); | |
174 | if (x) | |
175 | return 0; | |
176 | size -= segment; | |
177 | pos += segment; | |
178 | str += segment; | |
179 | } | |
180 | ||
181 | return 1; | |
182 | } | |
183 | #endif /* CONFIG_ROMFS_ON_BLOCK */ | |
184 | ||
185 | /* | |
186 | * read data from the romfs image | |
187 | */ | |
188 | int romfs_dev_read(struct super_block *sb, unsigned long pos, | |
189 | void *buf, size_t buflen) | |
190 | { | |
191 | size_t limit; | |
192 | ||
193 | limit = romfs_maxsize(sb); | |
194 | if (pos >= limit) | |
195 | return -EIO; | |
196 | if (buflen > limit - pos) | |
197 | buflen = limit - pos; | |
198 | ||
199 | #ifdef CONFIG_ROMFS_ON_MTD | |
200 | if (sb->s_mtd) | |
201 | return romfs_mtd_read(sb, pos, buf, buflen); | |
202 | #endif | |
203 | #ifdef CONFIG_ROMFS_ON_BLOCK | |
204 | if (sb->s_bdev) | |
205 | return romfs_blk_read(sb, pos, buf, buflen); | |
206 | #endif | |
207 | return -EIO; | |
208 | } | |
209 | ||
210 | /* | |
211 | * determine the length of a string in romfs | |
212 | */ | |
213 | ssize_t romfs_dev_strnlen(struct super_block *sb, | |
214 | unsigned long pos, size_t maxlen) | |
215 | { | |
216 | size_t limit; | |
217 | ||
218 | limit = romfs_maxsize(sb); | |
219 | if (pos >= limit) | |
220 | return -EIO; | |
221 | if (maxlen > limit - pos) | |
222 | maxlen = limit - pos; | |
223 | ||
224 | #ifdef CONFIG_ROMFS_ON_MTD | |
225 | if (sb->s_mtd) | |
226 | return romfs_mtd_strnlen(sb, pos, limit); | |
227 | #endif | |
228 | #ifdef CONFIG_ROMFS_ON_BLOCK | |
229 | if (sb->s_bdev) | |
230 | return romfs_blk_strnlen(sb, pos, limit); | |
231 | #endif | |
232 | return -EIO; | |
233 | } | |
234 | ||
235 | /* | |
236 | * compare a string to one in romfs | |
237 | * - return 1 if matched, 0 if differ, -ve if error | |
238 | */ | |
239 | int romfs_dev_strncmp(struct super_block *sb, unsigned long pos, | |
240 | const char *str, size_t size) | |
241 | { | |
242 | size_t limit; | |
243 | ||
244 | limit = romfs_maxsize(sb); | |
245 | if (pos >= limit) | |
246 | return -EIO; | |
247 | if (size > ROMFS_MAXFN) | |
248 | return -ENAMETOOLONG; | |
249 | if (size > limit - pos) | |
250 | return -EIO; | |
251 | ||
252 | #ifdef CONFIG_ROMFS_ON_MTD | |
253 | if (sb->s_mtd) | |
254 | return romfs_mtd_strncmp(sb, pos, str, size); | |
255 | #endif | |
256 | #ifdef CONFIG_ROMFS_ON_BLOCK | |
257 | if (sb->s_bdev) | |
258 | return romfs_blk_strncmp(sb, pos, str, size); | |
259 | #endif | |
260 | return -EIO; | |
261 | } |