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