Commit 155eb72b authored by Tycho Andersen's avatar Tycho Andersen Committed by Pavel Emelyanov

unix: don't drop the path on unix sockets if they don't exist

For standalone unix sockets, listen() will fail if we haven't called bind()
with an actual address. If we remove the name on dump, we won't call
bind(), and thus sockets in this state will fail to restore.

v2: temporarily rename a unix socket out of the way if necessary in order
    to bind() correctly and then delete it (e.g. when there are two unix
    sockets bound "on top" of each other)
v3: remove extra unlink(), do the real unlink() in bind_unix_sk() so we
    only need to do it once
Signed-off-by: 's avatarTycho Andersen <tycho.andersen@canonical.com>
Signed-off-by: 's avatarPavel Emelyanov <xemul@virtuozzo.com>
parent daeb9129
...@@ -72,6 +72,7 @@ struct unix_sk_desc { ...@@ -72,6 +72,7 @@ struct unix_sk_desc {
unsigned int nr_icons; unsigned int nr_icons;
unsigned int *icons; unsigned int *icons;
unsigned char shutdown; unsigned char shutdown;
bool deleted;
mode_t mode; mode_t mode;
uid_t uid; uid_t uid;
...@@ -337,6 +338,11 @@ static int dump_one_unix_fd(int lfd, u32 id, const struct fd_parms *p) ...@@ -337,6 +338,11 @@ static int dump_one_unix_fd(int lfd, u32 id, const struct fd_parms *p)
perms->gid = userns_gid(sk->gid); perms->gid = userns_gid(sk->gid);
} }
if (sk->deleted) {
ue->has_deleted = true;
ue->deleted = sk->deleted;
}
sk_encode_shutdown(ue, sk->shutdown); sk_encode_shutdown(ue, sk->shutdown);
if (ue->peer) { if (ue->peer) {
...@@ -503,7 +509,7 @@ static int unix_process_name(struct unix_sk_desc *d, const struct unix_diag_msg ...@@ -503,7 +509,7 @@ static int unix_process_name(struct unix_sk_desc *d, const struct unix_diag_msg
if (name[0] != '\0') { if (name[0] != '\0') {
struct unix_diag_vfs *uv; struct unix_diag_vfs *uv;
bool drop_path = false; bool deleted = false;
char rpath[PATH_MAX]; char rpath[PATH_MAX];
struct ns_id *ns; struct ns_id *ns;
struct stat st; struct stat st;
...@@ -554,30 +560,21 @@ static int unix_process_name(struct unix_sk_desc *d, const struct unix_diag_msg ...@@ -554,30 +560,21 @@ static int unix_process_name(struct unix_sk_desc *d, const struct unix_diag_msg
pr_info("unix: Dropping path %s for unlinked sk %#x\n", pr_info("unix: Dropping path %s for unlinked sk %#x\n",
name, m->udiag_ino); name, m->udiag_ino);
drop_path = true; deleted = true;
} else if ((st.st_ino != uv->udiag_vfs_ino) || } else if ((st.st_ino != uv->udiag_vfs_ino) ||
!phys_stat_dev_match(st.st_dev, uv->udiag_vfs_dev, ns, name)) { !phys_stat_dev_match(st.st_dev, uv->udiag_vfs_dev, ns, name)) {
pr_info("unix: Dropping path %s for unlinked bound " pr_info("unix: Dropping path %s for unlinked bound "
"sk %#x.%#x real %#x.%#x\n", "sk %#x.%#x real %#x.%#x\n",
name, (int)st.st_dev, (int)st.st_ino, name, (int)st.st_dev, (int)st.st_ino,
(int)uv->udiag_vfs_dev, (int)uv->udiag_vfs_ino); (int)uv->udiag_vfs_dev, (int)uv->udiag_vfs_ino);
drop_path = true; deleted = true;
}
if (drop_path) {
/*
* When a socket is bound to unlinked file, we
* just drop his name, since no one will access
* it via one.
*/
xfree(name);
len = 0;
name = NULL;
} }
d->mode = st.st_mode; d->mode = st.st_mode;
d->uid = st.st_uid; d->uid = st.st_uid;
d->gid = st.st_gid; d->gid = st.st_gid;
d->deleted = deleted;
} }
postprone: postprone:
...@@ -962,8 +959,47 @@ static int bind_unix_sk(int sk, struct unix_sk_info *ui) ...@@ -962,8 +959,47 @@ static int bind_unix_sk(int sk, struct unix_sk_info *ui)
ret = bind(sk, (struct sockaddr *)&addr, ret = bind(sk, (struct sockaddr *)&addr,
sizeof(addr.sun_family) + ui->ue->name.len); sizeof(addr.sun_family) + ui->ue->name.len);
if (ret < 0) { if (ret < 0) {
pr_perror("Can't bind socket"); if (ui->ue->has_deleted && ui->ue->deleted && errno == EADDRINUSE) {
goto done; char temp[PATH_MAX];
pr_info("found duplicate unix socket bound at %s\n", addr.sun_path);
ret = snprintf(temp, sizeof(temp), "%s-%s-%d", addr.sun_path, "criu-temp", getpid());
/* this shouldn't happen, since sun_addr is only 108 chars long */
if (ret < 0 || ret >= sizeof(temp)) {
pr_err("snprintf of %s failed?\n", addr.sun_path);
goto done;
}
ret = rename(addr.sun_path, temp);
if (ret < 0) {
pr_perror("couldn't move socket for binding");
goto done;
}
ret = bind(sk, (struct sockaddr *)&addr,
sizeof(addr.sun_family) + ui->ue->name.len);
if (ret < 0) {
pr_perror("Can't bind socket after move");
goto done;
}
ret = rename(temp, addr.sun_path);
if (ret < 0) {
pr_perror("couldn't move socket back");
goto done;
}
/* we've handled the deleted-ness of this
* socket and we don't want to delete it later
* since it's not /this/ socket.
*/
ui->ue->deleted = false;
} else {
pr_perror("Can't bind socket");
goto done;
}
} }
if (*ui->name && ui->ue->file_perms) { if (*ui->name && ui->ue->file_perms) {
...@@ -988,6 +1024,11 @@ static int bind_unix_sk(int sk, struct unix_sk_info *ui) ...@@ -988,6 +1024,11 @@ static int bind_unix_sk(int sk, struct unix_sk_info *ui)
goto done; goto done;
} }
} }
if (ui->ue->deleted && unlink((char *)ui->ue->name.data) < 0) {
pr_perror("failed to unlink %s\n", ui->ue->name.data);
return -1;
}
} }
if (ui->ue->state != TCP_LISTEN) if (ui->ue->state != TCP_LISTEN)
......
...@@ -47,4 +47,5 @@ message unix_sk_entry { ...@@ -47,4 +47,5 @@ message unix_sk_entry {
* Relative socket name may have prefix. * Relative socket name may have prefix.
*/ */
optional string name_dir = 14; optional string name_dir = 14;
optional bool deleted = 15;
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment