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 { 69 #if defined(HAVE_LINUX_LOOP_H) && defined(HAVE_LINUX_MAJOR_H) 70 struct loop_info64 loopinfo = {0, }; 71 int loop_fd, ret; 72 73 if (major(mnt_rdev) == LOOP_MAJOR) { 74 loop_fd = open(mnt_fsname, O_RDONLY); 75 if (loop_fd < 0) 76 return -1; 77 78 ret = ioctl(loop_fd, LOOP_GET_STATUS64, &loopinfo); 79 close(loop_fd); 80 if (ret < 0) 81 return -1; 82 83 if (file_dev == loopinfo.lo_device && 84 file_ino == loopinfo.lo_inode) 85 return 1; 86 } 87 #endif /* defined(HAVE_LINUX_LOOP_H) && defined(HAVE_LINUX_MAJOR_H) */ 88 return 0; 89 } 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 { 99 struct mntent *mnt; 100 struct stat st_buf; 101 errcode_t retval = 0; 102 dev_t file_dev=0, file_rdev=0; 103 ino_t file_ino=0; 104 FILE *f; 105 int fd; 106 107 *mount_flags = 0; 108 109 if ((f = setmntent (mtab_file, "r")) == NULL) { 110 if (errno == ENOENT) { 111 if (getenv("EXT2FS_NO_MTAB_OK")) 112 return 0; 113 else 114 return EXT2_ET_NO_MTAB_FILE; 115 } 116 return errno; 117 } 118 if (stat(file, &st_buf) == 0) { 119 if (ext2fsP_is_disk_device(st_buf.st_mode)) { 120 #ifndef __GNU__ /* The GNU hurd is broken with respect to stat devices */ 121 file_rdev = st_buf.st_rdev; 122 #endif /* __GNU__ */ 123 } else { 124 file_dev = st_buf.st_dev; 125 file_ino = st_buf.st_ino; 126 } 127 } 128 while ((mnt = getmntent (f)) != NULL) { 129 if (mnt->mnt_fsname[0] != '/') 130 continue; 131 if (strcmp(file, mnt->mnt_fsname) == 0) 132 break; 133 if (stat(mnt->mnt_fsname, &st_buf) == 0) { 134 if (ext2fsP_is_disk_device(st_buf.st_mode)) { 135 #ifndef __GNU__ 136 if (file_rdev && (file_rdev == st_buf.st_rdev)) 137 break; 138 if (check_loop_mounted(mnt->mnt_fsname, 139 st_buf.st_rdev, file_dev, 140 file_ino) == 1) 141 break; 142 #endif /* __GNU__ */ 143 } else { 144 if (file_dev && ((file_dev == st_buf.st_dev) && 145 (file_ino == st_buf.st_ino))) 146 break; 147 } 148 } 149 } 150 151 if (mnt == 0) { 152 #ifndef __GNU__ /* The GNU hurd is broken with respect to stat devices */ 153 /* 154 * Do an extra check to see if this is the root device. We 155 * can't trust /etc/mtab, and /proc/mounts will only list 156 * /dev/root for the root filesystem. Argh. Instead we 157 * check if the given device has the same major/minor number 158 * as the device that the root directory is on. 159 */ 160 if (file_rdev && stat("/", &st_buf) == 0) { 161 if (st_buf.st_dev == file_rdev) { 162 *mount_flags = EXT2_MF_MOUNTED; 163 if (mtpt) 164 strncpy(mtpt, "/", mtlen); 165 goto is_root; 166 } 167 } 168 #endif /* __GNU__ */ 169 goto errout; 170 } 171 #ifndef __GNU__ /* The GNU hurd is deficient; what else is new? */ 172 /* Validate the entry in case /etc/mtab is out of date */ 173 /* 174 * We need to be paranoid, because some broken distributions 175 * (read: Slackware) don't initialize /etc/mtab before checking 176 * all of the non-root filesystems on the disk. 177 */ 178 if (stat(mnt->mnt_dir, &st_buf) < 0) { 179 retval = errno; 180 if (retval == ENOENT) { 181 #ifdef DEBUG 182 printf("Bogus entry in %s! (%s does not exist)\n", 183 mtab_file, mnt->mnt_dir); 184 #endif /* DEBUG */ 185 retval = 0; 186 } 187 goto errout; 188 } 189 if (file_rdev && (st_buf.st_dev != file_rdev)) { 190 #ifdef DEBUG 191 printf("Bogus entry in %s! (%s not mounted on %s)\n", 192 mtab_file, file, mnt->mnt_dir); 193 #endif /* DEBUG */ 194 goto errout; 195 } 196 #endif /* __GNU__ */ 197 *mount_flags = EXT2_MF_MOUNTED; 198 199 #ifdef MNTOPT_RO 200 /* Check to see if the ro option is set */ 201 if (hasmntopt(mnt, MNTOPT_RO)) 202 *mount_flags |= EXT2_MF_READONLY; 203 #endif 204 205 if (mtpt) 206 strncpy(mtpt, mnt->mnt_dir, mtlen); 207 /* 208 * Check to see if we're referring to the root filesystem. 209 * If so, do a manual check to see if we can open /etc/mtab 210 * read/write, since if the root is mounted read/only, the 211 * contents of /etc/mtab may not be accurate. 212 */ 213 if (!strcmp(mnt->mnt_dir, "/")) { 214 is_root: 215 #define TEST_FILE "/.ismount-test-file" 216 *mount_flags |= EXT2_MF_ISROOT; 217 fd = open(TEST_FILE, O_RDWR|O_CREAT, 0600); 218 if (fd < 0) { 219 if (errno == EROFS) 220 *mount_flags |= EXT2_MF_READONLY; 221 } else 222 close(fd); 223 (void) unlink(TEST_FILE); 224 } 225 retval = 0; 226 errout: 227 endmntent (f); 228 return retval; 229 } 230 231 static errcode_t check_mntent(const char *file, int *mount_flags, 232 char *mtpt, int mtlen) 233 { 234 errcode_t retval; 235 236 #ifdef DEBUG 237 retval = check_mntent_file("/tmp/mtab", file, mount_flags, 238 mtpt, mtlen); 239 if (retval == 0) 240 return 0; 241 #endif /* DEBUG */ 242 #ifdef __linux__ 243 retval = check_mntent_file("/proc/mounts", file, mount_flags, 244 mtpt, mtlen); 245 if (retval == 0 && (*mount_flags != 0)) 246 return 0; 247 #endif /* __linux__ */ 248 #if defined(MOUNTED) || defined(_PATH_MOUNTED) 249 #ifndef MOUNTED 250 #define MOUNTED _PATH_MOUNTED 251 #endif /* MOUNTED */ 252 retval = check_mntent_file(MOUNTED, file, mount_flags, mtpt, mtlen); 253 return retval; 254 #else 255 *mount_flags = 0; 256 return 0; 257 #endif /* defined(MOUNTED) || defined(_PATH_MOUNTED) */ 258 } 259 260 #else 261 #if defined(HAVE_GETMNTINFO) 262 263 static errcode_t check_getmntinfo(const char *file, int *mount_flags, 264 char *mtpt, int mtlen) 265 { 266 struct statfs *mp; 267 int len, n; 268 const char *s1; 269 char *s2; 270 271 n = getmntinfo(&mp, MNT_NOWAIT); 272 if (n == 0) 273 return errno; 274 275 len = sizeof(_PATH_DEV) - 1; 276 s1 = file; 277 if (strncmp(_PATH_DEV, s1, len) == 0) 278 s1 += len; 279 280 *mount_flags = 0; 281 while (--n >= 0) { 282 s2 = mp->f_mntfromname; 283 if (strncmp(_PATH_DEV, s2, len) == 0) { 284 s2 += len - 1; 285 *s2 = 'r'; 286 } 287 if (strcmp(s1, s2) == 0 || strcmp(s1, &s2[1]) == 0) { 288 *mount_flags = EXT2_MF_MOUNTED; 289 break; 290 } 291 ++mp; 292 } 293 if (mtpt) 294 strncpy(mtpt, mp->f_mntonname, mtlen); 295 return 0; 296 } 297 #endif /* HAVE_GETMNTINFO */ 298 #endif /* HAVE_SETMNTENT */ 299 300 /* 301 * Check to see if we're dealing with the swap device. 302 */ 303 static int is_swap_device(const char *file) 304 { 305 FILE *f; 306 char buf[1024], *cp; 307 dev_t file_dev; 308 struct stat st_buf; 309 int ret = 0; 310 311 file_dev = 0; 312 #ifndef __GNU__ /* The GNU hurd is broken with respect to stat devices */ 313 if ((stat(file, &st_buf) == 0) && 314 ext2fsP_is_disk_device(st_buf.st_mode)) 315 file_dev = st_buf.st_rdev; 316 #endif /* __GNU__ */ 317 318 if (!(f = fopen("/proc/swaps", "r"))) 319 return 0; 320 /* Skip the first line */ 321 if (!fgets(buf, sizeof(buf), f)) 322 goto leave; 323 if (*buf && strncmp(buf, "Filename\t", 9)) 324 /* Linux <=2.6.19 contained a bug in the /proc/swaps 325 * code where the header would not be displayed 326 */ 327 goto valid_first_line; 328 329 while (fgets(buf, sizeof(buf), f)) { 330 valid_first_line: 331 if ((cp = strchr(buf, ' ')) != NULL) 332 *cp = 0; 333 if ((cp = strchr(buf, '\t')) != NULL) 334 *cp = 0; 335 if (strcmp(buf, file) == 0) { 336 ret++; 337 break; 338 } 339 #ifndef __GNU__ 340 if (file_dev && (stat(buf, &st_buf) == 0) && 341 ext2fsP_is_disk_device(st_buf.st_mode) && 342 file_dev == st_buf.st_rdev) { 343 ret++; 344 break; 345 } 346 #endif /* __GNU__ */ 347 } 348 349 leave: 350 fclose(f); 351 return ret; 352 } 353 354 355 /* 356 * ext2fs_check_mount_point() fills determines if the device is 357 * mounted or otherwise busy, and fills in mount_flags with one or 358 * more of the following flags: EXT2_MF_MOUNTED, EXT2_MF_ISROOT, 359 * EXT2_MF_READONLY, EXT2_MF_SWAP, and EXT2_MF_BUSY. If mtpt is 360 * non-NULL, the directory where the device is mounted is copied to 361 * where mtpt is pointing, up to mtlen characters. 362 */ 363 #ifdef __TURBOC__ 364 #pragma argsused 365 #endif 366 errcode_t ext2fs_check_mount_point(const char *device, int *mount_flags, 367 char *mtpt, int mtlen) 368 { 369 errcode_t retval = 0; 370 371 if (getenv("EXT2FS_PRETEND_RO_MOUNT")) { 372 *mount_flags = EXT2_MF_MOUNTED | EXT2_MF_READONLY; 373 if (getenv("EXT2FS_PRETEND_ROOTFS")) 374 *mount_flags = EXT2_MF_ISROOT; 375 return 0; 376 } 377 if (getenv("EXT2FS_PRETEND_RW_MOUNT")) { 378 *mount_flags = EXT2_MF_MOUNTED; 379 if (getenv("EXT2FS_PRETEND_ROOTFS")) 380 *mount_flags = EXT2_MF_ISROOT; 381 return 0; 382 } 383 384 if (is_swap_device(device)) { 385 *mount_flags = EXT2_MF_MOUNTED | EXT2_MF_SWAP; 386 strncpy(mtpt, "<swap>", mtlen); 387 } else { 388 #ifdef HAVE_SETMNTENT 389 retval = check_mntent(device, mount_flags, mtpt, mtlen); 390 #else 391 #ifdef HAVE_GETMNTINFO 392 retval = check_getmntinfo(device, mount_flags, mtpt, mtlen); 393 #else 394 #ifdef __GNUC__ 395 #warning "Can't use getmntent or getmntinfo to check for mounted filesystems!" 396 #endif 397 *mount_flags = 0; 398 #endif /* HAVE_GETMNTINFO */ 399 #endif /* HAVE_SETMNTENT */ 400 } 401 if (retval) 402 return retval; 403 404 #ifdef __linux__ /* This only works on Linux 2.6+ systems */ 405 { 406 struct stat st_buf; 407 408 if (stat(device, &st_buf) == 0 && 409 ext2fsP_is_disk_device(st_buf.st_mode)) { 410 int fd = open(device, O_RDONLY | O_EXCL); 411 412 if (fd >= 0) 413 close(fd); 414 else if (errno == EBUSY) 415 *mount_flags |= EXT2_MF_BUSY; 416 } 417 } 418 #endif 419 420 return 0; 421 } 422 423 /* 424 * ext2fs_check_if_mounted() sets the mount_flags EXT2_MF_MOUNTED, 425 * EXT2_MF_READONLY, and EXT2_MF_ROOT 426 * 427 */ 428 errcode_t ext2fs_check_if_mounted(const char *file, int *mount_flags) 429 { 430 return ext2fs_check_mount_point(file, mount_flags, NULL, 0); 431 } 432 433 #ifdef DEBUG 434 int main(int argc, char **argv) 435 { 436 int retval, mount_flags; 437 char mntpt[80]; 438 439 if (argc < 2) { 440 fprintf(stderr, "Usage: %s device\n", argv[0]); 441 exit(1); 442 } 443 444 add_error_table(&et_ext2_error_table); 445 mntpt[0] = 0; 446 retval = ext2fs_check_mount_point(argv[1], &mount_flags, 447 mntpt, sizeof(mntpt)); 448 if (retval) { 449 com_err(argv[0], retval, 450 "while calling ext2fs_check_if_mounted"); 451 exit(1); 452 } 453 printf("Device %s reports flags %02x\n", argv[1], mount_flags); 454 if (mount_flags & EXT2_MF_BUSY) 455 printf("\t%s is apparently in use.\n", argv[1]); 456 if (mount_flags & EXT2_MF_MOUNTED) 457 printf("\t%s is mounted.\n", argv[1]); 458 if (mount_flags & EXT2_MF_SWAP) 459 printf("\t%s is a swap device.\n", argv[1]); 460 if (mount_flags & EXT2_MF_READONLY) 461 printf("\t%s is read-only.\n", argv[1]); 462 if (mount_flags & EXT2_MF_ISROOT) 463 printf("\t%s is the root filesystem.\n", argv[1]); 464 if (mntpt[0]) 465 printf("\t%s is mounted on %s.\n", argv[1], mntpt); 466 exit(0); 467 } 468 #endif /* DEBUG */ 469