libsmb: Pass NTTIME to interpret_long_date()
[samba.git] / source3 / libsmb / clirap.c
index 6685fbd1d8b8c5a3e306c5bbcf1abea8e7c0af07..7944eeb847c2e2daf2783f299066812d97ebace4 100644 (file)
 #include "includes.h"
 #include "../libcli/auth/libcli_auth.h"
 #include "../librpc/gen_ndr/rap.h"
-#include "../lib/crypto/arcfour.h"
 #include "../lib/util/tevent_ntstatus.h"
 #include "async_smb.h"
 #include "libsmb/libsmb.h"
 #include "libsmb/clirap.h"
 #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>
 
 #define PIPE_LANMAN   "\\PIPE\\LANMAN"
 
@@ -67,14 +71,14 @@ bool cli_api(struct cli_state *cli,
         * talloc
         */
 
-       *rparam = (char *)memdup(my_rparam, num_my_rparam);
+       *rparam = (char *)smb_memdup(my_rparam, num_my_rparam);
        if (*rparam == NULL) {
                goto fail;
        }
        *rprcnt = num_my_rparam;
        TALLOC_FREE(my_rparam);
 
-       *rdata = (char *)memdup(my_rdata, num_my_rdata);
+       *rdata = (char *)smb_memdup(my_rdata, num_my_rdata);
        if (*rdata == NULL) {
                goto fail;
        }
@@ -92,76 +96,11 @@ fail:
        return false;
 }
 
-/****************************************************************************
- Perform a NetWkstaUserLogon.
-****************************************************************************/
-
-bool cli_NetWkstaUserLogon(struct cli_state *cli,char *user, char *workstation)
-{
-       char *rparam = NULL;
-       char *rdata = NULL;
-       char *p;
-       unsigned int rdrcnt,rprcnt;
-       char param[1024];
-
-       memset(param, 0, sizeof(param));
-
-       /* send a SMBtrans command with api NetWkstaUserLogon */
-       p = param;
-       SSVAL(p,0,132); /* api number */
-       p += 2;
-       strlcpy(p,"OOWb54WrLh",sizeof(param)-PTR_DIFF(p,param));
-       p = skip_string(param,sizeof(param),p);
-       strlcpy(p,"WB21BWDWWDDDDDDDzzzD",sizeof(param)-PTR_DIFF(p,param));
-       p = skip_string(param,sizeof(param),p);
-       SSVAL(p,0,1);
-       p += 2;
-       strlcpy(p,user,sizeof(param)-PTR_DIFF(p,param));
-       strupper_m(p);
-       p += 21;
-       p++;
-       p += 15;
-       p++;
-       strlcpy(p, workstation,sizeof(param)-PTR_DIFF(p,param));
-       strupper_m(p);
-       p += 16;
-       SSVAL(p, 0, CLI_BUFFER_SIZE);
-       p += 2;
-       SSVAL(p, 0, CLI_BUFFER_SIZE);
-       p += 2;
-
-       if (cli_api(cli,
-                    param, PTR_DIFF(p,param),1024,  /* param, length, max */
-                    NULL, 0, CLI_BUFFER_SIZE,           /* data, length, max */
-                    &rparam, &rprcnt,               /* return params, return size */
-                    &rdata, &rdrcnt                 /* return data, return size */
-                   )) {
-               cli->rap_error = rparam? SVAL(rparam,0) : -1;
-               p = rdata;
-
-               if (cli->rap_error == 0) {
-                       DEBUG(4,("NetWkstaUserLogon success\n"));
-                       /*
-                        * The cli->privileges = SVAL(p, 24); field was set here
-                        * but it was not use anywhere else.
-                        */
-                       /* The cli->eff_name field used to be set here
-                          but it wasn't used anywhere else. */
-               } else {
-                       DEBUG(1,("NetwkstaUserLogon gave error %d\n", cli->rap_error));
-               }
-       }
-
-       SAFE_FREE(rparam);
-       SAFE_FREE(rdata);
-       return (cli->rap_error == 0);
-}
-
 /****************************************************************************
  Call a NetShareEnum - try and browse available connections on a host.
 ****************************************************************************/
 
