288 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/* RomFS storage access routines3 *4 * Copyright © 2007 Red Hat, Inc. All Rights Reserved.5 * Written by David Howells (dhowells@redhat.com)6 */7 8#include <linux/fs.h>9#include <linux/mtd/super.h>10#include <linux/buffer_head.h>11#include "internal.h"12 13#if !defined(CONFIG_ROMFS_ON_MTD) && !defined(CONFIG_ROMFS_ON_BLOCK)14#error no ROMFS backing store interface configured15#endif16 17#ifdef CONFIG_ROMFS_ON_MTD18#define ROMFS_MTD_READ(sb, ...) mtd_read((sb)->s_mtd, ##__VA_ARGS__)19 20/*21 * read data from an romfs image on an MTD device22 */23static int romfs_mtd_read(struct super_block *sb, unsigned long pos,24 void *buf, size_t buflen)25{26 size_t rlen;27 int ret;28 29 ret = ROMFS_MTD_READ(sb, pos, buflen, &rlen, buf);30 return (ret < 0 || rlen != buflen) ? -EIO : 0;31}32 33/*34 * determine the length of a string in a romfs image on an MTD device35 */36static ssize_t romfs_mtd_strnlen(struct super_block *sb,37 unsigned long pos, size_t maxlen)38{39 ssize_t n = 0;40 size_t segment;41 u_char buf[16], *p;42 size_t len;43 int ret;44 45 /* scan the string up to 16 bytes at a time */46 while (maxlen > 0) {47 segment = min_t(size_t, maxlen, 16);48 ret = ROMFS_MTD_READ(sb, pos, segment, &len, buf);49 if (ret < 0)50 return ret;51 p = memchr(buf, 0, len);52 if (p)53 return n + (p - buf);54 maxlen -= len;55 pos += len;56 n += len;57 }58 59 return n;60}61 62/*63 * compare a string to one in a romfs image on MTD64 * - return 1 if matched, 0 if differ, -ve if error65 */66static int romfs_mtd_strcmp(struct super_block *sb, unsigned long pos,67 const char *str, size_t size)68{69 u_char buf[17];70 size_t len, segment;71 int ret;72 73 /* scan the string up to 16 bytes at a time, and attempt to grab the74 * trailing NUL whilst we're at it */75 buf[0] = 0xff;76 77 while (size > 0) {78 segment = min_t(size_t, size + 1, 17);79 ret = ROMFS_MTD_READ(sb, pos, segment, &len, buf);80 if (ret < 0)81 return ret;82 len--;83 if (memcmp(buf, str, len) != 0)84 return 0;85 buf[0] = buf[len];86 size -= len;87 pos += len;88 str += len;89 }90 91 /* check the trailing NUL was */92 if (buf[0])93 return 0;94 95 return 1;96}97#endif /* CONFIG_ROMFS_ON_MTD */98 99#ifdef CONFIG_ROMFS_ON_BLOCK100/*101 * read data from an romfs image on a block device102 */103static int romfs_blk_read(struct super_block *sb, unsigned long pos,104 void *buf, size_t buflen)105{106 struct buffer_head *bh;107 unsigned long offset;108 size_t segment;109 110 /* copy the string up to blocksize bytes at a time */111 while (buflen > 0) {112 offset = pos & (ROMBSIZE - 1);113 segment = min_t(size_t, buflen, ROMBSIZE - offset);114 bh = sb_bread(sb, pos >> ROMBSBITS);115 if (!bh)116 return -EIO;117 memcpy(buf, bh->b_data + offset, segment);118 brelse(bh);119 buf += segment;120 buflen -= segment;121 pos += segment;122 }123 124 return 0;125}126 127/*128 * determine the length of a string in romfs on a block device129 */130static ssize_t romfs_blk_strnlen(struct super_block *sb,131 unsigned long pos, size_t limit)132{133 struct buffer_head *bh;134 unsigned long offset;135 ssize_t n = 0;136 size_t segment;137 u_char *buf, *p;138 139 /* scan the string up to blocksize bytes at a time */140 while (limit > 0) {141 offset = pos & (ROMBSIZE - 1);142 segment = min_t(size_t, limit, ROMBSIZE - offset);143 bh = sb_bread(sb, pos >> ROMBSBITS);144 if (!bh)145 return -EIO;146 buf = bh->b_data + offset;147 p = memchr(buf, 0, segment);148 brelse(bh);149 if (p)150 return n + (p - buf);151 limit -= segment;152 pos += segment;153 n += segment;154 }155 156 return n;157}158 159/*160 * compare a string to one in a romfs image on a block device161 * - return 1 if matched, 0 if differ, -ve if error162 */163static int romfs_blk_strcmp(struct super_block *sb, unsigned long pos,164 const char *str, size_t size)165{166 struct buffer_head *bh;167 unsigned long offset;168 size_t segment;169 bool matched, terminated = false;170 171 /* compare string up to a block at a time */172 while (size > 0) {173 offset = pos & (ROMBSIZE - 1);174 segment = min_t(size_t, size, ROMBSIZE - offset);175 bh = sb_bread(sb, pos >> ROMBSBITS);176 if (!bh)177 return -EIO;178 matched = (memcmp(bh->b_data + offset, str, segment) == 0);179 180 size -= segment;181 pos += segment;182 str += segment;183 if (matched && size == 0 && offset + segment < ROMBSIZE) {184 if (!bh->b_data[offset + segment])185 terminated = true;186 else187 matched = false;188 }189 brelse(bh);190 if (!matched)191 return 0;192 }193 194 if (!terminated) {195 /* the terminating NUL must be on the first byte of the next196 * block */197 BUG_ON((pos & (ROMBSIZE - 1)) != 0);198 bh = sb_bread(sb, pos >> ROMBSBITS);199 if (!bh)200 return -EIO;201 matched = !bh->b_data[0];202 brelse(bh);203 if (!matched)204 return 0;205 }206 207 return 1;208}209#endif /* CONFIG_ROMFS_ON_BLOCK */210 211/*212 * read data from the romfs image213 */214int romfs_dev_read(struct super_block *sb, unsigned long pos,215 void *buf, size_t buflen)216{217 size_t limit;218 219 limit = romfs_maxsize(sb);220 if (pos >= limit || buflen > limit - pos)221 return -EIO;222 223#ifdef CONFIG_ROMFS_ON_MTD224 if (sb->s_mtd)225 return romfs_mtd_read(sb, pos, buf, buflen);226#endif227#ifdef CONFIG_ROMFS_ON_BLOCK228 if (sb->s_bdev)229 return romfs_blk_read(sb, pos, buf, buflen);230#endif231 return -EIO;232}233 234/*235 * determine the length of a string in romfs236 */237ssize_t romfs_dev_strnlen(struct super_block *sb,238 unsigned long pos, size_t maxlen)239{240 size_t limit;241 242 limit = romfs_maxsize(sb);243 if (pos >= limit)244 return -EIO;245 if (maxlen > limit - pos)246 maxlen = limit - pos;247 248#ifdef CONFIG_ROMFS_ON_MTD249 if (sb->s_mtd)250 return romfs_mtd_strnlen(sb, pos, maxlen);251#endif252#ifdef CONFIG_ROMFS_ON_BLOCK253 if (sb->s_bdev)254 return romfs_blk_strnlen(sb, pos, maxlen);255#endif256 return -EIO;257}258 259/*260 * compare a string to one in romfs261 * - the string to be compared to, str, may not be NUL-terminated; instead the262 * string is of the specified size263 * - return 1 if matched, 0 if differ, -ve if error264 */265int romfs_dev_strcmp(struct super_block *sb, unsigned long pos,266 const char *str, size_t size)267{268 size_t limit;269 270 limit = romfs_maxsize(sb);271 if (pos >= limit)272 return -EIO;273 if (size > ROMFS_MAXFN)274 return -ENAMETOOLONG;275 if (size + 1 > limit - pos)276 return -EIO;277 278#ifdef CONFIG_ROMFS_ON_MTD279 if (sb->s_mtd)280 return romfs_mtd_strcmp(sb, pos, str, size);281#endif282#ifdef CONFIG_ROMFS_ON_BLOCK283 if (sb->s_bdev)284 return romfs_blk_strcmp(sb, pos, str, size);285#endif286 return -EIO;287}288