static int hfsplus_mkdir(struct inode *dir, struct dentry *dentry, int mode) { struct inode *inode; hfsplus_handle_t hfsplus_handle; int res; if ((res = hfsplus_journal_start(__FUNCTION__, dir->i_sb, &hfsplus_handle))) return res; inode = hfsplus_new_inode(&hfsplus_handle, dir->i_sb, S_IFDIR | mode); if (!inode) { hfsplus_journal_stop(&hfsplus_handle); return -ENOSPC; } res = hfsplus_create_cat(&hfsplus_handle, inode->i_ino, dir, &dentry->d_name, inode); if (res) { inode->i_nlink = 0; hfsplus_delete_inode(&hfsplus_handle, inode); iput(inode); hfsplus_journal_stop(&hfsplus_handle); return res; } hfsplus_instantiate(dentry, inode, inode->i_ino); res = hfsplus_journalled_mark_inode_dirty(__FUNCTION__, &hfsplus_handle, inode); hfsplus_journal_stop(&hfsplus_handle); return res; }
static int hfsplus_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t rdev) { struct super_block *sb; struct inode *inode; hfsplus_handle_t hfsplus_handle; int res; if ((res = hfsplus_journal_start(__FUNCTION__, dir->i_sb, &hfsplus_handle))) return res; sb = dir->i_sb; inode = hfsplus_new_inode(&hfsplus_handle, sb, mode); if (!inode) { hfsplus_journal_stop(&hfsplus_handle); return -ENOSPC; } res = hfsplus_create_cat(&hfsplus_handle, inode->i_ino, dir, &dentry->d_name, inode); if (res) { inode->i_nlink = 0; hfsplus_delete_inode(&hfsplus_handle, inode); iput(inode); hfsplus_journal_stop(&hfsplus_handle); return res; } init_special_inode(inode, mode, rdev); hfsplus_instantiate(dentry, inode, inode->i_ino); res = hfsplus_journalled_mark_inode_dirty(__FUNCTION__, &hfsplus_handle, inode); hfsplus_journal_stop(&hfsplus_handle); return 0; }
static int hfsplus_unlink(struct inode *dir, struct dentry *dentry) { struct super_block *sb = dir->i_sb; struct inode *inode = dentry->d_inode; struct qstr str; char name[32]; u32 cnid; int res; hfsplus_handle_t hfsplus_handle; if (HFSPLUS_IS_RSRC(inode)) return -EPERM; if ((res = hfsplus_journal_start(__FUNCTION__, dir->i_sb, &hfsplus_handle))) return res; cnid = (u32)(unsigned long)dentry->d_fsdata; if (inode->i_ino == cnid && atomic_read(&HFSPLUS_I(inode).opencnt)) { str.name = name; str.len = sprintf(name, "temp%lu", inode->i_ino); res = hfsplus_rename_cat(&hfsplus_handle, inode->i_ino, dir, &dentry->d_name, HFSPLUS_SB(sb).hidden_dir, &str); if (!res) inode->i_flags |= S_DEAD; hfsplus_journal_stop(&hfsplus_handle); return res; } res = hfsplus_delete_cat(&hfsplus_handle, cnid, dir, &dentry->d_name); if (res) { hfsplus_journal_stop(&hfsplus_handle); return res; } if (inode->i_nlink > 0) drop_nlink(inode); if (inode->i_ino == cnid) clear_nlink(inode); if (!inode->i_nlink) { if (inode->i_ino != cnid) { HFSPLUS_SB(sb).file_count--; if (!atomic_read(&HFSPLUS_I(inode).opencnt)) { res = hfsplus_delete_cat(&hfsplus_handle, inode->i_ino, HFSPLUS_SB(sb).hidden_dir, NULL); if (!res) hfsplus_delete_inode(&hfsplus_handle, inode); } else inode->i_flags |= S_DEAD; } else hfsplus_delete_inode(&hfsplus_handle,inode); } else HFSPLUS_SB(sb).file_count--; inode->i_ctime = CURRENT_TIME_SEC; res = hfsplus_journalled_mark_inode_dirty(__FUNCTION__, &hfsplus_handle, inode); hfsplus_journal_stop(&hfsplus_handle); return res; }
int hfs_brec_remove(hfsplus_handle_t *hfsplus_handle, struct hfs_find_data *fd) { struct hfs_btree *tree; struct hfs_bnode *node, *parent; int end_off, rec_off, data_off, size; tree = fd->tree; node = fd->bnode; again: rec_off = tree->node_size - (fd->record + 2) * 2; end_off = tree->node_size - (node->num_recs + 1) * 2; if (node->type == HFS_NODE_LEAF) { tree->leaf_count--; if (hfsplus_journalled_mark_inode_dirty(__FUNCTION__, hfsplus_handle, tree->inode)) return -1; } hfs_bnode_dump(node); dprint(DBG_BNODE_MOD, "remove_rec: %d, %d\n", fd->record, fd->keylength + fd->entrylength); if (!--node->num_recs) { hfs_bnode_unlink(hfsplus_handle, node); if (!node->parent) return 0; parent = hfs_bnode_find(hfsplus_handle, tree, node->parent); if (IS_ERR(parent)) return PTR_ERR(parent); hfs_bnode_put(hfsplus_handle, node); node = fd->bnode = parent; __hfs_brec_find(node, fd); goto again; } hfs_bnode_write_u16(hfsplus_handle, node, offsetof(struct hfs_bnode_desc, num_recs), node->num_recs); if (rec_off == end_off) goto skip; size = fd->keylength + fd->entrylength; do { data_off = hfs_bnode_read_u16(node, rec_off); hfs_bnode_write_u16(hfsplus_handle, node, rec_off + 2, data_off - size); rec_off -= 2; } while (rec_off >= end_off); /* fill hole */ hfs_bnode_move(hfsplus_handle, node, fd->keyoffset, fd->keyoffset + size, data_off - fd->keyoffset - size); skip: hfs_bnode_dump(node); if (!fd->record) hfs_brec_update_parent(hfsplus_handle, fd); return 0; }
static int hfsplus_symlink(struct inode *dir, struct dentry *dentry, const char *symname) { struct super_block *sb; struct inode *inode; hfsplus_handle_t hfsplus_handle; int res; if ((res = hfsplus_journal_start(__FUNCTION__, dir->i_sb, &hfsplus_handle))) return res; sb = dir->i_sb; inode = hfsplus_new_inode(&hfsplus_handle, sb, S_IFLNK | S_IRWXUGO); if (!inode) { hfsplus_journal_stop(&hfsplus_handle); return -ENOSPC; } res = page_symlink(inode, symname, strlen(symname) + 1); if (res) { inode->i_nlink = 0; hfsplus_delete_inode(&hfsplus_handle, inode); iput(inode); hfsplus_journal_stop(&hfsplus_handle); return res; } if ((res = hfsplus_journalled_mark_inode_dirty(__FUNCTION__, &hfsplus_handle, inode))) goto symlink_out; res = hfsplus_create_cat(&hfsplus_handle, inode->i_ino, dir, &dentry->d_name, inode); if (!res) { hfsplus_instantiate(dentry, inode, inode->i_ino); res = hfsplus_journalled_mark_inode_dirty(__FUNCTION__, &hfsplus_handle, inode); } symlink_out: hfsplus_journal_stop(&hfsplus_handle); return res; }
void hfsplus_file_truncate(struct inode *inode) { struct super_block *sb = inode->i_sb; struct hfs_find_data fd; u32 alloc_cnt, blk_cnt, start; int res; hfsplus_handle_t hfsplus_handle; dprint(DBG_INODE, "truncate: %lu, %Lu -> %Lu\n", inode->i_ino, (long long)HFSPLUS_I(inode).phys_size, inode->i_size); if (inode->i_size > HFSPLUS_I(inode).phys_size) { struct address_space *mapping = inode->i_mapping; struct page *page; void *fsdata; u32 size = inode->i_size - 1; int res; page = grab_cache_page(mapping, size >> PAGE_CACHE_SHIFT); if (!page) return; size &= PAGE_CACHE_SIZE - 1; size++; res = mapping->a_ops->write_begin(NULL, mapping, size, 0, AOP_FLAG_UNINTERRUPTIBLE, &page, &fsdata); if (!res) res = mapping->a_ops->write_end(NULL, mapping, size, 0, 0, page, fsdata); if (res) inode->i_size = HFSPLUS_I(inode).phys_size; unlock_page(page); page_cache_release(page); if (hfsplus_journal_start(__FUNCTION__, sb, &hfsplus_handle)) return; hfsplus_journalled_mark_inode_dirty(__FUNCTION__, &hfsplus_handle, inode); hfsplus_journal_stop(&hfsplus_handle); return; } else if (inode->i_size == HFSPLUS_I(inode).phys_size)
static int hfsplus_rmdir(struct inode *dir, struct dentry *dentry) { struct inode *inode; hfsplus_handle_t hfsplus_handle; int res; inode = dentry->d_inode; if (inode->i_size != 2) return -ENOTEMPTY; if ((res = hfsplus_journal_start(__FUNCTION__, dir->i_sb, &hfsplus_handle))) return res; res = hfsplus_delete_cat(&hfsplus_handle, inode->i_ino, dir, &dentry->d_name); if (res) { hfsplus_journal_stop(&hfsplus_handle); return res; } clear_nlink(inode); inode->i_ctime = CURRENT_TIME_SEC; hfsplus_delete_inode(&hfsplus_handle, inode); res = hfsplus_journalled_mark_inode_dirty(__FUNCTION__, &hfsplus_handle, inode); hfsplus_journal_stop(&hfsplus_handle); return res; }
int hfs_brec_insert(hfsplus_handle_t *hfsplus_handle, struct hfs_find_data *fd, void *entry, int entry_len) { struct hfs_btree *tree; struct hfs_bnode *node, *new_node; int size, key_len, rec; int data_off, end_off; int idx_rec_off, data_rec_off, end_rec_off; __be32 cnid; tree = fd->tree; if (!fd->bnode) { if (!tree->root) hfs_btree_inc_height(hfsplus_handle, tree); fd->bnode = hfs_bnode_find(hfsplus_handle, tree, tree->leaf_head); if (IS_ERR(fd->bnode)) return PTR_ERR(fd->bnode); fd->record = -1; } new_node = NULL; key_len = be16_to_cpu(fd->search_key->key_len) + 2; again: /* new record idx and complete record size */ rec = fd->record + 1; size = key_len + entry_len; node = fd->bnode; hfs_bnode_dump(node); /* get last offset */ end_rec_off = tree->node_size - (node->num_recs + 1) * 2; end_off = hfs_bnode_read_u16(node, end_rec_off); end_rec_off -= 2; dprint(DBG_BNODE_MOD, "insert_rec: %d, %d, %d, %d\n", rec, size, end_off, end_rec_off); if (size > end_rec_off - end_off) { if (new_node) panic("not enough room!\n"); new_node = hfs_bnode_split(hfsplus_handle, fd); if (IS_ERR(new_node)) return PTR_ERR(new_node); goto again; } if (node->type == HFS_NODE_LEAF) { tree->leaf_count++; if (hfsplus_journalled_mark_inode_dirty(__FUNCTION__, hfsplus_handle, tree->inode)) return -1; } node->num_recs++; /* write new last offset */ hfs_bnode_write_u16(hfsplus_handle, node, offsetof(struct hfs_bnode_desc, num_recs), node->num_recs); hfs_bnode_write_u16(hfsplus_handle, node, end_rec_off, end_off + size); data_off = end_off; data_rec_off = end_rec_off + 2; idx_rec_off = tree->node_size - (rec + 1) * 2; if (idx_rec_off == data_rec_off) goto skip; /* move all following entries */ do { data_off = hfs_bnode_read_u16(node, data_rec_off + 2); hfs_bnode_write_u16(hfsplus_handle, node, data_rec_off, data_off + size); data_rec_off += 2; } while (data_rec_off < idx_rec_off); /* move data away */ hfs_bnode_move(hfsplus_handle, node, data_off + size, data_off, end_off - data_off); skip: hfs_bnode_write(hfsplus_handle, node, fd->search_key, data_off, key_len); hfs_bnode_write(hfsplus_handle, node, entry, data_off + key_len, entry_len); hfs_bnode_dump(node); if (new_node) { /* update parent key if we inserted a key * at the start of the first node */ if (!rec && new_node != node) hfs_brec_update_parent(hfsplus_handle, fd); hfs_bnode_put(hfsplus_handle, fd->bnode); if (!new_node->parent) { hfs_btree_inc_height(hfsplus_handle, tree); new_node->parent = tree->root; } fd->bnode = hfs_bnode_find(hfsplus_handle, tree, new_node->parent); /* create index data entry */ cnid = cpu_to_be32(new_node->this); entry = &cnid; entry_len = sizeof(cnid); /* get index key */ hfs_bnode_read_key(new_node, fd->search_key, 14); __hfs_brec_find(fd->bnode, fd); hfs_bnode_put(hfsplus_handle, new_node); new_node = NULL; if (tree->attributes & HFS_TREE_VARIDXKEYS) key_len = be16_to_cpu(fd->search_key->key_len) + 2; else { fd->search_key->key_len = cpu_to_be16(tree->max_key_len); key_len = tree->max_key_len + 2; } goto again; } if (!rec) hfs_brec_update_parent(hfsplus_handle, fd); return 0; }
static int hfsplus_link(struct dentry *src_dentry, struct inode *dst_dir, struct dentry *dst_dentry) { struct super_block *sb = dst_dir->i_sb; struct inode *inode = src_dentry->d_inode; struct inode *src_dir = src_dentry->d_parent->d_inode; hfsplus_handle_t hfsplus_handle; struct qstr str; char name[32]; u32 cnid, id; int res; if (HFSPLUS_IS_RSRC(inode)) return -EPERM; if ((res = hfsplus_journal_start(__FUNCTION__, dst_dir->i_sb, &hfsplus_handle))) return res; if (inode->i_ino == (u32)(unsigned long)src_dentry->d_fsdata) { for (;;) { get_random_bytes(&id, sizeof(cnid)); id &= 0x3fffffff; str.name = name; str.len = sprintf(name, "iNode%d", id); res = hfsplus_rename_cat(&hfsplus_handle, inode->i_ino, src_dir, &src_dentry->d_name, HFSPLUS_SB(sb).hidden_dir, &str); if (!res) break; if (res != -EEXIST) { hfsplus_journal_stop(&hfsplus_handle); return res; } } HFSPLUS_I(inode).dev = id; cnid = HFSPLUS_SB(sb).next_cnid++; src_dentry->d_fsdata = (void *)(unsigned long)cnid; res = hfsplus_create_cat(&hfsplus_handle, cnid, src_dir, &src_dentry->d_name, inode); if (res) { /* panic? */ hfsplus_journal_stop(&hfsplus_handle); return res; } HFSPLUS_SB(sb).file_count++; } cnid = HFSPLUS_SB(sb).next_cnid++; res = hfsplus_create_cat(&hfsplus_handle, cnid, dst_dir, &dst_dentry->d_name, inode); if (res) { hfsplus_journal_stop(&hfsplus_handle); return res; } inc_nlink(inode); hfsplus_instantiate(dst_dentry, inode, cnid); atomic_inc(&inode->i_count); inode->i_ctime = CURRENT_TIME_SEC; res = hfsplus_journalled_mark_inode_dirty(__FUNCTION__, &hfsplus_handle, inode); HFSPLUS_SB(sb).file_count++; sb->s_dirt = 1; hfsplus_journal_stop(&hfsplus_handle); return res; }
int hfsplus_file_extend(hfsplus_handle_t *hfsplus_handle, struct inode *inode) { struct super_block *sb = inode->i_sb; u32 start, len, goal; int res; if (HFSPLUS_SB(sb).alloc_file->i_size * 8 < HFSPLUS_SB(sb).total_blocks - HFSPLUS_SB(sb).free_blocks + 8) { // extend alloc file printk("extend alloc file! (%Lu,%u,%u)\n", HFSPLUS_SB(sb).alloc_file->i_size * 8, HFSPLUS_SB(sb).total_blocks, HFSPLUS_SB(sb).free_blocks); return -ENOSPC; //BUG(); } mutex_lock(&HFSPLUS_I(inode).extents_lock); if (HFSPLUS_I(inode).alloc_blocks == HFSPLUS_I(inode).first_blocks) goal = hfsplus_ext_lastblock(HFSPLUS_I(inode).first_extents); else { res = hfsplus_ext_read_extent(hfsplus_handle, inode, HFSPLUS_I(inode).alloc_blocks); if (res) goto out; goal = hfsplus_ext_lastblock(HFSPLUS_I(inode).cached_extents); } len = HFSPLUS_I(inode).clump_blocks; start = hfsplus_block_allocate(hfsplus_handle, sb, HFSPLUS_SB(sb).total_blocks, goal, &len); if (start >= HFSPLUS_SB(sb).total_blocks) { start = hfsplus_block_allocate(hfsplus_handle, sb, goal, 0, &len); if (start >= goal) { res = -ENOSPC; goto out; } } dprint(DBG_EXTENT, "extend %lu: %u,%u\n", inode->i_ino, start, len); if (HFSPLUS_I(inode).alloc_blocks <= HFSPLUS_I(inode).first_blocks) { if (!HFSPLUS_I(inode).first_blocks) { dprint(DBG_EXTENT, "first extents\n"); /* no extents yet */ HFSPLUS_I(inode).first_extents[0].start_block = cpu_to_be32(start); HFSPLUS_I(inode).first_extents[0].block_count = cpu_to_be32(len); res = 0; } else { /* try to append to extents in inode */ res = hfsplus_add_extent(HFSPLUS_I(inode).first_extents, HFSPLUS_I(inode).alloc_blocks, start, len); if (res == -ENOSPC) goto insert_extent; } if (!res) { hfsplus_dump_extent(HFSPLUS_I(inode).first_extents); HFSPLUS_I(inode).first_blocks += len; } } else { res = hfsplus_add_extent(HFSPLUS_I(inode).cached_extents, HFSPLUS_I(inode).alloc_blocks - HFSPLUS_I(inode).cached_start, start, len); if (!res) { hfsplus_dump_extent(HFSPLUS_I(inode).cached_extents); HFSPLUS_I(inode).flags |= HFSPLUS_FLG_EXT_DIRTY; HFSPLUS_I(inode).cached_blocks += len; } else if (res == -ENOSPC) goto insert_extent; } out: mutex_unlock(&HFSPLUS_I(inode).extents_lock); if (!res) { HFSPLUS_I(inode).alloc_blocks += len; res = hfsplus_journalled_mark_inode_dirty(__FUNCTION__, hfsplus_handle, inode); } return res; insert_extent: dprint(DBG_EXTENT, "insert new extent\n"); hfsplus_ext_write_extent(hfsplus_handle, inode); memset(HFSPLUS_I(inode).cached_extents, 0, sizeof(hfsplus_extent_rec)); HFSPLUS_I(inode).cached_extents[0].start_block = cpu_to_be32(start); HFSPLUS_I(inode).cached_extents[0].block_count = cpu_to_be32(len); hfsplus_dump_extent(HFSPLUS_I(inode).cached_extents); HFSPLUS_I(inode).flags |= HFSPLUS_FLG_EXT_DIRTY | HFSPLUS_FLG_EXT_NEW; HFSPLUS_I(inode).cached_start = HFSPLUS_I(inode).alloc_blocks; HFSPLUS_I(inode).cached_blocks = len; res = 0; goto out; }
/* Get a block at iblock for inode, possibly allocating if create */ int hfsplus_get_block(struct inode *inode, sector_t iblock, struct buffer_head *bh_result, int create) { struct super_block *sb; int res = -EIO; u32 ablock, dblock, mask; int shift; hfsplus_handle_t *hfsplus_handle, tmp_hfsplus_handle; tmp_hfsplus_handle.journaled = !HFSPLUS_JOURNAL_PRESENT; tmp_hfsplus_handle.handle = NULL; sb = inode->i_sb; /* Journal device */ if (HFSPLUS_SB(sb).jnl.journaled == HFSPLUS_JOURNAL_PRESENT) { /* Write Metadata */ if (((inode->i_mapping->a_ops == &hfsplus_journalled_btree_aops) || (inode->i_mapping->a_ops == &hfsplus_journalled_aops)) && create) { hfsplus_handle = hfsplus_jbd_current_handle(); if (hfsplus_handle == NULL) { printk("hfsplus_handle is NULL\n"); hfsplus_handle = &tmp_hfsplus_handle; } } else { hfsplus_handle = &tmp_hfsplus_handle; } } /* Non-journal device */ else { hfsplus_handle = &tmp_hfsplus_handle; } /* Convert inode block to disk allocation block */ shift = HFSPLUS_SB(sb).alloc_blksz_shift - sb->s_blocksize_bits; ablock = iblock >> HFSPLUS_SB(sb).fs_shift; if (iblock >= HFSPLUS_I(inode).fs_blocks) { if (iblock > HFSPLUS_I(inode).fs_blocks || !create) { return -EIO; } if (ablock >= HFSPLUS_I(inode).alloc_blocks) { res = hfsplus_file_extend(hfsplus_handle, inode); if (res) { return res; } } } else create = 0; if (ablock < HFSPLUS_I(inode).first_blocks) { dblock = hfsplus_ext_find_block(HFSPLUS_I(inode).first_extents, ablock); goto done; } mutex_lock(&HFSPLUS_I(inode).extents_lock); res = hfsplus_ext_read_extent(hfsplus_handle, inode, ablock); if (!res) { dblock = hfsplus_ext_find_block(HFSPLUS_I(inode).cached_extents, ablock - HFSPLUS_I(inode).cached_start); } else { mutex_unlock(&HFSPLUS_I(inode).extents_lock); return -EIO; } mutex_unlock(&HFSPLUS_I(inode).extents_lock); done: dprint(DBG_EXTENT, "get_block(%lu): %llu - %u\n", inode->i_ino, (long long)iblock, dblock); mask = (1 << HFSPLUS_SB(sb).fs_shift) - 1; map_bh(bh_result, sb, (dblock << HFSPLUS_SB(sb).fs_shift) + HFSPLUS_SB(sb).blockoffset + (iblock & mask)); if (create) { set_buffer_new(bh_result); HFSPLUS_I(inode).phys_size += sb->s_blocksize; HFSPLUS_I(inode).fs_blocks++; inode_add_bytes(inode, sb->s_blocksize); if (hfsplus_journalled_mark_inode_dirty(__FUNCTION__, hfsplus_handle, inode)) { printk("HFS+-fs: Error in %s()\n", __FUNCTION__); return -1; } } return 0; }