partition.cpp 85.6 KB
Newer Older
Dees Troy's avatar
Dees Troy committed
1
/*
bigbiff bigbiff's avatar
bigbiff bigbiff committed
2
	Copyright 2013 to 2017 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/>.
*/
Dees_Troy's avatar
Dees_Troy committed
18 19 20 21 22 23

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/vfs.h>
24
#include <sys/mount.h>
Dees_Troy's avatar
Dees_Troy committed
25
#include <unistd.h>
26
#include <dirent.h>
27
#include <libgen.h>
bigbiff's avatar
bigbiff committed
28
#include <zlib.h>
29 30
#include <iostream>
#include <sstream>
Ethan Yonker's avatar
Ethan Yonker committed
31
#include <sys/param.h>
32
#include <fcntl.h>
Dees_Troy's avatar
Dees_Troy committed
33

Dees_Troy's avatar
Dees_Troy committed
34 35 36 37
#ifdef TW_INCLUDE_CRYPTO
	#include "cutils/properties.h"
#endif

bigbiff's avatar
bigbiff committed
38
#include "libblkid/include/blkid.h"
Dees_Troy's avatar
Dees_Troy committed
39
#include "variables.h"
40
#include "twcommon.h"
Dees_Troy's avatar
Dees_Troy committed
41
#include "partitions.hpp"
42
#include "data.hpp"
43
#include "twrp-functions.hpp"
44
#include "twrpTar.hpp"
45
#include "exclude.hpp"
46
#include "infomanager.hpp"
47
#include "set_metadata.h"
Ethan Yonker's avatar
Ethan Yonker committed
48
#include "gui/gui.hpp"
bigbiff's avatar
bigbiff committed
49
#include "adbbu/libtwadbbu.hpp"
50
extern "C" {
51 52
	#include "mtdutils/mtdutils.h"
	#include "mtdutils/mounts.h"
Dees_Troy's avatar
Dees_Troy committed
53
#ifdef USE_EXT4
Ethan Yonker's avatar
Ethan Yonker committed
54 55
	// #include "make_ext4fs.h" TODO need ifdef for android8
	#include <ext4_utils/make_ext4fs.h>
Dees_Troy's avatar
Dees_Troy committed
56
#endif
57 58

#ifdef TW_INCLUDE_CRYPTO
59
	#include "crypto/lollipop/cryptfs.h"
Ethan Yonker's avatar
Ethan Yonker committed
60
	#include "gpt/gpt.h"
Ethan Yonker's avatar
Ethan Yonker committed
61 62 63
	#ifdef TW_INCLUDE_FBE
		#include "crypto/ext4crypt/Decrypt.h"
	#endif
64 65
#else
	#define CRYPT_FOOTER_OFFSET 0x4000
66
#endif
67
}
68
#include <selinux/selinux.h>
69
#include <selinux/label.h>
70 71 72 73 74
#ifdef HAVE_CAPABILITIES
#include <sys/capability.h>
#include <sys/xattr.h>
#include <linux/xattr.h>
#endif
HashBang's avatar
HashBang committed
75
#include <sparse_format.h>
76
#include "progresstracking.hpp"
Dees_Troy's avatar
Dees_Troy committed
77

78 79
using namespace std;

Dees_Troy's avatar
Dees_Troy committed
80
extern struct selabel_handle *selinux_handle;
81
extern bool datamedia;
Dees_Troy's avatar
Dees_Troy committed
82

83 84 85 86 87
struct flag_list {
	const char *name;
	unsigned flag;
};

88
const struct flag_list mount_flags[] = {
89 90 91 92 93 94 95 96 97 98
	{ "noatime",    MS_NOATIME },
	{ "noexec",     MS_NOEXEC },
	{ "nosuid",     MS_NOSUID },
	{ "nodev",      MS_NODEV },
	{ "nodiratime", MS_NODIRATIME },
	{ "ro",         MS_RDONLY },
	{ "rw",         0 },
	{ "remount",    MS_REMOUNT },
	{ "bind",       MS_BIND },
	{ "rec",        MS_REC },
99
#ifdef MS_UNBINDABLE
100
	{ "unbindable", MS_UNBINDABLE },
101 102
#endif
#ifdef MS_PRIVATE
103
	{ "private",    MS_PRIVATE },
104 105
#endif
#ifdef MS_SLAVE
106
	{ "slave",      MS_SLAVE },
107 108
#endif
#ifdef MS_SHARED
109
	{ "shared",     MS_SHARED },
110
#endif
111 112 113 114 115
	{ "sync",       MS_SYNCHRONOUS },
	{ "defaults",   0 },
	{ 0,            0 },
};

116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
enum TW_FSTAB_FLAGS {
	TWFLAG_DEFAULTS, // Retain position
	TWFLAG_ANDSEC,
	TWFLAG_BACKUP,
	TWFLAG_BACKUPNAME,
	TWFLAG_BLOCKSIZE,
	TWFLAG_CANBEWIPED,
	TWFLAG_CANENCRYPTBACKUP,
	TWFLAG_DISPLAY,
	TWFLAG_ENCRYPTABLE,
	TWFLAG_FLASHIMG,
	TWFLAG_FORCEENCRYPT,
	TWFLAG_FSFLAGS,
	TWFLAG_IGNOREBLKID,
	TWFLAG_LENGTH,
	TWFLAG_MOUNTTODECRYPT,
	TWFLAG_REMOVABLE,
	TWFLAG_RETAINLAYOUTVERSION,
	TWFLAG_SETTINGSSTORAGE,
	TWFLAG_STORAGE,
	TWFLAG_STORAGENAME,
	TWFLAG_SUBPARTITIONOF,
	TWFLAG_SYMLINK,
	TWFLAG_USERDATAENCRYPTBACKUP,
	TWFLAG_USERMRF,
	TWFLAG_WIPEDURINGFACTORYRESET,
	TWFLAG_WIPEINGUI,
Ethan Yonker's avatar
Ethan Yonker committed
143
	TWFLAG_SLOTSELECT,
144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
};

/* Flags without a trailing '=' are considered dual format flags and can be
 * written as either 'flagname' or 'flagname=', where the character following
 * the '=' is Y,y,1 for true and false otherwise.
 */
const struct flag_list tw_flags[] = {
	{ "andsec",                 TWFLAG_ANDSEC },
	{ "backup",                 TWFLAG_BACKUP },
	{ "backupname=",            TWFLAG_BACKUPNAME },
	{ "blocksize=",             TWFLAG_BLOCKSIZE },
	{ "canbewiped",             TWFLAG_CANBEWIPED },
	{ "canencryptbackup",       TWFLAG_CANENCRYPTBACKUP },
	{ "defaults",               TWFLAG_DEFAULTS },
	{ "display=",               TWFLAG_DISPLAY },
	{ "encryptable=",           TWFLAG_ENCRYPTABLE },
	{ "flashimg",               TWFLAG_FLASHIMG },
	{ "forceencrypt=",          TWFLAG_FORCEENCRYPT },
	{ "fsflags=",               TWFLAG_FSFLAGS },
	{ "ignoreblkid",            TWFLAG_IGNOREBLKID },
	{ "length=",                TWFLAG_LENGTH },
	{ "mounttodecrypt",         TWFLAG_MOUNTTODECRYPT },
	{ "removable",              TWFLAG_REMOVABLE },
	{ "retainlayoutversion",    TWFLAG_RETAINLAYOUTVERSION },
	{ "settingsstorage",        TWFLAG_SETTINGSSTORAGE },
	{ "storage",                TWFLAG_STORAGE },
	{ "storagename=",           TWFLAG_STORAGENAME },
	{ "subpartitionof=",        TWFLAG_SUBPARTITIONOF },
	{ "symlink=",               TWFLAG_SYMLINK },
	{ "userdataencryptbackup",  TWFLAG_USERDATAENCRYPTBACKUP },
	{ "usermrf",                TWFLAG_USERMRF },
	{ "wipeduringfactoryreset", TWFLAG_WIPEDURINGFACTORYRESET },
	{ "wipeingui",              TWFLAG_WIPEINGUI },
Ethan Yonker's avatar
Ethan Yonker committed
177
	{ "slotselect",             TWFLAG_SLOTSELECT },
178 179 180
	{ 0,                        0 },
};

