Skip to content

Commit c54b386

Browse files
neilbrownbrauner
authored andcommitted
VFS: Change vfs_mkdir() to return the dentry.
vfs_mkdir() does not guarantee to leave the child dentry hashed or make it positive on success, and in many such cases the filesystem had to use a different dentry which it can now return. This patch changes vfs_mkdir() to return the dentry provided by the filesystems which is hashed and positive when provided. This reduces the number of cases where the resulting dentry is not positive to a handful which don't deserve extra efforts. The only callers of vfs_mkdir() which are interested in the resulting inode are in-kernel filesystem clients: cachefiles, nfsd, smb/server. The only filesystems that don't reliably provide the inode are: - kernfs, tracefs which these clients are unlikely to be interested in - cifs in some configurations would need to do a lookup to find the created inode, but doesn't. cifs cannot be exported via NFS, is unlikely to be used by cachefiles, and smb/server only has a soft requirement for the inode, so this is unlikely to be a problem in practice. - hostfs, nfs, cifs may need to do a lookup (rarely for NFS) and it is possible for a race to make that lookup fail. Actual failure is unlikely and providing callers handle negative dentries graceful they will fail-safe. So this patch removes the lookup code in nfsd and smb/server and adjusts them to fail safe if a negative dentry is provided: - cache-files already fails safe by restarting the task from the top - it still does with this change, though it no longer calls cachefiles_put_directory() as that will crash if the dentry is negative. - nfsd reports "Server-fault" which it what it used to do if the lookup failed. This will never happen on any file-systems that it can actually export, so this is of no consequence. I removed the fh_update() call as that is not needed and out-of-place. A subsequent nfsd_create_setattr() call will call fh_update() when needed. - smb/server only wants the inode to call ksmbd_smb_inherit_owner() which updates ->i_uid (without calling notify_change() or similar) which can be safely skipping on cifs (I hope). If a different dentry is returned, the first one is put. If necessary the fact that it is new can be determined by comparing pointers. A new dentry will certainly have a new pointer (as the old is put after the new is obtained). Similarly if an error is returned (via ERR_PTR()) the original dentry is put. Reviewed-by: Jeff Layton <[email protected]> Signed-off-by: NeilBrown <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Christian Brauner <[email protected]>
1 parent 8376583 commit c54b386

File tree

13 files changed

+104
-127
lines changed

13 files changed

+104
-127
lines changed

drivers/base/devtmpfs.c

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -160,18 +160,17 @@ static int dev_mkdir(const char *name, umode_t mode)
160160
{
161161
struct dentry *dentry;
162162
struct path path;
163-
int err;
164163

165164
dentry = kern_path_create(AT_FDCWD, name, &path, LOOKUP_DIRECTORY);
166165
if (IS_ERR(dentry))
167166
return PTR_ERR(dentry);
168167

169-
err = vfs_mkdir(&nop_mnt_idmap, d_inode(path.dentry), dentry, mode);
170-
if (!err)
168+
dentry = vfs_mkdir(&nop_mnt_idmap, d_inode(path.dentry), dentry, mode);
169+
if (!IS_ERR(dentry))
171170
/* mark as kernel-created inode */
172171
d_inode(dentry)->i_private = &thread;
173172
done_path_create(&path, dentry);
174-
return err;
173+
return PTR_ERR_OR_ZERO(dentry);
175174
}
176175

177176
static int create_path(const char *nodepath)

