samba:python - Py_RETURN_NONE remove compatibility code for releases < 2.4
[amitay/samba.git] / source4 / libcli / pysmb.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    Copyright (C) Amitay Isaacs 2011
5
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.
10
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.
15
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/>.
18 */
19
20 #include <Python.h>
21 #include <tevent.h>
22 #include <pytalloc.h>
23 #include "includes.h"
24 #include "param/param.h"
25 #include "param/pyparam.h"
26 #include "system/dir.h"
27 #include "system/filesys.h"
28 #include "lib/events/events.h"
29 #include "auth/credentials/credentials.h"
30 #include "auth/credentials/pycredentials.h"
31 #include "auth/gensec/gensec.h"
32 #include "libcli/libcli.h"
33 #include "libcli/raw/libcliraw.h"
34 #include "libcli/raw/raw_proto.h"
35 #include "libcli/resolve/resolve.h"
36 #include "libcli/util/pyerrors.h"
37 #include "libcli/smb_composite/smb_composite.h"
38 #include "libcli/security/security_descriptor.h"
39 #include "librpc/rpc/pyrpc_util.h"
40
41 staticforward PyTypeObject PySMB;
42
43 void initsmb(void);
44
45 struct smb_private_data {
46         struct loadparm_context *lp_ctx;
47         struct cli_credentials *creds;
48         struct tevent_context *ev_ctx;
49         struct smbcli_tree *tree;
50 };
51
52 static void dos_format(char *s)
53 {
54         string_replace(s, '/', '\\');
55 }
56
57
58 /*
59  * Connect to SMB share using smb_full_connection
60  */
61 static NTSTATUS do_smb_connect(TALLOC_CTX *mem_ctx, struct smb_private_data *spdata,
62                         const char *hostname, const char *service, struct smbcli_tree **tree)
63 {
64         struct smbcli_state *smb_state;
65         NTSTATUS status;
66         struct smbcli_options options;
67         struct smbcli_session_options session_options;
68
69         *tree = NULL;
70
71         gensec_init();
72
73         smb_state = smbcli_state_init(mem_ctx);
74
75         lpcfg_smbcli_options(spdata->lp_ctx, &options);
76         lpcfg_smbcli_session_options(spdata->lp_ctx, &session_options);
77
78         status = smbcli_full_connection(mem_ctx, &smb_state, hostname, 
79                                         lpcfg_smb_ports(spdata->lp_ctx),
80                                         service, 
81                                         NULL,
82                                         lpcfg_socket_options(spdata->lp_ctx),
83                                         spdata->creds,
84                                         lpcfg_resolve_context(spdata->lp_ctx),
85                                         spdata->ev_ctx,
86                                         &options,
87                                         &session_options,
88                                         lpcfg_gensec_settings(mem_ctx, spdata->lp_ctx));
89
90         if (NT_STATUS_IS_OK(status)) {
91                 *tree = smb_state->tree;
92         }
93
94         return status;
95 }
96
97
98 /*
99  * Read SMB file and return the contents of the file as python string
100  */
101 static PyObject * py_smb_loadfile(pytalloc_Object *self, PyObject *args)
102 {
103         struct smb_composite_loadfile io;
104         const char *filename;
105         NTSTATUS status;
106         struct smb_private_data *spdata;
107
108         if (!PyArg_ParseTuple(args, "s:loadfile", &filename)) {
109                 return NULL;
110         }
111
112         ZERO_STRUCT(io);
113
114         io.in.fname = filename;
115
116         spdata = self->ptr;
117         status = smb_composite_loadfile(spdata->tree, self->talloc_ctx, &io);
118         PyErr_NTSTATUS_IS_ERR_RAISE(status);
119
120         return Py_BuildValue("s#", io.out.data, io.out.size);
121 }
122
123 /*
124  * Create a SMB file with given string as the contents
125  */
126 static PyObject * py_smb_savefile(pytalloc_Object *self, PyObject *args)
127 {
128         struct smb_composite_savefile io;
129         const char *filename;
130         char *data;
131         NTSTATUS status;
132         struct smb_private_data *spdata;
133
134         if (!PyArg_ParseTuple(args, "ss:savefile", &filename, &data)) {
135                 return NULL;
136         }
137
138         io.in.fname = filename;
139         io.in.data = (unsigned char *)data;
140         io.in.size = strlen(data);
141
142         spdata = self->ptr;
143         status = smb_composite_savefile(spdata->tree, &io);
144         PyErr_NTSTATUS_IS_ERR_RAISE(status);
145
146         Py_RETURN_NONE;
147 }
148
149 /*
150  * Callback function to accumulate directory contents in a python list
151  */
152 static void py_smb_list_callback(struct clilist_file_info *f, const char *mask, void *state)
153 {
154         PyObject *py_dirlist;
155         PyObject *dict;
156
157         if(!ISDOT(f->name) && !ISDOTDOT(f->name)) {
158                 py_dirlist = (PyObject *)state;
159
160                 dict = PyDict_New();
161                 if(dict) {
162                         PyDict_SetItemString(dict, "name", PyString_FromString(f->name));
163                         
164                         /* Windows does not always return short_name */
165                         if (f->short_name) {
166                                 PyDict_SetItemString(dict, "short_name", PyString_FromString(f->short_name));
167                         } else {
168                                 PyDict_SetItemString(dict, "short_name", Py_None);
169                         }
170
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));
174
175                         PyList_Append(py_dirlist, dict);
176                 }
177         }
178 }
179
180 /*
181  * List the directory contents for specified directory (Ignore '.' and '..' dirs)
182  */
183 static PyObject *py_smb_list(pytalloc_Object *self, PyObject *args, PyObject *kwargs)
184 {
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;
193
194         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "z|sH:list",
195                                         discard_const_p(char *, kwnames),
196                                         &base_dir, &user_mask, &attribute)) {
197                 return NULL;
198         }
199
200         if (user_mask == NULL) {
201                 mask = talloc_asprintf(self->talloc_ctx, "%s\\*", base_dir);
202         } else {
203                 mask = talloc_asprintf(self->talloc_ctx, "%s\\%s", base_dir, user_mask);
204         }
205         dos_format(mask);
206
207         spdata = self->ptr;
208
209         if((py_dirlist = PyList_New(0)) == NULL) {
210                 PyErr_NoMemory();
211                 return NULL;
212         }
213
214         smbcli_list(spdata->tree, mask, attribute, py_smb_list_callback, (void *)py_dirlist);
215
216         talloc_free(mask);
217
218         return py_dirlist;
219 }
220
221 /*
222  * Create a directory
223  */
224 static PyObject *py_smb_mkdir(pytalloc_Object *self, PyObject *args)
225 {
226         NTSTATUS status;
227         const char *dirname;
228         struct smb_private_data *spdata;
229
230         if (!PyArg_ParseTuple(args, "s:mkdir", &dirname)) {
231                 return NULL;
232         }
233
234         spdata = self->ptr;     
235         status = smbcli_mkdir(spdata->tree, dirname);
236         PyErr_NTSTATUS_IS_ERR_RAISE(status);
237
238         Py_RETURN_NONE;
239 }
240
241 /*
242  * Remove a directory
243  */
244 static PyObject *py_smb_rmdir(pytalloc_Object *self, PyObject *args)
245 {
246         NTSTATUS status;
247         const char *dirname;
248         struct smb_private_data *spdata;
249
250         if (!PyArg_ParseTuple(args, "s:rmdir", &dirname)) {
251                 return NULL;
252         }
253
254         spdata = self->ptr;     
255         status = smbcli_rmdir(spdata->tree, dirname);
256         PyErr_NTSTATUS_IS_ERR_RAISE(status);
257
258         Py_RETURN_NONE;
259 }
260
261 /*
262  * Remove a directory and all its contents
263  */
264 static PyObject *py_smb_deltree(pytalloc_Object *self, PyObject *args)
265 {
266         int status;
267         const char *dirname;
268         struct smb_private_data *spdata;
269
270         if (!PyArg_ParseTuple(args, "s:deltree", &dirname)) {
271                 return NULL;
272         }
273
274         spdata = self->ptr;
275         status = smbcli_deltree(spdata->tree, dirname);
276         if (status <= 0) {
277                 return NULL;
278         }
279
280         Py_RETURN_NONE;
281 }
282
283 /*
284  * Check existence of a path
285  */
286 static PyObject *py_smb_chkpath(pytalloc_Object *self, PyObject *args)
287 {
288         NTSTATUS status;
289         const char *path;
290         struct smb_private_data *spdata;
291
292         if (!PyArg_ParseTuple(args, "s:chkpath", &path)) {
293                 return NULL;
294         }
295
296         spdata = self->ptr;     
297         status = smbcli_chkpath(spdata->tree, path);
298
299         if (NT_STATUS_IS_OK(status)) {
300                 Py_RETURN_TRUE;
301         }
302
303         Py_RETURN_FALSE;
304 }
305
306 /*
307  * Read ACL on a given file/directory as a security descriptor object
308  */
309 static PyObject *py_smb_getacl(pytalloc_Object *self, PyObject *args, PyObject *kwargs)
310 {
311         NTSTATUS status;
312         union smb_open io;
313         union smb_fileinfo fio;
314         struct smb_private_data *spdata;
315         const char *filename;
316         uint32_t sinfo = 0;
317         int access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
318         int fnum;
319
320         if (!PyArg_ParseTuple(args, "s|Ii:get_acl", &filename, &sinfo, &access_mask)) {
321                 return NULL;
322         }
323
324         ZERO_STRUCT(io);
325
326         spdata = self->ptr;     
327
328         io.generic.level = RAW_OPEN_NTCREATEX;
329         io.ntcreatex.in.root_fid.fnum = 0;
330         io.ntcreatex.in.flags = 0;
331         io.ntcreatex.in.access_mask = access_mask;
332         io.ntcreatex.in.create_options = 0;
333         io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
334         io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | 
335                                         NTCREATEX_SHARE_ACCESS_WRITE;
336         io.ntcreatex.in.alloc_size = 0;
337         io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
338         io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
339         io.ntcreatex.in.security_flags = 0;
340         io.ntcreatex.in.fname = filename;
341         
342         status = smb_raw_open(spdata->tree, self->talloc_ctx, &io);
343         PyErr_NTSTATUS_IS_ERR_RAISE(status);
344
345         fnum = io.ntcreatex.out.file.fnum;
346
347         ZERO_STRUCT(fio);
348
349         fio.query_secdesc.level = RAW_FILEINFO_SEC_DESC;
350         fio.query_secdesc.in.file.fnum = fnum;
351         if (sinfo)
352                 fio.query_secdesc.in.secinfo_flags = sinfo;
353         else
354                 fio.query_secdesc.in.secinfo_flags = SECINFO_OWNER |
355                                                 SECINFO_GROUP |
356                                                 SECINFO_DACL |
357                                                 SECINFO_PROTECTED_DACL |
358                                                 SECINFO_UNPROTECTED_DACL |
359                                                 SECINFO_SACL |
360                                                 SECINFO_PROTECTED_SACL |
361                                                 SECINFO_UNPROTECTED_SACL;
362
363         status = smb_raw_query_secdesc(spdata->tree, self->talloc_ctx, &fio);
364         smbcli_close(spdata->tree, fnum);
365
366         PyErr_NTSTATUS_IS_ERR_RAISE(status);
367
368         return py_return_ndr_struct("samba.dcerpc.security", "descriptor",
369                                 self->talloc_ctx, fio.query_secdesc.out.sd);
370 }
371
372 /*
373  * Set ACL on file/directory using given security descriptor object
374  */
375 static PyObject *py_smb_setacl(pytalloc_Object *self, PyObject *args, PyObject *kwargs)
376 {
377         NTSTATUS status;
378         union smb_open io;
379         union smb_setfileinfo fio;
380         struct smb_private_data *spdata;
381         const char *filename;
382         PyObject *py_sd;
383         struct security_descriptor *sd;
384         uint32_t sinfo = 0;
385         int fnum;
386
387         if (!PyArg_ParseTuple(args, "sO|I:get_acl", &filename, &py_sd, &sinfo)) {
388                 return NULL;
389         }
390
391         spdata = self->ptr;
392
393         sd = pytalloc_get_type(py_sd, struct security_descriptor);
394         if (!sd) {
395                 PyErr_Format(PyExc_TypeError, 
396                         "Expected dcerpc.security.descriptor as argument, got %s", 
397                         talloc_get_name(pytalloc_get_ptr(py_sd)));
398                 return NULL;
399         }
400
401         ZERO_STRUCT(io);
402
403         spdata = self->ptr;     
404
405         io.generic.level = RAW_OPEN_NTCREATEX;
406         io.ntcreatex.in.root_fid.fnum = 0;
407         io.ntcreatex.in.flags = 0;
408         io.ntcreatex.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
409         io.ntcreatex.in.create_options = 0;
410         io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
411         io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | 
412                                         NTCREATEX_SHARE_ACCESS_WRITE;
413         io.ntcreatex.in.alloc_size = 0;
414         io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
415         io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
416         io.ntcreatex.in.security_flags = 0;
417         io.ntcreatex.in.fname = filename;
418         
419         status = smb_raw_open(spdata->tree, self->talloc_ctx, &io);
420         PyErr_NTSTATUS_IS_ERR_RAISE(status);
421
422         fnum = io.ntcreatex.out.file.fnum;
423
424         ZERO_STRUCT(fio);
425
426         fio.set_secdesc.level = RAW_SFILEINFO_SEC_DESC;
427         fio.set_secdesc.in.file.fnum = fnum;
428         if (sinfo)
429                 fio.set_secdesc.in.secinfo_flags = sinfo;
430         else
431                 fio.set_secdesc.in.secinfo_flags = SECINFO_OWNER |
432                                                 SECINFO_GROUP |
433                                                 SECINFO_DACL |
434                                                 SECINFO_PROTECTED_DACL |
435                                                 SECINFO_UNPROTECTED_DACL |
436                                                 SECINFO_SACL |
437                                                 SECINFO_PROTECTED_SACL |
438                                                 SECINFO_UNPROTECTED_SACL;
439
440         fio.set_secdesc.in.sd = sd;
441
442         status = smb_raw_set_secdesc(spdata->tree, &fio);
443         smbcli_close(spdata->tree, fnum);
444
445         PyErr_NTSTATUS_IS_ERR_RAISE(status);
446
447         Py_RETURN_NONE;
448 }
449
450 /*
451  * Open the file with the parameters passed in and return an object if OK
452  */
453 static PyObject *py_open_file(pytalloc_Object *self, PyObject *args, PyObject *kwargs)
454 {
455         NTSTATUS status;
456         union smb_open io;
457         struct smb_private_data *spdata;
458         const char *filename;
459         uint32_t access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
460         uint32_t share_access = NTCREATEX_SHARE_ACCESS_READ |
461                                 NTCREATEX_SHARE_ACCESS_WRITE;
462         uint32_t open_disposition = NTCREATEX_DISP_OPEN;
463         uint32_t create_options = 0;
464         TALLOC_CTX *mem_ctx;
465         int fnum;
466
467         if (!PyArg_ParseTuple(args, "s|iiii:open_file", 
468                                 &filename, 
469                                 &access_mask,
470                                 &share_access,
471                                 &open_disposition,
472                                 &create_options)) {
473                 return NULL;
474         }
475
476         ZERO_STRUCT(io);
477
478         spdata = self->ptr;     
479
480         mem_ctx = talloc_new(NULL);
481
482         io.generic.level = RAW_OPEN_NTCREATEX;
483         io.ntcreatex.in.root_fid.fnum = 0;
484         io.ntcreatex.in.flags = 0;
485         io.ntcreatex.in.access_mask = access_mask;
486         io.ntcreatex.in.create_options = create_options;
487         io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
488         io.ntcreatex.in.share_access = share_access;
489         io.ntcreatex.in.alloc_size = 0;
490         io.ntcreatex.in.open_disposition = open_disposition;
491         io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
492         io.ntcreatex.in.security_flags = 0;
493         io.ntcreatex.in.fname = filename;
494         
495         status = smb_raw_open(spdata->tree, mem_ctx, &io);
496         talloc_free(mem_ctx);
497
498         PyErr_NTSTATUS_IS_ERR_RAISE(status);
499
500         fnum = io.ntcreatex.out.file.fnum;
501
502         return Py_BuildValue("i", fnum);
503 }
504
505 /*
506  * Close the file based on the fnum passed in
507  */
508 static PyObject *py_close_file(pytalloc_Object *self, PyObject *args, PyObject *kwargs)
509 {
510         struct smb_private_data *spdata;
511         int fnum;
512
513         if (!PyArg_ParseTuple(args, "i:close_file", &fnum)) {
514                 return NULL;
515         }
516
517         spdata = self->ptr;     
518
519         /*
520          * Should check the status ...
521          */
522         smbcli_close(spdata->tree, fnum);
523
524         Py_RETURN_NONE;
525 }
526
527 static PyMethodDef py_smb_methods[] = {
528         { "loadfile", (PyCFunction)py_smb_loadfile, METH_VARARGS,
529                 "loadfile(path) -> file contents as a string\n\n \
530                 Read contents of a file." },
531         { "savefile", (PyCFunction)py_smb_savefile, METH_VARARGS,
532                 "savefile(path, str) -> None\n\n \
533                 Write string str to file." },
534         { "list", (PyCFunction)py_smb_list, METH_VARARGS|METH_KEYWORDS,
535                 "list(path) -> directory contents as a dictionary\n\n \
536                 List contents of a directory. The keys are, \n \
537                 \tname: Long name of the directory item\n \
538                 \tshort_name: Short name of the directory item\n \
539                 \tsize: File size in bytes\n \
540                 \tattrib: Attributes\n \
541                 \tmtime: Modification time\n" },
542         { "mkdir", (PyCFunction)py_smb_mkdir, METH_VARARGS,
543                 "mkdir(path) -> None\n\n \
544                 Create a directory." },
545         { "rmdir", (PyCFunction)py_smb_rmdir, METH_VARARGS,
546                 "rmdir(path) -> None\n\n \
547                 Delete a directory." },
548         { "deltree", (PyCFunction)py_smb_deltree, METH_VARARGS,
549                 "deltree(path) -> None\n\n \
550                 Delete a directory and all its contents." },
551         { "chkpath", (PyCFunction)py_smb_chkpath, METH_VARARGS,
552                 "chkpath(path) -> True or False\n\n \
553                 Return true if path exists, false otherwise." },
554         { "get_acl", (PyCFunction)py_smb_getacl, METH_VARARGS,
555                 "get_acl(path[, security_info=0]) -> security_descriptor object\n\n \
556                 Get security descriptor for file." },
557         { "set_acl", (PyCFunction)py_smb_setacl, METH_VARARGS,
558                 "set_acl(path, security_descriptor[, security_info=0]) -> None\n\n \
559                 Set security descriptor for file." },
560         { "open_file", (PyCFunction)py_open_file, METH_VARARGS,
561                 "open_file(path, access_mask[, share_access[, open_disposition[, create_options]]] -> fnum\n\n \
562                 Open a file. Throws NTSTATUS exceptions on errors." },
563         { "close_file", (PyCFunction)py_close_file, METH_VARARGS,
564                 "close_file(fnum) -> None\n\n \
565                 Close the file based on fnum."},
566         { NULL },
567 };
568
569 static PyObject *py_smb_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
570 {
571         PyObject *py_creds = Py_None;
572         PyObject *py_lp = Py_None;
573         const char *kwnames[] = { "hostname", "service", "creds", "lp", NULL };
574         const char *hostname = NULL;
575         const char *service = NULL;
576         pytalloc_Object *smb;
577         struct smb_private_data *spdata;
578         NTSTATUS status;
579
580         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "zz|OO",
581                                         discard_const_p(char *, kwnames),
582                                         &hostname, &service, &py_creds, &py_lp)) {
583                 return NULL;
584         }
585
586         smb = (pytalloc_Object *)type->tp_alloc(type, 0);
587         if (smb == NULL) {
588                 PyErr_NoMemory();
589                 return NULL;
590         }
591         smb->talloc_ctx = talloc_new(NULL);
592         if (smb->talloc_ctx == NULL) {
593                 PyErr_NoMemory();
594                 return NULL;
595         }
596
597         spdata = talloc_zero(smb->talloc_ctx, struct smb_private_data);
598         if (spdata == NULL) {
599                 PyErr_NoMemory();
600                 Py_DECREF(smb);
601                 return NULL;
602         }
603
604         spdata->lp_ctx = lpcfg_from_py_object(smb->talloc_ctx, py_lp);
605         if (spdata->lp_ctx == NULL) {
606                 Py_DECREF(smb);
607                 return NULL;
608         }
609         spdata->creds = PyCredentials_AsCliCredentials(py_creds);
610         spdata->ev_ctx = s4_event_context_init(smb->talloc_ctx);
611         if (spdata->ev_ctx == NULL) {
612                 PyErr_NoMemory();
613                 Py_DECREF(smb);
614                 return NULL;
615         }
616
617         status = do_smb_connect(smb->talloc_ctx, spdata, hostname, service, &spdata->tree);
618         PyErr_NTSTATUS_IS_ERR_RAISE(status);
619         if (spdata->tree == NULL) {
620                 Py_DECREF(smb);
621                 return NULL;
622         }
623
624         smb->ptr = spdata;
625         return (PyObject *)smb;
626 }
627
628 static PyTypeObject PySMB = {
629         .tp_name = "smb.SMB",
630         .tp_basicsize = sizeof(pytalloc_Object),
631         .tp_new = py_smb_new,
632         .tp_flags = Py_TPFLAGS_DEFAULT,
633         .tp_methods = py_smb_methods,
634         .tp_doc = "SMB(hostname, service[, creds[, lp]]) -> SMB connection object\n",
635
636 };
637
638 void initsmb(void)
639 {
640         PyObject *m;
641         PyTypeObject *talloc_type = pytalloc_GetObjectType();
642         if (talloc_type == NULL) {
643                 return;
644         }
645
646         PySMB.tp_base = talloc_type;
647
648         if (PyType_Ready(&PySMB) < 0) {
649                 return;
650         }
651
652         m = Py_InitModule3("smb", NULL, "SMB File I/O support");
653         if (m == NULL) {
654             return;
655         }
656
657         Py_INCREF(&PySMB);
658         PyModule_AddObject(m, "SMB", (PyObject *)&PySMB);
659
660 #define ADD_FLAGS(val)  PyModule_AddObject(m, #val, PyInt_FromLong(val))
661
662         ADD_FLAGS(FILE_ATTRIBUTE_READONLY);
663         ADD_FLAGS(FILE_ATTRIBUTE_HIDDEN);
664         ADD_FLAGS(FILE_ATTRIBUTE_SYSTEM);
665         ADD_FLAGS(FILE_ATTRIBUTE_VOLUME);
666         ADD_FLAGS(FILE_ATTRIBUTE_DIRECTORY);
667         ADD_FLAGS(FILE_ATTRIBUTE_ARCHIVE);
668         ADD_FLAGS(FILE_ATTRIBUTE_DEVICE);
669         ADD_FLAGS(FILE_ATTRIBUTE_NORMAL);
670         ADD_FLAGS(FILE_ATTRIBUTE_TEMPORARY);
671         ADD_FLAGS(FILE_ATTRIBUTE_SPARSE);
672         ADD_FLAGS(FILE_ATTRIBUTE_REPARSE_POINT);
673         ADD_FLAGS(FILE_ATTRIBUTE_COMPRESSED);
674         ADD_FLAGS(FILE_ATTRIBUTE_OFFLINE);
675         ADD_FLAGS(FILE_ATTRIBUTE_NONINDEXED);
676         ADD_FLAGS(FILE_ATTRIBUTE_ENCRYPTED);
677         ADD_FLAGS(FILE_ATTRIBUTE_ALL_MASK);
678 }