/*
* linux/fs/hfs/inode.c
*
* Copyright (C) 1995-1997 Paul H. Hargrove
* (C) 2003 Ardis Technologies <roman@ardistech.com>
* This file may be distributed under the terms of the GNU General Public License.
*
* This file contains inode-related functions which do not depend on
* which scheme is being used to represent forks.
*
* Based on the minix file system code, (C) 1991, 1992 by Linus Torvalds
*/
#include <linux/pagemap.h>
#include <linux/mpage.h>
#include <linux/sched.h>
#include "hfs_fs.h"
#include "btree.h"
static const struct file_operations hfs_file_operations;
static const struct inode_operations hfs_file_inode_operations;
/*================ Variable-like macros ================*/
#define HFS_VALID_MODE_BITS (S_IFREG | S_IFDIR | S_IRWXUGO)
static int hfs_writepage(struct page *page, struct writeback_control *wbc)
{
return block_write_full_page(page, hfs_get_block, wbc);
}
static int hfs_readpage(struct file *file, struct page *page)
{
return block_read_full_page(page, hfs_get_block);
}
static int hfs_write_begin(struct file *file, struct address_space *mapping,
loff_t pos, unsigned len, unsigned flags,
struct page **pagep, void **fsdata)
{
*pagep = NULL;
return cont_write_begin(file, mapping, pos, len, flags, pagep, fsdata,
hfs_get_block,
&HFS_I(mapping->host)->phys_size);
}
static sector_t hfs_bmap(struct address_space *mapping, sector_t block)
{
return generic_block_bmap(mapping, block, hfs_get_block);
}
static int hfs_releasepage(struct page *page, gfp_t mask)
{
struct inode *inode = page->mapping->host;
struct super_block *sb = inode->i_sb;
struct hfs_btree *tree;
struct hfs_bnode *node;
u32 nidx;
int i, res = 1;
switch (inode->i_ino) {
case HFS_EXT_CNID:
tree = HFS_SB(sb)->ext_tree;
break;
case HFS_CAT_CNID:
tree = HFS_SB(sb)->cat_tree;
break;
default:
BUG();
return 0;
}
if (tree->node_size >= PAGE_CACHE_SIZE) {
nidx = page->index >> (tree->node_size_shift - PAGE_CACHE_SHIFT);
spin_lock(&tree->hash_lock);
node = hfs_bnode_findhash(tree, nidx);
if (!node)
;
else if (atomic_read(&node->refcnt))
res = 0;
if (res && node) {
hfs_bnode_unhash(node);
hfs_bnode_free(node);
}
spin_unlock(&tree->hash_lock);
} else {
nidx = page->index << (PAGE_CACHE_SHIFT - tree->node_size_shift);
i = 1 << (PAGE_CACHE_SHIFT - tree->node_size_shift);
spin_lock(&tree->hash_lock);
do {
node = hfs_bnode_findhash(tree, nidx++);
if (!node)
continue;
if (atomic_read(&node->refcnt)) {
res = 0;
break;
}
hfs_bnode_unhash(node);
hfs_bnode_free(node);
} while (--i && nidx < tree->node_count);
spin_unlock(&tree->hash_lock);
}
return res ? try_to_free_buffers(page) : 0;
}
static ssize_t hfs_direct_IO(int rw, struct kiocb *iocb,
const struct iovec *iov, loff_t offset, unsigned long nr_segs)
{
struct file *file = iocb->ki_filp;
struct inode *inode = file->f_path.dentry->d_inode->i_mapping->host;
return blockdev_direct_IO(rw, iocb, inode, inode->i_sb->s_bdev, iov,
offset, nr_segs, hfs_get_block, NULL);
}
static int hfs_writepages(struct address_space *mapping,
struct writeback_control *wbc)
{
return mpage_writepages(mapping, wbc, hfs_get_block);
}
const struct address_space_operations hfs_btree_aops = {
.readpage = hfs_readpage,
.writepage = hfs_writepage,
.sync_page = block_sync_page,
.write_begin = hfs_write_begin,
.write_end = generic_write_end,
.bmap = hfs_bmap,
.releasepage = hfs_releasepage,
};
const struct address_space_operations hfs_aops = {
.readpage = hfs_readpage,
.writepage = hfs_writepage,
.sync_page = block_sync_page,
.write_begin = hfs_write_begin,
.write_end = generic_write_end,
.bmap = hfs_bmap,
.direct_IO = hfs_direct_IO,
.writepages = hfs_writepages,
};
/*
* hfs_new_inode
*/
struct inode *hfs_new_inode(struct inode *dir, struct qstr *name, int mode)
{
struct super_block *sb = dir->i_sb;
struct inode *inode = new_inode(sb);
if (!inode)
return NULL;
mutex_init(&HFS_I(inode)->extents_lock);
INIT_LIST_HEAD(&HFS_I(inode)->open_dir_list);
hfs_cat_build_key(sb, (btree_key *)&HFS_I(inode)->cat_key, dir->i_ino, name);
inode->i_ino = HFS_SB(sb)->next_id++;
inode->i_mode = mode;
inode->i_uid = current_fsuid();
inode->i_gid = current_fsgid();
inode->i_nlink = 1;
inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME_SEC;
HFS_I(inode)->flags = 0;
HFS_I(inode)->rsrc_inode = NULL;
HFS_I(inode)->fs_blocks = 0;
if (S_ISDIR(mode)) {
inode->i_size = 2;
HFS_SB(sb)->folder_count++;
if (dir->i_ino == HFS_ROOT_CNID)
HFS_SB(sb)->root_dirs++;
inode->i_op = &hfs_dir_inode_operations;
inode->i_fop = &hfs_dir_operations;
inode->i_mode |= S_IRWXUGO;
inode->i_mode &= ~HFS_SB(inode->i_sb)->s_dir_umask;
} else if (S_ISREG(mode)) {
HFS_I(inode)->clump_blocks = HFS_SB(sb)->clumpablks;
HFS_SB(sb)->file_count++;
if (dir->i_ino == HFS_ROOT_CNID)
HFS_SB(sb)->root_files++;
inode->i_op = &hfs_file_inode_operations;
inode->i_fop = &hfs_file_operations;
inode->i_mapping->a_ops = &hfs_aops;
inode->i_mode |= S_IRUGO|S_IXUGO;
if (mode & S_IWUSR)
inode->i_mode |= S_IWUGO;
inode->i_mode &= ~HFS_SB(inode->i_sb)->s_file_umask;
HFS_I(inode)->phys_size = 0;
HFS_I(inode)->alloc_blocks = 0;
HFS_I(inode)->first_blocks = 0;
HFS_I(inode)->cached_start = 0;
HFS_I(inode)->cached_blocks = 0;
memset(HFS_I(inode)->first_extents, 0, sizeof(hfs_extent_rec));
memset(HFS_I(inode)->cached_extents, 0, sizeof(hfs_extent_rec));
}
insert_inode_hash(inode);
mark_inode_dirty(inode);
set_bit(HFS_FLG_MDB_DIRTY, &HFS_SB(sb)->flags);
sb->s_dirt = 1;
return inode;
}
void hfs_delete_inode(struct inode *inode)
{
struct super_block *sb = inode->i_sb;
dprint(DBG_INODE, "delete_inode: %lu\n", inode->i_ino);
if (S_ISDIR(inode->i_mode)) {
HFS_SB(sb)->folder_count--;
if (HFS_I(inode)->cat_key.ParID == cpu_to_be32(HFS_ROOT_CNID))
HFS_SB(sb)->root_dirs--;
set_bit(HFS_FLG_MDB_DIRTY, &HFS_SB(sb)->flags);
sb->s_dirt = 1;
return;
}
HFS_SB(sb)->file_count--;
if (HFS_I(inode)->cat_key.ParID == cpu_to_be32(HFS_ROOT_CNID))
HFS_SB(sb)->root_files--;
if (S_ISREG(inode->i_mode)) {
if (!inode->i_nlink) {
inode->i_size = 0;
hfs_file_truncate(inode);
}
}
set_bit(HFS_FLG_MDB_DIRTY, &HFS_SB(sb)->flags);
sb->s_dirt = 1;
}
void hfs_inode_read_fork(struct inode *inode, struct hfs_extent *ext,
__be32 __log_size, __be32 phys_size, u32 clump_size)
{
struct super_block *sb = inode->i_sb;
u32 log_size = be32_to_cpu(__log_size);
u16 count;
int i;
memcpy(HFS_I(inode)->first_extents, ext, sizeof(hfs_extent_rec));
for (count = 0, i = 0; i < 3; i++)
count += be16_to_cpu(ext[i].count);
HFS_I(inode)->first_blocks = count;
inode->i_size = HFS_I(inode)->phys_size = log_size;
HFS_I(inode)->fs_blocks = (log_size + sb->s_blocksize - 1) >> sb->s_blocksize_bits;
inode_set_bytes(inode, HFS_I(inode)->fs_blocks << sb->s_blocksize_bits);
HFS_I(inode)->alloc_blocks = be32_to_cpu(phys_size) /
HFS_SB(sb)->alloc_blksz;
HFS_I(inode)->clump_blocks = clump_size / HFS_SB(sb)->alloc_blksz;
if (!HFS_I(inode)->clump_blocks)
HFS_I(inode)->clump_blocks = HFS_SB(sb)->clumpablks;
}
struct hfs_iget_data {
struct hfs_cat_key *key;
hfs_cat_rec *rec;
};
static int hfs_test_inode(struct inode *inode, void *data)
{
struct hfs_iget_data *idata = data;
hfs_cat_rec *rec;
rec = idata->rec;
switch (rec->type) {
case HFS_CDR_DIR:
return inode->i_ino == be32_to_cpu(rec->dir.DirID);
case HFS_CDR_FIL:
return inode->i_ino == be32_to_cpu(rec->file.FlNum);
default:
BUG();
return 1;
}
}
/*
* hfs_read_inode
*/
static int hfs_read_inode(struct inode *inode, void *data)
{
struct hfs_iget_data *idata = data;
struct hfs_sb_info *hsb = HFS_SB(inode->i_sb);
hfs_cat_rec *rec;
HFS_I(inode)->flags = 0;
HFS_I(inode)->rsrc_inode = NULL;
mutex_init(&HFS_I(inode)->extents_lock);
INIT_LIST_HEAD(&HFS_I(inode)->open_dir_list);
/* Initialize the inode */
inode->i_uid