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