Bug 1239013

Summary: Bind mount plugin deletes host directory!
Product: [Fedora] Fedora Reporter: Matthew Booth <mbooth>
Component: mockAssignee: Miroslav Suchý <msuchy>
Status: CLOSED CURRENTRELEASE QA Contact: Fedora Extras Quality Assurance <extras-qa>
Severity: high Docs Contact:
Priority: unspecified    
Version: 22CC: dennisml, jdisnard, mebrown, msimacek, msuchy, praiskup, vstinner, williams
Target Milestone: ---   
Target Release: ---   
Hardware: Unspecified   
OS: Unspecified   
Whiteboard:
Fixed In Version: mock-1.2.11 Doc Type: Bug Fix
Doc Text:
Story Points: ---
Clone Of: Environment:
Last Closed: 2015-09-04 12:09:32 UTC Type: Bug
Regression: --- Mount Type: ---
Documentation: --- CRM:
Verified Versions: Category: ---
oVirt Team: --- RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: --- Target Upstream Version:
Embargoed:

Description Matthew Booth 2015-07-03 09:12:34 UTC
Description of problem:

Before I continue, I set the severity to high. Yeah, I know, everybody does that. However, this bug just deleted my working directory, which is pretty severe.

I put the following in a mock config:
config_opts['plugin_conf']['bind_mount_enable'] = True
config_opts['plugin_conf']['bind_mount_opts']['dirs'].append(('/home/mbooth/src/openstack/nova', '/builddir/nova' ))

This dies with:
OSError: [Errno 16] Device or resource busy: '/var/lib/mock/rhos-7.0-rhel-7-candidate-x86_64/root/builddir/tester/nova'

On inspection, /home/mbooth/src/openstack/nova is now empty. It has not had anything bind mounted over it.

The following works, and does not delete the host directory:

config_opts['plugin_conf']['bind_mount_enable'] = True
config_opts['plugin_conf']['bind_mount_opts']['dirs'].append(('/home/mbooth/src/openstack/nova', '/builddir/foo/nova' ))

It appears that if the plugin has to create the parent directory in the chroot, it doesn't delete the host directory.

Don't test this with data you care about.

Version-Release number of selected component (if applicable):
mock-1.2.10-1.fc22.noarch

How reproducible:
Always

Comment 1 Miroslav Suchý 2015-07-04 11:21:24 UTC
It dies (beside the error message) with traceback, which is super helpful for me.
Is it this one?
Traceback (most recent call last):
  File "/usr/sbin/mock", line 831, in <module>
    main()
  File "/usr/lib/python2.7/site-packages/mockbuild/trace_decorator.py", line 84, in trace
    result = func(*args, **kw)
  File "/usr/sbin/mock", line 653, in main
    run_command(options, args, config_opts, commands, buildroot, state)
  File "/usr/lib/python2.7/site-packages/mockbuild/trace_decorator.py", line 84, in trace
    result = func(*args, **kw)
  File "/usr/sbin/mock", line 684, in run_command
    commands.init(do_log=False)
  File "/usr/lib/python2.7/site-packages/mockbuild/trace_decorator.py", line 84, in trace
    result = func(*args, **kw)
  File "/usr/lib/python2.7/site-packages/mockbuild/backend.py", line 123, in init
    self.buildroot.initialize(**kwargs)
  File "/usr/lib/python2.7/site-packages/mockbuild/trace_decorator.py", line 84, in trace
    result = func(*args, **kw)
  File "/usr/lib/python2.7/site-packages/mockbuild/buildroot.py", line 84, in initialize
    self._init(prebuild=prebuild, do_log=do_log)
  File "/usr/lib/python2.7/site-packages/mockbuild/trace_decorator.py", line 84, in trace
    result = func(*args, **kw)
  File "/usr/lib/python2.7/site-packages/mockbuild/buildroot.py", line 146, in _init
    self._make_build_user()
  File "/usr/lib/python2.7/site-packages/mockbuild/trace_decorator.py", line 84, in trace
    result = func(*args, **kw)
  File "/usr/lib/python2.7/site-packages/mockbuild/buildroot.py", line 234, in _make_build_user
    selinux=self.selinux, exclude=excluded)
  File "/usr/lib/python2.7/site-packages/mockbuild/trace_decorator.py", line 84, in trace
    result = func(*args, **kw)
  File "/usr/lib/python2.7/site-packages/mockbuild/util.py", line 135, in rmtree
    rmtree(fullname, selinux=selinux, exclude=exclude)
  File "/usr/lib/python2.7/site-packages/mockbuild/trace_decorator.py", line 84, in trace
    result = func(*args, **kw)
  File "/usr/lib/python2.7/site-packages/mockbuild/util.py", line 144, in rmtree
    os.rmdir(path)
OSError: [Errno 16] Device or resource busy: '/var/lib/mock/fedora-22-x86_64/root/builddir/nova'


