Skip to content

Commit d1466bc

Browse files
committed
Merge branch 'work.inode-type-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
Pull vfs inode type handling updates from Al Viro: "We should never change the type bits of ->i_mode or the method tables (->i_op and ->i_fop) of a live inode. Unfortunately, not all filesystems took care to prevent that" * 'work.inode-type-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs: spufs: fix bogosity in S_ISGID handling 9p: missing chunk of "fs/9p: Don't update file type when updating file attributes" openpromfs: don't do unlock_new_inode() until the new inode is set up hostfs_mknod(): don't bother with init_special_inode() cifs: have cifs_fattr_to_inode() refuse to change type on live inode cifs: have ->mkdir() handle race with another client sanely do_cifs_create(): don't set ->i_mode of something we had not created gfs2: be careful with inode refresh ocfs2_inode_lock_update(): make sure we don't change the type bits of i_mode orangefs_inode_is_stale(): i_mode type bits do *not* form a bitmap... vboxsf: don't allow to change the inode type afs: Fix updating of i_mode due to 3rd party change ceph: don't allow type or device number to change on non-I_NEW inodes ceph: fix up error handling with snapdirs new helper: inode_wrong_type()
2 parents 57fa236 + c4ab036 commit d1466bc

29 files changed

Lines changed: 225 additions & 164 deletions

File tree

