author | Baocheng 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) |
commit | 178b7fc63dd6534c0a55963b5f179ec11e829089 (patch) | |
tree | 8f0eb5feee21ab2151fbf9877720a48a80f48b97 | |
parent | c0076c31c0c9c92fe928a43eff4ef1bc5a62fd7c (diff) | |
download | droidvold-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
-rw-r--r-- | Android.mk | 72 | ||||
-rw-r--r-- | CommandListener.cpp | 224 | ||||
-rw-r--r-- | CommandListener.h | 50 | ||||
-rw-r--r-- | Disk.cpp | 479 | ||||
-rw-r--r-- | Disk.h | 124 | ||||
-rw-r--r-- | NetlinkHandler.cpp | 57 | ||||
-rw-r--r-- | NetlinkHandler.h | 34 | ||||
-rw-r--r-- | NetlinkManager.cpp | 109 | ||||
-rw-r--r-- | NetlinkManager.h | 48 | ||||
-rw-r--r-- | Process.cpp | 222 | ||||
-rw-r--r-- | Process.h | 44 | ||||
-rw-r--r-- | PublicVolume.cpp | 299 | ||||
-rw-r--r-- | PublicVolume.h | 91 | ||||
-rw-r--r-- | ResponseCode.cpp | 42 | ||||
-rw-r--r-- | ResponseCode.h | 81 | ||||
-rw-r--r-- | Utils.cpp | 787 | ||||
-rw-r--r-- | Utils.h | 167 | ||||
-rw-r--r-- | VoldCommand.cpp | 21 | ||||
-rw-r--r-- | VoldCommand.h | 28 | ||||
-rw-r--r-- | VolumeBase.cpp | 255 | ||||
-rw-r--r-- | VolumeBase.h | 159 | ||||
-rw-r--r-- | VolumeManager.cpp | 366 | ||||
-rw-r--r-- | VolumeManager.h | 118 | ||||
-rw-r--r-- | droidvold.rc | 7 | ||||
-rw-r--r-- | dvdc.cpp | 178 | ||||
-rw-r--r-- | fs/Exfat.cpp | 215 | ||||
-rw-r--r-- | fs/Exfat.h | 37 | ||||
-rw-r--r-- | fs/Ext4.cpp | 186 | ||||
-rw-r--r-- | fs/Ext4.h | 41 | ||||
-rw-r--r-- | fs/F2fs.cpp | 82 | ||||
-rw-r--r-- | fs/F2fs.h | 38 | ||||
-rw-r--r-- | fs/Hfsplus.cpp | 152 | ||||
-rw-r--r-- | fs/Hfsplus.h | 37 | ||||
-rw-r--r-- | fs/Iso9660.cpp | 87 | ||||
-rw-r--r-- | fs/Iso9660.h | 37 | ||||
-rw-r--r-- | fs/Ntfs.cpp | 249 | ||||
-rw-r--r-- | fs/Ntfs.h | 38 | ||||
-rw-r--r-- | fs/Vfat.cpp | 209 | ||||
-rw-r--r-- | fs/Vfat.h | 40 | ||||
-rw-r--r-- | main.cpp | 247 | ||||
-rw-r--r-- | secontext.cpp | 22 | ||||
-rw-r--r-- | secontext.h | 25 | ||||
-rw-r--r-- | sehandle.h | 24 |
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 @@ -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 @@ -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 |