Volume.cpp revision eba65e9d438a05f1c5dfd0f8d31bc463a5d08eee
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 <stdlib.h> 18#include <string.h> 19#include <dirent.h> 20#include <errno.h> 21#include <fcntl.h> 22 23#include <sys/types.h> 24#include <sys/stat.h> 25#include <sys/types.h> 26#include <sys/mman.h> 27#include <sys/mount.h> 28 29#include <linux/kdev_t.h> 30 31#include <cutils/properties.h> 32 33#include "diskmbr.h" 34 35#define LOG_TAG "Vold" 36 37#include <cutils/log.h> 38 39#include "Volume.h" 40#include "VolumeManager.h" 41#include "ResponseCode.h" 42#include "Fat.h" 43 44extern "C" void KillProcessesWithOpenFiles(const char *, int, int, int); 45extern "C" void dos_partition_dec(void const *pp, struct dos_partition *d); 46extern "C" void dos_partition_enc(void *pp, struct dos_partition *d); 47 48static const char *stateToStr(int state) { 49 if (state == Volume::State_Init) 50 return "Initializing"; 51 else if (state == Volume::State_NoMedia) 52 return "No-Media"; 53 else if (state == Volume::State_Idle) 54 return "Idle-Unmounted"; 55 else if (state == Volume::State_Pending) 56 return "Pending"; 57 else if (state == Volume::State_Mounted) 58 return "Mounted"; 59 else if (state == Volume::State_Unmounting) 60 return "Unmounting"; 61 else if (state == Volume::State_Checking) 62 return "Checking"; 63 else if (state == Volume::State_Formatting) 64 return "Formatting"; 65 else if (state == Volume::State_Shared) 66 return "Shared-Unmounted"; 67 else if (state == Volume::State_SharedMnt) 68 return "Shared-Mounted"; 69 else 70 return "Unknown-Error"; 71} 72 73Volume::Volume(VolumeManager *vm, const char *label, const char *mount_point) { 74 mVm = vm; 75 mLabel = strdup(label); 76 mMountpoint = strdup(mount_point); 77 mState = Volume::State_Init; 78 mCurrentlyMountedKdev = -1; 79} 80 81Volume::~Volume() { 82 free(mLabel); 83 free(mMountpoint); 84} 85 86dev_t Volume::getDiskDevice() { 87 return MKDEV(0, 0); 88}; 89 90void Volume::handleVolumeShared() { 91} 92 93void Volume::handleVolumeUnshared() { 94} 95 96int Volume::handleBlockEvent(NetlinkEvent *evt) { 97 errno = ENOSYS; 98 return -1; 99} 100 101void Volume::setState(int state) { 102 char msg[255]; 103 int oldState = mState; 104 105 if (oldState == state) { 106 LOGW("Duplicate state (%d)\n", state); 107 return; 108 } 109 110 mState = state; 111 112 LOGD("Volume %s state changing %d (%s) -> %d (%s)", mLabel, 113 oldState, stateToStr(oldState), mState, stateToStr(mState)); 114 snprintf(msg, sizeof(msg), 115 "Volume %s %s state changed from %d (%s) to %d (%s)", getLabel(), 116 getMountpoint(), oldState, stateToStr(oldState), mState, 117 stateToStr(mState)); 118 119 mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeStateChange, 120 msg, false); 121} 122 123int Volume::createDeviceNode(const char *path, int major, int minor) { 124 mode_t mode = 0660 | S_IFBLK; 125 dev_t dev = (major << 8) | minor; 126 if (mknod(path, mode, dev) < 0) { 127 if (errno != EEXIST) { 128 return -1; 129 } 130 } 131 return 0; 132} 133 134int Volume::formatVol() { 135 136 if (getState() == Volume::State_NoMedia) { 137 errno = ENODEV; 138 return -1; 139 } else if (getState() != Volume::State_Idle) { 140 errno = EBUSY; 141 return -1; 142 } 143 144 if (isMountpointMounted(getMountpoint())) { 145 LOGW("Volume is idle but appears to be mounted - fixing"); 146 setState(Volume::State_Mounted); 147 // mCurrentlyMountedKdev = XXX 148 errno = EBUSY; 149 return -1; 150 } 151 152 char devicePath[255]; 153 dev_t diskNode = getDiskDevice(); 154 dev_t partNode = MKDEV(MAJOR(diskNode), 1); // XXX: Hmmm 155 156 sprintf(devicePath, "/dev/block/vold/%d:%d", 157 MAJOR(diskNode), MINOR(diskNode)); 158 159 LOGI("Volume %s (%s) MBR being initialized", getLabel(), devicePath); 160 161 if (initializeMbr(devicePath)) { 162 LOGE("Failed to initialize MBR (%s)", strerror(errno)); 163 goto err; 164 } 165 166 sprintf(devicePath, "/dev/block/vold/%d:%d", 167 MAJOR(partNode), MINOR(partNode)); 168 169 LOGI("Volume %s (%s) being formatted", getLabel(), devicePath); 170 171 if (Fat::format(devicePath)) { 172 LOGE("Failed to format (%s)", strerror(errno)); 173 goto err; 174 } 175 176 LOGI("Volume %s (%s) formatted sucessfully", getLabel(), devicePath); 177 return 0; 178err: 179 return -1; 180} 181 182bool Volume::isMountpointMounted(const char *path) { 183 char device[256]; 184 char mount_path[256]; 185 char rest[256]; 186 FILE *fp; 187 char line[1024]; 188 189 if (!(fp = fopen("/proc/mounts", "r"))) { 190 LOGE("Error opening /proc/mounts (%s)", strerror(errno)); 191 return false; 192 } 193 194 while(fgets(line, sizeof(line), fp)) { 195 line[strlen(line)-1] = '\0'; 196 sscanf(line, "%255s %255s %255s\n", device, mount_path, rest); 197 if (!strcmp(mount_path, path)) { 198 fclose(fp); 199 return true; 200 } 201 202 } 203 204 fclose(fp); 205 return false; 206} 207 208int Volume::mountVol() { 209 dev_t deviceNodes[4]; 210 int n, i, rc = 0; 211 char errmsg[255]; 212 213 if (getState() == Volume::State_NoMedia) { 214 snprintf(errmsg, sizeof(errmsg), 215 "Volume %s %s mount failed - no media", 216 getLabel(), getMountpoint()); 217 mVm->getBroadcaster()->sendBroadcast( 218 ResponseCode::VolumeMountFailedNoMedia, 219 errmsg, false); 220 errno = ENODEV; 221 return -1; 222 } else if (getState() != Volume::State_Idle) { 223 errno = EBUSY; 224 return -1; 225 } 226 227 if (isMountpointMounted(getMountpoint())) { 228 LOGW("Volume is idle but appears to be mounted - fixing"); 229 setState(Volume::State_Mounted); 230 // mCurrentlyMountedKdev = XXX 231 return 0; 232 } 233 234 n = getDeviceNodes((dev_t *) &deviceNodes, 4); 235 if (!n) { 236 LOGE("Failed to get device nodes (%s)\n", strerror(errno)); 237 return -1; 238 } 239 240 for (i = 0; i < n; i++) { 241 char devicePath[255]; 242 243 sprintf(devicePath, "/dev/block/vold/%d:%d", MAJOR(deviceNodes[i]), 244 MINOR(deviceNodes[i])); 245 246 LOGI("%s being considered for volume %s\n", devicePath, getLabel()); 247 248 errno = 0; 249 setState(Volume::State_Checking); 250 251 if ((rc = Fat::check(devicePath))) { 252 if (errno == ENODATA) { 253 LOGW("%s does not contain a FAT filesystem\n", devicePath); 254 continue; 255 } 256 errno = EIO; 257 /* Badness - abort the mount */ 258 LOGE("%s failed FS checks (%s)", devicePath, strerror(errno)); 259 setState(Volume::State_Idle); 260 return -1; 261 } 262 263 LOGI("%s checks out - attempting to mount\n", devicePath); 264 errno = 0; 265 if (!(rc = Fat::doMount(devicePath, getMountpoint(), false, false, 266 1000, 1015, 0702, true))) { 267 LOGI("%s sucessfully mounted for volume %s\n", devicePath, getLabel()); 268 setState(Volume::State_Mounted); 269 mCurrentlyMountedKdev = deviceNodes[i]; 270 return 0; 271 } 272 273 LOGW("%s failed to mount via VFAT (%s)\n", devicePath, strerror(errno)); 274 } 275 276 LOGE("Volume %s found no suitable devices for mounting :(\n", getLabel()); 277 setState(Volume::State_Idle); 278 279 return -1; 280} 281 282int Volume::unmountVol() { 283 int i, rc; 284 285 if (getState() != Volume::State_Mounted) { 286 LOGE("Volume %s unmount request when not mounted", getLabel()); 287 errno = EINVAL; 288 return -1; 289 } 290 291 setState(Volume::State_Unmounting); 292 for (i = 0; i < 10; i++) { 293 rc = umount(getMountpoint()); 294 if (!rc) 295 break; 296 297 if (rc && (errno == EINVAL || errno == ENOENT)) { 298 rc = 0; 299 break; 300 } 301 302 LOGW("Volume %s unmount attempt %d failed (%s)", 303 getLabel(), i + 1, strerror(errno)); 304 305 if (i < 5) { 306 usleep(1000 * 250); 307 } else { 308 KillProcessesWithOpenFiles(getMountpoint(), 309 (i < 7 ? 0 : 1), 310 NULL, 0); 311 usleep(1000 * 250); 312 } 313 } 314 315 if (!rc) { 316 LOGI("Volume %s unmounted sucessfully", getLabel()); 317 setState(Volume::State_Idle); 318 mCurrentlyMountedKdev = -1; 319 return 0; 320 } 321 322 LOGE("Volume %s failed to unmount (%s)\n", getLabel(), strerror(errno)); 323 setState(Volume::State_Mounted); 324 return -1; 325} 326 327int Volume::initializeMbr(const char *deviceNode) { 328 int fd, rc; 329 unsigned char block[512]; 330 struct dos_partition part; 331 unsigned int nr_sec; 332 333 if ((fd = open(deviceNode, O_RDWR)) < 0) { 334 LOGE("Error opening disk file (%s)", strerror(errno)); 335 return -1; 336 } 337 338 if (ioctl(fd, BLKGETSIZE, &nr_sec)) { 339 LOGE("Unable to get device size (%s)", strerror(errno)); 340 close(fd); 341 return -1; 342 } 343 344 memset(&part, 0, sizeof(part)); 345 part.dp_flag = 0x80; 346 part.dp_typ = 0xc; 347 part.dp_start = ((1024 * 64) / 512) + 1; 348 part.dp_size = nr_sec - part.dp_start; 349 350 memset(block, 0, sizeof(block)); 351 block[0x1fe] = 0x55; 352 block[0x1ff] = 0xaa; 353 354 dos_partition_enc(block + DOSPARTOFF, &part); 355 356 if (write(fd, block, sizeof(block)) < 0) { 357 LOGE("Error writing MBR (%s)", strerror(errno)); 358 close(fd); 359 return -1; 360 } 361 362 if (ioctl(fd, BLKRRPART, NULL) < 0) { 363 LOGE("Error re-reading partition table (%s)", strerror(errno)); 364 close(fd); 365 return -1; 366 } 367 close(fd); 368 return 0; 369} 370