When wd_sanity_check() fails, enumerate the mounted devices, rather than the mounted...
[findutils.git] / find / fstype.c
blob6f156a21927570762b9394dce7fe7eb43e7f195a
1 /* fstype.c -- determine type of filesystems that files are on
2 Copyright (C) 1990, 91, 92, 93, 94, 2000 Free Software Foundation, Inc.
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2, or (at your option)
7 any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
17 USA.
20 /* Written by David MacKenzie <djm@gnu.ai.mit.edu>. */
22 #include <config.h>
23 #include <errno.h>
24 #include <assert.h>
26 #ifdef HAVE_SYS_MNTIO_H
27 #include <sys/mntio.h>
28 #endif
29 #ifdef HAVE_SYS_MKDEV_H
30 #include <sys/mkdev.h>
31 #endif
33 #if defined(MNTIOC_NMNTS) && defined(MNTIOC_GETDEVLIST)
34 #define USE_MNTIOC_GETDEVLIST 1
35 #endif
38 #ifdef STDC_HEADERS
39 #include <stdlib.h>
40 #else
41 extern int errno;
42 #endif
44 #include "defs.h"
45 #include "../gnulib/lib/dirname.h"
46 #include "modetype.h"
48 /* Need declaration of function `xstrtoumax' */
49 #include "../gnulib/lib/xstrtol.h"
51 #include "extendbuf.h"
54 #if ENABLE_NLS
55 # include <libintl.h>
56 # define _(Text) gettext (Text)
57 #else
58 # define _(Text) Text
59 #endif
60 #ifdef gettext_noop
61 # define N_(String) gettext_noop (String)
62 #else
63 /* See locate.c for explanation as to why not use (String) */
64 # define N_(String) String
65 #endif
67 static char *filesystem_type_uncached PARAMS((const char *path, const char *relpath, const struct stat *statp));
69 #if defined(FSTYPE_MNTENT) || defined(HAVE_GETMNTENT) || defined(HAVE_SYS_MNTTAB_H) /* 4.3BSD, SunOS, HP-UX, Dynix, Irix. */
71 #if HAVE_MNTENT_H
72 # include <mntent.h>
73 #endif
75 #if HAVE_SYS_MNTTAB_H
76 # include <stdio.h>
77 # include <sys/mnttab.h>
78 #endif
80 #if HAVE_STRUCT_MNTTAB_MNT_MOUNTP
81 #define mnt_dir mnt_mountp
82 #endif
85 #if !defined(MOUNTED)
86 # if defined(MNT_MNTTAB) /* HP-UX. */
87 # define MOUNTED MNT_MNTTAB
88 # endif
89 # if defined(MNTTABNAME) /* Dynix. */
90 # define MOUNTED MNTTABNAME
91 # endif
92 # if defined(MNTTAB) /* Solaris. */
93 # define MOUNTED MNTTAB
94 # endif
95 #endif
97 #if !defined(MOUNTED) /* last resort. */
98 # define MOUNTED "/etc/mtab"
99 #endif
102 #if HAVE_SETMNTENT
103 #define SETMNTENT(name,mode) setmntent(name,mode)
104 #else
105 #define SETMNTENT(name,mode) fopen(name,mode)
106 #endif
108 #if HAVE_ENDMNTENT
109 #define ENDMNTENT(fp) (0 != endmntent(fp))
110 #else
111 #define ENDMNTENT(fp) (0 == fclose(fp))
112 #endif
113 #endif
115 #ifdef FSTYPE_GETMNT /* Ultrix. */
116 #include <sys/param.h>
117 #include <sys/mount.h>
118 #include <sys/fs_types.h>
119 #endif
121 #ifdef FSTYPE_USG_STATFS /* SVR3. */
122 #include <sys/statfs.h>
123 #include <sys/fstyp.h>
124 #endif
126 #ifdef FSTYPE_STATVFS /* SVR4. */
127 #include <sys/statvfs.h>
128 #include <sys/fstyp.h>
129 #endif
131 #ifdef FSTYPE_STATFS /* 4.4BSD. */
132 #include <sys/param.h> /* NetBSD needs this. */
133 #include <sys/mount.h>
136 #include "xstrtol.h" /* for xstrtoumax(). */
139 #ifndef HAVE_F_FSTYPENAME_IN_STATFS
140 #ifndef MFSNAMELEN /* NetBSD defines this. */
141 static char *
142 fstype_to_string (t)
143 short t;
145 #ifdef INITMOUNTNAMES /* Defined in 4.4BSD, not in NET/2. */
146 static char *mn[] = INITMOUNTNAMES;
147 if (t >= 0 && t <= MOUNT_MAXTYPE)
148 return mn[t];
149 else
150 return "?";
151 #else /* !INITMOUNTNAMES */
152 switch (t)
154 case MOUNT_UFS:
155 return "ufs";
156 case MOUNT_NFS:
157 return "nfs";
158 #ifdef MOUNT_PC
159 case MOUNT_PC:
160 return "pc";
161 #endif
162 #ifdef MOUNT_MFS
163 case MOUNT_MFS:
164 return "mfs";
165 #endif
166 #ifdef MOUNT_LO
167 case MOUNT_LO:
168 return "lofs";
169 #endif
170 #ifdef MOUNT_TFS
171 case MOUNT_TFS:
172 return "tfs";
173 #endif
174 #ifdef MOUNT_TMP
175 case MOUNT_TMP:
176 return "tmp";
177 #endif
178 #ifdef MOUNT_MSDOS
179 case MOUNT_MSDOS:
180 return "msdos";
181 #endif
182 #ifdef MOUNT_ISO9660
183 case MOUNT_ISO9660:
184 return "iso9660fs";
185 #endif
186 default:
187 return "?";
189 #endif /* !INITMOUNTNAMES */
191 #endif /* !MFSNAMELEN */
192 #endif /* !HAVE_F_FSTYPENAME_IN_STATFS */
193 #endif /* FSTYPE_STATFS */
195 #ifdef FSTYPE_AIX_STATFS /* AIX. */
196 #include <sys/vmount.h>
197 #include <sys/statfs.h>
199 #define FSTYPE_STATFS /* Otherwise like 4.4BSD. */
200 #define f_type f_vfstype
202 static char *
203 fstype_to_string (t)
204 short t;
206 switch (t)
208 case MNT_AIX:
209 #if 0 /* NFS filesystems are actually MNT_AIX. */
210 return "aix";
211 #endif
212 case MNT_NFS:
213 return "nfs";
214 case MNT_JFS:
215 return "jfs";
216 case MNT_CDROM:
217 return "cdrom";
218 default:
219 return "?";
222 #endif /* FSTYPE_AIX_STATFS */
224 #ifdef AFS
225 #include <netinet/in.h>
226 #include <afs/venus.h>
227 #if __STDC__
228 /* On SunOS 4, afs/vice.h defines this to rely on a pre-ANSI cpp. */
229 #undef _VICEIOCTL
230 #define _VICEIOCTL(id) ((unsigned int ) _IOW('V', id, struct ViceIoctl))
231 #endif
232 #ifndef _IOW
233 /* AFS on Solaris 2.3 doesn't get this definition. */
234 #include <sys/ioccom.h>
235 #endif
237 static int
238 in_afs (path)
239 char *path;
241 static char space[2048];
242 struct ViceIoctl vi;
244 vi.in_size = 0;
245 vi.out_size = sizeof (space);
246 vi.out = space;
248 if (pioctl (path, VIOC_FILE_CELL_NAME, &vi, 1)
249 && (errno == EINVAL || errno == ENOENT))
250 return 0;
251 return 1;
253 #endif /* AFS */
255 /* Nonzero if the current filesystem's type is known. */
256 static int fstype_known = 0;
258 /* Return a static string naming the type of filesystem that the file PATH,
259 described by STATP, is on.
260 RELPATH is the file name relative to the current directory.
261 Return "unknown" if its filesystem type is unknown. */
263 char *
264 filesystem_type (const char *path, const char *relpath, const struct stat *statp)
266 static char *current_fstype = NULL;
267 static dev_t current_dev;
269 if (current_fstype != NULL)
271 if (fstype_known && statp->st_dev == current_dev)
272 return current_fstype; /* Cached value. */
273 free (current_fstype);
275 current_dev = statp->st_dev;
276 current_fstype = filesystem_type_uncached (path, relpath, statp);
277 return current_fstype;
280 /* Return a newly allocated string naming the type of filesystem that the
281 file PATH, described by STATP, is on.
282 RELPATH is the file name relative to the current directory.
283 Return "unknown" if its filesystem type is unknown. */
285 static char *
286 filesystem_type_uncached (const char *path, const char *relpath, const struct stat *statp)
288 char *type = NULL;
290 #ifdef FSTYPE_MNTENT /* 4.3BSD, SunOS, HP-UX, Dynix, Irix. */
291 char *table = MOUNTED;
292 FILE *mfp;
293 struct mntent *mnt;
295 (void) &path;
296 (void) &relpath;
298 mfp = SETMNTENT (table, "r");
299 if (mfp == NULL)
300 error (1, errno, "%s", table);
302 /* Find the entry with the same device number as STATP, and return
303 that entry's fstype. */
304 while (type == NULL && (mnt = getmntent (mfp)))
306 char *devopt;
307 dev_t dev;
308 struct stat disk_stats;
310 #ifdef MNTTYPE_IGNORE
311 if (!strcmp (mnt->mnt_type, MNTTYPE_IGNORE))
312 continue;
313 #endif
315 /* Newer systems like SunOS 4.1 keep the dev number in the mtab,
316 in the options string. For older systems, we need to stat the
317 directory that the filesystem is mounted on to get it.
319 Unfortunately, the HPUX 9.x mnttab entries created by automountq
320 contain a dev= option but the option value does not match the
321 st_dev value of the file (maybe the lower 16 bits match?). */
323 #if !defined(hpux) && !defined(__hpux__)
324 devopt = strstr (mnt->mnt_opts, "dev=");
325 if (devopt)
327 uintmax_t u = 0;
328 devopt += 4;
329 if (devopt[0] == '0' && (devopt[1] == 'x' || devopt[1] == 'X'))
330 devopt += 2;
331 xstrtoumax (devopt, NULL, 16, &u, NULL);
332 dev = u;
334 else
335 #endif /* not hpux */
337 if (stat (mnt-> mnt_dir, &disk_stats) == -1) {
338 if (errno == EACCES)
339 continue;
340 else
341 error (1, errno, _("error in %s: %s"), table, mnt-> mnt_dir);
343 dev = disk_stats.st_dev;
346 if (dev == statp->st_dev)
347 type = mnt->mnt_type;
350 if (ENDMNTENT (mfp) == 0)
351 error (0, errno, "%s", table);
352 #endif
354 #ifdef FSTYPE_GETMNT /* Ultrix. */
355 int offset = 0;
356 struct fs_data fsd;
358 while (type == NULL
359 && getmnt (&offset, &fsd, sizeof (fsd), NOSTAT_MANY, 0) > 0)
361 if (fsd.fd_req.dev == statp->st_dev)
362 type = gt_names[fsd.fd_req.fstype];
364 #endif
366 #ifdef FSTYPE_USG_STATFS /* SVR3. */
367 struct statfs fss;
368 char typebuf[FSTYPSZ];
370 if (statfs (relpath, &fss, sizeof (struct statfs), 0) == -1)
372 /* Don't die if a file was just removed. */
373 if (errno != ENOENT)
374 error (1, errno, "%s", path);
376 else if (!sysfs (GETFSTYP, fss.f_fstyp, typebuf))
377 type = typebuf;
378 #endif
380 #ifdef FSTYPE_STATVFS /* SVR4. */
381 struct statvfs fss;
383 if (statvfs (relpath, &fss) == -1)
385 /* Don't die if a file was just removed. */
386 if (errno != ENOENT)
387 error (1, errno, "%s", path);
389 else
390 type = fss.f_basetype;
391 #endif
393 #ifdef FSTYPE_STATFS /* 4.4BSD. */
394 struct statfs fss;
395 char *p;
397 if (S_ISLNK (statp->st_mode))
398 p = dir_name (relpath);
399 else
400 p = relpath;
402 if (statfs (p, &fss) == -1)
404 /* Don't die if symlink to nonexisting file, or a file that was
405 just removed. */
406 if (errno != ENOENT)
407 error (1, errno, "%s", path);
409 else
411 #ifdef HAVE_F_FSTYPENAME_IN_STATFS
412 type = xstrdup (fss.f_fstypename);
413 #else
414 type = fstype_to_string (fss.f_type);
415 #endif
417 if (p != relpath)
418 free (p);
419 #endif
421 #ifdef AFS
422 if ((!type || !strcmp (type, "xx")) && in_afs (relpath))
423 type = "afs";
424 #endif
426 /* An unknown value can be caused by an ENOENT error condition.
427 Don't cache those values. */
428 fstype_known = (type != NULL);
430 return xstrdup (type ? type : _("unknown"));
437 #ifdef HAVE_GETMNTENT
439 #if HAVE_STRUCT_MNTTAB
440 typedef struct mnttab MountPointEntry;
441 #elif HAVE_STRUCT_MNTENT
442 typedef struct mntent MountPointEntry;
443 #endif
445 #if GETMNTENT_RETURNS_STRUCT
446 static MountPointEntry*
447 next_mount_point(FILE *fp)
449 return getmntent(fp);
452 #elif GETMNTENT_RETURNS_INT && GETMNTENT_REQUIRES_STRUCT_PTR
453 static MountPointEntry current_mount_point;
455 static MountPointEntry*
456 next_mount_point(FILE *fp)
458 int rv = getmntent(fp, &current_mount_point);
460 switch (rv)
462 case 0:
463 return &current_mount_point; /* success */
465 case -1: /* EOF - this is normal.*/
466 return NULL;
468 case MNT_TOOLONG:
469 error(0, 0, _("Line too long in `%s'"), MOUNTED);
470 return NULL;
472 case MNT_TOOMANY:
473 error(0, 0,
474 _("One of the lines in `%s' has too many fields"),
475 MOUNTED);
476 return NULL;
478 case MNT_TOOFEW:
479 error(0, 0,
480 _("One of the lines in `%s' has too few fields"),
481 MOUNTED);
482 return NULL;
484 default:
485 error(0, 0,
486 _("Failed to parse an entry in `%s'"),
487 MOUNTED);
488 return NULL;
491 #else
492 static MountPointEntry*
493 next_mount_point(FILE *fp)
495 if (warnings)
497 error(0, 0, _("Don't know how to use getmntent() to read `%s'. This is a bug."));
499 return NULL;
502 #endif
504 char *
505 get_mounted_filesystems (void)
507 char *table = MOUNTED;
508 FILE *mfp;
509 #if HAVE_STRUCT_MNTTAB
510 struct mnttab *mnt;
511 #elif HAVE_STRUCT_MNTENT
512 struct mntent *mnt;
513 #endif
514 char *result = NULL;
515 size_t alloc_size = 0u;
516 size_t used = 0u;
518 mfp = SETMNTENT(table, "r");
519 if (mfp == NULL)
520 error (1, errno, "%s", table);
522 while (NULL != (mnt = next_mount_point (mfp)))
524 size_t len;
526 #ifdef MNTTYPE_IGNORE
527 if (!strcmp (mnt->mnt_type, MNTTYPE_IGNORE))
528 continue;
529 #endif
531 len = strlen(mnt-> mnt_dir) + 1;
532 result = extendbuf(result, used+len, &alloc_size);
533 strcpy(&result[used], mnt->mnt_dir);
534 used += len; /* len already includes one for the \0 */
536 if (ENDMNTENT(mfp) == 0)
537 error (0, errno, "%s", table);
539 if (used)
541 /* Add the extra terminating \0 */
542 result = extendbuf(result, used+1, &alloc_size);
543 result[used] = 0;
545 else
547 assert(NULL == result); /* Postcondition. */
549 return result;
551 #else
552 char *
553 get_mounted_filesystems (void)
555 return NULL; /* No getmntent(). */
557 #endif
559 #ifdef USE_MNTIOC_GETDEVLIST
561 dev_t*
562 get_mounted_devices (size_t *n)
564 dev_t *result = NULL;
565 int i, fd;
567 /* Yes, we really are issuing an ioctl() against a vanilla file in order to
568 * find out what's in it.
570 if ( (fd = open(MOUNTED, O_RDONLY)) >= 0)
572 int nmnts = -1;
573 if (0 == ioctl(fd, MNTIOC_NMNTS, &nmnts))
575 uint32_t * devlist = (uint32_t*) xcalloc(2 * nmnts, sizeof(uint32_t));
576 result = xcalloc(nmnts, sizeof(dev_t));
578 if (0 == ioctl(fd, MNTIOC_GETDEVLIST, devlist))
580 printf("fd=%d nmnts=%d\n", fd, nmnts);
581 for (i = 0; i < nmnts; ++i)
583 result[i] = makedev(devlist[2*i], devlist[2*i+1]);
585 free(devlist);
586 *n = nmnts;
587 return result;
591 error (1, errno, "%s", MOUNTED);
592 /*NOTREAHED*/
593 return 0;
596 #else
597 dev_t *
598 get_mounted_devices (size_t *n)
600 char *mountpoints = get_mounted_filesystems();
601 dev_t *result;
602 size_t alloc_size = 0u;
603 size_t used;
605 used = 0u;
606 result = NULL;
607 if (mountpoints)
609 const char *mountpoint = mountpoints;
610 while (*mountpoint)
612 struct stat st;
613 if (0 == lstat(mountpoint, &st))
615 result = extendbuf(result, sizeof(dev_t)*(used+1), &alloc_size);
616 result[used] = st.st_dev;
617 ++used;
619 else
621 if (errno == ENOENT || errno == EACCES)
623 /* ignore, carry on with the next. */
625 else
627 error (1, errno, "%s", mountpoint);
630 mountpoint += strlen(mountpoint);
631 ++mountpoint; /* skip the terminating NUL to find next entry. */
634 if (NULL != result)
635 result = xrealloc(result, sizeof(dev_t)*used);
638 *n = used;
639 return result;
641 #endif