1 /* 2 * Copyright (C) 2007 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 <stdio.h> 18 #include <stdlib.h> 19 #include <string.h> 20 #include <fcntl.h> 21 #include <errno.h> 22 #include <sys/mount.h> 23 24 #include "mounts.h" 25 26 struct MountedVolume { 27 const char *device; 28 const char *mount_point; 29 const char *filesystem; 30 const char *flags; 31 }; 32 33 typedef struct { 34 MountedVolume *volumes; 35 int volumes_allocd; 36 int volume_count; 37 } MountsState; 38 39 static MountsState g_mounts_state = { 40 NULL, // volumes 41 0, // volumes_allocd 42 0 // volume_count 43 }; 44 45 static inline void 46 free_volume_internals(const MountedVolume *volume, int zero) 47 { 48 free((char *)volume->device); 49 free((char *)volume->mount_point); 50 free((char *)volume->filesystem); 51 free((char *)volume->flags); 52 if (zero) { 53 memset((void *)volume, 0, sizeof(*volume)); 54 } 55 } 56 57 #define PROC_MOUNTS_FILENAME "/proc/mounts" 58 59 int 60 scan_mounted_volumes() 61 { 62 char buf[2048]; 63 const char *bufp; 64 int fd; 65 ssize_t nbytes; 66 67 if (g_mounts_state.volumes == NULL) { 68 const int numv = 32; 69 MountedVolume *volumes = malloc(numv * sizeof(*volumes)); 70 if (volumes == NULL) { 71 errno = ENOMEM; 72 return -1; 73 } 74 g_mounts_state.volumes = volumes; 75 g_mounts_state.volumes_allocd = numv; 76 memset(volumes, 0, numv * sizeof(*volumes)); 77 } else { 78 /* Free the old volume strings. 79 */ 80 int i; 81 for (i = 0; i < g_mounts_state.volume_count; i++) { 82 free_volume_internals(&g_mounts_state.volumes[i], 1); 83 } 84 } 85 g_mounts_state.volume_count = 0; 86 87 /* Open and read the file contents. 88 */ 89 fd = open(PROC_MOUNTS_FILENAME, O_RDONLY); 90 if (fd < 0) { 91 goto bail; 92 } 93 nbytes = read(fd, buf, sizeof(buf) - 1); 94 close(fd); 95 if (nbytes < 0) { 96 goto bail; 97 } 98 buf[nbytes] = '\0'; 99 100 /* Parse the contents of the file, which looks like: 101 * 102 * # cat /proc/mounts 103 * rootfs / rootfs rw 0 0 104 * /dev/pts /dev/pts devpts rw 0 0 105 * /proc /proc proc rw 0 0 106 * /sys /sys sysfs rw 0 0 107 * /dev/block/mtdblock4 /system yaffs2 rw,nodev,noatime,nodiratime 0 0 108 * /dev/block/mtdblock5 /data yaffs2 rw,nodev,noatime,nodiratime 0 0 109 * /dev/block/mmcblk0p1 /sdcard vfat rw,sync,dirsync,fmask=0000,dmask=0000,codepage=cp437,iocharset=iso8859-1,utf8 0 0 110 * 111 * The zeroes at the end are dummy placeholder fields to make the 112 * output match Linux's /etc/mtab, but don't represent anything here. 113 */ 114 bufp = buf; 115 while (nbytes > 0) { 116 char device[64]; 117 char mount_point[64]; 118 char filesystem[64]; 119 char flags[128]; 120 int matches; 121 122 /* %as is a gnu extension that malloc()s a string for each field. 123 */ 124 matches = sscanf(bufp, "%63s %63s %63s %127s", 125 device, mount_point, filesystem, flags); 126 127 if (matches == 4) { 128 device[sizeof(device)-1] = '\0'; 129 mount_point[sizeof(mount_point)-1] = '\0'; 130 filesystem[sizeof(filesystem)-1] = '\0'; 131 flags[sizeof(flags)-1] = '\0'; 132 133 MountedVolume *v = 134 &g_mounts_state.volumes[g_mounts_state.volume_count++]; 135 v->device = strdup(device); 136 v->mount_point = strdup(mount_point); 137 v->filesystem = strdup(filesystem); 138 v->flags = strdup(flags); 139 } else { 140 printf("matches was %d on <<%.40s>>\n", matches, bufp); 141 } 142 143 /* Eat the line. 144 */ 145 while (nbytes > 0 && *bufp != '\n') { 146 bufp++; 147 nbytes--; 148 } 149 if (nbytes > 0) { 150 bufp++; 151 nbytes--; 152 } 153 } 154 155 return 0; 156 157 bail: 158 //TODO: free the strings we've allocated. 159 g_mounts_state.volume_count = 0; 160 return -1; 161 } 162 163 const MountedVolume * 164 find_mounted_volume_by_device(const char *device) 165 { 166 if (g_mounts_state.volumes != NULL) { 167 int i; 168 for (i = 0; i < g_mounts_state.volume_count; i++) { 169 MountedVolume *v = &g_mounts_state.volumes[i]; 170 /* May be null if it was unmounted and we haven't rescanned. 171 */ 172 if (v->device != NULL) { 173 if (strcmp(v->device, device) == 0) { 174 return v; 175 } 176 } 177 } 178 } 179 return NULL; 180 } 181 182 const MountedVolume * 183 find_mounted_volume_by_mount_point(const char *mount_point) 184 { 185 if (g_mounts_state.volumes != NULL) { 186 int i; 187 for (i = 0; i < g_mounts_state.volume_count; i++) { 188 MountedVolume *v = &g_mounts_state.volumes[i]; 189 /* May be null if it was unmounted and we haven't rescanned. 190 */ 191 if (v->mount_point != NULL) { 192 if (strcmp(v->mount_point, mount_point) == 0) { 193 return v; 194 } 195 } 196 } 197 } 198 return NULL; 199 } 200 201 int 202 unmount_mounted_volume(const MountedVolume *volume) 203 { 204 /* Intentionally pass NULL to umount if the caller tries 205 * to unmount a volume they already unmounted using this 206 * function. 207 */ 208 int ret = umount(volume->mount_point); 209 if (ret == 0) { 210 free_volume_internals(volume, 1); 211 return 0; 212 } 213 return ret; 214 } 215 216 int 217 remount_read_only(const MountedVolume* volume) 218 { 219 return mount(volume->device, volume->mount_point, volume->filesystem, 220 MS_NOATIME | MS_NODEV | MS_NODIRATIME | 221 MS_RDONLY | MS_REMOUNT, 0); 222 } 223