Commit 04a6c897 authored by Jan Kara's avatar Jan Kara Committed by Linus Torvalds

[PATCH] Fix of quota deadlock on pagelock: reiserfs

Implement quota journaling and quota reading and writing functions for
reiserfs.  Solves also several other deadlocks possible for reiserfs due to
the lock inversion on journal_begin and quota locks.

From: Vladimir Saveliev <vs@namesys.com>

When CONFIG_QUOTA is defined reiserfs's finish_unfinished sets and clears
MS_ACTIVE bit in s_flags field of super block.  If that bit was set already
it should not be set.
Signed-off-by: default avatarJan Kara <jack@suse.cz>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 98887122
......@@ -54,7 +54,7 @@ static int reiserfs_file_release (struct inode * inode, struct file * filp)
/* freeing preallocation only involves relogging blocks that
* are already in the current transaction. preallocation gets
* freed at the end of each transaction, so it is impossible for
* us to log any additional blocks
* us to log any additional blocks (including quota blocks)
*/
err = journal_begin(&th, inode->i_sb, 1);
if (err) {
......@@ -201,7 +201,7 @@ int reiserfs_allocate_blocks_for_region(
/* If we came here, it means we absolutely need to open a transaction,
since we need to allocate some blocks */
reiserfs_write_lock(inode->i_sb); // Journaling stuff and we need that.
res = journal_begin(th, inode->i_sb, JOURNAL_PER_BALANCE_CNT * 3 + 1); // Wish I know if this number enough
res = journal_begin(th, inode->i_sb, JOURNAL_PER_BALANCE_CNT * 3 + 1 + 2 * REISERFS_QUOTA_TRANS_BLOCKS); // Wish I know if this number enough
if (res)
goto error_exit;
reiserfs_update_inode_transaction(inode) ;
......@@ -576,7 +576,7 @@ int reiserfs_allocate_blocks_for_region(
int err;
// update any changes we made to blk count
reiserfs_update_sd(th, inode);
err = journal_end(th, inode->i_sb, JOURNAL_PER_BALANCE_CNT * 3 + 1);
err = journal_end(th, inode->i_sb, JOURNAL_PER_BALANCE_CNT * 3 + 1 + 2 * REISERFS_QUOTA_TRANS_BLOCKS);
if (err)
res = err;
}
......
......@@ -20,27 +20,17 @@
extern int reiserfs_default_io_size; /* default io size devuned in super.c */
/* args for the create parameter of reiserfs_get_block */
#define GET_BLOCK_NO_CREATE 0 /* don't create new blocks or convert tails */
#define GET_BLOCK_CREATE 1 /* add anything you need to find block */
#define GET_BLOCK_NO_HOLE 2 /* return -ENOENT for file holes */
#define GET_BLOCK_READ_DIRECT 4 /* read the tail if indirect item not found */
#define GET_BLOCK_NO_ISEM 8 /* i_sem is not held, don't preallocate */
#define GET_BLOCK_NO_DANGLE 16 /* don't leave any transactions running */
static int reiserfs_get_block (struct inode * inode, sector_t block,
struct buffer_head * bh_result, int create);
static int reiserfs_commit_write(struct file *f, struct page *page,
unsigned from, unsigned to);
void reiserfs_delete_inode (struct inode * inode)
{
int jbegin_count = JOURNAL_PER_BALANCE_CNT * 2;
/* We need blocks for transaction + (user+group) quota update (possibly delete) */
int jbegin_count = JOURNAL_PER_BALANCE_CNT * 2 + 2 * REISERFS_QUOTA_INIT_BLOCKS;
struct reiserfs_transaction_handle th ;
reiserfs_write_lock(inode->i_sb);
DQUOT_FREE_INODE(inode);
/* The = 0 happens when we abort creating a new inode for some reason like lack of space.. */
if (!(inode->i_state & I_NEW) && INODE_PKEY(inode)->k_objectid != 0) { /* also handles bad_inode case */
down (&inode->i_sem);
......@@ -58,6 +48,11 @@ void reiserfs_delete_inode (struct inode * inode)
goto out;
}
/* Do quota update inside a transaction for journaled quotas. We must do that
* after delete_object so that quota updates go into the same transaction as
* stat data deletion */
DQUOT_FREE_INODE(inode);
if (journal_end(&th, inode->i_sb, jbegin_count)) {
up (&inode->i_sem);
goto out;
......@@ -592,8 +587,9 @@ int reiserfs_get_block (struct inode * inode, sector_t block,
. 3 balancings in direct->indirect conversion
. 1 block involved into reiserfs_update_sd()
XXX in practically impossible worst case direct2indirect()
can incur (much) more that 3 balancings. */
int jbegin_count = JOURNAL_PER_BALANCE_CNT * 3 + 1;
can incur (much) more than 3 balancings.
quota update for user, group */
int jbegin_count = JOURNAL_PER_BALANCE_CNT * 3 + 1 + 2 * REISERFS_QUOTA_TRANS_BLOCKS;
int version;
int dangle = 1;
loff_t new_offset = (((loff_t)block) << inode->i_sb->s_blocksize_bits) + 1 ;
......@@ -1699,6 +1695,10 @@ int reiserfs_new_inode (struct reiserfs_transaction_handle *th,
BUG_ON (!th->t_trans_id);
if (DQUOT_ALLOC_INODE(inode)) {
err = -EDQUOT;
goto out_end_trans;
}
if (!dir || !dir->i_nlink) {
err = -EPERM;
goto out_bad_inode;
......@@ -1866,9 +1866,12 @@ int reiserfs_new_inode (struct reiserfs_transaction_handle *th,
/* Invalidate the object, nothing was inserted yet */
INODE_PKEY(inode)->k_objectid = 0;
/* dquot_drop must be done outside a transaction */
journal_end(th, th->t_super, th->t_blocks_allocated) ;
/* Quota change must be inside a transaction for journaling */
DQUOT_FREE_INODE(inode);
out_end_trans:
journal_end(th, th->t_super, th->t_blocks_allocated) ;
/* Drop can be outside and it needs more credits so it's better to have it outside */
DQUOT_DROP(inode);
inode->i_flags |= S_NOQUOTA;
make_bad_inode(inode);
......@@ -2796,8 +2799,25 @@ int reiserfs_setattr(struct dentry *dentry, struct iattr *attr) {
(ia_valid & ATTR_GID && attr->ia_gid != inode->i_gid)) {
error = reiserfs_chown_xattrs (inode, attr);
if (!error)
if (!error) {
struct reiserfs_transaction_handle th;
/* (user+group)*(old+new) structure - we count quota info and , inode write (sb, inode) */
journal_begin(&th, inode->i_sb, 4*REISERFS_QUOTA_INIT_BLOCKS+2);
error = DQUOT_TRANSFER(inode, attr) ? -EDQUOT : 0;
if (error) {
journal_end(&th, inode->i_sb, 4*REISERFS_QUOTA_INIT_BLOCKS+2);
goto out;
}
/* Update corresponding info in inode so that everything is in
* one transaction */
if (attr->ia_valid & ATTR_UID)
inode->i_uid = attr->ia_uid;
if (attr->ia_valid & ATTR_GID)
inode->i_gid = attr->ia_gid;
mark_inode_dirty(inode);
journal_end(&th, inode->i_sb, 4*REISERFS_QUOTA_INIT_BLOCKS+2);
}
}
if (!error)
error = inode_setattr(inode, attr) ;
......
......@@ -545,7 +545,7 @@ static int reiserfs_add_entry (struct reiserfs_transaction_handle *th, struct in
/* quota utility function, call if you've had to abort after calling
** new_inode_init, and have not called reiserfs_new_inode yet.
** This should only be called on inodes that do not hav stat data
** This should only be called on inodes that do not have stat data
** inserted into the tree yet.
*/
static int drop_new_inode(struct inode *inode) {
......@@ -557,10 +557,9 @@ static int drop_new_inode(struct inode *inode) {
}
/* utility function that does setup for reiserfs_new_inode.
** DQUOT_ALLOC_INODE cannot be called inside a transaction, so we had
** to pull some bits of reiserfs_new_inode out into this func.
** Yes, the actual quota calls are missing, they are part of the quota
** patch.
** DQUOT_INIT needs lots of credits so it's better to have it
** outside of a transaction, so we had to pull some bits of
** reiserfs_new_inode out into this func.
*/
static int new_inode_init(struct inode *inode, struct inode *dir, int mode) {
......@@ -578,10 +577,6 @@ static int new_inode_init(struct inode *inode, struct inode *dir, int mode) {
inode->i_gid = current->fsgid;
}
DQUOT_INIT(inode);
if (DQUOT_ALLOC_INODE(inode)) {
drop_new_inode(inode);
return -EDQUOT;
}
return 0 ;
}
......@@ -590,16 +585,15 @@ static int reiserfs_create (struct inode * dir, struct dentry *dentry, int mode,
{
int retval;
struct inode * inode;
int jbegin_count = JOURNAL_PER_BALANCE_CNT * 2 ;
/* We need blocks for transaction + (user+group)*(quotas for new inode + update of quota for directory owner) */
int jbegin_count = JOURNAL_PER_BALANCE_CNT * 2 + 2 * (REISERFS_QUOTA_INIT_BLOCKS+REISERFS_QUOTA_TRANS_BLOCKS);
struct reiserfs_transaction_handle th ;
int locked;
if (!(inode = new_inode(dir->i_sb))) {
return -ENOMEM ;
}
retval = new_inode_init(inode, dir, mode);
if (retval)
return retval;
new_inode_init(inode, dir, mode);
locked = reiserfs_cache_default_acl (dir);
......@@ -658,7 +652,8 @@ static int reiserfs_mknod (struct inode * dir, struct dentry *dentry, int mode,
int retval;
struct inode * inode;
struct reiserfs_transaction_handle th ;
int jbegin_count = JOURNAL_PER_BALANCE_CNT * 3;
/* We need blocks for transaction + (user+group)*(quotas for new inode + update of quota for directory owner) */
int jbegin_count = JOURNAL_PER_BALANCE_CNT * 3 + 2 * (REISERFS_QUOTA_INIT_BLOCKS+REISERFS_QUOTA_TRANS_BLOCKS);
int locked;
if (!new_valid_dev(rdev))
......@@ -667,9 +662,7 @@ static int reiserfs_mknod (struct inode * dir, struct dentry *dentry, int mode,
if (!(inode = new_inode(dir->i_sb))) {
return -ENOMEM ;
}
retval = new_inode_init(inode, dir, mode);
if (retval)
return retval;
new_inode_init(inode, dir, mode);
locked = reiserfs_cache_default_acl (dir);
......@@ -733,7 +726,8 @@ static int reiserfs_mkdir (struct inode * dir, struct dentry *dentry, int mode)
int retval;
struct inode * inode;
struct reiserfs_transaction_handle th ;
int jbegin_count = JOURNAL_PER_BALANCE_CNT * 3;
/* We need blocks for transaction + (user+group)*(quotas for new inode + update of quota for directory owner) */
int jbegin_count = JOURNAL_PER_BALANCE_CNT * 3 + 2 * (REISERFS_QUOTA_INIT_BLOCKS+REISERFS_QUOTA_TRANS_BLOCKS);
int locked;
#ifdef DISPLACE_NEW_PACKING_LOCALITIES
......@@ -744,9 +738,7 @@ static int reiserfs_mkdir (struct inode * dir, struct dentry *dentry, int mode)
if (!(inode = new_inode(dir->i_sb))) {
return -ENOMEM ;
}
retval = new_inode_init(inode, dir, mode);
if (retval)
return retval;
new_inode_init(inode, dir, mode);
locked = reiserfs_cache_default_acl (dir);
......@@ -836,8 +828,9 @@ static int reiserfs_rmdir (struct inode * dir, struct dentry *dentry)
struct reiserfs_dir_entry de;
/* we will be doing 2 balancings and update 2 stat data */
jbegin_count = JOURNAL_PER_BALANCE_CNT * 2 + 2;
/* we will be doing 2 balancings and update 2 stat data, we change quotas
* of the owner of the directory and of the owner of the parent directory */
jbegin_count = JOURNAL_PER_BALANCE_CNT * 2 + 2 + 2 * (REISERFS_QUOTA_INIT_BLOCKS+REISERFS_QUOTA_TRANS_BLOCKS);
reiserfs_write_lock(dir->i_sb);
retval = journal_begin(&th, dir->i_sb, jbegin_count) ;
......@@ -920,8 +913,9 @@ static int reiserfs_unlink (struct inode * dir, struct dentry *dentry)
inode = dentry->d_inode;
/* in this transaction we can be doing at max two balancings and update
two stat datas */
jbegin_count = JOURNAL_PER_BALANCE_CNT * 2 + 2;
two stat datas, we change quotas of the owner of the directory and of
the owner of the parent directory */
jbegin_count = JOURNAL_PER_BALANCE_CNT * 2 + 2 + 2 * (REISERFS_QUOTA_INIT_BLOCKS+REISERFS_QUOTA_TRANS_BLOCKS);
reiserfs_write_lock(dir->i_sb);
retval = journal_begin(&th, dir->i_sb, jbegin_count) ;
......@@ -1005,15 +999,13 @@ static int reiserfs_symlink (struct inode * parent_dir,
int item_len;
struct reiserfs_transaction_handle th ;
int mode = S_IFLNK | S_IRWXUGO;
int jbegin_count = JOURNAL_PER_BALANCE_CNT * 3;
/* We need blocks for transaction + (user+group)*(quotas for new inode + update of quota for directory owner) */
int jbegin_count = JOURNAL_PER_BALANCE_CNT * 3 + 2 * (REISERFS_QUOTA_INIT_BLOCKS+REISERFS_QUOTA_TRANS_BLOCKS);
if (!(inode = new_inode(parent_dir->i_sb))) {
return -ENOMEM ;
}
retval = new_inode_init(inode, parent_dir, mode);
if (retval) {
return retval;
}
new_inode_init(inode, parent_dir, mode);
reiserfs_write_lock(parent_dir->i_sb);
item_len = ROUND_UP (strlen (symname));
......@@ -1083,7 +1075,8 @@ static int reiserfs_link (struct dentry * old_dentry, struct inode * dir, struct
int retval;
struct inode *inode = old_dentry->d_inode;
struct reiserfs_transaction_handle th ;
int jbegin_count = JOURNAL_PER_BALANCE_CNT * 3;
/* We need blocks for transaction + update of quotas for the owners of the directory */
int jbegin_count = JOURNAL_PER_BALANCE_CNT * 3 + 2 * REISERFS_QUOTA_TRANS_BLOCKS;
reiserfs_write_lock(dir->i_sb);
if (inode->i_nlink >= REISERFS_LINK_MAX) {
......@@ -1201,8 +1194,9 @@ static int reiserfs_rename (struct inode * old_dir, struct dentry *old_dentry,
(2) new directory and (3) maybe old object stat data (when it is
directory) and (4) maybe stat data of object to which new entry
pointed initially and (5) maybe block containing ".." of
renamed directory */
jbegin_count = JOURNAL_PER_BALANCE_CNT * 3 + 5;
renamed directory
quota updates: two parent directories */
jbegin_count = JOURNAL_PER_BALANCE_CNT * 3 + 5 + 4 * REISERFS_QUOTA_TRANS_BLOCKS;
old_inode = old_dentry->d_inode;
new_dentry_inode = new_dentry->d_inode;
......
......@@ -25,6 +25,9 @@
#include <linux/buffer_head.h>
#include <linux/vfs.h>
#include <linux/namespace.h>
#include <linux/mount.h>
#include <linux/namei.h>
#include <linux/quotaops.h>
struct file_system_type reiserfs_fs_type;
......@@ -135,6 +138,9 @@ static int remove_save_link_only (struct super_block * s, struct reiserfs_key *
return journal_end (&th, s, JOURNAL_PER_BALANCE_CNT);
}
#ifdef CONFIG_QUOTA
static int reiserfs_quota_on_mount(struct super_block *, int);
#endif
/* look for uncompleted unlinks and truncates and complete them */
static int finish_unfinished (struct super_block * s)
......@@ -150,6 +156,10 @@ static int finish_unfinished (struct super_block * s)
int done;
struct inode * inode;
int truncate;
#ifdef CONFIG_QUOTA
int i;
int ms_active_set;
#endif
/* compose key to look for "save" links */
......@@ -157,6 +167,24 @@ static int finish_unfinished (struct super_block * s)
max_cpu_key.on_disk_key = MAX_KEY;
max_cpu_key.key_length = 3;
#ifdef CONFIG_QUOTA
/* Needed for iput() to work correctly and not trash data */
if (s->s_flags & MS_ACTIVE) {
ms_active_set = 0;
} else {
ms_active_set = 1;
s->s_flags |= MS_ACTIVE;
}
/* Turn on quotas so that they are updated correctly */
for (i = 0; i < MAXQUOTAS; i++) {
if (REISERFS_SB(s)->s_qf_names[i]) {
int ret = reiserfs_quota_on_mount(s, i);
if (ret < 0)
reiserfs_warning(s, "reiserfs: cannot turn on journalled quota: error %d", ret);
}
}
#endif
done = 0;
REISERFS_SB(s)->s_is_unlinked_ok = 1;
while (!retval) {
......@@ -212,6 +240,7 @@ static int finish_unfinished (struct super_block * s)
retval = remove_save_link_only (s, &save_link_key, 0);
continue;
}
DQUOT_INIT(inode);
if (truncate && S_ISDIR (inode->i_mode) ) {
/* We got a truncate request for a dir which is impossible.
......@@ -247,6 +276,16 @@ static int finish_unfinished (struct super_block * s)
}
REISERFS_SB(s)->s_is_unlinked_ok = 0;
#ifdef CONFIG_QUOTA
/* Turn quotas off */
for (i = 0; i < MAXQUOTAS; i++) {
if (sb_dqopt(s)->files[i])
vfs_quota_off_mount(s, i);
}
if (ms_active_set)
/* Restore the flag back */
s->s_flags &= ~MS_ACTIVE;
#endif
pathrelse (&path);
if (done)
reiserfs_info (s, "There were %d uncompleted unlinks/truncates. "
......@@ -517,6 +556,11 @@ static void reiserfs_clear_inode (struct inode *inode)
REISERFS_I(inode)->i_acl_default = NULL;
}
#ifdef CONFIG_QUOTA
static ssize_t reiserfs_quota_write(struct super_block *, int, const char *, size_t, loff_t);
static ssize_t reiserfs_quota_read(struct super_block *, int, char *, size_t, loff_t);
#endif
struct super_operations reiserfs_sops =
{
.alloc_inode = reiserfs_alloc_inode,
......@@ -532,9 +576,52 @@ struct super_operations reiserfs_sops =
.unlockfs = reiserfs_unlockfs,
.statfs = reiserfs_statfs,
.remount_fs = reiserfs_remount,
#ifdef CONFIG_QUOTA
.quota_read = reiserfs_quota_read,
.quota_write = reiserfs_quota_write,
#endif
};
#ifdef CONFIG_QUOTA
#define QTYPE2NAME(t) ((t)==USRQUOTA?"user":"group")
static int reiserfs_dquot_initialize(struct inode *, int);
static int reiserfs_dquot_drop(struct inode *);
static int reiserfs_write_dquot(struct dquot *);
static int reiserfs_acquire_dquot(struct dquot *);
static int reiserfs_release_dquot(struct dquot *);
static int reiserfs_mark_dquot_dirty(struct dquot *);
static int reiserfs_write_info(struct super_block *, int);
static int reiserfs_quota_on(struct super_block *, int, int, char *);
static struct dquot_operations reiserfs_quota_operations =
{
.initialize = reiserfs_dquot_initialize,
.drop = reiserfs_dquot_drop,
.alloc_space = dquot_alloc_space,
.alloc_inode = dquot_alloc_inode,
.free_space = dquot_free_space,
.free_inode = dquot_free_inode,
.transfer = dquot_transfer,
.write_dquot = reiserfs_write_dquot,
.acquire_dquot = reiserfs_acquire_dquot,
.release_dquot = reiserfs_release_dquot,
.mark_dirty = reiserfs_mark_dquot_dirty,
.write_info = reiserfs_write_info,
};
static struct quotactl_ops reiserfs_qctl_operations =
{
.quota_on = reiserfs_quota_on,
.quota_off = vfs_quota_off,
.quota_sync = vfs_quota_sync,
.get_info = vfs_get_dqinfo,
.set_info = vfs_set_dqinfo,
.get_dqblk = vfs_get_dqblk,
.set_dqblk = vfs_set_dqblk,
};
#endif
static struct export_operations reiserfs_export_ops = {
.encode_fh = reiserfs_encode_fh,
.decode_fh = reiserfs_decode_fh,
......@@ -553,6 +640,8 @@ typedef struct {
applied BEFORE setmask */
} arg_desc_t;
/* Set this bit in arg_required to allow empty arguments */
#define REISERFS_OPT_ALLOWEMPTY 31
/* this struct is used in reiserfs_getopt() for describing the set of reiserfs
mount options */
......@@ -705,8 +794,8 @@ static int reiserfs_getopt ( struct super_block * s, char ** cur, opt_desc_t * o
/* move to the argument, or to next option if argument is not required */
p ++;
if ( opt->arg_required && !strlen (p) ) {
/* this catches "option=," */
if ( opt->arg_required && !(opt->arg_required & (1<<REISERFS_OPT_ALLOWEMPTY)) && !strlen (p) ) {
/* this catches "option=," if not allowed */
reiserfs_warning (s, "empty argument for \"%s\"", opt->option_name);
return -1;
}
......@@ -714,7 +803,7 @@ static int reiserfs_getopt ( struct super_block * s, char ** cur, opt_desc_t * o
if (!opt->values) {
/* *=NULLopt_arg contains pointer to argument */
*opt_arg = p;
return opt->arg_required;
return opt->arg_required & ~(1<<REISERFS_OPT_ALLOWEMPTY);
}
/* values possible for this option are listed in opt->values */
......@@ -778,6 +867,9 @@ static int reiserfs_parse_options (struct super_block * s, char * options, /* st
{"usrquota",},
{"grpquota",},
{"errors", .arg_required = 'e', .values = error_actions},
{"usrjquota", .arg_required = 'u'|(1<<REISERFS_OPT_ALLOWEMPTY), .values = NULL},
{"grpjquota", .arg_required = 'g'|(1<<REISERFS_OPT_ALLOWEMPTY), .values = NULL},
{"jqfmt", .arg_required = 'f', .values = NULL},
{NULL,}
};
......@@ -839,8 +931,62 @@ static int reiserfs_parse_options (struct super_block * s, char * options, /* st
*jdev_name = arg;
}
}
#ifdef CONFIG_QUOTA
if (c == 'u' || c == 'g') {
int qtype = c == 'u' ? USRQUOTA : GRPQUOTA;
if (sb_any_quota_enabled(s)) {
reiserfs_warning(s, "reiserfs_parse_options: cannot change journalled quota options when quota turned on.");
return 0;
}
if (*arg) { /* Some filename specified? */
if (REISERFS_SB(s)->s_qf_names[qtype] && strcmp(REISERFS_SB(s)->s_qf_names[qtype], arg)) {
reiserfs_warning(s, "reiserfs_parse_options: %s quota file already specified.", QTYPE2NAME(qtype));
return 0;
}
if (strchr(arg, '/')) {
reiserfs_warning(s, "reiserfs_parse_options: quotafile must be on filesystem root.");
return 0;
}
REISERFS_SB(s)->s_qf_names[qtype] = kmalloc(strlen(arg)+1, GFP_KERNEL);
if (!REISERFS_SB(s)->s_qf_names[qtype]) {
reiserfs_warning(s, "reiserfs_parse_options: not enough memory for storing quotafile name.");
return 0;
}
strcpy(REISERFS_SB(s)->s_qf_names[qtype], arg);
}
else {
if (REISERFS_SB(s)->s_qf_names[qtype]) {
kfree(REISERFS_SB(s)->s_qf_names[qtype]);
REISERFS_SB(s)->s_qf_names[qtype] = NULL;
}
}
}
if (c == 'f') {
if (!strcmp(arg, "vfsold"))
REISERFS_SB(s)->s_jquota_fmt = QFMT_VFS_OLD;
else if (!strcmp(arg, "vfsv0"))
REISERFS_SB(s)->s_jquota_fmt = QFMT_VFS_V0;
else {
reiserfs_warning(s, "reiserfs_parse_options: unknown quota format specified.");
return 0;
}
}
#else
if (c == 'u' || c == 'g' || c == 'f') {
reiserfs_warning(s, "reiserfs_parse_options: journalled quota options not supported.");
return 0;
}
#endif
}
#ifdef CONFIG_QUOTA
if (!REISERFS_SB(s)->s_jquota_fmt && (REISERFS_SB(s)->s_qf_names[USRQUOTA] || REISERFS_SB(s)->s_qf_names[GRPQUOTA])) {
reiserfs_warning(s, "reiserfs_parse_options: journalled quota format not specified.");
return 0;
}
#endif
return 1;
}
......@@ -916,11 +1062,22 @@ static int reiserfs_remount (struct super_block * s, int * mount_flags, char * a
unsigned int commit_max_age = (unsigned int)-1;
struct reiserfs_journal *journal = SB_JOURNAL(s);
int err;
#ifdef CONFIG_QUOTA
int i;
#endif
rs = SB_DISK_SUPER_BLOCK (s);
if (!reiserfs_parse_options(s, arg, &mount_options, &blocks, NULL, &commit_max_age))
if (!reiserfs_parse_options(s, arg, &mount_options, &blocks, NULL, &commit_max_age)) {
#ifdef CONFIG_QUOTA
for (i = 0; i < MAXQUOTAS; i++)
if (REISERFS_SB(s)->s_qf_names[i]) {
kfree(REISERFS_SB(s)->s_qf_names[i]);
REISERFS_SB(s)->s_qf_names[i] = NULL;
}
#endif
return -EINVAL;
}
handle_attrs(s);
......@@ -1225,6 +1382,10 @@ static int read_super_block (struct super_block * s, int offset)
s->s_op = &reiserfs_sops;
s->s_export_op = &reiserfs_export_ops;
#ifdef CONFIG_QUOTA
s->s_qcop = &reiserfs_qctl_operations;
s->dq_op = &reiserfs_quota_operations;
#endif
/* new format is limited by the 32 bit wide i_blocks field, want to
** be one full block below that.
......@@ -1656,7 +1817,12 @@ static int reiserfs_fill_super (struct super_block * s, void * data, int silent)
}
if (SB_BUFFER_WITH_SB (s))
brelse(SB_BUFFER_WITH_SB (s));
#ifdef CONFIG_QUOTA
for (j = 0; j < MAXQUOTAS; j++) {
if (sbi->s_qf_names[j])
kfree(sbi->s_qf_names[j]);
}
#endif
if (sbi != NULL) {
kfree(sbi);
}
......@@ -1680,6 +1846,245 @@ static int reiserfs_statfs (struct super_block * s, struct kstatfs * buf)
return 0;
}
#ifdef CONFIG_QUOTA
static int reiserfs_dquot_initialize(struct inode *inode, int type)
{
struct reiserfs_transaction_handle th;
int ret;
/* We may create quota structure so we need to reserve enough blocks */
journal_begin(&th, inode->i_sb, 2*REISERFS_QUOTA_INIT_BLOCKS);
ret = dquot_initialize(inode, type);
journal_end(&th, inode->i_sb, 2*REISERFS_QUOTA_INIT_BLOCKS);
return ret;
}
static int reiserfs_dquot_drop(struct inode *inode)
{
struct reiserfs_transaction_handle th;
int ret;
/* We may delete quota structure so we need to reserve enough blocks */
journal_begin(&th, inode->i_sb, 2*REISERFS_QUOTA_INIT_BLOCKS);
ret = dquot_drop(inode);
journal_end(&th, inode->i_sb, 2*REISERFS_QUOTA_INIT_BLOCKS);
return ret;
}
static int reiserfs_write_dquot(struct dquot *dquot)
{
struct reiserfs_transaction_handle th;
int ret;
journal_begin(&th, dquot->dq_sb, REISERFS_QUOTA_TRANS_BLOCKS);
ret = dquot_commit(dquot);
journal_end(&th, dquot->dq_sb, REISERFS_QUOTA_TRANS_BLOCKS);
return ret;
}
static int reiserfs_acquire_dquot(struct dquot *dquot)
{
struct reiserfs_transaction_handle th;
int ret;
journal_begin(&th, dquot->dq_sb, REISERFS_QUOTA_INIT_BLOCKS);
ret = dquot_acquire(dquot);
journal_end(&th, dquot->dq_sb, REISERFS_QUOTA_INIT_BLOCKS);
return ret;
}
static int reiserfs_release_dquot(struct dquot *dquot)
{
struct reiserfs_transaction_handle th;
int ret;
journal_begin(&th, dquot->dq_sb, REISERFS_QUOTA_INIT_BLOCKS);
ret = dquot_release(dquot);
journal_end(&th, dquot->dq_sb, REISERFS_QUOTA_INIT_BLOCKS);
return ret;
}
static int reiserfs_mark_dquot_dirty(struct dquot *dquot)
{
/* Are we journalling quotas? */
if (REISERFS_SB(dquot->dq_sb)->s_qf_names[USRQUOTA] ||
REISERFS_SB(dquot->dq_sb)->s_qf_names[GRPQUOTA]) {
dquot_mark_dquot_dirty(dquot);
return reiserfs_write_dquot(dquot);
}
else
return dquot_mark_dquot_dirty(dquot);
}
static int reiserfs_write_info(struct super_block *sb, int type)
{
struct reiserfs_transaction_handle th;
int ret;
/* Data block + inode block */
journal_begin(&th, sb, 2);
ret = dquot_commit_info(sb, type);
journal_end(&th, sb, 2);
return ret;
}
/*
* Turn on quotas during mount time - we need to find
* the quota file and such...
*/
static int reiserfs_quota_on_mount(struct super_block *sb, int type)
{
int err;
struct dentry *dentry;
struct qstr name = { .name = REISERFS_SB(sb)->s_qf_names[type],
.hash = 0,
.len = strlen(REISERFS_SB(sb)->s_qf_names[type])};
dentry = lookup_hash(&name, sb->s_root);
if (IS_ERR(dentry))
return PTR_ERR(dentry);
err = vfs_quota_on_mount(type, REISERFS_SB(sb)->s_jquota_fmt, dentry);
/* Now invalidate and put the dentry - quota got its own reference
* to inode and dentry has at least wrong hash so we had better
* throw it away */
d_invalidate(dentry);
dput(dentry);
return err;
}
/*
* Standard function to be called on quota_on
*/
static int reiserfs_quota_on(struct super_block *sb, int type, int format_id, char *path)
{
int err;
struct nameidata nd;
err = path_lookup(path, LOOKUP_FOLLOW, &nd);
if (err)
return err;
/* Quotafile not on the same filesystem? */
if (nd.mnt->mnt_sb != sb) {
path_release(&nd);
return -EXDEV;
}
/* We must not pack tails for quota files on reiserfs for quota IO to work */
if (!REISERFS_I(nd.dentry->d_inode)->i_flags & i_nopack_mask) {
reiserfs_warning(sb, "reiserfs: Quota file must have tail packing disabled.");
path_release(&nd);
return -EINVAL;
}
/* Not journalling quota? No more tests needed... */
if (!REISERFS_SB(sb)->s_qf_names[USRQUOTA] &&
!REISERFS_SB(sb)->s_qf_names[GRPQUOTA]) {
path_release(&nd);
return vfs_quota_on(sb, type, format_id, path);
}
/* Quotafile not of fs root? */
if (nd.dentry->d_parent->d_inode != sb->s_root->d_inode)
reiserfs_warning(sb, "reiserfs: Quota file not on filesystem root. "
"Journalled quota will not work.");
path_release(&nd);
return vfs_quota_on(sb, type, format_id, path);
}
/* Read data from quotafile - avoid pagecache and such because we cannot afford
* acquiring the locks... As quota files are never truncated and quota code
* itself serializes the operations (and noone else should touch the files)
* we don't have to be afraid of races */
static ssize_t reiserfs_quota_read(struct super_block *sb, int type, char *data,
size_t len, loff_t off)
{
struct inode *inode = sb_dqopt(sb)->files[type];
unsigned long blk = off >> sb->s_blocksize_bits;
int err = 0, offset = off & (sb->s_blocksize - 1), tocopy;
size_t toread;
struct buffer_head tmp_bh, *bh;
loff_t i_size = i_size_read(inode);
if (off > i_size)
return 0;
if (off+len > i_size)
len = i_size-off;
toread = len;
while (toread > 0) {
tocopy = sb->s_blocksize - offset < toread ? sb->s_blocksize - offset : toread;
tmp_bh.b_state = 0;
/* Quota files are without tails so we can safely use this function */
err = reiserfs_get_block(inode, blk, &tmp_bh, 0);
if (err)
return err;
if (!buffer_mapped(&tmp_bh)) /* A hole? */
memset(data, 0, tocopy);
else {
bh = sb_bread(sb, tmp_bh.b_blocknr);
if (!bh)
return -EIO;
memcpy(data, bh->b_data+offset, tocopy);
brelse(bh);
}
offset = 0;
toread -= tocopy;
data += tocopy;
blk++;
}
return len;
}
/* Write to quotafile (we know the transaction is already started and has
* enough credits) */
static ssize_t reiserfs_quota_write(struct super_block *sb, int type,
const char *data, size_t len, loff_t off)
{
struct inode *inode = sb_dqopt(sb)->files[type];
unsigned long blk = off >> sb->s_blocksize_bits;
int err = 0, offset = off & (sb->s_blocksize - 1), tocopy;
int journal_quota = REISERFS_SB(sb)->s_qf_names[type] != NULL;
size_t towrite = len;
struct buffer_head tmp_bh, *bh;
down(&inode->i_sem);
while (towrite > 0) {
tocopy = sb->s_blocksize - offset < towrite ?
sb->s_blocksize - offset : towrite;
tmp_bh.b_state = 0;
err = reiserfs_get_block(inode, blk, &tmp_bh, GET_BLOCK_CREATE);
if (err)
goto out;
if (offset || tocopy != sb->s_blocksize)
bh = sb_bread(sb, tmp_bh.b_blocknr);
else
bh = sb_getblk(sb, tmp_bh.b_blocknr);
if (!bh) {
err = -EIO;
goto out;
}
memcpy(bh->b_data+offset, data, tocopy);
set_buffer_uptodate(bh);
reiserfs_prepare_for_journal(sb, bh, 1);
journal_mark_dirty(current->journal_info, sb, bh);
if (!journal_quota)
reiserfs_add_ordered_list(inode, bh);
brelse(bh);
offset = 0;
towrite -= tocopy;
data += tocopy;
blk++;
}
out:
if (len == towrite)
return err;
if (inode->i_size < off+len-towrite)
i_size_write(inode, off+len-towrite);
inode->i_version++;
inode->i_mtime = inode->i_ctime = CURRENT_TIME;
mark_inode_dirty(inode);
up(&inode->i_sem);
return len - towrite;
}
#endif
static struct super_block*
get_super_block (struct file_system_type *fs_type, int flags,
const char *dev_name, void *data)
......
......@@ -1688,6 +1688,13 @@ struct reiserfs_journal_header {
#define JOURNAL_MAX_COMMIT_AGE 30
#define JOURNAL_MAX_TRANS_AGE 30
#define JOURNAL_PER_BALANCE_CNT (3 * (MAX_HEIGHT-2) + 9)
#ifdef CONFIG_QUOTA
#define REISERFS_QUOTA_TRANS_BLOCKS 2 /* We need to update data and inode (atime) */
#define REISERFS_QUOTA_INIT_BLOCKS (DQUOT_MAX_WRITES*(JOURNAL_PER_BALANCE_CNT+2)+1) /* 1 balancing, 1 bitmap, 1 data per write + stat data update */
#else
#define REISERFS_QUOTA_TRANS_BLOCKS 0
#define REISERFS_QUOTA_INIT_BLOCKS 0
#endif
/* both of these can be as low as 1, or as high as you want. The min is the
** number of 4k bitmap nodes preallocated on mount. New nodes are allocated
......@@ -1930,12 +1937,21 @@ int reiserfs_do_truncate (struct reiserfs_transaction_handle *th,
void padd_item (char * item, int total_length, int length);
/* inode.c */
/* args for the create parameter of reiserfs_get_block */
#define GET_BLOCK_NO_CREATE 0 /* don't create new blocks or convert tails */
#define GET_BLOCK_CREATE 1 /* add anything you need to find block */
#define GET_BLOCK_NO_HOLE 2 /* return -ENOENT for file holes */
#define GET_BLOCK_READ_DIRECT 4 /* read the tail if indirect item not found */
#define GET_BLOCK_NO_ISEM 8 /* i_sem is not held, don't preallocate */
#define GET_BLOCK_NO_DANGLE 16 /* don't leave any transactions running */
int restart_transaction(struct reiserfs_transaction_handle *th, struct inode *inode, struct path *path);
void reiserfs_read_locked_inode(struct inode * inode, struct reiserfs_iget_args *args) ;
int reiserfs_find_actor(struct inode * inode, void *p) ;
int reiserfs_init_locked_inode(struct inode * inode, void *p) ;
void reiserfs_delete_inode (struct inode * inode);
int reiserfs_write_inode (struct inode * inode, int) ;
int reiserfs_get_block (struct inode * inode, sector_t block, struct buffer_head * bh_result, int create);
struct dentry *reiserfs_get_dentry(struct super_block *, void *) ;
struct dentry *reiserfs_decode_fh(struct super_block *sb, __u32 *data,
int len, int fhtype,
......
......@@ -410,6 +410,10 @@ struct reiserfs_sb_info
struct rw_semaphore xattr_dir_sem;
int j_errno;
#ifdef CONFIG_QUOTA
char *s_qf_names[MAXQUOTAS];
int s_jquota_fmt;
#endif
};
/* Definitions of reiserfs on-disk properties: */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment