libsmb: Convert cli_list_recv() to single-recv
[samba.git] / source3 / libsmb / pylibsmb.c
index bb1d2b7f427d010fcdf1acca249cf5aafce69ead..a1d48fe863e62b77b483332169b18f2b0e200550 100644 (file)
@@ -1,6 +1,9 @@
 /*
  * Unix SMB/CIFS implementation.
- * Samba-internal work in progress Python binding for libsmbclient
+ *
+ * SMB client Python bindings used internally by Samba (for things like
+ * samba-tool). These Python bindings may change without warning, and so
+ * should not be used outside of the Samba codebase.
  *
  * Copyright (C) Volker Lendecke 2012
  *
@@ -21,6 +24,7 @@
 #include <Python.h>
 #include "includes.h"
 #include "python/py3compat.h"
+#include "python/modules.h"
 #include "libcli/smb/smbXcli_base.h"
 #include "libsmb/libsmb.h"
 #include "libcli/security/security.h"
 #include "source4/libcli/util/pyerrors.h"
 #include "auth/credentials/pycredentials.h"
 #include "trans2.h"
+#include "libsmb/clirap.h"
+#include "librpc/rpc/pyrpc_util.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 *get_pytype(const char *module, const char *type)
 {
@@ -423,20 +437,21 @@ static int py_cli_state_init(struct py_cli_state *self, PyObject *args,
        char *host, *share;
        PyObject *creds = NULL;
        struct cli_credentials *cli_creds;
+       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;
+       bool use_ipc = false;
        struct tevent_req *req;
        bool ret;
        int flags = 0;
 
        static const char *kwlist[] = {
-               "host", "share", "credentials",
-               "multi_threaded", "sign", "force_smb1",
+               "host", "share", "lp", "creds",
+               "multi_threaded", "force_smb1",
+               "ipc",
                NULL
        };
 
@@ -447,12 +462,12 @@ static int py_cli_state_init(struct py_cli_state *self, PyObject *args,
        }
 
        ret = ParseTupleAndKeywords(
-               args, kwds, "ss|O!OOO", kwlist,
-               &host, &share,
+               args, kwds, "ssO|O!OOO", kwlist,
+               &host, &share, &py_lp,
                py_type_Credentials, &creds,
                &py_multi_threaded,
-               &py_sign,
-               &py_force_smb1);
+               &py_force_smb1,
+               &py_ipc);
 
        Py_DECREF(py_type_Credentials);
 
@@ -461,13 +476,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
@@ -478,6 +488,11 @@ 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;
+       }
+
        if (multi_threaded) {
 #ifdef HAVE_PTHREAD
                ret = py_cli_state_setup_mt_ev(self);
@@ -510,7 +525,7 @@ 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);
        if (!py_tevent_req_wait_exc(self, req)) {
                return -1;
        }
@@ -656,6 +671,20 @@ static void py_cli_state_dealloc(struct py_cli_state *self)
        Py_TYPE(self)->tp_free((PyObject *)self);
 }
 
+static PyObject *py_cli_settimeout(struct py_cli_state *self, PyObject *args)
+{
+       unsigned int nmsecs = 0;
+       unsigned int omsecs = 0;
+
+       if (!PyArg_ParseTuple(args, "I", &nmsecs)) {
+               return NULL;
+       }
+
+       omsecs = cli_set_timeout(self->cli, nmsecs);
+
+       return PyLong_FromLong(omsecs);
+}
+
 static PyObject *py_cli_create(struct py_cli_state *self, PyObject *args,
                               PyObject *kwds)
 {
@@ -666,6 +695,7 @@ static PyObject *py_cli_create(struct py_cli_state *self, PyObject *args,
        unsigned ShareAccess = 0;
        unsigned CreateDisposition = FILE_OPEN;
        unsigned CreateOptions = 0;
+       unsigned ImpersonationLevel = SMB2_IMPERSONATION_IMPERSONATION;
        unsigned SecurityFlags = 0;
        uint16_t fnum;
        struct tevent_req *req;
@@ -674,20 +704,20 @@ static PyObject *py_cli_create(struct py_cli_state *self, PyObject *args,
        static const char *kwlist[] = {
                "Name", "CreateFlags", "DesiredAccess", "FileAttributes",
                "ShareAccess", "CreateDisposition", "CreateOptions",
-               "SecurityFlags", NULL };
+               "ImpersonationLevel", "SecurityFlags", NULL };
 
        if (!ParseTupleAndKeywords(
-                   args, kwds, "s|IIIIIII", kwlist,
+                   args, kwds, "s|IIIIIIII", kwlist,
                    &fname, &CreateFlags, &DesiredAccess, &FileAttributes,
                    &ShareAccess, &CreateDisposition, &CreateOptions,
-                   &SecurityFlags)) {
+                   &ImpersonationLevel, &SecurityFlags)) {
                return NULL;
        }
 
        req = cli_ntcreate_send(NULL, self->ev, self->cli, fname, CreateFlags,
                                DesiredAccess, FileAttributes, ShareAccess,
                                CreateDisposition, CreateOptions,
-                               SecurityFlags);
+                               ImpersonationLevel, SecurityFlags);
        if (!py_tevent_req_wait_exc(self, req)) {
                return NULL;
        }
@@ -725,6 +755,91 @@ static PyObject *py_cli_close(struct py_cli_state *self, PyObject *args)
        Py_RETURN_NONE;
 }
 
+struct push_state {
+       char *data;
+       off_t nread;
+       off_t total_data;
+};
+
+/*
+ * cli_push() helper to write a chunk of data to a remote file
+ */
+static size_t push_data(uint8_t *buf, size_t n, void *priv)
+{
+       struct push_state *state = (struct push_state *)priv;
+       char *curr_ptr = NULL;
+       off_t remaining;
+       size_t copied_bytes;
+
+       if (state->nread >= state->total_data) {
+               return 0;
+       }
+
+       curr_ptr = state->data + state->nread;
+       remaining = state->total_data - state->nread;
+       copied_bytes = MIN(remaining, n);
+
+       memcpy(buf, curr_ptr, copied_bytes);
+       state->nread += copied_bytes;
+       return copied_bytes;
+}
+
+/*
+ * Writes a file with the contents specified
+ */
+static PyObject *py_smb_savefile(struct py_cli_state *self, PyObject *args)
+{
+       uint16_t fnum;
+       const char *filename = NULL;
+       char *data = NULL;
+       Py_ssize_t size = 0;
+       NTSTATUS status;
+       struct tevent_req *req = NULL;
+       struct push_state state;
+
+       if (!PyArg_ParseTuple(args, "s"PYARG_BYTES_LEN":savefile", &filename,
+                             &data, &size)) {
+               return NULL;
+       }
+
+       /* create a new file handle for writing to */
+       req = cli_ntcreate_send(NULL, self->ev, self->cli, filename, 0,
+                               FILE_WRITE_DATA, FILE_ATTRIBUTE_NORMAL,
+                               FILE_SHARE_READ|FILE_SHARE_WRITE,
+                               FILE_OVERWRITE_IF, FILE_NON_DIRECTORY_FILE,
+                               SMB2_IMPERSONATION_IMPERSONATION, 0);
+       if (!py_tevent_req_wait_exc(self, req)) {
+               return NULL;
+       }
+       status = cli_ntcreate_recv(req, &fnum, NULL);
+       TALLOC_FREE(req);
+       PyErr_NTSTATUS_IS_ERR_RAISE(status);
+
+       /* write the new file contents */
+       state.data = data;
+       state.nread = 0;
+       state.total_data = size;
+
+       req = cli_push_send(NULL, self->ev, self->cli, fnum, 0, 0, 0,
+                           push_data, &state);
+       if (!py_tevent_req_wait_exc(self, req)) {
+               return NULL;
+       }
+       status = cli_push_recv(req);
+       TALLOC_FREE(req);
+       PyErr_NTSTATUS_IS_ERR_RAISE(status);
+
+       /* close the file handle */
+       req = cli_close_send(NULL, self->ev, self->cli, fnum);
+       if (!py_tevent_req_wait_exc(self, req)) {
+               return NULL;
+       }
+       status = cli_close_recv(req);
+       PyErr_NTSTATUS_IS_ERR_RAISE(status);
+
+       Py_RETURN_NONE;
+}
+
 static PyObject *py_cli_write(struct py_cli_state *self, PyObject *args,
                              PyObject *kwds)
 {
@@ -741,17 +856,17 @@ static PyObject *py_cli_write(struct py_cli_state *self, PyObject *args,
                "fnum", "buffer", "offset", "mode", NULL };
 
        if (!ParseTupleAndKeywords(
-                   args, kwds, "Is#K|I", kwlist,
+                   args, kwds, "i" PYARG_BYTES_LEN "K|I", kwlist,
                    &fnum, &buf, &buflen, &offset, &mode)) {
                return NULL;
        }
 
-       req = cli_write_andx_send(NULL, self->ev, self->cli, fnum, mode,
-                                 (uint8_t *)buf, offset, buflen);
+       req = cli_write_send(NULL, self->ev, self->cli, fnum, mode,
+                            (uint8_t *)buf, offset, buflen);
        if (!py_tevent_req_wait_exc(self, req)) {
                return NULL;
        }
-       status = cli_write_andx_recv(req, &written);
+       status = cli_write_recv(req, &written);
        TALLOC_FREE(req);
 
        if (!NT_STATUS_IS_OK(status)) {
@@ -761,6 +876,113 @@ static PyObject *py_cli_write(struct py_cli_state *self, PyObject *args,
        return Py_BuildValue("K", (unsigned long long)written);
 }
 
+/*
+ * Returns the size of the given file
+ */
+static NTSTATUS py_smb_filesize(struct py_cli_state *self, uint16_t fnum,
+                               off_t *size)
+{
+       NTSTATUS status;
+       struct tevent_req *req = 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)
+{
+       NTSTATUS status;
+       const char *filename = NULL;
+       struct tevent_req *req = NULL;
+       uint16_t fnum;
+       off_t size;
+       char *buf = NULL;
+       off_t nread = 0;
+       PyObject *result = NULL;
+
+       if (!PyArg_ParseTuple(args, "s:loadfile", &filename)) {
+               return NULL;
+       }
+
+       /* get a read file handle */
+       req = cli_ntcreate_send(NULL, self->ev, self->cli, filename, 0,
+                               FILE_READ_DATA | FILE_READ_ATTRIBUTES,
+                               FILE_ATTRIBUTE_NORMAL,
+                               FILE_SHARE_READ, FILE_OPEN, 0,
+                               SMB2_IMPERSONATION_IMPERSONATION, 0);
+       if (!py_tevent_req_wait_exc(self, req)) {
+               return NULL;
+       }
+       status = cli_ntcreate_recv(req, &fnum, NULL);
+       TALLOC_FREE(req);
+       PyErr_NTSTATUS_IS_ERR_RAISE(status);
+
+       /* get a buffer to hold the file contents */
+       status = py_smb_filesize(self, fnum, &size);
+       PyErr_NTSTATUS_IS_ERR_RAISE(status);
+
+       result = PyBytes_FromStringAndSize(NULL, size);
+       if (result == NULL) {
+               return NULL;
+       }
+
+       /* read the file contents */
+       buf = PyBytes_AS_STRING(result);
+       req = cli_pull_send(NULL, self->ev, self->cli, fnum, 0, size,
+                           size, cli_read_sink, &buf);
+       if (!py_tevent_req_wait_exc(self, req)) {
+               Py_XDECREF(result);
+               return NULL;
+       }
+       status = cli_pull_recv(req, &nread);
+       TALLOC_FREE(req);
+       if (!NT_STATUS_IS_OK(status)) {
+               Py_XDECREF(result);
+               PyErr_SetNTSTATUS(status);
+               return NULL;
+       }
+
+       /* close the file handle */
+       req = cli_close_send(NULL, self->ev, self->cli, fnum);
+       if (!py_tevent_req_wait_exc(self, req)) {
+               Py_XDECREF(result);
+               return NULL;
+       }
+       status = cli_close_recv(req);
+       TALLOC_FREE(req);
+       if (!NT_STATUS_IS_OK(status)) {
+               Py_XDECREF(result);
+               PyErr_SetNTSTATUS(status);
+               return NULL;
+       }
+
+       /* sanity-check we read the expected number of bytes */
+       if (nread > size) {
+               Py_XDECREF(result);
+               PyErr_Format(PyExc_IOError,
+                            "read invalid - got %zu requested %zu",
+                            nread, size);
+               return NULL;
+       }
+
+       if (nread < size) {
+               if (_PyBytes_Resize(&result, nread) < 0) {
+                       return NULL;
+               }
+       }
+
+       return result;
+}
+
 static PyObject *py_cli_read(struct py_cli_state *self, PyObject *args,
                             PyObject *kwds)
 {
@@ -769,33 +991,54 @@ static PyObject *py_cli_read(struct py_cli_state *self, PyObject *args,
        unsigned size;
        struct tevent_req *req;
        NTSTATUS status;
-       uint8_t *buf;
-       ssize_t buflen;
+       char *buf;
+       size_t received;
        PyObject *result;
 
        static const char *kwlist[] = {
                "fnum", "offset", "size", NULL };
 
        if (!ParseTupleAndKeywords(
-                   args, kwds, "IKI", kwlist, &fnum, &offset,
+                   args, kwds, "iKI", kwlist, &fnum, &offset,
                    &size)) {
                return NULL;
        }
 
-       req = cli_read_andx_send(NULL, self->ev, self->cli, fnum,
-                                offset, size);
+       result = PyBytes_FromStringAndSize(NULL, size);
+       if (result == NULL) {
+               return NULL;
+       }
+       buf = PyBytes_AS_STRING(result);
+
+       req = cli_read_send(NULL, self->ev, self->cli, fnum,
+                           buf, offset, size);
        if (!py_tevent_req_wait_exc(self, req)) {
+               Py_XDECREF(result);
                return NULL;
        }
-       status = cli_read_andx_recv(req, &buflen, &buf);
+       status = cli_read_recv(req, &received);
+       TALLOC_FREE(req);
 
        if (!NT_STATUS_IS_OK(status)) {
-               TALLOC_FREE(req);
+               Py_XDECREF(result);
                PyErr_SetNTSTATUS(status);
                return NULL;
        }
-       result = PyBytes_FromStringAndSize((const char *)buf, buflen);
-       TALLOC_FREE(req);
+
+       if (received > size) {
+               Py_XDECREF(result);
+               PyErr_Format(PyExc_IOError,
+                            "read invalid - got %zu requested %u",
+                            received, size);
+               return NULL;
+       }
+
+       if (received < size) {
+               if (_PyBytes_Resize(&result, received) < 0) {
+                       return NULL;
+               }
+       }
+
        return result;
 }
 
@@ -860,103 +1103,384 @@ 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(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",
+                            convert_timespec_to_time_t(finfo->mtime_ts));
+
+       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;
+}
+
+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)(struct file_info *,
+                                                  const char *, void *),
+                          void *priv)
+{
+       char *mask = NULL;
+       unsigned int info_level = SMB_FIND_FILE_BOTH_DIRECTORY_INFO;
+       NTSTATUS status;
+
+       if (user_mask == NULL) {
+               mask = talloc_asprintf(NULL, "%s\\*", base_dir);
+       } else {
+               mask = talloc_asprintf(NULL, "%s\\%s", base_dir, user_mask);
+       }
+
+       if (mask == NULL) {
+               return NT_STATUS_NO_MEMORY;
+       }
+       dos_format(mask);
+
+       if (self->is_smb1) {
+               struct do_listing_state state = {
+                       .mask = mask,
+                       .callback_fn = callback_fn,
+                       .private_data = priv,
+               };
+               struct tevent_req *req = NULL;
+
+               req = cli_list_send(NULL, self->ev, self->cli, mask, attribute,
+                                   info_level);
+               if (req == NULL) {
+                       status = NT_STATUS_NO_MEMORY;
+                       goto done;
+               }
+               tevent_req_set_callback(req, do_listing_cb, &state);
+
+               if (!py_tevent_req_wait_exc(self, req)) {
+                       return NT_STATUS_INTERNAL_ERROR;
+               }
+               TALLOC_FREE(req);
+
+               status = state.status;
+               if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_FILES)) {
+                       status = NT_STATUS_OK;
+               }
+       } else {
+               status = cli_list(self->cli, mask, attribute, callback_fn,
+                                 priv);
+       }
+
+done:
+       TALLOC_FREE(mask);
+       return status;
+}
+
 static PyObject *py_cli_list(struct py_cli_state *self,
                             PyObject *args,
                             PyObject *kwds)
 {
-       char *mask;
-       unsigned attribute =
-               FILE_ATTRIBUTE_DIRECTORY |
-               FILE_ATTRIBUTE_SYSTEM |
-               FILE_ATTRIBUTE_HIDDEN;
-       unsigned info_level = SMB_FIND_FILE_BOTH_DIRECTORY_INFO;
-       struct tevent_req *req;
+       char *base_dir;
+       char *user_mask = NULL;
+       unsigned int attribute = LIST_ATTRIBUTE_MASK;
        NTSTATUS status;
-       struct file_info *finfos;
-       size_t i, num_finfos;
-       PyObject *result;
+       PyObject *result = NULL;
+       const char *kwlist[] = { "directory", "mask", "attribs", NULL };
 
-       const char *kwlist[] = {
-               "mask", "attribute", "info_level", NULL
-       };
+       if (!ParseTupleAndKeywords(args, kwds, "z|sI:list", kwlist,
+                                  &base_dir, &user_mask, &attribute)) {
+               return NULL;
+       }
 
-       if (!ParseTupleAndKeywords(
-                   args, kwds, "s|II", kwlist,
-                   &mask, &attribute, &info_level)) {
+       result = Py_BuildValue("[]");
+       if (result == NULL) {
+               return NULL;
+       }
+
+       status = do_listing(self, base_dir, user_mask, attribute,
+                           list_helper, result);
+
+       if (!NT_STATUS_IS_OK(status)) {
+               Py_XDECREF(result);
+               PyErr_SetNTSTATUS(status);
                return NULL;
        }
 
-       req = cli_list_send(NULL, self->ev, self->cli, mask, attribute,
-                           info_level);
+       return result;
+}
+
+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;
+       }
+
+       req = cli_unlink_send(NULL, self->ev, self->cli, filename, attrs);
        if (!py_tevent_req_wait_exc(self, req)) {
                return NULL;
        }
