filestat.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417
  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_arch_file_io.h"
  17. #include "fsio.h"
  18. #include "nks/dirio.h"
  19. #include "apr_file_io.h"
  20. #include "apr_general.h"
  21. #include "apr_strings.h"
  22. #include "apr_errno.h"
  23. #include "apr_hash.h"
  24. #include "apr_thread_rwlock.h"
  25. #ifdef HAVE_UTIME_H
  26. #include <utime.h>
  27. #endif
  28. #define APR_HAS_PSA
  29. static apr_filetype_e filetype_from_mode(mode_t mode)
  30. {
  31. apr_filetype_e type = APR_NOFILE;
  32. if (S_ISREG(mode))
  33. type = APR_REG;
  34. else if (S_ISDIR(mode))
  35. type = APR_DIR;
  36. else if (S_ISCHR(mode))
  37. type = APR_CHR;
  38. else if (S_ISBLK(mode))
  39. type = APR_BLK;
  40. else if (S_ISFIFO(mode))
  41. type = APR_PIPE;
  42. else if (S_ISLNK(mode))
  43. type = APR_LNK;
  44. else if (S_ISSOCK(mode))
  45. type = APR_SOCK;
  46. else
  47. type = APR_UNKFILE;
  48. return type;
  49. }
  50. static void fill_out_finfo(apr_finfo_t *finfo, struct stat *info,
  51. apr_int32_t wanted)
  52. {
  53. finfo->valid = APR_FINFO_MIN | APR_FINFO_IDENT | APR_FINFO_NLINK
  54. | APR_FINFO_OWNER | APR_FINFO_PROT;
  55. finfo->protection = apr_unix_mode2perms(info->st_mode);
  56. finfo->filetype = filetype_from_mode(info->st_mode);
  57. finfo->user = info->st_uid;
  58. finfo->group = info->st_gid;
  59. finfo->size = info->st_size;
  60. finfo->inode = info->st_ino;
  61. finfo->device = info->st_dev;
  62. finfo->nlink = info->st_nlink;
  63. apr_time_ansi_put(&finfo->atime, info->st_atime.tv_sec);
  64. apr_time_ansi_put(&finfo->mtime, info->st_mtime.tv_sec);
  65. apr_time_ansi_put(&finfo->ctime, info->st_ctime.tv_sec);
  66. #ifdef HAVE_STRUCT_STAT_ST_BLOCKS
  67. #ifdef DEV_BSIZE
  68. finfo->csize = (apr_off_t)info->st_blocks * (apr_off_t)DEV_BSIZE;
  69. #else
  70. finfo->csize = (apr_off_t)info->st_blocks * (apr_off_t)512;
  71. #endif
  72. finfo->valid |= APR_FINFO_CSIZE;
  73. #endif
  74. }
  75. apr_status_t apr_file_info_get_locked(apr_finfo_t *finfo, apr_int32_t wanted,
  76. apr_file_t *thefile)
  77. {
  78. struct_stat info;
  79. if (thefile->buffered) {
  80. apr_status_t rv = apr_file_flush_locked(thefile);
  81. if (rv != APR_SUCCESS)
  82. return rv;
  83. }
  84. if (fstat(thefile->filedes, &info) == 0) {
  85. finfo->pool = thefile->pool;
  86. finfo->fname = thefile->fname;
  87. fill_out_finfo(finfo, &info, wanted);
  88. return (wanted & ~finfo->valid) ? APR_INCOMPLETE : APR_SUCCESS;
  89. }
  90. else {
  91. return errno;
  92. }
  93. }
  94. APR_DECLARE(apr_status_t) apr_file_info_get(apr_finfo_t *finfo,
  95. apr_int32_t wanted,
  96. apr_file_t *thefile)
  97. {
  98. struct stat info;
  99. if (thefile->buffered) {
  100. /* XXX: flush here is not mutex protected */
  101. apr_status_t rv = apr_file_flush(thefile);
  102. if (rv != APR_SUCCESS)
  103. return rv;
  104. }
  105. if (fstat(thefile->filedes, &info) == 0) {
  106. finfo->pool = thefile->pool;
  107. finfo->fname = thefile->fname;
  108. fill_out_finfo(finfo, &info, wanted);
  109. return (wanted & ~finfo->valid) ? APR_INCOMPLETE : APR_SUCCESS;
  110. }
  111. else {
  112. return errno;
  113. }
  114. }
  115. APR_DECLARE(apr_status_t) apr_file_perms_set(const char *fname,
  116. apr_fileperms_t perms)
  117. {
  118. mode_t mode = apr_unix_perms2mode(perms);
  119. if (chmod(fname, mode) == -1)
  120. return errno;
  121. return APR_SUCCESS;
  122. }
  123. APR_DECLARE(apr_status_t) apr_file_attrs_set(const char *fname,
  124. apr_fileattrs_t attributes,
  125. apr_fileattrs_t attr_mask,
  126. apr_pool_t *pool)
  127. {
  128. apr_status_t status;
  129. apr_finfo_t finfo;
  130. /* Don't do anything if we can't handle the requested attributes */
  131. if (!(attr_mask & (APR_FILE_ATTR_READONLY
  132. | APR_FILE_ATTR_EXECUTABLE)))
  133. return APR_SUCCESS;
  134. status = apr_stat(&finfo, fname, APR_FINFO_PROT, pool);
  135. if (status)
  136. return status;
  137. /* ### TODO: should added bits be umask'd? */
  138. if (attr_mask & APR_FILE_ATTR_READONLY)
  139. {
  140. if (attributes & APR_FILE_ATTR_READONLY)
  141. {
  142. finfo.protection &= ~APR_UWRITE;
  143. finfo.protection &= ~APR_GWRITE;
  144. finfo.protection &= ~APR_WWRITE;
  145. }
  146. else
  147. {
  148. /* ### umask this! */
  149. finfo.protection |= APR_UWRITE;
  150. finfo.protection |= APR_GWRITE;
  151. finfo.protection |= APR_WWRITE;
  152. }
  153. }
  154. if (attr_mask & APR_FILE_ATTR_EXECUTABLE)
  155. {
  156. if (attributes & APR_FILE_ATTR_EXECUTABLE)
  157. {
  158. /* ### umask this! */
  159. finfo.protection |= APR_UEXECUTE;
  160. finfo.protection |= APR_GEXECUTE;
  161. finfo.protection |= APR_WEXECUTE;
  162. }
  163. else
  164. {
  165. finfo.protection &= ~APR_UEXECUTE;
  166. finfo.protection &= ~APR_GEXECUTE;
  167. finfo.protection &= ~APR_WEXECUTE;
  168. }
  169. }
  170. return apr_file_perms_set(fname, finfo.protection);
  171. }
  172. #ifndef APR_HAS_PSA
  173. static apr_status_t stat_cache_cleanup(void *data)
  174. {
  175. apr_pool_t *p = (apr_pool_t *)getGlobalPool();
  176. apr_hash_index_t *hi;
  177. apr_hash_t *statCache = (apr_hash_t*)data;
  178. char *key;
  179. apr_ssize_t keylen;
  180. NXPathCtx_t pathctx;
  181. for (hi = apr_hash_first(p, statCache); hi; hi = apr_hash_next(hi)) {
  182. apr_hash_this(hi, (const void**)&key, &keylen, (void**)&pathctx);
  183. if (pathctx) {
  184. NXFreePathContext(pathctx);
  185. }
  186. }
  187. return APR_SUCCESS;
  188. }
  189. int cstat (NXPathCtx_t ctx, char *path, struct stat *buf, unsigned long requestmap, apr_pool_t *p)
  190. {
  191. apr_pool_t *gPool = (apr_pool_t *)getGlobalPool();
  192. apr_hash_t *statCache = NULL;
  193. apr_thread_rwlock_t *rwlock = NULL;
  194. NXPathCtx_t pathctx = 0;
  195. char *ptr = NULL, *tr;
  196. int len = 0, x;
  197. char *ppath;
  198. char *pinfo;
  199. if (ctx == 1) {
  200. /* If there isn't a global pool then just stat the file
  201. and return */
  202. if (!gPool) {
  203. char poolname[50];
  204. if (apr_pool_create(&gPool, NULL) != APR_SUCCESS) {
  205. return getstat(ctx, path, buf, requestmap);
  206. }
  207. setGlobalPool(gPool);
  208. apr_pool_tag(gPool, apr_pstrdup(gPool, "cstat_mem_pool"));
  209. statCache = apr_hash_make(gPool);
  210. apr_pool_userdata_set ((void*)statCache, "STAT_CACHE", stat_cache_cleanup, gPool);
  211. apr_thread_rwlock_create(&rwlock, gPool);
  212. apr_pool_userdata_set ((void*)rwlock, "STAT_CACHE_LOCK", apr_pool_cleanup_null, gPool);
  213. }
  214. else {
  215. apr_pool_userdata_get((void**)&statCache, "STAT_CACHE", gPool);
  216. apr_pool_userdata_get((void**)&rwlock, "STAT_CACHE_LOCK", gPool);
  217. }
  218. if (!gPool || !statCache || !rwlock) {
  219. return getstat(ctx, path, buf, requestmap);
  220. }
  221. for (x = 0,tr = path;*tr != '\0';tr++,x++) {
  222. if (*tr == '\\' || *tr == '/') {
  223. ptr = tr;
  224. len = x;
  225. }
  226. if (*tr == ':') {
  227. ptr = "\\";
  228. len = x;
  229. }
  230. }
  231. if (ptr) {
  232. ppath = apr_pstrndup (p, path, len);
  233. strlwr(ppath);
  234. if (ptr[1] != '\0') {
  235. ptr++;
  236. }
  237. /* If the path ended in a trailing slash then our result path
  238. will be a single slash. To avoid stat'ing the root with a
  239. slash, we need to make sure we stat the current directory
  240. with a dot */
  241. if (((*ptr == '/') || (*ptr == '\\')) && (*(ptr+1) == '\0')) {
  242. pinfo = apr_pstrdup (p, ".");
  243. }
  244. else {
  245. pinfo = apr_pstrdup (p, ptr);
  246. }
  247. }
  248. /* If we have a statCache then try to pull the information
  249. from the cache. Otherwise just stat the file and return.*/
  250. if (statCache) {
  251. apr_thread_rwlock_rdlock(rwlock);
  252. pathctx = (NXPathCtx_t) apr_hash_get(statCache, ppath, APR_HASH_KEY_STRING);
  253. apr_thread_rwlock_unlock(rwlock);
  254. if (pathctx) {
  255. return getstat(pathctx, pinfo, buf, requestmap);
  256. }
  257. else {
  258. int err;
  259. err = NXCreatePathContext(0, ppath, 0, NULL, &pathctx);
  260. if (!err) {
  261. apr_thread_rwlock_wrlock(rwlock);
  262. apr_hash_set(statCache, apr_pstrdup(gPool,ppath) , APR_HASH_KEY_STRING, (void*)pathctx);
  263. apr_thread_rwlock_unlock(rwlock);
  264. return getstat(pathctx, pinfo, buf, requestmap);
  265. }
  266. }
  267. }
  268. }
  269. return getstat(ctx, path, buf, requestmap);
  270. }
  271. #endif
  272. APR_DECLARE(apr_status_t) apr_stat(apr_finfo_t *finfo,
  273. const char *fname,
  274. apr_int32_t wanted, apr_pool_t *pool)
  275. {
  276. struct stat info;
  277. int srv;
  278. NXPathCtx_t pathCtx = 0;
  279. getcwdpath(NULL, &pathCtx, CTX_ACTUAL_CWD);
  280. #ifdef APR_HAS_PSA
  281. srv = getstat(pathCtx, (char*)fname, &info, ST_STAT_BITS|ST_NAME_BIT);
  282. #else
  283. srv = cstat(pathCtx, (char*)fname, &info, ST_STAT_BITS|ST_NAME_BIT, pool);
  284. #endif
  285. errno = srv;
  286. if (srv == 0) {
  287. finfo->pool = pool;
  288. finfo->fname = fname;
  289. fill_out_finfo(finfo, &info, wanted);
  290. if (wanted & APR_FINFO_LINK)
  291. wanted &= ~APR_FINFO_LINK;
  292. if (wanted & APR_FINFO_NAME) {
  293. finfo->name = apr_pstrdup(pool, info.st_name);
  294. finfo->valid |= APR_FINFO_NAME;
  295. }
  296. return (wanted & ~finfo->valid) ? APR_INCOMPLETE : APR_SUCCESS;
  297. }
  298. else {
  299. #if !defined(ENOENT) || !defined(ENOTDIR)
  300. #error ENOENT || ENOTDIR not defined; please see the
  301. #error comments at this line in the source for a workaround.
  302. /*
  303. * If ENOENT || ENOTDIR is not defined in one of the your OS's
  304. * include files, APR cannot report a good reason why the stat()
  305. * of the file failed; there are cases where it can fail even though
  306. * the file exists. This opens holes in Apache, for example, because
  307. * it becomes possible for someone to get a directory listing of a
  308. * directory even though there is an index (eg. index.html) file in
  309. * it. If you do not have a problem with this, delete the above
  310. * #error lines and start the compile again. If you need to do this,
  311. * please submit a bug report to http://www.apache.org/bug_report.html
  312. * letting us know that you needed to do this. Please be sure to
  313. * include the operating system you are using.
  314. */
  315. /* WARNING: All errors will be handled as not found
  316. */
  317. #if !defined(ENOENT)
  318. return APR_ENOENT;
  319. #else
  320. /* WARNING: All errors but not found will be handled as not directory
  321. */
  322. if (errno != ENOENT)
  323. return APR_ENOENT;
  324. else
  325. return errno;
  326. #endif
  327. #else /* All was defined well, report the usual: */
  328. return errno;
  329. #endif
  330. }
  331. }
  332. APR_DECLARE(apr_status_t) apr_file_mtime_set(const char *fname,
  333. apr_time_t mtime,
  334. apr_pool_t *pool)
  335. {
  336. apr_status_t status;
  337. apr_finfo_t finfo;
  338. status = apr_stat(&finfo, fname, APR_FINFO_ATIME, pool);
  339. if (status) {
  340. return status;
  341. }
  342. #ifdef HAVE_UTIMES
  343. {
  344. struct timeval tvp[2];
  345. tvp[0].tv_sec = apr_time_sec(finfo.atime);
  346. tvp[0].tv_usec = apr_time_usec(finfo.atime);
  347. tvp[1].tv_sec = apr_time_sec(mtime);
  348. tvp[1].tv_usec = apr_time_usec(mtime);
  349. if (utimes(fname, tvp) == -1) {
  350. return errno;
  351. }
  352. }
  353. #elif defined(HAVE_UTIME)
  354. {
  355. struct utimbuf buf;
  356. buf.actime = (time_t) (finfo.atime / APR_USEC_PER_SEC);
  357. buf.modtime = (time_t) (mtime / APR_USEC_PER_SEC);
  358. if (utime(fname, &buf) == -1) {
  359. return errno;
  360. }
  361. }
  362. #else
  363. return APR_ENOTIMPL;
  364. #endif
  365. return APR_SUCCESS;
  366. }