libsmb: Pass NTTIME to interpret_long_date()
[samba.git] / source3 / libsmb / clirap.c
index 7ba0c90e418c91116a03b1465a3f5e3cce4bbad0..7944eeb847c2e2daf2783f299066812d97ebace4 100644 (file)
@@ -29,6 +29,7 @@
 #include "trans2.h"
 #include "../libcli/smb/smbXcli_base.h"
 #include "cli_smb2_fnum.h"
+#include "lib/util/string_wrappers.h"
 
 #include <gnutls/gnutls.h>
 #include <gnutls/crypto.h>
@@ -551,144 +552,13 @@ done:
        return (cli->rap_error == 0);
 }
 
-/****************************************************************************
- Send a qpathinfo call.
-****************************************************************************/
-
-struct cli_qpathinfo1_state {
-       struct cli_state *cli;
-       uint32_t num_data;
-       uint8_t *data;
-};
-
-static void cli_qpathinfo1_done(struct tevent_req *subreq);
-
-struct tevent_req *cli_qpathinfo1_send(TALLOC_CTX *mem_ctx,
-                                      struct tevent_context *ev,
-                                      struct cli_state *cli,
-                                      const char *fname)
-{
-       struct tevent_req *req = NULL, *subreq = NULL;
-       struct cli_qpathinfo1_state *state = NULL;
-
-       req = tevent_req_create(mem_ctx, &state, struct cli_qpathinfo1_state);
-       if (req == NULL) {
-               return NULL;
-       }
-       state->cli = cli;
-       subreq = cli_qpathinfo_send(state, ev, cli, fname, SMB_INFO_STANDARD,
-                                   22, CLI_BUFFER_SIZE);
-       if (tevent_req_nomem(subreq, req)) {
-               return tevent_req_post(req, ev);
-       }
-       tevent_req_set_callback(subreq, cli_qpathinfo1_done, req);
-       return req;
-}
-
-static void cli_qpathinfo1_done(struct tevent_req *subreq)
-{
-       struct tevent_req *req = tevent_req_callback_data(
-               subreq, struct tevent_req);
-       struct cli_qpathinfo1_state *state = tevent_req_data(
-               req, struct cli_qpathinfo1_state);
-       NTSTATUS status;
-
-       status = cli_qpathinfo_recv(subreq, state, &state->data,
-                                   &state->num_data);
-       TALLOC_FREE(subreq);
-       if (!NT_STATUS_IS_OK(status)) {
-               tevent_req_nterror(req, status);
-               return;
-       }
-       tevent_req_done(req);
-}
-
-NTSTATUS cli_qpathinfo1_recv(struct tevent_req *req,
-                            time_t *change_time,
-                            time_t *access_time,
-                            time_t *write_time,
-                            off_t *size,
-                            uint16_t *pattr)
-{
-       struct cli_qpathinfo1_state *state = tevent_req_data(
-               req, struct cli_qpathinfo1_state);
-       NTSTATUS status;
-
-       time_t (*date_fn)(const void *buf, int serverzone);
-
-       if (tevent_req_is_nterror(req, &status)) {
-               return status;
-       }
-
-       if (state->cli->win95) {
-               date_fn = make_unix_date;
-       } else {
-               date_fn = make_unix_date2;
-       }
-
-       if (change_time) {
-               *change_time = date_fn(state->data+0, smb1cli_conn_server_time_zone(state->cli->conn));
-       }
-       if (access_time) {
-               *access_time = date_fn(state->data+4, smb1cli_conn_server_time_zone(state->cli->conn));
-       }
-       if (write_time) {
-               *write_time = date_fn(state->data+8, smb1cli_conn_server_time_zone(state->cli->conn));
-       }
-       if (size) {
-               *size = IVAL(state->data, 12);
-       }
-       if (pattr) {
-               *pattr = SVAL(state->data, l1_attrFile);
-       }
-       return NT_STATUS_OK;
-}
-
-NTSTATUS cli_qpathinfo1(struct cli_state *cli,
-                       const char *fname,
-                       time_t *change_time,
-                       time_t *access_time,
-                       time_t *write_time,
-                       off_t *size,
-                       uint16_t *pattr)
-{
-       TALLOC_CTX *frame = talloc_stackframe();
-       struct tevent_context *ev;
-       struct tevent_req *req;
-       NTSTATUS status = NT_STATUS_NO_MEMORY;
-
-       if (smbXcli_conn_has_async_calls(cli->conn)) {
-               /*
-                * Can't use sync call while an async call is in flight
-                */
-               status = NT_STATUS_INVALID_PARAMETER;
-               goto fail;
-       }
-       ev = samba_tevent_context_init(frame);
-       if (ev == NULL) {
-               goto fail;
-       }
-       req = cli_qpathinfo1_send(frame, ev, cli, fname);
-       if (req == NULL) {
-               goto fail;
-       }
-       if (!tevent_req_poll_ntstatus(req, ev, &status)) {
-               goto fail;
-       }
-       status = cli_qpathinfo1_recv(req, change_time, access_time,
-                                    write_time, size, pattr);
- fail:
-       TALLOC_FREE(frame);
-       return status;
-}
-
 static void prep_basic_information_buf(
        uint8_t buf[40],
        struct timespec create_time,
        struct timespec access_time,
        struct timespec write_time,
        struct timespec change_time,
-       uint16_t attr)
+       uint32_t attr)
 {
        char *p = (char *)buf;
        /*
@@ -710,7 +580,7 @@ static void prep_basic_information_buf(
                TIMESTAMP_SET_NT_OR_BETTER, p, &change_time);
        p += 8;
 
-       if (attr == (uint16_t)-1 || attr == FILE_ATTRIBUTE_NORMAL) {
+       if (attr == (uint32_t)-1 || attr == FILE_ATTRIBUTE_NORMAL) {
                /* No change. */
                attr = 0;
        } else if (attr == 0) {
@@ -735,7 +605,7 @@ NTSTATUS cli_setpathinfo_ext(struct cli_state *cli, const char *fname,
                             struct timespec access_time,
                             struct timespec write_time,
                             struct timespec change_time,
-                            uint16_t attr)
+                            uint32_t attr)
 {
        uint8_t buf[40];
 
@@ -781,7 +651,7 @@ struct tevent_req *cli_setfileinfo_ext_send(
        struct timespec access_time,
        struct timespec write_time,
        struct timespec change_time,
-       uint16_t attr)
+       uint32_t attr)
 {
        struct tevent_req *req = NULL, *subreq = NULL;
        struct cli_setfileinfo_ext_state *state = NULL;
@@ -860,7 +730,7 @@ NTSTATUS cli_setfileinfo_ext(
        struct timespec access_time,
        struct timespec write_time,
        struct timespec change_time,
-       uint16_t attr)
+       uint32_t attr)
 {
        TALLOC_CTX *frame = NULL;
        struct tevent_context *ev = NULL;
@@ -907,10 +777,16 @@ NTSTATUS cli_setfileinfo_ext(
 ****************************************************************************/
 
 struct cli_qpathinfo2_state {
-       uint32_t num_data;
-       uint8_t *data;
+       struct timespec create_time;
+       struct timespec access_time;
+       struct timespec write_time;
+       struct timespec change_time;
+       off_t size;
+       uint32_t attr;
+       SMB_INO_T ino;
 };
 
+static void cli_qpathinfo2_done2(struct tevent_req *subreq);
 static void cli_qpathinfo2_done(struct tevent_req *subreq);
 
 struct tevent_req *cli_qpathinfo2_send(TALLOC_CTX *mem_ctx,
@@ -925,6 +801,20 @@ struct tevent_req *cli_qpathinfo2_send(TALLOC_CTX *mem_ctx,
        if (req == NULL) {
                return NULL;
        }
+       if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+               subreq = cli_smb2_qpathinfo_send(state,
+                                                ev,
+                                                cli,
+                                                fname,
+                                                FSCC_FILE_ALL_INFORMATION,
+                                                0x60,
+                                                UINT16_MAX);
+               if (tevent_req_nomem(subreq, req)) {
+                       return tevent_req_post(req, ev);
+               }
+               tevent_req_set_callback(subreq, cli_qpathinfo2_done2, req);
+               return req;
+       }
        subreq = cli_qpathinfo_send(state, ev, cli, fname,
                                    SMB_QUERY_FILE_ALL_INFO,
                                    68, CLI_BUFFER_SIZE);
@@ -935,21 +825,66 @@ struct tevent_req *cli_qpathinfo2_send(TALLOC_CTX *mem_ctx,
        return req;
 }
 
+static void cli_qpathinfo2_done2(struct tevent_req *subreq)
+{
+       struct tevent_req *req =
+               tevent_req_callback_data(subreq, struct tevent_req);
+       struct cli_qpathinfo2_state *state =
+               tevent_req_data(req, struct cli_qpathinfo2_state);
+       uint8_t *rdata = NULL;
+       uint32_t num_rdata;
+       NTSTATUS status;
+
+       status = cli_smb2_qpathinfo_recv(subreq, state, &rdata, &num_rdata);
+       TALLOC_FREE(subreq);
+       if (tevent_req_nterror(req, status)) {
+               return;
+       }
+       state->create_time = interpret_long_date(BVAL(rdata, 0x0));
+       state->access_time = interpret_long_date(BVAL(rdata, 0x8));
+       state->write_time = interpret_long_date(BVAL(rdata, 0x10));
+       state->change_time = interpret_long_date(BVAL(rdata, 0x18));
+       state->attr = PULL_LE_U32(rdata, 0x20);
+       state->size = PULL_LE_U64(rdata, 0x30);
+       state->ino = PULL_LE_U64(rdata, 0x40);
+
+       tevent_req_done(req);
+}
+
 static void cli_qpathinfo2_done(struct tevent_req *subreq)
 {
        struct tevent_req *req = tevent_req_callback_data(
                subreq, struct tevent_req);
        struct cli_qpathinfo2_state *state = tevent_req_data(
                req, struct cli_qpathinfo2_state);
+       uint8_t *data = NULL;
+       uint32_t num_data;
        NTSTATUS status;
 
-       status = cli_qpathinfo_recv(subreq, state, &state->data,
-                                   &state->num_data);
+       status = cli_qpathinfo_recv(subreq, state, &data, &num_data);
        TALLOC_FREE(subreq);
-       if (!NT_STATUS_IS_OK(status)) {
-               tevent_req_nterror(req, status);
+       if (tevent_req_nterror(req, status)) {
                return;
        }
+
+       state->create_time = interpret_long_date(BVAL(data, 0));
+       state->access_time = interpret_long_date(BVAL(data, 8));
+       state->write_time = interpret_long_date(BVAL(data, 16));
+       state->change_time = interpret_long_date(BVAL(data, 24));
+       state->attr = PULL_LE_U32(data, 32);
+       state->size = PULL_LE_U64(data, 48);
+
+       /*
+        * SMB1 qpathinfo2 uses SMB_QUERY_FILE_ALL_INFO which doesn't
+        * return an inode number (fileid).  We can't change this to
+        * one of the FILE_ID info levels as only Win2003 and above
+        * support these [MS-SMB: 2.2.2.3.1] and the SMB1 code needs
+        * to support older servers.
+        */
+       state->ino = 0;
+
+       TALLOC_FREE(data);
+
        tevent_req_done(req);
 }
 
@@ -958,7 +893,7 @@ NTSTATUS cli_qpathinfo2_recv(struct tevent_req *req,
                             struct timespec *access_time,
                             struct timespec *write_time,
                             struct timespec *change_time,
-                            off_t *size, uint16_t *pattr,
+                            off_t *size, uint32_t *pattr,
                             SMB_INO_T *ino)
 {
        struct cli_qpathinfo2_state *state = tevent_req_data(
@@ -970,33 +905,25 @@ NTSTATUS cli_qpathinfo2_recv(struct tevent_req *req,
        }
 
        if (create_time) {
-                *create_time = interpret_long_date((char *)state->data+0);
+               *create_time = state->create_time;
        }
        if (access_time) {
-               *access_time = interpret_long_date((char *)state->data+8);
+               *access_time = state->access_time;
        }
        if (write_time) {
-               *write_time = interpret_long_date((char *)state->data+16);
+               *write_time = state->write_time;
        }
        if (change_time) {
-               *change_time = interpret_long_date((char *)state->data+24);
+               *change_time = state->change_time;
        }
        if (pattr) {
-               *pattr = SVAL(state->data, 32);
+               *pattr = state->attr;
        }
        if (size) {
-                *size = IVAL2_TO_SMB_BIG_UINT(state->data,48);
+               *size = state->size;
        }
        if (ino) {
-               /*
-                * SMB1 qpathinfo2 uses SMB_QUERY_FILE_ALL_INFO
-                * which doesn't return an inode number (fileid).
-                * We can't change this to one of the FILE_ID
-                * info levels as only Win2003 and above support
-                * these [MS-SMB: 2.2.2.3.1] and the SMB1 code
-                * needs to support older servers.
-                */
-               *ino = 0;
+               *ino = state->ino;
        }
        return NT_STATUS_OK;
 }
@@ -1006,28 +933,14 @@ NTSTATUS cli_qpathinfo2(struct cli_state *cli, const char *fname,
                        struct timespec *access_time,
                        struct timespec *write_time,
                        struct timespec *change_time,
-                       off_t *size, uint16_t *pattr,
+                       off_t *size, uint32_t *pattr,
                        SMB_INO_T *ino)
 {
-       TALLOC_CTX *frame = NULL;
-       struct tevent_context *ev;
-       struct tevent_req *req;
+       TALLOC_CTX *frame = talloc_stackframe();
+       struct tevent_context *ev = NULL;
+       struct tevent_req *req = NULL;
        NTSTATUS status = NT_STATUS_NO_MEMORY;
 
-       if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
-               return cli_smb2_qpathinfo2(cli,
-                                       fname,
-                                       create_time,
-                                       access_time,
-                                       write_time,
-                                       change_time,
-                                       size,
-                                       pattr,
-                                       ino);
-       }
-
-       frame = talloc_stackframe();
-
        if (smbXcli_conn_has_async_calls(cli->conn)) {
                /*
                 * Can't use sync call while an async call is in flight
@@ -1063,6 +976,7 @@ struct cli_qpathinfo_streams_state {
 };
 
 static void cli_qpathinfo_streams_done(struct tevent_req *subreq);
+static void cli_qpathinfo_streams_done2(struct tevent_req *subreq);
 
 struct tevent_req *cli_qpathinfo_streams_send(TALLOC_CTX *mem_ctx,
                                              struct tevent_context *ev,
@@ -1077,6 +991,22 @@ struct tevent_req *cli_qpathinfo_streams_send(TALLOC_CTX *mem_ctx,
        if (req == NULL) {
                return NULL;
        }
+       if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+               subreq = cli_smb2_qpathinfo_send(state,
+                                                ev,
+                                                cli,
+                                                fname,
+                                                FSCC_FILE_STREAM_INFORMATION,
+                                                0,
+                                                CLI_BUFFER_SIZE);
+               if (tevent_req_nomem(subreq, req)) {
+                       return tevent_req_post(req, ev);
+               }
+               tevent_req_set_callback(subreq,
+                                       cli_qpathinfo_streams_done2,
+                                       req);
+               return req;
+       }
        subreq = cli_qpathinfo_send(state, ev, cli, fname,
                                    SMB_FILE_STREAM_INFORMATION,
                                    0, CLI_BUFFER_SIZE);
@@ -1097,12 +1027,22 @@ static void cli_qpathinfo_streams_done(struct tevent_req *subreq)
 
        status = cli_qpathinfo_recv(subreq, state, &state->data,
                                    &state->num_data);
-       TALLOC_FREE(subreq);
-       if (!NT_STATUS_IS_OK(status)) {
-               tevent_req_nterror(req, status);
-               return;
-       }
-       tevent_req_done(req);
+       tevent_req_simple_finish_ntstatus(subreq, status);
+}
+
+static void cli_qpathinfo_streams_done2(struct tevent_req *subreq)
+{
+       struct tevent_req *req =
+               tevent_req_callback_data(subreq, struct tevent_req);
+       struct cli_qpathinfo_streams_state *state =
+               tevent_req_data(req, struct cli_qpathinfo_streams_state);
+       NTSTATUS status;
+
+       status = cli_smb2_qpathinfo_recv(subreq,
+                                        state,
+                                        &state->data,
+                                        &state->num_data);
+       tevent_req_simple_finish_ntstatus(subreq, status);
 }
 
 NTSTATUS cli_qpathinfo_streams_recv(struct tevent_req *req,
@@ -1134,14 +1074,6 @@ NTSTATUS cli_qpathinfo_streams(struct cli_state *cli, const char *fname,
        struct tevent_req *req;
        NTSTATUS status = NT_STATUS_NO_MEMORY;
 
-       if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
-               return cli_smb2_qpathinfo_streams(cli,
-                                       fname,
-                                       mem_ctx,
-                                       pnum_streams,
-                                       pstreams);
-       }
-
        frame = talloc_stackframe();
 
        if (smbXcli_conn_has_async_calls(cli->conn)) {
@@ -1255,124 +1187,279 @@ bool parse_streams_blob(TALLOC_CTX *mem_ctx, const uint8_t *rdata,
  Send a qfileinfo QUERY_FILE_NAME_INFO call.
 ****************************************************************************/
 
-NTSTATUS cli_qfilename(struct cli_state *cli, uint16_t fnum,
-                      TALLOC_CTX *mem_ctx, char **_name)
+struct cli_qfileinfo_basic_state {
+       uint32_t attr;
+       off_t size;
+       struct timespec create_time;
+       struct timespec access_time;
+       struct timespec write_time;
+       struct timespec change_time;
+       SMB_INO_T ino;
+};
+
+static void cli_qfileinfo_basic_done(struct tevent_req *subreq);
+static void cli_qfileinfo_basic_doneE(struct tevent_req *subreq);
+static void cli_qfileinfo_basic_done2(struct tevent_req *subreq);
+
+struct tevent_req *cli_qfileinfo_basic_send(
+       TALLOC_CTX *mem_ctx,
+       struct tevent_context *ev,
+       struct cli_state *cli,
+       uint16_t fnum)
 {
-       uint16_t recv_flags2;
-       uint8_t *rdata;
-       uint32_t num_rdata;
-       NTSTATUS status;
-       char *name = NULL;
-       uint32_t namelen;
+       struct tevent_req *req = NULL, *subreq = NULL;
+       struct cli_qfileinfo_basic_state *state = NULL;
 
-       status = cli_qfileinfo(talloc_tos(), cli, fnum,
-                              SMB_QUERY_FILE_NAME_INFO,
-                              4, CLI_BUFFER_SIZE, &recv_flags2,
-                              &rdata, &num_rdata);
-       if (!NT_STATUS_IS_OK(status)) {
-               return status;
+       req = tevent_req_create(
+               mem_ctx, &state, struct cli_qfileinfo_basic_state);
+       if (req == NULL) {
+               return NULL;
        }
 
-       namelen = IVAL(rdata, 0);
-       if (namelen > (num_rdata - 4)) {
-               TALLOC_FREE(rdata);
-               return NT_STATUS_INVALID_NETWORK_RESPONSE;
+       if ((smbXcli_conn_protocol(cli->conn) < PROTOCOL_LANMAN2) ||
+           cli->win95) {
+               /*
+                * According to
+                * https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-cifs/3d9d8f3e-dc70-410d-a3fc-6f4a881e8cab
+                * SMB_COM_TRANSACTION2 used in cli_qfileinfo_send()
+                * further down was introduced with the LAN Manager
+                * 1.2 dialect, which we encode as PROTOCOL_LANMAN2.
+                *
+                * The "win95" check was introduced with commit
+                * 27e5850fd3e1c8 in 1998. Hard to check these days,
+                * but leave it in.
+                *
+                * Use a lowerlevel fallback in both cases.
+                */
+
+               subreq = cli_getattrE_send(state, ev, cli, fnum);
+               if (tevent_req_nomem(subreq, req)) {
+                       return tevent_req_post(req, ev);
+               }
+               tevent_req_set_callback(
+                       subreq, cli_qfileinfo_basic_doneE, req);
+               return req;
        }
 
-       pull_string_talloc(mem_ctx,
-                          (const char *)rdata,
-                          recv_flags2,
-                          &name,
-                          rdata + 4,
-                          namelen,
-                          STR_UNICODE);
-       if (name == NULL) {
-               status = map_nt_error_from_unix(errno);
-               TALLOC_FREE(rdata);
-               return status;
+       if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+               subreq = cli_smb2_query_info_fnum_send(
+                       state,  /* mem_ctx */
+                       ev,     /* ev */
+                       cli,    /* cli */
+                       fnum,   /* fnum */
+                       1,      /* in_info_type */
+                       (SMB_FILE_ALL_INFORMATION - 1000), /* in_file_info_class */
+                       0xFFFF, /* in_max_output_length */
+                       NULL,   /* in_input_buffer */
+                       0,      /* in_additional_info */
+                       0);     /* in_flags */
+               if (tevent_req_nomem(subreq, req)) {
+                       return tevent_req_post(req, ev);
+               }
+               tevent_req_set_callback(
+                       subreq, cli_qfileinfo_basic_done2, req);
+               return req;
        }
 
-       *_name = name;
-       TALLOC_FREE(rdata);
-       return NT_STATUS_OK;
+       subreq = cli_qfileinfo_send(
+               state,
+               ev,
+               cli,
+               fnum,
+               SMB_QUERY_FILE_ALL_INFO, /* level */
+               68,                      /* min_rdata */
+               CLI_BUFFER_SIZE);        /* max_rdata */
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(subreq, cli_qfileinfo_basic_done, req);
+       return req;
 }
 
-/****************************************************************************
- Send a qfileinfo call.
-****************************************************************************/
-
-NTSTATUS cli_qfileinfo_basic(struct cli_state *cli, uint16_t fnum,
-                            uint16_t *pattr, off_t *size,
-                            struct timespec *create_time,
-                            struct timespec *access_time,
-                            struct timespec *write_time,
-                            struct timespec *change_time,
-                            SMB_INO_T *ino)
+static void cli_qfileinfo_basic_done(struct tevent_req *subreq)
 {
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct cli_qfileinfo_basic_state *state = tevent_req_data(
+               req, struct cli_qfileinfo_basic_state);
        uint8_t *rdata;
        uint32_t num_rdata;
        NTSTATUS status;
 
-       if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
-               uint32_t attr = 0;
-               status = cli_smb2_qfileinfo_basic(cli,
-                                               fnum,
-                                               &attr,
-                                               size,
-                                               create_time,
-                                               access_time,
-                                               write_time,
-                                               change_time,
-                                               ino);
-               if (!NT_STATUS_IS_OK(status)) {
-                       return status;
-               }
-               if (pattr != NULL) {
-                       *pattr = attr;
-               }
-               return status;
+       status = cli_qfileinfo_recv(
+               subreq, state, NULL, &rdata, &num_rdata);
+       TALLOC_FREE(subreq);
+       if (tevent_req_nterror(req, status)) {
+               return;
        }
 
-       /* if its a win95 server then fail this - win95 totally screws it
-          up */
-       if (cli->win95) {
-               return NT_STATUS_NOT_SUPPORTED;
+       state->create_time = interpret_long_date(BVAL(rdata, 0));
+       state->access_time = interpret_long_date(BVAL(rdata, 8));
+       state->write_time = interpret_long_date(BVAL(rdata, 16));
+       state->change_time = interpret_long_date(BVAL(rdata, 24));
+       state->attr = PULL_LE_U32(rdata, 32);
+       state->size = PULL_LE_U64(rdata,48);
+       state->ino = PULL_LE_U32(rdata, 64);
+       TALLOC_FREE(rdata);
+
+       tevent_req_done(req);
+}
+
+static void cli_qfileinfo_basic_doneE(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct cli_qfileinfo_basic_state *state = tevent_req_data(
+               req, struct cli_qfileinfo_basic_state);
+       NTSTATUS status;
+
+       status = cli_getattrE_recv(
+               subreq,
+               &state->attr,
+               &state->size,
+               &state->change_time.tv_sec,
+               &state->access_time.tv_sec,
+               &state->write_time.tv_sec);
+       TALLOC_FREE(subreq);
+       if (tevent_req_nterror(req, status)) {
+               return;
        }
+       tevent_req_done(req);
+}
 
-       status = cli_qfileinfo(talloc_tos(), cli, fnum,
-                              SMB_QUERY_FILE_ALL_INFO,
-                              68, CLI_BUFFER_SIZE,
-                              NULL,
-                              &rdata, &num_rdata);
-       if (!NT_STATUS_IS_OK(status)) {
+static void cli_qfileinfo_basic_done2(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct cli_qfileinfo_basic_state *state = tevent_req_data(
+               req, struct cli_qfileinfo_basic_state);
+       DATA_BLOB outbuf = {0};
+       NTSTATUS status;
+
+       status = cli_smb2_query_info_fnum_recv(subreq, state, &outbuf);
+       TALLOC_FREE(subreq);
+       if (tevent_req_nterror(req, status)) {
+               return;
+       }
+
+       /* Parse the reply. */
+       if (outbuf.length < 0x60) {
+               tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
+               return;
+       }
+
+       state->create_time = interpret_long_date(BVAL(outbuf.data, 0x0));
+       state->access_time = interpret_long_date(BVAL(outbuf.data, 0x8));
+       state->write_time = interpret_long_date(BVAL(outbuf.data, 0x10));
+       state->change_time = interpret_long_date(BVAL(outbuf.data, 0x18));
+       state->attr = IVAL(outbuf.data, 0x20);
+       state->size = BVAL(outbuf.data, 0x30);
+       state->ino = BVAL(outbuf.data, 0x40);
+
+       data_blob_free(&outbuf);
+
+       tevent_req_done(req);
+}
+
+NTSTATUS cli_qfileinfo_basic_recv(
+       struct tevent_req *req,
+       uint32_t *attr,
+       off_t *size,
+       struct timespec *create_time,
+       struct timespec *access_time,
+       struct timespec *write_time,
+       struct timespec *change_time,
+       SMB_INO_T *ino)
+{
+       struct cli_qfileinfo_basic_state *state = tevent_req_data(
+               req, struct cli_qfileinfo_basic_state);
+       NTSTATUS status;
+
+       if (tevent_req_is_nterror(req, &status)) {
                return status;
        }
 
-       if (create_time) {
-               *create_time = interpret_long_date((char *)rdata+0);
+       if (create_time != NULL) {
+               *create_time = state->create_time;
        }
-       if (access_time) {
-               *access_time = interpret_long_date((char *)rdata+8);
+       if (access_time != NULL) {
+               *access_time = state->access_time;
        }
-       if (write_time) {
-               *write_time = interpret_long_date((char *)rdata+16);
+       if (write_time != NULL) {
+               *write_time = state->write_time;
        }
-       if (change_time) {
-               *change_time = interpret_long_date((char *)rdata+24);
+       if (change_time != NULL) {
+               *change_time = state->change_time;
        }
-       if (pattr) {
-               *pattr = SVAL(rdata, 32);
+       if (attr != NULL) {
+               *attr = state->attr;
        }
-       if (size) {
-                *size = IVAL2_TO_SMB_BIG_UINT(rdata,48);
+       if (size != NULL) {
+                *size = state->size;
        }
        if (ino) {
-               *ino = IVAL(rdata, 64);
+               *ino = state->ino;
        }
 
-       TALLOC_FREE(rdata);
        return NT_STATUS_OK;
 }
+/****************************************************************************
+ Send a qfileinfo call.
+****************************************************************************/
+
+NTSTATUS cli_qfileinfo_basic(
+       struct cli_state *cli,
+       uint16_t fnum,
+       uint32_t *attr,
+       off_t *size,
+       struct timespec *create_time,
+       struct timespec *access_time,
+       struct timespec *write_time,
+       struct timespec *change_time,
+       SMB_INO_T *ino)
+{
+       TALLOC_CTX *frame = NULL;
+       struct tevent_context *ev = NULL;
+       struct tevent_req *req = NULL;
+       NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+       frame = talloc_stackframe();
+
+       if (smbXcli_conn_has_async_calls(cli->conn)) {
+               /*
+                * Can't use sync call while an async call is in flight
+                */
+               status = NT_STATUS_INVALID_PARAMETER;
+               goto fail;
+       }
+       ev = samba_tevent_context_init(frame);
+       if (ev == NULL) {
+               goto fail;
+       }
+       req = cli_qfileinfo_basic_send(frame, ev, cli, fnum);
+       if (req == NULL) {
+               goto fail;
+       }
+       if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+               goto fail;
+       }
+
+       status = cli_qfileinfo_basic_recv(
+               req,
+               attr,
+               size,
+               create_time,
+               access_time,
+               write_time,
+               change_time,
+               ino);
+
+       /* cli_smb2_query_info_fnum_recv doesn't set this */
+       cli->raw_status = status;
+fail:
+       TALLOC_FREE(frame);
+       return status;
+}
 
 /****************************************************************************
  Send a qpathinfo BASIC_INFO call.
@@ -1419,8 +1506,7 @@ static void cli_qpathinfo_basic_done(struct tevent_req *subreq)
        status = cli_qpathinfo_recv(subreq, state, &state->data,
                                    &state->num_data);
        TALLOC_FREE(subreq);
-       if (!NT_STATUS_IS_OK(status)) {
-               tevent_req_nterror(req, status);
+       if (tevent_req_nterror(req, status)) {
                return;
        }
        tevent_req_done(req);
@@ -1437,10 +1523,10 @@ NTSTATUS cli_qpathinfo_basic_recv(struct tevent_req *req,
                return status;
        }
 
-       sbuf->st_ex_btime = interpret_long_date((char *)state->data);
-       sbuf->st_ex_atime = interpret_long_date((char *)state->data+8);
-       sbuf->st_ex_mtime = interpret_long_date((char *)state->data+16);
-       sbuf->st_ex_ctime = interpret_long_date((char *)state->data+24);
+       sbuf->st_ex_btime = interpret_long_date(BVAL(state->data, 0));
+       sbuf->st_ex_atime = interpret_long_date(BVAL(state->data, 8));
+       sbuf->st_ex_mtime = interpret_long_date(BVAL(state->data, 16));
+       sbuf->st_ex_ctime = interpret_long_date(BVAL(state->data, 24));
        *attributes = IVAL(state->data, 32);
        return NT_STATUS_OK;
 }
@@ -1537,7 +1623,7 @@ NTSTATUS cli_qpathinfo_alt_name(struct cli_state *cli, const char *fname, fstrin
 }
 
 /****************************************************************************
- Send a qpathinfo SMB_QUERY_FILE_STADNDARD_INFO call.
+ Send a qpathinfo SMB_QUERY_FILE_STANDARD_INFO call.
 ****************************************************************************/
 
 NTSTATUS cli_qpathinfo_standard(struct cli_state *cli, const char *fname,
@@ -1592,7 +1678,7 @@ NTSTATUS cli_qpathinfo3(struct cli_state *cli, const char *fname,
                        struct timespec *access_time,
                        struct timespec *write_time,
                        struct timespec *change_time,
-                       off_t *size, uint16_t *pattr,
+                       off_t *size, uint32_t *pattr,
                        SMB_INO_T *ino)
 {
        NTSTATUS status = NT_STATUS_OK;
@@ -1601,12 +1687,24 @@ NTSTATUS cli_qpathinfo3(struct cli_state *cli, const char *fname,
        uint64_t pos;
 
        if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+               /*
+                * NB. cli_qpathinfo2() checks pattr is valid before
+                * storing a value into it, so we don't need to use
+                * an intermediate attr variable as below but can
+                * pass pattr directly.
+                */
                return cli_qpathinfo2(cli, fname,
                                      create_time, access_time, write_time, change_time,
                                      size, pattr, ino);
        }
 
        if (create_time || access_time || write_time || change_time || pattr) {
+               /*
+                * cli_qpathinfo_basic() always indirects the passed
+                * in pointers so we use intermediate variables to
+                * collect all of them before assigning any requested
+                * below.
+                */
                status = cli_qpathinfo_basic(cli, fname, &st, &attr);
                if (!NT_STATUS_IS_OK(status)) {
                        return status;