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