s3/libsmb: adjust smb2 code for new idl structs & generated ndr push/pull funcs.
[samba.git] / source3 / libsmb / cli_smb2_fnum.c
index f8861e53345c0ff2b027e6523468e1d57374e002..3537932c0d08481ae89ae65223ac240ea5f33eef 100644 (file)
@@ -41,6 +41,7 @@
 #include "lib/util_ea.h"
 #include "librpc/gen_ndr/ndr_ioctl.h"
 #include "ntioctl.h"
+#include "librpc/gen_ndr/ndr_quota.h"
 
 struct smb2_hnd {
        uint64_t fid_persistent;
@@ -686,6 +687,27 @@ NTSTATUS cli_smb2_rmdir(struct cli_state *cli, const char *dname)
                        &fnum,
                        NULL);
 
+       if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK)) {
+               /*
+                * Naive option to match our SMB1 code. Assume the
+                * symlink path that tripped us up was the last
+                * component and try again. Eventually we will have to
+                * deal with the returned path unprocessed component. JRA.
+                */
+               status = cli_smb2_create_fnum(cli,
+                       dname,
+                       0,                      /* create_flags */
+                       DELETE_ACCESS,          /* desired_access */
+                       FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
+                       FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */
+                       FILE_OPEN,              /* create_disposition */
+                       FILE_DIRECTORY_FILE|
+                               FILE_DELETE_ON_CLOSE|
+                               FILE_OPEN_REPARSE_POINT, /* create_options */
+                       &fnum,
+                       NULL);
+       }
+
        if (!NT_STATUS_IS_OK(status)) {
                return status;
        }
@@ -724,6 +746,26 @@ NTSTATUS cli_smb2_unlink(struct cli_state *cli, const char *fname)
                        &fnum,
                        NULL);
 
+       if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK)) {
+               /*
+                * Naive option to match our SMB1 code. Assume the
+                * symlink path that tripped us up was the last
+                * component and try again. Eventually we will have to
+                * deal with the returned path unprocessed component. JRA.
+                */
+               status = cli_smb2_create_fnum(cli,
+                       fname,
+                       0,                      /* create_flags */
+                       DELETE_ACCESS,          /* desired_access */
+                       FILE_ATTRIBUTE_NORMAL, /* file attributes */
+                       FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */
+                       FILE_OPEN,              /* create_disposition */
+                       FILE_DELETE_ON_CLOSE|
+                               FILE_OPEN_REPARSE_POINT, /* create_options */
+                       &fnum,
+                       NULL);
+       }
+
        if (!NT_STATUS_IS_OK(status)) {
                return status;
        }
@@ -762,6 +804,7 @@ static NTSTATUS parse_finfo_id_both_directory_info(uint8_t *dir_data,
                return NT_STATUS_INFO_LENGTH_MISMATCH;
        }
 
+       finfo->btime_ts = interpret_long_date((const char *)dir_data + 8);
        finfo->atime_ts = interpret_long_date((const char *)dir_data + 16);
        finfo->mtime_ts = interpret_long_date((const char *)dir_data + 24);
        finfo->ctime_ts = interpret_long_date((const char *)dir_data + 32);
