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