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