s4-pvfs: change the handling of access checking on create
[ira/wip.git] / source4 / ntvfs / posix / pvfs_setfileinfo.c
index 7144f37a14d88d24216754b910b0b709379a6dba..244548382ce1018c09247f8096d9f01fb10317fa 100644 (file)
@@ -7,7 +7,7 @@
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2 of the License, or
+   the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.
    
    This program is distributed in the hope that it will be useful,
    GNU General Public License for more details.
    
    You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software
-   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
 #include "includes.h"
 #include "vfs_posix.h"
 #include "system/time.h"
-#include "librpc/gen_ndr/ndr_xattr.h"
+#include "librpc/gen_ndr/xattr.h"
 
 
 /*
@@ -53,68 +52,123 @@ static uint32_t pvfs_setfileinfo_access(union smb_setfileinfo *info)
 
        case RAW_SFILEINFO_SEC_DESC:
                needed = 0;
-               if (info->set_secdesc.in.secinfo_flags & (SECINFO_DACL|SECINFO_SACL)) {
+               if (info->set_secdesc.in.secinfo_flags & (SECINFO_OWNER|SECINFO_GROUP)) {
+                       needed |= SEC_STD_WRITE_OWNER;
+               }
+               if (info->set_secdesc.in.secinfo_flags & SECINFO_DACL) {
                        needed |= SEC_STD_WRITE_DAC;
                }
+               if (info->set_secdesc.in.secinfo_flags & SECINFO_SACL) {
+                       needed |= SEC_FLAG_SYSTEM_SECURITY;
+               }
+               break;
+
+       case RAW_SFILEINFO_RENAME_INFORMATION:
+       case RAW_SFILEINFO_RENAME_INFORMATION_SMB2:
+               needed = SEC_STD_DELETE;
                break;
 
        default:
                needed = SEC_FILE_WRITE_ATTRIBUTE;
                break;
        }
-       return needed;  
+
+       return needed;
+}
+
+/*
+  rename_information level for streams
+*/
+static NTSTATUS pvfs_setfileinfo_rename_stream(struct pvfs_state *pvfs, 
+                                              struct ntvfs_request *req, 
+                                              struct pvfs_filename *name,
+                                              int fd,
+                                              DATA_BLOB *odb_locking_key,
+                                              union smb_setfileinfo *info)
+{
+       NTSTATUS status;
+       struct odb_lock *lck = NULL;
+
+       if (info->rename_information.in.new_name[0] != ':') {
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       status = pvfs_access_check_simple(pvfs, req, name, SEC_FILE_WRITE_ATTRIBUTE);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       lck = odb_lock(req, pvfs->odb_context, odb_locking_key);
+       if (lck == NULL) {
+               DEBUG(0,("Unable to lock opendb for can_stat\n"));
+               return NT_STATUS_INTERNAL_DB_CORRUPTION;
+       }
+
+
+       status = pvfs_stream_rename(pvfs, name, fd, 
+                                   info->rename_information.in.new_name+1);
+       return status;
 }
 
 /*
   rename_information level
 */
 static NTSTATUS pvfs_setfileinfo_rename(struct pvfs_state *pvfs, 
-                                       struct smbsrv_request *req, 
+                                       struct ntvfs_request *req, 
                                        struct pvfs_filename *name,
-                                       struct smb_rename_information *r)
+                                       int fd,
+                                       DATA_BLOB *odb_locking_key,
+                                       union smb_setfileinfo *info)
 {
        NTSTATUS status;
        struct pvfs_filename *name2;
        char *new_name, *p;
+       struct odb_lock *lck = NULL;
 
        /* renames are only allowed within a directory */
-       if (strchr_m(r->new_name, '\\')) {
+       if (strchr_m(info->rename_information.in.new_name, '\\') &&
+           (req->ctx->protocol != PROTOCOL_SMB2)) {
                return NT_STATUS_NOT_SUPPORTED;
        }
 
-       if (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) {
-               /* don't allow this for now */
-               return NT_STATUS_FILE_IS_A_DIRECTORY;
-       }
-
-       /* don't allow stream renames for now */
+       /* handle stream renames specially */
        if (name->stream_name) {
-               return NT_STATUS_INVALID_PARAMETER;
+               return pvfs_setfileinfo_rename_stream(pvfs, req, name, fd, 
+                                                     odb_locking_key, info);
        }
 
-       /* w2k3 does not appear to allow relative rename */
-       if (r->root_fid != 0) {
+       /* w2k3 does not appear to allow relative rename. On SMB2, vista sends it sometimes,
+          but I suspect it is just uninitialised memory */
+       if (info->rename_information.in.root_fid != 0 && 
+           (req->ctx->protocol != PROTOCOL_SMB2)) {
                return NT_STATUS_INVALID_PARAMETER;
        }
 
        /* construct the fully qualified windows name for the new file name */
-       new_name = talloc_strdup(req, name->original_name);
-       if (new_name == NULL) {
-               return NT_STATUS_NO_MEMORY;
-       }
-       p = strrchr_m(new_name, '\\');
-       if (p == NULL) {
-               return NT_STATUS_OBJECT_NAME_INVALID;
-       }
-       *p = 0;
+       if (req->ctx->protocol == PROTOCOL_SMB2) {
+               /* SMB2 sends the full path of the new name */
+               new_name = talloc_asprintf(req, "\\%s", info->rename_information.in.new_name);
+       } else {
+               new_name = talloc_strdup(req, name->original_name);
+               if (new_name == NULL) {
+                       return NT_STATUS_NO_MEMORY;
+               }
+               p = strrchr_m(new_name, '\\');
+               if (p == NULL) {
+                       return NT_STATUS_OBJECT_NAME_INVALID;
+               } else {
+                       *p = 0;
+               }
 
-       new_name = talloc_asprintf(req, "%s\\%s", new_name, r->new_name);
+               new_name = talloc_asprintf(req, "%s\\%s", new_name,
+                                          info->rename_information.in.new_name);
+       }
        if (new_name == NULL) {
                return NT_STATUS_NO_MEMORY;
        }
 
        /* resolve the new name */
-       status = pvfs_resolve_name(pvfs, name, new_name, 0, &name2);
+       status = pvfs_resolve_name(pvfs, req, new_name, 0, &name2);
        if (!NT_STATUS_IS_OK(status)) {
                return status;
        }
@@ -126,11 +180,14 @@ static NTSTATUS pvfs_setfileinfo_rename(struct pvfs_state *pvfs,
                        return NT_STATUS_OK;
                }
 
-               if (!r->overwrite) {
+               if (!info->rename_information.in.overwrite) {
                        return NT_STATUS_OBJECT_NAME_COLLISION;
                }
 
-               status = pvfs_can_delete(pvfs, req, name2);
+               status = pvfs_can_delete(pvfs, req, name2, NULL);
+               if (NT_STATUS_EQUAL(status, NT_STATUS_DELETE_PENDING)) {
+                       return NT_STATUS_ACCESS_DENIED;
+               }
                if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION)) {
                        return NT_STATUS_ACCESS_DENIED;
                }
@@ -139,17 +196,24 @@ static NTSTATUS pvfs_setfileinfo_rename(struct pvfs_state *pvfs,
                }
        }
 
-       status = pvfs_access_check_create(pvfs, req, name2);
+       status = pvfs_access_check_parent(pvfs, req, name2, SEC_DIR_ADD_FILE);
        if (!NT_STATUS_IS_OK(status)) {
                return status;
        }
 
-       if (rename(name->full_name, name2->full_name) == -1) {
-               return pvfs_map_errno(pvfs, errno);
+       lck = odb_lock(req, pvfs->odb_context, odb_locking_key);
+       if (lck == NULL) {
+               DEBUG(0,("Unable to lock opendb for can_stat\n"));
+               return NT_STATUS_INTERNAL_DB_CORRUPTION;
        }
 
-       name->full_name = talloc_steal(name, name2->full_name);
-       name->original_name = talloc_steal(name, name2->original_name);
+       status = pvfs_do_rename(pvfs, lck, name, name2->full_name);
+       talloc_free(lck);
+       NT_STATUS_NOT_OK_RETURN(status);
+       if (NT_STATUS_IS_OK(status)) {
+               name->full_name = talloc_steal(name, name2->full_name);
+               name->original_name = talloc_steal(name, name2->original_name);
+       }
 
        return NT_STATUS_OK;
 }
@@ -174,7 +238,7 @@ NTSTATUS pvfs_setfileinfo_ea_set(struct pvfs_state *pvfs,
                return NT_STATUS_NOT_SUPPORTED;
        }
 
-       ealist = talloc_p(name, struct xattr_DosEAs);
+       ealist = talloc(name, struct xattr_DosEAs);
 
        /* load the current list */
        status = pvfs_doseas_load(pvfs, name, fd, ealist);
@@ -186,7 +250,7 @@ NTSTATUS pvfs_setfileinfo_ea_set(struct pvfs_state *pvfs,
                struct ea_struct *ea = &eas[j];
                /* see if its already there */
                for (i=0;i<ealist->num_eas;i++) {
-                       if (StrCaseCmp(ealist->eas[i].name, ea->name.s) == 0) {
+                       if (strcasecmp_m(ealist->eas[i].name, ea->name.s) == 0) {
                                ealist->eas[i].value = ea->value;
                                break;
                        }
@@ -194,7 +258,7 @@ NTSTATUS pvfs_setfileinfo_ea_set(struct pvfs_state *pvfs,
 
                if (i==ealist->num_eas) {
                        /* add it */
-                       ealist->eas = talloc_realloc_p(ealist, ealist->eas, 
+                       ealist->eas = talloc_realloc(ealist, ealist->eas, 
                                                       struct xattr_EA, 
                                                       ealist->num_eas+1);
                        if (ealist->eas == NULL) {
@@ -222,6 +286,11 @@ NTSTATUS pvfs_setfileinfo_ea_set(struct pvfs_state *pvfs,
                return status;
        }
 
+       notify_trigger(pvfs->notify_context, 
+                      NOTIFY_ACTION_MODIFIED, 
+                      FILE_NOTIFY_CHANGE_EA,
+                      name->full_name);
+
        name->dos.ea_size = 4;
        for (i=0;i<ealist->num_eas;i++) {
                name->dos.ea_size += 4 + strlen(ealist->eas[i].name)+1 + 
@@ -236,19 +305,19 @@ NTSTATUS pvfs_setfileinfo_ea_set(struct pvfs_state *pvfs,
   set info on a open file
 */
 NTSTATUS pvfs_setfileinfo(struct ntvfs_module_context *ntvfs,
-                         struct smbsrv_request *req, 
+                         struct ntvfs_request *req, 
                          union smb_setfileinfo *info)
 {
-       struct pvfs_state *pvfs = ntvfs->private_data;
-       struct utimbuf unix_times;
+       struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
+                                 struct pvfs_state);
        struct pvfs_file *f;
        struct pvfs_file_handle *h;
-       uint32_t create_options;
        struct pvfs_filename newstats;
        NTSTATUS status;
        uint32_t access_needed;
+       uint32_t change_mask = 0;
 
-       f = pvfs_find_fd(pvfs, req, info->generic.file.fnum);
+       f = pvfs_find_fd(pvfs, req, info->generic.in.file.ntvfs);
        if (!f) {
                return NT_STATUS_INVALID_HANDLE;
        }
@@ -261,7 +330,7 @@ NTSTATUS pvfs_setfileinfo(struct ntvfs_module_context *ntvfs,
        }
 
        /* update the file information */
-       status = pvfs_resolve_name_fd(pvfs, h->fd, h->name);
+       status = pvfs_resolve_name_handle(pvfs, h);
        if (!NT_STATUS_IS_OK(status)) {
                return status;
        }
@@ -301,18 +370,16 @@ NTSTATUS pvfs_setfileinfo(struct ntvfs_module_context *ntvfs,
 
        case RAW_SFILEINFO_BASIC_INFO:
        case RAW_SFILEINFO_BASIC_INFORMATION:
-               if (info->basic_info.in.create_time) {
+               if (!null_nttime(info->basic_info.in.create_time)) {
                        newstats.dos.create_time = info->basic_info.in.create_time;
                }
-               if (info->basic_info.in.access_time) {
+               if (!null_nttime(info->basic_info.in.access_time)) {
                        newstats.dos.access_time = info->basic_info.in.access_time;
                }
-               if (info->basic_info.in.write_time) {
+               if (!null_nttime(info->basic_info.in.write_time)) {
                        newstats.dos.write_time = info->basic_info.in.write_time;
-                       newstats.dos.flags |= XATTR_ATTRIB_FLAG_STICKY_WRITE_TIME;
-                       h->sticky_write_time = True;
                }
-               if (info->basic_info.in.change_time) {
+               if (!null_nttime(info->basic_info.in.change_time)) {
                        newstats.dos.change_time = info->basic_info.in.change_time;
                }
                if (info->basic_info.in.attrib != 0) {
@@ -322,16 +389,14 @@ NTSTATUS pvfs_setfileinfo(struct ntvfs_module_context *ntvfs,
 
        case RAW_SFILEINFO_DISPOSITION_INFO:
        case RAW_SFILEINFO_DISPOSITION_INFORMATION:
-               create_options = h->create_options;
-               if (info->disposition_info.in.delete_on_close) {
-                       create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
-               } else {
-                       create_options &= ~NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
-               }
-               return pvfs_change_create_options(pvfs, req, f, create_options);
+               return pvfs_set_delete_on_close(pvfs, req, f, 
+                                               info->disposition_info.in.delete_on_close);
 
        case RAW_SFILEINFO_ALLOCATION_INFO:
        case RAW_SFILEINFO_ALLOCATION_INFORMATION:
+               status = pvfs_break_level2_oplocks(f);
+               NT_STATUS_NOT_OK_RETURN(status);
+
                newstats.dos.alloc_size = info->allocation_info.in.alloc_size;
                if (newstats.dos.alloc_size < newstats.st.st_size) {
                        newstats.st.st_size = newstats.dos.alloc_size;
@@ -342,6 +407,9 @@ NTSTATUS pvfs_setfileinfo(struct ntvfs_module_context *ntvfs,
 
        case RAW_SFILEINFO_END_OF_FILE_INFO:
        case RAW_SFILEINFO_END_OF_FILE_INFORMATION:
+               status = pvfs_break_level2_oplocks(f);
+               NT_STATUS_NOT_OK_RETURN(status);
+
                newstats.st.st_size = info->end_of_file_info.in.size;
                break;
 
@@ -361,10 +429,16 @@ NTSTATUS pvfs_setfileinfo(struct ntvfs_module_context *ntvfs,
                break;
 
        case RAW_SFILEINFO_RENAME_INFORMATION:
-               return pvfs_setfileinfo_rename(pvfs, req, h->name, 
-                                              &info->rename_information.in);
+       case RAW_SFILEINFO_RENAME_INFORMATION_SMB2:
+               return pvfs_setfileinfo_rename(pvfs, req, h->name, f->handle->fd,
+                                              &h->odb_locking_key,
+                                              info);
 
        case RAW_SFILEINFO_SEC_DESC:
+               notify_trigger(pvfs->notify_context, 
+                              NOTIFY_ACTION_MODIFIED, 
+                              FILE_NOTIFY_CHANGE_SECURITY,
+                              h->name->full_name);
                return pvfs_acl_set(pvfs, req, h->name, h->fd, f->access_mask, info);
 
        default:
@@ -381,6 +455,8 @@ NTSTATUS pvfs_setfileinfo(struct ntvfs_module_context *ntvfs,
                        if (!NT_STATUS_IS_OK(status)) {
                                return status;
                        }
+                       
+                       change_mask |= FILE_NOTIFY_CHANGE_STREAM_SIZE;
                } else {
                        int ret;
                        if (f->access_mask & 
@@ -392,54 +468,184 @@ NTSTATUS pvfs_setfileinfo(struct ntvfs_module_context *ntvfs,
                        if (ret == -1) {
                                return pvfs_map_errno(pvfs, errno);
                        }
+                       change_mask |= FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_ATTRIBUTES;
                }
        }
 
        /* possibly change the file timestamps */
-       ZERO_STRUCT(unix_times);
+       if (newstats.dos.create_time != h->name->dos.create_time) {
+               change_mask |= FILE_NOTIFY_CHANGE_CREATION;
+       }
        if (newstats.dos.access_time != h->name->dos.access_time) {
-               unix_times.actime = nt_time_to_unix(newstats.dos.access_time);
+               change_mask |= FILE_NOTIFY_CHANGE_LAST_ACCESS;
        }
        if (newstats.dos.write_time != h->name->dos.write_time) {
-               unix_times.modtime = nt_time_to_unix(newstats.dos.write_time);
+               change_mask |= FILE_NOTIFY_CHANGE_LAST_WRITE;
        }
-       if (unix_times.actime != 0 || unix_times.modtime != 0) {
-               if (utime(h->name->full_name, &unix_times) == -1) {
-                       return pvfs_map_errno(pvfs, errno);
+       if ((change_mask & FILE_NOTIFY_CHANGE_LAST_ACCESS) ||
+           (change_mask & FILE_NOTIFY_CHANGE_LAST_WRITE)) {
+               struct timeval tv[2];
+
+               nttime_to_timeval(&tv[0], newstats.dos.access_time);
+               nttime_to_timeval(&tv[1], newstats.dos.write_time);
+
+               if (!timeval_is_zero(&tv[0]) || !timeval_is_zero(&tv[1])) {
+                       if (utimes(h->name->full_name, tv) == -1) {
+                               DEBUG(0,("pvfs_setfileinfo: utimes() failed '%s' - %s\n",
+                                        h->name->full_name, strerror(errno)));
+                               return pvfs_map_errno(pvfs, errno);
+                       }
+               }
+       }
+       if (change_mask & FILE_NOTIFY_CHANGE_LAST_WRITE) {
+               struct odb_lock *lck;
+
+               lck = odb_lock(req, h->pvfs->odb_context, &h->odb_locking_key);
+               if (lck == NULL) {
+                       DEBUG(0,("Unable to lock opendb for write time update\n"));
+                       return NT_STATUS_INTERNAL_ERROR;
                }
+
+               status = odb_set_write_time(lck, newstats.dos.write_time, true);
+               if (!NT_STATUS_IS_OK(status)) {
+                       DEBUG(0,("Unable to update write time: %s\n",
+                               nt_errstr(status)));
+                       talloc_free(lck);
+                       return status;
+               }
+
+               talloc_free(lck);
+
+               h->write_time.update_forced = true;
+               h->write_time.update_on_close = false;
+               talloc_free(h->write_time.update_event);
+               h->write_time.update_event = NULL;
        }
 
        /* possibly change the attribute */
        if (newstats.dos.attrib != h->name->dos.attrib) {
-               mode_t mode = pvfs_fileperms(pvfs, newstats.dos.attrib);
+               mode_t mode;
+               if ((newstats.dos.attrib & FILE_ATTRIBUTE_DIRECTORY) &&
+                   !(h->name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) {
+                       return NT_STATUS_INVALID_PARAMETER;
+               }
+               mode = pvfs_fileperms(pvfs, newstats.dos.attrib);
                if (!(h->name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) {
                        if (fchmod(h->fd, mode) == -1) {
                                return pvfs_map_errno(pvfs, errno);
                        }
                }
+               change_mask |= FILE_NOTIFY_CHANGE_ATTRIBUTES;
        }
 
        *h->name = newstats;
 
+       notify_trigger(pvfs->notify_context, 
+                      NOTIFY_ACTION_MODIFIED, 
+                      change_mask,
+                      h->name->full_name);
+
        return pvfs_dosattrib_save(pvfs, h->name, h->fd);
 }
 
+/*
+  retry an open after a sharing violation
+*/
+static void pvfs_retry_setpathinfo(struct pvfs_odb_retry *r,
+                                  struct ntvfs_module_context *ntvfs,
+                                  struct ntvfs_request *req,
+                                  void *_info,
+                                  void *private_data,
+                                  enum pvfs_wait_notice reason)
+{
+       union smb_setfileinfo *info = talloc_get_type(_info,
+                                     union smb_setfileinfo);
+       NTSTATUS status = NT_STATUS_INTERNAL_ERROR;
+
+       talloc_free(r);
+
+       switch (reason) {
+       case PVFS_WAIT_CANCEL:
+/*TODO*/
+               status = NT_STATUS_CANCELLED;
+               break;
+       case PVFS_WAIT_TIMEOUT:
+               /* if it timed out, then give the failure
+                  immediately */
+/*TODO*/
+               status = NT_STATUS_SHARING_VIOLATION;
+               break;
+       case PVFS_WAIT_EVENT:
+
+               /* try the open again, which could trigger another retry setup
+                  if it wants to, so we have to unmark the async flag so we
+                  will know if it does a second async reply */
+               req->async_states->state &= ~NTVFS_ASYNC_STATE_ASYNC;
+
+               status = pvfs_setpathinfo(ntvfs, req, info);
+               if (req->async_states->state & NTVFS_ASYNC_STATE_ASYNC) {
+                       /* the 2nd try also replied async, so we don't send
+                          the reply yet */
+                       return;
+               }
+
+               /* re-mark it async, just in case someone up the chain does
+                  paranoid checking */
+               req->async_states->state |= NTVFS_ASYNC_STATE_ASYNC;
+               break;
+       }
+
+       /* send the reply up the chain */
+       req->async_states->status = status;
+       req->async_states->send_fn(req);
+}
+
+/*
+  setup for a unlink retry after a sharing violation
+  or a non granted oplock
+*/
+static NTSTATUS pvfs_setpathinfo_setup_retry(struct ntvfs_module_context *ntvfs,
+                                            struct ntvfs_request *req,
+                                            union smb_setfileinfo *info,
+                                            struct odb_lock *lck,
+                                            NTSTATUS status)
+{
+       struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
+                                 struct pvfs_state);
+       struct timeval end_time;
+
+       if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION)) {
+               end_time = timeval_add(&req->statistics.request_time,
+                                      0, pvfs->sharing_violation_delay);
+       } else if (NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) {
+               end_time = timeval_add(&req->statistics.request_time,
+                                      pvfs->oplock_break_timeout, 0);
+       } else {
+               return NT_STATUS_INTERNAL_ERROR;
+       }
+
+       return pvfs_odb_retry_setup(ntvfs, req, lck, end_time, info, NULL,
+                                   pvfs_retry_setpathinfo);
+}
 
 /*
   set info on a pathname
 */
 NTSTATUS pvfs_setpathinfo(struct ntvfs_module_context *ntvfs,
-                         struct smbsrv_request *req, union smb_setfileinfo *info)
+                         struct ntvfs_request *req, union smb_setfileinfo *info)
 {
-       struct pvfs_state *pvfs = ntvfs->private_data;
+       struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
+                                 struct pvfs_state);
        struct pvfs_filename *name;
        struct pvfs_filename newstats;
        NTSTATUS status;
-       struct utimbuf unix_times;
        uint32_t access_needed;
+       uint32_t change_mask = 0;
+       struct odb_lock *lck = NULL;
+       DATA_BLOB odb_locking_key;
 
        /* resolve the cifs name to a posix name */
-       status = pvfs_resolve_name(pvfs, req, info->generic.file.fname
+       status = pvfs_resolve_name(pvfs, req, info->generic.in.file.path
                                   PVFS_RESOLVE_STREAMS, &name);
        if (!NT_STATUS_IS_OK(status)) {
                return status;
@@ -465,7 +671,9 @@ NTSTATUS pvfs_setpathinfo(struct ntvfs_module_context *ntvfs,
                if (!null_time(info->setattr.in.write_time)) {
                        unix_to_nt_time(&newstats.dos.write_time, info->setattr.in.write_time);
                }
-               if (info->setattr.in.attrib != FILE_ATTRIBUTE_NORMAL) {
+               if (info->setattr.in.attrib == 0) {
+                       newstats.dos.attrib = FILE_ATTRIBUTE_NORMAL;
+               } else if (info->setattr.in.attrib != FILE_ATTRIBUTE_NORMAL) {
                        newstats.dos.attrib = info->setattr.in.attrib;
                }
                break;
@@ -490,16 +698,16 @@ NTSTATUS pvfs_setpathinfo(struct ntvfs_module_context *ntvfs,
 
        case RAW_SFILEINFO_BASIC_INFO:
        case RAW_SFILEINFO_BASIC_INFORMATION:
-               if (info->basic_info.in.create_time) {
+               if (!null_nttime(info->basic_info.in.create_time)) {
                        newstats.dos.create_time = info->basic_info.in.create_time;
                }
-               if (info->basic_info.in.access_time) {
+               if (!null_nttime(info->basic_info.in.access_time)) {
                        newstats.dos.access_time = info->basic_info.in.access_time;
                }
-               if (info->basic_info.in.write_time) {
+               if (!null_nttime(info->basic_info.in.write_time)) {
                        newstats.dos.write_time = info->basic_info.in.write_time;
                }
-               if (info->basic_info.in.change_time) {
+               if (!null_nttime(info->basic_info.in.change_time)) {
                        newstats.dos.change_time = info->basic_info.in.change_time;
                }
                if (info->basic_info.in.attrib != 0) {
@@ -509,6 +717,20 @@ NTSTATUS pvfs_setpathinfo(struct ntvfs_module_context *ntvfs,
 
        case RAW_SFILEINFO_ALLOCATION_INFO:
        case RAW_SFILEINFO_ALLOCATION_INFORMATION:
+               status = pvfs_can_update_file_size(pvfs, req, name, &lck);
+               /*
+                * on a sharing violation we need to retry when the file is closed by
+                * the other user, or after 1 second
+                * on a non granted oplock we need to retry when the file is closed by
+                * the other user, or after 30 seconds
+               */
+               if ((NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) ||
+                    NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) &&
+                   (req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
+                       return pvfs_setpathinfo_setup_retry(pvfs->ntvfs, req, info, lck, status);
+               }
+               NT_STATUS_NOT_OK_RETURN(status);
+
                if (info->allocation_info.in.alloc_size > newstats.dos.alloc_size) {
                        /* strange. Increasing the allocation size via setpathinfo 
                           should be silently ignored */
@@ -524,6 +746,20 @@ NTSTATUS pvfs_setpathinfo(struct ntvfs_module_context *ntvfs,
 
        case RAW_SFILEINFO_END_OF_FILE_INFO:
        case RAW_SFILEINFO_END_OF_FILE_INFORMATION:
+               status = pvfs_can_update_file_size(pvfs, req, name, &lck);
+               /*
+                * on a sharing violation we need to retry when the file is closed by
+                * the other user, or after 1 second
+                * on a non granted oplock we need to retry when the file is closed by
+                * the other user, or after 30 seconds
+               */
+               if ((NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) ||
+                    NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) &&
+                   (req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
+                       return pvfs_setpathinfo_setup_retry(pvfs->ntvfs, req, info, lck, status);
+               }
+               NT_STATUS_NOT_OK_RETURN(status);
+
                newstats.st.st_size = info->end_of_file_info.in.size;
                break;
 
@@ -537,8 +773,13 @@ NTSTATUS pvfs_setpathinfo(struct ntvfs_module_context *ntvfs,
                return NT_STATUS_OK;
 
        case RAW_SFILEINFO_RENAME_INFORMATION:
-               return pvfs_setfileinfo_rename(pvfs, req, name, 
-                                              &info->rename_information.in);
+       case RAW_SFILEINFO_RENAME_INFORMATION_SMB2:
+               status = pvfs_locking_key(name, name, &odb_locking_key);
+               NT_STATUS_NOT_OK_RETURN(status);
+               status = pvfs_setfileinfo_rename(pvfs, req, name, -1,
+                                                &odb_locking_key, info);
+               NT_STATUS_NOT_OK_RETURN(status);
+               return NT_STATUS_OK;
 
        case RAW_SFILEINFO_DISPOSITION_INFO:
        case RAW_SFILEINFO_DISPOSITION_INFORMATION:
@@ -559,19 +800,55 @@ NTSTATUS pvfs_setpathinfo(struct ntvfs_module_context *ntvfs,
                } else if (truncate(name->full_name, newstats.st.st_size) == -1) {
                        return pvfs_map_errno(pvfs, errno);
                }
+               change_mask |= FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_ATTRIBUTES;
        }
 
        /* possibly change the file timestamps */
-       ZERO_STRUCT(unix_times);
+       if (newstats.dos.create_time != name->dos.create_time) {
+               change_mask |= FILE_NOTIFY_CHANGE_CREATION;
+       }
        if (newstats.dos.access_time != name->dos.access_time) {
-               unix_times.actime = nt_time_to_unix(newstats.dos.access_time);
+               change_mask |= FILE_NOTIFY_CHANGE_LAST_ACCESS;
        }
        if (newstats.dos.write_time != name->dos.write_time) {
-               unix_times.modtime = nt_time_to_unix(newstats.dos.write_time);
+               change_mask |= FILE_NOTIFY_CHANGE_LAST_WRITE;
        }
-       if (unix_times.actime != 0 || unix_times.modtime != 0) {
-               if (utime(name->full_name, &unix_times) == -1) {
-                       return pvfs_map_errno(pvfs, errno);
+       if ((change_mask & FILE_NOTIFY_CHANGE_LAST_ACCESS) ||
+           (change_mask & FILE_NOTIFY_CHANGE_LAST_WRITE)) {
+               struct timeval tv[2];
+
+               nttime_to_timeval(&tv[0], newstats.dos.access_time);
+               nttime_to_timeval(&tv[1], newstats.dos.write_time);
+
+               if (!timeval_is_zero(&tv[0]) || !timeval_is_zero(&tv[1])) {
+                       if (utimes(name->full_name, tv) == -1) {
+                               DEBUG(0,("pvfs_setpathinfo: utimes() failed '%s' - %s\n",
+                                        name->full_name, strerror(errno)));
+                               return pvfs_map_errno(pvfs, errno);
+                       }
+               }
+       }
+       if (change_mask & FILE_NOTIFY_CHANGE_LAST_WRITE) {
+               if (lck == NULL) {
+                       DATA_BLOB lkey;
+                       status = pvfs_locking_key(name, name, &lkey);
+                       NT_STATUS_NOT_OK_RETURN(status);
+
+                       lck = odb_lock(req, pvfs->odb_context, &lkey);
+                       data_blob_free(&lkey);
+                       if (lck == NULL) {
+                               DEBUG(0,("Unable to lock opendb for write time update\n"));
+                               return NT_STATUS_INTERNAL_ERROR;
+                       }
+               }
+
+               status = odb_set_write_time(lck, newstats.dos.write_time, true);
+               if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
+                       /* it could be that nobody has opened the file */
+               } else if (!NT_STATUS_IS_OK(status)) {
+                       DEBUG(0,("Unable to update write time: %s\n",
+                               nt_errstr(status)));
+                       return status;
                }
        }
 
@@ -582,10 +859,18 @@ NTSTATUS pvfs_setpathinfo(struct ntvfs_module_context *ntvfs,
                if (chmod(name->full_name, mode) == -1) {
                        return pvfs_map_errno(pvfs, errno);
                }
+               change_mask |= FILE_NOTIFY_CHANGE_ATTRIBUTES;
        }
 
        *name = newstats;
 
+       if (change_mask != 0) {
+               notify_trigger(pvfs->notify_context, 
+                              NOTIFY_ACTION_MODIFIED, 
+                              change_mask,
+                              name->full_name);
+       }
+
        return pvfs_dosattrib_save(pvfs, name, -1);
 }