/src/e2fsprogs/lib/ext2fs/ismounted.c
Line | Count | Source |
1 | | /* |
2 | | * ismounted.c --- Check to see if the filesystem was mounted |
3 | | * |
4 | | * Copyright (C) 1995,1996,1997,1998,1999,2000 Theodore Ts'o. |
5 | | * |
6 | | * %Begin-Header% |
7 | | * This file may be redistributed under the terms of the GNU Library |
8 | | * General Public License, version 2. |
9 | | * %End-Header% |
10 | | */ |
11 | | |
12 | | /* define BSD_SOURCE to make sure we get the major() macro */ |
13 | | #ifndef _BSD_SOURCE |
14 | | #define _BSD_SOURCE |
15 | | #endif |
16 | | #ifndef _DEFAULT_SOURCE |
17 | | #define _DEFAULT_SOURCE /* since glibc 2.20 _SVID_SOURCE is deprecated */ |
18 | | #endif |
19 | | |
20 | | #include "config.h" |
21 | | #include <stdio.h> |
22 | | #if HAVE_UNISTD_H |
23 | | #include <unistd.h> |
24 | | #endif |
25 | | #if HAVE_ERRNO_H |
26 | | #include <errno.h> |
27 | | #endif |
28 | | #include <fcntl.h> |
29 | | #ifdef HAVE_LINUX_FD_H |
30 | | #include <linux/fd.h> |
31 | | #endif |
32 | | #ifdef HAVE_LINUX_LOOP_H |
33 | | #include <linux/loop.h> |
34 | | #include <sys/ioctl.h> |
35 | | #ifdef HAVE_LINUX_MAJOR_H |
36 | | #include <linux/major.h> |
37 | | #endif /* HAVE_LINUX_MAJOR_H */ |
38 | | #endif /* HAVE_LINUX_LOOP_H */ |
39 | | #ifdef HAVE_MNTENT_H |
40 | | #include <mntent.h> |
41 | | #endif |
42 | | #ifdef HAVE_GETMNTINFO |
43 | | #include <paths.h> |
44 | | #include <sys/param.h> |
45 | | #include <sys/mount.h> |
46 | | #endif /* HAVE_GETMNTINFO */ |
47 | | #include <string.h> |
48 | | #include <sys/stat.h> |
49 | | #if HAVE_SYS_TYPES_H |
50 | | #include <sys/types.h> |
51 | | #endif |
52 | | #ifdef HAVE_SYS_SYSMACROS_H |
53 | | #include <sys/sysmacros.h> |
54 | | #endif |
55 | | |
56 | | #include "ext2_fs.h" |
57 | | #include "ext2fs.h" |
58 | | #include "ext2fsP.h" |
59 | | |
60 | | #ifdef HAVE_SETMNTENT |
61 | | /* |
62 | | * Check to see if a regular file is mounted. |
63 | | * If /etc/mtab/ is a symlink of /proc/mounts, you will need the following check |
64 | | * because the name in /proc/mounts is a loopback device not a regular file. |
65 | | */ |
66 | | static int check_loop_mounted(const char *mnt_fsname, dev_t mnt_rdev, |
67 | | dev_t file_dev, ino_t file_ino) |
68 | 0 | { |
69 | 0 | #if defined(HAVE_LINUX_LOOP_H) && defined(HAVE_LINUX_MAJOR_H) |
70 | 0 | struct loop_info64 loopinfo = {0, }; |
71 | 0 | int loop_fd, ret; |
72 | |
|
73 | 0 | if (major(mnt_rdev) == LOOP_MAJOR) { |
74 | 0 | loop_fd = open(mnt_fsname, O_RDONLY); |
75 | 0 | if (loop_fd < 0) |
76 | 0 | return -1; |
77 | | |
78 | 0 | ret = ioctl(loop_fd, LOOP_GET_STATUS64, &loopinfo); |
79 | 0 | close(loop_fd); |
80 | 0 | if (ret < 0) |
81 | 0 | return -1; |
82 | | |
83 | 0 | if (file_dev == loopinfo.lo_device && |
84 | 0 | file_ino == loopinfo.lo_inode) |
85 | 0 | return 1; |
86 | 0 | } |
87 | 0 | #endif /* defined(HAVE_LINUX_LOOP_H) && defined(HAVE_LINUX_MAJOR_H) */ |
88 | 0 | return 0; |
89 | 0 | } |
90 | | |
91 | | /* |
92 | | * Helper function which checks a file in /etc/mtab format to see if a |
93 | | * filesystem is mounted. Returns an error if the file doesn't exist |
94 | | * or can't be opened. |
95 | | */ |
96 | | static errcode_t check_mntent_file(const char *mtab_file, const char *file, |
97 | | int *mount_flags, char *mtpt, int mtlen) |
98 | 0 | { |
99 | 0 | struct mntent *mnt; |
100 | 0 | struct stat st_buf, dir_st_buf; |
101 | 0 | errcode_t retval = 0; |
102 | 0 | dev_t file_dev=0, file_rdev=0; |
103 | 0 | ino_t file_ino=0; |
104 | 0 | FILE *f; |
105 | 0 | int fd; |
106 | |
|
107 | 0 | *mount_flags = 0; |
108 | |
|
109 | 0 | if ((f = setmntent (mtab_file, "r")) == NULL) { |
110 | 0 | if (errno == ENOENT) { |
111 | 0 | if (ext2fs_safe_getenv("EXT2FS_NO_MTAB_OK")) |
112 | 0 | return 0; |
113 | 0 | else |
114 | 0 | return EXT2_ET_NO_MTAB_FILE; |
115 | 0 | } |
116 | 0 | return errno; |
117 | 0 | } |
118 | 0 | if (stat(file, &st_buf) == 0) { |
119 | 0 | if (ext2fsP_is_disk_device(st_buf.st_mode)) { |
120 | 0 | #ifndef __GNU__ /* The GNU hurd is broken with respect to stat devices */ |
121 | 0 | file_rdev = st_buf.st_rdev; |
122 | 0 | #endif /* __GNU__ */ |
123 | 0 | } else { |
124 | 0 | file_dev = st_buf.st_dev; |
125 | 0 | file_ino = st_buf.st_ino; |
126 | 0 | } |
127 | 0 | } |
128 | 0 | while ((mnt = getmntent (f)) != NULL) { |
129 | 0 | if (mnt->mnt_fsname[0] != '/') |
130 | 0 | continue; |
131 | 0 | if (strcmp(file, mnt->mnt_fsname) == 0) { |
132 | 0 | if (stat(mnt->mnt_dir, &st_buf) != 0) |
133 | 0 | continue; |
134 | 0 | if (file_rdev && (file_rdev != st_buf.st_dev)) { |
135 | | #ifdef DEBUG |
136 | | printf("Bogus entry in %s! " |
137 | | "(%s is not mounted on %s)\n", |
138 | | mtab_file, file, mnt->mnt_dir); |
139 | | #endif /* DEBUG */ |
140 | 0 | continue; |
141 | 0 | } |
142 | 0 | break; |
143 | 0 | } |
144 | 0 | if (stat(mnt->mnt_fsname, &st_buf) == 0) { |
145 | 0 | if (ext2fsP_is_disk_device(st_buf.st_mode)) { |
146 | 0 | #ifndef __GNU__ |
147 | 0 | if (file_rdev && |
148 | 0 | (file_rdev == st_buf.st_rdev)) { |
149 | 0 | if (stat(mnt->mnt_dir, |
150 | 0 | &dir_st_buf) != 0) |
151 | 0 | continue; |
152 | 0 | if (file_rdev == dir_st_buf.st_dev) |
153 | 0 | break; |
154 | 0 | } |
155 | 0 | if (check_loop_mounted(mnt->mnt_fsname, |
156 | 0 | st_buf.st_rdev, file_dev, |
157 | 0 | file_ino) == 1) |
158 | 0 | break; |
159 | 0 | #endif /* __GNU__ */ |
160 | 0 | } else { |
161 | 0 | if (file_dev && ((file_dev == st_buf.st_dev) && |
162 | 0 | (file_ino == st_buf.st_ino))) |
163 | 0 | break; |
164 | 0 | } |
165 | 0 | } |
166 | 0 | } |
167 | |
|
168 | 0 | if (mnt == 0) { |
169 | 0 | #ifndef __GNU__ /* The GNU hurd is broken with respect to stat devices */ |
170 | | /* |
171 | | * Do an extra check to see if this is the root device. We |
172 | | * can't trust /etc/mtab, and /proc/mounts will only list |
173 | | * /dev/root for the root filesystem. Argh. Instead we |
174 | | * check if the given device has the same major/minor number |
175 | | * as the device that the root directory is on. |
176 | | */ |
177 | 0 | if (file_rdev && stat("/", &st_buf) == 0) { |
178 | 0 | if (st_buf.st_dev == file_rdev) { |
179 | 0 | *mount_flags = EXT2_MF_MOUNTED; |
180 | 0 | if (mtpt) |
181 | 0 | strncpy(mtpt, "/", mtlen); |
182 | 0 | goto is_root; |
183 | 0 | } |
184 | 0 | } |
185 | 0 | #endif /* __GNU__ */ |
186 | 0 | goto errout; |
187 | 0 | } |
188 | 0 | *mount_flags = EXT2_MF_MOUNTED; |
189 | |
|
190 | 0 | #ifdef MNTOPT_RO |
191 | | /* Check to see if the ro option is set */ |
192 | 0 | if (hasmntopt(mnt, MNTOPT_RO)) |
193 | 0 | *mount_flags |= EXT2_MF_READONLY; |
194 | 0 | #endif |
195 | |
|
196 | 0 | if (mtpt) |
197 | 0 | strncpy(mtpt, mnt->mnt_dir, mtlen); |
198 | | /* |
199 | | * Check to see if we're referring to the root filesystem. |
200 | | * If so, do a manual check to see if we can open /etc/mtab |
201 | | * read/write, since if the root is mounted read/only, the |
202 | | * contents of /etc/mtab may not be accurate. |
203 | | */ |
204 | 0 | if (!strcmp(mnt->mnt_dir, "/")) { |
205 | 0 | is_root: |
206 | 0 | #define TEST_FILE "/.ismount-test-file" |
207 | 0 | *mount_flags |= EXT2_MF_ISROOT; |
208 | 0 | fd = open(TEST_FILE, O_RDWR|O_CREAT, 0600); |
209 | 0 | if (fd < 0) { |
210 | 0 | if (errno == EROFS) |
211 | 0 | *mount_flags |= EXT2_MF_READONLY; |
212 | 0 | } else |
213 | 0 | close(fd); |
214 | 0 | (void) unlink(TEST_FILE); |
215 | 0 | } |
216 | |
|
217 | 0 | if (mnt && mnt->mnt_type && |
218 | 0 | (!strcmp(mnt->mnt_type, "ext4") || |
219 | 0 | !strcmp(mnt->mnt_type, "ext3") || |
220 | 0 | !strcmp(mnt->mnt_type, "ext2"))) |
221 | 0 | *mount_flags |= EXT2_MF_EXTFS; |
222 | 0 | retval = 0; |
223 | 0 | errout: |
224 | 0 | endmntent (f); |
225 | 0 | return retval; |
226 | 0 | } |
227 | | |
228 | | static errcode_t check_mntent(const char *file, int *mount_flags, |
229 | | char *mtpt, int mtlen) |
230 | 0 | { |
231 | 0 | errcode_t retval; |
232 | |
|
233 | | #ifdef DEBUG |
234 | | retval = check_mntent_file("/tmp/mtab", file, mount_flags, |
235 | | mtpt, mtlen); |
236 | | if (retval == 0) |
237 | | return 0; |
238 | | #endif /* DEBUG */ |
239 | 0 | #ifdef __linux__ |
240 | 0 | retval = check_mntent_file("/proc/mounts", file, mount_flags, |
241 | 0 | mtpt, mtlen); |
242 | 0 | if (retval == 0) |
243 | 0 | return 0; |
244 | 0 | #endif /* __linux__ */ |
245 | 0 | #if defined(MOUNTED) || defined(_PATH_MOUNTED) |
246 | | #ifndef MOUNTED |
247 | | #define MOUNTED _PATH_MOUNTED |
248 | | #endif /* MOUNTED */ |
249 | 0 | retval = check_mntent_file(MOUNTED, file, mount_flags, mtpt, mtlen); |
250 | 0 | return retval; |
251 | | #else |
252 | | *mount_flags = 0; |
253 | | return 0; |
254 | | #endif /* defined(MOUNTED) || defined(_PATH_MOUNTED) */ |
255 | 0 | } |
256 | | |
257 | | #else |
258 | | #if defined(HAVE_GETMNTINFO) |
259 | | |
260 | | static errcode_t check_getmntinfo(const char *file, int *mount_flags, |
261 | | char *mtpt, int mtlen) |
262 | | { |
263 | | struct statfs *mp; |
264 | | int len, n; |
265 | | const char *s1; |
266 | | char *s2; |
267 | | |
268 | | n = getmntinfo(&mp, MNT_NOWAIT); |
269 | | if (n == 0) |
270 | | return errno; |
271 | | |
272 | | len = sizeof(_PATH_DEV) - 1; |
273 | | s1 = file; |
274 | | if (strncmp(_PATH_DEV, s1, len) == 0) |
275 | | s1 += len; |
276 | | |
277 | | *mount_flags = 0; |
278 | | while (--n >= 0) { |
279 | | s2 = mp->f_mntfromname; |
280 | | if (strncmp(_PATH_DEV, s2, len) == 0) { |
281 | | s2 += len - 1; |
282 | | *s2 = 'r'; |
283 | | } |
284 | | if (strcmp(s1, s2) == 0 || strcmp(s1, &s2[1]) == 0) { |
285 | | *mount_flags = EXT2_MF_MOUNTED; |
286 | | break; |
287 | | } |
288 | | ++mp; |
289 | | } |
290 | | if (mtpt) |
291 | | strncpy(mtpt, mp->f_mntonname, mtlen); |
292 | | return 0; |
293 | | } |
294 | | #endif /* HAVE_GETMNTINFO */ |
295 | | #endif /* HAVE_SETMNTENT */ |
296 | | |
297 | | /* |
298 | | * Check to see if we're dealing with the swap device. |
299 | | */ |
300 | | static int is_swap_device(const char *file) |
301 | 0 | { |
302 | 0 | FILE *f; |
303 | 0 | char buf[1024], *cp; |
304 | 0 | dev_t file_dev; |
305 | 0 | struct stat st_buf; |
306 | 0 | int ret = 0; |
307 | |
|
308 | 0 | file_dev = 0; |
309 | 0 | #ifndef __GNU__ /* The GNU hurd is broken with respect to stat devices */ |
310 | 0 | if ((stat(file, &st_buf) == 0) && |
311 | 0 | ext2fsP_is_disk_device(st_buf.st_mode)) |
312 | 0 | file_dev = st_buf.st_rdev; |
313 | 0 | #endif /* __GNU__ */ |
314 | |
|
315 | 0 | if (!(f = fopen("/proc/swaps", "r"))) |
316 | 0 | return 0; |
317 | | /* Skip the first line */ |
318 | 0 | if (!fgets(buf, sizeof(buf), f)) |
319 | 0 | goto leave; |
320 | 0 | if (*buf && strncmp(buf, "Filename\t", 9)) |
321 | | /* Linux <=2.6.19 contained a bug in the /proc/swaps |
322 | | * code where the header would not be displayed |
323 | | */ |
324 | 0 | goto valid_first_line; |
325 | | |
326 | 0 | while (fgets(buf, sizeof(buf), f)) { |
327 | 0 | valid_first_line: |
328 | 0 | if ((cp = strchr(buf, ' ')) != NULL) |
329 | 0 | *cp = 0; |
330 | 0 | if ((cp = strchr(buf, '\t')) != NULL) |
331 | 0 | *cp = 0; |
332 | 0 | if (strcmp(buf, file) == 0) { |
333 | 0 | ret++; |
334 | 0 | break; |
335 | 0 | } |
336 | 0 | #ifndef __GNU__ |
337 | 0 | if (file_dev && (stat(buf, &st_buf) == 0) && |
338 | 0 | ext2fsP_is_disk_device(st_buf.st_mode) && |
339 | 0 | file_dev == st_buf.st_rdev) { |
340 | 0 | ret++; |
341 | 0 | break; |
342 | 0 | } |
343 | 0 | #endif /* __GNU__ */ |
344 | 0 | } |
345 | | |
346 | 0 | leave: |
347 | 0 | fclose(f); |
348 | 0 | return ret; |
349 | 0 | } |
350 | | |
351 | | |
352 | | /* |
353 | | * ext2fs_check_mount_point() fills determines if the device is |
354 | | * mounted or otherwise busy, and fills in mount_flags with one or |
355 | | * more of the following flags: EXT2_MF_MOUNTED, EXT2_MF_ISROOT, |
356 | | * EXT2_MF_READONLY, EXT2_MF_SWAP, and EXT2_MF_BUSY. If mtpt is |
357 | | * non-NULL, the directory where the device is mounted is copied to |
358 | | * where mtpt is pointing, up to mtlen characters. |
359 | | */ |
360 | | #ifdef __TURBOC__ |
361 | | #pragma argsused |
362 | | #endif |
363 | | errcode_t ext2fs_check_mount_point(const char *device, int *mount_flags, |
364 | | char *mtpt, int mtlen) |
365 | 0 | { |
366 | 0 | errcode_t retval = 0; |
367 | 0 | int busy = 0; |
368 | |
|
369 | 0 | if (ext2fs_safe_getenv("EXT2FS_PRETEND_RO_MOUNT")) { |
370 | 0 | *mount_flags = EXT2_MF_MOUNTED | EXT2_MF_READONLY; |
371 | 0 | if (ext2fs_safe_getenv("EXT2FS_PRETEND_ROOTFS")) |
372 | 0 | *mount_flags = EXT2_MF_ISROOT; |
373 | 0 | return 0; |
374 | 0 | } |
375 | 0 | if (ext2fs_safe_getenv("EXT2FS_PRETEND_RW_MOUNT")) { |
376 | 0 | *mount_flags = EXT2_MF_MOUNTED; |
377 | 0 | if (ext2fs_safe_getenv("EXT2FS_PRETEND_ROOTFS")) |
378 | 0 | *mount_flags = EXT2_MF_ISROOT; |
379 | 0 | return 0; |
380 | 0 | } |
381 | | |
382 | 0 | #ifdef __linux__ /* This only works on Linux 2.6+ systems */ |
383 | 0 | { |
384 | 0 | struct stat st_buf; |
385 | |
|
386 | 0 | if (stat(device, &st_buf) == 0 && |
387 | 0 | ext2fsP_is_disk_device(st_buf.st_mode)) { |
388 | 0 | int fd = open(device, O_RDONLY | O_EXCL); |
389 | |
|
390 | 0 | if (fd >= 0) { |
391 | | /* |
392 | | * The device is not busy so it's |
393 | | * definitelly not mounted. No need to |
394 | | * to perform any more checks. |
395 | | */ |
396 | 0 | close(fd); |
397 | 0 | *mount_flags = 0; |
398 | 0 | return 0; |
399 | 0 | } else if (errno == EBUSY) { |
400 | 0 | busy = 1; |
401 | 0 | } |
402 | 0 | } |
403 | 0 | } |
404 | 0 | #endif |
405 | | |
406 | 0 | if (is_swap_device(device)) { |
407 | 0 | *mount_flags = EXT2_MF_MOUNTED | EXT2_MF_SWAP; |
408 | 0 | if (mtpt) |
409 | 0 | strncpy(mtpt, "<swap>", mtlen); |
410 | 0 | } else { |
411 | 0 | #ifdef HAVE_SETMNTENT |
412 | 0 | retval = check_mntent(device, mount_flags, mtpt, mtlen); |
413 | | #else |
414 | | #ifdef HAVE_GETMNTINFO |
415 | | retval = check_getmntinfo(device, mount_flags, mtpt, mtlen); |
416 | | #else |
417 | | #if defined(__GNUC__) && !defined(_WIN32) |
418 | | #warning "Can't use getmntent or getmntinfo to check for mounted filesystems!" |
419 | | #endif |
420 | | *mount_flags = 0; |
421 | | #endif /* HAVE_GETMNTINFO */ |
422 | | #endif /* HAVE_SETMNTENT */ |
423 | 0 | } |
424 | 0 | if (retval) |
425 | 0 | return retval; |
426 | | |
427 | 0 | if (busy) |
428 | 0 | *mount_flags |= EXT2_MF_BUSY; |
429 | |
|
430 | 0 | return 0; |
431 | 0 | } |
432 | | |
433 | | /* |
434 | | * ext2fs_check_if_mounted() sets the mount_flags EXT2_MF_MOUNTED, |
435 | | * EXT2_MF_READONLY, and EXT2_MF_ROOT |
436 | | * |
437 | | */ |
438 | | errcode_t ext2fs_check_if_mounted(const char *file, int *mount_flags) |
439 | 0 | { |
440 | | return ext2fs_check_mount_point(file, mount_flags, NULL, 0); |
441 | 0 | } |
442 | | |
443 | | #ifdef DEBUG |
444 | | int main(int argc, char **argv) |
445 | | { |
446 | | int retval, mount_flags; |
447 | | char mntpt[80]; |
448 | | |
449 | | if (argc < 2) { |
450 | | fprintf(stderr, "Usage: %s device\n", argv[0]); |
451 | | exit(1); |
452 | | } |
453 | | |
454 | | add_error_table(&et_ext2_error_table); |
455 | | mntpt[0] = 0; |
456 | | retval = ext2fs_check_mount_point(argv[1], &mount_flags, |
457 | | mntpt, sizeof(mntpt)); |
458 | | if (retval) { |
459 | | com_err(argv[0], retval, |
460 | | "while calling ext2fs_check_if_mounted"); |
461 | | exit(1); |
462 | | } |
463 | | printf("Device %s reports flags %02x\n", argv[1], mount_flags); |
464 | | if (mount_flags & EXT2_MF_BUSY) |
465 | | printf("\t%s is apparently in use.\n", argv[1]); |
466 | | if (mount_flags & EXT2_MF_MOUNTED) |
467 | | printf("\t%s is mounted.\n", argv[1]); |
468 | | if (mount_flags & EXT2_MF_SWAP) |
469 | | printf("\t%s is a swap device.\n", argv[1]); |
470 | | if (mount_flags & EXT2_MF_READONLY) |
471 | | printf("\t%s is read-only.\n", argv[1]); |
472 | | if (mount_flags & EXT2_MF_ISROOT) |
473 | | printf("\t%s is the root filesystem.\n", argv[1]); |
474 | | if (mntpt[0]) |
475 | | printf("\t%s is mounted on %s.\n", argv[1], mntpt); |
476 | | exit(0); |
477 | | } |
478 | | #endif /* DEBUG */ |