dir.c 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323
  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 "fspr_arch_file_io.h"
  17. #include "fspr_strings.h"
  18. #include "fspr_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 fspr_status_t dir_cleanup(void *thedir)
  26. {
  27. fspr_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, fspr_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. fspr_size_t len = strlen (path);
  42. fspr_size_t orig_len = len;
  43. while ((len > 0) && (path[len - 1] == PATH_SEPARATOR))
  44. len--;
  45. if (len != orig_len)
  46. return fspr_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, fspr_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 fspr_pstrndup (pool, path, (i < 0) ? 0 : i);
  60. }
  61. fspr_status_t fspr_dir_open(fspr_dir_t **new, const char *dirname,
  62. fspr_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. fspr_size_t dirent_size =
  70. (sizeof((*new)->entry->d_name) > 1 ?
  71. sizeof(struct dirent) : sizeof (struct dirent) + 255);
  72. DIR *dir = opendir(dirname);
  73. if (!dir) {
  74. return errno;
  75. }
  76. (*new) = (fspr_dir_t *)fspr_palloc(pool, sizeof(fspr_dir_t));
  77. (*new)->pool = pool;
  78. (*new)->dirname = fspr_pstrdup(pool, dirname);
  79. (*new)->dirstruct = dir;
  80. (*new)->entry = fspr_pcalloc(pool, dirent_size);
  81. fspr_pool_cleanup_register((*new)->pool, *new, dir_cleanup,
  82. fspr_pool_cleanup_null);
  83. return APR_SUCCESS;
  84. }
  85. fspr_status_t fspr_dir_close(fspr_dir_t *thedir)
  86. {
  87. return fspr_pool_cleanup_run(thedir->pool, thedir, dir_cleanup);
  88. }
  89. #ifdef DIRENT_TYPE
  90. static fspr_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. fspr_status_t fspr_dir_read(fspr_finfo_t *finfo, fspr_int32_t wanted,
  117. fspr_dir_t *thedir)
  118. {
  119. fspr_status_t ret = 0;
  120. #ifdef DIRENT_TYPE
  121. fspr_filetype_e type;
  122. #endif
  123. #if APR_HAS_THREADS && defined(_POSIX_THREAD_SAFE_FUNCTIONS) \
  124. && !defined(READDIR_IS_THREAD_SAFE)
  125. struct dirent *retent;
  126. ret = readdir_r(thedir->dirstruct, thedir->entry, &retent);
  127. /* Avoid the Linux problem where at end-of-directory thedir->entry
  128. * is set to NULL, but ret = APR_SUCCESS.
  129. */
  130. if(!ret && thedir->entry != retent)
  131. ret = APR_ENOENT;
  132. /* Solaris is a bit strange, if there are no more entries in the
  133. * directory, it returns EINVAL. Since this is against POSIX, we
  134. * hack around the problem here. EINVAL is possible from other
  135. * readdir implementations, but only if the result buffer is too small.
  136. * since we control the size of that buffer, we should never have
  137. * that problem.
  138. */
  139. if (ret == EINVAL) {
  140. ret = ENOENT;
  141. }
  142. #else
  143. /* We're about to call a non-thread-safe readdir() that may
  144. possibly set `errno', and the logic below actually cares about
  145. errno after the call. Therefore we need to clear errno first. */
  146. errno = 0;
  147. thedir->entry = readdir(thedir->dirstruct);
  148. if (thedir->entry == NULL) {
  149. /* If NULL was returned, this can NEVER be a success. Can it?! */
  150. if (errno == APR_SUCCESS) {
  151. ret = APR_ENOENT;
  152. }
  153. else
  154. ret = errno;
  155. }
  156. #endif
  157. /* No valid bit flag to test here - do we want one? */
  158. finfo->fname = NULL;
  159. if (ret) {
  160. finfo->valid = 0;
  161. return ret;
  162. }
  163. #ifdef DIRENT_TYPE
  164. type = filetype_from_dirent_type(thedir->entry->DIRENT_TYPE);
  165. if (type != APR_UNKFILE) {
  166. wanted &= ~APR_FINFO_TYPE;
  167. }
  168. #endif
  169. #ifdef DIRENT_INODE
  170. if (thedir->entry->DIRENT_INODE && thedir->entry->DIRENT_INODE != -1) {
  171. wanted &= ~APR_FINFO_INODE;
  172. }
  173. #endif
  174. wanted &= ~APR_FINFO_NAME;
  175. if (wanted)
  176. {
  177. char fspec[APR_PATH_MAX];
  178. int off;
  179. fspr_cpystrn(fspec, thedir->dirname, sizeof(fspec));
  180. off = strlen(fspec);
  181. if ((fspec[off - 1] != '/') && (off + 1 < sizeof(fspec)))
  182. fspec[off++] = '/';
  183. fspr_cpystrn(fspec + off, thedir->entry->d_name, sizeof(fspec) - off);
  184. ret = fspr_stat(finfo, fspec, APR_FINFO_LINK | wanted, thedir->pool);
  185. /* We passed a stack name that will disappear */
  186. finfo->fname = NULL;
  187. }
  188. if (wanted && (ret == APR_SUCCESS || ret == APR_INCOMPLETE)) {
  189. wanted &= ~finfo->valid;
  190. }
  191. else {
  192. /* We don't bail because we fail to stat, when we are only -required-
  193. * to readdir... but the result will be APR_INCOMPLETE
  194. */
  195. finfo->pool = thedir->pool;
  196. finfo->valid = 0;
  197. #ifdef DIRENT_TYPE
  198. if (type != APR_UNKFILE) {
  199. finfo->filetype = type;
  200. finfo->valid |= APR_FINFO_TYPE;
  201. }
  202. #endif
  203. #ifdef DIRENT_INODE
  204. if (thedir->entry->DIRENT_INODE && thedir->entry->DIRENT_INODE != -1) {
  205. finfo->inode = thedir->entry->DIRENT_INODE;
  206. finfo->valid |= APR_FINFO_INODE;
  207. }
  208. #endif
  209. }
  210. finfo->name = fspr_pstrdup(thedir->pool, thedir->entry->d_name);
  211. finfo->valid |= APR_FINFO_NAME;
  212. if (wanted)
  213. return APR_INCOMPLETE;
  214. return APR_SUCCESS;
  215. }
  216. fspr_status_t fspr_dir_rewind(fspr_dir_t *thedir)
  217. {
  218. rewinddir(thedir->dirstruct);
  219. return APR_SUCCESS;
  220. }
  221. fspr_status_t fspr_dir_make(const char *path, fspr_fileperms_t perm,
  222. fspr_pool_t *pool)
  223. {
  224. mode_t mode = fspr_unix_perms2mode(perm);
  225. if (mkdir(path, mode) == 0) {
  226. return APR_SUCCESS;
  227. }
  228. else {
  229. return errno;
  230. }
  231. }
  232. fspr_status_t fspr_dir_make_recursive(const char *path, fspr_fileperms_t perm,
  233. fspr_pool_t *pool)
  234. {
  235. fspr_status_t fspr_err = 0;
  236. fspr_err = fspr_dir_make (path, perm, pool); /* Try to make PATH right out */
  237. if (fspr_err == EEXIST) /* It's OK if PATH exists */
  238. return APR_SUCCESS;
  239. if (fspr_err == ENOENT) { /* Missing an intermediate dir */
  240. char *dir;
  241. dir = path_remove_last_component(path, pool);
  242. /* If there is no path left, give up. */
  243. if (dir[0] == '\0') {
  244. return fspr_err;
  245. }
  246. fspr_err = fspr_dir_make_recursive(dir, perm, pool);
  247. if (!fspr_err)
  248. fspr_err = fspr_dir_make (path, perm, pool);
  249. }
  250. return fspr_err;
  251. }
  252. fspr_status_t fspr_dir_remove(const char *path, fspr_pool_t *pool)
  253. {
  254. if (rmdir(path) == 0) {
  255. return APR_SUCCESS;
  256. }
  257. else {
  258. return errno;
  259. }
  260. }
  261. fspr_status_t fspr_os_dir_get(fspr_os_dir_t **thedir, fspr_dir_t *dir)
  262. {
  263. if (dir == NULL) {
  264. return APR_ENODIR;
  265. }
  266. *thedir = dir->dirstruct;
  267. return APR_SUCCESS;
  268. }
  269. fspr_status_t fspr_os_dir_put(fspr_dir_t **dir, fspr_os_dir_t *thedir,
  270. fspr_pool_t *pool)
  271. {
  272. if ((*dir) == NULL) {
  273. (*dir) = (fspr_dir_t *)fspr_pcalloc(pool, sizeof(fspr_dir_t));
  274. (*dir)->pool = pool;
  275. }
  276. (*dir)->dirstruct = thedir;
  277. return APR_SUCCESS;
  278. }