From patchwork Wed Jul 31 04:38:35 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: yangerkun X-Patchwork-Id: 13748072 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from kanga.kvack.org (kanga.kvack.org [205.233.56.17]) by smtp.lore.kernel.org (Postfix) with ESMTP id 2E608C3DA64 for ; Wed, 31 Jul 2024 04:42:16 +0000 (UTC) Received: by kanga.kvack.org (Postfix) id 851D16B0082; Wed, 31 Jul 2024 00:42:15 -0400 (EDT) Received: by kanga.kvack.org (Postfix, from userid 40) id 802CD6B0083; Wed, 31 Jul 2024 00:42:15 -0400 (EDT) X-Delivered-To: int-list-linux-mm@kvack.org Received: by kanga.kvack.org (Postfix, from userid 63042) id 6A24A6B0085; Wed, 31 Jul 2024 00:42:15 -0400 (EDT) X-Delivered-To: linux-mm@kvack.org Received: from relay.hostedemail.com (smtprelay0017.hostedemail.com [216.40.44.17]) by kanga.kvack.org (Postfix) with ESMTP id 4C1426B0082 for ; Wed, 31 Jul 2024 00:42:15 -0400 (EDT) Received: from smtpin19.hostedemail.com (a10.router.float.18 [10.200.18.1]) by unirelay10.hostedemail.com (Postfix) with ESMTP id BAC9BC01FB for ; Wed, 31 Jul 2024 04:42:14 +0000 (UTC) X-FDA: 82398800988.19.C535235 Received: from dggsgout11.his.huawei.com (dggsgout11.his.huawei.com [45.249.212.51]) by imf30.hostedemail.com (Postfix) with ESMTP id 545A98000E for ; Wed, 31 Jul 2024 04:42:09 +0000 (UTC) Authentication-Results: imf30.hostedemail.com; dkim=none; spf=pass (imf30.hostedemail.com: domain of yangerkun@huaweicloud.com designates 45.249.212.51 as permitted sender) smtp.mailfrom=yangerkun@huaweicloud.com; dmarc=fail reason="SPF not aligned (relaxed), No valid DKIM" header.from=huawei.com (policy=quarantine) ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=hostedemail.com; s=arc-20220608; t=1722400877; h=from:from:sender:sender:reply-to:subject:subject:date:date: message-id:message-id:to:to:cc:cc:mime-version:mime-version: content-type:content-transfer-encoding:content-transfer-encoding: in-reply-to:references; bh=9cxa/g6bsXZGPgL2h9l44MgEZsOh1XTuG1PlUeqLUYk=; b=cwpj/IBYcIcC81NbhJ+TNfqld8sATojXIOGsmLtNy+tNWMqq2UXmJormrQJ0xgxlIjzLri +I11YWsQvcEXgZKmhmxBWLfHj8nj8Tsp1Q0Xi2y689iu7O8Ax8qAsaPdBGc7oIP3Rc/v3+ QoymWasWbggi3txsVBa0hGTlOqFcK7E= ARC-Seal: i=1; s=arc-20220608; d=hostedemail.com; t=1722400877; a=rsa-sha256; cv=none; b=vsDIGFVnFeAjs+RuB82aPNzsp5lbZhvMgYjr5xz0PE0DdQEeAMRZ8x6L96QmbMy16eEyzP EcSTnU/l1o2NkVcta9DMdPCl2c6r8ID51tiVaufI9Dqfz2v6CTMEJdqUW7FCu45g0EVLBs N9eWqCyTTpwhkFb4M4gbILyAsEtgcx4= ARC-Authentication-Results: i=1; imf30.hostedemail.com; dkim=none; spf=pass (imf30.hostedemail.com: domain of yangerkun@huaweicloud.com designates 45.249.212.51 as permitted sender) smtp.mailfrom=yangerkun@huaweicloud.com; dmarc=fail reason="SPF not aligned (relaxed), No valid DKIM" header.from=huawei.com (policy=quarantine) Received: from mail.maildlp.com (unknown [172.19.163.235]) by dggsgout11.his.huawei.com (SkyGuard) with ESMTP id 4WYfZL27wKz4f3jHk for ; Wed, 31 Jul 2024 12:41:46 +0800 (CST) Received: from mail02.huawei.com (unknown [10.116.40.128]) by mail.maildlp.com (Postfix) with ESMTP id 3C77D1A06D7 for ; Wed, 31 Jul 2024 12:42:00 +0800 (CST) Received: from huaweicloud.com (unknown [10.175.104.67]) by APP4 (Coremail) with SMTP id gCh0CgAXPoSTwKlmBphhAQ--.43201S4; Wed, 31 Jul 2024 12:42:00 +0800 (CST) From: yangerkun To: hch@infradead.org, chuck.lever@oracle.com, brauner@kernel.org, viro@zeniv.linux.org.uk, jack@suse.cz Cc: hughd@google.com, zlang@kernel.org, fdmanana@suse.com, linux-fsdevel@vger.kernel.org, linux-mm@kvack.org, yangerkun@huawei.com, yangerkun@huaweicloud.com Subject: [PATCH] libfs: fix infinite directory reads for offset dir Date: Wed, 31 Jul 2024 12:38:35 +0800 Message-Id: <20240731043835.1828697-1-yangerkun@huawei.com> X-Mailer: git-send-email 2.39.2 MIME-Version: 1.0 X-CM-TRANSID: gCh0CgAXPoSTwKlmBphhAQ--.43201S4 X-Coremail-Antispam: 1UD129KBjvJXoWxGF4DCw48XFykAF4fGryrtFb_yoWrCFyUpF ZxG3Z3tr1fW34jgF4kZF1DZr1F93Z3Ka1rX3s5Ww15tryaqws8KF92yr15Ka48tr95Cr1a qF45K343Xr4UCr7anT9S1TB71UUUUUDqnTZGkaVYY2UrUUUUjbIjqfuFe4nvWSU5nxnvy2 9KBjDU0xBIdaVrnRJUUUPFb4IE77IF4wAFF20E14v26r4j6ryUM7CY07I20VC2zVCF04k2 6cxKx2IYs7xG6rWj6s0DM7CIcVAFz4kK6r1j6r18M28lY4IEw2IIxxk0rwA2F7IY1VAKz4 vEj48ve4kI8wA2z4x0Y4vE2Ix0cI8IcVAFwI0_Xr0_Ar1l84ACjcxK6xIIjxv20xvEc7Cj xVAFwI0_Gr1j6F4UJwA2z4x0Y4vEx4A2jsIE14v26rxl6s0DM28EF7xvwVC2z280aVCY1x 0267AKxVW0oVCq3wAS0I0E0xvYzxvE52x082IY62kv0487Mc02F40EFcxC0VAKzVAqx4xG 6I80ewAv7VC0I7IYx2IY67AKxVWUXVWUAwAv7VC2z280aVAFwI0_Jr0_Gr1lOx8S6xCaFV Cjc4AY6r1j6r4UM4x0Y48IcxkI7VAKI48JM4x0aVACjI8F5VA0II8E6IAqYI8I648v4I1l FIxGxcIEc7CjxVA2Y2ka0xkIwI1lc7CjxVAaw2AFwI0_GFv_Wryl42xK82IYc2Ij64vIr4 1l42xK82IY64kExVAvwVAq07x20xyl4I8I3I0E4IkC6x0Yz7v_Jr0_Gr1lx2IqxVAqx4xG 67AKxVWUJVWUGwC20s026x8GjcxK67AKxVWUGVWUWwC2zVAF1VAY17CE14v26r1q6r43MI IYrxkI7VAKI48JMIIF0xvE2Ix0cI8IcVAFwI0_Jr0_JF4lIxAIcVC0I7IYx2IY6xkF7I0E 14v26r4j6F4UMIIF0xvE42xK8VAvwI8IcIk0rVWUJVWUCwCI42IY6I8E87Iv67AKxVWUJV W8JwCI42IY6I8E87Iv6xkF7I0E14v26r4j6r4UJbIYCTnIWIevJa73UjIFyTuYvjxUxX_T UUUUU X-CM-SenderInfo: 51dqwvhunx0q5kxd4v5lfo033gof0z/ X-Rspam-User: X-Rspamd-Queue-Id: 545A98000E X-Rspamd-Server: rspam04 X-Stat-Signature: 5j4ewytot6phtogb1k377ygw9qpba788 X-Rspamd-Pre-Result: action=add header; module=dmarc; Action set by DMARC X-Rspam: Yes X-HE-Tag: 1722400929-303859 X-HE-Meta: U2FsdGVkX19k23kUYfRoJYzo5YuNTXjPZowMVc0O0PruAhPppQP5iPFAgu6PaeHs1vi5nMC2KuA3qm4Vp1jviwfpZkBLbDJS49YME8APcPmWf2XtKMVaBIuWra3oBjLKoxpbsu4wUuKvE170mXmXNVew2T1bOfjTCGggSHPMSDyVKjAGrOPe/rW+tIQzmzZPVct+vqvxV7BKQ4x17kB+t5SwQzdtqubLm7ZV5cmOU4ehLbGT2eo2EoH/Y48sdmGXa1Mbt2n1pENm/cb94Axvxkha284d3xMy6pYxxQxY09aEcAtQVcyYYKmxIlzvcMPkkh2xI5PhN2DgdR8DfFAnDcUD2AuZZu08SX4wbAm7ywVnZWVEqdSUeQS/9wYNsk8uPz/ewVch7WAVImIpZGKhzvxS9PhO28dW5gVPS6e8fiBQvYOqS6SSLP/abNGZFz8IowzLf0Gd3mP3ZhrxrTtUPwU+gI/lj6oJy6wn8Ud+H+5m06qcwsb2L7rRjiMu5qbpcuH2ekXM4ruXFdUNOnf031BtWm6Iee6zYbuj5FNGsl/b2tGrg8FNlLP2rF7tdQ2K6/IFdzdRHb2HGkqbavzGPROxtLd5Ed03q5EwoTvpzZSBj5SZTBS0q6bfpGPkGEL+PD6f1f5iznVtfrt78J0HmwuGxxABU9f47zAuFbHPuf5w+B4AXlVaDC/UNNoZsn/qzuzYT7fBrt3tTTM9+Emn3drSP9bWP/eHPL/n1G20LJEEE+uLE1A66JHVzn3biGYSB5h75al1+C6555mTVuhkWAfakxCVkNTxJOKCWUOp5WsEvEPKxXf2HZubGjx2/xvRrS+395dVoV3SeoZy5+2g4E53x8FijcWYWS+cbUZ7YNSZFGPOibEeGp3uJNhip6R7USiVcjMyPeRzmvKVzWFSqoNLs+RpW908qsDiE/hNNnQMQeFFyk3HZIuOF1PaoPpbtw6FnWMgBKiLHecxkOI X5faGj9k EyZ1NUEFLLhtjM0vHhC67/bdI5uxuAkCuWX2tViP8+NOcIy9YLXdV8MomtTxOgQsHRSxeBDiUZOsBFMPmImYYC684ItQ6tfJ7+MdB3IDMabAOb9JmuTz3Mwgjm1MeluVREHwTR4kIT9AB59vXFkcGdEtRT2pNhtzCh8+gvVw3HtMyUC3wM22BR/dmuC4gnZOLP6PhPVPDzJMvP2tzA9Bqug0q53SB1VM0/8tNSFoWf0ow0V5XKoizevkLpU2Vh7Yza2gBP8E6JKTORUfTNzmz4+kTAbhi9V+b9TmT+Cln2ftDx6wL1FoQaGFv76UnMWlvk/LHME2KI5K4CY+Lzv5HjTKnF1xgwOU65uQge6Xbp+XFsEXZDxRyHe2ZDA== X-Bogosity: Ham, tests=bogofilter, spamicity=0.000000, version=1.2.4 Sender: owner-linux-mm@kvack.org Precedence: bulk X-Loop: owner-majordomo@kvack.org List-ID: List-Subscribe: List-Unsubscribe: After we switch tmpfs dir operations from simple_dir_operations to simple_offset_dir_operations, every rename happened will fill new dentry to dest dir's maple tree(&SHMEM_I(inode)->dir_offsets->mt) with a free key starting with octx->newx_offset, and then set newx_offset equals to free key + 1. This will lead to infinite readdir combine with rename happened at the same time, which fail generic/736 in xfstests(detail show as below). 1. create 5000 files(1 2 3...) under one dir 2. call readdir(man 3 readdir) once, and get one entry 3. rename(entry, "TEMPFILE"), then rename("TEMPFILE", entry) 4. loop 2~3, until readdir return nothing or we loop too many times(tmpfs break test with the second condition) We choose the same logic what commit 9b378f6ad48cf ("btrfs: fix infinite directory reads") to fix it, record the last_index when we open dir, and do not emit the entry which index >= last_index. The file->private_data now used in offset dir can use directly to do this, and we also update the last_index when we llseek the dir file. Fixes: a2e459555c5f ("shmem: stable directory offsets") Signed-off-by: yangerkun Reviewed-by: Chuck Lever Reviewed-by: Jan Kara --- fs/libfs.c | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/fs/libfs.c b/fs/libfs.c index 8aa34870449f..38b306738c00 100644 --- a/fs/libfs.c +++ b/fs/libfs.c @@ -450,6 +450,14 @@ void simple_offset_destroy(struct offset_ctx *octx) mtree_destroy(&octx->mt); } +static int offset_dir_open(struct inode *inode, struct file *file) +{ + struct offset_ctx *ctx = inode->i_op->get_offset_ctx(inode); + + file->private_data = (void *)ctx->next_offset; + return 0; +} + /** * offset_dir_llseek - Advance the read position of a directory descriptor * @file: an open directory whose position is to be updated @@ -463,6 +471,9 @@ void simple_offset_destroy(struct offset_ctx *octx) */ static loff_t offset_dir_llseek(struct file *file, loff_t offset, int whence) { + struct inode *inode = file->f_inode; + struct offset_ctx *ctx = inode->i_op->get_offset_ctx(inode); + switch (whence) { case SEEK_CUR: offset += file->f_pos; @@ -476,7 +487,7 @@ static loff_t offset_dir_llseek(struct file *file, loff_t offset, int whence) } /* In this case, ->private_data is protected by f_pos_lock */ - file->private_data = NULL; + file->private_data = (void *)ctx->next_offset; return vfs_setpos(file, offset, LONG_MAX); } @@ -507,7 +518,7 @@ static bool offset_dir_emit(struct dir_context *ctx, struct dentry *dentry) inode->i_ino, fs_umode_to_dtype(inode->i_mode)); } -static void *offset_iterate_dir(struct inode *inode, struct dir_context *ctx) +static void offset_iterate_dir(struct inode *inode, struct dir_context *ctx, long last_index) { struct offset_ctx *octx = inode->i_op->get_offset_ctx(inode); struct dentry *dentry; @@ -515,17 +526,21 @@ static void *offset_iterate_dir(struct inode *inode, struct dir_context *ctx) while (true) { dentry = offset_find_next(octx, ctx->pos); if (!dentry) - return ERR_PTR(-ENOENT); + return; + + if (dentry2offset(dentry) >= last_index) { + dput(dentry); + return; + } if (!offset_dir_emit(ctx, dentry)) { dput(dentry); - break; + return; } ctx->pos = dentry2offset(dentry) + 1; dput(dentry); } - return NULL; } /** @@ -552,22 +567,19 @@ static void *offset_iterate_dir(struct inode *inode, struct dir_context *ctx) static int offset_readdir(struct file *file, struct dir_context *ctx) { struct dentry *dir = file->f_path.dentry; + long last_index = (long)file->private_data; lockdep_assert_held(&d_inode(dir)->i_rwsem); if (!dir_emit_dots(file, ctx)) return 0; - /* In this case, ->private_data is protected by f_pos_lock */ - if (ctx->pos == DIR_OFFSET_MIN) - file->private_data = NULL; - else if (file->private_data == ERR_PTR(-ENOENT)) - return 0; - file->private_data = offset_iterate_dir(d_inode(dir), ctx); + offset_iterate_dir(d_inode(dir), ctx, last_index); return 0; } const struct file_operations simple_offset_dir_operations = { + .open = offset_dir_open, .llseek = offset_dir_llseek, .iterate_shared = offset_readdir, .read = generic_read_dir,