libsmb: Pass "flags" through cli_close_send() and pylibsmb
[samba.git] / source3 / libsmb / pylibsmb.c
index 5f3e6a8c724ac1a681778925997485e443f04bcd..9fa7ea278eae05b15d12437b06d570917ad879ce 100644 (file)
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
+/*
+Template code to use this library:
+
+-------------------------
+from samba.samba3 import libsmb_samba_internal as libsmb
+from samba.samba3 import param as s3param
+from samba import (credentials,NTSTATUSError)
+
+lp = s3param.get_context()
+lp.load("/etc/samba/smb.conf");
+
+creds = credentials.Credentials()
+creds.guess(lp)
+creds.set_username("administrator")
+creds.set_password("1234")
+
+c = libsmb.Conn("127.0.0.1",
+                "tmp",
+                lp,
+                creds,
+                multi_threaded=True)
+-------------------------
+*/
+
 #include <Python.h>
 #include "includes.h"
 #include "python/py3compat.h"
+#include "python/modules.h"
 #include "libcli/smb/smbXcli_base.h"
+#include "libcli/smb/smb2_negotiate_context.h"
+#include "libcli/smb/reparse.h"
 #include "libsmb/libsmb.h"
 #include "libcli/security/security.h"
 #include "system/select.h"
 #define LIST_ATTRIBUTE_MASK \
        (FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_HIDDEN)
 
