All of lore.kernel.org
 help / color / mirror / Atom feed
* [Ocfs2-devel] [PATCH 0/8] Quota support for ocfs2-tools
@ 2009-07-27 17:53 Jan Kara
  2009-07-27 17:53 ` [Ocfs2-devel] [PATCH 1/8] Update ocfs2_fs.h to contain all necessary quota structures and constants Jan Kara
                   ` (7 more replies)
  0 siblings, 8 replies; 32+ messages in thread
From: Jan Kara @ 2009-07-27 17:53 UTC (permalink / raw)
  To: ocfs2-devel

  Hi,

  I'm sending a series of patches implementing quota support into ocfs2-tools.
It's the same as the original huge patch I've sent but now it's split as Joel
asked. I've also realized that when disabling SPARSE feature, we should update
quota information. That piece of code is missing, I'll implement it soon.
  Comments welcome.

									Honza

^ permalink raw reply	[flat|nested] 32+ messages in thread

* [Ocfs2-devel] [PATCH 1/8] Update ocfs2_fs.h to contain all necessary quota structures and constants
  2009-07-27 17:53 [Ocfs2-devel] [PATCH 0/8] Quota support for ocfs2-tools Jan Kara
@ 2009-07-27 17:53 ` Jan Kara
  2009-07-28 21:45   ` Joel Becker
  2009-07-27 17:53 ` [Ocfs2-devel] [PATCH 2/8] Provide ocfs2_cached_inode_extend_allocation() Jan Kara
                   ` (6 subsequent siblings)
  7 siblings, 1 reply; 32+ messages in thread
From: Jan Kara @ 2009-07-27 17:53 UTC (permalink / raw)
  To: ocfs2-devel

Signed-off-by: Jan Kara <jack@suse.cz>
---
 include/ocfs2-kernel/ocfs2_fs.h |   26 ++++++++++++++++++++++++++
 1 files changed, 26 insertions(+), 0 deletions(-)

