s3:smbd: allow info class SMB_QUERY_FS_VOLUME_INFO to return partial data
[nivanova/samba-autobuild/.git] / source3 / smbd / trans2.c
index 2bc85bf55052921d873d37546ef198a30d5a4d55..3bd5656cd198ccfb2a7b33430d6aacee7364d70a 100644 (file)
@@ -67,6 +67,7 @@ NTSTATUS check_access(connection_struct *conn,
        } else {
                NTSTATUS status = smbd_check_access_rights(conn,
                                        smb_fname,
+                                       false,
                                        access_mask);
                if (!NT_STATUS_IS_OK(status)) {
                        return status;
@@ -322,6 +323,7 @@ static NTSTATUS get_ea_list_from_file_path(TALLOC_CTX *mem_ctx, connection_struc
        NTSTATUS status;
 
        *pea_total_len = 0;
+       *ea_list = NULL;
 
        status = get_ea_names_from_file(talloc_tos(), conn, fsp, fname,
                                        &names, &num_names);
@@ -343,19 +345,38 @@ static NTSTATUS get_ea_list_from_file_path(TALLOC_CTX *mem_ctx, connection_struc
                    || samba_private_attr_name(names[i]))
                        continue;
 
+               /*
+                * Filter out any underlying POSIX EA names
+                * that a Windows client can't handle.
+                */
+               if (!lp_posix_pathnames() &&
+                               is_invalid_windows_ea_name(names[i])) {
+                       continue;
+               }
+
                listp = talloc(mem_ctx, struct ea_list);
                if (listp == NULL) {
                        return NT_STATUS_NO_MEMORY;
                }
 
-               status = get_ea_value(mem_ctx, conn, fsp,
+               status = get_ea_value(listp, conn, fsp,
                                      fname, names[i],
                                      &listp->ea);
 
                if (!NT_STATUS_IS_OK(status)) {
+                       TALLOC_FREE(listp);
                        return status;
                }
 
+               if (listp->ea.value.length == 0) {
+                       /*
+                        * We can never return a zero length EA.
+                        * Windows reports the EA's as corrupted.
+                        */
+                       TALLOC_FREE(listp);
+                       continue;
+               }
+
                push_ascii_fstring(dos_ea_name, listp->ea.name);
 
                *pea_total_len +=
@@ -457,6 +478,7 @@ static NTSTATUS fill_ea_chained_buffer(TALLOC_CTX *mem_ctx,
 {
        uint8_t *p = (uint8_t *)pdata;
        uint8_t *last_start = NULL;
+       bool do_store_data = (pdata != NULL);
 
        *ret_data_size = 0;
 
@@ -468,8 +490,9 @@ static NTSTATUS fill_ea_chained_buffer(TALLOC_CTX *mem_ctx,
                size_t dos_namelen;
                fstring dos_ea_name;
                size_t this_size;
+               size_t pad = 0;
 
-               if (last_start) {
+               if (last_start != NULL && do_store_data) {
                        SIVAL(last_start, 0, PTR_DIFF(p, last_start));
                }
                last_start = p;
@@ -486,23 +509,30 @@ static NTSTATUS fill_ea_chained_buffer(TALLOC_CTX *mem_ctx,
                this_size = 0x08 + dos_namelen + 1 + ea_list->ea.value.length;
 
                if (ea_list->next) {
-                       size_t pad = 4 - (this_size % 4);
+                       pad = (4 - (this_size % 4)) % 4;
                        this_size += pad;
                }
 
-               if (this_size > total_data_size) {
-                       return NT_STATUS_INFO_LENGTH_MISMATCH;
+               if (do_store_data) {
+                       if (this_size > total_data_size) {
+                               return NT_STATUS_INFO_LENGTH_MISMATCH;
+                       }
+
+                       /* We know we have room. */
+                       SIVAL(p, 0x00, 0); /* next offset */
+                       SCVAL(p, 0x04, ea_list->ea.flags);
+                       SCVAL(p, 0x05, dos_namelen);
+                       SSVAL(p, 0x06, ea_list->ea.value.length);
+                       strlcpy((char *)(p+0x08), dos_ea_name, dos_namelen+1);
+                       memcpy(p + 0x08 + dos_namelen + 1, ea_list->ea.value.data, ea_list->ea.value.length);
+                       if (pad) {
+                               memset(p + 0x08 + dos_namelen + 1 + ea_list->ea.value.length,
+                                       '\0',
+                                       pad);
+                       }
+                       total_data_size -= this_size;
                }
 
-               /* We know we have room. */
-               SIVAL(p, 0x00, 0); /* next offset */
-               SCVAL(p, 0x04, ea_list->ea.flags);
-               SCVAL(p, 0x05, dos_namelen);
-               SSVAL(p, 0x06, ea_list->ea.value.length);
-               strlcpy((char *)(p+0x08), dos_ea_name, dos_namelen+1);
-               memcpy(p + 0x08 + dos_namelen + 1, ea_list->ea.value.data, ea_list->ea.value.length);
-
-               total_data_size -= this_size;
                p += this_size;
        }
 
@@ -515,7 +545,7 @@ static unsigned int estimate_ea_size(connection_struct *conn, files_struct *fsp,
 {
        size_t total_ea_len = 0;
        TALLOC_CTX *mem_ctx;
-       struct ea_list *ea_list;
+       struct ea_list *ea_list = NULL;
 
        if (!lp_ea_support(SNUM(conn))) {
                return 0;
@@ -530,6 +560,26 @@ static unsigned int estimate_ea_size(connection_struct *conn, files_struct *fsp,
                fsp = NULL;
        }
        (void)get_ea_list_from_file_path(mem_ctx, conn, fsp, smb_fname->base_name, &total_ea_len, &ea_list);
+       if(conn->sconn->using_smb2) {
+               NTSTATUS status;
+               unsigned int ret_data_size;
+               /*
+                * We're going to be using fill_ea_chained_buffer() to
+                * marshall EA's - this size is significantly larger
+                * than the SMB1 buffer. Re-calculate the size without
+                * marshalling.
+                */
+               status = fill_ea_chained_buffer(mem_ctx,
+                                               NULL,
+                                               0,
+                                               &ret_data_size,
+                                               conn,
+                                               ea_list);
+               if (!NT_STATUS_IS_OK(status)) {
+                       ret_data_size = 0;
+               }
+               total_ea_len = ret_data_size;
+       }
        TALLOC_FREE(mem_ctx);
        return total_ea_len;
 }
@@ -582,6 +632,15 @@ NTSTATUS set_ea(connection_struct *conn, files_struct *fsp,
                return NT_STATUS_INVALID_PARAMETER;
        }
 
+       /*
+        * Filter out invalid Windows EA names - before
+        * we set *any* of them.
+        */
+
+       if (ea_list_has_invalid_name(ea_list)) {
+               return STATUS_INVALID_EA_NAME;
+       }
+
        fname = smb_fname->base_name;
 
        for (;ea_list; ea_list = ea_list->next) {
@@ -837,6 +896,7 @@ static struct ea_list *ea_list_union(struct ea_list *name_list, struct ea_list *
 
 void send_trans2_replies(connection_struct *conn,
                        struct smb_request *req,
+                       NTSTATUS status,
                         const char *params,
                         int paramsize,
                         const char *pdata,
@@ -877,6 +937,14 @@ void send_trans2_replies(connection_struct *conn,
 
        if(params_to_send == 0 && data_to_send == 0) {
                reply_outbuf(req, 10, 0);
+               if (NT_STATUS_V(status)) {
+                       uint8_t eclass;
+                       uint32_t ecode;
+                       ntstatus_to_dos(status, &eclass, &ecode);
+                       error_packet_set((char *)req->outbuf,
+                                       eclass, ecode, status,
+                                       __LINE__,__FILE__);
+               }
                show_msg((char *)req->outbuf);
                if (!srv_send_smb(sconn,
                                (char *)req->outbuf,
@@ -1007,6 +1075,13 @@ void send_trans2_replies(connection_struct *conn,
                                         ERRDOS,ERRbufferoverflow,
                                         STATUS_BUFFER_OVERFLOW,
                                         __LINE__,__FILE__);
+               } else if (NT_STATUS_V(status)) {
+                       uint8_t eclass;
+                       uint32_t ecode;
+                       ntstatus_to_dos(status, &eclass, &ecode);
+                       error_packet_set((char *)req->outbuf,
+                                       eclass, ecode, status,
+                                       __LINE__,__FILE__);
                }
 
                /* Send the packet */
@@ -1178,6 +1253,20 @@ static void call_trans2open(connection_struct *conn,
                        reply_nterror(req, NT_STATUS_EAS_NOT_SUPPORTED);
                        goto out;
                }
+
+               if (ea_list_has_invalid_name(ea_list)) {
+                       int param_len = 30;
+                       *pparams = (char *)SMB_REALLOC(*pparams, param_len);
+                       if(*pparams == NULL ) {
+                               reply_nterror(req, NT_STATUS_NO_MEMORY);
+                               goto out;
+                       }
+                       params = *pparams;
+                       memset(params, '\0', param_len);
+                       send_trans2_replies(conn, req, STATUS_INVALID_EA_NAME,
+                               params, param_len, NULL, 0, max_data_bytes);
+                       goto out;
+               }
        }
 
        status = SMB_VFS_CREATE_FILE(
@@ -1253,7 +1342,7 @@ static void call_trans2open(connection_struct *conn,
        }
 
        /* Send the required number of replies */
-       send_trans2_replies(conn, req, params, 30, *ppdata, 0, max_data_bytes);
+       send_trans2_replies(conn, req, NT_STATUS_OK, params, 30, *ppdata, 0, max_data_bytes);
  out:
        TALLOC_FREE(smb_fname);
 }
@@ -1364,28 +1453,18 @@ static NTSTATUS unix_perms_from_wire( connection_struct *conn,
 
        switch (ptype) {
        case PERM_NEW_FILE:
+       case PERM_EXISTING_FILE:
                /* Apply mode mask */
                ret &= lp_create_mask(SNUM(conn));
                /* Add in force bits */
                ret |= lp_force_create_mode(SNUM(conn));
                break;
        case PERM_NEW_DIR:
+       case PERM_EXISTING_DIR:
                ret &= lp_dir_mask(SNUM(conn));
                /* Add in force bits */
                ret |= lp_force_dir_mode(SNUM(conn));
                break;
-       case PERM_EXISTING_FILE:
-               /* Apply mode mask */
-               ret &= lp_security_mask(SNUM(conn));
-               /* Add in force bits */
-               ret |= lp_force_security_mode(SNUM(conn));
-               break;
-       case PERM_EXISTING_DIR:
-               /* Apply mode mask */
-               ret &= lp_dir_security_mask(SNUM(conn));
-               /* Add in force bits */
-               ret |= lp_force_dir_security_mode(SNUM(conn));
-               break;
        }
 
        *ret_perms = ret;
@@ -2485,6 +2564,11 @@ total_data=%u (should be %u)\n", (unsigned int)total_data, (unsigned int)IVAL(pd
                }
        }
 
+       if (max_data_bytes + DIR_ENTRY_SAFETY_MARGIN < max_data_bytes) {
+               reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
+               goto out;
+       }
+
        *ppdata = (char *)SMB_REALLOC(
                *ppdata, max_data_bytes + DIR_ENTRY_SAFETY_MARGIN);
        if(*ppdata == NULL ) {
@@ -2627,7 +2711,7 @@ total_data=%u (should be %u)\n", (unsigned int)total_data, (unsigned int)IVAL(pd
        SSVAL(params,6,0); /* Never an EA error */
        SSVAL(params,8,last_entry_off);
 
-       send_trans2_replies(conn, req, params, 10, pdata, PTR_DIFF(p,pdata),
+       send_trans2_replies(conn, req, NT_STATUS_OK, params, 10, pdata, PTR_DIFF(p,pdata),
                            max_data_bytes);
 
        if ((! *directory) && dptr_path(sconn, dptr_num)) {
@@ -2814,6 +2898,11 @@ total_data=%u (should be %u)\n", (unsigned int)total_data, (unsigned int)IVAL(pd
                }
        }
 
+       if (max_data_bytes + DIR_ENTRY_SAFETY_MARGIN < max_data_bytes) {
+               reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
+               return;
+       }
+
        *ppdata = (char *)SMB_REALLOC(
                *ppdata, max_data_bytes + DIR_ENTRY_SAFETY_MARGIN);
        if(*ppdata == NULL) {
@@ -2978,7 +3067,7 @@ total_data=%u (should be %u)\n", (unsigned int)total_data, (unsigned int)IVAL(pd
        SSVAL(params,4,0); /* Never an EA error */
        SSVAL(params,6,last_entry_off);
 
-       send_trans2_replies(conn, req, params, 8, pdata, PTR_DIFF(p,pdata),
+       send_trans2_replies(conn, req, NT_STATUS_OK, params, 8, pdata, PTR_DIFF(p,pdata),
                            max_data_bytes);
 
        return;
@@ -3030,6 +3119,7 @@ NTSTATUS smbd_do_qfsinfo(connection_struct *conn,
                         uint16_t info_level,
                         uint16_t flags2,
                         unsigned int max_data_bytes,
+                        struct smb_filename *fname,
                         char **ppdata,
                         int *ret_data_len)
 {
@@ -3038,9 +3128,17 @@ NTSTATUS smbd_do_qfsinfo(connection_struct *conn,
        const char *vname = volume_label(talloc_tos(), SNUM(conn));
        int snum = SNUM(conn);
        char *fstype = lp_fstype(talloc_tos(), SNUM(conn));
+       char *filename = NULL;
        uint32 additional_flags = 0;
-       struct smb_filename smb_fname_dot;
+       struct smb_filename smb_fname;
        SMB_STRUCT_STAT st;
+       NTSTATUS status = NT_STATUS_OK;
+
+       if (fname == NULL || fname->base_name == NULL) {
+               filename = ".";
+       } else {
+               filename = fname->base_name;
+       }
 
        if (IS_IPC(conn)) {
                if (info_level != SMB_QUERY_CIFS_UNIX_INFO) {
@@ -3053,15 +3151,19 @@ NTSTATUS smbd_do_qfsinfo(connection_struct *conn,
 
        DEBUG(3,("smbd_do_qfsinfo: level = %d\n", info_level));
 
-       ZERO_STRUCT(smb_fname_dot);
-       smb_fname_dot.base_name = discard_const_p(char, ".");
+       ZERO_STRUCT(smb_fname);
+       smb_fname.base_name = discard_const_p(char, filename);
 
-       if(SMB_VFS_STAT(conn, &smb_fname_dot) != 0) {
+       if(SMB_VFS_STAT(conn, &smb_fname) != 0) {
                DEBUG(2,("stat of . failed (%s)\n", strerror(errno)));
                return map_nt_error_from_unix(errno);
        }
 
-       st = smb_fname_dot.st;
+       st = smb_fname.st;
+
+       if (max_data_bytes + DIR_ENTRY_SAFETY_MARGIN < max_data_bytes) {
+               return NT_STATUS_INVALID_PARAMETER;
+       }
 
        *ppdata = (char *)SMB_REALLOC(
                *ppdata, max_data_bytes + DIR_ENTRY_SAFETY_MARGIN);
@@ -3078,7 +3180,7 @@ NTSTATUS smbd_do_qfsinfo(connection_struct *conn,
                {
                        uint64_t dfree,dsize,bsize,block_size,sectors_per_unit,bytes_per_sector;
                        data_len = 18;
-                       if (get_dfree_info(conn,".",False,&bsize,&dfree,&dsize) == (uint64_t)-1) {
+                       if (get_dfree_info(conn,filename,False,&bsize,&dfree,&dsize) == (uint64_t)-1) {
                                return map_nt_error_from_unix(errno);
                        }
 
@@ -3195,6 +3297,12 @@ cBytesSector=%u, cUnitTotal=%u, cUnitAvail=%d\n", (unsigned int)st.st_ex_dev, (u
                        DEBUG(5,("smbd_do_qfsinfo : SMB_QUERY_FS_VOLUME_INFO namelen = %d, vol=%s serv=%s\n",
                                (int)strlen(vname),vname,
                                lp_servicename(talloc_tos(), snum)));
+                       if (max_data_bytes >= 24 && data_len > max_data_bytes) {
+                               /* the client only requested a portion of the
+                                  volume label */
+                               data_len = max_data_bytes;
+                               status = STATUS_BUFFER_OVERFLOW;
+                       }
                        break;
 
                case SMB_QUERY_FS_SIZE_INFO:
@@ -3202,7 +3310,7 @@ cBytesSector=%u, cUnitTotal=%u, cUnitAvail=%d\n", (unsigned int)st.st_ex_dev, (u
                {
                        uint64_t dfree,dsize,bsize,block_size,sectors_per_unit,bytes_per_sector;
                        data_len = 24;
-                       if (get_dfree_info(conn,".",False,&bsize,&dfree,&dsize) == (uint64_t)-1) {
+                       if (get_dfree_info(conn,filename,False,&bsize,&dfree,&dsize) == (uint64_t)-1) {
                                return map_nt_error_from_unix(errno);
                        }
                        block_size = lp_block_size(snum);
@@ -3234,7 +3342,7 @@ cBytesSector=%u, cUnitTotal=%u, cUnitAvail=%d\n", (unsigned int)bsize, (unsigned
                {
                        uint64_t dfree,dsize,bsize,block_size,sectors_per_unit,bytes_per_sector;
                        data_len = 32;
-                       if (get_dfree_info(conn,".",False,&bsize,&dfree,&dsize) == (uint64_t)-1) {
+                       if (get_dfree_info(conn,filename,False,&bsize,&dfree,&dsize) == (uint64_t)-1) {
                                return map_nt_error_from_unix(errno);
                        }
                        block_size = lp_block_size(snum);
@@ -3427,7 +3535,7 @@ cBytesSector=%u, cUnitTotal=%u, cUnitAvail=%d\n", (unsigned int)bsize, (unsigned
                                return NT_STATUS_INVALID_LEVEL;
                        }
 
-                       rc = SMB_VFS_STATVFS(conn, ".", &svfs);
+                       rc = SMB_VFS_STATVFS(conn, filename, &svfs);
 
                        if (!rc) {
                                data_len = 56;
@@ -3566,7 +3674,7 @@ cBytesSector=%u, cUnitTotal=%u, cUnitAvail=%d\n", (unsigned int)bsize, (unsigned
        }
 
        *ret_data_len = data_len;
-       return NT_STATUS_OK;
+       return status;
 }
 
 /****************************************************************************
@@ -3607,13 +3715,14 @@ static void call_trans2qfsinfo(connection_struct *conn,
                                 info_level,
                                 req->flags2,
                                 max_data_bytes,
+                                NULL,
                                 ppdata, &data_len);
        if (!NT_STATUS_IS_OK(status)) {
                reply_nterror(req, status);
                return;
        }
 
-       send_trans2_replies(conn, req, params, 0, *ppdata, data_len,
+       send_trans2_replies(conn, req, NT_STATUS_OK, params, 0, *ppdata, data_len,
                            max_data_bytes);
 
        DEBUG( 4, ( "%s info_level = %d\n",
@@ -3769,6 +3878,7 @@ static void call_trans2setfsinfo(connection_struct *conn,
                                }
 
                                send_trans2_replies(conn, req,
+                                               NT_STATUS_OK,
                                                *pparams,
                                                param_len,
                                                *ppdata,
@@ -4168,7 +4278,7 @@ static NTSTATUS marshall_stream_info(unsigned int num_streams,
        unsigned int i;
        unsigned int ofs = 0;
 
-       for (i = 0; i < num_streams && ofs <= max_data_bytes; i++) {
+       for (i = 0; i < num_streams; i++) {
                unsigned int next_offset;
                size_t namelen;
                smb_ucs2_t *namebuf;
@@ -4187,6 +4297,16 @@ static NTSTATUS marshall_stream_info(unsigned int num_streams,
 
                namelen -= 2;
 
+               /*
+                * We cannot overflow ...
+                */
+               if ((ofs + 24 + namelen) > max_data_bytes) {
+                       DEBUG(10, ("refusing to overflow reply at stream %u\n",
+                               i));
+                       TALLOC_FREE(namebuf);
+                       return STATUS_BUFFER_OVERFLOW;
+               }
+
                SIVAL(data, ofs+4, namelen);
                SOFF_T(data, ofs+8, streams[i].size);
                SOFF_T(data, ofs+16, streams[i].alloc_size);
@@ -4201,6 +4321,14 @@ static NTSTATUS marshall_stream_info(unsigned int num_streams,
                else {
                        unsigned int align = ndr_align_size(next_offset, 8);
 
+                       if ((next_offset + align) > max_data_bytes) {
+                               DEBUG(10, ("refusing to overflow align "
+                                       "reply at stream %u\n",
+                                       i));
+                               TALLOC_FREE(namebuf);
+                               return STATUS_BUFFER_OVERFLOW;
+                       }
+
                        memset(data+next_offset, 0, align);
                        next_offset += align;
 
@@ -4211,6 +4339,8 @@ static NTSTATUS marshall_stream_info(unsigned int num_streams,
                ofs = next_offset;
        }
 
+       DEBUG(10, ("max_data: %u, data_size: %u\n", max_data_bytes, ofs));
+
        *data_size = ofs;
 
        return NT_STATUS_OK;
@@ -4259,6 +4389,10 @@ static void call_trans2qpipeinfo(connection_struct *conn,
        }
        params = *pparams;
        SSVAL(params,0,0);
+       if (max_data_bytes + DIR_ENTRY_SAFETY_MARGIN < max_data_bytes) {
+               reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
+               return;
+       }
        data_size = max_data_bytes + DIR_ENTRY_SAFETY_MARGIN;
        *ppdata = (char *)SMB_REALLOC(*ppdata, data_size); 
        if (*ppdata == NULL ) {
@@ -4281,7 +4415,7 @@ static void call_trans2qpipeinfo(connection_struct *conn,
                        return;
        }
 
-       send_trans2_replies(conn, req, params, param_size, *ppdata, data_size,
+       send_trans2_replies(conn, req, NT_STATUS_OK, params, param_size, *ppdata, data_size,
                            max_data_bytes);
 
        return;
@@ -4340,6 +4474,10 @@ NTSTATUS smbd_do_qfilepathinfo(connection_struct *conn,
                nlink -= 1;
        }
 
+       if (max_data_bytes + DIR_ENTRY_SAFETY_MARGIN < max_data_bytes) {
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
        data_size = max_data_bytes + DIR_ENTRY_SAFETY_MARGIN;
        *ppdata = (char *)SMB_REALLOC(*ppdata, data_size); 
        if (*ppdata == NULL) {
@@ -4810,6 +4948,7 @@ NTSTATUS smbd_do_qfilepathinfo(connection_struct *conn,
                        if (!NT_STATUS_IS_OK(status)) {
                                DEBUG(10, ("marshall_stream_info failed: %s\n",
                                           nt_errstr(status)));
+                               TALLOC_FREE(streams);
                                return status;
                        }
 
@@ -4920,12 +5059,14 @@ NTSTATUS smbd_do_qfilepathinfo(connection_struct *conn,
                                uint16 num_def_acls = 0;
 
                                if (fsp && fsp->fh->fd != -1) {
-                                       file_acl = SMB_VFS_SYS_ACL_GET_FD(fsp);
+                                       file_acl = SMB_VFS_SYS_ACL_GET_FD(fsp,
+                                               talloc_tos());
                                } else {
                                        file_acl =
                                            SMB_VFS_SYS_ACL_GET_FILE(conn,
                                                smb_fname->base_name,
-                                               SMB_ACL_TYPE_ACCESS);
+                                               SMB_ACL_TYPE_ACCESS,
+                                               talloc_tos());
                                }
 
                                if (file_acl == NULL && no_acl_syscall_error(errno)) {
@@ -4942,13 +5083,15 @@ NTSTATUS smbd_do_qfilepathinfo(connection_struct *conn,
                                                    SMB_VFS_SYS_ACL_GET_FILE(
                                                            conn,
                                                            fsp->fsp_name->base_name,
-                                                           SMB_ACL_TYPE_DEFAULT);
+                                                           SMB_ACL_TYPE_DEFAULT,
+                                                           talloc_tos());
                                        } else {
                                                def_acl =
                                                    SMB_VFS_SYS_ACL_GET_FILE(
                                                            conn,
                                                            smb_fname->base_name,
-                                                           SMB_ACL_TYPE_DEFAULT);
+                                                           SMB_ACL_TYPE_DEFAULT,
+                                                           talloc_tos());
                                        }
                                        def_acl = free_empty_sys_acl(conn, def_acl);
                                }
@@ -5151,10 +5294,9 @@ static void call_trans2qfilepathinfo(connection_struct *conn,
                        return;
                }
 
-               status = copy_smb_filename(talloc_tos(), fsp->fsp_name,
-                                          &smb_fname);
-               if (!NT_STATUS_IS_OK(status)) {
-                       reply_nterror(req, status);
+               smb_fname = cp_smb_filename(talloc_tos(), fsp->fsp_name);
+               if (smb_fname == NULL) {
+                       reply_nterror(req, NT_STATUS_NO_MEMORY);
                        return;
                }
 
@@ -5267,16 +5409,14 @@ static void call_trans2qfilepathinfo(connection_struct *conn,
                /* If this is a stream, check if there is a delete_pending. */
                if ((conn->fs_capabilities & FILE_NAMED_STREAMS)
                    && is_ntfs_stream_smb_fname(smb_fname)) {
-                       struct smb_filename *smb_fname_base = NULL;
+                       struct smb_filename *smb_fname_base;
 
                        /* Create an smb_filename with stream_name == NULL. */
-                       status =
-                           create_synthetic_smb_fname(talloc_tos(),
-                                                      smb_fname->base_name,
-                                                      NULL, NULL,
-                                                      &smb_fname_base);
-                       if (!NT_STATUS_IS_OK(status)) {
-                               reply_nterror(req, status);
+                       smb_fname_base = synthetic_smb_fname(
+                               talloc_tos(), smb_fname->base_name,
+                               NULL, NULL);
+                       if (smb_fname_base == NULL) {
+                               reply_nterror(req, NT_STATUS_NO_MEMORY);
                                return;
                        }
 
@@ -5481,7 +5621,7 @@ total_data=%u (should be %u)\n", (unsigned int)total_data, (unsigned int)IVAL(pd
                return;
        }
 
-       send_trans2_replies(conn, req, params, param_size, *ppdata, data_size,
+       send_trans2_replies(conn, req, NT_STATUS_OK, params, param_size, *ppdata, data_size,
                            max_data_bytes);
 
        return;
@@ -5659,7 +5799,7 @@ static NTSTATUS smb_set_file_dosmode(connection_struct *conn,
                                     const struct smb_filename *smb_fname,
                                     uint32 dosmode)
 {
-       struct smb_filename *smb_fname_base = NULL;
+       struct smb_filename *smb_fname_base;
        NTSTATUS status;
 
        if (!VALID_STAT(smb_fname->st)) {
@@ -5667,11 +5807,10 @@ static NTSTATUS smb_set_file_dosmode(connection_struct *conn,
        }
 
        /* Always operate on the base_name, even if a stream was passed in. */
-       status = create_synthetic_smb_fname(talloc_tos(), smb_fname->base_name,
-                                           NULL, &smb_fname->st,
-                                           &smb_fname_base);
-       if (!NT_STATUS_IS_OK(status)) {
-               return status;
+       smb_fname_base = synthetic_smb_fname(
+               talloc_tos(), smb_fname->base_name, NULL, &smb_fname->st);
+       if (smb_fname_base == NULL) {
+               return NT_STATUS_NO_MEMORY;
        }
 
        if (dosmode) {
@@ -5748,9 +5887,9 @@ static NTSTATUS smb_set_file_size(connection_struct *conn,
                return NT_STATUS_OK;
        }
 
-       status = copy_smb_filename(talloc_tos(), smb_fname, &smb_fname_tmp);
-       if (!NT_STATUS_IS_OK(status)) {
-               return status;
+       smb_fname_tmp = cp_smb_filename(talloc_tos(), smb_fname);
+       if (smb_fname_tmp == NULL) {
+               return NT_STATUS_NO_MEMORY;
        }
 
        smb_fname_tmp->st = *psbuf;
@@ -6133,10 +6272,11 @@ static NTSTATUS smb2_file_rename_information(connection_struct *conn,
                }
 
                /* 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)) {
+               smb_fname_dst = synthetic_smb_fname(
+                       talloc_tos(), fsp->base_fsp->fsp_name->base_name,
+                       newname, NULL);
+               if (smb_fname_dst == NULL) {
+                       status = NT_STATUS_NO_MEMORY;
                        goto out;
                }
 
@@ -6302,10 +6442,11 @@ static NTSTATUS smb_file_rename_information(connection_struct *conn,
                }
 
                /* 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)) {
+               smb_fname_dst = synthetic_smb_fname(
+                       talloc_tos(), fsp->base_fsp->fsp_name->base_name,
+                       newname, NULL);
+               if (smb_fname_dst == NULL) {
+                       status = NT_STATUS_NO_MEMORY;
                        goto out;
                }
 
@@ -6373,11 +6514,10 @@ static NTSTATUS smb_file_rename_information(connection_struct *conn,
                                goto out;
                        }
                        /* Create an smb_fname to call rename_internals_fsp() */
-                       status = create_synthetic_smb_fname(ctx,
-                                                           base_name, NULL,
-                                                           NULL,
-                                                           &smb_fname_dst);
-                       if (!NT_STATUS_IS_OK(status)) {
+                       smb_fname_dst = synthetic_smb_fname(
+                               ctx, base_name, NULL, NULL);
+                       if (smb_fname_dst == NULL) {
+                               status = NT_STATUS_NO_MEMORY;
                                goto out;
                        }
                }
@@ -6989,10 +7129,9 @@ static NTSTATUS smb_set_file_unix_basic(connection_struct *conn,
                        return status;
                }
 
-               status = copy_smb_filename(talloc_tos(), smb_fname,
-                                          &smb_fname_tmp);
-               if (!NT_STATUS_IS_OK(status)) {
-                       return status;
+               smb_fname_tmp = cp_smb_filename(talloc_tos(), smb_fname);
+               if (smb_fname_tmp == NULL) {
+                       return NT_STATUS_NO_MEMORY;
                }
 
                if (SMB_VFS_STAT(conn, smb_fname_tmp) != 0) {
@@ -7387,12 +7526,19 @@ static NTSTATUS smb_posix_open(connection_struct *conn,
                        /* File exists open. File not exist create. */
                        create_disp = FILE_OPEN_IF;
                        break;
+               case SMB_O_EXCL:
+                       /* O_EXCL on its own without O_CREAT is undefined.
+                          We deliberately ignore it as some versions of
+                          Linux CIFSFS can send a bare O_EXCL on the
+                          wire which other filesystems in the kernel
+                          ignore. See bug 9519 for details. */
+
+                       /* Fallthrough. */
+
                case 0:
                        /* File exists open. File not exist fail. */
                        create_disp = FILE_OPEN;
                        break;
-               case SMB_O_EXCL:
-                       /* O_EXCL on its own without O_CREAT is undefined. */
                default:
                        DEBUG(5,("smb_posix_open: invalid create mode 0x%x\n",
                                (unsigned int)wire_open_mode ));
@@ -7647,8 +7793,8 @@ static NTSTATUS smb_posix_unlink(connection_struct *conn,
                                continue;
                        }
                        /* Fail with sharing violation. */
-                       close_file(req, fsp, NORMAL_CLOSE);
                        TALLOC_FREE(lck);
+                       close_file(req, fsp, NORMAL_CLOSE);
                        return NT_STATUS_SHARING_VIOLATION;
                }
        }
@@ -7662,12 +7808,12 @@ static NTSTATUS smb_posix_unlink(connection_struct *conn,
                                                fsp,
                                                smb_fname);
 
+       TALLOC_FREE(lck);
+
        if (!NT_STATUS_IS_OK(status)) {
                close_file(req, fsp, NORMAL_CLOSE);
-               TALLOC_FREE(lck);
                return status;
        }
-       TALLOC_FREE(lck);
        return close_file(req, fsp, NORMAL_CLOSE);
 }
 
@@ -7993,10 +8139,9 @@ static void call_trans2setfilepathinfo(connection_struct *conn,
                }
                info_level = SVAL(params,2);
 
-               status = copy_smb_filename(talloc_tos(), fsp->fsp_name,
-                                          &smb_fname);
-               if (!NT_STATUS_IS_OK(status)) {
-                       reply_nterror(req, status);
+               smb_fname = cp_smb_filename(talloc_tos(), fsp->fsp_name);
+               if (smb_fname == NULL) {
+                       reply_nterror(req, NT_STATUS_NO_MEMORY);
                        return;
                }
 
@@ -8039,7 +8184,7 @@ static void call_trans2setfilepathinfo(connection_struct *conn,
                                         fsp_str_dbg(fsp)));
 
                                SSVAL(params,0,0);
-                               send_trans2_replies(conn, req, params, 2,
+                               send_trans2_replies(conn, req, NT_STATUS_OK, params, 2,
                                                    *ppdata, 0,
                                                    max_data_bytes);
                                return;
@@ -8162,11 +8307,20 @@ static void call_trans2setfilepathinfo(connection_struct *conn,
                        return;
                }
 
-               reply_nterror(req, status);
+               /*
+                * Invalid EA name needs to return 2 param bytes,
+                * not a zero-length error packet.
+                */
+               if (NT_STATUS_EQUAL(status, STATUS_INVALID_EA_NAME)) {
+                       send_trans2_replies(conn, req, status, params, 2, NULL, 0,
+                                       max_data_bytes);
+               } else {
+                       reply_nterror(req, status);
+               }
                return;
        }
 
-       send_trans2_replies(conn, req, params, 2, *ppdata, data_return_size,
+       send_trans2_replies(conn, req, NT_STATUS_OK, params, 2, *ppdata, data_return_size,
                            max_data_bytes);
 
        return;
@@ -8291,7 +8445,7 @@ static void call_trans2mkdir(connection_struct *conn, struct smb_request *req,
 
        SSVAL(params,0,0);
 
-       send_trans2_replies(conn, req, params, 2, *ppdata, 0, max_data_bytes);
+       send_trans2_replies(conn, req, NT_STATUS_OK, params, 2, *ppdata, 0, max_data_bytes);
 
  out:
        TALLOC_FREE(smb_dname);
@@ -8346,7 +8500,7 @@ static void call_trans2findnotifyfirst(connection_struct *conn,
        if(fnf_handle == 0)
                fnf_handle = 257;
 
-       send_trans2_replies(conn, req, params, 6, *ppdata, 0, max_data_bytes);
+       send_trans2_replies(conn, req, NT_STATUS_OK, params, 6, *ppdata, 0, max_data_bytes);
 
        return;
 }
@@ -8377,7 +8531,7 @@ static void call_trans2findnotifynext(connection_struct *conn,
        SSVAL(params,0,0); /* No changes */
        SSVAL(params,2,0); /* No EA errors */
 
-       send_trans2_replies(conn, req, params, 4, *ppdata, 0, max_data_bytes);
+       send_trans2_replies(conn, req, NT_STATUS_OK, params, 4, *ppdata, 0, max_data_bytes);
 
        return;
 }
@@ -8427,7 +8581,7 @@ static void call_trans2getdfsreferral(connection_struct *conn,
 
        SSVAL((discard_const_p(uint8_t, req->inbuf)), smb_flg2,
              SVAL(req->inbuf,smb_flg2) | FLAGS2_DFS_PATHNAMES);
-       send_trans2_replies(conn, req,0,0,*ppdata,reply_size, max_data_bytes);
+       send_trans2_replies(conn, req, NT_STATUS_OK, 0,0,*ppdata,reply_size, max_data_bytes);
 
        return;
 }
@@ -8476,7 +8630,7 @@ static void call_trans2ioctl(connection_struct *conn,
                srvstr_push(pdata, req->flags2, pdata+18,
                            lp_servicename(talloc_tos(), SNUM(conn)), 13,
                            STR_ASCII|STR_TERMINATE); /* Service name */
-               send_trans2_replies(conn, req, *pparams, 0, *ppdata, 32,
+               send_trans2_replies(conn, req, NT_STATUS_OK, *pparams, 0, *ppdata, 32,
                                    max_data_bytes);
                return;
        }