ctdb-scripts: Move connection tracking to 10.interface
[samba4-gss.git] / source3 / libsmb / libsmb_xattr.c
blobc91970d778fa2f1ff151ed9cf8d6490124ceb10c
1 /*
2 Unix SMB/Netbios implementation.
3 SMB client library implementation
4 Copyright (C) Andrew Tridgell 1998
5 Copyright (C) Richard Sharpe 2000, 2002
6 Copyright (C) John Terpstra 2000
7 Copyright (C) Tom Jansen (Ninja ISD) 2002
8 Copyright (C) Derrell Lipman 2003-2008
9 Copyright (C) Jeremy Allison 2007, 2008
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 3 of the License, or
14 (at your option) any later version.
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with this program. If not, see <http://www.gnu.org/licenses/>.
25 #include "includes.h"
26 #include "libsmb/libsmb.h"
27 #include "libsmbclient.h"
28 #include "libsmb_internal.h"
29 #include "../librpc/gen_ndr/ndr_lsa.h"
30 #include "rpc_client/rpc_client.h"
31 #include "rpc_client/cli_lsarpc.h"
32 #include "../libcli/security/security.h"
33 #include "lib/util/string_wrappers.h"
36 * Find an lsa pipe handle associated with a cli struct.
38 static struct rpc_pipe_client *
39 find_lsa_pipe_hnd(struct cli_state *ipc_cli)
41 struct rpc_pipe_client *pipe_hnd;
43 for (pipe_hnd = ipc_cli->pipe_list;
44 pipe_hnd;
45 pipe_hnd = pipe_hnd->next) {
46 struct dcerpc_binding_handle *bh = NULL;
47 const struct dcerpc_binding *bd = NULL;
48 struct ndr_syntax_id syntax;
50 bh = pipe_hnd->binding_handle;
51 bd = dcerpc_binding_handle_get_binding(bh);
52 syntax = dcerpc_binding_get_abstract_syntax(bd);
54 if (ndr_syntax_id_equal(&syntax,
55 &ndr_table_lsarpc.syntax_id)) {
56 return pipe_hnd;
59 return NULL;
63 * Sort ACEs according to the documentation at
64 * http://support.microsoft.com/kb/269175, at least as far as it defines the
65 * order.
68 static int
69 ace_compare(struct security_ace *ace1,
70 struct security_ace *ace2)
72 bool b1;
73 bool b2;
75 /* If the ACEs are equal, we have nothing more to do. */
76 if (security_ace_equal(ace1, ace2)) {
77 return 0;
80 /* Inherited follow non-inherited */
81 b1 = ((ace1->flags & SEC_ACE_FLAG_INHERITED_ACE) != 0);
82 b2 = ((ace2->flags & SEC_ACE_FLAG_INHERITED_ACE) != 0);
83 if (b1 != b2) {
84 return (b1 ? 1 : -1);
88 * What shall we do with AUDITs and ALARMs? It's undefined. We'll
89 * sort them after DENY and ALLOW.
91 b1 = (ace1->type != SEC_ACE_TYPE_ACCESS_ALLOWED &&
92 ace1->type != SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT &&
93 ace1->type != SEC_ACE_TYPE_ACCESS_DENIED &&
94 ace1->type != SEC_ACE_TYPE_ACCESS_DENIED_OBJECT);
95 b2 = (ace2->type != SEC_ACE_TYPE_ACCESS_ALLOWED &&
96 ace2->type != SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT &&
97 ace2->type != SEC_ACE_TYPE_ACCESS_DENIED &&
98 ace2->type != SEC_ACE_TYPE_ACCESS_DENIED_OBJECT);
99 if (b1 != b2) {
100 return (b1 ? 1 : -1);
103 /* Allowed ACEs follow denied ACEs */
104 b1 = (ace1->type == SEC_ACE_TYPE_ACCESS_ALLOWED ||
105 ace1->type == SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT);
106 b2 = (ace2->type == SEC_ACE_TYPE_ACCESS_ALLOWED ||
107 ace2->type == SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT);
108 if (b1 != b2) {
109 return (b1 ? 1 : -1);
113 * ACEs applying to an entity's object follow those applying to the
114 * entity itself
116 b1 = (ace1->type == SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT ||
117 ace1->type == SEC_ACE_TYPE_ACCESS_DENIED_OBJECT);
118 b2 = (ace2->type == SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT ||
119 ace2->type == SEC_ACE_TYPE_ACCESS_DENIED_OBJECT);
120 if (b1 != b2) {
121 return (b1 ? 1 : -1);
125 * If we get this far, the ACEs are similar as far as the
126 * characteristics we typically care about (those defined by the
127 * referenced MS document). We'll now sort by characteristics that
128 * just seems reasonable.
131 if (ace1->type != ace2->type) {
133 * ace2 and ace1 are reversed here, so that
134 * ACCESS_DENIED_ACE_TYPE (1) sorts before
135 * ACCESS_ALLOWED_ACE_TYPE (0), which is the order you
136 * usually want.
138 return NUMERIC_CMP(ace2->type, ace1->type);
141 if (dom_sid_compare(&ace1->trustee, &ace2->trustee)) {
142 return dom_sid_compare(&ace1->trustee, &ace2->trustee);
145 if (ace1->flags != ace2->flags) {
146 return NUMERIC_CMP(ace1->flags, ace2->flags);
149 if (ace1->access_mask != ace2->access_mask) {
150 return NUMERIC_CMP(ace1->access_mask, ace2->access_mask);
153 if (ace1->size != ace2->size) {
154 return NUMERIC_CMP(ace1->size, ace2->size);
157 return memcmp(ace1, ace2, sizeof(struct security_ace));
161 static void
162 sort_acl(struct security_acl *the_acl)
164 uint32_t i;
165 if (!the_acl) return;
167 TYPESAFE_QSORT(the_acl->aces, the_acl->num_aces, ace_compare);
169 for (i=1;i<the_acl->num_aces;) {
170 if (security_ace_equal(&the_acl->aces[i-1],
171 &the_acl->aces[i])) {
172 ARRAY_DEL_ELEMENT(
173 the_acl->aces, i, the_acl->num_aces);
174 the_acl->num_aces--;
175 } else {
176 i++;
181 /* convert a SID to a string, either numeric or username/group */
182 static void
183 convert_sid_to_string(struct cli_state *ipc_cli,
184 struct policy_handle *pol,
185 fstring str,
186 bool numeric,
187 struct dom_sid *sid)
189 char **domains = NULL;
190 char **names = NULL;
191 enum lsa_SidType *types = NULL;
192 struct rpc_pipe_client *pipe_hnd = find_lsa_pipe_hnd(ipc_cli);
193 TALLOC_CTX *ctx;
195 sid_to_fstring(str, sid);
197 if (numeric) {
198 return; /* no lookup desired */
201 if (!pipe_hnd) {
202 return;
205 /* Ask LSA to convert the sid to a name */
207 ctx = talloc_stackframe();
209 if (!NT_STATUS_IS_OK(rpccli_lsa_lookup_sids(pipe_hnd, ctx,
210 pol, 1, sid, &domains,
211 &names, &types)) ||
212 !domains || !domains[0] || !names || !names[0]) {
213 TALLOC_FREE(ctx);
214 return;
217 /* Converted OK */
219 fstr_sprintf(str, "%s%s%s",
220 domains[0], lp_winbind_separator(), names[0]);
222 TALLOC_FREE(ctx);
225 /* convert a string to a SID, either numeric or username/group */
226 static bool
227 convert_string_to_sid(struct cli_state *ipc_cli,
228 struct policy_handle *pol,
229 bool numeric,
230 struct dom_sid *sid,
231 const char *str)
233 enum lsa_SidType *types = NULL;
234 struct dom_sid *sids = NULL;
235 bool result = True;
236 TALLOC_CTX *ctx = NULL;
237 struct rpc_pipe_client *pipe_hnd = find_lsa_pipe_hnd(ipc_cli);
239 if (!pipe_hnd) {
240 return False;
243 if (numeric) {
244 if (strncmp(str, "S-", 2) == 0) {
245 return string_to_sid(sid, str);
248 result = False;
249 goto done;
252 ctx = talloc_stackframe();
253 if (!NT_STATUS_IS_OK(rpccli_lsa_lookup_names(pipe_hnd, ctx,
254 pol, 1, &str,
255 NULL, 1, &sids,
256 &types))) {
257 result = False;
258 goto done;
261 sid_copy(sid, &sids[0]);
262 done:
263 TALLOC_FREE(ctx);
264 return result;
268 /* parse an struct security_ace in the same format as print_ace() */
269 static bool
270 parse_ace(struct cli_state *ipc_cli,
271 struct policy_handle *pol,
272 struct security_ace *ace,
273 bool numeric,
274 char *str)
276 char *p;
277 const char *cp;
278 char *tok;
279 unsigned int atype;
280 unsigned int aflags;
281 unsigned int amask;
282 struct dom_sid sid;
283 uint32_t mask;
284 struct perm_value {
285 const char perm[7];
286 uint32_t mask;
288 size_t i;
289 TALLOC_CTX *frame = talloc_stackframe();
291 /* These values discovered by inspection */
292 static const struct perm_value special_values[] = {
293 { "R", 0x00120089 },
294 { "W", 0x00120116 },
295 { "X", 0x001200a0 },
296 { "D", 0x00010000 },
297 { "P", 0x00040000 },
298 { "O", 0x00080000 },
301 static const struct perm_value standard_values[] = {
302 { "READ", 0x001200a9 },
303 { "CHANGE", 0x001301bf },
304 { "FULL", 0x001f01ff },
307 ZERO_STRUCTP(ace);
308 p = strchr_m(str,':');
309 if (!p) {
310 TALLOC_FREE(frame);
311 return False;
313 *p = '\0';
314 p++;
315 /* Try to parse numeric form */
317 if (sscanf(p, "%u/%u/%u", &atype, &aflags, &amask) == 3 &&
318 convert_string_to_sid(ipc_cli, pol, numeric, &sid, str)) {
319 goto done;
322 /* Try to parse text form */
324 if (!convert_string_to_sid(ipc_cli, pol, numeric, &sid, str)) {
325 TALLOC_FREE(frame);
326 return false;
329 cp = p;
330 if (!next_token_talloc(frame, &cp, &tok, "/")) {
331 TALLOC_FREE(frame);
332 return false;
335 if (strncasecmp_m(tok, "ALLOWED", strlen("ALLOWED")) == 0) {
336 atype = SEC_ACE_TYPE_ACCESS_ALLOWED;
337 } else if (strncasecmp_m(tok, "DENIED", strlen("DENIED")) == 0) {
338 atype = SEC_ACE_TYPE_ACCESS_DENIED;
339 } else {
340 TALLOC_FREE(frame);
341 return false;
344 /* Only numeric form accepted for flags at present */
346 if (!(next_token_talloc(frame, &cp, &tok, "/") &&
347 sscanf(tok, "%u", &aflags))) {
348 TALLOC_FREE(frame);
349 return false;
352 if (!next_token_talloc(frame, &cp, &tok, "/")) {
353 TALLOC_FREE(frame);
354 return false;
357 if (strncmp(tok, "0x", 2) == 0) {
358 if (sscanf(tok, "%u", &amask) != 1) {
359 TALLOC_FREE(frame);
360 return false;
362 goto done;
365 for (i = 0; i < ARRAY_SIZE(standard_values); i++) {
366 const struct perm_value *v = &standard_values[i];
367 if (strcmp(tok, v->perm) == 0) {
368 amask = v->mask;
369 goto done;
373 p = tok;
375 while(*p) {
376 bool found = False;
378 for (i = 0; i < ARRAY_SIZE(special_values); i++) {
379 const struct perm_value *v = &special_values[i];
380 if (v->perm[0] == *p) {
381 amask |= v->mask;
382 found = True;
386 if (!found) {
387 TALLOC_FREE(frame);
388 return false;
390 p++;
393 if (*p) {
394 TALLOC_FREE(frame);
395 return false;
398 done:
399 mask = amask;
400 init_sec_ace(ace, &sid, atype, mask, aflags);
401 TALLOC_FREE(frame);
402 return true;
405 /* add an struct security_ace to a list of struct security_aces in a struct security_acl */
406 static bool
407 add_ace(struct security_acl **the_acl,
408 const struct security_ace *ace,
409 TALLOC_CTX *ctx)
411 struct security_acl *acl = *the_acl;
413 if (acl == NULL) {
414 acl = make_sec_acl(ctx, 3, 0, NULL);
415 if (acl == NULL) {
416 return false;
420 if (acl->num_aces == UINT32_MAX) {
421 return false;
423 ADD_TO_ARRAY(
424 acl, struct security_ace, *ace, &acl->aces, &acl->num_aces);
425 *the_acl = acl;
426 return True;
430 /* parse a ascii version of a security descriptor */
431 static struct security_descriptor *
432 sec_desc_parse(TALLOC_CTX *ctx,
433 struct cli_state *ipc_cli,
434 struct policy_handle *pol,
435 bool numeric,
436 const char *str)
438 const char *p = str;
439 char *tok;
440 struct security_descriptor *ret = NULL;
441 size_t sd_size;
442 struct dom_sid owner_sid = { .num_auths = 0 };
443 struct dom_sid group_sid = { .num_auths = 0 };
444 bool have_owner = false, have_group = false;
445 struct security_acl *dacl=NULL;
446 int revision=1;
448 while (next_token_talloc(ctx, &p, &tok, "\t,\r\n")) {
450 if (strncasecmp_m(tok,"REVISION:", 9) == 0) {
451 revision = strtol(tok+9, NULL, 16);
452 continue;
455 if (strncasecmp_m(tok,"OWNER:", 6) == 0) {
456 if (have_owner) {
457 DEBUG(5,("OWNER specified more than once!\n"));
458 goto done;
460 if (!convert_string_to_sid(ipc_cli, pol,
461 numeric,
462 &owner_sid, tok+6)) {
463 DEBUG(5, ("Failed to parse owner sid\n"));
464 goto done;
466 have_owner = true;
467 continue;
470 if (strncasecmp_m(tok,"OWNER+:", 7) == 0) {
471 if (have_owner) {
472 DEBUG(5,("OWNER specified more than once!\n"));
473 goto done;
475 if (!convert_string_to_sid(ipc_cli, pol,
476 False,
477 &owner_sid, tok+7)) {
478 DEBUG(5, ("Failed to parse owner sid\n"));
479 goto done;
481 have_owner = true;
482 continue;
485 if (strncasecmp_m(tok,"GROUP:", 6) == 0) {
486 if (have_group) {
487 DEBUG(5,("GROUP specified more than once!\n"));
488 goto done;
490 if (!convert_string_to_sid(ipc_cli, pol,
491 numeric,
492 &group_sid, tok+6)) {
493 DEBUG(5, ("Failed to parse group sid\n"));
494 goto done;
496 have_group = true;
497 continue;
500 if (strncasecmp_m(tok,"GROUP+:", 7) == 0) {
501 if (have_group) {
502 DEBUG(5,("GROUP specified more than once!\n"));
503 goto done;
505 if (!convert_string_to_sid(ipc_cli, pol,
506 False,
507 &group_sid, tok+6)) {
508 DEBUG(5, ("Failed to parse group sid\n"));
509 goto done;
511 have_group = true;
512 continue;
515 if (strncasecmp_m(tok,"ACL:", 4) == 0) {
516 struct security_ace ace;
517 if (!parse_ace(ipc_cli, pol, &ace, numeric, tok+4)) {
518 DEBUG(5, ("Failed to parse ACL %s\n", tok));
519 goto done;
521 if(!add_ace(&dacl, &ace, ctx)) {
522 DEBUG(5, ("Failed to add ACL %s\n", tok));
523 goto done;
525 continue;
528 if (strncasecmp_m(tok,"ACL+:", 5) == 0) {
529 struct security_ace ace;
530 if (!parse_ace(ipc_cli, pol, &ace, False, tok+5)) {
531 DEBUG(5, ("Failed to parse ACL %s\n", tok));
532 goto done;
534 if(!add_ace(&dacl, &ace, ctx)) {
535 DEBUG(5, ("Failed to add ACL %s\n", tok));
536 goto done;
538 continue;
541 DEBUG(5, ("Failed to parse security descriptor\n"));
542 goto done;
545 ret = make_sec_desc(
546 ctx,
547 revision,
548 SEC_DESC_SELF_RELATIVE,
549 have_owner ? &owner_sid : NULL,
550 have_group ? &group_sid : NULL,
551 NULL,
552 dacl,
553 &sd_size);
555 done:
556 return ret;
560 /* Obtain the current dos attributes */
561 static struct DOS_ATTR_DESC *
562 dos_attr_query(SMBCCTX *context,
563 TALLOC_CTX *ctx,
564 const char *filename,
565 SMBCSRV *srv)
567 struct stat sb = {0};
568 struct DOS_ATTR_DESC *ret = NULL;
569 NTSTATUS status;
571 ret = talloc(ctx, struct DOS_ATTR_DESC);
572 if (!ret) {
573 errno = ENOMEM;
574 return NULL;
577 /* Obtain the DOS attributes */
578 status = SMBC_getatr(context, srv, filename, &sb);
579 if (!NT_STATUS_IS_OK(status)) {
580 DEBUG(5, ("dos_attr_query Failed to query old attributes\n"));
581 TALLOC_FREE(ret);
582 errno = cli_status_to_errno(status);
583 return NULL;
586 ret->mode = sb.st_mode;
587 ret->size = sb.st_size;
588 ret->create_time = sb.st_ctime;
589 ret->access_time = sb.st_atime;
590 ret->write_time = sb.st_mtime;
591 ret->change_time = sb.st_mtime;
592 ret->inode = sb.st_ino;
594 return ret;
598 /* parse a ascii version of a security descriptor */
599 static void
600 dos_attr_parse(SMBCCTX *context,
601 struct DOS_ATTR_DESC *dad,
602 SMBCSRV *srv,
603 char *str)
605 int n;
606 const char *p = str;
607 char *tok = NULL;
608 TALLOC_CTX *frame = NULL;
609 struct {
610 const char * create_time_attr;
611 const char * access_time_attr;
612 const char * write_time_attr;
613 const char * change_time_attr;
614 } attr_strings;
616 /* Determine whether to use old-style or new-style attribute names */
617 if (context->internal->full_time_names) {
618 /* new-style names */
619 attr_strings.create_time_attr = "CREATE_TIME";
620 attr_strings.access_time_attr = "ACCESS_TIME";
621 attr_strings.write_time_attr = "WRITE_TIME";
622 attr_strings.change_time_attr = "CHANGE_TIME";
623 } else {
624 /* old-style names */
625 attr_strings.create_time_attr = NULL;
626 attr_strings.access_time_attr = "A_TIME";
627 attr_strings.write_time_attr = "M_TIME";
628 attr_strings.change_time_attr = "C_TIME";
631 /* if this is to set the entire ACL... */
632 if (*str == '*') {
633 /* ... then increment past the first colon if there is one */
634 if ((p = strchr(str, ':')) != NULL) {
635 ++p;
636 } else {
637 p = str;
641 frame = talloc_stackframe();
642 while (next_token_talloc(frame, &p, &tok, "\t,\r\n")) {
643 if (strncasecmp_m(tok, "MODE:", 5) == 0) {
644 long request = strtol(tok+5, NULL, 16);
645 if (request == 0) {
646 dad->mode =
647 (dad->mode & FILE_ATTRIBUTE_DIRECTORY)
648 ? FILE_ATTRIBUTE_DIRECTORY
649 : FILE_ATTRIBUTE_NORMAL;
650 } else {
651 dad->mode = request;
653 continue;
656 if (strncasecmp_m(tok, "SIZE:", 5) == 0) {
657 dad->size = (off_t)atof(tok+5);
658 continue;
661 n = strlen(attr_strings.access_time_attr);
662 if (strncasecmp_m(tok, attr_strings.access_time_attr, n) == 0) {
663 dad->access_time = (time_t)strtol(tok+n+1, NULL, 10);
664 continue;
667 n = strlen(attr_strings.change_time_attr);
668 if (strncasecmp_m(tok, attr_strings.change_time_attr, n) == 0) {
669 dad->change_time = (time_t)strtol(tok+n+1, NULL, 10);
670 continue;
673 n = strlen(attr_strings.write_time_attr);
674 if (strncasecmp_m(tok, attr_strings.write_time_attr, n) == 0) {
675 dad->write_time = (time_t)strtol(tok+n+1, NULL, 10);
676 continue;
679 if (attr_strings.create_time_attr != NULL) {
680 n = strlen(attr_strings.create_time_attr);
681 if (strncasecmp_m(tok, attr_strings.create_time_attr,
682 n) == 0) {
683 dad->create_time = (time_t)strtol(tok+n+1,
684 NULL, 10);
685 continue;
689 if (strncasecmp_m(tok, "INODE:", 6) == 0) {
690 dad->inode = (SMB_INO_T)atof(tok+6);
691 continue;
694 TALLOC_FREE(frame);
697 /*****************************************************
698 Retrieve the acls for a file.
699 *******************************************************/
701 static int
702 cacl_get(SMBCCTX *context,
703 TALLOC_CTX *ctx,
704 SMBCSRV *srv,
705 struct cli_state *ipc_cli,
706 struct policy_handle *pol,
707 const char *filename,
708 const char *attr_name,
709 char *buf,
710 int bufsize)
712 uint32_t i;
713 int n = 0;
714 int n_used;
715 bool all;
716 bool all_nt;
717 bool all_nt_acls;
718 bool all_dos;
719 bool some_nt;
720 bool some_dos;
721 bool exclude_nt_revision = False;
722 bool exclude_nt_owner = False;
723 bool exclude_nt_group = False;
724 bool exclude_nt_acl = False;
725 bool exclude_dos_mode = False;
726 bool exclude_dos_size = False;
727 bool exclude_dos_create_time = False;
728 bool exclude_dos_access_time = False;
729 bool exclude_dos_write_time = False;
730 bool exclude_dos_change_time = False;
731 bool exclude_dos_inode = False;
732 bool numeric = True;
733 bool determine_size = (bufsize == 0);
734 uint16_t fnum;
735 struct security_descriptor *sd;
736 fstring sidstr;
737 fstring name_sandbox;
738 char *name;
739 char *pExclude;
740 char *p;
741 struct cli_state *cli = srv->cli;
742 struct {
743 const char * create_time_attr;
744 const char * access_time_attr;
745 const char * write_time_attr;
746 const char * change_time_attr;
747 } attr_strings;
748 struct {
749 const char * create_time_attr;
750 const char * access_time_attr;
751 const char * write_time_attr;
752 const char * change_time_attr;
753 } excl_attr_strings;
755 /* Determine whether to use old-style or new-style attribute names */
756 if (context->internal->full_time_names) {
757 /* new-style names */
758 attr_strings.create_time_attr = "CREATE_TIME";
759 attr_strings.access_time_attr = "ACCESS_TIME";
760 attr_strings.write_time_attr = "WRITE_TIME";
761 attr_strings.change_time_attr = "CHANGE_TIME";
763 excl_attr_strings.create_time_attr = "CREATE_TIME";
764 excl_attr_strings.access_time_attr = "ACCESS_TIME";
765 excl_attr_strings.write_time_attr = "WRITE_TIME";
766 excl_attr_strings.change_time_attr = "CHANGE_TIME";
767 } else {
768 /* old-style names */
769 attr_strings.create_time_attr = NULL;
770 attr_strings.access_time_attr = "A_TIME";
771 attr_strings.write_time_attr = "M_TIME";
772 attr_strings.change_time_attr = "C_TIME";
774 excl_attr_strings.create_time_attr = NULL;
775 excl_attr_strings.access_time_attr = "dos_attr.A_TIME";
776 excl_attr_strings.write_time_attr = "dos_attr.M_TIME";
777 excl_attr_strings.change_time_attr = "dos_attr.C_TIME";
780 /* Copy name so we can strip off exclusions (if any are specified) */
781 fstrcpy(name_sandbox, attr_name);
783 /* Ensure name is null terminated */
784 name_sandbox[sizeof(name_sandbox) - 1] = '\0';
786 /* Play in the sandbox */
787 name = name_sandbox;
789 /* If there are any exclusions, point to them and mask them from name */
790 if ((pExclude = strchr(name, '!')) != NULL)
792 *pExclude++ = '\0';
795 all = (strncasecmp_m(name, "system.*", 8) == 0);
796 all_nt = (strncasecmp_m(name, "system.nt_sec_desc.*", 20) == 0);
797 all_nt_acls = (strncasecmp_m(name, "system.nt_sec_desc.acl.*", 24) == 0);
798 all_dos = (strncasecmp_m(name, "system.dos_attr.*", 17) == 0);
799 some_nt = (strncasecmp_m(name, "system.nt_sec_desc.", 19) == 0);
800 some_dos = (strncasecmp_m(name, "system.dos_attr.", 16) == 0);
801 numeric = (* (name + strlen(name) - 1) != '+');
803 /* Look for exclusions from "all" requests */
804 if (all || all_nt || all_dos) {
805 /* Exclusions are delimited by '!' */
806 for (;
807 pExclude != NULL;
808 pExclude = (p == NULL ? NULL : p + 1)) {
810 /* Find end of this exclusion name */
811 if ((p = strchr(pExclude, '!')) != NULL)
813 *p = '\0';
816 /* Which exclusion name is this? */
817 if (strcasecmp_m(pExclude,
818 "nt_sec_desc.revision") == 0) {
819 exclude_nt_revision = True;
821 else if (strcasecmp_m(pExclude,
822 "nt_sec_desc.owner") == 0) {
823 exclude_nt_owner = True;
825 else if (strcasecmp_m(pExclude,
826 "nt_sec_desc.group") == 0) {
827 exclude_nt_group = True;
829 else if (strcasecmp_m(pExclude,
830 "nt_sec_desc.acl") == 0) {
831 exclude_nt_acl = True;
833 else if (strcasecmp_m(pExclude,
834 "dos_attr.mode") == 0) {
835 exclude_dos_mode = True;
837 else if (strcasecmp_m(pExclude,
838 "dos_attr.size") == 0) {
839 exclude_dos_size = True;
841 else if (excl_attr_strings.create_time_attr != NULL &&
842 strcasecmp_m(pExclude,
843 excl_attr_strings.change_time_attr) == 0) {
844 exclude_dos_create_time = True;
846 else if (strcasecmp_m(pExclude,
847 excl_attr_strings.access_time_attr) == 0) {
848 exclude_dos_access_time = True;
850 else if (strcasecmp_m(pExclude,
851 excl_attr_strings.write_time_attr) == 0) {
852 exclude_dos_write_time = True;
854 else if (strcasecmp_m(pExclude,
855 excl_attr_strings.change_time_attr) == 0) {
856 exclude_dos_change_time = True;
858 else if (strcasecmp_m(pExclude, "dos_attr.inode") == 0) {
859 exclude_dos_inode = True;
861 else {
862 DEBUG(5, ("cacl_get received unknown exclusion: %s\n",
863 pExclude));
864 errno = ENOATTR;
865 return -1;
870 n_used = 0;
873 * If we are (possibly) talking to an NT or new system and some NT
874 * attributes have been requested...
876 if (ipc_cli && (all || some_nt || all_nt_acls)) {
877 char *targetpath = NULL;
878 struct cli_state *targetcli = NULL;
879 struct cli_credentials *creds = NULL;
880 NTSTATUS status;
882 /* Point to the portion after "system.nt_sec_desc." */
883 name += 19; /* if (all) this will be invalid but unused */
885 creds = context->internal->creds;
887 status = cli_resolve_path(
888 ctx, "",
889 creds,
890 cli, filename, &targetcli, &targetpath);
891 if (!NT_STATUS_IS_OK(status)) {
892 DEBUG(5, ("cacl_get Could not resolve %s\n",
893 filename));
894 errno = ENOENT;
895 return -1;
898 /* ... then obtain any NT attributes which were requested */
899 status = cli_ntcreate(
900 targetcli, /* cli */
901 targetpath, /* fname */
902 0, /* CreatFlags */
903 READ_CONTROL_ACCESS, /* DesiredAccess */
904 0, /* FileAttributes */
905 FILE_SHARE_READ|
906 FILE_SHARE_WRITE, /* ShareAccess */
907 FILE_OPEN, /* CreateDisposition */
908 0x0, /* CreateOptions */
909 0x0, /* SecurityFlags */
910 &fnum, /* pfid */
911 NULL); /* cr */
912 if (!NT_STATUS_IS_OK(status)) {
913 DEBUG(5, ("cacl_get failed to open %s: %s\n",
914 targetpath, nt_errstr(status)));
915 errno = cli_status_to_errno(status);
916 return -1;
919 status = cli_query_secdesc(targetcli, fnum, ctx, &sd);
920 if (!NT_STATUS_IS_OK(status)) {
921 DEBUG(5,("cacl_get Failed to query old descriptor "
922 "of %s: %s\n",
923 targetpath, nt_errstr(status)));
924 errno = cli_status_to_errno(status);
925 return -1;
928 cli_close(targetcli, fnum);
930 if (! exclude_nt_revision) {
931 if (all || all_nt) {
932 if (determine_size) {
933 p = talloc_asprintf(ctx,
934 "REVISION:%d",
935 sd->revision);
936 if (!p) {
937 errno = ENOMEM;
938 return -1;
940 n = strlen(p);
941 } else {
942 n = snprintf(buf, bufsize,
943 "REVISION:%d",
944 sd->revision);
946 } else if (strcasecmp_m(name, "revision") == 0) {
947 if (determine_size) {
948 p = talloc_asprintf(ctx, "%d",
949 sd->revision);
950 if (!p) {
951 errno = ENOMEM;
952 return -1;
954 n = strlen(p);
955 } else {
956 n = snprintf(buf, bufsize, "%d",
957 sd->revision);
961 if (!determine_size && n > bufsize) {
962 errno = ERANGE;
963 return -1;
965 buf += n;
966 n_used += n;
967 bufsize -= n;
968 n = 0;
971 if (! exclude_nt_owner) {
972 /* Get owner and group sid */
973 if (sd->owner_sid) {
974 convert_sid_to_string(ipc_cli, pol,
975 sidstr,
976 numeric,
977 sd->owner_sid);
978 } else {
979 fstrcpy(sidstr, "");
982 if (all || all_nt) {
983 if (determine_size) {
984 p = talloc_asprintf(ctx, ",OWNER:%s",
985 sidstr);
986 if (!p) {
987 errno = ENOMEM;
988 return -1;
990 n = strlen(p);
991 } else if (sidstr[0] != '\0') {
992 n = snprintf(buf, bufsize,
993 ",OWNER:%s", sidstr);
995 } else if (strncasecmp_m(name, "owner", 5) == 0) {
996 if (determine_size) {
997 p = talloc_asprintf(ctx, "%s", sidstr);
998 if (!p) {
999 errno = ENOMEM;
1000 return -1;
1002 n = strlen(p);
1003 } else {
1004 n = snprintf(buf, bufsize, "%s",
1005 sidstr);
1009 if (!determine_size && n > bufsize) {
1010 errno = ERANGE;
1011 return -1;
1013 buf += n;
1014 n_used += n;
1015 bufsize -= n;
1016 n = 0;
1019 if (! exclude_nt_group) {
1020 if (sd->group_sid) {
1021 convert_sid_to_string(ipc_cli, pol,
1022 sidstr, numeric,
1023 sd->group_sid);
1024 } else {
1025 fstrcpy(sidstr, "");
1028 if (all || all_nt) {
1029 if (determine_size) {
1030 p = talloc_asprintf(ctx, ",GROUP:%s",
1031 sidstr);
1032 if (!p) {
1033 errno = ENOMEM;
1034 return -1;
1036 n = strlen(p);
1037 } else if (sidstr[0] != '\0') {
1038 n = snprintf(buf, bufsize,
1039 ",GROUP:%s", sidstr);
1041 } else if (strncasecmp_m(name, "group", 5) == 0) {
1042 if (determine_size) {
1043 p = talloc_asprintf(ctx, "%s", sidstr);
1044 if (!p) {
1045 errno = ENOMEM;
1046 return -1;
1048 n = strlen(p);
1049 } else {
1050 n = snprintf(buf, bufsize,
1051 "%s", sidstr);
1055 if (!determine_size && n > bufsize) {
1056 errno = ERANGE;
1057 return -1;
1059 buf += n;
1060 n_used += n;
1061 bufsize -= n;
1062 n = 0;
1065 if (! exclude_nt_acl) {
1066 /* Add aces to value buffer */
1067 for (i = 0; sd->dacl && i < sd->dacl->num_aces; i++) {
1069 struct security_ace *ace = &sd->dacl->aces[i];
1070 convert_sid_to_string(ipc_cli, pol,
1071 sidstr, numeric,
1072 &ace->trustee);
1074 if (all || all_nt) {
1075 if (determine_size) {
1076 p = talloc_asprintf(
1077 ctx,
1078 ",ACL:"
1079 "%s:%d/%d/0x%08x",
1080 sidstr,
1081 ace->type,
1082 ace->flags,
1083 ace->access_mask);
1084 if (!p) {
1085 errno = ENOMEM;
1086 return -1;
1088 n = strlen(p);
1089 } else {
1090 n = snprintf(
1091 buf, bufsize,
1092 ",ACL:%s:%d/%d/0x%08x",
1093 sidstr,
1094 ace->type,
1095 ace->flags,
1096 ace->access_mask);
1098 } else if ((strncasecmp_m(name, "acl", 3) == 0 &&
1099 strcasecmp_m(name+3, sidstr) == 0) ||
1100 (strncasecmp_m(name, "acl+", 4) == 0 &&
1101 strcasecmp_m(name+4, sidstr) == 0)) {
1102 if (determine_size) {
1103 p = talloc_asprintf(
1104 ctx,
1105 "%d/%d/0x%08x",
1106 ace->type,
1107 ace->flags,
1108 ace->access_mask);
1109 if (!p) {
1110 errno = ENOMEM;
1111 return -1;
1113 n = strlen(p);
1114 } else {
1115 n = snprintf(buf, bufsize,
1116 "%d/%d/0x%08x",
1117 ace->type,
1118 ace->flags,
1119 ace->access_mask);
1121 } else if (all_nt_acls) {
1122 if (determine_size) {
1123 p = talloc_asprintf(
1124 ctx,
1125 "%s%s:%d/%d/0x%08x",
1126 i ? "," : "",
1127 sidstr,
1128 ace->type,
1129 ace->flags,
1130 ace->access_mask);
1131 if (!p) {
1132 errno = ENOMEM;
1133 return -1;
1135 n = strlen(p);
1136 } else {
1137 n = snprintf(buf, bufsize,
1138 "%s%s:%d/%d/0x%08x",
1139 i ? "," : "",
1140 sidstr,
1141 ace->type,
1142 ace->flags,
1143 ace->access_mask);
1146 if (!determine_size && n > bufsize) {
1147 errno = ERANGE;
1148 return -1;
1150 buf += n;
1151 n_used += n;
1152 bufsize -= n;
1153 n = 0;
1157 /* Restore name pointer to its original value */
1158 name -= 19;
1161 if (all || some_dos) {
1162 struct stat sb = {0};
1163 time_t create_time = (time_t)0;
1164 time_t write_time = (time_t)0;
1165 time_t access_time = (time_t)0;
1166 time_t change_time = (time_t)0;
1167 off_t size = 0;
1168 uint16_t mode = 0;
1169 SMB_INO_T ino = 0;
1170 NTSTATUS status;
1172 /* Point to the portion after "system.dos_attr." */
1173 name += 16; /* if (all) this will be invalid but unused */
1175 /* Obtain the DOS attributes */
1176 status = SMBC_getatr(context, srv, filename, &sb);
1177 if (!NT_STATUS_IS_OK(status)) {
1178 errno = cli_status_to_errno(status);
1179 return -1;
1182 create_time = sb.st_ctime;
1183 access_time = sb.st_atime;
1184 write_time = sb.st_mtime;
1185 change_time = sb.st_mtime;
1186 size = sb.st_size;
1187 mode = sb.st_mode;
1188 ino = sb.st_ino;
1190 if (! exclude_dos_mode) {
1191 if (all || all_dos) {
1192 if (determine_size) {
1193 p = talloc_asprintf(ctx,
1194 "%sMODE:0x%x",
1195 (ipc_cli &&
1196 (all || some_nt)
1197 ? ","
1198 : ""),
1199 mode);
1200 if (!p) {
1201 errno = ENOMEM;
1202 return -1;
1204 n = strlen(p);
1205 } else {
1206 n = snprintf(buf, bufsize,
1207 "%sMODE:0x%x",
1208 (ipc_cli &&
1209 (all || some_nt)
1210 ? ","
1211 : ""),
1212 mode);
1214 } else if (strcasecmp_m(name, "mode") == 0) {
1215 if (determine_size) {
1216 p = talloc_asprintf(ctx, "0x%x", mode);
1217 if (!p) {
1218 errno = ENOMEM;
1219 return -1;
1221 n = strlen(p);
1222 } else {
1223 n = snprintf(buf, bufsize,
1224 "0x%x", mode);
1228 if (!determine_size && n > bufsize) {
1229 errno = ERANGE;
1230 return -1;
1232 buf += n;
1233 n_used += n;
1234 bufsize -= n;
1235 n = 0;
1238 if (! exclude_dos_size) {
1239 if (all || all_dos) {
1240 if (determine_size) {
1241 p = talloc_asprintf(
1242 ctx,
1243 ",SIZE:%.0f",
1244 (double)size);
1245 if (!p) {
1246 errno = ENOMEM;
1247 return -1;
1249 n = strlen(p);
1250 } else {
1251 n = snprintf(buf, bufsize,
1252 ",SIZE:%.0f",
1253 (double)size);
1255 } else if (strcasecmp_m(name, "size") == 0) {
1256 if (determine_size) {
1257 p = talloc_asprintf(
1258 ctx,
1259 "%.0f",
1260 (double)size);
1261 if (!p) {
1262 errno = ENOMEM;
1263 return -1;
1265 n = strlen(p);
1266 } else {
1267 n = snprintf(buf, bufsize,
1268 "%.0f",
1269 (double)size);
1273 if (!determine_size && n > bufsize) {
1274 errno = ERANGE;
1275 return -1;
1277 buf += n;
1278 n_used += n;
1279 bufsize -= n;
1280 n = 0;
1283 if (! exclude_dos_create_time &&
1284 attr_strings.create_time_attr != NULL) {
1285 if (all || all_dos) {
1286 if (determine_size) {
1287 p = talloc_asprintf(ctx,
1288 ",%s:%lu",
1289 attr_strings.create_time_attr,
1290 (unsigned long) create_time);
1291 if (!p) {
1292 errno = ENOMEM;
1293 return -1;
1295 n = strlen(p);
1296 } else {
1297 n = snprintf(buf, bufsize,
1298 ",%s:%lu",
1299 attr_strings.create_time_attr,
1300 (unsigned long) create_time);
1302 } else if (strcasecmp_m(name, attr_strings.create_time_attr) == 0) {
1303 if (determine_size) {
1304 p = talloc_asprintf(ctx, "%lu", (unsigned long) create_time);
1305 if (!p) {
1306 errno = ENOMEM;
1307 return -1;
1309 n = strlen(p);
1310 } else {
1311 n = snprintf(buf, bufsize,
1312 "%lu", (unsigned long) create_time);
1316 if (!determine_size && n > bufsize) {
1317 errno = ERANGE;
1318 return -1;
1320 buf += n;
1321 n_used += n;
1322 bufsize -= n;
1323 n = 0;
1326 if (! exclude_dos_access_time) {
1327 if (all || all_dos) {
1328 if (determine_size) {
1329 p = talloc_asprintf(ctx,
1330 ",%s:%lu",
1331 attr_strings.access_time_attr,
1332 (unsigned long) access_time);
1333 if (!p) {
1334 errno = ENOMEM;
1335 return -1;
1337 n = strlen(p);
1338 } else {
1339 n = snprintf(buf, bufsize,
1340 ",%s:%lu",
1341 attr_strings.access_time_attr,
1342 (unsigned long) access_time);
1344 } else if (strcasecmp_m(name, attr_strings.access_time_attr) == 0) {
1345 if (determine_size) {
1346 p = talloc_asprintf(ctx, "%lu", (unsigned long) access_time);
1347 if (!p) {
1348 errno = ENOMEM;
1349 return -1;
1351 n = strlen(p);
1352 } else {
1353 n = snprintf(buf, bufsize,
1354 "%lu", (unsigned long) access_time);
1358 if (!determine_size && n > bufsize) {
1359 errno = ERANGE;
1360 return -1;
1362 buf += n;
1363 n_used += n;
1364 bufsize -= n;
1365 n = 0;
1368 if (! exclude_dos_write_time) {
1369 if (all || all_dos) {
1370 if (determine_size) {
1371 p = talloc_asprintf(ctx,
1372 ",%s:%lu",
1373 attr_strings.write_time_attr,
1374 (unsigned long) write_time);
1375 if (!p) {
1376 errno = ENOMEM;
1377 return -1;
1379 n = strlen(p);
1380 } else {
1381 n = snprintf(buf, bufsize,
1382 ",%s:%lu",
1383 attr_strings.write_time_attr,
1384 (unsigned long) write_time);
1386 } else if (strcasecmp_m(name, attr_strings.write_time_attr) == 0) {
1387 if (determine_size) {
1388 p = talloc_asprintf(ctx, "%lu", (unsigned long) write_time);
1389 if (!p) {
1390 errno = ENOMEM;
1391 return -1;
1393 n = strlen(p);
1394 } else {
1395 n = snprintf(buf, bufsize,
1396 "%lu", (unsigned long) write_time);
1400 if (!determine_size && n > bufsize) {
1401 errno = ERANGE;
1402 return -1;
1404 buf += n;
1405 n_used += n;
1406 bufsize -= n;
1407 n = 0;
1410 if (! exclude_dos_change_time) {
1411 if (all || all_dos) {
1412 if (determine_size) {
1413 p = talloc_asprintf(ctx,
1414 ",%s:%lu",
1415 attr_strings.change_time_attr,
1416 (unsigned long) change_time);
1417 if (!p) {
1418 errno = ENOMEM;
1419 return -1;
1421 n = strlen(p);
1422 } else {
1423 n = snprintf(buf, bufsize,
1424 ",%s:%lu",
1425 attr_strings.change_time_attr,
1426 (unsigned long) change_time);
1428 } else if (strcasecmp_m(name, attr_strings.change_time_attr) == 0) {
1429 if (determine_size) {
1430 p = talloc_asprintf(ctx, "%lu", (unsigned long) change_time);
1431 if (!p) {
1432 errno = ENOMEM;
1433 return -1;
1435 n = strlen(p);
1436 } else {
1437 n = snprintf(buf, bufsize,
1438 "%lu", (unsigned long) change_time);
1442 if (!determine_size && n > bufsize) {
1443 errno = ERANGE;
1444 return -1;
1446 buf += n;
1447 n_used += n;
1448 bufsize -= n;
1449 n = 0;
1452 if (! exclude_dos_inode) {
1453 if (all || all_dos) {
1454 if (determine_size) {
1455 p = talloc_asprintf(
1456 ctx,
1457 ",INODE:%.0f",
1458 (double)ino);
1459 if (!p) {
1460 errno = ENOMEM;
1461 return -1;
1463 n = strlen(p);
1464 } else {
1465 n = snprintf(buf, bufsize,
1466 ",INODE:%.0f",
1467 (double) ino);
1469 } else if (strcasecmp_m(name, "inode") == 0) {
1470 if (determine_size) {
1471 p = talloc_asprintf(
1472 ctx,
1473 "%.0f",
1474 (double) ino);
1475 if (!p) {
1476 errno = ENOMEM;
1477 return -1;
1479 n = strlen(p);
1480 } else {
1481 n = snprintf(buf, bufsize,
1482 "%.0f",
1483 (double) ino);
1487 if (!determine_size && n > bufsize) {
1488 errno = ERANGE;
1489 return -1;
1491 buf += n;
1492 n_used += n;
1493 bufsize -= n;
1494 n = 0;
1497 /* Restore name pointer to its original value */
1498 name -= 16;
1501 if (n_used == 0) {
1502 errno = ENOATTR;
1503 return -1;
1506 return n_used;
1509 /*****************************************************
1510 set the ACLs on a file given an ascii description
1511 *******************************************************/
1512 static int
1513 cacl_set(SMBCCTX *context,
1514 TALLOC_CTX *ctx,
1515 struct cli_state *cli,
1516 struct cli_state *ipc_cli,
1517 struct policy_handle *pol,
1518 const char *filename,
1519 char *the_acl,
1520 int mode,
1521 int flags)
1523 uint16_t fnum = (uint16_t)-1;
1524 int err = 0;
1525 struct security_descriptor *sd = NULL, *old;
1526 struct security_acl *dacl = NULL;
1527 struct dom_sid *owner_sid = NULL;
1528 struct dom_sid *group_sid = NULL;
1529 uint32_t i, j;
1530 size_t sd_size;
1531 int ret = 0;
1532 char *p;
1533 bool numeric = True;
1534 char *targetpath = NULL;
1535 struct cli_state *targetcli = NULL;
1536 struct cli_credentials *creds = NULL;
1537 NTSTATUS status;
1539 /* the_acl will be null for REMOVE_ALL operations */
1540 if (the_acl) {
1541 numeric = ((p = strchr(the_acl, ':')) != NULL &&
1542 p > the_acl &&
1543 p[-1] != '+');
1545 /* if this is to set the entire ACL... */
1546 if (*the_acl == '*') {
1547 /* ... then increment past the first colon */
1548 the_acl = p + 1;
1551 sd = sec_desc_parse(ctx, ipc_cli, pol, numeric, the_acl);
1552 if (!sd) {
1553 errno = EINVAL;
1554 return -1;
1558 /* SMBC_XATTR_MODE_REMOVE_ALL is the only caller
1559 that doesn't deref sd */
1561 if (!sd && (mode != SMBC_XATTR_MODE_REMOVE_ALL)) {
1562 errno = EINVAL;
1563 return -1;
1566 creds = context->internal->creds;
1568 status = cli_resolve_path(ctx, "",
1569 creds,
1570 cli, filename, &targetcli, &targetpath);
1571 if (!NT_STATUS_IS_OK(status)) {
1572 DEBUG(5,("cacl_set: Could not resolve %s\n", filename));
1573 errno = ENOENT;
1574 return -1;
1577 /* The desired access below is the only one I could find that works
1578 with NT4, W2KP and Samba */
1580 status = cli_ntcreate(
1581 targetcli, /* cli */
1582 targetpath, /* fname */
1583 0, /* CreatFlags */
1584 READ_CONTROL_ACCESS, /* DesiredAccess */
1585 0, /* FileAttributes */
1586 FILE_SHARE_READ|
1587 FILE_SHARE_WRITE, /* ShareAccess */
1588 FILE_OPEN, /* CreateDisposition */
1589 0x0, /* CreateOptions */
1590 0x0, /* SecurityFlags */
1591 &fnum, /* pfid */
1592 NULL); /* cr */
1593 if (!NT_STATUS_IS_OK(status)) {
1594 DEBUG(5, ("cacl_set failed to open %s: %s\n",
1595 targetpath, nt_errstr(status)));
1596 errno = 0;
1597 return -1;
1600 status = cli_query_secdesc(targetcli, fnum, ctx, &old);
1601 if (!NT_STATUS_IS_OK(status)) {
1602 DEBUG(5,("cacl_set Failed to query old descriptor of %s: %s\n",
1603 targetpath, nt_errstr(status)));
1604 errno = 0;
1605 return -1;
1608 cli_close(targetcli, fnum);
1610 switch (mode) {
1611 case SMBC_XATTR_MODE_REMOVE_ALL:
1612 old->dacl->num_aces = 0;
1613 dacl = old->dacl;
1614 break;
1616 case SMBC_XATTR_MODE_REMOVE:
1617 for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
1618 bool found = False;
1620 for (j=0;old->dacl && j<old->dacl->num_aces;j++) {
1621 if (security_ace_equal(&sd->dacl->aces[i],
1622 &old->dacl->aces[j])) {
1623 uint32_t k;
1624 for (k=j; k<old->dacl->num_aces-1;k++) {
1625 old->dacl->aces[k] =
1626 old->dacl->aces[k+1];
1628 old->dacl->num_aces--;
1629 found = True;
1630 dacl = old->dacl;
1631 break;
1635 if (!found) {
1636 err = ENOATTR;
1637 ret = -1;
1638 goto failed;
1641 break;
1643 case SMBC_XATTR_MODE_ADD:
1644 for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
1645 bool found = False;
1647 for (j=0;old->dacl && j<old->dacl->num_aces;j++) {
1648 if (dom_sid_equal(&sd->dacl->aces[i].trustee,
1649 &old->dacl->aces[j].trustee)) {
1650 if (!(flags & SMBC_XATTR_FLAG_CREATE)) {
1651 err = EEXIST;
1652 ret = -1;
1653 goto failed;
1655 old->dacl->aces[j] = sd->dacl->aces[i];
1656 ret = -1;
1657 found = True;
1661 if (!found && (flags & SMBC_XATTR_FLAG_REPLACE)) {
1662 err = ENOATTR;
1663 ret = -1;
1664 goto failed;
1667 for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
1668 add_ace(&old->dacl, &sd->dacl->aces[i], ctx);
1671 dacl = old->dacl;
1672 break;
1674 case SMBC_XATTR_MODE_SET:
1675 old = sd;
1676 owner_sid = old->owner_sid;
1677 group_sid = old->group_sid;
1678 dacl = old->dacl;
1679 break;
1681 case SMBC_XATTR_MODE_CHOWN:
1682 owner_sid = sd->owner_sid;
1683 break;
1685 case SMBC_XATTR_MODE_CHGRP:
1686 group_sid = sd->group_sid;
1687 break;
1690 /* Denied ACE entries must come before allowed ones */
1691 sort_acl(old->dacl);
1693 /* Create new security descriptor and set it */
1694 sd = make_sec_desc(ctx, old->revision, SEC_DESC_SELF_RELATIVE,
1695 owner_sid, group_sid, NULL, dacl, &sd_size);
1697 status = cli_ntcreate(targetcli, targetpath, 0,
1698 WRITE_DAC_ACCESS | WRITE_OWNER_ACCESS, 0,
1699 FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_OPEN,
1700 0x0, 0x0, &fnum, NULL);
1701 if (!NT_STATUS_IS_OK(status)) {
1702 DEBUG(5, ("cacl_set failed to open %s: %s\n",
1703 targetpath, nt_errstr(status)));
1704 errno = 0;
1705 return -1;
1708 status = cli_set_secdesc(targetcli, fnum, sd);
1709 if (!NT_STATUS_IS_OK(status)) {
1710 DEBUG(5, ("ERROR: secdesc set failed: %s\n",
1711 nt_errstr(status)));
1712 ret = -1;
1715 /* Clean up */
1717 failed:
1718 cli_close(targetcli, fnum);
1720 if (err != 0) {
1721 errno = err;
1724 return ret;
1729 SMBC_setxattr_ctx(SMBCCTX *context,
1730 const char *fname,
1731 const char *name,
1732 const void *value,
1733 size_t size,
1734 int flags)
1736 int ret;
1737 int ret2;
1738 SMBCSRV *srv = NULL;
1739 SMBCSRV *ipc_srv = NULL;
1740 char *server = NULL;
1741 char *share = NULL;
1742 char *user = NULL;
1743 char *password = NULL;
1744 char *workgroup = NULL;
1745 char *path = NULL;
1746 struct DOS_ATTR_DESC *dad = NULL;
1747 struct {
1748 const char * create_time_attr;
1749 const char * access_time_attr;
1750 const char * write_time_attr;
1751 const char * change_time_attr;
1752 } attr_strings;
1753 uint16_t port = 0;
1754 TALLOC_CTX *frame = talloc_stackframe();
1756 if (!context || !context->internal->initialized) {
1757 errno = EINVAL; /* Best I can think of ... */
1758 TALLOC_FREE(frame);
1759 return -1;
1762 if (!fname) {
1763 errno = EINVAL;
1764 TALLOC_FREE(frame);
1765 return -1;
1768 DEBUG(4, ("smbc_setxattr(%s, %s, %.*s)\n",
1769 fname, name, (int) size, (const char*)value));
1771 if (SMBC_parse_path(frame,
1772 context,
1773 fname,
1774 &workgroup,
1775 &server,
1776 &port,
1777 &share,
1778 &path,
1779 &user,
1780 &password,
1781 NULL)) {
1782 errno = EINVAL;
1783 TALLOC_FREE(frame);
1784 return -1;
1787 if (!user || user[0] == (char)0) {
1788 user = talloc_strdup(frame, smbc_getUser(context));
1789 if (!user) {
1790 errno = ENOMEM;
1791 TALLOC_FREE(frame);
1792 return -1;
1796 srv = SMBC_server(frame, context, True,
1797 server, port, share, &workgroup, &user, &password);
1798 if (!srv) {
1799 TALLOC_FREE(frame);
1800 return -1; /* errno set by SMBC_server */
1803 if (! srv->no_nt_session) {
1804 ipc_srv = SMBC_attr_server(frame, context, server, port, share,
1805 &workgroup, &user, &password);
1806 if (! ipc_srv) {
1807 srv->no_nt_session = True;
1809 } else {
1810 ipc_srv = NULL;
1814 * Are they asking to set the entire set of known attributes?
1816 if (strcasecmp_m(name, "system.*") == 0 ||
1817 strcasecmp_m(name, "system.*+") == 0) {
1818 /* Yup. */
1819 char *namevalue =
1820 talloc_asprintf(talloc_tos(), "%s:%s",
1821 name+7, (const char *) value);
1822 if (! namevalue) {
1823 errno = ENOMEM;
1824 ret = -1;
1825 TALLOC_FREE(frame);
1826 return -1;
1829 if (ipc_srv) {
1830 ret = cacl_set(context, talloc_tos(), srv->cli,
1831 ipc_srv->cli, &ipc_srv->pol, path,
1832 namevalue,
1833 (*namevalue == '*'
1834 ? SMBC_XATTR_MODE_SET
1835 : SMBC_XATTR_MODE_ADD),
1836 flags);
1837 } else {
1838 ret = 0;
1841 /* get a DOS Attribute Descriptor with current attributes */
1842 dad = dos_attr_query(context, talloc_tos(), path, srv);
1843 if (dad) {
1844 bool ok;
1846 /* Overwrite old with new, using what was provided */
1847 dos_attr_parse(context, dad, srv, namevalue);
1849 /* Set the new DOS attributes */
1850 ok = SMBC_setatr(
1851 context,
1852 srv,
1853 path,
1854 (struct timespec) {
1855 .tv_sec = dad->create_time },
1856 (struct timespec) {
1857 .tv_sec = dad->access_time },
1858 (struct timespec) {
1859 .tv_sec = dad->write_time },
1860 (struct timespec) {
1861 .tv_sec = dad->change_time },
1862 dad->mode);
1863 if (!ok) {
1864 /* cause failure if NT failed too */
1865 dad = NULL;
1869 /* we only fail if both NT and DOS sets failed */
1870 if (ret < 0 && ! dad) {
1871 ret = -1; /* in case dad was null */
1873 else {
1874 ret = 0;
1877 TALLOC_FREE(frame);
1878 return ret;
1882 * Are they asking to set an access control element or to set
1883 * the entire access control list?
1885 if (strcasecmp_m(name, "system.nt_sec_desc.*") == 0 ||
1886 strcasecmp_m(name, "system.nt_sec_desc.*+") == 0 ||
1887 strcasecmp_m(name, "system.nt_sec_desc.revision") == 0 ||
1888 strncasecmp_m(name, "system.nt_sec_desc.acl", 22) == 0 ||
1889 strncasecmp_m(name, "system.nt_sec_desc.acl+", 23) == 0) {
1891 /* Yup. */
1892 char *namevalue =
1893 talloc_asprintf(talloc_tos(), "%s:%s",
1894 name+19, (const char *) value);
1896 if (! ipc_srv) {
1897 ret = -1; /* errno set by SMBC_server() */
1899 else if (! namevalue) {
1900 errno = ENOMEM;
1901 ret = -1;
1902 } else {
1903 ret = cacl_set(context, talloc_tos(), srv->cli,
1904 ipc_srv->cli, &ipc_srv->pol, path,
1905 namevalue,
1906 (*namevalue == '*'
1907 ? SMBC_XATTR_MODE_SET
1908 : SMBC_XATTR_MODE_ADD),
1909 flags);
1911 TALLOC_FREE(frame);
1912 return ret;
1916 * Are they asking to set the owner?
1918 if (strcasecmp_m(name, "system.nt_sec_desc.owner") == 0 ||
1919 strcasecmp_m(name, "system.nt_sec_desc.owner+") == 0) {
1921 /* Yup. */
1922 char *namevalue =
1923 talloc_asprintf(talloc_tos(), "%s:%s",
1924 name+19, (const char *) value);
1926 if (! ipc_srv) {
1927 ret = -1; /* errno set by SMBC_server() */
1929 else if (! namevalue) {
1930 errno = ENOMEM;
1931 ret = -1;
1932 } else {
1933 ret = cacl_set(context, talloc_tos(), srv->cli,
1934 ipc_srv->cli, &ipc_srv->pol, path,
1935 namevalue, SMBC_XATTR_MODE_CHOWN, 0);
1937 TALLOC_FREE(frame);
1938 return ret;
1942 * Are they asking to set the group?
1944 if (strcasecmp_m(name, "system.nt_sec_desc.group") == 0 ||
1945 strcasecmp_m(name, "system.nt_sec_desc.group+") == 0) {
1947 /* Yup. */
1948 char *namevalue =
1949 talloc_asprintf(talloc_tos(), "%s:%s",
1950 name+19, (const char *) value);
1952 if (! ipc_srv) {
1953 /* errno set by SMBC_server() */
1954 ret = -1;
1956 else if (! namevalue) {
1957 errno = ENOMEM;
1958 ret = -1;
1959 } else {
1960 ret = cacl_set(context, talloc_tos(), srv->cli,
1961 ipc_srv->cli, &ipc_srv->pol, path,
1962 namevalue, SMBC_XATTR_MODE_CHGRP, 0);
1964 TALLOC_FREE(frame);
1965 return ret;
1968 /* Determine whether to use old-style or new-style attribute names */
1969 if (context->internal->full_time_names) {
1970 /* new-style names */
1971 attr_strings.create_time_attr = "system.dos_attr.CREATE_TIME";
1972 attr_strings.access_time_attr = "system.dos_attr.ACCESS_TIME";
1973 attr_strings.write_time_attr = "system.dos_attr.WRITE_TIME";
1974 attr_strings.change_time_attr = "system.dos_attr.CHANGE_TIME";
1975 } else {
1976 /* old-style names */
1977 attr_strings.create_time_attr = NULL;
1978 attr_strings.access_time_attr = "system.dos_attr.A_TIME";
1979 attr_strings.write_time_attr = "system.dos_attr.M_TIME";
1980 attr_strings.change_time_attr = "system.dos_attr.C_TIME";
1984 * Are they asking to set a DOS attribute?
1986 if (strcasecmp_m(name, "system.dos_attr.*") == 0 ||
1987 strcasecmp_m(name, "system.dos_attr.mode") == 0 ||
1988 (attr_strings.create_time_attr != NULL &&
1989 strcasecmp_m(name, attr_strings.create_time_attr) == 0) ||
1990 strcasecmp_m(name, attr_strings.access_time_attr) == 0 ||
1991 strcasecmp_m(name, attr_strings.write_time_attr) == 0 ||
1992 strcasecmp_m(name, attr_strings.change_time_attr) == 0) {
1994 /* get a DOS Attribute Descriptor with current attributes */
1995 dad = dos_attr_query(context, talloc_tos(), path, srv);
1996 if (dad) {
1997 char *namevalue =
1998 talloc_asprintf(talloc_tos(), "%s:%s",
1999 name+16, (const char *) value);
2000 if (! namevalue) {
2001 errno = ENOMEM;
2002 ret = -1;
2003 } else {
2004 /* Overwrite old with provided new params */
2005 dos_attr_parse(context, dad, srv, namevalue);
2007 /* Set the new DOS attributes */
2008 ret2 = SMBC_setatr(
2009 context,
2010 srv,
2011 path,
2012 (struct timespec) {
2013 .tv_sec = dad->create_time },
2014 (struct timespec) {
2015 .tv_sec = dad->access_time },
2016 (struct timespec) {
2017 .tv_sec = dad->write_time },
2018 (struct timespec) {
2019 .tv_sec = dad->change_time },
2020 dad->mode);
2022 /* ret2 has True (success) / False (failure) */
2023 if (ret2) {
2024 ret = 0;
2025 } else {
2026 ret = -1;
2029 } else {
2030 ret = -1;
2033 TALLOC_FREE(frame);
2034 return ret;
2037 /* Unsupported attribute name */
2038 errno = EINVAL;
2039 TALLOC_FREE(frame);
2040 return -1;
2044 SMBC_getxattr_ctx(SMBCCTX *context,
2045 const char *fname,
2046 const char *name,
2047 const void *value,
2048 size_t size)
2050 int ret;
2051 SMBCSRV *srv = NULL;
2052 SMBCSRV *ipc_srv = NULL;
2053 char *server = NULL;
2054 char *share = NULL;
2055 char *user = NULL;
2056 char *password = NULL;
2057 char *workgroup = NULL;
2058 char *path = NULL;
2059 struct {
2060 const char * create_time_attr;
2061 const char * access_time_attr;
2062 const char * write_time_attr;
2063 const char * change_time_attr;
2064 } attr_strings;
2065 uint16_t port = 0;
2066 TALLOC_CTX *frame = talloc_stackframe();
2068 if (!context || !context->internal->initialized) {
2069 errno = EINVAL; /* Best I can think of ... */
2070 TALLOC_FREE(frame);
2071 return -1;
2074 if (!fname) {
2075 errno = EINVAL;
2076 TALLOC_FREE(frame);
2077 return -1;
2080 DEBUG(4, ("smbc_getxattr(%s, %s)\n", fname, name));
2082 if (SMBC_parse_path(frame,
2083 context,
2084 fname,
2085 &workgroup,
2086 &server,
2087 &port,
2088 &share,
2089 &path,
2090 &user,
2091 &password,
2092 NULL)) {
2093 errno = EINVAL;
2094 TALLOC_FREE(frame);
2095 return -1;
2098 if (!user || user[0] == '\0') {
2099 user = talloc_strdup(frame, smbc_getUser(context));
2100 if (!user) {
2101 errno = ENOMEM;
2102 TALLOC_FREE(frame);
2103 return -1;
2107 srv = SMBC_server(frame, context, True,
2108 server, port, share, &workgroup, &user, &password);
2109 if (!srv) {
2110 TALLOC_FREE(frame);
2111 return -1; /* errno set by SMBC_server */
2114 if (! srv->no_nt_session) {
2115 ipc_srv = SMBC_attr_server(frame, context, server, port, share,
2116 &workgroup, &user, &password);
2118 * SMBC_attr_server() can cause the original
2119 * server to be removed from the cache.
2120 * If so we must error out here as the srv
2121 * pointer has been freed.
2123 if (smbc_getFunctionGetCachedServer(context)(context,
2124 server,
2125 share,
2126 workgroup,
2127 user) != srv) {
2128 #if defined(ECONNRESET)
2129 errno = ECONNRESET;
2130 #else
2131 errno = ETIMEDOUT;
2132 #endif
2133 TALLOC_FREE(frame);
2134 return -1;
2136 if (! ipc_srv) {
2137 srv->no_nt_session = True;
2139 } else {
2140 ipc_srv = NULL;
2143 /* Determine whether to use old-style or new-style attribute names */
2144 if (context->internal->full_time_names) {
2145 /* new-style names */
2146 attr_strings.create_time_attr = "system.dos_attr.CREATE_TIME";
2147 attr_strings.access_time_attr = "system.dos_attr.ACCESS_TIME";
2148 attr_strings.write_time_attr = "system.dos_attr.WRITE_TIME";
2149 attr_strings.change_time_attr = "system.dos_attr.CHANGE_TIME";
2150 } else {
2151 /* old-style names */
2152 attr_strings.create_time_attr = NULL;
2153 attr_strings.access_time_attr = "system.dos_attr.A_TIME";
2154 attr_strings.write_time_attr = "system.dos_attr.M_TIME";
2155 attr_strings.change_time_attr = "system.dos_attr.C_TIME";
2158 /* Are they requesting a supported attribute? */
2159 if (strcasecmp_m(name, "system.*") == 0 ||
2160 strncasecmp_m(name, "system.*!", 9) == 0 ||
2161 strcasecmp_m(name, "system.*+") == 0 ||
2162 strncasecmp_m(name, "system.*+!", 10) == 0 ||
2163 strcasecmp_m(name, "system.nt_sec_desc.*") == 0 ||
2164 strncasecmp_m(name, "system.nt_sec_desc.*!", 21) == 0 ||
2165 strcasecmp_m(name, "system.nt_sec_desc.*+") == 0 ||
2166 strncasecmp_m(name, "system.nt_sec_desc.*+!", 22) == 0 ||
2167 strcasecmp_m(name, "system.nt_sec_desc.revision") == 0 ||
2168 strcasecmp_m(name, "system.nt_sec_desc.owner") == 0 ||
2169 strcasecmp_m(name, "system.nt_sec_desc.owner+") == 0 ||
2170 strcasecmp_m(name, "system.nt_sec_desc.group") == 0 ||
2171 strcasecmp_m(name, "system.nt_sec_desc.group+") == 0 ||
2172 strncasecmp_m(name, "system.nt_sec_desc.acl", 22) == 0 ||
2173 strncasecmp_m(name, "system.nt_sec_desc.acl+", 23) == 0 ||
2174 strcasecmp_m(name, "system.dos_attr.*") == 0 ||
2175 strncasecmp_m(name, "system.dos_attr.*!", 18) == 0 ||
2176 strcasecmp_m(name, "system.dos_attr.mode") == 0 ||
2177 strcasecmp_m(name, "system.dos_attr.size") == 0 ||
2178 (attr_strings.create_time_attr != NULL &&
2179 strcasecmp_m(name, attr_strings.create_time_attr) == 0) ||
2180 strcasecmp_m(name, attr_strings.access_time_attr) == 0 ||
2181 strcasecmp_m(name, attr_strings.write_time_attr) == 0 ||
2182 strcasecmp_m(name, attr_strings.change_time_attr) == 0 ||
2183 strcasecmp_m(name, "system.dos_attr.inode") == 0) {
2185 /* Yup. */
2186 const char *filename = name;
2187 ret = cacl_get(context, talloc_tos(), srv,
2188 ipc_srv == NULL ? NULL : ipc_srv->cli,
2189 &ipc_srv->pol, path,
2190 filename,
2191 discard_const_p(char, value),
2192 size);
2193 TALLOC_FREE(frame);
2195 * static function cacl_get returns a value greater than zero
2196 * which is needed buffer size needed when size_t is 0.
2198 return ret;
2201 /* Unsupported attribute name */
2202 errno = EINVAL;
2203 TALLOC_FREE(frame);
2204 return -1;
2209 SMBC_removexattr_ctx(SMBCCTX *context,
2210 const char *fname,
2211 const char *name)
2213 int ret;
2214 SMBCSRV *srv = NULL;
2215 SMBCSRV *ipc_srv = NULL;
2216 char *server = NULL;
2217 char *share = NULL;
2218 char *user = NULL;
2219 char *password = NULL;
2220 char *workgroup = NULL;
2221 char *path = NULL;
2222 uint16_t port = 0;
2223 TALLOC_CTX *frame = talloc_stackframe();
2225 if (!context || !context->internal->initialized) {
2226 errno = EINVAL; /* Best I can think of ... */
2227 TALLOC_FREE(frame);
2228 return -1;
2231 if (!fname) {
2232 errno = EINVAL;
2233 TALLOC_FREE(frame);
2234 return -1;
2237 DEBUG(4, ("smbc_removexattr(%s, %s)\n", fname, name));
2239 if (SMBC_parse_path(frame,
2240 context,
2241 fname,
2242 &workgroup,
2243 &server,
2244 &port,
2245 &share,
2246 &path,
2247 &user,
2248 &password,
2249 NULL)) {
2250 errno = EINVAL;
2251 TALLOC_FREE(frame);
2252 return -1;
2255 if (!user || user[0] == (char)0) {
2256 user = talloc_strdup(frame, smbc_getUser(context));
2257 if (!user) {
2258 errno = ENOMEM;
2259 TALLOC_FREE(frame);
2260 return -1;
2264 srv = SMBC_server(frame, context, True,
2265 server, port, share, &workgroup, &user, &password);
2266 if (!srv) {
2267 TALLOC_FREE(frame);
2268 return -1; /* errno set by SMBC_server */
2271 if (! srv->no_nt_session) {
2272 int saved_errno;
2273 ipc_srv = SMBC_attr_server(frame, context, server, port, share,
2274 &workgroup, &user, &password);
2275 saved_errno = errno;
2277 * SMBC_attr_server() can cause the original
2278 * server to be removed from the cache.
2279 * If so we must error out here as the srv
2280 * pointer has been freed.
2282 if (smbc_getFunctionGetCachedServer(context)(context,
2283 server,
2284 share,
2285 workgroup,
2286 user) != srv) {
2287 #if defined(ECONNRESET)
2288 errno = ECONNRESET;
2289 #else
2290 errno = ETIMEDOUT;
2291 #endif
2292 TALLOC_FREE(frame);
2293 return -1;
2295 if (! ipc_srv) {
2296 errno = saved_errno;
2297 srv->no_nt_session = True;
2299 } else {
2300 ipc_srv = NULL;
2303 if (! ipc_srv) {
2304 TALLOC_FREE(frame);
2305 return -1; /* errno set by SMBC_attr_server */
2308 /* Are they asking to set the entire ACL? */
2309 if (strcasecmp_m(name, "system.nt_sec_desc.*") == 0 ||
2310 strcasecmp_m(name, "system.nt_sec_desc.*+") == 0) {
2312 /* Yup. */
2313 ret = cacl_set(context, talloc_tos(), srv->cli,
2314 ipc_srv->cli, &ipc_srv->pol, path,
2315 NULL, SMBC_XATTR_MODE_REMOVE_ALL, 0);
2316 TALLOC_FREE(frame);
2317 return ret;
2321 * Are they asking to remove one or more specific security descriptor
2322 * attributes?
2324 if (strcasecmp_m(name, "system.nt_sec_desc.revision") == 0 ||
2325 strcasecmp_m(name, "system.nt_sec_desc.owner") == 0 ||
2326 strcasecmp_m(name, "system.nt_sec_desc.owner+") == 0 ||
2327 strcasecmp_m(name, "system.nt_sec_desc.group") == 0 ||
2328 strcasecmp_m(name, "system.nt_sec_desc.group+") == 0 ||
2329 strncasecmp_m(name, "system.nt_sec_desc.acl", 22) == 0 ||
2330 strncasecmp_m(name, "system.nt_sec_desc.acl+", 23) == 0) {
2332 /* Yup. */
2333 ret = cacl_set(context, talloc_tos(), srv->cli,
2334 ipc_srv->cli, &ipc_srv->pol, path,
2335 discard_const_p(char, name) + 19,
2336 SMBC_XATTR_MODE_REMOVE, 0);
2337 TALLOC_FREE(frame);
2338 return ret;
2341 /* Unsupported attribute name */
2342 errno = EINVAL;
2343 TALLOC_FREE(frame);
2344 return -1;
2348 SMBC_listxattr_ctx(SMBCCTX *context,
2349 const char *fname,
2350 char *list,
2351 size_t size)
2354 * This isn't quite what listxattr() is supposed to do. This returns
2355 * the complete set of attribute names, always, rather than only those
2356 * attribute names which actually exist for a file. Hmmm...
2358 size_t retsize;
2359 static const char supported_old[] =
2360 "system.*\0"
2361 "system.*+\0"
2362 "system.nt_sec_desc.revision\0"
2363 "system.nt_sec_desc.owner\0"
2364 "system.nt_sec_desc.owner+\0"
2365 "system.nt_sec_desc.group\0"
2366 "system.nt_sec_desc.group+\0"
2367 "system.nt_sec_desc.acl.*\0"
2368 "system.nt_sec_desc.acl\0"
2369 "system.nt_sec_desc.acl+\0"
2370 "system.nt_sec_desc.*\0"
2371 "system.nt_sec_desc.*+\0"
2372 "system.dos_attr.*\0"
2373 "system.dos_attr.mode\0"
2374 "system.dos_attr.c_time\0"
2375 "system.dos_attr.a_time\0"
2376 "system.dos_attr.m_time\0"
2378 static const char supported_new[] =
2379 "system.*\0"
2380 "system.*+\0"
2381 "system.nt_sec_desc.revision\0"
2382 "system.nt_sec_desc.owner\0"
2383 "system.nt_sec_desc.owner+\0"
2384 "system.nt_sec_desc.group\0"
2385 "system.nt_sec_desc.group+\0"
2386 "system.nt_sec_desc.acl.*\0"
2387 "system.nt_sec_desc.acl\0"
2388 "system.nt_sec_desc.acl+\0"
2389 "system.nt_sec_desc.*\0"
2390 "system.nt_sec_desc.*+\0"
2391 "system.dos_attr.*\0"
2392 "system.dos_attr.mode\0"
2393 "system.dos_attr.create_time\0"
2394 "system.dos_attr.access_time\0"
2395 "system.dos_attr.write_time\0"
2396 "system.dos_attr.change_time\0"
2398 const char * supported;
2400 if (context->internal->full_time_names) {
2401 supported = supported_new;
2402 retsize = sizeof(supported_new);
2403 } else {
2404 supported = supported_old;
2405 retsize = sizeof(supported_old);
2408 if (size == 0) {
2409 return retsize;
2412 if (retsize > size) {
2413 errno = ERANGE;
2414 return -1;
2417 /* this can't be strcpy() because there are embedded null characters */
2418 memcpy(list, supported, retsize);
2419 return retsize;