allow cross device temporary files atomic copying:

*) ngx_copy_file()
*) delete ngx_ext_rename_file_t.log_rename_error and .rename_error fields
This commit is contained in:
Igor Sysoev 2009-08-12 12:05:33 +00:00
parent 2980e4b813
commit 56c6e01f77
5 changed files with 198 additions and 107 deletions

View file

@ -524,7 +524,9 @@ ngx_create_pathes(ngx_cycle_t *cycle, ngx_uid_t user)
ngx_int_t
ngx_ext_rename_file(ngx_str_t *src, ngx_str_t *to, ngx_ext_rename_file_t *ext)
{
ngx_err_t err;
u_char *name;
ngx_err_t err;
ngx_copy_file_t cf;
#if !(NGX_WIN32)
@ -595,6 +597,44 @@ ngx_ext_rename_file(ngx_str_t *src, ngx_str_t *to, ngx_ext_rename_file_t *ext)
#endif
if (err == NGX_EXDEV) {
cf.size = -1;
cf.buf_size = 0;
cf.access = ext->access;
cf.time = ext->time;
cf.log = ext->log;
name = ngx_alloc(to->len + 1 + 10, ext->log);
if (name == NULL) {
return NGX_ERROR;
}
(void) ngx_sprintf(name, "%*s.%010uD%Z", to->len - 1, to->data,
(uint32_t) ngx_next_temp_number(0));
if (ngx_copy_file(src->data, name, &cf) == NGX_OK) {
if (ngx_rename_file(name, to->data) == NGX_FILE_ERROR) {
ngx_free(name);
goto failed;
}
ngx_free(name);
if (ngx_delete_file(src->data) == NGX_FILE_ERROR) {
ngx_log_error(NGX_LOG_CRIT, ext->log, ngx_errno,
ngx_delete_file_n " \"%s\" failed", src->data);
return NGX_ERROR;
}
return NGX_OK;
}
ngx_free(name);
}
failed:
if (ext->delete_file) {
@ -604,18 +644,144 @@ failed:
}
}
if (err && ext->log_rename_error) {
if (err) {
ngx_log_error(NGX_LOG_CRIT, ext->log, err,
ngx_rename_file_n " \"%s\" to \"%s\" failed",
src->data, to->data);
}
ext->rename_error = err;
return NGX_ERROR;
}
ngx_int_t
ngx_copy_file(u_char *from, u_char *to, ngx_copy_file_t *cf)
{
char *buf;
off_t size;
size_t len;
ssize_t n;
ngx_fd_t fd, nfd;
ngx_int_t rc;
ngx_file_info_t fi;
rc = NGX_ERROR;
buf = NULL;
nfd = NGX_INVALID_FILE;
fd = ngx_open_file(from, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0);
if (fd == NGX_INVALID_FILE) {
ngx_log_error(NGX_LOG_CRIT, cf->log, ngx_errno,
ngx_open_file_n " \"%s\" failed", from);
goto failed;
}
if (cf->size != -1) {
size = cf->size;
} else {
if (ngx_fd_info(fd, &fi) == NGX_FILE_ERROR) {
ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,
ngx_fd_info_n " \"%s\" failed", from);
goto failed;
}
size = ngx_file_size(&fi);
}
len = cf->buf_size ? cf->buf_size : 65536;
if (len > size) {
len = (size_t) size;
}
buf = ngx_alloc(len, cf->log);
if (buf == NULL) {
goto failed;
}
nfd = ngx_open_file(to, NGX_FILE_WRONLY, NGX_FILE_CREATE_OR_OPEN,
cf->access);
if (nfd == NGX_INVALID_FILE) {
ngx_log_error(NGX_LOG_CRIT, cf->log, ngx_errno,
ngx_open_file_n " \"%s\" failed", to);
goto failed;
}
while (size > 0) {
if (len > size) {
len = (size_t) size;
}
n = ngx_read_fd(fd, buf, len);
if (n == NGX_FILE_ERROR) {
ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,
ngx_read_fd_n " \"%s\" failed", from);
goto failed;
}
if ((size_t) n != len) {
ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,
ngx_read_fd_n " has read only %z of %uz from %s",
n, size, from);
goto failed;
}
n = ngx_write_fd(nfd, buf, len);
if (n == NGX_FILE_ERROR) {
ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,
ngx_write_fd_n " \"%s\" failed", to);
goto failed;
}
if ((size_t) n != len) {
ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,
ngx_write_fd_n " has written only %z of %uz to %s",
n, size, to);
goto failed;
}
size -= n;
}
if (ngx_set_file_time(to, nfd, cf->time) != NGX_OK) {
ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,
ngx_set_file_time_n " \"%s\" failed", to);
goto failed;
}
rc = NGX_OK;
failed:
if (nfd != NGX_INVALID_FILE) {
if (ngx_close_file(nfd) == NGX_FILE_ERROR) {
ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,
ngx_close_file_n " \"%s\" failed", to);
}
}
if (fd != NGX_INVALID_FILE) {
if (ngx_close_file(fd) == NGX_FILE_ERROR) {
ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,
ngx_close_file_n " \"%s\" failed", from);
}
}
if (buf) {
ngx_free(buf);
}
return rc;
}
/*
* ctx->init_handler() - see ctx->alloc
* ctx->file_handler() - file handler

View file

@ -73,16 +73,25 @@ typedef struct {
ngx_uint_t path_access;
time_t time;
ngx_fd_t fd;
ngx_err_t rename_error;
unsigned create_path:1;
unsigned delete_file:1;
unsigned log_rename_error:1;
ngx_log_t *log;
} ngx_ext_rename_file_t;
typedef struct {
off_t size;
size_t buf_size;
ngx_uint_t access;
time_t time;
ngx_log_t *log;
} ngx_copy_file_t;
typedef struct ngx_tree_ctx_s ngx_tree_ctx_t;
typedef ngx_int_t (*ngx_tree_init_handler_pt) (void *ctx, void *prev);
@ -117,6 +126,7 @@ ngx_int_t ngx_add_path(ngx_conf_t *cf, ngx_path_t **slot);
ngx_int_t ngx_create_pathes(ngx_cycle_t *cycle, ngx_uid_t user);
ngx_int_t ngx_ext_rename_file(ngx_str_t *src, ngx_str_t *to,
ngx_ext_rename_file_t *ext);
ngx_int_t ngx_copy_file(u_char *from, u_char *to, ngx_copy_file_t *cf);
ngx_int_t ngx_walk_tree(ngx_tree_ctx_t *ctx, ngx_str_t *tree);
void ngx_init_temp_number(void);

View file

@ -53,8 +53,6 @@ static ngx_int_t ngx_http_dav_copy_dir_time(ngx_tree_ctx_t *ctx,
ngx_str_t *path);
static ngx_int_t ngx_http_dav_copy_tree_file(ngx_tree_ctx_t *ctx,
ngx_str_t *path);
static ngx_int_t ngx_http_dav_copy_file(ngx_tree_ctx_t *ctx, u_char *from,
u_char *to);
static ngx_int_t ngx_http_dav_depth(ngx_http_request_t *r, ngx_int_t dflt);
static ngx_int_t ngx_http_dav_error(ngx_log_t *log, ngx_err_t err,
@ -249,7 +247,6 @@ ngx_http_dav_put_handler(ngx_http_request_t *r)
ext.time = -1;
ext.create_path = dlcf->create_full_put_path;
ext.delete_file = 1;
ext.log_rename_error = 1;
ext.log = r->connection->log;
if (r->headers_in.date) {
@ -520,6 +517,7 @@ ngx_http_dav_copy_move_handler(ngx_http_request_t *r)
ngx_uint_t overwrite, slash, dir;
ngx_str_t path, uri;
ngx_tree_ctx_t tree;
ngx_copy_file_t cf;
ngx_file_info_t fi;
ngx_table_elt_t *dest, *over;
ngx_ext_rename_file_t ext;
@ -791,43 +789,24 @@ overwrite_done:
ext.time = -1;
ext.create_path = 1;
ext.delete_file = 0;
ext.log_rename_error = 0;
ext.log = r->connection->log;
if (ngx_ext_rename_file(&path, &copy.path, &ext) == NGX_OK) {
return NGX_HTTP_NO_CONTENT;
}
if (ext.rename_error != NGX_EXDEV) {
if (ext.rename_error) {
ngx_log_error(NGX_LOG_CRIT, r->connection->log,
ext.rename_error,
ngx_rename_file_n " \"%s\" to \"%s\" failed",
path.data, copy.path.data);
}
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
dlcf = ngx_http_get_module_loc_conf(r, ngx_http_dav_module);
tree.size = ngx_file_size(&fi);
tree.mtime = ngx_file_mtime(&fi);
tree.access = dlcf->access;
tree.log = r->connection->log;
if (ngx_http_dav_copy_file(&tree, path.data, copy.path.data) == NGX_OK)
{
if (r->method == NGX_HTTP_MOVE) {
rc = ngx_http_dav_delete_path(r, &path, 0);
if (rc != NGX_OK) {
return rc;
}
}
cf.size = ngx_file_size(&fi);
cf.buf_size = 0;
cf.access = dlcf->access;
cf.time = ngx_file_mtime(&fi);
cf.log = r->connection->log;
if (ngx_copy_file(path.data, copy.path.data, &cf) == NGX_OK) {
return NGX_HTTP_NO_CONTENT;
}
}
@ -941,6 +920,7 @@ ngx_http_dav_copy_tree_file(ngx_tree_ctx_t *ctx, ngx_str_t *path)
{
u_char *p, *file;
size_t len;
ngx_copy_file_t cf;
ngx_http_dav_copy_ctx_t *copy;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
@ -961,7 +941,13 @@ ngx_http_dav_copy_tree_file(ngx_tree_ctx_t *ctx, ngx_str_t *path)
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
"http copy file to: \"%s\"", file);
(void) ngx_http_dav_copy_file(ctx, path->data, file);
cf.size = ctx->size;
cf.buf_size = 0;
cf.access = ctx->access;
cf.time = ctx->mtime;
cf.log = ctx->log;
(void) ngx_copy_file(path->data, file, &cf);
ngx_free(file);
@ -969,75 +955,6 @@ ngx_http_dav_copy_tree_file(ngx_tree_ctx_t *ctx, ngx_str_t *path)
}
static ngx_int_t
ngx_http_dav_copy_file(ngx_tree_ctx_t *ctx, u_char *from, u_char *to)
{
off_t size;
ssize_t n;
ngx_fd_t fd, cfd;
ngx_int_t rc;
u_char buf[NGX_HTTP_DAV_COPY_BLOCK];
fd = ngx_open_file(from, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0);
if (fd == NGX_INVALID_FILE) {
(void) ngx_http_dav_error(ctx->log, ngx_errno, 0, ngx_open_file_n,
from);
return NGX_ERROR;
}
cfd = ngx_open_file(to, NGX_FILE_WRONLY, NGX_FILE_CREATE_OR_OPEN,
ctx->access);
rc = NGX_ERROR;
if (cfd == NGX_INVALID_FILE) {
(void) ngx_http_dav_error(ctx->log, ngx_errno, 0, ngx_open_file_n, to);
goto failed;
}
for (size = ctx->size; size > 0; size -= n) {
n = ngx_read_fd(fd, buf, NGX_HTTP_DAV_COPY_BLOCK);
if (n == NGX_FILE_ERROR) {
ngx_log_error(NGX_LOG_ALERT, ctx->log, ngx_errno,
ngx_read_fd_n " \"%s\" failed", from);
goto failed;
}
if (ngx_write_fd(cfd, buf, n) == NGX_FILE_ERROR) {
ngx_log_error(NGX_LOG_ALERT, ctx->log, ngx_errno,
ngx_write_fd_n " \"%s\" failed", to);
goto failed;
}
}
if (ngx_set_file_time(to, cfd, ctx->mtime) != NGX_OK) {
ngx_log_error(NGX_LOG_ALERT, ctx->log, ngx_errno,
ngx_set_file_time_n " \"%s\" failed", to);
goto failed;
}
if (ngx_close_file(cfd) == NGX_FILE_ERROR) {
ngx_log_error(NGX_LOG_ALERT, ctx->log, ngx_errno,
ngx_close_file_n " \"%s\" failed", to);
goto failed;
}
rc = NGX_OK;
failed:
if (ngx_close_file(fd) == NGX_FILE_ERROR) {
ngx_log_error(NGX_LOG_ALERT, ctx->log, ngx_errno,
ngx_close_file_n " \"%s\" failed", from);
}
return rc;
}
static ngx_int_t
ngx_http_dav_depth(ngx_http_request_t *r, ngx_int_t dflt)
{

View file

@ -650,7 +650,6 @@ ngx_http_file_cache_update(ngx_http_request_t *r, ngx_temp_file_t *tf)
ext.time = -1;
ext.create_path = 1;
ext.delete_file = 1;
ext.log_rename_error = 1;
ext.log = r->connection->log;
rc = ngx_ext_rename_file(&tf->file.name, &c->file.name, &ext);

View file

@ -2635,7 +2635,6 @@ ngx_http_upstream_store(ngx_http_request_t *r, ngx_http_upstream_t *u)
ext.time = -1;
ext.create_path = 1;
ext.delete_file = 1;
ext.log_rename_error = 1;
ext.log = r->connection->log;
if (u->headers_in.last_modified) {