1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* RomFS storage access routines 3 * 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 configured 15 #endif 16 17 #ifdef CONFIG_ROMFS_ON_MTD 18 #define ROMFS_MTD_READ(sb, ...) mtd_read((sb)->s_mtd, ##__VA_ARGS__) 19 20 /* 21 * read data from an romfs image on an MTD device 22 */ 23 static 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 device 35 */ 36 static 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 MTD 64 * - return 1 if matched, 0 if differ, -ve if error 65 */ 66 static 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 the 74 * 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_BLOCK 100 /* 101 * read data from an romfs image on a block device 102 */ 103 static 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 device 129 */ 130 static 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 device 161 * - return 1 if matched, 0 if differ, -ve if error 162 */ 163 static 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 else 187 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 next 196 * 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 image 213 */ 214 int 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_MTD 224 if (sb->s_mtd) 225 return romfs_mtd_read(sb, pos, buf, buflen); 226 #endif 227 #ifdef CONFIG_ROMFS_ON_BLOCK 228 if (sb->s_bdev) 229 return romfs_blk_read(sb, pos, buf, buflen); 230 #endif 231 return -EIO; 232 } 233 234 /* 235 * determine the length of a string in romfs 236 */ 237 ssize_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_MTD 249 if (sb->s_mtd) 250 return romfs_mtd_strnlen(sb, pos, maxlen); 251 #endif 252 #ifdef CONFIG_ROMFS_ON_BLOCK 253 if (sb->s_bdev) 254 return romfs_blk_strnlen(sb, pos, maxlen); 255 #endif 256 return -EIO; 257 } 258 259 /* 260 * compare a string to one in romfs 261 * - the string to be compared to, str, may not be NUL-terminated; instead the 262 * string is of the specified size 263 * - return 1 if matched, 0 if differ, -ve if error 264 */ 265 int 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_MTD 279 if (sb->s_mtd) 280 return romfs_mtd_strcmp(sb, pos, str, size); 281 #endif 282 #ifdef CONFIG_ROMFS_ON_BLOCK 283 if (sb->s_bdev) 284 return romfs_blk_strcmp(sb, pos, str, size); 285 #endif 286 return -EIO; 287 } 288