162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* RomFS storage access routines 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright © 2007 Red Hat, Inc. All Rights Reserved. 562306a36Sopenharmony_ci * Written by David Howells (dhowells@redhat.com) 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/fs.h> 962306a36Sopenharmony_ci#include <linux/mtd/super.h> 1062306a36Sopenharmony_ci#include <linux/buffer_head.h> 1162306a36Sopenharmony_ci#include "internal.h" 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#if !defined(CONFIG_ROMFS_ON_MTD) && !defined(CONFIG_ROMFS_ON_BLOCK) 1462306a36Sopenharmony_ci#error no ROMFS backing store interface configured 1562306a36Sopenharmony_ci#endif 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#ifdef CONFIG_ROMFS_ON_MTD 1862306a36Sopenharmony_ci#define ROMFS_MTD_READ(sb, ...) mtd_read((sb)->s_mtd, ##__VA_ARGS__) 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci/* 2162306a36Sopenharmony_ci * read data from an romfs image on an MTD device 2262306a36Sopenharmony_ci */ 2362306a36Sopenharmony_cistatic int romfs_mtd_read(struct super_block *sb, unsigned long pos, 2462306a36Sopenharmony_ci void *buf, size_t buflen) 2562306a36Sopenharmony_ci{ 2662306a36Sopenharmony_ci size_t rlen; 2762306a36Sopenharmony_ci int ret; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci ret = ROMFS_MTD_READ(sb, pos, buflen, &rlen, buf); 3062306a36Sopenharmony_ci return (ret < 0 || rlen != buflen) ? -EIO : 0; 3162306a36Sopenharmony_ci} 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci/* 3462306a36Sopenharmony_ci * determine the length of a string in a romfs image on an MTD device 3562306a36Sopenharmony_ci */ 3662306a36Sopenharmony_cistatic ssize_t romfs_mtd_strnlen(struct super_block *sb, 3762306a36Sopenharmony_ci unsigned long pos, size_t maxlen) 3862306a36Sopenharmony_ci{ 3962306a36Sopenharmony_ci ssize_t n = 0; 4062306a36Sopenharmony_ci size_t segment; 4162306a36Sopenharmony_ci u_char buf[16], *p; 4262306a36Sopenharmony_ci size_t len; 4362306a36Sopenharmony_ci int ret; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci /* scan the string up to 16 bytes at a time */ 4662306a36Sopenharmony_ci while (maxlen > 0) { 4762306a36Sopenharmony_ci segment = min_t(size_t, maxlen, 16); 4862306a36Sopenharmony_ci ret = ROMFS_MTD_READ(sb, pos, segment, &len, buf); 4962306a36Sopenharmony_ci if (ret < 0) 5062306a36Sopenharmony_ci return ret; 5162306a36Sopenharmony_ci p = memchr(buf, 0, len); 5262306a36Sopenharmony_ci if (p) 5362306a36Sopenharmony_ci return n + (p - buf); 5462306a36Sopenharmony_ci maxlen -= len; 5562306a36Sopenharmony_ci pos += len; 5662306a36Sopenharmony_ci n += len; 5762306a36Sopenharmony_ci } 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci return n; 6062306a36Sopenharmony_ci} 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci/* 6362306a36Sopenharmony_ci * compare a string to one in a romfs image on MTD 6462306a36Sopenharmony_ci * - return 1 if matched, 0 if differ, -ve if error 6562306a36Sopenharmony_ci */ 6662306a36Sopenharmony_cistatic int romfs_mtd_strcmp(struct super_block *sb, unsigned long pos, 6762306a36Sopenharmony_ci const char *str, size_t size) 6862306a36Sopenharmony_ci{ 6962306a36Sopenharmony_ci u_char buf[17]; 7062306a36Sopenharmony_ci size_t len, segment; 7162306a36Sopenharmony_ci int ret; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci /* scan the string up to 16 bytes at a time, and attempt to grab the 7462306a36Sopenharmony_ci * trailing NUL whilst we're at it */ 7562306a36Sopenharmony_ci buf[0] = 0xff; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci while (size > 0) { 7862306a36Sopenharmony_ci segment = min_t(size_t, size + 1, 17); 7962306a36Sopenharmony_ci ret = ROMFS_MTD_READ(sb, pos, segment, &len, buf); 8062306a36Sopenharmony_ci if (ret < 0) 8162306a36Sopenharmony_ci return ret; 8262306a36Sopenharmony_ci len--; 8362306a36Sopenharmony_ci if (memcmp(buf, str, len) != 0) 8462306a36Sopenharmony_ci return 0; 8562306a36Sopenharmony_ci buf[0] = buf[len]; 8662306a36Sopenharmony_ci size -= len; 8762306a36Sopenharmony_ci pos += len; 8862306a36Sopenharmony_ci str += len; 8962306a36Sopenharmony_ci } 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci /* check the trailing NUL was */ 9262306a36Sopenharmony_ci if (buf[0]) 9362306a36Sopenharmony_ci return 0; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci return 1; 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ci#endif /* CONFIG_ROMFS_ON_MTD */ 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci#ifdef CONFIG_ROMFS_ON_BLOCK 10062306a36Sopenharmony_ci/* 10162306a36Sopenharmony_ci * read data from an romfs image on a block device 10262306a36Sopenharmony_ci */ 10362306a36Sopenharmony_cistatic int romfs_blk_read(struct super_block *sb, unsigned long pos, 10462306a36Sopenharmony_ci void *buf, size_t buflen) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci struct buffer_head *bh; 10762306a36Sopenharmony_ci unsigned long offset; 10862306a36Sopenharmony_ci size_t segment; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci /* copy the string up to blocksize bytes at a time */ 11162306a36Sopenharmony_ci while (buflen > 0) { 11262306a36Sopenharmony_ci offset = pos & (ROMBSIZE - 1); 11362306a36Sopenharmony_ci segment = min_t(size_t, buflen, ROMBSIZE - offset); 11462306a36Sopenharmony_ci bh = sb_bread(sb, pos >> ROMBSBITS); 11562306a36Sopenharmony_ci if (!bh) 11662306a36Sopenharmony_ci return -EIO; 11762306a36Sopenharmony_ci memcpy(buf, bh->b_data + offset, segment); 11862306a36Sopenharmony_ci brelse(bh); 11962306a36Sopenharmony_ci buf += segment; 12062306a36Sopenharmony_ci buflen -= segment; 12162306a36Sopenharmony_ci pos += segment; 12262306a36Sopenharmony_ci } 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci return 0; 12562306a36Sopenharmony_ci} 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci/* 12862306a36Sopenharmony_ci * determine the length of a string in romfs on a block device 12962306a36Sopenharmony_ci */ 13062306a36Sopenharmony_cistatic ssize_t romfs_blk_strnlen(struct super_block *sb, 13162306a36Sopenharmony_ci unsigned long pos, size_t limit) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci struct buffer_head *bh; 13462306a36Sopenharmony_ci unsigned long offset; 13562306a36Sopenharmony_ci ssize_t n = 0; 13662306a36Sopenharmony_ci size_t segment; 13762306a36Sopenharmony_ci u_char *buf, *p; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci /* scan the string up to blocksize bytes at a time */ 14062306a36Sopenharmony_ci while (limit > 0) { 14162306a36Sopenharmony_ci offset = pos & (ROMBSIZE - 1); 14262306a36Sopenharmony_ci segment = min_t(size_t, limit, ROMBSIZE - offset); 14362306a36Sopenharmony_ci bh = sb_bread(sb, pos >> ROMBSBITS); 14462306a36Sopenharmony_ci if (!bh) 14562306a36Sopenharmony_ci return -EIO; 14662306a36Sopenharmony_ci buf = bh->b_data + offset; 14762306a36Sopenharmony_ci p = memchr(buf, 0, segment); 14862306a36Sopenharmony_ci brelse(bh); 14962306a36Sopenharmony_ci if (p) 15062306a36Sopenharmony_ci return n + (p - buf); 15162306a36Sopenharmony_ci limit -= segment; 15262306a36Sopenharmony_ci pos += segment; 15362306a36Sopenharmony_ci n += segment; 15462306a36Sopenharmony_ci } 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci return n; 15762306a36Sopenharmony_ci} 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci/* 16062306a36Sopenharmony_ci * compare a string to one in a romfs image on a block device 16162306a36Sopenharmony_ci * - return 1 if matched, 0 if differ, -ve if error 16262306a36Sopenharmony_ci */ 16362306a36Sopenharmony_cistatic int romfs_blk_strcmp(struct super_block *sb, unsigned long pos, 16462306a36Sopenharmony_ci const char *str, size_t size) 16562306a36Sopenharmony_ci{ 16662306a36Sopenharmony_ci struct buffer_head *bh; 16762306a36Sopenharmony_ci unsigned long offset; 16862306a36Sopenharmony_ci size_t segment; 16962306a36Sopenharmony_ci bool matched, terminated = false; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci /* compare string up to a block at a time */ 17262306a36Sopenharmony_ci while (size > 0) { 17362306a36Sopenharmony_ci offset = pos & (ROMBSIZE - 1); 17462306a36Sopenharmony_ci segment = min_t(size_t, size, ROMBSIZE - offset); 17562306a36Sopenharmony_ci bh = sb_bread(sb, pos >> ROMBSBITS); 17662306a36Sopenharmony_ci if (!bh) 17762306a36Sopenharmony_ci return -EIO; 17862306a36Sopenharmony_ci matched = (memcmp(bh->b_data + offset, str, segment) == 0); 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci size -= segment; 18162306a36Sopenharmony_ci pos += segment; 18262306a36Sopenharmony_ci str += segment; 18362306a36Sopenharmony_ci if (matched && size == 0 && offset + segment < ROMBSIZE) { 18462306a36Sopenharmony_ci if (!bh->b_data[offset + segment]) 18562306a36Sopenharmony_ci terminated = true; 18662306a36Sopenharmony_ci else 18762306a36Sopenharmony_ci matched = false; 18862306a36Sopenharmony_ci } 18962306a36Sopenharmony_ci brelse(bh); 19062306a36Sopenharmony_ci if (!matched) 19162306a36Sopenharmony_ci return 0; 19262306a36Sopenharmony_ci } 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci if (!terminated) { 19562306a36Sopenharmony_ci /* the terminating NUL must be on the first byte of the next 19662306a36Sopenharmony_ci * block */ 19762306a36Sopenharmony_ci BUG_ON((pos & (ROMBSIZE - 1)) != 0); 19862306a36Sopenharmony_ci bh = sb_bread(sb, pos >> ROMBSBITS); 19962306a36Sopenharmony_ci if (!bh) 20062306a36Sopenharmony_ci return -EIO; 20162306a36Sopenharmony_ci matched = !bh->b_data[0]; 20262306a36Sopenharmony_ci brelse(bh); 20362306a36Sopenharmony_ci if (!matched) 20462306a36Sopenharmony_ci return 0; 20562306a36Sopenharmony_ci } 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci return 1; 20862306a36Sopenharmony_ci} 20962306a36Sopenharmony_ci#endif /* CONFIG_ROMFS_ON_BLOCK */ 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci/* 21262306a36Sopenharmony_ci * read data from the romfs image 21362306a36Sopenharmony_ci */ 21462306a36Sopenharmony_ciint romfs_dev_read(struct super_block *sb, unsigned long pos, 21562306a36Sopenharmony_ci void *buf, size_t buflen) 21662306a36Sopenharmony_ci{ 21762306a36Sopenharmony_ci size_t limit; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci limit = romfs_maxsize(sb); 22062306a36Sopenharmony_ci if (pos >= limit || buflen > limit - pos) 22162306a36Sopenharmony_ci return -EIO; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci#ifdef CONFIG_ROMFS_ON_MTD 22462306a36Sopenharmony_ci if (sb->s_mtd) 22562306a36Sopenharmony_ci return romfs_mtd_read(sb, pos, buf, buflen); 22662306a36Sopenharmony_ci#endif 22762306a36Sopenharmony_ci#ifdef CONFIG_ROMFS_ON_BLOCK 22862306a36Sopenharmony_ci if (sb->s_bdev) 22962306a36Sopenharmony_ci return romfs_blk_read(sb, pos, buf, buflen); 23062306a36Sopenharmony_ci#endif 23162306a36Sopenharmony_ci return -EIO; 23262306a36Sopenharmony_ci} 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci/* 23562306a36Sopenharmony_ci * determine the length of a string in romfs 23662306a36Sopenharmony_ci */ 23762306a36Sopenharmony_cissize_t romfs_dev_strnlen(struct super_block *sb, 23862306a36Sopenharmony_ci unsigned long pos, size_t maxlen) 23962306a36Sopenharmony_ci{ 24062306a36Sopenharmony_ci size_t limit; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci limit = romfs_maxsize(sb); 24362306a36Sopenharmony_ci if (pos >= limit) 24462306a36Sopenharmony_ci return -EIO; 24562306a36Sopenharmony_ci if (maxlen > limit - pos) 24662306a36Sopenharmony_ci maxlen = limit - pos; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci#ifdef CONFIG_ROMFS_ON_MTD 24962306a36Sopenharmony_ci if (sb->s_mtd) 25062306a36Sopenharmony_ci return romfs_mtd_strnlen(sb, pos, maxlen); 25162306a36Sopenharmony_ci#endif 25262306a36Sopenharmony_ci#ifdef CONFIG_ROMFS_ON_BLOCK 25362306a36Sopenharmony_ci if (sb->s_bdev) 25462306a36Sopenharmony_ci return romfs_blk_strnlen(sb, pos, maxlen); 25562306a36Sopenharmony_ci#endif 25662306a36Sopenharmony_ci return -EIO; 25762306a36Sopenharmony_ci} 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci/* 26062306a36Sopenharmony_ci * compare a string to one in romfs 26162306a36Sopenharmony_ci * - the string to be compared to, str, may not be NUL-terminated; instead the 26262306a36Sopenharmony_ci * string is of the specified size 26362306a36Sopenharmony_ci * - return 1 if matched, 0 if differ, -ve if error 26462306a36Sopenharmony_ci */ 26562306a36Sopenharmony_ciint romfs_dev_strcmp(struct super_block *sb, unsigned long pos, 26662306a36Sopenharmony_ci const char *str, size_t size) 26762306a36Sopenharmony_ci{ 26862306a36Sopenharmony_ci size_t limit; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci limit = romfs_maxsize(sb); 27162306a36Sopenharmony_ci if (pos >= limit) 27262306a36Sopenharmony_ci return -EIO; 27362306a36Sopenharmony_ci if (size > ROMFS_MAXFN) 27462306a36Sopenharmony_ci return -ENAMETOOLONG; 27562306a36Sopenharmony_ci if (size + 1 > limit - pos) 27662306a36Sopenharmony_ci return -EIO; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci#ifdef CONFIG_ROMFS_ON_MTD 27962306a36Sopenharmony_ci if (sb->s_mtd) 28062306a36Sopenharmony_ci return romfs_mtd_strcmp(sb, pos, str, size); 28162306a36Sopenharmony_ci#endif 28262306a36Sopenharmony_ci#ifdef CONFIG_ROMFS_ON_BLOCK 28362306a36Sopenharmony_ci if (sb->s_bdev) 28462306a36Sopenharmony_ci return romfs_blk_strcmp(sb, pos, str, size); 28562306a36Sopenharmony_ci#endif 28662306a36Sopenharmony_ci return -EIO; 28762306a36Sopenharmony_ci} 288