181
TWPartition::TWPartition() {
Dees_Troy's avatar
Dees_Troy committed
182 183
	Can_Be_Mounted = false;
	Can_Be_Wiped = false;
Dees_Troy's avatar
Dees_Troy committed
184
	Can_Be_Backed_Up = false;
185
	Use_Rm_Rf = false;
Dees_Troy's avatar
Dees_Troy committed
186 187 188
	Wipe_During_Factory_Reset = false;
	Wipe_Available_in_GUI = false;
	Is_SubPartition = false;
189
	Has_SubPartition = false;
Dees_Troy's avatar
Dees_Troy committed
190 191 192 193
	SubPartition_Of = "";
	Symlink_Path = "";
	Symlink_Mount_Point = "";
	Mount_Point = "";
Dees_Troy's avatar
Dees_Troy committed
194
	Backup_Path = "";
195 196
	Actual_Block_Device = "";
	Primary_Block_Device = "";
Dees_Troy's avatar
Dees_Troy committed
197 198 199 200 201 202 203 204 205 206 207
	Alternate_Block_Device = "";
	Removable = false;
	Is_Present = false;
	Length = 0;
	Size = 0;
	Used = 0;
	Free = 0;
	Backup_Size = 0;
	Can_Be_Encrypted = false;
	Is_Encrypted = false;
	Is_Decrypted = false;
Ethan Yonker's avatar
Ethan Yonker committed
208
	Is_FBE = false;
209
	Mount_To_Decrypt = false;
Dees_Troy's avatar
Dees_Troy committed
210 211
	Decrypted_Block_Device = "";
	Display_Name = "";
Dees_Troy's avatar
Dees_Troy committed
212 213
	Backup_Display_Name = "";
	Storage_Name = "";
Dees_Troy's avatar
Dees_Troy committed
214
	Backup_Name = "";
215
	Backup_FileName = "";
216
	MTD_Name = "";
bigbiff's avatar
bigbiff committed
217
	Backup_Method = BM_NONE;
Dees_Troy's avatar
Dees_Troy committed
218 219
	Can_Encrypt_Backup = false;
	Use_Userdata_Encryption = false;
Dees_Troy's avatar
Dees_Troy committed
220
	Has_Data_Media = false;
Dees_Troy's avatar
Dees_Troy committed
221
	Has_Android_Secure = false;
Dees_Troy's avatar
Dees_Troy committed
222
	Is_Storage = false;
Dees_Troy's avatar
Dees_Troy committed
223
	Is_Settings_Storage = false;
Dees_Troy's avatar
Dees_Troy committed
224 225 226
	Storage_Path = "";
	Current_File_System = "";
	Fstab_File_System = "";
227 228
	Mount_Flags = 0;
	Mount_Options = "";
Dees_Troy's avatar
Dees_Troy committed
229
	Format_Block_Size = 0;
230
	Ignore_Blkid = false;
231
	Retain_Layout_Version = false;
232
	Crypto_Key_Location = "footer";
233
	MTP_Storage_ID = 0;
234
	Can_Flash_Img = false;
235
	Mount_Read_Only = false;
Ethan Yonker's avatar
Ethan Yonker committed
236 237
	Is_Adopted_Storage = false;
	Adopted_GUID = "";
Ethan Yonker's avatar
Ethan Yonker committed
238
	SlotSelect = false;
Dees_Troy's avatar
Dees_Troy committed
239 240 241 242 243 244
}

TWPartition::~TWPartition(void) {
	// Do nothing
}

