1/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "Utils.h"
18#include "VolumeBase.h"
19#include "VolumeManager.h"
20#include "ResponseCode.h"
21
22#include <android-base/stringprintf.h>
23#include <android-base/logging.h>
24
25#include <fcntl.h>
26#include <stdlib.h>
27#include <sys/mount.h>
28#include <sys/stat.h>
29#include <sys/types.h>
30
31using android::base::StringPrintf;
32
33#define DEBUG 1
34
35namespace android {
36namespace vold {
37
38VolumeBase::VolumeBase(Type type) :
39        mType(type), mMountFlags(0), mMountUserId(-1), mCreated(false), mState(
40                State::kUnmounted), mSilent(false) {
41}
42
43VolumeBase::~VolumeBase() {
44    CHECK(!mCreated);
45}
46
47void VolumeBase::setState(State state) {
48    mState = state;
49    notifyEvent(ResponseCode::VolumeStateChanged, StringPrintf("%d", mState));
50}
51
52status_t VolumeBase::setDiskId(const std::string& diskId) {
53    if (mCreated) {
54        LOG(WARNING) << getId() << " diskId change requires destroyed";
55        return -EBUSY;
56    }
57
58    mDiskId = diskId;
59    return OK;
60}
61
62status_t VolumeBase::setPartGuid(const std::string& partGuid) {
63    if (mCreated) {
64        LOG(WARNING) << getId() << " partGuid change requires destroyed";
65        return -EBUSY;
66    }
67
68    mPartGuid = partGuid;
69    return OK;
70}
71
72status_t VolumeBase::setMountFlags(int mountFlags) {
73    if ((mState != State::kUnmounted) && (mState != State::kUnmountable)) {
74        LOG(WARNING) << getId() << " flags change requires state unmounted or unmountable";
75        return -EBUSY;
76    }
77
78    mMountFlags = mountFlags;
79    return OK;
80}
81
82status_t VolumeBase::setMountUserId(userid_t mountUserId) {
83    if ((mState != State::kUnmounted) && (mState != State::kUnmountable)) {
84        LOG(WARNING) << getId() << " user change requires state unmounted or unmountable";
85        return -EBUSY;
86    }
87
88    mMountUserId = mountUserId;
89    return OK;
90}
91
92status_t VolumeBase::setSilent(bool silent) {
93    if (mCreated) {
94        LOG(WARNING) << getId() << " silence change requires destroyed";
95        return -EBUSY;
96    }
97
98    mSilent = silent;
99    return OK;
100}
101
102status_t VolumeBase::setId(const std::string& id) {
103    if (mCreated) {
104        LOG(WARNING) << getId() << " id change requires not created";
105        return -EBUSY;
106    }
107
108    mId = id;
109    return OK;
110}
111
112status_t VolumeBase::setPath(const std::string& path) {
113    if (mState != State::kChecking) {
114        LOG(WARNING) << getId() << " path change requires state checking";
115        return -EBUSY;
116    }
117
118    mPath = path;
119    notifyEvent(ResponseCode::VolumePathChanged, mPath);
120    return OK;
121}
122
123status_t VolumeBase::setInternalPath(const std::string& internalPath) {
124    if (mState != State::kChecking) {
125        LOG(WARNING) << getId() << " internal path change requires state checking";
126        return -EBUSY;
127    }
128
129    mInternalPath = internalPath;
130    notifyEvent(ResponseCode::VolumeInternalPathChanged, mInternalPath);
131    return OK;
132}
133
134void VolumeBase::notifyEvent(int event) {
135    if (mSilent) return;
136    VolumeManager::Instance()->getBroadcaster()->sendBroadcast(event,
137            getId().c_str(), false);
138}
139
140void VolumeBase::notifyEvent(int event, const std::string& value) {
141    if (mSilent) return;
142    VolumeManager::Instance()->getBroadcaster()->sendBroadcast(event,
143            StringPrintf("%s %s", getId().c_str(), value.c_str()).c_str(), false);
144}
145
146void VolumeBase::addVolume(const std::shared_ptr<VolumeBase>& volume) {
147    mVolumes.push_back(volume);
148}
149
150void VolumeBase::removeVolume(const std::shared_ptr<VolumeBase>& volume) {
151    mVolumes.remove(volume);
152}
153
154std::shared_ptr<VolumeBase> VolumeBase::findVolume(const std::string& id) {
155    for (auto vol : mVolumes) {
156        if (vol->getId() == id) {
157            return vol;
158        }
159    }
160    return nullptr;
161}
162
163status_t VolumeBase::create() {
164    CHECK(!mCreated);
165
166    mCreated = true;
167    status_t res = doCreate();
168    notifyEvent(ResponseCode::VolumeCreated,
169            StringPrintf("%d \"%s\" \"%s\"", mType, mDiskId.c_str(), mPartGuid.c_str()));
170    setState(State::kUnmounted);
171    return res;
172}
173
174status_t VolumeBase::doCreate() {
175    return OK;
176}
177
178status_t VolumeBase::destroy() {
179    CHECK(mCreated);
180
181    if (mState == State::kMounted) {
182        unmount();
183        setState(State::kBadRemoval);
184    } else {
185        setState(State::kRemoved);
186    }
187
188    notifyEvent(ResponseCode::VolumeDestroyed);
189    status_t res = doDestroy();
190    mCreated = false;
191    return res;
192}
193
194status_t VolumeBase::doDestroy() {
195    return OK;
196}
197
198status_t VolumeBase::mount() {
199    if ((mState != State::kUnmounted) && (mState != State::kUnmountable)) {
200        LOG(WARNING) << getId() << " mount requires state unmounted or unmountable";
201        return -EBUSY;
202    }
203
204    setState(State::kChecking);
205    status_t res = doMount();
206    if (res == OK) {
207        setState(State::kMounted);
208    } else {
209        setState(State::kUnmountable);
210    }
211
212    return res;
213}
214
215status_t VolumeBase::unmount() {
216    if (mState != State::kMounted) {
217        LOG(WARNING) << getId() << " unmount requires state mounted";
218        return -EBUSY;
219    }
220
221    setState(State::kEjecting);
222    for (auto vol : mVolumes) {
223        if (vol->destroy()) {
224            LOG(WARNING) << getId() << " failed to destroy " << vol->getId()
225                    << " stacked above";
226        }
227    }
228    mVolumes.clear();
229
230    status_t res = doUnmount();
231    setState(State::kUnmounted);
232    return res;
233}
234
235status_t VolumeBase::format(const std::string& fsType) {
236    if (mState == State::kMounted) {
237        unmount();
238    }
239
240    if ((mState != State::kUnmounted) && (mState != State::kUnmountable)) {
241        LOG(WARNING) << getId() << " format requires state unmounted or unmountable";
242        return -EBUSY;
243    }
244
245    setState(State::kFormatting);
246    status_t res = doFormat(fsType);
247    setState(State::kUnmounted);
248    return res;
249}
250
251status_t VolumeBase::doFormat(const std::string& fsType) {
252    return -ENOTSUP;
253}
254
255}  // namespace vold
256}  // namespace android
257