Skip to content

Commit f0c0ac8

Browse files
amir73ilgregkh
authored andcommitted
ovl: support encoding fid from inode with no alias
commit c45beeb upstream. Dmitry Safonov reported that a WARN_ON() assertion can be trigered by userspace when calling inotify_show_fdinfo() for an overlayfs watched inode, whose dentry aliases were discarded with drop_caches. The WARN_ON() assertion in inotify_show_fdinfo() was removed, because it is possible for encoding file handle to fail for other reason, but the impact of failing to encode an overlayfs file handle goes beyond this assertion. As shown in the LTP test case mentioned in the link below, failure to encode an overlayfs file handle from a non-aliased inode also leads to failure to report an fid with FAN_DELETE_SELF fanotify events. As Dmitry notes in his analyzis of the problem, ovl_encode_fh() fails if it cannot find an alias for the inode, but this failure can be fixed. ovl_encode_fh() seldom uses the alias and in the case of non-decodable file handles, as is often the case with fanotify fid info, ovl_encode_fh() never needs to use the alias to encode a file handle. Defer finding an alias until it is actually needed so ovl_encode_fh() will not fail in the common case of FAN_DELETE_SELF fanotify events. Fixes: 16aac5a ("ovl: support encoding non-decodable file handles") Reported-by: Dmitry Safonov <dima@arista.com> Closes: https://lore.kernel.org/linux-fsdevel/CAOQ4uxiie81voLZZi2zXS1BziXZCM24nXqPAxbu8kxXCUWdwOg@mail.gmail.com/ Signed-off-by: Amir Goldstein <amir73il@gmail.com> Link: https://lore.kernel.org/r/20250105162404.357058-3-amir73il@gmail.com Signed-off-by: Christian Brauner <brauner@kernel.org> Signed-off-by: Sasha Levin <sashal@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent 955a355 commit f0c0ac8

File tree

1 file changed

+25
-21
lines changed

1 file changed

+25
-21
lines changed

fs/overlayfs/export.c

Lines changed: 25 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -181,35 +181,37 @@ static int ovl_connect_layer(struct dentry *dentry)
181181
*
182182
* Return 0 for upper file handle, > 0 for lower file handle or < 0 on error.
183183
*/
184-
static int ovl_check_encode_origin(struct dentry *dentry)
184+
static int ovl_check_encode_origin(struct inode *inode)
185185
{
186-
struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
186+
struct ovl_fs *ofs = OVL_FS(inode->i_sb);
187187
bool decodable = ofs->config.nfs_export;
188+
struct dentry *dentry;
189+
int err;
188190

189191
/* No upper layer? */
190192
if (!ovl_upper_mnt(ofs))
191193
return 1;
192194

193195
/* Lower file handle for non-upper non-decodable */
194-
if (!ovl_dentry_upper(dentry) && !decodable)
196+
if (!ovl_inode_upper(inode) && !decodable)
195197
return 1;
196198

197199
/* Upper file handle for pure upper */
198-
if (!ovl_dentry_lower(dentry))
200+
if (!ovl_inode_lower(inode))
199201
return 0;
200202

201203
/*
202204
* Root is never indexed, so if there's an upper layer, encode upper for
203205
* root.
204206
*/
205-
if (dentry == dentry->d_sb->s_root)
207+
if (inode == d_inode(inode->i_sb->s_root))
206208
return 0;
207209

208210
/*
209211
* Upper decodable file handle for non-indexed upper.
210212
*/
211-
if (ovl_dentry_upper(dentry) && decodable &&
212-
!ovl_test_flag(OVL_INDEX, d_inode(dentry)))
213+
if (ovl_inode_upper(inode) && decodable &&
214+
!ovl_test_flag(OVL_INDEX, inode))
213215
return 0;
214216

215217
/*
@@ -218,17 +220,25 @@ static int ovl_check_encode_origin(struct dentry *dentry)
218220
* ovl_connect_layer() will try to make origin's layer "connected" by
219221
* copying up a "connectable" ancestor.
220222
*/
221-
if (d_is_dir(dentry) && decodable)
222-
return ovl_connect_layer(dentry);
223+
if (!decodable || !S_ISDIR(inode->i_mode))
224+
return 1;
225+
226+
dentry = d_find_any_alias(inode);
227+
if (!dentry)
228+
return -ENOENT;
229+
230+
err = ovl_connect_layer(dentry);
231+
dput(dentry);
232+
if (err < 0)
233+
return err;
223234

224235
/* Lower file handle for indexed and non-upper dir/non-dir */
225236
return 1;
226237
}
227238

228-
static int ovl_dentry_to_fid(struct ovl_fs *ofs, struct dentry *dentry,
239+
static int ovl_dentry_to_fid(struct ovl_fs *ofs, struct inode *inode,
229240
u32 *fid, int buflen)
230241
{
231-
struct inode *inode = d_inode(dentry);
232242
struct ovl_fh *fh = NULL;
233243
int err, enc_lower;
234244
int len;
@@ -237,7 +247,7 @@ static int ovl_dentry_to_fid(struct ovl_fs *ofs, struct dentry *dentry,
237247
* Check if we should encode a lower or upper file handle and maybe
238248
* copy up an ancestor to make lower file handle connectable.
239249
*/
240-
err = enc_lower = ovl_check_encode_origin(dentry);
250+
err = enc_lower = ovl_check_encode_origin(inode);
241251
if (enc_lower < 0)
242252
goto fail;
243253

@@ -257,28 +267,22 @@ static int ovl_dentry_to_fid(struct ovl_fs *ofs, struct dentry *dentry,
257267
return err;
258268

259269
fail:
260-
pr_warn_ratelimited("failed to encode file handle (%pd2, err=%i)\n",
261-
dentry, err);
270+
pr_warn_ratelimited("failed to encode file handle (ino=%lu, err=%i)\n",
271+
inode->i_ino, err);
262272
goto out;
263273
}
264274

265275
static int ovl_encode_fh(struct inode *inode, u32 *fid, int *max_len,
266276
struct inode *parent)
267277
{
268278
struct ovl_fs *ofs = OVL_FS(inode->i_sb);
269-
struct dentry *dentry;
270279
int bytes, buflen = *max_len << 2;
271280

272281
/* TODO: encode connectable file handles */
273282
if (parent)
274283
return FILEID_INVALID;
275284

276-
dentry = d_find_any_alias(inode);
277-
if (!dentry)
278-
return FILEID_INVALID;
279-
280-
bytes = ovl_dentry_to_fid(ofs, dentry, fid, buflen);
281-
dput(dentry);
285+
bytes = ovl_dentry_to_fid(ofs, inode, fid, buflen);
282286
if (bytes <= 0)
283287
return FILEID_INVALID;
284288

0 commit comments

Comments
 (0)