18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* RomFS storage access routines 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright © 2007 Red Hat, Inc. All Rights Reserved. 58c2ecf20Sopenharmony_ci * Written by David Howells (dhowells@redhat.com) 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/fs.h> 98c2ecf20Sopenharmony_ci#include <linux/mtd/super.h> 108c2ecf20Sopenharmony_ci#include <linux/buffer_head.h> 118c2ecf20Sopenharmony_ci#include "internal.h" 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#if !defined(CONFIG_ROMFS_ON_MTD) && !defined(CONFIG_ROMFS_ON_BLOCK) 148c2ecf20Sopenharmony_ci#error no ROMFS backing store interface configured 158c2ecf20Sopenharmony_ci#endif 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#ifdef CONFIG_ROMFS_ON_MTD 188c2ecf20Sopenharmony_ci#define ROMFS_MTD_READ(sb, ...) mtd_read((sb)->s_mtd, ##__VA_ARGS__) 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci/* 218c2ecf20Sopenharmony_ci * read data from an romfs image on an MTD device 228c2ecf20Sopenharmony_ci */ 238c2ecf20Sopenharmony_cistatic int romfs_mtd_read(struct super_block *sb, unsigned long pos, 248c2ecf20Sopenharmony_ci void *buf, size_t buflen) 258c2ecf20Sopenharmony_ci{ 268c2ecf20Sopenharmony_ci size_t rlen; 278c2ecf20Sopenharmony_ci int ret; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci ret = ROMFS_MTD_READ(sb, pos, buflen, &rlen, buf); 308c2ecf20Sopenharmony_ci return (ret < 0 || rlen != buflen) ? -EIO : 0; 318c2ecf20Sopenharmony_ci} 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci/* 348c2ecf20Sopenharmony_ci * determine the length of a string in a romfs image on an MTD device 358c2ecf20Sopenharmony_ci */ 368c2ecf20Sopenharmony_cistatic ssize_t romfs_mtd_strnlen(struct super_block *sb, 378c2ecf20Sopenharmony_ci unsigned long pos, size_t maxlen) 388c2ecf20Sopenharmony_ci{ 398c2ecf20Sopenharmony_ci ssize_t n = 0; 408c2ecf20Sopenharmony_ci size_t segment; 418c2ecf20Sopenharmony_ci u_char buf[16], *p; 428c2ecf20Sopenharmony_ci size_t len; 438c2ecf20Sopenharmony_ci int ret; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci /* scan the string up to 16 bytes at a time */ 468c2ecf20Sopenharmony_ci while (maxlen > 0) { 478c2ecf20Sopenharmony_ci segment = min_t(size_t, maxlen, 16); 488c2ecf20Sopenharmony_ci ret = ROMFS_MTD_READ(sb, pos, segment, &len, buf); 498c2ecf20Sopenharmony_ci if (ret < 0) 508c2ecf20Sopenharmony_ci return ret; 518c2ecf20Sopenharmony_ci p = memchr(buf, 0, len); 528c2ecf20Sopenharmony_ci if (p) 538c2ecf20Sopenharmony_ci return n + (p - buf); 548c2ecf20Sopenharmony_ci maxlen -= len; 558c2ecf20Sopenharmony_ci pos += len; 568c2ecf20Sopenharmony_ci n += len; 578c2ecf20Sopenharmony_ci } 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci return n; 608c2ecf20Sopenharmony_ci} 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci/* 638c2ecf20Sopenharmony_ci * compare a string to one in a romfs image on MTD 648c2ecf20Sopenharmony_ci * - return 1 if matched, 0 if differ, -ve if error 658c2ecf20Sopenharmony_ci */ 668c2ecf20Sopenharmony_cistatic int romfs_mtd_strcmp(struct super_block *sb, unsigned long pos, 678c2ecf20Sopenharmony_ci const char *str, size_t size) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci u_char buf[17]; 708c2ecf20Sopenharmony_ci size_t len, segment; 718c2ecf20Sopenharmony_ci int ret; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci /* scan the string up to 16 bytes at a time, and attempt to grab the 748c2ecf20Sopenharmony_ci * trailing NUL whilst we're at it */ 758c2ecf20Sopenharmony_ci buf[0] = 0xff; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci while (size > 0) { 788c2ecf20Sopenharmony_ci segment = min_t(size_t, size + 1, 17); 798c2ecf20Sopenharmony_ci ret = ROMFS_MTD_READ(sb, pos, segment, &len, buf); 808c2ecf20Sopenharmony_ci if (ret < 0) 818c2ecf20Sopenharmony_ci return ret; 828c2ecf20Sopenharmony_ci len--; 838c2ecf20Sopenharmony_ci if (memcmp(buf, str, len) != 0) 848c2ecf20Sopenharmony_ci return 0; 858c2ecf20Sopenharmony_ci buf[0] = buf[len]; 868c2ecf20Sopenharmony_ci size -= len; 878c2ecf20Sopenharmony_ci pos += len; 888c2ecf20Sopenharmony_ci str += len; 898c2ecf20Sopenharmony_ci } 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci /* check the trailing NUL was */ 928c2ecf20Sopenharmony_ci if (buf[0]) 938c2ecf20Sopenharmony_ci return 0; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci return 1; 968c2ecf20Sopenharmony_ci} 978c2ecf20Sopenharmony_ci#endif /* CONFIG_ROMFS_ON_MTD */ 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci#ifdef CONFIG_ROMFS_ON_BLOCK 1008c2ecf20Sopenharmony_ci/* 1018c2ecf20Sopenharmony_ci * read data from an romfs image on a block device 1028c2ecf20Sopenharmony_ci */ 1038c2ecf20Sopenharmony_cistatic int romfs_blk_read(struct super_block *sb, unsigned long pos, 1048c2ecf20Sopenharmony_ci void *buf, size_t buflen) 1058c2ecf20Sopenharmony_ci{ 1068c2ecf20Sopenharmony_ci struct buffer_head *bh; 1078c2ecf20Sopenharmony_ci unsigned long offset; 1088c2ecf20Sopenharmony_ci size_t segment; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci /* copy the string up to blocksize bytes at a time */ 1118c2ecf20Sopenharmony_ci while (buflen > 0) { 1128c2ecf20Sopenharmony_ci offset = pos & (ROMBSIZE - 1); 1138c2ecf20Sopenharmony_ci segment = min_t(size_t, buflen, ROMBSIZE - offset); 1148c2ecf20Sopenharmony_ci bh = sb_bread(sb, pos >> ROMBSBITS); 1158c2ecf20Sopenharmony_ci if (!bh) 1168c2ecf20Sopenharmony_ci return -EIO; 1178c2ecf20Sopenharmony_ci memcpy(buf, bh->b_data + offset, segment); 1188c2ecf20Sopenharmony_ci brelse(bh); 1198c2ecf20Sopenharmony_ci buf += segment; 1208c2ecf20Sopenharmony_ci buflen -= segment; 1218c2ecf20Sopenharmony_ci pos += segment; 1228c2ecf20Sopenharmony_ci } 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci return 0; 1258c2ecf20Sopenharmony_ci} 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci/* 1288c2ecf20Sopenharmony_ci * determine the length of a string in romfs on a block device 1298c2ecf20Sopenharmony_ci */ 1308c2ecf20Sopenharmony_cistatic ssize_t romfs_blk_strnlen(struct super_block *sb, 1318c2ecf20Sopenharmony_ci unsigned long pos, size_t limit) 1328c2ecf20Sopenharmony_ci{ 1338c2ecf20Sopenharmony_ci struct buffer_head *bh; 1348c2ecf20Sopenharmony_ci unsigned long offset; 1358c2ecf20Sopenharmony_ci ssize_t n = 0; 1368c2ecf20Sopenharmony_ci size_t segment; 1378c2ecf20Sopenharmony_ci u_char *buf, *p; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci /* scan the string up to blocksize bytes at a time */ 1408c2ecf20Sopenharmony_ci while (limit > 0) { 1418c2ecf20Sopenharmony_ci offset = pos & (ROMBSIZE - 1); 1428c2ecf20Sopenharmony_ci segment = min_t(size_t, limit, ROMBSIZE - offset); 1438c2ecf20Sopenharmony_ci bh = sb_bread(sb, pos >> ROMBSBITS); 1448c2ecf20Sopenharmony_ci if (!bh) 1458c2ecf20Sopenharmony_ci return -EIO; 1468c2ecf20Sopenharmony_ci buf = bh->b_data + offset; 1478c2ecf20Sopenharmony_ci p = memchr(buf, 0, segment); 1488c2ecf20Sopenharmony_ci brelse(bh); 1498c2ecf20Sopenharmony_ci if (p) 1508c2ecf20Sopenharmony_ci return n + (p - buf); 1518c2ecf20Sopenharmony_ci limit -= segment; 1528c2ecf20Sopenharmony_ci pos += segment; 1538c2ecf20Sopenharmony_ci n += segment; 1548c2ecf20Sopenharmony_ci } 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci return n; 1578c2ecf20Sopenharmony_ci} 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci/* 1608c2ecf20Sopenharmony_ci * compare a string to one in a romfs image on a block device 1618c2ecf20Sopenharmony_ci * - return 1 if matched, 0 if differ, -ve if error 1628c2ecf20Sopenharmony_ci */ 1638c2ecf20Sopenharmony_cistatic int romfs_blk_strcmp(struct super_block *sb, unsigned long pos, 1648c2ecf20Sopenharmony_ci const char *str, size_t size) 1658c2ecf20Sopenharmony_ci{ 1668c2ecf20Sopenharmony_ci struct buffer_head *bh; 1678c2ecf20Sopenharmony_ci unsigned long offset; 1688c2ecf20Sopenharmony_ci size_t segment; 1698c2ecf20Sopenharmony_ci bool matched, terminated = false; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci /* compare string up to a block at a time */ 1728c2ecf20Sopenharmony_ci while (size > 0) { 1738c2ecf20Sopenharmony_ci offset = pos & (ROMBSIZE - 1); 1748c2ecf20Sopenharmony_ci segment = min_t(size_t, size, ROMBSIZE - offset); 1758c2ecf20Sopenharmony_ci bh = sb_bread(sb, pos >> ROMBSBITS); 1768c2ecf20Sopenharmony_ci if (!bh) 1778c2ecf20Sopenharmony_ci return -EIO; 1788c2ecf20Sopenharmony_ci matched = (memcmp(bh->b_data + offset, str, segment) == 0); 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci size -= segment; 1818c2ecf20Sopenharmony_ci pos += segment; 1828c2ecf20Sopenharmony_ci str += segment; 1838c2ecf20Sopenharmony_ci if (matched && size == 0 && offset + segment < ROMBSIZE) { 1848c2ecf20Sopenharmony_ci if (!bh->b_data[offset + segment]) 1858c2ecf20Sopenharmony_ci terminated = true; 1868c2ecf20Sopenharmony_ci else 1878c2ecf20Sopenharmony_ci matched = false; 1888c2ecf20Sopenharmony_ci } 1898c2ecf20Sopenharmony_ci brelse(bh); 1908c2ecf20Sopenharmony_ci if (!matched) 1918c2ecf20Sopenharmony_ci return 0; 1928c2ecf20Sopenharmony_ci } 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci if (!terminated) { 1958c2ecf20Sopenharmony_ci /* the terminating NUL must be on the first byte of the next 1968c2ecf20Sopenharmony_ci * block */ 1978c2ecf20Sopenharmony_ci BUG_ON((pos & (ROMBSIZE - 1)) != 0); 1988c2ecf20Sopenharmony_ci bh = sb_bread(sb, pos >> ROMBSBITS); 1998c2ecf20Sopenharmony_ci if (!bh) 2008c2ecf20Sopenharmony_ci return -EIO; 2018c2ecf20Sopenharmony_ci matched = !bh->b_data[0]; 2028c2ecf20Sopenharmony_ci brelse(bh); 2038c2ecf20Sopenharmony_ci if (!matched) 2048c2ecf20Sopenharmony_ci return 0; 2058c2ecf20Sopenharmony_ci } 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci return 1; 2088c2ecf20Sopenharmony_ci} 2098c2ecf20Sopenharmony_ci#endif /* CONFIG_ROMFS_ON_BLOCK */ 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci/* 2128c2ecf20Sopenharmony_ci * read data from the romfs image 2138c2ecf20Sopenharmony_ci */ 2148c2ecf20Sopenharmony_ciint romfs_dev_read(struct super_block *sb, unsigned long pos, 2158c2ecf20Sopenharmony_ci void *buf, size_t buflen) 2168c2ecf20Sopenharmony_ci{ 2178c2ecf20Sopenharmony_ci size_t limit; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci limit = romfs_maxsize(sb); 2208c2ecf20Sopenharmony_ci if (pos >= limit || buflen > limit - pos) 2218c2ecf20Sopenharmony_ci return -EIO; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci#ifdef CONFIG_ROMFS_ON_MTD 2248c2ecf20Sopenharmony_ci if (sb->s_mtd) 2258c2ecf20Sopenharmony_ci return romfs_mtd_read(sb, pos, buf, buflen); 2268c2ecf20Sopenharmony_ci#endif 2278c2ecf20Sopenharmony_ci#ifdef CONFIG_ROMFS_ON_BLOCK 2288c2ecf20Sopenharmony_ci if (sb->s_bdev) 2298c2ecf20Sopenharmony_ci return romfs_blk_read(sb, pos, buf, buflen); 2308c2ecf20Sopenharmony_ci#endif 2318c2ecf20Sopenharmony_ci return -EIO; 2328c2ecf20Sopenharmony_ci} 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci/* 2358c2ecf20Sopenharmony_ci * determine the length of a string in romfs 2368c2ecf20Sopenharmony_ci */ 2378c2ecf20Sopenharmony_cissize_t romfs_dev_strnlen(struct super_block *sb, 2388c2ecf20Sopenharmony_ci unsigned long pos, size_t maxlen) 2398c2ecf20Sopenharmony_ci{ 2408c2ecf20Sopenharmony_ci size_t limit; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci limit = romfs_maxsize(sb); 2438c2ecf20Sopenharmony_ci if (pos >= limit) 2448c2ecf20Sopenharmony_ci return -EIO; 2458c2ecf20Sopenharmony_ci if (maxlen > limit - pos) 2468c2ecf20Sopenharmony_ci maxlen = limit - pos; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci#ifdef CONFIG_ROMFS_ON_MTD 2498c2ecf20Sopenharmony_ci if (sb->s_mtd) 2508c2ecf20Sopenharmony_ci return romfs_mtd_strnlen(sb, pos, maxlen); 2518c2ecf20Sopenharmony_ci#endif 2528c2ecf20Sopenharmony_ci#ifdef CONFIG_ROMFS_ON_BLOCK 2538c2ecf20Sopenharmony_ci if (sb->s_bdev) 2548c2ecf20Sopenharmony_ci return romfs_blk_strnlen(sb, pos, maxlen); 2558c2ecf20Sopenharmony_ci#endif 2568c2ecf20Sopenharmony_ci return -EIO; 2578c2ecf20Sopenharmony_ci} 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci/* 2608c2ecf20Sopenharmony_ci * compare a string to one in romfs 2618c2ecf20Sopenharmony_ci * - the string to be compared to, str, may not be NUL-terminated; instead the 2628c2ecf20Sopenharmony_ci * string is of the specified size 2638c2ecf20Sopenharmony_ci * - return 1 if matched, 0 if differ, -ve if error 2648c2ecf20Sopenharmony_ci */ 2658c2ecf20Sopenharmony_ciint romfs_dev_strcmp(struct super_block *sb, unsigned long pos, 2668c2ecf20Sopenharmony_ci const char *str, size_t size) 2678c2ecf20Sopenharmony_ci{ 2688c2ecf20Sopenharmony_ci size_t limit; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci limit = romfs_maxsize(sb); 2718c2ecf20Sopenharmony_ci if (pos >= limit) 2728c2ecf20Sopenharmony_ci return -EIO; 2738c2ecf20Sopenharmony_ci if (size > ROMFS_MAXFN) 2748c2ecf20Sopenharmony_ci return -ENAMETOOLONG; 2758c2ecf20Sopenharmony_ci if (size + 1 > limit - pos) 2768c2ecf20Sopenharmony_ci return -EIO; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci#ifdef CONFIG_ROMFS_ON_MTD 2798c2ecf20Sopenharmony_ci if (sb->s_mtd) 2808c2ecf20Sopenharmony_ci return romfs_mtd_strcmp(sb, pos, str, size); 2818c2ecf20Sopenharmony_ci#endif 2828c2ecf20Sopenharmony_ci#ifdef CONFIG_ROMFS_ON_BLOCK 2838c2ecf20Sopenharmony_ci if (sb->s_bdev) 2848c2ecf20Sopenharmony_ci return romfs_blk_strcmp(sb, pos, str, size); 2858c2ecf20Sopenharmony_ci#endif 2868c2ecf20Sopenharmony_ci return -EIO; 2878c2ecf20Sopenharmony_ci} 288