Files
android_bootable_recovery/libtar/extract.c
Ethan Yonker c798c9cd24 Merge up to AOSP marshmallow-release
In order to maintain compatibility with older trees, we now have
minadbd.old and minui.old. I had to use a TARGET_GLOBAL_CFLAG to
handle ifdef issues in minui/minui.d because healthd includes
minui/minui.h and there was no other alternative to make minui.h
compatible with older trees without having to modify healthd rules
which is outside of TWRP.

Note that the new minui does not currently have support for qcom
overlay graphics. Support for this graphics mode will likely be
added in a later patch set. If you are building in a 6.0 tree and
have a device that needs qcom overlay graphics, be warned, as off
mode charging may not work properly. A dead battery in this case
could potentially brick your device if it is unable to charge as
healthd handles charging duties.

Update rules for building toolbox and add rules for making toybox

Use permissive.sh in init.rc which will follow symlinks so we do
not have to worry about what binary is supplying the setenforce
functionality (toolbox, toybox, or busybox).

Fix a few warnings in the main recovery binary source code.

Fix a few includes that were missing that prevented compiling in
6.0

Change-Id: Ia67aa2107d260883da5e365475a19bea538e8b97
2015-10-09 11:15:29 -05:00

571 lines
10 KiB
C

/*
** Copyright 1998-2003 University of Illinois Board of Trustees
** Copyright 1998-2003 Mark D. Roth
** All rights reserved.
**
** extract.c - libtar code to extract a file from a tar archive
**
** Mark D. Roth <roth@uiuc.edu>
** Campus Information Technologies and Educational Services
** University of Illinois at Urbana-Champaign
*/
#include <internal.h>
#include <stdio.h>
#include <sys/param.h>
#include <sys/types.h>
#include <fcntl.h>
#include <errno.h>
#include <utime.h>
#include <string.h>
#define DEBUG
#ifdef STDC_HEADERS
# include <stdlib.h>
#endif
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#define DEBUG
static int
tar_set_file_perms(TAR *t, char *realname)
{
mode_t mode;
uid_t uid;
gid_t gid;
struct utimbuf ut;
char *filename;
filename = (realname ? realname : th_get_pathname(t));
mode = th_get_mode(t);
uid = th_get_uid(t);
gid = th_get_gid(t);
ut.modtime = ut.actime = th_get_mtime(t);
#ifdef DEBUG
printf(" ==> setting perms: %s (mode %04o, uid %d, gid %d)\n",
filename, mode, uid, gid);
#endif
/* change owner/group */
if (geteuid() == 0)
#ifdef HAVE_LCHOWN
if (lchown(filename, uid, gid) == -1)
{
# ifdef DEBUG
fprintf(stderr, "lchown(\"%s\", %d, %d): %s\n",
filename, uid, gid, strerror(errno));
# endif
#else /* ! HAVE_LCHOWN */
if (!TH_ISSYM(t) && chown(filename, uid, gid) == -1)
{
# ifdef DEBUG
fprintf(stderr, "chown(\"%s\", %d, %d): %s\n",
filename, uid, gid, strerror(errno));
# endif
#endif /* HAVE_LCHOWN */
return -1;
}
/* change access/modification time */
if (!TH_ISSYM(t) && utime(filename, &ut) == -1)
{
#ifdef DEBUG
perror("utime()");
#endif
return -1;
}
/* change permissions */
if (!TH_ISSYM(t) && chmod(filename, mode) == -1)
{
#ifdef DEBUG
perror("chmod()");
#endif
return -1;
}
return 0;
}
/* switchboard */
int
tar_extract_file(TAR *t, char *realname, char *prefix, const int *progress_fd)
{
int i;
char *lnp;
int pathname_len;
int realname_len;
if (t->options & TAR_NOOVERWRITE)
{
struct stat s;
if (lstat(realname, &s) == 0 || errno != ENOENT)
{
errno = EEXIST;
return -1;
}
}
if (TH_ISDIR(t))
{
printf("dir\n");
i = tar_extract_dir(t, realname);
if (i == 1)
i = 0;
}
else if (TH_ISLNK(t)) {
printf("link\n");
i = tar_extract_hardlink(t, realname, prefix);
}
else if (TH_ISSYM(t)) {
printf("sym\n");
i = tar_extract_symlink(t, realname);
}
else if (TH_ISCHR(t)) {
printf("chr\n");
i = tar_extract_chardev(t, realname);
}
else if (TH_ISBLK(t)) {
printf("blk\n");
i = tar_extract_blockdev(t, realname);
}
else if (TH_ISFIFO(t)) {
printf("fifo\n");
i = tar_extract_fifo(t, realname);
}
else /* if (TH_ISREG(t)) */ {
printf("reg\n");
i = tar_extract_regfile(t, realname, progress_fd);
}
if (i != 0) {
printf("FAILED RESTORE OF FILE i: %s\n", realname);
return i;
}
i = tar_set_file_perms(t, realname);
if (i != 0) {
printf("FAILED SETTING PERMS: %d\n", i);
return i;
}
#ifdef HAVE_SELINUX
if((t->options & TAR_STORE_SELINUX) && t->th_buf.selinux_context != NULL)
{
#ifdef DEBUG
printf(" Restoring SELinux context %s to file %s\n", t->th_buf.selinux_context, realname);
#endif
if (lsetfilecon(realname, t->th_buf.selinux_context) < 0) {
fprintf(stderr, "Failed to restore SELinux context %s!\n", strerror(errno));
}
}
#endif
/*
pathname_len = strlen(th_get_pathname(t)) + 1;
realname_len = strlen(realname) + 1;
lnp = (char *)calloc(1, pathname_len + realname_len);
if (lnp == NULL)
return -1;
strcpy(&lnp[0], th_get_pathname(t));
strcpy(&lnp[pathname_len], realname);
#ifdef DEBUG
printf("tar_extract_file(): calling libtar_hash_add(): key=\"%s\", "
"value=\"%s\"\n", th_get_pathname(t), realname);
#endif
if (libtar_hash_add(t->h, lnp) != 0)
return -1;
free(lnp);
*/
return 0;
}
/* extract regular file */
int
tar_extract_regfile(TAR *t, char *realname, const int *progress_fd)
{
//mode_t mode;
size_t size, i;
//uid_t uid;
//gid_t gid;
int fdout;
int k;
char buf[T_BLOCKSIZE];
char *filename;
fflush(NULL);
#ifdef DEBUG
printf("==> tar_extract_regfile(t=0x%lx, realname=\"%s\")\n", t,
realname);
#endif
if (!TH_ISREG(t))
{
errno = EINVAL;
return -1;
}
filename = (realname ? realname : th_get_pathname(t));
//mode = th_get_mode(t);
size = th_get_size(t);
//uid = th_get_uid(t);
//gid = th_get_gid(t);
if (mkdirhier(dirname(filename)) == -1)
return -1;
#ifdef DEBUG
//printf(" ==> extracting: %s (mode %04o, uid %d, gid %d, %d bytes)\n",
// filename, mode, uid, gid, size);
printf(" ==> extracting: %s (file size %d bytes)\n",
filename, size);
#endif
fdout = open(filename, O_WRONLY | O_CREAT | O_TRUNC
#ifdef O_BINARY
| O_BINARY
#endif
, 0666);
if (fdout == -1)
{
#ifdef DEBUG
perror("open()");
#endif
return -1;
}
#if 0
/* change the owner. (will only work if run as root) */
if (fchown(fdout, uid, gid) == -1 && errno != EPERM)
{
#ifdef DEBUG
perror("fchown()");
#endif
return -1;
}
/* make sure the mode isn't inheritted from a file we're overwriting */
if (fchmod(fdout, mode & 07777) == -1)
{
#ifdef DEBUG
perror("fchmod()");
#endif
return -1;
}
#endif
/* extract the file */
for (i = size; i > 0; i -= tar_min(i, T_BLOCKSIZE))
{
k = tar_block_read(t, buf);
if (k != T_BLOCKSIZE)
{
if (k != -1)
errno = EINVAL;
return -1;
}
/* write block to output file */
if (write(fdout, buf,
((i > T_BLOCKSIZE) ? T_BLOCKSIZE : i)) == -1)
return -1;
}
/* close output file */
if (close(fdout) == -1)
return -1;
#ifdef DEBUG
printf("### done extracting %s\n", filename);
#endif
if (*progress_fd != 0) {
unsigned long long file_size = (unsigned long long)(size);
write(*progress_fd, &file_size, sizeof(file_size));
}
return 0;
}
/* skip regfile */
int
tar_skip_regfile(TAR *t)
{
int k;
size_t size, i;
char buf[T_BLOCKSIZE];
if (!TH_ISREG(t))
{
errno = EINVAL;
return -1;
}
size = th_get_size(t);
for (i = size; i > 0; i -= tar_min(i, T_BLOCKSIZE))
{
k = tar_block_read(t, buf);
if (k != T_BLOCKSIZE)
{
if (k != -1)
errno = EINVAL;
return -1;
}
}
return 0;
}
/* hardlink */
int
tar_extract_hardlink(TAR * t, char *realname, char *prefix)
{
char *filename;
char *linktgt = NULL;
char *lnp;
libtar_hashptr_t hp;
if (!TH_ISLNK(t))
{
errno = EINVAL;
return -1;
}
filename = (realname ? realname : th_get_pathname(t));
if (mkdirhier(dirname(filename)) == -1)
return -1;
libtar_hashptr_reset(&hp);
if (libtar_hash_getkey(t->h, &hp, th_get_linkname(t),
(libtar_matchfunc_t)libtar_str_match) != 0)
{
lnp = (char *)libtar_hashptr_data(&hp);
linktgt = &lnp[strlen(lnp) + 1];
}
else
linktgt = th_get_linkname(t);
char *newtgt = strdup(linktgt);
sprintf(linktgt, "%s/%s", prefix, newtgt);
#ifdef DEBUG
printf(" ==> extracting: %s (link to %s)\n", filename, linktgt);
#endif
if (link(linktgt, filename) == -1)
{
#ifdef DEBUG
perror("link()");
#endif
printf("Failed restore of hardlink '%s' but returning as if nothing bad happened anyway\n", filename);
return 0; // Used to be -1
}
return 0;
}
/* symlink */
int
tar_extract_symlink(TAR *t, char *realname)
{
char *filename;
if (!TH_ISSYM(t))
{
printf("not a sym\n");
errno = EINVAL;
return -1;
}
filename = (realname ? realname : th_get_pathname(t));
printf("file: %s\n", filename);
if (mkdirhier(dirname(filename)) == -1) {
printf("mkdirhier\n");
return -1;
}
if (unlink(filename) == -1 && errno != ENOENT) {
printf("unlink\n");
return -1;
}
#ifdef DEBUG
printf(" ==> extracting: %s (symlink to %s)\n",
filename, th_get_linkname(t));
#endif
if (symlink(th_get_linkname(t), filename) == -1)
{
#ifdef DEBUG
perror("symlink()");
#endif
return -1;
}
return 0;
}
/* character device */
int
tar_extract_chardev(TAR *t, char *realname)
{
mode_t mode;
unsigned long devmaj, devmin;
char *filename;
if (!TH_ISCHR(t))
{
errno = EINVAL;
return -1;
}
filename = (realname ? realname : th_get_pathname(t));
mode = th_get_mode(t);
devmaj = th_get_devmajor(t);
devmin = th_get_devminor(t);
if (mkdirhier(dirname(filename)) == -1)
return -1;
#ifdef DEBUG
printf(" ==> extracting: %s (character device %ld,%ld)\n",
filename, devmaj, devmin);
#endif
if (mknod(filename, mode | S_IFCHR,
compat_makedev(devmaj, devmin)) == -1)
{
#ifdef DEBUG
printf("mknod() failed, returning good anyway");
#endif
return 0;
}
return 0;
}
/* block device */
int
tar_extract_blockdev(TAR *t, char *realname)
{
mode_t mode;
unsigned long devmaj, devmin;
char *filename;
if (!TH_ISBLK(t))
{
errno = EINVAL;
return -1;
}
filename = (realname ? realname : th_get_pathname(t));
mode = th_get_mode(t);
devmaj = th_get_devmajor(t);
devmin = th_get_devminor(t);
if (mkdirhier(dirname(filename)) == -1)
return -1;
#ifdef DEBUG
printf(" ==> extracting: %s (block device %ld,%ld)\n",
filename, devmaj, devmin);
#endif
if (mknod(filename, mode | S_IFBLK,
compat_makedev(devmaj, devmin)) == -1)
{
#ifdef DEBUG
printf("mknod() failed but returning anyway");
#endif
return 0;
}
return 0;
}
/* directory */
int
tar_extract_dir(TAR *t, char *realname)
{
mode_t mode;
char *filename;
if (!TH_ISDIR(t))
{
errno = EINVAL;
return -1;
}
filename = (realname ? realname : th_get_pathname(t));
mode = th_get_mode(t);
if (mkdirhier(dirname(filename)) == -1) {
printf("tar_extract_dir mkdirhier failed\n");
return -1;
}
#ifdef DEBUG
printf(" ==> extracting: %s (mode %04o, directory)\n", filename,
mode);
#endif
if (mkdir(filename, mode) == -1)
{
if (errno == EEXIST)
{
#ifdef DEBUG
printf(" *** using existing directory");
#endif
}
else
{
#ifdef DEBUG
perror("mkdir()");
#endif
return -1;
}
}
return 0;
}
/* FIFO */
int
tar_extract_fifo(TAR *t, char *realname)
{
mode_t mode;
char *filename;
if (!TH_ISFIFO(t))
{
errno = EINVAL;
return -1;
}
filename = (realname ? realname : th_get_pathname(t));
mode = th_get_mode(t);
if (mkdirhier(dirname(filename)) == -1)
return -1;
#ifdef DEBUG
printf(" ==> extracting: %s (fifo)\n", filename);
#endif
if (mkfifo(filename, mode) == -1)
{
#ifdef DEBUG
perror("mkfifo()");
#endif
return -1;
}
return 0;
}