>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) -lpthread include $(TOPDIR)/Postamble.make -- 1.5.2.4