/* Common operations required to be done after creation of file on upper */
static void ovl_instantiate(struct dentry *dentry, struct inode *inode,
- struct dentry *newdentry)
+ struct dentry *newdentry, bool hardlink)
{
ovl_dentry_version_inc(dentry->d_parent);
ovl_dentry_update(dentry, newdentry);
- ovl_inode_update(inode, d_inode(newdentry));
- ovl_copyattr(newdentry->d_inode, inode);
+ if (!hardlink) {
+ ovl_inode_update(inode, d_inode(newdentry));
+ ovl_copyattr(newdentry->d_inode, inode);
+ } else {
+ WARN_ON(ovl_inode_real(inode, NULL) != d_inode(newdentry));
+ inc_nlink(inode);
+ }
d_instantiate(dentry, inode);
}
if (err)
goto out_dput;
- ovl_instantiate(dentry, inode, newdentry);
+ ovl_instantiate(dentry, inode, newdentry, !!hardlink);
newdentry = NULL;
out_dput:
dput(newdentry);
/*
* mode could have been mutilated due to umask (e.g. sgid directory)
*/
- if (!S_ISLNK(stat->mode) && newdentry->d_inode->i_mode != stat->mode) {
+ if (!hardlink &&
+ !S_ISLNK(stat->mode) && newdentry->d_inode->i_mode != stat->mode) {
struct iattr attr = {
.ia_valid = ATTR_MODE,
.ia_mode = stat->mode,
goto out_cleanup;
}
- if (S_ISDIR(stat->mode)) {
+ if (!hardlink && S_ISDIR(stat->mode)) {
err = ovl_set_opaque(newdentry);
if (err)
goto out_cleanup;
if (err)
goto out_cleanup;
}
- ovl_instantiate(dentry, inode, newdentry);
+ ovl_instantiate(dentry, inode, newdentry, !!hardlink);
newdentry = NULL;
out_dput2:
dput(upper);
goto out_dput2;
}
-static int ovl_create_or_link(struct dentry *dentry, int mode, dev_t rdev,
- const char *link, struct dentry *hardlink)
+static int ovl_create_or_link(struct dentry *dentry, struct inode *inode,
+ struct kstat *stat, const char *link,
+ struct dentry *hardlink)
{
int err;
- struct inode *inode;
const struct cred *old_cred;
struct cred *override_cred;
- struct kstat stat = {
- .rdev = rdev,
- };
-
- err = -ENOMEM;
- inode = ovl_new_inode(dentry->d_sb, mode);
- if (!inode)
- goto out;
err = ovl_copy_up(dentry->d_parent);
if (err)
- goto out_iput;
-
- inode_init_owner(inode, dentry->d_parent->d_inode, mode);
- stat.mode = inode->i_mode;
+ return err;
old_cred = ovl_override_creds(dentry->d_sb);
err = -ENOMEM;
put_cred(override_cred);
if (!ovl_dentry_is_opaque(dentry))
- err = ovl_create_upper(dentry, inode, &stat, link,
+ err = ovl_create_upper(dentry, inode, stat, link,
hardlink);
else
- err = ovl_create_over_whiteout(dentry, inode, &stat,
+ err = ovl_create_over_whiteout(dentry, inode, stat,
link, hardlink);
}
revert_creds(old_cred);
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));
- inode = NULL;
}
-out_iput:
- iput(inode);
-out:
return err;
}
const char *link)
{
int err;
+ struct inode *inode;
+ struct kstat stat = {
+ .rdev = rdev,
+ };
err = ovl_want_write(dentry);
- if (!err) {
- err = ovl_create_or_link(dentry, mode, rdev, link, NULL);
- ovl_drop_write(dentry);
- }
+ if (err)
+ goto out;
+
+ err = -ENOMEM;
+ inode = ovl_new_inode(dentry->d_sb, mode);
+ if (!inode)
+ goto out_drop_write;
+
+ inode_init_owner(inode, dentry->d_parent->d_inode, mode);
+ stat.mode = inode->i_mode;
+
+ err = ovl_create_or_link(dentry, inode, &stat, link, NULL);
+ if (err)
+ iput(inode);
+out_drop_write:
+ ovl_drop_write(dentry);
+out:
return err;
}
struct dentry *new)
{
int err;
- struct dentry *upper;
+ struct inode *inode;
err = ovl_want_write(old);
if (err)
if (err)
goto out_drop_write;
- upper = ovl_dentry_upper(old);
- err = ovl_create_or_link(new, upper->d_inode->i_mode, 0, NULL, upper);
+ inode = d_inode(old);
+ ihold(inode);
+
+ err = ovl_create_or_link(new, inode, NULL, NULL, ovl_dentry_upper(old));
+ if (err)
+ iput(inode);
out_drop_write:
ovl_drop_write(old);
else
err = ovl_remove_and_whiteout(dentry, is_dir);
revert_creds(old_cred);
+ if (!err && !is_dir)
+ drop_nlink(dentry->d_inode);
out_drop_write:
ovl_drop_write(dentry);
out:
.update_time = ovl_update_time,
};
-struct inode *ovl_new_inode(struct super_block *sb, umode_t mode)
+static void ovl_fill_inode(struct inode *inode, umode_t mode)
{
- struct inode *inode;
-
- inode = new_inode(sb);
- if (!inode)
- return NULL;
-
inode->i_ino = get_next_ino();
inode->i_mode = mode;
inode->i_flags |= S_NOCMTIME;
inode->i_op = &ovl_symlink_inode_operations;
break;
+ default:
+ WARN(1, "illegal file type: %i\n", mode);
+ /* Fall through */
+
case S_IFREG:
case S_IFSOCK:
case S_IFBLK:
case S_IFIFO:
inode->i_op = &ovl_file_inode_operations;
break;
+ }
+}
- default:
- WARN(1, "illegal file type: %i\n", mode);
- iput(inode);
- inode = NULL;
+struct inode *ovl_new_inode(struct super_block *sb, umode_t mode)
+{
+ struct inode *inode;
+
+ inode = new_inode(sb);
+ if (inode)
+ ovl_fill_inode(inode, mode);
+
+ return inode;
+}
+
+static int ovl_inode_test(struct inode *inode, void *data)
+{
+ return ovl_inode_real(inode, NULL) == data;
+}
+
+static int ovl_inode_set(struct inode *inode, void *data)
+{
+ inode->i_private = (void *) (((unsigned long) data) | OVL_ISUPPER_MASK);
+ return 0;
+}
+
+struct inode *ovl_get_inode(struct super_block *sb, struct inode *realinode)
+
+{
+ struct inode *inode;
+
+ inode = iget5_locked(sb, (unsigned long) realinode,
+ ovl_inode_test, ovl_inode_set, realinode);
+ if (inode && inode->i_state & I_NEW) {
+ ovl_fill_inode(inode, realinode->i_mode);
+ set_nlink(inode, realinode->i_nlink);
+ unlock_new_inode(inode);
}
return inode;
void ovl_inode_update(struct inode *inode, struct inode *upperinode)
{
WARN_ON(!upperinode);
+ WARN_ON(!inode_unhashed(inode));
WRITE_ONCE(inode->i_private,
(unsigned long) upperinode | OVL_ISUPPER_MASK);
+ if (!S_ISDIR(upperinode->i_mode))
+ __insert_inode_hash(inode, (unsigned long) upperinode);
}
void ovl_dentry_version_inc(struct dentry *dentry)
realinode = d_inode(realdentry);
err = -ENOMEM;
- inode = ovl_new_inode(dentry->d_sb, realinode->i_mode);
+ if (upperdentry && !d_is_dir(upperdentry)) {
+ inode = ovl_get_inode(dentry->d_sb, realinode);
+ } else {
+ inode = ovl_new_inode(dentry->d_sb, realinode->i_mode);
+ if (inode)
+ ovl_inode_init(inode, realinode, !!upperdentry);
+ }
if (!inode)
goto out_free_oe;
- ovl_inode_init(inode, realinode, !!upperdentry);
ovl_copyattr(realdentry->d_inode, inode);
}