Fix coverity CID 930. Pointer check can never be null here.
[ira/wip.git] / source3 / smbd / trans2.c
index 29abbfe3f5d62ec83d2d7702362865c1fa45f66f..cb76deb90ae31bd3ebd0f51c6a6459d12415063f 100644 (file)
@@ -367,6 +367,69 @@ static unsigned int fill_ea_buffer(TALLOC_CTX *mem_ctx, char *pdata, unsigned in
        return ret_data_size;
 }
 
+static NTSTATUS fill_ea_chained_buffer(TALLOC_CTX *mem_ctx,
+                                      char *pdata,
+                                      unsigned int total_data_size,
+                                      unsigned int *ret_data_size,
+                                      connection_struct *conn,
+                                      struct ea_list *ea_list)
+{
+       uint8_t *p = (uint8_t *)pdata;
+       uint8_t *last_start = NULL;
+
+       *ret_data_size = 0;
+
+       if (!lp_ea_support(SNUM(conn))) {
+               return NT_STATUS_NO_EAS_ON_FILE;
+       }
+
+       for (; ea_list; ea_list = ea_list->next) {
+               size_t dos_namelen;
+               fstring dos_ea_name;
+               size_t this_size;
+
+               if (last_start) {
+                       SIVAL(last_start, 0, PTR_DIFF(p, last_start));
+               }
+               last_start = p;
+
+               push_ascii_fstring(dos_ea_name, ea_list->ea.name);
+               dos_namelen = strlen(dos_ea_name);
+               if (dos_namelen > 255 || dos_namelen == 0) {
+                       return NT_STATUS_INTERNAL_ERROR;
+               }
+               if (ea_list->ea.value.length > 65535) {
+                       return NT_STATUS_INTERNAL_ERROR;
+               }
+
+               this_size = 0x08 + dos_namelen + 1 + ea_list->ea.value.length;
+
+               if (ea_list->next) {
+                       size_t pad = 4 - (this_size % 4);
+                       this_size += pad;
+               }
+
+               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);
+               fstrcpy((char *)(p+0x08), dos_ea_name);
+               memcpy(p + 0x08 + dos_namelen + 1, ea_list->ea.value.data, ea_list->ea.value.length);
+
+               total_data_size -= this_size;
+               p += this_size;
+       }
+
+       *ret_data_size = PTR_DIFF(p, pdata);
+       DEBUG(10,("fill_ea_chained_buffer: data_size = %u\n", *ret_data_size));
+       return NT_STATUS_OK;
+}
+
 static unsigned int estimate_ea_size(connection_struct *conn, files_struct *fsp, const char *fname)
 {
        size_t total_ea_len = 0;
@@ -2624,67 +2687,37 @@ static void samba_extended_info_version(struct smb_extended_info *extended_info)
                  "%s", samba_version_string());
 }
 
