linux-old/fs/romfs/inode.c
<<
>>
Prefs
   1/*
   2 * ROMFS file system, Linux implementation
   3 *
   4 * Copyright (C) 1997-1999  Janos Farkas <chexum@shadow.banki.hu>
   5 *
   6 * Using parts of the minix filesystem
   7 * Copyright (C) 1991, 1992  Linus Torvalds
   8 *
   9 * and parts of the affs filesystem additionally
  10 * Copyright (C) 1993  Ray Burr
  11 * Copyright (C) 1996  Hans-Joachim Widmaier
  12 *
  13 * This program is free software; you can redistribute it and/or
  14 * modify it under the terms of the GNU General Public License
  15 * as published by the Free Software Foundation; either version
  16 * 2 of the License, or (at your option) any later version.
  17 *
  18 * Changes
  19 *                                      Changed for 2.1.19 modules
  20 *      Jan 1997                        Initial release
  21 *      Jun 1997                        2.1.43+ changes
  22 *                                      Proper page locking in readpage
  23 *                                      Changed to work with 2.1.45+ fs
  24 *      Jul 1997                        Fixed follow_link
  25 *                      2.1.47
  26 *                                      lookup shouldn't return -ENOENT
  27 *                                      from Horst von Brand:
  28 *                                        fail on wrong checksum
  29 *                                        double unlock_super was possible
  30 *                                        correct namelen for statfs
  31 *                                      spotted by Bill Hawes:
  32 *                                        readlink shouldn't iput()
  33 *      Jun 1998        2.1.106         from Avery Pennarun: glibc scandir()
  34 *                                        exposed a problem in readdir
  35 *                      2.1.107         code-freeze spellchecker run
  36 *      Aug 1998                        2.1.118+ VFS changes
  37 *      Sep 1998        2.1.122         another VFS change (follow_link)
  38 *      Apr 1999        2.2.7           no more EBADF checking in
  39 *                                        lookup/readdir, use ERR_PTR
  40 *      Jun 1999        2.3.6           d_alloc_root use changed
  41 *                      2.3.9           clean up usage of ENOENT/negative
  42 *                                        dentries in lookup
  43 *                                      clean up page flags setting
  44 *                                        (error, uptodate, locking) in
  45 *                                        in readpage
  46 *                                      use init_special_inode for
  47 *                                        fifos/sockets (and streamline) in
  48 *                                        read_inode, fix _ops table order
  49 *      Aug 1999        2.3.16          __initfunc() => __init change
  50 *      Oct 1999        2.3.24          page->owner hack obsoleted
  51 *      Nov 1999        2.3.27          2.3.25+ page->offset => index change
  52 */
  53
  54/* todo:
  55 *      - see Documentation/filesystems/romfs.txt
  56 *      - use allocated, not stack memory for file names?
  57 *      - considering write access...
  58 *      - network (tftp) files?
  59 *      - merge back some _op tables
  60 */
  61
  62/*
  63 * Sorry about some optimizations and for some goto's.  I just wanted
  64 * to squeeze some more bytes out of this code.. :)
  65 */
  66
  67#include <linux/module.h>
  68#include <linux/types.h>
  69#include <linux/errno.h>
  70#include <linux/slab.h>
  71#include <linux/romfs_fs.h>
  72#include <linux/fs.h>
  73#include <linux/locks.h>
  74#include <linux/init.h>
  75#include <linux/smp_lock.h>
  76
  77#include <asm/uaccess.h>
  78
  79static __s32
  80romfs_checksum(void *data, int size)
  81{
  82        __s32 sum, *ptr;
  83
  84        sum = 0; ptr = data;
  85        size>>=2;
  86        while (size>0) {
  87                sum += ntohl(*ptr++);
  88                size--;
  89        }
  90        return sum;
  91}
  92
  93static struct super_operations romfs_ops;
  94
  95static struct super_block *
  96romfs_read_super(struct super_block *s, void *data, int silent)
  97{
  98        struct buffer_head *bh;
  99        kdev_t dev = s->s_dev;
 100        struct romfs_super_block *rsb;
 101        int sz;
 102
 103        /* I would parse the options here, but there are none.. :) */
 104
 105        set_blocksize(dev, ROMBSIZE);
 106        s->s_blocksize = ROMBSIZE;
 107        s->s_blocksize_bits = ROMBSBITS;
 108        s->u.generic_sbp = (void *) 0;
 109        s->s_maxbytes = 0xFFFFFFFF;
 110
 111        bh = sb_bread(s, 0);
 112        if (!bh) {
 113                /* XXX merge with other printk? */
 114                printk ("romfs: unable to read superblock\n");
 115                goto outnobh;
 116        }
 117
 118        rsb = (struct romfs_super_block *)bh->b_data;
 119        sz = ntohl(rsb->size);
 120        if (rsb->word0 != ROMSB_WORD0 || rsb->word1 != ROMSB_WORD1
 121           || sz < ROMFH_SIZE) {
 122                if (!silent)
 123                        printk ("VFS: Can't find a romfs filesystem on dev "
 124                                "%s.\n", kdevname(dev));
 125                goto out;
 126        }
 127        if (romfs_checksum(rsb, min_t(int, sz, 512))) {
 128                printk ("romfs: bad initial checksum on dev "
 129                        "%s.\n", kdevname(dev));
 130                goto out;
 131        }
 132
 133        s->s_magic = ROMFS_MAGIC;
 134        s->u.romfs_sb.s_maxsize = sz;
 135
 136        s->s_flags |= MS_RDONLY;
 137
 138        /* Find the start of the fs */
 139        sz = (ROMFH_SIZE +
 140              strnlen(rsb->name, ROMFS_MAXFN) + 1 + ROMFH_PAD)
 141             & ROMFH_MASK;
 142
 143        brelse(bh);
 144
 145        s->s_op = &romfs_ops;
 146        s->s_root = d_alloc_root(iget(s, sz));
 147
 148        if (!s->s_root)
 149                goto outnobh;
 150
 151        /* Ehrhm; sorry.. :)  And thanks to Hans-Joachim Widmaier  :) */
 152        if (0) {
 153out:
 154                brelse(bh);
 155outnobh:
 156                s = NULL;
 157        }
 158
 159        return s;
 160}
 161
 162/* That's simple too. */
 163
 164static int
 165romfs_statfs(struct super_block *sb, struct statfs *buf)
 166{
 167        buf->f_type = ROMFS_MAGIC;
 168        buf->f_bsize = ROMBSIZE;
 169        buf->f_bfree = buf->f_bavail = buf->f_ffree;
 170        buf->f_blocks = (sb->u.romfs_sb.s_maxsize+ROMBSIZE-1)>>ROMBSBITS;
 171        buf->f_namelen = ROMFS_MAXFN;
 172        return 0;
 173}
 174
 175/* some helper routines */
 176
 177static int
 178romfs_strnlen(struct inode *i, unsigned long offset, unsigned long count)
 179{
 180        struct buffer_head *bh;
 181        unsigned long avail, maxsize, res;
 182
 183        maxsize = i->i_sb->u.romfs_sb.s_maxsize;
 184        if (offset >= maxsize)
 185                return -1;
 186
 187        /* strnlen is almost always valid */
 188        if (count > maxsize || offset+count > maxsize)
 189                count = maxsize-offset;
 190
 191        bh = sb_bread(i->i_sb, offset>>ROMBSBITS);
 192        if (!bh)
 193                return -1;              /* error */
 194
 195        avail = ROMBSIZE - (offset & ROMBMASK);
 196        maxsize = min_t(unsigned long, count, avail);
 197        res = strnlen(((char *)bh->b_data)+(offset&ROMBMASK), maxsize);
 198        brelse(bh);
 199
 200        if (res < maxsize)
 201                return res;             /* found all of it */
 202
 203        while (res < count) {
 204                offset += maxsize;
 205
 206                bh = sb_bread(i->i_sb, offset>>ROMBSBITS);
 207                if (!bh)
 208                        return -1;
 209                maxsize = min_t(unsigned long, count - res, ROMBSIZE);
 210                avail = strnlen(bh->b_data, maxsize);
 211                res += avail;
 212                brelse(bh);
 213                if (avail < maxsize)
 214                        return res;
 215        }
 216        return res;
 217}
 218
 219static int
 220romfs_copyfrom(struct inode *i, void *dest, unsigned long offset, unsigned long count)
 221{
 222        struct buffer_head *bh;
 223        unsigned long avail, maxsize, res;
 224
 225        maxsize = i->i_sb->u.romfs_sb.s_maxsize;
 226        if (offset >= maxsize || count > maxsize || offset+count>maxsize)
 227                return -1;
 228
 229        bh = sb_bread(i->i_sb, offset>>ROMBSBITS);
 230        if (!bh)
 231                return -1;              /* error */
 232
 233        avail = ROMBSIZE - (offset & ROMBMASK);
 234        maxsize = min_t(unsigned long, count, avail);
 235        memcpy(dest, ((char *)bh->b_data) + (offset & ROMBMASK), maxsize);
 236        brelse(bh);
 237
 238        res = maxsize;                  /* all of it */
 239
 240        while (res < count) {
 241                offset += maxsize;
 242                dest += maxsize;
 243
 244                bh = sb_bread(i->i_sb, offset>>ROMBSBITS);
 245                if (!bh)
 246                        return -1;
 247                maxsize = min_t(unsigned long, count - res, ROMBSIZE);
 248                memcpy(dest, bh->b_data, maxsize);
 249                brelse(bh);
 250                res += maxsize;
 251        }
 252        return res;
 253}
 254
 255static unsigned char romfs_dtype_table[] = {
 256        DT_UNKNOWN, DT_DIR, DT_REG, DT_LNK, DT_BLK, DT_CHR, DT_SOCK, DT_FIFO
 257};
 258
 259static int
 260romfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
 261{
 262        struct inode *i = filp->f_dentry->d_inode;
 263        struct romfs_inode ri;
 264        unsigned long offset, maxoff;
 265        int j, ino, nextfh;
 266        int stored = 0;
 267        char fsname[ROMFS_MAXFN];       /* XXX dynamic? */
 268
 269        maxoff = i->i_sb->u.romfs_sb.s_maxsize;
 270
 271        offset = filp->f_pos;
 272        if (!offset) {
 273                offset = i->i_ino & ROMFH_MASK;
 274                if (romfs_copyfrom(i, &ri, offset, ROMFH_SIZE) <= 0)
 275                        return stored;
 276                offset = ntohl(ri.spec) & ROMFH_MASK;
 277        }
 278
 279        /* Not really failsafe, but we are read-only... */
 280        for(;;) {
 281                if (!offset || offset >= maxoff) {
 282                        offset = maxoff;
 283                        filp->f_pos = offset;
 284                        return stored;
 285                }
 286                filp->f_pos = offset;
 287
 288                /* Fetch inode info */
 289                if (romfs_copyfrom(i, &ri, offset, ROMFH_SIZE) <= 0)
 290                        return stored;
 291
 292                j = romfs_strnlen(i, offset+ROMFH_SIZE, sizeof(fsname)-1);
 293                if (j < 0)
 294                        return stored;
 295
 296                fsname[j]=0;
 297                romfs_copyfrom(i, fsname, offset+ROMFH_SIZE, j);
 298
 299                ino = offset;
 300                nextfh = ntohl(ri.next);
 301                if ((nextfh & ROMFH_TYPE) == ROMFH_HRD)
 302                        ino = ntohl(ri.spec);
 303                if (filldir(dirent, fsname, j, offset, ino,
 304                            romfs_dtype_table[nextfh & ROMFH_TYPE]) < 0) {
 305                        return stored;
 306                }
 307                stored++;
 308                offset = nextfh & ROMFH_MASK;
 309        }
 310}
 311
 312static struct dentry *
 313romfs_lookup(struct inode *dir, struct dentry *dentry)
 314{
 315        unsigned long offset, maxoff;
 316        int fslen, res;
 317        struct inode *inode;
 318        char fsname[ROMFS_MAXFN];       /* XXX dynamic? */
 319        struct romfs_inode ri;
 320        const char *name;               /* got from dentry */
 321        int len;
 322
 323        res = -EACCES;                  /* placeholder for "no data here" */
 324        offset = dir->i_ino & ROMFH_MASK;
 325        if (romfs_copyfrom(dir, &ri, offset, ROMFH_SIZE) <= 0)
 326                goto out;
 327
 328        maxoff = dir->i_sb->u.romfs_sb.s_maxsize;
 329        offset = ntohl(ri.spec) & ROMFH_MASK;
 330
 331        /* OK, now find the file whose name is in "dentry" in the
 332         * directory specified by "dir".  */
 333
 334        name = dentry->d_name.name;
 335        len = dentry->d_name.len;
 336
 337        for(;;) {
 338                if (!offset || offset >= maxoff)
 339                        goto out0;
 340                if (romfs_copyfrom(dir, &ri, offset, ROMFH_SIZE) <= 0)
 341                        goto out;
 342
 343                /* try to match the first 16 bytes of name */
 344                fslen = romfs_strnlen(dir, offset+ROMFH_SIZE, ROMFH_SIZE);
 345                if (len < ROMFH_SIZE) {
 346                        if (len == fslen) {
 347                                /* both are shorter, and same size */
 348                                romfs_copyfrom(dir, fsname, offset+ROMFH_SIZE, len+1);
 349                                if (strncmp (name, fsname, len) == 0)
 350                                        break;
 351                        }
 352                } else if (fslen >= ROMFH_SIZE) {
 353                        /* both are longer; XXX optimize max size */
 354                        fslen = romfs_strnlen(dir, offset+ROMFH_SIZE, sizeof(fsname)-1);
 355                        if (len == fslen) {
 356                                romfs_copyfrom(dir, fsname, offset+ROMFH_SIZE, len+1);
 357                                if (strncmp(name, fsname, len) == 0)
 358                                        break;
 359                        }
 360                }
 361                /* next entry */
 362                offset = ntohl(ri.next) & ROMFH_MASK;
 363        }
 364
 365        /* Hard link handling */
 366        if ((ntohl(ri.next) & ROMFH_TYPE) == ROMFH_HRD)
 367                offset = ntohl(ri.spec) & ROMFH_MASK;
 368
 369        if ((inode = iget(dir->i_sb, offset)))
 370                goto outi;
 371
 372        /*
 373         * it's a bit funky, _lookup needs to return an error code
 374         * (negative) or a NULL, both as a dentry.  ENOENT should not
 375         * be returned, instead we need to create a negative dentry by
 376         * d_add(dentry, NULL); and return 0 as no error.
 377         * (Although as I see, it only matters on writable file
 378         * systems).
 379         */
 380
 381out0:   inode = NULL;
 382outi:   res = 0;
 383        d_add (dentry, inode);
 384
 385out:    return ERR_PTR(res);
 386}
 387
 388/*
 389 * Ok, we do readpage, to be able to execute programs.  Unfortunately,
 390 * we can't use bmap, since we may have looser alignments.
 391 */
 392
 393static int
 394romfs_readpage(struct file *file, struct page * page)
 395{
 396        struct inode *inode = page->mapping->host;
 397        unsigned long offset, avail, readlen;
 398        void *buf;
 399        int result = -EIO;
 400
 401        page_cache_get(page);
 402        lock_kernel();
 403        buf = kmap(page);
 404        if (!buf)
 405                goto err_out;
 406
 407        /* 32 bit warning -- but not for us :) */
 408        offset = page->index << PAGE_CACHE_SHIFT;
 409        if (offset < inode->i_size) {
 410                avail = inode->i_size-offset;
 411                readlen = min_t(unsigned long, avail, PAGE_SIZE);
 412                if (romfs_copyfrom(inode, buf, inode->u.romfs_i.i_dataoffset+offset, readlen) == readlen) {
 413                        if (readlen < PAGE_SIZE) {
 414                                memset(buf + readlen,0,PAGE_SIZE-readlen);
 415                        }
 416                        SetPageUptodate(page);
 417                        result = 0;
 418                }
 419        }
 420        if (result) {
 421                memset(buf, 0, PAGE_SIZE);
 422                SetPageError(page);
 423        }
 424        flush_dcache_page(page);
 425
 426        UnlockPage(page);
 427
 428        kunmap(page);
 429err_out:
 430        page_cache_release(page);
 431        unlock_kernel();
 432
 433        return result;
 434}
 435
 436/* Mapping from our types to the kernel */
 437
 438static struct address_space_operations romfs_aops = {
 439        readpage: romfs_readpage
 440};
 441
 442static struct file_operations romfs_dir_operations = {
 443        read:           generic_read_dir,
 444        readdir:        romfs_readdir,
 445};
 446
 447static struct inode_operations romfs_dir_inode_operations = {
 448        lookup:         romfs_lookup,
 449};
 450
 451static mode_t romfs_modemap[] =
 452{
 453        0, S_IFDIR+0644, S_IFREG+0644, S_IFLNK+0777,
 454        S_IFBLK+0600, S_IFCHR+0600, S_IFSOCK+0644, S_IFIFO+0644
 455};
 456
 457static void
 458romfs_read_inode(struct inode *i)
 459{
 460        int nextfh, ino;
 461        struct romfs_inode ri;
 462
 463        ino = i->i_ino & ROMFH_MASK;
 464        i->i_mode = 0;
 465
 466        /* Loop for finding the real hard link */
 467        for(;;) {
 468                if (romfs_copyfrom(i, &ri, ino, ROMFH_SIZE) <= 0) {
 469                        printk("romfs: read error for inode 0x%x\n", ino);
 470                        return;
 471                }
 472                /* XXX: do romfs_checksum here too (with name) */
 473
 474                nextfh = ntohl(ri.next);
 475                if ((nextfh & ROMFH_TYPE) != ROMFH_HRD)
 476                        break;
 477
 478                ino = ntohl(ri.spec) & ROMFH_MASK;
 479        }
 480
 481        i->i_nlink = 1;         /* Hard to decide.. */
 482        i->i_size = ntohl(ri.size);
 483        i->i_mtime = i->i_atime = i->i_ctime = 0;
 484        i->i_uid = i->i_gid = 0;
 485
 486        /* Precalculate the data offset */
 487        ino = romfs_strnlen(i, ino+ROMFH_SIZE, ROMFS_MAXFN);
 488        if (ino >= 0)
 489                ino = ((ROMFH_SIZE+ino+1+ROMFH_PAD)&ROMFH_MASK);
 490        else
 491                ino = 0;
 492
 493        i->u.romfs_i.i_metasize = ino;
 494        i->u.romfs_i.i_dataoffset = ino+(i->i_ino&ROMFH_MASK);
 495
 496        /* Compute permissions */
 497        ino = romfs_modemap[nextfh & ROMFH_TYPE];
 498        /* only "normal" files have ops */
 499        switch (nextfh & ROMFH_TYPE) {
 500                case 1:
 501                        i->i_size = i->u.romfs_i.i_metasize;
 502                        i->i_op = &romfs_dir_inode_operations;
 503                        i->i_fop = &romfs_dir_operations;
 504                        if (nextfh & ROMFH_EXEC)
 505                                ino |= S_IXUGO;
 506                        i->i_mode = ino;
 507                        break;
 508                case 2:
 509                        i->i_fop = &generic_ro_fops;
 510                        i->i_data.a_ops = &romfs_aops;
 511                        if (nextfh & ROMFH_EXEC)
 512                                ino |= S_IXUGO;
 513                        i->i_mode = ino;
 514                        break;
 515                case 3:
 516                        i->i_op = &page_symlink_inode_operations;
 517                        i->i_data.a_ops = &romfs_aops;
 518                        i->i_mode = ino | S_IRWXUGO;
 519                        break;
 520                default:
 521                        /* depending on MBZ for sock/fifos */
 522                        nextfh = ntohl(ri.spec);
 523                        nextfh = kdev_t_to_nr(MKDEV(nextfh>>16,nextfh&0xffff));
 524                        init_special_inode(i, ino, nextfh);
 525        }
 526}
 527
 528static struct super_operations romfs_ops = {
 529        read_inode:     romfs_read_inode,
 530        statfs:         romfs_statfs,
 531};
 532
 533static DECLARE_FSTYPE_DEV(romfs_fs_type, "romfs", romfs_read_super);
 534
 535static int __init init_romfs_fs(void)
 536{
 537        return register_filesystem(&romfs_fs_type);
 538}
 539
 540static void __exit exit_romfs_fs(void)
 541{
 542        unregister_filesystem(&romfs_fs_type);
 543}
 544
 545/* Yes, works even as a module... :) */
 546
 547EXPORT_NO_SYMBOLS;
 548
 549module_init(init_romfs_fs)
 550module_exit(exit_romfs_fs)
 551MODULE_LICENSE("GPL");
 552
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.