-#define SECINFO_DEFAULT_FLAGS \
-       (SECINFO_OWNER | SECINFO_GROUP | \
-        SECINFO_DACL | SECINFO_PROTECTED_DACL | SECINFO_UNPROTECTED_DACL | \
-        SECINFO_SACL | SECINFO_PROTECTED_SACL | SECINFO_UNPROTECTED_SACL)
+static PyTypeObject *dom_sid_Type = NULL;
 
 static PyTypeObject *get_pytype(const char *module, const char *type)
 {
@@ -94,7 +118,6 @@ struct py_cli_oplock_break {
 struct py_cli_state {
        PyObject_HEAD
        struct cli_state *cli;
-       bool is_smb1;
        struct tevent_context *ev;
        int (*req_wait_fn)(struct tevent_context *ev,
                           struct tevent_req *req);
@@ -418,7 +441,6 @@ static PyObject *py_cli_state_new(PyTypeObject *type, PyObject *args,
                return NULL;
        }
        self->cli = NULL;
-       self->is_smb1 = false;
        self->ev = NULL;
        self->thread_state = NULL;
        self->oplock_waiter = NULL;
@@ -427,6 +449,86 @@ static PyObject *py_cli_state_new(PyTypeObject *type, PyObject *args,
        return (PyObject *)self;
 }
 
+static struct smb2_negotiate_contexts *py_cli_get_negotiate_contexts(
+       TALLOC_CTX *mem_ctx, PyObject *list)
+{
+       struct smb2_negotiate_contexts *ctxs = NULL;
+       Py_ssize_t i, len;
+       int ret;
+
+       ret = PyList_Check(list);
+       if (!ret) {
+               goto fail;
+       }
+
+       len = PyList_Size(list);
+       if (len == 0) {
+               goto fail;
+       }
+
+       ctxs = talloc_zero(mem_ctx, struct smb2_negotiate_contexts);
+       if (ctxs == NULL) {
+               goto fail;
+       }
+
+       for (i=0; i<len; i++) {
+               NTSTATUS status;
+
+               PyObject *t = PyList_GetItem(list, i);
+               Py_ssize_t tlen;
+
+               PyObject *ptype = NULL;
+               long type;
+
+               PyObject *pdata = NULL;
+               DATA_BLOB data = { .data = NULL, };
+
+               if (t == NULL) {
+                       goto fail;
+               }
+
+               ret = PyTuple_Check(t);
+               if (!ret) {
+                       goto fail;
+               }
+
+               tlen = PyTuple_Size(t);
+               if (tlen != 2) {
+                       goto fail;
+               }
+
+               ptype = PyTuple_GetItem(t, 0);
+               if (ptype == NULL) {
+                       goto fail;
+               }
+               type = PyLong_AsLong(ptype);
+               if ((type < 0) || (type > UINT16_MAX)) {
+                       goto fail;
+               }
+
+               pdata = PyTuple_GetItem(t, 1);
+
+               ret = PyBytes_Check(pdata);
+               if (!ret) {
+                       goto fail;
+               }
+
+               data.data = (uint8_t *)PyBytes_AsString(pdata);
+               data.length = PyBytes_Size(pdata);
+
+               status = smb2_negotiate_context_add(
+                       ctxs, ctxs, type, data.data, data.length);
+               if (!NT_STATUS_IS_OK(status)) {
+                       goto fail;
+               }
+       }
+       return ctxs;
+
+fail:
+       TALLOC_FREE(ctxs);
+       return NULL;
+}
+
 static void py_cli_got_oplock_break(struct tevent_req *req);
 
 static int py_cli_state_init(struct py_cli_state *self, PyObject *args,
@@ -439,18 +541,24 @@ static int py_cli_state_init(struct py_cli_state *self, PyObject *args,
        PyObject *py_lp = Py_None;
        PyObject *py_multi_threaded = Py_False;
        bool multi_threaded = false;
-       PyObject *py_sign = Py_False;
-       bool sign = false;
-       int signing_state = SMB_SIGNING_DEFAULT;
        PyObject *py_force_smb1 = Py_False;
        bool force_smb1 = false;
+       PyObject *py_ipc = Py_False;
+       PyObject *py_posix = Py_False;
+       PyObject *py_negotiate_contexts = NULL;
+       struct smb2_negotiate_contexts *negotiate_contexts = NULL;
+       bool use_ipc = false;
+       bool request_posix = false;
        struct tevent_req *req;
        bool ret;
        int flags = 0;
 
        static const char *kwlist[] = {
                "host", "share", "lp", "creds",
-               "multi_threaded", "sign", "force_smb1",
+               "multi_threaded", "force_smb1",
+               "ipc",
+               "posix",
+               "negotiate_contexts",
                NULL
        };
 
@@ -461,12 +569,14 @@ static int py_cli_state_init(struct py_cli_state *self, PyObject *args,
        }
 
        ret = ParseTupleAndKeywords(
-               args, kwds, "ssO|O!OOO", kwlist,
+               args, kwds, "ssO|O!OOOOO", kwlist,
                &host, &share, &py_lp,
                py_type_Credentials, &creds,
                &py_multi_threaded,
-               &py_sign,
-               &py_force_smb1);
+               &py_force_smb1,
+               &py_ipc,
+               &py_posix,
+               &py_negotiate_contexts);
 
        Py_DECREF(py_type_Credentials);
 
@@ -475,13 +585,8 @@ static int py_cli_state_init(struct py_cli_state *self, PyObject *args,
        }
 
        multi_threaded = PyObject_IsTrue(py_multi_threaded);
-       sign = PyObject_IsTrue(py_sign);
        force_smb1 = PyObject_IsTrue(py_force_smb1);
 
-       if (sign) {
-               signing_state = SMB_SIGNING_REQUIRED;
-       }
-
        if (force_smb1) {
                /*
                 * As most of the cli_*_send() function
@@ -492,6 +597,24 @@ static int py_cli_state_init(struct py_cli_state *self, PyObject *args,
                flags = CLI_FULL_CONNECTION_FORCE_SMB1;
        }
 
+       use_ipc = PyObject_IsTrue(py_ipc);
+       if (use_ipc) {
+               flags |= CLI_FULL_CONNECTION_IPC;
+       }
+
+       request_posix = PyObject_IsTrue(py_posix);
+       if (request_posix) {
+               flags |= CLI_FULL_CONNECTION_REQUEST_POSIX;
+       }
+
+       if (py_negotiate_contexts != NULL) {
+               negotiate_contexts = py_cli_get_negotiate_contexts(
+                       talloc_tos(), py_negotiate_contexts);
+               if (negotiate_contexts == NULL) {
+                       return -1;
+               }
+       }
+
        if (multi_threaded) {
 #ifdef HAVE_PTHREAD
                ret = py_cli_state_setup_mt_ev(self);
@@ -503,12 +626,6 @@ static int py_cli_state_init(struct py_cli_state *self, PyObject *args,
                                "No PTHREAD support available");
                return -1;
 #endif
-               if (!force_smb1) {
-                       PyErr_SetString(PyExc_RuntimeError,
-                                       "multi_threaded is only possible on "
-                                       "SMB1 connections");
-                       return -1;
-               }
        } else {
                ret = py_cli_state_setup_ev(self);
                if (!ret) {
@@ -524,7 +641,8 @@ static int py_cli_state_init(struct py_cli_state *self, PyObject *args,
 
        req = cli_full_connection_creds_send(
                NULL, self->ev, "myname", host, NULL, 0, share, "?????",
-               cli_creds, flags, signing_state);
+               cli_creds, flags,
+               negotiate_contexts);
        if (!py_tevent_req_wait_exc(self, req)) {
                return -1;
        }
@@ -536,10 +654,6 @@ static int py_cli_state_init(struct py_cli_state *self, PyObject *args,
                return -1;
        }
 
-       if (smbXcli_conn_protocol(self->cli->conn) < PROTOCOL_SMB2_02) {
-               self->is_smb1 = true;
-       }
-
        /*
         * Oplocks require a multi threaded connection
         */
@@ -681,7 +795,25 @@ static PyObject *py_cli_settimeout(struct py_cli_state *self, PyObject *args)
 
        omsecs = cli_set_timeout(self->cli, nmsecs);
 
-       return PyInt_FromLong(omsecs);
+       return PyLong_FromLong(omsecs);
+}
+
+static PyObject *py_cli_echo(struct py_cli_state *self,
+                            PyObject *Py_UNUSED(ignored))
+{
+       DATA_BLOB data = data_blob_string_const("keepalive");
+       struct tevent_req *req = NULL;
+       NTSTATUS status;
+
+       req = cli_echo_send(NULL, self->ev, self->cli, 1, data);
+       if (!py_tevent_req_wait_exc(self, req)) {
+               return NULL;
+       }
+       status = cli_echo_recv(req);
+       TALLOC_FREE(req);
+       PyErr_NTSTATUS_NOT_OK_RAISE(status);
+
+       Py_RETURN_NONE;
 }
 
 static PyObject *py_cli_create(struct py_cli_state *self, PyObject *args,
@@ -730,17 +862,394 @@ static PyObject *py_cli_create(struct py_cli_state *self, PyObject *args,
        return Py_BuildValue("I", (unsigned)fnum);
 }
 
+static struct smb2_create_blobs *py_cli_get_create_contexts(
+       TALLOC_CTX *mem_ctx, PyObject *list)
+{
+       struct smb2_create_blobs *ctxs = NULL;
+       Py_ssize_t i, len;
+       int ret;
+
+       ret = PyList_Check(list);
+       if (!ret) {
+               goto fail;
+       }
+
+       len = PyList_Size(list);
+       if (len == 0) {
+               goto fail;
+       }
+
+       ctxs = talloc_zero(mem_ctx, struct smb2_create_blobs);
+       if (ctxs == NULL) {
+               goto fail;
+       }
+
+       for (i=0; i<len; i++) {
+               NTSTATUS status;
+
+               PyObject *t = NULL;
+               Py_ssize_t tlen;
+
+               PyObject *pname = NULL;
+               char *name = NULL;
+
+               PyObject *pdata = NULL;
+               DATA_BLOB data = { .data = NULL, };
+
+               t = PyList_GetItem(list, i);
+               if (t == NULL) {
+                       goto fail;
+               }
+
+               ret = PyTuple_Check(t);
+               if (!ret) {
+                       goto fail;
+               }
+
+               tlen = PyTuple_Size(t);
+               if (tlen != 2) {
+                       goto fail;
+               }
+
+               pname = PyTuple_GetItem(t, 0);
+               if (pname == NULL) {
+                       goto fail;
+               }
+               ret = PyBytes_Check(pname);
+               if (!ret) {
+                       goto fail;
+               }
+               name = PyBytes_AsString(pname);
+
+               pdata = PyTuple_GetItem(t, 1);
+               if (pdata == NULL) {
+                       goto fail;
+               }
+               ret = PyBytes_Check(pdata);
+               if (!ret) {
+                       goto fail;
+               }
+               data = (DATA_BLOB) {
+                       .data = (uint8_t *)PyBytes_AsString(pdata),
+                       .length = PyBytes_Size(pdata),
+               };
+               status = smb2_create_blob_add(ctxs, ctxs, name, data);
+               if (!NT_STATUS_IS_OK(status)) {
+                       goto fail;
+               }
+       }
+       return ctxs;
+
+fail:
+       TALLOC_FREE(ctxs);
+       return NULL;
+}
+
+static PyObject *py_cli_create_contexts(const struct smb2_create_blobs *blobs)
+{
+       PyObject *py_blobs = NULL;
+       uint32_t i;
+
+       if (blobs == NULL) {
+               Py_RETURN_NONE;
+       }
+
+       py_blobs = PyList_New(blobs->num_blobs);
+       if (py_blobs == NULL) {
+               return NULL;
+       }
+
+       for (i=0; i<blobs->num_blobs; i++) {
+               struct smb2_create_blob *blob = &blobs->blobs[i];
+               PyObject *py_blob = NULL;
+               int ret;
+
+               py_blob = Py_BuildValue(
+                       "(yy#)",
+                       blob->tag,
+                       blob->data.data,
+                       (int)blob->data.length);
+               if (py_blob == NULL) {
+                       goto fail;
+               }
+
+               ret = PyList_SetItem(py_blobs, i, py_blob);
+               if (ret == -1) {
+                       Py_XDECREF(py_blob);
+                       goto fail;
+               }
+       }
+       return py_blobs;
+
+fail:
+       Py_XDECREF(py_blobs);
+       return NULL;
+}
+
+static PyObject *py_cli_create_returns(const struct smb_create_returns *r)
+{
+       PyObject *v = NULL;
+
+       v = Py_BuildValue(
+               "{sLsLsLsLsLsLsLsLsLsL}",
+               "oplock_level",
+               (unsigned long long)r->oplock_level,
+               "flags",
+               (unsigned long long)r->flags,
+               "create_action",
+               (unsigned long long)r->create_action,
+               "creation_time",
+               (unsigned long long)r->creation_time,
+               "last_access_time",
+               (unsigned long long)r->last_access_time,
+               "last_write_time",
+               (unsigned long long)r->last_write_time,
+               "change_time",
+               (unsigned long long)r->change_time,
+               "allocation_size",
+               (unsigned long long)r->allocation_size,
+               "end_of_file",
+               (unsigned long long)r->end_of_file,
+               "file_attributes",
+               (unsigned long long)r->file_attributes);
+       return v;
+}
+
+static PyObject *py_cli_symlink_error(const struct symlink_reparse_struct *s)
+{
+       char *subst_utf8 = NULL, *print_utf8 = NULL;
+       size_t subst_utf8_len, print_utf8_len;
+       PyObject *v = NULL;
+       bool ok = true;
+
+       /*
+        * Python wants utf-8, regardless of our unix charset (which
+        * most likely is utf-8 these days, but you never know).
+        */
+
+       ok = convert_string_talloc(
+               talloc_tos(),
+               CH_UNIX,
+               CH_UTF8,
+               s->substitute_name,
+               strlen(s->substitute_name),
+               &subst_utf8,
+               &subst_utf8_len);
+       if (!ok) {
+               goto fail;
+       }
+
+       ok = convert_string_talloc(
+               talloc_tos(),
+               CH_UNIX,
+               CH_UTF8,
+               s->print_name,
+               strlen(s->print_name),
+               &print_utf8,
+               &print_utf8_len);
+       if (!ok) {
+               goto fail;
+       }
+
+       v = Py_BuildValue(
+               "{sLsssssL}",
+               "unparsed_path_length",
+               (unsigned long long)s->unparsed_path_length,
+               "substitute_name",
+               subst_utf8,
+               "print_name",
+               print_utf8,
+               "flags",
+               (unsigned long long)s->flags);
+
+fail:
+       TALLOC_FREE(subst_utf8);
+       TALLOC_FREE(print_utf8);
+       return v;
+}
+
+static PyObject *py_cli_create_ex(
+       struct py_cli_state *self, PyObject *args, PyObject *kwds)
+{
+       char *fname = NULL;
+       unsigned CreateFlags = 0;
+       unsigned DesiredAccess = FILE_GENERIC_READ;
+       unsigned FileAttributes = 0;
+       unsigned ShareAccess = 0;
+       unsigned CreateDisposition = FILE_OPEN;
+       unsigned CreateOptions = 0;
+       unsigned ImpersonationLevel = SMB2_IMPERSONATION_IMPERSONATION;
+       unsigned SecurityFlags = 0;
+       PyObject *py_create_contexts_in = NULL;
+       PyObject *py_create_contexts_out = NULL;
+       struct smb2_create_blobs *create_contexts_in = NULL;
+       struct smb2_create_blobs create_contexts_out = { .num_blobs = 0 };
+       struct smb_create_returns cr = { .create_action = 0, };
+       struct symlink_reparse_struct *symlink = NULL;
+       PyObject *py_cr = NULL;
+       uint16_t fnum;
+       struct tevent_req *req;
+       NTSTATUS status;
+       int ret;
+       bool ok;
+       PyObject *v = NULL;
+
+       static const char *kwlist[] = {
+               "Name",
+               "CreateFlags",
+               "DesiredAccess",
+               "FileAttributes",
+               "ShareAccess",
+               "CreateDisposition",
+               "CreateOptions",
+               "ImpersonationLevel",
+               "SecurityFlags",
+               "CreateContexts",
+               NULL };
+
+       ret = ParseTupleAndKeywords(
+               args,
+               kwds,
+               "s|IIIIIIIIO",
+               kwlist,
+               &fname,
+               &CreateFlags,
+               &DesiredAccess,
+               &FileAttributes,
+               &ShareAccess,
+               &CreateDisposition,
+               &CreateOptions,
+               &ImpersonationLevel,
+               &SecurityFlags,
+               &py_create_contexts_in);
+       if (!ret) {
+               return NULL;
+       }
+
+       if (py_create_contexts_in != NULL) {
+               create_contexts_in = py_cli_get_create_contexts(
+                       NULL, py_create_contexts_in);
+               if (create_contexts_in == NULL) {
+                       errno = EINVAL;
+                       PyErr_SetFromErrno(PyExc_RuntimeError);
+                       return NULL;
+               }
+       }
+
+       if (smbXcli_conn_protocol(self->cli->conn) >= PROTOCOL_SMB2_02) {
+               struct cli_smb2_create_flags cflags = {
+                       .batch_oplock = (CreateFlags & REQUEST_BATCH_OPLOCK),
+                       .exclusive_oplock = (CreateFlags & REQUEST_OPLOCK),
+               };
+
+               req = cli_smb2_create_fnum_send(
+                       NULL,
+                       self->ev,
+                       self->cli,
+                       fname,
+                       cflags,
+                       ImpersonationLevel,
+                       DesiredAccess,
+                       FileAttributes,
+                       ShareAccess,
+                       CreateDisposition,
+                       CreateOptions,
+                       create_contexts_in);
+       } else {
+               req = cli_ntcreate_send(
+                       NULL,
+                       self->ev,
+                       self->cli,
+                       fname,
+                       CreateFlags,
+                       DesiredAccess,
+                       FileAttributes,
+                       ShareAccess,
+                       CreateDisposition,
+                       CreateOptions,
+                       ImpersonationLevel,
+                       SecurityFlags);
+       }
+
+       TALLOC_FREE(create_contexts_in);
+
+       ok = py_tevent_req_wait_exc(self, req);
+       if (!ok) {
+               return NULL;
+       }
+
+       if (smbXcli_conn_protocol(self->cli->conn) >= PROTOCOL_SMB2_02) {
+               status = cli_smb2_create_fnum_recv(
+                       req,
+                       &fnum,
+                       &cr,
+                       NULL,
+                       &create_contexts_out,
+                       &symlink);
+       } else {
+               status = cli_ntcreate_recv(req, &fnum, &cr);
+       }
+
+       TALLOC_FREE(req);
+
+       if (!NT_STATUS_IS_OK(status)) {
+               goto fail;
+       }
+
+       SMB_ASSERT(symlink == NULL);
+
+       py_create_contexts_out = py_cli_create_contexts(&create_contexts_out);
+       TALLOC_FREE(create_contexts_out.blobs);
+       if (py_create_contexts_out == NULL) {
+               goto nomem;
+       }
+
+       py_cr = py_cli_create_returns(&cr);
+       if (py_cr == NULL) {
+               goto nomem;
+       }
+
+       v = Py_BuildValue("(IOO)",
+                         (unsigned)fnum,
+                         py_cr,
+                         py_create_contexts_out);
+       return v;
+nomem:
+       status = NT_STATUS_NO_MEMORY;
+fail:
+       Py_XDECREF(py_create_contexts_out);
+       Py_XDECREF(py_cr);
+       Py_XDECREF(v);
+
+       if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK) &&
+           (symlink != NULL)) {
+               PyErr_SetObject(
+                       PyObject_GetAttrString(
+                               PyImport_ImportModule("samba"),
+                               "NTSTATUSError"),
+                       Py_BuildValue(
+                               "I,s,O",
+                               NT_STATUS_V(status),
+                               get_friendly_nt_error_msg(status),
+                               py_cli_symlink_error(symlink)));
+       } else {
+               PyErr_SetNTSTATUS(status);
+       }
+       return NULL;
+}
+
 static PyObject *py_cli_close(struct py_cli_state *self, PyObject *args)
 {
        struct tevent_req *req;
        int fnum;
+       int flags = 0;
        NTSTATUS status;
 
-       if (!PyArg_ParseTuple(args, "i", &fnum)) {
+       if (!PyArg_ParseTuple(args, "i|i", &fnum, &flags)) {
                return NULL;
        }
 
-       req = cli_close_send(NULL, self->ev, self->cli, fnum);
+       req = cli_close_send(NULL, self->ev, self->cli, fnum, flags);
        if (!py_tevent_req_wait_exc(self, req)) {
                return NULL;
        }
@@ -754,6 +1263,39 @@ static PyObject *py_cli_close(struct py_cli_state *self, PyObject *args)
        Py_RETURN_NONE;
 }
 
+static PyObject *py_cli_rename(
+       struct py_cli_state *self, PyObject *args, PyObject *kwds)
+{
+       char *fname_src = NULL, *fname_dst = NULL;
+       int replace = false;
+       struct tevent_req *req = NULL;
+       NTSTATUS status;
+       bool ok;
+
+       static const char *kwlist[] = { "src", "dst", "replace", NULL };
+
+       ok = ParseTupleAndKeywords(
+               args, kwds, "ss|p", kwlist, &fname_src, &fname_dst, &replace);
+       if (!ok) {
+               return NULL;
+       }
+
+       req = cli_rename_send(
+               NULL, self->ev, self->cli, fname_src, fname_dst, replace);
+       if (!py_tevent_req_wait_exc(self, req)) {
+               return NULL;
+       }
+       status = cli_rename_recv(req);
+       TALLOC_FREE(req);
+
+       if (!NT_STATUS_IS_OK(status)) {
+               PyErr_SetNTSTATUS(status);
+               return NULL;
+       }
+       Py_RETURN_NONE;
+}
+
+
 struct push_state {
        char *data;
        off_t nread;
@@ -786,8 +1328,7 @@ static size_t push_data(uint8_t *buf, size_t n, void *priv)
 /*
  * Writes a file with the contents specified
  */
-static PyObject *py_smb_savefile(struct py_cli_state *self, PyObject *args,
-                                PyObject *kwargs)
+static PyObject *py_smb_savefile(struct py_cli_state *self, PyObject *args)
 {
        uint16_t fnum;
        const char *filename = NULL;
@@ -813,7 +1354,7 @@ static PyObject *py_smb_savefile(struct py_cli_state *self, PyObject *args,
        }
        status = cli_ntcreate_recv(req, &fnum, NULL);
        TALLOC_FREE(req);
-       PyErr_NTSTATUS_IS_ERR_RAISE(status);
+       PyErr_NTSTATUS_NOT_OK_RAISE(status);
 
        /* write the new file contents */
        state.data = data;
@@ -827,15 +1368,15 @@ static PyObject *py_smb_savefile(struct py_cli_state *self, PyObject *args,
        }
        status = cli_push_recv(req);
        TALLOC_FREE(req);
-       PyErr_NTSTATUS_IS_ERR_RAISE(status);
+       PyErr_NTSTATUS_NOT_OK_RAISE(status);
 
        /* close the file handle */
-       req = cli_close_send(NULL, self->ev, self->cli, fnum);
+       req = cli_close_send(NULL, self->ev, self->cli, fnum, 0);
        if (!py_tevent_req_wait_exc(self, req)) {
                return NULL;
        }
        status = cli_close_recv(req);
-       PyErr_NTSTATUS_IS_ERR_RAISE(status);
+       PyErr_NTSTATUS_NOT_OK_RAISE(status);
 
        Py_RETURN_NONE;
 }
@@ -856,7 +1397,7 @@ static PyObject *py_cli_write(struct py_cli_state *self, PyObject *args,
                "fnum", "buffer", "offset", "mode", NULL };
 
        if (!ParseTupleAndKeywords(
-                   args, kwds, "I" PYARG_BYTES_LEN "K|I", kwlist,
+                   args, kwds, "i" PYARG_BYTES_LEN "K|I", kwlist,
                    &fnum, &buf, &buflen, &offset, &mode)) {
                return NULL;
        }
@@ -883,35 +1424,22 @@ static NTSTATUS py_smb_filesize(struct py_cli_state *self, uint16_t fnum,
                                off_t *size)
 {
        NTSTATUS status;
+       struct tevent_req *req = NULL;
 
-       if (self->is_smb1) {
-               uint8_t *rdata = NULL;
-               struct tevent_req *req = NULL;
-
-               req = cli_qfileinfo_send(NULL, self->ev, self->cli, fnum,
-                                        SMB_QUERY_FILE_ALL_INFO, 68,
-                                        CLI_BUFFER_SIZE);
-               if (!py_tevent_req_wait_exc(self, req)) {
-                       return NT_STATUS_INTERNAL_ERROR;
-               }
-               status = cli_qfileinfo_recv(req, NULL, NULL, &rdata, NULL);
-               if (NT_STATUS_IS_OK(status)) {
-                       *size = IVAL2_TO_SMB_BIG_UINT(rdata, 48);
-               }
-               TALLOC_FREE(req);
-               TALLOC_FREE(rdata);
-       } else {
-               status = cli_qfileinfo_basic(self->cli, fnum, NULL, size,
-                                            NULL, NULL, NULL, NULL, NULL);
+       req = cli_qfileinfo_basic_send(NULL, self->ev, self->cli, fnum);
+       if (!py_tevent_req_wait_exc(self, req)) {
+               return NT_STATUS_INTERNAL_ERROR;
        }
+       status = cli_qfileinfo_basic_recv(
+               req, NULL, size, NULL, NULL, NULL, NULL, NULL);
+       TALLOC_FREE(req);
        return status;
 }
 
 /*
  * Loads the specified file's contents and returns it
  */
-static PyObject *py_smb_loadfile(struct py_cli_state *self, PyObject *args,
-                                PyObject *kwargs)
+static PyObject *py_smb_loadfile(struct py_cli_state *self, PyObject *args)
 {
        NTSTATUS status;
        const char *filename = NULL;
@@ -937,11 +1465,11 @@ static PyObject *py_smb_loadfile(struct py_cli_state *self, PyObject *args,
        }
        status = cli_ntcreate_recv(req, &fnum, NULL);
        TALLOC_FREE(req);
-       PyErr_NTSTATUS_IS_ERR_RAISE(status);
+       PyErr_NTSTATUS_NOT_OK_RAISE(status);
 
        /* get a buffer to hold the file contents */
        status = py_smb_filesize(self, fnum, &size);
-       PyErr_NTSTATUS_IS_ERR_RAISE(status);
+       PyErr_NTSTATUS_NOT_OK_RAISE(status);
 
        result = PyBytes_FromStringAndSize(NULL, size);
        if (result == NULL) {
@@ -965,7 +1493,7 @@ static PyObject *py_smb_loadfile(struct py_cli_state *self, PyObject *args,
        }
 
        /* close the file handle */
-       req = cli_close_send(NULL, self->ev, self->cli, fnum);
+       req = cli_close_send(NULL, self->ev, self->cli, fnum, 0);
        if (!py_tevent_req_wait_exc(self, req)) {
                Py_XDECREF(result);
                return NULL;
@@ -1012,7 +1540,7 @@ static PyObject *py_cli_read(struct py_cli_state *self, PyObject *args,
                "fnum", "offset", "size", NULL };
 
        if (!ParseTupleAndKeywords(
-                   args, kwds, "IKI", kwlist, &fnum, &offset,
+                   args, kwds, "iKI", kwlist, &fnum, &offset,
                    &size)) {
                return NULL;
        }
@@ -1116,29 +1644,309 @@ static PyObject *py_cli_delete_on_close(struct py_cli_state *self,
        Py_RETURN_NONE;
 }
 
-/*
- * Helper to add directory listing entries to an overall Python list
- */
-static NTSTATUS list_helper(const char *mntpoint, struct file_info *finfo,
-                           const char *mask, void *state)
+struct py_cli_notify_state {
+       PyObject_HEAD
+       struct py_cli_state *py_cli_state;
+       struct tevent_req *req;
+};
+
+static void py_cli_notify_state_dealloc(struct py_cli_notify_state *self)
 {
-       PyObject *result = (PyObject *)state;
-       PyObject *file = NULL;
-       PyObject *size = NULL;
-       int ret;
+       TALLOC_FREE(self->req);
+       Py_CLEAR(self->py_cli_state);
+       Py_TYPE(self)->tp_free(self);
+}
 
-       /* suppress '.' and '..' in the results we return */
-       if (ISDOT(finfo->name) || ISDOTDOT(finfo->name)) {
-               return NT_STATUS_OK;
-       }
-       size = PyLong_FromUnsignedLongLong(finfo->size);
-       /*
-        * Build a dictionary representing the file info.
-        * Note: Windows does not always return short_name (so it may be None)
-        */
-       file = Py_BuildValue("{s:s,s:i,s:s,s:O,s:l}",
+static PyTypeObject py_cli_notify_state_type;
+
+static PyObject *py_cli_notify(struct py_cli_state *self,
+                              PyObject *args,
+                              PyObject *kwds)
+{
+       static const char *kwlist[] = {
+               "fnum",
+               "buffer_size",
+               "completion_filter",
+               "recursive",
+               NULL
+       };
+       unsigned fnum = 0;
+       unsigned buffer_size = 0;
+       unsigned completion_filter = 0;
+       PyObject *py_recursive = Py_False;
+       bool recursive = false;
+       struct tevent_req *req = NULL;
+       struct tevent_queue *send_queue = NULL;
+       struct tevent_req *flush_req = NULL;
+       bool ok;
+       struct py_cli_notify_state *py_notify_state = NULL;
+       struct timeval endtime;
+
+       ok = ParseTupleAndKeywords(args,
+                                  kwds,
+                                  "IIIO",
+                                  kwlist,
+                                  &fnum,
+                                  &buffer_size,
+                                  &completion_filter,
+                                  &py_recursive);
+       if (!ok) {
+               return NULL;
+       }
+
+       recursive = PyObject_IsTrue(py_recursive);
+
+       req = cli_notify_send(NULL,
+                             self->ev,
+                             self->cli,
+                             fnum,
+                             buffer_size,
+                             completion_filter,
+                             recursive);
+       if (req == NULL) {
+               PyErr_NoMemory();
+               return NULL;
+       }
+
+       /*
+        * Just wait for the request being submitted to
+        * the kernel/socket/wire.
+        */
+       send_queue = smbXcli_conn_send_queue(self->cli->conn);
+       flush_req = tevent_queue_wait_send(req,
+                                          self->ev,
+                                          send_queue);
+       endtime = timeval_current_ofs_msec(self->cli->timeout);
+       ok = tevent_req_set_endtime(flush_req,
+                                   self->ev,
+                                   endtime);
+       if (!ok) {
+               TALLOC_FREE(req);
+               PyErr_NoMemory();
+               return NULL;
+       }
+       ok = py_tevent_req_wait_exc(self, flush_req);
+       if (!ok) {
+               TALLOC_FREE(req);
+               return NULL;
+       }
+       TALLOC_FREE(flush_req);
+
+       py_notify_state = (struct py_cli_notify_state *)
+               py_cli_notify_state_type.tp_alloc(&py_cli_notify_state_type, 0);
+       if (py_notify_state == NULL) {
+               TALLOC_FREE(req);
+               PyErr_NoMemory();
+               return NULL;
+       }
+       Py_INCREF(self);
+       py_notify_state->py_cli_state = self;
+       py_notify_state->req = req;
+
+       return (PyObject *)py_notify_state;
+}
+
+static PyObject *py_cli_notify_get_changes(struct py_cli_notify_state *self,
+                                          PyObject *args,
+                                          PyObject *kwds)
+{
+       struct py_cli_state *py_cli_state = self->py_cli_state;
+       struct tevent_req *req = self->req;
+       uint32_t i;
+       uint32_t num_changes = 0;
+       struct notify_change *changes = NULL;
+       PyObject *result = NULL;
+       NTSTATUS status;
+       bool ok;
+       static const char *kwlist[] = {
+               "wait",
+               NULL
+       };
+       PyObject *py_wait = Py_False;
+       bool wait = false;
+       bool pending;
+
+       ok = ParseTupleAndKeywords(args,
+                                  kwds,
+                                  "O",
+                                  kwlist,
+                                  &py_wait);
+       if (!ok) {
+               return NULL;
+       }
+
+       wait = PyObject_IsTrue(py_wait);
+
+       if (req == NULL) {
+               PyErr_SetString(PyExc_RuntimeError,
+                               "TODO req == NULL "
+                               "- missing change notify request?");
+               return NULL;
+       }
+
+       pending = tevent_req_is_in_progress(req);
+       if (pending && !wait) {
+               Py_RETURN_NONE;
+       }
+
+       if (pending) {
+               struct timeval endtime;
+
+               endtime = timeval_current_ofs_msec(py_cli_state->cli->timeout);
+               ok = tevent_req_set_endtime(req,
+                                           py_cli_state->ev,
+                                           endtime);
+               if (!ok) {
+                       TALLOC_FREE(req);
+                       PyErr_NoMemory();
+                       return NULL;
+               }
+       }
+
+       ok = py_tevent_req_wait_exc(py_cli_state, req);
+       self->req = NULL;
+       Py_CLEAR(self->py_cli_state);
+       if (!ok) {
+               return NULL;
+       }
+
+       status = cli_notify_recv(req, req, &num_changes, &changes);
+       if (!NT_STATUS_IS_OK(status)) {
+               TALLOC_FREE(req);
+               PyErr_SetNTSTATUS(status);
+               return NULL;
+       }
+
+       result = Py_BuildValue("[]");
+       if (result == NULL) {
+               TALLOC_FREE(req);
+               return NULL;
+       }
+
+       for (i = 0; i < num_changes; i++) {
+               PyObject *change = NULL;
+               int ret;
+
+               change = Py_BuildValue("{s:s,s:I}",
+                                      "name", changes[i].name,
+                                      "action", changes[i].action);
+               if (change == NULL) {
+                       Py_XDECREF(result);
+                       TALLOC_FREE(req);
+                       return NULL;
+               }
+
+               ret = PyList_Append(result, change);
+               Py_DECREF(change);
+               if (ret == -1) {
+                       Py_XDECREF(result);
+                       TALLOC_FREE(req);
+                       return NULL;
+               }
+       }
+
+       TALLOC_FREE(req);
+       return result;
+}
+
+static PyMethodDef py_cli_notify_state_methods[] = {
+       {
+               .ml_name = "get_changes",
+               .ml_meth = (PyCFunction)py_cli_notify_get_changes,
+               .ml_flags = METH_VARARGS|METH_KEYWORDS,
+               .ml_doc  = "Wait for change notifications: \n"
+                          "N.get_changes(wait=BOOLEAN) -> "
+                          "change notifications as a dictionary\n"
+                          "\t\tList contents of a directory. The keys are, \n"
+                          "\t\t\tname: name of changed object\n"
+                          "\t\t\taction: type of the change\n"
+                          "None is returned if there's no response jet and "
+                          "wait=False is passed"
+       },
+       {
+               .ml_name = NULL
+       }
+};
+
+static PyTypeObject py_cli_notify_state_type = {
+       PyVarObject_HEAD_INIT(NULL, 0)
+       .tp_name = "libsmb_samba_cwrapper.Notify",
+       .tp_basicsize = sizeof(struct py_cli_notify_state),
+       .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+       .tp_doc = "notify request",
+       .tp_dealloc = (destructor)py_cli_notify_state_dealloc,
+       .tp_methods = py_cli_notify_state_methods,
+};
+
+/*
+ * Helper to add posix directory listing entries to an overall Python list
+ */
+static NTSTATUS list_posix_helper(struct file_info *finfo,
+                                 const char *mask, void *state)
+{
+       PyObject *result = (PyObject *)state;
+       PyObject *file = NULL;
+       PyObject *size = NULL;
+       int ret;
+
+       size = PyLong_FromUnsignedLongLong(finfo->size);
+       /*
+        * Build a dictionary representing the file info.
+        * Note: Windows does not always return short_name (so it may be None)
+        */
+       file = Py_BuildValue("{s:s,s:i,s:s,s:O,s:l,s:i,s:i,s:i,s:s,s:s}",
                             "name", finfo->name,
-                            "attrib", (int)finfo->mode,
+                            "attrib", (int)finfo->attr,
+                            "short_name", finfo->short_name,
+                            "size", size,
+                            "mtime",
+                            convert_timespec_to_time_t(finfo->mtime_ts),
+                            "perms", finfo->st_ex_mode,
+                            "ino", finfo->ino,
+                            "dev", finfo->st_ex_dev,
+                            "owner_sid",
+                            dom_sid_string(finfo, &finfo->owner_sid),
+                            "group_sid",
+                            dom_sid_string(finfo, &finfo->group_sid));
+
+       Py_CLEAR(size);
+
+       if (file == NULL) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       ret = PyList_Append(result, file);
+       Py_CLEAR(file);
+       if (ret == -1) {
+               return NT_STATUS_INTERNAL_ERROR;
+       }
+
+       return NT_STATUS_OK;
+}
+
+/*
+ * Helper to add directory listing entries to an overall Python list
+ */
+static NTSTATUS list_helper(struct file_info *finfo,
+                           const char *mask, void *state)
+{
+       PyObject *result = (PyObject *)state;
+       PyObject *file = NULL;
+       PyObject *size = NULL;
+       int ret;
+
+       /* suppress '.' and '..' in the results we return */
+       if (ISDOT(finfo->name) || ISDOTDOT(finfo->name)) {
+               return NT_STATUS_OK;
+       }
+       size = PyLong_FromUnsignedLongLong(finfo->size);
+       /*
+        * Build a dictionary representing the file info.
+        * Note: Windows does not always return short_name (so it may be None)
+        */
+       file = Py_BuildValue("{s:s,s:i,s:s,s:O,s:l}",
+                            "name", finfo->name,
+                            "attrib", (int)finfo->attr,
                             "short_name", finfo->short_name,
                             "size", size,
                             "mtime",
@@ -1150,6 +1958,18 @@ static NTSTATUS list_helper(const char *mntpoint, struct file_info *finfo,
                return NT_STATUS_NO_MEMORY;
        }
 
+       if (finfo->attr & FILE_ATTRIBUTE_REPARSE_POINT) {
+               unsigned long tag = finfo->reparse_tag;
+
+               ret = PyDict_SetItemString(
+                       file,
+                       "reparse_tag",
+                       PyLong_FromUnsignedLong(tag));
+               if (ret == -1) {
+                       return NT_STATUS_INTERNAL_ERROR;
+               }
+       }
+
        ret = PyList_Append(result, file);
        Py_CLEAR(file);
        if (ret == -1) {
@@ -1159,19 +1979,45 @@ static NTSTATUS list_helper(const char *mntpoint, struct file_info *finfo,
        return NT_STATUS_OK;
 }
 
+struct do_listing_state {
+       const char *mask;
+       NTSTATUS (*callback_fn)(
+               struct file_info *finfo,
+               const char *mask,
+               void *private_data);
+       void *private_data;
+       NTSTATUS status;
+};
+
+static void do_listing_cb(struct tevent_req *subreq)
+{
+       struct do_listing_state *state = tevent_req_callback_data_void(subreq);
+       struct file_info *finfo = NULL;
+
+       state->status = cli_list_recv(subreq, NULL, &finfo);
+       if (!NT_STATUS_IS_OK(state->status)) {
+               return;
+       }
+       state->callback_fn(finfo, state->mask, state->private_data);
+       TALLOC_FREE(finfo);
+}
+
 static NTSTATUS do_listing(struct py_cli_state *self,
                           const char *base_dir, const char *user_mask,
                           uint16_t attribute,
-                          NTSTATUS (*callback_fn)(const char *,
-                                                  struct file_info *,
+                          unsigned int info_level,
+                          bool posix,
+                          NTSTATUS (*callback_fn)(struct file_info *,
                                                   const char *, void *),
                           void *priv)
 {
        char *mask = NULL;
-       unsigned int info_level = SMB_FIND_FILE_BOTH_DIRECTORY_INFO;
-       struct file_info *finfos = NULL;
-       size_t i;
-       size_t num_finfos = 0;
+       struct do_listing_state state = {
+               .mask = mask,
+               .callback_fn = callback_fn,
+               .private_data = priv,
+       };
+       struct tevent_req *req = NULL;
        NTSTATUS status;
 
        if (user_mask == NULL) {
@@ -1185,37 +2031,26 @@ static NTSTATUS do_listing(struct py_cli_state *self,
        }
        dos_format(mask);
 
-       if (self->is_smb1) {
-               struct tevent_req *req = NULL;
-
-               req = cli_list_send(NULL, self->ev, self->cli, mask, attribute,
-                                   info_level);
-               if (!py_tevent_req_wait_exc(self, req)) {
-                       return NT_STATUS_INTERNAL_ERROR;
-               }
-               status = cli_list_recv(req, NULL, &finfos, &num_finfos);
-               TALLOC_FREE(req);
-       } else {
-               status = cli_list(self->cli, mask, attribute, callback_fn,
-                                 priv);
+       req = cli_list_send(NULL, self->ev, self->cli, mask, attribute,
+                           info_level, posix);
+       if (req == NULL) {
+               status = NT_STATUS_NO_MEMORY;
+               goto done;
        }
-       TALLOC_FREE(mask);
+       tevent_req_set_callback(req, do_listing_cb, &state);
 
-       if (!NT_STATUS_IS_OK(status)) {
-               return status;
+       if (!py_tevent_req_wait_exc(self, req)) {
+               return NT_STATUS_INTERNAL_ERROR;
        }
+       TALLOC_FREE(req);
 
-       /* invoke the callback for the async results (SMBv1 connections) */
-       for (i = 0; i < num_finfos; i++) {
-               status = callback_fn(base_dir, &finfos[i], user_mask,
-                                    priv);
-               if (!NT_STATUS_IS_OK(status)) {
-                       TALLOC_FREE(finfos);
-                       return status;
-               }
+       status = state.status;
+       if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_FILES)) {
+               status = NT_STATUS_OK;
        }
 
-       TALLOC_FREE(finfos);
+done:
+       TALLOC_FREE(mask);
        return status;
 }
 
@@ -1226,12 +2061,19 @@ static PyObject *py_cli_list(struct py_cli_state *self,
        char *base_dir;
        char *user_mask = NULL;
        unsigned int attribute = LIST_ATTRIBUTE_MASK;
+       unsigned int info_level = 0;
+       bool posix = false;
        NTSTATUS status;
+       enum protocol_types proto = smbXcli_conn_protocol(self->cli->conn);
        PyObject *result = NULL;
-       const char *kwlist[] = { "directory", "mask", "attribs", NULL };
-
-       if (!ParseTupleAndKeywords(args, kwds, "z|sH:list", kwlist,
-                                  &base_dir, &user_mask, &attribute)) {
+       const char *kwlist[] = { "directory", "mask", "attribs", "posix",
+                                "info_level", NULL };
+       NTSTATUS (*callback_fn)(struct file_info *, const char *, void *) =
+               &list_helper;
+
+       if (!ParseTupleAndKeywords(args, kwds, "z|sIpI:list", kwlist,
+                                  &base_dir, &user_mask, &attribute,
+                                  &posix, &info_level)) {
                return NULL;
        }
 
@@ -1240,8 +2082,19 @@ static PyObject *py_cli_list(struct py_cli_state *self,
                return NULL;
        }
 
+       if (!info_level) {
+               if (proto >= PROTOCOL_SMB2_02) {
+                       info_level = SMB2_FIND_ID_BOTH_DIRECTORY_INFO;
+               } else {
+                       info_level = SMB_FIND_FILE_BOTH_DIRECTORY_INFO;
+               }
+       }
+
+       if (posix) {
+               callback_fn = &list_posix_helper;
+       }
        status = do_listing(self, base_dir, user_mask, attribute,
-                           list_helper, result);
+                           info_level, posix, callback_fn, result);
 
        if (!NT_STATUS_IS_OK(status)) {
                Py_XDECREF(result);
@@ -1252,79 +2105,45 @@ static PyObject *py_cli_list(struct py_cli_state *self,
        return result;
 }
 
-/*
- * Deletes a file
- */
-static NTSTATUS unlink_file(struct py_cli_state *self, const char *filename)
-{
-       NTSTATUS status;
-       uint16_t attrs = (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
-
-       if (self->is_smb1) {
-               struct tevent_req *req = NULL;
-
-               req = cli_unlink_send(NULL, self->ev, self->cli, filename,
-                                     attrs);
-               if (!py_tevent_req_wait_exc(self, req)) {
-                       return NT_STATUS_INTERNAL_ERROR;
-               }
-               status = cli_unlink_recv(req);
-               TALLOC_FREE(req);
-       } else {
-               status = cli_unlink(self->cli, filename, attrs);
-       }
-
-       return status;
-}
-
 static PyObject *py_smb_unlink(struct py_cli_state *self, PyObject *args)
 {
        NTSTATUS status;
        const char *filename = NULL;
+       struct tevent_req *req = NULL;
+       const uint32_t attrs = (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
 
        if (!PyArg_ParseTuple(args, "s:unlink", &filename)) {
                return NULL;
        }
 
-       status = unlink_file(self, filename);
+       req = cli_unlink_send(NULL, self->ev, self->cli, filename, attrs);
+       if (!py_tevent_req_wait_exc(self, req)) {
+               return NULL;
+       }
+       status = cli_unlink_recv(req);
+       TALLOC_FREE(req);
        PyErr_NTSTATUS_NOT_OK_RAISE(status);
 
        Py_RETURN_NONE;
 }
 
-/*
- * Delete an empty directory
- */
-static NTSTATUS remove_dir(struct py_cli_state *self, const char *dirname)
-{
-       NTSTATUS status;
-
-       if (self->is_smb1) {
-               struct tevent_req *req = NULL;
-
-               req = cli_rmdir_send(NULL, self->ev, self->cli, dirname);
-               if (!py_tevent_req_wait_exc(self, req)) {
-                       return NT_STATUS_INTERNAL_ERROR;
-               }
-               status = cli_rmdir_recv(req);
-               TALLOC_FREE(req);
-       } else {
-               status = cli_rmdir(self->cli, dirname);
-       }
-       return status;
-}
-
 static PyObject *py_smb_rmdir(struct py_cli_state *self, PyObject *args)
 {
        NTSTATUS status;
+       struct tevent_req *req = NULL;
        const char *dirname = NULL;
 
        if (!PyArg_ParseTuple(args, "s:rmdir", &dirname)) {
                return NULL;
        }
 
-       status = remove_dir(self, dirname);
-       PyErr_NTSTATUS_IS_ERR_RAISE(status);
+       req = cli_rmdir_send(NULL, self->ev, self->cli, dirname);
+       if (!py_tevent_req_wait_exc(self, req)) {
+               return NULL;
+       }
+       status = cli_rmdir_recv(req);
+       TALLOC_FREE(req);
+       PyErr_NTSTATUS_NOT_OK_RAISE(status);
 
        Py_RETURN_NONE;
 }
@@ -1336,26 +2155,138 @@ static PyObject *py_smb_mkdir(struct py_cli_state *self, PyObject *args)
 {
        NTSTATUS status;
        const char *dirname = NULL;
+       struct tevent_req *req = NULL;
 
        if (!PyArg_ParseTuple(args, "s:mkdir", &dirname)) {
                return NULL;
        }
 
-       if (self->is_smb1) {
-               struct tevent_req *req = NULL;
+       req = cli_mkdir_send(NULL, self->ev, self->cli, dirname);
+       if (!py_tevent_req_wait_exc(self, req)) {
+               return NULL;
+       }
+       status = cli_mkdir_recv(req);
+       TALLOC_FREE(req);
+       PyErr_NTSTATUS_NOT_OK_RAISE(status);
+
+       Py_RETURN_NONE;
+}
 
-               req = cli_mkdir_send(NULL, self->ev, self->cli, dirname);
-               if (!py_tevent_req_wait_exc(self, req)) {
-                       return NULL;
+/*
+ * Does a whoami call
+ */
+static PyObject *py_smb_posix_whoami(struct py_cli_state *self,
+                                    PyObject *Py_UNUSED(ignored))
+{
+       TALLOC_CTX *frame = talloc_stackframe();
+       NTSTATUS status;
+       struct tevent_req *req = NULL;
+       uint64_t uid;
+       uint64_t gid;
+       uint32_t num_gids;
+       uint64_t *gids = NULL;
+       uint32_t num_sids;
+       struct dom_sid *sids = NULL;
+       bool guest;
+       PyObject *py_gids = NULL;
+       PyObject *py_sids = NULL;
+       PyObject *py_guest = NULL;
+       PyObject *py_ret = NULL;
+       Py_ssize_t i;
+
+       req = cli_posix_whoami_send(frame, self->ev, self->cli);
+       if (!py_tevent_req_wait_exc(self, req)) {
+               goto fail;
+       }
+       status = cli_posix_whoami_recv(req,
+                               frame,
+                               &uid,
+                               &gid,
+                               &num_gids,
+                               &gids,
+                               &num_sids,
+                               &sids,
+                               &guest);
+       if (!NT_STATUS_IS_OK(status)) {
+               PyErr_SetNTSTATUS(status);
+               goto fail;
+       }
+       if (num_gids > PY_SSIZE_T_MAX) {
+               PyErr_SetString(PyExc_OverflowError, "posix_whoami: Too many GIDs");
+               goto fail;
+       }
+       if (num_sids > PY_SSIZE_T_MAX) {
+               PyErr_SetString(PyExc_OverflowError, "posix_whoami: Too many SIDs");
+               goto fail;
+       }
+
+       py_gids = PyList_New(num_gids);
+       if (!py_gids) {
+               goto fail;
+       }
+       for (i = 0; i < num_gids; ++i) {
+               int ret;
+               PyObject *py_item = PyLong_FromUnsignedLongLong(gids[i]);
+               if (!py_item) {
+                       goto fail2;
                }
-               status = cli_mkdir_recv(req);
-               TALLOC_FREE(req);
-       } else {
-               status = cli_mkdir(self->cli, dirname);
+
+               ret = PyList_SetItem(py_gids, i, py_item);
+               if (ret) {
+                       goto fail2;
+               }
+       }
+       py_sids = PyList_New(num_sids);
+       if (!py_sids) {
+               goto fail2;
        }
-       PyErr_NTSTATUS_IS_ERR_RAISE(status);
+       for (i = 0; i < num_sids; ++i) {
+               int ret;
+               struct dom_sid *sid;
+               PyObject *py_item;
 
-       Py_RETURN_NONE;
+               sid = dom_sid_dup(frame, &sids[i]);
+               if (!sid) {
+                       PyErr_NoMemory();
+                       goto fail3;
+               }
+
+               py_item = pytalloc_steal(dom_sid_Type, sid);
+               if (!py_item) {
+                       PyErr_NoMemory();
+                       goto fail3;
+               }
+
+               ret = PyList_SetItem(py_sids, i, py_item);
+               if (ret) {
+                       goto fail3;
+               }
+       }
+
+       py_guest = guest ? Py_True : Py_False;
+
+       py_ret = Py_BuildValue("KKNNO",
+                       uid,
+                       gid,
+                       py_gids,
+                       py_sids,
+                       py_guest);
+       if (!py_ret) {
+               goto fail3;
+       }
+
+       TALLOC_FREE(frame);
+       return py_ret;
+
+fail3:
+       Py_CLEAR(py_sids);
+
+fail2:
+       Py_CLEAR(py_gids);
+
+fail:
+       TALLOC_FREE(frame);
+       return NULL;
 }
 
 /*
@@ -1364,19 +2295,14 @@ static PyObject *py_smb_mkdir(struct py_cli_state *self, PyObject *args)
 static bool check_dir_path(struct py_cli_state *self, const char *path)
 {
        NTSTATUS status;
+       struct tevent_req *req = NULL;
 
-       if (self->is_smb1) {
-               struct tevent_req *req = NULL;
-
-               req = cli_chkpath_send(NULL, self->ev, self->cli, path);
-               if (!py_tevent_req_wait_exc(self, req)) {
-                       return false;
-               }
-               status = cli_chkpath_recv(req);
-               TALLOC_FREE(req);
-       } else {
-               status = cli_chkpath(self->cli, path);
+       req = cli_chkpath_send(NULL, self->ev, self->cli, path);
+       if (!py_tevent_req_wait_exc(self, req)) {
+               return false;
        }
+       status = cli_chkpath_recv(req);
+       TALLOC_FREE(req);
 
        return NT_STATUS_IS_OK(status);
 }
@@ -1394,208 +2320,375 @@ static PyObject *py_smb_chkpath(struct py_cli_state *self, PyObject *args)
        return PyBool_FromLong(dir_exists);
 }
 
-struct deltree_state {
-       struct py_cli_state *self;
-       const char *full_dirpath;
-};
+static PyObject *py_smb_have_posix(struct py_cli_state *self,
+                                  PyObject *Py_UNUSED(ignored))
+{
+       bool posix = smbXcli_conn_have_posix(self->cli->conn);
 
-static NTSTATUS delete_dir_tree(struct py_cli_state *self,
-                               const char *dirpath);
+       if (posix) {
+               Py_RETURN_TRUE;
+       }
+       Py_RETURN_FALSE;
+}
 
-/*
- * Deletes a single item in the directory tree. This could be either a file
- * or a directory. This function gets invoked as a callback for every item in
- * the given directory's listings.
- */
-static NTSTATUS delete_tree_callback(const char *mntpoint,
-                                    struct file_info *finfo,
-                                    const char *mask, void *priv)
+static PyObject *py_smb_protocol(struct py_cli_state *self,
+                                PyObject *Py_UNUSED(ignored))
 {
-       char *filepath = NULL;
-       struct deltree_state *state = priv;
+       enum protocol_types proto = smbXcli_conn_protocol(self->cli->conn);
+       PyObject *result = PyLong_FromLong(proto);
+       return result;
+}
+
+static PyObject *py_smb_get_sd(struct py_cli_state *self, PyObject *args)
+{
+       int fnum;
+       unsigned sinfo;
+       struct tevent_req *req = NULL;
+       struct security_descriptor *sd = NULL;
        NTSTATUS status;
 
-       /* skip '.' or '..' directory listings */
-       if (ISDOT(finfo->name) || ISDOTDOT(finfo->name)) {
-               return NT_STATUS_OK;
+       if (!PyArg_ParseTuple(args, "iI:get_acl", &fnum, &sinfo)) {
+               return NULL;
        }
 
-       /* get the absolute filepath */
-       filepath = talloc_asprintf(NULL, "%s\\%s", state->full_dirpath,
-                                  finfo->name);
-       if (filepath == NULL) {
-               return NT_STATUS_NO_MEMORY;
+       req = cli_query_security_descriptor_send(
+               NULL, self->ev, self->cli, fnum, sinfo);
+       if (!py_tevent_req_wait_exc(self, req)) {
+               return NULL;
        }
+       status = cli_query_security_descriptor_recv(req, NULL, &sd);
+       PyErr_NTSTATUS_NOT_OK_RAISE(status);
 
-       if (finfo->mode & FILE_ATTRIBUTE_DIRECTORY) {
+       return py_return_ndr_struct(
+               "samba.dcerpc.security", "descriptor", sd, sd);
+}
 
-               /* recursively delete the sub-directory and its contents */
-               status = delete_dir_tree(state->self, filepath);
-       } else {
-               status = unlink_file(state->self, filepath);
+static PyObject *py_smb_set_sd(struct py_cli_state *self, PyObject *args)
+{
+       PyObject *py_sd = NULL;
+       struct tevent_req *req = NULL;
+       struct security_descriptor *sd = NULL;
+       uint16_t fnum;
+       unsigned int sinfo;
+       NTSTATUS status;
+
+       if (!PyArg_ParseTuple(args, "iOI:set_sd", &fnum, &py_sd, &sinfo)) {
+               return NULL;
        }
 
-       TALLOC_FREE(filepath);
-       return status;
+       sd = pytalloc_get_type(py_sd, struct security_descriptor);
+       if (!sd) {
+               PyErr_Format(PyExc_TypeError,
+                       "Expected dcerpc.security.descriptor as argument, got %s",
+                       pytalloc_get_name(py_sd));
+               return NULL;
+       }
+
+       req = cli_set_security_descriptor_send(
+               NULL, self->ev, self->cli, fnum, sinfo, sd);
+       if (!py_tevent_req_wait_exc(self, req)) {
+               return NULL;
+       }
+
+       status = cli_set_security_descriptor_recv(req);
+       PyErr_NTSTATUS_NOT_OK_RAISE(status);
+
+       Py_RETURN_NONE;
 }
 
-/*
- * Removes a directory and all its contents
- */
-static NTSTATUS delete_dir_tree(struct py_cli_state *self,
-                               const char *filepath)
+static PyObject *py_smb_smb1_posix(
+       struct py_cli_state *self, PyObject *Py_UNUSED(ignored))
 {
        NTSTATUS status;
-       const char *mask = "*";
-       struct deltree_state state = { 0 };
+       struct tevent_req *req = NULL;
+       uint16_t major, minor;
+       uint32_t caplow, caphigh;
+       PyObject *result = NULL;
 
-       /* go through the directory's contents, deleting each item */
-       state.self = self;
-       state.full_dirpath = filepath;
-       status = do_listing(self, filepath, mask, LIST_ATTRIBUTE_MASK,
-                           delete_tree_callback, &state);
+       req = cli_unix_extensions_version_send(NULL, self->ev, self->cli);
+       if (!py_tevent_req_wait_exc(self, req)) {
+               return NULL;
+       }
+       status = cli_unix_extensions_version_recv(
+               req, &major, &minor, &caplow, &caphigh);
+       TALLOC_FREE(req);
+       if (!NT_STATUS_IS_OK(status)) {
+               PyErr_SetNTSTATUS(status);
+               return NULL;
+       }
 
-       /* remove the directory itself */
-       if (NT_STATUS_IS_OK(status)) {
-               status = remove_dir(self, filepath);
+       req = cli_set_unix_extensions_capabilities_send(
+               NULL, self->ev, self->cli, major, minor, caplow, caphigh);
+       if (!py_tevent_req_wait_exc(self, req)) {
+               return NULL;
        }
-       return status;
+       status = cli_set_unix_extensions_capabilities_recv(req);
+       TALLOC_FREE(req);
+       if (!NT_STATUS_IS_OK(status)) {
+               PyErr_SetNTSTATUS(status);
+               return NULL;
+       }
+
+       result = Py_BuildValue(
+               "[IIII]",
+               (unsigned)minor,
+               (unsigned)major,
+               (unsigned)caplow,
+               (unsigned)caphigh);
+       return result;
 }
 
-static PyObject *py_smb_deltree(struct py_cli_state *self, PyObject *args)
+static PyObject *py_smb_smb1_readlink(
+       struct py_cli_state *self, PyObject *args)
 {
        NTSTATUS status;
-       const char *filepath = NULL;
-       bool dir_exists;
+       const char *filename = NULL;
+       struct tevent_req *req = NULL;
+       char *target = NULL;
+       PyObject *result = NULL;
 
-       if (!PyArg_ParseTuple(args, "s:deltree", &filepath)) {
+       if (!PyArg_ParseTuple(args, "s:smb1_readlink", &filename)) {
                return NULL;
        }
 
-       /* check whether we're removing a directory or a file */
-       dir_exists = check_dir_path(self, filepath);
-
-       if (dir_exists) {
-               status = delete_dir_tree(self, filepath);
-       } else {
-               status = unlink_file(self, filepath);
+       req = cli_posix_readlink_send(NULL, self->ev, self->cli, filename);
+       if (!py_tevent_req_wait_exc(self, req)) {
+               return NULL;
+       }
+       status = cli_posix_readlink_recv(req, NULL, &target);
+       TALLOC_FREE(req);
+       if (!NT_STATUS_IS_OK(status)) {
+               PyErr_SetNTSTATUS(status);
+               return NULL;
        }
 
-       PyErr_NTSTATUS_IS_ERR_RAISE(status);
-
-       Py_RETURN_NONE;
+       result = PyBytes_FromString(target);
+       TALLOC_FREE(target);
+       return result;
 }
 
-/*
- * Read ACL on a given file/directory as a security descriptor object
- */
-static PyObject *py_smb_getacl(struct py_cli_state *self, PyObject *args)
+static PyObject *py_smb_smb1_symlink(
+       struct py_cli_state *self, PyObject *args)
 {
        NTSTATUS status;
-       const char *filename = NULL;
-       unsigned int sinfo = SECINFO_DEFAULT_FLAGS;
-       unsigned int access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
-       uint16_t fnum;
-       struct security_descriptor *sd = NULL;
+       const char *target = NULL, *newname = NULL;
+       struct tevent_req *req = NULL;
 
-       /* there's no async version of cli_query_security_descriptor() */
-       if (self->thread_state != NULL) {
-               PyErr_SetString(PyExc_RuntimeError,
-                               "get_acl() is not supported on "
-                               "a multi_threaded connection");
+       if (!PyArg_ParseTuple(args, "ss:smb1_symlink", &target, &newname)) {
                return NULL;
        }
 
-       if (!PyArg_ParseTuple(args, "s|II:get_acl", &filename, &sinfo,
-                             &access_mask)) {
+       req = cli_posix_symlink_send(
+               NULL, self->ev, self->cli, target, newname);
+       if (!py_tevent_req_wait_exc(self, req)) {
+               return NULL;
+       }
+       status = cli_posix_symlink_recv(req);
+       TALLOC_FREE(req);
+       if (!NT_STATUS_IS_OK(status)) {
+               PyErr_SetNTSTATUS(status);
                return NULL;
        }
 
-       /* get a file handle with the desired access */
-       status = cli_ntcreate(self->cli, filename, 0, access_mask, 0,
-                             FILE_SHARE_READ|FILE_SHARE_WRITE,
-                             FILE_OPEN, 0x0, 0x0, &fnum, NULL);
-       PyErr_NTSTATUS_IS_ERR_RAISE(status);
+       Py_RETURN_NONE;
+}
+
+static PyObject *py_smb_smb1_stat(
+       struct py_cli_state *self, PyObject *args)
+{
+       NTSTATUS status;
+       const char *fname = NULL;
+       struct tevent_req *req = NULL;
+       struct stat_ex sbuf = { .st_ex_nlink = 0, };
 
-       /* query the security descriptor for this file */
-       status = cli_query_security_descriptor(self->cli, fnum, sinfo,
-                                              NULL, &sd);
-       PyErr_NTSTATUS_IS_ERR_RAISE(status);
+       if (!PyArg_ParseTuple(args, "s:smb1_stat", &fname)) {
+               return NULL;
+       }
 
-       /* close the file handle and convert the SD to a python struct */
-       status = cli_close(self->cli, fnum);
-       PyErr_NTSTATUS_IS_ERR_RAISE(status);
+       req = cli_posix_stat_send(NULL, self->ev, self->cli, fname);
+       if (!py_tevent_req_wait_exc(self, req)) {
+               return NULL;
+       }
+       status = cli_posix_stat_recv(req, &sbuf);
+       TALLOC_FREE(req);
+       if (!NT_STATUS_IS_OK(status)) {
+               PyErr_SetNTSTATUS(status);
+               return NULL;
+       }
 
-       return py_return_ndr_struct("samba.dcerpc.security", "descriptor",
-                                   sd, sd);
+       return Py_BuildValue(
+               "{sLsLsLsLsLsLsLsLsLsLsLsLsLsLsLsLsLsLsLsL}",
+               "dev",
+               (unsigned long long)sbuf.st_ex_dev,
+               "ino",
+               (unsigned long long)sbuf.st_ex_ino,
+               "mode",
+               (unsigned long long)sbuf.st_ex_mode,
+               "nlink",
+               (unsigned long long)sbuf.st_ex_nlink,
+               "uid",
+               (unsigned long long)sbuf.st_ex_uid,
+               "gid",
+               (unsigned long long)sbuf.st_ex_gid,
+               "rdev",
+               (unsigned long long)sbuf.st_ex_size,
+               "atime_sec",
+               (unsigned long long)sbuf.st_ex_atime.tv_sec,
+               "atime_nsec",
+               (unsigned long long)sbuf.st_ex_atime.tv_nsec,
+               "mtime_sec",
+               (unsigned long long)sbuf.st_ex_mtime.tv_sec,
+               "mtime_nsec",
+               (unsigned long long)sbuf.st_ex_mtime.tv_nsec,
+               "ctime_sec",
+               (unsigned long long)sbuf.st_ex_ctime.tv_sec,
+               "ctime_nsec",
+               (unsigned long long)sbuf.st_ex_ctime.tv_nsec,
+               "btime_sec",
+               (unsigned long long)sbuf.st_ex_btime.tv_sec,
+               "btime_nsec",
+               (unsigned long long)sbuf.st_ex_btime.tv_nsec,
+               "cached_dos_attributes",
+               (unsigned long long)sbuf.cached_dos_attributes,
+               "blksize",
+               (unsigned long long)sbuf.st_ex_blksize,
+               "blocks",
+               (unsigned long long)sbuf.st_ex_blocks,
+               "flags",
+               (unsigned long long)sbuf.st_ex_flags,
+               "iflags",
+               (unsigned long long)sbuf.st_ex_iflags);
 }
 
-/*
- * Set ACL on file/directory using given security descriptor object
- */
-static PyObject *py_smb_setacl(struct py_cli_state *self, PyObject *args)
+static PyObject *py_cli_mknod(
+       struct py_cli_state *self, PyObject *args, PyObject *kwds)
 {
+       char *fname = NULL;
+       int mode = 0, major = 0, minor = 0, dev = 0;
+       struct tevent_req *req = NULL;
+       static const char *kwlist[] = {
+               "fname", "mode", "major", "minor", NULL,
+       };
        NTSTATUS status;
-       char *filename = NULL;
-       PyObject *py_sd = NULL;
-       struct security_descriptor *sd = NULL;
-       unsigned int sinfo = SECINFO_DEFAULT_FLAGS;
-       uint16_t fnum;
-
-       /* there's no async version of cli_set_security_descriptor() */
-       if (self->thread_state != NULL) {
-               PyErr_SetString(PyExc_RuntimeError,
-                               "set_acl() is not supported on "
-                               "a multi_threaded connection");
+       bool ok;
+
+       ok = ParseTupleAndKeywords(
+               args,
+               kwds,
+               "sI|II:mknod",
+               kwlist,
+               &fname,
+               &mode,
+               &major,
+               &minor);
+       if (!ok) {
                return NULL;
        }
 
-       if (!PyArg_ParseTuple(args, "sO|I:set_acl", &filename, &py_sd,
-                             &sinfo)) {
+#if defined(HAVE_MAKEDEV)
+       dev = makedev(major, minor);
+#endif
+
+       req = cli_mknod_send(
+               NULL, self->ev, self->cli, fname, mode, dev);
+       if (!py_tevent_req_wait_exc(self, req)) {
                return NULL;
        }
+       status = cli_mknod_recv(req);
+       TALLOC_FREE(req);
+       if (!NT_STATUS_IS_OK(status)) {
+               PyErr_SetNTSTATUS(status);
+               return NULL;
+       }
+       Py_RETURN_NONE;
+}
 
-       sd = pytalloc_get_type(py_sd, struct security_descriptor);
-       if (!sd) {
-               PyErr_Format(PyExc_TypeError,
-                       "Expected dcerpc.security.descriptor as argument, got %s",
-                       talloc_get_name(pytalloc_get_ptr(py_sd)));
+static PyObject *py_cli_fsctl(
+       struct py_cli_state *self, PyObject *args, PyObject *kwds)
+{
+       int fnum, ctl_code;
+       int max_out = 0;
+       char *buf = NULL;
+       Py_ssize_t buflen;
+       DATA_BLOB in = { .data = NULL, };
+       DATA_BLOB out = { .data = NULL, };
+       struct tevent_req *req = NULL;
+       PyObject *result = NULL;
+       static const char *kwlist[] = {
+               "fnum", "ctl_code", "in", "max_out", NULL,
+       };
+       NTSTATUS status;
+       bool ok;
+
+       ok = ParseTupleAndKeywords(
+                   args,
+                   kwds,
+                   "ii" PYARG_BYTES_LEN "i",
+                   kwlist,
+                   &fnum,
+                   &ctl_code,
+                   &buf,
+                   &buflen,
+                   &max_out);
+       if (!ok) {
                return NULL;
        }
 
-       status = cli_ntcreate(self->cli, filename, 0,
-                             SEC_FLAG_MAXIMUM_ALLOWED, 0,
-                             FILE_SHARE_READ|FILE_SHARE_WRITE,
-                             FILE_OPEN, 0x0, 0x0, &fnum, NULL);
-       PyErr_NTSTATUS_IS_ERR_RAISE(status);
+       in = (DATA_BLOB) { .data = (uint8_t *)buf, .length = buflen, };
 
-       status = cli_set_security_descriptor(self->cli, fnum, sinfo, sd);
-       PyErr_NTSTATUS_IS_ERR_RAISE(status);
+       req = cli_fsctl_send(
+               NULL, self->ev, self->cli, fnum, ctl_code, &in, max_out);
 
-       status = cli_close(self->cli, fnum);
-       PyErr_NTSTATUS_IS_ERR_RAISE(status);
+       if (!py_tevent_req_wait_exc(self, req)) {
+               return NULL;
+       }
 
-       Py_RETURN_NONE;
+       status = cli_fsctl_recv(req, NULL, &out);
+       if (!NT_STATUS_IS_OK(status)) {
+               PyErr_SetNTSTATUS(status);
+               return NULL;
+       }
+
+       result = PyBytes_FromStringAndSize((char *)out.data, out.length);
+       data_blob_free(&out);
+       return result;
 }
 
 static PyMethodDef py_cli_state_methods[] = {
        { "settimeout", (PyCFunction)py_cli_settimeout, METH_VARARGS,
          "settimeout(new_timeout_msecs) => return old_timeout_msecs" },
-       { "create", (PyCFunction)py_cli_create, METH_VARARGS|METH_KEYWORDS,
+       { "echo", (PyCFunction)py_cli_echo, METH_NOARGS,
+         "Ping the server connection" },
+       { "create", PY_DISCARD_FUNC_SIG(PyCFunction, py_cli_create),
+               METH_VARARGS|METH_KEYWORDS,
          "Open a file" },
+       { "create_ex",
+         PY_DISCARD_FUNC_SIG(PyCFunction, py_cli_create_ex),
+         METH_VARARGS|METH_KEYWORDS,
+         "Open a file, SMB2 version returning create contexts" },
        { "close", (PyCFunction)py_cli_close, METH_VARARGS,
          "Close a file handle" },
-       { "write", (PyCFunction)py_cli_write, METH_VARARGS|METH_KEYWORDS,
+       { "write", PY_DISCARD_FUNC_SIG(PyCFunction, py_cli_write),
+               METH_VARARGS|METH_KEYWORDS,
          "Write to a file handle" },
-       { "read", (PyCFunction)py_cli_read, METH_VARARGS|METH_KEYWORDS,
+       { "read", PY_DISCARD_FUNC_SIG(PyCFunction, py_cli_read),
+               METH_VARARGS|METH_KEYWORDS,
          "Read from a file handle" },
-       { "truncate", (PyCFunction)py_cli_ftruncate,
+       { "truncate", PY_DISCARD_FUNC_SIG(PyCFunction,
+                       py_cli_ftruncate),
          METH_VARARGS|METH_KEYWORDS,
          "Truncate a file" },
-       { "delete_on_close", (PyCFunction)py_cli_delete_on_close,
+       { "delete_on_close", PY_DISCARD_FUNC_SIG(PyCFunction,
+                                        py_cli_delete_on_close),
          METH_VARARGS|METH_KEYWORDS,
          "Set/Reset the delete on close flag" },
-       { "list", (PyCFunction)py_cli_list, METH_VARARGS|METH_KEYWORDS,
+       { "notify", PY_DISCARD_FUNC_SIG(PyCFunction, py_cli_notify),
+         METH_VARARGS|METH_KEYWORDS,
+         "Wait for change notifications: \n"
+         "notify(fnum, buffer_size, completion_filter...) -> "
+         "libsmb_samba_internal.Notify request handle\n" },
+       { "list", PY_DISCARD_FUNC_SIG(PyCFunction, py_cli_list),
+               METH_VARARGS|METH_KEYWORDS,
          "list(directory, mask='*', attribs=DEFAULT_ATTRS) -> "
          "directory contents as a dictionary\n"
          "\t\tDEFAULT_ATTRS: FILE_ATTRIBUTE_SYSTEM | "
@@ -1613,35 +2706,79 @@ static PyMethodDef py_cli_state_methods[] = {
          "unlink(path) -> None\n\n \t\tDelete a file." },
        { "mkdir", (PyCFunction)py_smb_mkdir, METH_VARARGS,
          "mkdir(path) -> None\n\n \t\tCreate a directory." },
+       { "posix_whoami", (PyCFunction)py_smb_posix_whoami, METH_NOARGS,
+       "posix_whoami() -> (uid, gid, gids, sids, guest)" },
        { "rmdir", (PyCFunction)py_smb_rmdir, METH_VARARGS,
          "rmdir(path) -> None\n\n \t\tDelete a directory." },
+       { "rename",
+         PY_DISCARD_FUNC_SIG(PyCFunction, py_cli_rename),
+         METH_VARARGS|METH_KEYWORDS,
+         "rename(src,dst) -> None\n\n \t\tRename a file." },
        { "chkpath", (PyCFunction)py_smb_chkpath, METH_VARARGS,
          "chkpath(dir_path) -> True or False\n\n"
          "\t\tReturn true if directory exists, false otherwise." },
        { "savefile", (PyCFunction)py_smb_savefile, METH_VARARGS,
-         "savefile(path, str) -> None\n\n"
-         "\t\tWrite " PY_DESC_PY3_BYTES " str to file." },
+         "savefile(path, bytes) -> None\n\n"
+         "\t\tWrite bytes to file." },
        { "loadfile", (PyCFunction)py_smb_loadfile, METH_VARARGS,
-         "loadfile(path) -> file contents as a " PY_DESC_PY3_BYTES
+         "loadfile(path) -> file contents as a bytes object"
          "\n\n\t\tRead contents of a file." },
-       { "deltree", (PyCFunction)py_smb_deltree, METH_VARARGS,
-         "deltree(path) -> None\n\n"
-         "\t\tDelete a directory and all its contents." },
-       { "get_acl", (PyCFunction)py_smb_getacl, METH_VARARGS,
-         "get_acl(path[, security_info=0]) -> security_descriptor object\n\n"
-         "\t\tGet security descriptor for file." },
-       { "set_acl", (PyCFunction)py_smb_setacl, METH_VARARGS,
-         "set_acl(path, security_descriptor[, security_info=0]) -> None\n\n"
-         "\t\tSet security descriptor for file." },
+       { "get_sd", (PyCFunction)py_smb_get_sd, METH_VARARGS,
+         "get_sd(fnum[, security_info=0]) -> security_descriptor object\n\n"
+         "\t\tGet security descriptor for opened file." },
+       { "set_sd", (PyCFunction)py_smb_set_sd, METH_VARARGS,
+         "set_sd(fnum, security_descriptor[, security_info=0]) -> None\n\n"
+         "\t\tSet security descriptor for opened file." },
+       { "protocol",
+         (PyCFunction)py_smb_protocol,
+         METH_NOARGS,
+         "protocol() -> Number"
+       },
+       { "have_posix",
+         (PyCFunction)py_smb_have_posix,
+         METH_NOARGS,
+         "have_posix() -> True/False\n\n"
+         "\t\tReturn if the server has posix extensions"
+       },
+       { "smb1_posix",
+         (PyCFunction)py_smb_smb1_posix,
+         METH_NOARGS,
+         "Negotiate SMB1 posix extensions",
+       },
+       { "smb1_readlink",
+         (PyCFunction)py_smb_smb1_readlink,
+         METH_VARARGS,
+         "smb1_readlink(path) -> link target",
+       },
+       { "smb1_symlink",
+         (PyCFunction)py_smb_smb1_symlink,
+         METH_VARARGS,
+         "smb1_symlink(target, newname) -> None",
+       },
+       { "smb1_stat",
+         (PyCFunction)py_smb_smb1_stat,
+         METH_VARARGS,
+         "smb1_stat(path) -> stat info",
+       },
+       { "fsctl",
+         (PyCFunction)py_cli_fsctl,
+         METH_VARARGS|METH_KEYWORDS,
+         "fsctl(fnum, ctl_code, in_bytes, max_out) -> out_bytes",
+       },
+       { "mknod",
+         PY_DISCARD_FUNC_SIG(PyCFunction, py_cli_mknod),
+         METH_VARARGS|METH_KEYWORDS,
+         "mknod(path, mode | major, minor)",
+       },
        { NULL, NULL, 0, NULL }
 };
 
 static PyTypeObject py_cli_state_type = {
        PyVarObject_HEAD_INIT(NULL, 0)
-       .tp_name = "libsmb_samba_internal.Conn",
+       .tp_name = "libsmb_samba_cwrapper.LibsmbCConn",
        .tp_basicsize = sizeof(struct py_cli_state),
        .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
-       .tp_doc = "libsmb connection",
+       .tp_doc = "libsmb cwrapper connection",
        .tp_new = py_cli_state_new,
        .tp_init = (initproc)py_cli_state_init,
        .tp_dealloc = (destructor)py_cli_state_dealloc,
@@ -1649,36 +2786,66 @@ static PyTypeObject py_cli_state_type = {
 };
 
 static PyMethodDef py_libsmb_methods[] = {
-       { NULL },
+       {0},
 };
 
-void initlibsmb_samba_internal(void);
+void initlibsmb_samba_cwrapper(void);
 
 static struct PyModuleDef moduledef = {
     PyModuleDef_HEAD_INIT,
-    .m_name = "libsmb_samba_internal",
+    .m_name = "libsmb_samba_cwrapper",
     .m_doc = "libsmb wrapper",
     .m_size = -1,
     .m_methods = py_libsmb_methods,
 };
 
-MODULE_INIT_FUNC(libsmb_samba_internal)
+MODULE_INIT_FUNC(libsmb_samba_cwrapper)
 {
        PyObject *m = NULL;
+       PyObject *mod = NULL;
 
        talloc_stackframe();
 
+       if (PyType_Ready(&py_cli_state_type) < 0) {
+               return NULL;
+       }
+       if (PyType_Ready(&py_cli_notify_state_type) < 0) {
+               return NULL;
+       }
+
        m = PyModule_Create(&moduledef);
        if (m == NULL) {
                return m;
        }
-       if (PyType_Ready(&py_cli_state_type) < 0) {
+
+       /* Import dom_sid type from dcerpc.security */
+       mod = PyImport_ImportModule("samba.dcerpc.security");
+       if (mod == NULL) {
                return NULL;
        }
-       Py_INCREF(&py_cli_state_type);
-       PyModule_AddObject(m, "Conn", (PyObject *)&py_cli_state_type);
 
-#define ADD_FLAGS(val) PyModule_AddObject(m, #val, PyInt_FromLong(val))
+       dom_sid_Type = (PyTypeObject *)PyObject_GetAttrString(mod, "dom_sid");
+       if (dom_sid_Type == NULL) {
+               Py_DECREF(mod);
+               return NULL;
+       }
+
+       Py_INCREF(&py_cli_state_type);
+       PyModule_AddObject(m, "LibsmbCConn", (PyObject *)&py_cli_state_type);
+
+#define ADD_FLAGS(val) PyModule_AddObject(m, #val, PyLong_FromLong(val))
+
+       ADD_FLAGS(PROTOCOL_NONE);
+       ADD_FLAGS(PROTOCOL_CORE);
+       ADD_FLAGS(PROTOCOL_COREPLUS);
+       ADD_FLAGS(PROTOCOL_LANMAN1);
+       ADD_FLAGS(PROTOCOL_LANMAN2);
+       ADD_FLAGS(PROTOCOL_NT1);
+       ADD_FLAGS(PROTOCOL_SMB2_02);
+       ADD_FLAGS(PROTOCOL_SMB2_10);
+       ADD_FLAGS(PROTOCOL_SMB3_00);
+       ADD_FLAGS(PROTOCOL_SMB3_02);
+       ADD_FLAGS(PROTOCOL_SMB3_11);
 
        ADD_FLAGS(FILE_ATTRIBUTE_READONLY);
        ADD_FLAGS(FILE_ATTRIBUTE_HIDDEN);
@@ -1697,5 +2864,156 @@ MODULE_INIT_FUNC(libsmb_samba_internal)
        ADD_FLAGS(FILE_ATTRIBUTE_ENCRYPTED);
        ADD_FLAGS(FILE_ATTRIBUTE_ALL_MASK);
 
+       ADD_FLAGS(FILE_DIRECTORY_FILE);
+       ADD_FLAGS(FILE_WRITE_THROUGH);
+       ADD_FLAGS(FILE_SEQUENTIAL_ONLY);
+       ADD_FLAGS(FILE_NO_INTERMEDIATE_BUFFERING);
+       ADD_FLAGS(FILE_SYNCHRONOUS_IO_ALERT);
+       ADD_FLAGS(FILE_SYNCHRONOUS_IO_NONALERT);
+       ADD_FLAGS(FILE_NON_DIRECTORY_FILE);
+       ADD_FLAGS(FILE_CREATE_TREE_CONNECTION);
+       ADD_FLAGS(FILE_COMPLETE_IF_OPLOCKED);
+       ADD_FLAGS(FILE_NO_EA_KNOWLEDGE);
+       ADD_FLAGS(FILE_EIGHT_DOT_THREE_ONLY);
+       ADD_FLAGS(FILE_RANDOM_ACCESS);
+       ADD_FLAGS(FILE_DELETE_ON_CLOSE);
+       ADD_FLAGS(FILE_OPEN_BY_FILE_ID);
+       ADD_FLAGS(FILE_OPEN_FOR_BACKUP_INTENT);
+       ADD_FLAGS(FILE_NO_COMPRESSION);
+       ADD_FLAGS(FILE_RESERVER_OPFILTER);
+       ADD_FLAGS(FILE_OPEN_REPARSE_POINT);
+       ADD_FLAGS(FILE_OPEN_NO_RECALL);
+       ADD_FLAGS(FILE_OPEN_FOR_FREE_SPACE_QUERY);
+
+       ADD_FLAGS(FILE_SHARE_READ);
+       ADD_FLAGS(FILE_SHARE_WRITE);
+       ADD_FLAGS(FILE_SHARE_DELETE);
+
+       /* change notify completion filter flags */
+       ADD_FLAGS(FILE_NOTIFY_CHANGE_FILE_NAME);
+       ADD_FLAGS(FILE_NOTIFY_CHANGE_DIR_NAME);
+       ADD_FLAGS(FILE_NOTIFY_CHANGE_ATTRIBUTES);
+       ADD_FLAGS(FILE_NOTIFY_CHANGE_SIZE);
+       ADD_FLAGS(FILE_NOTIFY_CHANGE_LAST_WRITE);
+       ADD_FLAGS(FILE_NOTIFY_CHANGE_LAST_ACCESS);
+       ADD_FLAGS(FILE_NOTIFY_CHANGE_CREATION);
+       ADD_FLAGS(FILE_NOTIFY_CHANGE_EA);
+       ADD_FLAGS(FILE_NOTIFY_CHANGE_SECURITY);
+       ADD_FLAGS(FILE_NOTIFY_CHANGE_STREAM_NAME);
+       ADD_FLAGS(FILE_NOTIFY_CHANGE_STREAM_SIZE);
+       ADD_FLAGS(FILE_NOTIFY_CHANGE_STREAM_WRITE);
+       ADD_FLAGS(FILE_NOTIFY_CHANGE_NAME);
+       ADD_FLAGS(FILE_NOTIFY_CHANGE_ALL);
+
+       /* change notify action results */
+       ADD_FLAGS(NOTIFY_ACTION_ADDED);
+       ADD_FLAGS(NOTIFY_ACTION_REMOVED);
+       ADD_FLAGS(NOTIFY_ACTION_MODIFIED);
+       ADD_FLAGS(NOTIFY_ACTION_OLD_NAME);
+       ADD_FLAGS(NOTIFY_ACTION_NEW_NAME);
+       ADD_FLAGS(NOTIFY_ACTION_ADDED_STREAM);
+       ADD_FLAGS(NOTIFY_ACTION_REMOVED_STREAM);
+       ADD_FLAGS(NOTIFY_ACTION_MODIFIED_STREAM);
+
+       /* CreateDisposition values */
+       ADD_FLAGS(FILE_SUPERSEDE);
+       ADD_FLAGS(FILE_OPEN);
+       ADD_FLAGS(FILE_CREATE);
+       ADD_FLAGS(FILE_OPEN_IF);
+       ADD_FLAGS(FILE_OVERWRITE);
+       ADD_FLAGS(FILE_OVERWRITE_IF);
+
+       ADD_FLAGS(FSCTL_DFS_GET_REFERRALS);
+       ADD_FLAGS(FSCTL_DFS_GET_REFERRALS_EX);
+       ADD_FLAGS(FSCTL_REQUEST_OPLOCK_LEVEL_1);
+       ADD_FLAGS(FSCTL_REQUEST_OPLOCK_LEVEL_2);
+       ADD_FLAGS(FSCTL_REQUEST_BATCH_OPLOCK);
+       ADD_FLAGS(FSCTL_OPLOCK_BREAK_ACKNOWLEDGE);
+       ADD_FLAGS(FSCTL_OPBATCH_ACK_CLOSE_PENDING);
+       ADD_FLAGS(FSCTL_OPLOCK_BREAK_NOTIFY);
+       ADD_FLAGS(FSCTL_GET_COMPRESSION);
+       ADD_FLAGS(FSCTL_FILESYS_GET_STATISTICS);
+       ADD_FLAGS(FSCTL_GET_NTFS_VOLUME_DATA);
+       ADD_FLAGS(FSCTL_IS_VOLUME_DIRTY);
+       ADD_FLAGS(FSCTL_FIND_FILES_BY_SID);
+       ADD_FLAGS(FSCTL_SET_OBJECT_ID);
+       ADD_FLAGS(FSCTL_GET_OBJECT_ID);
+       ADD_FLAGS(FSCTL_DELETE_OBJECT_ID);
+       ADD_FLAGS(FSCTL_SET_REPARSE_POINT);
+       ADD_FLAGS(FSCTL_GET_REPARSE_POINT);
+       ADD_FLAGS(FSCTL_DELETE_REPARSE_POINT);
+       ADD_FLAGS(FSCTL_SET_OBJECT_ID_EXTENDED);
+       ADD_FLAGS(FSCTL_CREATE_OR_GET_OBJECT_ID);
+       ADD_FLAGS(FSCTL_SET_SPARSE);
+       ADD_FLAGS(FSCTL_SET_ZERO_DATA);
+       ADD_FLAGS(FSCTL_SET_ZERO_ON_DEALLOCATION);
+       ADD_FLAGS(FSCTL_READ_FILE_USN_DATA);
+       ADD_FLAGS(FSCTL_WRITE_USN_CLOSE_RECORD);
+       ADD_FLAGS(FSCTL_QUERY_ALLOCATED_RANGES);
+       ADD_FLAGS(FSCTL_QUERY_ON_DISK_VOLUME_INFO);
+       ADD_FLAGS(FSCTL_QUERY_SPARING_INFO);
+       ADD_FLAGS(FSCTL_FILE_LEVEL_TRIM);
+       ADD_FLAGS(FSCTL_OFFLOAD_READ);
+       ADD_FLAGS(FSCTL_OFFLOAD_WRITE);
+       ADD_FLAGS(FSCTL_SET_INTEGRITY_INFORMATION);
+       ADD_FLAGS(FSCTL_DUP_EXTENTS_TO_FILE);
+       ADD_FLAGS(FSCTL_DUPLICATE_EXTENTS_TO_FILE_EX);
+       ADD_FLAGS(FSCTL_STORAGE_QOS_CONTROL);
+       ADD_FLAGS(FSCTL_SVHDX_SYNC_TUNNEL_REQUEST);
+       ADD_FLAGS(FSCTL_QUERY_SHARED_VIRTUAL_DISK_SUPPORT);
+       ADD_FLAGS(FSCTL_PIPE_PEEK);
+       ADD_FLAGS(FSCTL_NAMED_PIPE_READ_WRITE);
+       ADD_FLAGS(FSCTL_PIPE_TRANSCEIVE);
+       ADD_FLAGS(FSCTL_PIPE_WAIT);
+       ADD_FLAGS(FSCTL_GET_SHADOW_COPY_DATA);
+       ADD_FLAGS(FSCTL_SRV_ENUM_SNAPS);
+       ADD_FLAGS(FSCTL_SRV_REQUEST_RESUME_KEY);
+       ADD_FLAGS(FSCTL_SRV_COPYCHUNK);
+       ADD_FLAGS(FSCTL_SRV_COPYCHUNK_WRITE);
+       ADD_FLAGS(FSCTL_SRV_READ_HASH);
+       ADD_FLAGS(FSCTL_LMR_REQ_RESILIENCY);
+       ADD_FLAGS(FSCTL_LMR_SET_LINK_TRACKING_INFORMATION);
+       ADD_FLAGS(FSCTL_QUERY_NETWORK_INTERFACE_INFO);
+
+       ADD_FLAGS(SYMLINK_ERROR_TAG);
+       ADD_FLAGS(SYMLINK_FLAG_RELATIVE);
+       ADD_FLAGS(SYMLINK_ADMIN);
+       ADD_FLAGS(SYMLINK_UNTRUSTED);
+       ADD_FLAGS(SYMLINK_TRUST_UNKNOWN);
+       ADD_FLAGS(SYMLINK_TRUST_MASK);
+
+       ADD_FLAGS(IO_REPARSE_TAG_SYMLINK);
+       ADD_FLAGS(IO_REPARSE_TAG_MOUNT_POINT);
+       ADD_FLAGS(IO_REPARSE_TAG_HSM);
+       ADD_FLAGS(IO_REPARSE_TAG_SIS);
+       ADD_FLAGS(IO_REPARSE_TAG_DFS);
+       ADD_FLAGS(IO_REPARSE_TAG_NFS);
+
+#define ADD_STRING(val) PyModule_AddObject(m, #val, PyBytes_FromString(val))
+
+       ADD_STRING(SMB2_CREATE_TAG_EXTA);
+       ADD_STRING(SMB2_CREATE_TAG_MXAC);
+       ADD_STRING(SMB2_CREATE_TAG_SECD);
+       ADD_STRING(SMB2_CREATE_TAG_DHNQ);
+       ADD_STRING(SMB2_CREATE_TAG_DHNC);
+       ADD_STRING(SMB2_CREATE_TAG_ALSI);
+       ADD_STRING(SMB2_CREATE_TAG_TWRP);
+       ADD_STRING(SMB2_CREATE_TAG_QFID);
+       ADD_STRING(SMB2_CREATE_TAG_RQLS);
+       ADD_STRING(SMB2_CREATE_TAG_DH2Q);
+       ADD_STRING(SMB2_CREATE_TAG_DH2C);
+       ADD_STRING(SMB2_CREATE_TAG_AAPL);
+       ADD_STRING(SMB2_CREATE_TAG_APP_INSTANCE_ID);
+       ADD_STRING(SVHDX_OPEN_DEVICE_CONTEXT);
+       ADD_STRING(SMB2_CREATE_TAG_POSIX);
+       ADD_FLAGS(SMB2_FIND_POSIX_INFORMATION);
+       ADD_FLAGS(FILE_SUPERSEDE);
+       ADD_FLAGS(FILE_OPEN);
+       ADD_FLAGS(FILE_CREATE);
+       ADD_FLAGS(FILE_OPEN_IF);
+       ADD_FLAGS(FILE_OVERWRITE);
+       ADD_FLAGS(FILE_OVERWRITE_IF);
+       ADD_FLAGS(FILE_DIRECTORY_FILE);
+
        return m;
 }