linux/fs/omfs/dir.c
<<
>>
Prefs
   1/*
   2 * OMFS (as used by RIO Karma) directory operations.
   3 * Copyright (C) 2005 Bob Copeland <me@bobcopeland.com>
   4 * Released under GPL v2.
   5 */
   6
   7#include <linux/fs.h>
   8#include <linux/ctype.h>
   9#include <linux/buffer_head.h>
  10#include "omfs.h"
  11
  12static int omfs_hash(const char *name, int namelen, int mod)
  13{
  14        int i, hash = 0;
  15        for (i = 0; i < namelen; i++)
  16                hash ^= tolower(name[i]) << (i % 24);
  17        return hash % mod;
  18}
  19
  20/*
  21 * Finds the bucket for a given name and reads the containing block;
  22 * *ofs is set to the offset of the first list entry.
  23 */
  24static struct buffer_head *omfs_get_bucket(struct inode *dir,
  25                const char *name, int namelen, int *ofs)
  26{
  27        int nbuckets = (dir->i_size - OMFS_DIR_START)/8;
  28        int bucket = omfs_hash(name, namelen, nbuckets);
  29
  30        *ofs = OMFS_DIR_START + bucket * 8;
  31        return omfs_bread(dir->i_sb, dir->i_ino);
  32}
  33
  34static struct buffer_head *omfs_scan_list(struct inode *dir, u64 block,
  35                                const char *name, int namelen,
  36                                u64 *prev_block)
  37{
  38        struct buffer_head *bh;
  39        struct omfs_inode *oi;
  40        int err = -ENOENT;
  41        *prev_block = ~0;
  42
  43        while (block != ~0) {
  44                bh = omfs_bread(dir->i_sb, block);
  45                if (!bh) {
  46                        err = -EIO;
  47                        goto err;
  48                }
  49
  50                oi = (struct omfs_inode *) bh->b_data;
  51                if (omfs_is_bad(OMFS_SB(dir->i_sb), &oi->i_head, block)) {
  52                        brelse(bh);
  53                        goto err;
  54                }
  55
  56                if (strncmp(oi->i_name, name, namelen) == 0)
  57                        return bh;
  58
  59                *prev_block = block;
  60                block = be64_to_cpu(oi->i_sibling);
  61                brelse(bh);
  62        }
  63err:
  64        return ERR_PTR(err);
  65}
  66
  67static struct buffer_head *omfs_find_entry(struct inode *dir,
  68                                           const char *name, int namelen)
  69{
  70        struct buffer_head *bh;
  71        int ofs;
  72        u64 block, dummy;
  73
  74        bh = omfs_get_bucket(dir, name, namelen, &ofs);
  75        if (!bh)
  76                return ERR_PTR(-EIO);
  77
  78        block = be64_to_cpu(*((__be64 *) &bh->b_data[ofs]));
  79        brelse(bh);
  80
  81        return omfs_scan_list(dir, block, name, namelen, &dummy);
  82}
  83
  84int omfs_make_empty(struct inode *inode, struct super_block *sb)
  85{
  86        struct omfs_sb_info *sbi = OMFS_SB(sb);
  87        struct buffer_head *bh;
  88        struct omfs_inode *oi;
  89
  90        bh = omfs_bread(sb, inode->i_ino);
  91        if (!bh)
  92                return -ENOMEM;
  93
  94        memset(bh->b_data, 0, sizeof(struct omfs_inode));
  95
  96        if (S_ISDIR(inode->i_mode)) {
  97                memset(&bh->b_data[OMFS_DIR_START], 0xff,
  98                        sbi->s_sys_blocksize - OMFS_DIR_START);
  99        } else
 100                omfs_make_empty_table(bh, OMFS_EXTENT_START);
 101
 102        oi = (struct omfs_inode *) bh->b_data;
 103        oi->i_head.h_self = cpu_to_be64(inode->i_ino);
 104        oi->i_sibling = ~cpu_to_be64(0ULL);
 105
 106        mark_buffer_dirty(bh);
 107        brelse(bh);
 108        return 0;
 109}
 110
 111static int omfs_add_link(struct dentry *dentry, struct inode *inode)
 112{
 113        struct inode *dir = dentry->d_parent->d_inode;
 114        const char *name = dentry->d_name.name;
 115        int namelen = dentry->d_name.len;
 116        struct omfs_inode *oi;
 117        struct buffer_head *bh;
 118        u64 block;
 119        __be64 *entry;
 120        int ofs;
 121
 122        /* just prepend to head of queue in proper bucket */
 123        bh = omfs_get_bucket(dir, name, namelen, &ofs);
 124        if (!bh)
 125                goto out;
 126
 127        entry = (__be64 *) &bh->b_data[ofs];
 128        block = be64_to_cpu(*entry);
 129        *entry = cpu_to_be64(inode->i_ino);
 130        mark_buffer_dirty(bh);
 131        brelse(bh);
 132
 133        /* now set the sibling and parent pointers on the new inode */
 134        bh = omfs_bread(dir->i_sb, inode->i_ino);
 135        if (!bh)
 136                goto out;
 137
 138        oi = (struct omfs_inode *) bh->b_data;
 139        memcpy(oi->i_name, name, namelen);
 140        memset(oi->i_name + namelen, 0, OMFS_NAMELEN - namelen);
 141        oi->i_sibling = cpu_to_be64(block);
 142        oi->i_parent = cpu_to_be64(dir->i_ino);
 143        mark_buffer_dirty(bh);
 144        brelse(bh);
 145
 146        dir->i_ctime = CURRENT_TIME_SEC;
 147
 148        /* mark affected inodes dirty to rebuild checksums */
 149        mark_inode_dirty(dir);
 150        mark_inode_dirty(inode);
 151        return 0;
 152out:
 153        return -ENOMEM;
 154}
 155
 156static int omfs_delete_entry(struct dentry *dentry)
 157{
 158        struct inode *dir = dentry->d_parent->d_inode;
 159        struct inode *dirty;
 160        const char *name = dentry->d_name.name;
 161        int namelen = dentry->d_name.len;
 162        struct omfs_inode *oi;
 163        struct buffer_head *bh, *bh2;
 164        __be64 *entry, next;
 165        u64 block, prev;
 166        int ofs;
 167        int err = -ENOMEM;
 168
 169        /* delete the proper node in the bucket's linked list */
 170        bh = omfs_get_bucket(dir, name, namelen, &ofs);
 171        if (!bh)
 172                goto out;
 173
 174        entry = (__be64 *) &bh->b_data[ofs];
 175        block = be64_to_cpu(*entry);
 176
 177        bh2 = omfs_scan_list(dir, block, name, namelen, &prev);
 178        if (IS_ERR(bh2)) {
 179                err = PTR_ERR(bh2);
 180                goto out_free_bh;
 181        }
 182
 183        oi = (struct omfs_inode *) bh2->b_data;
 184        next = oi->i_sibling;
 185        brelse(bh2);
 186
 187        if (prev != ~0) {
 188                /* found in middle of list, get list ptr */
 189                brelse(bh);
 190                bh = omfs_bread(dir->i_sb, prev);
 191                if (!bh)
 192                        goto out;
 193
 194                oi = (struct omfs_inode *) bh->b_data;
 195                entry = &oi->i_sibling;
 196        }
 197
 198        *entry = next;
 199        mark_buffer_dirty(bh);
 200
 201        if (prev != ~0) {
 202                dirty = omfs_iget(dir->i_sb, prev);
 203                if (!IS_ERR(dirty)) {
 204                        mark_inode_dirty(dirty);
 205                        iput(dirty);
 206                }
 207        }
 208
 209        err = 0;
 210out_free_bh:
 211        brelse(bh);
 212out:
 213        return err;
 214}
 215
 216static int omfs_dir_is_empty(struct inode *inode)
 217{
 218        int nbuckets = (inode->i_size - OMFS_DIR_START) / 8;
 219        struct buffer_head *bh;
 220        u64 *ptr;
 221        int i;
 222
 223        bh = omfs_bread(inode->i_sb, inode->i_ino);
 224
 225        if (!bh)
 226                return 0;
 227
 228        ptr = (u64 *) &bh->b_data[OMFS_DIR_START];
 229
 230        for (i = 0; i < nbuckets; i++, ptr++)
 231                if (*ptr != ~0)
 232                        break;
 233
 234        brelse(bh);
 235        return *ptr != ~0;
 236}
 237
 238static int omfs_remove(struct inode *dir, struct dentry *dentry)
 239{
 240        struct inode *inode = dentry->d_inode;
 241        int ret;
 242
 243
 244        if (S_ISDIR(inode->i_mode) &&
 245            !omfs_dir_is_empty(inode))
 246                return -ENOTEMPTY;
 247
 248        ret = omfs_delete_entry(dentry);
 249        if (ret)
 250                return ret;
 251        
 252        clear_nlink(inode);
 253        mark_inode_dirty(inode);
 254        mark_inode_dirty(dir);
 255        return 0;
 256}
 257
 258static int omfs_add_node(struct inode *dir, struct dentry *dentry, umode_t mode)
 259{
 260        int err;
 261        struct inode *inode = omfs_new_inode(dir, mode);
 262
 263        if (IS_ERR(inode))
 264                return PTR_ERR(inode);
 265
 266        err = omfs_make_empty(inode, dir->i_sb);
 267        if (err)
 268                goto out_free_inode;
 269
 270        err = omfs_add_link(dentry, inode);
 271        if (err)
 272                goto out_free_inode;
 273
 274        d_instantiate(dentry, inode);
 275        return 0;
 276
 277out_free_inode:
 278        iput(inode);
 279        return err;
 280}
 281
 282static int omfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
 283{
 284        return omfs_add_node(dir, dentry, mode | S_IFDIR);
 285}
 286
 287static int omfs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
 288                bool excl)
 289{
 290        return omfs_add_node(dir, dentry, mode | S_IFREG);
 291}
 292
 293static struct dentry *omfs_lookup(struct inode *dir, struct dentry *dentry,
 294                                  unsigned int flags)
 295{
 296        struct buffer_head *bh;
 297        struct inode *inode = NULL;
 298
 299        if (dentry->d_name.len > OMFS_NAMELEN)
 300                return ERR_PTR(-ENAMETOOLONG);
 301
 302        bh = omfs_find_entry(dir, dentry->d_name.name, dentry->d_name.len);
 303        if (!IS_ERR(bh)) {
 304                struct omfs_inode *oi = (struct omfs_inode *)bh->b_data;
 305                ino_t ino = be64_to_cpu(oi->i_head.h_self);
 306                brelse(bh);
 307                inode = omfs_iget(dir->i_sb, ino);
 308                if (IS_ERR(inode))
 309                        return ERR_CAST(inode);
 310        }
 311        d_add(dentry, inode);
 312        return NULL;
 313}
 314
 315/* sanity check block's self pointer */
 316int omfs_is_bad(struct omfs_sb_info *sbi, struct omfs_header *header,
 317        u64 fsblock)
 318{
 319        int is_bad;
 320        u64 ino = be64_to_cpu(header->h_self);
 321        is_bad = ((ino != fsblock) || (ino < sbi->s_root_ino) ||
 322                (ino > sbi->s_num_blocks));
 323
 324        if (is_bad)
 325                printk(KERN_WARNING "omfs: bad hash chain detected\n");
 326
 327        return is_bad;
 328}
 329
 330static int omfs_fill_chain(struct file *filp, void *dirent, filldir_t filldir,
 331                u64 fsblock, int hindex)
 332{
 333        struct inode *dir = filp->f_dentry->d_inode;
 334        struct buffer_head *bh;
 335        struct omfs_inode *oi;
 336        u64 self;
 337        int res = 0;
 338        unsigned char d_type;
 339
 340        /* follow chain in this bucket */
 341        while (fsblock != ~0) {
 342                bh = omfs_bread(dir->i_sb, fsblock);
 343                if (!bh)
 344                        goto out;
 345
 346                oi = (struct omfs_inode *) bh->b_data;
 347                if (omfs_is_bad(OMFS_SB(dir->i_sb), &oi->i_head, fsblock)) {
 348                        brelse(bh);
 349                        goto out;
 350                }
 351
 352                self = fsblock;
 353                fsblock = be64_to_cpu(oi->i_sibling);
 354
 355                /* skip visited nodes */
 356                if (hindex) {
 357                        hindex--;
 358                        brelse(bh);
 359                        continue;
 360                }
 361
 362                d_type = (oi->i_type == OMFS_DIR) ? DT_DIR : DT_REG;
 363
 364                res = filldir(dirent, oi->i_name, strnlen(oi->i_name,
 365                        OMFS_NAMELEN), filp->f_pos, self, d_type);
 366                brelse(bh);
 367                if (res < 0)
 368                        break;
 369                filp->f_pos++;
 370        }
 371out:
 372        return res;
 373}
 374
 375static int omfs_rename(struct inode *old_dir, struct dentry *old_dentry,
 376                struct inode *new_dir, struct dentry *new_dentry)
 377{
 378        struct inode *new_inode = new_dentry->d_inode;
 379        struct inode *old_inode = old_dentry->d_inode;
 380        int err;
 381
 382        if (new_inode) {
 383                /* overwriting existing file/dir */
 384                err = omfs_remove(new_dir, new_dentry);
 385                if (err)
 386                        goto out;
 387        }
 388
 389        /* since omfs locates files by name, we need to unlink _before_
 390         * adding the new link or we won't find the old one */
 391        err = omfs_delete_entry(old_dentry);
 392        if (err)
 393                goto out;
 394
 395        mark_inode_dirty(old_dir);
 396        err = omfs_add_link(new_dentry, old_inode);
 397        if (err)
 398                goto out;
 399
 400        old_inode->i_ctime = CURRENT_TIME_SEC;
 401        mark_inode_dirty(old_inode);
 402out:
 403        return err;
 404}
 405
 406static int omfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
 407{
 408        struct inode *dir = filp->f_dentry->d_inode;
 409        struct buffer_head *bh;
 410        loff_t offset, res;
 411        unsigned int hchain, hindex;
 412        int nbuckets;
 413        u64 fsblock;
 414        int ret = -EINVAL;
 415
 416        if (filp->f_pos >> 32)
 417                goto success;
 418
 419        switch ((unsigned long) filp->f_pos) {
 420        case 0:
 421                if (filldir(dirent, ".", 1, 0, dir->i_ino, DT_DIR) < 0)
 422                        goto success;
 423                filp->f_pos++;
 424                /* fall through */
 425        case 1:
 426                if (filldir(dirent, "..", 2, 1,
 427                    parent_ino(filp->f_dentry), DT_DIR) < 0)
 428                        goto success;
 429                filp->f_pos = 1 << 20;
 430                /* fall through */
 431        }
 432
 433        nbuckets = (dir->i_size - OMFS_DIR_START) / 8;
 434
 435        /* high 12 bits store bucket + 1 and low 20 bits store hash index */
 436        hchain = (filp->f_pos >> 20) - 1;
 437        hindex = filp->f_pos & 0xfffff;
 438
 439        bh = omfs_bread(dir->i_sb, dir->i_ino);
 440        if (!bh)
 441                goto out;
 442
 443        offset = OMFS_DIR_START + hchain * 8;
 444
 445        for (; hchain < nbuckets; hchain++, offset += 8) {
 446                fsblock = be64_to_cpu(*((__be64 *) &bh->b_data[offset]));
 447
 448                res = omfs_fill_chain(filp, dirent, filldir, fsblock, hindex);
 449                hindex = 0;
 450                if (res < 0)
 451                        break;
 452
 453                filp->f_pos = (hchain+2) << 20;
 454        }
 455        brelse(bh);
 456success:
 457        ret = 0;
 458out:
 459        return ret;
 460}
 461
 462const struct inode_operations omfs_dir_inops = {
 463        .lookup = omfs_lookup,
 464        .mkdir = omfs_mkdir,
 465        .rename = omfs_rename,
 466        .create = omfs_create,
 467        .unlink = omfs_remove,
 468        .rmdir = omfs_remove,
 469};
 470
 471const struct file_operations omfs_dir_operations = {
 472        .read = generic_read_dir,
 473        .readdir = omfs_readdir,
 474        .llseek = generic_file_llseek,
 475};
 476
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.