On Mon, Oct 07, 2024 at 05:55:40PM +0100, Al Viro wrote: > > +static int ovl_upper_fdget(const struct file *file, struct fd *real, bool data) > > +{ > > + struct dentry *dentry = file_dentry(file); > > + struct path realpath; > > + enum ovl_path_type type; > > + > > + if (data) > > + type = ovl_path_realdata(dentry, &realpath); > > ... but not here. > > I can see the point of not doing that in ->fsync() after we'd already > done ovl_verify_lowerdata() at open time, but what's different about > ->read_iter() and friends that also come only after ->open()? > IOW, why is fdatasync() different from other data-access cases? Nevermind that one - the answer is that ovl_path_realdata() calls ovl_path_lowerdata() only in case when it sees !OVL_TYPE_UPPER(type) || OVL_TYPE_MERGE(type), which guarantees that the type check below that if (data) will fail anyway (check being (!OVL_TYPE_UPPER(type) || (data && OVL_TYPE_MERGE(type))). So this reduces the fdatasync case to if (!OVL_TYPE_UPPER(type) || OVL_TYPE_MERGE(type)) fail; ovl_path_upper(dentry, &realpath); just as the fsync case reduces to if (!OVL_TYPE_UPPER(type)) fail; ovl_path_upper(dentry, &realpath); making any lowerpath-related stuff irrelevant.