All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC][PATCHSET v2] inode type bits fixes
       [not found] <YDqRThxQOVQO9uOx@zeniv-ca.linux.org.uk>
@ 2021-03-13  4:35 ` Al Viro
  2021-03-13  4:38   ` [PATCH v2 01/15] new helper: inode_wrong_type() Al Viro
  0 siblings, 1 reply; 21+ messages in thread
From: Al Viro @ 2021-03-13  4:35 UTC (permalink / raw)
  To: Linus Torvalds
  Cc: linux-fsdevel, Jeff Layton, David Howells, Hans de Goede,
	Mike Marshall, Joseph Qi, Bob Peterson, Steve French,
	Richard Weinberger, Dominique Martinet, Arnd Bergmann

[changes since the first variant: rebased on 5.12-rc2, new helper
for type mismatch (inode_wrong_type()), braino in vboxsf patch fixed]

Once an inode is live, we should never change the type bits of ->i_mode
(bits 12..15), ->i_op or ->i_rdev.  Most of the tree is fine in that
respect, but there are some places that get it wrong - e.g. d_revalidate
or getattr instances blindly assuming that server won't tell us that
regular file has become a directory, etc.

The series below is a result of code audit looking for such bugs.
There are two dubious places left after that - f2fs recover_inode()
might be unsafe in that respect and coda_iget() definitely is unsafe.
I don't know f2fs guts well enough to tell if we can end up with
trouble in there; coda_iget() is obviously wrong, but I'm not
sure if having it fail with ESTALE on type changes would be the
right fix.  The rest of the tree is OK after the series below.

The series lives in vfs.git #work.inode-type-fixes.  Branch is based
at 5.12-rc2 (to avoid the bisect hazard lurking at -rc1).  Individual
patches are in followups; please review.

I'm going to put the beginning of the branch into never-rebased
mode, and if somebody prefers to pull the stuff related to their
filesystem into their tree, I'd rather put that into an immutable
branch as well - there are some followups in the same general area,
and I'd like to avoid the headache with backmerges from individual
filesystems' git trees.

What's in the series:

[1/15] new helper: inode_wrong_type(); rather than open-coding the check
for type mismatch in a lot of places, provide an inlined helper and
convert the existing open-coded instances.

[2/15 and 3/15] ceph: Jeff's patches.  There are some other unpleasant
things in the same area, Jeff apparently has something for those as
well.

[4/15] afs: patch by dhowells.  Server is supposed to return just the
lower 12 bits of mode_t; unfortunately, it gets passed in 16bit field
and we shouldn't assume that the upper 4 bits will be zeroed.  If they
are not, we'll get type bits in ->i_mode screwed and it's not hard to
sanitize the server-reported value anyway.

[5/15] vboxsf: vboxsf_init_inode() is used both for initial setup of
inode and for metadata updates.  And it's too trusting, which can end
up with type of live inode being mangled.

Tell vboxsf_init_inode() if we are dealing with live inode and make it
report failure on type mismatches in that case.  There are two such call
sites - one in ->d_revalidate(), another in remount.  The latter can't
have a type mismatch, but the former needs to treat it as "stale inode,
need to redo lookup".

BTW, some of the things safe for initial setup (when nobody else might
see the struct inode instance) are not safe for live inodes - e.g.
we'd better not form ->i_mode in two steps, first setting permission bits
(with zeroes in type bits), then doing |= S_IF... to set the type bits.
Not a good idea if that thing can be observed in the intermediate state...

[6/15] orangefs: it tries to check for type mismatches; unfortunately,
it has a very odd idea of what the mismatch (and encoding of type)
looks like.  For the record, encoding (in upper 4 bits of mode_t) is
	0001	named pipe
	0010    character device
	0100    directory
	0110	block device
	1000    regular file
	1010	symlink
	1100	socket
orangefs does support regular files, directories and symlinks.	And the
check will cheerfully accept the type change from symlink to regular
files and vice versa.  It's not a bitmap...
	Fortunately, fixing the check is easy.

[7/15] ocfs2: ocfs2_inode_lock_update() and ocfs2_refresh_inode_from_lvb()
trust that type bits won't be screwed by another node in the cluster.
Odd belief, seeing that ocfs2_inode_lock_update() does some sanity
checking.  Make it verify that type does not change on a live inode
(and fail with ESTALE if it tries to).

[8/15] gfs2: gfs2_dinode_in() needs to be careful lest it fucks a live
inode over.  It can report a stale inode, so let it do so on type (and
device number) mismatches.

[9/15] cifs: if ->atomic_open() for non-exclusive create gets told that
the object already exists, it would better *NOT* assign it the mode we
would've used for file creation.  Moreover, in case when we do (creating)
open and subsequent equivalent of lookup as separate requests, there's a
possibility that another client will manage to unlink what we'd created
and mkdir something with the same name.  In that case we'd better leave
the ->i_mode alone, rather than blindly turning e.g. a directory in-core
inode into that of a regular file.

[10/15] cifs again: there's a counterpart of the same race for mkdir -
another client might've done rmdir+create between subdirectory creation
and getting its metadata.  If we run into that, don't try to mangle
i_mode, just nod politely, report success and do *not* hash the dentry.
That way the next lookup will pick whatever has ended up created in
that place.  ->mkdir() callers are ready to handle that case (e.g. NFS
has it happen on a regular basis).  ->create() and ->atomic_open() ones
are not, so we couldn't have used that approach for the previous problem.

[11/15] cifs: ->i_mode can be set by cifs_fattr_to_inode().  Some of its
callers check for type mismatches, some forget to do so.  Better have
it done in cifs_fattr_to_inode() itself.

[12/15] hostfs: in mknod() we end up with double call of
init_special_inode() - once in simulated lookup after creating the
object, once (pointlessly) before object creation.  Theoretically could
be a problem if object gets removed (by host) and then e.g. a regular
file is created in its place.  In any case, the call before do_mknod()
is useless and can be simply removed, getting rid of the problem.

[13/15] openpromfs: better set the inode up before unlocking it.  Not a
problem unless something's screwing OBP right under us (in which case
we are FUBAR), but getting that right is not hard and code is actually
cleaner that way.  Leave unlocking to the callers of openprom_iget(),
do not mess with live inode if ->lookup() finds one in icache.

[14/15] 9p: problem with inode type changes had been spotted there back
in 2011 and mostly fixed.  One case had been missed...

[15/15] spufs: only tangentially related to all this, but that's a place
where ->i_mode assignment is clearly bogus.  S_ISGID means "have child
inherit GID from parent and have it get S_ISGID as well"; it does *NOT*
mean "have all mode bits except S_ISGID cleared".

Shortlog:
Al Viro (12):
      new helper: inode_wrong_type()
      vboxsf: don't allow to change the inode type
      orangefs_inode_is_stale(): i_mode type bits do *not* form a bitmap...
      ocfs2_inode_lock_update(): make sure we don't change the type bits of i_mode
      gfs2: be careful with inode refresh
      do_cifs_create(): don't set ->i_mode of something we had not created
      cifs: have ->mkdir() handle race with another client sanely
      cifs: have cifs_fattr_to_inode() refuse to change type on live inode
      hostfs_mknod(): don't bother with init_special_inode()
      openpromfs: don't do unlock_new_inode() until the new inode is set up
      9p: missing chunk of "fs/9p: Don't update file type when updating file attributes"
      spufs: fix bogosity in S_ISGID handling

David Howells (1):
      afs: Fix updating of i_mode due to 3rd party change

Jeff Layton (2):
      ceph: fix up error handling with snapdirs
      ceph: don't allow type or device number to change on non-I_NEW inodes

Diffstat:
 arch/powerpc/platforms/cell/spufs/inode.c | 10 +----
 fs/9p/vfs_inode.c                         |  4 +-
 fs/9p/vfs_inode_dotl.c                    | 14 +++----
 fs/afs/inode.c                            |  6 +--
 fs/ceph/caps.c                            |  8 +++-
 fs/ceph/dir.c                             |  2 +
 fs/ceph/export.c                          |  9 ++--
 fs/ceph/inode.c                           | 41 ++++++++++++++++---
 fs/cifs/cifsproto.h                       |  2 +-
 fs/cifs/dir.c                             | 19 +++++----
 fs/cifs/file.c                            |  2 +-
 fs/cifs/inode.c                           | 57 ++++++++++++--------------
 fs/cifs/readdir.c                         |  4 +-
 fs/fuse/dir.c                             |  6 +--
 fs/fuse/inode.c                           |  2 +-
 fs/fuse/readdir.c                         |  2 +-
 fs/gfs2/glops.c                           | 22 ++++++----
 fs/hostfs/hostfs_kern.c                   |  1 -
 fs/nfs/inode.c                            |  6 +--
 fs/nfsd/nfsproc.c                         |  2 +-
 fs/ocfs2/dlmglue.c                        | 12 +++++-
 fs/openpromfs/inode.c                     | 67 +++++++++++++++---------------
 fs/orangefs/orangefs-utils.c              |  2 +-
 fs/overlayfs/namei.c                      |  4 +-
 fs/vboxsf/dir.c                           |  4 +-
 fs/vboxsf/super.c                         |  4 +-
 fs/vboxsf/utils.c                         | 68 +++++++++++++++++++------------
 fs/vboxsf/vfsmod.h                        |  4 +-
 include/linux/fs.h                        |  5 +++
 29 files changed, 225 insertions(+), 164 deletions(-)

^ permalink raw reply	[flat|nested] 21+ messages in thread

* [PATCH v2 01/15] new helper: inode_wrong_type()
  2021-03-13  4:35 ` [RFC][PATCHSET v2] inode type bits fixes Al Viro
@ 2021-03-13  4:38   ` Al Viro
  2021-03-13  4:38     ` [PATCH v2 02/15] ceph: fix up error handling with snapdirs Al Viro
                       ` (14 more replies)
  0 siblings, 15 replies; 21+ messages in thread
From: Al Viro @ 2021-03-13  4:38 UTC (permalink / raw)
  To: Linus Torvalds
  Cc: linux-fsdevel, Jeff Layton, David Howells, Hans de Goede,
	Mike Marshall, Joseph Qi, Bob Peterson, Steve French,
	Richard Weinberger, Dominique Martinet, Arnd Bergmann

inode_wrong_type(inode, mode) returns true if setting inode->i_mode
to given value would've changed the inode type.  We have enough of
those checks open-coded to make a helper worthwhile.

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
---
 fs/9p/vfs_inode.c      | 4 ++--
 fs/9p/vfs_inode_dotl.c | 4 ++--
 fs/cifs/inode.c        | 5 ++---
 fs/fuse/dir.c          | 6 +++---
 fs/fuse/inode.c        | 2 +-
 fs/fuse/readdir.c      | 2 +-
 fs/nfs/inode.c         | 6 +++---
 fs/nfsd/nfsproc.c      | 2 +-
 fs/overlayfs/namei.c   | 4 ++--
 include/linux/fs.h     | 5 +++++
 10 files changed, 22 insertions(+), 18 deletions(-)

diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c
index 8d97f0b45e9c..795706520b5e 100644
--- a/fs/9p/vfs_inode.c
+++ b/fs/9p/vfs_inode.c
@@ -399,7 +399,7 @@ static int v9fs_test_inode(struct inode *inode, void *data)
 
 	umode = p9mode2unixmode(v9ses, st, &rdev);
 	/* don't match inode of different type */
-	if ((inode->i_mode & S_IFMT) != (umode & S_IFMT))
+	if (inode_wrong_type(inode, umode))
 		return 0;
 
 	/* compare qid details */
@@ -1390,7 +1390,7 @@ int v9fs_refresh_inode(struct p9_fid *fid, struct inode *inode)
 	 * Don't update inode if the file type is different
 	 */
 	umode = p9mode2unixmode(v9ses, st, &rdev);
-	if ((inode->i_mode & S_IFMT) != (umode & S_IFMT))
+	if (inode_wrong_type(inode, umode))
 		goto out;
 
 	/*
diff --git a/fs/9p/vfs_inode_dotl.c b/fs/9p/vfs_inode_dotl.c
index 1dc7af046615..df0b87b05c42 100644
--- a/fs/9p/vfs_inode_dotl.c
+++ b/fs/9p/vfs_inode_dotl.c
@@ -59,7 +59,7 @@ static int v9fs_test_inode_dotl(struct inode *inode, void *data)
 	struct p9_stat_dotl *st = (struct p9_stat_dotl *)data;
 
 	/* don't match inode of different type */
-	if ((inode->i_mode & S_IFMT) != (st->st_mode & S_IFMT))
+	if (inode_wrong_type(inode, st->st_mode))
 		return 0;
 
 	if (inode->i_generation != st->st_gen)
@@ -959,7 +959,7 @@ int v9fs_refresh_inode_dotl(struct p9_fid *fid, struct inode *inode)
 	/*
 	 * Don't update inode if the file type is different
 	 */
-	if ((inode->i_mode & S_IFMT) != (st->st_mode & S_IFMT))
+	if (inode_wrong_type(inode, st->st_mode))
 		goto out;
 
 	/*
diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c
index 7c61bc9573c0..d46b36d52211 100644
--- a/fs/cifs/inode.c
+++ b/fs/cifs/inode.c
@@ -426,8 +426,7 @@ int cifs_get_inode_info_unix(struct inode **pinode,
 		}
 
 		/* if filetype is different, return error */
