[PATCH 08/13] GFS: mount and tuning options

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



There are a variety of mount options, tunable parameters, internal
statistics, and methods of online file system manipulation.

Signed-off-by: Ken Preslan <[email protected]>
Signed-off-by: David Teigland <[email protected]>

---

 fs/gfs2/ioctl.c  | 1485 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 fs/gfs2/ioctl.h  |   15 
 fs/gfs2/mount.c  |  209 +++++++
 fs/gfs2/mount.h  |   15 
 fs/gfs2/resize.c |  285 ++++++++++
 fs/gfs2/resize.h |   19 
 fs/gfs2/sys.c    |  201 +++++++
 fs/gfs2/sys.h    |   24 
 8 files changed, 2253 insertions(+)

--- a/fs/gfs2/ioctl.c	1970-01-01 07:30:00.000000000 +0730
+++ b/fs/gfs2/ioctl.c	2005-09-01 17:36:55.321114560 +0800
@@ -0,0 +1,1485 @@
+/*
+ * Copyright (C) Sistina Software, Inc.  1997-2003 All rights reserved.
+ * Copyright (C) 2004-2005 Red Hat, Inc.  All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU General Public License v.2.
+ */
+
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/spinlock.h>
+#include <linux/completion.h>
+#include <linux/buffer_head.h>
+#include <linux/gfs2_ioctl.h>
+#include <asm/semaphore.h>
+#include <asm/uaccess.h>
+
+#include "gfs2.h"
+#include "bmap.h"
+#include "dir.h"
+#include "eattr.h"
+#include "glock.h"
+#include "glops.h"
+#include "inode.h"
+#include "ioctl.h"
+#include "jdata.h"
+#include "log.h"
+#include "meta_io.h"
+#include "quota.h"
+#include "resize.h"
+#include "rgrp.h"
+#include "super.h"
+#include "trans.h"
+
+typedef int (*gi_filler_t) (struct gfs2_inode *ip,
+			    struct gfs2_ioctl *gi,
+			    char *buf,
+			    unsigned int size,
+			    unsigned int *count);
+
+#define ARG_SIZE 32
+
+/**
+ * gi_skeleton - Setup a buffer that functions can print into
+ * @ip:
+ * @gi:
+ * @filler:
+ *
+ * Returns: -errno or count of bytes copied to userspace
+ */
+
+static int gi_skeleton(struct gfs2_inode *ip, struct gfs2_ioctl *gi,
+		       gi_filler_t filler)
+{
+	unsigned int size = gfs2_tune_get(ip->i_sbd, gt_lockdump_size);
+	char *buf;
+	unsigned int count = 0;
+	int error;
+
+	if (size > gi->gi_size)
+		size = gi->gi_size;
+
+	buf = kmalloc(size, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	error = filler(ip, gi, buf, size, &count);
+	if (error)
+		goto out;
+
+	if (copy_to_user(gi->gi_data, buf, count + 1))
+		error = -EFAULT;
+	else
+		error = count + 1;
+
+ out:
+	kfree(buf);
+
+	return error;
+}
+
+/**
+ * gi_get_cookie - Return the "cookie" (identifying string) for a
+ *		 filesystem mount
+ * @ip:
+ * @gi:
+ * @buf:
+ * @size:
+ * @count:
+ *
+ * Returns: errno
+ */
+
+static int gi_get_cookie(struct gfs2_inode *ip, struct gfs2_ioctl *gi,
+			 char *buf, unsigned int size, unsigned int *count)
+{
+	int error = -ENOBUFS;
+
+	if (gi->gi_argc != 1)
+		return -EINVAL;
+
+	gfs2_printf("version 0\n");
+	gfs2_printf("%lu", (unsigned long)ip->i_sbd);
+
+	error = 0;
+
+ out:
+	return error;
+}
+
+/**
+ * gi_get_super - Return the "struct gfs2_sb" for a filesystem
+ * @sdp:
+ * @gi:
+ *
+ * Returns: errno
+ */
+
+static int gi_get_super(struct gfs2_sbd *sdp, struct gfs2_ioctl *gi)
+{
+	struct gfs2_holder sb_gh;
+	struct buffer_head *bh;
+	struct gfs2_sb *sb;
+	int error;
+
+	if (gi->gi_argc != 1)
+		return -EINVAL;
+	if (gi->gi_size != sizeof(struct gfs2_sb))
+		return -EINVAL;
+
+	sb = kmalloc(sizeof(struct gfs2_sb), GFP_KERNEL);
+	if (!sb)
+		return -ENOMEM;
+
+	error = gfs2_glock_nq_num(sdp,
+				 GFS2_SB_LOCK, &gfs2_meta_glops,
+				 LM_ST_SHARED, 0, &sb_gh);
+	if (error)
+		goto out;
+
+	error = gfs2_meta_read(sb_gh.gh_gl,
+			       GFS2_SB_ADDR >> sdp->sd_fsb2bb_shift,
+			       DIO_START | DIO_WAIT,
+			       &bh);
+	if (error) {
+		gfs2_glock_dq_uninit(&sb_gh);
+		goto out;
+	}
+	gfs2_sb_in(sb, bh->b_data);
+	brelse(bh);
+
+	gfs2_glock_dq_uninit(&sb_gh);
+
+	if (copy_to_user(gi->gi_data, sb,
+			 sizeof(struct gfs2_sb)))
+		error = -EFAULT;
+	else
+		error = sizeof(struct gfs2_sb);
+
+ out:
+	kfree(sb);
+
+	return error;
+}
+
+/**
+ * gi_get_args - Return the mount arguments
+ * @ip:
+ * @gi:
+ * @buf:
+ * @size:
+ * @count:
+ *
+ * Returns: errno
+ */
+
+static int gi_get_args(struct gfs2_inode *ip, struct gfs2_ioctl *gi,
+		       char *buf, unsigned int size, unsigned int *count)
+{
+	struct gfs2_sbd *sdp = ip->i_sbd;
+	struct gfs2_args *args = &sdp->sd_args;
+	int error = -ENOBUFS;
+
+	if (gi->gi_argc != 1)
+		return -EINVAL;
+
+	gfs2_printf("version 0\n");
+	gfs2_printf("lockproto %s\n", args->ar_lockproto);
+	gfs2_printf("locktable %s\n", args->ar_locktable);
+	gfs2_printf("hostdata %s\n", args->ar_hostdata);
+	gfs2_printf("spectator %d\n", args->ar_spectator);
+	gfs2_printf("ignore_local_fs %d\n", args->ar_ignore_local_fs);
+	gfs2_printf("localcaching %d\n", args->ar_localcaching);
+	gfs2_printf("localflocks %d\n", args->ar_localflocks);
+	gfs2_printf("oopses_ok %d\n", args->ar_oopses_ok);
+	gfs2_printf("debug %d\n", args->ar_debug);
+	gfs2_printf("upgrade %d\n", args->ar_upgrade);
+	gfs2_printf("num_glockd %u\n", args->ar_num_glockd);
+	gfs2_printf("posix_acl %d\n", args->ar_posix_acl);
+	gfs2_printf("quota %u\n", args->ar_quota);
+	gfs2_printf("suiddir %d\n", args->ar_suiddir);
+	gfs2_printf("data %d\n", args->ar_data);
+	gfs2_printf("noatime %d\n", !!test_bit(SDF_NOATIME, &sdp->sd_flags));
+
+	error = 0;
+	
+ out:
+	return error;
+}
+
+/**
+ * gi_get_lockstruct - Return the information in the FS' lockstruct
+ * @ip:
+ * @gi:
+ * @buf:
+ * @size:
+ * @count:
+ *
+ * Returns: errno
+ */
+
+static int gi_get_lockstruct(struct gfs2_inode *ip, struct gfs2_ioctl *gi,
+			     char *buf, unsigned int size, unsigned int *count)
+{
+	struct lm_lockstruct *ls = &ip->i_sbd->sd_lockstruct;
+	int error = -ENOBUFS;
+
+	if (gi->gi_argc != 1)
+		return -EINVAL;
+
+	gfs2_printf("version 0\n");
+	gfs2_printf("jid %u\n", ls->ls_jid);
+	gfs2_printf("first %u\n", ls->ls_first);
+	gfs2_printf("lvb_size %u\n", ls->ls_lvb_size);
+	gfs2_printf("flags %d\n", ls->ls_flags);
+
+	error = 0;
+
+ out:
+	return error;
+}
+
+/**
+ * gi_get_statfs - Return a filesystem's space usage information
+ * @ip:
+ * @gi:
+ * @buf:
+ * @size:
+ * @count:
+ *
+ * Returns: errno
+ */
+
+static int gi_get_statfs(struct gfs2_inode *ip, struct gfs2_ioctl *gi,
+			 char *buf, unsigned int size, unsigned int *count)
+{
+	struct gfs2_sbd *sdp = ip->i_sbd;
+	struct gfs2_statfs_change sc;
+	int error;
+
+	if (gi->gi_argc != 1)
+		return -EINVAL;
+
+	if (gfs2_tune_get(sdp, gt_statfs_slow))
+		error = gfs2_statfs_slow(sdp, &sc);
+	else
+		error = gfs2_statfs_i(sdp, &sc);
+
+	if (error)
+		return error;
+
+	error = -ENOBUFS;
+
+	gfs2_printf("version 0\n");
+	gfs2_printf("bsize %u\n", sdp->sd_sb.sb_bsize);
+	gfs2_printf("total %"PRIu64"\n", sc.sc_total);
+	gfs2_printf("free %"PRIu64"\n", sc.sc_free);
+	gfs2_printf("dinodes %"PRIu64"\n", sc.sc_dinodes);
+
+	error = 0;
+
+ out:
+	return error;
+}
+
+/**
+ * handle_roll - Read a atomic_t as an unsigned int
+ * @a: a counter
+ *
+ * if @a is negative, reset it to zero
+ *
+ * Returns: the value of the counter
+ */
+
+static unsigned int handle_roll(atomic_t *a)
+{
+	int x = atomic_read(a);
+	if (x < 0) {
+		atomic_set(a, 0);
+		return 0;
+	}
+	return (unsigned int)x;
+}
+
+/**
+ * gi_get_counters - Return usage counters
+ * @ip:
+ * @gi:
+ * @buf:
+ * @size:
+ * @count:
+ *
+ * Returns: errno
+ */
+
+static int gi_get_counters(struct gfs2_inode *ip, struct gfs2_ioctl *gi,
+			   char *buf, unsigned int size, unsigned int *count)
+{
+	struct gfs2_sbd *sdp = ip->i_sbd;
+	int error = -ENOBUFS;
+
+	if (gi->gi_argc != 1)
+		return -EINVAL;
+
+	gfs2_printf("version 0\n");
+	gfs2_printf("sd_glock_count:locks::%d\n",
+		    atomic_read(&sdp->sd_glock_count));
+	gfs2_printf("sd_glock_held_count:locks held::%d\n",
+		    atomic_read(&sdp->sd_glock_held_count));
+	gfs2_printf("sd_inode_count:incore inodes::%d\n",
+		    atomic_read(&sdp->sd_inode_count));
+	gfs2_printf("sd_bufdata_count:metadata buffers::%d\n",
+		    atomic_read(&sdp->sd_bufdata_count));
+	gfs2_printf("sd_unlinked_count:unlinked inodes::%d\n",
+		    atomic_read(&sdp->sd_unlinked_count));
+	gfs2_printf("sd_quota_count:quota IDs::%d\n",
+		    atomic_read(&sdp->sd_quota_count));
+	gfs2_printf("sd_log_num_gl:Glocks in current transaction::%u\n",
+		    sdp->sd_log_num_gl);
+	gfs2_printf("sd_log_num_buf:Blocks in current transaction::%u\n",
+		    sdp->sd_log_num_buf);
+	gfs2_printf("sd_log_num_revoke:Revokes in current transaction::%u\n",
+		    sdp->sd_log_num_revoke);
+	gfs2_printf("sd_log_num_rg:RGs in current transaction::%u\n",
+		    sdp->sd_log_num_rg);
+	gfs2_printf("sd_log_num_databuf:Databufs in current transaction::%u\n",
+		    sdp->sd_log_num_databuf);
+	gfs2_printf("sd_log_blks_free:log blks free::%u\n",
+		    sdp->sd_log_blks_free);
+	gfs2_printf("jd_blocks:log blocks total::%u\n",
+		    sdp->sd_jdesc->jd_blocks);
+	gfs2_printf("sd_reclaim_count:glocks on reclaim list::%d\n",
+		    atomic_read(&sdp->sd_reclaim_count));
+	gfs2_printf("sd_log_wraps:log wraps::%"PRIu64"\n",
+		    sdp->sd_log_wraps);
+	gfs2_printf("sd_bio_outstanding:outstanding BIO calls::%u\n",
+		    atomic_read(&sdp->sd_bio_outstanding));
+	gfs2_printf("sd_fh2dentry_misses:fh2dentry misses:diff:%u\n",
+		    handle_roll(&sdp->sd_fh2dentry_misses));
+	gfs2_printf("sd_reclaimed:glocks reclaimed:diff:%u\n",
+		    handle_roll(&sdp->sd_reclaimed));
+	gfs2_printf("sd_log_flush_incore:log incore flushes:diff:%u\n",
+		    handle_roll(&sdp->sd_log_flush_incore));
+	gfs2_printf("sd_log_flush_ondisk:log ondisk flushes:diff:%u\n",
+		    handle_roll(&sdp->sd_log_flush_ondisk));
+	gfs2_printf("sd_glock_nq_calls:glock nq calls:diff:%u\n",
+		    handle_roll(&sdp->sd_glock_nq_calls));
+	gfs2_printf("sd_glock_dq_calls:glock dq calls:diff:%u\n",
+		    handle_roll(&sdp->sd_glock_dq_calls));
+	gfs2_printf("sd_glock_prefetch_calls:glock prefetch calls:diff:%u\n",
+		    handle_roll(&sdp->sd_glock_prefetch_calls));
+	gfs2_printf("sd_lm_lock_calls:lm_lock calls:diff:%u\n",
+		    handle_roll(&sdp->sd_lm_lock_calls));
+	gfs2_printf("sd_lm_unlock_calls:lm_unlock calls:diff:%u\n",
+		    handle_roll(&sdp->sd_lm_unlock_calls));
+	gfs2_printf("sd_lm_callbacks:lm callbacks:diff:%u\n",
+		    handle_roll(&sdp->sd_lm_callbacks));
+	gfs2_printf("sd_ops_address:address operations:diff:%u\n",
+		    handle_roll(&sdp->sd_ops_address));
+	gfs2_printf("sd_ops_dentry:dentry operations:diff:%u\n",
+		    handle_roll(&sdp->sd_ops_dentry));
+	gfs2_printf("sd_ops_export:export operations:diff:%u\n",
+		    handle_roll(&sdp->sd_ops_export));
+	gfs2_printf("sd_ops_file:file operations:diff:%u\n",
+		    handle_roll(&sdp->sd_ops_file));
+	gfs2_printf("sd_ops_inode:inode operations:diff:%u\n",
+		    handle_roll(&sdp->sd_ops_inode));
+	gfs2_printf("sd_ops_super:super operations:diff:%u\n",
+		    handle_roll(&sdp->sd_ops_super));
+	gfs2_printf("sd_ops_vm:vm operations:diff:%u\n",
+		    handle_roll(&sdp->sd_ops_vm));
+	gfs2_printf("sd_bio_reads:block I/O reads:diff:%u\n",
+		    handle_roll(&sdp->sd_bio_reads) >>
+		    (sdp->sd_sb.sb_bsize_shift - 9));
+	gfs2_printf("sd_bio_writes:block I/O writes:diff:%u\n",
+		    handle_roll(&sdp->sd_bio_writes) >>
+		    (sdp->sd_sb.sb_bsize_shift - 9));
+
+	error = 0;
+
+ out:
+	return error;
+}
+
+/**
+ * gi_get_tune - Return current values of the tuneable parameters
+ * @ip:
+ * @gi:
+ * @buf:
+ * @size:
+ * @count:
+ *
+ * Returns: errno
+ */
+
+static int gi_get_tune(struct gfs2_inode *ip, struct gfs2_ioctl *gi,
+		       char *buf, unsigned int size, unsigned int *count)
+{
+	struct gfs2_tune *gt = &ip->i_sbd->sd_tune;
+	int error = -ENOBUFS;
+
+	if (gi->gi_argc != 1)
+		return -EINVAL;
+
+	spin_lock(&gt->gt_spin);
+
+	gfs2_printf("version 0\n");
+	gfs2_printf("ilimit %u\n", gt->gt_ilimit);
+	gfs2_printf("ilimit_tries %u\n", gt->gt_ilimit_tries);
+	gfs2_printf("ilimit_min %u\n", gt->gt_ilimit_min);
+	gfs2_printf("demote_secs %u\n", gt->gt_demote_secs);
+	gfs2_printf("incore_log_blocks %u\n", gt->gt_incore_log_blocks);
+	gfs2_printf("log_flush_secs %u\n", gt->gt_log_flush_secs);
+	gfs2_printf("jindex_refresh_secs %u\n", gt->gt_jindex_refresh_secs);
+	gfs2_printf("scand_secs %u\n", gt->gt_scand_secs);
+	gfs2_printf("recoverd_secs %u\n", gt->gt_recoverd_secs);
+	gfs2_printf("logd_secs %u\n", gt->gt_logd_secs);
+	gfs2_printf("quotad_secs %u\n", gt->gt_quotad_secs);
+	gfs2_printf("inoded_secs %u\n", gt->gt_inoded_secs);
+	gfs2_printf("quota_simul_sync %u\n", gt->gt_quota_simul_sync);
+	gfs2_printf("quota_warn_period %u\n", gt->gt_quota_warn_period);
+	gfs2_printf("quota_scale_num %u\n", gt->gt_quota_scale_num);
+	gfs2_printf("quota_scale_den %u\n", gt->gt_quota_scale_den);
+	gfs2_printf("quota_cache_secs %u\n", gt->gt_quota_cache_secs);
+	gfs2_printf("quota_quantum %u\n", gt->gt_quota_quantum);
+	gfs2_printf("atime_quantum %u\n", gt->gt_atime_quantum);
+	gfs2_printf("new_files_jdata %u\n", gt->gt_new_files_jdata);
+	gfs2_printf("new_files_directio %u\n", gt->gt_new_files_directio);
+	gfs2_printf("max_atomic_write %u\n", gt->gt_max_atomic_write);
+	gfs2_printf("max_readahead %u\n", gt->gt_max_readahead);
+	gfs2_printf("lockdump_size %u\n", gt->gt_lockdump_size);
+	gfs2_printf("stall_secs %u\n", gt->gt_stall_secs);
+	gfs2_printf("complain_secs %u\n", gt->gt_complain_secs);
+	gfs2_printf("reclaim_limit %u\n", gt->gt_reclaim_limit);
+	gfs2_printf("entries_per_readdir %u\n", gt->gt_entries_per_readdir);
+	gfs2_printf("prefetch_secs %u\n", gt->gt_prefetch_secs);
+	gfs2_printf("greedy_default %u\n", gt->gt_greedy_default);
+	gfs2_printf("greedy_quantum %u\n", gt->gt_greedy_quantum);
+	gfs2_printf("greedy_max %u\n", gt->gt_greedy_max);
+	gfs2_printf("statfs_quantum %u\n", gt->gt_statfs_quantum);
+	gfs2_printf("statfs_slow %u\n", gt->gt_statfs_slow);
+
+	error = 0;
+
+ out:
+	spin_unlock(&gt->gt_spin);
+
+	return error;
+}
+
+#define tune_set(f, v) \
+do { \
+	spin_lock(&gt->gt_spin); \
+	gt->f = (v); \
+	spin_unlock(&gt->gt_spin); \
+} while (0)
+
+/**
+ * gi_set_tune - Set a tuneable parameter
+ * @sdp:
+ * @gi:
+ *
+ * Returns: errno
+ */
+
+static int gi_set_tune(struct gfs2_sbd *sdp, struct gfs2_ioctl *gi)
+{
+	struct gfs2_tune *gt = &sdp->sd_tune;
+	char param[ARG_SIZE], value[ARG_SIZE];
+	unsigned int x;
+
+	if (!capable(CAP_SYS_ADMIN))
+		return -EACCES;
+	if (gi->gi_argc != 3)
+		return -EINVAL;
+
+	if (strncpy_from_user(param, gi->gi_argv[1], ARG_SIZE) < 0)
+		return -EFAULT;
+	param[ARG_SIZE - 1] = 0;
+
+	if (strncpy_from_user(value, gi->gi_argv[2], ARG_SIZE) < 0)
+		return -EFAULT;
+	value[ARG_SIZE - 1] = 0;
+
+	if (strcmp(param, "ilimit") == 0) {
+		if (sscanf(value, "%u", &x) != 1)
+			return -EINVAL;
+		tune_set(gt_ilimit, x);
+
+	} else if (strcmp(param, "ilimit_tries") == 0) {
+		if (sscanf(value, "%u", &x) != 1)
+			return -EINVAL;
+		tune_set(gt_ilimit_tries, x);
+
+	} else if (strcmp(param, "ilimit_min") == 0) {
+		if (sscanf(value, "%u", &x) != 1)
+			return -EINVAL;
+		tune_set(gt_ilimit_min, x);
+
+	} else if (strcmp(param, "demote_secs") == 0) {
+		if (sscanf(value, "%u", &x) != 1)
+			return -EINVAL;
+		tune_set(gt_demote_secs, x);
+
+	} else if (strcmp(param, "incore_log_blocks") == 0) {
+		if (sscanf(value, "%u", &x) != 1)
+			return -EINVAL;
+		tune_set(gt_incore_log_blocks, x);
+
+	} else if (strcmp(param, "log_flush_secs") == 0) {
+		if (sscanf(value, "%u", &x) != 1)
+			return -EINVAL;
+		tune_set(gt_log_flush_secs, x);
+
+	} else if (strcmp(param, "jindex_refresh_secs") == 0) {
+		if (sscanf(value, "%u", &x) != 1)
+			return -EINVAL;
+		tune_set(gt_jindex_refresh_secs, x);
+
+	} else if (strcmp(param, "scand_secs") == 0) {
+		if (sscanf(value, "%u", &x) != 1 || !x)
+			return -EINVAL;
+		tune_set(gt_scand_secs, x);
+		wake_up_process(sdp->sd_scand_process);
+
+	} else if (strcmp(param, "recoverd_secs") == 0) {
+		if (sscanf(value, "%u", &x) != 1 || !x)
+			return -EINVAL;
+		tune_set(gt_recoverd_secs, x);
+		wake_up_process(sdp->sd_recoverd_process);
+
+	} else if (strcmp(param, "logd_secs") == 0) {
+		if (sscanf(value, "%u", &x) != 1 || !x)
+			return -EINVAL;
+		tune_set(gt_logd_secs, x);
+		wake_up_process(sdp->sd_logd_process);
+
+	} else if (strcmp(param, "quotad_secs") == 0) {
+		if (sscanf(value, "%u", &x) != 1 || !x)
+			return -EINVAL;
+		tune_set(gt_quotad_secs, x);
+		wake_up_process(sdp->sd_quotad_process);
+
+	} else if (strcmp(param, "inoded_secs") == 0) {
+		if (sscanf(value, "%u", &x) != 1 || !x)
+			return -EINVAL;
+		tune_set(gt_inoded_secs, x);
+		wake_up_process(sdp->sd_inoded_process);
+
+	} else if (strcmp(param, "quota_simul_sync") == 0) {
+		if (sscanf(value, "%u", &x) != 1 || !x)
+			return -EINVAL;
+		tune_set(gt_quota_simul_sync, x);
+
+	} else if (strcmp(param, "quota_warn_period") == 0) {
+		if (sscanf(value, "%u", &x) != 1)
+			return -EINVAL;
+		tune_set(gt_quota_warn_period, x);
+
+	} else if (strcmp(param, "quota_scale") == 0) {
+		unsigned int y;
+		if (sscanf(value, "%u %u", &x, &y) != 2 || !y)
+			return -EINVAL;
+		spin_lock(&gt->gt_spin);
+		gt->gt_quota_scale_num = x;
+		gt->gt_quota_scale_den = y;
+		spin_unlock(&gt->gt_spin);
+
+	} else if (strcmp(param, "quota_cache_secs") == 0) {
+		if (sscanf(value, "%u", &x) != 1 || !x)
+			return -EINVAL;
+		tune_set(gt_quota_cache_secs, x);
+
+	} else if (strcmp(param, "quota_quantum") == 0) {
+		if (sscanf(value, "%u", &x) != 1)
+			return -EINVAL;
+		tune_set(gt_quota_quantum, x);
+
+	} else if (strcmp(param, "atime_quantum") == 0) {
+		if (sscanf(value, "%u", &x) != 1)
+			return -EINVAL;
+		tune_set(gt_atime_quantum, x);
+
+	} else if (strcmp(param, "new_files_jdata") == 0) {
+		if (sscanf(value, "%u", &x) != 1)
+			return -EINVAL;
+		x = !!x;
+		tune_set(gt_new_files_jdata, x);
+
+	} else if (strcmp(param, "new_files_directio") == 0) {
+		if (sscanf(value, "%u", &x) != 1)
+			return -EINVAL;
+		x = !!x;
+		tune_set(gt_new_files_directio, x);
+
+	} else if (strcmp(param, "max_atomic_write") == 0) {
+		if (sscanf(value, "%u", &x) != 1 || !x)
+			return -EINVAL;
+		tune_set(gt_max_atomic_write, x);
+
+	} else if (strcmp(param, "max_readahead") == 0) {
+		if (sscanf(value, "%u", &x) != 1)
+			return -EINVAL;
+		tune_set(gt_max_readahead, x);
+
+	} else if (strcmp(param, "lockdump_size") == 0) {
+		if (sscanf(value, "%u", &x) != 1 || !x)
+			return -EINVAL;
+		tune_set(gt_lockdump_size, x);
+
+	} else if (strcmp(param, "stall_secs") == 0) {
+		if (sscanf(value, "%u", &x) != 1 || !x)
+			return -EINVAL;
+		tune_set(gt_stall_secs, x);
+
+	} else if (strcmp(param, "complain_secs") == 0) {
+		if (sscanf(value, "%u", &x) != 1)
+			return -EINVAL;
+		tune_set(gt_complain_secs, x);
+
+	} else if (strcmp(param, "reclaim_limit") == 0) {
+		if (sscanf(value, "%u", &x) != 1)
+			return -EINVAL;
+		tune_set(gt_reclaim_limit, x);
+
+	} else if (strcmp(param, "entries_per_readdir") == 0) {
+		if (sscanf(value, "%u", &x) != 1 || !x)
+			return -EINVAL;
+		tune_set(gt_entries_per_readdir, x);
+
+	} else if (strcmp(param, "prefetch_secs") == 0) {
+		if (sscanf(value, "%u", &x) != 1)
+			return -EINVAL;
+		tune_set(gt_prefetch_secs, x);
+
+	} else if (strcmp(param, "greedy_default") == 0) {
+		if (sscanf(value, "%u", &x) != 1 || !x)
+			return -EINVAL;
+		tune_set(gt_greedy_default, x);
+
+	} else if (strcmp(param, "greedy_quantum") == 0) {
+		if (sscanf(value, "%u", &x) != 1 || !x)
+			return -EINVAL;
+		tune_set(gt_greedy_quantum, x);
+
+	} else if (strcmp(param, "greedy_max") == 0) {
+		if (sscanf(value, "%u", &x) != 1 || !x)
+			return -EINVAL;
+		tune_set(gt_greedy_max, x);
+
+	} else if (strcmp(param, "statfs_quantum") == 0) {
+		if (sscanf(value, "%u", &x) != 1 || !x)
+			return -EINVAL;
+		tune_set(gt_statfs_quantum, x);
+
+	} else if (strcmp(param, "statfs_slow") == 0) {
+		if (sscanf(value, "%u", &x) != 1)
+			return -EINVAL;
+		tune_set(gt_statfs_slow, x);
+
+	} else
+		return -EINVAL;
+
+	return 0;
+}
+
+/**
+ * gi_do_shrink - throw out unused glocks
+ * @sdp:
+ * @gi:
+ *
+ * Returns: 0
+ */
+
+static int gi_do_shrink(struct gfs2_sbd *sdp, struct gfs2_ioctl *gi)
+{
+	if (!capable(CAP_SYS_ADMIN))
+		return -EACCES;
+	if (gi->gi_argc != 1)
+		return -EINVAL;
+	gfs2_gl_hash_clear(sdp, NO_WAIT);
+	return 0;
+}
+
+/**
+ * gi_get_file_stat -
+ * @ip:
+ * @gi:
+ *
+ * Returns: the number of bytes copied, or -errno
+ */
+
+static int gi_get_file_stat(struct gfs2_inode *ip, struct gfs2_ioctl *gi)
+{
+	struct gfs2_holder i_gh;
+	struct gfs2_dinode *di;
+	int error;
+
+	if (gi->gi_argc != 1)
+		return -EINVAL;
+	if (gi->gi_size != sizeof(struct gfs2_dinode))
+		return -EINVAL;
+
+	di = kmalloc(sizeof(struct gfs2_dinode), GFP_KERNEL);
+	if (!di)
+		return -ENOMEM;
+
+	error = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED, LM_FLAG_ANY, &i_gh);
+	if (error)
+		goto out;
+	memcpy(di, &ip->i_di, sizeof(struct gfs2_dinode));
+	gfs2_glock_dq_uninit(&i_gh);
+
+	if (copy_to_user(gi->gi_data, di,
+			 sizeof(struct gfs2_dinode)))
+		error = -EFAULT;
+	else
+		error = sizeof(struct gfs2_dinode);
+
+ out:
+	kfree(di);
+
+	return error;
+}
+
+/**
+ * gi_set_file_flag - set or clear a flag on a file
+ * @ip:
+ * @gi:
+ *
+ * Returns: errno
+ */
+
+static int gi_set_file_flag(struct gfs2_inode *ip, struct gfs2_ioctl *gi)
+{
+	char buf[ARG_SIZE];
+	int set;
+	uint32_t flag;
+	struct gfs2_holder i_gh;
+	struct buffer_head *dibh;
+	int error;
+
+	if (gi->gi_argc != 3)
+		return -EINVAL;
+
+	if (strncpy_from_user(buf, gi->gi_argv[1], ARG_SIZE) < 0)
+		return -EFAULT;
+	buf[ARG_SIZE - 1] = 0;
+
+	if (strcmp(buf, "set") == 0)
+		set = TRUE;
+	else if (strcmp(buf, "clear") == 0)
+		set = FALSE;
+	else
+		return -EINVAL;
+
+	if (strncpy_from_user(buf, gi->gi_argv[2], ARG_SIZE) < 0)
+		return -EFAULT;
+	buf[ARG_SIZE - 1] = 0;
+
+	error = gfs2_glock_nq_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, &i_gh);
+	if (error)
+		return error;
+
+	error = -EACCES;
+	if (ip->i_di.di_uid != current->fsuid && !capable(CAP_FOWNER))
+		goto out;
+
+	error = -EINVAL;
+
+	if (strcmp(buf, "jdata") == 0) {
+		if (!S_ISREG(ip->i_di.di_mode) || ip->i_di.di_size)
+			goto out;
+		flag = GFS2_DIF_JDATA;
+	} else if (strcmp(buf, "directio") == 0) {
+		if (!S_ISREG(ip->i_di.di_mode))
+			goto out;
+		flag = GFS2_DIF_DIRECTIO;
+	} else if (strcmp(buf, "immutable") == 0) {
+		/* The IMMUTABLE flag can only be changed by
+		   the relevant capability. */
+		error = -EPERM;
+		if (!capable(CAP_LINUX_IMMUTABLE))
+			goto out;
+		flag = GFS2_DIF_IMMUTABLE;
+	} else if (strcmp(buf, "appendonly") == 0) {
+		/* The APPENDONLY flag can only be changed by
+		   the relevant capability. */
+		error = -EPERM;
+		if (!capable(CAP_LINUX_IMMUTABLE))
+			goto out;
+		flag = GFS2_DIF_APPENDONLY;
+	} else if (strcmp(buf, "inherit_jdata") == 0) {
+		if (!S_ISDIR(ip->i_di.di_mode))
+			goto out;
+		flag = GFS2_DIF_INHERIT_JDATA;
+	} else if (strcmp(buf, "inherit_directio") == 0) {
+		if (S_ISDIR(ip->i_di.di_mode))
+			goto out;
+		flag = GFS2_DIF_INHERIT_DIRECTIO;
+	} else
+		goto out;
+
+	error = gfs2_trans_begin(ip->i_sbd, RES_DINODE, 0);
+	if (error)
+		goto out;
+
+	error = gfs2_meta_inode_buffer(ip, &dibh);
+	if (error)
+		goto out_trans_end;
+
+	if (set)
+		ip->i_di.di_flags |= flag;
+	else
+		ip->i_di.di_flags &= ~flag;
+
+	gfs2_trans_add_bh(ip->i_gl, dibh);
+	gfs2_dinode_out(&ip->i_di, dibh->b_data);
+
+	brelse(dibh);
+
+ out_trans_end:
+	gfs2_trans_end(ip->i_sbd);
+
+ out:
+	gfs2_glock_dq_uninit(&i_gh);
+
+	return error;
+
+}
+
+static int gi_get_bmap(struct gfs2_inode *ip, struct gfs2_ioctl *gi)
+{
+	struct gfs2_holder gh;
+	uint64_t lblock, dblock = 0;
+	int new = FALSE;
+	int error;
+
+	if (gi->gi_argc != 1)
+		return -EINVAL;
+	if (gi->gi_size != sizeof(uint64_t))
+		return -EINVAL;
+
+	error = copy_from_user(&lblock, gi->gi_data, sizeof(uint64_t));
+	if (error)
+		return -EFAULT;
+
+	error = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED, LM_FLAG_ANY, &gh);
+	if (error)
+		return error;
+
+	error = -EACCES;
+	if (ip->i_di.di_uid == current->fsuid || capable(CAP_FOWNER)) {
+		error = 0;
+		if (!gfs2_is_stuffed(ip))
+			error = gfs2_block_map(ip, lblock, &new, &dblock, NULL);
+	}
+
+	gfs2_glock_dq_uninit(&gh);
+
+	if (!error) {
+		error = copy_to_user(gi->gi_data, &dblock, sizeof(uint64_t));
+		if (error)
+			error = -EFAULT;
+	}
+
+	return error;
+}
+
+/**
+ * gi_get_file_meta - Return all the metadata for a file
+ * @ip:
+ * @gi:
+ *
+ * Returns: the number of bytes copied, or -errno
+ */
+
+static int gi_get_file_meta(struct gfs2_inode *ip, struct gfs2_ioctl *gi)
+{
+	struct gfs2_holder i_gh;
+	struct gfs2_user_buffer ub;
+	int error;
+
+	if (gi->gi_argc != 1)
+		return -EINVAL;
+
+	ub.ub_data = gi->gi_data;
+	ub.ub_size = gi->gi_size;
+	ub.ub_count = 0;
+
+	error = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED, LM_FLAG_ANY, &i_gh);
+	if (error)
+		return error;
+
+	error = -EACCES;
+	if (ip->i_di.di_uid != current->fsuid && !capable(CAP_FOWNER))
+		goto out;
+
+	error = gfs2_get_file_meta(ip, &ub);
+	if (error)
+		goto out;
+
+	if (S_ISDIR(ip->i_di.di_mode) &&
+	    (ip->i_di.di_flags & GFS2_DIF_EXHASH)) {
+		error = gfs2_get_dir_meta(ip, &ub);
+		if (error)
+			goto out;
+	}
+
+	if (ip->i_di.di_eattr) {
+		error = gfs2_get_eattr_meta(ip, &ub);
+		if (error)
+			goto out;
+	}
+
+	error = ub.ub_count;
+
+ out:
+	gfs2_glock_dq_uninit(&i_gh);
+
+	return error;
+}
+
+/**
+ * gi_do_file_flush - sync out all dirty data and
+ *		    drop the cache (and lock) for a file.
+ * @ip:
+ * @gi:
+ *
+ * Returns: errno
+ */
+
+static int gi_do_file_flush(struct gfs2_inode *ip, struct gfs2_ioctl *gi)
+{
+	if (gi->gi_argc != 1)
+		return -EINVAL;
+	gfs2_glock_force_drop(ip->i_gl);
+	return 0;
+}
+
+/**
+ * gi2hip - return the "struct gfs2_inode" for a hidden file
+ * @sdp:
+ * @gi:
+ *
+ * Returns: the "struct gfs2_inode"
+ */
+
+static struct gfs2_inode *gi2hip(struct gfs2_sbd *sdp, struct gfs2_ioctl *gi)
+{
+	char buf[ARG_SIZE];
+
+	if (gi->gi_argc != 2)
+		return ERR_PTR(-EINVAL);
+
+	if (strncpy_from_user(buf, gi->gi_argv[1], ARG_SIZE) < 0)
+		return ERR_PTR(-EFAULT);
+	buf[ARG_SIZE - 1] = 0;
+
+	if (strcmp(buf, "jindex") == 0)
+		return sdp->sd_jindex;
+	if (strcmp(buf, "rindex") == 0)
+		return sdp->sd_rindex;
+	if (strcmp(buf, "quota") == 0)
+		return sdp->sd_quota_inode;
+
+	return ERR_PTR(-EINVAL);
+}
+
+/**
+ * gi_get_hfile_stat - get stat info on a hidden file
+ * @sdp:
+ * @gi:
+ *
+ * Returns: the number of bytes copied, or -errno
+ */
+
+static int gi_get_hfile_stat(struct gfs2_sbd *sdp, struct gfs2_ioctl *gi)
+{
+	struct gfs2_inode *ip;
+	struct gfs2_dinode *di;
+	struct gfs2_holder i_gh;
+	int error;
+
+	ip = gi2hip(sdp, gi);
+	if (IS_ERR(ip))
+		return PTR_ERR(ip);
+
+	if (gi->gi_size != sizeof(struct gfs2_dinode))
+		return -EINVAL;
+
+	di = kmalloc(sizeof(struct gfs2_dinode), GFP_KERNEL);
+	if (!di)
+		return -ENOMEM;
+
+	error = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED, LM_FLAG_ANY, &i_gh);
+	if (error)
+		goto out;
+	memcpy(di, &ip->i_di, sizeof(struct gfs2_dinode));
+	gfs2_glock_dq_uninit(&i_gh);
+
+	if (copy_to_user(gi->gi_data, di,
+			 sizeof(struct gfs2_dinode)))
+		error = -EFAULT;
+	else
+		error = sizeof(struct gfs2_dinode);
+
+ out:
+	kfree(di);
+
+	return error;
+}
+
+/**
+ * gi_do_hfile_read - Read data from a hidden file
+ * @sdp:
+ * @gi:
+ *
+ * Returns: the number of bytes read, or -errno
+ */
+
+static int gi_do_hfile_read(struct gfs2_sbd *sdp, struct gfs2_ioctl *gi)
+{
+	struct gfs2_inode *ip;
+	struct gfs2_holder i_gh;
+	int error;
+
+	if (!capable(CAP_SYS_ADMIN))
+		return -EACCES;
+
+	ip = gi2hip(sdp, gi);
+	if (IS_ERR(ip))
+		return PTR_ERR(ip);
+
+	if (!S_ISREG(ip->i_di.di_mode))
+		return -EINVAL;
+
+	if (!access_ok(VERIFY_WRITE, gi->gi_data, gi->gi_size))
+		return -EFAULT;
+
+	error = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED, 0, &i_gh);
+	if (error)
+		return error;
+
+	error = gfs2_jdata_read(ip, gi->gi_data, gi->gi_offset, gi->gi_size,
+				gfs2_copy2user);
+
+	gfs2_glock_dq_uninit(&i_gh);
+
+	return error;
+}
+
+/**
+ * gi_do_hfile_write - Write data to a hidden file
+ * @sdp:
+ * @gi:
+ *
+ * Returns: the number of bytes written, or -errno
+ */
+
+static int gi_do_hfile_write(struct gfs2_sbd *sdp, struct gfs2_ioctl *gi)
+{
+	struct gfs2_inode *ip;
+	struct gfs2_alloc *al = NULL;
+	struct gfs2_holder i_gh;
+	unsigned int data_blocks, ind_blocks;
+	int alloc_required;
+	int error;
+
+	if (!capable(CAP_SYS_ADMIN))
+		return -EACCES;
+
+	ip = gi2hip(sdp, gi);
+	if (IS_ERR(ip))
+		return PTR_ERR(ip);
+
+	if (!S_ISREG(ip->i_di.di_mode))
+		return -EINVAL;
+
+	if (!access_ok(VERIFY_READ, gi->gi_data, gi->gi_size))
+		return -EFAULT;
+
+	gfs2_write_calc_reserv(ip, gi->gi_size, &data_blocks, &ind_blocks);
+
+	error = gfs2_glock_nq_init(ip->i_gl, LM_ST_EXCLUSIVE,
+				   LM_FLAG_PRIORITY, &i_gh);
+	if (error)
+		return error;
+
+	if (!gfs2_is_jdata(ip)) {
+		gfs2_consist_inode(ip);
+		error = -EIO;
+		goto out;
+	}
+
+	error = gfs2_write_alloc_required(ip, gi->gi_offset, gi->gi_size,
+					  &alloc_required);
+	if (error)
+		goto out;
+
+	if (alloc_required) {
+		al = gfs2_alloc_get(ip);
+
+		al->al_requested = data_blocks + ind_blocks;
+
+		error = gfs2_inplace_reserve(ip);
+		if (error)
+			goto out_alloc;
+
+		error = gfs2_trans_begin(sdp,
+					 al->al_rgd->rd_ri.ri_length +
+					 data_blocks + ind_blocks +
+					 RES_DINODE + RES_STATFS, 0);
+		if (error)
+			goto out_relse;
+	} else {
+		error = gfs2_trans_begin(sdp, data_blocks + RES_DINODE, 0);
+		if (error)
+			goto out;
+	}
+
+	error = gfs2_jdata_write(ip, gi->gi_data, gi->gi_offset, gi->gi_size,
+				 gfs2_copy_from_user);
+
+	gfs2_trans_end(sdp);
+
+ out_relse:
+	if (alloc_required)
+		gfs2_inplace_release(ip);
+
+ out_alloc:
+	if (alloc_required)
+		gfs2_alloc_put(ip);
+
+ out:
+	gfs2_glock_dq_uninit(&i_gh);
+
+	return error;
+}
+
+/**
+ * gi_do_hfile_trunc - truncate a hidden file
+ * @sdp:
+ * @gi:
+ *
+ * Returns: the number of bytes copied, or -errno
+ */
+
+static int gi_do_hfile_trunc(struct gfs2_sbd *sdp, struct gfs2_ioctl *gi)
+{
+	struct gfs2_inode *ip;
+	struct gfs2_holder i_gh;
+	int error;
+
+	if (!capable(CAP_SYS_ADMIN))
+		return -EACCES;
+
+	ip = gi2hip(sdp, gi);
+	if (IS_ERR(ip))
+		return PTR_ERR(ip);
+
+	if (!S_ISREG(ip->i_di.di_mode))
+		return -EINVAL;
+
+	error = gfs2_glock_nq_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, &i_gh);
+	if (error)
+		return error;
+
+	error = gfs2_truncatei(ip, gi->gi_offset, NULL);
+
+	gfs2_glock_dq_uninit(&i_gh);
+
+	return error;
+}
+
+/**
+ * gi_do_quota_sync - sync the outstanding quota changes for a FS
+ * @sdp:
+ * @gi:
+ *
+ * Returns: errno
+ */
+
+static int gi_do_quota_sync(struct gfs2_sbd *sdp, struct gfs2_ioctl *gi)
+{
+	if (!capable(CAP_SYS_ADMIN))
+		return -EACCES;
+	if (gi->gi_argc != 1)
+		return -EINVAL;
+	return gfs2_quota_sync(sdp);
+}
+
+/**
+ * gi_do_quota_refresh - Refresh the a quota LVB from the quota file
+ * @sdp:
+ * @gi:
+ *
+ * Returns: errno
+ */
+
+static int gi_do_quota_refresh(struct gfs2_sbd *sdp, struct gfs2_ioctl *gi)
+{
+	char buf[ARG_SIZE];
+	int user;
+	uint32_t id;
+
+	if (!capable(CAP_SYS_ADMIN))
+		return -EACCES;
+	if (gi->gi_argc != 2)
+		return -EINVAL;
+
+	if (strncpy_from_user(buf, gi->gi_argv[1], ARG_SIZE) < 0)
+		return -EFAULT;
+	buf[ARG_SIZE - 1] = 0;
+
+	switch (buf[0]) {
+	case 'u':
+		user = TRUE;
+		break;
+	case 'g':
+		user = FALSE;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (buf[1] != ':')
+		return -EINVAL;
+
+	if (sscanf(buf + 2, "%u", &id) != 1)
+		return -EINVAL;
+
+	return gfs2_quota_refresh(sdp, user, id);
+}
+
+/**
+ * gi_do_quota_read - read quota values from the quota file
+ * @sdp:
+ * @gi:
+ *
+ * Returns: errno
+ */
+
+static int gi_do_quota_read(struct gfs2_sbd *sdp, struct gfs2_ioctl *gi)
+{
+	char buf[ARG_SIZE];
+	int user;
+	uint32_t id;
+	struct gfs2_quota q;
+	int error;
+
+	if (gi->gi_argc != 2)
+		return -EINVAL;
+	if (gi->gi_size != sizeof(struct gfs2_quota))
+		return -EINVAL;
+
+	if (strncpy_from_user(buf, gi->gi_argv[1], ARG_SIZE) < 0)
+		return -EFAULT;
+	buf[ARG_SIZE - 1] = 0;
+
+	switch (buf[0]) {
+	case 'u':
+		user = TRUE;
+		break;
+	case 'g':
+		user = FALSE;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (buf[1] != ':')
+		return -EINVAL;
+
+	if (sscanf(buf + 2, "%u", &id) != 1)
+		return -EINVAL;
+
+	error = gfs2_quota_read(sdp, user, id, &q);
+	if (error)
+		return error;
+
+	if (copy_to_user(gi->gi_data, &q, sizeof(struct gfs2_quota)))
+		return -EFAULT;
+
+	return 0;
+}
+
+/**
+ * gi_do_statfs_sync - sync the outstanding statfs changes for a FS
+ * @sdp:
+ * @gi:
+ *
+ * Returns: errno
+ */
+
+static int gi_do_statfs_sync(struct gfs2_sbd *sdp, struct gfs2_ioctl *gi)
+{
+	if (!capable(CAP_SYS_ADMIN))
+		return -EACCES;
+	if (gi->gi_argc != 1)
+		return -EINVAL;
+	return gfs2_statfs_sync(sdp);
+}
+
+static int gi_resize_add_rgrps(struct gfs2_sbd *sdp, struct gfs2_ioctl *gi)
+{
+	if (!capable(CAP_SYS_ADMIN))
+		return -EACCES;
+	if (gi->gi_argc != 1)
+		return -EINVAL;
+	if (gi->gi_size % sizeof(struct gfs2_rindex))
+		return -EINVAL;
+
+	return gfs2_resize_add_rgrps(sdp, gi->gi_data, gi->gi_size);
+}
+
+static int gi_rename2system(struct gfs2_sbd *sdp, struct gfs2_ioctl *gi)
+{
+	char new_dir[ARG_SIZE], new_name[ARG_SIZE];
+	struct gfs2_inode *old_dip, *ip, *new_dip;
+	int put_new_dip = FALSE;
+	int error;
+
+	if (!capable(CAP_SYS_ADMIN))
+		return -EACCES;
+	if (gi->gi_argc != 3)
+		return -EINVAL;
+
+	if (strncpy_from_user(new_dir, gi->gi_argv[1], ARG_SIZE) < 0)
+		return -EFAULT;
+	new_dir[ARG_SIZE - 1] = 0;
+	if (strncpy_from_user(new_name, gi->gi_argv[2], ARG_SIZE) < 0)
+		return -EFAULT;
+	new_name[ARG_SIZE - 1] = 0;
+
+	error = gfs2_lookup_simple(sdp->sd_root_dir, ".gfs2_admin", &old_dip);
+	if (error)
+		return error;
+
+	error = -ENOTDIR;
+	if (!S_ISDIR(old_dip->i_di.di_mode))
+		goto out;
+
+	error = gfs2_lookup_simple(old_dip, "new_inode", &ip);
+	if (error)
+		goto out;
+
+	if (!strcmp(new_dir, "per_node")) {
+		error = gfs2_lookup_simple(sdp->sd_master_dir, "per_node",
+					   &new_dip);
+		if (error)
+			goto out2;
+		put_new_dip = TRUE;
+	} else if (!strcmp(new_dir, "jindex"))
+		new_dip = sdp->sd_jindex;
+	else {
+		error = -EINVAL;
+		goto out2;
+	}
+
+	error = gfs2_rename2system(ip, old_dip, "new_inode", new_dip, new_name);
+
+	if (put_new_dip)
+		gfs2_inode_put(new_dip);
+
+ out2:
+	gfs2_inode_put(ip);
+	
+ out:
+	gfs2_inode_put(old_dip);
+
+	return error;
+}
+
+/**
+ * gfs2_ioctl_i -
+ * @ip:
+ * @arg:
+ *
+ * Returns: -errno or positive byte count
+ */
+
+int gfs2_ioctl_i(struct gfs2_inode *ip, void *arg)
+{
+	struct gfs2_ioctl *gi_user = (struct gfs2_ioctl *)arg;
+	struct gfs2_ioctl gi;
+	char **argv;
+	char arg0[ARG_SIZE];
+	int error = -EFAULT;
+
+	if (copy_from_user(&gi, gi_user, sizeof(struct gfs2_ioctl)))
+		return -EFAULT;
+	if (!gi.gi_argc)
+		return -EINVAL;
+	argv = kcalloc(gi.gi_argc, sizeof(char *), GFP_KERNEL);
+	if (!argv)
+		return -ENOMEM;
+	if (copy_from_user(argv, gi.gi_argv,
+			   gi.gi_argc * sizeof(char *)))
+		goto out;
+	gi.gi_argv = argv;
+
+	if (strncpy_from_user(arg0, argv[0], ARG_SIZE) < 0)
+		goto out;
+	arg0[ARG_SIZE - 1] = 0;
+
+	if (strcmp(arg0, "get_cookie") == 0)
+		error = gi_skeleton(ip, &gi, gi_get_cookie);
+	else if (strcmp(arg0, "get_super") == 0)
+		error = gi_get_super(ip->i_sbd, &gi);
+	else if (strcmp(arg0, "get_args") == 0)
+		error = gi_skeleton(ip, &gi, gi_get_args);
+	else if (strcmp(arg0, "get_lockstruct") == 0)
+		error = gi_skeleton(ip, &gi, gi_get_lockstruct);
+	else if (strcmp(arg0, "get_statfs") == 0)
+		error = gi_skeleton(ip, &gi, gi_get_statfs);
+	else if (strcmp(arg0, "get_counters") == 0)
+		error = gi_skeleton(ip, &gi, gi_get_counters);
+	else if (strcmp(arg0, "get_tune") == 0)
+		error = gi_skeleton(ip, &gi, gi_get_tune);
+	else if (strcmp(arg0, "set_tune") == 0)
+		error = gi_set_tune(ip->i_sbd, &gi);
+	else if (strcmp(arg0, "do_shrink") == 0)
+		error = gi_do_shrink(ip->i_sbd, &gi);
+	else if (strcmp(arg0, "get_file_stat") == 0)
+		error = gi_get_file_stat(ip, &gi);
+	else if (strcmp(arg0, "set_file_flag") == 0)
+		error = gi_set_file_flag(ip, &gi);
+	else if (strcmp(arg0, "get_bmap") == 0)
+		error = gi_get_bmap(ip, &gi);
+	else if (strcmp(arg0, "get_file_meta") == 0)
+		error = gi_get_file_meta(ip, &gi);
+	else if (strcmp(arg0, "do_file_flush") == 0)
+		error = gi_do_file_flush(ip, &gi);
+	else if (strcmp(arg0, "get_hfile_stat") == 0)
+		error = gi_get_hfile_stat(ip->i_sbd, &gi);
+	else if (strcmp(arg0, "do_hfile_read") == 0)
+		error = gi_do_hfile_read(ip->i_sbd, &gi);
+	else if (strcmp(arg0, "do_hfile_write") == 0)
+		error = gi_do_hfile_write(ip->i_sbd, &gi);
+	else if (strcmp(arg0, "do_hfile_trunc") == 0)
+		error = gi_do_hfile_trunc(ip->i_sbd, &gi);
+	else if (strcmp(arg0, "do_quota_sync") == 0)
+		error = gi_do_quota_sync(ip->i_sbd, &gi);
+	else if (strcmp(arg0, "do_quota_refresh") == 0)
+		error = gi_do_quota_refresh(ip->i_sbd, &gi);
+	else if (strcmp(arg0, "do_quota_read") == 0)
+		error = gi_do_quota_read(ip->i_sbd, &gi);
+	else if (strcmp(arg0, "do_statfs_sync") == 0)
+		error = gi_do_statfs_sync(ip->i_sbd, &gi);
+	else if (strcmp(arg0, "resize_add_rgrps") == 0)
+		error = gi_resize_add_rgrps(ip->i_sbd, &gi);
+	else if (strcmp(arg0, "rename2system") == 0)
+		error = gi_rename2system(ip->i_sbd, &gi);
+	else
+		error = -ENOTTY;
+
+ out:
+	kfree(argv);
+
+	return error;
+}
+
--- a/fs/gfs2/ioctl.h	1970-01-01 07:30:00.000000000 +0730
+++ b/fs/gfs2/ioctl.h	2005-09-01 17:36:55.324114104 +0800
@@ -0,0 +1,15 @@
+/*
+ * Copyright (C) Sistina Software, Inc.  1997-2003 All rights reserved.
+ * Copyright (C) 2004-2005 Red Hat, Inc.  All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU General Public License v.2.
+ */
+
+#ifndef __IOCTL_DOT_H__
+#define __IOCTL_DOT_H__
+
+int gfs2_ioctl_i(struct gfs2_inode *ip, void *arg);
+
+#endif /* __IOCTL_DOT_H__ */
--- a/fs/gfs2/sys.c	1970-01-01 07:30:00.000000000 +0730
+++ b/fs/gfs2/sys.c	2005-09-01 17:36:55.507086288 +0800
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) Sistina Software, Inc.  1997-2003 All rights reserved.
+ * Copyright (C) 2004-2005 Red Hat, Inc.  All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU General Public License v.2.
+ */
+
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/spinlock.h>
+#include <linux/completion.h>
+#include <linux/buffer_head.h>
+#include <linux/module.h>
+#include <linux/kobject.h>
+#include <asm/semaphore.h>
+#include <asm/uaccess.h>
+
+#include "gfs2.h"
+#include "lm.h"
+#include "sys.h"
+#include "super.h"
+
+char *gfs2_sys_margs;
+spinlock_t gfs2_sys_margs_lock;
+
+static ssize_t gfs2_id_show(struct gfs2_sbd *sdp, char *buf)
+{
+	return sprintf(buf, "%s\n", sdp->sd_vfs->s_id);
+}
+
+static ssize_t gfs2_fsname_show(struct gfs2_sbd *sdp, char *buf)
+{
+	return sprintf(buf, "%s\n", sdp->sd_fsname);
+}
+
+static ssize_t gfs2_freeze_show(struct gfs2_sbd *sdp, char *buf)
+{
+	unsigned int count;
+
+	down(&sdp->sd_freeze_lock);
+	count = sdp->sd_freeze_count;
+	up(&sdp->sd_freeze_lock);
+
+	return sprintf(buf, "%u\n", count);
+}
+
+static ssize_t gfs2_freeze_store(struct gfs2_sbd *sdp, const char *buf,
+				 size_t len)
+{
+	ssize_t ret = len;
+	int error = 0;
+	int n = simple_strtol(buf, NULL, 0);
+
+	switch (n) {
+	case 0:
+		gfs2_unfreeze_fs(sdp);
+		break;
+	case 1:
+		error = gfs2_freeze_fs(sdp);
+		break;
+	default:
+		ret = -EINVAL;
+	}
+
+	if (error)
+		fs_warn(sdp, "freeze %d error %d", n, error);
+
+	return ret;
+}
+
+static ssize_t gfs2_withdraw_show(struct gfs2_sbd *sdp, char *buf)
+{
+	unsigned int b = test_bit(SDF_SHUTDOWN, &sdp->sd_flags);
+	return sprintf(buf, "%u\n", b);
+}
+
+static ssize_t gfs2_withdraw_store(struct gfs2_sbd *sdp, const char *buf,
+				   size_t len)
+{
+	ssize_t ret = len;
+	int n = simple_strtol(buf, NULL, 0);
+
+	if (n != 1) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	gfs2_lm_withdraw(sdp,
+		"GFS2: fsid=%s: withdrawing from cluster at user's request\n",
+		sdp->sd_fsname);
+ out:
+	return ret;
+}
+
+struct gfs2_attr {
+	struct attribute attr;
+	ssize_t (*show)(struct gfs2_sbd*, char *);
+	ssize_t (*store)(struct gfs2_sbd*, const char *, size_t);
+};
+
+static struct gfs2_attr gfs2_attr_id = {
+	.attr = {.name = "id", .mode = S_IRUSR},
+	.show = gfs2_id_show
+};
+
+static struct gfs2_attr gfs2_attr_fsname = {
+	.attr = {.name = "fsname", .mode = S_IRUSR},
+	.show = gfs2_fsname_show
+};
+
+static struct gfs2_attr gfs2_attr_freeze = {
+	.attr  = {.name = "freeze", .mode = S_IRUSR | S_IWUSR},
+	.show  = gfs2_freeze_show,
+	.store = gfs2_freeze_store
+};
+
+static struct gfs2_attr gfs2_attr_withdraw = {
+	.attr  = {.name = "withdraw", .mode = S_IRUSR | S_IWUSR},
+	.show  = gfs2_withdraw_show,
+	.store = gfs2_withdraw_store
+};
+
+static struct attribute *gfs2_attrs[] = {
+	&gfs2_attr_id.attr,
+	&gfs2_attr_fsname.attr,
+	&gfs2_attr_freeze.attr,
+	&gfs2_attr_withdraw.attr,
+	NULL,
+};
+
+static ssize_t gfs2_attr_show(struct kobject *kobj, struct attribute *attr,
+			      char *buf)
+{
+	struct gfs2_sbd *sdp = container_of(kobj, struct gfs2_sbd, sd_kobj);
+	struct gfs2_attr *a = container_of(attr, struct gfs2_attr, attr);
+	return a->show ? a->show(sdp, buf) : 0;
+}
+
+static ssize_t gfs2_attr_store(struct kobject *kobj, struct attribute *attr,
+			       const char *buf, size_t len)
+{
+	struct gfs2_sbd *sdp = container_of(kobj, struct gfs2_sbd, sd_kobj);
+	struct gfs2_attr *a = container_of(attr, struct gfs2_attr, attr);
+	return a->store ? a->store(sdp, buf, len) : len;
+}
+
+static struct sysfs_ops gfs2_attr_ops = {
+	.show  = gfs2_attr_show,
+	.store = gfs2_attr_store,
+};
+
+static struct kobj_type gfs2_ktype = {
+	.default_attrs = gfs2_attrs,
+	.sysfs_ops     = &gfs2_attr_ops,
+};
+
+/* FIXME: should this go under /sys/fs/ ? */
+
+static struct kset gfs2_kset = {
+	.subsys = &kernel_subsys,
+	.kobj   = {.name = "gfs2",},
+	.ktype  = &gfs2_ktype,
+};
+
+int gfs2_sys_fs_add(struct gfs2_sbd *sdp)
+{
+	int error;
+
+	error = kobject_set_name(&sdp->sd_kobj, "%s", sdp->sd_fsname);
+	if (error)
+		goto out;
+
+	sdp->sd_kobj.kset = &gfs2_kset;
+	sdp->sd_kobj.ktype = &gfs2_ktype;
+
+	error = kobject_register(&sdp->sd_kobj);
+ out:
+	return error;
+}
+
+void gfs2_sys_fs_del(struct gfs2_sbd *sdp)
+{
+	kobject_unregister(&sdp->sd_kobj);
+}
+
+int gfs2_sys_init(void)
+{
+	gfs2_sys_margs = NULL;
+	spin_lock_init(&gfs2_sys_margs_lock);
+	return kset_register(&gfs2_kset);
+}
+
+void gfs2_sys_uninit(void)
+{
+	kfree(gfs2_sys_margs);
+	kset_unregister(&gfs2_kset);
+}
+
--- a/fs/gfs2/sys.h	1970-01-01 07:30:00.000000000 +0730
+++ b/fs/gfs2/sys.h	2005-09-01 17:36:55.517084768 +0800
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) Sistina Software, Inc.  1997-2003 All rights reserved.
+ * Copyright (C) 2004-2005 Red Hat, Inc.  All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU General Public License v.2.
+ */
+
+#ifndef __SYS_DOT_H__
+#define __SYS_DOT_H__
+
+/* Allow args to be passed to GFS2 when using an initial ram disk */
+extern char *gfs2_sys_margs;
+extern spinlock_t gfs2_sys_margs_lock;
+
+int gfs2_sys_fs_add(struct gfs2_sbd *sdp);
+void gfs2_sys_fs_del(struct gfs2_sbd *sdp);
+
+int gfs2_sys_init(void);
+void gfs2_sys_uninit(void);
+
+#endif /* __SYS_DOT_H__ */
+
--- a/fs/gfs2/resize.c	1970-01-01 07:30:00.000000000 +0730
+++ b/fs/gfs2/resize.c	2005-09-01 17:36:55.452094648 +0800
@@ -0,0 +1,285 @@
+/*
+ * Copyright (C) Sistina Software, Inc.  1997-2003 All rights reserved.
+ * Copyright (C) 2004-2005 Red Hat, Inc.  All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU General Public License v.2.
+ */
+
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/spinlock.h>
+#include <linux/completion.h>
+#include <linux/buffer_head.h>
+#include <asm/semaphore.h>
+
+#include "gfs2.h"
+#include "bmap.h"
+#include "dir.h"
+#include "glock.h"
+#include "inode.h"
+#include "jdata.h"
+#include "meta_io.h"
+#include "quota.h"
+#include "resize.h"
+#include "rgrp.h"
+#include "super.h"
+#include "trans.h"
+
+int gfs2_resize_add_rgrps(struct gfs2_sbd *sdp, char __user *buf,
+			  unsigned int size)
+{
+	unsigned int num = size / sizeof(struct gfs2_rindex);
+	struct gfs2_inode *ip = sdp->sd_rindex;
+	struct gfs2_alloc *al = NULL;
+	struct gfs2_holder i_gh;
+	unsigned int data_blocks, ind_blocks;
+	int alloc_required;
+	unsigned int x;
+	int error;
+
+	gfs2_write_calc_reserv(ip, size, &data_blocks, &ind_blocks);
+
+	error = gfs2_glock_nq_init(ip->i_gl, LM_ST_EXCLUSIVE,
+				   LM_FLAG_PRIORITY | GL_SYNC, &i_gh);
+	if (error)
+		return error;
+
+	if (!gfs2_is_jdata(ip)) {
+		gfs2_consist_inode(ip);
+		error = -EIO;
+		goto out;
+	}
+
+	error = gfs2_write_alloc_required(ip, ip->i_di.di_size, size,
+					  &alloc_required);
+	if (error)
+		goto out;
+
+	if (alloc_required) {
+		al = gfs2_alloc_get(ip);
+
+		al->al_requested = data_blocks + ind_blocks;
+
+		error = gfs2_inplace_reserve(ip);
+		if (error)
+			goto out_alloc;
+
+		error = gfs2_trans_begin(sdp,
+					 al->al_rgd->rd_ri.ri_length +
+					 data_blocks + ind_blocks +
+					 RES_DINODE + RES_STATFS, 0);
+		if (error)
+			goto out_relse;
+	} else {
+		error = gfs2_trans_begin(sdp, data_blocks +
+					 RES_DINODE + RES_STATFS, 0);
+		if (error)
+			goto out;
+	}
+
+	for (x = 0; x < num; x++) {
+		struct gfs2_rindex ri;
+		char ri_buf[sizeof(struct gfs2_rindex)];
+
+		error = copy_from_user(&ri, buf, sizeof(struct gfs2_rindex));
+		if (error) {
+			error = -EFAULT;
+			goto out_trans;
+		}
+		gfs2_rindex_out(&ri, ri_buf);
+
+		error = gfs2_jdata_write_mem(ip, ri_buf, ip->i_di.di_size,
+					     sizeof(struct gfs2_rindex));
+		if (error < 0)
+			goto out_trans;
+		gfs2_assert_withdraw(sdp, error == sizeof(struct gfs2_rindex));
+		error = 0;
+
+		gfs2_statfs_change(sdp, ri.ri_data, ri.ri_data, 0);
+
+		buf += sizeof(struct gfs2_rindex);
+	}
+
+ out_trans:
+	gfs2_trans_end(sdp);
+
+ out_relse:
+	if (alloc_required)
+		gfs2_inplace_release(ip);
+
+ out_alloc:
+	if (alloc_required)
+		gfs2_alloc_put(ip);
+
+ out:
+	ip->i_gl->gl_vn++;
+	gfs2_glock_dq_uninit(&i_gh);
+
+	return error;
+}
+
+static void drop_dentries(struct gfs2_inode *ip)
+{
+	struct inode *inode;
+	struct dentry *d;
+
+	inode = gfs2_ip2v_lookup(ip);
+	if (!inode)
+		return;
+
+ restart:
+	spin_lock(&dcache_lock);
+	list_for_each_entry(d, &inode->i_dentry, d_alias) {
+		if (d_unhashed(d))
+			continue;
+		dget_locked(d);
+		__d_drop(d);
+		spin_unlock(&dcache_lock);
+		dput(d);
+		goto restart;
+	}
+	spin_unlock(&dcache_lock);
+
+	iput(inode);
+}
+
+int gfs2_rename2system(struct gfs2_inode *ip,
+		       struct gfs2_inode *old_dip, char *old_name,
+		       struct gfs2_inode *new_dip, char *new_name)
+{
+	struct gfs2_sbd *sdp = ip->i_sbd;
+	struct gfs2_holder ghs[3];
+	struct qstr old_qstr, new_qstr;
+	struct gfs2_inum inum;
+	int alloc_required;
+	struct buffer_head *dibh;
+	int error;
+
+	gfs2_holder_init(ip->i_gl, LM_ST_EXCLUSIVE, GL_NOCACHE, ghs);
+	gfs2_holder_init(old_dip->i_gl, LM_ST_EXCLUSIVE, 0, ghs + 1);
+	gfs2_holder_init(new_dip->i_gl, LM_ST_EXCLUSIVE, GL_SYNC, ghs + 2);
+
+	error = gfs2_glock_nq_m(3, ghs);
+	if (error)
+		goto out;	
+
+	error = -EMLINK;
+	if (ip->i_di.di_nlink != 1)
+		goto out_gunlock;
+	error = -EINVAL;
+	if (!S_ISREG(ip->i_di.di_mode))
+		goto out_gunlock;
+
+	old_qstr.name = old_name;
+	old_qstr.len = strlen(old_name);
+	error = gfs2_dir_search(old_dip, &old_qstr, &inum, NULL);
+	switch (error) {
+	case 0:
+		break;
+	default:
+		goto out_gunlock;
+	}
+
+	error = -EINVAL;
+	if (!gfs2_inum_equal(&inum, &ip->i_num))
+		goto out_gunlock;
+
+	new_qstr.name = new_name;
+	new_qstr.len = strlen(new_name);
+	error = gfs2_dir_search(new_dip, &new_qstr, NULL, NULL);
+	switch (error) {
+	case -ENOENT:
+		break;
+	case 0:
+		error = -EEXIST;
+	default:
+		goto out_gunlock;
+	}
+
+	gfs2_alloc_get(ip);
+
+	error = gfs2_quota_hold(ip, NO_QUOTA_CHANGE, NO_QUOTA_CHANGE);
+	if (error)
+		goto out_alloc;
+
+	error = gfs2_diradd_alloc_required(new_dip, &new_qstr, &alloc_required);
+	if (error)
+		goto out_unhold;
+
+	if (alloc_required) {
+		struct gfs2_alloc *al = gfs2_alloc_get(new_dip);
+
+		al->al_requested = sdp->sd_max_dirres;
+
+		error = gfs2_inplace_reserve(new_dip);
+		if (error)
+			goto out_alloc2;
+
+		error = gfs2_trans_begin(sdp,
+					 sdp->sd_max_dirres +
+					 al->al_rgd->rd_ri.ri_length +
+					 3 * RES_DINODE + RES_LEAF +
+					 RES_STATFS + RES_QUOTA, 0);
+		if (error)
+			goto out_ipreserv;
+	} else {
+		error = gfs2_trans_begin(sdp,
+					 3 * RES_DINODE + 2 * RES_LEAF +
+					 RES_QUOTA, 0);
+		if (error)
+			goto out_unhold;
+	}
+	
+	error = gfs2_dir_del(old_dip, &old_qstr);
+	if (error)
+		goto out_trans;
+
+	error = gfs2_dir_add(new_dip, &new_qstr, &ip->i_num,
+			     IF2DT(ip->i_di.di_mode));
+	if (error)
+		goto out_trans;
+
+	gfs2_quota_change(ip, -ip->i_di.di_blocks, ip->i_di.di_uid,
+			  ip->i_di.di_gid);
+
+	error = gfs2_meta_inode_buffer(ip, &dibh);
+	if (error)
+		goto out_trans;
+	ip->i_di.di_flags |= GFS2_DIF_SYSTEM;
+	gfs2_trans_add_bh(ip->i_gl, dibh);
+	gfs2_dinode_out(&ip->i_di, dibh->b_data);
+	brelse(dibh);
+
+	drop_dentries(ip);
+
+ out_trans:
+	gfs2_trans_end(sdp);
+
+ out_ipreserv:
+	if (alloc_required)
+		gfs2_inplace_release(new_dip);
+
+ out_alloc2:
+	if (alloc_required)
+		gfs2_alloc_put(new_dip);
+
+ out_unhold:
+	gfs2_quota_unhold(ip);
+
+ out_alloc:
+	gfs2_alloc_put(ip);
+
+ out_gunlock:
+	gfs2_glock_dq_m(3, ghs);
+
+ out:
+	gfs2_holder_uninit(ghs);
+	gfs2_holder_uninit(ghs + 1);
+	gfs2_holder_uninit(ghs + 2);
+
+	return error;
+}
+
--- a/fs/gfs2/resize.h	1970-01-01 07:30:00.000000000 +0730
+++ b/fs/gfs2/resize.h	2005-09-01 17:36:55.461093280 +0800
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) Sistina Software, Inc.  1997-2003 All rights reserved.
+ * Copyright (C) 2004-2005 Red Hat, Inc.  All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU General Public License v.2.
+ */
+
+#ifndef __RESIZE_DOT_H__
+#define __RESIZE_DOT_H__
+
+int gfs2_resize_add_rgrps(struct gfs2_sbd *sdp, char __user *buf,
+			  unsigned int size);
+int gfs2_rename2system(struct gfs2_inode *ip,
+		       struct gfs2_inode *old_dip, char *old_name,
+		       struct gfs2_inode *new_dip, char *new_name);
+
+#endif /* __RESIZE_DOT_H__ */
--- a/fs/gfs2/mount.c	1970-01-01 07:30:00.000000000 +0730
+++ b/fs/gfs2/mount.c	2005-09-01 17:36:55.391103920 +0800
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) Sistina Software, Inc.  1997-2003 All rights reserved.
+ * Copyright (C) 2004-2005 Red Hat, Inc.  All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU General Public License v.2.
+ */
+
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/spinlock.h>
+#include <linux/completion.h>
+#include <linux/buffer_head.h>
+#include <asm/semaphore.h>
+
+#include "gfs2.h"
+#include "mount.h"
+#include "sys.h"
+
+/**
+ * gfs2_mount_args - Parse mount options
+ * @sdp:
+ * @data:
+ *
+ * Return: errno
+ */
+
+int gfs2_mount_args(struct gfs2_sbd *sdp, char *data_arg, int remount)
+{
+	struct gfs2_args *args = &sdp->sd_args;
+	char *data = data_arg;
+	char *options, *o, *v;
+	int error = 0;
+
+	if (!remount) {
+		/*  If someone preloaded options, use those instead  */
+		spin_lock(&gfs2_sys_margs_lock);
+		if (gfs2_sys_margs) {
+			data = gfs2_sys_margs;
+			gfs2_sys_margs = NULL;
+		}
+		spin_unlock(&gfs2_sys_margs_lock);
+
+		/*  Set some defaults  */
+		args->ar_num_glockd = GFS2_GLOCKD_DEFAULT;
+		args->ar_quota = GFS2_QUOTA_DEFAULT;
+		args->ar_data = GFS2_DATA_DEFAULT;
+	}
+
+	/* Split the options into tokens with the "," character and
+	   process them */
+
+	for (options = data; (o = strsep(&options, ",")); ) {
+		if (!*o)
+			continue;
+
+		v = strchr(o, '=');
+		if (v)
+			*v++ = 0;
+
+		if (!strcmp(o, "lockproto")) {
+			if (!v)
+				goto need_value;
+			if (remount && strcmp(v, args->ar_lockproto))
+				goto cant_remount;
+			strncpy(args->ar_lockproto, v, GFS2_LOCKNAME_LEN);
+			args->ar_lockproto[GFS2_LOCKNAME_LEN - 1] = 0;
+		}
+
+		else if (!strcmp(o, "locktable")) {
+			if (!v)
+				goto need_value;
+			if (remount && strcmp(v, args->ar_locktable))
+				goto cant_remount;
+			strncpy(args->ar_locktable, v, GFS2_LOCKNAME_LEN);
+			args->ar_locktable[GFS2_LOCKNAME_LEN - 1] = 0;
+		}
+
+		else if (!strcmp(o, "hostdata")) {
+			if (!v)
+				goto need_value;
+			if (remount && strcmp(v, args->ar_hostdata))
+				goto cant_remount;
+			strncpy(args->ar_hostdata, v, GFS2_LOCKNAME_LEN);
+			args->ar_hostdata[GFS2_LOCKNAME_LEN - 1] = 0;
+		}
+
+		else if (!strcmp(o, "spectator")) {
+			if (remount && !args->ar_spectator)
+				goto cant_remount;
+			args->ar_spectator = TRUE;
+			sdp->sd_vfs->s_flags |= MS_RDONLY;
+
+		} else if (!strcmp(o, "ignore_local_fs")) {
+			if (remount && !args->ar_ignore_local_fs)
+				goto cant_remount;
+			args->ar_ignore_local_fs = TRUE;
+
+		} else if (!strcmp(o, "localflocks")) {
+			if (remount && !args->ar_localflocks)
+				goto cant_remount;
+			args->ar_localflocks = TRUE;
+
+		} else if (!strcmp(o, "localcaching")) {
+			if (remount && !args->ar_localcaching)
+				goto cant_remount;
+			args->ar_localcaching = TRUE;
+
+		} else if (!strcmp(o, "oopses_ok"))
+			args->ar_oopses_ok = TRUE;
+
+		else if (!strcmp(o, "nooopses_ok"))
+			args->ar_oopses_ok = FALSE;
+
+		else if (!strcmp(o, "debug")) {
+			args->ar_debug = TRUE;
+
+		} else if (!strcmp(o, "nodebug"))
+			args->ar_debug = FALSE;
+
+		else if (!strcmp(o, "upgrade")) {
+			if (remount && !args->ar_upgrade)
+				goto cant_remount;
+			args->ar_upgrade = TRUE;
+
+		} else if (!strcmp(o, "num_glockd")) {
+			unsigned int x;
+			if (!v)
+				goto need_value;
+			sscanf(v, "%u", &x);
+			if (remount && x != args->ar_num_glockd)
+				goto cant_remount;
+			if (!x || x > GFS2_GLOCKD_MAX) {
+				fs_info(sdp, "0 < num_glockd <= %u  (not %u)\n",
+				        GFS2_GLOCKD_MAX, x);
+				error = -EINVAL;
+				break;
+			}
+			args->ar_num_glockd = x;
+		}
+
+		else if (!strcmp(o, "acl")) {
+			args->ar_posix_acl = TRUE;
+			sdp->sd_vfs->s_flags |= MS_POSIXACL;
+
+		} else if (!strcmp(o, "noacl")) {
+			args->ar_posix_acl = FALSE;
+			sdp->sd_vfs->s_flags &= ~MS_POSIXACL;
+
+		} else if (!strcmp(o, "quota")) {
+			if (!v)
+				goto need_value;
+			if (!strcmp(v, "off"))
+				args->ar_quota = GFS2_QUOTA_OFF;
+			else if (!strcmp(v, "account"))
+				args->ar_quota = GFS2_QUOTA_ACCOUNT;
+			else if (!strcmp(v, "on"))
+				args->ar_quota = GFS2_QUOTA_ON;
+			else {
+				fs_info(sdp, "invalid value for quota\n");
+				error = -EINVAL;
+				break;
+			}
+
+		} else if (!strcmp(o, "suiddir"))
+			args->ar_suiddir = TRUE;
+
+		else if (!strcmp(o, "nosuiddir"))
+			args->ar_suiddir = FALSE;
+
+		else if (!strcmp(o, "data")) {
+			if (!v)
+				goto need_value;
+			if (!strcmp(v, "writeback"))
+				args->ar_data = GFS2_DATA_WRITEBACK;
+			else if (!strcmp(v, "ordered"))
+				args->ar_data = GFS2_DATA_ORDERED;
+			else {
+				fs_info(sdp, "invalid value for data\n");
+				error = -EINVAL;
+				break;
+			}
+
+		} else {
+			fs_info(sdp, "unknown option: %s\n", o);
+			error = -EINVAL;
+			break;
+		}
+	}
+
+	if (error)
+		fs_info(sdp, "invalid mount option(s)\n");
+
+	if (data != data_arg)
+		kfree(data);
+
+	return error;
+
+ need_value:
+	fs_info(sdp, "need value for option %s\n", o);
+	return -EINVAL;
+
+ cant_remount:
+	fs_info(sdp, "can't remount with option %s\n", o);
+	return -EINVAL;
+}
+
--- a/fs/gfs2/mount.h	1970-01-01 07:30:00.000000000 +0730
+++ b/fs/gfs2/mount.h	2005-09-01 17:36:55.391103920 +0800
@@ -0,0 +1,15 @@
+/*
+ * Copyright (C) Sistina Software, Inc.  1997-2003 All rights reserved.
+ * Copyright (C) 2004-2005 Red Hat, Inc.  All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU General Public License v.2.
+ */
+
+#ifndef __MOUNT_DOT_H__
+#define __MOUNT_DOT_H__
+
+int gfs2_mount_args(struct gfs2_sbd *sdp, char *data_arg, int remount);
+
+#endif /* __MOUNT_DOT_H__ */
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[Index of Archives]     [Kernel Newbies]     [Netfilter]     [Bugtraq]     [Photo]     [Gimp]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Video 4 Linux]     [Linux for the blind]
  Powered by Linux