s3:smbd: add support for marshalling SMB2 FileFullEaInformation
[samba.git] / source3 / smbd / trans2.c
index c9e7e4bbfbd74cb1b399c24b3049231197c15267..6554fb67b75677da095908aca268522e080498f5 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;
@@ -3878,21 +3941,21 @@ static void call_trans2qpipeinfo(connection_struct *conn,
        return;
 }
 
-static NTSTATUS smbd_do_qfilepathinfo(connection_struct *conn,
-                                     TALLOC_CTX *mem_ctx,
-                                     uint16_t info_level,
-                                     files_struct *fsp,
-                                     const struct smb_filename *smb_fname,
-                                     bool delete_pending,
-                                     struct timespec write_time_ts,
-                                     bool ms_dfs_link,
-                                     struct ea_list *ea_list,
-                                     int lock_data_count,
-                                     char *lock_data,
-                                     uint16_t flags2,
-                                     unsigned int max_data_bytes,
-                                     char **ppdata,
-                                     unsigned int *pdata_size)
+NTSTATUS smbd_do_qfilepathinfo(connection_struct *conn,
+                              TALLOC_CTX *mem_ctx,
+                              uint16_t info_level,
+                              files_struct *fsp,
+                              const struct smb_filename *smb_fname,
+                              bool delete_pending,
+                              struct timespec write_time_ts,
+                              bool ms_dfs_link,
+                              struct ea_list *ea_list,
+                              int lock_data_count,
+                              char *lock_data,
+                              uint16_t flags2,
+                              unsigned int max_data_bytes,
+                              char **ppdata,
+                              unsigned int *pdata_size)
 {
        char *pdata = *ppdata;
        char *dstart, *dend;
@@ -3910,6 +3973,8 @@ static NTSTATUS smbd_do_qfilepathinfo(connection_struct *conn,
        uint64_t file_size = 0;
        uint64_t pos = 0;
        uint64_t allocation_size = 0;
+       uint64_t file_index = 0;
+       uint32_t access_mask = 0;
 
        sbuf = smb_fname->st;
 
@@ -4013,6 +4078,21 @@ static NTSTATUS smbd_do_qfilepathinfo(connection_struct *conn,
                pos = fsp->fh->position_information;
        }
 
+       if (fsp) {
+               access_mask = fsp->access_mask;
+       } else {
+               /* GENERIC_EXECUTE mapping from Windows */
+               access_mask = 0x12019F;
+       }
+
+       /* This should be an index number - looks like
+          dev/ino to me :-)
+
+          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 */
+
        switch (info_level) {
                case SMB_INFO_STANDARD:
                        DEBUG(10,("smbd_do_qfilepathinfo: SMB_INFO_STANDARD\n"));
@@ -4088,6 +4168,35 @@ static 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:
 
@@ -4217,27 +4326,52 @@ static NTSTATUS smbd_do_qfilepathinfo(connection_struct *conn,
                        data_size = PTR_DIFF(pdata,(*ppdata));
                        break;
                }
-               case SMB_FILE_INTERNAL_INFORMATION:
-                       /* This should be an index number - looks like
-                          dev/ino to me :-) 
 
-                          I think this causes us to fail the IFSKIT
-                          BasicFileInformationTest. -tpot */
+               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"));
-                       SIVAL(pdata,0,sbuf.st_ex_ino); /* FileIndexLow */
-                       SIVAL(pdata,4,sbuf.st_ex_dev); /* FileIndexHigh */
+                       SBVAL(pdata, 0, file_index);
                        data_size = 8;
                        break;
 
                case SMB_FILE_ACCESS_INFORMATION:
                        DEBUG(10,("smbd_do_qfilepathinfo: SMB_FILE_ACCESS_INFORMATION\n"));
-                       if (fsp) {
-                               SIVAL(pdata,0,fsp->access_mask);
-                       } else {
-                               /* GENERIC_EXECUTE mapping from Windows */
-                               SIVAL(pdata,0,0x12019F);
-                       }
+                       SIVAL(pdata, 0, access_mask);
                        data_size = 4;
                        break;
 
@@ -4932,6 +5066,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,