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:
Ernesto A. Fernández committed 2019-09-07 23:12:56 -03:00
1 parent 296f6db803
commit 930ac1900e
5 files changed
+135

No files matched your search

+67
View File
@@ -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;
+1
View File
@@ -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;
+65
View File
@@ -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
+1
View File
@@ -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,
+1
View File
@@ -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,