Extend NTIMES to allow setting create_time
[metze/samba/wip.git] / source3 / smbd / close.c
index 4c385d7611837f74823fa43f6d09aab48c3e07c2..2fb8ec2bb5514e5398034d7daecd4f98fb7a18a7 100644 (file)
@@ -67,10 +67,20 @@ static void check_magic(struct files_struct *fsp)
                return;
        }
 
-       chmod(fsp->fsp_name,0755);
-       ret = smbrun(fsp->fsp_name,&tmp_fd);
+       /* Ensure we don't depend on user's PATH. */
+       p = talloc_asprintf(ctx, "./%s", fsp->fsp_name);
+       if (!p) {
+               TALLOC_FREE(ctx);
+               return;
+       }
+
+       if (chmod(fsp->fsp_name,0755) == -1) {
+               TALLOC_FREE(ctx);
+               return;
+       }
+       ret = smbrun(p,&tmp_fd);
        DEBUG(3,("Invoking magic command %s gave %d\n",
-               fsp->fsp_name,ret));
+               p,ret));
 
        unlink(fsp->fsp_name);
        if (ret != 0 || tmp_fd == -1) {
@@ -106,8 +116,7 @@ static void check_magic(struct files_struct *fsp)
 static NTSTATUS close_filestruct(files_struct *fsp)
 {
        NTSTATUS status = NT_STATUS_OK;
-       connection_struct *conn = fsp->conn;
-    
+
        if (fsp->fh->fd != -1) {
                if(flush_write_cache(fsp, CLOSE_FLUSH) == -1) {
                        status = map_nt_error_from_unix(errno);
@@ -115,9 +124,8 @@ static NTSTATUS close_filestruct(files_struct *fsp)
                delete_write_cache(fsp);
        }
 
-       conn->num_files_open--;
        return status;
-}    
+}
 
 /****************************************************************************
  If any deferred opens are waiting on this close, notify them.
@@ -155,6 +163,75 @@ static void notify_deferred_opens(struct share_mode_lock *lck)
        }
 }
 
+/****************************************************************************
+ Delete all streams
+****************************************************************************/
+
+NTSTATUS delete_all_streams(connection_struct *conn, const char *fname)
+{
+       struct stream_struct *stream_info;
+       int i;
+       unsigned int num_streams;
+       TALLOC_CTX *frame = talloc_stackframe();
+       NTSTATUS status;
+
+       status = SMB_VFS_STREAMINFO(conn, NULL, fname, talloc_tos(),
+                                   &num_streams, &stream_info);
+
+       if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)) {
+               DEBUG(10, ("no streams around\n"));
+               TALLOC_FREE(frame);
+               return NT_STATUS_OK;
+       }
+
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(10, ("SMB_VFS_STREAMINFO failed: %s\n",
+                          nt_errstr(status)));
+               goto fail;
+       }
+
+       DEBUG(10, ("delete_all_streams found %d streams\n",
+                  num_streams));
+
+       if (num_streams == 0) {
+               TALLOC_FREE(frame);
+               return NT_STATUS_OK;
+       }
+
+       for (i=0; i<num_streams; i++) {
+               int res;
+               char *streamname;
+
+               if (strequal(stream_info[i].name, "::$DATA")) {
+                       continue;
+               }
+
+               streamname = talloc_asprintf(talloc_tos(), "%s%s", fname,
+                                            stream_info[i].name);
+
+               if (streamname == NULL) {
+                       DEBUG(0, ("talloc_aprintf failed\n"));
+                       status = NT_STATUS_NO_MEMORY;
+                       goto fail;
+               }
+
+               res = SMB_VFS_UNLINK(conn, streamname);
+
+               TALLOC_FREE(streamname);
+
+               if (res == -1) {
+                       status = map_nt_error_from_unix(errno);
+                       DEBUG(10, ("Could not delete stream %s: %s\n",
+                                  streamname, strerror(errno)));
+                       break;
+               }
+       }
+
+ fail:
+       TALLOC_FREE(frame);
+       return status;
+}
+
 /****************************************************************************
  Deal with removing a share mode on last close.
 ****************************************************************************/
