-----BEGIN PGP SIGNATURE----- iQEzBAABCAAdFiEEq1nRK9aeMoq1VSgcnJ2qBz9kQNkFAmXx5kwACgkQnJ2qBz9k QNmZowf/UlGJ1rmQFFhoodn3SyK48tQjOZ23Ygx6v9FZiLMuQ3b1k0kWKmwM4lZb mtRriCm+lPO9Yp/Sflz+jn8S51b/2bcTXiPV4w2Y4ZIun41wwggV7rWPnTCHhu94 rGEPu/SNSBdpxWGv43BKHSDl4XolsGbyusQKBbKZtftnrpIf0y2OnyEXSV91Vnlh KM/XxzacBD4/3r4KCljyEkORWlIIn2+gdZf58sKtxLKvnfCIxjB+BF1e0gOWgmNQ e/pVnzbAHO3wuavRlwnrtA+ekBYQiJq7T61yyYI8zpeSoLHmwvPoKSsZP+q4BTvV yrcVCbGp3uZlXHD93U3BOfdqS0xBmg== =84Q4 -----END PGP SIGNATURE----- Merge tag 'fs_for_v6.9-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/jack/linux-fs Pull ext2, isofs, udf, and quota updates from Jan Kara: "A lot of material this time: - removal of a lot of GFP_NOFS usage from ext2, udf, quota (either it was legacy or replaced with scoped memalloc_nofs_*() API) - removal of BUG_ONs in quota code - conversion of UDF to the new mount API - tightening quota on disk format verification - fix some potentially unsafe use of RCU pointers in quota code and annotate everything properly to make sparse happy - a few other small quota, ext2, udf, and isofs fixes" * tag 'fs_for_v6.9-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/jack/linux-fs: (26 commits) udf: remove SLAB_MEM_SPREAD flag usage quota: remove SLAB_MEM_SPREAD flag usage isofs: remove SLAB_MEM_SPREAD flag usage ext2: remove SLAB_MEM_SPREAD flag usage ext2: mark as deprecated udf: convert to new mount API udf: convert novrs to an option flag MAINTAINERS: add missing git address for ext2 entry quota: Detect loops in quota tree quota: Properly annotate i_dquot arrays with __rcu quota: Fix rcu annotations of inode dquot pointers isofs: handle CDs with bad root inode but good Joliet root directory udf: Avoid invalid LVID used on mount quota: Fix potential NULL pointer dereference quota: Drop GFP_NOFS instances under dquot->dq_lock and dqio_sem quota: Set nofs allocation context when acquiring dqio_sem ext2: Remove GFP_NOFS use in ext2_xattr_cache_insert() ext2: Drop GFP_NOFS use in ext2_get_blocks() ext2: Drop GFP_NOFS allocation from ext2_init_block_alloc_info() udf: Remove GFP_NOFS allocation in udf_expand_file_adinicb() ...
This commit is contained in:
commit
e5e038b7ae
|
@ -8021,6 +8021,7 @@ M: Jan Kara <jack@suse.com>
|
||||||
L: linux-ext4@vger.kernel.org
|
L: linux-ext4@vger.kernel.org
|
||||||
S: Maintained
|
S: Maintained
|
||||||
F: Documentation/filesystems/ext2.rst
|
F: Documentation/filesystems/ext2.rst
|
||||||
|
T: git git://git.kernel.org/pub/scm/linux/kernel/git/jack/linux-fs.git
|
||||||
F: fs/ext2/
|
F: fs/ext2/
|
||||||
F: include/linux/ext2*
|
F: include/linux/ext2*
|
||||||
|
|
||||||
|
|
|
@ -1,16 +1,23 @@
|
||||||
# SPDX-License-Identifier: GPL-2.0-only
|
# SPDX-License-Identifier: GPL-2.0-only
|
||||||
config EXT2_FS
|
config EXT2_FS
|
||||||
tristate "Second extended fs support"
|
tristate "Second extended fs support (DEPRECATED)"
|
||||||
select BUFFER_HEAD
|
select BUFFER_HEAD
|
||||||
select FS_IOMAP
|
select FS_IOMAP
|
||||||
select LEGACY_DIRECT_IO
|
select LEGACY_DIRECT_IO
|
||||||
help
|
help
|
||||||
Ext2 is a standard Linux file system for hard disks.
|
Ext2 is a standard Linux file system for hard disks.
|
||||||
|
|
||||||
To compile this file system support as a module, choose M here: the
|
This filesystem driver is deprecated because it does not properly
|
||||||
module will be called ext2.
|
support inode time stamps beyond 03:14:07 UTC on 19 January 2038.
|
||||||
|
|
||||||
If unsure, say Y.
|
Ext2 users are advised to use ext4 driver to access their filesystem.
|
||||||
|
The driver is fully compatible, supports filesystems without journal
|
||||||
|
or extents, and also supports larger time stamps if the filesystem
|
||||||
|
is created with at least 256 byte inodes.
|
||||||
|
|
||||||
|
This code is kept as a simple reference for filesystem developers.
|
||||||
|
|
||||||
|
If unsure, say N.
|
||||||
|
|
||||||
config EXT2_FS_XATTR
|
config EXT2_FS_XATTR
|
||||||
bool "Ext2 extended attributes"
|
bool "Ext2 extended attributes"
|
||||||
|
|
|
@ -412,7 +412,7 @@ void ext2_init_block_alloc_info(struct inode *inode)
|
||||||
struct ext2_block_alloc_info *block_i;
|
struct ext2_block_alloc_info *block_i;
|
||||||
struct super_block *sb = inode->i_sb;
|
struct super_block *sb = inode->i_sb;
|
||||||
|
|
||||||
block_i = kmalloc(sizeof(*block_i), GFP_NOFS);
|
block_i = kmalloc(sizeof(*block_i), GFP_KERNEL);
|
||||||
if (block_i) {
|
if (block_i) {
|
||||||
struct ext2_reserve_window_node *rsv = &block_i->rsv_window_node;
|
struct ext2_reserve_window_node *rsv = &block_i->rsv_window_node;
|
||||||
|
|
||||||
|
|
|
@ -674,7 +674,7 @@ struct ext2_inode_info {
|
||||||
struct inode vfs_inode;
|
struct inode vfs_inode;
|
||||||
struct list_head i_orphan; /* unlinked but open inodes */
|
struct list_head i_orphan; /* unlinked but open inodes */
|
||||||
#ifdef CONFIG_QUOTA
|
#ifdef CONFIG_QUOTA
|
||||||
struct dquot *i_dquot[MAXQUOTAS];
|
struct dquot __rcu *i_dquot[MAXQUOTAS];
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -754,7 +754,7 @@ static int ext2_get_blocks(struct inode *inode,
|
||||||
*/
|
*/
|
||||||
err = sb_issue_zeroout(inode->i_sb,
|
err = sb_issue_zeroout(inode->i_sb,
|
||||||
le32_to_cpu(chain[depth-1].key), count,
|
le32_to_cpu(chain[depth-1].key), count,
|
||||||
GFP_NOFS);
|
GFP_KERNEL);
|
||||||
if (err) {
|
if (err) {
|
||||||
mutex_unlock(&ei->truncate_mutex);
|
mutex_unlock(&ei->truncate_mutex);
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
|
|
@ -319,7 +319,7 @@ static ssize_t ext2_quota_read(struct super_block *sb, int type, char *data, siz
|
||||||
static ssize_t ext2_quota_write(struct super_block *sb, int type, const char *data, size_t len, loff_t off);
|
static ssize_t ext2_quota_write(struct super_block *sb, int type, const char *data, size_t len, loff_t off);
|
||||||
static int ext2_quota_on(struct super_block *sb, int type, int format_id,
|
static int ext2_quota_on(struct super_block *sb, int type, int format_id,
|
||||||
const struct path *path);
|
const struct path *path);
|
||||||
static struct dquot **ext2_get_dquots(struct inode *inode)
|
static struct dquot __rcu **ext2_get_dquots(struct inode *inode)
|
||||||
{
|
{
|
||||||
return EXT2_I(inode)->i_dquot;
|
return EXT2_I(inode)->i_dquot;
|
||||||
}
|
}
|
||||||
|
|
|
@ -874,7 +874,7 @@ ext2_xattr_cache_insert(struct mb_cache *cache, struct buffer_head *bh)
|
||||||
__u32 hash = le32_to_cpu(HDR(bh)->h_hash);
|
__u32 hash = le32_to_cpu(HDR(bh)->h_hash);
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
error = mb_cache_entry_create(cache, GFP_NOFS, hash, bh->b_blocknr,
|
error = mb_cache_entry_create(cache, GFP_KERNEL, hash, bh->b_blocknr,
|
||||||
true);
|
true);
|
||||||
if (error) {
|
if (error) {
|
||||||
if (error == -EBUSY) {
|
if (error == -EBUSY) {
|
||||||
|
|
|
@ -1158,7 +1158,7 @@ struct ext4_inode_info {
|
||||||
tid_t i_datasync_tid;
|
tid_t i_datasync_tid;
|
||||||
|
|
||||||
#ifdef CONFIG_QUOTA
|
#ifdef CONFIG_QUOTA
|
||||||
struct dquot *i_dquot[MAXQUOTAS];
|
struct dquot __rcu *i_dquot[MAXQUOTAS];
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Precomputed uuid+inum+igen checksum for seeding inode checksums */
|
/* Precomputed uuid+inum+igen checksum for seeding inode checksums */
|
||||||
|
|
|
@ -1599,7 +1599,7 @@ static ssize_t ext4_quota_write(struct super_block *sb, int type,
|
||||||
static int ext4_quota_enable(struct super_block *sb, int type, int format_id,
|
static int ext4_quota_enable(struct super_block *sb, int type, int format_id,
|
||||||
unsigned int flags);
|
unsigned int flags);
|
||||||
|
|
||||||
static struct dquot **ext4_get_dquots(struct inode *inode)
|
static struct dquot __rcu **ext4_get_dquots(struct inode *inode)
|
||||||
{
|
{
|
||||||
return EXT4_I(inode)->i_dquot;
|
return EXT4_I(inode)->i_dquot;
|
||||||
}
|
}
|
||||||
|
|
|
@ -830,7 +830,7 @@ struct f2fs_inode_info {
|
||||||
spinlock_t i_size_lock; /* protect last_disk_size */
|
spinlock_t i_size_lock; /* protect last_disk_size */
|
||||||
|
|
||||||
#ifdef CONFIG_QUOTA
|
#ifdef CONFIG_QUOTA
|
||||||
struct dquot *i_dquot[MAXQUOTAS];
|
struct dquot __rcu *i_dquot[MAXQUOTAS];
|
||||||
|
|
||||||
/* quota space reservation, managed internally by quota code */
|
/* quota space reservation, managed internally by quota code */
|
||||||
qsize_t i_reserved_quota;
|
qsize_t i_reserved_quota;
|
||||||
|
|
|
@ -2768,7 +2768,7 @@ int f2fs_dquot_initialize(struct inode *inode)
|
||||||
return dquot_initialize(inode);
|
return dquot_initialize(inode);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct dquot **f2fs_get_dquots(struct inode *inode)
|
static struct dquot __rcu **f2fs_get_dquots(struct inode *inode)
|
||||||
{
|
{
|
||||||
return F2FS_I(inode)->i_dquot;
|
return F2FS_I(inode)->i_dquot;
|
||||||
}
|
}
|
||||||
|
|
|
@ -908,8 +908,22 @@ root_found:
|
||||||
* we then decide whether to use the Joliet descriptor.
|
* we then decide whether to use the Joliet descriptor.
|
||||||
*/
|
*/
|
||||||
inode = isofs_iget(s, sbi->s_firstdatazone, 0);
|
inode = isofs_iget(s, sbi->s_firstdatazone, 0);
|
||||||
if (IS_ERR(inode))
|
|
||||||
goto out_no_root;
|
/*
|
||||||
|
* Fix for broken CDs with a corrupt root inode but a correct Joliet
|
||||||
|
* root directory.
|
||||||
|
*/
|
||||||
|
if (IS_ERR(inode)) {
|
||||||
|
if (joliet_level && sbi->s_firstdatazone != first_data_zone) {
|
||||||
|
printk(KERN_NOTICE
|
||||||
|
"ISOFS: root inode is unusable. "
|
||||||
|
"Disabling Rock Ridge and switching to Joliet.");
|
||||||
|
sbi->s_rock = 0;
|
||||||
|
inode = NULL;
|
||||||
|
} else {
|
||||||
|
goto out_no_root;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Fix for broken CDs with Rock Ridge and empty ISO root directory but
|
* Fix for broken CDs with Rock Ridge and empty ISO root directory but
|
||||||
|
|
|
@ -92,7 +92,7 @@ struct jfs_inode_info {
|
||||||
} link;
|
} link;
|
||||||
} u;
|
} u;
|
||||||
#ifdef CONFIG_QUOTA
|
#ifdef CONFIG_QUOTA
|
||||||
struct dquot *i_dquot[MAXQUOTAS];
|
struct dquot __rcu *i_dquot[MAXQUOTAS];
|
||||||
#endif
|
#endif
|
||||||
u32 dev; /* will die when we get wide dev_t */
|
u32 dev; /* will die when we get wide dev_t */
|
||||||
struct inode vfs_inode;
|
struct inode vfs_inode;
|
||||||
|
|
|
@ -824,7 +824,7 @@ out:
|
||||||
return len - towrite;
|
return len - towrite;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct dquot **jfs_get_dquots(struct inode *inode)
|
static struct dquot __rcu **jfs_get_dquots(struct inode *inode)
|
||||||
{
|
{
|
||||||
return JFS_IP(inode)->i_dquot;
|
return JFS_IP(inode)->i_dquot;
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,7 +65,7 @@ struct ocfs2_inode_info
|
||||||
tid_t i_sync_tid;
|
tid_t i_sync_tid;
|
||||||
tid_t i_datasync_tid;
|
tid_t i_datasync_tid;
|
||||||
|
|
||||||
struct dquot *i_dquot[MAXQUOTAS];
|
struct dquot __rcu *i_dquot[MAXQUOTAS];
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -447,14 +447,17 @@ int ocfs2_global_write_info(struct super_block *sb, int type)
|
||||||
int err;
|
int err;
|
||||||
struct quota_info *dqopt = sb_dqopt(sb);
|
struct quota_info *dqopt = sb_dqopt(sb);
|
||||||
struct ocfs2_mem_dqinfo *info = dqopt->info[type].dqi_priv;
|
struct ocfs2_mem_dqinfo *info = dqopt->info[type].dqi_priv;
|
||||||
|
unsigned int memalloc;
|
||||||
|
|
||||||
down_write(&dqopt->dqio_sem);
|
down_write(&dqopt->dqio_sem);
|
||||||
|
memalloc = memalloc_nofs_save();
|
||||||
err = ocfs2_qinfo_lock(info, 1);
|
err = ocfs2_qinfo_lock(info, 1);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
goto out_sem;
|
goto out_sem;
|
||||||
err = __ocfs2_global_write_info(sb, type);
|
err = __ocfs2_global_write_info(sb, type);
|
||||||
ocfs2_qinfo_unlock(info, 1);
|
ocfs2_qinfo_unlock(info, 1);
|
||||||
out_sem:
|
out_sem:
|
||||||
|
memalloc_nofs_restore(memalloc);
|
||||||
up_write(&dqopt->dqio_sem);
|
up_write(&dqopt->dqio_sem);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
@ -601,6 +604,7 @@ static int ocfs2_sync_dquot_helper(struct dquot *dquot, unsigned long type)
|
||||||
struct ocfs2_mem_dqinfo *oinfo = sb_dqinfo(sb, type)->dqi_priv;
|
struct ocfs2_mem_dqinfo *oinfo = sb_dqinfo(sb, type)->dqi_priv;
|
||||||
struct ocfs2_super *osb = OCFS2_SB(sb);
|
struct ocfs2_super *osb = OCFS2_SB(sb);
|
||||||
int status = 0;
|
int status = 0;
|
||||||
|
unsigned int memalloc;
|
||||||
|
|
||||||
trace_ocfs2_sync_dquot_helper(from_kqid(&init_user_ns, dquot->dq_id),
|
trace_ocfs2_sync_dquot_helper(from_kqid(&init_user_ns, dquot->dq_id),
|
||||||
dquot->dq_id.type,
|
dquot->dq_id.type,
|
||||||
|
@ -618,6 +622,7 @@ static int ocfs2_sync_dquot_helper(struct dquot *dquot, unsigned long type)
|
||||||
goto out_ilock;
|
goto out_ilock;
|
||||||
}
|
}
|
||||||
down_write(&sb_dqopt(sb)->dqio_sem);
|
down_write(&sb_dqopt(sb)->dqio_sem);
|
||||||
|
memalloc = memalloc_nofs_save();
|
||||||
status = ocfs2_sync_dquot(dquot);
|
status = ocfs2_sync_dquot(dquot);
|
||||||
if (status < 0)
|
if (status < 0)
|
||||||
mlog_errno(status);
|
mlog_errno(status);
|
||||||
|
@ -625,6 +630,7 @@ static int ocfs2_sync_dquot_helper(struct dquot *dquot, unsigned long type)
|
||||||
status = ocfs2_local_write_dquot(dquot);
|
status = ocfs2_local_write_dquot(dquot);
|
||||||
if (status < 0)
|
if (status < 0)
|
||||||
mlog_errno(status);
|
mlog_errno(status);
|
||||||
|
memalloc_nofs_restore(memalloc);
|
||||||
up_write(&sb_dqopt(sb)->dqio_sem);
|
up_write(&sb_dqopt(sb)->dqio_sem);
|
||||||
ocfs2_commit_trans(osb, handle);
|
ocfs2_commit_trans(osb, handle);
|
||||||
out_ilock:
|
out_ilock:
|
||||||
|
@ -662,6 +668,7 @@ static int ocfs2_write_dquot(struct dquot *dquot)
|
||||||
handle_t *handle;
|
handle_t *handle;
|
||||||
struct ocfs2_super *osb = OCFS2_SB(dquot->dq_sb);
|
struct ocfs2_super *osb = OCFS2_SB(dquot->dq_sb);
|
||||||
int status = 0;
|
int status = 0;
|
||||||
|
unsigned int memalloc;
|
||||||
|
|
||||||
trace_ocfs2_write_dquot(from_kqid(&init_user_ns, dquot->dq_id),
|
trace_ocfs2_write_dquot(from_kqid(&init_user_ns, dquot->dq_id),
|
||||||
dquot->dq_id.type);
|
dquot->dq_id.type);
|
||||||
|
@ -673,7 +680,9 @@ static int ocfs2_write_dquot(struct dquot *dquot)
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
down_write(&sb_dqopt(dquot->dq_sb)->dqio_sem);
|
down_write(&sb_dqopt(dquot->dq_sb)->dqio_sem);
|
||||||
|
memalloc = memalloc_nofs_save();
|
||||||
status = ocfs2_local_write_dquot(dquot);
|
status = ocfs2_local_write_dquot(dquot);
|
||||||
|
memalloc_nofs_restore(memalloc);
|
||||||
up_write(&sb_dqopt(dquot->dq_sb)->dqio_sem);
|
up_write(&sb_dqopt(dquot->dq_sb)->dqio_sem);
|
||||||
ocfs2_commit_trans(osb, handle);
|
ocfs2_commit_trans(osb, handle);
|
||||||
out:
|
out:
|
||||||
|
@ -920,6 +929,7 @@ static int ocfs2_mark_dquot_dirty(struct dquot *dquot)
|
||||||
struct ocfs2_mem_dqinfo *oinfo = sb_dqinfo(sb, type)->dqi_priv;
|
struct ocfs2_mem_dqinfo *oinfo = sb_dqinfo(sb, type)->dqi_priv;
|
||||||
handle_t *handle;
|
handle_t *handle;
|
||||||
struct ocfs2_super *osb = OCFS2_SB(sb);
|
struct ocfs2_super *osb = OCFS2_SB(sb);
|
||||||
|
unsigned int memalloc;
|
||||||
|
|
||||||
trace_ocfs2_mark_dquot_dirty(from_kqid(&init_user_ns, dquot->dq_id),
|
trace_ocfs2_mark_dquot_dirty(from_kqid(&init_user_ns, dquot->dq_id),
|
||||||
type);
|
type);
|
||||||
|
@ -946,6 +956,7 @@ static int ocfs2_mark_dquot_dirty(struct dquot *dquot)
|
||||||
goto out_ilock;
|
goto out_ilock;
|
||||||
}
|
}
|
||||||
down_write(&sb_dqopt(sb)->dqio_sem);
|
down_write(&sb_dqopt(sb)->dqio_sem);
|
||||||
|
memalloc = memalloc_nofs_save();
|
||||||
status = ocfs2_sync_dquot(dquot);
|
status = ocfs2_sync_dquot(dquot);
|
||||||
if (status < 0) {
|
if (status < 0) {
|
||||||
mlog_errno(status);
|
mlog_errno(status);
|
||||||
|
@ -954,6 +965,7 @@ static int ocfs2_mark_dquot_dirty(struct dquot *dquot)
|
||||||
/* Now write updated local dquot structure */
|
/* Now write updated local dquot structure */
|
||||||
status = ocfs2_local_write_dquot(dquot);
|
status = ocfs2_local_write_dquot(dquot);
|
||||||
out_dlock:
|
out_dlock:
|
||||||
|
memalloc_nofs_restore(memalloc);
|
||||||
up_write(&sb_dqopt(sb)->dqio_sem);
|
up_write(&sb_dqopt(sb)->dqio_sem);
|
||||||
ocfs2_commit_trans(osb, handle);
|
ocfs2_commit_trans(osb, handle);
|
||||||
out_ilock:
|
out_ilock:
|
||||||
|
|
|
@ -470,6 +470,7 @@ static int ocfs2_recover_local_quota_file(struct inode *lqinode,
|
||||||
int bit, chunk;
|
int bit, chunk;
|
||||||
struct ocfs2_recovery_chunk *rchunk, *next;
|
struct ocfs2_recovery_chunk *rchunk, *next;
|
||||||
qsize_t spacechange, inodechange;
|
qsize_t spacechange, inodechange;
|
||||||
|
unsigned int memalloc;
|
||||||
|
|
||||||
trace_ocfs2_recover_local_quota_file((unsigned long)lqinode->i_ino, type);
|
trace_ocfs2_recover_local_quota_file((unsigned long)lqinode->i_ino, type);
|
||||||
|
|
||||||
|
@ -521,6 +522,7 @@ static int ocfs2_recover_local_quota_file(struct inode *lqinode,
|
||||||
goto out_drop_lock;
|
goto out_drop_lock;
|
||||||
}
|
}
|
||||||
down_write(&sb_dqopt(sb)->dqio_sem);
|
down_write(&sb_dqopt(sb)->dqio_sem);
|
||||||
|
memalloc = memalloc_nofs_save();
|
||||||
spin_lock(&dquot->dq_dqb_lock);
|
spin_lock(&dquot->dq_dqb_lock);
|
||||||
/* Add usage from quota entry into quota changes
|
/* Add usage from quota entry into quota changes
|
||||||
* of our node. Auxiliary variables are important
|
* of our node. Auxiliary variables are important
|
||||||
|
@ -553,6 +555,7 @@ static int ocfs2_recover_local_quota_file(struct inode *lqinode,
|
||||||
unlock_buffer(qbh);
|
unlock_buffer(qbh);
|
||||||
ocfs2_journal_dirty(handle, qbh);
|
ocfs2_journal_dirty(handle, qbh);
|
||||||
out_commit:
|
out_commit:
|
||||||
|
memalloc_nofs_restore(memalloc);
|
||||||
up_write(&sb_dqopt(sb)->dqio_sem);
|
up_write(&sb_dqopt(sb)->dqio_sem);
|
||||||
ocfs2_commit_trans(OCFS2_SB(sb), handle);
|
ocfs2_commit_trans(OCFS2_SB(sb), handle);
|
||||||
out_drop_lock:
|
out_drop_lock:
|
||||||
|
|
|
@ -122,7 +122,7 @@ static int ocfs2_susp_quotas(struct ocfs2_super *osb, int unsuspend);
|
||||||
static int ocfs2_enable_quotas(struct ocfs2_super *osb);
|
static int ocfs2_enable_quotas(struct ocfs2_super *osb);
|
||||||
static void ocfs2_disable_quotas(struct ocfs2_super *osb);
|
static void ocfs2_disable_quotas(struct ocfs2_super *osb);
|
||||||
|
|
||||||
static struct dquot **ocfs2_get_dquots(struct inode *inode)
|
static struct dquot __rcu **ocfs2_get_dquots(struct inode *inode)
|
||||||
{
|
{
|
||||||
return OCFS2_I(inode)->i_dquot;
|
return OCFS2_I(inode)->i_dquot;
|
||||||
}
|
}
|
||||||
|
|
172
fs/quota/dquot.c
172
fs/quota/dquot.c
|
@ -399,15 +399,17 @@ int dquot_mark_dquot_dirty(struct dquot *dquot)
|
||||||
EXPORT_SYMBOL(dquot_mark_dquot_dirty);
|
EXPORT_SYMBOL(dquot_mark_dquot_dirty);
|
||||||
|
|
||||||
/* Dirtify all the dquots - this can block when journalling */
|
/* Dirtify all the dquots - this can block when journalling */
|
||||||
static inline int mark_all_dquot_dirty(struct dquot * const *dquot)
|
static inline int mark_all_dquot_dirty(struct dquot __rcu * const *dquots)
|
||||||
{
|
{
|
||||||
int ret, err, cnt;
|
int ret, err, cnt;
|
||||||
|
struct dquot *dquot;
|
||||||
|
|
||||||
ret = err = 0;
|
ret = err = 0;
|
||||||
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
|
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
|
||||||
if (dquot[cnt])
|
dquot = srcu_dereference(dquots[cnt], &dquot_srcu);
|
||||||
|
if (dquot)
|
||||||
/* Even in case of error we have to continue */
|
/* Even in case of error we have to continue */
|
||||||
ret = mark_dquot_dirty(dquot[cnt]);
|
ret = mark_dquot_dirty(dquot);
|
||||||
if (!err)
|
if (!err)
|
||||||
err = ret;
|
err = ret;
|
||||||
}
|
}
|
||||||
|
@ -875,10 +877,7 @@ void dqput(struct dquot *dquot)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Need to release dquot? */
|
/* Need to release dquot? */
|
||||||
#ifdef CONFIG_QUOTA_DEBUG
|
WARN_ON_ONCE(!list_empty(&dquot->dq_free));
|
||||||
/* sanity check */
|
|
||||||
BUG_ON(!list_empty(&dquot->dq_free));
|
|
||||||
#endif
|
|
||||||
put_releasing_dquots(dquot);
|
put_releasing_dquots(dquot);
|
||||||
atomic_dec(&dquot->dq_count);
|
atomic_dec(&dquot->dq_count);
|
||||||
spin_unlock(&dq_list_lock);
|
spin_unlock(&dq_list_lock);
|
||||||
|
@ -987,9 +986,8 @@ we_slept:
|
||||||
* smp_mb__before_atomic() in dquot_acquire().
|
* smp_mb__before_atomic() in dquot_acquire().
|
||||||
*/
|
*/
|
||||||
smp_rmb();
|
smp_rmb();
|
||||||
#ifdef CONFIG_QUOTA_DEBUG
|
/* Has somebody invalidated entry under us? */
|
||||||
BUG_ON(!dquot->dq_sb); /* Has somebody invalidated entry under us? */
|
WARN_ON_ONCE(hlist_unhashed(&dquot->dq_hash));
|
||||||
#endif
|
|
||||||
out:
|
out:
|
||||||
if (empty)
|
if (empty)
|
||||||
do_destroy_dquot(empty);
|
do_destroy_dquot(empty);
|
||||||
|
@ -998,14 +996,14 @@ out:
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(dqget);
|
EXPORT_SYMBOL(dqget);
|
||||||
|
|
||||||
static inline struct dquot **i_dquot(struct inode *inode)
|
static inline struct dquot __rcu **i_dquot(struct inode *inode)
|
||||||
{
|
{
|
||||||
return inode->i_sb->s_op->get_dquots(inode);
|
return inode->i_sb->s_op->get_dquots(inode);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int dqinit_needed(struct inode *inode, int type)
|
static int dqinit_needed(struct inode *inode, int type)
|
||||||
{
|
{
|
||||||
struct dquot * const *dquots;
|
struct dquot __rcu * const *dquots;
|
||||||
int cnt;
|
int cnt;
|
||||||
|
|
||||||
if (IS_NOQUOTA(inode))
|
if (IS_NOQUOTA(inode))
|
||||||
|
@ -1095,14 +1093,16 @@ static void remove_dquot_ref(struct super_block *sb, int type)
|
||||||
*/
|
*/
|
||||||
spin_lock(&dq_data_lock);
|
spin_lock(&dq_data_lock);
|
||||||
if (!IS_NOQUOTA(inode)) {
|
if (!IS_NOQUOTA(inode)) {
|
||||||
struct dquot **dquots = i_dquot(inode);
|
struct dquot __rcu **dquots = i_dquot(inode);
|
||||||
struct dquot *dquot = dquots[type];
|
struct dquot *dquot = srcu_dereference_check(
|
||||||
|
dquots[type], &dquot_srcu,
|
||||||
|
lockdep_is_held(&dq_data_lock));
|
||||||
|
|
||||||
#ifdef CONFIG_QUOTA_DEBUG
|
#ifdef CONFIG_QUOTA_DEBUG
|
||||||
if (unlikely(inode_get_rsv_space(inode) > 0))
|
if (unlikely(inode_get_rsv_space(inode) > 0))
|
||||||
reserved = 1;
|
reserved = 1;
|
||||||
#endif
|
#endif
|
||||||
dquots[type] = NULL;
|
rcu_assign_pointer(dquots[type], NULL);
|
||||||
if (dquot)
|
if (dquot)
|
||||||
dqput(dquot);
|
dqput(dquot);
|
||||||
}
|
}
|
||||||
|
@ -1455,7 +1455,8 @@ static int inode_quota_active(const struct inode *inode)
|
||||||
static int __dquot_initialize(struct inode *inode, int type)
|
static int __dquot_initialize(struct inode *inode, int type)
|
||||||
{
|
{
|
||||||
int cnt, init_needed = 0;
|
int cnt, init_needed = 0;
|
||||||
struct dquot **dquots, *got[MAXQUOTAS] = {};
|
struct dquot __rcu **dquots;
|
||||||
|
struct dquot *got[MAXQUOTAS] = {};
|
||||||
struct super_block *sb = inode->i_sb;
|
struct super_block *sb = inode->i_sb;
|
||||||
qsize_t rsv;
|
qsize_t rsv;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
@ -1530,7 +1531,7 @@ static int __dquot_initialize(struct inode *inode, int type)
|
||||||
if (!got[cnt])
|
if (!got[cnt])
|
||||||
continue;
|
continue;
|
||||||
if (!dquots[cnt]) {
|
if (!dquots[cnt]) {
|
||||||
dquots[cnt] = got[cnt];
|
rcu_assign_pointer(dquots[cnt], got[cnt]);
|
||||||
got[cnt] = NULL;
|
got[cnt] = NULL;
|
||||||
/*
|
/*
|
||||||
* Make quota reservation system happy if someone
|
* Make quota reservation system happy if someone
|
||||||
|
@ -1538,12 +1539,16 @@ static int __dquot_initialize(struct inode *inode, int type)
|
||||||
*/
|
*/
|
||||||
rsv = inode_get_rsv_space(inode);
|
rsv = inode_get_rsv_space(inode);
|
||||||
if (unlikely(rsv)) {
|
if (unlikely(rsv)) {
|
||||||
|
struct dquot *dquot = srcu_dereference_check(
|
||||||
|
dquots[cnt], &dquot_srcu,
|
||||||
|
lockdep_is_held(&dq_data_lock));
|
||||||
|
|
||||||
spin_lock(&inode->i_lock);
|
spin_lock(&inode->i_lock);
|
||||||
/* Get reservation again under proper lock */
|
/* Get reservation again under proper lock */
|
||||||
rsv = __inode_get_rsv_space(inode);
|
rsv = __inode_get_rsv_space(inode);
|
||||||
spin_lock(&dquots[cnt]->dq_dqb_lock);
|
spin_lock(&dquot->dq_dqb_lock);
|
||||||
dquots[cnt]->dq_dqb.dqb_rsvspace += rsv;
|
dquot->dq_dqb.dqb_rsvspace += rsv;
|
||||||
spin_unlock(&dquots[cnt]->dq_dqb_lock);
|
spin_unlock(&dquot->dq_dqb_lock);
|
||||||
spin_unlock(&inode->i_lock);
|
spin_unlock(&inode->i_lock);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1565,7 +1570,7 @@ EXPORT_SYMBOL(dquot_initialize);
|
||||||
|
|
||||||
bool dquot_initialize_needed(struct inode *inode)
|
bool dquot_initialize_needed(struct inode *inode)
|
||||||
{
|
{
|
||||||
struct dquot **dquots;
|
struct dquot __rcu **dquots;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if (!inode_quota_active(inode))
|
if (!inode_quota_active(inode))
|
||||||
|
@ -1590,13 +1595,14 @@ EXPORT_SYMBOL(dquot_initialize_needed);
|
||||||
static void __dquot_drop(struct inode *inode)
|
static void __dquot_drop(struct inode *inode)
|
||||||
{
|
{
|
||||||
int cnt;
|
int cnt;
|
||||||
struct dquot **dquots = i_dquot(inode);
|
struct dquot __rcu **dquots = i_dquot(inode);
|
||||||
struct dquot *put[MAXQUOTAS];
|
struct dquot *put[MAXQUOTAS];
|
||||||
|
|
||||||
spin_lock(&dq_data_lock);
|
spin_lock(&dq_data_lock);
|
||||||
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
|
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
|
||||||
put[cnt] = dquots[cnt];
|
put[cnt] = srcu_dereference_check(dquots[cnt], &dquot_srcu,
|
||||||
dquots[cnt] = NULL;
|
lockdep_is_held(&dq_data_lock));
|
||||||
|
rcu_assign_pointer(dquots[cnt], NULL);
|
||||||
}
|
}
|
||||||
spin_unlock(&dq_data_lock);
|
spin_unlock(&dq_data_lock);
|
||||||
dqput_all(put);
|
dqput_all(put);
|
||||||
|
@ -1604,7 +1610,7 @@ static void __dquot_drop(struct inode *inode)
|
||||||
|
|
||||||
void dquot_drop(struct inode *inode)
|
void dquot_drop(struct inode *inode)
|
||||||
{
|
{
|
||||||
struct dquot * const *dquots;
|
struct dquot __rcu * const *dquots;
|
||||||
int cnt;
|
int cnt;
|
||||||
|
|
||||||
if (IS_NOQUOTA(inode))
|
if (IS_NOQUOTA(inode))
|
||||||
|
@ -1677,7 +1683,8 @@ int __dquot_alloc_space(struct inode *inode, qsize_t number, int flags)
|
||||||
int cnt, ret = 0, index;
|
int cnt, ret = 0, index;
|
||||||
struct dquot_warn warn[MAXQUOTAS];
|
struct dquot_warn warn[MAXQUOTAS];
|
||||||
int reserve = flags & DQUOT_SPACE_RESERVE;
|
int reserve = flags & DQUOT_SPACE_RESERVE;
|
||||||
struct dquot **dquots;
|
struct dquot __rcu **dquots;
|
||||||
|
struct dquot *dquot;
|
||||||
|
|
||||||
if (!inode_quota_active(inode)) {
|
if (!inode_quota_active(inode)) {
|
||||||
if (reserve) {
|
if (reserve) {
|
||||||
|
@ -1697,27 +1704,26 @@ int __dquot_alloc_space(struct inode *inode, qsize_t number, int flags)
|
||||||
index = srcu_read_lock(&dquot_srcu);
|
index = srcu_read_lock(&dquot_srcu);
|
||||||
spin_lock(&inode->i_lock);
|
spin_lock(&inode->i_lock);
|
||||||
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
|
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
|
||||||
if (!dquots[cnt])
|
dquot = srcu_dereference(dquots[cnt], &dquot_srcu);
|
||||||
|
if (!dquot)
|
||||||
continue;
|
continue;
|
||||||
if (reserve) {
|
if (reserve) {
|
||||||
ret = dquot_add_space(dquots[cnt], 0, number, flags,
|
ret = dquot_add_space(dquot, 0, number, flags, &warn[cnt]);
|
||||||
&warn[cnt]);
|
|
||||||
} else {
|
} else {
|
||||||
ret = dquot_add_space(dquots[cnt], number, 0, flags,
|
ret = dquot_add_space(dquot, number, 0, flags, &warn[cnt]);
|
||||||
&warn[cnt]);
|
|
||||||
}
|
}
|
||||||
if (ret) {
|
if (ret) {
|
||||||
/* Back out changes we already did */
|
/* Back out changes we already did */
|
||||||
for (cnt--; cnt >= 0; cnt--) {
|
for (cnt--; cnt >= 0; cnt--) {
|
||||||
if (!dquots[cnt])
|
dquot = srcu_dereference(dquots[cnt], &dquot_srcu);
|
||||||
|
if (!dquot)
|
||||||
continue;
|
continue;
|
||||||
spin_lock(&dquots[cnt]->dq_dqb_lock);
|
spin_lock(&dquot->dq_dqb_lock);
|
||||||
if (reserve)
|
if (reserve)
|
||||||
dquot_free_reserved_space(dquots[cnt],
|
dquot_free_reserved_space(dquot, number);
|
||||||
number);
|
|
||||||
else
|
else
|
||||||
dquot_decr_space(dquots[cnt], number);
|
dquot_decr_space(dquot, number);
|
||||||
spin_unlock(&dquots[cnt]->dq_dqb_lock);
|
spin_unlock(&dquot->dq_dqb_lock);
|
||||||
}
|
}
|
||||||
spin_unlock(&inode->i_lock);
|
spin_unlock(&inode->i_lock);
|
||||||
goto out_flush_warn;
|
goto out_flush_warn;
|
||||||
|
@ -1747,7 +1753,8 @@ int dquot_alloc_inode(struct inode *inode)
|
||||||
{
|
{
|
||||||
int cnt, ret = 0, index;
|
int cnt, ret = 0, index;
|
||||||
struct dquot_warn warn[MAXQUOTAS];
|
struct dquot_warn warn[MAXQUOTAS];
|
||||||
struct dquot * const *dquots;
|
struct dquot __rcu * const *dquots;
|
||||||
|
struct dquot *dquot;
|
||||||
|
|
||||||
if (!inode_quota_active(inode))
|
if (!inode_quota_active(inode))
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -1758,17 +1765,19 @@ int dquot_alloc_inode(struct inode *inode)
|
||||||
index = srcu_read_lock(&dquot_srcu);
|
index = srcu_read_lock(&dquot_srcu);
|
||||||
spin_lock(&inode->i_lock);
|
spin_lock(&inode->i_lock);
|
||||||
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
|
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
|
||||||
if (!dquots[cnt])
|
dquot = srcu_dereference(dquots[cnt], &dquot_srcu);
|
||||||
|
if (!dquot)
|
||||||
continue;
|
continue;
|
||||||
ret = dquot_add_inodes(dquots[cnt], 1, &warn[cnt]);
|
ret = dquot_add_inodes(dquot, 1, &warn[cnt]);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
for (cnt--; cnt >= 0; cnt--) {
|
for (cnt--; cnt >= 0; cnt--) {
|
||||||
if (!dquots[cnt])
|
dquot = srcu_dereference(dquots[cnt], &dquot_srcu);
|
||||||
|
if (!dquot)
|
||||||
continue;
|
continue;
|
||||||
/* Back out changes we already did */
|
/* Back out changes we already did */
|
||||||
spin_lock(&dquots[cnt]->dq_dqb_lock);
|
spin_lock(&dquot->dq_dqb_lock);
|
||||||
dquot_decr_inodes(dquots[cnt], 1);
|
dquot_decr_inodes(dquot, 1);
|
||||||
spin_unlock(&dquots[cnt]->dq_dqb_lock);
|
spin_unlock(&dquot->dq_dqb_lock);
|
||||||
}
|
}
|
||||||
goto warn_put_all;
|
goto warn_put_all;
|
||||||
}
|
}
|
||||||
|
@ -1789,7 +1798,8 @@ EXPORT_SYMBOL(dquot_alloc_inode);
|
||||||
*/
|
*/
|
||||||
void dquot_claim_space_nodirty(struct inode *inode, qsize_t number)
|
void dquot_claim_space_nodirty(struct inode *inode, qsize_t number)
|
||||||
{
|
{
|
||||||
struct dquot **dquots;
|
struct dquot __rcu **dquots;
|
||||||
|
struct dquot *dquot;
|
||||||
int cnt, index;
|
int cnt, index;
|
||||||
|
|
||||||
if (!inode_quota_active(inode)) {
|
if (!inode_quota_active(inode)) {
|
||||||
|
@ -1805,9 +1815,8 @@ void dquot_claim_space_nodirty(struct inode *inode, qsize_t number)
|
||||||
spin_lock(&inode->i_lock);
|
spin_lock(&inode->i_lock);
|
||||||
/* Claim reserved quotas to allocated quotas */
|
/* Claim reserved quotas to allocated quotas */
|
||||||
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
|
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
|
||||||
if (dquots[cnt]) {
|
dquot = srcu_dereference(dquots[cnt], &dquot_srcu);
|
||||||
struct dquot *dquot = dquots[cnt];
|
if (dquot) {
|
||||||
|
|
||||||
spin_lock(&dquot->dq_dqb_lock);
|
spin_lock(&dquot->dq_dqb_lock);
|
||||||
if (WARN_ON_ONCE(dquot->dq_dqb.dqb_rsvspace < number))
|
if (WARN_ON_ONCE(dquot->dq_dqb.dqb_rsvspace < number))
|
||||||
number = dquot->dq_dqb.dqb_rsvspace;
|
number = dquot->dq_dqb.dqb_rsvspace;
|
||||||
|
@ -1831,7 +1840,8 @@ EXPORT_SYMBOL(dquot_claim_space_nodirty);
|
||||||
*/
|
*/
|
||||||
void dquot_reclaim_space_nodirty(struct inode *inode, qsize_t number)
|
void dquot_reclaim_space_nodirty(struct inode *inode, qsize_t number)
|
||||||
{
|
{
|
||||||
struct dquot **dquots;
|
struct dquot __rcu **dquots;
|
||||||
|
struct dquot *dquot;
|
||||||
int cnt, index;
|
int cnt, index;
|
||||||
|
|
||||||
if (!inode_quota_active(inode)) {
|
if (!inode_quota_active(inode)) {
|
||||||
|
@ -1847,9 +1857,8 @@ void dquot_reclaim_space_nodirty(struct inode *inode, qsize_t number)
|
||||||
spin_lock(&inode->i_lock);
|
spin_lock(&inode->i_lock);
|
||||||
/* Claim reserved quotas to allocated quotas */
|
/* Claim reserved quotas to allocated quotas */
|
||||||
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
|
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
|
||||||
if (dquots[cnt]) {
|
dquot = srcu_dereference(dquots[cnt], &dquot_srcu);
|
||||||
struct dquot *dquot = dquots[cnt];
|
if (dquot) {
|
||||||
|
|
||||||
spin_lock(&dquot->dq_dqb_lock);
|
spin_lock(&dquot->dq_dqb_lock);
|
||||||
if (WARN_ON_ONCE(dquot->dq_dqb.dqb_curspace < number))
|
if (WARN_ON_ONCE(dquot->dq_dqb.dqb_curspace < number))
|
||||||
number = dquot->dq_dqb.dqb_curspace;
|
number = dquot->dq_dqb.dqb_curspace;
|
||||||
|
@ -1875,7 +1884,8 @@ void __dquot_free_space(struct inode *inode, qsize_t number, int flags)
|
||||||
{
|
{
|
||||||
unsigned int cnt;
|
unsigned int cnt;
|
||||||
struct dquot_warn warn[MAXQUOTAS];
|
struct dquot_warn warn[MAXQUOTAS];
|
||||||
struct dquot **dquots;
|
struct dquot __rcu **dquots;
|
||||||
|
struct dquot *dquot;
|
||||||
int reserve = flags & DQUOT_SPACE_RESERVE, index;
|
int reserve = flags & DQUOT_SPACE_RESERVE, index;
|
||||||
|
|
||||||
if (!inode_quota_active(inode)) {
|
if (!inode_quota_active(inode)) {
|
||||||
|
@ -1896,17 +1906,18 @@ void __dquot_free_space(struct inode *inode, qsize_t number, int flags)
|
||||||
int wtype;
|
int wtype;
|
||||||
|
|
||||||
warn[cnt].w_type = QUOTA_NL_NOWARN;
|
warn[cnt].w_type = QUOTA_NL_NOWARN;
|
||||||
if (!dquots[cnt])
|
dquot = srcu_dereference(dquots[cnt], &dquot_srcu);
|
||||||
|
if (!dquot)
|
||||||
continue;
|
continue;
|
||||||
spin_lock(&dquots[cnt]->dq_dqb_lock);
|
spin_lock(&dquot->dq_dqb_lock);
|
||||||
wtype = info_bdq_free(dquots[cnt], number);
|
wtype = info_bdq_free(dquot, number);
|
||||||
if (wtype != QUOTA_NL_NOWARN)
|
if (wtype != QUOTA_NL_NOWARN)
|
||||||
prepare_warning(&warn[cnt], dquots[cnt], wtype);
|
prepare_warning(&warn[cnt], dquot, wtype);
|
||||||
if (reserve)
|
if (reserve)
|
||||||
dquot_free_reserved_space(dquots[cnt], number);
|
dquot_free_reserved_space(dquot, number);
|
||||||
else
|
else
|
||||||
dquot_decr_space(dquots[cnt], number);
|
dquot_decr_space(dquot, number);
|
||||||
spin_unlock(&dquots[cnt]->dq_dqb_lock);
|
spin_unlock(&dquot->dq_dqb_lock);
|
||||||
}
|
}
|
||||||
if (reserve)
|
if (reserve)
|
||||||
*inode_reserved_space(inode) -= number;
|
*inode_reserved_space(inode) -= number;
|
||||||
|
@ -1930,7 +1941,8 @@ void dquot_free_inode(struct inode *inode)
|
||||||
{
|
{
|
||||||
unsigned int cnt;
|
unsigned int cnt;
|
||||||
struct dquot_warn warn[MAXQUOTAS];
|
struct dquot_warn warn[MAXQUOTAS];
|
||||||
struct dquot * const *dquots;
|
struct dquot __rcu * const *dquots;
|
||||||
|
struct dquot *dquot;
|
||||||
int index;
|
int index;
|
||||||
|
|
||||||
if (!inode_quota_active(inode))
|
if (!inode_quota_active(inode))
|
||||||
|
@ -1941,16 +1953,16 @@ void dquot_free_inode(struct inode *inode)
|
||||||
spin_lock(&inode->i_lock);
|
spin_lock(&inode->i_lock);
|
||||||
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
|
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
|
||||||
int wtype;
|
int wtype;
|
||||||
|
|
||||||
warn[cnt].w_type = QUOTA_NL_NOWARN;
|
warn[cnt].w_type = QUOTA_NL_NOWARN;
|
||||||
if (!dquots[cnt])
|
dquot = srcu_dereference(dquots[cnt], &dquot_srcu);
|
||||||
|
if (!dquot)
|
||||||
continue;
|
continue;
|
||||||
spin_lock(&dquots[cnt]->dq_dqb_lock);
|
spin_lock(&dquot->dq_dqb_lock);
|
||||||
wtype = info_idq_free(dquots[cnt], 1);
|
wtype = info_idq_free(dquot, 1);
|
||||||
if (wtype != QUOTA_NL_NOWARN)
|
if (wtype != QUOTA_NL_NOWARN)
|
||||||
prepare_warning(&warn[cnt], dquots[cnt], wtype);
|
prepare_warning(&warn[cnt], dquot, wtype);
|
||||||
dquot_decr_inodes(dquots[cnt], 1);
|
dquot_decr_inodes(dquot, 1);
|
||||||
spin_unlock(&dquots[cnt]->dq_dqb_lock);
|
spin_unlock(&dquot->dq_dqb_lock);
|
||||||
}
|
}
|
||||||
spin_unlock(&inode->i_lock);
|
spin_unlock(&inode->i_lock);
|
||||||
mark_all_dquot_dirty(dquots);
|
mark_all_dquot_dirty(dquots);
|
||||||
|
@ -1976,8 +1988,9 @@ int __dquot_transfer(struct inode *inode, struct dquot **transfer_to)
|
||||||
qsize_t cur_space;
|
qsize_t cur_space;
|
||||||
qsize_t rsv_space = 0;
|
qsize_t rsv_space = 0;
|
||||||
qsize_t inode_usage = 1;
|
qsize_t inode_usage = 1;
|
||||||
|
struct dquot __rcu **dquots;
|
||||||
struct dquot *transfer_from[MAXQUOTAS] = {};
|
struct dquot *transfer_from[MAXQUOTAS] = {};
|
||||||
int cnt, ret = 0;
|
int cnt, index, ret = 0;
|
||||||
char is_valid[MAXQUOTAS] = {};
|
char is_valid[MAXQUOTAS] = {};
|
||||||
struct dquot_warn warn_to[MAXQUOTAS];
|
struct dquot_warn warn_to[MAXQUOTAS];
|
||||||
struct dquot_warn warn_from_inodes[MAXQUOTAS];
|
struct dquot_warn warn_from_inodes[MAXQUOTAS];
|
||||||
|
@ -2008,6 +2021,7 @@ int __dquot_transfer(struct inode *inode, struct dquot **transfer_to)
|
||||||
}
|
}
|
||||||
cur_space = __inode_get_bytes(inode);
|
cur_space = __inode_get_bytes(inode);
|
||||||
rsv_space = __inode_get_rsv_space(inode);
|
rsv_space = __inode_get_rsv_space(inode);
|
||||||
|
dquots = i_dquot(inode);
|
||||||
/*
|
/*
|
||||||
* Build the transfer_from list, check limits, and update usage in
|
* Build the transfer_from list, check limits, and update usage in
|
||||||
* the target structures.
|
* the target structures.
|
||||||
|
@ -2022,7 +2036,8 @@ int __dquot_transfer(struct inode *inode, struct dquot **transfer_to)
|
||||||
if (!sb_has_quota_active(inode->i_sb, cnt))
|
if (!sb_has_quota_active(inode->i_sb, cnt))
|
||||||
continue;
|
continue;
|
||||||
is_valid[cnt] = 1;
|
is_valid[cnt] = 1;
|
||||||
transfer_from[cnt] = i_dquot(inode)[cnt];
|
transfer_from[cnt] = srcu_dereference_check(dquots[cnt],
|
||||||
|
&dquot_srcu, lockdep_is_held(&dq_data_lock));
|
||||||
ret = dquot_add_inodes(transfer_to[cnt], inode_usage,
|
ret = dquot_add_inodes(transfer_to[cnt], inode_usage,
|
||||||
&warn_to[cnt]);
|
&warn_to[cnt]);
|
||||||
if (ret)
|
if (ret)
|
||||||
|
@ -2061,13 +2076,21 @@ int __dquot_transfer(struct inode *inode, struct dquot **transfer_to)
|
||||||
rsv_space);
|
rsv_space);
|
||||||
spin_unlock(&transfer_from[cnt]->dq_dqb_lock);
|
spin_unlock(&transfer_from[cnt]->dq_dqb_lock);
|
||||||
}
|
}
|
||||||
i_dquot(inode)[cnt] = transfer_to[cnt];
|
rcu_assign_pointer(dquots[cnt], transfer_to[cnt]);
|
||||||
}
|
}
|
||||||
spin_unlock(&inode->i_lock);
|
spin_unlock(&inode->i_lock);
|
||||||
spin_unlock(&dq_data_lock);
|
spin_unlock(&dq_data_lock);
|
||||||
|
|
||||||
mark_all_dquot_dirty(transfer_from);
|
/*
|
||||||
mark_all_dquot_dirty(transfer_to);
|
* These arrays are local and we hold dquot references so we don't need
|
||||||
|
* the srcu protection but still take dquot_srcu to avoid warning in
|
||||||
|
* mark_all_dquot_dirty().
|
||||||
|
*/
|
||||||
|
index = srcu_read_lock(&dquot_srcu);
|
||||||
|
mark_all_dquot_dirty((struct dquot __rcu **)transfer_from);
|
||||||
|
mark_all_dquot_dirty((struct dquot __rcu **)transfer_to);
|
||||||
|
srcu_read_unlock(&dquot_srcu, index);
|
||||||
|
|
||||||
flush_warnings(warn_to);
|
flush_warnings(warn_to);
|
||||||
flush_warnings(warn_from_inodes);
|
flush_warnings(warn_from_inodes);
|
||||||
flush_warnings(warn_from_space);
|
flush_warnings(warn_from_space);
|
||||||
|
@ -2388,7 +2411,8 @@ int dquot_load_quota_sb(struct super_block *sb, int type, int format_id,
|
||||||
lockdep_assert_held_write(&sb->s_umount);
|
lockdep_assert_held_write(&sb->s_umount);
|
||||||
|
|
||||||
/* Just unsuspend quotas? */
|
/* Just unsuspend quotas? */
|
||||||
BUG_ON(flags & DQUOT_SUSPENDED);
|
if (WARN_ON_ONCE(flags & DQUOT_SUSPENDED))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
if (!fmt)
|
if (!fmt)
|
||||||
return -ESRCH;
|
return -ESRCH;
|
||||||
|
|
|
@ -21,6 +21,12 @@ MODULE_AUTHOR("Jan Kara");
|
||||||
MODULE_DESCRIPTION("Quota trie support");
|
MODULE_DESCRIPTION("Quota trie support");
|
||||||
MODULE_LICENSE("GPL");
|
MODULE_LICENSE("GPL");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Maximum quota tree depth we support. Only to limit recursion when working
|
||||||
|
* with the tree.
|
||||||
|
*/
|
||||||
|
#define MAX_QTREE_DEPTH 6
|
||||||
|
|
||||||
#define __QUOTA_QT_PARANOIA
|
#define __QUOTA_QT_PARANOIA
|
||||||
|
|
||||||
static int __get_index(struct qtree_mem_dqinfo *info, qid_t id, int depth)
|
static int __get_index(struct qtree_mem_dqinfo *info, qid_t id, int depth)
|
||||||
|
@ -108,7 +114,7 @@ static int check_dquot_block_header(struct qtree_mem_dqinfo *info,
|
||||||
/* Remove empty block from list and return it */
|
/* Remove empty block from list and return it */
|
||||||
static int get_free_dqblk(struct qtree_mem_dqinfo *info)
|
static int get_free_dqblk(struct qtree_mem_dqinfo *info)
|
||||||
{
|
{
|
||||||
char *buf = kmalloc(info->dqi_usable_bs, GFP_NOFS);
|
char *buf = kmalloc(info->dqi_usable_bs, GFP_KERNEL);
|
||||||
struct qt_disk_dqdbheader *dh = (struct qt_disk_dqdbheader *)buf;
|
struct qt_disk_dqdbheader *dh = (struct qt_disk_dqdbheader *)buf;
|
||||||
int ret, blk;
|
int ret, blk;
|
||||||
|
|
||||||
|
@ -160,7 +166,7 @@ static int put_free_dqblk(struct qtree_mem_dqinfo *info, char *buf, uint blk)
|
||||||
static int remove_free_dqentry(struct qtree_mem_dqinfo *info, char *buf,
|
static int remove_free_dqentry(struct qtree_mem_dqinfo *info, char *buf,
|
||||||
uint blk)
|
uint blk)
|
||||||
{
|
{
|
||||||
char *tmpbuf = kmalloc(info->dqi_usable_bs, GFP_NOFS);
|
char *tmpbuf = kmalloc(info->dqi_usable_bs, GFP_KERNEL);
|
||||||
struct qt_disk_dqdbheader *dh = (struct qt_disk_dqdbheader *)buf;
|
struct qt_disk_dqdbheader *dh = (struct qt_disk_dqdbheader *)buf;
|
||||||
uint nextblk = le32_to_cpu(dh->dqdh_next_free);
|
uint nextblk = le32_to_cpu(dh->dqdh_next_free);
|
||||||
uint prevblk = le32_to_cpu(dh->dqdh_prev_free);
|
uint prevblk = le32_to_cpu(dh->dqdh_prev_free);
|
||||||
|
@ -207,7 +213,7 @@ out_buf:
|
||||||
static int insert_free_dqentry(struct qtree_mem_dqinfo *info, char *buf,
|
static int insert_free_dqentry(struct qtree_mem_dqinfo *info, char *buf,
|
||||||
uint blk)
|
uint blk)
|
||||||
{
|
{
|
||||||
char *tmpbuf = kmalloc(info->dqi_usable_bs, GFP_NOFS);
|
char *tmpbuf = kmalloc(info->dqi_usable_bs, GFP_KERNEL);
|
||||||
struct qt_disk_dqdbheader *dh = (struct qt_disk_dqdbheader *)buf;
|
struct qt_disk_dqdbheader *dh = (struct qt_disk_dqdbheader *)buf;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
|
@ -255,7 +261,7 @@ static uint find_free_dqentry(struct qtree_mem_dqinfo *info,
|
||||||
{
|
{
|
||||||
uint blk, i;
|
uint blk, i;
|
||||||
struct qt_disk_dqdbheader *dh;
|
struct qt_disk_dqdbheader *dh;
|
||||||
char *buf = kmalloc(info->dqi_usable_bs, GFP_NOFS);
|
char *buf = kmalloc(info->dqi_usable_bs, GFP_KERNEL);
|
||||||
char *ddquot;
|
char *ddquot;
|
||||||
|
|
||||||
*err = 0;
|
*err = 0;
|
||||||
|
@ -327,27 +333,36 @@ out_buf:
|
||||||
|
|
||||||
/* Insert reference to structure into the trie */
|
/* Insert reference to structure into the trie */
|
||||||
static int do_insert_tree(struct qtree_mem_dqinfo *info, struct dquot *dquot,
|
static int do_insert_tree(struct qtree_mem_dqinfo *info, struct dquot *dquot,
|
||||||
uint *treeblk, int depth)
|
uint *blks, int depth)
|
||||||
{
|
{
|
||||||
char *buf = kmalloc(info->dqi_usable_bs, GFP_NOFS);
|
char *buf = kmalloc(info->dqi_usable_bs, GFP_KERNEL);
|
||||||
int ret = 0, newson = 0, newact = 0;
|
int ret = 0, newson = 0, newact = 0;
|
||||||
__le32 *ref;
|
__le32 *ref;
|
||||||
uint newblk;
|
uint newblk;
|
||||||
|
int i;
|
||||||
|
|
||||||
if (!buf)
|
if (!buf)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
if (!*treeblk) {
|
if (!blks[depth]) {
|
||||||
ret = get_free_dqblk(info);
|
ret = get_free_dqblk(info);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto out_buf;
|
goto out_buf;
|
||||||
*treeblk = ret;
|
for (i = 0; i < depth; i++)
|
||||||
|
if (ret == blks[i]) {
|
||||||
|
quota_error(dquot->dq_sb,
|
||||||
|
"Free block already used in tree: block %u",
|
||||||
|
ret);
|
||||||
|
ret = -EIO;
|
||||||
|
goto out_buf;
|
||||||
|
}
|
||||||
|
blks[depth] = ret;
|
||||||
memset(buf, 0, info->dqi_usable_bs);
|
memset(buf, 0, info->dqi_usable_bs);
|
||||||
newact = 1;
|
newact = 1;
|
||||||
} else {
|
} else {
|
||||||
ret = read_blk(info, *treeblk, buf);
|
ret = read_blk(info, blks[depth], buf);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
quota_error(dquot->dq_sb, "Can't read tree quota "
|
quota_error(dquot->dq_sb, "Can't read tree quota "
|
||||||
"block %u", *treeblk);
|
"block %u", blks[depth]);
|
||||||
goto out_buf;
|
goto out_buf;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -357,8 +372,20 @@ static int do_insert_tree(struct qtree_mem_dqinfo *info, struct dquot *dquot,
|
||||||
info->dqi_blocks - 1);
|
info->dqi_blocks - 1);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto out_buf;
|
goto out_buf;
|
||||||
if (!newblk)
|
if (!newblk) {
|
||||||
newson = 1;
|
newson = 1;
|
||||||
|
} else {
|
||||||
|
for (i = 0; i <= depth; i++)
|
||||||
|
if (newblk == blks[i]) {
|
||||||
|
quota_error(dquot->dq_sb,
|
||||||
|
"Cycle in quota tree detected: block %u index %u",
|
||||||
|
blks[depth],
|
||||||
|
get_index(info, dquot->dq_id, depth));
|
||||||
|
ret = -EIO;
|
||||||
|
goto out_buf;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
blks[depth + 1] = newblk;
|
||||||
if (depth == info->dqi_qtree_depth - 1) {
|
if (depth == info->dqi_qtree_depth - 1) {
|
||||||
#ifdef __QUOTA_QT_PARANOIA
|
#ifdef __QUOTA_QT_PARANOIA
|
||||||
if (newblk) {
|
if (newblk) {
|
||||||
|
@ -370,16 +397,16 @@ static int do_insert_tree(struct qtree_mem_dqinfo *info, struct dquot *dquot,
|
||||||
goto out_buf;
|
goto out_buf;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
newblk = find_free_dqentry(info, dquot, &ret);
|
blks[depth + 1] = find_free_dqentry(info, dquot, &ret);
|
||||||
} else {
|
} else {
|
||||||
ret = do_insert_tree(info, dquot, &newblk, depth+1);
|
ret = do_insert_tree(info, dquot, blks, depth + 1);
|
||||||
}
|
}
|
||||||
if (newson && ret >= 0) {
|
if (newson && ret >= 0) {
|
||||||
ref[get_index(info, dquot->dq_id, depth)] =
|
ref[get_index(info, dquot->dq_id, depth)] =
|
||||||
cpu_to_le32(newblk);
|
cpu_to_le32(blks[depth + 1]);
|
||||||
ret = write_blk(info, *treeblk, buf);
|
ret = write_blk(info, blks[depth], buf);
|
||||||
} else if (newact && ret < 0) {
|
} else if (newact && ret < 0) {
|
||||||
put_free_dqblk(info, buf, *treeblk);
|
put_free_dqblk(info, buf, blks[depth]);
|
||||||
}
|
}
|
||||||
out_buf:
|
out_buf:
|
||||||
kfree(buf);
|
kfree(buf);
|
||||||
|
@ -390,7 +417,7 @@ out_buf:
|
||||||
static inline int dq_insert_tree(struct qtree_mem_dqinfo *info,
|
static inline int dq_insert_tree(struct qtree_mem_dqinfo *info,
|
||||||
struct dquot *dquot)
|
struct dquot *dquot)
|
||||||
{
|
{
|
||||||
int tmp = QT_TREEOFF;
|
uint blks[MAX_QTREE_DEPTH] = { QT_TREEOFF };
|
||||||
|
|
||||||
#ifdef __QUOTA_QT_PARANOIA
|
#ifdef __QUOTA_QT_PARANOIA
|
||||||
if (info->dqi_blocks <= QT_TREEOFF) {
|
if (info->dqi_blocks <= QT_TREEOFF) {
|
||||||
|
@ -398,7 +425,11 @@ static inline int dq_insert_tree(struct qtree_mem_dqinfo *info,
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
return do_insert_tree(info, dquot, &tmp, 0);
|
if (info->dqi_qtree_depth >= MAX_QTREE_DEPTH) {
|
||||||
|
quota_error(dquot->dq_sb, "Quota tree depth too big!");
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
return do_insert_tree(info, dquot, blks, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -410,7 +441,7 @@ int qtree_write_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot)
|
||||||
int type = dquot->dq_id.type;
|
int type = dquot->dq_id.type;
|
||||||
struct super_block *sb = dquot->dq_sb;
|
struct super_block *sb = dquot->dq_sb;
|
||||||
ssize_t ret;
|
ssize_t ret;
|
||||||
char *ddquot = kmalloc(info->dqi_entry_size, GFP_NOFS);
|
char *ddquot = kmalloc(info->dqi_entry_size, GFP_KERNEL);
|
||||||
|
|
||||||
if (!ddquot)
|
if (!ddquot)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
@ -449,7 +480,7 @@ static int free_dqentry(struct qtree_mem_dqinfo *info, struct dquot *dquot,
|
||||||
uint blk)
|
uint blk)
|
||||||
{
|
{
|
||||||
struct qt_disk_dqdbheader *dh;
|
struct qt_disk_dqdbheader *dh;
|
||||||
char *buf = kmalloc(info->dqi_usable_bs, GFP_NOFS);
|
char *buf = kmalloc(info->dqi_usable_bs, GFP_KERNEL);
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
if (!buf)
|
if (!buf)
|
||||||
|
@ -511,19 +542,20 @@ out_buf:
|
||||||
|
|
||||||
/* Remove reference to dquot from tree */
|
/* Remove reference to dquot from tree */
|
||||||
static int remove_tree(struct qtree_mem_dqinfo *info, struct dquot *dquot,
|
static int remove_tree(struct qtree_mem_dqinfo *info, struct dquot *dquot,
|
||||||
uint *blk, int depth)
|
uint *blks, int depth)
|
||||||
{
|
{
|
||||||
char *buf = kmalloc(info->dqi_usable_bs, GFP_NOFS);
|
char *buf = kmalloc(info->dqi_usable_bs, GFP_KERNEL);
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
uint newblk;
|
uint newblk;
|
||||||
__le32 *ref = (__le32 *)buf;
|
__le32 *ref = (__le32 *)buf;
|
||||||
|
int i;
|
||||||
|
|
||||||
if (!buf)
|
if (!buf)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
ret = read_blk(info, *blk, buf);
|
ret = read_blk(info, blks[depth], buf);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
quota_error(dquot->dq_sb, "Can't read quota data block %u",
|
quota_error(dquot->dq_sb, "Can't read quota data block %u",
|
||||||
*blk);
|
blks[depth]);
|
||||||
goto out_buf;
|
goto out_buf;
|
||||||
}
|
}
|
||||||
newblk = le32_to_cpu(ref[get_index(info, dquot->dq_id, depth)]);
|
newblk = le32_to_cpu(ref[get_index(info, dquot->dq_id, depth)]);
|
||||||
|
@ -532,29 +564,38 @@ static int remove_tree(struct qtree_mem_dqinfo *info, struct dquot *dquot,
|
||||||
if (ret)
|
if (ret)
|
||||||
goto out_buf;
|
goto out_buf;
|
||||||
|
|
||||||
|
for (i = 0; i <= depth; i++)
|
||||||
|
if (newblk == blks[i]) {
|
||||||
|
quota_error(dquot->dq_sb,
|
||||||
|
"Cycle in quota tree detected: block %u index %u",
|
||||||
|
blks[depth],
|
||||||
|
get_index(info, dquot->dq_id, depth));
|
||||||
|
ret = -EIO;
|
||||||
|
goto out_buf;
|
||||||
|
}
|
||||||
if (depth == info->dqi_qtree_depth - 1) {
|
if (depth == info->dqi_qtree_depth - 1) {
|
||||||
ret = free_dqentry(info, dquot, newblk);
|
ret = free_dqentry(info, dquot, newblk);
|
||||||
newblk = 0;
|
blks[depth + 1] = 0;
|
||||||
} else {
|
} else {
|
||||||
ret = remove_tree(info, dquot, &newblk, depth+1);
|
blks[depth + 1] = newblk;
|
||||||
|
ret = remove_tree(info, dquot, blks, depth + 1);
|
||||||
}
|
}
|
||||||
if (ret >= 0 && !newblk) {
|
if (ret >= 0 && !blks[depth + 1]) {
|
||||||
int i;
|
|
||||||
ref[get_index(info, dquot->dq_id, depth)] = cpu_to_le32(0);
|
ref[get_index(info, dquot->dq_id, depth)] = cpu_to_le32(0);
|
||||||
/* Block got empty? */
|
/* Block got empty? */
|
||||||
for (i = 0; i < (info->dqi_usable_bs >> 2) && !ref[i]; i++)
|
for (i = 0; i < (info->dqi_usable_bs >> 2) && !ref[i]; i++)
|
||||||
;
|
;
|
||||||
/* Don't put the root block into the free block list */
|
/* Don't put the root block into the free block list */
|
||||||
if (i == (info->dqi_usable_bs >> 2)
|
if (i == (info->dqi_usable_bs >> 2)
|
||||||
&& *blk != QT_TREEOFF) {
|
&& blks[depth] != QT_TREEOFF) {
|
||||||
put_free_dqblk(info, buf, *blk);
|
put_free_dqblk(info, buf, blks[depth]);
|
||||||
*blk = 0;
|
blks[depth] = 0;
|
||||||
} else {
|
} else {
|
||||||
ret = write_blk(info, *blk, buf);
|
ret = write_blk(info, blks[depth], buf);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
quota_error(dquot->dq_sb,
|
quota_error(dquot->dq_sb,
|
||||||
"Can't write quota tree block %u",
|
"Can't write quota tree block %u",
|
||||||
*blk);
|
blks[depth]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
out_buf:
|
out_buf:
|
||||||
|
@ -565,11 +606,15 @@ out_buf:
|
||||||
/* Delete dquot from tree */
|
/* Delete dquot from tree */
|
||||||
int qtree_delete_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot)
|
int qtree_delete_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot)
|
||||||
{
|
{
|
||||||
uint tmp = QT_TREEOFF;
|
uint blks[MAX_QTREE_DEPTH] = { QT_TREEOFF };
|
||||||
|
|
||||||
if (!dquot->dq_off) /* Even not allocated? */
|
if (!dquot->dq_off) /* Even not allocated? */
|
||||||
return 0;
|
return 0;
|
||||||
return remove_tree(info, dquot, &tmp, 0);
|
if (info->dqi_qtree_depth >= MAX_QTREE_DEPTH) {
|
||||||
|
quota_error(dquot->dq_sb, "Quota tree depth too big!");
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
return remove_tree(info, dquot, blks, 0);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(qtree_delete_dquot);
|
EXPORT_SYMBOL(qtree_delete_dquot);
|
||||||
|
|
||||||
|
@ -577,7 +622,7 @@ EXPORT_SYMBOL(qtree_delete_dquot);
|
||||||
static loff_t find_block_dqentry(struct qtree_mem_dqinfo *info,
|
static loff_t find_block_dqentry(struct qtree_mem_dqinfo *info,
|
||||||
struct dquot *dquot, uint blk)
|
struct dquot *dquot, uint blk)
|
||||||
{
|
{
|
||||||
char *buf = kmalloc(info->dqi_usable_bs, GFP_NOFS);
|
char *buf = kmalloc(info->dqi_usable_bs, GFP_KERNEL);
|
||||||
loff_t ret = 0;
|
loff_t ret = 0;
|
||||||
int i;
|
int i;
|
||||||
char *ddquot;
|
char *ddquot;
|
||||||
|
@ -613,18 +658,20 @@ out_buf:
|
||||||
|
|
||||||
/* Find entry for given id in the tree */
|
/* Find entry for given id in the tree */
|
||||||
static loff_t find_tree_dqentry(struct qtree_mem_dqinfo *info,
|
static loff_t find_tree_dqentry(struct qtree_mem_dqinfo *info,
|
||||||
struct dquot *dquot, uint blk, int depth)
|
struct dquot *dquot, uint *blks, int depth)
|
||||||
{
|
{
|
||||||
char *buf = kmalloc(info->dqi_usable_bs, GFP_NOFS);
|
char *buf = kmalloc(info->dqi_usable_bs, GFP_KERNEL);
|
||||||
loff_t ret = 0;
|
loff_t ret = 0;
|
||||||
__le32 *ref = (__le32 *)buf;
|
__le32 *ref = (__le32 *)buf;
|
||||||
|
uint blk;
|
||||||
|
int i;
|
||||||
|
|
||||||
if (!buf)
|
if (!buf)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
ret = read_blk(info, blk, buf);
|
ret = read_blk(info, blks[depth], buf);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
quota_error(dquot->dq_sb, "Can't read quota tree block %u",
|
quota_error(dquot->dq_sb, "Can't read quota tree block %u",
|
||||||
blk);
|
blks[depth]);
|
||||||
goto out_buf;
|
goto out_buf;
|
||||||
}
|
}
|
||||||
ret = 0;
|
ret = 0;
|
||||||
|
@ -636,8 +683,19 @@ static loff_t find_tree_dqentry(struct qtree_mem_dqinfo *info,
|
||||||
if (ret)
|
if (ret)
|
||||||
goto out_buf;
|
goto out_buf;
|
||||||
|
|
||||||
|
/* Check for cycles in the tree */
|
||||||
|
for (i = 0; i <= depth; i++)
|
||||||
|
if (blk == blks[i]) {
|
||||||
|
quota_error(dquot->dq_sb,
|
||||||
|
"Cycle in quota tree detected: block %u index %u",
|
||||||
|
blks[depth],
|
||||||
|
get_index(info, dquot->dq_id, depth));
|
||||||
|
ret = -EIO;
|
||||||
|
goto out_buf;
|
||||||
|
}
|
||||||
|
blks[depth + 1] = blk;
|
||||||
if (depth < info->dqi_qtree_depth - 1)
|
if (depth < info->dqi_qtree_depth - 1)
|
||||||
ret = find_tree_dqentry(info, dquot, blk, depth+1);
|
ret = find_tree_dqentry(info, dquot, blks, depth + 1);
|
||||||
else
|
else
|
||||||
ret = find_block_dqentry(info, dquot, blk);
|
ret = find_block_dqentry(info, dquot, blk);
|
||||||
out_buf:
|
out_buf:
|
||||||
|
@ -649,7 +707,13 @@ out_buf:
|
||||||
static inline loff_t find_dqentry(struct qtree_mem_dqinfo *info,
|
static inline loff_t find_dqentry(struct qtree_mem_dqinfo *info,
|
||||||
struct dquot *dquot)
|
struct dquot *dquot)
|
||||||
{
|
{
|
||||||
return find_tree_dqentry(info, dquot, QT_TREEOFF, 0);
|
uint blks[MAX_QTREE_DEPTH] = { QT_TREEOFF };
|
||||||
|
|
||||||
|
if (info->dqi_qtree_depth >= MAX_QTREE_DEPTH) {
|
||||||
|
quota_error(dquot->dq_sb, "Quota tree depth too big!");
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
return find_tree_dqentry(info, dquot, blks, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
int qtree_read_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot)
|
int qtree_read_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot)
|
||||||
|
@ -684,7 +748,7 @@ int qtree_read_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot)
|
||||||
}
|
}
|
||||||
dquot->dq_off = offset;
|
dquot->dq_off = offset;
|
||||||
}
|
}
|
||||||
ddquot = kmalloc(info->dqi_entry_size, GFP_NOFS);
|
ddquot = kmalloc(info->dqi_entry_size, GFP_KERNEL);
|
||||||
if (!ddquot)
|
if (!ddquot)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
ret = sb->s_op->quota_read(sb, type, ddquot, info->dqi_entry_size,
|
ret = sb->s_op->quota_read(sb, type, ddquot, info->dqi_entry_size,
|
||||||
|
@ -728,7 +792,7 @@ EXPORT_SYMBOL(qtree_release_dquot);
|
||||||
static int find_next_id(struct qtree_mem_dqinfo *info, qid_t *id,
|
static int find_next_id(struct qtree_mem_dqinfo *info, qid_t *id,
|
||||||
unsigned int blk, int depth)
|
unsigned int blk, int depth)
|
||||||
{
|
{
|
||||||
char *buf = kmalloc(info->dqi_usable_bs, GFP_NOFS);
|
char *buf = kmalloc(info->dqi_usable_bs, GFP_KERNEL);
|
||||||
__le32 *ref = (__le32 *)buf;
|
__le32 *ref = (__le32 *)buf;
|
||||||
ssize_t ret;
|
ssize_t ret;
|
||||||
unsigned int epb = info->dqi_usable_bs >> 2;
|
unsigned int epb = info->dqi_usable_bs >> 2;
|
||||||
|
|
|
@ -160,9 +160,11 @@ static int v1_read_file_info(struct super_block *sb, int type)
|
||||||
{
|
{
|
||||||
struct quota_info *dqopt = sb_dqopt(sb);
|
struct quota_info *dqopt = sb_dqopt(sb);
|
||||||
struct v1_disk_dqblk dqblk;
|
struct v1_disk_dqblk dqblk;
|
||||||
|
unsigned int memalloc;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
down_read(&dqopt->dqio_sem);
|
down_read(&dqopt->dqio_sem);
|
||||||
|
memalloc = memalloc_nofs_save();
|
||||||
ret = sb->s_op->quota_read(sb, type, (char *)&dqblk,
|
ret = sb->s_op->quota_read(sb, type, (char *)&dqblk,
|
||||||
sizeof(struct v1_disk_dqblk), v1_dqoff(0));
|
sizeof(struct v1_disk_dqblk), v1_dqoff(0));
|
||||||
if (ret != sizeof(struct v1_disk_dqblk)) {
|
if (ret != sizeof(struct v1_disk_dqblk)) {
|
||||||
|
@ -179,6 +181,7 @@ static int v1_read_file_info(struct super_block *sb, int type)
|
||||||
dqopt->info[type].dqi_bgrace =
|
dqopt->info[type].dqi_bgrace =
|
||||||
dqblk.dqb_btime ? dqblk.dqb_btime : MAX_DQ_TIME;
|
dqblk.dqb_btime ? dqblk.dqb_btime : MAX_DQ_TIME;
|
||||||
out:
|
out:
|
||||||
|
memalloc_nofs_restore(memalloc);
|
||||||
up_read(&dqopt->dqio_sem);
|
up_read(&dqopt->dqio_sem);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -187,9 +190,11 @@ static int v1_write_file_info(struct super_block *sb, int type)
|
||||||
{
|
{
|
||||||
struct quota_info *dqopt = sb_dqopt(sb);
|
struct quota_info *dqopt = sb_dqopt(sb);
|
||||||
struct v1_disk_dqblk dqblk;
|
struct v1_disk_dqblk dqblk;
|
||||||
|
unsigned int memalloc;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
down_write(&dqopt->dqio_sem);
|
down_write(&dqopt->dqio_sem);
|
||||||
|
memalloc = memalloc_nofs_save();
|
||||||
ret = sb->s_op->quota_read(sb, type, (char *)&dqblk,
|
ret = sb->s_op->quota_read(sb, type, (char *)&dqblk,
|
||||||
sizeof(struct v1_disk_dqblk), v1_dqoff(0));
|
sizeof(struct v1_disk_dqblk), v1_dqoff(0));
|
||||||
if (ret != sizeof(struct v1_disk_dqblk)) {
|
if (ret != sizeof(struct v1_disk_dqblk)) {
|
||||||
|
@ -209,6 +214,7 @@ static int v1_write_file_info(struct super_block *sb, int type)
|
||||||
else if (ret >= 0)
|
else if (ret >= 0)
|
||||||
ret = -EIO;
|
ret = -EIO;
|
||||||
out:
|
out:
|
||||||
|
memalloc_nofs_restore(memalloc);
|
||||||
up_write(&dqopt->dqio_sem);
|
up_write(&dqopt->dqio_sem);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
@ -96,9 +96,11 @@ static int v2_read_file_info(struct super_block *sb, int type)
|
||||||
struct qtree_mem_dqinfo *qinfo;
|
struct qtree_mem_dqinfo *qinfo;
|
||||||
ssize_t size;
|
ssize_t size;
|
||||||
unsigned int version;
|
unsigned int version;
|
||||||
|
unsigned int memalloc;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
down_read(&dqopt->dqio_sem);
|
down_read(&dqopt->dqio_sem);
|
||||||
|
memalloc = memalloc_nofs_save();
|
||||||
ret = v2_read_header(sb, type, &dqhead);
|
ret = v2_read_header(sb, type, &dqhead);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto out;
|
goto out;
|
||||||
|
@ -119,7 +121,7 @@ static int v2_read_file_info(struct super_block *sb, int type)
|
||||||
ret = -EIO;
|
ret = -EIO;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
info->dqi_priv = kmalloc(sizeof(struct qtree_mem_dqinfo), GFP_NOFS);
|
info->dqi_priv = kmalloc(sizeof(struct qtree_mem_dqinfo), GFP_KERNEL);
|
||||||
if (!info->dqi_priv) {
|
if (!info->dqi_priv) {
|
||||||
ret = -ENOMEM;
|
ret = -ENOMEM;
|
||||||
goto out;
|
goto out;
|
||||||
|
@ -166,14 +168,17 @@ static int v2_read_file_info(struct super_block *sb, int type)
|
||||||
i_size_read(sb_dqopt(sb)->files[type]));
|
i_size_read(sb_dqopt(sb)->files[type]));
|
||||||
goto out_free;
|
goto out_free;
|
||||||
}
|
}
|
||||||
if (qinfo->dqi_free_blk >= qinfo->dqi_blocks) {
|
if (qinfo->dqi_free_blk && (qinfo->dqi_free_blk <= QT_TREEOFF ||
|
||||||
quota_error(sb, "Free block number too big (%u >= %u).",
|
qinfo->dqi_free_blk >= qinfo->dqi_blocks)) {
|
||||||
qinfo->dqi_free_blk, qinfo->dqi_blocks);
|
quota_error(sb, "Free block number %u out of range (%u, %u).",
|
||||||
|
qinfo->dqi_free_blk, QT_TREEOFF, qinfo->dqi_blocks);
|
||||||
goto out_free;
|
goto out_free;
|
||||||
}
|
}
|
||||||
if (qinfo->dqi_free_entry >= qinfo->dqi_blocks) {
|
if (qinfo->dqi_free_entry && (qinfo->dqi_free_entry <= QT_TREEOFF ||
|
||||||
quota_error(sb, "Block with free entry too big (%u >= %u).",
|
qinfo->dqi_free_entry >= qinfo->dqi_blocks)) {
|
||||||
qinfo->dqi_free_entry, qinfo->dqi_blocks);
|
quota_error(sb, "Block with free entry %u out of range (%u, %u).",
|
||||||
|
qinfo->dqi_free_entry, QT_TREEOFF,
|
||||||
|
qinfo->dqi_blocks);
|
||||||
goto out_free;
|
goto out_free;
|
||||||
}
|
}
|
||||||
ret = 0;
|
ret = 0;
|
||||||
|
@ -183,6 +188,7 @@ out_free:
|
||||||
info->dqi_priv = NULL;
|
info->dqi_priv = NULL;
|
||||||
}
|
}
|
||||||
out:
|
out:
|
||||||
|
memalloc_nofs_restore(memalloc);
|
||||||
up_read(&dqopt->dqio_sem);
|
up_read(&dqopt->dqio_sem);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -195,8 +201,10 @@ static int v2_write_file_info(struct super_block *sb, int type)
|
||||||
struct mem_dqinfo *info = &dqopt->info[type];
|
struct mem_dqinfo *info = &dqopt->info[type];
|
||||||
struct qtree_mem_dqinfo *qinfo = info->dqi_priv;
|
struct qtree_mem_dqinfo *qinfo = info->dqi_priv;
|
||||||
ssize_t size;
|
ssize_t size;
|
||||||
|
unsigned int memalloc;
|
||||||
|
|
||||||
down_write(&dqopt->dqio_sem);
|
down_write(&dqopt->dqio_sem);
|
||||||
|
memalloc = memalloc_nofs_save();
|
||||||
spin_lock(&dq_data_lock);
|
spin_lock(&dq_data_lock);
|
||||||
info->dqi_flags &= ~DQF_INFO_DIRTY;
|
info->dqi_flags &= ~DQF_INFO_DIRTY;
|
||||||
dinfo.dqi_bgrace = cpu_to_le32(info->dqi_bgrace);
|
dinfo.dqi_bgrace = cpu_to_le32(info->dqi_bgrace);
|
||||||
|
@ -209,6 +217,7 @@ static int v2_write_file_info(struct super_block *sb, int type)
|
||||||
dinfo.dqi_free_entry = cpu_to_le32(qinfo->dqi_free_entry);
|
dinfo.dqi_free_entry = cpu_to_le32(qinfo->dqi_free_entry);
|
||||||
size = sb->s_op->quota_write(sb, type, (char *)&dinfo,
|
size = sb->s_op->quota_write(sb, type, (char *)&dinfo,
|
||||||
sizeof(struct v2_disk_dqinfo), V2_DQINFOOFF);
|
sizeof(struct v2_disk_dqinfo), V2_DQINFOOFF);
|
||||||
|
memalloc_nofs_restore(memalloc);
|
||||||
up_write(&dqopt->dqio_sem);
|
up_write(&dqopt->dqio_sem);
|
||||||
if (size != sizeof(struct v2_disk_dqinfo)) {
|
if (size != sizeof(struct v2_disk_dqinfo)) {
|
||||||
quota_error(sb, "Can't write info structure");
|
quota_error(sb, "Can't write info structure");
|
||||||
|
@ -328,11 +337,14 @@ static int v2_read_dquot(struct dquot *dquot)
|
||||||
{
|
{
|
||||||
struct quota_info *dqopt = sb_dqopt(dquot->dq_sb);
|
struct quota_info *dqopt = sb_dqopt(dquot->dq_sb);
|
||||||
int ret;
|
int ret;
|
||||||
|
unsigned int memalloc;
|
||||||
|
|
||||||
down_read(&dqopt->dqio_sem);
|
down_read(&dqopt->dqio_sem);
|
||||||
|
memalloc = memalloc_nofs_save();
|
||||||
ret = qtree_read_dquot(
|
ret = qtree_read_dquot(
|
||||||
sb_dqinfo(dquot->dq_sb, dquot->dq_id.type)->dqi_priv,
|
sb_dqinfo(dquot->dq_sb, dquot->dq_id.type)->dqi_priv,
|
||||||
dquot);
|
dquot);
|
||||||
|
memalloc_nofs_restore(memalloc);
|
||||||
up_read(&dqopt->dqio_sem);
|
up_read(&dqopt->dqio_sem);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -342,6 +354,7 @@ static int v2_write_dquot(struct dquot *dquot)
|
||||||
struct quota_info *dqopt = sb_dqopt(dquot->dq_sb);
|
struct quota_info *dqopt = sb_dqopt(dquot->dq_sb);
|
||||||
int ret;
|
int ret;
|
||||||
bool alloc = false;
|
bool alloc = false;
|
||||||
|
unsigned int memalloc;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If space for dquot is already allocated, we don't need any
|
* If space for dquot is already allocated, we don't need any
|
||||||
|
@ -355,9 +368,11 @@ static int v2_write_dquot(struct dquot *dquot)
|
||||||
} else {
|
} else {
|
||||||
down_read(&dqopt->dqio_sem);
|
down_read(&dqopt->dqio_sem);
|
||||||
}
|
}
|
||||||
|
memalloc = memalloc_nofs_save();
|
||||||
ret = qtree_write_dquot(
|
ret = qtree_write_dquot(
|
||||||
sb_dqinfo(dquot->dq_sb, dquot->dq_id.type)->dqi_priv,
|
sb_dqinfo(dquot->dq_sb, dquot->dq_id.type)->dqi_priv,
|
||||||
dquot);
|
dquot);
|
||||||
|
memalloc_nofs_restore(memalloc);
|
||||||
if (alloc)
|
if (alloc)
|
||||||
up_write(&dqopt->dqio_sem);
|
up_write(&dqopt->dqio_sem);
|
||||||
else
|
else
|
||||||
|
@ -368,10 +383,13 @@ static int v2_write_dquot(struct dquot *dquot)
|
||||||
static int v2_release_dquot(struct dquot *dquot)
|
static int v2_release_dquot(struct dquot *dquot)
|
||||||
{
|
{
|
||||||
struct quota_info *dqopt = sb_dqopt(dquot->dq_sb);
|
struct quota_info *dqopt = sb_dqopt(dquot->dq_sb);
|
||||||
|
unsigned int memalloc;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
down_write(&dqopt->dqio_sem);
|
down_write(&dqopt->dqio_sem);
|
||||||
|
memalloc = memalloc_nofs_save();
|
||||||
ret = qtree_release_dquot(sb_dqinfo(dquot->dq_sb, dquot->dq_id.type)->dqi_priv, dquot);
|
ret = qtree_release_dquot(sb_dqinfo(dquot->dq_sb, dquot->dq_id.type)->dqi_priv, dquot);
|
||||||
|
memalloc_nofs_restore(memalloc);
|
||||||
up_write(&dqopt->dqio_sem);
|
up_write(&dqopt->dqio_sem);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -386,10 +404,13 @@ static int v2_free_file_info(struct super_block *sb, int type)
|
||||||
static int v2_get_next_id(struct super_block *sb, struct kqid *qid)
|
static int v2_get_next_id(struct super_block *sb, struct kqid *qid)
|
||||||
{
|
{
|
||||||
struct quota_info *dqopt = sb_dqopt(sb);
|
struct quota_info *dqopt = sb_dqopt(sb);
|
||||||
|
unsigned int memalloc;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
down_read(&dqopt->dqio_sem);
|
down_read(&dqopt->dqio_sem);
|
||||||
|
memalloc = memalloc_nofs_save();
|
||||||
ret = qtree_get_next_id(sb_dqinfo(sb, qid->type)->dqi_priv, qid);
|
ret = qtree_get_next_id(sb_dqinfo(sb, qid->type)->dqi_priv, qid);
|
||||||
|
memalloc_nofs_restore(memalloc);
|
||||||
up_read(&dqopt->dqio_sem);
|
up_read(&dqopt->dqio_sem);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
@ -97,7 +97,7 @@ struct reiserfs_inode_info {
|
||||||
struct rw_semaphore i_xattr_sem;
|
struct rw_semaphore i_xattr_sem;
|
||||||
#endif
|
#endif
|
||||||
#ifdef CONFIG_QUOTA
|
#ifdef CONFIG_QUOTA
|
||||||
struct dquot *i_dquot[MAXQUOTAS];
|
struct dquot __rcu *i_dquot[MAXQUOTAS];
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
struct inode vfs_inode;
|
struct inode vfs_inode;
|
||||||
|
|
|
@ -801,7 +801,7 @@ static ssize_t reiserfs_quota_write(struct super_block *, int, const char *,
|
||||||
static ssize_t reiserfs_quota_read(struct super_block *, int, char *, size_t,
|
static ssize_t reiserfs_quota_read(struct super_block *, int, char *, size_t,
|
||||||
loff_t);
|
loff_t);
|
||||||
|
|
||||||
static struct dquot **reiserfs_get_dquots(struct inode *inode)
|
static struct dquot __rcu **reiserfs_get_dquots(struct inode *inode)
|
||||||
{
|
{
|
||||||
return REISERFS_I(inode)->i_dquot;
|
return REISERFS_I(inode)->i_dquot;
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,7 +67,7 @@ static int udf_readdir(struct file *file, struct dir_context *ctx)
|
||||||
pos_valid = true;
|
pos_valid = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
fname = kmalloc(UDF_NAME_LEN, GFP_NOFS);
|
fname = kmalloc(UDF_NAME_LEN, GFP_KERNEL);
|
||||||
if (!fname) {
|
if (!fname) {
|
||||||
ret = -ENOMEM;
|
ret = -ENOMEM;
|
||||||
goto out;
|
goto out;
|
||||||
|
|
|
@ -357,7 +357,7 @@ int udf_expand_file_adinicb(struct inode *inode)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
page = find_or_create_page(inode->i_mapping, 0, GFP_NOFS);
|
page = find_or_create_page(inode->i_mapping, 0, GFP_KERNEL);
|
||||||
if (!page)
|
if (!page)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
|
|
@ -59,7 +59,7 @@ static int udf_fiiter_find_entry(struct inode *dir, const struct qstr *child,
|
||||||
child->name[0] == '.' && child->name[1] == '.';
|
child->name[0] == '.' && child->name[1] == '.';
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
fname = kmalloc(UDF_NAME_LEN, GFP_NOFS);
|
fname = kmalloc(UDF_NAME_LEN, GFP_KERNEL);
|
||||||
if (!fname)
|
if (!fname)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
@ -566,7 +566,7 @@ out:
|
||||||
static int udf_symlink(struct mnt_idmap *idmap, struct inode *dir,
|
static int udf_symlink(struct mnt_idmap *idmap, struct inode *dir,
|
||||||
struct dentry *dentry, const char *symname)
|
struct dentry *dentry, const char *symname)
|
||||||
{
|
{
|
||||||
struct inode *inode = udf_new_inode(dir, S_IFLNK | 0777);
|
struct inode *inode;
|
||||||
struct pathComponent *pc;
|
struct pathComponent *pc;
|
||||||
const char *compstart;
|
const char *compstart;
|
||||||
struct extent_position epos = {};
|
struct extent_position epos = {};
|
||||||
|
@ -579,17 +579,20 @@ static int udf_symlink(struct mnt_idmap *idmap, struct inode *dir,
|
||||||
struct udf_inode_info *iinfo;
|
struct udf_inode_info *iinfo;
|
||||||
struct super_block *sb = dir->i_sb;
|
struct super_block *sb = dir->i_sb;
|
||||||
|
|
||||||
if (IS_ERR(inode))
|
name = kmalloc(UDF_NAME_LEN_CS0, GFP_KERNEL);
|
||||||
return PTR_ERR(inode);
|
if (!name) {
|
||||||
|
err = -ENOMEM;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
inode = udf_new_inode(dir, S_IFLNK | 0777);
|
||||||
|
if (IS_ERR(inode)) {
|
||||||
|
err = PTR_ERR(inode);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
iinfo = UDF_I(inode);
|
iinfo = UDF_I(inode);
|
||||||
down_write(&iinfo->i_data_sem);
|
down_write(&iinfo->i_data_sem);
|
||||||
name = kmalloc(UDF_NAME_LEN_CS0, GFP_NOFS);
|
|
||||||
if (!name) {
|
|
||||||
err = -ENOMEM;
|
|
||||||
goto out_no_entry;
|
|
||||||
}
|
|
||||||
|
|
||||||
inode->i_data.a_ops = &udf_symlink_aops;
|
inode->i_data.a_ops = &udf_symlink_aops;
|
||||||
inode->i_op = &udf_symlink_inode_operations;
|
inode->i_op = &udf_symlink_inode_operations;
|
||||||
inode_nohighmem(inode);
|
inode_nohighmem(inode);
|
||||||
|
|
551
fs/udf/super.c
551
fs/udf/super.c
|
@ -40,20 +40,20 @@
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/parser.h>
|
|
||||||
#include <linux/stat.h>
|
#include <linux/stat.h>
|
||||||
#include <linux/cdrom.h>
|
#include <linux/cdrom.h>
|
||||||
#include <linux/nls.h>
|
#include <linux/nls.h>
|
||||||
#include <linux/vfs.h>
|
#include <linux/vfs.h>
|
||||||
#include <linux/vmalloc.h>
|
#include <linux/vmalloc.h>
|
||||||
#include <linux/errno.h>
|
#include <linux/errno.h>
|
||||||
#include <linux/mount.h>
|
|
||||||
#include <linux/seq_file.h>
|
#include <linux/seq_file.h>
|
||||||
#include <linux/bitmap.h>
|
#include <linux/bitmap.h>
|
||||||
#include <linux/crc-itu-t.h>
|
#include <linux/crc-itu-t.h>
|
||||||
#include <linux/log2.h>
|
#include <linux/log2.h>
|
||||||
#include <asm/byteorder.h>
|
#include <asm/byteorder.h>
|
||||||
#include <linux/iversion.h>
|
#include <linux/iversion.h>
|
||||||
|
#include <linux/fs_context.h>
|
||||||
|
#include <linux/fs_parser.h>
|
||||||
|
|
||||||
#include "udf_sb.h"
|
#include "udf_sb.h"
|
||||||
#include "udf_i.h"
|
#include "udf_i.h"
|
||||||
|
@ -91,16 +91,20 @@ enum { UDF_MAX_LINKS = 0xffff };
|
||||||
#define UDF_MAX_FILESIZE (1ULL << 42)
|
#define UDF_MAX_FILESIZE (1ULL << 42)
|
||||||
|
|
||||||
/* These are the "meat" - everything else is stuffing */
|
/* These are the "meat" - everything else is stuffing */
|
||||||
static int udf_fill_super(struct super_block *, void *, int);
|
static int udf_fill_super(struct super_block *sb, struct fs_context *fc);
|
||||||
static void udf_put_super(struct super_block *);
|
static void udf_put_super(struct super_block *);
|
||||||
static int udf_sync_fs(struct super_block *, int);
|
static int udf_sync_fs(struct super_block *, int);
|
||||||
static int udf_remount_fs(struct super_block *, int *, char *);
|
|
||||||
static void udf_load_logicalvolint(struct super_block *, struct kernel_extent_ad);
|
static void udf_load_logicalvolint(struct super_block *, struct kernel_extent_ad);
|
||||||
static void udf_open_lvid(struct super_block *);
|
static void udf_open_lvid(struct super_block *);
|
||||||
static void udf_close_lvid(struct super_block *);
|
static void udf_close_lvid(struct super_block *);
|
||||||
static unsigned int udf_count_free(struct super_block *);
|
static unsigned int udf_count_free(struct super_block *);
|
||||||
static int udf_statfs(struct dentry *, struct kstatfs *);
|
static int udf_statfs(struct dentry *, struct kstatfs *);
|
||||||
static int udf_show_options(struct seq_file *, struct dentry *);
|
static int udf_show_options(struct seq_file *, struct dentry *);
|
||||||
|
static int udf_init_fs_context(struct fs_context *fc);
|
||||||
|
static int udf_parse_param(struct fs_context *fc, struct fs_parameter *param);
|
||||||
|
static int udf_reconfigure(struct fs_context *fc);
|
||||||
|
static void udf_free_fc(struct fs_context *fc);
|
||||||
|
static const struct fs_parameter_spec udf_param_spec[];
|
||||||
|
|
||||||
struct logicalVolIntegrityDescImpUse *udf_sb_lvidiu(struct super_block *sb)
|
struct logicalVolIntegrityDescImpUse *udf_sb_lvidiu(struct super_block *sb)
|
||||||
{
|
{
|
||||||
|
@ -119,18 +123,25 @@ struct logicalVolIntegrityDescImpUse *udf_sb_lvidiu(struct super_block *sb)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* UDF filesystem type */
|
/* UDF filesystem type */
|
||||||
static struct dentry *udf_mount(struct file_system_type *fs_type,
|
static int udf_get_tree(struct fs_context *fc)
|
||||||
int flags, const char *dev_name, void *data)
|
|
||||||
{
|
{
|
||||||
return mount_bdev(fs_type, flags, dev_name, data, udf_fill_super);
|
return get_tree_bdev(fc, udf_fill_super);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const struct fs_context_operations udf_context_ops = {
|
||||||
|
.parse_param = udf_parse_param,
|
||||||
|
.get_tree = udf_get_tree,
|
||||||
|
.reconfigure = udf_reconfigure,
|
||||||
|
.free = udf_free_fc,
|
||||||
|
};
|
||||||
|
|
||||||
static struct file_system_type udf_fstype = {
|
static struct file_system_type udf_fstype = {
|
||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
.name = "udf",
|
.name = "udf",
|
||||||
.mount = udf_mount,
|
|
||||||
.kill_sb = kill_block_super,
|
.kill_sb = kill_block_super,
|
||||||
.fs_flags = FS_REQUIRES_DEV,
|
.fs_flags = FS_REQUIRES_DEV,
|
||||||
|
.init_fs_context = udf_init_fs_context,
|
||||||
|
.parameters = udf_param_spec,
|
||||||
};
|
};
|
||||||
MODULE_ALIAS_FS("udf");
|
MODULE_ALIAS_FS("udf");
|
||||||
|
|
||||||
|
@ -203,12 +214,10 @@ static const struct super_operations udf_sb_ops = {
|
||||||
.put_super = udf_put_super,
|
.put_super = udf_put_super,
|
||||||
.sync_fs = udf_sync_fs,
|
.sync_fs = udf_sync_fs,
|
||||||
.statfs = udf_statfs,
|
.statfs = udf_statfs,
|
||||||
.remount_fs = udf_remount_fs,
|
|
||||||
.show_options = udf_show_options,
|
.show_options = udf_show_options,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct udf_options {
|
struct udf_options {
|
||||||
unsigned char novrs;
|
|
||||||
unsigned int blocksize;
|
unsigned int blocksize;
|
||||||
unsigned int session;
|
unsigned int session;
|
||||||
unsigned int lastblock;
|
unsigned int lastblock;
|
||||||
|
@ -222,6 +231,65 @@ struct udf_options {
|
||||||
struct nls_table *nls_map;
|
struct nls_table *nls_map;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* UDF has historically preserved prior mount options across
|
||||||
|
* a remount, so copy those here if remounting, otherwise set
|
||||||
|
* initial mount defaults.
|
||||||
|
*/
|
||||||
|
static void udf_init_options(struct fs_context *fc, struct udf_options *uopt)
|
||||||
|
{
|
||||||
|
if (fc->purpose == FS_CONTEXT_FOR_RECONFIGURE) {
|
||||||
|
struct super_block *sb = fc->root->d_sb;
|
||||||
|
struct udf_sb_info *sbi = UDF_SB(sb);
|
||||||
|
|
||||||
|
uopt->flags = sbi->s_flags;
|
||||||
|
uopt->uid = sbi->s_uid;
|
||||||
|
uopt->gid = sbi->s_gid;
|
||||||
|
uopt->umask = sbi->s_umask;
|
||||||
|
uopt->fmode = sbi->s_fmode;
|
||||||
|
uopt->dmode = sbi->s_dmode;
|
||||||
|
uopt->nls_map = NULL;
|
||||||
|
} else {
|
||||||
|
uopt->flags = (1 << UDF_FLAG_USE_AD_IN_ICB) |
|
||||||
|
(1 << UDF_FLAG_STRICT);
|
||||||
|
/*
|
||||||
|
* By default we'll use overflow[ug]id when UDF
|
||||||
|
* inode [ug]id == -1
|
||||||
|
*/
|
||||||
|
uopt->uid = make_kuid(current_user_ns(), overflowuid);
|
||||||
|
uopt->gid = make_kgid(current_user_ns(), overflowgid);
|
||||||
|
uopt->umask = 0;
|
||||||
|
uopt->fmode = UDF_INVALID_MODE;
|
||||||
|
uopt->dmode = UDF_INVALID_MODE;
|
||||||
|
uopt->nls_map = NULL;
|
||||||
|
uopt->session = 0xFFFFFFFF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int udf_init_fs_context(struct fs_context *fc)
|
||||||
|
{
|
||||||
|
struct udf_options *uopt;
|
||||||
|
|
||||||
|
uopt = kzalloc(sizeof(*uopt), GFP_KERNEL);
|
||||||
|
if (!uopt)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
udf_init_options(fc, uopt);
|
||||||
|
|
||||||
|
fc->fs_private = uopt;
|
||||||
|
fc->ops = &udf_context_ops;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void udf_free_fc(struct fs_context *fc)
|
||||||
|
{
|
||||||
|
struct udf_options *uopt = fc->fs_private;
|
||||||
|
|
||||||
|
unload_nls(uopt->nls_map);
|
||||||
|
kfree(fc->fs_private);
|
||||||
|
}
|
||||||
|
|
||||||
static int __init init_udf_fs(void)
|
static int __init init_udf_fs(void)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
|
@ -357,7 +425,7 @@ static int udf_show_options(struct seq_file *seq, struct dentry *root)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* udf_parse_options
|
* udf_parse_param
|
||||||
*
|
*
|
||||||
* PURPOSE
|
* PURPOSE
|
||||||
* Parse mount options.
|
* Parse mount options.
|
||||||
|
@ -400,12 +468,12 @@ static int udf_show_options(struct seq_file *seq, struct dentry *root)
|
||||||
* yield highly unpredictable results.
|
* yield highly unpredictable results.
|
||||||
*
|
*
|
||||||
* PRE-CONDITIONS
|
* PRE-CONDITIONS
|
||||||
* options Pointer to mount options string.
|
* fc fs_context with pointer to mount options variable.
|
||||||
* uopts Pointer to mount options variable.
|
* param Pointer to fs_parameter being parsed.
|
||||||
*
|
*
|
||||||
* POST-CONDITIONS
|
* POST-CONDITIONS
|
||||||
* <return> 1 Mount options parsed okay.
|
* <return> 0 Mount options parsed okay.
|
||||||
* <return> 0 Error parsing mount options.
|
* <return> errno Error parsing mount options.
|
||||||
*
|
*
|
||||||
* HISTORY
|
* HISTORY
|
||||||
* July 1, 1997 - Andrew E. Mileski
|
* July 1, 1997 - Andrew E. Mileski
|
||||||
|
@ -417,229 +485,193 @@ enum {
|
||||||
Opt_noadinicb, Opt_adinicb, Opt_shortad, Opt_longad,
|
Opt_noadinicb, Opt_adinicb, Opt_shortad, Opt_longad,
|
||||||
Opt_gid, Opt_uid, Opt_umask, Opt_session, Opt_lastblock,
|
Opt_gid, Opt_uid, Opt_umask, Opt_session, Opt_lastblock,
|
||||||
Opt_anchor, Opt_volume, Opt_partition, Opt_fileset,
|
Opt_anchor, Opt_volume, Opt_partition, Opt_fileset,
|
||||||
Opt_rootdir, Opt_utf8, Opt_iocharset,
|
Opt_rootdir, Opt_utf8, Opt_iocharset, Opt_err, Opt_fmode, Opt_dmode
|
||||||
Opt_err, Opt_uforget, Opt_uignore, Opt_gforget, Opt_gignore,
|
|
||||||
Opt_fmode, Opt_dmode
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static const match_table_t tokens = {
|
static const struct fs_parameter_spec udf_param_spec[] = {
|
||||||
{Opt_novrs, "novrs"},
|
fsparam_flag ("novrs", Opt_novrs),
|
||||||
{Opt_nostrict, "nostrict"},
|
fsparam_flag ("nostrict", Opt_nostrict),
|
||||||
{Opt_bs, "bs=%u"},
|
fsparam_u32 ("bs", Opt_bs),
|
||||||
{Opt_unhide, "unhide"},
|
fsparam_flag ("unhide", Opt_unhide),
|
||||||
{Opt_undelete, "undelete"},
|
fsparam_flag ("undelete", Opt_undelete),
|
||||||
{Opt_noadinicb, "noadinicb"},
|
fsparam_flag_no ("adinicb", Opt_adinicb),
|
||||||
{Opt_adinicb, "adinicb"},
|
fsparam_flag ("shortad", Opt_shortad),
|
||||||
{Opt_shortad, "shortad"},
|
fsparam_flag ("longad", Opt_longad),
|
||||||
{Opt_longad, "longad"},
|
fsparam_string ("gid", Opt_gid),
|
||||||
{Opt_uforget, "uid=forget"},
|
fsparam_string ("uid", Opt_uid),
|
||||||
{Opt_uignore, "uid=ignore"},
|
fsparam_u32 ("umask", Opt_umask),
|
||||||
{Opt_gforget, "gid=forget"},
|
fsparam_u32 ("session", Opt_session),
|
||||||
{Opt_gignore, "gid=ignore"},
|
fsparam_u32 ("lastblock", Opt_lastblock),
|
||||||
{Opt_gid, "gid=%u"},
|
fsparam_u32 ("anchor", Opt_anchor),
|
||||||
{Opt_uid, "uid=%u"},
|
fsparam_u32 ("volume", Opt_volume),
|
||||||
{Opt_umask, "umask=%o"},
|
fsparam_u32 ("partition", Opt_partition),
|
||||||
{Opt_session, "session=%u"},
|
fsparam_u32 ("fileset", Opt_fileset),
|
||||||
{Opt_lastblock, "lastblock=%u"},
|
fsparam_u32 ("rootdir", Opt_rootdir),
|
||||||
{Opt_anchor, "anchor=%u"},
|
fsparam_flag ("utf8", Opt_utf8),
|
||||||
{Opt_volume, "volume=%u"},
|
fsparam_string ("iocharset", Opt_iocharset),
|
||||||
{Opt_partition, "partition=%u"},
|
fsparam_u32 ("mode", Opt_fmode),
|
||||||
{Opt_fileset, "fileset=%u"},
|
fsparam_u32 ("dmode", Opt_dmode),
|
||||||
{Opt_rootdir, "rootdir=%u"},
|
{}
|
||||||
{Opt_utf8, "utf8"},
|
};
|
||||||
{Opt_iocharset, "iocharset=%s"},
|
|
||||||
{Opt_fmode, "mode=%o"},
|
|
||||||
{Opt_dmode, "dmode=%o"},
|
|
||||||
{Opt_err, NULL}
|
|
||||||
};
|
|
||||||
|
|
||||||
static int udf_parse_options(char *options, struct udf_options *uopt,
|
static int udf_parse_param(struct fs_context *fc, struct fs_parameter *param)
|
||||||
bool remount)
|
|
||||||
{
|
{
|
||||||
char *p;
|
|
||||||
int option;
|
|
||||||
unsigned int uv;
|
unsigned int uv;
|
||||||
|
unsigned int n;
|
||||||
|
struct udf_options *uopt = fc->fs_private;
|
||||||
|
struct fs_parse_result result;
|
||||||
|
int token;
|
||||||
|
bool remount = (fc->purpose & FS_CONTEXT_FOR_RECONFIGURE);
|
||||||
|
|
||||||
uopt->novrs = 0;
|
token = fs_parse(fc, udf_param_spec, param, &result);
|
||||||
uopt->session = 0xFFFFFFFF;
|
if (token < 0)
|
||||||
uopt->lastblock = 0;
|
return token;
|
||||||
uopt->anchor = 0;
|
|
||||||
|
|
||||||
if (!options)
|
switch (token) {
|
||||||
return 1;
|
case Opt_novrs:
|
||||||
|
uopt->flags |= (1 << UDF_FLAG_NOVRS);
|
||||||
while ((p = strsep(&options, ",")) != NULL) {
|
break;
|
||||||
substring_t args[MAX_OPT_ARGS];
|
case Opt_bs:
|
||||||
int token;
|
n = result.uint_32;
|
||||||
unsigned n;
|
if (n != 512 && n != 1024 && n != 2048 && n != 4096)
|
||||||
if (!*p)
|
return -EINVAL;
|
||||||
continue;
|
uopt->blocksize = n;
|
||||||
|
uopt->flags |= (1 << UDF_FLAG_BLOCKSIZE_SET);
|
||||||
token = match_token(p, tokens, args);
|
break;
|
||||||
switch (token) {
|
case Opt_unhide:
|
||||||
case Opt_novrs:
|
uopt->flags |= (1 << UDF_FLAG_UNHIDE);
|
||||||
uopt->novrs = 1;
|
break;
|
||||||
break;
|
case Opt_undelete:
|
||||||
case Opt_bs:
|
uopt->flags |= (1 << UDF_FLAG_UNDELETE);
|
||||||
if (match_int(&args[0], &option))
|
break;
|
||||||
return 0;
|
case Opt_adinicb:
|
||||||
n = option;
|
if (result.negated)
|
||||||
if (n != 512 && n != 1024 && n != 2048 && n != 4096)
|
|
||||||
return 0;
|
|
||||||
uopt->blocksize = n;
|
|
||||||
uopt->flags |= (1 << UDF_FLAG_BLOCKSIZE_SET);
|
|
||||||
break;
|
|
||||||
case Opt_unhide:
|
|
||||||
uopt->flags |= (1 << UDF_FLAG_UNHIDE);
|
|
||||||
break;
|
|
||||||
case Opt_undelete:
|
|
||||||
uopt->flags |= (1 << UDF_FLAG_UNDELETE);
|
|
||||||
break;
|
|
||||||
case Opt_noadinicb:
|
|
||||||
uopt->flags &= ~(1 << UDF_FLAG_USE_AD_IN_ICB);
|
uopt->flags &= ~(1 << UDF_FLAG_USE_AD_IN_ICB);
|
||||||
break;
|
else
|
||||||
case Opt_adinicb:
|
|
||||||
uopt->flags |= (1 << UDF_FLAG_USE_AD_IN_ICB);
|
uopt->flags |= (1 << UDF_FLAG_USE_AD_IN_ICB);
|
||||||
break;
|
break;
|
||||||
case Opt_shortad:
|
case Opt_shortad:
|
||||||
uopt->flags |= (1 << UDF_FLAG_USE_SHORT_AD);
|
uopt->flags |= (1 << UDF_FLAG_USE_SHORT_AD);
|
||||||
break;
|
break;
|
||||||
case Opt_longad:
|
case Opt_longad:
|
||||||
uopt->flags &= ~(1 << UDF_FLAG_USE_SHORT_AD);
|
uopt->flags &= ~(1 << UDF_FLAG_USE_SHORT_AD);
|
||||||
break;
|
break;
|
||||||
case Opt_gid:
|
case Opt_gid:
|
||||||
if (match_uint(args, &uv))
|
if (kstrtoint(param->string, 10, &uv) == 0) {
|
||||||
return 0;
|
kgid_t gid = make_kgid(current_user_ns(), uv);
|
||||||
uopt->gid = make_kgid(current_user_ns(), uv);
|
if (!gid_valid(gid))
|
||||||
if (!gid_valid(uopt->gid))
|
return -EINVAL;
|
||||||
return 0;
|
uopt->gid = gid;
|
||||||
uopt->flags |= (1 << UDF_FLAG_GID_SET);
|
uopt->flags |= (1 << UDF_FLAG_GID_SET);
|
||||||
break;
|
} else if (!strcmp(param->string, "forget")) {
|
||||||
case Opt_uid:
|
|
||||||
if (match_uint(args, &uv))
|
|
||||||
return 0;
|
|
||||||
uopt->uid = make_kuid(current_user_ns(), uv);
|
|
||||||
if (!uid_valid(uopt->uid))
|
|
||||||
return 0;
|
|
||||||
uopt->flags |= (1 << UDF_FLAG_UID_SET);
|
|
||||||
break;
|
|
||||||
case Opt_umask:
|
|
||||||
if (match_octal(args, &option))
|
|
||||||
return 0;
|
|
||||||
uopt->umask = option;
|
|
||||||
break;
|
|
||||||
case Opt_nostrict:
|
|
||||||
uopt->flags &= ~(1 << UDF_FLAG_STRICT);
|
|
||||||
break;
|
|
||||||
case Opt_session:
|
|
||||||
if (match_int(args, &option))
|
|
||||||
return 0;
|
|
||||||
uopt->session = option;
|
|
||||||
if (!remount)
|
|
||||||
uopt->flags |= (1 << UDF_FLAG_SESSION_SET);
|
|
||||||
break;
|
|
||||||
case Opt_lastblock:
|
|
||||||
if (match_int(args, &option))
|
|
||||||
return 0;
|
|
||||||
uopt->lastblock = option;
|
|
||||||
if (!remount)
|
|
||||||
uopt->flags |= (1 << UDF_FLAG_LASTBLOCK_SET);
|
|
||||||
break;
|
|
||||||
case Opt_anchor:
|
|
||||||
if (match_int(args, &option))
|
|
||||||
return 0;
|
|
||||||
uopt->anchor = option;
|
|
||||||
break;
|
|
||||||
case Opt_volume:
|
|
||||||
case Opt_partition:
|
|
||||||
case Opt_fileset:
|
|
||||||
case Opt_rootdir:
|
|
||||||
/* Ignored (never implemented properly) */
|
|
||||||
break;
|
|
||||||
case Opt_utf8:
|
|
||||||
if (!remount) {
|
|
||||||
unload_nls(uopt->nls_map);
|
|
||||||
uopt->nls_map = NULL;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Opt_iocharset:
|
|
||||||
if (!remount) {
|
|
||||||
unload_nls(uopt->nls_map);
|
|
||||||
uopt->nls_map = NULL;
|
|
||||||
}
|
|
||||||
/* When nls_map is not loaded then UTF-8 is used */
|
|
||||||
if (!remount && strcmp(args[0].from, "utf8") != 0) {
|
|
||||||
uopt->nls_map = load_nls(args[0].from);
|
|
||||||
if (!uopt->nls_map) {
|
|
||||||
pr_err("iocharset %s not found\n",
|
|
||||||
args[0].from);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Opt_uforget:
|
|
||||||
uopt->flags |= (1 << UDF_FLAG_UID_FORGET);
|
|
||||||
break;
|
|
||||||
case Opt_uignore:
|
|
||||||
case Opt_gignore:
|
|
||||||
/* These options are superseeded by uid=<number> */
|
|
||||||
break;
|
|
||||||
case Opt_gforget:
|
|
||||||
uopt->flags |= (1 << UDF_FLAG_GID_FORGET);
|
uopt->flags |= (1 << UDF_FLAG_GID_FORGET);
|
||||||
break;
|
} else if (!strcmp(param->string, "ignore")) {
|
||||||
case Opt_fmode:
|
/* this option is superseded by gid=<number> */
|
||||||
if (match_octal(args, &option))
|
;
|
||||||
return 0;
|
} else {
|
||||||
uopt->fmode = option & 0777;
|
return -EINVAL;
|
||||||
break;
|
|
||||||
case Opt_dmode:
|
|
||||||
if (match_octal(args, &option))
|
|
||||||
return 0;
|
|
||||||
uopt->dmode = option & 0777;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
pr_err("bad mount option \"%s\" or missing value\n", p);
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
case Opt_uid:
|
||||||
|
if (kstrtoint(param->string, 10, &uv) == 0) {
|
||||||
|
kuid_t uid = make_kuid(current_user_ns(), uv);
|
||||||
|
if (!uid_valid(uid))
|
||||||
|
return -EINVAL;
|
||||||
|
uopt->uid = uid;
|
||||||
|
uopt->flags |= (1 << UDF_FLAG_UID_SET);
|
||||||
|
} else if (!strcmp(param->string, "forget")) {
|
||||||
|
uopt->flags |= (1 << UDF_FLAG_UID_FORGET);
|
||||||
|
} else if (!strcmp(param->string, "ignore")) {
|
||||||
|
/* this option is superseded by uid=<number> */
|
||||||
|
;
|
||||||
|
} else {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Opt_umask:
|
||||||
|
uopt->umask = result.uint_32;
|
||||||
|
break;
|
||||||
|
case Opt_nostrict:
|
||||||
|
uopt->flags &= ~(1 << UDF_FLAG_STRICT);
|
||||||
|
break;
|
||||||
|
case Opt_session:
|
||||||
|
uopt->session = result.uint_32;
|
||||||
|
if (!remount)
|
||||||
|
uopt->flags |= (1 << UDF_FLAG_SESSION_SET);
|
||||||
|
break;
|
||||||
|
case Opt_lastblock:
|
||||||
|
uopt->lastblock = result.uint_32;
|
||||||
|
if (!remount)
|
||||||
|
uopt->flags |= (1 << UDF_FLAG_LASTBLOCK_SET);
|
||||||
|
break;
|
||||||
|
case Opt_anchor:
|
||||||
|
uopt->anchor = result.uint_32;
|
||||||
|
break;
|
||||||
|
case Opt_volume:
|
||||||
|
case Opt_partition:
|
||||||
|
case Opt_fileset:
|
||||||
|
case Opt_rootdir:
|
||||||
|
/* Ignored (never implemented properly) */
|
||||||
|
break;
|
||||||
|
case Opt_utf8:
|
||||||
|
if (!remount) {
|
||||||
|
unload_nls(uopt->nls_map);
|
||||||
|
uopt->nls_map = NULL;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Opt_iocharset:
|
||||||
|
if (!remount) {
|
||||||
|
unload_nls(uopt->nls_map);
|
||||||
|
uopt->nls_map = NULL;
|
||||||
|
}
|
||||||
|
/* When nls_map is not loaded then UTF-8 is used */
|
||||||
|
if (!remount && strcmp(param->string, "utf8") != 0) {
|
||||||
|
uopt->nls_map = load_nls(param->string);
|
||||||
|
if (!uopt->nls_map) {
|
||||||
|
errorf(fc, "iocharset %s not found",
|
||||||
|
param->string);
|
||||||
|
return -EINVAL;;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Opt_fmode:
|
||||||
|
uopt->fmode = result.uint_32 & 0777;
|
||||||
|
break;
|
||||||
|
case Opt_dmode:
|
||||||
|
uopt->dmode = result.uint_32 & 0777;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
return 1;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int udf_remount_fs(struct super_block *sb, int *flags, char *options)
|
static int udf_reconfigure(struct fs_context *fc)
|
||||||
{
|
{
|
||||||
struct udf_options uopt;
|
struct udf_options *uopt = fc->fs_private;
|
||||||
|
struct super_block *sb = fc->root->d_sb;
|
||||||
struct udf_sb_info *sbi = UDF_SB(sb);
|
struct udf_sb_info *sbi = UDF_SB(sb);
|
||||||
|
int readonly = fc->sb_flags & SB_RDONLY;
|
||||||
int error = 0;
|
int error = 0;
|
||||||
|
|
||||||
if (!(*flags & SB_RDONLY) && UDF_QUERY_FLAG(sb, UDF_FLAG_RW_INCOMPAT))
|
if (!readonly && UDF_QUERY_FLAG(sb, UDF_FLAG_RW_INCOMPAT))
|
||||||
return -EACCES;
|
return -EACCES;
|
||||||
|
|
||||||
sync_filesystem(sb);
|
sync_filesystem(sb);
|
||||||
|
|
||||||
uopt.flags = sbi->s_flags;
|
|
||||||
uopt.uid = sbi->s_uid;
|
|
||||||
uopt.gid = sbi->s_gid;
|
|
||||||
uopt.umask = sbi->s_umask;
|
|
||||||
uopt.fmode = sbi->s_fmode;
|
|
||||||
uopt.dmode = sbi->s_dmode;
|
|
||||||
uopt.nls_map = NULL;
|
|
||||||
|
|
||||||
if (!udf_parse_options(options, &uopt, true))
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
write_lock(&sbi->s_cred_lock);
|
write_lock(&sbi->s_cred_lock);
|
||||||
sbi->s_flags = uopt.flags;
|
sbi->s_flags = uopt->flags;
|
||||||
sbi->s_uid = uopt.uid;
|
sbi->s_uid = uopt->uid;
|
||||||
sbi->s_gid = uopt.gid;
|
sbi->s_gid = uopt->gid;
|
||||||
sbi->s_umask = uopt.umask;
|
sbi->s_umask = uopt->umask;
|
||||||
sbi->s_fmode = uopt.fmode;
|
sbi->s_fmode = uopt->fmode;
|
||||||
sbi->s_dmode = uopt.dmode;
|
sbi->s_dmode = uopt->dmode;
|
||||||
write_unlock(&sbi->s_cred_lock);
|
write_unlock(&sbi->s_cred_lock);
|
||||||
|
|
||||||
if ((bool)(*flags & SB_RDONLY) == sb_rdonly(sb))
|
if (readonly == sb_rdonly(sb))
|
||||||
goto out_unlock;
|
goto out_unlock;
|
||||||
|
|
||||||
if (*flags & SB_RDONLY)
|
if (readonly)
|
||||||
udf_close_lvid(sb);
|
udf_close_lvid(sb);
|
||||||
else
|
else
|
||||||
udf_open_lvid(sb);
|
udf_open_lvid(sb);
|
||||||
|
@ -863,7 +895,7 @@ static int udf_load_pvoldesc(struct super_block *sb, sector_t block)
|
||||||
int ret;
|
int ret;
|
||||||
struct timestamp *ts;
|
struct timestamp *ts;
|
||||||
|
|
||||||
outstr = kmalloc(128, GFP_NOFS);
|
outstr = kmalloc(128, GFP_KERNEL);
|
||||||
if (!outstr)
|
if (!outstr)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
@ -1538,6 +1570,20 @@ out_bh:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool udf_lvid_valid(struct super_block *sb,
|
||||||
|
struct logicalVolIntegrityDesc *lvid)
|
||||||
|
{
|
||||||
|
u32 parts, impuselen;
|
||||||
|
|
||||||
|
parts = le32_to_cpu(lvid->numOfPartitions);
|
||||||
|
impuselen = le32_to_cpu(lvid->lengthOfImpUse);
|
||||||
|
if (parts >= sb->s_blocksize || impuselen >= sb->s_blocksize ||
|
||||||
|
sizeof(struct logicalVolIntegrityDesc) + impuselen +
|
||||||
|
2 * parts * sizeof(u32) > sb->s_blocksize)
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Find the prevailing Logical Volume Integrity Descriptor.
|
* Find the prevailing Logical Volume Integrity Descriptor.
|
||||||
*/
|
*/
|
||||||
|
@ -1548,7 +1594,6 @@ static void udf_load_logicalvolint(struct super_block *sb, struct kernel_extent_
|
||||||
struct udf_sb_info *sbi = UDF_SB(sb);
|
struct udf_sb_info *sbi = UDF_SB(sb);
|
||||||
struct logicalVolIntegrityDesc *lvid;
|
struct logicalVolIntegrityDesc *lvid;
|
||||||
int indirections = 0;
|
int indirections = 0;
|
||||||
u32 parts, impuselen;
|
|
||||||
|
|
||||||
while (++indirections <= UDF_MAX_LVID_NESTING) {
|
while (++indirections <= UDF_MAX_LVID_NESTING) {
|
||||||
final_bh = NULL;
|
final_bh = NULL;
|
||||||
|
@ -1570,32 +1615,27 @@ static void udf_load_logicalvolint(struct super_block *sb, struct kernel_extent_
|
||||||
if (!final_bh)
|
if (!final_bh)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
brelse(sbi->s_lvid_bh);
|
|
||||||
sbi->s_lvid_bh = final_bh;
|
|
||||||
|
|
||||||
lvid = (struct logicalVolIntegrityDesc *)final_bh->b_data;
|
lvid = (struct logicalVolIntegrityDesc *)final_bh->b_data;
|
||||||
|
if (udf_lvid_valid(sb, lvid)) {
|
||||||
|
brelse(sbi->s_lvid_bh);
|
||||||
|
sbi->s_lvid_bh = final_bh;
|
||||||
|
} else {
|
||||||
|
udf_warn(sb, "Corrupted LVID (parts=%u, impuselen=%u), "
|
||||||
|
"ignoring.\n",
|
||||||
|
le32_to_cpu(lvid->numOfPartitions),
|
||||||
|
le32_to_cpu(lvid->lengthOfImpUse));
|
||||||
|
}
|
||||||
|
|
||||||
if (lvid->nextIntegrityExt.extLength == 0)
|
if (lvid->nextIntegrityExt.extLength == 0)
|
||||||
goto check;
|
return;
|
||||||
|
|
||||||
loc = leea_to_cpu(lvid->nextIntegrityExt);
|
loc = leea_to_cpu(lvid->nextIntegrityExt);
|
||||||
}
|
}
|
||||||
|
|
||||||
udf_warn(sb, "Too many LVID indirections (max %u), ignoring.\n",
|
udf_warn(sb, "Too many LVID indirections (max %u), ignoring.\n",
|
||||||
UDF_MAX_LVID_NESTING);
|
UDF_MAX_LVID_NESTING);
|
||||||
out_err:
|
|
||||||
brelse(sbi->s_lvid_bh);
|
brelse(sbi->s_lvid_bh);
|
||||||
sbi->s_lvid_bh = NULL;
|
sbi->s_lvid_bh = NULL;
|
||||||
return;
|
|
||||||
check:
|
|
||||||
parts = le32_to_cpu(lvid->numOfPartitions);
|
|
||||||
impuselen = le32_to_cpu(lvid->lengthOfImpUse);
|
|
||||||
if (parts >= sb->s_blocksize || impuselen >= sb->s_blocksize ||
|
|
||||||
sizeof(struct logicalVolIntegrityDesc) + impuselen +
|
|
||||||
2 * parts * sizeof(u32) > sb->s_blocksize) {
|
|
||||||
udf_warn(sb, "Corrupted LVID (parts=%u, impuselen=%u), "
|
|
||||||
"ignoring.\n", parts, impuselen);
|
|
||||||
goto out_err;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1945,7 +1985,7 @@ static int udf_load_vrs(struct super_block *sb, struct udf_options *uopt,
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
sbi->s_last_block = uopt->lastblock;
|
sbi->s_last_block = uopt->lastblock;
|
||||||
if (!uopt->novrs) {
|
if (!UDF_QUERY_FLAG(sb, UDF_FLAG_NOVRS)) {
|
||||||
/* Check that it is NSR02 compliant */
|
/* Check that it is NSR02 compliant */
|
||||||
nsr = udf_check_vsd(sb);
|
nsr = udf_check_vsd(sb);
|
||||||
if (!nsr) {
|
if (!nsr) {
|
||||||
|
@ -2083,23 +2123,15 @@ u64 lvid_get_unique_id(struct super_block *sb)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int udf_fill_super(struct super_block *sb, void *options, int silent)
|
static int udf_fill_super(struct super_block *sb, struct fs_context *fc)
|
||||||
{
|
{
|
||||||
int ret = -EINVAL;
|
int ret = -EINVAL;
|
||||||
struct inode *inode = NULL;
|
struct inode *inode = NULL;
|
||||||
struct udf_options uopt;
|
struct udf_options *uopt = fc->fs_private;
|
||||||
struct kernel_lb_addr rootdir, fileset;
|
struct kernel_lb_addr rootdir, fileset;
|
||||||
struct udf_sb_info *sbi;
|
struct udf_sb_info *sbi;
|
||||||
bool lvid_open = false;
|
bool lvid_open = false;
|
||||||
|
int silent = fc->sb_flags & SB_SILENT;
|
||||||
uopt.flags = (1 << UDF_FLAG_USE_AD_IN_ICB) | (1 << UDF_FLAG_STRICT);
|
|
||||||
/* By default we'll use overflow[ug]id when UDF inode [ug]id == -1 */
|
|
||||||
uopt.uid = make_kuid(current_user_ns(), overflowuid);
|
|
||||||
uopt.gid = make_kgid(current_user_ns(), overflowgid);
|
|
||||||
uopt.umask = 0;
|
|
||||||
uopt.fmode = UDF_INVALID_MODE;
|
|
||||||
uopt.dmode = UDF_INVALID_MODE;
|
|
||||||
uopt.nls_map = NULL;
|
|
||||||
|
|
||||||
sbi = kzalloc(sizeof(*sbi), GFP_KERNEL);
|
sbi = kzalloc(sizeof(*sbi), GFP_KERNEL);
|
||||||
if (!sbi)
|
if (!sbi)
|
||||||
|
@ -2109,25 +2141,23 @@ static int udf_fill_super(struct super_block *sb, void *options, int silent)
|
||||||
|
|
||||||
mutex_init(&sbi->s_alloc_mutex);
|
mutex_init(&sbi->s_alloc_mutex);
|
||||||
|
|
||||||
if (!udf_parse_options((char *)options, &uopt, false))
|
|
||||||
goto parse_options_failure;
|
|
||||||
|
|
||||||
fileset.logicalBlockNum = 0xFFFFFFFF;
|
fileset.logicalBlockNum = 0xFFFFFFFF;
|
||||||
fileset.partitionReferenceNum = 0xFFFF;
|
fileset.partitionReferenceNum = 0xFFFF;
|
||||||
|
|
||||||
sbi->s_flags = uopt.flags;
|
sbi->s_flags = uopt->flags;
|
||||||
sbi->s_uid = uopt.uid;
|
sbi->s_uid = uopt->uid;
|
||||||
sbi->s_gid = uopt.gid;
|
sbi->s_gid = uopt->gid;
|
||||||
sbi->s_umask = uopt.umask;
|
sbi->s_umask = uopt->umask;
|
||||||
sbi->s_fmode = uopt.fmode;
|
sbi->s_fmode = uopt->fmode;
|
||||||
sbi->s_dmode = uopt.dmode;
|
sbi->s_dmode = uopt->dmode;
|
||||||
sbi->s_nls_map = uopt.nls_map;
|
sbi->s_nls_map = uopt->nls_map;
|
||||||
|
uopt->nls_map = NULL;
|
||||||
rwlock_init(&sbi->s_cred_lock);
|
rwlock_init(&sbi->s_cred_lock);
|
||||||
|
|
||||||
if (uopt.session == 0xFFFFFFFF)
|
if (uopt->session == 0xFFFFFFFF)
|
||||||
sbi->s_session = udf_get_last_session(sb);
|
sbi->s_session = udf_get_last_session(sb);
|
||||||
else
|
else
|
||||||
sbi->s_session = uopt.session;
|
sbi->s_session = uopt->session;
|
||||||
|
|
||||||
udf_debug("Multi-session=%d\n", sbi->s_session);
|
udf_debug("Multi-session=%d\n", sbi->s_session);
|
||||||
|
|
||||||
|
@ -2138,16 +2168,16 @@ static int udf_fill_super(struct super_block *sb, void *options, int silent)
|
||||||
sb->s_magic = UDF_SUPER_MAGIC;
|
sb->s_magic = UDF_SUPER_MAGIC;
|
||||||
sb->s_time_gran = 1000;
|
sb->s_time_gran = 1000;
|
||||||
|
|
||||||
if (uopt.flags & (1 << UDF_FLAG_BLOCKSIZE_SET)) {
|
if (uopt->flags & (1 << UDF_FLAG_BLOCKSIZE_SET)) {
|
||||||
ret = udf_load_vrs(sb, &uopt, silent, &fileset);
|
ret = udf_load_vrs(sb, uopt, silent, &fileset);
|
||||||
} else {
|
} else {
|
||||||
uopt.blocksize = bdev_logical_block_size(sb->s_bdev);
|
uopt->blocksize = bdev_logical_block_size(sb->s_bdev);
|
||||||
while (uopt.blocksize <= 4096) {
|
while (uopt->blocksize <= 4096) {
|
||||||
ret = udf_load_vrs(sb, &uopt, silent, &fileset);
|
ret = udf_load_vrs(sb, uopt, silent, &fileset);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
if (!silent && ret != -EACCES) {
|
if (!silent && ret != -EACCES) {
|
||||||
pr_notice("Scanning with blocksize %u failed\n",
|
pr_notice("Scanning with blocksize %u failed\n",
|
||||||
uopt.blocksize);
|
uopt->blocksize);
|
||||||
}
|
}
|
||||||
brelse(sbi->s_lvid_bh);
|
brelse(sbi->s_lvid_bh);
|
||||||
sbi->s_lvid_bh = NULL;
|
sbi->s_lvid_bh = NULL;
|
||||||
|
@ -2160,7 +2190,7 @@ static int udf_fill_super(struct super_block *sb, void *options, int silent)
|
||||||
} else
|
} else
|
||||||
break;
|
break;
|
||||||
|
|
||||||
uopt.blocksize <<= 1;
|
uopt->blocksize <<= 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
|
@ -2265,8 +2295,7 @@ static int udf_fill_super(struct super_block *sb, void *options, int silent)
|
||||||
|
|
||||||
error_out:
|
error_out:
|
||||||
iput(sbi->s_vat_inode);
|
iput(sbi->s_vat_inode);
|
||||||
parse_options_failure:
|
unload_nls(uopt->nls_map);
|
||||||
unload_nls(uopt.nls_map);
|
|
||||||
if (lvid_open)
|
if (lvid_open)
|
||||||
udf_close_lvid(sb);
|
udf_close_lvid(sb);
|
||||||
brelse(sbi->s_lvid_bh);
|
brelse(sbi->s_lvid_bh);
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
#define UDF_FLAG_STRICT 5
|
#define UDF_FLAG_STRICT 5
|
||||||
#define UDF_FLAG_UNDELETE 6
|
#define UDF_FLAG_UNDELETE 6
|
||||||
#define UDF_FLAG_UNHIDE 7
|
#define UDF_FLAG_UNHIDE 7
|
||||||
|
#define UDF_FLAG_NOVRS 8
|
||||||
#define UDF_FLAG_UID_FORGET 11 /* save -1 for uid to disk */
|
#define UDF_FLAG_UID_FORGET 11 /* save -1 for uid to disk */
|
||||||
#define UDF_FLAG_GID_FORGET 12
|
#define UDF_FLAG_GID_FORGET 12
|
||||||
#define UDF_FLAG_UID_SET 13
|
#define UDF_FLAG_UID_SET 13
|
||||||
|
|
|
@ -2179,7 +2179,7 @@ struct super_operations {
|
||||||
#ifdef CONFIG_QUOTA
|
#ifdef CONFIG_QUOTA
|
||||||
ssize_t (*quota_read)(struct super_block *, int, char *, size_t, loff_t);
|
ssize_t (*quota_read)(struct super_block *, int, char *, size_t, loff_t);
|
||||||
ssize_t (*quota_write)(struct super_block *, int, const char *, size_t, loff_t);
|
ssize_t (*quota_write)(struct super_block *, int, const char *, size_t, loff_t);
|
||||||
struct dquot **(*get_dquots)(struct inode *);
|
struct dquot __rcu **(*get_dquots)(struct inode *);
|
||||||
#endif
|
#endif
|
||||||
long (*nr_cached_objects)(struct super_block *,
|
long (*nr_cached_objects)(struct super_block *,
|
||||||
struct shrink_control *);
|
struct shrink_control *);
|
||||||
|
|
|
@ -37,7 +37,7 @@ struct shmem_inode_info {
|
||||||
unsigned int fsflags; /* for FS_IOC_[SG]ETFLAGS */
|
unsigned int fsflags; /* for FS_IOC_[SG]ETFLAGS */
|
||||||
atomic_t stop_eviction; /* hold when working on inode */
|
atomic_t stop_eviction; /* hold when working on inode */
|
||||||
#ifdef CONFIG_TMPFS_QUOTA
|
#ifdef CONFIG_TMPFS_QUOTA
|
||||||
struct dquot *i_dquot[MAXQUOTAS];
|
struct dquot __rcu *i_dquot[MAXQUOTAS];
|
||||||
#endif
|
#endif
|
||||||
struct inode vfs_inode;
|
struct inode vfs_inode;
|
||||||
};
|
};
|
||||||
|
|
|
@ -317,7 +317,7 @@ static void shmem_disable_quotas(struct super_block *sb)
|
||||||
dquot_quota_off(sb, type);
|
dquot_quota_off(sb, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct dquot **shmem_get_dquots(struct inode *inode)
|
static struct dquot __rcu **shmem_get_dquots(struct inode *inode)
|
||||||
{
|
{
|
||||||
return SHMEM_I(inode)->i_dquot;
|
return SHMEM_I(inode)->i_dquot;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue