summaryrefslogtreecommitdiff
path: root/VolumeManager.cpp (plain)
blob: 5b821806c9068dd245488879cc05eabb162eebc4
1/*
2 * Copyright (C) 2008 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 <dirent.h>
18#include <errno.h>
19#include <fcntl.h>
20#include <fts.h>
21#include <mntent.h>
22#include <stdio.h>
23#include <stdlib.h>
24#include <string.h>
25#include <sys/ioctl.h>
26#include <sys/mount.h>
27#include <sys/stat.h>
28#include <sys/types.h>
29#include <sys/wait.h>
30#include <unistd.h>
31#include <sys/sysmacros.h>
32#include <sys/sysmacros.h>
33
34#include <linux/kdev_t.h>
35#include <linux/loop.h>
36
37#define LOG_TAG "droidVold"
38
39#include <android-base/logging.h>
40#include <android-base/stringprintf.h>
41#include <cutils/fs.h>
42#include <cutils/log.h>
43
44#include <selinux/android.h>
45
46#include <sysutils/NetlinkEvent.h>
47#include <private/android_filesystem_config.h>
48
49#include "VolumeManager.h"
50#include "NetlinkManager.h"
51#include "DroidVold.h"
52
53#include "fs/Ext4.h"
54#include "fs/Vfat.h"
55#include "Utils.h"
56#include "Process.h"
57#include "fs/Iso9660.h"
58
59#ifdef HAS_VIRTUAL_CDROM
60#define LOOP_DEV "/dev/block/loop0"
61#define LOOP_MOUNTPOINT "/mnt/loop"
62#endif
63
64using android::base::StringPrintf;
65
66static const unsigned int kMajorBlockMmc = 179;
67static const unsigned int kMajorBlockExperimentalMin = 240;
68static const unsigned int kMajorBlockExperimentalMax = 254;
69
70VolumeManager *VolumeManager::sInstance = NULL;
71
72VolumeManager *VolumeManager::Instance() {
73 if (!sInstance)
74 sInstance = new VolumeManager();
75 return sInstance;
76}
77
78VolumeManager::VolumeManager() {
79 mDebug = false;
80 mBroadcaster = NULL;
81#ifdef HAS_VIRTUAL_CDROM
82 mLoopPath = NULL;
83#endif
84}
85
86VolumeManager::~VolumeManager() {
87}
88
89int VolumeManager::setDebug(bool enable) {
90 mDebug = enable;
91 return 0;
92}
93
94int VolumeManager::start() {
95 // Always start from a clean slate by unmounting everything in
96 // directories that we own, in case we crashed.
97 unmountAll();
98
99 return 0;
100}
101
102int VolumeManager::stop() {
103 return 0;
104}
105
106void VolumeManager::handleBlockEvent(NetlinkEvent *evt) {
107 std::lock_guard<std::mutex> lock(mLock);
108
109 if (mDebug) {
110 LOG(VERBOSE) << "----------------";
111 LOG(VERBOSE) << "handleBlockEvent with action " << (int) evt->getAction();
112 evt->dump();
113 }
114
115 std::string devType(evt->findParam("DEVTYPE")?evt->findParam("DEVTYPE"):"");
116
117
118 if (devType == "disk") {
119 std::string eventPath(evt->findParam("DEVPATH")?evt->findParam("DEVPATH"):"");
120 std::string devName(evt->findParam("DEVNAME")?evt->findParam("DEVNAME"):"");
121
122 int major = atoi(evt->findParam("MAJOR"));
123 int minor = atoi(evt->findParam("MINOR"));
124 dev_t device = makedev(major, minor);
125
126 switch (evt->getAction()) {
127 case NetlinkEvent::Action::kAdd: {
128 for (auto source : mDiskSources) {
129 if (source->matches(eventPath)) {
130 // For now, assume that MMC and virtio-blk (the latter is
131 // emulator-specific; see Disk.cpp for details) devices are SD,
132 // and that everything else is USB
133 int flags = source->getFlags();
134 if (major == kMajorBlockMmc
135 || (android::droidvold::IsRunningInEmulator()
136 && major >= (int) kMajorBlockExperimentalMin
137 && major <= (int) kMajorBlockExperimentalMax)) {
138 flags |= android::droidvold::Disk::Flags::kSd;
139 } else {
140 flags |= android::droidvold::Disk::Flags::kUsb;
141 }
142
143 auto disk = new android::droidvold::Disk(eventPath, device,
144 source->getNickname(), devName, flags);
145 disk->create();
146 mDisks.push_back(std::shared_ptr<android::droidvold::Disk>(disk));
147 break;
148 }
149 }
150 break;
151 }
152 case NetlinkEvent::Action::kChange: {
153 LOG(DEBUG) << "Disk at " << major << ":" << minor << " changed";
154 for (auto disk : mDisks) {
155 if (disk->getDevice() == device) {
156 if (disk->isSrdiskMounted()) {
157 LOG(DEBUG) << "srdisk ejected";
158 disk->destroyAllVolumes();
159 break;
160 }
161
162 //disk->readMetadata();
163 //disk->readPartitions();
164 }
165 }
166 break;
167 }
168 case NetlinkEvent::Action::kRemove: {
169 auto i = mDisks.begin();
170 while (i != mDisks.end()) {
171 if ((*i)->getDevice() == device) {
172 (*i)->destroy();
173 i = mDisks.erase(i);
174 } else {
175 ++i;
176 }
177 }
178 break;
179 }
180 default: {
181 LOG(WARNING) << "Unexpected block event action " << (int) evt->getAction();
182 break;
183 }
184 }
185
186 } else {
187 for (auto disk : mDisks) {
188 disk->handleBlockEvent(evt);
189 }
190 }
191
192}
193
194void VolumeManager::addDiskSource(const std::shared_ptr<DiskSource>& diskSource) {
195 mDiskSources.push_back(diskSource);
196}
197
198std::shared_ptr<android::droidvold::VolumeBase> VolumeManager::findVolume(const std::string& id) {
199
200 for (auto disk : mDisks) {
201 auto vol = disk->findVolume(id);
202 if (vol != nullptr) {
203 return vol;
204 }
205 }
206 return nullptr;
207}
208
209void VolumeManager::listVolumes(android::droidvold::VolumeBase::Type type,
210 std::list<std::string>& list) {
211 list.clear();
212 for (auto disk : mDisks) {
213 disk->listVolumes(type, list);
214 }
215}
216
217int VolumeManager::unmountAll() {
218 std::lock_guard<std::mutex> lock(mLock);
219
220 for (auto disk : mDisks) {
221 disk->unmountAll();
222 }
223
224 return 0;
225}
226
227#ifdef HAS_VIRTUAL_CDROM
228int VolumeManager::loopsetfd(const char * path)
229{
230 int fd,file_fd;
231
232 if ((fd = open(LOOP_DEV, O_RDWR)) < 0) {
233 SLOGE("Unable to open loop0 device (%s)",strerror(errno));
234 return -1;
235 }
236
237 if ((file_fd = open(path, O_RDWR)) < 0) {
238 SLOGE("Unable to open %s (%s)", path, strerror(errno));
239 close(fd);
240 return -1;
241 }
242
243 if (ioctl(fd, LOOP_SET_FD, file_fd) < 0) {
244 SLOGE("Error setting up loopback interface (%s)", strerror(errno));
245 close(file_fd);
246 close(fd);
247 return -1;
248 }
249
250 close(fd);
251 close(file_fd);
252
253 SLOGD("loopsetfd (%s) ok\n", path);
254 return 0;
255}
256
257int VolumeManager::loopclrfd()
258{
259 int fd;
260 int rc=0;
261
262 if ((fd = open(LOOP_DEV, O_RDWR)) < 0) {
263 SLOGE("Unable to open loop0 device (%s)",strerror(errno));
264 return -1;
265 }
266
267 if (ioctl(fd, LOOP_CLR_FD, 0) < 0) {
268 SLOGE("Error setting up loopback interface (%s)", strerror(errno));
269 rc = -1;
270 }
271 close(fd);
272
273 SLOGD("loopclrfd ok\n");
274 return rc;
275}
276
277int VolumeManager::mountloop(const char * path) {
278 if (isMountpointMounted(LOOP_MOUNTPOINT)) {
279 SLOGW("loop file already mounted,please umount fist,then mount this file!");
280 errno = EBUSY;
281 return -1;
282 }
283
284 if (loopsetfd(path) < 0) {
285 return -1;
286 }
287
288 if (fs_prepare_dir(LOOP_MOUNTPOINT, 0700, AID_ROOT, AID_SDCARD_R)) {
289 SLOGE("failed to create loop mount points");
290 return -errno;
291 }
292
293 if (android::droidvold::iso9660::Mount(LOOP_DEV, LOOP_MOUNTPOINT, false, false,
294 AID_SDCARD_R, AID_SDCARD_R, 0007, true)) {
295 loopclrfd();
296 SLOGW("%s failed to mount via ISO9660(%s)", LOOP_DEV, strerror(errno));
297 return -1;
298 } else {
299 mLoopPath = strdup(path);
300 SLOGI("Successfully mount %s as ISO9660", LOOP_DEV);
301 }
302
303 return 0;
304}
305
306int VolumeManager::unmountloop(bool unused) {
307 if (!isMountpointMounted(LOOP_MOUNTPOINT)) {
308 SLOGW("no loop file mounted");
309 errno = ENOENT;
310 return -1;
311 }
312
313 android::droidvold::ForceUnmount(LOOP_MOUNTPOINT);
314 loopclrfd();
315 rmdir(LOOP_MOUNTPOINT);
316
317 if (mLoopPath != NULL) {
318 free(mLoopPath);
319 mLoopPath = NULL;
320 }
321
322 return 0;
323}
324
325void VolumeManager::unmountLoopIfNeed(const char *label) {
326 if (mLoopPath != NULL && strstr(mLoopPath, label)) {
327 SLOGD("umount loop");
328 unmountloop(true);
329 }
330}
331
332#endif
333
334bool VolumeManager::isMountpointMounted(const char *mp)
335{
336 FILE *fp = setmntent("/proc/mounts", "r");
337 if (fp == NULL) {
338 SLOGE("Error opening /proc/mounts (%s)", strerror(errno));
339 return false;
340 }
341
342 bool found_mp = false;
343 mntent* mentry;
344 while ((mentry = getmntent(fp)) != NULL) {
345 if (strcmp(mentry->mnt_dir, mp) == 0) {
346 found_mp = true;
347 break;
348 }
349 }
350 endmntent(fp);
351 return found_mp;
352}
353
354int VolumeManager::reset() {
355 // Tear down all existing disks/volumes and start from a blank slate so
356 // newly connected framework hears all events.
357 for (auto disk : mDisks) {
358 disk->destroy();
359 disk->reset();
360 }
361
362 return 0;
363}
364
365int VolumeManager::shutdown() {
366 for (auto disk : mDisks) {
367 disk->destroy();
368 }
369 mDisks.clear();
370 return 0;
371}
372
373int VolumeManager::mkdirs(char* path) {
374 // Only offer to create directories for paths managed by vold
375 if (strncmp(path, "/mnt/media_rw/", 13) == 0) {
376 // fs_mkdirs() does symlink checking and relative path enforcement
377 return fs_mkdirs(path, 0700);
378 } else {
379 SLOGE("Failed to find mounted volume for %s", path);
380 return -EINVAL;
381 }
382}
383
384static void do_coldboot(DIR *d, int lvl) {
385 struct dirent *de;
386 int dfd, fd;
387
388 dfd = dirfd(d);
389
390 fd = openat(dfd, "uevent", O_WRONLY | O_CLOEXEC);
391 if (fd >= 0) {
392 write(fd, "add\n", 4);
393 close(fd);
394 }
395
396 while ((de = readdir(d))) {
397 DIR *d2;
398
399 if (de->d_name[0] == '.')
400 continue;
401
402 if (de->d_type != DT_DIR && lvl > 0)
403 continue;
404
405 fd = openat(dfd, de->d_name, O_RDONLY | O_DIRECTORY);
406 if (fd < 0)
407 continue;
408
409 d2 = fdopendir(fd);
410 if (d2 == 0)
411 close(fd);
412 else {
413 do_coldboot(d2, lvl + 1);
414 closedir(d2);
415 }
416 }
417}
418
419void VolumeManager::coldboot(const char *path) {
420 DIR *d = opendir(path);
421
422 if (d) {
423 do_coldboot(d, 0);
424 closedir(d);
425 }
426}
427int VolumeManager::getDiskFlag(const std::string &path) {
428 if (mDebug)
429 LOG(DEBUG) << "getdisk flag path=" << path;
430 for (auto disk : mDisks) {
431 auto vol = disk->findVolumeByPath(path);
432 if (vol != nullptr) {
433 return disk->getFlags();
434 }
435 }
436 return 0;
437}
438