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
diff --git a/libtar/Android.mk b/libtar/Android.mk
index c8905d9..90a5006 100644
--- a/libtar/Android.mk
+++ b/libtar/Android.mk
@@ -5,7 +5,7 @@
 
 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 @@
 
 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 0000000..4aa3425
--- /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 <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/xattr.h>
+#include <string.h>
+#include <linux/limits.h>
+#include <errno.h>
+
+#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 0000000..72cb928
--- /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 d8ba3ca..8896764 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 @@
 		}
 	}
 
+	/* 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 1b3ba82..a94ba44 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 @@
 		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 @@
 			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 @@
 			} // 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 @@
 #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 @@
 		*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 87ccf24..82ed766 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 @@
 	return 0;
 }
 
-
 /* directory */
 int
 tar_extract_dir(TAR *t, const char *realname)
@@ -521,6 +521,33 @@
 		}
 	}
 
+	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 4d92124..2d0a3d3 100644
--- a/libtar/libtar.h
+++ b/libtar/libtar.h
@@ -74,6 +74,9 @@
 #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 @@
 #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