(root)/
util-linux-2.39/
lib/
ismounted.c
       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>
      14  #ifdef HAVE_MNTENT_H
      15  #include <mntent.h>
      16  #endif
      17  #include <string.h>
      18  #include <sys/stat.h>
      19  #include <ctype.h>
      20  #include <sys/param.h>
      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
      29  #endif
      30  
      31  #include "pathnames.h"
      32  #include "strutils.h"
      33  #include "ismounted.h"
      34  #include "c.h"
      35  #ifdef __linux__
      36  # include "loopdev.h"
      37  #endif
      38  
      39  
      40  
      41  #ifdef HAVE_MNTENT_H
      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,
      48  				   int *mount_flags, char *mtpt, int mtlen)
      49  {
      50  	struct mntent	*mnt;
      51  	struct stat	st_buf;
      52  	int		retval = 0;
      53  	dev_t		file_dev=0, file_rdev=0;
      54  	ino_t		file_ino=0;
      55  	FILE		*f;
      56  	int		fd;
      57  
      58  	*mount_flags = 0;
      59  	if ((f = setmntent (mtab_file, "r")) == NULL)
      60  		return errno;
      61  
      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  	}
      72  
      73  	while ((mnt = getmntent (f)) != NULL) {
      74  		if (mnt->mnt_fsname[0] != '/')
      75  			continue;
      76  		if (strcmp(file, mnt->mnt_fsname) == 0)
      77  			break;
      78  		if (stat(mnt->mnt_fsname, &st_buf) != 0)
      79  			continue;
      80  
      81  		if (S_ISBLK(st_buf.st_mode)) {
      82  #ifndef __GNU__
      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
      89  			    && loopdev_is_used(mnt->mnt_fsname, file, 0, 0, 0))
      90  				break;
      91  #endif /* __linux__ */
      92  #endif	/* __GNU__ */
      93  		} else {
      94  			if (file_dev && ((file_dev == st_buf.st_dev) &&
      95  					 (file_ino == st_buf.st_ino)))
      96  				break;
      97  		}
      98  	}
      99  
     100  	if (mnt == NULL) {
     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  		 */
     109  		if (file_rdev && stat("/", &st_buf) == 0 &&
     110  		    st_buf.st_dev == file_rdev) {
     111  			*mount_flags = MF_MOUNTED;
     112  			if (mtpt)
     113  				xstrncpy(mtpt, "/", mtlen);
     114  			goto is_root;
     115  		}
     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  	 */
     126  	if (stat(mnt->mnt_dir, &st_buf) < 0) {
     127  		retval = errno;
     128  		if (retval == ENOENT) {
     129  #ifdef DEBUG
     130  			printf("Bogus entry in %s!  (%s does not exist)\n",
     131  			       mtab_file, mnt->mnt_dir);
     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",
     140  		       mtab_file, file, mnt->mnt_dir);
     141  #endif /* DEBUG */
     142  		goto errout;
     143  	}
     144  #endif /* __GNU__ */
     145  	*mount_flags = MF_MOUNTED;
     146  
     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)
     154  		xstrncpy(mtpt, mnt->mnt_dir, mtlen);
     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;
     165  		fd = open(TEST_FILE, O_RDWR|O_CREAT|O_CLOEXEC, 0600);
     166  		if (fd < 0) {
     167  			if (errno == EROFS)
     168  				*mount_flags |= MF_READONLY;
     169  		} else
     170  			close(fd);
     171  		(void) unlink(TEST_FILE);
     172  	}
     173  	retval = 0;
     174  errout:
     175  	endmntent (f);
     176  	return retval;
     177  }
     178  
     179  static int check_mntent(const char *file, int *mount_flags,
     180  			      char *mtpt, int mtlen)
     181  {
     182  	int	retval;
     183  
     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 */
     190  #ifdef __linux__
     191  	retval = check_mntent_file("/proc/mounts", file, mount_flags,
     192  				   mtpt, mtlen);
     193  	if (retval == 0 && (*mount_flags != 0))
     194  		return 0;
     195  	if (access("/proc/mounts", R_OK) == 0) {
     196  		*mount_flags = 0;
     197  		return retval;
     198  	}
     199  #endif /* __linux__ */
     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  	}
     245  	if (mtpt && n >= 0)
     246  		xstrncpy(mtpt, mp->f_mntonname, mtlen);
     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  
     270  	if (!(f = fopen("/proc/swaps", "r" UL_CLOEXECSTR)))
     271  		return 0;
     272  	/* Skip the first line */
     273  	if (!fgets(buf, sizeof(buf), f))
     274  		goto leave;
     275  	if (*buf && strncmp(buf, "Filename\t", 9) != 0)
     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  /*
     308   * check_mount_point() determines if the device is mounted or otherwise
     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  {
     320  	int	retval = 0;
     321  
     322  	if (is_swap_device(device)) {
     323  		*mount_flags = MF_MOUNTED | MF_SWAP;
     324  		if (mtpt && mtlen)
     325  			xstrncpy(mtpt, "[SWAP]", mtlen);
     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 */
     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;
     350  		fd = open(device, O_RDONLY|O_EXCL|O_CLOEXEC|O_NONBLOCK);
     351  		if (fd < 0) {
     352  			if (errno == EBUSY)
     353  				*mount_flags |= MF_BUSY;
     354  		} else
     355  			close(fd);
     356  	}
     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);
     368  	if (retval)
     369  		return 0;
     370  	return mount_flags & MF_MOUNTED;
     371  }
     372  
     373  #ifdef TEST_PROGRAM_ISMOUNTED
     374  int main(int argc, char **argv)
     375  {
     376  	int flags = 0;
     377  	char devname[PATH_MAX];
     378  
     379  	if (argc < 2) {
     380  		fprintf(stderr, "Usage: %s device\n", argv[0]);
     381  		return EXIT_FAILURE;
     382  	}
     383  
     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);
     390  		return EXIT_SUCCESS;
     391  	}
     392  
     393  	printf("not mounted\n");
     394  	return EXIT_FAILURE;
     395  }
     396  #endif /* DEBUG */