-		if (unlikely(((*pinode)->i_mode & S_IFMT) !=
-		    (fattr.cf_mode & S_IFMT))) {
+		if (unlikely(inode_wrong_type(*pinode, fattr.cf_mode))) {
 			CIFS_I(*pinode)->time = 0; /* force reval */
 			rc = -ESTALE;
 			goto cgiiu_exit;
@@ -1249,7 +1248,7 @@ cifs_find_inode(struct inode *inode, void *opaque)
 		return 0;
 
 	/* don't match inode of different type */
-	if ((inode->i_mode & S_IFMT) != (fattr->cf_mode & S_IFMT))
+	if (inode_wrong_type(inode, fattr->cf_mode))
 		return 0;
 
 	/* if it's not a directory or has no dentries, then flag it */
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c
index 06a18700a845..2400b98e8808 100644
--- a/fs/fuse/dir.c
+++ b/fs/fuse/dir.c
@@ -252,7 +252,7 @@ static int fuse_dentry_revalidate(struct dentry *entry, unsigned int flags)
 		if (ret == -ENOMEM)
 			goto out;
 		if (ret || fuse_invalid_attr(&outarg.attr) ||
-		    (outarg.attr.mode ^ inode->i_mode) & S_IFMT)
+		    inode_wrong_type(inode, outarg.attr.mode))
 			goto invalid;
 
 		forget_all_cached_acls(inode);
@@ -1054,7 +1054,7 @@ static int fuse_do_getattr(struct inode *inode, struct kstat *stat,
 	err = fuse_simple_request(fm, &args);
 	if (!err) {
 		if (fuse_invalid_attr(&outarg.attr) ||
-		    (inode->i_mode ^ outarg.attr.mode) & S_IFMT) {
+		    inode_wrong_type(inode, outarg.attr.mode)) {
 			fuse_make_bad(inode);
 			err = -EIO;
 		} else {
@@ -1703,7 +1703,7 @@ int fuse_do_setattr(struct dentry *dentry, struct iattr *attr,
 	}
 
 	if (fuse_invalid_attr(&outarg.attr) ||
-	    (inode->i_mode ^ outarg.attr.mode) & S_IFMT) {
+	    inode_wrong_type(inode, outarg.attr.mode)) {
 		fuse_make_bad(inode);
 		err = -EIO;
 		goto error;
diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c
index b0e18b470e91..b4b956da3851 100644
--- a/fs/fuse/inode.c
+++ b/fs/fuse/inode.c
@@ -350,7 +350,7 @@ struct inode *fuse_iget(struct super_block *sb, u64 nodeid,
 		inode->i_generation = generation;
 		fuse_init_inode(inode, attr);
 		unlock_new_inode(inode);
-	} else if ((inode->i_mode ^ attr->mode) & S_IFMT) {
+	} else if (inode_wrong_type(inode, attr->mode)) {
 		/* Inode has changed type, any I/O on the old should fail */
 		fuse_make_bad(inode);
 		iput(inode);
diff --git a/fs/fuse/readdir.c b/fs/fuse/readdir.c
index 3441ffa740f3..277f7041d55a 100644
--- a/fs/fuse/readdir.c
+++ b/fs/fuse/readdir.c
@@ -202,7 +202,7 @@ static int fuse_direntplus_link(struct file *file,
 		inode = d_inode(dentry);
 		if (!inode ||
 		    get_node_id(inode) != o->nodeid ||
-		    ((o->attr.mode ^ inode->i_mode) & S_IFMT)) {
+		    inode_wrong_type(inode, o->attr.mode)) {
 			d_invalidate(dentry);
 			dput(dentry);
 			goto retry;
diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c
index 749bbea14d99..b0da2408816d 100644
--- a/fs/nfs/inode.c
+++ b/fs/nfs/inode.c
@@ -334,7 +334,7 @@ nfs_find_actor(struct inode *inode, void *opaque)
 
 	if (NFS_FILEID(inode) != fattr->fileid)
 		return 0;
-	if ((S_IFMT & inode->i_mode) != (S_IFMT & fattr->mode))
+	if (inode_wrong_type(inode, fattr->mode))
 		return 0;
 	if (nfs_compare_fh(NFS_FH(inode), fh))
 		return 0;
@@ -1460,7 +1460,7 @@ static int nfs_check_inode_attributes(struct inode *inode, struct nfs_fattr *fat
 			return 0;
 		return -ESTALE;
 	}
-	if ((fattr->valid & NFS_ATTR_FATTR_TYPE) && (inode->i_mode & S_IFMT) != (fattr->mode & S_IFMT))
+	if ((fattr->valid & NFS_ATTR_FATTR_TYPE) && inode_wrong_type(inode, fattr->mode))
 		return -ESTALE;
 
 
@@ -1875,7 +1875,7 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
 	/*
 	 * Make sure the inode's type hasn't changed.
 	 */
-	if ((fattr->valid & NFS_ATTR_FATTR_TYPE) && (inode->i_mode & S_IFMT) != (fattr->mode & S_IFMT)) {
+	if ((fattr->valid & NFS_ATTR_FATTR_TYPE) && inode_wrong_type(inode, fattr->mode)) {
 		/*
 		* Big trouble! The inode has become a different object.
 		*/
diff --git a/fs/nfsd/nfsproc.c b/fs/nfsd/nfsproc.c
index a8d5449dd0e9..6d51687a0585 100644
--- a/fs/nfsd/nfsproc.c
+++ b/fs/nfsd/nfsproc.c
@@ -381,7 +381,7 @@ nfsd_proc_create(struct svc_rqst *rqstp)
 
 		/* Make sure the type and device matches */
 		resp->status = nfserr_exist;
-		if (inode && type != (inode->i_mode & S_IFMT))
+		if (inode && inode_wrong_type(inode, type))
 			goto out_unlock;
 	}
 
diff --git a/fs/overlayfs/namei.c b/fs/overlayfs/namei.c
index 3fe05fb5d145..1d573972ce22 100644
--- a/fs/overlayfs/namei.c
+++ b/fs/overlayfs/namei.c
@@ -371,7 +371,7 @@ int ovl_check_origin_fh(struct ovl_fs *ofs, struct ovl_fh *fh, bool connected,
 		return PTR_ERR(origin);
 
 	if (upperdentry && !ovl_is_whiteout(upperdentry) &&
-	    ((d_inode(origin)->i_mode ^ d_inode(upperdentry)->i_mode) & S_IFMT))
+	    inode_wrong_type(d_inode(upperdentry), d_inode(origin)->i_mode))
 		goto invalid;
 
 	if (!*stackp)
@@ -730,7 +730,7 @@ struct dentry *ovl_lookup_index(struct ovl_fs *ofs, struct dentry *upper,
 		index = ERR_PTR(-ESTALE);
 		goto out;
 	} else if (ovl_dentry_weird(index) || ovl_is_whiteout(index) ||
-		   ((inode->i_mode ^ d_inode(origin)->i_mode) & S_IFMT)) {
+		   inode_wrong_type(inode, d_inode(origin)->i_mode)) {
 		/*
 		 * Index should always be of the same file type as origin
 		 * except for the case of a whiteout index. A whiteout
diff --git a/include/linux/fs.h b/include/linux/fs.h
index ec8f3ddf4a6a..9e0d76a41229 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -2884,6 +2884,11 @@ static inline bool execute_ok(struct inode *inode)
 	return (inode->i_mode & S_IXUGO) || S_ISDIR(inode->i_mode);
 }
 
+static inline bool inode_wrong_type(const struct inode *inode, umode_t mode)
+{
+	return (inode->i_mode ^ mode) & S_IFMT;
+}
+
 static inline void file_start_write(struct file *file)
 {
 	if (!S_ISREG(file_inode(file)->i_mode))
-- 
2.11.0


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v2 02/15] ceph: fix up error handling with snapdirs
  2021-03-13  4:38   ` [PATCH v2 01/15] new helper: inode_wrong_type() Al Viro
@ 2021-03-13  4:38     ` Al Viro
  2021-03-13  4:38     ` [PATCH v2 03/15] ceph: don't allow type or device number to change on non-I_NEW inodes Al Viro
                       ` (13 subsequent siblings)
  14 siblings, 0 replies; 21+ messages in thread
From: Al Viro @ 2021-03-13  4:38 UTC (permalink / raw)
  To: Linus Torvalds
  Cc: linux-fsdevel, Jeff Layton, David Howells, Hans de Goede,
	Mike Marshall, Joseph Qi, Bob Peterson, Steve French,
	Richard Weinberger, Dominique Martinet, Arnd Bergmann

From: Jeff Layton <jlayton@kernel.org>

There are several warts in the snapdir error handling. The -EOPNOTSUPP
return in __snapfh_to_dentry is currently lost, and the call to
ceph_handle_snapdir is not currently checked at all.

Fix all of this up and eliminate a BUG_ON in ceph_get_snapdir. We can
handle that case with a warning and return an error.

Signed-off-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
---
 fs/ceph/dir.c    |  2 ++
 fs/ceph/export.c |  9 +++++----
 fs/ceph/inode.c  | 14 +++++++++++++-
 3 files changed, 20 insertions(+), 5 deletions(-)

diff --git a/fs/ceph/dir.c b/fs/ceph/dir.c
index 83d9358854fb..f7a790ed62c4 100644
--- a/fs/ceph/dir.c
+++ b/fs/ceph/dir.c
@@ -677,6 +677,8 @@ int ceph_handle_snapdir(struct ceph_mds_request *req,
 	    strcmp(dentry->d_name.name,
 		   fsc->mount_options->snapdir_name) == 0) {
 		struct inode *inode = ceph_get_snapdir(parent);
+		if (IS_ERR(inode))
+			return PTR_ERR(inode);
 		dout("ENOENT on snapdir %p '%pd', linking to snapdir %p\n",
 		     dentry, dentry, inode);
 		BUG_ON(!d_unhashed(dentry));
diff --git a/fs/ceph/export.c b/fs/ceph/export.c
index e088843a7734..f22156ee7306 100644
--- a/fs/ceph/export.c
+++ b/fs/ceph/export.c
@@ -248,9 +248,10 @@ static struct dentry *__snapfh_to_dentry(struct super_block *sb,
 			ihold(inode);
 		} else {
 			/* mds does not support lookup snapped inode */
-			err = -EOPNOTSUPP;
-			inode = NULL;
+			inode = ERR_PTR(-EOPNOTSUPP);
 		}
+	} else {
+		inode = ERR_PTR(-ESTALE);
 	}
 	ceph_mdsc_put_request(req);
 
@@ -261,8 +262,8 @@ static struct dentry *__snapfh_to_dentry(struct super_block *sb,
 		dout("snapfh_to_dentry %llx.%llx parent %llx hash %x err=%d",
 		      vino.ino, vino.snap, sfh->parent_ino, sfh->hash, err);
 	}
-	if (!inode)
-		return ERR_PTR(-ESTALE);
+	if (IS_ERR(inode))
+		return ERR_CAST(inode);
 	/* see comments in ceph_get_parent() */
 	return unlinked ? d_obtain_root(inode) : d_obtain_alias(inode);
 }
diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c
index 156f849f5385..5db7bf4c6a26 100644
--- a/fs/ceph/inode.c
+++ b/fs/ceph/inode.c
@@ -78,9 +78,21 @@ struct inode *ceph_get_snapdir(struct inode *parent)
 	struct inode *inode = ceph_get_inode(parent->i_sb, vino);
 	struct ceph_inode_info *ci = ceph_inode(inode);
 
-	BUG_ON(!S_ISDIR(parent->i_mode));
 	if (IS_ERR(inode))
 		return inode;
+
+	if (!S_ISDIR(parent->i_mode)) {
+		pr_warn_once("bad snapdir parent type (mode=0%o)\n",
+			     parent->i_mode);
+		return ERR_PTR(-ENOTDIR);
+	}
+
+	if (!(inode->i_state & I_NEW) && !S_ISDIR(inode->i_mode)) {
+		pr_warn_once("bad snapdir inode type (mode=0%o)\n",
+			     inode->i_mode);
+		return ERR_PTR(-ENOTDIR);
+	}
+
 	inode->i_mode = parent->i_mode;
 	inode->i_uid = parent->i_uid;
 	inode->i_gid = parent->i_gid;
-- 
2.11.0


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v2 03/15] ceph: don't allow type or device number to change on non-I_NEW inodes
  2021-03-13  4:38   ` [PATCH v2 01/15] new helper: inode_wrong_type() Al Viro
  2021-03-13  4:38     ` [PATCH v2 02/15] ceph: fix up error handling with snapdirs Al Viro
@ 2021-03-13  4:38     ` Al Viro
  2021-03-13  4:38     ` [PATCH v2 04/15] afs: Fix updating of i_mode due to 3rd party change Al Viro
                       ` (12 subsequent siblings)
  14 siblings, 0 replies; 21+ messages in thread
From: Al Viro @ 2021-03-13  4:38 UTC (permalink / raw)
  To: Linus Torvalds
  Cc: linux-fsdevel, Jeff Layton, David Howells, Hans de Goede,
	Mike Marshall, Joseph Qi, Bob Peterson, Steve French,
	Richard Weinberger, Dominique Martinet, Arnd Bergmann

From: Jeff Layton <jlayton@kernel.org>

Al pointed out that a malicious or broken MDS could change the type or
device number of a given inode number. It may also be possible for the
MDS to reuse an old inode number.

Ensure that we never allow fill_inode to change the type part of the
i_mode or the i_rdev unless I_NEW is set. Throw warnings if the MDS ever
changes these on us mid-stream, and return an error.

Don't set i_rdev directly, and rely on init_special_inode to do it.
Also, fix up error handling in the callers of ceph_get_inode.

In handle_cap_grant, check for and warn if the inode type changes, and
only overwrite the mode if it didn't.

Reported-by: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
---
 fs/ceph/caps.c  |  8 +++++++-
 fs/ceph/inode.c | 27 +++++++++++++++++++++++----
 2 files changed, 30 insertions(+), 5 deletions(-)

diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c
index 570731c4d019..3c03fa37cac4 100644
--- a/fs/ceph/caps.c
+++ b/fs/ceph/caps.c
@@ -3358,7 +3358,13 @@ static void handle_cap_grant(struct inode *inode,
 
 	if ((newcaps & CEPH_CAP_AUTH_SHARED) &&
 	    (extra_info->issued & CEPH_CAP_AUTH_EXCL) == 0) {
-		inode->i_mode = le32_to_cpu(grant->mode);
+		umode_t mode = le32_to_cpu(grant->mode);
+
+		if (inode_wrong_type(inode, mode))
+			pr_warn_once("inode type changed! (ino %llx.%llx is 0%o, mds says 0%o)\n",
+				     ceph_vinop(inode), inode->i_mode, mode);
+		else
+			inode->i_mode = mode;
 		inode->i_uid = make_kuid(&init_user_ns, le32_to_cpu(grant->uid));
 		inode->i_gid = make_kgid(&init_user_ns, le32_to_cpu(grant->gid));
 		ci->i_btime = extra_info->btime;
diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c
index 5db7bf4c6a26..689e3ffd29d7 100644
--- a/fs/ceph/inode.c
+++ b/fs/ceph/inode.c
@@ -769,11 +769,32 @@ int ceph_fill_inode(struct inode *inode, struct page *locked_page,
 	bool queue_trunc = false;
 	bool new_version = false;
 	bool fill_inline = false;
+	umode_t mode = le32_to_cpu(info->mode);
+	dev_t rdev = le32_to_cpu(info->rdev);
 
 	dout("%s %p ino %llx.%llx v %llu had %llu\n", __func__,
 	     inode, ceph_vinop(inode), le64_to_cpu(info->version),
 	     ci->i_version);
 
+	/* Once I_NEW is cleared, we can't change type or dev numbers */
+	if (inode->i_state & I_NEW) {
+		inode->i_mode = mode;
+	} else {
+		if (inode_wrong_type(inode, mode)) {
+			pr_warn_once("inode type changed! (ino %llx.%llx is 0%o, mds says 0%o)\n",
+				     ceph_vinop(inode), inode->i_mode, mode);
+			return -ESTALE;
+		}
+
+		if ((S_ISCHR(mode) || S_ISBLK(mode)) && inode->i_rdev != rdev) {
+			pr_warn_once("dev inode rdev changed! (ino %llx.%llx is %u:%u, mds says %u:%u)\n",
+				     ceph_vinop(inode), MAJOR(inode->i_rdev),
+				     MINOR(inode->i_rdev), MAJOR(rdev),
+				     MINOR(rdev));
+			return -ESTALE;
+		}
+	}
+
 	info_caps = le32_to_cpu(info->cap.caps);
 
 	/* prealloc new cap struct */
@@ -827,8 +848,6 @@ int ceph_fill_inode(struct inode *inode, struct page *locked_page,
 	issued |= __ceph_caps_dirty(ci);
 	new_issued = ~issued & info_caps;
 
-	/* update inode */
-	inode->i_rdev = le32_to_cpu(info->rdev);
 	/* directories have fl_stripe_unit set to zero */
 	if (le32_to_cpu(info->layout.fl_stripe_unit))
 		inode->i_blkbits =
@@ -840,7 +859,7 @@ int ceph_fill_inode(struct inode *inode, struct page *locked_page,
 
 	if ((new_version || (new_issued & CEPH_CAP_AUTH_SHARED)) &&
 	    (issued & CEPH_CAP_AUTH_EXCL) == 0) {
-		inode->i_mode = le32_to_cpu(info->mode);
+		inode->i_mode = mode;
 		inode->i_uid = make_kuid(&init_user_ns, le32_to_cpu(info->uid));
 		inode->i_gid = make_kgid(&init_user_ns, le32_to_cpu(info->gid));
 		dout("%p mode 0%o uid.gid %d.%d\n", inode, inode->i_mode,
@@ -938,7 +957,7 @@ int ceph_fill_inode(struct inode *inode, struct page *locked_page,
 	case S_IFCHR:
 	case S_IFSOCK:
 		inode->i_blkbits = PAGE_SHIFT;
-		init_special_inode(inode, inode->i_mode, inode->i_rdev);
+		init_special_inode(inode, inode->i_mode, rdev);
 		inode->i_op = &ceph_file_iops;
 		break;
 	case S_IFREG:
-- 
2.11.0


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v2 04/15] afs: Fix updating of i_mode due to 3rd party change
  2021-03-13  4:38   ` [PATCH v2 01/15] new helper: inode_wrong_type() Al Viro
  2021-03-13  4:38     ` [PATCH v2 02/15] ceph: fix up error handling with snapdirs Al Viro
  2021-03-13  4:38     ` [PATCH v2 03/15] ceph: don't allow type or device number to change on non-I_NEW inodes Al Viro
@ 2021-03-13  4:38     ` Al Viro
  2021-03-13  4:38     ` [PATCH v2 05/15] vboxsf: don't allow to change the inode type Al Viro
                       ` (11 subsequent siblings)
  14 siblings, 0 replies; 21+ messages in thread
From: Al Viro @ 2021-03-13  4:38 UTC (permalink / raw)
  To: Linus Torvalds
  Cc: linux-fsdevel, Jeff Layton, David Howells, Hans de Goede,
	Mike Marshall, Joseph Qi, Bob Peterson, Steve French,
	Richard Weinberger, Dominique Martinet, Arnd Bergmann

From: David Howells <dhowells@redhat.com>

Fix afs_apply_status() to mask off the irrelevant bits from status->mode
when OR'ing them into i_mode.  This can happen when a 3rd party chmod
occurs.

Also fix afs_inode_init_from_status() to mask off the mode bits when
initialising i_mode.

Fixes: 260a980317da ("[AFS]: Add "directory write" support.")
Reported-by: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: David Howells <dhowells@redhat.com>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
---
 fs/afs/inode.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/fs/afs/inode.c b/fs/afs/inode.c
index 1156b2df28d3..9f83e671c785 100644
--- a/fs/afs/inode.c
+++ b/fs/afs/inode.c
@@ -103,13 +103,13 @@ static int afs_inode_init_from_status(struct afs_operation *op,
 
 	switch (status->type) {
 	case AFS_FTYPE_FILE:
-		inode->i_mode	= S_IFREG | status->mode;
+		inode->i_mode	= S_IFREG | (status->mode & S_IALLUGO);
 		inode->i_op	= &afs_file_inode_operations;
 		inode->i_fop	= &afs_file_operations;
 		inode->i_mapping->a_ops	= &afs_fs_aops;
 		break;
 	case AFS_FTYPE_DIR:
-		inode->i_mode	= S_IFDIR | status->mode;
+		inode->i_mode	= S_IFDIR |  (status->mode & S_IALLUGO);
 		inode->i_op	= &afs_dir_inode_operations;
 		inode->i_fop	= &afs_dir_file_operations;
 		inode->i_mapping->a_ops	= &afs_dir_aops;
@@ -199,7 +199,7 @@ static void afs_apply_status(struct afs_operation *op,
 	if (status->mode != vnode->status.mode) {
 		mode = inode->i_mode;
 		mode &= ~S_IALLUGO;
-		mode |= status->mode;
+		mode |= status->mode & S_IALLUGO;
 		WRITE_ONCE(inode->i_mode, mode);
 	}
 
-- 
2.11.0


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v2 05/15] vboxsf: don't allow to change the inode type
  2021-03-13  4:38   ` [PATCH v2 01/15] new helper: inode_wrong_type() Al Viro
                       ` (2 preceding siblings ...)
  2021-03-13  4:38     ` [PATCH v2 04/15] afs: Fix updating of i_mode due to 3rd party change Al Viro
@ 2021-03-13  4:38     ` Al Viro
  2021-03-13  4:38     ` [PATCH v2 06/15] orangefs_inode_is_stale(): i_mode type bits do *not* form a bitmap Al Viro
                       ` (10 subsequent siblings)
  14 siblings, 0 replies; 21+ messages in thread
From: Al Viro @ 2021-03-13  4:38 UTC (permalink / raw)
  To: Linus Torvalds
  Cc: linux-fsdevel, Jeff Layton, David Howells, Hans de Goede,
	Mike Marshall, Joseph Qi, Bob Peterson, Steve French,
	Richard Weinberger, Dominique Martinet, Arnd Bergmann

vboxsf_init_inode() is used both for initial setup of inode and for metadata
updates.  Tell it whether we are updating a live inode or setting up a new
instance and have it refuse to change type in the former case.

[fixed the braino caught by Hans de Goede <hdegoede@redhat.com>]

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
---
 fs/vboxsf/dir.c    |  4 ++--
 fs/vboxsf/super.c  |  4 ++--
 fs/vboxsf/utils.c  | 68 ++++++++++++++++++++++++++++++++++--------------------
 fs/vboxsf/vfsmod.h |  4 ++--
 4 files changed, 49 insertions(+), 31 deletions(-)

diff --git a/fs/vboxsf/dir.c b/fs/vboxsf/dir.c
index 7aee0ec63ade..eac6788fc6cf 100644
--- a/fs/vboxsf/dir.c
+++ b/fs/vboxsf/dir.c
@@ -225,7 +225,7 @@ static struct dentry *vboxsf_dir_lookup(struct inode *parent,
 	} else {
 		inode = vboxsf_new_inode(parent->i_sb);
 		if (!IS_ERR(inode))
-			vboxsf_init_inode(sbi, inode, &fsinfo);
+			vboxsf_init_inode(sbi, inode, &fsinfo, false);
 	}
 
 	return d_splice_alias(inode, dentry);
@@ -245,7 +245,7 @@ static int vboxsf_dir_instantiate(struct inode *parent, struct dentry *dentry,
 	sf_i = VBOXSF_I(inode);
 	/* The host may have given us different attr then requested */
 	sf_i->force_restat = 1;
-	vboxsf_init_inode(sbi, inode, info);
+	vboxsf_init_inode(sbi, inode, info, false);
 
 	d_instantiate(dentry, inode);
 
diff --git a/fs/vboxsf/super.c b/fs/vboxsf/super.c
index d7816c01a4f6..4f5e59f06284 100644
--- a/fs/vboxsf/super.c
+++ b/fs/vboxsf/super.c
@@ -207,7 +207,7 @@ static int vboxsf_fill_super(struct super_block *sb, struct fs_context *fc)
 		err = -ENOMEM;
 		goto fail_unmap;
 	}
-	vboxsf_init_inode(sbi, iroot, &sbi->root_info);
+	vboxsf_init_inode(sbi, iroot, &sbi->root_info, false);
 	unlock_new_inode(iroot);
 
 	droot = d_make_root(iroot);
@@ -418,7 +418,7 @@ static int vboxsf_reconfigure(struct fs_context *fc)
 
 	/* Apply changed options to the root inode */
 	sbi->o = ctx->o;
-	vboxsf_init_inode(sbi, iroot, &sbi->root_info);
+	vboxsf_init_inode(sbi, iroot, &sbi->root_info, true);
 
 	return 0;
 }
diff --git a/fs/vboxsf/utils.c b/fs/vboxsf/utils.c
index 3b847e3fba24..aec2ebf7d25a 100644
--- a/fs/vboxsf/utils.c
+++ b/fs/vboxsf/utils.c
@@ -45,12 +45,12 @@ struct inode *vboxsf_new_inode(struct super_block *sb)
 }
 
 /* set [inode] attributes based on [info], uid/gid based on [sbi] */
-void vboxsf_init_inode(struct vboxsf_sbi *sbi, struct inode *inode,
-		       const struct shfl_fsobjinfo *info)
+int vboxsf_init_inode(struct vboxsf_sbi *sbi, struct inode *inode,
+		       const struct shfl_fsobjinfo *info, bool reinit)
 {
 	const struct shfl_fsobjattr *attr;
 	s64 allocated;
-	int mode;
+	umode_t mode;
 
 	attr = &info->attr;
 
@@ -75,29 +75,44 @@ void vboxsf_init_inode(struct vboxsf_sbi *sbi, struct inode *inode,
 	inode->i_mapping->a_ops = &vboxsf_reg_aops;
 
 	if (SHFL_IS_DIRECTORY(attr->mode)) {
-		inode->i_mode = sbi->o.dmode_set ? sbi->o.dmode : mode;
-		inode->i_mode &= ~sbi->o.dmask;
-		inode->i_mode |= S_IFDIR;
-		inode->i_op = &vboxsf_dir_iops;
-		inode->i_fop = &vboxsf_dir_fops;
-		/*
-		 * XXX: this probably should be set to the number of entries
-		 * in the directory plus two (. ..)
-		 */
-		set_nlink(inode, 1);
+		if (sbi->o.dmode_set)
+			mode = sbi->o.dmode;
+		mode &= ~sbi->o.dmask;
+		mode |= S_IFDIR;
+		if (!reinit) {
+			inode->i_op = &vboxsf_dir_iops;
+			inode->i_fop = &vboxsf_dir_fops;
+			/*
+			 * XXX: this probably should be set to the number of entries
+			 * in the directory plus two (. ..)
+			 */
+			set_nlink(inode, 1);
+		} else if (!S_ISDIR(inode->i_mode))
+			return -ESTALE;
+		inode->i_mode = mode;
 	} else if (SHFL_IS_SYMLINK(attr->mode)) {
-		inode->i_mode = sbi->o.fmode_set ? sbi->o.fmode : mode;
-		inode->i_mode &= ~sbi->o.fmask;
-		inode->i_mode |= S_IFLNK;
-		inode->i_op = &vboxsf_lnk_iops;
-		set_nlink(inode, 1);
+		if (sbi->o.fmode_set)
+			mode = sbi->o.fmode;
+		mode &= ~sbi->o.fmask;
+		mode |= S_IFLNK;
+		if (!reinit) {
+			inode->i_op = &vboxsf_lnk_iops;
+			set_nlink(inode, 1);
+		} else if (!S_ISLNK(inode->i_mode))
+			return -ESTALE;
+		inode->i_mode = mode;
 	} else {
-		inode->i_mode = sbi->o.fmode_set ? sbi->o.fmode : mode;
-		inode->i_mode &= ~sbi->o.fmask;
-		inode->i_mode |= S_IFREG;
-		inode->i_op = &vboxsf_reg_iops;
-		inode->i_fop = &vboxsf_reg_fops;
-		set_nlink(inode, 1);
+		if (sbi->o.fmode_set)
+			mode = sbi->o.fmode;
+		mode &= ~sbi->o.fmask;
+		mode |= S_IFREG;
+		if (!reinit) {
+			inode->i_op = &vboxsf_reg_iops;
+			inode->i_fop = &vboxsf_reg_fops;
+			set_nlink(inode, 1);
+		} else if (!S_ISREG(inode->i_mode))
+			return -ESTALE;
+		inode->i_mode = mode;
 	}
 
 	inode->i_uid = sbi->o.uid;
@@ -116,6 +131,7 @@ void vboxsf_init_inode(struct vboxsf_sbi *sbi, struct inode *inode,
 				 info->change_time.ns_relative_to_unix_epoch);
 	inode->i_mtime = ns_to_timespec64(
 			   info->modification_time.ns_relative_to_unix_epoch);
+	return 0;
 }
 
 int vboxsf_create_at_dentry(struct dentry *dentry,
@@ -199,7 +215,9 @@ int vboxsf_inode_revalidate(struct dentry *dentry)
 
 	dentry->d_time = jiffies;
 	sf_i->force_restat = 0;
-	vboxsf_init_inode(sbi, inode, &info);
+	err = vboxsf_init_inode(sbi, inode, &info, true);
+	if (err)
+		return err;
 
 	/*
 	 * If the file was changed on the host side we need to invalidate the
diff --git a/fs/vboxsf/vfsmod.h b/fs/vboxsf/vfsmod.h
index 760524e78c88..6a7a9cedebc6 100644
--- a/fs/vboxsf/vfsmod.h
+++ b/fs/vboxsf/vfsmod.h
@@ -82,8 +82,8 @@ extern const struct dentry_operations vboxsf_dentry_ops;
 
 /* from utils.c */
 struct inode *vboxsf_new_inode(struct super_block *sb);
-void vboxsf_init_inode(struct vboxsf_sbi *sbi, struct inode *inode,
-		       const struct shfl_fsobjinfo *info);
+int vboxsf_init_inode(struct vboxsf_sbi *sbi, struct inode *inode,
+		       const struct shfl_fsobjinfo *info, bool reinit);
 int vboxsf_create_at_dentry(struct dentry *dentry,
 			    struct shfl_createparms *params);
 int vboxsf_stat(struct vboxsf_sbi *sbi, struct shfl_string *path,
-- 
2.11.0


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v2 06/15] orangefs_inode_is_stale(): i_mode type bits do *not* form a bitmap...
  2021-03-13  4:38   ` [PATCH v2 01/15] new helper: inode_wrong_type() Al Viro
                       ` (3 preceding siblings ...)
  2021-03-13  4:38     ` [PATCH v2 05/15] vboxsf: don't allow to change the inode type Al Viro
@ 2021-03-13  4:38     ` Al Viro
  2021-03-13  4:38     ` [PATCH v2 07/15] ocfs2_inode_lock_update(): make sure we don't change the type bits of i_mode Al Viro
                       ` (9 subsequent siblings)
  14 siblings, 0 replies; 21+ messages in thread
From: Al Viro @ 2021-03-13  4:38 UTC (permalink / raw)
  To: Linus Torvalds
  Cc: linux-fsdevel, Jeff Layton, David Howells, Hans de Goede,
	Mike Marshall, Joseph Qi, Bob Peterson, Steve French,
	Richard Weinberger, Dominique Martinet, Arnd Bergmann

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
---
 fs/orangefs/orangefs-utils.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/fs/orangefs/orangefs-utils.c b/fs/orangefs/orangefs-utils.c
index d4b7ae763186..46b7dcff18ac 100644
--- a/fs/orangefs/orangefs-utils.c
+++ b/fs/orangefs/orangefs-utils.c
@@ -221,7 +221,7 @@ static int orangefs_inode_is_stale(struct inode *inode,
 	 * If the inode type or symlink target have changed then this
 	 * inode is stale.
 	 */
-	if (type == -1 || !(inode->i_mode & type)) {
+	if (type == -1 || inode_wrong_type(inode, type)) {
 		orangefs_make_bad_inode(inode);
 		return 1;
 	}
-- 
2.11.0


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v2 07/15] ocfs2_inode_lock_update(): make sure we don't change the type bits of i_mode
  2021-03-13  4:38   ` [PATCH v2 01/15] new helper: inode_wrong_type() Al Viro
                       ` (4 preceding siblings ...)
  2021-03-13  4:38     ` [PATCH v2 06/15] orangefs_inode_is_stale(): i_mode type bits do *not* form a bitmap Al Viro
@ 2021-03-13  4:38     ` Al Viro
  2021-03-13  4:38     ` [PATCH v2 08/15] gfs2: be careful with inode refresh Al Viro
                       ` (8 subsequent siblings)
  14 siblings, 0 replies; 21+ messages in thread
From: Al Viro @ 2021-03-13  4:38 UTC (permalink / raw)
  To: Linus Torvalds
  Cc: linux-fsdevel, Jeff Layton, David Howells, Hans de Goede,
	Mike Marshall, Joseph Qi, Bob Peterson, Steve French,
	Richard Weinberger, Dominique Martinet, Arnd Bergmann

... even if another node has gone insane.  Fail with -ESTALE if it detects
an attempt to change the type bits.

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
---
 fs/ocfs2/dlmglue.c | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/fs/ocfs2/dlmglue.c b/fs/ocfs2/dlmglue.c
index 8e3a369086db..0fbe8bf7190f 100644
--- a/fs/ocfs2/dlmglue.c
+++ b/fs/ocfs2/dlmglue.c
@@ -2204,7 +2204,7 @@ static void ocfs2_unpack_timespec(struct timespec64 *spec,
 	spec->tv_nsec = packed_time & OCFS2_NSEC_MASK;
 }
 
-static void ocfs2_refresh_inode_from_lvb(struct inode *inode)
+static int ocfs2_refresh_inode_from_lvb(struct inode *inode)
 {
 	struct ocfs2_inode_info *oi = OCFS2_I(inode);
 	struct ocfs2_lock_res *lockres = &oi->ip_inode_lockres;
@@ -2213,6 +2213,8 @@ static void ocfs2_refresh_inode_from_lvb(struct inode *inode)
 	mlog_meta_lvb(0, lockres);
 
 	lvb = ocfs2_dlm_lvb(&lockres->l_lksb);
+	if (inode_wrong_type(inode, be16_to_cpu(lvb->lvb_imode)))
+		return -ESTALE;
 
 	/* We're safe here without the lockres lock... */
 	spin_lock(&oi->ip_lock);
@@ -2240,6 +2242,7 @@ static void ocfs2_refresh_inode_from_lvb(struct inode *inode)
 	ocfs2_unpack_timespec(&inode->i_ctime,
 			      be64_to_cpu(lvb->lvb_ictime_packed));
 	spin_unlock(&oi->ip_lock);
+	return 0;
 }
 
 static inline int ocfs2_meta_lvb_is_trustable(struct inode *inode,
@@ -2342,7 +2345,8 @@ static int ocfs2_inode_lock_update(struct inode *inode,
 	if (ocfs2_meta_lvb_is_trustable(inode, lockres)) {
 		mlog(0, "Trusting LVB on inode %llu\n",
 		     (unsigned long long)oi->ip_blkno);
-		ocfs2_refresh_inode_from_lvb(inode);
+		status = ocfs2_refresh_inode_from_lvb(inode);
+		goto bail_refresh;
 	} else {
 		/* Boo, we have to go to disk. */
 		/* read bh, cast, ocfs2_refresh_inode */
@@ -2352,6 +2356,10 @@ static int ocfs2_inode_lock_update(struct inode *inode,
 			goto bail_refresh;
 		}
 		fe = (struct ocfs2_dinode *) (*bh)->b_data;
+		if (inode_wrong_type(inode, le16_to_cpu(fe->i_mode))) {
+			status = -ESTALE;
+			goto bail_refresh;
+		}
 
 		/* This is a good chance to make sure we're not
 		 * locking an invalid object.  ocfs2_read_inode_block()
-- 
2.11.0


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v2 08/15] gfs2: be careful with inode refresh
  2021-03-13  4:38   ` [PATCH v2 01/15] new helper: inode_wrong_type() Al Viro
                       ` (5 preceding siblings ...)
  2021-03-13  4:38     ` [PATCH v2 07/15] ocfs2_inode_lock_update(): make sure we don't change the type bits of i_mode Al Viro
@ 2021-03-13  4:38     ` Al Viro
  2021-03-13  4:38     ` [PATCH v2 09/15] do_cifs_create(): don't set ->i_mode of something we had not created Al Viro
                       ` (7 subsequent siblings)
  14 siblings, 0 replies; 21+ messages in thread
From: Al Viro @ 2021-03-13  4:38 UTC (permalink / raw)
  To: Linus Torvalds
  Cc: linux-fsdevel, Jeff Layton, David Howells, Hans de Goede,
	Mike Marshall, Joseph Qi, Bob Peterson, Steve French,
	Richard Weinberger, Dominique Martinet, Arnd Bergmann

1) gfs2_dinode_in() should *not* touch ->i_rdev on live inodes; even
"zero and immediately reread the same value from dinode" is broken -
have it overlap with ->release() of char device and you can get all
kinds of bogus behaviour.

2) mismatch on inode type on live inodes should be treated as fs
corruption rather than blindly setting ->i_mode.

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
---
 fs/gfs2/glops.c | 22 ++++++++++++++--------
 1 file changed, 14 insertions(+), 8 deletions(-)

diff --git a/fs/gfs2/glops.c b/fs/gfs2/glops.c
index 8e32d569c8bf..ef0b583c3417 100644
--- a/fs/gfs2/glops.c
+++ b/fs/gfs2/glops.c
@@ -394,18 +394,24 @@ static int gfs2_dinode_in(struct gfs2_inode *ip, const void *buf)
 	const struct gfs2_dinode *str = buf;
 	struct timespec64 atime;
 	u16 height, depth;
+	umode_t mode = be32_to_cpu(str->di_mode);
+	bool is_new = ip->i_inode.i_flags & I_NEW;
 
 	if (unlikely(ip->i_no_addr != be64_to_cpu(str->di_num.no_addr)))
 		goto corrupt;
+	if (unlikely(!is_new && inode_wrong_type(&ip->i_inode, mode)))
+		goto corrupt;
 	ip->i_no_formal_ino = be64_to_cpu(str->di_num.no_formal_ino);
-	ip->i_inode.i_mode = be32_to_cpu(str->di_mode);
-	ip->i_inode.i_rdev = 0;
-	switch (ip->i_inode.i_mode & S_IFMT) {
-	case S_IFBLK:
-	case S_IFCHR:
-		ip->i_inode.i_rdev = MKDEV(be32_to_cpu(str->di_major),
-					   be32_to_cpu(str->di_minor));
-		break;
+	ip->i_inode.i_mode = mode;
+	if (is_new) {
+		ip->i_inode.i_rdev = 0;
+		switch (mode & S_IFMT) {
+		case S_IFBLK:
+		case S_IFCHR:
+			ip->i_inode.i_rdev = MKDEV(be32_to_cpu(str->di_major),
+						   be32_to_cpu(str->di_minor));
+			break;
+		}
 	}
 
 	i_uid_write(&ip->i_inode, be32_to_cpu(str->di_uid));
-- 
2.11.0


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v2 09/15] do_cifs_create(): don't set ->i_mode of something we had not created
  2021-03-13  4:38   ` [PATCH v2 01/15] new helper: inode_wrong_type() Al Viro
                       ` (6 preceding siblings ...)
  2021-03-13  4:38     ` [PATCH v2 08/15] gfs2: be careful with inode refresh Al Viro
@ 2021-03-13  4:38     ` Al Viro
  2021-03-15 17:12       ` Jeff Layton
  2021-03-13  4:38     ` [PATCH v2 10/15] cifs: have ->mkdir() handle race with another client sanely Al Viro
                       ` (6 subsequent siblings)
  14 siblings, 1 reply; 21+ messages in thread
From: Al Viro @ 2021-03-13  4:38 UTC (permalink / raw)
  To: Linus Torvalds
  Cc: linux-fsdevel, Jeff Layton, David Howells, Hans de Goede,
	Mike Marshall, Joseph Qi, Bob Peterson, Steve French,
	Richard Weinberger, Dominique Martinet, Arnd Bergmann

If the file had existed before we'd called ->atomic_open() (without
O_EXCL, that is), we have no more business setting ->i_mode than
we would setting ->i_uid or ->i_gid.  We also have no business
doing either if another client has managed to get unlink+mkdir
between ->open() and cifs_inode_get_info().

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
---
 fs/cifs/dir.c | 19 ++++++++++---------
 1 file changed, 10 insertions(+), 9 deletions(-)

diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c
index a3fb81e0ba17..9d7ae93c8af7 100644
--- a/fs/cifs/dir.c
+++ b/fs/cifs/dir.c
@@ -418,15 +418,16 @@ cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned int xid,
 		if (newinode) {
 			if (server->ops->set_lease_key)
 				server->ops->set_lease_key(newinode, fid);
-			if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM)
-				newinode->i_mode = mode;
-			if ((*oplock & CIFS_CREATE_ACTION) &&
-			    (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID)) {
-				newinode->i_uid = current_fsuid();
-				if (inode->i_mode & S_ISGID)
-					newinode->i_gid = inode->i_gid;
-				else
-					newinode->i_gid = current_fsgid();
+			if ((*oplock & CIFS_CREATE_ACTION) && S_ISREG(newinode->i_mode)) {
+				if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM)
+					newinode->i_mode = mode;
+				if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) {
+					newinode->i_uid = current_fsuid();
+					if (inode->i_mode & S_ISGID)
+						newinode->i_gid = inode->i_gid;
+					else
+						newinode->i_gid = current_fsgid();
+				}
 			}
 		}
 	}
-- 
2.11.0


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v2 10/15] cifs: have ->mkdir() handle race with another client sanely
  2021-03-13  4:38   ` [PATCH v2 01/15] new helper: inode_wrong_type() Al Viro
                       ` (7 preceding siblings ...)
  2021-03-13  4:38     ` [PATCH v2 09/15] do_cifs_create(): don't set ->i_mode of something we had not created Al Viro
@ 2021-03-13  4:38     ` Al Viro
  2021-03-15 17:08       ` Jeff Layton
  2021-03-13  4:38     ` [PATCH v2 11/15] cifs: have cifs_fattr_to_inode() refuse to change type on live inode Al Viro
                       ` (5 subsequent siblings)
  14 siblings, 1 reply; 21+ messages in thread
From: Al Viro @ 2021-03-13  4:38 UTC (permalink / raw)
  To: Linus Torvalds
  Cc: linux-fsdevel, Jeff Layton, David Howells, Hans de Goede,
	Mike Marshall, Joseph Qi, Bob Peterson, Steve French,
	Richard Weinberger, Dominique Martinet, Arnd Bergmann

if we have mkdir request reported successful *and* simulating lookup
gets us a non-directory (which is possible if another client has
managed to get rmdir and create in between), the sane action is not
to mangle ->i_mode of non-directory inode to S_IFDIR | mode, it's
"report success and return with dentry negative unhashed" - that
way the next lookup will do the right thing.

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
---
 fs/cifs/inode.c | 12 +++++++++++-
 1 file changed, 11 insertions(+), 1 deletion(-)

diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c
index d46b36d52211..80c487fcf10e 100644
--- a/fs/cifs/inode.c
+++ b/fs/cifs/inode.c
@@ -1739,6 +1739,16 @@ cifs_mkdir_qinfo(struct inode *parent, struct dentry *dentry, umode_t mode,
 	if (rc)
 		return rc;
 
+	if (!S_ISDIR(inode->i_mode)) {
+		/*
+		 * mkdir succeeded, but another client has managed to remove the
+		 * sucker and replace it with non-directory.  Return success,
+		 * but don't leave the child in dcache.
+		 */
+		 iput(inode);
+		 d_drop(dentry);
+		 return 0;
+	}
 	/*
 	 * setting nlink not necessary except in cases where we failed to get it
 	 * from the server or was set bogus. Also, since this is a brand new
@@ -1790,7 +1800,7 @@ cifs_mkdir_qinfo(struct inode *parent, struct dentry *dentry, umode_t mode,
 		}
 	}
 	d_instantiate(dentry, inode);
-	return rc;
+	return 0;
 }
 
 static int
-- 
2.11.0


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v2 11/15] cifs: have cifs_fattr_to_inode() refuse to change type on live inode
  2021-03-13  4:38   ` [PATCH v2 01/15] new helper: inode_wrong_type() Al Viro
                       ` (8 preceding siblings ...)
  2021-03-13  4:38     ` [PATCH v2 10/15] cifs: have ->mkdir() handle race with another client sanely Al Viro
@ 2021-03-13  4:38     ` Al Viro
  2021-03-15 17:13       ` Jeff Layton
  2021-03-13  4:38     ` [PATCH v2 12/15] hostfs_mknod(): don't bother with init_special_inode() Al Viro
                       ` (4 subsequent siblings)
  14 siblings, 1 reply; 21+ messages in thread
From: Al Viro @ 2021-03-13  4:38 UTC (permalink / raw)
  To: Linus Torvalds
  Cc: linux-fsdevel, Jeff Layton, David Howells, Hans de Goede,
	Mike Marshall, Joseph Qi, Bob Peterson, Steve French,
	Richard Weinberger, Dominique Martinet, Arnd Bergmann

... instead of trying to do that in the callers (and missing some,
at that)

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
---
 fs/cifs/cifsproto.h |  2 +-
 fs/cifs/file.c      |  2 +-
 fs/cifs/inode.c     | 42 +++++++++++++++---------------------------
 fs/cifs/readdir.c   |  4 +---
 4 files changed, 18 insertions(+), 32 deletions(-)

diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
index 75ce6f742b8d..2a72dc24b00a 100644
--- a/fs/cifs/cifsproto.h
+++ b/fs/cifs/cifsproto.h
@@ -194,7 +194,7 @@ extern void cifs_unix_basic_to_fattr(struct cifs_fattr *fattr,
 				     struct cifs_sb_info *cifs_sb);
 extern void cifs_dir_info_to_fattr(struct cifs_fattr *, FILE_DIRECTORY_INFO *,
 					struct cifs_sb_info *);
-extern void cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr);
+extern int cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr);
 extern struct inode *cifs_iget(struct super_block *sb,
 			       struct cifs_fattr *fattr);
 
