Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/kaber/nf-2.6
[linux-2.6] / fs / romfs / storage.c
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 }