File ocfs2-quota.diff of Package ocfs2-tools

>From 4220f907aba2afa2c045f26f0d9afe465aea6863 Mon Sep 17 00:00:00 2001
From: Jan Kara <jack@suse.cz>
Date: Tue, 14 Oct 2008 15:44:31 +0200
Subject: [PATCH] Implement quota support to mkfs and add a few auxiliary functions for quotas
into libocfs2.

Signed-off-by: Jan Kara <jack@suse.cz>
---
 Preamble.make                   |    3 +-
 include/ocfs2-kernel/ocfs2_fs.h |  114 +++++++++++++++++-
 include/ocfs2/ocfs2.h           |   25 ++++
 libocfs2/Makefile               |    1 +
 libocfs2/feature_string.c       |   22 +++-
 libocfs2/quota.c                |  261 +++++++++++++++++++++++++++++++++++++++
 mkfs.ocfs2/mkfs.c               |  140 +++++++++++++++++++++-
 mkfs.ocfs2/mkfs.h               |    1 +
 o2image/Makefile                |    2 +-
 9 files changed, 563 insertions(+), 6 deletions(-)
 create mode 100644 libocfs2/quota.c

diff --git a/include/ocfs2-kernel/ocfs2_fs.h b/include/ocfs2-kernel/ocfs2_fs.h
index e454099..d27b098 100644
--- a/include/ocfs2-kernel/ocfs2_fs.h
+++ b/include/ocfs2-kernel/ocfs2_fs.h
@@ -92,7 +92,9 @@
 					 | OCFS2_FEATURE_INCOMPAT_INLINE_DATA \
 					 | OCFS2_FEATURE_INCOMPAT_EXTENDED_SLOT_MAP \
 					 | OCFS2_FEATURE_INCOMPAT_USERSPACE_STACK)
-#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
@@ -159,6 +161,12 @@
  */
 #define OCFS2_FEATURE_RO_COMPAT_UNWRITTEN	0x0001
 
+/*
+ * Maintain quota information for this filesystem
+ */
+#define OCFS2_FEATURE_RO_COMPAT_USRQUOTA	0x0002
+#define OCFS2_FEATURE_RO_COMPAT_GRPQUOTA	0x0004
+
 /* The byte offset of the first backup block will be 1G.
  * The following will be 4G, 16G, 64G, 256G and 1T.
  */
@@ -188,6 +196,7 @@
 #define OCFS2_HEARTBEAT_FL	(0x00000200)	/* Heartbeat area */
 #define OCFS2_CHAIN_FL		(0x00000400)	/* Chain allocator */
 #define OCFS2_DEALLOC_FL	(0x00000800)	/* Truncate log */
+#define OCFS2_QUOTA_FL		(0x00001000)	/* Quota file */
 
 /*
  * Flags on ocfs2_dinode.i_dyn_features
@@ -311,6 +320,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,
@@ -318,6 +329,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
 };
 
@@ -331,6 +344,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 },
@@ -338,7 +353,9 @@ static struct ocfs2_system_inode_info ocfs2_system_inodes[NUM_SYSTEM_INODES] = {
 	[INODE_ALLOC_SYSTEM_INODE]		= { "inode_alloc:%04d", OCFS2_BITMAP_FL | OCFS2_CHAIN_FL, S_IFREG | 0644 },
 	[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 }
+	[TRUNCATE_LOG_SYSTEM_INODE]		= { "truncate_log:%04d", OCFS2_DEALLOC_FL, S_IFREG | 0644 },
+	[LOCAL_USER_QUOTA_SYSTEM_INODE]		= { "aquota%04d.user", OCFS2_QUOTA_FL, S_IFREG | 0644 },
+	[LOCAL_GROUP_QUOTA_SYSTEM_INODE]	= { "aquota%04d.group", OCFS2_QUOTA_FL, S_IFREG | 0644 },
 };
 
 /* Parameter passed from mount.ocfs2 to module */
@@ -713,6 +730,99 @@ struct ocfs2_group_desc
 /*40*/	__u8    bg_bitmap[0];
 };
 
