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