linux/fs/hfs/extent.c
<<
>>
Prefs
   1/*
   2 *  linux/fs/hfs/extent.c
   3 *
   4 * Copyright (C) 1995-1997  Paul H. Hargrove
   5 * (C) 2003 Ardis Technologies <roman@ardistech.com>
   6 * This file may be distributed under the terms of the GNU General Public License.
   7 *
   8 * This file contains the functions related to the extents B-tree.
   9 */
  10
  11#include <linux/pagemap.h>
  12
  13#include "hfs_fs.h"
  14#include "btree.h"
  15
  16/*================ File-local functions ================*/
  17
  18/*
  19 * build_key
  20 */
  21static void hfs_ext_build_key(hfs_btree_key *key, u32 cnid, u16 block, u8 type)
  22{
  23        key->key_len = 7;
  24        key->ext.FkType = type;
  25        key->ext.FNum = cpu_to_be32(cnid);
  26        key->ext.FABN = cpu_to_be16(block);
  27}
  28
  29/*
  30 * hfs_ext_compare()
  31 *
  32 * Description:
  33 *   This is the comparison function used for the extents B-tree.  In
  34 *   comparing extent B-tree entries, the file id is the most
  35 *   significant field (compared as unsigned ints); the fork type is
  36 *   the second most significant field (compared as unsigned chars);
  37 *   and the allocation block number field is the least significant
  38 *   (compared as unsigned ints).
  39 * Input Variable(s):
  40 *   struct hfs_ext_key *key1: pointer to the first key to compare
  41 *   struct hfs_ext_key *key2: pointer to the second key to compare
  42 * Output Variable(s):
  43 *   NONE
  44 * Returns:
  45 *   int: negative if key1<key2, positive if key1>key2, and 0 if key1==key2
  46 * Preconditions:
  47 *   key1 and key2 point to "valid" (struct hfs_ext_key)s.
  48 * Postconditions:
  49 *   This function has no side-effects */
  50int hfs_ext_keycmp(const btree_key *key1, const btree_key *key2)
  51{
  52        __be32 fnum1, fnum2;
  53        __be16 block1, block2;
  54
  55        fnum1 = key1->ext.FNum;
  56        fnum2 = key2->ext.FNum;
  57        if (fnum1 != fnum2)
  58                return be32_to_cpu(fnum1) < be32_to_cpu(fnum2) ? -1 : 1;
  59        if (key1->ext.FkType != key2->ext.FkType)
  60                return key1->ext.FkType < key2->ext.FkType ? -1 : 1;
  61
  62        block1 = key1->ext.FABN;
  63        block2 = key2->ext.FABN;
  64        if (block1 == block2)
  65                return 0;
  66        return be16_to_cpu(block1) < be16_to_cpu(block2) ? -1 : 1;
  67}
  68
  69/*
  70 * hfs_ext_find_block
  71 *
  72 * Find a block within an extent record
  73 */
  74static u16 hfs_ext_find_block(struct hfs_extent *ext, u16 off)
  75{
  76        int i;
  77        u16 count;
  78
  79        for (i = 0; i < 3; ext++, i++) {
  80                count = be16_to_cpu(ext->count);
  81                if (off < count)
  82                        return be16_to_cpu(ext->block) + off;
  83                off -= count;
  84        }
  85        /* panic? */
  86        return 0;
  87}
  88
  89static int hfs_ext_block_count(struct hfs_extent *ext)
  90{
  91        int i;
  92        u16 count = 0;
  93
  94        for (i = 0; i < 3; ext++, i++)
  95                count += be16_to_cpu(ext->count);
  96        return count;
  97}
  98
  99static u16 hfs_ext_lastblock(struct hfs_extent *ext)
 100{
 101        int i;
 102
 103        ext += 2;
 104        for (i = 0; i < 2; ext--, i++)
 105                if (ext->count)
 106                        break;
 107        return be16_to_cpu(ext->block) + be16_to_cpu(ext->count);
 108}
 109
 110static int __hfs_ext_write_extent(struct inode *inode, struct hfs_find_data *fd)
 111{
 112        int res;
 113
 114        hfs_ext_build_key(fd->search_key, inode->i_ino, HFS_I(inode)->cached_start,
 115                          HFS_IS_RSRC(inode) ?  HFS_FK_RSRC : HFS_FK_DATA);
 116        res = hfs_brec_find(fd);
 117        if (HFS_I(inode)->flags & HFS_FLG_EXT_NEW) {
 118                if (res != -ENOENT)
 119                        return res;
 120                hfs_brec_insert(fd, HFS_I(inode)->cached_extents, sizeof(hfs_extent_rec));
 121                HFS_I(inode)->flags &= ~(HFS_FLG_EXT_DIRTY|HFS_FLG_EXT_NEW);
 122        } else {
 123                if (res)
 124                        return res;
 125                hfs_bnode_write(fd->bnode, HFS_I(inode)->cached_extents, fd->entryoffset, fd->entrylength);
 126                HFS_I(inode)->flags &= ~HFS_FLG_EXT_DIRTY;
 127        }
 128        return 0;
 129}
 130
 131int hfs_ext_write_extent(struct inode *inode)
 132{
 133        struct hfs_find_data fd;
 134        int res = 0;
 135
 136        if (HFS_I(inode)->flags & HFS_FLG_EXT_DIRTY) {
 137                res = hfs_find_init(HFS_SB(inode->i_sb)->ext_tree, &fd);
 138                if (res)
 139                        return res;
 140                res = __hfs_ext_write_extent(inode, &fd);
 141                hfs_find_exit(&fd);
 142        }
 143        return res;
 144}
 145
 146static inline int __hfs_ext_read_extent(struct hfs_find_data *fd, struct hfs_extent *extent,
 147                                        u32 cnid, u32 block, u8 type)
 148{
 149        int res;
 150
 151        hfs_ext_build_key(fd->search_key, cnid, block, type);
 152        fd->key->ext.FNum = 0;
 153        res = hfs_brec_find(fd);
 154        if (res && res != -ENOENT)
 155                return res;
 156        if (fd->key->ext.FNum != fd->search_key->ext.FNum ||
 157            fd->key->ext.FkType != fd->search_key->ext.FkType)
 158                return -ENOENT;
 159        if (fd->entrylength != sizeof(hfs_extent_rec))
 160                return -EIO;
 161        hfs_bnode_read(fd->bnode, extent, fd->entryoffset, sizeof(hfs_extent_rec));
 162        return 0;
 163}
 164
 165static inline int __hfs_ext_cache_extent(struct hfs_find_data *fd, struct inode *inode, u32 block)
 166{
 167        int res;
 168
 169        if (HFS_I(inode)->flags & HFS_FLG_EXT_DIRTY) {
 170                res = __hfs_ext_write_extent(inode, fd);
 171                if (res)
 172                        return res;
 173        }
 174
 175        res = __hfs_ext_read_extent(fd, HFS_I(inode)->cached_extents, inode->i_ino,
 176                                    block, HFS_IS_RSRC(inode) ? HFS_FK_RSRC : HFS_FK_DATA);
 177        if (!res) {
 178                HFS_I(inode)->cached_start = be16_to_cpu(fd->key->ext.FABN);
 179                HFS_I(inode)->cached_blocks = hfs_ext_block_count(HFS_I(inode)->cached_extents);
 180        } else {
 181                HFS_I(inode)->cached_start = HFS_I(inode)->cached_blocks = 0;
 182                HFS_I(inode)->flags &= ~(HFS_FLG_EXT_DIRTY|HFS_FLG_EXT_NEW);
 183        }
 184        return res;
 185}
 186
 187static int hfs_ext_read_extent(struct inode *inode, u16 block)
 188{
 189        struct hfs_find_data fd;
 190        int res;
 191
 192        if (block >= HFS_I(inode)->cached_start &&
 193            block < HFS_I(inode)->cached_start + HFS_I(inode)->cached_blocks)
 194                return 0;
 195
 196        res = hfs_find_init(HFS_SB(inode->i_sb)->ext_tree, &fd);
 197        if (!res) {
 198                res = __hfs_ext_cache_extent(&fd, inode, block);
 199                hfs_find_exit(&fd);
 200        }
 201        return res;
 202}
 203
 204static void hfs_dump_extent(struct hfs_extent *extent)
 205{
 206        int i;
 207
 208        hfs_dbg(EXTENT, "   ");
 209        for (i = 0; i < 3; i++)
 210                hfs_dbg_cont(EXTENT, " %u:%u",
 211                             be16_to_cpu(extent[i].block),
 212                             be16_to_cpu(extent[i].count));
 213        hfs_dbg_cont(EXTENT, "\n");
 214}
 215
 216static int hfs_add_extent(struct hfs_extent *extent, u16 offset,
 217                          u16 alloc_block, u16 block_count)
 218{
 219        u16 count, start;
 220        int i;
 221
 222        hfs_dump_extent(extent);
 223        for (i = 0; i < 3; extent++, i++) {
 224                count = be16_to_cpu(extent->count);
 225                if (offset == count) {
 226                        start = be16_to_cpu(extent->block);
 227                        if (alloc_block != start + count) {
 228                                if (++i >= 3)
 229                                        return -ENOSPC;
 230                                extent++;
 231                                extent->block = cpu_to_be16(alloc_block);
 232                        } else
 233                                block_count += count;
 234                        extent->count = cpu_to_be16(block_count);
 235                        return 0;
 236                } else if (offset < count)
 237                        break;
 238                offset -= count;
 239        }
 240        /* panic? */
 241        return -EIO;
 242}
 243
 244static int hfs_free_extents(struct super_block *sb, struct hfs_extent *extent,
 245                            u16 offset, u16 block_nr)
 246{
 247        u16 count, start;
 248        int i;
 249
 250        hfs_dump_extent(extent);
 251        for (i = 0; i < 3; extent++, i++) {
 252                count = be16_to_cpu(extent->count);
 253                if (offset == count)
 254                        goto found;
 255                else if (offset < count)
 256                        break;
 257                offset -= count;
 258        }
 259        /* panic? */
 260        return -EIO;
 261found:
 262        for (;;) {
 263                start = be16_to_cpu(extent->block);
 264                if (count <= block_nr) {
 265                        hfs_clear_vbm_bits(sb, start, count);
 266                        extent->block = 0;
 267                        extent->count = 0;
 268                        block_nr -= count;
 269                } else {
 270                        count -= block_nr;
 271                        hfs_clear_vbm_bits(sb, start + count, block_nr);
 272                        extent->count = cpu_to_be16(count);
 273                        block_nr = 0;
 274                }
 275                if (!block_nr || !i)
 276                        return 0;
 277                i--;
 278                extent--;
 279                count = be16_to_cpu(extent->count);
 280        }
 281}
 282
 283int hfs_free_fork(struct super_block *sb, struct hfs_cat_file *file, int type)
 284{
 285        struct hfs_find_data fd;
 286        u32 total_blocks, blocks, start;
 287        u32 cnid = be32_to_cpu(file->FlNum);
 288        struct hfs_extent *extent;
 289        int res, i;
 290
 291        if (type == HFS_FK_DATA) {
 292                total_blocks = be32_to_cpu(file->PyLen);
 293                extent = file->ExtRec;
 294        } else {
 295                total_blocks = be32_to_cpu(file->RPyLen);
 296                extent = file->RExtRec;
 297        }
 298        total_blocks /= HFS_SB(sb)->alloc_blksz;
 299        if (!total_blocks)
 300                return 0;
 301
 302        blocks = 0;
 303        for (i = 0; i < 3; extent++, i++)
 304                blocks += be16_to_cpu(extent[i].count);
 305
 306        res = hfs_free_extents(sb, extent, blocks, blocks);
 307        if (res)
 308                return res;
 309        if (total_blocks == blocks)
 310                return 0;
 311
 312        res = hfs_find_init(HFS_SB(sb)->ext_tree, &fd);
 313        if (res)
 314                return res;
 315        do {
 316                res = __hfs_ext_read_extent(&fd, extent, cnid, total_blocks, type);
 317                if (res)
 318                        break;
 319                start = be16_to_cpu(fd.key->ext.FABN);
 320                hfs_free_extents(sb, extent, total_blocks - start, total_blocks);
 321                hfs_brec_remove(&fd);
 322                total_blocks = start;
 323        } while (total_blocks > blocks);
 324        hfs_find_exit(&fd);
 325
 326        return res;
 327}
 328
 329/*
 330 * hfs_get_block
 331 */
 332int hfs_get_block(struct inode *inode, sector_t block,
 333                  struct buffer_head *bh_result, int create)
 334{
 335        struct super_block *sb;
 336        u16 dblock, ablock;
 337        int res;
 338
 339        sb = inode->i_sb;
 340        /* Convert inode block to disk allocation block */
 341        ablock = (u32)block / HFS_SB(sb)->fs_div;
 342
 343        if (block >= HFS_I(inode)->fs_blocks) {
 344                if (block > HFS_I(inode)->fs_blocks || !create)
 345                        return -EIO;
 346                if (ablock >= HFS_I(inode)->alloc_blocks) {
 347                        res = hfs_extend_file(inode);
 348                        if (res)
 349                                return res;
 350                }
 351        } else
 352                create = 0;
 353
 354        if (ablock < HFS_I(inode)->first_blocks) {
 355                dblock = hfs_ext_find_block(HFS_I(inode)->first_extents, ablock);
 356                goto done;
 357        }
 358
 359        mutex_lock(&HFS_I(inode)->extents_lock);
 360        res = hfs_ext_read_extent(inode, ablock);
 361        if (!res)
 362                dblock = hfs_ext_find_block(HFS_I(inode)->cached_extents,
 363                                            ablock - HFS_I(inode)->cached_start);
 364        else {
 365                mutex_unlock(&HFS_I(inode)->extents_lock);
 366                return -EIO;
 367        }
 368        mutex_unlock(&HFS_I(inode)->extents_lock);
 369
 370done:
 371        map_bh(bh_result, sb, HFS_SB(sb)->fs_start +
 372               dblock * HFS_SB(sb)->fs_div +
 373               (u32)block % HFS_SB(sb)->fs_div);
 374
 375        if (create) {
 376                set_buffer_new(bh_result);
 377                HFS_I(inode)->phys_size += sb->s_blocksize;
 378                HFS_I(inode)->fs_blocks++;
 379                inode_add_bytes(inode, sb->s_blocksize);
 380                mark_inode_dirty(inode);
 381        }
 382        return 0;
 383}
 384
 385int hfs_extend_file(struct inode *inode)
 386{
 387        struct super_block *sb = inode->i_sb;
 388        u32 start, len, goal;
 389        int res;
 390
 391        mutex_lock(&HFS_I(inode)->extents_lock);
 392        if (HFS_I(inode)->alloc_blocks == HFS_I(inode)->first_blocks)
 393                goal = hfs_ext_lastblock(HFS_I(inode)->first_extents);
 394        else {
 395                res = hfs_ext_read_extent(inode, HFS_I(inode)->alloc_blocks);
 396                if (res)
 397                        goto out;
 398                goal = hfs_ext_lastblock(HFS_I(inode)->cached_extents);
 399        }
 400
 401        len = HFS_I(inode)->clump_blocks;
 402        start = hfs_vbm_search_free(sb, goal, &len);
 403        if (!len) {
 404                res = -ENOSPC;
 405                goto out;
 406        }
 407
 408        hfs_dbg(EXTENT, "extend %lu: %u,%u\n", inode->i_ino, start, len);
 409        if (HFS_I(inode)->alloc_blocks == HFS_I(inode)->first_blocks) {
 410                if (!HFS_I(inode)->first_blocks) {
 411                        hfs_dbg(EXTENT, "first extents\n");
 412                        /* no extents yet */
 413                        HFS_I(inode)->first_extents[0].block = cpu_to_be16(start);
 414                        HFS_I(inode)->first_extents[0].count = cpu_to_be16(len);
 415                        res = 0;
 416                } else {
 417                        /* try to append to extents in inode */
 418                        res = hfs_add_extent(HFS_I(inode)->first_extents,
 419                                             HFS_I(inode)->alloc_blocks,
 420                                             start, len);
 421                        if (res == -ENOSPC)
 422                                goto insert_extent;
 423                }
 424                if (!res) {
 425                        hfs_dump_extent(HFS_I(inode)->first_extents);
 426                        HFS_I(inode)->first_blocks += len;
 427                }
 428        } else {
 429                res = hfs_add_extent(HFS_I(inode)->cached_extents,
 430                                     HFS_I(inode)->alloc_blocks -
 431                                     HFS_I(inode)->cached_start,
 432                                     start, len);
 433                if (!res) {
 434                        hfs_dump_extent(HFS_I(inode)->cached_extents);
 435                        HFS_I(inode)->flags |= HFS_FLG_EXT_DIRTY;
 436                        HFS_I(inode)->cached_blocks += len;
 437                } else if (res == -ENOSPC)
 438                        goto insert_extent;
 439        }
 440out:
 441        mutex_unlock(&HFS_I(inode)->extents_lock);
 442        if (!res) {
 443                HFS_I(inode)->alloc_blocks += len;
 444                mark_inode_dirty(inode);
 445                if (inode->i_ino < HFS_FIRSTUSER_CNID)
 446                        set_bit(HFS_FLG_ALT_MDB_DIRTY, &HFS_SB(sb)->flags);
 447                set_bit(HFS_FLG_MDB_DIRTY, &HFS_SB(sb)->flags);
 448                hfs_mark_mdb_dirty(sb);
 449        }
 450        return res;
 451
 452insert_extent:
 453        hfs_dbg(EXTENT, "insert new extent\n");
 454        res = hfs_ext_write_extent(inode);
 455        if (res)
 456                goto out;
 457
 458        memset(HFS_I(inode)->cached_extents, 0, sizeof(hfs_extent_rec));
 459        HFS_I(inode)->cached_extents[0].block = cpu_to_be16(start);
 460        HFS_I(inode)->cached_extents[0].count = cpu_to_be16(len);
 461        hfs_dump_extent(HFS_I(inode)->cached_extents);
 462        HFS_I(inode)->flags |= HFS_FLG_EXT_DIRTY|HFS_FLG_EXT_NEW;
 463        HFS_I(inode)->cached_start = HFS_I(inode)->alloc_blocks;
 464        HFS_I(inode)->cached_blocks = len;
 465
 466        res = 0;
 467        goto out;
 468}
 469
 470void hfs_file_truncate(struct inode *inode)
 471{
 472        struct super_block *sb = inode->i_sb;
 473        struct hfs_find_data fd;
 474        u16 blk_cnt, alloc_cnt, start;
 475        u32 size;
 476        int res;
 477
 478        hfs_dbg(INODE, "truncate: %lu, %Lu -> %Lu\n",
 479                inode->i_ino, (long long)HFS_I(inode)->phys_size,
 480                inode->i_size);
 481        if (inode->i_size > HFS_I(inode)->phys_size) {
 482                struct address_space *mapping = inode->i_mapping;
 483                void *fsdata;
 484                struct page *page;
 485
 486                /* XXX: Can use generic_cont_expand? */
 487                size = inode->i_size - 1;
 488                res = pagecache_write_begin(NULL, mapping, size+1, 0,
 489                                AOP_FLAG_UNINTERRUPTIBLE, &page, &fsdata);
 490                if (!res) {
 491                        res = pagecache_write_end(NULL, mapping, size+1, 0, 0,
 492                                        page, fsdata);
 493                }
 494                if (res)
 495                        inode->i_size = HFS_I(inode)->phys_size;
 496                return;
 497        } else if (inode->i_size == HFS_I(inode)->phys_size)
 498                return;
 499        size = inode->i_size + HFS_SB(sb)->alloc_blksz - 1;
 500        blk_cnt = size / HFS_SB(sb)->alloc_blksz;
 501        alloc_cnt = HFS_I(inode)->alloc_blocks;
 502        if (blk_cnt == alloc_cnt)
 503                goto out;
 504
 505        mutex_lock(&HFS_I(inode)->extents_lock);
 506        res = hfs_find_init(HFS_SB(sb)->ext_tree, &fd);
 507        if (res) {
 508                mutex_unlock(&HFS_I(inode)->extents_lock);
 509                /* XXX: We lack error handling of hfs_file_truncate() */
 510                return;
 511        }
 512        while (1) {
 513                if (alloc_cnt == HFS_I(inode)->first_blocks) {
 514                        hfs_free_extents(sb, HFS_I(inode)->first_extents,
 515                                         alloc_cnt, alloc_cnt - blk_cnt);
 516                        hfs_dump_extent(HFS_I(inode)->first_extents);
 517                        HFS_I(inode)->first_blocks = blk_cnt;
 518                        break;
 519                }
 520                res = __hfs_ext_cache_extent(&fd, inode, alloc_cnt);
 521                if (res)
 522                        break;
 523                start = HFS_I(inode)->cached_start;
 524                hfs_free_extents(sb, HFS_I(inode)->cached_extents,
 525                                 alloc_cnt - start, alloc_cnt - blk_cnt);
 526                hfs_dump_extent(HFS_I(inode)->cached_extents);
 527                if (blk_cnt > start) {
 528                        HFS_I(inode)->flags |= HFS_FLG_EXT_DIRTY;
 529                        break;
 530                }
 531                alloc_cnt = start;
 532                HFS_I(inode)->cached_start = HFS_I(inode)->cached_blocks = 0;
 533                HFS_I(inode)->flags &= ~(HFS_FLG_EXT_DIRTY|HFS_FLG_EXT_NEW);
 534                hfs_brec_remove(&fd);
 535        }
 536        hfs_find_exit(&fd);
 537        mutex_unlock(&HFS_I(inode)->extents_lock);
 538
 539        HFS_I(inode)->alloc_blocks = blk_cnt;
 540out:
 541        HFS_I(inode)->phys_size = inode->i_size;
 542        HFS_I(inode)->fs_blocks = (inode->i_size + sb->s_blocksize - 1) >> sb->s_blocksize_bits;
 543        inode_set_bytes(inode, HFS_I(inode)->fs_blocks << sb->s_blocksize_bits);
 544        mark_inode_dirty(inode);
 545}
 546
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.