12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2da4458bdSDavid Howells /* RomFS storage access routines
3da4458bdSDavid Howells *
4da4458bdSDavid Howells * Copyright © 2007 Red Hat, Inc. All Rights Reserved.
5da4458bdSDavid Howells * Written by David Howells (dhowells@redhat.com)
6da4458bdSDavid Howells */
7da4458bdSDavid Howells
8da4458bdSDavid Howells #include <linux/fs.h>
9da4458bdSDavid Howells #include <linux/mtd/super.h>
10da4458bdSDavid Howells #include <linux/buffer_head.h>
11da4458bdSDavid Howells #include "internal.h"
12da4458bdSDavid Howells
13da4458bdSDavid Howells #if !defined(CONFIG_ROMFS_ON_MTD) && !defined(CONFIG_ROMFS_ON_BLOCK)
14da4458bdSDavid Howells #error no ROMFS backing store interface configured
15da4458bdSDavid Howells #endif
16da4458bdSDavid Howells
17da4458bdSDavid Howells #ifdef CONFIG_ROMFS_ON_MTD
18c382fb43SArtem Bityutskiy #define ROMFS_MTD_READ(sb, ...) mtd_read((sb)->s_mtd, ##__VA_ARGS__)
19da4458bdSDavid Howells
20da4458bdSDavid Howells /*
21da4458bdSDavid Howells * read data from an romfs image on an MTD device
22da4458bdSDavid Howells */
romfs_mtd_read(struct super_block * sb,unsigned long pos,void * buf,size_t buflen)23da4458bdSDavid Howells static int romfs_mtd_read(struct super_block *sb, unsigned long pos,
24da4458bdSDavid Howells void *buf, size_t buflen)
25da4458bdSDavid Howells {
26da4458bdSDavid Howells size_t rlen;
27da4458bdSDavid Howells int ret;
28da4458bdSDavid Howells
29da4458bdSDavid Howells ret = ROMFS_MTD_READ(sb, pos, buflen, &rlen, buf);
30da4458bdSDavid Howells return (ret < 0 || rlen != buflen) ? -EIO : 0;
31da4458bdSDavid Howells }
32da4458bdSDavid Howells
33da4458bdSDavid Howells /*
34da4458bdSDavid Howells * determine the length of a string in a romfs image on an MTD device
35da4458bdSDavid Howells */
romfs_mtd_strnlen(struct super_block * sb,unsigned long pos,size_t maxlen)36da4458bdSDavid Howells static ssize_t romfs_mtd_strnlen(struct super_block *sb,
37da4458bdSDavid Howells unsigned long pos, size_t maxlen)
38da4458bdSDavid Howells {
39da4458bdSDavid Howells ssize_t n = 0;
40da4458bdSDavid Howells size_t segment;
41da4458bdSDavid Howells u_char buf[16], *p;
42da4458bdSDavid Howells size_t len;
43da4458bdSDavid Howells int ret;
44da4458bdSDavid Howells
45da4458bdSDavid Howells /* scan the string up to 16 bytes at a time */
46da4458bdSDavid Howells while (maxlen > 0) {
47da4458bdSDavid Howells segment = min_t(size_t, maxlen, 16);
48da4458bdSDavid Howells ret = ROMFS_MTD_READ(sb, pos, segment, &len, buf);
49da4458bdSDavid Howells if (ret < 0)
50da4458bdSDavid Howells return ret;
51da4458bdSDavid Howells p = memchr(buf, 0, len);
52da4458bdSDavid Howells if (p)
53da4458bdSDavid Howells return n + (p - buf);
54da4458bdSDavid Howells maxlen -= len;
55da4458bdSDavid Howells pos += len;
56da4458bdSDavid Howells n += len;
57da4458bdSDavid Howells }
58da4458bdSDavid Howells
59da4458bdSDavid Howells return n;
60da4458bdSDavid Howells }
61da4458bdSDavid Howells
62da4458bdSDavid Howells /*
63da4458bdSDavid Howells * compare a string to one in a romfs image on MTD
64da4458bdSDavid Howells * - return 1 if matched, 0 if differ, -ve if error
65da4458bdSDavid Howells */
romfs_mtd_strcmp(struct super_block * sb,unsigned long pos,const char * str,size_t size)6684baf74bSDavid Howells static int romfs_mtd_strcmp(struct super_block *sb, unsigned long pos,
67da4458bdSDavid Howells const char *str, size_t size)
68da4458bdSDavid Howells {
6984baf74bSDavid Howells u_char buf[17];
70da4458bdSDavid Howells size_t len, segment;
71da4458bdSDavid Howells int ret;
72da4458bdSDavid Howells
7384baf74bSDavid Howells /* scan the string up to 16 bytes at a time, and attempt to grab the
7484baf74bSDavid Howells * trailing NUL whilst we're at it */
7584baf74bSDavid Howells buf[0] = 0xff;
7684baf74bSDavid Howells
77da4458bdSDavid Howells while (size > 0) {
7884baf74bSDavid Howells segment = min_t(size_t, size + 1, 17);
79da4458bdSDavid Howells ret = ROMFS_MTD_READ(sb, pos, segment, &len, buf);
80da4458bdSDavid Howells if (ret < 0)
81da4458bdSDavid Howells return ret;
8284baf74bSDavid Howells len--;
83da4458bdSDavid Howells if (memcmp(buf, str, len) != 0)
84da4458bdSDavid Howells return 0;
8584baf74bSDavid Howells buf[0] = buf[len];
86da4458bdSDavid Howells size -= len;
87da4458bdSDavid Howells pos += len;
88da4458bdSDavid Howells str += len;
89da4458bdSDavid Howells }
90da4458bdSDavid Howells
9184baf74bSDavid Howells /* check the trailing NUL was */
9284baf74bSDavid Howells if (buf[0])
9384baf74bSDavid Howells return 0;
9484baf74bSDavid Howells
95da4458bdSDavid Howells return 1;
96da4458bdSDavid Howells }
97da4458bdSDavid Howells #endif /* CONFIG_ROMFS_ON_MTD */
98da4458bdSDavid Howells
99da4458bdSDavid Howells #ifdef CONFIG_ROMFS_ON_BLOCK
100da4458bdSDavid Howells /*
101da4458bdSDavid Howells * read data from an romfs image on a block device
102da4458bdSDavid Howells */
romfs_blk_read(struct super_block * sb,unsigned long pos,void * buf,size_t buflen)103da4458bdSDavid Howells static int romfs_blk_read(struct super_block *sb, unsigned long pos,
104da4458bdSDavid Howells void *buf, size_t buflen)
105da4458bdSDavid Howells {
106da4458bdSDavid Howells struct buffer_head *bh;
107da4458bdSDavid Howells unsigned long offset;
108da4458bdSDavid Howells size_t segment;
109da4458bdSDavid Howells
110da4458bdSDavid Howells /* copy the string up to blocksize bytes at a time */
111da4458bdSDavid Howells while (buflen > 0) {
112da4458bdSDavid Howells offset = pos & (ROMBSIZE - 1);
113da4458bdSDavid Howells segment = min_t(size_t, buflen, ROMBSIZE - offset);
114da4458bdSDavid Howells bh = sb_bread(sb, pos >> ROMBSBITS);
115da4458bdSDavid Howells if (!bh)
116da4458bdSDavid Howells return -EIO;
117da4458bdSDavid Howells memcpy(buf, bh->b_data + offset, segment);
118da4458bdSDavid Howells brelse(bh);
1194b2b0b97SDavid Howells buf += segment;
120da4458bdSDavid Howells buflen -= segment;
121da4458bdSDavid Howells pos += segment;
122da4458bdSDavid Howells }
123da4458bdSDavid Howells
124da4458bdSDavid Howells return 0;
125da4458bdSDavid Howells }
126da4458bdSDavid Howells
127da4458bdSDavid Howells /*
128da4458bdSDavid Howells * determine the length of a string in romfs on a block device
129da4458bdSDavid Howells */
romfs_blk_strnlen(struct super_block * sb,unsigned long pos,size_t limit)130da4458bdSDavid Howells static ssize_t romfs_blk_strnlen(struct super_block *sb,
131da4458bdSDavid Howells unsigned long pos, size_t limit)
132da4458bdSDavid Howells {
133da4458bdSDavid Howells struct buffer_head *bh;
134da4458bdSDavid Howells unsigned long offset;
135da4458bdSDavid Howells ssize_t n = 0;
136da4458bdSDavid Howells size_t segment;
137da4458bdSDavid Howells u_char *buf, *p;
138da4458bdSDavid Howells
139da4458bdSDavid Howells /* scan the string up to blocksize bytes at a time */
140da4458bdSDavid Howells while (limit > 0) {
141da4458bdSDavid Howells offset = pos & (ROMBSIZE - 1);
142da4458bdSDavid Howells segment = min_t(size_t, limit, ROMBSIZE - offset);
143da4458bdSDavid Howells bh = sb_bread(sb, pos >> ROMBSBITS);
144da4458bdSDavid Howells if (!bh)
145da4458bdSDavid Howells return -EIO;
146da4458bdSDavid Howells buf = bh->b_data + offset;
147da4458bdSDavid Howells p = memchr(buf, 0, segment);
148da4458bdSDavid Howells brelse(bh);
149da4458bdSDavid Howells if (p)
150da4458bdSDavid Howells return n + (p - buf);
151da4458bdSDavid Howells limit -= segment;
152da4458bdSDavid Howells pos += segment;
153da4458bdSDavid Howells n += segment;
154da4458bdSDavid Howells }
155da4458bdSDavid Howells
156da4458bdSDavid Howells return n;
157da4458bdSDavid Howells }
158da4458bdSDavid Howells
159da4458bdSDavid Howells /*
160da4458bdSDavid Howells * compare a string to one in a romfs image on a block device
161da4458bdSDavid Howells * - return 1 if matched, 0 if differ, -ve if error
162da4458bdSDavid Howells */
romfs_blk_strcmp(struct super_block * sb,unsigned long pos,const char * str,size_t size)16384baf74bSDavid Howells static int romfs_blk_strcmp(struct super_block *sb, unsigned long pos,
164da4458bdSDavid Howells const char *str, size_t size)
165da4458bdSDavid Howells {
166da4458bdSDavid Howells struct buffer_head *bh;
167da4458bdSDavid Howells unsigned long offset;
168da4458bdSDavid Howells size_t segment;
16984baf74bSDavid Howells bool matched, terminated = false;
170da4458bdSDavid Howells
17184baf74bSDavid Howells /* compare string up to a block at a time */
172da4458bdSDavid Howells while (size > 0) {
173da4458bdSDavid Howells offset = pos & (ROMBSIZE - 1);
174da4458bdSDavid Howells segment = min_t(size_t, size, ROMBSIZE - offset);
175da4458bdSDavid Howells bh = sb_bread(sb, pos >> ROMBSBITS);
176da4458bdSDavid Howells if (!bh)
177da4458bdSDavid Howells return -EIO;
17884baf74bSDavid Howells matched = (memcmp(bh->b_data + offset, str, segment) == 0);
17984baf74bSDavid Howells
180da4458bdSDavid Howells size -= segment;
181da4458bdSDavid Howells pos += segment;
182da4458bdSDavid Howells str += segment;
18384baf74bSDavid Howells if (matched && size == 0 && offset + segment < ROMBSIZE) {
18484baf74bSDavid Howells if (!bh->b_data[offset + segment])
18584baf74bSDavid Howells terminated = true;
18684baf74bSDavid Howells else
18784baf74bSDavid Howells matched = false;
18884baf74bSDavid Howells }
18984baf74bSDavid Howells brelse(bh);
19084baf74bSDavid Howells if (!matched)
19184baf74bSDavid Howells return 0;
19284baf74bSDavid Howells }
19384baf74bSDavid Howells
19484baf74bSDavid Howells if (!terminated) {
19584baf74bSDavid Howells /* the terminating NUL must be on the first byte of the next
19684baf74bSDavid Howells * block */
19784baf74bSDavid Howells BUG_ON((pos & (ROMBSIZE - 1)) != 0);
19884baf74bSDavid Howells bh = sb_bread(sb, pos >> ROMBSBITS);
19984baf74bSDavid Howells if (!bh)
20084baf74bSDavid Howells return -EIO;
20184baf74bSDavid Howells matched = !bh->b_data[0];
20284baf74bSDavid Howells brelse(bh);
20384baf74bSDavid Howells if (!matched)
20484baf74bSDavid Howells return 0;
205da4458bdSDavid Howells }
206da4458bdSDavid Howells
207da4458bdSDavid Howells return 1;
208da4458bdSDavid Howells }
209da4458bdSDavid Howells #endif /* CONFIG_ROMFS_ON_BLOCK */
210da4458bdSDavid Howells
211da4458bdSDavid Howells /*
212da4458bdSDavid Howells * read data from the romfs image
213da4458bdSDavid Howells */
romfs_dev_read(struct super_block * sb,unsigned long pos,void * buf,size_t buflen)214da4458bdSDavid Howells int romfs_dev_read(struct super_block *sb, unsigned long pos,
215da4458bdSDavid Howells void *buf, size_t buflen)
216da4458bdSDavid Howells {
217da4458bdSDavid Howells size_t limit;
218da4458bdSDavid Howells
219da4458bdSDavid Howells limit = romfs_maxsize(sb);
220*bcf85fceSJann Horn if (pos >= limit || buflen > limit - pos)
221da4458bdSDavid Howells return -EIO;
222da4458bdSDavid Howells
223da4458bdSDavid Howells #ifdef CONFIG_ROMFS_ON_MTD
224da4458bdSDavid Howells if (sb->s_mtd)
225da4458bdSDavid Howells return romfs_mtd_read(sb, pos, buf, buflen);
226da4458bdSDavid Howells #endif
227da4458bdSDavid Howells #ifdef CONFIG_ROMFS_ON_BLOCK
228da4458bdSDavid Howells if (sb->s_bdev)
229da4458bdSDavid Howells return romfs_blk_read(sb, pos, buf, buflen);
230da4458bdSDavid Howells #endif
231da4458bdSDavid Howells return -EIO;
232da4458bdSDavid Howells }
233da4458bdSDavid Howells
234da4458bdSDavid Howells /*
235da4458bdSDavid Howells * determine the length of a string in romfs
236da4458bdSDavid Howells */
romfs_dev_strnlen(struct super_block * sb,unsigned long pos,size_t maxlen)237da4458bdSDavid Howells ssize_t romfs_dev_strnlen(struct super_block *sb,
238da4458bdSDavid Howells unsigned long pos, size_t maxlen)
239da4458bdSDavid Howells {
240da4458bdSDavid Howells size_t limit;
241da4458bdSDavid Howells
242da4458bdSDavid Howells limit = romfs_maxsize(sb);
243da4458bdSDavid Howells if (pos >= limit)
244da4458bdSDavid Howells return -EIO;
245da4458bdSDavid Howells if (maxlen > limit - pos)
246da4458bdSDavid Howells maxlen = limit - pos;
247da4458bdSDavid Howells
248da4458bdSDavid Howells #ifdef CONFIG_ROMFS_ON_MTD
249da4458bdSDavid Howells if (sb->s_mtd)
250ef1f7a7eSBernd Schmidt return romfs_mtd_strnlen(sb, pos, maxlen);
251da4458bdSDavid Howells #endif
252da4458bdSDavid Howells #ifdef CONFIG_ROMFS_ON_BLOCK
253da4458bdSDavid Howells if (sb->s_bdev)
254ef1f7a7eSBernd Schmidt return romfs_blk_strnlen(sb, pos, maxlen);
255da4458bdSDavid Howells #endif
256da4458bdSDavid Howells return -EIO;
257da4458bdSDavid Howells }
258da4458bdSDavid Howells
259da4458bdSDavid Howells /*
260da4458bdSDavid Howells * compare a string to one in romfs
26184baf74bSDavid Howells * - the string to be compared to, str, may not be NUL-terminated; instead the
26284baf74bSDavid Howells * string is of the specified size
263da4458bdSDavid Howells * - return 1 if matched, 0 if differ, -ve if error
264da4458bdSDavid Howells */
romfs_dev_strcmp(struct super_block * sb,unsigned long pos,const char * str,size_t size)26584baf74bSDavid Howells int romfs_dev_strcmp(struct super_block *sb, unsigned long pos,
266da4458bdSDavid Howells const char *str, size_t size)
267da4458bdSDavid Howells {
268da4458bdSDavid Howells size_t limit;
269da4458bdSDavid Howells
270da4458bdSDavid Howells limit = romfs_maxsize(sb);
271da4458bdSDavid Howells if (pos >= limit)
272da4458bdSDavid Howells return -EIO;
273da4458bdSDavid Howells if (size > ROMFS_MAXFN)
274da4458bdSDavid Howells return -ENAMETOOLONG;
27584baf74bSDavid Howells if (size + 1 > limit - pos)
276da4458bdSDavid Howells return -EIO;
277da4458bdSDavid Howells
278da4458bdSDavid Howells #ifdef CONFIG_ROMFS_ON_MTD
279da4458bdSDavid Howells if (sb->s_mtd)
28084baf74bSDavid Howells return romfs_mtd_strcmp(sb, pos, str, size);
281da4458bdSDavid Howells #endif
282da4458bdSDavid Howells #ifdef CONFIG_ROMFS_ON_BLOCK
283da4458bdSDavid Howells if (sb->s_bdev)
28484baf74bSDavid Howells return romfs_blk_strcmp(sb, pos, str, size);
285da4458bdSDavid Howells #endif
286da4458bdSDavid Howells return -EIO;
287da4458bdSDavid Howells }
288