diff --git a/ocfs2-quota.diff b/ocfs2-quota.diff new file mode 100644 index 0000000..8338837 --- /dev/null +++ b/ocfs2-quota.diff @@ -0,0 +1,795 @@ +>From 4220f907aba2afa2c045f26f0d9afe465aea6863 Mon Sep 17 00:00:00 2001 +From: Jan Kara +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 +--- + 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 ++ ++#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 + diff --git a/ocfs2-tools.changes b/ocfs2-tools.changes index 49b36fd..630b27a 100644 --- a/ocfs2-tools.changes +++ b/ocfs2-tools.changes @@ -5,6 +5,11 @@ Mon Oct 27 14:58:59 CET 2008 - abeekhof@suse.de - Remove part of the static glib patch as tunefs.ocfs2 no longer uses glib - Combine patches yet to be upstreamed to ease maintenance +------------------------------------------------------------------- +Mon Oct 27 15:53:41 CEST 2008 - mfasheh@suse.com + +- Add quota support patches + ------------------------------------------------------------------- Mon Oct 20 21:08:34 CEST 2008 - abeekhof@suse.de diff --git a/ocfs2-tools.spec b/ocfs2-tools.spec index a4a23d6..806d855 100644 --- a/ocfs2-tools.spec +++ b/ocfs2-tools.spec @@ -22,12 +22,13 @@ Name: ocfs2-tools BuildRequires: e2fsprogs-devel glib2-devel libdlm-devel libopenais-devel libpacemaker-devel libxml2-devel libxslt-devel python-devel python-gtk-devel readline-devel update-desktop-files Summary: Oracle Cluster File System 2 Core Tools Version: 1.4.1 -Release: 3 +Release: 4 License: GPL v2 or later Group: System/Filesystems Source: ocfs2-tools.tar.bz2 Patch5: ocfs2-tools-static-glib.diff Patch99: ocfs2-devel.diff +Patch100: ocfs2-quota.diff Url: http://oss.oracle.com/projects/ocfs2-tools/ Requires: net-tools, modutils, e2fsprogs, /sbin/chkconfig, glib2 >= 2.2.3 PreReq: %insserv_prereq %fillup_prereq @@ -88,6 +89,7 @@ Authors: %setup -n %{name} %patch5 -p1 %patch99 -p1 +%patch100 -p1 %build %{?suse_update_config:%{suse_update_config -f}} @@ -191,6 +193,8 @@ fi - Refresh package to upstream a93731c164d024e0016520a1f4cd8f9007d7c05e - Remove part of the static glib patch as tunefs.ocfs2 no longer uses glib - Combine patches yet to be upstreamed to ease maintenance +* Mon Oct 27 2008 mfasheh@suse.com +- Add quota support patches * Mon Oct 20 2008 abeekhof@suse.de - New patch for ocfs2_controld - make sure stad(err|out|in) are mapped to /dev/null and not left unassigned (which causes issues when logging)