linux/fs/omfs/dir.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * OMFS (as used by RIO Karma) directory operations.
   4 * Copyright (C) 2005 Bob Copeland <me@bobcopeland.com>
   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 user_namespace *mnt_userns, struct inode *dir,
 283                      struct dentry *dentry, umode_t mode)
 284{
 285        return omfs_add_node(dir, dentry, mode | S_IFDIR);
 286}
 287
 288static int omfs_create(struct user_namespace *mnt_userns, struct inode *dir,
 289                       struct dentry *dentry, umode_t mode, bool excl)
 290{
 291        return omfs_add_node(dir, dentry, mode | S_IFREG);
 292}
 293
 294static struct dentry *omfs_lookup(struct inode *dir, struct dentry *dentry,
 295                                  unsigned int flags)
 296{
 297        struct buffer_head *bh;
 298        struct inode *inode = NULL;
 299
 300        if (dentry->d_name.len > OMFS_NAMELEN)
 301                return ERR_PTR(-ENAMETOOLONG);
 302
 303        bh = omfs_find_entry(dir, dentry->d_name.name, dentry->d_name.len);
 304        if (!IS_ERR(bh)) {
 305                struct omfs_inode *oi = (struct omfs_inode *)bh->b_data;
 306                ino_t ino = be64_to_cpu(oi->i_head.h_self);
 307                brelse(bh);
 308                inode = omfs_iget(dir->i_sb, ino);
 309        } else if (bh != ERR_PTR(-ENOENT)) {
 310                inode = ERR_CAST(bh);
 311        }
 312        return d_splice_alias(inode, dentry);
 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 user_namespace *mnt_userns, struct inode *old_dir,
 374                       struct dentry *old_dentry, struct inode *new_dir,
 375                       struct dentry *new_dentry, unsigned int flags)
 376{
 377        struct inode *new_inode = d_inode(new_dentry);
 378        struct inode *old_inode = d_inode(old_dentry);
 379        int err;
 380
 381        if (flags & ~RENAME_NOREPLACE)
 382                return -EINVAL;
 383
 384        if (new_inode) {
 385                /* overwriting existing file/dir */
 386                err = omfs_remove(new_dir, new_dentry);
 387                if (err)
 388                        goto out;
 389        }
 390
 391        /* since omfs locates files by name, we need to unlink _before_
 392         * adding the new link or we won't find the old one */
 393        err = omfs_delete_entry(old_dentry);
 394        if (err)
 395                goto out;
 396
 397        mark_inode_dirty(old_dir);
 398        err = omfs_add_link(new_dentry, old_inode);
 399        if (err)
 400                goto out;
 401
 402        old_inode->i_ctime = current_time(old_inode);
 403        mark_inode_dirty(old_inode);
 404out:
 405        return err;
 406}
 407
 408static int omfs_readdir(struct file *file, struct dir_context *ctx)
 409{
 410        struct inode *dir = file_inode(file);
 411        struct buffer_head *bh;
 412        __be64 *p;
 413        unsigned int hchain, hindex;
 414        int nbuckets;
 415
 416        if (ctx->pos >> 32)
 417                return -EINVAL;
 418
 419        if (ctx->pos < 1 << 20) {
 420                if (!dir_emit_dots(file, ctx))
 421                        return 0;
 422                ctx->pos = 1 << 20;
 423        }
 424
 425        nbuckets = (dir->i_size - OMFS_DIR_START) / 8;
 426
 427        /* high 12 bits store bucket + 1 and low 20 bits store hash index */
 428        hchain = (ctx->pos >> 20) - 1;
 429        hindex = ctx->pos & 0xfffff;
 430
 431        bh = omfs_bread(dir->i_sb, dir->i_ino);
 432        if (!bh)
 433                return -EINVAL;
 434
 435        p = (__be64 *)(bh->b_data + OMFS_DIR_START) + hchain;
 436
 437        for (; hchain < nbuckets; hchain++) {
 438                __u64 fsblock = be64_to_cpu(*p++);
 439                if (!omfs_fill_chain(dir, ctx, fsblock, hindex))
 440                        break;
 441                hindex = 0;
 442                ctx->pos = (hchain+2) << 20;
 443        }
 444        brelse(bh);
 445        return 0;
 446}
 447
 448const struct inode_operations omfs_dir_inops = {
 449        .lookup = omfs_lookup,
 450        .mkdir = omfs_mkdir,
 451        .rename = omfs_rename,
 452        .create = omfs_create,
 453        .unlink = omfs_remove,
 454        .rmdir = omfs_remove,
 455};
 456
 457const struct file_operations omfs_dir_operations = {
 458        .read = generic_read_dir,
 459        .iterate_shared = omfs_readdir,
 460        .llseek = generic_file_llseek,
 461};
 462