s3:pylibsmb: .get_oplock_break API is dependent on multi_threaded=True
[nivanova/samba-autobuild/.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 SecurityFlags = 0;
670         uint16_t fnum;
671         struct tevent_req *req;
672         NTSTATUS status;
673
674         static const char *kwlist[] = {
675                 "Name", "CreateFlags", "DesiredAccess", "FileAttributes",
676                 "ShareAccess", "CreateDisposition", "CreateOptions",
677                 "SecurityFlags", NULL };
678
679         if (!ParseTupleAndKeywords(
680                     args, kwds, "s|IIIIIII", kwlist,
681                     &fname, &CreateFlags, &DesiredAccess, &FileAttributes,
682                     &ShareAccess, &CreateDisposition, &CreateOptions,
683                     &SecurityFlags)) {
684                 return NULL;
685         }
686
687         req = cli_ntcreate_send(NULL, self->ev, self->cli, fname, CreateFlags,
688                                 DesiredAccess, FileAttributes, ShareAccess,
689                                 CreateDisposition, CreateOptions,
690                                 SecurityFlags);
691         if (!py_tevent_req_wait_exc(self, req)) {
692                 return NULL;
693         }
694         status = cli_ntcreate_recv(req, &fnum, NULL);
695         TALLOC_FREE(req);
696
697         if (!NT_STATUS_IS_OK(status)) {
698                 PyErr_SetNTSTATUS(status);
699                 return NULL;
700         }
701         return Py_BuildValue("I", (unsigned)fnum);
702 }
703
704 static PyObject *py_cli_close(struct py_cli_state *self, PyObject *args)
705 {
706         struct tevent_req *req;
707         int fnum;
708         NTSTATUS status;
709
710         if (!PyArg_ParseTuple(args, "i", &fnum)) {
711                 return NULL;
712         }
713
714         req = cli_close_send(NULL, self->ev, self->cli, fnum);
715         if (!py_tevent_req_wait_exc(self, req)) {
716                 return NULL;
717         }
718         status = cli_close_recv(req);
719         TALLOC_FREE(req);
720
721         if (!NT_STATUS_IS_OK(status)) {
722                 PyErr_SetNTSTATUS(status);
723                 return NULL;
724         }
725         Py_RETURN_NONE;
726 }
727
728 static PyObject *py_cli_write(struct py_cli_state *self, PyObject *args,
729                               PyObject *kwds)
730 {
731         int fnum;
732         unsigned mode = 0;
733         char *buf;
734         Py_ssize_t buflen;
735         unsigned long long offset;
736         struct tevent_req *req;
737         NTSTATUS status;
738         size_t written;
739
740         static const char *kwlist[] = {
741                 "fnum", "buffer", "offset", "mode", NULL };
742
743         if (!ParseTupleAndKeywords(
744                     args, kwds, "Is#K|I", kwlist,
745                     &fnum, &buf, &buflen, &offset, &mode)) {
746                 return NULL;
747         }
748
749         req = cli_write_andx_send(NULL, self->ev, self->cli, fnum, mode,
750                                   (uint8_t *)buf, offset, buflen);
751         if (!py_tevent_req_wait_exc(self, req)) {
752                 return NULL;
753         }
754         status = cli_write_andx_recv(req, &written);
755         TALLOC_FREE(req);
756
757         if (!NT_STATUS_IS_OK(status)) {
758                 PyErr_SetNTSTATUS(status);
759                 return NULL;
760         }
761         return Py_BuildValue("K", (unsigned long long)written);
762 }
763
764 static PyObject *py_cli_read(struct py_cli_state *self, PyObject *args,
765                              PyObject *kwds)
766 {
767         int fnum;
768         unsigned long long offset;
769         unsigned size;
770         struct tevent_req *req;
771         NTSTATUS status;
772         uint8_t *buf;
773         ssize_t buflen;
774         PyObject *result;
775
776         static const char *kwlist[] = {
777                 "fnum", "offset", "size", NULL };
778
779         if (!ParseTupleAndKeywords(
780                     args, kwds, "IKI", kwlist, &fnum, &offset,
781                     &size)) {
782                 return NULL;
783         }
784
785         req = cli_read_andx_send(NULL, self->ev, self->cli, fnum,
786                                  offset, size);
787         if (!py_tevent_req_wait_exc(self, req)) {
788                 return NULL;
789         }
790         status = cli_read_andx_recv(req, &buflen, &buf);
791
792         if (!NT_STATUS_IS_OK(status)) {
793                 TALLOC_FREE(req);
794                 PyErr_SetNTSTATUS(status);
795                 return NULL;
796         }
797         result = Py_BuildValue("s#", (char *)buf, (int)buflen);
798         TALLOC_FREE(req);
799         return result;
800 }
801
802 static PyObject *py_cli_ftruncate(struct py_cli_state *self, PyObject *args,
803                                   PyObject *kwds)
804 {
805         int fnum;
806         unsigned long long size;
807         struct tevent_req *req;
808         NTSTATUS status;
809
810         static const char *kwlist[] = {
811                 "fnum", "size", NULL };
812
813         if (!ParseTupleAndKeywords(
814                     args, kwds, "IK", kwlist, &fnum, &size)) {
815                 return NULL;
816         }
817
818         req = cli_ftruncate_send(NULL, self->ev, self->cli, fnum, size);
819         if (!py_tevent_req_wait_exc(self, req)) {
820                 return NULL;
821         }
822         status = cli_ftruncate_recv(req);
823         TALLOC_FREE(req);
824
825         if (!NT_STATUS_IS_OK(status)) {
826                 PyErr_SetNTSTATUS(status);
827                 return NULL;
828         }
829         Py_RETURN_NONE;
830 }
831
832 static PyObject *py_cli_delete_on_close(struct py_cli_state *self,
833                                         PyObject *args,
834                                         PyObject *kwds)
835 {
836         unsigned fnum, flag;
837         struct tevent_req *req;
838         NTSTATUS status;
839
840         static const char *kwlist[] = {
841                 "fnum", "flag", NULL };
842
843         if (!ParseTupleAndKeywords(
844                     args, kwds, "II", kwlist, &fnum, &flag)) {
845                 return NULL;
846         }
847
848         req = cli_nt_delete_on_close_send(NULL, self->ev, self->cli, fnum,
849                                           flag);
850         if (!py_tevent_req_wait_exc(self, req)) {
851                 return NULL;
852         }
853         status = cli_nt_delete_on_close_recv(req);
854         TALLOC_FREE(req);
855
856         if (!NT_STATUS_IS_OK(status)) {
857                 PyErr_SetNTSTATUS(status);
858                 return NULL;
859         }
860         Py_RETURN_NONE;
861 }
862
863 static PyObject *py_cli_list(struct py_cli_state *self,
864                              PyObject *args,
865                              PyObject *kwds)
866 {
867         char *mask;
868         unsigned attribute =
869                 FILE_ATTRIBUTE_DIRECTORY |
870                 FILE_ATTRIBUTE_SYSTEM |
871                 FILE_ATTRIBUTE_HIDDEN;
872         unsigned info_level = SMB_FIND_FILE_BOTH_DIRECTORY_INFO;
873         struct tevent_req *req;
874         NTSTATUS status;
875         struct file_info *finfos;
876         size_t i, num_finfos;
877         PyObject *result;
878
879         const char *kwlist[] = {
880                 "mask", "attribute", "info_level", NULL
881         };
882
883         if (!ParseTupleAndKeywords(
884                     args, kwds, "s|II", kwlist,
885                     &mask, &attribute, &info_level)) {
886                 return NULL;
887         }
888
889         req = cli_list_send(NULL, self->ev, self->cli, mask, attribute,
890                             info_level);
891         if (!py_tevent_req_wait_exc(self, req)) {
892                 return NULL;
893         }
894         status = cli_list_recv(req, NULL, &finfos, &num_finfos);
895         TALLOC_FREE(req);
896
897         if (!NT_STATUS_IS_OK(status)) {
898                 PyErr_SetNTSTATUS(status);
899                 return NULL;
900         }
901
902         result = Py_BuildValue("[]");
903         if (result == NULL) {
904                 return NULL;
905         }
906
907         for (i=0; i<num_finfos; i++) {
908                 struct file_info *finfo = &finfos[i];
909                 PyObject *file;
910                 int ret;
911
912                 file = Py_BuildValue(
913                         "{s:s,s:i}",
914                         "name", finfo->name,
915                         "mode", (int)finfo->mode);
916                 if (file == NULL) {
917                         Py_XDECREF(result);
918                         return NULL;
919                 }
920
921                 ret = PyList_Append(result, file);
922                 if (ret == -1) {
923                         Py_XDECREF(result);
924                         return NULL;
925                 }
926         }
927
928         return result;
929 }
930
931 static PyMethodDef py_cli_state_methods[] = {
932         { "create", (PyCFunction)py_cli_create, METH_VARARGS|METH_KEYWORDS,
933           "Open a file" },
934         { "close", (PyCFunction)py_cli_close, METH_VARARGS,
935           "Close a file handle" },
936         { "write", (PyCFunction)py_cli_write, METH_VARARGS|METH_KEYWORDS,
937           "Write to a file handle" },
938         { "read", (PyCFunction)py_cli_read, METH_VARARGS|METH_KEYWORDS,
939           "Read from a file handle" },
940         { "truncate", (PyCFunction)py_cli_ftruncate,
941           METH_VARARGS|METH_KEYWORDS,
942           "Truncate a file" },
943         { "delete_on_close", (PyCFunction)py_cli_delete_on_close,
944           METH_VARARGS|METH_KEYWORDS,
945           "Set/Reset the delete on close flag" },
946         { "readdir", (PyCFunction)py_cli_list,
947           METH_VARARGS|METH_KEYWORDS,
948           "List a directory" },
949         { "get_oplock_break", (PyCFunction)py_cli_get_oplock_break,
950           METH_VARARGS, "Wait for an oplock break" },
951         { NULL, NULL, 0, NULL }
952 };
953
954 static PyTypeObject py_cli_state_type = {
955         PyVarObject_HEAD_INIT(NULL, 0)
956         .tp_name = "libsmb_samba_internal.Conn",
957         .tp_basicsize = sizeof(struct py_cli_state),
958         .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
959         .tp_doc = "libsmb connection",
960         .tp_new = py_cli_state_new,
961         .tp_init = (initproc)py_cli_state_init,
962         .tp_dealloc = (destructor)py_cli_state_dealloc,
963         .tp_methods = py_cli_state_methods,
964 };
965
966 static PyMethodDef py_libsmb_methods[] = {
967         { NULL },
968 };
969
970 void initlibsmb_samba_internal(void);
971
972 static struct PyModuleDef moduledef = {
973     PyModuleDef_HEAD_INIT,
974     .m_name = "libsmb_samba_internal",
975     .m_doc = "libsmb wrapper",
976     .m_size = -1,
977     .m_methods = py_libsmb_methods,
978 };
979
980 MODULE_INIT_FUNC(libsmb_samba_internal)
981 {
982         PyObject *m = NULL;
983
984         talloc_stackframe();
985
986         m = PyModule_Create(&moduledef);
987         if (m == NULL) {
988                 return m;
989         }
990         if (PyType_Ready(&py_cli_state_type) < 0) {
991                 return NULL;
992         }
993         Py_INCREF(&py_cli_state_type);
994         PyModule_AddObject(m, "Conn", (PyObject *)&py_cli_state_type);
995         return m;
996 }