diff --git a/fs/cifs/file.c b/fs/cifs/file.c
index 26de4329d161..78266f0e0595 100644
--- a/fs/cifs/file.c
+++ b/fs/cifs/file.c
@@ -165,7 +165,7 @@ int cifs_posix_open(char *full_path, struct inode **pinode,
 			goto posix_open_ret;
 		}
 	} else {
-		cifs_fattr_to_inode(*pinode, &fattr);
+		rc = cifs_fattr_to_inode(*pinode, &fattr);
 	}
 
 posix_open_ret:
diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c
index 80c487fcf10e..51cb1ca829ec 100644
--- a/fs/cifs/inode.c
+++ b/fs/cifs/inode.c
@@ -157,12 +157,18 @@ cifs_nlink_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr)
 }
 
 /* populate an inode with info from a cifs_fattr struct */
-void
+int
 cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr)
 {
 	struct cifsInodeInfo *cifs_i = CIFS_I(inode);
 	struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
 
+	if (!(inode->i_state & I_NEW) &&
+	    unlikely(inode_wrong_type(inode, fattr->cf_mode))) {
+		CIFS_I(inode)->time = 0; /* force reval */
+		return -ESTALE;
+	}
+
 	cifs_revalidate_cache(inode, fattr);
 
 	spin_lock(&inode->i_lock);
@@ -219,6 +225,7 @@ cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr)
 		inode->i_flags |= S_AUTOMOUNT;
 	if (inode->i_state & I_NEW)
 		cifs_set_ops(inode);
