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