/* * Ideally this function should call VFS:do_last() in order to keep all its * checkings. But it is very hard for aufs to regenerate several VFS internal * structure such as nameidata. This is a second (or third) best approach. * cf. linux/fs/namei.c:do_last(), lookup_open() and atomic_open(). */ int vfsub_atomic_open(struct inode *dir, struct dentry *dentry, struct vfsub_aopen_args *args, struct au_branch *br) { int err; struct file *file = args->file; /* copied from linux/fs/namei.c:atomic_open() */ struct dentry *const DENTRY_NOT_SET = (void *)-1UL; IMustLock(dir); AuDebugOn(!dir->i_op->atomic_open); err = au_br_test_oflag(args->open_flag, br); if (unlikely(err)) goto out; args->file->f_path.dentry = DENTRY_NOT_SET; args->file->f_path.mnt = au_br_mnt(br); err = dir->i_op->atomic_open(dir, dentry, file, args->open_flag, args->create_mode, args->opened); if (err >= 0) { /* some filesystems don't set FILE_CREATED while succeeded? */ if (*args->opened & FILE_CREATED) fsnotify_create(dir, dentry); } else goto out; if (!err) { /* todo: call VFS:may_open() here */ err = open_check_o_direct(file); /* todo: ima_file_check() too? */ if (!err && (args->open_flag & __FMODE_EXEC)) err = deny_write_access(file); if (unlikely(err)) /* note that the file is created and still opened */ goto out; } atomic_inc(&br->br_count); fsnotify_open(file); out: return err; }
/* common functions to regular file and dir */ struct file *au_h_open(struct dentry *dentry, aufs_bindex_t bindex, int flags, struct file *file, int force_wr) { struct file *h_file; struct dentry *h_dentry; struct inode *h_inode; struct super_block *sb; struct au_branch *br; struct path h_path; int err; /* a race condition can happen between open and unlink/rmdir */ h_file = ERR_PTR(-ENOENT); h_dentry = au_h_dptr(dentry, bindex); if (au_test_nfsd() && !h_dentry) goto out; h_inode = h_dentry->d_inode; if (au_test_nfsd() && !h_inode) goto out; spin_lock(&h_dentry->d_lock); err = (!d_unhashed(dentry) && d_unlinked(h_dentry)) || !h_inode /* || !dentry->d_inode->i_nlink */ ; spin_unlock(&h_dentry->d_lock); if (unlikely(err)) goto out; sb = dentry->d_sb; br = au_sbr(sb, bindex); err = au_br_test_oflag(flags, br); h_file = ERR_PTR(err); if (unlikely(err)) goto out; /* drop flags for writing */ if (au_test_ro(sb, bindex, dentry->d_inode)) { if (force_wr && !(flags & O_WRONLY)) force_wr = 0; flags = au_file_roflags(flags); if (force_wr) { h_file = ERR_PTR(-EROFS); flags = au_file_roflags(flags); if (unlikely(vfsub_native_ro(h_inode) || IS_APPEND(h_inode))) goto out; flags &= ~O_ACCMODE; flags |= O_WRONLY; } } flags &= ~O_CREAT; atomic_inc(&br->br_count); h_path.dentry = h_dentry; h_path.mnt = au_br_mnt(br); h_file = vfsub_dentry_open(&h_path, flags); if (IS_ERR(h_file)) goto out_br; if (flags & __FMODE_EXEC) { err = deny_write_access(h_file); if (unlikely(err)) { fput(h_file); h_file = ERR_PTR(err); goto out_br; } } fsnotify_open(h_file); goto out; /* success */ out_br: atomic_dec(&br->br_count); out: return h_file; }