We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Chroot doesn't work with busybox
With busybox, `/bin/sh` can be a relative symlink, and won't resolve correctly prior to `chroot()`.

```
PLAY ***************************************************************************

TASK [setup] *******************************************************************
An exception occurred during task execution. To see the full traceback, use -vvv. The error was: OSError: [Errno 2] No such file or directory: '/rootfs/bin/sh'
fatal: [/rootfs]: FAILED! => {"failed": true, "stdout": ""}

NO MORE HOSTS LEFT *************************************************************

PLAY RECAP *********************************************************************
/rootfs                    : ok=0    changed=0    unreachable=0    failed=1   
```

```
# ls -la /rootfs/bin/sh
lrwxrwxrwx 1 root root 12 Jun  1 22:17 /rootfs/bin/sh -> /bin/busybox
```

A sensible solution for this would be through an option to override [the hardcoded shell path](https://github.com/ansible/ansible/blob/v2.0.0.1-1/lib/ansible/plugins/connection/chroot.py#L67-L68), or something like that.


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `lib/ansible/plugins/connection/chroot.py`
Content:
```
1 # Based on local.py (c) 2012, Michael DeHaan <michael.dehaan@gmail.com>
2 # (c) 2013, Maykel Moya <mmoya@speedyrails.com>
3 # (c) 2015, Toshio Kuratomi <tkuratomi@ansible.com>
4 #
5 # This file is part of Ansible
6 #
7 # Ansible is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation, either version 3 of the License, or
10 # (at your option) any later version.
11 #
12 # Ansible is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 # GNU General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License
18 # along with Ansible.  If not, see <http://www.gnu.org/licenses/>.
19 from __future__ import (absolute_import, division, print_function)
20 __metaclass__ = type
21 
22 import distutils.spawn
23 import os
24 import os.path
25 import pipes
26 import subprocess
27 import traceback
28 
29 from ansible import constants as C
30 from ansible.errors import AnsibleError
31 from ansible.plugins.connection import ConnectionBase, BUFSIZE
32 from ansible.module_utils.basic import is_executable
33 from ansible.utils.unicode import to_bytes
34 
35 try:
36     from __main__ import display
37 except ImportError:
38     from ansible.utils.display import Display
39     display = Display()
40 
41 
42 class Connection(ConnectionBase):
43     ''' Local chroot based connections '''
44 
45     transport = 'chroot'
46     has_pipelining = True
47     # su currently has an undiagnosed issue with calculating the file
48     # checksums (so copy, for instance, doesn't work right)
49     # Have to look into that before re-enabling this
50     become_methods = frozenset(C.BECOME_METHODS).difference(('su',))
51 
52     def __init__(self, play_context, new_stdin, *args, **kwargs):
53         super(Connection, self).__init__(play_context, new_stdin, *args, **kwargs)
54 
55         self.chroot = self._play_context.remote_addr
56 
57         if os.geteuid() != 0:
58             raise AnsibleError("chroot connection requires running as root")
59 
60         # we're running as root on the local system so do some
61         # trivial checks for ensuring 'host' is actually a chroot'able dir
62         if not os.path.isdir(self.chroot):
63             raise AnsibleError("%s is not a directory" % self.chroot)
64 
65         chrootsh = os.path.join(self.chroot, 'bin/sh')
66         if not is_executable(chrootsh):
67             raise AnsibleError("%s does not look like a chrootable dir (/bin/sh missing)" % self.chroot)
68 
69         self.chroot_cmd = distutils.spawn.find_executable('chroot')
70         if not self.chroot_cmd:
71             raise AnsibleError("chroot command not found in PATH")
72 
73     def _connect(self):
74         ''' connect to the chroot; nothing to do here '''
75         super(Connection, self)._connect()
76         if not self._connected:
77             display.vvv("THIS IS A LOCAL CHROOT DIR", host=self.chroot)
78             self._connected = True
79 
80     def _buffered_exec_command(self, cmd, stdin=subprocess.PIPE):
81         ''' run a command on the chroot.  This is only needed for implementing
82         put_file() get_file() so that we don't have to read the whole file
83         into memory.
84 
85         compared to exec_command() it looses some niceties like being able to
86         return the process's exit code immediately.
87         '''
88         executable = C.DEFAULT_EXECUTABLE.split()[0] if C.DEFAULT_EXECUTABLE else '/bin/sh'
89         local_cmd = [self.chroot_cmd, self.chroot, executable, '-c', cmd]
90 
91         display.vvv("EXEC %s" % (local_cmd), host=self.chroot)
92         local_cmd = [to_bytes(i, errors='strict') for i in local_cmd]
93         p = subprocess.Popen(local_cmd, shell=False, stdin=stdin,
94                 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
95 
96         return p
97 
98     def exec_command(self, cmd, in_data=None, sudoable=False):
99         ''' run a command on the chroot '''
100         super(Connection, self).exec_command(cmd, in_data=in_data, sudoable=sudoable)
101 
102         p = self._buffered_exec_command(cmd)
103 
104         stdout, stderr = p.communicate(in_data)
105         return (p.returncode, stdout, stderr)
106 
107     def _prefix_login_path(self, remote_path):
108         ''' Make sure that we put files into a standard path
109 
110             If a path is relative, then we need to choose where to put it.
111             ssh chooses $HOME but we aren't guaranteed that a home dir will
112             exist in any given chroot.  So for now we're choosing "/" instead.
113             This also happens to be the former default.
114 
115             Can revisit using $HOME instead if it's a problem
116         '''
117         if not remote_path.startswith(os.path.sep):
118             remote_path = os.path.join(os.path.sep, remote_path)
119         return os.path.normpath(remote_path)
120 
121     def put_file(self, in_path, out_path):
122         ''' transfer a file from local to chroot '''
123         super(Connection, self).put_file(in_path, out_path)
124         display.vvv("PUT %s TO %s" % (in_path, out_path), host=self.chroot)
125 
126         out_path = pipes.quote(self._prefix_login_path(out_path))
127         try:
128             with open(to_bytes(in_path, errors='strict'), 'rb') as in_file:
129                 try:
130                     p = self._buffered_exec_command('dd of=%s bs=%s' % (out_path, BUFSIZE), stdin=in_file)
131                 except OSError:
132                     raise AnsibleError("chroot connection requires dd command in the chroot")
133                 try:
134                     stdout, stderr = p.communicate()
135                 except:
136                     traceback.print_exc()
137                     raise AnsibleError("failed to transfer file %s to %s" % (in_path, out_path))
138                 if p.returncode != 0:
139                     raise AnsibleError("failed to transfer file %s to %s:\n%s\n%s" % (in_path, out_path, stdout, stderr))
140         except IOError:
141             raise AnsibleError("file or module does not exist at: %s" % in_path)
142 
143     def fetch_file(self, in_path, out_path):
144         ''' fetch a file from chroot to local '''
145         super(Connection, self).fetch_file(in_path, out_path)
146         display.vvv("FETCH %s TO %s" % (in_path, out_path), host=self.chroot)
147 
148         in_path = pipes.quote(self._prefix_login_path(in_path))
149         try:
150             p = self._buffered_exec_command('dd if=%s bs=%s' % (in_path, BUFSIZE))
151         except OSError:
152             raise AnsibleError("chroot connection requires dd command in the chroot")
153 
154         with open(to_bytes(out_path, errors='strict'), 'wb+') as out_file:
155             try:
156                 chunk = p.stdout.read(BUFSIZE)
157                 while chunk:
158                     out_file.write(chunk)
159                     chunk = p.stdout.read(BUFSIZE)
160             except:
161                 traceback.print_exc()
162                 raise AnsibleError("failed to transfer file %s to %s" % (in_path, out_path))
163             stdout, stderr = p.communicate()
164             if p.returncode != 0:
165                 raise AnsibleError("failed to transfer file %s to %s:\n%s\n%s" % (in_path, out_path, stdout, stderr))
166 
167     def close(self):
168         ''' terminate the connection; nothing to do here '''
169         super(Connection, self).close()
170         self._connected = False
171
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/lib/ansible/plugins/connection/chroot.py b/lib/ansible/plugins/connection/chroot.py
--- a/lib/ansible/plugins/connection/chroot.py
+++ b/lib/ansible/plugins/connection/chroot.py
@@ -63,7 +63,11 @@
             raise AnsibleError("%s is not a directory" % self.chroot)
 
         chrootsh = os.path.join(self.chroot, 'bin/sh')
-        if not is_executable(chrootsh):
+        # Want to check for a usable bourne shell inside the chroot.
+        # is_executable() == True is sufficient.  For symlinks it
+        # gets really complicated really fast.  So we punt on finding that
+        # out.  As long as it's a symlink we assume that it will work
+        if not (is_executable(chrootsh) or (os.path.lexists(chrootsh) and os.path.islink(chrootsh))):
             raise AnsibleError("%s does not look like a chrootable dir (/bin/sh missing)" % self.chroot)
 
         self.chroot_cmd = distutils.spawn.find_executable('chroot')

{"golden_diff": "diff --git a/lib/ansible/plugins/connection/chroot.py b/lib/ansible/plugins/connection/chroot.py\n--- a/lib/ansible/plugins/connection/chroot.py\n+++ b/lib/ansible/plugins/connection/chroot.py\n@@ -63,7 +63,11 @@\n             raise AnsibleError(\"%s is not a directory\" % self.chroot)\n \n         chrootsh = os.path.join(self.chroot, 'bin/sh')\n-        if not is_executable(chrootsh):\n+        # Want to check for a usable bourne shell inside the chroot.\n+        # is_executable() == True is sufficient.  For symlinks it\n+        # gets really complicated really fast.  So we punt on finding that\n+        # out.  As long as it's a symlink we assume that it will work\n+        if not (is_executable(chrootsh) or (os.path.lexists(chrootsh) and os.path.islink(chrootsh))):\n             raise AnsibleError(\"%s does not look like a chrootable dir (/bin/sh missing)\" % self.chroot)\n \n         self.chroot_cmd = distutils.spawn.find_executable('chroot')\n", "issue": "Chroot doesn't work with busybox\nWith busybox, `/bin/sh` can be a relative symlink, and won't resolve correctly prior to `chroot()`.\n\n```\nPLAY ***************************************************************************\n\nTASK [setup] *******************************************************************\nAn exception occurred during task execution. To see the full traceback, use -vvv. The error was: OSError: [Errno 2] No such file or directory: '/rootfs/bin/sh'\nfatal: [/rootfs]: FAILED! => {\"failed\": true, \"stdout\": \"\"}\n\nNO MORE HOSTS LEFT *************************************************************\n\nPLAY RECAP *********************************************************************\n/rootfs                    : ok=0    changed=0    unreachable=0    failed=1   \n```\n\n```\n# ls -la /rootfs/bin/sh\nlrwxrwxrwx 1 root root 12 Jun  1 22:17 /rootfs/bin/sh -> /bin/busybox\n```\n\nA sensible solution for this would be through an option to override [the hardcoded shell path](https://github.com/ansible/ansible/blob/v2.0.0.1-1/lib/ansible/plugins/connection/chroot.py#L67-L68), or something like that.\n\n", "before_files": [{"content": "# Based on local.py (c) 2012, Michael DeHaan <michael.dehaan@gmail.com>\n# (c) 2013, Maykel Moya <mmoya@speedyrails.com>\n# (c) 2015, Toshio Kuratomi <tkuratomi@ansible.com>\n#\n# This file is part of Ansible\n#\n# Ansible is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# Ansible is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with Ansible.  If not, see <http://www.gnu.org/licenses/>.\nfrom __future__ import (absolute_import, division, print_function)\n__metaclass__ = type\n\nimport distutils.spawn\nimport os\nimport os.path\nimport pipes\nimport subprocess\nimport traceback\n\nfrom ansible import constants as C\nfrom ansible.errors import AnsibleError\nfrom ansible.plugins.connection import ConnectionBase, BUFSIZE\nfrom ansible.module_utils.basic import is_executable\nfrom ansible.utils.unicode import to_bytes\n\ntry:\n    from __main__ import display\nexcept ImportError:\n    from ansible.utils.display import Display\n    display = Display()\n\n\nclass Connection(ConnectionBase):\n    ''' Local chroot based connections '''\n\n    transport = 'chroot'\n    has_pipelining = True\n    # su currently has an undiagnosed issue with calculating the file\n    # checksums (so copy, for instance, doesn't work right)\n    # Have to look into that before re-enabling this\n    become_methods = frozenset(C.BECOME_METHODS).difference(('su',))\n\n    def __init__(self, play_context, new_stdin, *args, **kwargs):\n        super(Connection, self).__init__(play_context, new_stdin, *args, **kwargs)\n\n        self.chroot = self._play_context.remote_addr\n\n        if os.geteuid() != 0:\n            raise AnsibleError(\"chroot connection requires running as root\")\n\n        # we're running as root on the local system so do some\n        # trivial checks for ensuring 'host' is actually a chroot'able dir\n        if not os.path.isdir(self.chroot):\n            raise AnsibleError(\"%s is not a directory\" % self.chroot)\n\n        chrootsh = os.path.join(self.chroot, 'bin/sh')\n        if not is_executable(chrootsh):\n            raise AnsibleError(\"%s does not look like a chrootable dir (/bin/sh missing)\" % self.chroot)\n\n        self.chroot_cmd = distutils.spawn.find_executable('chroot')\n        if not self.chroot_cmd:\n            raise AnsibleError(\"chroot command not found in PATH\")\n\n    def _connect(self):\n        ''' connect to the chroot; nothing to do here '''\n        super(Connection, self)._connect()\n        if not self._connected:\n            display.vvv(\"THIS IS A LOCAL CHROOT DIR\", host=self.chroot)\n            self._connected = True\n\n    def _buffered_exec_command(self, cmd, stdin=subprocess.PIPE):\n        ''' run a command on the chroot.  This is only needed for implementing\n        put_file() get_file() so that we don't have to read the whole file\n        into memory.\n\n        compared to exec_command() it looses some niceties like being able to\n        return the process's exit code immediately.\n        '''\n        executable = C.DEFAULT_EXECUTABLE.split()[0] if C.DEFAULT_EXECUTABLE else '/bin/sh'\n        local_cmd = [self.chroot_cmd, self.chroot, executable, '-c', cmd]\n\n        display.vvv(\"EXEC %s\" % (local_cmd), host=self.chroot)\n        local_cmd = [to_bytes(i, errors='strict') for i in local_cmd]\n        p = subprocess.Popen(local_cmd, shell=False, stdin=stdin,\n                stdout=subprocess.PIPE, stderr=subprocess.PIPE)\n\n        return p\n\n    def exec_command(self, cmd, in_data=None, sudoable=False):\n        ''' run a command on the chroot '''\n        super(Connection, self).exec_command(cmd, in_data=in_data, sudoable=sudoable)\n\n        p = self._buffered_exec_command(cmd)\n\n        stdout, stderr = p.communicate(in_data)\n        return (p.returncode, stdout, stderr)\n\n    def _prefix_login_path(self, remote_path):\n        ''' Make sure that we put files into a standard path\n\n            If a path is relative, then we need to choose where to put it.\n            ssh chooses $HOME but we aren't guaranteed that a home dir will\n            exist in any given chroot.  So for now we're choosing \"/\" instead.\n            This also happens to be the former default.\n\n            Can revisit using $HOME instead if it's a problem\n        '''\n        if not remote_path.startswith(os.path.sep):\n            remote_path = os.path.join(os.path.sep, remote_path)\n        return os.path.normpath(remote_path)\n\n    def put_file(self, in_path, out_path):\n        ''' transfer a file from local to chroot '''\n        super(Connection, self).put_file(in_path, out_path)\n        display.vvv(\"PUT %s TO %s\" % (in_path, out_path), host=self.chroot)\n\n        out_path = pipes.quote(self._prefix_login_path(out_path))\n        try:\n            with open(to_bytes(in_path, errors='strict'), 'rb') as in_file:\n                try:\n                    p = self._buffered_exec_command('dd of=%s bs=%s' % (out_path, BUFSIZE), stdin=in_file)\n                except OSError:\n                    raise AnsibleError(\"chroot connection requires dd command in the chroot\")\n                try:\n                    stdout, stderr = p.communicate()\n                except:\n                    traceback.print_exc()\n                    raise AnsibleError(\"failed to transfer file %s to %s\" % (in_path, out_path))\n                if p.returncode != 0:\n                    raise AnsibleError(\"failed to transfer file %s to %s:\\n%s\\n%s\" % (in_path, out_path, stdout, stderr))\n        except IOError:\n            raise AnsibleError(\"file or module does not exist at: %s\" % in_path)\n\n    def fetch_file(self, in_path, out_path):\n        ''' fetch a file from chroot to local '''\n        super(Connection, self).fetch_file(in_path, out_path)\n        display.vvv(\"FETCH %s TO %s\" % (in_path, out_path), host=self.chroot)\n\n        in_path = pipes.quote(self._prefix_login_path(in_path))\n        try:\n            p = self._buffered_exec_command('dd if=%s bs=%s' % (in_path, BUFSIZE))\n        except OSError:\n            raise AnsibleError(\"chroot connection requires dd command in the chroot\")\n\n        with open(to_bytes(out_path, errors='strict'), 'wb+') as out_file:\n            try:\n                chunk = p.stdout.read(BUFSIZE)\n                while chunk:\n                    out_file.write(chunk)\n                    chunk = p.stdout.read(BUFSIZE)\n            except:\n                traceback.print_exc()\n                raise AnsibleError(\"failed to transfer file %s to %s\" % (in_path, out_path))\n            stdout, stderr = p.communicate()\n            if p.returncode != 0:\n                raise AnsibleError(\"failed to transfer file %s to %s:\\n%s\\n%s\" % (in_path, out_path, stdout, stderr))\n\n    def close(self):\n        ''' terminate the connection; nothing to do here '''\n        super(Connection, self).close()\n        self._connected = False\n", "path": "lib/ansible/plugins/connection/chroot.py"}], "after_files": [{"content": "# Based on local.py (c) 2012, Michael DeHaan <michael.dehaan@gmail.com>\n# (c) 2013, Maykel Moya <mmoya@speedyrails.com>\n# (c) 2015, Toshio Kuratomi <tkuratomi@ansible.com>\n#\n# This file is part of Ansible\n#\n# Ansible is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# Ansible is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with Ansible.  If not, see <http://www.gnu.org/licenses/>.\nfrom __future__ import (absolute_import, division, print_function)\n__metaclass__ = type\n\nimport distutils.spawn\nimport os\nimport os.path\nimport pipes\nimport subprocess\nimport traceback\n\nfrom ansible import constants as C\nfrom ansible.errors import AnsibleError\nfrom ansible.plugins.connection import ConnectionBase, BUFSIZE\nfrom ansible.module_utils.basic import is_executable\nfrom ansible.utils.unicode import to_bytes\n\ntry:\n    from __main__ import display\nexcept ImportError:\n    from ansible.utils.display import Display\n    display = Display()\n\n\nclass Connection(ConnectionBase):\n    ''' Local chroot based connections '''\n\n    transport = 'chroot'\n    has_pipelining = True\n    # su currently has an undiagnosed issue with calculating the file\n    # checksums (so copy, for instance, doesn't work right)\n    # Have to look into that before re-enabling this\n    become_methods = frozenset(C.BECOME_METHODS).difference(('su',))\n\n    def __init__(self, play_context, new_stdin, *args, **kwargs):\n        super(Connection, self).__init__(play_context, new_stdin, *args, **kwargs)\n\n        self.chroot = self._play_context.remote_addr\n\n        if os.geteuid() != 0:\n            raise AnsibleError(\"chroot connection requires running as root\")\n\n        # we're running as root on the local system so do some\n        # trivial checks for ensuring 'host' is actually a chroot'able dir\n        if not os.path.isdir(self.chroot):\n            raise AnsibleError(\"%s is not a directory\" % self.chroot)\n\n        chrootsh = os.path.join(self.chroot, 'bin/sh')\n        # Want to check for a usable bourne shell inside the chroot.\n        # is_executable() == True is sufficient.  For symlinks it\n        # gets really complicated really fast.  So we punt on finding that\n        # out.  As long as it's a symlink we assume that it will work\n        if not (is_executable(chrootsh) or (os.path.lexists(chrootsh) and os.path.islink(chrootsh))):\n            raise AnsibleError(\"%s does not look like a chrootable dir (/bin/sh missing)\" % self.chroot)\n\n        self.chroot_cmd = distutils.spawn.find_executable('chroot')\n        if not self.chroot_cmd:\n            raise AnsibleError(\"chroot command not found in PATH\")\n\n    def _connect(self):\n        ''' connect to the chroot; nothing to do here '''\n        super(Connection, self)._connect()\n        if not self._connected:\n            display.vvv(\"THIS IS A LOCAL CHROOT DIR\", host=self.chroot)\n            self._connected = True\n\n    def _buffered_exec_command(self, cmd, stdin=subprocess.PIPE):\n        ''' run a command on the chroot.  This is only needed for implementing\n        put_file() get_file() so that we don't have to read the whole file\n        into memory.\n\n        compared to exec_command() it looses some niceties like being able to\n        return the process's exit code immediately.\n        '''\n        executable = C.DEFAULT_EXECUTABLE.split()[0] if C.DEFAULT_EXECUTABLE else '/bin/sh'\n        local_cmd = [self.chroot_cmd, self.chroot, executable, '-c', cmd]\n\n        display.vvv(\"EXEC %s\" % (local_cmd), host=self.chroot)\n        local_cmd = [to_bytes(i, errors='strict') for i in local_cmd]\n        p = subprocess.Popen(local_cmd, shell=False, stdin=stdin,\n                stdout=subprocess.PIPE, stderr=subprocess.PIPE)\n\n        return p\n\n    def exec_command(self, cmd, in_data=None, sudoable=False):\n        ''' run a command on the chroot '''\n        super(Connection, self).exec_command(cmd, in_data=in_data, sudoable=sudoable)\n\n        p = self._buffered_exec_command(cmd)\n\n        stdout, stderr = p.communicate(in_data)\n        return (p.returncode, stdout, stderr)\n\n    def _prefix_login_path(self, remote_path):\n        ''' Make sure that we put files into a standard path\n\n            If a path is relative, then we need to choose where to put it.\n            ssh chooses $HOME but we aren't guaranteed that a home dir will\n            exist in any given chroot.  So for now we're choosing \"/\" instead.\n            This also happens to be the former default.\n\n            Can revisit using $HOME instead if it's a problem\n        '''\n        if not remote_path.startswith(os.path.sep):\n            remote_path = os.path.join(os.path.sep, remote_path)\n        return os.path.normpath(remote_path)\n\n    def put_file(self, in_path, out_path):\n        ''' transfer a file from local to chroot '''\n        super(Connection, self).put_file(in_path, out_path)\n        display.vvv(\"PUT %s TO %s\" % (in_path, out_path), host=self.chroot)\n\n        out_path = pipes.quote(self._prefix_login_path(out_path))\n        try:\n            with open(to_bytes(in_path, errors='strict'), 'rb') as in_file:\n                try:\n                    p = self._buffered_exec_command('dd of=%s bs=%s' % (out_path, BUFSIZE), stdin=in_file)\n                except OSError:\n                    raise AnsibleError(\"chroot connection requires dd command in the chroot\")\n                try:\n                    stdout, stderr = p.communicate()\n                except:\n                    traceback.print_exc()\n                    raise AnsibleError(\"failed to transfer file %s to %s\" % (in_path, out_path))\n                if p.returncode != 0:\n                    raise AnsibleError(\"failed to transfer file %s to %s:\\n%s\\n%s\" % (in_path, out_path, stdout, stderr))\n        except IOError:\n            raise AnsibleError(\"file or module does not exist at: %s\" % in_path)\n\n    def fetch_file(self, in_path, out_path):\n        ''' fetch a file from chroot to local '''\n        super(Connection, self).fetch_file(in_path, out_path)\n        display.vvv(\"FETCH %s TO %s\" % (in_path, out_path), host=self.chroot)\n\n        in_path = pipes.quote(self._prefix_login_path(in_path))\n        try:\n            p = self._buffered_exec_command('dd if=%s bs=%s' % (in_path, BUFSIZE))\n        except OSError:\n            raise AnsibleError(\"chroot connection requires dd command in the chroot\")\n\n        with open(to_bytes(out_path, errors='strict'), 'wb+') as out_file:\n            try:\n                chunk = p.stdout.read(BUFSIZE)\n                while chunk:\n                    out_file.write(chunk)\n                    chunk = p.stdout.read(BUFSIZE)\n            except:\n                traceback.print_exc()\n                raise AnsibleError(\"failed to transfer file %s to %s\" % (in_path, out_path))\n            stdout, stderr = p.communicate()\n            if p.returncode != 0:\n                raise AnsibleError(\"failed to transfer file %s to %s:\\n%s\\n%s\" % (in_path, out_path, stdout, stderr))\n\n    def close(self):\n        ''' terminate the connection; nothing to do here '''\n        super(Connection, self).close()\n        self._connected = False\n", "path": "lib/ansible/plugins/connection/chroot.py"}]}