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