linux-old/fs/hfsplus/bnode.c
<<
>>
Prefs
   1/*
   2 *  linux/fs/hfsplus/bnode.c
   3 *
   4 * Copyright (C) 2001
   5 * Brad Boyer (flar@allandria.com)
   6 * (C) 2003 Ardis Technologies <roman@ardistech.com>
   7 *
   8 * Handle basic btree node operations
   9 */
  10
  11#include <linux/string.h>
  12#include <linux/slab.h>
  13#include <linux/pagemap.h>
  14#include <linux/fs.h>
  15#include <linux/swap.h>
  16#include <linux/version.h>
  17#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
  18#include <linux/buffer_head.h>
  19#endif
  20
  21#include "hfsplus_fs.h"
  22#include "hfsplus_raw.h"
  23
  24#define REF_PAGES       0
  25
  26int hfsplus_update_idx_rec(struct hfsplus_find_data *fd);
  27
  28/* Get the given block associated with the tree owning node */
  29struct buffer_head *hfsplus_getblk(struct inode *inode, unsigned long n)
  30{
  31        struct super_block *sb;
  32        struct buffer_head tmp_bh;
  33
  34        sb = inode->i_sb;
  35        if (hfsplus_get_block(inode, n, &tmp_bh, 1)) {
  36                printk("HFS+-fs: Failed to find block for B*Tree data\n");
  37                return NULL;
  38        }
  39        return sb_bread(sb, tmp_bh.b_blocknr);
  40}
  41
  42/* Copy a specified range of bytes from the raw data of a node */
  43void hfsplus_bnode_readbytes(hfsplus_bnode *node, void *buf,
  44                             unsigned long off, unsigned long len)
  45{
  46        unsigned long l;
  47        struct page **pagep;
  48
  49        off += node->page_offset;
  50        pagep = node->page + (off >> PAGE_CACHE_SHIFT);
  51        off &= ~PAGE_CACHE_MASK;
  52
  53        l = min(len, PAGE_CACHE_SIZE - off);
  54        memcpy(buf, hfsplus_kmap(*pagep) + off, l);
  55        hfsplus_kunmap(*pagep++);
  56
  57        while ((len -= l)) {
  58                buf += l;
  59                l = min(len, PAGE_CACHE_SIZE);
  60                memcpy(buf, hfsplus_kmap(*pagep), l);
  61                hfsplus_kunmap(*pagep++);
  62        }
  63}
  64
  65u16 hfsplus_bnode_read_u16(hfsplus_bnode *node, unsigned long off)
  66{
  67        u16 data;
  68        // optimize later...
  69        hfsplus_bnode_readbytes(node, &data, off, 2);
  70        return be16_to_cpu(data);
  71}
  72
  73void hfsplus_bnode_read_key(hfsplus_bnode *node, void *key, unsigned long off)
  74{
  75        hfsplus_btree *tree;
  76        unsigned long key_len;
  77
  78        tree = node->tree;
  79        if (node->kind == HFSPLUS_NODE_LEAF ||
  80            tree->attributes & HFSPLUS_TREE_VAR_NDXKEY_SIZE)
  81                key_len = hfsplus_bnode_read_u16(node, off) + 2;
  82        else
  83                key_len = tree->max_key_len + 2;
  84        
  85        hfsplus_bnode_readbytes(node, key, off, key_len);       
  86}
  87
  88void hfsplus_bnode_writebytes(hfsplus_bnode *node, void *buf,
  89                              unsigned long off, unsigned long len)
  90{
  91        unsigned long l;
  92        struct page **pagep;
  93
  94        off += node->page_offset;
  95        pagep = node->page + (off >> PAGE_CACHE_SHIFT);
  96        off &= ~PAGE_CACHE_MASK;
  97
  98        l = min(len, PAGE_CACHE_SIZE - off);
  99        memcpy(hfsplus_kmap(*pagep) + off, buf, l);
 100        set_page_dirty(*pagep);
 101        hfsplus_kunmap(*pagep++);
 102
 103        while ((len -= l)) {
 104                buf += l;
 105                l = min(len, PAGE_CACHE_SIZE);
 106                memcpy(hfsplus_kmap(*pagep), buf, l);
 107                set_page_dirty(*pagep);
 108                hfsplus_kunmap(*pagep++);
 109        }
 110}
 111
 112void hfsplus_bnode_write_u16(hfsplus_bnode *node, unsigned long off, u16 data)
 113{
 114        data = cpu_to_be16(data);
 115        // optimize later...
 116        hfsplus_bnode_writebytes(node, &data, off, 2);
 117}
 118
 119void hfsplus_bnode_copybytes(hfsplus_bnode *dst_node, unsigned long dst,
 120                             hfsplus_bnode *src_node, unsigned long src, unsigned long len)
 121{
 122        struct hfsplus_btree *tree;
 123        struct page **src_page, **dst_page;
 124        unsigned long l;
 125
 126        dprint(DBG_BNODE_MOD, "copybytes: %lu,%lu,%lu\n", dst, src, len);
 127        if (!len)
 128                return;
 129        tree = src_node->tree;
 130        src += src_node->page_offset;
 131        dst += dst_node->page_offset;
 132        src_page = src_node->page + (src >> PAGE_CACHE_SHIFT);
 133        src &= ~PAGE_CACHE_MASK;
 134        dst_page = dst_node->page + (dst >> PAGE_CACHE_SHIFT);
 135        dst &= ~PAGE_CACHE_MASK;
 136
 137        if (src == dst) {
 138                l = min(len, PAGE_CACHE_SIZE - src);
 139                memcpy(hfsplus_kmap(*dst_page) + src, hfsplus_kmap(*src_page) + src, l);
 140                hfsplus_kunmap(*src_page++);
 141                set_page_dirty(*dst_page);
 142                hfsplus_kunmap(*dst_page++);
 143
 144                while ((len -= l)) {
 145                        l = min(len, PAGE_CACHE_SIZE);
 146                        memcpy(hfsplus_kmap(*dst_page), hfsplus_kmap(*src_page), l);
 147                        hfsplus_kunmap(*src_page++);
 148                        set_page_dirty(*dst_page);
 149                        hfsplus_kunmap(*dst_page++);
 150                }
 151        } else {
 152                void *src_ptr, *dst_ptr;
 153
 154                do {
 155                        src_ptr = hfsplus_kmap(*src_page) + src;
 156                        dst_ptr = hfsplus_kmap(*dst_page) + dst;
 157                        if (PAGE_CACHE_SIZE - src < PAGE_CACHE_SIZE - dst) {
 158                                l = PAGE_CACHE_SIZE - src;
 159                                src = 0;
 160                                dst += l;
 161                        } else {
 162                                l = PAGE_CACHE_SIZE - dst;
 163                                src += l;
 164                                dst = 0;
 165                        }
 166                        l = min(len, l);
 167                        memcpy(dst_ptr, src_ptr, l);
 168                        hfsplus_kunmap(*src_page);
 169                        set_page_dirty(*dst_page);
 170                        hfsplus_kunmap(*dst_page);
 171                        if (!dst)
 172                                dst_page++;
 173                        else
 174                                src_page++;
 175                } while ((len -= l));
 176        }
 177}
 178
 179void hfsplus_bnode_movebytes(hfsplus_bnode *node, unsigned long dst,
 180                             unsigned long src, unsigned long len)
 181{
 182        struct page **src_page, **dst_page;
 183        unsigned long l;
 184
 185        dprint(DBG_BNODE_MOD, "movebytes: %lu,%lu,%lu\n", dst, src, len);
 186        if (!len)
 187                return;
 188        src += node->page_offset;
 189        dst += node->page_offset;
 190        if (dst > src) {
 191                src += len - 1;
 192                src_page = node->page + (src >> PAGE_CACHE_SHIFT);
 193                src = (src & ~PAGE_CACHE_MASK) + 1;
 194                dst += len - 1;
 195                dst_page = node->page + (dst >> PAGE_CACHE_SHIFT);
 196                dst = (dst & ~PAGE_CACHE_MASK) + 1;
 197
 198                if (src == dst) {
 199                        while (src < len) {
 200                                memmove(hfsplus_kmap(*dst_page), hfsplus_kmap(*src_page), src);
 201                                hfsplus_kunmap(*src_page--);
 202                                set_page_dirty(*dst_page);
 203                                hfsplus_kunmap(*dst_page--);
 204                                len -= src;
 205                                src = PAGE_CACHE_SIZE;
 206                        }
 207                        src -= len;
 208                        memmove(hfsplus_kmap(*dst_page) + src, hfsplus_kmap(*src_page) + src, len);
 209                        hfsplus_kunmap(*src_page);
 210                        set_page_dirty(*dst_page);
 211                        hfsplus_kunmap(*dst_page);
 212                } else {
 213                        void *src_ptr, *dst_ptr;
 214
 215                        do {
 216                                src_ptr = hfsplus_kmap(*src_page) + src;
 217                                dst_ptr = hfsplus_kmap(*dst_page) + dst;
 218                                if (src < dst) {
 219                                        l = src;
 220                                        src = PAGE_CACHE_SIZE;
 221                                        dst -= l;
 222                                } else {
 223                                        l = dst;
 224                                        src -= l;
 225                                        dst = PAGE_CACHE_SIZE;
 226                                }
 227                                l = min(len, l);
 228                                memmove(dst_ptr - l, src_ptr - l, l);
 229                                hfsplus_kunmap(*src_page);
 230                                set_page_dirty(*dst_page);
 231                                hfsplus_kunmap(*dst_page);
 232                                if (dst == PAGE_CACHE_SIZE)
 233                                        dst_page--;
 234                                else
 235                                        src_page--;
 236                        } while ((len -= l));
 237                }
 238        } else {
 239                src_page = node->page + (src >> PAGE_CACHE_SHIFT);
 240                src &= ~PAGE_CACHE_MASK;
 241                dst_page = node->page + (dst >> PAGE_CACHE_SHIFT);
 242                dst &= ~PAGE_CACHE_MASK;
 243
 244                if (src == dst) {
 245                        l = min(len, PAGE_CACHE_SIZE - src);
 246                        memmove(hfsplus_kmap(*dst_page) + src, hfsplus_kmap(*src_page) + src, l);
 247                        hfsplus_kunmap(*src_page++);
 248                        set_page_dirty(*dst_page);
 249                        hfsplus_kunmap(*dst_page++);
 250
 251                        while ((len -= l)) {
 252                                l = min(len, PAGE_CACHE_SIZE);
 253                                memmove(hfsplus_kmap(*dst_page), hfsplus_kmap(*src_page), l);
 254                                hfsplus_kunmap(*src_page++);
 255                                set_page_dirty(*dst_page);
 256                                hfsplus_kunmap(*dst_page++);
 257                        }
 258                } else {
 259                        void *src_ptr, *dst_ptr;
 260
 261                        do {
 262                                src_ptr = hfsplus_kmap(*src_page) + src;
 263                                dst_ptr = hfsplus_kmap(*dst_page) + dst;
 264                                if (PAGE_CACHE_SIZE - src < PAGE_CACHE_SIZE - dst) {
 265                                        l = PAGE_CACHE_SIZE - src;
 266                                        src = 0;
 267                                        dst += l;
 268                                } else {
 269                                        l = PAGE_CACHE_SIZE - dst;
 270                                        src += l;
 271                                        dst = 0;
 272                                }
 273                                l = min(len, l);
 274                                memmove(dst_ptr, src_ptr, l);
 275                                hfsplus_kunmap(*src_page);
 276                                set_page_dirty(*dst_page);
 277                                hfsplus_kunmap(*dst_page);
 278                                if (!dst)
 279                                        dst_page++;
 280                                else
 281                                        src_page++;
 282                        } while ((len -= l));
 283                }
 284        }
 285}
 286
 287void hfsplus_bnode_dump(hfsplus_bnode *node)
 288{
 289        hfsplus_btree_node_desc desc;
 290        u32 cnid;
 291        int i, off, key_off;
 292
 293        dprint(DBG_BNODE_MOD, "bnode: %d\n", node->this);
 294        hfsplus_bnode_readbytes(node, &desc, 0, sizeof(desc));
 295        dprint(DBG_BNODE_MOD, "%d, %d, %d, %d, %d\n",
 296                be32_to_cpu(desc.next), be32_to_cpu(desc.prev),
 297                desc.kind, desc.height, be16_to_cpu(desc.num_rec));
 298
 299        off = node->tree->node_size - 2;
 300        for (i = be16_to_cpu(desc.num_rec); i >= 0; off -= 2, i--) {
 301                key_off = hfsplus_bnode_read_u16(node, off);
 302                dprint(DBG_BNODE_MOD, " %d", key_off);
 303                if (i && node->kind == HFSPLUS_NODE_NDX) {
 304                        int tmp;
 305
 306                        tmp = hfsplus_bnode_read_u16(node, key_off);
 307                        dprint(DBG_BNODE_MOD, " (%d", tmp);
 308                        hfsplus_bnode_readbytes(node, &cnid, key_off + 2 + tmp, 4);
 309                        dprint(DBG_BNODE_MOD, ",%d)", be32_to_cpu(cnid));
 310                }
 311        }
 312        dprint(DBG_BNODE_MOD, "\n");
 313}
 314
 315int hfsplus_btree_add_level(hfsplus_btree *tree)
 316{
 317        hfsplus_bnode *node, *new_node;
 318        hfsplus_btree_node_desc node_desc;
 319        int key_size, rec;
 320        u32 cnid;
 321
 322        node = NULL;
 323        if (tree->root)
 324                node = hfsplus_find_bnode(tree, tree->root);
 325        new_node = hfsplus_btree_alloc_node(tree);
 326        if (IS_ERR(new_node))
 327                return PTR_ERR(new_node);
 328
 329        tree->root = new_node->this;
 330        if (!tree->depth) {
 331                tree->leaf_head = tree->leaf_tail = new_node->this;
 332                new_node->kind = HFSPLUS_NODE_LEAF;
 333                new_node->num_recs = 0;
 334        } else {
 335                new_node->kind = HFSPLUS_NODE_NDX;
 336                new_node->num_recs = 1;
 337        }
 338        new_node->parent = 0;
 339        new_node->next = 0;
 340        new_node->prev = 0;
 341        new_node->height = ++tree->depth;
 342
 343        node_desc.next = cpu_to_be32(new_node->next);
 344        node_desc.prev = cpu_to_be32(new_node->prev);
 345        node_desc.kind = new_node->kind;
 346        node_desc.height = new_node->height;
 347        node_desc.num_rec = cpu_to_be16(new_node->num_recs);
 348        node_desc.reserved = 0;
 349        hfsplus_bnode_writebytes(new_node, &node_desc, 0, sizeof(node_desc));
 350
 351        rec = tree->node_size - 2;
 352        hfsplus_bnode_write_u16(new_node, rec, 14);
 353
 354        if (node) {
 355                /* insert old root idx into new root */
 356                node->parent = tree->root;
 357                key_size = hfsplus_bnode_read_u16(node, 14) + 2;
 358                // key_size if index node
 359                hfsplus_bnode_copybytes(new_node, 14, node, 14, key_size);
 360                cnid = cpu_to_be32(node->this);
 361                hfsplus_bnode_writebytes(new_node, &cnid, 14 + key_size, 4);
 362
 363                rec -= 2;
 364                hfsplus_bnode_write_u16(new_node, rec, 14 + key_size + 4);
 365
 366                hfsplus_put_bnode(node);
 367        }
 368        hfsplus_put_bnode(new_node);
 369        mark_inode_dirty(tree->inode);
 370
 371        return 0;
 372}
 373
 374hfsplus_bnode *hfsplus_bnode_split(struct hfsplus_find_data *fd)
 375{
 376        hfsplus_btree *tree;
 377        hfsplus_bnode *node, *new_node;
 378        hfsplus_btree_node_desc node_desc;
 379        int num_recs, new_rec_off, new_off, old_rec_off;
 380        int data_start, data_end, size;
 381
 382        tree = fd->tree;
 383        node = fd->bnode;
 384        new_node = hfsplus_btree_alloc_node(tree);
 385        if (IS_ERR(new_node))
 386                return new_node;
 387        hfsplus_get_bnode(node);
 388        dprint(DBG_BNODE_MOD, "split_nodes: %d - %d - %d\n",
 389                node->this, new_node->this, node->next);
 390        new_node->next = node->next;
 391        new_node->prev = node->this;
 392        new_node->parent = node->parent;
 393        new_node->kind = node->kind;
 394        new_node->height = node->height;
 395
 396        size = tree->node_size / 2;
 397        old_rec_off = tree->node_size - 4;
 398        num_recs = 1;
 399        for (;;) {
 400                data_start = hfsplus_bnode_read_u16(node, old_rec_off);
 401                if (data_start > size)
 402                        break;
 403                old_rec_off -= 2;
 404                num_recs++;
 405                if (num_recs < node->num_recs)
 406                        continue;
 407                /* panic? */
 408                hfsplus_put_bnode(node);
 409                hfsplus_put_bnode(new_node);
 410                return NULL;
 411        }
 412
 413        if (fd->record + 1 < num_recs) {
 414                /* new record is in the lower half,
 415                 * so leave some more space there
 416                 */
 417                old_rec_off += 2;
 418                num_recs--;
 419                data_start = hfsplus_bnode_read_u16(node, old_rec_off);
 420        } else {
 421                hfsplus_put_bnode(node);
 422                hfsplus_get_bnode(new_node);
 423                fd->bnode = new_node;
 424                fd->record -= num_recs;
 425                fd->keyoffset -= data_start;
 426                fd->entryoffset -= data_start;
 427        }
 428        new_node->num_recs = node->num_recs - num_recs;
 429        node->num_recs = num_recs;
 430
 431        new_rec_off = tree->node_size - 2;
 432        new_off = 14;
 433        size = data_start - new_off;
 434        num_recs = new_node->num_recs;
 435        data_end = data_start;
 436        while (num_recs) {
 437                hfsplus_bnode_write_u16(new_node, new_rec_off, new_off);
 438                old_rec_off -= 2;
 439                new_rec_off -= 2;
 440                data_end = hfsplus_bnode_read_u16(node, old_rec_off);
 441                new_off = data_end - size;
 442                num_recs--;
 443        }
 444        hfsplus_bnode_write_u16(new_node, new_rec_off, new_off);
 445        hfsplus_bnode_copybytes(new_node, 14, node, data_start, data_end - data_start);
 446
 447        /* update new bnode header */
 448        node_desc.next = cpu_to_be32(new_node->next);
 449        node_desc.prev = cpu_to_be32(new_node->prev);
 450        node_desc.kind = new_node->kind;
 451        node_desc.height = new_node->height;
 452        node_desc.num_rec = cpu_to_be16(new_node->num_recs);
 453        node_desc.reserved = 0;
 454        hfsplus_bnode_writebytes(new_node, &node_desc, 0, sizeof(node_desc));
 455
 456        /* update previous bnode header */
 457        node->next = new_node->this;
 458        hfsplus_bnode_readbytes(node, &node_desc, 0, sizeof(node_desc));
 459        node_desc.next = cpu_to_be32(node->next);
 460        node_desc.num_rec = cpu_to_be16(node->num_recs);
 461        hfsplus_bnode_writebytes(node, &node_desc, 0, sizeof(node_desc));
 462
 463        /* update next bnode header */
 464        if (new_node->next) {
 465                hfsplus_bnode *next_node = hfsplus_find_bnode(tree, new_node->next);
 466                next_node->prev = new_node->this;
 467                hfsplus_bnode_readbytes(next_node, &node_desc, 0, sizeof(node_desc));
 468                node_desc.prev = cpu_to_be32(next_node->prev);
 469                hfsplus_bnode_writebytes(next_node, &node_desc, 0, sizeof(node_desc));
 470                hfsplus_put_bnode(next_node);
 471        } else if (node->this == tree->leaf_tail) {
 472                /* if there is no next node, this might be the new tail */
 473                tree->leaf_tail = new_node->this;
 474                mark_inode_dirty(tree->inode);
 475        }
 476
 477        hfsplus_bnode_dump(node);
 478        hfsplus_bnode_dump(new_node);
 479        hfsplus_put_bnode(node);
 480
 481        return new_node;
 482}
 483
 484int hfsplus_bnode_insert_rec(struct hfsplus_find_data *fd, void *entry, int entry_len)
 485{
 486        hfsplus_btree *tree;
 487        hfsplus_bnode *node, *new_node;
 488        int size, key_len, rec;
 489        int data_off, end_off;
 490        int idx_rec_off, data_rec_off, end_rec_off;
 491        u32 cnid;
 492
 493        tree = fd->tree;
 494        if (!fd->bnode) {
 495                if (!tree->root)
 496                        hfsplus_btree_add_level(tree);
 497                fd->bnode = hfsplus_find_bnode(tree, tree->leaf_head);
 498                fd->record = -1;
 499        }
 500        new_node = NULL;
 501again:
 502        /* new record idx and complete record size */
 503        rec = fd->record + 1;
 504        key_len = be16_to_cpu(fd->search_key->key_len) + 2;
 505        size = key_len + entry_len;
 506
 507        node = fd->bnode;
 508        hfsplus_bnode_dump(node);
 509        /* get last offset */
 510        end_rec_off = tree->node_size - (node->num_recs + 1) * 2;
 511        end_off = hfsplus_bnode_read_u16(node, end_rec_off);
 512        end_rec_off -= 2;
 513        dprint(DBG_BNODE_MOD, "insert_rec: %d, %d, %d, %d\n", rec, size, end_off, end_rec_off);
 514        if (size > end_rec_off - end_off) {
 515                if (new_node)
 516                        panic("not enough room!\n");
 517                new_node = hfsplus_bnode_split(fd);
 518                if (IS_ERR(new_node))
 519                        return PTR_ERR(new_node);
 520                goto again;
 521        }
 522        if (node->kind == HFSPLUS_NODE_LEAF) {
 523                tree->leaf_count++;
 524                mark_inode_dirty(tree->inode);
 525        }
 526        node->num_recs++;
 527        /* write new last offset */
 528        hfsplus_bnode_write_u16(node, offsetof(hfsplus_btree_node_desc, num_rec), node->num_recs);
 529        hfsplus_bnode_write_u16(node, end_rec_off, end_off + size);
 530        data_off = end_off;
 531        data_rec_off = end_rec_off + 2;
 532        idx_rec_off = tree->node_size - (rec + 1) * 2;
 533        if (idx_rec_off == data_rec_off)
 534                goto skip;
 535        /* move all following entries */
 536        do {
 537                data_off = hfsplus_bnode_read_u16(node, data_rec_off + 2);
 538                hfsplus_bnode_write_u16(node, data_rec_off, data_off + size);
 539                data_rec_off += 2;
 540        } while (data_rec_off < idx_rec_off);
 541
 542        /* move data away */
 543        hfsplus_bnode_movebytes(node, data_off + size, data_off,
 544                                end_off - data_off);
 545
 546skip:
 547        hfsplus_bnode_writebytes(node, fd->search_key, data_off, key_len);
 548        hfsplus_bnode_writebytes(node, entry, data_off + key_len, entry_len);
 549        hfsplus_bnode_dump(node);
 550
 551        if (new_node) {
 552                if (!rec && new_node != node)
 553                        hfsplus_update_idx_rec(fd);
 554
 555                hfsplus_put_bnode(fd->bnode);
 556                if (!new_node->parent) {
 557                        hfsplus_btree_add_level(tree);
 558                        new_node->parent = tree->root;
 559                }
 560                fd->bnode = hfsplus_find_bnode(tree, new_node->parent);
 561
 562                /* create index data entry */
 563                cnid = cpu_to_be32(new_node->this);
 564                entry = &cnid;
 565                entry_len = sizeof(cnid);
 566
 567                /* get index key */
 568                hfsplus_bnode_read_key(new_node, fd->search_key, 14);
 569                hfsplus_find_rec(fd->bnode, fd);
 570
 571                hfsplus_put_bnode(new_node);
 572                new_node = NULL;
 573                goto again;
 574        }
 575
 576        if (!rec)
 577                hfsplus_update_idx_rec(fd);
 578
 579        return 0;
 580}
 581
 582int hfsplus_update_idx_rec(struct hfsplus_find_data *fd)
 583{
 584        hfsplus_btree *tree;
 585        hfsplus_bnode *node, *new_node, *parent;
 586        int newkeylen, diff;
 587        int rec, rec_off, end_rec_off;
 588        int start_off, end_off;
 589
 590        tree = fd->tree;
 591        node = fd->bnode;
 592        new_node = NULL;
 593        if (!node->parent)
 594                return 0;
 595
 596again:
 597        parent = hfsplus_find_bnode(tree, node->parent);
 598        hfsplus_find_rec(parent, fd);
 599        hfsplus_bnode_dump(parent);
 600        rec = fd->record;
 601
 602        /* size difference between old and new key */
 603        newkeylen = hfsplus_bnode_read_u16(node, 14) + 2;
 604        dprint(DBG_BNODE_MOD, "update_rec: %d, %d, %d\n", rec, fd->keylength, newkeylen);
 605
 606        rec_off = tree->node_size - (rec + 2) * 2;
 607        end_rec_off = tree->node_size - (parent->num_recs + 1) * 2;
 608        diff = newkeylen - fd->keylength;
 609        if (!diff)
 610                goto skip;
 611        if (diff > 0) {
 612                end_off = hfsplus_bnode_read_u16(parent, end_rec_off);
 613                if (end_rec_off - end_off < diff) {
 614
 615                        printk("splitting index node...\n");
 616                        fd->bnode = parent;
 617                        new_node = hfsplus_bnode_split(fd);
 618                        if (IS_ERR(new_node))
 619                                return PTR_ERR(new_node);
 620                        parent = fd->bnode;
 621                        rec = fd->record;
 622                        rec_off = tree->node_size - (rec + 2) * 2;
 623                        end_rec_off = tree->node_size - (parent->num_recs + 1) * 2;
 624                }
 625        }
 626
 627        end_off = start_off = hfsplus_bnode_read_u16(parent, rec_off);
 628        hfsplus_bnode_write_u16(parent, rec_off, start_off + diff);
 629        start_off -= 4; /* move previous cnid too */
 630
 631        while (rec_off > end_rec_off) {
 632                rec_off -= 2;
 633                end_off = hfsplus_bnode_read_u16(parent, rec_off);
 634                hfsplus_bnode_write_u16(parent, rec_off, end_off + diff);
 635        }
 636        hfsplus_bnode_movebytes(parent, start_off + diff, start_off,
 637                                end_off - start_off);
 638skip:
 639        hfsplus_bnode_copybytes(parent, fd->keyoffset, node, 14, newkeylen);
 640        hfsplus_bnode_dump(parent);
 641
 642        hfsplus_put_bnode(node);
 643        node = parent;
 644
 645        if (new_node) {
 646                u32 cnid;
 647
 648                fd->bnode = hfsplus_find_bnode(tree, new_node->parent);
 649                /* create index key and entry */
 650                hfsplus_bnode_read_key(new_node, fd->search_key, 14);
 651                cnid = cpu_to_be32(new_node->this);
 652
 653                hfsplus_find_rec(fd->bnode, fd);
 654                hfsplus_bnode_insert_rec(fd, &cnid, sizeof(cnid));
 655                hfsplus_put_bnode(fd->bnode);
 656                hfsplus_put_bnode(new_node);
 657
 658                if (!rec) {
 659                        if (new_node == node)
 660                                goto out;
 661                        /* restore search_key */
 662                        hfsplus_bnode_read_key(node, fd->search_key, 14);
 663                }
 664        }
 665
 666        if (!rec && node->parent)
 667                goto again;
 668out:
 669        fd->bnode = node;
 670        return 0;
 671}
 672
 673int hfsplus_bnode_remove_rec(struct hfsplus_find_data *fd)
 674{
 675        hfsplus_btree *tree;
 676        hfsplus_bnode *node, *parent;
 677        int end_off, rec_off, data_off, size;
 678
 679        tree = fd->tree;
 680        node = fd->bnode;
 681again:
 682        rec_off = tree->node_size - (fd->record + 2) * 2;
 683        end_off = tree->node_size - (node->num_recs + 1) * 2;
 684
 685        if (node->kind == HFSPLUS_NODE_LEAF) {
 686                tree->leaf_count--;
 687                mark_inode_dirty(tree->inode);
 688        }
 689        hfsplus_bnode_dump(node);
 690        dprint(DBG_BNODE_MOD, "remove_rec: %d, %d\n", fd->record, fd->keylength + fd->entrylength);
 691        if (!--node->num_recs) {
 692                hfsplus_btree_remove_node(node);
 693                if (!node->parent)
 694                        return 0;
 695                parent = hfsplus_find_bnode(tree, node->parent);
 696                if (!parent)
 697                        return -EIO;
 698                hfsplus_put_bnode(node);
 699                node = fd->bnode = parent;
 700
 701                hfsplus_find_rec(node, fd);
 702                goto again;
 703        }
 704        hfsplus_bnode_write_u16(node, offsetof(hfsplus_btree_node_desc, num_rec), node->num_recs);
 705
 706        if (rec_off == end_off)
 707                goto skip;
 708        size = fd->keylength + fd->entrylength;
 709
 710        do {
 711                data_off = hfsplus_bnode_read_u16(node, rec_off);
 712                hfsplus_bnode_write_u16(node, rec_off + 2, data_off - size);
 713                rec_off -= 2;
 714        } while (rec_off >= end_off);
 715
 716        /* fill hole */
 717        hfsplus_bnode_movebytes(node, fd->keyoffset, fd->keyoffset + size,
 718                                data_off - fd->keyoffset - size);
 719skip:
 720        hfsplus_bnode_dump(node);
 721        if (!fd->record)
 722                hfsplus_update_idx_rec(fd);
 723        return 0;
 724}
 725
 726/* Check for valid kind/height pairs , return 0 for bad pairings */
 727static int hfsplus_check_kh(hfsplus_btree *tree, u8 kind, u8 height)
 728{
 729        if ((kind == HFSPLUS_NODE_HEAD) || (kind == HFSPLUS_NODE_MAP)) {
 730                if (height != 0)
 731                        goto hk_error;
 732        } else if (kind == HFSPLUS_NODE_LEAF) {
 733                if (height != 1)
 734                        goto hk_error;
 735        } else if (kind == HFSPLUS_NODE_NDX) {
 736                if ((height <= 1) || (height > tree->depth))
 737                        goto hk_error;
 738        } else {
 739                printk("HFS+-fs: unknown node type in B*Tree\n");
 740                return 0;
 741        }
 742        return 1;
 743 hk_error:
 744        printk("HFS+-fs: corrupt node height in B*Tree\n");
 745        return 0;
 746}
 747
 748static inline int hfsplus_bnode_hash(u32 num)
 749{
 750        num = (num >> 16) + num;
 751        num += num >> 8;
 752        return num & (NODE_HASH_SIZE - 1);
 753}
 754
 755hfsplus_bnode *__hfsplus_find_bnode(hfsplus_btree *tree, u32 cnid)
 756{
 757        hfsplus_bnode *node;
 758
 759        if (cnid >= tree->node_count) {
 760                printk("HFS+-fs: request for non-existent node %d in B*Tree\n", cnid);
 761                return NULL;
 762        }
 763
 764        for (node = tree->node_hash[hfsplus_bnode_hash(cnid)];
 765             node; node = node->next_hash) {
 766                if (node->this == cnid) {
 767                        return node;
 768                }
 769        }
 770        return NULL;
 771}
 772
 773hfsplus_bnode *__hfsplus_create_bnode(hfsplus_btree *tree, u32 cnid)
 774{
 775        struct super_block *sb;
 776        hfsplus_bnode *node, *node2;
 777        struct address_space *mapping;
 778        struct page *page;
 779        int size, block, i, hash;
 780        loff_t off;
 781
 782        if (cnid >= tree->node_count) {
 783                printk("HFS+-fs: request for non-existent node %d in B*Tree\n", cnid);
 784                return NULL;
 785        }
 786
 787        sb = tree->inode->i_sb;
 788        size = sizeof(hfsplus_bnode) + tree->pages_per_bnode *
 789                sizeof(struct page *);
 790        node = kmalloc(size, GFP_KERNEL);
 791        if (!node)
 792                return NULL;
 793        memset(node, 0, size);
 794        node->tree = tree;
 795        node->this = cnid;
 796        set_bit(HFSPLUS_BNODE_NEW, &node->flags);
 797        atomic_set(&node->refcnt, 1);
 798        dprint(DBG_BNODE_REFS, "new_node(%d:%d): 1\n",
 799               node->tree->cnid, node->this);
 800        init_waitqueue_head(&node->lock_wq);
 801        spin_lock(&tree->hash_lock);
 802        node2 = __hfsplus_find_bnode(tree, cnid);
 803        if (!node2) {
 804                hash = hfsplus_bnode_hash(cnid);
 805                node->next_hash = tree->node_hash[hash];
 806                tree->node_hash[hash] = node;
 807                tree->node_hash_cnt++;
 808        } else {
 809                spin_unlock(&tree->hash_lock);
 810                kfree(node);
 811                wait_event(node2->lock_wq, !test_bit(HFSPLUS_BNODE_NEW, &node2->flags));
 812                return node2;
 813        }
 814        spin_unlock(&tree->hash_lock);
 815
 816        mapping = tree->inode->i_mapping;
 817        off = (loff_t)cnid * tree->node_size;
 818        block = off >> PAGE_CACHE_SHIFT;
 819        node->page_offset = off & ~PAGE_CACHE_MASK;
 820        for (i = 0; i < tree->pages_per_bnode; i++) {
 821                page = grab_cache_page(mapping, block++);
 822                if (!page)
 823                        goto fail;
 824                if (!PageUptodate(page)) {
 825                        if (mapping->a_ops->readpage(NULL, page))
 826                                goto fail;
 827                        wait_on_page_locked(page);
 828                        if (!PageUptodate(page))
 829                                goto fail;
 830                } else
 831                        unlock_page(page);
 832#if !REF_PAGES
 833                page_cache_release(page);
 834#endif
 835                node->page[i] = page;
 836        }
 837
 838        return node;
 839fail:
 840        if (page)
 841                page_cache_release(page);
 842        set_bit(HFSPLUS_BNODE_ERROR, &node->flags);
 843        return node;
 844}
 845
 846void __hfsplus_bnode_remove(hfsplus_bnode *node)
 847{
 848        hfsplus_bnode **p;
 849
 850        dprint(DBG_BNODE_REFS, "remove_node(%d:%d): %d\n",
 851                node->tree->cnid, node->this, atomic_read(&node->refcnt));
 852        for (p = &node->tree->node_hash[hfsplus_bnode_hash(node->this)];
 853             *p && *p != node; p = &(*p)->next_hash)
 854                ;
 855        if (!*p)
 856                BUG();
 857        *p = node->next_hash;
 858        node->tree->node_hash_cnt--;
 859}
 860
 861/* Load a particular node out of a tree */
 862hfsplus_bnode *hfsplus_find_bnode(hfsplus_btree *tree, u32 num)
 863{
 864        hfsplus_bnode *node;
 865        hfsplus_btree_node_desc *desc;
 866        int i, rec_off, off, next_off;
 867        int entry_size, key_size;
 868
 869        spin_lock(&tree->hash_lock);
 870        node = __hfsplus_find_bnode(tree, num);
 871        if (node) {
 872                hfsplus_get_bnode(node);
 873                spin_unlock(&tree->hash_lock);
 874                wait_event(node->lock_wq, !test_bit(HFSPLUS_BNODE_NEW, &node->flags));
 875                return node;
 876        }
 877        spin_unlock(&tree->hash_lock);
 878        node = __hfsplus_create_bnode(tree, num);
 879        if (!node)
 880                return ERR_PTR(-ENOMEM);
 881        if (!test_bit(HFSPLUS_BNODE_NEW, &node->flags))
 882                return node;
 883
 884        desc = (hfsplus_btree_node_desc *)(hfsplus_kmap(node->page[0]) + node->page_offset);
 885        node->prev = be32_to_cpu(desc->prev);
 886        node->next = be32_to_cpu(desc->next);
 887        node->num_recs = be16_to_cpu(desc->num_rec);
 888        node->kind = desc->kind;
 889        node->height = desc->height;
 890
 891        if (!hfsplus_check_kh(tree, desc->kind, desc->height)) {
 892                hfsplus_kunmap(node->page[0]);
 893                goto node_error;
 894        }
 895        hfsplus_kunmap(node->page[0]);
 896
 897        rec_off = tree->node_size - 2;
 898        off = hfsplus_bnode_read_u16(node, rec_off);
 899        if (off != sizeof(hfsplus_btree_node_desc))
 900                goto node_error;
 901        for (i = 1; i <= node->num_recs; off = next_off, i++) {
 902                rec_off -= 2;
 903                next_off = hfsplus_bnode_read_u16(node, rec_off);
 904                if (next_off <= off ||
 905                    next_off > tree->node_size ||
 906                    next_off & 1)
 907                        goto node_error;
 908                entry_size = next_off - off;
 909                if (node->kind != HFSPLUS_NODE_NDX &&
 910                    node->kind != HFSPLUS_NODE_LEAF)
 911                        continue;
 912                key_size = hfsplus_bnode_read_u16(node, off) + 2;
 913                if (key_size >= entry_size || key_size & 1)
 914                        goto node_error;
 915        }
 916        clear_bit(HFSPLUS_BNODE_NEW, &node->flags);
 917        wake_up(&node->lock_wq);
 918        return node;
 919
 920node_error:
 921        set_bit(HFSPLUS_BNODE_ERROR, &node->flags);
 922        clear_bit(HFSPLUS_BNODE_NEW, &node->flags);
 923        wake_up(&node->lock_wq);
 924        hfsplus_put_bnode(node);
 925        return ERR_PTR(-EIO);
 926}
 927
 928void hfsplus_bnode_free(hfsplus_bnode *node)
 929{
 930        //int i;
 931
 932        //for (i = 0; i < node->tree->pages_per_bnode; i++)
 933        //      if (node->page[i])
 934        //              page_cache_release(node->page[i]);
 935        kfree(node);
 936}
 937
 938hfsplus_bnode *hfsplus_create_bnode(hfsplus_btree *tree, u32 num)
 939{
 940        hfsplus_bnode *node;
 941        struct page **pagep;
 942        int i;
 943
 944        spin_lock(&tree->hash_lock);
 945        node = __hfsplus_find_bnode(tree, num);
 946        spin_unlock(&tree->hash_lock);
 947        if (node)
 948                BUG();
 949        node = __hfsplus_create_bnode(tree, num);
 950        if (!node)
 951                return ERR_PTR(-ENOMEM);
 952
 953        pagep = node->page;
 954        memset(hfsplus_kmap(*pagep) + node->page_offset, 0,
 955               min((int)PAGE_CACHE_SIZE, (int)tree->node_size));
 956        set_page_dirty(*pagep);
 957        hfsplus_kunmap(*pagep++);
 958        for (i = 1; i < tree->pages_per_bnode; i++) {
 959                memset(hfsplus_kmap(*pagep), 0, PAGE_CACHE_SIZE);
 960                set_page_dirty(*pagep);
 961                hfsplus_kunmap(*pagep++);
 962        }
 963        clear_bit(HFSPLUS_BNODE_NEW, &node->flags);
 964        wake_up(&node->lock_wq);
 965
 966        return node;
 967}
 968
 969void hfsplus_get_bnode(hfsplus_bnode *node)
 970{
 971        if (node) {
 972                atomic_inc(&node->refcnt);
 973#if REF_PAGES
 974                {
 975                int i;
 976                for (i = 0; i < node->tree->pages_per_bnode; i++)
 977                        get_page(node->page[i]);
 978                }
 979#endif
 980                dprint(DBG_BNODE_REFS, "get_node(%d:%d): %d\n",
 981                       node->tree->cnid, node->this, atomic_read(&node->refcnt));
 982        }
 983}
 984
 985/* Dispose of resources used by a node */
 986void hfsplus_put_bnode(hfsplus_bnode *node)
 987{
 988        if (node) {
 989                struct hfsplus_btree *tree = node->tree;
 990                int i;
 991
 992                dprint(DBG_BNODE_REFS, "put_node(%d:%d): %d\n",
 993                       node->tree->cnid, node->this, atomic_read(&node->refcnt));
 994                if (!atomic_read(&node->refcnt))
 995                        BUG();
 996                if (!atomic_dec_and_lock(&node->refcnt, &tree->hash_lock)) {
 997#if REF_PAGES
 998                        for (i = 0; i < tree->pages_per_bnode; i++)
 999                                put_page(node->page[i]);
1000#endif
1001                        return;
1002                }
1003                for (i = 0; i < tree->pages_per_bnode; i++) {
1004                        mark_page_accessed(node->page[i]);
1005#if REF_PAGES
1006                        put_page(node->page[i]);
1007#endif
1008                }
1009
1010                if (test_bit(HFSPLUS_BNODE_DELETED, &node->flags)) {
1011                        __hfsplus_bnode_remove(node);
1012                        spin_unlock(&tree->hash_lock);
1013                        hfsplus_btree_free_node(node);
1014                        hfsplus_bnode_free(node);
1015                        return;
1016                }
1017                spin_unlock(&tree->hash_lock);
1018        }
1019}
1020
1021void hfsplus_lock_bnode(hfsplus_bnode *node)
1022{
1023        wait_event(node->lock_wq, !test_and_set_bit(HFSPLUS_BNODE_LOCK, &node->flags));
1024}
1025
1026void hfsplus_unlock_bnode(hfsplus_bnode *node)
1027{
1028        clear_bit(HFSPLUS_BNODE_LOCK, &node->flags);
1029        if (waitqueue_active(&node->lock_wq))
1030                wake_up(&node->lock_wq);
1031}
1032
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.