arch/powerpc/platforms/cell/spufs/inode.c

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -236,10 +236,7 @@ spufs_mkdir(struct inode *dir, struct dentry *dentry, unsigned int flags,
236236
if (!inode)
237237
return -ENOSPC;
238238

239-
if (dir->i_mode & S_ISGID) {
240-
inode->i_gid = dir->i_gid;
241-
inode->i_mode &= S_ISGID;
242-
}
239+
inode_init_owner(&init_user_ns, inode, dir, mode | S_IFDIR);
243240
ctx = alloc_spu_context(SPUFS_I(dir)->i_gang); /* XXX gang */
244241
SPUFS_I(inode)->i_ctx = ctx;
245242
if (!ctx) {
@@ -470,10 +467,7 @@ spufs_mkgang(struct inode *dir, struct dentry *dentry, umode_t mode)
470467
goto out;
471468

472469
ret = 0;
473-
if (dir->i_mode & S_ISGID) {
474-
inode->i_gid = dir->i_gid;
475-
inode->i_mode &= S_ISGID;
476-
}
470+
inode_init_owner(&init_user_ns, inode, dir, mode | S_IFDIR);
477471
gang = alloc_spu_gang();
478472
SPUFS_I(inode)->i_ctx = NULL;
479473
SPUFS_I(inode)->i_gang = gang;

fs/9p/vfs_inode.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -399,7 +399,7 @@ static int v9fs_test_inode(struct inode *inode, void *data)
399399

400400
umode = p9mode2unixmode(v9ses, st, &rdev);
401401
/* don't match inode of different type */
402-
if ((inode->i_mode & S_IFMT) != (umode & S_IFMT))
402+
if (inode_wrong_type(inode, umode))
403403
return 0;
404404

405405
/* compare qid details */
@@ -1390,7 +1390,7 @@ int v9fs_refresh_inode(struct p9_fid *fid, struct inode *inode)
13901390
* Don't update inode if the file type is different
13911391
*/
13921392
umode = p9mode2unixmode(v9ses, st, &rdev);
1393-
if ((inode->i_mode & S_IFMT) != (umode & S_IFMT))
1393+
if (inode_wrong_type(inode, umode))
13941394
goto out;
13951395

13961396
/*

fs/9p/vfs_inode_dotl.c

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ static int v9fs_test_inode_dotl(struct inode *inode, void *data)
5959
struct p9_stat_dotl *st = (struct p9_stat_dotl *)data;
6060

6161
/* don't match inode of different type */
62-
if ((inode->i_mode & S_IFMT) != (st->st_mode & S_IFMT))
62+
if (inode_wrong_type(inode, st->st_mode))
6363
return 0;
6464

6565
if (inode->i_generation != st->st_gen)
@@ -663,14 +663,10 @@ v9fs_stat2inode_dotl(struct p9_stat_dotl *stat, struct inode *inode,
663663
if (stat->st_result_mask & P9_STATS_NLINK)
664664
set_nlink(inode, stat->st_nlink);
665665
if (stat->st_result_mask & P9_STATS_MODE) {
666-
inode->i_mode = stat->st_mode;
667-
if ((S_ISBLK(inode->i_mode)) ||
668-
(S_ISCHR(inode->i_mode)))
669-
init_special_inode(inode, inode->i_mode,
670-
inode->i_rdev);
666+
mode = stat->st_mode & S_IALLUGO;
667+
mode |= inode->i_mode & ~S_IALLUGO;
668+
inode->i_mode = mode;
671669
}
672-
if (stat->st_result_mask & P9_STATS_RDEV)
673-
inode->i_rdev = new_decode_dev(stat->st_rdev);
674670
if (!(flags & V9FS_STAT2INODE_KEEP_ISIZE) &&
675671
stat->st_result_mask & P9_STATS_SIZE)
676672
v9fs_i_size_write(inode, stat->st_size);
@@ -959,7 +955,7 @@ int v9fs_refresh_inode_dotl(struct p9_fid *fid, struct inode *inode)
959955
/*
960956
* Don't update inode if the file type is different
961957
*/
962-
if ((inode->i_mode & S_IFMT) != (st->st_mode & S_IFMT))
958+
if (inode_wrong_type(inode, st->st_mode))
963959
goto out;
964960

965961
/*

fs/afs/inode.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,13 +102,13 @@ static int afs_inode_init_from_status(struct afs_operation *op,
102102

103103
switch (status->type) {
104104
case AFS_FTYPE_FILE:
105-
inode->i_mode = S_IFREG | status->mode;
105+
inode->i_mode = S_IFREG | (status->mode & S_IALLUGO);
106106
inode->i_op = &afs_file_inode_operations;
107107
inode->i_fop = &afs_file_operations;
108108
inode->i_mapping->a_ops = &afs_fs_aops;
109109
break;
110110
case AFS_FTYPE_DIR:
111-
inode->i_mode = S_IFDIR | status->mode;
111+
inode->i_mode = S_IFDIR | (status->mode & S_IALLUGO);
112112
inode->i_op = &afs_dir_inode_operations;
113113
inode->i_fop = &afs_dir_file_operations;
114114
inode->i_mapping->a_ops = &afs_dir_aops;
@@ -198,7 +198,7 @@ static void afs_apply_status(struct afs_operation *op,
198198
if (status->mode != vnode->status.mode) {
199199
mode = inode->i_mode;
200200
mode &= ~S_IALLUGO;
201-
mode |= status->mode;
201+
mode |= status->mode & S_IALLUGO;
202202
WRITE_ONCE(inode->i_mode, mode);
203203
}
204204

fs/ceph/caps.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3358,7 +3358,13 @@ static void handle_cap_grant(struct inode *inode,
33583358

33593359
if ((newcaps & CEPH_CAP_AUTH_SHARED) &&
33603360
(extra_info->issued & CEPH_CAP_AUTH_EXCL) == 0) {
3361-
inode->i_mode = le32_to_cpu(grant->mode);
3361+
umode_t mode = le32_to_cpu(grant->mode);
3362+
3363+
if (inode_wrong_type(inode, mode))
3364+
pr_warn_once("inode type changed! (ino %llx.%llx is 0%o, mds says 0%o)\n",
3365+
ceph_vinop(inode), inode->i_mode, mode);
3366+
else
3367+
inode->i_mode = mode;
33623368
inode->i_uid = make_kuid(&init_user_ns, le32_to_cpu(grant->uid));
33633369
inode->i_gid = make_kgid(&init_user_ns, le32_to_cpu(grant->gid));
33643370
ci->i_btime = extra_info->btime;

fs/ceph/dir.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -677,6 +677,8 @@ int ceph_handle_snapdir(struct ceph_mds_request *req,
677677
strcmp(dentry->d_name.name,
678678
fsc->mount_options->snapdir_name) == 0) {
679679
struct inode *inode = ceph_get_snapdir(parent);
680+
if (IS_ERR(inode))
681+
return PTR_ERR(inode);
680682
dout("ENOENT on snapdir %p '%pd', linking to snapdir %p\n",
681683
dentry, dentry, inode);
682684
BUG_ON(!d_unhashed(dentry));

fs/ceph/export.c

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -248,9 +248,10 @@ static struct dentry *__snapfh_to_dentry(struct super_block *sb,
248248
ihold(inode);
249249
} else {
250250
/* mds does not support lookup snapped inode */
251-
err = -EOPNOTSUPP;
252-
inode = NULL;
251+
inode = ERR_PTR(-EOPNOTSUPP);
253252
}
253+
} else {
254+
inode = ERR_PTR(-ESTALE);
254255
}
255256
ceph_mdsc_put_request(req);
256257

@@ -261,8 +262,8 @@ static struct dentry *__snapfh_to_dentry(struct super_block *sb,
261262
dout("snapfh_to_dentry %llx.%llx parent %llx hash %x err=%d",
262263
vino.ino, vino.snap, sfh->parent_ino, sfh->hash, err);
263264
}
264-
if (!inode)
265-
return ERR_PTR(-ESTALE);
265+
if (IS_ERR(inode))
266+
return ERR_CAST(inode);
266267
/* see comments in ceph_get_parent() */
267268
return unlinked ? d_obtain_root(inode) : d_obtain_alias(inode);
268269
}

