twinstall.cpp 12.7 KB
Newer Older
Dees Troy's avatar
Dees Troy committed
1
/*
2
	Copyright 2012 to 2017 bigbiff/Dees_Troy TeamWin
Dees Troy's avatar
Dees Troy committed
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
	This file is part of TWRP/TeamWin Recovery Project.

	TWRP is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	TWRP is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with TWRP.  If not, see <http://www.gnu.org/licenses/>.
*/
18

19 20 21 22 23

#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif

24 25 26 27
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
28
#include <sys/mman.h>
29 30 31 32 33 34 35
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>

#include <string.h>
#include <stdio.h>

36
#include "twcommon.h"
37 38
#include "mtdutils/mounts.h"
#include "mtdutils/mtdutils.h"
Ethan Yonker's avatar
Ethan Yonker committed
39 40

#ifdef USE_MINZIP
41
#include "minzip/SysUtil.h"
Ethan Yonker's avatar
Ethan Yonker committed
42 43 44 45 46
#else
#include "otautil/SysUtil.h"
#include <ziparchive/zip_archive.h>
#endif
#include "zipwrap.hpp"
47
#ifdef USE_OLD_VERIFIER
48
#include "verifier24/verifier.h"
49
#else
50
#include "verifier.h"
51
#endif
52 53 54
#include "variables.h"
#include "data.hpp"
#include "partitions.hpp"
bigbiff bigbiff's avatar
bigbiff bigbiff committed
55 56 57
#include "twrpDigestDriver.hpp"
#include "twrpDigest/twrpDigest.hpp"
#include "twrpDigest/twrpMD5.hpp"
58
#include "twrp-functions.hpp"
Ethan Yonker's avatar
Ethan Yonker committed
59
#include "gui/gui.hpp"
60
#include "gui/pages.hpp"
61
#include "legacy_property_service.h"
Ethan Yonker's avatar
Ethan Yonker committed
62 63
#include "twinstall.h"
#include "installcommand.h"
64 65
extern "C" {
	#include "gui/gui.h"
66 67
}

Ethan Yonker's avatar
Ethan Yonker committed
68 69
#define AB_OTA "payload_properties.txt"

70 71
static const char* properties_path = "/dev/__properties__";
static const char* properties_path_renamed = "/dev/__properties_kk__";
72 73
static bool legacy_props_env_initd = false;
static bool legacy_props_path_modified = false;
74

Ethan Yonker's avatar
Ethan Yonker committed
75 76 77 78 79 80 81
enum zip_type {
	UNKNOWN_ZIP_TYPE = 0,
	UPDATE_BINARY_ZIP_TYPE,
	AB_OTA_ZIP_TYPE,
	TWRP_THEME_ZIP_TYPE
};

82
// to support pre-KitKat update-binaries that expect properties in the legacy format
83
static int switch_to_legacy_properties()
84
{
85 86 87 88 89 90 91 92 93 94 95
	if (!legacy_props_env_initd) {
		if (legacy_properties_init() != 0)
			return -1;

		char tmp[32];
		int propfd, propsz;
		legacy_get_property_workspace(&propfd, &propsz);
		sprintf(tmp, "%d,%d", dup(propfd), propsz);
		setenv("ANDROID_PROPERTY_WORKSPACE", tmp, 1);
		legacy_props_env_initd = true;
	}
96 97 98

	if (TWFunc::Path_Exists(properties_path)) {
		// hide real properties so that the updater uses the envvar to find the legacy format properties
99 100 101 102 103 104
		if (rename(properties_path, properties_path_renamed) != 0) {
			LOGERR("Renaming %s failed: %s\n", properties_path, strerror(errno));
			return -1;
		} else {
			legacy_props_path_modified = true;
		}
105
	}
106 107

	return 0;
108 109
}

110
static int switch_to_new_properties()
111 112
{
	if (TWFunc::Path_Exists(properties_path_renamed)) {
113 114 115 116 117 118
		if (rename(properties_path_renamed, properties_path) != 0) {
			LOGERR("Renaming %s failed: %s\n", properties_path_renamed, strerror(errno));
			return -1;
		} else {
			legacy_props_path_modified = false;
		}
119
	}
120 121

	return 0;
122
}
123

