source: vendor/current/source4/libcli/pysmb.c

Last change on this file was 988, checked in by Silvan Scherrer, 9 years ago

Samba Server: update vendor to version 4.4.3

File size: 17.0 KB
Line 
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
41static PyTypeObject PySMB;
42
43void initsmb(void);
44
45struct 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
52static void dos_format(char *s)
53{
54 string_replace(s, '/', '\\');
55}
56
57
58/*
59 * Connect to SMB share using smb_full_connection
60 */
61static 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 */
101static PyObject * py_smb_loadfile(PyObject *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 = pytalloc_get_ptr(self);
117 status = smb_composite_loadfile(spdata->tree, pytalloc_get_mem_ctx(self), &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 */
126static PyObject * py_smb_savefile(PyObject *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 = pytalloc_get_ptr(self);
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 */
152static 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 */
183static PyObject *py_smb_list(PyObject *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(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);
204 }
205 dos_format(mask);
206
207 spdata = pytalloc_get_ptr(self);
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 */
224static PyObject *py_smb_mkdir(PyObject *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 = pytalloc_get_ptr(self);
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 */
244static PyObject *py_smb_rmdir(PyObject *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 = pytalloc_get_ptr(self);
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 */
264static PyObject *py_smb_deltree(PyObject *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 = pytalloc_get_ptr(self);
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 */
286static PyObject *py_smb_chkpath(PyObject *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 = pytalloc_get_ptr(self);
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 */
309static PyObject *py_smb_getacl(PyObject *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 = pytalloc_get_ptr(self);
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, pytalloc_get_mem_ctx(self), &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, pytalloc_get_mem_ctx(self), &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 pytalloc_get_mem_ctx(self), fio.query_secdesc.out.sd);
370}
371
372/*
373 * Set ACL on file/directory using given security descriptor object
374 */
375static PyObject *py_smb_setacl(PyObject *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 = pytalloc_get_ptr(self);
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 = pytalloc_get_ptr(self);
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, pytalloc_get_mem_ctx(self), &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 */
453static PyObject *py_open_file(PyObject *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 = pytalloc_get_ptr(self);
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 */
508static PyObject *py_close_file(PyObject *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 = pytalloc_get_ptr(self);
518
519 /*
520 * Should check the status ...
521 */
522 smbcli_close(spdata->tree, fnum);
523
524 Py_RETURN_NONE;
525}
526
527static PyMethodDef py_smb_methods[] = {
528 { "loadfile", py_smb_loadfile, METH_VARARGS,
529 "loadfile(path) -> file contents as a string\n\n \
530 Read contents of a file." },
531 { "savefile", 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", py_smb_mkdir, METH_VARARGS,
543 "mkdir(path) -> None\n\n \
544 Create a directory." },
545 { "rmdir", py_smb_rmdir, METH_VARARGS,
546 "rmdir(path) -> None\n\n \
547 Delete a directory." },
548 { "deltree", py_smb_deltree, METH_VARARGS,
549 "deltree(path) -> None\n\n \
550 Delete a directory and all its contents." },
551 { "chkpath", 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
569static 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 PyObject *smb;
577 struct smb_private_data *spdata;
578 NTSTATUS status;
579 TALLOC_CTX *frame = NULL;
580
581 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "zz|OO",
582 discard_const_p(char *, kwnames),
583 &hostname, &service, &py_creds, &py_lp)) {
584 return NULL;
585 }
586
587 frame = talloc_stackframe();
588
589 spdata = talloc_zero(frame, struct smb_private_data);
590 if (spdata == NULL) {
591 PyErr_NoMemory();
592 TALLOC_FREE(frame);
593 return NULL;
594 }
595
596 spdata->lp_ctx = lpcfg_from_py_object(spdata, py_lp);
597 if (spdata->lp_ctx == NULL) {
598 TALLOC_FREE(frame);
599 return NULL;
600 }
601 spdata->creds = PyCredentials_AsCliCredentials(py_creds);
602 spdata->ev_ctx = s4_event_context_init(spdata);
603 if (spdata->ev_ctx == NULL) {
604 PyErr_NoMemory();
605 TALLOC_FREE(frame);
606 return NULL;
607 }
608
609 status = do_smb_connect(spdata, spdata, hostname, service, &spdata->tree);
610 PyErr_NTSTATUS_IS_ERR_RAISE(status);
611 if (spdata->tree == NULL) {
612 TALLOC_FREE(frame);
613 return NULL;
614 }
615
616 smb = pytalloc_steal(type, spdata);
617 TALLOC_FREE(frame);
618 return smb;
619}
620
621static PyTypeObject PySMB = {
622 .tp_name = "smb.SMB",
623 .tp_new = py_smb_new,
624 .tp_flags = Py_TPFLAGS_DEFAULT,
625 .tp_methods = py_smb_methods,
626 .tp_doc = "SMB(hostname, service[, creds[, lp]]) -> SMB connection object\n",
627
628};
629
630void initsmb(void)
631{
632 PyObject *m;
633
634 if (pytalloc_BaseObject_PyType_Ready(&PySMB) < 0) {
635 return;
636 }
637
638 m = Py_InitModule3("smb", NULL, "SMB File I/O support");
639 if (m == NULL) {
640 return;
641 }
642
643 Py_INCREF(&PySMB);
644 PyModule_AddObject(m, "SMB", (PyObject *)&PySMB);
645
646#define ADD_FLAGS(val) PyModule_AddObject(m, #val, PyInt_FromLong(val))
647
648 ADD_FLAGS(FILE_ATTRIBUTE_READONLY);
649 ADD_FLAGS(FILE_ATTRIBUTE_HIDDEN);
650 ADD_FLAGS(FILE_ATTRIBUTE_SYSTEM);
651 ADD_FLAGS(FILE_ATTRIBUTE_VOLUME);
652 ADD_FLAGS(FILE_ATTRIBUTE_DIRECTORY);
653 ADD_FLAGS(FILE_ATTRIBUTE_ARCHIVE);
654 ADD_FLAGS(FILE_ATTRIBUTE_DEVICE);
655 ADD_FLAGS(FILE_ATTRIBUTE_NORMAL);
656 ADD_FLAGS(FILE_ATTRIBUTE_TEMPORARY);
657 ADD_FLAGS(FILE_ATTRIBUTE_SPARSE);
658 ADD_FLAGS(FILE_ATTRIBUTE_REPARSE_POINT);
659 ADD_FLAGS(FILE_ATTRIBUTE_COMPRESSED);
660 ADD_FLAGS(FILE_ATTRIBUTE_OFFLINE);
661 ADD_FLAGS(FILE_ATTRIBUTE_NONINDEXED);
662 ADD_FLAGS(FILE_ATTRIBUTE_ENCRYPTED);
663 ADD_FLAGS(FILE_ATTRIBUTE_ALL_MASK);
664}
Note: See TracBrowser for help on using the repository browser.