int hfsplus_rmxattr(struct dentry *dentry, const char *name) { struct inode *inode = dentry->d_inode; struct hfs_btree *btree = HFSPLUS_SB(inode->i_sb)->attr_tree; struct hfs_find_data fd; hfsplus_attr_entry entry; char tmp[32] = {0}; int res = 0; dprint(DBG_XATTR, "hfs: rmattr [%s][%s] [%lu]\n", dentry->d_name.name, name, dentry->d_inode->i_ino); if (!strcmp(name, SZ_XATTR_NAME_TYPE) || !strcmp(name, SZ_XATTR_NAME_CREATOR)) { return hfsplus_setxattr_buildin(dentry, name, tmp, 4, 0); } if (!strcmp(name, SZ_XATTR_NAME_FINDRINFO)) { return hfsplus_setxattr_buildin(dentry, name, tmp, 32, 0); } else if (!strcmp(name, SZ_XATTR_NAME_RFORK)) { return -EOPNOTSUPP; } res = hfs_find_init(btree, &fd); if (res) { return res; } hfsplus_attr_build_key(inode->i_sb, fd.search_key, cpu_to_be32((u32)(unsigned long)dentry->d_fsdata), name, 0); if ((res = hfs_brec_find(&fd))) { hfs_find_exit(&fd); goto out; } hfs_bnode_read(fd.bnode, &entry, fd.entryoffset, sizeof(hfsplus_attr_entry)); if (be32_to_cpu(entry.type) != kHFSPlusAttrData) { res = -EOPNOTSUPP; hfs_find_exit(&fd); goto out; } res = hfs_brec_remove(&fd); inode->i_mtime = inode->i_ctime = CURRENT_TIME_SEC; hfsplus_mark_inode_dirty(inode, HFSPLUS_I_ATTR_DIRTY); hfs_find_exit(&fd); // check xattr after hfs_find_exit (unlock attr btree) if (!hfsplus_has_xattr(dentry)) { hfsplus_set_cat_flag(inode, HFS_HAS_ATTR_MASK, 0); // here would lock cat tree. } //if ((res = filemap_write_and_wait(inode->i_mapping))) { // goto out; //} out: return res; }
int hfs_free_fork(struct super_block *sb, struct hfs_cat_file *file, int type) { struct hfs_find_data fd; u32 total_blocks, blocks, start; u32 cnid = be32_to_cpu(file->FlNum); struct hfs_extent *extent; int res, i; if (type == HFS_FK_DATA) { total_blocks = be32_to_cpu(file->PyLen); extent = file->ExtRec; } else { total_blocks = be32_to_cpu(file->RPyLen); extent = file->RExtRec; } total_blocks /= HFS_SB(sb)->alloc_blksz; if (!total_blocks) return 0; blocks = 0; for (i = 0; i < 3; extent++, i++) blocks += be16_to_cpu(extent[i].count); res = hfs_free_extents(sb, extent, blocks, blocks); if (res) return res; if (total_blocks == blocks) return 0; hfs_find_init(HFS_SB(sb)->ext_tree, &fd); do { res = __hfs_ext_read_extent(&fd, extent, cnid, total_blocks, type); if (res) break; start = be16_to_cpu(fd.key->ext.FABN); hfs_free_extents(sb, extent, total_blocks - start, total_blocks); hfs_brec_remove(&fd); total_blocks = start; } while (total_blocks > blocks); hfs_find_exit(&fd); return res; }
int hfsplus_free_fork(struct super_block *sb, u32 cnid, struct hfsplus_fork_raw *fork, int type) { struct hfs_find_data fd; hfsplus_extent_rec ext_entry; u32 total_blocks, blocks, start; int res, i; total_blocks = be32_to_cpu(fork->total_blocks); if (!total_blocks) return 0; blocks = 0; for (i = 0; i < 8; i++) blocks += be32_to_cpu(fork->extents[i].block_count); res = hfsplus_free_extents(sb, fork->extents, blocks, blocks); if (res) return res; if (total_blocks == blocks) return 0; res = hfs_find_init(HFSPLUS_SB(sb)->ext_tree, &fd); if (res) return res; do { res = __hfsplus_ext_read_extent(&fd, ext_entry, cnid, total_blocks, type); if (res) break; start = be32_to_cpu(fd.key->ext.start_block); hfsplus_free_extents(sb, ext_entry, total_blocks - start, total_blocks); hfs_brec_remove(&fd); total_blocks = start; } while (total_blocks > blocks); hfs_find_exit(&fd); 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; 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; 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->prepare_write(NULL, page, size, size); if (!res) res = mapping->a_ops->commit_write(NULL, page, size, size); if (res) inode->i_size = HFSPLUS_I(inode).phys_size; unlock_page(page); page_cache_release(page); mark_inode_dirty(inode); return; } blk_cnt = (inode->i_size + HFSPLUS_SB(sb).alloc_blksz - 1) >> HFSPLUS_SB(sb).alloc_blksz_shift; alloc_cnt = HFSPLUS_I(inode).alloc_blocks; if (blk_cnt == alloc_cnt) goto out; down(&HFSPLUS_I(inode).extents_lock); hfs_find_init(HFSPLUS_SB(sb).ext_tree, &fd); while (1) { if (alloc_cnt == HFSPLUS_I(inode).first_blocks) { hfsplus_free_extents(sb, HFSPLUS_I(inode).first_extents, alloc_cnt, alloc_cnt - blk_cnt); hfsplus_dump_extent(HFSPLUS_I(inode).first_extents); HFSPLUS_I(inode).first_blocks = blk_cnt; break; } res = __hfsplus_ext_cache_extent(&fd, inode, alloc_cnt); if (res) break; start = HFSPLUS_I(inode).cached_start; hfsplus_free_extents(sb, HFSPLUS_I(inode).cached_extents, alloc_cnt - start, alloc_cnt - blk_cnt); hfsplus_dump_extent(HFSPLUS_I(inode).cached_extents); if (blk_cnt > start) { HFSPLUS_I(inode).flags |= HFSPLUS_FLG_EXT_DIRTY; break; } alloc_cnt = start; HFSPLUS_I(inode).cached_start = HFSPLUS_I(inode).cached_blocks = 0; HFSPLUS_I(inode).flags &= ~(HFSPLUS_FLG_EXT_DIRTY | HFSPLUS_FLG_EXT_NEW); hfs_brec_remove(&fd); } hfs_find_exit(&fd); up(&HFSPLUS_I(inode).extents_lock); HFSPLUS_I(inode).alloc_blocks = blk_cnt; out: HFSPLUS_I(inode).phys_size = inode->i_size; mark_inode_dirty(inode); inode->i_blocks = (inode->i_size + sb->s_blocksize - 1) >> sb->s_blocksize_bits; }
void hfsplus_file_truncate(struct inode *inode) { struct super_block *sb = inode->i_sb; struct hfsplus_inode_info *hip = HFSPLUS_I(inode); struct hfs_find_data fd; u32 alloc_cnt, blk_cnt, start; int res; hfs_dbg(INODE, "truncate: %lu, %llu -> %llu\n", inode->i_ino, (long long)hip->phys_size, inode->i_size); if (inode->i_size > hip->phys_size) { struct address_space *mapping = inode->i_mapping; struct page *page; void *fsdata; loff_t size = inode->i_size; res = pagecache_write_begin(NULL, mapping, size, 0, AOP_FLAG_UNINTERRUPTIBLE, &page, &fsdata); if (res) return; res = pagecache_write_end(NULL, mapping, size, 0, 0, page, fsdata); if (res < 0) return; mark_inode_dirty(inode); return; } else if (inode->i_size == hip->phys_size) return; blk_cnt = (inode->i_size + HFSPLUS_SB(sb)->alloc_blksz - 1) >> HFSPLUS_SB(sb)->alloc_blksz_shift; mutex_lock(&hip->extents_lock); alloc_cnt = hip->alloc_blocks; if (blk_cnt == alloc_cnt) goto out_unlock; res = hfs_find_init(HFSPLUS_SB(sb)->ext_tree, &fd); if (res) { mutex_unlock(&hip->extents_lock); /* XXX: We lack error handling of hfsplus_file_truncate() */ return; } while (1) { if (alloc_cnt == hip->first_blocks) { hfsplus_free_extents(sb, hip->first_extents, alloc_cnt, alloc_cnt - blk_cnt); hfsplus_dump_extent(hip->first_extents); hip->first_blocks = blk_cnt; break; } res = __hfsplus_ext_cache_extent(&fd, inode, alloc_cnt); if (res) break; start = hip->cached_start; hfsplus_free_extents(sb, hip->cached_extents, alloc_cnt - start, alloc_cnt - blk_cnt); hfsplus_dump_extent(hip->cached_extents); if (blk_cnt > start) { hip->extent_state |= HFSPLUS_EXT_DIRTY; break; } alloc_cnt = start; hip->cached_start = hip->cached_blocks = 0; hip->extent_state &= ~(HFSPLUS_EXT_DIRTY | HFSPLUS_EXT_NEW); hfs_brec_remove(&fd); } hfs_find_exit(&fd); hip->alloc_blocks = blk_cnt; out_unlock: mutex_unlock(&hip->extents_lock); hip->phys_size = inode->i_size; hip->fs_blocks = (inode->i_size + sb->s_blocksize - 1) >> sb->s_blocksize_bits; inode_set_bytes(inode, hip->fs_blocks << sb->s_blocksize_bits); hfsplus_mark_inode_dirty(inode, HFSPLUS_I_ALLOC_DIRTY); }
void hfs_file_truncate(struct inode *inode) { struct super_block *sb = inode->i_sb; struct hfs_find_data fd; u16 blk_cnt, alloc_cnt, start; u32 size; int res; dprint(DBG_INODE, "truncate: %lu, %Lu -> %Lu\n", inode->i_ino, (long long)HFS_I(inode)->phys_size, inode->i_size); if (inode->i_size > HFS_I(inode)->phys_size) { struct address_space *mapping = inode->i_mapping; void *fsdata; struct page *page; int res; /* XXX: Can use generic_cont_expand? */ size = inode->i_size - 1; res = pagecache_write_begin(NULL, mapping, size+1, 0, AOP_FLAG_UNINTERRUPTIBLE, &page, &fsdata); if (!res) { res = pagecache_write_end(NULL, mapping, size+1, 0, 0, page, fsdata); } if (res) inode->i_size = HFS_I(inode)->phys_size; return; } else if (inode->i_size == HFS_I(inode)->phys_size) return; size = inode->i_size + HFS_SB(sb)->alloc_blksz - 1; blk_cnt = size / HFS_SB(sb)->alloc_blksz; alloc_cnt = HFS_I(inode)->alloc_blocks; if (blk_cnt == alloc_cnt) goto out; mutex_lock(&HFS_I(inode)->extents_lock); hfs_find_init(HFS_SB(sb)->ext_tree, &fd); while (1) { if (alloc_cnt == HFS_I(inode)->first_blocks) { hfs_free_extents(sb, HFS_I(inode)->first_extents, alloc_cnt, alloc_cnt - blk_cnt); hfs_dump_extent(HFS_I(inode)->first_extents); HFS_I(inode)->first_blocks = blk_cnt; break; } res = __hfs_ext_cache_extent(&fd, inode, alloc_cnt); if (res) break; start = HFS_I(inode)->cached_start; hfs_free_extents(sb, HFS_I(inode)->cached_extents, alloc_cnt - start, alloc_cnt - blk_cnt); hfs_dump_extent(HFS_I(inode)->cached_extents); if (blk_cnt > start) { HFS_I(inode)->flags |= HFS_FLG_EXT_DIRTY; break; } alloc_cnt = start; HFS_I(inode)->cached_start = HFS_I(inode)->cached_blocks = 0; HFS_I(inode)->flags &= ~(HFS_FLG_EXT_DIRTY|HFS_FLG_EXT_NEW); hfs_brec_remove(&fd); } hfs_find_exit(&fd); mutex_unlock(&HFS_I(inode)->extents_lock); HFS_I(inode)->alloc_blocks = blk_cnt; out: HFS_I(inode)->phys_size = inode->i_size; HFS_I(inode)->fs_blocks = (inode->i_size + sb->s_blocksize - 1) >> sb->s_blocksize_bits; inode_set_bytes(inode, HFS_I(inode)->fs_blocks << sb->s_blocksize_bits); mark_inode_dirty(inode); }
/* * The flags XATTR_REPLACE and XATTR_CREATE * specify that an extended attribute must exist and must not exist * previous to the call, respectively. */ int hfsplus_setxattr(struct dentry *dentry, const char *name, const void *value, size_t size, int flags) { struct inode *inode = dentry->d_inode; struct hfs_btree *btree = HFSPLUS_SB(inode->i_sb)->attr_tree; struct hfs_find_data fd; hfsplus_attr_entry *entry = NULL; int res = 0, entry_size = 0; dprint(DBG_XATTR, "hfs: setattr [%s][%s] [%lu]\n", dentry->d_name.name, name, inode->i_ino); if (!value) { return -EINVAL; } if (!strcmp(name, SZ_XATTR_NAME_TYPE) || !strcmp(name, SZ_XATTR_NAME_CREATOR) || !strcmp(name, SZ_XATTR_NAME_FINDRINFO)) { return hfsplus_setxattr_buildin(dentry, name, value, size, flags); } else if (!strcmp(name, SZ_XATTR_NAME_RFORK)) { return -EOPNOTSUPP; } #if 0 #define HFSPLUS_MAX_ATTR_LEN (128*1024) if (size > HFSPLUS_MAX_ATTR_LEN) { return E2BIG; } #endif if (size > hfsplus_get_maxinline_attrsize(btree)) { return -E2BIG; } res = hfs_find_init(btree, &fd); if (res) { return res; } hfsplus_attr_build_key(inode->i_sb, fd.search_key, cpu_to_be32((u32)(unsigned long)dentry->d_fsdata), name, 0); res = hfs_brec_find(&fd); if (res == -ENOENT) { if (flags & XATTR_REPLACE) { res = -ENODATA; goto out; } } else if (res != 0) { goto out; } else { // res == 0 if (flags & XATTR_CREATE) { res = -EEXIST; goto out; } if ((res = hfs_brec_remove(&fd))) { goto out; } // decrease record count to avoid the next insert worng. --fd.record; //hfsplus_mark_inode_dirty(btree->inode, HFSPLUS_I_ATTR_DIRTY); //if ((res = filemap_write_and_wait(btree->inode->i_mapping))) { // goto out; //} } entry_size = sizeof(struct hfsplus_attr_data) - 2 + size + ((size & 1) ? 1 : 0); entry = kmalloc(entry_size * sizeof(char), GFP_NOFS); if (!entry) { res = -ENOMEM; goto out; } entry->type = cpu_to_be32(kHFSPlusAttrData); entry->data.attr_size = cpu_to_be32(size); memcpy(entry->data.attr_data, value, size); res = hfs_brec_insert(&fd, entry, entry_size); // set has attribute bit on file/folder hfsplus_set_cat_flag(inode, HFS_HAS_ATTR_MASK, 1); inode->i_mtime = inode->i_ctime = CURRENT_TIME_SEC; hfsplus_mark_inode_dirty(inode, HFSPLUS_I_ATTR_DIRTY); //if ((res = filemap_write_and_wait(btree->inode->i_mapping))) { // goto out; //} out: if (entry) { kfree(entry); } hfs_find_exit(&fd); return res; }