1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0 2ca4d147eSHerbert Poetzl /* 3ca4d147eSHerbert Poetzl * linux/fs/ocfs2/ioctl.c 4ca4d147eSHerbert Poetzl * 5ca4d147eSHerbert Poetzl * Copyright (C) 2006 Herbert Poetzl 6ca4d147eSHerbert Poetzl * adapted from Remy Card's ext2/ioctl.c 7ca4d147eSHerbert Poetzl */ 8ca4d147eSHerbert Poetzl 9ca4d147eSHerbert Poetzl #include <linux/fs.h> 10ca4d147eSHerbert Poetzl #include <linux/mount.h> 1119e8ac27SJie Liu #include <linux/blkdev.h> 1234e6c59aSTao Ma #include <linux/compat.h> 132b5f52c5SMiklos Szeredi #include <linux/fileattr.h> 14ca4d147eSHerbert Poetzl 15ca4d147eSHerbert Poetzl #include <cluster/masklog.h> 16ca4d147eSHerbert Poetzl 17ca4d147eSHerbert Poetzl #include "ocfs2.h" 18ca4d147eSHerbert Poetzl #include "alloc.h" 19ca4d147eSHerbert Poetzl #include "dlmglue.h" 20b2580103SMark Fasheh #include "file.h" 21ca4d147eSHerbert Poetzl #include "inode.h" 22ca4d147eSHerbert Poetzl #include "journal.h" 23ca4d147eSHerbert Poetzl 24ca4d147eSHerbert Poetzl #include "ocfs2_fs.h" 252d562518SAdrian Bunk #include "ioctl.h" 26d659072fSTao Ma #include "resize.h" 27bd50873dSTao Ma #include "refcounttree.h" 283e5db17dSTristan Ye #include "sysfile.h" 293e5db17dSTristan Ye #include "dir.h" 303e5db17dSTristan Ye #include "buffer_head_io.h" 31d24a10b9STristan Ye #include "suballoc.h" 3253069d4eSTristan Ye #include "move_extents.h" 332d562518SAdrian Bunk 34ddee5cdbSTristan Ye #define o2info_from_user(a, b) \ 35ddee5cdbSTristan Ye copy_from_user(&(a), (b), sizeof(a)) 36ddee5cdbSTristan Ye #define o2info_to_user(a, b) \ 37ddee5cdbSTristan Ye copy_to_user((typeof(a) __user *)b, &(a), sizeof(a)) 38ddee5cdbSTristan Ye 39ddee5cdbSTristan Ye /* 402b462638SBen Hutchings * This is just a best-effort to tell userspace that this request 412b462638SBen Hutchings * caused the error. 42ddee5cdbSTristan Ye */ 438aa1fa36STristan Ye static inline void o2info_set_request_error(struct ocfs2_info_request *kreq, 44ddee5cdbSTristan Ye struct ocfs2_info_request __user *req) 45ddee5cdbSTristan Ye { 46ddee5cdbSTristan Ye kreq->ir_flags |= OCFS2_INFO_FL_ERROR; 47ddee5cdbSTristan Ye (void)put_user(kreq->ir_flags, (__u32 __user *)&(req->ir_flags)); 48ddee5cdbSTristan Ye } 49ddee5cdbSTristan Ye 508aa1fa36STristan Ye static inline void o2info_set_request_filled(struct ocfs2_info_request *req) 511936a267STristan Ye { 521936a267STristan Ye req->ir_flags |= OCFS2_INFO_FL_FILLED; 531936a267STristan Ye } 541936a267STristan Ye 558aa1fa36STristan Ye static inline void o2info_clear_request_filled(struct ocfs2_info_request *req) 561936a267STristan Ye { 571936a267STristan Ye req->ir_flags &= ~OCFS2_INFO_FL_FILLED; 581936a267STristan Ye } 591936a267STristan Ye 603e5db17dSTristan Ye static inline int o2info_coherent(struct ocfs2_info_request *req) 613e5db17dSTristan Ye { 623e5db17dSTristan Ye return (!(req->ir_flags & OCFS2_INFO_FL_NON_COHERENT)); 633e5db17dSTristan Ye } 641936a267STristan Ye 652b5f52c5SMiklos Szeredi int ocfs2_fileattr_get(struct dentry *dentry, struct fileattr *fa) 66ca4d147eSHerbert Poetzl { 672b5f52c5SMiklos Szeredi struct inode *inode = d_inode(dentry); 682b5f52c5SMiklos Szeredi unsigned int flags; 69ca4d147eSHerbert Poetzl int status; 70ca4d147eSHerbert Poetzl 71e63aecb6SMark Fasheh status = ocfs2_inode_lock(inode, NULL, 0); 72ca4d147eSHerbert Poetzl if (status < 0) { 73ca4d147eSHerbert Poetzl mlog_errno(status); 74ca4d147eSHerbert Poetzl return status; 75ca4d147eSHerbert Poetzl } 766e4b0d56SJan Kara ocfs2_get_inode_flags(OCFS2_I(inode)); 772b5f52c5SMiklos Szeredi flags = OCFS2_I(inode)->ip_attr; 78e63aecb6SMark Fasheh ocfs2_inode_unlock(inode, 0); 79ca4d147eSHerbert Poetzl 802b5f52c5SMiklos Szeredi fileattr_fill_flags(fa, flags & OCFS2_FL_VISIBLE); 812b5f52c5SMiklos Szeredi 82ca4d147eSHerbert Poetzl return status; 83ca4d147eSHerbert Poetzl } 84ca4d147eSHerbert Poetzl 858782a9aeSChristian Brauner int ocfs2_fileattr_set(struct mnt_idmap *idmap, 862b5f52c5SMiklos Szeredi struct dentry *dentry, struct fileattr *fa) 87ca4d147eSHerbert Poetzl { 882b5f52c5SMiklos Szeredi struct inode *inode = d_inode(dentry); 892b5f52c5SMiklos Szeredi unsigned int flags = fa->flags; 90ca4d147eSHerbert Poetzl struct ocfs2_inode_info *ocfs2_inode = OCFS2_I(inode); 91ca4d147eSHerbert Poetzl struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); 921fabe148SMark Fasheh handle_t *handle = NULL; 93ca4d147eSHerbert Poetzl struct buffer_head *bh = NULL; 94ca4d147eSHerbert Poetzl unsigned oldflags; 95ca4d147eSHerbert Poetzl int status; 96ca4d147eSHerbert Poetzl 972b5f52c5SMiklos Szeredi if (fileattr_has_fsx(fa)) 982b5f52c5SMiklos Szeredi return -EOPNOTSUPP; 99ca4d147eSHerbert Poetzl 100e63aecb6SMark Fasheh status = ocfs2_inode_lock(inode, &bh, 1); 101ca4d147eSHerbert Poetzl if (status < 0) { 102ca4d147eSHerbert Poetzl mlog_errno(status); 103ca4d147eSHerbert Poetzl goto bail; 104ca4d147eSHerbert Poetzl } 105ca4d147eSHerbert Poetzl 106ca4d147eSHerbert Poetzl if (!S_ISDIR(inode->i_mode)) 107ca4d147eSHerbert Poetzl flags &= ~OCFS2_DIRSYNC_FL; 108ca4d147eSHerbert Poetzl 109ca4d147eSHerbert Poetzl oldflags = ocfs2_inode->ip_attr; 1102b5f52c5SMiklos Szeredi flags = flags & OCFS2_FL_MODIFIABLE; 1112b5f52c5SMiklos Szeredi flags |= oldflags & ~OCFS2_FL_MODIFIABLE; 112ca4d147eSHerbert Poetzl 1132b5f52c5SMiklos Szeredi /* Check already done by VFS, but repeat with ocfs lock */ 1142b5f52c5SMiklos Szeredi status = -EPERM; 1152b5f52c5SMiklos Szeredi if ((flags ^ oldflags) & (FS_APPEND_FL | FS_IMMUTABLE_FL) && 1162b5f52c5SMiklos Szeredi !capable(CAP_LINUX_IMMUTABLE)) 117b3e0767aSJeff Liu goto bail_unlock; 118b3e0767aSJeff Liu 119b3e0767aSJeff Liu handle = ocfs2_start_trans(osb, OCFS2_INODE_UPDATE_CREDITS); 120b3e0767aSJeff Liu if (IS_ERR(handle)) { 121b3e0767aSJeff Liu status = PTR_ERR(handle); 122b3e0767aSJeff Liu mlog_errno(status); 123b3e0767aSJeff Liu goto bail_unlock; 124ca4d147eSHerbert Poetzl } 125ca4d147eSHerbert Poetzl 126ca4d147eSHerbert Poetzl ocfs2_inode->ip_attr = flags; 127ca4d147eSHerbert Poetzl ocfs2_set_inode_flags(inode); 128*c9abe099SSu Yue inode_set_ctime_current(inode); 129ca4d147eSHerbert Poetzl 130ca4d147eSHerbert Poetzl status = ocfs2_mark_inode_dirty(handle, inode, bh); 131ca4d147eSHerbert Poetzl if (status < 0) 132ca4d147eSHerbert Poetzl mlog_errno(status); 133ca4d147eSHerbert Poetzl 13402dc1af4SMark Fasheh ocfs2_commit_trans(osb, handle); 135b3e0767aSJeff Liu 136ca4d147eSHerbert Poetzl bail_unlock: 137e63aecb6SMark Fasheh ocfs2_inode_unlock(inode, 1); 138ca4d147eSHerbert Poetzl bail: 139ca4d147eSHerbert Poetzl brelse(bh); 140ca4d147eSHerbert Poetzl 141ca4d147eSHerbert Poetzl return status; 142ca4d147eSHerbert Poetzl } 143ca4d147eSHerbert Poetzl 144c253ed1fSFabian Frederick static int ocfs2_info_handle_blocksize(struct inode *inode, 145ddee5cdbSTristan Ye struct ocfs2_info_request __user *req) 146ddee5cdbSTristan Ye { 147ddee5cdbSTristan Ye struct ocfs2_info_blocksize oib; 148ddee5cdbSTristan Ye 149ddee5cdbSTristan Ye if (o2info_from_user(oib, req)) 1502b462638SBen Hutchings return -EFAULT; 151ddee5cdbSTristan Ye 152ddee5cdbSTristan Ye oib.ib_blocksize = inode->i_sb->s_blocksize; 1531936a267STristan Ye 1548aa1fa36STristan Ye o2info_set_request_filled(&oib.ib_req); 155ddee5cdbSTristan Ye 156ddee5cdbSTristan Ye if (o2info_to_user(oib, req)) 1572b462638SBen Hutchings return -EFAULT; 158ddee5cdbSTristan Ye 1592b462638SBen Hutchings return 0; 160ddee5cdbSTristan Ye } 161ddee5cdbSTristan Ye 162c253ed1fSFabian Frederick static int ocfs2_info_handle_clustersize(struct inode *inode, 163ddee5cdbSTristan Ye struct ocfs2_info_request __user *req) 164ddee5cdbSTristan Ye { 165ddee5cdbSTristan Ye struct ocfs2_info_clustersize oic; 166ddee5cdbSTristan Ye struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); 167ddee5cdbSTristan Ye 168ddee5cdbSTristan Ye if (o2info_from_user(oic, req)) 1692b462638SBen Hutchings return -EFAULT; 170ddee5cdbSTristan Ye 171ddee5cdbSTristan Ye oic.ic_clustersize = osb->s_clustersize; 1721936a267STristan Ye 1738aa1fa36STristan Ye o2info_set_request_filled(&oic.ic_req); 174ddee5cdbSTristan Ye 175ddee5cdbSTristan Ye if (o2info_to_user(oic, req)) 1762b462638SBen Hutchings return -EFAULT; 177ddee5cdbSTristan Ye 1782b462638SBen Hutchings return 0; 179ddee5cdbSTristan Ye } 180ddee5cdbSTristan Ye 181c253ed1fSFabian Frederick static int ocfs2_info_handle_maxslots(struct inode *inode, 182ddee5cdbSTristan Ye struct ocfs2_info_request __user *req) 183ddee5cdbSTristan Ye { 184ddee5cdbSTristan Ye struct ocfs2_info_maxslots oim; 185ddee5cdbSTristan Ye struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); 186ddee5cdbSTristan Ye 187ddee5cdbSTristan Ye if (o2info_from_user(oim, req)) 1882b462638SBen Hutchings return -EFAULT; 189ddee5cdbSTristan Ye 190ddee5cdbSTristan Ye oim.im_max_slots = osb->max_slots; 1911936a267STristan Ye 1928aa1fa36STristan Ye o2info_set_request_filled(&oim.im_req); 193ddee5cdbSTristan Ye 194ddee5cdbSTristan Ye if (o2info_to_user(oim, req)) 1952b462638SBen Hutchings return -EFAULT; 196ddee5cdbSTristan Ye 1972b462638SBen Hutchings return 0; 198ddee5cdbSTristan Ye } 199ddee5cdbSTristan Ye 200c253ed1fSFabian Frederick static int ocfs2_info_handle_label(struct inode *inode, 201ddee5cdbSTristan Ye struct ocfs2_info_request __user *req) 202ddee5cdbSTristan Ye { 203ddee5cdbSTristan Ye struct ocfs2_info_label oil; 204ddee5cdbSTristan Ye struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); 205ddee5cdbSTristan Ye 206ddee5cdbSTristan Ye if (o2info_from_user(oil, req)) 2072b462638SBen Hutchings return -EFAULT; 208ddee5cdbSTristan Ye 209ddee5cdbSTristan Ye memcpy(oil.il_label, osb->vol_label, OCFS2_MAX_VOL_LABEL_LEN); 2101936a267STristan Ye 2118aa1fa36STristan Ye o2info_set_request_filled(&oil.il_req); 212ddee5cdbSTristan Ye 213ddee5cdbSTristan Ye if (o2info_to_user(oil, req)) 2142b462638SBen Hutchings return -EFAULT; 215ddee5cdbSTristan Ye 2162b462638SBen Hutchings return 0; 217ddee5cdbSTristan Ye } 218ddee5cdbSTristan Ye 219c253ed1fSFabian Frederick static int ocfs2_info_handle_uuid(struct inode *inode, 220ddee5cdbSTristan Ye struct ocfs2_info_request __user *req) 221ddee5cdbSTristan Ye { 222ddee5cdbSTristan Ye struct ocfs2_info_uuid oiu; 223ddee5cdbSTristan Ye struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); 224ddee5cdbSTristan Ye 225ddee5cdbSTristan Ye if (o2info_from_user(oiu, req)) 2262b462638SBen Hutchings return -EFAULT; 227ddee5cdbSTristan Ye 228ddee5cdbSTristan Ye memcpy(oiu.iu_uuid_str, osb->uuid_str, OCFS2_TEXT_UUID_LEN + 1); 2291936a267STristan Ye 2308aa1fa36STristan Ye o2info_set_request_filled(&oiu.iu_req); 231ddee5cdbSTristan Ye 232ddee5cdbSTristan Ye if (o2info_to_user(oiu, req)) 2332b462638SBen Hutchings return -EFAULT; 234ddee5cdbSTristan Ye 2352b462638SBen Hutchings return 0; 236ddee5cdbSTristan Ye } 237ddee5cdbSTristan Ye 238c253ed1fSFabian Frederick static int ocfs2_info_handle_fs_features(struct inode *inode, 239ddee5cdbSTristan Ye struct ocfs2_info_request __user *req) 240ddee5cdbSTristan Ye { 241ddee5cdbSTristan Ye struct ocfs2_info_fs_features oif; 242ddee5cdbSTristan Ye struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); 243ddee5cdbSTristan Ye 244ddee5cdbSTristan Ye if (o2info_from_user(oif, req)) 2452b462638SBen Hutchings return -EFAULT; 246ddee5cdbSTristan Ye 247ddee5cdbSTristan Ye oif.if_compat_features = osb->s_feature_compat; 248ddee5cdbSTristan Ye oif.if_incompat_features = osb->s_feature_incompat; 249ddee5cdbSTristan Ye oif.if_ro_compat_features = osb->s_feature_ro_compat; 2501936a267STristan Ye 2518aa1fa36STristan Ye o2info_set_request_filled(&oif.if_req); 252ddee5cdbSTristan Ye 253ddee5cdbSTristan Ye if (o2info_to_user(oif, req)) 2542b462638SBen Hutchings return -EFAULT; 255ddee5cdbSTristan Ye 2562b462638SBen Hutchings return 0; 257ddee5cdbSTristan Ye } 258ddee5cdbSTristan Ye 259c253ed1fSFabian Frederick static int ocfs2_info_handle_journal_size(struct inode *inode, 260ddee5cdbSTristan Ye struct ocfs2_info_request __user *req) 261ddee5cdbSTristan Ye { 262ddee5cdbSTristan Ye struct ocfs2_info_journal_size oij; 263ddee5cdbSTristan Ye struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); 264ddee5cdbSTristan Ye 265ddee5cdbSTristan Ye if (o2info_from_user(oij, req)) 2662b462638SBen Hutchings return -EFAULT; 267ddee5cdbSTristan Ye 268f17c20ddSJunxiao Bi oij.ij_journal_size = i_size_read(osb->journal->j_inode); 269ddee5cdbSTristan Ye 2708aa1fa36STristan Ye o2info_set_request_filled(&oij.ij_req); 271ddee5cdbSTristan Ye 272ddee5cdbSTristan Ye if (o2info_to_user(oij, req)) 2732b462638SBen Hutchings return -EFAULT; 274ddee5cdbSTristan Ye 2752b462638SBen Hutchings return 0; 276ddee5cdbSTristan Ye } 277ddee5cdbSTristan Ye 278c253ed1fSFabian Frederick static int ocfs2_info_scan_inode_alloc(struct ocfs2_super *osb, 2793e5db17dSTristan Ye struct inode *inode_alloc, u64 blkno, 280c253ed1fSFabian Frederick struct ocfs2_info_freeinode *fi, 281c253ed1fSFabian Frederick u32 slot) 2823e5db17dSTristan Ye { 2833e5db17dSTristan Ye int status = 0, unlock = 0; 2843e5db17dSTristan Ye 2853e5db17dSTristan Ye struct buffer_head *bh = NULL; 2863e5db17dSTristan Ye struct ocfs2_dinode *dinode_alloc = NULL; 2873e5db17dSTristan Ye 2883e5db17dSTristan Ye if (inode_alloc) 2895955102cSAl Viro inode_lock(inode_alloc); 2903e5db17dSTristan Ye 2912abb7d3bSJia-Ju Bai if (inode_alloc && o2info_coherent(&fi->ifi_req)) { 2923e5db17dSTristan Ye status = ocfs2_inode_lock(inode_alloc, &bh, 0); 2933e5db17dSTristan Ye if (status < 0) { 2943e5db17dSTristan Ye mlog_errno(status); 2953e5db17dSTristan Ye goto bail; 2963e5db17dSTristan Ye } 2973e5db17dSTristan Ye unlock = 1; 2983e5db17dSTristan Ye } else { 2993e5db17dSTristan Ye status = ocfs2_read_blocks_sync(osb, blkno, 1, &bh); 3003e5db17dSTristan Ye if (status < 0) { 3013e5db17dSTristan Ye mlog_errno(status); 3023e5db17dSTristan Ye goto bail; 3033e5db17dSTristan Ye } 3043e5db17dSTristan Ye } 3053e5db17dSTristan Ye 3063e5db17dSTristan Ye dinode_alloc = (struct ocfs2_dinode *)bh->b_data; 3073e5db17dSTristan Ye 3083e5db17dSTristan Ye fi->ifi_stat[slot].lfi_total = 3093e5db17dSTristan Ye le32_to_cpu(dinode_alloc->id1.bitmap1.i_total); 3103e5db17dSTristan Ye fi->ifi_stat[slot].lfi_free = 3113e5db17dSTristan Ye le32_to_cpu(dinode_alloc->id1.bitmap1.i_total) - 3123e5db17dSTristan Ye le32_to_cpu(dinode_alloc->id1.bitmap1.i_used); 3133e5db17dSTristan Ye 3143e5db17dSTristan Ye bail: 3153e5db17dSTristan Ye if (unlock) 3163e5db17dSTristan Ye ocfs2_inode_unlock(inode_alloc, 0); 3173e5db17dSTristan Ye 3183e5db17dSTristan Ye if (inode_alloc) 3195955102cSAl Viro inode_unlock(inode_alloc); 3203e5db17dSTristan Ye 3213e5db17dSTristan Ye brelse(bh); 3223e5db17dSTristan Ye 3233e5db17dSTristan Ye return status; 3243e5db17dSTristan Ye } 3253e5db17dSTristan Ye 326c253ed1fSFabian Frederick static int ocfs2_info_handle_freeinode(struct inode *inode, 3273e5db17dSTristan Ye struct ocfs2_info_request __user *req) 3283e5db17dSTristan Ye { 3293e5db17dSTristan Ye u32 i; 3303e5db17dSTristan Ye u64 blkno = -1; 3313e5db17dSTristan Ye char namebuf[40]; 3322b462638SBen Hutchings int status, type = INODE_ALLOC_SYSTEM_INODE; 3333e5db17dSTristan Ye struct ocfs2_info_freeinode *oifi = NULL; 3343e5db17dSTristan Ye struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); 3353e5db17dSTristan Ye struct inode *inode_alloc = NULL; 3363e5db17dSTristan Ye 3373e5db17dSTristan Ye oifi = kzalloc(sizeof(struct ocfs2_info_freeinode), GFP_KERNEL); 3383e5db17dSTristan Ye if (!oifi) { 3393e5db17dSTristan Ye status = -ENOMEM; 3403e5db17dSTristan Ye mlog_errno(status); 34187f0d5c8SDan Carpenter goto out_err; 3423e5db17dSTristan Ye } 3433e5db17dSTristan Ye 3442b462638SBen Hutchings if (o2info_from_user(*oifi, req)) { 3452b462638SBen Hutchings status = -EFAULT; 3462b462638SBen Hutchings goto out_free; 3472b462638SBen Hutchings } 3483e5db17dSTristan Ye 3493e5db17dSTristan Ye oifi->ifi_slotnum = osb->max_slots; 3503e5db17dSTristan Ye 3513e5db17dSTristan Ye for (i = 0; i < oifi->ifi_slotnum; i++) { 3523e5db17dSTristan Ye if (o2info_coherent(&oifi->ifi_req)) { 3533e5db17dSTristan Ye inode_alloc = ocfs2_get_system_file_inode(osb, type, i); 3543e5db17dSTristan Ye if (!inode_alloc) { 3553e5db17dSTristan Ye mlog(ML_ERROR, "unable to get alloc inode in " 3563e5db17dSTristan Ye "slot %u\n", i); 3573e5db17dSTristan Ye status = -EIO; 3583e5db17dSTristan Ye goto bail; 3593e5db17dSTristan Ye } 3603e5db17dSTristan Ye } else { 3613e5db17dSTristan Ye ocfs2_sprintf_system_inode_name(namebuf, 3623e5db17dSTristan Ye sizeof(namebuf), 3633e5db17dSTristan Ye type, i); 3643e5db17dSTristan Ye status = ocfs2_lookup_ino_from_name(osb->sys_root_inode, 3653e5db17dSTristan Ye namebuf, 3663e5db17dSTristan Ye strlen(namebuf), 3673e5db17dSTristan Ye &blkno); 3683e5db17dSTristan Ye if (status < 0) { 3693e5db17dSTristan Ye status = -ENOENT; 3703e5db17dSTristan Ye goto bail; 3713e5db17dSTristan Ye } 3723e5db17dSTristan Ye } 3733e5db17dSTristan Ye 3743e5db17dSTristan Ye status = ocfs2_info_scan_inode_alloc(osb, inode_alloc, blkno, oifi, i); 3753e5db17dSTristan Ye 3763e5db17dSTristan Ye iput(inode_alloc); 3773e5db17dSTristan Ye inode_alloc = NULL; 3787dc3e839Sjiangyiwen 3797dc3e839Sjiangyiwen if (status < 0) 3807dc3e839Sjiangyiwen goto bail; 3813e5db17dSTristan Ye } 3823e5db17dSTristan Ye 3833e5db17dSTristan Ye o2info_set_request_filled(&oifi->ifi_req); 3843e5db17dSTristan Ye 3852b462638SBen Hutchings if (o2info_to_user(*oifi, req)) { 3862b462638SBen Hutchings status = -EFAULT; 3872b462638SBen Hutchings goto out_free; 3882b462638SBen Hutchings } 3893e5db17dSTristan Ye 3903e5db17dSTristan Ye status = 0; 3913e5db17dSTristan Ye bail: 3923e5db17dSTristan Ye if (status) 3933e5db17dSTristan Ye o2info_set_request_error(&oifi->ifi_req, req); 3942b462638SBen Hutchings out_free: 3953e5db17dSTristan Ye kfree(oifi); 39687f0d5c8SDan Carpenter out_err: 3973e5db17dSTristan Ye return status; 3983e5db17dSTristan Ye } 3993e5db17dSTristan Ye 400d24a10b9STristan Ye static void o2ffg_update_histogram(struct ocfs2_info_free_chunk_list *hist, 401d24a10b9STristan Ye unsigned int chunksize) 402d24a10b9STristan Ye { 403731a40faSZhen Lei u32 index; 404d24a10b9STristan Ye 405d24a10b9STristan Ye index = __ilog2_u32(chunksize); 406d24a10b9STristan Ye if (index >= OCFS2_INFO_MAX_HIST) 407d24a10b9STristan Ye index = OCFS2_INFO_MAX_HIST - 1; 408d24a10b9STristan Ye 409d24a10b9STristan Ye hist->fc_chunks[index]++; 410d24a10b9STristan Ye hist->fc_clusters[index] += chunksize; 411d24a10b9STristan Ye } 412d24a10b9STristan Ye 413d24a10b9STristan Ye static void o2ffg_update_stats(struct ocfs2_info_freefrag_stats *stats, 414d24a10b9STristan Ye unsigned int chunksize) 415d24a10b9STristan Ye { 416d24a10b9STristan Ye if (chunksize > stats->ffs_max) 417d24a10b9STristan Ye stats->ffs_max = chunksize; 418d24a10b9STristan Ye 419d24a10b9STristan Ye if (chunksize < stats->ffs_min) 420d24a10b9STristan Ye stats->ffs_min = chunksize; 421d24a10b9STristan Ye 422d24a10b9STristan Ye stats->ffs_avg += chunksize; 423d24a10b9STristan Ye stats->ffs_free_chunks_real++; 424d24a10b9STristan Ye } 425d24a10b9STristan Ye 426c253ed1fSFabian Frederick static void ocfs2_info_update_ffg(struct ocfs2_info_freefrag *ffg, 427d24a10b9STristan Ye unsigned int chunksize) 428d24a10b9STristan Ye { 429d24a10b9STristan Ye o2ffg_update_histogram(&(ffg->iff_ffs.ffs_fc_hist), chunksize); 430d24a10b9STristan Ye o2ffg_update_stats(&(ffg->iff_ffs), chunksize); 431d24a10b9STristan Ye } 432d24a10b9STristan Ye 433c253ed1fSFabian Frederick static int ocfs2_info_freefrag_scan_chain(struct ocfs2_super *osb, 434d24a10b9STristan Ye struct inode *gb_inode, 435d24a10b9STristan Ye struct ocfs2_dinode *gb_dinode, 436d24a10b9STristan Ye struct ocfs2_chain_rec *rec, 437d24a10b9STristan Ye struct ocfs2_info_freefrag *ffg, 438d24a10b9STristan Ye u32 chunks_in_group) 439d24a10b9STristan Ye { 440d24a10b9STristan Ye int status = 0, used; 441d24a10b9STristan Ye u64 blkno; 442d24a10b9STristan Ye 443d24a10b9STristan Ye struct buffer_head *bh = NULL; 444d24a10b9STristan Ye struct ocfs2_group_desc *bg = NULL; 445d24a10b9STristan Ye 446d24a10b9STristan Ye unsigned int max_bits, num_clusters; 447d24a10b9STristan Ye unsigned int offset = 0, cluster, chunk; 448d24a10b9STristan Ye unsigned int chunk_free, last_chunksize = 0; 449d24a10b9STristan Ye 450d24a10b9STristan Ye if (!le32_to_cpu(rec->c_free)) 451d24a10b9STristan Ye goto bail; 452d24a10b9STristan Ye 453d24a10b9STristan Ye do { 454d24a10b9STristan Ye if (!bg) 455d24a10b9STristan Ye blkno = le64_to_cpu(rec->c_blkno); 456d24a10b9STristan Ye else 457d24a10b9STristan Ye blkno = le64_to_cpu(bg->bg_next_group); 458d24a10b9STristan Ye 459d24a10b9STristan Ye if (bh) { 460d24a10b9STristan Ye brelse(bh); 461d24a10b9STristan Ye bh = NULL; 462d24a10b9STristan Ye } 463d24a10b9STristan Ye 464d24a10b9STristan Ye if (o2info_coherent(&ffg->iff_req)) 465d24a10b9STristan Ye status = ocfs2_read_group_descriptor(gb_inode, 466d24a10b9STristan Ye gb_dinode, 467d24a10b9STristan Ye blkno, &bh); 468d24a10b9STristan Ye else 469d24a10b9STristan Ye status = ocfs2_read_blocks_sync(osb, blkno, 1, &bh); 470d24a10b9STristan Ye 471d24a10b9STristan Ye if (status < 0) { 472d24a10b9STristan Ye mlog(ML_ERROR, "Can't read the group descriptor # " 473d24a10b9STristan Ye "%llu from device.", (unsigned long long)blkno); 474d24a10b9STristan Ye status = -EIO; 475d24a10b9STristan Ye goto bail; 476d24a10b9STristan Ye } 477d24a10b9STristan Ye 478d24a10b9STristan Ye bg = (struct ocfs2_group_desc *)bh->b_data; 479d24a10b9STristan Ye 480d24a10b9STristan Ye if (!le16_to_cpu(bg->bg_free_bits_count)) 481d24a10b9STristan Ye continue; 482d24a10b9STristan Ye 483d24a10b9STristan Ye max_bits = le16_to_cpu(bg->bg_bits); 484d24a10b9STristan Ye offset = 0; 485d24a10b9STristan Ye 486d24a10b9STristan Ye for (chunk = 0; chunk < chunks_in_group; chunk++) { 487d24a10b9STristan Ye /* 488d24a10b9STristan Ye * last chunk may be not an entire one. 489d24a10b9STristan Ye */ 490d24a10b9STristan Ye if ((offset + ffg->iff_chunksize) > max_bits) 491d24a10b9STristan Ye num_clusters = max_bits - offset; 492d24a10b9STristan Ye else 493d24a10b9STristan Ye num_clusters = ffg->iff_chunksize; 494d24a10b9STristan Ye 495d24a10b9STristan Ye chunk_free = 0; 496d24a10b9STristan Ye for (cluster = 0; cluster < num_clusters; cluster++) { 497d24a10b9STristan Ye used = ocfs2_test_bit(offset, 498d24a10b9STristan Ye (unsigned long *)bg->bg_bitmap); 499d24a10b9STristan Ye /* 500d24a10b9STristan Ye * - chunk_free counts free clusters in #N chunk. 501d24a10b9STristan Ye * - last_chunksize records the size(in) clusters 502d24a10b9STristan Ye * for the last real free chunk being counted. 503d24a10b9STristan Ye */ 504d24a10b9STristan Ye if (!used) { 505d24a10b9STristan Ye last_chunksize++; 506d24a10b9STristan Ye chunk_free++; 507d24a10b9STristan Ye } 508d24a10b9STristan Ye 509d24a10b9STristan Ye if (used && last_chunksize) { 510d24a10b9STristan Ye ocfs2_info_update_ffg(ffg, 511d24a10b9STristan Ye last_chunksize); 512d24a10b9STristan Ye last_chunksize = 0; 513d24a10b9STristan Ye } 514d24a10b9STristan Ye 515d24a10b9STristan Ye offset++; 516d24a10b9STristan Ye } 517d24a10b9STristan Ye 518d24a10b9STristan Ye if (chunk_free == ffg->iff_chunksize) 519d24a10b9STristan Ye ffg->iff_ffs.ffs_free_chunks++; 520d24a10b9STristan Ye } 521d24a10b9STristan Ye 522d24a10b9STristan Ye /* 523d24a10b9STristan Ye * need to update the info for last free chunk. 524d24a10b9STristan Ye */ 525d24a10b9STristan Ye if (last_chunksize) 526d24a10b9STristan Ye ocfs2_info_update_ffg(ffg, last_chunksize); 527d24a10b9STristan Ye 528d24a10b9STristan Ye } while (le64_to_cpu(bg->bg_next_group)); 529d24a10b9STristan Ye 530d24a10b9STristan Ye bail: 531d24a10b9STristan Ye brelse(bh); 532d24a10b9STristan Ye 533d24a10b9STristan Ye return status; 534d24a10b9STristan Ye } 535d24a10b9STristan Ye 536c253ed1fSFabian Frederick static int ocfs2_info_freefrag_scan_bitmap(struct ocfs2_super *osb, 537d24a10b9STristan Ye struct inode *gb_inode, u64 blkno, 538d24a10b9STristan Ye struct ocfs2_info_freefrag *ffg) 539d24a10b9STristan Ye { 540d24a10b9STristan Ye u32 chunks_in_group; 541d24a10b9STristan Ye int status = 0, unlock = 0, i; 542d24a10b9STristan Ye 543d24a10b9STristan Ye struct buffer_head *bh = NULL; 544d24a10b9STristan Ye struct ocfs2_chain_list *cl = NULL; 545d24a10b9STristan Ye struct ocfs2_chain_rec *rec = NULL; 546d24a10b9STristan Ye struct ocfs2_dinode *gb_dinode = NULL; 547d24a10b9STristan Ye 548d24a10b9STristan Ye if (gb_inode) 5495955102cSAl Viro inode_lock(gb_inode); 550d24a10b9STristan Ye 551d24a10b9STristan Ye if (o2info_coherent(&ffg->iff_req)) { 552d24a10b9STristan Ye status = ocfs2_inode_lock(gb_inode, &bh, 0); 553d24a10b9STristan Ye if (status < 0) { 554d24a10b9STristan Ye mlog_errno(status); 555d24a10b9STristan Ye goto bail; 556d24a10b9STristan Ye } 557d24a10b9STristan Ye unlock = 1; 558d24a10b9STristan Ye } else { 559d24a10b9STristan Ye status = ocfs2_read_blocks_sync(osb, blkno, 1, &bh); 560d24a10b9STristan Ye if (status < 0) { 561d24a10b9STristan Ye mlog_errno(status); 562d24a10b9STristan Ye goto bail; 563d24a10b9STristan Ye } 564d24a10b9STristan Ye } 565d24a10b9STristan Ye 566d24a10b9STristan Ye gb_dinode = (struct ocfs2_dinode *)bh->b_data; 567d24a10b9STristan Ye cl = &(gb_dinode->id2.i_chain); 568d24a10b9STristan Ye 569d24a10b9STristan Ye /* 570d24a10b9STristan Ye * Chunksize(in) clusters from userspace should be 571d24a10b9STristan Ye * less than clusters in a group. 572d24a10b9STristan Ye */ 573d24a10b9STristan Ye if (ffg->iff_chunksize > le16_to_cpu(cl->cl_cpg)) { 574d24a10b9STristan Ye status = -EINVAL; 575d24a10b9STristan Ye goto bail; 576d24a10b9STristan Ye } 577d24a10b9STristan Ye 578d24a10b9STristan Ye memset(&ffg->iff_ffs, 0, sizeof(struct ocfs2_info_freefrag_stats)); 579d24a10b9STristan Ye 580d24a10b9STristan Ye ffg->iff_ffs.ffs_min = ~0U; 581d24a10b9STristan Ye ffg->iff_ffs.ffs_clusters = 582d24a10b9STristan Ye le32_to_cpu(gb_dinode->id1.bitmap1.i_total); 583d24a10b9STristan Ye ffg->iff_ffs.ffs_free_clusters = ffg->iff_ffs.ffs_clusters - 584d24a10b9STristan Ye le32_to_cpu(gb_dinode->id1.bitmap1.i_used); 585d24a10b9STristan Ye 586d24a10b9STristan Ye chunks_in_group = le16_to_cpu(cl->cl_cpg) / ffg->iff_chunksize + 1; 587d24a10b9STristan Ye 588d24a10b9STristan Ye for (i = 0; i < le16_to_cpu(cl->cl_next_free_rec); i++) { 589d24a10b9STristan Ye rec = &(cl->cl_recs[i]); 590d24a10b9STristan Ye status = ocfs2_info_freefrag_scan_chain(osb, gb_inode, 591d24a10b9STristan Ye gb_dinode, 592d24a10b9STristan Ye rec, ffg, 593d24a10b9STristan Ye chunks_in_group); 594d24a10b9STristan Ye if (status) 595d24a10b9STristan Ye goto bail; 596d24a10b9STristan Ye } 597d24a10b9STristan Ye 598d24a10b9STristan Ye if (ffg->iff_ffs.ffs_free_chunks_real) 599d24a10b9STristan Ye ffg->iff_ffs.ffs_avg = (ffg->iff_ffs.ffs_avg / 600d24a10b9STristan Ye ffg->iff_ffs.ffs_free_chunks_real); 601d24a10b9STristan Ye bail: 602d24a10b9STristan Ye if (unlock) 603d24a10b9STristan Ye ocfs2_inode_unlock(gb_inode, 0); 604d24a10b9STristan Ye 605d24a10b9STristan Ye if (gb_inode) 6065955102cSAl Viro inode_unlock(gb_inode); 607d24a10b9STristan Ye 608d24a10b9STristan Ye iput(gb_inode); 609d24a10b9STristan Ye brelse(bh); 610d24a10b9STristan Ye 611d24a10b9STristan Ye return status; 612d24a10b9STristan Ye } 613d24a10b9STristan Ye 614c253ed1fSFabian Frederick static int ocfs2_info_handle_freefrag(struct inode *inode, 615d24a10b9STristan Ye struct ocfs2_info_request __user *req) 616d24a10b9STristan Ye { 617d24a10b9STristan Ye u64 blkno = -1; 618d24a10b9STristan Ye char namebuf[40]; 6192b462638SBen Hutchings int status, type = GLOBAL_BITMAP_SYSTEM_INODE; 620d24a10b9STristan Ye 621d24a10b9STristan Ye struct ocfs2_info_freefrag *oiff; 622d24a10b9STristan Ye struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); 623d24a10b9STristan Ye struct inode *gb_inode = NULL; 624d24a10b9STristan Ye 625d24a10b9STristan Ye oiff = kzalloc(sizeof(struct ocfs2_info_freefrag), GFP_KERNEL); 626d24a10b9STristan Ye if (!oiff) { 627d24a10b9STristan Ye status = -ENOMEM; 628d24a10b9STristan Ye mlog_errno(status); 62987f0d5c8SDan Carpenter goto out_err; 630d24a10b9STristan Ye } 631d24a10b9STristan Ye 6322b462638SBen Hutchings if (o2info_from_user(*oiff, req)) { 6332b462638SBen Hutchings status = -EFAULT; 6342b462638SBen Hutchings goto out_free; 6352b462638SBen Hutchings } 636d24a10b9STristan Ye /* 637d24a10b9STristan Ye * chunksize from userspace should be power of 2. 638d24a10b9STristan Ye */ 639d24a10b9STristan Ye if ((oiff->iff_chunksize & (oiff->iff_chunksize - 1)) || 640d24a10b9STristan Ye (!oiff->iff_chunksize)) { 641d24a10b9STristan Ye status = -EINVAL; 642d24a10b9STristan Ye goto bail; 643d24a10b9STristan Ye } 644d24a10b9STristan Ye 645d24a10b9STristan Ye if (o2info_coherent(&oiff->iff_req)) { 646d24a10b9STristan Ye gb_inode = ocfs2_get_system_file_inode(osb, type, 647d24a10b9STristan Ye OCFS2_INVALID_SLOT); 648d24a10b9STristan Ye if (!gb_inode) { 649d24a10b9STristan Ye mlog(ML_ERROR, "unable to get global_bitmap inode\n"); 650d24a10b9STristan Ye status = -EIO; 651d24a10b9STristan Ye goto bail; 652d24a10b9STristan Ye } 653d24a10b9STristan Ye } else { 654d24a10b9STristan Ye ocfs2_sprintf_system_inode_name(namebuf, sizeof(namebuf), type, 655d24a10b9STristan Ye OCFS2_INVALID_SLOT); 656d24a10b9STristan Ye status = ocfs2_lookup_ino_from_name(osb->sys_root_inode, 657d24a10b9STristan Ye namebuf, 658d24a10b9STristan Ye strlen(namebuf), 659d24a10b9STristan Ye &blkno); 660d24a10b9STristan Ye if (status < 0) { 661d24a10b9STristan Ye status = -ENOENT; 662d24a10b9STristan Ye goto bail; 663d24a10b9STristan Ye } 664d24a10b9STristan Ye } 665d24a10b9STristan Ye 666d24a10b9STristan Ye status = ocfs2_info_freefrag_scan_bitmap(osb, gb_inode, blkno, oiff); 667d24a10b9STristan Ye if (status < 0) 668d24a10b9STristan Ye goto bail; 669d24a10b9STristan Ye 670d24a10b9STristan Ye o2info_set_request_filled(&oiff->iff_req); 671d24a10b9STristan Ye 6727ebab453SWei Yongjun if (o2info_to_user(*oiff, req)) { 6737ebab453SWei Yongjun status = -EFAULT; 6742b462638SBen Hutchings goto out_free; 6757ebab453SWei Yongjun } 676d24a10b9STristan Ye 677d24a10b9STristan Ye status = 0; 678d24a10b9STristan Ye bail: 679d24a10b9STristan Ye if (status) 680d24a10b9STristan Ye o2info_set_request_error(&oiff->iff_req, req); 6812b462638SBen Hutchings out_free: 682d24a10b9STristan Ye kfree(oiff); 68387f0d5c8SDan Carpenter out_err: 684ddee5cdbSTristan Ye return status; 685ddee5cdbSTristan Ye } 686ddee5cdbSTristan Ye 687c253ed1fSFabian Frederick static int ocfs2_info_handle_unknown(struct inode *inode, 688ddee5cdbSTristan Ye struct ocfs2_info_request __user *req) 689ddee5cdbSTristan Ye { 690ddee5cdbSTristan Ye struct ocfs2_info_request oir; 691ddee5cdbSTristan Ye 692ddee5cdbSTristan Ye if (o2info_from_user(oir, req)) 6932b462638SBen Hutchings return -EFAULT; 694ddee5cdbSTristan Ye 6958aa1fa36STristan Ye o2info_clear_request_filled(&oir); 696ddee5cdbSTristan Ye 697ddee5cdbSTristan Ye if (o2info_to_user(oir, req)) 6982b462638SBen Hutchings return -EFAULT; 699ddee5cdbSTristan Ye 7002b462638SBen Hutchings return 0; 701ddee5cdbSTristan Ye } 702ddee5cdbSTristan Ye 703ddee5cdbSTristan Ye /* 704ddee5cdbSTristan Ye * Validate and distinguish OCFS2_IOC_INFO requests. 705ddee5cdbSTristan Ye * 706ddee5cdbSTristan Ye * - validate the magic number. 707ddee5cdbSTristan Ye * - distinguish different requests. 708ddee5cdbSTristan Ye * - validate size of different requests. 709ddee5cdbSTristan Ye */ 710c253ed1fSFabian Frederick static int ocfs2_info_handle_request(struct inode *inode, 711ddee5cdbSTristan Ye struct ocfs2_info_request __user *req) 712ddee5cdbSTristan Ye { 713ddee5cdbSTristan Ye int status = -EFAULT; 714ddee5cdbSTristan Ye struct ocfs2_info_request oir; 715ddee5cdbSTristan Ye 716ddee5cdbSTristan Ye if (o2info_from_user(oir, req)) 717ddee5cdbSTristan Ye goto bail; 718ddee5cdbSTristan Ye 719ddee5cdbSTristan Ye status = -EINVAL; 720ddee5cdbSTristan Ye if (oir.ir_magic != OCFS2_INFO_MAGIC) 721ddee5cdbSTristan Ye goto bail; 722ddee5cdbSTristan Ye 723ddee5cdbSTristan Ye switch (oir.ir_code) { 724ddee5cdbSTristan Ye case OCFS2_INFO_BLOCKSIZE: 725ddee5cdbSTristan Ye if (oir.ir_size == sizeof(struct ocfs2_info_blocksize)) 726ddee5cdbSTristan Ye status = ocfs2_info_handle_blocksize(inode, req); 727ddee5cdbSTristan Ye break; 728ddee5cdbSTristan Ye case OCFS2_INFO_CLUSTERSIZE: 729ddee5cdbSTristan Ye if (oir.ir_size == sizeof(struct ocfs2_info_clustersize)) 730ddee5cdbSTristan Ye status = ocfs2_info_handle_clustersize(inode, req); 731ddee5cdbSTristan Ye break; 732ddee5cdbSTristan Ye case OCFS2_INFO_MAXSLOTS: 733ddee5cdbSTristan Ye if (oir.ir_size == sizeof(struct ocfs2_info_maxslots)) 734ddee5cdbSTristan Ye status = ocfs2_info_handle_maxslots(inode, req); 735ddee5cdbSTristan Ye break; 736ddee5cdbSTristan Ye case OCFS2_INFO_LABEL: 737ddee5cdbSTristan Ye if (oir.ir_size == sizeof(struct ocfs2_info_label)) 738ddee5cdbSTristan Ye status = ocfs2_info_handle_label(inode, req); 739ddee5cdbSTristan Ye break; 740ddee5cdbSTristan Ye case OCFS2_INFO_UUID: 741ddee5cdbSTristan Ye if (oir.ir_size == sizeof(struct ocfs2_info_uuid)) 742ddee5cdbSTristan Ye status = ocfs2_info_handle_uuid(inode, req); 743ddee5cdbSTristan Ye break; 744ddee5cdbSTristan Ye case OCFS2_INFO_FS_FEATURES: 745ddee5cdbSTristan Ye if (oir.ir_size == sizeof(struct ocfs2_info_fs_features)) 746ddee5cdbSTristan Ye status = ocfs2_info_handle_fs_features(inode, req); 747ddee5cdbSTristan Ye break; 748ddee5cdbSTristan Ye case OCFS2_INFO_JOURNAL_SIZE: 749ddee5cdbSTristan Ye if (oir.ir_size == sizeof(struct ocfs2_info_journal_size)) 750ddee5cdbSTristan Ye status = ocfs2_info_handle_journal_size(inode, req); 751ddee5cdbSTristan Ye break; 7523e5db17dSTristan Ye case OCFS2_INFO_FREEINODE: 7533e5db17dSTristan Ye if (oir.ir_size == sizeof(struct ocfs2_info_freeinode)) 7543e5db17dSTristan Ye status = ocfs2_info_handle_freeinode(inode, req); 7553e5db17dSTristan Ye break; 756d24a10b9STristan Ye case OCFS2_INFO_FREEFRAG: 757d24a10b9STristan Ye if (oir.ir_size == sizeof(struct ocfs2_info_freefrag)) 758d24a10b9STristan Ye status = ocfs2_info_handle_freefrag(inode, req); 759d24a10b9STristan Ye break; 760ddee5cdbSTristan Ye default: 761ddee5cdbSTristan Ye status = ocfs2_info_handle_unknown(inode, req); 762ddee5cdbSTristan Ye break; 763ddee5cdbSTristan Ye } 764ddee5cdbSTristan Ye 765ddee5cdbSTristan Ye bail: 766ddee5cdbSTristan Ye return status; 767ddee5cdbSTristan Ye } 768ddee5cdbSTristan Ye 769c253ed1fSFabian Frederick static int ocfs2_get_request_ptr(struct ocfs2_info *info, int idx, 770ddee5cdbSTristan Ye u64 *req_addr, int compat_flag) 771ddee5cdbSTristan Ye { 772ddee5cdbSTristan Ye int status = -EFAULT; 773ddee5cdbSTristan Ye u64 __user *bp = NULL; 774ddee5cdbSTristan Ye 775ddee5cdbSTristan Ye if (compat_flag) { 776ddee5cdbSTristan Ye #ifdef CONFIG_COMPAT 777ddee5cdbSTristan Ye /* 778ddee5cdbSTristan Ye * pointer bp stores the base address of a pointers array, 779ddee5cdbSTristan Ye * which collects all addresses of separate request. 780ddee5cdbSTristan Ye */ 781ddee5cdbSTristan Ye bp = (u64 __user *)(unsigned long)compat_ptr(info->oi_requests); 782ddee5cdbSTristan Ye #else 783ddee5cdbSTristan Ye BUG(); 784ddee5cdbSTristan Ye #endif 785ddee5cdbSTristan Ye } else 786ddee5cdbSTristan Ye bp = (u64 __user *)(unsigned long)(info->oi_requests); 787ddee5cdbSTristan Ye 788ddee5cdbSTristan Ye if (o2info_from_user(*req_addr, bp + idx)) 789ddee5cdbSTristan Ye goto bail; 790ddee5cdbSTristan Ye 791ddee5cdbSTristan Ye status = 0; 792ddee5cdbSTristan Ye bail: 793ddee5cdbSTristan Ye return status; 794ddee5cdbSTristan Ye } 795ddee5cdbSTristan Ye 796ddee5cdbSTristan Ye /* 797ddee5cdbSTristan Ye * OCFS2_IOC_INFO handles an array of requests passed from userspace. 798ddee5cdbSTristan Ye * 799ddee5cdbSTristan Ye * ocfs2_info_handle() recevies a large info aggregation, grab and 800ddee5cdbSTristan Ye * validate the request count from header, then break it into small 801ddee5cdbSTristan Ye * pieces, later specific handlers can handle them one by one. 802ddee5cdbSTristan Ye * 803ddee5cdbSTristan Ye * Idea here is to make each separate request small enough to ensure 804ddee5cdbSTristan Ye * a better backward&forward compatibility, since a small piece of 805ddee5cdbSTristan Ye * request will be less likely to be broken if disk layout get changed. 806ddee5cdbSTristan Ye */ 80709d49eb9SArnd Bergmann static noinline_for_stack int 80809d49eb9SArnd Bergmann ocfs2_info_handle(struct inode *inode, struct ocfs2_info *info, int compat_flag) 809ddee5cdbSTristan Ye { 810ddee5cdbSTristan Ye int i, status = 0; 811ddee5cdbSTristan Ye u64 req_addr; 812ddee5cdbSTristan Ye struct ocfs2_info_request __user *reqp; 813ddee5cdbSTristan Ye 814ddee5cdbSTristan Ye if ((info->oi_count > OCFS2_INFO_MAX_REQUEST) || 815ddee5cdbSTristan Ye (!info->oi_requests)) { 816ddee5cdbSTristan Ye status = -EINVAL; 817ddee5cdbSTristan Ye goto bail; 818ddee5cdbSTristan Ye } 819ddee5cdbSTristan Ye 820ddee5cdbSTristan Ye for (i = 0; i < info->oi_count; i++) { 821ddee5cdbSTristan Ye 822ddee5cdbSTristan Ye status = ocfs2_get_request_ptr(info, i, &req_addr, compat_flag); 823ddee5cdbSTristan Ye if (status) 824ddee5cdbSTristan Ye break; 825ddee5cdbSTristan Ye 826f6a56903SAl Viro reqp = (struct ocfs2_info_request __user *)(unsigned long)req_addr; 827ddee5cdbSTristan Ye if (!reqp) { 828ddee5cdbSTristan Ye status = -EINVAL; 829ddee5cdbSTristan Ye goto bail; 830ddee5cdbSTristan Ye } 831ddee5cdbSTristan Ye 832ddee5cdbSTristan Ye status = ocfs2_info_handle_request(inode, reqp); 833ddee5cdbSTristan Ye if (status) 834ddee5cdbSTristan Ye break; 835ddee5cdbSTristan Ye } 836ddee5cdbSTristan Ye 837ddee5cdbSTristan Ye bail: 838ddee5cdbSTristan Ye return status; 839ddee5cdbSTristan Ye } 840ddee5cdbSTristan Ye 841c9ec1488SAndi Kleen long ocfs2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) 842ca4d147eSHerbert Poetzl { 843496ad9aaSAl Viro struct inode *inode = file_inode(filp); 844f6a56903SAl Viro void __user *argp = (void __user *)arg; 84509d49eb9SArnd Bergmann int status; 846ca4d147eSHerbert Poetzl 847ca4d147eSHerbert Poetzl switch (cmd) { 848b2580103SMark Fasheh case OCFS2_IOC_RESVSP: 849b2580103SMark Fasheh case OCFS2_IOC_RESVSP64: 850b2580103SMark Fasheh case OCFS2_IOC_UNRESVSP: 851b2580103SMark Fasheh case OCFS2_IOC_UNRESVSP64: 85209d49eb9SArnd Bergmann { 85309d49eb9SArnd Bergmann struct ocfs2_space_resv sr; 85409d49eb9SArnd Bergmann 855b2580103SMark Fasheh if (copy_from_user(&sr, (int __user *) arg, sizeof(sr))) 856b2580103SMark Fasheh return -EFAULT; 857b2580103SMark Fasheh 858b2580103SMark Fasheh return ocfs2_change_file_space(filp, cmd, &sr); 85909d49eb9SArnd Bergmann } 860d659072fSTao Ma case OCFS2_IOC_GROUP_EXTEND: 86109d49eb9SArnd Bergmann { 86209d49eb9SArnd Bergmann int new_clusters; 86309d49eb9SArnd Bergmann 8640957f007SMark Fasheh if (!capable(CAP_SYS_RESOURCE)) 8650957f007SMark Fasheh return -EPERM; 8660957f007SMark Fasheh 867d659072fSTao Ma if (get_user(new_clusters, (int __user *)arg)) 868d659072fSTao Ma return -EFAULT; 869d659072fSTao Ma 870fef6925cSJan Kara status = mnt_want_write_file(filp); 871fef6925cSJan Kara if (status) 872fef6925cSJan Kara return status; 873fef6925cSJan Kara status = ocfs2_group_extend(inode, new_clusters); 874fef6925cSJan Kara mnt_drop_write_file(filp); 875fef6925cSJan Kara return status; 87609d49eb9SArnd Bergmann } 8777909f2bfSTao Ma case OCFS2_IOC_GROUP_ADD: 8787909f2bfSTao Ma case OCFS2_IOC_GROUP_ADD64: 87909d49eb9SArnd Bergmann { 88009d49eb9SArnd Bergmann struct ocfs2_new_group_input input; 88109d49eb9SArnd Bergmann 8820957f007SMark Fasheh if (!capable(CAP_SYS_RESOURCE)) 8830957f007SMark Fasheh return -EPERM; 8840957f007SMark Fasheh 8857909f2bfSTao Ma if (copy_from_user(&input, (int __user *) arg, sizeof(input))) 8867909f2bfSTao Ma return -EFAULT; 8877909f2bfSTao Ma 888fef6925cSJan Kara status = mnt_want_write_file(filp); 889fef6925cSJan Kara if (status) 890fef6925cSJan Kara return status; 891fef6925cSJan Kara status = ocfs2_group_add(inode, &input); 892fef6925cSJan Kara mnt_drop_write_file(filp); 893fef6925cSJan Kara return status; 89409d49eb9SArnd Bergmann } 895bd50873dSTao Ma case OCFS2_IOC_REFLINK: 89609d49eb9SArnd Bergmann { 89709d49eb9SArnd Bergmann struct reflink_arguments args; 89809d49eb9SArnd Bergmann const char __user *old_path; 89909d49eb9SArnd Bergmann const char __user *new_path; 90009d49eb9SArnd Bergmann bool preserve; 90109d49eb9SArnd Bergmann 902f6a56903SAl Viro if (copy_from_user(&args, argp, sizeof(args))) 903bd50873dSTao Ma return -EFAULT; 904f6a56903SAl Viro old_path = (const char __user *)(unsigned long)args.old_path; 905f6a56903SAl Viro new_path = (const char __user *)(unsigned long)args.new_path; 906bd50873dSTao Ma preserve = (args.preserve != 0); 907bd50873dSTao Ma 908bd50873dSTao Ma return ocfs2_reflink_ioctl(inode, old_path, new_path, preserve); 90909d49eb9SArnd Bergmann } 910ddee5cdbSTristan Ye case OCFS2_IOC_INFO: 91109d49eb9SArnd Bergmann { 91209d49eb9SArnd Bergmann struct ocfs2_info info; 91309d49eb9SArnd Bergmann 914f6a56903SAl Viro if (copy_from_user(&info, argp, sizeof(struct ocfs2_info))) 915ddee5cdbSTristan Ye return -EFAULT; 916ddee5cdbSTristan Ye 917ddee5cdbSTristan Ye return ocfs2_info_handle(inode, &info, 0); 91809d49eb9SArnd Bergmann } 91955e67872STao Ma case FITRIM: 92055e67872STao Ma { 92155e67872STao Ma struct super_block *sb = inode->i_sb; 92255e67872STao Ma struct fstrim_range range; 92355e67872STao Ma int ret = 0; 92455e67872STao Ma 92555e67872STao Ma if (!capable(CAP_SYS_ADMIN)) 92655e67872STao Ma return -EPERM; 92755e67872STao Ma 92870200574SChristoph Hellwig if (!bdev_max_discard_sectors(sb->s_bdev)) 92919e8ac27SJie Liu return -EOPNOTSUPP; 93019e8ac27SJie Liu 931f6a56903SAl Viro if (copy_from_user(&range, argp, sizeof(range))) 93255e67872STao Ma return -EFAULT; 93355e67872STao Ma 9347b47ef52SChristoph Hellwig range.minlen = max_t(u64, bdev_discard_granularity(sb->s_bdev), 9351ba2212bSJie Liu range.minlen); 93655e67872STao Ma ret = ocfs2_trim_fs(sb, &range); 93755e67872STao Ma if (ret < 0) 93855e67872STao Ma return ret; 93955e67872STao Ma 940f6a56903SAl Viro if (copy_to_user(argp, &range, sizeof(range))) 94155e67872STao Ma return -EFAULT; 94255e67872STao Ma 94355e67872STao Ma return 0; 94455e67872STao Ma } 94553069d4eSTristan Ye case OCFS2_IOC_MOVE_EXT: 946f6a56903SAl Viro return ocfs2_ioctl_move_extents(filp, argp); 947ca4d147eSHerbert Poetzl default: 948ca4d147eSHerbert Poetzl return -ENOTTY; 949ca4d147eSHerbert Poetzl } 950ca4d147eSHerbert Poetzl } 951ca4d147eSHerbert Poetzl 952586d232bSMark Fasheh #ifdef CONFIG_COMPAT 953586d232bSMark Fasheh long ocfs2_compat_ioctl(struct file *file, unsigned cmd, unsigned long arg) 954586d232bSMark Fasheh { 95534e6c59aSTao Ma bool preserve; 95634e6c59aSTao Ma struct reflink_arguments args; 957496ad9aaSAl Viro struct inode *inode = file_inode(file); 958ddee5cdbSTristan Ye struct ocfs2_info info; 959f6a56903SAl Viro void __user *argp = (void __user *)arg; 96034e6c59aSTao Ma 961586d232bSMark Fasheh switch (cmd) { 962b2580103SMark Fasheh case OCFS2_IOC_RESVSP: 963b2580103SMark Fasheh case OCFS2_IOC_RESVSP64: 964b2580103SMark Fasheh case OCFS2_IOC_UNRESVSP: 965b2580103SMark Fasheh case OCFS2_IOC_UNRESVSP64: 966d659072fSTao Ma case OCFS2_IOC_GROUP_EXTEND: 9677909f2bfSTao Ma case OCFS2_IOC_GROUP_ADD: 9687909f2bfSTao Ma case OCFS2_IOC_GROUP_ADD64: 969b2580103SMark Fasheh break; 97034e6c59aSTao Ma case OCFS2_IOC_REFLINK: 971f6a56903SAl Viro if (copy_from_user(&args, argp, sizeof(args))) 97234e6c59aSTao Ma return -EFAULT; 97334e6c59aSTao Ma preserve = (args.preserve != 0); 97434e6c59aSTao Ma 97534e6c59aSTao Ma return ocfs2_reflink_ioctl(inode, compat_ptr(args.old_path), 97634e6c59aSTao Ma compat_ptr(args.new_path), preserve); 977ddee5cdbSTristan Ye case OCFS2_IOC_INFO: 978f6a56903SAl Viro if (copy_from_user(&info, argp, sizeof(struct ocfs2_info))) 979ddee5cdbSTristan Ye return -EFAULT; 980ddee5cdbSTristan Ye 981ddee5cdbSTristan Ye return ocfs2_info_handle(inode, &info, 1); 982314999dcSArnd Bergmann case FITRIM: 98353069d4eSTristan Ye case OCFS2_IOC_MOVE_EXT: 98453069d4eSTristan Ye break; 985586d232bSMark Fasheh default: 986586d232bSMark Fasheh return -ENOIOCTLCMD; 987586d232bSMark Fasheh } 988586d232bSMark Fasheh 989c9ec1488SAndi Kleen return ocfs2_ioctl(file, cmd, arg); 990586d232bSMark Fasheh } 991586d232bSMark Fasheh #endif 992