Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

418 lines
15 KiB

  1. /***
  2. *stat.c - get file status
  3. *
  4. * Copyright (c) 1985-2001, Microsoft Corporation. All rights reserved.
  5. *
  6. *Purpose:
  7. * defines _stat() - get file status
  8. *
  9. *Revision History:
  10. * 03-??-84 RLB Module created
  11. * 05-??-84 DCW Some cleanup and addition of register variables
  12. * 05-17-86 SKS Ported to OS/2
  13. * 11-19-86 SKS Better check for root directory; KANJI support
  14. * 05-22-87 SKS Cleaned up declarations and include files
  15. * 11-18-87 SKS Make _dtoxmode a static near procedure
  16. * 12-11-87 JCR Added "_LOAD_DS" to declaration
  17. * 12-21-87 WAJ stat no longer uses chdir to figure out if it has been
  18. * passed a root directory in the MTHREAD case.
  19. * 01-05-88 WAJ now uses _MAX_PATH (defined in stdlib.h)
  20. * 06-22-88 SKS find Hidden and System files, not just normal ones
  21. * 06-22-88 SKS Always use better algorithm to detect root dirs
  22. * 06-29-88 WAJ When looking for root dir makes sure it exists
  23. * 09-28-88 JCR Use new 386 dostypes.h structures
  24. * 10-03-88 JCR 386: Change DOS calls to SYS calls
  25. * 10-04-88 JCR 386: Removed 'far' keyword
  26. * 10-10-88 GJF Made API names match DOSCALLS.H
  27. * 11-24-88 GJF ".cmd" should be considered executable, not ".bat"
  28. * 01-31-89 JCR _canonic() is now _fullpath() and args reversed
  29. * 04-12-89 JCR New syscall interace
  30. * 05-25-89 JCR 386 OS/2 calls use '_syscall' calling convention
  31. * 03-07-90 GJF Replaced _LOAD_DS with _CALLTYPE1, added #include
  32. * <cruntime.h>, removed #include <register.h> and
  33. * removed some leftover 16-bit support. Also, fixed
  34. * the copyright.
  35. * 04-02-90 GJF Made _ValidDrive() and _dtoxmode() _CALLTYPE1. Removed
  36. * #include <dos.h>.
  37. * 07-23-90 SBM Compiles cleanly with -W3 (added/removed appropriate
  38. * includes), removed '32' from API names
  39. * 08-10-90 SBM Compiles cleanly with -W3 with new build of compiler
  40. * 09-03-90 SBM Removed EXT macro
  41. * 09-27-90 GJF New-style function declarators.
  42. * 12-04-90 SRW Changed to include <oscalls.h> instead of <doscalls.h>
  43. * 12-06-90 SRW Added _CRUISER_ and _WIN32 conditionals.
  44. * 12-28-90 SRW Added cast of void * to char * for Mips C Compiler
  45. * 01-18-91 GJF ANSI naming.
  46. * 01-28-91 GJF Fixed call to DOSFINDFIRST (removed last arg).
  47. * 02-28-91 SRW Fixed bug in _dtoxtime calls [_WIN32_]
  48. * 03-05-91 MHL Fixed stat to not use _ValidDrive for stat of root
  49. * 05-19-92 SKS .BAT is a valid "executable" extension for NT, as
  50. * well as CMD. Also, File Creation and File Last Access
  51. * timestamps may be 0 on some file systems (e.g. FAT)
  52. * in which case the File Last Write time should be used.
  53. * 05-29-92 SKS Files with SYSTEM bit set should NOT be marked
  54. * READ-ONLY; these two attributes are independent.
  55. * 08-18-92 SKS Add a call to FileTimeToLocalFileTime
  56. * as a temporary fix until _dtoxtime takes UTC
  57. * 11-20-92 SKS _doserrno must always be set whenever errno is.
  58. * 11-30-92 KRS Port _MBCS support from 16-bit tree.
  59. * 03-29-93 GJF Converted from using _dtoxtime() to __gmtotime_t().
  60. * 04-06-93 SKS Replace _CRTAPI* with __cdecl
  61. * Change _ValidDrive to _validdrive
  62. * 04-07-93 GJF Changed first arg type to const char *.
  63. * 04-18-93 SKS Move _validdrive to getcwd.c and make it static
  64. * 07-21-93 GJF Converted from using __gmtotime_t to __loctotime_t
  65. * (amounts to reversing the change of 03-29-93).
  66. * 12-16-93 CFW Enable Unicode variant.
  67. * 12-28-94 GJF Added _stati64 and _wstati64.
  68. * 02-08-95 JWM Spliced _WIN32 & Mac versions.
  69. * 03-27-95 SKS loctotime_t uses absolute years (not year-1900)!
  70. * 09-25-95 GJF __loctotime_t now takes a DST flag, pass -1 in this
  71. * slot to indicate DST is undetermined.
  72. * 11-29-95 SKS Add support for calls such as stat("//server/share/")
  73. * 07-01-96 GJF Replaced defined(_WIN32) with !defined(_MAC). Also,
  74. * detab-ed and cleaned up the format.
  75. * 05-17-99 PML Remove all Macintosh support.
  76. * 10-27-99 GB Remove #inlcude <dostypes.h>
  77. *
  78. *******************************************************************************/
  79. #include <cruntime.h>
  80. #include <sys/types.h>
  81. #include <sys/stat.h>
  82. #include <errno.h>
  83. #include <ctype.h>
  84. #include <msdos.h>
  85. #include <oscalls.h>
  86. #include <string.h>
  87. #include <internal.h>
  88. #include <stdlib.h>
  89. #include <direct.h>
  90. #include <mbstring.h>
  91. #include <tchar.h>
  92. #define ISSLASH(a) ((a) == _T('\\') || (a) == _T('/'))
  93. #ifdef _UNICODE
  94. #define __tdtoxmode __wdtoxmode
  95. #else /* ndef _UNICODE */
  96. #define __tdtoxmode __dtoxmode
  97. #endif /* _UNICODE */
  98. /*
  99. * Local routine which returns true if the argument is a UNC name
  100. * specifying the root name of a share, such as '\\server\share\'.
  101. */
  102. static int IsRootUNCName(const _TSCHAR *path);
  103. /***
  104. *unsigned __tdtoxmode(attr, name) -
  105. *
  106. *Purpose:
  107. *
  108. *Entry:
  109. *
  110. *Exit:
  111. *
  112. *Exceptions:
  113. *
  114. *******************************************************************************/
  115. #ifdef _USE_INT64
  116. extern unsigned short __cdecl __tdtoxmode(int, const _TSCHAR *);
  117. #else /* ndef _USE_INT64 */
  118. unsigned short __cdecl __tdtoxmode (
  119. int attr,
  120. const _TSCHAR *name
  121. )
  122. {
  123. REG1 unsigned short uxmode;
  124. unsigned dosmode;
  125. REG2 const _TSCHAR *p;
  126. dosmode = attr & 0xff;
  127. if ((p = name)[1] == _T(':'))
  128. p += 2;
  129. /* check to see if this is a directory - note we must make a special
  130. * check for the root, which DOS thinks is not a directory
  131. */
  132. uxmode = (unsigned short)
  133. (((ISSLASH(*p) && !p[1]) || (dosmode & A_D) || !*p)
  134. ? _S_IFDIR|_S_IEXEC : _S_IFREG);
  135. /* If attribute byte does not have read-only bit, it is read-write */
  136. uxmode |= (dosmode & A_RO) ? _S_IREAD : (_S_IREAD|_S_IWRITE);
  137. /* see if file appears to be executable - check extension of name */
  138. if (p = _tcsrchr(name, _T('.'))) {
  139. if ( !_tcsicmp(p, _T(".exe")) ||
  140. !_tcsicmp(p, _T(".cmd")) ||
  141. !_tcsicmp(p, _T(".bat")) ||
  142. !_tcsicmp(p, _T(".com")) )
  143. uxmode |= _S_IEXEC;
  144. }
  145. /* propagate user read/write/execute bits to group/other fields */
  146. uxmode |= (uxmode & 0700) >> 3;
  147. uxmode |= (uxmode & 0700) >> 6;
  148. return(uxmode);
  149. }
  150. #endif /* _USE_INT64 */
  151. /***
  152. *int _stat(name, buf) - get file status info
  153. *
  154. *Purpose:
  155. * _stat obtains information about the file and stores it in the
  156. * structure pointed to by buf.
  157. *
  158. * Note: We cannot directly use the file time stamps returned in the
  159. * WIN32_FIND_DATA structure. The values are supposedly in system time
  160. * and system time is ambiguously defined (it is UTC for Windows NT, local
  161. * time for Win32S and probably local time for Win32C). Therefore, these
  162. * values must be converted to local time before than can be used.
  163. *
  164. *Entry:
  165. * _TSCHAR *name - pathname of given file
  166. * struct _stat *buffer - pointer to buffer to store info in
  167. *
  168. *Exit:
  169. * fills in structure pointed to by buffer
  170. * returns 0 if successful
  171. * returns -1 and sets errno if unsuccessful
  172. *
  173. *Exceptions:
  174. *
  175. *******************************************************************************/
  176. #ifdef _USE_INT64
  177. int __cdecl _tstati64 (
  178. REG1 const _TSCHAR *name,
  179. REG2 struct _stati64 *buf
  180. )
  181. #else /* ndef _USE_INT64 */
  182. int __cdecl _tstat (
  183. REG1 const _TSCHAR *name,
  184. REG2 struct _stat *buf
  185. )
  186. #endif /* _USE_INT64 */
  187. {
  188. _TSCHAR * path;
  189. _TSCHAR pathbuf[ _MAX_PATH ];
  190. int drive; /* A: = 1, B: = 2, etc. */
  191. HANDLE findhandle;
  192. WIN32_FIND_DATA findbuf;
  193. /* Don't allow wildcards to be interpreted by system */
  194. #ifdef _UNICODE
  195. if (wcspbrk(name, L"?*")) {
  196. #else
  197. if (_mbspbrk(name, "?*")) {
  198. #endif
  199. errno = ENOENT;
  200. _doserrno = E_nofile;
  201. return(-1);
  202. }
  203. /* Try to get disk from name. If none, get current disk. */
  204. if (name[1] == _T(':')){
  205. if ( *name && !name[2] ){
  206. errno = ENOENT; /* return an error if name is */
  207. _doserrno = E_nofile; /* just drive letter then colon */
  208. return( -1 );
  209. }
  210. drive = _totlower(*name) - _T('a') + 1;
  211. }
  212. else
  213. drive = _getdrive();
  214. /* Call Find Match File */
  215. findhandle = FindFirstFile((_TSCHAR *)name, &findbuf);
  216. if ( findhandle == INVALID_HANDLE_VALUE ) {
  217. #ifdef _UNICODE
  218. if ( !( wcspbrk(name, L"./\\") &&
  219. #else
  220. if ( !( _mbspbrk(name, "./\\") &&
  221. #endif
  222. (path = _tfullpath( pathbuf, name, _MAX_PATH )) &&
  223. /* root dir. ('C:\') or UNC root dir. ('\\server\share\') */
  224. ((_tcslen( path ) == 3) || IsRootUNCName(path)) &&
  225. (GetDriveType( path ) > 1) ) )
  226. {
  227. errno = ENOENT;
  228. _doserrno = E_nofile;
  229. return( -1 );
  230. }
  231. /*
  232. * Root directories (such as C:\ or \\server\share\ are fabricated.
  233. */
  234. findbuf.dwFileAttributes = A_D;
  235. findbuf.nFileSizeHigh = 0;
  236. findbuf.nFileSizeLow = 0;
  237. findbuf.cFileName[0] = _T('\0');
  238. buf->st_mtime = __loctotime_t(1980,1,1,0,0,0, -1);
  239. buf->st_atime = buf->st_mtime;
  240. buf->st_ctime = buf->st_mtime;
  241. }
  242. else {
  243. SYSTEMTIME SystemTime;
  244. FILETIME LocalFTime;
  245. if ( !FileTimeToLocalFileTime( &findbuf.ftLastWriteTime,
  246. &LocalFTime ) ||
  247. !FileTimeToSystemTime( &LocalFTime, &SystemTime ) )
  248. {
  249. _dosmaperr( GetLastError() );
  250. FindClose( findhandle );
  251. return( -1 );
  252. }
  253. buf->st_mtime = __loctotime_t( SystemTime.wYear,
  254. SystemTime.wMonth,
  255. SystemTime.wDay,
  256. SystemTime.wHour,
  257. SystemTime.wMinute,
  258. SystemTime.wSecond,
  259. -1 );
  260. if ( findbuf.ftLastAccessTime.dwLowDateTime ||
  261. findbuf.ftLastAccessTime.dwHighDateTime )
  262. {
  263. if ( !FileTimeToLocalFileTime( &findbuf.ftLastAccessTime,
  264. &LocalFTime ) ||
  265. !FileTimeToSystemTime( &LocalFTime, &SystemTime ) )
  266. {
  267. _dosmaperr( GetLastError() );
  268. FindClose( findhandle );
  269. return( -1 );
  270. }
  271. buf->st_atime = __loctotime_t( SystemTime.wYear,
  272. SystemTime.wMonth,
  273. SystemTime.wDay,
  274. SystemTime.wHour,
  275. SystemTime.wMinute,
  276. SystemTime.wSecond,
  277. -1 );
  278. } else
  279. buf->st_atime = buf->st_mtime ;
  280. if ( findbuf.ftCreationTime.dwLowDateTime ||
  281. findbuf.ftCreationTime.dwHighDateTime )
  282. {
  283. if ( !FileTimeToLocalFileTime( &findbuf.ftCreationTime,
  284. &LocalFTime ) ||
  285. !FileTimeToSystemTime( &LocalFTime, &SystemTime ) )
  286. {
  287. _dosmaperr( GetLastError() );
  288. FindClose( findhandle );
  289. return( -1 );
  290. }
  291. buf->st_ctime = __loctotime_t( SystemTime.wYear,
  292. SystemTime.wMonth,
  293. SystemTime.wDay,
  294. SystemTime.wHour,
  295. SystemTime.wMinute,
  296. SystemTime.wSecond,
  297. -1 );
  298. } else
  299. buf->st_ctime = buf->st_mtime ;
  300. FindClose(findhandle);
  301. }
  302. /* Fill in buf */
  303. buf->st_mode = __tdtoxmode(findbuf.dwFileAttributes, name);
  304. buf->st_nlink = 1;
  305. #ifdef _USE_INT64
  306. buf->st_size = ((__int64)(findbuf.nFileSizeHigh)) * (0x100000000i64) +
  307. (__int64)(findbuf.nFileSizeLow);
  308. #else /* ndef _USE_INT64 */
  309. buf->st_size = findbuf.nFileSizeLow;
  310. #endif /* _USE_INT64 */
  311. /* now set the common fields */
  312. buf->st_uid = buf->st_gid = buf->st_ino = 0;
  313. buf->st_rdev = buf->st_dev = (_dev_t)(drive - 1); /* A=0, B=1, etc. */
  314. return(0);
  315. }
  316. /*
  317. * IsRootUNCName - returns TRUE if the argument is a UNC name specifying
  318. * a root share. That is, if it is of the form \\server\share\.
  319. * This routine will also return true if the argument is of the
  320. * form \\server\share (no trailing slash) but Win32 currently
  321. * does not like that form.
  322. *
  323. * Forward slashes ('/') may be used instead of backslashes ('\').
  324. */
  325. static int IsRootUNCName(const _TSCHAR *path)
  326. {
  327. /*
  328. * If a root UNC name, path will start with 2 (but not 3) slashes
  329. */
  330. if ( ( _tcslen ( path ) >= 5 ) /* minimum string is "//x/y" */
  331. && ISSLASH(path[0]) && ISSLASH(path[1]))
  332. {
  333. const _TSCHAR * p = path + 2 ;
  334. /*
  335. * find the slash between the server name and share name
  336. */
  337. while ( * ++ p )
  338. if ( ISSLASH(*p) )
  339. break ;
  340. if ( *p && p[1] )
  341. {
  342. /*
  343. * is there a further slash?
  344. */
  345. while ( * ++ p )
  346. if ( ISSLASH(*p) )
  347. break ;
  348. /*
  349. * just final slash (or no final slash)
  350. */
  351. if ( !*p || !p[1])
  352. return 1;
  353. }
  354. }
  355. return 0 ;
  356. }