From: Wang Shilong <[email protected]>

From: Wang Shilong <[email protected]>

Currently, Filesystem use FS_IOC_FS_SETXATTR ioctl
to change project ID of file. However we don't
support ioctl on symlink files, and it is desirable
to change symlink files' project ID just like uid/gid.

This patch try to reuse existed interface fchownat(),
use group id to set project ID if flag AT_FCHOWN_PROJID
passed in.

Signed-off-by: Wang Shilong <[email protected]>
---
 fs/attr.c                        | 26 ++++++++++++++++++++++++--
 fs/open.c                        | 29 +++++++++++++++++++++++------
 fs/quota/dquot.c                 | 23 +++++++++++++++++++++++
 include/linux/fs.h               |  3 +++
 include/linux/quotaops.h         |  9 +++++++++
 include/uapi/linux/fcntl.h       |  1 +
 tools/include/uapi/linux/fcntl.h |  1 +
 7 files changed, 84 insertions(+), 8 deletions(-)

diff --git a/fs/attr.c b/fs/attr.c
index d22e8187477f..c6b1c1132c8f 100644
--- a/fs/attr.c
+++ b/fs/attr.c
@@ -85,6 +85,28 @@ int setattr_prepare(struct dentry *dentry, struct iattr 
*attr)
        if ((ia_valid & ATTR_GID) && !chgrp_ok(inode, attr->ia_gid))
                return -EPERM;
 
+       /*
+        * Project Quota ID state is only allowed to change from within the init
+        * namespace. Enforce that restriction only if we are trying to change
+        * the quota ID state. Everything else is allowed in user namespaces.
+        */
+       if ((ia_valid & ATTR_PROJID) && current_user_ns() != &init_user_ns) {
+               kprojid_t projid;
+               int rc;
+
+               /*
+                * Filesystem like xfs does't have ->get_projid hook
+                * should check permission by themselves.
+                */
+               if (inode->i_sb->dq_op->get_projid) {
+                       rc = inode->i_sb->dq_op->get_projid(inode, &projid);
+                       if (rc)
+                               return rc;
+                       if (!projid_eq(projid, attr->ia_projid))
+                               return -EPERM;
+               }
+       }
+
        /* Make sure a caller can chmod. */
        if (ia_valid & ATTR_MODE) {
                if (!inode_owner_or_capable(inode))
@@ -232,8 +254,8 @@ int notify_change(struct dentry * dentry, struct iattr * 
attr, struct inode **de
        unsigned int ia_valid = attr->ia_valid;
 
        WARN_ON_ONCE(!inode_is_locked(inode));
-
-       if (ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID | ATTR_TIMES_SET)) {
+       if (ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID | ATTR_PROJID |
+                       ATTR_TIMES_SET)) {
                if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
                        return -EPERM;
        }
diff --git a/fs/open.c b/fs/open.c
index 0285ce7dbd51..4e58c6ee23b3 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -597,7 +597,8 @@ SYSCALL_DEFINE2(chmod, const char __user *, filename, 
umode_t, mode)
        return do_fchmodat(AT_FDCWD, filename, mode);
 }
 
-static int chown_common(const struct path *path, uid_t user, gid_t group)
+static int chown_common(const struct path *path, uid_t user, gid_t group,
+                       bool set_project)
 {
        struct inode *inode = path->dentry->d_inode;
        struct inode *delegated_inode = NULL;
@@ -605,9 +606,11 @@ static int chown_common(const struct path *path, uid_t 
user, gid_t group)
        struct iattr newattrs;
        kuid_t uid;
        kgid_t gid;
+       kprojid_t projid;
 
        uid = make_kuid(current_user_ns(), user);
        gid = make_kgid(current_user_ns(), group);
+       projid = make_kprojid(current_user_ns(), (projid_t)group);
 
 retry_deleg:
        newattrs.ia_valid =  ATTR_CTIME;
@@ -620,13 +623,22 @@ static int chown_common(const struct path *path, uid_t 
user, gid_t group)
        if (group != (gid_t) -1) {
                if (!gid_valid(gid))
                        return -EINVAL;
-               newattrs.ia_valid |= ATTR_GID;
-               newattrs.ia_gid = gid;
+               if (!set_project) {
+                       newattrs.ia_valid |= ATTR_GID;
+                       newattrs.ia_gid = gid;
+               } else {
+                       newattrs.ia_valid |= ATTR_PROJID;
+                       newattrs.ia_projid = projid;
+               }
+       } else if (set_project) {
+               return -EINVAL;
        }
        if (!S_ISDIR(inode->i_mode))
                newattrs.ia_valid |=
                        ATTR_KILL_SUID | ATTR_KILL_SGID | ATTR_KILL_PRIV;
        inode_lock(inode);
+       if (set_project)
+               gid = make_kgid(current_user_ns(), (gid_t) -1);
        error = security_path_chown(path, uid, gid);
        if (!error)
                error = notify_change(path->dentry, &newattrs, 
&delegated_inode);
@@ -645,10 +657,15 @@ int do_fchownat(int dfd, const char __user *filename, 
uid_t user, gid_t group,
        struct path path;
        int error = -EINVAL;
        int lookup_flags;
+       bool set_project = false;
 
-       if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH)) != 0)
+       if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH |
+                     AT_FCHOWN_PROJID)) != 0)
                goto out;
 
+       if (flag & AT_FCHOWN_PROJID)
+               set_project = true;
+
        lookup_flags = (flag & AT_SYMLINK_NOFOLLOW) ? 0 : LOOKUP_FOLLOW;
        if (flag & AT_EMPTY_PATH)
                lookup_flags |= LOOKUP_EMPTY;
