src/core/ngx_file.c - nginx source code

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. #if (NGX_THREADS && NGX_HAVE_PWRITEV)

  87.     if (tf->thread_write) {
  88.         return ngx_thread_write_chain_to_file(&tf->file, chain, tf->offset,
  89.                                               tf->pool);
  90.     }

  91. #endif

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


  94. ngx_int_t
  95. ngx_create_temp_file(ngx_file_t *file, ngx_path_t *path, ngx_pool_t *pool,
  96.     ngx_uint_t persistent, ngx_uint_t clean, ngx_uint_t access)
  97. {
  98.     size_t                    levels;
  99.     u_char                   *p;
  100.     uint32_t                  n;
  101.     ngx_err_t                 err;
  102.     ngx_str_t                 name;
  103.     ngx_uint_t                prefix;
  104.     ngx_pool_cleanup_t       *cln;
  105.     ngx_pool_cleanup_file_t  *clnf;

  106.     if (file->name.len) {
  107.         name = file->name;
  108.         levels = 0;
  109.         prefix = 1;

  110.     } else {
  111.         name = path->name;
  112.         levels = path->len;
  113.         prefix = 0;
  114.     }

  115.     file->name.len = name.len + 1 + levels + 10;

  116.     file->name.data = ngx_pnalloc(pool, file->name.len + 1);
  117.     if (file->name.data == NULL) {
  118.         return NGX_ERROR;
  119.     }

  120. #if 0
  121.     for (i = 0; i < file->name.len; i++) {
  122.         file->name.data[i] = 'X';
  123.     }
  124. #endif

  125.     p = ngx_cpymem(file->name.data, name.data, name.len);

  126.     if (prefix) {
  127.         *p = '.';
  128.     }

  129.     p += 1 + levels;

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

  131.     cln = ngx_pool_cleanup_add(pool, sizeof(ngx_pool_cleanup_file_t));
  132.     if (cln == NULL) {
  133.         return NGX_ERROR;
  134.     }

  135.     for ( ;; ) {
  136.         (void) ngx_sprintf(p, "%010uD%Z", n);

  137.         if (!prefix) {
  138.             ngx_create_hashed_filename(path, file->name.data, file->name.len);
  139.         }

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

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

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

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

  146.             cln->handler = clean ? ngx_pool_delete_file : ngx_pool_cleanup_file;
  147.             clnf = cln->data;

  148.             clnf->fd = file->fd;
  149.             clnf->name = file->name.data;
  150.             clnf->log = pool->log;

  151.             return NGX_OK;
  152.         }

  153.         err = ngx_errno;

  154.         if (err == NGX_EEXIST_FILE) {
  155.             n = (uint32_t) ngx_next_temp_number(1);
  156.             continue;
  157.         }

  158.         if ((path->level[0] == 0) || (err != NGX_ENOPATH)) {
  159.             ngx_log_error(NGX_LOG_CRIT, file->log, err,
  160.                           ngx_open_tempfile_n " \"%s\" failed",
  161.                           file->name.data);
  162.             return NGX_ERROR;
  163.         }

  164.         if (ngx_create_path(file, path) == NGX_ERROR) {
  165.             return NGX_ERROR;
  166.         }
  167.     }
  168. }


  169. void
  170. ngx_create_hashed_filename(ngx_path_t *path, u_char *file, size_t len)
  171. {
  172.     size_t      i, level;
  173.     ngx_uint_t  n;

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

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

  176.     for (n = 0; n < NGX_MAX_PATH_LEVEL; n++) {
  177.         level = path->level[n];

  178.         if (level == 0) {
  179.             break;
  180.         }

  181.         len -= level;
  182.         file[i - 1] = '/';
  183.         ngx_memcpy(&file[i], &file[len], level);
  184.         i += level + 1;
  185.     }
  186. }


  187. ngx_int_t
  188. ngx_create_path(ngx_file_t *file, ngx_path_t *path)
  189. {
  190.     size_t      pos;
  191.     ngx_err_t   err;
  192.     ngx_uint_t  i;

  193.     pos = path->name.len;

  194.     for (i = 0; i < NGX_MAX_PATH_LEVEL; i++) {
  195.         if (path->level[i] == 0) {
  196.             break;
  197.         }

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

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

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

  202.         if (ngx_create_dir(file->name.data, 0700) == NGX_FILE_ERROR) {
  203.             err = ngx_errno;
  204.             if (err != NGX_EEXIST) {
  205.                 ngx_log_error(NGX_LOG_CRIT, file->log, err,
  206.                               ngx_create_dir_n " \"%s\" failed",
  207.                               file->name.data);
  208.                 return NGX_ERROR;
  209.             }
  210.         }

  211.         file->name.data[pos] = '/';
  212.     }

  213.     return NGX_OK;
  214. }


  215. ngx_err_t
  216. ngx_create_full_path(u_char *dir, ngx_uint_t access)
  217. {
  218.     u_char     *p, ch;
  219.     ngx_err_t   err;

  220.     err = 0;

  221. #if (NGX_WIN32)
  222.     p = dir + 3;
  223. #else
  224.     p = dir + 1;
  225. #endif

  226.     for ( /* void */ ; *p; p++) {
  227.         ch = *p;

  228.         if (ch != '/') {
  229.             continue;
  230.         }

  231.         *p = '\0';

  232.         if (ngx_create_dir(dir, access) == NGX_FILE_ERROR) {
  233.             err = ngx_errno;

  234.             switch (err) {
  235.             case NGX_EEXIST:
  236.                 err = 0;
  237.             case NGX_EACCES:
  238.                 break;

  239.             default:
  240.                 return err;
  241.             }
  242.         }

  243.         *p = '/';
  244.     }

  245.     return err;
  246. }


  247. ngx_atomic_uint_t
  248. ngx_next_temp_number(ngx_uint_t collision)
  249. {
  250.     ngx_atomic_uint_t  n, add;

  251.     add = collision ? ngx_random_number : 1;

  252.     n = ngx_atomic_fetch_add(ngx_temp_number, add);

  253.     return n + add;
  254. }


  255. char *
  256. ngx_conf_set_path_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
  257. {
  258.     char  *p = conf;

  259.     ssize_t      level;
  260.     ngx_str_t   *value;
  261.     ngx_uint_t   i, n;
  262.     ngx_path_t  *path, **slot;

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

  264.     if (*slot) {
  265.         return "is duplicate";
  266.     }

  267.     path = ngx_pcalloc(cf->pool, sizeof(ngx_path_t));
  268.     if (path == NULL) {
  269.         return NGX_CONF_ERROR;
  270.     }

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

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

  273.     if (path->name.data[path->name.len - 1] == '/') {
  274.         path->name.len--;
  275.     }

  276.     if (ngx_conf_full_name(cf->cycle, &path->name, 0) != NGX_OK) {
  277.         return NGX_CONF_ERROR;
  278.     }

  279.     path->conf_file = cf->conf_file->file.name.data;
  280.     path->line = cf->conf_file->line;

  281.     for (i = 0, n = 2; n < cf->args->nelts; i++, n++) {
  282.         level = ngx_atoi(value[n].data, value[n].len);
  283.         if (level == NGX_ERROR || level == 0) {
  284.             return "invalid value";
  285.         }

  286.         path->level[i] = level;
  287.         path->len += level + 1;
  288.     }

  289.     if (path->len > 10 + i) {
  290.         return "invalid value";
  291.     }

  292.     *slot = path;

  293.     if (ngx_add_path(cf, slot) == NGX_ERROR) {
  294.         return NGX_CONF_ERROR;
  295.     }

  296.     return NGX_CONF_OK;
  297. }


  298. char *
  299. ngx_conf_merge_path_value(ngx_conf_t *cf, ngx_path_t **path, ngx_path_t *prev,
  300.     ngx_path_init_t *init)
  301. {
  302.     ngx_uint_t  i;

  303.     if (*path) {
  304.         return NGX_CONF_OK;
  305.     }

  306.     if (prev) {
  307.         *path = prev;
  308.         return NGX_CONF_OK;
  309.     }

  310.     *path = ngx_pcalloc(cf->pool, sizeof(ngx_path_t));
  311.     if (*path == NULL) {
  312.         return NGX_CONF_ERROR;
  313.     }

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

  315.     if (ngx_conf_full_name(cf->cycle, &(*path)->name, 0) != NGX_OK) {
  316.         return NGX_CONF_ERROR;
  317.     }

  318.     for (i = 0; i < NGX_MAX_PATH_LEVEL; i++) {
  319.         (*path)->level[i] = init->level[i];
  320.         (*path)->len += init->level[i] + (init->level[i] ? 1 : 0);
  321.     }

  322.     if (ngx_add_path(cf, path) != NGX_OK) {
  323.         return NGX_CONF_ERROR;
  324.     }

  325.     return NGX_CONF_OK;
  326. }


  327. char *
  328. ngx_conf_set_access_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
  329. {
  330.     char  *confp = conf;

  331.     u_char      *p;
  332.     ngx_str_t   *value;
  333.     ngx_uint_t   i, right, shift, *access, user;

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

  335.     if (*access != NGX_CONF_UNSET_UINT) {
  336.         return "is duplicate";
  337.     }

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

  339.     *access = 0;
  340.     user = 0600;

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

  342.         p = value[i].data;

  343.         if (ngx_strncmp(p, "user:", sizeof("user:") - 1) == 0) {
  344.             shift = 6;
  345.             p += sizeof("user:") - 1;
  346.             user = 0;

  347.         } else if (ngx_strncmp(p, "group:", sizeof("group:") - 1) == 0) {
  348.             shift = 3;
  349.             p += sizeof("group:") - 1;

  350.         } else if (ngx_strncmp(p, "all:", sizeof("all:") - 1) == 0) {
  351.             shift = 0;
  352.             p += sizeof("all:") - 1;

  353.         } else {
  354.             goto invalid;
  355.         }

  356.         if (ngx_strcmp(p, "rw") == 0) {
  357.             right = 6;

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

  360.         } else {
  361.             goto invalid;
  362.         }

  363.         *access |= right << shift;
  364.     }

  365.     *access |= user;

  366.     return NGX_CONF_OK;

  367. invalid:

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

  369.     return NGX_CONF_ERROR;
  370. }


  371. ngx_int_t
  372. ngx_add_path(ngx_conf_t *cf, ngx_path_t **slot)
  373. {
  374.     ngx_uint_t   i, n;
  375.     ngx_path_t  *path, **p;

  376.     path = *slot;

  377.     p = cf->cycle->paths.elts;
  378.     for (i = 0; i < cf->cycle->paths.nelts; i++) {
  379.         if (p[i]->name.len == path->name.len
  380.             && ngx_strcmp(p[i]->name.data, path->name.data) == 0)
  381.         {
  382.             if (p[i]->data != path->data) {
  383.                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  384.                                    "the same path name \"%V\" "
  385.                                    "used in %s:%ui and",
  386.                                    &p[i]->name, p[i]->conf_file, p[i]->line);
  387.                 return NGX_ERROR;
  388.             }

  389.             for (n = 0; n < NGX_MAX_PATH_LEVEL; n++) {
  390.                 if (p[i]->level[n] != path->level[n]) {
  391.                     if (path->conf_file == NULL) {
  392.                         if (p[i]->conf_file == NULL) {
  393.                             ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
  394.                                       "the default path name \"%V\" has "
  395.                                       "the same name as another default path, "
  396.                                       "but the different levels, you need to "
  397.                                       "redefine one of them in http section",
  398.                                       &p[i]->name);
  399.                             return NGX_ERROR;
  400.                         }

  401.                         ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
  402.                                       "the path name \"%V\" in %s:%ui has "
  403.                                       "the same name as default path, but "
  404.                                       "the different levels, you need to "
  405.                                       "define default path in http section",
  406.                                       &p[i]->name, p[i]->conf_file, p[i]->line);
  407.                         return NGX_ERROR;
  408.                     }

  409.                     ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  410.                                       "the same path name \"%V\" in %s:%ui "
  411.                                       "has the different levels than",
  412.                                       &p[i]->name, p[i]->conf_file, p[i]->line);
  413.                     return NGX_ERROR;
  414.                 }

  415.                 if (p[i]->level[n] == 0) {
  416.                     break;
  417.                 }
  418.             }

  419.             *slot = p[i];

  420.             return NGX_OK;
  421.         }
  422.     }

  423.     p = ngx_array_push(&cf->cycle->paths);
  424.     if (p == NULL) {
  425.         return NGX_ERROR;
  426.     }

  427.     *p = path;

  428.     return NGX_OK;
  429. }


  430. ngx_int_t
  431. ngx_create_paths(ngx_cycle_t *cycle, ngx_uid_t user)
  432. {
  433.     ngx_err_t         err;
  434.     ngx_uint_t        i;
  435.     ngx_path_t      **path;

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

  438.         if (ngx_create_dir(path[i]->name.data, 0700) == NGX_FILE_ERROR) {
  439.             err = ngx_errno;
  440.             if (err != NGX_EEXIST) {
  441.                 ngx_log_error(NGX_LOG_EMERG, cycle->log, err,
  442.                               ngx_create_dir_n " \"%s\" failed",
  443.                               path[i]->name.data);
  444.                 return NGX_ERROR;
  445.             }
  446.         }

  447.         if (user == (ngx_uid_t) NGX_CONF_UNSET_UINT) {
  448.             continue;
  449.         }

  450. #if !(NGX_WIN32)
  451.         {
  452.         ngx_file_info_t   fi;

  453.         if (ngx_file_info(path[i]->name.data, &fi) == NGX_FILE_ERROR) {
  454.             ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
  455.                           ngx_file_info_n " \"%s\" failed", path[i]->name.data);
  456.             return NGX_ERROR;
  457.         }

  458.         if (fi.st_uid != user) {
  459.             if (chown((const char *) path[i]->name.data, user, -1) == -1) {
  460.                 ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
  461.                               "chown(\"%s\", %d) failed",
  462.                               path[i]->name.data, user);
  463.                 return NGX_ERROR;
  464.             }
  465.         }

  466.         if ((fi.st_mode & (S_IRUSR|S_IWUSR|S_IXUSR))
  467.                                                   != (S_IRUSR|S_IWUSR|S_IXUSR))
  468.         {
  469.             fi.st_mode |= (S_IRUSR|S_IWUSR|S_IXUSR);

  470.             if (chmod((const char *) path[i]->name.data, fi.st_mode) == -1) {
  471.                 ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
  472.                               "chmod() \"%s\" failed", path[i]->name.data);
  473.                 return NGX_ERROR;
  474.             }
  475.         }
  476.         }
  477. #endif
  478.     }

  479.     return NGX_OK;
  480. }


  481. ngx_int_t
  482. ngx_ext_rename_file(ngx_str_t *src, ngx_str_t *to, ngx_ext_rename_file_t *ext)
  483. {
  484.     u_char           *name;
  485.     ngx_err_t         err;
  486.     ngx_copy_file_t   cf;

  487. #if !(NGX_WIN32)

  488.     if (ext->access) {
  489.         if (ngx_change_file_access(src->data, ext->access) == NGX_FILE_ERROR) {
  490.             ngx_log_error(NGX_LOG_CRIT, ext->log, ngx_errno,
  491.                           ngx_change_file_access_n " \"%s\" failed", src->data);
  492.             err = 0;
  493.             goto failed;
  494.         }
  495.     }

  496. #endif

  497.     if (ext->time != -1) {
  498.         if (ngx_set_file_time(src->data, ext->fd, ext->time) != NGX_OK) {
  499.             ngx_log_error(NGX_LOG_CRIT, ext->log, ngx_errno,
  500.                           ngx_set_file_time_n " \"%s\" failed", src->data);
  501.             err = 0;
  502.             goto failed;
  503.         }
  504.     }

  505.     if (ngx_rename_file(src->data, to->data) != NGX_FILE_ERROR) {
  506.         return NGX_OK;
  507.     }

  508.     err = ngx_errno;

  509.     if (err == NGX_ENOPATH) {

  510.         if (!ext->create_path) {
  511.             goto failed;
  512.         }

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

  514.         if (err) {
  515.             ngx_log_error(NGX_LOG_CRIT, ext->log, err,
  516.                           ngx_create_dir_n " \"%s\" failed", to->data);
  517.             err = 0;
  518.             goto failed;
  519.         }

  520.         if (ngx_rename_file(src->data, to->data) != NGX_FILE_ERROR) {
  521.             return NGX_OK;
  522.         }

  523.         err = ngx_errno;
  524.     }

  525. #if (NGX_WIN32)

  526.     if (err == NGX_EEXIST || err == NGX_EEXIST_FILE) {
  527.         err = ngx_win32_rename_file(src, to, ext->log);

  528.         if (err == 0) {
  529.             return NGX_OK;
  530.         }
  531.     }

  532. #endif

  533.     if (err == NGX_EXDEV) {

  534.         cf.size = -1;
  535.         cf.buf_size = 0;
  536.         cf.access = ext->access;
  537.         cf.time = ext->time;
  538.         cf.log = ext->log;

  539.         name = ngx_alloc(to->len + 1 + 10 + 1, ext->log);
  540.         if (name == NULL) {
  541.             return NGX_ERROR;
  542.         }

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

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

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

  548.                 if (ngx_delete_file(src->data) == NGX_FILE_ERROR) {
  549.                     ngx_log_error(NGX_LOG_CRIT, ext->log, ngx_errno,
  550.                                   ngx_delete_file_n " \"%s\" failed",
  551.                                   src->data);
  552.                     return NGX_ERROR;
  553.                 }

  554.                 return NGX_OK;
  555.             }

  556.             ngx_log_error(NGX_LOG_CRIT, ext->log, ngx_errno,
  557.                           ngx_rename_file_n " \"%s\" to \"%s\" failed",
  558.                           name, to->data);

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

  562.             }
  563.         }

  564.         ngx_free(name);

  565.         err = 0;
  566.     }

  567. failed:

  568.     if (ext->delete_file) {
  569.         if (ngx_delete_file(src->data) == NGX_FILE_ERROR) {
  570.             ngx_log_error(NGX_LOG_CRIT, ext->log, ngx_errno,
  571.                           ngx_delete_file_n " \"%s\" failed", src->data);
  572.         }
  573.     }

  574.     if (err) {
  575.         ngx_log_error(NGX_LOG_CRIT, ext->log, err,
  576.                       ngx_rename_file_n " \"%s\" to \"%s\" failed",
  577.                       src->data, to->data);
  578.     }

  579.     return NGX_ERROR;
  580. }


  581. ngx_int_t
  582. ngx_copy_file(u_char *from, u_char *to, ngx_copy_file_t *cf)
  583. {
  584.     char             *buf;
  585.     off_t             size;
  586.     time_t            time;
  587.     size_t            len;
  588.     ssize_t           n;
  589.     ngx_fd_t          fd, nfd;
  590.     ngx_int_t         rc;
  591.     ngx_uint_t        access;
  592.     ngx_file_info_t   fi;

  593.     rc = NGX_ERROR;
  594.     buf = NULL;
  595.     nfd = NGX_INVALID_FILE;

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

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

  602.     if (cf->size != -1 && cf->access != 0 && cf->time != -1) {
  603.         size = cf->size;
  604.         access = cf->access;
  605.         time = cf->time;

  606.     } else {
  607.         if (ngx_fd_info(fd, &fi) == NGX_FILE_ERROR) {
  608.             ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,
  609.                           ngx_fd_info_n " \"%s\" failed", from);

  610.             goto failed;
  611.         }

  612.         size = (cf->size != -1) ? cf->size : ngx_file_size(&fi);
  613.         access = cf->access ? cf->access : ngx_file_access(&fi);
  614.         time = (cf->time != -1) ? cf->time : ngx_file_mtime(&fi);
  615.     }

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

  617.     if ((off_t) len > size) {
  618.         len = (size_t) size;
  619.     }

  620.     buf = ngx_alloc(len, cf->log);
  621.     if (buf == NULL) {
  622.         goto failed;
  623.     }

  624.     nfd = ngx_open_file(to, NGX_FILE_WRONLY, NGX_FILE_TRUNCATE, access);

  625.     if (nfd == NGX_INVALID_FILE) {
  626.         ngx_log_error(NGX_LOG_CRIT, cf->log, ngx_errno,
  627.                       ngx_open_file_n " \"%s\" failed", to);
  628.         goto failed;
  629.     }

  630.     while (size > 0) {

  631.         if ((off_t) len > size) {
  632.             len = (size_t) size;
  633.         }

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

  635.         if (n == -1) {
  636.             ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,
  637.                           ngx_read_fd_n " \"%s\" failed", from);
  638.             goto failed;
  639.         }

  640.         if ((size_t) n != len) {
  641.             ngx_log_error(NGX_LOG_ALERT, cf->log, 0,
  642.                           ngx_read_fd_n " has read only %z of %O from %s",
  643.                           n, size, from);
  644.             goto failed;
  645.         }

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

  647.         if (n == -1) {
  648.             ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,
  649.                           ngx_write_fd_n " \"%s\" failed", to);
  650.             goto failed;
  651.         }

  652.         if ((size_t) n != len) {
  653.             ngx_log_error(NGX_LOG_ALERT, cf->log, 0,
  654.                           ngx_write_fd_n " has written only %z of %O to %s",
  655.                           n, size, to);
  656.             goto failed;
  657.         }

  658.         size -= n;
  659.     }

  660.     if (ngx_set_file_time(to, nfd, time) != NGX_OK) {
  661.         ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,
  662.                       ngx_set_file_time_n " \"%s\" failed", to);
  663.         goto failed;
  664.     }

  665.     rc = NGX_OK;

  666. failed:

  667.     if (nfd != NGX_INVALID_FILE) {
  668.         if (ngx_close_file(nfd) == NGX_FILE_ERROR) {
  669.             ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,
  670.                           ngx_close_file_n " \"%s\" failed", to);
  671.         }
  672.     }

  673.     if (fd != NGX_INVALID_FILE) {
  674.         if (ngx_close_file(fd) == NGX_FILE_ERROR) {
  675.             ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,
  676.                           ngx_close_file_n " \"%s\" failed", from);
  677.         }
  678.     }

  679.     if (buf) {
  680.         ngx_free(buf);
  681.     }

  682.     return rc;
  683. }


  684. /*
  685. * ctx->init_handler() - see ctx->alloc
  686. * ctx->file_handler() - file handler
  687. * ctx->pre_tree_handler() - handler is called before entering directory
  688. * ctx->post_tree_handler() - handler is called after leaving directory
  689. * ctx->spec_handler() - special (socket, FIFO, etc.) file handler
  690. *
  691. * ctx->data - some data structure, it may be the same on all levels, or
  692. *     reallocated if ctx->alloc is nonzero
  693. *
  694. * ctx->alloc - a size of data structure that is allocated at every level
  695. *     and is initialized by ctx->init_handler()
  696. *
  697. * ctx->log - a log
  698. *
  699. * on fatal (memory) error handler must return NGX_ABORT to stop walking tree
  700. */

  701. ngx_int_t
  702. ngx_walk_tree(ngx_tree_ctx_t *ctx, ngx_str_t *tree)
  703. {
  704.     void       *data, *prev;
  705.     u_char     *p, *name;
  706.     size_t      len;
  707.     ngx_int_t   rc;
  708.     ngx_err_t   err;
  709.     ngx_str_t   file, buf;
  710.     ngx_dir_t   dir;

  711.     ngx_str_null(&buf);

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

  714.     if (ngx_open_dir(tree, &dir) == NGX_ERROR) {
  715.         ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno,
  716.                       ngx_open_dir_n " \"%s\" failed", tree->data);
  717.         return NGX_ERROR;
  718.     }

  719.     prev = ctx->data;

  720.     if (ctx->alloc) {
  721.         data = ngx_alloc(ctx->alloc, ctx->log);
  722.         if (data == NULL) {
  723.             goto failed;
  724.         }

  725.         if (ctx->init_handler(data, prev) == NGX_ABORT) {
  726.             goto failed;
  727.         }

  728.         ctx->data = data;

  729.     } else {
  730.         data = NULL;
  731.     }

  732.     for ( ;; ) {

  733.         ngx_set_errno(0);

  734.         if (ngx_read_dir(&dir) == NGX_ERROR) {
  735.             err = ngx_errno;

  736.             if (err == NGX_ENOMOREFILES) {
  737.                 rc = NGX_OK;

  738.             } else {
  739.                 ngx_log_error(NGX_LOG_CRIT, ctx->log, err,
  740.                               ngx_read_dir_n " \"%s\" failed", tree->data);
  741.                 rc = NGX_ERROR;
  742.             }

  743.             goto done;
  744.         }

  745.         len = ngx_de_namelen(&dir);
  746.         name = ngx_de_name(&dir);

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

  749.         if (len == 1 && name[0] == '.') {
  750.             continue;
  751.         }

  752.         if (len == 2 && name[0] == '.' && name[1] == '.') {
  753.             continue;
  754.         }

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

  756.         if (file.len > buf.len) {

  757.             if (buf.len) {
  758.                 ngx_free(buf.data);
  759.             }

  760.             buf.len = tree->len + 1 + len;

  761.             buf.data = ngx_alloc(buf.len + 1, ctx->log);
  762.             if (buf.data == NULL) {
  763.                 goto failed;
  764.             }
  765.         }

  766.         p = ngx_cpymem(buf.data, tree->data, tree->len);
  767.         *p++ = '/';
  768.         ngx_memcpy(p, name, len + 1);

  769.         file.data = buf.data;

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

  772.         if (!dir.valid_info) {
  773.             if (ngx_de_info(file.data, &dir) == NGX_FILE_ERROR) {
  774.                 ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno,
  775.                               ngx_de_info_n " \"%s\" failed", file.data);
  776.                 continue;
  777.             }
  778.         }

  779.         if (ngx_de_is_file(&dir)) {

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

  782.             ctx->size = ngx_de_size(&dir);
  783.             ctx->fs_size = ngx_de_fs_size(&dir);
  784.             ctx->access = ngx_de_access(&dir);
  785.             ctx->mtime = ngx_de_mtime(&dir);

  786.             if (ctx->file_handler(ctx, &file) == NGX_ABORT) {
  787.                 goto failed;
  788.             }

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

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

  792.             ctx->access = ngx_de_access(&dir);
  793.             ctx->mtime = ngx_de_mtime(&dir);

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

  795.             if (rc == NGX_ABORT) {
  796.                 goto failed;
  797.             }

  798.             if (rc == NGX_DECLINED) {
  799.                 ngx_log_debug1(NGX_LOG_DEBUG_CORE, ctx->log, 0,
  800.                                "tree skip dir \"%s\"", file.data);
  801.                 continue;
  802.             }

  803.             if (ngx_walk_tree(ctx, &file) == NGX_ABORT) {
  804.                 goto failed;
  805.             }

  806.             ctx->access = ngx_de_access(&dir);
  807.             ctx->mtime = ngx_de_mtime(&dir);

  808.             if (ctx->post_tree_handler(ctx, &file) == NGX_ABORT) {
  809.                 goto failed;
  810.             }

  811.         } else {

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

  814.             if (ctx->spec_handler(ctx, &file) == NGX_ABORT) {
  815.                 goto failed;
  816.             }
  817.         }
  818.     }

  819. failed:

  820.     rc = NGX_ABORT;

  821. done:

  822.     if (buf.len) {
  823.         ngx_free(buf.data);
  824.     }

  825.     if (data) {
  826.         ngx_free(data);
  827.         ctx->data = prev;
  828.     }

  829.     if (ngx_close_dir(&dir) == NGX_ERROR) {
  830.         ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno,
  831.                       ngx_close_dir_n " \"%s\" failed", tree->data);
  832.     }

  833.     return rc;
  834. }