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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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); 220da4458bdSDavid Howells if (pos >= limit) 221da4458bdSDavid Howells return -EIO; 222da4458bdSDavid Howells if (buflen > limit - pos) 223da4458bdSDavid Howells buflen = limit - pos; 224da4458bdSDavid Howells 225da4458bdSDavid Howells #ifdef CONFIG_ROMFS_ON_MTD 226da4458bdSDavid Howells if (sb->s_mtd) 227da4458bdSDavid Howells return romfs_mtd_read(sb, pos, buf, buflen); 228da4458bdSDavid Howells #endif 229da4458bdSDavid Howells #ifdef CONFIG_ROMFS_ON_BLOCK 230da4458bdSDavid Howells if (sb->s_bdev) 231da4458bdSDavid Howells return romfs_blk_read(sb, pos, buf, buflen); 232da4458bdSDavid Howells #endif 233da4458bdSDavid Howells return -EIO; 234da4458bdSDavid Howells } 235da4458bdSDavid Howells 236da4458bdSDavid Howells /* 237da4458bdSDavid Howells * determine the length of a string in romfs 238da4458bdSDavid Howells */ 239da4458bdSDavid Howells ssize_t romfs_dev_strnlen(struct super_block *sb, 240da4458bdSDavid Howells unsigned long pos, size_t maxlen) 241da4458bdSDavid Howells { 242da4458bdSDavid Howells size_t limit; 243da4458bdSDavid Howells 244da4458bdSDavid Howells limit = romfs_maxsize(sb); 245da4458bdSDavid Howells if (pos >= limit) 246da4458bdSDavid Howells return -EIO; 247da4458bdSDavid Howells if (maxlen > limit - pos) 248da4458bdSDavid Howells maxlen = limit - pos; 249da4458bdSDavid Howells 250da4458bdSDavid Howells #ifdef CONFIG_ROMFS_ON_MTD 251da4458bdSDavid Howells if (sb->s_mtd) 252ef1f7a7eSBernd Schmidt return romfs_mtd_strnlen(sb, pos, maxlen); 253da4458bdSDavid Howells #endif 254da4458bdSDavid Howells #ifdef CONFIG_ROMFS_ON_BLOCK 255da4458bdSDavid Howells if (sb->s_bdev) 256ef1f7a7eSBernd Schmidt return romfs_blk_strnlen(sb, pos, maxlen); 257da4458bdSDavid Howells #endif 258da4458bdSDavid Howells return -EIO; 259da4458bdSDavid Howells } 260da4458bdSDavid Howells 261da4458bdSDavid Howells /* 262da4458bdSDavid Howells * compare a string to one in romfs 26384baf74bSDavid Howells * - the string to be compared to, str, may not be NUL-terminated; instead the 26484baf74bSDavid Howells * string is of the specified size 265da4458bdSDavid Howells * - return 1 if matched, 0 if differ, -ve if error 266da4458bdSDavid Howells */ 26784baf74bSDavid Howells int romfs_dev_strcmp(struct super_block *sb, unsigned long pos, 268da4458bdSDavid Howells const char *str, size_t size) 269da4458bdSDavid Howells { 270da4458bdSDavid Howells size_t limit; 271da4458bdSDavid Howells 272da4458bdSDavid Howells limit = romfs_maxsize(sb); 273da4458bdSDavid Howells if (pos >= limit) 274da4458bdSDavid Howells return -EIO; 275da4458bdSDavid Howells if (size > ROMFS_MAXFN) 276da4458bdSDavid Howells return -ENAMETOOLONG; 27784baf74bSDavid Howells if (size + 1 > limit - pos) 278da4458bdSDavid Howells return -EIO; 279da4458bdSDavid Howells 280da4458bdSDavid Howells #ifdef CONFIG_ROMFS_ON_MTD 281da4458bdSDavid Howells if (sb->s_mtd) 28284baf74bSDavid Howells return romfs_mtd_strcmp(sb, pos, str, size); 283da4458bdSDavid Howells #endif 284da4458bdSDavid Howells #ifdef CONFIG_ROMFS_ON_BLOCK 285da4458bdSDavid Howells if (sb->s_bdev) 28684baf74bSDavid Howells return romfs_blk_strcmp(sb, pos, str, size); 287da4458bdSDavid Howells #endif 288da4458bdSDavid Howells return -EIO; 289da4458bdSDavid Howells } 290