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