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:
* Replace security_context_t type
security_context_t has been marked as deprecated in libselinux from
version 3.2. Update to the `char*` type.
Bug: 190808996
Test: m
Change-Id: I6f40e161251c79893d41e12c368715736578aacc
* 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
diff --git a/Android.bp b/Android.bp
index 9b2f80f..a23a885 100644
--- a/Android.bp
+++ b/Android.bp
@@ -105,6 +105,7 @@
"liblog",
"libprotobuf-cpp-lite",
"libziparchive",
+ "libvolume_manager",
],
static_libs: [
@@ -159,6 +160,7 @@
srcs: [
"recovery_main.cpp",
+ "volclient.cpp",
],
shared_libs: [
diff --git a/etc/init.rc b/etc/init.rc
index d94d0f6..29ee29a 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 1aae38a..1f88eef 100644
--- a/recovery.cpp
+++ b/recovery.cpp
@@ -43,6 +43,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"
@@ -62,6 +63,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";
@@ -198,6 +203,47 @@
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]);
+ }
+ }
+ 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{
@@ -428,7 +474,6 @@
switch (chosen_action) {
case Device::MENU_BASE:
- case Device::MENU_UPDATE:
case Device::MENU_WIPE:
case Device::MENU_ADVANCED:
goto change_menu;
@@ -493,28 +538,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;
}
@@ -522,6 +561,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()) {
@@ -744,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);
@@ -938,5 +984,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 c998b71..5348c68 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 34c71b0..f4dc2c5 100644
--- a/recovery_ui/screen_ui.cpp
+++ b/recovery_ui/screen_ui.cpp
@@ -1401,7 +1401,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();
@@ -1449,6 +1450,7 @@
bool visible = IsTextVisible();
action = key_handler(evt.key(), visible);
}
+
if (action < 0) {
switch (action) {
case Device::kHighlightUp:
@@ -1481,13 +1483,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;
}
}
@@ -1500,13 +1507,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 6006e00..1b6bdf4 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"
@@ -492,6 +493,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..0c5f50b
--- /dev/null
+++ b/volume_manager/Android.bp
@@ -0,0 +1,60 @@
+//
+// 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",
+ "libdiskconfig",
+ "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..832d61b
--- /dev/null
+++ b/volume_manager/Disk.cpp
@@ -0,0 +1,424 @@
+/*
+ * 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 <diskconfig/diskconfig.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..6f5cd01
--- /dev/null
+++ b/volume_manager/DiskPartition.cpp
@@ -0,0 +1,79 @@
+/*
+ * 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 <diskconfig/diskconfig.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..b8cf4eb
--- /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..0bb4a71
--- /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