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
;
99 struct tevent_context
*ev
;
100 int (*req_wait_fn
)(struct tevent_context
*ev
,
101 struct tevent_req
*req
);
102 struct py_cli_thread
*thread_state
;
104 struct tevent_req
*oplock_waiter
;
105 struct py_cli_oplock_break
*oplock_breaks
;
106 struct py_tevent_cond
*oplock_cond
;
113 struct py_cli_thread
{
116 * Pipe to make the poll thread wake up in our destructor, so
117 * that we can exit and join the thread.
119 int shutdown_pipe
[2];
120 struct tevent_fd
*shutdown_fde
;
125 * Thread state to release the GIL during the poll(2) syscall
127 PyThreadState
*py_threadstate
;
130 static void *py_cli_state_poll_thread(void *private_data
)
132 struct py_cli_state
*self
= (struct py_cli_state
*)private_data
;
133 struct py_cli_thread
*t
= self
->thread_state
;
134 PyGILState_STATE gstate
;
136 gstate
= PyGILState_Ensure();
138 while (!t
->do_shutdown
) {
140 ret
= tevent_loop_once(self
->ev
);
143 PyGILState_Release(gstate
);
147 static void py_cli_state_trace_callback(enum tevent_trace_point point
,
150 struct py_cli_state
*self
= (struct py_cli_state
*)private_data
;
151 struct py_cli_thread
*t
= self
->thread_state
;
154 case TEVENT_TRACE_BEFORE_WAIT
:
155 assert(t
->py_threadstate
== NULL
);
156 t
->py_threadstate
= PyEval_SaveThread();
158 case TEVENT_TRACE_AFTER_WAIT
:
159 assert(t
->py_threadstate
!= NULL
);
160 PyEval_RestoreThread(t
->py_threadstate
);
161 t
->py_threadstate
= NULL
;
168 static void py_cli_state_shutdown_handler(struct tevent_context
*ev
,
169 struct tevent_fd
*fde
,
173 struct py_cli_state
*self
= (struct py_cli_state
*)private_data
;
174 struct py_cli_thread
*t
= self
->thread_state
;
176 if ((flags
& TEVENT_FD_READ
) == 0) {
179 TALLOC_FREE(t
->shutdown_fde
);
180 t
->do_shutdown
= true;
183 static int py_cli_thread_destructor(struct py_cli_thread
*t
)
191 * This will wake the poll thread from the poll(2)
193 written
= write(t
->shutdown_pipe
[1], &c
, 1);
194 } while ((written
== -1) && (errno
== EINTR
));
197 * Allow the poll thread to do its own cleanup under the GIL
199 Py_BEGIN_ALLOW_THREADS
200 ret
= pthread_join(t
->id
, NULL
);
204 if (t
->shutdown_pipe
[0] != -1) {
205 close(t
->shutdown_pipe
[0]);
206 t
->shutdown_pipe
[0] = -1;
208 if (t
->shutdown_pipe
[1] != -1) {
209 close(t
->shutdown_pipe
[1]);
210 t
->shutdown_pipe
[1] = -1;
215 static int py_tevent_cond_req_wait(struct tevent_context
*ev
,
216 struct tevent_req
*req
);
218 static bool py_cli_state_setup_mt_ev(struct py_cli_state
*self
)
220 struct py_cli_thread
*t
= NULL
;
223 self
->ev
= tevent_context_init_byname(NULL
, "poll_mt");
224 if (self
->ev
== NULL
) {
227 samba_tevent_set_debug(self
->ev
, "pylibsmb_tevent_mt");
228 tevent_set_trace_callback(self
->ev
, py_cli_state_trace_callback
, self
);
230 self
->req_wait_fn
= py_tevent_cond_req_wait
;
232 self
->thread_state
= talloc_zero(NULL
, struct py_cli_thread
);
233 if (self
->thread_state
== NULL
) {
236 t
= self
->thread_state
;
238 ret
= pipe(t
->shutdown_pipe
);
242 t
->shutdown_fde
= tevent_add_fd(
243 self
->ev
, self
->ev
, t
->shutdown_pipe
[0], TEVENT_FD_READ
,
244 py_cli_state_shutdown_handler
, self
);
245 if (t
->shutdown_fde
== NULL
) {
249 PyEval_InitThreads();
251 ret
= pthread_create(&t
->id
, NULL
, py_cli_state_poll_thread
, self
);
255 talloc_set_destructor(self
->thread_state
, py_cli_thread_destructor
);
260 TALLOC_FREE(t
->shutdown_fde
);
262 if (t
->shutdown_pipe
[0] != -1) {
263 close(t
->shutdown_pipe
[0]);
264 t
->shutdown_pipe
[0] = -1;
266 if (t
->shutdown_pipe
[1] != -1) {
267 close(t
->shutdown_pipe
[1]);
268 t
->shutdown_pipe
[1] = -1;
272 TALLOC_FREE(self
->thread_state
);
273 TALLOC_FREE(self
->ev
);
277 struct py_tevent_cond
{
278 pthread_mutex_t mutex
;
283 static void py_tevent_signalme(struct tevent_req
*req
);
285 static int py_tevent_cond_wait(struct py_tevent_cond
*cond
)
289 result
= pthread_mutex_init(&cond
->mutex
, NULL
);
293 result
= pthread_cond_init(&cond
->cond
, NULL
);
298 result
= pthread_mutex_lock(&cond
->mutex
);
303 cond
->is_done
= false;
305 while (!cond
->is_done
) {
307 Py_BEGIN_ALLOW_THREADS
308 result
= pthread_cond_wait(&cond
->cond
, &cond
->mutex
);
317 ret
= pthread_mutex_unlock(&cond
->mutex
);
320 ret
= pthread_cond_destroy(&cond
->cond
);
323 ret
= pthread_mutex_destroy(&cond
->mutex
);
329 static int py_tevent_cond_req_wait(struct tevent_context
*ev
,
330 struct tevent_req
*req
)
332 struct py_tevent_cond cond
;
333 tevent_req_set_callback(req
, py_tevent_signalme
, &cond
);
334 return py_tevent_cond_wait(&cond
);
337 static void py_tevent_cond_signal(struct py_tevent_cond
*cond
)
341 ret
= pthread_mutex_lock(&cond
->mutex
);
344 cond
->is_done
= true;
346 ret
= pthread_cond_signal(&cond
->cond
);
348 ret
= pthread_mutex_unlock(&cond
->mutex
);
352 static void py_tevent_signalme(struct tevent_req
*req
)
354 struct py_tevent_cond
*cond
= (struct py_tevent_cond
*)
355 tevent_req_callback_data_void(req
);
357 py_tevent_cond_signal(cond
);
362 static int py_tevent_req_wait(struct tevent_context
*ev
,
363 struct tevent_req
*req
);
365 static bool py_cli_state_setup_ev(struct py_cli_state
*self
)
367 self
->ev
= tevent_context_init(NULL
);
368 if (self
->ev
== NULL
) {
372 samba_tevent_set_debug(self
->ev
, "pylibsmb_tevent");
374 self
->req_wait_fn
= py_tevent_req_wait
;
379 static int py_tevent_req_wait(struct tevent_context
*ev
,
380 struct tevent_req
*req
)
382 while (tevent_req_is_in_progress(req
)) {
385 ret
= tevent_loop_once(ev
);
393 static bool py_tevent_req_wait_exc(struct py_cli_state
*self
,
394 struct tevent_req
*req
)
402 ret
= self
->req_wait_fn(self
->ev
, req
);
406 PyErr_SetFromErrno(PyExc_RuntimeError
);
412 static PyObject
*py_cli_state_new(PyTypeObject
*type
, PyObject
*args
,
415 struct py_cli_state
*self
;
417 self
= (struct py_cli_state
*)type
->tp_alloc(type
, 0);
422 self
->is_smb1
= false;
424 self
->thread_state
= NULL
;
425 self
->oplock_waiter
= NULL
;
426 self
->oplock_cond
= NULL
;
427 self
->oplock_breaks
= NULL
;
428 return (PyObject
*)self
;
431 static void py_cli_got_oplock_break(struct tevent_req
*req
);
433 static int py_cli_state_init(struct py_cli_state
*self
, PyObject
*args
,
438 PyObject
*creds
= NULL
;
439 struct cli_credentials
*cli_creds
;
440 PyObject
*py_lp
= Py_None
;
441 PyObject
*py_multi_threaded
= Py_False
;
442 bool multi_threaded
= false;
443 PyObject
*py_sign
= Py_False
;
445 int signing_state
= SMB_SIGNING_DEFAULT
;
446 PyObject
*py_force_smb1
= Py_False
;
447 bool force_smb1
= false;
448 struct tevent_req
*req
;
452 static const char *kwlist
[] = {
453 "host", "share", "lp", "creds",
454 "multi_threaded", "sign", "force_smb1",
458 PyTypeObject
*py_type_Credentials
= get_pytype(
459 "samba.credentials", "Credentials");
460 if (py_type_Credentials
== NULL
) {
464 ret
= ParseTupleAndKeywords(
465 args
, kwds
, "ssO|O!OOO", kwlist
,
466 &host
, &share
, &py_lp
,
467 py_type_Credentials
, &creds
,
472 Py_DECREF(py_type_Credentials
);
478 multi_threaded
= PyObject_IsTrue(py_multi_threaded
);
479 sign
= PyObject_IsTrue(py_sign
);
480 force_smb1
= PyObject_IsTrue(py_force_smb1
);
483 signing_state
= SMB_SIGNING_REQUIRED
;
488 * As most of the cli_*_send() function
489 * don't support SMB2 (it's only plugged
490 * into the sync wrapper functions currently)
491 * we have a way to force SMB1.
493 flags
= CLI_FULL_CONNECTION_FORCE_SMB1
;
496 if (multi_threaded
) {
498 ret
= py_cli_state_setup_mt_ev(self
);
503 PyErr_SetString(PyExc_RuntimeError
,
504 "No PTHREAD support available");
508 PyErr_SetString(PyExc_RuntimeError
,
509 "multi_threaded is only possible on "
514 ret
= py_cli_state_setup_ev(self
);
521 cli_creds
= cli_credentials_init_anon(NULL
);
523 cli_creds
= PyCredentials_AsCliCredentials(creds
);
526 req
= cli_full_connection_creds_send(
527 NULL
, self
->ev
, "myname", host
, NULL
, 0, share
, "?????",
528 cli_creds
, flags
, signing_state
);
529 if (!py_tevent_req_wait_exc(self
, req
)) {
532 status
= cli_full_connection_creds_recv(req
, &self
->cli
);
535 if (!NT_STATUS_IS_OK(status
)) {
536 PyErr_SetNTSTATUS(status
);
540 if (smbXcli_conn_protocol(self
->cli
->conn
) < PROTOCOL_SMB2_02
) {
541 self
->is_smb1
= true;
545 * Oplocks require a multi threaded connection
547 if (self
->thread_state
== NULL
) {
551 self
->oplock_waiter
= cli_smb_oplock_break_waiter_send(
552 self
->ev
, self
->ev
, self
->cli
);
553 if (self
->oplock_waiter
== NULL
) {
557 tevent_req_set_callback(self
->oplock_waiter
, py_cli_got_oplock_break
,
562 static void py_cli_got_oplock_break(struct tevent_req
*req
)
564 struct py_cli_state
*self
= (struct py_cli_state
*)
565 tevent_req_callback_data_void(req
);
566 struct py_cli_oplock_break b
;
567 struct py_cli_oplock_break
*tmp
;
571 status
= cli_smb_oplock_break_waiter_recv(req
, &b
.fnum
, &b
.level
);
573 self
->oplock_waiter
= NULL
;
575 if (!NT_STATUS_IS_OK(status
)) {
579 num_breaks
= talloc_array_length(self
->oplock_breaks
);
580 tmp
= talloc_realloc(self
->ev
, self
->oplock_breaks
,
581 struct py_cli_oplock_break
, num_breaks
+1);
585 self
->oplock_breaks
= tmp
;
586 self
->oplock_breaks
[num_breaks
] = b
;
588 if (self
->oplock_cond
!= NULL
) {
589 py_tevent_cond_signal(self
->oplock_cond
);
592 self
->oplock_waiter
= cli_smb_oplock_break_waiter_send(
593 self
->ev
, self
->ev
, self
->cli
);
594 if (self
->oplock_waiter
== NULL
) {
597 tevent_req_set_callback(self
->oplock_waiter
, py_cli_got_oplock_break
,
601 static PyObject
*py_cli_get_oplock_break(struct py_cli_state
*self
,
604 size_t num_oplock_breaks
;
606 if (!PyArg_ParseTuple(args
, "")) {
610 if (self
->thread_state
== NULL
) {
611 PyErr_SetString(PyExc_RuntimeError
,
612 "get_oplock_break() only possible on "
613 "a multi_threaded connection");
617 if (self
->oplock_cond
!= NULL
) {
619 PyErr_SetFromErrno(PyExc_RuntimeError
);
623 num_oplock_breaks
= talloc_array_length(self
->oplock_breaks
);
625 if (num_oplock_breaks
== 0) {
626 struct py_tevent_cond cond
;
629 self
->oplock_cond
= &cond
;
630 ret
= py_tevent_cond_wait(&cond
);
631 self
->oplock_cond
= NULL
;
635 PyErr_SetFromErrno(PyExc_RuntimeError
);
640 num_oplock_breaks
= talloc_array_length(self
->oplock_breaks
);
641 if (num_oplock_breaks
> 0) {
644 result
= Py_BuildValue(
646 "fnum", self
->oplock_breaks
[0].fnum
,
647 "level", self
->oplock_breaks
[0].level
);
649 memmove(&self
->oplock_breaks
[0], &self
->oplock_breaks
[1],
650 sizeof(self
->oplock_breaks
[0]) *
651 (num_oplock_breaks
- 1));
652 self
->oplock_breaks
= talloc_realloc(
653 NULL
, self
->oplock_breaks
, struct py_cli_oplock_break
,
654 num_oplock_breaks
- 1);
661 static void py_cli_state_dealloc(struct py_cli_state
*self
)
663 TALLOC_FREE(self
->thread_state
);
664 TALLOC_FREE(self
->oplock_waiter
);
665 TALLOC_FREE(self
->ev
);
667 if (self
->cli
!= NULL
) {
668 cli_shutdown(self
->cli
);
671 Py_TYPE(self
)->tp_free((PyObject
*)self
);
674 static PyObject
*py_cli_settimeout(struct py_cli_state
*self
, PyObject
*args
)
676 unsigned int nmsecs
= 0;
677 unsigned int omsecs
= 0;
679 if (!PyArg_ParseTuple(args
, "I", &nmsecs
)) {
683 omsecs
= cli_set_timeout(self
->cli
, nmsecs
);
685 return PyInt_FromLong(omsecs
);
688 static PyObject
*py_cli_create(struct py_cli_state
*self
, PyObject
*args
,
692 unsigned CreateFlags
= 0;
693 unsigned DesiredAccess
= FILE_GENERIC_READ
;
694 unsigned FileAttributes
= 0;
695 unsigned ShareAccess
= 0;
696 unsigned CreateDisposition
= FILE_OPEN
;
697 unsigned CreateOptions
= 0;
698 unsigned ImpersonationLevel
= SMB2_IMPERSONATION_IMPERSONATION
;
699 unsigned SecurityFlags
= 0;
701 struct tevent_req
*req
;
704 static const char *kwlist
[] = {
705 "Name", "CreateFlags", "DesiredAccess", "FileAttributes",
706 "ShareAccess", "CreateDisposition", "CreateOptions",
707 "ImpersonationLevel", "SecurityFlags", NULL
};
709 if (!ParseTupleAndKeywords(
710 args
, kwds
, "s|IIIIIIII", kwlist
,
711 &fname
, &CreateFlags
, &DesiredAccess
, &FileAttributes
,
712 &ShareAccess
, &CreateDisposition
, &CreateOptions
,
713 &ImpersonationLevel
, &SecurityFlags
)) {
717 req
= cli_ntcreate_send(NULL
, self
->ev
, self
->cli
, fname
, CreateFlags
,
718 DesiredAccess
, FileAttributes
, ShareAccess
,
719 CreateDisposition
, CreateOptions
,
720 ImpersonationLevel
, SecurityFlags
);
721 if (!py_tevent_req_wait_exc(self
, req
)) {
724 status
= cli_ntcreate_recv(req
, &fnum
, NULL
);
727 if (!NT_STATUS_IS_OK(status
)) {
728 PyErr_SetNTSTATUS(status
);
731 return Py_BuildValue("I", (unsigned)fnum
);
734 static PyObject
*py_cli_close(struct py_cli_state
*self
, PyObject
*args
)
736 struct tevent_req
*req
;
740 if (!PyArg_ParseTuple(args
, "i", &fnum
)) {
744 req
= cli_close_send(NULL
, self
->ev
, self
->cli
, fnum
);
745 if (!py_tevent_req_wait_exc(self
, req
)) {
748 status
= cli_close_recv(req
);
751 if (!NT_STATUS_IS_OK(status
)) {
752 PyErr_SetNTSTATUS(status
);
765 * cli_push() helper to write a chunk of data to a remote file
767 static size_t push_data(uint8_t *buf
, size_t n
, void *priv
)
769 struct push_state
*state
= (struct push_state
*)priv
;
770 char *curr_ptr
= NULL
;
774 if (state
->nread
>= state
->total_data
) {
778 curr_ptr
= state
->data
+ state
->nread
;
779 remaining
= state
->total_data
- state
->nread
;
780 copied_bytes
= MIN(remaining
, n
);
782 memcpy(buf
, curr_ptr
, copied_bytes
);
783 state
->nread
+= copied_bytes
;
788 * Writes a file with the contents specified
790 static PyObject
*py_smb_savefile(struct py_cli_state
*self
, PyObject
*args
)
793 const char *filename
= NULL
;
797 struct tevent_req
*req
= NULL
;
798 struct push_state state
;
800 if (!PyArg_ParseTuple(args
, "s"PYARG_BYTES_LEN
":savefile", &filename
,
805 /* create a new file handle for writing to */
806 req
= cli_ntcreate_send(NULL
, self
->ev
, self
->cli
, filename
, 0,
807 FILE_WRITE_DATA
, FILE_ATTRIBUTE_NORMAL
,
808 FILE_SHARE_READ
|FILE_SHARE_WRITE
,
809 FILE_OVERWRITE_IF
, FILE_NON_DIRECTORY_FILE
,
810 SMB2_IMPERSONATION_IMPERSONATION
, 0);
811 if (!py_tevent_req_wait_exc(self
, req
)) {
814 status
= cli_ntcreate_recv(req
, &fnum
, NULL
);
816 PyErr_NTSTATUS_IS_ERR_RAISE(status
);
818 /* write the new file contents */
821 state
.total_data
= size
;
823 req
= cli_push_send(NULL
, self
->ev
, self
->cli
, fnum
, 0, 0, 0,
825 if (!py_tevent_req_wait_exc(self
, req
)) {
828 status
= cli_push_recv(req
);
830 PyErr_NTSTATUS_IS_ERR_RAISE(status
);
832 /* close the file handle */
833 req
= cli_close_send(NULL
, self
->ev
, self
->cli
, fnum
);
834 if (!py_tevent_req_wait_exc(self
, req
)) {
837 status
= cli_close_recv(req
);
838 PyErr_NTSTATUS_IS_ERR_RAISE(status
);
843 static PyObject
*py_cli_write(struct py_cli_state
*self
, PyObject
*args
,
850 unsigned long long offset
;
851 struct tevent_req
*req
;
855 static const char *kwlist
[] = {
856 "fnum", "buffer", "offset", "mode", NULL
};
858 if (!ParseTupleAndKeywords(
859 args
, kwds
, "i" PYARG_BYTES_LEN
"K|I", kwlist
,
860 &fnum
, &buf
, &buflen
, &offset
, &mode
)) {
864 req
= cli_write_send(NULL
, self
->ev
, self
->cli
, fnum
, mode
,
865 (uint8_t *)buf
, offset
, buflen
);
866 if (!py_tevent_req_wait_exc(self
, req
)) {
869 status
= cli_write_recv(req
, &written
);
872 if (!NT_STATUS_IS_OK(status
)) {
873 PyErr_SetNTSTATUS(status
);
876 return Py_BuildValue("K", (unsigned long long)written
);
880 * Returns the size of the given file
882 static NTSTATUS
py_smb_filesize(struct py_cli_state
*self
, uint16_t fnum
,
888 uint8_t *rdata
= NULL
;
889 struct tevent_req
*req
= NULL
;
891 req
= cli_qfileinfo_send(NULL
, self
->ev
, self
->cli
, fnum
,
892 SMB_QUERY_FILE_ALL_INFO
, 68,
894 if (!py_tevent_req_wait_exc(self
, req
)) {
895 return NT_STATUS_INTERNAL_ERROR
;
897 status
= cli_qfileinfo_recv(req
, NULL
, NULL
, &rdata
, NULL
);
898 if (NT_STATUS_IS_OK(status
)) {
899 *size
= IVAL2_TO_SMB_BIG_UINT(rdata
, 48);
904 status
= cli_qfileinfo_basic(self
->cli
, fnum
, NULL
, size
,
905 NULL
, NULL
, NULL
, NULL
, NULL
);
911 * Loads the specified file's contents and returns it
913 static PyObject
*py_smb_loadfile(struct py_cli_state
*self
, PyObject
*args
)
916 const char *filename
= NULL
;
917 struct tevent_req
*req
= NULL
;
922 PyObject
*result
= NULL
;
924 if (!PyArg_ParseTuple(args
, "s:loadfile", &filename
)) {
928 /* get a read file handle */
929 req
= cli_ntcreate_send(NULL
, self
->ev
, self
->cli
, filename
, 0,
930 FILE_READ_DATA
| FILE_READ_ATTRIBUTES
,
931 FILE_ATTRIBUTE_NORMAL
,
932 FILE_SHARE_READ
, FILE_OPEN
, 0,
933 SMB2_IMPERSONATION_IMPERSONATION
, 0);
934 if (!py_tevent_req_wait_exc(self
, req
)) {
937 status
= cli_ntcreate_recv(req
, &fnum
, NULL
);
939 PyErr_NTSTATUS_IS_ERR_RAISE(status
);
941 /* get a buffer to hold the file contents */
942 status
= py_smb_filesize(self
, fnum
, &size
);
943 PyErr_NTSTATUS_IS_ERR_RAISE(status
);
945 result
= PyBytes_FromStringAndSize(NULL
, size
);
946 if (result
== NULL
) {
950 /* read the file contents */
951 buf
= PyBytes_AS_STRING(result
);
952 req
= cli_pull_send(NULL
, self
->ev
, self
->cli
, fnum
, 0, size
,
953 size
, cli_read_sink
, &buf
);
954 if (!py_tevent_req_wait_exc(self
, req
)) {
958 status
= cli_pull_recv(req
, &nread
);
960 if (!NT_STATUS_IS_OK(status
)) {
962 PyErr_SetNTSTATUS(status
);
966 /* close the file handle */
967 req
= cli_close_send(NULL
, self
->ev
, self
->cli
, fnum
);
968 if (!py_tevent_req_wait_exc(self
, req
)) {
972 status
= cli_close_recv(req
);
974 if (!NT_STATUS_IS_OK(status
)) {
976 PyErr_SetNTSTATUS(status
);
980 /* sanity-check we read the expected number of bytes */
983 PyErr_Format(PyExc_IOError
,
984 "read invalid - got %zu requested %zu",
990 if (_PyBytes_Resize(&result
, nread
) < 0) {
998 static PyObject
*py_cli_read(struct py_cli_state
*self
, PyObject
*args
,
1002 unsigned long long offset
;
1004 struct tevent_req
*req
;
1010 static const char *kwlist
[] = {
1011 "fnum", "offset", "size", NULL
};
1013 if (!ParseTupleAndKeywords(
1014 args
, kwds
, "iKI", kwlist
, &fnum
, &offset
,
1019 result
= PyBytes_FromStringAndSize(NULL
, size
);
1020 if (result
== NULL
) {
1023 buf
= PyBytes_AS_STRING(result
);
1025 req
= cli_read_send(NULL
, self
->ev
, self
->cli
, fnum
,
1027 if (!py_tevent_req_wait_exc(self
, req
)) {
1031 status
= cli_read_recv(req
, &received
);
1034 if (!NT_STATUS_IS_OK(status
)) {
1036 PyErr_SetNTSTATUS(status
);
1040 if (received
> size
) {
1042 PyErr_Format(PyExc_IOError
,
1043 "read invalid - got %zu requested %u",
1048 if (received
< size
) {
1049 if (_PyBytes_Resize(&result
, received
) < 0) {
1057 static PyObject
*py_cli_ftruncate(struct py_cli_state
*self
, PyObject
*args
,
1061 unsigned long long size
;
1062 struct tevent_req
*req
;
1065 static const char *kwlist
[] = {
1066 "fnum", "size", NULL
};
1068 if (!ParseTupleAndKeywords(
1069 args
, kwds
, "IK", kwlist
, &fnum
, &size
)) {
1073 req
= cli_ftruncate_send(NULL
, self
->ev
, self
->cli
, fnum
, size
);
1074 if (!py_tevent_req_wait_exc(self
, req
)) {
1077 status
= cli_ftruncate_recv(req
);
1080 if (!NT_STATUS_IS_OK(status
)) {
1081 PyErr_SetNTSTATUS(status
);
1087 static PyObject
*py_cli_delete_on_close(struct py_cli_state
*self
,
1091 unsigned fnum
, flag
;
1092 struct tevent_req
*req
;
1095 static const char *kwlist
[] = {
1096 "fnum", "flag", NULL
};
1098 if (!ParseTupleAndKeywords(
1099 args
, kwds
, "II", kwlist
, &fnum
, &flag
)) {
1103 req
= cli_nt_delete_on_close_send(NULL
, self
->ev
, self
->cli
, fnum
,
1105 if (!py_tevent_req_wait_exc(self
, req
)) {
1108 status
= cli_nt_delete_on_close_recv(req
);
1111 if (!NT_STATUS_IS_OK(status
)) {
1112 PyErr_SetNTSTATUS(status
);
1119 * Helper to add directory listing entries to an overall Python list
1121 static NTSTATUS
list_helper(const char *mntpoint
, struct file_info
*finfo
,
1122 const char *mask
, void *state
)
1124 PyObject
*result
= (PyObject
*)state
;
1125 PyObject
*file
= NULL
;
1126 PyObject
*size
= NULL
;
1129 /* suppress '.' and '..' in the results we return */
1130 if (ISDOT(finfo
->name
) || ISDOTDOT(finfo
->name
)) {
1131 return NT_STATUS_OK
;
1133 size
= PyLong_FromUnsignedLongLong(finfo
->size
);
1135 * Build a dictionary representing the file info.
1136 * Note: Windows does not always return short_name (so it may be None)
1138 file
= Py_BuildValue("{s:s,s:i,s:s,s:O,s:l}",
1139 "name", finfo
->name
,
1140 "attrib", (int)finfo
->mode
,
1141 "short_name", finfo
->short_name
,
1144 convert_timespec_to_time_t(finfo
->mtime_ts
));
1149 return NT_STATUS_NO_MEMORY
;
1152 ret
= PyList_Append(result
, file
);
1155 return NT_STATUS_INTERNAL_ERROR
;
1158 return NT_STATUS_OK
;
1161 static NTSTATUS
do_listing(struct py_cli_state
*self
,
1162 const char *base_dir
, const char *user_mask
,
1164 NTSTATUS (*callback_fn
)(const char *,
1166 const char *, void *),
1170 unsigned int info_level
= SMB_FIND_FILE_BOTH_DIRECTORY_INFO
;
1171 struct file_info
*finfos
= NULL
;
1173 size_t num_finfos
= 0;
1176 if (user_mask
== NULL
) {
1177 mask
= talloc_asprintf(NULL
, "%s\\*", base_dir
);
1179 mask
= talloc_asprintf(NULL
, "%s\\%s", base_dir
, user_mask
);
1183 return NT_STATUS_NO_MEMORY
;
1187 if (self
->is_smb1
) {
1188 struct tevent_req
*req
= NULL
;
1190 req
= cli_list_send(NULL
, self
->ev
, self
->cli
, mask
, attribute
,
1192 if (!py_tevent_req_wait_exc(self
, req
)) {
1193 return NT_STATUS_INTERNAL_ERROR
;
1195 status
= cli_list_recv(req
, NULL
, &finfos
, &num_finfos
);
1198 status
= cli_list(self
->cli
, mask
, attribute
, callback_fn
,
1203 if (!NT_STATUS_IS_OK(status
)) {
1207 /* invoke the callback for the async results (SMBv1 connections) */
1208 for (i
= 0; i
< num_finfos
; i
++) {
1209 status
= callback_fn(base_dir
, &finfos
[i
], user_mask
,
1211 if (!NT_STATUS_IS_OK(status
)) {
1212 TALLOC_FREE(finfos
);
1217 TALLOC_FREE(finfos
);
1221 static PyObject
*py_cli_list(struct py_cli_state
*self
,
1226 char *user_mask
= NULL
;
1227 unsigned int attribute
= LIST_ATTRIBUTE_MASK
;
1229 PyObject
*result
= NULL
;
1230 const char *kwlist
[] = { "directory", "mask", "attribs", NULL
};
1232 if (!ParseTupleAndKeywords(args
, kwds
, "z|sI:list", kwlist
,
1233 &base_dir
, &user_mask
, &attribute
)) {
1237 result
= Py_BuildValue("[]");
1238 if (result
== NULL
) {
1242 status
= do_listing(self
, base_dir
, user_mask
, attribute
,
1243 list_helper
, result
);
1245 if (!NT_STATUS_IS_OK(status
)) {
1247 PyErr_SetNTSTATUS(status
);
1257 static NTSTATUS
unlink_file(struct py_cli_state
*self
, const char *filename
)
1260 uint16_t attrs
= (FILE_ATTRIBUTE_SYSTEM
| FILE_ATTRIBUTE_HIDDEN
);
1262 if (self
->is_smb1
) {
1263 struct tevent_req
*req
= NULL
;
1265 req
= cli_unlink_send(NULL
, self
->ev
, self
->cli
, filename
,
1267 if (!py_tevent_req_wait_exc(self
, req
)) {
1268 return NT_STATUS_INTERNAL_ERROR
;
1270 status
= cli_unlink_recv(req
);
1273 status
= cli_unlink(self
->cli
, filename
, attrs
);
1279 static PyObject
*py_smb_unlink(struct py_cli_state
*self
, PyObject
*args
)
1282 const char *filename
= NULL
;
1284 if (!PyArg_ParseTuple(args
, "s:unlink", &filename
)) {
1288 status
= unlink_file(self
, filename
);
1289 PyErr_NTSTATUS_NOT_OK_RAISE(status
);
1295 * Delete an empty directory
1297 static NTSTATUS
remove_dir(struct py_cli_state
*self
, const char *dirname
)
1301 if (self
->is_smb1
) {
1302 struct tevent_req
*req
= NULL
;
1304 req
= cli_rmdir_send(NULL
, self
->ev
, self
->cli
, dirname
);
1305 if (!py_tevent_req_wait_exc(self
, req
)) {
1306 return NT_STATUS_INTERNAL_ERROR
;
1308 status
= cli_rmdir_recv(req
);
1311 status
= cli_rmdir(self
->cli
, dirname
);
1316 static PyObject
*py_smb_rmdir(struct py_cli_state
*self
, PyObject
*args
)
1319 const char *dirname
= NULL
;
1321 if (!PyArg_ParseTuple(args
, "s:rmdir", &dirname
)) {
1325 status
= remove_dir(self
, dirname
);
1326 PyErr_NTSTATUS_IS_ERR_RAISE(status
);
1332 * Create a directory
1334 static PyObject
*py_smb_mkdir(struct py_cli_state
*self
, PyObject
*args
)
1337 const char *dirname
= NULL
;
1339 if (!PyArg_ParseTuple(args
, "s:mkdir", &dirname
)) {
1343 if (self
->is_smb1
) {
1344 struct tevent_req
*req
= NULL
;
1346 req
= cli_mkdir_send(NULL
, self
->ev
, self
->cli
, dirname
);
1347 if (!py_tevent_req_wait_exc(self
, req
)) {
1350 status
= cli_mkdir_recv(req
);
1353 status
= cli_mkdir(self
->cli
, dirname
);
1355 PyErr_NTSTATUS_IS_ERR_RAISE(status
);
1361 * Checks existence of a directory
1363 static bool check_dir_path(struct py_cli_state
*self
, const char *path
)
1367 if (self
->is_smb1
) {
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 status
= cli_chkpath(self
->cli
, path
);
1380 return NT_STATUS_IS_OK(status
);
1383 static PyObject
*py_smb_chkpath(struct py_cli_state
*self
, PyObject
*args
)
1385 const char *path
= NULL
;
1388 if (!PyArg_ParseTuple(args
, "s:chkpath", &path
)) {
1392 dir_exists
= check_dir_path(self
, path
);
1393 return PyBool_FromLong(dir_exists
);
1396 struct deltree_state
{
1397 struct py_cli_state
*self
;
1398 const char *full_dirpath
;
1401 static NTSTATUS
delete_dir_tree(struct py_cli_state
*self
,
1402 const char *dirpath
);
1405 * Deletes a single item in the directory tree. This could be either a file
1406 * or a directory. This function gets invoked as a callback for every item in
1407 * the given directory's listings.
1409 static NTSTATUS
delete_tree_callback(const char *mntpoint
,
1410 struct file_info
*finfo
,
1411 const char *mask
, void *priv
)
1413 char *filepath
= NULL
;
1414 struct deltree_state
*state
= priv
;
1417 /* skip '.' or '..' directory listings */
1418 if (ISDOT(finfo
->name
) || ISDOTDOT(finfo
->name
)) {
1419 return NT_STATUS_OK
;
1422 /* get the absolute filepath */
1423 filepath
= talloc_asprintf(NULL
, "%s\\%s", state
->full_dirpath
,
1425 if (filepath
== NULL
) {
1426 return NT_STATUS_NO_MEMORY
;
1429 if (finfo
->mode
& FILE_ATTRIBUTE_DIRECTORY
) {
1431 /* recursively delete the sub-directory and its contents */
1432 status
= delete_dir_tree(state
->self
, filepath
);
1434 status
= unlink_file(state
->self
, filepath
);
1437 TALLOC_FREE(filepath
);
1442 * Removes a directory and all its contents
1444 static NTSTATUS
delete_dir_tree(struct py_cli_state
*self
,
1445 const char *filepath
)
1448 const char *mask
= "*";
1449 struct deltree_state state
= { 0 };
1451 /* go through the directory's contents, deleting each item */
1453 state
.full_dirpath
= filepath
;
1454 status
= do_listing(self
, filepath
, mask
, LIST_ATTRIBUTE_MASK
,
1455 delete_tree_callback
, &state
);
1457 /* remove the directory itself */
1458 if (NT_STATUS_IS_OK(status
)) {
1459 status
= remove_dir(self
, filepath
);
1464 static PyObject
*py_smb_deltree(struct py_cli_state
*self
, PyObject
*args
)
1467 const char *filepath
= NULL
;
1470 if (!PyArg_ParseTuple(args
, "s:deltree", &filepath
)) {
1474 /* check whether we're removing a directory or a file */
1475 dir_exists
= check_dir_path(self
, filepath
);
1478 status
= delete_dir_tree(self
, filepath
);
1480 status
= unlink_file(self
, filepath
);
1483 PyErr_NTSTATUS_IS_ERR_RAISE(status
);
1489 * Read ACL on a given file/directory as a security descriptor object
1491 static PyObject
*py_smb_getacl(struct py_cli_state
*self
, PyObject
*args
)
1494 const char *filename
= NULL
;
1495 unsigned int sinfo
= SECINFO_DEFAULT_FLAGS
;
1496 unsigned int access_mask
= SEC_FLAG_MAXIMUM_ALLOWED
;
1498 struct security_descriptor
*sd
= NULL
;
1500 /* there's no async version of cli_query_security_descriptor() */
1501 if (self
->thread_state
!= NULL
) {
1502 PyErr_SetString(PyExc_RuntimeError
,
1503 "get_acl() is not supported on "
1504 "a multi_threaded connection");
1508 if (!PyArg_ParseTuple(args
, "s|II:get_acl", &filename
, &sinfo
,
1513 /* get a file handle with the desired access */
1514 status
= cli_ntcreate(self
->cli
, filename
, 0, access_mask
, 0,
1515 FILE_SHARE_READ
|FILE_SHARE_WRITE
,
1516 FILE_OPEN
, 0x0, 0x0, &fnum
, NULL
);
1517 PyErr_NTSTATUS_IS_ERR_RAISE(status
);
1519 /* query the security descriptor for this file */
1520 status
= cli_query_security_descriptor(self
->cli
, fnum
, sinfo
,
1522 PyErr_NTSTATUS_IS_ERR_RAISE(status
);
1524 /* close the file handle and convert the SD to a python struct */
1525 status
= cli_close(self
->cli
, fnum
);
1526 PyErr_NTSTATUS_IS_ERR_RAISE(status
);
1528 return py_return_ndr_struct("samba.dcerpc.security", "descriptor",
1533 * Set ACL on file/directory using given security descriptor object
1535 static PyObject
*py_smb_setacl(struct py_cli_state
*self
, PyObject
*args
)
1538 char *filename
= NULL
;
1539 PyObject
*py_sd
= NULL
;
1540 struct security_descriptor
*sd
= NULL
;
1541 unsigned int sinfo
= SECINFO_DEFAULT_FLAGS
;
1544 /* there's no async version of cli_set_security_descriptor() */
1545 if (self
->thread_state
!= NULL
) {
1546 PyErr_SetString(PyExc_RuntimeError
,
1547 "set_acl() is not supported on "
1548 "a multi_threaded connection");
1552 if (!PyArg_ParseTuple(args
, "sO|I:set_acl", &filename
, &py_sd
,
1557 sd
= pytalloc_get_type(py_sd
, struct security_descriptor
);
1559 PyErr_Format(PyExc_TypeError
,
1560 "Expected dcerpc.security.descriptor as argument, got %s",
1561 talloc_get_name(pytalloc_get_ptr(py_sd
)));
1565 status
= cli_ntcreate(self
->cli
, filename
, 0,
1566 SEC_FLAG_MAXIMUM_ALLOWED
, 0,
1567 FILE_SHARE_READ
|FILE_SHARE_WRITE
,
1568 FILE_OPEN
, 0x0, 0x0, &fnum
, NULL
);
1569 PyErr_NTSTATUS_IS_ERR_RAISE(status
);
1571 status
= cli_set_security_descriptor(self
->cli
, fnum
, sinfo
, sd
);
1572 PyErr_NTSTATUS_IS_ERR_RAISE(status
);
1574 status
= cli_close(self
->cli
, fnum
);
1575 PyErr_NTSTATUS_IS_ERR_RAISE(status
);
1580 static PyMethodDef py_cli_state_methods
[] = {
1581 { "settimeout", (PyCFunction
)py_cli_settimeout
, METH_VARARGS
,
1582 "settimeout(new_timeout_msecs) => return old_timeout_msecs" },
1583 { "create", PY_DISCARD_FUNC_SIG(PyCFunction
, py_cli_create
),
1584 METH_VARARGS
|METH_KEYWORDS
,
1586 { "close", (PyCFunction
)py_cli_close
, METH_VARARGS
,
1587 "Close a file handle" },
1588 { "write", PY_DISCARD_FUNC_SIG(PyCFunction
, py_cli_write
),
1589 METH_VARARGS
|METH_KEYWORDS
,
1590 "Write to a file handle" },
1591 { "read", PY_DISCARD_FUNC_SIG(PyCFunction
, py_cli_read
),
1592 METH_VARARGS
|METH_KEYWORDS
,
1593 "Read from a file handle" },
1594 { "truncate", PY_DISCARD_FUNC_SIG(PyCFunction
,
1596 METH_VARARGS
|METH_KEYWORDS
,
1597 "Truncate a file" },
1598 { "delete_on_close", PY_DISCARD_FUNC_SIG(PyCFunction
,
1599 py_cli_delete_on_close
),
1600 METH_VARARGS
|METH_KEYWORDS
,
1601 "Set/Reset the delete on close flag" },
1602 { "list", PY_DISCARD_FUNC_SIG(PyCFunction
, py_cli_list
),
1603 METH_VARARGS
|METH_KEYWORDS
,
1604 "list(directory, mask='*', attribs=DEFAULT_ATTRS) -> "
1605 "directory contents as a dictionary\n"
1606 "\t\tDEFAULT_ATTRS: FILE_ATTRIBUTE_SYSTEM | "
1607 "FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_ARCHIVE\n\n"
1608 "\t\tList contents of a directory. The keys are, \n"
1609 "\t\t\tname: Long name of the directory item\n"
1610 "\t\t\tshort_name: Short name of the directory item\n"
1611 "\t\t\tsize: File size in bytes\n"
1612 "\t\t\tattrib: Attributes\n"
1613 "\t\t\tmtime: Modification time\n" },
1614 { "get_oplock_break", (PyCFunction
)py_cli_get_oplock_break
,
1615 METH_VARARGS
, "Wait for an oplock break" },
1616 { "unlink", (PyCFunction
)py_smb_unlink
,
1618 "unlink(path) -> None\n\n \t\tDelete a file." },
1619 { "mkdir", (PyCFunction
)py_smb_mkdir
, METH_VARARGS
,
1620 "mkdir(path) -> None\n\n \t\tCreate a directory." },
1621 { "rmdir", (PyCFunction
)py_smb_rmdir
, METH_VARARGS
,
1622 "rmdir(path) -> None\n\n \t\tDelete a directory." },
1623 { "chkpath", (PyCFunction
)py_smb_chkpath
, METH_VARARGS
,
1624 "chkpath(dir_path) -> True or False\n\n"
1625 "\t\tReturn true if directory exists, false otherwise." },
1626 { "savefile", (PyCFunction
)py_smb_savefile
, METH_VARARGS
,
1627 "savefile(path, str) -> None\n\n"
1628 "\t\tWrite " PY_DESC_PY3_BYTES
" str to file." },
1629 { "loadfile", (PyCFunction
)py_smb_loadfile
, METH_VARARGS
,
1630 "loadfile(path) -> file contents as a " PY_DESC_PY3_BYTES
1631 "\n\n\t\tRead contents of a file." },
1632 { "deltree", (PyCFunction
)py_smb_deltree
, METH_VARARGS
,
1633 "deltree(path) -> None\n\n"
1634 "\t\tDelete a directory and all its contents." },
1635 { "get_acl", (PyCFunction
)py_smb_getacl
, METH_VARARGS
,
1636 "get_acl(path[, security_info=0]) -> security_descriptor object\n\n"
1637 "\t\tGet security descriptor for file." },
1638 { "set_acl", (PyCFunction
)py_smb_setacl
, METH_VARARGS
,
1639 "set_acl(path, security_descriptor[, security_info=0]) -> None\n\n"
1640 "\t\tSet security descriptor for file." },
1641 { NULL
, NULL
, 0, NULL
}
1644 static PyTypeObject py_cli_state_type
= {
1645 PyVarObject_HEAD_INIT(NULL
, 0)
1646 .tp_name
= "libsmb_samba_internal.Conn",
1647 .tp_basicsize
= sizeof(struct py_cli_state
),
1648 .tp_flags
= Py_TPFLAGS_DEFAULT
| Py_TPFLAGS_BASETYPE
,
1649 .tp_doc
= "libsmb connection",
1650 .tp_new
= py_cli_state_new
,
1651 .tp_init
= (initproc
)py_cli_state_init
,
1652 .tp_dealloc
= (destructor
)py_cli_state_dealloc
,
1653 .tp_methods
= py_cli_state_methods
,
1656 static PyMethodDef py_libsmb_methods
[] = {
1660 void initlibsmb_samba_internal(void);
1662 static struct PyModuleDef moduledef
= {
1663 PyModuleDef_HEAD_INIT
,
1664 .m_name
= "libsmb_samba_internal",
1665 .m_doc
= "libsmb wrapper",
1667 .m_methods
= py_libsmb_methods
,
1670 MODULE_INIT_FUNC(libsmb_samba_internal
)
1674 talloc_stackframe();
1676 m
= PyModule_Create(&moduledef
);
1680 if (PyType_Ready(&py_cli_state_type
) < 0) {
1683 Py_INCREF(&py_cli_state_type
);
1684 PyModule_AddObject(m
, "Conn", (PyObject
*)&py_cli_state_type
);
1686 #define ADD_FLAGS(val) PyModule_AddObject(m, #val, PyInt_FromLong(val))
1688 ADD_FLAGS(FILE_ATTRIBUTE_READONLY
);
1689 ADD_FLAGS(FILE_ATTRIBUTE_HIDDEN
);
1690 ADD_FLAGS(FILE_ATTRIBUTE_SYSTEM
);
1691 ADD_FLAGS(FILE_ATTRIBUTE_VOLUME
);
1692 ADD_FLAGS(FILE_ATTRIBUTE_DIRECTORY
);
1693 ADD_FLAGS(FILE_ATTRIBUTE_ARCHIVE
);
1694 ADD_FLAGS(FILE_ATTRIBUTE_DEVICE
);
1695 ADD_FLAGS(FILE_ATTRIBUTE_NORMAL
);
1696 ADD_FLAGS(FILE_ATTRIBUTE_TEMPORARY
);
1697 ADD_FLAGS(FILE_ATTRIBUTE_SPARSE
);
1698 ADD_FLAGS(FILE_ATTRIBUTE_REPARSE_POINT
);
1699 ADD_FLAGS(FILE_ATTRIBUTE_COMPRESSED
);
1700 ADD_FLAGS(FILE_ATTRIBUTE_OFFLINE
);
1701 ADD_FLAGS(FILE_ATTRIBUTE_NONINDEXED
);
1702 ADD_FLAGS(FILE_ATTRIBUTE_ENCRYPTED
);
1703 ADD_FLAGS(FILE_ATTRIBUTE_ALL_MASK
);