Opened 6 years ago

Closed 6 years ago

#1577 closed defect (fixed)

When the file is moved to the directory that the disk is different by the MOVE method under the following conditions, content of moved file is not correct.

Reported by: 373kwsk@… Owned by:
Priority: minor Milestone:
Component: other Version: 1.12.x
Keywords: WebDAV Cc:
uname -a: Linux vm-search-004.fcxlocal 3.10.0-693.21.1.el7.x86_64 #1 SMP Wed Mar 7 19:03:37 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux
nginx -V: nginx version: nginx/1.12.2
built by gcc 4.8.5 20150623 (Red Hat 4.8.5-16) (GCC)
built with OpenSSL 1.0.2k-fips 26 Jan 2017
TLS SNI support enabled
configure arguments: --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib64/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --http-client-body-temp-path=/var/cache/nginx/client_temp --http-proxy-temp-path=/var/cache/nginx/proxy_temp --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp --http-scgi-temp-path=/var/cache/nginx/scgi_temp --user=nginx --group=nginx --with-compat --with-file-aio --with-threads --with-http_addition_module --with-http_auth_request_module --with-http_dav_module --with-http_flv_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_mp4_module --with-http_random_index_module --with-http_realip_module --with-http_secure_link_module --with-http_slice_module --with-http_ssl_module --with-http_stub_status_module --with-http_sub_module --with-http_v2_module --with-mail --with-mail_ssl_module --with-stream --with-stream_realip_module --with-stream_ssl_module --with-stream_ssl_preread_module --with-cc-opt='-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -m64 -mtune=generic -fPIC' --with-ld-opt='-Wl,-z,relro -Wl,-z,now -pie'

Description

When the file is moved to the directory that the disk is different by the MOVE method under the following conditions, content of moved file is not correct.

  • OS: CentOS 7.4
  • Nginx Version: 1.12.2
# df -h
Filesystem      Size  Used Avail Use% Mounted on
/dev/vda1        30G  1.4G   29G   5% /
devtmpfs        899M     0  899M   0% /dev
tmpfs           920M     0  920M   0% /dev/shm
tmpfs           920M   17M  904M   2% /run
tmpfs           920M     0  920M   0% /sys/fs/cgroup
/dev/vdb1        50G   33M   50G   1% /ext_disk
tmpfs           184M     0  184M   0% /run/user/1000
# nginx
# ps auxf | grep nginx
root     18826  0.0  0.0 112660   972 pts/1    S+   02:45   0:00  |                       \_ grep --color=auto nginx
root     18797  0.0  0.0  46432   988 ?        Ss   02:17   0:00 nginx: master process nginx
nginx    18798  0.0  0.1  46840  2212 ?        S    02:17   0:00  \_ nginx: worker process
# cd /var/lib/nginx
# ls -l
total 0
lrwxrwxrwx. 1 root  root  14 May 15 07:50 dest -> /ext_disk/dest
drwxr-xr-x. 2 nginx nginx 22 May 16 02:29 source
drwxr-xr-x. 2 nginx nginx  6 May 15 07:35 tmp
# ls -l source/.
total 4
-rw-r--r--. 1 nginx nginx 5 May 16 02:29 move.txt
# cat source/move.txt
move
# ls -l dest/.
total 0
# curl -XMOVE http://localhost:8080/source/move.txt -H Destination:/dest/move.txt
# ls -l source/.
total 0
# ls -l dest/.
total 4
----------. 1 nginx nginx 5 May 16 02:33 move.txt
# cat dest/move.txt
move

I have attached the configuration file, the access log, and the error log at the debug level.

I expected that the permissions of "dest/move.txt" and "source/move.txt" were equal after MOVE had been done.
However, the permission of "dest/move.txt" is 000("----------").
What should I do to get the expected result?

Attachments (3)

access.log (104 bytes ) - added by 373kwsk@… 6 years ago.
error.log (6.3 KB ) - added by 373kwsk@… 6 years ago.
my-nginx.conf (334 bytes ) - added by 373kwsk@… 6 years ago.

Download all attachments as: .zip

Change History (6)

by 373kwsk@…, 6 years ago

Attachment: access.log added

by 373kwsk@…, 6 years ago

Attachment: error.log added

by 373kwsk@…, 6 years ago

Attachment: my-nginx.conf added

comment:1 by Maxim Dounin, 6 years ago

The following patch series should fix this:

# HG changeset patch
# User Maxim Dounin <mdounin@mdounin.ru>
# Date 1531147912 -10800
#      Mon Jul 09 17:51:52 2018 +0300
# Node ID a1668faa205aa6ee0ce7936bdbb15e4e600b3931
# Parent  54683f650cbdcd73f7f8d845c843295978da5a85
Fixed ngx_copy_file() to truncate destination file.

