linux/fs/omfs/file.c
<<
>>
Prefs
   1/*
   2 * OMFS (as used by RIO Karma) file operations.
   3 * Copyright (C) 2005 Bob Copeland <me@bobcopeland.com>
   4 * Released under GPL v2.
   5 */
   6
   7#include <linux/module.h>
   8#include <linux/fs.h>
   9#include <linux/buffer_head.h>
  10#include <linux/mpage.h>
  11#include "omfs.h"
  12
  13static u32 omfs_max_extents(struct omfs_sb_info *sbi, int offset)
  14{
  15        return (sbi->s_sys_blocksize - offset -
  16                sizeof(struct omfs_extent)) /
  17                sizeof(struct omfs_extent_entry) + 1;
  18}
  19
  20void omfs_make_empty_table(struct buffer_head *bh, int offset)
  21{
  22        struct omfs_extent *oe = (struct omfs_extent *) &bh->b_data[offset];
  23
  24        oe->e_next = ~cpu_to_be64(0ULL);
  25        oe->e_extent_count = cpu_to_be32(1),
  26        oe->e_fill = cpu_to_be32(0x22),
  27        oe->e_entry.e_cluster = ~cpu_to_be64(0ULL);
  28        oe->e_entry.e_blocks = ~cpu_to_be64(0ULL);
  29}
  30
  31int omfs_shrink_inode(struct inode *inode)
  32{
  33        struct omfs_sb_info *sbi = OMFS_SB(inode->i_sb);
  34        struct omfs_extent *oe;
  35        struct omfs_extent_entry *entry;
  36        struct buffer_head *bh;
  37        u64 next, last;
  38        u32 extent_count;
  39        u32 max_extents;
  40        int ret;
  41
  42        /* traverse extent table, freeing each entry that is greater
  43         * than inode->i_size;
  44         */
  45        next = inode->i_ino;
  46
  47        /* only support truncate -> 0 for now */
  48        ret = -EIO;
  49        if (inode->i_size != 0)
  50                goto out;
  51
  52        bh = omfs_bread(inode->i_sb, next);
  53        if (!bh)
  54                goto out;
  55
  56        oe = (struct omfs_extent *)(&bh->b_data[OMFS_EXTENT_START]);
  57        max_extents = omfs_max_extents(sbi, OMFS_EXTENT_START);
  58
  59        for (;;) {
  60
  61                if (omfs_is_bad(sbi, (struct omfs_header *) bh->b_data, next))
  62                        goto out_brelse;
  63
  64                extent_count = be32_to_cpu(oe->e_extent_count);
  65
  66                if (extent_count > max_extents)
  67                        goto out_brelse;
  68
  69                last = next;
  70                next = be64_to_cpu(oe->e_next);
  71                entry = &oe->e_entry;
  72
  73                /* ignore last entry as it is the terminator */
  74                for (; extent_count > 1; extent_count--) {
  75                        u64 start, count;
  76                        start = be64_to_cpu(entry->e_cluster);
  77                        count = be64_to_cpu(entry->e_blocks);
  78
  79                        omfs_clear_range(inode->i_sb, start, (int) count);
  80                        entry++;
  81                }
  82                omfs_make_empty_table(bh, (char *) oe - bh->b_data);
  83                mark_buffer_dirty(bh);
  84                brelse(bh);
  85
  86                if (last != inode->i_ino)
  87                        omfs_clear_range(inode->i_sb, last, sbi->s_mirrors);
  88
  89                if (next == ~0)
  90                        break;
  91
  92                bh = omfs_bread(inode->i_sb, next);
  93                if (!bh)
  94                        goto out;
  95                oe = (struct omfs_extent *) (&bh->b_data[OMFS_EXTENT_CONT]);
  96                max_extents = omfs_max_extents(sbi, OMFS_EXTENT_CONT);
  97        }
  98        ret = 0;
  99out:
 100        return ret;
 101out_brelse:
 102        brelse(bh);
 103        return ret;
 104}
 105
 106static void omfs_truncate(struct inode *inode)
 107{
 108        omfs_shrink_inode(inode);
 109        mark_inode_dirty(inode);
 110}
 111
 112/*
 113 * Add new blocks to the current extent, or create new entries/continuations
 114 * as necessary.
 115 */
 116static int omfs_grow_extent(struct inode *inode, struct omfs_extent *oe,
 117                        u64 *ret_block)
 118{
 119        struct omfs_extent_entry *terminator;
 120        struct omfs_extent_entry *entry = &oe->e_entry;
 121        struct omfs_sb_info *sbi = OMFS_SB(inode->i_sb);
 122        u32 extent_count = be32_to_cpu(oe->e_extent_count);
 123        u64 new_block = 0;
 124        u32 max_count;
 125        int new_count;
 126        int ret = 0;
 127
 128        /* reached the end of the extent table with no blocks mapped.
 129         * there are three possibilities for adding: grow last extent,
 130         * add a new extent to the current extent table, and add a
 131         * continuation inode.  in last two cases need an allocator for
 132         * sbi->s_cluster_size
 133         */
 134
 135        /* TODO: handle holes */
 136
 137        /* should always have a terminator */
 138        if (extent_count < 1)
 139                return -EIO;
 140
 141        /* trivially grow current extent, if next block is not taken */
 142        terminator = entry + extent_count - 1;
 143        if (extent_count > 1) {
 144                entry = terminator-1;
 145                new_block = be64_to_cpu(entry->e_cluster) +
 146                        be64_to_cpu(entry->e_blocks);
 147
 148                if (omfs_allocate_block(inode->i_sb, new_block)) {
 149                        be64_add_cpu(&entry->e_blocks, 1);
 150                        terminator->e_blocks = ~(cpu_to_be64(
 151                                be64_to_cpu(~terminator->e_blocks) + 1));
 152                        goto out;
 153                }
 154        }
 155        max_count = omfs_max_extents(sbi, OMFS_EXTENT_START);
 156
 157        /* TODO: add a continuation block here */
 158        if (be32_to_cpu(oe->e_extent_count) > max_count-1)
 159                return -EIO;
 160
 161        /* try to allocate a new cluster */
 162        ret = omfs_allocate_range(inode->i_sb, 1, sbi->s_clustersize,
 163                &new_block, &new_count);
 164        if (ret)
 165                goto out_fail;
 166
 167        /* copy terminator down an entry */
 168        entry = terminator;
 169        terminator++;
 170        memcpy(terminator, entry, sizeof(struct omfs_extent_entry));
 171
 172        entry->e_cluster = cpu_to_be64(new_block);
 173        entry->e_blocks = cpu_to_be64((u64) new_count);
 174
 175        terminator->e_blocks = ~(cpu_to_be64(
 176                be64_to_cpu(~terminator->e_blocks) + (u64) new_count));
 177
 178        /* write in new entry */
 179        be32_add_cpu(&oe->e_extent_count, 1);
 180
 181out:
 182        *ret_block = new_block;
 183out_fail:
 184        return ret;
 185}
 186
 187/*
 188 * Scans across the directory table for a given file block number.
 189 * If block not found, return 0.
 190 */
 191static sector_t find_block(struct inode *inode, struct omfs_extent_entry *ent,
 192                        sector_t block, int count, int *left)
 193{
 194        /* count > 1 because of terminator */
 195        sector_t searched = 0;
 196        for (; count > 1; count--) {
 197                int numblocks = clus_to_blk(OMFS_SB(inode->i_sb),
 198                        be64_to_cpu(ent->e_blocks));
 199
 200                if (block >= searched  &&
 201                    block < searched + numblocks) {
 202                        /*
 203                         * found it at cluster + (block - searched)
 204                         * numblocks - (block - searched) is remainder
 205                         */
 206                        *left = numblocks - (block - searched);
 207                        return clus_to_blk(OMFS_SB(inode->i_sb),
 208                                be64_to_cpu(ent->e_cluster)) +
 209                                block - searched;
 210                }
 211                searched += numblocks;
 212                ent++;
 213        }
 214        return 0;
 215}
 216
 217static int omfs_get_block(struct inode *inode, sector_t block,
 218                          struct buffer_head *bh_result, int create)
 219{
 220        struct buffer_head *bh;
 221        sector_t next, offset;
 222        int ret;
 223        u64 uninitialized_var(new_block);
 224        u32 max_extents;
 225        int extent_count;
 226        struct omfs_extent *oe;
 227        struct omfs_extent_entry *entry;
 228        struct omfs_sb_info *sbi = OMFS_SB(inode->i_sb);
 229        int max_blocks = bh_result->b_size >> inode->i_blkbits;
 230        int remain;
 231
 232        ret = -EIO;
 233        bh = omfs_bread(inode->i_sb, inode->i_ino);
 234        if (!bh)
 235                goto out;
 236
 237        oe = (struct omfs_extent *)(&bh->b_data[OMFS_EXTENT_START]);
 238        max_extents = omfs_max_extents(sbi, OMFS_EXTENT_START);
 239        next = inode->i_ino;
 240
 241        for (;;) {
 242
 243                if (omfs_is_bad(sbi, (struct omfs_header *) bh->b_data, next))
 244                        goto out_brelse;
 245
 246                extent_count = be32_to_cpu(oe->e_extent_count);
 247                next = be64_to_cpu(oe->e_next);
 248                entry = &oe->e_entry;
 249
 250                if (extent_count > max_extents)
 251                        goto out_brelse;
 252
 253                offset = find_block(inode, entry, block, extent_count, &remain);
 254                if (offset > 0) {
 255                        ret = 0;
 256                        map_bh(bh_result, inode->i_sb, offset);
 257                        if (remain > max_blocks)
 258                                remain = max_blocks;
 259                        bh_result->b_size = (remain << inode->i_blkbits);
 260                        goto out_brelse;
 261                }
 262                if (next == ~0)
 263                        break;
 264
 265                brelse(bh);
 266                bh = omfs_bread(inode->i_sb, next);
 267                if (!bh)
 268                        goto out;
 269                oe = (struct omfs_extent *) (&bh->b_data[OMFS_EXTENT_CONT]);
 270                max_extents = omfs_max_extents(sbi, OMFS_EXTENT_CONT);
 271        }
 272        if (create) {
 273                ret = omfs_grow_extent(inode, oe, &new_block);
 274                if (ret == 0) {
 275                        mark_buffer_dirty(bh);
 276                        mark_inode_dirty(inode);
 277                        map_bh(bh_result, inode->i_sb,
 278                                        clus_to_blk(sbi, new_block));
 279                }
 280        }
 281out_brelse:
 282        brelse(bh);
 283out:
 284        return ret;
 285}
 286
 287static int omfs_readpage(struct file *file, struct page *page)
 288{
 289        return block_read_full_page(page, omfs_get_block);
 290}
 291
 292static int omfs_readpages(struct file *file, struct address_space *mapping,
 293                struct list_head *pages, unsigned nr_pages)
 294{
 295        return mpage_readpages(mapping, pages, nr_pages, omfs_get_block);
 296}
 297
 298static int omfs_writepage(struct page *page, struct writeback_control *wbc)
 299{
 300        return block_write_full_page(page, omfs_get_block, wbc);
 301}
 302
 303static int
 304omfs_writepages(struct address_space *mapping, struct writeback_control *wbc)
 305{
 306        return mpage_writepages(mapping, wbc, omfs_get_block);
 307}
 308
 309static int omfs_write_begin(struct file *file, struct address_space *mapping,
 310                        loff_t pos, unsigned len, unsigned flags,
 311                        struct page **pagep, void **fsdata)
 312{
 313        int ret;
 314
 315        ret = block_write_begin(mapping, pos, len, flags, pagep,
 316                                omfs_get_block);
 317        if (unlikely(ret)) {
 318                loff_t isize = mapping->host->i_size;
 319                if (pos + len > isize)
 320                        vmtruncate(mapping->host, isize);
 321        }
 322
 323        return ret;
 324}
 325
 326static sector_t omfs_bmap(struct address_space *mapping, sector_t block)
 327{
 328        return generic_block_bmap(mapping, block, omfs_get_block);
 329}
 330
 331const struct file_operations omfs_file_operations = {
 332        .llseek = generic_file_llseek,
 333        .read = do_sync_read,
 334        .write = do_sync_write,
 335        .aio_read = generic_file_aio_read,
 336        .aio_write = generic_file_aio_write,
 337        .mmap = generic_file_mmap,
 338        .fsync = generic_file_fsync,
 339        .splice_read = generic_file_splice_read,
 340};
 341
 342static int omfs_setattr(struct dentry *dentry, struct iattr *attr)
 343{
 344        struct inode *inode = dentry->d_inode;
 345        int error;
 346
 347        error = inode_change_ok(inode, attr);
 348        if (error)
 349                return error;
 350
 351        if ((attr->ia_valid & ATTR_SIZE) &&
 352            attr->ia_size != i_size_read(inode)) {
 353                error = vmtruncate(inode, attr->ia_size);
 354                if (error)
 355                        return error;
 356        }
 357
 358        setattr_copy(inode, attr);
 359        mark_inode_dirty(inode);
 360        return 0;
 361}
 362
 363const struct inode_operations omfs_file_inops = {
 364        .setattr = omfs_setattr,
 365        .truncate = omfs_truncate
 366};
 367
 368const struct address_space_operations omfs_aops = {
 369        .readpage = omfs_readpage,
 370        .readpages = omfs_readpages,
 371        .writepage = omfs_writepage,
 372        .writepages = omfs_writepages,
 373        .write_begin = omfs_write_begin,
 374        .write_end = generic_write_end,
 375        .bmap = omfs_bmap,
 376};
 377
 378
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.