162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * linux/fs/hfsplus/brec.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2001 662306a36Sopenharmony_ci * Brad Boyer (flar@allandria.com) 762306a36Sopenharmony_ci * (C) 2003 Ardis Technologies <roman@ardistech.com> 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Handle individual btree records 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include "hfsplus_fs.h" 1362306a36Sopenharmony_ci#include "hfsplus_raw.h" 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_cistatic struct hfs_bnode *hfs_bnode_split(struct hfs_find_data *fd); 1662306a36Sopenharmony_cistatic int hfs_brec_update_parent(struct hfs_find_data *fd); 1762306a36Sopenharmony_cistatic int hfs_btree_inc_height(struct hfs_btree *); 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci/* Get the length and offset of the given record in the given node */ 2062306a36Sopenharmony_ciu16 hfs_brec_lenoff(struct hfs_bnode *node, u16 rec, u16 *off) 2162306a36Sopenharmony_ci{ 2262306a36Sopenharmony_ci __be16 retval[2]; 2362306a36Sopenharmony_ci u16 dataoff; 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci dataoff = node->tree->node_size - (rec + 2) * 2; 2662306a36Sopenharmony_ci hfs_bnode_read(node, retval, dataoff, 4); 2762306a36Sopenharmony_ci *off = be16_to_cpu(retval[1]); 2862306a36Sopenharmony_ci return be16_to_cpu(retval[0]) - *off; 2962306a36Sopenharmony_ci} 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci/* Get the length of the key from a keyed record */ 3262306a36Sopenharmony_ciu16 hfs_brec_keylen(struct hfs_bnode *node, u16 rec) 3362306a36Sopenharmony_ci{ 3462306a36Sopenharmony_ci u16 retval, recoff; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci if (node->type != HFS_NODE_INDEX && node->type != HFS_NODE_LEAF) 3762306a36Sopenharmony_ci return 0; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci if ((node->type == HFS_NODE_INDEX) && 4062306a36Sopenharmony_ci !(node->tree->attributes & HFS_TREE_VARIDXKEYS) && 4162306a36Sopenharmony_ci (node->tree->cnid != HFSPLUS_ATTR_CNID)) { 4262306a36Sopenharmony_ci retval = node->tree->max_key_len + 2; 4362306a36Sopenharmony_ci } else { 4462306a36Sopenharmony_ci recoff = hfs_bnode_read_u16(node, 4562306a36Sopenharmony_ci node->tree->node_size - (rec + 1) * 2); 4662306a36Sopenharmony_ci if (!recoff) 4762306a36Sopenharmony_ci return 0; 4862306a36Sopenharmony_ci if (recoff > node->tree->node_size - 2) { 4962306a36Sopenharmony_ci pr_err("recoff %d too large\n", recoff); 5062306a36Sopenharmony_ci return 0; 5162306a36Sopenharmony_ci } 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci retval = hfs_bnode_read_u16(node, recoff) + 2; 5462306a36Sopenharmony_ci if (retval > node->tree->max_key_len + 2) { 5562306a36Sopenharmony_ci pr_err("keylen %d too large\n", 5662306a36Sopenharmony_ci retval); 5762306a36Sopenharmony_ci retval = 0; 5862306a36Sopenharmony_ci } 5962306a36Sopenharmony_ci } 6062306a36Sopenharmony_ci return retval; 6162306a36Sopenharmony_ci} 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ciint hfs_brec_insert(struct hfs_find_data *fd, void *entry, int entry_len) 6462306a36Sopenharmony_ci{ 6562306a36Sopenharmony_ci struct hfs_btree *tree; 6662306a36Sopenharmony_ci struct hfs_bnode *node, *new_node; 6762306a36Sopenharmony_ci int size, key_len, rec; 6862306a36Sopenharmony_ci int data_off, end_off; 6962306a36Sopenharmony_ci int idx_rec_off, data_rec_off, end_rec_off; 7062306a36Sopenharmony_ci __be32 cnid; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci tree = fd->tree; 7362306a36Sopenharmony_ci if (!fd->bnode) { 7462306a36Sopenharmony_ci if (!tree->root) 7562306a36Sopenharmony_ci hfs_btree_inc_height(tree); 7662306a36Sopenharmony_ci node = hfs_bnode_find(tree, tree->leaf_head); 7762306a36Sopenharmony_ci if (IS_ERR(node)) 7862306a36Sopenharmony_ci return PTR_ERR(node); 7962306a36Sopenharmony_ci fd->bnode = node; 8062306a36Sopenharmony_ci fd->record = -1; 8162306a36Sopenharmony_ci } 8262306a36Sopenharmony_ci new_node = NULL; 8362306a36Sopenharmony_ci key_len = be16_to_cpu(fd->search_key->key_len) + 2; 8462306a36Sopenharmony_ciagain: 8562306a36Sopenharmony_ci /* new record idx and complete record size */ 8662306a36Sopenharmony_ci rec = fd->record + 1; 8762306a36Sopenharmony_ci size = key_len + entry_len; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci node = fd->bnode; 9062306a36Sopenharmony_ci hfs_bnode_dump(node); 9162306a36Sopenharmony_ci /* get last offset */ 9262306a36Sopenharmony_ci end_rec_off = tree->node_size - (node->num_recs + 1) * 2; 9362306a36Sopenharmony_ci end_off = hfs_bnode_read_u16(node, end_rec_off); 9462306a36Sopenharmony_ci end_rec_off -= 2; 9562306a36Sopenharmony_ci hfs_dbg(BNODE_MOD, "insert_rec: %d, %d, %d, %d\n", 9662306a36Sopenharmony_ci rec, size, end_off, end_rec_off); 9762306a36Sopenharmony_ci if (size > end_rec_off - end_off) { 9862306a36Sopenharmony_ci if (new_node) 9962306a36Sopenharmony_ci panic("not enough room!\n"); 10062306a36Sopenharmony_ci new_node = hfs_bnode_split(fd); 10162306a36Sopenharmony_ci if (IS_ERR(new_node)) 10262306a36Sopenharmony_ci return PTR_ERR(new_node); 10362306a36Sopenharmony_ci goto again; 10462306a36Sopenharmony_ci } 10562306a36Sopenharmony_ci if (node->type == HFS_NODE_LEAF) { 10662306a36Sopenharmony_ci tree->leaf_count++; 10762306a36Sopenharmony_ci mark_inode_dirty(tree->inode); 10862306a36Sopenharmony_ci } 10962306a36Sopenharmony_ci node->num_recs++; 11062306a36Sopenharmony_ci /* write new last offset */ 11162306a36Sopenharmony_ci hfs_bnode_write_u16(node, 11262306a36Sopenharmony_ci offsetof(struct hfs_bnode_desc, num_recs), 11362306a36Sopenharmony_ci node->num_recs); 11462306a36Sopenharmony_ci hfs_bnode_write_u16(node, end_rec_off, end_off + size); 11562306a36Sopenharmony_ci data_off = end_off; 11662306a36Sopenharmony_ci data_rec_off = end_rec_off + 2; 11762306a36Sopenharmony_ci idx_rec_off = tree->node_size - (rec + 1) * 2; 11862306a36Sopenharmony_ci if (idx_rec_off == data_rec_off) 11962306a36Sopenharmony_ci goto skip; 12062306a36Sopenharmony_ci /* move all following entries */ 12162306a36Sopenharmony_ci do { 12262306a36Sopenharmony_ci data_off = hfs_bnode_read_u16(node, data_rec_off + 2); 12362306a36Sopenharmony_ci hfs_bnode_write_u16(node, data_rec_off, data_off + size); 12462306a36Sopenharmony_ci data_rec_off += 2; 12562306a36Sopenharmony_ci } while (data_rec_off < idx_rec_off); 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci /* move data away */ 12862306a36Sopenharmony_ci hfs_bnode_move(node, data_off + size, data_off, 12962306a36Sopenharmony_ci end_off - data_off); 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ciskip: 13262306a36Sopenharmony_ci hfs_bnode_write(node, fd->search_key, data_off, key_len); 13362306a36Sopenharmony_ci hfs_bnode_write(node, entry, data_off + key_len, entry_len); 13462306a36Sopenharmony_ci hfs_bnode_dump(node); 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci /* 13762306a36Sopenharmony_ci * update parent key if we inserted a key 13862306a36Sopenharmony_ci * at the start of the node and it is not the new node 13962306a36Sopenharmony_ci */ 14062306a36Sopenharmony_ci if (!rec && new_node != node) { 14162306a36Sopenharmony_ci hfs_bnode_read_key(node, fd->search_key, data_off + size); 14262306a36Sopenharmony_ci hfs_brec_update_parent(fd); 14362306a36Sopenharmony_ci } 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci if (new_node) { 14662306a36Sopenharmony_ci hfs_bnode_put(fd->bnode); 14762306a36Sopenharmony_ci if (!new_node->parent) { 14862306a36Sopenharmony_ci hfs_btree_inc_height(tree); 14962306a36Sopenharmony_ci new_node->parent = tree->root; 15062306a36Sopenharmony_ci } 15162306a36Sopenharmony_ci fd->bnode = hfs_bnode_find(tree, new_node->parent); 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci /* create index data entry */ 15462306a36Sopenharmony_ci cnid = cpu_to_be32(new_node->this); 15562306a36Sopenharmony_ci entry = &cnid; 15662306a36Sopenharmony_ci entry_len = sizeof(cnid); 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci /* get index key */ 15962306a36Sopenharmony_ci hfs_bnode_read_key(new_node, fd->search_key, 14); 16062306a36Sopenharmony_ci __hfs_brec_find(fd->bnode, fd, hfs_find_rec_by_key); 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci hfs_bnode_put(new_node); 16362306a36Sopenharmony_ci new_node = NULL; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci if ((tree->attributes & HFS_TREE_VARIDXKEYS) || 16662306a36Sopenharmony_ci (tree->cnid == HFSPLUS_ATTR_CNID)) 16762306a36Sopenharmony_ci key_len = be16_to_cpu(fd->search_key->key_len) + 2; 16862306a36Sopenharmony_ci else { 16962306a36Sopenharmony_ci fd->search_key->key_len = 17062306a36Sopenharmony_ci cpu_to_be16(tree->max_key_len); 17162306a36Sopenharmony_ci key_len = tree->max_key_len + 2; 17262306a36Sopenharmony_ci } 17362306a36Sopenharmony_ci goto again; 17462306a36Sopenharmony_ci } 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci return 0; 17762306a36Sopenharmony_ci} 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ciint hfs_brec_remove(struct hfs_find_data *fd) 18062306a36Sopenharmony_ci{ 18162306a36Sopenharmony_ci struct hfs_btree *tree; 18262306a36Sopenharmony_ci struct hfs_bnode *node, *parent; 18362306a36Sopenharmony_ci int end_off, rec_off, data_off, size; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci tree = fd->tree; 18662306a36Sopenharmony_ci node = fd->bnode; 18762306a36Sopenharmony_ciagain: 18862306a36Sopenharmony_ci rec_off = tree->node_size - (fd->record + 2) * 2; 18962306a36Sopenharmony_ci end_off = tree->node_size - (node->num_recs + 1) * 2; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci if (node->type == HFS_NODE_LEAF) { 19262306a36Sopenharmony_ci tree->leaf_count--; 19362306a36Sopenharmony_ci mark_inode_dirty(tree->inode); 19462306a36Sopenharmony_ci } 19562306a36Sopenharmony_ci hfs_bnode_dump(node); 19662306a36Sopenharmony_ci hfs_dbg(BNODE_MOD, "remove_rec: %d, %d\n", 19762306a36Sopenharmony_ci fd->record, fd->keylength + fd->entrylength); 19862306a36Sopenharmony_ci if (!--node->num_recs) { 19962306a36Sopenharmony_ci hfs_bnode_unlink(node); 20062306a36Sopenharmony_ci if (!node->parent) 20162306a36Sopenharmony_ci return 0; 20262306a36Sopenharmony_ci parent = hfs_bnode_find(tree, node->parent); 20362306a36Sopenharmony_ci if (IS_ERR(parent)) 20462306a36Sopenharmony_ci return PTR_ERR(parent); 20562306a36Sopenharmony_ci hfs_bnode_put(node); 20662306a36Sopenharmony_ci node = fd->bnode = parent; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci __hfs_brec_find(node, fd, hfs_find_rec_by_key); 20962306a36Sopenharmony_ci goto again; 21062306a36Sopenharmony_ci } 21162306a36Sopenharmony_ci hfs_bnode_write_u16(node, 21262306a36Sopenharmony_ci offsetof(struct hfs_bnode_desc, num_recs), 21362306a36Sopenharmony_ci node->num_recs); 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci if (rec_off == end_off) 21662306a36Sopenharmony_ci goto skip; 21762306a36Sopenharmony_ci size = fd->keylength + fd->entrylength; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci do { 22062306a36Sopenharmony_ci data_off = hfs_bnode_read_u16(node, rec_off); 22162306a36Sopenharmony_ci hfs_bnode_write_u16(node, rec_off + 2, data_off - size); 22262306a36Sopenharmony_ci rec_off -= 2; 22362306a36Sopenharmony_ci } while (rec_off >= end_off); 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci /* fill hole */ 22662306a36Sopenharmony_ci hfs_bnode_move(node, fd->keyoffset, fd->keyoffset + size, 22762306a36Sopenharmony_ci data_off - fd->keyoffset - size); 22862306a36Sopenharmony_ciskip: 22962306a36Sopenharmony_ci hfs_bnode_dump(node); 23062306a36Sopenharmony_ci if (!fd->record) 23162306a36Sopenharmony_ci hfs_brec_update_parent(fd); 23262306a36Sopenharmony_ci return 0; 23362306a36Sopenharmony_ci} 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_cistatic struct hfs_bnode *hfs_bnode_split(struct hfs_find_data *fd) 23662306a36Sopenharmony_ci{ 23762306a36Sopenharmony_ci struct hfs_btree *tree; 23862306a36Sopenharmony_ci struct hfs_bnode *node, *new_node, *next_node; 23962306a36Sopenharmony_ci struct hfs_bnode_desc node_desc; 24062306a36Sopenharmony_ci int num_recs, new_rec_off, new_off, old_rec_off; 24162306a36Sopenharmony_ci int data_start, data_end, size; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci tree = fd->tree; 24462306a36Sopenharmony_ci node = fd->bnode; 24562306a36Sopenharmony_ci new_node = hfs_bmap_alloc(tree); 24662306a36Sopenharmony_ci if (IS_ERR(new_node)) 24762306a36Sopenharmony_ci return new_node; 24862306a36Sopenharmony_ci hfs_bnode_get(node); 24962306a36Sopenharmony_ci hfs_dbg(BNODE_MOD, "split_nodes: %d - %d - %d\n", 25062306a36Sopenharmony_ci node->this, new_node->this, node->next); 25162306a36Sopenharmony_ci new_node->next = node->next; 25262306a36Sopenharmony_ci new_node->prev = node->this; 25362306a36Sopenharmony_ci new_node->parent = node->parent; 25462306a36Sopenharmony_ci new_node->type = node->type; 25562306a36Sopenharmony_ci new_node->height = node->height; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci if (node->next) 25862306a36Sopenharmony_ci next_node = hfs_bnode_find(tree, node->next); 25962306a36Sopenharmony_ci else 26062306a36Sopenharmony_ci next_node = NULL; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci if (IS_ERR(next_node)) { 26362306a36Sopenharmony_ci hfs_bnode_put(node); 26462306a36Sopenharmony_ci hfs_bnode_put(new_node); 26562306a36Sopenharmony_ci return next_node; 26662306a36Sopenharmony_ci } 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci size = tree->node_size / 2 - node->num_recs * 2 - 14; 26962306a36Sopenharmony_ci old_rec_off = tree->node_size - 4; 27062306a36Sopenharmony_ci num_recs = 1; 27162306a36Sopenharmony_ci for (;;) { 27262306a36Sopenharmony_ci data_start = hfs_bnode_read_u16(node, old_rec_off); 27362306a36Sopenharmony_ci if (data_start > size) 27462306a36Sopenharmony_ci break; 27562306a36Sopenharmony_ci old_rec_off -= 2; 27662306a36Sopenharmony_ci if (++num_recs < node->num_recs) 27762306a36Sopenharmony_ci continue; 27862306a36Sopenharmony_ci /* panic? */ 27962306a36Sopenharmony_ci hfs_bnode_put(node); 28062306a36Sopenharmony_ci hfs_bnode_put(new_node); 28162306a36Sopenharmony_ci if (next_node) 28262306a36Sopenharmony_ci hfs_bnode_put(next_node); 28362306a36Sopenharmony_ci return ERR_PTR(-ENOSPC); 28462306a36Sopenharmony_ci } 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci if (fd->record + 1 < num_recs) { 28762306a36Sopenharmony_ci /* new record is in the lower half, 28862306a36Sopenharmony_ci * so leave some more space there 28962306a36Sopenharmony_ci */ 29062306a36Sopenharmony_ci old_rec_off += 2; 29162306a36Sopenharmony_ci num_recs--; 29262306a36Sopenharmony_ci data_start = hfs_bnode_read_u16(node, old_rec_off); 29362306a36Sopenharmony_ci } else { 29462306a36Sopenharmony_ci hfs_bnode_put(node); 29562306a36Sopenharmony_ci hfs_bnode_get(new_node); 29662306a36Sopenharmony_ci fd->bnode = new_node; 29762306a36Sopenharmony_ci fd->record -= num_recs; 29862306a36Sopenharmony_ci fd->keyoffset -= data_start - 14; 29962306a36Sopenharmony_ci fd->entryoffset -= data_start - 14; 30062306a36Sopenharmony_ci } 30162306a36Sopenharmony_ci new_node->num_recs = node->num_recs - num_recs; 30262306a36Sopenharmony_ci node->num_recs = num_recs; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci new_rec_off = tree->node_size - 2; 30562306a36Sopenharmony_ci new_off = 14; 30662306a36Sopenharmony_ci size = data_start - new_off; 30762306a36Sopenharmony_ci num_recs = new_node->num_recs; 30862306a36Sopenharmony_ci data_end = data_start; 30962306a36Sopenharmony_ci while (num_recs) { 31062306a36Sopenharmony_ci hfs_bnode_write_u16(new_node, new_rec_off, new_off); 31162306a36Sopenharmony_ci old_rec_off -= 2; 31262306a36Sopenharmony_ci new_rec_off -= 2; 31362306a36Sopenharmony_ci data_end = hfs_bnode_read_u16(node, old_rec_off); 31462306a36Sopenharmony_ci new_off = data_end - size; 31562306a36Sopenharmony_ci num_recs--; 31662306a36Sopenharmony_ci } 31762306a36Sopenharmony_ci hfs_bnode_write_u16(new_node, new_rec_off, new_off); 31862306a36Sopenharmony_ci hfs_bnode_copy(new_node, 14, node, data_start, data_end - data_start); 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci /* update new bnode header */ 32162306a36Sopenharmony_ci node_desc.next = cpu_to_be32(new_node->next); 32262306a36Sopenharmony_ci node_desc.prev = cpu_to_be32(new_node->prev); 32362306a36Sopenharmony_ci node_desc.type = new_node->type; 32462306a36Sopenharmony_ci node_desc.height = new_node->height; 32562306a36Sopenharmony_ci node_desc.num_recs = cpu_to_be16(new_node->num_recs); 32662306a36Sopenharmony_ci node_desc.reserved = 0; 32762306a36Sopenharmony_ci hfs_bnode_write(new_node, &node_desc, 0, sizeof(node_desc)); 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci /* update previous bnode header */ 33062306a36Sopenharmony_ci node->next = new_node->this; 33162306a36Sopenharmony_ci hfs_bnode_read(node, &node_desc, 0, sizeof(node_desc)); 33262306a36Sopenharmony_ci node_desc.next = cpu_to_be32(node->next); 33362306a36Sopenharmony_ci node_desc.num_recs = cpu_to_be16(node->num_recs); 33462306a36Sopenharmony_ci hfs_bnode_write(node, &node_desc, 0, sizeof(node_desc)); 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci /* update next bnode header */ 33762306a36Sopenharmony_ci if (next_node) { 33862306a36Sopenharmony_ci next_node->prev = new_node->this; 33962306a36Sopenharmony_ci hfs_bnode_read(next_node, &node_desc, 0, sizeof(node_desc)); 34062306a36Sopenharmony_ci node_desc.prev = cpu_to_be32(next_node->prev); 34162306a36Sopenharmony_ci hfs_bnode_write(next_node, &node_desc, 0, sizeof(node_desc)); 34262306a36Sopenharmony_ci hfs_bnode_put(next_node); 34362306a36Sopenharmony_ci } else if (node->this == tree->leaf_tail) { 34462306a36Sopenharmony_ci /* if there is no next node, this might be the new tail */ 34562306a36Sopenharmony_ci tree->leaf_tail = new_node->this; 34662306a36Sopenharmony_ci mark_inode_dirty(tree->inode); 34762306a36Sopenharmony_ci } 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci hfs_bnode_dump(node); 35062306a36Sopenharmony_ci hfs_bnode_dump(new_node); 35162306a36Sopenharmony_ci hfs_bnode_put(node); 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci return new_node; 35462306a36Sopenharmony_ci} 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_cistatic int hfs_brec_update_parent(struct hfs_find_data *fd) 35762306a36Sopenharmony_ci{ 35862306a36Sopenharmony_ci struct hfs_btree *tree; 35962306a36Sopenharmony_ci struct hfs_bnode *node, *new_node, *parent; 36062306a36Sopenharmony_ci int newkeylen, diff; 36162306a36Sopenharmony_ci int rec, rec_off, end_rec_off; 36262306a36Sopenharmony_ci int start_off, end_off; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci tree = fd->tree; 36562306a36Sopenharmony_ci node = fd->bnode; 36662306a36Sopenharmony_ci new_node = NULL; 36762306a36Sopenharmony_ci if (!node->parent) 36862306a36Sopenharmony_ci return 0; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ciagain: 37162306a36Sopenharmony_ci parent = hfs_bnode_find(tree, node->parent); 37262306a36Sopenharmony_ci if (IS_ERR(parent)) 37362306a36Sopenharmony_ci return PTR_ERR(parent); 37462306a36Sopenharmony_ci __hfs_brec_find(parent, fd, hfs_find_rec_by_key); 37562306a36Sopenharmony_ci if (fd->record < 0) 37662306a36Sopenharmony_ci return -ENOENT; 37762306a36Sopenharmony_ci hfs_bnode_dump(parent); 37862306a36Sopenharmony_ci rec = fd->record; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci /* size difference between old and new key */ 38162306a36Sopenharmony_ci if ((tree->attributes & HFS_TREE_VARIDXKEYS) || 38262306a36Sopenharmony_ci (tree->cnid == HFSPLUS_ATTR_CNID)) 38362306a36Sopenharmony_ci newkeylen = hfs_bnode_read_u16(node, 14) + 2; 38462306a36Sopenharmony_ci else 38562306a36Sopenharmony_ci fd->keylength = newkeylen = tree->max_key_len + 2; 38662306a36Sopenharmony_ci hfs_dbg(BNODE_MOD, "update_rec: %d, %d, %d\n", 38762306a36Sopenharmony_ci rec, fd->keylength, newkeylen); 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci rec_off = tree->node_size - (rec + 2) * 2; 39062306a36Sopenharmony_ci end_rec_off = tree->node_size - (parent->num_recs + 1) * 2; 39162306a36Sopenharmony_ci diff = newkeylen - fd->keylength; 39262306a36Sopenharmony_ci if (!diff) 39362306a36Sopenharmony_ci goto skip; 39462306a36Sopenharmony_ci if (diff > 0) { 39562306a36Sopenharmony_ci end_off = hfs_bnode_read_u16(parent, end_rec_off); 39662306a36Sopenharmony_ci if (end_rec_off - end_off < diff) { 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci hfs_dbg(BNODE_MOD, "splitting index node\n"); 39962306a36Sopenharmony_ci fd->bnode = parent; 40062306a36Sopenharmony_ci new_node = hfs_bnode_split(fd); 40162306a36Sopenharmony_ci if (IS_ERR(new_node)) 40262306a36Sopenharmony_ci return PTR_ERR(new_node); 40362306a36Sopenharmony_ci parent = fd->bnode; 40462306a36Sopenharmony_ci rec = fd->record; 40562306a36Sopenharmony_ci rec_off = tree->node_size - (rec + 2) * 2; 40662306a36Sopenharmony_ci end_rec_off = tree->node_size - 40762306a36Sopenharmony_ci (parent->num_recs + 1) * 2; 40862306a36Sopenharmony_ci } 40962306a36Sopenharmony_ci } 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci end_off = start_off = hfs_bnode_read_u16(parent, rec_off); 41262306a36Sopenharmony_ci hfs_bnode_write_u16(parent, rec_off, start_off + diff); 41362306a36Sopenharmony_ci start_off -= 4; /* move previous cnid too */ 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci while (rec_off > end_rec_off) { 41662306a36Sopenharmony_ci rec_off -= 2; 41762306a36Sopenharmony_ci end_off = hfs_bnode_read_u16(parent, rec_off); 41862306a36Sopenharmony_ci hfs_bnode_write_u16(parent, rec_off, end_off + diff); 41962306a36Sopenharmony_ci } 42062306a36Sopenharmony_ci hfs_bnode_move(parent, start_off + diff, start_off, 42162306a36Sopenharmony_ci end_off - start_off); 42262306a36Sopenharmony_ciskip: 42362306a36Sopenharmony_ci hfs_bnode_copy(parent, fd->keyoffset, node, 14, newkeylen); 42462306a36Sopenharmony_ci hfs_bnode_dump(parent); 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci hfs_bnode_put(node); 42762306a36Sopenharmony_ci node = parent; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci if (new_node) { 43062306a36Sopenharmony_ci __be32 cnid; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci if (!new_node->parent) { 43362306a36Sopenharmony_ci hfs_btree_inc_height(tree); 43462306a36Sopenharmony_ci new_node->parent = tree->root; 43562306a36Sopenharmony_ci } 43662306a36Sopenharmony_ci fd->bnode = hfs_bnode_find(tree, new_node->parent); 43762306a36Sopenharmony_ci /* create index key and entry */ 43862306a36Sopenharmony_ci hfs_bnode_read_key(new_node, fd->search_key, 14); 43962306a36Sopenharmony_ci cnid = cpu_to_be32(new_node->this); 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci __hfs_brec_find(fd->bnode, fd, hfs_find_rec_by_key); 44262306a36Sopenharmony_ci hfs_brec_insert(fd, &cnid, sizeof(cnid)); 44362306a36Sopenharmony_ci hfs_bnode_put(fd->bnode); 44462306a36Sopenharmony_ci hfs_bnode_put(new_node); 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci if (!rec) { 44762306a36Sopenharmony_ci if (new_node == node) 44862306a36Sopenharmony_ci goto out; 44962306a36Sopenharmony_ci /* restore search_key */ 45062306a36Sopenharmony_ci hfs_bnode_read_key(node, fd->search_key, 14); 45162306a36Sopenharmony_ci } 45262306a36Sopenharmony_ci new_node = NULL; 45362306a36Sopenharmony_ci } 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci if (!rec && node->parent) 45662306a36Sopenharmony_ci goto again; 45762306a36Sopenharmony_ciout: 45862306a36Sopenharmony_ci fd->bnode = node; 45962306a36Sopenharmony_ci return 0; 46062306a36Sopenharmony_ci} 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_cistatic int hfs_btree_inc_height(struct hfs_btree *tree) 46362306a36Sopenharmony_ci{ 46462306a36Sopenharmony_ci struct hfs_bnode *node, *new_node; 46562306a36Sopenharmony_ci struct hfs_bnode_desc node_desc; 46662306a36Sopenharmony_ci int key_size, rec; 46762306a36Sopenharmony_ci __be32 cnid; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci node = NULL; 47062306a36Sopenharmony_ci if (tree->root) { 47162306a36Sopenharmony_ci node = hfs_bnode_find(tree, tree->root); 47262306a36Sopenharmony_ci if (IS_ERR(node)) 47362306a36Sopenharmony_ci return PTR_ERR(node); 47462306a36Sopenharmony_ci } 47562306a36Sopenharmony_ci new_node = hfs_bmap_alloc(tree); 47662306a36Sopenharmony_ci if (IS_ERR(new_node)) { 47762306a36Sopenharmony_ci hfs_bnode_put(node); 47862306a36Sopenharmony_ci return PTR_ERR(new_node); 47962306a36Sopenharmony_ci } 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci tree->root = new_node->this; 48262306a36Sopenharmony_ci if (!tree->depth) { 48362306a36Sopenharmony_ci tree->leaf_head = tree->leaf_tail = new_node->this; 48462306a36Sopenharmony_ci new_node->type = HFS_NODE_LEAF; 48562306a36Sopenharmony_ci new_node->num_recs = 0; 48662306a36Sopenharmony_ci } else { 48762306a36Sopenharmony_ci new_node->type = HFS_NODE_INDEX; 48862306a36Sopenharmony_ci new_node->num_recs = 1; 48962306a36Sopenharmony_ci } 49062306a36Sopenharmony_ci new_node->parent = 0; 49162306a36Sopenharmony_ci new_node->next = 0; 49262306a36Sopenharmony_ci new_node->prev = 0; 49362306a36Sopenharmony_ci new_node->height = ++tree->depth; 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci node_desc.next = cpu_to_be32(new_node->next); 49662306a36Sopenharmony_ci node_desc.prev = cpu_to_be32(new_node->prev); 49762306a36Sopenharmony_ci node_desc.type = new_node->type; 49862306a36Sopenharmony_ci node_desc.height = new_node->height; 49962306a36Sopenharmony_ci node_desc.num_recs = cpu_to_be16(new_node->num_recs); 50062306a36Sopenharmony_ci node_desc.reserved = 0; 50162306a36Sopenharmony_ci hfs_bnode_write(new_node, &node_desc, 0, sizeof(node_desc)); 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci rec = tree->node_size - 2; 50462306a36Sopenharmony_ci hfs_bnode_write_u16(new_node, rec, 14); 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci if (node) { 50762306a36Sopenharmony_ci /* insert old root idx into new root */ 50862306a36Sopenharmony_ci node->parent = tree->root; 50962306a36Sopenharmony_ci if (node->type == HFS_NODE_LEAF || 51062306a36Sopenharmony_ci tree->attributes & HFS_TREE_VARIDXKEYS || 51162306a36Sopenharmony_ci tree->cnid == HFSPLUS_ATTR_CNID) 51262306a36Sopenharmony_ci key_size = hfs_bnode_read_u16(node, 14) + 2; 51362306a36Sopenharmony_ci else 51462306a36Sopenharmony_ci key_size = tree->max_key_len + 2; 51562306a36Sopenharmony_ci hfs_bnode_copy(new_node, 14, node, 14, key_size); 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci if (!(tree->attributes & HFS_TREE_VARIDXKEYS) && 51862306a36Sopenharmony_ci (tree->cnid != HFSPLUS_ATTR_CNID)) { 51962306a36Sopenharmony_ci key_size = tree->max_key_len + 2; 52062306a36Sopenharmony_ci hfs_bnode_write_u16(new_node, 14, tree->max_key_len); 52162306a36Sopenharmony_ci } 52262306a36Sopenharmony_ci cnid = cpu_to_be32(node->this); 52362306a36Sopenharmony_ci hfs_bnode_write(new_node, &cnid, 14 + key_size, 4); 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci rec -= 2; 52662306a36Sopenharmony_ci hfs_bnode_write_u16(new_node, rec, 14 + key_size + 4); 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci hfs_bnode_put(node); 52962306a36Sopenharmony_ci } 53062306a36Sopenharmony_ci hfs_bnode_put(new_node); 53162306a36Sopenharmony_ci mark_inode_dirty(tree->inode); 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci return 0; 53462306a36Sopenharmony_ci} 535