+	return 0;
 }
 
 void
@@ -363,7 +370,7 @@ cifs_get_file_info_unix(struct file *filp)
 		rc = 0;
 	}
 
-	cifs_fattr_to_inode(inode, &fattr);
+	rc = cifs_fattr_to_inode(inode, &fattr);
 	free_xid(xid);
 	return rc;
 }
@@ -426,13 +433,7 @@ int cifs_get_inode_info_unix(struct inode **pinode,
 		}
 
 		/* if filetype is different, return error */
-		if (unlikely(inode_wrong_type(*pinode, fattr.cf_mode))) {
-			CIFS_I(*pinode)->time = 0; /* force reval */
-			rc = -ESTALE;
-			goto cgiiu_exit;
-		}
-
-		cifs_fattr_to_inode(*pinode, &fattr);
+		rc = cifs_fattr_to_inode(*pinode, &fattr);
 	}
 
 cgiiu_exit:
@@ -782,7 +783,8 @@ cifs_get_file_info(struct file *filp)
 	 */
 	fattr.cf_uniqueid = CIFS_I(inode)->uniqueid;
 	fattr.cf_flags |= CIFS_FATTR_NEED_REVAL;
-	cifs_fattr_to_inode(inode, &fattr);
+	/* if filetype is different, return error */
+	rc = cifs_fattr_to_inode(inode, &fattr);
 cgfi_exit:
 	free_xid(xid);
 	return rc;