@@ -861,6 +904,7 @@ NTSTATUS cli_smb2_list(struct cli_state *cli,
        TALLOC_CTX *frame = talloc_stackframe();
        TALLOC_CTX *subframe = NULL;
        bool mask_has_wild;
+       uint32_t max_trans = smb2cli_conn_max_trans_size(cli->conn);
 
        if (smbXcli_conn_has_async_calls(cli->conn)) {
                /*
@@ -924,7 +968,7 @@ NTSTATUS cli_smb2_list(struct cli_state *cli,
                                        ph->fid_persistent,
                                        ph->fid_volatile,
                                        mask,
-                                       0xffff,
+                                       max_trans,
                                        subframe,
                                        &dir_data,
                                        &dir_data_length);
@@ -1157,6 +1201,7 @@ static NTSTATUS get_fnum_from_path(struct cli_state *cli,
        NTSTATUS status;
        size_t namelen = strlen(name);
        TALLOC_CTX *frame = talloc_stackframe();
+       uint32_t create_options = 0;
 
        /* SMB2 is pickier about pathnames. Ensure it doesn't
           end in a '\' */
@@ -1178,11 +1223,32 @@ static NTSTATUS get_fnum_from_path(struct cli_state *cli,
                        0, /* file attributes */
                        FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */
                        FILE_OPEN,              /* create_disposition */
-                       0,      /* create_options */
+                       create_options,
                        pfnum,
                        NULL);
 
+       if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK)) {
+               /*
+                * Naive option to match our SMB1 code. Assume the
+                * symlink path that tripped us up was the last
+                * component and try again. Eventually we will have to
+                * deal with the returned path unprocessed component. JRA.
+                */
+               create_options |= FILE_OPEN_REPARSE_POINT;
+               status = cli_smb2_create_fnum(cli,
+                       name,
+                       0,                      /* create_flags */
+                       desired_access,
+                       0, /* file attributes */
+                       FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */
+                       FILE_OPEN,              /* create_disposition */
+                       create_options,
+                       pfnum,
+                       NULL);
+       }
+
        if (NT_STATUS_EQUAL(status, NT_STATUS_FILE_IS_A_DIRECTORY)) {
+               create_options |= FILE_DIRECTORY_FILE;
                status = cli_smb2_create_fnum(cli,
                        name,
                        0,                      /* create_flags */
@@ -2845,12 +2911,16 @@ NTSTATUS cli_smb2_get_user_quota(struct cli_state *cli,
 {
        NTSTATUS status;
        DATA_BLOB inbuf = data_blob_null;
+       DATA_BLOB info_blob = data_blob_null;
        DATA_BLOB outbuf = data_blob_null;
        struct smb2_hnd *ph = NULL;
        TALLOC_CTX *frame = talloc_stackframe();
        unsigned sid_len;
        unsigned int offset;
-       uint8_t *buf;
+       struct smb2_query_quota_info query = {0};
+       struct file_get_quota_info info = {0};
+       enum ndr_err_code err;
+       struct ndr_push *ndr_push = NULL;
 
        if (smbXcli_conn_has_async_calls(cli->conn)) {
                /*
@@ -2872,27 +2942,52 @@ NTSTATUS cli_smb2_get_user_quota(struct cli_state *cli,
 
        sid_len = ndr_size_dom_sid(&pqt->sid, 0);
 
-       inbuf = data_blob_talloc_zero(frame, 24 + sid_len);
-       if (inbuf.data == NULL) {
+       query.return_single = 1;
+       if (sid_len < 0) {
+               status = NT_STATUS_INVALID_PARAMETER;
+               goto fail;
+       }
+
+       info.next_entry_offset = 0;
+       info.sid_length = sid_len;
+       info.sid = pqt->sid;
+
+       err = ndr_push_struct_blob(
+                       &info_blob,
+                       frame,
+                       &info,
+                       (ndr_push_flags_fn_t)ndr_push_file_get_quota_info);
+
+       if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
+               status = NT_STATUS_INTERNAL_ERROR;
+               goto fail;
+       }
+
+       query.sid_list_length = info_blob.length;
+       ndr_push = ndr_push_init_ctx(frame);
+       if (!ndr_push) {
                status = NT_STATUS_NO_MEMORY;
                goto fail;
        }
 
-       buf = inbuf.data;
+       err = ndr_push_smb2_query_quota_info(ndr_push,
+                                            NDR_SCALARS | NDR_BUFFERS,
+                                            &query);
 
-       SCVAL(buf, 0, 1);          /* ReturnSingle */
-       SCVAL(buf, 1, 0);          /* RestartScan */
-       SSVAL(buf, 2, 0);          /* Reserved */
-       if (8 + sid_len < 8) {
-               status = NT_STATUS_INVALID_PARAMETER;
+       if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
+               status = NT_STATUS_INTERNAL_ERROR;
                goto fail;
        }
-       SIVAL(buf, 4, 8 + sid_len); /* SidListLength */
-       SIVAL(buf, 8, 0);          /* StartSidLength */
-       SIVAL(buf, 12, 0);        /* StartSidOffset */
-       SIVAL(buf, 16, 0);        /* NextEntryOffset */
-       SIVAL(buf, 20, sid_len);    /* SidLength */
-       sid_linearize(buf + 24, sid_len, &pqt->sid);
+
+       err = ndr_push_array_uint8(ndr_push, NDR_SCALARS, info_blob.data,
+                                  info_blob.length);
+
+       if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
+               status = NT_STATUS_INTERNAL_ERROR;
+               goto fail;
+       }
+       inbuf.data = ndr_push->data;
+       inbuf.length = ndr_push->offset;
 
        status = smb2cli_query_info(cli->conn, cli->timeout, cli->smb2.session,
                                    cli->smb2.tcon, 4, /* in_info_type */
@@ -2937,7 +3032,8 @@ NTSTATUS cli_smb2_list_user_quota_step(struct cli_state *cli,
        DATA_BLOB outbuf = data_blob_null;
        struct smb2_hnd *ph = NULL;
        TALLOC_CTX *frame = talloc_stackframe();
-       uint8_t *buf;
+       struct smb2_query_quota_info info = {0};
+       enum ndr_err_code err;
 
        if (smbXcli_conn_has_async_calls(cli->conn)) {
                /*
@@ -2957,20 +3053,19 @@ NTSTATUS cli_smb2_list_user_quota_step(struct cli_state *cli,
                goto cleanup;
        }
 
-       inbuf = data_blob_talloc_zero(frame, 16);
-       if (inbuf.data == NULL) {
-               status = NT_STATUS_NO_MEMORY;
-               goto cleanup;
-       }
 
-       buf = inbuf.data;
+       info.restart_scan = first ? 1 : 0;
 
-       SCVAL(buf, 0, 0);            /* ReturnSingle */
-       SCVAL(buf, 1, first ? 1 : 0); /* RestartScan */
-       SSVAL(buf, 2, 0);            /* Reserved */
-       SIVAL(buf, 4, 0);            /* SidListLength */
-       SIVAL(buf, 8, 0);            /* StartSidLength */
-       SIVAL(buf, 12, 0);          /* StartSidOffset */
+       err = ndr_push_struct_blob(
+                       &inbuf,
+                       frame,
+                       &info,
+                       (ndr_push_flags_fn_t)ndr_push_smb2_query_quota_info);
+
+       if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
+               status = NT_STATUS_INTERNAL_ERROR;
+               goto cleanup;
+       }
 
        status = smb2cli_query_info(cli->conn, cli->timeout, cli->smb2.session,
                                    cli->smb2.tcon, 4, /* in_info_type */
@@ -2982,6 +3077,14 @@ NTSTATUS cli_smb2_list_user_quota_step(struct cli_state *cli,
                                    ph->fid_persistent, ph->fid_volatile, frame,
                                    &outbuf);
 
+       /*
+        * safeguard against panic from calling parse_user_quota_list with
+        * NULL buffer
+        */
+       if (NT_STATUS_IS_OK(status) && outbuf.length == 0) {
+               status = NT_STATUS_NO_MORE_ENTRIES;
+       }
+
        if (!NT_STATUS_IS_OK(status)) {
                goto cleanup;
        }
@@ -4129,6 +4232,15 @@ NTSTATUS cli_smb2_notify(struct cli_state *cli, uint16_t fnum,
                                completion_filter, recursive,
                                frame, &base, &len);
 
+       if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) {
+               len = 0;
+               status = NT_STATUS_OK;
+       }
+
+       if (!NT_STATUS_IS_OK(status)) {
+               goto fail;
+       }
+
        ofs = 0;
 
        while (len - ofs >= 12) {
@@ -4274,3 +4386,103 @@ NTSTATUS cli_smb2_set_reparse_point_fnum_recv(struct tevent_req *req)
 {
         return tevent_req_simple_recv_ntstatus(req);
 }
+
+struct cli_smb2_get_reparse_point_fnum_state {
+       struct cli_state *cli;
+       uint16_t fnum;
+       struct smb2_hnd *ph;
+       DATA_BLOB output_buffer;
+};
+
+static void cli_smb2_get_reparse_point_fnum_done(struct tevent_req *subreq);
+
+struct tevent_req *cli_smb2_get_reparse_point_fnum_send(
+                               TALLOC_CTX *mem_ctx,
+                               struct tevent_context *ev,
+                               struct cli_state *cli,
+                               uint16_t fnum)
+{
+       struct tevent_req *req, *subreq;
+       struct cli_smb2_set_reparse_point_fnum_state *state = NULL;
+       NTSTATUS status;
+
+       req = tevent_req_create(mem_ctx, &state,
+                               struct cli_smb2_get_reparse_point_fnum_state);
+       if (req == NULL) {
+               return NULL;
+       }
+
+       if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
+               tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+               return tevent_req_post(req, ev);
+       }
+
+       state->cli = cli;
+       state->fnum = fnum;
+
+       status = map_fnum_to_smb2_handle(cli, fnum, &state->ph);
+       if (tevent_req_nterror(req, status)) {
+               return tevent_req_post(req, ev);
+       }
+
+       subreq = smb2cli_ioctl_send(state, ev, state->cli->conn,
+                       state->cli->timeout,
+                       state->cli->smb2.session,
+                       state->cli->smb2.tcon,
+                       state->ph->fid_persistent, /* in_fid_persistent */
+                       state->ph->fid_volatile, /* in_fid_volatile */
+                       FSCTL_GET_REPARSE_POINT,
+                       0, /* in_max_input_length */
+                       NULL,
+                       64*1024,
+                       NULL,
+                       SMB2_IOCTL_FLAG_IS_FSCTL);
+
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(subreq,
+                               cli_smb2_get_reparse_point_fnum_done,
+                               req);
+
+       return req;
+}
+
+static void cli_smb2_get_reparse_point_fnum_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct cli_smb2_get_reparse_point_fnum_state *state = tevent_req_data(
+               req, struct cli_smb2_get_reparse_point_fnum_state);
+       NTSTATUS status;
+
+       status = smb2cli_ioctl_recv(subreq, state,
+                               NULL,
+                               &state->output_buffer);
+       TALLOC_FREE(subreq);
+       if (tevent_req_nterror(req, status)) {
+               state->cli->raw_status = status;
+               return;
+       }
+       tevent_req_done(req);
+}
+
+NTSTATUS cli_smb2_get_reparse_point_fnum_recv(struct tevent_req *req,
+                               TALLOC_CTX *mem_ctx,
+                               DATA_BLOB *output)
+{
+       struct cli_smb2_get_reparse_point_fnum_state *state = tevent_req_data(
+               req, struct cli_smb2_get_reparse_point_fnum_state);
+
+       if (tevent_req_is_nterror(req, &state->cli->raw_status)) {
+               tevent_req_received(req);
+               return state->cli->raw_status;
+       }
+       *output = data_blob_dup_talloc(mem_ctx, state->output_buffer);
+       if (output->data == NULL) {
+               tevent_req_received(req);
+               return NT_STATUS_NO_MEMORY;
+       }
+       tevent_req_received(req);
+       return NT_STATUS_OK;
+}