dir.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  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 "apr_strings.h"
  18. #include "apr_portable.h"
  19. #if APR_HAVE_SYS_SYSLIMITS_H
  20. #include <sys/syslimits.h>
  21. #endif
  22. #if APR_HAVE_LIMITS_H
  23. #include <limits.h>
  24. #endif
  25. static apr_status_t dir_cleanup(void *thedir)
  26. {
  27. apr_dir_t *dir = thedir;
  28. if (closedir(dir->dirstruct) == 0) {
  29. return APR_SUCCESS;
  30. }
  31. else {
  32. return errno;
  33. }
  34. }
  35. #define PATH_SEPARATOR '/'
  36. /* Remove trailing separators that don't affect the meaning of PATH. */
  37. static const char *path_canonicalize (const char *path, apr_pool_t *pool)
  38. {
  39. /* At some point this could eliminate redundant components. For
  40. * now, it just makes sure there is no trailing slash. */
  41. apr_size_t len = strlen (path);
  42. apr_size_t orig_len = len;
  43. while ((len > 0) && (path[len - 1] == PATH_SEPARATOR))
  44. len--;
  45. if (len != orig_len)
  46. return apr_pstrndup (pool, path, len);
  47. else
  48. return path;
  49. }
  50. /* Remove one component off the end of PATH. */
  51. static char *path_remove_last_component (const char *path, apr_pool_t *pool)
  52. {
  53. const char *newpath = path_canonicalize (path, pool);
  54. int i;
  55. for (i = (strlen(newpath) - 1); i >= 0; i--) {
  56. if (path[i] == PATH_SEPARATOR)
  57. break;
  58. }
  59. return apr_pstrndup (pool, path, (i < 0) ? 0 : i);
  60. }
  61. apr_status_t apr_dir_open(apr_dir_t **new, const char *dirname,
  62. apr_pool_t *pool)
  63. {
  64. /* On some platforms (e.g., Linux+GNU libc), d_name[] in struct
  65. * dirent is declared with enough storage for the name. On other
  66. * platforms (e.g., Solaris 8 for Intel), d_name is declared as a
  67. * one-byte array. Note: gcc evaluates this at compile time.
  68. */
  69. apr_size_t dirent_size =
  70. sizeof(*(*new)->entry) +
  71. (sizeof((*new)->entry->d_name) > 1 ? 0 : 255);
  72. DIR *dir = opendir(dirname);
  73. if (!dir) {
  74. return errno;
  75. }
  76. (*new) = (apr_dir_t *)apr_palloc(pool, sizeof(apr_dir_t));
  77. (*new)->pool = pool;
  78. (*new)->dirname = apr_pstrdup(pool, dirname);
  79. (*new)->dirstruct = dir;
  80. (*new)->entry = apr_pcalloc(pool, dirent_size);
  81. apr_pool_cleanup_register((*new)->pool, *new, dir_cleanup,
  82. apr_pool_cleanup_null);
  83. return APR_SUCCESS;
  84. }
  85. apr_status_t apr_dir_close(apr_dir_t *thedir)
  86. {
  87. return apr_pool_cleanup_run(thedir->pool, thedir, dir_cleanup);
  88. }
  89. #ifdef DIRENT_TYPE
  90. static apr_filetype_e filetype_from_dirent_type(int type)
  91. {
  92. switch (type) {
  93. case DT_REG:
  94. return APR_REG;
  95. case DT_DIR:
  96. return APR_DIR;
  97. case DT_LNK:
  98. return APR_LNK;
  99. case DT_CHR:
  100. return APR_CHR;
  101. case DT_BLK:
  102. return APR_BLK;
  103. #if defined(DT_FIFO)
  104. case DT_FIFO:
  105. return APR_PIPE;
  106. #endif
  107. #if !defined(BEOS) && defined(DT_SOCK)
  108. case DT_SOCK:
  109. return APR_SOCK;
  110. #endif
  111. default:
  112. return APR_UNKFILE;
  113. }
  114. }
  115. #endif
  116. apr_status_t apr_dir_read(apr_finfo_t *finfo, apr_int32_t wanted,
  117. apr_dir_t *thedir)
  118. {
  119. apr_status_t ret = 0;
  120. #ifdef DIRENT_TYPE
  121. apr_filetype_e type;
  122. #endif
  123. #if APR_HAS_THREADS && defined(_POSIX_THREAD_SAFE_FUNCTIONS) \
  124. && !defined(READDIR_IS_THREAD_SAFE)
  125. #ifdef APR_USE_READDIR64_R
  126. struct dirent64 *retent;
  127. /* If LFS is enabled and readdir64_r is available, readdir64_r is
  128. * used in preference to readdir_r. This allows directories to be
  129. * read which contain a (64-bit) inode number which doesn't fit
  130. * into the 32-bit apr_ino_t, iff the caller doesn't actually care
  131. * about the inode number (i.e. wanted & APR_FINFO_INODE == 0).
  132. * (such inodes may be seen in some wonky NFS environments)
  133. *
  134. * Similarly, if the d_off field cannot be reprented in a 32-bit
  135. * offset, the libc readdir_r() would barf; using readdir64_r
  136. * bypasses that case entirely since APR does not care about
  137. * d_off. */
  138. ret = readdir64_r(thedir->dirstruct, thedir->entry, &retent);
  139. #else
  140. struct dirent *retent;
  141. ret = readdir_r(thedir->dirstruct, thedir->entry, &retent);
  142. #endif
  143. /* POSIX treats "end of directory" as a non-error case, so ret
  144. * will be zero and retent will be set to NULL in that case. */
  145. if (!ret && retent == NULL) {
  146. ret = APR_ENOENT;
  147. }
  148. /* Solaris is a bit strange, if there are no more entries in the
  149. * directory, it returns EINVAL. Since this is against POSIX, we
  150. * hack around the problem here. EINVAL is possible from other
  151. * readdir implementations, but only if the result buffer is too small.
  152. * since we control the size of that buffer, we should never have
  153. * that problem.
  154. */
  155. if (ret == EINVAL) {
  156. ret = APR_ENOENT;
  157. }
  158. #else
  159. /* We're about to call a non-thread-safe readdir() that may
  160. possibly set `errno', and the logic below actually cares about
  161. errno after the call. Therefore we need to clear errno first. */
  162. errno = 0;
  163. thedir->entry = readdir(thedir->dirstruct);
  164. if (thedir->entry == NULL) {
  165. /* If NULL was returned, this can NEVER be a success. Can it?! */
  166. if (errno == APR_SUCCESS) {
  167. ret = APR_ENOENT;
  168. }
  169. else
  170. ret = errno;
  171. }
  172. #endif
  173. /* No valid bit flag to test here - do we want one? */
  174. finfo->fname = NULL;
  175. if (ret) {
  176. finfo->valid = 0;
  177. return ret;
  178. }
  179. #ifdef DIRENT_TYPE
  180. type = filetype_from_dirent_type(thedir->entry->DIRENT_TYPE);
  181. if (type != APR_UNKFILE) {
  182. wanted &= ~APR_FINFO_TYPE;
  183. }
  184. #endif
  185. #ifdef DIRENT_INODE
  186. if (thedir->entry->DIRENT_INODE && thedir->entry->DIRENT_INODE != -1) {
  187. #ifdef APR_USE_READDIR64_R
  188. /* If readdir64_r is used, check for the overflow case of trying
  189. * to fit a 64-bit integer into a 32-bit integer. */
  190. if (sizeof(apr_ino_t) >= sizeof(retent->DIRENT_INODE)
  191. || (apr_ino_t)retent->DIRENT_INODE == retent->DIRENT_INODE) {
  192. wanted &= ~APR_FINFO_INODE;
  193. } else {
  194. /* Prevent the fallback code below from filling in the
  195. * inode if the stat call fails. */
  196. retent->DIRENT_INODE = 0;
  197. }
  198. #else
  199. wanted &= ~APR_FINFO_INODE;
  200. #endif /* APR_USE_READDIR64_R */
  201. }
  202. #endif /* DIRENT_INODE */
  203. wanted &= ~APR_FINFO_NAME;
  204. if (wanted)
  205. {
  206. char fspec[APR_PATH_MAX];
  207. char *end;
  208. end = apr_cpystrn(fspec, thedir->dirname, sizeof fspec);
  209. if (end > fspec && end[-1] != '/' && (end < fspec + APR_PATH_MAX))
  210. *end++ = '/';
  211. apr_cpystrn(end, thedir->entry->d_name,
  212. sizeof fspec - (end - fspec));
  213. ret = apr_stat(finfo, fspec, APR_FINFO_LINK | wanted, thedir->pool);
  214. /* We passed a stack name that will disappear */
  215. finfo->fname = NULL;
  216. }
  217. if (wanted && (ret == APR_SUCCESS || ret == APR_INCOMPLETE)) {
  218. wanted &= ~finfo->valid;
  219. }
  220. else {
  221. /* We don't bail because we fail to stat, when we are only -required-
  222. * to readdir... but the result will be APR_INCOMPLETE
  223. */
  224. finfo->pool = thedir->pool;
  225. finfo->valid = 0;
  226. #ifdef DIRENT_TYPE
  227. if (type != APR_UNKFILE) {
  228. finfo->filetype = type;
  229. finfo->valid |= APR_FINFO_TYPE;
  230. }
  231. #endif
  232. #ifdef DIRENT_INODE
  233. if (thedir->entry->DIRENT_INODE && thedir->entry->DIRENT_INODE != -1) {
  234. finfo->inode = thedir->entry->DIRENT_INODE;
  235. finfo->valid |= APR_FINFO_INODE;
  236. }
  237. #endif
  238. }
  239. finfo->name = apr_pstrdup(thedir->pool, thedir->entry->d_name);
  240. finfo->valid |= APR_FINFO_NAME;
  241. if (wanted)
  242. return APR_INCOMPLETE;
  243. return APR_SUCCESS;
  244. }
  245. apr_status_t apr_dir_rewind(apr_dir_t *thedir)
  246. {
  247. rewinddir(thedir->dirstruct);
  248. return APR_SUCCESS;
  249. }
  250. apr_status_t apr_dir_make(const char *path, apr_fileperms_t perm,
  251. apr_pool_t *pool)
  252. {
  253. mode_t mode = apr_unix_perms2mode(perm);
  254. if (mkdir(path, mode) == 0) {
  255. return APR_SUCCESS;
  256. }
  257. else {
  258. return errno;
  259. }
  260. }
  261. apr_status_t apr_dir_make_recursive(const char *path, apr_fileperms_t perm,
  262. apr_pool_t *pool)
  263. {
  264. apr_status_t apr_err = 0;
  265. apr_err = apr_dir_make (path, perm, pool); /* Try to make PATH right out */
  266. if (apr_err == ENOENT) { /* Missing an intermediate dir */
  267. char *dir;
  268. dir = path_remove_last_component(path, pool);
  269. /* If there is no path left, give up. */
  270. if (dir[0] == '\0') {
  271. return apr_err;
  272. }
  273. apr_err = apr_dir_make_recursive(dir, perm, pool);
  274. if (!apr_err)
  275. apr_err = apr_dir_make (path, perm, pool);
  276. }
  277. /*
  278. * It's OK if PATH exists. Timing issues can lead to the second
  279. * apr_dir_make being called on existing dir, therefore this check
  280. * has to come last.
  281. */
  282. if (APR_STATUS_IS_EEXIST(apr_err))
  283. return APR_SUCCESS;
  284. return apr_err;
  285. }
  286. apr_status_t apr_dir_remove(const char *path, apr_pool_t *pool)
  287. {
  288. if (rmdir(path) == 0) {
  289. return APR_SUCCESS;
  290. }
  291. else {
  292. return errno;
  293. }
  294. }
  295. apr_status_t apr_os_dir_get(apr_os_dir_t **thedir, apr_dir_t *dir)
  296. {
  297. if (dir == NULL) {
  298. return APR_ENODIR;
  299. }
  300. *thedir = dir->dirstruct;
  301. return APR_SUCCESS;
  302. }
  303. apr_status_t apr_os_dir_put(apr_dir_t **dir, apr_os_dir_t *thedir,
  304. apr_pool_t *pool)
  305. {
  306. if ((*dir) == NULL) {
  307. (*dir) = (apr_dir_t *)apr_pcalloc(pool, sizeof(apr_dir_t));
  308. (*dir)->pool = pool;
  309. }
  310. (*dir)->dirstruct = thedir;
  311. return APR_SUCCESS;
  312. }