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                /* Fail early and avoid ENOSPC during the btree operation */
 121                res = hfs_bmap_reserve(fd->tree, fd->tree->depth + 1);
 122                if (res)
 123                        return res;
 124                hfs_brec_insert(fd, HFS_I(inode)->cached_extents, sizeof(hfs_extent_rec));
 125                HFS_I(inode)->flags &= ~(HFS_FLG_EXT_DIRTY|HFS_FLG_EXT_NEW);
 126        } else {
 127                if (res)
 128                        return res;
 129                hfs_bnode_write(fd->bnode, HFS_I(inode)->cached_extents, fd->entryoffset, fd->entrylength);
 130                HFS_I(inode)->flags &= ~HFS_FLG_EXT_DIRTY;
 131        }
 132        return 0;
 133}
 134
 135int hfs_ext_write_extent(struct inode *inode)
 136{
 137        struct hfs_find_data fd;
 138        int res = 0;
 139
 140        if (HFS_I(inode)->flags & HFS_FLG_EXT_DIRTY) {
 141                res = hfs_find_init(HFS_SB(inode->i_sb)->ext_tree, &fd);
 142                if (res)
 143                        return res;
 144                res = __hfs_ext_write_extent(inode, &fd);
 145                hfs_find_exit(&fd);
 146        }
 147        return res;
 148}
 149
 150static inline int __hfs_ext_read_extent(struct hfs_find_data *fd, struct hfs_extent *extent,
 151                                        u32 cnid, u32 block, u8 type)
 152{
 153        int res;
 154
 155        hfs_ext_build_key(fd->search_key, cnid, block, type);
 156        fd->key->ext.FNum = 0;
 157        res = hfs_brec_find(fd);
 158        if (res && res != -ENOENT)
 159                return res;
 160        if (fd->key->ext.FNum != fd->search_key->ext.FNum ||
 161            fd->key->ext.FkType != fd->search_key->ext.FkType)
 162                return -ENOENT;
 163        if (fd->entrylength != sizeof(hfs_extent_rec))
 164                return -EIO;
 165        hfs_bnode_read(fd->bnode, extent, fd->entryoffset, sizeof(hfs_extent_rec));
 166        return 0;
 167}
 168
 169static inline int __hfs_ext_cache_extent(struct hfs_find_data *fd, struct inode *inode, u32 block)
 170{
 171        int res;
 172
 173        if (HFS_I(inode)->flags & HFS_FLG_EXT_DIRTY) {
 174                res = __hfs_ext_write_extent(inode, fd);
 175                if (res)
 176                        return res;
 177        }
 178
 179        res = __hfs_ext_read_extent(fd, HFS_I(inode)->cached_extents, inode->i_ino,
 180                                    block, HFS_IS_RSRC(inode) ? HFS_FK_RSRC : HFS_FK_DATA);
 181        if (!res) {
 182                HFS_I(inode)->cached_start = be16_to_cpu(fd->key->ext.FABN);
 183                HFS_I(inode)->cached_blocks = hfs_ext_block_count(HFS_I(inode)->cached_extents);
 184        } else {
 185                HFS_I(inode)->cached_start = HFS_I(inode)->cached_blocks = 0;
 186                HFS_I(inode)->flags &= ~(HFS_FLG_EXT_DIRTY|HFS_FLG_EXT_NEW);
 187        }
 188        return res;
 189}
 190
 191static int hfs_ext_read_extent(struct inode *inode, u16 block)
 192{
 193        struct hfs_find_data fd;
 194        int res;
 195
 196        if (block >= HFS_I(inode)->cached_start &&
 197            block < HFS_I(inode)->cached_start + HFS_I(inode)->cached_blocks)
 198                return 0;
 199
 200        res = hfs_find_init(HFS_SB(inode->i_sb)->ext_tree, &fd);
 201        if (!res) {
 202                res = __hfs_ext_cache_extent(&fd, inode, block);
 203                hfs_find_exit(&fd);
 204        }
 205        return res;
 206}
 207
 208static void hfs_dump_extent(struct hfs_extent *extent)
 209{
 210        int i;
 211
 212        hfs_dbg(EXTENT, "   ");
 213        for (i = 0; i < 3; i++)
 214                hfs_dbg_cont(EXTENT, " %u:%u",
 215                             be16_to_cpu(extent[i].block),
 216                             be16_to_cpu(extent[i].count));
 217        hfs_dbg_cont(EXTENT, "\n");
 218}
 219
 220static int hfs_add_extent(struct hfs_extent *extent, u16 offset,
 221                          u16 alloc_block, u16 block_count)
 222{
 223        u16 count, start;
 224        int i;
 225
 226        hfs_dump_extent(extent);
 227        for (i = 0; i < 3; extent++, i++) {
 228                count = be16_to_cpu(extent->count);
 229                if (offset == count) {
 230                        start = be16_to_cpu(extent->block);
 231                        if (alloc_block != start + count) {
 232                                if (++i >= 3)
 233                                        return -ENOSPC;
 234                                extent++;
 235                                extent->block = cpu_to_be16(alloc_block);
 236                        } else
 237                                block_count += count;
 238                        extent->count = cpu_to_be16(block_count);
 239                        return 0;
 240                } else if (offset < count)
 241                        break;
 242                offset -= count;
 243        }
 244        /* panic? */
 245        return -EIO;
 246}
 247
 248static int hfs_free_extents(struct super_block *sb, struct hfs_extent *extent,
 249                            u16 offset, u16 block_nr)
 250{
 251        u16 count, start;
 252        int i;
 253
 254        hfs_dump_extent(extent);
 255        for (i = 0; i < 3; extent++, i++) {
 256                count = be16_to_cpu(extent->count);
 257                if (offset == count)
 258                        goto found;
 259                else if (offset < count)
 260                        break;
 261                offset -= count;
 262        }
 263        /* panic? */
 264        return -EIO;
 265found:
 266        for (;;) {
 267                start = be16_to_cpu(extent->block);
 268                if (count <= block_nr) {
 269                        hfs_clear_vbm_bits(sb, start, count);
 270                        extent->block = 0;
 271                        extent->count = 0;
 272                        block_nr -= count;
 273                } else {
 274                        count -= block_nr;
 275                        hfs_clear_vbm_bits(sb, start + count, block_nr);
 276                        extent->count = cpu_to_be16(count);
 277                        block_nr = 0;
 278                }
 279                if (!block_nr || !i)
 280                        return 0;
 281                i--;
 282                extent--;
 283                count = be16_to_cpu(extent->count);
 284        }
 285}
 286
 287int hfs_free_fork(struct super_block *sb, struct hfs_cat_file *file, int type)
 288{
 289        struct hfs_find_data fd;
 290        u32 total_blocks, blocks, start;
 291        u32 cnid = be32_to_cpu(file->FlNum);
 292        struct hfs_extent *extent;
 293        int res, i;
 294
 295        if (type == HFS_FK_DATA) {
 296                total_blocks = be32_to_cpu(file->PyLen);
 297                extent = file->ExtRec;
 298        } else {
 299                total_blocks = be32_to_cpu(file->RPyLen);
 300                extent = file->RExtRec;
 301        }
 302        total_blocks /= HFS_SB(sb)->alloc_blksz;
 303        if (!total_blocks)
 304                return 0;
 305
 306        blocks = 0;
 307        for (i = 0; i < 3; i++)
 308                blocks += be16_to_cpu(extent[i].count);
 309
 310        res = hfs_free_extents(sb, extent, blocks, blocks);
 311        if (res)
 312                return res;
 313        if (total_blocks == blocks)
 314                return 0;
 315
 316        res = hfs_find_init(HFS_SB(sb)->ext_tree, &fd);
 317        if (res)
 318                return res;
 319        do {
 320                res = __hfs_ext_read_extent(&fd, extent, cnid, total_blocks, type);
 321                if (res)
 322                        break;
 323                start = be16_to_cpu(fd.key->ext.FABN);
 324                hfs_free_extents(sb, extent, total_blocks - start, total_blocks);
 325                hfs_brec_remove(&fd);
 326                total_blocks = start;
 327        } while (total_blocks > blocks);
 328        hfs_find_exit(&fd);
 329
 330        return res;
 331}
 332
 333/*
 334 * hfs_get_block
 335 */
 336int hfs_get_block(struct inode *inode, sector_t block,
 337                  struct buffer_head *bh_result, int create)
 338{
 339        struct super_block *sb;
 340        u16 dblock, ablock;
 341        int res;
 342
 343        sb = inode->i_sb;
 344        /* Convert inode block to disk allocation block */
 345        ablock = (u32)block / HFS_SB(sb)->fs_div;
 346
 347        if (block >= HFS_I(inode)->fs_blocks) {
 348                if (!create)
 349                        return 0;
 350                if (block > HFS_I(inode)->fs_blocks)
 351                        return -EIO;
 352                if (ablock >= HFS_I(inode)->alloc_blocks) {
 353                        res = hfs_extend_file(inode);
 354                        if (res)
 355                                return res;
 356                }
 357        } else
 358                create = 0;
 359
 360        if (ablock < HFS_I(inode)->first_blocks) {
 361                dblock = hfs_ext_find_block(HFS_I(inode)->first_extents, ablock);
 362                goto done;
 363        }
 364
 365        mutex_lock(&HFS_I(inode)->extents_lock);
 366        res = hfs_ext_read_extent(inode, ablock);
 367        if (!res)
 368                dblock = hfs_ext_find_block(HFS_I(inode)->cached_extents,
 369                                            ablock - HFS_I(inode)->cached_start);
 370        else {
 371                mutex_unlock(&HFS_I(inode)->extents_lock);
 372                return -EIO;
 373        }
 374        mutex_unlock(&HFS_I(inode)->extents_lock);
 375
 376done:
 377        map_bh(bh_result, sb, HFS_SB(sb)->fs_start +
 378               dblock * HFS_SB(sb)->fs_div +
 379               (u32)block % HFS_SB(sb)->fs_div);
 380
 381        if (create) {
 382                set_buffer_new(bh_result);
 383                HFS_I(inode)->phys_size += sb->s_blocksize;
 384                HFS_I(inode)->fs_blocks++;
 385                inode_add_bytes(inode, sb->s_blocksize);
 386                mark_inode_dirty(inode);
 387        }
 388        return 0;
 389}
 390
 391int hfs_extend_file(struct inode *inode)
 392{
 393        struct super_block *sb = inode->i_sb;
 394        u32 start, len, goal;
 395        int res;
 396
 397        mutex_lock(&HFS_I(inode)->extents_lock);
 398        if (HFS_I(inode)->alloc_blocks == HFS_I(inode)->first_blocks)
 399                goal = hfs_ext_lastblock(HFS_I(inode)->first_extents);
 400        else {
 401                res = hfs_ext_read_extent(inode, HFS_I(inode)->alloc_blocks);
 402                if (res)
 403                        goto out;
 404                goal = hfs_ext_lastblock(HFS_I(inode)->cached_extents);
 405        }
 406
 407        len = HFS_I(inode)->clump_blocks;
 408        start = hfs_vbm_search_free(sb, goal, &len);
 409        if (!len) {
 410                res = -ENOSPC;
 411                goto out;
 412        }
 413
 414        hfs_dbg(EXTENT, "extend %lu: %u,%u\n", inode->i_ino, start, len);
 415        if (HFS_I(inode)->alloc_blocks == HFS_I(inode)->first_blocks) {
 416                if (!HFS_I(inode)->first_blocks) {
 417                        hfs_dbg(EXTENT, "first extents\n");
 418                        /* no extents yet */
 419                        HFS_I(inode)->first_extents[0].block = cpu_to_be16(start);
 420                        HFS_I(inode)->first_extents[0].count = cpu_to_be16(len);
 421                        res = 0;
 422                } else {
 423                        /* try to append to extents in inode */
 424                        res = hfs_add_extent(HFS_I(inode)->first_extents,
 425                                             HFS_I(inode)->alloc_blocks,
 426                                             start, len);
 427                        if (res == -ENOSPC)
 428                                goto insert_extent;
 429                }
 430                if (!res) {
 431                        hfs_dump_extent(HFS_I(inode)->first_extents);
 432                        HFS_I(inode)->first_blocks += len;
 433                }
 434        } else {
 435                res = hfs_add_extent(HFS_I(inode)->cached_extents,
 436                                     HFS_I(inode)->alloc_blocks -
 437                                     HFS_I(inode)->cached_start,
 438                                     start, len);
 439                if (!res) {
 440                        hfs_dump_extent(HFS_I(inode)->cached_extents);
 441                        HFS_I(inode)->flags |= HFS_FLG_EXT_DIRTY;
 442                        HFS_I(inode)->cached_blocks += len;
 443                } else if (res == -ENOSPC)
 444                        goto insert_extent;
 445        }
 446out:
 447        mutex_unlock(&HFS_I(inode)->extents_lock);
 448        if (!res) {
 449                HFS_I(inode)->alloc_blocks += len;
 450                mark_inode_dirty(inode);
 451                if (inode->i_ino < HFS_FIRSTUSER_CNID)
 452                        set_bit(HFS_FLG_ALT_MDB_DIRTY, &HFS_SB(sb)->flags);
 453                set_bit(HFS_FLG_MDB_DIRTY, &HFS_SB(sb)->flags);
 454                hfs_mark_mdb_dirty(sb);
 455        }
 456        return res;
 457
 458insert_extent:
 459        hfs_dbg(EXTENT, "insert new extent\n");
 460        res = hfs_ext_write_extent(inode);
 461        if (res)
 462                goto out;
 463
 464        memset(HFS_I(inode)->cached_extents, 0, sizeof(hfs_extent_rec));
 465        HFS_I(inode)->cached_extents[0].block = cpu_to_be16(start);
 466        HFS_I(inode)->cached_extents[0].count = cpu_to_be16(len);
 467        hfs_dump_extent(HFS_I(inode)->cached_extents);
 468        HFS_I(inode)->flags |= HFS_FLG_EXT_DIRTY|HFS_FLG_EXT_NEW;
 469        HFS_I(inode)->cached_start = HFS_I(inode)->alloc_blocks;
 470        HFS_I(inode)->cached_blocks = len;
 471
 472        res = 0;
 473        goto out;
 474}
 475
 476void hfs_file_truncate(struct inode *inode)
 477{
 478        struct super_block *sb = inode->i_sb;
 479        struct hfs_find_data fd;
 480        u16 blk_cnt, alloc_cnt, start;
 481        u32 size;
 482        int res;
 483
 484        hfs_dbg(INODE, "truncate: %lu, %Lu -> %Lu\n",
 485                inode->i_ino, (long long)HFS_I(inode)->phys_size,
 486                inode->i_size);
 487        if (inode->i_size > HFS_I(inode)->phys_size) {
 488                struct address_space *mapping = inode->i_mapping;
 489                void *fsdata;
 490                struct page *page;
 491
 492                /* XXX: Can use generic_cont_expand? */
 493                size = inode->i_size - 1;
 494                res = pagecache_write_begin(NULL, mapping, size+1, 0, 0,
 495                                            &page, &fsdata);
 496                if (!res) {
 497                        res = pagecache_write_end(NULL, mapping, size+1, 0, 0,
 498                                        page, fsdata);
 499                }
 500                if (res)
 501                        inode->i_size = HFS_I(inode)->phys_size;
 502                return;
 503        } else if (inode->i_size == HFS_I(inode)->phys_size)
 504                return;
 505        size = inode->i_size + HFS_SB(sb)->alloc_blksz - 1;
 506        blk_cnt = size / HFS_SB(sb)->alloc_blksz;
 507        alloc_cnt = HFS_I(inode)->alloc_blocks;
 508        if (blk_cnt == alloc_cnt)
 509                goto out;
 510
 511        mutex_lock(&HFS_I(inode)->extents_lock);
 512        res = hfs_find_init(HFS_SB(sb)->ext_tree, &fd);
 513        if (res) {
 514                mutex_unlock(&HFS_I(inode)->extents_lock);
 515                /* XXX: We lack error handling of hfs_file_truncate() */
 516                return;
 517        }
 518        while (1) {
 519                if (alloc_cnt == HFS_I(inode)->first_blocks) {
 520                        hfs_free_extents(sb, HFS_I(inode)->first_extents,
 521                                         alloc_cnt, alloc_cnt - blk_cnt);
 522                        hfs_dump_extent(HFS_I(inode)->first_extents);
 523                        HFS_I(inode)->first_blocks = blk_cnt;
 524                        break;
 525                }
 526                res = __hfs_ext_cache_extent(&fd, inode, alloc_cnt);
 527                if (res)
 528                        break;
 529                start = HFS_I(inode)->cached_start;
 530                hfs_free_extents(sb, HFS_I(inode)->cached_extents,
 531                                 alloc_cnt - start, alloc_cnt - blk_cnt);
 532                hfs_dump_extent(HFS_I(inode)->cached_extents);
 533                if (blk_cnt > start) {
 534                        HFS_I(inode)->flags |= HFS_FLG_EXT_DIRTY;
 535                        break;
 536                }
 537                alloc_cnt = start;
 538                HFS_I(inode)->cached_start = HFS_I(inode)->cached_blocks = 0;
 539                HFS_I(inode)->flags &= ~(HFS_FLG_EXT_DIRTY|HFS_FLG_EXT_NEW);
 540                hfs_brec_remove(&fd);
 541        }
 542        hfs_find_exit(&fd);
 543        mutex_unlock(&HFS_I(inode)->extents_lock);
 544
 545        HFS_I(inode)->alloc_blocks = blk_cnt;
 546out:
 547        HFS_I(inode)->phys_size = inode->i_size;
 548        HFS_I(inode)->fs_blocks = (inode->i_size + sb->s_blocksize - 1) >> sb->s_blocksize_bits;
 549        inode_set_bytes(inode, HFS_I(inode)->fs_blocks << sb->s_blocksize_bits);
 550        mark_inode_dirty(inode);
 551}
 552