From patchwork Sat Jul 2 17:20:33 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Eric W. Biederman" X-Patchwork-Id: 9210965 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id E43B76075F for ; Sat, 2 Jul 2016 17:34:01 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id D7B8B28592 for ; Sat, 2 Jul 2016 17:34:01 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id CC0D328598; Sat, 2 Jul 2016 17:34:01 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.9 required=2.0 tests=BAYES_00,RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id EBCA628592 for ; Sat, 2 Jul 2016 17:34:00 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932105AbcGBRd7 (ORCPT ); Sat, 2 Jul 2016 13:33:59 -0400 Received: from out03.mta.xmission.com ([166.70.13.233]:45859 "EHLO out03.mta.xmission.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S932081AbcGBRd5 (ORCPT ); Sat, 2 Jul 2016 13:33:57 -0400 Received: from in01.mta.xmission.com ([166.70.13.51]) by out03.mta.xmission.com with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.87) (envelope-from ) id 1bJOnk-00010p-CP; Sat, 02 Jul 2016 11:33:56 -0600 Received: from 67-3-204-119.omah.qwest.net ([67.3.204.119] helo=x220.int.ebiederm.org) by in01.mta.xmission.com with esmtpsa (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.87) (envelope-from ) id 1bJOnj-0008Q5-3m; Sat, 02 Jul 2016 11:33:56 -0600 From: "Eric W. Biederman" To: Seth Forshee Cc: Linux Containers , linux-fsdevel@vger.kernel.org, Linux API , James Bottomley , Djalal Harouni , "Serge E. Hallyn" , Andy Lutomirski , Jan Kara , Jann Horn , Michael Kerrisk Date: Sat, 2 Jul 2016 12:20:33 -0500 Message-Id: <20160702172035.19568-9-ebiederm@xmission.com> X-Mailer: git-send-email 2.8.3 In-Reply-To: <20160702172035.19568-1-ebiederm@xmission.com> References: <87ziq03qnj.fsf@x220.int.ebiederm.org> <20160702172035.19568-1-ebiederm@xmission.com> X-XM-SPF: eid=1bJOnj-0008Q5-3m; ; ; mid=<20160702172035.19568-9-ebiederm@xmission.com>; ; ; hst=in01.mta.xmission.com; ; ; ip=67.3.204.119; ; ; frm=ebiederm@xmission.com; ; ; spf=neutral X-XM-AID: U2FsdGVkX1910f0nuZ8yFgx73L8DMq4waxbZgQGlmO0= X-SA-Exim-Connect-IP: 67.3.204.119 X-SA-Exim-Mail-From: ebiederm@xmission.com Subject: [PATCH review 09/11] quota: Handle quota data stored in s_user_ns. X-SA-Exim-Version: 4.2.1 (built Thu, 05 May 2016 13:38:54 -0600) X-SA-Exim-Scanned: Yes (on in01.mta.xmission.com) Sender: linux-fsdevel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-fsdevel@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP In Q_XSETQLIMIT use sb->s_user_ns to detect when we are dealing with the filesystems notion of id 0. For the dquot hashfn translate the qid into sb->s_user_ns to remove extraneous bits before hashing. When dealing with generic quota files in quota_tree.c, quota_v1.c, and quota_v2.c before writing quotas to the file encode them in sb->s_user_ns, and decode ids read from the file from sb->s_user_ns. One complication comes up in qtree_get_next_id, as it is possible in principle for id's to be present in the quota file that do not have a mapping in the filesystems user namespace. If that is the case the code is modified to skip the unmapped ids. Inspired-by: Seth Forshee Signed-off-by: "Eric W. Biederman" --- fs/quota/dquot.c | 4 ++-- fs/quota/quota.c | 2 +- fs/quota/quota_tree.c | 23 ++++++++++++++--------- fs/quota/quota_v1.c | 6 ++++-- fs/quota/quota_v2.c | 10 ++++++---- 5 files changed, 27 insertions(+), 18 deletions(-) diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c index 74706b6aa747..9b39925f34f4 100644 --- a/fs/quota/dquot.c +++ b/fs/quota/dquot.c @@ -252,7 +252,7 @@ static int __dquot_initialize(struct inode *inode, int type); static inline unsigned int hashfn(const struct super_block *sb, struct kqid qid) { - unsigned int id = from_kqid(&init_user_ns, qid); + unsigned int id = from_kqid(sb->s_user_ns, qid); int type = qid.type; unsigned long tmp; @@ -748,7 +748,7 @@ void dqput(struct dquot *dquot) if (!atomic_read(&dquot->dq_count)) { quota_error(dquot->dq_sb, "trying to free free dquot of %s %d", quotatypes[dquot->dq_id.type], - from_kqid(&init_user_ns, dquot->dq_id)); + from_kqid(dquot->dq_sb->s_user_ns, dquot->dq_id)); BUG(); } #endif diff --git a/fs/quota/quota.c b/fs/quota/quota.c index 73f6f4cf0a21..35df08ee9c97 100644 --- a/fs/quota/quota.c +++ b/fs/quota/quota.c @@ -584,7 +584,7 @@ static int quota_setxquota(struct super_block *sb, int type, qid_t id, if (!qid_has_mapping(sb->s_user_ns, qid)) return -EINVAL; /* Are we actually setting timer / warning limits for all users? */ - if (from_kqid(&init_user_ns, qid) == 0 && + if (from_kqid(sb->s_user_ns, qid) == 0 && fdq.d_fieldmask & (FS_DQ_WARNS_MASK | FS_DQ_TIMER_MASK)) { struct qc_info qinfo; int ret; diff --git a/fs/quota/quota_tree.c b/fs/quota/quota_tree.c index 0738972e8d3f..4f8f6bdc698c 100644 --- a/fs/quota/quota_tree.c +++ b/fs/quota/quota_tree.c @@ -34,7 +34,7 @@ static int __get_index(struct qtree_mem_dqinfo *info, qid_t id, int depth) static int get_index(struct qtree_mem_dqinfo *info, struct kqid qid, int depth) { - qid_t id = from_kqid(&init_user_ns, qid); + qid_t id = from_kqid(info->dqi_sb->s_user_ns, qid); return __get_index(info, id, depth); } @@ -554,7 +554,7 @@ static loff_t find_block_dqentry(struct qtree_mem_dqinfo *info, if (i == qtree_dqstr_in_blk(info)) { quota_error(dquot->dq_sb, "Quota for id %u referenced but not present", - from_kqid(&init_user_ns, dquot->dq_id)); + from_kqid(dquot->dq_sb->s_user_ns, dquot->dq_id)); ret = -EIO; goto out_buf; } else { @@ -624,7 +624,7 @@ int qtree_read_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot) if (offset < 0) quota_error(sb,"Can't read quota structure " "for id %u", - from_kqid(&init_user_ns, + from_kqid(sb->s_user_ns, dquot->dq_id)); dquot->dq_off = 0; set_bit(DQ_FAKE_B, &dquot->dq_flags); @@ -643,7 +643,7 @@ int qtree_read_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot) if (ret >= 0) ret = -EIO; quota_error(sb, "Error while reading quota structure for id %u", - from_kqid(&init_user_ns, dquot->dq_id)); + from_kqid(sb->s_user_ns, dquot->dq_id)); set_bit(DQ_FAKE_B, &dquot->dq_flags); memset(&dquot->dq_dqb, 0, sizeof(struct mem_dqblk)); kfree(ddquot); @@ -721,13 +721,18 @@ out_buf: int qtree_get_next_id(struct qtree_mem_dqinfo *info, struct kqid *qid) { - qid_t id = from_kqid(&init_user_ns, *qid); + qid_t id = from_kqid(info->dqi_sb->s_user_ns, *qid); + struct kqid next_id; int ret; - ret = find_next_id(info, &id, QT_TREEOFF, 0); - if (ret < 0) - return ret; - *qid = make_kqid(&init_user_ns, qid->type, id); + do { + ret = find_next_id(info, &id, QT_TREEOFF, 0); + if (ret < 0) + return ret; + next_id = make_kqid(info->dqi_sb->s_user_ns, qid->type, id); + } while (!qid_valid(next_id)); /* Loop until a mapped id is found */ + + *qid = next_id; return 0; } EXPORT_SYMBOL(qtree_get_next_id); diff --git a/fs/quota/quota_v1.c b/fs/quota/quota_v1.c index 8fe79beced5c..087e7743b996 100644 --- a/fs/quota/quota_v1.c +++ b/fs/quota/quota_v1.c @@ -54,6 +54,7 @@ static void v1_mem2disk_dqblk(struct v1_disk_dqblk *d, struct mem_dqblk *m) static int v1_read_dqblk(struct dquot *dquot) { + struct user_namespace *dq_user_ns = dquot->dq_sb->s_user_ns; int type = dquot->dq_id.type; struct v1_disk_dqblk dqblk; @@ -64,7 +65,7 @@ static int v1_read_dqblk(struct dquot *dquot) memset(&dqblk, 0, sizeof(struct v1_disk_dqblk)); dquot->dq_sb->s_op->quota_read(dquot->dq_sb, type, (char *)&dqblk, sizeof(struct v1_disk_dqblk), - v1_dqoff(from_kqid(&init_user_ns, dquot->dq_id))); + v1_dqoff(from_kqid(dq_user_ns, dquot->dq_id)); v1_disk2mem_dqblk(&dquot->dq_dqb, &dqblk); if (dquot->dq_dqb.dqb_bhardlimit == 0 && @@ -79,6 +80,7 @@ static int v1_read_dqblk(struct dquot *dquot) static int v1_commit_dqblk(struct dquot *dquot) { + struct user_namespace *dq_user_ns = dquot->dq_sb->s_user_ns; short type = dquot->dq_id.type; ssize_t ret; struct v1_disk_dqblk dqblk; @@ -95,7 +97,7 @@ static int v1_commit_dqblk(struct dquot *dquot) if (sb_dqopt(dquot->dq_sb)->files[type]) ret = dquot->dq_sb->s_op->quota_write(dquot->dq_sb, type, (char *)&dqblk, sizeof(struct v1_disk_dqblk), - v1_dqoff(from_kqid(&init_user_ns, dquot->dq_id))); + v1_dqoff(from_kquid(dq_user_ns, dquot->dq_id))); if (ret != sizeof(struct v1_disk_dqblk)) { quota_error(dquot->dq_sb, "dquota write failed"); if (ret >= 0) diff --git a/fs/quota/quota_v2.c b/fs/quota/quota_v2.c index ca71bf881ad1..4ecf91611a87 100644 --- a/fs/quota/quota_v2.c +++ b/fs/quota/quota_v2.c @@ -199,6 +199,7 @@ static void v2r0_disk2memdqb(struct dquot *dquot, void *dp) static void v2r0_mem2diskdqb(void *dp, struct dquot *dquot) { + struct user_namespace *dq_user_ns = dquot->dq_sb->s_user_ns; struct v2r0_disk_dqblk *d = dp; struct mem_dqblk *m = &dquot->dq_dqb; struct qtree_mem_dqinfo *info = @@ -212,7 +213,7 @@ static void v2r0_mem2diskdqb(void *dp, struct dquot *dquot) d->dqb_bsoftlimit = cpu_to_le32(v2_stoqb(m->dqb_bsoftlimit)); d->dqb_curspace = cpu_to_le64(m->dqb_curspace); d->dqb_btime = cpu_to_le64(m->dqb_btime); - d->dqb_id = cpu_to_le32(from_kqid(&init_user_ns, dquot->dq_id)); + d->dqb_id = cpu_to_le32(from_kqid(dq_user_ns, dquot->dq_qid)); if (qtree_entry_unused(info, dp)) d->dqb_itime = cpu_to_le64(1); } @@ -225,7 +226,7 @@ static int v2r0_is_id(void *dp, struct dquot *dquot) if (qtree_entry_unused(info, dp)) return 0; - return qid_eq(make_kqid(&init_user_ns, dquot->dq_id.type, + return qid_eq(make_kqid(dquot->dq_sb->s_user_ns, quot->dq_id.type, le32_to_cpu(d->dqb_id)), dquot->dq_id); } @@ -252,6 +253,7 @@ static void v2r1_disk2memdqb(struct dquot *dquot, void *dp) static void v2r1_mem2diskdqb(void *dp, struct dquot *dquot) { + struct user_namespace *dq_user_ns = dquot->dq_sb->s_user_ns; struct v2r1_disk_dqblk *d = dp; struct mem_dqblk *m = &dquot->dq_dqb; struct qtree_mem_dqinfo *info = @@ -265,7 +267,7 @@ static void v2r1_mem2diskdqb(void *dp, struct dquot *dquot) d->dqb_bsoftlimit = cpu_to_le64(v2_stoqb(m->dqb_bsoftlimit)); d->dqb_curspace = cpu_to_le64(m->dqb_curspace); d->dqb_btime = cpu_to_le64(m->dqb_btime); - d->dqb_id = cpu_to_le32(from_kqid(&init_user_ns, dquot->dq_id)); + d->dqb_id = cpu_to_le32(from_kqid(dq_user_ns, dquot->dq_id)); if (qtree_entry_unused(info, dp)) d->dqb_itime = cpu_to_le64(1); } @@ -278,7 +280,7 @@ static int v2r1_is_id(void *dp, struct dquot *dquot) if (qtree_entry_unused(info, dp)) return 0; - return qid_eq(make_kqid(&init_user_ns, dquot->dq_id.type, + return qid_eq(make_kqid(dquot->dq_sb->s_user_ns, dquot->dq_id.type, le32_to_cpu(d->dqb_id)), dquot->dq_id); }