module load: attempt to load modules from /vendor

Use TW_LOAD_VENDOR_MODULES := "module1.ko module2.ko modulen.ko"
in BoardConfig to have TWRP attempt to load kernel modules during
startup. For fastboot ramdisks, TWRP will attempt to load from
the ramdisk from /vendor/lib/modules. You can have the build
system copy the modules to
$(TARGET_RECOVERY_ROOT_OUT)/vendor/lib/modules/1.1
Otherwise in recovery in boot mode, TWRP will attempt the following:
check /lib/modules (ramdisk vendor_boot)
check /lib/modules/N.N (ramdisk vendor_boot)
check /lib/modules/N.N-gki (ramdisk vendor_boot)
check /vendor/lib/modules (ramdisk)
check /vendor/lib/modules/1.1 (ramdisk prebuilt modules)
check /vendor/lib/modules/N.N (vendor mounted)
check /vendor/lib/modules/N.N-gki (vendor mounted)

Change-Id: I2dccf199e37d47cb7a7e79b0e11026d67b4e3186
diff --git a/Android.mk b/Android.mk
index bc6341e..6ac4633 100755
--- a/Android.mk
+++ b/Android.mk
@@ -62,6 +62,7 @@
     find_file.cpp \
     infomanager.cpp \
     data.cpp \
+    kernel_module_loader.cpp \
     partition.cpp \
     partitionmanager.cpp \
     progresstracking.cpp \
@@ -155,7 +156,10 @@
 ifeq ($(PRODUCT_USE_DYNAMIC_PARTITIONS),true)
     LOCAL_CFLAGS += -DPRODUCT_USE_DYNAMIC_PARTITIONS=1
     TWRP_REQUIRED_MODULES += android.hardware.health@2.1-service android.hardware.health@2.1-impl.recovery android.hardware.health@2.1-service.rc android.hardware.health@2.1.xml
-    TWRP_REQUIRED_MODULES += android.hardware.health@2.0-service android.hardware.health@2.0-impl.recovery android.hardware.health@2.0-service.rc 
+    TWRP_REQUIRED_MODULES += android.hardware.health@2.0-service android.hardware.health@2.0-impl.recovery android.hardware.health@2.0-service.rc
+    TWRP_REQUIRED_MODULES += libmodprobe
+    LOCAL_C_INCLUDES += system/core/libmodprobe/include/
+    LOCAL_STATIC_LIBRARIES += libmodprobe
     ifeq ($(TW_EXCLUDE_LPDUMP),)
         TWRP_REQUIRED_MODULES += lpdump lpdumpd.rc
     endif
@@ -297,6 +301,9 @@
 ifneq ($(TW_ADDITIONAL_APEX_FILES),)
     LOCAL_CFLAGS += -DTW_ADDITIONAL_APEX_FILES=$(TW_ADDITIONAL_APEX_FILES)
 endif
+ifneq ($(TW_LOAD_VENDOR_MODULES),)
+    LOCAL_CFLAGS += -DTW_LOAD_VENDOR_MODULES=$(TW_LOAD_VENDOR_MODULES)
+endif
 ifeq ($(TW_INCLUDE_CRYPTO), true)
     LOCAL_CFLAGS += -DTW_INCLUDE_CRYPTO -DUSE_FSCRYPT -Wno-macro-redefined
     LOCAL_SHARED_LIBRARIES += libcryptfsfde
