recovery: Implement a volume manager
This is a copy of the pre-binderized vold which has been converted to
use direct calls instead of sockets and stripped down to only what is
needed to support recovery.
Includes:
* recovery: volmgr: remove unused IsSupported
Change-Id: If8206658fdfb6108221806c09c99bf0a30f4a586
Signed-off-by: Jesse Chan <jc@lineageos.org>
* recovery: volmgr: remove filesystem checks
Those checks are not strictly necessary and we are
not building fsck tools for recovery for now.
Remove those checks so volmgr can be useful.
Change-Id: I87756c61b933b6cdccd281c6276b686fbd36019f
Signed-off-by: Jesse Chan <jc@lineageos.org>
* recovery: fixup `EmulatedVolume creating`
Avoid dangling pointer. Instead of pointing to FstabEntry create copy.
Change-Id: I57f76006db09a6add2c173f43175f0f6b848d87b
* recovery: fix volmgr cleaning up
Don't reset pointer to netlink manager
Delete disks in stop() rather then in ~VolumeManager
Call destroy() before deleting disks cause delete expects the
disk to be destroyed
Clear the lists or we would read garbage data on the next scan
Change-Id: Idadfa1f33b7cb5f2f3c780848a99344a6608420e
* recovery: handle interrupts in apply update menu
Change-Id: I1f78f9196634353b77986545332d7d52a5f0c161
Change-Id: Ic82d929e052b5ba70ecf7b475e0a223d77d9687e
recovery: fix applying updates
We need to break after applying from either ADB or Storage to return
the status of the installation.
Otherwise the log is stuck until you press on the back arrow, you
are able to start sideload multiple times and other shenanigans.
Change-Id: I06c9dcf6161dd1a0199417cfed2465914837d795
Signed-off-by: Alexander Martinz <amartinz@shiftphones.com>
diff --git a/Android.bp b/Android.bp
index cd20870..10474fa 100644
--- a/Android.bp
+++ b/Android.bp
@@ -106,6 +106,7 @@
"liblog",
"libprotobuf-cpp-lite",
"libziparchive",
+ "libvolume_manager",
],
static_libs: [
@@ -160,6 +161,7 @@
srcs: [
"recovery_main.cpp",
+ "volclient.cpp",
],
shared_libs: [
diff --git a/etc/init.rc b/etc/init.rc
index b0d31fd..1628293 100644
--- a/etc/init.rc
+++ b/etc/init.rc
@@ -37,6 +37,17 @@
chown root shell /tmp
chmod 0775 /tmp
+ mkdir /storage 0050 root sdcard_r
+ mount tmpfs tmpfs /storage mode=0050,uid=0,gid=1028
+
+ mkdir /mnt 0775 root system
+
+ # See storage config details at http://source.android.com/tech/storage/
+ mkdir /mnt/shell 0700 shell shell
+
+ # Directory for staging bindmounts
+ mkdir /mnt/staging 0700 root root
+
write /proc/sys/kernel/panic_on_oops 1
write /proc/sys/vm/max_map_count 1000000
diff --git a/install/Android.bp b/install/Android.bp
index c591714..b52ac40 100644
--- a/install/Android.bp
+++ b/install/Android.bp
@@ -119,6 +119,7 @@
shared_libs: [
"librecovery_ui",
+ "libvolume_manager",
],
export_include_dirs: [
diff --git a/install/fuse_install.cpp b/install/fuse_install.cpp
index e481f91..727419d 100644
--- a/install/fuse_install.cpp
+++ b/install/fuse_install.cpp
@@ -39,7 +39,9 @@
#include "install/install.h"
#include "recovery_utils/roots.h"
-static constexpr const char* SDCARD_ROOT = "/data/media/0";
+using android::volmgr::VolumeInfo;
+using android::volmgr::VolumeManager;
+
// How long (in seconds) we wait for the fuse-provided package file to
// appear, before timing out.
static constexpr int SDCARD_INSTALL_TIMEOUT = 10;
@@ -56,8 +58,6 @@
// Returns the selected filename, or an empty string.
static std::string BrowseDirectory(const std::string& path, Device* device, RecoveryUI* ui) {
- ensure_path_mounted(path);
-
std::unique_ptr<DIR, decltype(&closedir)> d(opendir(path.c_str()), closedir);
if (!d) {
PLOG(ERROR) << "error opening " << path;
@@ -140,12 +140,6 @@
return false;
}
- if (android::base::StartsWith(path, SDCARD_ROOT)) {
- // The installation process expects to find the sdcard unmounted. Unmount it with MNT_DETACH so
- // that our open file continues to work but new references see it as unmounted.
- umount2("/data", MNT_DETACH);
- }
-
return run_fuse_sideload(std::move(fuse_data_provider)) == 0;
}
@@ -208,17 +202,15 @@
return result;
}
-InstallResult ApplyFromSdcard(Device* device) {
+InstallResult ApplyFromStorage(Device* device, VolumeInfo& vi) {
auto ui = device->GetUI();
- if (ensure_path_mounted(SDCARD_ROOT) != 0) {
- LOG(ERROR) << "\n-- Couldn't mount " << SDCARD_ROOT << ".\n";
+ if (!VolumeManager::Instance()->volumeMount(vi.mId)) {
return INSTALL_NONE;
}
- std::string path = BrowseDirectory(SDCARD_ROOT, device, ui);
+ std::string path = BrowseDirectory(vi.mPath, device, ui);
if (path.empty()) {
- LOG(ERROR) << "\n-- No package file selected.\n";
- ensure_path_unmounted(SDCARD_ROOT);
+ VolumeManager::Instance()->volumeUnmount(vi.mId);
return INSTALL_NONE;
}
@@ -231,6 +223,7 @@
SetSdcardUpdateBootloaderMessage();
auto result = InstallWithFuseFromPath(path, device);
- ensure_path_unmounted(SDCARD_ROOT);
+
+ VolumeManager::Instance()->volumeUnmount(vi.mId);
return result;
}
diff --git a/install/include/install/fuse_install.h b/install/include/install/fuse_install.h
index 29c283f..7c38d0d 100644
--- a/install/include/install/fuse_install.h
+++ b/install/include/install/fuse_install.h
@@ -22,9 +22,13 @@
#include "recovery_ui/device.h"
#include "recovery_ui/ui.h"
+#include <volume_manager/VolumeManager.h>
+
+using android::volmgr::VolumeInfo;
+
// Starts FUSE with the package from |path| as the data source. And installs the package from
// |FUSE_SIDELOAD_HOST_PATHNAME|. The |path| can point to the location of a package zip file or a
// block map file with the prefix '@'; e.g. /sdcard/package.zip, @/cache/recovery/block.map.
InstallResult InstallWithFuseFromPath(std::string_view path, Device* device);
-InstallResult ApplyFromSdcard(Device* device);
+InstallResult ApplyFromStorage(Device* device, VolumeInfo& vi);
diff --git a/recovery.cpp b/recovery.cpp
index 3497302..9a17949 100644
--- a/recovery.cpp
+++ b/recovery.cpp
@@ -39,6 +39,7 @@
#include <android-base/strings.h>
#include <cutils/properties.h> /* for property_list */
#include <fs_mgr/roots.h>
+#include <volume_manager/VolumeManager.h>
#include <ziparchive/zip_archive.h>
#include "bootloader_message/bootloader_message.h"
@@ -57,6 +58,10 @@
#include "recovery_utils/battery_utils.h"
#include "recovery_utils/logging.h"
#include "recovery_utils/roots.h"
+#include "volclient.h"
+
+using android::volmgr::VolumeManager;
+using android::volmgr::VolumeInfo;
static constexpr const char* COMMAND_FILE = "/cache/recovery/command";
static constexpr const char* LAST_KMSG_FILE = "/cache/recovery/last_kmsg";
@@ -193,6 +198,48 @@
return (chosen_item == 1);
}
+static InstallResult apply_update_menu(Device* device, Device::BuiltinAction* reboot_action){
+ RecoveryUI* ui = device->GetUI();
+ std::vector<std::string> headers{ "Apply update" };
+ std::vector<std::string> items;
+
+ const int item_sideload = 0;
+ std::vector<VolumeInfo> volumes;
+
+ InstallResult status = INSTALL_NONE;
+
+ for (;;) {
+ items.clear();
+ items.push_back("Apply from ADB");
+ VolumeManager::Instance()->getVolumeInfo(volumes);
+ for (auto& vitr : volumes) {
+ items.push_back("Choose from " + vitr.mLabel);
+ }
+
+ int chosen = ui->ShowMenu(
+ headers, items, 0, false,
+ std::bind(&Device::HandleMenuKey, device, std::placeholders::_1, std::placeholders::_2),
+ true /* refreshable */);
+ if (chosen == Device::kRefresh) {
+ continue;
+ }
+ if (chosen == Device::kGoBack) {
+ break;
+ }
+ if (chosen == static_cast<size_t>(RecoveryUI::KeyError::INTERRUPTED)) {
+ return INSTALL_KEY_INTERRUPTED;
+ }
+
+ if (chosen == item_sideload) {
+ status = ApplyFromAdb(device, false /* rescue_mode */, reboot_action);
+ } else {
+ status = ApplyFromStorage(device, volumes[chosen - 1]);
+ }
+ break;
+ }
+ return status;
+}
+
static InstallResult prompt_and_wipe_data(Device* device) {
// Use a single string and let ScreenRecoveryUI handles the wrapping.
std::vector<std::string> wipe_data_menu_headers{
@@ -423,7 +470,6 @@
switch (chosen_action) {
case Device::MENU_BASE:
- case Device::MENU_UPDATE:
case Device::MENU_WIPE:
case Device::MENU_ADVANCED:
goto change_menu;
@@ -488,28 +534,22 @@
break;
}
- case Device::APPLY_ADB_SIDELOAD:
- case Device::APPLY_SDCARD:
+ case Device::APPLY_UPDATE:
case Device::ENTER_RESCUE: {
save_current_log = true;
update_in_progress = true;
WriteUpdateInProgress();
- bool adb = true;
Device::BuiltinAction reboot_action{};
if (chosen_action == Device::ENTER_RESCUE) {
// Switch to graphics screen.
ui->ShowText(false);
status = ApplyFromAdb(device, true /* rescue_mode */, &reboot_action);
- } else if (chosen_action == Device::APPLY_ADB_SIDELOAD) {
- status = ApplyFromAdb(device, false /* rescue_mode */, &reboot_action);
- } else {
- adb = false;
- status = ApplyFromSdcard(device);
+ } else if (chosen_action == Device::APPLY_UPDATE) {
+ status = apply_update_menu(device, &reboot_action);
}
- ui->Print("\nInstall from %s completed with status %d.\n", adb ? "ADB" : "SD card", status);
if (status == INSTALL_REBOOT) {
return reboot_action;
}
@@ -517,6 +557,7 @@
update_in_progress = false;
}
+ ui->Print("\nInstall completed with status %d.\n", status);
if (status == INSTALL_SUCCESS) {
update_in_progress = false;
if (!ui->IsTextVisible()) {
@@ -743,6 +784,12 @@
auto ui = device->GetUI();
+ VolumeClient* volclient = new VolumeClient(device);
+ VolumeManager* volmgr = VolumeManager::Instance();
+ if (!volmgr->start(volclient)) {
+ printf("Failed to start volume manager\n");
+ }
+
// Set background string to "installing security update" for security update,
// otherwise set it to "installing system update".
ui->SetSystemUpdateText(security_update);
@@ -930,5 +977,11 @@
// Save logs and clean up before rebooting or shutting down.
FinishRecovery(ui);
+ volmgr->unmountAll();
+ volmgr->stop();
+ delete volclient;
+
+ sync();
+
return next_action;
}
diff --git a/recovery_ui/Android.bp b/recovery_ui/Android.bp
index f64b0d1..454947b 100644
--- a/recovery_ui/Android.bp
+++ b/recovery_ui/Android.bp
@@ -51,6 +51,7 @@
"libbase",
"libpng",
"libz",
+ "libvolume_manager",
],
}
diff --git a/recovery_ui/device.cpp b/recovery_ui/device.cpp
index 4408c9f..34c4ce9 100644
--- a/recovery_ui/device.cpp
+++ b/recovery_ui/device.cpp
@@ -32,7 +32,7 @@
static std::vector<std::string> g_main_header{};
static std::vector<menu_action_t> g_main_actions{
{ "Reboot system now", Device::REBOOT },
- { "Apply update", Device::MENU_UPDATE },
+ { "Apply update", Device::APPLY_UPDATE },
{ "Factory reset", Device::MENU_WIPE },
{ "Advanced", Device::MENU_ADVANCED },
};
@@ -58,12 +58,6 @@
{ "Format system partition", Device::WIPE_SYSTEM },
};
-static std::vector<std::string> g_update_header{ "Apply update" };
-static std::vector<menu_action_t> g_update_actions{
- { "Apply from ADB", Device::APPLY_ADB_SIDELOAD },
- { "Choose from internal storage", Device::APPLY_SDCARD },
-};
-
static std::vector<menu_action_t>* current_menu_ = &g_main_actions;
static std::vector<std::string> g_menu_items;
@@ -91,7 +85,6 @@
}
void Device::RemoveMenuItemForAction(Device::BuiltinAction action) {
- ::RemoveMenuItemForAction(g_update_actions, action);
::RemoveMenuItemForAction(g_wipe_actions, action);
::RemoveMenuItemForAction(g_advanced_actions, action);
}
@@ -101,11 +94,9 @@
}
const std::vector<std::string>& Device::GetMenuHeaders() {
- if (current_menu_ == &g_update_actions)
- return g_update_header;
- else if (current_menu_ == &g_wipe_actions)
+ if (current_menu_ == &g_wipe_actions)
return g_wipe_header;
- else if (current_menu_ == &g_advanced_actions)
+ if (current_menu_ == &g_advanced_actions)
return g_advanced_header;
return g_main_header;
}
@@ -115,9 +106,6 @@
if (action > MENU_BASE) {
switch (action) {
- case Device::BuiltinAction::MENU_UPDATE:
- current_menu_ = &g_update_actions;
- break;
case Device::BuiltinAction::MENU_WIPE:
current_menu_ = &g_wipe_actions;
break;
@@ -171,6 +159,9 @@
case KEY_AGAIN:
return kDoSideload;
+ case KEY_REFRESH:
+ return kRefresh;
+
default:
// If you have all of the above buttons, any other buttons
// are ignored. Otherwise, any button cycles the highlight.
diff --git a/recovery_ui/include/recovery_ui/device.h b/recovery_ui/include/recovery_ui/device.h
index d95d6fd..c2fc38d 100644
--- a/recovery_ui/include/recovery_ui/device.h
+++ b/recovery_ui/include/recovery_ui/device.h
@@ -25,8 +25,7 @@
#include <string>
#include <vector>
-// Forward declaration to avoid including "ui.h".
-class RecoveryUI;
+#include "ui.h"
class BootState;
@@ -41,6 +40,7 @@
static constexpr const int kDoSideload = -7;
static constexpr const int kScrollUp = -8;
static constexpr const int kScrollDown = -9;
+ static constexpr const int kRefresh = -10;
// ENTER vs REBOOT: The latter will trigger a reboot that goes through bootloader, which allows
// using a new bootloader / recovery image if applicable. For example, REBOOT_RESCUE goes from
@@ -49,9 +49,9 @@
enum BuiltinAction {
NO_ACTION = 0,
REBOOT = 1,
- APPLY_SDCARD = 2,
+ APPLY_UPDATE = 2,
// APPLY_CACHE was 3.
- APPLY_ADB_SIDELOAD = 4,
+ // APPLY_ADB_SIDELOAD was 4.
WIPE_DATA = 5,
WIPE_CACHE = 6,
REBOOT_BOOTLOADER = 7,
@@ -72,7 +72,6 @@
WIPE_SYSTEM = 100,
ENABLE_ADB = 101,
MENU_BASE = 200,
- MENU_UPDATE = 201,
MENU_WIPE = 202,
MENU_ADVANCED = 203,
};
@@ -165,6 +164,10 @@
std::optional<std::string> GetReason() const;
std::optional<std::string> GetStage() const;
+ virtual void handleVolumeChanged() {
+ ui_->onVolumeChanged();
+ }
+
private:
// The RecoveryUI object that should be used to display the user interface for this device.
std::unique_ptr<RecoveryUI> ui_;
diff --git a/recovery_ui/include/recovery_ui/screen_ui.h b/recovery_ui/include/recovery_ui/screen_ui.h
index f48045a..a115592 100644
--- a/recovery_ui/include/recovery_ui/screen_ui.h
+++ b/recovery_ui/include/recovery_ui/screen_ui.h
@@ -313,7 +313,7 @@
// menu display
size_t ShowMenu(const std::vector<std::string>& headers, const std::vector<std::string>& items,
size_t initial_selection, bool menu_only,
- const std::function<int(int, bool)>& key_handler) override;
+ const std::function<int(int, bool)>& key_handler, bool refreshable) override;
void SetTitle(const std::vector<std::string>& lines) override;
void KeyLongPress(int) override;
@@ -379,7 +379,8 @@
// Takes the ownership of |menu| and displays it.
virtual size_t ShowMenu(std::unique_ptr<Menu>&& menu, bool menu_only,
- const std::function<int(int, bool)>& key_handler);
+ const std::function<int(int, bool)>& key_handler,
+ bool refreshable = false);
// Sets the menu highlight to the given index, wrapping if necessary. Returns the actual item
// selected.
diff --git a/recovery_ui/include/recovery_ui/stub_ui.h b/recovery_ui/include/recovery_ui/stub_ui.h
index 49689ba..838f2fa 100644
--- a/recovery_ui/include/recovery_ui/stub_ui.h
+++ b/recovery_ui/include/recovery_ui/stub_ui.h
@@ -64,7 +64,8 @@
size_t ShowMenu(const std::vector<std::string>& /* headers */,
const std::vector<std::string>& /* items */, size_t /* initial_selection */,
bool /* menu_only */,
- const std::function<int(int, bool)>& /* key_handler */) override;
+ const std::function<int(int, bool)>& /* key_handler */,
+ bool /*refreshable*/) override;
size_t ShowPromptWipeDataMenu(const std::vector<std::string>& /* backup_headers */,
const std::vector<std::string>& /* backup_items */,
diff --git a/recovery_ui/include/recovery_ui/ui.h b/recovery_ui/include/recovery_ui/ui.h
index 6e0f89e..c55044d 100644
--- a/recovery_ui/include/recovery_ui/ui.h
+++ b/recovery_ui/include/recovery_ui/ui.h
@@ -255,7 +255,8 @@
// static_cast<size_t>(ERR_KEY_INTERTUPT) if interrupted, such as by InterruptKey().
virtual size_t ShowMenu(const std::vector<std::string>& headers,
const std::vector<std::string>& items, size_t initial_selection,
- bool menu_only, const std::function<int(int, bool)>& key_handler) = 0;
+ bool menu_only, const std::function<int(int, bool)>& key_handler,
+ bool refreshable = false) = 0;
// Displays the localized wipe data menu with pre-generated graphs. If there's an issue
// with the graphs, falls back to use the backup string headers and items instead. The initial
@@ -295,6 +296,11 @@
virtual bool IsUsbConnected();
+ // Notify of volume state change
+ void onVolumeChanged() {
+ EnqueueKey(KEY_REFRESH);
+ }
+
protected:
void EnqueueKey(int key_code);
void EnqueueTouch(const Point& pos);
diff --git a/recovery_ui/screen_ui.cpp b/recovery_ui/screen_ui.cpp
index 43121fd..76e6d76 100644
--- a/recovery_ui/screen_ui.cpp
+++ b/recovery_ui/screen_ui.cpp
@@ -1407,7 +1407,8 @@
}
size_t ScreenRecoveryUI::ShowMenu(std::unique_ptr<Menu>&& menu, bool menu_only,
- const std::function<int(int, bool)>& key_handler) {
+ const std::function<int(int, bool)>& key_handler,
+ bool refreshable) {
// Throw away keys pressed previously, so user doesn't accidentally trigger menu items.
FlushKeys();
@@ -1455,6 +1456,7 @@
bool visible = IsTextVisible();
action = key_handler(evt.key(), visible);
}
+
if (action < 0) {
switch (action) {
case Device::kHighlightUp:
@@ -1487,13 +1489,18 @@
case Device::kDoSideload:
chosen_item = Device::kDoSideload;
break;
+ case Device::kRefresh:
+ if (refreshable) {
+ chosen_item = Device::kRefresh;
+ }
+ break;
}
} else if (!menu_only) {
chosen_item = action;
}
if (chosen_item == Device::kGoBack || chosen_item == Device::kGoHome ||
- chosen_item == Device::kDoSideload) {
+ chosen_item == Device::kDoSideload || chosen_item == Device::kRefresh) {
break;
}
}
@@ -1506,13 +1513,14 @@
size_t ScreenRecoveryUI::ShowMenu(const std::vector<std::string>& headers,
const std::vector<std::string>& items, size_t initial_selection,
bool menu_only,
- const std::function<int(int, bool)>& key_handler) {
+ const std::function<int(int, bool)>& key_handler,
+ bool refreshable) {
auto menu = CreateMenu(headers, items, initial_selection);
if (menu == nullptr) {
return initial_selection;
}
- return ShowMenu(std::move(menu), menu_only, key_handler);
+ return ShowMenu(std::move(menu), menu_only, key_handler, refreshable);
}
size_t ScreenRecoveryUI::ShowPromptWipeDataMenu(const std::vector<std::string>& backup_headers,
diff --git a/recovery_ui/stub_ui.cpp b/recovery_ui/stub_ui.cpp
index 87605cf..cabbd5f 100644
--- a/recovery_ui/stub_ui.cpp
+++ b/recovery_ui/stub_ui.cpp
@@ -23,7 +23,8 @@
size_t StubRecoveryUI::ShowMenu(const std::vector<std::string>& /* headers */,
const std::vector<std::string>& /* items */,
size_t /* initial_selection */, bool /* menu_only */,
- const std::function<int(int, bool)>& /*key_handler*/) {
+ const std::function<int(int, bool)>& /*key_handler*/,
+ bool /* refreshable */) {
while (true) {
// Exit the loop in the case of interruption or time out.
InputEvent evt = WaitInputEvent();
diff --git a/recovery_ui/ui.cpp b/recovery_ui/ui.cpp
index 5d407fc..d3323d7 100644
--- a/recovery_ui/ui.cpp
+++ b/recovery_ui/ui.cpp
@@ -36,6 +36,7 @@
#include <android-base/parseint.h>
#include <android-base/properties.h>
#include <android-base/strings.h>
+#include <volume_manager/VolumeManager.h>
#include "minui/minui.h"
#include "otautil/sysutil.h"
@@ -477,6 +478,7 @@
case RecoveryUI::REBOOT:
if (reboot_enabled) {
+ android::volmgr::VolumeManager::Instance()->unmountAll();
Reboot("userrequested,recovery,ui");
}
break;
diff --git a/volclient.cpp b/volclient.cpp
new file mode 100644
index 0000000..b775370
--- /dev/null
+++ b/volclient.cpp
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2019 The LineageOS Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "volclient.h"
+#include <volume_manager/ResponseCode.h>
+
+void VolumeClient::handleEvent(int code, const std::vector<std::string>& argv) {
+ // This client is only interested in volume addition/deletion
+ if (code != ResponseCode::VolumeDestroyed &&
+ code != ResponseCode::DiskScanned)
+ return;
+
+ printf("VolumeClient::handleEvent: code=%d, argv=<", code);
+ for (auto& arg : argv) {
+ printf("%s,", arg.c_str());
+ }
+ printf(">\n");
+
+ mDevice->handleVolumeChanged();
+}
diff --git a/volclient.h b/volclient.h
new file mode 100644
index 0000000..e534068
--- /dev/null
+++ b/volclient.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2019 The LineageOS Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _RECOVERY_VOLCLIENT_H
+#define _RECOVERY_VOLCLIENT_H
+
+#include <volume_manager/VolumeManager.h>
+#include "recovery_ui/device.h"
+
+class VolumeClient : public VolumeWatcher {
+ public:
+ VolumeClient(Device* device) : mDevice(device) {}
+ virtual ~VolumeClient(void) {}
+ virtual void handleEvent(int code, const std::vector<std::string>& argv);
+
+ private:
+ Device* mDevice;
+};
+
+#endif
diff --git a/volume_manager/.clang-format b/volume_manager/.clang-format
new file mode 100644
index 0000000..ae4a451
--- /dev/null
+++ b/volume_manager/.clang-format
@@ -0,0 +1,11 @@
+BasedOnStyle: Google
+AccessModifierOffset: -2
+AllowShortFunctionsOnASingleLine: Inline
+ColumnLimit: 100
+CommentPragmas: NOLINT:.*
+DerivePointerAlignment: false
+IndentWidth: 4
+PointerAlignment: Left
+TabWidth: 4
+UseTab: Never
+PenaltyExcessCharacter: 32
diff --git a/volume_manager/Android.bp b/volume_manager/Android.bp
new file mode 100644
index 0000000..e386721
--- /dev/null
+++ b/volume_manager/Android.bp
@@ -0,0 +1,59 @@
+//
+// Copyright (C) 2019 The LineageOS Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_library_shared {
+ name: "libvolume_manager",
+ recovery_available: true,
+ srcs: [
+ "Disk.cpp",
+ "DiskPartition.cpp",
+ "EmulatedVolume.cpp",
+ "NetlinkHandler.cpp",
+ "NetlinkManager.cpp",
+ "Process.cpp",
+ "PublicVolume.cpp",
+ "Utils.cpp",
+ "VolumeBase.cpp",
+ "VolumeManager.cpp",
+ "fs/Exfat.cpp",
+ "fs/Ext4.cpp",
+ "fs/F2fs.cpp",
+ "fs/Ntfs.cpp",
+ "fs/Vfat.cpp",
+ ],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+ include_dirs: [
+ "external/gptfdisk",
+ ],
+ shared_libs: [
+ "libbase",
+ "libcutils",
+ "liblog",
+ "libselinux",
+ "libsgdisk",
+ "libsysutils",
+ ],
+ static_libs: [
+ "libfs_mgr",
+ "libfstab",
+ "libext2_blkid",
+ "libext2_uuid",
+ ],
+ export_include_dirs: ["include"],
+}
diff --git a/volume_manager/Disk.cpp b/volume_manager/Disk.cpp
new file mode 100644
index 0000000..79a0ddc
--- /dev/null
+++ b/volume_manager/Disk.cpp
@@ -0,0 +1,423 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2019 The LineageOS Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Disk.h"
+#include "PublicVolume.h"
+#include <volume_manager/ResponseCode.h>
+#include <volume_manager/VolumeManager.h>
+#include "Utils.h"
+#include "VolumeBase.h"
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+
+#include <sgdisk.h>
+
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/sysmacros.h>
+#include <sys/types.h>
+#include <vector>
+
+using android::base::ReadFileToString;
+using android::base::WriteStringToFile;
+using android::base::StringPrintf;
+
+namespace android {
+namespace volmgr {
+
+static const char* kSysfsLoopMaxMinors = "/sys/module/loop/parameters/max_part";
+static const char* kSysfsMmcMaxMinorsDeprecated = "/sys/module/mmcblk/parameters/perdev_minors";
+static const char* kSysfsMmcMaxMinors = "/sys/module/mmc_block/parameters/perdev_minors";
+
+static const unsigned int kMajorBlockLoop = 7;
+static const unsigned int kMajorBlockScsiA = 8;
+static const unsigned int kMajorBlockScsiB = 65;
+static const unsigned int kMajorBlockScsiC = 66;
+static const unsigned int kMajorBlockScsiD = 67;
+static const unsigned int kMajorBlockScsiE = 68;
+static const unsigned int kMajorBlockScsiF = 69;
+static const unsigned int kMajorBlockScsiG = 70;
+static const unsigned int kMajorBlockScsiH = 71;
+static const unsigned int kMajorBlockScsiI = 128;
+static const unsigned int kMajorBlockScsiJ = 129;
+static const unsigned int kMajorBlockScsiK = 130;
+static const unsigned int kMajorBlockScsiL = 131;
+static const unsigned int kMajorBlockScsiM = 132;
+static const unsigned int kMajorBlockScsiN = 133;
+static const unsigned int kMajorBlockScsiO = 134;
+static const unsigned int kMajorBlockScsiP = 135;
+static const unsigned int kMajorBlockMmc = 179;
+static const unsigned int kMajorBlockExperimentalMin = 240;
+static const unsigned int kMajorBlockExperimentalMax = 254;
+
+static const char* kGptBasicData = "EBD0A0A2-B9E5-4433-87C0-68B6B72699C7";
+static const char* kGptLinuxFilesystem = "0FC63DAF-8483-4772-8E79-3D69D8477DE4";
+
+enum class Table {
+ kUnknown,
+ kMbr,
+ kGpt,
+};
+
+static bool isVirtioBlkDevice(unsigned int major) {
+ /*
+ * The new emulator's "ranchu" virtual board no longer includes a goldfish
+ * MMC-based SD card device; instead, it emulates SD cards with virtio-blk,
+ * which has been supported by upstream kernel and QEMU for quite a while.
+ * Unfortunately, the virtio-blk block device driver does not use a fixed
+ * major number, but relies on the kernel to assign one from a specific
+ * range of block majors, which are allocated for "LOCAL/EXPERIMENAL USE"
+ * per Documentation/devices.txt. This is true even for the latest Linux
+ * kernel (4.4; see init() in drivers/block/virtio_blk.c).
+ *
+ * This makes it difficult for vold to detect a virtio-blk based SD card.
+ * The current solution checks two conditions (both must be met):
+ *
+ * a) If the running environment is the emulator;
+ * b) If the major number is an experimental block device major number (for
+ * x86/x86_64 3.10 ranchu kernels, virtio-blk always gets major number
+ * 253, but it is safer to match the range than just one value).
+ *
+ * Other conditions could be used, too, e.g. the hardware name should be
+ * "ranchu", the device's sysfs path should end with "/block/vd[d-z]", etc.
+ * But just having a) and b) is enough for now.
+ */
+ return IsRunningInEmulator() && major >= kMajorBlockExperimentalMin &&
+ major <= kMajorBlockExperimentalMax;
+}
+
+Disk::Disk(const std::string& eventPath, dev_t device, const std::string& nickname, int flags)
+ : mDevice(device),
+ mSize(-1),
+ mNickname(nickname),
+ mFlags(flags),
+ mCreated(false),
+ mSkipChange(false) {
+ mId = StringPrintf("disk:%u_%u", major(device), minor(device));
+ mEventPath = eventPath;
+ mSysPath = StringPrintf("/sys/%s", eventPath.c_str());
+ mDevPath = StringPrintf("/dev/block/volmgr/%s", mId.c_str());
+ CreateDeviceNode(mDevPath, mDevice);
+}
+
+Disk::~Disk() {
+ CHECK(!mCreated);
+ DestroyDeviceNode(mDevPath);
+}
+
+void Disk::getVolumeInfo(std::vector<VolumeInfo>& info) {
+ for (auto vol : mVolumes) {
+ info.push_back(VolumeInfo(vol.get()));
+ }
+}
+
+std::shared_ptr<VolumeBase> Disk::findVolume(const std::string& id) {
+ for (auto vol : mVolumes) {
+ if (vol->getId() == id) {
+ return vol;
+ }
+ }
+ return nullptr;
+}
+
+void Disk::listVolumes(VolumeBase::Type type, std::list<std::string>& list) {
+ for (const auto& vol : mVolumes) {
+ if (vol->getType() == type) {
+ list.push_back(vol->getId());
+ }
+ // TODO: consider looking at stacked volumes
+ }
+}
+
+status_t Disk::create() {
+ CHECK(!mCreated);
+ mCreated = true;
+ VolumeManager::Instance()->notifyEvent(ResponseCode::DiskCreated, StringPrintf("%d", mFlags));
+ readMetadata();
+ readPartitions();
+ return OK;
+}
+
+status_t Disk::destroy() {
+ CHECK(mCreated);
+ destroyAllVolumes();
+ mCreated = false;
+ VolumeManager::Instance()->notifyEvent(ResponseCode::DiskDestroyed);
+ return OK;
+}
+
+void Disk::createPublicVolume(dev_t device, const std::string& fstype /* = "" */,
+ const std::string& mntopts /* = "" */) {
+ auto vol = std::shared_ptr<VolumeBase>(new PublicVolume(device, mNickname, fstype, mntopts));
+
+ mVolumes.push_back(vol);
+ vol->setDiskId(getId());
+ vol->create();
+}
+
+void Disk::destroyAllVolumes() {
+ for (const auto& vol : mVolumes) {
+ vol->destroy();
+ }
+ mVolumes.clear();
+}
+
+status_t Disk::readMetadata() {
+ if (mSkipChange) {
+ return OK;
+ }
+
+ mSize = -1;
+ mLabel.clear();
+
+ int fd = open(mDevPath.c_str(), O_RDONLY | O_CLOEXEC);
+ if (fd != -1) {
+ if (ioctl(fd, BLKGETSIZE64, &mSize)) {
+ mSize = -1;
+ }
+ close(fd);
+ }
+
+ unsigned int majorId = major(mDevice);
+ switch (majorId) {
+ case kMajorBlockLoop: {
+ mLabel = "Virtual";
+ break;
+ }
+ case kMajorBlockScsiA:
+ case kMajorBlockScsiB:
+ case kMajorBlockScsiC:
+ case kMajorBlockScsiD:
+ case kMajorBlockScsiE:
+ case kMajorBlockScsiF:
+ case kMajorBlockScsiG:
+ case kMajorBlockScsiH:
+ case kMajorBlockScsiI:
+ case kMajorBlockScsiJ:
+ case kMajorBlockScsiK:
+ case kMajorBlockScsiL:
+ case kMajorBlockScsiM:
+ case kMajorBlockScsiN:
+ case kMajorBlockScsiO:
+ case kMajorBlockScsiP: {
+ std::string path(mSysPath + "/device/vendor");
+ std::string tmp;
+ if (!ReadFileToString(path, &tmp)) {
+ PLOG(WARNING) << "Failed to read vendor from " << path;
+ return -errno;
+ }
+ mLabel = tmp;
+ break;
+ }
+ case kMajorBlockMmc: {
+ std::string path(mSysPath + "/device/manfid");
+ std::string tmp;
+ if (!ReadFileToString(path, &tmp)) {
+ PLOG(WARNING) << "Failed to read manufacturer from " << path;
+ return -errno;
+ }
+ uint64_t manfid = strtoll(tmp.c_str(), nullptr, 16);
+ // Our goal here is to give the user a meaningful label, ideally
+ // matching whatever is silk-screened on the card. To reduce
+ // user confusion, this list doesn't contain white-label manfid.
+ switch (manfid) {
+ case 0x000003:
+ mLabel = "SanDisk";
+ break;
+ case 0x00001b:
+ mLabel = "Samsung";
+ break;
+ case 0x000028:
+ mLabel = "Lexar";
+ break;
+ case 0x000074:
+ mLabel = "Transcend";
+ break;
+ }
+ break;
+ }
+ default: {
+ if (isVirtioBlkDevice(majorId)) {
+ LOG(DEBUG) << "Recognized experimental block major ID " << majorId
+ << " as virtio-blk (emulator's virtual SD card device)";
+ mLabel = "Virtual";
+ break;
+ }
+ LOG(WARNING) << "Unsupported block major type " << majorId;
+ return -ENOTSUP;
+ }
+ }
+
+ VolumeManager::Instance()->notifyEvent(ResponseCode::DiskSizeChanged,
+ StringPrintf("%" PRIu64, mSize));
+ VolumeManager::Instance()->notifyEvent(ResponseCode::DiskLabelChanged, mLabel);
+ VolumeManager::Instance()->notifyEvent(ResponseCode::DiskSysPathChanged, mSysPath);
+ return OK;
+}
+
+status_t Disk::readPartitions() {
+ int8_t maxMinors = getMaxMinors();
+ if (maxMinors < 0) {
+ return -ENOTSUP;
+ }
+
+ if (mSkipChange) {
+ mSkipChange = false;
+ LOG(INFO) << "Skip first change";
+ return OK;
+ }
+
+ destroyAllVolumes();
+
+ // Parse partition table
+ sgdisk_partition_table ptbl;
+ std::vector<sgdisk_partition> partitions;
+ int res = sgdisk_read(mDevPath.c_str(), ptbl, partitions);
+ if (res != 0) {
+ LOG(WARNING) << "sgdisk failed to scan " << mDevPath;
+ VolumeManager::Instance()->notifyEvent(ResponseCode::DiskScanned);
+ return res;
+ }
+
+ Table table = Table::kUnknown;
+ bool foundParts = false;
+
+ switch (ptbl.type) {
+ case MBR:
+ table = Table::kMbr;
+ break;
+ case GPT:
+ table = Table::kGpt;
+ break;
+ default:
+ table = Table::kUnknown;
+ }
+
+ foundParts = partitions.size() > 0;
+ for (const auto& part : partitions) {
+ if (part.num <= 0 || part.num > maxMinors) {
+ LOG(WARNING) << mId << " is ignoring partition " << part.num
+ << " beyond max supported devices";
+ continue;
+ }
+ dev_t partDevice = makedev(major(mDevice), minor(mDevice) + part.num);
+ if (table == Table::kMbr) {
+ switch (strtol(part.type.c_str(), nullptr, 16)) {
+ case 0x06: // FAT16
+ case 0x07: // NTFS/exFAT
+ case 0x0b: // W95 FAT32 (LBA)
+ case 0x0c: // W95 FAT32 (LBA)
+ case 0x0e: // W95 FAT16 (LBA)
+ case 0x83: // Linux EXT4/F2FS/...
+ createPublicVolume(partDevice);
+ break;
+ }
+ } else if (table == Table::kGpt) {
+ if (!strcasecmp(part.guid.c_str(), kGptBasicData) ||
+ !strcasecmp(part.guid.c_str(), kGptLinuxFilesystem)) {
+ createPublicVolume(partDevice);
+ }
+ }
+ }
+
+ // Ugly last ditch effort, treat entire disk as partition
+ if (table == Table::kUnknown || !foundParts) {
+ LOG(WARNING) << mId << " has unknown partition table; trying entire device";
+
+ std::string fsType;
+ std::string unused;
+ if (ReadMetadataUntrusted(mDevPath, fsType, unused, unused) == OK) {
+ createPublicVolume(mDevice);
+ } else {
+ LOG(WARNING) << mId << " failed to identify, giving up";
+ }
+ }
+
+ VolumeManager::Instance()->notifyEvent(ResponseCode::DiskScanned);
+ return OK;
+}
+
+status_t Disk::unmountAll() {
+ for (const auto& vol : mVolumes) {
+ vol->unmount();
+ }
+ return OK;
+}
+
+int Disk::getMaxMinors() {
+ // Figure out maximum partition devices supported
+ unsigned int majorId = major(mDevice);
+ switch (majorId) {
+ case kMajorBlockLoop: {
+ std::string tmp;
+ if (!ReadFileToString(kSysfsLoopMaxMinors, &tmp)) {
+ LOG(ERROR) << "Failed to read max minors";
+ return -errno;
+ }
+ return atoi(tmp.c_str());
+ }
+ case kMajorBlockScsiA:
+ case kMajorBlockScsiB:
+ case kMajorBlockScsiC:
+ case kMajorBlockScsiD:
+ case kMajorBlockScsiE:
+ case kMajorBlockScsiF:
+ case kMajorBlockScsiG:
+ case kMajorBlockScsiH:
+ case kMajorBlockScsiI:
+ case kMajorBlockScsiJ:
+ case kMajorBlockScsiK:
+ case kMajorBlockScsiL:
+ case kMajorBlockScsiM:
+ case kMajorBlockScsiN:
+ case kMajorBlockScsiO:
+ case kMajorBlockScsiP: {
+ // Per Documentation/devices.txt this is static
+ return 15;
+ }
+ case kMajorBlockMmc: {
+ // Per Documentation/devices.txt this is dynamic
+ std::string tmp;
+ if (!ReadFileToString(kSysfsMmcMaxMinors, &tmp) &&
+ !ReadFileToString(kSysfsMmcMaxMinorsDeprecated, &tmp)) {
+ LOG(ERROR) << "Failed to read max minors";
+ return -errno;
+ }
+ return atoi(tmp.c_str());
+ }
+ default: {
+ if (isVirtioBlkDevice(majorId)) {
+ // drivers/block/virtio_blk.c has "#define PART_BITS 4", so max is
+ // 2^4 - 1 = 15
+ return 15;
+ }
+ }
+ }
+
+ LOG(ERROR) << "Unsupported block major type " << majorId;
+ return -ENOTSUP;
+}
+
+} // namespace volmgr
+} // namespace android
diff --git a/volume_manager/Disk.h b/volume_manager/Disk.h
new file mode 100644
index 0000000..af86f6d
--- /dev/null
+++ b/volume_manager/Disk.h
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2019 The LineageOS Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_VOLMGR_DISK_H
+#define ANDROID_VOLMGR_DISK_H
+
+#include "Utils.h"
+#include "VolumeBase.h"
+
+#include <utils/Errors.h>
+
+#include <vector>
+
+#include <volume_manager/VolumeManager.h>
+
+namespace android {
+namespace volmgr {
+
+class VolumeBase;
+
+/*
+ * Representation of detected physical media.
+ *
+ * Knows how to create volumes based on the partition tables found, and also
+ * how to repartition itself.
+ */
+class Disk {
+ public:
+ Disk(const std::string& eventPath, dev_t device, const std::string& nickname, int flags);
+ virtual ~Disk();
+
+ enum Flags {
+ /* Flag that disk is adoptable */
+ kAdoptable = 1 << 0,
+ /* Flag that disk is considered primary when the user hasn't
+ * explicitly picked a primary storage location */
+ kDefaultPrimary = 1 << 1,
+ /* Flag that disk is SD card */
+ kSd = 1 << 2,
+ /* Flag that disk is USB disk */
+ kUsb = 1 << 3,
+ /* Flag that disk is EMMC internal */
+ kEmmc = 1 << 4,
+ /* Flag that disk is non-removable */
+ kNonRemovable = 1 << 5,
+ };
+
+ const std::string& getId() { return mId; }
+ const std::string& getEventPath() { return mEventPath; }
+ const std::string& getSysPath() { return mSysPath; }
+ const std::string& getDevPath() { return mDevPath; }
+ dev_t getDevice() { return mDevice; }
+ uint64_t getSize() { return mSize; }
+ const std::string& getLabel() { return mLabel; }
+ int getFlags() { return mFlags; }
+
+ void getVolumeInfo(std::vector<VolumeInfo>& info);
+
+ std::shared_ptr<VolumeBase> findVolume(const std::string& id);
+
+ void listVolumes(VolumeBase::Type type, std::list<std::string>& list);
+
+ virtual status_t create();
+ virtual status_t destroy();
+
+ virtual status_t readMetadata();
+ virtual status_t readPartitions();
+
+ status_t unmountAll();
+
+ protected:
+ /* ID that uniquely references this disk */
+ std::string mId;
+ /* Original event path */
+ std::string mEventPath;
+ /* Device path under sysfs */
+ std::string mSysPath;
+ /* Device path under dev */
+ std::string mDevPath;
+ /* Kernel device representing disk */
+ dev_t mDevice;
+ /* Size of disk, in bytes */
+ uint64_t mSize;
+ /* User-visible label, such as manufacturer */
+ std::string mLabel;
+ /* Current partitions on disk */
+ std::vector<std::shared_ptr<VolumeBase>> mVolumes;
+ /* Nickname for this disk */
+ std::string mNickname;
+ /* Flags applicable to this disk */
+ int mFlags;
+ /* Flag indicating object is created */
+ bool mCreated;
+ /* Flag that we need to skip first disk change events after partitioning*/
+ bool mSkipChange;
+
+ void createPublicVolume(dev_t device, const std::string& fstype = "",
+ const std::string& mntopts = "");
+
+ void destroyAllVolumes();
+
+ int getMaxMinors();
+
+ DISALLOW_COPY_AND_ASSIGN(Disk);
+};
+
+} // namespace volmgr
+} // namespace android
+
+#endif
diff --git a/volume_manager/DiskPartition.cpp b/volume_manager/DiskPartition.cpp
new file mode 100644
index 0000000..26ac6f5
--- /dev/null
+++ b/volume_manager/DiskPartition.cpp
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2015 Cyanogen, Inc.
+ * Copyright (C) 2019 The LineageOS Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "DiskPartition.h"
+#include "PublicVolume.h"
+#include <volume_manager/ResponseCode.h>
+#include <volume_manager/VolumeManager.h>
+#include "Utils.h"
+#include "VolumeBase.h"
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/sysmacros.h>
+#include <sys/types.h>
+#include <vector>
+
+using android::base::ReadFileToString;
+using android::base::WriteStringToFile;
+using android::base::StringPrintf;
+
+namespace android {
+namespace volmgr {
+
+DiskPartition::DiskPartition(const std::string& eventPath, dev_t device, const std::string& nickname,
+ int flags, int partnum, const std::string& fstype /* = "" */,
+ const std::string& mntopts /* = "" */)
+ : Disk(eventPath, device, nickname, flags),
+ mPartNum(partnum),
+ mFsType(fstype),
+ mMntOpts(mntopts) {
+ // Empty
+}
+
+DiskPartition::~DiskPartition() {
+ // Empty
+}
+
+status_t DiskPartition::create() {
+ CHECK(!mCreated);
+ mCreated = true;
+ VolumeManager::Instance()->notifyEvent(ResponseCode::DiskCreated, StringPrintf("%d", mFlags));
+ dev_t partDevice = makedev(major(mDevice), minor(mDevice) + mPartNum);
+ createPublicVolume(partDevice, mFsType, mMntOpts);
+ return OK;
+}
+
+status_t DiskPartition::destroy() {
+ CHECK(mCreated);
+ destroyAllVolumes();
+ mCreated = false;
+ VolumeManager::Instance()->notifyEvent(ResponseCode::DiskDestroyed);
+ return OK;
+}
+
+} // namespace volmgr
+} // namespace android
diff --git a/volume_manager/DiskPartition.h b/volume_manager/DiskPartition.h
new file mode 100644
index 0000000..0f39583
--- /dev/null
+++ b/volume_manager/DiskPartition.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2015 Cyanogen, Inc.
+ * Copyright (C) 2019 The LineageOS Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_VOLMGR_DISKPARTITION_H
+#define ANDROID_VOLMGR_DISKPARTITION_H
+
+#include "Disk.h"
+
+namespace android {
+namespace volmgr {
+
+/*
+ * Representation of a single partition on physical media. Useful for
+ * single media partitions such as "internal" sdcard partitions.
+ */
+
+class DiskPartition : public Disk {
+ public:
+ DiskPartition(const std::string& eventPath, dev_t device, const std::string& nickname, int flags,
+ int partnum, const std::string& fstype = "", const std::string& mntopts = "");
+ virtual ~DiskPartition();
+
+ virtual status_t create();
+ virtual status_t destroy();
+
+ private:
+ /* Partition number */
+ int mPartNum;
+ /* Filesystem type */
+ std::string mFsType;
+ /* Mount options */
+ std::string mMntOpts;
+};
+
+} // namespace volmgr
+} // namespace android
+
+#endif
diff --git a/volume_manager/EmulatedVolume.cpp b/volume_manager/EmulatedVolume.cpp
new file mode 100644
index 0000000..23e6434
--- /dev/null
+++ b/volume_manager/EmulatedVolume.cpp
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2019 The LineageOS Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "EmulatedVolume.h"
+#include <volume_manager/ResponseCode.h>
+#include <volume_manager/VolumeManager.h>
+#include "Utils.h"
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <cutils/fs.h>
+#include <private/android_filesystem_config.h>
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/sysmacros.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+using android::base::StringPrintf;
+
+namespace android {
+namespace volmgr {
+
+static const std::string kStagingPath = "/mnt/staging/emulated";
+
+EmulatedVolume::EmulatedVolume(FstabEntry* rec, const std::string& subdir)
+ : VolumeBase(Type::kEmulated),
+ mSubdir(subdir),
+ mDevPath(rec->blk_device),
+ mFsType(rec->fs_type),
+ mFlags(rec->flags),
+ mFsOptions(rec->fs_options) {
+ setId("emulated");
+ setPartLabel("internal storage");
+ setPath("/storage/emulated");
+}
+
+EmulatedVolume::~EmulatedVolume() {}
+
+status_t EmulatedVolume::doMount() {
+ if (fs_prepare_dir(kStagingPath.c_str(), 0700, AID_ROOT, AID_ROOT)) {
+ PLOG(ERROR) << getId() << " failed to create mount points";
+ return -errno;
+ }
+ if (fs_prepare_dir(getPath().c_str(), 0700, AID_ROOT, AID_ROOT)) {
+ PLOG(ERROR) << getId() << " failed to create mount points";
+ return -errno;
+ }
+
+ std::string bindPath = kStagingPath + "/" + mSubdir;
+
+ if (::mount(mDevPath.c_str(), kStagingPath.c_str(), mFsType.c_str(), mFlags,
+ mFsOptions.c_str()) != 0) {
+ PLOG(ERROR) << getId() << " failed to mount " << mDevPath << " on " << kStagingPath;
+ return -EIO;
+ }
+ if (BindMount(bindPath, getPath()) != OK) {
+ PLOG(ERROR) << getId() << " failed to bind mount " << bindPath << " on " << getPath();
+ ForceUnmount(kStagingPath);
+ return -EIO;
+ }
+
+ return OK;
+}
+
+status_t EmulatedVolume::doUnmount(bool detach /* = false */) {
+ ForceUnmount(getPath(), detach);
+ ForceUnmount(kStagingPath, detach);
+
+ rmdir(getPath().c_str());
+ rmdir(kStagingPath.c_str());
+
+ return OK;
+}
+
+} // namespace volmgr
+} // namespace android
diff --git a/volume_manager/EmulatedVolume.h b/volume_manager/EmulatedVolume.h
new file mode 100644
index 0000000..ac40b6f
--- /dev/null
+++ b/volume_manager/EmulatedVolume.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2019 The LineageOS Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_VOLMGR_EMULATED_VOLUME_H
+#define ANDROID_VOLMGR_EMULATED_VOLUME_H
+
+#include "VolumeBase.h"
+
+#include <cutils/multiuser.h>
+#include <fstab/fstab.h>
+
+using android::fs_mgr::FstabEntry;
+
+namespace android {
+namespace volmgr {
+
+/*
+ * Shared storage emulated on top of private storage.
+ *
+ * Knows how to spawn a FUSE daemon to synthesize permissions. ObbVolume
+ * can be stacked above it.
+ *
+ * This volume is always multi-user aware, but is only binds itself to
+ * users when its primary storage. This volume should never be presented
+ * as secondary storage, since we're strongly encouraging developers to
+ * store data local to their app.
+ */
+class EmulatedVolume : public VolumeBase {
+ public:
+ explicit EmulatedVolume(FstabEntry* rec, const std::string& subdir);
+ virtual ~EmulatedVolume();
+
+ protected:
+ status_t doMount() override;
+ status_t doUnmount(bool detach = false) override;
+
+ private:
+ std::string mSubdir;
+ std::string mDevPath;
+ std::string mFsType;
+ unsigned long mFlags;
+ std::string mFsOptions;
+
+ DISALLOW_COPY_AND_ASSIGN(EmulatedVolume);
+};
+
+} // namespace volmgr
+} // namespace android
+
+#endif
diff --git a/volume_manager/NetlinkHandler.cpp b/volume_manager/NetlinkHandler.cpp
new file mode 100644
index 0000000..1cd469d
--- /dev/null
+++ b/volume_manager/NetlinkHandler.cpp
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2019 The LineageOS Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define LOG_TAG "Vold"
+
+#include <cutils/log.h>
+
+#include <sysutils/NetlinkEvent.h>
+#include <volume_manager/VolumeManager.h>
+#include "NetlinkHandler.h"
+
+NetlinkHandler::NetlinkHandler(int listenerSocket) : NetlinkListener(listenerSocket) {}
+
+NetlinkHandler::~NetlinkHandler() {}
+
+bool NetlinkHandler::start() {
+ return this->startListener() == 0;
+}
+
+void NetlinkHandler::stop() {
+ this->stopListener();
+}
+
+void NetlinkHandler::onEvent(NetlinkEvent* evt) {
+ android::volmgr::VolumeManager* vm = android::volmgr::VolumeManager::Instance();
+ const char* subsys = evt->getSubsystem();
+
+ if (!subsys) {
+ SLOGW("No subsystem found in netlink event");
+ return;
+ }
+
+ if (!strcmp(subsys, "block")) {
+ vm->handleBlockEvent(evt);
+ }
+}
diff --git a/volume_manager/NetlinkHandler.h b/volume_manager/NetlinkHandler.h
new file mode 100644
index 0000000..09f474f
--- /dev/null
+++ b/volume_manager/NetlinkHandler.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2019 The LineageOS Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _NETLINKHANDLER_H
+#define _NETLINKHANDLER_H
+
+#include <sysutils/NetlinkListener.h>
+
+class NetlinkHandler : public NetlinkListener {
+ public:
+ explicit NetlinkHandler(int listenerSocket);
+ virtual ~NetlinkHandler();
+
+ bool start(void);
+ void stop(void);
+
+ protected:
+ virtual void onEvent(NetlinkEvent* evt);
+};
+#endif
diff --git a/volume_manager/NetlinkManager.cpp b/volume_manager/NetlinkManager.cpp
new file mode 100644
index 0000000..28640e9
--- /dev/null
+++ b/volume_manager/NetlinkManager.cpp
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2019 The LineageOS Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <sys/select.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/un.h>
+
+#include <linux/netlink.h>
+
+#define LOG_TAG "Vold"
+
+#include <cutils/log.h>
+
+#include "NetlinkHandler.h"
+#include "NetlinkManager.h"
+
+NetlinkManager* NetlinkManager::sInstance = NULL;
+
+NetlinkManager* NetlinkManager::Instance() {
+ if (!sInstance) sInstance = new NetlinkManager();
+ return sInstance;
+}
+
+NetlinkManager::NetlinkManager() {
+ // Empty
+}
+
+NetlinkManager::~NetlinkManager() {}
+
+bool NetlinkManager::start() {
+ struct sockaddr_nl nladdr;
+ int sz = 64 * 1024;
+ int on = 1;
+
+ memset(&nladdr, 0, sizeof(nladdr));
+ nladdr.nl_family = AF_NETLINK;
+ nladdr.nl_pid = getpid();
+ nladdr.nl_groups = 0xffffffff;
+
+ if ((mSock = socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, NETLINK_KOBJECT_UEVENT)) < 0) {
+ SLOGE("Unable to create uevent socket: %s", strerror(errno));
+ return false;
+ }
+
+ // When running in a net/user namespace, SO_RCVBUFFORCE is not available.
+ // Try using SO_RCVBUF first.
+ if ((setsockopt(mSock, SOL_SOCKET, SO_RCVBUF, &sz, sizeof(sz)) < 0) &&
+ (setsockopt(mSock, SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz)) < 0)) {
+ SLOGE("Unable to set uevent socket SO_RCVBUF/SO_RCVBUFFORCE option: %s", strerror(errno));
+ goto out;
+ }
+
+ if (setsockopt(mSock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)) < 0) {
+ SLOGE("Unable to set uevent socket SO_PASSCRED option: %s", strerror(errno));
+ goto out;
+ }
+
+ if (bind(mSock, (struct sockaddr*)&nladdr, sizeof(nladdr)) < 0) {
+ SLOGE("Unable to bind uevent socket: %s", strerror(errno));
+ goto out;
+ }
+
+ mHandler = new NetlinkHandler(mSock);
+ if (!mHandler->start()) {
+ SLOGE("Unable to start NetlinkHandler: %s", strerror(errno));
+ goto out;
+ }
+
+ return true;
+
+out:
+ close(mSock);
+ return false;
+}
+
+void NetlinkManager::stop() {
+ mHandler->stop();
+ delete mHandler;
+ mHandler = NULL;
+
+ close(mSock);
+ mSock = -1;
+}
diff --git a/volume_manager/NetlinkManager.h b/volume_manager/NetlinkManager.h
new file mode 100644
index 0000000..c09ff06
--- /dev/null
+++ b/volume_manager/NetlinkManager.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2019 The LineageOS Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _NETLINKMANAGER_H
+#define _NETLINKMANAGER_H
+
+#include <sysutils/NetlinkListener.h>
+#include <sysutils/SocketListener.h>
+
+class NetlinkHandler;
+
+class NetlinkManager {
+ private:
+ static NetlinkManager* sInstance;
+
+ private:
+ NetlinkHandler* mHandler;
+ int mSock;
+
+ public:
+ virtual ~NetlinkManager();
+
+ bool start();
+ void stop();
+
+ static NetlinkManager* Instance();
+
+ private:
+ NetlinkManager();
+};
+#endif
diff --git a/volume_manager/Process.cpp b/volume_manager/Process.cpp
new file mode 100644
index 0000000..387d8d3
--- /dev/null
+++ b/volume_manager/Process.cpp
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2019 The LineageOS Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#define LOG_TAG "ProcessKiller"
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <cutils/log.h>
+
+#include "Process.h"
+
+using android::base::ReadFileToString;
+using android::base::StringPrintf;
+
+int Process::readSymLink(const char* path, char* link, size_t max) {
+ struct stat s;
+ int length;
+
+ if (lstat(path, &s) < 0) return 0;
+ if ((s.st_mode & S_IFMT) != S_IFLNK) return 0;
+
+ // we have a symlink
+ length = readlink(path, link, max - 1);
+ if (length <= 0) return 0;
+ link[length] = 0;
+ return 1;
+}
+
+int Process::pathMatchesMountPoint(const char* path, const char* mountPoint) {
+ int length = strlen(mountPoint);
+ if (length > 1 && strncmp(path, mountPoint, length) == 0) {
+ // we need to do extra checking if mountPoint does not end in a '/'
+ if (mountPoint[length - 1] == '/') return 1;
+ // if mountPoint does not have a trailing slash, we need to make sure
+ // there is one in the path to avoid partial matches.
+ return (path[length] == 0 || path[length] == '/');
+ }
+
+ return 0;
+}
+
+void Process::getProcessName(int pid, std::string& out_name) {
+ if (!ReadFileToString(StringPrintf("/proc/%d/cmdline", pid), &out_name)) {
+ out_name = "???";
+ }
+}
+
+int Process::checkFileDescriptorSymLinks(int pid, const char* mountPoint) {
+ return checkFileDescriptorSymLinks(pid, mountPoint, NULL, 0);
+}
+
+int Process::checkFileDescriptorSymLinks(int pid, const char* mountPoint, char* openFilename,
+ size_t max) {
+ // compute path to process's directory of open files
+ char path[PATH_MAX];
+ snprintf(path, sizeof(path), "/proc/%d/fd", pid);
+ DIR* dir = opendir(path);
+ if (!dir) return 0;
+
+ // remember length of the path
+ int parent_length = strlen(path);
+ // append a trailing '/'
+ path[parent_length++] = '/';
+
+ struct dirent* de;
+ while ((de = readdir(dir))) {
+ if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..") ||
+ strlen(de->d_name) + parent_length + 1 >= PATH_MAX)
+ continue;
+
+ // append the file name, after truncating to parent directory
+ path[parent_length] = 0;
+ strlcat(path, de->d_name, PATH_MAX);
+
+ char link[PATH_MAX];
+
+ if (readSymLink(path, link, sizeof(link)) && pathMatchesMountPoint(link, mountPoint)) {
+ if (openFilename) {
+ memset(openFilename, 0, max);
+ strlcpy(openFilename, link, max);
+ }
+ closedir(dir);
+ return 1;
+ }
+ }
+
+ closedir(dir);
+ return 0;
+}
+
+int Process::checkFileMaps(int pid, const char* mountPoint) {
+ return checkFileMaps(pid, mountPoint, NULL, 0);
+}
+
+int Process::checkFileMaps(int pid, const char* mountPoint, char* openFilename, size_t max) {
+ FILE* file;
+ char buffer[PATH_MAX + 100];
+
+ snprintf(buffer, sizeof(buffer), "/proc/%d/maps", pid);
+ file = fopen(buffer, "re");
+ if (!file) return 0;
+
+ while (fgets(buffer, sizeof(buffer), file)) {
+ // skip to the path
+ const char* path = strchr(buffer, '/');
+ if (path && pathMatchesMountPoint(path, mountPoint)) {
+ if (openFilename) {
+ memset(openFilename, 0, max);
+ strlcpy(openFilename, path, max);
+ }
+ fclose(file);
+ return 1;
+ }
+ }
+
+ fclose(file);
+ return 0;
+}
+
+int Process::checkSymLink(int pid, const char* mountPoint, const char* name) {
+ char path[PATH_MAX];
+ char link[PATH_MAX];
+
+ snprintf(path, sizeof(path), "/proc/%d/%s", pid, name);
+ if (readSymLink(path, link, sizeof(link)) && pathMatchesMountPoint(link, mountPoint)) return 1;
+ return 0;
+}
+
+int Process::getPid(const char* s) {
+ int result = 0;
+ while (*s) {
+ if (!isdigit(*s)) return -1;
+ result = 10 * result + (*s++ - '0');
+ }
+ return result;
+}
+
+/*
+ * Hunt down processes that have files open at the given mount point.
+ */
+int Process::killProcessesWithOpenFiles(const char* path, int signal) {
+ int count = 0;
+ DIR* dir;
+ struct dirent* de;
+
+ if (!(dir = opendir("/proc"))) {
+ SLOGE("opendir failed (%s)", strerror(errno));
+ return count;
+ }
+
+ while ((de = readdir(dir))) {
+ int pid = getPid(de->d_name);
+ if (pid == -1) continue;
+
+ std::string name;
+ getProcessName(pid, name);
+
+ char openfile[PATH_MAX];
+
+ if (checkFileDescriptorSymLinks(pid, path, openfile, sizeof(openfile))) {
+ SLOGE("Process %s (%d) has open file %s", name.c_str(), pid, openfile);
+ } else if (checkFileMaps(pid, path, openfile, sizeof(openfile))) {
+ SLOGE("Process %s (%d) has open filemap for %s", name.c_str(), pid, openfile);
+ } else if (checkSymLink(pid, path, "cwd")) {
+ SLOGE("Process %s (%d) has cwd within %s", name.c_str(), pid, path);
+ } else if (checkSymLink(pid, path, "root")) {
+ SLOGE("Process %s (%d) has chroot within %s", name.c_str(), pid, path);
+ } else if (checkSymLink(pid, path, "exe")) {
+ SLOGE("Process %s (%d) has executable path within %s", name.c_str(), pid, path);
+ } else {
+ continue;
+ }
+
+ if (signal != 0) {
+ SLOGW("Sending %s to process %d", strsignal(signal), pid);
+ kill(pid, signal);
+ count++;
+ }
+ }
+ closedir(dir);
+ return count;
+}
diff --git a/volume_manager/Process.h b/volume_manager/Process.h
new file mode 100644
index 0000000..4abdc1b
--- /dev/null
+++ b/volume_manager/Process.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2019 The LineageOS Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _PROCESS_H
+#define _PROCESS_H
+
+class Process {
+ public:
+ static int killProcessesWithOpenFiles(const char* path, int signal);
+ static int getPid(const char* s);
+ static int checkSymLink(int pid, const char* path, const char* name);
+ static int checkFileMaps(int pid, const char* path);
+ static int checkFileMaps(int pid, const char* path, char* openFilename, size_t max);
+ static int checkFileDescriptorSymLinks(int pid, const char* mountPoint);
+ static int checkFileDescriptorSymLinks(int pid, const char* mountPoint, char* openFilename,
+ size_t max);
+ static void getProcessName(int pid, std::string& out_name);
+
+ private:
+ static int readSymLink(const char* path, char* link, size_t max);
+ static int pathMatchesMountPoint(const char* path, const char* mountPoint);
+};
+
+#endif
diff --git a/volume_manager/PublicVolume.cpp b/volume_manager/PublicVolume.cpp
new file mode 100644
index 0000000..8fb3b96
--- /dev/null
+++ b/volume_manager/PublicVolume.cpp
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2019 The LineageOS Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "PublicVolume.h"
+#include <volume_manager/ResponseCode.h>
+#include <volume_manager/VolumeManager.h>
+#include "Utils.h"
+#include "fs/Exfat.h"
+#include "fs/Ext4.h"
+#include "fs/F2fs.h"
+#include "fs/Ntfs.h"
+#include "fs/Vfat.h"
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <cutils/fs.h>
+#include <private/android_filesystem_config.h>
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/sysmacros.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+using android::base::StringPrintf;
+
+namespace android {
+namespace volmgr {
+
+PublicVolume::PublicVolume(dev_t device, const std::string& nickname,
+ const std::string& fstype /* = "" */,
+ const std::string& mntopts /* = "" */)
+ : VolumeBase(Type::kPublic), mDevice(device), mFsType(fstype), mMntOpts(mntopts) {
+ setId(StringPrintf("public:%u_%u", major(device), minor(device)));
+ setPartLabel(nickname);
+ mDevPath = StringPrintf("/dev/block/volmgr/%s", getId().c_str());
+}
+
+PublicVolume::~PublicVolume() {}
+
+status_t PublicVolume::readMetadata() {
+ std::string label;
+ status_t res = ReadMetadataUntrusted(mDevPath, mFsType, mFsUuid, label);
+ if (!label.empty()) {
+ setPartLabel(label);
+ }
+ VolumeManager::Instance()->notifyEvent(ResponseCode::VolumeFsTypeChanged, mFsType);
+ VolumeManager::Instance()->notifyEvent(ResponseCode::VolumeFsUuidChanged, mFsUuid);
+ return res;
+}
+
+status_t PublicVolume::doCreate() {
+ status_t res = CreateDeviceNode(mDevPath, mDevice);
+ if (res != OK) {
+ return res;
+ }
+ readMetadata();
+
+ // Use UUID as stable name, if available
+ std::string stableName = getId();
+ if (!mFsUuid.empty()) {
+ stableName = mFsUuid;
+ }
+ setPath(StringPrintf("/storage/%s", stableName.c_str()));
+
+ return OK;
+}
+
+status_t PublicVolume::doDestroy() {
+ return DestroyDeviceNode(mDevPath);
+}
+
+status_t PublicVolume::doMount() {
+ if (!IsFilesystemSupported(mFsType)) {
+ LOG(ERROR) << getId() << " unsupported filesystem " << mFsType;
+ return -EIO;
+ }
+
+ if (fs_prepare_dir(getPath().c_str(), 0700, AID_ROOT, AID_ROOT)) {
+ PLOG(ERROR) << getId() << " failed to create mount points";
+ return -errno;
+ }
+
+ int ret = 0;
+ if (mFsType == "exfat") {
+ ret = exfat::Mount(mDevPath, getPath(), AID_MEDIA_RW, AID_MEDIA_RW, 0007);
+ } else if (mFsType == "ext4") {
+ ret = ext4::Mount(mDevPath, getPath(), false, false, true, mMntOpts, false, true);
+ } else if (mFsType == "f2fs") {
+ ret = f2fs::Mount(mDevPath, getPath(), mMntOpts, false, true);
+ } else if (mFsType == "ntfs") {
+ ret =
+ ntfs::Mount(mDevPath, getPath(), false, false, false, AID_MEDIA_RW, AID_MEDIA_RW, 0007);
+ } else if (mFsType == "vfat") {
+ ret = vfat::Mount(mDevPath, getPath(), false, false, false, AID_MEDIA_RW, AID_MEDIA_RW,
+ 0007, true);
+ } else {
+ ret = ::mount(mDevPath.c_str(), getPath().c_str(), mFsType.c_str(), 0, nullptr);
+ }
+ if (ret) {
+ PLOG(ERROR) << getId() << " failed to mount " << mDevPath;
+ return -EIO;
+ }
+
+ return OK;
+}
+
+status_t PublicVolume::doUnmount(bool detach /* = false */) {
+ ForceUnmount(getPath(), detach);
+
+ rmdir(getPath().c_str());
+
+ return OK;
+}
+
+} // namespace volmgr
+} // namespace android
diff --git a/volume_manager/PublicVolume.h b/volume_manager/PublicVolume.h
new file mode 100644
index 0000000..c740c34
--- /dev/null
+++ b/volume_manager/PublicVolume.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2019 The LineageOS Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_VOLMGR_PUBLIC_VOLUME_H
+#define ANDROID_VOLMGR_PUBLIC_VOLUME_H
+
+#include "VolumeBase.h"
+
+#include <cutils/multiuser.h>
+
+namespace android {
+namespace volmgr {
+
+/*
+ * Shared storage provided by public (vfat) partition.
+ *
+ * Knows how to mount itself and then spawn a FUSE daemon to synthesize
+ * permissions.
+ *
+ * This volume is not inherently multi-user aware, so it has two possible
+ * modes of operation:
+ * 1. If primary storage for the device, it only binds itself to the
+ * owner user.
+ * 2. If secondary storage, it binds itself for all users, but masks
+ * away the Android directory for secondary users.
+ */
+class PublicVolume : public VolumeBase {
+ public:
+ PublicVolume(dev_t device, const std::string& nickname, const std::string& mntopts = "",
+ const std::string& fstype = "");
+ virtual ~PublicVolume();
+
+ protected:
+ status_t doCreate() override;
+ status_t doDestroy() override;
+ status_t doMount() override;
+ status_t doUnmount(bool detach = false) override;
+
+ status_t readMetadata();
+
+ private:
+ /* Kernel device representing partition */
+ dev_t mDevice;
+ /* Block device path */
+ std::string mDevPath;
+
+ /* Filesystem type */
+ std::string mFsType;
+ /* Filesystem UUID */
+ std::string mFsUuid;
+ /* Mount options */
+ std::string mMntOpts;
+
+ DISALLOW_COPY_AND_ASSIGN(PublicVolume);
+};
+
+} // namespace volmgr
+} // namespace android
+
+#endif
diff --git a/volume_manager/ResponseCode.cpp b/volume_manager/ResponseCode.cpp
new file mode 100644
index 0000000..5365295
--- /dev/null
+++ b/volume_manager/ResponseCode.cpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2019 The LineageOS Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+#define LOG_TAG "Vold"
+
+#include <cutils/log.h>
+
+#include <volume_manager/ResponseCode.h>
+
+int ResponseCode::convertFromErrno() {
+ if (errno == ENODEV) {
+ return (ResponseCode::OpFailedNoMedia);
+ } else if (errno == ENODATA) {
+ return (ResponseCode::OpFailedMediaBlank);
+ } else if (errno == EIO) {
+ return (ResponseCode::OpFailedMediaCorrupt);
+ } else if (errno == EBUSY) {
+ return (ResponseCode::OpFailedStorageBusy);
+ } else if (errno == ENOENT) {
+ return (ResponseCode::OpFailedStorageNotFound);
+ }
+
+ SLOGW("Returning OperationFailed - no handler for errno %d", errno);
+ return (ResponseCode::OperationFailed);
+}
diff --git a/volume_manager/Utils.cpp b/volume_manager/Utils.cpp
new file mode 100644
index 0000000..50fe356
--- /dev/null
+++ b/volume_manager/Utils.cpp
@@ -0,0 +1,338 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2019 The LineageOS Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Utils.h"
+#include <volume_manager/VolumeManager.h>
+#include "Process.h"
+#include "sehandle.h"
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+
+#include <cutils/fs.h>
+#include <private/android_filesystem_config.h>
+
+#include <dirent.h>
+#include <fcntl.h>
+#include <linux/fs.h>
+#include <stdlib.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+#include <sys/sysmacros.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <mutex>
+#include <thread>
+
+#ifndef UMOUNT_NOFOLLOW
+#define UMOUNT_NOFOLLOW 0x00000008 /* Don't follow symlink on umount */
+#endif
+
+using android::base::ReadFileToString;
+using android::base::StringPrintf;
+
+using namespace std::chrono_literals;
+
+namespace android {
+namespace volmgr {
+
+char * sBlkidContext = nullptr;
+char * sBlkidUntrustedContext = nullptr;
+char * sFsckContext = nullptr;
+char * sFsckUntrustedContext = nullptr;
+
+#include <blkid/blkid.h>
+
+static const char* kProcFilesystems = "/proc/filesystems";
+
+status_t CreateDeviceNode(const std::string& path, dev_t dev) {
+ const char* cpath = path.c_str();
+ status_t res = 0;
+
+ char* secontext = nullptr;
+ if (sehandle) {
+ if (!selabel_lookup(sehandle, &secontext, cpath, S_IFBLK)) {
+ setfscreatecon(secontext);
+ }
+ }
+
+ mode_t mode = 0660 | S_IFBLK;
+ if (mknod(cpath, mode, dev) < 0) {
+ if (errno != EEXIST) {
+ PLOG(ERROR) << "Failed to create device node for " << major(dev) << ":" << minor(dev)
+ << " at " << path;
+ res = -errno;
+ }
+ }
+
+ if (secontext) {
+ setfscreatecon(nullptr);
+ freecon(secontext);
+ }
+
+ return res;
+}
+
+status_t DestroyDeviceNode(const std::string& path) {
+ const char* cpath = path.c_str();
+ if (TEMP_FAILURE_RETRY(unlink(cpath))) {
+ return -errno;
+ } else {
+ return OK;
+ }
+}
+
+status_t PrepareDir(const std::string& path, mode_t mode, uid_t uid, gid_t gid) {
+ const char* cpath = path.c_str();
+
+ char* secontext = nullptr;
+ if (sehandle) {
+ if (!selabel_lookup(sehandle, &secontext, cpath, S_IFDIR)) {
+ setfscreatecon(secontext);
+ }
+ }
+
+ int res = fs_prepare_dir(cpath, mode, uid, gid);
+
+ if (secontext) {
+ setfscreatecon(nullptr);
+ freecon(secontext);
+ }
+
+ if (res == 0) {
+ return OK;
+ } else {
+ return -errno;
+ }
+}
+
+status_t ForceUnmount(const std::string& path, bool detach /* = false */) {
+ const char* cpath = path.c_str();
+ if (detach) {
+ if (!umount2(path.c_str(), UMOUNT_NOFOLLOW | MNT_DETACH) || errno == EINVAL ||
+ errno == ENOENT) {
+ return OK;
+ }
+ PLOG(WARNING) << "Failed to unmount " << path;
+ return -errno;
+ }
+ if (!umount2(cpath, UMOUNT_NOFOLLOW) || errno == EINVAL || errno == ENOENT) {
+ return OK;
+ }
+ sleep(1);
+
+ Process::killProcessesWithOpenFiles(cpath, SIGINT);
+ sleep(1);
+ if (!umount2(cpath, UMOUNT_NOFOLLOW) || errno == EINVAL || errno == ENOENT) {
+ return OK;
+ }
+
+ Process::killProcessesWithOpenFiles(cpath, SIGTERM);
+ sleep(1);
+ if (!umount2(cpath, UMOUNT_NOFOLLOW) || errno == EINVAL || errno == ENOENT) {
+ return OK;
+ }
+
+ Process::killProcessesWithOpenFiles(cpath, SIGKILL);
+ sleep(1);
+ if (!umount2(cpath, UMOUNT_NOFOLLOW) || errno == EINVAL || errno == ENOENT) {
+ return OK;
+ }
+
+ return -errno;
+}
+
+status_t KillProcessesUsingPath(const std::string& path) {
+ const char* cpath = path.c_str();
+ if (Process::killProcessesWithOpenFiles(cpath, SIGINT) == 0) {
+ return OK;
+ }
+ sleep(1);
+
+ if (Process::killProcessesWithOpenFiles(cpath, SIGTERM) == 0) {
+ return OK;
+ }
+ sleep(1);
+
+ if (Process::killProcessesWithOpenFiles(cpath, SIGKILL) == 0) {
+ return OK;
+ }
+ sleep(1);
+
+ // Send SIGKILL a second time to determine if we've
+ // actually killed everyone with open files
+ if (Process::killProcessesWithOpenFiles(cpath, SIGKILL) == 0) {
+ return OK;
+ }
+ PLOG(ERROR) << "Failed to kill processes using " << path;
+ return -EBUSY;
+}
+
+status_t BindMount(const std::string& source, const std::string& target) {
+ if (::mount(source.c_str(), target.c_str(), "", MS_BIND, NULL)) {
+ PLOG(ERROR) << "Failed to bind mount " << source << " to " << target;
+ return -errno;
+ }
+ return OK;
+}
+
+static status_t readMetadata(const std::string& path, std::string& fsType, std::string& fsUuid,
+ std::string& fsLabel) {
+ char* val = NULL;
+ val = blkid_get_tag_value(NULL, "TYPE", path.c_str());
+ if (val) {
+ fsType = val;
+ }
+ val = blkid_get_tag_value(NULL, "UUID", path.c_str());
+ if (val) {
+ fsUuid = val;
+ }
+ val = blkid_get_tag_value(NULL, "LABEL", path.c_str());
+ if (val) {
+ fsLabel = val;
+ }
+
+ return OK;
+}
+
+status_t ReadMetadata(const std::string& path, std::string& fsType, std::string& fsUuid,
+ std::string& fsLabel) {
+ return readMetadata(path, fsType, fsUuid, fsLabel);
+}
+
+status_t ReadMetadataUntrusted(const std::string& path, std::string& fsType, std::string& fsUuid,
+ std::string& fsLabel) {
+ return readMetadata(path, fsType, fsUuid, fsLabel);
+}
+
+status_t ForkExecvp(const std::vector<std::string>& args) {
+ return ForkExecvp(args, nullptr);
+}
+
+status_t ForkExecvp(const std::vector<std::string>& args, char * context) {
+ std::vector<std::string> output;
+ size_t argc = args.size();
+ char** argv = (char**)calloc(argc + 1, sizeof(char*));
+ for (size_t i = 0; i < argc; i++) {
+ argv[i] = (char*)args[i].c_str();
+ if (i == 0) {
+ LOG(VERBOSE) << args[i];
+ } else {
+ LOG(VERBOSE) << " " << args[i];
+ }
+ }
+
+ if (setexeccon(context)) {
+ LOG(ERROR) << "Failed to setexeccon";
+ abort();
+ }
+
+ pid_t pid = fork();
+ int fork_errno = errno;
+ if (pid == 0) {
+ close(STDIN_FILENO);
+ close(STDOUT_FILENO);
+ close(STDERR_FILENO);
+
+ if (execvp(argv[0], argv)) {
+ PLOG(ERROR) << "Failed to exec";
+ }
+
+ _exit(1);
+ }
+
+ if (setexeccon(nullptr)) {
+ LOG(ERROR) << "Failed to setexeccon";
+ abort();
+ }
+
+ if (pid == -1) {
+ PLOG(ERROR) << "Failed to exec";
+ return -fork_errno;
+ }
+
+ waitpid(pid, nullptr, 0);
+
+ free(argv);
+ return OK;
+}
+
+bool IsFilesystemSupported(const std::string& fsType) {
+ std::string supported;
+ if (!ReadFileToString(kProcFilesystems, &supported)) {
+ PLOG(ERROR) << "Failed to read supported filesystems";
+ return false;
+ }
+
+ /* fuse filesystems */
+ supported.append("fuse\tntfs\n");
+
+ return supported.find(fsType + "\n") != std::string::npos;
+}
+
+status_t WipeBlockDevice(const std::string& path) {
+ status_t res = -1;
+ const char* c_path = path.c_str();
+ unsigned long nr_sec = 0;
+ unsigned long long range[2];
+
+ int fd = TEMP_FAILURE_RETRY(open(c_path, O_RDWR | O_CLOEXEC));
+ if (fd == -1) {
+ PLOG(ERROR) << "Failed to open " << path;
+ goto done;
+ }
+
+ if ((ioctl(fd, BLKGETSIZE, &nr_sec)) == -1) {
+ PLOG(ERROR) << "Failed to determine size of " << path;
+ goto done;
+ }
+
+ range[0] = 0;
+ range[1] = (unsigned long long)nr_sec * 512;
+
+ LOG(INFO) << "About to discard " << range[1] << " on " << path;
+ if (ioctl(fd, BLKDISCARD, &range) == 0) {
+ LOG(INFO) << "Discard success on " << path;
+ res = 0;
+ } else {
+ PLOG(ERROR) << "Discard failure on " << path;
+ }
+
+done:
+ close(fd);
+ return res;
+}
+
+dev_t GetDevice(const std::string& path) {
+ struct stat sb;
+ if (stat(path.c_str(), &sb)) {
+ PLOG(WARNING) << "Failed to stat " << path;
+ return 0;
+ } else {
+ return sb.st_dev;
+ }
+}
+
+bool IsRunningInEmulator() {
+ return android::base::GetBoolProperty("ro.kernel.qemu", false);
+}
+
+} // namespace volmgr
+} // namespace android
diff --git a/volume_manager/Utils.h b/volume_manager/Utils.h
new file mode 100644
index 0000000..0496a4e
--- /dev/null
+++ b/volume_manager/Utils.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2019 The LineageOS Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_VOLMGR_UTILS_H
+#define ANDROID_VOLMGR_UTILS_H
+
+#include <cutils/multiuser.h>
+#include <selinux/selinux.h>
+#include <utils/Errors.h>
+
+#include <chrono>
+#include <string>
+#include <vector>
+
+// DISALLOW_COPY_AND_ASSIGN disallows the copy and operator= functions. It goes in the private:
+// declarations in a class.
+#if !defined(DISALLOW_COPY_AND_ASSIGN)
+#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
+ TypeName(const TypeName&) = delete; \
+ void operator=(const TypeName&) = delete
+#endif
+
+struct DIR;
+
+namespace android {
+namespace volmgr {
+
+/* SELinux contexts used depending on the block device type */
+extern char * sBlkidContext;
+extern char * sBlkidUntrustedContext;
+extern char * sFsckContext;
+extern char * sFsckUntrustedContext;
+
+status_t CreateDeviceNode(const std::string& path, dev_t dev);
+status_t DestroyDeviceNode(const std::string& path);
+
+/* fs_prepare_dir wrapper that creates with SELinux context */
+status_t PrepareDir(const std::string& path, mode_t mode, uid_t uid, gid_t gid);
+
+/* Really unmounts the path, killing active processes along the way */
+status_t ForceUnmount(const std::string& path, bool detach = false);
+
+/* Kills any processes using given path */
+status_t KillProcessesUsingPath(const std::string& path);
+
+/* Creates bind mount from source to target */
+status_t BindMount(const std::string& source, const std::string& target);
+
+/* Reads filesystem metadata from device at path */
+status_t ReadMetadata(const std::string& path, std::string& fsType, std::string& fsUuid,
+ std::string& fsLabel);
+
+/* Reads filesystem metadata from untrusted device at path */
+status_t ReadMetadataUntrusted(const std::string& path, std::string& fsType, std::string& fsUuid,
+ std::string& fsLabel);
+
+/* Returns either WEXITSTATUS() status, or a negative errno */
+status_t ForkExecvp(const std::vector<std::string>& args);
+status_t ForkExecvp(const std::vector<std::string>& args, char * context);
+
+bool IsFilesystemSupported(const std::string& fsType);
+
+/* Wipes contents of block device at given path */
+status_t WipeBlockDevice(const std::string& path);
+
+dev_t GetDevice(const std::string& path);
+
+/* Checks if Android is running in QEMU */
+bool IsRunningInEmulator();
+
+} // namespace volmgr
+} // namespace android
+
+#endif
diff --git a/volume_manager/VolumeBase.cpp b/volume_manager/VolumeBase.cpp
new file mode 100644
index 0000000..a0fd33b
--- /dev/null
+++ b/volume_manager/VolumeBase.cpp
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2019 The LineageOS Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "VolumeBase.h"
+#include <volume_manager/ResponseCode.h>
+#include <volume_manager/VolumeManager.h>
+#include "Utils.h"
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+using android::base::StringPrintf;
+
+namespace android {
+namespace volmgr {
+
+VolumeBase::VolumeBase(Type type)
+ : mType(type), mMountFlags(0), mCreated(false), mState(State::kUnmounted), mSilent(false) {}
+
+VolumeBase::~VolumeBase() {
+ CHECK(!mCreated);
+}
+
+void VolumeBase::setState(State state) {
+ mState = state;
+ VolumeManager::Instance()->notifyEvent(ResponseCode::VolumeStateChanged,
+ StringPrintf("%d", mState));
+}
+
+status_t VolumeBase::setDiskId(const std::string& diskId) {
+ if (mCreated) {
+ LOG(WARNING) << getId() << " diskId change requires destroyed";
+ return -EBUSY;
+ }
+
+ mDiskId = diskId;
+ return OK;
+}
+
+status_t VolumeBase::setPartGuid(const std::string& partGuid) {
+ if (mCreated) {
+ LOG(WARNING) << getId() << " partGuid change requires destroyed";
+ return -EBUSY;
+ }
+
+ mPartGuid = partGuid;
+ return OK;
+}
+
+status_t VolumeBase::setPartLabel(const std::string& partLabel) {
+ if ((mState != State::kUnmounted) && (mState != State::kUnmountable)) {
+ LOG(WARNING) << getId() << " partLabel change requires state unmounted or unmountable";
+ LOG(WARNING) << getId() << " state=" << (int)mState;
+ }
+
+ mPartLabel = partLabel;
+ return OK;
+}
+
+status_t VolumeBase::setMountFlags(int mountFlags) {
+ if ((mState != State::kUnmounted) && (mState != State::kUnmountable)) {
+ LOG(WARNING) << getId() << " flags change requires state unmounted or unmountable";
+ return -EBUSY;
+ }
+
+ mMountFlags = mountFlags;
+ return OK;
+}
+
+status_t VolumeBase::setSilent(bool silent) {
+ if (mCreated) {
+ LOG(WARNING) << getId() << " silence change requires destroyed";
+ return -EBUSY;
+ }
+
+ mSilent = silent;
+ return OK;
+}
+
+status_t VolumeBase::setId(const std::string& id) {
+ if (mCreated) {
+ LOG(WARNING) << getId() << " id change requires not created";
+ return -EBUSY;
+ }
+
+ mId = id;
+ return OK;
+}
+
+status_t VolumeBase::setPath(const std::string& path) {
+ mPath = path;
+ VolumeManager::Instance()->notifyEvent(ResponseCode::VolumePathChanged, mPath);
+ return OK;
+}
+
+status_t VolumeBase::create() {
+ if (mCreated) {
+ return BAD_VALUE;
+ }
+
+ mCreated = true;
+ std::vector<std::string> argv;
+ argv.push_back(StringPrintf("%d", mType));
+ argv.push_back(mDiskId);
+ argv.push_back(mPartGuid);
+ status_t res = doCreate();
+ if (res == OK) {
+ VolumeManager::Instance()->notifyEvent(ResponseCode::VolumeCreated, argv);
+ if (mPartLabel.size() > 0) {
+ VolumeManager::Instance()->notifyEvent(ResponseCode::VolumeFsLabelChanged, mPartLabel);
+ }
+ }
+ setState(State::kUnmounted);
+ return res;
+}
+
+status_t VolumeBase::doCreate() {
+ return OK;
+}
+
+status_t VolumeBase::destroy() {
+ if (!mCreated) {
+ return NO_INIT;
+ }
+
+ if (mState == State::kMounted) {
+ unmount(true /* detatch */);
+ setState(State::kBadRemoval);
+ } else {
+ setState(State::kRemoved);
+ }
+
+ VolumeManager::Instance()->notifyEvent(ResponseCode::VolumeDestroyed);
+ status_t res = doDestroy();
+ mCreated = false;
+ return res;
+}
+
+status_t VolumeBase::doDestroy() {
+ return OK;
+}
+
+status_t VolumeBase::mount() {
+ if ((mState != State::kUnmounted) && (mState != State::kUnmountable)) {
+ LOG(WARNING) << getId() << " mount requires state unmounted or unmountable";
+ return -EBUSY;
+ }
+
+ setState(State::kChecking);
+ status_t res = doMount();
+ if (res == OK) {
+ setState(State::kMounted);
+ } else {
+ setState(State::kUnmountable);
+ }
+
+ return res;
+}
+
+status_t VolumeBase::unmount(bool detach /* = false */) {
+ setState(State::kEjecting);
+
+ status_t res = doUnmount(detach);
+ setState(State::kUnmounted);
+ return res;
+}
+
+} // namespace volmgr
+} // namespace android
diff --git a/volume_manager/VolumeBase.h b/volume_manager/VolumeBase.h
new file mode 100644
index 0000000..6ec6e27
--- /dev/null
+++ b/volume_manager/VolumeBase.h
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2019 The LineageOS Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_VOLMGR_VOLUMEBASE_H
+#define ANDROID_VOLMGR_VOLUMEBASE_H
+
+#include "Utils.h"
+
+#include <cutils/multiuser.h>
+#include <utils/Errors.h>
+
+#include <sys/types.h>
+#include <list>
+#include <string>
+
+namespace android {
+namespace volmgr {
+
+/*
+ * Representation of a mounted volume ready for presentation.
+ *
+ * Various subclasses handle the different mounting prerequisites, such as
+ * encryption details, etc. Volumes can also be "stacked" above other
+ * volumes to help communicate dependencies. For example, an ASEC volume
+ * can be stacked on a vfat volume.
+ *
+ * Mounted volumes can be asked to manage bind mounts to present themselves
+ * to specific users on the device.
+ *
+ * When an unmount is requested, the volume recursively unmounts any stacked
+ * volumes and removes any bind mounts before finally unmounting itself.
+ */
+class VolumeBase {
+ public:
+ virtual ~VolumeBase();
+
+ enum class Type {
+ kPublic = 0,
+ kPrivate,
+ kEmulated,
+ kAsec,
+ kObb,
+ };
+
+ enum MountFlags {
+ /* Flag that volume is primary external storage */
+ kPrimary = 1 << 0,
+ /* Flag that volume is visible to normal apps */
+ kVisible = 1 << 1,
+ };
+
+ enum class State {
+ kUnmounted = 0,
+ kChecking,
+ kMounted,
+ kMountedReadOnly,
+ kEjecting,
+ kUnmountable,
+ kRemoved,
+ kBadRemoval,
+ };
+
+ const std::string& getId() const { return mId; }
+ const std::string& getDiskId() const { return mDiskId; }
+ const std::string& getPartGuid() const { return mPartGuid; }
+ const std::string& getPartLabel() const { return mPartLabel; }
+ Type getType() const { return mType; }
+ int getMountFlags() const { return mMountFlags; }
+ State getState() const { return mState; }
+ const std::string& getPath() const { return mPath; }
+
+ status_t setDiskId(const std::string& diskId);
+ status_t setPartGuid(const std::string& partGuid);
+ status_t setPartLabel(const std::string& partLabel);
+ status_t setMountFlags(int mountFlags);
+ status_t setSilent(bool silent);
+
+ status_t create();
+ status_t destroy();
+ status_t mount();
+ status_t unmount(bool detach = false);
+
+ protected:
+ explicit VolumeBase(Type type);
+
+ virtual status_t doCreate();
+ virtual status_t doDestroy();
+ virtual status_t doMount() = 0;
+ virtual status_t doUnmount(bool detach = false) = 0;
+
+ status_t setId(const std::string& id);
+ status_t setPath(const std::string& path);
+
+ private:
+ /* ID that uniquely references volume while alive */
+ std::string mId;
+ /* ID that uniquely references parent disk while alive */
+ std::string mDiskId;
+ /* Partition GUID of this volume */
+ std::string mPartGuid;
+ /* Partition label of this volume */
+ std::string mPartLabel;
+ /* Volume type */
+ Type mType;
+ /* Flags used when mounting this volume */
+ int mMountFlags;
+ /* Flag indicating object is created */
+ bool mCreated;
+ /* Current state of volume */
+ State mState;
+ /* Path to mounted volume */
+ std::string mPath;
+ /* Flag indicating that volume should emit no events */
+ bool mSilent;
+
+ void setState(State state);
+
+ DISALLOW_COPY_AND_ASSIGN(VolumeBase);
+};
+
+} // namespace volmgr
+} // namespace android
+
+#endif
diff --git a/volume_manager/VolumeManager.cpp b/volume_manager/VolumeManager.cpp
new file mode 100644
index 0000000..7609b1b
--- /dev/null
+++ b/volume_manager/VolumeManager.cpp
@@ -0,0 +1,369 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2019 The LineageOS Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <blkid/blkid.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <fs_mgr.h>
+#include <sys/stat.h>
+#include <sys/sysmacros.h>
+#include <sys/types.h>
+
+#define LOG_TAG "VolumeManager"
+
+#include <android-base/logging.h>
+#include <cutils/properties.h>
+
+#include <sysutils/NetlinkEvent.h>
+
+#include <volume_manager/VolumeManager.h>
+#include <fstab/fstab.h>
+#include "Disk.h"
+#include "DiskPartition.h"
+#include "EmulatedVolume.h"
+#include "VolumeBase.h"
+#include "NetlinkManager.h"
+
+#include "sehandle.h"
+
+struct selabel_handle* sehandle;
+
+using android::fs_mgr::Fstab;
+using android::fs_mgr::FstabEntry;
+
+static const unsigned int kMajorBlockMmc = 179;
+static const unsigned int kMajorBlockExperimentalMin = 240;
+static const unsigned int kMajorBlockExperimentalMax = 254;
+
+namespace android {
+namespace volmgr {
+
+static void do_coldboot(DIR* d, int lvl) {
+ struct dirent* de;
+ int dfd, fd;
+
+ dfd = dirfd(d);
+
+ fd = openat(dfd, "uevent", O_WRONLY | O_CLOEXEC);
+ if (fd >= 0) {
+ write(fd, "add\n", 4);
+ close(fd);
+ }
+
+ while ((de = readdir(d))) {
+ DIR* d2;
+
+ if (de->d_name[0] == '.') continue;
+
+ if (de->d_type != DT_DIR && lvl > 0) continue;
+
+ fd = openat(dfd, de->d_name, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
+ if (fd < 0) continue;
+
+ d2 = fdopendir(fd);
+ if (d2 == 0)
+ close(fd);
+ else {
+ do_coldboot(d2, lvl + 1);
+ closedir(d2);
+ }
+ }
+}
+
+static void coldboot(const char* path) {
+ DIR* d = opendir(path);
+ if (d) {
+ do_coldboot(d, 0);
+ closedir(d);
+ }
+}
+
+static int process_config(VolumeManager* vm, FstabEntry* data_recp) {
+ Fstab fstab;
+ if (!ReadDefaultFstab(&fstab)) {
+ PLOG(ERROR) << "Failed to open default fstab";
+ return -1;
+ }
+
+ /* Loop through entries looking for ones that vold manages */
+ for (int i = 0; i < fstab.size(); i++) {
+ if (fstab[i].fs_mgr_flags.vold_managed) {
+ std::string sysPattern(fstab[i].blk_device);
+ std::string fstype = fstab[i].fs_type;
+ std::string mntopts = fstab[i].fs_options;
+ std::string nickname = fstab[i].label;
+ int partnum = fstab[i].partnum;
+ int flags = 0;
+
+ if (fstab[i].is_encryptable()) {
+ flags |= android::volmgr::Disk::Flags::kAdoptable;
+ }
+ if (fstab[i].fs_mgr_flags.no_emulated_sd ||
+ property_get_bool("vold.debug.default_primary", false)) {
+ flags |= android::volmgr::Disk::Flags::kDefaultPrimary;
+ }
+ if (fstab[i].fs_mgr_flags.nonremovable) {
+ flags |= android::volmgr::Disk::Flags::kNonRemovable;
+ }
+
+ vm->addDiskSource(new VolumeManager::DiskSource(sysPattern, nickname, partnum, flags,
+ fstype, mntopts));
+ } else {
+ if (data_recp->fs_type.empty() && fstab[i].mount_point == "/data") {
+ char* detected_fs_type =
+ blkid_get_tag_value(nullptr, "TYPE", fstab[i].blk_device.c_str());
+ if (!detected_fs_type || fstab[i].fs_type == detected_fs_type) {
+ *data_recp = fstab[i];
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+VolumeInfo::VolumeInfo(const VolumeBase* vol)
+ : mId(vol->getId()), mLabel(vol->getPartLabel()), mPath(vol->getPath()) {
+ // Empty
+}
+
+VolumeManager* VolumeManager::sInstance = nullptr;
+
+VolumeManager* VolumeManager::Instance(void) {
+ if (!sInstance) {
+ sInstance = new VolumeManager();
+ }
+ return sInstance;
+}
+
+VolumeManager::VolumeManager(void)
+ : mWatcher(nullptr), mNetlinkManager(NetlinkManager::Instance()), mInternalEmulated(nullptr) {
+ // Empty
+}
+
+VolumeManager::~VolumeManager(void) {
+ stop();
+}
+
+bool VolumeManager::start(VolumeWatcher* watcher) {
+ setenv("BLKID_FILE", "/tmp/vold_blkid.tab", 1);
+
+ sehandle = selinux_android_file_context_handle();
+ if (sehandle) {
+ selinux_android_set_sehandle(sehandle);
+ }
+
+ mkdir("/dev/block/volmgr", 0755);
+
+ mWatcher = watcher;
+
+ FstabEntry data_rec;
+ if (process_config(this, &data_rec) != 0) {
+ LOG(ERROR) << "Error reading configuration... continuing anyway";
+ }
+
+ if (!data_rec.fs_type.empty()) {
+ mInternalEmulated = new EmulatedVolume(&data_rec, "media/0");
+ mInternalEmulated->create();
+ }
+
+ if (!mNetlinkManager->start()) {
+ LOG(ERROR) << "Unable to start NetlinkManager";
+ return false;
+ }
+
+ coldboot("/sys/block");
+
+ unmountAll();
+
+ LOG(INFO) << "VolumeManager initialized";
+ return true;
+}
+
+void VolumeManager::stop(void) {
+ for (auto& disk : mDisks) {
+ disk->destroy();
+ delete disk;
+ }
+ mDisks.clear();
+ for (auto& source : mDiskSources) {
+ delete source;
+ }
+ mDiskSources.clear();
+
+ mNetlinkManager->stop();
+ mWatcher = nullptr;
+}
+
+bool VolumeManager::reset(void) {
+ return false;
+}
+
+bool VolumeManager::unmountAll(void) {
+ std::lock_guard<std::mutex> lock(mLock);
+
+ if (mInternalEmulated) {
+ mInternalEmulated->unmount();
+ }
+
+ for (auto& disk : mDisks) {
+ disk->unmountAll();
+ }
+
+ return true;
+}
+
+void VolumeManager::getVolumeInfo(std::vector<VolumeInfo>& info) {
+ std::lock_guard<std::mutex> lock(mLock);
+
+ info.clear();
+ if (mInternalEmulated) {
+ info.push_back(VolumeInfo(mInternalEmulated));
+ }
+ for (const auto& disk : mDisks) {
+ disk->getVolumeInfo(info);
+ }
+}
+
+VolumeBase* VolumeManager::findVolume(const std::string& id) {
+ if (mInternalEmulated && mInternalEmulated->getId() == id) {
+ return mInternalEmulated;
+ }
+ for (const auto& disk : mDisks) {
+ auto vol = disk->findVolume(id);
+ if (vol != nullptr) {
+ return vol.get();
+ }
+ }
+ return nullptr;
+}
+
+bool VolumeManager::volumeMount(const std::string& id) {
+ std::lock_guard<std::mutex> lock(mLock);
+ auto vol = findVolume(id);
+ if (!vol) {
+ return false;
+ }
+ status_t res = vol->mount();
+ return (res == OK);
+}
+
+bool VolumeManager::volumeUnmount(const std::string& id, bool detach /* = false */) {
+ std::lock_guard<std::mutex> lock(mLock);
+ auto vol = findVolume(id);
+ if (!vol) {
+ return false;
+ }
+ status_t res = vol->unmount(detach);
+ return (res == OK);
+}
+
+void VolumeManager::addDiskSource(DiskSource* source) {
+ std::lock_guard<std::mutex> lock(mLock);
+
+ mDiskSources.push_back(source);
+}
+
+void VolumeManager::handleBlockEvent(NetlinkEvent* evt) {
+ std::lock_guard<std::mutex> lock(mLock);
+
+ const char* param;
+ param = evt->findParam("DEVTYPE");
+ std::string devType(param ? param : "");
+ if (devType != "disk") {
+ return;
+ }
+ param = evt->findParam("DEVPATH");
+ std::string eventPath(param ? param : "");
+
+ int major = atoi(evt->findParam("MAJOR"));
+ int minor = atoi(evt->findParam("MINOR"));
+ dev_t device = makedev(major, minor);
+
+ switch (evt->getAction()) {
+ case NetlinkEvent::Action::kAdd: {
+ for (const auto& source : mDiskSources) {
+ if (source->matches(eventPath)) {
+ // For now, assume that MMC, virtio-blk (the latter is
+ // emulator-specific; see Disk.cpp for details) and UFS card
+ // devices are SD, and that everything else is USB
+ int flags = source->getFlags();
+ if (major == kMajorBlockMmc || (eventPath.find("ufs") != std::string::npos) ||
+ (IsRunningInEmulator() && major >= (int)kMajorBlockExperimentalMin &&
+ major <= (int)kMajorBlockExperimentalMax)) {
+ flags |= Disk::Flags::kSd;
+ } else {
+ flags |= Disk::Flags::kUsb;
+ }
+
+ Disk* disk = (source->getPartNum() == -1)
+ ? new Disk(eventPath, device, source->getNickname(), flags)
+ : new DiskPartition(eventPath, device, source->getNickname(),
+ flags, source->getPartNum(),
+ source->getFsType(), source->getMntOpts());
+ disk->create();
+ mDisks.push_back(disk);
+ break;
+ }
+ }
+ break;
+ }
+ case NetlinkEvent::Action::kChange: {
+ LOG(DEBUG) << "Disk at " << major << ":" << minor << " changed";
+ for (const auto& disk : mDisks) {
+ if (disk->getDevice() == device) {
+ disk->readMetadata();
+ disk->readPartitions();
+ }
+ }
+ break;
+ }
+ case NetlinkEvent::Action::kRemove: {
+ auto i = mDisks.begin();
+ while (i != mDisks.end()) {
+ if ((*i)->getDevice() == device) {
+ (*i)->destroy();
+ i = mDisks.erase(i);
+ } else {
+ ++i;
+ }
+ }
+ break;
+ }
+ default: {
+ LOG(WARNING) << "Unexpected block event action " << (int)evt->getAction();
+ break;
+ }
+ }
+}
+
+void VolumeManager::notifyEvent(int code) {
+ std::vector<std::string> argv;
+ notifyEvent(code, argv);
+}
+
+void VolumeManager::notifyEvent(int code, const std::string& arg) {
+ std::vector<std::string> argv;
+ argv.push_back(arg);
+ notifyEvent(code, argv);
+}
+
+void VolumeManager::notifyEvent(int code, const std::vector<std::string>& argv) {
+ mWatcher->handleEvent(code, argv);
+}
+
+} // namespace volmgr
+} // namespace android
diff --git a/volume_manager/fs/Exfat.cpp b/volume_manager/fs/Exfat.cpp
new file mode 100644
index 0000000..56ae430
--- /dev/null
+++ b/volume_manager/fs/Exfat.cpp
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2019 The LineageOS Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Exfat.h"
+#include "Utils.h"
+
+#define LOG_TAG "Vold"
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+
+#include <cutils/log.h>
+
+#include <logwrap/logwrap.h>
+
+#include <string>
+#include <vector>
+
+#include <sys/mount.h>
+
+using android::base::StringPrintf;
+
+namespace android {
+namespace volmgr {
+namespace exfat {
+
+status_t Mount(const std::string& source, const std::string& target, int ownerUid, int ownerGid,
+ int permMask) {
+ int mountFlags = MS_NODEV | MS_NOSUID | MS_DIRSYNC | MS_NOATIME | MS_NOEXEC;
+ auto mountData = android::base::StringPrintf("uid=%d,gid=%d,fmask=%o,dmask=%o", ownerUid,
+ ownerGid, permMask, permMask);
+
+ if (mount(source.c_str(), target.c_str(), "exfat", mountFlags, mountData.c_str()) == 0) {
+ return 0;
+ }
+ PLOG(ERROR) << "Mount failed; attempting read-only";
+ mountFlags |= MS_RDONLY;
+ if (mount(source.c_str(), target.c_str(), "exfat", mountFlags, mountData.c_str()) == 0) {
+ return 0;
+ }
+
+ return -1;
+}
+
+} // namespace exfat
+} // namespace volmgr
+} // namespace android
diff --git a/volume_manager/fs/Exfat.h b/volume_manager/fs/Exfat.h
new file mode 100644
index 0000000..80bb289
--- /dev/null
+++ b/volume_manager/fs/Exfat.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2019 The LineageOS Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef VOLMGR_EXFAT_H
+#define VOLMGR_EXFAT_H
+
+#include <utils/Errors.h>
+
+#include <string>
+
+namespace android {
+namespace volmgr {
+namespace exfat {
+
+status_t Mount(const std::string& source, const std::string& target, int ownerUid, int ownerGid,
+ int permMask);
+
+} // namespace exfat
+} // namespace volmgr
+} // namespace android
+
+#endif
diff --git a/volume_manager/fs/Ext4.cpp b/volume_manager/fs/Ext4.cpp
new file mode 100644
index 0000000..48d848c
--- /dev/null
+++ b/volume_manager/fs/Ext4.cpp
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ * Copyright (C) 2019 The LineageOS Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <string>
+#include <vector>
+
+#include <sys/mman.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <linux/kdev_t.h>
+
+#define LOG_TAG "Vold"
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <cutils/log.h>
+#include <cutils/properties.h>
+#include <logwrap/logwrap.h>
+#include <private/android_filesystem_config.h>
+#include <selinux/selinux.h>
+
+#include "Ext4.h"
+#include "Utils.h"
+
+using android::base::StringPrintf;
+
+namespace android {
+namespace volmgr {
+namespace ext4 {
+
+status_t Mount(const std::string& source, const std::string& target, bool ro, bool remount,
+ bool executable, const std::string& opts /* = "" */, bool trusted, bool portable) {
+ int rc;
+ unsigned long flags;
+
+ std::string data(opts);
+
+ if (portable) {
+ if (!data.empty()) {
+ data += ",";
+ }
+ data += "context=u:object_r:sdcard_posix:s0";
+ }
+ const char* c_source = source.c_str();
+ const char* c_target = target.c_str();
+ const char* c_data = data.c_str();
+
+ flags = MS_NOATIME | MS_NODEV | MS_NOSUID;
+
+ // Only use MS_DIRSYNC if we're not mounting adopted storage
+ if (!trusted) {
+ flags |= MS_DIRSYNC;
+ }
+
+ flags |= (executable ? 0 : MS_NOEXEC);
+ flags |= (ro ? MS_RDONLY : 0);
+ flags |= (remount ? MS_REMOUNT : 0);
+
+ rc = mount(c_source, c_target, "ext4", flags, c_data);
+ if (portable && rc == 0) {
+ chown(c_target, AID_MEDIA_RW, AID_MEDIA_RW);
+ chmod(c_target, 0775);
+ }
+
+ if (rc && errno == EROFS) {
+ SLOGE("%s appears to be a read only filesystem - retrying mount RO", c_source);
+ flags |= MS_RDONLY;
+ rc = mount(c_source, c_target, "ext4", flags, c_data);
+ }
+
+ return rc;
+}
+
+} // namespace ext4
+} // namespace volmgr
+} // namespace android
diff --git a/volume_manager/fs/Ext4.h b/volume_manager/fs/Ext4.h
new file mode 100644
index 0000000..01add43
--- /dev/null
+++ b/volume_manager/fs/Ext4.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ * Copyright (C) 2019 The LineageOS Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef VOLMGR_EXT4_H
+#define VOLMGR_EXT4_H
+
+#include <utils/Errors.h>
+
+#include <string>
+
+namespace android {
+namespace volmgr {
+namespace ext4 {
+
+status_t Mount(const std::string& source, const std::string& target, bool ro, bool remount,
+ bool executable, const std::string& opts = "", bool trusted = false,
+ bool portable = false);
+
+} // namespace ext4
+} // namespace volmgr
+} // namespace android
+
+#endif
diff --git a/volume_manager/fs/F2fs.cpp b/volume_manager/fs/F2fs.cpp
new file mode 100644
index 0000000..fb50fb3
--- /dev/null
+++ b/volume_manager/fs/F2fs.cpp
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2019 The LineageOS Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "F2fs.h"
+#include "Utils.h"
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <private/android_filesystem_config.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <string>
+#include <vector>
+
+using android::base::StringPrintf;
+
+namespace android {
+namespace volmgr {
+namespace f2fs {
+
+status_t Mount(const std::string& source, const std::string& target,
+ const std::string& opts /* = "" */, bool trusted, bool portable) {
+ std::string data(opts);
+
+ if (portable) {
+ if (!data.empty()) {
+ data += ",";
+ }
+ data += "context=u:object_r:sdcard_posix:s0";
+ }
+
+ const char* c_source = source.c_str();
+ const char* c_target = target.c_str();
+ const char* c_data = data.c_str();
+
+ unsigned long flags = MS_NOATIME | MS_NODEV | MS_NOSUID;
+
+ // Only use MS_DIRSYNC if we're not mounting adopted storage
+ if (!trusted) {
+ flags |= MS_DIRSYNC;
+ }
+
+ int res = mount(c_source, c_target, "f2fs", flags, c_data);
+ if (portable && res == 0) {
+ chown(c_target, AID_MEDIA_RW, AID_MEDIA_RW);
+ chmod(c_target, 0775);
+ }
+
+ if (res != 0) {
+ PLOG(ERROR) << "Failed to mount " << source;
+ if (errno == EROFS) {
+ res = mount(c_source, c_target, "f2fs", flags | MS_RDONLY, c_data);
+ if (res != 0) {
+ PLOG(ERROR) << "Failed to mount read-only " << source;
+ }
+ }
+ }
+
+ return res;
+}
+
+} // namespace f2fs
+} // namespace volmgr
+} // namespace android
diff --git a/volume_manager/fs/F2fs.h b/volume_manager/fs/F2fs.h
new file mode 100644
index 0000000..e96b39f
--- /dev/null
+++ b/volume_manager/fs/F2fs.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2019 The LineageOS Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef VOLMGR_F2FS_H
+#define VOLMGR_F2FS_H
+
+#include <utils/Errors.h>
+
+#include <string>
+
+namespace android {
+namespace volmgr {
+namespace f2fs {
+
+status_t Mount(const std::string& source, const std::string& target, const std::string& opts = "",
+ bool trusted = false, bool portable = false);
+
+} // namespace f2fs
+} // namespace volmgr
+} // namespace android
+
+#endif
diff --git a/volume_manager/fs/Ntfs.cpp b/volume_manager/fs/Ntfs.cpp
new file mode 100644
index 0000000..45661ec
--- /dev/null
+++ b/volume_manager/fs/Ntfs.cpp
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2019 The LineageOS Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Ntfs.h"
+#include "Utils.h"
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+
+#include <string>
+#include <vector>
+
+#include <sys/mount.h>
+
+using android::base::StringPrintf;
+
+namespace android {
+namespace volmgr {
+namespace ntfs {
+
+static const char* kMountPath = "/sbin/mount.ntfs";
+
+status_t Mount(const std::string& source, const std::string& target, bool ro, bool remount,
+ bool executable, int ownerUid, int ownerGid, int permMask) {
+ char mountData[255];
+
+ const char* c_source = source.c_str();
+ const char* c_target = target.c_str();
+
+ sprintf(mountData,
+ "utf8,uid=%d,gid=%d,fmask=%o,dmask=%o,"
+ "shortname=mixed,nodev,nosuid,dirsync",
+ ownerUid, ownerGid, permMask, permMask);
+
+ if (!executable) strlcat(mountData, ",noexec", sizeof(mountData));
+ if (ro) strlcat(mountData, ",ro", sizeof(mountData));
+ if (remount) strlcat(mountData, ",remount", sizeof(mountData));
+
+ std::vector<std::string> cmd;
+ cmd.push_back(kMountPath);
+ cmd.push_back("-o");
+ cmd.push_back(mountData);
+ cmd.push_back(c_source);
+ cmd.push_back(c_target);
+
+ return ForkExecvp(cmd);
+}
+
+} // namespace ntfs
+} // namespace volmgr
+} // namespace android
diff --git a/volume_manager/fs/Ntfs.h b/volume_manager/fs/Ntfs.h
new file mode 100644
index 0000000..e6d152a
--- /dev/null
+++ b/volume_manager/fs/Ntfs.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2019 The LineageOS Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_VOLD_NTFS_H
+#define ANDROID_VOLD_NTFS_H
+
+#include <utils/Errors.h>
+
+#include <string>
+
+namespace android {
+namespace volmgr {
+namespace ntfs {
+
+status_t Mount(const std::string& source, const std::string& target, bool ro, bool remount,
+ bool executable, int ownerUid, int ownerGid, int permMask);
+
+} // namespace ntfs
+} // namespace volmgr
+} // namespace android
+
+#endif
diff --git a/volume_manager/fs/Vfat.cpp b/volume_manager/fs/Vfat.cpp
new file mode 100644
index 0000000..395664a
--- /dev/null
+++ b/volume_manager/fs/Vfat.cpp
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2019 The LineageOS Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <linux/fs.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <linux/kdev_t.h>
+
+#define LOG_TAG "Vold"
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <cutils/log.h>
+#include <cutils/properties.h>
+#include <selinux/selinux.h>
+
+#include <logwrap/logwrap.h>
+
+#include "Utils.h"
+#include "Vfat.h"
+
+using android::base::StringPrintf;
+
+namespace android {
+namespace volmgr {
+namespace vfat {
+
+status_t Mount(const std::string& source, const std::string& target, bool ro, bool remount,
+ bool executable, int ownerUid, int ownerGid, int permMask, bool createLost) {
+ int rc;
+ unsigned long flags;
+ char mountData[255];
+
+ const char* c_source = source.c_str();
+ const char* c_target = target.c_str();
+
+ flags = MS_NODEV | MS_NOSUID | MS_DIRSYNC | MS_NOATIME;
+
+ flags |= (executable ? 0 : MS_NOEXEC);
+ flags |= (ro ? MS_RDONLY : 0);
+ flags |= (remount ? MS_REMOUNT : 0);
+
+ snprintf(mountData, sizeof(mountData), "utf8,uid=%d,gid=%d,fmask=%o,dmask=%o,shortname=mixed",
+ ownerUid, ownerGid, permMask, permMask);
+
+ rc = mount(c_source, c_target, "vfat", flags, mountData);
+
+ if (rc && errno == EROFS) {
+ SLOGE("%s appears to be a read only filesystem - retrying mount RO", c_source);
+ flags |= MS_RDONLY;
+ rc = mount(c_source, c_target, "vfat", flags, mountData);
+ }
+
+ if (rc == 0 && createLost) {
+ char* lost_path;
+ asprintf(&lost_path, "%s/LOST.DIR", c_target);
+ if (access(lost_path, F_OK)) {
+ /*
+ * Create a LOST.DIR in the root so we have somewhere to put
+ * lost cluster chains (fsck_msdos doesn't currently do this)
+ */
+ if (mkdir(lost_path, 0755)) {
+ SLOGE("Unable to create LOST.DIR (%s)", strerror(errno));
+ }
+ }
+ free(lost_path);
+ }
+
+ return rc;
+}
+
+} // namespace vfat
+} // namespace volmgr
+} // namespace android
diff --git a/volume_manager/fs/Vfat.h b/volume_manager/fs/Vfat.h
new file mode 100644
index 0000000..fc1c50b
--- /dev/null
+++ b/volume_manager/fs/Vfat.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2019 The LineageOS Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef VOLMGR_VFAT_H
+#define VOLMGR_VFAT_H
+
+#include <utils/Errors.h>
+
+#include <string>
+
+namespace android {
+namespace volmgr {
+namespace vfat {
+
+status_t Mount(const std::string& source, const std::string& target, bool ro, bool remount,
+ bool executable, int ownerUid, int ownerGid, int permMask, bool createLost);
+
+} // namespace vfat
+} // namespace volmgr
+} // namespace android
+
+#endif
diff --git a/volume_manager/include/volume_manager/ResponseCode.h b/volume_manager/include/volume_manager/ResponseCode.h
new file mode 100644
index 0000000..47b1401
--- /dev/null
+++ b/volume_manager/include/volume_manager/ResponseCode.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2019 The LineageOS Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _RESPONSECODE_H
+#define _RESPONSECODE_H
+
+class ResponseCode {
+ public:
+ // 100 series - Requestion action was initiated; expect another reply
+ // before proceeding with a new command.
+ static const int ActionInitiated = 100;
+
+ static const int VolumeListResult = 110;
+ static const int AsecListResult = 111;
+ static const int StorageUsersListResult = 112;
+ static const int CryptfsGetfieldResult = 113;
+
+ // 200 series - Requested action has been successfully completed
+ static const int CommandOkay = 200;
+ static const int ShareStatusResult = 210;
+ static const int AsecPathResult = 211;
+ static const int ShareEnabledResult = 212;
+ static const int PasswordTypeResult = 213;
+
+ // 400 series - The command was accepted but the requested action
+ // did not take place.
+ static const int OperationFailed = 400;
+ static const int OpFailedNoMedia = 401;
+ static const int OpFailedMediaBlank = 402;
+ static const int OpFailedMediaCorrupt = 403;
+ static const int OpFailedVolNotMounted = 404;
+ static const int OpFailedStorageBusy = 405;
+ static const int OpFailedStorageNotFound = 406;
+
+ // 500 series - The command was not accepted and the requested
+ // action did not take place.
+ static const int CommandSyntaxError = 500;
+ static const int CommandParameterError = 501;
+ static const int CommandNoPermission = 502;
+
+ // 600 series - Unsolicited broadcasts
+ static const int UnsolicitedInformational = 600;
+ static const int VolumeStateChange = 605;
+ static const int VolumeMountFailedBlank = 610;
+ static const int VolumeMountFailedDamaged = 611;
+ static const int VolumeMountFailedNoMedia = 612;
+ static const int VolumeUuidChange = 613;
+ static const int VolumeUserLabelChange = 614;
+
+ static const int ShareAvailabilityChange = 620;
+
+ static const int VolumeDiskInserted = 630;
+ static const int VolumeDiskRemoved = 631;
+ static const int VolumeBadRemoval = 632;
+
+ static const int DiskCreated = 640;
+ static const int DiskSizeChanged = 641;
+ static const int DiskLabelChanged = 642;
+ static const int DiskScanned = 643;
+ static const int DiskSysPathChanged = 644;
+ static const int DiskDestroyed = 649;
+
+ static const int VolumeCreated = 650;
+ static const int VolumeStateChanged = 651;
+ static const int VolumeFsTypeChanged = 652;
+ static const int VolumeFsUuidChanged = 653;
+ static const int VolumeFsLabelChanged = 654;
+ static const int VolumePathChanged = 655;
+ static const int VolumeDestroyed = 659;
+
+ static const int MoveStatus = 660;
+ static const int BenchmarkResult = 661;
+ static const int TrimResult = 662;
+
+ static int convertFromErrno();
+};
+#endif
diff --git a/volume_manager/include/volume_manager/VolumeManager.h b/volume_manager/include/volume_manager/VolumeManager.h
new file mode 100644
index 0000000..e5a65d4
--- /dev/null
+++ b/volume_manager/include/volume_manager/VolumeManager.h
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2019 The LineageOS Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _VOLMGR_VOLUME_MANAGER_H
+#define _VOLMGR_VOLUME_MANAGER_H
+
+#include <fnmatch.h>
+#include <pthread.h>
+#include <stdlib.h>
+
+#include <list>
+#include <mutex>
+#include <string>
+
+struct selabel_handle;
+class NetlinkManager;
+class NetlinkEvent;
+
+class VolumeWatcher {
+ public:
+ virtual ~VolumeWatcher(void) {}
+ virtual void handleEvent(int code, const std::vector<std::string>& argv) = 0;
+};
+
+namespace android {
+namespace volmgr {
+
+class Disk;
+class VolumeBase;
+
+class VolumeInfo {
+ public:
+ explicit VolumeInfo(const VolumeBase* vol);
+
+ std::string mId;
+ std::string mLabel;
+ std::string mPath;
+};
+
+class VolumeManager {
+ private:
+ VolumeManager(const VolumeManager&);
+ VolumeManager& operator=(const VolumeManager&);
+
+ public:
+ static VolumeManager* Instance(void);
+
+ private:
+ static VolumeManager* sInstance;
+
+ public:
+ class DiskSource {
+ public:
+ DiskSource(const std::string& sysPattern, const std::string& nickname, int partnum,
+ int flags, const std::string& fstype, const std::string mntopts)
+ : mSysPattern(sysPattern),
+ mNickname(nickname),
+ mPartNum(partnum),
+ mFlags(flags),
+ mFsType(fstype),
+ mMntOpts(mntopts) {}
+
+ bool matches(const std::string& sysPath) {
+ return !fnmatch(mSysPattern.c_str(), sysPath.c_str(), 0);
+ }
+
+ const std::string& getNickname() { return mNickname; }
+ int getPartNum() { return mPartNum; }
+ int getFlags() { return mFlags; }
+ const std::string& getFsType() { return mFsType; }
+ const std::string& getMntOpts() { return mMntOpts; }
+
+ private:
+ std::string mSysPattern;
+ std::string mNickname;
+ int mPartNum;
+ int mFlags;
+ std::string mFsType;
+ std::string mMntOpts;
+ };
+
+ public:
+ VolumeManager(void);
+ ~VolumeManager(void);
+
+ bool start(VolumeWatcher* watcher);
+ void stop(void);
+
+ bool reset(void);
+ bool unmountAll(void);
+
+ void getVolumeInfo(std::vector<VolumeInfo>& info);
+
+ VolumeBase* findVolume(const std::string& id);
+
+ bool volumeMount(const std::string& id);
+ bool volumeUnmount(const std::string& id, bool detach = false);
+ bool volumeFormat(const std::string& id, const std::string& fsType);
+
+ public:
+ void addDiskSource(DiskSource* source);
+ void handleBlockEvent(NetlinkEvent* evt);
+
+ void notifyEvent(int code);
+ void notifyEvent(int code, const std::string& arg);
+ void notifyEvent(int code, const std::vector<std::string>& argv);
+
+ private:
+ VolumeWatcher* mWatcher;
+ NetlinkManager* mNetlinkManager;
+ std::mutex mLock;
+ VolumeBase* mInternalEmulated;
+ std::list<DiskSource*> mDiskSources;
+ std::list<Disk*> mDisks;
+};
+
+} // namespace volmgr
+} // namespace android
+
+#endif
diff --git a/volume_manager/sehandle.h b/volume_manager/sehandle.h
new file mode 100644
index 0000000..d9e969d
--- /dev/null
+++ b/volume_manager/sehandle.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (C) 2019 The LineageOS Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _SEHANDLE_H
+#define _SEHANDLE_H
+
+#include <selinux/android.h>
+
+extern struct selabel_handle* sehandle;
+
+#endif