+/* Magic numbers and known versions for local quota files */
+#define OCFS2_LOCAL_QMAGICS {\
+	0x0cf524c0, /* USRQUOTA */ \
+	0x0cf524c1  /* GRPQUOTA */ \
+}
+
+#define OCFS2_LOCAL_QVERSIONS {\
+	0, \
+	0, \
+}
+
+/* Magic numbers and known versions for global quota files */
+#define OCFS2_GLOBAL_QMAGICS {\
+	0x0cf52470, /* USRQUOTA */ \
+	0x0cf52471  /* GRPQUOTA */ \
+}
+
+#define OCFS2_GLOBAL_QVERSIONS {\
+	0, \
+	0, \
+}
+
+/* How many bytes to we reserve in each quota file block for our internal
+ * purposes? E.g. checksums... */
+#define OCFS2_QBLK_RESERVED_SPACE 8
+
+/* Generic header of all quota files */
+struct ocfs2_disk_dqheader {
+	__le32 dqh_magic;	/* Magic number identifying file */
+	__le32 dqh_version;	/* Quota format version */
+};
+
+/* Quota flags in dqinfo header */
+#define OLQF_CLEAN      0x0001  /* Quota file is empty (this should be after\
+				 * quota has been cleanly turned off) */
+
+#define OCFS2_LOCAL_INFO_OFF (sizeof(struct ocfs2_disk_dqheader))
+
+/* Information header of local quota file (immediately follows the generic
+ * header) */
+struct ocfs2_local_disk_dqinfo {
+	__le32 dqi_flags;	/* Flags for quota file */
+	__le32 dqi_chunks;	/* Number of chunks of quota structures
+				 * with a bitmap */
+	__le32 dqi_blocks;	/* Number of blocks allocated for quota file */
+};
+
+/* Header of one chunk of a quota file */
+struct ocfs2_local_disk_chunk {
+	__le32 dqc_free;	/* Number of free entries in the bitmap */
+	uint8_t dqc_bitmap[0];	/* Bitmap of entries in the corresponding
+				 * chunk of quota file */
+};
+
+#define OCFS2_GLOBAL_INFO_OFF (sizeof(struct ocfs2_disk_dqheader))
+
+/* Information header of global quota file (immediately follows the generic
+ * header) */
+struct ocfs2_global_disk_dqinfo {
+	__le32 dqi_bgrace;
+	__le32 dqi_igrace;
+	__le32 dqi_syncms;
+	__le32 dqi_blocks;
+	__le32 dqi_free_blk;
+	__le32 dqi_free_entry;
+};
+
+/* Header of leaf tree block */
+struct ocfs2_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 {
+	__le32 dqb_id;          /* ID the structure belongs to */
+	__le32 dqb_use_count;   /* Number of nodes having reference to this structure */
+	__le64 dqb_ihardlimit;  /* absolute limit on allocated inodes */
+	__le64 dqb_isoftlimit;  /* preferred inode limit */
+	__le64 dqb_curinodes;   /* current # allocated inodes */
+	__le64 dqb_bhardlimit;  /* absolute limit on disk space */
+	__le64 dqb_bsoftlimit;  /* preferred limit on disk space */
+	__le64 dqb_curspace;    /* current space occupied */
+	__le64 dqb_btime;       /* time limit for excessive disk use */
+	__le64 dqb_itime;       /* time limit for excessive inode use */
+	__le64 dqb_pad1;
+	__le64 dqb_pad2;
+};
+
 #ifdef __KERNEL__
 static inline int ocfs2_fast_symlink_chars(struct super_block *sb)
 {
diff --git a/include/ocfs2/ocfs2.h b/include/ocfs2/ocfs2.h
index 68ba4f5..cabae04 100644
--- a/include/ocfs2/ocfs2.h
+++ b/include/ocfs2/ocfs2.h
@@ -134,6 +134,16 @@ typedef struct _ocfs2_dir_scan ocfs2_dir_scan;
 typedef struct _ocfs2_bitmap ocfs2_bitmap;
 typedef struct _ocfs2_devices ocfs2_devices;
 
+#define MAXQUOTAS 2
+
+struct _ocfs2_quota_info {
+	uint32_t dqi_bgrace;
+	uint32_t dqi_igrace;
+	uint32_t dqi_syncms;
+};
+
+typedef struct _ocfs2_quota_info ocfs2_quota_info;
+
 struct _ocfs2_filesys {
 	char *fs_devname;
 	uint32_t fs_flags;
@@ -160,6 +170,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;
 };
@@ -557,6 +569,19 @@ 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_disk_dqdbheader *bheader);
+errcode_t ocfs2_create_local_quota_file(ocfs2_filesys *fs, int type,
+					uint64_t blkno);
+int ocfs2_qtree_depth(int blocksize);
+errcode_t ocfs2_create_global_quota_file(ocfs2_filesys *fs, int type,
+					 uint64_t blkno);
+
 /* 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 446c8b4..18cf2ee 100644
--- a/libocfs2/Makefile
+++ b/libocfs2/Makefile
@@ -72,6 +72,7 @@ CFILES = 		\
 	lockid.c	\
 	backup_super.c	\
 	feature_string.c\
+	quota.c		\
 	image.c
 
 HFILES =		\
diff --git a/libocfs2/feature_string.c b/libocfs2/feature_string.c
index 7b1f93e..00c4f26 100644
--- a/libocfs2/feature_string.c
+++ b/libocfs2/feature_string.c
@@ -73,7 +73,9 @@ static ocfs2_fs_options feature_level_defaults[] = {
 	 OCFS2_FEATURE_INCOMPAT_SPARSE_ALLOC |
 	 OCFS2_FEATURE_INCOMPAT_EXTENDED_SLOT_MAP |
 	 OCFS2_FEATURE_INCOMPAT_INLINE_DATA,
-	 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 */
@@ -110,6 +112,16 @@ static struct fs_feature_flags ocfs2_supported_features[] = {
 		{0, OCFS2_FEATURE_INCOMPAT_INLINE_DATA, 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}
@@ -161,6 +173,14 @@ static struct feature_name ocfs2_feature_names[] = {
 		.fn_flag = {0, 0, OCFS2_FEATURE_RO_COMPAT_UNWRITTEN},
 	},
 	{
+		.fn_name = "UserQuota",
+		.fn_flag = {0, 0, OCFS2_FEATURE_RO_COMPAT_USRQUOTA},
+	},
+	{
+		.fn_name = "GroupQuota",
+		.fn_flag = {0, 0, OCFS2_FEATURE_RO_COMPAT_GRPQUOTA},
+	},
+	{
 		.fn_name = "InlineData",
 		.fn_flag = {0, OCFS2_FEATURE_INCOMPAT_INLINE_DATA, 0},
 	},
diff --git a/libocfs2/quota.c b/libocfs2/quota.c
new file mode 100644
index 0000000..8670ae0
--- /dev/null
+++ b/libocfs2/quota.c
@@ -0,0 +1,261 @@
+/* -*- 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_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);
+}
+
+errcode_t ocfs2_create_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_extend_allocation(fs, blkno, (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 */
+
+	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;
+}
+
+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;
+}
+
+errcode_t ocfs2_create_global_quota_file(ocfs2_filesys *fs, int type,
+					 uint64_t blkno)
+{
+	ocfs2_cached_inode *ci = NULL;
+	struct ocfs2_dinode *di;
+	char *buf = NULL;
+	struct ocfs2_disk_dqheader *header;
+	struct ocfs2_global_disk_dqinfo *info;
+	struct ocfs2_global_disk_dqblk *dqblk;
+	struct ocfs2_disk_dqdbheader *bheader;
+	uint32_t *treeblk;
+	unsigned int magics[] = OCFS2_GLOBAL_QMAGICS;
+	int versions[] = OCFS2_GLOBAL_QVERSIONS;
+	errcode_t err;
+	int blocks = ocfs2_qtree_depth(fs->fs_blocksize) + 2;
+	unsigned int written;
+	int i;
+
+	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;
+
+	err = ocfs2_extend_allocation(fs, blkno, (blocks * fs->fs_blocksize +
+			fs->fs_clustersize - 1) / fs->fs_clustersize);
+	if (err)
+		goto out;
+	di->i_size = blocks * 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 * 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);
+
+	info = (struct ocfs2_global_disk_dqinfo *)(buf + OCFS2_GLOBAL_INFO_OFF);
+	info->dqi_bgrace = fs->qinfo[type].dqi_bgrace;
+	info->dqi_igrace = fs->qinfo[type].dqi_igrace;
+	info->dqi_syncms = fs->qinfo[type].dqi_syncms;
+	info->dqi_blocks = blocks;
+	info->dqi_free_blk = 0;
+	info->dqi_free_entry = blocks - 1;
+	ocfs2_swap_quota_global_info(info);
+
+	/* FIXME: This should be split into a separate function that is able to
+	 * add blocks etc. Usage information can then be properly specified by
+	 * the caller. */
+	/* Create quota structure for root user */
+	for (i = 0; i < ocfs2_qtree_depth(fs->fs_blocksize); i++) {
+		treeblk = (uint32_t *)(buf +
+				       ((i + 1) * fs->fs_blocksize));
+		*treeblk = cpu_to_le32(i + 2);
+	}
+
+	bheader = (struct ocfs2_disk_dqdbheader *)(buf +
+				((i + 1) * fs->fs_blocksize));
+	bheader->dqdh_entries = 1;
+	ocfs2_swap_quota_leaf_block_header(bheader);
+
+	dqblk = (struct ocfs2_global_disk_dqblk *)(buf +
+				((i + 1) * fs->fs_blocksize) +
+				sizeof(struct ocfs2_disk_dqdbheader));
+	dqblk->dqb_curinodes = 2;
+	dqblk->dqb_curspace = 2 * fs->fs_blocksize;
+	ocfs2_swap_quota_global_dqblk(dqblk);
+
+	err = ocfs2_file_write(ci, buf, blocks * fs->fs_blocksize, 0, &written);
+	if (!err && written != blocks * 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;
+}
diff --git a/mkfs.ocfs2/mkfs.c b/mkfs.ocfs2/mkfs.c
index aad166b..9fe2ac2 100644
--- a/mkfs.ocfs2/mkfs.c
+++ b/mkfs.ocfs2/mkfs.c
@@ -96,12 +96,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%04d.user", SFI_QUOTA, 0, S_IFREG | 0644 },
+	{ "aquota%04d.group", SFI_QUOTA, 0, S_IFREG | 0644 },
 };
 
 struct fs_type_translation {
@@ -184,6 +188,23 @@ static int hb_dev_skip(State *s, int system_inode)
 	return ret;
 }
 