@@ -1099,16 +1101,8 @@ cifs_get_inode_info(struct inode **inode,
 			rc = -ESTALE;
 			goto out;
 		}
-
 		/* if filetype is different, return error */
-		if (unlikely(((*inode)->i_mode & S_IFMT) !=
-		    (fattr.cf_mode & S_IFMT))) {
-			CIFS_I(*inode)->time = 0; /* force reval */
-			rc = -ESTALE;
-			goto out;
-		}
-
-		cifs_fattr_to_inode(*inode, &fattr);
+		rc = cifs_fattr_to_inode(*inode, &fattr);
 	}
 out:
 	cifs_buf_release(smb1_backup_rsp_buf);
@@ -1214,14 +1208,7 @@ smb311_posix_get_inode_info(struct inode **inode,
 		}
 
 		/* if filetype is different, return error */
-		if (unlikely(((*inode)->i_mode & S_IFMT) !=
-		    (fattr.cf_mode & S_IFMT))) {
-			CIFS_I(*inode)->time = 0; /* force reval */
-			rc = -ESTALE;
-			goto out;
-		}
-
-		cifs_fattr_to_inode(*inode, &fattr);
+		rc = cifs_fattr_to_inode(*inode, &fattr);
 	}
 out:
 	cifs_put_tlink(tlink);
@@ -1316,6 +1303,7 @@ cifs_iget(struct super_block *sb, struct cifs_fattr *fattr)
 			}
 		}
 
+		/* can't fail - see cifs_find_inode() */
 		cifs_fattr_to_inode(inode, fattr);
 		if (sb->s_flags & SB_NOATIME)
 			inode->i_flags |= S_NOATIME | S_NOCMTIME;
diff --git a/fs/cifs/readdir.c b/fs/cifs/readdir.c
index 80bf4c6f4c7b..e563c0fb47cb 100644
--- a/fs/cifs/readdir.c
+++ b/fs/cifs/readdir.c
@@ -119,9 +119,7 @@ cifs_prime_dcache(struct dentry *parent, struct qstr *name,
 			/* update inode in place
 			 * if both i_ino and i_mode didn't change */
 			if (CIFS_I(inode)->uniqueid == fattr->cf_uniqueid &&
-			    (inode->i_mode & S_IFMT) ==
-			    (fattr->cf_mode & S_IFMT)) {
-				cifs_fattr_to_inode(inode, fattr);
+			    cifs_fattr_to_inode(inode, fattr) == 0) {
 				dput(dentry);
 				return;
 			}
-- 
2.11.0


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v2 12/15] hostfs_mknod(): don't bother with init_special_inode()
  2021-03-13  4:38   ` [PATCH v2 01/15] new helper: inode_wrong_type() Al Viro
                       ` (9 preceding siblings ...)
  2021-03-13  4:38     ` [PATCH v2 11/15] cifs: have cifs_fattr_to_inode() refuse to change type on live inode Al Viro
@ 2021-03-13  4:38     ` Al Viro
  2021-03-13  4:38     ` [PATCH v2 13/15] openpromfs: don't do unlock_new_inode() until the new inode is set up Al Viro
                       ` (3 subsequent siblings)
  14 siblings, 0 replies; 21+ messages in thread
From: Al Viro @ 2021-03-13  4:38 UTC (permalink / raw)
  To: Linus Torvalds
  Cc: linux-fsdevel, Jeff Layton, David Howells, Hans de Goede,
	Mike Marshall, Joseph Qi, Bob Peterson, Steve French,
	Richard Weinberger, Dominique Martinet, Arnd Bergmann

read_name() in the end of hostfs_mknod() will DTRT

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
---
 fs/hostfs/hostfs_kern.c | 1 -
 1 file changed, 1 deletion(-)

diff --git a/fs/hostfs/hostfs_kern.c b/fs/hostfs/hostfs_kern.c
index 29e407762626..aed8c4f28ad3 100644
--- a/fs/hostfs/hostfs_kern.c
+++ b/fs/hostfs/hostfs_kern.c
@@ -712,7 +712,6 @@ static int hostfs_mknod(struct user_namespace *mnt_userns, struct inode *dir,
 	if (name == NULL)
 		goto out_put;
 
-	init_special_inode(inode, mode, dev);
 	err = do_mknod(name, mode, MAJOR(dev), MINOR(dev));
 	if (err)
 		goto out_free;
-- 
2.11.0


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v2 13/15] openpromfs: don't do unlock_new_inode() until the new inode is set up
  2021-03-13  4:38   ` [PATCH v2 01/15] new helper: inode_wrong_type() Al Viro
                       ` (10 preceding siblings ...)
  2021-03-13  4:38     ` [PATCH v2 12/15] hostfs_mknod(): don't bother with init_special_inode() Al Viro
@ 2021-03-13  4:38     ` Al Viro
  2021-03-13  4:38     ` [PATCH v2 14/15] 9p: missing chunk of "fs/9p: Don't update file type when updating file attributes" Al Viro
                       ` (2 subsequent siblings)
  14 siblings, 0 replies; 21+ messages in thread
