1d6fbcc389
When requested, kick off a thread that will migrate storage contents between two locations. This is performed in several steps that also interact with the framework: 1. Take old and new volumes offline during migration 2. Wipe new location clean (10% of progress) 3. Copy files from old to new (60% of progress) 4. Inform framework that move was successful so it can persist 5. Wipe old location clean (15% of progress) Derives a hacky progress estimate by using a rough proxy of free disk space changes while a cp/rm is taking place. Add new internal path for direct access to volumes to bypass any FUSE emulation overhead, and send it to framework. Remove mutex around various exec calls since setexeccon() is already per-thread. Bug: 19993667 Change-Id: Ibcb4f6fe0126d05b2365f316f53e71dc3e79a2b8
246 lines
6 KiB
C++
246 lines
6 KiB
C++
/*
|
|
* 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 <base/stringprintf.h>
|
|
#include <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;
|
|
|
|
#define DEBUG 1
|
|
|
|
namespace android {
|
|
namespace vold {
|
|
|
|
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::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", mType, mDiskId.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() {
|
|
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();
|
|
setState(State::kUnmounted);
|
|
return res;
|
|
}
|
|
|
|
status_t VolumeBase::doFormat() {
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
} // namespace vold
|
|
} // namespace android
|