Implement rename/move in SMB2 from Windows7.
[ira/wip.git] / source3 / smbd / trans2.c
index ce001cb2fa2ac6b761541f88121b2465cd8cb6fd..eff5fba6760649b73e9fb3ac14876aa9561ed8d7 100644 (file)
@@ -2462,7 +2462,7 @@ total_data=%u (should be %u)\n", (unsigned int)total_data, (unsigned int)IVAL(pd
        if(numentries == 0) {
                dptr_close(sconn, &dptr_num);
                if (get_Protocol() < PROTOCOL_NT1) {
-                       reply_nterror(req, NT_STATUS_DOS(ERRDOS, ERRnofiles));
+                       reply_force_doserror(req, ERRDOS, ERRnofiles);
                        goto out;
                } else {
                        reply_botherror(req, NT_STATUS_NO_SUCH_FILE,
@@ -5815,7 +5815,6 @@ static NTSTATUS smb_set_file_unix_link(connection_struct *conn,
 {
        char *link_target = NULL;
        const char *newname = smb_fname->base_name;
-       NTSTATUS status = NT_STATUS_OK;
        TALLOC_CTX *ctx = talloc_tos();
 
        /* Set a symbolic link. */
@@ -5836,42 +5835,6 @@ static NTSTATUS smb_set_file_unix_link(connection_struct *conn,
                return NT_STATUS_INVALID_PARAMETER;
        }
 
-       /* !widelinks forces the target path to be within the share. */
-       /* This means we can interpret the target as a pathname. */
-       if (!lp_widelinks(SNUM(conn))) {
-               char *rel_name = NULL;
-               char *last_dirp = NULL;
-
-               if (*link_target == '/') {
-                       /* No absolute paths allowed. */
-                       return NT_STATUS_ACCESS_DENIED;
-               }
-               rel_name = talloc_strdup(ctx,newname);
-               if (!rel_name) {
-                       return NT_STATUS_NO_MEMORY;
-               }
-               last_dirp = strrchr_m(rel_name, '/');
-               if (last_dirp) {
-                       last_dirp[1] = '\0';
-               } else {
-                       rel_name = talloc_strdup(ctx,"./");
-                       if (!rel_name) {
-                               return NT_STATUS_NO_MEMORY;
-                       }
-               }
-               rel_name = talloc_asprintf_append(rel_name,
-                               "%s",
-                               link_target);
-               if (!rel_name) {
-                       return NT_STATUS_NO_MEMORY;
-               }
-
-               status = check_name(conn, rel_name);
-               if (!NT_STATUS_IS_OK(status)) {
-                       return status;
-               }
-       }
-
        DEBUG(10,("smb_set_file_unix_link: SMB_SET_FILE_UNIX_LINK doing symlink %s -> %s\n",
                        newname, link_target ));
 
@@ -5924,6 +5887,107 @@ static NTSTATUS smb_set_file_unix_hlink(connection_struct *conn,
        return hardlink_internals(ctx, conn, smb_fname_old, smb_fname_new);
 }
 
+/****************************************************************************
+ Deal with SMB2_FILE_RENAME_INFORMATION_INTERNAL
+****************************************************************************/
+
+static NTSTATUS smb2_file_rename_information(connection_struct *conn,
+                                           struct smb_request *req,
+                                           const char *pdata,
+                                           int total_data,
+                                           files_struct *fsp,
+                                           struct smb_filename *smb_fname_src)
+{
+       bool overwrite;
+       uint32_t len;
+       char *newname = NULL;
+       struct smb_filename *smb_fname_dst = NULL;
+       NTSTATUS status = NT_STATUS_OK;
+       TALLOC_CTX *ctx = talloc_tos();
+
+       if (!fsp) {
+               return NT_STATUS_INVALID_HANDLE;
+       }
+
+       if (total_data < 20) {
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       overwrite = (CVAL(pdata,0) ? True : False);
+       len = IVAL(pdata,16);
+
+       if (len > (total_data - 20) || (len == 0)) {
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       srvstr_get_path(ctx, pdata, req->flags2, &newname,
+                               &pdata[20], len, STR_TERMINATE,
+                               &status);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       DEBUG(10,("smb_file_rename_information: got name |%s|\n",
+                               newname));
+
+#if 0
+       /* Check the new name has no '/' characters. */
+       if (strchr_m(newname, '/')) {
+               return NT_STATUS_NOT_SUPPORTED;
+       }
+#endif
+
+       status = filename_convert(ctx,
+                               conn,
+                               req->flags2 & FLAGS2_DFS_PATHNAMES,
+                               newname,
+                               0,
+                               NULL,
+                               &smb_fname_dst);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       if (fsp && fsp->base_fsp) {
+               /* newname must be a stream name. */
+               if (newname[0] != ':') {
+                       return NT_STATUS_NOT_SUPPORTED;
+               }
+
+               /* Create an smb_fname to call rename_internals_fsp() with. */
+               status = create_synthetic_smb_fname(talloc_tos(),
+                   fsp->base_fsp->fsp_name->base_name, newname, NULL,
+                   &smb_fname_dst);
+               if (!NT_STATUS_IS_OK(status)) {
+                       goto out;
+               }
+
+               /*
+                * Set the original last component, since
+                * rename_internals_fsp() requires it.
+                */
+               smb_fname_dst->original_lcomp = talloc_strdup(smb_fname_dst,
+                                                             newname);
+               if (smb_fname_dst->original_lcomp == NULL) {
+                       status = NT_STATUS_NO_MEMORY;
+                       goto out;
+               }
+
+       }
+
+       DEBUG(10,("smb_file_rename_information: "
+                 "SMB_FILE_RENAME_INFORMATION (fnum %d) %s -> %s\n",
+                 fsp->fnum, fsp_str_dbg(fsp),
+                 smb_fname_str_dbg(smb_fname_dst)));
+       status = rename_internals_fsp(conn, fsp, smb_fname_dst, 0,
+                                     overwrite);
+
+ out:
+       TALLOC_FREE(smb_fname_dst);
+       return status;
+}
+
+
 /****************************************************************************
  Deal with SMB_FILE_RENAME_INFORMATION.
 ****************************************************************************/
@@ -7512,6 +7576,14 @@ NTSTATUS smbd_do_setfilepathinfo(connection_struct *conn,
                        break;
                }
 
+               case SMB2_FILE_RENAME_INFORMATION_INTERNAL:
+               {
+                       /* SMB2 rename information. */
+                       status = smb2_file_rename_information(conn, req,
+                                                            pdata, total_data,
+                                                            fsp, smb_fname);
+                       break;
+               }
 #if defined(HAVE_POSIX_ACLS)
                case SMB_SET_POSIX_ACL:
                {