From: Al Viro @ 2021-03-13  4:38 UTC (permalink / raw)
  To: Linus Torvalds
  Cc: linux-fsdevel, Jeff Layton, David Howells, Hans de Goede,
	Mike Marshall, Joseph Qi, Bob Peterson, Steve French,
	Richard Weinberger, Dominique Martinet, Arnd Bergmann

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
---
 fs/openpromfs/inode.c | 67 +++++++++++++++++++++++++--------------------------
 1 file changed, 33 insertions(+), 34 deletions(-)

diff --git a/fs/openpromfs/inode.c b/fs/openpromfs/inode.c
index 40c8c2e32fa3..f825176ff4ed 100644
--- a/fs/openpromfs/inode.c
+++ b/fs/openpromfs/inode.c
@@ -236,27 +236,31 @@ static struct dentry *openpromfs_lookup(struct inode *dir, struct dentry *dentry
 	mutex_unlock(&op_mutex);
 	if (IS_ERR(inode))
 		return ERR_CAST(inode);
-	ent_oi = OP_I(inode);
-	ent_oi->type = ent_type;
-	ent_oi->u = ent_data;
-
-	switch (ent_type) {
-	case op_inode_node:
-		inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO;
-		inode->i_op = &openprom_inode_operations;
-		inode->i_fop = &openprom_operations;
-		set_nlink(inode, 2);
-		break;
-	case op_inode_prop:
-		if (of_node_name_eq(dp, "options") && (len == 17) &&
-		    !strncmp (name, "security-password", 17))
-			inode->i_mode = S_IFREG | S_IRUSR | S_IWUSR;
-		else
-			inode->i_mode = S_IFREG | S_IRUGO;
-		inode->i_fop = &openpromfs_prop_ops;
-		set_nlink(inode, 1);
-		inode->i_size = ent_oi->u.prop->length;
-		break;
+	if (inode->i_state & I_NEW) {
+		inode->i_mtime = inode->i_atime = inode->i_ctime = current_time(inode);
+		ent_oi = OP_I(inode);
+		ent_oi->type = ent_type;
+		ent_oi->u = ent_data;
+
+		switch (ent_type) {
+		case op_inode_node:
+			inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO;
+			inode->i_op = &openprom_inode_operations;
+			inode->i_fop = &openprom_operations;
+			set_nlink(inode, 2);
+			break;
+		case op_inode_prop:
+			if (of_node_name_eq(dp, "options") && (len == 17) &&
+			    !strncmp (name, "security-password", 17))
+				inode->i_mode = S_IFREG | S_IRUSR | S_IWUSR;
+			else
+				inode->i_mode = S_IFREG | S_IRUGO;
+			inode->i_fop = &openpromfs_prop_ops;
+			set_nlink(inode, 1);
+			inode->i_size = ent_oi->u.prop->length;
+			break;
+		}
+		unlock_new_inode(inode);
 	}
 
 	return d_splice_alias(inode, dentry);
@@ -345,20 +349,9 @@ static void openprom_free_inode(struct inode *inode)
 
 static struct inode *openprom_iget(struct super_block *sb, ino_t ino)
 {
-	struct inode *inode;
-
-	inode = iget_locked(sb, ino);
+	struct inode *inode = iget_locked(sb, ino);
 	if (!inode)
-		return ERR_PTR(-ENOMEM);
-	if (inode->i_state & I_NEW) {
-		inode->i_mtime = inode->i_atime = inode->i_ctime = current_time(inode);
-		if (inode->i_ino == OPENPROM_ROOT_INO) {
-			inode->i_op = &openprom_inode_operations;
-			inode->i_fop = &openprom_operations;
-			inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO;
-		}
-		unlock_new_inode(inode);
-	}
+		inode = ERR_PTR(-ENOMEM);
 	return inode;
 }
 
@@ -394,9 +387,15 @@ static int openprom_fill_super(struct super_block *s, struct fs_context *fc)
 		goto out_no_root;
 	}
 
+	root_inode->i_mtime = root_inode->i_atime =
+		root_inode->i_ctime = current_time(root_inode);
+	root_inode->i_op = &openprom_inode_operations;
+	root_inode->i_fop = &openprom_operations;
+	root_inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO;
 	oi = OP_I(root_inode);
 	oi->type = op_inode_node;
 	oi->u.node = of_find_node_by_path("/");
+	unlock_new_inode(root_inode);
 
 	s->s_root = d_make_root(root_inode);
 	if (!s->s_root)
-- 
2.11.0


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v2 14/15] 9p: missing chunk of "fs/9p: Don't update file type when updating file attributes"
  2021-03-13  4:38   ` [PATCH v2 01/15] new helper: inode_wrong_type() Al Viro
                       ` (11 preceding siblings ...)
  2021-03-13  4:38     ` [PATCH v2 13/15] openpromfs: don't do unlock_new_inode() until the new inode is set up Al Viro
@ 2021-03-13  4:38     ` Al Viro
  2021-03-15 17:14       ` Jeff Layton
  2021-03-13  4:38     ` [PATCH v2 15/15] spufs: fix bogosity in S_ISGID handling Al Viro
  2021-03-15 17:19     ` [PATCH v2 01/15] new helper: inode_wrong_type() Jeff Layton
  14 siblings, 1 reply; 21+ messages in thread
From: Al Viro @ 2021-03-13  4:38 UTC (permalink / raw)
  To: Linus Torvalds
  Cc: linux-fsdevel, Jeff Layton, David Howells, Hans de Goede,
	Mike Marshall, Joseph Qi, Bob Peterson, Steve French,
	Richard Weinberger, Dominique Martinet, Arnd Bergmann

In commit 45089142b149 Aneesh had missed one (admittedly, very unlikely
to hit) case in v9fs_stat2inode_dotl().  However, the same considerations
apply there as well - we have no business whatsoever to change ->i_rdev
or the file type.

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
---
 fs/9p/vfs_inode_dotl.c | 10 +++-------
 1 file changed, 3 insertions(+), 7 deletions(-)

diff --git a/fs/9p/vfs_inode_dotl.c b/fs/9p/vfs_inode_dotl.c
index df0b87b05c42..e1c0240b51c0 100644
--- a/fs/9p/vfs_inode_dotl.c
+++ b/fs/9p/vfs_inode_dotl.c
@@ -663,14 +663,10 @@ v9fs_stat2inode_dotl(struct p9_stat_dotl *stat, struct inode *inode,
 		if (stat->st_result_mask & P9_STATS_NLINK)
 			set_nlink(inode, stat->st_nlink);
 		if (stat->st_result_mask & P9_STATS_MODE) {
-			inode->i_mode = stat->st_mode;
-			if ((S_ISBLK(inode->i_mode)) ||
-						(S_ISCHR(inode->i_mode)))
-				init_special_inode(inode, inode->i_mode,
-								inode->i_rdev);
+			mode = stat->st_mode & S_IALLUGO;
+			mode |= inode->i_mode & ~S_IALLUGO;
+			inode->i_mode = mode;
 		}
-		if (stat->st_result_mask & P9_STATS_RDEV)
-			inode->i_rdev = new_decode_dev(stat->st_rdev);
 		if (!(flags & V9FS_STAT2INODE_KEEP_ISIZE) &&
 		    stat->st_result_mask & P9_STATS_SIZE)
 			v9fs_i_size_write(inode, stat->st_size);
-- 
2.11.0


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v2 15/15] spufs: fix bogosity in S_ISGID handling
  2021-03-13  4:38   ` [PATCH v2 01/15] new helper: inode_wrong_type() Al Viro
                       ` (12 preceding siblings ...)
  2021-03-13  4:38     ` [PATCH v2 14/15] 9p: missing chunk of "fs/9p: Don't update file type when updating file attributes" Al Viro
@ 2021-03-13  4:38     ` Al Viro
  2021-03-15 17:19     ` [PATCH v2 01/15] new helper: inode_wrong_type() Jeff Layton
  14 siblings, 0 replies; 21+ messages in thread
From: Al Viro @ 2021-03-13  4:38 UTC (permalink / raw)
  To: Linus Torvalds
  Cc: linux-fsdevel, Jeff Layton, David Howells, Hans de Goede,
	Mike Marshall, Joseph Qi, Bob Peterson, Steve French,
	Richard Weinberger, Dominique Martinet, Arnd Bergmann

clearing everything *except* S_ISGID (including the S_IFDIR, among
other things) is wrong.  Just use init_inode_owner() and be done
with that...

Acked-by: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
---
 arch/powerpc/platforms/cell/spufs/inode.c | 10 ++--------
 1 file changed, 2 insertions(+), 8 deletions(-)