-       status = cli_list_recv(req, NULL, &finfos, &num_finfos);
+       status = cli_unlink_recv(req);
        TALLOC_FREE(req);
+       PyErr_NTSTATUS_NOT_OK_RAISE(status);
 
-       if (!NT_STATUS_IS_OK(status)) {
-               PyErr_SetNTSTATUS(status);
+       Py_RETURN_NONE;
+}
+
+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;
        }
 
-       result = Py_BuildValue("[]");
-       if (result == NULL) {
+       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_IS_ERR_RAISE(status);
+
+       Py_RETURN_NONE;
+}
+
+/*
+ * Create a directory
+ */
+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;
        }
 
-       for (i=0; i<num_finfos; i++) {
-               struct file_info *finfo = &finfos[i];
-               PyObject *file;
-               int ret;
+       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_IS_ERR_RAISE(status);
 
-               file = Py_BuildValue(
-                       "{s:s,s:i}",
-                       "name", finfo->name,
-                       "mode", (int)finfo->mode);
-               if (file == NULL) {
-                       Py_XDECREF(result);
-                       return NULL;
-               }
+       Py_RETURN_NONE;
+}
 
-               ret = PyList_Append(result, file);
-               if (ret == -1) {
-                       Py_XDECREF(result);
-                       return NULL;
-               }
+/*
+ * Checks existence of a directory
+ */
+static bool check_dir_path(struct py_cli_state *self, const char *path)
+{
+       NTSTATUS status;
+       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);
 
