<div dir="ltr">Here is the new post review patch!</div><div class="gmail_extra"><br><div class="gmail_quote">2015-08-05 10:51 GMT-07:00 Gabriel Guimaraes <span dir="ltr">&lt;<a href="mailto:guimaraesg@google.com" target="_blank">guimaraesg@google.com</a>&gt;</span>:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr">Hi Pavel,<div><br></div><div>Thanks for the review.</div><div class="gmail_extra"><br><div class="gmail_quote">On Wed, Aug 5, 2015 at 4:00 AM, Pavel Emelyanov <span dir="ltr">&lt;<a href="mailto:xemul@parallels.com" target="_blank">xemul@parallels.com</a>&gt;</span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">Gabriel,<br>
<br>
Please, find my comments inline.<br>
<br>
&gt; diff --git a/crtools.c b/crtools.c<br>
&gt; index 6af6080..521d0ff 100644<br>
&gt; --- a/crtools.c<br>
&gt; +++ b/crtools.c<br>
&gt; @@ -235,6 +235,7 @@ int main(int argc, char *argv[], char *envp[])<br>
&gt;               { &quot;enable-fs&quot;,                  required_argument,      0, 1065 },<br>
&gt;               { &quot;enable-external-sharing&quot;,    no_argument,            0, 1066 },<br>
&gt;               { &quot;enable-external-masters&quot;,    no_argument,            0, 1067 },<br>
&gt; +             { &quot;overlayfs&quot;,                  no_argument,            0, 1068 },<br>
<br>
With AUFS workaround we ended up with auto-detection scheme -- when<br>
meeting an AUFS mount the opts.aufs is set to true. Can we do the<br>
same with overlayfs?<br></blockquote><div><br></div><div>Technically we could always enable overlayfs by default, since we only do something if there is an overlayfs mounted entry in the mountinfo table. Should I enable it always?</div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<br>
&gt;               { },<br>
&gt;       };<br>
&gt;<br>
&gt; @@ -465,6 +466,9 @@ int main(int argc, char *argv[], char *envp[])<br>
&gt;               case 1067:<br>
&gt;                       opts.enable_external_masters = true;<br>
&gt;                       break;<br>
&gt; +             case 1068:<br>
&gt; +                     opts.overlayfs = true;<br>
&gt; +                     break;<br>
&gt;               case &#39;M&#39;:<br>
&gt;                       {<br>
&gt;                               char *aux;<br>
&gt; diff --git a/files.c b/files.c<br>
&gt; index c82920d..2c6cca0 100644<br>
&gt; --- a/files.c<br>
&gt; +++ b/files.c<br>
&gt; @@ -30,6 +30,7 @@<br>
&gt;  #include &quot;eventfd.h&quot;<br>
&gt;  #include &quot;eventpoll.h&quot;<br>
&gt;  #include &quot;fsnotify.h&quot;<br>
&gt; +#include &quot;mount.h&quot;<br>
&gt;  #include &quot;signalfd.h&quot;<br>
&gt;  #include &quot;namespaces.h&quot;<br>
&gt;  #include &quot;tun.h&quot;<br>
&gt; @@ -157,6 +158,45 @@ void show_saved_files(void)<br>
&gt;  }<br>
&gt;<br>
&gt;  /*<br>
&gt; + * Workaround for the OverlayFS bug present before Kernel 4.2<br>
&gt; + *<br>
&gt; + * When a process has a file open in an OverlayFS directory,<br>
&gt; + * the information in /proc/&lt;pid&gt;/fd/&lt;fd&gt; and /proc/&lt;pid&gt;/fdinfo/&lt;fd&gt;<br>
&gt; + * is wrong, so, if --overlayfs is specified, we grab that information<br>
&gt; + * from the mountinfo table instead.<br>
&gt; + *<br>
&gt; + * This is done every time fill_fdlink is called. See lookup_overlayfs<br>
&gt; + * for more details.<br>
&gt; + */<br>
&gt; +static int fixup_overlayfs(struct fd_parms *p, struct fd_link *link)<br>
&gt; +{<br>
&gt; +     struct mount_info *m;<br>
&gt; +<br>
&gt; +     if (!link)<br>
&gt; +             return 0;<br>
&gt; +<br>
&gt; +     m = lookup_overlayfs(link-&gt;name, p-&gt;stat.st_dev, p-&gt;stat.st_ino, p-&gt;mnt_id);<br>
&gt; +     if (!m)<br>
&gt; +             return 0;<br>
<br>
NULL here means both -- error occurred and nothing-to-do. Can you<br>
check for errors using the ERR_PTR notation?<br>
<br>
&gt; +<br>
&gt; +     p-&gt;mnt_id = m-&gt;mnt_id;<br>
&gt; +<br>
&gt; +     /* Stores the correct file path in p-&gt;link-&gt;name */<br>
<br>
Can you extend this comment specifying what the incorrectness is.<br></blockquote><div>Sure.</div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<br>
&gt; +     if (strcmp(&quot;./&quot;, m-&gt;mountpoint) != 0) {<br>
&gt; +             char buf[PATH_MAX];<br>
&gt; +             int n;<br>
&gt; +<br>
&gt; +             strncpy(buf, link-&gt;name, PATH_MAX);<br>
&gt; +             n = snprintf(link-&gt;name, PATH_MAX, &quot;%s/%s&quot;, m-&gt;mountpoint, buf + 2);<br>
&gt; +             if (n &gt;= PATH_MAX) {<br>
&gt; +                     pr_err(&quot;Not enough space to replace %s\n&quot;, buf);<br>
&gt; +                     return -1;<br>
&gt; +             }<br>
&gt; +     }<br>
&gt; +     return 0;<br>
&gt; +}<br>
&gt; +<br>
&gt; +/*<br>
&gt;   * The gen_id thing is used to optimize the comparison of shared files.<br>
&gt;   * If two files have different gen_ids, then they are different for sure.<br>
&gt;   * If it matches, we don&#39;t know it and have to call sys_kcmp().<br>
&gt; @@ -206,6 +246,10 @@ int fill_fdlink(int lfd, const struct fd_parms *p, struct fd_link *link)<br>
&gt;       }<br>
&gt;<br>
&gt;       link-&gt;len = len + 1;<br>
&gt; +<br>
&gt; +     if (opts.overlayfs)<br>
&gt; +             if (fixup_overlayfs((struct fd_parms *)p, link) &lt; 0)<br>
&gt; +                     return -1;<br>
&gt;       return 0;<br>
&gt;  }<br>
&gt;<br>
&gt; diff --git a/include/cr_options.h b/include/cr_options.h<br>
&gt; index 19c2f77..011349c 100644<br>
&gt; --- a/include/cr_options.h<br>
&gt; +++ b/include/cr_options.h<br>
&gt; @@ -79,6 +79,7 @@ struct cr_options {<br>
&gt;       bool                    enable_external_sharing;<br>
&gt;       bool                    enable_external_masters;<br>
&gt;       bool                    aufs;           /* auto-deteced, not via cli */<br>
&gt; +     bool                    overlayfs;<br>
&gt;  };<br>
&gt;<br>
&gt;  extern struct cr_options opts;<br>
&gt; diff --git a/include/mount.h b/include/mount.h<br>
&gt; index 0d5fc7f..01da3f5 100644<br>
&gt; --- a/include/mount.h<br>
&gt; +++ b/include/mount.h<br>
&gt; @@ -22,6 +22,8 @@ extern int prepare_mnt_ns(void);<br>
&gt;  extern int pivot_root(const char *new_root, const char *put_old);<br>
&gt;<br>
&gt;  struct mount_info;<br>
&gt; +struct mount_info *lookup_overlayfs(char *rpath, unsigned int s_dev,<br>
&gt; +                             unsigned int st_ino, unsigned int mnt_id);<br>
&gt;  extern struct mount_info *lookup_mnt_id(unsigned int id);<br>
&gt;  extern struct mount_info *lookup_mnt_sdev(unsigned int s_dev);<br>
&gt;<br>
&gt; diff --git a/mount.c b/mount.c<br>
&gt; index acd74f0..6072050 100644<br>
&gt; --- a/mount.c<br>
&gt; +++ b/mount.c<br>
&gt; @@ -127,6 +127,95 @@ static inline int fsroot_mounted(struct mount_info *mi)<br>
&gt;       return is_root(mi-&gt;root);<br>
&gt;  }<br>
&gt;<br>
&gt; +static struct mount_info *__lookup_overlayfs(struct mount_info *list, char *rpath,<br>
&gt; +                                             unsigned int st_dev, unsigned int st_ino,<br>
&gt; +                                             unsigned int mnt_id)<br>
&gt; +{<br>
&gt; +     /*<br>
&gt; +      * Goes through all entries in the mountinfo table<br>
&gt; +      * looking for a mount point that contains the file specified<br>
&gt; +      * in rpath. Uses the device number st_dev and the inode number st_ino<br>
&gt; +      * to make sure the file is correct.<br>
&gt; +      */<br>
&gt; +     struct mount_info *mi_ret = NULL;<br>
&gt; +     struct mount_info *m;<br>
&gt; +<br>
&gt; +     for (m = list; m != NULL; m = m-&gt;next) {<br>
&gt; +             /* Case 1: If mnt_id is correct, don&#39;t do anything */<br>
&gt; +             if (st_dev == m-&gt;s_dev &amp;&amp; mnt_id == m-&gt;mnt_id)<br>
&gt; +                     return NULL;<br>
<br>
Shouldn&#39;t this if () be under the next one, so that everything that happens<br>
here applies to overlayfs only?</blockquote><div> </div><div>This if () is basically a check to see if the bug exists. We look for all instances of the device st_dev and see if any of them match our mnt_id. If that&#39;s not the case, the bug exists and we want to proceed to try to fix it. </div><div><br></div><div>I could have left this as a separate for loop to make this more clear, but this way we only use one for loop for both. I believe it&#39;s better if we separate it into two for loops since we prevent some unnecessary stating. I can also move this part to lookup_overlayfs, before dealing with namespaces. What do you think?</div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<br>
&gt; +<br>
&gt; +             /*<br>
&gt; +              * Case 2: If overlayFS we try to find the correct mnt_id.<br>
&gt; +              *<br>
&gt; +              * Note that even if we do find something, we want to keep looking for<br>
&gt; +              * Case 1, which takes precedence.<br>
&gt; +              */<br>
&gt; +             if (m-&gt;fstype-&gt;code == FSTYPE__OVERLAYFS) {<br>
&gt; +                     char _abs_path[PATH_MAX];<br>
&gt; +                     struct stat f_stat;<br>
&gt; +                     int ret_stat;<br>
&gt; +<br>
&gt; +                     /* Concatenates m-&gt;mountpoint with rpath and attempts to stat the resulting path */<br>
&gt; +                     if (strcmp(&quot;./&quot;, m-&gt;mountpoint) == 0)<br>
&gt; +                             ret_stat = stat(rpath + 1, &amp;f_stat);<br>
&gt; +                     else {<br>
&gt; +                             int n = snprintf(_abs_path, PATH_MAX, &quot;%s/%s&quot;, m-&gt;mountpoint + 1, rpath);<br>
&gt; +<br>
&gt; +                             if (n &gt;= PATH_MAX) {<br>
&gt; +                                     pr_err(&quot;Not enough space to concatenate %s and %s\n&quot;, m-&gt;mountpoint, rpath);<br>
&gt; +                                     return NULL;<br>
&gt; +                             }<br>
&gt; +                             ret_stat = stat(_abs_path, &amp;f_stat);<br>
&gt; +                     }<br>
&gt; +<br>
&gt; +                     if (ret_stat == 0 &amp;&amp; st_dev == f_stat.st_dev &amp;&amp; st_ino == f_stat.st_ino)<br>
&gt; +                             mi_ret = m;<br>
&gt; +             }<br>
&gt; +     }<br>
&gt; +<br>
&gt; +     return mi_ret;<br>
&gt; +}<br>
&gt; +<br>
&gt; +/*<br>
&gt; + * Looks up the mnt_id and path of a file in an overlayFS directory.<br>
&gt; + *<br>
&gt; + * This is useful in order to fix the OverlayFS bug present in the<br>
&gt; + * Linux Kernel before version 4.2. See fixup_overlayfs for details.<br>
&gt; + *<br>
&gt; + * We first enter the mount namespace of the process and check to see<br>
&gt; + * if there are any overlayFS mounted directories in the mountinfo table.<br>
&gt; + * If so, we concatenate the mountpoint with the name of the file, and<br>
&gt; + * stat the resulting path to check if we found the correct device id<br>
&gt; + * and node number. If that is the case, we update the mount id and<br>
&gt; + * link variables with the correct values.<br>
&gt; + *<br>
&gt; + * This update is skipped if there is an entry in the<br>
&gt; + * mountinfo table with the same known device number and<br>
&gt; + * mount id, signifying that we already have the correct id.<br>
&gt; + *<br>
&gt; + * At the end of this procedure, we exit the mount namespace<br>
&gt; + * of the process, back to the original one.<br>
&gt; + */<br>
&gt; +struct mount_info *lookup_overlayfs(char *rpath, unsigned int st_dev,<br>
&gt; +                                     unsigned int st_ino, unsigned int mnt_id)<br>
&gt; +{<br>
&gt; +     int ns_old = -1;<br>
&gt; +     struct mount_info *mi;<br>
&gt; +<br>
&gt; +     if (switch_ns(root_item-&gt;pid.real, &amp;mnt_ns_desc, &amp;ns_old) &lt; 0) {<br>
<br>
The switch_ns call is quite slow. If you only need to stat the file by path<br>
in proper mount namespace, you can get the namespace&#39;s root fd with the<br>
mntns_get_root_fd() call and then do fstatat(mntns_root, path, ...);</blockquote><div><br></div><div>I&#39;ll look into this!</div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"> <br></blockquote><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
&gt; +             pr_err(&quot;Failed to switch into mount namespace of PID %d\n&quot;, root_item-&gt;pid.real);<br>
&gt; +             return NULL;<br>
&gt; +     }<br>
&gt; +     mi = __lookup_overlayfs(mntinfo, rpath, st_dev, st_ino, mnt_id);<br>
&gt; +<br>
&gt; +     if (restore_ns(ns_old, &amp;mnt_ns_desc)) {<br>
&gt; +             pr_err(&quot;Failed to restore to old mount namespace %d\n&quot;, ns_old);<br>
&gt; +             return NULL;<br>
&gt; +     }<br>
&gt; +     return mi;<br>
&gt; +}<br>
&gt; +<br>
&gt;  static struct mount_info *__lookup_mnt_id(struct mount_info *list, int id)<br>
&gt;  {<br>
&gt;       struct mount_info *m;<br>
&gt; @@ -1365,6 +1454,9 @@ static struct fstype fstypes[32] = {<br>
&gt;               .code = FSTYPE__FUSE,<br>
&gt;               .dump = always_fail,<br>
&gt;               .restore = always_fail,<br>
&gt; +     }, {<br>
&gt; +             .name = &quot;overlay&quot;,<br>
&gt; +             .code = FSTYPE__OVERLAYFS,<br>
&gt;       },<br>
&gt;  };<br>
&gt;<br>
&gt; diff --git a/protobuf/mnt.proto b/protobuf/mnt.proto<br>
&gt; index 6f8e7d1..6e58e1d 100644<br>
&gt; --- a/protobuf/mnt.proto<br>
&gt; +++ b/protobuf/mnt.proto<br>
&gt; @@ -18,6 +18,7 @@ enum fstype {<br>
&gt;       MQUEUE                  = 14;<br>
&gt;       FUSE                    = 15;<br>
&gt;       AUTO                    = 16;<br>
&gt; +     OVERLAYFS               = 17;<br>
&gt;  };<br>
&gt;<br>
&gt;  message mnt_entry {<br>
<span><font color="#888888"><br>
-- Pavel<br>
</font></span></blockquote></div><br></div></div>
</blockquote></div><br></div>