libsmb: Pass "flags" through cli_close_send() and pylibsmb
[samba.git] / source3 / libsmb / pylibsmb.c
index 4cbc425bbedd0f285fd814b28ba1c448c24a448f..9fa7ea278eae05b15d12437b06d570917ad879ce 100644 (file)
@@ -51,7 +51,7 @@ c = libsmb.Conn("127.0.0.1",
 #include "python/modules.h"
 #include "libcli/smb/smbXcli_base.h"
 #include "libcli/smb/smb2_negotiate_context.h"
-#include "libcli/smb/reparse_symlink.h"
+#include "libcli/smb/reparse.h"
 #include "libsmb/libsmb.h"
 #include "libcli/security/security.h"
 #include "system/select.h"
@@ -991,9 +991,11 @@ static PyObject *py_cli_create_returns(const struct smb_create_returns *r)
        PyObject *v = NULL;
 
        v = Py_BuildValue(
-               "{sLsLsLsLsLsLsLsLsL}",
+               "{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",
@@ -1135,12 +1137,17 @@ static PyObject *py_cli_create_ex(
        }
 
        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,
-                       CreateFlags,
+                       cflags,
                        ImpersonationLevel,
                        DesiredAccess,
                        FileAttributes,
@@ -1202,26 +1209,10 @@ static PyObject *py_cli_create_ex(
                goto nomem;
        }
 
-       v = PyTuple_New(3);
-       if (v == NULL) {
-               goto nomem;
-       }
-       ret = PyTuple_SetItem(v, 0, Py_BuildValue("I", (unsigned)fnum));
-       if (ret == -1) {
-               status = NT_STATUS_INTERNAL_ERROR;
-               goto fail;
-       }
-       ret = PyTuple_SetItem(v, 1, py_cr);
-       if (ret == -1) {
-               status = NT_STATUS_INTERNAL_ERROR;
-               goto fail;
-       }
-       ret = PyTuple_SetItem(v, 2, py_create_contexts_out);
-       if (ret == -1) {
-               status = NT_STATUS_INTERNAL_ERROR;
-               goto fail;
-       }
-
+       v = Py_BuildValue("(IOO)",
+                         (unsigned)fnum,
+                         py_cr,
+                         py_create_contexts_out);
        return v;
 nomem:
        status = NT_STATUS_NO_MEMORY;
@@ -1251,13 +1242,14 @@ 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;
        }
@@ -1379,7 +1371,7 @@ static PyObject *py_smb_savefile(struct py_cli_state *self, PyObject *args)
        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;
        }
@@ -1501,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;
@@ -1661,10 +1653,7 @@ struct py_cli_notify_state {
 static void py_cli_notify_state_dealloc(struct py_cli_notify_state *self)
 {
        TALLOC_FREE(self->req);
-       if (self->py_cli_state != NULL) {
-               Py_DECREF(self->py_cli_state);
-               self->py_cli_state = NULL;
-       }
+       Py_CLEAR(self->py_cli_state);
        Py_TYPE(self)->tp_free(self);
 }
 
@@ -1816,8 +1805,7 @@ static PyObject *py_cli_notify_get_changes(struct py_cli_notify_state *self,
 
        ok = py_tevent_req_wait_exc(py_cli_state, req);
        self->req = NULL;
-       Py_DECREF(self->py_cli_state);
-       self->py_cli_state = NULL;
+       Py_CLEAR(self->py_cli_state);
        if (!ok) {
                return NULL;
        }
@@ -1890,6 +1878,52 @@ static PyTypeObject py_cli_notify_state_type = {
        .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->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
  */
@@ -1924,6 +1958,18 @@ static NTSTATUS list_helper(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) {
@@ -2022,6 +2068,8 @@ static PyObject *py_cli_list(struct py_cli_state *self,
        PyObject *result = NULL;
        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,
@@ -2042,8 +2090,11 @@ static PyObject *py_cli_list(struct py_cli_state *self,
                }
        }
 
+       if (posix) {
+               callback_fn = &list_posix_helper;
+       }
        status = do_listing(self, base_dir, user_mask, attribute,
-                           info_level, posix, list_helper, result);
+                           info_level, posix, callback_fn, result);
 
        if (!NT_STATUS_IS_OK(status)) {
                Py_XDECREF(result);
@@ -2442,6 +2493,116 @@ static PyObject *py_smb_smb1_symlink(
        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, };
+
+       if (!PyArg_ParseTuple(args, "s:smb1_stat", &fname)) {
+               return NULL;
+       }
+
+       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_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);
+}
+
+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;
+       bool ok;
+
+       ok = ParseTupleAndKeywords(
+               args,
+               kwds,
+               "sI|II:mknod",
+               kwlist,
+               &fname,
+               &mode,
+               &major,
+               &minor);
+       if (!ok) {
+               return NULL;
+       }
+
+#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;
+}
+
 static PyObject *py_cli_fsctl(
        struct py_cli_state *self, PyObject *args, PyObject *kwds)
 {
@@ -2594,11 +2755,21 @@ static PyMethodDef py_cli_state_methods[] = {
          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 }
 };
 
@@ -2804,6 +2975,20 @@ MODULE_INIT_FUNC(libsmb_samba_cwrapper)
        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);
@@ -2821,6 +3006,14 @@ MODULE_INIT_FUNC(libsmb_samba_cwrapper)
        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;
 }