fs/cachefiles/namei.c

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -128,18 +128,19 @@ struct dentry *cachefiles_get_directory(struct cachefiles_cache *cache,
128128
ret = security_path_mkdir(&path, subdir, 0700);
129129
if (ret < 0)
130130
goto mkdir_error;
131-
ret = cachefiles_inject_write_error();
132-
if (ret == 0)
133-
ret = vfs_mkdir(&nop_mnt_idmap, d_inode(dir), subdir, 0700);
134-
if (ret < 0) {
131+
subdir = ERR_PTR(cachefiles_inject_write_error());
132+
if (!IS_ERR(subdir))
133+
subdir = vfs_mkdir(&nop_mnt_idmap, d_inode(dir), subdir, 0700);
134+
ret = PTR_ERR(subdir);
135+
if (IS_ERR(subdir)) {
135136
trace_cachefiles_vfs_error(NULL, d_inode(dir), ret,
136137
cachefiles_trace_mkdir_error);
137138
goto mkdir_error;
138139
}
139140
trace_cachefiles_mkdir(dir, subdir);
140141

141-
if (unlikely(d_unhashed(subdir))) {
142-
cachefiles_put_directory(subdir);
142+
if (unlikely(d_unhashed(subdir) || d_is_negative(subdir))) {
143+
dput(subdir);
143144
goto retry;
144145
}
145146
ASSERT(d_backing_inode(subdir));
@@ -195,7 +196,8 @@ struct dentry *cachefiles_get_directory(struct cachefiles_cache *cache,
195196

196197
mkdir_error:
197198
inode_unlock(d_inode(dir));
198-
dput(subdir);
199+
if (!IS_ERR(subdir))
200+
dput(subdir);
199201
pr_err("mkdir %s failed with error %d\n", dirname, ret);
200202
return ERR_PTR(ret);
201203

fs/ecryptfs/inode.c

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -511,10 +511,16 @@ static struct dentry *ecryptfs_mkdir(struct mnt_idmap *idmap, struct inode *dir,
511511
struct inode *lower_dir;
512512

513513
rc = lock_parent(dentry, &lower_dentry, &lower_dir);
514-
if (!rc)
515-
rc = vfs_mkdir(&nop_mnt_idmap, lower_dir,
516-
lower_dentry, mode);
517-
if (rc || d_really_is_negative(lower_dentry))
514+
if (rc)
515+
goto out;
516+
517+
lower_dentry = vfs_mkdir(&nop_mnt_idmap, lower_dir,
518+
lower_dentry, mode);
519+
rc = PTR_ERR(lower_dentry);
520+
if (IS_ERR(lower_dentry))
521+
goto out;
522+
rc = 0;
523+
if (d_unhashed(lower_dentry))
518524
goto out;
519525
rc = ecryptfs_interpose(lower_dentry, dentry, dir->i_sb);
520526
if (rc)

fs/init.c

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -230,9 +230,12 @@ int __init init_mkdir(const char *pathname, umode_t mode)
230230
return PTR_ERR(dentry);
231231
mode = mode_strip_umask(d_inode(path.dentry), mode);
232232
error = security_path_mkdir(&path, dentry, mode);
233-
if (!error)
234-
error = vfs_mkdir(mnt_idmap(path.mnt), path.dentry->d_inode,
233+
if (!error) {
234+
dentry = vfs_mkdir(mnt_idmap(path.mnt), path.dentry->d_inode,
235235
dentry, mode);
236+
if (IS_ERR(dentry))
237+
error = PTR_ERR(dentry);
238+
}
236239
done_path_create(&path, dentry);
237240
return error;
238241
}

fs/namei.c

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4128,7 +4128,8 @@ EXPORT_SYMBOL(kern_path_create);
41284128

41294129
void done_path_create(struct path *path, struct dentry *dentry)
41304130
{
4131-
dput(dentry);
4131+
if (!IS_ERR(dentry))
4132+
dput(dentry);
41324133
inode_unlock(path->dentry->d_inode);
41334134
mnt_drop_write(path->mnt);
41344135
path_put(path);
@@ -4274,7 +4275,7 @@ SYSCALL_DEFINE3(mknod, const char __user *, filename, umode_t, mode, unsigned, d
42744275
}
42754276

42764277
/**
4277-
* vfs_mkdir - create directory
4278+
* vfs_mkdir - create directory returning correct dentry if possible
42784279
* @idmap: idmap of the mount the inode was found from
42794280
* @dir: inode of the parent directory
42804281
* @dentry: dentry of the child directory
@@ -4287,41 +4288,51 @@ SYSCALL_DEFINE3(mknod, const char __user *, filename, umode_t, mode, unsigned, d
42874288
* care to map the inode according to @idmap before checking permissions.
42884289
* On non-idmapped mounts or if permission checking is to be performed on the
42894290
* raw inode simply pass @nop_mnt_idmap.
4291+
*
4292+
* In the event that the filesystem does not use the *@dentry but leaves it
4293+
* negative or unhashes it and possibly splices a different one returning it,
4294+
* the original dentry is dput() and the alternate is returned.
4295+
*
4296+
* In case of an error the dentry is dput() and an ERR_PTR() is returned.
42904297
*/
4291-
int vfs_mkdir(struct mnt_idmap *idmap, struct inode *dir,
4292-
struct dentry *dentry, umode_t mode)
4298+
struct dentry *vfs_mkdir(struct mnt_idmap *idmap, struct inode *dir,
4299+
struct dentry *dentry, umode_t mode)
42934300
{
42944301
int error;
42954302
unsigned max_links = dir->i_sb->s_max_links;
42964303
struct dentry *de;
42974304

42984305
error = may_create(idmap, dir, dentry);
42994306
if (error)
4300-
return error;
4307+
goto err;
43014308

4309+
error = -EPERM;
43024310
if (!dir->i_op->mkdir)
4303-
return -EPERM;
4311+
goto err;
43044312

43054313
mode = vfs_prepare_mode(idmap, dir, mode, S_IRWXUGO | S_ISVTX, 0);
43064314
error = security_inode_mkdir(dir, dentry, mode);
43074315
if (error)
4308-
return error;
4316+
goto err;
43094317

4318+
error = -EMLINK;
43104319
if (max_links && dir->i_nlink >= max_links)
4311-
return -EMLINK;
4320+
goto err;
43124321

43134322
de = dir->i_op->mkdir(idmap, dir, dentry, mode);
4323+
error = PTR_ERR(de);
43144324
if (IS_ERR(de))
4315-
return PTR_ERR(de);
4325+
goto err;
43164326
if (de) {
4317-
fsnotify_mkdir(dir, de);
4318-
/* Cannot return de yet */
4319-
dput(de);
4320-
} else {
4321-
fsnotify_mkdir(dir, dentry);
4327+
dput(dentry);
4328+
dentry = de;
43224329
}
4330+
fsnotify_mkdir(dir, dentry);
4331+
return dentry;
43234332

4324-
return 0;
4333+
err:
4334+
dput(dentry);
4335+
return ERR_PTR(error);
43254336
}
43264337
EXPORT_SYMBOL(vfs_mkdir);
43274338

@@ -4341,8 +4352,10 @@ int do_mkdirat(int dfd, struct filename *name, umode_t mode)
43414352
error = security_path_mkdir(&path, dentry,
43424353
mode_strip_umask(path.dentry->d_inode, mode));
43434354
if (!error) {
4344-
error = vfs_mkdir(mnt_idmap(path.mnt), path.dentry->d_inode,
4355+
dentry = vfs_mkdir(mnt_idmap(path.mnt), path.dentry->d_inode,
43454356
dentry, mode);
4357+
if (IS_ERR(dentry))
4358+
error = PTR_ERR(dentry);
43464359
}
43474360
done_path_create(&path, dentry);
43484361
if (retry_estale(error, lookup_flags)) {

fs/nfsd/nfs4recover.c

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -233,9 +233,12 @@ nfsd4_create_clid_dir(struct nfs4_client *clp)
233233
* as well be forgiving and just succeed silently.
234234
*/
235235
goto out_put;
236-
status = vfs_mkdir(&nop_mnt_idmap, d_inode(dir), dentry, S_IRWXU);
236+
dentry = vfs_mkdir(&nop_mnt_idmap, d_inode(dir), dentry, S_IRWXU);
237+
if (IS_ERR(dentry))
238+
status = PTR_ERR(dentry);
237239
out_put:
238-
dput(dentry);
240+
if (!status)
241+
dput(dentry);
239242
out_unlock:
240243
inode_unlock(d_inode(dir));
241244
if (status == 0) {

fs/nfsd/vfs.c

Lines changed: 11 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1461,7 +1461,7 @@ nfsd_create_locked(struct svc_rqst *rqstp, struct svc_fh *fhp,
14611461
struct inode *dirp;
14621462
struct iattr *iap = attrs->na_iattr;
14631463
__be32 err;
1464-
int host_err;
1464+
int host_err = 0;
14651465

14661466
dentry = fhp->fh_dentry;
14671467
dirp = d_inode(dentry);
@@ -1488,25 +1488,15 @@ nfsd_create_locked(struct svc_rqst *rqstp, struct svc_fh *fhp,
14881488
nfsd_check_ignore_resizing(iap);
14891489
break;
14901490
case S_IFDIR:
1491-
host_err = vfs_mkdir(&nop_mnt_idmap, dirp, dchild, iap->ia_mode);
1492-
if (!host_err && unlikely(d_unhashed(dchild))) {
1493-
struct dentry *d;
1494-
d = lookup_one_len(dchild->d_name.name,
1495-
dchild->d_parent,
1496-
dchild->d_name.len);
1497-
if (IS_ERR(d)) {
1498-
host_err = PTR_ERR(d);
1499-
break;
1500-
}
1501-
if (unlikely(d_is_negative(d))) {
1502-
dput(d);
1503-
err = nfserr_serverfault;
1504-
goto out;
1505-
}
1491+
dchild = vfs_mkdir(&nop_mnt_idmap, dirp, dchild, iap->ia_mode);
1492+
if (IS_ERR(dchild)) {
1493+
host_err = PTR_ERR(dchild);
1494+
} else if (d_is_negative(dchild)) {
1495+
err = nfserr_serverfault;
1496+
goto out;
1497+
} else if (unlikely(dchild != resfhp->fh_dentry)) {
15061498
dput(resfhp->fh_dentry);
1507-
resfhp->fh_dentry = dget(d);
1508-
dput(dchild);
1509-
dchild = d;
1499+
resfhp->fh_dentry = dget(dchild);
15101500
}
15111501
break;
15121502
case S_IFCHR:
@@ -1527,7 +1517,8 @@ nfsd_create_locked(struct svc_rqst *rqstp, struct svc_fh *fhp,
15271517
err = nfsd_create_setattr(rqstp, fhp, resfhp, attrs);
15281518

15291519
out:
1530-
dput(dchild);
1520+
if (!IS_ERR(dchild))
1521+
dput(dchild);
15311522
return err;
15321523

15331524
out_nfserr:

fs/overlayfs/dir.c

Lines changed: 4 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -138,37 +138,6 @@ int ovl_cleanup_and_whiteout(struct ovl_fs *ofs, struct inode *dir,
138138
goto out;
139139
}
140140

141-
int ovl_mkdir_real(struct ovl_fs *ofs, struct inode *dir,
142-
struct dentry **newdentry, umode_t mode)
143-
{
144-
int err;
145-
struct dentry *d, *dentry = *newdentry;
146-
147-
err = ovl_do_mkdir(ofs, dir, dentry, mode);
148-
if (err)
149-
return err;
150-
151-
if (likely(!d_unhashed(dentry)))
152-
return 0;
153-
154-
/*
155-
* vfs_mkdir() may succeed and leave the dentry passed
156-
* to it unhashed and negative. If that happens, try to
157-
* lookup a new hashed and positive dentry.
158-
*/
159-
d = ovl_lookup_upper(ofs, dentry->d_name.name, dentry->d_parent,
160-
dentry->d_name.len);
161-
if (IS_ERR(d)) {
162-
pr_warn("failed lookup after mkdir (%pd2, err=%i).\n",
163-
dentry, err);
164-
return PTR_ERR(d);
165-
}
166-
dput(dentry);
167-
*newdentry = d;
168-
169-
return 0;
170-
}
171-
172141
struct dentry *ovl_create_real(struct ovl_fs *ofs, struct inode *dir,
173142
struct dentry *newdentry, struct ovl_cattr *attr)
174143
{
@@ -191,7 +160,8 @@ struct dentry *ovl_create_real(struct ovl_fs *ofs, struct inode *dir,
191160

192161
case S_IFDIR:
193162
/* mkdir is special... */
194-
err = ovl_mkdir_real(ofs, dir, &newdentry, attr->mode);
163+
newdentry = ovl_do_mkdir(ofs, dir, newdentry, attr->mode);
164+
err = PTR_ERR_OR_ZERO(newdentry);
195165
break;
196166

197167
case S_IFCHR:
@@ -219,7 +189,8 @@ struct dentry *ovl_create_real(struct ovl_fs *ofs, struct inode *dir,
219189
}
220190
out:
221191
if (err) {
222-
dput(newdentry);
192+
if (!IS_ERR(newdentry))
193+
dput(newdentry);
223194
return ERR_PTR(err);
224195
}
225196
return newdentry;

fs/overlayfs/overlayfs.h

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -241,13 +241,14 @@ static inline int ovl_do_create(struct ovl_fs *ofs,
241241
return err;
242242
}
243243

244-
static inline int ovl_do_mkdir(struct ovl_fs *ofs,
245-
struct inode *dir, struct dentry *dentry,
246-
umode_t mode)
244+
static inline struct dentry *ovl_do_mkdir(struct ovl_fs *ofs,
245+
struct inode *dir,
246+
struct dentry *dentry,
247+
umode_t mode)
247248
{
248-
int err = vfs_mkdir(ovl_upper_mnt_idmap(ofs), dir, dentry, mode);
249-
pr_debug("mkdir(%pd2, 0%o) = %i\n", dentry, mode, err);
250-
return err;
249+
dentry = vfs_mkdir(ovl_upper_mnt_idmap(ofs), dir, dentry, mode);
250+
pr_debug("mkdir(%pd2, 0%o) = %i\n", dentry, mode, PTR_ERR_OR_ZERO(dentry));
251+
return dentry;
251252
}
252253

253254
static inline int ovl_do_mknod(struct ovl_fs *ofs,
@@ -838,8 +839,6 @@ struct ovl_cattr {
838839

839840
#define OVL_CATTR(m) (&(struct ovl_cattr) { .mode = (m) })
840841

841-
int ovl_mkdir_real(struct ovl_fs *ofs, struct inode *dir,
842-
struct dentry **newdentry, umode_t mode);
843842
struct dentry *ovl_create_real(struct ovl_fs *ofs,
844843
struct inode *dir, struct dentry *newdentry,
845844
struct ovl_cattr *attr);

fs/overlayfs/super.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -327,9 +327,10 @@ static struct dentry *ovl_workdir_create(struct ovl_fs *ofs,
327327
goto retry;
328328
}
329329

330-
err = ovl_mkdir_real(ofs, dir, &work, attr.ia_mode);
331-
if (err)
332-
goto out_dput;
330+
work = ovl_do_mkdir(ofs, dir, work, attr.ia_mode);
331+
err = PTR_ERR(work);
332+
if (IS_ERR(work))
333+
goto out_err;
333334

334335
/* Weird filesystem returning with hashed negative (kernfs)? */
335336
err = -EINVAL;

0 commit comments

Comments
 (0)