diff --git a/kernel_module_loader.cpp b/kernel_module_loader.cpp
new file mode 100644
index 0000000..fd24a91
--- /dev/null
+++ b/kernel_module_loader.cpp
@@ -0,0 +1,121 @@
+#include "kernel_module_loader.hpp"
+
+bool KernelModuleLoader::Load_Vendor_Modules(BOOT_MODE mode) {
+    // check /lib/modules (ramdisk vendor_boot)
+    // check /lib/modules/N.N (ramdisk vendor_boot)
+    // check /lib/modules/N.N-gki (ramdisk vendor_boot)
+    // check /vendor/lib/modules (ramdisk)
+    // check /vendor/lib/modules/1.1 (ramdisk prebuilt modules)
+    // check /vendor/lib/modules/N.N (vendor mounted)
+    // check /vendor/lib/modules/N.N-gki (vendor mounted)
+
+    LOGINFO("Attempting to load modules\n");
+    std::string vendor_base_dir(VENDOR_MODULE_DIR);
+    std::string base_dir(VENDOR_BOOT_MODULE_DIR);
+    std::vector<std::string> module_dirs;
+    std::vector<std::string> vendor_module_dirs;
+
+    TWPartition* ven = PartitionManager.Find_Partition_By_Path("/vendor");
+    vendor_module_dirs.push_back(VENDOR_MODULE_DIR);
+    vendor_module_dirs.push_back(vendor_base_dir + "/1.1");
+
+    module_dirs.push_back(base_dir);
+
+    struct utsname uts;
+    if (uname(&uts)) {
+        LOGERR("Unable to query kernel for version info\n");
+    }
+
+    std::string rls(uts.release);
+    std::vector<std::string> release = TWFunc::split_string(rls, '.', true);
+    module_dirs.push_back(base_dir + "/" + release[0] + "." + release[1]);
+
+    for (auto&& module_dir:module_dirs) {
+        Try_And_Load_Modules(module_dir);
+    }
+
+    for (auto&& module_dir:vendor_module_dirs) {
+        Try_And_Load_Modules(module_dir);
+    }
+
+    if (ven) {
+        LOGINFO("Checking mounted /vendor\n");
+        ven->Mount(true);
+    }
+
+    for (auto&& module_dir:vendor_module_dirs) {
+        Try_And_Load_Modules(module_dir);
+    }
+
+    if (ven)
+        ven->UnMount(false);
+
+	return true;
+}
+
+bool KernelModuleLoader::Try_And_Load_Modules(std::string module_dir) {
+        LOGINFO("Checking directory: %s\n", module_dir.c_str());
+        std::string dest_module_dir;
+        dest_module_dir = "/tmp" + module_dir;
+        TWFunc::Recursive_Mkdir(dest_module_dir);
+        Copy_Modules_To_Tmpfs(module_dir);
+        Write_Module_List(dest_module_dir);
+        Modprobe m({dest_module_dir}, "modules.load.twrp");
+        m.EnableVerbose(true);
+        m.LoadListedModules(false);
+        int modules_loaded = m.GetModuleCount();
+        LOGINFO("Modules Loaded: %d\n", modules_loaded);
+        return modules_loaded > 0 ? true : false;
+}
+
+bool KernelModuleLoader::Write_Module_List(std::string module_dir) {
+	DIR* d;
+	struct dirent* de;
+	std::vector<std::string> kernel_modules;
+	std::vector<std::string> kernel_modules_requested = TWFunc::split_string(EXPAND(TW_LOAD_VENDOR_MODULES), ' ', true);
+	d = opendir(module_dir.c_str());
+	if (d != nullptr) {
+		while ((de = readdir(d)) != nullptr) {
+			std::string kernel_module = de->d_name;
+			if (de->d_type == DT_REG) {
+				if (android::base::EndsWith(kernel_module, ".ko")) {
+					for (auto&& requested:kernel_modules_requested) {
+						if (kernel_module == requested) {
+							kernel_modules.push_back(kernel_module);
+                            continue;
+                        }
+					}
+					continue;
+				}
+			}
+		}
+        std::string module_file = module_dir + "/modules.load.twrp";
+		TWFunc::write_to_file(module_file, kernel_modules);
+        closedir(d);
+	}
+	return true;
+}
+
+bool KernelModuleLoader::Copy_Modules_To_Tmpfs(std::string module_dir) {
+    std::string ramdisk_dir = "/tmp" + module_dir;
+    DIR* d;
+	struct dirent* de;
+    d = opendir(module_dir.c_str());
+    if (d != nullptr) {
+        while ((de = readdir(d)) != nullptr) {
+            std::string kernel_module = de->d_name;
+            if (de->d_type == DT_REG) {
+                std::string src =  module_dir + "/" + de->d_name;
+                std::string dest = ramdisk_dir + "/" + de->d_name;
+                if (TWFunc::copy_file(src, dest, 0700, false) != 0) {
+                    return false;
+                }
+            }
+        }
+        closedir(d);
+    } else {
+        LOGINFO("Unable to open module directory: %s. Skipping\n", module_dir.c_str());
+        return false;
+    }
+    return true;
+}
\ No newline at end of file
diff --git a/kernel_module_loader.hpp b/kernel_module_loader.hpp
new file mode 100644
index 0000000..fe89031
--- /dev/null
+++ b/kernel_module_loader.hpp
@@ -0,0 +1,33 @@
+#ifndef _KERNELMODULELOADER_HPP
+#define _KERNELMODULELOADER_HPP
+
+#include <dirent.h>
+#include <string>
+#include <vector>
+#include <android-base/strings.h>
+#include <modprobe/modprobe.h>
+#include <sys/utsname.h>
+
+#include "twcommon.h"
+#include "twrp-functions.hpp"
+
+#define VENDOR_MODULE_DIR "/vendor/lib/modules"    // Base path for vendor kernel modules to check by TWRP
+#define VENDOR_BOOT_MODULE_DIR "/lib/modules"      // vendor_boot ramdisk GKI modules to check by TWRP
+typedef enum {
+	RECOVERY_FASTBOOT_MODE = 0,
+	RECOVERY_IN_BOOT_MODE,
+	FASTBOOTD_MODE
+} BOOT_MODE;
+
+class KernelModuleLoader
+{
+public:
+    static bool Load_Vendor_Modules(BOOT_MODE mode); // Load specific maintainer defined kernel modules in TWRP
+
+private:
+	static bool Try_And_Load_Modules(std::string module_dir); // Use libmodprobe to attempt loading kernel modules
+	static bool Write_Module_List(std::string module_dir); // Write list of modules to load from TW_LOAD_VENDOR_MODULES
+    static bool Copy_Modules_To_Tmpfs(std::string module_dir); // Copy modules to ramdisk for loading
+};
+
+#endif // _KERNELMODULELOADER_HPP
\ No newline at end of file
diff --git a/partitionmanager.cpp b/partitionmanager.cpp
index 2b8a3fd..481d8d8 100755
--- a/partitionmanager.cpp
+++ b/partitionmanager.cpp
@@ -148,12 +148,9 @@
 	return 0;
 }
 