245 246
bool TWPartition::Process_Fstab_Line(const char *fstab_line, bool Display_Error) {
	char full_line[MAX_FSTAB_LINE_LENGTH];
247
	char twflags[MAX_FSTAB_LINE_LENGTH] = "";
248
	char* ptr;
249
	int line_len = strlen(fstab_line), index = 0, item_index = 0;
Dees_Troy's avatar
Dees_Troy committed
250
	bool skip = false;
251

252
	strlcpy(full_line, fstab_line, sizeof(full_line));
253
	for (index = 0; index < line_len; index++) {
Dees_Troy's avatar
Dees_Troy committed
254 255 256
		if (full_line[index] == 34)
			skip = !skip;
		if (!skip && full_line[index] <= 32)
257 258
			full_line[index] = '\0';
	}
Dees_Troy's avatar
Dees_Troy committed
259
	Mount_Point = full_line;
260
	LOGINFO("Processing '%s'\n", Mount_Point.c_str());
Dees_Troy's avatar
Dees_Troy committed
261
	Backup_Path = Mount_Point;
Dees_Troy's avatar
Dees_Troy committed
262
	Storage_Path = Mount_Point;
263 264 265
	Display_Name = full_line + 1;
	Backup_Display_Name = Display_Name;
	Storage_Name = Display_Name;
266 267 268 269 270 271 272 273 274 275 276 277 278 279
	index = Mount_Point.size();
	while (index < line_len) {
		while (index < line_len && full_line[index] == '\0')
			index++;
		if (index >= line_len)
			continue;
		ptr = full_line + index;
		if (item_index == 0) {
			// File System
			Fstab_File_System = ptr;
			Current_File_System = ptr;
			item_index++;
		} else if (item_index == 1) {
			// Primary Block Device
280
			if (Fstab_File_System == "mtd" || Fstab_File_System == "yaffs2") {
Dees_Troy's avatar
Dees_Troy committed
281 282
				MTD_Name = ptr;
				Find_MTD_Block_Device(MTD_Name);
Dees_Troy's avatar
Dees_Troy committed
283 284 285 286 287 288 289
			} else if (Fstab_File_System == "bml") {
				if (Mount_Point == "/boot")
					MTD_Name = "boot";
				else if (Mount_Point == "/recovery")
					MTD_Name = "recovery";
				Primary_Block_Device = ptr;
				if (*ptr != '/')
290
					LOGERR("Until we get better BML support, you will have to find and provide the full block device path to the BML devices e.g. /dev/block/bml9 instead of the partition name\n");
291
			} else if (*ptr != '/') {
292
				if (Display_Error)
293
					LOGERR("Invalid block device '%s' in fstab line '%s'", ptr, fstab_line);
294
				else
295
					LOGINFO("Invalid block device '%s' in fstab line '%s'", ptr, fstab_line);
296
				return false;
297 298 299
			} else {
				Primary_Block_Device = ptr;
				Find_Real_Block_Device(Primary_Block_Device, Display_Error);
300 301 302 303 304 305 306 307 308 309 310
			}
			item_index++;
		} else if (item_index > 1) {
			if (*ptr == '/') {
				// Alternate Block Device
				Alternate_Block_Device = ptr;
				Find_Real_Block_Device(Alternate_Block_Device, Display_Error);
			} else if (strlen(ptr) > 7 && strncmp(ptr, "length=", 7) == 0) {
				// Partition length
				ptr += 7;
				Length = atoi(ptr);
311 312 313
			} else if (strlen(ptr) > 6 && strncmp(ptr, "flags=", 6) == 0) {
				// Custom flags, save for later so that new values aren't overwritten by defaults
				ptr += 6;
314
				strlcpy(twflags, ptr, sizeof(twflags));
315 316
			} else if (strlen(ptr) == 4 && (strncmp(ptr, "NULL", 4) == 0 || strncmp(ptr, "null", 4) == 0 || strncmp(ptr, "null", 4) == 0)) {
				// Do nothing
317 318
			} else {
				// Unhandled data
319
				LOGINFO("Unhandled fstab information '%s' in fstab line '%s'\n", ptr, fstab_line);
320 321 322 323 324 325 326 327
			}
		}
		while (index < line_len && full_line[index] != '\0')
			index++;
	}

	if (!Is_File_System(Fstab_File_System) && !Is_Image(Fstab_File_System)) {
		if (Display_Error)
328
			LOGERR("Unknown File System: '%s'\n", Fstab_File_System.c_str());
329
		else
330
			LOGINFO("Unknown File System: '%s'\n", Fstab_File_System.c_str());
331
		return false;
332
	} else if (Is_File_System(Fstab_File_System)) {
333
		Find_Actual_Block_Device();
334 335 336
		Setup_File_System(Display_Error);
		if (Mount_Point == "/system") {
			Display_Name = "System";
Dees_Troy's avatar
Dees_Troy committed
337 338
			Backup_Display_Name = Display_Name;
			Storage_Name = Display_Name;
339
			Wipe_Available_in_GUI = true;
Dees_Troy's avatar
Dees_Troy committed
340
			Can_Be_Backed_Up = true;
341
			Mount_Read_Only = true;
342 343
		} else if (Mount_Point == "/data") {
			Display_Name = "Data";
Dees_Troy's avatar
Dees_Troy committed
344 345
			Backup_Display_Name = Display_Name;
			Storage_Name = Display_Name;
346
			Wipe_Available_in_GUI = true;
347
			Wipe_During_Factory_Reset = true;
Dees_Troy's avatar
Dees_Troy committed
348
			Can_Be_Backed_Up = true;
Dees_Troy's avatar
Dees_Troy committed
349 350
			Can_Encrypt_Backup = true;
			Use_Userdata_Encryption = true;
351 352
		} else if (Mount_Point == "/cache") {
			Display_Name = "Cache";
Dees_Troy's avatar
Dees_Troy committed
353 354
			Backup_Display_Name = Display_Name;
			Storage_Name = Display_Name;
355
			Wipe_Available_in_GUI = true;
356
			Wipe_During_Factory_Reset = true;
Dees_Troy's avatar
Dees_Troy committed
357
			Can_Be_Backed_Up = true;
358
		} else if (Mount_Point == "/datadata") {
359
			Wipe_During_Factory_Reset = true;
360
			Display_Name = "DataData";
Dees_Troy's avatar
Dees_Troy committed
361 362
			Backup_Display_Name = Display_Name;
			Storage_Name = Display_Name;
363 364 365
			Is_SubPartition = true;
			SubPartition_Of = "/data";
			DataManager::SetValue(TW_HAS_DATADATA, 1);
Dees_Troy's avatar
Dees_Troy committed
366
			Can_Be_Backed_Up = true;
Dees_Troy's avatar
Dees_Troy committed
367 368
			Can_Encrypt_Backup = true;
			Use_Userdata_Encryption = false; // This whole partition should be encrypted
369
		} else if (Mount_Point == "/sd-ext") {
370
			Wipe_During_Factory_Reset = true;
371
			Display_Name = "SD-Ext";
Dees_Troy's avatar
Dees_Troy committed
372 373
			Backup_Display_Name = Display_Name;
			Storage_Name = Display_Name;
374
			Wipe_Available_in_GUI = true;
Dees_Troy's avatar
Dees_Troy committed
375
			Removable = true;
Dees_Troy's avatar
Dees_Troy committed
376
			Can_Be_Backed_Up = true;
Dees_Troy's avatar
Dees_Troy committed
377 378
			Can_Encrypt_Backup = true;
			Use_Userdata_Encryption = true;
Dees_Troy's avatar
Dees_Troy committed
379 380
		} else if (Mount_Point == "/boot") {
			Display_Name = "Boot";
Dees_Troy's avatar
Dees_Troy committed
381
			Backup_Display_Name = Display_Name;
Dees_Troy's avatar
Dees_Troy committed
382
			DataManager::SetValue("tw_boot_is_mountable", 1);
Dees_Troy's avatar
Dees_Troy committed
383
			Can_Be_Backed_Up = true;
384 385 386 387 388
		} else if (Mount_Point == "/vendor") {
			Display_Name = "Vendor";
			Backup_Display_Name = Display_Name;
			Storage_Name = Display_Name;
			Mount_Read_Only = true;
Dees_Troy's avatar
Dees_Troy committed
389 390 391 392 393
		}
#ifdef TW_EXTERNAL_STORAGE_PATH
		if (Mount_Point == EXPAND(TW_EXTERNAL_STORAGE_PATH)) {
			Is_Storage = true;
			Storage_Path = EXPAND(TW_EXTERNAL_STORAGE_PATH);
Dees_Troy's avatar
Dees_Troy committed
394
			Removable = true;
Dees_Troy's avatar
Dees_Troy committed
395
			Wipe_Available_in_GUI = true;
Dees_Troy's avatar
Dees_Troy committed
396
#else
397
		if (Mount_Point == "/sdcard" || Mount_Point == "/external_sd" || Mount_Point == "/external_sdcard") {
Dees_Troy's avatar
Dees_Troy committed
398
			Is_Storage = true;
Dees_Troy's avatar
Dees_Troy committed
399
			Removable = true;
Dees_Troy's avatar
Dees_Troy committed
400
			Wipe_Available_in_GUI = true;
Dees_Troy's avatar
Dees_Troy committed
401
#endif
Dees_Troy's avatar
Dees_Troy committed
402
		}
Dees_Troy's avatar
Dees_Troy committed
403 404 405
#ifdef TW_INTERNAL_STORAGE_PATH
		if (Mount_Point == EXPAND(TW_INTERNAL_STORAGE_PATH)) {
			Is_Storage = true;
Dees_Troy's avatar
Dees_Troy committed
406
			Is_Settings_Storage = true;
Dees_Troy's avatar
Dees_Troy committed
407
			Storage_Path = EXPAND(TW_INTERNAL_STORAGE_PATH);
Dees_Troy's avatar
Dees_Troy committed
408
			Wipe_Available_in_GUI = true;
Dees_Troy's avatar
Dees_Troy committed
409 410
		}
#else
411
		if (Mount_Point == "/emmc" || Mount_Point == "/internal_sd" || Mount_Point == "/internal_sdcard") {
Dees_Troy's avatar
Dees_Troy committed
412
			Is_Storage = true;
Dees_Troy's avatar
Dees_Troy committed
413 414
			Is_Settings_Storage = true;
			Wipe_Available_in_GUI = true;
Dees_Troy's avatar
Dees_Troy committed
415 416
		}
#endif
417
	} else if (Is_Image(Fstab_File_System)) {
418
		Find_Actual_Block_Device();
Ethan Yonker's avatar
Ethan Yonker committed
419
		Setup_Image();
Dees_Troy's avatar
Dees_Troy committed
420 421 422 423
		if (Mount_Point == "/boot") {
			Display_Name = "Boot";
			Backup_Display_Name = Display_Name;
			Can_Be_Backed_Up = true;
424
			Can_Flash_Img = true;
Dees_Troy's avatar
Dees_Troy committed
425 426 427
		} else if (Mount_Point == "/recovery") {
			Display_Name = "Recovery";
			Backup_Display_Name = Display_Name;
428
			Can_Flash_Img = true;
429 430 431
		} else if (Mount_Point == "/system_image") {
			Display_Name = "System Image";
			Backup_Display_Name = Display_Name;
HashBang's avatar
HashBang committed
432
			Can_Flash_Img = true;
433
			Can_Be_Backed_Up = true;
434 435 436
		} else if (Mount_Point == "/vendor_image") {
			Display_Name = "Vendor Image";
			Backup_Display_Name = Display_Name;
HashBang's avatar
HashBang committed
437
			Can_Flash_Img = true;
438
			Can_Be_Backed_Up = true;
Dees_Troy's avatar
Dees_Troy committed
439
		}
440 441
	}

442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468
	// Process TWRP fstab flags
	if (strlen(twflags) > 0) {
		string Prev_Display_Name = Display_Name;
		string Prev_Storage_Name = Storage_Name;
		string Prev_Backup_Display_Name = Backup_Display_Name;
		Display_Name = "";
		Storage_Name = "";
		Backup_Display_Name = "";

		Process_TW_Flags(twflags, Display_Error);

		bool has_display_name = !Display_Name.empty();
		bool has_storage_name = !Storage_Name.empty();
		bool has_backup_name = !Backup_Display_Name.empty();
		if (!has_display_name) Display_Name = Prev_Display_Name;
		if (!has_storage_name) Storage_Name = Prev_Storage_Name;
		if (!has_backup_name) Backup_Display_Name = Prev_Backup_Display_Name;

		if (has_display_name && !has_storage_name)
			Storage_Name = Display_Name;
		if (!has_display_name && has_storage_name)
			Display_Name = Storage_Name;
		if (has_display_name && !has_backup_name && Backup_Display_Name != "Android Secure")
			Backup_Display_Name = Display_Name;
		if (!has_display_name && has_backup_name)
			Display_Name = Backup_Display_Name;
	}
469 470 471
	return true;
}

472 473 474 475 476 477 478
void TWPartition::Partition_Post_Processing(bool Display_Error) {
	if (Mount_Point == "/data")
		Setup_Data_Partition(Display_Error);
	else if (Mount_Point == "/cache")
		Setup_Cache_Partition(Display_Error);
}

Ethan Yonker's avatar
Ethan Yonker committed
479 480 481 482 483
void TWPartition::ExcludeAll(const string& path) {
	backup_exclusions.add_absolute_dir(path);
	wipe_exclusions.add_absolute_dir(path);
}

484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500
void TWPartition::Setup_Data_Partition(bool Display_Error) {
	if (Mount_Point != "/data")
		return;

	// Ensure /data is not mounted as tmpfs for qcom hardware decrypt
	UnMount(false);

#ifdef TW_INCLUDE_CRYPTO
	if (datamedia)
		Setup_Data_Media();
	Can_Be_Encrypted = true;
	char crypto_blkdev[255];
	property_get("ro.crypto.fs_crypto_blkdev", crypto_blkdev, "error");
	if (strcmp(crypto_blkdev, "error") != 0) {
		DataManager::SetValue(TW_IS_DECRYPTED, 1);
		Is_Encrypted = true;
		Is_Decrypted = true;
Ethan Yonker's avatar
Ethan Yonker committed
501 502
		Is_FBE = false;
		DataManager::SetValue(TW_IS_FBE, 0);
503 504 505 506 507 508 509 510 511 512
		Decrypted_Block_Device = crypto_blkdev;
		LOGINFO("Data already decrypted, new block device: '%s'\n", crypto_blkdev);
	} else if (!Mount(false)) {
		if (Is_Present) {
			set_partition_data(Actual_Block_Device.c_str(), Crypto_Key_Location.c_str(), Fstab_File_System.c_str());
			if (cryptfs_check_footer() == 0) {
				Is_Encrypted = true;
				Is_Decrypted = false;
				Can_Be_Mounted = false;
				Current_File_System = "emmc";
Ethan Yonker's avatar
Ethan Yonker committed
513
				Setup_Image();
514 515 516 517 518 519 520 521 522 523 524
				DataManager::SetValue(TW_IS_ENCRYPTED, 1);
				DataManager::SetValue(TW_CRYPTO_PWTYPE, cryptfs_get_password_type());
				DataManager::SetValue(TW_CRYPTO_PASSWORD, "");
				DataManager::SetValue("tw_crypto_display", "");
			} else {
				gui_err("mount_data_footer=Could not mount /data and unable to find crypto footer.");
			}
		} else {
			LOGERR("Primary block device '%s' for mount point '%s' is not present!\n", Primary_Block_Device.c_str(), Mount_Point.c_str());
		}
	} else {
Ethan Yonker's avatar
Ethan Yonker committed
525 526 527 528 529 530 531
		if (TWFunc::Path_Exists("/data/unencrypted/key/version")) {
			LOGINFO("File Based Encryption is present\n");
#ifdef TW_INCLUDE_FBE
			ExcludeAll(Mount_Point + "/convert_fbe");
			ExcludeAll(Mount_Point + "/unencrypted");
			//ExcludeAll(Mount_Point + "/system/users/0"); // we WILL need to retain some of this if multiple users are present or we just need to delete more folders for the extra users somewhere else
			ExcludeAll(Mount_Point + "/misc/vold/user_keys");
532 533 534 535
			//ExcludeAll(Mount_Point + "/system_ce");
			//ExcludeAll(Mount_Point + "/system_de");
			//ExcludeAll(Mount_Point + "/misc_ce");
			//ExcludeAll(Mount_Point + "/misc_de");
Ethan Yonker's avatar
Ethan Yonker committed
536 537 538 539 540
			ExcludeAll(Mount_Point + "/system/gatekeeper.password.key");
			ExcludeAll(Mount_Point + "/system/gatekeeper.pattern.key");
			ExcludeAll(Mount_Point + "/system/locksettings.db");
			//ExcludeAll(Mount_Point + "/system/locksettings.db-shm"); // don't seem to need this one, but the other 2 are needed
			ExcludeAll(Mount_Point + "/system/locksettings.db-wal");
541
			//ExcludeAll(Mount_Point + "/user_de");
Ethan Yonker's avatar
Ethan Yonker committed
542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567
			//ExcludeAll(Mount_Point + "/misc/profiles/cur/0"); // might be important later
			ExcludeAll(Mount_Point + "/misc/gatekeeper");
			ExcludeAll(Mount_Point + "/drm/kek.dat");
			int retry_count = 3;
			while (!Decrypt_DE() && --retry_count)
				usleep(2000);
			if (retry_count > 0) {
				property_set("ro.crypto.state", "encrypted");
				Is_Encrypted = true;
				Is_Decrypted = false;
				Is_FBE = true;
				DataManager::SetValue(TW_IS_FBE, 1);
				DataManager::SetValue(TW_IS_ENCRYPTED, 1);
				string filename;
				DataManager::SetValue(TW_CRYPTO_PWTYPE, Get_Password_Type(0, filename));
				DataManager::SetValue(TW_CRYPTO_PASSWORD, "");
				DataManager::SetValue("tw_crypto_display", "");
			}
#else
			LOGERR("FBE found but FBE support not present in TWRP\n");
#endif
		} else {
			// Filesystem is not encrypted and the mount succeeded, so return to
			// the original unmounted state
			UnMount(false);
		}
568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594
	}
	if (datamedia && (!Is_Encrypted || (Is_Encrypted && Is_Decrypted))) {
		Setup_Data_Media();
		Recreate_Media_Folder();
	}
#else
	if (datamedia) {
		Setup_Data_Media();
		Recreate_Media_Folder();
	}
#endif
}

void TWPartition::Setup_Cache_Partition(bool Display_Error __unused) {
	if (Mount_Point != "/cache")
		return;

	if (!Mount(true))
		return;

	if (!TWFunc::Path_Exists("/cache/recovery/.")) {
		LOGINFO("Recreating /cache/recovery folder\n");
		if (mkdir("/cache/recovery", S_IRWXU | S_IRWXG | S_IWGRP | S_IXGRP) != 0)
			LOGERR("Could not create /cache/recovery\n");
	}
}

595 596 597
void TWPartition::Process_FS_Flags(const char *str) {
	char *options = strdup(str);
	char *ptr, *savep;
598

599 600 601 602 603 604
	Mount_Options = "";

	// Avoid issues with potentially nested strtok by using strtok_r
	ptr = strtok_r(options, ",", &savep);
	while (ptr) {
		const struct flag_list* mount_flag = mount_flags;
605

606 607 608 609 610
		for (; mount_flag->name; mount_flag++) {
			// mount_flags are never postfixed by '=',
			// so only match identical strings (including length)
			if (strcmp(ptr, mount_flag->name) == 0) {
				Mount_Flags |= mount_flag->flag;
611 612 613 614
				break;
			}
		}

615 616 617 618 619 620 621 622 623
		if (mount_flag->flag == MS_RDONLY)
			Mount_Read_Only = true;

		if (mount_flag->name != 0) {
			if (!Mount_Options.empty())
				Mount_Options += ",";
			Mount_Options += mount_flag->name;
		} else {
			LOGINFO("Unhandled mount flag: '%s'\n", ptr);
624 625
		}

626 627 628
		ptr = strtok_r(NULL, ",", &savep);
	}
	free(options);
629 630
}

631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664
void TWPartition::Apply_TW_Flag(const unsigned flag, const char* str, const bool val) {
	switch (flag) {
		case TWFLAG_ANDSEC:
			Has_Android_Secure = val;
			break;
		case TWFLAG_BACKUP:
			Can_Be_Backed_Up = val;
			break;
		case TWFLAG_BACKUPNAME:
			Backup_Display_Name = str;
			break;
		case TWFLAG_BLOCKSIZE:
			Format_Block_Size = (unsigned long)(atol(str));
			break;
		case TWFLAG_CANBEWIPED:
			Can_Be_Wiped = val;
			break;
		case TWFLAG_CANENCRYPTBACKUP:
			Can_Encrypt_Backup = val;
			break;
		case TWFLAG_DEFAULTS:
			// Do nothing
			break;
		case TWFLAG_DISPLAY:
			Display_Name = str;
			break;
		case TWFLAG_ENCRYPTABLE:
		case TWFLAG_FORCEENCRYPT:
			Crypto_Key_Location = str;
			break;
		case TWFLAG_FLASHIMG:
			Can_Flash_Img = val;
			break;
		case TWFLAG_FSFLAGS:
665
			Process_FS_Flags(str);
666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684
			break;
		case TWFLAG_IGNOREBLKID:
			Ignore_Blkid = val;
			break;
		case TWFLAG_LENGTH:
			Length = atoi(str);
			break;
		case TWFLAG_MOUNTTODECRYPT:
			Mount_To_Decrypt = val;
			break;
		case TWFLAG_REMOVABLE:
			Removable = val;
			break;
		case TWFLAG_RETAINLAYOUTVERSION:
			Retain_Layout_Version = val;
			break;
		case TWFLAG_SETTINGSSTORAGE:
			Is_Settings_Storage = val;
			if (Is_Settings_Storage)
685
				Is_Storage = true;
686 687 688 689 690 691 692 693
			break;
		case TWFLAG_STORAGE:
			Is_Storage = val;
			break;
		case TWFLAG_STORAGENAME:
			Storage_Name = str;
			break;
		case TWFLAG_SUBPARTITIONOF:
694
			Is_SubPartition = true;
695 696 697 698 699 700 701 702
			SubPartition_Of = str;
			break;
		case TWFLAG_SYMLINK:
			Symlink_Path = str;
			break;
		case TWFLAG_USERDATAENCRYPTBACKUP:
			Use_Userdata_Encryption = val;
			if (Use_Userdata_Encryption)
Dees_Troy's avatar
Dees_Troy committed
703
				Can_Encrypt_Backup = true;
704 705 706 707 708 709 710 711 712
			break;
		case TWFLAG_USERMRF:
			Use_Rm_Rf = val;
			break;
		case TWFLAG_WIPEDURINGFACTORYRESET:
			Wipe_During_Factory_Reset = val;
			if (Wipe_During_Factory_Reset) {
				Can_Be_Wiped = true;
				Wipe_Available_in_GUI = true;
Dees_Troy's avatar
Dees_Troy committed
713
			}
714 715 716 717 718 719
			break;
		case TWFLAG_WIPEINGUI:
			Wipe_Available_in_GUI = val;
			if (Wipe_Available_in_GUI)
				Can_Be_Wiped = true;
			break;
Ethan Yonker's avatar
Ethan Yonker committed
720 721 722
		case TWFLAG_SLOTSELECT:
			SlotSelect = true;
			break;
723 724 725 726 727 728
		default:
			// Should not get here
			LOGINFO("Flag identified for processing, but later unmatched: %i\n", flag);
			break;
	}
}
729

730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793
void TWPartition::Process_TW_Flags(char *flags, bool Display_Error) {
	char separator[2] = {'\n', 0};
	char *ptr, *savep;

	// Semicolons within double-quotes are not forbidden, so replace
	// only the semicolons intended as separators with '\n' for strtok
	for (unsigned i = 0, skip = 0; i < strlen(flags); i++) {
		if (flags[i] == '\"')
			skip = !skip;
		if (!skip && flags[i] == ';')
			flags[i] = separator[0];
	}

	// Avoid issues with potentially nested strtok by using strtok_r
	ptr = strtok_r(flags, separator, &savep);
	while (ptr) {
		int ptr_len = strlen(ptr);
		const struct flag_list* tw_flag = tw_flags;

		for (; tw_flag->name; tw_flag++) {
			int flag_len = strlen(tw_flag->name);

			if (strncmp(ptr, tw_flag->name, flag_len) == 0) {
				bool flag_val = false;

				if (ptr_len > flag_len && (tw_flag->name)[flag_len-1] != '='
						&& ptr[flag_len] != '=') {
					// Handle flags with same starting string
					// (e.g. backup and backupname)
					continue;
				} else if (ptr_len > flag_len && ptr[flag_len] == '=') {
					// Handle flags with dual format: Part 1
					// (e.g. backup and backup=y. backup=y handled here)
					ptr += flag_len + 1;
					TWFunc::Strip_Quotes(ptr);
					// Skip flags with empty argument
					// (e.g. backup=)
					if (strlen(ptr) == 0) {
						LOGINFO("Flag missing argument or should not include '=': %s=\n", tw_flag->name);
						break;
					}
					flag_val = strchr("yY1", *ptr) != NULL;
				} else if (ptr_len == flag_len
						&& (tw_flag->name)[flag_len-1] == '=') {
					// Skip flags missing argument after =
					// (e.g. backupname=)
					LOGINFO("Flag missing argument: %s\n", tw_flag->name);
					break;
				} else if (ptr_len > flag_len
						&& (tw_flag->name)[flag_len-1] == '=') {
					// Handle arguments to flags
					// (e.g. backupname="My Stuff")
					ptr += flag_len;
					TWFunc::Strip_Quotes(ptr);
					// Skip flags with empty argument
					// (e.g. backupname="")
					if (strlen(ptr) == 0) {
						LOGINFO("Flag missing argument: %s\n", tw_flag->name);
						break;
					}
				} else if (ptr_len == flag_len) {
					// Handle flags with dual format: Part 2
					// (e.g. backup and backup=y. backup handled here)
					flag_val = true;
794
				} else {
795 796
					LOGINFO("Flag matched, but could not be processed: %s\n", ptr);
					break;
797
				}
798 799 800

				Apply_TW_Flag(tw_flag->flag, ptr, flag_val);
				break;
801
			}
802 803
		}
		if (tw_flag->name == 0) {
804
			if (Display_Error)
805
				LOGERR("Unhandled flag: '%s'\n", ptr);
806
			else
807
				LOGINFO("Unhandled flag: '%s'\n", ptr);
808
		}
809
		ptr = strtok_r(NULL, separator, &savep);
810
	}
Dees_Troy's avatar
Dees_Troy committed
811 812
}

813 814
bool TWPartition::Is_File_System(string File_System) {
	if (File_System == "ext2" ||
815
		File_System == "ext3" ||
816 817 818 819
		File_System == "ext4" ||
		File_System == "vfat" ||
		File_System == "ntfs" ||
		File_System == "yaffs2" ||
820
		File_System == "exfat" ||
Dees_Troy's avatar
Dees_Troy committed
821
		File_System == "f2fs" ||
822 823 824 825 826 827 828
		File_System == "auto")
		return true;
	else
		return false;
}

bool TWPartition::Is_Image(string File_System) {
Dees_Troy's avatar
Dees_Troy committed
829
	if (File_System == "emmc" || File_System == "mtd" || File_System == "bml")
830 831 832 833 834
		return true;
	else
		return false;
}

835
bool TWPartition::Make_Dir(string Path, bool Display_Error) {
836 837
	if (TWFunc::Get_D_Type_From_Stat(Path) != S_IFDIR)
		unlink(Path.c_str());
838
	if (!TWFunc::Path_Exists(Path)) {
839 840
		if (mkdir(Path.c_str(), 0777) == -1) {
			if (Display_Error)
Ethan Yonker's avatar
Ethan Yonker committed
841
				gui_msg(Msg(msg::kError, "create_folder_strerr=Can not create '{1}' folder ({2}).")(Path)(strerror(errno)));
842
			else
843
				LOGINFO("Can not create '%s' folder.\n", Path.c_str());
844 845
			return false;
		} else {
846
			LOGINFO("Created '%s' folder.\n", Path.c_str());
847 848 849 850 851 852
			return true;
		}
	}
	return true;
}

853 854 855 856 857
void TWPartition::Setup_File_System(bool Display_Error) {
	Can_Be_Mounted = true;
	Can_Be_Wiped = true;

	// Make the mount point folder if it doesn't exist
858
	Make_Dir(Mount_Point, Display_Error);
859 860
	Display_Name = Mount_Point.substr(1, Mount_Point.size() - 1);
	Backup_Name = Display_Name;
bigbiff's avatar
bigbiff committed
861
	Backup_Method = BM_FILES;
862 863
}

Ethan Yonker's avatar
Ethan Yonker committed
864
void TWPartition::Setup_Image() {
865 866
	Display_Name = Mount_Point.substr(1, Mount_Point.size() - 1);
	Backup_Name = Display_Name;
867
	if (Current_File_System == "emmc")
bigbiff's avatar
bigbiff committed
868
		Backup_Method = BM_DD;
869
	else if (Current_File_System == "mtd" || Current_File_System == "bml")
bigbiff's avatar
bigbiff committed
870
		Backup_Method = BM_FLASH_UTILS;
871
	else
872
		LOGINFO("Unhandled file system '%s' on image '%s'\n", Current_File_System.c_str(), Display_Name.c_str());
873 874
}

Dees_Troy's avatar
Dees_Troy committed
875
void TWPartition::Setup_AndSec(void) {
Dees_Troy's avatar
Dees_Troy committed
876
	Backup_Display_Name = "Android Secure";
Dees_Troy's avatar
Dees_Troy committed
877
	Backup_Name = "and-sec";
Dees_Troy's avatar
Dees_Troy committed
878
	Can_Be_Backed_Up = true;
Dees_Troy's avatar
Dees_Troy committed
879 880 881 882 883 884
	Has_Android_Secure = true;
	Symlink_Path = Mount_Point + "/.android_secure";
	Symlink_Mount_Point = "/and-sec";
	Backup_Path = Symlink_Mount_Point;
	Make_Dir("/and-sec", true);
	Recreate_AndSec_Folder();
885
	Mount_Storage_Retry(true);
Dees_Troy's avatar
Dees_Troy committed
886 887
}

888
void TWPartition::Setup_Data_Media() {
889
	LOGINFO("Setting up '%s' as data/media emulated storage.\n", Mount_Point.c_str());
Ethan Yonker's avatar
Ethan Yonker committed
890 891
	if (Storage_Name.empty() || Storage_Name == "Data")
		Storage_Name = "Internal Storage";
892 893
	Has_Data_Media = true;
	Is_Storage = true;
Ethan Yonker's avatar
Ethan Yonker committed
894
	Storage_Path = Mount_Point + "/media";
895
	Symlink_Path = Storage_Path;
Ethan Yonker's avatar
Ethan Yonker committed
896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912
	if (Mount_Point == "/data") {
		Is_Settings_Storage = true;
		if (strcmp(EXPAND(TW_EXTERNAL_STORAGE_PATH), "/sdcard") == 0) {
			Make_Dir("/emmc", false);
			Symlink_Mount_Point = "/emmc";
		} else {
			Make_Dir("/sdcard", false);
			Symlink_Mount_Point = "/sdcard";
		}
		if (Mount(false) && TWFunc::Path_Exists(Mount_Point + "/media/0")) {
			Storage_Path = Mount_Point + "/media/0";
			Symlink_Path = Storage_Path;
			DataManager::SetValue(TW_INTERNAL_PATH, Mount_Point + "/media/0");
			UnMount(true);
		}
		DataManager::SetValue("tw_has_internal", 1);
		DataManager::SetValue("tw_has_data_media", 1);
913
		backup_exclusions.add_absolute_dir("/data/data/com.google.android.music/files");
914
		wipe_exclusions.add_absolute_dir(Mount_Point + "/misc/vold"); // adopted storage keys
915 916
		ExcludeAll(Mount_Point + "/.layout_version");
		ExcludeAll(Mount_Point + "/system/storage.xml");
917
	} else {
Ethan Yonker's avatar
Ethan Yonker committed
918 919 920 921 922
		if (Mount(true) && TWFunc::Path_Exists(Mount_Point + "/media/0")) {
			Storage_Path = Mount_Point + "/media/0";
			Symlink_Path = Storage_Path;
			UnMount(true);
		}
923
	}
924
	ExcludeAll(Mount_Point + "/media");
925 926
}

927 928 929 930 931 932 933 934 935 936 937 938 939
void TWPartition::Find_Real_Block_Device(string& Block, bool Display_Error) {
	char device[512], realDevice[512];

	strcpy(device, Block.c_str());
	memset(realDevice, 0, sizeof(realDevice));
	while (readlink(device, realDevice, sizeof(realDevice)) > 0)
	{
		strcpy(device, realDevice);
		memset(realDevice, 0, sizeof(realDevice));
	}

	if (device[0] != '/') {
		if (Display_Error)
940
			LOGERR("Invalid symlink path '%s' found on block device '%s'\n", device, Block.c_str());
941
		else
942
			LOGINFO("Invalid symlink path '%s' found on block device '%s'\n", device, Block.c_str());
943 944 945 946 947 948 949
		return;
	} else {
		Block = device;
		return;
	}
}

950
bool TWPartition::Mount_Storage_Retry(bool Display_Error) {
951
	// On some devices, storage doesn't want to mount right away, retry and sleep
952
	if (!Mount(Display_Error)) {
953 954 955 956 957
		int retry_count = 5;
		while (retry_count > 0 && !Mount(false)) {
			usleep(500000);
			retry_count--;
		}
958
		return Mount(Display_Error);
959
	}
960
	return true;
961 962
}

963 964 965 966 967 968
bool TWPartition::Find_MTD_Block_Device(string MTD_Name) {
	FILE *fp = NULL;
	char line[255];

	fp = fopen("/proc/mtd", "rt");
	if (fp == NULL) {
969
		LOGERR("Device does not support /proc/mtd\n");
970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994
		return false;
	}

	while (fgets(line, sizeof(line), fp) != NULL)
	{
		char device[32], label[32];
		unsigned long size = 0;
		int deviceId;

		sscanf(line, "%s %lx %*s %*c%s", device, &size, label);

		// Skip header and blank lines
		if ((strcmp(device, "dev:") == 0) || (strlen(line) < 8))
			continue;

		// Strip off the trailing " from the label
		label[strlen(label)-1] = '\0';

		if (strcmp(label, MTD_Name.c_str()) == 0) {
			// We found our device
			// Strip off the trailing : from the device
			device[strlen(device)-1] = '\0';
			if (sscanf(device,"mtd%d", &deviceId) == 1) {
				sprintf(device, "/dev/block/mtdblock%d", deviceId);
				Primary_Block_Device = device;
Dees_Troy's avatar
Dees_Troy committed
995 996
				fclose(fp);
				return true;
997 998 999 1000 1001 1002 1003 1004
			}
		}
	}
	fclose(fp);

	return false;
}

1005 1006 1007 1008 1009 1010 1011 1012 1013 1014
bool TWPartition::Get_Size_Via_statfs(bool Display_Error) {
	struct statfs st;
	string Local_Path = Mount_Point + "/.";

	if (!Mount(Display_Error))
		return false;

	if (statfs(Local_Path.c_str(), &st) != 0) {
		if (!Removable) {
			if (Display_Error)
1015
				LOGERR("Unable to statfs '%s'\n", Local_Path.c_str());
1016
			else
1017
				LOGINFO("Unable to statfs '%s'\n", Local_Path.c_str());
1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028
		}
		return false;
	}
	Size = (st.f_blocks * st.f_bsize);
	Used = ((st.f_blocks - st.f_bfree) * st.f_bsize);
	Free = (st.f_bfree * st.f_bsize);
	Backup_Size = Used;
	return true;
}

bool TWPartition::Get_Size_Via_df(bool Display_Error) {
1029 1030 1031 1032 1033 1034 1035 1036
	FILE* fp;
	char command[255], line[512];
	int include_block = 1;
	unsigned int min_len;

	if (!Mount(Display_Error))
		return false;

1037
	min_len = Actual_Block_Device.size() + 2;
1038
	sprintf(command, "df %s > /tmp/dfoutput.txt", Mount_Point.c_str());
1039
	TWFunc::Exec_Cmd(command);
1040 1041
	fp = fopen("/tmp/dfoutput.txt", "rt");
	if (fp == NULL) {
1042
		LOGINFO("Unable to open /tmp/dfoutput.txt.\n");
1043
		return false;
1044
	}
1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062

	while (fgets(line, sizeof(line), fp) != NULL)
	{
		unsigned long blocks, used, available;
		char device[64];
		char tmpString[64];

		if (strncmp(line, "Filesystem", 10) == 0)
			continue;
		if (strlen(line) < min_len) {
			include_block = 0;
			continue;
		}
		if (include_block) {
			sscanf(line, "%s %lu %lu %lu", device, &blocks, &used, &available);
		} else {
			// The device block string is so long that the df information is on the next line
			int space_count = 0;
Dees_Troy's avatar
Dees_Troy committed
1063
			sprintf(tmpString, "/dev/block/%s", Actual_Block_Device.c_str());
1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078
			while (tmpString[space_count] == 32)
				space_count++;
			sscanf(line + space_count, "%lu %lu %lu", &blocks, &used, &available);
		}

		// Adjust block size to byte size
		Size = blocks * 1024ULL;
		Used = used * 1024ULL;
		Free = available * 1024ULL;
		Backup_Size = Used;
	}
	fclose(fp);
	return true;
}

1079 1080
unsigned long long TWPartition::IOCTL_Get_Block_Size() {
	Find_Actual_Block_Device();
1081 1082

	return TWFunc::IOCTL_Get_Block_Size(Actual_Block_Device.c_str());
1083 1084
}

1085 1086 1087 1088 1089
bool TWPartition::Find_Partition_Size(void) {
	FILE* fp;
	char line[512];
	string tmpdevice;

1090 1091 1092 1093 1094 1095 1096
	fp = fopen("/proc/dumchar_info", "rt");
	if (fp != NULL) {
		while (fgets(line, sizeof(line), fp) != NULL)
		{
			char label[32], device[32];
			unsigned long size = 0;

that's avatar
that committed
1097
			sscanf(line, "%s %lx %*x %*u %s", label, &size, device);
1098

1099
			// Skip header, annotation	and blank lines
1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112
			if ((strncmp(device, "/dev/", 5) != 0) || (strlen(line) < 8))
				continue;

			tmpdevice = "/dev/";
			tmpdevice += label;
			if (tmpdevice == Primary_Block_Device || tmpdevice == Alternate_Block_Device) {
				Size = size;
				fclose(fp);
				return true;
			}
		}
	}

1113 1114 1115 1116 1117 1118
	unsigned long long ioctl_size = IOCTL_Get_Block_Size();
	if (ioctl_size) {
		Size = ioctl_size;
		return true;
	}

1119 1120 1121 1122 1123 1124 1125 1126 1127 1128
	// In this case, we'll first get the partitions we care about (with labels)
	fp = fopen("/proc/partitions", "rt");
	if (fp == NULL)
		return false;

	while (fgets(line, sizeof(line), fp) != NULL)
	{
		unsigned long major, minor, blocks;
		char device[512];

1129
		if (strlen(line) < 7 || line[0] == 'm')	 continue;
1130 1131 1132 1133
		sscanf(line + 1, "%lu %lu %lu %s", &major, &minor, &blocks, device);

		tmpdevice = "/dev/block/";
		tmpdevice += device;
1134
		if (tmpdevice == Primary_Block_Device || tmpdevice == Alternate_Block_Device) {
1135 1136 1137 1138 1139 1140 1141 1142 1143 1144
			// Adjust block size to byte size
			Size = blocks * 1024ULL;
			fclose(fp);
			return true;
		}
	}
	fclose(fp);
	return false;
}

Dees_Troy's avatar
Dees_Troy committed
1145
bool TWPartition::Is_Mounted(void) {
1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163
	if (!Can_Be_Mounted)
		return false;

	struct stat st1, st2;
	string test_path;

	// Check to see if the mount point directory exists
	test_path = Mount_Point + "/.";
	if (stat(test_path.c_str(), &st1) != 0)  return false;

	// Check to see if the directory above the mount point exists
	test_path = Mount_Point + "/../.";
	if (stat(test_path.c_str(), &st2) != 0)  return false;

	// Compare the device IDs -- if they match then we're (probably) using tmpfs instead of an actual device
	int ret = (st1.st_dev != st2.st_dev) ? true : false;

	return ret;
Dees_Troy's avatar
Dees_Troy committed
1164 1165
}

1166 1167 1168 1169 1170 1171 1172 1173
bool TWPartition::Is_File_System_Writable(void) {
	if (!Is_File_System(Current_File_System) || !Is_Mounted())
		return false;

	string test_path = Mount_Point + "/.";
	return (access(test_path.c_str(), W_OK) == 0);
}

Dees_Troy's avatar
Dees_Troy committed
1174
bool TWPartition::Mount(bool Display_Error) {
Dees_Troy's avatar
Dees_Troy committed
1175
	int exfat_mounted = 0;
1176
	unsigned long flags = Mount_Flags;
Dees_Troy's avatar
Dees_Troy committed
1177

Dees_Troy's avatar
Dees_Troy committed
1178
	if (Is_Mounted()) {
1179 1180 1181 1182
		return true;
	} else if (!Can_Be_Mounted) {
		return false;
	}
1183 1184 1185 1186 1187

	Find_Actual_Block_Device();

	// Check the current file system before mounting
	Check_FS_Type();
Dees_Troy's avatar
Dees_Troy committed
1188
	if (Current_File_System == "exfat" && TWFunc::Path_Exists("/sbin/exfat-fuse")) {
Dees_Troy's avatar
Dees_Troy committed
1189
		string cmd = "/sbin/exfat-fuse -o big_writes,max_read=131072,max_write=131072 " + Actual_Block_Device + " " + Mount_Point;
1190
		LOGINFO("cmd: %s\n", cmd.c_str());
Dees_Troy's avatar
Dees_Troy committed
1191 1192
		string result;
		if (TWFunc::Exec_Cmd(cmd, result) != 0) {
1193
			LOGINFO("exfat-fuse failed to mount with result '%s', trying vfat\n", result.c_str());
Dees_Troy's avatar
Dees_Troy committed
1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204
			Current_File_System = "vfat";
		} else {
#ifdef TW_NO_EXFAT_FUSE
			UnMount(false);
			// We'll let the kernel handle it but using exfat-fuse to detect if the file system is actually exfat
			// Some kernels let us mount vfat as exfat which doesn't work out too well
#else
			exfat_mounted = 1;
#endif
		}
	}
1205

1206
	if (Current_File_System == "ntfs" && (TWFunc::Path_Exists("/sbin/ntfs-3g") || TWFunc::Path_Exists("/sbin/mount.ntfs"))) {
Ethan Yonker's avatar
Ethan Yonker committed
1207
		string cmd;
1208 1209 1210 1211 1212 1213 1214
		string Ntfsmount_Binary = "";

		if (TWFunc::Path_Exists("/sbin/ntfs-3g"))
			Ntfsmount_Binary = "ntfs-3g";
		else if (TWFunc::Path_Exists("/sbin/mount.ntfs"))
			Ntfsmount_Binary = "mount.ntfs";

Ethan Yonker's avatar
Ethan Yonker committed
1215
		if (Mount_Read_Only)
1216
			cmd = "/sbin/" + Ntfsmount_Binary + " -o ro " + Actual_Block_Device + " " + Mount_Point;
Ethan Yonker's avatar
Ethan Yonker committed
1217
		else
1218
			cmd = "/sbin/" + Ntfsmount_Binary + " " + Actual_Block_Device + " " + Mount_Point;
Ethan Yonker's avatar
Ethan Yonker committed
1219
		LOGINFO("cmd: '%s'\n", cmd.c_str());
1220

Ethan Yonker's avatar
Ethan Yonker committed
1221 1222 1223 1224 1225 1226 1227
		if (TWFunc::Exec_Cmd(cmd) == 0) {
			return true;
		} else {
			LOGINFO("ntfs-3g failed to mount, trying regular mount method.\n");
		}
	}

1228 1229 1230
	if (Mount_Read_Only)
		flags |= MS_RDONLY;

1231 1232
	if (Fstab_File_System == "yaffs2") {
		// mount an MTD partition as a YAFFS2 filesystem.
1233 1234 1235
		flags = MS_NOATIME | MS_NODEV | MS_NODIRATIME;
		if (Mount_Read_Only)
			flags |= MS_RDONLY;
Dees_Troy's avatar
Dees_Troy committed
1236 1237 1238
		if (mount(Actual_Block_Device.c_str(), Mount_Point.c_str(), Fstab_File_System.c_str(), flags, NULL) < 0) {
			if (mount(Actual_Block_Device.c_str(), Mount_Point.c_str(), Fstab_File_System.c_str(), flags | MS_RDONLY, NULL) < 0) {
				if (Display_Error)
Ethan Yonker's avatar
Ethan Yonker committed
1239
					gui_msg(Msg(msg::kError, "fail_mount=Failed to mount '{1}' ({2})")(Mount_Point)(strerror(errno)));
Dees_Troy's avatar
Dees_Troy committed
1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251
				else
					LOGINFO("Failed to mount '%s' (MTD)\n", Mount_Point.c_str());
				return false;
			} else {
				LOGINFO("Mounted '%s' (MTD) as RO\n", Mount_Point.c_str());
				return true;
			}
		} else {
			struct stat st;
			string test_path = Mount_Point;
			if (stat(test_path.c_str(), &st) < 0) {
				if (Display_Error)
Ethan Yonker's avatar
Ethan Yonker committed
1252
					gui_msg(Msg(msg::kError, "fail_mount=Failed to mount '{1}' ({2})")(Mount_Point)(strerror(errno)));
Dees_Troy's avatar
Dees_Troy committed
1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267
				else
					LOGINFO("Failed to mount '%s' (MTD)\n", Mount_Point.c_str());
				return false;
			}
			mode_t new_mode = st.st_mode | S_IXUSR | S_IXGRP | S_IXOTH;
			if (new_mode != st.st_mode) {
				LOGINFO("Fixing execute permissions for %s\n", Mount_Point.c_str());
				if (chmod(Mount_Point.c_str(), new_mode) < 0) {
					if (Display_Error)
						LOGERR("Couldn't fix permissions for %s: %s\n", Mount_Point.c_str(), strerror(errno));
					else
						LOGINFO("Couldn't fix permissions for %s: %s\n", Mount_Point.c_str(), strerror(errno));
					return false;
				}
			}
1268
			return true;
Dees_Troy's avatar
Dees_Troy committed
1269
		}
that's avatar
that committed
1270 1271 1272 1273 1274 1275 1276
	}

	string mount_fs = Current_File_System;
	if (Current_File_System == "exfat" && TWFunc::Path_Exists("/sys/module/texfat"))
		mount_fs = "texfat";

	if (!exfat_mounted &&
1277 1278
		mount(Actual_Block_Device.c_str(), Mount_Point.c_str(), mount_fs.c_str(), flags, Mount_Options.c_str()) != 0 &&
		mount(Actual_Block_Device.c_str(), Mount_Point.c_str(), mount_fs.c_str(), flags, NULL) != 0) {
Dees_Troy's avatar
Dees_Troy committed
1279 1280
#ifdef TW_NO_EXFAT_FUSE
		if (Current_File_System == "exfat") {
1281
			LOGINFO("Mounting exfat failed, trying vfat...\n");
Dees_Troy's avatar
Dees_Troy committed
1282
			if (mount(Actual_Block_Device.c_str(), Mount_Point.c_str(), "vfat", 0, NULL) != 0) {
1283
				if (Display_Error)
Ethan Yonker's avatar
Ethan Yonker committed
1284
					gui_msg(Msg(msg::kError, "fail_mount=Failed to mount '{1}' ({2})")(Mount_Point)(strerror(errno)));
1285
				else
1286
					LOGINFO("Unable to mount '%s'\n", Mount_Point.c_str());
1287
				LOGINFO("Actual block device: '%s', current file system: '%s', flags: 0x%8x, options: '%s'\n", Actual_Block_Device.c_str(), Current_File_System.c_str(), flags, Mount_Options.c_str());
Dees_Troy's avatar
Dees_Troy committed
1288
				return false;
1289
			}
Dees_Troy's avatar