@@ -659,7 +676,7 @@ int do_fchownat(int dfd, const char __user *filename, uid_t 
user, gid_t group,
        error = mnt_want_write(path.mnt);
        if (error)
                goto out_release;
-       error = chown_common(&path, user, group);
+       error = chown_common(&path, user, group, set_project);
        mnt_drop_write(path.mnt);
 out_release:
        path_put(&path);
@@ -700,7 +717,7 @@ int ksys_fchown(unsigned int fd, uid_t user, gid_t group)
        if (error)
                goto out_fput;
        audit_file(f.file);
-       error = chown_common(&f.file->f_path, user, group);
+       error = chown_common(&f.file->f_path, user, group, false);
        mnt_drop_write_file(f.file);
 out_fput:
        fdput(f);
diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c
index fc20e06c56ba..46f39ee87312 100644
--- a/fs/quota/dquot.c
+++ b/fs/quota/dquot.c
@@ -2095,6 +2095,29 @@ int dquot_transfer(struct inode *inode, struct iattr 
*iattr)
                }
                transfer_to[GRPQUOTA] = dquot;
        }
+
+       if (iattr->ia_valid & ATTR_PROJID) {
+               kprojid_t projid;
+
+               if (!inode->i_sb->dq_op->get_projid)
+                       return -ENOTSUPP;
+
+               ret = inode->i_sb->dq_op->get_projid(inode, &projid);
+               if (ret)
+                       return ret;
+               if (!projid_eq(iattr->ia_projid, projid)) {
+                       dquot = dqget(sb, make_kqid_projid(iattr->ia_projid));
+                       if (IS_ERR(dquot)) {
+                               if (PTR_ERR(dquot) != -ESRCH) {
+                                       ret = PTR_ERR(dquot);
+                                       goto out_put;
+                               }
+                               dquot = NULL;
+                       }
+                       transfer_to[PRJQUOTA] = dquot;
+               }
+       }
+
        ret = __dquot_transfer(inode, transfer_to);
 out_put:
        dqput_all(transfer_to);
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 29d8e2cfed0e..2a878a2b90e3 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -37,6 +37,7 @@
 #include <linux/uuid.h>
 #include <linux/errseq.h>
 #include <linux/ioprio.h>
+#include <linux/projid.h>
 
 #include <asm/byteorder.h>
 #include <uapi/linux/fs.h>
@@ -191,6 +192,7 @@ typedef int (dio_iodone_t)(struct kiocb *iocb, loff_t 
offset,
 #define ATTR_OPEN      (1 << 15) /* Truncating from open(O_TRUNC) */
 #define ATTR_TIMES_SET (1 << 16)
 #define ATTR_TOUCH     (1 << 17)
+#define ATTR_PROJID    (1 << 18)
 
 /*
  * Whiteout is represented by a char device.  The following constants define 
the
@@ -213,6 +215,7 @@ struct iattr {
        umode_t         ia_mode;
        kuid_t          ia_uid;
        kgid_t          ia_gid;
+       kprojid_t       ia_projid;
        loff_t          ia_size;
        struct timespec64 ia_atime;
        struct timespec64 ia_mtime;
diff --git a/include/linux/quotaops.h b/include/linux/quotaops.h
index dc905a4ff8d7..84d3aeb43e2c 100644
--- a/include/linux/quotaops.h
+++ b/include/linux/quotaops.h
@@ -22,6 +22,15 @@ static inline struct quota_info *sb_dqopt(struct super_block 
*sb)
 /* i_mutex must being held */
 static inline bool is_quota_modification(struct inode *inode, struct iattr *ia)
 {
+       if (ia->ia_valid & ATTR_PROJID && inode->i_sb->dq_op->get_projid) {
+               kprojid_t projid;
+               int rc;
+
+               rc = inode->i_sb->dq_op->get_projid(inode, &projid);
+               if (!rc && !projid_eq(projid, ia->ia_projid))
+                       return true;
+       }
+
        return (ia->ia_valid & ATTR_SIZE && ia->ia_size != inode->i_size) ||
                (ia->ia_valid & ATTR_UID && !uid_eq(ia->ia_uid, inode->i_uid)) 
||
                (ia->ia_valid & ATTR_GID && !gid_eq(ia->ia_gid, inode->i_gid));
diff --git a/include/uapi/linux/fcntl.h b/include/uapi/linux/fcntl.h
index 6448cdd9a350..712c60d7f727 100644
--- a/include/uapi/linux/fcntl.h
+++ b/include/uapi/linux/fcntl.h
@@ -90,5 +90,6 @@
 #define AT_STATX_FORCE_SYNC    0x2000  /* - Force the attributes to be sync'd 
with the server */
 #define AT_STATX_DONT_SYNC     0x4000  /* - Don't sync attributes with the 
server */
 
+#define AT_FCHOWN_PROJID       0x40000000 /* Change project ID instead of 
group id */
 
 #endif /* _UAPI_LINUX_FCNTL_H */
diff --git a/tools/include/uapi/linux/fcntl.h b/tools/include/uapi/linux/fcntl.h
index 6448cdd9a350..712c60d7f727 100644
--- a/tools/include/uapi/linux/fcntl.h
+++ b/tools/include/uapi/linux/fcntl.h
@@ -90,5 +90,6 @@
 #define AT_STATX_FORCE_SYNC    0x2000  /* - Force the attributes to be sync'd 
with the server */
 #define AT_STATX_DONT_SYNC     0x4000  /* - Don't sync attributes with the 
server */
 
+#define AT_FCHOWN_PROJID       0x40000000 /* Change project ID instead of 
group id */
 
 #endif /* _UAPI_LINUX_FCNTL_H */
-- 
2.19.1



_______________________________________________
Linux-f2fs-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel

Reply via email to