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_strcmp(struct super_block *sb, unsigned long pos, 71 const char *str, size_t size) 72 { 73 u_char buf[17]; 74 size_t len, segment; 75 int ret; 76 77 /* scan the string up to 16 bytes at a time, and attempt to grab the 78 * trailing NUL whilst we're at it */ 79 buf[0] = 0xff; 80 81 while (size > 0) { 82 segment = min_t(size_t, size + 1, 17); 83 ret = ROMFS_MTD_READ(sb, pos, segment, &len, buf); 84 if (ret < 0) 85 return ret; 86 len--; 87 if (memcmp(buf, str, len) != 0) 88 return 0; 89 buf[0] = buf[len]; 90 size -= len; 91 pos += len; 92 str += len; 93 } 94 95 /* check the trailing NUL was */ 96 if (buf[0]) 97 return 0; 98 99 return 1; 100 } 101 #endif /* CONFIG_ROMFS_ON_MTD */ 102 103 #ifdef CONFIG_ROMFS_ON_BLOCK 104 /* 105 * read data from an romfs image on a block device 106 */ 107 static int romfs_blk_read(struct super_block *sb, unsigned long pos, 108 void *buf, size_t buflen) 109 { 110 struct buffer_head *bh; 111 unsigned long offset; 112 size_t segment; 113 114 /* copy the string up to blocksize bytes at a time */ 115 while (buflen > 0) { 116 offset = pos & (ROMBSIZE - 1); 117 segment = min_t(size_t, buflen, ROMBSIZE - offset); 118 bh = sb_bread(sb, pos >> ROMBSBITS); 119 if (!bh) 120 return -EIO; 121 memcpy(buf, bh->b_data + offset, segment); 122 brelse(bh); 123 buf += segment; 124 buflen -= segment; 125 pos += segment; 126 } 127 128 return 0; 129 } 130 131 /* 132 * determine the length of a string in romfs on a block device 133 */ 134 static ssize_t romfs_blk_strnlen(struct super_block *sb, 135 unsigned long pos, size_t limit) 136 { 137 struct buffer_head *bh; 138 unsigned long offset; 139 ssize_t n = 0; 140 size_t segment; 141 u_char *buf, *p; 142 143 /* scan the string up to blocksize bytes at a time */ 144 while (limit > 0) { 145 offset = pos & (ROMBSIZE - 1); 146 segment = min_t(size_t, limit, ROMBSIZE - offset); 147 bh = sb_bread(sb, pos >> ROMBSBITS); 148 if (!bh) 149 return -EIO; 150 buf = bh->b_data + offset; 151 p = memchr(buf, 0, segment); 152 brelse(bh); 153 if (p) 154 return n + (p - buf); 155 limit -= segment; 156 pos += segment; 157 n += segment; 158 } 159 160 return n; 161 } 162 163 /* 164 * compare a string to one in a romfs image on a block device 165 * - return 1 if matched, 0 if differ, -ve if error 166 */ 167 static int romfs_blk_strcmp(struct super_block *sb, unsigned long pos, 168 const char *str, size_t size) 169 { 170 struct buffer_head *bh; 171 unsigned long offset; 172 size_t segment; 173 bool matched, terminated = false; 174 175 /* compare string up to a block at a time */ 176 while (size > 0) { 177 offset = pos & (ROMBSIZE - 1); 178 segment = min_t(size_t, size, ROMBSIZE - offset); 179 bh = sb_bread(sb, pos >> ROMBSBITS); 180 if (!bh) 181 return -EIO; 182 matched = (memcmp(bh->b_data + offset, str, segment) == 0); 183 184 size -= segment; 185 pos += segment; 186 str += segment; 187 if (matched && size == 0 && offset + segment < ROMBSIZE) { 188 if (!bh->b_data[offset + segment]) 189 terminated = true; 190 else 191 matched = false; 192 } 193 brelse(bh); 194 if (!matched) 195 return 0; 196 } 197 198 if (!terminated) { 199 /* the terminating NUL must be on the first byte of the next 200 * block */ 201 BUG_ON((pos & (ROMBSIZE - 1)) != 0); 202 bh = sb_bread(sb, pos >> ROMBSBITS); 203 if (!bh) 204 return -EIO; 205 matched = !bh->b_data[0]; 206 brelse(bh); 207 if (!matched) 208 return 0; 209 } 210 211 return 1; 212 } 213 #endif /* CONFIG_ROMFS_ON_BLOCK */ 214 215 /* 216 * read data from the romfs image 217 */ 218 int romfs_dev_read(struct super_block *sb, unsigned long pos, 219 void *buf, size_t buflen) 220 { 221 size_t limit; 222 223 limit = romfs_maxsize(sb); 224 if (pos >= limit) 225 return -EIO; 226 if (buflen > limit - pos) 227 buflen = limit - pos; 228 229 #ifdef CONFIG_ROMFS_ON_MTD 230 if (sb->s_mtd) 231 return romfs_mtd_read(sb, pos, buf, buflen); 232 #endif 233 #ifdef CONFIG_ROMFS_ON_BLOCK 234 if (sb->s_bdev) 235 return romfs_blk_read(sb, pos, buf, buflen); 236 #endif 237 return -EIO; 238 } 239 240 /* 241 * determine the length of a string in romfs 242 */ 243 ssize_t romfs_dev_strnlen(struct super_block *sb, 244 unsigned long pos, size_t maxlen) 245 { 246 size_t limit; 247 248 limit = romfs_maxsize(sb); 249 if (pos >= limit) 250 return -EIO; 251 if (maxlen > limit - pos) 252 maxlen = limit - pos; 253 254 #ifdef CONFIG_ROMFS_ON_MTD 255 if (sb->s_mtd) 256 return romfs_mtd_strnlen(sb, pos, maxlen); 257 #endif 258 #ifdef CONFIG_ROMFS_ON_BLOCK 259 if (sb->s_bdev) 260 return romfs_blk_strnlen(sb, pos, maxlen); 261 #endif 262 return -EIO; 263 } 264 265 /* 266 * compare a string to one in romfs 267 * - the string to be compared to, str, may not be NUL-terminated; instead the 268 * string is of the specified size 269 * - return 1 if matched, 0 if differ, -ve if error 270 */ 271 int romfs_dev_strcmp(struct super_block *sb, unsigned long pos, 272 const char *str, size_t size) 273 { 274 size_t limit; 275 276 limit = romfs_maxsize(sb); 277 if (pos >= limit) 278 return -EIO; 279 if (size > ROMFS_MAXFN) 280 return -ENAMETOOLONG; 281 if (size + 1 > limit - pos) 282 return -EIO; 283 284 #ifdef CONFIG_ROMFS_ON_MTD 285 if (sb->s_mtd) 286 return romfs_mtd_strcmp(sb, pos, str, size); 287 #endif 288 #ifdef CONFIG_ROMFS_ON_BLOCK 289 if (sb->s_bdev) 290 return romfs_blk_strcmp(sb, pos, str, size); 291 #endif 292 return -EIO; 293 } 294