]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blobdiff - fs/ceph/dir.c
ceph: quota: don't allow cross-quota renames
[mirror_ubuntu-bionic-kernel.git] / fs / ceph / dir.c
index 8a5266699b67657b0ea9053a03fe0ed255abb293..733f21cb8b4dcd2793c40a2ff041a7752d6ccfb6 100644 (file)
@@ -173,7 +173,7 @@ __dcache_find_get_entry(struct dentry *parent, u64 idx,
  * the MDS if/when the directory is modified).
  */
 static int __dcache_readdir(struct file *file,  struct dir_context *ctx,
-                           u32 shared_gen)
+                           int shared_gen)
 {
        struct ceph_file_info *fi = file->private_data;
        struct dentry *parent = file->f_path.dentry;
@@ -184,7 +184,7 @@ static int __dcache_readdir(struct file *file,  struct dir_context *ctx,
        u64 idx = 0;
        int err = 0;
 
-       dout("__dcache_readdir %p v%u at %llx\n", dir, shared_gen, ctx->pos);
+       dout("__dcache_readdir %p v%u at %llx\n", dir, (unsigned)shared_gen, ctx->pos);
 
        /* search start position */
        if (ctx->pos > 2) {
@@ -231,11 +231,17 @@ static int __dcache_readdir(struct file *file,  struct dir_context *ctx,
                        goto out;
                }
 
-               di = ceph_dentry(dentry);
                spin_lock(&dentry->d_lock);
-               if (di->lease_shared_gen == shared_gen &&
-                   d_really_is_positive(dentry) &&
-                   fpos_cmp(ctx->pos, di->offset) <= 0) {
+               di = ceph_dentry(dentry);
+               if (d_unhashed(dentry) ||
+                   d_really_is_negative(dentry) ||
+                   di->lease_shared_gen != shared_gen) {
+                       spin_unlock(&dentry->d_lock);
+                       dput(dentry);
+                       err = -EAGAIN;
+                       goto out;
+               }
+               if (fpos_cmp(ctx->pos, di->offset) <= 0) {
                        emit_dentry = true;
                }
                spin_unlock(&dentry->d_lock);
@@ -333,7 +339,7 @@ static int ceph_readdir(struct file *file, struct dir_context *ctx)
            ceph_snap(inode) != CEPH_SNAPDIR &&
            __ceph_dir_is_complete_ordered(ci) &&
            __ceph_caps_issued_mask(ci, CEPH_CAP_FILE_SHARED, 1)) {
-               u32 shared_gen = ci->i_shared_gen;
+               int shared_gen = atomic_read(&ci->i_shared_gen);
                spin_unlock(&ci->i_ceph_lock);
                err = __dcache_readdir(file, ctx, shared_gen);
                if (err != -EAGAIN)
@@ -750,7 +756,7 @@ static struct dentry *ceph_lookup(struct inode *dir, struct dentry *dentry,
                        spin_unlock(&ci->i_ceph_lock);
                        dout(" dir %p complete, -ENOENT\n", dir);
                        d_add(dentry, NULL);
-                       di->lease_shared_gen = ci->i_shared_gen;
+                       di->lease_shared_gen = atomic_read(&ci->i_shared_gen);
                        return NULL;
                }
                spin_unlock(&ci->i_ceph_lock);
@@ -818,6 +824,9 @@ static int ceph_mknod(struct inode *dir, struct dentry *dentry,
        if (ceph_snap(dir) != CEPH_NOSNAP)
                return -EROFS;
 
+       if (ceph_quota_is_max_files_exceeded(dir))
+               return -EDQUOT;
+
        err = ceph_pre_init_acls(dir, &mode, &acls);
        if (err < 0)
                return err;
@@ -871,6 +880,9 @@ static int ceph_symlink(struct inode *dir, struct dentry *dentry,
        if (ceph_snap(dir) != CEPH_NOSNAP)
                return -EROFS;
 
+       if (ceph_quota_is_max_files_exceeded(dir))
+               return -EDQUOT;
+
        dout("symlink in dir %p dentry %p to '%s'\n", dir, dentry, dest);
        req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_SYMLINK, USE_AUTH_MDS);
        if (IS_ERR(req)) {
@@ -920,6 +932,11 @@ static int ceph_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
                goto out;
        }
 
+       if (ceph_quota_is_max_files_exceeded(dir)) {
+               err = -EDQUOT;
+               goto out;
+       }
+
        mode |= S_IFDIR;
        err = ceph_pre_init_acls(dir, &mode, &acls);
        if (err < 0)
@@ -1079,6 +1096,11 @@ static int ceph_rename(struct inode *old_dir, struct dentry *old_dentry,
                else
                        return -EROFS;
        }
+       /* don't allow cross-quota renames */
+       if ((old_dir != new_dir) &&
+           (!ceph_quota_is_same_realm(old_dir, new_dir)))
+               return -EXDEV;
+
        dout("rename dir %p dentry %p to dir %p dentry %p\n",
             old_dir, old_dentry, new_dir, new_dentry);
        req = ceph_mdsc_create_request(mdsc, op, USE_AUTH_MDS);
@@ -1199,12 +1221,12 @@ static int dir_lease_is_valid(struct inode *dir, struct dentry *dentry)
        int valid = 0;
 
        spin_lock(&ci->i_ceph_lock);
-       if (ci->i_shared_gen == di->lease_shared_gen)
+       if (atomic_read(&ci->i_shared_gen) == di->lease_shared_gen)
                valid = __ceph_caps_issued_mask(ci, CEPH_CAP_FILE_SHARED, 1);
        spin_unlock(&ci->i_ceph_lock);
        dout("dir_lease_is_valid dir %p v%u dentry %p v%u = %d\n",
-            dir, (unsigned)ci->i_shared_gen, dentry,
-            (unsigned)di->lease_shared_gen, valid);
+            dir, (unsigned)atomic_read(&ci->i_shared_gen),
+            dentry, (unsigned)di->lease_shared_gen, valid);
        return valid;
 }
 
@@ -1332,24 +1354,37 @@ static void ceph_d_release(struct dentry *dentry)
  */
 static void ceph_d_prune(struct dentry *dentry)
 {
-       dout("ceph_d_prune %p\n", dentry);
+       struct ceph_inode_info *dir_ci;
+       struct ceph_dentry_info *di;
+
+       dout("ceph_d_prune %pd %p\n", dentry, dentry);
 
        /* do we have a valid parent? */
        if (IS_ROOT(dentry))
                return;
 
-       /* if we are not hashed, we don't affect dir's completeness */
-       if (d_unhashed(dentry))
+       /* we hold d_lock, so d_parent is stable */
+       dir_ci = ceph_inode(d_inode(dentry->d_parent));
+       if (dir_ci->i_vino.snap == CEPH_SNAPDIR)
                return;
 
-       if (ceph_snap(d_inode(dentry->d_parent)) == CEPH_SNAPDIR)
+       /* who calls d_delete() should also disable dcache readdir */
+       if (d_really_is_negative(dentry))
                return;
 
-       /*
-        * we hold d_lock, so d_parent is stable, and d_fsdata is never
-        * cleared until d_release
-        */
-       ceph_dir_clear_complete(d_inode(dentry->d_parent));
+       /* d_fsdata does not get cleared until d_release */
+       if (!d_unhashed(dentry)) {
+               __ceph_dir_clear_complete(dir_ci);
+               return;
+       }
+
+       /* Disable dcache readdir just in case that someone called d_drop()
+        * or d_invalidate(), but MDS didn't revoke CEPH_CAP_FILE_SHARED
+        * properly (dcache readdir is still enabled) */
+       di = ceph_dentry(dentry);
+       if (di->offset > 0 &&
+           di->lease_shared_gen == atomic_read(&dir_ci->i_shared_gen))
+               __ceph_dir_clear_ordered(dir_ci);
 }
 
 /*