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 <mntent.h>
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <fcntl.h>
22 #include <errno.h>
23 #include <sys/mount.h>
24 
25 #include "mounts.h"
26 
27 struct MountedVolume {
28     const char *device;
29     const char *mount_point;
30     const char *filesystem;
31     const char *flags;
32 };
33 
34 typedef struct {
35     MountedVolume *volumes;
36     int volumes_allocd;
37     int volume_count;
38 } MountsState;
39 
40 static MountsState g_mounts_state = {
41     NULL,   // volumes
42     0,      // volumes_allocd
43     0       // volume_count
44 };
45 
46 static inline void
free_volume_internals(const MountedVolume * volume,int zero)47 free_volume_internals(const MountedVolume *volume, int zero)
48 {
49     free((char *)volume->device);
50     free((char *)volume->mount_point);
51     free((char *)volume->filesystem);
52     free((char *)volume->flags);
53     if (zero) {
54         memset((void *)volume, 0, sizeof(*volume));
55     }
56 }
57 
58 #define PROC_MOUNTS_FILENAME   "/proc/mounts"
59 
60 int
scan_mounted_volumes()61 scan_mounted_volumes()
62 {
63     FILE* fp;
64     struct mntent* mentry;
65 
66     if (g_mounts_state.volumes == NULL) {
67         const int numv = 32;
68         MountedVolume *volumes = malloc(numv * sizeof(*volumes));
69         if (volumes == NULL) {
70             errno = ENOMEM;
71             return -1;
72         }
73         g_mounts_state.volumes = volumes;
74         g_mounts_state.volumes_allocd = numv;
75         memset(volumes, 0, numv * sizeof(*volumes));
76     } else {
77         /* Free the old volume strings.
78          */
79         int i;
80         for (i = 0; i < g_mounts_state.volume_count; i++) {
81             free_volume_internals(&g_mounts_state.volumes[i], 1);
82         }
83     }
84     g_mounts_state.volume_count = 0;
85 
86     /* Open and read mount table entries. */
87     fp = setmntent(PROC_MOUNTS_FILENAME, "r");
88     if (fp == NULL) {
89         return -1;
90     }
91     while ((mentry = getmntent(fp)) != NULL) {
92         MountedVolume* v = &g_mounts_state.volumes[g_mounts_state.volume_count++];
93         v->device = strdup(mentry->mnt_fsname);
94         v->mount_point = strdup(mentry->mnt_dir);
95         v->filesystem = strdup(mentry->mnt_type);
96         v->flags = strdup(mentry->mnt_opts);
97     }
98     endmntent(fp);
99     return 0;
100 }
101 
102 const MountedVolume *
find_mounted_volume_by_device(const char * device)103 find_mounted_volume_by_device(const char *device)
104 {
105     if (g_mounts_state.volumes != NULL) {
106         int i;
107         for (i = 0; i < g_mounts_state.volume_count; i++) {
108             MountedVolume *v = &g_mounts_state.volumes[i];
109             /* May be null if it was unmounted and we haven't rescanned.
110              */
111             if (v->device != NULL) {
112                 if (strcmp(v->device, device) == 0) {
113                     return v;
114                 }
115             }
116         }
117     }
118     return NULL;
119 }
120 
121 const MountedVolume *
find_mounted_volume_by_mount_point(const char * mount_point)122 find_mounted_volume_by_mount_point(const char *mount_point)
123 {
124     if (g_mounts_state.volumes != NULL) {
125         int i;
126         for (i = 0; i < g_mounts_state.volume_count; i++) {
127             MountedVolume *v = &g_mounts_state.volumes[i];
128             /* May be null if it was unmounted and we haven't rescanned.
129              */
130             if (v->mount_point != NULL) {
131                 if (strcmp(v->mount_point, mount_point) == 0) {
132                     return v;
133                 }
134             }
135         }
136     }
137     return NULL;
138 }
139 
140 int
unmount_mounted_volume(const MountedVolume * volume)141 unmount_mounted_volume(const MountedVolume *volume)
142 {
143     /* Intentionally pass NULL to umount if the caller tries
144      * to unmount a volume they already unmounted using this
145      * function.
146      */
147     int ret = umount(volume->mount_point);
148     if (ret == 0) {
149         free_volume_internals(volume, 1);
150         return 0;
151     }
152     return ret;
153 }
154 
155 int
remount_read_only(const MountedVolume * volume)156 remount_read_only(const MountedVolume* volume)
157 {
158     return mount(volume->device, volume->mount_point, volume->filesystem,
159                  MS_NOATIME | MS_NODEV | MS_NODIRATIME |
160                  MS_RDONLY | MS_REMOUNT, 0);
161 }
162