-       return result;
+       return NT_STATUS_IS_OK(status);
+}
+
+static PyObject *py_smb_chkpath(struct py_cli_state *self, PyObject *args)
+{
+       const char *path = NULL;
+       bool dir_exists;
+
+       if (!PyArg_ParseTuple(args, "s:chkpath", &path)) {
+               return NULL;
+       }
+
+       dir_exists = check_dir_path(self, path);
+       return PyBool_FromLong(dir_exists);
+}
+
+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;
+
+       if (!PyArg_ParseTuple(args, "iI:get_acl", &fnum, &sinfo)) {
+               return NULL;
+       }
+
+       req = cli_query_security_descriptor_send(
+               NULL, self->ev, self->cli, fnum, sinfo);
+       if (!py_tevent_req_wait_exc(self, req)) {
+               return false;
+       }
+       status = cli_query_security_descriptor_recv(req, NULL, &sd);
+       PyErr_NTSTATUS_IS_ERR_RAISE(status);
+
+       return py_return_ndr_struct(
+               "samba.dcerpc.security", "descriptor", sd, sd);
+}
+
+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;
+       }
+
+       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 false;
+       }
+
+       status = cli_set_security_descriptor_recv(req);
+       PyErr_NTSTATUS_IS_ERR_RAISE(status);
+
+       Py_RETURN_NONE;
 }
 
 static PyMethodDef py_cli_state_methods[] = {
-       { "create", (PyCFunction)py_cli_create, METH_VARARGS|METH_KEYWORDS,
+       { "settimeout", (PyCFunction)py_cli_settimeout, METH_VARARGS,
+         "settimeout(new_timeout_msecs) => return old_timeout_msecs" },
+       { "create", PY_DISCARD_FUNC_SIG(PyCFunction, py_cli_create),
+               METH_VARARGS|METH_KEYWORDS,
          "Open a file" },
        { "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" },
-       { "readdir", (PyCFunction)py_cli_list,
-         METH_VARARGS|METH_KEYWORDS,
-         "List a directory" },
+       { "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 | "
+         "FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_ARCHIVE\n\n"
+         "\t\tList contents of a directory. The keys are, \n"
+         "\t\t\tname: Long name of the directory item\n"
+         "\t\t\tshort_name: Short name of the directory item\n"
+         "\t\t\tsize: File size in bytes\n"
+         "\t\t\tattrib: Attributes\n"
+         "\t\t\tmtime: Modification time\n" },
        { "get_oplock_break", (PyCFunction)py_cli_get_oplock_break,
          METH_VARARGS, "Wait for an oplock break" },
+       { "unlink", (PyCFunction)py_smb_unlink,
+         METH_VARARGS,
+         "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." },
+       { "rmdir", (PyCFunction)py_smb_rmdir, METH_VARARGS,
+         "rmdir(path) -> None\n\n \t\tDelete a directory." },
+       { "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." },
+       { "loadfile", (PyCFunction)py_smb_loadfile, METH_VARARGS,
+         "loadfile(path) -> file contents as a " PY_DESC_PY3_BYTES
+         "\n\n\t\tRead contents of a 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." },
        { 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,
@@ -964,20 +1488,20 @@ 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;
 
@@ -991,6 +1515,30 @@ MODULE_INIT_FUNC(libsmb_samba_internal)
                return NULL;
        }
        Py_INCREF(&py_cli_state_type);
-       PyModule_AddObject(m, "Conn", (PyObject *)&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(FILE_ATTRIBUTE_READONLY);
+       ADD_FLAGS(FILE_ATTRIBUTE_HIDDEN);
+       ADD_FLAGS(FILE_ATTRIBUTE_SYSTEM);
+       ADD_FLAGS(FILE_ATTRIBUTE_VOLUME);
+       ADD_FLAGS(FILE_ATTRIBUTE_DIRECTORY);
+       ADD_FLAGS(FILE_ATTRIBUTE_ARCHIVE);
+       ADD_FLAGS(FILE_ATTRIBUTE_DEVICE);
+       ADD_FLAGS(FILE_ATTRIBUTE_NORMAL);
+       ADD_FLAGS(FILE_ATTRIBUTE_TEMPORARY);
+       ADD_FLAGS(FILE_ATTRIBUTE_SPARSE);
+       ADD_FLAGS(FILE_ATTRIBUTE_REPARSE_POINT);
+       ADD_FLAGS(FILE_ATTRIBUTE_COMPRESSED);
+       ADD_FLAGS(FILE_ATTRIBUTE_OFFLINE);
+       ADD_FLAGS(FILE_ATTRIBUTE_NONINDEXED);
+       ADD_FLAGS(FILE_ATTRIBUTE_ENCRYPTED);
+       ADD_FLAGS(FILE_ATTRIBUTE_ALL_MASK);
+
+       ADD_FLAGS(FILE_SHARE_READ);
+       ADD_FLAGS(FILE_SHARE_WRITE);
+       ADD_FLAGS(FILE_SHARE_DELETE);
+
        return m;
 }