dir.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393
  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 "apr_arch_file_io.h"
  18. #include "apr_file_io.h"
  19. #include "apr_strings.h"
  20. #include "apr_portable.h"
  21. #include "apr_arch_atime.h"
  22. #if APR_HAVE_ERRNO_H
  23. #include <errno.h>
  24. #endif
  25. #if APR_HAVE_STRING_H
  26. #include <string.h>
  27. #endif
  28. #if APR_HAVE_DIRENT_H
  29. #include <dirent.h>
  30. #endif
  31. #ifdef HAVE_SYS_STAT_H
  32. #include <sys/stat.h>
  33. #endif
  34. static apr_status_t dir_cleanup(void *thedir)
  35. {
  36. apr_dir_t *dir = thedir;
  37. if (dir->dirhand != INVALID_HANDLE_VALUE && !FindClose(dir->dirhand)) {
  38. return apr_get_os_error();
  39. }
  40. dir->dirhand = INVALID_HANDLE_VALUE;
  41. return APR_SUCCESS;
  42. }
  43. APR_DECLARE(apr_status_t) apr_dir_open(apr_dir_t **new, const char *dirname,
  44. apr_pool_t *pool)
  45. {
  46. apr_status_t rv;
  47. apr_size_t len = strlen(dirname);
  48. (*new) = apr_pcalloc(pool, sizeof(apr_dir_t));
  49. /* Leave room here to add and pop the '*' wildcard for FindFirstFile
  50. * and double-null terminate so we have one character to change.
  51. */
  52. (*new)->dirname = apr_palloc(pool, len + 3);
  53. memcpy((*new)->dirname, dirname, len);
  54. if (len && (*new)->dirname[len - 1] != '/') {
  55. (*new)->dirname[len++] = '/';
  56. }
  57. (*new)->dirname[len++] = '\0';
  58. (*new)->dirname[len] = '\0';
  59. #if APR_HAS_UNICODE_FS
  60. IF_WIN_OS_IS_UNICODE
  61. {
  62. /* Create a buffer for the longest file name we will ever see
  63. */
  64. (*new)->w.entry = apr_pcalloc(pool, sizeof(WIN32_FIND_DATAW));
  65. (*new)->name = apr_pcalloc(pool, APR_FILE_MAX * 3 + 1);
  66. }
  67. #endif
  68. #if APR_HAS_ANSI_FS
  69. ELSE_WIN_OS_IS_ANSI
  70. {
  71. /* Note that we won't open a directory that is greater than MAX_PATH,
  72. * including the trailing /* wildcard suffix. If a * won't fit, then
  73. * neither will any other file name within the directory.
  74. * The length not including the trailing '*' is stored as rootlen, to
  75. * skip over all paths which are too long.
  76. */
  77. if (len >= APR_PATH_MAX) {
  78. (*new) = NULL;
  79. return APR_ENAMETOOLONG;
  80. }
  81. (*new)->n.entry = apr_pcalloc(pool, sizeof(WIN32_FIND_DATAW));
  82. }
  83. #endif
  84. (*new)->rootlen = len - 1;
  85. (*new)->pool = pool;
  86. (*new)->dirhand = INVALID_HANDLE_VALUE;
  87. apr_pool_cleanup_register((*new)->pool, (void *)(*new), dir_cleanup,
  88. apr_pool_cleanup_null);
  89. rv = apr_dir_read(NULL, 0, *new);
  90. if (rv != APR_SUCCESS) {
  91. dir_cleanup(*new);
  92. *new = NULL;
  93. }
  94. return rv;
  95. }
  96. APR_DECLARE(apr_status_t) apr_dir_close(apr_dir_t *dir)
  97. {
  98. apr_pool_cleanup_kill(dir->pool, dir, dir_cleanup);
  99. return dir_cleanup(dir);
  100. }
  101. APR_DECLARE(apr_status_t) apr_dir_read(apr_finfo_t *finfo, apr_int32_t wanted,
  102. apr_dir_t *thedir)
  103. {
  104. apr_status_t rv;
  105. char *fname;
  106. /* The while loops below allow us to skip all invalid file names, so that
  107. * we aren't reporting any files where their absolute paths are too long.
  108. */
  109. #if APR_HAS_UNICODE_FS
  110. apr_wchar_t wdirname[APR_PATH_MAX];
  111. apr_wchar_t *eos = NULL;
  112. IF_WIN_OS_IS_UNICODE
  113. {
  114. /* This code path is always be invoked by apr_dir_open or
  115. * apr_dir_rewind, so return without filling out the finfo.
  116. */
  117. if (thedir->dirhand == INVALID_HANDLE_VALUE)
  118. {
  119. apr_status_t rv;
  120. if (rv = utf8_to_unicode_path(wdirname, sizeof(wdirname)
  121. / sizeof(apr_wchar_t),
  122. thedir->dirname)) {
  123. return rv;
  124. }
  125. eos = wcschr(wdirname, '\0');
  126. eos[0] = '*';
  127. eos[1] = '\0';
  128. thedir->dirhand = FindFirstFileW(wdirname, thedir->w.entry);
  129. eos[0] = '\0';
  130. if (thedir->dirhand == INVALID_HANDLE_VALUE) {
  131. return apr_get_os_error();
  132. }
  133. thedir->bof = 1;
  134. return APR_SUCCESS;
  135. }
  136. else if (thedir->bof) {
  137. /* Noop - we already called FindFirstFileW from
  138. * either apr_dir_open or apr_dir_rewind ... use
  139. * that first record.
  140. */
  141. thedir->bof = 0;
  142. }
  143. else if (!FindNextFileW(thedir->dirhand, thedir->w.entry)) {
  144. return apr_get_os_error();
  145. }
  146. while (thedir->rootlen &&
  147. thedir->rootlen + wcslen(thedir->w.entry->cFileName) >= APR_PATH_MAX)
  148. {
  149. if (!FindNextFileW(thedir->dirhand, thedir->w.entry)) {
  150. return apr_get_os_error();
  151. }
  152. }
  153. if (rv = unicode_to_utf8_path(thedir->name, APR_FILE_MAX * 3 + 1,
  154. thedir->w.entry->cFileName))
  155. return rv;
  156. fname = thedir->name;
  157. }
  158. #endif
  159. #if APR_HAS_ANSI_FS
  160. ELSE_WIN_OS_IS_ANSI
  161. {
  162. /* This code path is always be invoked by apr_dir_open or
  163. * apr_dir_rewind, so return without filling out the finfo.
  164. */
  165. if (thedir->dirhand == INVALID_HANDLE_VALUE) {
  166. /* '/' terminated, so add the '*' and pop it when we finish */
  167. char *eop = strchr(thedir->dirname, '\0');
  168. eop[0] = '*';
  169. eop[1] = '\0';
  170. thedir->dirhand = FindFirstFileA(thedir->dirname,
  171. thedir->n.entry);
  172. eop[0] = '\0';
  173. if (thedir->dirhand == INVALID_HANDLE_VALUE) {
  174. return apr_get_os_error();
  175. }
  176. thedir->bof = 1;
  177. return APR_SUCCESS;
  178. }
  179. else if (thedir->bof) {
  180. /* Noop - we already called FindFirstFileW from
  181. * either apr_dir_open or apr_dir_rewind ... use
  182. * that first record.
  183. */
  184. thedir->bof = 0;
  185. }
  186. else if (!FindNextFile(thedir->dirhand, thedir->n.entry)) {
  187. return apr_get_os_error();
  188. }
  189. while (thedir->rootlen &&
  190. thedir->rootlen + strlen(thedir->n.entry->cFileName) >= MAX_PATH)
  191. {
  192. if (!FindNextFileW(thedir->dirhand, thedir->w.entry)) {
  193. return apr_get_os_error();
  194. }
  195. }
  196. fname = thedir->n.entry->cFileName;
  197. }
  198. #endif
  199. fillin_fileinfo(finfo, (WIN32_FILE_ATTRIBUTE_DATA *) thedir->w.entry,
  200. 0, wanted);
  201. finfo->pool = thedir->pool;
  202. finfo->valid |= APR_FINFO_NAME;
  203. finfo->name = fname;
  204. if (wanted &= ~finfo->valid) {
  205. /* Go back and get more_info if we can't answer the whole inquiry
  206. */
  207. #if APR_HAS_UNICODE_FS
  208. IF_WIN_OS_IS_UNICODE
  209. {
  210. /* Almost all our work is done. Tack on the wide file name
  211. * to the end of the wdirname (already / delimited)
  212. */
  213. if (!eos)
  214. eos = wcschr(wdirname, '\0');
  215. wcscpy(eos, thedir->w.entry->cFileName);
  216. rv = more_finfo(finfo, wdirname, wanted, MORE_OF_WFSPEC);
  217. eos[0] = '\0';
  218. return rv;
  219. }
  220. #endif
  221. #if APR_HAS_ANSI_FS
  222. ELSE_WIN_OS_IS_ANSI
  223. {
  224. #if APR_HAS_UNICODE_FS
  225. /* Don't waste stack space on a second buffer, the one we set
  226. * aside for the wide directory name is twice what we need.
  227. */
  228. char *fspec = (char*)wdirname;
  229. #else
  230. char fspec[APR_PATH_MAX];
  231. #endif
  232. apr_size_t dirlen = strlen(thedir->dirname);
  233. if (dirlen >= sizeof(fspec))
  234. dirlen = sizeof(fspec) - 1;
  235. apr_cpystrn(fspec, thedir->dirname, sizeof(fspec));
  236. apr_cpystrn(fspec + dirlen, fname, sizeof(fspec) - dirlen);
  237. return more_finfo(finfo, fspec, wanted, MORE_OF_FSPEC);
  238. }
  239. #endif
  240. }
  241. return APR_SUCCESS;
  242. }
  243. APR_DECLARE(apr_status_t) apr_dir_rewind(apr_dir_t *dir)
  244. {
  245. apr_status_t rv;
  246. /* this will mark the handle as invalid and we'll open it
  247. * again if apr_dir_read() is subsequently called
  248. */
  249. rv = dir_cleanup(dir);
  250. if (rv == APR_SUCCESS)
  251. rv = apr_dir_read(NULL, 0, dir);
  252. return rv;
  253. }
  254. APR_DECLARE(apr_status_t) apr_dir_make(const char *path, apr_fileperms_t perm,
  255. apr_pool_t *pool)
  256. {
  257. #if APR_HAS_UNICODE_FS
  258. IF_WIN_OS_IS_UNICODE
  259. {
  260. apr_wchar_t wpath[APR_PATH_MAX];
  261. apr_status_t rv;
  262. if (rv = utf8_to_unicode_path(wpath, sizeof(wpath)
  263. / sizeof(apr_wchar_t), path)) {
  264. return rv;
  265. }
  266. if (!CreateDirectoryW(wpath, NULL)) {
  267. return apr_get_os_error();
  268. }
  269. }
  270. #endif
  271. #if APR_HAS_ANSI_FS
  272. ELSE_WIN_OS_IS_ANSI
  273. if (!CreateDirectory(path, NULL)) {
  274. return apr_get_os_error();
  275. }
  276. #endif
  277. return APR_SUCCESS;
  278. }
  279. static apr_status_t dir_make_parent(char *path,
  280. apr_fileperms_t perm,
  281. apr_pool_t *pool)
  282. {
  283. apr_status_t rv;
  284. char *ch = strrchr(path, '\\');
  285. if (!ch) {
  286. return APR_ENOENT;
  287. }
  288. *ch = '\0';
  289. rv = apr_dir_make (path, perm, pool); /* Try to make straight off */
  290. if (APR_STATUS_IS_ENOENT(rv)) { /* Missing an intermediate dir */
  291. rv = dir_make_parent(path, perm, pool);
  292. if (rv == APR_SUCCESS) {
  293. rv = apr_dir_make (path, perm, pool); /* And complete the path */
  294. }
  295. }
  296. *ch = '\\'; /* Always replace the slash before returning */
  297. return rv;
  298. }
  299. APR_DECLARE(apr_status_t) apr_dir_make_recursive(const char *path,
  300. apr_fileperms_t perm,
  301. apr_pool_t *pool)
  302. {
  303. apr_status_t rv = 0;
  304. rv = apr_dir_make (path, perm, pool); /* Try to make PATH right out */
  305. if (APR_STATUS_IS_EEXIST(rv)) /* It's OK if PATH exists */
  306. return APR_SUCCESS;
  307. if (APR_STATUS_IS_ENOENT(rv)) { /* Missing an intermediate dir */
  308. char *dir;
  309. rv = apr_filepath_merge(&dir, "", path, APR_FILEPATH_NATIVE, pool);
  310. if (rv == APR_SUCCESS)
  311. rv = dir_make_parent(dir, perm, pool); /* Make intermediate dirs */
  312. if (rv == APR_SUCCESS)
  313. rv = apr_dir_make (dir, perm, pool); /* And complete the path */
  314. }
  315. return rv;
  316. }
  317. APR_DECLARE(apr_status_t) apr_dir_remove(const char *path, apr_pool_t *pool)
  318. {
  319. #if APR_HAS_UNICODE_FS
  320. IF_WIN_OS_IS_UNICODE
  321. {
  322. apr_wchar_t wpath[APR_PATH_MAX];
  323. apr_status_t rv;
  324. if (rv = utf8_to_unicode_path(wpath, sizeof(wpath)
  325. / sizeof(apr_wchar_t), path)) {
  326. return rv;
  327. }
  328. if (!RemoveDirectoryW(wpath)) {
  329. return apr_get_os_error();
  330. }
  331. }
  332. #endif
  333. #if APR_HAS_ANSI_FS
  334. ELSE_WIN_OS_IS_ANSI
  335. if (!RemoveDirectory(path)) {
  336. return apr_get_os_error();
  337. }
  338. #endif
  339. return APR_SUCCESS;
  340. }
  341. APR_DECLARE(apr_status_t) apr_os_dir_get(apr_os_dir_t **thedir,
  342. apr_dir_t *dir)
  343. {
  344. if (dir == NULL) {
  345. return APR_ENODIR;
  346. }
  347. *thedir = dir->dirhand;
  348. return APR_SUCCESS;
  349. }
  350. APR_DECLARE(apr_status_t) apr_os_dir_put(apr_dir_t **dir,
  351. apr_os_dir_t *thedir,
  352. apr_pool_t *pool)
  353. {
  354. return APR_ENOTIMPL;
  355. }