diff --git a/include/util.h b/include/util.h index 8bbd4a01b..ee5e8e83e 100644 --- a/include/util.h +++ b/include/util.h @@ -183,6 +183,8 @@ static inline bool dir_dots(struct dirent *de) return !strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."); } +extern int is_empty_dir(int dirfd); + /* * Size of buffer to carry the worst case or /proc/self/fd/N * path. Since fd is an integer, we can easily estimate one :) diff --git a/mount.c b/mount.c index 102d1291d..1016b976f 100644 --- a/mount.c +++ b/mount.c @@ -1357,31 +1357,19 @@ out: static int dump_empty_fs(struct mount_info *pm) { int fd, ret = -1; - struct dirent *de; - DIR *fdir = NULL; fd = open_mountpoint(pm); if (fd < 0) return -1; - fdir = fdopendir(fd); - if (fdir == NULL) { - close(fd); + ret = is_empty_dir(fd); + close(fd); + if (ret < 0) { + pr_err("%s isn't empty\n", pm->fstype->name); return -1; } - while ((de = readdir(fdir))) { - if (dir_dots(de)) - continue; - - pr_err("%s isn't empty: %s\n", pm->fstype->name, de->d_name); - goto out; - } - - ret = 0; -out: - closedir(fdir); - return ret; + return ret ? 0 : -1; } /* diff --git a/net.c b/net.c index f68a2d81d..148d6b2ea 100644 --- a/net.c +++ b/net.c @@ -225,6 +225,41 @@ static int dump_unknown_device(struct ifinfomsg *ifi, char *kind, return -1; } +static int dump_bridge(NetDeviceEntry *nde, struct cr_imgset *imgset) +{ + char spath[IFNAMSIZ + 16]; /* len("class/net//brif") + 1 for null */ + int ret, fd; + + ret = snprintf(spath, sizeof(spath), "class/net/%s/brif", nde->name); + if (ret < 0 || ret >= sizeof(spath)) + return -1; + + /* Let's only allow dumping empty bridges for now. To do a full bridge + * restore, we need to make sure the bridge and slaves are restored in + * the right order and attached correctly. It looks like the veth code + * supports this, but we need some way to do ordering. + */ + fd = openat(ns_sysfs_fd, spath, O_DIRECTORY, 0); + if (fd < 0) { + pr_perror("opening %s failed", spath); + return -1; + } + + ret = is_empty_dir(fd); + close(fd); + if (ret < 0) { + pr_perror("problem testing %s for emptiness", spath); + return -1; + } + + if (!ret) { + pr_err("dumping bridges with attached slaves not supported currently\n"); + return -1; + } + + return write_netdev_img(nde, imgset); +} + static int dump_one_ethernet(struct ifinfomsg *ifi, char *kind, struct rtattr **tb, struct cr_imgset *fds) { @@ -240,6 +275,8 @@ static int dump_one_ethernet(struct ifinfomsg *ifi, char *kind, return dump_one_netdev(ND_TYPE__VETH, ifi, tb, fds, NULL); if (!strcmp(kind, "tun")) return dump_one_netdev(ND_TYPE__TUN, ifi, tb, fds, dump_tun_link); + if (!strcmp(kind, "bridge")) + return dump_one_netdev(ND_TYPE__BRIDGE, ifi, tb, fds, dump_bridge); return dump_unknown_device(ifi, kind, tb, fds); } @@ -465,6 +502,17 @@ static int venet_link_info(NetDeviceEntry *nde, struct newlink_req *req) return 0; } +static int bridge_link_info(NetDeviceEntry *nde, struct newlink_req *req) +{ + struct rtattr *bridge_data; + + bridge_data = NLMSG_TAIL(&req->h); + addattr_l(&req->h, sizeof(*req), IFLA_INFO_KIND, "bridge", sizeof("bridge")); + bridge_data->rta_len = (void *)NLMSG_TAIL(&req->h) - (void *)bridge_data; + + return 0; +} + static int restore_link(NetDeviceEntry *nde, int nlsk) { pr_info("Restoring link %s type %d\n", nde->name, nde->type); @@ -479,6 +527,9 @@ static int restore_link(NetDeviceEntry *nde, int nlsk) return restore_one_link(nde, nlsk, veth_link_info); case ND_TYPE__TUN: return restore_one_tun(nde, nlsk); + case ND_TYPE__BRIDGE: + return restore_one_link(nde, nlsk, bridge_link_info); + default: pr_err("Unsupported link type %d\n", nde->type); break; diff --git a/protobuf/netdev.proto b/protobuf/netdev.proto index c189ff64b..dafa2bd8f 100644 --- a/protobuf/netdev.proto +++ b/protobuf/netdev.proto @@ -16,6 +16,7 @@ enum nd_type { * Virtuozzo specific device. */ VENET = 5; + BRIDGE = 6; } message net_device_entry { diff --git a/util.c b/util.c index 6365fb903..7a2265678 100644 --- a/util.c +++ b/util.c @@ -665,6 +665,29 @@ int is_root_user() return 1; } +int is_empty_dir(int dirfd) +{ + int ret = 0; + DIR *fdir = NULL; + struct dirent *de; + + fdir = fdopendir(dirfd); + if (!fdir) + return -1; + + while ((de = readdir(fdir))) { + if (dir_dots(de)) + continue; + + goto out; + } + + ret = 1; +out: + closedir(fdir); + return ret; +} + int vaddr_to_pfn(unsigned long vaddr, u64 *pfn) { int fd, ret = -1;