[CRIU] [PATCH] add join-ns opt to criu restore
Dengguangxing
dengguangxing at huawei.com
Fri Mar 25 02:52:40 PDT 2016
Hi, Pavel
Thanks for your reviewing.
I will refactor this patch, divide it into several patches and resend them later.
> Note, that once the patch gets merged we'll need the zdtm.py extention
> to test this functionaluty. Only after this (and after tests pass) I'll
> be able to move this patch from criu-dev branch into master and release.
>
where can I get the zdtm guidance? I don't know how to write a test case for
this new-added opts. An existing similiar test-case will help :)
>
>> @@ -99,6 +101,41 @@ bad_ns:
>> return -1;
>> }
>>
>> +static int parse_join_ns(const char *ptr)
>> +{
>> + char *aux, *ns_file, *extra_opts = NULL;
>> +
>> + aux = strchr(ptr, ':');
>> + if (aux == NULL)
>> + return -1;
>> + *aux = '\0';
>> +
>> + if (!strncmp(ptr, "pid", 4)) {
>> + pr_perror("pid namespace not supported in join-ns\n");
>> + return -1;
>> + } else if (strncmp(ptr, "net", 4) &&
>> + strncmp(ptr, "uts", 4) &&
>> + strncmp(ptr, "ipc", 4) &&
>> + strncmp(ptr, "user", 5) &&
>> + strncmp(ptr, "mnt", 4)) {
>> + pr_perror("Illegal namespace %s in join-ns\n", ptr);
>> + return -1;
>> + }
>
> We already have a code that turns namespaces' names into flags,
> it's in parse_unshare_arg. If you _do_ need to turn namesp into
> flags, please, pull this code into a separate function (with
> separate patch) and use it in your code.
I don't know exactly what do you mean. However, I found this code redundant
and have removed it :)
>> @@ -810,6 +857,12 @@ usage:
>> " --empty-ns {net}\n"
>> " Create a namespace, but don't restore its properies.\n"
>> " An user will retore them from action scripts.\n"
>> +" -J|--join-ns NS:PID|NS_FILE,EXTRA_OPTS\n"
>
> Please, separate extra opts with colon and mark them as optional. So the
> help would look like this
>
> -J|--join-ns NS:PID|NS_FILE[:EXTRA_OPTS]
>
thanks, fixed!
>
>> diff --git a/criu/include/namespaces.h b/criu/include/namespaces.h
>> index 303c9e6..3729aed 100644
>> --- a/criu/include/namespaces.h
>> +++ b/criu/include/namespaces.h
>> @@ -19,6 +21,24 @@ struct ns_desc {
>> size_t len;
>> };
>>
>> +struct user_ns_extra {
>> + char *uid;
>> + char *gid;
>> +};
>> +
>> +/* struct join_ns is used for storing parameters specified by --join-ns */
>> +struct join_ns {
>> + struct list_head list;
>> + char *ns_file;
>> + int ns_fd; /* namespace file descriptor */
>
> Not sure this is required on join_ns. The fd obtained is purely used
> in one call-stack, so just keeping it on stack should work.
>
yes, ns_fd is purely used in one call-stack. however as ns_fd and and ns_file are corresponding.
I think this is a convenient way to do it.
>> + struct ns_desc nd; /* namespace descriptor */
>
> Better grab pointer on one of existing nd-s rather than constructing
> those by yourself.
>
thanks, fixed!
>>
>> +unsigned int get_join_ns_flag() {
>> + struct join_ns *jn;
>> + unsigned int join_ns_flags = 0;
>> +
>> + list_for_each_entry(jn, &opts.join_ns, list)
>> + if (!strncmp(jn->nd.str, "uts", 4))
>> + join_ns_flags |= CLONE_NEWUTS;
>> + else if (!strncmp(jn->nd.str, "ipc", 4))
>> + join_ns_flags |= CLONE_NEWIPC;
>> + else if (!strncmp(jn->nd.str, "mnt", 4))
>> + join_ns_flags |= CLONE_NEWNS;
>> + else if (!strncmp(jn->nd.str, "pid", 4))
>> + join_ns_flags |= CLONE_NEWPID;
>> + else if (!strncmp(jn->nd.str, "net", 4))
>> + join_ns_flags |= CLONE_NEWNET;
>> + else if (!strncmp(jn->nd.str, "user", 5))
>> + join_ns_flags |= CLONE_NEWNET;
>> +
>> + return join_ns_flags;
>
> This is too heavy. Instead of keepin the strings with names and
> parsing them every time, either store the namespaces' flags in
> a variable, or keep an array/list of required ns_desc-s.
>
thanks, fixed!
>> +static int check_ns_file(char *ns_file) {
>> + int pid, ret, proc_dir;
>> +
>> + if (!check_int_str(ns_file)) {
>> + pid = atoi(ns_file);
>> + if (pid <= 0) {
>> + pr_perror("Invalid join_ns pid %s", ns_file);
>> + return -1;
>> + }
>> + proc_dir = open_pid_proc(pid);
>> + if (proc_dir < 0) {
>> + pr_perror("Invalid join_ns pid: /proc/%s not found", ns_file);
>> + return -1;
>> + }
>> + return 0;
>> + }
>> +
>> + ret = access(ns_file, 0);
>> + if (ret < 0) {
>> + pr_perror("Can't access join-ns file: %s", ns_file);
>> + return -1;
>> + }
>
> I doubt these checks should be done here. Once you'll try to join the
> namespace, specified by the option, you'll hit an error and restore
> would abort antway.
>
These checks mean to prevent some obvious mistakes here, before restore.
>> +int join_ns_add(const char *type, char *ns_file, char *extra_opts) {
>> + struct join_ns *jn;
>> +
>> + jn = xmalloc(sizeof(*jn));
>> + if (!jn) {
>> + return -1;
>> + }
>> +
>> + if (check_ns_file(ns_file)) {
>> + return -1;
>> + }
>> +
>> + jn->ns_file = ns_file;
>> + if (!strncmp(type, "net", 4)) {
>> + jn->nd = net_ns_desc;
>> + } else if (!strncmp(type, "uts", 4)) {
>> + jn->nd = uts_ns_desc;
>> + } else if (!strncmp(type, "ipc", 4)) {
>> + jn->nd = ipc_ns_desc;
>> + } else if (!strncmp(type, "pid", 4)) {
>> + jn->nd = pid_ns_desc;
>> + } else if (!strncmp(type, "user", 5)) {
>> + jn->nd = user_ns_desc;
>> + if (set_user_extra_opts(jn, extra_opts)) {
>> + pr_perror("invalid user namespace extra_opts %s\n", extra_opts);
>> + return -1;
>> + }
>> + } else if (!strncmp(type, "mnt", 4)) {
>> + jn->nd = mnt_ns_desc;
>> + } else {
>> + pr_perror("invalid namespace type %s\n", type);
>> + return -1;
>
> Indentation is one tab wider than it should.
thanks, fixed!
>> + }
>> +
>> + list_add_tail(&jn->list, &opts.join_ns);
>> + pr_info("Added %s:%s join namespace\n", type, ns_file);
>> + return 0;
>> +}
>> +
>> static unsigned int parse_ns_link(char *link, size_t len, struct ns_desc *d)
>> {
>> unsigned long kid = 0;
>> @@ -78,7 +240,7 @@ int switch_ns(int pid, struct ns_desc *nd, int *rst)
>>
>> nsfd = open_proc(pid, "ns/%s", nd->str);
>> if (nsfd < 0) {
>> - pr_perror("Can't open ipcns file");
>> + pr_perror("Can't open ns file");
>
> All cleanups should go in separate patches, please.
>
thanks, fixed.
>> +static int switch_join_ns(struct join_ns *jn) {
>> + if (jn->ns_fd < 0) {
>> + pr_perror("Invalid ns fd in join-ns");
>> + return -1;
>> + }
>> +
>> + int ret = -1;
>> + int self_fd;
>> + struct stat st, self_st;
>> + char buf[32];
>
> We follow the Linux kernel coding style. In particular, in function
> declarations braces should be on separate line and all the variables
> should be declared before any code.
>
fixed.
>> +
>> + if (fstat(jn->ns_fd, &st) == -1) {
>> + pr_perror("Can't get ns file %s stat", jn->ns_file);
>> + goto err_ns;
>> + }
>> +
>> + snprintf(buf, sizeof(buf), "/proc/self/ns/%s", jn->nd.str);
>> + self_fd = open(buf, O_RDONLY);
>> + if (self_fd < 0) {
>> + pr_perror("Can't open ns file: %s", buf);
>> + goto err_ns;
>> + }
>> + if (fstat(self_fd, &self_st) == -1) {
>
> There's no need in opening self-fd, just use stat() system call and
> stat by path.
fixed
>
>> + pr_perror("Can't get ns file %s stat", buf);
>> + goto err_self;
>> + }
>> +
>> + if (st.st_ino != self_st.st_ino) {
>
> Is this check _really_ required? Kernel would behave OK and won't
> move the process if namespaces already match.
yes, this is necessary. because calling set_ns() to the same user-ns
twice will return an error, so we will check it here.
>> +static int switch_user_join_ns(struct join_ns *jn) {
>> + if (jn == NULL) {
>> + return 0;
>> + }
>> +
>> + uid_t uid;
>> + gid_t gid;
>> +
>> + if (switch_join_ns(jn))
>> + return -1;
>> +
>> + if (jn->extra_opts.user_extra.uid == NULL) {
>> + uid = getuid();
>> + } else {
>> + uid = atoi(jn->extra_opts.user_extra.uid);
>> + }
>> + if (jn->extra_opts.user_extra.gid == NULL) {
>> + gid = getgid();
>> + } else {
>> + gid = atoi(jn->extra_opts.user_extra.gid);
>> + }
>
> All those { and } braces can be avoided here
thanks, fixed
>> index a67cd6e..ba871c7 100644
>> --- a/criu/net.c
>> +++ b/criu/net.c
>> @@ -1094,8 +1094,11 @@ int prepare_net_ns(int pid)
>> {
>> int ret = 0;
>> NetnsEntry *netns = NULL;
>> + unsigned int join_ns_flag = 0;
>>
>> - if (!(opts.empty_ns & CLONE_NEWNET)) {
>> + join_ns_flag = get_join_ns_flag();
>> + if (!(opts.empty_ns & CLONE_NEWNET) ||
>> + !(join_ns_flag & CLONE_NEWNET)) {
>
> Hasn't this already been checked in check_namespace_opts()?
here I mean, if net emtpy-ns or net join-ns is set, will skip these operations below.
'||' should be replace with '&&' :) have fixed that.
More information about the CRIU
mailing list