/* caller holds i_mutex on workdir */ static struct dentry *ovl_whiteout(struct dentry *workdir) { int err; struct dentry *whiteout; struct inode *wdir = workdir->d_inode; whiteout = ovl_lookup_temp(workdir); if (IS_ERR(whiteout)) return whiteout; err = ovl_do_whiteout(wdir, whiteout); if (err) { dput(whiteout); whiteout = ERR_PTR(err); } return whiteout; }
static struct dentry *ovl_clear_empty(struct dentry *dentry, struct list_head *list) { struct dentry *workdir = ovl_workdir(dentry); struct inode *wdir = workdir->d_inode; struct dentry *upperdir = ovl_dentry_upper(dentry->d_parent); struct inode *udir = upperdir->d_inode; struct path upperpath; struct dentry *upper; struct dentry *opaquedir; struct kstat stat; int err; if (WARN_ON(!workdir)) return ERR_PTR(-EROFS); err = ovl_lock_rename_workdir(workdir, upperdir); if (err) goto out; ovl_path_upper(dentry, &upperpath); err = vfs_getattr(&upperpath, &stat); if (err) goto out_unlock; err = -ESTALE; if (!S_ISDIR(stat.mode)) goto out_unlock; upper = upperpath.dentry; if (upper->d_parent->d_inode != udir) goto out_unlock; opaquedir = ovl_lookup_temp(workdir, dentry); err = PTR_ERR(opaquedir); if (IS_ERR(opaquedir)) goto out_unlock; err = ovl_create_real(wdir, opaquedir, &(struct cattr){.mode = stat.mode}, NULL, true);
struct dentry *ovl_create_temp(struct dentry *workdir, struct ovl_cattr *attr) { return ovl_create_real(d_inode(workdir), ovl_lookup_temp(workdir), attr); }
static int ovl_create_over_whiteout(struct dentry *dentry, struct inode *inode, struct kstat *stat, const char *link, struct dentry *hardlink) { struct dentry *workdir = ovl_workdir(dentry); struct inode *wdir = workdir->d_inode; struct dentry *upperdir = ovl_dentry_upper(dentry->d_parent); struct inode *udir = upperdir->d_inode; struct dentry *upper; struct dentry *newdentry; int err; if (WARN_ON(!workdir)) return -EROFS; err = ovl_lock_rename_workdir(workdir, upperdir); if (err) goto out; newdentry = ovl_lookup_temp(workdir, dentry); err = PTR_ERR(newdentry); if (IS_ERR(newdentry)) goto out_unlock; upper = lookup_one_len(dentry->d_name.name, upperdir, dentry->d_name.len); err = PTR_ERR(upper); if (IS_ERR(upper)) goto out_dput; err = ovl_create_real(wdir, newdentry, stat, link, hardlink, true); if (err) goto out_dput2; if (S_ISDIR(stat->mode)) { err = ovl_set_opaque(newdentry); if (err) goto out_cleanup; err = ovl_do_rename(wdir, newdentry, udir, upper, RENAME_EXCHANGE); if (err) goto out_cleanup; ovl_cleanup(wdir, upper); } else { err = ovl_do_rename(wdir, newdentry, udir, upper, 0); if (err) goto out_cleanup; } ovl_dentry_version_inc(dentry->d_parent); ovl_dentry_update(dentry, newdentry); ovl_copyattr(newdentry->d_inode, inode); d_instantiate(dentry, inode); newdentry = NULL; out_dput2: dput(upper); out_dput: dput(newdentry); out_unlock: unlock_rename(workdir, upperdir); out: return err; out_cleanup: ovl_cleanup(wdir, newdentry); goto out_dput2; }
static struct dentry *ovl_clear_empty(struct dentry *dentry, struct list_head *list) { struct dentry *workdir = ovl_workdir(dentry); struct inode *wdir = workdir->d_inode; struct dentry *upperdir = ovl_dentry_upper(dentry->d_parent); struct inode *udir = upperdir->d_inode; struct path upperpath; struct dentry *upper; struct dentry *opaquedir; struct kstat stat; int err; if (WARN_ON(!workdir)) return ERR_PTR(-EROFS); err = ovl_lock_rename_workdir(workdir, upperdir); if (err) goto out; ovl_path_upper(dentry, &upperpath); err = vfs_getattr(&upperpath, &stat); if (err) goto out_unlock; err = -ESTALE; if (!S_ISDIR(stat.mode)) goto out_unlock; upper = upperpath.dentry; if (upper->d_parent->d_inode != udir) goto out_unlock; opaquedir = ovl_lookup_temp(workdir, dentry); err = PTR_ERR(opaquedir); if (IS_ERR(opaquedir)) goto out_unlock; err = ovl_create_real(wdir, opaquedir, &stat, NULL, NULL, true); if (err) goto out_dput; err = ovl_copy_xattr(upper, opaquedir); if (err) goto out_cleanup; err = ovl_set_opaque(opaquedir); if (err) goto out_cleanup; inode_lock(opaquedir->d_inode); err = ovl_set_attr(opaquedir, &stat); inode_unlock(opaquedir->d_inode); if (err) goto out_cleanup; err = ovl_do_rename(wdir, opaquedir, udir, upper, RENAME_EXCHANGE); if (err) goto out_cleanup; ovl_cleanup_whiteouts(upper, list); ovl_cleanup(wdir, upper); unlock_rename(workdir, upperdir); /* dentry's upper doesn't match now, get rid of it */ d_drop(dentry); return opaquedir; out_cleanup: ovl_cleanup(wdir, opaquedir); out_dput: dput(opaquedir); out_unlock: unlock_rename(workdir, upperdir); out: return ERR_PTR(err); }
static int ovl_create_over_whiteout(struct dentry *dentry, struct inode *inode, struct kstat *stat, const char *link, struct dentry *hardlink) { struct dentry *workdir = ovl_workdir(dentry); struct inode *wdir = workdir->d_inode; struct dentry *upperdir = ovl_dentry_upper(dentry->d_parent); struct inode *udir = upperdir->d_inode; struct dentry *upper; struct dentry *newdentry; int err; struct posix_acl *acl, *default_acl; if (WARN_ON(!workdir)) return -EROFS; if (!hardlink) { err = posix_acl_create(dentry->d_parent->d_inode, &stat->mode, &default_acl, &acl); if (err) return err; } err = ovl_lock_rename_workdir(workdir, upperdir); if (err) goto out; newdentry = ovl_lookup_temp(workdir, dentry); err = PTR_ERR(newdentry); if (IS_ERR(newdentry)) goto out_unlock; upper = lookup_one_len(dentry->d_name.name, upperdir, dentry->d_name.len); err = PTR_ERR(upper); if (IS_ERR(upper)) goto out_dput; err = ovl_create_real(wdir, newdentry, stat, link, hardlink, true); if (err) goto out_dput2; /* * mode could have been mutilated due to umask (e.g. sgid directory) */ if (!hardlink && !S_ISLNK(stat->mode) && newdentry->d_inode->i_mode != stat->mode) { struct iattr attr = { .ia_valid = ATTR_MODE, .ia_mode = stat->mode, }; inode_lock(newdentry->d_inode); err = notify_change(newdentry, &attr, NULL); inode_unlock(newdentry->d_inode); if (err) goto out_cleanup; } if (!hardlink) { err = ovl_set_upper_acl(newdentry, XATTR_NAME_POSIX_ACL_ACCESS, acl); if (err) goto out_cleanup; err = ovl_set_upper_acl(newdentry, XATTR_NAME_POSIX_ACL_DEFAULT, default_acl); if (err) goto out_cleanup; } if (!hardlink && S_ISDIR(stat->mode)) { err = ovl_set_opaque(newdentry); if (err) goto out_cleanup; err = ovl_do_rename(wdir, newdentry, udir, upper, RENAME_EXCHANGE); if (err) goto out_cleanup; ovl_cleanup(wdir, upper); } else { err = ovl_do_rename(wdir, newdentry, udir, upper, 0); if (err) goto out_cleanup; } ovl_instantiate(dentry, inode, newdentry, !!hardlink); newdentry = NULL; out_dput2: dput(upper); out_dput: dput(newdentry); out_unlock: unlock_rename(workdir, upperdir); out: if (!hardlink) { posix_acl_release(acl); posix_acl_release(default_acl); } return err; out_cleanup: ovl_cleanup(wdir, newdentry); goto out_dput2; } static int ovl_create_or_link(struct dentry *dentry, struct inode *inode, struct kstat *stat, const char *link, struct dentry *hardlink) { int err; const struct cred *old_cred; struct cred *override_cred; err = ovl_copy_up(dentry->d_parent); if (err) return err; old_cred = ovl_override_creds(dentry->d_sb); err = -ENOMEM; override_cred = prepare_creds(); if (override_cred) { override_cred->fsuid = inode->i_uid; override_cred->fsgid = inode->i_gid; put_cred(override_creds(override_cred)); put_cred(override_cred); if (!ovl_dentry_is_opaque(dentry)) err = ovl_create_upper(dentry, inode, stat, link, hardlink); else err = ovl_create_over_whiteout(dentry, inode, stat, link, hardlink); } revert_creds(old_cred); if (!err) { struct inode *realinode = d_inode(ovl_dentry_upper(dentry)); WARN_ON(inode->i_mode != realinode->i_mode); WARN_ON(!uid_eq(inode->i_uid, realinode->i_uid)); WARN_ON(!gid_eq(inode->i_gid, realinode->i_gid)); } return err; }