Implement missing info level SMB_FILE_LINK_INFORMATION.
authorJeremy Allison <jra@samba.org>
Wed, 19 May 2010 01:34:54 +0000 (18:34 -0700)
committerJeremy Allison <jra@samba.org>
Wed, 19 May 2010 01:34:54 +0000 (18:34 -0700)
Fix bug #7435 - SMB2 hardlink fails (invalid level).
Found at the Microsoft plugsharing plugfest.

Jeremy.

source3/include/proto.h
source3/smbd/nttrans.c
source3/smbd/trans2.c

index 154efeb43b042a84ade2bdca67a3bc153989c5bc..3deeb9fa2f68c961590341a4eae0c767cc67b656 100644 (file)
@@ -6368,8 +6368,10 @@ void send_trans2_replies(connection_struct *conn,
 unsigned char *create_volume_objectid(connection_struct *conn, unsigned char objid[16]);
 NTSTATUS hardlink_internals(TALLOC_CTX *ctx,
                connection_struct *conn,
+               struct smb_request *req,
+               bool overwrite_if_exists,
                const struct smb_filename *smb_fname_old,
-               const struct smb_filename *smb_fname_new);
+               struct smb_filename *smb_fname_new);
 NTSTATUS smb_set_file_time(connection_struct *conn,
                           files_struct *fsp,
                           const struct smb_filename *smb_fname,
index b42d665668d2ebd772aec311d54d15b6afd01256..9b4d38a90498e84c970730d253abef46ec831c81 100644 (file)
@@ -1550,6 +1550,8 @@ void reply_ntrename(struct smb_request *req)
                                status = NT_STATUS_OBJECT_PATH_SYNTAX_BAD;
                        } else {
                                status = hardlink_internals(ctx, conn,
+                                                           req,
+                                                           false,
                                                            smb_fname_old,
                                                            smb_fname_new);
                        }
index 56d22b37bbccd7c9550cb5bd539a8e75c06a444b..dec9d7f8f977ac9d02a8833ccbd9ba3c43805657 100644 (file)
@@ -5348,8 +5348,10 @@ total_data=%u (should be %u)\n", (unsigned int)total_data, (unsigned int)IVAL(pd
 
 NTSTATUS hardlink_internals(TALLOC_CTX *ctx,
                connection_struct *conn,
+               struct smb_request *req,
+               bool overwrite_if_exists,
                const struct smb_filename *smb_fname_old,
-               const struct smb_filename *smb_fname_new)
+               struct smb_filename *smb_fname_new)
 {
        NTSTATUS status = NT_STATUS_OK;
 
@@ -5358,9 +5360,23 @@ NTSTATUS hardlink_internals(TALLOC_CTX *ctx,
                return NT_STATUS_OBJECT_NAME_NOT_FOUND;
        }
 
-       /* Disallow if newname already exists. */
        if (VALID_STAT(smb_fname_new->st)) {
-               return NT_STATUS_OBJECT_NAME_COLLISION;
+               if (overwrite_if_exists) {
+                       if (S_ISDIR(smb_fname_new->st.st_ex_mode)) {
+                               return NT_STATUS_FILE_IS_A_DIRECTORY;
+                       }
+                       status = unlink_internals(conn,
+                                               req,
+                                               FILE_ATTRIBUTE_NORMAL,
+                                               smb_fname_new,
+                                               false);
+                       if (!NT_STATUS_IS_OK(status)) {
+                               return status;
+                       }
+               } else {
+                       /* Disallow if newname already exists. */
+                       return NT_STATUS_OBJECT_NAME_COLLISION;
+               }
        }
 
        /* No links from a directory. */
@@ -5870,7 +5886,7 @@ static NTSTATUS smb_set_file_unix_link(connection_struct *conn,
 static NTSTATUS smb_set_file_unix_hlink(connection_struct *conn,
                                        struct smb_request *req,
                                        const char *pdata, int total_data,
-                                       const struct smb_filename *smb_fname_new)
+                                       struct smb_filename *smb_fname_new)
 {
        char *oldname = NULL;
        struct smb_filename *smb_fname_old = NULL;
@@ -5902,7 +5918,8 @@ static NTSTATUS smb_set_file_unix_hlink(connection_struct *conn,
                return status;
        }
 
-       return hardlink_internals(ctx, conn, smb_fname_old, smb_fname_new);
+       return hardlink_internals(ctx, conn, req, false,
+                       smb_fname_old, smb_fname_new);
 }
 
 /****************************************************************************
@@ -6006,6 +6023,75 @@ static NTSTATUS smb2_file_rename_information(connection_struct *conn,
        return status;
 }
 
+static NTSTATUS smb_file_link_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_link_information: got name |%s|\n",
+                               newname));
+
+       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->base_fsp) {
+               /* No stream names. */
+               return NT_STATUS_NOT_SUPPORTED;
+       }
+
+       DEBUG(10,("smb_file_link_information: "
+                 "SMB_FILE_LINK_INFORMATION (fnum %d) %s -> %s\n",
+                 fsp->fnum, fsp_str_dbg(fsp),
+                 smb_fname_str_dbg(smb_fname_dst)));
+       status = hardlink_internals(ctx,
+                               conn,
+                               req,
+                               overwrite,
+                               fsp->fsp_name,
+                               smb_fname_dst);
+
+       TALLOC_FREE(smb_fname_dst);
+       return status;
+}
 
 /****************************************************************************
  Deal with SMB_FILE_RENAME_INFORMATION.
@@ -7609,6 +7695,14 @@ NTSTATUS smbd_do_setfilepathinfo(connection_struct *conn,
                        break;
                }
 
+               case SMB_FILE_LINK_INFORMATION:
+               {
+                       status = smb_file_link_information(conn, req,
+                                                       pdata, total_data,
+                                                       fsp, smb_fname);
+                       break;
+               }
+
 #if defined(HAVE_POSIX_ACLS)
                case SMB_SET_POSIX_ACL:
                {