-int TWPartitionManager::Process_Fstab(string Fstab_Filename, bool Display_Error) {
+int TWPartitionManager::Process_Fstab(string Fstab_Filename, bool Display_Error, bool recovery_mode) {
 	FILE *fstabFile;
 	char fstab_line[MAX_FSTAB_LINE_LENGTH];
-	TWPartition* settings_partition = NULL;
-	TWPartition* andsec_partition = NULL;
-	unsigned int storageid = 1 << 16;	// upper 16 bits are for physical storage device, we pretend to have only one
 	std::map<string, Flags_Map> twrp_flags;
 
 	fstabFile = fopen("/etc/twrp.flags", "rt");
@@ -262,114 +259,124 @@
 	}
 	LOGINFO("Done processing fstab files\n");
 
-	std::vector<TWPartition*>::iterator iter;
-	for (iter = Partitions.begin(); iter != Partitions.end(); iter++) {
-		(*iter)->Partition_Post_Processing(Display_Error);
-
-		if ((*iter)->Is_Storage) {
-			++storageid;
-			(*iter)->MTP_Storage_ID = storageid;
-		}
-
-		if (!settings_partition && (*iter)->Is_Settings_Storage && (*iter)->Is_Present)
-			settings_partition = (*iter);
-		else
-			(*iter)->Is_Settings_Storage = false;
-
-		if (!andsec_partition && (*iter)->Has_Android_Secure && (*iter)->Is_Present)
-			andsec_partition = (*iter);
-		else
-			(*iter)->Has_Android_Secure = false;
-
-		if ((*iter)->Is_Super)
-			Prepare_Super_Volume((*iter));
+	if (recovery_mode) {
+		Setup_Fstab_Partitions(Display_Error);
 	}
-
-	//Setup Apex before decryption
-	TWPartition* sys = PartitionManager.Find_Partition_By_Path(PartitionManager.Get_Android_Root_Path());
-	TWPartition* ven = PartitionManager.Find_Partition_By_Path("/vendor");
-	if (sys) {
-		if (sys->Get_Super_Status()) {
-			sys->Mount(true);
-			if (ven) {
-				ven->Mount(true);
-			}
-#ifdef TW_EXCLUDE_APEX
-			LOGINFO("Apex is disabled in this build\n");
-#else
-			twrpApex apex;
-			if (!apex.loadApexImages()) {
-				LOGERR("Unable to load apex images from %s\n", APEX_DIR);
-				property_set("twrp.apex.loaded", "false");
-			} else {
-				property_set("twrp.apex.loaded", "true");
-			}
-			TWFunc::check_and_run_script("/sbin/resyncapex.sh", "apex");
-#endif
-		}
-	}
-#ifndef USE_VENDOR_LIBS
-	if (ven)
-		ven->UnMount(true);
-	if (sys)
-		sys->UnMount(true);
-#endif
-
-	if (!datamedia && !settings_partition && Find_Partition_By_Path("/sdcard") == NULL && Find_Partition_By_Path("/internal_sd") == NULL && Find_Partition_By_Path("/internal_sdcard") == NULL && Find_Partition_By_Path("/emmc") == NULL) {
-		// Attempt to automatically identify /data/media emulated storage devices
-		TWPartition* Dat = Find_Partition_By_Path("/data");
-		if (Dat) {
-			LOGINFO("Using automatic handling for /data/media emulated storage device.\n");
-			datamedia = true;
-			Dat->Setup_Data_Media();
-			settings_partition = Dat;
-			// Since /data was not considered a storage partition earlier, we still need to assign an MTP ID
-			++storageid;
-			Dat->MTP_Storage_ID = storageid;
-		}
-	}
-	if (!settings_partition) {
-		for (iter = Partitions.begin(); iter != Partitions.end(); iter++) {
-			if ((*iter)->Is_Storage) {
-				settings_partition = (*iter);
-				break;
-			}
-		}
-		if (!settings_partition)
-			LOGERR("Unable to locate storage partition for storing settings file.\n");
-	}
-	if (!Write_Fstab()) {
-		if (Display_Error)
-			LOGERR("Error creating fstab\n");
-		else
-			LOGINFO("Error creating fstab\n");
-	}
-
-	if (andsec_partition) {
-		Setup_Android_Secure_Location(andsec_partition);
-	} else if (settings_partition) {
-		Setup_Android_Secure_Location(settings_partition);
-	}
-	if (settings_partition) {
-		Setup_Settings_Storage_Partition(settings_partition);
-	}
-
-#ifdef TW_INCLUDE_CRYPTO
-	DataManager::SetValue(TW_IS_ENCRYPTED, 1);
-	Decrypt_Data();
-#endif
-
-	Update_System_Details();
-	if (Get_Super_Status())
-		Setup_Super_Partition();
-	UnMount_Main_Partitions();
-#ifdef AB_OTA_UPDATER
-	DataManager::SetValue("tw_active_slot", Get_Active_Slot_Display());
-#endif
-	setup_uevent();
 	return true;
 }
 
+void TWPartitionManager::Setup_Fstab_Partitions(bool Display_Error) {
+		TWPartition* settings_partition = NULL;
+		TWPartition* andsec_partition = NULL;
+		std::vector<TWPartition*>::iterator iter;
+		unsigned int storageid = 1 << 16;	// upper 16 bits are for physical storage device, we pretend to have only one
+
+		for (iter = Partitions.begin(); iter != Partitions.end(); iter++) {
+			(*iter)->Partition_Post_Processing(Display_Error);
+
+			if ((*iter)->Is_Storage) {
+				++storageid;
+				(*iter)->MTP_Storage_ID = storageid;
+			}
+
+			if (!settings_partition && (*iter)->Is_Settings_Storage && (*iter)->Is_Present)
+				settings_partition = (*iter);
+			else
+				(*iter)->Is_Settings_Storage = false;
+
+			if (!andsec_partition && (*iter)->Has_Android_Secure && (*iter)->Is_Present)
+				andsec_partition = (*iter);
+			else
+				(*iter)->Has_Android_Secure = false;
+
+			if ((*iter)->Is_Super)
+				Prepare_Super_Volume((*iter));
+		}
+
+		//Setup Apex before decryption
+		TWPartition* sys = PartitionManager.Find_Partition_By_Path(PartitionManager.Get_Android_Root_Path());
+		TWPartition* ven = PartitionManager.Find_Partition_By_Path("/vendor");
+		if (sys) {
+			if (sys->Get_Super_Status()) {
+				sys->Mount(true);
+				if (ven) {
+					ven->Mount(true);
+				}
+	#ifdef TW_EXCLUDE_APEX
+				LOGINFO("Apex is disabled in this build\n");
+	#else
+				twrpApex apex;
+				if (!apex.loadApexImages()) {
+					LOGERR("Unable to load apex images from %s\n", APEX_DIR);
+					property_set("twrp.apex.loaded", "false");
+				} else {
+					property_set("twrp.apex.loaded", "true");
+				}
+				TWFunc::check_and_run_script("/sbin/resyncapex.sh", "apex");
+	#endif
+			}
+		}
+	#ifndef USE_VENDOR_LIBS
+		if (ven)
+			ven->UnMount(true);
+		if (sys)
+			sys->UnMount(true);
+	#endif
+
+		if (!datamedia && !settings_partition && Find_Partition_By_Path("/sdcard") == NULL && Find_Partition_By_Path("/internal_sd") == NULL && Find_Partition_By_Path("/internal_sdcard") == NULL && Find_Partition_By_Path("/emmc") == NULL) {
+			// Attempt to automatically identify /data/media emulated storage devices
+			TWPartition* Dat = Find_Partition_By_Path("/data");
+			if (Dat) {
+				LOGINFO("Using automatic handling for /data/media emulated storage device.\n");
+				datamedia = true;
+				Dat->Setup_Data_Media();
+				settings_partition = Dat;
+				// Since /data was not considered a storage partition earlier, we still need to assign an MTP ID
+				++storageid;
+				Dat->MTP_Storage_ID = storageid;
+			}
+		}
+		if (!settings_partition) {
+			for (iter = Partitions.begin(); iter != Partitions.end(); iter++) {
+				if ((*iter)->Is_Storage) {
+					settings_partition = (*iter);
+					break;
+				}
+			}
+			if (!settings_partition)
+				LOGERR("Unable to locate storage partition for storing settings file.\n");
+		}
+		if (!Write_Fstab()) {
+			if (Display_Error)
+				LOGERR("Error creating fstab\n");
+			else
+				LOGINFO("Error creating fstab\n");
+		}
+
+		if (andsec_partition) {
+			Setup_Android_Secure_Location(andsec_partition);
+		} else if (settings_partition) {
+			Setup_Android_Secure_Location(settings_partition);
+		}
+		if (settings_partition) {
+			Setup_Settings_Storage_Partition(settings_partition);
+		}
+
+	#ifdef TW_INCLUDE_CRYPTO
+		DataManager::SetValue(TW_IS_ENCRYPTED, 1);
+		Decrypt_Data();
+	#endif
+
+		Update_System_Details();
+		if (Get_Super_Status())
+			Setup_Super_Partition();
+		UnMount_Main_Partitions();
+	#ifdef AB_OTA_UPDATER
+		DataManager::SetValue("tw_active_slot", Get_Active_Slot_Display());
+	#endif
+		setup_uevent();
+}
+
 int TWPartitionManager::Write_Fstab(void) {
 	FILE *fp;
 	std::vector<TWPartition*>::iterator iter;
@@ -618,8 +625,6 @@
 		return ret;
 	} else if (Display_Error) {
 		gui_msg(Msg(msg::kError, "unable_find_part_path=Unable to find partition for path '{1}'")(Local_Path));
-	} else {
-		LOGINFO("Mount: Unable to find partition for path '%s'\n", Local_Path.c_str());
 	}
 	return false;
 }
