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 | |
64 | using android::base::StringPrintf; |
65 | |
66 | static const unsigned int kMajorBlockMmc = 179; |
67 | static const unsigned int kMajorBlockExperimentalMin = 240; |
68 | static const unsigned int kMajorBlockExperimentalMax = 254; |
69 | |
70 | VolumeManager *VolumeManager::sInstance = NULL; |
71 | |
72 | VolumeManager *VolumeManager::Instance() { |
73 | if (!sInstance) |
74 | sInstance = new VolumeManager(); |
75 | return sInstance; |
76 | } |
77 | |
78 | VolumeManager::VolumeManager() { |
79 | mDebug = false; |
80 | mBroadcaster = NULL; |
81 | #ifdef HAS_VIRTUAL_CDROM |
82 | mLoopPath = NULL; |
83 | #endif |
84 | } |
85 | |
86 | VolumeManager::~VolumeManager() { |
87 | } |
88 | |
89 | int VolumeManager::setDebug(bool enable) { |
90 | mDebug = enable; |
91 | return 0; |
92 | } |
93 | |
94 | int 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 | |
102 | int VolumeManager::stop() { |
103 | return 0; |
104 | } |
105 | |
106 | void 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 | |
194 | void VolumeManager::addDiskSource(const std::shared_ptr<DiskSource>& diskSource) { |
195 | mDiskSources.push_back(diskSource); |
196 | } |
197 | |
198 | std::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 | |
209 | void 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 | |
217 | int 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 |
228 | int 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 | |
257 | int 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 | |
277 | int 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 | |
306 | int 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 | |
325 | void 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 | |
334 | bool 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 | |
354 | int 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 | |
365 | int VolumeManager::shutdown() { |
366 | for (auto disk : mDisks) { |
367 | disk->destroy(); |
368 | } |
369 | mDisks.clear(); |
370 | return 0; |
371 | } |
372 | |
373 | int 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 | |
384 | static 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 | |
419 | void VolumeManager::coldboot(const char *path) { |
420 | DIR *d = opendir(path); |
421 | |
422 | if (d) { |
423 | do_coldboot(d, 0); |
424 | closedir(d); |
425 | } |
426 | } |
427 | int 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 |