If stat() fails with ELOOP, we issue a diagnostic message.
[findutils.git] / find / fstype.c
blob3ca910968ed1f02f0e9b2dbf7799f95bfba56daf
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_TYPES_H
27 #include <sys/types.h>
28 #endif
30 #ifdef HAVE_UNISTD_H
31 #include <unistd.h>
32 #endif
34 #ifdef HAVE_SYS_MNTIO_H
35 #ifdef HAVE_FCNTL_H
36 #include <fcntl.h>
37 #endif
38 #include <sys/mntio.h>
39 #endif
40 #ifdef HAVE_SYS_MKDEV_H
41 #include <sys/mkdev.h>
42 #endif
44 #if defined(MNTIOC_NMNTS) && defined(MNTIOC_GETDEVLIST)
45 #define USE_MNTIOC_GETDEVLIST 1
46 #else
47 #undef USE_MNTIOC_GETDEVLIST
48 #endif
51 #ifdef STDC_HEADERS
52 #include <stdlib.h>
53 #else
54 extern int errno;
55 #endif
57 #include "defs.h"
58 #include "../gnulib/lib/dirname.h"
59 #include "xalloc.h"
60 #include "modetype.h"
62 /* Need declaration of function `xstrtoumax' */
63 #include "../gnulib/lib/xstrtol.h"
65 #include "extendbuf.h"
68 #if ENABLE_NLS
69 # include <libintl.h>
70 # define _(Text) gettext (Text)
71 #else
72 # define _(Text) Text
73 #endif
74 #ifdef gettext_noop
75 # define N_(String) gettext_noop (String)
76 #else
77 /* See locate.c for explanation as to why not use (String) */
78 # define N_(String) String
79 #endif
81 static char *filesystem_type_uncached PARAMS((const char *path, const char *relpath, const struct stat *statp));
83 #if defined(FSTYPE_MNTENT) || defined(HAVE_GETMNTENT) || defined(HAVE_SYS_MNTTAB_H) /* 4.3BSD, SunOS, HP-UX, Dynix, Irix. */
85 #if HAVE_MNTENT_H
86 # include <mntent.h>
87 #endif
89 #if HAVE_SYS_MNTTAB_H
90 # include <stdio.h>
91 # include <sys/mnttab.h>
92 #endif
94 #if HAVE_STRUCT_MNTTAB_MNT_MOUNTP
95 #define mnt_dir mnt_mountp
96 #endif
99 #if !defined(MOUNTED)
100 # if defined(MNT_MNTTAB) /* HP-UX. */
101 # define MOUNTED MNT_MNTTAB
102 # endif
103 # if defined(MNTTABNAME) /* Dynix. */
104 # define MOUNTED MNTTABNAME
105 # endif
106 # if defined(MNTTAB) /* Solaris. */
107 # define MOUNTED MNTTAB
108 # endif
109 #endif
111 #if !defined(MOUNTED) /* last resort. */
112 # define MOUNTED "/etc/mtab"
113 #endif
116 #if HAVE_SETMNTENT
117 #define SETMNTENT(name,mode) setmntent(name,mode)
118 #else
119 #define SETMNTENT(name,mode) fopen(name,mode)
120 #endif
122 #if HAVE_ENDMNTENT
123 #define ENDMNTENT(fp) (0 != endmntent(fp))
124 #else
125 #define ENDMNTENT(fp) (0 == fclose(fp))
126 #endif
127 #endif
129 #ifdef FSTYPE_GETMNT /* Ultrix. */
130 #include <sys/param.h>
131 #include <sys/mount.h>
132 #include <sys/fs_types.h>
133 #endif
135 #ifdef FSTYPE_USG_STATFS /* SVR3. */
136 #include <sys/statfs.h>
137 #include <sys/fstyp.h>
138 #endif
140 #ifdef FSTYPE_STATVFS /* SVR4. */
141 #include <sys/statvfs.h>
142 #include <sys/fstyp.h>
143 #endif
145 #ifdef FSTYPE_STATFS /* 4.4BSD. */
146 #include <sys/param.h> /* NetBSD needs this. */
147 #include <sys/mount.h>
150 #include "xstrtol.h" /* for xstrtoumax(). */
153 #ifndef HAVE_F_FSTYPENAME_IN_STATFS
154 #ifndef MFSNAMELEN /* NetBSD defines this. */
155 static char *
156 fstype_to_string (t)
157 short t;
159 #ifdef INITMOUNTNAMES /* Defined in 4.4BSD, not in NET/2. */
160 static char *mn[] = INITMOUNTNAMES;
161 if (t >= 0 && t <= MOUNT_MAXTYPE)
162 return mn[t];
163 else
164 return "?";
165 #else /* !INITMOUNTNAMES */
166 switch (t)
168 case MOUNT_UFS:
169 return "ufs";
170 case MOUNT_NFS:
171 return "nfs";
172 #ifdef MOUNT_PC
173 case MOUNT_PC:
174 return "pc";
175 #endif
176 #ifdef MOUNT_MFS
177 case MOUNT_MFS:
178 return "mfs";
179 #endif
180 #ifdef MOUNT_LO
181 case MOUNT_LO:
182 return "lofs";
183 #endif
184 #ifdef MOUNT_TFS
185 case MOUNT_TFS:
186 return "tfs";
187 #endif
188 #ifdef MOUNT_TMP
189 case MOUNT_TMP:
190 return "tmp";
191 #endif
192 #ifdef MOUNT_MSDOS
193 case MOUNT_MSDOS:
194 return "msdos";
195 #endif
196 #ifdef MOUNT_ISO9660
197 case MOUNT_ISO9660:
198 return "iso9660fs";
199 #endif
200 default:
201 return "?";
203 #endif /* !INITMOUNTNAMES */
205 #endif /* !MFSNAMELEN */
206 #endif /* !HAVE_F_FSTYPENAME_IN_STATFS */
207 #endif /* FSTYPE_STATFS */
209 #ifdef FSTYPE_AIX_STATFS /* AIX. */
210 #include <sys/vmount.h>
211 #include <sys/statfs.h>
213 #define FSTYPE_STATFS /* Otherwise like 4.4BSD. */
214 #define f_type f_vfstype
216 static char *
217 fstype_to_string (t)
218 short t;
220 switch (t)
222 case MNT_AIX:
223 #if 0 /* NFS filesystems are actually MNT_AIX. */
224 return "aix";
225 #endif
226 case MNT_NFS:
227 return "nfs";
228 case MNT_JFS:
229 return "jfs";
230 case MNT_CDROM:
231 return "cdrom";
232 default:
233 return "?";
236 #endif /* FSTYPE_AIX_STATFS */
238 #ifdef AFS
239 #include <netinet/in.h>
240 #include <afs/venus.h>
241 #if __STDC__
242 /* On SunOS 4, afs/vice.h defines this to rely on a pre-ANSI cpp. */
243 #undef _VICEIOCTL
244 #define _VICEIOCTL(id) ((unsigned int ) _IOW('V', id, struct ViceIoctl))
245 #endif
246 #ifndef _IOW
247 /* AFS on Solaris 2.3 doesn't get this definition. */
248 #include <sys/ioccom.h>
249 #endif
251 static int
252 in_afs (path)
253 char *path;
255 static char space[2048];
256 struct ViceIoctl vi;
258 vi.in_size = 0;
259 vi.out_size = sizeof (space);
260 vi.out = space;
262 if (pioctl (path, VIOC_FILE_CELL_NAME, &vi, 1)
263 && (errno == EINVAL || errno == ENOENT))
264 return 0;
265 return 1;
267 #endif /* AFS */
269 /* Nonzero if the current filesystem's type is known. */
270 static int fstype_known = 0;
272 /* Return a static string naming the type of filesystem that the file PATH,
273 described by STATP, is on.
274 RELPATH is the file name relative to the current directory.
275 Return "unknown" if its filesystem type is unknown. */
277 char *
278 filesystem_type (const char *path, const char *relpath, const struct stat *statp)
280 static char *current_fstype = NULL;
281 static dev_t current_dev;
283 if (current_fstype != NULL)
285 if (fstype_known && statp->st_dev == current_dev)
286 return current_fstype; /* Cached value. */
287 free (current_fstype);
289 current_dev = statp->st_dev;
290 current_fstype = filesystem_type_uncached (path, relpath, statp);
291 return current_fstype;
294 /* Return a newly allocated string naming the type of filesystem that the
295 file PATH, described by STATP, is on.
296 RELPATH is the file name relative to the current directory.
297 Return "unknown" if its filesystem type is unknown. */
299 static char *
300 filesystem_type_uncached (const char *path, const char *relpath, const struct stat *statp)
302 char *type = NULL;
304 #ifdef FSTYPE_MNTENT /* 4.3BSD, SunOS, HP-UX, Dynix, Irix. */
305 char *table = MOUNTED;
306 FILE *mfp;
307 struct mntent *mnt;
309 (void) &path;
310 (void) &relpath;
312 mfp = SETMNTENT (table, "r");
313 if (mfp == NULL)
314 error (1, errno, "%s", table);
316 /* Find the entry with the same device number as STATP, and return
317 that entry's fstype. */
318 while (type == NULL && (mnt = getmntent (mfp)))
320 char *devopt;
321 dev_t dev;
322 struct stat disk_stats;
324 #ifdef MNTTYPE_IGNORE
325 if (!strcmp (mnt->mnt_type, MNTTYPE_IGNORE))
326 continue;
327 #endif
329 /* Newer systems like SunOS 4.1 keep the dev number in the mtab,
330 in the options string. For older systems, we need to stat the
331 directory that the filesystem is mounted on to get it.
333 Unfortunately, the HPUX 9.x mnttab entries created by automountq
334 contain a dev= option but the option value does not match the
335 st_dev value of the file (maybe the lower 16 bits match?). */
337 #if !defined(hpux) && !defined(__hpux__)
338 devopt = strstr (mnt->mnt_opts, "dev=");
339 if (devopt)
341 uintmax_t u = 0;
342 devopt += 4;
343 if (devopt[0] == '0' && (devopt[1] == 'x' || devopt[1] == 'X'))
344 devopt += 2;
345 xstrtoumax (devopt, NULL, 16, &u, NULL);
346 dev = u;
348 else
349 #endif /* not hpux */
351 if (stat (mnt-> mnt_dir, &disk_stats) == -1) {
352 if (errno == EACCES)
353 continue;
354 else
355 error (1, errno, _("error in %s: %s"), table, mnt-> mnt_dir);
357 dev = disk_stats.st_dev;
360 if (dev == statp->st_dev)
361 type = mnt->mnt_type;
364 if (ENDMNTENT (mfp) == 0)
365 error (0, errno, "%s", table);
366 #endif
368 #ifdef FSTYPE_GETMNT /* Ultrix. */
369 int offset = 0;
370 struct fs_data fsd;
372 while (type == NULL
373 && getmnt (&offset, &fsd, sizeof (fsd), NOSTAT_MANY, 0) > 0)
375 if (fsd.fd_req.dev == statp->st_dev)
376 type = gt_names[fsd.fd_req.fstype];
378 #endif
380 #ifdef FSTYPE_USG_STATFS /* SVR3. */
381 struct statfs fss;
382 char typebuf[FSTYPSZ];
384 if (statfs (relpath, &fss, sizeof (struct statfs), 0) == -1)
386 /* Don't die if a file was just removed. */
387 if (errno != ENOENT)
388 error (1, errno, "%s", path);
390 else if (!sysfs (GETFSTYP, fss.f_fstyp, typebuf))
391 type = typebuf;
392 #endif
394 #ifdef FSTYPE_STATVFS /* SVR4. */
395 struct statvfs fss;
397 if (statvfs (relpath, &fss) == -1)
399 /* Don't die if a file was just removed. */
400 if (errno != ENOENT)
401 error (1, errno, "%s", path);
403 else
404 type = fss.f_basetype;
405 #endif
407 #ifdef FSTYPE_STATFS /* 4.4BSD. */
408 struct statfs fss;
409 char *p;
411 if (S_ISLNK (statp->st_mode))
412 p = dir_name (relpath);
413 else
414 p = relpath;
416 if (statfs (p, &fss) == -1)
418 /* Don't die if symlink to nonexisting file, or a file that was
419 just removed. */
420 if (errno != ENOENT)
421 error (1, errno, "%s", path);
423 else
425 #ifdef HAVE_F_FSTYPENAME_IN_STATFS
426 type = xstrdup (fss.f_fstypename);
427 #else
428 type = fstype_to_string (fss.f_type);
429 #endif
431 if (p != relpath)
432 free (p);
433 #endif
435 #ifdef AFS
436 if ((!type || !strcmp (type, "xx")) && in_afs (relpath))
437 type = "afs";
438 #endif
440 /* An unknown value can be caused by an ENOENT error condition.
441 Don't cache those values. */
442 fstype_known = (type != NULL);
444 return xstrdup (type ? type : _("unknown"));
451 #ifdef HAVE_GETMNTENT
453 #if HAVE_STRUCT_MNTTAB
454 typedef struct mnttab MountPointEntry;
455 #elif HAVE_STRUCT_MNTENT
456 typedef struct mntent MountPointEntry;
457 #endif
459 #if GETMNTENT_RETURNS_STRUCT
460 static MountPointEntry*
461 next_mount_point(FILE *fp)
463 return getmntent(fp);
466 #elif GETMNTENT_RETURNS_INT && GETMNTENT_REQUIRES_STRUCT_PTR
467 static MountPointEntry current_mount_point;
469 static MountPointEntry*
470 next_mount_point(FILE *fp)
472 int rv = getmntent(fp, &current_mount_point);
474 switch (rv)
476 case 0:
477 return &current_mount_point; /* success */
479 case -1: /* EOF - this is normal.*/
480 return NULL;
482 case MNT_TOOLONG:
483 error(0, 0, _("Line too long in `%s'"), MOUNTED);
484 return NULL;
486 case MNT_TOOMANY:
487 error(0, 0,
488 _("One of the lines in `%s' has too many fields"),
489 MOUNTED);
490 return NULL;
492 case MNT_TOOFEW:
493 error(0, 0,
494 _("One of the lines in `%s' has too few fields"),
495 MOUNTED);
496 return NULL;
498 default:
499 error(0, 0,
500 _("Failed to parse an entry in `%s'"),
501 MOUNTED);
502 return NULL;
505 #else
506 static MountPointEntry*
507 next_mount_point(FILE *fp)
509 if (warnings)
511 error(0, 0, _("Don't know how to use getmntent() to read `%s'. This is a bug."));
513 return NULL;
516 #endif
518 char *
519 get_mounted_filesystems (void)
521 char *table = MOUNTED;
522 FILE *mfp;
523 #if HAVE_STRUCT_MNTTAB
524 struct mnttab *mnt;
525 #elif HAVE_STRUCT_MNTENT
526 struct mntent *mnt;
527 #endif
528 char *result = NULL;
529 size_t alloc_size = 0u;
530 size_t used = 0u;
532 mfp = SETMNTENT(table, "r");
533 if (mfp == NULL)
534 error (1, errno, "%s", table);
536 while (NULL != (mnt = next_mount_point (mfp)))
538 size_t len;
540 #ifdef MNTTYPE_IGNORE
541 if (!strcmp (mnt->mnt_type, MNTTYPE_IGNORE))
542 continue;
543 #endif
545 len = strlen(mnt-> mnt_dir) + 1;
546 result = extendbuf(result, used+len, &alloc_size);
547 strcpy(&result[used], mnt->mnt_dir);
548 used += len; /* len already includes one for the \0 */
550 if (ENDMNTENT(mfp) == 0)
551 error (0, errno, "%s", table);
553 if (used)
555 /* Add the extra terminating \0 */
556 result = extendbuf(result, used+1, &alloc_size);
557 result[used] = 0;
559 else
561 assert(NULL == result); /* Postcondition. */
563 return result;
565 #else
566 char *
567 get_mounted_filesystems (void)
569 return NULL; /* No getmntent(). */
571 #endif
573 #ifdef USE_MNTIOC_GETDEVLIST
575 dev_t*
576 get_mounted_devices (size_t *n)
578 dev_t *result = NULL;
579 int i, fd;
581 /* Yes, we really are issuing an ioctl() against a vanilla file in order to
582 * find out what's in it.
584 if ( (fd = open(MOUNTED, O_RDONLY)) >= 0)
586 int nmnts = -1;
587 if (0 == ioctl(fd, MNTIOC_NMNTS, &nmnts))
589 uint32_t * devlist = (uint32_t*) xcalloc(2 * nmnts, sizeof(uint32_t));
590 result = xcalloc(nmnts, sizeof(dev_t));
592 if (0 == ioctl(fd, MNTIOC_GETDEVLIST, devlist))
594 for (i = 0; i < nmnts; ++i)
596 result[i] = makedev(devlist[2*i], devlist[2*i+1]);
598 free(devlist);
599 *n = nmnts;
600 return result;
603 close(fd);
605 error (1, errno, "%s", MOUNTED);
606 /*NOTREAHED*/
607 return 0;
610 #else
611 dev_t *
612 get_mounted_devices (size_t *n)
614 char *mountpoints = get_mounted_filesystems();
615 dev_t *result;
616 size_t alloc_size = 0u;
617 size_t used;
619 used = 0u;
620 result = NULL;
621 if (mountpoints)
623 const char *mountpoint = mountpoints;
624 while (*mountpoint)
626 struct stat st;
627 if (0 == lstat(mountpoint, &st))
629 result = extendbuf(result, sizeof(dev_t)*(used+1), &alloc_size);
630 result[used] = st.st_dev;
631 ++used;
633 else
635 if (errno == ENOENT || errno == EACCES)
637 /* ignore, carry on with the next. */
639 else
641 error (1, errno, "%s", mountpoint);
644 mountpoint += strlen(mountpoint);
645 ++mountpoint; /* skip the terminating NUL to find next entry. */
648 if (NULL != result)
649 result = xrealloc(result, sizeof(dev_t)*used);
652 *n = used;
653 return result;
655 #endif