@@ -2085,7 +2090,7 @@
 	if (!Part->UnMount(true) || !Part->Is_Present)
 		return false;
 
-	if (TWFunc::write_to_file(Lun_File, Part->Actual_Block_Device)) {
+	if (!TWFunc::write_to_file(Lun_File, Part->Actual_Block_Device)) {
 		LOGERR("Unable to write to ums lunfile '%s': (%s)\n", Lun_File.c_str(), strerror(errno));
 		return false;
 	}
@@ -2151,15 +2156,15 @@
 }
 
 int TWPartitionManager::usb_storage_disable(void) {
-	int index, ret;
+	int index, ret = 0;
 	char lun_file[255], ch[2] = {0, 0};
 	string str = ch;
 
 	for (index=0; index<2; index++) {
 		sprintf(lun_file, CUSTOM_LUN_FILE, index);
-		ret = TWFunc::write_to_file(lun_file, str);
-		if (ret < 0) {
+		if (!TWFunc::write_to_file(lun_file, str)) {
 			break;
+			ret = -1;
 		}
 	}
 	Mount_All_Storage();
diff --git a/partitions.hpp b/partitions.hpp
index 936d99b..4d432c3 100755
--- a/partitions.hpp
+++ b/partitions.hpp
@@ -317,7 +317,8 @@
 	~TWPartitionManager() {}
 
 public:
-	int Process_Fstab(string Fstab_Filename, bool Display_Error);             // Parses the fstab and populates the partitions
+	int Process_Fstab(string Fstab_Filename, bool Display_Error, bool recovery_mode); // Parses the fstab files
+	void Setup_Fstab_Partitions(bool Display_Error);                          // Populates the partitions
 	int Write_Fstab();                                                        // Creates /etc/fstab file that's used by the command line for mount commands
 	void Decrypt_Data();													  // Decrypt Data if enabled
 	void Output_Partition_Logging();                                          // Outputs partition information to the log
diff --git a/twrp-functions.cpp b/twrp-functions.cpp
index c56e194..0f7ebb3 100755
--- a/twrp-functions.cpp
+++ b/twrp-functions.cpp
@@ -38,6 +38,9 @@
 #include <cctype>
 #include <algorithm>
 #include <selinux/label.h>
+
+#include <android-base/strings.h>
+
 #include "twrp-functions.hpp"
 #include "twcommon.h"
 #include "gui/gui.hpp"
@@ -694,11 +697,13 @@
 	return r;
 }
 
-int TWFunc::copy_file(string src, string dst, int mode) {
-	PartitionManager.Mount_By_Path(src, false);
-	PartitionManager.Mount_By_Path(dst, false);
+int TWFunc::copy_file(string src, string dst, int mode, bool mount_paths) {
+	if (mount_paths) {
+		PartitionManager.Mount_By_Path(src, false);
+		PartitionManager.Mount_By_Path(dst, false);
+	}
 	if (!Path_Exists(src)) {
-		LOGINFO("Path %s does not exist. Unable to copy %s\n", src.c_str(), dst.c_str());
+		LOGINFO("Path %s does not exist. Unable to copy file to %s\n", src.c_str(), dst.c_str());
 		return -1;
 	}
 	std::ifstream srcfile(src.c_str(), ios::binary);
@@ -714,8 +719,10 @@
 
 	srcfile.close();
 	dstfile.close();
-	if (chmod(dst.c_str(), mode) != 0)
+	if (chmod(dst.c_str(), mode) != 0) {
+		LOGERR("Unable to chmod file: %s. Error: %s\n", dst.c_str(), strerror(errno));
 		return -1;
+	}
 	return 0;
 }
 
@@ -745,7 +752,10 @@
 	file.open(fn.c_str(), ios::in);
 
 	if (file.is_open()) {
-		file >> results;
+		std::string line;
+		while (std::getline(file, line)) {
+			results += line;
+		}
 		file.close();
 		return 0;
 	}
@@ -782,18 +792,33 @@
 	return -1;
 }
 
-int TWFunc::write_to_file(const string& fn, const string& line) {
+bool TWFunc::write_to_file(const string& fn, const string& line) {
 	FILE *file;
 	file = fopen(fn.c_str(), "w");
 	if (file != NULL) {
 		fwrite(line.c_str(), line.size(), 1, file);
 		fclose(file);
-		return 0;
+		return true;
 	}
 	LOGINFO("Cannot find file %s\n", fn.c_str());
-	return -1;
+	return false;
 }
 
+bool TWFunc::write_to_file(const string& fn, const std::vector<string> lines) {
+	FILE *file;
+	file = fopen(fn.c_str(), "a+");
+	if (file != NULL) {
+		for (auto&& line: lines) {
+			fwrite(line.c_str(), line.size(), 1, file);
+			fwrite("\n", sizeof(char), 1, file);
+		}
+		fclose(file);
+		return true;
+	}
+	return false;
+}
+
+
 bool TWFunc::Try_Decrypting_Backup(string Restore_Path, string Password) {
 	DIR* d;
 
@@ -1086,7 +1111,7 @@
 			TWFunc::write_to_file(secondary_brightness_file, brightness_value);
 		}
 	}
-	return result;
+	return result ? 0 : -1;
 }
 
 bool TWFunc::Toggle_MTP(bool enable) {
diff --git a/twrp-functions.hpp b/twrp-functions.hpp
index dc51454..77ca1a2 100755
--- a/twrp-functions.hpp
+++ b/twrp-functions.hpp
@@ -30,8 +30,8 @@
 
 using namespace std;
 
-#define CACHE_LOGS_DIR "/cache/"		// For devices with a dedicated cache partition
-#define DATA_LOGS_DIR "/data/"			// For devices that do not have a dedicated cache partition
+#define CACHE_LOGS_DIR "/cache/" // For devices with a dedicated cache partition
+#define DATA_LOGS_DIR "/data/"	 // For devices that do not have a dedicated cache partition
 
 typedef enum
 {
@@ -87,12 +87,13 @@
 	static int tw_reboot(RebootCommand command);                                // Prepares the device for rebooting
 	static void check_and_run_script(const char* script_file, const char* display_name); // checks for the existence of a script, chmods it to 755, then runs it
 	static int removeDir(const string path, bool removeParent); //recursively remove a directory
-	static int copy_file(string src, string dst, int mode); //copy file from src to dst with mode permissions
+	static int copy_file(string src, string dst, int mode, bool mount_paths=true); //copy file from src to dst with mode permissions
 	static unsigned int Get_D_Type_From_Stat(string Path);                      // Returns a dirent dt_type value using stat instead of dirent
 	static int read_file(string fn, vector<string>& results); //read from file
 	static int read_file(string fn, string& results); //read from file
 	static int read_file(string fn, uint64_t& results); //read from file
-	static int write_to_file(const string& fn, const string& line);             //write to file
+	static bool write_to_file(const string& fn, const string& line);              //write single line to file with no newline
+	static bool write_to_file(const string& fn, const std::vector<string> lines); // write vector of strings line by line with newlines
 	static bool Try_Decrypting_Backup(string Restore_Path, string Password); // true for success, false for failed to decrypt
 	static string System_Property_Get(string Prop_Name);                // Returns value of Prop_Name from reading /system/build.prop
 	static string System_Property_Get(string Prop_Name, TWPartitionManager &PartitionManager, string Mount_Point);                // Returns value of Prop_Name from reading /system/build.prop
@@ -125,9 +126,9 @@
 	static bool Set_Encryption_Policy(std::string path, struct fscrypt_policy_v2 &policy); // set encryption policy for path
 #endif
 #endif
-	static void List_Mounts();
-	static void Clear_Bootloader_Message();
-	static string Check_For_TwrpFolder();
+	static void List_Mounts(); // List current mounts by the kernel
+	static void Clear_Bootloader_Message(); // Removes the bootloader message from misc for next boot
+	static string Check_For_TwrpFolder(); // Gets user defined path on storage where backups should be stored
 
 private:
 	static void Copy_Log(string Source, string Destination);
diff --git a/twrp.cpp b/twrp.cpp
index 6de0e62..1b0e337 100644
--- a/twrp.cpp
+++ b/twrp.cpp
@@ -44,6 +44,7 @@
 #include "twcommon.h"
 #include "twrp-functions.hpp"
 #include "data.hpp"
+#include "kernel_module_loader.hpp"
 #include "partitions.hpp"
 #ifdef __ANDROID_API_N__
 #include <android-base/strings.h>
@@ -101,9 +102,39 @@
 	}
 }
 
