]>
Commit | Line | Data |
---|---|---|
607c2a72 KZ |
1 | /* |
2 | * ismounted.c --- Check to see if the filesystem was mounted | |
3 | * | |
4 | * Copyright (C) 1995,1996,1997,1998,1999,2000,2008 Theodore Ts'o. | |
5 | * | |
6 | * This file may be redistributed under the terms of the GNU Public | |
7 | * License. | |
8 | */ | |
9 | #include <stdio.h> | |
10 | #include <unistd.h> | |
11 | #include <stdlib.h> | |
12 | #include <errno.h> | |
13 | #include <fcntl.h> | |
fbc333fe | 14 | #ifdef HAVE_MNTENT_H |
607c2a72 | 15 | #include <mntent.h> |
9779651e | 16 | #endif |
607c2a72 KZ |
17 | #include <string.h> |
18 | #include <sys/stat.h> | |
19 | #include <ctype.h> | |
acf6ab6f | 20 | #include <sys/param.h> |
ac1c53e4 RM |
21 | |
22 | #ifndef __linux__ | |
23 | # ifdef HAVE_SYS_UCRED_H | |
24 | # include <sys/ucred.h> | |
25 | # endif | |
26 | # ifdef HAVE_SYS_MOUNT_H | |
27 | # include <sys/mount.h> | |
28 | # endif | |
9779651e | 29 | #endif |
607c2a72 | 30 | |
fb429f22 | 31 | #include "pathnames.h" |
8ecc6ba8 | 32 | #include "strutils.h" |
607c2a72 | 33 | #include "ismounted.h" |
4951f9b3 | 34 | #include "c.h" |
ca4f6229 KZ |
35 | #ifdef __linux__ |
36 | # include "loopdev.h" | |
37 | #endif | |
38 | ||
39 | ||
607c2a72 | 40 | |
acf6ab6f | 41 | #ifdef HAVE_MNTENT_H |
607c2a72 KZ |
42 | /* |
43 | * Helper function which checks a file in /etc/mtab format to see if a | |
44 | * filesystem is mounted. Returns an error if the file doesn't exist | |
45 | * or can't be opened. | |
46 | */ | |
47 | static int check_mntent_file(const char *mtab_file, const char *file, | |
acf6ab6f | 48 | int *mount_flags, char *mtpt, int mtlen) |
607c2a72 | 49 | { |
acf6ab6f | 50 | struct mntent *mnt; |
607c2a72 KZ |
51 | struct stat st_buf; |
52 | int retval = 0; | |
45ddc110 | 53 | dev_t file_dev=0, file_rdev=0; |
607c2a72 | 54 | ino_t file_ino=0; |
fb429f22 | 55 | FILE *f; |
acf6ab6f | 56 | int fd; |
607c2a72 KZ |
57 | |
58 | *mount_flags = 0; | |
607c2a72 KZ |
59 | if ((f = setmntent (mtab_file, "r")) == NULL) |
60 | return errno; | |
ca4f6229 | 61 | |
607c2a72 KZ |
62 | if (stat(file, &st_buf) == 0) { |
63 | if (S_ISBLK(st_buf.st_mode)) { | |
64 | #ifndef __GNU__ /* The GNU hurd is broken with respect to stat devices */ | |
65 | file_rdev = st_buf.st_rdev; | |
66 | #endif /* __GNU__ */ | |
67 | } else { | |
68 | file_dev = st_buf.st_dev; | |
69 | file_ino = st_buf.st_ino; | |
70 | } | |
71 | } | |
ca4f6229 | 72 | |
acf6ab6f KZ |
73 | while ((mnt = getmntent (f)) != NULL) { |
74 | if (mnt->mnt_fsname[0] != '/') | |
607c2a72 | 75 | continue; |
acf6ab6f | 76 | if (strcmp(file, mnt->mnt_fsname) == 0) |
607c2a72 | 77 | break; |
ca4f6229 KZ |
78 | if (stat(mnt->mnt_fsname, &st_buf) != 0) |
79 | continue; | |
80 | ||
81 | if (S_ISBLK(st_buf.st_mode)) { | |
607c2a72 | 82 | #ifndef __GNU__ |
45ddc110 KZ |
83 | if (file_rdev && file_rdev == st_buf.st_rdev) |
84 | break; | |
85 | #ifdef __linux__ | |
86 | /* maybe the file is loopdev backing file */ | |
87 | if (file_dev | |
88 | && major(st_buf.st_rdev) == LOOPDEV_MAJOR | |
74a4705a | 89 | && loopdev_is_used(mnt->mnt_fsname, file, 0, 0, 0)) |
ca4f6229 | 90 | break; |
45ddc110 | 91 | #endif /* __linux__ */ |
ca4f6229 KZ |
92 | #endif /* __GNU__ */ |
93 | } else { | |
94 | if (file_dev && ((file_dev == st_buf.st_dev) && | |
95 | (file_ino == st_buf.st_ino))) | |
96 | break; | |
607c2a72 KZ |
97 | } |
98 | } | |
99 | ||
f8bd089b | 100 | if (mnt == NULL) { |
607c2a72 KZ |
101 | #ifndef __GNU__ /* The GNU hurd is broken with respect to stat devices */ |
102 | /* | |
103 | * Do an extra check to see if this is the root device. We | |
104 | * can't trust /etc/mtab, and /proc/mounts will only list | |
105 | * /dev/root for the root filesystem. Argh. Instead we | |
106 | * check if the given device has the same major/minor number | |
107 | * as the device that the root directory is on. | |
108 | */ | |
acf6ab6f KZ |
109 | if (file_rdev && stat("/", &st_buf) == 0 && |
110 | st_buf.st_dev == file_rdev) { | |
607c2a72 | 111 | *mount_flags = MF_MOUNTED; |
acf6ab6f | 112 | if (mtpt) |
8ecc6ba8 | 113 | xstrncpy(mtpt, "/", mtlen); |
acf6ab6f KZ |
114 | goto is_root; |
115 | } | |
607c2a72 KZ |
116 | #endif /* __GNU__ */ |
117 | goto errout; | |
118 | } | |
119 | #ifndef __GNU__ /* The GNU hurd is deficient; what else is new? */ | |
120 | /* Validate the entry in case /etc/mtab is out of date */ | |
121 | /* | |
122 | * We need to be paranoid, because some broken distributions | |
123 | * (read: Slackware) don't initialize /etc/mtab before checking | |
124 | * all of the non-root filesystems on the disk. | |
125 | */ | |
acf6ab6f | 126 | if (stat(mnt->mnt_dir, &st_buf) < 0) { |
607c2a72 KZ |
127 | retval = errno; |
128 | if (retval == ENOENT) { | |
129 | #ifdef DEBUG | |
130 | printf("Bogus entry in %s! (%s does not exist)\n", | |
acf6ab6f | 131 | mtab_file, mnt->mnt_dir); |
607c2a72 KZ |
132 | #endif /* DEBUG */ |
133 | retval = 0; | |
134 | } | |
135 | goto errout; | |
136 | } | |
137 | if (file_rdev && (st_buf.st_dev != file_rdev)) { | |
138 | #ifdef DEBUG | |
139 | printf("Bogus entry in %s! (%s not mounted on %s)\n", | |
acf6ab6f | 140 | mtab_file, file, mnt->mnt_dir); |
607c2a72 KZ |
141 | #endif /* DEBUG */ |
142 | goto errout; | |
143 | } | |
144 | #endif /* __GNU__ */ | |
145 | *mount_flags = MF_MOUNTED; | |
146 | ||
acf6ab6f KZ |
147 | #ifdef MNTOPT_RO |
148 | /* Check to see if the ro option is set */ | |
149 | if (hasmntopt(mnt, MNTOPT_RO)) | |
150 | *mount_flags |= MF_READONLY; | |
151 | #endif | |
152 | ||
153 | if (mtpt) | |
8ecc6ba8 | 154 | xstrncpy(mtpt, mnt->mnt_dir, mtlen); |
acf6ab6f KZ |
155 | /* |
156 | * Check to see if we're referring to the root filesystem. | |
157 | * If so, do a manual check to see if we can open /etc/mtab | |
158 | * read/write, since if the root is mounted read/only, the | |
159 | * contents of /etc/mtab may not be accurate. | |
160 | */ | |
161 | if (!strcmp(mnt->mnt_dir, "/")) { | |
162 | is_root: | |
163 | #define TEST_FILE "/.ismount-test-file" | |
164 | *mount_flags |= MF_ISROOT; | |
b1fa3e22 | 165 | fd = open(TEST_FILE, O_RDWR|O_CREAT|O_CLOEXEC, 0600); |
acf6ab6f KZ |
166 | if (fd < 0) { |
167 | if (errno == EROFS) | |
168 | *mount_flags |= MF_READONLY; | |
169 | } else | |
170 | close(fd); | |
171 | (void) unlink(TEST_FILE); | |
172 | } | |
607c2a72 KZ |
173 | retval = 0; |
174 | errout: | |
175 | endmntent (f); | |
176 | return retval; | |
177 | } | |
178 | ||
acf6ab6f KZ |
179 | static int check_mntent(const char *file, int *mount_flags, |
180 | char *mtpt, int mtlen) | |
607c2a72 KZ |
181 | { |
182 | int retval; | |
607c2a72 | 183 | |
acf6ab6f KZ |
184 | #ifdef DEBUG |
185 | retval = check_mntent_file("/tmp/mtab", file, mount_flags, | |
186 | mtpt, mtlen); | |
187 | if (retval == 0) | |
188 | return 0; | |
189 | #endif /* DEBUG */ | |
607c2a72 | 190 | #ifdef __linux__ |
acf6ab6f KZ |
191 | retval = check_mntent_file("/proc/mounts", file, mount_flags, |
192 | mtpt, mtlen); | |
193 | if (retval == 0 && (*mount_flags != 0)) | |
607c2a72 | 194 | return 0; |
8c9e72ce KZ |
195 | if (access("/proc/mounts", R_OK) == 0) { |
196 | *mount_flags = 0; | |
197 | return retval; | |
198 | } | |
607c2a72 | 199 | #endif /* __linux__ */ |
acf6ab6f KZ |
200 | #if defined(MOUNTED) || defined(_PATH_MOUNTED) |
201 | #ifndef MOUNTED | |
202 | #define MOUNTED _PATH_MOUNTED | |
203 | #endif /* MOUNTED */ | |
204 | retval = check_mntent_file(MOUNTED, file, mount_flags, mtpt, mtlen); | |
205 | return retval; | |
206 | #else | |
207 | *mount_flags = 0; | |
208 | return 0; | |
209 | #endif /* defined(MOUNTED) || defined(_PATH_MOUNTED) */ | |
210 | } | |
211 | ||
212 | #else | |
213 | #if defined(HAVE_GETMNTINFO) | |
214 | ||
215 | static int check_getmntinfo(const char *file, int *mount_flags, | |
216 | char *mtpt, int mtlen) | |
217 | { | |
218 | struct statfs *mp; | |
219 | int len, n; | |
220 | const char *s1; | |
221 | char *s2; | |
222 | ||
223 | n = getmntinfo(&mp, MNT_NOWAIT); | |
224 | if (n == 0) | |
225 | return errno; | |
226 | ||
227 | len = sizeof(_PATH_DEV) - 1; | |
228 | s1 = file; | |
229 | if (strncmp(_PATH_DEV, s1, len) == 0) | |
230 | s1 += len; | |
231 | ||
232 | *mount_flags = 0; | |
233 | while (--n >= 0) { | |
234 | s2 = mp->f_mntfromname; | |
235 | if (strncmp(_PATH_DEV, s2, len) == 0) { | |
236 | s2 += len - 1; | |
237 | *s2 = 'r'; | |
238 | } | |
239 | if (strcmp(s1, s2) == 0 || strcmp(s1, &s2[1]) == 0) { | |
240 | *mount_flags = MF_MOUNTED; | |
241 | break; | |
242 | } | |
243 | ++mp; | |
244 | } | |
4a479160 | 245 | if (mtpt && n >= 0) |
8ecc6ba8 | 246 | xstrncpy(mtpt, mp->f_mntonname, mtlen); |
acf6ab6f KZ |
247 | return 0; |
248 | } | |
249 | #endif /* HAVE_GETMNTINFO */ | |
250 | #endif /* HAVE_MNTENT_H */ | |
251 | ||
252 | /* | |
253 | * Check to see if we're dealing with the swap device. | |
254 | */ | |
255 | static int is_swap_device(const char *file) | |
256 | { | |
257 | FILE *f; | |
258 | char buf[1024], *cp; | |
259 | dev_t file_dev; | |
260 | struct stat st_buf; | |
261 | int ret = 0; | |
262 | ||
263 | file_dev = 0; | |
264 | #ifndef __GNU__ /* The GNU hurd is broken with respect to stat devices */ | |
265 | if ((stat(file, &st_buf) == 0) && | |
266 | S_ISBLK(st_buf.st_mode)) | |
267 | file_dev = st_buf.st_rdev; | |
268 | #endif /* __GNU__ */ | |
269 | ||
b1fa3e22 | 270 | if (!(f = fopen("/proc/swaps", "r" UL_CLOEXECSTR))) |
acf6ab6f KZ |
271 | return 0; |
272 | /* Skip the first line */ | |
273 | if (!fgets(buf, sizeof(buf), f)) | |
274 | goto leave; | |
ad296391 | 275 | if (*buf && strncmp(buf, "Filename\t", 9) != 0) |
acf6ab6f KZ |
276 | /* Linux <=2.6.19 contained a bug in the /proc/swaps |
277 | * code where the header would not be displayed | |
278 | */ | |
279 | goto valid_first_line; | |
280 | ||
281 | while (fgets(buf, sizeof(buf), f)) { | |
282 | valid_first_line: | |
283 | if ((cp = strchr(buf, ' ')) != NULL) | |
284 | *cp = 0; | |
285 | if ((cp = strchr(buf, '\t')) != NULL) | |
286 | *cp = 0; | |
287 | if (strcmp(buf, file) == 0) { | |
288 | ret++; | |
289 | break; | |
290 | } | |
291 | #ifndef __GNU__ | |
292 | if (file_dev && (stat(buf, &st_buf) == 0) && | |
293 | S_ISBLK(st_buf.st_mode) && | |
294 | file_dev == st_buf.st_rdev) { | |
295 | ret++; | |
296 | break; | |
297 | } | |
298 | #endif /* __GNU__ */ | |
299 | } | |
300 | ||
301 | leave: | |
302 | fclose(f); | |
303 | return ret; | |
304 | } | |
305 | ||
306 | ||
307 | /* | |
4a479160 | 308 | * check_mount_point() determines if the device is mounted or otherwise |
acf6ab6f KZ |
309 | * busy, and fills in mount_flags with one or more of the following flags: |
310 | * MF_MOUNTED, MF_ISROOT, MF_READONLY, MF_SWAP, and MF_BUSY. If mtpt is | |
311 | * non-NULL, the directory where the device is mounted is copied to where mtpt | |
312 | * is pointing, up to mtlen characters. | |
313 | */ | |
314 | #ifdef __TURBOC__ | |
315 | #pragma argsused | |
316 | #endif | |
317 | int check_mount_point(const char *device, int *mount_flags, | |
318 | char *mtpt, int mtlen) | |
319 | { | |
acf6ab6f | 320 | int retval = 0; |
acf6ab6f KZ |
321 | |
322 | if (is_swap_device(device)) { | |
323 | *mount_flags = MF_MOUNTED | MF_SWAP; | |
f3135f93 | 324 | if (mtpt && mtlen) |
8ecc6ba8 | 325 | xstrncpy(mtpt, "[SWAP]", mtlen); |
acf6ab6f KZ |
326 | } else { |
327 | #ifdef HAVE_MNTENT_H | |
328 | retval = check_mntent(device, mount_flags, mtpt, mtlen); | |
329 | #else | |
330 | #ifdef HAVE_GETMNTINFO | |
331 | retval = check_getmntinfo(device, mount_flags, mtpt, mtlen); | |
332 | #else | |
333 | #ifdef __GNUC__ | |
334 | #warning "Can't use getmntent or getmntinfo to check for mounted filesystems!" | |
335 | #endif | |
336 | *mount_flags = 0; | |
337 | #endif /* HAVE_GETMNTINFO */ | |
338 | #endif /* HAVE_MNTENT_H */ | |
339 | } | |
340 | if (retval) | |
341 | return retval; | |
342 | ||
343 | #ifdef __linux__ /* This only works on Linux 2.6+ systems */ | |
8b9cf26a RM |
344 | { |
345 | struct stat st_buf; | |
346 | int fd; | |
347 | if ((stat(device, &st_buf) != 0) || | |
348 | !S_ISBLK(st_buf.st_mode)) | |
349 | return 0; | |
38401453 | 350 | fd = open(device, O_RDONLY|O_EXCL|O_CLOEXEC|O_NONBLOCK); |
8b9cf26a RM |
351 | if (fd < 0) { |
352 | if (errno == EBUSY) | |
353 | *mount_flags |= MF_BUSY; | |
354 | } else | |
355 | close(fd); | |
356 | } | |
acf6ab6f KZ |
357 | #endif |
358 | ||
359 | return 0; | |
360 | } | |
361 | ||
362 | int is_mounted(const char *file) | |
363 | { | |
364 | int retval; | |
365 | int mount_flags = 0; | |
366 | ||
367 | retval = check_mount_point(file, &mount_flags, NULL, 0); | |
607c2a72 KZ |
368 | if (retval) |
369 | return 0; | |
acf6ab6f | 370 | return mount_flags & MF_MOUNTED; |
607c2a72 KZ |
371 | } |
372 | ||
e8f7acb0 | 373 | #ifdef TEST_PROGRAM_ISMOUNTED |
607c2a72 KZ |
374 | int main(int argc, char **argv) |
375 | { | |
acf6ab6f KZ |
376 | int flags = 0; |
377 | char devname[PATH_MAX]; | |
378 | ||
607c2a72 KZ |
379 | if (argc < 2) { |
380 | fprintf(stderr, "Usage: %s device\n", argv[0]); | |
355eceba | 381 | return EXIT_FAILURE; |
607c2a72 KZ |
382 | } |
383 | ||
acf6ab6f KZ |
384 | if (check_mount_point(argv[1], &flags, devname, sizeof(devname)) == 0 && |
385 | (flags & MF_MOUNTED)) { | |
386 | if (flags & MF_SWAP) | |
387 | printf("used swap device\n"); | |
388 | else | |
389 | printf("mounted on %s\n", devname); | |
355eceba KZ |
390 | return EXIT_SUCCESS; |
391 | } | |
acf6ab6f | 392 | |
355eceba KZ |
393 | printf("not mounted\n"); |
394 | return EXIT_FAILURE; | |
607c2a72 KZ |
395 | } |
396 | #endif /* DEBUG */ |