You've already forked linux-apfs-rw
mirror of
https://github.com/linux-apfs/linux-apfs-rw.git
synced 2026-05-01 15:01:34 -07:00
Check free space before starting a transaction
Try to avoid ENOSPC during a transaction by checking ahead of time if the available space is enough for the worst case of the operation. Now that ENOSPC is not expected to happen, simply handle the remaining weird errors by aborting and setting the filesystem read-only. Signed-off-by: Ernesto A. Fernández <ernesto@corellium.com>
This commit is contained in:
@@ -149,6 +149,14 @@ struct apfs_bh_info {
|
||||
struct list_head list; /* List of buffers in the transaction */
|
||||
};
|
||||
|
||||
/*
|
||||
* Used to report how many operations may be needed for a transaction
|
||||
*/
|
||||
struct apfs_max_ops {
|
||||
int cat; /* Maximum catalog records that may need changing */
|
||||
int blks; /* Maximum extent blocks that may need changing */
|
||||
};
|
||||
|
||||
/* Mount option flags for a container */
|
||||
#define APFS_CHECK_NODES 1
|
||||
#define APFS_READWRITE 2
|
||||
@@ -641,6 +649,7 @@ extern int apfs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||
struct inode *new_dir, struct dentry *new_dentry,
|
||||
unsigned int flags);
|
||||
extern int apfs_delete_orphan_link(struct inode *inode);
|
||||
extern int APFS_DELETE_ORPHAN_LINK_MAXOPS(void);
|
||||
|
||||
/* extents.c */
|
||||
extern int apfs_extent_from_query(struct apfs_query *query,
|
||||
@@ -652,15 +661,18 @@ extern int apfs_get_block(struct inode *inode, sector_t iblock,
|
||||
extern int apfs_flush_extent_cache(struct inode *inode);
|
||||
extern int apfs_get_new_block(struct inode *inode, sector_t iblock,
|
||||
struct buffer_head *bh_result, int create);
|
||||
extern int APFS_GET_NEW_BLOCK_MAXOPS(void);
|
||||
|
||||
/* inode.c */
|
||||
extern struct inode *apfs_iget(struct super_block *sb, u64 cnid);
|
||||
extern int apfs_update_inode(struct inode *inode, char *new_name);
|
||||
extern int APFS_UPDATE_INODE_MAXOPS(void);
|
||||
extern void apfs_evict_inode(struct inode *inode);
|
||||
extern struct inode *apfs_new_inode(struct inode *dir, umode_t mode,
|
||||
dev_t rdev);
|
||||
extern int apfs_create_inode_rec(struct super_block *sb, struct inode *inode,
|
||||
struct dentry *dentry);
|
||||
extern int APFS_CREATE_INODE_REC_MAXOPS(void);
|
||||
extern int apfs_setattr(struct dentry *dentry, struct iattr *iattr);
|
||||
long apfs_dir_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
|
||||
long apfs_file_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
|
||||
@@ -672,6 +684,7 @@ extern int apfs_getattr(const struct path *path, struct kstat *stat, u32 request
|
||||
#endif
|
||||
|
||||
extern int apfs_crypto_adj_refcnt(struct super_block *sb, u64 crypto_id, int delta);
|
||||
extern int APFS_CRYPTO_ADJ_REFCNT_MAXOPS(void);
|
||||
|
||||
/* key.c */
|
||||
extern int apfs_filename_cmp(struct super_block *sb,
|
||||
@@ -728,7 +741,7 @@ extern int apfs_read_catalog(struct super_block *sb, bool write);
|
||||
/* transaction.c */
|
||||
extern void apfs_cpoint_data_allocate(struct super_block *sb, u64 *bno);
|
||||
extern int apfs_cpoint_data_free(struct super_block *sb, u64 bno);
|
||||
extern int apfs_transaction_start(struct super_block *sb);
|
||||
extern int apfs_transaction_start(struct super_block *sb, struct apfs_max_ops maxops);
|
||||
extern int apfs_transaction_commit(struct super_block *sb);
|
||||
extern int apfs_transaction_join(struct super_block *sb,
|
||||
struct buffer_head *bh);
|
||||
@@ -743,6 +756,7 @@ extern int apfs_xattr_get(struct inode *inode, const char *name, void *buffer,
|
||||
size_t size);
|
||||
extern int apfs_xattr_set(struct inode *inode, const char *name, const void *value,
|
||||
size_t size, int flags);
|
||||
extern int APFS_XATTR_SET_MAXOPS(void);
|
||||
extern ssize_t apfs_listxattr(struct dentry *dentry, char *buffer, size_t size);
|
||||
|
||||
/* xfield.c */
|
||||
|
||||
@@ -394,6 +394,7 @@ fail:
|
||||
apfs_free_query(sb, query);
|
||||
return ret;
|
||||
}
|
||||
#define APFS_CREATE_DENTRY_REC_MAXOPS 1
|
||||
|
||||
/**
|
||||
* apfs_build_sibling_val - Allocate and initialize a sibling link's value
|
||||
@@ -469,6 +470,7 @@ fail:
|
||||
apfs_free_query(sb, query);
|
||||
return ret;
|
||||
}
|
||||
#define APFS_CREATE_SIBLING_LINK_REC_MAXOPS 1
|
||||
|
||||
/**
|
||||
* apfs_create_sibling_map_rec - Create a sibling map record for a dentry
|
||||
@@ -510,6 +512,7 @@ fail:
|
||||
apfs_free_query(sb, query);
|
||||
return ret;
|
||||
}
|
||||
#define APFS_CREATE_SIBLING_MAP_REC_MAXOPS 1
|
||||
|
||||
/**
|
||||
* apfs_create_sibling_recs - Create sibling link and map records for a dentry
|
||||
@@ -542,6 +545,8 @@ static int apfs_create_sibling_recs(struct dentry *dentry,
|
||||
*sibling_id = cnid;
|
||||
return 0;
|
||||
}
|
||||
#define APFS_CREATE_SIBLING_RECS_MAXOPS (APFS_CREATE_SIBLING_LINK_REC_MAXOPS + \
|
||||
APFS_CREATE_SIBLING_MAP_REC_MAXOPS)
|
||||
|
||||
/**
|
||||
* apfs_create_dentry - Create all records for a new dentry
|
||||
@@ -578,6 +583,9 @@ static int apfs_create_dentry(struct dentry *dentry, struct inode *inode)
|
||||
--APFS_I(parent)->i_nchildren;
|
||||
return err;
|
||||
}
|
||||
#define APFS_CREATE_DENTRY_MAXOPS (APFS_CREATE_SIBLING_RECS_MAXOPS + \
|
||||
APFS_CREATE_DENTRY_REC_MAXOPS + \
|
||||
APFS_UPDATE_INODE_MAXOPS())
|
||||
|
||||
/**
|
||||
* apfs_undo_create_dentry - Clean up apfs_create_dentry()
|
||||
@@ -595,9 +603,15 @@ int apfs_mkany(struct inode *dir, struct dentry *dentry, umode_t mode,
|
||||
{
|
||||
struct super_block *sb = dir->i_sb;
|
||||
struct inode *inode;
|
||||
struct apfs_max_ops maxops;
|
||||
int err;
|
||||
|
||||
err = apfs_transaction_start(sb);
|
||||
maxops.cat = APFS_CREATE_INODE_REC_MAXOPS() + APFS_CREATE_DENTRY_MAXOPS;
|
||||
if (symname)
|
||||
maxops.cat += APFS_XATTR_SET_MAXOPS();
|
||||
maxops.blks = 0;
|
||||
|
||||
err = apfs_transaction_start(sb, maxops);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
@@ -693,6 +707,8 @@ static int apfs_prepare_dentry_for_link(struct dentry *dentry)
|
||||
return apfs_create_dentry_rec(d_inode(dentry), &dentry->d_name,
|
||||
apfs_ino(parent), sibling_id);
|
||||
}
|
||||
#define APFS_PREPARE_DENTRY_FOR_LINK_MAXOPS (1 + APFS_CREATE_SIBLING_RECS_MAXOPS + \
|
||||
APFS_CREATE_DENTRY_REC_MAXOPS)
|
||||
|
||||
/**
|
||||
* __apfs_undo_link - Clean up __apfs_link()
|
||||
@@ -743,15 +759,22 @@ fail:
|
||||
drop_nlink(inode);
|
||||
return err;
|
||||
}
|
||||
#define __APFS_LINK_MAXOPS (APFS_UPDATE_INODE_MAXOPS() + \
|
||||
APFS_PREPARE_DENTRY_FOR_LINK_MAXOPS + \
|
||||
APFS_CREATE_DENTRY_MAXOPS)
|
||||
|
||||
int apfs_link(struct dentry *old_dentry, struct inode *dir,
|
||||
struct dentry *dentry)
|
||||
{
|
||||
struct super_block *sb = dir->i_sb;
|
||||
struct inode *inode = d_inode(old_dentry);
|
||||
struct apfs_max_ops maxops;
|
||||
int err;
|
||||
|
||||
err = apfs_transaction_start(sb);
|
||||
maxops.cat = __APFS_LINK_MAXOPS;
|
||||
maxops.blks = 0;
|
||||
|
||||
err = apfs_transaction_start(sb, maxops);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
@@ -813,6 +836,7 @@ fail:
|
||||
apfs_free_query(sb, query);
|
||||
return ret;
|
||||
}
|
||||
#define APFS_DELETE_SIBLING_LINK_REC_MAXOPS 1
|
||||
|
||||
/**
|
||||
* apfs_delete_sibling_map_rec - Delete the sibling map record for a dentry
|
||||
@@ -851,6 +875,7 @@ fail:
|
||||
apfs_free_query(sb, query);
|
||||
return ret;
|
||||
}
|
||||
#define APFS_DELETE_SIBLING_MAP_REC_MAXOPS 1
|
||||
|
||||
/**
|
||||
* apfs_delete_dentry - Delete all records for a dentry
|
||||
@@ -893,6 +918,9 @@ static int apfs_delete_dentry(struct dentry *dentry)
|
||||
++APFS_I(parent)->i_nchildren;
|
||||
return err;
|
||||
}
|
||||
#define APFS_DELETE_DENTRY_MAXOPS (1 + APFS_DELETE_SIBLING_LINK_REC_MAXOPS + \
|
||||
APFS_DELETE_SIBLING_MAP_REC_MAXOPS + \
|
||||
APFS_UPDATE_INODE_MAXOPS())
|
||||
|
||||
/**
|
||||
* apfs_undo_delete_dentry - Clean up apfs_delete_dentry()
|
||||
@@ -1059,6 +1087,8 @@ fail:
|
||||
kfree(qname.name);
|
||||
return err;
|
||||
}
|
||||
#define APFS_CREATE_ORPHAN_LINK_MAXOPS (APFS_CREATE_DENTRY_REC_MAXOPS + \
|
||||
APFS_UPDATE_INODE_MAXOPS())
|
||||
|
||||
/**
|
||||
* apfs_delete_orphan_link - Delete the link for an orphan inode
|
||||
@@ -1102,6 +1132,10 @@ fail:
|
||||
kfree(qname.name);
|
||||
return err;
|
||||
}
|
||||
int APFS_DELETE_ORPHAN_LINK_MAXOPS(void)
|
||||
{
|
||||
return 1 + APFS_UPDATE_INODE_MAXOPS();
|
||||
}
|
||||
|
||||
/**
|
||||
* __apfs_undo_unlink - Clean up __apfs_unlink()
|
||||
@@ -1159,13 +1193,20 @@ fail:
|
||||
__apfs_undo_unlink(dentry);
|
||||
return err;
|
||||
}
|
||||
#define __APFS_UNLINK_MAXOPS (APFS_DELETE_DENTRY_MAXOPS + \
|
||||
APFS_CREATE_ORPHAN_LINK_MAXOPS + \
|
||||
APFS_UPDATE_INODE_MAXOPS())
|
||||
|
||||
int apfs_unlink(struct inode *dir, struct dentry *dentry)
|
||||
{
|
||||
struct super_block *sb = dir->i_sb;
|
||||
struct apfs_max_ops maxops;
|
||||
int err;
|
||||
|
||||
err = apfs_transaction_start(sb);
|
||||
maxops.cat = __APFS_UNLINK_MAXOPS;
|
||||
maxops.blks = 0;
|
||||
|
||||
err = apfs_transaction_start(sb, maxops);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
@@ -1201,12 +1242,18 @@ int apfs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||
struct super_block *sb = old_dir->i_sb;
|
||||
struct inode *old_inode = d_inode(old_dentry);
|
||||
struct inode *new_inode = d_inode(new_dentry);
|
||||
struct apfs_max_ops maxops;
|
||||
int err;
|
||||
|
||||
if (flags & ~RENAME_NOREPLACE) /* TODO: support RENAME_EXCHANGE */
|
||||
return -EINVAL;
|
||||
|
||||
err = apfs_transaction_start(sb);
|
||||
maxops.cat = __APFS_UNLINK_MAXOPS + __APFS_LINK_MAXOPS;
|
||||
if (new_inode)
|
||||
maxops.cat += __APFS_UNLINK_MAXOPS;
|
||||
maxops.blks = 0;
|
||||
|
||||
err = apfs_transaction_start(sb, maxops);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
|
||||
@@ -231,6 +231,7 @@ fail:
|
||||
apfs_free_query(sb, query);
|
||||
return ret;
|
||||
}
|
||||
#define APFS_UPDATE_EXTENTS_MAXOPS (1 + 2 * APFS_CRYPTO_ADJ_REFCNT_MAXOPS())
|
||||
|
||||
/**
|
||||
* apfs_update_phys_extent - Create or update the physical record for an extent
|
||||
@@ -396,6 +397,7 @@ int apfs_flush_extent_cache(struct inode *inode)
|
||||
ai->i_extent_dirty = false;
|
||||
return 0;
|
||||
}
|
||||
#define APFS_FLUSH_EXTENT_CACHE APFS_UPDATE_EXTENTS_MAXOPS
|
||||
|
||||
int apfs_get_new_block(struct inode *inode, sector_t iblock,
|
||||
struct buffer_head *bh_result, int create)
|
||||
@@ -441,3 +443,7 @@ int apfs_get_new_block(struct inode *inode, sector_t iblock,
|
||||
ai->i_extent_dirty = true;
|
||||
return 0;
|
||||
}
|
||||
int APFS_GET_NEW_BLOCK_MAXOPS(void)
|
||||
{
|
||||
return APFS_FLUSH_EXTENT_CACHE;
|
||||
}
|
||||
|
||||
@@ -73,6 +73,7 @@ out:
|
||||
apfs_free_query(sb, query);
|
||||
return ret;
|
||||
}
|
||||
#define APFS_CREATE_DSTREAM_REC_MAXOPS 1
|
||||
|
||||
/**
|
||||
* apfs_create_crypto_rec - Create the crypto state record for an inode
|
||||
@@ -128,6 +129,7 @@ out:
|
||||
apfs_free_query(sb, query);
|
||||
return ret;
|
||||
}
|
||||
#define APFS_CREATE_CRYPTO_REC_MAXOPS 1
|
||||
|
||||
/**
|
||||
* apfs_dflt_key_class - Returns default key class for files in volume
|
||||
@@ -188,6 +190,10 @@ out:
|
||||
apfs_free_query(sb, query);
|
||||
return ret;
|
||||
}
|
||||
int APFS_CRYPTO_ADJ_REFCNT_MAXOPS(void)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* apfs_crypto_set_key - Modify content of crypto state record
|
||||
@@ -235,6 +241,7 @@ out:
|
||||
apfs_free_query(sb, query);
|
||||
return ret;
|
||||
}
|
||||
#define APFS_CRYPTO_SET_KEY_MAXOPS 1
|
||||
|
||||
/**
|
||||
* apfs_crypto_get_key - Retrieve content of crypto state record
|
||||
@@ -294,9 +301,17 @@ static int apfs_write_begin(struct file *file, struct address_space *mapping,
|
||||
unsigned int blocksize, block_start, block_end, from, to;
|
||||
pgoff_t index = pos >> PAGE_SHIFT;
|
||||
sector_t iblock = (sector_t)index << (PAGE_SHIFT - inode->i_blkbits);
|
||||
int blkcount = (len + sb->s_blocksize - 1) >> inode->i_blkbits;
|
||||
struct apfs_max_ops maxops;
|
||||
int err;
|
||||
|
||||
err = apfs_transaction_start(sb);
|
||||
maxops.cat = APFS_CREATE_DSTREAM_REC_MAXOPS +
|
||||
APFS_CREATE_CRYPTO_REC_MAXOPS +
|
||||
APFS_UPDATE_INODE_MAXOPS() +
|
||||
blkcount * APFS_GET_NEW_BLOCK_MAXOPS();
|
||||
maxops.blks = blkcount;
|
||||
|
||||
err = apfs_transaction_start(sb, maxops);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
@@ -806,6 +821,7 @@ fail:
|
||||
kfree(new_val);
|
||||
return err;
|
||||
}
|
||||
#define APFS_INODE_RENAME_MAXOPS 1
|
||||
|
||||
/**
|
||||
* apfs_create_dstream_xfield - Create the inode xfield for a new data stream
|
||||
@@ -860,6 +876,7 @@ fail:
|
||||
kfree(new_val);
|
||||
return err;
|
||||
}
|
||||
#define APFS_CREATE_DSTREAM_XFIELD_MAXOPS 1
|
||||
|
||||
/**
|
||||
* apfs_inode_resize - Update the sizes reported in an inode record
|
||||
@@ -903,6 +920,7 @@ static int apfs_inode_resize(struct inode *inode, struct apfs_query *query)
|
||||
/* This inode has no dstream xfield, so we need to create it */
|
||||
return apfs_create_dstream_xfield(inode, query);
|
||||
}
|
||||
#define APFS_INODE_RESIZE_MAXOPS (1 + APFS_CREATE_DSTREAM_XFIELD_MAXOPS)
|
||||
|
||||
/**
|
||||
* apfs_update_inode - Update an existing inode record
|
||||
@@ -973,6 +991,10 @@ fail:
|
||||
apfs_free_query(sb, query);
|
||||
return err;
|
||||
}
|
||||
int APFS_UPDATE_INODE_MAXOPS(void)
|
||||
{
|
||||
return APFS_INODE_RENAME_MAXOPS + APFS_INODE_RESIZE_MAXOPS + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* apfs_delete_inode - Delete an inode record and update the volume file count
|
||||
@@ -1013,15 +1035,20 @@ static int apfs_delete_inode(struct inode *inode)
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
#define APFS_DELETE_INODE_MAXOPS 1
|
||||
|
||||
void apfs_evict_inode(struct inode *inode)
|
||||
{
|
||||
struct super_block *sb = inode->i_sb;
|
||||
struct apfs_max_ops maxops;
|
||||
|
||||
if (is_bad_inode(inode) || inode->i_nlink)
|
||||
goto out_clear;
|
||||
|
||||
if (apfs_transaction_start(sb))
|
||||
maxops.cat = APFS_DELETE_INODE_MAXOPS + APFS_DELETE_ORPHAN_LINK_MAXOPS();
|
||||
maxops.blks = 0;
|
||||
|
||||
if (apfs_transaction_start(sb, maxops))
|
||||
goto out_report;
|
||||
if (apfs_delete_inode(inode))
|
||||
goto out_abort;
|
||||
@@ -1169,11 +1196,16 @@ fail:
|
||||
apfs_free_query(sb, query);
|
||||
return ret;
|
||||
}
|
||||
int APFS_CREATE_INODE_REC_MAXOPS(void)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
int apfs_setattr(struct dentry *dentry, struct iattr *iattr)
|
||||
{
|
||||
struct inode *inode = d_inode(dentry);
|
||||
struct super_block *sb = inode->i_sb;
|
||||
struct apfs_max_ops maxops;
|
||||
int err;
|
||||
|
||||
if (iattr->ia_valid & ATTR_SIZE && iattr->ia_size != inode->i_size)
|
||||
@@ -1182,8 +1214,11 @@ int apfs_setattr(struct dentry *dentry, struct iattr *iattr)
|
||||
if(err)
|
||||
return err;
|
||||
|
||||
maxops.cat = APFS_UPDATE_INODE_MAXOPS();
|
||||
maxops.blks = 0;
|
||||
|
||||
/* TODO: figure out why ->write_inode() isn't firing */
|
||||
err = apfs_transaction_start(sb);
|
||||
err = apfs_transaction_start(sb, maxops);
|
||||
if (err)
|
||||
return err;
|
||||
err = apfs_update_inode(inode, NULL /* new_name */);
|
||||
@@ -1239,6 +1274,7 @@ static int apfs_ioc_set_dir_class(struct file *file, u32 __user *user_class)
|
||||
struct inode *inode = file_inode(file);
|
||||
struct apfs_inode_info *ai = APFS_I(inode);
|
||||
struct super_block *sb = inode->i_sb;
|
||||
struct apfs_max_ops maxops;
|
||||
u32 class;
|
||||
int err;
|
||||
|
||||
@@ -1247,7 +1283,10 @@ static int apfs_ioc_set_dir_class(struct file *file, u32 __user *user_class)
|
||||
|
||||
ai->i_key_class = class;
|
||||
|
||||
err = apfs_transaction_start(sb);
|
||||
maxops.cat = APFS_UPDATE_INODE_MAXOPS();
|
||||
maxops.blks = 0;
|
||||
|
||||
err = apfs_transaction_start(sb, maxops);
|
||||
if (err)
|
||||
return err;
|
||||
err = apfs_update_inode(inode, NULL /* new_name */);
|
||||
@@ -1270,6 +1309,7 @@ static int apfs_ioc_set_pfk(struct file *file, void __user *user_pfk)
|
||||
struct apfs_wrapped_crypto_state pfk_hdr;
|
||||
struct apfs_crypto_state_val *pfk;
|
||||
struct apfs_inode_info *ai = APFS_I(inode);
|
||||
struct apfs_max_ops maxops;
|
||||
unsigned key_len, key_class;
|
||||
int err;
|
||||
|
||||
@@ -1287,7 +1327,10 @@ static int apfs_ioc_set_pfk(struct file *file, void __user *user_pfk)
|
||||
}
|
||||
pfk->refcnt = cpu_to_le32(1);
|
||||
|
||||
err = apfs_transaction_start(sb);
|
||||
maxops.cat = APFS_CRYPTO_SET_KEY_MAXOPS + APFS_UPDATE_INODE_MAXOPS();
|
||||
maxops.blks = 0;
|
||||
|
||||
err = apfs_transaction_start(sb, maxops);
|
||||
if (err) {
|
||||
kfree(pfk);
|
||||
return err;
|
||||
|
||||
@@ -524,8 +524,9 @@ static void apfs_put_super(struct super_block *sb)
|
||||
if (!(sb->s_flags & SB_RDONLY)) {
|
||||
struct apfs_superblock *vsb_raw;
|
||||
struct buffer_head *vsb_bh;
|
||||
struct apfs_max_ops maxops = {0};
|
||||
|
||||
if (apfs_transaction_start(sb))
|
||||
if (apfs_transaction_start(sb, maxops))
|
||||
goto fail;
|
||||
vsb_raw = sbi->s_vsb_raw;
|
||||
vsb_bh = sbi->s_vobject.bh;
|
||||
@@ -617,9 +618,13 @@ static int __init init_inodecache(void)
|
||||
static int apfs_write_inode(struct inode *inode, struct writeback_control *wbc)
|
||||
{
|
||||
struct super_block *sb = inode->i_sb;
|
||||
struct apfs_max_ops maxops;
|
||||
int err;
|
||||
|
||||
err = apfs_transaction_start(sb);
|
||||
maxops.cat = APFS_UPDATE_INODE_MAXOPS();
|
||||
maxops.blks = 0;
|
||||
|
||||
err = apfs_transaction_start(sb, maxops);
|
||||
if (err)
|
||||
return err;
|
||||
err = apfs_update_inode(inode, NULL /* new_name */);
|
||||
|
||||
+61
-16
@@ -357,14 +357,51 @@ static int apfs_checkpoint_end(struct super_block *sb)
|
||||
return sync_dirty_buffer(bh);
|
||||
}
|
||||
|
||||
/**
|
||||
* apfs_transaction_has_room - Is there enough free space for this transaction?
|
||||
* @sb: superblock structure
|
||||
* @maxops: maximum operations expected
|
||||
*/
|
||||
static bool apfs_transaction_has_room(struct super_block *sb, struct apfs_max_ops maxops)
|
||||
{
|
||||
u64 max_cat_blks, max_omap_blks, max_extref_blks, max_blks;
|
||||
/* I don't know the actual maximum heights, just guessing */
|
||||
const u64 max_cat_height = 8, max_omap_height = 3, max_extref_height = 3;
|
||||
|
||||
/*
|
||||
* On the worst possible case (a tree of max_height), each new insertion
|
||||
* to the catalog may both cow and split every node up to the root. The
|
||||
* root though, is only cowed once.
|
||||
*/
|
||||
max_cat_blks = 1 + 2 * maxops.cat * max_cat_height;
|
||||
|
||||
/*
|
||||
* Any new catalog node could require a new entry in the object map,
|
||||
* because the original might belong to a snapshot.
|
||||
*/
|
||||
max_omap_blks = 1 + 2 * max_cat_blks * max_omap_height;
|
||||
|
||||
/* The extent reference tree needs a maximum of one record per block */
|
||||
max_extref_blks = 1 + 2 * maxops.blks * max_extref_height;
|
||||
|
||||
/*
|
||||
* Ephemeral allocations shouldn't fail, and neither should those in the
|
||||
* internal pool. So just add the actual file blocks and we are done.
|
||||
*/
|
||||
max_blks = max_cat_blks + max_omap_blks + max_extref_blks + maxops.blks;
|
||||
|
||||
return max_blks < APFS_SM(sb)->sm_free_count;
|
||||
}
|
||||
|
||||
/**
|
||||
* apfs_transaction_start - Begin a new transaction
|
||||
* @sb: superblock structure
|
||||
* @sb: superblock structure
|
||||
* @maxops: maximum operations expected
|
||||
*
|
||||
* Also locks the filesystem for writing; returns 0 on success or a negative
|
||||
* error code in case of failure.
|
||||
*/
|
||||
int apfs_transaction_start(struct super_block *sb)
|
||||
int apfs_transaction_start(struct super_block *sb, struct apfs_max_ops maxops)
|
||||
{
|
||||
struct apfs_sb_info *sbi = APFS_SB(sb);
|
||||
struct apfs_nxsb_info *nxi = APFS_NXI(sb);
|
||||
@@ -404,6 +441,14 @@ int apfs_transaction_start(struct super_block *sb)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Don't start transactions unless we are sure they fit in disk */
|
||||
if (!apfs_transaction_has_room(sb, maxops)) {
|
||||
/* Commit what we have so far to flush the queues */
|
||||
nx_trans->force_commit = true;
|
||||
err = apfs_transaction_commit(sb);
|
||||
return err ? err : -ENOSPC;
|
||||
}
|
||||
|
||||
if (!vol_trans->t_old_vsb) {
|
||||
vol_trans->t_old_vsb = sbi->s_vobject.bh;
|
||||
get_bh(vol_trans->t_old_vsb);
|
||||
@@ -474,10 +519,10 @@ static int apfs_transaction_commit_nx(struct super_block *sb)
|
||||
kfree(bhi);
|
||||
}
|
||||
if (err)
|
||||
goto fail;
|
||||
return err;
|
||||
err = apfs_checkpoint_end(sb);
|
||||
if (err)
|
||||
goto fail;
|
||||
return err;
|
||||
|
||||
/* Success: forget the old container and volume superblocks */
|
||||
brelse(nx_trans->t_old_msb);
|
||||
@@ -501,16 +546,6 @@ static int apfs_transaction_commit_nx(struct super_block *sb)
|
||||
brelse(APFS_SM(sb)->sm_ip);
|
||||
APFS_SM(sb)->sm_ip = NULL;
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
/*
|
||||
* This won't happen on ENOSPC, so it should be rare. Set the
|
||||
* filesystem read-only to simplify cleanup for the callers and
|
||||
* avoid deciding if the transaction was completed.
|
||||
*/
|
||||
apfs_info(sb, "transaction commit failed, forcing read-only");
|
||||
sb->s_flags |= SB_RDONLY;
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -566,8 +601,14 @@ int apfs_transaction_commit(struct super_block *sb)
|
||||
struct apfs_nxsb_info *nxi = APFS_NXI(sb);
|
||||
int err = 0;
|
||||
|
||||
if(apfs_transaction_need_commit(sb))
|
||||
if (apfs_transaction_need_commit(sb)) {
|
||||
err = apfs_transaction_commit_nx(sb);
|
||||
if (err) {
|
||||
apfs_warn(sb, "transaction commit failed");
|
||||
apfs_transaction_abort(sb);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&nxs_mutex);
|
||||
up_write(&nxi->nx_big_sem);
|
||||
@@ -612,7 +653,7 @@ int apfs_transaction_join(struct super_block *sb, struct buffer_head *bh)
|
||||
*
|
||||
* Releases the big filesystem lock and clears the in-memory transaction data;
|
||||
* the on-disk changes are irrelevant because the superblock checksum hasn't
|
||||
* been written yet.
|
||||
* been written yet. Leaves the filesystem in read-only state.
|
||||
*/
|
||||
void apfs_transaction_abort(struct super_block *sb)
|
||||
{
|
||||
@@ -623,6 +664,7 @@ void apfs_transaction_abort(struct super_block *sb)
|
||||
|
||||
ASSERT(nx_trans->t_old_msb);
|
||||
nx_trans->force_commit = false;
|
||||
apfs_warn(sb, "aborting transaction");
|
||||
|
||||
--nxi->nx_xid;
|
||||
list_for_each_entry_safe(bhi, tmp, &nx_trans->t_buffers, list) {
|
||||
@@ -670,6 +712,9 @@ void apfs_transaction_abort(struct super_block *sb)
|
||||
brelse(APFS_SM(sb)->sm_ip);
|
||||
APFS_SM(sb)->sm_ip = NULL;
|
||||
|
||||
/* Set the filesystem read-only to simplify cleanup for the callers */
|
||||
sb->s_flags |= SB_RDONLY; /* TODO: the other volumes */
|
||||
|
||||
mutex_unlock(&nxs_mutex);
|
||||
up_write(&nxi->nx_big_sem);
|
||||
}
|
||||
|
||||
@@ -418,6 +418,10 @@ done:
|
||||
apfs_free_query(sb, query);
|
||||
return ret;
|
||||
}
|
||||
int APFS_XATTR_SET_MAXOPS(void)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int apfs_xattr_osx_set(const struct xattr_handler *handler,
|
||||
struct dentry *unused, struct inode *inode,
|
||||
@@ -425,9 +429,13 @@ static int apfs_xattr_osx_set(const struct xattr_handler *handler,
|
||||
size_t size, int flags)
|
||||
{
|
||||
struct super_block *sb = inode->i_sb;
|
||||
struct apfs_max_ops maxops;
|
||||
int err;
|
||||
|
||||
err = apfs_transaction_start(sb);
|
||||
maxops.cat = APFS_XATTR_SET_MAXOPS();
|
||||
maxops.blks = 0;
|
||||
|
||||
err = apfs_transaction_start(sb, maxops);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user