| Herbert Poetzl | ca4d147 | 2006-07-03 17:27:12 -0700 | [diff] [blame] | 1 | /* | 
|  | 2 | * linux/fs/ocfs2/ioctl.c | 
|  | 3 | * | 
|  | 4 | * Copyright (C) 2006 Herbert Poetzl | 
|  | 5 | * adapted from Remy Card's ext2/ioctl.c | 
|  | 6 | */ | 
|  | 7 |  | 
|  | 8 | #include <linux/fs.h> | 
|  | 9 | #include <linux/mount.h> | 
| Tao Ma | 34e6c59 | 2010-01-27 10:21:52 +0800 | [diff] [blame] | 10 | #include <linux/compat.h> | 
| Herbert Poetzl | ca4d147 | 2006-07-03 17:27:12 -0700 | [diff] [blame] | 11 |  | 
| Herbert Poetzl | ca4d147 | 2006-07-03 17:27:12 -0700 | [diff] [blame] | 12 | #include <cluster/masklog.h> | 
|  | 13 |  | 
|  | 14 | #include "ocfs2.h" | 
|  | 15 | #include "alloc.h" | 
|  | 16 | #include "dlmglue.h" | 
| Mark Fasheh | b258010 | 2007-03-09 16:53:21 -0800 | [diff] [blame] | 17 | #include "file.h" | 
| Herbert Poetzl | ca4d147 | 2006-07-03 17:27:12 -0700 | [diff] [blame] | 18 | #include "inode.h" | 
|  | 19 | #include "journal.h" | 
|  | 20 |  | 
|  | 21 | #include "ocfs2_fs.h" | 
| Adrian Bunk | 2d56251 | 2006-07-10 01:32:51 +0200 | [diff] [blame] | 22 | #include "ioctl.h" | 
| Tao Ma | d659072 | 2007-12-18 15:47:03 +0800 | [diff] [blame] | 23 | #include "resize.h" | 
| Tao Ma | bd50873 | 2009-09-21 11:25:14 +0800 | [diff] [blame] | 24 | #include "refcounttree.h" | 
| Tristan Ye | 3e5db17 | 2011-05-24 15:25:54 +0800 | [diff] [blame] | 25 | #include "sysfile.h" | 
|  | 26 | #include "dir.h" | 
|  | 27 | #include "buffer_head_io.h" | 
| Tristan Ye | d24a10b9 | 2011-05-24 15:27:17 +0800 | [diff] [blame] | 28 | #include "suballoc.h" | 
| Tristan Ye | 53069d4 | 2011-05-25 14:23:43 +0800 | [diff] [blame] | 29 | #include "move_extents.h" | 
| Adrian Bunk | 2d56251 | 2006-07-10 01:32:51 +0200 | [diff] [blame] | 30 |  | 
| Herbert Poetzl | ca4d147 | 2006-07-03 17:27:12 -0700 | [diff] [blame] | 31 | #include <linux/ext2_fs.h> | 
|  | 32 |  | 
| Tristan Ye | ddee5cd | 2010-05-22 16:26:33 +0800 | [diff] [blame] | 33 | #define o2info_from_user(a, b)	\ | 
|  | 34 | copy_from_user(&(a), (b), sizeof(a)) | 
|  | 35 | #define o2info_to_user(a, b)	\ | 
|  | 36 | copy_to_user((typeof(a) __user *)b, &(a), sizeof(a)) | 
|  | 37 |  | 
|  | 38 | /* | 
|  | 39 | * This call is void because we are already reporting an error that may | 
|  | 40 | * be -EFAULT.  The error will be returned from the ioctl(2) call.  It's | 
|  | 41 | * just a best-effort to tell userspace that this request caused the error. | 
|  | 42 | */ | 
| Tristan Ye | 8aa1fa3 | 2011-05-24 15:22:59 +0800 | [diff] [blame] | 43 | static inline void o2info_set_request_error(struct ocfs2_info_request *kreq, | 
| Tristan Ye | ddee5cd | 2010-05-22 16:26:33 +0800 | [diff] [blame] | 44 | struct ocfs2_info_request __user *req) | 
|  | 45 | { | 
|  | 46 | kreq->ir_flags |= OCFS2_INFO_FL_ERROR; | 
|  | 47 | (void)put_user(kreq->ir_flags, (__u32 __user *)&(req->ir_flags)); | 
|  | 48 | } | 
|  | 49 |  | 
| Tristan Ye | 8aa1fa3 | 2011-05-24 15:22:59 +0800 | [diff] [blame] | 50 | static inline void o2info_set_request_filled(struct ocfs2_info_request *req) | 
| Tristan Ye | 1936a26 | 2011-01-30 14:25:59 +0800 | [diff] [blame] | 51 | { | 
|  | 52 | req->ir_flags |= OCFS2_INFO_FL_FILLED; | 
|  | 53 | } | 
|  | 54 |  | 
| Tristan Ye | 8aa1fa3 | 2011-05-24 15:22:59 +0800 | [diff] [blame] | 55 | static inline void o2info_clear_request_filled(struct ocfs2_info_request *req) | 
| Tristan Ye | 1936a26 | 2011-01-30 14:25:59 +0800 | [diff] [blame] | 56 | { | 
|  | 57 | req->ir_flags &= ~OCFS2_INFO_FL_FILLED; | 
|  | 58 | } | 
|  | 59 |  | 
| Tristan Ye | 3e5db17 | 2011-05-24 15:25:54 +0800 | [diff] [blame] | 60 | static inline int o2info_coherent(struct ocfs2_info_request *req) | 
|  | 61 | { | 
|  | 62 | return (!(req->ir_flags & OCFS2_INFO_FL_NON_COHERENT)); | 
|  | 63 | } | 
| Tristan Ye | 1936a26 | 2011-01-30 14:25:59 +0800 | [diff] [blame] | 64 |  | 
| Herbert Poetzl | ca4d147 | 2006-07-03 17:27:12 -0700 | [diff] [blame] | 65 | static int ocfs2_get_inode_attr(struct inode *inode, unsigned *flags) | 
|  | 66 | { | 
|  | 67 | int status; | 
|  | 68 |  | 
| Mark Fasheh | e63aecb6 | 2007-10-18 15:30:42 -0700 | [diff] [blame] | 69 | status = ocfs2_inode_lock(inode, NULL, 0); | 
| Herbert Poetzl | ca4d147 | 2006-07-03 17:27:12 -0700 | [diff] [blame] | 70 | if (status < 0) { | 
|  | 71 | mlog_errno(status); | 
|  | 72 | return status; | 
|  | 73 | } | 
| Jan Kara | 6e4b0d5 | 2007-04-27 11:08:01 -0700 | [diff] [blame] | 74 | ocfs2_get_inode_flags(OCFS2_I(inode)); | 
| Herbert Poetzl | ca4d147 | 2006-07-03 17:27:12 -0700 | [diff] [blame] | 75 | *flags = OCFS2_I(inode)->ip_attr; | 
| Mark Fasheh | e63aecb6 | 2007-10-18 15:30:42 -0700 | [diff] [blame] | 76 | ocfs2_inode_unlock(inode, 0); | 
| Herbert Poetzl | ca4d147 | 2006-07-03 17:27:12 -0700 | [diff] [blame] | 77 |  | 
| Herbert Poetzl | ca4d147 | 2006-07-03 17:27:12 -0700 | [diff] [blame] | 78 | return status; | 
|  | 79 | } | 
|  | 80 |  | 
|  | 81 | static int ocfs2_set_inode_attr(struct inode *inode, unsigned flags, | 
|  | 82 | unsigned mask) | 
|  | 83 | { | 
|  | 84 | struct ocfs2_inode_info *ocfs2_inode = OCFS2_I(inode); | 
|  | 85 | struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); | 
| Mark Fasheh | 1fabe14 | 2006-10-09 18:11:45 -0700 | [diff] [blame] | 86 | handle_t *handle = NULL; | 
| Herbert Poetzl | ca4d147 | 2006-07-03 17:27:12 -0700 | [diff] [blame] | 87 | struct buffer_head *bh = NULL; | 
|  | 88 | unsigned oldflags; | 
|  | 89 | int status; | 
|  | 90 |  | 
|  | 91 | mutex_lock(&inode->i_mutex); | 
|  | 92 |  | 
| Mark Fasheh | e63aecb6 | 2007-10-18 15:30:42 -0700 | [diff] [blame] | 93 | status = ocfs2_inode_lock(inode, &bh, 1); | 
| Herbert Poetzl | ca4d147 | 2006-07-03 17:27:12 -0700 | [diff] [blame] | 94 | if (status < 0) { | 
|  | 95 | mlog_errno(status); | 
|  | 96 | goto bail; | 
|  | 97 | } | 
|  | 98 |  | 
| Herbert Poetzl | ca4d147 | 2006-07-03 17:27:12 -0700 | [diff] [blame] | 99 | status = -EACCES; | 
| Serge E. Hallyn | 2e14967 | 2011-03-23 16:43:26 -0700 | [diff] [blame] | 100 | if (!inode_owner_or_capable(inode)) | 
| Herbert Poetzl | ca4d147 | 2006-07-03 17:27:12 -0700 | [diff] [blame] | 101 | goto bail_unlock; | 
|  | 102 |  | 
|  | 103 | if (!S_ISDIR(inode->i_mode)) | 
|  | 104 | flags &= ~OCFS2_DIRSYNC_FL; | 
|  | 105 |  | 
| Mark Fasheh | 65eff9c | 2006-10-09 17:26:22 -0700 | [diff] [blame] | 106 | handle = ocfs2_start_trans(osb, OCFS2_INODE_UPDATE_CREDITS); | 
| Herbert Poetzl | ca4d147 | 2006-07-03 17:27:12 -0700 | [diff] [blame] | 107 | if (IS_ERR(handle)) { | 
|  | 108 | status = PTR_ERR(handle); | 
|  | 109 | mlog_errno(status); | 
|  | 110 | goto bail_unlock; | 
|  | 111 | } | 
|  | 112 |  | 
|  | 113 | oldflags = ocfs2_inode->ip_attr; | 
|  | 114 | flags = flags & mask; | 
|  | 115 | flags |= oldflags & ~mask; | 
|  | 116 |  | 
|  | 117 | /* | 
|  | 118 | * The IMMUTABLE and APPEND_ONLY flags can only be changed by | 
|  | 119 | * the relevant capability. | 
|  | 120 | */ | 
|  | 121 | status = -EPERM; | 
|  | 122 | if ((oldflags & OCFS2_IMMUTABLE_FL) || ((flags ^ oldflags) & | 
|  | 123 | (OCFS2_APPEND_FL | OCFS2_IMMUTABLE_FL))) { | 
|  | 124 | if (!capable(CAP_LINUX_IMMUTABLE)) | 
| Wengang Wang | b8a0ae5 | 2011-10-12 15:22:15 +0800 | [diff] [blame] | 125 | goto bail_commit; | 
| Herbert Poetzl | ca4d147 | 2006-07-03 17:27:12 -0700 | [diff] [blame] | 126 | } | 
|  | 127 |  | 
|  | 128 | ocfs2_inode->ip_attr = flags; | 
|  | 129 | ocfs2_set_inode_flags(inode); | 
|  | 130 |  | 
|  | 131 | status = ocfs2_mark_inode_dirty(handle, inode, bh); | 
|  | 132 | if (status < 0) | 
|  | 133 | mlog_errno(status); | 
|  | 134 |  | 
| Wengang Wang | b8a0ae5 | 2011-10-12 15:22:15 +0800 | [diff] [blame] | 135 | bail_commit: | 
| Mark Fasheh | 02dc1af | 2006-10-09 16:48:10 -0700 | [diff] [blame] | 136 | ocfs2_commit_trans(osb, handle); | 
| Herbert Poetzl | ca4d147 | 2006-07-03 17:27:12 -0700 | [diff] [blame] | 137 | bail_unlock: | 
| Mark Fasheh | e63aecb6 | 2007-10-18 15:30:42 -0700 | [diff] [blame] | 138 | ocfs2_inode_unlock(inode, 1); | 
| Herbert Poetzl | ca4d147 | 2006-07-03 17:27:12 -0700 | [diff] [blame] | 139 | bail: | 
|  | 140 | mutex_unlock(&inode->i_mutex); | 
|  | 141 |  | 
| Mark Fasheh | a81cb88 | 2008-10-07 14:25:16 -0700 | [diff] [blame] | 142 | brelse(bh); | 
| Herbert Poetzl | ca4d147 | 2006-07-03 17:27:12 -0700 | [diff] [blame] | 143 |  | 
| Herbert Poetzl | ca4d147 | 2006-07-03 17:27:12 -0700 | [diff] [blame] | 144 | return status; | 
|  | 145 | } | 
|  | 146 |  | 
| Tristan Ye | ddee5cd | 2010-05-22 16:26:33 +0800 | [diff] [blame] | 147 | int ocfs2_info_handle_blocksize(struct inode *inode, | 
|  | 148 | struct ocfs2_info_request __user *req) | 
|  | 149 | { | 
|  | 150 | int status = -EFAULT; | 
|  | 151 | struct ocfs2_info_blocksize oib; | 
|  | 152 |  | 
|  | 153 | if (o2info_from_user(oib, req)) | 
|  | 154 | goto bail; | 
|  | 155 |  | 
|  | 156 | oib.ib_blocksize = inode->i_sb->s_blocksize; | 
| Tristan Ye | 1936a26 | 2011-01-30 14:25:59 +0800 | [diff] [blame] | 157 |  | 
| Tristan Ye | 8aa1fa3 | 2011-05-24 15:22:59 +0800 | [diff] [blame] | 158 | o2info_set_request_filled(&oib.ib_req); | 
| Tristan Ye | ddee5cd | 2010-05-22 16:26:33 +0800 | [diff] [blame] | 159 |  | 
|  | 160 | if (o2info_to_user(oib, req)) | 
|  | 161 | goto bail; | 
|  | 162 |  | 
|  | 163 | status = 0; | 
|  | 164 | bail: | 
|  | 165 | if (status) | 
| Tristan Ye | 8aa1fa3 | 2011-05-24 15:22:59 +0800 | [diff] [blame] | 166 | o2info_set_request_error(&oib.ib_req, req); | 
| Tristan Ye | ddee5cd | 2010-05-22 16:26:33 +0800 | [diff] [blame] | 167 |  | 
|  | 168 | return status; | 
|  | 169 | } | 
|  | 170 |  | 
|  | 171 | int ocfs2_info_handle_clustersize(struct inode *inode, | 
|  | 172 | struct ocfs2_info_request __user *req) | 
|  | 173 | { | 
|  | 174 | int status = -EFAULT; | 
|  | 175 | struct ocfs2_info_clustersize oic; | 
|  | 176 | struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); | 
|  | 177 |  | 
|  | 178 | if (o2info_from_user(oic, req)) | 
|  | 179 | goto bail; | 
|  | 180 |  | 
|  | 181 | oic.ic_clustersize = osb->s_clustersize; | 
| Tristan Ye | 1936a26 | 2011-01-30 14:25:59 +0800 | [diff] [blame] | 182 |  | 
| Tristan Ye | 8aa1fa3 | 2011-05-24 15:22:59 +0800 | [diff] [blame] | 183 | o2info_set_request_filled(&oic.ic_req); | 
| Tristan Ye | ddee5cd | 2010-05-22 16:26:33 +0800 | [diff] [blame] | 184 |  | 
|  | 185 | if (o2info_to_user(oic, req)) | 
|  | 186 | goto bail; | 
|  | 187 |  | 
|  | 188 | status = 0; | 
|  | 189 | bail: | 
|  | 190 | if (status) | 
| Tristan Ye | 8aa1fa3 | 2011-05-24 15:22:59 +0800 | [diff] [blame] | 191 | o2info_set_request_error(&oic.ic_req, req); | 
| Tristan Ye | ddee5cd | 2010-05-22 16:26:33 +0800 | [diff] [blame] | 192 |  | 
|  | 193 | return status; | 
|  | 194 | } | 
|  | 195 |  | 
|  | 196 | int ocfs2_info_handle_maxslots(struct inode *inode, | 
|  | 197 | struct ocfs2_info_request __user *req) | 
|  | 198 | { | 
|  | 199 | int status = -EFAULT; | 
|  | 200 | struct ocfs2_info_maxslots oim; | 
|  | 201 | struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); | 
|  | 202 |  | 
|  | 203 | if (o2info_from_user(oim, req)) | 
|  | 204 | goto bail; | 
|  | 205 |  | 
|  | 206 | oim.im_max_slots = osb->max_slots; | 
| Tristan Ye | 1936a26 | 2011-01-30 14:25:59 +0800 | [diff] [blame] | 207 |  | 
| Tristan Ye | 8aa1fa3 | 2011-05-24 15:22:59 +0800 | [diff] [blame] | 208 | o2info_set_request_filled(&oim.im_req); | 
| Tristan Ye | ddee5cd | 2010-05-22 16:26:33 +0800 | [diff] [blame] | 209 |  | 
|  | 210 | if (o2info_to_user(oim, req)) | 
|  | 211 | goto bail; | 
|  | 212 |  | 
|  | 213 | status = 0; | 
|  | 214 | bail: | 
|  | 215 | if (status) | 
| Tristan Ye | 8aa1fa3 | 2011-05-24 15:22:59 +0800 | [diff] [blame] | 216 | o2info_set_request_error(&oim.im_req, req); | 
| Tristan Ye | ddee5cd | 2010-05-22 16:26:33 +0800 | [diff] [blame] | 217 |  | 
|  | 218 | return status; | 
|  | 219 | } | 
|  | 220 |  | 
|  | 221 | int ocfs2_info_handle_label(struct inode *inode, | 
|  | 222 | struct ocfs2_info_request __user *req) | 
|  | 223 | { | 
|  | 224 | int status = -EFAULT; | 
|  | 225 | struct ocfs2_info_label oil; | 
|  | 226 | struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); | 
|  | 227 |  | 
|  | 228 | if (o2info_from_user(oil, req)) | 
|  | 229 | goto bail; | 
|  | 230 |  | 
|  | 231 | memcpy(oil.il_label, osb->vol_label, OCFS2_MAX_VOL_LABEL_LEN); | 
| Tristan Ye | 1936a26 | 2011-01-30 14:25:59 +0800 | [diff] [blame] | 232 |  | 
| Tristan Ye | 8aa1fa3 | 2011-05-24 15:22:59 +0800 | [diff] [blame] | 233 | o2info_set_request_filled(&oil.il_req); | 
| Tristan Ye | ddee5cd | 2010-05-22 16:26:33 +0800 | [diff] [blame] | 234 |  | 
|  | 235 | if (o2info_to_user(oil, req)) | 
|  | 236 | goto bail; | 
|  | 237 |  | 
|  | 238 | status = 0; | 
|  | 239 | bail: | 
|  | 240 | if (status) | 
| Tristan Ye | 8aa1fa3 | 2011-05-24 15:22:59 +0800 | [diff] [blame] | 241 | o2info_set_request_error(&oil.il_req, req); | 
| Tristan Ye | ddee5cd | 2010-05-22 16:26:33 +0800 | [diff] [blame] | 242 |  | 
|  | 243 | return status; | 
|  | 244 | } | 
|  | 245 |  | 
|  | 246 | int ocfs2_info_handle_uuid(struct inode *inode, | 
|  | 247 | struct ocfs2_info_request __user *req) | 
|  | 248 | { | 
|  | 249 | int status = -EFAULT; | 
|  | 250 | struct ocfs2_info_uuid oiu; | 
|  | 251 | struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); | 
|  | 252 |  | 
|  | 253 | if (o2info_from_user(oiu, req)) | 
|  | 254 | goto bail; | 
|  | 255 |  | 
|  | 256 | memcpy(oiu.iu_uuid_str, osb->uuid_str, OCFS2_TEXT_UUID_LEN + 1); | 
| Tristan Ye | 1936a26 | 2011-01-30 14:25:59 +0800 | [diff] [blame] | 257 |  | 
| Tristan Ye | 8aa1fa3 | 2011-05-24 15:22:59 +0800 | [diff] [blame] | 258 | o2info_set_request_filled(&oiu.iu_req); | 
| Tristan Ye | ddee5cd | 2010-05-22 16:26:33 +0800 | [diff] [blame] | 259 |  | 
|  | 260 | if (o2info_to_user(oiu, req)) | 
|  | 261 | goto bail; | 
|  | 262 |  | 
|  | 263 | status = 0; | 
|  | 264 | bail: | 
|  | 265 | if (status) | 
| Tristan Ye | 8aa1fa3 | 2011-05-24 15:22:59 +0800 | [diff] [blame] | 266 | o2info_set_request_error(&oiu.iu_req, req); | 
| Tristan Ye | ddee5cd | 2010-05-22 16:26:33 +0800 | [diff] [blame] | 267 |  | 
|  | 268 | return status; | 
|  | 269 | } | 
|  | 270 |  | 
|  | 271 | int ocfs2_info_handle_fs_features(struct inode *inode, | 
|  | 272 | struct ocfs2_info_request __user *req) | 
|  | 273 | { | 
|  | 274 | int status = -EFAULT; | 
|  | 275 | struct ocfs2_info_fs_features oif; | 
|  | 276 | struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); | 
|  | 277 |  | 
|  | 278 | if (o2info_from_user(oif, req)) | 
|  | 279 | goto bail; | 
|  | 280 |  | 
|  | 281 | oif.if_compat_features = osb->s_feature_compat; | 
|  | 282 | oif.if_incompat_features = osb->s_feature_incompat; | 
|  | 283 | oif.if_ro_compat_features = osb->s_feature_ro_compat; | 
| Tristan Ye | 1936a26 | 2011-01-30 14:25:59 +0800 | [diff] [blame] | 284 |  | 
| Tristan Ye | 8aa1fa3 | 2011-05-24 15:22:59 +0800 | [diff] [blame] | 285 | o2info_set_request_filled(&oif.if_req); | 
| Tristan Ye | ddee5cd | 2010-05-22 16:26:33 +0800 | [diff] [blame] | 286 |  | 
|  | 287 | if (o2info_to_user(oif, req)) | 
|  | 288 | goto bail; | 
|  | 289 |  | 
|  | 290 | status = 0; | 
|  | 291 | bail: | 
|  | 292 | if (status) | 
| Tristan Ye | 8aa1fa3 | 2011-05-24 15:22:59 +0800 | [diff] [blame] | 293 | o2info_set_request_error(&oif.if_req, req); | 
| Tristan Ye | ddee5cd | 2010-05-22 16:26:33 +0800 | [diff] [blame] | 294 |  | 
|  | 295 | return status; | 
|  | 296 | } | 
|  | 297 |  | 
|  | 298 | int ocfs2_info_handle_journal_size(struct inode *inode, | 
|  | 299 | struct ocfs2_info_request __user *req) | 
|  | 300 | { | 
|  | 301 | int status = -EFAULT; | 
|  | 302 | struct ocfs2_info_journal_size oij; | 
|  | 303 | struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); | 
|  | 304 |  | 
|  | 305 | if (o2info_from_user(oij, req)) | 
|  | 306 | goto bail; | 
|  | 307 |  | 
|  | 308 | oij.ij_journal_size = osb->journal->j_inode->i_size; | 
|  | 309 |  | 
| Tristan Ye | 8aa1fa3 | 2011-05-24 15:22:59 +0800 | [diff] [blame] | 310 | o2info_set_request_filled(&oij.ij_req); | 
| Tristan Ye | ddee5cd | 2010-05-22 16:26:33 +0800 | [diff] [blame] | 311 |  | 
|  | 312 | if (o2info_to_user(oij, req)) | 
|  | 313 | goto bail; | 
|  | 314 |  | 
|  | 315 | status = 0; | 
|  | 316 | bail: | 
|  | 317 | if (status) | 
| Tristan Ye | 8aa1fa3 | 2011-05-24 15:22:59 +0800 | [diff] [blame] | 318 | o2info_set_request_error(&oij.ij_req, req); | 
| Tristan Ye | ddee5cd | 2010-05-22 16:26:33 +0800 | [diff] [blame] | 319 |  | 
|  | 320 | return status; | 
|  | 321 | } | 
|  | 322 |  | 
| Tristan Ye | 3e5db17 | 2011-05-24 15:25:54 +0800 | [diff] [blame] | 323 | int ocfs2_info_scan_inode_alloc(struct ocfs2_super *osb, | 
|  | 324 | struct inode *inode_alloc, u64 blkno, | 
|  | 325 | struct ocfs2_info_freeinode *fi, u32 slot) | 
|  | 326 | { | 
|  | 327 | int status = 0, unlock = 0; | 
|  | 328 |  | 
|  | 329 | struct buffer_head *bh = NULL; | 
|  | 330 | struct ocfs2_dinode *dinode_alloc = NULL; | 
|  | 331 |  | 
|  | 332 | if (inode_alloc) | 
|  | 333 | mutex_lock(&inode_alloc->i_mutex); | 
|  | 334 |  | 
|  | 335 | if (o2info_coherent(&fi->ifi_req)) { | 
|  | 336 | status = ocfs2_inode_lock(inode_alloc, &bh, 0); | 
|  | 337 | if (status < 0) { | 
|  | 338 | mlog_errno(status); | 
|  | 339 | goto bail; | 
|  | 340 | } | 
|  | 341 | unlock = 1; | 
|  | 342 | } else { | 
|  | 343 | status = ocfs2_read_blocks_sync(osb, blkno, 1, &bh); | 
|  | 344 | if (status < 0) { | 
|  | 345 | mlog_errno(status); | 
|  | 346 | goto bail; | 
|  | 347 | } | 
|  | 348 | } | 
|  | 349 |  | 
|  | 350 | dinode_alloc = (struct ocfs2_dinode *)bh->b_data; | 
|  | 351 |  | 
|  | 352 | fi->ifi_stat[slot].lfi_total = | 
|  | 353 | le32_to_cpu(dinode_alloc->id1.bitmap1.i_total); | 
|  | 354 | fi->ifi_stat[slot].lfi_free = | 
|  | 355 | le32_to_cpu(dinode_alloc->id1.bitmap1.i_total) - | 
|  | 356 | le32_to_cpu(dinode_alloc->id1.bitmap1.i_used); | 
|  | 357 |  | 
|  | 358 | bail: | 
|  | 359 | if (unlock) | 
|  | 360 | ocfs2_inode_unlock(inode_alloc, 0); | 
|  | 361 |  | 
|  | 362 | if (inode_alloc) | 
|  | 363 | mutex_unlock(&inode_alloc->i_mutex); | 
|  | 364 |  | 
|  | 365 | brelse(bh); | 
|  | 366 |  | 
|  | 367 | return status; | 
|  | 368 | } | 
|  | 369 |  | 
|  | 370 | int ocfs2_info_handle_freeinode(struct inode *inode, | 
|  | 371 | struct ocfs2_info_request __user *req) | 
|  | 372 | { | 
|  | 373 | u32 i; | 
|  | 374 | u64 blkno = -1; | 
|  | 375 | char namebuf[40]; | 
|  | 376 | int status = -EFAULT, type = INODE_ALLOC_SYSTEM_INODE; | 
|  | 377 | struct ocfs2_info_freeinode *oifi = NULL; | 
|  | 378 | struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); | 
|  | 379 | struct inode *inode_alloc = NULL; | 
|  | 380 |  | 
|  | 381 | oifi = kzalloc(sizeof(struct ocfs2_info_freeinode), GFP_KERNEL); | 
|  | 382 | if (!oifi) { | 
|  | 383 | status = -ENOMEM; | 
|  | 384 | mlog_errno(status); | 
| Dan Carpenter | 87f0d5c | 2011-05-29 22:57:16 +0300 | [diff] [blame] | 385 | goto out_err; | 
| Tristan Ye | 3e5db17 | 2011-05-24 15:25:54 +0800 | [diff] [blame] | 386 | } | 
|  | 387 |  | 
|  | 388 | if (o2info_from_user(*oifi, req)) | 
|  | 389 | goto bail; | 
|  | 390 |  | 
|  | 391 | oifi->ifi_slotnum = osb->max_slots; | 
|  | 392 |  | 
|  | 393 | for (i = 0; i < oifi->ifi_slotnum; i++) { | 
|  | 394 | if (o2info_coherent(&oifi->ifi_req)) { | 
|  | 395 | inode_alloc = ocfs2_get_system_file_inode(osb, type, i); | 
|  | 396 | if (!inode_alloc) { | 
|  | 397 | mlog(ML_ERROR, "unable to get alloc inode in " | 
|  | 398 | "slot %u\n", i); | 
|  | 399 | status = -EIO; | 
|  | 400 | goto bail; | 
|  | 401 | } | 
|  | 402 | } else { | 
|  | 403 | ocfs2_sprintf_system_inode_name(namebuf, | 
|  | 404 | sizeof(namebuf), | 
|  | 405 | type, i); | 
|  | 406 | status = ocfs2_lookup_ino_from_name(osb->sys_root_inode, | 
|  | 407 | namebuf, | 
|  | 408 | strlen(namebuf), | 
|  | 409 | &blkno); | 
|  | 410 | if (status < 0) { | 
|  | 411 | status = -ENOENT; | 
|  | 412 | goto bail; | 
|  | 413 | } | 
|  | 414 | } | 
|  | 415 |  | 
|  | 416 | status = ocfs2_info_scan_inode_alloc(osb, inode_alloc, blkno, oifi, i); | 
|  | 417 | if (status < 0) | 
|  | 418 | goto bail; | 
|  | 419 |  | 
|  | 420 | iput(inode_alloc); | 
|  | 421 | inode_alloc = NULL; | 
|  | 422 | } | 
|  | 423 |  | 
|  | 424 | o2info_set_request_filled(&oifi->ifi_req); | 
|  | 425 |  | 
|  | 426 | if (o2info_to_user(*oifi, req)) | 
|  | 427 | goto bail; | 
|  | 428 |  | 
|  | 429 | status = 0; | 
|  | 430 | bail: | 
|  | 431 | if (status) | 
|  | 432 | o2info_set_request_error(&oifi->ifi_req, req); | 
|  | 433 |  | 
|  | 434 | kfree(oifi); | 
| Dan Carpenter | 87f0d5c | 2011-05-29 22:57:16 +0300 | [diff] [blame] | 435 | out_err: | 
| Tristan Ye | 3e5db17 | 2011-05-24 15:25:54 +0800 | [diff] [blame] | 436 | return status; | 
|  | 437 | } | 
|  | 438 |  | 
| Tristan Ye | d24a10b9 | 2011-05-24 15:27:17 +0800 | [diff] [blame] | 439 | static void o2ffg_update_histogram(struct ocfs2_info_free_chunk_list *hist, | 
|  | 440 | unsigned int chunksize) | 
|  | 441 | { | 
|  | 442 | int index; | 
|  | 443 |  | 
|  | 444 | index = __ilog2_u32(chunksize); | 
|  | 445 | if (index >= OCFS2_INFO_MAX_HIST) | 
|  | 446 | index = OCFS2_INFO_MAX_HIST - 1; | 
|  | 447 |  | 
|  | 448 | hist->fc_chunks[index]++; | 
|  | 449 | hist->fc_clusters[index] += chunksize; | 
|  | 450 | } | 
|  | 451 |  | 
|  | 452 | static void o2ffg_update_stats(struct ocfs2_info_freefrag_stats *stats, | 
|  | 453 | unsigned int chunksize) | 
|  | 454 | { | 
|  | 455 | if (chunksize > stats->ffs_max) | 
|  | 456 | stats->ffs_max = chunksize; | 
|  | 457 |  | 
|  | 458 | if (chunksize < stats->ffs_min) | 
|  | 459 | stats->ffs_min = chunksize; | 
|  | 460 |  | 
|  | 461 | stats->ffs_avg += chunksize; | 
|  | 462 | stats->ffs_free_chunks_real++; | 
|  | 463 | } | 
|  | 464 |  | 
|  | 465 | void ocfs2_info_update_ffg(struct ocfs2_info_freefrag *ffg, | 
|  | 466 | unsigned int chunksize) | 
|  | 467 | { | 
|  | 468 | o2ffg_update_histogram(&(ffg->iff_ffs.ffs_fc_hist), chunksize); | 
|  | 469 | o2ffg_update_stats(&(ffg->iff_ffs), chunksize); | 
|  | 470 | } | 
|  | 471 |  | 
|  | 472 | int ocfs2_info_freefrag_scan_chain(struct ocfs2_super *osb, | 
|  | 473 | struct inode *gb_inode, | 
|  | 474 | struct ocfs2_dinode *gb_dinode, | 
|  | 475 | struct ocfs2_chain_rec *rec, | 
|  | 476 | struct ocfs2_info_freefrag *ffg, | 
|  | 477 | u32 chunks_in_group) | 
|  | 478 | { | 
|  | 479 | int status = 0, used; | 
|  | 480 | u64 blkno; | 
|  | 481 |  | 
|  | 482 | struct buffer_head *bh = NULL; | 
|  | 483 | struct ocfs2_group_desc *bg = NULL; | 
|  | 484 |  | 
|  | 485 | unsigned int max_bits, num_clusters; | 
|  | 486 | unsigned int offset = 0, cluster, chunk; | 
|  | 487 | unsigned int chunk_free, last_chunksize = 0; | 
|  | 488 |  | 
|  | 489 | if (!le32_to_cpu(rec->c_free)) | 
|  | 490 | goto bail; | 
|  | 491 |  | 
|  | 492 | do { | 
|  | 493 | if (!bg) | 
|  | 494 | blkno = le64_to_cpu(rec->c_blkno); | 
|  | 495 | else | 
|  | 496 | blkno = le64_to_cpu(bg->bg_next_group); | 
|  | 497 |  | 
|  | 498 | if (bh) { | 
|  | 499 | brelse(bh); | 
|  | 500 | bh = NULL; | 
|  | 501 | } | 
|  | 502 |  | 
|  | 503 | if (o2info_coherent(&ffg->iff_req)) | 
|  | 504 | status = ocfs2_read_group_descriptor(gb_inode, | 
|  | 505 | gb_dinode, | 
|  | 506 | blkno, &bh); | 
|  | 507 | else | 
|  | 508 | status = ocfs2_read_blocks_sync(osb, blkno, 1, &bh); | 
|  | 509 |  | 
|  | 510 | if (status < 0) { | 
|  | 511 | mlog(ML_ERROR, "Can't read the group descriptor # " | 
|  | 512 | "%llu from device.", (unsigned long long)blkno); | 
|  | 513 | status = -EIO; | 
|  | 514 | goto bail; | 
|  | 515 | } | 
|  | 516 |  | 
|  | 517 | bg = (struct ocfs2_group_desc *)bh->b_data; | 
|  | 518 |  | 
|  | 519 | if (!le16_to_cpu(bg->bg_free_bits_count)) | 
|  | 520 | continue; | 
|  | 521 |  | 
|  | 522 | max_bits = le16_to_cpu(bg->bg_bits); | 
|  | 523 | offset = 0; | 
|  | 524 |  | 
|  | 525 | for (chunk = 0; chunk < chunks_in_group; chunk++) { | 
|  | 526 | /* | 
|  | 527 | * last chunk may be not an entire one. | 
|  | 528 | */ | 
|  | 529 | if ((offset + ffg->iff_chunksize) > max_bits) | 
|  | 530 | num_clusters = max_bits - offset; | 
|  | 531 | else | 
|  | 532 | num_clusters = ffg->iff_chunksize; | 
|  | 533 |  | 
|  | 534 | chunk_free = 0; | 
|  | 535 | for (cluster = 0; cluster < num_clusters; cluster++) { | 
|  | 536 | used = ocfs2_test_bit(offset, | 
|  | 537 | (unsigned long *)bg->bg_bitmap); | 
|  | 538 | /* | 
|  | 539 | * - chunk_free counts free clusters in #N chunk. | 
|  | 540 | * - last_chunksize records the size(in) clusters | 
|  | 541 | *   for the last real free chunk being counted. | 
|  | 542 | */ | 
|  | 543 | if (!used) { | 
|  | 544 | last_chunksize++; | 
|  | 545 | chunk_free++; | 
|  | 546 | } | 
|  | 547 |  | 
|  | 548 | if (used && last_chunksize) { | 
|  | 549 | ocfs2_info_update_ffg(ffg, | 
|  | 550 | last_chunksize); | 
|  | 551 | last_chunksize = 0; | 
|  | 552 | } | 
|  | 553 |  | 
|  | 554 | offset++; | 
|  | 555 | } | 
|  | 556 |  | 
|  | 557 | if (chunk_free == ffg->iff_chunksize) | 
|  | 558 | ffg->iff_ffs.ffs_free_chunks++; | 
|  | 559 | } | 
|  | 560 |  | 
|  | 561 | /* | 
|  | 562 | * need to update the info for last free chunk. | 
|  | 563 | */ | 
|  | 564 | if (last_chunksize) | 
|  | 565 | ocfs2_info_update_ffg(ffg, last_chunksize); | 
|  | 566 |  | 
|  | 567 | } while (le64_to_cpu(bg->bg_next_group)); | 
|  | 568 |  | 
|  | 569 | bail: | 
|  | 570 | brelse(bh); | 
|  | 571 |  | 
|  | 572 | return status; | 
|  | 573 | } | 
|  | 574 |  | 
|  | 575 | int ocfs2_info_freefrag_scan_bitmap(struct ocfs2_super *osb, | 
|  | 576 | struct inode *gb_inode, u64 blkno, | 
|  | 577 | struct ocfs2_info_freefrag *ffg) | 
|  | 578 | { | 
|  | 579 | u32 chunks_in_group; | 
|  | 580 | int status = 0, unlock = 0, i; | 
|  | 581 |  | 
|  | 582 | struct buffer_head *bh = NULL; | 
|  | 583 | struct ocfs2_chain_list *cl = NULL; | 
|  | 584 | struct ocfs2_chain_rec *rec = NULL; | 
|  | 585 | struct ocfs2_dinode *gb_dinode = NULL; | 
|  | 586 |  | 
|  | 587 | if (gb_inode) | 
|  | 588 | mutex_lock(&gb_inode->i_mutex); | 
|  | 589 |  | 
|  | 590 | if (o2info_coherent(&ffg->iff_req)) { | 
|  | 591 | status = ocfs2_inode_lock(gb_inode, &bh, 0); | 
|  | 592 | if (status < 0) { | 
|  | 593 | mlog_errno(status); | 
|  | 594 | goto bail; | 
|  | 595 | } | 
|  | 596 | unlock = 1; | 
|  | 597 | } else { | 
|  | 598 | status = ocfs2_read_blocks_sync(osb, blkno, 1, &bh); | 
|  | 599 | if (status < 0) { | 
|  | 600 | mlog_errno(status); | 
|  | 601 | goto bail; | 
|  | 602 | } | 
|  | 603 | } | 
|  | 604 |  | 
|  | 605 | gb_dinode = (struct ocfs2_dinode *)bh->b_data; | 
|  | 606 | cl = &(gb_dinode->id2.i_chain); | 
|  | 607 |  | 
|  | 608 | /* | 
|  | 609 | * Chunksize(in) clusters from userspace should be | 
|  | 610 | * less than clusters in a group. | 
|  | 611 | */ | 
|  | 612 | if (ffg->iff_chunksize > le16_to_cpu(cl->cl_cpg)) { | 
|  | 613 | status = -EINVAL; | 
|  | 614 | goto bail; | 
|  | 615 | } | 
|  | 616 |  | 
|  | 617 | memset(&ffg->iff_ffs, 0, sizeof(struct ocfs2_info_freefrag_stats)); | 
|  | 618 |  | 
|  | 619 | ffg->iff_ffs.ffs_min = ~0U; | 
|  | 620 | ffg->iff_ffs.ffs_clusters = | 
|  | 621 | le32_to_cpu(gb_dinode->id1.bitmap1.i_total); | 
|  | 622 | ffg->iff_ffs.ffs_free_clusters = ffg->iff_ffs.ffs_clusters - | 
|  | 623 | le32_to_cpu(gb_dinode->id1.bitmap1.i_used); | 
|  | 624 |  | 
|  | 625 | chunks_in_group = le16_to_cpu(cl->cl_cpg) / ffg->iff_chunksize + 1; | 
|  | 626 |  | 
|  | 627 | for (i = 0; i < le16_to_cpu(cl->cl_next_free_rec); i++) { | 
|  | 628 | rec = &(cl->cl_recs[i]); | 
|  | 629 | status = ocfs2_info_freefrag_scan_chain(osb, gb_inode, | 
|  | 630 | gb_dinode, | 
|  | 631 | rec, ffg, | 
|  | 632 | chunks_in_group); | 
|  | 633 | if (status) | 
|  | 634 | goto bail; | 
|  | 635 | } | 
|  | 636 |  | 
|  | 637 | if (ffg->iff_ffs.ffs_free_chunks_real) | 
|  | 638 | ffg->iff_ffs.ffs_avg = (ffg->iff_ffs.ffs_avg / | 
|  | 639 | ffg->iff_ffs.ffs_free_chunks_real); | 
|  | 640 | bail: | 
|  | 641 | if (unlock) | 
|  | 642 | ocfs2_inode_unlock(gb_inode, 0); | 
|  | 643 |  | 
|  | 644 | if (gb_inode) | 
|  | 645 | mutex_unlock(&gb_inode->i_mutex); | 
|  | 646 |  | 
|  | 647 | if (gb_inode) | 
|  | 648 | iput(gb_inode); | 
|  | 649 |  | 
|  | 650 | brelse(bh); | 
|  | 651 |  | 
|  | 652 | return status; | 
|  | 653 | } | 
|  | 654 |  | 
|  | 655 | int ocfs2_info_handle_freefrag(struct inode *inode, | 
|  | 656 | struct ocfs2_info_request __user *req) | 
|  | 657 | { | 
|  | 658 | u64 blkno = -1; | 
|  | 659 | char namebuf[40]; | 
|  | 660 | int status = -EFAULT, type = GLOBAL_BITMAP_SYSTEM_INODE; | 
|  | 661 |  | 
|  | 662 | struct ocfs2_info_freefrag *oiff; | 
|  | 663 | struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); | 
|  | 664 | struct inode *gb_inode = NULL; | 
|  | 665 |  | 
|  | 666 | oiff = kzalloc(sizeof(struct ocfs2_info_freefrag), GFP_KERNEL); | 
|  | 667 | if (!oiff) { | 
|  | 668 | status = -ENOMEM; | 
|  | 669 | mlog_errno(status); | 
| Dan Carpenter | 87f0d5c | 2011-05-29 22:57:16 +0300 | [diff] [blame] | 670 | goto out_err; | 
| Tristan Ye | d24a10b9 | 2011-05-24 15:27:17 +0800 | [diff] [blame] | 671 | } | 
|  | 672 |  | 
|  | 673 | if (o2info_from_user(*oiff, req)) | 
|  | 674 | goto bail; | 
|  | 675 | /* | 
|  | 676 | * chunksize from userspace should be power of 2. | 
|  | 677 | */ | 
|  | 678 | if ((oiff->iff_chunksize & (oiff->iff_chunksize - 1)) || | 
|  | 679 | (!oiff->iff_chunksize)) { | 
|  | 680 | status = -EINVAL; | 
|  | 681 | goto bail; | 
|  | 682 | } | 
|  | 683 |  | 
|  | 684 | if (o2info_coherent(&oiff->iff_req)) { | 
|  | 685 | gb_inode = ocfs2_get_system_file_inode(osb, type, | 
|  | 686 | OCFS2_INVALID_SLOT); | 
|  | 687 | if (!gb_inode) { | 
|  | 688 | mlog(ML_ERROR, "unable to get global_bitmap inode\n"); | 
|  | 689 | status = -EIO; | 
|  | 690 | goto bail; | 
|  | 691 | } | 
|  | 692 | } else { | 
|  | 693 | ocfs2_sprintf_system_inode_name(namebuf, sizeof(namebuf), type, | 
|  | 694 | OCFS2_INVALID_SLOT); | 
|  | 695 | status = ocfs2_lookup_ino_from_name(osb->sys_root_inode, | 
|  | 696 | namebuf, | 
|  | 697 | strlen(namebuf), | 
|  | 698 | &blkno); | 
|  | 699 | if (status < 0) { | 
|  | 700 | status = -ENOENT; | 
|  | 701 | goto bail; | 
|  | 702 | } | 
|  | 703 | } | 
|  | 704 |  | 
|  | 705 | status = ocfs2_info_freefrag_scan_bitmap(osb, gb_inode, blkno, oiff); | 
|  | 706 | if (status < 0) | 
|  | 707 | goto bail; | 
|  | 708 |  | 
|  | 709 | o2info_set_request_filled(&oiff->iff_req); | 
|  | 710 |  | 
|  | 711 | if (o2info_to_user(*oiff, req)) | 
|  | 712 | goto bail; | 
|  | 713 |  | 
|  | 714 | status = 0; | 
|  | 715 | bail: | 
|  | 716 | if (status) | 
|  | 717 | o2info_set_request_error(&oiff->iff_req, req); | 
|  | 718 |  | 
|  | 719 | kfree(oiff); | 
| Dan Carpenter | 87f0d5c | 2011-05-29 22:57:16 +0300 | [diff] [blame] | 720 | out_err: | 
| Tristan Ye | ddee5cd | 2010-05-22 16:26:33 +0800 | [diff] [blame] | 721 | return status; | 
|  | 722 | } | 
|  | 723 |  | 
|  | 724 | int ocfs2_info_handle_unknown(struct inode *inode, | 
|  | 725 | struct ocfs2_info_request __user *req) | 
|  | 726 | { | 
|  | 727 | int status = -EFAULT; | 
|  | 728 | struct ocfs2_info_request oir; | 
|  | 729 |  | 
|  | 730 | if (o2info_from_user(oir, req)) | 
|  | 731 | goto bail; | 
|  | 732 |  | 
| Tristan Ye | 8aa1fa3 | 2011-05-24 15:22:59 +0800 | [diff] [blame] | 733 | o2info_clear_request_filled(&oir); | 
| Tristan Ye | ddee5cd | 2010-05-22 16:26:33 +0800 | [diff] [blame] | 734 |  | 
|  | 735 | if (o2info_to_user(oir, req)) | 
|  | 736 | goto bail; | 
|  | 737 |  | 
|  | 738 | status = 0; | 
|  | 739 | bail: | 
|  | 740 | if (status) | 
| Tristan Ye | 8aa1fa3 | 2011-05-24 15:22:59 +0800 | [diff] [blame] | 741 | o2info_set_request_error(&oir, req); | 
| Tristan Ye | ddee5cd | 2010-05-22 16:26:33 +0800 | [diff] [blame] | 742 |  | 
|  | 743 | return status; | 
|  | 744 | } | 
|  | 745 |  | 
|  | 746 | /* | 
|  | 747 | * Validate and distinguish OCFS2_IOC_INFO requests. | 
|  | 748 | * | 
|  | 749 | * - validate the magic number. | 
|  | 750 | * - distinguish different requests. | 
|  | 751 | * - validate size of different requests. | 
|  | 752 | */ | 
|  | 753 | int ocfs2_info_handle_request(struct inode *inode, | 
|  | 754 | struct ocfs2_info_request __user *req) | 
|  | 755 | { | 
|  | 756 | int status = -EFAULT; | 
|  | 757 | struct ocfs2_info_request oir; | 
|  | 758 |  | 
|  | 759 | if (o2info_from_user(oir, req)) | 
|  | 760 | goto bail; | 
|  | 761 |  | 
|  | 762 | status = -EINVAL; | 
|  | 763 | if (oir.ir_magic != OCFS2_INFO_MAGIC) | 
|  | 764 | goto bail; | 
|  | 765 |  | 
|  | 766 | switch (oir.ir_code) { | 
|  | 767 | case OCFS2_INFO_BLOCKSIZE: | 
|  | 768 | if (oir.ir_size == sizeof(struct ocfs2_info_blocksize)) | 
|  | 769 | status = ocfs2_info_handle_blocksize(inode, req); | 
|  | 770 | break; | 
|  | 771 | case OCFS2_INFO_CLUSTERSIZE: | 
|  | 772 | if (oir.ir_size == sizeof(struct ocfs2_info_clustersize)) | 
|  | 773 | status = ocfs2_info_handle_clustersize(inode, req); | 
|  | 774 | break; | 
|  | 775 | case OCFS2_INFO_MAXSLOTS: | 
|  | 776 | if (oir.ir_size == sizeof(struct ocfs2_info_maxslots)) | 
|  | 777 | status = ocfs2_info_handle_maxslots(inode, req); | 
|  | 778 | break; | 
|  | 779 | case OCFS2_INFO_LABEL: | 
|  | 780 | if (oir.ir_size == sizeof(struct ocfs2_info_label)) | 
|  | 781 | status = ocfs2_info_handle_label(inode, req); | 
|  | 782 | break; | 
|  | 783 | case OCFS2_INFO_UUID: | 
|  | 784 | if (oir.ir_size == sizeof(struct ocfs2_info_uuid)) | 
|  | 785 | status = ocfs2_info_handle_uuid(inode, req); | 
|  | 786 | break; | 
|  | 787 | case OCFS2_INFO_FS_FEATURES: | 
|  | 788 | if (oir.ir_size == sizeof(struct ocfs2_info_fs_features)) | 
|  | 789 | status = ocfs2_info_handle_fs_features(inode, req); | 
|  | 790 | break; | 
|  | 791 | case OCFS2_INFO_JOURNAL_SIZE: | 
|  | 792 | if (oir.ir_size == sizeof(struct ocfs2_info_journal_size)) | 
|  | 793 | status = ocfs2_info_handle_journal_size(inode, req); | 
|  | 794 | break; | 
| Tristan Ye | 3e5db17 | 2011-05-24 15:25:54 +0800 | [diff] [blame] | 795 | case OCFS2_INFO_FREEINODE: | 
|  | 796 | if (oir.ir_size == sizeof(struct ocfs2_info_freeinode)) | 
|  | 797 | status = ocfs2_info_handle_freeinode(inode, req); | 
|  | 798 | break; | 
| Tristan Ye | d24a10b9 | 2011-05-24 15:27:17 +0800 | [diff] [blame] | 799 | case OCFS2_INFO_FREEFRAG: | 
|  | 800 | if (oir.ir_size == sizeof(struct ocfs2_info_freefrag)) | 
|  | 801 | status = ocfs2_info_handle_freefrag(inode, req); | 
|  | 802 | break; | 
| Tristan Ye | ddee5cd | 2010-05-22 16:26:33 +0800 | [diff] [blame] | 803 | default: | 
|  | 804 | status = ocfs2_info_handle_unknown(inode, req); | 
|  | 805 | break; | 
|  | 806 | } | 
|  | 807 |  | 
|  | 808 | bail: | 
|  | 809 | return status; | 
|  | 810 | } | 
|  | 811 |  | 
|  | 812 | int ocfs2_get_request_ptr(struct ocfs2_info *info, int idx, | 
|  | 813 | u64 *req_addr, int compat_flag) | 
|  | 814 | { | 
|  | 815 | int status = -EFAULT; | 
|  | 816 | u64 __user *bp = NULL; | 
|  | 817 |  | 
|  | 818 | if (compat_flag) { | 
|  | 819 | #ifdef CONFIG_COMPAT | 
|  | 820 | /* | 
|  | 821 | * pointer bp stores the base address of a pointers array, | 
|  | 822 | * which collects all addresses of separate request. | 
|  | 823 | */ | 
|  | 824 | bp = (u64 __user *)(unsigned long)compat_ptr(info->oi_requests); | 
|  | 825 | #else | 
|  | 826 | BUG(); | 
|  | 827 | #endif | 
|  | 828 | } else | 
|  | 829 | bp = (u64 __user *)(unsigned long)(info->oi_requests); | 
|  | 830 |  | 
|  | 831 | if (o2info_from_user(*req_addr, bp + idx)) | 
|  | 832 | goto bail; | 
|  | 833 |  | 
|  | 834 | status = 0; | 
|  | 835 | bail: | 
|  | 836 | return status; | 
|  | 837 | } | 
|  | 838 |  | 
|  | 839 | /* | 
|  | 840 | * OCFS2_IOC_INFO handles an array of requests passed from userspace. | 
|  | 841 | * | 
|  | 842 | * ocfs2_info_handle() recevies a large info aggregation, grab and | 
|  | 843 | * validate the request count from header, then break it into small | 
|  | 844 | * pieces, later specific handlers can handle them one by one. | 
|  | 845 | * | 
|  | 846 | * Idea here is to make each separate request small enough to ensure | 
|  | 847 | * a better backward&forward compatibility, since a small piece of | 
|  | 848 | * request will be less likely to be broken if disk layout get changed. | 
|  | 849 | */ | 
|  | 850 | int ocfs2_info_handle(struct inode *inode, struct ocfs2_info *info, | 
|  | 851 | int compat_flag) | 
|  | 852 | { | 
|  | 853 | int i, status = 0; | 
|  | 854 | u64 req_addr; | 
|  | 855 | struct ocfs2_info_request __user *reqp; | 
|  | 856 |  | 
|  | 857 | if ((info->oi_count > OCFS2_INFO_MAX_REQUEST) || | 
|  | 858 | (!info->oi_requests)) { | 
|  | 859 | status = -EINVAL; | 
|  | 860 | goto bail; | 
|  | 861 | } | 
|  | 862 |  | 
|  | 863 | for (i = 0; i < info->oi_count; i++) { | 
|  | 864 |  | 
|  | 865 | status = ocfs2_get_request_ptr(info, i, &req_addr, compat_flag); | 
|  | 866 | if (status) | 
|  | 867 | break; | 
|  | 868 |  | 
|  | 869 | reqp = (struct ocfs2_info_request *)(unsigned long)req_addr; | 
|  | 870 | if (!reqp) { | 
|  | 871 | status = -EINVAL; | 
|  | 872 | goto bail; | 
|  | 873 | } | 
|  | 874 |  | 
|  | 875 | status = ocfs2_info_handle_request(inode, reqp); | 
|  | 876 | if (status) | 
|  | 877 | break; | 
|  | 878 | } | 
|  | 879 |  | 
|  | 880 | bail: | 
|  | 881 | return status; | 
|  | 882 | } | 
|  | 883 |  | 
| Andi Kleen | c9ec148 | 2008-01-27 03:17:17 +0100 | [diff] [blame] | 884 | long ocfs2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) | 
| Herbert Poetzl | ca4d147 | 2006-07-03 17:27:12 -0700 | [diff] [blame] | 885 | { | 
| Andi Kleen | c9ec148 | 2008-01-27 03:17:17 +0100 | [diff] [blame] | 886 | struct inode *inode = filp->f_path.dentry->d_inode; | 
| Herbert Poetzl | ca4d147 | 2006-07-03 17:27:12 -0700 | [diff] [blame] | 887 | unsigned int flags; | 
| Tao Ma | d659072 | 2007-12-18 15:47:03 +0800 | [diff] [blame] | 888 | int new_clusters; | 
| Herbert Poetzl | ca4d147 | 2006-07-03 17:27:12 -0700 | [diff] [blame] | 889 | int status; | 
| Mark Fasheh | b258010 | 2007-03-09 16:53:21 -0800 | [diff] [blame] | 890 | struct ocfs2_space_resv sr; | 
| Tao Ma | 7909f2b | 2007-12-18 15:47:25 +0800 | [diff] [blame] | 891 | struct ocfs2_new_group_input input; | 
| Tao Ma | bd50873 | 2009-09-21 11:25:14 +0800 | [diff] [blame] | 892 | struct reflink_arguments args; | 
|  | 893 | const char *old_path, *new_path; | 
|  | 894 | bool preserve; | 
| Tristan Ye | ddee5cd | 2010-05-22 16:26:33 +0800 | [diff] [blame] | 895 | struct ocfs2_info info; | 
| Herbert Poetzl | ca4d147 | 2006-07-03 17:27:12 -0700 | [diff] [blame] | 896 |  | 
|  | 897 | switch (cmd) { | 
|  | 898 | case OCFS2_IOC_GETFLAGS: | 
|  | 899 | status = ocfs2_get_inode_attr(inode, &flags); | 
|  | 900 | if (status < 0) | 
|  | 901 | return status; | 
|  | 902 |  | 
|  | 903 | flags &= OCFS2_FL_VISIBLE; | 
|  | 904 | return put_user(flags, (int __user *) arg); | 
|  | 905 | case OCFS2_IOC_SETFLAGS: | 
|  | 906 | if (get_user(flags, (int __user *) arg)) | 
|  | 907 | return -EFAULT; | 
|  | 908 |  | 
| Dave Hansen | 42a74f2 | 2008-02-15 14:37:46 -0800 | [diff] [blame] | 909 | status = mnt_want_write(filp->f_path.mnt); | 
|  | 910 | if (status) | 
|  | 911 | return status; | 
|  | 912 | status = ocfs2_set_inode_attr(inode, flags, | 
| Herbert Poetzl | ca4d147 | 2006-07-03 17:27:12 -0700 | [diff] [blame] | 913 | OCFS2_FL_MODIFIABLE); | 
| Dave Hansen | 42a74f2 | 2008-02-15 14:37:46 -0800 | [diff] [blame] | 914 | mnt_drop_write(filp->f_path.mnt); | 
|  | 915 | return status; | 
| Mark Fasheh | b258010 | 2007-03-09 16:53:21 -0800 | [diff] [blame] | 916 | case OCFS2_IOC_RESVSP: | 
|  | 917 | case OCFS2_IOC_RESVSP64: | 
|  | 918 | case OCFS2_IOC_UNRESVSP: | 
|  | 919 | case OCFS2_IOC_UNRESVSP64: | 
|  | 920 | if (copy_from_user(&sr, (int __user *) arg, sizeof(sr))) | 
|  | 921 | return -EFAULT; | 
|  | 922 |  | 
|  | 923 | return ocfs2_change_file_space(filp, cmd, &sr); | 
| Tao Ma | d659072 | 2007-12-18 15:47:03 +0800 | [diff] [blame] | 924 | case OCFS2_IOC_GROUP_EXTEND: | 
| Mark Fasheh | 0957f00 | 2007-12-18 18:58:18 -0800 | [diff] [blame] | 925 | if (!capable(CAP_SYS_RESOURCE)) | 
|  | 926 | return -EPERM; | 
|  | 927 |  | 
| Tao Ma | d659072 | 2007-12-18 15:47:03 +0800 | [diff] [blame] | 928 | if (get_user(new_clusters, (int __user *)arg)) | 
|  | 929 | return -EFAULT; | 
|  | 930 |  | 
|  | 931 | return ocfs2_group_extend(inode, new_clusters); | 
| Tao Ma | 7909f2b | 2007-12-18 15:47:25 +0800 | [diff] [blame] | 932 | case OCFS2_IOC_GROUP_ADD: | 
|  | 933 | case OCFS2_IOC_GROUP_ADD64: | 
| Mark Fasheh | 0957f00 | 2007-12-18 18:58:18 -0800 | [diff] [blame] | 934 | if (!capable(CAP_SYS_RESOURCE)) | 
|  | 935 | return -EPERM; | 
|  | 936 |  | 
| Tao Ma | 7909f2b | 2007-12-18 15:47:25 +0800 | [diff] [blame] | 937 | if (copy_from_user(&input, (int __user *) arg, sizeof(input))) | 
|  | 938 | return -EFAULT; | 
|  | 939 |  | 
|  | 940 | return ocfs2_group_add(inode, &input); | 
| Tao Ma | bd50873 | 2009-09-21 11:25:14 +0800 | [diff] [blame] | 941 | case OCFS2_IOC_REFLINK: | 
|  | 942 | if (copy_from_user(&args, (struct reflink_arguments *)arg, | 
|  | 943 | sizeof(args))) | 
|  | 944 | return -EFAULT; | 
|  | 945 | old_path = (const char *)(unsigned long)args.old_path; | 
|  | 946 | new_path = (const char *)(unsigned long)args.new_path; | 
|  | 947 | preserve = (args.preserve != 0); | 
|  | 948 |  | 
|  | 949 | return ocfs2_reflink_ioctl(inode, old_path, new_path, preserve); | 
| Tristan Ye | ddee5cd | 2010-05-22 16:26:33 +0800 | [diff] [blame] | 950 | case OCFS2_IOC_INFO: | 
|  | 951 | if (copy_from_user(&info, (struct ocfs2_info __user *)arg, | 
|  | 952 | sizeof(struct ocfs2_info))) | 
|  | 953 | return -EFAULT; | 
|  | 954 |  | 
|  | 955 | return ocfs2_info_handle(inode, &info, 0); | 
| Tao Ma | 55e6787 | 2011-05-23 10:36:44 +0800 | [diff] [blame] | 956 | case FITRIM: | 
|  | 957 | { | 
|  | 958 | struct super_block *sb = inode->i_sb; | 
|  | 959 | struct fstrim_range range; | 
|  | 960 | int ret = 0; | 
|  | 961 |  | 
|  | 962 | if (!capable(CAP_SYS_ADMIN)) | 
|  | 963 | return -EPERM; | 
|  | 964 |  | 
|  | 965 | if (copy_from_user(&range, (struct fstrim_range *)arg, | 
|  | 966 | sizeof(range))) | 
|  | 967 | return -EFAULT; | 
|  | 968 |  | 
|  | 969 | ret = ocfs2_trim_fs(sb, &range); | 
|  | 970 | if (ret < 0) | 
|  | 971 | return ret; | 
|  | 972 |  | 
|  | 973 | if (copy_to_user((struct fstrim_range *)arg, &range, | 
|  | 974 | sizeof(range))) | 
|  | 975 | return -EFAULT; | 
|  | 976 |  | 
|  | 977 | return 0; | 
|  | 978 | } | 
| Tristan Ye | 53069d4 | 2011-05-25 14:23:43 +0800 | [diff] [blame] | 979 | case OCFS2_IOC_MOVE_EXT: | 
|  | 980 | return ocfs2_ioctl_move_extents(filp, (void __user *)arg); | 
| Herbert Poetzl | ca4d147 | 2006-07-03 17:27:12 -0700 | [diff] [blame] | 981 | default: | 
|  | 982 | return -ENOTTY; | 
|  | 983 | } | 
|  | 984 | } | 
|  | 985 |  | 
| Mark Fasheh | 586d232 | 2007-03-09 15:56:28 -0800 | [diff] [blame] | 986 | #ifdef CONFIG_COMPAT | 
|  | 987 | long ocfs2_compat_ioctl(struct file *file, unsigned cmd, unsigned long arg) | 
|  | 988 | { | 
| Tao Ma | 34e6c59 | 2010-01-27 10:21:52 +0800 | [diff] [blame] | 989 | bool preserve; | 
|  | 990 | struct reflink_arguments args; | 
|  | 991 | struct inode *inode = file->f_path.dentry->d_inode; | 
| Tristan Ye | ddee5cd | 2010-05-22 16:26:33 +0800 | [diff] [blame] | 992 | struct ocfs2_info info; | 
| Tao Ma | 34e6c59 | 2010-01-27 10:21:52 +0800 | [diff] [blame] | 993 |  | 
| Mark Fasheh | 586d232 | 2007-03-09 15:56:28 -0800 | [diff] [blame] | 994 | switch (cmd) { | 
|  | 995 | case OCFS2_IOC32_GETFLAGS: | 
|  | 996 | cmd = OCFS2_IOC_GETFLAGS; | 
|  | 997 | break; | 
|  | 998 | case OCFS2_IOC32_SETFLAGS: | 
|  | 999 | cmd = OCFS2_IOC_SETFLAGS; | 
|  | 1000 | break; | 
| Mark Fasheh | b258010 | 2007-03-09 16:53:21 -0800 | [diff] [blame] | 1001 | case OCFS2_IOC_RESVSP: | 
|  | 1002 | case OCFS2_IOC_RESVSP64: | 
|  | 1003 | case OCFS2_IOC_UNRESVSP: | 
|  | 1004 | case OCFS2_IOC_UNRESVSP64: | 
| Tao Ma | d659072 | 2007-12-18 15:47:03 +0800 | [diff] [blame] | 1005 | case OCFS2_IOC_GROUP_EXTEND: | 
| Tao Ma | 7909f2b | 2007-12-18 15:47:25 +0800 | [diff] [blame] | 1006 | case OCFS2_IOC_GROUP_ADD: | 
|  | 1007 | case OCFS2_IOC_GROUP_ADD64: | 
| Tao Ma | 55e6787 | 2011-05-23 10:36:44 +0800 | [diff] [blame] | 1008 | case FITRIM: | 
| Mark Fasheh | b258010 | 2007-03-09 16:53:21 -0800 | [diff] [blame] | 1009 | break; | 
| Tao Ma | 34e6c59 | 2010-01-27 10:21:52 +0800 | [diff] [blame] | 1010 | case OCFS2_IOC_REFLINK: | 
|  | 1011 | if (copy_from_user(&args, (struct reflink_arguments *)arg, | 
|  | 1012 | sizeof(args))) | 
|  | 1013 | return -EFAULT; | 
|  | 1014 | preserve = (args.preserve != 0); | 
|  | 1015 |  | 
|  | 1016 | return ocfs2_reflink_ioctl(inode, compat_ptr(args.old_path), | 
|  | 1017 | compat_ptr(args.new_path), preserve); | 
| Tristan Ye | ddee5cd | 2010-05-22 16:26:33 +0800 | [diff] [blame] | 1018 | case OCFS2_IOC_INFO: | 
|  | 1019 | if (copy_from_user(&info, (struct ocfs2_info __user *)arg, | 
|  | 1020 | sizeof(struct ocfs2_info))) | 
|  | 1021 | return -EFAULT; | 
|  | 1022 |  | 
|  | 1023 | return ocfs2_info_handle(inode, &info, 1); | 
| Tristan Ye | 53069d4 | 2011-05-25 14:23:43 +0800 | [diff] [blame] | 1024 | case OCFS2_IOC_MOVE_EXT: | 
|  | 1025 | break; | 
| Mark Fasheh | 586d232 | 2007-03-09 15:56:28 -0800 | [diff] [blame] | 1026 | default: | 
|  | 1027 | return -ENOIOCTLCMD; | 
|  | 1028 | } | 
|  | 1029 |  | 
| Andi Kleen | c9ec148 | 2008-01-27 03:17:17 +0100 | [diff] [blame] | 1030 | return ocfs2_ioctl(file, cmd, arg); | 
| Mark Fasheh | 586d232 | 2007-03-09 15:56:28 -0800 | [diff] [blame] | 1031 | } | 
|  | 1032 | #endif |