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
21#include <android-base/stringprintf.h>
22#include <android-base/logging.h>
23
24#include <fcntl.h>
25#include <stdlib.h>
26#include <sys/mount.h>
27#include <sys/stat.h>
28#include <sys/types.h>
29
30using android::base::StringPrintf;
31
32namespace android {
33namespace vold {
34
35VolumeBase::VolumeBase(Type type) :
36        mType(type), mMountFlags(0), mMountUserId(-1), mCreated(false), mState(
37                State::kUnmounted), mSilent(false) {
38}
39
40VolumeBase::~VolumeBase() {
41    CHECK(!mCreated);
42}
43
44void VolumeBase::setState(State state) {
45    mState = state;
46
47    auto listener = getListener();
48    if (listener) listener->onVolumeStateChanged(getId(), static_cast<int32_t>(mState));
49}
50
51status_t VolumeBase::setDiskId(const std::string& diskId) {
52    if (mCreated) {
53        LOG(WARNING) << getId() << " diskId change requires destroyed";
54        return -EBUSY;
55    }
56
57    mDiskId = diskId;
58    return OK;
59}
60
61status_t VolumeBase::setPartGuid(const std::string& partGuid) {
62    if (mCreated) {
63        LOG(WARNING) << getId() << " partGuid change requires destroyed";
64        return -EBUSY;
65    }
66
67    mPartGuid = partGuid;
68    return OK;
69}
70
71status_t VolumeBase::setMountFlags(int mountFlags) {
72    if ((mState != State::kUnmounted) && (mState != State::kUnmountable)) {
73        LOG(WARNING) << getId() << " flags change requires state unmounted or unmountable";
74        return -EBUSY;
75    }
76
77    mMountFlags = mountFlags;
78    return OK;
79}
80
81status_t VolumeBase::setMountUserId(userid_t mountUserId) {
82    if ((mState != State::kUnmounted) && (mState != State::kUnmountable)) {
83        LOG(WARNING) << getId() << " user change requires state unmounted or unmountable";
84        return -EBUSY;
85    }
86
87    mMountUserId = mountUserId;
88    return OK;
89}
90
91status_t VolumeBase::setSilent(bool silent) {
92    if (mCreated) {
93        LOG(WARNING) << getId() << " silence change requires destroyed";
94        return -EBUSY;
95    }
96
97    mSilent = silent;
98    return OK;
99}
100
101status_t VolumeBase::setId(const std::string& id) {
102    if (mCreated) {
103        LOG(WARNING) << getId() << " id change requires not created";
104        return -EBUSY;
105    }
106
107    mId = id;
108    return OK;
109}
110
111status_t VolumeBase::setPath(const std::string& path) {
112    if (mState != State::kChecking) {
113        LOG(WARNING) << getId() << " path change requires state checking";
114        return -EBUSY;
115    }
116
117    mPath = path;
118
119    auto listener = getListener();
120    if (listener) listener->onVolumePathChanged(getId(), mPath);
121
122    return OK;
123}
124
125status_t VolumeBase::setInternalPath(const std::string& internalPath) {
126    if (mState != State::kChecking) {
127        LOG(WARNING) << getId() << " internal path change requires state checking";
128        return -EBUSY;
129    }
130
131    mInternalPath = internalPath;
132
133    auto listener = getListener();
134    if (listener) listener->onVolumeInternalPathChanged(getId(), mInternalPath);
135
136    return OK;
137}
138
139android::sp<android::os::IVoldListener> VolumeBase::getListener() {
140    if (mSilent) {
141        return nullptr;
142    } else {
143        return VolumeManager::Instance()->getListener();
144    }
145}
146
147void VolumeBase::addVolume(const std::shared_ptr<VolumeBase>& volume) {
148    mVolumes.push_back(volume);
149}
150
151void VolumeBase::removeVolume(const std::shared_ptr<VolumeBase>& volume) {
152    mVolumes.remove(volume);
153}
154
155std::shared_ptr<VolumeBase> VolumeBase::findVolume(const std::string& id) {
156    for (auto vol : mVolumes) {
157        if (vol->getId() == id) {
158            return vol;
159        }
160    }
161    return nullptr;
162}
163
164status_t VolumeBase::create() {
165    CHECK(!mCreated);
166
167    mCreated = true;
168    status_t res = doCreate();
169
170    auto listener = getListener();
171    if (listener) listener->onVolumeCreated(getId(),
172            static_cast<int32_t>(mType), mDiskId, mPartGuid);
173
174    setState(State::kUnmounted);
175    return res;
176}
177
178status_t VolumeBase::doCreate() {
179    return OK;
180}
181
182status_t VolumeBase::destroy() {
183    CHECK(mCreated);
184
185    if (mState == State::kMounted) {
186        unmount();
187        setState(State::kBadRemoval);
188    } else {
189        setState(State::kRemoved);
190    }
191
192
193    auto listener = getListener();
194    if (listener) listener->onVolumeDestroyed(getId());
195
196    status_t res = doDestroy();
197    mCreated = false;
198    return res;
199}
200
201status_t VolumeBase::doDestroy() {
202    return OK;
203}
204
205status_t VolumeBase::mount() {
206    if ((mState != State::kUnmounted) && (mState != State::kUnmountable)) {
207        LOG(WARNING) << getId() << " mount requires state unmounted or unmountable";
208        return -EBUSY;
209    }
210
211    setState(State::kChecking);
212    status_t res = doMount();
213    if (res == OK) {
214        setState(State::kMounted);
215    } else {
216        setState(State::kUnmountable);
217    }
218
219    return res;
220}
221
222status_t VolumeBase::unmount() {
223    if (mState != State::kMounted) {
224        LOG(WARNING) << getId() << " unmount requires state mounted";
225        return -EBUSY;
226    }
227
228    setState(State::kEjecting);
229    for (const auto& vol : mVolumes) {
230        if (vol->destroy()) {
231            LOG(WARNING) << getId() << " failed to destroy " << vol->getId()
232                    << " stacked above";
233        }
234    }
235    mVolumes.clear();
236
237    status_t res = doUnmount();
238    setState(State::kUnmounted);
239    return res;
240}
241
242status_t VolumeBase::format(const std::string& fsType) {
243    if (mState == State::kMounted) {
244        unmount();
245    }
246
247    if ((mState != State::kUnmounted) && (mState != State::kUnmountable)) {
248        LOG(WARNING) << getId() << " format requires state unmounted or unmountable";
249        return -EBUSY;
250    }
251
252    setState(State::kFormatting);
253    status_t res = doFormat(fsType);
254    setState(State::kUnmounted);
255    return res;
256}
257
258status_t VolumeBase::doFormat(const std::string& fsType) {
259    return -ENOTSUP;
260}
261
262}  // namespace vold
263}  // namespace android
264