From nobody Sat Oct 4 01:42:13 2025 Received: from neil.brown.name (neil.brown.name [103.29.64.221]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9A4B615539A; Fri, 22 Aug 2025 00:11:25 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=103.29.64.221 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755821488; cv=none; b=bk6eX8GyiGSyYwLWgsheTW1OU22QmAByhPzGiniZbnEsiOEp/IE9jAemdqOuC1ohE51YlXFLU/CQTbzqdCo/6PCDiWYfCV8yoHpUZqOeQHV1IHA7bs1CVuWw9SVp50vnfqqFIJPRyHVfWkCHhtb7beyDtT4Yn7oECjfXKH+0haw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755821488; c=relaxed/simple; bh=cLxOonLPhU4Ns4lppuaRfso35+0UkceNyrDxB0iS+uQ=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=neSzbm0GV8pc1Z8r2RxOuehrWJpMAq6g2Qe6b5dnW3gZl1lF+srzjj1Vpm4AC4ELamhFp/jGgyuzmN6I6IlH96luo6Mp6VKHKTH3COxiUhCy1/GihrYl/RIPiqFtGcswSTZVq/PkHcSdA9aq6XkrrbU1ufGxcgA6hOOZX1OqB14= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=brown.name; spf=pass smtp.mailfrom=neil.brown.name; arc=none smtp.client-ip=103.29.64.221 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=brown.name Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=neil.brown.name Received: from 196.186.233.220.static.exetel.com.au ([220.233.186.196] helo=home.neil.brown.name) by neil.brown.name with esmtp (Exim 4.95) (envelope-from ) id 1upFND-006nbL-WB; Fri, 22 Aug 2025 00:11:17 +0000 From: NeilBrown To: Alexander Viro , Christian Brauner Cc: Jan Kara , linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v2 13/16] VFS/nfsd/cachefiles: introduce start_removing() Date: Fri, 22 Aug 2025 10:00:31 +1000 Message-ID: <20250822000818.1086550-14-neil@brown.name> X-Mailer: git-send-email 2.50.0.107.gf914562f5916.dirty In-Reply-To: <20250822000818.1086550-1-neil@brown.name> References: <20250822000818.1086550-1-neil@brown.name> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" start_removing() is similar to start_creating() to will only return a positive dentry and the expectation is that it will be removed. This is used by nfsd and cachefiles. They are changed to also use end_dirop() to terminate the action begun by start_removing(). Signed-off-by: NeilBrown --- fs/cachefiles/namei.c | 25 ++++++++++--------------- fs/namei.c | 27 +++++++++++++++++++++++++++ fs/nfsd/nfs4recover.c | 24 +++++++----------------- fs/nfsd/vfs.c | 26 ++++++++++---------------- include/linux/namei.h | 2 ++ 5 files changed, 56 insertions(+), 48 deletions(-) diff --git a/fs/cachefiles/namei.c b/fs/cachefiles/namei.c index 9af324473967..ddced50afb66 100644 --- a/fs/cachefiles/namei.c +++ b/fs/cachefiles/namei.c @@ -260,6 +260,7 @@ static int cachefiles_unlink(struct cachefiles_cache *c= ache, * - File backed objects are unlinked * - Directory backed objects are stuffed into the graveyard for userspace= to * delete + * On entry dir must be locked. It will be unlocked on exit. */ int cachefiles_bury_object(struct cachefiles_cache *cache, struct cachefiles_object *object, @@ -275,7 +276,8 @@ int cachefiles_bury_object(struct cachefiles_cache *cac= he, _enter(",'%pd','%pd'", dir, rep); =20 if (rep->d_parent !=3D dir) { - inode_unlock(d_inode(dir)); + dget(rep); + end_dirop(rep); _leave(" =3D -ESTALE"); return -ESTALE; } @@ -286,16 +288,16 @@ int cachefiles_bury_object(struct cachefiles_cache *c= ache, * by a file struct. */ ret =3D cachefiles_unlink(cache, object, dir, rep, why); - dput(rep); + end_dirop(rep); =20 - inode_unlock(d_inode(dir)); _leave(" =3D %d", ret); return ret; } =20 /* directories have to be moved to the graveyard */ _debug("move stale object to graveyard"); - inode_unlock(d_inode(dir)); + dget(rep); + end_dirop(rep); =20 try_again: /* first step is to make up a grave dentry in the graveyard */ @@ -745,26 +747,20 @@ static struct dentry *cachefiles_lookup_for_cull(stru= ct cachefiles_cache *cache, struct dentry *victim; int ret =3D -ENOENT; =20 - inode_lock_nested(d_inode(dir), I_MUTEX_PARENT); + victim =3D start_removing(&nop_mnt_idmap, dir, &QSTR(filename)); =20 - victim =3D lookup_one(&nop_mnt_idmap, &QSTR(filename), dir); if (IS_ERR(victim)) goto lookup_error; - if (d_is_negative(victim)) - goto lookup_put; if (d_inode(victim)->i_flags & S_KERNEL_FILE) goto lookup_busy; return victim; =20 lookup_busy: ret =3D -EBUSY; -lookup_put: - inode_unlock(d_inode(dir)); - dput(victim); + end_dirop(victim); return ERR_PTR(ret); =20 lookup_error: - inode_unlock(d_inode(dir)); ret =3D PTR_ERR(victim); if (ret =3D=3D -ENOENT) return ERR_PTR(-ESTALE); /* Probably got retired by the netfs */ @@ -812,18 +808,17 @@ int cachefiles_cull(struct cachefiles_cache *cache, s= truct dentry *dir, =20 ret =3D cachefiles_bury_object(cache, NULL, dir, victim, FSCACHE_OBJECT_WAS_CULLED); + dput(victim); if (ret < 0) goto error; =20 fscache_count_culled(); - dput(victim); _leave(" =3D 0"); return 0; =20 error_unlock: - inode_unlock(d_inode(dir)); + end_dirop(victim); error: - dput(victim); if (ret =3D=3D -ENOENT) return -ESTALE; /* Probably got retired by the netfs */ =20 diff --git a/fs/namei.c b/fs/namei.c index 407f3516b335..27a99c276137 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -3186,6 +3186,33 @@ struct dentry *start_creating(struct mnt_idmap *idma= p, struct dentry *parent, } EXPORT_SYMBOL(start_creating); =20 +/** + * start_removing - prepare to remove a given name with permission checking + * @idmap - idmap of the mount + * @parent - directory in which to find the name + * @name - the name to be removed + * + * Locks are taken and a lookup in performed prior to removing + * an object from a directory. Permission checking (MAY_EXEC) is performed + * against @idmap. + * + * If the name doesn't exist, an error is returned. + * + * end_dirop() should be called when removal is complete, or aborted. + * + * Returns: a positive dentry, or an error. + */ +struct dentry *start_removing(struct mnt_idmap *idmap, struct dentry *pare= nt, + struct qstr *name) +{ + int err =3D lookup_one_common(idmap, name, parent); + + if (err) + return ERR_PTR(err); + return start_dirop(parent, name, 0); +} +EXPORT_SYMBOL(start_removing); + #ifdef CONFIG_UNIX98_PTYS int path_pts(struct path *path) { diff --git a/fs/nfsd/nfs4recover.c b/fs/nfsd/nfs4recover.c index f65cf7ecea6d..8d4bb22db3b7 100644 --- a/fs/nfsd/nfs4recover.c +++ b/fs/nfsd/nfs4recover.c @@ -335,20 +335,12 @@ nfsd4_unlink_clid_dir(char *name, struct nfsd_net *nn) dprintk("NFSD: nfsd4_unlink_clid_dir. name %s\n", name); =20 dir =3D nn->rec_file->f_path.dentry; - inode_lock_nested(d_inode(dir), I_MUTEX_PARENT); - dentry =3D lookup_one(&nop_mnt_idmap, &QSTR(name), dir); - if (IS_ERR(dentry)) { - status =3D PTR_ERR(dentry); - goto out_unlock; - } - status =3D -ENOENT; - if (d_really_is_negative(dentry)) - goto out; + dentry =3D start_removing(&nop_mnt_idmap, dir, &QSTR(name)); + if (IS_ERR(dentry)) + return PTR_ERR(dentry); + status =3D vfs_rmdir(&nop_mnt_idmap, d_inode(dir), dentry); -out: - dput(dentry); -out_unlock: - inode_unlock(d_inode(dir)); + end_dirop(dentry); return status; } =20 @@ -435,16 +427,14 @@ purge_old(struct dentry *parent, char *cname, struct = nfsd_net *nn) if (nfs4_has_reclaimed_state(name, nn)) goto out_free; =20 - inode_lock_nested(d_inode(parent), I_MUTEX_PARENT); - child =3D lookup_one(&nop_mnt_idmap, &QSTR(cname), parent); + child =3D start_removing(&nop_mnt_idmap, parent, &QSTR(cname)); if (!IS_ERR(child)) { status =3D vfs_rmdir(&nop_mnt_idmap, d_inode(parent), child); if (status) printk("failed to remove client recovery directory %pd\n", child); - dput(child); + end_dirop(child); } - inode_unlock(d_inode(parent)); =20 out_free: kfree(name.data); diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index 5c809cbc05fe..5bdd068dbdd7 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -2013,7 +2013,7 @@ nfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *fh= p, int type, { struct dentry *dentry, *rdentry; struct inode *dirp; - struct inode *rinode; + struct inode *rinode =3D NULL; __be32 err; int host_err; =20 @@ -2032,24 +2032,21 @@ nfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *= fhp, int type, =20 dentry =3D fhp->fh_dentry; dirp =3D d_inode(dentry); - inode_lock_nested(dirp, I_MUTEX_PARENT); =20 - rdentry =3D lookup_one(&nop_mnt_idmap, &QSTR_LEN(fname, flen), dentry); + rdentry =3D start_removing(&nop_mnt_idmap, dentry, &QSTR_LEN(fname, flen)= ); + host_err =3D PTR_ERR(rdentry); if (IS_ERR(rdentry)) - goto out_unlock; + goto out_drop_write; =20 - if (d_really_is_negative(rdentry)) { - dput(rdentry); - host_err =3D -ENOENT; - goto out_unlock; - } - rinode =3D d_inode(rdentry); err =3D fh_fill_pre_attrs(fhp); if (err !=3D nfs_ok) goto out_unlock; =20 + rinode =3D d_inode(rdentry); + /* Prevent truncation until after locks dropped */ ihold(rinode); + if (!type) type =3D d_inode(rdentry)->i_mode & S_IFMT; =20 @@ -2071,10 +2068,10 @@ nfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *= fhp, int type, } fh_fill_post_attrs(fhp); =20 - inode_unlock(dirp); - if (!host_err) +out_unlock: + end_dirop(rdentry); + if (!err && !host_err) host_err =3D commit_metadata(fhp); - dput(rdentry); iput(rinode); /* truncate the inode here */ =20 out_drop_write: @@ -2092,9 +2089,6 @@ nfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *fh= p, int type, } out: return err !=3D nfs_ok ? err : nfserrno(host_err); -out_unlock: - inode_unlock(dirp); - goto out_drop_write; } =20 /* diff --git a/include/linux/namei.h b/include/linux/namei.h index 7371f586e318..5feb92b84d84 100644 --- a/include/linux/namei.h +++ b/include/linux/namei.h @@ -83,6 +83,8 @@ struct dentry *lookup_one_positive_unlocked(struct mnt_id= map *idmap, =20 struct dentry *start_creating(struct mnt_idmap *idmap, struct dentry *pare= nt, struct qstr *name); +struct dentry *start_removing(struct mnt_idmap *idmap, struct dentry *pare= nt, + struct qstr *name); =20 void end_dirop(struct dentry *de); void end_dirop_mkdir(struct dentry *de, struct dentry *parent); --=20 2.50.0.107.gf914562f5916.dirty