Imported Upstream version 4.5.10
[platform/upstream/findutils.git] / find / fstype.c
1 /* fstype.c -- determine type of file systems that files are on
2    Copyright (C) 1990, 1991, 1992, 1993, 1994, 2000,
3                  2004, 2010 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 3 of the License, or
7    (at your option) any later version.
8
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.
13
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 /* Written by David MacKenzie <djm@gnu.org>.
19  *
20  * Converted to use gnulib's read_file_system_list()
21  * by James Youngman <jay@gnu.org> (which saves a lot
22  * of manual hacking of configure.in).
23  */
24
25
26 #include <config.h>
27 #include <errno.h>
28 #include <stdbool.h>
29
30 #ifdef HAVE_SYS_TYPES_H
31 #include <sys/types.h>
32 #endif
33 #include <sys/stat.h>
34
35 /* The presence of unistd.h is assumed by gnulib these days, so we
36  * might as well assume it too.
37  */
38 #include <unistd.h>
39
40 #include <fcntl.h>
41 #ifdef HAVE_SYS_MNTIO_H
42 #include <sys/mntio.h>
43 #endif
44 #ifdef HAVE_SYS_MKDEV_H
45 #include <sys/mkdev.h>
46 #endif
47
48 #ifdef STDC_HEADERS
49 #include <stdlib.h>
50 #else
51 extern int errno;
52 #endif
53
54 #include "defs.h"
55 #include "../gnulib/lib/dirname.h"
56 #include "xalloc.h"
57
58 /* Need declaration of function `xstrtoumax' */
59 #include "../gnulib/lib/xstrtol.h"
60
61 #include "extendbuf.h"
62 #include "mountlist.h"
63 #include "error.h"
64
65
66
67 #if ENABLE_NLS
68 # include <libintl.h>
69 # define _(Text) gettext (Text)
70 #else
71 # define _(Text) Text
72 #endif
73 #ifdef gettext_noop
74 # define N_(String) gettext_noop (String)
75 #else
76 /* See locate.c for explanation as to why not use (String) */
77 # define N_(String) String
78 #endif
79
80 static char *file_system_type_uncached (const struct stat *statp, const char *path);
81
82
83 /* Get MNTTYPE_IGNORE if it is available. */
84 #if HAVE_MNTENT_H
85 # include <mntent.h>
86 #endif
87 #if HAVE_SYS_MNTTAB_H
88 # include <stdio.h>
89 # include <sys/mnttab.h>
90 #endif
91
92
93
94
95
96 static void
97 free_file_system_list (struct mount_entry *p)
98 {
99   while (p)
100     {
101       struct mount_entry *pnext = p->me_next;
102
103       free (p->me_devname);
104       free (p->me_mountdir);
105
106       if (p->me_type_malloced)
107         free (p->me_type);
108       p->me_next = NULL;
109       free (p);
110       p = pnext;
111     }
112 }
113
114
115
116
117 #ifdef AFS
118 #include <netinet/in.h>
119 #include <afs/venus.h>
120 #if __STDC__
121 /* On SunOS 4, afs/vice.h defines this to rely on a pre-ANSI cpp.  */
122 #undef _VICEIOCTL
123 #define _VICEIOCTL(id)  ((unsigned int ) _IOW('V', id, struct ViceIoctl))
124 #endif
125 #ifndef _IOW
126 /* AFS on Solaris 2.3 doesn't get this definition.  */
127 #include <sys/ioccom.h>
128 #endif
129
130 static int
131 in_afs (char *path)
132 {
133   static char space[2048];
134   struct ViceIoctl vi;
135
136   vi.in_size = 0;
137   vi.out_size = sizeof (space);
138   vi.out = space;
139
140   if (pioctl (path, VIOC_FILE_CELL_NAME, &vi, 1)
141       && (errno == EINVAL || errno == ENOENT))
142         return 0;
143   return 1;
144 }
145 #endif /* AFS */
146
147 /* Nonzero if the current file system's type is known.  */
148 static int fstype_known = 0;
149
150 /* Return a static string naming the type of file system that the file PATH,
151    described by STATP, is on.
152    RELPATH is the file name relative to the current directory.
153    Return "unknown" if its file system type is unknown.  */
154
155 char *
156 filesystem_type (const struct stat *statp, const char *path)
157 {
158   static char *current_fstype = NULL;
159   static dev_t current_dev;
160
161   if (current_fstype != NULL)
162     {
163       if (fstype_known && statp->st_dev == current_dev)
164         return current_fstype;  /* Cached value.  */
165       free (current_fstype);
166     }
167   current_dev = statp->st_dev;
168   current_fstype = file_system_type_uncached (statp, path);
169   return current_fstype;
170 }
171
172 static int
173 set_fstype_devno (struct mount_entry *p)
174 {
175   struct stat stbuf;
176
177   if (p->me_dev == (dev_t)-1)
178     {
179       set_stat_placeholders (&stbuf);
180       if (0 == (options.xstat)(p->me_mountdir, &stbuf))
181         {
182           p->me_dev = stbuf.st_dev;
183           return 0;
184         }
185       else
186         {
187           return -1;
188         }
189     }
190   return 0;                     /* not needed */
191 }
192
193 static struct mount_entry *
194 must_read_fs_list (bool need_fs_type)
195 {
196   struct mount_entry *entries = read_file_system_list (need_fs_type);
197   if (NULL == entries)
198     {
199       /* We cannot determine for sure which file we were trying to
200        * use because gnulib has abstracted all that stuff away.
201        * Hence we cannot issue a specific error message here.
202        */
203       error (EXIT_FAILURE, 0, _("Cannot read mounted file system list"));
204     }
205   return entries;
206 }
207
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 {
218   struct mount_entry *entries, *entry;
219   char *type;
220
221   (void) path;
222
223 #ifdef AFS
224   if (in_afs (path))
225     {
226       fstype_known = 1;
227       return xstrdup ("afs");
228     }
229 #endif
230
231   entries = must_read_fs_list (true);
232   for (type=NULL, entry=entries; entry; entry=entry->me_next)
233     {
234 #ifdef MNTTYPE_IGNORE
235       if (!strcmp (entry->me_type, MNTTYPE_IGNORE))
236         continue;
237 #endif
238       set_fstype_devno (entry);
239       if (entry->me_dev == statp->st_dev)
240         {
241           type = xstrdup (entry->me_type);
242           break;
243         }
244     }
245   free_file_system_list (entries);
246
247   /* Don't cache unknown values. */
248   fstype_known = (type != NULL);
249
250   return type ? type : xstrdup (_("unknown"));
251 }
252
253
254 char *
255 get_mounted_filesystems (void)
256 {
257   char *result = NULL;
258   size_t alloc_size = 0u;
259   size_t used = 0u;
260   struct mount_entry *entries, *entry;
261   void *p;
262
263   entries = must_read_fs_list (false);
264   for (entry=entries; entry; entry=entry->me_next)
265     {
266       size_t len;
267
268 #ifdef MNTTYPE_IGNORE
269       if (!strcmp (entry->me_type, MNTTYPE_IGNORE))
270         continue;
271 #endif
272       set_fstype_devno (entry);
273
274       len = strlen (entry->me_mountdir) + 1;
275       p = extendbuf (result, used+len, &alloc_size);
276       if (p)
277         {
278           result = p;
279           strcpy (&result[used], entry->me_mountdir);
280           used += len;          /* len already includes one for the \0 */
281         }
282       else
283         {
284           break;
285         }
286     }
287
288   free_file_system_list (entries);
289   return result;
290 }
291
292
293 dev_t *
294 get_mounted_devices (size_t *n)
295 {
296   size_t alloc_size = 0u;
297   size_t used = 0u;
298   struct mount_entry *entries, *entry;
299   dev_t *result = NULL;
300
301   /* Use read_file_system_list () rather than must_read_fs_list()
302    * because on some system this is always called at startup,
303    * and find should only exit fatally if it needs to use the
304    * result of this operation.   If we can't get the fs list
305    * but we never need the information, there is no need to fail.
306    */
307   for (entry = entries = read_file_system_list (false);
308        entry;
309        entry = entry->me_next)
310     {
311       void *p = extendbuf (result, sizeof(dev_t)*(used+1), &alloc_size);
312       if (p)
313         {
314           result = p;
315           set_fstype_devno (entry);
316           result[used] = entry->me_dev;
317           ++used;
318         }
319       else
320         {
321           free (result);
322           result = NULL;
323         }
324     }
325   free_file_system_list (entries);
326   if (result)
327     {
328       *n = used;
329     }
330   return result;
331 }