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 "libcli/smb/smbXcli_base.h"
28 #include "libsmb/libsmb.h"
29 #include "libcli/security/security.h"
30 #include "system/select.h"
31 #include "source4/libcli/util/pyerrors.h"
32 #include "auth/credentials/pycredentials.h"
34 #include "libsmb/clirap.h"
35 #include "librpc/rpc/pyrpc_util.h"
37 #define LIST_ATTRIBUTE_MASK \
38 (FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_HIDDEN)
40 #define SECINFO_DEFAULT_FLAGS \
41 (SECINFO_OWNER | SECINFO_GROUP | \
42 SECINFO_DACL | SECINFO_PROTECTED_DACL | SECINFO_UNPROTECTED_DACL | \
43 SECINFO_SACL | SECINFO_PROTECTED_SACL | SECINFO_UNPROTECTED_SACL)
45 static PyTypeObject
*get_pytype(const char *module
, const char *type
)
50 mod
= PyImport_ImportModule(module
);
52 PyErr_Format(PyExc_RuntimeError
,
53 "Unable to import %s to check type %s",
57 result
= (PyTypeObject
*)PyObject_GetAttrString(mod
, type
);
60 PyErr_Format(PyExc_RuntimeError
,
61 "Unable to find type %s in module %s",
69 * We're using "const char * const *" for keywords,
70 * PyArg_ParseTupleAndKeywords expects a "char **". Confine the
71 * inevitable warnings to just one place.
73 static int ParseTupleAndKeywords(PyObject
*args
, PyObject
*kw
,
74 const char *format
, const char * const *keywords
,
77 char **_keywords
= discard_const_p(char *, keywords
);
80 va_start(a
, keywords
);
81 ret
= PyArg_VaParseTupleAndKeywords(args
, kw
, format
,
89 struct py_cli_oplock_break
{
96 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);
421 self
->is_smb1
= false;
423 self
->thread_state
= NULL
;
424 self
->oplock_waiter
= NULL
;
425 self
->oplock_cond
= NULL
;
426 self
->oplock_breaks
= NULL
;
427 return (PyObject
*)self
;
430 static void py_cli_got_oplock_break(struct tevent_req
*req
);
432 static int py_cli_state_init(struct py_cli_state
*self
, PyObject
*args
,
437 PyObject
*creds
= NULL
;
438 struct cli_credentials
*cli_creds
;
439 PyObject
*py_lp
= Py_None
;
440 PyObject
*py_multi_threaded
= Py_False
;
441 bool multi_threaded
= false;
442 PyObject
*py_sign
= Py_False
;
444 int signing_state
= SMB_SIGNING_DEFAULT
;
445 PyObject
*py_force_smb1
= Py_False
;
446 bool force_smb1
= false;
447 struct tevent_req
*req
;
451 static const char *kwlist
[] = {
452 "host", "share", "lp", "creds",
453 "multi_threaded", "sign", "force_smb1",
457 PyTypeObject
*py_type_Credentials
= get_pytype(
458 "samba.credentials", "Credentials");
459 if (py_type_Credentials
== NULL
) {
463 ret
= ParseTupleAndKeywords(
464 args
, kwds
, "ssO|O!OOO", kwlist
,
465 &host
, &share
, &py_lp
,
466 py_type_Credentials
, &creds
,
471 Py_DECREF(py_type_Credentials
);
477 multi_threaded
= PyObject_IsTrue(py_multi_threaded
);
478 sign
= PyObject_IsTrue(py_sign
);
479 force_smb1
= PyObject_IsTrue(py_force_smb1
);
482 signing_state
= SMB_SIGNING_REQUIRED
;
487 * As most of the cli_*_send() function
488 * don't support SMB2 (it's only plugged
489 * into the sync wrapper functions currently)
490 * we have a way to force SMB1.
492 flags
= CLI_FULL_CONNECTION_FORCE_SMB1
;
495 if (multi_threaded
) {
497 ret
= py_cli_state_setup_mt_ev(self
);
502 PyErr_SetString(PyExc_RuntimeError
,
503 "No PTHREAD support available");
507 PyErr_SetString(PyExc_RuntimeError
,
508 "multi_threaded is only possible on "
513 ret
= py_cli_state_setup_ev(self
);
520 cli_creds
= cli_credentials_init_anon(NULL
);
522 cli_creds
= PyCredentials_AsCliCredentials(creds
);
525 req
= cli_full_connection_creds_send(
526 NULL
, self
->ev
, "myname", host
, NULL
, 0, share
, "?????",
527 cli_creds
, flags
, signing_state
);
528 if (!py_tevent_req_wait_exc(self
, req
)) {
531 status
= cli_full_connection_creds_recv(req
, &self
->cli
);
534 if (!NT_STATUS_IS_OK(status
)) {
535 PyErr_SetNTSTATUS(status
);
539 if (smbXcli_conn_protocol(self
->cli
->conn
) < PROTOCOL_SMB2_02
) {
540 self
->is_smb1
= true;
544 * Oplocks require a multi threaded connection
546 if (self
->thread_state
== NULL
) {
550 self
->oplock_waiter
= cli_smb_oplock_break_waiter_send(
551 self
->ev
, self
->ev
, self
->cli
);
552 if (self
->oplock_waiter
== NULL
) {
556 tevent_req_set_callback(self
->oplock_waiter
, py_cli_got_oplock_break
,
561 static void py_cli_got_oplock_break(struct tevent_req
*req
)
563 struct py_cli_state
*self
= (struct py_cli_state
*)
564 tevent_req_callback_data_void(req
);
565 struct py_cli_oplock_break b
;
566 struct py_cli_oplock_break
*tmp
;
570 status
= cli_smb_oplock_break_waiter_recv(req
, &b
.fnum
, &b
.level
);
572 self
->oplock_waiter
= NULL
;
574 if (!NT_STATUS_IS_OK(status
)) {
578 num_breaks
= talloc_array_length(self
->oplock_breaks
);
579 tmp
= talloc_realloc(self
->ev
, self
->oplock_breaks
,
580 struct py_cli_oplock_break
, num_breaks
+1);
584 self
->oplock_breaks
= tmp
;
585 self
->oplock_breaks
[num_breaks
] = b
;
587 if (self
->oplock_cond
!= NULL
) {
588 py_tevent_cond_signal(self
->oplock_cond
);
591 self
->oplock_waiter
= cli_smb_oplock_break_waiter_send(
592 self
->ev
, self
->ev
, self
->cli
);
593 if (self
->oplock_waiter
== NULL
) {
596 tevent_req_set_callback(self
->oplock_waiter
, py_cli_got_oplock_break
,
600 static PyObject
*py_cli_get_oplock_break(struct py_cli_state
*self
,
603 size_t num_oplock_breaks
;
605 if (!PyArg_ParseTuple(args
, "")) {
609 if (self
->thread_state
== NULL
) {
610 PyErr_SetString(PyExc_RuntimeError
,
611 "get_oplock_break() only possible on "
612 "a multi_threaded connection");
616 if (self
->oplock_cond
!= NULL
) {
618 PyErr_SetFromErrno(PyExc_RuntimeError
);
622 num_oplock_breaks
= talloc_array_length(self
->oplock_breaks
);
624 if (num_oplock_breaks
== 0) {
625 struct py_tevent_cond cond
;
628 self
->oplock_cond
= &cond
;
629 ret
= py_tevent_cond_wait(&cond
);
630 self
->oplock_cond
= NULL
;
634 PyErr_SetFromErrno(PyExc_RuntimeError
);
639 num_oplock_breaks
= talloc_array_length(self
->oplock_breaks
);
640 if (num_oplock_breaks
> 0) {
643 result
= Py_BuildValue(
645 "fnum", self
->oplock_breaks
[0].fnum
,
646 "level", self
->oplock_breaks
[0].level
);
648 memmove(&self
->oplock_breaks
[0], &self
->oplock_breaks
[1],
649 sizeof(self
->oplock_breaks
[0]) *
650 (num_oplock_breaks
- 1));
651 self
->oplock_breaks
= talloc_realloc(
652 NULL
, self
->oplock_breaks
, struct py_cli_oplock_break
,
653 num_oplock_breaks
- 1);
660 static void py_cli_state_dealloc(struct py_cli_state
*self
)
662 TALLOC_FREE(self
->thread_state
);
663 TALLOC_FREE(self
->oplock_waiter
);
664 TALLOC_FREE(self
->ev
);
666 if (self
->cli
!= NULL
) {
667 cli_shutdown(self
->cli
);
670 Py_TYPE(self
)->tp_free((PyObject
*)self
);
673 static PyObject
*py_cli_settimeout(struct py_cli_state
*self
, PyObject
*args
)
675 unsigned int nmsecs
= 0;
676 unsigned int omsecs
= 0;
678 if (!PyArg_ParseTuple(args
, "I", &nmsecs
)) {
682 omsecs
= cli_set_timeout(self
->cli
, nmsecs
);
684 return PyInt_FromLong(omsecs
);
687 static PyObject
*py_cli_create(struct py_cli_state
*self
, PyObject
*args
,
691 unsigned CreateFlags
= 0;
692 unsigned DesiredAccess
= FILE_GENERIC_READ
;
693 unsigned FileAttributes
= 0;
694 unsigned ShareAccess
= 0;
695 unsigned CreateDisposition
= FILE_OPEN
;
696 unsigned CreateOptions
= 0;
697 unsigned ImpersonationLevel
= SMB2_IMPERSONATION_IMPERSONATION
;
698 unsigned SecurityFlags
= 0;
700 struct tevent_req
*req
;
703 static const char *kwlist
[] = {
704 "Name", "CreateFlags", "DesiredAccess", "FileAttributes",
705 "ShareAccess", "CreateDisposition", "CreateOptions",
706 "ImpersonationLevel", "SecurityFlags", NULL
};
708 if (!ParseTupleAndKeywords(
709 args
, kwds
, "s|IIIIIIII", kwlist
,
710 &fname
, &CreateFlags
, &DesiredAccess
, &FileAttributes
,
711 &ShareAccess
, &CreateDisposition
, &CreateOptions
,
712 &ImpersonationLevel
, &SecurityFlags
)) {
716 req
= cli_ntcreate_send(NULL
, self
->ev
, self
->cli
, fname
, CreateFlags
,
717 DesiredAccess
, FileAttributes
, ShareAccess
,
718 CreateDisposition
, CreateOptions
,
719 ImpersonationLevel
, SecurityFlags
);
720 if (!py_tevent_req_wait_exc(self
, req
)) {
723 status
= cli_ntcreate_recv(req
, &fnum
, NULL
);
726 if (!NT_STATUS_IS_OK(status
)) {
727 PyErr_SetNTSTATUS(status
);
730 return Py_BuildValue("I", (unsigned)fnum
);
733 static PyObject
*py_cli_close(struct py_cli_state
*self
, PyObject
*args
)
735 struct tevent_req
*req
;
739 if (!PyArg_ParseTuple(args
, "i", &fnum
)) {
743 req
= cli_close_send(NULL
, self
->ev
, self
->cli
, fnum
);
744 if (!py_tevent_req_wait_exc(self
, req
)) {
747 status
= cli_close_recv(req
);
750 if (!NT_STATUS_IS_OK(status
)) {
751 PyErr_SetNTSTATUS(status
);
764 * cli_push() helper to write a chunk of data to a remote file
766 static size_t push_data(uint8_t *buf
, size_t n
, void *priv
)
768 struct push_state
*state
= (struct push_state
*)priv
;
769 char *curr_ptr
= NULL
;
773 if (state
->nread
>= state
->total_data
) {
777 curr_ptr
= state
->data
+ state
->nread
;
778 remaining
= state
->total_data
- state
->nread
;
779 copied_bytes
= MIN(remaining
, n
);
781 memcpy(buf
, curr_ptr
, copied_bytes
);
782 state
->nread
+= copied_bytes
;
787 * Writes a file with the contents specified
789 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
,
917 const char *filename
= NULL
;
918 struct tevent_req
*req
= NULL
;
923 PyObject
*result
= NULL
;
925 if (!PyArg_ParseTuple(args
, "s:loadfile", &filename
)) {
929 /* get a read file handle */
930 req
= cli_ntcreate_send(NULL
, self
->ev
, self
->cli
, filename
, 0,
931 FILE_READ_DATA
| FILE_READ_ATTRIBUTES
,
932 FILE_ATTRIBUTE_NORMAL
,
933 FILE_SHARE_READ
, FILE_OPEN
, 0,
934 SMB2_IMPERSONATION_IMPERSONATION
, 0);
935 if (!py_tevent_req_wait_exc(self
, req
)) {
938 status
= cli_ntcreate_recv(req
, &fnum
, NULL
);
940 PyErr_NTSTATUS_IS_ERR_RAISE(status
);
942 /* get a buffer to hold the file contents */
943 status
= py_smb_filesize(self
, fnum
, &size
);
944 PyErr_NTSTATUS_IS_ERR_RAISE(status
);
946 result
= PyBytes_FromStringAndSize(NULL
, size
);
947 if (result
== NULL
) {
951 /* read the file contents */
952 buf
= PyBytes_AS_STRING(result
);
953 req
= cli_pull_send(NULL
, self
->ev
, self
->cli
, fnum
, 0, size
,
954 size
, cli_read_sink
, &buf
);
955 if (!py_tevent_req_wait_exc(self
, req
)) {
959 status
= cli_pull_recv(req
, &nread
);
961 if (!NT_STATUS_IS_OK(status
)) {
963 PyErr_SetNTSTATUS(status
);
967 /* close the file handle */
968 req
= cli_close_send(NULL
, self
->ev
, self
->cli
, fnum
);
969 if (!py_tevent_req_wait_exc(self
, req
)) {
973 status
= cli_close_recv(req
);
975 if (!NT_STATUS_IS_OK(status
)) {
977 PyErr_SetNTSTATUS(status
);
981 /* sanity-check we read the expected number of bytes */
984 PyErr_Format(PyExc_IOError
,
985 "read invalid - got %zu requested %zu",
991 if (_PyBytes_Resize(&result
, nread
) < 0) {
999 static PyObject
*py_cli_read(struct py_cli_state
*self
, PyObject
*args
,
1003 unsigned long long offset
;
1005 struct tevent_req
*req
;
1011 static const char *kwlist
[] = {
1012 "fnum", "offset", "size", NULL
};
1014 if (!ParseTupleAndKeywords(
1015 args
, kwds
, "iKI", kwlist
, &fnum
, &offset
,
1020 result
= PyBytes_FromStringAndSize(NULL
, size
);
1021 if (result
== NULL
) {
1024 buf
= PyBytes_AS_STRING(result
);
1026 req
= cli_read_send(NULL
, self
->ev
, self
->cli
, fnum
,
1028 if (!py_tevent_req_wait_exc(self
, req
)) {
1032 status
= cli_read_recv(req
, &received
);
1035 if (!NT_STATUS_IS_OK(status
)) {
1037 PyErr_SetNTSTATUS(status
);
1041 if (received
> size
) {
1043 PyErr_Format(PyExc_IOError
,
1044 "read invalid - got %zu requested %u",
1049 if (received
< size
) {
1050 if (_PyBytes_Resize(&result
, received
) < 0) {
1058 static PyObject
*py_cli_ftruncate(struct py_cli_state
*self
, PyObject
*args
,
1062 unsigned long long size
;
1063 struct tevent_req
*req
;
1066 static const char *kwlist
[] = {
1067 "fnum", "size", NULL
};
1069 if (!ParseTupleAndKeywords(
1070 args
, kwds
, "IK", kwlist
, &fnum
, &size
)) {
1074 req
= cli_ftruncate_send(NULL
, self
->ev
, self
->cli
, fnum
, size
);
1075 if (!py_tevent_req_wait_exc(self
, req
)) {
1078 status
= cli_ftruncate_recv(req
);
1081 if (!NT_STATUS_IS_OK(status
)) {
1082 PyErr_SetNTSTATUS(status
);
1088 static PyObject
*py_cli_delete_on_close(struct py_cli_state
*self
,
1092 unsigned fnum
, flag
;
1093 struct tevent_req
*req
;
1096 static const char *kwlist
[] = {
1097 "fnum", "flag", NULL
};
1099 if (!ParseTupleAndKeywords(
1100 args
, kwds
, "II", kwlist
, &fnum
, &flag
)) {
1104 req
= cli_nt_delete_on_close_send(NULL
, self
->ev
, self
->cli
, fnum
,
1106 if (!py_tevent_req_wait_exc(self
, req
)) {
1109 status
= cli_nt_delete_on_close_recv(req
);
1112 if (!NT_STATUS_IS_OK(status
)) {
1113 PyErr_SetNTSTATUS(status
);
1120 * Helper to add directory listing entries to an overall Python list
1122 static NTSTATUS
list_helper(const char *mntpoint
, struct file_info
*finfo
,
1123 const char *mask
, void *state
)
1125 PyObject
*result
= (PyObject
*)state
;
1126 PyObject
*file
= NULL
;
1127 PyObject
*size
= NULL
;
1130 /* suppress '.' and '..' in the results we return */
1131 if (ISDOT(finfo
->name
) || ISDOTDOT(finfo
->name
)) {
1132 return NT_STATUS_OK
;
1134 size
= PyLong_FromUnsignedLongLong(finfo
->size
);
1136 * Build a dictionary representing the file info.
1137 * Note: Windows does not always return short_name (so it may be None)
1139 file
= Py_BuildValue("{s:s,s:i,s:s,s:O,s:l}",
1140 "name", finfo
->name
,
1141 "attrib", (int)finfo
->mode
,
1142 "short_name", finfo
->short_name
,
1145 convert_timespec_to_time_t(finfo
->mtime_ts
));
1150 return NT_STATUS_NO_MEMORY
;
1153 ret
= PyList_Append(result
, file
);
1156 return NT_STATUS_INTERNAL_ERROR
;
1159 return NT_STATUS_OK
;
1162 static NTSTATUS
do_listing(struct py_cli_state
*self
,
1163 const char *base_dir
, const char *user_mask
,
1165 NTSTATUS (*callback_fn
)(const char *,
1167 const char *, void *),
1171 unsigned int info_level
= SMB_FIND_FILE_BOTH_DIRECTORY_INFO
;
1172 struct file_info
*finfos
= NULL
;
1174 size_t num_finfos
= 0;
1177 if (user_mask
== NULL
) {
1178 mask
= talloc_asprintf(NULL
, "%s\\*", base_dir
);
1180 mask
= talloc_asprintf(NULL
, "%s\\%s", base_dir
, user_mask
);
1184 return NT_STATUS_NO_MEMORY
;
1188 if (self
->is_smb1
) {
1189 struct tevent_req
*req
= NULL
;
1191 req
= cli_list_send(NULL
, self
->ev
, self
->cli
, mask
, attribute
,
1193 if (!py_tevent_req_wait_exc(self
, req
)) {
1194 return NT_STATUS_INTERNAL_ERROR
;
1196 status
= cli_list_recv(req
, NULL
, &finfos
, &num_finfos
);
1199 status
= cli_list(self
->cli
, mask
, attribute
, callback_fn
,
1204 if (!NT_STATUS_IS_OK(status
)) {
1208 /* invoke the callback for the async results (SMBv1 connections) */
1209 for (i
= 0; i
< num_finfos
; i
++) {
1210 status
= callback_fn(base_dir
, &finfos
[i
], user_mask
,
1212 if (!NT_STATUS_IS_OK(status
)) {
1213 TALLOC_FREE(finfos
);
1218 TALLOC_FREE(finfos
);
1222 static PyObject
*py_cli_list(struct py_cli_state
*self
,
1227 char *user_mask
= NULL
;
1228 unsigned int attribute
= LIST_ATTRIBUTE_MASK
;
1230 PyObject
*result
= NULL
;
1231 const char *kwlist
[] = { "directory", "mask", "attribs", NULL
};
1233 if (!ParseTupleAndKeywords(args
, kwds
, "z|sI:list", kwlist
,
1234 &base_dir
, &user_mask
, &attribute
)) {
1238 result
= Py_BuildValue("[]");
1239 if (result
== NULL
) {
1243 status
= do_listing(self
, base_dir
, user_mask
, attribute
,
1244 list_helper
, result
);
1246 if (!NT_STATUS_IS_OK(status
)) {
1248 PyErr_SetNTSTATUS(status
);
1258 static NTSTATUS
unlink_file(struct py_cli_state
*self
, const char *filename
)
1261 uint16_t attrs
= (FILE_ATTRIBUTE_SYSTEM
| FILE_ATTRIBUTE_HIDDEN
);
1263 if (self
->is_smb1
) {
1264 struct tevent_req
*req
= NULL
;
1266 req
= cli_unlink_send(NULL
, self
->ev
, self
->cli
, filename
,
1268 if (!py_tevent_req_wait_exc(self
, req
)) {
1269 return NT_STATUS_INTERNAL_ERROR
;
1271 status
= cli_unlink_recv(req
);
1274 status
= cli_unlink(self
->cli
, filename
, attrs
);
1280 static PyObject
*py_smb_unlink(struct py_cli_state
*self
, PyObject
*args
)
1283 const char *filename
= NULL
;
1285 if (!PyArg_ParseTuple(args
, "s:unlink", &filename
)) {
1289 status
= unlink_file(self
, filename
);
1290 PyErr_NTSTATUS_NOT_OK_RAISE(status
);
1296 * Delete an empty directory
1298 static NTSTATUS
remove_dir(struct py_cli_state
*self
, const char *dirname
)
1302 if (self
->is_smb1
) {
1303 struct tevent_req
*req
= NULL
;
1305 req
= cli_rmdir_send(NULL
, self
->ev
, self
->cli
, dirname
);
1306 if (!py_tevent_req_wait_exc(self
, req
)) {
1307 return NT_STATUS_INTERNAL_ERROR
;
1309 status
= cli_rmdir_recv(req
);
1312 status
= cli_rmdir(self
->cli
, dirname
);
1317 static PyObject
*py_smb_rmdir(struct py_cli_state
*self
, PyObject
*args
)
1320 const char *dirname
= NULL
;
1322 if (!PyArg_ParseTuple(args
, "s:rmdir", &dirname
)) {
1326 status
= remove_dir(self
, dirname
);
1327 PyErr_NTSTATUS_IS_ERR_RAISE(status
);
1333 * Create a directory
1335 static PyObject
*py_smb_mkdir(struct py_cli_state
*self
, PyObject
*args
)
1338 const char *dirname
= NULL
;
1340 if (!PyArg_ParseTuple(args
, "s:mkdir", &dirname
)) {
1344 if (self
->is_smb1
) {
1345 struct tevent_req
*req
= NULL
;
1347 req
= cli_mkdir_send(NULL
, self
->ev
, self
->cli
, dirname
);
1348 if (!py_tevent_req_wait_exc(self
, req
)) {
1351 status
= cli_mkdir_recv(req
);
1354 status
= cli_mkdir(self
->cli
, dirname
);
1356 PyErr_NTSTATUS_IS_ERR_RAISE(status
);
1362 * Checks existence of a directory
1364 static bool check_dir_path(struct py_cli_state
*self
, const char *path
)
1368 if (self
->is_smb1
) {
1369 struct tevent_req
*req
= NULL
;
1371 req
= cli_chkpath_send(NULL
, self
->ev
, self
->cli
, path
);
1372 if (!py_tevent_req_wait_exc(self
, req
)) {
1375 status
= cli_chkpath_recv(req
);
1378 status
= cli_chkpath(self
->cli
, path
);
1381 return NT_STATUS_IS_OK(status
);
1384 static PyObject
*py_smb_chkpath(struct py_cli_state
*self
, PyObject
*args
)
1386 const char *path
= NULL
;
1389 if (!PyArg_ParseTuple(args
, "s:chkpath", &path
)) {
1393 dir_exists
= check_dir_path(self
, path
);
1394 return PyBool_FromLong(dir_exists
);
1397 struct deltree_state
{
1398 struct py_cli_state
*self
;
1399 const char *full_dirpath
;
1402 static NTSTATUS
delete_dir_tree(struct py_cli_state
*self
,
1403 const char *dirpath
);
1406 * Deletes a single item in the directory tree. This could be either a file
1407 * or a directory. This function gets invoked as a callback for every item in
1408 * the given directory's listings.
1410 static NTSTATUS
delete_tree_callback(const char *mntpoint
,
1411 struct file_info
*finfo
,
1412 const char *mask
, void *priv
)
1414 char *filepath
= NULL
;
1415 struct deltree_state
*state
= priv
;
1418 /* skip '.' or '..' directory listings */
1419 if (ISDOT(finfo
->name
) || ISDOTDOT(finfo
->name
)) {
1420 return NT_STATUS_OK
;
1423 /* get the absolute filepath */
1424 filepath
= talloc_asprintf(NULL
, "%s\\%s", state
->full_dirpath
,
1426 if (filepath
== NULL
) {
1427 return NT_STATUS_NO_MEMORY
;
1430 if (finfo
->mode
& FILE_ATTRIBUTE_DIRECTORY
) {
1432 /* recursively delete the sub-directory and its contents */
1433 status
= delete_dir_tree(state
->self
, filepath
);
1435 status
= unlink_file(state
->self
, filepath
);
1438 TALLOC_FREE(filepath
);
1443 * Removes a directory and all its contents
1445 static NTSTATUS
delete_dir_tree(struct py_cli_state
*self
,
1446 const char *filepath
)
1449 const char *mask
= "*";
1450 struct deltree_state state
= { 0 };
1452 /* go through the directory's contents, deleting each item */
1454 state
.full_dirpath
= filepath
;
1455 status
= do_listing(self
, filepath
, mask
, LIST_ATTRIBUTE_MASK
,
1456 delete_tree_callback
, &state
);
1458 /* remove the directory itself */
1459 if (NT_STATUS_IS_OK(status
)) {
1460 status
= remove_dir(self
, filepath
);
1465 static PyObject
*py_smb_deltree(struct py_cli_state
*self
, PyObject
*args
)
1468 const char *filepath
= NULL
;
1471 if (!PyArg_ParseTuple(args
, "s:deltree", &filepath
)) {
1475 /* check whether we're removing a directory or a file */
1476 dir_exists
= check_dir_path(self
, filepath
);
1479 status
= delete_dir_tree(self
, filepath
);
1481 status
= unlink_file(self
, filepath
);
1484 PyErr_NTSTATUS_IS_ERR_RAISE(status
);
1490 * Read ACL on a given file/directory as a security descriptor object
1492 static PyObject
*py_smb_getacl(struct py_cli_state
*self
, PyObject
*args
)
1495 const char *filename
= NULL
;
1496 unsigned int sinfo
= SECINFO_DEFAULT_FLAGS
;
1497 unsigned int access_mask
= SEC_FLAG_MAXIMUM_ALLOWED
;
1499 struct security_descriptor
*sd
= NULL
;
1501 /* there's no async version of cli_query_security_descriptor() */
1502 if (self
->thread_state
!= NULL
) {
1503 PyErr_SetString(PyExc_RuntimeError
,
1504 "get_acl() is not supported on "
1505 "a multi_threaded connection");
1509 if (!PyArg_ParseTuple(args
, "s|II:get_acl", &filename
, &sinfo
,
1514 /* get a file handle with the desired access */
1515 status
= cli_ntcreate(self
->cli
, filename
, 0, access_mask
, 0,
1516 FILE_SHARE_READ
|FILE_SHARE_WRITE
,
1517 FILE_OPEN
, 0x0, 0x0, &fnum
, NULL
);
1518 PyErr_NTSTATUS_IS_ERR_RAISE(status
);
1520 /* query the security descriptor for this file */
1521 status
= cli_query_security_descriptor(self
->cli
, fnum
, sinfo
,
1523 PyErr_NTSTATUS_IS_ERR_RAISE(status
);
1525 /* close the file handle and convert the SD to a python struct */
1526 status
= cli_close(self
->cli
, fnum
);
1527 PyErr_NTSTATUS_IS_ERR_RAISE(status
);
1529 return py_return_ndr_struct("samba.dcerpc.security", "descriptor",
1534 * Set ACL on file/directory using given security descriptor object
1536 static PyObject
*py_smb_setacl(struct py_cli_state
*self
, PyObject
*args
)
1539 char *filename
= NULL
;
1540 PyObject
*py_sd
= NULL
;
1541 struct security_descriptor
*sd
= NULL
;
1542 unsigned int sinfo
= SECINFO_DEFAULT_FLAGS
;
1545 /* there's no async version of cli_set_security_descriptor() */
1546 if (self
->thread_state
!= NULL
) {
1547 PyErr_SetString(PyExc_RuntimeError
,
1548 "set_acl() is not supported on "
1549 "a multi_threaded connection");
1553 if (!PyArg_ParseTuple(args
, "sO|I:set_acl", &filename
, &py_sd
,
1558 sd
= pytalloc_get_type(py_sd
, struct security_descriptor
);
1560 PyErr_Format(PyExc_TypeError
,
1561 "Expected dcerpc.security.descriptor as argument, got %s",
1562 talloc_get_name(pytalloc_get_ptr(py_sd
)));
1566 status
= cli_ntcreate(self
->cli
, filename
, 0,
1567 SEC_FLAG_MAXIMUM_ALLOWED
, 0,
1568 FILE_SHARE_READ
|FILE_SHARE_WRITE
,
1569 FILE_OPEN
, 0x0, 0x0, &fnum
, NULL
);
1570 PyErr_NTSTATUS_IS_ERR_RAISE(status
);
1572 status
= cli_set_security_descriptor(self
->cli
, fnum
, sinfo
, sd
);
1573 PyErr_NTSTATUS_IS_ERR_RAISE(status
);
1575 status
= cli_close(self
->cli
, fnum
);
1576 PyErr_NTSTATUS_IS_ERR_RAISE(status
);
1581 static PyMethodDef py_cli_state_methods
[] = {
1582 { "settimeout", (PyCFunction
)py_cli_settimeout
, METH_VARARGS
,
1583 "settimeout(new_timeout_msecs) => return old_timeout_msecs" },
1584 { "create", (PyCFunction
)py_cli_create
, METH_VARARGS
|METH_KEYWORDS
,
1586 { "close", (PyCFunction
)py_cli_close
, METH_VARARGS
,
1587 "Close a file handle" },
1588 { "write", (PyCFunction
)py_cli_write
, METH_VARARGS
|METH_KEYWORDS
,
1589 "Write to a file handle" },
1590 { "read", (PyCFunction
)py_cli_read
, METH_VARARGS
|METH_KEYWORDS
,
1591 "Read from a file handle" },
1592 { "truncate", (PyCFunction
)py_cli_ftruncate
,
1593 METH_VARARGS
|METH_KEYWORDS
,
1594 "Truncate a file" },
1595 { "delete_on_close", (PyCFunction
)py_cli_delete_on_close
,
1596 METH_VARARGS
|METH_KEYWORDS
,
1597 "Set/Reset the delete on close flag" },
1598 { "list", (PyCFunction
)py_cli_list
, METH_VARARGS
|METH_KEYWORDS
,
1599 "list(directory, mask='*', attribs=DEFAULT_ATTRS) -> "
1600 "directory contents as a dictionary\n"
1601 "\t\tDEFAULT_ATTRS: FILE_ATTRIBUTE_SYSTEM | "
1602 "FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_ARCHIVE\n\n"
1603 "\t\tList contents of a directory. The keys are, \n"
1604 "\t\t\tname: Long name of the directory item\n"
1605 "\t\t\tshort_name: Short name of the directory item\n"
1606 "\t\t\tsize: File size in bytes\n"
1607 "\t\t\tattrib: Attributes\n"
1608 "\t\t\tmtime: Modification time\n" },
1609 { "get_oplock_break", (PyCFunction
)py_cli_get_oplock_break
,
1610 METH_VARARGS
, "Wait for an oplock break" },
1611 { "unlink", (PyCFunction
)py_smb_unlink
,
1613 "unlink(path) -> None\n\n \t\tDelete a file." },
1614 { "mkdir", (PyCFunction
)py_smb_mkdir
, METH_VARARGS
,
1615 "mkdir(path) -> None\n\n \t\tCreate a directory." },
1616 { "rmdir", (PyCFunction
)py_smb_rmdir
, METH_VARARGS
,
1617 "rmdir(path) -> None\n\n \t\tDelete a directory." },
1618 { "chkpath", (PyCFunction
)py_smb_chkpath
, METH_VARARGS
,
1619 "chkpath(dir_path) -> True or False\n\n"
1620 "\t\tReturn true if directory exists, false otherwise." },
1621 { "savefile", (PyCFunction
)py_smb_savefile
, METH_VARARGS
,
1622 "savefile(path, str) -> None\n\n"
1623 "\t\tWrite " PY_DESC_PY3_BYTES
" str to file." },
1624 { "loadfile", (PyCFunction
)py_smb_loadfile
, METH_VARARGS
,
1625 "loadfile(path) -> file contents as a " PY_DESC_PY3_BYTES
1626 "\n\n\t\tRead contents of a file." },
1627 { "deltree", (PyCFunction
)py_smb_deltree
, METH_VARARGS
,
1628 "deltree(path) -> None\n\n"
1629 "\t\tDelete a directory and all its contents." },
1630 { "get_acl", (PyCFunction
)py_smb_getacl
, METH_VARARGS
,
1631 "get_acl(path[, security_info=0]) -> security_descriptor object\n\n"
1632 "\t\tGet security descriptor for file." },
1633 { "set_acl", (PyCFunction
)py_smb_setacl
, METH_VARARGS
,
1634 "set_acl(path, security_descriptor[, security_info=0]) -> None\n\n"
1635 "\t\tSet security descriptor for file." },
1636 { NULL
, NULL
, 0, NULL
}
1639 static PyTypeObject py_cli_state_type
= {
1640 PyVarObject_HEAD_INIT(NULL
, 0)
1641 .tp_name
= "libsmb_samba_internal.Conn",
1642 .tp_basicsize
= sizeof(struct py_cli_state
),
1643 .tp_flags
= Py_TPFLAGS_DEFAULT
| Py_TPFLAGS_BASETYPE
,
1644 .tp_doc
= "libsmb connection",
1645 .tp_new
= py_cli_state_new
,
1646 .tp_init
= (initproc
)py_cli_state_init
,
1647 .tp_dealloc
= (destructor
)py_cli_state_dealloc
,
1648 .tp_methods
= py_cli_state_methods
,
1651 static PyMethodDef py_libsmb_methods
[] = {
1655 void initlibsmb_samba_internal(void);
1657 static struct PyModuleDef moduledef
= {
1658 PyModuleDef_HEAD_INIT
,
1659 .m_name
= "libsmb_samba_internal",
1660 .m_doc
= "libsmb wrapper",
1662 .m_methods
= py_libsmb_methods
,
1665 MODULE_INIT_FUNC(libsmb_samba_internal
)
1669 talloc_stackframe();
1671 m
= PyModule_Create(&moduledef
);
1675 if (PyType_Ready(&py_cli_state_type
) < 0) {
1678 Py_INCREF(&py_cli_state_type
);
1679 PyModule_AddObject(m
, "Conn", (PyObject
*)&py_cli_state_type
);
1681 #define ADD_FLAGS(val) PyModule_AddObject(m, #val, PyInt_FromLong(val))
1683 ADD_FLAGS(FILE_ATTRIBUTE_READONLY
);
1684 ADD_FLAGS(FILE_ATTRIBUTE_HIDDEN
);
1685 ADD_FLAGS(FILE_ATTRIBUTE_SYSTEM
);
1686 ADD_FLAGS(FILE_ATTRIBUTE_VOLUME
);
1687 ADD_FLAGS(FILE_ATTRIBUTE_DIRECTORY
);
1688 ADD_FLAGS(FILE_ATTRIBUTE_ARCHIVE
);
1689 ADD_FLAGS(FILE_ATTRIBUTE_DEVICE
);
1690 ADD_FLAGS(FILE_ATTRIBUTE_NORMAL
);
1691 ADD_FLAGS(FILE_ATTRIBUTE_TEMPORARY
);
1692 ADD_FLAGS(FILE_ATTRIBUTE_SPARSE
);
1693 ADD_FLAGS(FILE_ATTRIBUTE_REPARSE_POINT
);
1694 ADD_FLAGS(FILE_ATTRIBUTE_COMPRESSED
);
1695 ADD_FLAGS(FILE_ATTRIBUTE_OFFLINE
);
1696 ADD_FLAGS(FILE_ATTRIBUTE_NONINDEXED
);
1697 ADD_FLAGS(FILE_ATTRIBUTE_ENCRYPTED
);
1698 ADD_FLAGS(FILE_ATTRIBUTE_ALL_MASK
);