[Ocfs2-tools-devel] [Ocfs2-devel] [PATCH 3/9] Implement quota functions to libocfs2

Jan Kara jack at suse.cz
Wed Aug 5 04:56:50 PDT 2009


On Wed 05-08-09 16:46:34, tristan.ye wrote:
> On Mon, 2009-08-03 at 15:23 +0200, Jan Kara wrote:
> > Signed-off-by: Jan Kara <jack at suse.cz>
> > ---
> >  include/ocfs2/ocfs2.h     |   87 ++++
> >  libocfs2/Makefile         |    1 +
> >  libocfs2/feature_string.c |   18 +
> >  libocfs2/ocfs2_err.et     |    6 +
> >  libocfs2/quota.c          | 1217 +++++++++++++++++++++++++++++++++++++++++++++
> >  5 files changed, 1329 insertions(+), 0 deletions(-)
> >  create mode 100644 libocfs2/quota.c
> > 
> > diff --git a/include/ocfs2/ocfs2.h b/include/ocfs2/ocfs2.h
> > index 47fede3..9f861d6 100644
> > --- a/include/ocfs2/ocfs2.h
> > +++ b/include/ocfs2/ocfs2.h
> > @@ -50,6 +50,7 @@
> >  #include <ocfs2-kernel/kernel-list.h>
> >  #include <ocfs2-kernel/sparse_endian_types.h>
> >  #include <ocfs2-kernel/ocfs2_fs.h>
> > +#include <ocfs2-kernel/quota_tree.h>
> >  #include <o2dlm/o2dlm.h>
> >  #include <o2cb/o2cb.h>
> >  #include <ocfs2/ocfs2_err.h>
> > @@ -125,16 +126,36 @@
> >  #define OCFS2_CHB_WAITING	2
> >  #define OCFS2_CHB_COMPLETE	3
> >  
> > +/* Flags for global quotafile info */
> > +#define OCFS2_QF_INFO_DIRTY 1
> > +
> >  typedef void (*ocfs2_chb_notify)(int state, char *progress, void *data);
> >  
> >  typedef struct _ocfs2_filesys ocfs2_filesys;
> >  typedef struct _ocfs2_cached_inode ocfs2_cached_inode;
> > +typedef struct _ocfs2_cached_dquot ocfs2_cached_dquot;
> >  typedef struct _io_channel io_channel;
> >  typedef struct _ocfs2_inode_scan ocfs2_inode_scan;
> >  typedef struct _ocfs2_dir_scan ocfs2_dir_scan;
> >  typedef struct _ocfs2_bitmap ocfs2_bitmap;
> >  typedef struct _ocfs2_devices ocfs2_devices;
> >  
> > +#define MAXQUOTAS 2
> > +#define USRQUOTA 0
> > +#define GRPQUOTA 1
> > +
> > +#define OCFS2_DEF_BLOCK_GRACE 604800 /* 1 week */
> > +#define OCFS2_DEF_INODE_GRACE 604800 /* 1 week */
> > +#define OCFS2_DEF_QUOTA_SYNC 10000   /* 10 seconds */
> > +
> > +struct _ocfs2_quota_info {
> > +	ocfs2_cached_inode *qi_inode;
> > +	int flags;
> > +	struct ocfs2_global_disk_dqinfo qi_info;
> > +};
> > +
> > +typedef struct _ocfs2_quota_info ocfs2_quota_info;
> > +
> >  struct _ocfs2_filesys {
> >  	char *fs_devname;
> >  	uint32_t fs_flags;
> > @@ -161,6 +182,8 @@ struct _ocfs2_filesys {
> >  	struct o2dlm_ctxt *fs_dlm_ctxt;
> >  	struct ocfs2_image_state *ost;
> >  
> > +	ocfs2_quota_info qinfo[MAXQUOTAS];
> > +
> >  	/* Reserved for the use of the calling application. */
> >  	void *fs_private;
> >  };
> > @@ -172,6 +195,15 @@ struct _ocfs2_cached_inode {
> >  	ocfs2_bitmap *ci_chains;
> >  };
> >  
> > +typedef unsigned int qid_t;
> > +
> > +struct _ocfs2_cached_dquot {
> > +	loff_t d_off;	/* Offset of structure in the file */
> > +	struct _ocfs2_cached_dquot *d_next;	/* Next entry in hashchain */
> > +	struct _ocfs2_cached_dquot **d_pprev;	/* Previous pointer in hashchain */
> > +	struct ocfs2_global_disk_dqblk d_ddquot;	/* Quota entry */
> > +};
> > +
> >  struct ocfs2_slot_data {
> >  	int		sd_valid;
> >  	unsigned int	sd_node_num;
> > @@ -205,6 +237,14 @@ struct _ocfs2_fs_options {
> >  	uint32_t opt_ro_compat;
> >  };
> >  
> > +struct _ocfs2_quota_hash {
> > +	int alloc_entries;
> > +	int used_entries;
> > +	ocfs2_cached_dquot **hash;
> > +};
> > +
> > +typedef struct _ocfs2_quota_hash ocfs2_quota_hash;
> > +
> >  errcode_t ocfs2_malloc(unsigned long size, void *ptr);
> >  errcode_t ocfs2_malloc0(unsigned long size, void *ptr);
> >  errcode_t ocfs2_free(void *ptr);
> > @@ -581,6 +621,53 @@ errcode_t ocfs2_meta_lock(ocfs2_filesys *fs, ocfs2_cached_inode *inode,
> >  
> >  errcode_t ocfs2_meta_unlock(ocfs2_filesys *fs, ocfs2_cached_inode *ci);
> >  
> > +/* Quota operations */
> > +static inline int ocfs2_global_dqstr_in_blk(int blocksize)
> > +{
> > +	return (blocksize - OCFS2_QBLK_RESERVED_SPACE -
> > +		sizeof(struct qt_disk_dqdbheader)) /
> > +		sizeof(struct ocfs2_global_disk_dqblk);
> > +}
> > +void ocfs2_swap_quota_header(struct ocfs2_disk_dqheader *header);
> > +void ocfs2_swap_quota_local_info(struct ocfs2_local_disk_dqinfo *info);
> > +void ocfs2_swap_quota_chunk_header(struct ocfs2_local_disk_chunk *chunk);
> > +void ocfs2_swap_quota_global_info(struct ocfs2_global_disk_dqinfo *info);
> > +void ocfs2_swap_quota_global_dqblk(struct ocfs2_global_disk_dqblk *dqblk);
> > +void ocfs2_swap_quota_leaf_block_header(struct qt_disk_dqdbheader *bheader);
> > +errcode_t ocfs2_init_local_quota_file(ocfs2_filesys *fs, int type,
> > +				      uint64_t blkno);
> > +errcode_t ocfs2_init_local_quota_files(ocfs2_filesys *fs, int type);
> > +int ocfs2_qtree_depth(int blocksize);
> > +int ocfs2_qtree_entry_unused(struct ocfs2_global_disk_dqblk *ddquot);
> > +errcode_t ocfs2_init_global_quota_file(ocfs2_filesys *fs, int type);
> > +errcode_t ocfs2_init_fs_quota_info(ocfs2_filesys *fs, int type);
> > +errcode_t ocfs2_read_global_quota_info(ocfs2_filesys *fs, int type);
> > +errcode_t ocfs2_write_global_quota_info(ocfs2_filesys *fs, int type);
> > +errcode_t ocfs2_write_dquot(ocfs2_filesys *fs, int type,
> > +			    ocfs2_cached_dquot *dquot);
> > +errcode_t ocfs2_delete_dquot(ocfs2_filesys *fs, int type,
> > +			     ocfs2_cached_dquot *dquot);
> > +errcode_t ocfs2_read_dquot(ocfs2_filesys *fs, int type, qid_t id,
> > +			   ocfs2_cached_dquot **ret_dquot);
> > +errcode_t ocfs2_new_quota_hash(ocfs2_quota_hash **hashp);
> > +errcode_t ocfs2_free_quota_hash(ocfs2_quota_hash *hash);
> > +errcode_t ocfs2_insert_quota_hash(ocfs2_quota_hash *hash,
> > +				  ocfs2_cached_dquot *dquot);
> > +errcode_t ocfs2_remove_quota_hash(ocfs2_quota_hash *hash,
> > +				  ocfs2_cached_dquot *dquot);
> > +errcode_t ocfs2_find_quota_hash(ocfs2_quota_hash *hash, qid_t id,
> > +				ocfs2_cached_dquot **dquotp);
> > +errcode_t ocfs2_find_create_quota_hash(ocfs2_quota_hash *hash, qid_t id,
> > +				       ocfs2_cached_dquot **dquotp);
> > +errcode_t ocfs2_compute_quota_usage(ocfs2_filesys *fs,
> > +				    ocfs2_quota_hash *usr_hash,
> > +				    ocfs2_quota_hash *grp_hash);
> > +errcode_t ocfs2_iterate_quota_hash(ocfs2_quota_hash *hash,
> > +				   errcode_t (*f)(ocfs2_cached_dquot *, void *),
> > +				   void *data);
> > +errcode_t ocfs2_write_release_dquots(ocfs2_filesys *fs, int type,
> > +				     ocfs2_quota_hash *hash);
> > +
> >  /* Low level */
> >  void ocfs2_swap_slot_map(struct ocfs2_slot_map *sm, int num_slots);
> >  void ocfs2_swap_slot_map_extended(struct ocfs2_slot_map_extended *se,
> > diff --git a/libocfs2/Makefile b/libocfs2/Makefile
> > index 48cfe80..eeb854a 100644
> > --- a/libocfs2/Makefile
> > +++ b/libocfs2/Makefile
> > @@ -73,6 +73,7 @@ CFILES = 		\
> >  	lockid.c	\
> >  	backup_super.c	\
> >  	feature_string.c\
> > +	quota.c		\
> >  	image.c		\
> >  	xattr.c
> >  
> > diff --git a/libocfs2/feature_string.c b/libocfs2/feature_string.c
> > index 17e2675..18ae6e9 100644
> > --- a/libocfs2/feature_string.c
> > +++ b/libocfs2/feature_string.c
> > @@ -123,6 +123,16 @@ static struct fs_feature_flags ocfs2_supported_features[] = {
> >  		{0, OCFS2_FEATURE_INCOMPAT_XATTR, 0},
> >  	},
> >  	{
> > +		"usrquota",
> > +		{0, 0, OCFS2_FEATURE_RO_COMPAT_USRQUOTA},
> > +		{0, 0, OCFS2_FEATURE_RO_COMPAT_USRQUOTA},
> > +	},
> > +	{
> > +		"grpquota",
> > +		{0, 0, OCFS2_FEATURE_RO_COMPAT_GRPQUOTA},
> > +		{0, 0, OCFS2_FEATURE_RO_COMPAT_GRPQUOTA},
> > +	},
> > +	{
> >  		NULL,
> >  		{0, 0, 0},
> >  		{0, 0, 0}
> > @@ -190,6 +200,14 @@ static struct feature_name ocfs2_feature_names[] = {
> >  		.fn_flag = {0, OCFS2_FEATURE_INCOMPAT_XATTR, 0},
> >  	},
> >  	{
> > +		.fn_name = "usrquota",
> > +		.fn_flag = {0, 0, OCFS2_FEATURE_RO_COMPAT_USRQUOTA},
> > +	},
> > +	{
> > +		.fn_name = "grpquota",
> > +		.fn_flag = {0, 0, OCFS2_FEATURE_RO_COMPAT_GRPQUOTA},
> > +	},
> > +	{
> >  		.fn_name = NULL,
> >  	},
> >  };
> > diff --git a/libocfs2/ocfs2_err.et b/libocfs2/ocfs2_err.et
> > index ddfa07c..13b03c4 100644
> > --- a/libocfs2/ocfs2_err.et
> > +++ b/libocfs2/ocfs2_err.et
> > @@ -183,7 +183,13 @@ ec	OCFS2_ET_BAD_XATTR_BLOCK_MAGIC,
> >  ec	OCFS2_ET_UNKNOWN_FEATURE,
> >  	"Unknown feature"
> >  
> > +ec	OCFS2_ET_CORRUPT_QUOTA_FILE,
> > +	"Quota file is corrupted"
> > +
> >  ec	OCFS2_ET_CANNOT_DETERMINE_SECTOR_SIZE,
> >  	"Cannot determine sector size"
> >  
> > +ec	OCFS2_ET_NONEMTY_QUOTA_HASH,
> > +	"Freeing non-empty quota hash"
> > +
> >  	end
> > diff --git a/libocfs2/quota.c b/libocfs2/quota.c
> > new file mode 100644
> > index 0000000..85ba576
> > --- /dev/null
> > +++ b/libocfs2/quota.c
> > @@ -0,0 +1,1217 @@
> > +/* -*- mode: c; c-basic-offset: 8; -*-
> > + * vim: noexpandtab sw=8 ts=8 sts=0:
> > + *
> > + * quota.c
> > + *
> > + * Quota operations for the OCFS2 userspace library.
> > + *
> > + * Copyright (C) 2008 Novell.  All rights reserved.
> > + *
> > + * This program is free software; you can redistribute it and/or
> > + * modify it under the terms of the GNU General Public
> > + * License, version 2,  as published by the Free Software Foundation.
> > + *
> > + * This program is distributed in the hope that it will be useful,
> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> > + * General Public License for more details.
> > + *
> > + * You should have received a copy of the GNU General Public
> > + * License along with this program; if not, write to the
> > + * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
> > + * Boston, MA 021110-1307, USA.
> > + */
> > +
> > +#include <inttypes.h>
> > +
> > +#include "ocfs2/byteorder.h"
> > +#include "ocfs2/ocfs2.h"
> > +
> > +void ocfs2_swap_quota_header(struct ocfs2_disk_dqheader *header)
> > +{
> > +	if (cpu_is_little_endian)
> > +		return;
> > +	header->dqh_magic = bswap_32(header->dqh_magic);
> > +	header->dqh_version = bswap_32(header->dqh_version);
> > +}
> > +
> > +void ocfs2_swap_quota_local_info(struct ocfs2_local_disk_dqinfo *info)
> > +{
> > +	if (cpu_is_little_endian)
> > +		return;
> > +	info->dqi_flags = bswap_32(info->dqi_flags);
> > +	info->dqi_chunks = bswap_32(info->dqi_chunks);
> > +	info->dqi_blocks = bswap_32(info->dqi_blocks);
> > +}
> > +
> > +void ocfs2_swap_quota_chunk_header(struct ocfs2_local_disk_chunk *chunk)
> > +{
> > +	if (cpu_is_little_endian)
> > +		return;
> > +	chunk->dqc_free = bswap_32(chunk->dqc_free);
> > +}
> > +
> > +void ocfs2_swap_quota_global_info(struct ocfs2_global_disk_dqinfo *info)
> > +{
> > +	if (cpu_is_little_endian)
> > +		return;
> > +	info->dqi_bgrace = bswap_32(info->dqi_bgrace);
> > +	info->dqi_igrace = bswap_32(info->dqi_igrace);
> > +	info->dqi_syncms = bswap_32(info->dqi_syncms);
> > +	info->dqi_blocks = bswap_32(info->dqi_blocks);
> > +	info->dqi_free_blk = bswap_32(info->dqi_free_blk);
> > +	info->dqi_free_entry = bswap_32(info->dqi_free_entry);
> > +}
> > +
> > +void ocfs2_swap_quota_global_dqblk(struct ocfs2_global_disk_dqblk *dqblk)
> > +{
> > +	if (cpu_is_little_endian)
> > +		return;
> > +	dqblk->dqb_id = bswap_32(dqblk->dqb_id);
> > +	dqblk->dqb_use_count = bswap_32(dqblk->dqb_use_count);
> > +	dqblk->dqb_ihardlimit = bswap_64(dqblk->dqb_ihardlimit);
> > +	dqblk->dqb_isoftlimit = bswap_64(dqblk->dqb_isoftlimit);
> > +	dqblk->dqb_curinodes = bswap_64(dqblk->dqb_curinodes);
> > +	dqblk->dqb_bhardlimit = bswap_64(dqblk->dqb_bhardlimit);
> > +	dqblk->dqb_bsoftlimit = bswap_64(dqblk->dqb_bsoftlimit);
> > +	dqblk->dqb_curspace = bswap_64(dqblk->dqb_curspace);
> > +	dqblk->dqb_btime = bswap_64(dqblk->dqb_btime);
> > +	dqblk->dqb_itime = bswap_64(dqblk->dqb_itime);
> > +}
> > +
> > +void ocfs2_swap_quota_leaf_block_header(struct qt_disk_dqdbheader *bheader)
> > +{
> > +	if (cpu_is_little_endian)
> > +		return;
> > +	bheader->dqdh_next_free = bswap_32(bheader->dqdh_next_free);
> > +	bheader->dqdh_prev_free = bswap_32(bheader->dqdh_prev_free);
> > +	bheader->dqdh_entries = bswap_16(bheader->dqdh_entries);
> > +}
> > +
> > +/* Should be power of two */
> > +#define DEFAULT_QUOTA_HASH_SIZE 8192
> > +/* Maxinum number of hash buckets - use at most 16 MB on a 64-bit arch */
> > +#define MAX_QUOTA_HASH_SIZE (1<<21)
> > +
> > +errcode_t ocfs2_new_quota_hash(ocfs2_quota_hash **hashp)
> > +{
> > +	ocfs2_quota_hash *hash;
> > +	errcode_t err;
> > +
> > +	err = ocfs2_malloc(sizeof(ocfs2_quota_hash), &hash);
> > +	if (err)
> > +		return err;
> > +	hash->alloc_entries = DEFAULT_QUOTA_HASH_SIZE;
> > +	hash->used_entries = 0;
> > +	err = ocfs2_malloc0(sizeof(ocfs2_quota_hash *) *
> > +			    DEFAULT_QUOTA_HASH_SIZE, &hash->hash);
> > +	if (err) {
> > +		ocfs2_free(&hash);
> > +		return err;
> > +	}
> > +	*hashp = hash;
> > +	return 0;
> > +}
> > +
> > +errcode_t ocfs2_free_quota_hash(ocfs2_quota_hash *hash)
> > +{
> > +	errcode_t err = 0, ret;
> > +
> > +	if (hash->used_entries)
> > +		return OCFS2_ET_NONEMTY_QUOTA_HASH;
> > +	ret = ocfs2_free(&hash->hash);
> > +	if (!err && ret)
> > +		err = ret;
> > +	ret = ocfs2_free(&hash);
> > +	if (!err && ret)
> > +		err = ret;
> > +	return err;
> > +}
> > +
> > +static int quota_hash(ocfs2_quota_hash *hash, qid_t id)
> > +{
> > +	return (((unsigned long)id) * 5) & (hash->alloc_entries - 1);
> > +}
> > +
> > +static void quota_add_hash_chain(ocfs2_quota_hash *hash,
> > +				 ocfs2_cached_dquot *dquot)
> > +{
> > +	int hash_val = quota_hash(hash, dquot->d_ddquot.dqb_id);
> > +
> > +	dquot->d_next = hash->hash[hash_val];
> > +	if (dquot->d_next)
> > +		dquot->d_next->d_pprev = &dquot->d_next;
> > +	hash->hash[hash_val] = dquot;
> > +	dquot->d_pprev = hash->hash + hash_val;
> > +}
> > +
> > +errcode_t ocfs2_insert_quota_hash(ocfs2_quota_hash *hash,
> > +				  ocfs2_cached_dquot *dquot)
> > +{
> > +	errcode_t err;
> > +
> > +	if (hash->used_entries > hash->alloc_entries &&
> > +	    hash->alloc_entries * 2 < MAX_QUOTA_HASH_SIZE) {
> > +		ocfs2_cached_dquot **new_hash, **old_hash;
> > +		ocfs2_cached_dquot *h_dquot, *h_next;
> > +		int i;
> > +		int old_entries;
> > +
> > +		err = ocfs2_malloc0(sizeof(ocfs2_quota_hash *) *
> > +				    hash->alloc_entries * 2, &new_hash);
> > +		if (err)
> > +			return err;
> > +		old_entries = hash->alloc_entries;
> > +		old_hash = hash->hash;
> > +		hash->alloc_entries *= 2;
> > +		hash->hash = new_hash;
> > +		/* Rehash */
> > +		for (i = 0; i < old_entries; i++) {
> > +			for (h_dquot = old_hash[i]; h_dquot; h_dquot = h_next) {
> > +				h_next = h_dquot->d_next;
> > +				quota_add_hash_chain(hash, h_dquot);
> > +			}
> > +		}
> > +		err = ocfs2_free(&old_hash);
> > +		if (err)
> > +			return err;
> > +	}
> > +	quota_add_hash_chain(hash, dquot);
> > +	hash->used_entries++;
> > +	return 0;
> > +}
> > +
> > +errcode_t ocfs2_remove_quota_hash(ocfs2_quota_hash *hash,
> > +				  ocfs2_cached_dquot *dquot)
> > +{
> > +	*(dquot->d_pprev) = dquot->d_next;
> > +	if (dquot->d_next)
> > +		dquot->d_next->d_pprev = dquot->d_pprev;
> > +	hash->used_entries--;
> > +	return 0;
> > +}
> > +
> > +errcode_t ocfs2_find_quota_hash(ocfs2_quota_hash *hash, qid_t id,
> > +				ocfs2_cached_dquot **dquotp)
> > +{
> > +	int hash_val = quota_hash(hash, id);
> > +	ocfs2_cached_dquot *dquot;
> > +
> > +	for (dquot = hash->hash[hash_val]; dquot; dquot = dquot->d_next) {
> > +		if (dquot->d_ddquot.dqb_id == id) {
> > +			*dquotp = dquot;
> > +			return 0;
> > +		}
> > +	}
> > +	*dquotp = NULL;
> > +	return 0;
> > +}
> > +
> > +errcode_t ocfs2_find_create_quota_hash(ocfs2_quota_hash *hash, qid_t id,
> > +				       ocfs2_cached_dquot **dquotp)
> > +{
> > +	errcode_t err;
> > +
> > +	err = ocfs2_find_quota_hash(hash, id, dquotp);
> > +	if (err)
> > +		return err;
> > +	if (*dquotp)
> > +		return 0;
> > +	err = ocfs2_malloc0(sizeof(ocfs2_cached_dquot), dquotp);
> > +	if (err)
> > +		return err;
> > +	(*dquotp)->d_ddquot.dqb_id = id;
> > +	err = ocfs2_insert_quota_hash(hash, *dquotp);
> > +	if (err) {
> > +		ocfs2_free(dquotp);
> > +		return err;
> > +	}
> > +	return 0;
> > +}
> > +
> > +errcode_t ocfs2_compute_quota_usage(ocfs2_filesys *fs,
> > +				    ocfs2_quota_hash *usr_hash,
> > +				    ocfs2_quota_hash *grp_hash)
> > +{
> > +	errcode_t err = 0;
> > +	ocfs2_inode_scan *scan;
> > +	uint64_t blkno;
> > +	char *buf;
> > +	int close_scan = 0;
> > +	struct ocfs2_dinode *di;
> > +	ocfs2_cached_dquot *dquot;
> > +
> > +	err = ocfs2_malloc_block(fs->fs_io, &buf);
> > +	if (err)
> > +		return err;
> > +	di = (struct ocfs2_dinode *)buf;
> > +
> > +	err = ocfs2_open_inode_scan(fs, &scan);
> > +	if (err)
> > +		goto out;
> > +	close_scan = 1;
> > +
> > +	while (1) {
> > +		err = ocfs2_get_next_inode(scan, &blkno, buf);
> > +		if (err || !blkno)
> > +			break;
> > +		/*
> > +		 * Check whether the inode looks reasonable and interesting
> > +		 * for quota
> > +		 */
> > +		if (memcmp(di->i_signature, OCFS2_INODE_SIGNATURE,
> > +			   strlen(OCFS2_INODE_SIGNATURE)))
> > +			continue;
> > +		ocfs2_swap_inode_to_cpu(di, fs->fs_blocksize);
> > +		if (di->i_fs_generation != fs->fs_super->i_fs_generation)
> > +			continue;
> > +		if (!(di->i_flags & OCFS2_VALID_FL))
> > +			continue;
> > +		if (di->i_flags & OCFS2_SYSTEM_FL &&
> > +		    blkno != OCFS2_RAW_SB(fs->fs_super)->s_root_blkno)
> > +			continue;
> > +		if (usr_hash) {
> > +			err = ocfs2_find_create_quota_hash(usr_hash, di->i_uid,
> > +							   &dquot);
> > +			if (err)
> > +				break;
> > +			dquot->d_ddquot.dqb_curspace +=
> > +				ocfs2_clusters_to_bytes(fs, di->i_clusters);
> > +			dquot->d_ddquot.dqb_curinodes++;
> > +		}
> > +		if (grp_hash) {
> > +			err = ocfs2_find_create_quota_hash(grp_hash, di->i_gid,
> > +							   &dquot);
> > +			if (err)
> > +				break;
> > +			dquot->d_ddquot.dqb_curspace +=
> > +				ocfs2_clusters_to_bytes(fs, di->i_clusters);
> > +			dquot->d_ddquot.dqb_curinodes++;
> > +		}
> > +	}
> > +out:
> > +	if (close_scan)
> > +		ocfs2_close_inode_scan(scan);
> > +	ocfs2_free(&buf);
> > +	return err;
> > +}
> > +
> > +errcode_t ocfs2_iterate_quota_hash(ocfs2_quota_hash *hash,
> > +				   errcode_t (*f)(ocfs2_cached_dquot *, void *),
> > +				   void *data)
> > +{
> > +	errcode_t err = 0;
> > +	int i;
> > +	ocfs2_cached_dquot *dquot, *next;
> > +
> > +	for (i = 0; i < hash->alloc_entries; i++)
> > +		for (dquot = hash->hash[i]; dquot; dquot = next) {
> > +			next = dquot->d_next;
> > +			err = f(dquot, data);
> > +			if (err)
> > +				goto out;
> > +		}
> > +out:
> > +	return err;
> > +}
> > +
> > +struct write_rel_ctx {
> > +	ocfs2_filesys *fs;
> > +	ocfs2_quota_hash *hash;
> > +	int type;
> > +};
> > +
> > +static errcode_t write_release_quota_hash(ocfs2_cached_dquot *dquot, void *p)
> > +{
> > +	struct write_rel_ctx *ctx = p;
> > +	errcode_t err;
> > +
> > +	if (!dquot->d_ddquot.dqb_isoftlimit ||
> > +	    dquot->d_ddquot.dqb_curinodes < dquot->d_ddquot.dqb_isoftlimit)
> > +		dquot->d_ddquot.dqb_itime = 0;
> > +	if (!dquot->d_ddquot.dqb_bsoftlimit ||
> > +	    dquot->d_ddquot.dqb_curspace < dquot->d_ddquot.dqb_bsoftlimit)
> > +		dquot->d_ddquot.dqb_btime = 0;
> > +
> > +	err = ocfs2_write_dquot(ctx->fs, ctx->type, dquot);
> > +	if (err)
> > +		return err;
> > +	err = ocfs2_remove_quota_hash(ctx->hash, dquot);
> > +	if (err)
> > +		return err;
> > +	return ocfs2_free(&dquot);
> > +}
> > +
> > +errcode_t ocfs2_write_release_dquots(ocfs2_filesys *fs, int type,
> > +				     ocfs2_quota_hash *hash)
> > +{
> > +	struct write_rel_ctx ctx;
> > +
> > +	ctx.fs = fs;
> > +	ctx.hash = hash;
> > +	ctx.type = type;
> > +
> > +	return ocfs2_iterate_quota_hash(hash, write_release_quota_hash, &ctx);
> > +}
> > +
> > +static void mark_quotafile_info_dirty(ocfs2_filesys *fs, int type)
> > +{
> > +	fs->qinfo[type].flags |= OCFS2_QF_INFO_DIRTY;
> > +}
> > +
> > +static void ocfs2_checksum_quota_block(ocfs2_filesys *fs, char *buf)
> > +{
> > +	struct ocfs2_disk_dqtrailer *dqt =
> > +			ocfs2_block_dqtrailer(fs->fs_blocksize, buf);
> > +
> > +	ocfs2_compute_meta_ecc(fs, buf, &dqt->dq_check);
> > +}
> > +
> > +#define OCFS2_LOCAL_QF_INIT_BLOCKS 2
> > +
> > +errcode_t ocfs2_init_local_quota_file(ocfs2_filesys *fs, int type,
> > +				      uint64_t blkno)
> > +{
> > +	ocfs2_cached_inode *ci = NULL;
> > +	struct ocfs2_dinode *di;
> > +	struct ocfs2_disk_dqheader *header;
> > +	struct ocfs2_local_disk_dqinfo *info;
> > +	unsigned int magics[] = OCFS2_LOCAL_QMAGICS;
> > +	int versions[] = OCFS2_LOCAL_QVERSIONS;
> > +	char *buf = NULL;
> > +	unsigned int written;
> > +	int bytes = ocfs2_blocks_to_bytes(fs, OCFS2_LOCAL_QF_INIT_BLOCKS);
> > +	errcode_t err;
> > +
> > +	err = ocfs2_read_cached_inode(fs, blkno, &ci);
> > +	if (err)
> > +		goto out;
> > +
> > +	if (!(ci->ci_inode->i_flags & OCFS2_VALID_FL) ||
> > +	    !(ci->ci_inode->i_flags & OCFS2_SYSTEM_FL) ||
> > +	    !(ci->ci_inode->i_flags & OCFS2_QUOTA_FL)) {
> > +		err = OCFS2_ET_INTERNAL_FAILURE;
> > +		goto out;
> > +	}
> > +	di = ci->ci_inode;
> > +
> > +	/* We need at least two blocks */
> > +	err = ocfs2_cached_inode_extend_allocation(ci,
> > +		ocfs2_clusters_in_blocks(fs, OCFS2_LOCAL_QF_INIT_BLOCKS));
> > +	if (err)
> > +		goto out;
> > +	di->i_size = bytes;
> > +	di->i_mtime = time(NULL);
> > +	err = ocfs2_write_inode(fs, blkno, (char *)di);
> > +	if (err)
> > +		goto out;
> > +
> > +	err = ocfs2_malloc_blocks(fs->fs_io, OCFS2_LOCAL_QF_INIT_BLOCKS, &buf);
> > +	if (err)
> > +		goto out;
> > +	memset(buf, 0, bytes);
> > +
> > +	header = (struct ocfs2_disk_dqheader *)buf;
> > +	header->dqh_magic = magics[type];
> > +	header->dqh_version = versions[type];
> > +	ocfs2_swap_quota_header(header);
> > +
> > +	info = (struct ocfs2_local_disk_dqinfo *)(buf + OCFS2_LOCAL_INFO_OFF);
> > +	info->dqi_chunks = 1;
> > +	info->dqi_blocks = OCFS2_LOCAL_QF_INIT_BLOCKS;
> > +	info->dqi_flags = OLQF_CLEAN;
> > +	ocfs2_swap_quota_local_info(info);
> > +
> > +	/* There are no free chunks because there are no blocks allocated for
> > +	 * them yet. So chunk header is all-zero and needs no initialization */
> > +	ocfs2_checksum_quota_block(fs, buf);
> > +	ocfs2_checksum_quota_block(fs, buf + fs->fs_blocksize);
> > +	err = ocfs2_file_write(ci, buf, bytes, 0, &written);
> > +	if (!err && written != bytes) {
> > +		err = OCFS2_ET_INTERNAL_FAILURE;
> > +		goto out;
> > +	}
> > +out:
> > +	if (ci)
> > +		ocfs2_free_cached_inode(fs, ci);
> > +	if (buf)
> > +		ocfs2_free(&buf);
> > +	return err;
> > +}
> > +
> > +errcode_t ocfs2_init_local_quota_files(ocfs2_filesys *fs, int type)
> > +{
> > +	int num_slots = OCFS2_RAW_SB(fs->fs_super)->s_max_slots;
> > +	char fname[OCFS2_MAX_FILENAME_LEN];
> > +	errcode_t ret;
> > +	uint64_t blkno;
> > +	int local_type = (type == USRQUOTA) ? LOCAL_USER_QUOTA_SYSTEM_INODE :
> > +					      LOCAL_GROUP_QUOTA_SYSTEM_INODE;
> > +	int i;
> > +
> > +	for (i = 0; i < num_slots; i++) {
> > +		ocfs2_sprintf_system_inode_name(fname, sizeof(fname),
> > +						local_type, i);
> > +		ret = ocfs2_lookup(fs, fs->fs_sysdir_blkno, fname,
> > +				   strlen(fname), NULL, &blkno);
> > +		if (ret)
> > +			return ret;
> > +		/* This is here mainly for fsck... */
> > +		ret = ocfs2_truncate(fs, blkno, 0);
> > +		if (ret)
> > +			return ret;
> > +		ret = ocfs2_init_local_quota_file(fs, type, blkno);
> > +		if (ret)
> > +			return ret;
> > +	}
> > +	return 0;
> > +}
> > +
> > +/* Return depth of quota tree in global file */
> > +int ocfs2_qtree_depth(int blocksize)
> > +{
> > +	unsigned int epb = (blocksize - OCFS2_QBLK_RESERVED_SPACE) >> 2;
> > +	unsigned long long entries = epb;
> > +	int i;
> > +
> > +	for (i = 1; entries < (1ULL << 32); i++)
> > +		entries *= epb;
> > +	return i;
> > +}
> > +
> > +/* Returns index of next block in the tree of dquots */
> > +static int ocfs2_qtree_index(int blocksize, qid_t id, int depth)
> > +{
> > +	unsigned int epb = (blocksize - OCFS2_QBLK_RESERVED_SPACE) >> 2;
> > +
> > +	depth = ocfs2_qtree_depth(blocksize) - depth - 1;
> > +	while (depth--)
> > +		id /= epb;
> > +	return id % epb;
> > +}
> > +
> > +/* Is given leaf entry unused? */
> > +int ocfs2_qtree_entry_unused(struct ocfs2_global_disk_dqblk *ddquot)
> > +{
> > +	static struct ocfs2_global_disk_dqblk empty;
> > +
> > +	return !memcmp(&empty, ddquot, sizeof(empty));
> > +}
> > +
> > +errcode_t ocfs2_init_fs_quota_info(ocfs2_filesys *fs, int type)
> > +{
> > +	int global_type = (type == USRQUOTA) ?
> > +				USER_QUOTA_SYSTEM_INODE :
> > +				GROUP_QUOTA_SYSTEM_INODE;
> > +	uint64_t blkno;
> > +	char fname[OCFS2_MAX_FILENAME_LEN];
> > +	errcode_t ret;
> > +
> > +	ocfs2_sprintf_system_inode_name(fname, sizeof(fname),
> > +		global_type, 0);
> > +	ret = ocfs2_lookup(fs, fs->fs_sysdir_blkno, fname, strlen(fname),
> > +			   NULL, &blkno);
> > +	if (ret)
> > +		return ret;
> > +	ret = ocfs2_read_cached_inode(fs, blkno, &(fs->qinfo[type].qi_inode));
> > +	if (ret)
> > +		return ret;
> > +	return 0;
> > +}
> > +
> > +/* Read given block */
> > +static errcode_t read_blk(ocfs2_filesys *fs, int type, unsigned int blk,
> > +			  char *buf)
> > +{
> > +	errcode_t err;
> > +	uint32_t got;
> > +	struct ocfs2_disk_dqtrailer *dqt =
> > +			ocfs2_block_dqtrailer(fs->fs_blocksize, buf);
> > +
> > +	err = ocfs2_file_read(fs->qinfo[type].qi_inode, buf,
> > +			      fs->fs_blocksize, blk * fs->fs_blocksize, &got);
> > +	if (err)
> > +		return err;
> > +	if (got != fs->fs_blocksize)
> > +		return OCFS2_ET_SHORT_READ;
> > +
> > +	return ocfs2_validate_meta_ecc(fs, buf, &dqt->dq_check);
> > +}
> > +
> > +/* Write given block */
> > +static errcode_t write_blk(ocfs2_filesys *fs, int type, unsigned int blk,
> > +			   char *buf)
> > +{
> > +	errcode_t err;
> > +	uint32_t written;
> > +
> > +	ocfs2_checksum_quota_block(fs, buf);
> > +
> > +	err = ocfs2_file_write(fs->qinfo[type].qi_inode, buf, fs->fs_blocksize,
> > +			       blk * fs->fs_blocksize, &written);
> > +	if (err)
> > +		return err;
> > +	if (written != fs->fs_blocksize)
> > +		return OCFS2_ET_SHORT_WRITE;
> > +	return 0;
> > +}
> > +
> > +errcode_t ocfs2_read_global_quota_info(ocfs2_filesys *fs, int type)
> > +{
> > +	char *buf;
> > +	errcode_t ret;
> > +	struct ocfs2_global_disk_dqinfo *info;
> > +
> > +	ret = ocfs2_malloc_block(fs->fs_io, &buf);
> > +	if (ret)
> > +		return ret;
> > +
> > +	ret = read_blk(fs, type, 0, buf);
> > +	if (ret)
> > +		return ret;
> > +	info = (struct ocfs2_global_disk_dqinfo *)(buf + OCFS2_GLOBAL_INFO_OFF);
> > +	ocfs2_swap_quota_global_info(info);
> > +	memcpy(&(fs->qinfo[type].qi_info), info,
> > +	       sizeof(struct ocfs2_global_disk_dqinfo));
> > +	return 0;
> > +}
> > +
> > +errcode_t ocfs2_write_global_quota_info(ocfs2_filesys *fs, int type)
> > +{
> > +	errcode_t ret;
> > +	char *buf;
> > +	struct ocfs2_disk_dqheader *header;
> > +	struct ocfs2_global_disk_dqinfo *info;
> > +	unsigned int magics[] = OCFS2_GLOBAL_QMAGICS;
> > +	int versions[] = OCFS2_GLOBAL_QVERSIONS;
> > +
> > +	ret = ocfs2_malloc_block(fs->fs_io, &buf);
> > +	if (ret)
> > +		return ret;
> > +	header = (struct ocfs2_disk_dqheader *)buf;
> > +	header->dqh_magic = magics[type];
> > +	header->dqh_version = versions[type];
> > +	ocfs2_swap_quota_header(header);
> > +
> > +	info = (struct ocfs2_global_disk_dqinfo *)(buf + OCFS2_GLOBAL_INFO_OFF);
> > +	memcpy(info, &(fs->qinfo[type].qi_info),
> > +	       sizeof(struct ocfs2_global_disk_dqinfo));
> > +	ocfs2_swap_quota_global_info(info);
> > +	ret = write_blk(fs, type, 0, buf);
> > +	if (ret)
> > +		goto bail;
> > +bail:
> > +	ocfs2_free(&buf);
> > +	return ret;
> > +}
> > +
> > +#define OCFS2_GLOBAL_QF_INIT_BLOCKS 2
> > +
> > +errcode_t ocfs2_init_global_quota_file(ocfs2_filesys *fs, int type)
> > +{
> > +	ocfs2_cached_inode *ci = fs->qinfo[type].qi_inode;
> > +	struct ocfs2_dinode *di;
> > +	char *buf = NULL;
> > +	struct ocfs2_disk_dqheader *header;
> > +	struct ocfs2_global_disk_dqinfo *info;
> > +	unsigned int magics[] = OCFS2_GLOBAL_QMAGICS;
> > +	int versions[] = OCFS2_GLOBAL_QVERSIONS;
> > +	errcode_t err;
> > +	int i;
> > +	int bytes = ocfs2_blocks_to_bytes(fs, OCFS2_GLOBAL_QF_INIT_BLOCKS);
> > +
> > +	if (!(ci->ci_inode->i_flags & OCFS2_VALID_FL) ||
> > +	    !(ci->ci_inode->i_flags & OCFS2_SYSTEM_FL) ||
> > +	    !(ci->ci_inode->i_flags & OCFS2_QUOTA_FL)) {
> > +		err = OCFS2_ET_INTERNAL_FAILURE;
> > +		goto out;
> > +	}
> > +	err = ocfs2_cached_inode_extend_allocation(ci,
> > +		ocfs2_clusters_in_blocks(fs, OCFS2_GLOBAL_QF_INIT_BLOCKS));
> > +	if (err)
> > +		goto out;
> > +
> > +	/* Mark info dirty so that quota inode gets written */
> > +	mark_quotafile_info_dirty(fs, type);
> > +
> > +	di = ci->ci_inode;
> > +	di->i_size = bytes;
> > +	di->i_mtime = time(NULL);
> > +
> > +	err = ocfs2_malloc_blocks(fs->fs_io, OCFS2_GLOBAL_QF_INIT_BLOCKS,
> > +				  &buf);
> > +	if (err)
> > +		goto out;
> > +	memset(buf, 0, bytes);
> > +
> > +	header = (struct ocfs2_disk_dqheader *)buf;
> > +	header->dqh_magic = magics[type];
> > +	header->dqh_version = versions[type];
> > +	ocfs2_swap_quota_header(header);
> > +
> > +	fs->qinfo[type].qi_info.dqi_blocks = OCFS2_GLOBAL_QF_INIT_BLOCKS;
> > +	fs->qinfo[type].qi_info.dqi_free_blk = 0;
> > +	fs->qinfo[type].qi_info.dqi_free_entry = 0;
> > +
> > +	info = (struct ocfs2_global_disk_dqinfo *)(buf + OCFS2_GLOBAL_INFO_OFF);
> > +	info->dqi_bgrace = fs->qinfo[type].qi_info.dqi_bgrace;
> > +	info->dqi_igrace = fs->qinfo[type].qi_info.dqi_igrace;
> > +	info->dqi_syncms = fs->qinfo[type].qi_info.dqi_syncms;
> > +	info->dqi_blocks = OCFS2_GLOBAL_QF_INIT_BLOCKS;
> > +	info->dqi_free_blk = 0;
> > +	info->dqi_free_entry = 0;
> > +	ocfs2_swap_quota_global_info(info);
> > +
> > +	/*
> > +	 * Write the buffer here so that all the headers are properly written.
> > +	 * Normally we don't write tree root block.
> > +	 */
> > +	for (i = 0; i < OCFS2_GLOBAL_QF_INIT_BLOCKS; i++) {
> > +		err = write_blk(fs, type, i, buf + (i * fs->fs_blocksize));
> > +		if (err)
> > +			goto out;
> > +	}
> > +out:
> > +	if (buf)
> > +		ocfs2_free(&buf);
> > +	return err;
> > +}
> > +
> > +/* Is given dquot empty? */
> > +static int ocfs2_global_entry_unused(struct ocfs2_global_disk_dqblk *ddqblk)
> > +{
> > +	static struct ocfs2_global_disk_dqblk empty;
> > +
> > +	return !memcmp(&empty, ddqblk, sizeof(empty));
> > +}
> > +
> > +/* Get free block in file (either from free list or create new one) */
> > +static errcode_t ocfs2_get_free_dqblk(ocfs2_filesys *fs, int type,
> > +				      unsigned int *blk)
> > +{
> > +	errcode_t err;
> > +	char *buf;
> > +	struct qt_disk_dqdbheader *dh;
> > +	struct ocfs2_global_disk_dqinfo *info = &(fs->qinfo[type].qi_info);
> > +	ocfs2_cached_inode *ci = fs->qinfo[type].qi_inode;
> > +
> > +	err = ocfs2_malloc_block(fs->fs_io, &buf);
> > +	if (err)
> > +		return err;
> > +	dh = (struct qt_disk_dqdbheader *)buf;
> > +	if (info->dqi_free_blk) {
> > +		*blk = info->dqi_free_blk;
> > +		err = read_blk(fs, type, *blk, buf);
> > +		if (err)
> > +			goto bail;
> > +		info->dqi_free_blk = le32_to_cpu(dh->dqdh_next_free);
> > +	}
> > +	else {
> > +		if (info->dqi_blocks ==
> > +		    ocfs2_clusters_to_blocks(fs, ci->ci_inode->i_clusters)) {
> > +			err = ocfs2_cached_inode_extend_allocation(ci, 1);
> > +			if (err)
> > +				goto bail;
> > +		}
> > +		*blk = info->dqi_blocks++;
> > +		ci->ci_inode->i_size =
> > +				ocfs2_blocks_to_bytes(fs, info->dqi_blocks);
> > +	}
> > +	mark_quotafile_info_dirty(fs, type);
> > +bail:
> > +	ocfs2_free(&buf);
> > +	return err;
> > +}
> > +
> > +/* Put given block to free list */
> > +static errcode_t ocfs2_put_free_dqblk(ocfs2_filesys *fs, int type,
> > +				      char *buf, unsigned int blk)
> > +{
> > +	errcode_t err;
> > +	struct qt_disk_dqdbheader *dh = (struct qt_disk_dqdbheader *)buf;
> > +	struct ocfs2_global_disk_dqinfo *info = &(fs->qinfo[type].qi_info);
> > +
> > +	dh->dqdh_next_free = info->dqi_free_blk;
> > +	dh->dqdh_prev_free = 0;
> > +	dh->dqdh_entries = 0;
> > +	ocfs2_swap_quota_leaf_block_header(dh);
> > +	err = write_blk(fs, type, blk, buf);
> > +	ocfs2_swap_quota_leaf_block_header(dh);
> > +	if (err)
> > +		return err;
> > +	info->dqi_free_blk = blk;
> > +	mark_quotafile_info_dirty(fs, type);
> > +	return 0;
> > +}
> > +
> > +/* Remove given block from the list of blocks with free entries */
> > +static errcode_t ocfs2_remove_free_dqentry(ocfs2_filesys *fs, int type,
> > +					   char *buf, unsigned int blk)
> > +{
> > +	errcode_t err;
> > +	char *tmpbuf;
> > +	struct qt_disk_dqdbheader *dh, *tdh;
> > +	unsigned int nextblk, prevblk;
> > +
> > +	err = ocfs2_malloc_block(fs->fs_io, &tmpbuf);
> > +	if (err)
> > +		return err;
> > +	dh = (struct qt_disk_dqdbheader *)buf;
> > +	tdh = (struct qt_disk_dqdbheader *)tmpbuf;
> > +	nextblk = dh->dqdh_next_free;
> > +	prevblk = dh->dqdh_prev_free;
> > +
> > +	if (nextblk) {
> > +		err = read_blk(fs, type, nextblk, tmpbuf);
> > +		if (err)
> > +			goto bail;
> > +		ocfs2_swap_quota_leaf_block_header(tdh);
> > +		tdh->dqdh_prev_free = prevblk;
> > +		ocfs2_swap_quota_leaf_block_header(tdh);
> > +		err = write_blk(fs, type, nextblk, tmpbuf);
> > +		if (err)
> > +			goto bail;
> > +	}
> > +	if (prevblk) {
> > +		/* Failure here is bad since we potentially corrupt free list.
> > +		 * OTOH something must be really wrong when read/write fails */
> > +		err = read_blk(fs, type, prevblk, tmpbuf);
> > +		if (err)
> > +			goto bail;
> > +		ocfs2_swap_quota_leaf_block_header(tdh);
> > +		tdh->dqdh_next_free = nextblk;
> > +		ocfs2_swap_quota_leaf_block_header(tdh);
> > +		err = write_blk(fs, type, prevblk, tmpbuf);
> > +		if (err)
> > +			goto bail;
> > +	}
> > +	else {
> > +		fs->qinfo[type].qi_info.dqi_free_entry = nextblk;
> > +		mark_quotafile_info_dirty(fs, type);
> > +	}
> > +	dh->dqdh_next_free = dh->dqdh_prev_free = 0;
> > +	ocfs2_swap_quota_leaf_block_header(dh);
> > +	/* No matter whether write succeeds block is out of list */
> > +	write_blk(fs, type, blk, buf);
> > +	ocfs2_swap_quota_leaf_block_header(dh);
> > +bail:
> > +	ocfs2_free(&tmpbuf);
> > +	return err;
> > +}
> > +
> > +/* Insert given block to the beginning of list with free entries */
> > +static errcode_t ocfs2_insert_free_dqentry(ocfs2_filesys *fs, int type,
> > +					   char *buf, unsigned int blk)
> > +{
> > +	errcode_t err;
> > +	char *tmpbuf;
> > +	struct qt_disk_dqdbheader *tdh, *dh = (struct qt_disk_dqdbheader *)buf;
> > +	struct ocfs2_global_disk_dqinfo *info = &(fs->qinfo[type].qi_info);
> > +
> > +	err = ocfs2_malloc_block(fs->fs_io, &tmpbuf);
> > +	if (err)
> > +		return err;
> > +	dh->dqdh_next_free = info->dqi_free_entry;
> > +	dh->dqdh_prev_free = 0;
> > +	ocfs2_swap_quota_leaf_block_header(dh);
> > +	err = write_blk(fs, type, blk, buf);
> > +	ocfs2_swap_quota_leaf_block_header(dh);
> > +	if (err)
> > +		goto bail;
> > +
> > +	if (info->dqi_free_entry) {
> > +		tdh = (struct qt_disk_dqdbheader *)tmpbuf;
> > +		err = read_blk(fs, type, info->dqi_free_entry, tmpbuf);
> > +		if (err)
> > +			goto bail;
> > +		ocfs2_swap_quota_leaf_block_header(tdh);
> > +		tdh->dqdh_prev_free = blk;
> > +		ocfs2_swap_quota_leaf_block_header(tdh);
> > +		err = write_blk(fs, type, info->dqi_free_entry, tmpbuf);
> > +		if (err)
> > +			goto bail;
> > +	}
> > +	info->dqi_free_entry = blk;
> > +	mark_quotafile_info_dirty(fs, type);
> > +bail:
> > +	ocfs2_free(&tmpbuf);
> > +	return err;
> > +}
> > +
> > +/* Find space for dquot */
> > +static errcode_t ocfs2_find_free_dqentry(ocfs2_filesys *fs, int type,
> > +					 unsigned int *treeblk, loff_t *off)
> > +{
> > +	errcode_t err;
> > +	unsigned int blk, i;
> > +	struct ocfs2_global_disk_dqblk *ddquot;
> > +	struct qt_disk_dqdbheader *dh;
> > +	struct ocfs2_global_disk_dqinfo *info = &(fs->qinfo[type].qi_info);
> > +	char *buf;
> > +
> > +	err = ocfs2_malloc_block(fs->fs_io, &buf);
> > +	if (err)
> > +		return err;
> > +	dh = (struct qt_disk_dqdbheader *)buf;
> > +	ddquot = (struct ocfs2_global_disk_dqblk *)(buf +
> > +		 sizeof(struct qt_disk_dqdbheader));
> > +	if (info->dqi_free_entry) {
> > +		blk = info->dqi_free_entry;
> > +		err = read_blk(fs, type, blk, buf);
> > +		if (err)
> > +			goto bail;
> > +		ocfs2_swap_quota_leaf_block_header(dh);
> > +	}
> > +	else {
> > +		err = ocfs2_get_free_dqblk(fs, type, &blk);
> > +		if (err)
> > +			goto bail;
> > +		memset(buf, 0, fs->fs_blocksize);
> > +		info->dqi_free_entry = blk;
> > +		mark_quotafile_info_dirty(fs, type);
> > +	}
> > +	/* Block will be full? */
> > +	if (dh->dqdh_entries + 1 >=
> > +	    ocfs2_global_dqstr_in_blk(fs->fs_blocksize)) {
> > +		err = ocfs2_remove_free_dqentry(fs, type, buf, blk);
> > +		if (err)
> > +			goto bail;
> > +	}
> > +	dh->dqdh_entries++;
> > +	/* Find free structure in block */
> > +	for (i = 0;
> > +	     i < ocfs2_global_dqstr_in_blk(fs->fs_blocksize) &&
> > +	     !ocfs2_global_entry_unused(ddquot + i);
> > +	     i++);
> > +	if (i == ocfs2_global_dqstr_in_blk(fs->fs_blocksize)) {
> > +		err = OCFS2_ET_CORRUPT_QUOTA_FILE;
> > +		goto bail;
> > +	}
> > +	ocfs2_swap_quota_leaf_block_header(dh);
> > +	err = write_blk(fs, type, blk, buf);
> > +	if (err)
> > +		goto bail;
> > +	*off = (blk * fs->fs_blocksize) + sizeof(struct qt_disk_dqdbheader) +
> > +	       i * sizeof(struct ocfs2_global_disk_dqblk);
> > +	*treeblk = blk;
> > +bail:
> > +	ocfs2_free(&buf);
> > +	return err;
> > +}
> > +
> > +/* Insert reference to structure into the trie */
> > +static errcode_t ocfs2_do_insert_tree(ocfs2_filesys *fs, int type, qid_t id,
> > +				      unsigned int *treeblk, int depth,
> > +				      loff_t *off)
> > +{
> > +	char *buf;
> > +	int newson = 0, newact = 0;
> > +	u_int32_t *ref;
> > +	unsigned int newblk;
> > +	errcode_t err;
> > +
> > +	err = ocfs2_malloc_block(fs->fs_io, &buf);
> > +	if (err)
> > +		return err;
> > +	if (!*treeblk) {
> > +		err = ocfs2_get_free_dqblk(fs, type, &newblk);
> > +		if (err)
> > +			goto bail;
> > +		*treeblk = newblk;
> > +		memset(buf, 0, fs->fs_blocksize);
> > +		newact = 1;
> > +	}
> > +	else {
> > +		err = read_blk(fs, type, *treeblk, buf);
> > +		if (err)
> > +			goto bail;
> > +	}
> > +	ref = (u_int32_t *) buf;
> > +	newblk = le32_to_cpu(ref[
> > +		 ocfs2_qtree_index(fs->fs_blocksize, id, depth)]);
> > +	if (!newblk)
> > +		newson = 1;
> > +	if (depth == ocfs2_qtree_depth(fs->fs_blocksize) - 1) {
> > +		if (newblk) {
> > +			err = OCFS2_ET_CORRUPT_QUOTA_FILE;
> > +			goto bail;
> > +		}
> > +		err = ocfs2_find_free_dqentry(fs, type, &newblk, off);
> > +	}
> > +	else
> > +		err = ocfs2_do_insert_tree(fs, type, id, &newblk, depth + 1,
> > +					   off);
> > +	if (newson && !err) {
> > +		ref[ocfs2_qtree_index(fs->fs_blocksize, id, depth)] =
> > +							cpu_to_le32(newblk);
> > +		err = write_blk(fs, type, *treeblk, buf);
> > +	}
> > +	else if (newact && err)
> > +		ocfs2_put_free_dqblk(fs, type, buf, *treeblk);
> > +bail:
> > +	ocfs2_free(&buf);
> > +	return err;
> > +}
> > +
> > +/* Wrapper for inserting quota structure into tree */
> > +static errcode_t ocfs2_insert_qtree(ocfs2_filesys *fs, int type, qid_t id,
> > +				    loff_t *off)
> > +{
> > +	unsigned int tmp = QT_TREEOFF;
> > +
> > +	return ocfs2_do_insert_tree(fs, type, id, &tmp, 0, off);
> > +}
> > +
> > +/* Write dquot to file */
> > +errcode_t ocfs2_write_dquot(ocfs2_filesys *fs, int type,
> > +			    ocfs2_cached_dquot *dquot)
> > +{
> > +	errcode_t err;
> > +	char *buf;
> > +	struct ocfs2_global_disk_dqblk *ddquot;
> > +
> > +	err = ocfs2_malloc_block(fs->fs_io, &buf);
> > +	if (err)
> > +		return err;
> > +
> > +	if (!dquot->d_off) {
> > +		err = ocfs2_insert_qtree(fs, type, dquot->d_ddquot.dqb_id,
> > +					 &dquot->d_off);
> > +		if (err)
> > +			goto bail;
> > +	}
> > +	err = read_blk(fs, type, dquot->d_off / fs->fs_blocksize, buf);
> > +	if (err)
> > +		goto bail;
> > +	ddquot = (struct ocfs2_global_disk_dqblk *)(buf +
> > +					(dquot->d_off % fs->fs_blocksize));
> > +	memcpy(ddquot, &dquot->d_ddquot,
> > +	       sizeof(struct ocfs2_global_disk_dqblk));
> > +	ddquot->dqb_pad1 = 0;
> > +	ddquot->dqb_pad2 = 0;
> > +	ocfs2_swap_quota_global_dqblk(ddquot);
> > +	err = write_blk(fs, type, dquot->d_off / fs->fs_blocksize, buf);
> > +bail:
> > +	ocfs2_free(&buf);
> > +	return err;
> > +}
> > +
> > +/* Remove dquot entry from its data block */
> > +static errcode_t ocfs2_remove_leaf_dqentry(ocfs2_filesys *fs,
> > +					   int type,
> > +					   ocfs2_cached_dquot *dquot,
> > +					   unsigned int blk)
> > +{
> > +	errcode_t err;
> > +	char *buf;
> > +	struct qt_disk_dqdbheader *dh;
> > +
> > +	if (blk != dquot->d_off / fs->fs_blocksize)
> > +		return OCFS2_ET_CORRUPT_QUOTA_FILE;
> > +
> > +	err = ocfs2_malloc_block(fs->fs_io, &buf);
> > +	if (err)
> > +		return err;
> > +
> > +	err = read_blk(fs, type, blk, buf);
> > +	if (err)
> > +		goto bail;
> > +
> > +	dh = (struct qt_disk_dqdbheader *)buf;
> > +	ocfs2_swap_quota_leaf_block_header(dh);
> > +	dh->dqdh_entries--;
> > +	if (!dh->dqdh_entries) {	/* Block got free? */
> > +		err = ocfs2_remove_free_dqentry(fs, type, buf, blk);
> > +		if (err)
> > +			goto bail;
> > +		err = ocfs2_put_free_dqblk(fs, type, buf, blk);
> > +		if (err)
> > +			goto bail;
> > +	}
> > +	else {
> > +		memset(buf + (dquot->d_off & (fs->fs_blocksize - 1)), 0,
> > +		       sizeof(struct ocfs2_global_disk_dqblk));
> > +
> > +		/* First free entry? */
> > +		if (dh->dqdh_entries ==
> > +		    ocfs2_global_dqstr_in_blk(fs->fs_blocksize) - 1) {
> > +			/* This will also write data block */
> > +			err = ocfs2_insert_free_dqentry(fs, type, buf, blk);
> > +		}
> > +		else
> > +			err = write_blk(fs, type, blk, buf);
> > +	}
> > +	dquot->d_off = 0;
> > +bail:
> > +	ocfs2_free(&buf);
> > +
> > +	return err;
> > +}
> > +
> > +/* Remove reference to dquot from tree */
> > +static errcode_t ocfs2_remove_tree_dqentry(ocfs2_filesys *fs,
> > +					   int type,
> > +					   ocfs2_cached_dquot *dquot,
> > +					   unsigned int *blk,
> > +					   int depth)
> > +{
> > +	errcode_t err;
> > +	char *buf;
> > +	unsigned int newblk;
> > +	u_int32_t *ref;
> > +
> > +	err = ocfs2_malloc_block(fs->fs_io, &buf);
> > +	if (err)
> > +		return err;
> > +
> > +	err = read_blk(fs, type, *blk, buf);
> > +	if (err)
> > +		goto bail;
> > +
> > +	ref = (u_int32_t *)buf;
> > +	newblk = le32_to_cpu(ref[ocfs2_qtree_index(fs->fs_blocksize,
> > +		 dquot->d_ddquot.dqb_id, depth)]);
> > +	if (depth == ocfs2_qtree_depth(fs->fs_blocksize) - 1) {
> > +		err = ocfs2_remove_leaf_dqentry(fs, type, dquot, newblk);
> > +		newblk = 0;
> > +	}
> > +	else
> > +		err = ocfs2_remove_tree_dqentry(fs, type, dquot, &newblk,
> > +						depth + 1);
> > +	if (err)
> > +		goto bail;
> > +
> > +	if (!newblk) {
> > +		int i;
> > +
> > +		ref[ocfs2_qtree_index(fs->fs_blocksize,
> > +				      dquot->d_ddquot.dqb_id,
> > +				      depth)] = cpu_to_le32(0);
> > +		/* Block got empty? */
> > +		for (i = 0; i < fs->fs_blocksize - OCFS2_QBLK_RESERVED_SPACE &&
> > +		     !buf[i]; i++);
> > +		/* Don't put the root block into the free block list */
> > +		if (i == fs->fs_blocksize - OCFS2_QBLK_RESERVED_SPACE &&
> > +		    *blk != QT_TREEOFF) {
> > +			err = ocfs2_put_free_dqblk(fs, type, buf, *blk);
> > +			if (err)
> > +				goto bail;
> > +			*blk = 0;
> > +		}
> > +		else
> > +			err = write_blk(fs, type, *blk, buf);
> > +	}
> > +bail:
> > +	ocfs2_free(&buf);
> > +
> > +	return err;
> > +}
> > +
> > +/* Delete dquot from tree */
> > +errcode_t ocfs2_delete_dquot(ocfs2_filesys *fs, int type,
> > +			     ocfs2_cached_dquot *dquot)
> > +{
> > +	unsigned int tmp = QT_TREEOFF;
> > +
> > +	if (!dquot->d_off)	/* Even not allocated? */
> > +		return 0;
> > +	return ocfs2_remove_tree_dqentry(fs, type, dquot, &tmp, 0);
> > +}
> > +
> > +/* Find entry in block */
> > +static errcode_t ocfs2_find_block_dqentry(ocfs2_filesys *fs, int type,
> > +					  ocfs2_cached_dquot *dquot,
> > +					  unsigned int blk)
> > +{
> > +	char *buf;
> > +	errcode_t err;
> > +	int i;
> > +	struct ocfs2_global_disk_dqblk *ddquot;
> > +
> > +	err = ocfs2_malloc_block(fs->fs_io, &buf);
> > +	if (err)
> > +		return err;
> > +
> > +	err = read_blk(fs, type, blk, buf);
> > +	if (err)
> > +		goto bail;
> > +
> > +	ddquot = (struct ocfs2_global_disk_dqblk *)(buf +
> > +		 sizeof(struct qt_disk_dqdbheader));
> > +
> > +	for (i = 0; i < ocfs2_global_dqstr_in_blk(fs->fs_blocksize);
> > +	     i++, ddquot++) {
> > +		if (le32_to_cpu(ddquot->dqb_id) == dquot->d_ddquot.dqb_id) {
> > +			if (dquot->d_ddquot.dqb_id == 0 &&
> > +			    ocfs2_qtree_entry_unused(ddquot))
> > +				continue;
> > +			break;
> > +		}
> > +	}
> > +	if (i == ocfs2_global_dqstr_in_blk(fs->fs_blocksize)) {
> > +		err = OCFS2_ET_CORRUPT_QUOTA_FILE;
> > +		goto bail;
> > +	}
> > +	dquot->d_off = blk * fs->fs_blocksize + ((char *)ddquot - buf);
> > +	memcpy(&dquot->d_ddquot, ddquot,
> > +	       sizeof(struct ocfs2_global_disk_dqblk));
> > +	ocfs2_swap_quota_global_dqblk(&dquot->d_ddquot);
> > +bail:
> > +	ocfs2_free(&buf);
> > +	return err;
> > +}
> > +
> > +/* Find entry for given id in the tree */
> > +static errcode_t ocfs2_find_tree_dqentry(ocfs2_filesys *fs,
> > +					 int type,
> > +					 ocfs2_cached_dquot *dquot,
> > +					 unsigned int blk,
> > +					 int depth)
> > +{
> > +	errcode_t err;
> > +	char *buf;
> > +	u_int32_t *ref;
> > +
> > +	err = ocfs2_malloc_block(fs->fs_io, &buf);
> > +	if (err)
> > +		return err;
> > +
> > +	err = read_blk(fs, type, blk, buf);
> > +	if (err)
> > +		goto bail;
> > +	ref = (u_int32_t *)buf;
> > +	blk = le32_to_cpu(ref[ocfs2_qtree_index(fs->fs_blocksize,
> > +	      dquot->d_ddquot.dqb_id, depth)]);
> > +	if (!blk)		/* No reference? */
> > +		goto bail;
> > +	if (depth < ocfs2_qtree_depth(fs->fs_blocksize) - 1)
> > +		err = ocfs2_find_tree_dqentry(fs, type, dquot, blk, depth + 1);
> > +	else
> > +		err = ocfs2_find_block_dqentry(fs, type, dquot, blk);
> > +bail:
> > +	ocfs2_free(&buf);
> > +	return err;
> > +}
> > +
> > +/*
> > + *  Read dquot from disk
> > + */
> > +errcode_t ocfs2_read_dquot(ocfs2_filesys *fs, int type, qid_t id,
> > +			   ocfs2_cached_dquot **ret_dquot)
> 
> One concern, the second argument 'id' never in function.
  Oh, good catch. The function works correctly only for the root user.
Because it is used only when disabling sparse feature and I've tested that
as root, I didn't notice.

> Another thing is, you malloc the space for &dquot, then pass it to
> caller's arguments, and we're noticing that the caller will be
> responsible for the relasing of allocated space instead of function
> itself, if it run without error.  right?
> 
> unfortunately, i didn't see any caller(such as fill_sparse_files() in
> tunefs.ocfs2) finally release the space...
  And this is true as well. Thanks.

> To keep consistent with other tool's code(such as ocfs2_read_inode()), i
> think you may allocate/free the space by caller?
  This is consistent with ocfs2_read_cached_inode() which allocates the
cached structure. So I don't think it needs to be changed. I'll just fix
fill_sparse_files() to properly release the structure.

> > +{
> > +	errcode_t err;
> > +	ocfs2_cached_dquot *dquot;
> > +
> > +	err = ocfs2_malloc0(sizeof(ocfs2_cached_dquot), &dquot);
> > +	if (err)
> > +		return err;
> > +
> > +	err = ocfs2_find_tree_dqentry(fs, type, dquot, QT_TREEOFF, 0);
> > +	if (err)
> > +		goto bail;
> > +	*ret_dquot = dquot;
> > +	return 0;
> > +bail:
> > +	ocfs2_free(&dquot);
> > +	return err;
> > +}
  Thanks for review.

								Honza
-- 
Jan Kara <jack at suse.cz>
SUSE Labs, CR



More information about the Ocfs2-tools-devel mailing list