On Tue, Jul 10, 2018 at 10:17:36AM +1000, Stephen Rothwell wrote: > --- a/fs/open.c > +++ b/fs/open.c > @@@ -731,7 -732,6 +721,7 @@@ static int do_dentry_open(struct file * > static const struct file_operations empty_fops = {}; > int error; > > - WARN_ON(f->f_mode & ~FMODE_NOACCOUNT); > ++ WARN_ON(f->f_mode & ~ (FMODE_NOACCOUNT | FMODE_CREATED)); > f->f_mode |= OPEN_FMODE(f->f_flags) | FMODE_LSEEK | > FMODE_PREAD | FMODE_PWRITE;
That part is sane > +/** > + * path_open() - Open an inode by a particular name. > + * @path: The name of the file. > + * @flags: The O_ flags used to open this file. > + * @inode: The inode to open. > + * @cred: The task's credentials used when opening this file. > + * > + * Context: Process context. > + * Return: A pointer to a struct file or an IS_ERR pointer. Cannot return > NULL. > + */ > +struct file *path_open(const struct path *path, int flags, struct inode > *inode, > + const struct cred *cred, bool account) > +{ > + struct file *file; > + int error; > + > + file = __get_empty_filp(account); > + if (IS_ERR(file)) > + return file; > > + file->f_flags = flags; > file->f_path = *path; > - return do_dentry_open(file, d_backing_inode(dentry), NULL, cred); > + error = do_dentry_open(file, inode, NULL, cred); > + if (error) { > - put_filp(file); > ++ if (file->f_mode & FMODE_OPENED) > ++ fput(file); > ++ else > ++ put_filp(file); > + return ERR_PTR(error); > + } > + > - error = open_check_o_direct(file); > - if (error) { > - fput(file); > - file = ERR_PTR(error); > - } > - > + return file; > } > +EXPORT_SYMBOL(path_open); First of all, I'm still not at all convinced that this "noaccount" thing is sane, especially since path_open() is exported. But that aside, __get_empty_filp() needs to be shot, just for the name and calling conventions alone. It gets a bullshit argument (bool account) *AND* does not get the argument it does need. Note that the first thing get_empty_filp() (now __get...) does is const struct cred *cred = current_cred(); followed by f->f_cred = get_cred(cred); Now look at path_open(). What happens to the cred argument it gets? It goes to do_dentry_open(), where it gets passed to security_file_open() and not used by anything else. In security_file_open() we have it passed to ret = call_int_hook(file_open, 0, file, cred); and there are three instances of ->file_open() - apparmor, selinux and tomoyo. The last one ignores cred entirely; the other two do checks based on it, but *all* of them leave file->f_cred as it was. This is not a new crap. It had been inherited from dentry_open(), which got it from "CRED: Pass credentials through dentry_open()" back in 2008. Note that * among the callers of dentry_open() (20) and vfs_open() (2 more) only these fs/cachefiles/rdwr.c:913: file = dentry_open(&path, O_RDWR | O_LARGEFILE, cache->cache_cred); security/apparmor/file.c:695: devnull = dentry_open(&aa_null, O_RDWR, cred); security/selinux/hooks.c:2628: devnull = dentry_open(&selinux_null, O_RDWR, cred); get cred != current_cred(). Which helps masking the issue, but makes the decision to add that argument (instead of a separate helper) rather dubious. * overlayfs itself appears to *have* run into the problem, judging by old_cred = ovl_override_creds(inode->i_sb); realfile = path_open(&file->f_path, file->f_flags | O_NOATIME, realinode, current_cred(), false); revert_creds(old_cred); in there. Folks, if you have to go to that kind of contortions, why not do it right? * add static __alloc_file(cred), which would get cred pointer (and not use current_cred() internally), allocated a file (without bothering with nr_files) and returned it * have alloc_empty_file(cred) that would do the song and dance with nr_files (and used __alloc_file() internally). * use that as a replacement for get_empty_filp() - path_openat() would *probably* use current_cred() for argument, alloc_file() definitely would and dentry_open() would pass its cred argument. * in internal.h, static inline alloc_empty_file_noaccount(cred) would use __alloc_file() and set FMODE_NOACCOUNT in case of success. * do_dentry_open() loses the fucking cred argument - it should be in file->f_cred. * vfs_open() goes away - in your branch it's absolutely pointless. * path_open() loses its 'account' argument - it's always false. Uses alloc_empty_file_noaccount() to allocate the sucker. And for fsck sake, pass it the creds you want to use rather than playing that kind of games with override/revert.