s3: libsmb: Add cli_smb2_chkpath() and use from cli_chkpath().
[sfrench/samba-autobuild/.git] / source3 / libsmb / cli_smb2_fnum.c
index 351fccfb6453d07ddd5420ee5c04628232d848a6..b8179b0a48c8b241bfc7663b3cd5a992b81e6ddf 100644 (file)
@@ -485,6 +485,133 @@ NTSTATUS cli_smb2_close_fnum(struct cli_state *cli, uint16_t fnum)
        return status;
 }
 
+struct cli_smb2_delete_on_close_state {
+       struct cli_state *cli;
+       uint16_t fnum;
+       struct smb2_hnd *ph;
+       uint8_t data[1];
+       DATA_BLOB inbuf;
+};
+
+static void cli_smb2_delete_on_close_done(struct tevent_req *subreq);
+
+struct tevent_req *cli_smb2_delete_on_close_send(TALLOC_CTX *mem_ctx,
+                                       struct tevent_context *ev,
+                                       struct cli_state *cli,
+                                       uint16_t fnum,
+                                       bool flag)
+{
+       struct tevent_req *req = NULL;
+       struct cli_smb2_delete_on_close_state *state = NULL;
+       struct tevent_req *subreq = NULL;
+       uint8_t in_info_type;
+       uint8_t in_file_info_class;
+       NTSTATUS status;
+
+       req = tevent_req_create(mem_ctx, &state,
+                               struct cli_smb2_delete_on_close_state);
+       if (req == NULL) {
+               return NULL;
+       }
+       state->cli = cli;
+       state->fnum = fnum;
+
+       if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
+               tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+               return tevent_req_post(req, ev);
+       }
+
+       status = map_fnum_to_smb2_handle(cli, fnum, &state->ph);
+       if (tevent_req_nterror(req, status)) {
+               return tevent_req_post(req, ev);
+       }
+
+       /*
+        * setinfo on the handle with info_type SMB2_SETINFO_FILE (1),
+        * level 13 (SMB_FILE_DISPOSITION_INFORMATION - 1000).
+        */
+       in_info_type = 1;
+       in_file_info_class = SMB_FILE_DISPOSITION_INFORMATION - 1000;
+       /* Setup data array. */
+       SCVAL(&state->data[0], 0, flag ? 1 : 0);
+       state->inbuf.data = &state->data[0];
+       state->inbuf.length = 1;
+
+       subreq = smb2cli_set_info_send(state, ev,
+                                      cli->conn,
+                                      cli->timeout,
+                                      cli->smb2.session,
+                                      cli->smb2.tcon,
+                                      in_info_type,
+                                      in_file_info_class,
+                                      &state->inbuf, /* in_input_buffer */
+                                      0, /* in_additional_info */
+                                      state->ph->fid_persistent,
+                                      state->ph->fid_volatile);
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(subreq,
+                               cli_smb2_delete_on_close_done,
+                               req);
+       return req;
+}
+
+static void cli_smb2_delete_on_close_done(struct tevent_req *subreq)
+{
+       NTSTATUS status = smb2cli_set_info_recv(subreq);
+       tevent_req_simple_finish_ntstatus(subreq, status);
+}
+
+NTSTATUS cli_smb2_delete_on_close_recv(struct tevent_req *req)
+{
+       struct cli_smb2_delete_on_close_state *state =
+               tevent_req_data(req,
+               struct cli_smb2_delete_on_close_state);
+       NTSTATUS status;
+
+       if (tevent_req_is_nterror(req, &status)) {
+               state->cli->raw_status = status;
+               tevent_req_received(req);
+               return status;
+       }
+
+       state->cli->raw_status = NT_STATUS_OK;
+       tevent_req_received(req);
+       return NT_STATUS_OK;
+}
+
+NTSTATUS cli_smb2_delete_on_close(struct cli_state *cli, uint16_t fnum, bool flag)
+{
+       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_smb2_delete_on_close_send(frame, ev, cli, fnum, flag);
+       if (req == NULL) {
+               goto fail;
+       }
+       if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+               goto fail;
+       }
+       status = cli_smb2_delete_on_close_recv(req);
+ fail:
+       TALLOC_FREE(frame);
+       return status;
+}
+
 /***************************************************************
  Small wrapper that allows SMB2 to create a directory
  Synchronous only.
@@ -973,6 +1100,47 @@ NTSTATUS cli_smb2_qpathinfo_basic(struct cli_state *cli,
        return status;
 }
 
+/***************************************************************
+ Wrapper that allows SMB2 to check if a path is a directory.
+ Synchronous only.
+***************************************************************/
+
+NTSTATUS cli_smb2_chkpath(struct cli_state *cli,
+                               const char *name)
+{
+       NTSTATUS status;
+       uint16_t fnum = 0xffff;
+
+       if (smbXcli_conn_has_async_calls(cli->conn)) {
+               /*
+                * Can't use sync call while an async call is in flight
+                */
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       /* Ensure this is a directory. */
+       status = cli_smb2_create_fnum(cli,
+                       name,
+                       0,                      /* create_flags */
+                       FILE_READ_ATTRIBUTES,   /* 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,    /* create_options */
+                       &fnum,
+                       NULL);
+
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       return cli_smb2_close_fnum(cli, fnum);
+}
+
 /***************************************************************
  Helper function for pathname operations.
 ***************************************************************/
@@ -1519,20 +1687,20 @@ NTSTATUS cli_smb2_qpathinfo_streams(struct cli_state *cli,
 }
 
 /***************************************************************
- Wrapper that allows SMB2 to set pathname attributes.
+ Wrapper that allows SMB2 to set SMB_FILE_BASIC_INFORMATION on
+ a pathname.
  Synchronous only.
 ***************************************************************/
 
-NTSTATUS cli_smb2_setatr(struct cli_state *cli,
+NTSTATUS cli_smb2_setpathinfo(struct cli_state *cli,
                        const char *name,
-                       uint16_t attr,
-                       time_t mtime)
+                       uint8_t in_info_type,
+                       uint8_t in_file_info_class,
+                       const DATA_BLOB *p_in_data)
 {
        NTSTATUS status;
        uint16_t fnum = 0xffff;
        struct smb2_hnd *ph = NULL;
-       uint8_t inbuf_store[40];
-       DATA_BLOB inbuf = data_blob_null;
        TALLOC_CTX *frame = talloc_stackframe();
 
        if (smbXcli_conn_has_async_calls(cli->conn)) {
@@ -1564,29 +1732,13 @@ NTSTATUS cli_smb2_setatr(struct cli_state *cli,
                goto fail;
        }
 
-       /* setinfo on the handle with info_type SMB2_SETINFO_FILE (1),
-          level 4 (SMB_FILE_BASIC_INFORMATION - 1000). */
-
-       inbuf.data = inbuf_store;
-       inbuf.length = sizeof(inbuf_store);
-       data_blob_clear(&inbuf);
-
-       SSVAL(inbuf.data, 32, attr);
-       if (mtime != 0) {
-               put_long_date((char *)inbuf.data + 16,mtime);
-       }
-       /* Set all the other times to -1. */
-       SBVAL(inbuf.data, 0, 0xFFFFFFFFFFFFFFFFLL);
-       SBVAL(inbuf.data, 8, 0xFFFFFFFFFFFFFFFFLL);
-       SBVAL(inbuf.data, 24, 0xFFFFFFFFFFFFFFFFLL);
-
        status = smb2cli_set_info(cli->conn,
                                cli->timeout,
                                cli->smb2.session,
                                cli->smb2.tcon,
-                               1, /* in_info_type */
-                               SMB_FILE_BASIC_INFORMATION - 1000, /* in_file_info_class */
-                               &inbuf, /* in_input_buffer */
+                               in_info_type,
+                               in_file_info_class,
+                               p_in_data, /* in_input_buffer */
                                0, /* in_additional_info */
                                ph->fid_persistent,
                                ph->fid_volatile);
@@ -1602,6 +1754,68 @@ NTSTATUS cli_smb2_setatr(struct cli_state *cli,
        return status;
 }
 
+
+/***************************************************************
+ Wrapper that allows SMB2 to set pathname attributes.
+ Synchronous only.
+***************************************************************/
+
+NTSTATUS cli_smb2_setatr(struct cli_state *cli,
+                       const char *name,
+                       uint16_t attr,
+                       time_t mtime)
+{
+       uint8_t inbuf_store[40];
+       DATA_BLOB inbuf = data_blob_null;
+
+       /* setinfo on the handle with info_type SMB2_SETINFO_FILE (1),
+          level 4 (SMB_FILE_BASIC_INFORMATION - 1000). */
+
+       inbuf.data = inbuf_store;
+       inbuf.length = sizeof(inbuf_store);
+       data_blob_clear(&inbuf);
+
+       /*
+        * SMB1 uses attr == 0 to clear all attributes
+        * on a file (end up with FILE_ATTRIBUTE_NORMAL),
+        * and attr == FILE_ATTRIBUTE_NORMAL to mean ignore
+        * request attribute change.
+        *
+        * SMB2 uses exactly the reverse. Unfortunately as the
+        * cli_setatr() ABI is exposed inside libsmbclient,
+        * we must make the SMB2 cli_smb2_setatr() call
+        * export the same ABI as the SMB1 cli_setatr()
+        * which calls it. This means reversing the sense
+        * of the requested attr argument if it's zero
+        * or FILE_ATTRIBUTE_NORMAL.
+        *
+        * See BUG: https://bugzilla.samba.org/show_bug.cgi?id=12899
+        */
+
+       if (attr == 0) {
+               attr = FILE_ATTRIBUTE_NORMAL;
+       } else if (attr == FILE_ATTRIBUTE_NORMAL) {
+               attr = 0;
+       }
+
+       SSVAL(inbuf.data, 32, attr);
+       if (mtime != 0) {
+               put_long_date((char *)inbuf.data + 16,mtime);
+       }
+       /* Set all the other times to -1. */
+       SBVAL(inbuf.data, 0, 0xFFFFFFFFFFFFFFFFLL);
+       SBVAL(inbuf.data, 8, 0xFFFFFFFFFFFFFFFFLL);
+       SBVAL(inbuf.data, 24, 0xFFFFFFFFFFFFFFFFLL);
+
+       return cli_smb2_setpathinfo(cli,
+                               name,
+                               1, /* in_info_type */
+                               /* in_file_info_class */
+                               SMB_FILE_BASIC_INFORMATION - 1000,
+                               &inbuf);
+}
+
+
 /***************************************************************
  Wrapper that allows SMB2 to set file handle times.
  Synchronous only.
@@ -3644,3 +3858,95 @@ NTSTATUS cli_smb2_ftruncate(struct cli_state *cli,
        TALLOC_FREE(frame);
        return status;
 }
+
+NTSTATUS cli_smb2_notify(struct cli_state *cli, uint16_t fnum,
+                        uint32_t buffer_size, uint32_t completion_filter,
+                        bool recursive, TALLOC_CTX *mem_ctx,
+                        struct notify_change **pchanges,
+                        uint32_t *pnum_changes)
+{
+       NTSTATUS status;
+       struct smb2_hnd *ph = NULL;
+       TALLOC_CTX *frame = talloc_stackframe();
+       uint8_t *base;
+       uint32_t len, ofs;
+       struct notify_change *changes = NULL;
+       size_t num_changes = 0;
+
+       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;
+       }
+
+       if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
+               status = NT_STATUS_INVALID_PARAMETER;
+               goto fail;
+       }
+
+       status = map_fnum_to_smb2_handle(cli, fnum, &ph);
+       if (!NT_STATUS_IS_OK(status)) {
+               goto fail;
+       }
+
+       status = smb2cli_notify(cli->conn, cli->timeout,
+                               cli->smb2.session, cli->smb2.tcon,
+                               buffer_size,
+                               ph->fid_persistent, ph->fid_volatile,
+                               completion_filter, recursive,
+                               frame, &base, &len);
+
+       ofs = 0;
+
+       while (len - ofs >= 12) {
+               struct notify_change *tmp;
+               struct notify_change *c;
+               uint32_t next_ofs = IVAL(base, ofs);
+               uint32_t file_name_length = IVAL(base, ofs+8);
+               size_t namelen;
+               bool ok;
+
+               tmp = talloc_realloc(frame, changes, struct notify_change,
+                                    num_changes + 1);
+               if (tmp == NULL) {
+                       status = NT_STATUS_NO_MEMORY;
+                       goto fail;
+               }
+               changes = tmp;
+               c = &changes[num_changes];
+               num_changes += 1;
+
+               if (smb_buffer_oob(len, ofs, next_ofs) ||
+                   smb_buffer_oob(len, ofs+12, file_name_length)) {
+                       status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+                       goto fail;
+               }
+
+               c->action = IVAL(base, ofs+4);
+
+               ok = convert_string_talloc(changes, CH_UTF16LE, CH_UNIX,
+                                          base + ofs + 12, file_name_length,
+                                          &c->name, &namelen);
+               if (!ok) {
+                       status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+                       goto fail;
+               }
+
+               if (next_ofs == 0) {
+                       break;
+               }
+               ofs += next_ofs;
+       }
+
+       *pchanges = talloc_move(mem_ctx, &changes);
+       *pnum_changes = num_changes;
+       status = NT_STATUS_OK;
+
+fail:
+       cli->raw_status = status;
+
+       TALLOC_FREE(frame);
+       return status;
+}