diff --git a/arch/powerpc/platforms/cell/spufs/inode.c b/arch/powerpc/platforms/cell/spufs/inode.c
index b83a3670bd74..bed05b644c2c 100644
--- a/arch/powerpc/platforms/cell/spufs/inode.c
+++ b/arch/powerpc/platforms/cell/spufs/inode.c
@@ -236,10 +236,7 @@ spufs_mkdir(struct inode *dir, struct dentry *dentry, unsigned int flags,
 	if (!inode)
 		return -ENOSPC;
 
-	if (dir->i_mode & S_ISGID) {
-		inode->i_gid = dir->i_gid;
-		inode->i_mode &= S_ISGID;
-	}
+	inode_init_owner(&init_user_ns, inode, dir, mode | S_IFDIR);
 	ctx = alloc_spu_context(SPUFS_I(dir)->i_gang); /* XXX gang */
 	SPUFS_I(inode)->i_ctx = ctx;
 	if (!ctx) {
@@ -470,10 +467,7 @@ spufs_mkgang(struct inode *dir, struct dentry *dentry, umode_t mode)
 		goto out;
 
 	ret = 0;
-	if (dir->i_mode & S_ISGID) {
-		inode->i_gid = dir->i_gid;
-		inode->i_mode &= S_ISGID;
-	}
+	inode_init_owner(&init_user_ns, inode, dir, mode | S_IFDIR);
 	gang = alloc_spu_gang();
 	SPUFS_I(inode)->i_ctx = NULL;
 	SPUFS_I(inode)->i_gang = gang;
-- 
2.11.0


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* Re: [PATCH v2 10/15] cifs: have ->mkdir() handle race with another client sanely
  2021-03-13  4:38     ` [PATCH v2 10/15] cifs: have ->mkdir() handle race with another client sanely Al Viro
@ 2021-03-15 17:08       ` Jeff Layton
  0 siblings, 0 replies; 21+ messages in thread
From: Jeff Layton @ 2021-03-15 17:08 UTC (permalink / raw)
  To: Al Viro, Linus Torvalds
  Cc: linux-fsdevel, David Howells, Hans de Goede, Mike Marshall,
	Joseph Qi, Bob Peterson, Steve French, Richard Weinberger,
	Dominique Martinet, Arnd Bergmann

On Sat, 2021-03-13 at 04:38 +0000, Al Viro wrote:
> if we have mkdir request reported successful *and* simulating lookup
> gets us a non-directory (which is possible if another client has
> managed to get rmdir and create in between), the sane action is not
> to mangle ->i_mode of non-directory inode to S_IFDIR | mode, it's
> "report success and return with dentry negative unhashed" - that
> way the next lookup will do the right thing.
> 
> Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
> ---
>  fs/cifs/inode.c | 12 +++++++++++-
>  1 file changed, 11 insertions(+), 1 deletion(-)
> 
> diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c
> index d46b36d52211..80c487fcf10e 100644
> --- a/fs/cifs/inode.c
> +++ b/fs/cifs/inode.c
> @@ -1739,6 +1739,16 @@ cifs_mkdir_qinfo(struct inode *parent, struct dentry *dentry, umode_t mode,
>  	if (rc)
>  		return rc;
>  
> 
> +	if (!S_ISDIR(inode->i_mode)) {
> +		/*
> +		 * mkdir succeeded, but another client has managed to remove the
> +		 * sucker and replace it with non-directory.  Return success,
> +		 * but don't leave the child in dcache.
> +		 */
> +		 iput(inode);
> +		 d_drop(dentry);
> +		 return 0;
> +	}
>  	/*
>  	 * setting nlink not necessary except in cases where we failed to get it
>  	 * from the server or was set bogus. Also, since this is a brand new
> @@ -1790,7 +1800,7 @@ cifs_mkdir_qinfo(struct inode *parent, struct dentry *dentry, umode_t mode,
>  		}
>  	}
>  	d_instantiate(dentry, inode);
> -	return rc;
> +	return 0;
>  }
>  
> 
>  static int

Reviewed-by: Jeff Layton <jlayton@kernel.org>


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v2 09/15] do_cifs_create(): don't set ->i_mode of something we had not created
  2021-03-13  4:38     ` [PATCH v2 09/15] do_cifs_create(): don't set ->i_mode of something we had not created Al Viro
@ 2021-03-15 17:12       ` Jeff Layton
  0 siblings, 0 replies; 21+ messages in thread
From: Jeff Layton @ 2021-03-15 17:12 UTC (permalink / raw)
  To: Al Viro, Linus Torvalds
  Cc: linux-fsdevel, David Howells, Hans de Goede, Mike Marshall,
	Joseph Qi, Bob Peterson, Steve French, Richard Weinberger,
	Dominique Martinet, Arnd Bergmann

On Sat, 2021-03-13 at 04:38 +0000, Al Viro wrote:
> If the file had existed before we'd called ->atomic_open() (without
> O_EXCL, that is), we have no more business setting ->i_mode than
> we would setting ->i_uid or ->i_gid.  We also have no business
> doing either if another client has managed to get unlink+mkdir
> between ->open() and cifs_inode_get_info().
> 
> Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
> ---
>  fs/cifs/dir.c | 19 ++++++++++---------
>  1 file changed, 10 insertions(+), 9 deletions(-)
> 
> diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c
> index a3fb81e0ba17..9d7ae93c8af7 100644
> --- a/fs/cifs/dir.c
> +++ b/fs/cifs/dir.c
> @@ -418,15 +418,16 @@ cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned int xid,
>  		if (newinode) {
>  			if (server->ops->set_lease_key)
>  				server->ops->set_lease_key(newinode, fid);
> -			if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM)
> -				newinode->i_mode = mode;
> -			if ((*oplock & CIFS_CREATE_ACTION) &&
> -			    (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID)) {
> -				newinode->i_uid = current_fsuid();
> -				if (inode->i_mode & S_ISGID)
> -					newinode->i_gid = inode->i_gid;
> -				else
> -					newinode->i_gid = current_fsgid();
> +			if ((*oplock & CIFS_CREATE_ACTION) && S_ISREG(newinode->i_mode)) {
> +				if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM)
> +					newinode->i_mode = mode;
> +				if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) {
> +					newinode->i_uid = current_fsuid();
> +					if (inode->i_mode & S_ISGID)
> +						newinode->i_gid = inode->i_gid;
> +					else
> +						newinode->i_gid = current_fsgid();
> +				}
>  			}
>  		}
>  	}

Reviewed-by: Jeff Layton <jlayton@kernel.org>


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v2 11/15] cifs: have cifs_fattr_to_inode() refuse to change type on live inode
  2021-03-13  4:38     ` [PATCH v2 11/15] cifs: have cifs_fattr_to_inode() refuse to change type on live inode Al Viro
@ 2021-03-15 17:13       ` Jeff Layton
  0 siblings, 0 replies; 21+ messages in thread
From: Jeff Layton @ 2021-03-15 17:13 UTC (permalink / raw)
  To: Al Viro, Linus Torvalds
  Cc: linux-fsdevel, David Howells, Hans de Goede, Mike Marshall,
	Joseph Qi, Bob Peterson, Steve French, Richard Weinberger,
	Dominique Martinet, Arnd Bergmann

On Sat, 2021-03-13 at 04:38 +0000, Al Viro wrote:
> ... instead of trying to do that in the callers (and missing some,
> at that)
> 
> Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
> ---
>  fs/cifs/cifsproto.h |  2 +-
>  fs/cifs/file.c      |  2 +-
>  fs/cifs/inode.c     | 42 +++++++++++++++---------------------------
>  fs/cifs/readdir.c   |  4 +---
>  4 files changed, 18 insertions(+), 32 deletions(-)
> 
> diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
> index 75ce6f742b8d..2a72dc24b00a 100644
> --- a/fs/cifs/cifsproto.h
> +++ b/fs/cifs/cifsproto.h
> @@ -194,7 +194,7 @@ extern void cifs_unix_basic_to_fattr(struct cifs_fattr *fattr,
>  				     struct cifs_sb_info *cifs_sb);
>  extern void cifs_dir_info_to_fattr(struct cifs_fattr *, FILE_DIRECTORY_INFO *,
>  					struct cifs_sb_info *);
> -extern void cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr);
> +extern int cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr);
>  extern struct inode *cifs_iget(struct super_block *sb,
>  			       struct cifs_fattr *fattr);
>  
> 
> diff --git a/fs/cifs/file.c b/fs/cifs/file.c
> index 26de4329d161..78266f0e0595 100644
> --- a/fs/cifs/file.c
> +++ b/fs/cifs/file.c
> @@ -165,7 +165,7 @@ int cifs_posix_open(char *full_path, struct inode **pinode,
>  			goto posix_open_ret;
>  		}
>  	} else {
> -		cifs_fattr_to_inode(*pinode, &fattr);
> +		rc = cifs_fattr_to_inode(*pinode, &fattr);
>  	}
>  
> 
>  posix_open_ret:
> diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c
> index 80c487fcf10e..51cb1ca829ec 100644
> --- a/fs/cifs/inode.c
> +++ b/fs/cifs/inode.c
> @@ -157,12 +157,18 @@ cifs_nlink_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr)
>  }
>  
> 
>  /* populate an inode with info from a cifs_fattr struct */
> -void
> +int
>  cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr)
>  {
>  	struct cifsInodeInfo *cifs_i = CIFS_I(inode);
>  	struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
>  
> 
> +	if (!(inode->i_state & I_NEW) &&
> +	    unlikely(inode_wrong_type(inode, fattr->cf_mode))) {
> +		CIFS_I(inode)->time = 0; /* force reval */
> +		return -ESTALE;
> +	}
> +
>  	cifs_revalidate_cache(inode, fattr);
>  
> 
>  	spin_lock(&inode->i_lock);
> @@ -219,6 +225,7 @@ cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr)
>  		inode->i_flags |= S_AUTOMOUNT;
>  	if (inode->i_state & I_NEW)
>  		cifs_set_ops(inode);
> +	return 0;
>  }
>  
> 
>  void
> @@ -363,7 +370,7 @@ cifs_get_file_info_unix(struct file *filp)
>  		rc = 0;
>  	}
>  
> 
> -	cifs_fattr_to_inode(inode, &fattr);
> +	rc = cifs_fattr_to_inode(inode, &fattr);
>  	free_xid(xid);
>  	return rc;
>  }
> @@ -426,13 +433,7 @@ int cifs_get_inode_info_unix(struct inode **pinode,
>  		}
>  
> 
>  		/* if filetype is different, return error */
> -		if (unlikely(inode_wrong_type(*pinode, fattr.cf_mode))) {
> -			CIFS_I(*pinode)->time = 0; /* force reval */
> -			rc = -ESTALE;
> -			goto cgiiu_exit;
> -		}
> -
> -		cifs_fattr_to_inode(*pinode, &fattr);
> +		rc = cifs_fattr_to_inode(*pinode, &fattr);
>  	}
>  
> 
>  cgiiu_exit:
> @@ -782,7 +783,8 @@ cifs_get_file_info(struct file *filp)
>  	 */
>  	fattr.cf_uniqueid = CIFS_I(inode)->uniqueid;
>  	fattr.cf_flags |= CIFS_FATTR_NEED_REVAL;
> -	cifs_fattr_to_inode(inode, &fattr);
> +	/* if filetype is different, return error */
> +	rc = cifs_fattr_to_inode(inode, &fattr);
>  cgfi_exit:
>  	free_xid(xid);
>  	return rc;
> @@ -1099,16 +1101,8 @@ cifs_get_inode_info(struct inode **inode,
>  			rc = -ESTALE;
>  			goto out;
>  		}
> -
>  		/* if filetype is different, return error */
> -		if (unlikely(((*inode)->i_mode & S_IFMT) !=
> -		    (fattr.cf_mode & S_IFMT))) {
> -			CIFS_I(*inode)->time = 0; /* force reval */
> -			rc = -ESTALE;
> -			goto out;
> -		}
> -
> -		cifs_fattr_to_inode(*inode, &fattr);
> +		rc = cifs_fattr_to_inode(*inode, &fattr);
>  	}
>  out:
>  	cifs_buf_release(smb1_backup_rsp_buf);
> @@ -1214,14 +1208,7 @@ smb311_posix_get_inode_info(struct inode **inode,
>  		}
>  
> 
>  		/* if filetype is different, return error */
> -		if (unlikely(((*inode)->i_mode & S_IFMT) !=
> -		    (fattr.cf_mode & S_IFMT))) {
> -			CIFS_I(*inode)->time = 0; /* force reval */
> -			rc = -ESTALE;
> -			goto out;
> -		}
> -
> -		cifs_fattr_to_inode(*inode, &fattr);
> +		rc = cifs_fattr_to_inode(*inode, &fattr);
>  	}
>  out:
>  	cifs_put_tlink(tlink);
> @@ -1316,6 +1303,7 @@ cifs_iget(struct super_block *sb, struct cifs_fattr *fattr)
>  			}
>  		}
>  
> 
> +		/* can't fail - see cifs_find_inode() */
>  		cifs_fattr_to_inode(inode, fattr);
>  		if (sb->s_flags & SB_NOATIME)
>  			inode->i_flags |= S_NOATIME | S_NOCMTIME;
> diff --git a/fs/cifs/readdir.c b/fs/cifs/readdir.c
> index 80bf4c6f4c7b..e563c0fb47cb 100644
> --- a/fs/cifs/readdir.c
> +++ b/fs/cifs/readdir.c
> @@ -119,9 +119,7 @@ cifs_prime_dcache(struct dentry *parent, struct qstr *name,
>  			/* update inode in place
>  			 * if both i_ino and i_mode didn't change */
>  			if (CIFS_I(inode)->uniqueid == fattr->cf_uniqueid &&
> -			    (inode->i_mode & S_IFMT) ==
> -			    (fattr->cf_mode & S_IFMT)) {
> -				cifs_fattr_to_inode(inode, fattr);
> +			    cifs_fattr_to_inode(inode, fattr) == 0) {
>  				dput(dentry);
>  				return;
>  			}

Nice cleanup, too.

Reviewed-by: Jeff Layton <jlayton@kernel.org>


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v2 14/15] 9p: missing chunk of "fs/9p: Don't update file type when updating file attributes"
  2021-03-13  4:38     ` [PATCH v2 14/15] 9p: missing chunk of "fs/9p: Don't update file type when updating file attributes" Al Viro
@ 2021-03-15 17:14       ` Jeff Layton
  0 siblings, 0 replies; 21+ messages in thread
From: Jeff Layton @ 2021-03-15 17:14 UTC (permalink / raw)
  To: Al Viro, Linus Torvalds
  Cc: linux-fsdevel, David Howells, Hans de Goede, Mike Marshall,
	Joseph Qi, Bob Peterson, Steve French, Richard Weinberger,
	Dominique Martinet, Arnd Bergmann

On Sat, 2021-03-13 at 04:38 +0000, Al Viro wrote:
> In commit 45089142b149 Aneesh had missed one (admittedly, very unlikely
> to hit) case in v9fs_stat2inode_dotl().  However, the same considerations
> apply there as well - we have no business whatsoever to change ->i_rdev
> or the file type.
> 
> Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
> ---
>  fs/9p/vfs_inode_dotl.c | 10 +++-------
>  1 file changed, 3 insertions(+), 7 deletions(-)
> 
> diff --git a/fs/9p/vfs_inode_dotl.c b/fs/9p/vfs_inode_dotl.c
> index df0b87b05c42..e1c0240b51c0 100644
> --- a/fs/9p/vfs_inode_dotl.c
> +++ b/fs/9p/vfs_inode_dotl.c
> @@ -663,14 +663,10 @@ v9fs_stat2inode_dotl(struct p9_stat_dotl *stat, struct inode *inode,
>  		if (stat->st_result_mask & P9_STATS_NLINK)
>  			set_nlink(inode, stat->st_nlink);
>  		if (stat->st_result_mask & P9_STATS_MODE) {
> -			inode->i_mode = stat->st_mode;
> -			if ((S_ISBLK(inode->i_mode)) ||
> -						(S_ISCHR(inode->i_mode)))
> -				init_special_inode(inode, inode->i_mode,
> -								inode->i_rdev);
> +			mode = stat->st_mode & S_IALLUGO;
> +			mode |= inode->i_mode & ~S_IALLUGO;
> +			inode->i_mode = mode;
>  		}
> -		if (stat->st_result_mask & P9_STATS_RDEV)
> -			inode->i_rdev = new_decode_dev(stat->st_rdev);
>  		if (!(flags & V9FS_STAT2INODE_KEEP_ISIZE) &&
>  		    stat->st_result_mask & P9_STATS_SIZE)
>  			v9fs_i_size_write(inode, stat->st_size);

Reviewed-by: Jeff Layton <jlayton@kernel.org>


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v2 01/15] new helper: inode_wrong_type()
  2021-03-13  4:38   ` [PATCH v2 01/15] new helper: inode_wrong_type() Al Viro
                       ` (13 preceding siblings ...)
  2021-03-13  4:38     ` [PATCH v2 15/15] spufs: fix bogosity in S_ISGID handling Al Viro
@ 2021-03-15 17:19     ` Jeff Layton
  14 siblings, 0 replies; 21+ messages in thread
From: Jeff Layton @ 2021-03-15 17:19 UTC (permalink / raw)
  To: Al Viro, Linus Torvalds
  Cc: linux-fsdevel, David Howells, Hans de Goede, Mike Marshall,
	Joseph Qi, Bob Peterson, Steve French, Richard Weinberger,
	Dominique Martinet, Arnd Bergmann