diff --git a/include/ocfs2-kernel/ocfs2_fs.h b/include/ocfs2-kernel/ocfs2_fs.h
index 2140bb3..ad655dc 100644
--- a/include/ocfs2-kernel/ocfs2_fs.h
+++ b/include/ocfs2-kernel/ocfs2_fs.h
@@ -344,6 +344,8 @@ enum {
 #define OCFS2_FIRST_ONLINE_SYSTEM_INODE SLOT_MAP_SYSTEM_INODE
 	HEARTBEAT_SYSTEM_INODE,
 	GLOBAL_BITMAP_SYSTEM_INODE,
+	USER_QUOTA_SYSTEM_INODE,
+	GROUP_QUOTA_SYSTEM_INODE,
 #define OCFS2_LAST_GLOBAL_SYSTEM_INODE GLOBAL_BITMAP_SYSTEM_INODE
 	ORPHAN_DIR_SYSTEM_INODE,
 	EXTENT_ALLOC_SYSTEM_INODE,
@@ -351,6 +353,8 @@ enum {
 	JOURNAL_SYSTEM_INODE,
 	LOCAL_ALLOC_SYSTEM_INODE,
 	TRUNCATE_LOG_SYSTEM_INODE,
+	LOCAL_USER_QUOTA_SYSTEM_INODE,
+	LOCAL_GROUP_QUOTA_SYSTEM_INODE,
 	NUM_SYSTEM_INODES
 };
 
@@ -364,6 +368,8 @@ static struct ocfs2_system_inode_info ocfs2_system_inodes[NUM_SYSTEM_INODES] = {
 	[SLOT_MAP_SYSTEM_INODE]			= { "slot_map", 0, S_IFREG | 0644 },
 	[HEARTBEAT_SYSTEM_INODE]		= { "heartbeat", OCFS2_HEARTBEAT_FL, S_IFREG | 0644 },
 	[GLOBAL_BITMAP_SYSTEM_INODE]		= { "global_bitmap", 0, S_IFREG | 0644 },
+	[USER_QUOTA_SYSTEM_INODE]		= { "aquota.user", OCFS2_QUOTA_FL, S_IFREG | 0644 },
+	[GROUP_QUOTA_SYSTEM_INODE]		= { "aquota.group", OCFS2_QUOTA_FL, S_IFREG | 0644 },
 
 	/* Slot-specific system inodes (one copy per slot) */
 	[ORPHAN_DIR_SYSTEM_INODE]		= { "orphan_dir:%04d", 0, S_IFDIR | 0755 },
@@ -372,6 +378,8 @@ static struct ocfs2_system_inode_info ocfs2_system_inodes[NUM_SYSTEM_INODES] = {
 	[JOURNAL_SYSTEM_INODE]			= { "journal:%04d", OCFS2_JOURNAL_FL, S_IFREG | 0644 },
 	[LOCAL_ALLOC_SYSTEM_INODE]		= { "local_alloc:%04d", OCFS2_BITMAP_FL | OCFS2_LOCAL_ALLOC_FL, S_IFREG | 0644 },
 	[TRUNCATE_LOG_SYSTEM_INODE]		= { "truncate_log:%04d", OCFS2_DEALLOC_FL, S_IFREG | 0644 },
+	[LOCAL_USER_QUOTA_SYSTEM_INODE]		= { "aquota.user:%04d", OCFS2_QUOTA_FL, S_IFREG | 0644 },
+	[LOCAL_GROUP_QUOTA_SYSTEM_INODE]	= { "aquota.group:%04d", OCFS2_QUOTA_FL, S_IFREG | 0644 },
 };
 
 /* Parameter passed from mount.ocfs2 to module */
@@ -963,6 +971,7 @@ struct ocfs2_disk_dqheader {
 };
 
 #define OCFS2_GLOBAL_INFO_OFF (sizeof(struct ocfs2_disk_dqheader))
+#define OCFS2_GLOBAL_TREE_BLK 1
 
 /* Information header of global quota file (immediately follows the generic
  * header) */
@@ -977,6 +986,16 @@ struct ocfs2_global_disk_dqinfo {
 				 * file */
 };
 
+/* Header of leaf tree block */
+struct ocfs2_global_disk_dqdbheader {
+	__le32 dqdh_next_free;	/* Number of next block with free entry */
+	__le32 dqdh_prev_free;	/* Number of previous block with free entry */
+	__le16 dqdh_entries;	/* Number of valid entries in block */
+	__le16 dqdh_pad1;
+	__le32 dqdh_pad2;
+};
+
+
 /* Structure with global user / group information. We reserve some space
  * for future use. */
 struct ocfs2_global_disk_dqblk {
@@ -1187,6 +1206,13 @@ static inline u16 ocfs2_xattr_recs_per_xb(struct super_block *sb)
 	return size / sizeof(struct ocfs2_extent_rec);
 }
 #else
+static inline int ocfs2_global_dqstr_in_blk(int blocksize)
+{
+	return (blocksize - OCFS2_QBLK_RESERVED_SPACE -
+		sizeof(struct ocfs2_global_disk_dqdbheader)) /
+		sizeof(struct ocfs2_global_disk_dqblk);
+}
+
 static inline int ocfs2_fast_symlink_chars(int blocksize)
 {
 	return blocksize - offsetof(struct ocfs2_dinode, id2.i_symlink);
-- 
1.6.0.2

^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [Ocfs2-devel] [PATCH 2/8] Provide ocfs2_cached_inode_extend_allocation()
  2009-07-27 17:53 [Ocfs2-devel] [PATCH 0/8] Quota support for ocfs2-tools Jan Kara
  2009-07-27 17:53 ` [Ocfs2-devel] [PATCH 1/8] Update ocfs2_fs.h to contain all necessary quota structures and constants Jan Kara
@ 2009-07-27 17:53 ` Jan Kara
  2009-07-28 21:47   ` Joel Becker
  2009-07-27 17:53 ` [Ocfs2-devel] [PATCH 3/8] Implement quota functions to libocfs2 Jan Kara
                   ` (5 subsequent siblings)
  7 siblings, 1 reply; 32+ messages in thread
From: Jan Kara @ 2009-07-27 17:53 UTC (permalink / raw)
  To: ocfs2-devel

So far we had only ocfs2_extend_allocation() which read the inode from
disk. Provide also the cached variant.

Signed-off-by: Jan Kara <jack@suse.cz>
---
 include/ocfs2/ocfs2.h  |    2 +
 libocfs2/extend_file.c |   51 ++++++++++++++++++++++++++---------------------
 2 files changed, 30 insertions(+), 23 deletions(-)

diff --git a/include/ocfs2/ocfs2.h b/include/ocfs2/ocfs2.h
index 4dbbf43..ac16823 100644
--- a/include/ocfs2/ocfs2.h
+++ b/include/ocfs2/ocfs2.h
@@ -505,6 +505,8 @@ errcode_t ocfs2_delete_extent_block(ocfs2_filesys *fs, uint64_t blkno);
  */
 errcode_t ocfs2_extend_allocation(ocfs2_filesys *fs, uint64_t ino,
 				  uint32_t new_clusters);
+/* Ditto for cached inode */
+errcode_t ocfs2_cached_inode_extend_allocation(ocfs2_cached_inode *ci, uint32_t new_clusters);
 /* Extend the file to the new size. No clusters will be allocated. */
 errcode_t ocfs2_extend_file(ocfs2_filesys *fs, uint64_t ino, uint64_t new_size);
 
diff --git a/libocfs2/extend_file.c b/libocfs2/extend_file.c
index a27b478..c2029d4 100644
--- a/libocfs2/extend_file.c
+++ b/libocfs2/extend_file.c
@@ -3764,29 +3764,16 @@ out:
 	return ret;
 }
 
-errcode_t ocfs2_extend_allocation(ocfs2_filesys *fs, uint64_t ino,
-				  uint32_t new_clusters)
+errcode_t ocfs2_cached_inode_extend_allocation(ocfs2_cached_inode *ci,
+					       uint32_t new_clusters)
 {
 	errcode_t ret = 0;
 	uint32_t n_clusters = 0, cpos;
 	uint64_t blkno, file_size;
 	char *buf = NULL;
-	struct ocfs2_dinode* di = NULL;
-
-	if (!(fs->fs_flags & OCFS2_FLAG_RW))
-		return OCFS2_ET_RO_FILESYS;
-
-	ret = ocfs2_malloc_block(fs->fs_io, &buf);
-	if (ret)
-		goto out_free_buf;
-
-	ret = ocfs2_read_inode(fs, ino, buf);
-	if (ret)
-		goto out_free_buf;
-
-	di = (struct ocfs2_dinode *)buf;
+	ocfs2_filesys *fs = ci->ci_fs;
 
-	file_size = di->i_size;
+	file_size = ci->ci_inode->i_size;
 	cpos = (file_size + fs->fs_clustersize - 1) / fs->fs_clustersize;
 	while (new_clusters) {
 		n_clusters = 1;
@@ -3795,23 +3782,41 @@ errcode_t ocfs2_extend_allocation(ocfs2_filesys *fs, uint64_t ino,
 		if (ret)
 			break;
 
-		ret = ocfs2_insert_extent(fs, ino, cpos, blkno, n_clusters,
-					  0);
+		ret = ocfs2_cached_inode_insert_extent(ci, cpos, blkno,
+						       n_clusters, 0);
 		if (ret) {
 			/* XXX: We don't wan't to overwrite the error
 			 * from insert_extent().  But we probably need
 			 * to BE LOUDLY UPSET. */
 			ocfs2_free_clusters(fs, n_clusters, blkno);
-			goto out_free_buf;
+			break;
 		}
 
 	 	new_clusters -= n_clusters;
 		cpos += n_clusters;
 	}
+	return ret;
+}
+
+errcode_t ocfs2_extend_allocation(ocfs2_filesys *fs, uint64_t ino,
+				  uint32_t new_clusters)
+{
+	errcode_t ret;
+	ocfs2_cached_inode *ci = NULL;
+
+	ret = ocfs2_read_cached_inode(fs, ino, &ci);
+	if (ret)
+		goto bail;
+
+	ret = ocfs2_cached_inode_extend_allocation(ci, new_clusters);
+	if (ret)
+		goto bail;
+
+	ret = ocfs2_write_cached_inode(fs, ci);
+bail:
+	if (ci)
+		ocfs2_free_cached_inode(fs, ci);
 
-out_free_buf:
-	if (buf)
-		ocfs2_free(&buf);
 	return ret;
 }
 
-- 
1.6.0.2

^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [Ocfs2-devel] [PATCH 3/8] Implement quota functions to libocfs2
  2009-07-27 17:53 [Ocfs2-devel] [PATCH 0/8] Quota support for ocfs2-tools Jan Kara
  2009-07-27 17:53 ` [Ocfs2-devel] [PATCH 1/8] Update ocfs2_fs.h to contain all necessary quota structures and constants Jan Kara
  2009-07-27 17:53 ` [Ocfs2-devel] [PATCH 2/8] Provide ocfs2_cached_inode_extend_allocation() Jan Kara
@ 2009-07-27 17:53 ` Jan Kara
  2009-07-28  7:55   ` Tao Ma
  2009-07-27 17:53 ` [Ocfs2-devel] [PATCH 4/8] Write out quota info changes on ocfs2_close() Jan Kara
                   ` (4 subsequent siblings)
  7 siblings, 1 reply; 32+ messages in thread
From: Jan Kara @ 2009-07-27 17:53 UTC (permalink / raw)
  To: ocfs2-devel

Signed-off-by: Jan Kara <jack@suse.cz>
---
 include/ocfs2/ocfs2.h     |   80 +++
 libocfs2/Makefile         |    1 +
 libocfs2/feature_string.c |   18 +
 libocfs2/ocfs2_err.et     |    6 +
 libocfs2/quota.c          | 1208 +++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 1313 insertions(+), 0 deletions(-)
 create mode 100644 libocfs2/quota.c

diff --git a/include/ocfs2/ocfs2.h b/include/ocfs2/ocfs2.h
index ac16823..7f136ca 100644
--- a/include/ocfs2/ocfs2.h
+++ b/include/ocfs2/ocfs2.h
@@ -125,16 +125,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 +181,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 +194,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 +236,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);
@@ -580,6 +619,47 @@ 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 */
+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 ocfs2_global_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..f0ad301
--- /dev/null
+++ b/libocfs2/quota.c
@@ -0,0 +1,1208 @@
+/* -*- 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 ocfs2_global_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
+#define MAX_QUOTA_HASH_SIZE (1<<21)	/* 16 MB on 64-bit arch*/
+
+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);
+}
+
+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;
+	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, (2 * fs->fs_blocksize +
+			fs->fs_clustersize - 1) / fs->fs_clustersize);
+	if (err)
+		goto out;
+	di->i_size = 2 * fs->fs_blocksize;
+	di->i_mtime = time(NULL);
+	err = ocfs2_write_inode(fs, blkno, (char *)di);
+	if (err)
+		goto out;
+
+	err = ocfs2_malloc_blocks(fs->fs_io, fs->fs_blocksize * 2, &buf);
+	if (err)
+		goto out;
+	memset(buf, 0, 2 * fs->fs_blocksize);
+
+	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 = 2;
+	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, 2 * fs->fs_blocksize, 0, &written);
+	if (!err && written != 2 * fs->fs_blocksize) {
+		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, uint 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, uint 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_blocks(fs->fs_io, fs->fs_blocksize, &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_blocks(fs->fs_io, fs->fs_blocksize, &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;
+}
+
+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, blocks = 2;
+
+	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,
+			(blocks * fs->fs_blocksize + fs->fs_clustersize - 1) /
+			fs->fs_clustersize);
+	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 = blocks * fs->fs_blocksize;
+	di->i_mtime = time(NULL);
+
+	err = ocfs2_malloc_blocks(fs->fs_io, fs->fs_blocksize * blocks, &buf);
+	if (err)
+		goto out;
+	memset(buf, 0, fs->fs_blocksize * blocks);
+
+	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 = 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 = 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 < 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, uint *blk)
+{
+	errcode_t err;
+	char *buf;
+	struct ocfs2_global_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 ocfs2_global_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 = info->dqi_blocks * fs->fs_blocksize;
+	}
+	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, uint blk)
+{
+	errcode_t err;
+	struct ocfs2_global_disk_dqdbheader *dh =
+				(struct ocfs2_global_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, uint blk)
+{
+	errcode_t err;
+	char *tmpbuf;
+	struct ocfs2_global_disk_dqdbheader *dh, *tdh;
+	uint nextblk, prevblk;
+
+	err = ocfs2_malloc_block(fs->fs_io, &tmpbuf);
+	if (err)
+		return err;
+	dh = (struct ocfs2_global_disk_dqdbheader *)buf;
+	tdh = (struct ocfs2_global_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, uint blk)
+{
+	errcode_t err;
+	char *tmpbuf;
+	struct ocfs2_global_disk_dqdbheader *tdh, *dh =
+				(struct ocfs2_global_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 ocfs2_global_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,
+					 uint *treeblk, loff_t *off)
+{
+	errcode_t err;
+	unsigned int blk, i;
+	struct ocfs2_global_disk_dqblk *ddquot;
+	struct ocfs2_global_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 ocfs2_global_disk_dqdbheader *)buf;
+	ddquot = (struct ocfs2_global_disk_dqblk *)(buf +
+		 sizeof(struct ocfs2_global_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 ocfs2_global_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,
+				      uint *treeblk, int depth, loff_t *off)
+{
+	char *buf;
+	int newson = 0, newact = 0;
+	u_int32_t *ref;
+	uint 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)
+{
+	uint tmp = OCFS2_GLOBAL_TREE_BLK;
+
+	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,
+					   uint blk)
+{
+	errcode_t err;
+	char *buf;
+	struct ocfs2_global_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 ocfs2_global_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,
+					   uint *blk,
+					   int depth)
+{
+	errcode_t err;
+	char *buf;
+	uint 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 != OCFS2_GLOBAL_TREE_BLK) {
+			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)
+{
+	uint tmp = OCFS2_GLOBAL_TREE_BLK;
+
+	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, uint 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 ocfs2_global_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,
+					 uint 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)
+{
+	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,
+				      OCFS2_GLOBAL_TREE_BLK, 0);
+	if (err)
+		goto bail;
+	*ret_dquot = dquot;
+	return 0;
+bail:
+	ocfs2_free(&dquot);
+	return err;
+}
-- 
1.6.0.2

^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [Ocfs2-devel] [PATCH 4/8] Write out quota info changes on ocfs2_close()
  2009-07-27 17:53 [Ocfs2-devel] [PATCH 0/8] Quota support for ocfs2-tools Jan Kara
                   ` (2 preceding siblings ...)
  2009-07-27 17:53 ` [Ocfs2-devel] [PATCH 3/8] Implement quota functions to libocfs2 Jan Kara
@ 2009-07-27 17:53 ` Jan Kara
  2009-07-28 22:53   ` Joel Becker
  2009-07-27 17:53 ` [Ocfs2-devel] [PATCH 5/8] Quota support for fsck.ocfs2 Jan Kara
                   ` (3 subsequent siblings)
  7 siblings, 1 reply; 32+ messages in thread
From: Jan Kara @ 2009-07-27 17:53 UTC (permalink / raw)
  To: ocfs2-devel

We don't write out change of information in quota file header on each change,
we rather cache it in ocfs2_filesys structure. So write out all the
information when ocfs2_close() is called.

Signed-off-by: Jan Kara <jack@suse.cz>
---
 libocfs2/closefs.c |   12 ++++++++++++
 1 files changed, 12 insertions(+), 0 deletions(-)

diff --git a/libocfs2/closefs.c b/libocfs2/closefs.c
index 54411da..290bfd1 100644
--- a/libocfs2/closefs.c
+++ b/libocfs2/closefs.c
@@ -39,6 +39,7 @@ errcode_t ocfs2_flush(ocfs2_filesys *fs)
 errcode_t ocfs2_close(ocfs2_filesys *fs)
 {
 	errcode_t ret;
+	int type;
 
 	if (fs->fs_flags & OCFS2_FLAG_DIRTY) {
 		ret = ocfs2_flush(fs);
@@ -46,6 +47,17 @@ errcode_t ocfs2_close(ocfs2_filesys *fs)
 			return ret;
 	}
 
+	for (type = 0; type < MAXQUOTAS; type++)
+		if (fs->qinfo[type].flags & OCFS2_QF_INFO_DIRTY) {
+			ret = ocfs2_write_global_quota_info(fs, type);
+			if (ret)
+				return ret;
+			ret = ocfs2_write_cached_inode(fs,
+						fs->qinfo[type].qi_inode);
+			if (ret)
+				return ret;
+		}
+
 	ocfs2_freefs(fs);
 	return 0;
 }
-- 
1.6.0.2

^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [Ocfs2-devel] [PATCH 5/8] Quota support for fsck.ocfs2
  2009-07-27 17:53 [Ocfs2-devel] [PATCH 0/8] Quota support for ocfs2-tools Jan Kara
                   ` (3 preceding siblings ...)
  2009-07-27 17:53 ` [Ocfs2-devel] [PATCH 4/8] Write out quota info changes on ocfs2_close() Jan Kara
@ 2009-07-27 17:53 ` Jan Kara
  2009-07-28 23:05   ` Joel Becker
  2009-07-27 17:53 ` [Ocfs2-devel] [PATCH 6/8] Quota support for mkfs.ocfs2 Jan Kara
                   ` (2 subsequent siblings)
  7 siblings, 1 reply; 32+ messages in thread
From: Jan Kara @ 2009-07-27 17:53 UTC (permalink / raw)
  To: ocfs2-devel

After the filesystem is fixed, scan quota files and try to gather as much
information about set limits as possible. Then scan the filesystem to
compute correct quota usage and dump all the gathered information into
freshly created quota files.

Signed-off-by: Jan Kara <jack@suse.cz>
---
 fsck.ocfs2/Makefile               |    2 +
 fsck.ocfs2/fsck.c                 |    9 +
 fsck.ocfs2/fsck.ocfs2.checks.8.in |   30 ++
 fsck.ocfs2/include/pass5.h        |   32 +++
 fsck.ocfs2/pass5.c                |  534 +++++++++++++++++++++++++++++++++++++
 5 files changed, 607 insertions(+), 0 deletions(-)
 create mode 100644 fsck.ocfs2/include/pass5.h
 create mode 100644 fsck.ocfs2/pass5.c

diff --git a/fsck.ocfs2/Makefile b/fsck.ocfs2/Makefile
index 7f6e547..850e106 100644
--- a/fsck.ocfs2/Makefile
+++ b/fsck.ocfs2/Makefile
@@ -28,6 +28,7 @@ CFILES =	fsck.c		\
 		pass2.c 	\
 		pass3.c 	\
 		pass4.c 	\
+		pass5.c		\
 		problem.c 	\
 		slot_recovery.c \
 		strings.c 	\
@@ -46,6 +47,7 @@ HFILES = 	include/fsck.h		\
 		include/pass2.h		\
 		include/pass3.h		\
 		include/pass4.h		\
+		include/pass5.h		\
 		include/problem.h	\
 		include/slot_recovery.h	\
 		include/strings.h	\
diff --git a/fsck.ocfs2/fsck.c b/fsck.ocfs2/fsck.c
index a686886..fe15783 100644
--- a/fsck.ocfs2/fsck.c
+++ b/fsck.ocfs2/fsck.c
@@ -30,6 +30,8 @@
  * pass2.c: verify directory entries, record some linkage metadata
  * pass3.c: make sure all dirs are reachable
  * pass4.c: resolve inode's link counts, move disconnected inodes to lost+found
+ * pass5.c: load global quota file, merge node-local quota files to global
+ *          quota file, recompute quota usage and recreate quota files
  *
  * When hacking on this keep the following in mind:
  *
@@ -64,6 +66,7 @@
 #include "pass2.h"
 #include "pass3.h"
 #include "pass4.h"
+#include "pass5.h"
 #include "problem.h"
 #include "util.h"
 #include "slot_recovery.h"
@@ -917,6 +920,12 @@ int main(int argc, char **argv)
 		goto done;
 	}
 
+	ret = o2fsck_pass5(ost);
+	if (ret) {
+		com_err(whoami, ret, "while performing pass 5");
+		goto done;
+	}
+
 done:
 	if (ret)
 		fsck_mask |= FSCK_ERROR;
diff --git a/fsck.ocfs2/fsck.ocfs2.checks.8.in b/fsck.ocfs2/fsck.ocfs2.checks.8.in
index 739b867..f70f48d 100644
--- a/fsck.ocfs2/fsck.ocfs2.checks.8.in
+++ b/fsck.ocfs2/fsck.ocfs2.checks.8.in
@@ -851,6 +851,36 @@ does not match the number of buckets found by fsck.
 
 Answering yes will change this to the correct count.
 
+\" pass5.c
+
+.SS "QMAGIC_INVALID"
+The magic number in the header of quota file does not match the proper
+number.
+
+Answering yes will make fsck use values in the quota file header anyway.
+
+.SS "QTREE_BLK_INVALID"
+Block with references to other blocks with quota data is corrupted.
+
+Answering yes will make fsck use references in the block.
+
+.SS "DQBLK_INVALID"
+The structure with quota limits was found in a corrupted block.
+
+Answering yes will use the values of limits for the user / group.
+
+.SS "DUP_DQBLK_INVALID"
+The structure with quota limits was found in a corrupted block
+and fsck has already found quota limits for this user / group.
+
+Answering yes will use new values of limits for the user / group.
+
+.SS "DUP_DQBLK_VALID"
+The structure with quota limits was found in a correct block
+but fsck has already found quota limits for this user / group.
+
+Answering yes will use new values of limits for the user / group.
+
 .SH "SEE ALSO"
 .BR fsck.ocfs2(8)
 
diff --git a/fsck.ocfs2/include/pass5.h b/fsck.ocfs2/include/pass5.h
new file mode 100644
index 0000000..dfd27cd
--- /dev/null
+++ b/fsck.ocfs2/include/pass5.h
@@ -0,0 +1,32 @@
+/*
+ * pass5.h
+ *
+ * Copyright (C) 2009 Novell Corporation.  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 as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * 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.
+ *
+ * Author: Jan Kara
+ */
+
+#ifndef __O2FSCK_PASS5_H__
+#define __O2FSCK_PASS5_H__
+
+#include "fsck.h"
+
+errcode_t o2fsck_pass5(o2fsck_state *ost);
+
+#endif /* __O2FSCK_PASS4_H__ */
+
diff --git a/fsck.ocfs2/pass5.c b/fsck.ocfs2/pass5.c
new file mode 100644
index 0000000..b64898a
--- /dev/null
+++ b/fsck.ocfs2/pass5.c
@@ -0,0 +1,534 @@
+/* -*- mode: c; c-basic-offset: 8; -*-
+ * vim: noexpandtab sw=8 ts=8 sts=0:
+ *
+ * Copyright (C) 2009 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.
+ *
+ * --
+ * Pass 5 tries to read as much data as possible from the global quota file.
+ * (we are interested mainly in limits for users and groups). After that we
+ * scan the filesystem and recompute quota usage for each user / group and
+ * finally we dump all the information into freshly created quota files.
+ *
+ * At this pass, filesystem should be already sound, so we use libocfs2
+ * functions for low-level operations.
+ *
+ * FIXME: We could also check node-local quota files and use limits there.
+ *        For now we just discard them.
+ */
+#include <string.h>
+#include <inttypes.h>
+#include <time.h>
+
+#include "ocfs2/ocfs2.h"
+#include "ocfs2/bitops.h"
+#include "ocfs2/byteorder.h"
+
+#include "fsck.h"
+#include "pass5.h"
+#include "problem.h"
+#include "strings.h"
+#include "util.h"
+
+static const char *whoami = "pass5";
+static char *qbmp[MAXQUOTAS];
+static ocfs2_quota_hash *qhash[MAXQUOTAS];
+
+static char *type2name(int type)
+{
+	if (type == USRQUOTA)
+		return "user";
+	return "group";
+}
+
+static errcode_t o2fsck_release_dquot(ocfs2_cached_dquot *dquot, void *p)
+{
+	ocfs2_quota_hash *hash = p;
+
+	ocfs2_remove_quota_hash(hash, dquot);
+	ocfs2_free(&dquot);
+	return 0;
+}
+
+static int check_blkref(uint32_t block, uint32_t maxblocks)
+{
+	if (block < OCFS2_GLOBAL_TREE_BLK || block >= maxblocks)
+		return 0;
+	return 1;
+}
+
+static errcode_t o2fsck_validate_blk(ocfs2_filesys *fs, char *buf)
+{
+	struct ocfs2_disk_dqtrailer *dqt =
+				ocfs2_block_dqtrailer(fs->fs_blocksize, buf);
+	return ocfs2_validate_meta_ecc(fs, buf, &dqt->dq_check);
+}
+
+static int o2fsck_valid_quota_info(ocfs2_filesys *fs, int type,
+				   struct ocfs2_disk_dqheader *header,
+				   struct ocfs2_global_disk_dqinfo *info)
+{
+	uint32_t magics[MAXQUOTAS] = OCFS2_GLOBAL_QMAGICS;
+	int versions[MAXQUOTAS] = OCFS2_GLOBAL_QVERSIONS;
+
+	if (header->dqh_magic != magics[type] ||
+	    header->dqh_version > versions[type])
+		return 0;
+	if (info->dqi_blocks !=
+	    fs->qinfo[type].qi_inode->ci_inode->i_size / fs->fs_blocksize)
+		return 0;
+	if ((info->dqi_free_blk &&
+	     !check_blkref(info->dqi_free_blk, info->dqi_blocks)) ||
+	    (info->dqi_free_entry &&
+	     !check_blkref(info->dqi_free_entry, info->dqi_blocks)))
+		return 0;
+	return 1;
+}
+
+static errcode_t o2fsck_read_blk(ocfs2_filesys *fs, int type, char *buf,
+				 uint32_t blk)
+{
+	uint32_t got;
+	errcode_t ret;
+
+	ret = ocfs2_file_read(fs->qinfo[type].qi_inode, buf, fs->fs_blocksize,
+			      blk * fs->fs_blocksize, &got);
+	if (ret)
+		return ret;
+	if (got != fs->fs_blocksize)
+		return OCFS2_ET_SHORT_READ;
+	return 0;
+}
+
+static errcode_t o2fsck_check_info(o2fsck_state *ost, int type)
+{
+	errcode_t ret;
+	ocfs2_filesys *fs = ost->ost_fs;
+	char *buf;
+	struct ocfs2_disk_dqheader *header;
+	struct ocfs2_global_disk_dqinfo *info;
+	uint64_t blocks;
+	int checksum_valid;
+
+	ret = ocfs2_malloc_blocks(fs->fs_io, fs->fs_blocksize, &buf);
+	if (ret) {
+		com_err(whoami, ret, "while allocating block buffer");
+		goto set_default;
+	}
+	ret = o2fsck_read_blk(fs, type, buf, 0);
+	if (ret) {
+		com_err(whoami, ret, "while reading global %s quota info "
+			"block", type2name(type));
+		goto set_default;
+	}
+	checksum_valid = !o2fsck_validate_blk(fs, buf);
+	header = (struct ocfs2_disk_dqheader *)buf;
+	info = (struct ocfs2_global_disk_dqinfo *)(buf + OCFS2_GLOBAL_INFO_OFF);
+	ocfs2_swap_quota_header(header);
+	ocfs2_swap_quota_global_info(info);
+	if ((!checksum_valid ||
+	     !o2fsck_valid_quota_info(fs, type, header, info)) &&
+	    !prompt(ost, PN, PR_QMAGIC_INVALID, "%s quota info looks corrupt."
+		    " Use its content:\nBlock grace time: %"PRIu32" sec\n"
+		    "Inode grace time: %"PRIu32" sec\n"
+		    "Cluster quota sync time: %"PRIu32" ms\n",
+		    type2name(type), info->dqi_bgrace, info->dqi_igrace,
+		    info->dqi_syncms)) {
+		goto set_default;
+	}
+	fs->qinfo[type].qi_info.dqi_bgrace = info->dqi_bgrace;
+	fs->qinfo[type].qi_info.dqi_igrace = info->dqi_igrace;
+	fs->qinfo[type].qi_info.dqi_syncms = info->dqi_syncms;
+	goto set_blocks;
+set_default:
+	fs->qinfo[type].qi_info.dqi_bgrace = OCFS2_DEF_BLOCK_GRACE;
+	fs->qinfo[type].qi_info.dqi_igrace = OCFS2_DEF_INODE_GRACE;
+	fs->qinfo[type].qi_info.dqi_syncms = OCFS2_DEF_QUOTA_SYNC;
+set_blocks:
+	blocks = fs->qinfo[type].qi_inode->ci_inode->i_size / fs->fs_blocksize;
+	if (blocks > (1ULL << 32) - 1)
+		fs->qinfo[type].qi_info.dqi_blocks = (1ULL << 32) - 1;
+	else
+		fs->qinfo[type].qi_info.dqi_blocks = blocks;
+	return ret;
+}
+
+/* Check whether a reference to a tree block is sane */
+static int o2fsck_check_tree_ref(o2fsck_state *ost, int type, uint32_t blk,
+				 int depth)
+{
+	ocfs2_filesys *fs = ost->ost_fs;
+	uint32_t blocks = fs->qinfo[type].qi_info.dqi_blocks;
+
+	/* Bogus block number? */
+	if (!check_blkref(blk, blocks)) {
+		verbosef("ignoring invalid %s quota block reference %"PRIu32,
+			 type2name(type), blk);
+		return 0;
+	}
+	/* Already scanned block? */
+	if (depth < ocfs2_qtree_depth(fs->fs_blocksize) &&
+	    ocfs2_test_bit(blk, qbmp[type])) {
+		verbosef("ignoring duplicate %s quota block reference %"PRIu32,
+			 type2name(type), blk);
+		return 0;
+	}
+	return 1;
+}
+
+/* Read the block, check dquot structures in it */
+static errcode_t o2fsck_check_data_blk(o2fsck_state *ost, int type,
+				       uint32_t blk, char *buf)
+{
+	ocfs2_filesys *fs = ost->ost_fs;
+	errcode_t ret;
+	struct ocfs2_global_disk_dqdbheader *dh =
+			(struct ocfs2_global_disk_dqdbheader *)buf;
+	int str_in_blk = ocfs2_global_dqstr_in_blk(fs->fs_blocksize);
+	int i;
+	struct ocfs2_global_disk_dqblk *ddquot;
+	ocfs2_cached_dquot *dquot;
+	uint32_t blocks = fs->qinfo[type].qi_info.dqi_blocks;
+	int valid;
+
+	ocfs2_set_bit(blk, qbmp[type]);
+	ret = o2fsck_read_blk(fs, type, buf, blk);
+	if (ret) {
+		com_err(whoami, ret,
+			"while reading %s quota file block %"PRIu32,
+			type2name(type), blk);
+		return ret;
+	}
+	ret = o2fsck_validate_blk(fs, buf);
+	if (ret) {
+		verbosef("%s: invalid checksum in %s quota leaf block (block %"
+			 PRIu32")", error_message(ret), type2name(type), blk);
+		valid = 0;
+	}
+	ocfs2_swap_quota_leaf_block_header(dh);
+	if ((dh->dqdh_next_free && !check_blkref(dh->dqdh_next_free, blocks)) ||
+	    (dh->dqdh_prev_free && !check_blkref(dh->dqdh_prev_free, blocks)) ||
+	    dh->dqdh_entries > str_in_blk) {
+		verbosef("corrupt %s quota leaf block header (block %"PRIu32")",
+			 type2name(type), blk);
+		valid = 0;
+	}
+	ddquot = (struct ocfs2_global_disk_dqblk *)(buf +
+			sizeof(struct ocfs2_global_disk_dqdbheader));
+	for (i = 0; i < str_in_blk; i++, ddquot++) {
+		if (ocfs2_qtree_entry_unused(ddquot))
+			continue;
+		ocfs2_swap_quota_global_dqblk(ddquot);
+		ret = ocfs2_find_quota_hash(qhash[type], ddquot->dqb_id,
+					    &dquot);
+		if (ret) {
+			com_err(whoami, ret,
+				"while searching in %s quota hash",
+				type2name(type));
+			return ret;
+		}
+		if (dquot && valid) {
+			if (!prompt(ost, PY, PR_DUP_DQBLK_VALID,
+				    "Duplicate %s quota structure for id %"
+				    PRIu32":\nCurrent quota limits: Inode: %"
+				    PRIu64" %"PRIu64" Space: %"PRIu64" %"
+				    PRIu64"\nFound quota limits: Inode: %"
+				    PRIu64" %"PRIu64" Space: %"PRIu64" %"
+				    PRIu64"\nUse found limits?",
+				    type2name(type), ddquot->dqb_id,
+				    dquot->d_ddquot.dqb_isoftlimit,
+				    dquot->d_ddquot.dqb_ihardlimit,
+				    dquot->d_ddquot.dqb_bsoftlimit,
+				    dquot->d_ddquot.dqb_bhardlimit,
+				    ddquot->dqb_isoftlimit,
+				    ddquot->dqb_ihardlimit,
+				    ddquot->dqb_bsoftlimit,
+				    ddquot->dqb_bhardlimit))
+				continue;
+		} else if (dquot && !valid) {
+			if (!prompt(ost, PN, PR_DUP_DQBLK_INVALID,
+				    "Found %s quota structure for id %"PRIu32
+				    " in a corrupted block and already have "
+				    "values for this id:\nCurrent quota "
+				    "limits: Inode: %"PRIu64" %"PRIu64" Space:"
+				    " %"PRIu64" %"PRIu64"\nFound quota limits:"
+				    " Inode: %"PRIu64" %"PRIu64" Space: %"
+				    PRIu64" %"PRIu64"\nUse found limits?",
+				    type2name(type), ddquot->dqb_id,
+				    dquot->d_ddquot.dqb_isoftlimit,
+				    dquot->d_ddquot.dqb_ihardlimit,
+				    dquot->d_ddquot.dqb_bsoftlimit,
+				    dquot->d_ddquot.dqb_bhardlimit,
+				    ddquot->dqb_isoftlimit,
+				    ddquot->dqb_ihardlimit,
+				    ddquot->dqb_bsoftlimit,
+				    ddquot->dqb_bhardlimit))
+				continue;
+		} else if (!dquot && !valid) {
+			if (!prompt(ost, PN, PR_DQBLK_INVALID,
+				    "Found corrupted %s quota structure for id"
+				    " %"PRIu32":\nFound quota limits: Inode: %"
+				    PRIu64" %"PRIu64" Space: %"PRIu64" %"
+				    PRIu64"\nUse found limits?",
+				    type2name(type), ddquot->dqb_id,
+				    ddquot->dqb_isoftlimit,
+				    ddquot->dqb_ihardlimit,
+				    ddquot->dqb_bsoftlimit,
+				    ddquot->dqb_bhardlimit))
+				continue;
+		}
+
+		if (!dquot) {
+			ret = ocfs2_find_create_quota_hash(qhash[type],
+					ddquot->dqb_id, &dquot);
+			if (ret) {
+				com_err(whoami, ret, "while inserting quota"
+					" structure into hash");
+				return ret;
+			}
+		}
+		memcpy(&dquot->d_ddquot, ddquot,
+				sizeof(struct ocfs2_global_disk_dqblk));
+		dquot->d_ddquot.dqb_use_count = 0;
+		dquot->d_ddquot.dqb_curinodes = 0;
+		dquot->d_ddquot.dqb_curspace = 0;
+	}
+	return 0;
+}
+
+/* Read the block, check references in it */
+static errcode_t o2fsck_check_tree_blk(o2fsck_state *ost, int type,
+				       uint32_t blk, int depth,
+				       char *buf)
+{
+	ocfs2_filesys *fs = ost->ost_fs;
+	errcode_t ret;
+	int epb = (fs->fs_blocksize - OCFS2_QBLK_RESERVED_SPACE) >> 2;
+	int tree_depth = ocfs2_qtree_depth(fs->fs_blocksize);
+	int i;
+	uint32_t *refs = (uint32_t *)buf, actref;
+
+	ocfs2_set_bit(blk, qbmp[type]);
+	ret = o2fsck_read_blk(fs, type, buf, blk);
+	if (ret) {
+		com_err(whoami, ret,
+			"while reading %s quota file block %"PRIu32,
+			type2name(type), blk);
+		goto out;
+	}
+	ret = o2fsck_validate_blk(fs, buf);
+	if (ret &&
+	    !prompt(ost, PN, PR_QTREE_BLK_INVALID, "Corrupted %s quota tree "
+		    "block %"PRIu32" (checksum error: %s). Scan referenced "
+		    "blocks anyway?", type2name(type), blk,
+		    error_message(ret))) {
+		goto out;
+	}
+	for (i = 0; i < epb; i++) {
+		actref = le32_to_cpu(refs[i]);
+		if (!actref)
+			continue;
+		/* Valid block reference? */
+		if (o2fsck_check_tree_ref(ost, type, actref, depth + 1)) {
+			if (depth + 1 < tree_depth) {
+				ret = o2fsck_check_tree_blk(ost, type, actref,
+					depth + 1, buf + fs->fs_blocksize);
+			} else if (!ocfs2_test_bit(actref, qbmp[type])) {
+				ret = o2fsck_check_data_blk(ost, type, actref,
+					buf + fs->fs_blocksize);
+			}
+			if (ret)
+				goto out;
+		}
+	}
+out:
+	return ret;
+}
+
+static errcode_t load_quota_file(o2fsck_state *ost, int type)
+{
+	ocfs2_filesys *fs = ost->ost_fs;
+	char *buf = NULL;
+	errcode_t ret;
+
+	ret = ocfs2_init_fs_quota_info(fs, type);
+	if (ret) {
+		com_err(whoami, ret, "while looking up global %s quota file",
+			type2name(type));
+		goto out;
+	}
+	ret = o2fsck_check_info(ost, type);
+	/* Some fatal error happened? */
+	if (ret)
+		goto out;
+
+	ret = ocfs2_malloc0((fs->qinfo[type].qi_info.dqi_blocks + 7) / 8,
+			    qbmp + type);
+	if (ret) {
+		com_err(whoami, ret, "while allocating %s quota file block "
+			"bitmap", type2name(type));
+		goto out;
+	}
+	ret = ocfs2_malloc_blocks(fs->fs_io,
+		fs->fs_blocksize * (ocfs2_qtree_depth(fs->fs_blocksize) + 1),
+		&buf);
+	if (ret) {
+		com_err(whoami, ret,
+			"while allocating buffer for quota blocks");
+		goto out;
+	}
+
+	if (!o2fsck_check_tree_ref(ost, type, OCFS2_GLOBAL_TREE_BLK, 0))
+		goto out;
+	ret = o2fsck_check_tree_blk(ost, type, OCFS2_GLOBAL_TREE_BLK, 0, buf);
+out:
+	if (qbmp[type])
+		ocfs2_free(qbmp + type);
+	if (buf)
+		ocfs2_free(&buf);
+	return ret;
+}
+
+/* You have to write the inode yourself after calling this function! */
+static errcode_t truncate_cached_inode(ocfs2_filesys *fs,
+				       ocfs2_cached_inode *ci)
+{
+	uint32_t new_clusters;
+	errcode_t ret;
+
+	ret = ocfs2_zero_tail_and_truncate(fs, ci, 0, &new_clusters);
+	if (ret)
+		return ret;
+	ci->ci_inode->i_clusters = new_clusters;
+	if (new_clusters == 0)
+		ci->ci_inode->id2.i_list.l_tree_depth = 0;
+	ci->ci_inode->i_size = 0;
+
+	return 0;
+}
+
+static errcode_t recreate_quota_files(ocfs2_filesys *fs, int type)
+{
+	ocfs2_cached_inode *ci = fs->qinfo[type].qi_inode;
+	errcode_t ret;
+
+	ret = truncate_cached_inode(fs, ci);
+	if (ret) {
+		com_err(whoami, ret, "while truncating global %s quota file",
+			type2name(type));
+		return ret;
+	}
+
+	ret = ocfs2_init_global_quota_file(fs, type);
+	if (ret) {
+		com_err(whoami, ret,
+			"while reinitializing global %s quota file",
+			type2name(type));
+		return ret;
+	}
+	ret = ocfs2_write_release_dquots(fs, type, qhash[type]);
+	if (ret) {
+		com_err(whoami, ret, "while writing %s quota usage",
+			type2name(type));
+		return ret;
+	}
+
+	ret = ocfs2_init_local_quota_files(fs, type);
+	if (ret) {
+		com_err(whoami, ret,
+			"while initializing local quota files");
+		return ret;
+	}
+	return 0;
+}
+
+errcode_t o2fsck_pass5(o2fsck_state *ost)
+{
+	errcode_t ret;
+	ocfs2_filesys *fs = ost->ost_fs;
+	struct ocfs2_super_block *super = OCFS2_RAW_SB(fs->fs_super);
+	int has_usrquota, has_grpquota;
+
+	has_usrquota = OCFS2_HAS_RO_COMPAT_FEATURE(super,
+				OCFS2_FEATURE_RO_COMPAT_USRQUOTA);
+	has_grpquota = OCFS2_HAS_RO_COMPAT_FEATURE(super,
+				OCFS2_FEATURE_RO_COMPAT_GRPQUOTA);
+	/* Nothing to check? */
+	if (!has_usrquota && !has_grpquota)
+		return 0;
+	printf("Pass 5: Checking quota information.\n");
+	if (has_usrquota) {
+		ret = ocfs2_new_quota_hash(qhash + USRQUOTA);
+		if (ret) {
+			com_err(whoami, ret,
+				"while allocating user quota hash");
+			goto out;
+		}
+		ret = load_quota_file(ost, USRQUOTA);
+		if (ret)
+			goto out;
+	}
+	if (has_grpquota) {
+		ret = ocfs2_new_quota_hash(qhash + GRPQUOTA);
+		if (ret) {
+			com_err(whoami, ret,
+				"while allocating group quota hash");
+			goto out;
+		}
+		ret = load_quota_file(ost, GRPQUOTA);
+		if (ret)
+			goto out;
+	}
+	ret = ocfs2_compute_quota_usage(fs, qhash[USRQUOTA], qhash[GRPQUOTA]);
+	if (ret) {
+		com_err(whoami, ret, "while computing quota usage");
+		goto out;
+	}
+	if (has_usrquota) {
+		ret = recreate_quota_files(fs, USRQUOTA);
+		if (ret)
+			goto out;
+		ret = ocfs2_free_quota_hash(qhash[USRQUOTA]);
+		if (ret) {
+			com_err(whoami, ret, "while release user quota hash");
+			goto out;
+		}
+	}
+	if (has_grpquota) {
+		ret = recreate_quota_files(fs, GRPQUOTA);
+		if (ret)
+			goto out;
+		ret = ocfs2_free_quota_hash(qhash[GRPQUOTA]);
+		if (ret) {
+			com_err(whoami, ret, "while release group quota hash");
+			goto out;
+		}
+	}
+
+	return 0;
+out:
+	if (qhash[USRQUOTA]) {
+		ocfs2_iterate_quota_hash(qhash[USRQUOTA], o2fsck_release_dquot,
+					 qhash[USRQUOTA]);
+		ocfs2_free_quota_hash(qhash[USRQUOTA]);
+	}
+	if (qhash[GRPQUOTA]) {
+		ocfs2_iterate_quota_hash(qhash[GRPQUOTA], o2fsck_release_dquot,
+					 qhash[GRPQUOTA]);
+		ocfs2_free_quota_hash(qhash[GRPQUOTA]);
+	}
+	return ret;
+}
-- 
1.6.0.2

^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [Ocfs2-devel] [PATCH 6/8] Quota support for mkfs.ocfs2
  2009-07-27 17:53 [Ocfs2-devel] [PATCH 0/8] Quota support for ocfs2-tools Jan Kara
                   ` (4 preceding siblings ...)
  2009-07-27 17:53 ` [Ocfs2-devel] [PATCH 5/8] Quota support for fsck.ocfs2 Jan Kara
@ 2009-07-27 17:53 ` Jan Kara
  2009-07-28  8:11   ` Tao Ma
  2009-07-27 17:53 ` [Ocfs2-devel] [PATCH 7/8] Add quota support to tunefs.ocfs2 Jan Kara
  2009-07-27 17:53 ` [Ocfs2-devel] [PATCH 8/8] Change headers to reflect that quota is now fully supported Jan Kara
  7 siblings, 1 reply; 32+ messages in thread
From: Jan Kara @ 2009-07-27 17:53 UTC (permalink / raw)
  To: ocfs2-devel

Signed-off-by: Jan Kara <jack@suse.cz>
---
 mkfs.ocfs2/mkfs.c          |  146 +++++++++++++++++++++++++++++++++++++++++++-
 mkfs.ocfs2/mkfs.h          |    1 +
 mkfs.ocfs2/mkfs.ocfs2.8.in |   16 +++++
 3 files changed, 162 insertions(+), 1 deletions(-)

diff --git a/mkfs.ocfs2/mkfs.c b/mkfs.ocfs2/mkfs.c
index 6acc6e6..8496d03 100644
--- a/mkfs.ocfs2/mkfs.c
+++ b/mkfs.ocfs2/mkfs.c
@@ -98,12 +98,16 @@ static SystemFileInfo system_files[] = {
 	{ "slot_map", SFI_OTHER, 1, S_IFREG | 0644 },
 	{ "heartbeat", SFI_HEARTBEAT, 1, S_IFREG | 0644 },
 	{ "global_bitmap", SFI_CLUSTER, 1, S_IFREG | 0644 },
+	{ "aquota.user", SFI_QUOTA, 1, S_IFREG | 0644 },
+	{ "aquota.group", SFI_QUOTA, 1, S_IFREG | 0644 },
 	{ "orphan_dir:%04d", SFI_OTHER, 0, S_IFDIR | 0755 },
 	{ "extent_alloc:%04d", SFI_CHAIN, 0, S_IFREG | 0644 },
 	{ "inode_alloc:%04d", SFI_CHAIN, 0, S_IFREG | 0644 },
 	{ "journal:%04d", SFI_JOURNAL, 0, S_IFREG | 0644 },
 	{ "local_alloc:%04d", SFI_LOCAL_ALLOC, 0, S_IFREG | 0644 },
-	{ "truncate_log:%04d", SFI_TRUNCATE_LOG, 0, S_IFREG | 0644 }
+	{ "truncate_log:%04d", SFI_TRUNCATE_LOG, 0, S_IFREG | 0644 },
+	{ "aquota.user:%04d", SFI_QUOTA, 0, S_IFREG | 0644 },
+	{ "aquota.group:%04d", SFI_QUOTA, 0, S_IFREG | 0644 },
 };
 
 struct fs_type_translation {
@@ -226,6 +230,23 @@ static void mkfs_init_dir_trailer(State *s, DirData *dir, void *buf)
 	}
 }
 
+/* Should we skip this inode because of features enabled / disabled? */
+static int feature_skip(State *s, int system_inode)
+{
+	switch (system_inode) {
+		case USER_QUOTA_SYSTEM_INODE:
+		case LOCAL_USER_QUOTA_SYSTEM_INODE:
+			return !(s->feature_flags.opt_ro_compat &
+					OCFS2_FEATURE_RO_COMPAT_USRQUOTA);
+		case GROUP_QUOTA_SYSTEM_INODE:
+		case LOCAL_GROUP_QUOTA_SYSTEM_INODE:
+			return !(s->feature_flags.opt_ro_compat &
+					OCFS2_FEATURE_RO_COMPAT_GRPQUOTA);
+		default:
+			return 0;
+	}
+}
+
 static inline uint32_t system_dir_bytes_needed(State *s)
 {
 	int each = OCFS2_DIR_REC_LEN(SYSTEM_FILE_NAME_MAX);
@@ -233,6 +254,114 @@ static inline uint32_t system_dir_bytes_needed(State *s)
 	return each * sys_blocks_needed(s->initial_slots);
 }
 
+static void format_quota_files(State *s, ocfs2_filesys *fs)
+{
+	errcode_t ret;
+	ocfs2_quota_hash *usr_hash = NULL, *grp_hash = NULL;
+
+	/* Write correct data into quota files */
+	if (!feature_skip(s, USER_QUOTA_SYSTEM_INODE)) {
+		ret = ocfs2_init_fs_quota_info(fs, USRQUOTA);
+		if (ret) {
+			com_err(s->progname, ret,
+				"while looking up global user quota file");
+			goto error;
+		}
+		fs->qinfo[USRQUOTA].flags = 0;
+		fs->qinfo[USRQUOTA].qi_info.dqi_syncms = OCFS2_DEF_QUOTA_SYNC;
+		fs->qinfo[USRQUOTA].qi_info.dqi_bgrace = OCFS2_DEF_BLOCK_GRACE;
+		fs->qinfo[USRQUOTA].qi_info.dqi_igrace = OCFS2_DEF_INODE_GRACE;
+
+		ret = ocfs2_new_quota_hash(&usr_hash);
+		if (ret) {
+			com_err(s->progname, ret,
+				"while creating user quota hash.");
+			goto error;
+		}
+		ret = ocfs2_init_global_quota_file(fs, USRQUOTA);
+		if (ret) {
+			com_err(s->progname, ret, "while creating global user "
+				"quota file");
+			goto error;
+		}
+		ret = ocfs2_init_local_quota_files(fs, USRQUOTA);
+		if (ret) {
+			com_err(s->progname, ret,
+				"while initializing local user quota files");
+			goto error;
+		}
+	}
+	if (!feature_skip(s, GROUP_QUOTA_SYSTEM_INODE)) {
+		ret = ocfs2_init_fs_quota_info(fs, GRPQUOTA);
+		if (ret) {
+			com_err(s->progname, ret,
+				"while looking up global group quota file");
+			goto error;
+		}
+		fs->qinfo[GRPQUOTA].flags = 0;
+		fs->qinfo[GRPQUOTA].qi_info.dqi_syncms = OCFS2_DEF_QUOTA_SYNC;
+		fs->qinfo[GRPQUOTA].qi_info.dqi_bgrace = OCFS2_DEF_BLOCK_GRACE;
+		fs->qinfo[GRPQUOTA].qi_info.dqi_igrace = OCFS2_DEF_INODE_GRACE;
+		ret = ocfs2_new_quota_hash(&usr_hash);
+		if (ret) {
+			com_err(s->progname, ret,
+				"while creating group quota hash.");
+			goto error;
+		}
+		ret = ocfs2_init_global_quota_file(fs, GRPQUOTA);
+		if (ret) {
+			com_err(s->progname, ret, "while creating global group "
+				"quota file");
+			goto error;
+		}
+
+		ret = ocfs2_init_local_quota_files(fs, GRPQUOTA);
+		if (ret) {
+			com_err(s->progname, ret,
+				"while initializing local group quota files");
+			goto error;
+		}
+	}
+
+	ret = ocfs2_compute_quota_usage(fs, usr_hash, grp_hash);
+	if (ret) {
+		com_err(s->progname, ret, "while computing quota usage");
+		goto error;
+	}
+	if (usr_hash) {
+		ret = ocfs2_write_release_dquots(fs, USRQUOTA, usr_hash);
+		if (ret) {
+			com_err(s->progname, ret,
+				"while writing user quota usage");
+			goto error;
+		}
+		ret = ocfs2_free_quota_hash(usr_hash);
+		if (ret) {
+			com_err(s->progname, ret,
+				"while releasing user quota hash");
+			goto error;
+		}
+	}
+	if (grp_hash) {
+		ret = ocfs2_write_release_dquots(fs, GRPQUOTA, grp_hash);
+		if (ret) {
+			com_err(s->progname, ret,
+				"while writing group quota usage");
+			goto error;
+		}
+		ret = ocfs2_free_quota_hash(grp_hash);
+		if (ret) {
+			com_err(s->progname, ret,
+				"while releasing group quota hash");
+			goto error;
+		}
+	}
+	return;
+error:
+	clear_both_ends(s);
+	exit(1);
+}
+
 static void finish_normal_format(State *s)
 {
 	errcode_t ret;
@@ -304,6 +433,14 @@ static void finish_normal_format(State *s)
 	if (!s->quiet)
 		printf("done\n");
 
+	if (!s->quiet)
+		printf("Formatting quota files: ");
+
+	format_quota_files(s, fs);
+
+	if (!s->quiet)
+		printf("done\n");
+
 	ocfs2_close(fs);
 }
 
@@ -471,6 +608,8 @@ main(int argc, char **argv)
 	for (i = 0; i < NUM_SYSTEM_INODES; i++) {
 		if (hb_dev_skip(s, i))
 			continue;
+		if (feature_skip(s, i))
+			continue;
 
 		num = (system_files[i].global) ? 1 : s->initial_slots;
 		for (j = 0; j < num; j++) {
@@ -529,6 +668,8 @@ main(int argc, char **argv)
 	for (i = 0; i < NUM_SYSTEM_INODES; i++) {
 		if (hb_dev_skip(s, i))
 			continue;
+		if (feature_skip(s, i))
+			continue;
 
 		num = system_files[i].global ? 1 : s->initial_slots;
 		for (j = 0; j < num; j++) {
@@ -2432,6 +2573,9 @@ init_record(State *s, SystemFileDiskRecord *rec, int type, int mode)
 	case SFI_TRUNCATE_LOG:
 		rec->flags |= OCFS2_DEALLOC_FL;
 		break;
+	case SFI_QUOTA:
+		rec->flags |= OCFS2_QUOTA_FL;
+		break;
 	case SFI_OTHER:
 		break;
 	}
diff --git a/mkfs.ocfs2/mkfs.h b/mkfs.ocfs2/mkfs.h
index bd8ac45..969e4df 100644
--- a/mkfs.ocfs2/mkfs.h
+++ b/mkfs.ocfs2/mkfs.h
@@ -96,6 +96,7 @@ enum {
 	SFI_HEARTBEAT,
 	SFI_CHAIN,
 	SFI_TRUNCATE_LOG,
+	SFI_QUOTA,
 	SFI_OTHER
 };
 
diff --git a/mkfs.ocfs2/mkfs.ocfs2.8.in b/mkfs.ocfs2/mkfs.ocfs2.8.in
index 38433ee..d1f4011 100644
--- a/mkfs.ocfs2/mkfs.ocfs2.8.in
+++ b/mkfs.ocfs2/mkfs.ocfs2.8.in
@@ -146,6 +146,22 @@ arbitrary binary data. Attributes can be attached to all types of inodes: regula
 symbolic links, device nodes, etc. This feature is required for users wanting to use extended security
 facilities like POSIX ACLs or SELinux.
 .RE
+.RS 1.2i
+.TP
+\fBusrquota\fR
+Enable user quota support. With this feature enabled, filesystem will track amount of space
+and number of inodes (files, directories, symbolic links) each user owns. It is then possible
+to limit the maximum amount of space or inodes user can have. See a documentation of
+quota-tools package for more details.
+.RE
+.RS 1.2i
+.TP
+\fBgrpquota\fR
+Enable group quota support. With this feature enabled, filesystem will track amount of space
+and number of inodes (files, directories, symbolic links) each group owns. It is then possible
+to limit the maximum amount of space or inodes user can have. See a documentation of
+quota-tools package for more details.
+.RE
 
 .TP
 \fB\-\-fs\-feature\-level=\fR\fR\fIfeature\-level\fR
-- 
1.6.0.2

^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [Ocfs2-devel] [PATCH 7/8] Add quota support to tunefs.ocfs2
  2009-07-27 17:53 [Ocfs2-devel] [PATCH 0/8] Quota support for ocfs2-tools Jan Kara
                   ` (5 preceding siblings ...)
  2009-07-27 17:53 ` [Ocfs2-devel] [PATCH 6/8] Quota support for mkfs.ocfs2 Jan Kara
@ 2009-07-27 17:53 ` Jan Kara
  2009-07-29  1:04   ` Joel Becker
  2009-07-27 17:53 ` [Ocfs2-devel] [PATCH 8/8] Change headers to reflect that quota is now fully supported Jan Kara
  7 siblings, 1 reply; 32+ messages in thread
From: Jan Kara @ 2009-07-27 17:53 UTC (permalink / raw)
  To: ocfs2-devel

Implement setting of quota feature via tunefs.ocfs2 and also properly
create / delete local quota files when number of slots increases /
decreases. Implement setting of interval in which we sync changes
in local quota file to the global quota file.

Signed-off-by: Jan Kara <jack@suse.cz>
---
 tunefs.ocfs2/Makefile                     |    6 +-
 tunefs.ocfs2/feature_quota.c              |  418 +++++++++++++++++++++++++++++
 tunefs.ocfs2/ocfs2ne.c                    |   26 ++
 tunefs.ocfs2/op_features.c                |    4 +
 tunefs.ocfs2/op_set_quota_sync_interval.c |  170 ++++++++++++
 tunefs.ocfs2/op_set_slot_count.c          |   91 ++++++-
 6 files changed, 712 insertions(+), 3 deletions(-)
 create mode 100644 tunefs.ocfs2/feature_quota.c
 create mode 100644 tunefs.ocfs2/op_set_quota_sync_interval.c

diff --git a/tunefs.ocfs2/Makefile b/tunefs.ocfs2/Makefile
index fd244d8..d28da81 100644
--- a/tunefs.ocfs2/Makefile
+++ b/tunefs.ocfs2/Makefile
@@ -24,7 +24,8 @@ OCFS2NE_FEATURES =			\
 	feature_metaecc			\
 	feature_sparse_files		\
 	feature_unwritten_extents	\
-	feature_xattr
+	feature_xattr			\
+	feature_quota
 
 OCFS2NE_OPERATIONS =			\
 	op_cloned_volume		\
@@ -36,7 +37,8 @@ OCFS2NE_OPERATIONS =			\
 	op_set_label			\
 	op_set_journal_size		\
 	op_set_slot_count		\
-	op_update_cluster_stack
+	op_update_cluster_stack		\
+	op_set_quota_sync_interval	\
 
 sbindir = $(root_sbindir)
 SBIN_PROGRAMS = tunefs.ocfs2
diff --git a/tunefs.ocfs2/feature_quota.c b/tunefs.ocfs2/feature_quota.c
new file mode 100644
index 0000000..b734d49
--- /dev/null
+++ b/tunefs.ocfs2/feature_quota.c
@@ -0,0 +1,418 @@
+/* -*- mode: c; c-basic-offset: 8; -*-
+ * vim: noexpandtab sw=8 ts=8 sts=0:
+ *
+ * feature_quota.c
+ *
+ * ocfs2 tune utility for enabling and disabling quota support.
+ *
+ * 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <inttypes.h>
+#include <assert.h>
+
+#include "ocfs2/ocfs2.h"
+
+#include "libocfs2ne.h"
+
+static char *type2name(int type)
+{
+	if (type == USRQUOTA)
+		return "user";
+	return "group";
+}
+
+static errcode_t create_system_file(ocfs2_filesys *fs, int type, int node)
+{
+	char fname[OCFS2_MAX_FILENAME_LEN];
+	uint64_t blkno;
+	errcode_t ret;
+
+	ocfs2_sprintf_system_inode_name(fname, sizeof(fname),
+		type, node);
+	ret = ocfs2_lookup(fs, fs->fs_sysdir_blkno, fname, strlen(fname), NULL,
+			   &blkno);
+	if (!ret) {
+		verbosef(VL_APP, "System file \"%s\" already exists!\n",
+			 fname);
+		return 0;
+	}
+	ret = ocfs2_new_system_inode(fs, &blkno,
+				ocfs2_system_inodes[type].si_mode,
+				ocfs2_system_inodes[type].si_iflags);
+	if (ret) {
+		tcom_err(ret, "while creating system file \"%s\"", fname);
+		return ret;
+	}
+
+	ret = ocfs2_link(fs, fs->fs_sysdir_blkno, fname, blkno,
+			 OCFS2_FT_REG_FILE);
+	if (ret) {
+		tcom_err(ret, "while linking file \"%s\" in the system "
+			 "directory", fname);
+		return ret;
+	}
+	return 0;
+}
+
+static errcode_t create_quota_files(ocfs2_filesys *fs, int type,
+				    struct tools_progress *prog)
+{
+	ocfs2_quota_hash *hash;
+	errcode_t ret;
+	int num_slots = OCFS2_RAW_SB(fs->fs_super)->s_max_slots;
+	int i;
+	int local_type = (type == USRQUOTA) ?
+				LOCAL_USER_QUOTA_SYSTEM_INODE :
+				LOCAL_GROUP_QUOTA_SYSTEM_INODE;
+	int global_type = (type == USRQUOTA) ?
+				USER_QUOTA_SYSTEM_INODE :
+				GROUP_QUOTA_SYSTEM_INODE;
+
+	verbosef(VL_APP, "Creating %s quota system files\n", type2name(type));
+	ret = create_system_file(fs, global_type, 0);
+	if (ret)
+		return ret;
+	for (i = 0; i < num_slots; i++) {
+		ret = create_system_file(fs, local_type, i);
+		if (ret)
+			return ret;
+	}
+	tools_progress_step(prog, 1);
+
+	verbosef(VL_APP, "Initializing global %s quota file\n",
+		 type2name(type));
+	ret = ocfs2_init_fs_quota_info(fs, type);
+	if (ret) {
+		tcom_err(ret, "while looking up global %s quota file",
+			 type2name(type));
+		return ret;
+	}
+	fs->qinfo[type].flags = 0;
+	fs->qinfo[type].qi_info.dqi_syncms = OCFS2_DEF_QUOTA_SYNC;
+	fs->qinfo[type].qi_info.dqi_bgrace = OCFS2_DEF_BLOCK_GRACE;
+	fs->qinfo[type].qi_info.dqi_igrace = OCFS2_DEF_INODE_GRACE;
+
+	ret = ocfs2_init_global_quota_file(fs, type);
+	if (ret) {
+		tcom_err(ret, "while initilizing global %s quota files",
+			 type2name(type));
+		return ret;
+	}
+	tools_progress_step(prog, 1);
+
+	verbosef(VL_APP, "Initializing local %s quota files\n",
+		 type2name(type));
+	ret = ocfs2_init_local_quota_files(fs, type);
+	if (ret) {
+		tcom_err(ret, "while initilizing local %s quota files",
+			 type2name(type));
+		return ret;
+	}
+	tools_progress_step(prog, 1);
+
+	verbosef(VL_APP, "Computing %s quota usage\n",
+		 type2name(type));
+	ret = ocfs2_new_quota_hash(&hash);
+	if (ret) {
+		tcom_err(ret, "while creating quota hash");
+		return ret;
+	}
+	if (type == USRQUOTA)
+		ret = ocfs2_compute_quota_usage(fs, hash, NULL);
+	else
+		ret = ocfs2_compute_quota_usage(fs, NULL, hash);
+	if (ret) {
+		tcom_err(ret, "while scanning filesystem to gather "
+			 "quota usage");
+		return ret;
+	}
+	tools_progress_step(prog, 1);
+
+	verbosef(VL_APP, "Write %s quotas to file\n",
+		 type2name(type));
+	ret = ocfs2_write_release_dquots(fs, type, hash);
+	if (ret) {
+		tcom_err(ret, "while writing %s quota usage to disk",
+			 type2name(type));
+		return ret;
+	}
+	tools_progress_step(prog, 1);
+
+	ret = ocfs2_free_quota_hash(hash);
+	if (ret)
+		tcom_err(ret, "while freeing quota hash");
+	return ret;
+}
+
+struct remove_quota_files_ctxt {
+	ocfs2_filesys *fs;
+	errcode_t err;
+	int type;
+};
+
+static int remove_quota_files_iterate(struct ocfs2_dir_entry *dirent,
+				      int offset, int blocksize, char *buf,
+				      void *priv_data)
+{
+	struct remove_quota_files_ctxt *ctxt = priv_data;
+	char dname[OCFS2_MAX_FILENAME_LEN];
+	char wname[OCFS2_MAX_FILENAME_LEN];
+	errcode_t ret;
+	int tail, i;
+	int ret_flags = 0;
+
+	strncpy(dname, dirent->name, dirent->name_len);
+	dname[dirent->name_len] = 0;
+
+	/* Check whether entry is quota file of type we want - i.e. matching
+	 * aquota.user:[0-9][0-9][0-9][0-9] or aquota.user for type == USRQUOTA
+	 * and similarly for type == GRPQUOTA */
+	strcpy(wname, "aquota.");
+	strcat(wname, type2name(ctxt->type));
+	tail = strlen(wname);
+	if (strncmp(dname, wname, tail))
+		return 0;
+	if (dname[tail] == ':') {	/* May be local file? */
+		tail++;
+		for (i = 0; i < 4; i++)
+			if (dname[tail + i] < '0' || dname[tail + i] > '9')
+				return 0;
+		if (dname[tail + i])
+			return 0;
+	} else if (dname[tail])		/* May be global file? */
+		return 0;
+
+	verbosef(VL_APP, "Deleting quota file %s\n",
+		 dname);
+	ret = ocfs2_truncate(ctxt->fs, dirent->inode, 0);
+	if (ret) {
+		tcom_err(ret, "while truncating quota file \"%s\"", dname);
+		ret_flags |= OCFS2_DIRENT_ERROR;
+		ctxt->err = ret;
+		goto out;
+	}
+	ret = ocfs2_delete_inode(ctxt->fs, dirent->inode);
+	if (ret) {
+		tcom_err(ret, "while deleting quota file \"%s\"", dname);
+		ret_flags |= OCFS2_DIRENT_ERROR;
+		ctxt->err = ret;
+	} else {
+		dirent->inode = 0;
+		ret_flags |= OCFS2_DIRENT_CHANGED;
+	}
+out:
+	return ret_flags;
+}
+
+static errcode_t remove_quota_files(ocfs2_filesys *fs, int type,
+				    struct tools_progress *prog)
+{
+	struct remove_quota_files_ctxt ctxt = {
+		.fs = fs,
+		.type = type,
+		.err = 0,
+	};
+
+	ocfs2_dir_iterate(fs, fs->fs_sysdir_blkno,
+			  OCFS2_DIRENT_FLAG_EXCLUDE_DOTS, NULL,
+			  remove_quota_files_iterate, &ctxt);
+	tools_progress_step(prog, 1);
+	return ctxt.err;
+}
+
+static int enable_usrquota(ocfs2_filesys *fs, int flags)
+{
+	struct ocfs2_super_block *super = OCFS2_RAW_SB(fs->fs_super);
+	errcode_t ret;
+	struct tools_progress *prog = NULL;
+
+	if (OCFS2_HAS_RO_COMPAT_FEATURE(super,
+	    OCFS2_FEATURE_RO_COMPAT_USRQUOTA)) {
+		verbosef(VL_APP, "User quotas are already enabled; "
+			 "nothing to enable\n");
+		return 0;
+	}
+
+	if (!tools_interact("Enable user quota feature on device "
+			    "\"%s\"? ",
+			    fs->fs_devname))
+		return 0;
+
+	prog = tools_progress_start("Enabling user quota", "usrquota", 6);
+	if (!prog) {
+		ret = TUNEFS_ET_NO_MEMORY;
+		tcom_err(ret, "while initializing progress display");
+		return ret;
+	}
+	tunefs_block_signals();
+	ret = create_quota_files(fs, USRQUOTA, prog);
+	if (ret) {
+		tcom_err(ret, "while creating user quota files");
+		goto bail;
+	}
+	OCFS2_SET_RO_COMPAT_FEATURE(super,
+				    OCFS2_FEATURE_RO_COMPAT_USRQUOTA);
+	ret = ocfs2_write_super(fs);
+	tools_progress_step(prog, 1);
+bail:
+	tunefs_unblock_signals();
+	tools_progress_stop(prog);
+	return ret;
+}
+
+static int disable_usrquota(ocfs2_filesys *fs, int flags)
+{
+	errcode_t ret;
+	struct ocfs2_super_block *super = OCFS2_RAW_SB(fs->fs_super);
+	struct tools_progress *prog = NULL;
+
+	if (!OCFS2_HAS_RO_COMPAT_FEATURE(super,
+	    OCFS2_FEATURE_RO_COMPAT_USRQUOTA)) {
+		verbosef(VL_APP, "User quotas are already disabled; "
+			 "nothing to disable\n");
+		return 0;
+	}
+
+	if (!tools_interact("Disable user quota feature on device "
+			    "\"%s\"? ",
+			    fs->fs_devname))
+		return 0;
+
+	prog = tools_progress_start("Disabling user quota", "nousrquota", 2);
+	if (!prog) {
+		ret = TUNEFS_ET_NO_MEMORY;
+		tcom_err(ret, "while initializing progress display");
+		return ret;
+	}
+	tunefs_block_signals();
+	ret = remove_quota_files(fs, USRQUOTA, prog);
+	if (ret) {
+		tcom_err(ret, "while removing user quota files");
+		goto bail;
+	}
+	OCFS2_CLEAR_RO_COMPAT_FEATURE(super,
+				      OCFS2_FEATURE_RO_COMPAT_USRQUOTA);
+	ret = ocfs2_write_super(fs);
+	tools_progress_step(prog, 1);
+bail:
+	tunefs_unblock_signals();
+	tools_progress_stop(prog);
+	return ret;
+}
+
+static int enable_grpquota(ocfs2_filesys *fs, int flags)
+{
+	struct ocfs2_super_block *super = OCFS2_RAW_SB(fs->fs_super);
+	errcode_t ret;
+	struct tools_progress *prog = NULL;
+
+	if (OCFS2_HAS_RO_COMPAT_FEATURE(super,
+	    OCFS2_FEATURE_RO_COMPAT_GRPQUOTA)) {
+		verbosef(VL_APP, "Group quotas are already enabled; "
+			 "nothing to enable\n");
+		return 0;
+	}
+
+	if (!tools_interact("Enable group quota feature on device "
+			    "\"%s\"? ",
+			    fs->fs_devname))
+		return 0;
+	prog = tools_progress_start("Enabling group quota", "grpquota", 6);
+	if (!prog) {
+		ret = TUNEFS_ET_NO_MEMORY;
+		tcom_err(ret, "while initializing progress display");
+		return ret;
+	}
+
+	tunefs_block_signals();
+	ret = create_quota_files(fs, GRPQUOTA, prog);
+	if (ret) {
+		tcom_err(ret, "while creating group quota files");
+		goto bail;
+	}
+	OCFS2_SET_RO_COMPAT_FEATURE(super,
+				    OCFS2_FEATURE_RO_COMPAT_GRPQUOTA);
+	ret = ocfs2_write_super(fs);
+	tools_progress_step(prog, 1);
+bail:
+	tools_progress_stop(prog);
+	tunefs_unblock_signals();
+	return ret;
+}
+
+static int disable_grpquota(ocfs2_filesys *fs, int flags)
+{
+	errcode_t ret;
+	struct ocfs2_super_block *super = OCFS2_RAW_SB(fs->fs_super);
+	struct tools_progress *prog = NULL;
+
+	if (!OCFS2_HAS_RO_COMPAT_FEATURE(super,
+	    OCFS2_FEATURE_RO_COMPAT_GRPQUOTA)) {
+		verbosef(VL_APP, "Group quotas are already disabled; "
+			 "nothing to disable\n");
+		return 0;
+	}
+
+	if (!tools_interact("Disable group quota feature on device "
+			    "\"%s\"? ",
+			    fs->fs_devname))
+		return 0;
+	prog = tools_progress_start("Disabling user quota", "nousrquota", 2);
+	if (!prog) {
+		ret = TUNEFS_ET_NO_MEMORY;
+		tcom_err(ret, "while initializing progress display");
+		return ret;
+	}
+
+	tunefs_block_signals();
+	ret = remove_quota_files(fs, GRPQUOTA, prog);
+	if (ret) {
+		tcom_err(ret, "while removing group quota files");
+		goto bail;
+	}
+	OCFS2_CLEAR_RO_COMPAT_FEATURE(super,
+				      OCFS2_FEATURE_RO_COMPAT_GRPQUOTA);
+	ret = ocfs2_write_super(fs);
+	tools_progress_step(prog, 1);
+bail:
+	tools_progress_stop(prog);
+	tunefs_unblock_signals();
+	return ret;
+}
+
+DEFINE_TUNEFS_FEATURE_RO_COMPAT(usrquota,
+				OCFS2_FEATURE_RO_COMPAT_USRQUOTA,
+				TUNEFS_FLAG_RW | TUNEFS_FLAG_ALLOCATION,
+				enable_usrquota,
+				disable_usrquota);
+
+DEFINE_TUNEFS_FEATURE_RO_COMPAT(grpquota,
+				OCFS2_FEATURE_RO_COMPAT_GRPQUOTA,
+				TUNEFS_FLAG_RW | TUNEFS_FLAG_ALLOCATION,
+				enable_grpquota,
+				disable_grpquota);
+#ifdef DEBUG_EXE
+int main(int argc, char *argv[])
+{
+	int ret;
+
+	ret = tunefs_feature_main(argc, argv, &usrquota_feature);
+	if (ret)
+		return ret;
+	return tunefs_feature_main(argc, argv, &grpquota_feature);
+}
+#endif
diff --git a/tunefs.ocfs2/ocfs2ne.c b/tunefs.ocfs2/ocfs2ne.c
index c9051a4..48760c6 100644
--- a/tunefs.ocfs2/ocfs2ne.c
+++ b/tunefs.ocfs2/ocfs2ne.c
@@ -98,6 +98,8 @@ extern struct tunefs_operation set_label_op;
 extern struct tunefs_operation set_slot_count_op;
 extern struct tunefs_operation update_cluster_stack_op;
 extern struct tunefs_operation cloned_volume_op;
+extern struct tunefs_operation set_usrquota_sync_interval_op;
+extern struct tunefs_operation set_grpquota_sync_interval_op;
 
 /* List of operations we're going to run */
 static LIST_HEAD(tunefs_run_list);
@@ -583,6 +585,28 @@ static struct tunefs_option journal_option = {
 	.opt_handle	= handle_journal_arg,
 };
 
+static struct tunefs_option set_usrquota_sync_interval_option = {
+	.opt_option	= {
+		.name		= "usrquota-sync-interval",
+		.val		= 256,
+		.has_arg	= 1,
+	},
+	.opt_help	= "   --usrquota-sync-interval <interval>",
+	.opt_handle	= generic_handle_arg,
+	.opt_op		= &set_usrquota_sync_interval_op,
+};
+
+static struct tunefs_option set_grpquota_sync_interval_option = {
+	.opt_option	= {
+		.name		= "grpquota-sync-interval",
+		.val		= 257,
+		.has_arg	= 1,
+	},
+	.opt_help	= "   --grpquota-sync-interval <interval>",
+	.opt_handle	= generic_handle_arg,
+	.opt_op		= &set_grpquota_sync_interval_op,
+};
+
 /* The order here creates the order in print_usage() */
 static struct tunefs_option *options[] = {
 	&help_option,
@@ -603,6 +627,8 @@ static struct tunefs_option *options[] = {
 	&features_option,
 	&update_cluster_stack_option,
 	&cloned_volume_option,
+	&set_usrquota_sync_interval_option,
+	&set_grpquota_sync_interval_option,
 	&yes_option,
 	&no_option,
 	NULL,
diff --git a/tunefs.ocfs2/op_features.c b/tunefs.ocfs2/op_features.c
index ee9442e..afcc1f4 100644
--- a/tunefs.ocfs2/op_features.c
+++ b/tunefs.ocfs2/op_features.c
@@ -41,6 +41,8 @@ extern struct tunefs_feature metaecc_feature;
 extern struct tunefs_feature sparse_files_feature;
 extern struct tunefs_feature unwritten_extents_feature;
 extern struct tunefs_feature xattr_feature;
+extern struct tunefs_feature usrquota_feature;
+extern struct tunefs_feature grpquota_feature;
 
 /* List of features supported by ocfs2ne */
 static struct tunefs_feature *features[] = {
@@ -52,6 +54,8 @@ static struct tunefs_feature *features[] = {
 	&sparse_files_feature,
 	&unwritten_extents_feature,
 	&xattr_feature,
+	&usrquota_feature,
+	&grpquota_feature,
 	NULL,
 };
 
diff --git a/tunefs.ocfs2/op_set_quota_sync_interval.c b/tunefs.ocfs2/op_set_quota_sync_interval.c
new file mode 100644
index 0000000..12b70b5
--- /dev/null
+++ b/tunefs.ocfs2/op_set_quota_sync_interval.c
@@ -0,0 +1,170 @@
+/* -*- mode: c; c-basic-offset: 8; -*-
+ * vim: noexpandtab sw=8 ts=8 sts=0:
+ *
+ * op_set_quota_sync_interval.c
+ *
+ * ocfs2 tune utility for updating interval for syncing quota structures
+ * to global quota file.
+ *
+ * Copyright (C) 2009 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.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <inttypes.h>
+
+#include "ocfs2/ocfs2.h"
+
+#include "libocfs2ne.h"
+
+static char *type2name(int type)
+{
+	if (type == USRQUOTA)
+		return "user";
+	return "group";
+}
+
+static int update_sync_interval(ocfs2_filesys *fs, int type,
+				unsigned long syncms)
+{
+	errcode_t err;
+	struct tools_progress *prog;
+	int feature = (type == USRQUOTA) ? OCFS2_FEATURE_RO_COMPAT_USRQUOTA :
+					   OCFS2_FEATURE_RO_COMPAT_GRPQUOTA;
+	struct ocfs2_global_disk_dqinfo *qinfo;
+
+	if (!OCFS2_HAS_RO_COMPAT_FEATURE(OCFS2_RAW_SB(fs->fs_super), feature)) {
+		errorf("The %s quota is not enabled on device \"%s\"\n",
+		       type2name(type), fs->fs_devname);
+		return 1;
+	}
+	err = ocfs2_init_fs_quota_info(fs, type);
+	if (err) {
+		tcom_err(err, "while looking up %s quota file on device "
+			 "\"%s\"", type2name(type), fs->fs_devname);
+		return 1;
+	}
+	err = ocfs2_read_global_quota_info(fs, type);
+	if (err) {
+		tcom_err(err, "while reading %s quota info on device \"%s\"",
+			 type2name(type), fs->fs_devname);
+		return 1;
+	}
+	qinfo = &fs->qinfo[type].qi_info;
+	if (qinfo->dqi_syncms == syncms) {
+		verbosef(VL_APP,
+			 "Device \"%s\" already has interval %lu set; "
+			 "nothing to do\n", fs->fs_devname, syncms);
+		return 0;
+	}
+
+	if (!tools_interact("Change quota syncing interval on device \"%s\" "
+			    "from %lu to %lu? ", fs->fs_devname,
+			    (unsigned long)qinfo->dqi_syncms, syncms))
+		return 0;
+
+	prog = tools_progress_start("Setting syncing interval", "interval", 1);
+	if (!prog) {
+		tcom_err(err, "while initializing the progress display");
+		return 1;
+	}
+
+	tunefs_block_signals();
+	qinfo->dqi_syncms = syncms;
+	err = ocfs2_write_global_quota_info(fs, type);
+	tunefs_unblock_signals();
+
+	tools_progress_step(prog, 1);
+	tools_progress_stop(prog);
+
+	if (err) {
+		tcom_err(err,
+			 "- unable to update %s quota syncing interval on "
+			 "device \"%s\"", type2name(type), fs->fs_devname);
+		return 1;
+	}
+
+	return 0;
+}
+
+static int set_quota_sync_interval_parse_option(struct tunefs_operation *op,
+						char *arg)
+{
+	int rc = 1;
+	unsigned long interval;
+	char *ptr;
+
+	if (!arg) {
+		errorf("No interval specified\n");
+		goto out;
+	}
+
+	interval = strtoul(arg, &ptr, 10);
+	if (*ptr != 0) {
+		errorf("Invalid number: %s", arg);
+		goto out;
+	}
+
+	if (interval < 100 || interval == ULONG_MAX ||
+	    interval > ~(uint32_t)0) {
+		errorf("Quota sync interval is out of range (minimum is 100,"
+		       " maximum is 4294967295): %s\n", arg);
+		goto out;
+	}
+
+	op->to_private = (void *)interval;
+	rc = 0;
+out:
+	return rc;
+}
+
+static int set_usrquota_sync_interval_run(struct tunefs_operation *op,
+					  ocfs2_filesys *fs,
+					  int flags)
+{
+	return update_sync_interval(fs, USRQUOTA,
+				    (unsigned long)op->to_private);
+}
+
+static int set_grpquota_sync_interval_run(struct tunefs_operation *op,
+					  ocfs2_filesys *fs,
+					  int flags)
+{
+	return update_sync_interval(fs, GRPQUOTA,
+				    (unsigned long)op->to_private);
+}
+
+
+DEFINE_TUNEFS_OP(set_usrquota_sync_interval,
+		 "Usage: op_set_usrquota_sync_interval [opts] <device> <interval in ms>\n",
+		 TUNEFS_FLAG_RW,
+		 set_quota_sync_interval_parse_option,
+		 set_usrquota_sync_interval_run);
+
+DEFINE_TUNEFS_OP(set_grpquota_sync_interval,
+		 "Usage: op_set_grpquota_sync_interval [opts] <device> <interval in ms>\n",
+		 TUNEFS_FLAG_RW,
+		 set_quota_sync_interval_parse_option,
+		 set_grpquota_sync_interval_run);
+
+#ifdef DEBUG_EXE
+int main(int argc, char *argv[])
+{
+	int ret;
+
+	ret = tunefs_op_main(argc, argv, &set_usrquota_sync_interval_op);
+	if (ret)
+		return ret;
+	return tunefs_op_main(argc, argv, &set_grpquota_sync_interval_op);
+}
+#endif
diff --git a/tunefs.ocfs2/op_set_slot_count.c b/tunefs.ocfs2/op_set_slot_count.c
index 7d94b98..46ce2de 100644
--- a/tunefs.ocfs2/op_set_slot_count.c
+++ b/tunefs.ocfs2/op_set_slot_count.c
@@ -56,6 +56,7 @@ static errcode_t add_slots(ocfs2_filesys *fs, int num_slots)
 {
 	errcode_t ret;
 	uint16_t old_num = OCFS2_RAW_SB(fs->fs_super)->s_max_slots;
+	struct ocfs2_super_block *super = OCFS2_RAW_SB(fs->fs_super);
 	char fname[OCFS2_MAX_FILENAME_LEN];
 	uint64_t blkno;
 	int i, j, max_slots;
@@ -83,6 +84,14 @@ static errcode_t add_slots(ocfs2_filesys *fs, int num_slots)
 
 	ret = 0;
 	for (i = OCFS2_LAST_GLOBAL_SYSTEM_INODE + 1; i < NUM_SYSTEM_INODES; ++i) {
+		if (i == LOCAL_USER_QUOTA_SYSTEM_INODE &&
+		    !OCFS2_HAS_RO_COMPAT_FEATURE(super,
+					OCFS2_FEATURE_RO_COMPAT_USRQUOTA))
+			continue;
+		if (i == LOCAL_GROUP_QUOTA_SYSTEM_INODE &&
+		    !OCFS2_HAS_RO_COMPAT_FEATURE(super,
+					OCFS2_FEATURE_RO_COMPAT_GRPQUOTA))
+			continue;
 		for (j = old_num; j < num_slots; ++j) {
 			ocfs2_sprintf_system_inode_name(fname,
 							OCFS2_MAX_FILENAME_LEN,
@@ -141,6 +150,32 @@ static errcode_t add_slots(ocfs2_filesys *fs, int num_slots)
 					error_message(ret), blkno, fname);
 				goto bail;
 			}
+			/* Initialize quota files */
+			if (i == LOCAL_USER_QUOTA_SYSTEM_INODE) {
+				verbosef(VL_APP, "Initializing local user "
+					 "quota file\n");
+				ret = ocfs2_init_local_quota_file(fs, USRQUOTA,
+								  blkno);
+				if (ret) {
+					verbosef(VL_APP,
+						 "%s while initializing user "
+						 "quota file %s\n",
+						 error_message(ret), fname);
+					goto bail;
+				}
+			} else if (i == LOCAL_GROUP_QUOTA_SYSTEM_INODE) {
+				verbosef(VL_APP, "Initializing local group "
+					 "quota file\n");
+				ret = ocfs2_init_local_quota_file(fs, GRPQUOTA,
+								  blkno);
+				if (ret) {
+					verbosef(VL_APP,
+						 "%s while initializing group "
+						 "quota file %s\n",
+						 error_message(ret), fname);
+					goto bail;
+				}
+			}
 			verbosef(VL_APP, "System file \"%s\" created\n",
 				 fname);
 			tools_progress_step(prog, 1);
@@ -585,6 +620,54 @@ bail:
 	return ret;
 }
 
+static errcode_t truncate_quota_file(ocfs2_filesys *fs,
+				     uint16_t removed_slot,
+				     int type)
+{
+	errcode_t ret;
+	uint64_t blkno;
+	char fname[OCFS2_MAX_FILENAME_LEN];
+	int local_type = (type == USRQUOTA) ? LOCAL_USER_QUOTA_SYSTEM_INODE :
+					      LOCAL_GROUP_QUOTA_SYSTEM_INODE;
+
+	ocfs2_sprintf_system_inode_name(fname, OCFS2_MAX_FILENAME_LEN,
+					local_type, removed_slot);
+	verbosef(VL_APP, "Truncating quota file \"%s\"\n", fname);
+
+	ret = ocfs2_lookup_system_inode(fs, local_type, removed_slot, &blkno);
+	if (!ret) {
+		ret = ocfs2_truncate(fs, blkno, 0);
+		if (!ret)
+			verbosef(VL_APP, "Quota file \"%s\" truncated\n",
+				 fname);
+		else
+			verbosef(VL_APP,
+				 "%s while truncating quota file \"%s\"\n",
+				 error_message(ret), fname);
+	} else
+		verbosef(VL_APP,
+			 "%s while looking up quota file \"%s\"\n",
+			 error_message(ret), fname);
+
+	return ret;
+}
+
+static errcode_t truncate_quota_files(ocfs2_filesys *fs,
+				      uint16_t removed_slot)
+{
+	errcode_t ret = 0;
+
+	if (OCFS2_HAS_RO_COMPAT_FEATURE(OCFS2_RAW_SB(fs->fs_super),
+					OCFS2_FEATURE_RO_COMPAT_USRQUOTA))
+		ret = truncate_quota_file(fs, removed_slot, USRQUOTA);
+	if (ret)
+		return ret;
+	if (OCFS2_HAS_RO_COMPAT_FEATURE(OCFS2_RAW_SB(fs->fs_super),
+					OCFS2_FEATURE_RO_COMPAT_GRPQUOTA))
+		ret = truncate_quota_file(fs, removed_slot, GRPQUOTA);
+	return ret;
+}
+
 static errcode_t truncate_orphan_dir(ocfs2_filesys *fs,
 				     uint16_t removed_slot)
 {
@@ -887,7 +970,7 @@ static errcode_t remove_slots(ocfs2_filesys *fs, int num_slots)
 
 	/* We have seven steps in removing each slot */
 	prog = tools_progress_start("Removing slots", "rmslots",
-				    (old_num - num_slots) * 7);
+				    (old_num - num_slots) * 8);
 	if (!prog) {
 		ret = TUNEFS_ET_NO_MEMORY;
 		goto bail;
@@ -930,6 +1013,12 @@ static errcode_t remove_slots(ocfs2_filesys *fs, int num_slots)
 			goto bail;
 		tools_progress_step(prog, 1);
 
+		/* truncate local quota files */
+		ret = truncate_quota_files(fs, removed_slot);
+		if (ret)
+			goto bail;
+		tools_progress_step(prog, 1);
+
 		/* Now, we decrease the max_slots first and then remove the
 		 * slots for the reason that:
 		 *
-- 
1.6.0.2

^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [Ocfs2-devel] [PATCH 8/8] Change headers to reflect that quota is now fully supported.
  2009-07-27 17:53 [Ocfs2-devel] [PATCH 0/8] Quota support for ocfs2-tools Jan Kara
                   ` (6 preceding siblings ...)
  2009-07-27 17:53 ` [Ocfs2-devel] [PATCH 7/8] Add quota support to tunefs.ocfs2 Jan Kara
@ 2009-07-27 17:53 ` Jan Kara
  7 siblings, 0 replies; 32+ messages in thread
From: Jan Kara @ 2009-07-27 17:53 UTC (permalink / raw)
  To: ocfs2-devel

Signed-off-by: Jan Kara <jack@suse.cz>
---
 include/ocfs2-kernel/ocfs2_fs.h |    4 +++-
 libocfs2/feature_string.c       |    4 +++-
 2 files changed, 6 insertions(+), 2 deletions(-)

diff --git a/include/ocfs2-kernel/ocfs2_fs.h b/include/ocfs2-kernel/ocfs2_fs.h
index ad655dc..32a4d91 100644
--- a/include/ocfs2-kernel/ocfs2_fs.h
+++ b/include/ocfs2-kernel/ocfs2_fs.h
@@ -96,7 +96,9 @@
 					 | OCFS2_FEATURE_INCOMPAT_USERSPACE_STACK \
 					 | OCFS2_FEATURE_INCOMPAT_META_ECC \
 					 | OCFS2_FEATURE_INCOMPAT_XATTR)
-#define OCFS2_FEATURE_RO_COMPAT_SUPP	OCFS2_FEATURE_RO_COMPAT_UNWRITTEN
+#define OCFS2_FEATURE_RO_COMPAT_SUPP	(OCFS2_FEATURE_RO_COMPAT_UNWRITTEN \
+					 | OCFS2_FEATURE_RO_COMPAT_USRQUOTA \
+					 | OCFS2_FEATURE_RO_COMPAT_GRPQUOTA)
 
 /*
  * Heartbeat-only devices are missing journals and other files.  The
diff --git a/libocfs2/feature_string.c b/libocfs2/feature_string.c
index 18ae6e9..cf3a0dd 100644
--- a/libocfs2/feature_string.c
+++ b/libocfs2/feature_string.c
@@ -76,7 +76,9 @@ static ocfs2_fs_options feature_level_defaults[] = {
 	 OCFS2_FEATURE_INCOMPAT_INLINE_DATA |
 	 OCFS2_FEATURE_INCOMPAT_META_ECC |
 	 OCFS2_FEATURE_INCOMPAT_XATTR,
-	 OCFS2_FEATURE_RO_COMPAT_UNWRITTEN}, /* OCFS2_FEATURE_LEVEL_MAX_FEATURES */
+	 OCFS2_FEATURE_RO_COMPAT_UNWRITTEN |
+	 OCFS2_FEATURE_RO_COMPAT_USRQUOTA |
+	 OCFS2_FEATURE_RO_COMPAT_GRPQUOTA }, /* OCFS2_FEATURE_LEVEL_MAX_FEATURES */
 };
 
 /* These are the features we support in mkfs/tunefs via --fs-features */
-- 
1.6.0.2

^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [Ocfs2-devel] [PATCH 3/8] Implement quota functions to libocfs2
  2009-07-27 17:53 ` [Ocfs2-devel] [PATCH 3/8] Implement quota functions to libocfs2 Jan Kara
@ 2009-07-28  7:55   ` Tao Ma
  2009-07-28  8:40     ` Jan Kara
  0 siblings, 1 reply; 32+ messages in thread
From: Tao Ma @ 2009-07-28  7:55 UTC (permalink / raw)
  To: ocfs2-devel

Hi Jan,

Jan Kara wrote:
> Signed-off-by: Jan Kara <jack@suse.cz>
> ---
>  include/ocfs2/ocfs2.h     |   80 +++
>  libocfs2/Makefile         |    1 +
>  libocfs2/feature_string.c |   18 +
>  libocfs2/ocfs2_err.et     |    6 +
>  libocfs2/quota.c          | 1208 +++++++++++++++++++++++++++++++++++++++++++++
>  5 files changed, 1313 insertions(+), 0 deletions(-)
>  create mode 100644 libocfs2/quota.c
> 
> diff --git a/include/ocfs2/ocfs2.h b/include/ocfs2/ocfs2.h
> index ac16823..7f136ca 100644
> --- a/include/ocfs2/ocfs2.h
> +++ b/include/ocfs2/ocfs2.h
> @@ -125,16 +125,36 @@
>  #define OCFS2_CHB_WAITING	2
>  #define OCFS2_CHB_COMPLETE	3
>  
> +/* Flags for global quotafile info */
> +#define OCFS2_QF_INFO_DIRTY 1
> +/* Should be power of two */
> +#define DEFAULT_QUOTA_HASH_SIZE 8192
> +#define MAX_QUOTA_HASH_SIZE (1<<21)	/* 16 MB on 64-bit arch*/
16MB is 1<<24?
> +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;
> +	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, (2 * fs->fs_blocksize +
> +			fs->fs_clustersize - 1) / fs->fs_clustersize);
> +	if (err)
> +		goto out;
> +	di->i_size = 2 * fs->fs_blocksize;
> +	di->i_mtime = time(NULL);
> +	err = ocfs2_write_inode(fs, blkno, (char *)di);
> +	if (err)
> +		goto out;
> +
> +	err = ocfs2_malloc_blocks(fs->fs_io, fs->fs_blocksize * 2, &buf);
> +	if (err)
> +		goto out;
> +	memset(buf, 0, 2 * fs->fs_blocksize);
> +
> +	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 = 2;
I see hardcoded "2" so many times in this function while in 
ocfs2_init_global_quota_file you define "int blocks = 2", so I just 
wonder if we can change it somehow like that function?
> +	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, 2 * fs->fs_blocksize, 0, &written);
> +	if (!err && written != 2 * fs->fs_blocksize) {
> +		err = OCFS2_ET_INTERNAL_FAILURE;
> +		goto out;
> +	}
and here in ocfs2_init_global_quota_file you use write_blk which will 
calculate ocfs2_checksum_quota_block. So we can use that function also here?

Regards,
Tao

^ permalink raw reply	[flat|nested] 32+ messages in thread

* [Ocfs2-devel] [PATCH 6/8] Quota support for mkfs.ocfs2
  2009-07-27 17:53 ` [Ocfs2-devel] [PATCH 6/8] Quota support for mkfs.ocfs2 Jan Kara
@ 2009-07-28  8:11   ` Tao Ma
  2009-07-28  8:43     ` Jan Kara
  0 siblings, 1 reply; 32+ messages in thread
From: Tao Ma @ 2009-07-28  8:11 UTC (permalink / raw)
  To: ocfs2-devel

Hi Jan,

Jan Kara wrote:
> Signed-off-by: Jan Kara <jack@suse.cz>
> ---
>  mkfs.ocfs2/mkfs.c          |  146 +++++++++++++++++++++++++++++++++++++++++++-
>  mkfs.ocfs2/mkfs.h          |    1 +
>  mkfs.ocfs2/mkfs.ocfs2.8.in |   16 +++++
>  3 files changed, 162 insertions(+), 1 deletions(-)
> 
> diff --git a/mkfs.ocfs2/mkfs.c b/mkfs.ocfs2/mkfs.c
> index 6acc6e6..8496d03 100644
> --- a/mkfs.ocfs2/mkfs.c
> +++ b/mkfs.ocfs2/mkfs.c
> +static void format_quota_files(State *s, ocfs2_filesys *fs)
> +{
> +	errcode_t ret;
> +	ocfs2_quota_hash *usr_hash = NULL, *grp_hash = NULL;
> +
> +	/* Write correct data into quota files */
> +	if (!feature_skip(s, USER_QUOTA_SYSTEM_INODE)) {
> +		ret = ocfs2_init_fs_quota_info(fs, USRQUOTA);
> +		if (ret) {
> +			com_err(s->progname, ret,
> +				"while looking up global user quota file");
> +			goto error;
> +		}
> +		fs->qinfo[USRQUOTA].flags = 0;
> +		fs->qinfo[USRQUOTA].qi_info.dqi_syncms = OCFS2_DEF_QUOTA_SYNC;
> +		fs->qinfo[USRQUOTA].qi_info.dqi_bgrace = OCFS2_DEF_BLOCK_GRACE;
> +		fs->qinfo[USRQUOTA].qi_info.dqi_igrace = OCFS2_DEF_INODE_GRACE;
> +
> +		ret = ocfs2_new_quota_hash(&usr_hash);
> +		if (ret) {
> +			com_err(s->progname, ret,
> +				"while creating user quota hash.");
> +			goto error;
> +		}
> +		ret = ocfs2_init_global_quota_file(fs, USRQUOTA);
> +		if (ret) {
> +			com_err(s->progname, ret, "while creating global user "
> +				"quota file");
> +			goto error;
> +		}
> +		ret = ocfs2_init_local_quota_files(fs, USRQUOTA);
> +		if (ret) {
> +			com_err(s->progname, ret,
> +				"while initializing local user quota files");
> +			goto error;
> +		}
> +	}
> +	if (!feature_skip(s, GROUP_QUOTA_SYSTEM_INODE)) {
> +		ret = ocfs2_init_fs_quota_info(fs, GRPQUOTA);
> +		if (ret) {
> +			com_err(s->progname, ret,
> +				"while looking up global group quota file");
> +			goto error;
> +		}
> +		fs->qinfo[GRPQUOTA].flags = 0;
> +		fs->qinfo[GRPQUOTA].qi_info.dqi_syncms = OCFS2_DEF_QUOTA_SYNC;
> +		fs->qinfo[GRPQUOTA].qi_info.dqi_bgrace = OCFS2_DEF_BLOCK_GRACE;
> +		fs->qinfo[GRPQUOTA].qi_info.dqi_igrace = OCFS2_DEF_INODE_GRACE;
> +		ret = ocfs2_new_quota_hash(&usr_hash);
grp_hash here?

Regards,
Tao

^ permalink raw reply	[flat|nested] 32+ messages in thread

* [Ocfs2-devel] [PATCH 3/8] Implement quota functions to libocfs2
  2009-07-28  7:55   ` Tao Ma
@ 2009-07-28  8:40     ` Jan Kara
  2009-07-28  8:43       ` Tao Ma
  0 siblings, 1 reply; 32+ messages in thread
From: Jan Kara @ 2009-07-28  8:40 UTC (permalink / raw)
  To: ocfs2-devel

On Tue 28-07-09 15:55:30, Tao Ma wrote:
> Hi Jan,
>
> Jan Kara wrote:
>> Signed-off-by: Jan Kara <jack@suse.cz>
>> ---
>>  include/ocfs2/ocfs2.h     |   80 +++
>>  libocfs2/Makefile         |    1 +
>>  libocfs2/feature_string.c |   18 +
>>  libocfs2/ocfs2_err.et     |    6 +
>>  libocfs2/quota.c          | 1208 +++++++++++++++++++++++++++++++++++++++++++++
>>  5 files changed, 1313 insertions(+), 0 deletions(-)
>>  create mode 100644 libocfs2/quota.c
>>
>> diff --git a/include/ocfs2/ocfs2.h b/include/ocfs2/ocfs2.h
>> index ac16823..7f136ca 100644
>> --- a/include/ocfs2/ocfs2.h
>> +++ b/include/ocfs2/ocfs2.h
>> @@ -125,16 +125,36 @@
>>  #define OCFS2_CHB_WAITING	2
>>  #define OCFS2_CHB_COMPLETE	3
>>  +/* Flags for global quotafile info */
>> +#define OCFS2_QF_INFO_DIRTY 1
>> +/* Should be power of two */
>> +#define DEFAULT_QUOTA_HASH_SIZE 8192
>> +#define MAX_QUOTA_HASH_SIZE (1<<21)	/* 16 MB on 64-bit arch*/
> 16MB is 1<<24?
  The comment is meant to say that the hash-table will occupy 16 MB on a
64-bit architecture where each pointer has 8 bytes... I'll expand the
comment a bit.

>> +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;
>> +	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, (2 * fs->fs_blocksize +
>> +			fs->fs_clustersize - 1) / fs->fs_clustersize);
>> +	if (err)
>> +		goto out;
>> +	di->i_size = 2 * fs->fs_blocksize;
>> +	di->i_mtime = time(NULL);
>> +	err = ocfs2_write_inode(fs, blkno, (char *)di);
>> +	if (err)
>> +		goto out;
>> +
>> +	err = ocfs2_malloc_blocks(fs->fs_io, fs->fs_blocksize * 2, &buf);
>> +	if (err)
>> +		goto out;
>> +	memset(buf, 0, 2 * fs->fs_blocksize);
>> +
>> +	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 = 2;
> I see hardcoded "2" so many times in this function while in 
> ocfs2_init_global_quota_file you define "int blocks = 2", so I just wonder 
> if we can change it somehow like that function?
  OK, will do.

>> +	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, 2 * fs->fs_blocksize, 0, &written);
>> +	if (!err && written != 2 * fs->fs_blocksize) {
>> +		err = OCFS2_ET_INTERNAL_FAILURE;
>> +		goto out;
>> +	}
> and here in ocfs2_init_global_quota_file you use write_blk which will 
> calculate ocfs2_checksum_quota_block. So we can use that function also 
> here?
  No, we cannot since write_blk() writes to a global quota file. Here we
need to write to a node-local quota file. Since there's only this place,
where we write to local quota file, I'd just leave the code as is.

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

^ permalink raw reply	[flat|nested] 32+ messages in thread

* [Ocfs2-devel] [PATCH 6/8] Quota support for mkfs.ocfs2
  2009-07-28  8:11   ` Tao Ma
@ 2009-07-28  8:43     ` Jan Kara
  2009-07-28 10:09       ` Jan Kara
  0 siblings, 1 reply; 32+ messages in thread
From: Jan Kara @ 2009-07-28  8:43 UTC (permalink / raw)
  To: ocfs2-devel

  Hi,

On Tue 28-07-09 16:11:01, Tao Ma wrote:
> Jan Kara wrote:
>> Signed-off-by: Jan Kara <jack@suse.cz>
>> ---
>>  mkfs.ocfs2/mkfs.c          |  146 +++++++++++++++++++++++++++++++++++++++++++-
>>  mkfs.ocfs2/mkfs.h          |    1 +
>>  mkfs.ocfs2/mkfs.ocfs2.8.in |   16 +++++
>>  3 files changed, 162 insertions(+), 1 deletions(-)
>>
>> diff --git a/mkfs.ocfs2/mkfs.c b/mkfs.ocfs2/mkfs.c
>> index 6acc6e6..8496d03 100644
>> --- a/mkfs.ocfs2/mkfs.c
>> +++ b/mkfs.ocfs2/mkfs.c
>> +static void format_quota_files(State *s, ocfs2_filesys *fs)
>> +{
>> +	errcode_t ret;
>> +	ocfs2_quota_hash *usr_hash = NULL, *grp_hash = NULL;
>> +
>> +	/* Write correct data into quota files */
>> +	if (!feature_skip(s, USER_QUOTA_SYSTEM_INODE)) {
>> +		ret = ocfs2_init_fs_quota_info(fs, USRQUOTA);
>> +		if (ret) {
>> +			com_err(s->progname, ret,
>> +				"while looking up global user quota file");
>> +			goto error;
>> +		}
>> +		fs->qinfo[USRQUOTA].flags = 0;
>> +		fs->qinfo[USRQUOTA].qi_info.dqi_syncms = OCFS2_DEF_QUOTA_SYNC;
>> +		fs->qinfo[USRQUOTA].qi_info.dqi_bgrace = OCFS2_DEF_BLOCK_GRACE;
>> +		fs->qinfo[USRQUOTA].qi_info.dqi_igrace = OCFS2_DEF_INODE_GRACE;
>> +
>> +		ret = ocfs2_new_quota_hash(&usr_hash);
>> +		if (ret) {
>> +			com_err(s->progname, ret,
>> +				"while creating user quota hash.");
>> +			goto error;
>> +		}
>> +		ret = ocfs2_init_global_quota_file(fs, USRQUOTA);
>> +		if (ret) {
>> +			com_err(s->progname, ret, "while creating global user "
>> +				"quota file");
>> +			goto error;
>> +		}
>> +		ret = ocfs2_init_local_quota_files(fs, USRQUOTA);
>> +		if (ret) {
>> +			com_err(s->progname, ret,
>> +				"while initializing local user quota files");
>> +			goto error;
>> +		}
>> +	}
>> +	if (!feature_skip(s, GROUP_QUOTA_SYSTEM_INODE)) {
>> +		ret = ocfs2_init_fs_quota_info(fs, GRPQUOTA);
>> +		if (ret) {
>> +			com_err(s->progname, ret,
>> +				"while looking up global group quota file");
>> +			goto error;
>> +		}
>> +		fs->qinfo[GRPQUOTA].flags = 0;
>> +		fs->qinfo[GRPQUOTA].qi_info.dqi_syncms = OCFS2_DEF_QUOTA_SYNC;
>> +		fs->qinfo[GRPQUOTA].qi_info.dqi_bgrace = OCFS2_DEF_BLOCK_GRACE;
>> +		fs->qinfo[GRPQUOTA].qi_info.dqi_igrace = OCFS2_DEF_INODE_GRACE;
>> +		ret = ocfs2_new_quota_hash(&usr_hash);
> grp_hash here?
   Good catch! Obviously I didn't check that the group usage was set correctly
:). Thanks for the review.

										Honza

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

^ permalink raw reply	[flat|nested] 32+ messages in thread

* [Ocfs2-devel] [PATCH 3/8] Implement quota functions to libocfs2
  2009-07-28  8:40     ` Jan Kara
@ 2009-07-28  8:43       ` Tao Ma
  2009-07-28 10:08         ` Jan Kara
  0 siblings, 1 reply; 32+ messages in thread
From: Tao Ma @ 2009-07-28  8:43 UTC (permalink / raw)
  To: ocfs2-devel



Jan Kara wrote:
> On Tue 28-07-09 15:55:30, Tao Ma wrote:
>> Hi Jan,
>>
>> Jan Kara wrote:
>>> Signed-off-by: Jan Kara <jack@suse.cz>
>>> ---
>>>  include/ocfs2/ocfs2.h     |   80 +++
>>>  libocfs2/Makefile         |    1 +
>>>  libocfs2/feature_string.c |   18 +
>>>  libocfs2/ocfs2_err.et     |    6 +
>>>  libocfs2/quota.c          | 1208 +++++++++++++++++++++++++++++++++++++++++++++
>>>  5 files changed, 1313 insertions(+), 0 deletions(-)
>>>  create mode 100644 libocfs2/quota.c
>>>
>>> diff --git a/include/ocfs2/ocfs2.h b/include/ocfs2/ocfs2.h
>>> index ac16823..7f136ca 100644
>>> --- a/include/ocfs2/ocfs2.h
>>> +++ b/include/ocfs2/ocfs2.h
>>> @@ -125,16 +125,36 @@
>>>  #define OCFS2_CHB_WAITING	2
>>>  #define OCFS2_CHB_COMPLETE	3
>>>  +/* Flags for global quotafile info */
>>> +#define OCFS2_QF_INFO_DIRTY 1
>>> +/* Should be power of two */
>>> +#define DEFAULT_QUOTA_HASH_SIZE 8192
>>> +#define MAX_QUOTA_HASH_SIZE (1<<21)	/* 16 MB on 64-bit arch*/
>> 16MB is 1<<24?
>   The comment is meant to say that the hash-table will occupy 16 MB on a
> 64-bit architecture where each pointer has 8 bytes... I'll expand the
> comment a bit.
got it.
>>> +	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, 2 * fs->fs_blocksize, 0, &written);
>>> +	if (!err && written != 2 * fs->fs_blocksize) {
>>> +		err = OCFS2_ET_INTERNAL_FAILURE;
>>> +		goto out;
>>> +	}
>> and here in ocfs2_init_global_quota_file you use write_blk which will 
>> calculate ocfs2_checksum_quota_block. So we can use that function also 
>> here?
>   No, we cannot since write_blk() writes to a global quota file. Here we
> need to write to a node-local quota file. Since there's only this place,
> where we write to local quota file, I'd just leave the code as is.
Oh, I see. Thanks for the explanation.

Regards,
Tao

^ permalink raw reply	[flat|nested] 32+ messages in thread

* [Ocfs2-devel] [PATCH 3/8] Implement quota functions to libocfs2
  2009-07-28  8:43       ` Tao Ma
@ 2009-07-28 10:08         ` Jan Kara
  2009-07-28 22:52           ` Joel Becker
  0 siblings, 1 reply; 32+ messages in thread
From: Jan Kara @ 2009-07-28 10:08 UTC (permalink / raw)
  To: ocfs2-devel

On Tue 28-07-09 16:43:54, Tao Ma wrote:
> Jan Kara wrote:
>> On Tue 28-07-09 15:55:30, Tao Ma wrote:
>>> Hi Jan,
>>>
>>> Jan Kara wrote:
>>>> Signed-off-by: Jan Kara <jack@suse.cz>
>>>> ---
>>>>  include/ocfs2/ocfs2.h     |   80 +++
>>>>  libocfs2/Makefile         |    1 +
>>>>  libocfs2/feature_string.c |   18 +
>>>>  libocfs2/ocfs2_err.et     |    6 +
>>>>  libocfs2/quota.c          | 1208 +++++++++++++++++++++++++++++++++++++++++++++
>>>>  5 files changed, 1313 insertions(+), 0 deletions(-)
>>>>  create mode 100644 libocfs2/quota.c
>>>>
>>>> diff --git a/include/ocfs2/ocfs2.h b/include/ocfs2/ocfs2.h
>>>> index ac16823..7f136ca 100644
>>>> --- a/include/ocfs2/ocfs2.h
>>>> +++ b/include/ocfs2/ocfs2.h
>>>> @@ -125,16 +125,36 @@
>>>>  #define OCFS2_CHB_WAITING	2
>>>>  #define OCFS2_CHB_COMPLETE	3
>>>>  +/* Flags for global quotafile info */
>>>> +#define OCFS2_QF_INFO_DIRTY 1
>>>> +/* Should be power of two */
>>>> +#define DEFAULT_QUOTA_HASH_SIZE 8192
>>>> +#define MAX_QUOTA_HASH_SIZE (1<<21)	/* 16 MB on 64-bit arch*/
>>> 16MB is 1<<24?
>>   The comment is meant to say that the hash-table will occupy 16 MB on a
>> 64-bit architecture where each pointer has 8 bytes... I'll expand the
>> comment a bit.
> got it.
>>>> +	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, 2 * fs->fs_blocksize, 0, &written);
>>>> +	if (!err && written != 2 * fs->fs_blocksize) {
>>>> +		err = OCFS2_ET_INTERNAL_FAILURE;
>>>> +		goto out;
>>>> +	}
>>> and here in ocfs2_init_global_quota_file you use write_blk which will 
>>> calculate ocfs2_checksum_quota_block. So we can use that function also 
>>> here?
>>   No, we cannot since write_blk() writes to a global quota file. Here we
>> need to write to a node-local quota file. Since there's only this place,
>> where we write to local quota file, I'd just leave the code as is.
> Oh, I see. Thanks for the explanation.
  Below is an updated patch.

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

From eba8c4001513164c4d823d55976c0aa9a64c9615 Mon Sep 17 00:00:00 2001
From: Jan Kara <jack@suse.cz>
Date: Mon, 27 Jul 2009 19:22:55 +0200
Subject: [PATCH] Implement quota functions to libocfs2

Signed-off-by: Jan Kara <jack@suse.cz>
---
 include/ocfs2/ocfs2.h     |   80 +++
 libocfs2/Makefile         |    1 +
 libocfs2/feature_string.c |   18 +
 libocfs2/ocfs2_err.et     |    6 +
 libocfs2/quota.c          | 1215 +++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 1320 insertions(+), 0 deletions(-)
 create mode 100644 libocfs2/quota.c

diff --git a/include/ocfs2/ocfs2.h b/include/ocfs2/ocfs2.h
index ac16823..7f136ca 100644
--- a/include/ocfs2/ocfs2.h
+++ b/include/ocfs2/ocfs2.h
@@ -125,16 +125,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 +181,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 +194,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 +236,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);
@@ -580,6 +619,47 @@ 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 */
+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 ocfs2_global_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..44d9662
--- /dev/null
+++ b/libocfs2/quota.c
@@ -0,0 +1,1215 @@
+/* -*- 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 ocfs2_global_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, bytes, &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, uint 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, uint 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_blocks(fs->fs_io, fs->fs_blocksize, &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_blocks(fs->fs_io, fs->fs_blocksize, &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, bytes, &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, uint *blk)
+{
+	errcode_t err;
+	char *buf;
+	struct ocfs2_global_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 ocfs2_global_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, uint blk)
+{
+	errcode_t err;
+	struct ocfs2_global_disk_dqdbheader *dh =
+				(struct ocfs2_global_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, uint blk)
+{
+	errcode_t err;
+	char *tmpbuf;
+	struct ocfs2_global_disk_dqdbheader *dh, *tdh;
+	uint nextblk, prevblk;
+
+	err = ocfs2_malloc_block(fs->fs_io, &tmpbuf);
+	if (err)
+		return err;
+	dh = (struct ocfs2_global_disk_dqdbheader *)buf;
+	tdh = (struct ocfs2_global_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, uint blk)
+{
+	errcode_t err;
+	char *tmpbuf;
+	struct ocfs2_global_disk_dqdbheader *tdh, *dh =
+				(struct ocfs2_global_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 ocfs2_global_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,
+					 uint *treeblk, loff_t *off)
+{
+	errcode_t err;
+	unsigned int blk, i;
+	struct ocfs2_global_disk_dqblk *ddquot;
+	struct ocfs2_global_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 ocfs2_global_disk_dqdbheader *)buf;
+	ddquot = (struct ocfs2_global_disk_dqblk *)(buf +
+		 sizeof(struct ocfs2_global_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 ocfs2_global_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,
+				      uint *treeblk, int depth, loff_t *off)
+{
+	char *buf;
+	int newson = 0, newact = 0;
+	u_int32_t *ref;
+	uint 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)
+{
+	uint tmp = OCFS2_GLOBAL_TREE_BLK;
+
+	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,
+					   uint blk)
+{
+	errcode_t err;
+	char *buf;
+	struct ocfs2_global_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 ocfs2_global_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,
+					   uint *blk,
+					   int depth)
+{
+	errcode_t err;
+	char *buf;
+	uint 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 != OCFS2_GLOBAL_TREE_BLK) {
+			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)
+{
+	uint tmp = OCFS2_GLOBAL_TREE_BLK;
+
+	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, uint 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 ocfs2_global_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,
+					 uint 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)
+{
+	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,
+				      OCFS2_GLOBAL_TREE_BLK, 0);
+	if (err)
+		goto bail;
+	*ret_dquot = dquot;
+	return 0;
+bail:
+	ocfs2_free(&dquot);
+	return err;
+}
-- 
1.6.0.2

^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [Ocfs2-devel] [PATCH 6/8] Quota support for mkfs.ocfs2
  2009-07-28  8:43     ` Jan Kara
@ 2009-07-28 10:09       ` Jan Kara
  2009-07-28 23:07         ` Joel Becker
  0 siblings, 1 reply; 32+ messages in thread
From: Jan Kara @ 2009-07-28 10:09 UTC (permalink / raw)
  To: ocfs2-devel

On Tue 28-07-09 10:43:28, Jan Kara wrote:
>   Hi,
> On Tue 28-07-09 16:11:01, Tao Ma wrote:
> > Jan Kara wrote:
> >> Signed-off-by: Jan Kara <jack@suse.cz>
> >> ---
> >>  mkfs.ocfs2/mkfs.c          |  146 +++++++++++++++++++++++++++++++++++++++++++-
> >>  mkfs.ocfs2/mkfs.h          |    1 +
> >>  mkfs.ocfs2/mkfs.ocfs2.8.in |   16 +++++
> >>  3 files changed, 162 insertions(+), 1 deletions(-)
> >>
> >> diff --git a/mkfs.ocfs2/mkfs.c b/mkfs.ocfs2/mkfs.c
> >> index 6acc6e6..8496d03 100644
> >> --- a/mkfs.ocfs2/mkfs.c
> >> +++ b/mkfs.ocfs2/mkfs.c
> >> +static void format_quota_files(State *s, ocfs2_filesys *fs)
> >> +{
> >> +	errcode_t ret;
> >> +	ocfs2_quota_hash *usr_hash = NULL, *grp_hash = NULL;
> >> +
> >> +	/* Write correct data into quota files */
> >> +	if (!feature_skip(s, USER_QUOTA_SYSTEM_INODE)) {
> >> +		ret = ocfs2_init_fs_quota_info(fs, USRQUOTA);
> >> +		if (ret) {
> >> +			com_err(s->progname, ret,
> >> +				"while looking up global user quota file");
> >> +			goto error;
> >> +		}
> >> +		fs->qinfo[USRQUOTA].flags = 0;
> >> +		fs->qinfo[USRQUOTA].qi_info.dqi_syncms = OCFS2_DEF_QUOTA_SYNC;
> >> +		fs->qinfo[USRQUOTA].qi_info.dqi_bgrace = OCFS2_DEF_BLOCK_GRACE;
> >> +		fs->qinfo[USRQUOTA].qi_info.dqi_igrace = OCFS2_DEF_INODE_GRACE;
> >> +
> >> +		ret = ocfs2_new_quota_hash(&usr_hash);
> >> +		if (ret) {
> >> +			com_err(s->progname, ret,
> >> +				"while creating user quota hash.");
> >> +			goto error;
> >> +		}
> >> +		ret = ocfs2_init_global_quota_file(fs, USRQUOTA);
> >> +		if (ret) {
> >> +			com_err(s->progname, ret, "while creating global user "
> >> +				"quota file");
> >> +			goto error;
> >> +		}
> >> +		ret = ocfs2_init_local_quota_files(fs, USRQUOTA);
> >> +		if (ret) {
> >> +			com_err(s->progname, ret,
> >> +				"while initializing local user quota files");
> >> +			goto error;
> >> +		}
> >> +	}
> >> +	if (!feature_skip(s, GROUP_QUOTA_SYSTEM_INODE)) {
> >> +		ret = ocfs2_init_fs_quota_info(fs, GRPQUOTA);
> >> +		if (ret) {
> >> +			com_err(s->progname, ret,
> >> +				"while looking up global group quota file");
> >> +			goto error;
> >> +		}
> >> +		fs->qinfo[GRPQUOTA].flags = 0;
> >> +		fs->qinfo[GRPQUOTA].qi_info.dqi_syncms = OCFS2_DEF_QUOTA_SYNC;
> >> +		fs->qinfo[GRPQUOTA].qi_info.dqi_bgrace = OCFS2_DEF_BLOCK_GRACE;
> >> +		fs->qinfo[GRPQUOTA].qi_info.dqi_igrace = OCFS2_DEF_INODE_GRACE;
> >> +		ret = ocfs2_new_quota_hash(&usr_hash);
> > grp_hash here?
>    Good catch! Obviously I didn't check that the group usage was set correctly
> :). Thanks for the review.
  Below is an updated patch.

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

From 148eb11dc3e1de1907bb6069b8d976451a5ff54a Mon Sep 17 00:00:00 2001
From: Jan Kara <jack@suse.cz>
Date: Mon, 27 Jul 2009 19:36:22 +0200
Subject: [PATCH 06/10] Quota support for mkfs.ocfs2

Signed-off-by: Jan Kara <jack@suse.cz>
---
 mkfs.ocfs2/mkfs.c          |  146 +++++++++++++++++++++++++++++++++++++++++++-
 mkfs.ocfs2/mkfs.h          |    1 +
 mkfs.ocfs2/mkfs.ocfs2.8.in |   16 +++++
 3 files changed, 162 insertions(+), 1 deletions(-)

diff --git a/mkfs.ocfs2/mkfs.c b/mkfs.ocfs2/mkfs.c
index 6acc6e6..031b8a6 100644
--- a/mkfs.ocfs2/mkfs.c
+++ b/mkfs.ocfs2/mkfs.c
@@ -98,12 +98,16 @@ static SystemFileInfo system_files[] = {
 	{ "slot_map", SFI_OTHER, 1, S_IFREG | 0644 },
 	{ "heartbeat", SFI_HEARTBEAT, 1, S_IFREG | 0644 },
 	{ "global_bitmap", SFI_CLUSTER, 1, S_IFREG | 0644 },
+	{ "aquota.user", SFI_QUOTA, 1, S_IFREG | 0644 },
+	{ "aquota.group", SFI_QUOTA, 1, S_IFREG | 0644 },
 	{ "orphan_dir:%04d", SFI_OTHER, 0, S_IFDIR | 0755 },
 	{ "extent_alloc:%04d", SFI_CHAIN, 0, S_IFREG | 0644 },
 	{ "inode_alloc:%04d", SFI_CHAIN, 0, S_IFREG | 0644 },
 	{ "journal:%04d", SFI_JOURNAL, 0, S_IFREG | 0644 },
 	{ "local_alloc:%04d", SFI_LOCAL_ALLOC, 0, S_IFREG | 0644 },
-	{ "truncate_log:%04d", SFI_TRUNCATE_LOG, 0, S_IFREG | 0644 }
+	{ "truncate_log:%04d", SFI_TRUNCATE_LOG, 0, S_IFREG | 0644 },
+	{ "aquota.user:%04d", SFI_QUOTA, 0, S_IFREG | 0644 },
+	{ "aquota.group:%04d", SFI_QUOTA, 0, S_IFREG | 0644 },
 };
 
 struct fs_type_translation {
@@ -226,6 +230,23 @@ static void mkfs_init_dir_trailer(State *s, DirData *dir, void *buf)
 	}
 }
 
+/* Should we skip this inode because of features enabled / disabled? */
+static int feature_skip(State *s, int system_inode)
+{
+	switch (system_inode) {
+		case USER_QUOTA_SYSTEM_INODE:
+		case LOCAL_USER_QUOTA_SYSTEM_INODE:
+			return !(s->feature_flags.opt_ro_compat &
+					OCFS2_FEATURE_RO_COMPAT_USRQUOTA);
+		case GROUP_QUOTA_SYSTEM_INODE:
+		case LOCAL_GROUP_QUOTA_SYSTEM_INODE:
+			return !(s->feature_flags.opt_ro_compat &
+					OCFS2_FEATURE_RO_COMPAT_GRPQUOTA);
+		default:
+			return 0;
+	}
+}
+
 static inline uint32_t system_dir_bytes_needed(State *s)
 {
 	int each = OCFS2_DIR_REC_LEN(SYSTEM_FILE_NAME_MAX);
@@ -233,6 +254,114 @@ static inline uint32_t system_dir_bytes_needed(State *s)
 	return each * sys_blocks_needed(s->initial_slots);
 }
 
+static void format_quota_files(State *s, ocfs2_filesys *fs)
+{
+	errcode_t ret;
+	ocfs2_quota_hash *usr_hash = NULL, *grp_hash = NULL;
+
+	/* Write correct data into quota files */
+	if (!feature_skip(s, USER_QUOTA_SYSTEM_INODE)) {
+		ret = ocfs2_init_fs_quota_info(fs, USRQUOTA);
+		if (ret) {
+			com_err(s->progname, ret,
+				"while looking up global user quota file");
+			goto error;
+		}
+		fs->qinfo[USRQUOTA].flags = 0;
+		fs->qinfo[USRQUOTA].qi_info.dqi_syncms = OCFS2_DEF_QUOTA_SYNC;
+		fs->qinfo[USRQUOTA].qi_info.dqi_bgrace = OCFS2_DEF_BLOCK_GRACE;
+		fs->qinfo[USRQUOTA].qi_info.dqi_igrace = OCFS2_DEF_INODE_GRACE;
+
+		ret = ocfs2_new_quota_hash(&usr_hash);
+		if (ret) {
+			com_err(s->progname, ret,
+				"while creating user quota hash.");
+			goto error;
+		}
+		ret = ocfs2_init_global_quota_file(fs, USRQUOTA);
+		if (ret) {
+			com_err(s->progname, ret, "while creating global user "
+				"quota file");
+			goto error;
+		}
+		ret = ocfs2_init_local_quota_files(fs, USRQUOTA);
+		if (ret) {
+			com_err(s->progname, ret,
+				"while initializing local user quota files");
+			goto error;
+		}
+	}
+	if (!feature_skip(s, GROUP_QUOTA_SYSTEM_INODE)) {
+		ret = ocfs2_init_fs_quota_info(fs, GRPQUOTA);
+		if (ret) {
+			com_err(s->progname, ret,
+				"while looking up global group quota file");
+			goto error;
+		}
+		fs->qinfo[GRPQUOTA].flags = 0;
+		fs->qinfo[GRPQUOTA].qi_info.dqi_syncms = OCFS2_DEF_QUOTA_SYNC;
+		fs->qinfo[GRPQUOTA].qi_info.dqi_bgrace = OCFS2_DEF_BLOCK_GRACE;
+		fs->qinfo[GRPQUOTA].qi_info.dqi_igrace = OCFS2_DEF_INODE_GRACE;
+		ret = ocfs2_new_quota_hash(&grp_hash);
+		if (ret) {
+			com_err(s->progname, ret,
+				"while creating group quota hash.");
+			goto error;
+		}
+		ret = ocfs2_init_global_quota_file(fs, GRPQUOTA);
+		if (ret) {
+			com_err(s->progname, ret, "while creating global group "
+				"quota file");
+			goto error;
+		}
+
+		ret = ocfs2_init_local_quota_files(fs, GRPQUOTA);
+		if (ret) {
+			com_err(s->progname, ret,
+				"while initializing local group quota files");
+			goto error;
+		}
+	}
+
+	ret = ocfs2_compute_quota_usage(fs, usr_hash, grp_hash);
+	if (ret) {
+		com_err(s->progname, ret, "while computing quota usage");
+		goto error;
+	}
+	if (usr_hash) {
+		ret = ocfs2_write_release_dquots(fs, USRQUOTA, usr_hash);
+		if (ret) {
+			com_err(s->progname, ret,
+				"while writing user quota usage");
+			goto error;
+		}
+		ret = ocfs2_free_quota_hash(usr_hash);
+		if (ret) {
+			com_err(s->progname, ret,
+				"while releasing user quota hash");
+			goto error;
+		}
+	}
+	if (grp_hash) {
+		ret = ocfs2_write_release_dquots(fs, GRPQUOTA, grp_hash);
+		if (ret) {
+			com_err(s->progname, ret,
+				"while writing group quota usage");
+			goto error;
+		}
+		ret = ocfs2_free_quota_hash(grp_hash);
+		if (ret) {
+			com_err(s->progname, ret,
+				"while releasing group quota hash");
+			goto error;
+		}
+	}
+	return;
+error:
+	clear_both_ends(s);
+	exit(1);
+}
+
 static void finish_normal_format(State *s)
 {
 	errcode_t ret;
@@ -304,6 +433,14 @@ static void finish_normal_format(State *s)
 	if (!s->quiet)
 		printf("done\n");
 
+	if (!s->quiet)
+		printf("Formatting quota files: ");
+
+	format_quota_files(s, fs);
+
+	if (!s->quiet)
+		printf("done\n");
+
 	ocfs2_close(fs);
 }
 
@@ -471,6 +608,8 @@ main(int argc, char **argv)
 	for (i = 0; i < NUM_SYSTEM_INODES; i++) {
 		if (hb_dev_skip(s, i))
 			continue;
+		if (feature_skip(s, i))
+			continue;
 
 		num = (system_files[i].global) ? 1 : s->initial_slots;
 		for (j = 0; j < num; j++) {
@@ -529,6 +668,8 @@ main(int argc, char **argv)
 	for (i = 0; i < NUM_SYSTEM_INODES; i++) {
 		if (hb_dev_skip(s, i))
 			continue;
+		if (feature_skip(s, i))
+			continue;
 
 		num = system_files[i].global ? 1 : s->initial_slots;
 		for (j = 0; j < num; j++) {
@@ -2432,6 +2573,9 @@ init_record(State *s, SystemFileDiskRecord *rec, int type, int mode)
 	case SFI_TRUNCATE_LOG:
 		rec->flags |= OCFS2_DEALLOC_FL;
 		break;
+	case SFI_QUOTA:
+		rec->flags |= OCFS2_QUOTA_FL;
+		break;
 	case SFI_OTHER:
 		break;
 	}
diff --git a/mkfs.ocfs2/mkfs.h b/mkfs.ocfs2/mkfs.h
index bd8ac45..969e4df 100644
--- a/mkfs.ocfs2/mkfs.h
+++ b/mkfs.ocfs2/mkfs.h
@@ -96,6 +96,7 @@ enum {
 	SFI_HEARTBEAT,
 	SFI_CHAIN,
 	SFI_TRUNCATE_LOG,
+	SFI_QUOTA,
 	SFI_OTHER
 };
 
diff --git a/mkfs.ocfs2/mkfs.ocfs2.8.in b/mkfs.ocfs2/mkfs.ocfs2.8.in
index 38433ee..d1f4011 100644
--- a/mkfs.ocfs2/mkfs.ocfs2.8.in
+++ b/mkfs.ocfs2/mkfs.ocfs2.8.in
@@ -146,6 +146,22 @@ arbitrary binary data. Attributes can be attached to all types of inodes: regula
 symbolic links, device nodes, etc. This feature is required for users wanting to use extended security
 facilities like POSIX ACLs or SELinux.
 .RE
+.RS 1.2i
+.TP
+\fBusrquota\fR
+Enable user quota support. With this feature enabled, filesystem will track amount of space
+and number of inodes (files, directories, symbolic links) each user owns. It is then possible
+to limit the maximum amount of space or inodes user can have. See a documentation of
+quota-tools package for more details.
+.RE
+.RS 1.2i
+.TP
+\fBgrpquota\fR
+Enable group quota support. With this feature enabled, filesystem will track amount of space
+and number of inodes (files, directories, symbolic links) each group owns. It is then possible
+to limit the maximum amount of space or inodes user can have. See a documentation of
+quota-tools package for more details.
+.RE
 
 .TP
 \fB\-\-fs\-feature\-level=\fR\fR\fIfeature\-level\fR
-- 
1.6.0.2

^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [Ocfs2-devel] [PATCH 1/8] Update ocfs2_fs.h to contain all necessary quota structures and constants
  2009-07-27 17:53 ` [Ocfs2-devel] [PATCH 1/8] Update ocfs2_fs.h to contain all necessary quota structures and constants Jan Kara
@ 2009-07-28 21:45   ` Joel Becker
  2009-07-29  9:42     ` Jan Kara
  0 siblings, 1 reply; 32+ messages in thread
From: Joel Becker @ 2009-07-28 21:45 UTC (permalink / raw)
  To: ocfs2-devel

On Mon, Jul 27, 2009 at 07:53:29PM +0200, Jan Kara wrote:
> diff --git a/include/ocfs2-kernel/ocfs2_fs.h b/include/ocfs2-kernel/ocfs2_fs.h
> index 2140bb3..ad655dc 100644
> --- a/include/ocfs2-kernel/ocfs2_fs.h
> +++ b/include/ocfs2-kernel/ocfs2_fs.h
> @@ -344,6 +344,8 @@ enum {
>  #define OCFS2_FIRST_ONLINE_SYSTEM_INODE SLOT_MAP_SYSTEM_INODE
>  	HEARTBEAT_SYSTEM_INODE,
>  	GLOBAL_BITMAP_SYSTEM_INODE,
> +	USER_QUOTA_SYSTEM_INODE,
> +	GROUP_QUOTA_SYSTEM_INODE,
>  #define OCFS2_LAST_GLOBAL_SYSTEM_INODE GLOBAL_BITMAP_SYSTEM_INODE

	You need to redefine OCFS2_LAST_GLOBAL_SYSTEM_INODE like it is
in the kernel.

>  #define OCFS2_GLOBAL_INFO_OFF (sizeof(struct ocfs2_disk_dqheader))
> +#define OCFS2_GLOBAL_TREE_BLK 1

	I don't see GLOBAL_TREE_BLK in the kernel.

>  /* Information header of global quota file (immediately follows the generic
>   * header) */
> @@ -977,6 +986,16 @@ struct ocfs2_global_disk_dqinfo {
>  				 * file */
>  };
>  
> +/* Header of leaf tree block */
> +struct ocfs2_global_disk_dqdbheader {
> +	__le32 dqdh_next_free;	/* Number of next block with free entry */
> +	__le32 dqdh_prev_free;	/* Number of previous block with free entry */
> +	__le16 dqdh_entries;	/* Number of valid entries in block */
> +	__le16 dqdh_pad1;
> +	__le32 dqdh_pad2;
> +};

	Nor do I see ocfs2_global_disk_dqdbheader.

> +static inline int ocfs2_global_dqstr_in_blk(int blocksize)
> +{
> +	return (blocksize - OCFS2_QBLK_RESERVED_SPACE -
> +		sizeof(struct ocfs2_global_disk_dqdbheader)) /
> +		sizeof(struct ocfs2_global_disk_dqblk);
> +}
> +

	Nor ocfs2_global_dqstr_in_blk.

Joel

-- 

"Not being known doesn't stop the truth from being true."
        - Richard Bach

Joel Becker
Principal Software Developer
Oracle
E-mail: joel.becker at oracle.com
Phone: (650) 506-8127

^ permalink raw reply	[flat|nested] 32+ messages in thread

* [Ocfs2-devel] [PATCH 2/8] Provide ocfs2_cached_inode_extend_allocation()
  2009-07-27 17:53 ` [Ocfs2-devel] [PATCH 2/8] Provide ocfs2_cached_inode_extend_allocation() Jan Kara
@ 2009-07-28 21:47   ` Joel Becker
  2009-07-29  9:46     ` Jan Kara
  0 siblings, 1 reply; 32+ messages in thread
From: Joel Becker @ 2009-07-28 21:47 UTC (permalink / raw)
  To: ocfs2-devel

On Mon, Jul 27, 2009 at 07:53:30PM +0200, Jan Kara wrote:
> So far we had only ocfs2_extend_allocation() which read the inode from
> disk. Provide also the cached variant.
> 
> Signed-off-by: Jan Kara <jack@suse.cz>
> ---
>  include/ocfs2/ocfs2.h  |    2 +
>  libocfs2/extend_file.c |   51 ++++++++++++++++++++++++++---------------------
>  2 files changed, 30 insertions(+), 23 deletions(-)
> 
> diff --git a/include/ocfs2/ocfs2.h b/include/ocfs2/ocfs2.h
> index 4dbbf43..ac16823 100644
> --- a/include/ocfs2/ocfs2.h
> +++ b/include/ocfs2/ocfs2.h
> @@ -505,6 +505,8 @@ errcode_t ocfs2_delete_extent_block(ocfs2_filesys *fs, uint64_t blkno);
>   */
>  errcode_t ocfs2_extend_allocation(ocfs2_filesys *fs, uint64_t ino,
>  				  uint32_t new_clusters);
> +/* Ditto for cached inode */
> +errcode_t ocfs2_cached_inode_extend_allocation(ocfs2_cached_inode *ci, uint32_t new_clusters);

	Break the long line, but otherwise this patch looks good.  I'm
assuming you want to work on cached inodes later.

Joel

-- 

Life's Little Instruction Book #313

	"Never underestimate the power of love."

Joel Becker
Principal Software Developer
Oracle
E-mail: joel.becker at oracle.com
Phone: (650) 506-8127

^ permalink raw reply	[flat|nested] 32+ messages in thread

* [Ocfs2-devel] [PATCH 3/8] Implement quota functions to libocfs2
  2009-07-28 10:08         ` Jan Kara
@ 2009-07-28 22:52           ` Joel Becker
  2009-07-29  9:57             ` Jan Kara
  0 siblings, 1 reply; 32+ messages in thread
From: Joel Becker @ 2009-07-28 22:52 UTC (permalink / raw)
  To: ocfs2-devel

On Tue, Jul 28, 2009 at 12:08:29PM +0200, Jan Kara wrote:
> +/* Read given block */
> +static errcode_t read_blk(ocfs2_filesys *fs, int type, uint blk, char *buf)

	You're sure quota files will never be larger than a uint?  Also,
we tend to do 'unsigned int', not 'uint'.

Joel

-- 

"When I am working on a problem I never think about beauty. I
 only think about how to solve the problem. But when I have finished, if
 the solution is not beautiful, I know it is wrong."
         - Buckminster Fuller

Joel Becker
Principal Software Developer
Oracle
E-mail: joel.becker at oracle.com
Phone: (650) 506-8127

^ permalink raw reply	[flat|nested] 32+ messages in thread

* [Ocfs2-devel] [PATCH 4/8] Write out quota info changes on ocfs2_close()
  2009-07-27 17:53 ` [Ocfs2-devel] [PATCH 4/8] Write out quota info changes on ocfs2_close() Jan Kara
@ 2009-07-28 22:53   ` Joel Becker
  2009-07-29  9:58     ` Jan Kara
  0 siblings, 1 reply; 32+ messages in thread
From: Joel Becker @ 2009-07-28 22:53 UTC (permalink / raw)
  To: ocfs2-devel

On Mon, Jul 27, 2009 at 07:53:32PM +0200, Jan Kara wrote:
> We don't write out change of information in quota file header on each change,
> we rather cache it in ocfs2_filesys structure. So write out all the
> information when ocfs2_close() is called.
> 
> Signed-off-by: Jan Kara <jack@suse.cz>

This looks good as long as all quota ops keep those ocfs2_cached_inodes
up to date.

Joel

-- 

"In the beginning, the universe was created. This has made a lot 
 of people very angry, and is generally considered to have been a 
 bad move."
        - Douglas Adams

Joel Becker
Principal Software Developer
Oracle
E-mail: joel.becker at oracle.com
Phone: (650) 506-8127

^ permalink raw reply	[flat|nested] 32+ messages in thread

* [Ocfs2-devel] [PATCH 5/8] Quota support for fsck.ocfs2
  2009-07-27 17:53 ` [Ocfs2-devel] [PATCH 5/8] Quota support for fsck.ocfs2 Jan Kara
@ 2009-07-28 23:05   ` Joel Becker
  2009-07-29 10:07     ` Jan Kara
  0 siblings, 1 reply; 32+ messages in thread
From: Joel Becker @ 2009-07-28 23:05 UTC (permalink / raw)
  To: ocfs2-devel

On Mon, Jul 27, 2009 at 07:53:33PM +0200, Jan Kara wrote:
> diff --git a/fsck.ocfs2/include/pass5.h b/fsck.ocfs2/include/pass5.h
> new file mode 100644
> index 0000000..dfd27cd
> --- /dev/null
> +++ b/fsck.ocfs2/include/pass5.h
> @@ -0,0 +1,32 @@
> +/*
> + * pass5.h
> + *
> + * Copyright (C) 2009 Novell Corporation.  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 as published by the Free Software Foundation; either
> + * version 2 of the License, or (at your option) any later version.

	We generally use GPL version 2 only, but you are welcome to
license this 2-or-later.  I only mention this because...

> + * 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.

	Can you check http://kernel.us.oracle.com/~smushran/license.txt
and update your header?  The last paragraph is no longer recommended by
the FSF, and we'd love for this header to have the emacs/vim lines at
the top.

> + *
> + * Author: Jan Kara
> + */

	Also, we deliberately drop author attribution from individual
source files.  You can add yourself to MAINTAINERS.

> +errcode_t o2fsck_pass5(o2fsck_state *ost)
> +{
> +	errcode_t ret;
> +	ocfs2_filesys *fs = ost->ost_fs;
> +	struct ocfs2_super_block *super = OCFS2_RAW_SB(fs->fs_super);
> +	int has_usrquota, has_grpquota;
> +
> +	has_usrquota = OCFS2_HAS_RO_COMPAT_FEATURE(super,
> +				OCFS2_FEATURE_RO_COMPAT_USRQUOTA);
> +	has_grpquota = OCFS2_HAS_RO_COMPAT_FEATURE(super,
> +				OCFS2_FEATURE_RO_COMPAT_GRPQUOTA);
> +	/* Nothing to check? */
> +	if (!has_usrquota && !has_grpquota)
> +		return 0;
> +	printf("Pass 5: Checking quota information.\n");
> +	if (has_usrquota) {
> +		ret = ocfs2_new_quota_hash(qhash + USRQUOTA);
> +		if (ret) {
> +			com_err(whoami, ret,
> +				"while allocating user quota hash");
> +			goto out;
> +		}
> +		ret = load_quota_file(ost, USRQUOTA);
> +		if (ret)
> +			goto out;
> +	}
> +	if (has_grpquota) {
> +		ret = ocfs2_new_quota_hash(qhash + GRPQUOTA);
> +		if (ret) {
> +			com_err(whoami, ret,
> +				"while allocating group quota hash");
> +			goto out;
> +		}
> +		ret = load_quota_file(ost, GRPQUOTA);
> +		if (ret)
> +			goto out;
> +	}
> +	ret = ocfs2_compute_quota_usage(fs, qhash[USRQUOTA], qhash[GRPQUOTA]);
> +	if (ret) {
> +		com_err(whoami, ret, "while computing quota usage");
> +		goto out;
> +	}
> +	if (has_usrquota) {
> +		ret = recreate_quota_files(fs, USRQUOTA);
> +		if (ret)
> +			goto out;
> +		ret = ocfs2_free_quota_hash(qhash[USRQUOTA]);
> +		if (ret) {
> +			com_err(whoami, ret, "while release user quota hash");
> +			goto out;
> +		}
> +	}
> +	if (has_grpquota) {
> +		ret = recreate_quota_files(fs, GRPQUOTA);
> +		if (ret)
> +			goto out;
> +		ret = ocfs2_free_quota_hash(qhash[GRPQUOTA]);
> +		if (ret) {
> +			com_err(whoami, ret, "while release group quota hash");
> +			goto out;
> +		}
> +	}

	The way I read this, you check the existing quota structures,
and then ignore them while recomputing the usage.  You just do a full-on
recompute.  Then you write out the newly computed data.  Am I correct?

Joel

-- 

You can use a screwdriver to screw in screws or to clean your ears,
however, the latter needs real skill, determination and a lack of fear
of injuring yourself.  It is much the same with JavaScript.
	- Chris Heilmann

Joel Becker
Principal Software Developer
Oracle
E-mail: joel.becker at oracle.com
Phone: (650) 506-8127

^ permalink raw reply	[flat|nested] 32+ messages in thread

* [Ocfs2-devel] [PATCH 6/8] Quota support for mkfs.ocfs2
  2009-07-28 10:09       ` Jan Kara
@ 2009-07-28 23:07         ` Joel Becker
  0 siblings, 0 replies; 32+ messages in thread
From: Joel Becker @ 2009-07-28 23:07 UTC (permalink / raw)
  To: ocfs2-devel

Looks good to me.

On Tue, Jul 28, 2009 at 12:09:51PM +0200, Jan Kara wrote:
> On Tue 28-07-09 10:43:28, Jan Kara wrote:
> >   Hi,
> > On Tue 28-07-09 16:11:01, Tao Ma wrote:
> > > Jan Kara wrote:
> > >> Signed-off-by: Jan Kara <jack@suse.cz>
> > >> ---
> > >>  mkfs.ocfs2/mkfs.c          |  146 +++++++++++++++++++++++++++++++++++++++++++-
> > >>  mkfs.ocfs2/mkfs.h          |    1 +
> > >>  mkfs.ocfs2/mkfs.ocfs2.8.in |   16 +++++
> > >>  3 files changed, 162 insertions(+), 1 deletions(-)
> > >>
> > >> diff --git a/mkfs.ocfs2/mkfs.c b/mkfs.ocfs2/mkfs.c
> > >> index 6acc6e6..8496d03 100644
> > >> --- a/mkfs.ocfs2/mkfs.c
> > >> +++ b/mkfs.ocfs2/mkfs.c
> > >> +static void format_quota_files(State *s, ocfs2_filesys *fs)
> > >> +{
> > >> +	errcode_t ret;
> > >> +	ocfs2_quota_hash *usr_hash = NULL, *grp_hash = NULL;
> > >> +
> > >> +	/* Write correct data into quota files */
> > >> +	if (!feature_skip(s, USER_QUOTA_SYSTEM_INODE)) {
> > >> +		ret = ocfs2_init_fs_quota_info(fs, USRQUOTA);
> > >> +		if (ret) {
> > >> +			com_err(s->progname, ret,
> > >> +				"while looking up global user quota file");
> > >> +			goto error;
> > >> +		}
> > >> +		fs->qinfo[USRQUOTA].flags = 0;
> > >> +		fs->qinfo[USRQUOTA].qi_info.dqi_syncms = OCFS2_DEF_QUOTA_SYNC;
> > >> +		fs->qinfo[USRQUOTA].qi_info.dqi_bgrace = OCFS2_DEF_BLOCK_GRACE;
> > >> +		fs->qinfo[USRQUOTA].qi_info.dqi_igrace = OCFS2_DEF_INODE_GRACE;
> > >> +
> > >> +		ret = ocfs2_new_quota_hash(&usr_hash);
> > >> +		if (ret) {
> > >> +			com_err(s->progname, ret,
> > >> +				"while creating user quota hash.");
> > >> +			goto error;
> > >> +		}
> > >> +		ret = ocfs2_init_global_quota_file(fs, USRQUOTA);
> > >> +		if (ret) {
> > >> +			com_err(s->progname, ret, "while creating global user "
> > >> +				"quota file");
> > >> +			goto error;
> > >> +		}
> > >> +		ret = ocfs2_init_local_quota_files(fs, USRQUOTA);
> > >> +		if (ret) {
> > >> +			com_err(s->progname, ret,
> > >> +				"while initializing local user quota files");
> > >> +			goto error;
> > >> +		}
> > >> +	}
> > >> +	if (!feature_skip(s, GROUP_QUOTA_SYSTEM_INODE)) {
> > >> +		ret = ocfs2_init_fs_quota_info(fs, GRPQUOTA);
> > >> +		if (ret) {
> > >> +			com_err(s->progname, ret,
> > >> +				"while looking up global group quota file");
> > >> +			goto error;
> > >> +		}
> > >> +		fs->qinfo[GRPQUOTA].flags = 0;
> > >> +		fs->qinfo[GRPQUOTA].qi_info.dqi_syncms = OCFS2_DEF_QUOTA_SYNC;
> > >> +		fs->qinfo[GRPQUOTA].qi_info.dqi_bgrace = OCFS2_DEF_BLOCK_GRACE;
> > >> +		fs->qinfo[GRPQUOTA].qi_info.dqi_igrace = OCFS2_DEF_INODE_GRACE;
> > >> +		ret = ocfs2_new_quota_hash(&usr_hash);
> > > grp_hash here?
> >    Good catch! Obviously I didn't check that the group usage was set correctly
> > :). Thanks for the review.
>   Below is an updated patch.
> 
> -- 
> Jan Kara <jack@suse.cz>
> SUSE Labs, CR
> ---
> 
> >From 148eb11dc3e1de1907bb6069b8d976451a5ff54a Mon Sep 17 00:00:00 2001
> From: Jan Kara <jack@suse.cz>
> Date: Mon, 27 Jul 2009 19:36:22 +0200
> Subject: [PATCH 06/10] Quota support for mkfs.ocfs2
> 
> Signed-off-by: Jan Kara <jack@suse.cz>
> ---
>  mkfs.ocfs2/mkfs.c          |  146 +++++++++++++++++++++++++++++++++++++++++++-
>  mkfs.ocfs2/mkfs.h          |    1 +
>  mkfs.ocfs2/mkfs.ocfs2.8.in |   16 +++++
>  3 files changed, 162 insertions(+), 1 deletions(-)
> 
> diff --git a/mkfs.ocfs2/mkfs.c b/mkfs.ocfs2/mkfs.c
> index 6acc6e6..031b8a6 100644
> --- a/mkfs.ocfs2/mkfs.c
> +++ b/mkfs.ocfs2/mkfs.c
> @@ -98,12 +98,16 @@ static SystemFileInfo system_files[] = {
>  	{ "slot_map", SFI_OTHER, 1, S_IFREG | 0644 },
>  	{ "heartbeat", SFI_HEARTBEAT, 1, S_IFREG | 0644 },
>  	{ "global_bitmap", SFI_CLUSTER, 1, S_IFREG | 0644 },
> +	{ "aquota.user", SFI_QUOTA, 1, S_IFREG | 0644 },
> +	{ "aquota.group", SFI_QUOTA, 1, S_IFREG | 0644 },
>  	{ "orphan_dir:%04d", SFI_OTHER, 0, S_IFDIR | 0755 },
>  	{ "extent_alloc:%04d", SFI_CHAIN, 0, S_IFREG | 0644 },
>  	{ "inode_alloc:%04d", SFI_CHAIN, 0, S_IFREG | 0644 },
>  	{ "journal:%04d", SFI_JOURNAL, 0, S_IFREG | 0644 },
>  	{ "local_alloc:%04d", SFI_LOCAL_ALLOC, 0, S_IFREG | 0644 },
> -	{ "truncate_log:%04d", SFI_TRUNCATE_LOG, 0, S_IFREG | 0644 }
> +	{ "truncate_log:%04d", SFI_TRUNCATE_LOG, 0, S_IFREG | 0644 },
> +	{ "aquota.user:%04d", SFI_QUOTA, 0, S_IFREG | 0644 },
> +	{ "aquota.group:%04d", SFI_QUOTA, 0, S_IFREG | 0644 },
>  };
>  
>  struct fs_type_translation {
> @@ -226,6 +230,23 @@ static void mkfs_init_dir_trailer(State *s, DirData *dir, void *buf)
>  	}
>  }
>  
> +/* Should we skip this inode because of features enabled / disabled? */
> +static int feature_skip(State *s, int system_inode)
> +{
> +	switch (system_inode) {
> +		case USER_QUOTA_SYSTEM_INODE:
> +		case LOCAL_USER_QUOTA_SYSTEM_INODE:
> +			return !(s->feature_flags.opt_ro_compat &
> +					OCFS2_FEATURE_RO_COMPAT_USRQUOTA);
> +		case GROUP_QUOTA_SYSTEM_INODE:
> +		case LOCAL_GROUP_QUOTA_SYSTEM_INODE:
> +			return !(s->feature_flags.opt_ro_compat &
> +					OCFS2_FEATURE_RO_COMPAT_GRPQUOTA);
> +		default:
> +			return 0;
> +	}
> +}
> +
>  static inline uint32_t system_dir_bytes_needed(State *s)
>  {
>  	int each = OCFS2_DIR_REC_LEN(SYSTEM_FILE_NAME_MAX);
> @@ -233,6 +254,114 @@ static inline uint32_t system_dir_bytes_needed(State *s)
>  	return each * sys_blocks_needed(s->initial_slots);
>  }
>  
> +static void format_quota_files(State *s, ocfs2_filesys *fs)
> +{
> +	errcode_t ret;
> +	ocfs2_quota_hash *usr_hash = NULL, *grp_hash = NULL;
> +
> +	/* Write correct data into quota files */
> +	if (!feature_skip(s, USER_QUOTA_SYSTEM_INODE)) {
> +		ret = ocfs2_init_fs_quota_info(fs, USRQUOTA);
> +		if (ret) {
> +			com_err(s->progname, ret,
> +				"while looking up global user quota file");
> +			goto error;
> +		}
> +		fs->qinfo[USRQUOTA].flags = 0;
> +		fs->qinfo[USRQUOTA].qi_info.dqi_syncms = OCFS2_DEF_QUOTA_SYNC;
> +		fs->qinfo[USRQUOTA].qi_info.dqi_bgrace = OCFS2_DEF_BLOCK_GRACE;
> +		fs->qinfo[USRQUOTA].qi_info.dqi_igrace = OCFS2_DEF_INODE_GRACE;
> +
> +		ret = ocfs2_new_quota_hash(&usr_hash);
> +		if (ret) {
> +			com_err(s->progname, ret,
> +				"while creating user quota hash.");
> +			goto error;
> +		}
> +		ret = ocfs2_init_global_quota_file(fs, USRQUOTA);
> +		if (ret) {
> +			com_err(s->progname, ret, "while creating global user "
> +				"quota file");
> +			goto error;
> +		}
> +		ret = ocfs2_init_local_quota_files(fs, USRQUOTA);
> +		if (ret) {
> +			com_err(s->progname, ret,
> +				"while initializing local user quota files");
> +			goto error;
> +		}
> +	}
> +	if (!feature_skip(s, GROUP_QUOTA_SYSTEM_INODE)) {
> +		ret = ocfs2_init_fs_quota_info(fs, GRPQUOTA);
> +		if (ret) {
> +			com_err(s->progname, ret,
> +				"while looking up global group quota file");
> +			goto error;
> +		}
> +		fs->qinfo[GRPQUOTA].flags = 0;
> +		fs->qinfo[GRPQUOTA].qi_info.dqi_syncms = OCFS2_DEF_QUOTA_SYNC;
> +		fs->qinfo[GRPQUOTA].qi_info.dqi_bgrace = OCFS2_DEF_BLOCK_GRACE;
> +		fs->qinfo[GRPQUOTA].qi_info.dqi_igrace = OCFS2_DEF_INODE_GRACE;
> +		ret = ocfs2_new_quota_hash(&grp_hash);
> +		if (ret) {
> +			com_err(s->progname, ret,
> +				"while creating group quota hash.");
> +			goto error;
> +		}
> +		ret = ocfs2_init_global_quota_file(fs, GRPQUOTA);
> +		if (ret) {
> +			com_err(s->progname, ret, "while creating global group "
> +				"quota file");
> +			goto error;
> +		}
> +
> +		ret = ocfs2_init_local_quota_files(fs, GRPQUOTA);
> +		if (ret) {
> +			com_err(s->progname, ret,
> +				"while initializing local group quota files");
> +			goto error;
> +		}
> +	}
> +
> +	ret = ocfs2_compute_quota_usage(fs, usr_hash, grp_hash);
> +	if (ret) {
> +		com_err(s->progname, ret, "while computing quota usage");
> +		goto error;
> +	}
> +	if (usr_hash) {
> +		ret = ocfs2_write_release_dquots(fs, USRQUOTA, usr_hash);
> +		if (ret) {
> +			com_err(s->progname, ret,
> +				"while writing user quota usage");
> +			goto error;
> +		}
> +		ret = ocfs2_free_quota_hash(usr_hash);
> +		if (ret) {
> +			com_err(s->progname, ret,
> +				"while releasing user quota hash");
> +			goto error;
> +		}
> +	}
> +	if (grp_hash) {
> +		ret = ocfs2_write_release_dquots(fs, GRPQUOTA, grp_hash);
> +		if (ret) {
> +			com_err(s->progname, ret,
> +				"while writing group quota usage");
> +			goto error;
> +		}
> +		ret = ocfs2_free_quota_hash(grp_hash);
> +		if (ret) {
> +			com_err(s->progname, ret,
> +				"while releasing group quota hash");
> +			goto error;
> +		}
> +	}
> +	return;
> +error:
> +	clear_both_ends(s);
> +	exit(1);
> +}
> +
>  static void finish_normal_format(State *s)
>  {
>  	errcode_t ret;
> @@ -304,6 +433,14 @@ static void finish_normal_format(State *s)
>  	if (!s->quiet)
>  		printf("done\n");
>  
> +	if (!s->quiet)
> +		printf("Formatting quota files: ");
> +
> +	format_quota_files(s, fs);
> +
> +	if (!s->quiet)
> +		printf("done\n");
> +
>  	ocfs2_close(fs);
>  }
>  
> @@ -471,6 +608,8 @@ main(int argc, char **argv)
>  	for (i = 0; i < NUM_SYSTEM_INODES; i++) {
>  		if (hb_dev_skip(s, i))
>  			continue;
> +		if (feature_skip(s, i))
> +			continue;
>  
>  		num = (system_files[i].global) ? 1 : s->initial_slots;
>  		for (j = 0; j < num; j++) {
> @@ -529,6 +668,8 @@ main(int argc, char **argv)
>  	for (i = 0; i < NUM_SYSTEM_INODES; i++) {
>  		if (hb_dev_skip(s, i))
>  			continue;
> +		if (feature_skip(s, i))
> +			continue;
>  
>  		num = system_files[i].global ? 1 : s->initial_slots;
>  		for (j = 0; j < num; j++) {
> @@ -2432,6 +2573,9 @@ init_record(State *s, SystemFileDiskRecord *rec, int type, int mode)
>  	case SFI_TRUNCATE_LOG:
>  		rec->flags |= OCFS2_DEALLOC_FL;
>  		break;
> +	case SFI_QUOTA:
> +		rec->flags |= OCFS2_QUOTA_FL;
> +		break;
>  	case SFI_OTHER:
>  		break;
>  	}
> diff --git a/mkfs.ocfs2/mkfs.h b/mkfs.ocfs2/mkfs.h
> index bd8ac45..969e4df 100644
> --- a/mkfs.ocfs2/mkfs.h
> +++ b/mkfs.ocfs2/mkfs.h
> @@ -96,6 +96,7 @@ enum {
>  	SFI_HEARTBEAT,
>  	SFI_CHAIN,
>  	SFI_TRUNCATE_LOG,
> +	SFI_QUOTA,
>  	SFI_OTHER
>  };
>  
> diff --git a/mkfs.ocfs2/mkfs.ocfs2.8.in b/mkfs.ocfs2/mkfs.ocfs2.8.in
> index 38433ee..d1f4011 100644
> --- a/mkfs.ocfs2/mkfs.ocfs2.8.in
> +++ b/mkfs.ocfs2/mkfs.ocfs2.8.in
> @@ -146,6 +146,22 @@ arbitrary binary data. Attributes can be attached to all types of inodes: regula
>  symbolic links, device nodes, etc. This feature is required for users wanting to use extended security
>  facilities like POSIX ACLs or SELinux.
>  .RE
> +.RS 1.2i
> +.TP
> +\fBusrquota\fR
> +Enable user quota support. With this feature enabled, filesystem will track amount of space
> +and number of inodes (files, directories, symbolic links) each user owns. It is then possible
> +to limit the maximum amount of space or inodes user can have. See a documentation of
> +quota-tools package for more details.
> +.RE
> +.RS 1.2i
> +.TP
> +\fBgrpquota\fR
> +Enable group quota support. With this feature enabled, filesystem will track amount of space
> +and number of inodes (files, directories, symbolic links) each group owns. It is then possible
> +to limit the maximum amount of space or inodes user can have. See a documentation of
> +quota-tools package for more details.
> +.RE
>  
>  .TP
>  \fB\-\-fs\-feature\-level=\fR\fR\fIfeature\-level\fR
> -- 
> 1.6.0.2
> 

-- 

"Behind every successful man there's a lot of unsuccessful years."
        - Bob Brown

Joel Becker
Principal Software Developer
Oracle
E-mail: joel.becker at oracle.com
Phone: (650) 506-8127

^ permalink raw reply	[flat|nested] 32+ messages in thread

* [Ocfs2-devel] [PATCH 7/8] Add quota support to tunefs.ocfs2
  2009-07-27 17:53 ` [Ocfs2-devel] [PATCH 7/8] Add quota support to tunefs.ocfs2 Jan Kara
@ 2009-07-29  1:04   ` Joel Becker
  0 siblings, 0 replies; 32+ messages in thread
From: Joel Becker @ 2009-07-29  1:04 UTC (permalink / raw)
  To: ocfs2-devel

On Mon, Jul 27, 2009 at 07:53:35PM +0200, Jan Kara wrote:
> Implement setting of quota feature via tunefs.ocfs2 and also properly
> create / delete local quota files when number of slots increases /
> decreases. Implement setting of interval in which we sync changes
> in local quota file to the global quota file.
> 
> Signed-off-by: Jan Kara <jack@suse.cz>

Looks good, I think.


-- 

"Baby, even the losers
 Get luck sometimes.
 Even the losers
 Keep a little bit of pride."

Joel Becker
Principal Software Developer
Oracle
E-mail: joel.becker at oracle.com
Phone: (650) 506-8127

^ permalink raw reply	[flat|nested] 32+ messages in thread

* [Ocfs2-devel] [PATCH 1/8] Update ocfs2_fs.h to contain all necessary quota structures and constants
  2009-07-28 21:45   ` Joel Becker
@ 2009-07-29  9:42     ` Jan Kara
  2009-07-29 18:19       ` Joel Becker
  0 siblings, 1 reply; 32+ messages in thread
From: Jan Kara @ 2009-07-29  9:42 UTC (permalink / raw)
  To: ocfs2-devel

On Tue 28-07-09 14:45:07, Joel Becker wrote:
> On Mon, Jul 27, 2009 at 07:53:29PM +0200, Jan Kara wrote:
> > diff --git a/include/ocfs2-kernel/ocfs2_fs.h b/include/ocfs2-kernel/ocfs2_fs.h
> > index 2140bb3..ad655dc 100644
> > --- a/include/ocfs2-kernel/ocfs2_fs.h
> > +++ b/include/ocfs2-kernel/ocfs2_fs.h
> > @@ -344,6 +344,8 @@ enum {
> >  #define OCFS2_FIRST_ONLINE_SYSTEM_INODE SLOT_MAP_SYSTEM_INODE
> >  	HEARTBEAT_SYSTEM_INODE,
> >  	GLOBAL_BITMAP_SYSTEM_INODE,
> > +	USER_QUOTA_SYSTEM_INODE,
> > +	GROUP_QUOTA_SYSTEM_INODE,
> >  #define OCFS2_LAST_GLOBAL_SYSTEM_INODE GLOBAL_BITMAP_SYSTEM_INODE
> 
> 	You need to redefine OCFS2_LAST_GLOBAL_SYSTEM_INODE like it is
> in the kernel.
  Ah, right. Will fix.

> >  #define OCFS2_GLOBAL_INFO_OFF (sizeof(struct ocfs2_disk_dqheader))
> > +#define OCFS2_GLOBAL_TREE_BLK 1
> 
> 	I don't see GLOBAL_TREE_BLK in the kernel.
  They are in the generic quota header fs/quota/quota_tree.h defining quota
file format. For global quota files we use generic quota code which also
defines some parts of the disk format...

> >  /* Information header of global quota file (immediately follows the generic
> >   * header) */
> > @@ -977,6 +986,16 @@ struct ocfs2_global_disk_dqinfo {
> >  				 * file */
> >  };
> >  
> > +/* Header of leaf tree block */
> > +struct ocfs2_global_disk_dqdbheader {
> > +	__le32 dqdh_next_free;	/* Number of next block with free entry */
> > +	__le32 dqdh_prev_free;	/* Number of previous block with free entry */
> > +	__le16 dqdh_entries;	/* Number of valid entries in block */
> > +	__le16 dqdh_pad1;
> > +	__le32 dqdh_pad2;
> > +};
> 
> 	Nor do I see ocfs2_global_disk_dqdbheader.
> 
> > +static inline int ocfs2_global_dqstr_in_blk(int blocksize)
> > +{
> > +	return (blocksize - OCFS2_QBLK_RESERVED_SPACE -
> > +		sizeof(struct ocfs2_global_disk_dqdbheader)) /
> > +		sizeof(struct ocfs2_global_disk_dqblk);
> > +}
> > +
> 
> 	Nor ocfs2_global_dqstr_in_blk.
  These are the same cases. I can split them into a separate header file if
you like it better. But from the tools point of view, I felt that keeping
all disk-format related things in one header makes sence.

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

^ permalink raw reply	[flat|nested] 32+ messages in thread

* [Ocfs2-devel] [PATCH 2/8] Provide ocfs2_cached_inode_extend_allocation()
  2009-07-28 21:47   ` Joel Becker
@ 2009-07-29  9:46     ` Jan Kara
  0 siblings, 0 replies; 32+ messages in thread
From: Jan Kara @ 2009-07-29  9:46 UTC (permalink / raw)
  To: ocfs2-devel

On Tue 28-07-09 14:47:44, Joel Becker wrote:
> On Mon, Jul 27, 2009 at 07:53:30PM +0200, Jan Kara wrote:
> > So far we had only ocfs2_extend_allocation() which read the inode from
> > disk. Provide also the cached variant.
> > 
> > Signed-off-by: Jan Kara <jack@suse.cz>
> > ---
> >  include/ocfs2/ocfs2.h  |    2 +
> >  libocfs2/extend_file.c |   51 ++++++++++++++++++++++++++---------------------
> >  2 files changed, 30 insertions(+), 23 deletions(-)
> > 
> > diff --git a/include/ocfs2/ocfs2.h b/include/ocfs2/ocfs2.h
> > index 4dbbf43..ac16823 100644
> > --- a/include/ocfs2/ocfs2.h
> > +++ b/include/ocfs2/ocfs2.h
> > @@ -505,6 +505,8 @@ errcode_t ocfs2_delete_extent_block(ocfs2_filesys *fs, uint64_t blkno);
> >   */
> >  errcode_t ocfs2_extend_allocation(ocfs2_filesys *fs, uint64_t ino,
> >  				  uint32_t new_clusters);
> > +/* Ditto for cached inode */
> > +errcode_t ocfs2_cached_inode_extend_allocation(ocfs2_cached_inode *ci, uint32_t new_clusters);
> 
> 	Break the long line, but otherwise this patch looks good.  I'm
  Fixed.

> assuming you want to work on cached inodes later.
  Yes.

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

^ permalink raw reply	[flat|nested] 32+ messages in thread

* [Ocfs2-devel] [PATCH 3/8] Implement quota functions to libocfs2
  2009-07-28 22:52           ` Joel Becker
@ 2009-07-29  9:57             ` Jan Kara
  0 siblings, 0 replies; 32+ messages in thread
From: Jan Kara @ 2009-07-29  9:57 UTC (permalink / raw)
  To: ocfs2-devel

On Tue 28-07-09 15:52:45, Joel Becker wrote:
> On Tue, Jul 28, 2009 at 12:08:29PM +0200, Jan Kara wrote:
> > +/* Read given block */
> > +static errcode_t read_blk(ocfs2_filesys *fs, int type, uint blk, char *buf)
> 
> 	You're sure quota files will never be larger than a uint?  Also,
> we tend to do 'unsigned int', not 'uint'.
  Well, the disk format is designed only for 32-bit uids and gids (which is
consistent with the rest of OCFS2). For 2^32 users tracked in the quota file,
it is going to have around 2^29 blocks so yes, unsigned int should be safe
:)
  I'll substitute uint with unsigned int... although it's horribly long for
such a useful type ;).

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

^ permalink raw reply	[flat|nested] 32+ messages in thread

* [Ocfs2-devel] [PATCH 4/8] Write out quota info changes on ocfs2_close()
  2009-07-28 22:53   ` Joel Becker
@ 2009-07-29  9:58     ` Jan Kara
  0 siblings, 0 replies; 32+ messages in thread
From: Jan Kara @ 2009-07-29  9:58 UTC (permalink / raw)
  To: ocfs2-devel

On Tue 28-07-09 15:53:56, Joel Becker wrote:
> On Mon, Jul 27, 2009 at 07:53:32PM +0200, Jan Kara wrote:
> > We don't write out change of information in quota file header on each change,
> > we rather cache it in ocfs2_filesys structure. So write out all the
> > information when ocfs2_close() is called.
> > 
> > Signed-off-by: Jan Kara <jack@suse.cz>
> 
> This looks good as long as all quota ops keep those ocfs2_cached_inodes
> up to date.
  Yes, all quota ops use only these cached inodes so it should be fine.

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

^ permalink raw reply	[flat|nested] 32+ messages in thread

* [Ocfs2-devel] [PATCH 5/8] Quota support for fsck.ocfs2
  2009-07-28 23:05   ` Joel Becker
@ 2009-07-29 10:07     ` Jan Kara
  2009-07-29 14:28       ` Tao Ma
  2009-07-29 18:22       ` Joel Becker
  0 siblings, 2 replies; 32+ messages in thread
From: Jan Kara @ 2009-07-29 10:07 UTC (permalink / raw)
  To: ocfs2-devel

On Tue 28-07-09 16:05:09, Joel Becker wrote:
> On Mon, Jul 27, 2009 at 07:53:33PM +0200, Jan Kara wrote:
> > diff --git a/fsck.ocfs2/include/pass5.h b/fsck.ocfs2/include/pass5.h
> > new file mode 100644
> > index 0000000..dfd27cd
> > --- /dev/null
> > +++ b/fsck.ocfs2/include/pass5.h
> > @@ -0,0 +1,32 @@
> > +/*
> > + * pass5.h
> > + *
> > + * Copyright (C) 2009 Novell Corporation.  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 as published by the Free Software Foundation; either
> > + * version 2 of the License, or (at your option) any later version.
> 
> 	We generally use GPL version 2 only, but you are welcome to
> license this 2-or-later.  I only mention this because...
> 
> > + * 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.
> 
> 	Can you check http://kernel.us.oracle.com/~smushran/license.txt
> and update your header?  The last paragraph is no longer recommended by
> the FSF, and we'd love for this header to have the emacs/vim lines at
> the top.
  I've just copy-pasted the header from pass4.h :) I'll happily update it
but the server you referenced doesn't seem to exist anymore.

> > + *
> > + * Author: Jan Kara
> > + */
> 
> 	Also, we deliberately drop author attribution from individual
> source files.  You can add yourself to MAINTAINERS.
  No problem with dropping it.

> > +errcode_t o2fsck_pass5(o2fsck_state *ost)
> > +{
> > +	errcode_t ret;
> > +	ocfs2_filesys *fs = ost->ost_fs;
> > +	struct ocfs2_super_block *super = OCFS2_RAW_SB(fs->fs_super);
> > +	int has_usrquota, has_grpquota;
> > +
> > +	has_usrquota = OCFS2_HAS_RO_COMPAT_FEATURE(super,
> > +				OCFS2_FEATURE_RO_COMPAT_USRQUOTA);
> > +	has_grpquota = OCFS2_HAS_RO_COMPAT_FEATURE(super,
> > +				OCFS2_FEATURE_RO_COMPAT_GRPQUOTA);
> > +	/* Nothing to check? */
> > +	if (!has_usrquota && !has_grpquota)
> > +		return 0;
> > +	printf("Pass 5: Checking quota information.\n");
> > +	if (has_usrquota) {
> > +		ret = ocfs2_new_quota_hash(qhash + USRQUOTA);
> > +		if (ret) {
> > +			com_err(whoami, ret,
> > +				"while allocating user quota hash");
> > +			goto out;
> > +		}
> > +		ret = load_quota_file(ost, USRQUOTA);
> > +		if (ret)
> > +			goto out;
> > +	}
> > +	if (has_grpquota) {
> > +		ret = ocfs2_new_quota_hash(qhash + GRPQUOTA);
> > +		if (ret) {
> > +			com_err(whoami, ret,
> > +				"while allocating group quota hash");
> > +			goto out;
> > +		}
> > +		ret = load_quota_file(ost, GRPQUOTA);
> > +		if (ret)
> > +			goto out;
> > +	}
> > +	ret = ocfs2_compute_quota_usage(fs, qhash[USRQUOTA], qhash[GRPQUOTA]);
> > +	if (ret) {
> > +		com_err(whoami, ret, "while computing quota usage");
> > +		goto out;
> > +	}
> > +	if (has_usrquota) {
> > +		ret = recreate_quota_files(fs, USRQUOTA);
> > +		if (ret)
> > +			goto out;
> > +		ret = ocfs2_free_quota_hash(qhash[USRQUOTA]);
> > +		if (ret) {
> > +			com_err(whoami, ret, "while release user quota hash");
> > +			goto out;
> > +		}
> > +	}
> > +	if (has_grpquota) {
> > +		ret = recreate_quota_files(fs, GRPQUOTA);
> > +		if (ret)
> > +			goto out;
> > +		ret = ocfs2_free_quota_hash(qhash[GRPQUOTA]);
> > +		if (ret) {
> > +			com_err(whoami, ret, "while release group quota hash");
> > +			goto out;
> > +		}
> > +	}
> 
> 	The way I read this, you check the existing quota structures,
> and then ignore them while recomputing the usage.  You just do a full-on
> recompute.  Then you write out the newly computed data.  Am I correct?
  Yes, the point is: Quota *usage* is recomputed from scratch (and
basically there's not much better way because e.g. a corruption could have
changed owners of files). Quota *limits* have to be loaded from old quota
files and that's the whole point of our quota checking excercise.

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

^ permalink raw reply	[flat|nested] 32+ messages in thread

* [Ocfs2-devel] [PATCH 5/8] Quota support for fsck.ocfs2
  2009-07-29 10:07     ` Jan Kara
@ 2009-07-29 14:28       ` Tao Ma
  2009-07-29 18:22       ` Joel Becker
  1 sibling, 0 replies; 32+ messages in thread
From: Tao Ma @ 2009-07-29 14:28 UTC (permalink / raw)
  To: ocfs2-devel

Hi Jan,
Jan Kara wrote:
> On Tue 28-07-09 16:05:09, Joel Becker wrote:
>   
>> On Mon, Jul 27, 2009 at 07:53:33PM +0200, Jan Kara wrote:
>>     
>>> diff --git a/fsck.ocfs2/include/pass5.h b/fsck.ocfs2/include/pass5.h
>>> new file mode 100644
>>> index 0000000..dfd27cd
>>> --- /dev/null
>>> +++ b/fsck.ocfs2/include/pass5.h
>>> @@ -0,0 +1,32 @@
>>> +/*
>>> + * pass5.h
>>> + *
>>> + * Copyright (C) 2009 Novell Corporation.  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 as published by the Free Software Foundation; either
>>> + * version 2 of the License, or (at your option) any later version.
>>>       
>> 	We generally use GPL version 2 only, but you are welcome to
>> license this 2-or-later.  I only mention this because...
>>
>>     
>>> + * 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.
>>>       
>> 	Can you check http://kernel.us.oracle.com/~smushran/license.txt
>> and update your header?  The last paragraph is no longer recommended by
>> the FSF, and we'd love for this header to have the emacs/vim lines at
>> the top.
>>     
>   I've just copy-pasted the header from pass4.h :) I'll happily update it
> but the server you referenced doesn't seem to exist anymore.
>   
You can copy/paste from libocfs2/xattr.c. It should have the right one.

Regards,
Tao

^ permalink raw reply	[flat|nested] 32+ messages in thread

* [Ocfs2-devel] [PATCH 1/8] Update ocfs2_fs.h to contain all necessary quota structures and constants
  2009-07-29  9:42     ` Jan Kara
@ 2009-07-29 18:19       ` Joel Becker
  2009-07-30 16:57         ` Jan Kara
  0 siblings, 1 reply; 32+ messages in thread
From: Joel Becker @ 2009-07-29 18:19 UTC (permalink / raw)
  To: ocfs2-devel

On Wed, Jul 29, 2009 at 11:42:56AM +0200, Jan Kara wrote:
> > >  #define OCFS2_GLOBAL_INFO_OFF (sizeof(struct ocfs2_disk_dqheader))
> > > +#define OCFS2_GLOBAL_TREE_BLK 1
> > 
> > 	I don't see GLOBAL_TREE_BLK in the kernel.
>   They are in the generic quota header fs/quota/quota_tree.h defining quota
> file format. For global quota files we use generic quota code which also
> defines some parts of the disk format...
 
<snip>

>   These are the same cases. I can split them into a separate header file if
> you like it better. But from the tools point of view, I felt that keeping
> all disk-format related things in one header makes sence.

	ocfs2_fs.h is a shared header, though.  It needs to have the
same contents in the tools and in the kernel (at any point where the
feature set is identical, of course).  Why can't you just put the
quota_tree.h and quotaio_v2.h headers in include/ocfs2-kernel?  This not
only signifies that they aren't ocfs2-specific structures, it allows
them to follow the kernel headers in an easier fashion.  We do the same
thing with ocfs1_fs_compat.h, which defines the ocfs header we
deliberately break in ocfs2.

Joel

-- 

"When choosing between two evils, I always like to try the one
 I've never tried before."
        - Mae West

Joel Becker
Principal Software Developer
Oracle
E-mail: joel.becker at oracle.com
Phone: (650) 506-8127

^ permalink raw reply	[flat|nested] 32+ messages in thread

* [Ocfs2-devel] [PATCH 5/8] Quota support for fsck.ocfs2
  2009-07-29 10:07     ` Jan Kara
  2009-07-29 14:28       ` Tao Ma
@ 2009-07-29 18:22       ` Joel Becker
  1 sibling, 0 replies; 32+ messages in thread
From: Joel Becker @ 2009-07-29 18:22 UTC (permalink / raw)
  To: ocfs2-devel

On Wed, Jul 29, 2009 at 12:07:33PM +0200, Jan Kara wrote:
> On Tue 28-07-09 16:05:09, Joel Becker wrote:
> > 	Can you check http://kernel.us.oracle.com/~smushran/license.txt
> > and update your header?  The last paragraph is no longer recommended by
> > the FSF, and we'd love for this header to have the emacs/vim lines at
> > the top.
>   I've just copy-pasted the header from pass4.h :) I'll happily update it
> but the server you referenced doesn't seem to exist anymore.

	Haha, that's our internal server.  I'll attach that file :-)

> > 	The way I read this, you check the existing quota structures,
> > and then ignore them while recomputing the usage.  You just do a full-on
> > recompute.  Then you write out the newly computed data.  Am I correct?
>   Yes, the point is: Quota *usage* is recomputed from scratch (and
> basically there's not much better way because e.g. a corruption could have
> changed owners of files). Quota *limits* have to be loaded from old quota
> files and that's the whole point of our quota checking excercise.

	Ahh, I get it.  I was thinking that the recompute of usage was
expensive, but there's no way to validate the usage in the quota files
without running the recompute, and at that point the simple code is, as
you have done, to just write the recomputed data.

Joel

-- 

"In a crisis, don't hide behind anything or anybody. They're going
 to find you anyway."
	- Paul "Bear" Bryant

Joel Becker
Principal Software Developer
Oracle
E-mail: joel.becker at oracle.com
Phone: (650) 506-8127
-------------- next part --------------
/* -*- mode: c; c-basic-offset: 8; -*-
 * vim: noexpandtab sw=8 ts=8 sts=0:
 *
 * filename.c
 *
 * file description
 *
 * Copyright (C) 2004, 2008 Oracle.  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.
 */

^ permalink raw reply	[flat|nested] 32+ messages in thread

* [Ocfs2-devel] [PATCH 1/8] Update ocfs2_fs.h to contain all necessary quota structures and constants
  2009-07-29 18:19       ` Joel Becker
@ 2009-07-30 16:57         ` Jan Kara
  0 siblings, 0 replies; 32+ messages in thread
From: Jan Kara @ 2009-07-30 16:57 UTC (permalink / raw)
  To: ocfs2-devel

On Wed 29-07-09 11:19:00, Joel Becker wrote:
> On Wed, Jul 29, 2009 at 11:42:56AM +0200, Jan Kara wrote:
> > > >  #define OCFS2_GLOBAL_INFO_OFF (sizeof(struct ocfs2_disk_dqheader))
> > > > +#define OCFS2_GLOBAL_TREE_BLK 1
> > > 
> > > 	I don't see GLOBAL_TREE_BLK in the kernel.
> >   They are in the generic quota header fs/quota/quota_tree.h defining quota
> > file format. For global quota files we use generic quota code which also
> > defines some parts of the disk format...
>  
> <snip>
> 
> >   These are the same cases. I can split them into a separate header file if
> > you like it better. But from the tools point of view, I felt that keeping
> > all disk-format related things in one header makes sence.
> 
> 	ocfs2_fs.h is a shared header, though.  It needs to have the
> same contents in the tools and in the kernel (at any point where the
> feature set is identical, of course).  Why can't you just put the
> quota_tree.h and quotaio_v2.h headers in include/ocfs2-kernel?  This not
> only signifies that they aren't ocfs2-specific structures, it allows
> them to follow the kernel headers in an easier fashion.  We do the same
> thing with ocfs1_fs_compat.h, which defines the ocfs header we
> deliberately break in ocfs2.
  Ok. I'll create quota_tree.h and put there those defines with the same
names as they have in the kernel.

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

^ permalink raw reply	[flat|nested] 32+ messages in thread

end of thread, other threads:[~2009-07-30 16:57 UTC | newest]

Thread overview: 32+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2009-07-27 17:53 [Ocfs2-devel] [PATCH 0/8] Quota support for ocfs2-tools Jan Kara
2009-07-27 17:53 ` [Ocfs2-devel] [PATCH 1/8] Update ocfs2_fs.h to contain all necessary quota structures and constants Jan Kara
2009-07-28 21:45   ` Joel Becker
2009-07-29  9:42     ` Jan Kara
2009-07-29 18:19       ` Joel Becker
2009-07-30 16:57         ` Jan Kara
2009-07-27 17:53 ` [Ocfs2-devel] [PATCH 2/8] Provide ocfs2_cached_inode_extend_allocation() Jan Kara
2009-07-28 21:47   ` Joel Becker
2009-07-29  9:46     ` Jan Kara
2009-07-27 17:53 ` [Ocfs2-devel] [PATCH 3/8] Implement quota functions to libocfs2 Jan Kara
2009-07-28  7:55   ` Tao Ma
2009-07-28  8:40     ` Jan Kara
2009-07-28  8:43       ` Tao Ma
2009-07-28 10:08         ` Jan Kara
2009-07-28 22:52           ` Joel Becker
2009-07-29  9:57             ` Jan Kara
2009-07-27 17:53 ` [Ocfs2-devel] [PATCH 4/8] Write out quota info changes on ocfs2_close() Jan Kara
2009-07-28 22:53   ` Joel Becker
2009-07-29  9:58     ` Jan Kara
2009-07-27 17:53 ` [Ocfs2-devel] [PATCH 5/8] Quota support for fsck.ocfs2 Jan Kara
2009-07-28 23:05   ` Joel Becker
2009-07-29 10:07     ` Jan Kara
2009-07-29 14:28       ` Tao Ma
2009-07-29 18:22       ` Joel Becker
2009-07-27 17:53 ` [Ocfs2-devel] [PATCH 6/8] Quota support for mkfs.ocfs2 Jan Kara
2009-07-28  8:11   ` Tao Ma
2009-07-28  8:43     ` Jan Kara
2009-07-28 10:09       ` Jan Kara
2009-07-28 23:07         ` Joel Becker
2009-07-27 17:53 ` [Ocfs2-devel] [PATCH 7/8] Add quota support to tunefs.ocfs2 Jan Kara
2009-07-29  1:04   ` Joel Becker
2009-07-27 17:53 ` [Ocfs2-devel] [PATCH 8/8] Change headers to reflect that quota is now fully supported Jan Kara

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.