src/core/ngx_file.c - nginx-1.7.10

Global variables defined

Functions defined

Source code


  1. /*
  2. * Copyright (C) Igor Sysoev
  3. * Copyright (C) Nginx, Inc.
  4. */


  5. #include <ngx_config.h>
  6. #include <ngx_core.h>


  7. static ngx_int_t ngx_test_full_name(ngx_str_t *name);


  8. static ngx_atomic_t   temp_number = 0;
  9. ngx_atomic_t         *ngx_temp_number = &temp_number;
  10. ngx_atomic_int_t      ngx_random_number = 123456;


  11. ngx_int_t
  12. ngx_get_full_name(ngx_pool_t *pool, ngx_str_t *prefix, ngx_str_t *name)
  13. {
  14.     size_t      len;
  15.     u_char     *p, *n;
  16.     ngx_int_t   rc;

  17.     rc = ngx_test_full_name(name);

  18.     if (rc == NGX_OK) {
  19.         return rc;
  20.     }

  21.     len = prefix->len;

  22. #if (NGX_WIN32)

  23.     if (rc == 2) {
  24.         len = rc;
  25.     }

  26. #endif

  27.     n = ngx_pnalloc(pool, len + name->len + 1);
  28.     if (n == NULL) {
  29.         return NGX_ERROR;
  30.     }

  31.     p = ngx_cpymem(n, prefix->data, len);
  32.     ngx_cpystrn(p, name->data, name->len + 1);

  33.     name->len += len;
  34.     name->data = n;

  35.     return NGX_OK;
  36. }


  37. static ngx_int_t
  38. ngx_test_full_name(ngx_str_t *name)
  39. {
  40. #if (NGX_WIN32)
  41.     u_char  c0, c1;

  42.     c0 = name->data[0];

  43.     if (name->len < 2) {
  44.         if (c0 == '/') {
  45.             return 2;
  46.         }

  47.         return NGX_DECLINED;
  48.     }

  49.     c1 = name->data[1];

  50.     if (c1 == ':') {
  51.         c0 |= 0x20;

  52.         if ((c0 >= 'a' && c0 <= 'z')) {
  53.             return NGX_OK;
  54.         }

  55.         return NGX_DECLINED;
  56.     }

  57.     if (c1 == '/') {
  58.         return NGX_OK;
  59.     }

  60.     if (c0 == '/') {
  61.         return 2;
  62.     }

  63.     return NGX_DECLINED;

  64. #else

  65.     if (name->data[0] == '/') {
  66.         return NGX_OK;
  67.     }

  68.     return NGX_DECLINED;

  69. #endif
  70. }


  71. ssize_t
  72. ngx_write_chain_to_temp_file(ngx_temp_file_t *tf, ngx_chain_t *chain)
  73. {
  74.     ngx_int_t  rc;

  75.     if (tf->file.fd == NGX_INVALID_FILE) {
  76.         rc = ngx_create_temp_file(&tf->file, tf->path, tf->pool,
  77.                                   tf->persistent, tf->clean, tf->access);

  78.         if (rc != NGX_OK) {
  79.             return rc;
  80.         }

  81.         if (tf->log_level) {
  82.             ngx_log_error(tf->log_level, tf->file.log, 0, "%s %V",
  83.                           tf->warn, &tf->file.name);
  84.         }
  85.     }

  86.     return ngx_write_chain_to_file(&tf->file, chain, tf->offset, tf->pool);
  87. }


  88. ngx_int_t
  89. ngx_create_temp_file(ngx_file_t *file, ngx_path_t *path, ngx_pool_t *pool,
  90.     ngx_uint_t persistent, ngx_uint_t clean, ngx_uint_t access)
  91. {
  92.     uint32_t                  n;
  93.     ngx_err_t                 err;
  94.     ngx_pool_cleanup_t       *cln;
  95.     ngx_pool_cleanup_file_t  *clnf;

  96.     file->name.len = path->name.len + 1 + path->len + 10;

  97.     file->name.data = ngx_pnalloc(pool, file->name.len + 1);
  98.     if (file->name.data == NULL) {
  99.         return NGX_ERROR;
  100.     }

  101. #if 0
  102.     for (i = 0; i < file->name.len; i++) {
  103.          file->name.data[i] = 'X';
  104.     }
  105. #endif

  106.     ngx_memcpy(file->name.data, path->name.data, path->name.len);

  107.     n = (uint32_t) ngx_next_temp_number(0);

  108.     cln = ngx_pool_cleanup_add(pool, sizeof(ngx_pool_cleanup_file_t));
  109.     if (cln == NULL) {
  110.         return NGX_ERROR;
  111.     }

  112.     for ( ;; ) {
  113.         (void) ngx_sprintf(file->name.data + path->name.len + 1 + path->len,
  114.                            "%010uD%Z", n);

  115.         ngx_create_hashed_filename(path, file->name.data, file->name.len);

  116.         ngx_log_debug1(NGX_LOG_DEBUG_CORE, file->log, 0,
  117.                        "hashed path: %s", file->name.data);

  118.         file->fd = ngx_open_tempfile(file->name.data, persistent, access);

  119.         ngx_log_debug1(NGX_LOG_DEBUG_CORE, file->log, 0,
  120.                        "temp fd:%d", file->fd);

  121.         if (file->fd != NGX_INVALID_FILE) {

  122.             cln->handler = clean ? ngx_pool_delete_file : ngx_pool_cleanup_file;
  123.             clnf = cln->data;

  124.             clnf->fd = file->fd;
  125.             clnf->name = file->name.data;
  126.             clnf->log = pool->log;

  127.             return NGX_OK;
  128.         }

  129.         err = ngx_errno;

  130.         if (err == NGX_EEXIST) {
  131.             n = (uint32_t) ngx_next_temp_number(1);
  132.             continue;
  133.         }

  134.         if ((path->level[0] == 0) || (err != NGX_ENOPATH)) {
  135.             ngx_log_error(NGX_LOG_CRIT, file->log, err,
  136.                           ngx_open_tempfile_n " \"%s\" failed",
  137.                           file->name.data);
  138.             return NGX_ERROR;
  139.         }

  140.         if (ngx_create_path(file, path) == NGX_ERROR) {
  141.             return NGX_ERROR;
  142.         }
  143.     }
  144. }


  145. void
  146. ngx_create_hashed_filename(ngx_path_t *path, u_char *file, size_t len)
  147. {
  148.     size_t      i, level;
  149.     ngx_uint_t  n;

  150.     i = path->name.len + 1;

  151.     file[path->name.len + path->len]  = '/';

  152.     for (n = 0; n < 3; n++) {
  153.         level = path->level[n];

  154.         if (level == 0) {
  155.             break;
  156.         }

  157.         len -= level;
  158.         file[i - 1] = '/';
  159.         ngx_memcpy(&file[i], &file[len], level);
  160.         i += level + 1;
  161.     }
  162. }


  163. ngx_int_t
  164. ngx_create_path(ngx_file_t *file, ngx_path_t *path)
  165. {
  166.     size_t      pos;
  167.     ngx_err_t   err;
  168.     ngx_uint_t  i;

  169.     pos = path->name.len;

  170.     for (i = 0; i < 3; i++) {
  171.         if (path->level[i] == 0) {
  172.             break;
  173.         }

  174.         pos += path->level[i] + 1;

  175.         file->name.data[pos] = '\0';

  176.         ngx_log_debug1(NGX_LOG_DEBUG_CORE, file->log, 0,
  177.                        "temp file: \"%s\"", file->name.data);

  178.         if (ngx_create_dir(file->name.data, 0700) == NGX_FILE_ERROR) {
  179.             err = ngx_errno;
  180.             if (err != NGX_EEXIST) {
  181.                 ngx_log_error(NGX_LOG_CRIT, file->log, err,
  182.                               ngx_create_dir_n " \"%s\" failed",
  183.                               file->name.data);
  184.                 return NGX_ERROR;
  185.             }
  186.         }

  187.         file->name.data[pos] = '/';
  188.     }

  189.     return NGX_OK;
  190. }


  191. ngx_err_t
  192. ngx_create_full_path(u_char *dir, ngx_uint_t access)
  193. {
  194.     u_char     *p, ch;
  195.     ngx_err_t   err;

  196.     err = 0;

  197. #if (NGX_WIN32)
  198.     p = dir + 3;
  199. #else
  200.     p = dir + 1;
  201. #endif

  202.     for ( /* void */ ; *p; p++) {
  203.         ch = *p;

  204.         if (ch != '/') {
  205.             continue;
  206.         }

  207.         *p = '\0';

  208.         if (ngx_create_dir(dir, access) == NGX_FILE_ERROR) {
  209.             err = ngx_errno;

  210.             switch (err) {
  211.             case NGX_EEXIST:
  212.                 err = 0;
  213.             case NGX_EACCES:
  214.                 break;

  215.             default:
  216.                 return err;
  217.             }
  218.         }

  219.         *p = '/';
  220.     }

  221.     return err;
  222. }


  223. ngx_atomic_uint_t
  224. ngx_next_temp_number(ngx_uint_t collision)
  225. {
  226.     ngx_atomic_uint_t  n, add;

  227.     add = collision ? ngx_random_number : 1;

  228.     n = ngx_atomic_fetch_add(ngx_temp_number, add);

  229.     return n + add;
  230. }


  231. char *
  232. ngx_conf_set_path_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
  233. {
  234.     char  *p = conf;

  235.     ssize_t      level;
  236.     ngx_str_t   *value;
  237.     ngx_uint_t   i, n;
  238.     ngx_path_t  *path, **slot;

  239.     slot = (ngx_path_t **) (p + cmd->offset);

  240.     if (*slot) {
  241.         return "is duplicate";
  242.     }

  243.     path = ngx_pcalloc(cf->pool, sizeof(ngx_path_t));
  244.     if (path == NULL) {
  245.         return NGX_CONF_ERROR;
  246.     }

  247.     value = cf->args->elts;

  248.     path->name = value[1];

  249.     if (path->name.data[path->name.len - 1] == '/') {
  250.         path->name.len--;
  251.     }

  252.     if (ngx_conf_full_name(cf->cycle, &path->name, 0) != NGX_OK) {
  253.         return NULL;
  254.     }

  255.     path->conf_file = cf->conf_file->file.name.data;
  256.     path->line = cf->conf_file->line;

  257.     for (i = 0, n = 2; n < cf->args->nelts; i++, n++) {
  258.         level = ngx_atoi(value[n].data, value[n].len);
  259.         if (level == NGX_ERROR || level == 0) {
  260.             return "invalid value";
  261.         }

  262.         path->level[i] = level;
  263.         path->len += level + 1;
  264.     }

  265.     while (i < 3) {
  266.         path->level[i++] = 0;
  267.     }

  268.     *slot = path;

  269.     if (ngx_add_path(cf, slot) == NGX_ERROR) {
  270.         return NGX_CONF_ERROR;
  271.     }

  272.     return NGX_CONF_OK;
  273. }


  274. char *
  275. ngx_conf_merge_path_value(ngx_conf_t *cf, ngx_path_t **path, ngx_path_t *prev,
  276.     ngx_path_init_t *init)
  277. {
  278.     if (*path) {
  279.         return NGX_CONF_OK;
  280.     }

  281.     if (prev) {
  282.         *path = prev;
  283.         return NGX_CONF_OK;
  284.     }

  285.     *path = ngx_pcalloc(cf->pool, sizeof(ngx_path_t));
  286.     if (*path == NULL) {
  287.         return NGX_CONF_ERROR;
  288.     }

  289.     (*path)->name = init->name;

  290.     if (ngx_conf_full_name(cf->cycle, &(*path)->name, 0) != NGX_OK) {
  291.         return NGX_CONF_ERROR;
  292.     }

  293.     (*path)->level[0] = init->level[0];
  294.     (*path)->level[1] = init->level[1];
  295.     (*path)->level[2] = init->level[2];

  296.     (*path)->len = init->level[0] + (init->level[0] ? 1 : 0)
  297.                    + init->level[1] + (init->level[1] ? 1 : 0)
  298.                    + init->level[2] + (init->level[2] ? 1 : 0);

  299.     if (ngx_add_path(cf, path) != NGX_OK) {
  300.         return NGX_CONF_ERROR;
  301.     }

  302.     return NGX_CONF_OK;
  303. }


  304. char *
  305. ngx_conf_set_access_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
  306. {
  307.     char  *confp = conf;

  308.     u_char      *p;
  309.     ngx_str_t   *value;
  310.     ngx_uint_t   i, right, shift, *access;

  311.     access = (ngx_uint_t *) (confp + cmd->offset);

  312.     if (*access != NGX_CONF_UNSET_UINT) {
  313.         return "is duplicate";
  314.     }

  315.     value = cf->args->elts;

  316.     *access = 0600;

  317.     for (i = 1; i < cf->args->nelts; i++) {

  318.         p = value[i].data;

  319.         if (ngx_strncmp(p, "user:", sizeof("user:") - 1) == 0) {
  320.             shift = 6;
  321.             p += sizeof("user:") - 1;

  322.         } else if (ngx_strncmp(p, "group:", sizeof("group:") - 1) == 0) {
  323.             shift = 3;
  324.             p += sizeof("group:") - 1;

  325.         } else if (ngx_strncmp(p, "all:", sizeof("all:") - 1) == 0) {
  326.             shift = 0;
  327.             p += sizeof("all:") - 1;

  328.         } else {
  329.             goto invalid;
  330.         }

  331.         if (ngx_strcmp(p, "rw") == 0) {
  332.             right = 6;

  333.         } else if (ngx_strcmp(p, "r") == 0) {
  334.             right = 4;

  335.         } else {
  336.             goto invalid;
  337.         }

  338.         *access |= right << shift;
  339.     }

  340.     return NGX_CONF_OK;

  341. invalid:

  342.     ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid value \"%V\"", &value[i]);

  343.     return NGX_CONF_ERROR;
  344. }


  345. ngx_int_t
  346. ngx_add_path(ngx_conf_t *cf, ngx_path_t **slot)
  347. {
  348.     ngx_uint_t   i, n;
  349.     ngx_path_t  *path, **p;

  350.     path = *slot;

  351.     p = cf->cycle->paths.elts;
  352.     for (i = 0; i < cf->cycle->paths.nelts; i++) {
  353.         if (p[i]->name.len == path->name.len
  354.             && ngx_strcmp(p[i]->name.data, path->name.data) == 0)
  355.         {
  356.             if (p[i]->data != path->data) {
  357.                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  358.                                    "the same path name \"%V\" "
  359.                                    "used in %s:%ui and",
  360.                                    &p[i]->name, p[i]->conf_file, p[i]->line);
  361.                 return NGX_ERROR;
  362.             }

  363.             for (n = 0; n < 3; n++) {
  364.                 if (p[i]->level[n] != path->level[n]) {
  365.                     if (path->conf_file == NULL) {
  366.                         if (p[i]->conf_file == NULL) {
  367.                             ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
  368.                                       "the default path name \"%V\" has "
  369.                                       "the same name as another default path, "
  370.                                       "but the different levels, you need to "
  371.                                       "redefine one of them in http section",
  372.                                       &p[i]->name);
  373.                             return NGX_ERROR;
  374.                         }

  375.                         ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
  376.                                       "the path name \"%V\" in %s:%ui has "
  377.                                       "the same name as default path, but "
  378.                                       "the different levels, you need to "
  379.                                       "define default path in http section",
  380.                                       &p[i]->name, p[i]->conf_file, p[i]->line);
  381.                         return NGX_ERROR;
  382.                     }

  383.                     ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  384.                                       "the same path name \"%V\" in %s:%ui "
  385.                                       "has the different levels than",
  386.                                       &p[i]->name, p[i]->conf_file, p[i]->line);
  387.                     return NGX_ERROR;
  388.                 }

  389.                 if (p[i]->level[n] == 0) {
  390.                     break;
  391.                 }
  392.             }

  393.             *slot = p[i];

  394.             return NGX_OK;
  395.         }
  396.     }

  397.     p = ngx_array_push(&cf->cycle->paths);
  398.     if (p == NULL) {
  399.         return NGX_ERROR;
  400.     }

  401.     *p = path;

  402.     return NGX_OK;
  403. }


  404. ngx_int_t
  405. ngx_create_paths(ngx_cycle_t *cycle, ngx_uid_t user)
  406. {
  407.     ngx_err_t         err;
  408.     ngx_uint_t        i;
  409.     ngx_path_t      **path;

  410.     path = cycle->paths.elts;
  411.     for (i = 0; i < cycle->paths.nelts; i++) {

  412.         if (ngx_create_dir(path[i]->name.data, 0700) == NGX_FILE_ERROR) {
  413.             err = ngx_errno;
  414.             if (err != NGX_EEXIST) {
  415.                 ngx_log_error(NGX_LOG_EMERG, cycle->log, err,
  416.                               ngx_create_dir_n " \"%s\" failed",
  417.                               path[i]->name.data);
  418.                 return NGX_ERROR;
  419.             }
  420.         }

  421.         if (user == (ngx_uid_t) NGX_CONF_UNSET_UINT) {
  422.             continue;
  423.         }

  424. #if !(NGX_WIN32)
  425.         {
  426.         ngx_file_info_t   fi;

  427.         if (ngx_file_info((const char *) path[i]->name.data, &fi)
  428.             == NGX_FILE_ERROR)
  429.         {
  430.             ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
  431.                           ngx_file_info_n " \"%s\" failed", path[i]->name.data);
  432.             return NGX_ERROR;
  433.         }

  434.         if (fi.st_uid != user) {
  435.             if (chown((const char *) path[i]->name.data, user, -1) == -1) {
  436.                 ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
  437.                               "chown(\"%s\", %d) failed",
  438.                               path[i]->name.data, user);
  439.                 return NGX_ERROR;
  440.             }
  441.         }

  442.         if ((fi.st_mode & (S_IRUSR|S_IWUSR|S_IXUSR))
  443.                                                   != (S_IRUSR|S_IWUSR|S_IXUSR))
  444.         {
  445.             fi.st_mode |= (S_IRUSR|S_IWUSR|S_IXUSR);

  446.             if (chmod((const char *) path[i]->name.data, fi.st_mode) == -1) {
  447.                 ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
  448.                               "chmod() \"%s\" failed", path[i]->name.data);
  449.                 return NGX_ERROR;
  450.             }
  451.         }
  452.         }
  453. #endif
  454.     }

  455.     return NGX_OK;
  456. }


  457. ngx_int_t
  458. ngx_ext_rename_file(ngx_str_t *src, ngx_str_t *to, ngx_ext_rename_file_t *ext)
  459. {
  460.     u_char           *name;
  461.     ngx_err_t         err;
  462.     ngx_copy_file_t   cf;

  463. #if !(NGX_WIN32)

  464.     if (ext->access) {
  465.         if (ngx_change_file_access(src->data, ext->access) == NGX_FILE_ERROR) {
  466.             ngx_log_error(NGX_LOG_CRIT, ext->log, ngx_errno,
  467.                           ngx_change_file_access_n " \"%s\" failed", src->data);
  468.             err = 0;
  469.             goto failed;
  470.         }
  471.     }

  472. #endif

  473.     if (ext->time != -1) {
  474.         if (ngx_set_file_time(src->data, ext->fd, ext->time) != NGX_OK) {
  475.             ngx_log_error(NGX_LOG_CRIT, ext->log, ngx_errno,
  476.                           ngx_set_file_time_n " \"%s\" failed", src->data);
  477.             err = 0;
  478.             goto failed;
  479.         }
  480.     }

  481.     if (ngx_rename_file(src->data, to->data) != NGX_FILE_ERROR) {
  482.         return NGX_OK;
  483.     }

  484.     err = ngx_errno;

  485.     if (err == NGX_ENOPATH) {

  486.         if (!ext->create_path) {
  487.             goto failed;
  488.         }

  489.         err = ngx_create_full_path(to->data, ngx_dir_access(ext->path_access));

  490.         if (err) {
  491.             ngx_log_error(NGX_LOG_CRIT, ext->log, err,
  492.                           ngx_create_dir_n " \"%s\" failed", to->data);
  493.             err = 0;
  494.             goto failed;
  495.         }

  496.         if (ngx_rename_file(src->data, to->data) != NGX_FILE_ERROR) {
  497.             return NGX_OK;
  498.         }

  499.         err = ngx_errno;
  500.     }

  501. #if (NGX_WIN32)

  502.     if (err == NGX_EEXIST) {
  503.         err = ngx_win32_rename_file(src, to, ext->log);

  504.         if (err == 0) {
  505.             return NGX_OK;
  506.         }
  507.     }

  508. #endif

  509.     if (err == NGX_EXDEV) {

  510.         cf.size = -1;
  511.         cf.buf_size = 0;
  512.         cf.access = ext->access;
  513.         cf.time = ext->time;
  514.         cf.log = ext->log;

  515.         name = ngx_alloc(to->len + 1 + 10 + 1, ext->log);
  516.         if (name == NULL) {
  517.             return NGX_ERROR;
  518.         }

  519.         (void) ngx_sprintf(name, "%*s.%010uD%Z", to->len, to->data,
  520.                            (uint32_t) ngx_next_temp_number(0));

  521.         if (ngx_copy_file(src->data, name, &cf) == NGX_OK) {

  522.             if (ngx_rename_file(name, to->data) != NGX_FILE_ERROR) {
  523.                 ngx_free(name);

  524.                 if (ngx_delete_file(src->data) == NGX_FILE_ERROR) {
  525.                     ngx_log_error(NGX_LOG_CRIT, ext->log, ngx_errno,
  526.                                   ngx_delete_file_n " \"%s\" failed",
  527.                                   src->data);
  528.                     return NGX_ERROR;
  529.                 }

  530.                 return NGX_OK;
  531.             }

  532.             ngx_log_error(NGX_LOG_CRIT, ext->log, ngx_errno,
  533.                           ngx_rename_file_n " \"%s\" to \"%s\" failed",
  534.                           name, to->data);

  535.             if (ngx_delete_file(name) == NGX_FILE_ERROR) {
  536.                 ngx_log_error(NGX_LOG_CRIT, ext->log, ngx_errno,
  537.                               ngx_delete_file_n " \"%s\" failed", name);

  538.             }
  539.         }

  540.         ngx_free(name);

  541.         err = 0;
  542.     }

  543. failed:

  544.     if (ext->delete_file) {
  545.         if (ngx_delete_file(src->data) == NGX_FILE_ERROR) {
  546.             ngx_log_error(NGX_LOG_CRIT, ext->log, ngx_errno,
  547.                           ngx_delete_file_n " \"%s\" failed", src->data);
  548.         }
  549.     }

  550.     if (err) {
  551.         ngx_log_error(NGX_LOG_CRIT, ext->log, err,
  552.                       ngx_rename_file_n " \"%s\" to \"%s\" failed",
  553.                       src->data, to->data);
  554.     }

  555.     return NGX_ERROR;
  556. }


  557. ngx_int_t
  558. ngx_copy_file(u_char *from, u_char *to, ngx_copy_file_t *cf)
  559. {
  560.     char             *buf;
  561.     off_t             size;
  562.     size_t            len;
  563.     ssize_t           n;
  564.     ngx_fd_t          fd, nfd;
  565.     ngx_int_t         rc;
  566.     ngx_file_info_t   fi;

  567.     rc = NGX_ERROR;
  568.     buf = NULL;
  569.     nfd = NGX_INVALID_FILE;

  570.     fd = ngx_open_file(from, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0);

  571.     if (fd == NGX_INVALID_FILE) {
  572.         ngx_log_error(NGX_LOG_CRIT, cf->log, ngx_errno,
  573.                       ngx_open_file_n " \"%s\" failed", from);
  574.         goto failed;
  575.     }

  576.     if (cf->size != -1) {
  577.         size = cf->size;

  578.     } else {
  579.         if (ngx_fd_info(fd, &fi) == NGX_FILE_ERROR) {
  580.             ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,
  581.                           ngx_fd_info_n " \"%s\" failed", from);

  582.             goto failed;
  583.         }

  584.         size = ngx_file_size(&fi);
  585.     }

  586.     len = cf->buf_size ? cf->buf_size : 65536;

  587.     if ((off_t) len > size) {
  588.         len = (size_t) size;
  589.     }

  590.     buf = ngx_alloc(len, cf->log);
  591.     if (buf == NULL) {
  592.         goto failed;
  593.     }

  594.     nfd = ngx_open_file(to, NGX_FILE_WRONLY, NGX_FILE_CREATE_OR_OPEN,
  595.                         cf->access);

  596.     if (nfd == NGX_INVALID_FILE) {
  597.         ngx_log_error(NGX_LOG_CRIT, cf->log, ngx_errno,
  598.                       ngx_open_file_n " \"%s\" failed", to);
  599.         goto failed;
  600.     }

  601.     while (size > 0) {

  602.         if ((off_t) len > size) {
  603.             len = (size_t) size;
  604.         }

  605.         n = ngx_read_fd(fd, buf, len);

  606.         if (n == -1) {
  607.             ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,
  608.                           ngx_read_fd_n " \"%s\" failed", from);
  609.             goto failed;
  610.         }

  611.         if ((size_t) n != len) {
  612.             ngx_log_error(NGX_LOG_ALERT, cf->log, 0,
  613.                           ngx_read_fd_n " has read only %z of %uz from %s",
  614.                           n, size, from);
  615.             goto failed;
  616.         }

  617.         n = ngx_write_fd(nfd, buf, len);

  618.         if (n == -1) {
  619.             ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,
  620.                           ngx_write_fd_n " \"%s\" failed", to);
  621.             goto failed;
  622.         }

  623.         if ((size_t) n != len) {
  624.             ngx_log_error(NGX_LOG_ALERT, cf->log, 0,
  625.                           ngx_write_fd_n " has written only %z of %uz to %s",
  626.                           n, size, to);
  627.             goto failed;
  628.         }

  629.         size -= n;
  630.     }

  631.     if (cf->time != -1) {
  632.         if (ngx_set_file_time(to, nfd, cf->time) != NGX_OK) {
  633.             ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,
  634.                           ngx_set_file_time_n " \"%s\" failed", to);
  635.             goto failed;
  636.         }
  637.     }

  638.     rc = NGX_OK;

  639. failed:

  640.     if (nfd != NGX_INVALID_FILE) {
  641.         if (ngx_close_file(nfd) == NGX_FILE_ERROR) {
  642.             ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,
  643.                           ngx_close_file_n " \"%s\" failed", to);
  644.         }
  645.     }

  646.     if (fd != NGX_INVALID_FILE) {
  647.         if (ngx_close_file(fd) == NGX_FILE_ERROR) {
  648.             ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,
  649.                           ngx_close_file_n " \"%s\" failed", from);
  650.         }
  651.     }

  652.     if (buf) {
  653.         ngx_free(buf);
  654.     }

  655.     return rc;
  656. }


  657. /*
  658. * ctx->init_handler() - see ctx->alloc
  659. * ctx->file_handler() - file handler
  660. * ctx->pre_tree_handler() - handler is called before entering directory
  661. * ctx->post_tree_handler() - handler is called after leaving directory
  662. * ctx->spec_handler() - special (socket, FIFO, etc.) file handler
  663. *
  664. * ctx->data - some data structure, it may be the same on all levels, or
  665. *     reallocated if ctx->alloc is nonzero
  666. *
  667. * ctx->alloc - a size of data structure that is allocated at every level
  668. *     and is initialized by ctx->init_handler()
  669. *
  670. * ctx->log - a log
  671. *
  672. * on fatal (memory) error handler must return NGX_ABORT to stop walking tree
  673. */

  674. ngx_int_t
  675. ngx_walk_tree(ngx_tree_ctx_t *ctx, ngx_str_t *tree)
  676. {
  677.     void       *data, *prev;
  678.     u_char     *p, *name;
  679.     size_t      len;
  680.     ngx_int_t   rc;
  681.     ngx_err_t   err;
  682.     ngx_str_t   file, buf;
  683.     ngx_dir_t   dir;

  684.     ngx_str_null(&buf);

  685.     ngx_log_debug1(NGX_LOG_DEBUG_CORE, ctx->log, 0,
  686.                    "walk tree \"%V\"", tree);

  687.     if (ngx_open_dir(tree, &dir) == NGX_ERROR) {
  688.         ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno,
  689.                       ngx_open_dir_n " \"%s\" failed", tree->data);
  690.         return NGX_ERROR;
  691.     }

  692.     prev = ctx->data;

  693.     if (ctx->alloc) {
  694.         data = ngx_alloc(ctx->alloc, ctx->log);
  695.         if (data == NULL) {
  696.             goto failed;
  697.         }

  698.         if (ctx->init_handler(data, prev) == NGX_ABORT) {
  699.             goto failed;
  700.         }

  701.         ctx->data = data;

  702.     } else {
  703.         data = NULL;
  704.     }

  705.     for ( ;; ) {

  706.         ngx_set_errno(0);

  707.         if (ngx_read_dir(&dir) == NGX_ERROR) {
  708.             err = ngx_errno;

  709.             if (err == NGX_ENOMOREFILES) {
  710.                 rc = NGX_OK;

  711.             } else {
  712.                 ngx_log_error(NGX_LOG_CRIT, ctx->log, err,
  713.                               ngx_read_dir_n " \"%s\" failed", tree->data);
  714.                 rc = NGX_ERROR;
  715.             }

  716.             goto done;
  717.         }

  718.         len = ngx_de_namelen(&dir);
  719.         name = ngx_de_name(&dir);

  720.         ngx_log_debug2(NGX_LOG_DEBUG_CORE, ctx->log, 0,
  721.                       "tree name %uz:\"%s\"", len, name);

  722.         if (len == 1 && name[0] == '.') {
  723.             continue;
  724.         }

  725.         if (len == 2 && name[0] == '.' && name[1] == '.') {
  726.             continue;
  727.         }

  728.         file.len = tree->len + 1 + len;

  729.         if (file.len + NGX_DIR_MASK_LEN > buf.len) {

  730.             if (buf.len) {
  731.                 ngx_free(buf.data);
  732.             }

  733.             buf.len = tree->len + 1 + len + NGX_DIR_MASK_LEN;

  734.             buf.data = ngx_alloc(buf.len + 1, ctx->log);
  735.             if (buf.data == NULL) {
  736.                 goto failed;
  737.             }
  738.         }

  739.         p = ngx_cpymem(buf.data, tree->data, tree->len);
  740.         *p++ = '/';
  741.         ngx_memcpy(p, name, len + 1);

  742.         file.data = buf.data;

  743.         ngx_log_debug1(NGX_LOG_DEBUG_CORE, ctx->log, 0,
  744.                        "tree path \"%s\"", file.data);

  745.         if (!dir.valid_info) {
  746.             if (ngx_de_info(file.data, &dir) == NGX_FILE_ERROR) {
  747.                 ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno,
  748.                               ngx_de_info_n " \"%s\" failed", file.data);
  749.                 continue;
  750.             }
  751.         }

  752.         if (ngx_de_is_file(&dir)) {

  753.             ngx_log_debug1(NGX_LOG_DEBUG_CORE, ctx->log, 0,
  754.                            "tree file \"%s\"", file.data);

  755.             ctx->size = ngx_de_size(&dir);
  756.             ctx->fs_size = ngx_de_fs_size(&dir);
  757.             ctx->access = ngx_de_access(&dir);
  758.             ctx->mtime = ngx_de_mtime(&dir);

  759.             if (ctx->file_handler(ctx, &file) == NGX_ABORT) {
  760.                 goto failed;
  761.             }

  762.         } else if (ngx_de_is_dir(&dir)) {

  763.             ngx_log_debug1(NGX_LOG_DEBUG_CORE, ctx->log, 0,
  764.                            "tree enter dir \"%s\"", file.data);

  765.             ctx->access = ngx_de_access(&dir);
  766.             ctx->mtime = ngx_de_mtime(&dir);

  767.             rc = ctx->pre_tree_handler(ctx, &file);

  768.             if (rc == NGX_ABORT) {
  769.                 goto failed;
  770.             }

  771.             if (rc == NGX_DECLINED) {
  772.                 ngx_log_debug1(NGX_LOG_DEBUG_CORE, ctx->log, 0,
  773.                                "tree skip dir \"%s\"", file.data);
  774.                 continue;
  775.             }

  776.             if (ngx_walk_tree(ctx, &file) == NGX_ABORT) {
  777.                 goto failed;
  778.             }

  779.             ctx->access = ngx_de_access(&dir);
  780.             ctx->mtime = ngx_de_mtime(&dir);

  781.             if (ctx->post_tree_handler(ctx, &file) == NGX_ABORT) {
  782.                 goto failed;
  783.             }

  784.         } else {

  785.             ngx_log_debug1(NGX_LOG_DEBUG_CORE, ctx->log, 0,
  786.                            "tree special \"%s\"", file.data);

  787.             if (ctx->spec_handler(ctx, &file) == NGX_ABORT) {
  788.                 goto failed;
  789.             }
  790.         }
  791.     }

  792. failed:

  793.     rc = NGX_ABORT;

  794. done:

  795.     if (buf.len) {
  796.         ngx_free(buf.data);
  797.     }

  798.     if (data) {
  799.         ngx_free(data);
  800.         ctx->data = prev;
  801.     }

  802.     if (ngx_close_dir(&dir) == NGX_ERROR) {
  803.         ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno,
  804.                       ngx_close_dir_n " \"%s\" failed", tree->data);
  805.     }

  806.     return rc;
  807. }