+/* 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);
@@ -191,6 +212,108 @@ 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)
+{
+	errcode_t ret;
+	ocfs2_filesys *fs = NULL;
+	char fname[SYSTEM_FILE_NAME_MAX];
+	uint64_t blkno;
+	int i;
+
+	ret = ocfs2_open(s->device_name, OCFS2_FLAG_RW, 0, 0, &fs);
+	if (ret) {
+		com_err(s->progname, ret, "while opening new file system");
+		exit(1);
+	}
+
+	/* Write correct data into quota files */
+	if (!feature_skip(s, USER_QUOTA_SYSTEM_INODE)) {
+		fs->qinfo[0].dqi_syncms = 10000;
+		fs->qinfo[0].dqi_bgrace = 604800;	/* 1 week */
+		fs->qinfo[0].dqi_igrace = 604800;	/* 1 week */
+		snprintf(fname, sizeof(fname),
+			 ocfs2_system_inodes[USER_QUOTA_SYSTEM_INODE].si_name);
+		ret = ocfs2_lookup(fs, fs->fs_sysdir_blkno, fname,
+				   strlen(fname), NULL, &blkno);
+		if (ret) {
+			com_err(s->progname, ret,
+				"while looking up quota filename \"%.*s\"",
+				(int)strlen(fname), fname);
+			goto error;
+		}
+		ret = ocfs2_create_global_quota_file(fs, 0, blkno);
+		if (ret) {
+			com_err(s->progname, ret, "while creating global user "
+				"quota file");
+			goto error;
+		}
+		for (i = 0; i < s->initial_slots; ++i) {
+			snprintf(fname, sizeof(fname),
+				ocfs2_system_inodes[
+				LOCAL_USER_QUOTA_SYSTEM_INODE].si_name, i);
+			ret = ocfs2_lookup(fs, fs->fs_sysdir_blkno, fname,
+					   strlen(fname), NULL, &blkno);
+			if (ret) {
+				com_err(s->progname, ret,
+					"while looking up quota filename "
+					"\"%.*s\"", (int)strlen(fname), fname);
+				goto error;
+			}
+			ret = ocfs2_create_local_quota_file(fs, 0, blkno);
+			if (ret) {
+				com_err(s->progname, ret, "while creating "
+					"local user quota file");
+				goto error;
+			}
+		}
+	}
+	if (!feature_skip(s, GROUP_QUOTA_SYSTEM_INODE)) {
+		fs->qinfo[1].dqi_syncms = 10000;
+		fs->qinfo[1].dqi_bgrace = 604800;	/* 1 week */
+		fs->qinfo[1].dqi_igrace = 604800;	/* 1 week */
+		snprintf(fname, sizeof(fname),
+			 ocfs2_system_inodes[GROUP_QUOTA_SYSTEM_INODE].si_name);
+		ret = ocfs2_lookup(fs, fs->fs_sysdir_blkno, fname,
+				   strlen(fname), NULL, &blkno);
+		if (ret) {
+			com_err(s->progname, ret,
+				"while looking up quota filename \"%.*s\"",
+				(int)strlen(fname), fname);
+			goto error;
+		}
+		ret = ocfs2_create_global_quota_file(fs, 1, blkno);
+		if (ret) {
+			com_err(s->progname, ret, "while creating global group "
+				"quota file");
+			goto error;
+		}
+		for (i = 0; i < s->initial_slots; ++i) {
+			snprintf(fname, sizeof(fname),
+				ocfs2_system_inodes[
+				LOCAL_GROUP_QUOTA_SYSTEM_INODE].si_name, i);
+			ret = ocfs2_lookup(fs, fs->fs_sysdir_blkno, fname,
+					   strlen(fname), NULL, &blkno);
+			if (ret) {
+				com_err(s->progname, ret,
+					"while looking up quota filename "
+					"\"%.*s\"", (int)strlen(fname), fname);
+				goto error;
+			}
+			ret = ocfs2_create_local_quota_file(fs, 1, blkno);
+			if (ret) {
+				com_err(s->progname, ret, "while creating "
+					"local group quota file");
+				goto error;
+			}
+		}
+	}
+	ocfs2_close(fs);
+	return;
+error:
+	clear_both_ends(s);
+	exit(1);
+}
+
 int
 main(int argc, char **argv)
 {
@@ -355,6 +478,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++) {
@@ -413,6 +538,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++) {
@@ -496,6 +623,14 @@ main(int argc, char **argv)
 
 		if (!s->quiet)
 			printf("done\n");
+
+		if (!s->quiet)
+			printf("Formatting quota files: ");
+
+		format_quota_files(s);
+
+		if (!s->quiet)
+			printf("done\n");
 	}
 
 	close_device(s);
@@ -2304,6 +2439,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/o2image/Makefile b/o2image/Makefile
index 0259161..b50201a 100644
--- a/o2image/Makefile
+++ b/o2image/Makefile
@@ -32,6 +32,6 @@ OBJS = $(subst .c,.o,$(CFILES))
 DIST_FILES = $(CFILES) $(HFILES) o2image.8.in
 
 o2image: $(OBJS) $(LIBOCFS2_DEPS)
-	$(LINK) $(GLIB_LIBS) $(LIBOCFS2_LIBS) $(COM_ERR_LIBS)
+	$(LINK) -static $(GLIB_LIBS) $(LIBOCFS2_LIBS) $(COM_ERR_LIBS)
 
 include $(TOPDIR)/Postamble.make
-- 
1.5.2.4

openSUSE Build Service is sponsored by