From 8d039f7bd84ce8cbbbe9ba5677f5a353d6224b66 Mon Sep 17 00:00:00 2001 From: Ethan Yonker Date: Fri, 3 Feb 2017 14:26:15 -0600 Subject: libtar: support backing up and restoring new Android user.* xattr Support for backing up and restoring user.default, user.inode_cache, and user.inode_code_cache xattrs introduced in Android 7.x Change-Id: I6e0aa7fc9cd30ed004ef28ebb58d60a82e518123 --- libtar/Android.mk | 4 +- libtar/android_utils.c | 123 +++++++++++++++++++++++++++++++++++++++++++++++++ libtar/android_utils.h | 22 +++++++++ libtar/append.c | 27 +++++++++++ libtar/block.c | 120 ++++++++++++++++++++++++++++++++++++++++++++--- libtar/extract.c | 29 +++++++++++- libtar/libtar.h | 4 ++ 7 files changed, 320 insertions(+), 9 deletions(-) create mode 100644 libtar/android_utils.c create mode 100644 libtar/android_utils.h (limited to 'libtar') diff --git a/libtar/Android.mk b/libtar/Android.mk index c8905d93f..90a5006c5 100644 --- a/libtar/Android.mk +++ b/libtar/Android.mk @@ -5,7 +5,7 @@ include $(CLEAR_VARS) LOCAL_MODULE := libtar LOCAL_MODULE_TAGS := eng optional -LOCAL_SRC_FILES := append.c block.c decode.c encode.c extract.c handle.c output.c util.c wrapper.c basename.c strmode.c libtar_hash.c libtar_list.c dirname.c +LOCAL_SRC_FILES := append.c block.c decode.c encode.c extract.c handle.c output.c util.c wrapper.c basename.c strmode.c libtar_hash.c libtar_list.c dirname.c android_utils.c LOCAL_C_INCLUDES += $(LOCAL_PATH) \ external/zlib LOCAL_SHARED_LIBRARIES += libz libc @@ -26,7 +26,7 @@ include $(CLEAR_VARS) LOCAL_MODULE := libtar_static LOCAL_MODULE_TAGS := eng optional -LOCAL_SRC_FILES := append.c block.c decode.c encode.c extract.c handle.c output.c util.c wrapper.c basename.c strmode.c libtar_hash.c libtar_list.c dirname.c +LOCAL_SRC_FILES := append.c block.c decode.c encode.c extract.c handle.c output.c util.c wrapper.c basename.c strmode.c libtar_hash.c libtar_list.c dirname.c android_utils.c LOCAL_C_INCLUDES += $(LOCAL_PATH) \ external/zlib LOCAL_STATIC_LIBRARIES += libz libc diff --git a/libtar/android_utils.c b/libtar/android_utils.c new file mode 100644 index 000000000..4aa3425b7 --- /dev/null +++ b/libtar/android_utils.c @@ -0,0 +1,123 @@ +/* +** Copyright 2008, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libtar.h" +#include "android_utils.h" + +/* This code may come in handy later if we ever need to extend to storing more user.inode_* xattrs +#define USER_INODE_SEPARATOR "\0" +#define ANDROID_USER_INODE_XATTR_PREFIX "user.inode_" +#define ANDROID_USER_INODE_XATTR_PREFIX_LEN strlen(ANDROID_USER_INODE_XATTR_PREFIX) + +char* scan_xattrs_for_user_inode (const char *realname, size_t *return_size) +{ + ssize_t size; + char xattr_list[PATH_MAX]; + size = listxattr(realname, xattr_list, sizeof(xattr_list)); + if (size < 0) { + return NULL; + } + char xattr[T_BLOCKSIZE]; + char *xattr_ptr; + int first = 1; + *return_size = 0; + for (int i = 0; i < size; i++) { + if (xattr_list[i]) { + xattr_ptr = xattr_list + i; + if (strncmp(xattr_ptr, ANDROID_USER_INODE_XATTR_PREFIX, ANDROID_USER_INODE_XATTR_PREFIX_LEN) == 0) { + // found a user.inode xattr + if (first) { + first = 0; + strcpy(xattr, xattr_ptr); + *return_size = strlen(xattr_ptr); + } else { + char *ptr = xattr + *return_size; + snprintf(ptr, T_BLOCKSIZE - *return_size, "%s", xattr_ptr); + *return_size += strlen(xattr_ptr) + 1; // + 1 for null separator + if (*return_size >= T_BLOCKSIZE) { + *return_size = 0; + return NULL; + } + } + } + i += strlen(xattr_ptr); + } + } + if (first) + return NULL; + return strdup(xattr); +}*/ + +/* + * get_path_inode and write_path_inode were taken from frameworks/native/cmds/installd/utils.cpp + */ + +static int get_path_inode(const char* path, ino_t *inode) { + struct stat buf; + memset(&buf, 0, sizeof(buf)); + if (stat(path, &buf) != 0) { + printf("failed to stat %s\n", path); + return -1; + } + *inode = buf.st_ino; + return 0; +} + +/** + * Write the inode of a specific child file into the given xattr on the + * parent directory. This allows you to find the child later, even if its + * name is encrypted. + */ +int write_path_inode(const char* parent, const char* name, const char* inode_xattr) { + ino_t inode = 0; + uint64_t inode_raw = 0; + char path[PATH_MAX]; + snprintf(path, PATH_MAX, "%s/%s", parent, name); + + if (mkdirhier(path) == -1) { + printf("failed to mkdirhier for %s\n", path); + return -1; + } + + if (get_path_inode(path, &inode) != 0) { + return -1; + } + + // Check to see if already set correctly + if (getxattr(parent, inode_xattr, &inode_raw, sizeof(inode_raw)) == sizeof(inode_raw)) { + if (inode_raw == inode) { + // Already set correctly; skip writing + return 0; + } + } + + inode_raw = inode; + printf("setting %s on %s pointing to %s\n", inode_xattr, parent, path); + if (setxattr(parent, inode_xattr, &inode_raw, sizeof(inode_raw), 0) != 0 && errno != EOPNOTSUPP) { + printf("Failed to write xattr %s at %s (%s)\n", inode_xattr, parent, strerror(errno)); + return -1; + } + return 0; +} diff --git a/libtar/android_utils.h b/libtar/android_utils.h new file mode 100644 index 000000000..72cb9285b --- /dev/null +++ b/libtar/android_utils.h @@ -0,0 +1,22 @@ +/* +** Copyright 2008, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef LIBTAR_ANDROID_UTILS_H +#define LIBTAR_ANDROID_UTILS_H + +int write_path_inode(const char* parent, const char* name, const char* inode_xattr); + +#endif diff --git a/libtar/append.c b/libtar/append.c index d8ba3cad5..8896764f2 100644 --- a/libtar/append.c +++ b/libtar/append.c @@ -40,6 +40,7 @@ #ifdef HAVE_EXT4_CRYPT # include "ext4crypt_tar.h" #endif +#include "android_utils.h" struct tar_dev { @@ -172,6 +173,32 @@ tar_append_file(TAR *t, const char *realname, const char *savename) } } + /* get android user.default xattr */ + if (TH_ISDIR(t) && t->options & TAR_STORE_ANDROID_USER_XATTR) + { + if (getxattr(realname, "user.default", NULL, 0) >= 0) + { + t->th_buf.has_user_default = 1; +#if 1 //def DEBUG + printf("storing xattr user.default\n"); +#endif + } + if (getxattr(realname, "user.inode_cache", NULL, 0) >= 0) + { + t->th_buf.has_user_cache = 1; +#if 1 //def DEBUG + printf("storing xattr user.inode_cache\n"); +#endif + } + if (getxattr(realname, "user.inode_code_cache", NULL, 0) >= 0) + { + t->th_buf.has_user_code_cache = 1; +#if 1 //def DEBUG + printf("storing xattr user.inode_code_cache\n"); +#endif + } + } + /* check if it's a hardlink */ #ifdef DEBUG puts("tar_append_file(): checking inode cache for hardlink..."); diff --git a/libtar/block.c b/libtar/block.c index 1b3ba8242..a94ba445d 100644 --- a/libtar/block.c +++ b/libtar/block.c @@ -23,15 +23,27 @@ // Used to identify selinux_context in extended ('x') // metadata. From RedHat implementation. #define SELINUX_TAG "RHT.security.selinux=" -#define SELINUX_TAG_LEN 21 +#define SELINUX_TAG_LEN strlen(SELINUX_TAG) // Used to identify e4crypt_policy in extended ('x') #define E4CRYPT_TAG "TWRP.security.e4crypt=" -#define E4CRYPT_TAG_LEN 22 +#define E4CRYPT_TAG_LEN strlen(E4CRYPT_TAG) // Used to identify Posix capabilities in extended ('x') #define CAPABILITIES_TAG "SCHILY.xattr.security.capability=" -#define CAPABILITIES_TAG_LEN 33 +#define CAPABILITIES_TAG_LEN strlen(CAPABILITIES_TAG) + +// Used to identify Android user.default xattr in extended ('x') +#define ANDROID_USER_DEFAULT_TAG "ANDROID.user.default" +#define ANDROID_USER_DEFAULT_TAG_LEN strlen(ANDROID_USER_DEFAULT_TAG) + +// Used to identify Android user.inode_cache xattr in extended ('x') +#define ANDROID_USER_CACHE_TAG "ANDROID.user.inode_cache" +#define ANDROID_USER_CACHE_TAG_LEN strlen(ANDROID_USER_CACHE_TAG) + +// Used to identify Android user.inode_code_cache xattr in extended ('x') +#define ANDROID_USER_CODE_CACHE_TAG "ANDROID.user.inode_code_cache" +#define ANDROID_USER_CODE_CACHE_TAG_LEN strlen(ANDROID_USER_CODE_CACHE_TAG) /* read a header block */ /* FIXME: the return value of this function should match the return value @@ -135,6 +147,9 @@ th_read(TAR *t) memset(&t->th_buf.cap_data, 0, sizeof(struct vfs_cap_data)); t->th_buf.has_cap_data = 0; } + t->th_buf.has_user_default = 0; + t->th_buf.has_user_cache = 0; + t->th_buf.has_user_code_cache = 0; memset(&(t->th_buf), 0, sizeof(struct tar_header)); @@ -276,7 +291,7 @@ th_read(TAR *t) int len = strlen(buf); // posix capabilities char *start = strstr(buf, CAPABILITIES_TAG); - if(start && start+CAPABILITIES_TAG_LEN < buf+len) + if (start && start+CAPABILITIES_TAG_LEN < buf+len) { start += CAPABILITIES_TAG_LEN; memcpy(&t->th_buf.cap_data, start, sizeof(struct vfs_cap_data)); @@ -287,7 +302,7 @@ th_read(TAR *t) } // end posix capabilities // selinux contexts start = strstr(buf, SELINUX_TAG); - if(start && start+SELINUX_TAG_LEN < buf+len) + if (start && start+SELINUX_TAG_LEN < buf+len) { start += SELINUX_TAG_LEN; char *end = strchr(start, '\n'); @@ -299,9 +314,36 @@ th_read(TAR *t) #endif } } // end selinux contexts + // android user.default xattr + start = strstr(buf, ANDROID_USER_DEFAULT_TAG); + if (start) + { + t->th_buf.has_user_default = 1; +#ifdef DEBUG + printf(" th_read(): android user.default xattr detected\n"); +#endif + } // end android user.default xattr + // android user.inode_cache xattr + start = strstr(buf, ANDROID_USER_CACHE_TAG); + if (start) + { + t->th_buf.has_user_cache = 1; +#ifdef DEBUG + printf(" th_read(): android user.inode_cache xattr detected\n"); +#endif + } // end android user.inode_cache xattr + // android user.inode_code_cache xattr + start = strstr(buf, ANDROID_USER_CODE_CACHE_TAG); + if (start) + { + t->th_buf.has_user_code_cache = 1; +#ifdef DEBUG + printf(" th_read(): android user.inode_code_cache xattr detected\n"); +#endif + } // end android user.inode_code_cache xattr #ifdef HAVE_EXT4_CRYPT start = strstr(buf, E4CRYPT_TAG); - if(start && start+E4CRYPT_TAG_LEN < buf+len) + if (start && start+E4CRYPT_TAG_LEN < buf+len) { start += E4CRYPT_TAG_LEN; char *end = strchr(start, '\n'); @@ -569,6 +611,72 @@ th_write(TAR *t) *nlptr = '\n'; ptr += sz; } + if (t->options & TAR_STORE_ANDROID_USER_XATTR) + { + if (t->th_buf.has_user_default) { +#ifdef DEBUG + printf("th_write(): has android user.default xattr\n"); +#endif + sz = ANDROID_USER_DEFAULT_TAG_LEN + 3 + 1; + + if (total_sz + sz >= T_BLOCKSIZE) + { + if (th_write_extended(t, &buf, total_sz)) + return -1; + ptr = buf; + total_sz = sz; + } + else + total_sz += sz; + + snprintf(ptr, T_BLOCKSIZE, "%d "ANDROID_USER_DEFAULT_TAG, (int)sz); + char *nlptr = ptr + sz - 1; + *nlptr = '\n'; + ptr += sz; + } + if (t->th_buf.has_user_cache) { +#ifdef DEBUG + printf("th_write(): has android user.inode_cache xattr\n"); +#endif + sz = ANDROID_USER_CACHE_TAG_LEN + 3 + 1; + + if (total_sz + sz >= T_BLOCKSIZE) + { + if (th_write_extended(t, &buf, total_sz)) + return -1; + ptr = buf; + total_sz = sz; + } + else + total_sz += sz; + + snprintf(ptr, T_BLOCKSIZE, "%d "ANDROID_USER_CACHE_TAG, (int)sz); + char *nlptr = ptr + sz - 1; + *nlptr = '\n'; + ptr += sz; + } + if (t->th_buf.has_user_code_cache) { +#ifdef DEBUG + printf("th_write(): has android user.inode_code_cache xattr\n"); +#endif + sz = ANDROID_USER_CODE_CACHE_TAG_LEN + 3 + 1; + + if (total_sz + sz >= T_BLOCKSIZE) + { + if (th_write_extended(t, &buf, total_sz)) + return -1; + ptr = buf; + total_sz = sz; + } + else + total_sz += sz; + + snprintf(ptr, T_BLOCKSIZE, "%d "ANDROID_USER_CODE_CACHE_TAG, (int)sz); + char *nlptr = ptr + sz - 1; + *nlptr = '\n'; + ptr += sz; + } + } if (total_sz > 0 && th_write_extended(t, &buf, total_sz)) // write any outstanding tar extended header return -1; diff --git a/libtar/extract.c b/libtar/extract.c index 87ccf245f..82ed766b2 100644 --- a/libtar/extract.c +++ b/libtar/extract.c @@ -37,6 +37,7 @@ #ifdef HAVE_EXT4_CRYPT # include "ext4crypt_tar.h" #endif +#include "android_utils.h" const unsigned long long progress_size = (unsigned long long)(T_BLOCKSIZE); @@ -469,7 +470,6 @@ tar_extract_blockdev(TAR *t, const char *realname) return 0; } - /* directory */ int tar_extract_dir(TAR *t, const char *realname) @@ -521,6 +521,33 @@ tar_extract_dir(TAR *t, const char *realname) } } + if (t->options & TAR_STORE_ANDROID_USER_XATTR) + { + if (t->th_buf.has_user_default) { +#if 1 //def DEBUG + printf("tar_extract_file(): restoring android user.default xattr to %s\n", realname); +#endif + if (setxattr(realname, "user.default", NULL, 0, 0) < 0) { + fprintf(stderr, "tar_extract_file(): failed to restore android user.default to file %s !!!\n", realname); + return -1; + } + } + if (t->th_buf.has_user_cache) { +#if 1 //def DEBUG + printf("tar_extract_file(): restoring android user.inode_cache xattr to %s\n", realname); +#endif + if (write_path_inode(realname, "cache", "user.inode_cache")) + return -1; + } + if (t->th_buf.has_user_code_cache) { +#if 1 //def DEBUG + printf("tar_extract_file(): restoring android user.inode_code_cache xattr to %s\n", realname); +#endif + if (write_path_inode(realname, "code_cache", "user.inode_code_cache")) + return -1; + } + } + #ifdef HAVE_EXT4_CRYPT if(t->th_buf.e4crypt_policy != NULL) { diff --git a/libtar/libtar.h b/libtar/libtar.h index 4d921247b..2d0a3d3fc 100644 --- a/libtar/libtar.h +++ b/libtar/libtar.h @@ -74,6 +74,9 @@ struct tar_header #endif int has_cap_data; struct vfs_cap_data cap_data; + int has_user_default; + int has_user_cache; + int has_user_code_cache; }; @@ -120,6 +123,7 @@ TAR; #define TAR_USE_NUMERIC_ID 256 /* favor numeric owner over names */ #define TAR_STORE_EXT4_POL 512 /* store ext4 crypto policy */ #define TAR_STORE_POSIX_CAP 1024 /* store posix file capabilities */ +#define TAR_STORE_ANDROID_USER_XATTR 2048 /* store android user.* xattr */ /* this is obsolete - it's here for backwards-compatibility only */ #define TAR_IGNORE_MAGIC 0 -- cgit v1.2.3