(root)/
findutils-4.9.0/
find/
fstype.c
       1  /* fstype.c -- determine type of file systems that files are on
       2     Copyright (C) 1990-2022 Free Software Foundation, Inc.
       3     This program is free software: you can redistribute it and/or modify
       4     it under the terms of the GNU General Public License as published by
       5     the Free Software Foundation, either version 3 of the License, or
       6     (at your option) any later version.
       7  
       8     This program is distributed in the hope that it will be useful,
       9     but WITHOUT ANY WARRANTY; without even the implied warranty of
      10     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      11     GNU General Public License for more details.
      12  
      13     You should have received a copy of the GNU General Public License
      14     along with this program.  If not, see <https://www.gnu.org/licenses/>.
      15  */
      16  
      17  /* Written by David MacKenzie <djm@gnu.org>.
      18   *
      19   * Converted to use gnulib's read_file_system_list()
      20   * by James Youngman <jay@gnu.org> (which saves a lot
      21   * of manual hacking of configure.in).
      22   */
      23  
      24  /* config.h must be included first. */
      25  #include <config.h>
      26  
      27  /* system headers. */
      28  #include <errno.h>
      29  #include <fcntl.h>
      30  #if HAVE_MNTENT_H
      31  # include <mntent.h>
      32  #endif
      33  #include <stdbool.h>
      34  #include <stdio.h>
      35  #include <stdlib.h>
      36  #ifdef HAVE_SYS_MKDEV_H
      37  # include <sys/mkdev.h>
      38  #endif
      39  #ifdef HAVE_SYS_MNTIO_H
      40  # include <sys/mntio.h>
      41  #endif
      42  #if HAVE_SYS_MNTTAB_H
      43  # include <sys/mnttab.h>
      44  #endif
      45  #include <sys/stat.h>
      46  #include <sys/types.h>
      47  #include <unistd.h>
      48  
      49  /* gnulib headers. */
      50  #include "dirname.h"
      51  #include "xalloc.h"
      52  #include "xstrtol.h"
      53  #include "mountlist.h"
      54  
      55  /* find headers. */
      56  #include "defs.h"
      57  #include "die.h"
      58  #include "extendbuf.h"
      59  #include "system.h"
      60  
      61  static char *file_system_type_uncached (const struct stat *statp,
      62                                          const char *path,
      63                                          bool *fstype_known);
      64  
      65  
      66  static void
      67  free_file_system_list (struct mount_entry *p)
      68  {
      69    while (p)
      70      {
      71        struct mount_entry *pnext = p->me_next;
      72        free_mount_entry (p);
      73        p = pnext;
      74      }
      75  }
      76  
      77  
      78  
      79  
      80  #ifdef AFS
      81  # include <netinet/in.h>
      82  # include <afs/venus.h>
      83  # if __STDC__
      84  /* On SunOS 4, afs/vice.h defines this to rely on a pre-ANSI cpp.  */
      85  #  undef _VICEIOCTL
      86  #  define _VICEIOCTL(id)  ((unsigned int ) _IOW('V', id, struct ViceIoctl))
      87  # endif
      88  # ifndef _IOW
      89  /* AFS on Solaris 2.3 doesn't get this definition.  */
      90  #  include <sys/ioccom.h>
      91  # endif
      92  
      93  static int
      94  in_afs (char *path)
      95  {
      96    static char space[2048];
      97    struct ViceIoctl vi;
      98  
      99    vi.in_size = 0;
     100    vi.out_size = sizeof (space);
     101    vi.out = space;
     102  
     103    if (pioctl (path, VIOC_FILE_CELL_NAME, &vi, 1)
     104        && (errno == EINVAL || errno == ENOENT))
     105  	return 0;
     106    return 1;
     107  }
     108  #endif /* AFS */
     109  
     110  /* Read the mount list into a static cache, and return it.
     111     This is a wrapper around gnulib's read_file_system_list ()
     112     to avoid unnecessary reading of the mount list.  */
     113  static struct mount_entry *
     114  get_file_system_list (bool need_fs_type)
     115  {
     116    /* Local cache for the mount list.  */
     117    static struct mount_entry *mount_list = NULL;
     118  
     119    /* Remember if the list contains the ME_TYPE members.  */
     120    static bool has_fstype = false;
     121  
     122    if (mount_list && ! has_fstype && need_fs_type)
     123      {
     124        free_file_system_list (mount_list);
     125        mount_list = NULL;
     126      }
     127    if (! mount_list)
     128      {
     129        mount_list = read_file_system_list(need_fs_type);
     130        has_fstype = need_fs_type;
     131      }
     132    return mount_list;
     133  }
     134  
     135  /* Return a static string naming the type of file system that the file PATH,
     136     described by STATP, is on.
     137     RELPATH is the file name relative to the current directory.
     138     Return "unknown" if its file system type is unknown.  */
     139  
     140  char *
     141  filesystem_type (const struct stat *statp, const char *path)
     142  {
     143    /* Nonzero if the current file system's type is known.  */
     144    static bool fstype_known = false;
     145  
     146    static char *current_fstype = NULL;
     147    static dev_t current_dev;
     148  
     149    if (current_fstype != NULL)
     150      {
     151        if (fstype_known && statp->st_dev == current_dev)
     152  	return current_fstype;	/* Cached value.  */
     153        free (current_fstype);
     154      }
     155    current_dev = statp->st_dev;
     156    current_fstype = file_system_type_uncached (statp, path, &fstype_known);
     157    return current_fstype;
     158  }
     159  
     160  bool
     161  is_used_fs_type(const char *name)
     162  {
     163    if (0 == strcmp("afs", name))
     164      {
     165        /* I guess AFS may not appear in /etc/mtab (or equivalent) but still be in use,
     166  	 so assume we always need to check for AFS.  */
     167        return true;
     168      }
     169    else
     170      {
     171        const struct mount_entry *entries = get_file_system_list(false);
     172        if (entries)
     173  	{
     174  	  const struct mount_entry *entry;
     175  	  for (entry = entries; entry; entry = entry->me_next)
     176  	    {
     177  	      if (0 == strcmp(name, entry->me_type))
     178  		return true;
     179  	    }
     180  	}
     181        else
     182  	{
     183  	  return true;
     184  	}
     185      }
     186    return false;
     187  }
     188  
     189  static int
     190  set_fstype_devno (struct mount_entry *p)
     191  {
     192    struct stat stbuf;
     193  
     194    if (p->me_dev == (dev_t)-1)
     195      {
     196        set_stat_placeholders (&stbuf);
     197        if (0 == (options.xstat)(p->me_mountdir, &stbuf))
     198  	{
     199  	  p->me_dev = stbuf.st_dev;
     200  	  return 0;
     201  	}
     202        else
     203  	{
     204  	  return -1;
     205  	}
     206      }
     207    return 0;			/* not needed */
     208  }
     209  
     210  /* Return a newly allocated string naming the type of file system that the
     211     file PATH, described by STATP, is on.
     212     RELPATH is the file name relative to the current directory.
     213     Return "unknown" if its file system type is unknown.  */
     214  
     215  static char *
     216  file_system_type_uncached (const struct stat *statp, const char *path,
     217                             bool *fstype_known)
     218  {
     219    struct mount_entry *entries, *entry, *best;
     220    char *type;
     221  
     222    (void) path;
     223  
     224  #ifdef AFS
     225    if (in_afs (path))
     226      {
     227        *fstype_known = true;
     228        return xstrdup ("afs");
     229      }
     230  #endif
     231  
     232    best = NULL;
     233    entries = get_file_system_list (true);
     234    if (NULL == entries)
     235      {
     236        /* We cannot determine for sure which file we were trying to
     237         * use because gnulib has abstracted all that stuff away.
     238         * Hence we cannot issue a specific error message here.
     239         */
     240        die (EXIT_FAILURE, 0, _("Cannot read mounted file system list"));
     241      }
     242    for (type=NULL, entry=entries; entry; entry=entry->me_next)
     243      {
     244  #ifdef MNTTYPE_IGNORE
     245        if (!strcmp (entry->me_type, MNTTYPE_IGNORE))
     246  	continue;
     247  #endif
     248        if (0 == set_fstype_devno (entry))
     249  	{
     250  	  if (entry->me_dev == statp->st_dev)
     251  	    {
     252  	      best = entry;
     253  	      /* Don't exit the loop, because some systems (for
     254  		 example Linux-based systems in which /etc/mtab is a
     255  		 symlink to /proc/mounts) can have duplicate entries
     256  		 in the filesystem list.  This happens most frequently
     257  		 for /.
     258  	      */
     259  	    }
     260  	}
     261      }
     262    if (best)
     263      {
     264        type = xstrdup (best->me_type);
     265      }
     266  
     267    /* Don't cache unknown values. */
     268    *fstype_known = (type != NULL);
     269  
     270    return type ? type : xstrdup (_("unknown"));
     271  }
     272  
     273  
     274  dev_t *
     275  get_mounted_devices (size_t *n)
     276  {
     277    size_t alloc_size = 0u;
     278    size_t used = 0u;
     279    struct mount_entry *entries, *entry;
     280    dev_t *result = NULL;
     281  
     282    /* Ignore read_file_system_list () not returning a valid list
     283     * because on some system this is always called at startup,
     284     * and find should only exit fatally if it needs to use the
     285     * result of this operation.   If we can't get the fs list
     286     * but we never need the information, there is no need to fail.
     287     */
     288    for (entry = entries = read_file_system_list (false);
     289         entry;
     290         entry = entry->me_next)
     291      {
     292        void *p = extendbuf (result, sizeof(dev_t)*(used+1), &alloc_size);
     293        if (p)
     294  	{
     295  	  result = p;
     296  	  if (0 == set_fstype_devno (entry))
     297  	    {
     298  	      result[used] = entry->me_dev;
     299  	      ++used;
     300  	    }
     301  	}
     302        else
     303  	{
     304  	  free (result);
     305  	  result = NULL;
     306  	}
     307      }
     308    free_file_system_list (entries);
     309    if (result)
     310      {
     311        *n = used;
     312      }
     313    return result;
     314  }