]> git.ipfire.org Git - thirdparty/util-linux.git/blob - lib/ismounted.c
libblkid: do not interpret NTFS as MBR
[thirdparty/util-linux.git] / 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)
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))
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() fills 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);
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 */