Ethan Yonker's avatar
Ethan Yonker committed
124
static int Install_Theme(const char* path, ZipWrap *Zip) {
125
#ifdef TW_OEM_BUILD // We don't do custom themes in OEM builds
Ethan Yonker's avatar
Ethan Yonker committed
126
	Zip->Close();
127 128
	return INSTALL_CORRUPT;
#else
Ethan Yonker's avatar
Ethan Yonker committed
129
	if (!Zip->EntryExists("ui.xml")) {
130 131
		return INSTALL_CORRUPT;
	}
Ethan Yonker's avatar
Ethan Yonker committed
132
	Zip->Close();
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151
	if (!PartitionManager.Mount_Settings_Storage(true))
		return INSTALL_ERROR;
	string theme_path = DataManager::GetSettingsStoragePath();
	theme_path += "/TWRP/theme";
	if (!TWFunc::Path_Exists(theme_path)) {
		if (!TWFunc::Recursive_Mkdir(theme_path)) {
			return INSTALL_ERROR;
		}
	}
	theme_path += "/ui.zip";
	if (TWFunc::copy_file(path, theme_path, 0644) != 0) {
		return INSTALL_ERROR;
	}
	LOGINFO("Installing custom theme '%s' to '%s'\n", path, theme_path.c_str());
	PageManager::RequestReload();
	return INSTALL_SUCCESS;
#endif
}

Ethan Yonker's avatar
Ethan Yonker committed
152 153 154
static int Prepare_Update_Binary(const char *path, ZipWrap *Zip, int* wipe_cache) {
	if (!Zip->ExtractEntry(ASSUMED_UPDATE_BINARY_NAME, TMP_UPDATER_BINARY_PATH, 0755)) {
		Zip->Close();
155 156 157
		LOGERR("Could not extract '%s'\n", ASSUMED_UPDATE_BINARY_NAME);
		return INSTALL_ERROR;
	}
158

159
	// If exists, extract file_contexts from the zip file
Ethan Yonker's avatar
Ethan Yonker committed
160 161
	if (!Zip->EntryExists("file_contexts")) {
		Zip->Close();
162 163
		LOGINFO("Zip does not contain SELinux file_contexts file in its root.\n");
	} else {
Ethan Yonker's avatar
Ethan Yonker committed
164
		const string output_filename = "/file_contexts";
165
		LOGINFO("Zip contains SELinux file_contexts file in its root. Extracting to %s\n", output_filename.c_str());
Ethan Yonker's avatar
Ethan Yonker committed
166 167
		if (!Zip->ExtractEntry("file_contexts", output_filename, 0644)) {
			Zip->Close();
168
			LOGERR("Could not extract '%s'\n", output_filename.c_str());
169 170 171
			return INSTALL_ERROR;
		}
	}
Ethan Yonker's avatar
Ethan Yonker committed
172
	Zip->Close();
Ethan Yonker's avatar
Ethan Yonker committed
173 174 175
	return INSTALL_SUCCESS;
}

