gsasl  1.10.0
About: GNU SASL is an implementation of the Simple Authentication and Security Layer (SASL). Development version.
  Fossies Dox: gsasl-1.10.0.tar.gz  ("unofficial" and yet experimental doxygen-generated source code documentation)  

stat.c
Go to the documentation of this file.
1 /* Work around platform bugs in stat.
2  Copyright (C) 2009-2021 Free Software Foundation, Inc.
3 
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 <https://www.gnu.org/licenses/>. */
16 
17 /* Written by Eric Blake and Bruno Haible. */
18 
19 /* If the user's config.h happens to include <sys/stat.h>, let it include only
20  the system's <sys/stat.h> here, so that orig_stat doesn't recurse to
21  rpl_stat. */
22 #define __need_system_sys_stat_h
23 #include <config.h>
24 
25 /* Get the original definition of stat. It might be defined as a macro. */
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #undef __need_system_sys_stat_h
29 
30 #if defined _WIN32 && ! defined __CYGWIN__
31 # define WINDOWS_NATIVE
32 #endif
33 
34 #if !defined WINDOWS_NATIVE
35 
36 static int
37 orig_stat (const char *filename, struct stat *buf)
38 {
39  return stat (filename, buf);
40 }
41 
42 #endif
43 
44 /* Specification. */
45 #ifdef __osf__
46 /* Write "sys/stat.h" here, not <sys/stat.h>, otherwise OSF/1 5.1 DTK cc
47  eliminates this include because of the preliminary #include <sys/stat.h>
48  above. */
49 # include "sys/stat.h"
50 #else
51 # include <sys/stat.h>
52 #endif
53 
54 #include "stat-time.h"
55 
56 #include <errno.h>
57 #include <limits.h>
58 #include <stdbool.h>
59 #include <string.h>
60 #include "filename.h"
61 #include "malloca.h"
62 #include "verify.h"
63 
64 #ifdef WINDOWS_NATIVE
65 # define WIN32_LEAN_AND_MEAN
66 # include <windows.h>
67 # include "stat-w32.h"
68 /* Don't assume that UNICODE is not defined. */
69 # undef WIN32_FIND_DATA
70 # define WIN32_FIND_DATA WIN32_FIND_DATAA
71 # undef CreateFile
72 # define CreateFile CreateFileA
73 # undef FindFirstFile
74 # define FindFirstFile FindFirstFileA
75 #endif
76 
77 #ifdef WINDOWS_NATIVE
78 /* Return TRUE if the given file name denotes an UNC root. */
79 static BOOL
80 is_unc_root (const char *rname)
81 {
82  /* Test whether it has the syntax '\\server\share'. */
83  if (ISSLASH (rname[0]) && ISSLASH (rname[1]))
84  {
85  /* It starts with two slashes. Find the next slash. */
86  const char *p = rname + 2;
87  const char *q = p;
88  while (*q != '\0' && !ISSLASH (*q))
89  q++;
90  if (q > p && *q != '\0')
91  {
92  /* Found the next slash at q. */
93  q++;
94  const char *r = q;
95  while (*r != '\0' && !ISSLASH (*r))
96  r++;
97  if (r > q && *r == '\0')
98  return TRUE;
99  }
100  }
101  return FALSE;
102 }
103 #endif
104 
105 /* Store information about NAME into ST. Work around bugs with
106  trailing slashes. Mingw has other bugs (such as st_ino always
107  being 0 on success) which this wrapper does not work around. But
108  at least this implementation provides the ability to emulate fchdir
109  correctly. */
110 
111 int
112 rpl_stat (char const *name, struct stat *buf)
113 {
114 #ifdef WINDOWS_NATIVE
115  /* Fill the fields ourselves, because the original stat function returns
116  values for st_atime, st_mtime, st_ctime that depend on the current time
117  zone. See
118  <https://lists.gnu.org/r/bug-gnulib/2017-04/msg00134.html> */
119  /* XXX Should we convert to wchar_t* and prepend '\\?\', in order to work
120  around length limitations
121  <https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-file> ? */
122 
123  /* POSIX <https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_13>
124  specifies: "More than two leading <slash> characters shall be treated as
125  a single <slash> character." */
126  if (ISSLASH (name[0]) && ISSLASH (name[1]) && ISSLASH (name[2]))
127  {
128  name += 2;
129  while (ISSLASH (name[1]))
130  name++;
131  }
132 
133  size_t len = strlen (name);
134  size_t drive_prefix_len = (HAS_DEVICE (name) ? 2 : 0);
135 
136  /* Remove trailing slashes (except the very first one, at position
137  drive_prefix_len), but remember their presence. */
138  size_t rlen;
139  bool check_dir = false;
140 
141  rlen = len;
142  while (rlen > drive_prefix_len && ISSLASH (name[rlen-1]))
143  {
144  check_dir = true;
145  if (rlen == drive_prefix_len + 1)
146  break;
147  rlen--;
148  }
149 
150  /* Handle '' and 'C:'. */
151  if (!check_dir && rlen == drive_prefix_len)
152  {
153  errno = ENOENT;
154  return -1;
155  }
156 
157  /* Handle '\\'. */
158  if (rlen == 1 && ISSLASH (name[0]) && len >= 2)
159  {
160  errno = ENOENT;
161  return -1;
162  }
163 
164  const char *rname;
165  char *malloca_rname;
166  if (rlen == len)
167  {
168  rname = name;
169  malloca_rname = NULL;
170  }
171  else
172  {
173  malloca_rname = malloca (rlen + 1);
174  if (malloca_rname == NULL)
175  {
176  errno = ENOMEM;
177  return -1;
178  }
179  memcpy (malloca_rname, name, rlen);
180  malloca_rname[rlen] = '\0';
181  rname = malloca_rname;
182  }
183 
184  /* There are two ways to get at the requested information:
185  - by scanning the parent directory and examining the relevant
186  directory entry,
187  - by opening the file directly.
188  The first approach fails for root directories (e.g. 'C:\') and
189  UNC root directories (e.g. '\\server\share').
190  The second approach fails for some system files (e.g. 'C:\pagefile.sys'
191  and 'C:\hiberfil.sys'): ERROR_SHARING_VIOLATION.
192  The second approach gives more information (in particular, correct
193  st_dev, st_ino, st_nlink fields).
194  So we use the second approach and, as a fallback except for root and
195  UNC root directories, also the first approach. */
196  {
197  int ret;
198 
199  {
200  /* Approach based on the file. */
201 
202  /* Open a handle to the file.
203  CreateFile
204  <https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-createfilea>
205  <https://docs.microsoft.com/en-us/windows/desktop/FileIO/creating-and-opening-files> */
206  HANDLE h =
207  CreateFile (rname,
208  FILE_READ_ATTRIBUTES,
209  FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
210  NULL,
211  OPEN_EXISTING,
212  /* FILE_FLAG_POSIX_SEMANTICS (treat file names that differ only
213  in case as different) makes sense only when applied to *all*
214  filesystem operations. */
215  FILE_FLAG_BACKUP_SEMANTICS /* | FILE_FLAG_POSIX_SEMANTICS */,
216  NULL);
217  if (h != INVALID_HANDLE_VALUE)
218  {
219  ret = _gl_fstat_by_handle (h, rname, buf);
220  CloseHandle (h);
221  goto done;
222  }
223  }
224 
225  /* Test for root and UNC root directories. */
226  if ((rlen == drive_prefix_len + 1 && ISSLASH (rname[drive_prefix_len]))
227  || is_unc_root (rname))
228  goto failed;
229 
230  /* Fallback. */
231  {
232  /* Approach based on the directory entry. */
233 
234  if (strchr (rname, '?') != NULL || strchr (rname, '*') != NULL)
235  {
236  /* Other Windows API functions would fail with error
237  ERROR_INVALID_NAME. */
238  if (malloca_rname != NULL)
239  freea (malloca_rname);
240  errno = ENOENT;
241  return -1;
242  }
243 
244  /* Get the details about the directory entry. This can be done through
245  FindFirstFile
246  <https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-findfirstfilea>
247  <https://docs.microsoft.com/en-us/windows/desktop/api/minwinbase/ns-minwinbase-_win32_find_dataa>
248  or through
249  FindFirstFileEx with argument FindExInfoBasic
250  <https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-findfirstfileexa>
251  <https://docs.microsoft.com/en-us/windows/desktop/api/minwinbase/ne-minwinbase-findex_info_levels>
252  <https://docs.microsoft.com/en-us/windows/desktop/api/minwinbase/ns-minwinbase-_win32_find_dataa> */
253  WIN32_FIND_DATA info;
254  HANDLE h = FindFirstFile (rname, &info);
255  if (h == INVALID_HANDLE_VALUE)
256  goto failed;
257 
258  /* Test for error conditions before starting to fill *buf. */
259  if (sizeof (buf->st_size) <= 4 && info.nFileSizeHigh > 0)
260  {
261  FindClose (h);
262  if (malloca_rname != NULL)
263  freea (malloca_rname);
264  errno = EOVERFLOW;
265  return -1;
266  }
267 
268 # if _GL_WINDOWS_STAT_INODES
269  buf->st_dev = 0;
270 # if _GL_WINDOWS_STAT_INODES == 2
271  buf->st_ino._gl_ino[0] = buf->st_ino._gl_ino[1] = 0;
272 # else /* _GL_WINDOWS_STAT_INODES == 1 */
273  buf->st_ino = 0;
274 # endif
275 # else
276  /* st_ino is not wide enough for identifying a file on a device.
277  Without st_ino, st_dev is pointless. */
278  buf->st_dev = 0;
279  buf->st_ino = 0;
280 # endif
281 
282  /* st_mode. */
283  unsigned int mode =
284  /* XXX How to handle FILE_ATTRIBUTE_REPARSE_POINT ? */
285  ((info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? _S_IFDIR | S_IEXEC_UGO : _S_IFREG)
286  | S_IREAD_UGO
287  | ((info.dwFileAttributes & FILE_ATTRIBUTE_READONLY) ? 0 : S_IWRITE_UGO);
288  if (!(info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
289  {
290  /* Determine whether the file is executable by looking at the file
291  name suffix. */
292  if (info.nFileSizeHigh > 0 || info.nFileSizeLow > 0)
293  {
294  const char *last_dot = NULL;
295  const char *p;
296  for (p = info.cFileName; *p != '\0'; p++)
297  if (*p == '.')
298  last_dot = p;
299  if (last_dot != NULL)
300  {
301  const char *suffix = last_dot + 1;
302  if (_stricmp (suffix, "exe") == 0
303  || _stricmp (suffix, "bat") == 0
304  || _stricmp (suffix, "cmd") == 0
305  || _stricmp (suffix, "com") == 0)
306  mode |= S_IEXEC_UGO;
307  }
308  }
309  }
310  buf->st_mode = mode;
311 
312  /* st_nlink. Ignore hard links here. */
313  buf->st_nlink = 1;
314 
315  /* There's no easy way to map the Windows SID concept to an integer. */
316  buf->st_uid = 0;
317  buf->st_gid = 0;
318 
319  /* st_rdev is irrelevant for normal files and directories. */
320  buf->st_rdev = 0;
321 
322  /* st_size. */
323  if (sizeof (buf->st_size) <= 4)
324  /* Range check already done above. */
325  buf->st_size = info.nFileSizeLow;
326  else
327  buf->st_size = ((long long) info.nFileSizeHigh << 32) | (long long) info.nFileSizeLow;
328 
329  /* st_atime, st_mtime, st_ctime. */
330 # if _GL_WINDOWS_STAT_TIMESPEC
331  buf->st_atim = _gl_convert_FILETIME_to_timespec (&info.ftLastAccessTime);
332  buf->st_mtim = _gl_convert_FILETIME_to_timespec (&info.ftLastWriteTime);
333  buf->st_ctim = _gl_convert_FILETIME_to_timespec (&info.ftCreationTime);
334 # else
335  buf->st_atime = _gl_convert_FILETIME_to_POSIX (&info.ftLastAccessTime);
336  buf->st_mtime = _gl_convert_FILETIME_to_POSIX (&info.ftLastWriteTime);
337  buf->st_ctime = _gl_convert_FILETIME_to_POSIX (&info.ftCreationTime);
338 # endif
339 
340  FindClose (h);
341 
342  ret = 0;
343  }
344 
345  done:
346  if (ret >= 0 && check_dir && !S_ISDIR (buf->st_mode))
347  {
348  errno = ENOTDIR;
349  ret = -1;
350  }
351  if (malloca_rname != NULL)
352  {
353  int saved_errno = errno;
354  freea (malloca_rname);
355  errno = saved_errno;
356  }
357  return ret;
358  }
359 
360  failed:
361  {
362  DWORD error = GetLastError ();
363  #if 0
364  fprintf (stderr, "rpl_stat error 0x%x\n", (unsigned int) error);
365  #endif
366 
367  if (malloca_rname != NULL)
368  freea (malloca_rname);
369 
370  switch (error)
371  {
372  /* Some of these errors probably cannot happen with the specific flags
373  that we pass to CreateFile. But who knows... */
374  case ERROR_FILE_NOT_FOUND: /* The last component of rname does not exist. */
375  case ERROR_PATH_NOT_FOUND: /* Some directory component in rname does not exist. */
376  case ERROR_BAD_PATHNAME: /* rname is such as '\\server'. */
377  case ERROR_BAD_NET_NAME: /* rname is such as '\\server\nonexistentshare'. */
378  case ERROR_INVALID_NAME: /* rname contains wildcards, misplaced colon, etc. */
379  case ERROR_DIRECTORY:
380  errno = ENOENT;
381  break;
382 
383  case ERROR_ACCESS_DENIED: /* rname is such as 'C:\System Volume Information\foo'. */
384  case ERROR_SHARING_VIOLATION: /* rname is such as 'C:\pagefile.sys' (second approach only). */
385  /* XXX map to EACCES or EPERM? */
386  errno = EACCES;
387  break;
388 
389  case ERROR_OUTOFMEMORY:
390  errno = ENOMEM;
391  break;
392 
393  case ERROR_WRITE_PROTECT:
394  errno = EROFS;
395  break;
396 
397  case ERROR_WRITE_FAULT:
398  case ERROR_READ_FAULT:
399  case ERROR_GEN_FAILURE:
400  errno = EIO;
401  break;
402 
403  case ERROR_BUFFER_OVERFLOW:
404  case ERROR_FILENAME_EXCED_RANGE:
405  errno = ENAMETOOLONG;
406  break;
407 
408  case ERROR_DELETE_PENDING: /* XXX map to EACCES or EPERM? */
409  errno = EPERM;
410  break;
411 
412  default:
413  errno = EINVAL;
414  break;
415  }
416 
417  return -1;
418  }
419 #else
420  int result = orig_stat (name, buf);
421  if (result == 0)
422  {
423 # if REPLACE_FUNC_STAT_FILE
424  /* Solaris 9 mistakenly succeeds when given a non-directory with a
425  trailing slash. */
426  if (!S_ISDIR (buf->st_mode))
427  {
428  size_t len = strlen (name);
429  if (ISSLASH (name[len - 1]))
430  {
431  errno = ENOTDIR;
432  return -1;
433  }
434  }
435 # endif /* REPLACE_FUNC_STAT_FILE */
436  result = stat_time_normalize (result, buf);
437  }
438  return result;
439 #endif
440 }
#define EOVERFLOW
Definition: errno.in.h:208
void error(int status, int errnum, const char *message,...)
Definition: error.c:295
#define HAS_DEVICE(Filename)
Definition: filename.h:94
#define ISSLASH(C)
Definition: filename.h:93
void freea(void *p)
Definition: malloca.c:83
#define malloca(N)
Definition: malloca.h:58
_GL_INLINE int stat_time_normalize(int result, struct stat *st _GL_UNUSED)
Definition: stat-time.h:211
time_t _gl_convert_FILETIME_to_POSIX(const FILETIME *ft)
#define S_IREAD_UGO
Definition: stat-w32.h:33
int _gl_fstat_by_handle(HANDLE h, const char *path, struct stat *buf)
#define S_IWRITE_UGO
Definition: stat-w32.h:34
#define S_IEXEC_UGO
Definition: stat-w32.h:35
static int orig_stat(const char *filename, struct stat *buf)
Definition: stat.c:37
int rpl_stat(char const *name, struct stat *buf)
Definition: stat.c:112
#define NULL
Definition: stddef.in.h:72
#define S_ISDIR(m)
Definition: sys_stat.in.h:195
const char * name
Definition: error.c:43
const char * p
Definition: mbrtowc-impl.h:42
char buf[4]
Definition: mbrtowc-impl.h:39