-/****************************************************************************
- Reply to a TRANS2_QFSINFO (query filesystem info).
-****************************************************************************/
-
-static void call_trans2qfsinfo(connection_struct *conn,
-                              struct smb_request *req,
-                              char **pparams, int total_params,
-                              char **ppdata, int total_data,
-                              unsigned int max_data_bytes)
+NTSTATUS smbd_do_qfsinfo(connection_struct *conn,
+                        TALLOC_CTX *mem_ctx,
+                        uint16_t info_level,
+                        SMB_STRUCT_STAT st,
+                        uint16_t flags2,
+                        unsigned int max_data_bytes,
+                        char **ppdata,
+                        int *ret_data_len)
 {
        char *pdata, *end_data;
-       char *params = *pparams;
-       uint16 info_level;
-       int data_len, len;
-       SMB_STRUCT_STAT st;
+       int data_len = 0, len;
        const char *vname = volume_label(SNUM(conn));
        int snum = SNUM(conn);
        char *fstype = lp_fstype(SNUM(conn));
        uint32 additional_flags = 0;
 
-       if (total_params < 2) {
-               reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
-               return;
-       }
-
-       info_level = SVAL(params,0);
-
        if (IS_IPC(conn)) {
                if (info_level != SMB_QUERY_CIFS_UNIX_INFO) {
-                       DEBUG(0,("call_trans2qfsinfo: not an allowed "
+                       DEBUG(0,("smbd_do_qfsinfo: not an allowed "
                                "info level (0x%x) on IPC$.\n",
                                (unsigned int)info_level));
-                       reply_nterror(req, NT_STATUS_ACCESS_DENIED);
-                       return;
-               }
-       }
-
-       if (ENCRYPTION_REQUIRED(conn) && !req->encrypted) {
-               if (info_level != SMB_QUERY_CIFS_UNIX_INFO) {
-                       DEBUG(0,("call_trans2qfsinfo: encryption required "
-                               "and info level 0x%x sent.\n",
-                               (unsigned int)info_level));
-                       exit_server_cleanly("encryption required "
-                               "on connection");
-                       return;
+                       return NT_STATUS_ACCESS_DENIED;
                }
        }
 
-       DEBUG(3,("call_trans2qfsinfo: level = %d\n", info_level));
-
-       if(vfs_stat_smb_fname(conn,".",&st)!=0) {
-               DEBUG(2,("call_trans2qfsinfo: stat of . failed (%s)\n", strerror(errno)));
-               reply_doserror(req, ERRSRV, ERRinvdevice);
-               return;
-       }
+       DEBUG(3,("smbd_do_qfsinfo: level = %d\n", info_level));
 
        *ppdata = (char *)SMB_REALLOC(
                *ppdata, max_data_bytes + DIR_ENTRY_SAFETY_MARGIN);
-       if (*ppdata == NULL ) {
-               reply_nterror(req, NT_STATUS_NO_MEMORY);
-               return;
+       if (*ppdata == NULL) {
+               return NT_STATUS_NO_MEMORY;
        }
 
        pdata = *ppdata;
@@ -2697,8 +2730,7 @@ static void call_trans2qfsinfo(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) {
-                               reply_nterror(req, map_nt_error_from_unix(errno));
-                               return;
+                               return map_nt_error_from_unix(errno);
                        }
 
                        block_size = lp_block_size(snum);
@@ -2717,7 +2749,7 @@ static void call_trans2qfsinfo(connection_struct *conn,
                        bytes_per_sector = 512;
                        sectors_per_unit = bsize/bytes_per_sector;
 
-                       DEBUG(5,("call_trans2qfsinfo : SMB_INFO_ALLOCATION id=%x, bsize=%u, cSectorUnit=%u, \
+                       DEBUG(5,("smbd_do_qfsinfo : SMB_INFO_ALLOCATION id=%x, bsize=%u, cSectorUnit=%u, \
 cBytesSector=%u, cUnitTotal=%u, cUnitAvail=%d\n", (unsigned int)st.st_ex_dev, (unsigned int)bsize, (unsigned int)sectors_per_unit,
                                (unsigned int)bytes_per_sector, (unsigned int)dsize, (unsigned int)dfree));
 
@@ -2743,13 +2775,13 @@ cBytesSector=%u, cUnitTotal=%u, cUnitAvail=%d\n", (unsigned int)st.st_ex_dev, (u
                         * the pushed string. The change here was adding the STR_TERMINATE. JRA.
                         */
                        len = srvstr_push(
-                               pdata, req->flags2,
+                               pdata, flags2,
                                pdata+l2_vol_szVolLabel, vname,
                                PTR_DIFF(end_data, pdata+l2_vol_szVolLabel),
                                STR_NOALIGN|STR_TERMINATE);
                        SCVAL(pdata,l2_vol_cch,len);
                        data_len = l2_vol_szVolLabel + len;
-                       DEBUG(5,("call_trans2qfsinfo : time = %x, namelen = %d, name = %s\n",
+                       DEBUG(5,("smbd_do_qfsinfo : time = %x, namelen = %d, name = %s\n",
                                 (unsigned)convert_timespec_to_time_t(st.st_ex_ctime),
                                 len, vname));
                        break;
@@ -2776,7 +2808,7 @@ cBytesSector=%u, cUnitTotal=%u, cUnitAvail=%d\n", (unsigned int)st.st_ex_dev, (u
                        SIVAL(pdata,4,255); /* Max filename component length */
                        /* NOTE! the fstype must *not* be null terminated or win98 won't recognise it
                                and will think we can't do long filenames */
-                       len = srvstr_push(pdata, req->flags2, pdata+12, fstype,
+                       len = srvstr_push(pdata, flags2, pdata+12, fstype,
                                          PTR_DIFF(end_data, pdata+12),
                                          STR_UNICODE);
                        SIVAL(pdata,8,len);
@@ -2785,7 +2817,7 @@ cBytesSector=%u, cUnitTotal=%u, cUnitAvail=%d\n", (unsigned int)st.st_ex_dev, (u
 
                case SMB_QUERY_FS_LABEL_INFO:
                case SMB_FS_LABEL_INFORMATION:
-                       len = srvstr_push(pdata, req->flags2, pdata+4, vname,
+                       len = srvstr_push(pdata, flags2, pdata+4, vname,
                                          PTR_DIFF(end_data, pdata+4), 0);
                        data_len = 4 + len;
                        SIVAL(pdata,0,len);
@@ -2802,13 +2834,13 @@ cBytesSector=%u, cUnitTotal=%u, cUnitAvail=%d\n", (unsigned int)st.st_ex_dev, (u
                                (str_checksum(get_local_machine_name())<<16));
 
                        /* Max label len is 32 characters. */
-                       len = srvstr_push(pdata, req->flags2, pdata+18, vname,
+                       len = srvstr_push(pdata, flags2, pdata+18, vname,
                                          PTR_DIFF(end_data, pdata+18),
                                          STR_UNICODE);
                        SIVAL(pdata,12,len);
                        data_len = 18+len;
 
-                       DEBUG(5,("call_trans2qfsinfo : SMB_QUERY_FS_VOLUME_INFO namelen = %d, vol=%s serv=%s\n", 
+                       DEBUG(5,("smbd_do_qfsinfo : SMB_QUERY_FS_VOLUME_INFO namelen = %d, vol=%s serv=%s\n",
                                (int)strlen(vname),vname, lp_servicename(snum)));
                        break;
 
@@ -2818,8 +2850,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) {
-                               reply_nterror(req, map_nt_error_from_unix(errno));
-                               return;
+                               return map_nt_error_from_unix(errno);
                        }
                        block_size = lp_block_size(snum);
                        if (bsize < block_size) {
@@ -2836,7 +2867,7 @@ cBytesSector=%u, cUnitTotal=%u, cUnitAvail=%d\n", (unsigned int)st.st_ex_dev, (u
                        }
                        bytes_per_sector = 512;
                        sectors_per_unit = bsize/bytes_per_sector;
-                       DEBUG(5,("call_trans2qfsinfo : SMB_QUERY_FS_SIZE_INFO bsize=%u, cSectorUnit=%u, \
+                       DEBUG(5,("smbd_do_qfsinfo : SMB_QUERY_FS_SIZE_INFO bsize=%u, cSectorUnit=%u, \
 cBytesSector=%u, cUnitTotal=%u, cUnitAvail=%d\n", (unsigned int)bsize, (unsigned int)sectors_per_unit,
                                (unsigned int)bytes_per_sector, (unsigned int)dsize, (unsigned int)dfree));
                        SBIG_UINT(pdata,0,dsize);
@@ -2851,8 +2882,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) {
-                               reply_nterror(req, map_nt_error_from_unix(errno));
-                               return;
+                               return map_nt_error_from_unix(errno);
                        }
                        block_size = lp_block_size(snum);
                        if (bsize < block_size) {
@@ -2869,7 +2899,7 @@ cBytesSector=%u, cUnitTotal=%u, cUnitAvail=%d\n", (unsigned int)bsize, (unsigned
                        }
                        bytes_per_sector = 512;
                        sectors_per_unit = bsize/bytes_per_sector;
-                       DEBUG(5,("call_trans2qfsinfo : SMB_QUERY_FS_FULL_SIZE_INFO bsize=%u, cSectorUnit=%u, \
+                       DEBUG(5,("smbd_do_qfsinfo : SMB_QUERY_FS_FULL_SIZE_INFO bsize=%u, cSectorUnit=%u, \
 cBytesSector=%u, cUnitTotal=%u, cUnitAvail=%d\n", (unsigned int)bsize, (unsigned int)sectors_per_unit,
                                (unsigned int)bytes_per_sector, (unsigned int)dsize, (unsigned int)dfree));
                        SBIG_UINT(pdata,0,dsize); /* Total Allocation units. */
@@ -2922,24 +2952,23 @@ cBytesSector=%u, cUnitTotal=%u, cUnitAvail=%d\n", (unsigned int)bsize, (unsigned
                        fsp.fnum = -1;
 
                        /* access check */
-                       if (conn->server_info->utok.uid != 0) {
+                       if (conn->server_info->utok.uid != sec_initial_uid()) {
                                DEBUG(0,("set_user_quota: access_denied "
                                         "service [%s] user [%s]\n",
                                         lp_servicename(SNUM(conn)),
                                         conn->server_info->unix_name));
-                               reply_doserror(req, ERRDOS, ERRnoaccess);
-                               return;
+                               return NT_STATUS_ACCESS_DENIED;
                        }
 
                        if (vfs_get_ntquota(&fsp, SMB_USER_FS_QUOTA_TYPE, NULL, &quotas)!=0) {
                                DEBUG(0,("vfs_get_ntquota() failed for service [%s]\n",lp_servicename(SNUM(conn))));
-                               reply_doserror(req, ERRSRV, ERRerror);
-                               return;
+                               return map_nt_error_from_unix(errno);
                        }
 
                        data_len = 48;
 
-                       DEBUG(10,("SMB_FS_QUOTA_INFORMATION: for service [%s]\n",lp_servicename(SNUM(conn))));          
+                       DEBUG(10,("SMB_FS_QUOTA_INFORMATION: for service [%s]\n",
+                                 lp_servicename(SNUM(conn))));
 
                        /* Unknown1 24 NULL bytes*/
                        SBIG_UINT(pdata,0,(uint64_t)0);
@@ -2990,8 +3019,7 @@ cBytesSector=%u, cUnitTotal=%u, cUnitAvail=%d\n", (unsigned int)bsize, (unsigned
                        int encrypt_caps = 0;
 
                        if (!lp_unix_extensions()) {
-                               reply_nterror(req, NT_STATUS_INVALID_LEVEL);
-                               return;
+                               return NT_STATUS_INVALID_LEVEL;
                        }
 
                        switch (conn->encrypt_level) {
@@ -3036,8 +3064,7 @@ cBytesSector=%u, cUnitTotal=%u, cUnitAvail=%d\n", (unsigned int)bsize, (unsigned
                        vfs_statvfs_struct svfs;
 
                        if (!lp_unix_extensions()) {
-                               reply_nterror(req, NT_STATUS_INVALID_LEVEL);
-                               return;
+                               return NT_STATUS_INVALID_LEVEL;
                        }
 
                        rc = SMB_VFS_STATVFS(conn, ".", &svfs);
@@ -3052,16 +3079,14 @@ cBytesSector=%u, cUnitTotal=%u, cUnitAvail=%d\n", (unsigned int)bsize, (unsigned
                                SBIG_UINT(pdata,32,svfs.TotalFileNodes);
                                SBIG_UINT(pdata,40,svfs.FreeFileNodes);
                                SBIG_UINT(pdata,48,svfs.FsIdentifier);
-                               DEBUG(5,("call_trans2qfsinfo : SMB_QUERY_POSIX_FS_INFO succsessful\n"));
+                               DEBUG(5,("smbd_do_qfsinfo : SMB_QUERY_POSIX_FS_INFO succsessful\n"));
 #ifdef EOPNOTSUPP
                        } else if (rc == EOPNOTSUPP) {
-                               reply_nterror(req, NT_STATUS_INVALID_LEVEL);
-                               return;
+                               return NT_STATUS_INVALID_LEVEL;
 #endif /* EOPNOTSUPP */
                        } else {
                                DEBUG(0,("vfs_statvfs() failed for service [%s]\n",lp_servicename(SNUM(conn))));
-                               reply_doserror(req, ERRSRV, ERRerror);
-                               return;
+                               return NT_STATUS_DOS(ERRSRV, ERRerror);
                        }
                        break;
                }
@@ -3073,13 +3098,11 @@ cBytesSector=%u, cUnitTotal=%u, cUnitAvail=%d\n", (unsigned int)bsize, (unsigned
                        int i;
 
                        if (!lp_unix_extensions()) {
-                               reply_nterror(req, NT_STATUS_INVALID_LEVEL);
-                               return;
+                               return NT_STATUS_INVALID_LEVEL;
                        }
 
                        if (max_data_bytes < 40) {
-                               reply_nterror(req, NT_STATUS_BUFFER_TOO_SMALL);
-                               return;
+                               return NT_STATUS_BUFFER_TOO_SMALL;
                        }
 
                        /* We ARE guest if global_sid_Builtin_Guests is
@@ -3193,12 +3216,66 @@ cBytesSector=%u, cUnitTotal=%u, cUnitAvail=%d\n", (unsigned int)bsize, (unsigned
                        }
                        /* drop through */
                default:
-                       reply_nterror(req, NT_STATUS_INVALID_LEVEL);
+                       return NT_STATUS_INVALID_LEVEL;
+       }
+
+       *ret_data_len = data_len;
+       return NT_STATUS_OK;
+}
+
+/****************************************************************************
+ Reply to a TRANS2_QFSINFO (query filesystem info).
+****************************************************************************/
+
+static void call_trans2qfsinfo(connection_struct *conn,
+                              struct smb_request *req,
+                              char **pparams, int total_params,
+                              char **ppdata, int total_data,
+                              unsigned int max_data_bytes)
+{
+       char *params = *pparams;
+       uint16_t info_level;
+       int data_len = 0;
+       SMB_STRUCT_STAT st;
+       NTSTATUS status;
+
+       if (total_params < 2) {
+               reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
+               return;
+       }
+
+       info_level = SVAL(params,0);
+
+       if (ENCRYPTION_REQUIRED(conn) && !req->encrypted) {
+               if (info_level != SMB_QUERY_CIFS_UNIX_INFO) {
+                       DEBUG(0,("call_trans2qfsinfo: encryption required "
+                               "and info level 0x%x sent.\n",
+                               (unsigned int)info_level));
+                       exit_server_cleanly("encryption required "
+                               "on connection");
                        return;
+               }
+       }
+
+       DEBUG(3,("call_trans2qfsinfo: level = %d\n", info_level));
+
+       if(vfs_stat_smb_fname(conn,".",&st)!=0) {
+               DEBUG(2,("call_trans2qfsinfo: stat of . failed (%s)\n", strerror(errno)));
+               reply_doserror(req, ERRSRV, ERRinvdevice);
+               return;
        }
 
+       status = smbd_do_qfsinfo(conn, req,
+                                info_level, st,
+                                req->flags2,
+                                max_data_bytes,
+                                ppdata, &data_len);
+       if (!NT_STATUS_IS_OK(status)) {
+               reply_nterror(req, status);
+               return;
+       }
 
-       send_trans2_replies(conn, req, params, 0, pdata, data_len,
+       send_trans2_replies(conn, req, params, 0, *ppdata, data_len,
                            max_data_bytes);
 
        DEBUG( 4, ( "%s info_level = %d\n",
@@ -3369,12 +3446,12 @@ cap_low = 0x%x, cap_high = 0x%x\n",
                                ZERO_STRUCT(quotas);
 
                                /* access check */
-                               if ((conn->server_info->utok.uid != 0)
+                               if ((conn->server_info->utok.uid != sec_initial_uid())
                                    ||!CAN_WRITE(conn)) {
                                        DEBUG(0,("set_user_quota: access_denied service [%s] user [%s]\n",
                                                 lp_servicename(SNUM(conn)),
                                                 conn->server_info->unix_name));
-                                       reply_doserror(req, ERRSRV, ERRaccess);
+                                       reply_nterror(req, NT_STATUS_ACCESS_DENIED);
                                        return;
                                }
 
@@ -3443,7 +3520,7 @@ cap_low = 0x%x, cap_high = 0x%x\n",
                                /* now set the quotas */
                                if (vfs_set_ntquota(fsp, SMB_USER_FS_QUOTA_TYPE, NULL, &quotas)!=0) {
                                        DEBUG(0,("vfs_set_ntquota() failed for service [%s]\n",lp_servicename(SNUM(conn))));
-                                       reply_doserror(req, ERRSRV, ERRerror);
+                                       reply_nterror(req, map_nt_error_from_unix(errno));
                                        return;
                                }
 
@@ -4028,7 +4105,7 @@ NTSTATUS smbd_do_qfilepathinfo(connection_struct *conn,
           I think this causes us to fail the IFSKIT
           BasicFileInformationTest. -tpot */
        file_index =  ((sbuf.st_ex_ino) & UINT32_MAX); /* FileIndexLow */
-       file_index |= ((sbuf.st_ex_dev) & UINT32_MAX) << 32; /* FileIndexHigh */
+       file_index |= ((uint64_t)((sbuf.st_ex_dev) & UINT32_MAX)) << 32; /* FileIndexHigh */
 
        switch (info_level) {
                case SMB_INFO_STANDARD:
@@ -4105,6 +4182,35 @@ NTSTATUS smbd_do_qfilepathinfo(connection_struct *conn,
                        break;
                }
 
+               case 0xFF0F:/*SMB2_INFO_QUERY_ALL_EAS*/
+               {
+                       /* We have data_size bytes to put EA's into. */
+                       size_t total_ea_len = 0;
+                       struct ea_list *ea_file_list = NULL;
+
+                       DEBUG(10,("smbd_do_qfilepathinfo: SMB2_INFO_QUERY_ALL_EAS\n"));
+
+                       /*TODO: add filtering and index handling */
+
+                       ea_file_list = get_ea_list_from_file(mem_ctx,
+                                                            conn, fsp,
+                                                            fname,
+                                                            &total_ea_len);
+                       if (!ea_file_list) {
+                               return NT_STATUS_NO_EAS_ON_FILE;
+                       }
+
+                       status = fill_ea_chained_buffer(mem_ctx,
+                                                       pdata,
+                                                       data_size,
+                                                       &data_size,
+                                                       conn, ea_file_list);
+                       if (!NT_STATUS_IS_OK(status)) {
+                               return status;
+                       }
+                       break;
+               }
+
                case SMB_FILE_BASIC_INFORMATION:
                case SMB_QUERY_FILE_BASIC_INFO:
 
@@ -4234,6 +4340,42 @@ NTSTATUS smbd_do_qfilepathinfo(connection_struct *conn,
                        data_size = PTR_DIFF(pdata,(*ppdata));
                        break;
                }
+
+               case 0xFF12:/*SMB2_FILE_ALL_INFORMATION*/
+               {
+                       int len;
+                       unsigned int ea_size = estimate_ea_size(conn, fsp, fname);
+                       DEBUG(10,("smbd_do_qfilepathinfo: SMB2_FILE_ALL_INFORMATION\n"));
+                       put_long_date_timespec(pdata+0x00,create_time_ts);
+                       put_long_date_timespec(pdata+0x08,atime_ts);
+                       put_long_date_timespec(pdata+0x10,mtime_ts); /* write time */
+                       put_long_date_timespec(pdata+0x18,mtime_ts); /* change time */
+                       SIVAL(pdata,    0x20, mode);
+                       SIVAL(pdata,    0x24, 0); /* padding. */
+                       SBVAL(pdata,    0x28, allocation_size);
+                       SBVAL(pdata,    0x30, file_size);
+                       SIVAL(pdata,    0x38, nlink);
+                       SCVAL(pdata,    0x3C, delete_pending);
+                       SCVAL(pdata,    0x3D, (mode&aDIR)?1:0);
+                       SSVAL(pdata,    0x3E, 0); /* padding */
+                       SBVAL(pdata,    0x40, file_index);
+                       SIVAL(pdata,    0x48, ea_size);
+                       SIVAL(pdata,    0x4C, access_mask);
+                       SBVAL(pdata,    0x50, pos);
+                       SIVAL(pdata,    0x58, mode); /*TODO: mode != mode fix this!!! */
+                       SIVAL(pdata,    0x5C, 0); /* No alignment needed. */
+
+                       pdata += 0x60;
+
+                       len = srvstr_push(dstart, flags2,
+                                         pdata+4, dos_fname,
+                                         PTR_DIFF(dend, pdata+4),
+                                         STR_UNICODE);
+                       SIVAL(pdata,0,len);
+                       pdata += 4 + len;
+                       data_size = PTR_DIFF(pdata,(*ppdata));
+                       break;
+               }
                case SMB_FILE_INTERNAL_INFORMATION:
 
                        DEBUG(10,("smbd_do_qfilepathinfo: SMB_FILE_INTERNAL_INFORMATION\n"));
@@ -4664,7 +4806,7 @@ static void call_trans2qfilepathinfo(connection_struct *conn,
 
                        /* We know this name is ok, it's already passed the checks. */
 
-               } else if(fsp && (fsp->is_directory || fsp->fh->fd == -1)) {
+               } else if(fsp->is_directory || fsp->fh->fd == -1) {
                        /*
                         * This is actually a QFILEINFO on a directory
                         * handle (returned from an NT SMB). NT5.0 seems
@@ -4938,6 +5080,15 @@ total_data=%u (should be %u)\n", (unsigned int)total_data, (unsigned int)IVAL(pd
                break;
        }
 
+       if ((info_level & 0xFF00) == 0xFF00) {
+               /*
+                * We use levels that start with 0xFF00
+                * internally to represent SMB2 specific levels
+                */
+               reply_nterror(req, NT_STATUS_INVALID_LEVEL);
+               return;
+       }
+
        status = smbd_do_qfilepathinfo(conn, req, info_level,
                                       fsp, smb_fname,
                                       delete_pending, write_time_ts,
@@ -6918,211 +7069,56 @@ static NTSTATUS smb_posix_unlink(connection_struct *conn,
        return close_file(req, fsp, NORMAL_CLOSE);
 }
 
-/****************************************************************************
- Reply to a TRANS2_SETFILEINFO (set file info by fileid or pathname).
-****************************************************************************/
-
-static void call_trans2setfilepathinfo(connection_struct *conn,
-                                      struct smb_request *req,
-                                      unsigned int tran_call,
-                                      char **pparams, int total_params,
-                                      char **ppdata, int total_data,
-                                      unsigned int max_data_bytes)
+NTSTATUS smbd_do_setfilepathinfo(connection_struct *conn,
+                               struct smb_request *req,
+                               TALLOC_CTX *mem_ctx,
+                               uint16_t info_level,
+                               files_struct *fsp,
+                               struct smb_filename *smb_fname,
+                               char **ppdata, int total_data,
+                               int *ret_data_size)
 {
-       char *params = *pparams;
        char *pdata = *ppdata;
-       uint16 info_level;
        SMB_STRUCT_STAT sbuf;
        char *fname = NULL;
-       struct smb_filename *smb_fname = NULL;
-       files_struct *fsp = NULL;
        NTSTATUS status = NT_STATUS_OK;
        int data_return_size = 0;
-       TALLOC_CTX *ctx = talloc_tos();
 
-       if (!params) {
-               reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
-               return;
+       *ret_data_size = 0;
+
+       /* Set sbuf for use below. */
+       sbuf = smb_fname->st;
+
+       if (INFO_LEVEL_IS_UNIX(info_level) && !lp_unix_extensions()) {
+               return NT_STATUS_INVALID_LEVEL;
        }
 
-       if (tran_call == TRANSACT2_SETFILEINFO) {
-               if (total_params < 4) {
-                       reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
-                       return;
+       if (!CAN_WRITE(conn)) {
+               /* Allow POSIX opens. The open path will deny
+                * any non-readonly opens. */
+               if (info_level != SMB_POSIX_PATH_OPEN) {
+                       return NT_STATUS_DOS(ERRSRV, ERRaccess);
                }
+       }
 
-               fsp = file_fsp(req, SVAL(params,0));
-               /* Basic check for non-null fsp. */
-               if (!check_fsp_open(conn, req, fsp)) {
-                       return;
-               }
-               info_level = SVAL(params,2);
+       status = get_full_smb_filename(mem_ctx, smb_fname, &fname);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
 
-               fname = talloc_strdup(talloc_tos(),fsp->fsp_name);
-               if (!fname) {
-                       reply_nterror(req, NT_STATUS_NO_MEMORY);
-                       return;
-               }
+       DEBUG(3,("smbd_do_setfilepathinfo: %s (fnum %d) info_level=%d totdata=%d\n",
+               fname, fsp ? fsp->fnum : -1, info_level, total_data));
 
-               status = create_synthetic_smb_fname_split(talloc_tos(), fname,
-                                                         NULL, &smb_fname);
-               if (!NT_STATUS_IS_OK(status)) {
-                       reply_nterror(req, status);
-                       return;
-               }
-
-               if(fsp->is_directory || fsp->fh->fd == -1) {
-                       /*
-                        * This is actually a SETFILEINFO on a directory
-                        * handle (returned from an NT SMB). NT5.0 seems
-                        * to do this call. JRA.
-                        */
-                       if (INFO_LEVEL_IS_UNIX(info_level)) {
-                               /* Always do lstat for UNIX calls. */
-                               if (SMB_VFS_LSTAT(conn, smb_fname)) {
-                                       DEBUG(3,("call_trans2setfilepathinfo: "
-                                                "SMB_VFS_LSTAT of %s failed "
-                                                "(%s)\n",
-                                                smb_fname_str_dbg(smb_fname),
-                                                strerror(errno)));
-                                       reply_nterror(req, map_nt_error_from_unix(errno));
-                                       return;
-                               }
-                       } else {
-                               if (SMB_VFS_STAT(conn, smb_fname) != 0) {
-                                       DEBUG(3,("call_trans2setfilepathinfo: "
-                                                "fileinfo of %s failed (%s)\n",
-                                                smb_fname_str_dbg(smb_fname),
-                                                strerror(errno)));
-                                       reply_nterror(req, map_nt_error_from_unix(errno));
-                                       return;
-                               }
-                       }
-               } else if (fsp->print_file) {
-                       /*
-                        * Doing a DELETE_ON_CLOSE should cancel a print job.
-                        */
-                       if ((info_level == SMB_SET_FILE_DISPOSITION_INFO) && CVAL(pdata,0)) {
-                               fsp->fh->private_options |= FILE_DELETE_ON_CLOSE;
-
-                               DEBUG(3,("call_trans2setfilepathinfo: Cancelling print job (%s)\n", fsp->fsp_name ));
-
-                               SSVAL(params,0,0);
-                               send_trans2_replies(conn, req, params, 2,
-                                                   *ppdata, 0,
-                                                   max_data_bytes);
-                               return;
-                       } else {
-                               reply_doserror(req, ERRDOS, ERRbadpath);
-                               return;
-                       }
-               } else {
-                       /*
-                        * Original code - this is an open file.
-                        */
-                       if (!check_fsp(conn, req, fsp)) {
-                               return;
-                       }
-
-                       if (SMB_VFS_FSTAT(fsp, &smb_fname->st) != 0) {
-                               DEBUG(3,("call_trans2setfilepathinfo: fstat "
-                                        "of fnum %d failed (%s)\n", fsp->fnum,
-                                        strerror(errno)));
-                               reply_nterror(req, map_nt_error_from_unix(errno));
-                               return;
-                       }
-               }
-       } else {
-               /* set path info */
-               if (total_params < 7) {
-                       reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
-                       return;
-               }
-
-               info_level = SVAL(params,0);
-               srvstr_get_path(ctx, params, req->flags2, &fname, &params[6],
-                               total_params - 6, STR_TERMINATE,
-                               &status);
-               if (!NT_STATUS_IS_OK(status)) {
-                       reply_nterror(req, status);
-                       return;
-               }
-
-               status = filename_convert(ctx, conn,
-                                        req->flags2 & FLAGS2_DFS_PATHNAMES,
-                                        fname,
-                                        &smb_fname,
-                                        &fname);
-               if (!NT_STATUS_IS_OK(status)) {
-                       if (NT_STATUS_EQUAL(status,NT_STATUS_PATH_NOT_COVERED)) {
-                               reply_botherror(req,
-                                               NT_STATUS_PATH_NOT_COVERED,
-                                               ERRSRV, ERRbadpath);
-                               return;
-                       }
-                       reply_nterror(req, status);
-                       return;
-               }
-
-               if (INFO_LEVEL_IS_UNIX(info_level)) {
-                       /*
-                        * For CIFS UNIX extensions the target name may not exist.
-                        */
-
-                       /* Always do lstat for UNIX calls. */
-                       SMB_VFS_LSTAT(conn, smb_fname);
-
-               } else if (!VALID_STAT(smb_fname->st) &&
-                          SMB_VFS_STAT(conn, smb_fname)) {
-                       DEBUG(3,("call_trans2setfilepathinfo: SMB_VFS_STAT of "
-                                "%s failed (%s)\n",
-                                smb_fname_str_dbg(smb_fname),
-                                strerror(errno)));
-                       reply_nterror(req, map_nt_error_from_unix(errno));
-                       return;
-               }
-       }
-
-       /* Set sbuf for use below. */
-       sbuf = smb_fname->st;
-
-       if (INFO_LEVEL_IS_UNIX(info_level) && !lp_unix_extensions()) {
-               reply_nterror(req, NT_STATUS_INVALID_LEVEL);
-               return;
-       }
-
-       if (!CAN_WRITE(conn)) {
-               /* Allow POSIX opens. The open path will deny
-                * any non-readonly opens. */
-               if (info_level != SMB_POSIX_PATH_OPEN) {
-                       reply_doserror(req, ERRSRV, ERRaccess);
-                       return;
-               }
-       }
-
-       DEBUG(3,("call_trans2setfilepathinfo(%d) %s (fnum %d) info_level=%d totdata=%d\n",
-               tran_call,fname, fsp ? fsp->fnum : -1, info_level,total_data));
-
-       /* Realloc the parameter size */
-       *pparams = (char *)SMB_REALLOC(*pparams,2);
-       if (*pparams == NULL) {
-               reply_nterror(req, NT_STATUS_NO_MEMORY);
-               return;
-       }
-       params = *pparams;
-
-       SSVAL(params,0,0);
-
-       switch (info_level) {
-
-               case SMB_INFO_STANDARD:
-               {
-                       status = smb_set_info_standard(conn,
-                                       pdata,
-                                       total_data,
-                                       fsp,
-                                       smb_fname);
-                       break;
+       switch (info_level) {
+
+               case SMB_INFO_STANDARD:
+               {
+                       status = smb_set_info_standard(conn,
+                                       pdata,
+                                       total_data,
+                                       fsp,
+                                       smb_fname);
+                       break;
                }
 
                case SMB_INFO_SET_EA:
@@ -7237,10 +7233,9 @@ static void call_trans2setfilepathinfo(connection_struct *conn,
 
                case SMB_SET_FILE_UNIX_LINK:
                {
-                       if (tran_call != TRANSACT2_SETPATHINFO) {
+                       if (fsp) {
                                /* We must have a pathname for this. */
-                               reply_nterror(req, NT_STATUS_INVALID_LEVEL);
-                               return;
+                               return NT_STATUS_INVALID_LEVEL;
                        }
                        status = smb_set_file_unix_link(conn, req, pdata,
                                                        total_data, fname);
@@ -7249,10 +7244,9 @@ static void call_trans2setfilepathinfo(connection_struct *conn,
 
                case SMB_SET_FILE_UNIX_HLINK:
                {
-                       if (tran_call != TRANSACT2_SETPATHINFO || smb_fname == NULL) {
+                       if (fsp) {
                                /* We must have a pathname for this. */
-                               reply_nterror(req, NT_STATUS_INVALID_LEVEL);
-                               return;
+                               return NT_STATUS_INVALID_LEVEL;
                        }
                        status = smb_set_file_unix_hlink(conn, req,
                                                         pdata, total_data,
@@ -7283,9 +7277,8 @@ static void call_trans2setfilepathinfo(connection_struct *conn,
 
                case SMB_SET_POSIX_LOCK:
                {
-                       if (tran_call != TRANSACT2_SETFILEINFO) {
-                               reply_nterror(req, NT_STATUS_INVALID_LEVEL);
-                               return;
+                       if (!fsp) {
+                               return NT_STATUS_INVALID_LEVEL;
                        }
                        status = smb_set_posix_lock(conn, req,
                                                    pdata, total_data, fsp);
@@ -7294,10 +7287,9 @@ static void call_trans2setfilepathinfo(connection_struct *conn,
 
                case SMB_POSIX_PATH_OPEN:
                {
-                       if (tran_call != TRANSACT2_SETPATHINFO) {
+                       if (fsp) {
                                /* We must have a pathname for this. */
-                               reply_nterror(req, NT_STATUS_INVALID_LEVEL);
-                               return;
+                               return NT_STATUS_INVALID_LEVEL;
                        }
 
                        status = smb_posix_open(conn, req,
@@ -7311,10 +7303,9 @@ static void call_trans2setfilepathinfo(connection_struct *conn,
 
                case SMB_POSIX_PATH_UNLINK:
                {
-                       if (tran_call != TRANSACT2_SETPATHINFO) {
+                       if (fsp) {
                                /* We must have a pathname for this. */
-                               reply_nterror(req, NT_STATUS_INVALID_LEVEL);
-                               return;
+                               return NT_STATUS_INVALID_LEVEL;
                        }
 
                        status = smb_posix_unlink(conn, req,
@@ -7325,10 +7316,199 @@ static void call_trans2setfilepathinfo(connection_struct *conn,
                }
 
                default:
-                       reply_nterror(req, NT_STATUS_INVALID_LEVEL);
+                       return NT_STATUS_INVALID_LEVEL;
+       }
+
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       *ret_data_size = data_return_size;
+       return NT_STATUS_OK;
+}
+
+/****************************************************************************
+ Reply to a TRANS2_SETFILEINFO (set file info by fileid or pathname).
+****************************************************************************/
+
+static void call_trans2setfilepathinfo(connection_struct *conn,
+                                      struct smb_request *req,
+                                      unsigned int tran_call,
+                                      char **pparams, int total_params,
+                                      char **ppdata, int total_data,
+                                      unsigned int max_data_bytes)
+{
+       char *params = *pparams;
+       char *pdata = *ppdata;
+       uint16 info_level;
+       char *fname = NULL;
+       struct smb_filename *smb_fname = NULL;
+       files_struct *fsp = NULL;
+       NTSTATUS status = NT_STATUS_OK;
+       int data_return_size = 0;
+
+       if (!params) {
+               reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
+               return;
+       }
+
+       if (tran_call == TRANSACT2_SETFILEINFO) {
+               if (total_params < 4) {
+                       reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
+                       return;
+               }
+
+               fsp = file_fsp(req, SVAL(params,0));
+               /* Basic check for non-null fsp. */
+               if (!check_fsp_open(conn, req, fsp)) {
+                       return;
+               }
+               info_level = SVAL(params,2);
+
+               fname = talloc_strdup(talloc_tos(),fsp->fsp_name);
+               if (!fname) {
+                       reply_nterror(req, NT_STATUS_NO_MEMORY);
+                       return;
+               }
+
+               status = create_synthetic_smb_fname_split(talloc_tos(), fname,
+                                                         NULL, &smb_fname);
+               if (!NT_STATUS_IS_OK(status)) {
+                       reply_nterror(req, status);
                        return;
+               }
+
+               if(fsp->is_directory || fsp->fh->fd == -1) {
+                       /*
+                        * This is actually a SETFILEINFO on a directory
+                        * handle (returned from an NT SMB). NT5.0 seems
+                        * to do this call. JRA.
+                        */
+                       if (INFO_LEVEL_IS_UNIX(info_level)) {
+                               /* Always do lstat for UNIX calls. */
+                               if (SMB_VFS_LSTAT(conn, smb_fname)) {
+                                       DEBUG(3,("call_trans2setfilepathinfo: "
+                                                "SMB_VFS_LSTAT of %s failed "
+                                                "(%s)\n",
+                                                smb_fname_str_dbg(smb_fname),
+                                                strerror(errno)));
+                                       reply_nterror(req, map_nt_error_from_unix(errno));
+                                       return;
+                               }
+                       } else {
+                               if (SMB_VFS_STAT(conn, smb_fname) != 0) {
+                                       DEBUG(3,("call_trans2setfilepathinfo: "
+                                                "fileinfo of %s failed (%s)\n",
+                                                smb_fname_str_dbg(smb_fname),
+                                                strerror(errno)));
+                                       reply_nterror(req, map_nt_error_from_unix(errno));
+                                       return;
+                               }
+                       }
+               } else if (fsp->print_file) {
+                       /*
+                        * Doing a DELETE_ON_CLOSE should cancel a print job.
+                        */
+                       if ((info_level == SMB_SET_FILE_DISPOSITION_INFO) && CVAL(pdata,0)) {
+                               fsp->fh->private_options |= FILE_DELETE_ON_CLOSE;
+
+                               DEBUG(3,("call_trans2setfilepathinfo: Cancelling print job (%s)\n", fsp->fsp_name ));
+
+                               SSVAL(params,0,0);
+                               send_trans2_replies(conn, req, params, 2,
+                                                   *ppdata, 0,
+                                                   max_data_bytes);
+                               return;
+                       } else {
+                               reply_doserror(req, ERRDOS, ERRbadpath);
+                               return;
+                       }
+               } else {
+                       /*
+                        * Original code - this is an open file.
+                        */
+                       if (!check_fsp(conn, req, fsp)) {
+                               return;
+                       }
+
+                       if (SMB_VFS_FSTAT(fsp, &smb_fname->st) != 0) {
+                               DEBUG(3,("call_trans2setfilepathinfo: fstat "
+                                        "of fnum %d failed (%s)\n", fsp->fnum,
+                                        strerror(errno)));
+                               reply_nterror(req, map_nt_error_from_unix(errno));
+                               return;
+                       }
+               }
+       } else {
+               /* set path info */
+               if (total_params < 7) {
+                       reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
+                       return;
+               }
+
+               info_level = SVAL(params,0);
+               srvstr_get_path(req, params, req->flags2, &fname, &params[6],
+                               total_params - 6, STR_TERMINATE,
+                               &status);
+               if (!NT_STATUS_IS_OK(status)) {
+                       reply_nterror(req, status);
+                       return;
+               }
+
+               status = filename_convert(req, conn,
+                                        req->flags2 & FLAGS2_DFS_PATHNAMES,
+                                        fname,
+                                        &smb_fname,
+                                        &fname);
+               if (!NT_STATUS_IS_OK(status)) {
+                       if (NT_STATUS_EQUAL(status,NT_STATUS_PATH_NOT_COVERED)) {
+                               reply_botherror(req,
+                                               NT_STATUS_PATH_NOT_COVERED,
+                                               ERRSRV, ERRbadpath);
+                               return;
+                       }
+                       reply_nterror(req, status);
+                       return;
+               }
+
+               if (INFO_LEVEL_IS_UNIX(info_level)) {
+                       /*
+                        * For CIFS UNIX extensions the target name may not exist.
+                        */
+
+                       /* Always do lstat for UNIX calls. */
+                       SMB_VFS_LSTAT(conn, smb_fname);
+
+               } else if (!VALID_STAT(smb_fname->st) &&
+                          SMB_VFS_STAT(conn, smb_fname)) {
+                       DEBUG(3,("call_trans2setfilepathinfo: SMB_VFS_STAT of "
+                                "%s failed (%s)\n",
+                                smb_fname_str_dbg(smb_fname),
+                                strerror(errno)));
+                       reply_nterror(req, map_nt_error_from_unix(errno));
+                       return;
+               }
        }
 
+       DEBUG(3,("call_trans2setfilepathinfo(%d) %s (fnum %d) info_level=%d totdata=%d\n",
+               tran_call, fname, fsp ? fsp->fnum : -1, info_level,total_data));
+
+       /* Realloc the parameter size */
+       *pparams = (char *)SMB_REALLOC(*pparams,2);
+       if (*pparams == NULL) {
+               reply_nterror(req, NT_STATUS_NO_MEMORY);
+               return;
+       }
+       params = *pparams;
+
+       SSVAL(params,0,0);
+
+       status = smbd_do_setfilepathinfo(conn, req, req,
+                                        info_level,
+                                        fsp,
+                                        smb_fname,
+                                        ppdata, total_data,
+                                        &data_return_size);
        if (!NT_STATUS_IS_OK(status)) {
                if (open_was_deferred(req->mid)) {
                        /* We have re-scheduled this call. */
@@ -7352,7 +7532,6 @@ static void call_trans2setfilepathinfo(connection_struct *conn,
                return;
        }
 
-       SSVAL(params,0,0);
        send_trans2_replies(conn, req, params, 2, *ppdata, data_return_size,
                            max_data_bytes);