aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorJan Kara <jack@suse.cz>2005-01-03 04:13:18 -0800
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-01-03 04:13:18 -0800
commit04a6c897f404972d0f2664e4fe1c1978bd5e0e5e (patch)
tree52eb61a8c78c5cf36c061528af42f713486a2964 /fs
parent988871227a0527e38573693e53ae03120d0d05ea (diff)
downloadhistory-04a6c897f404972d0f2664e4fe1c1978bd5e0e5e.tar.gz
[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: Jan Kara <jack@suse.cz> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'fs')
-rw-r--r--fs/reiserfs/file.c6
-rw-r--r--fs/reiserfs/inode.c54
-rw-r--r--fs/reiserfs/namei.c60
-rw-r--r--fs/reiserfs/super.c415
4 files changed, 477 insertions, 58 deletions
diff --git a/fs/reiserfs/file.c b/fs/reiserfs/file.c
index b46b75f915a785..45e5a733b5bb6f 100644
--- a/fs/reiserfs/file.c
+++ b/fs/reiserfs/file.c
@@ -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 @@ error_exit:
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;
}
diff --git a/fs/reiserfs/inode.c b/fs/reiserfs/inode.c
index 0fb2abb9a967c4..fb0c5c90e5fa44 100644
--- a/fs/reiserfs/inode.c
+++ b/fs/reiserfs/inode.c
@@ -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 @@ out_bad_inode:
/* 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) ;
diff --git a/fs/reiserfs/namei.c b/fs/reiserfs/namei.c
index 30e19a145fa79b..f18ed34caf1f98 100644
--- a/fs/reiserfs/namei.c
+++ b/fs/reiserfs/namei.c
@@ -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;
diff --git a/fs/reiserfs/super.c b/fs/reiserfs/super.c
index 465a6ad755a9e0..992221af3f2dae 100644
--- a/fs/reiserfs/super.c
+++ b/fs/reiserfs/super.c
@@ -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,12 +156,34 @@ 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 */
max_cpu_key.version = KEY_FORMAT_3_5;
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;
@@ -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,8 +576,51 @@ 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,
@@ -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)