Previously, ngx_open_file(NGX_FILE_CREATE_OR_OPEN) was used, resulting
in destination file being partially rewritten if exists.  Notably,
this affected WebDAV COPY command (ticket #1576).

diff --git a/src/core/ngx_file.c b/src/core/ngx_file.c
--- a/src/core/ngx_file.c
+++ b/src/core/ngx_file.c
@@ -839,8 +839,7 @@ ngx_copy_file(u_char *from, u_char *to, 
         goto failed;
     }
 
-    nfd = ngx_open_file(to, NGX_FILE_WRONLY, NGX_FILE_CREATE_OR_OPEN,
-                        cf->access);
+    nfd = ngx_open_file(to, NGX_FILE_WRONLY, NGX_FILE_TRUNCATE, cf->access);
 
     if (nfd == NGX_INVALID_FILE) {
         ngx_log_error(NGX_LOG_CRIT, cf->log, ngx_errno,
# HG changeset patch
# User Maxim Dounin <mdounin@mdounin.ru>
# Date 1531156413 -10800
#      Mon Jul 09 20:13:33 2018 +0300
# Node ID cb76bc603dadf79bc475e4b6f2139030aff0fab9
# Parent  a1668faa205aa6ee0ce7936bdbb15e4e600b3931
Changed ngx_copy_file() to preserve access and mtime.

This fixes wrong permissions and file time after cross-device MOVE
in the DAV module (ticket #1577).  Broken in dbe746851b31 (0.7.27) when
switching to ngx_ext_rename_file().

With this change, ngx_copy_file() always calls ngx_set_file_time(),
either with the time provided, or with the time from the original file.
This is considered acceptable given that copying the file is costly anyway,
and optimizing cases when we do not need to preserve time will require
interface changes.

diff --git a/src/core/ngx_file.c b/src/core/ngx_file.c
--- a/src/core/ngx_file.c
+++ b/src/core/ngx_file.c
@@ -796,10 +796,12 @@ ngx_copy_file(u_char *from, u_char *to, 
 {
     char             *buf;
     off_t             size;
+    time_t            time;
     size_t            len;
     ssize_t           n;
     ngx_fd_t          fd, nfd;
     ngx_int_t         rc;
+    ngx_uint_t        access;
     ngx_file_info_t   fi;
 
     rc = NGX_ERROR;
@@ -814,8 +816,10 @@ ngx_copy_file(u_char *from, u_char *to, 
         goto failed;
     }
 
-    if (cf->size != -1) {
+    if (cf->size != -1 && cf->access != 0 && cf->time != -1) {
         size = cf->size;
+        access = cf->access;
+        time = cf->time;
 
     } else {
         if (ngx_fd_info(fd, &fi) == NGX_FILE_ERROR) {
@@ -825,7 +829,9 @@ ngx_copy_file(u_char *from, u_char *to, 
             goto failed;
         }
 
-        size = ngx_file_size(&fi);
+        size = (cf->size != -1) ? cf->size : ngx_file_size(&fi);
+        access = cf->access ? cf->access : ngx_file_access(&fi);
+        time = (cf->time != -1) ? cf->time : ngx_file_mtime(&fi);
     }
 
     len = cf->buf_size ? cf->buf_size : 65536;
@@ -839,7 +845,7 @@ ngx_copy_file(u_char *from, u_char *to, 
         goto failed;
     }
 
-    nfd = ngx_open_file(to, NGX_FILE_WRONLY, NGX_FILE_TRUNCATE, cf->access);
+    nfd = ngx_open_file(to, NGX_FILE_WRONLY, NGX_FILE_TRUNCATE, access);
 
     if (nfd == NGX_INVALID_FILE) {
         ngx_log_error(NGX_LOG_CRIT, cf->log, ngx_errno,
@@ -886,12 +892,10 @@ ngx_copy_file(u_char *from, u_char *to, 
         size -= n;
     }
 
-    if (cf->time != -1) {
-        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;
-        }
+    if (ngx_set_file_time(to, nfd, 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;

comment:2 by Maxim Dounin <mdounin@…>, 6 years ago

In 7329:63a76a594dd8/nginx:

Dav: changed ngx_copy_file() to preserve access and mtime.

This fixes wrong permissions and file time after cross-device MOVE
in the DAV module (ticket #1577). Broken in 8101d9101ed8 (0.8.9) when
cross-device copying was introduced in ngx_ext_rename_file().

With this change, ngx_copy_file() always calls ngx_set_file_time(),
either with the time provided, or with the time from the original file.
This is considered acceptable given that copying the file is costly anyway,
and optimizing cases when we do not need to preserve time will require
interface changes.

comment:3 by Maxim Dounin, 6 years ago

Resolution: fixed
Status: newclosed

Fix committed, thanks for reporting this.

Note: See TracTickets for help on using tickets.