mirror of
https://github.com/linux-apfs/linux-apfs.git
synced 2026-08-18 02:22:47 -07:00
apfs: implement ->evict_inode() method
Implement the ->evict_inode() superblock operation, which must deal with orphaned inode records and also their fake hardlinks under private-dir. Signed-off-by: Ernesto A. Fernández <ernesto.mnd.fernandez@gmail.com>
This commit is contained in:
1 parent
296f6db803
commit
930ac1900e
5 files changed
+135
No files matched your search
@@ -996,6 +996,73 @@ fail:
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* apfs_delete_orphan_link - Delete the link for an orphan inode
|
||||
* @inode: the vfs inode
|
||||
*
|
||||
* Returns 0 on success or a negative error code in case of failure.
|
||||
*/
|
||||
int apfs_delete_orphan_link(struct inode *inode)
|
||||
{
|
||||
struct super_block *sb = inode->i_sb;
|
||||
struct apfs_sb_info *sbi = APFS_SB(sb);
|
||||
struct apfs_key key;
|
||||
struct apfs_query *query;
|
||||
struct apfs_inode_val *inode_val;
|
||||
char *node_raw;
|
||||
struct qstr qname;
|
||||
struct apfs_drec drec;
|
||||
struct apfs_inode_info parent_info;
|
||||
int err;
|
||||
|
||||
err = apfs_orphan_name(inode, &qname);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/*
|
||||
* XXX: fake the basic parent fields needed by apfs_dentry_lookup(); in
|
||||
* the future we'll use a real private-dir inode.
|
||||
*/
|
||||
parent_info.vfs_inode.i_sb = sb;
|
||||
parent_info.vfs_inode.i_ino = APFS_PRIV_DIR_INO_NUM;
|
||||
#if BITS_PER_LONG == 32
|
||||
parent_info.i_ino = APFS_PRIV_DIR_INO_NUM;
|
||||
#endif
|
||||
|
||||
query = apfs_dentry_lookup(&parent_info.vfs_inode, &qname, &drec);
|
||||
if (IS_ERR(query)) {
|
||||
err = PTR_ERR(query);
|
||||
query = NULL;
|
||||
goto fail;
|
||||
}
|
||||
err = apfs_btree_remove(query);
|
||||
if (err)
|
||||
goto fail;
|
||||
apfs_free_query(sb, query);
|
||||
query = NULL;
|
||||
|
||||
/* XXX: update the child count in the parent inode */
|
||||
apfs_init_inode_key(APFS_PRIV_DIR_INO_NUM, &key);
|
||||
query = apfs_alloc_query(sbi->s_cat_root, NULL /* parent */);
|
||||
if (!query) {
|
||||
err = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
query->key = &key;
|
||||
query->flags |= APFS_QUERY_CAT | APFS_QUERY_EXACT;
|
||||
err = apfs_btree_query(sb, &query);
|
||||
if (err)
|
||||
goto fail;
|
||||
node_raw = query->node->object.bh->b_data;
|
||||
inode_val = (struct apfs_inode_val *)(node_raw + query->off);
|
||||
le32_add_cpu(&inode_val->nchildren, -1);
|
||||
|
||||
fail:
|
||||
apfs_free_query(sb, query);
|
||||
kfree(qname.name);
|
||||
return err;
|
||||
}
|
||||
|
||||
int apfs_unlink(struct inode *dir, struct dentry *dentry)
|
||||
{
|
||||
struct super_block *sb = dir->i_sb;
|
||||
|
||||
@@ -64,6 +64,7 @@ extern int apfs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
|
||||
extern int apfs_link(struct dentry *old_dentry, struct inode *dir,
|
||||
struct dentry *dentry);
|
||||
extern int apfs_unlink(struct inode *dir, struct dentry *dentry);
|
||||
extern int apfs_delete_orphan_link(struct inode *inode);
|
||||
|
||||
extern const struct file_operations apfs_dir_operations;
|
||||
|
||||
|
||||
@@ -508,6 +508,71 @@ fail:
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* apfs_delete_inode - Delete an inode record and update the volume file count
|
||||
* @inode: the vfs inode to delete
|
||||
*
|
||||
* Returns 0 on success or a negative error code in case of failure.
|
||||
*/
|
||||
static int apfs_delete_inode(struct inode *inode)
|
||||
{
|
||||
struct super_block *sb = inode->i_sb;
|
||||
struct apfs_sb_info *sbi = APFS_SB(sb);
|
||||
struct apfs_superblock *vsb_raw = sbi->s_vsb_raw;
|
||||
struct apfs_query *query;
|
||||
int ret;
|
||||
|
||||
query = apfs_inode_lookup(inode);
|
||||
if (IS_ERR(query))
|
||||
return PTR_ERR(query);
|
||||
ret = apfs_btree_remove(query);
|
||||
apfs_free_query(sb, query);
|
||||
|
||||
ASSERT(sbi->s_xid == le64_to_cpu(vsb_raw->apfs_o.o_xid));
|
||||
switch (inode->i_mode & S_IFMT) {
|
||||
case S_IFREG:
|
||||
le64_add_cpu(&vsb_raw->apfs_num_files, -1);
|
||||
break;
|
||||
case S_IFDIR:
|
||||
le64_add_cpu(&vsb_raw->apfs_num_directories, -1);
|
||||
break;
|
||||
case S_IFLNK:
|
||||
le64_add_cpu(&vsb_raw->apfs_num_symlinks, -1);
|
||||
break;
|
||||
default:
|
||||
le64_add_cpu(&vsb_raw->apfs_num_other_fsobjects, -1);
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void apfs_evict_inode(struct inode *inode)
|
||||
{
|
||||
struct super_block *sb = inode->i_sb;
|
||||
|
||||
if (is_bad_inode(inode) || inode->i_nlink)
|
||||
goto out_clear;
|
||||
|
||||
if (apfs_transaction_start(sb))
|
||||
goto out_report;
|
||||
if (apfs_delete_inode(inode))
|
||||
goto out_abort;
|
||||
if (apfs_delete_orphan_link(inode))
|
||||
goto out_abort;
|
||||
if (apfs_transaction_commit(sb))
|
||||
goto out_abort;
|
||||
goto out_clear;
|
||||
|
||||
out_abort:
|
||||
apfs_transaction_abort(sb);
|
||||
out_report:
|
||||
apfs_warn(sb,
|
||||
"failed to delete orphan inode 0x%llx\n", apfs_ino(inode));
|
||||
out_clear:
|
||||
truncate_inode_pages_final(&inode->i_data);
|
||||
clear_inode(inode);
|
||||
}
|
||||
|
||||
/**
|
||||
* apfs_new_inode - Create a new in-memory inode
|
||||
* @dir: parent inode
|
||||
|
||||
@@ -153,6 +153,7 @@ static inline u64 apfs_ino(const struct inode *inode)
|
||||
|
||||
extern struct inode *apfs_iget(struct super_block *sb, u64 cnid);
|
||||
extern int apfs_update_inode(struct inode *inode, char *new_name);
|
||||
extern void apfs_evict_inode(struct inode *inode);
|
||||
extern int apfs_getattr(const struct path *path, struct kstat *stat,
|
||||
u32 request_mask, unsigned int query_flags);
|
||||
extern struct inode *apfs_new_inode(struct inode *dir, umode_t mode,
|
||||
|
||||
@@ -693,6 +693,7 @@ static const struct super_operations apfs_sops = {
|
||||
.alloc_inode = apfs_alloc_inode,
|
||||
.destroy_inode = apfs_destroy_inode,
|
||||
.write_inode = apfs_write_inode,
|
||||
.evict_inode = apfs_evict_inode,
|
||||
.put_super = apfs_put_super,
|
||||
.statfs = apfs_statfs,
|
||||
.show_options = apfs_show_options,
|
||||
|
||||
Reference in new issue
Block a user