avahi-daemon sets up inotify marks inside chroot'ed environment, ie it does chroot(/etc/avahi/) inotify_add_watch(/) inotify_add_watch(/services) so that when we "remember" paths in our @wd_path variables, which in turn will be needed when we restore the notification marks by their paths, we don't take chroot into account. In example above the paths should be expanded to /etc/avahi/ and /etc/avahi/services respectively, otherwise we might hit the clash where both marks would look identical from kernel point of view and targeting global root point '/' instead leading the kernel to refuse restoring the container. Thus when we remember the paths lets switch to VE's root, fetch them and restore original paths back. https://jira.sw.ru/browse/PSBM-31567 QA: Please run LTP inotify tests together with suspend/resume. Signed-off-by: Cyrill Gorcunov CC: Andrew Vagin CC: Kirill Tkhai CC: Kir Kolyshkin CC: Pavel Emelyanov --- fs/notify/inotify/inotify_user.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) Index: linux-rhel6.git/fs/notify/inotify/inotify_user.c =================================================================== --- linux-rhel6.git.orig/fs/notify/inotify/inotify_user.c +++ linux-rhel6.git/fs/notify/inotify/inotify_user.c @@ -41,6 +41,7 @@ #include #include #include +#include #include "inotify.h" @@ -547,11 +548,27 @@ int __inotify_new_watch(struct fsnotify_ return -EINVAL; if (!ve_is_super(get_exec_env())) { + struct path old_root; + kwd_path = kmalloc(PATH_MAX, GFP_KERNEL); if (!kwd_path) return -ENOMEM; + /* + * The application might have done chroot() + * call so the path should be fetched relative + * the VE's root, otherwise we might hit mark + * clash. Imagine two marks '/', and '/etc/avahi/services' + * (as it was found in bug report): the avahi has chroot'ed + * to /etc/avahi/ and in result we saw two '/' identical + * marks. + */ + get_fs_root(current->fs, &old_root); + set_fs_root(current->fs, &get_exec_env()->root_path); wd_path = d_path(path, kwd_path, PATH_MAX); + set_fs_root(current->fs, &old_root); + path_put(&old_root); + if (IS_ERR(wd_path)) { kfree(kwd_path); return PTR_ERR(wd_path);