From: Stanislav Kinsburskiy <skinsbur...@parallels.com> If there is another dentry for the unlinked socket inode, then first create a hardlink for this dentry, then rebind and unlink.
Signed-off-by: Stanislav Kinsburskiy <skinsbur...@parallels.com> --- include/linux/cpt_image.h | 2 - kernel/cpt/rst_socket.c | 96 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 95 insertions(+), 3 deletions(-) diff --git a/include/linux/cpt_image.h b/include/linux/cpt_image.h index 75fa878..394fe0f 100644 --- a/include/linux/cpt_image.h +++ b/include/linux/cpt_image.h @@ -964,7 +964,7 @@ struct cpt_sock_image __u32 cpt_i_uid; __u32 cpt_i_gid; - __u32 cpt_d_alias[128/4]; + __u32 cpt_d_alias[108/4]; __u32 cpt_d_aliaslen; __u32 __cpt_pad15; diff --git a/kernel/cpt/rst_socket.c b/kernel/cpt/rst_socket.c index 79a9dab..beb7857 100644 --- a/kernel/cpt/rst_socket.c +++ b/kernel/cpt/rst_socket.c @@ -189,6 +189,85 @@ static int unix_bind_to_path(struct socket *sock, char *name, return err; } +static struct dentry *select_link_dentry(char *name, struct dentry *dirde, + cpt_context_t *ctx) +{ + struct dentry *link; + int i; + + for (i=0; i<100; i++) { + unsigned int rnd = net_random(); + + sprintf(name, "SOCK.%08x", rnd); + + link = lookup_one_len(name, dirde, strlen(name)); + if (IS_ERR(link)) { + eprintk_ctx("Can't lookup %s\n", name); + return link; + } + + if (!link->d_inode) + return link; + + dput(link); + } + + eprintk_ctx("failed to allocate deleted socket dentry\n"); + return ERR_PTR(-ELOOP); +} + +static int rst_create_link(struct dentry *d, char *name, + struct cpt_context *ctx) +{ + int err; + struct dentry *dirde, *hardde; + + dirde = d->d_parent; + + mutex_lock(&dirde->d_inode->i_mutex); + + hardde = select_link_dentry(name, dirde, ctx); + if (IS_ERR(hardde)) { + eprintk_ctx("Can't find hardde: %s\n", name); + err = PTR_ERR(hardde); + goto out_unlock; + } + + err = vfs_link(d, dirde->d_inode, hardde); + if (err) + eprintk_ctx("error hardlink %s, %d\n", name, err); + + dput(hardde); +out_unlock: + mutex_unlock(&dirde->d_inode->i_mutex); + return err; +} + +static int create_deleted_socket_link(char *name, char *alias, struct vfsmount *mnt, + cpt_context_t *ctx) +{ + struct nameidata nd; + int err; + + strcpy(name, alias); + + err = rst_path_lookup_at(mnt, mnt->mnt_root, alias, 0, &nd); + if (err) { + eprintk_ctx("failed to lookup deleted socket alias\n"); + return err; + } + + err = rst_create_link(nd.path.dentry, strrchr(name, '/') + 1, ctx); + path_put(&nd.path); + if (err) { + eprintk_ctx("%s: failed to link socket alias\n", __func__); + return err; + } + + printk("link path: %s\n", name); + return 0; +} + static int unix_bind_to_mntref(struct sock *sk, char *name, struct sockaddr* addr, int addrlen, struct cpt_sock_image *si, cpt_context_t *ctx) @@ -222,6 +301,13 @@ static int unix_bind_to_mntref(struct sock *sk, char *name, return -EINVAL; } + if (si->cpt_sockflags & CPT_SOCK_DELETED) { + err = create_deleted_socket_link(name, (char *)si->cpt_d_alias, + (struct vfsmount *)mntobj->o_obj, ctx); + if (err) + return err; + } + if (strlen(name) < mntobj->o_lock) { eprintk_ctx("%s: unix socket with too short name (%d %s)\n", __func__, mntobj->o_lock, name); @@ -242,7 +328,12 @@ static int unix_bind_to_mntref(struct sock *sk, char *name, } bi.next = NULL; - return rebind_unix_socket(mntobj->o_obj, &bi, LOOKUP_DIVE); + err = rebind_unix_socket(mntobj->o_obj, &bi, LOOKUP_DIVE); + + if (si->cpt_sockflags & CPT_SOCK_DELETED) + sc_unlink(name); + + return err; } static int can_be_rebound_by_mntref(struct socket *sock, @@ -252,7 +343,8 @@ static int can_be_rebound_by_mntref(struct socket *sock, if (ctx->image_version < CPT_VERSION_18_4) return 0; - if (si->cpt_sockflags & CPT_SOCK_DELETED) + if ((si->cpt_sockflags & CPT_SOCK_DELETED) && + (!cpt_object_has(si, cpt_d_alias) || !si->cpt_d_aliaslen)) return 0; if (si->cpt_vfsmount_ref == CPT_NULL) _______________________________________________ Devel mailing list Devel@openvz.org https://lists.openvz.org/mailman/listinfo/devel