root/fs/omfs/dir.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. omfs_hash
  2. omfs_get_bucket
  3. omfs_scan_list
  4. omfs_find_entry
  5. omfs_make_empty
  6. omfs_add_link
  7. omfs_delete_entry
  8. omfs_dir_is_empty
  9. omfs_remove
  10. omfs_add_node
  11. omfs_mkdir
  12. omfs_create
  13. omfs_lookup
  14. omfs_is_bad
  15. omfs_fill_chain
  16. omfs_rename
  17. omfs_readdir

   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 
  12 static 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  */
  24 static 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 
  34 static 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         }
  63 err:
  64         return ERR_PTR(err);
  65 }
  66 
  67 static 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 
  84 int 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 
 111 static 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;
 152 out:
 153         return -ENOMEM;
 154 }
 155 
 156 static 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;
 210 out_free_bh:
 211         brelse(bh);
 212 out:
 213         return err;
 214 }
 215 
 216 static 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 
 238 static 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 
 258 static 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 
 277 out_free_inode:
 278         iput(inode);
 279         return err;
 280 }
 281 
 282 static 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 
 287 static 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 
 293 static 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 */
 315 int 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 
 329 static 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 
 372 static 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);
 403 out:
 404         return err;
 405 }
 406 
 407 static 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 
 447 const 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 
 456 const struct file_operations omfs_dir_operations = {
 457         .read = generic_read_dir,
 458         .iterate_shared = omfs_readdir,
 459         .llseek = generic_file_llseek,
 460 };

/* [<][>][^][v][top][bottom][index][help] */