The problem seems to be that to keep reproducibility we remove /builddir/* in chroot before we start building (there were good reason for that). We only preserve:

# The build user's homedir is partially cleaned up even when --no-clean is
# specified in order to prevent garbage from previous builds from altering
# succesive builds. Mock can be configured to exclude certain files/directories
# from this. Default is SOURCES directory to support nosrc rpms. Paths are
# relative to build user's homedir
# config_opts['exclude_from_homedir_cleanup'] = ['build/SOURCES']

It seems that we mount ['bind_mount_opts']['dirs'] first and then we remove /builddir/, therefore if you mount something there is is deleted :(
I will try to move mounting of those directories after that deleting. That should fix this bug.

Comment 2 Miroslav Suchý 2015-07-04 11:35:49 UTC
Notes for myself - this should work too:
--- /usr/lib/python2.7/site-packages/mockbuild/util.py.orig     2015-07-04 07:34:30.678596660 -0400
+++ /usr/lib/python2.7/site-packages/mockbuild/util.py  2015-07-04 07:34:39.267596410 -0400
@@ -122,8 +122,6 @@
     while try_again:
         try_again = False
         try:
+            if os.path.ismount(path):
+                continue
             names = os.listdir(path)
             for name in names:
                 fullname = os.path.join(path, name)

But I should check other calling of rmtree() first.

Comment 3 Matthew Booth 2015-07-06 09:12:22 UTC
(In reply to Miroslav Suchý from comment #1)
> It dies (beside the error message) with traceback, which is super helpful
> for me.
> Is it this one?
> Traceback (most recent call last):
>   File "/usr/sbin/mock", line 831, in <module>
>     main()
>   File "/usr/lib/python2.7/site-packages/mockbuild/trace_decorator.py", line
> 84, in trace
>     result = func(*args, **kw)
>   File "/usr/sbin/mock", line 653, in main
>     run_command(options, args, config_opts, commands, buildroot, state)
>   File "/usr/lib/python2.7/site-packages/mockbuild/trace_decorator.py", line
> 84, in trace
>     result = func(*args, **kw)
>   File "/usr/sbin/mock", line 684, in run_command
>     commands.init(do_log=False)
>   File "/usr/lib/python2.7/site-packages/mockbuild/trace_decorator.py", line
> 84, in trace
>     result = func(*args, **kw)
>   File "/usr/lib/python2.7/site-packages/mockbuild/backend.py", line 123, in
> init
>     self.buildroot.initialize(**kwargs)
>   File "/usr/lib/python2.7/site-packages/mockbuild/trace_decorator.py", line
> 84, in trace
>     result = func(*args, **kw)
>   File "/usr/lib/python2.7/site-packages/mockbuild/buildroot.py", line 84,
> in initialize
>     self._init(prebuild=prebuild, do_log=do_log)
>   File "/usr/lib/python2.7/site-packages/mockbuild/trace_decorator.py", line
> 84, in trace
>     result = func(*args, **kw)
>   File "/usr/lib/python2.7/site-packages/mockbuild/buildroot.py", line 146,
> in _init
>     self._make_build_user()
>   File "/usr/lib/python2.7/site-packages/mockbuild/trace_decorator.py", line
> 84, in trace
>     result = func(*args, **kw)
>   File "/usr/lib/python2.7/site-packages/mockbuild/buildroot.py", line 234,
> in _make_build_user
>     selinux=self.selinux, exclude=excluded)
>   File "/usr/lib/python2.7/site-packages/mockbuild/trace_decorator.py", line
> 84, in trace
>     result = func(*args, **kw)
>   File "/usr/lib/python2.7/site-packages/mockbuild/util.py", line 135, in
> rmtree
>     rmtree(fullname, selinux=selinux, exclude=exclude)
>   File "/usr/lib/python2.7/site-packages/mockbuild/trace_decorator.py", line
> 84, in trace
>     result = func(*args, **kw)
>   File "/usr/lib/python2.7/site-packages/mockbuild/util.py", line 144, in
> rmtree
>     os.rmdir(path)
> OSError: [Errno 16] Device or resource busy:
> '/var/lib/mock/fedora-22-x86_64/root/builddir/nova'

Yup.

> The problem seems to be that to keep reproducibility we remove /builddir/*
> in chroot before we start building (there were good reason for that). We
> only preserve:
> 
> # The build user's homedir is partially cleaned up even when --no-clean is
> # specified in order to prevent garbage from previous builds from altering
> # succesive builds. Mock can be configured to exclude certain
> files/directories
> # from this. Default is SOURCES directory to support nosrc rpms. Paths are
> # relative to build user's homedir
> # config_opts['exclude_from_homedir_cleanup'] = ['build/SOURCES']
> 
> It seems that we mount ['bind_mount_opts']['dirs'] first and then we remove
> /builddir/, therefore if you mount something there is is deleted :(
> I will try to move mounting of those directories after that deleting. That
> should fix this bug.

Thanks for looking at this.

Comment 4 Miroslav Suchý 2015-07-07 14:53:33 UTC
Fixed by commit:
* 101d694  exclude our mountpoints from deleting when deleting homedir in chroot [RHBZ#1239013]

Comment 5 Miroslav Suchý 2015-09-04 11:07:17 UTC
*** Bug 1260063 has been marked as a duplicate of this bug. ***

Comment 6 Dennis Jacobfeuerborn 2015-09-04 11:22:19 UTC
I can't find the commit in any of the branches here:
https://git.fedorahosted.org/cgit/mock.git/

I want to test if this fixes my issue so is this commit the change you mentioned in comment #2 or is this a different fix?

Comment 7 Miroslav Suchý 2015-09-04 12:09:32 UTC
https://git.fedorahosted.org/cgit/mock.git/commit/?id=101d694a68ca2bd517449b1e2981f6e5a79a3130&h=master

And as I see it should be already in mock-1.2.11

Comment 8 Dennis Jacobfeuerborn 2015-09-04 15:06:22 UTC
Yes, I didn't pay attention to the date and though it must have been added very recently. 

Why was my other bug marked as a duplicate of this one though? I'm running version 1.2.12 and have the problem mentioned in the original bug despite the presence of the patch mentioned in this one.