Groonga 3.0.9 Source Code Document
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
ngx_file.c
Go to the documentation of this file.
1 
2 /*
3  * Copyright (C) Igor Sysoev
4  * Copyright (C) Nginx, Inc.
5  */
6 
7 
8 #include <ngx_config.h>
9 #include <ngx_core.h>
10 
11 
12 static ngx_atomic_t temp_number = 0;
13 ngx_atomic_t *ngx_temp_number = &temp_number;
15 
16 
17 ssize_t
19 {
20  ngx_int_t rc;
21 
22  if (tf->file.fd == NGX_INVALID_FILE) {
23  rc = ngx_create_temp_file(&tf->file, tf->path, tf->pool,
24  tf->persistent, tf->clean, tf->access);
25 
26  if (rc == NGX_ERROR || rc == NGX_AGAIN) {
27  return rc;
28  }
29 
30  if (tf->log_level) {
31  ngx_log_error(tf->log_level, tf->file.log, 0, "%s %V",
32  tf->warn, &tf->file.name);
33  }
34  }
35 
36  return ngx_write_chain_to_file(&tf->file, chain, tf->offset, tf->pool);
37 }
38 
39 
42  ngx_uint_t persistent, ngx_uint_t clean, ngx_uint_t access)
43 {
44  uint32_t n;
45  ngx_err_t err;
46  ngx_pool_cleanup_t *cln;
48 
49  file->name.len = path->name.len + 1 + path->len + 10;
50 
51  file->name.data = ngx_pnalloc(pool, file->name.len + 1);
52  if (file->name.data == NULL) {
53  return NGX_ERROR;
54  }
55 
56 #if 0
57  for (i = 0; i < file->name.len; i++) {
58  file->name.data[i] = 'X';
59  }
60 #endif
61 
62  ngx_memcpy(file->name.data, path->name.data, path->name.len);
63 
64  n = (uint32_t) ngx_next_temp_number(0);
65 
66  cln = ngx_pool_cleanup_add(pool, sizeof(ngx_pool_cleanup_file_t));
67  if (cln == NULL) {
68  return NGX_ERROR;
69  }
70 
71  for ( ;; ) {
72  (void) ngx_sprintf(file->name.data + path->name.len + 1 + path->len,
73  "%010uD%Z", n);
74 
75  ngx_create_hashed_filename(path, file->name.data, file->name.len);
76 
78  "hashed path: %s", file->name.data);
79 
80  file->fd = ngx_open_tempfile(file->name.data, persistent, access);
81 
83  "temp fd:%d", file->fd);
84 
85  if (file->fd != NGX_INVALID_FILE) {
86 
88  clnf = cln->data;
89 
90  clnf->fd = file->fd;
91  clnf->name = file->name.data;
92  clnf->log = pool->log;
93 
94  return NGX_OK;
95  }
96 
97  err = ngx_errno;
98 
99  if (err == NGX_EEXIST) {
100  n = (uint32_t) ngx_next_temp_number(1);
101  continue;
102  }
103 
104  if ((path->level[0] == 0) || (err != NGX_ENOPATH)) {
105  ngx_log_error(NGX_LOG_CRIT, file->log, err,
106  ngx_open_tempfile_n " \"%s\" failed",
107  file->name.data);
108  return NGX_ERROR;
109  }
110 
111  if (ngx_create_path(file, path) == NGX_ERROR) {
112  return NGX_ERROR;
113  }
114  }
115 }
116 
117 
118 void
119 ngx_create_hashed_filename(ngx_path_t *path, u_char *file, size_t len)
120 {
121  size_t i, level;
122  ngx_uint_t n;
123 
124  i = path->name.len + 1;
125 
126  file[path->name.len + path->len] = '/';
127 
128  for (n = 0; n < 3; n++) {
129  level = path->level[n];
130 
131  if (level == 0) {
132  break;
133  }
134 
135  len -= level;
136  file[i - 1] = '/';
137  ngx_memcpy(&file[i], &file[len], level);
138  i += level + 1;
139  }
140 }
141 
142 
143 ngx_int_t
145 {
146  size_t pos;
147  ngx_err_t err;
148  ngx_uint_t i;
149 
150  pos = path->name.len;
151 
152  for (i = 0; i < 3; i++) {
153  if (path->level[i] == 0) {
154  break;
155  }
156 
157  pos += path->level[i] + 1;
158 
159  file->name.data[pos] = '\0';
160 
162  "temp file: \"%s\"", file->name.data);
163 
164  if (ngx_create_dir(file->name.data, 0700) == NGX_FILE_ERROR) {
165  err = ngx_errno;
166  if (err != NGX_EEXIST) {
167  ngx_log_error(NGX_LOG_CRIT, file->log, err,
168  ngx_create_dir_n " \"%s\" failed",
169  file->name.data);
170  return NGX_ERROR;
171  }
172  }
173 
174  file->name.data[pos] = '/';
175  }
176 
177  return NGX_OK;
178 }
179 
180 
181 ngx_err_t
182 ngx_create_full_path(u_char *dir, ngx_uint_t access)
183 {
184  u_char *p, ch;
185  ngx_err_t err;
186 
187  err = 0;
188 
189 #if (NGX_WIN32)
190  p = dir + 3;
191 #else
192  p = dir + 1;
193 #endif
194 
195  for ( /* void */ ; *p; p++) {
196  ch = *p;
197 
198  if (ch != '/') {
199  continue;
200  }
201 
202  *p = '\0';
203 
204  if (ngx_create_dir(dir, access) == NGX_FILE_ERROR) {
205  err = ngx_errno;
206 
207  switch (err) {
208  case NGX_EEXIST:
209  err = 0;
210  case NGX_EACCES:
211  break;
212 
213  default:
214  return err;
215  }
216  }
217 
218  *p = '/';
219  }
220 
221  return err;
222 }
223 
224 
227 {
228  ngx_atomic_uint_t n, add;
229 
230  add = collision ? ngx_random_number : 1;
231 
232  n = ngx_atomic_fetch_add(ngx_temp_number, add);
233 
234  return n + add;
235 }
236 
237 
238 char *
240 {
241  char *p = conf;
242 
243  ssize_t level;
244  ngx_str_t *value;
245  ngx_uint_t i, n;
246  ngx_path_t *path, **slot;
247 
248  slot = (ngx_path_t **) (p + cmd->offset);
249 
250  if (*slot) {
251  return "is duplicate";
252  }
253 
254  path = ngx_pcalloc(cf->pool, sizeof(ngx_path_t));
255  if (path == NULL) {
256  return NGX_CONF_ERROR;
257  }
258 
259  value = cf->args->elts;
260 
261  path->name = value[1];
262 
263  if (path->name.data[path->name.len - 1] == '/') {
264  path->name.len--;
265  }
266 
267  if (ngx_conf_full_name(cf->cycle, &path->name, 0) != NGX_OK) {
268  return NULL;
269  }
270 
271  path->len = 0;
272  path->manager = NULL;
273  path->loader = NULL;
274  path->conf_file = cf->conf_file->file.name.data;
275  path->line = cf->conf_file->line;
276 
277  for (i = 0, n = 2; n < cf->args->nelts; i++, n++) {
278  level = ngx_atoi(value[n].data, value[n].len);
279  if (level == NGX_ERROR || level == 0) {
280  return "invalid value";
281  }
282 
283  path->level[i] = level;
284  path->len += level + 1;
285  }
286 
287  while (i < 3) {
288  path->level[i++] = 0;
289  }
290 
291  *slot = path;
292 
293  if (ngx_add_path(cf, slot) == NGX_ERROR) {
294  return NGX_CONF_ERROR;
295  }
296 
297  return NGX_CONF_OK;
298 }
299 
300 
301 char *
303  ngx_path_init_t *init)
304 {
305  if (*path) {
306  return NGX_CONF_OK;
307  }
308 
309  if (prev) {
310  *path = prev;
311  return NGX_CONF_OK;
312  }
313 
314  *path = ngx_palloc(cf->pool, sizeof(ngx_path_t));
315  if (*path == NULL) {
316  return NGX_CONF_ERROR;
317  }
318 
319  (*path)->name = init->name;
320 
321  if (ngx_conf_full_name(cf->cycle, &(*path)->name, 0) != NGX_OK) {
322  return NGX_CONF_ERROR;
323  }
324 
325  (*path)->level[0] = init->level[0];
326  (*path)->level[1] = init->level[1];
327  (*path)->level[2] = init->level[2];
328 
329  (*path)->len = init->level[0] + (init->level[0] ? 1 : 0)
330  + init->level[1] + (init->level[1] ? 1 : 0)
331  + init->level[2] + (init->level[2] ? 1 : 0);
332 
333  (*path)->manager = NULL;
334  (*path)->loader = NULL;
335  (*path)->conf_file = NULL;
336 
337  if (ngx_add_path(cf, path) != NGX_OK) {
338  return NGX_CONF_ERROR;
339  }
340 
341  return NGX_CONF_OK;
342 }
343 
344 
345 char *
347 {
348  char *confp = conf;
349 
350  u_char *p;
351  ngx_str_t *value;
352  ngx_uint_t i, right, shift, *access;
353 
354  access = (ngx_uint_t *) (confp + cmd->offset);
355 
356  if (*access != NGX_CONF_UNSET_UINT) {
357  return "is duplicate";
358  }
359 
360  value = cf->args->elts;
361 
362  *access = 0600;
363 
364  for (i = 1; i < cf->args->nelts; i++) {
365 
366  p = value[i].data;
367 
368  if (ngx_strncmp(p, "user:", sizeof("user:") - 1) == 0) {
369  shift = 6;
370  p += sizeof("user:") - 1;
371 
372  } else if (ngx_strncmp(p, "group:", sizeof("group:") - 1) == 0) {
373  shift = 3;
374  p += sizeof("group:") - 1;
375 
376  } else if (ngx_strncmp(p, "all:", sizeof("all:") - 1) == 0) {
377  shift = 0;
378  p += sizeof("all:") - 1;
379 
380  } else {
381  goto invalid;
382  }
383 
384  if (ngx_strcmp(p, "rw") == 0) {
385  right = 6;
386 
387  } else if (ngx_strcmp(p, "r") == 0) {
388  right = 4;
389 
390  } else {
391  goto invalid;
392  }
393 
394  *access |= right << shift;
395  }
396 
397  return NGX_CONF_OK;
398 
399 invalid:
400 
401  ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid value \"%V\"", &value[i]);
402 
403  return NGX_CONF_ERROR;
404 }
405 
406 
407 ngx_int_t
409 {
410  ngx_uint_t i, n;
411  ngx_path_t *path, **p;
412 
413  path = *slot;
414 
415  p = cf->cycle->paths.elts;
416  for (i = 0; i < cf->cycle->paths.nelts; i++) {
417  if (p[i]->name.len == path->name.len
418  && ngx_strcmp(p[i]->name.data, path->name.data) == 0)
419  {
420  for (n = 0; n < 3; n++) {
421  if (p[i]->level[n] != path->level[n]) {
422  if (path->conf_file == NULL) {
423  if (p[i]->conf_file == NULL) {
425  "the default path name \"%V\" has "
426  "the same name as another default path, "
427  "but the different levels, you need to "
428  "redefine one of them in http section",
429  &p[i]->name);
430  return NGX_ERROR;
431  }
432 
434  "the path name \"%V\" in %s:%ui has "
435  "the same name as default path, but "
436  "the different levels, you need to "
437  "define default path in http section",
438  &p[i]->name, p[i]->conf_file, p[i]->line);
439  return NGX_ERROR;
440  }
441 
443  "the same path name \"%V\" in %s:%ui "
444  "has the different levels than",
445  &p[i]->name, p[i]->conf_file, p[i]->line);
446  return NGX_ERROR;
447  }
448 
449  if (p[i]->level[n] == 0) {
450  break;
451  }
452  }
453 
454  *slot = p[i];
455 
456  return NGX_OK;
457  }
458  }
459 
460  p = ngx_array_push(&cf->cycle->paths);
461  if (p == NULL) {
462  return NGX_ERROR;
463  }
464 
465  *p = path;
466 
467  return NGX_OK;
468 }
469 
470 
471 ngx_int_t
473 {
474  ngx_err_t err;
475  ngx_uint_t i;
476  ngx_path_t **path;
477 
478  path = cycle->paths.elts;
479  for (i = 0; i < cycle->paths.nelts; i++) {
480 
481  if (ngx_create_dir(path[i]->name.data, 0700) == NGX_FILE_ERROR) {
482  err = ngx_errno;
483  if (err != NGX_EEXIST) {
484  ngx_log_error(NGX_LOG_EMERG, cycle->log, err,
485  ngx_create_dir_n " \"%s\" failed",
486  path[i]->name.data);
487  return NGX_ERROR;
488  }
489  }
490 
491  if (user == (ngx_uid_t) NGX_CONF_UNSET_UINT) {
492  continue;
493  }
494 
495 #if !(NGX_WIN32)
496  {
497  ngx_file_info_t fi;
498 
499  if (ngx_file_info((const char *) path[i]->name.data, &fi)
500  == NGX_FILE_ERROR)
501  {
503  ngx_file_info_n " \"%s\" failed", path[i]->name.data);
504  return NGX_ERROR;
505  }
506 
507  if (fi.st_uid != user) {
508  if (chown((const char *) path[i]->name.data, user, -1) == -1) {
510  "chown(\"%s\", %d) failed",
511  path[i]->name.data, user);
512  return NGX_ERROR;
513  }
514  }
515 
516  if ((fi.st_mode & (S_IRUSR|S_IWUSR|S_IXUSR))
517  != (S_IRUSR|S_IWUSR|S_IXUSR))
518  {
519  fi.st_mode |= (S_IRUSR|S_IWUSR|S_IXUSR);
520 
521  if (chmod((const char *) path[i]->name.data, fi.st_mode) == -1) {
523  "chmod() \"%s\" failed", path[i]->name.data);
524  return NGX_ERROR;
525  }
526  }
527  }
528 #endif
529  }
530 
531  return NGX_OK;
532 }
533 
534 
535 ngx_int_t
537 {
538  u_char *name;
539  ngx_err_t err;
540  ngx_copy_file_t cf;
541 
542 #if !(NGX_WIN32)
543 
544  if (ext->access) {
545  if (ngx_change_file_access(src->data, ext->access) == NGX_FILE_ERROR) {
547  ngx_change_file_access_n " \"%s\" failed", src->data);
548  err = 0;
549  goto failed;
550  }
551  }
552 
553 #endif
554 
555  if (ext->time != -1) {
556  if (ngx_set_file_time(src->data, ext->fd, ext->time) != NGX_OK) {
558  ngx_set_file_time_n " \"%s\" failed", src->data);
559  err = 0;
560  goto failed;
561  }
562  }
563 
564  if (ngx_rename_file(src->data, to->data) != NGX_FILE_ERROR) {
565  return NGX_OK;
566  }
567 
568  err = ngx_errno;
569 
570  if (err == NGX_ENOPATH) {
571 
572  if (!ext->create_path) {
573  goto failed;
574  }
575 
577 
578  if (err) {
579  ngx_log_error(NGX_LOG_CRIT, ext->log, err,
580  ngx_create_dir_n " \"%s\" failed", to->data);
581  err = 0;
582  goto failed;
583  }
584 
585  if (ngx_rename_file(src->data, to->data) != NGX_FILE_ERROR) {
586  return NGX_OK;
587  }
588 
589  err = ngx_errno;
590  }
591 
592 #if (NGX_WIN32)
593 
594  if (err == NGX_EEXIST) {
595  err = ngx_win32_rename_file(src, to, ext->log);
596 
597  if (err == 0) {
598  return NGX_OK;
599  }
600  }
601 
602 #endif
603 
604  if (err == NGX_EXDEV) {
605 
606  cf.size = -1;
607  cf.buf_size = 0;
608  cf.access = ext->access;
609  cf.time = ext->time;
610  cf.log = ext->log;
611 
612  name = ngx_alloc(to->len + 1 + 10 + 1, ext->log);
613  if (name == NULL) {
614  return NGX_ERROR;
615  }
616 
617  (void) ngx_sprintf(name, "%*s.%010uD%Z", to->len, to->data,
618  (uint32_t) ngx_next_temp_number(0));
619 
620  if (ngx_copy_file(src->data, name, &cf) == NGX_OK) {
621 
622  if (ngx_rename_file(name, to->data) != NGX_FILE_ERROR) {
623  ngx_free(name);
624 
625  if (ngx_delete_file(src->data) == NGX_FILE_ERROR) {
627  ngx_delete_file_n " \"%s\" failed",
628  src->data);
629  return NGX_ERROR;
630  }
631 
632  return NGX_OK;
633  }
634 
636  ngx_rename_file_n " \"%s\" to \"%s\" failed",
637  name, to->data);
638 
639  if (ngx_delete_file(name) == NGX_FILE_ERROR) {
641  ngx_delete_file_n " \"%s\" failed", name);
642 
643  }
644  }
645 
646  ngx_free(name);
647 
648  err = 0;
649  }
650 
651 failed:
652 
653  if (ext->delete_file) {
654  if (ngx_delete_file(src->data) == NGX_FILE_ERROR) {
656  ngx_delete_file_n " \"%s\" failed", src->data);
657  }
658  }
659 
660  if (err) {
661  ngx_log_error(NGX_LOG_CRIT, ext->log, err,
662  ngx_rename_file_n " \"%s\" to \"%s\" failed",
663  src->data, to->data);
664  }
665 
666  return NGX_ERROR;
667 }
668 
669 
670 ngx_int_t
671 ngx_copy_file(u_char *from, u_char *to, ngx_copy_file_t *cf)
672 {
673  char *buf;
674  off_t size;
675  size_t len;
676  ssize_t n;
677  ngx_fd_t fd, nfd;
678  ngx_int_t rc;
679  ngx_file_info_t fi;
680 
681  rc = NGX_ERROR;
682  buf = NULL;
683  nfd = NGX_INVALID_FILE;
684 
686 
687  if (fd == NGX_INVALID_FILE) {
689  ngx_open_file_n " \"%s\" failed", from);
690  goto failed;
691  }
692 
693  if (cf->size != -1) {
694  size = cf->size;
695 
696  } else {
697  if (ngx_fd_info(fd, &fi) == NGX_FILE_ERROR) {
699  ngx_fd_info_n " \"%s\" failed", from);
700 
701  goto failed;
702  }
703 
704  size = ngx_file_size(&fi);
705  }
706 
707  len = cf->buf_size ? cf->buf_size : 65536;
708 
709  if ((off_t) len > size) {
710  len = (size_t) size;
711  }
712 
713  buf = ngx_alloc(len, cf->log);
714  if (buf == NULL) {
715  goto failed;
716  }
717 
719  cf->access);
720 
721  if (nfd == NGX_INVALID_FILE) {
723  ngx_open_file_n " \"%s\" failed", to);
724  goto failed;
725  }
726 
727  while (size > 0) {
728 
729  if ((off_t) len > size) {
730  len = (size_t) size;
731  }
732 
733  n = ngx_read_fd(fd, buf, len);
734 
735  if (n == -1) {
737  ngx_read_fd_n " \"%s\" failed", from);
738  goto failed;
739  }
740 
741  if ((size_t) n != len) {
743  ngx_read_fd_n " has read only %z of %uz from %s",
744  n, size, from);
745  goto failed;
746  }
747 
748  n = ngx_write_fd(nfd, buf, len);
749 
750  if (n == -1) {
752  ngx_write_fd_n " \"%s\" failed", to);
753  goto failed;
754  }
755 
756  if ((size_t) n != len) {
758  ngx_write_fd_n " has written only %z of %uz to %s",
759  n, size, to);
760  goto failed;
761  }
762 
763  size -= n;
764  }
765 
766  if (cf->time != -1) {
767  if (ngx_set_file_time(to, nfd, cf->time) != NGX_OK) {
769  ngx_set_file_time_n " \"%s\" failed", to);
770  goto failed;
771  }
772  }
773 
774  rc = NGX_OK;
775 
776 failed:
777 
778  if (nfd != NGX_INVALID_FILE) {
779  if (ngx_close_file(nfd) == NGX_FILE_ERROR) {
781  ngx_close_file_n " \"%s\" failed", to);
782  }
783  }
784 
785  if (fd != NGX_INVALID_FILE) {
786  if (ngx_close_file(fd) == NGX_FILE_ERROR) {
788  ngx_close_file_n " \"%s\" failed", from);
789  }
790  }
791 
792  if (buf) {
793  ngx_free(buf);
794  }
795 
796  return rc;
797 }
798 
799 
800 /*
801  * ctx->init_handler() - see ctx->alloc
802  * ctx->file_handler() - file handler
803  * ctx->pre_tree_handler() - handler is called before entering directory
804  * ctx->post_tree_handler() - handler is called after leaving directory
805  * ctx->spec_handler() - special (socket, FIFO, etc.) file handler
806  *
807  * ctx->data - some data structure, it may be the same on all levels, or
808  * reallocated if ctx->alloc is nonzero
809  *
810  * ctx->alloc - a size of data structure that is allocated at every level
811  * and is initialized by ctx->init_handler()
812  *
813  * ctx->log - a log
814  *
815  * on fatal (memory) error handler must return NGX_ABORT to stop walking tree
816  */
817 
818 ngx_int_t
820 {
821  void *data, *prev;
822  u_char *p, *name;
823  size_t len;
824  ngx_int_t rc;
825  ngx_err_t err;
826  ngx_str_t file, buf;
827  ngx_dir_t dir;
828 
829  ngx_str_null(&buf);
830 
832  "walk tree \"%V\"", tree);
833 
834  if (ngx_open_dir(tree, &dir) == NGX_ERROR) {
836  ngx_open_dir_n " \"%s\" failed", tree->data);
837  return NGX_ERROR;
838  }
839 
840  prev = ctx->data;
841 
842  if (ctx->alloc) {
843  data = ngx_alloc(ctx->alloc, ctx->log);
844  if (data == NULL) {
845  goto failed;
846  }
847 
848  if (ctx->init_handler(data, prev) == NGX_ABORT) {
849  goto failed;
850  }
851 
852  ctx->data = data;
853 
854  } else {
855  data = NULL;
856  }
857 
858  for ( ;; ) {
859 
860  ngx_set_errno(0);
861 
862  if (ngx_read_dir(&dir) == NGX_ERROR) {
863  err = ngx_errno;
864 
865  if (err == NGX_ENOMOREFILES) {
866  rc = NGX_OK;
867 
868  } else {
869  ngx_log_error(NGX_LOG_CRIT, ctx->log, err,
870  ngx_read_dir_n " \"%s\" failed", tree->data);
871  rc = NGX_ERROR;
872  }
873 
874  goto done;
875  }
876 
877  len = ngx_de_namelen(&dir);
878  name = ngx_de_name(&dir);
879 
881  "tree name %uz:\"%s\"", len, name);
882 
883  if (len == 1 && name[0] == '.') {
884  continue;
885  }
886 
887  if (len == 2 && name[0] == '.' && name[1] == '.') {
888  continue;
889  }
890 
891  file.len = tree->len + 1 + len;
892 
893  if (file.len + NGX_DIR_MASK_LEN > buf.len) {
894 
895  if (buf.len) {
896  ngx_free(buf.data);
897  }
898 
899  buf.len = tree->len + 1 + len + NGX_DIR_MASK_LEN;
900 
901  buf.data = ngx_alloc(buf.len + 1, ctx->log);
902  if (buf.data == NULL) {
903  goto failed;
904  }
905  }
906 
907  p = ngx_cpymem(buf.data, tree->data, tree->len);
908  *p++ = '/';
909  ngx_memcpy(p, name, len + 1);
910 
911  file.data = buf.data;
912 
914  "tree path \"%s\"", file.data);
915 
916  if (!dir.valid_info) {
917  if (ngx_de_info(file.data, &dir) == NGX_FILE_ERROR) {
919  ngx_de_info_n " \"%s\" failed", file.data);
920  continue;
921  }
922  }
923 
924  if (ngx_de_is_file(&dir)) {
925 
927  "tree file \"%s\"", file.data);
928 
929  ctx->size = ngx_de_size(&dir);
930  ctx->fs_size = ngx_de_fs_size(&dir);
931  ctx->access = ngx_de_access(&dir);
932  ctx->mtime = ngx_de_mtime(&dir);
933 
934  if (ctx->file_handler(ctx, &file) == NGX_ABORT) {
935  goto failed;
936  }
937 
938  } else if (ngx_de_is_dir(&dir)) {
939 
941  "tree enter dir \"%s\"", file.data);
942 
943  ctx->access = ngx_de_access(&dir);
944  ctx->mtime = ngx_de_mtime(&dir);
945 
946  if (ctx->pre_tree_handler(ctx, &file) == NGX_ABORT) {
947  goto failed;
948  }
949 
950  if (ngx_walk_tree(ctx, &file) == NGX_ABORT) {
951  goto failed;
952  }
953 
954  ctx->access = ngx_de_access(&dir);
955  ctx->mtime = ngx_de_mtime(&dir);
956 
957  if (ctx->post_tree_handler(ctx, &file) == NGX_ABORT) {
958  goto failed;
959  }
960 
961  } else {
962 
964  "tree special \"%s\"", file.data);
965 
966  if (ctx->spec_handler(ctx, &file) == NGX_ABORT) {
967  goto failed;
968  }
969  }
970  }
971 
972 failed:
973 
974  rc = NGX_ABORT;
975 
976 done:
977 
978  if (buf.len) {
979  ngx_free(buf.data);
980  }
981 
982  if (data) {
983  ngx_free(data);
984  ctx->data = prev;
985  }
986 
987  if (ngx_close_dir(&dir) == NGX_ERROR) {
989  ngx_close_dir_n " \"%s\" failed", tree->data);
990  }
991 
992  return rc;
993 }