s3:pylibsmb: allow ImpersonationLevel argument to create()
[amitay/samba.git] / source3 / libsmb / pylibsmb.c
1 /*
2  * Unix SMB/CIFS implementation.
3  * Samba-internal work in progress Python binding for libsmbclient
4  *
5  * Copyright (C) Volker Lendecke 2012
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include <Python.h>
22 #include "includes.h"
23 #include "python/py3compat.h"
24 #include "libcli/smb/smbXcli_base.h"
25 #include "libsmb/libsmb.h"
26 #include "libcli/security/security.h"
27 #include "system/select.h"
28 #include "source4/libcli/util/pyerrors.h"
29 #include "auth/credentials/pycredentials.h"
30 #include "trans2.h"
31
32 static PyTypeObject *get_pytype(const char *module, const char *type)
33 {
34         PyObject *mod;
35         PyTypeObject *result;
36
37         mod = PyImport_ImportModule(module);
38         if (mod == NULL) {
39                 PyErr_Format(PyExc_RuntimeError,
40                              "Unable to import %s to check type %s",
41                              module, type);
42                 return NULL;
43         }
44         result = (PyTypeObject *)PyObject_GetAttrString(mod, type);
45         Py_DECREF(mod);
46         if (result == NULL) {
47                 PyErr_Format(PyExc_RuntimeError,
48                              "Unable to find type %s in module %s",
49                              module, type);
50                 return NULL;
51         }
52         return result;
53 }
54
55 /*
56  * We're using "const char * const *" for keywords,
57  * PyArg_ParseTupleAndKeywords expects a "char **". Confine the
58  * inevitable warnings to just one place.
59  */
60 static int ParseTupleAndKeywords(PyObject *args, PyObject *kw,
61                                  const char *format, const char * const *keywords,
62                                  ...)
63 {
64         char **_keywords = discard_const_p(char *, keywords);
65         va_list a;
66         int ret;
67         va_start(a, keywords);
68         ret = PyArg_VaParseTupleAndKeywords(args, kw, format,
69                                             _keywords, a);
70         va_end(a);
71         return ret;
72 }
73
74 struct py_cli_thread;
75
76 struct py_cli_oplock_break {
77         uint16_t fnum;
78         uint8_t level;
79 };
80
81 struct py_cli_state {
82         PyObject_HEAD
83         struct cli_state *cli;
84         bool is_smb1;
85         struct tevent_context *ev;
86         int (*req_wait_fn)(struct tevent_context *ev,
87                            struct tevent_req *req);
88         struct py_cli_thread *thread_state;
89
90         struct tevent_req *oplock_waiter;
91         struct py_cli_oplock_break *oplock_breaks;
92         struct py_tevent_cond *oplock_cond;
93 };
94
95 #ifdef HAVE_PTHREAD
96
97 #include <pthread.h>
98
99 struct py_cli_thread {
100
101         /*
102          * Pipe to make the poll thread wake up in our destructor, so
103          * that we can exit and join the thread.
104          */
105         int shutdown_pipe[2];
106         struct tevent_fd *shutdown_fde;
107         bool do_shutdown;
108         pthread_t id;
109
110         /*
111          * Thread state to release the GIL during the poll(2) syscall
112          */
113         PyThreadState *py_threadstate;
114 };
115
116 static void *py_cli_state_poll_thread(void *private_data)
117 {
118         struct py_cli_state *self = (struct py_cli_state *)private_data;
119         struct py_cli_thread *t = self->thread_state;
120         PyGILState_STATE gstate;
121
122         gstate = PyGILState_Ensure();
123
124         while (!t->do_shutdown) {
125                 int ret;
126                 ret = tevent_loop_once(self->ev);
127                 assert(ret == 0);
128         }
129         PyGILState_Release(gstate);
130         return NULL;
131 }
132
133 static void py_cli_state_trace_callback(enum tevent_trace_point point,
134                                         void *private_data)
135 {
136         struct py_cli_state *self = (struct py_cli_state *)private_data;
137         struct py_cli_thread *t = self->thread_state;
138
139         switch(point) {
140         case TEVENT_TRACE_BEFORE_WAIT:
141                 assert(t->py_threadstate == NULL);
142                 t->py_threadstate = PyEval_SaveThread();
143                 break;
144         case TEVENT_TRACE_AFTER_WAIT:
145                 assert(t->py_threadstate != NULL);
146                 PyEval_RestoreThread(t->py_threadstate);
147                 t->py_threadstate = NULL;
148                 break;
149         default:
150                 break;
151         }
152 }
153
154 static void py_cli_state_shutdown_handler(struct tevent_context *ev,
155                                           struct tevent_fd *fde,
156                                           uint16_t flags,
157                                           void *private_data)
158 {
159         struct py_cli_state *self = (struct py_cli_state *)private_data;
160         struct py_cli_thread *t = self->thread_state;
161
162         if ((flags & TEVENT_FD_READ) == 0) {
163                 return;
164         }
165         TALLOC_FREE(t->shutdown_fde);
166         t->do_shutdown = true;
167 }
168
169 static int py_cli_thread_destructor(struct py_cli_thread *t)
170 {
171         char c = 0;
172         ssize_t written;
173         int ret;
174
175         do {
176                 /*
177                  * This will wake the poll thread from the poll(2)
178                  */
179                 written = write(t->shutdown_pipe[1], &c, 1);
180         } while ((written == -1) && (errno == EINTR));
181
182         /*
183          * Allow the poll thread to do its own cleanup under the GIL
184          */
185         Py_BEGIN_ALLOW_THREADS
186         ret = pthread_join(t->id, NULL);
187         Py_END_ALLOW_THREADS
188         assert(ret == 0);
189
190         if (t->shutdown_pipe[0] != -1) {
191                 close(t->shutdown_pipe[0]);
192                 t->shutdown_pipe[0] = -1;
193         }
194         if (t->shutdown_pipe[1] != -1) {
195                 close(t->shutdown_pipe[1]);
196                 t->shutdown_pipe[1] = -1;
197         }
198         return 0;
199 }
200
201 static int py_tevent_cond_req_wait(struct tevent_context *ev,
202                                    struct tevent_req *req);
203
204 static bool py_cli_state_setup_mt_ev(struct py_cli_state *self)
205 {
206         struct py_cli_thread *t = NULL;
207         int ret;
208
209         self->ev = tevent_context_init_byname(NULL, "poll_mt");
210         if (self->ev == NULL) {
211                 goto fail;
212         }
213         samba_tevent_set_debug(self->ev, "pylibsmb_tevent_mt");
214         tevent_set_trace_callback(self->ev, py_cli_state_trace_callback, self);
215
216         self->req_wait_fn = py_tevent_cond_req_wait;
217
218         self->thread_state = talloc_zero(NULL, struct py_cli_thread);
219         if (self->thread_state == NULL) {
220                 goto fail;
221         }
222         t = self->thread_state;
223
224         ret = pipe(t->shutdown_pipe);
225         if (ret == -1) {
226                 goto fail;
227         }
228         t->shutdown_fde = tevent_add_fd(
229                 self->ev, self->ev, t->shutdown_pipe[0], TEVENT_FD_READ,
230                 py_cli_state_shutdown_handler, self);
231         if (t->shutdown_fde == NULL) {
232                 goto fail;
233         }
234
235         PyEval_InitThreads();
236
237         ret = pthread_create(&t->id, NULL, py_cli_state_poll_thread, self);
238         if (ret != 0) {
239                 goto fail;
240         }
241         talloc_set_destructor(self->thread_state, py_cli_thread_destructor);
242         return true;
243
244 fail:
245         if (t != NULL) {
246                 TALLOC_FREE(t->shutdown_fde);
247
248                 if (t->shutdown_pipe[0] != -1) {
249                         close(t->shutdown_pipe[0]);
250                         t->shutdown_pipe[0] = -1;
251                 }
252                 if (t->shutdown_pipe[1] != -1) {
253                         close(t->shutdown_pipe[1]);
254                         t->shutdown_pipe[1] = -1;
255                 }
256         }
257
258         TALLOC_FREE(self->thread_state);
259         TALLOC_FREE(self->ev);
260         return false;
261 }
262
263 struct py_tevent_cond {
264         pthread_mutex_t mutex;
265         pthread_cond_t cond;
266         bool is_done;
267 };
268
269 static void py_tevent_signalme(struct tevent_req *req);
270
271 static int py_tevent_cond_wait(struct py_tevent_cond *cond)
272 {
273         int ret, result;
274
275         result = pthread_mutex_init(&cond->mutex, NULL);
276         if (result != 0) {
277                 goto fail;
278         }
279         result = pthread_cond_init(&cond->cond, NULL);
280         if (result != 0) {
281                 goto fail_mutex;
282         }
283
284         result = pthread_mutex_lock(&cond->mutex);
285         if (result != 0) {
286                 goto fail_cond;
287         }
288
289         cond->is_done = false;
290
291         while (!cond->is_done) {
292
293                 Py_BEGIN_ALLOW_THREADS
294                 result = pthread_cond_wait(&cond->cond, &cond->mutex);
295                 Py_END_ALLOW_THREADS
296
297                 if (result != 0) {
298                         goto fail_unlock;
299                 }
300         }
301
302 fail_unlock:
303         ret = pthread_mutex_unlock(&cond->mutex);
304         assert(ret == 0);
305 fail_cond:
306         ret = pthread_cond_destroy(&cond->cond);
307         assert(ret == 0);
308 fail_mutex:
309         ret = pthread_mutex_destroy(&cond->mutex);
310         assert(ret == 0);
311 fail:
312         return result;
313 }
314
315 static int py_tevent_cond_req_wait(struct tevent_context *ev,
316                                    struct tevent_req *req)
317 {
318         struct py_tevent_cond cond;
319         tevent_req_set_callback(req, py_tevent_signalme, &cond);
320         return py_tevent_cond_wait(&cond);
321 }
322
323 static void py_tevent_cond_signal(struct py_tevent_cond *cond)
324 {
325         int ret;
326
327         ret = pthread_mutex_lock(&cond->mutex);
328         assert(ret == 0);
329
330         cond->is_done = true;
331
332         ret = pthread_cond_signal(&cond->cond);
333         assert(ret == 0);
334         ret = pthread_mutex_unlock(&cond->mutex);
335         assert(ret == 0);
336 }
337
338 static void py_tevent_signalme(struct tevent_req *req)
339 {
340         struct py_tevent_cond *cond = (struct py_tevent_cond *)
341                 tevent_req_callback_data_void(req);
342
343         py_tevent_cond_signal(cond);
344 }
345
346 #endif
347
348 static int py_tevent_req_wait(struct tevent_context *ev,
349                               struct tevent_req *req);
350
351 static bool py_cli_state_setup_ev(struct py_cli_state *self)
352 {
353         self->ev = tevent_context_init(NULL);
354         if (self->ev == NULL) {
355                 return false;
356         }
357
358         samba_tevent_set_debug(self->ev, "pylibsmb_tevent");
359
360         self->req_wait_fn = py_tevent_req_wait;
361
362         return true;
363 }
364
365 static int py_tevent_req_wait(struct tevent_context *ev,
366                               struct tevent_req *req)
367 {
368         while (tevent_req_is_in_progress(req)) {
369                 int ret;
370
371                 ret = tevent_loop_once(ev);
372                 if (ret != 0) {
373                         return ret;
374                 }
375         }
376         return 0;
377 }
378
379 static bool py_tevent_req_wait_exc(struct py_cli_state *self,
380                                    struct tevent_req *req)
381 {
382         int ret;
383
384         if (req == NULL) {
385                 PyErr_NoMemory();
386                 return false;
387         }
388         ret = self->req_wait_fn(self->ev, req);
389         if (ret != 0) {
390                 TALLOC_FREE(req);
391                 errno = ret;
392                 PyErr_SetFromErrno(PyExc_RuntimeError);
393                 return false;
394         }
395         return true;
396 }
397
398 static PyObject *py_cli_state_new(PyTypeObject *type, PyObject *args,
399                                   PyObject *kwds)
400 {
401         struct py_cli_state *self;
402
403         self = (struct py_cli_state *)type->tp_alloc(type, 0);
404         if (self == NULL) {
405                 return NULL;
406         }
407         self->cli = NULL;
408         self->is_smb1 = false;
409         self->ev = NULL;
410         self->thread_state = NULL;
411         self->oplock_waiter = NULL;
412         self->oplock_cond = NULL;
413         self->oplock_breaks = NULL;
414         return (PyObject *)self;
415 }
416
417 static void py_cli_got_oplock_break(struct tevent_req *req);
418
419 static int py_cli_state_init(struct py_cli_state *self, PyObject *args,
420                              PyObject *kwds)
421 {
422         NTSTATUS status;
423         char *host, *share;
424         PyObject *creds = NULL;
425         struct cli_credentials *cli_creds;
426         PyObject *py_multi_threaded = Py_False;
427         bool multi_threaded = false;
428         PyObject *py_sign = Py_False;
429         bool sign = false;
430         int signing_state = SMB_SIGNING_DEFAULT;
431         PyObject *py_force_smb1 = Py_False;
432         bool force_smb1 = false;
433         struct tevent_req *req;
434         bool ret;
435         int flags = 0;
436
437         static const char *kwlist[] = {
438                 "host", "share", "credentials",
439                 "multi_threaded", "sign", "force_smb1",
440                 NULL
441         };
442
443         PyTypeObject *py_type_Credentials = get_pytype(
444                 "samba.credentials", "Credentials");
445         if (py_type_Credentials == NULL) {
446                 return -1;
447         }
448
449         ret = ParseTupleAndKeywords(
450                 args, kwds, "ss|O!OOO", kwlist,
451                 &host, &share,
452                 py_type_Credentials, &creds,
453                 &py_multi_threaded,
454                 &py_sign,
455                 &py_force_smb1);
456
457         Py_DECREF(py_type_Credentials);
458
459         if (!ret) {
460                 return -1;
461         }
462
463         multi_threaded = PyObject_IsTrue(py_multi_threaded);
464         sign = PyObject_IsTrue(py_sign);
465         force_smb1 = PyObject_IsTrue(py_force_smb1);
466
467         if (sign) {
468                 signing_state = SMB_SIGNING_REQUIRED;
469         }
470
471         if (force_smb1) {
472                 /*
473                  * As most of the cli_*_send() function
474                  * don't support SMB2 (it's only plugged
475                  * into the sync wrapper functions currently)
476                  * we have a way to force SMB1.
477                  */
478                 flags = CLI_FULL_CONNECTION_FORCE_SMB1;
479         }
480
481         if (multi_threaded) {
482 #ifdef HAVE_PTHREAD
483                 ret = py_cli_state_setup_mt_ev(self);
484                 if (!ret) {
485                         return -1;
486                 }
487 #else
488                 PyErr_SetString(PyExc_RuntimeError,
489                                 "No PTHREAD support available");
490                 return -1;
491 #endif
492                 if (!force_smb1) {
493                         PyErr_SetString(PyExc_RuntimeError,
494                                         "multi_threaded is only possible on "
495                                         "SMB1 connections");
496                         return -1;
497                 }
498         } else {
499                 ret = py_cli_state_setup_ev(self);
500                 if (!ret) {
501                         return -1;
502                 }
503         }
504
505         if (creds == NULL) {
506                 cli_creds = cli_credentials_init_anon(NULL);
507         } else {
508                 cli_creds = PyCredentials_AsCliCredentials(creds);
509         }
510
511         req = cli_full_connection_creds_send(
512                 NULL, self->ev, "myname", host, NULL, 0, share, "?????",
513                 cli_creds, flags, signing_state);
514         if (!py_tevent_req_wait_exc(self, req)) {
515                 return -1;
516         }
517         status = cli_full_connection_creds_recv(req, &self->cli);
518         TALLOC_FREE(req);
519
520         if (!NT_STATUS_IS_OK(status)) {
521                 PyErr_SetNTSTATUS(status);
522                 return -1;
523         }
524
525         if (smbXcli_conn_protocol(self->cli->conn) < PROTOCOL_SMB2_02) {
526                 self->is_smb1 = true;
527         }
528
529         /*
530          * Oplocks require a multi threaded connection
531          */
532         if (self->thread_state == NULL) {
533                 return 0;
534         }
535
536         self->oplock_waiter = cli_smb_oplock_break_waiter_send(
537                 self->ev, self->ev, self->cli);
538         if (self->oplock_waiter == NULL) {
539                 PyErr_NoMemory();
540                 return -1;
541         }
542         tevent_req_set_callback(self->oplock_waiter, py_cli_got_oplock_break,
543                                 self);
544         return 0;
545 }
546
547 static void py_cli_got_oplock_break(struct tevent_req *req)
548 {
549         struct py_cli_state *self = (struct py_cli_state *)
550                 tevent_req_callback_data_void(req);
551         struct py_cli_oplock_break b;
552         struct py_cli_oplock_break *tmp;
553         size_t num_breaks;
554         NTSTATUS status;
555
556         status = cli_smb_oplock_break_waiter_recv(req, &b.fnum, &b.level);
557         TALLOC_FREE(req);
558         self->oplock_waiter = NULL;
559
560         if (!NT_STATUS_IS_OK(status)) {
561                 return;
562         }
563
564         num_breaks = talloc_array_length(self->oplock_breaks);
565         tmp = talloc_realloc(self->ev, self->oplock_breaks,
566                              struct py_cli_oplock_break, num_breaks+1);
567         if (tmp == NULL) {
568                 return;
569         }
570         self->oplock_breaks = tmp;
571         self->oplock_breaks[num_breaks] = b;
572
573         if (self->oplock_cond != NULL) {
574                 py_tevent_cond_signal(self->oplock_cond);
575         }
576
577         self->oplock_waiter = cli_smb_oplock_break_waiter_send(
578                 self->ev, self->ev, self->cli);
579         if (self->oplock_waiter == NULL) {
580                 return;
581         }
582         tevent_req_set_callback(self->oplock_waiter, py_cli_got_oplock_break,
583                                 self);
584 }
585
586 static PyObject *py_cli_get_oplock_break(struct py_cli_state *self,
587                                          PyObject *args)
588 {
589         size_t num_oplock_breaks;
590
591         if (!PyArg_ParseTuple(args, "")) {
592                 return NULL;
593         }
594
595         if (self->thread_state == NULL) {
596                 PyErr_SetString(PyExc_RuntimeError,
597                                 "get_oplock_break() only possible on "
598                                 "a multi_threaded connection");
599                 return NULL;
600         }
601
602         if (self->oplock_cond != NULL) {
603                 errno = EBUSY;
604                 PyErr_SetFromErrno(PyExc_RuntimeError);
605                 return NULL;
606         }
607
608         num_oplock_breaks = talloc_array_length(self->oplock_breaks);
609
610         if (num_oplock_breaks == 0) {
611                 struct py_tevent_cond cond;
612                 int ret;
613
614                 self->oplock_cond = &cond;
615                 ret = py_tevent_cond_wait(&cond);
616                 self->oplock_cond = NULL;
617
618                 if (ret != 0) {
619                         errno = ret;
620                         PyErr_SetFromErrno(PyExc_RuntimeError);
621                         return NULL;
622                 }
623         }
624
625         num_oplock_breaks = talloc_array_length(self->oplock_breaks);
626         if (num_oplock_breaks > 0) {
627                 PyObject *result;
628
629                 result = Py_BuildValue(
630                         "{s:i,s:i}",
631                         "fnum", self->oplock_breaks[0].fnum,
632                         "level", self->oplock_breaks[0].level);
633
634                 memmove(&self->oplock_breaks[0], &self->oplock_breaks[1],
635                         sizeof(self->oplock_breaks[0]) *
636                         (num_oplock_breaks - 1));
637                 self->oplock_breaks = talloc_realloc(
638                         NULL, self->oplock_breaks, struct py_cli_oplock_break,
639                         num_oplock_breaks - 1);
640
641                 return result;
642         }
643         Py_RETURN_NONE;
644 }
645
646 static void py_cli_state_dealloc(struct py_cli_state *self)
647 {
648         TALLOC_FREE(self->thread_state);
649         TALLOC_FREE(self->oplock_waiter);
650         TALLOC_FREE(self->ev);
651
652         if (self->cli != NULL) {
653                 cli_shutdown(self->cli);
654                 self->cli = NULL;
655         }
656         Py_TYPE(self)->tp_free((PyObject *)self);
657 }
658
659 static PyObject *py_cli_create(struct py_cli_state *self, PyObject *args,
660                                PyObject *kwds)
661 {
662         char *fname;
663         unsigned CreateFlags = 0;
664         unsigned DesiredAccess = FILE_GENERIC_READ;
665         unsigned FileAttributes = 0;
666         unsigned ShareAccess = 0;
667         unsigned CreateDisposition = FILE_OPEN;
668         unsigned CreateOptions = 0;
669         unsigned ImpersonationLevel = SMB2_IMPERSONATION_IMPERSONATION;
670         unsigned SecurityFlags = 0;
671         uint16_t fnum;
672         struct tevent_req *req;
673         NTSTATUS status;
674
675         static const char *kwlist[] = {
676                 "Name", "CreateFlags", "DesiredAccess", "FileAttributes",
677                 "ShareAccess", "CreateDisposition", "CreateOptions",
678                 "ImpersonationLevel", "SecurityFlags", NULL };
679
680         if (!ParseTupleAndKeywords(
681                     args, kwds, "s|IIIIIIII", kwlist,
682                     &fname, &CreateFlags, &DesiredAccess, &FileAttributes,
683                     &ShareAccess, &CreateDisposition, &CreateOptions,
684                     &ImpersonationLevel, &SecurityFlags)) {
685                 return NULL;
686         }
687
688         req = cli_ntcreate_send(NULL, self->ev, self->cli, fname, CreateFlags,
689                                 DesiredAccess, FileAttributes, ShareAccess,
690                                 CreateDisposition, CreateOptions,
691                                 ImpersonationLevel, SecurityFlags);
692         if (!py_tevent_req_wait_exc(self, req)) {
693                 return NULL;
694         }
695         status = cli_ntcreate_recv(req, &fnum, NULL);
696         TALLOC_FREE(req);
697
698         if (!NT_STATUS_IS_OK(status)) {
699                 PyErr_SetNTSTATUS(status);
700                 return NULL;
701         }
702         return Py_BuildValue("I", (unsigned)fnum);
703 }
704
705 static PyObject *py_cli_close(struct py_cli_state *self, PyObject *args)
706 {
707         struct tevent_req *req;
708         int fnum;
709         NTSTATUS status;
710
711         if (!PyArg_ParseTuple(args, "i", &fnum)) {
712                 return NULL;
713         }
714
715         req = cli_close_send(NULL, self->ev, self->cli, fnum);
716         if (!py_tevent_req_wait_exc(self, req)) {
717                 return NULL;
718         }
719         status = cli_close_recv(req);
720         TALLOC_FREE(req);
721
722         if (!NT_STATUS_IS_OK(status)) {
723                 PyErr_SetNTSTATUS(status);
724                 return NULL;
725         }
726         Py_RETURN_NONE;
727 }
728
729 static PyObject *py_cli_write(struct py_cli_state *self, PyObject *args,
730                               PyObject *kwds)
731 {
732         int fnum;
733         unsigned mode = 0;
734         char *buf;
735         Py_ssize_t buflen;
736         unsigned long long offset;
737         struct tevent_req *req;
738         NTSTATUS status;
739         size_t written;
740
741         static const char *kwlist[] = {
742                 "fnum", "buffer", "offset", "mode", NULL };
743
744         if (!ParseTupleAndKeywords(
745                     args, kwds, "I" PYARG_BYTES_LEN "K|I", kwlist,
746                     &fnum, &buf, &buflen, &offset, &mode)) {
747                 return NULL;
748         }
749
750         req = cli_write_send(NULL, self->ev, self->cli, fnum, mode,
751                              (uint8_t *)buf, offset, buflen);
752         if (!py_tevent_req_wait_exc(self, req)) {
753                 return NULL;
754         }
755         status = cli_write_recv(req, &written);
756         TALLOC_FREE(req);
757
758         if (!NT_STATUS_IS_OK(status)) {
759                 PyErr_SetNTSTATUS(status);
760                 return NULL;
761         }
762         return Py_BuildValue("K", (unsigned long long)written);
763 }
764
765 static PyObject *py_cli_read(struct py_cli_state *self, PyObject *args,
766                              PyObject *kwds)
767 {
768         int fnum;
769         unsigned long long offset;
770         unsigned size;
771         struct tevent_req *req;
772         NTSTATUS status;
773         char *buf;
774         size_t received;
775         PyObject *result;
776
777         static const char *kwlist[] = {
778                 "fnum", "offset", "size", NULL };
779
780         if (!ParseTupleAndKeywords(
781                     args, kwds, "IKI", kwlist, &fnum, &offset,
782                     &size)) {
783                 return NULL;
784         }
785
786         result = PyBytes_FromStringAndSize(NULL, size);
787         if (result == NULL) {
788                 return NULL;
789         }
790         buf = PyBytes_AS_STRING(result);
791
792         req = cli_read_send(NULL, self->ev, self->cli, fnum,
793                             buf, offset, size);
794         if (!py_tevent_req_wait_exc(self, req)) {
795                 Py_XDECREF(result);
796                 return NULL;
797         }
798         status = cli_read_recv(req, &received);
799         TALLOC_FREE(req);
800
801         if (!NT_STATUS_IS_OK(status)) {
802                 Py_XDECREF(result);
803                 PyErr_SetNTSTATUS(status);
804                 return NULL;
805         }
806
807         if (received > size) {
808                 Py_XDECREF(result);
809                 PyErr_Format(PyExc_IOError,
810                              "read invalid - got %zu requested %u",
811                              received, size);
812                 return NULL;
813         }
814
815         if (received < size) {
816                 if (_PyBytes_Resize(&result, received) < 0) {
817                         return NULL;
818                 }
819         }
820
821         return result;
822 }
823
824 static PyObject *py_cli_ftruncate(struct py_cli_state *self, PyObject *args,
825                                   PyObject *kwds)
826 {
827         int fnum;
828         unsigned long long size;
829         struct tevent_req *req;
830         NTSTATUS status;
831
832         static const char *kwlist[] = {
833                 "fnum", "size", NULL };
834
835         if (!ParseTupleAndKeywords(
836                     args, kwds, "IK", kwlist, &fnum, &size)) {
837                 return NULL;
838         }
839
840         req = cli_ftruncate_send(NULL, self->ev, self->cli, fnum, size);
841         if (!py_tevent_req_wait_exc(self, req)) {
842                 return NULL;
843         }
844         status = cli_ftruncate_recv(req);
845         TALLOC_FREE(req);
846
847         if (!NT_STATUS_IS_OK(status)) {
848                 PyErr_SetNTSTATUS(status);
849                 return NULL;
850         }
851         Py_RETURN_NONE;
852 }
853
854 static PyObject *py_cli_delete_on_close(struct py_cli_state *self,
855                                         PyObject *args,
856                                         PyObject *kwds)
857 {
858         unsigned fnum, flag;
859         struct tevent_req *req;
860         NTSTATUS status;
861
862         static const char *kwlist[] = {
863                 "fnum", "flag", NULL };
864
865         if (!ParseTupleAndKeywords(
866                     args, kwds, "II", kwlist, &fnum, &flag)) {
867                 return NULL;
868         }
869
870         req = cli_nt_delete_on_close_send(NULL, self->ev, self->cli, fnum,
871                                           flag);
872         if (!py_tevent_req_wait_exc(self, req)) {
873                 return NULL;
874         }
875         status = cli_nt_delete_on_close_recv(req);
876         TALLOC_FREE(req);
877
878         if (!NT_STATUS_IS_OK(status)) {
879                 PyErr_SetNTSTATUS(status);
880                 return NULL;
881         }
882         Py_RETURN_NONE;
883 }
884
885 static PyObject *py_cli_list(struct py_cli_state *self,
886                              PyObject *args,
887                              PyObject *kwds)
888 {
889         char *mask;
890         unsigned attribute =
891                 FILE_ATTRIBUTE_DIRECTORY |
892                 FILE_ATTRIBUTE_SYSTEM |
893                 FILE_ATTRIBUTE_HIDDEN;
894         unsigned info_level = SMB_FIND_FILE_BOTH_DIRECTORY_INFO;
895         struct tevent_req *req;
896         NTSTATUS status;
897         struct file_info *finfos;
898         size_t i, num_finfos;
899         PyObject *result;
900
901         const char *kwlist[] = {
902                 "mask", "attribute", "info_level", NULL
903         };
904
905         if (!ParseTupleAndKeywords(
906                     args, kwds, "s|II", kwlist,
907                     &mask, &attribute, &info_level)) {
908                 return NULL;
909         }
910
911         req = cli_list_send(NULL, self->ev, self->cli, mask, attribute,
912                             info_level);
913         if (!py_tevent_req_wait_exc(self, req)) {
914                 return NULL;
915         }
916         status = cli_list_recv(req, NULL, &finfos, &num_finfos);
917         TALLOC_FREE(req);
918
919         if (!NT_STATUS_IS_OK(status)) {
920                 PyErr_SetNTSTATUS(status);
921                 return NULL;
922         }
923
924         result = Py_BuildValue("[]");
925         if (result == NULL) {
926                 return NULL;
927         }
928
929         for (i=0; i<num_finfos; i++) {
930                 struct file_info *finfo = &finfos[i];
931                 PyObject *file;
932                 int ret;
933
934                 file = Py_BuildValue(
935                         "{s:s,s:i}",
936                         "name", finfo->name,
937                         "mode", (int)finfo->mode);
938                 if (file == NULL) {
939                         Py_XDECREF(result);
940                         return NULL;
941                 }
942
943                 ret = PyList_Append(result, file);
944                 if (ret == -1) {
945                         Py_XDECREF(result);
946                         return NULL;
947                 }
948         }
949
950         return result;
951 }
952
953 static PyMethodDef py_cli_state_methods[] = {
954         { "create", (PyCFunction)py_cli_create, METH_VARARGS|METH_KEYWORDS,
955           "Open a file" },
956         { "close", (PyCFunction)py_cli_close, METH_VARARGS,
957           "Close a file handle" },
958         { "write", (PyCFunction)py_cli_write, METH_VARARGS|METH_KEYWORDS,
959           "Write to a file handle" },
960         { "read", (PyCFunction)py_cli_read, METH_VARARGS|METH_KEYWORDS,
961           "Read from a file handle" },
962         { "truncate", (PyCFunction)py_cli_ftruncate,
963           METH_VARARGS|METH_KEYWORDS,
964           "Truncate a file" },
965         { "delete_on_close", (PyCFunction)py_cli_delete_on_close,
966           METH_VARARGS|METH_KEYWORDS,
967           "Set/Reset the delete on close flag" },
968         { "readdir", (PyCFunction)py_cli_list,
969           METH_VARARGS|METH_KEYWORDS,
970           "List a directory" },
971         { "get_oplock_break", (PyCFunction)py_cli_get_oplock_break,
972           METH_VARARGS, "Wait for an oplock break" },
973         { NULL, NULL, 0, NULL }
974 };
975
976 static PyTypeObject py_cli_state_type = {
977         PyVarObject_HEAD_INIT(NULL, 0)
978         .tp_name = "libsmb_samba_internal.Conn",
979         .tp_basicsize = sizeof(struct py_cli_state),
980         .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
981         .tp_doc = "libsmb connection",
982         .tp_new = py_cli_state_new,
983         .tp_init = (initproc)py_cli_state_init,
984         .tp_dealloc = (destructor)py_cli_state_dealloc,
985         .tp_methods = py_cli_state_methods,
986 };
987
988 static PyMethodDef py_libsmb_methods[] = {
989         { NULL },
990 };
991
992 void initlibsmb_samba_internal(void);
993
994 static struct PyModuleDef moduledef = {
995     PyModuleDef_HEAD_INIT,
996     .m_name = "libsmb_samba_internal",
997     .m_doc = "libsmb wrapper",
998     .m_size = -1,
999     .m_methods = py_libsmb_methods,
1000 };
1001
1002 MODULE_INIT_FUNC(libsmb_samba_internal)
1003 {
1004         PyObject *m = NULL;
1005
1006         talloc_stackframe();
1007
1008         m = PyModule_Create(&moduledef);
1009         if (m == NULL) {
1010                 return m;
1011         }
1012         if (PyType_Ready(&py_cli_state_type) < 0) {
1013                 return NULL;
1014         }
1015         Py_INCREF(&py_cli_state_type);
1016         PyModule_AddObject(m, "Conn", (PyObject *)&py_cli_state_type);
1017         return m;
1018 }