2
0

filestat.c 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761
  1. /* Licensed to the Apache Software Foundation (ASF) under one or more
  2. * contributor license agreements. See the NOTICE file distributed with
  3. * this work for additional information regarding copyright ownership.
  4. * The ASF licenses this file to You under the Apache License, Version 2.0
  5. * (the "License"); you may not use this file except in compliance with
  6. * the License. You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #include "apr.h"
  17. #include <aclapi.h>
  18. #include "apr_private.h"
  19. #include "apr_arch_file_io.h"
  20. #include "apr_file_io.h"
  21. #include "apr_general.h"
  22. #include "apr_strings.h"
  23. #include "apr_errno.h"
  24. #include "apr_time.h"
  25. #include <sys/stat.h>
  26. #include "apr_arch_atime.h"
  27. #include "apr_arch_misc.h"
  28. /* We have to assure that the file name contains no '*'s, or other
  29. * wildcards when using FindFirstFile to recover the true file name.
  30. */
  31. static apr_status_t test_safe_name(const char *name)
  32. {
  33. /* Only accept ':' in the second position of the filename,
  34. * as the drive letter delimiter:
  35. */
  36. if (apr_isalpha(*name) && (name[1] == ':')) {
  37. name += 2;
  38. }
  39. while (*name) {
  40. if (!IS_FNCHAR(*name) && (*name != '\\') && (*name != '/')) {
  41. if (*name == '?' || *name == '*')
  42. return APR_EPATHWILD;
  43. else
  44. return APR_EBADPATH;
  45. }
  46. ++name;
  47. }
  48. return APR_SUCCESS;
  49. }
  50. static apr_status_t free_localheap(void *heap) {
  51. LocalFree(heap);
  52. return APR_SUCCESS;
  53. }
  54. static apr_gid_t worldid = NULL;
  55. static void free_world(void)
  56. {
  57. if (worldid) {
  58. FreeSid(worldid);
  59. worldid = NULL;
  60. }
  61. }
  62. /* Left bit shifts from World scope to given scope */
  63. typedef enum prot_scope_e {
  64. prot_scope_world = 0,
  65. prot_scope_group = 4,
  66. prot_scope_user = 8
  67. } prot_scope_e;
  68. static apr_fileperms_t convert_prot(ACCESS_MASK acc, prot_scope_e scope)
  69. {
  70. /* These choices are based on the single filesystem bit that controls
  71. * the given behavior. They are -not- recommended for any set protection
  72. * function, such a function should -set- use GENERIC_READ/WRITE/EXECUTE
  73. */
  74. apr_fileperms_t prot = 0;
  75. if (acc & FILE_EXECUTE)
  76. prot |= APR_WEXECUTE;
  77. if (acc & FILE_WRITE_DATA)
  78. prot |= APR_WWRITE;
  79. if (acc & FILE_READ_DATA)
  80. prot |= APR_WREAD;
  81. return (prot << scope);
  82. }
  83. static void resolve_prot(apr_finfo_t *finfo, apr_int32_t wanted, PACL dacl)
  84. {
  85. TRUSTEE_W ident = {NULL, NO_MULTIPLE_TRUSTEE, TRUSTEE_IS_SID};
  86. ACCESS_MASK acc;
  87. /*
  88. * This function is only invoked for WinNT,
  89. * there is no reason for os_level testing here.
  90. */
  91. if ((wanted & APR_FINFO_WPROT) && !worldid) {
  92. SID_IDENTIFIER_AUTHORITY SIDAuth = SECURITY_WORLD_SID_AUTHORITY;
  93. if (AllocateAndInitializeSid(&SIDAuth, 1, SECURITY_WORLD_RID,
  94. 0, 0, 0, 0, 0, 0, 0, &worldid))
  95. atexit(free_world);
  96. else
  97. worldid = NULL;
  98. }
  99. if ((wanted & APR_FINFO_UPROT) && (finfo->valid & APR_FINFO_USER)) {
  100. ident.TrusteeType = TRUSTEE_IS_USER;
  101. ident.ptstrName = finfo->user;
  102. /* GetEffectiveRightsFromAcl isn't supported under Win9x,
  103. * which shouldn't come as a surprize. Since we are passing
  104. * TRUSTEE_IS_SID, always skip the A->W layer.
  105. */
  106. if (GetEffectiveRightsFromAclW(dacl, &ident, &acc) == ERROR_SUCCESS) {
  107. finfo->protection |= convert_prot(acc, prot_scope_user);
  108. finfo->valid |= APR_FINFO_UPROT;
  109. }
  110. }
  111. /* Windows NT: did not return group rights.
  112. * Windows 2000 returns group rights information.
  113. * Since WinNT kernels don't follow the unix model of
  114. * group associations, this all all pretty mute.
  115. */
  116. if ((wanted & APR_FINFO_GPROT) && (finfo->valid & APR_FINFO_GROUP)) {
  117. ident.TrusteeType = TRUSTEE_IS_GROUP;
  118. ident.ptstrName = finfo->group;
  119. if (GetEffectiveRightsFromAclW(dacl, &ident, &acc) == ERROR_SUCCESS) {
  120. finfo->protection |= convert_prot(acc, prot_scope_group);
  121. finfo->valid |= APR_FINFO_GPROT;
  122. }
  123. }
  124. if ((wanted & APR_FINFO_WPROT) && (worldid)) {
  125. ident.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
  126. ident.ptstrName = worldid;
  127. if (GetEffectiveRightsFromAclW(dacl, &ident, &acc) == ERROR_SUCCESS) {
  128. finfo->protection |= convert_prot(acc, prot_scope_world);
  129. finfo->valid |= APR_FINFO_WPROT;
  130. }
  131. }
  132. }
  133. static apr_status_t resolve_ident(apr_finfo_t *finfo, const char *fname,
  134. apr_int32_t wanted, apr_pool_t *pool)
  135. {
  136. apr_file_t *thefile = NULL;
  137. apr_status_t rv;
  138. /*
  139. * NT5 (W2K) only supports symlinks in the same manner as mount points.
  140. * This code should eventually take that into account, for now treat
  141. * every reparse point as a symlink...
  142. *
  143. * We must open the file with READ_CONTROL if we plan to retrieve the
  144. * user, group or permissions.
  145. */
  146. if ((rv = apr_file_open(&thefile, fname, APR_OPENINFO
  147. | ((wanted & APR_FINFO_LINK) ? APR_OPENLINK : 0)
  148. | ((wanted & (APR_FINFO_PROT | APR_FINFO_OWNER))
  149. ? APR_READCONTROL : 0),
  150. APR_OS_DEFAULT, pool)) == APR_SUCCESS) {
  151. rv = apr_file_info_get(finfo, wanted, thefile);
  152. finfo->filehand = NULL;
  153. apr_file_close(thefile);
  154. }
  155. else if (APR_STATUS_IS_EACCES(rv) && (wanted & (APR_FINFO_PROT
  156. | APR_FINFO_OWNER))) {
  157. /* We have a backup plan. Perhaps we couldn't grab READ_CONTROL?
  158. * proceed without asking for that permission...
  159. */
  160. if ((rv = apr_file_open(&thefile, fname, APR_OPENINFO
  161. | ((wanted & APR_FINFO_LINK) ? APR_OPENLINK : 0),
  162. APR_OS_DEFAULT, pool)) == APR_SUCCESS) {
  163. rv = apr_file_info_get(finfo, wanted & ~(APR_FINFO_PROT
  164. | APR_FINFO_OWNER),
  165. thefile);
  166. finfo->filehand = NULL;
  167. apr_file_close(thefile);
  168. }
  169. }
  170. if (rv != APR_SUCCESS && rv != APR_INCOMPLETE)
  171. return (rv);
  172. /* We picked up this case above and had opened the link's properties */
  173. if (wanted & APR_FINFO_LINK)
  174. finfo->valid |= APR_FINFO_LINK;
  175. return rv;
  176. }
  177. static void guess_protection_bits(apr_finfo_t *finfo)
  178. {
  179. /* Read, write execute for owner. In the Win9x environment, any
  180. * readable file is executable (well, not entirely 100% true, but
  181. * still looking for some cheap logic that would help us here.)
  182. * The same holds on NT if a file doesn't have a DACL (e.g., on FAT)
  183. */
  184. if (finfo->protection & APR_FREADONLY) {
  185. finfo->protection |= APR_WREAD | APR_WEXECUTE;
  186. }
  187. else {
  188. finfo->protection |= APR_WREAD | APR_WEXECUTE | APR_WWRITE;
  189. }
  190. finfo->protection |= (finfo->protection << prot_scope_group)
  191. | (finfo->protection << prot_scope_user);
  192. finfo->valid |= APR_FINFO_UPROT | APR_FINFO_GPROT | APR_FINFO_WPROT;
  193. }
  194. apr_status_t more_finfo(apr_finfo_t *finfo, const void *ufile,
  195. apr_int32_t wanted, int whatfile)
  196. {
  197. PSID user = NULL, grp = NULL;
  198. PACL dacl = NULL;
  199. apr_status_t rv;
  200. if (apr_os_level < APR_WIN_NT)
  201. guess_protection_bits(finfo);
  202. else if (wanted & (APR_FINFO_PROT | APR_FINFO_OWNER))
  203. {
  204. /* On NT this request is incredibly expensive, but accurate.
  205. * Since the WinNT-only functions below are protected by the
  206. * (apr_os_level < APR_WIN_NT) case above, we need no extra
  207. * tests, but remember GetNamedSecurityInfo & GetSecurityInfo
  208. * are not supported on 9x.
  209. */
  210. SECURITY_INFORMATION sinf = 0;
  211. PSECURITY_DESCRIPTOR pdesc = NULL;
  212. if (wanted & (APR_FINFO_USER | APR_FINFO_UPROT))
  213. sinf |= OWNER_SECURITY_INFORMATION;
  214. if (wanted & (APR_FINFO_GROUP | APR_FINFO_GPROT))
  215. sinf |= GROUP_SECURITY_INFORMATION;
  216. if (wanted & APR_FINFO_PROT)
  217. sinf |= DACL_SECURITY_INFORMATION;
  218. if (whatfile == MORE_OF_WFSPEC) {
  219. apr_wchar_t *wfile = (apr_wchar_t*) ufile;
  220. int fix = 0;
  221. if (wcsncmp(wfile, L"\\\\?\\", 4) == 0) {
  222. fix = 4;
  223. if (wcsncmp(wfile + fix, L"UNC\\", 4) == 0)
  224. wfile[6] = L'\\', fix = 6;
  225. }
  226. rv = GetNamedSecurityInfoW(wfile + fix,
  227. SE_FILE_OBJECT, sinf,
  228. ((wanted & (APR_FINFO_USER | APR_FINFO_UPROT)) ? &user : NULL),
  229. ((wanted & (APR_FINFO_GROUP | APR_FINFO_GPROT)) ? &grp : NULL),
  230. ((wanted & APR_FINFO_PROT) ? &dacl : NULL),
  231. NULL, &pdesc);
  232. if (fix == 6)
  233. wfile[6] = L'C';
  234. }
  235. else if (whatfile == MORE_OF_FSPEC)
  236. rv = GetNamedSecurityInfoA((char*)ufile,
  237. SE_FILE_OBJECT, sinf,
  238. ((wanted & (APR_FINFO_USER | APR_FINFO_UPROT)) ? &user : NULL),
  239. ((wanted & (APR_FINFO_GROUP | APR_FINFO_GPROT)) ? &grp : NULL),
  240. ((wanted & APR_FINFO_PROT) ? &dacl : NULL),
  241. NULL, &pdesc);
  242. else if (whatfile == MORE_OF_HANDLE)
  243. rv = GetSecurityInfo((HANDLE)ufile,
  244. SE_FILE_OBJECT, sinf,
  245. ((wanted & (APR_FINFO_USER | APR_FINFO_UPROT)) ? &user : NULL),
  246. ((wanted & (APR_FINFO_GROUP | APR_FINFO_GPROT)) ? &grp : NULL),
  247. ((wanted & APR_FINFO_PROT) ? &dacl : NULL),
  248. NULL, &pdesc);
  249. else
  250. return APR_INCOMPLETE;
  251. if (rv == ERROR_SUCCESS)
  252. apr_pool_cleanup_register(finfo->pool, pdesc, free_localheap,
  253. apr_pool_cleanup_null);
  254. else
  255. user = grp = dacl = NULL;
  256. if (user) {
  257. finfo->user = user;
  258. finfo->valid |= APR_FINFO_USER;
  259. }
  260. if (grp) {
  261. finfo->group = grp;
  262. finfo->valid |= APR_FINFO_GROUP;
  263. }
  264. if (dacl) {
  265. /* Retrieved the discresionary access list */
  266. resolve_prot(finfo, wanted, dacl);
  267. }
  268. else if (wanted & APR_FINFO_PROT)
  269. guess_protection_bits(finfo);
  270. }
  271. return ((wanted & ~finfo->valid) ? APR_INCOMPLETE : APR_SUCCESS);
  272. }
  273. /* This generic fillin depends upon byhandle to be passed as 0 when
  274. * a WIN32_FILE_ATTRIBUTE_DATA or either WIN32_FIND_DATA [A or W] is
  275. * passed for wininfo. When the BY_HANDLE_FILE_INFORMATION structure
  276. * is passed for wininfo, byhandle is passed as 1 to offset the one
  277. * dword discrepancy in offset of the High/Low size structure members.
  278. *
  279. * The generic fillin returns 1 if the caller should further inquire
  280. * if this is a CHR filetype. If it's reasonably certain it can't be,
  281. * then the function returns 0.
  282. */
  283. int fillin_fileinfo(apr_finfo_t *finfo,
  284. WIN32_FILE_ATTRIBUTE_DATA *wininfo,
  285. int byhandle, apr_int32_t wanted)
  286. {
  287. DWORD *sizes = &wininfo->nFileSizeHigh + byhandle;
  288. int warn = 0;
  289. memset(finfo, '\0', sizeof(*finfo));
  290. FileTimeToAprTime(&finfo->atime, &wininfo->ftLastAccessTime);
  291. FileTimeToAprTime(&finfo->ctime, &wininfo->ftCreationTime);
  292. FileTimeToAprTime(&finfo->mtime, &wininfo->ftLastWriteTime);
  293. #if APR_HAS_LARGE_FILES
  294. finfo->size = (apr_off_t)sizes[1]
  295. | ((apr_off_t)sizes[0] << 32);
  296. #else
  297. finfo->size = (apr_off_t)sizes[1];
  298. if (finfo->size < 0 || sizes[0])
  299. finfo->size = 0x7fffffff;
  300. #endif
  301. if (wanted & APR_FINFO_LINK &&
  302. wininfo->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
  303. finfo->filetype = APR_LNK;
  304. }
  305. else if (wininfo->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
  306. finfo->filetype = APR_DIR;
  307. }
  308. else if (wininfo->dwFileAttributes & FILE_ATTRIBUTE_DEVICE) {
  309. /* Warning: This test only succeeds on Win9x, on NT these files
  310. * (con, aux, nul, lpt#, com# etc) escape early detection!
  311. */
  312. finfo->filetype = APR_CHR;
  313. }
  314. else {
  315. /* Warning: Short of opening the handle to the file, the 'FileType'
  316. * appears to be unknowable (in any trustworthy or consistent sense)
  317. * on WinNT/2K as far as PIPE, CHR, etc are concerned.
  318. */
  319. if (!wininfo->ftLastWriteTime.dwLowDateTime
  320. && !wininfo->ftLastWriteTime.dwHighDateTime
  321. && !finfo->size)
  322. warn = 1;
  323. finfo->filetype = APR_REG;
  324. }
  325. /* The following flags are [for this moment] private to Win32.
  326. * That's the only excuse for not toggling valid bits to reflect them.
  327. */
  328. if (wininfo->dwFileAttributes & FILE_ATTRIBUTE_READONLY)
  329. finfo->protection = APR_FREADONLY;
  330. finfo->valid = APR_FINFO_ATIME | APR_FINFO_CTIME | APR_FINFO_MTIME
  331. | APR_FINFO_SIZE | APR_FINFO_TYPE; /* == APR_FINFO_MIN */
  332. /* Only byhandle optionally tests link targets, so tell that caller
  333. * what it wants to hear, otherwise the byattributes is never
  334. * reporting anything but the link.
  335. */
  336. if (!byhandle || (wanted & APR_FINFO_LINK))
  337. finfo->valid |= APR_FINFO_LINK;
  338. return warn;
  339. }
  340. APR_DECLARE(apr_status_t) apr_file_info_get(apr_finfo_t *finfo, apr_int32_t wanted,
  341. apr_file_t *thefile)
  342. {
  343. BY_HANDLE_FILE_INFORMATION FileInfo;
  344. if (thefile->buffered) {
  345. /* XXX: flush here is not mutex protected */
  346. apr_status_t rv = apr_file_flush(thefile);
  347. if (rv != APR_SUCCESS)
  348. return rv;
  349. }
  350. if (!GetFileInformationByHandle(thefile->filehand, &FileInfo)) {
  351. return apr_get_os_error();
  352. }
  353. fillin_fileinfo(finfo, (WIN32_FILE_ATTRIBUTE_DATA *) &FileInfo, 1, wanted);
  354. if (finfo->filetype == APR_REG)
  355. {
  356. /* Go the extra mile to be -certain- that we have a real, regular
  357. * file, since the attribute bits aren't a certain thing. Even
  358. * though fillin should have hinted if we *must* do this, we
  359. * don't need to take chances while the handle is already open.
  360. */
  361. DWORD FileType;
  362. if (FileType = GetFileType(thefile->filehand)) {
  363. if (FileType == FILE_TYPE_CHAR) {
  364. finfo->filetype = APR_CHR;
  365. }
  366. else if (FileType == FILE_TYPE_PIPE) {
  367. finfo->filetype = APR_PIPE;
  368. }
  369. /* Otherwise leave the original conclusion alone
  370. */
  371. }
  372. }
  373. finfo->pool = thefile->pool;
  374. /* ### The finfo lifetime may exceed the lifetime of thefile->pool
  375. * but finfo's aren't managed in pools, so where on earth would
  376. * we pstrdup the fname into???
  377. */
  378. finfo->fname = thefile->fname;
  379. /* Extra goodies known only by GetFileInformationByHandle() */
  380. finfo->inode = (apr_ino_t)FileInfo.nFileIndexLow
  381. | ((apr_ino_t)FileInfo.nFileIndexHigh << 32);
  382. finfo->device = FileInfo.dwVolumeSerialNumber;
  383. finfo->nlink = FileInfo.nNumberOfLinks;
  384. finfo->valid |= APR_FINFO_IDENT | APR_FINFO_NLINK;
  385. /* If we still want something more (besides the name) go get it!
  386. */
  387. if ((wanted &= ~finfo->valid) & ~APR_FINFO_NAME) {
  388. return more_finfo(finfo, thefile->filehand, wanted, MORE_OF_HANDLE);
  389. }
  390. return APR_SUCCESS;
  391. }
  392. APR_DECLARE(apr_status_t) apr_file_perms_set(const char *fname,
  393. apr_fileperms_t perms)
  394. {
  395. return APR_ENOTIMPL;
  396. }
  397. APR_DECLARE(apr_status_t) apr_stat(apr_finfo_t *finfo, const char *fname,
  398. apr_int32_t wanted, apr_pool_t *pool)
  399. {
  400. /* XXX: is constant - needs testing - which requires a lighter-weight root test fn */
  401. int isroot = 0;
  402. apr_status_t ident_rv = 0;
  403. apr_status_t rv;
  404. #if APR_HAS_UNICODE_FS
  405. apr_wchar_t wfname[APR_PATH_MAX];
  406. #endif
  407. char *filename = NULL;
  408. /* These all share a common subset of this structure */
  409. union {
  410. WIN32_FIND_DATAW w;
  411. WIN32_FIND_DATAA n;
  412. WIN32_FILE_ATTRIBUTE_DATA i;
  413. } FileInfo;
  414. /* Catch fname length == MAX_PATH since GetFileAttributesEx fails
  415. * with PATH_NOT_FOUND. We would rather indicate length error than
  416. * 'not found'
  417. */
  418. if (strlen(fname) >= APR_PATH_MAX) {
  419. return APR_ENAMETOOLONG;
  420. }
  421. #if APR_HAS_UNICODE_FS
  422. IF_WIN_OS_IS_UNICODE
  423. {
  424. if ((wanted & (APR_FINFO_IDENT | APR_FINFO_NLINK))
  425. || (~wanted & APR_FINFO_LINK)) {
  426. /* FindFirstFile and GetFileAttributesEx can't figure the inode,
  427. * device or number of links, so we need to resolve with an open
  428. * file handle. If the user has asked for these fields, fall over
  429. * to the get file info by handle method. If we fail, or the user
  430. * also asks for the file name, continue by our usual means.
  431. *
  432. * We also must use this method for a 'true' stat, that resolves
  433. * a symlink (NTFS Junction) target. This is because all fileinfo
  434. * on a Junction always returns the junction, opening the target
  435. * is the only way to resolve the target's attributes.
  436. */
  437. if ((ident_rv = resolve_ident(finfo, fname, wanted, pool))
  438. == APR_SUCCESS)
  439. return ident_rv;
  440. else if (ident_rv == APR_INCOMPLETE)
  441. wanted &= ~finfo->valid;
  442. }
  443. if (rv = utf8_to_unicode_path(wfname, sizeof(wfname)
  444. / sizeof(apr_wchar_t), fname))
  445. return rv;
  446. if (!(wanted & APR_FINFO_NAME)) {
  447. if (!GetFileAttributesExW(wfname, GetFileExInfoStandard,
  448. &FileInfo.i))
  449. return apr_get_os_error();
  450. }
  451. else {
  452. /* Guard against bogus wildcards and retrieve by name
  453. * since we want the true name, and set aside a long
  454. * enough string to handle the longest file name.
  455. */
  456. char tmpname[APR_FILE_MAX * 3 + 1];
  457. HANDLE hFind;
  458. if ((rv = test_safe_name(fname)) != APR_SUCCESS) {
  459. return rv;
  460. }
  461. hFind = FindFirstFileW(wfname, &FileInfo.w);
  462. if (hFind == INVALID_HANDLE_VALUE)
  463. return apr_get_os_error();
  464. FindClose(hFind);
  465. if (unicode_to_utf8_path(tmpname, sizeof(tmpname),
  466. FileInfo.w.cFileName)) {
  467. return APR_ENAMETOOLONG;
  468. }
  469. filename = apr_pstrdup(pool, tmpname);
  470. }
  471. }
  472. #endif
  473. #if APR_HAS_ANSI_FS
  474. ELSE_WIN_OS_IS_ANSI
  475. {
  476. char *root = NULL;
  477. const char *test = fname;
  478. rv = apr_filepath_root(&root, &test, APR_FILEPATH_NATIVE, pool);
  479. isroot = (root && *root && !(*test));
  480. if ((apr_os_level >= APR_WIN_98) && (!(wanted & APR_FINFO_NAME) || isroot))
  481. {
  482. /* cannot use FindFile on a Win98 root, it returns \*
  483. * GetFileAttributesExA is not available on Win95
  484. */
  485. if (!GetFileAttributesExA(fname, GetFileExInfoStandard,
  486. &FileInfo.i)) {
  487. return apr_get_os_error();
  488. }
  489. }
  490. else if (isroot) {
  491. /* This is Win95 and we are trying to stat a root. Lie.
  492. */
  493. if (GetDriveType(fname) != DRIVE_UNKNOWN)
  494. {
  495. finfo->pool = pool;
  496. finfo->filetype = 0;
  497. finfo->mtime = apr_time_now();
  498. finfo->protection |= APR_WREAD | APR_WEXECUTE | APR_WWRITE;
  499. finfo->protection |= (finfo->protection << prot_scope_group)
  500. | (finfo->protection << prot_scope_user);
  501. finfo->valid |= APR_FINFO_TYPE | APR_FINFO_PROT
  502. | APR_FINFO_MTIME
  503. | (wanted & APR_FINFO_LINK);
  504. return (wanted &= ~finfo->valid) ? APR_INCOMPLETE
  505. : APR_SUCCESS;
  506. }
  507. else
  508. return APR_FROM_OS_ERROR(ERROR_PATH_NOT_FOUND);
  509. }
  510. else {
  511. /* Guard against bogus wildcards and retrieve by name
  512. * since we want the true name, or are stuck in Win95,
  513. * or are looking for the root of a Win98 drive.
  514. */
  515. HANDLE hFind;
  516. if ((rv = test_safe_name(fname)) != APR_SUCCESS) {
  517. return rv;
  518. }
  519. hFind = FindFirstFileA(fname, &FileInfo.n);
  520. if (hFind == INVALID_HANDLE_VALUE) {
  521. return apr_get_os_error();
  522. }
  523. FindClose(hFind);
  524. filename = apr_pstrdup(pool, FileInfo.n.cFileName);
  525. }
  526. }
  527. #endif
  528. if (ident_rv != APR_INCOMPLETE) {
  529. if (fillin_fileinfo(finfo, (WIN32_FILE_ATTRIBUTE_DATA *) &FileInfo,
  530. 0, wanted))
  531. {
  532. /* Go the extra mile to assure we have a file. WinNT/2000 seems
  533. * to reliably translate char devices to the path '\\.\device'
  534. * so go ask for the full path.
  535. */
  536. if (apr_os_level >= APR_WIN_NT)
  537. {
  538. #if APR_HAS_UNICODE_FS
  539. apr_wchar_t tmpname[APR_FILE_MAX];
  540. apr_wchar_t *tmpoff = NULL;
  541. if (GetFullPathNameW(wfname, sizeof(tmpname) / sizeof(apr_wchar_t),
  542. tmpname, &tmpoff))
  543. {
  544. if (!wcsncmp(tmpname, L"\\\\.\\", 4)) {
  545. #else
  546. /* Same initial logic as above, but
  547. * only for WinNT/non-UTF-8 builds of APR:
  548. */
  549. char tmpname[APR_FILE_MAX];
  550. char *tmpoff;
  551. if (GetFullPathName(fname, sizeof(tmpname), tmpname, &tmpoff))
  552. {
  553. if (!strncmp(tmpname, "\\\\.\\", 4)) {
  554. #endif
  555. if (tmpoff == tmpname + 4) {
  556. finfo->filetype = APR_CHR;
  557. }
  558. /* For WHATEVER reason, CHR devices such as \\.\con
  559. * or \\.\lpt1 *may*not* update tmpoff; in fact the
  560. * resulting tmpoff is set to NULL. Guard against
  561. * either case.
  562. *
  563. * This code is identical for wide and narrow chars...
  564. */
  565. else if (!tmpoff) {
  566. tmpoff = tmpname + 4;
  567. while (*tmpoff) {
  568. if (*tmpoff == '\\' || *tmpoff == '/') {
  569. break;
  570. }
  571. ++tmpoff;
  572. }
  573. if (!*tmpoff) {
  574. finfo->filetype = APR_CHR;
  575. }
  576. }
  577. }
  578. }
  579. else {
  580. finfo->valid &= ~APR_FINFO_TYPE;
  581. }
  582. }
  583. else {
  584. finfo->valid &= ~APR_FINFO_TYPE;
  585. }
  586. }
  587. finfo->pool = pool;
  588. }
  589. if (filename && !isroot) {
  590. finfo->name = filename;
  591. finfo->valid |= APR_FINFO_NAME;
  592. }
  593. if (wanted &= ~finfo->valid) {
  594. /* Caller wants more than APR_FINFO_MIN | APR_FINFO_NAME */
  595. #if APR_HAS_UNICODE_FS
  596. if (apr_os_level >= APR_WIN_NT)
  597. return more_finfo(finfo, wfname, wanted, MORE_OF_WFSPEC);
  598. #endif
  599. return more_finfo(finfo, fname, wanted, MORE_OF_FSPEC);
  600. }
  601. return APR_SUCCESS;
  602. }
  603. APR_DECLARE(apr_status_t) apr_file_attrs_set(const char *fname,
  604. apr_fileattrs_t attributes,
  605. apr_fileattrs_t attr_mask,
  606. apr_pool_t *pool)
  607. {
  608. DWORD flags;
  609. apr_status_t rv;
  610. #if APR_HAS_UNICODE_FS
  611. apr_wchar_t wfname[APR_PATH_MAX];
  612. #endif
  613. /* Don't do anything if we can't handle the requested attributes */
  614. if (!(attr_mask & (APR_FILE_ATTR_READONLY
  615. | APR_FILE_ATTR_HIDDEN)))
  616. return APR_SUCCESS;
  617. #if APR_HAS_UNICODE_FS
  618. IF_WIN_OS_IS_UNICODE
  619. {
  620. if (rv = utf8_to_unicode_path(wfname,
  621. sizeof(wfname) / sizeof(wfname[0]),
  622. fname))
  623. return rv;
  624. flags = GetFileAttributesW(wfname);
  625. }
  626. #endif
  627. #if APR_HAS_ANSI_FS
  628. ELSE_WIN_OS_IS_ANSI
  629. {
  630. flags = GetFileAttributesA(fname);
  631. }
  632. #endif
  633. if (flags == 0xFFFFFFFF)
  634. return apr_get_os_error();
  635. if (attr_mask & APR_FILE_ATTR_READONLY)
  636. {
  637. if (attributes & APR_FILE_ATTR_READONLY)
  638. flags |= FILE_ATTRIBUTE_READONLY;
  639. else
  640. flags &= ~FILE_ATTRIBUTE_READONLY;
  641. }
  642. if (attr_mask & APR_FILE_ATTR_HIDDEN)
  643. {
  644. if (attributes & APR_FILE_ATTR_HIDDEN)
  645. flags |= FILE_ATTRIBUTE_HIDDEN;
  646. else
  647. flags &= ~FILE_ATTRIBUTE_HIDDEN;
  648. }
  649. #if APR_HAS_UNICODE_FS
  650. IF_WIN_OS_IS_UNICODE
  651. {
  652. rv = SetFileAttributesW(wfname, flags);
  653. }
  654. #endif
  655. #if APR_HAS_ANSI_FS
  656. ELSE_WIN_OS_IS_ANSI
  657. {
  658. rv = SetFileAttributesA(fname, flags);
  659. }
  660. #endif
  661. if (rv == 0)
  662. return apr_get_os_error();
  663. return APR_SUCCESS;
  664. }
  665. APR_DECLARE(apr_status_t) apr_file_mtime_set(const char *fname,
  666. apr_time_t mtime,
  667. apr_pool_t *pool)
  668. {
  669. apr_file_t *thefile;
  670. apr_status_t rv;
  671. rv = apr_file_open(&thefile, fname,
  672. APR_READ | APR_WRITEATTRS,
  673. APR_OS_DEFAULT, pool);
  674. if (!rv)
  675. {
  676. FILETIME file_ctime;
  677. FILETIME file_atime;
  678. FILETIME file_mtime;
  679. if (!GetFileTime(thefile->filehand,
  680. &file_ctime, &file_atime, &file_mtime))
  681. rv = apr_get_os_error();
  682. else
  683. {
  684. AprTimeToFileTime(&file_mtime, mtime);
  685. if (!SetFileTime(thefile->filehand,
  686. &file_ctime, &file_atime, &file_mtime))
  687. rv = apr_get_os_error();
  688. }
  689. apr_file_close(thefile);
  690. }
  691. return rv;
  692. }