fs/ceph/inode.c

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -78,9 +78,21 @@ struct inode *ceph_get_snapdir(struct inode *parent)
7878
struct inode *inode = ceph_get_inode(parent->i_sb, vino);
7979
struct ceph_inode_info *ci = ceph_inode(inode);
8080

81-
BUG_ON(!S_ISDIR(parent->i_mode));
8281
if (IS_ERR(inode))
8382
return inode;
83+
84+
if (!S_ISDIR(parent->i_mode)) {
85+
pr_warn_once("bad snapdir parent type (mode=0%o)\n",
86+
parent->i_mode);
87+
return ERR_PTR(-ENOTDIR);
88+
}
89+
90+
if (!(inode->i_state & I_NEW) && !S_ISDIR(inode->i_mode)) {
91+
pr_warn_once("bad snapdir inode type (mode=0%o)\n",
92+
inode->i_mode);
93+
return ERR_PTR(-ENOTDIR);
94+
}
95+
8496
inode->i_mode = parent->i_mode;
8597
inode->i_uid = parent->i_uid;
8698
inode->i_gid = parent->i_gid;
@@ -757,11 +769,32 @@ int ceph_fill_inode(struct inode *inode, struct page *locked_page,
757769
bool queue_trunc = false;
758770
bool new_version = false;
759771
bool fill_inline = false;
772+
umode_t mode = le32_to_cpu(info->mode);
773+
dev_t rdev = le32_to_cpu(info->rdev);
760774

761775
dout("%s %p ino %llx.%llx v %llu had %llu\n", __func__,
762776
inode, ceph_vinop(inode), le64_to_cpu(info->version),
763777
ci->i_version);
764778

779+
/* Once I_NEW is cleared, we can't change type or dev numbers */
780+
if (inode->i_state & I_NEW) {
781+
inode->i_mode = mode;
782+
} else {
783+
if (inode_wrong_type(inode, mode)) {
784+
pr_warn_once("inode type changed! (ino %llx.%llx is 0%o, mds says 0%o)\n",
785+
ceph_vinop(inode), inode->i_mode, mode);
786+
return -ESTALE;
787+
}
788+
789+
if ((S_ISCHR(mode) || S_ISBLK(mode)) && inode->i_rdev != rdev) {
790+
pr_warn_once("dev inode rdev changed! (ino %llx.%llx is %u:%u, mds says %u:%u)\n",
791+
ceph_vinop(inode), MAJOR(inode->i_rdev),
792+
MINOR(inode->i_rdev), MAJOR(rdev),
793+
MINOR(rdev));
794+
return -ESTALE;
795+
}
796+
}
797+
765798
info_caps = le32_to_cpu(info->cap.caps);
766799

767800
/* prealloc new cap struct */
@@ -815,8 +848,6 @@ int ceph_fill_inode(struct inode *inode, struct page *locked_page,
815848
issued |= __ceph_caps_dirty(ci);
816849
new_issued = ~issued & info_caps;
817850

818-
/* update inode */
819-
inode->i_rdev = le32_to_cpu(info->rdev);
820851
/* directories have fl_stripe_unit set to zero */
821852
if (le32_to_cpu(info->layout.fl_stripe_unit))
822853
inode->i_blkbits =
@@ -828,7 +859,7 @@ int ceph_fill_inode(struct inode *inode, struct page *locked_page,
828859

829860
if ((new_version || (new_issued & CEPH_CAP_AUTH_SHARED)) &&
830861
(issued & CEPH_CAP_AUTH_EXCL) == 0) {
831-
inode->i_mode = le32_to_cpu(info->mode);
862+
inode->i_mode = mode;
832863
inode->i_uid = make_kuid(&init_user_ns, le32_to_cpu(info->uid));
833864
inode->i_gid = make_kgid(&init_user_ns, le32_to_cpu(info->gid));
834865
dout("%p mode 0%o uid.gid %d.%d\n", inode, inode->i_mode,
@@ -926,7 +957,7 @@ int ceph_fill_inode(struct inode *inode, struct page *locked_page,
926957
case S_IFCHR:
927958
case S_IFSOCK:
928959
inode->i_blkbits = PAGE_SHIFT;
929-
init_special_inode(inode, inode->i_mode, inode->i_rdev);
960+
init_special_inode(inode, inode->i_mode, rdev);
930961
inode->i_op = &ceph_file_iops;
931962
break;
932963
case S_IFREG:

fs/cifs/cifsproto.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ extern void cifs_unix_basic_to_fattr(struct cifs_fattr *fattr,
205205
struct cifs_sb_info *cifs_sb);
206206
extern void cifs_dir_info_to_fattr(struct cifs_fattr *, FILE_DIRECTORY_INFO *,
207207
struct cifs_sb_info *);
208-
extern void cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr);
208+
extern int cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr);
209209
extern struct inode *cifs_iget(struct super_block *sb,
210210
struct cifs_fattr *fattr);
211211

fs/cifs/dir.c

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -374,15 +374,16 @@ cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned int xid,
374374
if (newinode) {
375375
if (server->ops->set_lease_key)
376376
server->ops->set_lease_key(newinode, fid);
377-
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM)
378-
newinode->i_mode = mode;
379-
if ((*oplock & CIFS_CREATE_ACTION) &&
380-
(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID)) {
381-
newinode->i_uid = current_fsuid();
382-
if (inode->i_mode & S_ISGID)
383-
newinode->i_gid = inode->i_gid;
384-
else
385-
newinode->i_gid = current_fsgid();
377+
if ((*oplock & CIFS_CREATE_ACTION) && S_ISREG(newinode->i_mode)) {
378+
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM)
379+
newinode->i_mode = mode;
380+
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) {
381+
newinode->i_uid = current_fsuid();
382+
if (inode->i_mode & S_ISGID)
383+
newinode->i_gid = inode->i_gid;
384+
else
385+
newinode->i_gid = current_fsgid();
386+
}
386387
}
387388
}
388389
}

0 commit comments

Comments
 (0)