176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208
static bool update_binary_has_legacy_properties(const char *binary) {
	const char str_to_match[] = "ANDROID_PROPERTY_WORKSPACE";
	int len_to_match = sizeof(str_to_match) - 1;
	bool found = false;

	int fd = open(binary, O_RDONLY);
	if (fd < 0) {
		LOGINFO("has_legacy_properties: Could not open %s: %s!\n", binary, strerror(errno));
		return false;
	}

	struct stat finfo;
	if (fstat(fd, &finfo) < 0) {
		LOGINFO("has_legacy_properties: Could not fstat %d: %s!\n", fd, strerror(errno));
		close(fd);
		return false;
	}

	void *data = mmap(NULL, finfo.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
	if (data == MAP_FAILED) {
		LOGINFO("has_legacy_properties: mmap (size=%lld) failed: %s!\n", finfo.st_size, strerror(errno));
	} else {
		if (memmem(data, finfo.st_size, str_to_match, len_to_match)) {
			LOGINFO("has_legacy_properties: Found legacy property match!\n");
			found = true;
		}
		munmap(data, finfo.st_size);
	}
	close(fd);

	return found;
}

Ethan Yonker's avatar
Ethan Yonker committed
209
static int Run_Update_Binary(const char *path, ZipWrap *Zip, int* wipe_cache, zip_type ztype) {
Ethan Yonker's avatar
Ethan Yonker committed
210 211 212
	int ret_val, pipe_fd[2], status, zip_verify;
	char buffer[1024];
	FILE* child_data;
213

214
#ifndef TW_NO_LEGACY_PROPS
215 216 217 218 219 220
	if (!update_binary_has_legacy_properties(TMP_UPDATER_BINARY_PATH)) {
		LOGINFO("Legacy property environment not used in updater.\n");
	} else if (switch_to_legacy_properties() != 0) { /* Set legacy properties */
		LOGERR("Legacy property environment did not initialize successfully. Properties may not be detected.\n");
	} else {
		LOGINFO("Legacy property environment initialized.\n");
221
	}
222
#endif
223

224 225
	pipe(pipe_fd);

Ethan Yonker's avatar
Ethan Yonker committed
226 227
	std::vector<std::string> args;
    if (ztype == UPDATE_BINARY_ZIP_TYPE) {
Ethan Yonker's avatar
Ethan Yonker committed
228
		ret_val = update_binary_command(path, 0, pipe_fd[1], &args);
Ethan Yonker's avatar
Ethan Yonker committed
229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245
    } else if (ztype == AB_OTA_ZIP_TYPE) {
		ret_val = abupdate_binary_command(path, Zip, 0, pipe_fd[1], &args);
	} else {
		LOGERR("Unknown zip type %i\n", ztype);
		ret_val = INSTALL_CORRUPT;
	}
    if (ret_val) {
        close(pipe_fd[0]);
        close(pipe_fd[1]);
        return ret_val;
    }

	// Convert the vector to a NULL-terminated char* array suitable for execv.
	const char* chr_args[args.size() + 1];
	chr_args[args.size()] = NULL;
	for (size_t i = 0; i < args.size(); i++)
		chr_args[i] = args[i].c_str();
246 247 248 249

	pid_t pid = fork();
	if (pid == 0) {
		close(pipe_fd[0]);
Ethan Yonker's avatar
Ethan Yonker committed
250 251
		execve(chr_args[0], const_cast<char**>(chr_args), environ);
		printf("E:Can't execute '%s': %s\n", chr_args[0], strerror(errno));
252 253 254
		_exit(-1);
	}
	close(pipe_fd[1]);
255

256
	*wipe_cache = 0;
257

258 259 260 261
	DataManager::GetValue(TW_SIGNED_ZIP_VERIFY_VAR, zip_verify);
	child_data = fdopen(pipe_fd[0], "r");
	while (fgets(buffer, sizeof(buffer), child_data) != NULL) {
		char* command = strtok(buffer, " \n");
262 263 264 265 266
		if (command == NULL) {
			continue;
		} else if (strcmp(command, "progress") == 0) {
			char* fraction_char = strtok(NULL, " \n");
			char* seconds_char = strtok(NULL, " \n");
267

268 269
			float fraction_float = strtof(fraction_char, NULL);
			int seconds_float = strtol(seconds_char, NULL, 10);
270

271
			if (zip_verify)
Ethan Yonker's avatar
Ethan Yonker committed
272
				DataManager::ShowProgress(fraction_float * (1 - VERIFICATION_PROGRESS_FRAC), seconds_float);
273 274
			else
				DataManager::ShowProgress(fraction_float, seconds_float);
275 276 277 278 279 280 281 282 283 284 285 286 287 288 289
		} else if (strcmp(command, "set_progress") == 0) {
			char* fraction_char = strtok(NULL, " \n");
			float fraction_float = strtof(fraction_char, NULL);
			DataManager::SetProgress(fraction_float);
		} else if (strcmp(command, "ui_print") == 0) {
			char* display_value = strtok(NULL, "\n");
			if (display_value) {
				gui_print("%s", display_value);
			} else {
				gui_print("\n");
			}
		} else if (strcmp(command, "wipe_cache") == 0) {
			*wipe_cache = 1;
		} else if (strcmp(command, "clear_display") == 0) {
			// Do nothing, not supported by TWRP
290 291
		} else if (strcmp(command, "log") == 0) {
			printf("%s\n", strtok(NULL, "\n"));
292 293 294
		} else {
			LOGERR("unknown command [%s]\n", command);
		}
295 296
	}
	fclose(child_data);
297

298
	int waitrc = TWFunc::Wait_For_Child(pid, &status, "Updater");
299

300
#ifndef TW_NO_LEGACY_PROPS
301 302 303 304 305 306
	/* Unset legacy properties */
	if (legacy_props_path_modified) {
		if (switch_to_new_properties() != 0) {
			LOGERR("Legacy property environment did not disable successfully. Legacy properties may still be in use.\n");
		} else {
			LOGINFO("Legacy property environment disabled.\n");
307 308
		}
	}
309
#endif
310

311
	if (waitrc != 0)
312
		return INSTALL_ERROR;
313

314
	return INSTALL_SUCCESS;
315 316
}

Ethan Yonker's avatar
Ethan Yonker committed
317
int TWinstall_zip(const char* path, int* wipe_cache) {
318
	int ret_val, zip_verify = 1;
319

Ethan Yonker's avatar
Ethan Yonker committed
320 321
	if (strcmp(path, "error") == 0) {
		LOGERR("Failed to get adb sideload file: '%s'\n", path);
322
		return INSTALL_CORRUPT;
323
	}
324

Ethan Yonker's avatar
Ethan Yonker committed
325
	gui_msg(Msg("installing_zip=Installing zip file '{1}'")(path));
Ethan Yonker's avatar
Ethan Yonker committed
326
	if (strlen(path) < 9 || strncmp(path, "/sideload", 9) != 0) {
bigbiff bigbiff's avatar
bigbiff bigbiff committed
327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356
		string digest_str;
		string Full_Filename = path;
		string digest_file = path;
		digest_file += ".md5";

		gui_msg("check_for_digest=Checking for Digest file...");
		if (!TWFunc::Path_Exists(digest_file)) {
			gui_msg("no_digest=Skipping Digest check: no Digest file found");
		}
		else {
			if (TWFunc::read_file(digest_file, digest_str) != 0) {
				LOGERR("Skipping MD5 check: MD5 file unreadable\n");
			}
			else {
				twrpDigest *digest = new twrpMD5();
				if (!twrpDigestDriver::stream_file_to_digest(Full_Filename, digest)) {
					delete digest;
					return INSTALL_CORRUPT;
				}
				string digest_check = digest->return_digest_string();
				if (digest_str == digest_check) {
					gui_msg(Msg("digest_matched=Digest matched for '{1}'.")(path));
				}
				else {
					LOGERR("Aborting zip install: Digest verification failed\n");
					delete digest;
					return INSTALL_CORRUPT;
				}
				delete digest;
			}
Ethan Yonker's avatar
Ethan Yonker committed
357 358 359
		}
	}

360
#ifndef TW_OEM_BUILD
361
	DataManager::GetValue(TW_SIGNED_ZIP_VERIFY_VAR, zip_verify);
362
#endif
363
	DataManager::SetProgress(0);
Ethan Yonker's avatar
Ethan Yonker committed
364 365

	MemMapping map;
366
#ifdef USE_MINZIP
Ethan Yonker's avatar
Ethan Yonker committed
367
	if (sysMapFile(path, &map) != 0) {
368 369 370
#else
	if (!map.MapFile(path)) {
#endif
Ethan Yonker's avatar
Ethan Yonker committed
371
		gui_msg(Msg(msg::kError, "fail_sysmap=Failed to map file '{1}'")(path));
372 373
		return -1;
	}
Ethan Yonker's avatar
Ethan Yonker committed
374

375
	if (zip_verify) {
Ethan Yonker's avatar
Ethan Yonker committed
376
		gui_msg("verify_zip_sig=Verifying zip signature...");
Ethan Yonker's avatar
Ethan Yonker committed
377
#ifdef USE_OLD_VERIFIER
Ethan Yonker's avatar
Ethan Yonker committed
378
		ret_val = verify_file(map.addr, map.length);
Ethan Yonker's avatar
Ethan Yonker committed
379 380 381 382 383
#else
		std::vector<Certificate> loadedKeys;
		if (!load_keys("/res/keys", loadedKeys)) {
			LOGINFO("Failed to load keys");
			gui_err("verify_zip_fail=Zip signature verification failed!");
384 385 386
#ifdef USE_MINZIP
			sysReleaseMap(&map);
#endif
Ethan Yonker's avatar
Ethan Yonker committed
387 388 389 390
			return -1;
		}
		ret_val = verify_file(map.addr, map.length, loadedKeys, std::bind(&DataManager::SetProgress, std::placeholders::_1));
#endif
391
		if (ret_val != VERIFY_SUCCESS) {
Ethan Yonker's avatar
Ethan Yonker committed
392 393
			LOGINFO("Zip signature verification failed: %i\n", ret_val);
			gui_err("verify_zip_fail=Zip signature verification failed!");
394
#ifdef USE_MINZIP
Ethan Yonker's avatar
Ethan Yonker committed
395
			sysReleaseMap(&map);
396
#endif
397
			return -1;
Ethan Yonker's avatar
Ethan Yonker committed
398
		} else {
Ethan Yonker's avatar
Ethan Yonker committed
399
			gui_msg("verify_zip_done=Zip signature verified successfully.");
400 401
		}
	}
Ethan Yonker's avatar
Ethan Yonker committed
402 403
	ZipWrap Zip;
	if (!Zip.Open(path, &map)) {
Ethan Yonker's avatar
Ethan Yonker committed
404
		gui_err("zip_corrupt=Zip file is corrupt!");
405 406 407
#ifdef USE_MINZIP
			sysReleaseMap(&map);
#endif
408 409
		return INSTALL_CORRUPT;
	}
Ethan Yonker's avatar
Ethan Yonker committed
410

411 412
	time_t start, stop;
	time(&start);
Ethan Yonker's avatar
Ethan Yonker committed
413
	if (Zip.EntryExists(ASSUMED_UPDATE_BINARY_NAME)) {
Ethan Yonker's avatar
Ethan Yonker committed
414
		LOGINFO("Update binary zip\n");
Ethan Yonker's avatar
Ethan Yonker committed
415 416 417 418
		// Additionally verify the compatibility of the package.
		if (!verify_package_compatibility(&Zip)) {
			gui_err("zip_compatible_err=Zip Treble compatibility error!");
			Zip.Close();
419 420 421
#ifdef USE_MINZIP
			sysReleaseMap(&map);
#endif
Ethan Yonker's avatar
Ethan Yonker committed
422 423 424 425 426 427
			ret_val = INSTALL_CORRUPT;
		} else {
			ret_val = Prepare_Update_Binary(path, &Zip, wipe_cache);
			if (ret_val == INSTALL_SUCCESS)
				ret_val = Run_Update_Binary(path, &Zip, wipe_cache, UPDATE_BINARY_ZIP_TYPE);
		}
Ethan Yonker's avatar
Ethan Yonker committed
428
	} else {
Ethan Yonker's avatar
Ethan Yonker committed
429
		if (Zip.EntryExists(AB_OTA)) {
Ethan Yonker's avatar
Ethan Yonker committed
430 431 432
			LOGINFO("AB zip\n");
			ret_val = Run_Update_Binary(path, &Zip, wipe_cache, AB_OTA_ZIP_TYPE);
		} else {
Ethan Yonker's avatar
Ethan Yonker committed
433
			if (Zip.EntryExists("ui.xml")) {
Ethan Yonker's avatar
Ethan Yonker committed
434 435 436
				LOGINFO("TWRP theme zip\n");
				ret_val = Install_Theme(path, &Zip);
			} else {
Ethan Yonker's avatar
Ethan Yonker committed
437
				Zip.Close();
Ethan Yonker's avatar
Ethan Yonker committed
438 439 440 441
				ret_val = INSTALL_CORRUPT;
			}
		}
	}
442 443
	time(&stop);
	int total_time = (int) difftime(stop, start);
444
	if (ret_val == INSTALL_CORRUPT) {
Ethan Yonker's avatar
Ethan Yonker committed
445
		gui_err("invalid_zip_format=Invalid zip file format!");
446 447
	} else {
		LOGINFO("Install took %i second(s).\n", total_time);
448
	}
449
#ifdef USE_MINZIP
Ethan Yonker's avatar
Ethan Yonker committed
450
	sysReleaseMap(&map);
451
#endif
Ethan Yonker's avatar
Ethan Yonker committed
452
	return ret_val;
453
}