upstream nginx-0.7.38
[nginx.git] / nginx / src / core / ngx_file.c
1
2 /*
3  * Copyright (C) Igor Sysoev
4  */
5
6
7 #include <ngx_config.h>
8 #include <ngx_core.h>
9
10
11 static ngx_atomic_uint_t  ngx_temp_number;
12 static ngx_atomic_uint_t  ngx_random_number;
13
14
15 ssize_t
16 ngx_write_chain_to_temp_file(ngx_temp_file_t *tf, ngx_chain_t *chain)
17 {
18     ngx_int_t  rc;
19
20     if (tf->file.fd == NGX_INVALID_FILE) {
21         rc = ngx_create_temp_file(&tf->file, tf->path, tf->pool,
22                                   tf->persistent, tf->clean, tf->access);
23
24         if (rc == NGX_ERROR || rc == NGX_AGAIN) {
25             return rc;
26         }
27
28         if (tf->log_level) {
29             ngx_log_error(tf->log_level, tf->file.log, 0, "%s %V",
30                           tf->warn, &tf->file.name);
31         }
32     }
33
34     return ngx_write_chain_to_file(&tf->file, chain, tf->offset, tf->pool);
35 }
36
37
38 ngx_int_t
39 ngx_create_temp_file(ngx_file_t *file, ngx_path_t *path, ngx_pool_t *pool,
40     ngx_uint_t persistent, ngx_uint_t clean, ngx_uint_t access)
41 {
42     uint32_t                  n;
43     ngx_err_t                 err;
44     ngx_pool_cleanup_t       *cln;
45     ngx_pool_cleanup_file_t  *clnf;
46
47     file->name.len = path->name.len + 1 + path->len + 10;
48
49     file->name.data = ngx_pnalloc(pool, file->name.len + 1);
50     if (file->name.data == NULL) {
51         return NGX_ERROR;
52     }
53
54 #if 0
55     for (i = 0; i < file->name.len; i++) {
56          file->name.data[i] = 'X';
57     }
58 #endif
59
60     ngx_memcpy(file->name.data, path->name.data, path->name.len);
61
62     n = (uint32_t) ngx_next_temp_number(0);
63
64     cln = ngx_pool_cleanup_add(pool, sizeof(ngx_pool_cleanup_file_t));
65     if (cln == NULL) {
66         return NGX_ERROR;
67     }
68
69     for ( ;; ) {
70         (void) ngx_sprintf(file->name.data + path->name.len + 1 + path->len,
71                            "%010uD%Z", n);
72
73         ngx_create_hashed_filename(path, file->name.data, file->name.len);
74
75         ngx_log_debug1(NGX_LOG_DEBUG_CORE, file->log, 0,
76                        "hashed path: %s", file->name.data);
77
78         file->fd = ngx_open_tempfile(file->name.data, persistent, access);
79
80         ngx_log_debug1(NGX_LOG_DEBUG_CORE, file->log, 0,
81                        "temp fd:%d", file->fd);
82
83         if (file->fd != NGX_INVALID_FILE) {
84
85             cln->handler = clean ? ngx_pool_delete_file : ngx_pool_cleanup_file;
86             clnf = cln->data;
87
88             clnf->fd = file->fd;
89             clnf->name = file->name.data;
90             clnf->log = pool->log;
91
92             return NGX_OK;
93         }
94
95         err = ngx_errno;
96
97         if (err == NGX_EEXIST) {
98             n = (uint32_t) ngx_next_temp_number(1);
99             continue;
100         }
101
102         if ((path->level[0] == 0)
103             || (err != NGX_ENOENT
104 #if (NGX_WIN32)
105                 && err != NGX_ENOTDIR
106 #endif
107             ))
108         {
109             ngx_log_error(NGX_LOG_CRIT, file->log, err,
110                           ngx_open_tempfile_n " \"%s\" failed",
111                           file->name.data);
112             return NGX_ERROR;
113         }
114
115         if (ngx_create_path(file, path) == NGX_ERROR) {
116             return NGX_ERROR;
117         }
118     }
119 }
120
121
122 void
123 ngx_create_hashed_filename(ngx_path_t *path, u_char *file, size_t len)
124 {
125     size_t      i, level;
126     ngx_uint_t  n;
127
128     i = path->name.len + 1;
129
130     file[path->name.len + path->len]  = '/';
131
132     for (n = 0; n < 3; n++) {
133         level = path->level[n];
134
135         if (level == 0) {
136             break;
137         }
138
139         len -= level;
140         file[i - 1] = '/';
141         ngx_memcpy(&file[i], &file[len], level);
142         i += level + 1;
143     }
144 }
145
146
147 ngx_int_t
148 ngx_create_path(ngx_file_t *file, ngx_path_t *path)
149 {
150     size_t      pos;
151     ngx_err_t   err;
152     ngx_uint_t  i;
153
154     pos = path->name.len;
155
156     for (i = 0; i < 3; i++) {
157         if (path->level[i] == 0) {
158             break;
159         }
160
161         pos += path->level[i] + 1;
162
163         file->name.data[pos] = '\0';
164
165         ngx_log_debug1(NGX_LOG_DEBUG_CORE, file->log, 0,
166                        "temp file: \"%s\"", file->name.data);
167
168         if (ngx_create_dir(file->name.data, 0700) == NGX_FILE_ERROR) {
169             err = ngx_errno;
170             if (err != NGX_EEXIST) {
171                 ngx_log_error(NGX_LOG_CRIT, file->log, err,
172                               ngx_create_dir_n " \"%s\" failed",
173                               file->name.data);
174                 return NGX_ERROR;
175             }
176         }
177
178         file->name.data[pos] = '/';
179     }
180
181     return NGX_OK;
182 }
183
184
185 ngx_err_t
186 ngx_create_full_path(u_char *dir, ngx_uint_t access)
187 {
188     u_char     *p, ch;
189     ngx_err_t   err;
190
191     for (p = dir + 1; *p; p++) {
192         ch = *p;
193
194         if (ch != '/') {
195             continue;
196         }
197
198         *p = '\0';
199
200         if (ngx_create_dir(dir, access) == NGX_FILE_ERROR) {
201             err = ngx_errno;
202             if (err != NGX_EEXIST) {
203                 return err;
204             }
205         }
206
207         *p = '/';
208     }
209
210     return 0;
211 }
212
213
214 void
215 ngx_init_temp_number(void)
216 {
217     ngx_temp_number = 0;
218     ngx_random_number = 123456;
219 }
220
221
222 ngx_atomic_uint_t
223 ngx_next_temp_number(ngx_uint_t collision)
224 {
225     if (collision) {
226         ngx_temp_number += ngx_random_number;
227     }
228
229     return ngx_temp_number++;
230 }
231
232
233 char *
234 ngx_conf_set_path_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
235 {
236     char  *p = conf;
237
238     ssize_t      level;
239     ngx_str_t   *value;
240     ngx_uint_t   i, n;
241     ngx_path_t  *path, **slot;
242
243     slot = (ngx_path_t **) (p + cmd->offset);
244
245     if (*slot) {
246         return "is duplicate";
247     }
248
249     path = ngx_pcalloc(cf->pool, sizeof(ngx_path_t));
250     if (path == NULL) {
251         return NGX_CONF_ERROR;
252     }
253
254     value = cf->args->elts;
255
256     path->name = value[1];
257
258     if (path->name.data[path->name.len - 1] == '/') {
259         path->name.len--;
260     }
261
262     if (ngx_conf_full_name(cf->cycle, &path->name, 0) == NGX_ERROR) {
263         return NULL;
264     }
265
266     path->len = 0;
267     path->cleaner = (ngx_gc_handler_pt) cmd->post;
268     path->conf_file = cf->conf_file->file.name.data;
269     path->line = cf->conf_file->line;
270
271     for (i = 0, n = 2; n < cf->args->nelts; i++, n++) {
272         level = ngx_atoi(value[n].data, value[n].len);
273         if (level == NGX_ERROR || level == 0) {
274             return "invalid value";
275         }
276
277         path->level[i] = level;
278         path->len += level + 1;
279     }
280
281     while (i < 3) {
282         path->level[i++] = 0;
283     }
284
285     *slot = path;
286
287     if (ngx_add_path(cf, slot) == NGX_ERROR) {
288         return NGX_CONF_ERROR;
289     }
290
291     return NGX_CONF_OK;
292 }
293
294
295 char *
296 ngx_conf_set_access_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
297 {
298     char  *confp = conf;
299
300     u_char      *p;
301     ngx_str_t   *value;
302     ngx_uint_t   i, right, shift, *access;
303
304     access = (ngx_uint_t *) (confp + cmd->offset);
305
306     if (*access != NGX_CONF_UNSET_UINT) {
307         return "is duplicate";
308     }
309
310     value = cf->args->elts;
311
312     *access = 0600;
313
314     for (i = 1; i < cf->args->nelts; i++) {
315
316         p = value[i].data;
317
318         if (ngx_strncmp(p, "user:", sizeof("user:") - 1) == 0) {
319             shift = 6;
320             p += sizeof("user:") - 1;
321
322         } else if (ngx_strncmp(p, "group:", sizeof("group:") - 1) == 0) {
323             shift = 3;
324             p += sizeof("group:") - 1;
325
326         } else if (ngx_strncmp(p, "all:", sizeof("all:") - 1) == 0) {
327             shift = 0;
328             p += sizeof("all:") - 1;
329
330         } else {
331             goto invalid;
332         }
333
334         if (ngx_strcmp(p, "rw") == 0) {
335             right = 6;
336
337         } else if (ngx_strcmp(p, "r") == 0) {
338             right = 4;
339
340         } else {
341             goto invalid;
342         }
343
344         *access |= right << shift;
345     }
346
347     return NGX_CONF_OK;
348
349 invalid:
350
351     ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid value \"%V\"", &value[i]);
352
353     return NGX_CONF_ERROR;
354 }
355
356
357 ngx_int_t
358 ngx_add_path(ngx_conf_t *cf, ngx_path_t **slot)
359 {
360     ngx_uint_t   i, n;
361     ngx_path_t  *path, **p;
362
363     path = *slot;
364
365     p = cf->cycle->pathes.elts;
366     for (i = 0; i < cf->cycle->pathes.nelts; i++) {
367         if (p[i]->name.len == path->name.len
368             && ngx_strcmp(p[i]->name.data, path->name.data) == 0)
369         {
370             for (n = 0; n < 3; n++) {
371                 if (p[i]->level[n] != path->level[n]) {
372                     if (path->conf_file == NULL) {
373                         if (p[i]->conf_file == NULL) {
374                             ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
375                                       "the default path name \"%V\" has "
376                                       "the same name as another default path, "
377                                       "but the different levels, you need to "
378                                       "redefine one of them in http section",
379                                       &p[i]->name);
380                             return NGX_ERROR;
381                         }
382
383                         ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
384                                       "the path name \"%V\" in %s:%ui has "
385                                       "the same name as default path, but "
386                                       "the different levels, you need to "
387                                       "define default path in http section",
388                                       &p[i]->name, p[i]->conf_file, p[i]->line);
389                         return NGX_ERROR;
390                     }
391
392                     ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
393                                       "the same path name \"%V\" in %s:%ui "
394                                       "has the different levels than",
395                                       &p[i]->name, p[i]->conf_file, p[i]->line);
396                     return NGX_ERROR;
397                 }
398
399                 if (p[i]->level[n] == 0) {
400                     break;
401                 }
402             }
403
404             *slot = p[i];
405
406             return NGX_OK;
407         }
408     }
409
410     p = ngx_array_push(&cf->cycle->pathes);
411     if (p == NULL) {
412         return NGX_ERROR;
413     }
414
415     *p = path;
416
417     return NGX_OK;
418 }
419
420
421 ngx_int_t
422 ngx_create_pathes(ngx_cycle_t *cycle, ngx_uid_t user)
423 {
424     ngx_err_t         err;
425     ngx_uint_t        i;
426     ngx_path_t      **path;
427
428     path = cycle->pathes.elts;
429     for (i = 0; i < cycle->pathes.nelts; i++) {
430
431         if (ngx_create_dir(path[i]->name.data, 0700) == NGX_FILE_ERROR) {
432             err = ngx_errno;
433             if (err != NGX_EEXIST) {
434                 ngx_log_error(NGX_LOG_EMERG, cycle->log, err,
435                               ngx_create_dir_n " \"%s\" failed",
436                               path[i]->name.data);
437                 return NGX_ERROR;
438             }
439         }
440
441         if (user == (ngx_uid_t) NGX_CONF_UNSET_UINT) {
442             continue;
443         }
444
445 #if !(NGX_WIN32)
446         {
447         ngx_file_info_t   fi;
448
449         if (ngx_file_info((const char *) path[i]->name.data, &fi) == -1) {
450             ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
451                           ngx_file_info_n " \"%s\" failed", path[i]->name.data);
452             return NGX_ERROR;
453         }
454
455         if (fi.st_uid != user) {
456             if (chown((const char *) path[i]->name.data, user, -1) == -1) {
457                 ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
458                               "chown(\"%s\", %d) failed",
459                               path[i]->name.data, user);
460                 return NGX_ERROR;
461             }
462         }
463
464         if ((fi.st_mode & (S_IRUSR|S_IWUSR|S_IXUSR))
465                                                   != (S_IRUSR|S_IWUSR|S_IXUSR))
466         {
467             fi.st_mode |= (S_IRUSR|S_IWUSR|S_IXUSR);
468
469             if (chmod((const char *) path[i]->name.data, fi.st_mode) == -1) {
470                 ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
471                               "chmod() \"%s\" failed", path[i]->name.data);
472                 return NGX_ERROR;
473             }
474         }
475         }
476 #endif
477     }
478
479     return NGX_OK;
480 }
481
482
483 ngx_int_t
484 ngx_ext_rename_file(ngx_str_t *src, ngx_str_t *to, ngx_ext_rename_file_t *ext)
485 {
486     ngx_err_t  err;
487
488 #if !(NGX_WIN32)
489
490     if (ext->access) {
491         if (ngx_change_file_access(src->data, ext->access) == NGX_FILE_ERROR) {
492             ngx_log_error(NGX_LOG_CRIT, ext->log, ngx_errno,
493                           ngx_change_file_access_n " \"%s\" failed", src->data);
494             err = 0;
495             goto failed;
496         }
497     }
498
499 #endif
500
501     if (ext->time != -1) {
502         if (ngx_set_file_time(src->data, ext->fd, ext->time) != NGX_OK) {
503             ngx_log_error(NGX_LOG_CRIT, ext->log, ngx_errno,
504                           ngx_set_file_time_n " \"%s\" failed", src->data);
505             err = 0;
506             goto failed;
507         }
508     }
509
510     if (ngx_rename_file(src->data, to->data) != NGX_FILE_ERROR) {
511         return NGX_OK;
512     }
513
514     err = ngx_errno;
515
516     if (err == NGX_ENOENT) {
517
518         if (!ext->create_path) {
519             goto failed;
520         }
521
522         err = ngx_create_full_path(to->data, ngx_dir_access(ext->path_access));
523
524         if (err) {
525             ngx_log_error(NGX_LOG_CRIT, ext->log, err,
526                           ngx_create_dir_n " \"%s\" failed", to->data);
527             err = 0;
528             goto failed;
529         }
530
531         if (ngx_rename_file(src->data, to->data) != NGX_FILE_ERROR) {
532             return NGX_OK;
533         }
534
535         err = ngx_errno;
536         goto failed;
537     }
538
539 #if (NGX_WIN32)
540
541     if (err == NGX_EEXIST) {
542         if (ngx_win32_rename_file(src, to, ext->log) == NGX_OK) {
543
544             if (ngx_rename_file(src->data, to->data) != NGX_FILE_ERROR) {
545                 return NGX_OK;
546             }
547
548             err = ngx_errno;
549
550         } else {
551             err = 0;
552         }
553     }
554
555 #endif
556
557 failed:
558
559     if (ext->delete_file) {
560         if (ngx_delete_file(src->data) == NGX_FILE_ERROR) {
561             ngx_log_error(NGX_LOG_CRIT, ext->log, ngx_errno,
562                           ngx_delete_file_n " \"%s\" failed", src->data);
563         }
564     }
565
566     if (err && ext->log_rename_error) {
567         ngx_log_error(NGX_LOG_CRIT, ext->log, err,
568                       ngx_rename_file_n " \"%s\" to \"%s\" failed",
569                       src->data, to->data);
570     }
571
572     ext->rename_error = err;
573
574     return NGX_ERROR;
575 }
576
577
578 /*
579  * ctx->init_handler() - see ctx->alloc
580  * ctx->file_handler() - file handler
581  * ctx->pre_tree_handler() - handler is called before entering directory
582  * ctx->post_tree_handler() - handler is called after leaving directory
583  * ctx->spec_handler() - special (socket, FIFO, etc.) file handler
584  *
585  * ctx->data - some data structure, it may be the same on all levels, or
586  *     reallocated if ctx->alloc is nonzero
587  *
588  * ctx->alloc - a size of data structure that is allocated at every level
589  *     and is initilialized by ctx->init_handler()
590  *
591  * ctx->log - a log
592  *
593  * on fatal (memory) error handler must return NGX_ABORT to stop walking tree
594  */
595
596 ngx_int_t
597 ngx_walk_tree(ngx_tree_ctx_t *ctx, ngx_str_t *tree)
598 {
599     void       *data, *prev;
600     u_char     *p, *name;
601     size_t      len;
602     ngx_int_t   rc;
603     ngx_err_t   err;
604     ngx_str_t   file, buf;
605     ngx_dir_t   dir;
606
607     buf.len = 0;
608     buf.data = NULL;
609
610     ngx_log_debug1(NGX_LOG_DEBUG_CORE, ctx->log, 0,
611                    "walk tree \"%V\"", tree);
612
613     if (ngx_open_dir(tree, &dir) == NGX_ERROR) {
614         ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno,
615                       ngx_open_dir_n " \"%s\" failed", tree->data);
616         return NGX_ERROR;
617     }
618
619     prev = ctx->data;
620
621     if (ctx->alloc) {
622         data = ngx_alloc(ctx->alloc, ctx->log);
623         if (data == NULL) {
624             goto failed;
625         }
626
627         if (ctx->init_handler(data, prev) == NGX_ABORT) {
628             goto failed;
629         }
630
631         ctx->data = data;
632
633     } else {
634         data = NULL;
635     }
636
637     for ( ;; ) {
638
639         ngx_set_errno(0);
640
641         if (ngx_read_dir(&dir) == NGX_ERROR) {
642             err = ngx_errno;
643
644             if (err == NGX_ENOMOREFILES) {
645                 rc = NGX_OK;
646
647             } else {
648                 ngx_log_error(NGX_LOG_CRIT, ctx->log, err,
649                               ngx_read_dir_n " \"%s\" failed", tree->data);
650                 rc = NGX_ERROR;
651             }
652
653             goto done;
654         }
655
656         len = ngx_de_namelen(&dir);
657         name = ngx_de_name(&dir);
658
659         ngx_log_debug2(NGX_LOG_DEBUG_CORE, ctx->log, 0,
660                       "tree name %uz:\"%s\"", len, name);
661
662         if (len == 1 && name[0] == '.') {
663             continue;
664         }
665
666         if (len == 2 && name[0] == '.' && name[1] == '.') {
667             continue;
668         }
669
670         file.len = tree->len + 1 + len;
671
672         if (file.len + NGX_DIR_MASK_LEN > buf.len) {
673
674             if (buf.len) {
675                 ngx_free(buf.data);
676             }
677
678             buf.len = tree->len + 1 + len + NGX_DIR_MASK_LEN;
679
680             buf.data = ngx_alloc(buf.len + 1, ctx->log);
681             if (buf.data == NULL) {
682                 goto failed;
683             }
684         }
685
686         p = ngx_cpymem(buf.data, tree->data, tree->len);
687         *p++ = '/';
688         ngx_memcpy(p, name, len + 1);
689
690         file.data = buf.data;
691
692         ngx_log_debug1(NGX_LOG_DEBUG_CORE, ctx->log, 0,
693                        "tree path \"%s\"", file.data);
694
695         if (!dir.valid_info) {
696             if (ngx_de_info(file.data, &dir) == NGX_FILE_ERROR) {
697                 ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno,
698                               ngx_de_info_n " \"%s\" failed", file.data);
699                 continue;
700             }
701         }
702
703         if (ngx_de_is_file(&dir)) {
704
705             ngx_log_debug1(NGX_LOG_DEBUG_CORE, ctx->log, 0,
706                            "tree file \"%s\"", file.data);
707
708             ctx->size = ngx_de_size(&dir);
709             ctx->access = ngx_de_access(&dir);
710             ctx->mtime = ngx_de_mtime(&dir);
711
712             if (ctx->file_handler(ctx, &file) == NGX_ABORT) {
713                 goto failed;
714             }
715
716         } else if (ngx_de_is_dir(&dir)) {
717
718             ngx_log_debug1(NGX_LOG_DEBUG_CORE, ctx->log, 0,
719                            "tree enter dir \"%s\"", file.data);
720
721             ctx->access = ngx_de_access(&dir);
722             ctx->mtime = ngx_de_mtime(&dir);
723
724             if (ctx->pre_tree_handler(ctx, &file) == NGX_ABORT) {
725                 goto failed;
726             }
727
728             if (ngx_walk_tree(ctx, &file) == NGX_ABORT) {
729                 goto failed;
730             }
731
732             ctx->access = ngx_de_access(&dir);
733             ctx->mtime = ngx_de_mtime(&dir);
734
735             if (ctx->post_tree_handler(ctx, &file) == NGX_ABORT) {
736                 goto failed;
737             }
738
739         } else {
740
741             ngx_log_debug1(NGX_LOG_DEBUG_CORE, ctx->log, 0,
742                            "tree special \"%s\"", file.data);
743
744             if (ctx->spec_handler(ctx, &file) == NGX_ABORT) {
745                 goto failed;
746             }
747         }
748     }
749
750 failed:
751
752     rc = NGX_ABORT;
753
754 done:
755
756     if (buf.len) {
757         ngx_free(buf.data);
758     }
759
760     if (data) {
761         ngx_free(data);
762         ctx->data = prev;
763     }
764
765     if (ngx_close_dir(&dir) == NGX_ERROR) {
766         ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno,
767                       ngx_close_dir_n " \"%s\" failed", tree->data);
768     }
769
770     return rc;
771 }