162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * linux/fs/ocfs2/ioctl.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2006 Herbert Poetzl
662306a36Sopenharmony_ci * adapted from Remy Card's ext2/ioctl.c
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/fs.h>
1062306a36Sopenharmony_ci#include <linux/mount.h>
1162306a36Sopenharmony_ci#include <linux/blkdev.h>
1262306a36Sopenharmony_ci#include <linux/compat.h>
1362306a36Sopenharmony_ci#include <linux/fileattr.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include <cluster/masklog.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#include "ocfs2.h"
1862306a36Sopenharmony_ci#include "alloc.h"
1962306a36Sopenharmony_ci#include "dlmglue.h"
2062306a36Sopenharmony_ci#include "file.h"
2162306a36Sopenharmony_ci#include "inode.h"
2262306a36Sopenharmony_ci#include "journal.h"
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#include "ocfs2_fs.h"
2562306a36Sopenharmony_ci#include "ioctl.h"
2662306a36Sopenharmony_ci#include "resize.h"
2762306a36Sopenharmony_ci#include "refcounttree.h"
2862306a36Sopenharmony_ci#include "sysfile.h"
2962306a36Sopenharmony_ci#include "dir.h"
3062306a36Sopenharmony_ci#include "buffer_head_io.h"
3162306a36Sopenharmony_ci#include "suballoc.h"
3262306a36Sopenharmony_ci#include "move_extents.h"
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci#define o2info_from_user(a, b)	\
3562306a36Sopenharmony_ci		copy_from_user(&(a), (b), sizeof(a))
3662306a36Sopenharmony_ci#define o2info_to_user(a, b)	\
3762306a36Sopenharmony_ci		copy_to_user((typeof(a) __user *)b, &(a), sizeof(a))
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci/*
4062306a36Sopenharmony_ci * This is just a best-effort to tell userspace that this request
4162306a36Sopenharmony_ci * caused the error.
4262306a36Sopenharmony_ci */
4362306a36Sopenharmony_cistatic inline void o2info_set_request_error(struct ocfs2_info_request *kreq,
4462306a36Sopenharmony_ci					struct ocfs2_info_request __user *req)
4562306a36Sopenharmony_ci{
4662306a36Sopenharmony_ci	kreq->ir_flags |= OCFS2_INFO_FL_ERROR;
4762306a36Sopenharmony_ci	(void)put_user(kreq->ir_flags, (__u32 __user *)&(req->ir_flags));
4862306a36Sopenharmony_ci}
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_cistatic inline void o2info_set_request_filled(struct ocfs2_info_request *req)
5162306a36Sopenharmony_ci{
5262306a36Sopenharmony_ci	req->ir_flags |= OCFS2_INFO_FL_FILLED;
5362306a36Sopenharmony_ci}
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_cistatic inline void o2info_clear_request_filled(struct ocfs2_info_request *req)
5662306a36Sopenharmony_ci{
5762306a36Sopenharmony_ci	req->ir_flags &= ~OCFS2_INFO_FL_FILLED;
5862306a36Sopenharmony_ci}
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_cistatic inline int o2info_coherent(struct ocfs2_info_request *req)
6162306a36Sopenharmony_ci{
6262306a36Sopenharmony_ci	return (!(req->ir_flags & OCFS2_INFO_FL_NON_COHERENT));
6362306a36Sopenharmony_ci}
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ciint ocfs2_fileattr_get(struct dentry *dentry, struct fileattr *fa)
6662306a36Sopenharmony_ci{
6762306a36Sopenharmony_ci	struct inode *inode = d_inode(dentry);
6862306a36Sopenharmony_ci	unsigned int flags;
6962306a36Sopenharmony_ci	int status;
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	status = ocfs2_inode_lock(inode, NULL, 0);
7262306a36Sopenharmony_ci	if (status < 0) {
7362306a36Sopenharmony_ci		mlog_errno(status);
7462306a36Sopenharmony_ci		return status;
7562306a36Sopenharmony_ci	}
7662306a36Sopenharmony_ci	ocfs2_get_inode_flags(OCFS2_I(inode));
7762306a36Sopenharmony_ci	flags = OCFS2_I(inode)->ip_attr;
7862306a36Sopenharmony_ci	ocfs2_inode_unlock(inode, 0);
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	fileattr_fill_flags(fa, flags & OCFS2_FL_VISIBLE);
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	return status;
8362306a36Sopenharmony_ci}
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ciint ocfs2_fileattr_set(struct mnt_idmap *idmap,
8662306a36Sopenharmony_ci		       struct dentry *dentry, struct fileattr *fa)
8762306a36Sopenharmony_ci{
8862306a36Sopenharmony_ci	struct inode *inode = d_inode(dentry);
8962306a36Sopenharmony_ci	unsigned int flags = fa->flags;
9062306a36Sopenharmony_ci	struct ocfs2_inode_info *ocfs2_inode = OCFS2_I(inode);
9162306a36Sopenharmony_ci	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
9262306a36Sopenharmony_ci	handle_t *handle = NULL;
9362306a36Sopenharmony_ci	struct buffer_head *bh = NULL;
9462306a36Sopenharmony_ci	unsigned oldflags;
9562306a36Sopenharmony_ci	int status;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	if (fileattr_has_fsx(fa))
9862306a36Sopenharmony_ci		return -EOPNOTSUPP;
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	status = ocfs2_inode_lock(inode, &bh, 1);
10162306a36Sopenharmony_ci	if (status < 0) {
10262306a36Sopenharmony_ci		mlog_errno(status);
10362306a36Sopenharmony_ci		goto bail;
10462306a36Sopenharmony_ci	}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	if (!S_ISDIR(inode->i_mode))
10762306a36Sopenharmony_ci		flags &= ~OCFS2_DIRSYNC_FL;
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	oldflags = ocfs2_inode->ip_attr;
11062306a36Sopenharmony_ci	flags = flags & OCFS2_FL_MODIFIABLE;
11162306a36Sopenharmony_ci	flags |= oldflags & ~OCFS2_FL_MODIFIABLE;
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	/* Check already done by VFS, but repeat with ocfs lock */
11462306a36Sopenharmony_ci	status = -EPERM;
11562306a36Sopenharmony_ci	if ((flags ^ oldflags) & (FS_APPEND_FL | FS_IMMUTABLE_FL) &&
11662306a36Sopenharmony_ci	    !capable(CAP_LINUX_IMMUTABLE))
11762306a36Sopenharmony_ci		goto bail_unlock;
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	handle = ocfs2_start_trans(osb, OCFS2_INODE_UPDATE_CREDITS);
12062306a36Sopenharmony_ci	if (IS_ERR(handle)) {
12162306a36Sopenharmony_ci		status = PTR_ERR(handle);
12262306a36Sopenharmony_ci		mlog_errno(status);
12362306a36Sopenharmony_ci		goto bail_unlock;
12462306a36Sopenharmony_ci	}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	ocfs2_inode->ip_attr = flags;
12762306a36Sopenharmony_ci	ocfs2_set_inode_flags(inode);
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	status = ocfs2_mark_inode_dirty(handle, inode, bh);
13062306a36Sopenharmony_ci	if (status < 0)
13162306a36Sopenharmony_ci		mlog_errno(status);
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	ocfs2_commit_trans(osb, handle);
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_cibail_unlock:
13662306a36Sopenharmony_ci	ocfs2_inode_unlock(inode, 1);
13762306a36Sopenharmony_cibail:
13862306a36Sopenharmony_ci	brelse(bh);
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	return status;
14162306a36Sopenharmony_ci}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_cistatic int ocfs2_info_handle_blocksize(struct inode *inode,
14462306a36Sopenharmony_ci				       struct ocfs2_info_request __user *req)
14562306a36Sopenharmony_ci{
14662306a36Sopenharmony_ci	struct ocfs2_info_blocksize oib;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	if (o2info_from_user(oib, req))
14962306a36Sopenharmony_ci		return -EFAULT;
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	oib.ib_blocksize = inode->i_sb->s_blocksize;
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	o2info_set_request_filled(&oib.ib_req);
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	if (o2info_to_user(oib, req))
15662306a36Sopenharmony_ci		return -EFAULT;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	return 0;
15962306a36Sopenharmony_ci}
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_cistatic int ocfs2_info_handle_clustersize(struct inode *inode,
16262306a36Sopenharmony_ci					 struct ocfs2_info_request __user *req)
16362306a36Sopenharmony_ci{
16462306a36Sopenharmony_ci	struct ocfs2_info_clustersize oic;
16562306a36Sopenharmony_ci	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	if (o2info_from_user(oic, req))
16862306a36Sopenharmony_ci		return -EFAULT;
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	oic.ic_clustersize = osb->s_clustersize;
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	o2info_set_request_filled(&oic.ic_req);
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	if (o2info_to_user(oic, req))
17562306a36Sopenharmony_ci		return -EFAULT;
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	return 0;
17862306a36Sopenharmony_ci}
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_cistatic int ocfs2_info_handle_maxslots(struct inode *inode,
18162306a36Sopenharmony_ci				      struct ocfs2_info_request __user *req)
18262306a36Sopenharmony_ci{
18362306a36Sopenharmony_ci	struct ocfs2_info_maxslots oim;
18462306a36Sopenharmony_ci	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	if (o2info_from_user(oim, req))
18762306a36Sopenharmony_ci		return -EFAULT;
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	oim.im_max_slots = osb->max_slots;
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	o2info_set_request_filled(&oim.im_req);
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	if (o2info_to_user(oim, req))
19462306a36Sopenharmony_ci		return -EFAULT;
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	return 0;
19762306a36Sopenharmony_ci}
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_cistatic int ocfs2_info_handle_label(struct inode *inode,
20062306a36Sopenharmony_ci				   struct ocfs2_info_request __user *req)
20162306a36Sopenharmony_ci{
20262306a36Sopenharmony_ci	struct ocfs2_info_label oil;
20362306a36Sopenharmony_ci	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	if (o2info_from_user(oil, req))
20662306a36Sopenharmony_ci		return -EFAULT;
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	memcpy(oil.il_label, osb->vol_label, OCFS2_MAX_VOL_LABEL_LEN);
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	o2info_set_request_filled(&oil.il_req);
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	if (o2info_to_user(oil, req))
21362306a36Sopenharmony_ci		return -EFAULT;
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	return 0;
21662306a36Sopenharmony_ci}
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_cistatic int ocfs2_info_handle_uuid(struct inode *inode,
21962306a36Sopenharmony_ci				  struct ocfs2_info_request __user *req)
22062306a36Sopenharmony_ci{
22162306a36Sopenharmony_ci	struct ocfs2_info_uuid oiu;
22262306a36Sopenharmony_ci	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	if (o2info_from_user(oiu, req))
22562306a36Sopenharmony_ci		return -EFAULT;
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	memcpy(oiu.iu_uuid_str, osb->uuid_str, OCFS2_TEXT_UUID_LEN + 1);
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	o2info_set_request_filled(&oiu.iu_req);
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	if (o2info_to_user(oiu, req))
23262306a36Sopenharmony_ci		return -EFAULT;
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	return 0;
23562306a36Sopenharmony_ci}
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_cistatic int ocfs2_info_handle_fs_features(struct inode *inode,
23862306a36Sopenharmony_ci					 struct ocfs2_info_request __user *req)
23962306a36Sopenharmony_ci{
24062306a36Sopenharmony_ci	struct ocfs2_info_fs_features oif;
24162306a36Sopenharmony_ci	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	if (o2info_from_user(oif, req))
24462306a36Sopenharmony_ci		return -EFAULT;
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	oif.if_compat_features = osb->s_feature_compat;
24762306a36Sopenharmony_ci	oif.if_incompat_features = osb->s_feature_incompat;
24862306a36Sopenharmony_ci	oif.if_ro_compat_features = osb->s_feature_ro_compat;
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	o2info_set_request_filled(&oif.if_req);
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	if (o2info_to_user(oif, req))
25362306a36Sopenharmony_ci		return -EFAULT;
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	return 0;
25662306a36Sopenharmony_ci}
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_cistatic int ocfs2_info_handle_journal_size(struct inode *inode,
25962306a36Sopenharmony_ci					  struct ocfs2_info_request __user *req)
26062306a36Sopenharmony_ci{
26162306a36Sopenharmony_ci	struct ocfs2_info_journal_size oij;
26262306a36Sopenharmony_ci	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	if (o2info_from_user(oij, req))
26562306a36Sopenharmony_ci		return -EFAULT;
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	oij.ij_journal_size = i_size_read(osb->journal->j_inode);
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	o2info_set_request_filled(&oij.ij_req);
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	if (o2info_to_user(oij, req))
27262306a36Sopenharmony_ci		return -EFAULT;
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	return 0;
27562306a36Sopenharmony_ci}
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_cistatic int ocfs2_info_scan_inode_alloc(struct ocfs2_super *osb,
27862306a36Sopenharmony_ci				       struct inode *inode_alloc, u64 blkno,
27962306a36Sopenharmony_ci				       struct ocfs2_info_freeinode *fi,
28062306a36Sopenharmony_ci				       u32 slot)
28162306a36Sopenharmony_ci{
28262306a36Sopenharmony_ci	int status = 0, unlock = 0;
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	struct buffer_head *bh = NULL;
28562306a36Sopenharmony_ci	struct ocfs2_dinode *dinode_alloc = NULL;
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	if (inode_alloc)
28862306a36Sopenharmony_ci		inode_lock(inode_alloc);
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	if (inode_alloc && o2info_coherent(&fi->ifi_req)) {
29162306a36Sopenharmony_ci		status = ocfs2_inode_lock(inode_alloc, &bh, 0);
29262306a36Sopenharmony_ci		if (status < 0) {
29362306a36Sopenharmony_ci			mlog_errno(status);
29462306a36Sopenharmony_ci			goto bail;
29562306a36Sopenharmony_ci		}
29662306a36Sopenharmony_ci		unlock = 1;
29762306a36Sopenharmony_ci	} else {
29862306a36Sopenharmony_ci		status = ocfs2_read_blocks_sync(osb, blkno, 1, &bh);
29962306a36Sopenharmony_ci		if (status < 0) {
30062306a36Sopenharmony_ci			mlog_errno(status);
30162306a36Sopenharmony_ci			goto bail;
30262306a36Sopenharmony_ci		}
30362306a36Sopenharmony_ci	}
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	dinode_alloc = (struct ocfs2_dinode *)bh->b_data;
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	fi->ifi_stat[slot].lfi_total =
30862306a36Sopenharmony_ci		le32_to_cpu(dinode_alloc->id1.bitmap1.i_total);
30962306a36Sopenharmony_ci	fi->ifi_stat[slot].lfi_free =
31062306a36Sopenharmony_ci		le32_to_cpu(dinode_alloc->id1.bitmap1.i_total) -
31162306a36Sopenharmony_ci		le32_to_cpu(dinode_alloc->id1.bitmap1.i_used);
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_cibail:
31462306a36Sopenharmony_ci	if (unlock)
31562306a36Sopenharmony_ci		ocfs2_inode_unlock(inode_alloc, 0);
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	if (inode_alloc)
31862306a36Sopenharmony_ci		inode_unlock(inode_alloc);
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	brelse(bh);
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	return status;
32362306a36Sopenharmony_ci}
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_cistatic int ocfs2_info_handle_freeinode(struct inode *inode,
32662306a36Sopenharmony_ci				       struct ocfs2_info_request __user *req)
32762306a36Sopenharmony_ci{
32862306a36Sopenharmony_ci	u32 i;
32962306a36Sopenharmony_ci	u64 blkno = -1;
33062306a36Sopenharmony_ci	char namebuf[40];
33162306a36Sopenharmony_ci	int status, type = INODE_ALLOC_SYSTEM_INODE;
33262306a36Sopenharmony_ci	struct ocfs2_info_freeinode *oifi = NULL;
33362306a36Sopenharmony_ci	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
33462306a36Sopenharmony_ci	struct inode *inode_alloc = NULL;
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	oifi = kzalloc(sizeof(struct ocfs2_info_freeinode), GFP_KERNEL);
33762306a36Sopenharmony_ci	if (!oifi) {
33862306a36Sopenharmony_ci		status = -ENOMEM;
33962306a36Sopenharmony_ci		mlog_errno(status);
34062306a36Sopenharmony_ci		goto out_err;
34162306a36Sopenharmony_ci	}
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	if (o2info_from_user(*oifi, req)) {
34462306a36Sopenharmony_ci		status = -EFAULT;
34562306a36Sopenharmony_ci		goto out_free;
34662306a36Sopenharmony_ci	}
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	oifi->ifi_slotnum = osb->max_slots;
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	for (i = 0; i < oifi->ifi_slotnum; i++) {
35162306a36Sopenharmony_ci		if (o2info_coherent(&oifi->ifi_req)) {
35262306a36Sopenharmony_ci			inode_alloc = ocfs2_get_system_file_inode(osb, type, i);
35362306a36Sopenharmony_ci			if (!inode_alloc) {
35462306a36Sopenharmony_ci				mlog(ML_ERROR, "unable to get alloc inode in "
35562306a36Sopenharmony_ci				    "slot %u\n", i);
35662306a36Sopenharmony_ci				status = -EIO;
35762306a36Sopenharmony_ci				goto bail;
35862306a36Sopenharmony_ci			}
35962306a36Sopenharmony_ci		} else {
36062306a36Sopenharmony_ci			ocfs2_sprintf_system_inode_name(namebuf,
36162306a36Sopenharmony_ci							sizeof(namebuf),
36262306a36Sopenharmony_ci							type, i);
36362306a36Sopenharmony_ci			status = ocfs2_lookup_ino_from_name(osb->sys_root_inode,
36462306a36Sopenharmony_ci							    namebuf,
36562306a36Sopenharmony_ci							    strlen(namebuf),
36662306a36Sopenharmony_ci							    &blkno);
36762306a36Sopenharmony_ci			if (status < 0) {
36862306a36Sopenharmony_ci				status = -ENOENT;
36962306a36Sopenharmony_ci				goto bail;
37062306a36Sopenharmony_ci			}
37162306a36Sopenharmony_ci		}
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci		status = ocfs2_info_scan_inode_alloc(osb, inode_alloc, blkno, oifi, i);
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci		iput(inode_alloc);
37662306a36Sopenharmony_ci		inode_alloc = NULL;
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci		if (status < 0)
37962306a36Sopenharmony_ci			goto bail;
38062306a36Sopenharmony_ci	}
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	o2info_set_request_filled(&oifi->ifi_req);
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	if (o2info_to_user(*oifi, req)) {
38562306a36Sopenharmony_ci		status = -EFAULT;
38662306a36Sopenharmony_ci		goto out_free;
38762306a36Sopenharmony_ci	}
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	status = 0;
39062306a36Sopenharmony_cibail:
39162306a36Sopenharmony_ci	if (status)
39262306a36Sopenharmony_ci		o2info_set_request_error(&oifi->ifi_req, req);
39362306a36Sopenharmony_ciout_free:
39462306a36Sopenharmony_ci	kfree(oifi);
39562306a36Sopenharmony_ciout_err:
39662306a36Sopenharmony_ci	return status;
39762306a36Sopenharmony_ci}
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_cistatic void o2ffg_update_histogram(struct ocfs2_info_free_chunk_list *hist,
40062306a36Sopenharmony_ci				   unsigned int chunksize)
40162306a36Sopenharmony_ci{
40262306a36Sopenharmony_ci	u32 index;
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	index = __ilog2_u32(chunksize);
40562306a36Sopenharmony_ci	if (index >= OCFS2_INFO_MAX_HIST)
40662306a36Sopenharmony_ci		index = OCFS2_INFO_MAX_HIST - 1;
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	hist->fc_chunks[index]++;
40962306a36Sopenharmony_ci	hist->fc_clusters[index] += chunksize;
41062306a36Sopenharmony_ci}
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_cistatic void o2ffg_update_stats(struct ocfs2_info_freefrag_stats *stats,
41362306a36Sopenharmony_ci			       unsigned int chunksize)
41462306a36Sopenharmony_ci{
41562306a36Sopenharmony_ci	if (chunksize > stats->ffs_max)
41662306a36Sopenharmony_ci		stats->ffs_max = chunksize;
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	if (chunksize < stats->ffs_min)
41962306a36Sopenharmony_ci		stats->ffs_min = chunksize;
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	stats->ffs_avg += chunksize;
42262306a36Sopenharmony_ci	stats->ffs_free_chunks_real++;
42362306a36Sopenharmony_ci}
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_cistatic void ocfs2_info_update_ffg(struct ocfs2_info_freefrag *ffg,
42662306a36Sopenharmony_ci				  unsigned int chunksize)
42762306a36Sopenharmony_ci{
42862306a36Sopenharmony_ci	o2ffg_update_histogram(&(ffg->iff_ffs.ffs_fc_hist), chunksize);
42962306a36Sopenharmony_ci	o2ffg_update_stats(&(ffg->iff_ffs), chunksize);
43062306a36Sopenharmony_ci}
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_cistatic int ocfs2_info_freefrag_scan_chain(struct ocfs2_super *osb,
43362306a36Sopenharmony_ci					  struct inode *gb_inode,
43462306a36Sopenharmony_ci					  struct ocfs2_dinode *gb_dinode,
43562306a36Sopenharmony_ci					  struct ocfs2_chain_rec *rec,
43662306a36Sopenharmony_ci					  struct ocfs2_info_freefrag *ffg,
43762306a36Sopenharmony_ci					  u32 chunks_in_group)
43862306a36Sopenharmony_ci{
43962306a36Sopenharmony_ci	int status = 0, used;
44062306a36Sopenharmony_ci	u64 blkno;
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	struct buffer_head *bh = NULL;
44362306a36Sopenharmony_ci	struct ocfs2_group_desc *bg = NULL;
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	unsigned int max_bits, num_clusters;
44662306a36Sopenharmony_ci	unsigned int offset = 0, cluster, chunk;
44762306a36Sopenharmony_ci	unsigned int chunk_free, last_chunksize = 0;
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	if (!le32_to_cpu(rec->c_free))
45062306a36Sopenharmony_ci		goto bail;
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	do {
45362306a36Sopenharmony_ci		if (!bg)
45462306a36Sopenharmony_ci			blkno = le64_to_cpu(rec->c_blkno);
45562306a36Sopenharmony_ci		else
45662306a36Sopenharmony_ci			blkno = le64_to_cpu(bg->bg_next_group);
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci		if (bh) {
45962306a36Sopenharmony_ci			brelse(bh);
46062306a36Sopenharmony_ci			bh = NULL;
46162306a36Sopenharmony_ci		}
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci		if (o2info_coherent(&ffg->iff_req))
46462306a36Sopenharmony_ci			status = ocfs2_read_group_descriptor(gb_inode,
46562306a36Sopenharmony_ci							     gb_dinode,
46662306a36Sopenharmony_ci							     blkno, &bh);
46762306a36Sopenharmony_ci		else
46862306a36Sopenharmony_ci			status = ocfs2_read_blocks_sync(osb, blkno, 1, &bh);
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci		if (status < 0) {
47162306a36Sopenharmony_ci			mlog(ML_ERROR, "Can't read the group descriptor # "
47262306a36Sopenharmony_ci			     "%llu from device.", (unsigned long long)blkno);
47362306a36Sopenharmony_ci			status = -EIO;
47462306a36Sopenharmony_ci			goto bail;
47562306a36Sopenharmony_ci		}
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci		bg = (struct ocfs2_group_desc *)bh->b_data;
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci		if (!le16_to_cpu(bg->bg_free_bits_count))
48062306a36Sopenharmony_ci			continue;
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci		max_bits = le16_to_cpu(bg->bg_bits);
48362306a36Sopenharmony_ci		offset = 0;
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci		for (chunk = 0; chunk < chunks_in_group; chunk++) {
48662306a36Sopenharmony_ci			/*
48762306a36Sopenharmony_ci			 * last chunk may be not an entire one.
48862306a36Sopenharmony_ci			 */
48962306a36Sopenharmony_ci			if ((offset + ffg->iff_chunksize) > max_bits)
49062306a36Sopenharmony_ci				num_clusters = max_bits - offset;
49162306a36Sopenharmony_ci			else
49262306a36Sopenharmony_ci				num_clusters = ffg->iff_chunksize;
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci			chunk_free = 0;
49562306a36Sopenharmony_ci			for (cluster = 0; cluster < num_clusters; cluster++) {
49662306a36Sopenharmony_ci				used = ocfs2_test_bit(offset,
49762306a36Sopenharmony_ci						(unsigned long *)bg->bg_bitmap);
49862306a36Sopenharmony_ci				/*
49962306a36Sopenharmony_ci				 * - chunk_free counts free clusters in #N chunk.
50062306a36Sopenharmony_ci				 * - last_chunksize records the size(in) clusters
50162306a36Sopenharmony_ci				 *   for the last real free chunk being counted.
50262306a36Sopenharmony_ci				 */
50362306a36Sopenharmony_ci				if (!used) {
50462306a36Sopenharmony_ci					last_chunksize++;
50562306a36Sopenharmony_ci					chunk_free++;
50662306a36Sopenharmony_ci				}
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci				if (used && last_chunksize) {
50962306a36Sopenharmony_ci					ocfs2_info_update_ffg(ffg,
51062306a36Sopenharmony_ci							      last_chunksize);
51162306a36Sopenharmony_ci					last_chunksize = 0;
51262306a36Sopenharmony_ci				}
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci				offset++;
51562306a36Sopenharmony_ci			}
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci			if (chunk_free == ffg->iff_chunksize)
51862306a36Sopenharmony_ci				ffg->iff_ffs.ffs_free_chunks++;
51962306a36Sopenharmony_ci		}
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci		/*
52262306a36Sopenharmony_ci		 * need to update the info for last free chunk.
52362306a36Sopenharmony_ci		 */
52462306a36Sopenharmony_ci		if (last_chunksize)
52562306a36Sopenharmony_ci			ocfs2_info_update_ffg(ffg, last_chunksize);
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	} while (le64_to_cpu(bg->bg_next_group));
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_cibail:
53062306a36Sopenharmony_ci	brelse(bh);
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci	return status;
53362306a36Sopenharmony_ci}
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_cistatic int ocfs2_info_freefrag_scan_bitmap(struct ocfs2_super *osb,
53662306a36Sopenharmony_ci					   struct inode *gb_inode, u64 blkno,
53762306a36Sopenharmony_ci					   struct ocfs2_info_freefrag *ffg)
53862306a36Sopenharmony_ci{
53962306a36Sopenharmony_ci	u32 chunks_in_group;
54062306a36Sopenharmony_ci	int status = 0, unlock = 0, i;
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci	struct buffer_head *bh = NULL;
54362306a36Sopenharmony_ci	struct ocfs2_chain_list *cl = NULL;
54462306a36Sopenharmony_ci	struct ocfs2_chain_rec *rec = NULL;
54562306a36Sopenharmony_ci	struct ocfs2_dinode *gb_dinode = NULL;
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	if (gb_inode)
54862306a36Sopenharmony_ci		inode_lock(gb_inode);
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci	if (o2info_coherent(&ffg->iff_req)) {
55162306a36Sopenharmony_ci		status = ocfs2_inode_lock(gb_inode, &bh, 0);
55262306a36Sopenharmony_ci		if (status < 0) {
55362306a36Sopenharmony_ci			mlog_errno(status);
55462306a36Sopenharmony_ci			goto bail;
55562306a36Sopenharmony_ci		}
55662306a36Sopenharmony_ci		unlock = 1;
55762306a36Sopenharmony_ci	} else {
55862306a36Sopenharmony_ci		status = ocfs2_read_blocks_sync(osb, blkno, 1, &bh);
55962306a36Sopenharmony_ci		if (status < 0) {
56062306a36Sopenharmony_ci			mlog_errno(status);
56162306a36Sopenharmony_ci			goto bail;
56262306a36Sopenharmony_ci		}
56362306a36Sopenharmony_ci	}
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci	gb_dinode = (struct ocfs2_dinode *)bh->b_data;
56662306a36Sopenharmony_ci	cl = &(gb_dinode->id2.i_chain);
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	/*
56962306a36Sopenharmony_ci	 * Chunksize(in) clusters from userspace should be
57062306a36Sopenharmony_ci	 * less than clusters in a group.
57162306a36Sopenharmony_ci	 */
57262306a36Sopenharmony_ci	if (ffg->iff_chunksize > le16_to_cpu(cl->cl_cpg)) {
57362306a36Sopenharmony_ci		status = -EINVAL;
57462306a36Sopenharmony_ci		goto bail;
57562306a36Sopenharmony_ci	}
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci	memset(&ffg->iff_ffs, 0, sizeof(struct ocfs2_info_freefrag_stats));
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	ffg->iff_ffs.ffs_min = ~0U;
58062306a36Sopenharmony_ci	ffg->iff_ffs.ffs_clusters =
58162306a36Sopenharmony_ci			le32_to_cpu(gb_dinode->id1.bitmap1.i_total);
58262306a36Sopenharmony_ci	ffg->iff_ffs.ffs_free_clusters = ffg->iff_ffs.ffs_clusters -
58362306a36Sopenharmony_ci			le32_to_cpu(gb_dinode->id1.bitmap1.i_used);
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci	chunks_in_group = le16_to_cpu(cl->cl_cpg) / ffg->iff_chunksize + 1;
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci	for (i = 0; i < le16_to_cpu(cl->cl_next_free_rec); i++) {
58862306a36Sopenharmony_ci		rec = &(cl->cl_recs[i]);
58962306a36Sopenharmony_ci		status = ocfs2_info_freefrag_scan_chain(osb, gb_inode,
59062306a36Sopenharmony_ci							gb_dinode,
59162306a36Sopenharmony_ci							rec, ffg,
59262306a36Sopenharmony_ci							chunks_in_group);
59362306a36Sopenharmony_ci		if (status)
59462306a36Sopenharmony_ci			goto bail;
59562306a36Sopenharmony_ci	}
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci	if (ffg->iff_ffs.ffs_free_chunks_real)
59862306a36Sopenharmony_ci		ffg->iff_ffs.ffs_avg = (ffg->iff_ffs.ffs_avg /
59962306a36Sopenharmony_ci					ffg->iff_ffs.ffs_free_chunks_real);
60062306a36Sopenharmony_cibail:
60162306a36Sopenharmony_ci	if (unlock)
60262306a36Sopenharmony_ci		ocfs2_inode_unlock(gb_inode, 0);
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci	if (gb_inode)
60562306a36Sopenharmony_ci		inode_unlock(gb_inode);
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci	iput(gb_inode);
60862306a36Sopenharmony_ci	brelse(bh);
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci	return status;
61162306a36Sopenharmony_ci}
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_cistatic int ocfs2_info_handle_freefrag(struct inode *inode,
61462306a36Sopenharmony_ci				      struct ocfs2_info_request __user *req)
61562306a36Sopenharmony_ci{
61662306a36Sopenharmony_ci	u64 blkno = -1;
61762306a36Sopenharmony_ci	char namebuf[40];
61862306a36Sopenharmony_ci	int status, type = GLOBAL_BITMAP_SYSTEM_INODE;
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci	struct ocfs2_info_freefrag *oiff;
62162306a36Sopenharmony_ci	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
62262306a36Sopenharmony_ci	struct inode *gb_inode = NULL;
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci	oiff = kzalloc(sizeof(struct ocfs2_info_freefrag), GFP_KERNEL);
62562306a36Sopenharmony_ci	if (!oiff) {
62662306a36Sopenharmony_ci		status = -ENOMEM;
62762306a36Sopenharmony_ci		mlog_errno(status);
62862306a36Sopenharmony_ci		goto out_err;
62962306a36Sopenharmony_ci	}
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci	if (o2info_from_user(*oiff, req)) {
63262306a36Sopenharmony_ci		status = -EFAULT;
63362306a36Sopenharmony_ci		goto out_free;
63462306a36Sopenharmony_ci	}
63562306a36Sopenharmony_ci	/*
63662306a36Sopenharmony_ci	 * chunksize from userspace should be power of 2.
63762306a36Sopenharmony_ci	 */
63862306a36Sopenharmony_ci	if ((oiff->iff_chunksize & (oiff->iff_chunksize - 1)) ||
63962306a36Sopenharmony_ci	    (!oiff->iff_chunksize)) {
64062306a36Sopenharmony_ci		status = -EINVAL;
64162306a36Sopenharmony_ci		goto bail;
64262306a36Sopenharmony_ci	}
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci	if (o2info_coherent(&oiff->iff_req)) {
64562306a36Sopenharmony_ci		gb_inode = ocfs2_get_system_file_inode(osb, type,
64662306a36Sopenharmony_ci						       OCFS2_INVALID_SLOT);
64762306a36Sopenharmony_ci		if (!gb_inode) {
64862306a36Sopenharmony_ci			mlog(ML_ERROR, "unable to get global_bitmap inode\n");
64962306a36Sopenharmony_ci			status = -EIO;
65062306a36Sopenharmony_ci			goto bail;
65162306a36Sopenharmony_ci		}
65262306a36Sopenharmony_ci	} else {
65362306a36Sopenharmony_ci		ocfs2_sprintf_system_inode_name(namebuf, sizeof(namebuf), type,
65462306a36Sopenharmony_ci						OCFS2_INVALID_SLOT);
65562306a36Sopenharmony_ci		status = ocfs2_lookup_ino_from_name(osb->sys_root_inode,
65662306a36Sopenharmony_ci						    namebuf,
65762306a36Sopenharmony_ci						    strlen(namebuf),
65862306a36Sopenharmony_ci						    &blkno);
65962306a36Sopenharmony_ci		if (status < 0) {
66062306a36Sopenharmony_ci			status = -ENOENT;
66162306a36Sopenharmony_ci			goto bail;
66262306a36Sopenharmony_ci		}
66362306a36Sopenharmony_ci	}
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci	status = ocfs2_info_freefrag_scan_bitmap(osb, gb_inode, blkno, oiff);
66662306a36Sopenharmony_ci	if (status < 0)
66762306a36Sopenharmony_ci		goto bail;
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci	o2info_set_request_filled(&oiff->iff_req);
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci	if (o2info_to_user(*oiff, req)) {
67262306a36Sopenharmony_ci		status = -EFAULT;
67362306a36Sopenharmony_ci		goto out_free;
67462306a36Sopenharmony_ci	}
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci	status = 0;
67762306a36Sopenharmony_cibail:
67862306a36Sopenharmony_ci	if (status)
67962306a36Sopenharmony_ci		o2info_set_request_error(&oiff->iff_req, req);
68062306a36Sopenharmony_ciout_free:
68162306a36Sopenharmony_ci	kfree(oiff);
68262306a36Sopenharmony_ciout_err:
68362306a36Sopenharmony_ci	return status;
68462306a36Sopenharmony_ci}
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_cistatic int ocfs2_info_handle_unknown(struct inode *inode,
68762306a36Sopenharmony_ci				     struct ocfs2_info_request __user *req)
68862306a36Sopenharmony_ci{
68962306a36Sopenharmony_ci	struct ocfs2_info_request oir;
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_ci	if (o2info_from_user(oir, req))
69262306a36Sopenharmony_ci		return -EFAULT;
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ci	o2info_clear_request_filled(&oir);
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci	if (o2info_to_user(oir, req))
69762306a36Sopenharmony_ci		return -EFAULT;
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci	return 0;
70062306a36Sopenharmony_ci}
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci/*
70362306a36Sopenharmony_ci * Validate and distinguish OCFS2_IOC_INFO requests.
70462306a36Sopenharmony_ci *
70562306a36Sopenharmony_ci * - validate the magic number.
70662306a36Sopenharmony_ci * - distinguish different requests.
70762306a36Sopenharmony_ci * - validate size of different requests.
70862306a36Sopenharmony_ci */
70962306a36Sopenharmony_cistatic int ocfs2_info_handle_request(struct inode *inode,
71062306a36Sopenharmony_ci				     struct ocfs2_info_request __user *req)
71162306a36Sopenharmony_ci{
71262306a36Sopenharmony_ci	int status = -EFAULT;
71362306a36Sopenharmony_ci	struct ocfs2_info_request oir;
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci	if (o2info_from_user(oir, req))
71662306a36Sopenharmony_ci		goto bail;
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci	status = -EINVAL;
71962306a36Sopenharmony_ci	if (oir.ir_magic != OCFS2_INFO_MAGIC)
72062306a36Sopenharmony_ci		goto bail;
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_ci	switch (oir.ir_code) {
72362306a36Sopenharmony_ci	case OCFS2_INFO_BLOCKSIZE:
72462306a36Sopenharmony_ci		if (oir.ir_size == sizeof(struct ocfs2_info_blocksize))
72562306a36Sopenharmony_ci			status = ocfs2_info_handle_blocksize(inode, req);
72662306a36Sopenharmony_ci		break;
72762306a36Sopenharmony_ci	case OCFS2_INFO_CLUSTERSIZE:
72862306a36Sopenharmony_ci		if (oir.ir_size == sizeof(struct ocfs2_info_clustersize))
72962306a36Sopenharmony_ci			status = ocfs2_info_handle_clustersize(inode, req);
73062306a36Sopenharmony_ci		break;
73162306a36Sopenharmony_ci	case OCFS2_INFO_MAXSLOTS:
73262306a36Sopenharmony_ci		if (oir.ir_size == sizeof(struct ocfs2_info_maxslots))
73362306a36Sopenharmony_ci			status = ocfs2_info_handle_maxslots(inode, req);
73462306a36Sopenharmony_ci		break;
73562306a36Sopenharmony_ci	case OCFS2_INFO_LABEL:
73662306a36Sopenharmony_ci		if (oir.ir_size == sizeof(struct ocfs2_info_label))
73762306a36Sopenharmony_ci			status = ocfs2_info_handle_label(inode, req);
73862306a36Sopenharmony_ci		break;
73962306a36Sopenharmony_ci	case OCFS2_INFO_UUID:
74062306a36Sopenharmony_ci		if (oir.ir_size == sizeof(struct ocfs2_info_uuid))
74162306a36Sopenharmony_ci			status = ocfs2_info_handle_uuid(inode, req);
74262306a36Sopenharmony_ci		break;
74362306a36Sopenharmony_ci	case OCFS2_INFO_FS_FEATURES:
74462306a36Sopenharmony_ci		if (oir.ir_size == sizeof(struct ocfs2_info_fs_features))
74562306a36Sopenharmony_ci			status = ocfs2_info_handle_fs_features(inode, req);
74662306a36Sopenharmony_ci		break;
74762306a36Sopenharmony_ci	case OCFS2_INFO_JOURNAL_SIZE:
74862306a36Sopenharmony_ci		if (oir.ir_size == sizeof(struct ocfs2_info_journal_size))
74962306a36Sopenharmony_ci			status = ocfs2_info_handle_journal_size(inode, req);
75062306a36Sopenharmony_ci		break;
75162306a36Sopenharmony_ci	case OCFS2_INFO_FREEINODE:
75262306a36Sopenharmony_ci		if (oir.ir_size == sizeof(struct ocfs2_info_freeinode))
75362306a36Sopenharmony_ci			status = ocfs2_info_handle_freeinode(inode, req);
75462306a36Sopenharmony_ci		break;
75562306a36Sopenharmony_ci	case OCFS2_INFO_FREEFRAG:
75662306a36Sopenharmony_ci		if (oir.ir_size == sizeof(struct ocfs2_info_freefrag))
75762306a36Sopenharmony_ci			status = ocfs2_info_handle_freefrag(inode, req);
75862306a36Sopenharmony_ci		break;
75962306a36Sopenharmony_ci	default:
76062306a36Sopenharmony_ci		status = ocfs2_info_handle_unknown(inode, req);
76162306a36Sopenharmony_ci		break;
76262306a36Sopenharmony_ci	}
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_cibail:
76562306a36Sopenharmony_ci	return status;
76662306a36Sopenharmony_ci}
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_cistatic int ocfs2_get_request_ptr(struct ocfs2_info *info, int idx,
76962306a36Sopenharmony_ci				 u64 *req_addr, int compat_flag)
77062306a36Sopenharmony_ci{
77162306a36Sopenharmony_ci	int status = -EFAULT;
77262306a36Sopenharmony_ci	u64 __user *bp = NULL;
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_ci	if (compat_flag) {
77562306a36Sopenharmony_ci#ifdef CONFIG_COMPAT
77662306a36Sopenharmony_ci		/*
77762306a36Sopenharmony_ci		 * pointer bp stores the base address of a pointers array,
77862306a36Sopenharmony_ci		 * which collects all addresses of separate request.
77962306a36Sopenharmony_ci		 */
78062306a36Sopenharmony_ci		bp = (u64 __user *)(unsigned long)compat_ptr(info->oi_requests);
78162306a36Sopenharmony_ci#else
78262306a36Sopenharmony_ci		BUG();
78362306a36Sopenharmony_ci#endif
78462306a36Sopenharmony_ci	} else
78562306a36Sopenharmony_ci		bp = (u64 __user *)(unsigned long)(info->oi_requests);
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci	if (o2info_from_user(*req_addr, bp + idx))
78862306a36Sopenharmony_ci		goto bail;
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_ci	status = 0;
79162306a36Sopenharmony_cibail:
79262306a36Sopenharmony_ci	return status;
79362306a36Sopenharmony_ci}
79462306a36Sopenharmony_ci
79562306a36Sopenharmony_ci/*
79662306a36Sopenharmony_ci * OCFS2_IOC_INFO handles an array of requests passed from userspace.
79762306a36Sopenharmony_ci *
79862306a36Sopenharmony_ci * ocfs2_info_handle() recevies a large info aggregation, grab and
79962306a36Sopenharmony_ci * validate the request count from header, then break it into small
80062306a36Sopenharmony_ci * pieces, later specific handlers can handle them one by one.
80162306a36Sopenharmony_ci *
80262306a36Sopenharmony_ci * Idea here is to make each separate request small enough to ensure
80362306a36Sopenharmony_ci * a better backward&forward compatibility, since a small piece of
80462306a36Sopenharmony_ci * request will be less likely to be broken if disk layout get changed.
80562306a36Sopenharmony_ci */
80662306a36Sopenharmony_cistatic noinline_for_stack int
80762306a36Sopenharmony_ciocfs2_info_handle(struct inode *inode, struct ocfs2_info *info, int compat_flag)
80862306a36Sopenharmony_ci{
80962306a36Sopenharmony_ci	int i, status = 0;
81062306a36Sopenharmony_ci	u64 req_addr;
81162306a36Sopenharmony_ci	struct ocfs2_info_request __user *reqp;
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_ci	if ((info->oi_count > OCFS2_INFO_MAX_REQUEST) ||
81462306a36Sopenharmony_ci	    (!info->oi_requests)) {
81562306a36Sopenharmony_ci		status = -EINVAL;
81662306a36Sopenharmony_ci		goto bail;
81762306a36Sopenharmony_ci	}
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci	for (i = 0; i < info->oi_count; i++) {
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_ci		status = ocfs2_get_request_ptr(info, i, &req_addr, compat_flag);
82262306a36Sopenharmony_ci		if (status)
82362306a36Sopenharmony_ci			break;
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ci		reqp = (struct ocfs2_info_request __user *)(unsigned long)req_addr;
82662306a36Sopenharmony_ci		if (!reqp) {
82762306a36Sopenharmony_ci			status = -EINVAL;
82862306a36Sopenharmony_ci			goto bail;
82962306a36Sopenharmony_ci		}
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_ci		status = ocfs2_info_handle_request(inode, reqp);
83262306a36Sopenharmony_ci		if (status)
83362306a36Sopenharmony_ci			break;
83462306a36Sopenharmony_ci	}
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_cibail:
83762306a36Sopenharmony_ci	return status;
83862306a36Sopenharmony_ci}
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_cilong ocfs2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
84162306a36Sopenharmony_ci{
84262306a36Sopenharmony_ci	struct inode *inode = file_inode(filp);
84362306a36Sopenharmony_ci	void __user *argp = (void __user *)arg;
84462306a36Sopenharmony_ci	int status;
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci	switch (cmd) {
84762306a36Sopenharmony_ci	case OCFS2_IOC_RESVSP:
84862306a36Sopenharmony_ci	case OCFS2_IOC_RESVSP64:
84962306a36Sopenharmony_ci	case OCFS2_IOC_UNRESVSP:
85062306a36Sopenharmony_ci	case OCFS2_IOC_UNRESVSP64:
85162306a36Sopenharmony_ci	{
85262306a36Sopenharmony_ci		struct ocfs2_space_resv sr;
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_ci		if (copy_from_user(&sr, (int __user *) arg, sizeof(sr)))
85562306a36Sopenharmony_ci			return -EFAULT;
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_ci		return ocfs2_change_file_space(filp, cmd, &sr);
85862306a36Sopenharmony_ci	}
85962306a36Sopenharmony_ci	case OCFS2_IOC_GROUP_EXTEND:
86062306a36Sopenharmony_ci	{
86162306a36Sopenharmony_ci		int new_clusters;
86262306a36Sopenharmony_ci
86362306a36Sopenharmony_ci		if (!capable(CAP_SYS_RESOURCE))
86462306a36Sopenharmony_ci			return -EPERM;
86562306a36Sopenharmony_ci
86662306a36Sopenharmony_ci		if (get_user(new_clusters, (int __user *)arg))
86762306a36Sopenharmony_ci			return -EFAULT;
86862306a36Sopenharmony_ci
86962306a36Sopenharmony_ci		status = mnt_want_write_file(filp);
87062306a36Sopenharmony_ci		if (status)
87162306a36Sopenharmony_ci			return status;
87262306a36Sopenharmony_ci		status = ocfs2_group_extend(inode, new_clusters);
87362306a36Sopenharmony_ci		mnt_drop_write_file(filp);
87462306a36Sopenharmony_ci		return status;
87562306a36Sopenharmony_ci	}
87662306a36Sopenharmony_ci	case OCFS2_IOC_GROUP_ADD:
87762306a36Sopenharmony_ci	case OCFS2_IOC_GROUP_ADD64:
87862306a36Sopenharmony_ci	{
87962306a36Sopenharmony_ci		struct ocfs2_new_group_input input;
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_ci		if (!capable(CAP_SYS_RESOURCE))
88262306a36Sopenharmony_ci			return -EPERM;
88362306a36Sopenharmony_ci
88462306a36Sopenharmony_ci		if (copy_from_user(&input, (int __user *) arg, sizeof(input)))
88562306a36Sopenharmony_ci			return -EFAULT;
88662306a36Sopenharmony_ci
88762306a36Sopenharmony_ci		status = mnt_want_write_file(filp);
88862306a36Sopenharmony_ci		if (status)
88962306a36Sopenharmony_ci			return status;
89062306a36Sopenharmony_ci		status = ocfs2_group_add(inode, &input);
89162306a36Sopenharmony_ci		mnt_drop_write_file(filp);
89262306a36Sopenharmony_ci		return status;
89362306a36Sopenharmony_ci	}
89462306a36Sopenharmony_ci	case OCFS2_IOC_REFLINK:
89562306a36Sopenharmony_ci	{
89662306a36Sopenharmony_ci		struct reflink_arguments args;
89762306a36Sopenharmony_ci		const char __user *old_path;
89862306a36Sopenharmony_ci		const char __user *new_path;
89962306a36Sopenharmony_ci		bool preserve;
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_ci		if (copy_from_user(&args, argp, sizeof(args)))
90262306a36Sopenharmony_ci			return -EFAULT;
90362306a36Sopenharmony_ci		old_path = (const char __user *)(unsigned long)args.old_path;
90462306a36Sopenharmony_ci		new_path = (const char __user *)(unsigned long)args.new_path;
90562306a36Sopenharmony_ci		preserve = (args.preserve != 0);
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_ci		return ocfs2_reflink_ioctl(inode, old_path, new_path, preserve);
90862306a36Sopenharmony_ci	}
90962306a36Sopenharmony_ci	case OCFS2_IOC_INFO:
91062306a36Sopenharmony_ci	{
91162306a36Sopenharmony_ci		struct ocfs2_info info;
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_ci		if (copy_from_user(&info, argp, sizeof(struct ocfs2_info)))
91462306a36Sopenharmony_ci			return -EFAULT;
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_ci		return ocfs2_info_handle(inode, &info, 0);
91762306a36Sopenharmony_ci	}
91862306a36Sopenharmony_ci	case FITRIM:
91962306a36Sopenharmony_ci	{
92062306a36Sopenharmony_ci		struct super_block *sb = inode->i_sb;
92162306a36Sopenharmony_ci		struct fstrim_range range;
92262306a36Sopenharmony_ci		int ret = 0;
92362306a36Sopenharmony_ci
92462306a36Sopenharmony_ci		if (!capable(CAP_SYS_ADMIN))
92562306a36Sopenharmony_ci			return -EPERM;
92662306a36Sopenharmony_ci
92762306a36Sopenharmony_ci		if (!bdev_max_discard_sectors(sb->s_bdev))
92862306a36Sopenharmony_ci			return -EOPNOTSUPP;
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci		if (copy_from_user(&range, argp, sizeof(range)))
93162306a36Sopenharmony_ci			return -EFAULT;
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_ci		range.minlen = max_t(u64, bdev_discard_granularity(sb->s_bdev),
93462306a36Sopenharmony_ci				     range.minlen);
93562306a36Sopenharmony_ci		ret = ocfs2_trim_fs(sb, &range);
93662306a36Sopenharmony_ci		if (ret < 0)
93762306a36Sopenharmony_ci			return ret;
93862306a36Sopenharmony_ci
93962306a36Sopenharmony_ci		if (copy_to_user(argp, &range, sizeof(range)))
94062306a36Sopenharmony_ci			return -EFAULT;
94162306a36Sopenharmony_ci
94262306a36Sopenharmony_ci		return 0;
94362306a36Sopenharmony_ci	}
94462306a36Sopenharmony_ci	case OCFS2_IOC_MOVE_EXT:
94562306a36Sopenharmony_ci		return ocfs2_ioctl_move_extents(filp, argp);
94662306a36Sopenharmony_ci	default:
94762306a36Sopenharmony_ci		return -ENOTTY;
94862306a36Sopenharmony_ci	}
94962306a36Sopenharmony_ci}
95062306a36Sopenharmony_ci
95162306a36Sopenharmony_ci#ifdef CONFIG_COMPAT
95262306a36Sopenharmony_cilong ocfs2_compat_ioctl(struct file *file, unsigned cmd, unsigned long arg)
95362306a36Sopenharmony_ci{
95462306a36Sopenharmony_ci	bool preserve;
95562306a36Sopenharmony_ci	struct reflink_arguments args;
95662306a36Sopenharmony_ci	struct inode *inode = file_inode(file);
95762306a36Sopenharmony_ci	struct ocfs2_info info;
95862306a36Sopenharmony_ci	void __user *argp = (void __user *)arg;
95962306a36Sopenharmony_ci
96062306a36Sopenharmony_ci	switch (cmd) {
96162306a36Sopenharmony_ci	case OCFS2_IOC_RESVSP:
96262306a36Sopenharmony_ci	case OCFS2_IOC_RESVSP64:
96362306a36Sopenharmony_ci	case OCFS2_IOC_UNRESVSP:
96462306a36Sopenharmony_ci	case OCFS2_IOC_UNRESVSP64:
96562306a36Sopenharmony_ci	case OCFS2_IOC_GROUP_EXTEND:
96662306a36Sopenharmony_ci	case OCFS2_IOC_GROUP_ADD:
96762306a36Sopenharmony_ci	case OCFS2_IOC_GROUP_ADD64:
96862306a36Sopenharmony_ci		break;
96962306a36Sopenharmony_ci	case OCFS2_IOC_REFLINK:
97062306a36Sopenharmony_ci		if (copy_from_user(&args, argp, sizeof(args)))
97162306a36Sopenharmony_ci			return -EFAULT;
97262306a36Sopenharmony_ci		preserve = (args.preserve != 0);
97362306a36Sopenharmony_ci
97462306a36Sopenharmony_ci		return ocfs2_reflink_ioctl(inode, compat_ptr(args.old_path),
97562306a36Sopenharmony_ci					   compat_ptr(args.new_path), preserve);
97662306a36Sopenharmony_ci	case OCFS2_IOC_INFO:
97762306a36Sopenharmony_ci		if (copy_from_user(&info, argp, sizeof(struct ocfs2_info)))
97862306a36Sopenharmony_ci			return -EFAULT;
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_ci		return ocfs2_info_handle(inode, &info, 1);
98162306a36Sopenharmony_ci	case FITRIM:
98262306a36Sopenharmony_ci	case OCFS2_IOC_MOVE_EXT:
98362306a36Sopenharmony_ci		break;
98462306a36Sopenharmony_ci	default:
98562306a36Sopenharmony_ci		return -ENOIOCTLCMD;
98662306a36Sopenharmony_ci	}
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_ci	return ocfs2_ioctl(file, cmd, arg);
98962306a36Sopenharmony_ci}
99062306a36Sopenharmony_ci#endif
991