2 * Unix SMB/CIFS implementation.
4 * SMB client Python bindings used internally by Samba (for things like
5 * samba-tool). These Python bindings may change without warning, and so
6 * should not be used outside of the Samba codebase.
8 * Copyright (C) Volker Lendecke 2012
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 3 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program. If not, see <http://www.gnu.org/licenses/>.
26 #include "python/py3compat.h"
27 #include "python/modules.h"
28 #include "libcli/smb/smbXcli_base.h"
29 #include "libsmb/libsmb.h"
30 #include "libcli/security/security.h"
31 #include "system/select.h"
32 #include "source4/libcli/util/pyerrors.h"
33 #include "auth/credentials/pycredentials.h"
35 #include "libsmb/clirap.h"
36 #include "librpc/rpc/pyrpc_util.h"
38 #define LIST_ATTRIBUTE_MASK \
39 (FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_HIDDEN)
41 #define SECINFO_DEFAULT_FLAGS \
42 (SECINFO_OWNER | SECINFO_GROUP | \
43 SECINFO_DACL | SECINFO_PROTECTED_DACL | SECINFO_UNPROTECTED_DACL | \
44 SECINFO_SACL | SECINFO_PROTECTED_SACL | SECINFO_UNPROTECTED_SACL)
46 static PyTypeObject *get_pytype(const char *module, const char *type)
51 mod = PyImport_ImportModule(module);
53 PyErr_Format(PyExc_RuntimeError,
54 "Unable to import %s to check type %s",
58 result = (PyTypeObject *)PyObject_GetAttrString(mod, type);
61 PyErr_Format(PyExc_RuntimeError,
62 "Unable to find type %s in module %s",
70 * We're using "const char * const *" for keywords,
71 * PyArg_ParseTupleAndKeywords expects a "char **". Confine the
72 * inevitable warnings to just one place.
74 static int ParseTupleAndKeywords(PyObject *args, PyObject *kw,
75 const char *format, const char * const *keywords,
78 char **_keywords = discard_const_p(char *, keywords);
81 va_start(a, keywords);
82 ret = PyArg_VaParseTupleAndKeywords(args, kw, format,
90 struct py_cli_oplock_break {
97 struct cli_state *cli;
98 struct tevent_context *ev;
99 int (*req_wait_fn)(struct tevent_context *ev,
100 struct tevent_req *req);
101 struct py_cli_thread *thread_state;
103 struct tevent_req *oplock_waiter;
104 struct py_cli_oplock_break *oplock_breaks;
105 struct py_tevent_cond *oplock_cond;
112 struct py_cli_thread {
115 * Pipe to make the poll thread wake up in our destructor, so
116 * that we can exit and join the thread.
118 int shutdown_pipe[2];
119 struct tevent_fd *shutdown_fde;
124 * Thread state to release the GIL during the poll(2) syscall
126 PyThreadState *py_threadstate;
129 static void *py_cli_state_poll_thread(void *private_data)
131 struct py_cli_state *self = (struct py_cli_state *)private_data;
132 struct py_cli_thread *t = self->thread_state;
133 PyGILState_STATE gstate;
135 gstate = PyGILState_Ensure();
137 while (!t->do_shutdown) {
139 ret = tevent_loop_once(self->ev);
142 PyGILState_Release(gstate);
146 static void py_cli_state_trace_callback(enum tevent_trace_point point,
149 struct py_cli_state *self = (struct py_cli_state *)private_data;
150 struct py_cli_thread *t = self->thread_state;
153 case TEVENT_TRACE_BEFORE_WAIT:
154 assert(t->py_threadstate == NULL);
155 t->py_threadstate = PyEval_SaveThread();
157 case TEVENT_TRACE_AFTER_WAIT:
158 assert(t->py_threadstate != NULL);
159 PyEval_RestoreThread(t->py_threadstate);
160 t->py_threadstate = NULL;
167 static void py_cli_state_shutdown_handler(struct tevent_context *ev,
168 struct tevent_fd *fde,
172 struct py_cli_state *self = (struct py_cli_state *)private_data;
173 struct py_cli_thread *t = self->thread_state;
175 if ((flags & TEVENT_FD_READ) == 0) {
178 TALLOC_FREE(t->shutdown_fde);
179 t->do_shutdown = true;
182 static int py_cli_thread_destructor(struct py_cli_thread *t)
190 * This will wake the poll thread from the poll(2)
192 written = write(t->shutdown_pipe[1], &c, 1);
193 } while ((written == -1) && (errno == EINTR));
196 * Allow the poll thread to do its own cleanup under the GIL
198 Py_BEGIN_ALLOW_THREADS
199 ret = pthread_join(t->id, NULL);
203 if (t->shutdown_pipe[0] != -1) {
204 close(t->shutdown_pipe[0]);
205 t->shutdown_pipe[0] = -1;
207 if (t->shutdown_pipe[1] != -1) {
208 close(t->shutdown_pipe[1]);
209 t->shutdown_pipe[1] = -1;
214 static int py_tevent_cond_req_wait(struct tevent_context *ev,
215 struct tevent_req *req);
217 static bool py_cli_state_setup_mt_ev(struct py_cli_state *self)
219 struct py_cli_thread *t = NULL;
222 self->ev = tevent_context_init_byname(NULL, "poll_mt");
223 if (self->ev == NULL) {
226 samba_tevent_set_debug(self->ev, "pylibsmb_tevent_mt");
227 tevent_set_trace_callback(self->ev, py_cli_state_trace_callback, self);
229 self->req_wait_fn = py_tevent_cond_req_wait;
231 self->thread_state = talloc_zero(NULL, struct py_cli_thread);
232 if (self->thread_state == NULL) {
235 t = self->thread_state;
237 ret = pipe(t->shutdown_pipe);
241 t->shutdown_fde = tevent_add_fd(
242 self->ev, self->ev, t->shutdown_pipe[0], TEVENT_FD_READ,
243 py_cli_state_shutdown_handler, self);
244 if (t->shutdown_fde == NULL) {
248 PyEval_InitThreads();
250 ret = pthread_create(&t->id, NULL, py_cli_state_poll_thread, self);
254 talloc_set_destructor(self->thread_state, py_cli_thread_destructor);
259 TALLOC_FREE(t->shutdown_fde);
261 if (t->shutdown_pipe[0] != -1) {
262 close(t->shutdown_pipe[0]);
263 t->shutdown_pipe[0] = -1;
265 if (t->shutdown_pipe[1] != -1) {
266 close(t->shutdown_pipe[1]);
267 t->shutdown_pipe[1] = -1;
271 TALLOC_FREE(self->thread_state);
272 TALLOC_FREE(self->ev);
276 struct py_tevent_cond {
277 pthread_mutex_t mutex;
282 static void py_tevent_signalme(struct tevent_req *req);
284 static int py_tevent_cond_wait(struct py_tevent_cond *cond)
288 result = pthread_mutex_init(&cond->mutex, NULL);
292 result = pthread_cond_init(&cond->cond, NULL);
297 result = pthread_mutex_lock(&cond->mutex);
302 cond->is_done = false;
304 while (!cond->is_done) {
306 Py_BEGIN_ALLOW_THREADS
307 result = pthread_cond_wait(&cond->cond, &cond->mutex);
316 ret = pthread_mutex_unlock(&cond->mutex);
319 ret = pthread_cond_destroy(&cond->cond);
322 ret = pthread_mutex_destroy(&cond->mutex);
328 static int py_tevent_cond_req_wait(struct tevent_context *ev,
329 struct tevent_req *req)
331 struct py_tevent_cond cond;
332 tevent_req_set_callback(req, py_tevent_signalme, &cond);
333 return py_tevent_cond_wait(&cond);
336 static void py_tevent_cond_signal(struct py_tevent_cond *cond)
340 ret = pthread_mutex_lock(&cond->mutex);
343 cond->is_done = true;
345 ret = pthread_cond_signal(&cond->cond);
347 ret = pthread_mutex_unlock(&cond->mutex);
351 static void py_tevent_signalme(struct tevent_req *req)
353 struct py_tevent_cond *cond = (struct py_tevent_cond *)
354 tevent_req_callback_data_void(req);
356 py_tevent_cond_signal(cond);
361 static int py_tevent_req_wait(struct tevent_context *ev,
362 struct tevent_req *req);
364 static bool py_cli_state_setup_ev(struct py_cli_state *self)
366 self->ev = tevent_context_init(NULL);
367 if (self->ev == NULL) {
371 samba_tevent_set_debug(self->ev, "pylibsmb_tevent");
373 self->req_wait_fn = py_tevent_req_wait;
378 static int py_tevent_req_wait(struct tevent_context *ev,
379 struct tevent_req *req)
381 while (tevent_req_is_in_progress(req)) {
384 ret = tevent_loop_once(ev);
392 static bool py_tevent_req_wait_exc(struct py_cli_state *self,
393 struct tevent_req *req)
401 ret = self->req_wait_fn(self->ev, req);
405 PyErr_SetFromErrno(PyExc_RuntimeError);
411 static PyObject *py_cli_state_new(PyTypeObject *type, PyObject *args,
414 struct py_cli_state *self;
416 self = (struct py_cli_state *)type->tp_alloc(type, 0);
422 self->thread_state = NULL;
423 self->oplock_waiter = NULL;
424 self->oplock_cond = NULL;
425 self->oplock_breaks = NULL;
426 return (PyObject *)self;
429 static void py_cli_got_oplock_break(struct tevent_req *req);
431 static int py_cli_state_init(struct py_cli_state *self, PyObject *args,
436 PyObject *creds = NULL;
437 struct cli_credentials *cli_creds;
438 PyObject *py_lp = Py_None;
439 PyObject *py_multi_threaded = Py_False;
440 bool multi_threaded = false;
441 PyObject *py_force_smb1 = Py_False;
442 bool force_smb1 = false;
443 PyObject *py_ipc = Py_False;
444 bool use_ipc = false;
445 struct tevent_req *req;
449 static const char *kwlist[] = {
450 "host", "share", "lp", "creds",
451 "multi_threaded", "force_smb1",
456 PyTypeObject *py_type_Credentials = get_pytype(
457 "samba.credentials", "Credentials");
458 if (py_type_Credentials == NULL) {
462 ret = ParseTupleAndKeywords(
463 args, kwds, "ssO|O!OOO", kwlist,
464 &host, &share, &py_lp,
465 py_type_Credentials, &creds,
470 Py_DECREF(py_type_Credentials);
476 multi_threaded = PyObject_IsTrue(py_multi_threaded);
477 force_smb1 = PyObject_IsTrue(py_force_smb1);
481 * As most of the cli_*_send() function
482 * don't support SMB2 (it's only plugged
483 * into the sync wrapper functions currently)
484 * we have a way to force SMB1.
486 flags = CLI_FULL_CONNECTION_FORCE_SMB1;
489 use_ipc = PyObject_IsTrue(py_ipc);
491 flags |= CLI_FULL_CONNECTION_IPC;
494 if (multi_threaded) {
496 ret = py_cli_state_setup_mt_ev(self);
501 PyErr_SetString(PyExc_RuntimeError,
502 "No PTHREAD support available");
506 ret = py_cli_state_setup_ev(self);
513 cli_creds = cli_credentials_init_anon(NULL);
515 cli_creds = PyCredentials_AsCliCredentials(creds);
518 req = cli_full_connection_creds_send(
519 NULL, self->ev, "myname", host, NULL, 0, share, "?????",
521 if (!py_tevent_req_wait_exc(self, req)) {
524 status = cli_full_connection_creds_recv(req, &self->cli);
527 if (!NT_STATUS_IS_OK(status)) {
528 PyErr_SetNTSTATUS(status);
533 * Oplocks require a multi threaded connection
535 if (self->thread_state == NULL) {
539 self->oplock_waiter = cli_smb_oplock_break_waiter_send(
540 self->ev, self->ev, self->cli);
541 if (self->oplock_waiter == NULL) {
545 tevent_req_set_callback(self->oplock_waiter, py_cli_got_oplock_break,
550 static void py_cli_got_oplock_break(struct tevent_req *req)
552 struct py_cli_state *self = (struct py_cli_state *)
553 tevent_req_callback_data_void(req);
554 struct py_cli_oplock_break b;
555 struct py_cli_oplock_break *tmp;
559 status = cli_smb_oplock_break_waiter_recv(req, &b.fnum, &b.level);
561 self->oplock_waiter = NULL;
563 if (!NT_STATUS_IS_OK(status)) {
567 num_breaks = talloc_array_length(self->oplock_breaks);
568 tmp = talloc_realloc(self->ev, self->oplock_breaks,
569 struct py_cli_oplock_break, num_breaks+1);
573 self->oplock_breaks = tmp;
574 self->oplock_breaks[num_breaks] = b;
576 if (self->oplock_cond != NULL) {
577 py_tevent_cond_signal(self->oplock_cond);
580 self->oplock_waiter = cli_smb_oplock_break_waiter_send(
581 self->ev, self->ev, self->cli);
582 if (self->oplock_waiter == NULL) {
585 tevent_req_set_callback(self->oplock_waiter, py_cli_got_oplock_break,
589 static PyObject *py_cli_get_oplock_break(struct py_cli_state *self,
592 size_t num_oplock_breaks;
594 if (!PyArg_ParseTuple(args, "")) {
598 if (self->thread_state == NULL) {
599 PyErr_SetString(PyExc_RuntimeError,
600 "get_oplock_break() only possible on "
601 "a multi_threaded connection");
605 if (self->oplock_cond != NULL) {
607 PyErr_SetFromErrno(PyExc_RuntimeError);
611 num_oplock_breaks = talloc_array_length(self->oplock_breaks);
613 if (num_oplock_breaks == 0) {
614 struct py_tevent_cond cond;
617 self->oplock_cond = &cond;
618 ret = py_tevent_cond_wait(&cond);
619 self->oplock_cond = NULL;
623 PyErr_SetFromErrno(PyExc_RuntimeError);
628 num_oplock_breaks = talloc_array_length(self->oplock_breaks);
629 if (num_oplock_breaks > 0) {
632 result = Py_BuildValue(
634 "fnum", self->oplock_breaks[0].fnum,
635 "level", self->oplock_breaks[0].level);
637 memmove(&self->oplock_breaks[0], &self->oplock_breaks[1],
638 sizeof(self->oplock_breaks[0]) *
639 (num_oplock_breaks - 1));
640 self->oplock_breaks = talloc_realloc(
641 NULL, self->oplock_breaks, struct py_cli_oplock_break,
642 num_oplock_breaks - 1);
649 static void py_cli_state_dealloc(struct py_cli_state *self)
651 TALLOC_FREE(self->thread_state);
652 TALLOC_FREE(self->oplock_waiter);
653 TALLOC_FREE(self->ev);
655 if (self->cli != NULL) {
656 cli_shutdown(self->cli);
659 Py_TYPE(self)->tp_free((PyObject *)self);
662 static PyObject *py_cli_settimeout(struct py_cli_state *self, PyObject *args)
664 unsigned int nmsecs = 0;
665 unsigned int omsecs = 0;
667 if (!PyArg_ParseTuple(args, "I", &nmsecs)) {
671 omsecs = cli_set_timeout(self->cli, nmsecs);
673 return PyLong_FromLong(omsecs);
676 static PyObject *py_cli_echo(struct py_cli_state *self,
677 PyObject *Py_UNUSED(ignored))
679 DATA_BLOB data = data_blob_string_const("keepalive");
680 struct tevent_req *req = NULL;
683 req = cli_echo_send(NULL, self->ev, self->cli, 1, data);
684 if (!py_tevent_req_wait_exc(self, req)) {
687 status = cli_echo_recv(req);
689 PyErr_NTSTATUS_NOT_OK_RAISE(status);
694 static PyObject *py_cli_create(struct py_cli_state *self, PyObject *args,
698 unsigned CreateFlags = 0;
699 unsigned DesiredAccess = FILE_GENERIC_READ;
700 unsigned FileAttributes = 0;
701 unsigned ShareAccess = 0;
702 unsigned CreateDisposition = FILE_OPEN;
703 unsigned CreateOptions = 0;
704 unsigned ImpersonationLevel = SMB2_IMPERSONATION_IMPERSONATION;
705 unsigned SecurityFlags = 0;
707 struct tevent_req *req;
710 static const char *kwlist[] = {
711 "Name", "CreateFlags", "DesiredAccess", "FileAttributes",
712 "ShareAccess", "CreateDisposition", "CreateOptions",
713 "ImpersonationLevel", "SecurityFlags", NULL };
715 if (!ParseTupleAndKeywords(
716 args, kwds, "s|IIIIIIII", kwlist,
717 &fname, &CreateFlags, &DesiredAccess, &FileAttributes,
718 &ShareAccess, &CreateDisposition, &CreateOptions,
719 &ImpersonationLevel, &SecurityFlags)) {
723 req = cli_ntcreate_send(NULL, self->ev, self->cli, fname, CreateFlags,
724 DesiredAccess, FileAttributes, ShareAccess,
725 CreateDisposition, CreateOptions,
726 ImpersonationLevel, SecurityFlags);
727 if (!py_tevent_req_wait_exc(self, req)) {
730 status = cli_ntcreate_recv(req, &fnum, NULL);
733 if (!NT_STATUS_IS_OK(status)) {
734 PyErr_SetNTSTATUS(status);
737 return Py_BuildValue("I", (unsigned)fnum);
740 static PyObject *py_cli_close(struct py_cli_state *self, PyObject *args)
742 struct tevent_req *req;
746 if (!PyArg_ParseTuple(args, "i", &fnum)) {
750 req = cli_close_send(NULL, self->ev, self->cli, fnum);
751 if (!py_tevent_req_wait_exc(self, req)) {
754 status = cli_close_recv(req);
757 if (!NT_STATUS_IS_OK(status)) {
758 PyErr_SetNTSTATUS(status);
764 static PyObject *py_cli_rename(
765 struct py_cli_state *self, PyObject *args, PyObject *kwds)
767 char *fname_src = NULL, *fname_dst = NULL;
769 struct tevent_req *req = NULL;
773 static const char *kwlist[] = { "src", "dst", "replace", NULL };
775 ok = ParseTupleAndKeywords(
776 args, kwds, "ss|p", kwlist, &fname_src, &fname_dst, &replace);
781 req = cli_rename_send(
782 NULL, self->ev, self->cli, fname_src, fname_dst, replace);
783 if (!py_tevent_req_wait_exc(self, req)) {
786 status = cli_rename_recv(req);
789 if (!NT_STATUS_IS_OK(status)) {
790 PyErr_SetNTSTATUS(status);
804 * cli_push() helper to write a chunk of data to a remote file
806 static size_t push_data(uint8_t *buf, size_t n, void *priv)
808 struct push_state *state = (struct push_state *)priv;
809 char *curr_ptr = NULL;
813 if (state->nread >= state->total_data) {
817 curr_ptr = state->data + state->nread;
818 remaining = state->total_data - state->nread;
819 copied_bytes = MIN(remaining, n);
821 memcpy(buf, curr_ptr, copied_bytes);
822 state->nread += copied_bytes;
827 * Writes a file with the contents specified
829 static PyObject *py_smb_savefile(struct py_cli_state *self, PyObject *args)
832 const char *filename = NULL;
836 struct tevent_req *req = NULL;
837 struct push_state state;
839 if (!PyArg_ParseTuple(args, "s"PYARG_BYTES_LEN":savefile", &filename,
844 /* create a new file handle for writing to */
845 req = cli_ntcreate_send(NULL, self->ev, self->cli, filename, 0,
846 FILE_WRITE_DATA, FILE_ATTRIBUTE_NORMAL,
847 FILE_SHARE_READ|FILE_SHARE_WRITE,
848 FILE_OVERWRITE_IF, FILE_NON_DIRECTORY_FILE,
849 SMB2_IMPERSONATION_IMPERSONATION, 0);
850 if (!py_tevent_req_wait_exc(self, req)) {
853 status = cli_ntcreate_recv(req, &fnum, NULL);
855 PyErr_NTSTATUS_NOT_OK_RAISE(status);
857 /* write the new file contents */
860 state.total_data = size;
862 req = cli_push_send(NULL, self->ev, self->cli, fnum, 0, 0, 0,
864 if (!py_tevent_req_wait_exc(self, req)) {
867 status = cli_push_recv(req);
869 PyErr_NTSTATUS_NOT_OK_RAISE(status);
871 /* close the file handle */
872 req = cli_close_send(NULL, self->ev, self->cli, fnum);
873 if (!py_tevent_req_wait_exc(self, req)) {
876 status = cli_close_recv(req);
877 PyErr_NTSTATUS_NOT_OK_RAISE(status);
882 static PyObject *py_cli_write(struct py_cli_state *self, PyObject *args,
889 unsigned long long offset;
890 struct tevent_req *req;
894 static const char *kwlist[] = {
895 "fnum", "buffer", "offset", "mode", NULL };
897 if (!ParseTupleAndKeywords(
898 args, kwds, "i" PYARG_BYTES_LEN "K|I", kwlist,
899 &fnum, &buf, &buflen, &offset, &mode)) {
903 req = cli_write_send(NULL, self->ev, self->cli, fnum, mode,
904 (uint8_t *)buf, offset, buflen);
905 if (!py_tevent_req_wait_exc(self, req)) {
908 status = cli_write_recv(req, &written);
911 if (!NT_STATUS_IS_OK(status)) {
912 PyErr_SetNTSTATUS(status);
915 return Py_BuildValue("K", (unsigned long long)written);
919 * Returns the size of the given file
921 static NTSTATUS py_smb_filesize(struct py_cli_state *self, uint16_t fnum,
925 struct tevent_req *req = NULL;
927 req = cli_qfileinfo_basic_send(NULL, self->ev, self->cli, fnum);
928 if (!py_tevent_req_wait_exc(self, req)) {
929 return NT_STATUS_INTERNAL_ERROR;
931 status = cli_qfileinfo_basic_recv(
932 req, NULL, size, NULL, NULL, NULL, NULL, NULL);
938 * Loads the specified file's contents and returns it
940 static PyObject *py_smb_loadfile(struct py_cli_state *self, PyObject *args)
943 const char *filename = NULL;
944 struct tevent_req *req = NULL;
949 PyObject *result = NULL;
951 if (!PyArg_ParseTuple(args, "s:loadfile", &filename)) {
955 /* get a read file handle */
956 req = cli_ntcreate_send(NULL, self->ev, self->cli, filename, 0,
957 FILE_READ_DATA | FILE_READ_ATTRIBUTES,
958 FILE_ATTRIBUTE_NORMAL,
959 FILE_SHARE_READ, FILE_OPEN, 0,
960 SMB2_IMPERSONATION_IMPERSONATION, 0);
961 if (!py_tevent_req_wait_exc(self, req)) {
964 status = cli_ntcreate_recv(req, &fnum, NULL);
966 PyErr_NTSTATUS_NOT_OK_RAISE(status);
968 /* get a buffer to hold the file contents */
969 status = py_smb_filesize(self, fnum, &size);
970 PyErr_NTSTATUS_NOT_OK_RAISE(status);
972 result = PyBytes_FromStringAndSize(NULL, size);
973 if (result == NULL) {
977 /* read the file contents */
978 buf = PyBytes_AS_STRING(result);
979 req = cli_pull_send(NULL, self->ev, self->cli, fnum, 0, size,
980 size, cli_read_sink, &buf);
981 if (!py_tevent_req_wait_exc(self, req)) {
985 status = cli_pull_recv(req, &nread);
987 if (!NT_STATUS_IS_OK(status)) {
989 PyErr_SetNTSTATUS(status);
993 /* close the file handle */
994 req = cli_close_send(NULL, self->ev, self->cli, fnum);
995 if (!py_tevent_req_wait_exc(self, req)) {
999 status = cli_close_recv(req);
1001 if (!NT_STATUS_IS_OK(status)) {
1003 PyErr_SetNTSTATUS(status);
1007 /* sanity-check we read the expected number of bytes */
1010 PyErr_Format(PyExc_IOError,
1011 "read invalid - got %zu requested %zu",
1017 if (_PyBytes_Resize(&result, nread) < 0) {
1025 static PyObject *py_cli_read(struct py_cli_state *self, PyObject *args,
1029 unsigned long long offset;
1031 struct tevent_req *req;
1037 static const char *kwlist[] = {
1038 "fnum", "offset", "size", NULL };
1040 if (!ParseTupleAndKeywords(
1041 args, kwds, "iKI", kwlist, &fnum, &offset,
1046 result = PyBytes_FromStringAndSize(NULL, size);
1047 if (result == NULL) {
1050 buf = PyBytes_AS_STRING(result);
1052 req = cli_read_send(NULL, self->ev, self->cli, fnum,
1054 if (!py_tevent_req_wait_exc(self, req)) {
1058 status = cli_read_recv(req, &received);
1061 if (!NT_STATUS_IS_OK(status)) {
1063 PyErr_SetNTSTATUS(status);
1067 if (received > size) {
1069 PyErr_Format(PyExc_IOError,
1070 "read invalid - got %zu requested %u",
1075 if (received < size) {
1076 if (_PyBytes_Resize(&result, received) < 0) {
1084 static PyObject *py_cli_ftruncate(struct py_cli_state *self, PyObject *args,
1088 unsigned long long size;
1089 struct tevent_req *req;
1092 static const char *kwlist[] = {
1093 "fnum", "size", NULL };
1095 if (!ParseTupleAndKeywords(
1096 args, kwds, "IK", kwlist, &fnum, &size)) {
1100 req = cli_ftruncate_send(NULL, self->ev, self->cli, fnum, size);
1101 if (!py_tevent_req_wait_exc(self, req)) {
1104 status = cli_ftruncate_recv(req);
1107 if (!NT_STATUS_IS_OK(status)) {
1108 PyErr_SetNTSTATUS(status);
1114 static PyObject *py_cli_delete_on_close(struct py_cli_state *self,
1118 unsigned fnum, flag;
1119 struct tevent_req *req;
1122 static const char *kwlist[] = {
1123 "fnum", "flag", NULL };
1125 if (!ParseTupleAndKeywords(
1126 args, kwds, "II", kwlist, &fnum, &flag)) {
1130 req = cli_nt_delete_on_close_send(NULL, self->ev, self->cli, fnum,
1132 if (!py_tevent_req_wait_exc(self, req)) {
1135 status = cli_nt_delete_on_close_recv(req);
1138 if (!NT_STATUS_IS_OK(status)) {
1139 PyErr_SetNTSTATUS(status);
1146 * Helper to add directory listing entries to an overall Python list
1148 static NTSTATUS list_helper(struct file_info *finfo,
1149 const char *mask, void *state)
1151 PyObject *result = (PyObject *)state;
1152 PyObject *file = NULL;
1153 PyObject *size = NULL;
1156 /* suppress '.' and '..' in the results we return */
1157 if (ISDOT(finfo->name) || ISDOTDOT(finfo->name)) {
1158 return NT_STATUS_OK;
1160 size = PyLong_FromUnsignedLongLong(finfo->size);
1162 * Build a dictionary representing the file info.
1163 * Note: Windows does not always return short_name (so it may be None)
1165 file = Py_BuildValue("{s:s,s:i,s:s,s:O,s:l}",
1166 "name", finfo->name,
1167 "attrib", (int)finfo->attr,
1168 "short_name", finfo->short_name,
1171 convert_timespec_to_time_t(finfo->mtime_ts));
1176 return NT_STATUS_NO_MEMORY;
1179 ret = PyList_Append(result, file);
1182 return NT_STATUS_INTERNAL_ERROR;
1185 return NT_STATUS_OK;
1188 struct do_listing_state {
1190 NTSTATUS (*callback_fn)(
1191 struct file_info *finfo,
1193 void *private_data);
1198 static void do_listing_cb(struct tevent_req *subreq)
1200 struct do_listing_state *state = tevent_req_callback_data_void(subreq);
1201 struct file_info *finfo = NULL;
1203 state->status = cli_list_recv(subreq, NULL, &finfo);
1204 if (!NT_STATUS_IS_OK(state->status)) {
1207 state->callback_fn(finfo, state->mask, state->private_data);
1211 static NTSTATUS do_listing(struct py_cli_state *self,
1212 const char *base_dir, const char *user_mask,
1214 NTSTATUS (*callback_fn)(struct file_info *,
1215 const char *, void *),
1219 unsigned int info_level = SMB_FIND_FILE_BOTH_DIRECTORY_INFO;
1220 struct do_listing_state state = {
1222 .callback_fn = callback_fn,
1223 .private_data = priv,
1225 struct tevent_req *req = NULL;
1228 if (user_mask == NULL) {
1229 mask = talloc_asprintf(NULL, "%s\\*", base_dir);
1231 mask = talloc_asprintf(NULL, "%s\\%s", base_dir, user_mask);
1235 return NT_STATUS_NO_MEMORY;
1239 req = cli_list_send(NULL, self->ev, self->cli, mask, attribute,
1242 status = NT_STATUS_NO_MEMORY;
1245 tevent_req_set_callback(req, do_listing_cb, &state);
1247 if (!py_tevent_req_wait_exc(self, req)) {
1248 return NT_STATUS_INTERNAL_ERROR;
1252 status = state.status;
1253 if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_FILES)) {
1254 status = NT_STATUS_OK;
1262 static PyObject *py_cli_list(struct py_cli_state *self,
1267 char *user_mask = NULL;
1268 unsigned int attribute = LIST_ATTRIBUTE_MASK;
1270 PyObject *result = NULL;
1271 const char *kwlist[] = { "directory", "mask", "attribs", NULL };
1273 if (!ParseTupleAndKeywords(args, kwds, "z|sI:list", kwlist,
1274 &base_dir, &user_mask, &attribute)) {
1278 result = Py_BuildValue("[]");
1279 if (result == NULL) {
1283 status = do_listing(self, base_dir, user_mask, attribute,
1284 list_helper, result);
1286 if (!NT_STATUS_IS_OK(status)) {
1288 PyErr_SetNTSTATUS(status);
1295 static PyObject *py_smb_unlink(struct py_cli_state *self, PyObject *args)
1298 const char *filename = NULL;
1299 struct tevent_req *req = NULL;
1300 const uint32_t attrs = (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
1302 if (!PyArg_ParseTuple(args, "s:unlink", &filename)) {
1306 req = cli_unlink_send(NULL, self->ev, self->cli, filename, attrs);
1307 if (!py_tevent_req_wait_exc(self, req)) {
1310 status = cli_unlink_recv(req);
1312 PyErr_NTSTATUS_NOT_OK_RAISE(status);
1317 static PyObject *py_smb_rmdir(struct py_cli_state *self, PyObject *args)
1320 struct tevent_req *req = NULL;
1321 const char *dirname = NULL;
1323 if (!PyArg_ParseTuple(args, "s:rmdir", &dirname)) {
1327 req = cli_rmdir_send(NULL, self->ev, self->cli, dirname);
1328 if (!py_tevent_req_wait_exc(self, req)) {
1331 status = cli_rmdir_recv(req);
1333 PyErr_NTSTATUS_NOT_OK_RAISE(status);
1339 * Create a directory
1341 static PyObject *py_smb_mkdir(struct py_cli_state *self, PyObject *args)
1344 const char *dirname = NULL;
1345 struct tevent_req *req = NULL;
1347 if (!PyArg_ParseTuple(args, "s:mkdir", &dirname)) {
1351 req = cli_mkdir_send(NULL, self->ev, self->cli, dirname);
1352 if (!py_tevent_req_wait_exc(self, req)) {
1355 status = cli_mkdir_recv(req);
1357 PyErr_NTSTATUS_NOT_OK_RAISE(status);
1363 * Checks existence of a directory
1365 static bool check_dir_path(struct py_cli_state *self, const char *path)
1368 struct tevent_req *req = NULL;
1370 req = cli_chkpath_send(NULL, self->ev, self->cli, path);
1371 if (!py_tevent_req_wait_exc(self, req)) {
1374 status = cli_chkpath_recv(req);
1377 return NT_STATUS_IS_OK(status);
1380 static PyObject *py_smb_chkpath(struct py_cli_state *self, PyObject *args)
1382 const char *path = NULL;
1385 if (!PyArg_ParseTuple(args, "s:chkpath", &path)) {
1389 dir_exists = check_dir_path(self, path);
1390 return PyBool_FromLong(dir_exists);
1393 static PyObject *py_smb_get_sd(struct py_cli_state *self, PyObject *args)
1397 struct tevent_req *req = NULL;
1398 struct security_descriptor *sd = NULL;
1401 if (!PyArg_ParseTuple(args, "iI:get_acl", &fnum, &sinfo)) {
1405 req = cli_query_security_descriptor_send(
1406 NULL, self->ev, self->cli, fnum, sinfo);
1407 if (!py_tevent_req_wait_exc(self, req)) {
1410 status = cli_query_security_descriptor_recv(req, NULL, &sd);
1411 PyErr_NTSTATUS_NOT_OK_RAISE(status);
1413 return py_return_ndr_struct(
1414 "samba.dcerpc.security", "descriptor", sd, sd);
1417 static PyObject *py_smb_set_sd(struct py_cli_state *self, PyObject *args)
1419 PyObject *py_sd = NULL;
1420 struct tevent_req *req = NULL;
1421 struct security_descriptor *sd = NULL;
1426 if (!PyArg_ParseTuple(args, "iOI:set_sd", &fnum, &py_sd, &sinfo)) {
1430 sd = pytalloc_get_type(py_sd, struct security_descriptor);
1432 PyErr_Format(PyExc_TypeError,
1433 "Expected dcerpc.security.descriptor as argument, got %s",
1434 pytalloc_get_name(py_sd));
1438 req = cli_set_security_descriptor_send(
1439 NULL, self->ev, self->cli, fnum, sinfo, sd);
1440 if (!py_tevent_req_wait_exc(self, req)) {
1444 status = cli_set_security_descriptor_recv(req);
1445 PyErr_NTSTATUS_NOT_OK_RAISE(status);
1450 static PyMethodDef py_cli_state_methods[] = {
1451 { "settimeout", (PyCFunction)py_cli_settimeout, METH_VARARGS,
1452 "settimeout(new_timeout_msecs) => return old_timeout_msecs" },
1453 { "echo", (PyCFunction)py_cli_echo, METH_NOARGS,
1454 "Ping the server connection" },
1455 { "create", PY_DISCARD_FUNC_SIG(PyCFunction, py_cli_create),
1456 METH_VARARGS|METH_KEYWORDS,
1458 { "close", (PyCFunction)py_cli_close, METH_VARARGS,
1459 "Close a file handle" },
1460 { "write", PY_DISCARD_FUNC_SIG(PyCFunction, py_cli_write),
1461 METH_VARARGS|METH_KEYWORDS,
1462 "Write to a file handle" },
1463 { "read", PY_DISCARD_FUNC_SIG(PyCFunction, py_cli_read),
1464 METH_VARARGS|METH_KEYWORDS,
1465 "Read from a file handle" },
1466 { "truncate", PY_DISCARD_FUNC_SIG(PyCFunction,
1468 METH_VARARGS|METH_KEYWORDS,
1469 "Truncate a file" },
1470 { "delete_on_close", PY_DISCARD_FUNC_SIG(PyCFunction,
1471 py_cli_delete_on_close),
1472 METH_VARARGS|METH_KEYWORDS,
1473 "Set/Reset the delete on close flag" },
1474 { "list", PY_DISCARD_FUNC_SIG(PyCFunction, py_cli_list),
1475 METH_VARARGS|METH_KEYWORDS,
1476 "list(directory, mask='*', attribs=DEFAULT_ATTRS) -> "
1477 "directory contents as a dictionary\n"
1478 "\t\tDEFAULT_ATTRS: FILE_ATTRIBUTE_SYSTEM | "
1479 "FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_ARCHIVE\n\n"
1480 "\t\tList contents of a directory. The keys are, \n"
1481 "\t\t\tname: Long name of the directory item\n"
1482 "\t\t\tshort_name: Short name of the directory item\n"
1483 "\t\t\tsize: File size in bytes\n"
1484 "\t\t\tattrib: Attributes\n"
1485 "\t\t\tmtime: Modification time\n" },
1486 { "get_oplock_break", (PyCFunction)py_cli_get_oplock_break,
1487 METH_VARARGS, "Wait for an oplock break" },
1488 { "unlink", (PyCFunction)py_smb_unlink,
1490 "unlink(path) -> None\n\n \t\tDelete a file." },
1491 { "mkdir", (PyCFunction)py_smb_mkdir, METH_VARARGS,
1492 "mkdir(path) -> None\n\n \t\tCreate a directory." },
1493 { "rmdir", (PyCFunction)py_smb_rmdir, METH_VARARGS,
1494 "rmdir(path) -> None\n\n \t\tDelete a directory." },
1496 PY_DISCARD_FUNC_SIG(PyCFunction, py_cli_rename),
1497 METH_VARARGS|METH_KEYWORDS,
1498 "rename(src,dst) -> None\n\n \t\tRename a file." },
1499 { "chkpath", (PyCFunction)py_smb_chkpath, METH_VARARGS,
1500 "chkpath(dir_path) -> True or False\n\n"
1501 "\t\tReturn true if directory exists, false otherwise." },
1502 { "savefile", (PyCFunction)py_smb_savefile, METH_VARARGS,
1503 "savefile(path, str) -> None\n\n"
1504 "\t\tWrite " PY_DESC_PY3_BYTES " str to file." },
1505 { "loadfile", (PyCFunction)py_smb_loadfile, METH_VARARGS,
1506 "loadfile(path) -> file contents as a " PY_DESC_PY3_BYTES
1507 "\n\n\t\tRead contents of a file." },
1508 { "get_sd", (PyCFunction)py_smb_get_sd, METH_VARARGS,
1509 "get_sd(fnum[, security_info=0]) -> security_descriptor object\n\n"
1510 "\t\tGet security descriptor for opened file." },
1511 { "set_sd", (PyCFunction)py_smb_set_sd, METH_VARARGS,
1512 "set_sd(fnum, security_descriptor[, security_info=0]) -> None\n\n"
1513 "\t\tSet security descriptor for opened file." },
1514 { NULL, NULL, 0, NULL }
1517 static PyTypeObject py_cli_state_type = {
1518 PyVarObject_HEAD_INIT(NULL, 0)
1519 .tp_name = "libsmb_samba_cwrapper.LibsmbCConn",
1520 .tp_basicsize = sizeof(struct py_cli_state),
1521 .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
1522 .tp_doc = "libsmb cwrapper connection",
1523 .tp_new = py_cli_state_new,
1524 .tp_init = (initproc)py_cli_state_init,
1525 .tp_dealloc = (destructor)py_cli_state_dealloc,
1526 .tp_methods = py_cli_state_methods,
1529 static PyMethodDef py_libsmb_methods[] = {
1533 void initlibsmb_samba_cwrapper(void);
1535 static struct PyModuleDef moduledef = {
1536 PyModuleDef_HEAD_INIT,
1537 .m_name = "libsmb_samba_cwrapper",
1538 .m_doc = "libsmb wrapper",
1540 .m_methods = py_libsmb_methods,
1543 MODULE_INIT_FUNC(libsmb_samba_cwrapper)
1547 talloc_stackframe();
1549 m = PyModule_Create(&moduledef);
1553 if (PyType_Ready(&py_cli_state_type) < 0) {
1556 Py_INCREF(&py_cli_state_type);
1557 PyModule_AddObject(m, "LibsmbCConn", (PyObject *)&py_cli_state_type);
1559 #define ADD_FLAGS(val) PyModule_AddObject(m, #val, PyLong_FromLong(val))
1561 ADD_FLAGS(FILE_ATTRIBUTE_READONLY);
1562 ADD_FLAGS(FILE_ATTRIBUTE_HIDDEN);
1563 ADD_FLAGS(FILE_ATTRIBUTE_SYSTEM);
1564 ADD_FLAGS(FILE_ATTRIBUTE_VOLUME);
1565 ADD_FLAGS(FILE_ATTRIBUTE_DIRECTORY);
1566 ADD_FLAGS(FILE_ATTRIBUTE_ARCHIVE);
1567 ADD_FLAGS(FILE_ATTRIBUTE_DEVICE);
1568 ADD_FLAGS(FILE_ATTRIBUTE_NORMAL);
1569 ADD_FLAGS(FILE_ATTRIBUTE_TEMPORARY);
1570 ADD_FLAGS(FILE_ATTRIBUTE_SPARSE);
1571 ADD_FLAGS(FILE_ATTRIBUTE_REPARSE_POINT);
1572 ADD_FLAGS(FILE_ATTRIBUTE_COMPRESSED);
1573 ADD_FLAGS(FILE_ATTRIBUTE_OFFLINE);
1574 ADD_FLAGS(FILE_ATTRIBUTE_NONINDEXED);
1575 ADD_FLAGS(FILE_ATTRIBUTE_ENCRYPTED);
1576 ADD_FLAGS(FILE_ATTRIBUTE_ALL_MASK);
1578 ADD_FLAGS(FILE_SHARE_READ);
1579 ADD_FLAGS(FILE_SHARE_WRITE);
1580 ADD_FLAGS(FILE_SHARE_DELETE);