static int ovl_do_lookup(struct dentry *dentry) { struct ovl_entry *oe; struct dentry *upperdir; struct dentry *lowerdir; struct dentry *upperdentry = NULL; struct dentry *lowerdentry = NULL; struct inode *inode = NULL; int err; err = -ENOMEM; oe = ovl_alloc_entry(); if (!oe) goto out; upperdir = ovl_dentry_upper(dentry->d_parent); lowerdir = ovl_dentry_lower(dentry->d_parent); if (upperdir) { upperdentry = ovl_lookup_real(upperdir, &dentry->d_name); err = PTR_ERR(upperdentry); if (IS_ERR(upperdentry)) goto out_put_dir; if (lowerdir && upperdentry && (S_ISLNK(upperdentry->d_inode->i_mode) || S_ISDIR(upperdentry->d_inode->i_mode))) { const struct cred *old_cred; struct cred *override_cred; err = -ENOMEM; override_cred = prepare_creds(); if (!override_cred) goto out_dput_upper; /* CAP_SYS_ADMIN needed for getxattr */ cap_raise(override_cred->cap_effective, CAP_SYS_ADMIN); old_cred = override_creds(override_cred); if (ovl_is_opaquedir(upperdentry)) { oe->opaque = true; } else if (ovl_is_whiteout(upperdentry)) { dput(upperdentry); upperdentry = NULL; oe->opaque = true; } revert_creds(old_cred); put_cred(override_cred); } } if (lowerdir && !oe->opaque) { lowerdentry = ovl_lookup_real(lowerdir, &dentry->d_name); err = PTR_ERR(lowerdentry); if (IS_ERR(lowerdentry)) goto out_dput_upper; } if (lowerdentry && upperdentry && (!S_ISDIR(upperdentry->d_inode->i_mode) || !S_ISDIR(lowerdentry->d_inode->i_mode))) { dput(lowerdentry); lowerdentry = NULL; oe->opaque = true; } if (lowerdentry || upperdentry) { struct dentry *realdentry; realdentry = upperdentry ? upperdentry : lowerdentry; err = -ENOMEM; inode = ovl_new_inode(dentry->d_sb, realdentry->d_inode->i_mode, oe); if (!inode) goto out_dput; ovl_copyattr(realdentry->d_inode, inode); } if (upperdentry) oe->__upperdentry = dget(upperdentry); if (lowerdentry) oe->lowerdentry = lowerdentry; dentry->d_fsdata = oe; dentry->d_op = &ovl_dentry_operations; d_add(dentry, inode); return 0; out_dput: dput(lowerdentry); out_dput_upper: dput(upperdentry); out_put_dir: kfree(oe); out: return err; }
static int ovl_do_lookup(struct dentry *dentry) { struct ovl_entry *oe; struct dentry *upperdir; struct dentry *lowerdir; struct dentry *upperdentry = NULL; struct dentry *lowerdentry = NULL; struct inode *inode = NULL; int err; err = -ENOMEM; oe = ovl_alloc_entry(); if (!oe) goto out; upperdir = ovl_dentry_upper(dentry->d_parent); lowerdir = ovl_dentry_lower(dentry->d_parent); if (upperdir) { upperdentry = ovl_lookup_real(upperdir, &dentry->d_name); err = PTR_ERR(upperdentry); if (IS_ERR(upperdentry)) goto out_put_dir; if (lowerdir && upperdentry) { if (ovl_is_whiteout(upperdentry)) { dput(upperdentry); upperdentry = NULL; oe->opaque = true; } else if (ovl_is_opaquedir(upperdentry)) { oe->opaque = true; } } } if (lowerdir && !oe->opaque) { lowerdentry = ovl_lookup_real(lowerdir, &dentry->d_name); err = PTR_ERR(lowerdentry); if (IS_ERR(lowerdentry)) goto out_dput_upper; } if (lowerdentry && upperdentry && (!S_ISDIR(upperdentry->d_inode->i_mode) || !S_ISDIR(lowerdentry->d_inode->i_mode))) { dput(lowerdentry); lowerdentry = NULL; oe->opaque = true; } if (lowerdentry || upperdentry) { struct dentry *realdentry; realdentry = upperdentry ? upperdentry : lowerdentry; err = -ENOMEM; inode = ovl_new_inode(dentry->d_sb, realdentry->d_inode->i_mode, oe); if (!inode) goto out_dput; ovl_copyattr(realdentry->d_inode, inode); } if (upperdentry) oe->__upperdentry = dget(upperdentry); if (lowerdentry) oe->lowerdentry = lowerdentry; dentry->d_fsdata = oe; dentry->d_op = &ovl_dentry_operations; d_add(dentry, inode); return 0; out_dput: dput(lowerdentry); out_dput_upper: dput(upperdentry); out_put_dir: kfree(oe); out: return err; }