1 /* fstype.c -- determine type of file systems that files are on
2    Copyright (C) 1990-2021 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
free_file_system_list(struct mount_entry * p)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
in_afs(char * path)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 *
get_file_system_list(bool need_fs_type)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 *
filesystem_type(const struct stat * statp,const char * path)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
is_used_fs_type(const char * name)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
set_fstype_devno(struct mount_entry * p)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 *
file_system_type_uncached(const struct stat * statp,const char * path,bool * fstype_known)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 *
get_mounted_devices(size_t * n)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 }
315