-int cli_RNetShareEnum(struct cli_state *cli, void (*fn)(const char *, uint32, const char *, void *), void *state)
+int cli_RNetShareEnum(struct cli_state *cli, void (*fn)(const char *, uint32_t, const char *, void *), void *state)
 {
        char *rparam = NULL;
        char *rdata = NULL;
@@ -169,6 +108,8 @@ int cli_RNetShareEnum(struct cli_state *cli, void (*fn)(const char *, uint32, co
        unsigned int rdrcnt,rprcnt;
        char param[1024];
        int count = -1;
+       bool ok;
+       int res;
 
        /* now send a SMBtrans command with api RNetShareEnum */
        p = param;
@@ -186,74 +127,82 @@ int cli_RNetShareEnum(struct cli_state *cli, void (*fn)(const char *, uint32, co
        SSVAL(p,2,0xFFE0);
        p += 4;
 
-       if (cli_api(cli,
-                   param, PTR_DIFF(p,param), 1024,  /* Param, length, maxlen */
-                   NULL, 0, 0xFFE0,            /* data, length, maxlen - Win2k needs a small buffer here too ! */
-                   &rparam, &rprcnt,                /* return params, length */
-                   &rdata, &rdrcnt))                /* return data, length */
-               {
-                       int res = rparam? SVAL(rparam,0) : -1;
-
-                       if (res == 0 || res == ERRmoredata) {
-                               int converter=SVAL(rparam,2);
-                               int i;
-                               char *rdata_end = rdata + rdrcnt;
-
-                               count=SVAL(rparam,4);
-                               p = rdata;
-
-                               for (i=0;i<count;i++,p+=20) {
-                                       char *sname;
-                                       int type;
-                                       int comment_offset;
-                                       const char *cmnt;
-                                       const char *p1;
-                                       char *s1, *s2;
-                                       size_t len;
-                                       TALLOC_CTX *frame = talloc_stackframe();
-
-                                       if (p + 20 > rdata_end) {
-                                               TALLOC_FREE(frame);
-                                               break;
-                                       }
-
-                                       sname = p;
-                                       type = SVAL(p,14);
-                                       comment_offset = (IVAL(p,16) & 0xFFFF) - converter;
-                                       if (comment_offset < 0 ||
-                                                       comment_offset > (int)rdrcnt) {
-                                               TALLOC_FREE(frame);
-                                               break;
-                                       }
-                                       cmnt = comment_offset?(rdata+comment_offset):"";
-
-                                       /* Work out the comment length. */
-                                       for (p1 = cmnt, len = 0; *p1 &&
-                                                       p1 < rdata_end; len++)
-                                               p1++;
-                                       if (!*p1) {
-                                               len++;
-                                       }
-                                       pull_string_talloc(frame,rdata,0,
-                                               &s1,sname,14,STR_ASCII);
-                                       pull_string_talloc(frame,rdata,0,
-                                               &s2,cmnt,len,STR_ASCII);
-                                       if (!s1 || !s2) {
-                                               TALLOC_FREE(frame);
-                                               continue;
-                                       }
-
-                                       fn(s1, type, s2, state);
+       ok = cli_api(
+               cli,
+               param, PTR_DIFF(p,param), 1024,  /* Param, length, maxlen */
+               NULL, 0, 0xFFE0,            /* data, length, maxlen - Win2k needs a small buffer here too ! */
+               &rparam, &rprcnt,                /* return params, length */
+               &rdata, &rdrcnt);                /* return data, length */
+       if (!ok) {
+               DEBUG(4,("NetShareEnum failed\n"));
+               goto done;
+       }
 
-                                       TALLOC_FREE(frame);
-                               }
-                       } else {
-                               DEBUG(4,("NetShareEnum res=%d\n", res));
+       if (rprcnt < 6) {
+               DBG_ERR("Got invalid result: rprcnt=%u\n", rprcnt);
+               goto done;
+       }
+
+       res = rparam? SVAL(rparam,0) : -1;
+
+       if (res == 0 || res == ERRmoredata) {
+               int converter=SVAL(rparam,2);
+               int i;
+               char *rdata_end = rdata + rdrcnt;
+
+               count=SVAL(rparam,4);
+               p = rdata;
+
+               for (i=0;i<count;i++,p+=20) {
+                       char *sname;
+                       int type;
+                       int comment_offset;
+                       const char *cmnt;
+                       const char *p1;
+                       char *s1, *s2;
+                       size_t len;
+                       TALLOC_CTX *frame = talloc_stackframe();
+
+                       if (p + 20 > rdata_end) {
+                               TALLOC_FREE(frame);
+                               break;
                        }
-               } else {
-                       DEBUG(4,("NetShareEnum failed\n"));
+
+                       sname = p;
+                       type = SVAL(p,14);
+                       comment_offset = (IVAL(p,16) & 0xFFFF) - converter;
+                       if (comment_offset < 0 ||
+                           comment_offset > (int)rdrcnt) {
+                               TALLOC_FREE(frame);
+                               break;
+                       }
+                       cmnt = comment_offset?(rdata+comment_offset):"";
+
+                       /* Work out the comment length. */
+                       for (p1 = cmnt, len = 0; *p1 &&
+                                    p1 < rdata_end; len++)
+                               p1++;
+                       if (!*p1) {
+                               len++;
+                       }
+                       pull_string_talloc(frame,rdata,0,
+                                          &s1,sname,14,STR_ASCII);
+                       pull_string_talloc(frame,rdata,0,
+                                          &s2,cmnt,len,STR_ASCII);
+                       if (!s1 || !s2) {
+                               TALLOC_FREE(frame);
+                               continue;
+                       }
+
+                       fn(s1, type, s2, state);
+
+                       TALLOC_FREE(frame);
                }
+       } else {
+                       DEBUG(4,("NetShareEnum res=%d\n", res));
+       }
 
+done:
        SAFE_FREE(rparam);
        SAFE_FREE(rdata);
 
@@ -268,8 +217,8 @@ int cli_RNetShareEnum(struct cli_state *cli, void (*fn)(const char *, uint32, co
  the comment and a state pointer.
 ****************************************************************************/
 
-bool cli_NetServerEnum(struct cli_state *cli, char *workgroup, uint32 stype,
-                      void (*fn)(const char *, uint32, const char *, void *),
+bool cli_NetServerEnum(struct cli_state *cli, char *workgroup, uint32_t stype,
+                      void (*fn)(const char *, uint32_t, const char *, void *),
                       void *state)
 {
        char *rparam = NULL;
@@ -280,7 +229,7 @@ bool cli_NetServerEnum(struct cli_state *cli, char *workgroup, uint32 stype,
        char param[1024];
        int uLevel = 1;
        size_t len;
-       uint32 func = RAP_NetServerEnum2;
+       uint32_t func = RAP_NetServerEnum2;
        char *last_entry = NULL;
        int total_cnt = 0;
        int return_cnt = 0;
@@ -323,7 +272,7 @@ bool cli_NetServerEnum(struct cli_state *cli, char *workgroup, uint32 stype,
                                sizeof(param) - PTR_DIFF(p,param) - 1,
                                STR_TERMINATE|STR_UPPER);
 
-               if (len == (size_t)-1) {
+               if (len == 0) {
                        SAFE_FREE(last_entry);
                        return false;
                }
@@ -335,7 +284,7 @@ bool cli_NetServerEnum(struct cli_state *cli, char *workgroup, uint32 stype,
                                        sizeof(param) - PTR_DIFF(p,param) - 1,
                                        STR_TERMINATE);
 
-                       if (len == (size_t)-1) {
+                       if (len == 0) {
                                SAFE_FREE(last_entry);
                                return false;
                        }
@@ -357,6 +306,13 @@ bool cli_NetServerEnum(struct cli_state *cli, char *workgroup, uint32 stype,
                }
 
                rdata_end = rdata + rdrcnt;
+
+               if (rprcnt < 6) {
+                       DBG_ERR("Got invalid result: rprcnt=%u\n", rprcnt);
+                       res = -1;
+                       break;
+               }
+
                res = rparam ? SVAL(rparam,0) : -1;
 
                if (res == 0 || res == ERRmoredata ||
@@ -503,6 +459,12 @@ bool cli_oem_change_password(struct cli_state *cli, const char *user, const char
        char *rparam = NULL;
        char *rdata = NULL;
        unsigned int rprcnt, rdrcnt;
+       gnutls_cipher_hd_t cipher_hnd = NULL;
+       gnutls_datum_t old_pw_key = {
+               .data = old_pw_hash,
+               .size = sizeof(old_pw_hash),
+       };
+       int rc;
 
        if (strlen(user) >= sizeof(fstring)-1) {
                DEBUG(0,("cli_oem_change_password: user name %s is too long.\n", user));
@@ -534,14 +496,33 @@ bool cli_oem_change_password(struct cli_state *cli, const char *user, const char
        DEBUG(100,("make_oem_passwd_hash\n"));
        dump_data(100, data, 516);
 #endif
-       arcfour_crypt( (unsigned char *)data, (unsigned char *)old_pw_hash, 516);
+       rc = gnutls_cipher_init(&cipher_hnd,
+                               GNUTLS_CIPHER_ARCFOUR_128,
+                               &old_pw_key,
+                               NULL);
+       if (rc < 0) {
+               DBG_ERR("gnutls_cipher_init failed: %s\n",
+                       gnutls_strerror(rc));
+               return false;
+       }
+       rc = gnutls_cipher_encrypt(cipher_hnd,
+                             data,
+                             516);
+       gnutls_cipher_deinit(cipher_hnd);
+       if (rc < 0) {
+               return false;
+       }
 
        /*
         * Now place the old password hash in the data.
         */
        E_deshash(new_password, new_pw_hash);
 
-       E_old_pw_hash( new_pw_hash, old_pw_hash, (uchar *)&data[516]);
+       rc = E_old_pw_hash( new_pw_hash, old_pw_hash, (uchar *)&data[516]);
+       if (rc != 0) {
+               DBG_ERR("E_old_pw_hash failed: %s\n", gnutls_strerror(rc));
+               return false;
+       }
 
        data_len = 532;
 
@@ -555,206 +536,261 @@ bool cli_oem_change_password(struct cli_state *cli, const char *user, const char
                return False;
        }
 
+       if (rdrcnt < 2) {
+               cli->rap_error = ERRbadformat;
+               goto done;
+       }
+
        if (rparam) {
                cli->rap_error = SVAL(rparam,0);
        }
 
+done:
        SAFE_FREE(rparam);
        SAFE_FREE(rdata);
 
        return (cli->rap_error == 0);
 }
 
-/****************************************************************************
- Send a qpathinfo call.
-****************************************************************************/
+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,
+       uint32_t attr)
+{
+       char *p = (char *)buf;
+       /*
+        * Add the create, last access, modification, and status change times
+        */
+       put_long_date_full_timespec(
+               TIMESTAMP_SET_NT_OR_BETTER, p, &create_time);
+       p += 8;
 
-struct cli_qpathinfo1_state {
-       struct cli_state *cli;
-       uint32_t num_data;
-       uint8_t *data;
-};
+       put_long_date_full_timespec(
+               TIMESTAMP_SET_NT_OR_BETTER, p, &access_time);
+       p += 8;
 
-static void cli_qpathinfo1_done(struct tevent_req *subreq);
+       put_long_date_full_timespec(
+               TIMESTAMP_SET_NT_OR_BETTER, p, &write_time);
+       p += 8;
 
-struct tevent_req *cli_qpathinfo1_send(TALLOC_CTX *mem_ctx,
-                                      struct event_context *ev,
-                                      struct cli_state *cli,
-                                      const char *fname)
+       put_long_date_full_timespec(
+               TIMESTAMP_SET_NT_OR_BETTER, p, &change_time);
+       p += 8;
+
+       if (attr == (uint32_t)-1 || attr == FILE_ATTRIBUTE_NORMAL) {
+               /* No change. */
+               attr = 0;
+       } else if (attr == 0) {
+               /* Clear all existing attributes. */
+               attr = FILE_ATTRIBUTE_NORMAL;
+       }
+
+       /* Add attributes */
+       SIVAL(p, 0, attr);
+
+       p += 4;
+
+       /* Add padding */
+       SIVAL(p, 0, 0);
+       p += 4;
+
+       SMB_ASSERT(PTR_DIFF(p, buf) == 40);
+}
+
+NTSTATUS cli_setpathinfo_ext(struct cli_state *cli, const char *fname,
+                            struct timespec create_time,
+                            struct timespec access_time,
+                            struct timespec write_time,
+                            struct timespec change_time,
+                            uint32_t attr)
+{
+       uint8_t buf[40];
+
+       prep_basic_information_buf(
+               buf,
+               create_time,
+               access_time,
+               write_time,
+               change_time,
+               attr);
+
+       if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+               DATA_BLOB in_data = data_blob_const(buf, sizeof(buf));
+               /*
+                * Split out SMB2 here as we need to select
+                * the correct info type and level.
+                */
+               return cli_smb2_setpathinfo(cli,
+                               fname,
+                               1, /* SMB2_SETINFO_FILE */
+                               SMB_FILE_BASIC_INFORMATION - 1000,
+                               &in_data);
+       }
+
+       return cli_setpathinfo(
+               cli, SMB_FILE_BASIC_INFORMATION, fname, buf, sizeof(buf));
+}
+
+struct cli_setfileinfo_ext_state {
+       uint8_t data[40];
+       DATA_BLOB in_data;
+};
+
+static void cli_setfileinfo_ext_done(struct tevent_req *subreq);
+static void cli_setfileinfo_ext_done2(struct tevent_req *subreq);
+
+struct tevent_req *cli_setfileinfo_ext_send(
+       TALLOC_CTX *mem_ctx,
+       struct tevent_context *ev,
+       struct cli_state *cli,
+       uint16_t fnum,
+       struct timespec create_time,
+       struct timespec access_time,
+       struct timespec write_time,
+       struct timespec change_time,
+       uint32_t attr)
 {
        struct tevent_req *req = NULL, *subreq = NULL;
-       struct cli_qpathinfo1_state *state = NULL;
+       struct cli_setfileinfo_ext_state *state = NULL;
 
-       req = tevent_req_create(mem_ctx, &state, struct cli_qpathinfo1_state);
+       req = tevent_req_create(
+               mem_ctx, &state, struct cli_setfileinfo_ext_state);
        if (req == NULL) {
                return NULL;
        }
-       state->cli = cli;
-       subreq = cli_qpathinfo_send(state, ev, cli, fname, SMB_INFO_STANDARD,
-                                   22, CLI_BUFFER_SIZE);
+       prep_basic_information_buf(
+               state->data,
+               create_time,
+               access_time,
+               write_time,
+               change_time,
+               attr);
+
+       if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+               state->in_data = (DATA_BLOB) {
+                       .data = state->data, .length = sizeof(state->data),
+               };
+
+               subreq = cli_smb2_set_info_fnum_send(
+                       state,
+                       ev,
+                       cli,
+                       fnum,
+                       SMB2_0_INFO_FILE,
+                       SMB_FILE_BASIC_INFORMATION - 1000,
+                       &state->in_data,
+                       0);     /* in_additional_info */
+               if (tevent_req_nomem(subreq, req)) {
+                       return tevent_req_post(req, ev);
+               }
+               tevent_req_set_callback(
+                       subreq, cli_setfileinfo_ext_done2, req);
+               return req;
+       }
+
+       subreq = cli_setfileinfo_send(
+               state,
+               ev,
+               cli,
+               fnum,
+               SMB_FILE_BASIC_INFORMATION,
+               state->data,
+               sizeof(state->data));
        if (tevent_req_nomem(subreq, req)) {
                return tevent_req_post(req, ev);
        }
-       tevent_req_set_callback(subreq, cli_qpathinfo1_done, req);
+       tevent_req_set_callback(subreq, cli_setfileinfo_ext_done, req);
        return req;
 }
 
-static void cli_qpathinfo1_done(struct tevent_req *subreq)
+static void cli_setfileinfo_ext_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 status = cli_setfileinfo_recv(subreq);
+       tevent_req_simple_finish_ntstatus(subreq, status);
 }
 
-NTSTATUS cli_qpathinfo1_recv(struct tevent_req *req,
-                            time_t *change_time,
-                            time_t *access_time,
-                            time_t *write_time,
-                            off_t *size,
-                            uint16 *mode)
+static void cli_setfileinfo_ext_done2(struct tevent_req *subreq)
 {
-       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;
-       }
+       NTSTATUS status = cli_smb2_set_info_fnum_recv(subreq);
+       tevent_req_simple_finish_ntstatus(subreq, status);
+}
 
-       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 (mode) {
-               *mode = SVAL(state->data, l1_attrFile);
-       }
-       return NT_STATUS_OK;
+NTSTATUS cli_setfileinfo_ext_recv(struct tevent_req *req)
+{
+       return tevent_req_simple_recv_ntstatus(req);
 }
 
-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 *mode)
+NTSTATUS cli_setfileinfo_ext(
+       struct cli_state *cli,
+       uint16_t fnum,
+       struct timespec create_time,
+       struct timespec access_time,
+       struct timespec write_time,
+       struct timespec change_time,
+       uint32_t attr)
 {
-       TALLOC_CTX *frame = talloc_stackframe();
-       struct event_context *ev;
-       struct tevent_req *req;
+       TALLOC_CTX *frame = NULL;
+       struct tevent_context *ev = NULL;
+       struct tevent_req *req = NULL;
        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;
+               return NT_STATUS_INVALID_PARAMETER;
        }
-       ev = event_context_init(frame);
+
+       frame = talloc_stackframe();
+
+       ev = samba_tevent_context_init(frame);
        if (ev == NULL) {
                goto fail;
        }
-       req = cli_qpathinfo1_send(frame, ev, cli, fname);
+       req = cli_setfileinfo_ext_send(
+               ev,
+               ev,
+               cli,
+               fnum,
+               create_time,
+               access_time,
+               write_time,
+               change_time,
+               attr);
        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, mode);
+       status = cli_setfileinfo_ext_recv(req);
  fail:
        TALLOC_FREE(frame);
        return status;
 }
 
-/****************************************************************************
- Send a setpathinfo call.
-****************************************************************************/
-
-NTSTATUS cli_setpathinfo_basic(struct cli_state *cli, const char *fname,
-                              time_t create_time,
-                              time_t access_time,
-                              time_t write_time,
-                              time_t change_time,
-                              uint16 mode)
-{
-       unsigned int data_len = 0;
-       char data[40];
-       char *p;
-
-        p = data;
-
-        /*
-         * Add the create, last access, modification, and status change times
-         */
-        put_long_date(p, create_time);
-        p += 8;
-
-        put_long_date(p, access_time);
-        p += 8;
-
-        put_long_date(p, write_time);
-        p += 8;
-
-        put_long_date(p, change_time);
-        p += 8;
-
-        /* Add attributes */
-        SIVAL(p, 0, mode);
-        p += 4;
-
-        /* Add padding */
-        SIVAL(p, 0, 0);
-        p += 4;
-
-        data_len = PTR_DIFF(p, data);
-
-       return cli_setpathinfo(cli, SMB_FILE_BASIC_INFORMATION, fname,
-                              (uint8_t *)data, data_len);
-}
-
 /****************************************************************************
  Send a qpathinfo call with the SMB_QUERY_FILE_ALL_INFO info level.
 ****************************************************************************/
 
 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,
-                                      struct event_context *ev,
+                                      struct tevent_context *ev,
                                       struct cli_state *cli,
                                       const char *fname)
 {
@@ -765,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);
@@ -775,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);
 }
 
@@ -798,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 *mode,
+                            off_t *size, uint32_t *pattr,
                             SMB_INO_T *ino)
 {
        struct cli_qpathinfo2_state *state = tevent_req_data(
@@ -810,25 +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 (mode) {
-               *mode = SVAL(state->data, 32);
+       if (pattr) {
+               *pattr = state->attr;
        }
        if (size) {
-                *size = IVAL2_TO_SMB_BIG_UINT(state->data,48);
+               *size = state->size;
        }
        if (ino) {
-               *ino = IVAL(state->data, 64);
+               *ino = state->ino;
        }
        return NT_STATUS_OK;
 }
@@ -838,12 +933,12 @@ 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 *mode,
+                       off_t *size, uint32_t *pattr,
                        SMB_INO_T *ino)
 {
        TALLOC_CTX *frame = talloc_stackframe();
-       struct event_context *ev;
-       struct tevent_req *req;
+       struct tevent_context *ev = NULL;
+       struct tevent_req *req = NULL;
        NTSTATUS status = NT_STATUS_NO_MEMORY;
 
        if (smbXcli_conn_has_async_calls(cli->conn)) {
@@ -853,7 +948,7 @@ NTSTATUS cli_qpathinfo2(struct cli_state *cli, const char *fname,
                status = NT_STATUS_INVALID_PARAMETER;
                goto fail;
        }
-       ev = event_context_init(frame);
+       ev = samba_tevent_context_init(frame);
        if (ev == NULL) {
                goto fail;
        }
@@ -865,7 +960,7 @@ NTSTATUS cli_qpathinfo2(struct cli_state *cli, const char *fname,
                goto fail;
        }
        status = cli_qpathinfo2_recv(req, create_time, access_time,
-                                    write_time, change_time, size, mode, ino);
+                                    write_time, change_time, size, pattr, ino);
  fail:
        TALLOC_FREE(frame);
        return status;
@@ -875,17 +970,13 @@ NTSTATUS cli_qpathinfo2(struct cli_state *cli, const char *fname,
  Get the stream info
 ****************************************************************************/
 
-static bool parse_streams_blob(TALLOC_CTX *mem_ctx, const uint8_t *data,
-                              size_t data_len,
-                              unsigned int *pnum_streams,
-                              struct stream_struct **pstreams);
-
 struct cli_qpathinfo_streams_state {
        uint32_t num_data;
        uint8_t *data;
 };
 
 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,
@@ -900,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);
@@ -920,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,
@@ -952,11 +1069,13 @@ NTSTATUS cli_qpathinfo_streams(struct cli_state *cli, const char *fname,
                               unsigned int *pnum_streams,
                               struct stream_struct **pstreams)
 {
-       TALLOC_CTX *frame = talloc_stackframe();
-       struct event_context *ev;
+       TALLOC_CTX *frame = NULL;
+       struct tevent_context *ev;
        struct tevent_req *req;
        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
@@ -964,7 +1083,7 @@ NTSTATUS cli_qpathinfo_streams(struct cli_state *cli, const char *fname,
                status = NT_STATUS_INVALID_PARAMETER;
                goto fail;
        }
-       ev = event_context_init(frame);
+       ev = samba_tevent_context_init(frame);
        if (ev == NULL) {
                goto fail;
        }
@@ -982,7 +1101,7 @@ NTSTATUS cli_qpathinfo_streams(struct cli_state *cli, const char *fname,
        return status;
 }
 
-static bool parse_streams_blob(TALLOC_CTX *mem_ctx, const uint8_t *rdata,
+bool parse_streams_blob(TALLOC_CTX *mem_ctx, const uint8_t *rdata,
                               size_t data_len,
                               unsigned int *pnum_streams,
                               struct stream_struct **pstreams)
@@ -1068,104 +1187,279 @@ static 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;
+       }
+
+       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;
+       }
+
+       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;
+}
 
-       clistr_pull_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;
+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;
+
+       status = cli_qfileinfo_recv(
+               subreq, state, NULL, &rdata, &num_rdata);
+       TALLOC_FREE(subreq);
+       if (tevent_req_nterror(req, status)) {
+               return;
        }
 
-       *_name = name;
+       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);
-       return NT_STATUS_OK;
+
+       tevent_req_done(req);
 }
 
-/****************************************************************************
- Send a qfileinfo call.
-****************************************************************************/
+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;
 
-NTSTATUS cli_qfileinfo_basic(struct cli_state *cli, uint16_t fnum,
-                            uint16 *mode, off_t *size,
-                            struct timespec *create_time,
-                            struct timespec *access_time,
-                            struct timespec *write_time,
-                            struct timespec *change_time,
-                            SMB_INO_T *ino)
+       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);
+}
+
+static void cli_qfileinfo_basic_done2(struct tevent_req *subreq)
 {
-       uint8_t *rdata;
-       uint32_t num_rdata;
+       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;
 
-       /* if its a win95 server then fail this - win95 totally screws it
-          up */
-       if (cli->win95) {
-               return NT_STATUS_NOT_SUPPORTED;
+       status = cli_smb2_query_info_fnum_recv(subreq, state, &outbuf);
+       TALLOC_FREE(subreq);
+       if (tevent_req_nterror(req, status)) {
+               return;
        }
 
-       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)) {
+       /* 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 (mode) {
-               *mode = 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.
@@ -1179,7 +1473,7 @@ struct cli_qpathinfo_basic_state {
 static void cli_qpathinfo_basic_done(struct tevent_req *subreq);
 
 struct tevent_req *cli_qpathinfo_basic_send(TALLOC_CTX *mem_ctx,
-                                           struct event_context *ev,
+                                           struct tevent_context *ev,
                                            struct cli_state *cli,
                                            const char *fname)
 {
@@ -1212,15 +1506,14 @@ 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);
 }
 
 NTSTATUS cli_qpathinfo_basic_recv(struct tevent_req *req,
-                                 SMB_STRUCT_STAT *sbuf, uint32 *attributes)
+                                 SMB_STRUCT_STAT *sbuf, uint32_t *attributes)
 {
        struct cli_qpathinfo_basic_state *state = tevent_req_data(
                req, struct cli_qpathinfo_basic_state);
@@ -1230,21 +1523,31 @@ NTSTATUS cli_qpathinfo_basic_recv(struct tevent_req *req,
                return status;
        }
 
-       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;
 }
 
 NTSTATUS cli_qpathinfo_basic(struct cli_state *cli, const char *name,
-                            SMB_STRUCT_STAT *sbuf, uint32 *attributes)
+                            SMB_STRUCT_STAT *sbuf, uint32_t *attributes)
 {
-       TALLOC_CTX *frame = talloc_stackframe();
-       struct event_context *ev;
+       TALLOC_CTX *frame = NULL;
+       struct tevent_context *ev;
        struct tevent_req *req;
        NTSTATUS status = NT_STATUS_NO_MEMORY;
 
+       if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+               return cli_smb2_qpathinfo_basic(cli,
+                                               name,
+                                               sbuf,
+                                               attributes);
+       }
+
+       frame = talloc_stackframe();
+
        if (smbXcli_conn_has_async_calls(cli->conn)) {
                /*
                 * Can't use sync call while an async call is in flight
@@ -1252,7 +1555,7 @@ NTSTATUS cli_qpathinfo_basic(struct cli_state *cli, const char *name,
                status = NT_STATUS_INVALID_PARAMETER;
                goto fail;
        }
-       ev = event_context_init(frame);
+       ev = samba_tevent_context_init(frame);
        if (ev == NULL) {
                goto fail;
        }
@@ -1282,6 +1585,12 @@ NTSTATUS cli_qpathinfo_alt_name(struct cli_state *cli, const char *fname, fstrin
        size_t converted_size = 0;
        NTSTATUS status;
 
+       if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+               return cli_smb2_qpathinfo_alt_name(cli,
+                                               fname,
+                                               alt_name);
+       }
+
        status = cli_qpathinfo(talloc_tos(), cli, fname,
                               SMB_QUERY_FILE_ALT_NAME_INFO,
                               4, CLI_BUFFER_SIZE, &rdata, &num_rdata);
@@ -1312,3 +1621,124 @@ NTSTATUS cli_qpathinfo_alt_name(struct cli_state *cli, const char *fname, fstrin
 
        return NT_STATUS_OK;
 }
+
+/****************************************************************************
+ Send a qpathinfo SMB_QUERY_FILE_STANDARD_INFO call.
+****************************************************************************/
+
+NTSTATUS cli_qpathinfo_standard(struct cli_state *cli, const char *fname,
+                               uint64_t *allocated, uint64_t *size,
+                               uint32_t *nlinks,
+                               bool *is_del_pending, bool *is_dir)
+{
+       uint8_t *rdata;
+       uint32_t num_rdata;
+       NTSTATUS status;
+
+       if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+               return NT_STATUS_NOT_IMPLEMENTED;
+       }
+
+       status = cli_qpathinfo(talloc_tos(), cli, fname,
+                              SMB_QUERY_FILE_STANDARD_INFO,
+                              24, CLI_BUFFER_SIZE, &rdata, &num_rdata);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       if (allocated) {
+               *allocated = BVAL(rdata, 0);
+       }
+
+       if (size) {
+               *size = BVAL(rdata, 8);
+       }
+
+       if (nlinks) {
+               *nlinks = IVAL(rdata, 16);
+       }
+
+       if (is_del_pending) {
+               *is_del_pending = CVAL(rdata, 20);
+       }
+
+       if (is_dir) {
+               *is_dir = CVAL(rdata, 20);
+       }
+
+       TALLOC_FREE(rdata);
+
+       return NT_STATUS_OK;
+}
+
+
+/* like cli_qpathinfo2 but do not use SMB_QUERY_FILE_ALL_INFO with smb1 */
+NTSTATUS cli_qpathinfo3(struct cli_state *cli, const char *fname,
+                       struct timespec *create_time,
+                       struct timespec *access_time,
+                       struct timespec *write_time,
+                       struct timespec *change_time,
+                       off_t *size, uint32_t *pattr,
+                       SMB_INO_T *ino)
+{
+       NTSTATUS status = NT_STATUS_OK;
+       SMB_STRUCT_STAT st = { 0 };
+       uint32_t attr = 0;
+       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;
+               }
+       }
+
+       if (size) {
+               status = cli_qpathinfo_standard(cli, fname,
+                                               NULL, &pos, NULL, NULL, NULL);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return status;
+               }
+
+               *size = pos;
+       }
+
+       if (create_time) {
+               *create_time = st.st_ex_btime;
+       }
+       if (access_time) {
+               *access_time = st.st_ex_atime;
+       }
+       if (write_time) {
+               *write_time = st.st_ex_mtime;
+       }
+       if (change_time) {
+               *change_time = st.st_ex_ctime;
+       }
+       if (pattr) {
+               *pattr = attr;
+       }
+       if (ino) {
+               *ino = 0;
+       }
+
+       return NT_STATUS_OK;
+}