On Sat, 2021-03-13 at 04:38 +0000, Al Viro wrote:
> inode_wrong_type(inode, mode) returns true if setting inode->i_mode
> to given value would've changed the inode type.  We have enough of
> those checks open-coded to make a helper worthwhile.
> 
> Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
> ---
>  fs/9p/vfs_inode.c      | 4 ++--
>  fs/9p/vfs_inode_dotl.c | 4 ++--
>  fs/cifs/inode.c        | 5 ++---
>  fs/fuse/dir.c          | 6 +++---
>  fs/fuse/inode.c        | 2 +-
>  fs/fuse/readdir.c      | 2 +-
>  fs/nfs/inode.c         | 6 +++---
>  fs/nfsd/nfsproc.c      | 2 +-
>  fs/overlayfs/namei.c   | 4 ++--
>  include/linux/fs.h     | 5 +++++
>  10 files changed, 22 insertions(+), 18 deletions(-)
> 
> diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c
> index 8d97f0b45e9c..795706520b5e 100644
> --- a/fs/9p/vfs_inode.c
> +++ b/fs/9p/vfs_inode.c
> @@ -399,7 +399,7 @@ static int v9fs_test_inode(struct inode *inode, void *data)
>  
> 
>  	umode = p9mode2unixmode(v9ses, st, &rdev);
>  	/* don't match inode of different type */
> -	if ((inode->i_mode & S_IFMT) != (umode & S_IFMT))
> +	if (inode_wrong_type(inode, umode))
>  		return 0;
>  
> 
>  	/* compare qid details */
> @@ -1390,7 +1390,7 @@ int v9fs_refresh_inode(struct p9_fid *fid, struct inode *inode)
>  	 * Don't update inode if the file type is different
>  	 */
>  	umode = p9mode2unixmode(v9ses, st, &rdev);
> -	if ((inode->i_mode & S_IFMT) != (umode & S_IFMT))
> +	if (inode_wrong_type(inode, umode))
>  		goto out;
>  
> 
>  	/*
> diff --git a/fs/9p/vfs_inode_dotl.c b/fs/9p/vfs_inode_dotl.c
> index 1dc7af046615..df0b87b05c42 100644
> --- a/fs/9p/vfs_inode_dotl.c
> +++ b/fs/9p/vfs_inode_dotl.c
> @@ -59,7 +59,7 @@ static int v9fs_test_inode_dotl(struct inode *inode, void *data)
>  	struct p9_stat_dotl *st = (struct p9_stat_dotl *)data;
>  
> 
>  	/* don't match inode of different type */
> -	if ((inode->i_mode & S_IFMT) != (st->st_mode & S_IFMT))
> +	if (inode_wrong_type(inode, st->st_mode))
>  		return 0;
>  
> 
>  	if (inode->i_generation != st->st_gen)
> @@ -959,7 +959,7 @@ int v9fs_refresh_inode_dotl(struct p9_fid *fid, struct inode *inode)
>  	/*
>  	 * Don't update inode if the file type is different
>  	 */
> -	if ((inode->i_mode & S_IFMT) != (st->st_mode & S_IFMT))
> +	if (inode_wrong_type(inode, st->st_mode))
>  		goto out;
>  
> 
>  	/*
> diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c
> index 7c61bc9573c0..d46b36d52211 100644
> --- a/fs/cifs/inode.c
> +++ b/fs/cifs/inode.c
> @@ -426,8 +426,7 @@ int cifs_get_inode_info_unix(struct inode **pinode,
>  		}
>  
> 
>  		/* if filetype is different, return error */
> -		if (unlikely(((*pinode)->i_mode & S_IFMT) !=
> -		    (fattr.cf_mode & S_IFMT))) {
> +		if (unlikely(inode_wrong_type(*pinode, fattr.cf_mode))) {
>  			CIFS_I(*pinode)->time = 0; /* force reval */
>  			rc = -ESTALE;
>  			goto cgiiu_exit;
> @@ -1249,7 +1248,7 @@ cifs_find_inode(struct inode *inode, void *opaque)
>  		return 0;
>  
> 
>  	/* don't match inode of different type */
> -	if ((inode->i_mode & S_IFMT) != (fattr->cf_mode & S_IFMT))
> +	if (inode_wrong_type(inode, fattr->cf_mode))
>  		return 0;
>  
> 
>  	/* if it's not a directory or has no dentries, then flag it */
> diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c
> index 06a18700a845..2400b98e8808 100644
> --- a/fs/fuse/dir.c
> +++ b/fs/fuse/dir.c
> @@ -252,7 +252,7 @@ static int fuse_dentry_revalidate(struct dentry *entry, unsigned int flags)
>  		if (ret == -ENOMEM)
>  			goto out;
>  		if (ret || fuse_invalid_attr(&outarg.attr) ||
> -		    (outarg.attr.mode ^ inode->i_mode) & S_IFMT)
> +		    inode_wrong_type(inode, outarg.attr.mode))
>  			goto invalid;
>  
> 
>  		forget_all_cached_acls(inode);
> @@ -1054,7 +1054,7 @@ static int fuse_do_getattr(struct inode *inode, struct kstat *stat,
>  	err = fuse_simple_request(fm, &args);
>  	if (!err) {
>  		if (fuse_invalid_attr(&outarg.attr) ||
> -		    (inode->i_mode ^ outarg.attr.mode) & S_IFMT) {
> +		    inode_wrong_type(inode, outarg.attr.mode)) {
>  			fuse_make_bad(inode);
>  			err = -EIO;
>  		} else {
> @@ -1703,7 +1703,7 @@ int fuse_do_setattr(struct dentry *dentry, struct iattr *attr,
>  	}
>  
> 
>  	if (fuse_invalid_attr(&outarg.attr) ||
> -	    (inode->i_mode ^ outarg.attr.mode) & S_IFMT) {
> +	    inode_wrong_type(inode, outarg.attr.mode)) {
>  		fuse_make_bad(inode);
>  		err = -EIO;
>  		goto error;
> diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c
> index b0e18b470e91..b4b956da3851 100644
> --- a/fs/fuse/inode.c
> +++ b/fs/fuse/inode.c
> @@ -350,7 +350,7 @@ struct inode *fuse_iget(struct super_block *sb, u64 nodeid,
>  		inode->i_generation = generation;
>  		fuse_init_inode(inode, attr);
>  		unlock_new_inode(inode);
> -	} else if ((inode->i_mode ^ attr->mode) & S_IFMT) {
> +	} else if (inode_wrong_type(inode, attr->mode)) {
>  		/* Inode has changed type, any I/O on the old should fail */
>  		fuse_make_bad(inode);
>  		iput(inode);
> diff --git a/fs/fuse/readdir.c b/fs/fuse/readdir.c
> index 3441ffa740f3..277f7041d55a 100644
> --- a/fs/fuse/readdir.c
> +++ b/fs/fuse/readdir.c
> @@ -202,7 +202,7 @@ static int fuse_direntplus_link(struct file *file,
>  		inode = d_inode(dentry);
>  		if (!inode ||
>  		    get_node_id(inode) != o->nodeid ||
> -		    ((o->attr.mode ^ inode->i_mode) & S_IFMT)) {
> +		    inode_wrong_type(inode, o->attr.mode)) {
>  			d_invalidate(dentry);
>  			dput(dentry);
>  			goto retry;
> diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c
> index 749bbea14d99..b0da2408816d 100644
> --- a/fs/nfs/inode.c
> +++ b/fs/nfs/inode.c
> @@ -334,7 +334,7 @@ nfs_find_actor(struct inode *inode, void *opaque)
>  
> 
>  	if (NFS_FILEID(inode) != fattr->fileid)
>  		return 0;
> -	if ((S_IFMT & inode->i_mode) != (S_IFMT & fattr->mode))
> +	if (inode_wrong_type(inode, fattr->mode))
>  		return 0;
>  	if (nfs_compare_fh(NFS_FH(inode), fh))
>  		return 0;
> @@ -1460,7 +1460,7 @@ static int nfs_check_inode_attributes(struct inode *inode, struct nfs_fattr *fat
>  			return 0;
>  		return -ESTALE;
>  	}
> -	if ((fattr->valid & NFS_ATTR_FATTR_TYPE) && (inode->i_mode & S_IFMT) != (fattr->mode & S_IFMT))
> +	if ((fattr->valid & NFS_ATTR_FATTR_TYPE) && inode_wrong_type(inode, fattr->mode))
>  		return -ESTALE;
>  
> 
>  
> 
> @@ -1875,7 +1875,7 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
>  	/*
>  	 * Make sure the inode's type hasn't changed.
>  	 */
> -	if ((fattr->valid & NFS_ATTR_FATTR_TYPE) && (inode->i_mode & S_IFMT) != (fattr->mode & S_IFMT)) {
> +	if ((fattr->valid & NFS_ATTR_FATTR_TYPE) && inode_wrong_type(inode, fattr->mode)) {
>  		/*
>  		* Big trouble! The inode has become a different object.
>  		*/
> diff --git a/fs/nfsd/nfsproc.c b/fs/nfsd/nfsproc.c
> index a8d5449dd0e9..6d51687a0585 100644
> --- a/fs/nfsd/nfsproc.c
> +++ b/fs/nfsd/nfsproc.c
> @@ -381,7 +381,7 @@ nfsd_proc_create(struct svc_rqst *rqstp)
>  
> 
>  		/* Make sure the type and device matches */
>  		resp->status = nfserr_exist;
> -		if (inode && type != (inode->i_mode & S_IFMT))
> +		if (inode && inode_wrong_type(inode, type))
>  			goto out_unlock;
>  	}
>  
> 
> diff --git a/fs/overlayfs/namei.c b/fs/overlayfs/namei.c
> index 3fe05fb5d145..1d573972ce22 100644
> --- a/fs/overlayfs/namei.c
> +++ b/fs/overlayfs/namei.c
> @@ -371,7 +371,7 @@ int ovl_check_origin_fh(struct ovl_fs *ofs, struct ovl_fh *fh, bool connected,
>  		return PTR_ERR(origin);
>  
> 
>  	if (upperdentry && !ovl_is_whiteout(upperdentry) &&
> -	    ((d_inode(origin)->i_mode ^ d_inode(upperdentry)->i_mode) & S_IFMT))
> +	    inode_wrong_type(d_inode(upperdentry), d_inode(origin)->i_mode))
>  		goto invalid;
>  
> 
>  	if (!*stackp)
> @@ -730,7 +730,7 @@ struct dentry *ovl_lookup_index(struct ovl_fs *ofs, struct dentry *upper,
>  		index = ERR_PTR(-ESTALE);
>  		goto out;
>  	} else if (ovl_dentry_weird(index) || ovl_is_whiteout(index) ||
> -		   ((inode->i_mode ^ d_inode(origin)->i_mode) & S_IFMT)) {
> +		   inode_wrong_type(inode, d_inode(origin)->i_mode)) {
>  		/*
>  		 * Index should always be of the same file type as origin
>  		 * except for the case of a whiteout index. A whiteout
> diff --git a/include/linux/fs.h b/include/linux/fs.h
> index ec8f3ddf4a6a..9e0d76a41229 100644
> --- a/include/linux/fs.h
> +++ b/include/linux/fs.h
> @@ -2884,6 +2884,11 @@ static inline bool execute_ok(struct inode *inode)
>  	return (inode->i_mode & S_IXUGO) || S_ISDIR(inode->i_mode);
>  }
>  
> 
> +static inline bool inode_wrong_type(const struct inode *inode, umode_t mode)
> +{
> +	return (inode->i_mode ^ mode) & S_IFMT;
> +}
> +
>  static inline void file_start_write(struct file *file)
>  {
>  	if (!S_ISREG(file_inode(file)->i_mode))

I like the new helper -- much easier to read.

Reviewed-by: Jeff Layton <jlayton@kernel.org>


^ permalink raw reply	[flat|nested] 21+ messages in thread

end of thread, other threads:[~2021-03-15 17:20 UTC | newest]

Thread overview: 21+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
     [not found] <YDqRThxQOVQO9uOx@zeniv-ca.linux.org.uk>
2021-03-13  4:35 ` [RFC][PATCHSET v2] inode type bits fixes Al Viro
2021-03-13  4:38   ` [PATCH v2 01/15] new helper: inode_wrong_type() Al Viro
2021-03-13  4:38     ` [PATCH v2 02/15] ceph: fix up error handling with snapdirs Al Viro
2021-03-13  4:38     ` [PATCH v2 03/15] ceph: don't allow type or device number to change on non-I_NEW inodes Al Viro
2021-03-13  4:38     ` [PATCH v2 04/15] afs: Fix updating of i_mode due to 3rd party change Al Viro
2021-03-13  4:38     ` [PATCH v2 05/15] vboxsf: don't allow to change the inode type Al Viro
2021-03-13  4:38     ` [PATCH v2 06/15] orangefs_inode_is_stale(): i_mode type bits do *not* form a bitmap Al Viro
2021-03-13  4:38     ` [PATCH v2 07/15] ocfs2_inode_lock_update(): make sure we don't change the type bits of i_mode Al Viro
2021-03-13  4:38     ` [PATCH v2 08/15] gfs2: be careful with inode refresh Al Viro
2021-03-13  4:38     ` [PATCH v2 09/15] do_cifs_create(): don't set ->i_mode of something we had not created Al Viro
2021-03-15 17:12       ` Jeff Layton
2021-03-13  4:38     ` [PATCH v2 10/15] cifs: have ->mkdir() handle race with another client sanely Al Viro
2021-03-15 17:08       ` Jeff Layton
2021-03-13  4:38     ` [PATCH v2 11/15] cifs: have cifs_fattr_to_inode() refuse to change type on live inode Al Viro
2021-03-15 17:13       ` Jeff Layton
2021-03-13  4:38     ` [PATCH v2 12/15] hostfs_mknod(): don't bother with init_special_inode() Al Viro
2021-03-13  4:38     ` [PATCH v2 13/15] openpromfs: don't do unlock_new_inode() until the new inode is set up Al Viro
2021-03-13  4:38     ` [PATCH v2 14/15] 9p: missing chunk of "fs/9p: Don't update file type when updating file attributes" Al Viro
2021-03-15 17:14       ` Jeff Layton
2021-03-13  4:38     ` [PATCH v2 15/15] spufs: fix bogosity in S_ISGID handling Al Viro
2021-03-15 17:19     ` [PATCH v2 01/15] new helper: inode_wrong_type() Jeff Layton

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.