| 1 | |
| 2 | #include <linux/ceph/ceph_debug.h>
|
| 3 | #include <linux/in.h>
|
| 4 |
|
| 5 | #include "super.h"
|
| 6 | #include "mds_client.h"
|
| 7 | #include "ioctl.h"
|
| 8 | #include <linux/ceph/striper.h>
|
| 9 | #include <linux/fscrypt.h>
|
| 10 |
|
| 11 | |
| 12 | * ioctls
|
| 13 |
|
| 14 |
|
| 15 | |
| 16 | * get and set the file layout
|
| 17 |
|
| 18 | static long ceph_ioctl_get_layout(struct file *file, void __user *arg)
|
| 19 | {
|
| 20 | struct ceph_inode_info *ci = ceph_inode(file_inode(file));
|
| 21 | struct ceph_ioctl_layout l;
|
| 22 | int err;
|
| 23 |
|
| 24 | err = ceph_do_getattr(file_inode(file), CEPH_STAT_CAP_LAYOUT, false);
|
| 25 | if (!err) {
|
| 26 | l.stripe_unit = ci->i_layout.stripe_unit;
|
| 27 | l.stripe_count = ci->i_layout.stripe_count;
|
| 28 | l.object_size = ci->i_layout.object_size;
|
| 29 | l.data_pool = ci->i_layout.pool_id;
|
| 30 | l.preferred_osd = -1;
|
| 31 | if (copy_to_user(arg, &l, sizeof(l)))
|
| 32 | return -EFAULT;
|
| 33 | }
|
| 34 |
|
| 35 | return err;
|
| 36 | }
|
| 37 |
|
| 38 | static long __validate_layout(struct ceph_mds_client *mdsc,
|
| 39 | struct ceph_ioctl_layout *l)
|
| 40 | {
|
| 41 | int i, err;
|
| 42 |
|
| 43 |
|
| 44 | if ((l->object_size & ~PAGE_MASK) ||
|
| 45 | (l->stripe_unit & ~PAGE_MASK) ||
|
| 46 | ((unsigned)l->stripe_unit != 0 &&
|
| 47 | ((unsigned)l->object_size % (unsigned)l->stripe_unit)))
|
| 48 | return -EINVAL;
|
| 49 |
|
| 50 |
|
| 51 | mutex_lock(&mdsc->mutex);
|
| 52 | err = -EINVAL;
|
| 53 | for (i = 0; i < mdsc->mdsmap->m_num_data_pg_pools; i++)
|
| 54 | if (mdsc->mdsmap->m_data_pg_pools[i] == l->data_pool) {
|
| 55 | err = 0;
|
| 56 | break;
|
| 57 | }
|
| 58 | mutex_unlock(&mdsc->mutex);
|
| 59 | if (err)
|
| 60 | return err;
|
| 61 |
|
| 62 | return 0;
|
| 63 | }
|
| 64 |
|
| 65 | static long ceph_ioctl_set_layout(struct file *file, void __user *arg)
|
| 66 | {
|
| 67 | struct inode *inode = file_inode(file);
|
| 68 | struct ceph_mds_client *mdsc = ceph_sb_to_fs_client(inode->i_sb)->mdsc;
|
| 69 | struct ceph_mds_request *req;
|
| 70 | struct ceph_ioctl_layout l;
|
| 71 | struct ceph_inode_info *ci = ceph_inode(file_inode(file));
|
| 72 | struct ceph_ioctl_layout nl;
|
| 73 | int err;
|
| 74 |
|
| 75 | if (!inode_owner_or_capable(file_mnt_idmap(file), inode))
|
| 76 | return -EACCES;
|
| 77 |
|
| 78 | if (copy_from_user(&l, arg, sizeof(l)))
|
| 79 | return -EFAULT;
|
| 80 |
|
| 81 |
|
| 82 | err = ceph_do_getattr(file_inode(file), CEPH_STAT_CAP_LAYOUT, false);
|
| 83 | if (err)
|
| 84 | return err;
|
| 85 |
|
| 86 | memset(&nl, 0, sizeof(nl));
|
| 87 | if (l.stripe_count)
|
| 88 | nl.stripe_count = l.stripe_count;
|
| 89 | else
|
| 90 | nl.stripe_count = ci->i_layout.stripe_count;
|
| 91 | if (l.stripe_unit)
|
| 92 | nl.stripe_unit = l.stripe_unit;
|
| 93 | else
|
| 94 | nl.stripe_unit = ci->i_layout.stripe_unit;
|
| 95 | if (l.object_size)
|
| 96 | nl.object_size = l.object_size;
|
| 97 | else
|
| 98 | nl.object_size = ci->i_layout.object_size;
|
| 99 | if (l.data_pool)
|
| 100 | nl.data_pool = l.data_pool;
|
| 101 | else
|
| 102 | nl.data_pool = ci->i_layout.pool_id;
|
| 103 |
|
| 104 |
|
| 105 | nl.preferred_osd = -1;
|
| 106 |
|
| 107 | err = __validate_layout(mdsc, &nl);
|
| 108 | if (err)
|
| 109 | return err;
|
| 110 |
|
| 111 | req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_SETLAYOUT,
|
| 112 | USE_AUTH_MDS);
|
| 113 | if (IS_ERR(req))
|
| 114 | return PTR_ERR(req);
|
| 115 | req->r_inode = inode;
|
| 116 | ihold(inode);
|
| 117 | req->r_num_caps = 1;
|
| 118 |
|
| 119 | req->r_inode_drop = CEPH_CAP_FILE_SHARED | CEPH_CAP_FILE_EXCL;
|
| 120 |
|
| 121 | req->r_args.setlayout.layout.fl_stripe_unit =
|
| 122 | cpu_to_le32(l.stripe_unit);
|
| 123 | req->r_args.setlayout.layout.fl_stripe_count =
|
| 124 | cpu_to_le32(l.stripe_count);
|
| 125 | req->r_args.setlayout.layout.fl_object_size =
|
| 126 | cpu_to_le32(l.object_size);
|
| 127 | req->r_args.setlayout.layout.fl_pg_pool = cpu_to_le32(l.data_pool);
|
| 128 |
|
| 129 | err = ceph_mdsc_do_request(mdsc, NULL, req);
|
| 130 | ceph_mdsc_put_request(req);
|
| 131 | return err;
|
| 132 | }
|
| 133 |
|
| 134 | |
| 135 | * Set a layout policy on a directory inode. All items in the tree
|
| 136 | * rooted at this inode will inherit this layout on creation,
|
| 137 | * (It doesn't apply retroactively )
|
| 138 | * unless a subdirectory has its own layout policy.
|
| 139 |
|
| 140 | static long ceph_ioctl_set_layout_policy (struct file *file, void __user *arg)
|
| 141 | {
|
| 142 | struct inode *inode = file_inode(file);
|
| 143 | struct ceph_mds_request *req;
|
| 144 | struct ceph_ioctl_layout l;
|
| 145 | int err;
|
| 146 | struct ceph_mds_client *mdsc = ceph_sb_to_fs_client(inode->i_sb)->mdsc;
|
| 147 |
|
| 148 | if (!inode_owner_or_capable(file_mnt_idmap(file), inode))
|
| 149 | return -EACCES;
|
| 150 |
|
| 151 |
|
| 152 | if (copy_from_user(&l, arg, sizeof(l)))
|
| 153 | return -EFAULT;
|
| 154 |
|
| 155 | err = __validate_layout(mdsc, &l);
|
| 156 | if (err)
|
| 157 | return err;
|
| 158 |
|
| 159 | req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_SETDIRLAYOUT,
|
| 160 | USE_AUTH_MDS);
|
| 161 |
|
| 162 | if (IS_ERR(req))
|
| 163 | return PTR_ERR(req);
|
| 164 | req->r_inode = inode;
|
| 165 | ihold(inode);
|
| 166 | req->r_num_caps = 1;
|
| 167 |
|
| 168 | req->r_args.setlayout.layout.fl_stripe_unit =
|
| 169 | cpu_to_le32(l.stripe_unit);
|
| 170 | req->r_args.setlayout.layout.fl_stripe_count =
|
| 171 | cpu_to_le32(l.stripe_count);
|
| 172 | req->r_args.setlayout.layout.fl_object_size =
|
| 173 | cpu_to_le32(l.object_size);
|
| 174 | req->r_args.setlayout.layout.fl_pg_pool =
|
| 175 | cpu_to_le32(l.data_pool);
|
| 176 |
|
| 177 | err = ceph_mdsc_do_request(mdsc, inode, req);
|
| 178 | ceph_mdsc_put_request(req);
|
| 179 | return err;
|
| 180 | }
|
| 181 |
|
| 182 | |
| 183 | * Return object name, size/offset information, and location (OSD
|
| 184 | * number, network address) for a given file offset.
|
| 185 |
|
| 186 | static long ceph_ioctl_get_dataloc(struct file *file, void __user *arg)
|
| 187 | {
|
| 188 | struct ceph_ioctl_dataloc dl;
|
| 189 | struct inode *inode = file_inode(file);
|
| 190 | struct ceph_inode_info *ci = ceph_inode(inode);
|
| 191 | struct ceph_osd_client *osdc =
|
| 192 | &ceph_sb_to_fs_client(inode->i_sb)->client->osdc;
|
| 193 | struct ceph_object_locator oloc;
|
| 194 | CEPH_DEFINE_OID_ONSTACK(oid);
|
| 195 | u32 xlen;
|
| 196 | u64 tmp;
|
| 197 | struct ceph_pg pgid;
|
| 198 | int r;
|
| 199 |
|
| 200 |
|
| 201 | if (copy_from_user(&dl, arg, sizeof(dl)))
|
| 202 | return -EFAULT;
|
| 203 |
|
| 204 | down_read(&osdc->lock);
|
| 205 | ceph_calc_file_object_mapping(&ci->i_layout, dl.file_offset, 1,
|
| 206 | &dl.object_no, &dl.object_offset, &xlen);
|
| 207 | dl.file_offset -= dl.object_offset;
|
| 208 | dl.object_size = ci->i_layout.object_size;
|
| 209 | dl.block_size = ci->i_layout.stripe_unit;
|
| 210 |
|
| 211 |
|
| 212 | tmp = dl.object_offset;
|
| 213 | dl.block_offset = do_div(tmp, dl.block_size);
|
| 214 |
|
| 215 | snprintf(dl.object_name, sizeof(dl.object_name), "%llx.%08llx",
|
| 216 | ceph_ino(inode), dl.object_no);
|
| 217 |
|
| 218 | oloc.pool = ci->i_layout.pool_id;
|
| 219 | oloc.pool_ns = ceph_try_get_string(ci->i_layout.pool_ns);
|
| 220 | ceph_oid_printf(&oid, "%s", dl.object_name);
|
| 221 |
|
| 222 | r = ceph_object_locator_to_pg(osdc->osdmap, &oid, &oloc, &pgid);
|
| 223 |
|
| 224 | ceph_oloc_destroy(&oloc);
|
| 225 | if (r < 0) {
|
| 226 | up_read(&osdc->lock);
|
| 227 | return r;
|
| 228 | }
|
| 229 |
|
| 230 | dl.osd = ceph_pg_to_acting_primary(osdc->osdmap, &pgid);
|
| 231 | if (dl.osd >= 0) {
|
| 232 | struct ceph_entity_addr *a =
|
| 233 | ceph_osd_addr(osdc->osdmap, dl.osd);
|
| 234 | if (a)
|
| 235 | memcpy(&dl.osd_addr, &a->in_addr, sizeof(dl.osd_addr));
|
| 236 | } else {
|
| 237 | memset(&dl.osd_addr, 0, sizeof(dl.osd_addr));
|
| 238 | }
|
| 239 | up_read(&osdc->lock);
|
| 240 |
|
| 241 |
|
| 242 | if (copy_to_user(arg, &dl, sizeof(dl)))
|
| 243 | return -EFAULT;
|
| 244 |
|
| 245 | return 0;
|
| 246 | }
|
| 247 |
|
| 248 | static long ceph_ioctl_lazyio(struct file *file)
|
| 249 | {
|
| 250 | struct ceph_file_info *fi = file->private_data;
|
| 251 | struct inode *inode = file_inode(file);
|
| 252 | struct ceph_inode_info *ci = ceph_inode(inode);
|
| 253 | struct ceph_mds_client *mdsc = ceph_inode_to_fs_client(inode)->mdsc;
|
| 254 | struct ceph_client *cl = mdsc->fsc->client;
|
| 255 | bool is_file_already_lazy = false;
|
| 256 |
|
| 257 | spin_lock(&ci->i_ceph_lock);
|
| 258 | if ((fi->fmode & CEPH_FILE_MODE_LAZY) == 0) {
|
| 259 | fi->fmode |= CEPH_FILE_MODE_LAZY;
|
| 260 | ci->i_nr_by_mode[ffs(CEPH_FILE_MODE_LAZY)]++;
|
| 261 | __ceph_touch_fmode(ci, mdsc, fi->fmode);
|
| 262 | } else {
|
| 263 | is_file_already_lazy = true;
|
| 264 | }
|
| 265 | spin_unlock(&ci->i_ceph_lock);
|
| 266 |
|
| 267 | if (is_file_already_lazy) {
|
| 268 | doutc(cl, "file %p %p %llx.%llx already lazy\n", file, inode,
|
| 269 | ceph_vinop(inode));
|
| 270 | } else {
|
| 271 | doutc(cl, "file %p %p %llx.%llx marked lazy\n", file, inode,
|
| 272 | ceph_vinop(inode));
|
| 273 |
|
| 274 | ceph_check_caps(ci, 0);
|
| 275 | }
|
| 276 |
|
| 277 | return 0;
|
| 278 | }
|
| 279 |
|
| 280 | static long ceph_ioctl_syncio(struct file *file)
|
| 281 | {
|
| 282 | struct ceph_file_info *fi = file->private_data;
|
| 283 |
|
| 284 | fi->flags |= CEPH_F_SYNC;
|
| 285 | return 0;
|
| 286 | }
|
| 287 |
|
| 288 | static int vet_mds_for_fscrypt(struct file *file)
|
| 289 | {
|
| 290 | int i, ret = -EOPNOTSUPP;
|
| 291 | struct ceph_mds_client *mdsc = ceph_sb_to_mdsc(file_inode(file)->i_sb);
|
| 292 |
|
| 293 | mutex_lock(&mdsc->mutex);
|
| 294 | for (i = 0; i < mdsc->max_sessions; i++) {
|
| 295 | struct ceph_mds_session *s = mdsc->sessions[i];
|
| 296 |
|
| 297 | if (!s)
|
| 298 | continue;
|
| 299 | if (test_bit(CEPHFS_FEATURE_ALTERNATE_NAME, &s->s_features))
|
| 300 | ret = 0;
|
| 301 | break;
|
| 302 | }
|
| 303 | mutex_unlock(&mdsc->mutex);
|
| 304 | return ret;
|
| 305 | }
|
| 306 |
|
| 307 | static long ceph_set_encryption_policy(struct file *file, unsigned long arg)
|
| 308 | {
|
| 309 | int ret, got = 0;
|
| 310 | struct inode *inode = file_inode(file);
|
| 311 | struct ceph_inode_info *ci = ceph_inode(inode);
|
| 312 |
|
| 313 |
|
| 314 | if (ci->i_layout.stripe_count > 1)
|
| 315 | return -EINVAL;
|
| 316 |
|
| 317 | ret = vet_mds_for_fscrypt(file);
|
| 318 | if (ret)
|
| 319 | return ret;
|
| 320 |
|
| 321 | |
| 322 | * Ensure we hold these caps so that we _know_ that the rstats check
|
| 323 | * in the empty_dir check is reliable.
|
| 324 |
|
| 325 | ret = ceph_get_caps(file, CEPH_CAP_FILE_SHARED, 0, -1, &got);
|
| 326 | if (ret)
|
| 327 | return ret;
|
| 328 |
|
| 329 | ret = fscrypt_ioctl_set_policy(file, (const void __user *)arg);
|
| 330 | if (got)
|
| 331 | ceph_put_cap_refs(ci, got);
|
| 332 |
|
| 333 | return ret;
|
| 334 | }
|
| 335 |
|
| 336 | static const char *ceph_ioctl_cmd_name(const unsigned int cmd)
|
| 337 | {
|
| 338 | switch (cmd) {
|
| 339 | case CEPH_IOC_GET_LAYOUT:
|
| 340 | return "get_layout";
|
| 341 | case CEPH_IOC_SET_LAYOUT:
|
| 342 | return "set_layout";
|
| 343 | case CEPH_IOC_SET_LAYOUT_POLICY:
|
| 344 | return "set_layout_policy";
|
| 345 | case CEPH_IOC_GET_DATALOC:
|
| 346 | return "get_dataloc";
|
| 347 | case CEPH_IOC_LAZYIO:
|
| 348 | return "lazyio";
|
| 349 | case CEPH_IOC_SYNCIO:
|
| 350 | return "syncio";
|
| 351 | case FS_IOC_SET_ENCRYPTION_POLICY:
|
| 352 | return "set_encryption_policy";
|
| 353 | case FS_IOC_GET_ENCRYPTION_POLICY:
|
| 354 | return "get_encryption_policy";
|
| 355 | case FS_IOC_GET_ENCRYPTION_POLICY_EX:
|
| 356 | return "get_encryption_policy_ex";
|
| 357 | case FS_IOC_ADD_ENCRYPTION_KEY:
|
| 358 | return "add_encryption_key";
|
| 359 | case FS_IOC_REMOVE_ENCRYPTION_KEY:
|
| 360 | return "remove_encryption_key";
|
| 361 | case FS_IOC_REMOVE_ENCRYPTION_KEY_ALL_USERS:
|
| 362 | return "remove_encryption_key_all_users";
|
| 363 | case FS_IOC_GET_ENCRYPTION_KEY_STATUS:
|
| 364 | return "get_encryption_key_status";
|
| 365 | case FS_IOC_GET_ENCRYPTION_NONCE:
|
| 366 | return "get_encryption_nonce";
|
| 367 | default:
|
| 368 | return "unknown";
|
| 369 | }
|
| 370 | }
|
| 371 |
|
| 372 | long ceph_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
| 373 | {
|
| 374 | struct inode *inode = file_inode(file);
|
| 375 | struct ceph_fs_client *fsc = ceph_inode_to_fs_client(inode);
|
| 376 | int ret;
|
| 377 |
|
| 378 | doutc(fsc->client, "file %p %p %llx.%llx cmd %s arg %lu\n", file,
|
| 379 | inode, ceph_vinop(inode), ceph_ioctl_cmd_name(cmd), arg);
|
| 380 | switch (cmd) {
|
| 381 | case CEPH_IOC_GET_LAYOUT:
|
| 382 | return ceph_ioctl_get_layout(file, (void __user *)arg);
|
| 383 |
|
| 384 | case CEPH_IOC_SET_LAYOUT:
|
| 385 | return ceph_ioctl_set_layout(file, (void __user *)arg);
|
| 386 |
|
| 387 | case CEPH_IOC_SET_LAYOUT_POLICY:
|
| 388 | return ceph_ioctl_set_layout_policy(file, (void __user *)arg);
|
| 389 |
|
| 390 | case CEPH_IOC_GET_DATALOC:
|
| 391 | return ceph_ioctl_get_dataloc(file, (void __user *)arg);
|
| 392 |
|
| 393 | case CEPH_IOC_LAZYIO:
|
| 394 | return ceph_ioctl_lazyio(file);
|
| 395 |
|
| 396 | case CEPH_IOC_SYNCIO:
|
| 397 | return ceph_ioctl_syncio(file);
|
| 398 |
|
| 399 | case FS_IOC_SET_ENCRYPTION_POLICY:
|
| 400 | return ceph_set_encryption_policy(file, arg);
|
| 401 |
|
| 402 | case FS_IOC_GET_ENCRYPTION_POLICY:
|
| 403 | ret = vet_mds_for_fscrypt(file);
|
| 404 | if (ret)
|
| 405 | return ret;
|
| 406 | return fscrypt_ioctl_get_policy(file, (void __user *)arg);
|
| 407 |
|
| 408 | case FS_IOC_GET_ENCRYPTION_POLICY_EX:
|
| 409 | ret = vet_mds_for_fscrypt(file);
|
| 410 | if (ret)
|
| 411 | return ret;
|
| 412 | return fscrypt_ioctl_get_policy_ex(file, (void __user *)arg);
|
| 413 |
|
| 414 | case FS_IOC_ADD_ENCRYPTION_KEY:
|
| 415 | ret = vet_mds_for_fscrypt(file);
|
| 416 | if (ret)
|
| 417 | return ret;
|
| 418 | return fscrypt_ioctl_add_key(file, (void __user *)arg);
|
| 419 |
|
| 420 | case FS_IOC_REMOVE_ENCRYPTION_KEY:
|
| 421 | return fscrypt_ioctl_remove_key(file, (void __user *)arg);
|
| 422 |
|
| 423 | case FS_IOC_REMOVE_ENCRYPTION_KEY_ALL_USERS:
|
| 424 | return fscrypt_ioctl_remove_key_all_users(file,
|
| 425 | (void __user *)arg);
|
| 426 |
|
| 427 | case FS_IOC_GET_ENCRYPTION_KEY_STATUS:
|
| 428 | return fscrypt_ioctl_get_key_status(file, (void __user *)arg);
|
| 429 |
|
| 430 | case FS_IOC_GET_ENCRYPTION_NONCE:
|
| 431 | ret = vet_mds_for_fscrypt(file);
|
| 432 | if (ret)
|
| 433 | return ret;
|
| 434 | return fscrypt_ioctl_get_nonce(file, (void __user *)arg);
|
| 435 | }
|
| 436 |
|
| 437 | return -ENOTTY;
|
| 438 | }
|