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