mirror of
https://github.com/ARM-software/devlib.git
synced 2025-01-31 02:00:45 +00:00
target: Robustify read_tree_values()
target.read_tree_values() has several weaknesses. It doesn't support files with ':' in their name, and it fails when reading binary files. In essence, these limitations are cause by its fragile implementation based on grep in shutils. In order to robustify read_tree_values(), use tar and base64 to send the content of a tree to the host, which can then process it from there. In the process, read_tree_values() gains two new arguments: - decode_unicode: must be set to work text/utf-8 content; - strip_null_chars: must be set to remove '\00' chars from text files. Both are set to true by default to keep backward compatibility with the existing code. Suggested-by: Douglas Raillard <douglas.raillard@arm.com> Signed-off-by: Quentin Perret <quentin.perret@arm.com>
This commit is contained in:
parent
911a9f2ef4
commit
7e79eeb9cb
@ -255,26 +255,32 @@ sched_get_kernel_attributes() {
|
|||||||
# Misc
|
# Misc
|
||||||
################################################################################
|
################################################################################
|
||||||
|
|
||||||
read_tree_values() {
|
read_tree_tgz_b64() {
|
||||||
BASEPATH=$1
|
BASEPATH=$1
|
||||||
MAXDEPTH=$2
|
MAXDEPTH=$2
|
||||||
|
TMPBASE=$3
|
||||||
|
|
||||||
if [ ! -e $BASEPATH ]; then
|
if [ ! -e $BASEPATH ]; then
|
||||||
echo "ERROR: $BASEPATH does not exist"
|
echo "ERROR: $BASEPATH does not exist"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
PATHS=$($BUSYBOX find $BASEPATH -follow -maxdepth $MAXDEPTH)
|
cd $TMPBASE
|
||||||
i=0
|
TMP_FOLDER=$($BUSYBOX realpath $($BUSYBOX mktemp -d XXXXXX))
|
||||||
for path in $PATHS; do
|
|
||||||
i=$(expr $i + 1)
|
# 'tar' doesn't work as expected on debugfs, so copy the tree first to
|
||||||
if [ $i -gt 1 ]; then
|
# workaround the issue
|
||||||
break;
|
cd $BASEPATH
|
||||||
fi
|
for CUR_FILE in $($BUSYBOX find . -follow -type f -maxdepth $MAXDEPTH); do
|
||||||
|
$BUSYBOX cp --parents $CUR_FILE $TMP_FOLDER/ 2> /dev/null
|
||||||
done
|
done
|
||||||
if [ $i -gt 1 ]; then
|
|
||||||
$BUSYBOX grep -s '' $PATHS
|
cd $TMP_FOLDER
|
||||||
fi
|
$BUSYBOX tar cz * | $BUSYBOX base64
|
||||||
|
|
||||||
|
# Clean-up the tmp folder since we won't need it any more
|
||||||
|
cd $TMPBASE
|
||||||
|
rm -rf $TMP_FOLDER
|
||||||
}
|
}
|
||||||
|
|
||||||
get_linux_system_id() {
|
get_linux_system_id() {
|
||||||
@ -347,8 +353,8 @@ ftrace_get_function_stats)
|
|||||||
hotplug_online_all)
|
hotplug_online_all)
|
||||||
hotplug_online_all
|
hotplug_online_all
|
||||||
;;
|
;;
|
||||||
read_tree_values)
|
read_tree_tgz_b64)
|
||||||
read_tree_values $*
|
read_tree_tgz_b64 $*
|
||||||
;;
|
;;
|
||||||
get_linux_system_id)
|
get_linux_system_id)
|
||||||
get_linux_system_id $*
|
get_linux_system_id $*
|
||||||
|
@ -13,6 +13,9 @@
|
|||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
#
|
#
|
||||||
|
|
||||||
|
import io
|
||||||
|
import base64
|
||||||
|
import gzip
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import time
|
import time
|
||||||
@ -684,23 +687,61 @@ class Target(object):
|
|||||||
timeout = duration + 10
|
timeout = duration + 10
|
||||||
self.execute('sleep {}'.format(duration), timeout=timeout)
|
self.execute('sleep {}'.format(duration), timeout=timeout)
|
||||||
|
|
||||||
def read_tree_values_flat(self, path, depth=1, check_exit_code=True):
|
def read_tree_values_flat(self, path, depth=1, check_exit_code=True,
|
||||||
command = 'read_tree_values {} {}'.format(quote(path), depth)
|
decode_unicode=True, strip_null_chars=True):
|
||||||
|
command = 'read_tree_tgz_b64 {} {} {}'.format(quote(path), depth,
|
||||||
|
quote(self.working_directory))
|
||||||
output = self._execute_util(command, as_root=self.is_rooted,
|
output = self._execute_util(command, as_root=self.is_rooted,
|
||||||
check_exit_code=check_exit_code)
|
check_exit_code=check_exit_code)
|
||||||
|
|
||||||
accumulator = defaultdict(list)
|
result = {}
|
||||||
for entry in output.strip().split('\n'):
|
|
||||||
if ':' not in entry:
|
# Unpack the archive in memory
|
||||||
continue
|
tar_gz = base64.b64decode(output)
|
||||||
path, value = entry.strip().split(':', 1)
|
tar_gz_bytes = io.BytesIO(tar_gz)
|
||||||
accumulator[path].append(value)
|
tar_buf = gzip.GzipFile(fileobj=tar_gz_bytes).read()
|
||||||
|
tar_bytes = io.BytesIO(tar_buf)
|
||||||
|
with tarfile.open(fileobj=tar_bytes) as tar:
|
||||||
|
for member in tar.getmembers():
|
||||||
|
try:
|
||||||
|
content_f = tar.extractfile(member)
|
||||||
|
# ignore exotic members like sockets
|
||||||
|
except Exception:
|
||||||
|
continue
|
||||||
|
# if it is a file and not a folder
|
||||||
|
if content_f:
|
||||||
|
content = content_f.read()
|
||||||
|
if decode_unicode:
|
||||||
|
try:
|
||||||
|
content = content.decode('utf-8').strip()
|
||||||
|
if strip_null_chars:
|
||||||
|
content = content.replace('\x00', '').strip()
|
||||||
|
except UnicodeDecodeError:
|
||||||
|
content = ''
|
||||||
|
|
||||||
|
name = self.path.join(path, member.name)
|
||||||
|
result[name] = content
|
||||||
|
|
||||||
result = {k: '\n'.join(v).strip() for k, v in accumulator.items()}
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def read_tree_values(self, path, depth=1, dictcls=dict, check_exit_code=True):
|
def read_tree_values(self, path, depth=1, dictcls=dict,
|
||||||
value_map = self.read_tree_values_flat(path, depth, check_exit_code)
|
check_exit_code=True, decode_unicode=True,
|
||||||
|
strip_null_chars=True):
|
||||||
|
"""
|
||||||
|
Reads the content of all files under a given tree
|
||||||
|
|
||||||
|
:path: path to the tree
|
||||||
|
:depth: maximum tree depth to read
|
||||||
|
:dictcls: type of the dict used to store the results
|
||||||
|
:check_exit_code: raise an exception if the shutil command fails
|
||||||
|
:decode_unicode: decode the content of files as utf-8
|
||||||
|
:strip_null_chars: remove '\x00' chars from the content of utf-8
|
||||||
|
decoded files
|
||||||
|
|
||||||
|
:returns: a tree-like dict with the content of files as leafs
|
||||||
|
"""
|
||||||
|
value_map = self.read_tree_values_flat(path, depth, check_exit_code,
|
||||||
|
decode_unicode, strip_null_chars)
|
||||||
return _build_path_tree(value_map, path, self.path.sep, dictcls)
|
return _build_path_tree(value_map, path, self.path.sep, dictcls)
|
||||||
|
|
||||||
# internal methods
|
# internal methods
|
||||||
|
Loading…
x
Reference in New Issue
Block a user