+static void process_fastbootd_mode() {
+		LOGINFO("starting fastboot\n");
+		printf("=> Linking mtab\n");
+		symlink("/proc/mounts", "/etc/mtab");
+		std::string fstab_filename = "/etc/twrp.fstab";
+		if (!TWFunc::Path_Exists(fstab_filename)) {
+			fstab_filename = "/etc/recovery.fstab";
+		}
+		printf("=> Processing %s\n", fstab_filename.c_str());
+		if (!PartitionManager.Process_Fstab(fstab_filename, 1, false)) {
+			LOGERR("Failing out of recovery due to problem with fstab.\n");
+			return;
+		}
+		TWPartition* ven = PartitionManager.Find_Partition_By_Path("/vendor");
+		PartitionManager.Setup_Super_Devices();
+		PartitionManager.Prepare_Super_Volume(ven);
+#ifdef TW_LOAD_VENDOR_MODULES
+		KernelModuleLoader::Load_Vendor_Modules(FASTBOOTD_MODE);
+#endif
+		gui_msg(Msg("fastboot_console_msg=Entered Fastboot mode..."));
+		if (gui_startPage("fastboot", 1, 1) != 0) {
+			LOGERR("Failed to start fastbootd page.\n");
+		}
+}
+
 static void process_recovery_mode(twrpAdbBuFifo* adb_bu_fifo, bool skip_decryption) {
 	char crash_prop_val[PROPERTY_VALUE_MAX];
 	int crash_counter;
+	std::string cmdline;
+	if (TWFunc::read_file("/proc/cmdline", cmdline) != 0) {
+		LOGINFO("Unable to read cmdline for fastboot mode\n");
+	}
+
 	property_get("twrp.crash_counter", crash_prop_val, "-1");
 	crash_counter = atoi(crash_prop_val) + 1;
 	snprintf(crash_prop_val, sizeof(crash_prop_val), "%d", crash_counter);
@@ -123,11 +154,19 @@
 		fstab_filename = "/etc/recovery.fstab";
 	}
 	printf("=> Processing %s\n", fstab_filename.c_str());
