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 = d_inode(dentry->d_parent);
 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(dir);
 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 = d_inode(dentry->d_parent);
 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 = d_inode(dentry);
 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        } else if (bh != ERR_PTR(-ENOENT)) {
 309                inode = ERR_CAST(bh);
 310        }
 311        return d_splice_alias(inode, dentry);
 312}
 313
 314/* sanity check block's self pointer */
 315int omfs_is_bad(struct omfs_sb_info *sbi, struct omfs_header *header,
 316        u64 fsblock)
 317{
 318        int is_bad;
 319        u64 ino = be64_to_cpu(header->h_self);
 320        is_bad = ((ino != fsblock) || (ino < sbi->s_root_ino) ||
 321                (ino > sbi->s_num_blocks));
 322
 323        if (is_bad)
 324                printk(KERN_WARNING "omfs: bad hash chain detected\n");
 325
 326        return is_bad;
 327}
 328
 329static bool omfs_fill_chain(struct inode *dir, struct dir_context *ctx,
 330                u64 fsblock, int hindex)
 331{
 332        /* follow chain in this bucket */
 333        while (fsblock != ~0) {
 334                struct buffer_head *bh = omfs_bread(dir->i_sb, fsblock);
 335                struct omfs_inode *oi;
 336                u64 self;
 337                unsigned char d_type;
 338
 339                if (!bh)
 340                        return true;
 341
 342                oi = (struct omfs_inode *) bh->b_data;
 343                if (omfs_is_bad(OMFS_SB(dir->i_sb), &oi->i_head, fsblock)) {
 344                        brelse(bh);
 345                        return true;
 346                }
 347
 348                self = fsblock;
 349                fsblock = be64_to_cpu(oi->i_sibling);
 350
 351                /* skip visited nodes */
 352                if (hindex) {
 353                        hindex--;
 354                        brelse(bh);
 355                        continue;
 356                }
 357
 358                d_type = (oi->i_type == OMFS_DIR) ? DT_DIR : DT_REG;
 359
 360                if (!dir_emit(ctx, oi->i_name,
 361                              strnlen(oi->i_name, OMFS_NAMELEN),
 362                              self, d_type)) {
 363                        brelse(bh);
 364                        return false;
 365                }
 366                brelse(bh);
 367                ctx->pos++;
 368        }
 369        return true;
 370}
 371
 372static int omfs_rename(struct inode *old_dir, struct dentry *old_dentry,
 373                       struct inode *new_dir, struct dentry *new_dentry,
 374                       unsigned int flags)
 375{
 376        struct inode *new_inode = d_inode(new_dentry);
 377        struct inode *old_inode = d_inode(old_dentry);
 378        int err;
 379
 380        if (flags & ~RENAME_NOREPLACE)
 381                return -EINVAL;
 382
 383        if (new_inode) {
 384                /* overwriting existing file/dir */
 385                err = omfs_remove(new_dir, new_dentry);
 386                if (err)
 387                        goto out;
 388        }
 389
 390        /* since omfs locates files by name, we need to unlink _before_
 391         * adding the new link or we won't find the old one */
 392        err = omfs_delete_entry(old_dentry);
 393        if (err)
 394                goto out;
 395
 396        mark_inode_dirty(old_dir);
 397        err = omfs_add_link(new_dentry, old_inode);
 398        if (err)
 399                goto out;
 400
 401        old_inode->i_ctime = current_time(old_inode);
 402        mark_inode_dirty(old_inode);
 403out:
 404        return err;
 405}
 406
 407static int omfs_readdir(struct file *file, struct dir_context *ctx)
 408{
 409        struct inode *dir = file_inode(file);
 410        struct buffer_head *bh;
 411        __be64 *p;
 412        unsigned int hchain, hindex;
 413        int nbuckets;
 414
 415        if (ctx->pos >> 32)
 416                return -EINVAL;
 417
 418        if (ctx->pos < 1 << 20) {
 419                if (!dir_emit_dots(file, ctx))
 420                        return 0;
 421                ctx->pos = 1 << 20;
 422        }
 423
 424        nbuckets = (dir->i_size - OMFS_DIR_START) / 8;
 425
 426        /* high 12 bits store bucket + 1 and low 20 bits store hash index */
 427        hchain = (ctx->pos >> 20) - 1;
 428        hindex = ctx->pos & 0xfffff;
 429
 430        bh = omfs_bread(dir->i_sb, dir->i_ino);
 431        if (!bh)
 432                return -EINVAL;
 433
 434        p = (__be64 *)(bh->b_data + OMFS_DIR_START) + hchain;
 435
 436        for (; hchain < nbuckets; hchain++) {
 437                __u64 fsblock = be64_to_cpu(*p++);
 438                if (!omfs_fill_chain(dir, ctx, fsblock, hindex))
 439                        break;
 440                hindex = 0;
 441                ctx->pos = (hchain+2) << 20;
 442        }
 443        brelse(bh);
 444        return 0;
 445}
 446
 447const struct inode_operations omfs_dir_inops = {
 448        .lookup = omfs_lookup,
 449        .mkdir = omfs_mkdir,
 450        .rename = omfs_rename,
 451        .create = omfs_create,
 452        .unlink = omfs_remove,
 453        .rmdir = omfs_remove,
 454};
 455
 456const struct file_operations omfs_dir_operations = {
 457        .read = generic_read_dir,
 458        .iterate_shared = omfs_readdir,
 459        .llseek = generic_file_llseek,
 460};
 461