xref: /kernel/linux/linux-5.10/fs/romfs/storage.c (revision 8c2ecf20)
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