-	if (!PartitionManager.Process_Fstab(fstab_filename, 1)) {
+	if (!PartitionManager.Process_Fstab(fstab_filename, 1, true)) {
 		LOGERR("Failing out of recovery due to problem with fstab.\n");
 		return;
 	}
 
+#ifdef TW_LOAD_VENDOR_MODULES
+	bool fastboot_mode = cmdline.find("twrpfastboot=1") != std::string::npos;
+	if (fastboot_mode)
+		KernelModuleLoader::Load_Vendor_Modules(RECOVERY_FASTBOOT_MODE);
+	else
+		KernelModuleLoader::Load_Vendor_Modules(RECOVERY_IN_BOOT_MODE);
+#endif
+
 // We are doing this here to allow super partition to be set up prior to overriding properties
 #if defined(TW_INCLUDE_LIBRESETPROP) && defined(TW_OVERRIDE_SYSTEM_PROPS)
 	if (!PartitionManager.Mount_By_Path(PartitionManager.Get_Android_Root_Path(), true)) {
@@ -340,14 +379,9 @@
 	TWFunc::Clear_Bootloader_Message();
 
 	if (startup.Get_Fastboot_Mode()) {
-		LOGINFO("starting fastboot\n");
-		gui_msg(Msg("fastboot_console_msg=Entered Fastboot mode..."));
-		if (gui_startPage("fastboot", 1, 1) != 0) {
-			LOGERR("Failed to start fastbootd page.\n");
-		}
+		process_fastbootd_mode();
 		delete adb_bu_fifo;
 		TWFunc::Update_Intent_File(startup.Get_Intent());
-
 		reboot();
 		return 0;
 	} else {
@@ -363,7 +397,6 @@
 	gui_start();
 	delete adb_bu_fifo;
 	TWFunc::Update_Intent_File(startup.Get_Intent());
-
 	reboot();
 
 	return 0;
diff --git a/twrpDigestDriver.cpp b/twrpDigestDriver.cpp
index af31417..3e21d06 100755
--- a/twrpDigestDriver.cpp
+++ b/twrpDigestDriver.cpp
@@ -168,7 +168,7 @@
 	digest_str = digest_str + "  " + TWFunc::Get_Filename(Full_Filename) + "\n";
 	LOGINFO("digest_filename: %s\n", digest_filename.c_str());
 
-	if (TWFunc::write_to_file(digest_filename, digest_str) == 0) {
+	if (TWFunc::write_to_file(digest_filename, digest_str)) {
 		tw_set_default_metadata(digest_filename.c_str());
 		gui_msg("digest_created= * Digest Created.");
 	}