mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Tomasz.Wasiak@odynca.pex.com.pl (Tomasz Wasiak)
To: Chris Wright <chrisw@osdl.org>
Cc: linux-kernel@vger.kernel.org
Subject: Re: PROBLEM: changing ownership of directory with reiserfs mounted inside
Date: Fri, 7 Jan 2005 02:25:20 +0100	[thread overview]
Message-ID: <20050107012520.GA3352@router.odynca.pex.com.pl> (raw)
In-Reply-To: <20050105111913.I469@build.pdx.osdl.net>

After applying patch you attached in reverse form to bk9 everything
works as it should.

On Wed, Jan 05, 2005 at 11:19:13AM -0800, Chris Wright wrote:
> * Tomasz.Wasiak@pex.com.pl (Tomasz.Wasiak@pex.com.pl) wrote:
> > I was trying to change ownership of directory where I have reiserfs
> > mounted (I was preparing it for squid cache dir). Under 2.6.10 and up
> > to 2.6.10-bk5 (and 2.6.10-ac4 too) everything works as it should, but 
> > when I tried to do it under 2.6.10-bk7 (or bk8) chown suspended. This
> > bug is very simple to reproduct - just mount reiserfs into directory
> > and then try to change ownership of that directory. Something must
> > have happened between bk5 and bk7.
> 
> Well, setattr stuff did change in there.  What happens with this patch
> backed out?
> 
> # This is a BitKeeper generated diff -Nru style patch.
> #
> # ChangeSet
> #   2005/01/03 20:13:18-08:00 jack@suse.cz 
> #   [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>
> # 
> # include/linux/reiserfs_fs_sb.h
> #   2005/01/03 15:49:13-08:00 jack@suse.cz +4 -0
> #   Fix of quota deadlock on pagelock: reiserfs
> # 
> # include/linux/reiserfs_fs.h
> #   2005/01/03 15:49:13-08:00 jack@suse.cz +16 -0
> #   Fix of quota deadlock on pagelock: reiserfs
> # 
> # fs/reiserfs/super.c
> #   2005/01/03 15:49:13-08:00 jack@suse.cz +410 -5
> #   Fix of quota deadlock on pagelock: reiserfs
> # 
> # fs/reiserfs/namei.c
> #   2005/01/03 15:49:13-08:00 jack@suse.cz +27 -33
> #   Fix of quota deadlock on pagelock: reiserfs
> # 
> # fs/reiserfs/inode.c
> #   2005/01/03 15:49:13-08:00 jack@suse.cz +37 -17
> #   Fix of quota deadlock on pagelock: reiserfs
> # 
> # fs/reiserfs/file.c
> #   2005/01/03 15:49:13-08:00 jack@suse.cz +3 -3
> #   Fix of quota deadlock on pagelock: reiserfs
> # 
> diff -Nru a/fs/reiserfs/file.c b/fs/reiserfs/file.c
> --- a/fs/reiserfs/file.c	2005-01-05 11:01:19 -08:00
> +++ b/fs/reiserfs/file.c	2005-01-05 11:01:19 -08:00
> @@ -54,7 +54,7 @@
>      /* 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 @@
>      /* 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 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 -Nru a/fs/reiserfs/inode.c b/fs/reiserfs/inode.c
> --- a/fs/reiserfs/inode.c	2005-01-05 11:01:19 -08:00
> +++ b/fs/reiserfs/inode.c	2005-01-05 11:01:19 -08:00
> @@ -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 @@
>  	    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 @@
>          . 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 @@
>  
>      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 @@
>      /* 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 @@
>  	    (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 -Nru a/fs/reiserfs/namei.c b/fs/reiserfs/namei.c
> --- a/fs/reiserfs/namei.c	2005-01-05 11:01:19 -08:00
> +++ b/fs/reiserfs/namei.c	2005-01-05 11:01:19 -08:00
> @@ -545,7 +545,7 @@
>  
>  /* 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 @@
>  }
>  
>  /* 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 @@
>          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 @@
>  {
>      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 @@
>      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 @@
>      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 @@
>      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 @@
>      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 @@
>      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 @@
>      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 @@
>      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 @@
>      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 @@
>         (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 -Nru a/fs/reiserfs/super.c b/fs/reiserfs/super.c
> --- a/fs/reiserfs/super.c	2005-01-05 11:01:19 -08:00
> +++ b/fs/reiserfs/super.c	2005-01-05 11:01:19 -08:00
> @@ -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 @@
>       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 @@
>      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 @@
>              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 @@
>      }
>      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 @@
>      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 @@
>    .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 @@
>  		    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 @@
>      /* 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 @@
>      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 @@
>  	{"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 @@
>  		*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 @@
>    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 @@
>  
>      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 @@
>      }
>      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);
>      }
> @@ -1679,6 +1845,245 @@
>    buf->f_type    =  REISERFS_SUPER_MAGIC;
>    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,
> diff -Nru a/include/linux/reiserfs_fs.h b/include/linux/reiserfs_fs.h
> --- a/include/linux/reiserfs_fs.h	2005-01-05 11:01:19 -08:00
> +++ b/include/linux/reiserfs_fs.h	2005-01-05 11:01:19 -08:00
> @@ -1688,6 +1688,13 @@
>  #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 @@
>  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,
> diff -Nru a/include/linux/reiserfs_fs_sb.h b/include/linux/reiserfs_fs_sb.h
> --- a/include/linux/reiserfs_fs_sb.h	2005-01-05 11:01:19 -08:00
> +++ b/include/linux/reiserfs_fs_sb.h	2005-01-05 11:01:19 -08:00
> @@ -410,6 +410,10 @@
>      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: */

      reply	other threads:[~2005-01-07  1:50 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2005-01-05 16:26 Tomasz.Wasiak
2005-01-05 19:19 ` Chris Wright
2005-01-07  1:25   ` Tomasz Wasiak [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20050107012520.GA3352@router.odynca.pex.com.pl \
    --to=tomasz.wasiak@odynca.pex.com.pl \
    --cc=chrisw@osdl.org \
    --cc=linux-kernel@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

Powered by JetHome