@@ -177,7 +254,8 @@ static NTSTATUS close_remove_share_mode(files_struct *fsp,
         * This prevents race conditions with the file being created. JRA.
         */
 
-       lck = get_share_mode_lock(talloc_tos(), fsp->file_id, NULL, NULL);
+       lck = get_share_mode_lock(talloc_tos(), fsp->file_id, NULL, NULL,
+                                 NULL);
 
        if (lck == NULL) {
                DEBUG(0, ("close_remove_share_mode: Could not get share mode "
@@ -185,6 +263,10 @@ static NTSTATUS close_remove_share_mode(files_struct *fsp,
                return NT_STATUS_INVALID_PARAMETER;
        }
 
+       if (fsp->write_time_forced) {
+               set_close_write_time(fsp, lck->changed_write_time);
+       }
+
        if (!del_share_mode(lck, fsp)) {
                DEBUG(0, ("close_remove_share_mode: Could not delete share "
                          "entry for file %s\n", fsp->fsp_name));
@@ -247,6 +329,11 @@ static NTSTATUS close_remove_share_mode(files_struct *fsp,
        DEBUG(5,("close_remove_share_mode: file %s. Delete on close was set "
                 "- deleting file.\n", fsp->fsp_name));
 
+       /*
+        * Don't try to update the write time when we delete the file
+        */
+       fsp->update_write_time_on_close = false;
+
        if (!unix_token_equal(lck->delete_token, &current_user.ut)) {
                /* Become the user who requested the delete. */
 
@@ -305,6 +392,19 @@ static NTSTATUS close_remove_share_mode(files_struct *fsp,
                goto done;
        }
 
+       if ((conn->fs_capabilities & FILE_NAMED_STREAMS)
+           && !is_ntfs_stream_name(fsp->fsp_name)) {
+
+               status = delete_all_streams(conn, fsp->fsp_name);
+
+               if (!NT_STATUS_IS_OK(status)) {
+                       DEBUG(5, ("delete_all_streams failed: %s\n",
+                                 nt_errstr(status)));
+                       goto done;
+               }
+       }
+
+
        if (SMB_VFS_UNLINK(conn,fsp->fsp_name) != 0) {
                /*
                 * This call can potentially fail as another smbd may
@@ -345,6 +445,66 @@ static NTSTATUS close_remove_share_mode(files_struct *fsp,
        return status;
 }
 
+void set_close_write_time(struct files_struct *fsp, struct timespec ts)
+{
+       DEBUG(6,("close_write_time: %s" , time_to_asc(convert_timespec_to_time_t(ts))));
+
+       if (null_timespec(ts)) {
+               return;
+       }
+       /*
+        * if the write time on close is explict set, then don't
+        * need to fix it up to the value in the locking db
+        */
+       fsp->write_time_forced = false;
+
+       fsp->update_write_time_on_close = true;
+       fsp->close_write_time = ts;
+}
+
+static NTSTATUS update_write_time_on_close(struct files_struct *fsp)
+{
+       SMB_STRUCT_STAT sbuf;
+       struct smb_file_time ft;
+       NTSTATUS status;
+
+       ZERO_STRUCT(sbuf);
+       ZERO_STRUCT(ft);
+
+       if (!fsp->update_write_time_on_close) {
+               return NT_STATUS_OK;
+       }
+
+       if (null_timespec(fsp->close_write_time)) {
+               fsp->close_write_time = timespec_current();
+       }
+
+       /* Ensure we have a valid stat struct for the source. */
+       if (fsp->fh->fd != -1) {
+               if (SMB_VFS_FSTAT(fsp, &sbuf) == -1) {
+                       return map_nt_error_from_unix(errno);
+               }
+       } else {
+               if (SMB_VFS_STAT(fsp->conn,fsp->fsp_name,&sbuf) == -1) {
+                       return map_nt_error_from_unix(errno);
+               }
+       }
+
+       if (!VALID_STAT(sbuf)) {
+               /* if it doesn't seem to be a real file */
+               return NT_STATUS_OK;
+       }
+
+       ft.mtime = fsp->close_write_time;
+       status = smb_set_file_time(fsp->conn, fsp, fsp->fsp_name,
+                                  &sbuf, &ft, true);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       return NT_STATUS_OK;
+}
+
 /****************************************************************************
  Close a file.
 
@@ -353,12 +513,14 @@ static NTSTATUS close_remove_share_mode(files_struct *fsp,
  delete on close is done on normal and shutdown close.
 ****************************************************************************/
 
-static NTSTATUS close_normal_file(files_struct *fsp, enum file_close_type close_type)
+static NTSTATUS close_normal_file(struct smb_request *req, files_struct *fsp,
+                                 enum file_close_type close_type)
 {
        NTSTATUS status = NT_STATUS_OK;
        NTSTATUS saved_status1 = NT_STATUS_OK;
        NTSTATUS saved_status2 = NT_STATUS_OK;
        NTSTATUS saved_status3 = NT_STATUS_OK;
+       NTSTATUS saved_status4 = NT_STATUS_OK;
        connection_struct *conn = fsp->conn;
 
        if (fsp->aio_write_behind) {
@@ -383,7 +545,7 @@ static NTSTATUS close_normal_file(files_struct *fsp, enum file_close_type close_
 
        if (fsp->print_file) {
                print_fsp_end(fsp, close_type);
-               file_free(fsp);
+               file_free(req, fsp);
                return NT_STATUS_OK;
        }
 
@@ -413,11 +575,7 @@ static NTSTATUS close_normal_file(files_struct *fsp, enum file_close_type close_
         * Ensure pending modtime is set after close.
         */
 
-       if (fsp->pending_modtime_owner && !null_timespec(fsp->pending_modtime)) {
-               set_filetime(conn, fsp->fsp_name, fsp->pending_modtime);
-       } else if (!null_timespec(fsp->last_write_time)) {
-               set_filetime(conn, fsp->fsp_name, fsp->last_write_time);
-       }
+       saved_status4 = update_write_time_on_close(fsp);
 
        if (NT_STATUS_IS_OK(status)) {
                if (!NT_STATUS_IS_OK(saved_status1)) {
@@ -426,15 +584,17 @@ static NTSTATUS close_normal_file(files_struct *fsp, enum file_close_type close_
                        status = saved_status2;
                } else if (!NT_STATUS_IS_OK(saved_status3)) {
                        status = saved_status3;
+               } else if (!NT_STATUS_IS_OK(saved_status4)) {
+                       status = saved_status4;
                }
        }
 
        DEBUG(2,("%s closed file %s (numopen=%d) %s\n",
-               conn->user,fsp->fsp_name,
-               conn->num_files_open,
+               conn->server_info->unix_name,fsp->fsp_name,
+               conn->num_files_open - 1,
                nt_errstr(status) ));
 
-       file_free(fsp);
+       file_free(req, fsp);
        return status;
 }
 
@@ -442,7 +602,8 @@ static NTSTATUS close_normal_file(files_struct *fsp, enum file_close_type close_
  Close a directory opened by an NT SMB call. 
 ****************************************************************************/
   
-static NTSTATUS close_directory(files_struct *fsp, enum file_close_type close_type)
+static NTSTATUS close_directory(struct smb_request *req, files_struct *fsp,
+                               enum file_close_type close_type)
 {
        struct share_mode_lock *lck = 0;
        bool delete_dir = False;
@@ -453,7 +614,8 @@ static NTSTATUS close_directory(files_struct *fsp, enum file_close_type close_ty
         * reference to a directory also.
         */
 
-       lck = get_share_mode_lock(talloc_tos(), fsp->file_id, NULL, NULL);
+       lck = get_share_mode_lock(talloc_tos(), fsp->file_id, NULL, NULL,
+                                 NULL);
 
        if (lck == NULL) {
                DEBUG(0, ("close_directory: Could not get share mode lock for %s\n", fsp->fsp_name));
@@ -542,40 +704,87 @@ static NTSTATUS close_directory(files_struct *fsp, enum file_close_type close_ty
                        fsp, NT_STATUS_OK);
        }
 
+       status = fd_close(fsp);
+
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(0, ("Could not close dir! fname=%s, fd=%d, err=%d=%s\n",
+                         fsp->fsp_name, fsp->fh->fd, errno, strerror(errno)));
+       }
+
        /*
         * Do the code common to files and directories.
         */
        close_filestruct(fsp);
-       file_free(fsp);
+       file_free(req, fsp);
        return status;
 }
 
 /****************************************************************************
- Close a 'stat file' opened internally.
+ Close a files_struct.
 ****************************************************************************/
   
-static NTSTATUS close_stat(files_struct *fsp)
+NTSTATUS close_file(struct smb_request *req, files_struct *fsp,
+                   enum file_close_type close_type)
 {
-       /*
-        * Do the code common to files and directories.
-        */
-       close_filestruct(fsp);
-       file_free(fsp);
-       return NT_STATUS_OK;
+       NTSTATUS status;
+       struct files_struct *base_fsp = fsp->base_fsp;
+
+       if(fsp->is_directory) {
+               status = close_directory(req, fsp, close_type);
+       } else if (fsp->fake_file_handle != NULL) {
+               status = close_fake_file(req, fsp);
+       } else {
+               status = close_normal_file(req, fsp, close_type);
+       }
+
+       if ((base_fsp != NULL) && (close_type != SHUTDOWN_CLOSE)) {
+
+               /*
+                * fsp was a stream, the base fsp can't be a stream as well
+                *
+                * For SHUTDOWN_CLOSE this is not possible here, because
+                * SHUTDOWN_CLOSE only happens from files.c which walks the
+                * complete list of files. If we mess with more than one fsp
+                * those loops will become confused.
+                */
+
+               SMB_ASSERT(base_fsp->base_fsp == NULL);
+               close_file(req, base_fsp, close_type);
+       }
+
+       return status;
 }
 
 /****************************************************************************
- Close a files_struct.
+ Deal with an (authorized) message to close a file given the share mode
+ entry.
 ****************************************************************************/
-  
-NTSTATUS close_file(files_struct *fsp, enum file_close_type close_type)
+
+void msg_close_file(struct messaging_context *msg_ctx,
+                       void *private_data,
+                       uint32_t msg_type,
+                       struct server_id server_id,
+                       DATA_BLOB *data)
 {
-       if(fsp->is_directory) {
-               return close_directory(fsp, close_type);
-       } else if (fsp->is_stat) {
-               return close_stat(fsp);
-       } else if (fsp->fake_file_handle != NULL) {
-               return close_fake_file(fsp);
+       files_struct *fsp = NULL;
+       struct share_mode_entry e;
+
+       message_to_share_mode_entry(&e, (char *)data->data);
+
+       if(DEBUGLVL(10)) {
+               char *sm_str = share_mode_str(NULL, 0, &e);
+               if (!sm_str) {
+                       smb_panic("talloc failed");
+               }
+               DEBUG(10,("msg_close_file: got request to close share mode "
+                       "entry %s\n", sm_str));
+               TALLOC_FREE(sm_str);
+       }
+
+       fsp = file_find_dif(e.id, e.share_file_id);
+       if (!fsp) {
+               DEBUG(10,("msg_close_file: failed to find file.\n"));
+               return;
        }
-       return close_normal_file(fsp, close_type);
+       close_file(NULL, fsp, NORMAL_CLOSE);
 }