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