|
@@ -1598,3 +1598,206 @@ int ceph_mmap(struct file *file, struct vm_area_struct *vma)
|
|
|
vma->vm_ops = &ceph_vmops;
|
|
|
return 0;
|
|
|
}
|
|
|
+
|
|
|
+enum {
|
|
|
+ POOL_READ = 1,
|
|
|
+ POOL_WRITE = 2,
|
|
|
+};
|
|
|
+
|
|
|
+static int __ceph_pool_perm_get(struct ceph_inode_info *ci, u32 pool)
|
|
|
+{
|
|
|
+ struct ceph_fs_client *fsc = ceph_inode_to_client(&ci->vfs_inode);
|
|
|
+ struct ceph_mds_client *mdsc = fsc->mdsc;
|
|
|
+ struct ceph_osd_request *rd_req = NULL, *wr_req = NULL;
|
|
|
+ struct rb_node **p, *parent;
|
|
|
+ struct ceph_pool_perm *perm;
|
|
|
+ struct page **pages;
|
|
|
+ int err = 0, err2 = 0, have = 0;
|
|
|
+
|
|
|
+ down_read(&mdsc->pool_perm_rwsem);
|
|
|
+ p = &mdsc->pool_perm_tree.rb_node;
|
|
|
+ while (*p) {
|
|
|
+ perm = rb_entry(*p, struct ceph_pool_perm, node);
|
|
|
+ if (pool < perm->pool)
|
|
|
+ p = &(*p)->rb_left;
|
|
|
+ else if (pool > perm->pool)
|
|
|
+ p = &(*p)->rb_right;
|
|
|
+ else {
|
|
|
+ have = perm->perm;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ up_read(&mdsc->pool_perm_rwsem);
|
|
|
+ if (*p)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ dout("__ceph_pool_perm_get pool %u no perm cached\n", pool);
|
|
|
+
|
|
|
+ down_write(&mdsc->pool_perm_rwsem);
|
|
|
+ parent = NULL;
|
|
|
+ while (*p) {
|
|
|
+ parent = *p;
|
|
|
+ perm = rb_entry(parent, struct ceph_pool_perm, node);
|
|
|
+ if (pool < perm->pool)
|
|
|
+ p = &(*p)->rb_left;
|
|
|
+ else if (pool > perm->pool)
|
|
|
+ p = &(*p)->rb_right;
|
|
|
+ else {
|
|
|
+ have = perm->perm;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (*p) {
|
|
|
+ up_write(&mdsc->pool_perm_rwsem);
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ rd_req = ceph_osdc_alloc_request(&fsc->client->osdc,
|
|
|
+ ci->i_snap_realm->cached_context,
|
|
|
+ 1, false, GFP_NOFS);
|
|
|
+ if (!rd_req) {
|
|
|
+ err = -ENOMEM;
|
|
|
+ goto out_unlock;
|
|
|
+ }
|
|
|
+
|
|
|
+ rd_req->r_flags = CEPH_OSD_FLAG_READ;
|
|
|
+ osd_req_op_init(rd_req, 0, CEPH_OSD_OP_STAT, 0);
|
|
|
+ rd_req->r_base_oloc.pool = pool;
|
|
|
+ snprintf(rd_req->r_base_oid.name, sizeof(rd_req->r_base_oid.name),
|
|
|
+ "%llx.00000000", ci->i_vino.ino);
|
|
|
+ rd_req->r_base_oid.name_len = strlen(rd_req->r_base_oid.name);
|
|
|
+
|
|
|
+ wr_req = ceph_osdc_alloc_request(&fsc->client->osdc,
|
|
|
+ ci->i_snap_realm->cached_context,
|
|
|
+ 1, false, GFP_NOFS);
|
|
|
+ if (!wr_req) {
|
|
|
+ err = -ENOMEM;
|
|
|
+ goto out_unlock;
|
|
|
+ }
|
|
|
+
|
|
|
+ wr_req->r_flags = CEPH_OSD_FLAG_WRITE |
|
|
|
+ CEPH_OSD_FLAG_ACK | CEPH_OSD_FLAG_ONDISK;
|
|
|
+ osd_req_op_init(wr_req, 0, CEPH_OSD_OP_CREATE, CEPH_OSD_OP_FLAG_EXCL);
|
|
|
+ wr_req->r_base_oloc.pool = pool;
|
|
|
+ wr_req->r_base_oid = rd_req->r_base_oid;
|
|
|
+
|
|
|
+ /* one page should be large enough for STAT data */
|
|
|
+ pages = ceph_alloc_page_vector(1, GFP_KERNEL);
|
|
|
+ if (IS_ERR(pages)) {
|
|
|
+ err = PTR_ERR(pages);
|
|
|
+ goto out_unlock;
|
|
|
+ }
|
|
|
+
|
|
|
+ osd_req_op_raw_data_in_pages(rd_req, 0, pages, PAGE_SIZE,
|
|
|
+ 0, false, true);
|
|
|
+ ceph_osdc_build_request(rd_req, 0, NULL, CEPH_NOSNAP,
|
|
|
+ &ci->vfs_inode.i_mtime);
|
|
|
+ err = ceph_osdc_start_request(&fsc->client->osdc, rd_req, false);
|
|
|
+
|
|
|
+ ceph_osdc_build_request(wr_req, 0, NULL, CEPH_NOSNAP,
|
|
|
+ &ci->vfs_inode.i_mtime);
|
|
|
+ err2 = ceph_osdc_start_request(&fsc->client->osdc, wr_req, false);
|
|
|
+
|
|
|
+ if (!err)
|
|
|
+ err = ceph_osdc_wait_request(&fsc->client->osdc, rd_req);
|
|
|
+ if (!err2)
|
|
|
+ err2 = ceph_osdc_wait_request(&fsc->client->osdc, wr_req);
|
|
|
+
|
|
|
+ if (err >= 0 || err == -ENOENT)
|
|
|
+ have |= POOL_READ;
|
|
|
+ else if (err != -EPERM)
|
|
|
+ goto out_unlock;
|
|
|
+
|
|
|
+ if (err2 == 0 || err2 == -EEXIST)
|
|
|
+ have |= POOL_WRITE;
|
|
|
+ else if (err2 != -EPERM) {
|
|
|
+ err = err2;
|
|
|
+ goto out_unlock;
|
|
|
+ }
|
|
|
+
|
|
|
+ perm = kmalloc(sizeof(*perm), GFP_NOFS);
|
|
|
+ if (!perm) {
|
|
|
+ err = -ENOMEM;
|
|
|
+ goto out_unlock;
|
|
|
+ }
|
|
|
+
|
|
|
+ perm->pool = pool;
|
|
|
+ perm->perm = have;
|
|
|
+ rb_link_node(&perm->node, parent, p);
|
|
|
+ rb_insert_color(&perm->node, &mdsc->pool_perm_tree);
|
|
|
+ err = 0;
|
|
|
+out_unlock:
|
|
|
+ up_write(&mdsc->pool_perm_rwsem);
|
|
|
+
|
|
|
+ if (rd_req)
|
|
|
+ ceph_osdc_put_request(rd_req);
|
|
|
+ if (wr_req)
|
|
|
+ ceph_osdc_put_request(wr_req);
|
|
|
+out:
|
|
|
+ if (!err)
|
|
|
+ err = have;
|
|
|
+ dout("__ceph_pool_perm_get pool %u result = %d\n", pool, err);
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
+int ceph_pool_perm_check(struct ceph_inode_info *ci, int need)
|
|
|
+{
|
|
|
+ u32 pool;
|
|
|
+ int ret, flags;
|
|
|
+
|
|
|
+ if (ceph_test_mount_opt(ceph_inode_to_client(&ci->vfs_inode),
|
|
|
+ NOPOOLPERM))
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ spin_lock(&ci->i_ceph_lock);
|
|
|
+ flags = ci->i_ceph_flags;
|
|
|
+ pool = ceph_file_layout_pg_pool(ci->i_layout);
|
|
|
+ spin_unlock(&ci->i_ceph_lock);
|
|
|
+check:
|
|
|
+ if (flags & CEPH_I_POOL_PERM) {
|
|
|
+ if ((need & CEPH_CAP_FILE_RD) && !(flags & CEPH_I_POOL_RD)) {
|
|
|
+ dout("ceph_pool_perm_check pool %u no read perm\n",
|
|
|
+ pool);
|
|
|
+ return -EPERM;
|
|
|
+ }
|
|
|
+ if ((need & CEPH_CAP_FILE_WR) && !(flags & CEPH_I_POOL_WR)) {
|
|
|
+ dout("ceph_pool_perm_check pool %u no write perm\n",
|
|
|
+ pool);
|
|
|
+ return -EPERM;
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ ret = __ceph_pool_perm_get(ci, pool);
|
|
|
+ if (ret < 0)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ flags = CEPH_I_POOL_PERM;
|
|
|
+ if (ret & POOL_READ)
|
|
|
+ flags |= CEPH_I_POOL_RD;
|
|
|
+ if (ret & POOL_WRITE)
|
|
|
+ flags |= CEPH_I_POOL_WR;
|
|
|
+
|
|
|
+ spin_lock(&ci->i_ceph_lock);
|
|
|
+ if (pool == ceph_file_layout_pg_pool(ci->i_layout)) {
|
|
|
+ ci->i_ceph_flags = flags;
|
|
|
+ } else {
|
|
|
+ pool = ceph_file_layout_pg_pool(ci->i_layout);
|
|
|
+ flags = ci->i_ceph_flags;
|
|
|
+ }
|
|
|
+ spin_unlock(&ci->i_ceph_lock);
|
|
|
+ goto check;
|
|
|
+}
|
|
|
+
|
|
|
+void ceph_pool_perm_destroy(struct ceph_mds_client *mdsc)
|
|
|
+{
|
|
|
+ struct ceph_pool_perm *perm;
|
|
|
+ struct rb_node *n;
|
|
|
+
|
|
|
+ while (!RB_EMPTY_ROOT(&mdsc->pool_perm_tree)) {
|
|
|
+ n = rb_first(&mdsc->pool_perm_tree);
|
|
|
+ perm = rb_entry(n, struct ceph_pool_perm, node);
|
|
|
+ rb_erase(n, &mdsc->pool_perm_tree);
|
|
|
+ kfree(perm);
|
|
|
+ }
|
|
|
+}
|