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 "fs/Vfat.h"
18#include "PublicVolume.h"
19#include "Utils.h"
20#include "VolumeManager.h"
21#include "ResponseCode.h"
22
23#include <android-base/stringprintf.h>
24#include <android-base/logging.h>
25#include <cutils/fs.h>
26#include <private/android_filesystem_config.h>
27
28#include <fcntl.h>
29#include <stdlib.h>
30#include <sys/mount.h>
31#include <sys/stat.h>
32#include <sys/types.h>
33#include <sys/wait.h>
34
35using android::base::StringPrintf;
36
37namespace android {
38namespace vold {
39
40static const char* kFusePath = "/system/bin/sdcard";
41
42static const char* kAsecPath = "/mnt/secure/asec";
43
44PublicVolume::PublicVolume(dev_t device) :
45        VolumeBase(Type::kPublic), mDevice(device), mFusePid(0) {
46    setId(StringPrintf("public:%u,%u", major(device), minor(device)));
47    mDevPath = StringPrintf("/dev/block/vold/%s", getId().c_str());
48}
49
50PublicVolume::~PublicVolume() {
51}
52
53status_t PublicVolume::readMetadata() {
54    status_t res = ReadMetadataUntrusted(mDevPath, mFsType, mFsUuid, mFsLabel);
55    notifyEvent(ResponseCode::VolumeFsTypeChanged, mFsType);
56    notifyEvent(ResponseCode::VolumeFsUuidChanged, mFsUuid);
57    notifyEvent(ResponseCode::VolumeFsLabelChanged, mFsLabel);
58    return res;
59}
60
61status_t PublicVolume::initAsecStage() {
62    std::string legacyPath(mRawPath + "/android_secure");
63    std::string securePath(mRawPath + "/.android_secure");
64
65    // Recover legacy secure path
66    if (!access(legacyPath.c_str(), R_OK | X_OK)
67            && access(securePath.c_str(), R_OK | X_OK)) {
68        if (rename(legacyPath.c_str(), securePath.c_str())) {
69            PLOG(WARNING) << getId() << " failed to rename legacy ASEC dir";
70        }
71    }
72
73    if (TEMP_FAILURE_RETRY(mkdir(securePath.c_str(), 0700))) {
74        if (errno != EEXIST) {
75            PLOG(WARNING) << getId() << " creating ASEC stage failed";
76            return -errno;
77        }
78    }
79
80    BindMount(securePath, kAsecPath);
81
82    return OK;
83}
84
85status_t PublicVolume::doCreate() {
86    return CreateDeviceNode(mDevPath, mDevice);
87}
88
89status_t PublicVolume::doDestroy() {
90    return DestroyDeviceNode(mDevPath);
91}
92
93status_t PublicVolume::doMount() {
94    // TODO: expand to support mounting other filesystems
95    readMetadata();
96
97    if (mFsType != "vfat") {
98        LOG(ERROR) << getId() << " unsupported filesystem " << mFsType;
99        return -EIO;
100    }
101
102    if (vfat::Check(mDevPath)) {
103        LOG(ERROR) << getId() << " failed filesystem check";
104        return -EIO;
105    }
106
107    // Use UUID as stable name, if available
108    std::string stableName = getId();
109    if (!mFsUuid.empty()) {
110        stableName = mFsUuid;
111    }
112
113    mRawPath = StringPrintf("/mnt/media_rw/%s", stableName.c_str());
114
115    mFuseDefault = StringPrintf("/mnt/runtime/default/%s", stableName.c_str());
116    mFuseRead = StringPrintf("/mnt/runtime/read/%s", stableName.c_str());
117    mFuseWrite = StringPrintf("/mnt/runtime/write/%s", stableName.c_str());
118
119    setInternalPath(mRawPath);
120    if (getMountFlags() & MountFlags::kVisible) {
121        setPath(StringPrintf("/storage/%s", stableName.c_str()));
122    } else {
123        setPath(mRawPath);
124    }
125
126    if (fs_prepare_dir(mRawPath.c_str(), 0700, AID_ROOT, AID_ROOT)) {
127        PLOG(ERROR) << getId() << " failed to create mount points";
128        return -errno;
129    }
130
131    if (vfat::Mount(mDevPath, mRawPath, false, false, false,
132            AID_MEDIA_RW, AID_MEDIA_RW, 0007, true)) {
133        PLOG(ERROR) << getId() << " failed to mount " << mDevPath;
134        return -EIO;
135    }
136
137    if (getMountFlags() & MountFlags::kPrimary) {
138        initAsecStage();
139    }
140
141    if (!(getMountFlags() & MountFlags::kVisible)) {
142        // Not visible to apps, so no need to spin up FUSE
143        return OK;
144    }
145
146    if (fs_prepare_dir(mFuseDefault.c_str(), 0700, AID_ROOT, AID_ROOT) ||
147            fs_prepare_dir(mFuseRead.c_str(), 0700, AID_ROOT, AID_ROOT) ||
148            fs_prepare_dir(mFuseWrite.c_str(), 0700, AID_ROOT, AID_ROOT)) {
149        PLOG(ERROR) << getId() << " failed to create FUSE mount points";
150        return -errno;
151    }
152
153    dev_t before = GetDevice(mFuseWrite);
154
155    if (!(mFusePid = fork())) {
156        if (getMountFlags() & MountFlags::kPrimary) {
157            if (execl(kFusePath, kFusePath,
158                    "-u", "1023", // AID_MEDIA_RW
159                    "-g", "1023", // AID_MEDIA_RW
160                    "-U", std::to_string(getMountUserId()).c_str(),
161                    "-w",
162                    mRawPath.c_str(),
163                    stableName.c_str(),
164                    NULL)) {
165                PLOG(ERROR) << "Failed to exec";
166            }
167        } else {
168            if (execl(kFusePath, kFusePath,
169                    "-u", "1023", // AID_MEDIA_RW
170                    "-g", "1023", // AID_MEDIA_RW
171                    "-U", std::to_string(getMountUserId()).c_str(),
172                    mRawPath.c_str(),
173                    stableName.c_str(),
174                    NULL)) {
175                PLOG(ERROR) << "Failed to exec";
176            }
177        }
178
179        LOG(ERROR) << "FUSE exiting";
180        _exit(1);
181    }
182
183    if (mFusePid == -1) {
184        PLOG(ERROR) << getId() << " failed to fork";
185        return -errno;
186    }
187
188    while (before == GetDevice(mFuseWrite)) {
189        LOG(VERBOSE) << "Waiting for FUSE to spin up...";
190        usleep(50000); // 50ms
191    }
192
193    return OK;
194}
195
196status_t PublicVolume::doUnmount() {
197    // Unmount the storage before we kill the FUSE process. If we kill
198    // the FUSE process first, most file system operations will return
199    // ENOTCONN until the unmount completes. This is an exotic and unusual
200    // error code and might cause broken behaviour in applications.
201    KillProcessesUsingPath(getPath());
202
203    ForceUnmount(kAsecPath);
204
205    ForceUnmount(mFuseDefault);
206    ForceUnmount(mFuseRead);
207    ForceUnmount(mFuseWrite);
208    ForceUnmount(mRawPath);
209
210    if (mFusePid > 0) {
211        kill(mFusePid, SIGTERM);
212        TEMP_FAILURE_RETRY(waitpid(mFusePid, nullptr, 0));
213        mFusePid = 0;
214    }
215
216    rmdir(mFuseDefault.c_str());
217    rmdir(mFuseRead.c_str());
218    rmdir(mFuseWrite.c_str());
219    rmdir(mRawPath.c_str());
220
221    mFuseDefault.clear();
222    mFuseRead.clear();
223    mFuseWrite.clear();
224    mRawPath.clear();
225
226    return OK;
227}
228
229status_t PublicVolume::doFormat(const std::string& fsType) {
230    if (fsType == "vfat" || fsType == "auto") {
231        if (WipeBlockDevice(mDevPath) != OK) {
232            LOG(WARNING) << getId() << " failed to wipe";
233        }
234        if (vfat::Format(mDevPath, 0)) {
235            LOG(ERROR) << getId() << " failed to format";
236            return -errno;
237        }
238    } else {
239        LOG(ERROR) << "Unsupported filesystem " << fsType;
240        return -EINVAL;
241    }
242
243    return OK;
244}
245
246}  // namespace vold
247}  // namespace android
248