summaryrefslogtreecommitdiff
authorBaocheng Sun <baocheng.sun@amlogic.com>2017-09-04 06:15:35 (GMT)
committer Baocheng Sun <baocheng.sun@amlogic.com>2017-09-21 07:19:12 (GMT)
commit178b7fc63dd6534c0a55963b5f179ec11e829089 (patch)
tree8f0eb5feee21ab2151fbf9877720a48a80f48b97
parentc0076c31c0c9c92fe928a43eff4ef1bc5a62fd7c (diff)
downloaddroidvold-178b7fc63dd6534c0a55963b5f179ec11e829089.zip
droidvold-178b7fc63dd6534c0a55963b5f179ec11e829089.tar.gz
droidvold-178b7fc63dd6534c0a55963b5f179ec11e829089.tar.bz2
droidvold: a volume Daemon [1/4]
PD#147260 Add exfat/ntfs/hfsplus/iso9660/ext4 file system support. Add support for virtual cdrom and srdisk. Add readonly mount option. Without fuse, mount extern volume at /mnt/media_rw/uuid (unique between volumes) Change-Id: I74abf0d79bec9d9ee9da177d024cfe290fe35f6d
Diffstat
-rw-r--r--Android.mk72
-rw-r--r--CommandListener.cpp224
-rw-r--r--CommandListener.h50
-rw-r--r--Disk.cpp479
-rw-r--r--Disk.h124
-rw-r--r--NetlinkHandler.cpp57
-rw-r--r--NetlinkHandler.h34
-rw-r--r--NetlinkManager.cpp109
-rw-r--r--NetlinkManager.h48
-rw-r--r--Process.cpp222
-rw-r--r--Process.h44
-rw-r--r--PublicVolume.cpp299
-rw-r--r--PublicVolume.h91
-rw-r--r--ResponseCode.cpp42
-rw-r--r--ResponseCode.h81
-rw-r--r--Utils.cpp787
-rw-r--r--Utils.h167
-rw-r--r--VoldCommand.cpp21
-rw-r--r--VoldCommand.h28
-rw-r--r--VolumeBase.cpp255
-rw-r--r--VolumeBase.h159
-rw-r--r--VolumeManager.cpp366
-rw-r--r--VolumeManager.h118
-rw-r--r--droidvold.rc7
-rw-r--r--dvdc.cpp178
-rw-r--r--fs/Exfat.cpp215
-rw-r--r--fs/Exfat.h37
-rw-r--r--fs/Ext4.cpp186
-rw-r--r--fs/Ext4.h41
-rw-r--r--fs/F2fs.cpp82
-rw-r--r--fs/F2fs.h38
-rw-r--r--fs/Hfsplus.cpp152
-rw-r--r--fs/Hfsplus.h37
-rw-r--r--fs/Iso9660.cpp87
-rw-r--r--fs/Iso9660.h37
-rw-r--r--fs/Ntfs.cpp249
-rw-r--r--fs/Ntfs.h38
-rw-r--r--fs/Vfat.cpp209
-rw-r--r--fs/Vfat.h40
-rw-r--r--main.cpp247
-rw-r--r--secontext.cpp22
-rw-r--r--secontext.h25
-rw-r--r--sehandle.h24
43 files changed, 5828 insertions, 0 deletions
diff --git a/Android.mk b/Android.mk
new file mode 100644
index 0000000..b801242
--- a/dev/null
+++ b/Android.mk
@@ -0,0 +1,72 @@
+LOCAL_PATH:= $(call my-dir)
+
+common_src_files := \
+ VolumeManager.cpp \
+ CommandListener.cpp \
+ VoldCommand.cpp \
+ NetlinkManager.cpp \
+ NetlinkHandler.cpp \
+ Process.cpp \
+ fs/Ext4.cpp \
+ fs/Vfat.cpp \
+ fs/Ntfs.cpp \
+ fs/Exfat.cpp \
+ fs/Hfsplus.cpp \
+ fs/Iso9660.cpp \
+ Disk.cpp \
+ VolumeBase.cpp \
+ PublicVolume.cpp \
+ ResponseCode.cpp \
+ Utils.cpp \
+ secontext.cpp \
+
+common_shared_libraries := \
+ libsysutils \
+ libcutils \
+ liblog \
+ liblogwrap \
+ libext4_utils \
+ libselinux \
+ libutils \
+ libbase
+
+common_static_libraries := \
+ libfs_mgr \
+
+vold_conlyflags := -std=c11
+vold_cflags := -Werror -Wall -Wno-missing-field-initializers -Wno-unused-variable -Wno-unused-parameter
+
+include $(CLEAR_VARS)
+
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+LOCAL_MODULE := droidvold
+LOCAL_CLANG := true
+LOCAL_SRC_FILES := \
+ main.cpp \
+ $(common_src_files)
+
+LOCAL_INIT_RC := droidvold.rc
+
+LOCAL_C_INCLUDES := $(common_c_includes)
+LOCAL_CFLAGS := $(vold_cflags)
+LOCAL_CONLYFLAGS := $(vold_conlyflags)
+
+LOCAL_SHARED_LIBRARIES := $(common_shared_libraries)
+LOCAL_STATIC_LIBRARIES := $(common_static_libraries)
+LOCAL_REQUIRED_MODULES := $(required_modules)
+LOCAL_PROPRIETARY_MODULE := true
+
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+LOCAL_CLANG := true
+LOCAL_SRC_FILES := dvdc.cpp
+LOCAL_MODULE := dvdc
+LOCAL_SHARED_LIBRARIES := libcutils libbase
+LOCAL_CFLAGS := $(vold_cflags)
+LOCAL_CONLYFLAGS := $(vold_conlyflags)
+LOCAL_PROPRIETARY_MODULE := true
+
+include $(BUILD_EXECUTABLE)
diff --git a/CommandListener.cpp b/CommandListener.cpp
new file mode 100644
index 0000000..18eddfc
--- a/dev/null
+++ b/CommandListener.cpp
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2008 The Android Open Source 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 <stdlib.h>
+#include <sys/mount.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <fs_mgr.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <ctype.h>
+
+#define LOG_TAG "DroidVoldCmdListener"
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <cutils/fs.h>
+#include <cutils/log.h>
+
+#include <sysutils/SocketClient.h>
+#include <private/android_filesystem_config.h>
+
+#include "CommandListener.h"
+#include "VolumeManager.h"
+#include "VolumeBase.h"
+#include "ResponseCode.h"
+#include "Process.h"
+
+#define DUMP_ARGS 0
+
+CommandListener::CommandListener() :
+ FrameworkListener("droidvold", true) {
+ registerCmd(new VolumeCmd());
+#ifdef HAS_VIRTUAL_CDROM
+ registerCmd(new LoopCmd());
+#endif
+}
+
+#if DUMP_ARGS
+void CommandListener::dumpArgs(int argc, char **argv, int argObscure) {
+ char buffer[4096];
+ char *p = buffer;
+
+ memset(buffer, 0, sizeof(buffer));
+ int i;
+ for (i = 0; i < argc; i++) {
+ unsigned int len = strlen(argv[i]) + 1; // Account for space
+ if (i == argObscure) {
+ len += 2; // Account for {}
+ }
+ if (((p - buffer) + len) < (long)(sizeof(buffer)-1)) {
+ if (i == argObscure) {
+ *p++ = '{';
+ *p++ = '}';
+ *p++ = ' ';
+ continue;
+ }
+ strcpy(p, argv[i]);
+ p+= strlen(argv[i]);
+ if (i != (argc -1)) {
+ *p++ = ' ';
+ }
+ }
+ }
+ SLOGD("%s", buffer);
+}
+#else
+void CommandListener::dumpArgs(int /*argc*/, char ** /*argv*/, int /*argObscure*/) { }
+#endif
+
+int CommandListener::sendGenericOkFail(SocketClient *cli, int cond) {
+ if (!cond) {
+ return cli->sendMsg(ResponseCode::CommandOkay, "Command succeeded", false);
+ } else {
+ return cli->sendMsg(ResponseCode::OperationFailed, "Command failed", false);
+ }
+}
+
+CommandListener::VolumeCmd::VolumeCmd() :
+ VoldCommand("volume") {
+}
+
+int CommandListener::VolumeCmd::runCommand(SocketClient *cli,
+ int argc, char **argv) {
+ dumpArgs(argc, argv, -1);
+
+ if (argc < 2) {
+ cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing Argument", false);
+ return 0;
+ }
+
+ VolumeManager *vm = VolumeManager::Instance();
+ std::lock_guard<std::mutex> lock(vm->getLock());
+
+ // TODO: tease out methods not directly related to volumes
+
+ std::string cmd(argv[1]);
+ if (cmd == "reset") {
+ return sendGenericOkFail(cli, vm->reset());
+
+ } else if (cmd == "shutdown") {
+ return sendGenericOkFail(cli, vm->shutdown());
+
+ } else if (cmd == "debug") {
+ return sendGenericOkFail(cli, vm->setDebug(true));
+
+ } else if (cmd == "mkdirs" && argc > 2) {
+ // mkdirs [path]
+ return sendGenericOkFail(cli, vm->mkdirs(argv[2]));
+
+ } else if (cmd == "mount" && argc > 2) {
+ // mount [volId] [flags] [user]
+ std::string id(argv[2]);
+ auto vol = vm->findVolume(id);
+ if (vol == nullptr) {
+ return cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown volume", false);
+ }
+
+ int mountFlags = (argc > 3) ? atoi(argv[3]) : 0;
+ userid_t mountUserId = (argc > 4) ? atoi(argv[4]) : -1;
+
+ vol->setMountFlags(mountFlags);
+ vol->setMountUserId(mountUserId);
+
+ int res = vol->mount();
+
+ return sendGenericOkFail(cli, res);
+
+ } else if (cmd == "unmount" && argc > 2) {
+ // unmount [volId]
+ std::string id(argv[2]);
+ auto vol = vm->findVolume(id);
+ if (vol == nullptr) {
+ return cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown volume", false);
+ }
+
+ return sendGenericOkFail(cli, vol->unmount());
+
+ } else if (cmd == "format" && argc > 3) {
+ // format [volId] [fsType|auto]
+ std::string id(argv[2]);
+ std::string fsType(argv[3]);
+ auto vol = vm->findVolume(id);
+ if (vol == nullptr) {
+ return cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown volume", false);
+ }
+
+ return sendGenericOkFail(cli, vol->format(fsType));
+
+ }
+
+ return cli->sendMsg(ResponseCode::CommandSyntaxError, nullptr, false);
+}
+
+#ifdef HAS_VIRTUAL_CDROM
+CommandListener::LoopCmd::LoopCmd() :
+ VoldCommand("loop") {
+}
+
+int CommandListener::LoopCmd::runCommand(SocketClient *cli,
+ int argc, char **argv) {
+ dumpArgs(argc, argv, -1);
+ if (argc < 2) {
+ cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing Argument", false);
+ return 0;
+ }
+
+ VolumeManager *vm = VolumeManager::Instance();
+ int rc = 0;
+ if (!strcmp(argv[1], "mount")) {
+ if (argc != 3) {
+ cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: loop mount <path>", false);
+ return 0;
+ }
+ rc = vm->mountloop(argv[2]);
+ } else if (!strcmp(argv[1], "unmount")) {
+ if (argc < 2 || argc > 3) {
+ cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: loop unmount [force]", false);
+ return 0;
+ }
+
+ bool force = false;
+ if (argc == 3 && !strcmp(argv[2], "force")) {
+ force = true;
+ }
+ rc = vm->unmountloop(force);
+ } else {
+ cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown loop cmd", false);
+ return 0;
+ }
+
+ if (!rc) {
+ cli->sendMsg(ResponseCode::CommandOkay, "loop operation succeeded", false);
+ } else {
+ int erno = errno;
+ rc = ResponseCode::convertFromErrno();
+ cli->sendMsg(rc, "loop operation failed", true);
+ }
+
+ return 0;
+}
+#endif
diff --git a/CommandListener.h b/CommandListener.h
new file mode 100644
index 0000000..437b99c
--- a/dev/null
+++ b/CommandListener.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2008 The Android Open Source 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 _COMMANDLISTENER_H__
+#define _COMMANDLISTENER_H__
+
+#include <sysutils/FrameworkListener.h>
+#include <utils/Errors.h>
+#include "VoldCommand.h"
+
+class CommandListener : public FrameworkListener {
+public:
+ CommandListener();
+ virtual ~CommandListener() {}
+
+private:
+ static void dumpArgs(int argc, char **argv, int argObscure);
+ static int sendGenericOkFail(SocketClient *cli, int cond);
+
+ class VolumeCmd : public VoldCommand {
+ public:
+ VolumeCmd();
+ virtual ~VolumeCmd() {}
+ int runCommand(SocketClient *c, int argc, char ** argv);
+ };
+
+#ifdef HAS_VIRTUAL_CDROM
+ class LoopCmd : public VoldCommand {
+ public:
+ LoopCmd();
+ virtual ~LoopCmd() {}
+ int runCommand(SocketClient *c, int argc, char ** argv);
+ };
+#endif
+};
+
+#endif
diff --git a/Disk.cpp b/Disk.cpp
new file mode 100644
index 0000000..1b697d6
--- a/dev/null
+++ b/Disk.cpp
@@ -0,0 +1,479 @@
+/*
+ * Copyright (C) 2015 The Android Open Source 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 "Utils.h"
+#include "VolumeBase.h"
+#include "VolumeManager.h"
+#include "ResponseCode.h"
+
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <android-base/logging.h>
+
+#include <vector>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mount.h>
+
+using android::base::ReadFileToString;
+using android::base::WriteStringToFile;
+using android::base::StringPrintf;
+
+namespace android {
+namespace droidvold {
+
+static const char* kSgdiskPath = "/system/bin/sgdisk";
+static const char* kSgdiskToken = " \t\n";
+
+static const char* kSysfsMmcMaxMinors = "/sys/module/mmcblk/parameters/perdev_minors";
+
+static const unsigned int kMajorBlockScsiA = 8;
+static const unsigned int kMajorBlockSr = 11;
+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* kGptAndroidMeta = "19A710A2-B3CA-11E4-B026-10604B889DCF";
+static const char* kGptAndroidExpand = "193D1EA4-B3CA-11E4-B075-10604B889DCF";
+
+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), mJustPartitioned(false) {
+ mId = StringPrintf("disk:%u,%u", major(device), minor(device));
+ mEventPath = eventPath;
+ mSysPath = StringPrintf("/sys/%s", eventPath.c_str());
+ mDevPath = StringPrintf("/dev/block/droidvold/%s", mId.c_str());
+
+ mSrdisk = (!strncmp(nickname.c_str(), "sr", 2)) ? true : false;
+ CreateDeviceNode(mDevPath, mDevice);
+}
+
+Disk::~Disk() {
+ CHECK(!mCreated);
+ DestroyDeviceNode(mDevPath);
+}
+
+std::shared_ptr<VolumeBase> Disk::findVolume(const std::string& id) {
+ for (auto vol : mVolumes) {
+ if (vol->getId() == id) {
+ return vol;
+ }
+ auto stackedVol = vol->findVolume(id);
+ if (stackedVol != nullptr) {
+ return stackedVol;
+ }
+ }
+ return nullptr;
+}
+
+void Disk::listVolumes(VolumeBase::Type type, std::list<std::string>& list) {
+ for (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;
+ notifyEvent(ResponseCode::DiskCreated, StringPrintf("%d", mFlags));
+ // do nothing when srdisk is created
+ if (!mSrdisk) {
+ readMetadata();
+ readPartitions();
+ }
+ return OK;
+}
+
+status_t Disk::destroy() {
+ CHECK(mCreated);
+ destroyAllVolumes();
+ notifyEvent(ResponseCode::DiskDestroyed);
+ mCreated = false;
+ return OK;
+}
+
+void Disk::handleJustPublicPhysicalDevice(
+ const std::string& physicalDevName) {
+ auto vol = std::shared_ptr<VolumeBase>(new PublicVolume(physicalDevName));
+ if (mJustPartitioned) {
+ LOG(DEBUG) << "Device just partitioned; silently formatting";
+ vol->setSilent(true);
+ vol->create();
+ vol->format("auto");
+ vol->destroy();
+ vol->setSilent(false);
+ }
+
+ mVolumes.push_back(vol);
+ vol->setDiskId(getId());
+ vol->setSysPath(getSysPath());
+ vol->create();
+ //vol->mount();
+}
+
+void Disk::createPublicVolume(dev_t device) {
+ auto vol = std::shared_ptr<VolumeBase>(new PublicVolume(device));
+ if (mJustPartitioned) {
+ LOG(DEBUG) << "Device just partitioned; silently formatting";
+ vol->setSilent(true);
+ vol->create();
+ vol->format("auto");
+ vol->destroy();
+ vol->setSilent(false);
+ }
+
+ mVolumes.push_back(vol);
+ vol->setDiskId(getId());
+ vol->setSysPath(getSysPath());
+ vol->create();
+ //vol->mount();
+}
+
+void Disk::destroyAllVolumes() {
+ for (auto vol : mVolumes) {
+ vol->destroy();
+ }
+ mVolumes.clear();
+}
+
+status_t Disk::readMetadata() {
+ 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 kMajorBlockSr:
+ 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;
+ }
+ }
+
+ notifyEvent(ResponseCode::DiskSizeChanged, StringPrintf("%" PRIu64, mSize));
+ notifyEvent(ResponseCode::DiskLabelChanged, mLabel);
+ notifyEvent(ResponseCode::DiskSysPathChanged, mSysPath);
+ return OK;
+}
+
+status_t Disk::readPartitions() {
+ if (mSrdisk) {
+ // srdisk has no partiton concept.
+ LOG(INFO) << "srdisk try entire disk as fake partition";
+ createPublicVolume(mDevice);
+ return OK;
+ }
+
+ int8_t maxMinors = getMaxMinors();
+ if (maxMinors < 0) {
+ return -ENOTSUP;
+ }
+
+ destroyAllVolumes();
+
+ // Parse partition table
+
+ std::vector<std::string> cmd;
+ cmd.push_back(kSgdiskPath);
+ cmd.push_back("--android-dump");
+ cmd.push_back(mDevPath);
+
+ std::vector<std::string> output;
+ status_t res = ForkExecvp(cmd, output);
+ if (res != OK) {
+ LOG(WARNING) << "sgdisk failed to scan " << mDevPath;
+ notifyEvent(ResponseCode::DiskScanned);
+ mJustPartitioned = false;
+ return res;
+ }
+
+ Table table = Table::kUnknown;
+ bool foundParts = false;
+ std::string physicalDevName;
+ for (auto line : output) {
+ char* cline = (char*) line.c_str();
+ char* token = strtok(cline, kSgdiskToken);
+ if (token == nullptr) continue;
+
+ if (!strcmp(token, "DISK")) {
+ const char* type = strtok(nullptr, kSgdiskToken);
+ if (!strcmp(type, "mbr")) {
+ table = Table::kMbr;
+ } else if (!strcmp(type, "gpt")) {
+ table = Table::kGpt;
+ }
+ } else if (!strcmp(token, "PART")) {
+ foundParts = true;
+ int i = strtol(strtok(nullptr, kSgdiskToken), nullptr, 10);
+ if (i <= 0 || i > maxMinors) {
+ LOG(WARNING) << mId << " is ignoring partition " << i
+ << " beyond max supported devices";
+ continue;
+ }
+ dev_t partDevice = makedev(major(mDevice), minor(mDevice) + i);
+
+ if (table == Table::kMbr) {
+ const char* type = strtok(nullptr, kSgdiskToken);
+
+ if (IsJustPhysicalDevice(mSysPath, physicalDevName)) {
+ LOG(INFO) << " here,we don't create public:xx,xx for physical device only!";
+ handleJustPublicPhysicalDevice(physicalDevName);
+ break;
+ }
+
+ // support mort than 16 partitions
+ if (i > 15)
+ getPhysicalDev(partDevice, mSysPath, i);
+
+ switch (strtol(type, nullptr, 16)) {
+ case 0x06: // FAT16
+ case 0x0b: // W95 FAT32 (LBA)
+ case 0x0c: // W95 FAT32 (LBA)
+ case 0x0e: // W95 FAT16 (LBA)
+
+ case 0x07: // NTFS & EXFAT
+ createPublicVolume(partDevice);
+ break;
+
+ default:
+ // We should still create public volume here
+ // cause some disk table types are not matched above
+ // but can be mounted successfully
+ createPublicVolume(partDevice);
+ LOG(WARNING) << "unsupported table kMbr type " << type;
+ break;
+ }
+ } else if (table == Table::kGpt) {
+ const char* typeGuid = strtok(nullptr, kSgdiskToken);
+ const char* partGuid = strtok(nullptr, kSgdiskToken);
+
+ if (!strcasecmp(typeGuid, kGptBasicData)) {
+ 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) {
+ if (IsJustPhysicalDevice(mSysPath, physicalDevName)) {
+ handleJustPublicPhysicalDevice(physicalDevName);
+ } else {
+ createPublicVolume(mDevice);
+ }
+ } else {
+ LOG(WARNING) << mId << " failed to identify, giving up";
+ }
+ }
+
+ notifyEvent(ResponseCode::DiskScanned);
+ mJustPartitioned = false;
+ return OK;
+}
+
+status_t Disk::unmountAll() {
+ for (auto vol : mVolumes) {
+ vol->unmount();
+ }
+ return OK;
+}
+
+void Disk::notifyEvent(int event) {
+ VolumeManager::Instance()->getBroadcaster()->sendBroadcast(event,
+ getId().c_str(), false);
+}
+
+void Disk::notifyEvent(int event, const std::string& value) {
+ VolumeManager::Instance()->getBroadcaster()->sendBroadcast(event,
+ StringPrintf("%s %s", getId().c_str(), value.c_str()).c_str(), false);
+}
+
+
+bool Disk::isSrdiskMounted() {
+ if (!mSrdisk) {
+ return false;
+ }
+
+ for (auto vol : mVolumes) {
+ return vol->isSrdiskMounted();
+ }
+
+ return false;
+}
+
+int Disk::getMaxMinors() {
+ // Figure out maximum partition devices supported
+ unsigned int majorId = major(mDevice);
+ switch (majorId) {
+ 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 31;
+ }
+ case kMajorBlockMmc: {
+ // Per Documentation/devices.txt this is dynamic
+ std::string tmp;
+ if (!ReadFileToString(kSysfsMmcMaxMinors, &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;
+}
+
+void Disk::getPhysicalDev(dev_t &device, const std:: string& sysPath, int part) {
+ std::string smajor, sminor;
+ std::string physicalDev, lpDev;
+ int major, minor;
+
+ if (part <= 15)
+ return;
+
+ if (GetPhysicalDevice(sysPath, physicalDev) == OK) {
+ lpDev = StringPrintf("%s%d", physicalDev.c_str(), part);
+ if (!access(lpDev.c_str(), F_OK) &&
+ readBlockDevMajorAndMinor(lpDev, smajor, sminor) == OK) {
+ major = atoi(smajor.c_str());
+ minor = atoi(sminor.c_str());
+ device = makedev(major, minor);
+ }
+ }
+}
+
+} // namespace vold
+} // namespace android
diff --git a/Disk.h b/Disk.h
new file mode 100644
index 0000000..e62205d
--- a/dev/null
+++ b/Disk.h
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2015 The Android Open Source 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_DISK_H
+#define ANDROID_VOLD_DISK_H
+
+#include "Utils.h"
+#include "VolumeBase.h"
+
+#include <utils/Errors.h>
+
+#include <vector>
+
+namespace android {
+namespace droidvold {
+
+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,
+ };
+
+ 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; }
+
+ std::shared_ptr<VolumeBase> findVolume(const std::string& id);
+
+ void listVolumes(VolumeBase::Type type, std::list<std::string>& list);
+
+ status_t create();
+ status_t destroy();
+
+ status_t readMetadata();
+ status_t readPartitions();
+
+ status_t unmountAll();
+
+ bool isSrdiskMounted();
+ void notifyEvent(int msg);
+ void notifyEvent(int msg, const std::string& value);
+ void destroyAllVolumes();
+
+private:
+ /* 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 just partitioned and should format all volumes */
+ bool mJustPartitioned;
+ /* Flag indicating is srdisk or not */
+ bool mSrdisk;
+
+ void createPublicVolume(dev_t device);
+ void createPrivateVolume(dev_t device, const std::string& partGuid);
+ void handleJustPublicPhysicalDevice(const std::string& physicalDevName);
+ void getPhysicalDev(dev_t &device, const std:: string& sysPath, int part);
+
+ int getMaxMinors();
+
+ DISALLOW_COPY_AND_ASSIGN(Disk);
+};
+
+} // namespace vold
+} // namespace android
+
+#endif
diff --git a/NetlinkHandler.cpp b/NetlinkHandler.cpp
new file mode 100644
index 0000000..5c3efc4
--- a/dev/null
+++ b/NetlinkHandler.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2008 The Android Open Source 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 <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+
+#define LOG_TAG "droidVold"
+
+#include <cutils/log.h>
+
+#include <sysutils/NetlinkEvent.h>
+#include "NetlinkHandler.h"
+#include "VolumeManager.h"
+
+NetlinkHandler::NetlinkHandler(int listenerSocket) :
+ NetlinkListener(listenerSocket) {
+}
+
+NetlinkHandler::~NetlinkHandler() {
+}
+
+int NetlinkHandler::start() {
+ return this->startListener();
+}
+
+int NetlinkHandler::stop() {
+ return this->stopListener();
+}
+
+void NetlinkHandler::onEvent(NetlinkEvent *evt) {
+ VolumeManager *vm = 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/NetlinkHandler.h b/NetlinkHandler.h
new file mode 100644
index 0000000..00a31c8
--- a/dev/null
+++ b/NetlinkHandler.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2008 The Android Open Source 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:
+ NetlinkHandler(int listenerSocket);
+ virtual ~NetlinkHandler();
+
+ int start(void);
+ int stop(void);
+
+protected:
+ virtual void onEvent(NetlinkEvent *evt);
+};
+#endif
diff --git a/NetlinkManager.cpp b/NetlinkManager.cpp
new file mode 100644
index 0000000..177dba8
--- a/dev/null
+++ b/NetlinkManager.cpp
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2008 The Android Open Source 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 <stdio.h>
+#include <errno.h>
+#include <string.h>
+
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/un.h>
+
+#include <linux/netlink.h>
+
+#define LOG_TAG "droidVold"
+
+#include <cutils/log.h>
+
+#include "NetlinkManager.h"
+#include "NetlinkHandler.h"
+
+NetlinkManager *NetlinkManager::sInstance = NULL;
+
+NetlinkManager *NetlinkManager::Instance() {
+ if (!sInstance)
+ sInstance = new NetlinkManager();
+ return sInstance;
+}
+
+NetlinkManager::NetlinkManager() {
+ mBroadcaster = NULL;
+}
+
+NetlinkManager::~NetlinkManager() {
+}
+
+int 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 -1;
+ }
+
+ if (setsockopt(mSock, SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz)) < 0) {
+ SLOGE("Unable to set uevent socket 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 0;
+
+out:
+ close(mSock);
+ return -1;
+}
+
+int NetlinkManager::stop() {
+ int status = 0;
+
+ if (mHandler->stop()) {
+ SLOGE("Unable to stop NetlinkHandler: %s", strerror(errno));
+ status = -1;
+ }
+ delete mHandler;
+ mHandler = NULL;
+
+ close(mSock);
+ mSock = -1;
+
+ return status;
+}
diff --git a/NetlinkManager.h b/NetlinkManager.h
new file mode 100644
index 0000000..9c7ba11
--- a/dev/null
+++ b/NetlinkManager.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2008 The Android Open Source 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/SocketListener.h>
+#include <sysutils/NetlinkListener.h>
+
+class NetlinkHandler;
+
+class NetlinkManager {
+private:
+ static NetlinkManager *sInstance;
+
+private:
+ SocketListener *mBroadcaster;
+ NetlinkHandler *mHandler;
+ int mSock;
+
+public:
+ virtual ~NetlinkManager();
+
+ int start();
+ int stop();
+
+ void setBroadcaster(SocketListener *sl) { mBroadcaster = sl; }
+ SocketListener *getBroadcaster() { return mBroadcaster; }
+
+ static NetlinkManager *Instance();
+
+private:
+ NetlinkManager();
+};
+#endif
diff --git a/Process.cpp b/Process.cpp
new file mode 100644
index 0000000..e68011e
--- a/dev/null
+++ b/Process.cpp
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2008 The Android Open Source 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 <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <ctype.h>
+#include <pwd.h>
+#include <stdlib.h>
+#include <poll.h>
+#include <sys/stat.h>
+#include <signal.h>
+
+#define LOG_TAG "ProcessKiller"
+#include <cutils/log.h>
+
+#include "Process.h"
+
+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, char *buffer, size_t max) {
+ int fd;
+ snprintf(buffer, max, "/proc/%d/cmdline", pid);
+ fd = open(buffer, O_RDONLY | O_CLOEXEC);
+ if (fd < 0) {
+ strcpy(buffer, "???");
+ } else {
+ int length = read(fd, buffer, max - 1);
+ buffer[length] = 0;
+ close(fd);
+ }
+}
+
+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];
+ sprintf(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;
+ strcat(path, de->d_name);
+
+ 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];
+
+ sprintf(buffer, "/proc/%d/maps", pid);
+ file = fopen(buffer, "r");
+ 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];
+
+ sprintf(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;
+}
+
+extern "C" void vold_killProcessesWithOpenFiles(const char *path, int signal) {
+ Process::killProcessesWithOpenFiles(path, signal);
+}
+
+/*
+ * 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);
+ char name[PATH_MAX];
+
+ if (pid == -1)
+ continue;
+ getProcessName(pid, name, sizeof(name));
+
+ char openfile[PATH_MAX];
+
+ if (checkFileDescriptorSymLinks(pid, path, openfile, sizeof(openfile))) {
+ SLOGE("Process %s (%d) has open file %s", name, pid, openfile);
+ } else if (checkFileMaps(pid, path, openfile, sizeof(openfile))) {
+ SLOGE("Process %s (%d) has open filemap for %s", name, pid, openfile);
+ } else if (checkSymLink(pid, path, "cwd")) {
+ SLOGE("Process %s (%d) has cwd within %s", name, pid, path);
+ } else if (checkSymLink(pid, path, "root")) {
+ SLOGE("Process %s (%d) has chroot within %s", name, pid, path);
+ } else if (checkSymLink(pid, path, "exe")) {
+ SLOGE("Process %s (%d) has executable path within %s", name, 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/Process.h b/Process.h
new file mode 100644
index 0000000..62a9313
--- a/dev/null
+++ b/Process.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2008 The Android Open Source 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
+
+#ifdef __cplusplus
+
+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, char *buffer, size_t max);
+private:
+ static int readSymLink(const char *path, char *link, size_t max);
+ static int pathMatchesMountPoint(const char *path, const char *mountPoint);
+};
+
+extern "C" {
+#endif /* __cplusplus */
+ void vold_killProcessesWithOpenFiles(const char *path, int signal);
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/PublicVolume.cpp b/PublicVolume.cpp
new file mode 100644
index 0000000..663c351
--- a/dev/null
+++ b/PublicVolume.cpp
@@ -0,0 +1,299 @@
+/*
+ * Copyright (C) 2015 The Android Open Source 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 "fs/Vfat.h"
+#include "fs/Ntfs.h"
+#include "fs/Exfat.h"
+#include "fs/Hfsplus.h"
+#include "fs/Iso9660.h"
+#include "fs/Ext4.h"
+#include "PublicVolume.h"
+#include "Utils.h"
+#include "VolumeManager.h"
+#include "ResponseCode.h"
+
+#include <android-base/stringprintf.h>
+#include <android-base/logging.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/types.h>
+#include <sys/wait.h>
+
+using android::base::StringPrintf;
+
+namespace android {
+namespace droidvold {
+
+static const char* kFusePath = "/system/bin/sdcard";
+
+static const char* kChownPath = "/system/bin/chown";
+
+PublicVolume::PublicVolume(dev_t device) :
+ VolumeBase(Type::kPublic), mDevice(device), mFusePid(0), mJustPhysicalDev(false) {
+ setId(StringPrintf("public:%u,%u", major(device), minor(device)));
+ mDevPath = StringPrintf("/dev/block/droidvold/%s", getId().c_str());
+ mSrMounted = false;
+}
+
+PublicVolume::PublicVolume(const std::string& physicalDevName) :
+ VolumeBase(Type::kPublic), mFusePid(0), mJustPhysicalDev(true) {
+ setId(physicalDevName);
+ mDevPath = StringPrintf("/dev/block/%s", getId().c_str());
+}
+
+PublicVolume::~PublicVolume() {
+}
+
+status_t PublicVolume::readMetadata() {
+ status_t res = ReadMetadataUntrusted(mDevPath, mFsType, mFsUuid, mFsLabel);
+ notifyEvent(ResponseCode::VolumeFsTypeChanged, mFsType);
+ // TODO: find the Uuid of srdisk
+ // If mFsUuid of publicVolume is empty,
+ // it will cause systemUi crash when it is mounted
+ if (mFsUuid.empty()) {
+ if (major(mDevice) == 11)
+ mFsUuid = "sr0";
+ else
+ mFsUuid = "fakeUuid";
+ }
+
+ notifyEvent(ResponseCode::VolumeFsUuidChanged, mFsUuid);
+ notifyEvent(ResponseCode::VolumeFsLabelChanged, mFsLabel);
+ return res;
+}
+
+status_t PublicVolume::doCreate() {
+ if (mJustPhysicalDev) return 0;
+ return CreateDeviceNode(mDevPath, mDevice);
+}
+
+status_t PublicVolume::doDestroy() {
+ if (mJustPhysicalDev) return 0;
+ return DestroyDeviceNode(mDevPath);
+}
+
+status_t PublicVolume::doMount() {
+ // TODO: expand to support mounting other filesystems
+ readMetadata();
+
+ if (mFsType != "vfat" &&
+ mFsType != "ntfs" &&
+ mFsType != "exfat" &&
+ strncmp(mFsType.c_str(), "ext", 3) &&
+ mFsType != "hfs" &&
+ mFsType != "iso9660" &&
+ mFsType != "udf") {
+ LOG(ERROR) << getId() << " unsupported filesystem " << mFsType;
+ return -EIO;
+ }
+
+ if (!mJustPhysicalDev && mFsType == "vfat") {
+ LOG(DEBUG) << getId() << " vfat will handle by vold";
+ return 0;
+ }
+
+ // Use UUID as stable name, if available
+ std::string stableName = getId();
+ if (!mFsUuid.empty()) {
+ stableName = mFsUuid;
+ }
+ mRawPath = StringPrintf("/mnt/media_rw/%s", stableName.c_str());
+
+ VolumeManager *vm = VolumeManager::Instance();
+ if (vm->isMountpointMounted(mRawPath.c_str())) {
+ LOG(ERROR) << " path:" << mRawPath << " is already mounted";
+ return -EIO;
+ }
+
+ // Check filesystems
+ status_t checkStatus = -1;
+ if (mFsType == "vfat") {
+ checkStatus = vfat::Check(mDevPath);
+ } else if (mFsType == "ntfs") {
+ checkStatus = ntfs::Check(mDevPath.c_str());
+ } else if (mFsType == "exfat") {
+ checkStatus = exfat::Check(mDevPath.c_str());
+ } else if (!strncmp(mFsType.c_str(), "ext", 3)) {
+ // ext2/3/4 check later
+ checkStatus = 0;
+ } else if (mFsType == "hfs") {
+ checkStatus = hfsplus::Check(mDevPath.c_str());
+ } else if (mFsType == "iso9660" || mFsType == "udf") {
+ // iso needn't check
+ checkStatus = iso9660::Check(mDevPath.c_str());
+ }
+
+
+ if (checkStatus) {
+ LOG(ERROR) << getId() << " failed to check filesystem " << mFsType;
+ return -EIO;
+ }
+
+
+ setInternalPath(mRawPath);
+ setPath(mRawPath);
+
+ if (prepareDir(mRawPath, 0700, AID_ROOT, AID_ROOT)) {
+ PLOG(ERROR) << getId() << " failed to create mount points";
+ return -errno;
+ }
+
+ // Mount device
+ status_t mountStatus = -1;
+ std::string logicPartDevPath = mDevPath;
+ if (!mJustPhysicalDev &&
+ (mFsType == "ntfs" || mFsType == "exfat")) {
+ if (GetLogicalPartitionDevice(mDevice, getSysPath(), logicPartDevPath) != OK) {
+ LOG(ERROR) << "failed to get logical partition device for fstype " << mFsType;
+ return -errno;
+ }
+ }
+
+ if (mFsType == "vfat") {
+ mountStatus = vfat::Mount(mDevPath, mRawPath, false, false, false,
+ AID_MEDIA_RW, AID_MEDIA_RW, 0007, true);
+ } else if (mFsType == "ntfs") {
+ mountStatus = ntfs::Mount(logicPartDevPath.c_str(), mRawPath.c_str(), false, false,
+ AID_MEDIA_RW, AID_MEDIA_RW, 0007, true);
+ } else if (mFsType == "exfat") {
+ mountStatus = exfat::Mount(logicPartDevPath.c_str(), mRawPath.c_str(), false, false,
+ AID_MEDIA_RW, AID_MEDIA_RW, 0007, true);
+ } else if (!strncmp(mFsType.c_str(), "ext", 3)) {
+ int res = ext4::Check(logicPartDevPath, mRawPath);
+ if (res == 0 || res == 1) {
+ LOG(DEBUG) << getId() << " passed filesystem check";
+ } else {
+ PLOG(ERROR) << getId() << " failed filesystem check";
+ // return -EIO;
+ }
+
+ mountStatus = ext4::Mount(logicPartDevPath, mRawPath, false, false, true, mFsType);
+ } else if (mFsType == "hfs") {
+ mountStatus = hfsplus::Mount(mDevPath.c_str(), mRawPath.c_str(), false, false,
+ AID_MEDIA_RW, AID_MEDIA_RW, 0007, true);
+ } else if (mFsType == "iso9660" || mFsType == "udf") {
+ if ((mountStatus = iso9660::Mount(mDevPath.c_str(), mRawPath.c_str(), false, false,
+ AID_MEDIA_RW, AID_MEDIA_RW, 0007, true)) == 0)
+ mSrMounted = true;
+ }
+
+ if (mountStatus) {
+ PLOG(ERROR) << " failed to mount " << mDevPath << " as " << mFsType;
+ return -EIO;
+ } else {
+ LOG(INFO) << "successfully mount " << mDevPath << " as " << mFsType;
+ }
+
+ if (!strncmp(mFsType.c_str(), "ext", 3)) {
+ std::vector<std::string> cmd;
+ cmd.push_back(kChownPath);
+ cmd.push_back("-R");
+ cmd.push_back("media_rw:media_rw");
+ cmd.push_back(mRawPath);
+
+ std::vector<std::string> output;
+ status_t res = ForkExecvp(cmd, output);
+ if (res != OK) {
+ LOG(WARNING) << "chown failed " << mRawPath;
+ return res;
+ }
+
+ RestoreconRecursive(mRawPath);
+
+ LOG(VERBOSE) << "Finished restorecon of " << mRawPath;
+ }
+
+ return OK;
+}
+
+status_t PublicVolume::doUnmount() {
+ // Unmount the storage before we kill the FUSE process. If we kill
+ // the FUSE process first, most file system operations will return
+ // ENOTCONN until the unmount completes. This is an exotic and unusual
+ // error code and might cause broken behaviour in applications.
+ KillProcessesUsingPath(getPath());
+
+#ifdef HAS_VIRTUAL_CDROM
+ std::string stableName = getId();
+ if (!mFsUuid.empty()) {
+ stableName = mFsUuid;
+ }
+
+ VolumeManager *vm = VolumeManager::Instance();
+ vm->unmountLoopIfNeed(stableName.c_str());
+#endif
+
+ ForceUnmount(mRawPath);
+
+ if (mFusePid > 0) {
+ kill(mFusePid, SIGTERM);
+ TEMP_FAILURE_RETRY(waitpid(mFusePid, nullptr, 0));
+ mFusePid = 0;
+ }
+
+ rmdir(mRawPath.c_str());
+
+ mRawPath.clear();
+
+ return OK;
+}
+
+status_t PublicVolume::doFormat(const std::string& fsType) {
+ if (fsType == "vfat" || fsType == "auto") {
+ if (WipeBlockDevice(mDevPath) != OK) {
+ LOG(WARNING) << getId() << " failed to wipe";
+ }
+ if (vfat::Format(mDevPath, 0)) {
+ LOG(ERROR) << getId() << " failed to format";
+ return -errno;
+ }
+ } else {
+ LOG(ERROR) << "Unsupported filesystem " << fsType;
+ return -EINVAL;
+ }
+
+ return OK;
+}
+
+status_t PublicVolume::prepareDir(const std::string& path,
+ mode_t mode, uid_t uid, gid_t gid) {
+ if (fs_prepare_dir(path.c_str(), 0700, AID_ROOT, AID_ROOT)) {
+ if (errno == ENOTCONN) { // Transport endpoint is not connected
+ LOG(ERROR) << getId() << " failed to create mount point";
+ LOG(INFO) << "umount " << path << " and try again";
+ // lazy umount
+ if (!umount2(path.c_str(), MNT_DETACH) || errno == EINVAL || errno == ENOENT) {
+ if (fs_prepare_dir(path.c_str(), 0700, AID_ROOT, AID_ROOT)) {
+ return -1;
+ }
+ return OK;
+ }
+ PLOG(ERROR) << " failed to umount " << path;
+ return -1;
+ }
+ return -1;
+ }
+
+ return OK;
+}
+
+} // namespace vold
+} // namespace android
diff --git a/PublicVolume.h b/PublicVolume.h
new file mode 100644
index 0000000..6d3f7f5
--- a/dev/null
+++ b/PublicVolume.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2015 The Android Open Source 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_PUBLIC_VOLUME_H
+#define ANDROID_VOLD_PUBLIC_VOLUME_H
+
+#include "VolumeBase.h"
+
+#include <cutils/multiuser.h>
+
+namespace android {
+namespace droidvold {
+
+/*
+ * Shared storage provided by public (vfat) partition.
+ *
+ * Knows how to mount itself and then spawn a FUSE daemon to synthesize
+ * permissions. AsecVolume and ObbVolume can be stacked above it.
+ *
+ * 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:
+ explicit PublicVolume(dev_t device);
+ explicit PublicVolume(const std::string& physicalDevName);
+ virtual ~PublicVolume();
+
+protected:
+ status_t doCreate() override;
+ status_t doDestroy() override;
+ status_t doMount() override;
+ status_t doUnmount() override;
+ status_t doFormat(const std::string& fsType) override;
+ bool isSrdiskMounted() { return mSrMounted;}
+
+ status_t readMetadata();
+ status_t initAsecStage();
+ status_t prepareDir(const std::string& path, mode_t mode, uid_t uid, gid_t gid);
+
+private:
+ /* Kernel device representing partition */
+ dev_t mDevice;
+ /* Block device path */
+ std::string mDevPath;
+ /* Mount point of raw partition */
+ std::string mRawPath;
+
+ std::string mFuseDefault;
+ std::string mFuseRead;
+ std::string mFuseWrite;
+
+ /* PID of FUSE wrapper */
+ pid_t mFusePid;
+
+ /* Filesystem type */
+ std::string mFsType;
+ /* Filesystem UUID */
+ std::string mFsUuid;
+ /* User-visible filesystem label */
+ std::string mFsLabel;
+
+ bool mSrMounted;
+
+ /* Just sd/udisk physical devices are used */
+ bool mJustPhysicalDev;
+
+ DISALLOW_COPY_AND_ASSIGN(PublicVolume);
+};
+
+} // namespace vold
+} // namespace android
+
+#endif
diff --git a/ResponseCode.cpp b/ResponseCode.cpp
new file mode 100644
index 0000000..d7e778d
--- a/dev/null
+++ b/ResponseCode.cpp
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2008 The Android Open Source 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 <stdio.h>
+#include <errno.h>
+#include <string.h>
+
+#define LOG_TAG "Vold"
+
+#include <cutils/log.h>
+
+#include "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/ResponseCode.h b/ResponseCode.h
new file mode 100644
index 0000000..52faf9a
--- a/dev/null
+++ b/ResponseCode.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2008 The Android Open Source 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;
+
+ // 200 series - Requested action has been successfully completed
+ static const int CommandOkay = 200;
+ static const int ShareStatusResult = 210;
+
+ // 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 VolumeInternalPathChanged = 656;
+ static const int VolumeDestroyed = 659;
+
+ static int convertFromErrno();
+};
+#endif
diff --git a/Utils.cpp b/Utils.cpp
new file mode 100644
index 0000000..d37e9ac
--- a/dev/null
+++ b/Utils.cpp
@@ -0,0 +1,787 @@
+/*
+ * Copyright (C) 2015 The Android Open Source 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 "sehandle.h"
+#include "Utils.h"
+#include "Process.h"
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <cutils/fs.h>
+#include <cutils/properties.h>
+#include <private/android_filesystem_config.h>
+#include <logwrap/logwrap.h>
+
+#include <mutex>
+#include <dirent.h>
+#include <fcntl.h>
+#include <linux/fs.h>
+#include <stdlib.h>
+#include <sys/mount.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/statvfs.h>
+
+#ifndef UMOUNT_NOFOLLOW
+#define UMOUNT_NOFOLLOW 0x00000008 /* Don't follow symlink on umount */
+#endif
+
+using android::base::ReadFileToString;
+using android::base::StringPrintf;
+
+namespace android {
+namespace droidvold {
+
+security_context_t sBlkidContext = nullptr;
+security_context_t sBlkidUntrustedContext = nullptr;
+security_context_t sFsckContext = nullptr;
+security_context_t sFsckUntrustedContext = nullptr;
+
+static const char* kBlkidPath = "/system/bin/blkid";
+static const char* kKeyPath = "/data/misc/vold";
+
+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);
+ }
+ }
+
+ if (access(cpath, F_OK) == 0) {
+ LOG(DEBUG) << "path: " << path << " already exist";
+ return res;
+ }
+
+ 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) {
+ const char* cpath = path.c_str();
+ if (!umount2(cpath, UMOUNT_NOFOLLOW) || errno == EINVAL || errno == ENOENT) {
+ return OK;
+ }
+ // Apps might still be handling eject request, so wait before
+ // we start sending signals
+ sleep(5);
+
+ Process::killProcessesWithOpenFiles(cpath, SIGINT);
+ sleep(5);
+ if (!umount2(cpath, UMOUNT_NOFOLLOW) || errno == EINVAL || errno == ENOENT) {
+ return OK;
+ }
+
+ Process::killProcessesWithOpenFiles(cpath, SIGTERM);
+ sleep(5);
+ if (!umount2(cpath, UMOUNT_NOFOLLOW) || errno == EINVAL || errno == ENOENT) {
+ return OK;
+ }
+
+ Process::killProcessesWithOpenFiles(cpath, SIGKILL);
+ sleep(5);
+ 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(5);
+
+ if (Process::killProcessesWithOpenFiles(cpath, SIGTERM) == 0) {
+ return OK;
+ }
+ sleep(5);
+
+ if (Process::killProcessesWithOpenFiles(cpath, SIGKILL) == 0) {
+ return OK;
+ }
+ sleep(5);
+
+ // 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, bool untrusted) {
+ fsType.clear();
+ fsUuid.clear();
+ fsLabel.clear();
+
+ std::vector<std::string> cmd;
+ cmd.push_back(kBlkidPath);
+ cmd.push_back("-c");
+ cmd.push_back("/dev/null");
+ cmd.push_back("-s");
+ cmd.push_back("TYPE");
+ cmd.push_back("-s");
+ cmd.push_back("UUID");
+ cmd.push_back("-s");
+ cmd.push_back("LABEL");
+ cmd.push_back(path);
+
+ std::vector<std::string> output;
+ status_t res = ForkExecvp(cmd, output, untrusted ? sBlkidUntrustedContext : sBlkidContext);
+ if (res != OK) {
+ LOG(WARNING) << "blkid failed to identify " << path;
+ return res;
+ }
+
+ char value[128];
+ for (auto line : output) {
+ // Extract values from blkid output, if defined
+ const char* cline = line.c_str();
+ const char* start = strstr(cline, "TYPE=");
+ if (start != nullptr && sscanf(start + 5, "\"%127[^\"]\"", value) == 1) {
+ fsType = value;
+ }
+
+ start = strstr(cline, "UUID=");
+ if (start != nullptr && sscanf(start + 5, "\"%127[^\"]\"", value) == 1) {
+ fsUuid = value;
+ }
+
+ start = strstr(cline, "LABEL=");
+ if (start != nullptr && sscanf(start + 6, "\"%127[^\"]\"", value) == 1) {
+ fsLabel = value;
+ }
+ }
+
+ return OK;
+}
+
+status_t ReadMetadata(const std::string& path, std::string& fsType,
+ std::string& fsUuid, std::string& fsLabel) {
+ return readMetadata(path, fsType, fsUuid, fsLabel, false);
+}
+
+status_t ReadMetadataUntrusted(const std::string& path, std::string& fsType,
+ std::string& fsUuid, std::string& fsLabel) {
+ return readMetadata(path, fsType, fsUuid, fsLabel, true);
+}
+
+status_t ForkExecvp(const std::vector<std::string>& args) {
+ return ForkExecvp(args, nullptr);
+}
+
+status_t ForkExecvp(const std::vector<std::string>& args, security_context_t context) {
+ size_t argc = args.size();
+ char** argv = (char**) calloc(argc, 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();
+ }
+ status_t res = android_fork_execvp(argc, argv, NULL, false, true);
+ if (setexeccon(nullptr)) {
+ LOG(ERROR) << "Failed to setexeccon";
+ abort();
+ }
+
+ free(argv);
+ return res;
+}
+
+status_t ForkExecvp(const std::vector<std::string>& args,
+ std::vector<std::string>& output) {
+ return ForkExecvp(args, output, nullptr);
+}
+
+status_t ForkExecvp(const std::vector<std::string>& args,
+ std::vector<std::string>& output, security_context_t context) {
+ std::string cmd;
+ for (size_t i = 0; i < args.size(); i++) {
+ cmd += args[i] + " ";
+ if (i == 0) {
+ LOG(VERBOSE) << args[i];
+ } else {
+ LOG(VERBOSE) << " " << args[i];
+ }
+ }
+ output.clear();
+
+ if (setexeccon(context)) {
+ LOG(ERROR) << "Failed to setexeccon";
+ abort();
+ }
+ FILE* fp = popen(cmd.c_str(), "r");
+ if (setexeccon(nullptr)) {
+ LOG(ERROR) << "Failed to setexeccon";
+ abort();
+ }
+
+ if (!fp) {
+ PLOG(ERROR) << "Failed to popen " << cmd;
+ return -errno;
+ }
+ char line[1024];
+ while (fgets(line, sizeof(line), fp) != nullptr) {
+ LOG(VERBOSE) << line;
+ output.push_back(std::string(line));
+ }
+ if (pclose(fp) != 0) {
+ PLOG(ERROR) << "Failed to pclose " << cmd;
+ return -errno;
+ }
+
+ return OK;
+}
+
+pid_t ForkExecvpAsync(const std::vector<std::string>& args) {
+ 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];
+ }
+ }
+
+ pid_t pid = fork();
+ 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 (pid == -1) {
+ PLOG(ERROR) << "Failed to exec";
+ }
+
+ free(argv);
+ return pid;
+}
+
+status_t ReadRandomBytes(size_t bytes, std::string& out) {
+ out.clear();
+
+ int fd = TEMP_FAILURE_RETRY(open("/dev/urandom", O_RDONLY | O_CLOEXEC | O_NOFOLLOW));
+ if (fd == -1) {
+ return -errno;
+ }
+
+ char buf[BUFSIZ];
+ size_t n;
+ while ((n = TEMP_FAILURE_RETRY(read(fd, &buf[0], std::min(sizeof(buf), bytes)))) > 0) {
+ out.append(buf, n);
+ bytes -= n;
+ }
+ close(fd);
+
+ if (bytes == 0) {
+ return OK;
+ } else {
+ return -EIO;
+ }
+}
+
+status_t HexToStr(const std::string& hex, std::string& str) {
+ str.clear();
+ bool even = true;
+ char cur = 0;
+ for (size_t i = 0; i < hex.size(); i++) {
+ int val = 0;
+ switch (hex[i]) {
+ case ' ': case '-': case ':': continue;
+ case 'f': case 'F': val = 15; break;
+ case 'e': case 'E': val = 14; break;
+ case 'd': case 'D': val = 13; break;
+ case 'c': case 'C': val = 12; break;
+ case 'b': case 'B': val = 11; break;
+ case 'a': case 'A': val = 10; break;
+ case '9': val = 9; break;
+ case '8': val = 8; break;
+ case '7': val = 7; break;
+ case '6': val = 6; break;
+ case '5': val = 5; break;
+ case '4': val = 4; break;
+ case '3': val = 3; break;
+ case '2': val = 2; break;
+ case '1': val = 1; break;
+ case '0': val = 0; break;
+ default: return -EINVAL;
+ }
+
+ if (even) {
+ cur = val << 4;
+ } else {
+ cur += val;
+ str.push_back(cur);
+ cur = 0;
+ }
+ even = !even;
+ }
+ return even ? OK : -EINVAL;
+}
+
+static const char* kLookup = "0123456789abcdef";
+
+status_t StrToHex(const std::string& str, std::string& hex) {
+ hex.clear();
+ for (size_t i = 0; i < str.size(); i++) {
+ hex.push_back(kLookup[(str[i] & 0xF0) >> 4]);
+ hex.push_back(kLookup[str[i] & 0x0F]);
+ }
+ return OK;
+}
+
+status_t NormalizeHex(const std::string& in, std::string& out) {
+ std::string tmp;
+ if (HexToStr(in, tmp)) {
+ return -EINVAL;
+ }
+ return StrToHex(tmp, out);
+}
+
+uint64_t GetFreeBytes(const std::string& path) {
+ struct statvfs sb;
+ if (statvfs(path.c_str(), &sb) == 0) {
+ return (uint64_t)sb.f_bfree * sb.f_bsize;
+ } else {
+ return -1;
+ }
+}
+
+// TODO: borrowed from frameworks/native/libs/diskusage/ which should
+// eventually be migrated into system/
+static int64_t stat_size(struct stat *s) {
+ int64_t blksize = s->st_blksize;
+ // count actual blocks used instead of nominal file size
+ int64_t size = s->st_blocks * 512;
+
+ if (blksize) {
+ /* round up to filesystem block size */
+ size = (size + blksize - 1) & (~(blksize - 1));
+ }
+
+ return size;
+}
+
+// TODO: borrowed from frameworks/native/libs/diskusage/ which should
+// eventually be migrated into system/
+int64_t calculate_dir_size(int dfd) {
+ int64_t size = 0;
+ struct stat s;
+ DIR *d;
+ struct dirent *de;
+
+ d = fdopendir(dfd);
+ if (d == NULL) {
+ close(dfd);
+ return 0;
+ }
+
+ while ((de = readdir(d))) {
+ const char *name = de->d_name;
+ if (fstatat(dfd, name, &s, AT_SYMLINK_NOFOLLOW) == 0) {
+ size += stat_size(&s);
+ }
+ if (de->d_type == DT_DIR) {
+ int subfd;
+
+ /* always skip "." and ".." */
+ if (name[0] == '.') {
+ if (name[1] == 0)
+ continue;
+ if ((name[1] == '.') && (name[2] == 0))
+ continue;
+ }
+
+ subfd = openat(dfd, name, O_RDONLY | O_DIRECTORY);
+ if (subfd >= 0) {
+ size += calculate_dir_size(subfd);
+ }
+ }
+ }
+ closedir(d);
+ return size;
+}
+
+uint64_t GetTreeBytes(const std::string& path) {
+ int dirfd = open(path.c_str(), O_DIRECTORY, O_RDONLY);
+ if (dirfd < 0) {
+ PLOG(WARNING) << "Failed to open " << path;
+ return -1;
+ } else {
+ uint64_t res = calculate_dir_size(dirfd);
+ close(dirfd);
+ return res;
+ }
+}
+
+bool IsFilesystemSupported(const std::string& fsType) {
+ std::string supported;
+ if (!ReadFileToString(kProcFilesystems, &supported)) {
+ PLOG(ERROR) << "Failed to read supported filesystems";
+ return false;
+ }
+ 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;
+}
+
+std::string BuildDataUserDePath(const char* volumeUuid, userid_t userId) {
+ // TODO: unify with installd path generation logic
+ std::string data(BuildDataPath(volumeUuid));
+ return StringPrintf("%s/user_de/%u", data.c_str(), userId);
+}
+
+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;
+ }
+}
+
+std::string DefaultFstabPath() {
+ char hardware[PROPERTY_VALUE_MAX];
+ property_get("ro.hardware", hardware, "");
+ return StringPrintf("/fstab.%s", hardware);
+}
+
+status_t RestoreconRecursive(const std::string& path) {
+ LOG(VERBOSE) << "Starting restorecon of " << path;
+
+ // TODO: find a cleaner way of waiting for restorecon to finish
+ const char* cpath = path.c_str();
+ property_set("selinux.restorecon_recursive", "");
+ property_set("selinux.restorecon_recursive", cpath);
+
+ char value[PROPERTY_VALUE_MAX];
+ while (true) {
+ property_get("selinux.restorecon_recursive", value, "");
+ if (strcmp(cpath, value) == 0) {
+ break;
+ }
+ usleep(100000); // 100ms
+ }
+
+ LOG(VERBOSE) << "Finished restorecon of " << path;
+ return OK;
+}
+
+status_t SaneReadLinkAt(int dirfd, const char* path, char* buf, size_t bufsiz) {
+ ssize_t len = readlinkat(dirfd, path, buf, bufsiz);
+ if (len < 0) {
+ return -1;
+ } else if (len == (ssize_t) bufsiz) {
+ return -1;
+ } else {
+ buf[len] = '\0';
+ return 0;
+ }
+}
+
+ScopedFd::ScopedFd(int fd) : fd_(fd) {}
+
+ScopedFd::~ScopedFd() {
+ close(fd_);
+}
+
+ScopedDir::ScopedDir(DIR* dir) : dir_(dir) {}
+
+ScopedDir::~ScopedDir() {
+ if (dir_ != nullptr) {
+ closedir(dir_);
+ }
+}
+
+bool IsRunningInEmulator() {
+ return property_get_bool("ro.kernel.qemu", 0);
+}
+
+status_t readBlockDevMajorAndMinor(
+ const std::string& devPath,
+ std::string& major, std::string& minor) {
+ major.clear();
+ minor.clear();
+
+ std::vector<std::string> cmd;
+ cmd.push_back("/system/bin/ls");
+ cmd.push_back("-l");
+ cmd.push_back(devPath);
+
+ std::vector<std::string> output;
+ status_t res = ForkExecvp(cmd, output);
+ if (res != OK) {
+ LOG(WARNING) << "failed to identify ls -l " << devPath;
+ return res;
+ }
+
+ // Extract values from output
+ // brw------- root root 179, 1 2015-01-01 00:00 mmcblk0p1
+ char value[128];
+ for (auto line : output) {
+ int count = sscanf(line.c_str(), "%3s", value);
+ if (count == 1 && !strcmp(value, "brw")) { // block device
+ char f[128], s[128];
+ if (sscanf(line.c_str(), "%[^','],%s", f, s) == 2) { // split ','
+ minor = s;
+ char *cline = strdup(f);
+ char *str = strtok(cline, " ");
+ char buf[25];
+ while (str != nullptr) {
+ if (sscanf(str, "%[1-9]", buf) == 1) {
+ major = buf;
+ }
+ str = strtok(nullptr, " ");
+ }
+ }
+ }
+ }
+
+ return OK;
+}
+
+// Get physical device path (such as /dev/block/sda) by kernel event sys path
+status_t GetPhysicalDevice(
+ const std::string& sysPath, std::string& physicalDev) {
+ int iPos = sysPath.find("/block/");
+ if (iPos < 0) {
+ LOG(WARNING) << "can't find \"/block/\" in " << sysPath;
+ return -1;
+ }
+
+ physicalDev = StringPrintf("/dev/block/%s", sysPath.substr(iPos + 7).c_str());
+ if (access(physicalDev.c_str(), F_OK)) {
+ LOG(INFO) << "physical dev: " << physicalDev + " doesn't exist";
+ return -1;
+ }
+
+ return OK;
+}
+
+// /sys//devices/d0072000.sd/mmc_host/sd/sd:0007/block/mmcblk0
+// /sys//devices/dwc2_b/usb1/1-1/1-1.2/1-1.2:1.0/host0/target0:0:0/0:0:0:0/block/sda
+status_t GetLogicalPartitionDevice(
+ const dev_t device, const std::string& sysPath, std::string& logicalPartitionDev) {
+ std::string physicalDev;
+ const unsigned int kMajorBlockMmc = 179;
+ const unsigned int kMaxNumOfPartition = 31;
+
+ // logical partition dev's major & minor
+ unsigned int devMajor = major(device);
+ unsigned int devMinor = minor(device);
+
+ if (GetPhysicalDevice(sysPath, physicalDev) != OK) {
+ return -1;
+ }
+
+ LOG(INFO) << "physical dev: " << physicalDev <<
+ ", logical partition dev's major: " << devMajor << ", minor: " << devMinor;
+
+ // For now, assume that MMC devices are SD, and that
+ // everything else is USB
+ std::string lpDev;
+ std::string major, minor;
+ for (unsigned int i = 1; i <= kMaxNumOfPartition; i ++) {
+ if (devMajor == kMajorBlockMmc) { // SD
+ lpDev = StringPrintf("%sp%d", physicalDev.c_str(), i);
+ } else { // USB
+ lpDev = StringPrintf("%s%d", physicalDev.c_str(), i);
+ }
+
+ if (!access(lpDev.c_str(), F_OK) &&
+ readBlockDevMajorAndMinor(lpDev, major, minor) == OK &&
+ (int)devMajor == atoi(major.c_str()) &&
+ (int)devMinor == atoi(minor.c_str())) {
+ logicalPartitionDev = lpDev;
+ LOG(INFO) << "find logical partition dev: " << logicalPartitionDev;
+ break;
+ }
+ }
+
+ return OK;
+}
+
+// Such as /dev/block/sda is used, return true,otherwise false
+// just true,saved sda to physicalDevName
+bool IsJustPhysicalDevice(
+ const std::string& sysPath, std::string& physicalDevName) {
+ std::string major, minor;
+ std::string physicalDev;
+ std::string logicalPartitionDev;
+ const unsigned int kMajorBlockMmc = 179;
+
+ if (GetPhysicalDevice(sysPath, physicalDev) == OK) {
+ std::vector<std::string> cmd;
+ cmd.push_back(kBlkidPath);
+ cmd.push_back("-c");
+ cmd.push_back("/dev/null");
+ cmd.push_back("-s");
+ cmd.push_back("TYPE");
+ cmd.push_back("-s");
+ cmd.push_back("UUID");
+ cmd.push_back("-s");
+ cmd.push_back("LABEL");
+ cmd.push_back(physicalDev);
+
+ std::vector<std::string> output;
+ status_t res = ForkExecvp(cmd, output, sBlkidUntrustedContext);
+ if (res != OK) {
+ LOG(WARNING) << "failed to identify blkid " << physicalDev;
+ return false;
+ }
+
+ char value[128];
+ for (auto line : output) {
+ // Extract values from blkid output, if defined
+ const char* cline = line.c_str();
+ if (!strncmp(cline, physicalDev.c_str(), strlen(physicalDev.c_str()))) {
+ if (readBlockDevMajorAndMinor(physicalDev, major, minor) == OK) {
+ logicalPartitionDev = (atoi(major.c_str()) == kMajorBlockMmc) ?
+ StringPrintf("%sp1", physicalDev.c_str()) :
+ StringPrintf("%s1", physicalDev.c_str());
+ if (access(logicalPartitionDev.c_str(), F_OK)) {
+ // And logical partition device doesn't exist,
+ // we're sure physical device is used.
+ // length /dev/block/ = 11,such as sda or mmcblk0,
+ // we get as physical device name.
+ physicalDevName = StringPrintf("%s", physicalDev.substr(11).c_str());
+ return true;
+ }
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+} // namespace vold
+} // namespace android
diff --git a/Utils.h b/Utils.h
new file mode 100644
index 0000000..aac9162
--- a/dev/null
+++ b/Utils.h
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2015 The Android Open Source 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_UTILS_H
+#define ANDROID_VOLD_UTILS_H
+
+#include <utils/Errors.h>
+#include <cutils/multiuser.h>
+#include <selinux/selinux.h>
+
+#include <vector>
+#include <string>
+
+// 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 droidvold {
+
+/* SELinux contexts used depending on the block device type */
+extern security_context_t sBlkidContext;
+extern security_context_t sBlkidUntrustedContext;
+extern security_context_t sFsckContext;
+extern security_context_t 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);
+
+/* 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, security_context_t context);
+
+status_t ForkExecvp(const std::vector<std::string>& args,
+ std::vector<std::string>& output);
+status_t ForkExecvp(const std::vector<std::string>& args,
+ std::vector<std::string>& output, security_context_t context);
+
+pid_t ForkExecvpAsync(const std::vector<std::string>& args);
+
+status_t ReadRandomBytes(size_t bytes, std::string& out);
+
+/* Converts hex string to raw bytes, ignoring [ :-] */
+status_t HexToStr(const std::string& hex, std::string& str);
+/* Converts raw bytes to hex string */
+status_t StrToHex(const std::string& str, std::string& hex);
+/* Normalize given hex string into consistent format */
+status_t NormalizeHex(const std::string& in, std::string& out);
+
+uint64_t GetFreeBytes(const std::string& path);
+uint64_t GetTreeBytes(const std::string& path);
+
+bool IsFilesystemSupported(const std::string& fsType);
+
+/* Wipes contents of block device at given path */
+status_t WipeBlockDevice(const std::string& path);
+
+std::string BuildKeyPath(const std::string& partGuid);
+
+std::string BuildDataSystemLegacyPath(userid_t userid);
+std::string BuildDataSystemCePath(userid_t userid);
+std::string BuildDataSystemDePath(userid_t userid);
+std::string BuildDataMiscLegacyPath(userid_t userid);
+std::string BuildDataMiscCePath(userid_t userid);
+std::string BuildDataMiscDePath(userid_t userid);
+std::string BuildDataProfilesDePath(userid_t userid);
+std::string BuildDataProfilesForeignDexDePath(userid_t userid);
+
+std::string BuildDataPath(const char* volumeUuid);
+std::string BuildDataMediaCePath(const char* volumeUuid, userid_t userid);
+std::string BuildDataUserCePath(const char* volumeUuid, userid_t userid);
+std::string BuildDataUserDePath(const char* volumeUuid, userid_t userid);
+
+dev_t GetDevice(const std::string& path);
+
+std::string DefaultFstabPath();
+
+status_t RestoreconRecursive(const std::string& path);
+
+status_t SaneReadLinkAt(int dirfd, const char* path, char* buf, size_t bufsiz);
+
+class ScopedFd {
+ const int fd_;
+public:
+ ScopedFd(int fd);
+ ~ScopedFd();
+ int get() const { return fd_; }
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedFd);
+};
+
+class ScopedDir {
+ DIR* const dir_;
+public:
+ ScopedDir(DIR* dir);
+ ~ScopedDir();
+ DIR* get() const { return dir_; }
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedDir);
+};
+
+/* Checks if Android is running in QEMU */
+bool IsRunningInEmulator();
+
+/* Get physical device,such as /dev/block/mmcblk1 */
+status_t GetPhysicalDevice(
+ const std::string& sysPath,
+ std::string& physicalDev);
+
+/* Get logical partition device,such as /dev/block/mmcblk1p1 */
+status_t GetLogicalPartitionDevice(
+ const dev_t device,
+ const std::string& sysPath,
+ std::string& logicalPartitionDev);
+
+/* Check sd/udisk physical devices whether are used */
+bool IsJustPhysicalDevice(
+ const std::string& sysPath,
+ std::string& physicalDevName);
+
+status_t readBlockDevMajorAndMinor(
+ const std::string& devPath,
+ std::string& major, std::string& minor);
+
+} // namespace vold
+} // namespace android
+
+#endif
diff --git a/VoldCommand.cpp b/VoldCommand.cpp
new file mode 100644
index 0000000..3c0d58d
--- a/dev/null
+++ b/VoldCommand.cpp
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2008 The Android Open Source 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 "VoldCommand.h"
+
+VoldCommand::VoldCommand(const char *cmd) :
+ FrameworkCommand(cmd) {
+}
diff --git a/VoldCommand.h b/VoldCommand.h
new file mode 100644
index 0000000..5ddc666
--- a/dev/null
+++ b/VoldCommand.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2008 The Android Open Source 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 _VOLD_COMMAND_H
+#define _VOLD_COMMAND_H
+
+#include <sysutils/FrameworkCommand.h>
+
+class VoldCommand : public FrameworkCommand {
+public:
+ VoldCommand(const char *cmd);
+ virtual ~VoldCommand() {}
+};
+
+#endif
diff --git a/VolumeBase.cpp b/VolumeBase.cpp
new file mode 100644
index 0000000..2bcef32
--- a/dev/null
+++ b/VolumeBase.cpp
@@ -0,0 +1,255 @@
+/*
+ * Copyright (C) 2015 The Android Open Source 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 "VolumeBase.h"
+#include "VolumeManager.h"
+#include "ResponseCode.h"
+
+#include <android-base/stringprintf.h>
+#include <android-base/logging.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 droidvold {
+
+VolumeBase::VolumeBase(Type type) :
+ mType(type), mMountFlags(0), mMountUserId(-1), mCreated(false), mState(
+ State::kUnmounted), mSilent(false) {
+}
+
+VolumeBase::~VolumeBase() {
+ CHECK(!mCreated);
+}
+
+void VolumeBase::setState(State state) {
+ mState = state;
+ 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::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::setMountUserId(userid_t mountUserId) {
+ if ((mState != State::kUnmounted) && (mState != State::kUnmountable)) {
+ LOG(WARNING) << getId() << " user change requires state unmounted or unmountable";
+ return -EBUSY;
+ }
+
+ mMountUserId = mountUserId;
+ 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) {
+ if (mState != State::kChecking) {
+ LOG(WARNING) << getId() << " path change requires state checking";
+ return -EBUSY;
+ }
+
+ mPath = path;
+ notifyEvent(ResponseCode::VolumePathChanged, mPath);
+ return OK;
+}
+
+status_t VolumeBase::setInternalPath(const std::string& internalPath) {
+ if (mState != State::kChecking) {
+ LOG(WARNING) << getId() << " internal path change requires state checking";
+ return -EBUSY;
+ }
+
+ mInternalPath = internalPath;
+ notifyEvent(ResponseCode::VolumeInternalPathChanged, mInternalPath);
+ return OK;
+}
+
+void VolumeBase::notifyEvent(int event) {
+ if (mSilent) return;
+ VolumeManager::Instance()->getBroadcaster()->sendBroadcast(event,
+ getId().c_str(), false);
+}
+
+void VolumeBase::notifyEvent(int event, const std::string& value) {
+ if (mSilent) return;
+ VolumeManager::Instance()->getBroadcaster()->sendBroadcast(event,
+ StringPrintf("%s %s", getId().c_str(), value.c_str()).c_str(), false);
+}
+
+
+void VolumeBase::addVolume(const std::shared_ptr<VolumeBase>& volume) {
+ mVolumes.push_back(volume);
+}
+
+void VolumeBase::removeVolume(const std::shared_ptr<VolumeBase>& volume) {
+ mVolumes.remove(volume);
+}
+
+std::shared_ptr<VolumeBase> VolumeBase::findVolume(const std::string& id) {
+ for (auto vol : mVolumes) {
+ if (vol->getId() == id) {
+ return vol;
+ }
+ }
+ return nullptr;
+}
+
+status_t VolumeBase::create() {
+ CHECK(!mCreated);
+
+ mCreated = true;
+ status_t res = doCreate();
+ notifyEvent(ResponseCode::VolumeCreated,
+ StringPrintf("%d \"%s\" \"%s\"", mType, mDiskId.c_str(), mPartGuid.c_str()));
+ setState(State::kUnmounted);
+ return res;
+}
+
+status_t VolumeBase::doCreate() {
+ return OK;
+}
+
+status_t VolumeBase::destroy() {
+ CHECK(mCreated);
+
+ if (mState == State::kMounted) {
+ unmount();
+ setState(State::kBadRemoval);
+ } else {
+ setState(State::kRemoved);
+ }
+
+ 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() {
+ if (mState != State::kMounted) {
+ LOG(WARNING) << getId() << " unmount requires state mounted";
+ return -EBUSY;
+ }
+
+ setState(State::kEjecting);
+ for (auto vol : mVolumes) {
+ if (vol->destroy()) {
+ LOG(WARNING) << getId() << " failed to destroy " << vol->getId()
+ << " stacked above";
+ }
+ }
+ mVolumes.clear();
+
+ status_t res = doUnmount();
+ setState(State::kUnmounted);
+ return res;
+}
+
+status_t VolumeBase::format(const std::string& fsType) {
+ if (mState == State::kMounted) {
+ unmount();
+ }
+
+ if ((mState != State::kUnmounted) && (mState != State::kUnmountable)) {
+ LOG(WARNING) << getId() << " format requires state unmounted or unmountable";
+ return -EBUSY;
+ }
+
+ setState(State::kFormatting);
+ status_t res = doFormat(fsType);
+ setState(State::kUnmounted);
+ return res;
+}
+
+status_t VolumeBase::doFormat(const std::string& fsType) {
+ return -ENOTSUP;
+}
+
+} // namespace vold
+} // namespace android
diff --git a/VolumeBase.h b/VolumeBase.h
new file mode 100644
index 0000000..8438396
--- a/dev/null
+++ b/VolumeBase.h
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2008 The Android Open Source 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_VOLUME_BASE_H
+#define ANDROID_VOLD_VOLUME_BASE_H
+
+#include "Utils.h"
+
+#include <cutils/multiuser.h>
+#include <utils/Errors.h>
+
+#include <sys/types.h>
+#include <list>
+#include <string>
+
+namespace android {
+namespace droidvold {
+
+/*
+ * 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,
+ kFormatting,
+ kEjecting,
+ kUnmountable,
+ kRemoved,
+ kBadRemoval,
+ };
+
+ const std::string& getId() { return mId; }
+ const std::string& getDiskId() { return mDiskId; }
+ const std::string& getPartGuid() { return mPartGuid; }
+ Type getType() { return mType; }
+ int getMountFlags() { return mMountFlags; }
+ userid_t getMountUserId() { return mMountUserId; }
+ State getState() { return mState; }
+ const std::string& getPath() { return mPath; }
+ const std::string& getSysPath() { return mSysPath; }
+ const std::string& getInternalPath() { return mInternalPath; }
+
+ status_t setDiskId(const std::string& diskId);
+ status_t setPartGuid(const std::string& partGuid);
+ status_t setMountFlags(int mountFlags);
+ status_t setMountUserId(userid_t mountUserId);
+ status_t setSilent(bool silent);
+ void setSysPath(const std::string& sysPath) { mSysPath = sysPath; }
+
+ void addVolume(const std::shared_ptr<VolumeBase>& volume);
+ void removeVolume(const std::shared_ptr<VolumeBase>& volume);
+
+ std::shared_ptr<VolumeBase> findVolume(const std::string& id);
+
+ status_t create();
+ status_t destroy();
+ status_t mount();
+ status_t unmount();
+ status_t format(const std::string& fsType);
+ virtual bool isSrdiskMounted() = 0;
+
+protected:
+ explicit VolumeBase(Type type);
+
+ virtual status_t doCreate();
+ virtual status_t doDestroy();
+ virtual status_t doMount() = 0;
+ virtual status_t doUnmount() = 0;
+ virtual status_t doFormat(const std::string& fsType);
+
+ status_t setId(const std::string& id);
+ status_t setPath(const std::string& path);
+ status_t setInternalPath(const std::string& internalPath);
+ void notifyEvent(int msg);
+ void notifyEvent(int msg, const std::string& value);
+
+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;
+ /* Volume type */
+ Type mType;
+ /* Flags used when mounting this volume */
+ int mMountFlags;
+ /* User that owns this volume, otherwise -1 */
+ userid_t mMountUserId;
+ /* Flag indicating object is created */
+ bool mCreated;
+ /* Current state of volume */
+ State mState;
+ /* Path to mounted volume */
+ std::string mPath;
+ /* Device path under sysfs */
+ std::string mSysPath;
+ /* Path to internal backing storage */
+ std::string mInternalPath;
+ /* Flag indicating that volume should emit no events */
+ bool mSilent;
+
+ /* Volumes stacked on top of this volume */
+ std::list<std::shared_ptr<VolumeBase>> mVolumes;
+
+ void setState(State state);
+
+ DISALLOW_COPY_AND_ASSIGN(VolumeBase);
+};
+
+} // namespace vold
+} // namespace android
+
+#endif
diff --git a/VolumeManager.cpp b/VolumeManager.cpp
new file mode 100644
index 0000000..fc9da65
--- a/dev/null
+++ b/VolumeManager.cpp
@@ -0,0 +1,366 @@
+/*
+ * Copyright (C) 2008 The Android Open Source 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 <fts.h>
+#include <mntent.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <linux/kdev_t.h>
+
+#define LOG_TAG "droidVold"
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <cutils/fs.h>
+#include <cutils/log.h>
+
+#include <selinux/android.h>
+
+#include <sysutils/NetlinkEvent.h>
+
+#include "VolumeManager.h"
+#include "NetlinkManager.h"
+#include "fs/Ext4.h"
+#include "fs/Vfat.h"
+#include "Utils.h"
+#include "Process.h"
+#include "fs/Iso9660.h"
+
+#ifdef HAS_VIRTUAL_CDROM
+#define LOOP_DEV "/dev/block/loop0"
+#define LOOP_MOUNTPOINT "/mnt/loop"
+#endif
+
+using android::base::StringPrintf;
+
+static const unsigned int kMajorBlockMmc = 179;
+static const unsigned int kMajorBlockExperimentalMin = 240;
+static const unsigned int kMajorBlockExperimentalMax = 254;
+
+VolumeManager *VolumeManager::sInstance = NULL;
+
+VolumeManager *VolumeManager::Instance() {
+ if (!sInstance)
+ sInstance = new VolumeManager();
+ return sInstance;
+}
+
+VolumeManager::VolumeManager() {
+ mDebug = false;
+ mBroadcaster = NULL;
+#ifdef HAS_VIRTUAL_CDROM
+ mLoopPath = NULL;
+#endif
+}
+
+VolumeManager::~VolumeManager() {
+}
+
+int VolumeManager::setDebug(bool enable) {
+ mDebug = enable;
+ return 0;
+}
+
+int VolumeManager::start() {
+ // Always start from a clean slate by unmounting everything in
+ // directories that we own, in case we crashed.
+ unmountAll();
+
+ return 0;
+}
+
+int VolumeManager::stop() {
+ return 0;
+}
+
+void VolumeManager::handleBlockEvent(NetlinkEvent *evt) {
+ std::lock_guard<std::mutex> lock(mLock);
+
+ if (mDebug) {
+ LOG(VERBOSE) << "----------------";
+ LOG(VERBOSE) << "handleBlockEvent with action " << (int) evt->getAction();
+ evt->dump();
+ }
+
+ std::string eventPath(evt->findParam("DEVPATH")?evt->findParam("DEVPATH"):"");
+ std::string devType(evt->findParam("DEVTYPE")?evt->findParam("DEVTYPE"):"");
+
+ if (devType != "disk") return;
+
+ 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 (auto source : mDiskSources) {
+ if (source->matches(eventPath)) {
+ // For now, assume that MMC and virtio-blk (the latter is
+ // emulator-specific; see Disk.cpp for details) devices are SD,
+ // and that everything else is USB
+ int flags = source->getFlags();
+ if (major == kMajorBlockMmc
+ || (android::droidvold::IsRunningInEmulator()
+ && major >= (int) kMajorBlockExperimentalMin
+ && major <= (int) kMajorBlockExperimentalMax)) {
+ flags |= android::droidvold::Disk::Flags::kSd;
+ } else {
+ flags |= android::droidvold::Disk::Flags::kUsb;
+ }
+
+ auto disk = new android::droidvold::Disk(eventPath, device,
+ source->getNickname(), flags);
+ disk->create();
+ mDisks.push_back(std::shared_ptr<android::droidvold::Disk>(disk));
+ break;
+ }
+ }
+ break;
+ }
+ case NetlinkEvent::Action::kChange: {
+ LOG(DEBUG) << "Disk at " << major << ":" << minor << " changed";
+ for (auto disk : mDisks) {
+ if (disk->getDevice() == device) {
+ if (disk->isSrdiskMounted()) {
+ LOG(DEBUG) << "srdisk ejected";
+ disk->destroyAllVolumes();
+ break;
+ }
+
+ 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::addDiskSource(const std::shared_ptr<DiskSource>& diskSource) {
+ mDiskSources.push_back(diskSource);
+}
+
+std::shared_ptr<android::droidvold::VolumeBase> VolumeManager::findVolume(const std::string& id) {
+
+ for (auto disk : mDisks) {
+ auto vol = disk->findVolume(id);
+ if (vol != nullptr) {
+ return vol;
+ }
+ }
+ return nullptr;
+}
+
+void VolumeManager::listVolumes(android::droidvold::VolumeBase::Type type,
+ std::list<std::string>& list) {
+ list.clear();
+ for (auto disk : mDisks) {
+ disk->listVolumes(type, list);
+ }
+}
+
+int VolumeManager::unmountAll() {
+ std::lock_guard<std::mutex> lock(mLock);
+
+ for (auto disk : mDisks) {
+ disk->unmountAll();
+ }
+
+ return 0;
+}
+
+#ifdef HAS_VIRTUAL_CDROM
+int VolumeManager::loopsetfd(const char * path)
+{
+ int fd,file_fd;
+
+ if ((fd = open(LOOP_DEV, O_RDWR)) < 0) {
+ SLOGE("Unable to open loop0 device (%s)",strerror(errno));
+ return -1;
+ }
+
+ if ((file_fd = open(path, O_RDWR)) < 0) {
+ SLOGE("Unable to open %s (%s)", path, strerror(errno));
+ close(fd);
+ return -1;
+ }
+
+ if (ioctl(fd, LOOP_SET_FD, file_fd) < 0) {
+ SLOGE("Error setting up loopback interface (%s)", strerror(errno));
+ close(file_fd);
+ close(fd);
+ return -1;
+ }
+
+ close(fd);
+ close(file_fd);
+
+ SLOGD("loopsetfd (%s) ok\n", path);
+ return 0;
+}
+
+int VolumeManager::loopclrfd()
+{
+ int fd;
+ int rc=0;
+
+ if ((fd = open(LOOP_DEV, O_RDWR)) < 0) {
+ SLOGE("Unable to open loop0 device (%s)",strerror(errno));
+ return -1;
+ }
+
+ if (ioctl(fd, LOOP_CLR_FD, 0) < 0) {
+ SLOGE("Error setting up loopback interface (%s)", strerror(errno));
+ rc = -1;
+ }
+ close(fd);
+
+ SLOGD("loopclrfd ok\n");
+ return rc;
+}
+
+int VolumeManager::mountloop(const char * path) {
+ if (isMountpointMounted(LOOP_MOUNTPOINT)) {
+ SLOGW("loop file already mounted,please umount fist,then mount this file!");
+ errno = EBUSY;
+ return -1;
+ }
+
+ if (loopsetfd(path) < 0) {
+ return -1;
+ }
+
+ if (fs_prepare_dir(LOOP_MOUNTPOINT, 0700, AID_ROOT, AID_SDCARD_R)) {
+ SLOGE("failed to create loop mount points");
+ return -errno;
+ }
+
+ if (android::droidvold::iso9660::Mount(LOOP_DEV, LOOP_MOUNTPOINT, false, false,
+ AID_SDCARD_R, AID_SDCARD_R, 0007, true)) {
+ loopclrfd();
+ SLOGW("%s failed to mount via ISO9660(%s)", LOOP_DEV, strerror(errno));
+ return -1;
+ } else {
+ mLoopPath = strdup(path);
+ SLOGI("Successfully mount %s as ISO9660", LOOP_DEV);
+ }
+
+ return 0;
+}
+
+int VolumeManager::unmountloop(bool unused) {
+ if (!isMountpointMounted(LOOP_MOUNTPOINT)) {
+ SLOGW("no loop file mounted");
+ errno = ENOENT;
+ return -1;
+ }
+
+ android::droidvold::ForceUnmount(LOOP_MOUNTPOINT);
+ loopclrfd();
+ rmdir(LOOP_MOUNTPOINT);
+
+ if (mLoopPath != NULL) {
+ free(mLoopPath);
+ mLoopPath = NULL;
+ }
+
+ return 0;
+}
+
+void VolumeManager::unmountLoopIfNeed(const char *label) {
+ if (mLoopPath != NULL && strstr(mLoopPath, label)) {
+ SLOGD("umount loop");
+ unmountloop(true);
+ }
+}
+
+#endif
+
+bool VolumeManager::isMountpointMounted(const char *mp)
+{
+ FILE *fp = setmntent("/proc/mounts", "r");
+ if (fp == NULL) {
+ SLOGE("Error opening /proc/mounts (%s)", strerror(errno));
+ return false;
+ }
+
+ bool found_mp = false;
+ mntent* mentry;
+ while ((mentry = getmntent(fp)) != NULL) {
+ if (strcmp(mentry->mnt_dir, mp) == 0) {
+ found_mp = true;
+ break;
+ }
+ }
+ endmntent(fp);
+ return found_mp;
+}
+
+int VolumeManager::reset() {
+ // Tear down all existing disks/volumes and start from a blank slate so
+ // newly connected framework hears all events.
+ for (auto disk : mDisks) {
+ disk->destroy();
+ disk->create();
+ }
+ return 0;
+}
+
+int VolumeManager::shutdown() {
+ for (auto disk : mDisks) {
+ disk->destroy();
+ }
+ mDisks.clear();
+ return 0;
+}
+
+int VolumeManager::mkdirs(char* path) {
+ // Only offer to create directories for paths managed by vold
+ if (strncmp(path, "/mnt/media_rw/", 13) == 0) {
+ // fs_mkdirs() does symlink checking and relative path enforcement
+ return fs_mkdirs(path, 0700);
+ } else {
+ SLOGE("Failed to find mounted volume for %s", path);
+ return -EINVAL;
+ }
+}
diff --git a/VolumeManager.h b/VolumeManager.h
new file mode 100644
index 0000000..715e68b
--- a/dev/null
+++ b/VolumeManager.h
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2008 The Android Open Source 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_DROIDVOLD_VOLUME_MANAGER_H
+#define ANDROID_DROIDVOLD_VOLUME_MANAGER_H
+
+#include <pthread.h>
+#include <fnmatch.h>
+#include <stdlib.h>
+
+#include <list>
+#include <mutex>
+#include <string>
+#include <unordered_map>
+#include <unordered_set>
+
+#include <cutils/multiuser.h>
+#include <utils/List.h>
+#include <utils/Timers.h>
+#include <sysutils/NetlinkEvent.h>
+
+#include "Disk.h"
+#include "VolumeBase.h"
+
+class VolumeManager {
+public:
+#ifdef HAS_VIRTUAL_CDROM
+ char *mLoopPath;
+#endif
+
+private:
+ static VolumeManager *sInstance;
+
+ SocketListener *mBroadcaster;
+
+ bool mDebug;
+
+public:
+ virtual ~VolumeManager();
+ // TODO: pipe all requests through VM to avoid exposing this lock
+ std::mutex& getLock() { return mLock; }
+
+ int start();
+ int stop();
+
+ void handleBlockEvent(NetlinkEvent *evt);
+
+ class DiskSource {
+ public:
+ DiskSource(const std::string& sysPattern, const std::string& nickname, int flags) :
+ mSysPattern(sysPattern), mNickname(nickname), mFlags(flags) {
+ }
+
+ bool matches(const std::string& sysPath) {
+ return !fnmatch(mSysPattern.c_str(), sysPath.c_str(), 0);
+ }
+
+ const std::string& getNickname() { return mNickname; }
+ int getFlags() { return mFlags; }
+
+ private:
+ std::string mSysPattern;
+ std::string mNickname;
+ int mFlags;
+ };
+
+ void addDiskSource(const std::shared_ptr<DiskSource>& diskSource);
+ std::shared_ptr<android::droidvold::VolumeBase> findVolume(const std::string& id);
+ void listVolumes(android::droidvold::VolumeBase::Type type, std::list<std::string>& list);
+
+ /* Reset all internal state, typically during framework boot */
+ int reset();
+ /* Prepare for device shutdown, safely unmounting all devices */
+ int shutdown();
+
+ /* Unmount all volumes, usually for encryption */
+ int unmountAll();
+
+ /* for iso file mount and umount */
+#ifdef HAS_VIRTUAL_CDROM
+ int loopsetfd(const char * path);
+ int loopclrfd();
+ int mountloop(const char * path);
+ int unmountloop(bool force);
+ void unmountLoopIfNeed(const char *label);
+#endif
+
+ int setDebug(bool enable);
+
+ void setBroadcaster(SocketListener *sl) { mBroadcaster = sl; }
+ SocketListener *getBroadcaster() { return mBroadcaster; }
+
+ static VolumeManager *Instance();
+ bool isMountpointMounted(const char *mp);
+ int mkdirs(char* path);
+
+private:
+ VolumeManager();
+
+ std::mutex mLock;
+ std::list<std::shared_ptr<DiskSource>> mDiskSources;
+ std::list<std::shared_ptr<android::droidvold::Disk>> mDisks;
+};
+
+#endif
diff --git a/droidvold.rc b/droidvold.rc
new file mode 100644
index 0000000..0511135
--- a/dev/null
+++ b/droidvold.rc
@@ -0,0 +1,7 @@
+service droidvold /vendor/bin/droidvold \
+ --blkid_context=u:r:blkid:s0 --blkid_untrusted_context=u:r:blkid_untrusted:s0 \
+ --fsck_context=u:r:fsck:s0 --fsck_untrusted_context=u:r:fsck_untrusted:s0
+ class core
+ socket droidvold stream 0660 root system
+ ioprio be 2
+ writepid /dev/cpuset/foreground/tasks
diff --git a/dvdc.cpp b/dvdc.cpp
new file mode 100644
index 0000000..31b6ff8
--- a/dev/null
+++ b/dvdc.cpp
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2008 The Android Open Source 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 <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <signal.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <poll.h>
+
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/un.h>
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+
+#include <cutils/sockets.h>
+#include <private/android_filesystem_config.h>
+
+static void usage(char *progname);
+static int do_monitor(int sock, int stop_after_cmd);
+static int do_cmd(int sock, int argc, char **argv);
+
+static constexpr int kCommandTimeoutMs = 20 * 1000;
+
+int main(int argc, char **argv) {
+ int sock;
+ int wait_for_socket;
+ char *progname;
+
+ progname = argv[0];
+
+ if (getppid() == 1) {
+ // If init is calling us then it's during boot and we should log to kmsg
+ android::base::InitLogging(argv, &android::base::KernelLogger);
+ } else {
+ android::base::InitLogging(argv, &android::base::StderrLogger);
+ }
+
+ wait_for_socket = argc > 1 && strcmp(argv[1], "--wait") == 0;
+ if (wait_for_socket) {
+ argv++;
+ argc--;
+ }
+
+ if (argc < 2) {
+ usage(progname);
+ exit(5);
+ }
+
+ const char* sockname = "droidvold";
+
+ while ((sock = socket_local_client(sockname,
+ ANDROID_SOCKET_NAMESPACE_RESERVED,
+ SOCK_STREAM)) < 0) {
+ if (!wait_for_socket) {
+ PLOG(ERROR) << "Error connecting to " << sockname;
+ exit(4);
+ } else {
+ usleep(10000);
+ }
+ }
+
+ if (!strcmp(argv[1], "monitor")) {
+ exit(do_monitor(sock, 0));
+ } else {
+ exit(do_cmd(sock, argc, argv));
+ }
+}
+
+static int do_cmd(int sock, int argc, char **argv) {
+ int seq = getpid();
+
+ std::string cmd(android::base::StringPrintf("%d ", seq));
+ for (int i = 1; i < argc; i++) {
+ if (!strchr(argv[i], ' ')) {
+ cmd.append(argv[i]);
+ } else {
+ cmd.push_back('\"');
+ cmd.append(argv[i]);
+ cmd.push_back('\"');
+ }
+
+ if (i < argc - 1) {
+ cmd.push_back(' ');
+ }
+ }
+
+ if (TEMP_FAILURE_RETRY(write(sock, cmd.c_str(), cmd.length() + 1)) < 0) {
+ PLOG(ERROR) << "Failed to write command";
+ return errno;
+ }
+
+ return do_monitor(sock, seq);
+}
+
+static int do_monitor(int sock, int stop_after_seq) {
+ char buffer[4096];
+ int timeout = kCommandTimeoutMs;
+
+ if (stop_after_seq == 0) {
+ LOG(INFO) << "Connected to vold";
+ timeout = -1;
+ }
+
+ while (1) {
+ struct pollfd poll_sock = { sock, POLLIN, 0 };
+ int rc = TEMP_FAILURE_RETRY(poll(&poll_sock, 1, timeout));
+ if (rc == 0) {
+ LOG(ERROR) << "Timeout waiting for " << stop_after_seq;
+ return ETIMEDOUT;
+ } else if (rc < 0) {
+ PLOG(ERROR) << "Failed during poll";
+ return errno;
+ }
+
+ if (!(poll_sock.revents & POLLIN)) {
+ LOG(INFO) << "No data; trying again";
+ continue;
+ }
+
+ memset(buffer, 0, sizeof(buffer));
+ rc = TEMP_FAILURE_RETRY(read(sock, buffer, sizeof(buffer)));
+ if (rc == 0) {
+ LOG(ERROR) << "Lost connection, did vold crash?";
+ return ECONNRESET;
+ } else if (rc < 0) {
+ PLOG(ERROR) << "Error reading data";
+ return errno;
+ }
+
+ int offset = 0;
+ for (int i = 0; i < rc; i++) {
+ if (buffer[i] == '\0') {
+ char* res = buffer + offset;
+ fprintf(stdout, "%s\n", res);
+
+ int code = atoi(strtok(res, " "));
+ if (code >= 200 && code < 600) {
+ int seq = atoi(strtok(nullptr, " "));
+ if (seq == stop_after_seq) {
+ if (code == 200) {
+ return 0;
+ } else {
+ return code;
+ }
+ }
+ }
+
+ offset = i + 1;
+ }
+ }
+ }
+ return EIO;
+}
+
+static void usage(char *progname) {
+ LOG(INFO) << "Usage: " << progname << " [--wait] <monitor>|<cmd> [arg1] [arg2...]";
+}
diff --git a/fs/Exfat.cpp b/fs/Exfat.cpp
new file mode 100644
index 0000000..d05afbd
--- a/dev/null
+++ b/fs/Exfat.cpp
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2008 The Android Open Source 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 <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/mount.h>
+#include <sys/wait.h>
+#include <linux/kdev_t.h>
+
+#define LOG_TAG "droidVold"
+
+#include <cutils/log.h>
+#include <cutils/properties.h>
+#include <logwrap/logwrap.h>
+#include <android-base/stringprintf.h>
+#include <android-base/logging.h>
+
+#include "Exfat.h"
+
+#define UNUSED __attribute__((unused))
+
+static char FSCK_EXFAT_PATH[] = "/system/bin/fsck.exfat";
+static char MKEXFAT_PATH[] = "/system/bin/mkfs.exfat";
+static char MOUNT_EXFAT_PATH[] = "/system/bin/mount.exfat";
+
+extern "C" int mount(
+ const char *, const char *, const char *,
+ unsigned long, const void *);
+
+namespace android {
+namespace droidvold {
+namespace exfat {
+
+int Check(const char *fsPath) {
+ bool rw = true;
+ if (access(FSCK_EXFAT_PATH, X_OK)) {
+ LOG(WARNING) << "skipping exfat check";
+ return 0;
+ }
+
+ int pass = 1;
+ int rc = 0;
+ int status;
+ do {
+ const char *args[3];
+ args[0] = FSCK_EXFAT_PATH;
+ args[1] = fsPath;
+ args[2] = NULL;
+
+ rc = android_fork_execvp(2, (char **)args, &status, false, true);
+ if (rc != 0) {
+ LOG(ERROR) << "exfat check failed due to logwrap error";
+ errno = EIO;
+ return -1;
+ }
+
+ if (!WIFEXITED(status)) {
+ LOG(ERROR) << "exfat check did not exit properly";
+ errno = EIO;
+ return -1;
+ }
+
+ status = WEXITSTATUS(status);
+ switch (status) {
+ case 0:
+ LOG(INFO) << "exfat check completed ok";
+ return 0;
+
+ case 2:
+ LOG(ERROR) << "exfat check failed (not exfat filesystem)";
+ errno = ENODATA;
+ return -1;
+
+ default:
+ LOG(ERROR) << "exfat check failed.unknown exit code " << status;
+ //errno = EIO;
+ //We can still mount some disks even if fs check failed.
+ return 0;
+ }
+ } while (0);
+
+ return 0;
+}
+
+int Mount(const char *fsPath, const char *mountPoint,
+ bool ro UNUSED, bool remount UNUSED, int ownerUid UNUSED,
+ int ownerGid UNUSED, int permMask UNUSED, bool createLost UNUSED) {
+#ifdef HAS_EXFAT_FUSE
+ int rc;
+ int status;
+ do {
+ const char *args[4];
+ args[0] = MOUNT_EXFAT_PATH;
+ args[1] = fsPath;
+ args[2] = mountPoint;
+ args[3] = NULL;
+
+ rc = android_fork_execvp(3, (char **)args, &status, false, true);
+ if (rc != 0) {
+ LOG(ERROR) << "exfat mount failed due to logwrap error";
+ errno = EIO;
+ return -1;
+ }
+
+ if (!WIFEXITED(status)) {
+ LOG(ERROR) << "exfat mount did not exit properly";
+ errno = EIO;
+ return -1;
+ }
+
+ status = WEXITSTATUS(status);
+ switch (status) {
+ case 0:
+ return 0; // mount ok
+
+ default:
+ LOG(ERROR) << "exfat mount failed.unknown exit code " << rc;
+ errno = EIO;
+ return -1;
+ }
+ } while (0);
+#else
+ int rc;
+ unsigned long flags;
+ char mountData[255];
+
+ flags = MS_NODEV | MS_NOEXEC | MS_NOSUID | MS_DIRSYNC;
+ flags |= (ro ? MS_RDONLY : 0);
+ flags |= (remount ? MS_REMOUNT : 0);
+
+ sprintf(mountData, "uid=%d,gid=%d,fmask=%o,dmask=%o",
+ ownerUid, ownerGid, permMask, permMask);
+
+ rc = mount(fsPath, mountPoint, "exfat", flags, mountData);
+ if (rc && errno == EROFS) {
+ LOG(ERROR) << fsPath <<
+ " appears to be a read only filesystem - retry mount ro";
+ flags |= MS_RDONLY;
+ rc = mount(fsPath, mountPoint, "exfat", flags, mountData);
+ }
+#endif
+ return rc;
+}
+
+int Format(const char *fsPath, unsigned int numSectors) {
+ const char *args[11];
+ int rc;
+ int status;
+ args[0] = MKEXFAT_PATH;
+
+ if (numSectors) {
+ char tmp[32];
+ snprintf(tmp, sizeof(tmp), "%u", numSectors);
+ const char *size = tmp;
+ args[1] = "-s";
+ args[2] = size;
+ args[3] = fsPath;
+ args[4] = NULL;
+ rc = android_fork_execvp(4, (char **)args, &status, false, true);
+ } else {
+ args[7] = fsPath;
+ args[8] = NULL;
+ rc = android_fork_execvp(8, (char **)args, &status, false, true);
+ }
+
+ if (rc != 0) {
+ LOG(ERROR) << "exfat check failed due to logwrap error";
+ errno = EIO;
+ return -1;
+ }
+
+ if (!WIFEXITED(status)) {
+ LOG(ERROR) << "exfat check did not exit properly";
+ errno = EIO;
+ return -1;
+ }
+
+ status = WEXITSTATUS(status);
+ if (status == 0) {
+ LOG(INFO) << "exfat format ok";
+ return 0;
+ } else {
+ LOG(ERROR) << "exfat format failed.unknown exit code " << rc;
+ errno = EIO;
+ return -1;
+ }
+}
+
+} // namespace exfat
+} // namespace vold
+} // namespace android
diff --git a/fs/Exfat.h b/fs/Exfat.h
new file mode 100644
index 0000000..5b86a4d
--- a/dev/null
+++ b/fs/Exfat.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2008 The Android Open Source 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_EXFAT_H
+#define ANDROID_VOLD_EXFAT_H
+
+#include <unistd.h>
+#include <string>
+
+namespace android {
+namespace droidvold {
+namespace exfat {
+
+int Check(const char *fsPath);
+int Mount(const char *fsPath, const char *mountPoint, bool ro,
+ bool remount, int ownerUid, int ownerGid, int permMask,
+ bool createLost);
+int Format(const char *fsPath, unsigned int numSectors);
+
+} // namespace exfat
+} // namespace vold
+} // namespace android
+
+#endif
diff --git a/fs/Ext4.cpp b/fs/Ext4.cpp
new file mode 100644
index 0000000..a0708e2
--- a/dev/null
+++ b/fs/Ext4.cpp
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2012 The Android Open Source 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 <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <vector>
+#include <string>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/mount.h>
+#include <sys/wait.h>
+
+#include <linux/kdev_t.h>
+
+#define LOG_TAG "droidVold"
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <cutils/log.h>
+#include <cutils/properties.h>
+#include <logwrap/logwrap.h>
+#include <selinux/selinux.h>
+
+#include "Ext4.h"
+#include "Utils.h"
+
+using android::base::StringPrintf;
+
+namespace android {
+namespace droidvold {
+namespace ext4 {
+
+static const char* kResizefsPath = "/system/bin/resize2fs";
+static const char* kMkfsPath = "/system/bin/make_ext4fs";
+static const char* kFsckPath = "/system/bin/e2fsck";
+
+bool IsSupported() {
+ return access(kMkfsPath, X_OK) == 0
+ && access(kFsckPath, X_OK) == 0
+ && IsFilesystemSupported("ext4");
+}
+
+status_t Check(const std::string& source, const std::string& target) {
+ // The following is shamelessly borrowed from fs_mgr.c, so it should be
+ // kept in sync with any changes over there.
+
+ const char* c_source = source.c_str();
+ const char* c_target = target.c_str();
+
+ int status;
+ int ret;
+ long tmpmnt_flags = MS_NOATIME | MS_NOEXEC | MS_NOSUID;
+ char *tmpmnt_opts = (char*) "nomblk_io_submit,errors=remount-ro";
+
+ /*
+ * First try to mount and unmount the filesystem. We do this because
+ * the kernel is more efficient than e2fsck in running the journal and
+ * processing orphaned inodes, and on at least one device with a
+ * performance issue in the emmc firmware, it can take e2fsck 2.5 minutes
+ * to do what the kernel does in about a second.
+ *
+ * After mounting and unmounting the filesystem, run e2fsck, and if an
+ * error is recorded in the filesystem superblock, e2fsck will do a full
+ * check. Otherwise, it does nothing. If the kernel cannot mount the
+ * filesytsem due to an error, e2fsck is still run to do a full check
+ * fix the filesystem.
+ */
+ ret = mount(c_source, c_target, "ext4", tmpmnt_flags, tmpmnt_opts);
+ if (!ret) {
+ int i;
+ for (i = 0; i < 5; i++) {
+ // Try to umount 5 times before continuing on.
+ // Should we try rebooting if all attempts fail?
+ int result = umount(c_target);
+ if (result == 0) {
+ break;
+ }
+ ALOGW("%s(): umount(%s)=%d: %s\n", __func__, c_target, result, strerror(errno));
+ sleep(1);
+ }
+ }
+
+ /*
+ * Some system images do not have e2fsck for licensing reasons
+ * (e.g. recent SDK system images). Detect these and skip the check.
+ */
+ if (access(kFsckPath, X_OK)) {
+ ALOGD("Not running %s on %s (executable not in system image)\n",
+ kFsckPath, c_source);
+ } else {
+ ALOGD("Running %s on %s\n", kFsckPath, c_source);
+
+ std::vector<std::string> cmd;
+ cmd.push_back(kFsckPath);
+ cmd.push_back("-y");
+ cmd.push_back(c_source);
+
+ // ext4 devices are currently always trusted
+ return ForkExecvp(cmd, sFsckContext);
+ }
+
+ return 0;
+}
+
+status_t Mount(const std::string& source, const std::string& target, bool ro,
+ bool remount, bool executable, const std::string& type) {
+ int rc;
+ unsigned long flags;
+
+ const char* c_source = source.c_str();
+ const char* c_target = target.c_str();
+
+ flags = MS_NOATIME | MS_NODEV | MS_NOSUID | MS_DIRSYNC;
+
+ flags |= (executable ? 0 : MS_NOEXEC);
+ flags |= (ro ? MS_RDONLY : 0);
+ flags |= (remount ? MS_REMOUNT : 0);
+
+ rc = mount(c_source, c_target, type.c_str(), flags, NULL);
+
+ 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, type.c_str(), flags, NULL);
+ }
+
+ return rc;
+}
+
+status_t Resize(const std::string& source, unsigned long numSectors) {
+ std::vector<std::string> cmd;
+ cmd.push_back(kResizefsPath);
+ cmd.push_back("-f");
+ cmd.push_back(source);
+ cmd.push_back(StringPrintf("%lu", numSectors));
+
+ return ForkExecvp(cmd);
+}
+
+status_t Format(const std::string& source, unsigned long numSectors,
+ const std::string& target) {
+ std::vector<std::string> cmd;
+ cmd.push_back(kMkfsPath);
+ cmd.push_back("-J");
+
+ cmd.push_back("-a");
+ cmd.push_back(target);
+
+ if (numSectors) {
+ cmd.push_back("-l");
+ cmd.push_back(StringPrintf("%lu", numSectors * 512));
+ }
+
+ // Always generate a real UUID
+ cmd.push_back("-u");
+ cmd.push_back(source);
+
+ return ForkExecvp(cmd);
+}
+
+} // namespace ext4
+} // namespace vold
+} // namespace android
diff --git a/fs/Ext4.h b/fs/Ext4.h
new file mode 100644
index 0000000..c071066
--- a/dev/null
+++ b/fs/Ext4.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2012 The Android Open Source 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_EXT4_H
+#define ANDROID_VOLD_EXT4_H
+
+#include <utils/Errors.h>
+
+#include <string>
+
+namespace android {
+namespace droidvold {
+namespace ext4 {
+
+bool IsSupported();
+
+status_t Check(const std::string& source, const std::string& target);
+status_t Mount(const std::string& source, const std::string& target, bool ro,
+ bool remount, bool executable, const std::string& type="ext4");
+status_t Format(const std::string& source, unsigned long numSectors,
+ const std::string& target);
+status_t Resize(const std::string& source, unsigned long numSectors);
+
+} // namespace ext4
+} // namespace vold
+} // namespace android
+
+#endif
diff --git a/fs/F2fs.cpp b/fs/F2fs.cpp
new file mode 100644
index 0000000..0fd32fa
--- a/dev/null
+++ b/fs/F2fs.cpp
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2015 The Android Open Source 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 <vector>
+#include <string>
+
+#include <sys/mount.h>
+
+using android::base::StringPrintf;
+
+namespace android {
+namespace droidvold {
+namespace f2fs {
+
+static const char* kMkfsPath = "/system/bin/make_f2fs";
+static const char* kFsckPath = "/system/bin/fsck.f2fs";
+
+bool IsSupported() {
+ return access(kMkfsPath, X_OK) == 0
+ && access(kFsckPath, X_OK) == 0
+ && IsFilesystemSupported("f2fs");
+}
+
+status_t Check(const std::string& source) {
+ std::vector<std::string> cmd;
+ cmd.push_back(kFsckPath);
+ cmd.push_back("-a");
+ cmd.push_back(source);
+
+ // f2fs devices are currently always trusted
+ return ForkExecvp(cmd, sFsckContext);
+}
+
+status_t Mount(const std::string& source, const std::string& target) {
+ const char* c_source = source.c_str();
+ const char* c_target = target.c_str();
+ unsigned long flags = MS_NOATIME | MS_NODEV | MS_NOSUID | MS_DIRSYNC;
+
+ int res = mount(c_source, c_target, "f2fs", flags, NULL);
+ if (res != 0) {
+ PLOG(ERROR) << "Failed to mount " << source;
+ if (errno == EROFS) {
+ res = mount(c_source, c_target, "f2fs", flags | MS_RDONLY, NULL);
+ if (res != 0) {
+ PLOG(ERROR) << "Failed to mount read-only " << source;
+ }
+ }
+ }
+
+ return res;
+}
+
+status_t Format(const std::string& source) {
+ std::vector<std::string> cmd;
+ cmd.push_back(kMkfsPath);
+ cmd.push_back(source);
+
+ return ForkExecvp(cmd);
+}
+
+} // namespace f2fs
+} // namespace vold
+} // namespace android
diff --git a/fs/F2fs.h b/fs/F2fs.h
new file mode 100644
index 0000000..ede9465
--- a/dev/null
+++ b/fs/F2fs.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2015 The Android Open Source 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_F2FS_H
+#define ANDROID_VOLD_F2FS_H
+
+#include <utils/Errors.h>
+
+#include <string>
+
+namespace android {
+namespace droidvold {
+namespace f2fs {
+
+bool IsSupported();
+
+status_t Check(const std::string& source);
+status_t Mount(const std::string& source, const std::string& target);
+status_t Format(const std::string& source);
+
+} // namespace f2fs
+} // namespace vold
+} // namespace android
+
+#endif
diff --git a/fs/Hfsplus.cpp b/fs/Hfsplus.cpp
new file mode 100644
index 0000000..8a01d03
--- a/dev/null
+++ b/fs/Hfsplus.cpp
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2008 The Android Open Source 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 <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/mount.h>
+#include <sys/wait.h>
+
+#include <linux/kdev_t.h>
+
+#define LOG_TAG "droidVold"
+
+#include <cutils/log.h>
+#include <cutils/properties.h>
+#include <logwrap/logwrap.h>
+#include <android-base/stringprintf.h>
+#include <android-base/logging.h>
+
+#include "Hfsplus.h"
+
+#define UNUSED __attribute__((unused))
+
+static char FSCK_HFSPLUS_PATH[] = "/system/bin/fsck_hfsplus";
+
+extern "C" int mount(
+ const char *, const char *, const char *,
+ unsigned long, const void *);
+
+namespace android {
+namespace droidvold {
+namespace hfsplus {
+
+int Check(const char *fsPath) {
+ bool rw = true;
+ if (access(FSCK_HFSPLUS_PATH, X_OK)) {
+ LOG(WARNING) << "skipping hfsplus check";
+ return 0;
+ }
+
+ int pass = 1;
+ int rc = 0;
+ int status;
+
+ do {
+ const char *args[5];
+ args[0] = FSCK_HFSPLUS_PATH;
+ args[1] = "-p";
+ args[2] = "-f";
+ args[3] = fsPath;
+ args[4] = NULL;
+
+ rc = android_fork_execvp(4, (char **)args, &status, false, true);
+ if (rc != 0) {
+ LOG(ERROR) << "hfsplus check failed due to logwrap error";
+ errno = EIO;
+ return -1;
+ }
+
+ if (!WIFEXITED(status)) {
+ LOG(ERROR) << "hfsplus chedk did not exit properly";
+ errno = EIO;
+ return -1;
+ }
+
+ status = WEXITSTATUS(status);
+ switch (status) {
+ case 0:
+ LOG(INFO) << "hfsplus check completed ok";
+ return 0;
+
+ case 8:
+ LOG(ERROR) << "hfsplus check failed (not hfsplus filesystem)";
+ errno = ENODATA;
+ return -1;
+
+ case 4:
+ if (pass++ <= 3) {
+ LOG(INFO) << "hfsplus modified - rechecking pass " << pass;
+ continue;
+ }
+ LOG(ERROR) << "failed check hfsplus after too many rechecks";
+ errno = EIO;
+ return -1;
+
+ default:
+ LOG(ERROR) << "hfsplus check failed.unknown exit code " << rc;
+ errno = EIO;
+ return -1;
+ }
+ } while (0);
+
+ return 0;
+}
+
+int Mount(const char *fsPath, const char *mountPoint,
+ bool ro, bool remount, int ownerUid, int ownerGid,
+ int permMask UNUSED, bool createLost UNUSED) {
+ int rc;
+ unsigned long flags;
+ char mountData[255];
+
+ flags = MS_NODEV | MS_NOEXEC | MS_NOSUID | MS_DIRSYNC;
+ flags |= (ro ? MS_RDONLY : 0);
+ flags |= (remount ? MS_REMOUNT : 0);
+
+ permMask = 0;
+ sprintf(mountData, "nls=utf8,uid=%d,gid=%d", ownerUid, ownerGid);
+
+ rc = mount(fsPath, mountPoint, "hfsplus", flags, mountData);
+ if (rc && errno == EROFS) {
+ LOG(ERROR) << fsPath <<
+ " appears to be a read only filesystem - retrying mount ro";
+ flags |= MS_RDONLY;
+ rc = mount(fsPath, mountPoint, "hfsplus", flags, mountData);
+ }
+
+ return rc;
+}
+
+int Format(const char *fsPath UNUSED, unsigned int numSectors UNUSED) {
+ LOG(WARNING) << "skipping hfsplus format";
+ errno = EIO;
+ return -1;
+}
+
+} // namespace hfsplus
+} // namespace vold
+} // namespace android
diff --git a/fs/Hfsplus.h b/fs/Hfsplus.h
new file mode 100644
index 0000000..52171c8
--- a/dev/null
+++ b/fs/Hfsplus.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2008 The Android Open Source 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_HFSPLUS_H
+#define ANDROID_VOLD_HFSPLUS_H
+
+#include <unistd.h>
+#include <string>
+
+namespace android {
+namespace droidvold {
+namespace hfsplus {
+
+int Check(const char *fsPath);
+int Mount(const char *fsPath, const char *mountPoint, bool ro,
+ bool remount, int ownerUid, int ownerGid, int permMask,
+ bool createLost);
+int Format(const char *fsPath, unsigned int numSectors);
+
+} // namespace hfsplus
+} // namespace vold
+} // namespace android
+
+#endif
diff --git a/fs/Iso9660.cpp b/fs/Iso9660.cpp
new file mode 100644
index 0000000..8918f48
--- a/dev/null
+++ b/fs/Iso9660.cpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2008 The Android Open Source 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 <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/mount.h>
+
+#include <linux/kdev_t.h>
+
+#define LOG_TAG "droidVold"
+
+#include <cutils/log.h>
+#include <cutils/properties.h>
+#include <android-base/stringprintf.h>
+#include <android-base/logging.h>
+
+#include "Iso9660.h"
+
+#define UNUSED __attribute__((unused))
+
+extern "C" int mount(
+ const char *, const char *, const char *,
+ unsigned long, const void *);
+
+namespace android {
+namespace droidvold {
+namespace iso9660 {
+
+int Check(const char *fsPath UNUSED) {
+ LOG(WARNING) << "skipping iso9660 check";
+ return 0;
+}
+
+int Mount(const char *fsPath, const char *mountPoint,
+ bool ro UNUSED, bool remount, int ownerUid, int ownerGid,
+ int permMask UNUSED, bool createLost UNUSED) {
+ int rc;
+ unsigned long flags;
+ char mountData[255];
+
+ flags = MS_NODEV | MS_NOEXEC | MS_NOSUID | MS_DIRSYNC;
+ flags |= MS_RDONLY;
+ flags |= (remount ? MS_REMOUNT : 0);
+
+ sprintf(mountData, "utf8,uid=%d,gid=%d",ownerUid, ownerGid);
+
+ rc = mount(fsPath, mountPoint, "iso9660", flags, mountData);
+ if (rc !=0) {
+ rc = mount(fsPath, mountPoint, "udf", flags, mountData);
+ }
+
+ return rc;
+}
+
+int Format(const char *fsPath UNUSED, unsigned int numSectors UNUSED) {
+ LOG(WARNING) << "skipping iso9660 format";
+ errno = EIO;
+ return -1;
+}
+
+} // namespace iso9660
+} // namespace vold
+} // namespace android
diff --git a/fs/Iso9660.h b/fs/Iso9660.h
new file mode 100644
index 0000000..1606621
--- a/dev/null
+++ b/fs/Iso9660.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2008 The Android Open Source 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_ISO9660_H
+#define ANDROID_VOLD_ISO9660_H
+
+#include <unistd.h>
+#include <string>
+
+namespace android {
+namespace droidvold {
+namespace iso9660 {
+
+int Check(const char *fsPath);
+int Mount(const char *fsPath, const char *mountPoint, bool ro,
+ bool remount, int ownerUid, int ownerGid, int permMask,
+ bool createLost);
+int format(const char *fsPath, unsigned int numSectors);
+
+} // namespace iso9660
+} // namespace vold
+} // namespace android
+
+#endif
diff --git a/fs/Ntfs.cpp b/fs/Ntfs.cpp
new file mode 100644
index 0000000..7c5a04d
--- a/dev/null
+++ b/fs/Ntfs.cpp
@@ -0,0 +1,249 @@
+/*
+ * Copyright (C) 2008 The Android Open Source 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 <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/mount.h>
+#include <sys/wait.h>
+
+#include <linux/kdev_t.h>
+
+#define LOG_TAG "droidVold"
+
+#include <cutils/log.h>
+#include <cutils/properties.h>
+#include <logwrap/logwrap.h>
+#include <android-base/stringprintf.h>
+#include <android-base/logging.h>
+
+#include "Ntfs.h"
+
+#define UNUSED __attribute__((unused))
+
+#ifdef HAS_NTFS_3G
+static char NTFS_3G_PATH[] = "/system/bin/ntfs-3g";
+static char NTFSFIX_3G_PATH[] = "/system/bin/ntfsfix";
+static char MKNTFS_3G_PATH[] = "/system/bin/mkntfs";
+#endif /* HAS_NTFS_3G */
+
+extern "C" int mount(
+ const char *, const char *, const char *,
+ unsigned long, const void *);
+
+namespace android {
+namespace droidvold {
+namespace ntfs {
+
+int Check(const char *fsPath UNUSED) {
+#ifndef HAS_NTFS_3G
+ LOG(WARNING) << "skipping ntfs check";
+ return 0;
+#else
+ bool rw = true;
+ if (access(NTFSFIX_3G_PATH, X_OK)) {
+ LOG(WARNING) << "skipping ntfs check";
+ return 0;
+ }
+
+ int pass = 1;
+ int rc = 0;
+ int status;
+ do {
+ const char *args[4];
+ args[0] = NTFSFIX_3G_PATH;
+ args[1] = "-n";
+ args[2] = fsPath;
+ args[3] = NULL;
+
+ rc = android_fork_execvp(3, (char **)args, &status, false, true);
+ if (rc != 0) {
+ LOG(ERROR) << "ntfs check failed due to logwrap error";
+ errno = EIO;
+ return -1;
+ }
+
+ if (!WIFEXITED(status)) {
+ LOG(ERROR) << "ntfs check did not exit properly";
+ errno = EIO;
+ return -1;
+ }
+
+ status = WEXITSTATUS(status);
+
+ switch (status) {
+ case 0:
+ LOG(INFO) << "ntfs check completed ok";
+ return 0;
+ case 1:
+ LOG(ERROR) << "ntfs check failed (not a ntfs filesystem)";
+ errno = ENODATA;
+ return -1;
+ default:
+ LOG(ERROR) << "ntfs check failed.unknown exit code " << rc;
+ errno = EIO;
+ return -1;
+ }
+ } while (0);
+
+ return 0;
+#endif
+}
+
+int Mount(const char *fsPath, const char *mountPoint,
+ bool ro UNUSED, bool remount UNUSED, int ownerUid,
+ int ownerGid, int permMask, bool createLost UNUSED) {
+#ifndef HAS_NTFS_3G
+ int rc;
+ unsigned long flags;
+ char mountData[255];
+
+ flags = MS_NODEV | MS_NOEXEC | MS_NOSUID | MS_DIRSYNC;
+ flags |= (ro ? MS_RDONLY : 0);
+ flags |= (remount ? MS_REMOUNT : 0);
+
+ permMask = 0;
+ sprintf(mountData, "uid=%d,gid=%d,fmask=%o,dmask=%o",
+ ownerUid, ownerGid, permMask, permMask);
+
+ rc = mount(fsPath, mountPoint, "ntfs", flags, mountData);
+ if (rc && errno == EROFS) {
+ LOG(ERROR) << fsPath <<
+ " appears to be a read only filesystem - retry mount ro";
+ flags |= MS_RDONLY;
+ rc = mount(fsPath, mountPoint, "ntfs", flags, mountData);
+ }
+
+ return rc;
+#else
+ int rc;
+ int status;
+ const char *args[11];
+ char mountData[255];
+
+ sprintf(mountData, "locale=utf8,uid=%d,gid=%d,fmask=%o,dmask=%o",
+ ownerUid, ownerGid, permMask, permMask);
+
+ args[0] = NTFS_3G_PATH;
+ args[1] = fsPath;
+ args[2] = mountPoint;
+ args[3] = "-o";
+ args[4] = mountData;
+ args[5] = NULL;
+
+ rc = android_fork_execvp(5, (char **)args, &status, false, true);
+ if (rc != 0) {
+ LOG(ERROR) << "ntfs check failed due to logwrap error";
+ errno = EIO;
+ return -1;
+ }
+
+ if (!WIFEXITED(status)) {
+ LOG(ERROR) << "ntfs check did not exit properly";
+ errno = EIO;
+ return -1;
+ }
+
+ status = WEXITSTATUS(status);
+ return status;
+#endif /* HAS_NTFS_3G */
+}
+
+int Format(const char *fsPath UNUSED, unsigned int numSectors UNUSED) {
+#ifndef HAS_NTFS_3G
+ LOG(WARNING) << "skipping ntfs format";
+ errno = EIO;
+ return -1;
+#else
+ char * label = NULL;
+ int fd;
+ int argc;
+ const char *args[10];
+ int rc;
+ int status;
+
+ args[0] = MKNTFS_3G_PATH;
+ args[1] = "-f";
+
+ if (numSectors) {
+ char tmp[32];
+ snprintf(tmp, sizeof(tmp), "%u", numSectors);
+ const char *size = tmp;
+ args[2] = "-s";
+ args[3] = size;
+ if (label == NULL) {
+ args[4] = fsPath;
+ args[5] = NULL;
+ argc = 6;
+ } else {
+ args[4] = "-L";
+ args[5] = label;
+ args[6] = fsPath;
+ args[7] = NULL;
+ argc = 7;
+ }
+ } else {
+ if (label == NULL) {
+ args[2] = fsPath;
+ args[3] = NULL;
+ argc = 3;
+ } else {
+ args[2] = "-L";
+ args[3] = label;
+ args[4] = fsPath;
+ args[5] = NULL;
+ argc = 5;
+ }
+ }
+
+ rc = android_fork_execvp(argc, (char **)args, &status, false, true);
+ if (rc != 0) {
+ LOG(ERROR) << "ntfs format failed due to logwrap error";
+ errno = EIO;
+ return -1;
+ }
+
+ if (!WIFEXITED(status)) {
+ LOG(ERROR) << "ntfs format did not exit properly";
+ errno = EIO;
+ return -1;
+ }
+
+ status = WEXITSTATUS(status);
+ if (status == 0) {
+ LOG(INFO) << "ntfs formatted ok";
+ } else {
+ LOG(ERROR) << "ntfs Format failed.unknown exit code " << rc;
+ errno = EIO;
+ return -1;
+ }
+ return 0;
+#endif
+}
+
+} // namespace ntfs
+} // namespace vold
+} // namespace android
diff --git a/fs/Ntfs.h b/fs/Ntfs.h
new file mode 100644
index 0000000..f68080f
--- a/dev/null
+++ b/fs/Ntfs.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2008 The Android Open Source 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 <unistd.h>
+#include <string>
+
+namespace android {
+namespace droidvold {
+namespace ntfs {
+
+int Check(const char *fsPath);
+int Mount(const char *fsPath, const char *mountPoint, bool ro,
+ bool remount, int ownerUid, int ownerGid, int permMask,
+ bool createLost);
+int Format(const char *fsPath, unsigned int numSectors);
+
+} // namespace ntfs
+} // namespace vold
+} // namespace android
+
+#endif
diff --git a/fs/Vfat.cpp b/fs/Vfat.cpp
new file mode 100644
index 0000000..6b9d53c
--- a/dev/null
+++ b/fs/Vfat.cpp
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) 2008 The Android Open Source 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 <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/mount.h>
+#include <sys/wait.h>
+#include <linux/fs.h>
+#include <sys/ioctl.h>
+
+#include <linux/kdev_t.h>
+
+#define LOG_TAG "droidVold"
+
+#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 "Vfat.h"
+#include "Utils.h"
+
+using android::base::StringPrintf;
+
+namespace android {
+namespace droidvold {
+namespace vfat {
+
+static const char* kMkfsPath = "/system/bin/newfs_msdos";
+static const char* kFsckPath = "/system/bin/fsck_msdos";
+
+bool IsSupported() {
+ return access(kMkfsPath, X_OK) == 0
+ && access(kFsckPath, X_OK) == 0
+ && IsFilesystemSupported("vfat");
+}
+
+status_t Check(const std::string& source) {
+ if (access(kFsckPath, X_OK)) {
+ SLOGW("Skipping fs checks\n");
+ return 0;
+ }
+
+ int pass = 1;
+ int rc = 0;
+ do {
+ std::vector<std::string> cmd;
+ cmd.push_back(kFsckPath);
+ cmd.push_back("-p");
+ cmd.push_back("-f");
+ cmd.push_back(source);
+
+ // Fat devices are currently always untrusted
+ rc = ForkExecvp(cmd, sFsckUntrustedContext);
+
+ if (rc < 0) {
+ SLOGE("Filesystem check failed due to logwrap error");
+ errno = EIO;
+ return -1;
+ }
+
+ switch (rc) {
+ case 0:
+ SLOGI("Filesystem check completed OK");
+ return 0;
+
+ case 2:
+ SLOGE("Filesystem check failed (not a FAT filesystem)");
+ errno = ENODATA;
+ return -1;
+
+ case 4:
+ if (pass++ <= 3) {
+ SLOGW("Filesystem modified - rechecking (pass %d)",
+ pass);
+ continue;
+ }
+ SLOGE("Failing check after too many rechecks");
+ errno = EIO;
+ return -1;
+
+ case 8:
+ SLOGE("Filesystem check failed (no filesystem)");
+ errno = ENODATA;
+ return -1;
+
+ default:
+ SLOGE("Filesystem check failed (unknown exit code %d)", rc);
+ errno = EIO;
+ return -1;
+ }
+ } while (0);
+
+ return 0;
+}
+
+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;
+
+ flags |= (executable ? 0 : MS_NOEXEC);
+ flags |= (ro ? MS_RDONLY : 0);
+ flags |= (remount ? MS_REMOUNT : 0);
+
+ sprintf(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;
+}
+
+status_t Format(const std::string& source, unsigned long numSectors) {
+ std::vector<std::string> cmd;
+ cmd.push_back(kMkfsPath);
+ cmd.push_back("-F");
+ cmd.push_back("32");
+ cmd.push_back("-O");
+ cmd.push_back("android");
+ cmd.push_back("-c");
+ cmd.push_back("64");
+ cmd.push_back("-A");
+
+ if (numSectors) {
+ cmd.push_back("-s");
+ cmd.push_back(StringPrintf("%lu", numSectors));
+ }
+
+ cmd.push_back(source);
+
+ int rc = ForkExecvp(cmd);
+ if (rc < 0) {
+ SLOGE("Filesystem format failed due to logwrap error");
+ errno = EIO;
+ return -1;
+ }
+
+ if (rc == 0) {
+ SLOGI("Filesystem formatted OK");
+ return 0;
+ } else {
+ SLOGE("Format failed (unknown exit code %d)", rc);
+ errno = EIO;
+ return -1;
+ }
+ return 0;
+}
+
+} // namespace vfat
+} // namespace vold
+} // namespace android
diff --git a/fs/Vfat.h b/fs/Vfat.h
new file mode 100644
index 0000000..383ec74
--- a/dev/null
+++ b/fs/Vfat.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2008 The Android Open Source 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_VFAT_H
+#define ANDROID_VOLD_VFAT_H
+
+#include <utils/Errors.h>
+
+#include <string>
+
+namespace android {
+namespace droidvold {
+namespace vfat {
+
+bool IsSupported();
+
+status_t Check(const std::string& source);
+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);
+status_t Format(const std::string& source, unsigned long numSectors);
+
+} // namespace vfat
+} // namespace vold
+} // namespace android
+
+#endif
diff --git a/main.cpp b/main.cpp
new file mode 100644
index 0000000..7a94843
--- a/dev/null
+++ b/main.cpp
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2008 The Android Open Source 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 "VolumeManager.h"
+#include "CommandListener.h"
+#include "NetlinkManager.h"
+#include "sehandle.h"
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <cutils/klog.h>
+#include <cutils/properties.h>
+#include <cutils/sockets.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <getopt.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <fs_mgr.h>
+
+#define LOG_TAG "droidVold"
+
+#include "cutils/klog.h"
+#include "cutils/log.h"
+#include "cutils/properties.h"
+
+static int process_config(VolumeManager *vm, bool* has_adoptable);
+
+static void coldboot(const char *path);
+static void parse_args(int argc, char** argv);
+static void set_media_poll_time(void);
+
+struct fstab *fstab;
+
+struct selabel_handle *sehandle;
+
+using android::base::StringPrintf;
+
+int main(int argc, char** argv) {
+ setenv("ANDROID_LOG_TAGS", "*:v", 1);
+ android::base::InitLogging(argv, android::base::LogdLogger(android::base::SYSTEM));
+
+ LOG(INFO) << "doildVold 1.0 firing up";
+
+ VolumeManager *vm;
+ NetlinkManager *nm;
+ CommandListener *cl;
+
+ parse_args(argc, argv);
+
+ sehandle = selinux_android_file_context_handle();
+ if (sehandle) {
+ selinux_android_set_sehandle(sehandle);
+ }
+
+ mkdir("/dev/block/droidvold", 0755);
+
+ /* Create our singleton managers */
+ if (!(vm = VolumeManager::Instance())) {
+ LOG(ERROR) << "Unable to create VolumeManager";
+ exit(1);
+ }
+
+ if (!(nm = NetlinkManager::Instance())) {
+ LOG(ERROR) << "Unable to create NetlinkManager";
+ exit(1);
+ }
+
+ if (property_get_bool("droidvold.debug", false)) {
+ vm->setDebug(true);
+ }
+
+ cl = new CommandListener();
+ vm->setBroadcaster((SocketListener *) cl);
+ nm->setBroadcaster((SocketListener *) cl);
+
+ if (vm->start()) {
+ PLOG(ERROR) << "Unable to start VolumeManager";
+ exit(1);
+ }
+
+ bool has_adoptable;
+
+ if (process_config(vm, &has_adoptable)) {
+ PLOG(ERROR) << "Error reading configuration... continuing anyways";
+ }
+
+ if (nm->start()) {
+ PLOG(ERROR) << "Unable to start NetlinkManager";
+ exit(1);
+ }
+
+ set_media_poll_time();
+ coldboot("/sys/block");
+
+ /*
+ * Now that we're up, we can respond to commands
+ */
+ if (cl->startListener()) {
+ PLOG(ERROR) << "Unable to start CommandListener";
+ exit(1);
+ }
+
+ // Eventually we'll become the monitoring thread
+ while (1) {
+ sleep(1000);
+ }
+
+ LOG(ERROR) << "droidVold exiting";
+ exit(0);
+}
+
+static void set_media_poll_time(void) {
+ int fd;
+
+ fd = open ("/sys/module/block/parameters/events_dfl_poll_msecs", O_WRONLY);
+ if (fd >= 0) {
+ write(fd, "2000", 4);
+ close (fd);
+ } else {
+ LOG(ERROR) << "kernel not support media poll uevent!";
+ }
+}
+
+static void parse_args(int argc, char** argv) {
+ static struct option opts[] = {
+ {"blkid_context", required_argument, 0, 'b' },
+ {"blkid_untrusted_context", required_argument, 0, 'B' },
+ {"fsck_context", required_argument, 0, 'f' },
+ {"fsck_untrusted_context", required_argument, 0, 'F' },
+ };
+
+ int c;
+ while ((c = getopt_long(argc, argv, "", opts, nullptr)) != -1) {
+ switch (c) {
+ case 'b': android::droidvold::sBlkidContext = optarg; break;
+ case 'B': android::droidvold::sBlkidUntrustedContext = optarg; break;
+ case 'f': android::droidvold::sFsckContext = optarg; break;
+ case 'F': android::droidvold::sFsckUntrustedContext = optarg; break;
+ }
+ }
+
+ CHECK(android::droidvold::sBlkidContext != nullptr);
+ CHECK(android::droidvold::sBlkidUntrustedContext != nullptr);
+ CHECK(android::droidvold::sFsckContext != nullptr);
+ CHECK(android::droidvold::sFsckUntrustedContext != nullptr);
+}
+
+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);
+ 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, bool* has_adoptable) {
+ std::string path(android::droidvold::DefaultFstabPath());
+ fstab = fs_mgr_read_fstab(path.c_str());
+ if (!fstab) {
+ PLOG(ERROR) << "Failed to open default fstab " << path;
+ return -1;
+ }
+
+ /* Loop through entries looking for ones that vold manages */
+ *has_adoptable = false;
+ for (int i = 0; i < fstab->num_entries; i++) {
+ if (fs_mgr_is_voldmanaged(&fstab->recs[i])) {
+ if (fs_mgr_is_nonremovable(&fstab->recs[i])) {
+ LOG(WARNING) << "nonremovable no longer supported; ignoring volume";
+ continue;
+ }
+
+ std::string sysPattern(fstab->recs[i].blk_device);
+ std::string nickname(fstab->recs[i].label);
+ int flags = 0;
+
+ if (fs_mgr_is_encryptable(&fstab->recs[i])) {
+ flags |= android::droidvold::Disk::Flags::kAdoptable;
+ *has_adoptable = true;
+ }
+ if (fs_mgr_is_noemulatedsd(&fstab->recs[i])
+ || property_get_bool("vold.debug.default_primary", false)) {
+ flags |= android::droidvold::Disk::Flags::kDefaultPrimary;
+ }
+
+ vm->addDiskSource(std::shared_ptr<VolumeManager::DiskSource>(
+ new VolumeManager::DiskSource(sysPattern, nickname, flags)));
+ }
+ }
+ return 0;
+}
diff --git a/secontext.cpp b/secontext.cpp
new file mode 100644
index 0000000..1465a11
--- a/dev/null
+++ b/secontext.cpp
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2016 The Android Open Source 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 "secontext.h"
+
+security_context_t secontextFsck()
+{
+ return android::droidvold::sFsckContext;
+}
diff --git a/secontext.h b/secontext.h
new file mode 100644
index 0000000..08ad48e
--- a/dev/null
+++ b/secontext.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2016 The Android Open Source 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 _SECONTEXT_H_
+#define _SECONTEXT_H_
+
+#include <selinux/selinux.h>
+
+__BEGIN_DECLS
+security_context_t secontextFsck();
+__END_DECLS
+
+#endif
diff --git a/sehandle.h b/sehandle.h
new file mode 100644
index 0000000..f59d7eb
--- a/dev/null
+++ b/sehandle.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2014 The Android Open Source 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