Revert "smbd: add simple noop smbd_impersonate_{conn_vuid,conn_sess,root,guest}_creat...
[Samba.git] / source4 / libcli / pysmb.c
blob45ff9a0a7c02be6148ce84a2470353775d4e1548
1 /*
2 Unix SMB/CIFS implementation.
4 Copyright (C) Amitay Isaacs 2011
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
20 #include <Python.h>
21 #include "python/py3compat.h"
22 #include <tevent.h>
23 #include <pytalloc.h>
24 #include "includes.h"
25 #include "param/param.h"
26 #include "param/pyparam.h"
27 #include "system/dir.h"
28 #include "system/filesys.h"
29 #include "lib/events/events.h"
30 #include "auth/credentials/credentials.h"
31 #include "auth/credentials/pycredentials.h"
32 #include "auth/gensec/gensec.h"
33 #include "libcli/libcli.h"
34 #include "libcli/raw/libcliraw.h"
35 #include "libcli/raw/raw_proto.h"
36 #include "libcli/resolve/resolve.h"
37 #include "libcli/util/pyerrors.h"
38 #include "libcli/smb_composite/smb_composite.h"
39 #include "libcli/security/security_descriptor.h"
40 #include "librpc/rpc/pyrpc_util.h"
42 static PyTypeObject PySMB;
44 void initsmb(void);
46 struct smb_private_data {
47 struct loadparm_context *lp_ctx;
48 struct cli_credentials *creds;
49 struct tevent_context *ev_ctx;
50 struct smbcli_tree *tree;
53 static void dos_format(char *s)
55 string_replace(s, '/', '\\');
60 * Connect to SMB share using smb_full_connection
62 static NTSTATUS do_smb_connect(TALLOC_CTX *mem_ctx, struct smb_private_data *spdata,
63 const char *hostname, const char *service,
64 struct smbcli_options *options,
65 struct smbcli_session_options *session_options,
66 struct smbcli_tree **tree)
68 struct smbcli_state *smb_state;
69 NTSTATUS status;
71 *tree = NULL;
73 gensec_init();
75 smb_state = smbcli_state_init(mem_ctx);
77 status = smbcli_full_connection(mem_ctx, &smb_state, hostname,
78 lpcfg_smb_ports(spdata->lp_ctx),
79 service,
80 NULL,
81 lpcfg_socket_options(spdata->lp_ctx),
82 spdata->creds,
83 lpcfg_resolve_context(spdata->lp_ctx),
84 spdata->ev_ctx,
85 options,
86 session_options,
87 lpcfg_gensec_settings(mem_ctx, spdata->lp_ctx));
89 if (NT_STATUS_IS_OK(status)) {
90 *tree = smb_state->tree;
93 return status;
98 * Read SMB file and return the contents of the file as python string
100 static PyObject * py_smb_loadfile(PyObject *self, PyObject *args)
102 struct smb_composite_loadfile io;
103 const char *filename;
104 NTSTATUS status;
105 struct smb_private_data *spdata;
107 if (!PyArg_ParseTuple(args, "s:loadfile", &filename)) {
108 return NULL;
111 ZERO_STRUCT(io);
113 io.in.fname = filename;
115 spdata = pytalloc_get_ptr(self);
116 status = smb_composite_loadfile(spdata->tree, pytalloc_get_mem_ctx(self), &io);
117 PyErr_NTSTATUS_IS_ERR_RAISE(status);
119 return Py_BuildValue(PYARG_BYTES_LEN, io.out.data, io.out.size);
123 * Create a SMB file with given string as the contents
125 static PyObject * py_smb_savefile(PyObject *self, PyObject *args)
127 struct smb_composite_savefile io;
128 const char *filename;
129 char *data = NULL;
130 Py_ssize_t size = 0;
131 NTSTATUS status;
132 struct smb_private_data *spdata;
134 if (!PyArg_ParseTuple(args, "s"PYARG_BYTES_LEN":savefile", &filename, &data, &size )) {
135 return NULL;
138 io.in.fname = filename;
139 io.in.data = (unsigned char *)data;
140 io.in.size = size;
142 spdata = pytalloc_get_ptr(self);
143 status = smb_composite_savefile(spdata->tree, &io);
144 PyErr_NTSTATUS_IS_ERR_RAISE(status);
146 Py_RETURN_NONE;
150 * Callback function to accumulate directory contents in a python list
152 static void py_smb_list_callback(struct clilist_file_info *f, const char *mask, void *state)
154 PyObject *py_dirlist;
155 PyObject *dict;
157 if(!ISDOT(f->name) && !ISDOTDOT(f->name)) {
158 py_dirlist = (PyObject *)state;
160 dict = PyDict_New();
161 if(dict) {
162 PyDict_SetItemString(dict, "name", PyStr_FromString(f->name));
164 /* Windows does not always return short_name */
165 if (f->short_name) {
166 PyDict_SetItemString(dict, "short_name", PyStr_FromString(f->short_name));
167 } else {
168 PyDict_SetItemString(dict, "short_name", Py_None);
171 PyDict_SetItemString(dict, "size", PyLong_FromUnsignedLongLong(f->size));
172 PyDict_SetItemString(dict, "attrib", PyInt_FromLong(f->attrib));
173 PyDict_SetItemString(dict, "mtime", PyInt_FromLong(f->mtime));
175 PyList_Append(py_dirlist, dict);
181 * List the directory contents for specified directory (Ignore '.' and '..' dirs)
183 static PyObject *py_smb_list(PyObject *self, PyObject *args, PyObject *kwargs)
185 struct smb_private_data *spdata;
186 PyObject *py_dirlist;
187 const char *kwnames[] = { "directory", "mask", "attribs", NULL };
188 char *base_dir;
189 char *user_mask = NULL;
190 char *mask;
191 uint16_t attribute = FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_DIRECTORY
192 | FILE_ATTRIBUTE_ARCHIVE;
194 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "z|sH:list",
195 discard_const_p(char *, kwnames),
196 &base_dir, &user_mask, &attribute)) {
197 return NULL;
200 if (user_mask == NULL) {
201 mask = talloc_asprintf(pytalloc_get_mem_ctx(self), "%s\\*", base_dir);
202 } else {
203 mask = talloc_asprintf(pytalloc_get_mem_ctx(self), "%s\\%s", base_dir, user_mask);
205 dos_format(mask);
207 spdata = pytalloc_get_ptr(self);
209 if((py_dirlist = PyList_New(0)) == NULL) {
210 PyErr_NoMemory();
211 return NULL;
214 smbcli_list(spdata->tree, mask, attribute, py_smb_list_callback, (void *)py_dirlist);
216 talloc_free(mask);
218 return py_dirlist;
222 * Create a directory
224 static PyObject *py_smb_mkdir(PyObject *self, PyObject *args)
226 NTSTATUS status;
227 const char *dirname;
228 struct smb_private_data *spdata;
230 if (!PyArg_ParseTuple(args, "s:mkdir", &dirname)) {
231 return NULL;
234 spdata = pytalloc_get_ptr(self);
235 status = smbcli_mkdir(spdata->tree, dirname);
236 PyErr_NTSTATUS_IS_ERR_RAISE(status);
238 Py_RETURN_NONE;
242 * Remove a directory
244 static PyObject *py_smb_rmdir(PyObject *self, PyObject *args)
246 NTSTATUS status;
247 const char *dirname;
248 struct smb_private_data *spdata;
250 if (!PyArg_ParseTuple(args, "s:rmdir", &dirname)) {
251 return NULL;
254 spdata = pytalloc_get_ptr(self);
255 status = smbcli_rmdir(spdata->tree, dirname);
256 PyErr_NTSTATUS_IS_ERR_RAISE(status);
258 Py_RETURN_NONE;
263 * Remove a file
265 static PyObject *py_smb_unlink(PyObject *self, PyObject *args)
267 NTSTATUS status;
268 const char *filename;
269 struct smb_private_data *spdata;
271 if (!PyArg_ParseTuple(args, "s:unlink", &filename)) {
272 return NULL;
275 spdata = pytalloc_get_ptr(self);
276 status = smbcli_unlink(spdata->tree, filename);
277 PyErr_NTSTATUS_IS_ERR_RAISE(status);
279 Py_RETURN_NONE;
283 * Remove a directory and all its contents
285 static PyObject *py_smb_deltree(PyObject *self, PyObject *args)
287 int status;
288 const char *dirname;
289 struct smb_private_data *spdata;
291 if (!PyArg_ParseTuple(args, "s:deltree", &dirname)) {
292 return NULL;
295 spdata = pytalloc_get_ptr(self);
296 status = smbcli_deltree(spdata->tree, dirname);
297 if (status <= 0) {
298 return NULL;
301 Py_RETURN_NONE;
305 * Check existence of a path
307 static PyObject *py_smb_chkpath(PyObject *self, PyObject *args)
309 NTSTATUS status;
310 const char *path;
311 struct smb_private_data *spdata;
313 if (!PyArg_ParseTuple(args, "s:chkpath", &path)) {
314 return NULL;
317 spdata = pytalloc_get_ptr(self);
318 status = smbcli_chkpath(spdata->tree, path);
320 if (NT_STATUS_IS_OK(status)) {
321 Py_RETURN_TRUE;
324 Py_RETURN_FALSE;
328 * Read ACL on a given file/directory as a security descriptor object
330 static PyObject *py_smb_getacl(PyObject *self, PyObject *args, PyObject *kwargs)
332 NTSTATUS status;
333 union smb_open io;
334 union smb_fileinfo fio;
335 struct smb_private_data *spdata;
336 const char *filename;
337 uint32_t sinfo = 0;
338 int access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
339 int fnum;
341 if (!PyArg_ParseTuple(args, "s|Ii:get_acl", &filename, &sinfo, &access_mask)) {
342 return NULL;
345 ZERO_STRUCT(io);
347 spdata = pytalloc_get_ptr(self);
349 io.generic.level = RAW_OPEN_NTCREATEX;
350 io.ntcreatex.in.root_fid.fnum = 0;
351 io.ntcreatex.in.flags = 0;
352 io.ntcreatex.in.access_mask = access_mask;
353 io.ntcreatex.in.create_options = 0;
354 io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
355 io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
356 NTCREATEX_SHARE_ACCESS_WRITE;
357 io.ntcreatex.in.alloc_size = 0;
358 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
359 io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
360 io.ntcreatex.in.security_flags = 0;
361 io.ntcreatex.in.fname = filename;
363 status = smb_raw_open(spdata->tree, pytalloc_get_mem_ctx(self), &io);
364 PyErr_NTSTATUS_IS_ERR_RAISE(status);
366 fnum = io.ntcreatex.out.file.fnum;
368 ZERO_STRUCT(fio);
370 fio.query_secdesc.level = RAW_FILEINFO_SEC_DESC;
371 fio.query_secdesc.in.file.fnum = fnum;
372 if (sinfo)
373 fio.query_secdesc.in.secinfo_flags = sinfo;
374 else
375 fio.query_secdesc.in.secinfo_flags = SECINFO_OWNER |
376 SECINFO_GROUP |
377 SECINFO_DACL |
378 SECINFO_PROTECTED_DACL |
379 SECINFO_UNPROTECTED_DACL |
380 SECINFO_SACL |
381 SECINFO_PROTECTED_SACL |
382 SECINFO_UNPROTECTED_SACL;
384 status = smb_raw_query_secdesc(spdata->tree, pytalloc_get_mem_ctx(self), &fio);
385 smbcli_close(spdata->tree, fnum);
387 PyErr_NTSTATUS_IS_ERR_RAISE(status);
389 return py_return_ndr_struct("samba.dcerpc.security", "descriptor",
390 pytalloc_get_mem_ctx(self), fio.query_secdesc.out.sd);
394 * Set ACL on file/directory using given security descriptor object
396 static PyObject *py_smb_setacl(PyObject *self, PyObject *args, PyObject *kwargs)
398 NTSTATUS status;
399 union smb_open io;
400 union smb_setfileinfo fio;
401 struct smb_private_data *spdata;
402 const char *filename;
403 PyObject *py_sd;
404 struct security_descriptor *sd;
405 uint32_t sinfo = 0;
406 int fnum;
408 if (!PyArg_ParseTuple(args, "sO|I:get_acl", &filename, &py_sd, &sinfo)) {
409 return NULL;
412 spdata = pytalloc_get_ptr(self);
414 sd = pytalloc_get_type(py_sd, struct security_descriptor);
415 if (!sd) {
416 PyErr_Format(PyExc_TypeError,
417 "Expected dcerpc.security.descriptor as argument, got %s",
418 talloc_get_name(pytalloc_get_ptr(py_sd)));
419 return NULL;
422 ZERO_STRUCT(io);
424 spdata = pytalloc_get_ptr(self);
426 io.generic.level = RAW_OPEN_NTCREATEX;
427 io.ntcreatex.in.root_fid.fnum = 0;
428 io.ntcreatex.in.flags = 0;
429 io.ntcreatex.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
430 io.ntcreatex.in.create_options = 0;
431 io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
432 io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
433 NTCREATEX_SHARE_ACCESS_WRITE;
434 io.ntcreatex.in.alloc_size = 0;
435 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
436 io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
437 io.ntcreatex.in.security_flags = 0;
438 io.ntcreatex.in.fname = filename;
440 status = smb_raw_open(spdata->tree, pytalloc_get_mem_ctx(self), &io);
441 PyErr_NTSTATUS_IS_ERR_RAISE(status);
443 fnum = io.ntcreatex.out.file.fnum;
445 ZERO_STRUCT(fio);
447 fio.set_secdesc.level = RAW_SFILEINFO_SEC_DESC;
448 fio.set_secdesc.in.file.fnum = fnum;
449 if (sinfo)
450 fio.set_secdesc.in.secinfo_flags = sinfo;
451 else
452 fio.set_secdesc.in.secinfo_flags = SECINFO_OWNER |
453 SECINFO_GROUP |
454 SECINFO_DACL |
455 SECINFO_PROTECTED_DACL |
456 SECINFO_UNPROTECTED_DACL |
457 SECINFO_SACL |
458 SECINFO_PROTECTED_SACL |
459 SECINFO_UNPROTECTED_SACL;
461 fio.set_secdesc.in.sd = sd;
463 status = smb_raw_set_secdesc(spdata->tree, &fio);
464 smbcli_close(spdata->tree, fnum);
466 PyErr_NTSTATUS_IS_ERR_RAISE(status);
468 Py_RETURN_NONE;
472 * Open the file with the parameters passed in and return an object if OK
474 static PyObject *py_open_file(PyObject *self, PyObject *args, PyObject *kwargs)
476 NTSTATUS status;
477 union smb_open io;
478 struct smb_private_data *spdata;
479 const char *filename;
480 uint32_t access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
481 uint32_t share_access = NTCREATEX_SHARE_ACCESS_READ |
482 NTCREATEX_SHARE_ACCESS_WRITE;
483 uint32_t open_disposition = NTCREATEX_DISP_OPEN;
484 uint32_t create_options = 0;
485 TALLOC_CTX *mem_ctx;
486 int fnum;
488 if (!PyArg_ParseTuple(args, "s|iiii:open_file",
489 &filename,
490 &access_mask,
491 &share_access,
492 &open_disposition,
493 &create_options)) {
494 return NULL;
497 ZERO_STRUCT(io);
499 spdata = pytalloc_get_ptr(self);
501 mem_ctx = talloc_new(NULL);
503 io.generic.level = RAW_OPEN_NTCREATEX;
504 io.ntcreatex.in.root_fid.fnum = 0;
505 io.ntcreatex.in.flags = 0;
506 io.ntcreatex.in.access_mask = access_mask;
507 io.ntcreatex.in.create_options = create_options;
508 io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
509 io.ntcreatex.in.share_access = share_access;
510 io.ntcreatex.in.alloc_size = 0;
511 io.ntcreatex.in.open_disposition = open_disposition;
512 io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
513 io.ntcreatex.in.security_flags = 0;
514 io.ntcreatex.in.fname = filename;
516 status = smb_raw_open(spdata->tree, mem_ctx, &io);
517 talloc_free(mem_ctx);
519 PyErr_NTSTATUS_IS_ERR_RAISE(status);
521 fnum = io.ntcreatex.out.file.fnum;
523 return Py_BuildValue("i", fnum);
527 * Close the file based on the fnum passed in
529 static PyObject *py_close_file(PyObject *self, PyObject *args, PyObject *kwargs)
531 struct smb_private_data *spdata;
532 int fnum;
534 if (!PyArg_ParseTuple(args, "i:close_file", &fnum)) {
535 return NULL;
538 spdata = pytalloc_get_ptr(self);
541 * Should check the status ...
543 smbcli_close(spdata->tree, fnum);
545 Py_RETURN_NONE;
548 static PyMethodDef py_smb_methods[] = {
549 { "loadfile", py_smb_loadfile, METH_VARARGS,
550 "loadfile(path) -> file contents as a "
551 PY_DESC_PY3_BYTES
552 "\n\n Read contents of a file." },
553 { "savefile", py_smb_savefile, METH_VARARGS,
554 "savefile(path, str) -> None\n\n Write "
555 PY_DESC_PY3_BYTES
556 " str to file." },
557 { "list", (PyCFunction)py_smb_list, METH_VARARGS|METH_KEYWORDS,
558 "list(path, access_mask='*', attribs=DEFAULT_ATTRS) -> \
559 directory contents as a dictionary\n \
560 DEFAULT_ATTRS: FILE_ATTRIBUTE_SYSTEM | \
561 FILE_ATTRIBUTE_DIRECTORY | \
562 FILE_ATTRIBUTE_ARCHIVE\n\n \
563 List contents of a directory. The keys are, \n \
564 \tname: Long name of the directory item\n \
565 \tshort_name: Short name of the directory item\n \
566 \tsize: File size in bytes\n \
567 \tattrib: Attributes\n \
568 \tmtime: Modification time\n" },
569 { "mkdir", py_smb_mkdir, METH_VARARGS,
570 "mkdir(path) -> None\n\n \
571 Create a directory." },
572 { "rmdir", py_smb_rmdir, METH_VARARGS,
573 "rmdir(path) -> None\n\n \
574 Delete a directory." },
575 { "unlink", py_smb_unlink, METH_VARARGS,
576 "unlink(path) -> None\n\n \
577 Delete a file." },
578 { "deltree", py_smb_deltree, METH_VARARGS,
579 "deltree(path) -> None\n\n \
580 Delete a directory and all its contents." },
581 { "chkpath", py_smb_chkpath, METH_VARARGS,
582 "chkpath(path) -> True or False\n\n \
583 Return true if path exists, false otherwise." },
584 { "get_acl", (PyCFunction)py_smb_getacl, METH_VARARGS,
585 "get_acl(path[, security_info=0]) -> security_descriptor object\n\n \
586 Get security descriptor for file." },
587 { "set_acl", (PyCFunction)py_smb_setacl, METH_VARARGS,
588 "set_acl(path, security_descriptor[, security_info=0]) -> None\n\n \
589 Set security descriptor for file." },
590 { "open_file", (PyCFunction)py_open_file, METH_VARARGS,
591 "open_file(path, access_mask[, share_access[, open_disposition[, create_options]]] -> fnum\n\n \
592 Open a file. Throws NTSTATUS exceptions on errors." },
593 { "close_file", (PyCFunction)py_close_file, METH_VARARGS,
594 "close_file(fnum) -> None\n\n \
595 Close the file based on fnum."},
596 { NULL },
599 static PyObject *py_smb_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
601 PyObject *py_creds = Py_None;
602 PyObject *py_lp = Py_None;
603 const char *kwnames[] = { "hostname", "service", "creds", "lp",
604 "ntlmv2_auth", "use_spnego", "sign", NULL };
605 const char *hostname = NULL;
606 const char *service = NULL;
607 PyObject *smb;
608 struct smb_private_data *spdata;
609 NTSTATUS status;
610 TALLOC_CTX *frame = NULL;
611 struct smbcli_options options;
612 struct smbcli_session_options session_options;
613 uint8_t ntlmv2_auth = 0xFF;
614 uint8_t use_spnego = 0xFF;
615 PyObject *sign = Py_False;
617 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "zz|OObbO",
618 discard_const_p(char *, kwnames),
619 &hostname, &service, &py_creds, &py_lp,
620 &ntlmv2_auth, &use_spnego, &sign)) {
621 return NULL;
624 frame = talloc_stackframe();
626 spdata = talloc_zero(frame, struct smb_private_data);
627 if (spdata == NULL) {
628 PyErr_NoMemory();
629 TALLOC_FREE(frame);
630 return NULL;
633 spdata->lp_ctx = lpcfg_from_py_object(spdata, py_lp);
634 if (spdata->lp_ctx == NULL) {
635 PyErr_SetString(PyExc_TypeError, "Expected loadparm context");
636 TALLOC_FREE(frame);
637 return NULL;
640 spdata->creds = cli_credentials_from_py_object(py_creds);
641 if (spdata->creds == NULL) {
642 PyErr_SetString(PyExc_TypeError, "Expected credentials");
643 TALLOC_FREE(frame);
644 return NULL;
646 spdata->ev_ctx = s4_event_context_init(spdata);
647 if (spdata->ev_ctx == NULL) {
648 PyErr_NoMemory();
649 TALLOC_FREE(frame);
650 return NULL;
653 lpcfg_smbcli_options(spdata->lp_ctx, &options);
654 lpcfg_smbcli_session_options(spdata->lp_ctx, &session_options);
656 if (ntlmv2_auth != 0xFF) {
657 session_options.ntlmv2_auth = ntlmv2_auth;
659 if (use_spnego != 0xFF) {
660 options.use_spnego = use_spnego;
662 if (PyObject_IsTrue(sign)) {
663 options.signing = SMB_SIGNING_REQUIRED;
666 status = do_smb_connect(spdata, spdata, hostname, service,
667 &options,
668 &session_options,
669 &spdata->tree);
670 PyErr_NTSTATUS_IS_ERR_RAISE(status);
671 if (spdata->tree == NULL) {
672 TALLOC_FREE(frame);
673 return NULL;
676 smb = pytalloc_steal(type, spdata);
677 TALLOC_FREE(frame);
678 return smb;
681 static PyTypeObject PySMB = {
682 .tp_name = "smb.SMB",
683 .tp_new = py_smb_new,
684 .tp_flags = Py_TPFLAGS_DEFAULT,
685 .tp_methods = py_smb_methods,
686 .tp_doc = "SMB(hostname, service[, creds[, lp]]) -> SMB connection object\n",
690 static struct PyModuleDef moduledef = {
691 PyModuleDef_HEAD_INIT,
692 .m_name = "smb",
693 .m_doc = "SMB File I/O support",
694 .m_size = -1,
695 .m_methods = NULL,
698 void initsmb(void);
700 MODULE_INIT_FUNC(smb)
702 PyObject *m = NULL;
704 if (pytalloc_BaseObject_PyType_Ready(&PySMB) < 0) {
705 return m;
708 m = PyModule_Create(&moduledef);
709 if (m == NULL) {
710 return m;
713 Py_INCREF(&PySMB);
714 PyModule_AddObject(m, "SMB", (PyObject *)&PySMB);
716 #define ADD_FLAGS(val) PyModule_AddObject(m, #val, PyInt_FromLong(val))
718 ADD_FLAGS(FILE_ATTRIBUTE_READONLY);
719 ADD_FLAGS(FILE_ATTRIBUTE_HIDDEN);
720 ADD_FLAGS(FILE_ATTRIBUTE_SYSTEM);
721 ADD_FLAGS(FILE_ATTRIBUTE_VOLUME);
722 ADD_FLAGS(FILE_ATTRIBUTE_DIRECTORY);
723 ADD_FLAGS(FILE_ATTRIBUTE_ARCHIVE);
724 ADD_FLAGS(FILE_ATTRIBUTE_DEVICE);
725 ADD_FLAGS(FILE_ATTRIBUTE_NORMAL);
726 ADD_FLAGS(FILE_ATTRIBUTE_TEMPORARY);
727 ADD_FLAGS(FILE_ATTRIBUTE_SPARSE);
728 ADD_FLAGS(FILE_ATTRIBUTE_REPARSE_POINT);
729 ADD_FLAGS(FILE_ATTRIBUTE_COMPRESSED);
730 ADD_FLAGS(FILE_ATTRIBUTE_OFFLINE);
731 ADD_FLAGS(FILE_ATTRIBUTE_NONINDEXED);
732 ADD_FLAGS(FILE_ATTRIBUTE_ENCRYPTED);
733 ADD_FLAGS(FILE_ATTRIBUTE_ALL_MASK);
734 return m;