linux/fs/romfs/storage.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/* RomFS storage access routines
   3 *
   4 * Copyright © 2007 Red Hat, Inc. All Rights Reserved.
   5 * Written by David Howells (dhowells@redhat.com)
   6 */
   7
   8#include <linux/fs.h>
   9#include <linux/mtd/super.h>
  10#include <linux/buffer_head.h>
  11#include "internal.h"
  12
  13#if !defined(CONFIG_ROMFS_ON_MTD) && !defined(CONFIG_ROMFS_ON_BLOCK)
  14#error no ROMFS backing store interface configured
  15#endif
  16
  17#ifdef CONFIG_ROMFS_ON_MTD
  18#define ROMFS_MTD_READ(sb, ...) mtd_read((sb)->s_mtd, ##__VA_ARGS__)
  19
  20/*
  21 * read data from an romfs image on an MTD device
  22 */
  23static int romfs_mtd_read(struct super_block *sb, unsigned long pos,
  24                          void *buf, size_t buflen)
  25{
  26        size_t rlen;
  27        int ret;
  28
  29        ret = ROMFS_MTD_READ(sb, pos, buflen, &rlen, buf);
  30        return (ret < 0 || rlen != buflen) ? -EIO : 0;
  31}
  32
  33/*
  34 * determine the length of a string in a romfs image on an MTD device
  35 */
  36static ssize_t romfs_mtd_strnlen(struct super_block *sb,
  37                                 unsigned long pos, size_t maxlen)
  38{
  39        ssize_t n = 0;
  40        size_t segment;
  41        u_char buf[16], *p;
  42        size_t len;
  43        int ret;
  44
  45        /* scan the string up to 16 bytes at a time */
  46        while (maxlen > 0) {
  47                segment = min_t(size_t, maxlen, 16);
  48                ret = ROMFS_MTD_READ(sb, pos, segment, &len, buf);
  49                if (ret < 0)
  50                        return ret;
  51                p = memchr(buf, 0, len);
  52                if (p)
  53                        return n + (p - buf);
  54                maxlen -= len;
  55                pos += len;
  56                n += len;
  57        }
  58
  59        return n;
  60}
  61
  62/*
  63 * compare a string to one in a romfs image on MTD
  64 * - return 1 if matched, 0 if differ, -ve if error
  65 */
  66static int romfs_mtd_strcmp(struct super_block *sb, unsigned long pos,
  67                            const char *str, size_t size)
  68{
  69        u_char buf[17];
  70        size_t len, segment;
  71        int ret;
  72
  73        /* scan the string up to 16 bytes at a time, and attempt to grab the
  74         * trailing NUL whilst we're at it */
  75        buf[0] = 0xff;
  76
  77        while (size > 0) {
  78                segment = min_t(size_t, size + 1, 17);
  79                ret = ROMFS_MTD_READ(sb, pos, segment, &len, buf);
  80                if (ret < 0)
  81                        return ret;
  82                len--;
  83                if (memcmp(buf, str, len) != 0)
  84                        return 0;
  85                buf[0] = buf[len];
  86                size -= len;
  87                pos += len;
  88                str += len;
  89        }
  90
  91        /* check the trailing NUL was */
  92        if (buf[0])
  93                return 0;
  94
  95        return 1;
  96}
  97#endif /* CONFIG_ROMFS_ON_MTD */
  98
  99#ifdef CONFIG_ROMFS_ON_BLOCK
 100/*
 101 * read data from an romfs image on a block device
 102 */
 103static int romfs_blk_read(struct super_block *sb, unsigned long pos,
 104                          void *buf, size_t buflen)
 105{
 106        struct buffer_head *bh;
 107        unsigned long offset;
 108        size_t segment;
 109
 110        /* copy the string up to blocksize bytes at a time */
 111        while (buflen > 0) {
 112                offset = pos & (ROMBSIZE - 1);
 113                segment = min_t(size_t, buflen, ROMBSIZE - offset);
 114                bh = sb_bread(sb, pos >> ROMBSBITS);
 115                if (!bh)
 116                        return -EIO;
 117                memcpy(buf, bh->b_data + offset, segment);
 118                brelse(bh);
 119                buf += segment;
 120                buflen -= segment;
 121                pos += segment;
 122        }
 123
 124        return 0;
 125}
 126
 127/*
 128 * determine the length of a string in romfs on a block device
 129 */
 130static ssize_t romfs_blk_strnlen(struct super_block *sb,
 131                                 unsigned long pos, size_t limit)
 132{
 133        struct buffer_head *bh;
 134        unsigned long offset;
 135        ssize_t n = 0;
 136        size_t segment;
 137        u_char *buf, *p;
 138
 139        /* scan the string up to blocksize bytes at a time */
 140        while (limit > 0) {
 141                offset = pos & (ROMBSIZE - 1);
 142                segment = min_t(size_t, limit, ROMBSIZE - offset);
 143                bh = sb_bread(sb, pos >> ROMBSBITS);
 144                if (!bh)
 145                        return -EIO;
 146                buf = bh->b_data + offset;
 147                p = memchr(buf, 0, segment);
 148                brelse(bh);
 149                if (p)
 150                        return n + (p - buf);
 151                limit -= segment;
 152                pos += segment;
 153                n += segment;
 154        }
 155
 156        return n;
 157}
 158
 159/*
 160 * compare a string to one in a romfs image on a block device
 161 * - return 1 if matched, 0 if differ, -ve if error
 162 */
 163static int romfs_blk_strcmp(struct super_block *sb, unsigned long pos,
 164                            const char *str, size_t size)
 165{
 166        struct buffer_head *bh;
 167        unsigned long offset;
 168        size_t segment;
 169        bool matched, terminated = false;
 170
 171        /* compare string up to a block at a time */
 172        while (size > 0) {
 173                offset = pos & (ROMBSIZE - 1);
 174                segment = min_t(size_t, size, ROMBSIZE - offset);
 175                bh = sb_bread(sb, pos >> ROMBSBITS);
 176                if (!bh)
 177                        return -EIO;
 178                matched = (memcmp(bh->b_data + offset, str, segment) == 0);
 179
 180                size -= segment;
 181                pos += segment;
 182                str += segment;
 183                if (matched && size == 0 && offset + segment < ROMBSIZE) {
 184                        if (!bh->b_data[offset + segment])
 185                                terminated = true;
 186                        else
 187                                matched = false;
 188                }
 189                brelse(bh);
 190                if (!matched)
 191                        return 0;
 192        }
 193
 194        if (!terminated) {
 195                /* the terminating NUL must be on the first byte of the next
 196                 * block */
 197                BUG_ON((pos & (ROMBSIZE - 1)) != 0);
 198                bh = sb_bread(sb, pos >> ROMBSBITS);
 199                if (!bh)
 200                        return -EIO;
 201                matched = !bh->b_data[0];
 202                brelse(bh);
 203                if (!matched)
 204                        return 0;
 205        }
 206
 207        return 1;
 208}
 209#endif /* CONFIG_ROMFS_ON_BLOCK */
 210
 211/*
 212 * read data from the romfs image
 213 */
 214int romfs_dev_read(struct super_block *sb, unsigned long pos,
 215                   void *buf, size_t buflen)
 216{
 217        size_t limit;
 218
 219        limit = romfs_maxsize(sb);
 220        if (pos >= limit || buflen > limit - pos)
 221                return -EIO;
 222
 223#ifdef CONFIG_ROMFS_ON_MTD
 224        if (sb->s_mtd)
 225                return romfs_mtd_read(sb, pos, buf, buflen);
 226#endif
 227#ifdef CONFIG_ROMFS_ON_BLOCK
 228        if (sb->s_bdev)
 229                return romfs_blk_read(sb, pos, buf, buflen);
 230#endif
 231        return -EIO;
 232}
 233
 234/*
 235 * determine the length of a string in romfs
 236 */
 237ssize_t romfs_dev_strnlen(struct super_block *sb,
 238                          unsigned long pos, size_t maxlen)
 239{
 240        size_t limit;
 241
 242        limit = romfs_maxsize(sb);
 243        if (pos >= limit)
 244                return -EIO;
 245        if (maxlen > limit - pos)
 246                maxlen = limit - pos;
 247
 248#ifdef CONFIG_ROMFS_ON_MTD
 249        if (sb->s_mtd)
 250                return romfs_mtd_strnlen(sb, pos, maxlen);
 251#endif
 252#ifdef CONFIG_ROMFS_ON_BLOCK
 253        if (sb->s_bdev)
 254                return romfs_blk_strnlen(sb, pos, maxlen);
 255#endif
 256        return -EIO;
 257}
 258
 259/*
 260 * compare a string to one in romfs
 261 * - the string to be compared to, str, may not be NUL-terminated; instead the
 262 *   string is of the specified size
 263 * - return 1 if matched, 0 if differ, -ve if error
 264 */
 265int romfs_dev_strcmp(struct super_block *sb, unsigned long pos,
 266                     const char *str, size_t size)
 267{
 268        size_t limit;
 269
 270        limit = romfs_maxsize(sb);
 271        if (pos >= limit)
 272                return -EIO;
 273        if (size > ROMFS_MAXFN)
 274                return -ENAMETOOLONG;
 275        if (size + 1 > limit - pos)
 276                return -EIO;
 277
 278#ifdef CONFIG_ROMFS_ON_MTD
 279        if (sb->s_mtd)
 280                return romfs_mtd_strcmp(sb, pos, str, size);
 281#endif
 282#ifdef CONFIG_ROMFS_ON_BLOCK
 283        if (sb->s_bdev)
 284                return romfs_blk_strcmp(sb, pos, str, size);
 285#endif
 286        return -EIO;
 287}
 288