diff options
author | Paul Eggleton <paul.eggleton@linux.intel.com> | 2015-06-16 17:16:51 +0100 |
---|---|---|
committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2015-06-18 09:14:06 +0100 |
commit | 2054b29dd7be74d09b53df178ef87c69b15a809e (patch) | |
tree | 980b4727db3176e15d6bcb9fe373cb791a565af3 | |
parent | e22c18113b11f557c4502738ccdfbb06093a759a (diff) | |
download | poky-2054b29dd7be74d09b53df178ef87c69b15a809e.tar.gz |
devtool: deploy: fix preservation of symlinks and permissions/ownership
It turns out that scp can't be used to copy symlinks because it follows
them instead of copying them, and this is by design (since it emulates
rcp which also behaved this way); the unfortunate result is that
symlinks that point to valid files on the host translate into the host
file being copied to the target (yuck). The simplest alternative that
does not have this undesirable behaviour is to use tar and pipe it over
ssh.
At the same time, it would be even better if we properly reflect file
permissions and ownership on the target that have been established
within the pseudo environment. We can do this by executing the copy
process under pseudo, which turns out to be quite easy with access to
the pseudo environment set up by the build system.
Fixes [YOCTO #7868].
(From OE-Core rev: 69adaed0e982d627ebfa57b360b0ee049ea7a276)
Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
-rw-r--r-- | meta/lib/oeqa/selftest/devtool.py | 48 | ||||
-rw-r--r-- | scripts/lib/devtool/__init__.py | 16 | ||||
-rw-r--r-- | scripts/lib/devtool/deploy.py | 10 |
3 files changed, 69 insertions, 5 deletions
diff --git a/meta/lib/oeqa/selftest/devtool.py b/meta/lib/oeqa/selftest/devtool.py index 4e22e1dfe4..c4a0399832 100644 --- a/meta/lib/oeqa/selftest/devtool.py +++ b/meta/lib/oeqa/selftest/devtool.py | |||
@@ -60,6 +60,27 @@ class DevtoolBase(oeSelfTest): | |||
60 | self.add_command_to_tearDown('bitbake-layers remove-layer %s || true' % templayerdir) | 60 | self.add_command_to_tearDown('bitbake-layers remove-layer %s || true' % templayerdir) |
61 | result = runCmd('bitbake-layers add-layer %s' % templayerdir, cwd=self.builddir) | 61 | result = runCmd('bitbake-layers add-layer %s' % templayerdir, cwd=self.builddir) |
62 | 62 | ||
63 | def _process_ls_output(self, output): | ||
64 | """ | ||
65 | Convert ls -l output to a format we can reasonably compare from one context | ||
66 | to another (e.g. from host to target) | ||
67 | """ | ||
68 | filelist = [] | ||
69 | for line in output.splitlines(): | ||
70 | splitline = line.split() | ||
71 | # Remove trailing . on perms | ||
72 | splitline[0] = splitline[0].rstrip('.') | ||
73 | # Remove leading . on paths | ||
74 | splitline[-1] = splitline[-1].lstrip('.') | ||
75 | # Drop fields we don't want to compare | ||
76 | del splitline[7] | ||
77 | del splitline[6] | ||
78 | del splitline[5] | ||
79 | del splitline[4] | ||
80 | del splitline[1] | ||
81 | filelist.append(' '.join(splitline)) | ||
82 | return filelist | ||
83 | |||
63 | 84 | ||
64 | class DevtoolTests(DevtoolBase): | 85 | class DevtoolTests(DevtoolBase): |
65 | 86 | ||
@@ -796,9 +817,32 @@ class DevtoolTests(DevtoolBase): | |||
796 | console.expect("login:", timeout=120) | 817 | console.expect("login:", timeout=120) |
797 | # Now really test deploy-target | 818 | # Now really test deploy-target |
798 | result = runCmd('devtool deploy-target -c %s root@%s' % (testrecipe, testhost)) | 819 | result = runCmd('devtool deploy-target -c %s root@%s' % (testrecipe, testhost)) |
799 | result = runCmd('ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no root@%s %s' % (testhost, testcommand)) | 820 | # Run a test command to see if it was installed properly |
821 | sshargs = '-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no' | ||
822 | result = runCmd('ssh %s root@%s %s' % (sshargs, testhost, testcommand)) | ||
823 | # Check if it deployed all of the files with the right ownership/perms | ||
824 | # First look on the host - need to do this under pseudo to get the correct ownership/perms | ||
825 | installdir = get_bb_var('D', testrecipe) | ||
826 | fakerootenv = get_bb_var('FAKEROOTENV', testrecipe) | ||
827 | fakerootcmd = get_bb_var('FAKEROOTCMD', testrecipe) | ||
828 | result = runCmd('%s %s find . -type f -exec ls -l {} \;' % (fakerootenv, fakerootcmd), cwd=installdir) | ||
829 | filelist1 = self._process_ls_output(result.output) | ||
830 | |||
831 | # Now look on the target | ||
832 | tempdir2 = tempfile.mkdtemp(prefix='devtoolqa') | ||
833 | self.track_for_cleanup(tempdir2) | ||
834 | tmpfilelist = os.path.join(tempdir2, 'files.txt') | ||
835 | with open(tmpfilelist, 'w') as f: | ||
836 | for line in filelist1: | ||
837 | splitline = line.split() | ||
838 | f.write(splitline[-1] + '\n') | ||
839 | result = runCmd('cat %s | ssh -q %s root@%s \'xargs ls -l\'' % (tmpfilelist, sshargs, testhost)) | ||
840 | filelist2 = self._process_ls_output(result.output) | ||
841 | filelist1.sort(key=lambda item: item.split()[-1]) | ||
842 | filelist2.sort(key=lambda item: item.split()[-1]) | ||
843 | self.assertEqual(filelist1, filelist2) | ||
800 | # Test undeploy-target | 844 | # Test undeploy-target |
801 | result = runCmd('devtool undeploy-target -c %s root@%s' % (testrecipe, testhost)) | 845 | result = runCmd('devtool undeploy-target -c %s root@%s' % (testrecipe, testhost)) |
802 | result = runCmd('ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no root@%s %s' % (testhost, testcommand), ignore_status=True) | 846 | result = runCmd('ssh %s root@%s %s' % (sshargs, testhost, testcommand), ignore_status=True) |
803 | self.assertNotEqual(result, 0, 'undeploy-target did not remove command as it should have') | 847 | self.assertNotEqual(result, 0, 'undeploy-target did not remove command as it should have') |
804 | console.close() | 848 | console.close() |
diff --git a/scripts/lib/devtool/__init__.py b/scripts/lib/devtool/__init__.py index ea0b63e767..61b810c938 100644 --- a/scripts/lib/devtool/__init__.py +++ b/scripts/lib/devtool/__init__.py | |||
@@ -80,6 +80,22 @@ def exec_watch(cmd, **options): | |||
80 | 80 | ||
81 | return buf, None | 81 | return buf, None |
82 | 82 | ||
83 | def exec_fakeroot(d, cmd, **kwargs): | ||
84 | """Run a command under fakeroot (pseudo, in fact) so that it picks up the appropriate file permissions""" | ||
85 | # Grab the command and check it actually exists | ||
86 | fakerootcmd = d.getVar('FAKEROOTCMD', True) | ||
87 | if not os.path.exists(fakerootcmd): | ||
88 | logger.error('pseudo executable %s could not be found - have you run a build yet? pseudo-native should install this and if you have run any build then that should have been built') | ||
89 | return 2 | ||
90 | # Set up the appropriate environment | ||
91 | newenv = dict(os.environ) | ||
92 | fakerootenv = d.getVar('FAKEROOTENV', True) | ||
93 | for varvalue in fakerootenv.split(): | ||
94 | if '=' in varvalue: | ||
95 | splitval = varvalue.split('=', 1) | ||
96 | newenv[splitval[0]] = splitval[1] | ||
97 | return subprocess.call("%s %s" % (fakerootcmd, cmd), env=newenv, **kwargs) | ||
98 | |||
83 | def setup_tinfoil(): | 99 | def setup_tinfoil(): |
84 | """Initialize tinfoil api from bitbake""" | 100 | """Initialize tinfoil api from bitbake""" |
85 | import scriptpath | 101 | import scriptpath |
diff --git a/scripts/lib/devtool/deploy.py b/scripts/lib/devtool/deploy.py index ca74a8e51d..448db9637d 100644 --- a/scripts/lib/devtool/deploy.py +++ b/scripts/lib/devtool/deploy.py | |||
@@ -19,7 +19,7 @@ | |||
19 | import os | 19 | import os |
20 | import subprocess | 20 | import subprocess |
21 | import logging | 21 | import logging |
22 | from devtool import exec_build_env_command, setup_tinfoil, DevtoolError | 22 | from devtool import exec_fakeroot, setup_tinfoil, DevtoolError |
23 | 23 | ||
24 | logger = logging.getLogger('devtool') | 24 | logger = logging.getLogger('devtool') |
25 | 25 | ||
@@ -73,9 +73,13 @@ def deploy(args, config, basepath, workspace): | |||
73 | extraoptions = '' | 73 | extraoptions = '' |
74 | if args.no_host_check: | 74 | if args.no_host_check: |
75 | extraoptions += '-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no' | 75 | extraoptions += '-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no' |
76 | if not args.show_status: | 76 | if args.show_status: |
77 | tarextractopts = 'xv' | ||
78 | else: | ||
79 | tarextractopts = 'x' | ||
77 | extraoptions += ' -q' | 80 | extraoptions += ' -q' |
78 | ret = subprocess.call('scp -r %s %s/* %s:%s' % (extraoptions, recipe_outdir, args.target, destdir), shell=True) | 81 | # We cannot use scp here, because it doesn't preserve symlinks |
82 | ret = exec_fakeroot(rd, 'tar cf - . | ssh %s %s \'tar %s -C %s -f -\'' % (extraoptions, args.target, tarextractopts, destdir), cwd=recipe_outdir, shell=True) | ||
79 | if ret != 0: | 83 | if ret != 0: |
80 | raise DevtoolError('Deploy failed - rerun with -s to get a complete ' | 84 | raise DevtoolError('Deploy failed - rerun with -s to get a complete ' |
81 | 'error message') | 85 | 'error message') |