Make sure we are still able to join Windows 2008.
[Samba.git] / source / libsmb / libsmbclient.c
blobfe008ed6b670dfac357a151689e3a02ba75adf86
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, 2004
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"
27 #include "include/libsmb_internal.h"
29 struct smbc_dirent *smbc_readdir_ctx(SMBCCTX *context, SMBCFILE *dir);
30 struct smbc_dir_list *smbc_check_dir_ent(struct smbc_dir_list *list,
31 struct smbc_dirent *dirent);
34 * DOS Attribute values (used internally)
36 typedef struct DOS_ATTR_DESC {
37 int mode;
38 SMB_OFF_T size;
39 time_t create_time;
40 time_t access_time;
41 time_t write_time;
42 time_t change_time;
43 SMB_INO_T inode;
44 } DOS_ATTR_DESC;
48 * Internal flags for extended attributes
51 /* internal mode values */
52 #define SMBC_XATTR_MODE_ADD 1
53 #define SMBC_XATTR_MODE_REMOVE 2
54 #define SMBC_XATTR_MODE_REMOVE_ALL 3
55 #define SMBC_XATTR_MODE_SET 4
56 #define SMBC_XATTR_MODE_CHOWN 5
57 #define SMBC_XATTR_MODE_CHGRP 6
59 #define CREATE_ACCESS_READ READ_CONTROL_ACCESS
61 /*We should test for this in configure ... */
62 #ifndef ENOTSUP
63 #define ENOTSUP EOPNOTSUPP
64 #endif
67 * Functions exported by libsmb_cache.c that we need here
69 int smbc_default_cache_functions(SMBCCTX *context);
71 /*
72 * check if an element is part of the list.
73 * FIXME: Does not belong here !
74 * Can anyone put this in a macro in dlinklist.h ?
75 * -- Tom
77 static int DLIST_CONTAINS(SMBCFILE * list, SMBCFILE *p) {
78 if (!p || !list) return False;
79 do {
80 if (p == list) return True;
81 list = list->next;
82 } while (list);
83 return False;
87 * Find an lsa pipe handle associated with a cli struct.
89 static struct rpc_pipe_client *
90 find_lsa_pipe_hnd(struct cli_state *ipc_cli)
92 struct rpc_pipe_client *pipe_hnd;
94 for (pipe_hnd = ipc_cli->pipe_list;
95 pipe_hnd;
96 pipe_hnd = pipe_hnd->next) {
98 if (pipe_hnd->pipe_idx == PI_LSARPC) {
99 return pipe_hnd;
103 return NULL;
106 static int
107 smbc_close_ctx(SMBCCTX *context,
108 SMBCFILE *file);
109 static off_t
110 smbc_lseek_ctx(SMBCCTX *context,
111 SMBCFILE *file,
112 off_t offset,
113 int whence);
115 extern bool in_client;
118 * Is the logging working / configfile read ?
120 static int smbc_initialized = 0;
122 static int
123 hex2int( unsigned int _char )
125 if ( _char >= 'A' && _char <='F')
126 return _char - 'A' + 10;
127 if ( _char >= 'a' && _char <='f')
128 return _char - 'a' + 10;
129 if ( _char >= '0' && _char <='9')
130 return _char - '0';
131 return -1;
135 * smbc_urldecode()
136 * and smbc_urldecode_talloc() (internal fn.)
138 * Convert strings of %xx to their single character equivalent. Each 'x' must
139 * be a valid hexadecimal digit, or that % sequence is left undecoded.
141 * dest may, but need not be, the same pointer as src.
143 * Returns the number of % sequences which could not be converted due to lack
144 * of two following hexadecimal digits.
146 static int
147 smbc_urldecode_talloc(TALLOC_CTX *ctx, char **pp_dest, const char *src)
149 int old_length = strlen(src);
150 int i = 0;
151 int err_count = 0;
152 size_t newlen = 1;
153 char *p, *dest;
155 if (old_length == 0) {
156 return 0;
159 *pp_dest = NULL;
160 for (i = 0; i < old_length; ) {
161 unsigned char character = src[i++];
163 if (character == '%') {
164 int a = i+1 < old_length ? hex2int(src[i]) : -1;
165 int b = i+1 < old_length ? hex2int(src[i+1]) : -1;
167 /* Replace valid sequence */
168 if (a != -1 && b != -1) {
169 /* Replace valid %xx sequence with %dd */
170 character = (a * 16) + b;
171 if (character == '\0') {
172 break; /* Stop at %00 */
174 i += 2;
175 } else {
176 err_count++;
179 newlen++;
182 dest = TALLOC_ARRAY(ctx, char, newlen);
183 if (!dest) {
184 return err_count;
187 err_count = 0;
188 for (p = dest, i = 0; i < old_length; ) {
189 unsigned char character = src[i++];
191 if (character == '%') {
192 int a = i+1 < old_length ? hex2int(src[i]) : -1;
193 int b = i+1 < old_length ? hex2int(src[i+1]) : -1;
195 /* Replace valid sequence */
196 if (a != -1 && b != -1) {
197 /* Replace valid %xx sequence with %dd */
198 character = (a * 16) + b;
199 if (character == '\0') {
200 break; /* Stop at %00 */
202 i += 2;
203 } else {
204 err_count++;
207 *p++ = character;
210 *p = '\0';
211 *pp_dest = dest;
212 return err_count;
216 smbc_urldecode(char *dest, char *src, size_t max_dest_len)
218 TALLOC_CTX *frame = talloc_stackframe();
219 char *pdest;
220 int ret = smbc_urldecode_talloc(frame, &pdest, src);
222 if (pdest) {
223 strlcpy(dest, pdest, max_dest_len);
225 TALLOC_FREE(frame);
226 return ret;
230 * smbc_urlencode()
232 * Convert any characters not specifically allowed in a URL into their %xx
233 * equivalent.
235 * Returns the remaining buffer length.
238 smbc_urlencode(char *dest, char *src, int max_dest_len)
240 char hex[] = "0123456789ABCDEF";
242 for (; *src != '\0' && max_dest_len >= 3; src++) {
244 if ((*src < '0' &&
245 *src != '-' &&
246 *src != '.') ||
247 (*src > '9' &&
248 *src < 'A') ||
249 (*src > 'Z' &&
250 *src < 'a' &&
251 *src != '_') ||
252 (*src > 'z')) {
253 *dest++ = '%';
254 *dest++ = hex[(*src >> 4) & 0x0f];
255 *dest++ = hex[*src & 0x0f];
256 max_dest_len -= 3;
257 } else {
258 *dest++ = *src;
259 max_dest_len--;
263 *dest++ = '\0';
264 max_dest_len--;
266 return max_dest_len;
270 * Function to parse a path and turn it into components
272 * The general format of an SMB URI is explain in Christopher Hertel's CIFS
273 * book, at http://ubiqx.org/cifs/Appendix-D.html. We accept a subset of the
274 * general format ("smb:" only; we do not look for "cifs:").
277 * We accept:
278 * smb://[[[domain;]user[:password]@]server[/share[/path[/file]]]][?options]
280 * Meaning of URLs:
282 * smb:// Show all workgroups.
284 * The method of locating the list of workgroups varies
285 * depending upon the setting of the context variable
286 * context->options.browse_max_lmb_count. This value
287 * determine the maximum number of local master browsers to
288 * query for the list of workgroups. In order to ensure that
289 * a complete list of workgroups is obtained, all master
290 * browsers must be queried, but if there are many
291 * workgroups, the time spent querying can begin to add up.
292 * For small networks (not many workgroups), it is suggested
293 * that this variable be set to 0, indicating query all local
294 * master browsers. When the network has many workgroups, a
295 * reasonable setting for this variable might be around 3.
297 * smb://name/ if name<1D> or name<1B> exists, list servers in
298 * workgroup, else, if name<20> exists, list all shares
299 * for server ...
301 * If "options" are provided, this function returns the entire option list as a
302 * string, for later parsing by the caller. Note that currently, no options
303 * are supported.
306 static const char *smbc_prefix = "smb:";
308 static int
309 smbc_parse_path(TALLOC_CTX *ctx,
310 SMBCCTX *context,
311 const char *fname,
312 char **pp_workgroup,
313 char **pp_server,
314 char **pp_share,
315 char **pp_path,
316 char **pp_user,
317 char **pp_password,
318 char **pp_options)
320 char *s;
321 const char *p;
322 char *q, *r;
323 int len;
325 /* Ensure these returns are at least valid pointers. */
326 *pp_server = talloc_strdup(ctx, "");
327 *pp_share = talloc_strdup(ctx, "");
328 *pp_path = talloc_strdup(ctx, "");
329 *pp_user = talloc_strdup(ctx, "");
330 *pp_password = talloc_strdup(ctx, "");
332 if (!*pp_server || !*pp_share || !*pp_path ||
333 !*pp_user || !*pp_password) {
334 return -1;
338 * Assume we wont find an authentication domain to parse, so default
339 * to the workgroup in the provided context.
341 if (pp_workgroup != NULL) {
342 *pp_workgroup = talloc_strdup(ctx, context->workgroup);
345 if (pp_options) {
346 *pp_options = talloc_strdup(ctx, "");
348 s = talloc_strdup(ctx, fname);
350 /* see if it has the right prefix */
351 len = strlen(smbc_prefix);
352 if (strncmp(s,smbc_prefix,len) || (s[len] != '/' && s[len] != 0)) {
353 return -1; /* What about no smb: ? */
356 p = s + len;
358 /* Watch the test below, we are testing to see if we should exit */
360 if (strncmp(p, "//", 2) && strncmp(p, "\\\\", 2)) {
361 DEBUG(1, ("Invalid path (does not begin with smb://"));
362 return -1;
365 p += 2; /* Skip the double slash */
367 /* See if any options were specified */
368 if ((q = strrchr(p, '?')) != NULL ) {
369 /* There are options. Null terminate here and point to them */
370 *q++ = '\0';
372 DEBUG(4, ("Found options '%s'", q));
374 /* Copy the options */
375 if (*pp_options != NULL) {
376 TALLOC_FREE(*pp_options);
377 *pp_options = talloc_strdup(ctx, q);
381 if (*p == '\0') {
382 goto decoding;
385 if (*p == '/') {
386 int wl = strlen(context->workgroup);
388 if (wl > 16) {
389 wl = 16;
392 *pp_server = talloc_strdup(ctx, context->workgroup);
393 if (!*pp_server) {
394 return -1;
396 *pp_server[wl] = '\0';
397 return 0;
401 * ok, its for us. Now parse out the server, share etc.
403 * However, we want to parse out [[domain;]user[:password]@] if it
404 * exists ...
407 /* check that '@' occurs before '/', if '/' exists at all */
408 q = strchr_m(p, '@');
409 r = strchr_m(p, '/');
410 if (q && (!r || q < r)) {
411 char *userinfo = NULL;
412 const char *u;
414 next_token_no_ltrim_talloc(ctx, &p, &userinfo, "@");
415 if (!userinfo) {
416 return -1;
418 u = userinfo;
420 if (strchr_m(u, ';')) {
421 char *workgroup;
422 next_token_no_ltrim_talloc(ctx, &u, &workgroup, ";");
423 if (!workgroup) {
424 return -1;
426 if (pp_workgroup) {
427 *pp_workgroup = workgroup;
431 if (strchr_m(u, ':')) {
432 next_token_no_ltrim_talloc(ctx, &u, pp_user, ":");
433 if (!*pp_user) {
434 return -1;
436 *pp_password = talloc_strdup(ctx, u);
437 if (!*pp_password) {
438 return -1;
440 } else {
441 *pp_user = talloc_strdup(ctx, u);
442 if (!*pp_user) {
443 return -1;
448 if (!next_token_talloc(ctx, &p, pp_server, "/")) {
449 return -1;
452 if (*p == (char)0) {
453 goto decoding; /* That's it ... */
456 if (!next_token_talloc(ctx, &p, pp_share, "/")) {
457 return -1;
461 * Prepend a leading slash if there's a file path, as required by
462 * NetApp filers.
464 if (*p != '\0') {
465 *pp_path = talloc_asprintf(ctx,
466 "\\%s",
468 } else {
469 *pp_path = talloc_strdup(ctx, "");
471 if (!*pp_path) {
472 return -1;
474 string_replace(*pp_path, '/', '\\');
476 decoding:
478 (void) smbc_urldecode_talloc(ctx, pp_path, *pp_path);
479 (void) smbc_urldecode_talloc(ctx, pp_server, *pp_server);
480 (void) smbc_urldecode_talloc(ctx, pp_share, *pp_share);
481 (void) smbc_urldecode_talloc(ctx, pp_user, *pp_user);
482 (void) smbc_urldecode_talloc(ctx, pp_password, *pp_password);
484 return 0;
488 * Verify that the options specified in a URL are valid
490 static int
491 smbc_check_options(char *server,
492 char *share,
493 char *path,
494 char *options)
496 DEBUG(4, ("smbc_check_options(): server='%s' share='%s' "
497 "path='%s' options='%s'\n",
498 server, share, path, options));
500 /* No options at all is always ok */
501 if (! *options) return 0;
503 /* Currently, we don't support any options. */
504 return -1;
508 * Convert an SMB error into a UNIX error ...
510 static int
511 smbc_errno(SMBCCTX *context,
512 struct cli_state *c)
514 int ret = cli_errno(c);
516 if (cli_is_dos_error(c)) {
517 uint8 eclass;
518 uint32 ecode;
520 cli_dos_error(c, &eclass, &ecode);
522 DEBUG(3,("smbc_error %d %d (0x%x) -> %d\n",
523 (int)eclass, (int)ecode, (int)ecode, ret));
524 } else {
525 NTSTATUS status;
527 status = cli_nt_error(c);
529 DEBUG(3,("smbc errno %s -> %d\n",
530 nt_errstr(status), ret));
533 return ret;
537 * Check a server for being alive and well.
538 * returns 0 if the server is in shape. Returns 1 on error
540 * Also useable outside libsmbclient to enable external cache
541 * to do some checks too.
543 static int
544 smbc_check_server(SMBCCTX * context,
545 SMBCSRV * server)
547 socklen_t size;
548 struct sockaddr addr;
550 size = sizeof(addr);
551 return (getpeername(server->cli->fd, &addr, &size) == -1);
555 * Remove a server from the cached server list it's unused.
556 * On success, 0 is returned. 1 is returned if the server could not be removed.
558 * Also useable outside libsmbclient
561 smbc_remove_unused_server(SMBCCTX * context,
562 SMBCSRV * srv)
564 SMBCFILE * file;
566 /* are we being fooled ? */
567 if (!context || !context->internal ||
568 !context->internal->_initialized || !srv) return 1;
571 /* Check all open files/directories for a relation with this server */
572 for (file = context->internal->_files; file; file=file->next) {
573 if (file->srv == srv) {
574 /* Still used */
575 DEBUG(3, ("smbc_remove_usused_server: "
576 "%p still used by %p.\n",
577 srv, file));
578 return 1;
582 DLIST_REMOVE(context->internal->_servers, srv);
584 cli_shutdown(srv->cli);
585 srv->cli = NULL;
587 DEBUG(3, ("smbc_remove_usused_server: %p removed.\n", srv));
589 (context->callbacks.remove_cached_srv_fn)(context, srv);
591 SAFE_FREE(srv);
592 return 0;
595 /****************************************************************
596 * Call the auth_fn with fixed size (fstring) buffers.
597 ***************************************************************/
599 static void call_auth_fn(TALLOC_CTX *ctx,
600 SMBCCTX *context,
601 const char *server,
602 const char *share,
603 char **pp_workgroup,
604 char **pp_username,
605 char **pp_password)
607 fstring workgroup;
608 fstring username;
609 fstring password;
611 strlcpy(workgroup, *pp_workgroup, sizeof(workgroup));
612 strlcpy(username, *pp_username, sizeof(username));
613 strlcpy(password, *pp_password, sizeof(password));
615 if (context->internal->_auth_fn_with_context != NULL) {
616 (context->internal->_auth_fn_with_context)(
617 context,
618 server, share,
619 workgroup, sizeof(workgroup),
620 username, sizeof(username),
621 password, sizeof(password));
622 } else {
623 (context->callbacks.auth_fn)(
624 server, share,
625 workgroup, sizeof(workgroup),
626 username, sizeof(username),
627 password, sizeof(password));
630 TALLOC_FREE(*pp_workgroup);
631 TALLOC_FREE(*pp_username);
632 TALLOC_FREE(*pp_password);
634 *pp_workgroup = talloc_strdup(ctx, workgroup);
635 *pp_username = talloc_strdup(ctx, username);
636 *pp_password = talloc_strdup(ctx, password);
639 static SMBCSRV *
640 find_server(TALLOC_CTX *ctx,
641 SMBCCTX *context,
642 const char *server,
643 const char *share,
644 char **pp_workgroup,
645 char **pp_username,
646 char **pp_password)
648 SMBCSRV *srv;
649 int auth_called = 0;
651 check_server_cache:
653 srv = (context->callbacks.get_cached_srv_fn)(context, server, share,
654 *pp_workgroup, *pp_username);
656 if (!auth_called && !srv && (!*pp_username || !(*pp_username)[0] ||
657 !*pp_password || !(*pp_password)[0])) {
658 call_auth_fn(ctx, context, server, share,
659 pp_workgroup, pp_username, pp_password);
661 if (!pp_workgroup || !pp_username || !pp_password) {
662 return NULL;
666 * However, smbc_auth_fn may have picked up info relating to
667 * an existing connection, so try for an existing connection
668 * again ...
670 auth_called = 1;
671 goto check_server_cache;
675 if (srv) {
676 if ((context->callbacks.check_server_fn)(context, srv)) {
678 * This server is no good anymore
679 * Try to remove it and check for more possible
680 * servers in the cache
682 if ((context->callbacks.remove_unused_server_fn)(context,
683 srv)) {
685 * We could not remove the server completely,
686 * remove it from the cache so we will not get
687 * it again. It will be removed when the last
688 * file/dir is closed.
690 (context->callbacks.remove_cached_srv_fn)(context,
691 srv);
695 * Maybe there are more cached connections to this
696 * server
698 goto check_server_cache;
701 return srv;
704 return NULL;
708 * Connect to a server, possibly on an existing connection
710 * Here, what we want to do is: If the server and username
711 * match an existing connection, reuse that, otherwise, establish a
712 * new connection.
714 * If we have to create a new connection, call the auth_fn to get the
715 * info we need, unless the username and password were passed in.
718 static SMBCSRV *
719 smbc_server(TALLOC_CTX *ctx,
720 SMBCCTX *context,
721 bool connect_if_not_found,
722 const char *server,
723 const char *share,
724 char **pp_workgroup,
725 char **pp_username,
726 char **pp_password)
728 SMBCSRV *srv=NULL;
729 struct cli_state *c;
730 struct nmb_name called, calling;
731 const char *server_n = server;
732 struct sockaddr_storage ss;
733 int tried_reverse = 0;
734 int port_try_first;
735 int port_try_next;
736 const char *username_used;
737 NTSTATUS status;
739 zero_addr(&ss);
740 ZERO_STRUCT(c);
742 if (server[0] == 0) {
743 errno = EPERM;
744 return NULL;
747 /* Look for a cached connection */
748 srv = find_server(ctx, context, server, share,
749 pp_workgroup, pp_username, pp_password);
752 * If we found a connection and we're only allowed one share per
753 * server...
755 if (srv && *share != '\0' && context->options.one_share_per_server) {
758 * ... then if there's no current connection to the share,
759 * connect to it. find_server(), or rather the function
760 * pointed to by context->callbacks.get_cached_srv_fn which
761 * was called by find_server(), will have issued a tree
762 * disconnect if the requested share is not the same as the
763 * one that was already connected.
765 if (srv->cli->cnum == (uint16) -1) {
766 /* Ensure we have accurate auth info */
767 call_auth_fn(ctx, context, server, share,
768 pp_workgroup, pp_username, pp_password);
770 if (!*pp_workgroup || !*pp_username || !*pp_password) {
771 errno = ENOMEM;
772 cli_shutdown(srv->cli);
773 srv->cli = NULL;
774 (context->callbacks.remove_cached_srv_fn)(context,
775 srv);
776 return NULL;
780 * We don't need to renegotiate encryption
781 * here as the encryption context is not per
782 * tid.
785 if (!cli_send_tconX(srv->cli, share, "?????",
786 *pp_password,
787 strlen(*pp_password)+1)) {
789 errno = smbc_errno(context, srv->cli);
790 cli_shutdown(srv->cli);
791 srv->cli = NULL;
792 (context->callbacks.remove_cached_srv_fn)(context,
793 srv);
794 srv = NULL;
798 * Regenerate the dev value since it's based on both
799 * server and share
801 if (srv) {
802 srv->dev = (dev_t)(str_checksum(server) ^
803 str_checksum(share));
808 /* If we have a connection... */
809 if (srv) {
811 /* ... then we're done here. Give 'em what they came for. */
812 return srv;
815 /* If we're not asked to connect when a connection doesn't exist... */
816 if (! connect_if_not_found) {
817 /* ... then we're done here. */
818 return NULL;
821 if (!*pp_workgroup || !*pp_username || !*pp_password) {
822 errno = ENOMEM;
823 return NULL;
826 make_nmb_name(&calling, context->netbios_name, 0x0);
827 make_nmb_name(&called , server, 0x20);
829 DEBUG(4,("smbc_server: server_n=[%s] server=[%s]\n", server_n, server));
831 DEBUG(4,(" -> server_n=[%s] server=[%s]\n", server_n, server));
833 again:
835 zero_addr(&ss);
837 /* have to open a new connection */
838 if ((c = cli_initialise()) == NULL) {
839 errno = ENOMEM;
840 return NULL;
843 if (context->flags & SMB_CTX_FLAG_USE_KERBEROS) {
844 c->use_kerberos = True;
846 if (context->flags & SMB_CTX_FLAG_FALLBACK_AFTER_KERBEROS) {
847 c->fallback_after_kerberos = True;
850 c->timeout = context->timeout;
853 * Force use of port 139 for first try if share is $IPC, empty, or
854 * null, so browse lists can work
856 if (share == NULL || *share == '\0' || strcmp(share, "IPC$") == 0) {
857 port_try_first = 139;
858 port_try_next = 445;
859 } else {
860 port_try_first = 445;
861 port_try_next = 139;
864 c->port = port_try_first;
866 status = cli_connect(c, server_n, &ss);
867 if (!NT_STATUS_IS_OK(status)) {
869 /* First connection attempt failed. Try alternate port. */
870 c->port = port_try_next;
872 status = cli_connect(c, server_n, &ss);
873 if (!NT_STATUS_IS_OK(status)) {
874 cli_shutdown(c);
875 errno = ETIMEDOUT;
876 return NULL;
880 if (!cli_session_request(c, &calling, &called)) {
881 cli_shutdown(c);
882 if (strcmp(called.name, "*SMBSERVER")) {
883 make_nmb_name(&called , "*SMBSERVER", 0x20);
884 goto again;
885 } else { /* Try one more time, but ensure we don't loop */
887 /* Only try this if server is an IP address ... */
889 if (is_ipaddress(server) && !tried_reverse) {
890 fstring remote_name;
891 struct sockaddr_storage rem_ss;
893 if (!interpret_string_addr(&rem_ss, server,
894 NI_NUMERICHOST)) {
895 DEBUG(4, ("Could not convert IP address "
896 "%s to struct sockaddr_storage\n",
897 server));
898 errno = ETIMEDOUT;
899 return NULL;
902 tried_reverse++; /* Yuck */
904 if (name_status_find("*", 0, 0, &rem_ss, remote_name)) {
905 make_nmb_name(&called, remote_name, 0x20);
906 goto again;
910 errno = ETIMEDOUT;
911 return NULL;
914 DEBUG(4,(" session request ok\n"));
916 if (!cli_negprot(c)) {
917 cli_shutdown(c);
918 errno = ETIMEDOUT;
919 return NULL;
922 username_used = *pp_username;
924 if (!NT_STATUS_IS_OK(cli_session_setup(c, username_used,
925 *pp_password, strlen(*pp_password),
926 *pp_password, strlen(*pp_password),
927 *pp_workgroup))) {
929 /* Failed. Try an anonymous login, if allowed by flags. */
930 username_used = "";
932 if ((context->flags & SMBCCTX_FLAG_NO_AUTO_ANONYMOUS_LOGON) ||
933 !NT_STATUS_IS_OK(cli_session_setup(c, username_used,
934 *pp_password, 1,
935 *pp_password, 0,
936 *pp_workgroup))) {
938 cli_shutdown(c);
939 errno = EPERM;
940 return NULL;
944 DEBUG(4,(" session setup ok\n"));
946 if (!cli_send_tconX(c, share, "?????",
947 *pp_password, strlen(*pp_password)+1)) {
948 errno = smbc_errno(context, c);
949 cli_shutdown(c);
950 return NULL;
953 DEBUG(4,(" tconx ok\n"));
955 if (context->internal->_smb_encryption_level) {
956 /* Attempt UNIX smb encryption. */
957 if (!NT_STATUS_IS_OK(cli_force_encryption(c,
958 username_used,
959 *pp_password,
960 *pp_workgroup))) {
963 * context->internal->_smb_encryption_level == 1
964 * means don't fail if encryption can't be negotiated,
965 * == 2 means fail if encryption can't be negotiated.
968 DEBUG(4,(" SMB encrypt failed\n"));
970 if (context->internal->_smb_encryption_level == 2) {
971 cli_shutdown(c);
972 errno = EPERM;
973 return NULL;
976 DEBUG(4,(" SMB encrypt ok\n"));
980 * Ok, we have got a nice connection
981 * Let's allocate a server structure.
984 srv = SMB_MALLOC_P(SMBCSRV);
985 if (!srv) {
986 errno = ENOMEM;
987 goto failed;
990 ZERO_STRUCTP(srv);
991 srv->cli = c;
992 srv->dev = (dev_t)(str_checksum(server) ^ str_checksum(share));
993 srv->no_pathinfo = False;
994 srv->no_pathinfo2 = False;
995 srv->no_nt_session = False;
997 /* now add it to the cache (internal or external) */
998 /* Let the cache function set errno if it wants to */
999 errno = 0;
1000 if ((context->callbacks.add_cached_srv_fn)(context, srv,
1001 server, share,
1002 *pp_workgroup,
1003 *pp_username)) {
1004 int saved_errno = errno;
1005 DEBUG(3, (" Failed to add server to cache\n"));
1006 errno = saved_errno;
1007 if (errno == 0) {
1008 errno = ENOMEM;
1010 goto failed;
1013 DEBUG(2, ("Server connect ok: //%s/%s: %p\n",
1014 server, share, srv));
1016 DLIST_ADD(context->internal->_servers, srv);
1017 return srv;
1019 failed:
1020 cli_shutdown(c);
1021 if (!srv) {
1022 return NULL;
1025 SAFE_FREE(srv);
1026 return NULL;
1030 * Connect to a server for getting/setting attributes, possibly on an existing
1031 * connection. This works similarly to smbc_server().
1033 static SMBCSRV *
1034 smbc_attr_server(TALLOC_CTX *ctx,
1035 SMBCCTX *context,
1036 const char *server,
1037 const char *share,
1038 char **pp_workgroup,
1039 char **pp_username,
1040 char **pp_password)
1042 int flags;
1043 struct sockaddr_storage ss;
1044 struct cli_state *ipc_cli;
1045 struct rpc_pipe_client *pipe_hnd;
1046 NTSTATUS nt_status;
1047 SMBCSRV *ipc_srv=NULL;
1050 * See if we've already created this special connection. Reference
1051 * our "special" share name '*IPC$', which is an impossible real share
1052 * name due to the leading asterisk.
1054 ipc_srv = find_server(ctx, context, server, "*IPC$",
1055 pp_workgroup, pp_username, pp_password);
1056 if (!ipc_srv) {
1058 /* We didn't find a cached connection. Get the password */
1059 if (!*pp_password || (*pp_password)[0] == '\0') {
1060 /* ... then retrieve it now. */
1061 call_auth_fn(ctx, context, server, share,
1062 pp_workgroup, pp_username, pp_password);
1063 if (!*pp_workgroup || !*pp_username || !*pp_password) {
1064 errno = ENOMEM;
1065 return NULL;
1069 flags = 0;
1070 if (context->flags & SMB_CTX_FLAG_USE_KERBEROS) {
1071 flags |= CLI_FULL_CONNECTION_USE_KERBEROS;
1074 zero_addr(&ss);
1075 nt_status = cli_full_connection(&ipc_cli,
1076 global_myname(), server,
1077 &ss, 0, "IPC$", "?????",
1078 *pp_username,
1079 *pp_workgroup,
1080 *pp_password,
1081 flags,
1082 Undefined, NULL);
1083 if (! NT_STATUS_IS_OK(nt_status)) {
1084 DEBUG(1,("cli_full_connection failed! (%s)\n",
1085 nt_errstr(nt_status)));
1086 errno = ENOTSUP;
1087 return NULL;
1090 if (context->internal->_smb_encryption_level) {
1091 /* Attempt UNIX smb encryption. */
1092 if (!NT_STATUS_IS_OK(cli_force_encryption(ipc_cli,
1093 *pp_username,
1094 *pp_password,
1095 *pp_workgroup))) {
1098 * context->internal->_smb_encryption_level == 1
1099 * means don't fail if encryption can't be negotiated,
1100 * == 2 means fail if encryption can't be negotiated.
1103 DEBUG(4,(" SMB encrypt failed on IPC$\n"));
1105 if (context->internal->_smb_encryption_level == 2) {
1106 cli_shutdown(ipc_cli);
1107 errno = EPERM;
1108 return NULL;
1111 DEBUG(4,(" SMB encrypt ok on IPC$\n"));
1114 ipc_srv = SMB_MALLOC_P(SMBCSRV);
1115 if (!ipc_srv) {
1116 errno = ENOMEM;
1117 cli_shutdown(ipc_cli);
1118 return NULL;
1121 ZERO_STRUCTP(ipc_srv);
1122 ipc_srv->cli = ipc_cli;
1124 pipe_hnd = cli_rpc_pipe_open_noauth(ipc_srv->cli,
1125 PI_LSARPC,
1126 &nt_status);
1127 if (!pipe_hnd) {
1128 DEBUG(1, ("cli_nt_session_open fail!\n"));
1129 errno = ENOTSUP;
1130 cli_shutdown(ipc_srv->cli);
1131 free(ipc_srv);
1132 return NULL;
1136 * Some systems don't support
1137 * SEC_RIGHTS_MAXIMUM_ALLOWED, but NT sends 0x2000000
1138 * so we might as well do it too.
1141 nt_status = rpccli_lsa_open_policy(
1142 pipe_hnd,
1143 talloc_tos(),
1144 True,
1145 GENERIC_EXECUTE_ACCESS,
1146 &ipc_srv->pol);
1148 if (!NT_STATUS_IS_OK(nt_status)) {
1149 errno = smbc_errno(context, ipc_srv->cli);
1150 cli_shutdown(ipc_srv->cli);
1151 return NULL;
1154 /* now add it to the cache (internal or external) */
1156 errno = 0; /* let cache function set errno if it likes */
1157 if ((context->callbacks.add_cached_srv_fn)(context, ipc_srv,
1158 server,
1159 "*IPC$",
1160 *pp_workgroup,
1161 *pp_username)) {
1162 DEBUG(3, (" Failed to add server to cache\n"));
1163 if (errno == 0) {
1164 errno = ENOMEM;
1166 cli_shutdown(ipc_srv->cli);
1167 free(ipc_srv);
1168 return NULL;
1171 DLIST_ADD(context->internal->_servers, ipc_srv);
1174 return ipc_srv;
1178 * Routine to open() a file ...
1181 static SMBCFILE *
1182 smbc_open_ctx(SMBCCTX *context,
1183 const char *fname,
1184 int flags,
1185 mode_t mode)
1187 char *server = NULL, *share = NULL, *user = NULL, *password = NULL, *workgroup = NULL;
1188 char *path = NULL;
1189 char *targetpath = NULL;
1190 struct cli_state *targetcli = NULL;
1191 SMBCSRV *srv = NULL;
1192 SMBCFILE *file = NULL;
1193 int fd;
1194 TALLOC_CTX *frame = talloc_stackframe();
1196 if (!context || !context->internal ||
1197 !context->internal->_initialized) {
1199 errno = EINVAL; /* Best I can think of ... */
1200 TALLOC_FREE(frame);
1201 return NULL;
1205 if (!fname) {
1207 errno = EINVAL;
1208 TALLOC_FREE(frame);
1209 return NULL;
1213 if (smbc_parse_path(frame,
1214 context,
1215 fname,
1216 &workgroup,
1217 &server,
1218 &share,
1219 &path,
1220 &user,
1221 &password,
1222 NULL)) {
1223 errno = EINVAL;
1224 TALLOC_FREE(frame);
1225 return NULL;
1228 if (!user || user[0] == (char)0) {
1229 user = talloc_strdup(frame, context->user);
1230 if (!user) {
1231 errno = ENOMEM;
1232 TALLOC_FREE(frame);
1233 return NULL;
1237 srv = smbc_server(frame, context, True,
1238 server, share, &workgroup, &user, &password);
1240 if (!srv) {
1241 if (errno == EPERM) errno = EACCES;
1242 TALLOC_FREE(frame);
1243 return NULL; /* smbc_server sets errno */
1246 /* Hmmm, the test for a directory is suspect here ... FIXME */
1248 if (strlen(path) > 0 && path[strlen(path) - 1] == '\\') {
1249 fd = -1;
1250 } else {
1251 file = SMB_MALLOC_P(SMBCFILE);
1253 if (!file) {
1254 errno = ENOMEM;
1255 TALLOC_FREE(frame);
1256 return NULL;
1259 ZERO_STRUCTP(file);
1261 /*d_printf(">>>open: resolving %s\n", path);*/
1262 if (!cli_resolve_path(frame, "", srv->cli, path, &targetcli, &targetpath)) {
1263 d_printf("Could not resolve %s\n", path);
1264 SAFE_FREE(file);
1265 TALLOC_FREE(frame);
1266 return NULL;
1268 /*d_printf(">>>open: resolved %s as %s\n", path, targetpath);*/
1270 if ((fd = cli_open(targetcli, targetpath, flags,
1271 context->internal->_share_mode)) < 0) {
1273 /* Handle the error ... */
1275 SAFE_FREE(file);
1276 errno = smbc_errno(context, targetcli);
1277 TALLOC_FREE(frame);
1278 return NULL;
1282 /* Fill in file struct */
1284 file->cli_fd = fd;
1285 file->fname = SMB_STRDUP(fname);
1286 file->srv = srv;
1287 file->offset = 0;
1288 file->file = True;
1290 DLIST_ADD(context->internal->_files, file);
1293 * If the file was opened in O_APPEND mode, all write
1294 * operations should be appended to the file. To do that,
1295 * though, using this protocol, would require a getattrE()
1296 * call for each and every write, to determine where the end
1297 * of the file is. (There does not appear to be an append flag
1298 * in the protocol.) Rather than add all of that overhead of
1299 * retrieving the current end-of-file offset prior to each
1300 * write operation, we'll assume that most append operations
1301 * will continuously write, so we'll just set the offset to
1302 * the end of the file now and hope that's adequate.
1304 * Note to self: If this proves inadequate, and O_APPEND
1305 * should, in some cases, be forced for each write, add a
1306 * field in the context options structure, for
1307 * "strict_append_mode" which would select between the current
1308 * behavior (if FALSE) or issuing a getattrE() prior to each
1309 * write and forcing the write to the end of the file (if
1310 * TRUE). Adding that capability will likely require adding
1311 * an "append" flag into the _SMBCFILE structure to track
1312 * whether a file was opened in O_APPEND mode. -- djl
1314 if (flags & O_APPEND) {
1315 if (smbc_lseek_ctx(context, file, 0, SEEK_END) < 0) {
1316 (void) smbc_close_ctx(context, file);
1317 errno = ENXIO;
1318 TALLOC_FREE(frame);
1319 return NULL;
1323 TALLOC_FREE(frame);
1324 return file;
1328 /* Check if opendir needed ... */
1330 if (fd == -1) {
1331 int eno = 0;
1333 eno = smbc_errno(context, srv->cli);
1334 file = (context->opendir)(context, fname);
1335 if (!file) errno = eno;
1336 TALLOC_FREE(frame);
1337 return file;
1341 errno = EINVAL; /* FIXME, correct errno ? */
1342 TALLOC_FREE(frame);
1343 return NULL;
1348 * Routine to create a file
1351 static int creat_bits = O_WRONLY | O_CREAT | O_TRUNC; /* FIXME: Do we need this */
1353 static SMBCFILE *
1354 smbc_creat_ctx(SMBCCTX *context,
1355 const char *path,
1356 mode_t mode)
1359 if (!context || !context->internal ||
1360 !context->internal->_initialized) {
1362 errno = EINVAL;
1363 return NULL;
1367 return smbc_open_ctx(context, path, creat_bits, mode);
1371 * Routine to read() a file ...
1374 static ssize_t
1375 smbc_read_ctx(SMBCCTX *context,
1376 SMBCFILE *file,
1377 void *buf,
1378 size_t count)
1380 int ret;
1381 char *server = NULL, *share = NULL, *user = NULL, *password = NULL;
1382 char *path = NULL;
1383 char *targetpath = NULL;
1384 struct cli_state *targetcli = NULL;
1385 TALLOC_CTX *frame = talloc_stackframe();
1388 * offset:
1390 * Compiler bug (possibly) -- gcc (GCC) 3.3.5 (Debian 1:3.3.5-2) --
1391 * appears to pass file->offset (which is type off_t) differently than
1392 * a local variable of type off_t. Using local variable "offset" in
1393 * the call to cli_read() instead of file->offset fixes a problem
1394 * retrieving data at an offset greater than 4GB.
1396 off_t offset;
1398 if (!context || !context->internal ||
1399 !context->internal->_initialized) {
1400 errno = EINVAL;
1401 TALLOC_FREE(frame);
1402 return -1;
1406 DEBUG(4, ("smbc_read(%p, %d)\n", file, (int)count));
1408 if (!file || !DLIST_CONTAINS(context->internal->_files, file)) {
1409 errno = EBADF;
1410 TALLOC_FREE(frame);
1411 return -1;
1415 offset = file->offset;
1417 /* Check that the buffer exists ... */
1419 if (buf == NULL) {
1420 errno = EINVAL;
1421 TALLOC_FREE(frame);
1422 return -1;
1426 /*d_printf(">>>read: parsing %s\n", file->fname);*/
1427 if (smbc_parse_path(frame,
1428 context,
1429 file->fname,
1430 NULL,
1431 &server,
1432 &share,
1433 &path,
1434 &user,
1435 &password,
1436 NULL)) {
1437 errno = EINVAL;
1438 TALLOC_FREE(frame);
1439 return -1;
1442 /*d_printf(">>>read: resolving %s\n", path);*/
1443 if (!cli_resolve_path(frame, "", file->srv->cli, path,
1444 &targetcli, &targetpath)) {
1445 d_printf("Could not resolve %s\n", path);
1446 TALLOC_FREE(frame);
1447 return -1;
1449 /*d_printf(">>>fstat: resolved path as %s\n", targetpath);*/
1451 ret = cli_read(targetcli, file->cli_fd, (char *)buf, offset, count);
1453 if (ret < 0) {
1455 errno = smbc_errno(context, targetcli);
1456 TALLOC_FREE(frame);
1457 return -1;
1461 file->offset += ret;
1463 DEBUG(4, (" --> %d\n", ret));
1465 TALLOC_FREE(frame);
1466 return ret; /* Success, ret bytes of data ... */
1471 * Routine to write() a file ...
1474 static ssize_t
1475 smbc_write_ctx(SMBCCTX *context,
1476 SMBCFILE *file,
1477 void *buf,
1478 size_t count)
1480 int ret;
1481 off_t offset;
1482 char *server = NULL, *share = NULL, *user = NULL, *password = NULL;
1483 char *path = NULL;
1484 char *targetpath = NULL;
1485 struct cli_state *targetcli = NULL;
1486 TALLOC_CTX *frame = talloc_stackframe();
1488 /* First check all pointers before dereferencing them */
1490 if (!context || !context->internal ||
1491 !context->internal->_initialized) {
1492 errno = EINVAL;
1493 TALLOC_FREE(frame);
1494 return -1;
1498 if (!file || !DLIST_CONTAINS(context->internal->_files, file)) {
1499 errno = EBADF;
1500 TALLOC_FREE(frame);
1501 return -1;
1504 /* Check that the buffer exists ... */
1506 if (buf == NULL) {
1507 errno = EINVAL;
1508 TALLOC_FREE(frame);
1509 return -1;
1513 offset = file->offset; /* See "offset" comment in smbc_read_ctx() */
1515 /*d_printf(">>>write: parsing %s\n", file->fname);*/
1516 if (smbc_parse_path(frame,
1517 context,
1518 file->fname,
1519 NULL,
1520 &server,
1521 &share,
1522 &path,
1523 &user,
1524 &password,
1525 NULL)) {
1526 errno = EINVAL;
1527 TALLOC_FREE(frame);
1528 return -1;
1531 /*d_printf(">>>write: resolving %s\n", path);*/
1532 if (!cli_resolve_path(frame, "", file->srv->cli, path,
1533 &targetcli, &targetpath)) {
1534 d_printf("Could not resolve %s\n", path);
1535 TALLOC_FREE(frame);
1536 return -1;
1538 /*d_printf(">>>write: resolved path as %s\n", targetpath);*/
1540 ret = cli_write(targetcli, file->cli_fd, 0, (char *)buf, offset, count);
1542 if (ret <= 0) {
1543 errno = smbc_errno(context, targetcli);
1544 TALLOC_FREE(frame);
1545 return -1;
1549 file->offset += ret;
1551 TALLOC_FREE(frame);
1552 return ret; /* Success, 0 bytes of data ... */
1556 * Routine to close() a file ...
1559 static int
1560 smbc_close_ctx(SMBCCTX *context,
1561 SMBCFILE *file)
1563 SMBCSRV *srv;
1564 char *server = NULL, *share = NULL, *user = NULL, *password = NULL;
1565 char *path = NULL;
1566 char *targetpath = NULL;
1567 struct cli_state *targetcli = NULL;
1568 TALLOC_CTX *frame = talloc_stackframe();
1570 if (!context || !context->internal ||
1571 !context->internal->_initialized) {
1573 errno = EINVAL;
1574 TALLOC_FREE(frame);
1575 return -1;
1578 if (!file || !DLIST_CONTAINS(context->internal->_files, file)) {
1579 errno = EBADF;
1580 TALLOC_FREE(frame);
1581 return -1;
1584 /* IS a dir ... */
1585 if (!file->file) {
1586 TALLOC_FREE(frame);
1587 return (context->closedir)(context, file);
1590 /*d_printf(">>>close: parsing %s\n", file->fname);*/
1591 if (smbc_parse_path(frame,
1592 context,
1593 file->fname,
1594 NULL,
1595 &server,
1596 &share,
1597 &path,
1598 &user,
1599 &password,
1600 NULL)) {
1601 errno = EINVAL;
1602 TALLOC_FREE(frame);
1603 return -1;
1606 /*d_printf(">>>close: resolving %s\n", path);*/
1607 if (!cli_resolve_path(frame, "", file->srv->cli, path,
1608 &targetcli, &targetpath)) {
1609 d_printf("Could not resolve %s\n", path);
1610 TALLOC_FREE(frame);
1611 return -1;
1613 /*d_printf(">>>close: resolved path as %s\n", targetpath);*/
1615 if (!cli_close(targetcli, file->cli_fd)) {
1617 DEBUG(3, ("cli_close failed on %s. purging server.\n",
1618 file->fname));
1619 /* Deallocate slot and remove the server
1620 * from the server cache if unused */
1621 errno = smbc_errno(context, targetcli);
1622 srv = file->srv;
1623 DLIST_REMOVE(context->internal->_files, file);
1624 SAFE_FREE(file->fname);
1625 SAFE_FREE(file);
1626 (context->callbacks.remove_unused_server_fn)(context, srv);
1627 TALLOC_FREE(frame);
1628 return -1;
1632 DLIST_REMOVE(context->internal->_files, file);
1633 SAFE_FREE(file->fname);
1634 SAFE_FREE(file);
1635 TALLOC_FREE(frame);
1637 return 0;
1641 * Get info from an SMB server on a file. Use a qpathinfo call first
1642 * and if that fails, use getatr, as Win95 sometimes refuses qpathinfo
1644 static bool
1645 smbc_getatr(SMBCCTX * context,
1646 SMBCSRV *srv,
1647 char *path,
1648 uint16 *mode,
1649 SMB_OFF_T *size,
1650 struct timespec *create_time_ts,
1651 struct timespec *access_time_ts,
1652 struct timespec *write_time_ts,
1653 struct timespec *change_time_ts,
1654 SMB_INO_T *ino)
1656 char *fixedpath = NULL;
1657 char *targetpath = NULL;
1658 struct cli_state *targetcli = NULL;
1659 time_t write_time;
1660 TALLOC_CTX *frame = talloc_stackframe();
1662 if (!context || !context->internal ||
1663 !context->internal->_initialized) {
1664 errno = EINVAL;
1665 TALLOC_FREE(frame);
1666 return -1;
1669 /* path fixup for . and .. */
1670 if (strequal(path, ".") || strequal(path, "..")) {
1671 fixedpath = talloc_strdup(frame, "\\");
1672 if (!fixedpath) {
1673 errno = ENOMEM;
1674 TALLOC_FREE(frame);
1675 return -1;
1677 } else {
1678 fixedpath = talloc_strdup(frame, path);
1679 if (!fixedpath) {
1680 errno = ENOMEM;
1681 TALLOC_FREE(frame);
1682 return -1;
1684 trim_string(fixedpath, NULL, "\\..");
1685 trim_string(fixedpath, NULL, "\\.");
1687 DEBUG(4,("smbc_getatr: sending qpathinfo\n"));
1689 if (!cli_resolve_path(frame, "", srv->cli, fixedpath,
1690 &targetcli, &targetpath)) {
1691 d_printf("Couldn't resolve %s\n", path);
1692 TALLOC_FREE(frame);
1693 return False;
1696 if (!srv->no_pathinfo2 &&
1697 cli_qpathinfo2(targetcli, targetpath,
1698 create_time_ts,
1699 access_time_ts,
1700 write_time_ts,
1701 change_time_ts,
1702 size, mode, ino)) {
1703 TALLOC_FREE(frame);
1704 return True;
1707 /* if this is NT then don't bother with the getatr */
1708 if (targetcli->capabilities & CAP_NT_SMBS) {
1709 errno = EPERM;
1710 TALLOC_FREE(frame);
1711 return False;
1714 if (cli_getatr(targetcli, targetpath, mode, size, &write_time)) {
1716 struct timespec w_time_ts;
1718 w_time_ts = convert_time_t_to_timespec(write_time);
1720 if (write_time_ts != NULL) {
1721 *write_time_ts = w_time_ts;
1724 if (create_time_ts != NULL) {
1725 *create_time_ts = w_time_ts;
1728 if (access_time_ts != NULL) {
1729 *access_time_ts = w_time_ts;
1732 if (change_time_ts != NULL) {
1733 *change_time_ts = w_time_ts;
1736 srv->no_pathinfo2 = True;
1737 TALLOC_FREE(frame);
1738 return True;
1741 errno = EPERM;
1742 TALLOC_FREE(frame);
1743 return False;
1748 * Set file info on an SMB server. Use setpathinfo call first. If that
1749 * fails, use setattrE..
1751 * Access and modification time parameters are always used and must be
1752 * provided. Create time, if zero, will be determined from the actual create
1753 * time of the file. If non-zero, the create time will be set as well.
1755 * "mode" (attributes) parameter may be set to -1 if it is not to be set.
1757 static bool
1758 smbc_setatr(SMBCCTX * context, SMBCSRV *srv, char *path,
1759 time_t create_time,
1760 time_t access_time,
1761 time_t write_time,
1762 time_t change_time,
1763 uint16 mode)
1765 int fd;
1766 int ret;
1767 TALLOC_CTX *frame = talloc_stackframe();
1770 * First, try setpathinfo (if qpathinfo succeeded), for it is the
1771 * modern function for "new code" to be using, and it works given a
1772 * filename rather than requiring that the file be opened to have its
1773 * attributes manipulated.
1775 if (srv->no_pathinfo ||
1776 ! cli_setpathinfo(srv->cli, path,
1777 create_time,
1778 access_time,
1779 write_time,
1780 change_time,
1781 mode)) {
1784 * setpathinfo is not supported; go to plan B.
1786 * cli_setatr() does not work on win98, and it also doesn't
1787 * support setting the access time (only the modification
1788 * time), so in all cases, we open the specified file and use
1789 * cli_setattrE() which should work on all OS versions, and
1790 * supports both times.
1793 /* Don't try {q,set}pathinfo() again, with this server */
1794 srv->no_pathinfo = True;
1796 /* Open the file */
1797 if ((fd = cli_open(srv->cli, path, O_RDWR, DENY_NONE)) < 0) {
1799 errno = smbc_errno(context, srv->cli);
1800 TALLOC_FREE(frame);
1801 return -1;
1804 /* Set the new attributes */
1805 ret = cli_setattrE(srv->cli, fd,
1806 change_time,
1807 access_time,
1808 write_time);
1810 /* Close the file */
1811 cli_close(srv->cli, fd);
1814 * Unfortunately, setattrE() doesn't have a provision for
1815 * setting the access mode (attributes). We'll have to try
1816 * cli_setatr() for that, and with only this parameter, it
1817 * seems to work on win98.
1819 if (ret && mode != (uint16) -1) {
1820 ret = cli_setatr(srv->cli, path, mode, 0);
1823 if (! ret) {
1824 errno = smbc_errno(context, srv->cli);
1825 TALLOC_FREE(frame);
1826 return False;
1830 TALLOC_FREE(frame);
1831 return True;
1835 * Routine to unlink() a file
1838 static int
1839 smbc_unlink_ctx(SMBCCTX *context,
1840 const char *fname)
1842 char *server = NULL, *share = NULL, *user = NULL, *password = NULL, *workgroup = NULL;
1843 char *path = NULL;
1844 char *targetpath = NULL;
1845 struct cli_state *targetcli = NULL;
1846 SMBCSRV *srv = NULL;
1847 TALLOC_CTX *frame = talloc_stackframe();
1849 if (!context || !context->internal ||
1850 !context->internal->_initialized) {
1851 errno = EINVAL; /* Best I can think of ... */
1852 TALLOC_FREE(frame);
1853 return -1;
1857 if (!fname) {
1858 errno = EINVAL;
1859 TALLOC_FREE(frame);
1860 return -1;
1864 if (smbc_parse_path(frame,
1865 context,
1866 fname,
1867 &workgroup,
1868 &server,
1869 &share,
1870 &path,
1871 &user,
1872 &password,
1873 NULL)) {
1874 errno = EINVAL;
1875 TALLOC_FREE(frame);
1876 return -1;
1879 if (!user || user[0] == (char)0) {
1880 user = talloc_strdup(frame, context->user);
1881 if (!user) {
1882 errno = ENOMEM;
1883 TALLOC_FREE(frame);
1884 return -1;
1888 srv = smbc_server(frame, context, True,
1889 server, share, &workgroup, &user, &password);
1891 if (!srv) {
1892 TALLOC_FREE(frame);
1893 return -1; /* smbc_server sets errno */
1897 /*d_printf(">>>unlink: resolving %s\n", path);*/
1898 if (!cli_resolve_path(frame, "", srv->cli, path,
1899 &targetcli, &targetpath)) {
1900 d_printf("Could not resolve %s\n", path);
1901 TALLOC_FREE(frame);
1902 return -1;
1904 /*d_printf(">>>unlink: resolved path as %s\n", targetpath);*/
1906 if (!cli_unlink(targetcli, targetpath)) {
1908 errno = smbc_errno(context, targetcli);
1910 if (errno == EACCES) { /* Check if the file is a directory */
1912 int saverr = errno;
1913 SMB_OFF_T size = 0;
1914 uint16 mode = 0;
1915 struct timespec write_time_ts;
1916 struct timespec access_time_ts;
1917 struct timespec change_time_ts;
1918 SMB_INO_T ino = 0;
1920 if (!smbc_getatr(context, srv, path, &mode, &size,
1921 NULL,
1922 &access_time_ts,
1923 &write_time_ts,
1924 &change_time_ts,
1925 &ino)) {
1927 /* Hmmm, bad error ... What? */
1929 errno = smbc_errno(context, targetcli);
1930 TALLOC_FREE(frame);
1931 return -1;
1934 else {
1936 if (IS_DOS_DIR(mode))
1937 errno = EISDIR;
1938 else
1939 errno = saverr; /* Restore this */
1944 TALLOC_FREE(frame);
1945 return -1;
1949 TALLOC_FREE(frame);
1950 return 0; /* Success ... */
1955 * Routine to rename() a file
1958 static int
1959 smbc_rename_ctx(SMBCCTX *ocontext,
1960 const char *oname,
1961 SMBCCTX *ncontext,
1962 const char *nname)
1964 char *server1 = NULL;
1965 char *share1 = NULL;
1966 char *server2 = NULL;
1967 char *share2 = NULL;
1968 char *user1 = NULL;
1969 char *user2 = NULL;
1970 char *password1 = NULL;
1971 char *password2 = NULL;
1972 char *workgroup = NULL;
1973 char *path1 = NULL;
1974 char *path2 = NULL;
1975 char *targetpath1 = NULL;
1976 char *targetpath2 = NULL;
1977 struct cli_state *targetcli1 = NULL;
1978 struct cli_state *targetcli2 = NULL;
1979 SMBCSRV *srv = NULL;
1980 TALLOC_CTX *frame = talloc_stackframe();
1982 if (!ocontext || !ncontext ||
1983 !ocontext->internal || !ncontext->internal ||
1984 !ocontext->internal->_initialized ||
1985 !ncontext->internal->_initialized) {
1986 errno = EINVAL; /* Best I can think of ... */
1987 TALLOC_FREE(frame);
1988 return -1;
1991 if (!oname || !nname) {
1992 errno = EINVAL;
1993 TALLOC_FREE(frame);
1994 return -1;
1997 DEBUG(4, ("smbc_rename(%s,%s)\n", oname, nname));
1999 if (smbc_parse_path(frame,
2000 ocontext,
2001 oname,
2002 &workgroup,
2003 &server1,
2004 &share1,
2005 &path1,
2006 &user1,
2007 &password1,
2008 NULL)) {
2009 errno = EINVAL;
2010 TALLOC_FREE(frame);
2011 return -1;
2014 if (!user1 || user1[0] == (char)0) {
2015 user1 = talloc_strdup(frame, ocontext->user);
2016 if (!user1) {
2017 errno = ENOMEM;
2018 TALLOC_FREE(frame);
2019 return -1;
2023 if (smbc_parse_path(frame,
2024 ncontext,
2025 nname,
2026 NULL,
2027 &server2,
2028 &share2,
2029 &path2,
2030 &user2,
2031 &password2,
2032 NULL)) {
2033 errno = EINVAL;
2034 TALLOC_FREE(frame);
2035 return -1;
2038 if (!user2 || user2[0] == (char)0) {
2039 user2 = talloc_strdup(frame, ncontext->user);
2040 if (!user2) {
2041 errno = ENOMEM;
2042 TALLOC_FREE(frame);
2043 return -1;
2047 if (strcmp(server1, server2) || strcmp(share1, share2) ||
2048 strcmp(user1, user2)) {
2049 /* Can't rename across file systems, or users?? */
2050 errno = EXDEV;
2051 TALLOC_FREE(frame);
2052 return -1;
2055 srv = smbc_server(frame, ocontext, True,
2056 server1, share1, &workgroup, &user1, &password1);
2057 if (!srv) {
2058 TALLOC_FREE(frame);
2059 return -1;
2063 /*d_printf(">>>rename: resolving %s\n", path1);*/
2064 if (!cli_resolve_path(frame, "", srv->cli, path1,
2065 &targetcli1, &targetpath1)) {
2066 d_printf("Could not resolve %s\n", path1);
2067 TALLOC_FREE(frame);
2068 return -1;
2070 /*d_printf(">>>rename: resolved path as %s\n", targetpath1);*/
2071 /*d_printf(">>>rename: resolving %s\n", path2);*/
2072 if (!cli_resolve_path(frame, "", srv->cli, path2,
2073 &targetcli2, &targetpath2)) {
2074 d_printf("Could not resolve %s\n", path2);
2075 TALLOC_FREE(frame);
2076 return -1;
2078 /*d_printf(">>>rename: resolved path as %s\n", targetpath2);*/
2080 if (strcmp(targetcli1->desthost, targetcli2->desthost) ||
2081 strcmp(targetcli1->share, targetcli2->share))
2083 /* can't rename across file systems */
2084 errno = EXDEV;
2085 TALLOC_FREE(frame);
2086 return -1;
2089 if (!cli_rename(targetcli1, targetpath1, targetpath2)) {
2090 int eno = smbc_errno(ocontext, targetcli1);
2092 if (eno != EEXIST ||
2093 !cli_unlink(targetcli1, targetpath2) ||
2094 !cli_rename(targetcli1, targetpath1, targetpath2)) {
2096 errno = eno;
2097 TALLOC_FREE(frame);
2098 return -1;
2103 TALLOC_FREE(frame);
2104 return 0; /* Success */
2108 * A routine to lseek() a file
2111 static off_t
2112 smbc_lseek_ctx(SMBCCTX *context,
2113 SMBCFILE *file,
2114 off_t offset,
2115 int whence)
2117 SMB_OFF_T size;
2118 char *server = NULL, *share = NULL, *user = NULL, *password = NULL;
2119 char *path = NULL;
2120 char *targetpath = NULL;
2121 struct cli_state *targetcli = NULL;
2122 TALLOC_CTX *frame = talloc_stackframe();
2124 if (!context || !context->internal ||
2125 !context->internal->_initialized) {
2126 errno = EINVAL;
2127 TALLOC_FREE(frame);
2128 return -1;
2131 if (!file || !DLIST_CONTAINS(context->internal->_files, file)) {
2133 errno = EBADF;
2134 TALLOC_FREE(frame);
2135 return -1;
2139 if (!file->file) {
2141 errno = EINVAL;
2142 TALLOC_FREE(frame);
2143 return -1; /* Can't lseek a dir ... */
2147 switch (whence) {
2148 case SEEK_SET:
2149 file->offset = offset;
2150 break;
2152 case SEEK_CUR:
2153 file->offset += offset;
2154 break;
2156 case SEEK_END:
2157 /*d_printf(">>>lseek: parsing %s\n", file->fname);*/
2158 if (smbc_parse_path(frame,
2159 context,
2160 file->fname,
2161 NULL,
2162 &server,
2163 &share,
2164 &path,
2165 &user,
2166 &password,
2167 NULL)) {
2168 errno = EINVAL;
2169 TALLOC_FREE(frame);
2170 return -1;
2173 /*d_printf(">>>lseek: resolving %s\n", path);*/
2174 if (!cli_resolve_path(frame, "", file->srv->cli, path,
2175 &targetcli, &targetpath)) {
2176 d_printf("Could not resolve %s\n", path);
2177 TALLOC_FREE(frame);
2178 return -1;
2180 /*d_printf(">>>lseek: resolved path as %s\n", targetpath);*/
2182 if (!cli_qfileinfo(targetcli, file->cli_fd, NULL,
2183 &size, NULL, NULL, NULL, NULL, NULL))
2185 SMB_OFF_T b_size = size;
2186 if (!cli_getattrE(targetcli, file->cli_fd,
2187 NULL, &b_size, NULL, NULL, NULL))
2189 errno = EINVAL;
2190 TALLOC_FREE(frame);
2191 return -1;
2192 } else
2193 size = b_size;
2195 file->offset = size + offset;
2196 break;
2198 default:
2199 errno = EINVAL;
2200 break;
2204 TALLOC_FREE(frame);
2205 return file->offset;
2210 * Generate an inode number from file name for those things that need it
2213 static ino_t
2214 smbc_inode(SMBCCTX *context,
2215 const char *name)
2217 if (!context || !context->internal ||
2218 !context->internal->_initialized) {
2220 errno = EINVAL;
2221 return -1;
2225 if (!*name) return 2; /* FIXME, why 2 ??? */
2226 return (ino_t)str_checksum(name);
2231 * Routine to put basic stat info into a stat structure ... Used by stat and
2232 * fstat below.
2235 static int
2236 smbc_setup_stat(SMBCCTX *context,
2237 struct stat *st,
2238 char *fname,
2239 SMB_OFF_T size,
2240 int mode)
2242 TALLOC_CTX *frame = talloc_stackframe();
2244 st->st_mode = 0;
2246 if (IS_DOS_DIR(mode)) {
2247 st->st_mode = SMBC_DIR_MODE;
2248 } else {
2249 st->st_mode = SMBC_FILE_MODE;
2252 if (IS_DOS_ARCHIVE(mode)) st->st_mode |= S_IXUSR;
2253 if (IS_DOS_SYSTEM(mode)) st->st_mode |= S_IXGRP;
2254 if (IS_DOS_HIDDEN(mode)) st->st_mode |= S_IXOTH;
2255 if (!IS_DOS_READONLY(mode)) st->st_mode |= S_IWUSR;
2257 st->st_size = size;
2258 #ifdef HAVE_STAT_ST_BLKSIZE
2259 st->st_blksize = 512;
2260 #endif
2261 #ifdef HAVE_STAT_ST_BLOCKS
2262 st->st_blocks = (size+511)/512;
2263 #endif
2264 #ifdef HAVE_STRUCT_STAT_ST_RDEV
2265 st->st_rdev = 0;
2266 #endif
2267 st->st_uid = getuid();
2268 st->st_gid = getgid();
2270 if (IS_DOS_DIR(mode)) {
2271 st->st_nlink = 2;
2272 } else {
2273 st->st_nlink = 1;
2276 if (st->st_ino == 0) {
2277 st->st_ino = smbc_inode(context, fname);
2280 TALLOC_FREE(frame);
2281 return True; /* FIXME: Is this needed ? */
2286 * Routine to stat a file given a name
2289 static int
2290 smbc_stat_ctx(SMBCCTX *context,
2291 const char *fname,
2292 struct stat *st)
2294 SMBCSRV *srv = NULL;
2295 char *server = NULL;
2296 char *share = NULL;
2297 char *user = NULL;
2298 char *password = NULL;
2299 char *workgroup = NULL;
2300 char *path = NULL;
2301 struct timespec write_time_ts;
2302 struct timespec access_time_ts;
2303 struct timespec change_time_ts;
2304 SMB_OFF_T size = 0;
2305 uint16 mode = 0;
2306 SMB_INO_T ino = 0;
2307 TALLOC_CTX *frame = talloc_stackframe();
2309 if (!context || !context->internal ||
2310 !context->internal->_initialized) {
2312 errno = EINVAL; /* Best I can think of ... */
2313 TALLOC_FREE(frame);
2314 return -1;
2317 if (!fname) {
2318 errno = EINVAL;
2319 TALLOC_FREE(frame);
2320 return -1;
2323 DEBUG(4, ("smbc_stat(%s)\n", fname));
2325 if (smbc_parse_path(frame,
2326 context,
2327 fname,
2328 &workgroup,
2329 &server,
2330 &share,
2331 &path,
2332 &user,
2333 &password,
2334 NULL)) {
2335 errno = EINVAL;
2336 TALLOC_FREE(frame);
2337 return -1;
2340 if (!user || user[0] == (char)0) {
2341 user = talloc_strdup(frame,context->user);
2342 if (!user) {
2343 errno = ENOMEM;
2344 TALLOC_FREE(frame);
2345 return -1;
2349 srv = smbc_server(frame, context, True,
2350 server, share, &workgroup, &user, &password);
2352 if (!srv) {
2353 TALLOC_FREE(frame);
2354 return -1; /* errno set by smbc_server */
2357 if (!smbc_getatr(context, srv, path, &mode, &size,
2358 NULL,
2359 &access_time_ts,
2360 &write_time_ts,
2361 &change_time_ts,
2362 &ino)) {
2363 errno = smbc_errno(context, srv->cli);
2364 TALLOC_FREE(frame);
2365 return -1;
2368 st->st_ino = ino;
2370 smbc_setup_stat(context, st, (char *) fname, size, mode);
2372 set_atimespec(st, access_time_ts);
2373 set_ctimespec(st, change_time_ts);
2374 set_mtimespec(st, write_time_ts);
2375 st->st_dev = srv->dev;
2377 TALLOC_FREE(frame);
2378 return 0;
2383 * Routine to stat a file given an fd
2386 static int
2387 smbc_fstat_ctx(SMBCCTX *context,
2388 SMBCFILE *file,
2389 struct stat *st)
2391 struct timespec change_time_ts;
2392 struct timespec access_time_ts;
2393 struct timespec write_time_ts;
2394 SMB_OFF_T size;
2395 uint16 mode;
2396 char *server = NULL;
2397 char *share = NULL;
2398 char *user = NULL;
2399 char *password = NULL;
2400 char *path = NULL;
2401 char *targetpath = NULL;
2402 struct cli_state *targetcli = NULL;
2403 SMB_INO_T ino = 0;
2404 TALLOC_CTX *frame = talloc_stackframe();
2406 if (!context || !context->internal ||
2407 !context->internal->_initialized) {
2408 errno = EINVAL;
2409 TALLOC_FREE(frame);
2410 return -1;
2413 if (!file || !DLIST_CONTAINS(context->internal->_files, file)) {
2414 errno = EBADF;
2415 TALLOC_FREE(frame);
2416 return -1;
2419 if (!file->file) {
2420 TALLOC_FREE(frame);
2421 return (context->fstatdir)(context, file, st);
2424 /*d_printf(">>>fstat: parsing %s\n", file->fname);*/
2425 if (smbc_parse_path(frame,
2426 context,
2427 file->fname,
2428 NULL,
2429 &server,
2430 &share,
2431 &path,
2432 &user,
2433 &password,
2434 NULL)) {
2435 errno = EINVAL;
2436 TALLOC_FREE(frame);
2437 return -1;
2440 /*d_printf(">>>fstat: resolving %s\n", path);*/
2441 if (!cli_resolve_path(frame, "", file->srv->cli, path,
2442 &targetcli, &targetpath)) {
2443 d_printf("Could not resolve %s\n", path);
2444 TALLOC_FREE(frame);
2445 return -1;
2447 /*d_printf(">>>fstat: resolved path as %s\n", targetpath);*/
2449 if (!cli_qfileinfo(targetcli, file->cli_fd, &mode, &size,
2450 NULL,
2451 &access_time_ts,
2452 &write_time_ts,
2453 &change_time_ts,
2454 &ino)) {
2456 time_t change_time, access_time, write_time;
2458 if (!cli_getattrE(targetcli, file->cli_fd, &mode, &size,
2459 &change_time, &access_time, &write_time)) {
2461 errno = EINVAL;
2462 TALLOC_FREE(frame);
2463 return -1;
2466 change_time_ts = convert_time_t_to_timespec(change_time);
2467 access_time_ts = convert_time_t_to_timespec(access_time);
2468 write_time_ts = convert_time_t_to_timespec(write_time);
2471 st->st_ino = ino;
2473 smbc_setup_stat(context, st, file->fname, size, mode);
2475 set_atimespec(st, access_time_ts);
2476 set_ctimespec(st, change_time_ts);
2477 set_mtimespec(st, write_time_ts);
2478 st->st_dev = file->srv->dev;
2480 TALLOC_FREE(frame);
2481 return 0;
2486 * Routine to truncate a file given by its file descriptor, to a specified size
2489 static int
2490 smbc_ftruncate_ctx(SMBCCTX *context,
2491 SMBCFILE *file,
2492 off_t length)
2494 SMB_OFF_T size = length;
2495 char *server = NULL;
2496 char *share = NULL;
2497 char *user = NULL;
2498 char *password = NULL;
2499 char *path = NULL;
2500 char *targetpath = NULL;
2501 struct cli_state *targetcli = NULL;
2502 TALLOC_CTX *frame = talloc_stackframe();
2504 if (!context || !context->internal ||
2505 !context->internal->_initialized) {
2506 errno = EINVAL;
2507 TALLOC_FREE(frame);
2508 return -1;
2511 if (!file || !DLIST_CONTAINS(context->internal->_files, file)) {
2512 errno = EBADF;
2513 TALLOC_FREE(frame);
2514 return -1;
2517 if (!file->file) {
2518 errno = EINVAL;
2519 TALLOC_FREE(frame);
2520 return -1;
2523 /*d_printf(">>>fstat: parsing %s\n", file->fname);*/
2524 if (smbc_parse_path(frame,
2525 context,
2526 file->fname,
2527 NULL,
2528 &server,
2529 &share,
2530 &path,
2531 &user,
2532 &password,
2533 NULL)) {
2534 errno = EINVAL;
2535 TALLOC_FREE(frame);
2536 return -1;
2539 /*d_printf(">>>fstat: resolving %s\n", path);*/
2540 if (!cli_resolve_path(frame, "", file->srv->cli, path,
2541 &targetcli, &targetpath)) {
2542 d_printf("Could not resolve %s\n", path);
2543 TALLOC_FREE(frame);
2544 return -1;
2546 /*d_printf(">>>fstat: resolved path as %s\n", targetpath);*/
2548 if (!cli_ftruncate(targetcli, file->cli_fd, size)) {
2549 errno = EINVAL;
2550 TALLOC_FREE(frame);
2551 return -1;
2554 TALLOC_FREE(frame);
2555 return 0;
2560 * Routine to open a directory
2561 * We accept the URL syntax explained in smbc_parse_path(), above.
2564 static void
2565 smbc_remove_dir(SMBCFILE *dir)
2567 struct smbc_dir_list *d,*f;
2569 d = dir->dir_list;
2570 while (d) {
2572 f = d; d = d->next;
2574 SAFE_FREE(f->dirent);
2575 SAFE_FREE(f);
2579 dir->dir_list = dir->dir_end = dir->dir_next = NULL;
2583 static int
2584 add_dirent(SMBCFILE *dir,
2585 const char *name,
2586 const char *comment,
2587 uint32 type)
2589 struct smbc_dirent *dirent;
2590 int size;
2591 int name_length = (name == NULL ? 0 : strlen(name));
2592 int comment_len = (comment == NULL ? 0 : strlen(comment));
2595 * Allocate space for the dirent, which must be increased by the
2596 * size of the name and the comment and 1 each for the null terminator.
2599 size = sizeof(struct smbc_dirent) + name_length + comment_len + 2;
2601 dirent = (struct smbc_dirent *)SMB_MALLOC(size);
2603 if (!dirent) {
2605 dir->dir_error = ENOMEM;
2606 return -1;
2610 ZERO_STRUCTP(dirent);
2612 if (dir->dir_list == NULL) {
2614 dir->dir_list = SMB_MALLOC_P(struct smbc_dir_list);
2615 if (!dir->dir_list) {
2617 SAFE_FREE(dirent);
2618 dir->dir_error = ENOMEM;
2619 return -1;
2622 ZERO_STRUCTP(dir->dir_list);
2624 dir->dir_end = dir->dir_next = dir->dir_list;
2626 else {
2628 dir->dir_end->next = SMB_MALLOC_P(struct smbc_dir_list);
2630 if (!dir->dir_end->next) {
2632 SAFE_FREE(dirent);
2633 dir->dir_error = ENOMEM;
2634 return -1;
2637 ZERO_STRUCTP(dir->dir_end->next);
2639 dir->dir_end = dir->dir_end->next;
2642 dir->dir_end->next = NULL;
2643 dir->dir_end->dirent = dirent;
2645 dirent->smbc_type = type;
2646 dirent->namelen = name_length;
2647 dirent->commentlen = comment_len;
2648 dirent->dirlen = size;
2651 * dirent->namelen + 1 includes the null (no null termination needed)
2652 * Ditto for dirent->commentlen.
2653 * The space for the two null bytes was allocated.
2655 strncpy(dirent->name, (name?name:""), dirent->namelen + 1);
2656 dirent->comment = (char *)(&dirent->name + dirent->namelen + 1);
2657 strncpy(dirent->comment, (comment?comment:""), dirent->commentlen + 1);
2659 return 0;
2663 static void
2664 list_unique_wg_fn(const char *name,
2665 uint32 type,
2666 const char *comment,
2667 void *state)
2669 SMBCFILE *dir = (SMBCFILE *)state;
2670 struct smbc_dir_list *dir_list;
2671 struct smbc_dirent *dirent;
2672 int dirent_type;
2673 int do_remove = 0;
2675 dirent_type = dir->dir_type;
2677 if (add_dirent(dir, name, comment, dirent_type) < 0) {
2679 /* An error occurred, what do we do? */
2680 /* FIXME: Add some code here */
2683 /* Point to the one just added */
2684 dirent = dir->dir_end->dirent;
2686 /* See if this was a duplicate */
2687 for (dir_list = dir->dir_list;
2688 dir_list != dir->dir_end;
2689 dir_list = dir_list->next) {
2690 if (! do_remove &&
2691 strcmp(dir_list->dirent->name, dirent->name) == 0) {
2692 /* Duplicate. End end of list need to be removed. */
2693 do_remove = 1;
2696 if (do_remove && dir_list->next == dir->dir_end) {
2697 /* Found the end of the list. Remove it. */
2698 dir->dir_end = dir_list;
2699 free(dir_list->next);
2700 free(dirent);
2701 dir_list->next = NULL;
2702 break;
2707 static void
2708 list_fn(const char *name,
2709 uint32 type,
2710 const char *comment,
2711 void *state)
2713 SMBCFILE *dir = (SMBCFILE *)state;
2714 int dirent_type;
2717 * We need to process the type a little ...
2719 * Disk share = 0x00000000
2720 * Print share = 0x00000001
2721 * Comms share = 0x00000002 (obsolete?)
2722 * IPC$ share = 0x00000003
2724 * administrative shares:
2725 * ADMIN$, IPC$, C$, D$, E$ ... are type |= 0x80000000
2728 if (dir->dir_type == SMBC_FILE_SHARE) {
2729 switch (type) {
2730 case 0 | 0x80000000:
2731 case 0:
2732 dirent_type = SMBC_FILE_SHARE;
2733 break;
2735 case 1:
2736 dirent_type = SMBC_PRINTER_SHARE;
2737 break;
2739 case 2:
2740 dirent_type = SMBC_COMMS_SHARE;
2741 break;
2743 case 3 | 0x80000000:
2744 case 3:
2745 dirent_type = SMBC_IPC_SHARE;
2746 break;
2748 default:
2749 dirent_type = SMBC_FILE_SHARE; /* FIXME, error? */
2750 break;
2753 else {
2754 dirent_type = dir->dir_type;
2757 if (add_dirent(dir, name, comment, dirent_type) < 0) {
2759 /* An error occurred, what do we do? */
2760 /* FIXME: Add some code here */
2765 static void
2766 dir_list_fn(const char *mnt,
2767 file_info *finfo,
2768 const char *mask,
2769 void *state)
2772 if (add_dirent((SMBCFILE *)state, finfo->name, "",
2773 (finfo->mode&aDIR?SMBC_DIR:SMBC_FILE)) < 0) {
2775 /* Handle an error ... */
2777 /* FIXME: Add some code ... */
2783 static int
2784 net_share_enum_rpc(struct cli_state *cli,
2785 void (*fn)(const char *name,
2786 uint32 type,
2787 const char *comment,
2788 void *state),
2789 void *state)
2791 int i;
2792 WERROR result;
2793 ENUM_HND enum_hnd;
2794 uint32 info_level = 1;
2795 uint32 preferred_len = 0xffffffff;
2796 uint32 type;
2797 SRV_SHARE_INFO_CTR ctr;
2798 fstring name = "";
2799 fstring comment = "";
2800 struct rpc_pipe_client *pipe_hnd;
2801 NTSTATUS nt_status;
2803 /* Open the server service pipe */
2804 pipe_hnd = cli_rpc_pipe_open_noauth(cli, PI_SRVSVC, &nt_status);
2805 if (!pipe_hnd) {
2806 DEBUG(1, ("net_share_enum_rpc pipe open fail!\n"));
2807 return -1;
2810 /* Issue the NetShareEnum RPC call and retrieve the response */
2811 init_enum_hnd(&enum_hnd, 0);
2812 result = rpccli_srvsvc_net_share_enum(pipe_hnd,
2813 talloc_tos(),
2814 info_level,
2815 &ctr,
2816 preferred_len,
2817 &enum_hnd);
2819 /* Was it successful? */
2820 if (!W_ERROR_IS_OK(result) || ctr.num_entries == 0) {
2821 /* Nope. Go clean up. */
2822 goto done;
2825 /* For each returned entry... */
2826 for (i = 0; i < ctr.num_entries; i++) {
2828 /* pull out the share name */
2829 rpcstr_pull_unistr2_fstring(
2830 name, &ctr.share.info1[i].info_1_str.uni_netname);
2832 /* pull out the share's comment */
2833 rpcstr_pull_unistr2_fstring(
2834 comment, &ctr.share.info1[i].info_1_str.uni_remark);
2836 /* Get the type value */
2837 type = ctr.share.info1[i].info_1.type;
2839 /* Add this share to the list */
2840 (*fn)(name, type, comment, state);
2843 done:
2844 /* Close the server service pipe */
2845 cli_rpc_pipe_close(pipe_hnd);
2847 /* Tell 'em if it worked */
2848 return W_ERROR_IS_OK(result) ? 0 : -1;
2853 static SMBCFILE *
2854 smbc_opendir_ctx(SMBCCTX *context,
2855 const char *fname)
2857 int saved_errno;
2858 char *server = NULL, *share = NULL, *user = NULL, *password = NULL, *options = NULL;
2859 char *workgroup = NULL;
2860 char *path = NULL;
2861 uint16 mode;
2862 char *p = NULL;
2863 SMBCSRV *srv = NULL;
2864 SMBCFILE *dir = NULL;
2865 struct _smbc_callbacks *cb = NULL;
2866 struct sockaddr_storage rem_ss;
2867 TALLOC_CTX *frame = talloc_stackframe();
2869 if (!context || !context->internal ||
2870 !context->internal->_initialized) {
2871 DEBUG(4, ("no valid context\n"));
2872 errno = EINVAL + 8192;
2873 TALLOC_FREE(frame);
2874 return NULL;
2878 if (!fname) {
2879 DEBUG(4, ("no valid fname\n"));
2880 errno = EINVAL + 8193;
2881 TALLOC_FREE(frame);
2882 return NULL;
2885 if (smbc_parse_path(frame,
2886 context,
2887 fname,
2888 &workgroup,
2889 &server,
2890 &share,
2891 &path,
2892 &user,
2893 &password,
2894 &options)) {
2895 DEBUG(4, ("no valid path\n"));
2896 errno = EINVAL + 8194;
2897 TALLOC_FREE(frame);
2898 return NULL;
2901 DEBUG(4, ("parsed path: fname='%s' server='%s' share='%s' "
2902 "path='%s' options='%s'\n",
2903 fname, server, share, path, options));
2905 /* Ensure the options are valid */
2906 if (smbc_check_options(server, share, path, options)) {
2907 DEBUG(4, ("unacceptable options (%s)\n", options));
2908 errno = EINVAL + 8195;
2909 TALLOC_FREE(frame);
2910 return NULL;
2913 if (!user || user[0] == (char)0) {
2914 user = talloc_strdup(frame, context->user);
2915 if (!user) {
2916 errno = ENOMEM;
2917 TALLOC_FREE(frame);
2918 return NULL;
2922 dir = SMB_MALLOC_P(SMBCFILE);
2924 if (!dir) {
2925 errno = ENOMEM;
2926 TALLOC_FREE(frame);
2927 return NULL;
2930 ZERO_STRUCTP(dir);
2932 dir->cli_fd = 0;
2933 dir->fname = SMB_STRDUP(fname);
2934 dir->srv = NULL;
2935 dir->offset = 0;
2936 dir->file = False;
2937 dir->dir_list = dir->dir_next = dir->dir_end = NULL;
2939 if (server[0] == (char)0) {
2941 int i;
2942 int count;
2943 int max_lmb_count;
2944 struct ip_service *ip_list;
2945 struct ip_service server_addr;
2946 struct user_auth_info u_info;
2948 if (share[0] != (char)0 || path[0] != (char)0) {
2950 errno = EINVAL + 8196;
2951 if (dir) {
2952 SAFE_FREE(dir->fname);
2953 SAFE_FREE(dir);
2955 TALLOC_FREE(frame);
2956 return NULL;
2959 /* Determine how many local master browsers to query */
2960 max_lmb_count = (context->options.browse_max_lmb_count == 0
2961 ? INT_MAX
2962 : context->options.browse_max_lmb_count);
2964 memset(&u_info, '\0', sizeof(u_info));
2965 u_info.username = talloc_strdup(frame,user);
2966 u_info.password = talloc_strdup(frame,password);
2967 if (!u_info.username || !u_info.password) {
2968 if (dir) {
2969 SAFE_FREE(dir->fname);
2970 SAFE_FREE(dir);
2972 TALLOC_FREE(frame);
2973 return NULL;
2977 * We have server and share and path empty but options
2978 * requesting that we scan all master browsers for their list
2979 * of workgroups/domains. This implies that we must first try
2980 * broadcast queries to find all master browsers, and if that
2981 * doesn't work, then try our other methods which return only
2982 * a single master browser.
2985 ip_list = NULL;
2986 if (!NT_STATUS_IS_OK(name_resolve_bcast(MSBROWSE, 1, &ip_list,
2987 &count)))
2990 SAFE_FREE(ip_list);
2992 if (!find_master_ip(workgroup, &server_addr.ss)) {
2994 if (dir) {
2995 SAFE_FREE(dir->fname);
2996 SAFE_FREE(dir);
2998 errno = ENOENT;
2999 TALLOC_FREE(frame);
3000 return NULL;
3003 ip_list = (struct ip_service *)memdup(
3004 &server_addr, sizeof(server_addr));
3005 if (ip_list == NULL) {
3006 errno = ENOMEM;
3007 TALLOC_FREE(frame);
3008 return NULL;
3010 count = 1;
3013 for (i = 0; i < count && i < max_lmb_count; i++) {
3014 char addr[INET6_ADDRSTRLEN];
3015 char *wg_ptr = NULL;
3016 struct cli_state *cli = NULL;
3018 print_sockaddr(addr, sizeof(addr), &ip_list[i].ss);
3019 DEBUG(99, ("Found master browser %d of %d: %s\n",
3020 i+1, MAX(count, max_lmb_count),
3021 addr));
3023 cli = get_ipc_connect_master_ip(talloc_tos(),
3024 &ip_list[i],
3025 &u_info,
3026 &wg_ptr);
3027 /* cli == NULL is the master browser refused to talk or
3028 could not be found */
3029 if (!cli) {
3030 continue;
3033 workgroup = talloc_strdup(frame, wg_ptr);
3034 server = talloc_strdup(frame, cli->desthost);
3036 cli_shutdown(cli);
3038 if (!workgroup || !server) {
3039 errno = ENOMEM;
3040 TALLOC_FREE(frame);
3041 return NULL;
3044 DEBUG(4, ("using workgroup %s %s\n",
3045 workgroup, server));
3048 * For each returned master browser IP address, get a
3049 * connection to IPC$ on the server if we do not
3050 * already have one, and determine the
3051 * workgroups/domains that it knows about.
3054 srv = smbc_server(frame, context, True, server, "IPC$",
3055 &workgroup, &user, &password);
3056 if (!srv) {
3057 continue;
3060 dir->srv = srv;
3061 dir->dir_type = SMBC_WORKGROUP;
3063 /* Now, list the stuff ... */
3065 if (!cli_NetServerEnum(srv->cli,
3066 workgroup,
3067 SV_TYPE_DOMAIN_ENUM,
3068 list_unique_wg_fn,
3069 (void *)dir)) {
3070 continue;
3074 SAFE_FREE(ip_list);
3075 } else {
3077 * Server not an empty string ... Check the rest and see what
3078 * gives
3080 if (*share == '\0') {
3081 if (*path != '\0') {
3083 /* Should not have empty share with path */
3084 errno = EINVAL + 8197;
3085 if (dir) {
3086 SAFE_FREE(dir->fname);
3087 SAFE_FREE(dir);
3089 TALLOC_FREE(frame);
3090 return NULL;
3095 * We don't know if <server> is really a server name
3096 * or is a workgroup/domain name. If we already have
3097 * a server structure for it, we'll use it.
3098 * Otherwise, check to see if <server><1D>,
3099 * <server><1B>, or <server><20> translates. We check
3100 * to see if <server> is an IP address first.
3104 * See if we have an existing server. Do not
3105 * establish a connection if one does not already
3106 * exist.
3108 srv = smbc_server(frame, context, False, server, "IPC$",
3109 &workgroup, &user, &password);
3112 * If no existing server and not an IP addr, look for
3113 * LMB or DMB
3115 if (!srv &&
3116 !is_ipaddress(server) &&
3117 (resolve_name(server, &rem_ss, 0x1d) || /* LMB */
3118 resolve_name(server, &rem_ss, 0x1b) )) { /* DMB */
3120 fstring buserver;
3122 dir->dir_type = SMBC_SERVER;
3125 * Get the backup list ...
3127 if (!name_status_find(server, 0, 0,
3128 &rem_ss, buserver)) {
3130 DEBUG(0, ("Could not get name of "
3131 "local/domain master browser "
3132 "for server %s\n", server));
3133 if (dir) {
3134 SAFE_FREE(dir->fname);
3135 SAFE_FREE(dir);
3137 errno = EPERM;
3138 TALLOC_FREE(frame);
3139 return NULL;
3144 * Get a connection to IPC$ on the server if
3145 * we do not already have one
3147 srv = smbc_server(frame, context, True,
3148 buserver, "IPC$",
3149 &workgroup, &user, &password);
3150 if (!srv) {
3151 DEBUG(0, ("got no contact to IPC$\n"));
3152 if (dir) {
3153 SAFE_FREE(dir->fname);
3154 SAFE_FREE(dir);
3156 TALLOC_FREE(frame);
3157 return NULL;
3161 dir->srv = srv;
3163 /* Now, list the servers ... */
3164 if (!cli_NetServerEnum(srv->cli, server,
3165 0x0000FFFE, list_fn,
3166 (void *)dir)) {
3168 if (dir) {
3169 SAFE_FREE(dir->fname);
3170 SAFE_FREE(dir);
3172 TALLOC_FREE(frame);
3173 return NULL;
3175 } else if (srv ||
3176 (resolve_name(server, &rem_ss, 0x20))) {
3178 /* If we hadn't found the server, get one now */
3179 if (!srv) {
3180 srv = smbc_server(frame, context, True,
3181 server, "IPC$",
3182 &workgroup,
3183 &user, &password);
3186 if (!srv) {
3187 if (dir) {
3188 SAFE_FREE(dir->fname);
3189 SAFE_FREE(dir);
3191 TALLOC_FREE(frame);
3192 return NULL;
3196 dir->dir_type = SMBC_FILE_SHARE;
3197 dir->srv = srv;
3199 /* List the shares ... */
3201 if (net_share_enum_rpc(
3202 srv->cli,
3203 list_fn,
3204 (void *) dir) < 0 &&
3205 cli_RNetShareEnum(
3206 srv->cli,
3207 list_fn,
3208 (void *)dir) < 0) {
3210 errno = cli_errno(srv->cli);
3211 if (dir) {
3212 SAFE_FREE(dir->fname);
3213 SAFE_FREE(dir);
3215 TALLOC_FREE(frame);
3216 return NULL;
3219 } else {
3220 /* Neither the workgroup nor server exists */
3221 errno = ECONNREFUSED;
3222 if (dir) {
3223 SAFE_FREE(dir->fname);
3224 SAFE_FREE(dir);
3226 TALLOC_FREE(frame);
3227 return NULL;
3231 else {
3233 * The server and share are specified ... work from
3234 * there ...
3236 char *targetpath;
3237 struct cli_state *targetcli;
3239 /* We connect to the server and list the directory */
3240 dir->dir_type = SMBC_FILE_SHARE;
3242 srv = smbc_server(frame, context, True, server, share,
3243 &workgroup, &user, &password);
3245 if (!srv) {
3246 if (dir) {
3247 SAFE_FREE(dir->fname);
3248 SAFE_FREE(dir);
3250 TALLOC_FREE(frame);
3251 return NULL;
3254 dir->srv = srv;
3256 /* Now, list the files ... */
3258 p = path + strlen(path);
3259 path = talloc_asprintf_append(path, "\\*");
3260 if (!path) {
3261 if (dir) {
3262 SAFE_FREE(dir->fname);
3263 SAFE_FREE(dir);
3265 TALLOC_FREE(frame);
3266 return NULL;
3269 if (!cli_resolve_path(frame, "", srv->cli, path,
3270 &targetcli, &targetpath)) {
3271 d_printf("Could not resolve %s\n", path);
3272 if (dir) {
3273 SAFE_FREE(dir->fname);
3274 SAFE_FREE(dir);
3276 TALLOC_FREE(frame);
3277 return NULL;
3280 if (cli_list(targetcli, targetpath,
3281 aDIR | aSYSTEM | aHIDDEN,
3282 dir_list_fn, (void *)dir) < 0) {
3284 if (dir) {
3285 SAFE_FREE(dir->fname);
3286 SAFE_FREE(dir);
3288 saved_errno = smbc_errno(context, targetcli);
3290 if (saved_errno == EINVAL) {
3292 * See if they asked to opendir something
3293 * other than a directory. If so, the
3294 * converted error value we got would have
3295 * been EINVAL rather than ENOTDIR.
3297 *p = '\0'; /* restore original path */
3299 if (smbc_getatr(context, srv, path,
3300 &mode, NULL,
3301 NULL, NULL, NULL, NULL,
3302 NULL) &&
3303 ! IS_DOS_DIR(mode)) {
3305 /* It is. Correct the error value */
3306 saved_errno = ENOTDIR;
3311 * If there was an error and the server is no
3312 * good any more...
3314 cb = &context->callbacks;
3315 if (cli_is_error(targetcli) &&
3316 (cb->check_server_fn)(context, srv)) {
3318 /* ... then remove it. */
3319 if ((cb->remove_unused_server_fn)(context,
3320 srv)) {
3322 * We could not remove the
3323 * server completely, remove
3324 * it from the cache so we
3325 * will not get it again. It
3326 * will be removed when the
3327 * last file/dir is closed.
3329 (cb->remove_cached_srv_fn)(context,
3330 srv);
3334 errno = saved_errno;
3335 TALLOC_FREE(frame);
3336 return NULL;
3342 DLIST_ADD(context->internal->_files, dir);
3343 TALLOC_FREE(frame);
3344 return dir;
3349 * Routine to close a directory
3352 static int
3353 smbc_closedir_ctx(SMBCCTX *context,
3354 SMBCFILE *dir)
3356 TALLOC_CTX *frame = talloc_stackframe();
3358 if (!context || !context->internal ||
3359 !context->internal->_initialized) {
3360 errno = EINVAL;
3361 TALLOC_FREE(frame);
3362 return -1;
3365 if (!dir || !DLIST_CONTAINS(context->internal->_files, dir)) {
3366 errno = EBADF;
3367 TALLOC_FREE(frame);
3368 return -1;
3371 smbc_remove_dir(dir); /* Clean it up */
3373 DLIST_REMOVE(context->internal->_files, dir);
3375 if (dir) {
3377 SAFE_FREE(dir->fname);
3378 SAFE_FREE(dir); /* Free the space too */
3381 TALLOC_FREE(frame);
3382 return 0;
3386 static void
3387 smbc_readdir_internal(SMBCCTX * context,
3388 struct smbc_dirent *dest,
3389 struct smbc_dirent *src,
3390 int max_namebuf_len)
3392 if (context->options.urlencode_readdir_entries) {
3394 /* url-encode the name. get back remaining buffer space */
3395 max_namebuf_len =
3396 smbc_urlencode(dest->name, src->name, max_namebuf_len);
3398 /* We now know the name length */
3399 dest->namelen = strlen(dest->name);
3401 /* Save the pointer to the beginning of the comment */
3402 dest->comment = dest->name + dest->namelen + 1;
3404 /* Copy the comment */
3405 strncpy(dest->comment, src->comment, max_namebuf_len - 1);
3406 dest->comment[max_namebuf_len - 1] = '\0';
3408 /* Save other fields */
3409 dest->smbc_type = src->smbc_type;
3410 dest->commentlen = strlen(dest->comment);
3411 dest->dirlen = ((dest->comment + dest->commentlen + 1) -
3412 (char *) dest);
3413 } else {
3415 /* No encoding. Just copy the entry as is. */
3416 memcpy(dest, src, src->dirlen);
3417 dest->comment = (char *)(&dest->name + src->namelen + 1);
3423 * Routine to get a directory entry
3426 struct smbc_dirent *
3427 smbc_readdir_ctx(SMBCCTX *context,
3428 SMBCFILE *dir)
3430 int maxlen;
3431 struct smbc_dirent *dirp, *dirent;
3432 TALLOC_CTX *frame = talloc_stackframe();
3434 /* Check that all is ok first ... */
3436 if (!context || !context->internal ||
3437 !context->internal->_initialized) {
3439 errno = EINVAL;
3440 DEBUG(0, ("Invalid context in smbc_readdir_ctx()\n"));
3441 TALLOC_FREE(frame);
3442 return NULL;
3446 if (!dir || !DLIST_CONTAINS(context->internal->_files, dir)) {
3448 errno = EBADF;
3449 DEBUG(0, ("Invalid dir in smbc_readdir_ctx()\n"));
3450 TALLOC_FREE(frame);
3451 return NULL;
3455 if (dir->file != False) { /* FIXME, should be dir, perhaps */
3457 errno = ENOTDIR;
3458 DEBUG(0, ("Found file vs directory in smbc_readdir_ctx()\n"));
3459 TALLOC_FREE(frame);
3460 return NULL;
3464 if (!dir->dir_next) {
3465 TALLOC_FREE(frame);
3466 return NULL;
3469 dirent = dir->dir_next->dirent;
3470 if (!dirent) {
3472 errno = ENOENT;
3473 TALLOC_FREE(frame);
3474 return NULL;
3478 dirp = (struct smbc_dirent *)context->internal->_dirent;
3479 maxlen = (sizeof(context->internal->_dirent) -
3480 sizeof(struct smbc_dirent));
3482 smbc_readdir_internal(context, dirp, dirent, maxlen);
3484 dir->dir_next = dir->dir_next->next;
3486 TALLOC_FREE(frame);
3487 return dirp;
3491 * Routine to get directory entries
3494 static int
3495 smbc_getdents_ctx(SMBCCTX *context,
3496 SMBCFILE *dir,
3497 struct smbc_dirent *dirp,
3498 int count)
3500 int rem = count;
3501 int reqd;
3502 int maxlen;
3503 char *ndir = (char *)dirp;
3504 struct smbc_dir_list *dirlist;
3505 TALLOC_CTX *frame = talloc_stackframe();
3507 /* Check that all is ok first ... */
3509 if (!context || !context->internal ||
3510 !context->internal->_initialized) {
3512 errno = EINVAL;
3513 TALLOC_FREE(frame);
3514 return -1;
3518 if (!dir || !DLIST_CONTAINS(context->internal->_files, dir)) {
3520 errno = EBADF;
3521 TALLOC_FREE(frame);
3522 return -1;
3526 if (dir->file != False) { /* FIXME, should be dir, perhaps */
3528 errno = ENOTDIR;
3529 TALLOC_FREE(frame);
3530 return -1;
3535 * Now, retrieve the number of entries that will fit in what was passed
3536 * We have to figure out if the info is in the list, or we need to
3537 * send a request to the server to get the info.
3540 while ((dirlist = dir->dir_next)) {
3541 struct smbc_dirent *dirent;
3543 if (!dirlist->dirent) {
3545 errno = ENOENT; /* Bad error */
3546 TALLOC_FREE(frame);
3547 return -1;
3551 /* Do urlencoding of next entry, if so selected */
3552 dirent = (struct smbc_dirent *)context->internal->_dirent;
3553 maxlen = (sizeof(context->internal->_dirent) -
3554 sizeof(struct smbc_dirent));
3555 smbc_readdir_internal(context, dirent, dirlist->dirent, maxlen);
3557 reqd = dirent->dirlen;
3559 if (rem < reqd) {
3561 if (rem < count) { /* We managed to copy something */
3563 errno = 0;
3564 TALLOC_FREE(frame);
3565 return count - rem;
3568 else { /* Nothing copied ... */
3570 errno = EINVAL; /* Not enough space ... */
3571 TALLOC_FREE(frame);
3572 return -1;
3578 memcpy(ndir, dirent, reqd); /* Copy the data in ... */
3580 ((struct smbc_dirent *)ndir)->comment =
3581 (char *)(&((struct smbc_dirent *)ndir)->name +
3582 dirent->namelen +
3585 ndir += reqd;
3587 rem -= reqd;
3589 dir->dir_next = dirlist = dirlist -> next;
3592 TALLOC_FREE(frame);
3594 if (rem == count)
3595 return 0;
3596 else
3597 return count - rem;
3602 * Routine to create a directory ...
3605 static int
3606 smbc_mkdir_ctx(SMBCCTX *context,
3607 const char *fname,
3608 mode_t mode)
3610 SMBCSRV *srv = NULL;
3611 char *server = NULL;
3612 char *share = NULL;
3613 char *user = NULL;
3614 char *password = NULL;
3615 char *workgroup = NULL;
3616 char *path = NULL;
3617 char *targetpath = NULL;
3618 struct cli_state *targetcli = NULL;
3619 TALLOC_CTX *frame = talloc_stackframe();
3621 if (!context || !context->internal ||
3622 !context->internal->_initialized) {
3623 errno = EINVAL;
3624 TALLOC_FREE(frame);
3625 return -1;
3628 if (!fname) {
3629 errno = EINVAL;
3630 TALLOC_FREE(frame);
3631 return -1;
3634 DEBUG(4, ("smbc_mkdir(%s)\n", fname));
3636 if (smbc_parse_path(frame,
3637 context,
3638 fname,
3639 &workgroup,
3640 &server,
3641 &share,
3642 &path,
3643 &user,
3644 &password,
3645 NULL)) {
3646 errno = EINVAL;
3647 TALLOC_FREE(frame);
3648 return -1;
3651 if (!user || user[0] == (char)0) {
3652 user = talloc_strdup(frame, context->user);
3653 if (!user) {
3654 errno = ENOMEM;
3655 TALLOC_FREE(frame);
3656 return -1;
3660 srv = smbc_server(frame, context, True,
3661 server, share, &workgroup, &user, &password);
3663 if (!srv) {
3665 TALLOC_FREE(frame);
3666 return -1; /* errno set by smbc_server */
3670 /*d_printf(">>>mkdir: resolving %s\n", path);*/
3671 if (!cli_resolve_path(frame, "", srv->cli, path,
3672 &targetcli, &targetpath)) {
3673 d_printf("Could not resolve %s\n", path);
3674 TALLOC_FREE(frame);
3675 return -1;
3677 /*d_printf(">>>mkdir: resolved path as %s\n", targetpath);*/
3679 if (!cli_mkdir(targetcli, targetpath)) {
3681 errno = smbc_errno(context, targetcli);
3682 TALLOC_FREE(frame);
3683 return -1;
3687 TALLOC_FREE(frame);
3688 return 0;
3693 * Our list function simply checks to see if a directory is not empty
3696 static int smbc_rmdir_dirempty = True;
3698 static void
3699 rmdir_list_fn(const char *mnt,
3700 file_info *finfo,
3701 const char *mask,
3702 void *state)
3704 if (strncmp(finfo->name, ".", 1) != 0 &&
3705 strncmp(finfo->name, "..", 2) != 0) {
3706 smbc_rmdir_dirempty = False;
3711 * Routine to remove a directory
3714 static int
3715 smbc_rmdir_ctx(SMBCCTX *context,
3716 const char *fname)
3718 SMBCSRV *srv = NULL;
3719 char *server = NULL;
3720 char *share = NULL;
3721 char *user = NULL;
3722 char *password = NULL;
3723 char *workgroup = NULL;
3724 char *path = NULL;
3725 char *targetpath = NULL;
3726 struct cli_state *targetcli = NULL;
3727 TALLOC_CTX *frame = talloc_stackframe();
3729 if (!context || !context->internal ||
3730 !context->internal->_initialized) {
3731 errno = EINVAL;
3732 TALLOC_FREE(frame);
3733 return -1;
3736 if (!fname) {
3737 errno = EINVAL;
3738 TALLOC_FREE(frame);
3739 return -1;
3742 DEBUG(4, ("smbc_rmdir(%s)\n", fname));
3744 if (smbc_parse_path(frame,
3745 context,
3746 fname,
3747 &workgroup,
3748 &server,
3749 &share,
3750 &path,
3751 &user,
3752 &password,
3753 NULL)) {
3754 errno = EINVAL;
3755 TALLOC_FREE(frame);
3756 return -1;
3759 if (!user || user[0] == (char)0) {
3760 user = talloc_strdup(frame, context->user);
3761 if (!user) {
3762 errno = ENOMEM;
3763 TALLOC_FREE(frame);
3764 return -1;
3768 srv = smbc_server(frame, context, True,
3769 server, share, &workgroup, &user, &password);
3771 if (!srv) {
3773 TALLOC_FREE(frame);
3774 return -1; /* errno set by smbc_server */
3778 /*d_printf(">>>rmdir: resolving %s\n", path);*/
3779 if (!cli_resolve_path(frame, "", srv->cli, path,
3780 &targetcli, &targetpath)) {
3781 d_printf("Could not resolve %s\n", path);
3782 TALLOC_FREE(frame);
3783 return -1;
3785 /*d_printf(">>>rmdir: resolved path as %s\n", targetpath);*/
3788 if (!cli_rmdir(targetcli, targetpath)) {
3790 errno = smbc_errno(context, targetcli);
3792 if (errno == EACCES) { /* Check if the dir empty or not */
3794 /* Local storage to avoid buffer overflows */
3795 char *lpath;
3797 smbc_rmdir_dirempty = True; /* Make this so ... */
3799 lpath = talloc_asprintf(frame, "%s\\*",
3800 targetpath);
3801 if (!lpath) {
3802 errno = ENOMEM;
3803 TALLOC_FREE(frame);
3804 return -1;
3807 if (cli_list(targetcli, lpath,
3808 aDIR | aSYSTEM | aHIDDEN,
3809 rmdir_list_fn, NULL) < 0) {
3811 /* Fix errno to ignore latest error ... */
3812 DEBUG(5, ("smbc_rmdir: "
3813 "cli_list returned an error: %d\n",
3814 smbc_errno(context, targetcli)));
3815 errno = EACCES;
3819 if (smbc_rmdir_dirempty)
3820 errno = EACCES;
3821 else
3822 errno = ENOTEMPTY;
3826 TALLOC_FREE(frame);
3827 return -1;
3831 TALLOC_FREE(frame);
3832 return 0;
3837 * Routine to return the current directory position
3840 static off_t
3841 smbc_telldir_ctx(SMBCCTX *context,
3842 SMBCFILE *dir)
3844 TALLOC_CTX *frame = talloc_stackframe();
3846 if (!context || !context->internal ||
3847 !context->internal->_initialized) {
3849 errno = EINVAL;
3850 TALLOC_FREE(frame);
3851 return -1;
3855 if (!dir || !DLIST_CONTAINS(context->internal->_files, dir)) {
3857 errno = EBADF;
3858 TALLOC_FREE(frame);
3859 return -1;
3863 if (dir->file != False) { /* FIXME, should be dir, perhaps */
3865 errno = ENOTDIR;
3866 TALLOC_FREE(frame);
3867 return -1;
3871 /* See if we're already at the end. */
3872 if (dir->dir_next == NULL) {
3873 /* We are. */
3874 TALLOC_FREE(frame);
3875 return -1;
3879 * We return the pointer here as the offset
3881 TALLOC_FREE(frame);
3882 return (off_t)(long)dir->dir_next->dirent;
3886 * A routine to run down the list and see if the entry is OK
3889 struct smbc_dir_list *
3890 smbc_check_dir_ent(struct smbc_dir_list *list,
3891 struct smbc_dirent *dirent)
3894 /* Run down the list looking for what we want */
3896 if (dirent) {
3898 struct smbc_dir_list *tmp = list;
3900 while (tmp) {
3902 if (tmp->dirent == dirent)
3903 return tmp;
3905 tmp = tmp->next;
3911 return NULL; /* Not found, or an error */
3917 * Routine to seek on a directory
3920 static int
3921 smbc_lseekdir_ctx(SMBCCTX *context,
3922 SMBCFILE *dir,
3923 off_t offset)
3925 long int l_offset = offset; /* Handle problems of size */
3926 struct smbc_dirent *dirent = (struct smbc_dirent *)l_offset;
3927 struct smbc_dir_list *list_ent = (struct smbc_dir_list *)NULL;
3928 TALLOC_CTX *frame = talloc_stackframe();
3930 if (!context || !context->internal ||
3931 !context->internal->_initialized) {
3933 errno = EINVAL;
3934 TALLOC_FREE(frame);
3935 return -1;
3939 if (dir->file != False) { /* FIXME, should be dir, perhaps */
3941 errno = ENOTDIR;
3942 TALLOC_FREE(frame);
3943 return -1;
3947 /* Now, check what we were passed and see if it is OK ... */
3949 if (dirent == NULL) { /* Seek to the begining of the list */
3951 dir->dir_next = dir->dir_list;
3952 TALLOC_FREE(frame);
3953 return 0;
3957 if (offset == -1) { /* Seek to the end of the list */
3958 dir->dir_next = NULL;
3959 TALLOC_FREE(frame);
3960 return 0;
3963 /* Now, run down the list and make sure that the entry is OK */
3964 /* This may need to be changed if we change the format of the list */
3966 if ((list_ent = smbc_check_dir_ent(dir->dir_list, dirent)) == NULL) {
3967 errno = EINVAL; /* Bad entry */
3968 TALLOC_FREE(frame);
3969 return -1;
3972 dir->dir_next = list_ent;
3974 TALLOC_FREE(frame);
3975 return 0;
3979 * Routine to fstat a dir
3982 static int
3983 smbc_fstatdir_ctx(SMBCCTX *context,
3984 SMBCFILE *dir,
3985 struct stat *st)
3988 if (!context || !context->internal ||
3989 !context->internal->_initialized) {
3990 errno = EINVAL;
3991 return -1;
3994 /* No code yet ... */
3995 return 0;
3998 static int
3999 smbc_chmod_ctx(SMBCCTX *context,
4000 const char *fname,
4001 mode_t newmode)
4003 SMBCSRV *srv = NULL;
4004 char *server = NULL;
4005 char *share = NULL;
4006 char *user = NULL;
4007 char *password = NULL;
4008 char *workgroup = NULL;
4009 char *path = NULL;
4010 uint16 mode;
4011 TALLOC_CTX *frame = talloc_stackframe();
4013 if (!context || !context->internal ||
4014 !context->internal->_initialized) {
4015 errno = EINVAL; /* Best I can think of ... */
4016 TALLOC_FREE(frame);
4017 return -1;
4020 if (!fname) {
4021 errno = EINVAL;
4022 TALLOC_FREE(frame);
4023 return -1;
4026 DEBUG(4, ("smbc_chmod(%s, 0%3o)\n", fname, newmode));
4028 if (smbc_parse_path(frame,
4029 context,
4030 fname,
4031 &workgroup,
4032 &server,
4033 &share,
4034 &path,
4035 &user,
4036 &password,
4037 NULL)) {
4038 errno = EINVAL;
4039 TALLOC_FREE(frame);
4040 return -1;
4043 if (!user || user[0] == (char)0) {
4044 user = talloc_strdup(frame, context->user);
4045 if (!user) {
4046 errno = ENOMEM;
4047 TALLOC_FREE(frame);
4048 return -1;
4052 srv = smbc_server(frame, context, True,
4053 server, share, &workgroup, &user, &password);
4055 if (!srv) {
4056 TALLOC_FREE(frame);
4057 return -1; /* errno set by smbc_server */
4060 mode = 0;
4062 if (!(newmode & (S_IWUSR | S_IWGRP | S_IWOTH))) mode |= aRONLY;
4063 if ((newmode & S_IXUSR) && lp_map_archive(-1)) mode |= aARCH;
4064 if ((newmode & S_IXGRP) && lp_map_system(-1)) mode |= aSYSTEM;
4065 if ((newmode & S_IXOTH) && lp_map_hidden(-1)) mode |= aHIDDEN;
4067 if (!cli_setatr(srv->cli, path, mode, 0)) {
4068 errno = smbc_errno(context, srv->cli);
4069 TALLOC_FREE(frame);
4070 return -1;
4073 TALLOC_FREE(frame);
4074 return 0;
4077 static int
4078 smbc_utimes_ctx(SMBCCTX *context,
4079 const char *fname,
4080 struct timeval *tbuf)
4082 SMBCSRV *srv = NULL;
4083 char *server = NULL;
4084 char *share = NULL;
4085 char *user = NULL;
4086 char *password = NULL;
4087 char *workgroup = NULL;
4088 char *path = NULL;
4089 time_t access_time;
4090 time_t write_time;
4091 TALLOC_CTX *frame = talloc_stackframe();
4093 if (!context || !context->internal ||
4094 !context->internal->_initialized) {
4095 errno = EINVAL; /* Best I can think of ... */
4096 TALLOC_FREE(frame);
4097 return -1;
4100 if (!fname) {
4101 errno = EINVAL;
4102 TALLOC_FREE(frame);
4103 return -1;
4106 if (tbuf == NULL) {
4107 access_time = write_time = time(NULL);
4108 } else {
4109 access_time = tbuf[0].tv_sec;
4110 write_time = tbuf[1].tv_sec;
4113 if (DEBUGLVL(4)) {
4114 char *p;
4115 char atimebuf[32];
4116 char mtimebuf[32];
4118 strncpy(atimebuf, ctime(&access_time), sizeof(atimebuf) - 1);
4119 atimebuf[sizeof(atimebuf) - 1] = '\0';
4120 if ((p = strchr(atimebuf, '\n')) != NULL) {
4121 *p = '\0';
4124 strncpy(mtimebuf, ctime(&write_time), sizeof(mtimebuf) - 1);
4125 mtimebuf[sizeof(mtimebuf) - 1] = '\0';
4126 if ((p = strchr(mtimebuf, '\n')) != NULL) {
4127 *p = '\0';
4130 dbgtext("smbc_utimes(%s, atime = %s mtime = %s)\n",
4131 fname, atimebuf, mtimebuf);
4134 if (smbc_parse_path(frame,
4135 context,
4136 fname,
4137 &workgroup,
4138 &server,
4139 &share,
4140 &path,
4141 &user,
4142 &password,
4143 NULL)) {
4144 errno = EINVAL;
4145 TALLOC_FREE(frame);
4146 return -1;
4149 if (!user || user[0] == (char)0) {
4150 user = talloc_strdup(frame, context->user);
4151 if (!user) {
4152 errno = ENOMEM;
4153 TALLOC_FREE(frame);
4154 return -1;
4158 srv = smbc_server(frame, context, True,
4159 server, share, &workgroup, &user, &password);
4161 if (!srv) {
4162 TALLOC_FREE(frame);
4163 return -1; /* errno set by smbc_server */
4166 if (!smbc_setatr(context, srv, path,
4167 0, access_time, write_time, 0, 0)) {
4168 TALLOC_FREE(frame);
4169 return -1; /* errno set by smbc_setatr */
4172 TALLOC_FREE(frame);
4173 return 0;
4178 * Sort ACEs according to the documentation at
4179 * http://support.microsoft.com/kb/269175, at least as far as it defines the
4180 * order.
4183 static int
4184 ace_compare(SEC_ACE *ace1,
4185 SEC_ACE *ace2)
4187 bool b1;
4188 bool b2;
4190 /* If the ACEs are equal, we have nothing more to do. */
4191 if (sec_ace_equal(ace1, ace2)) {
4192 return 0;
4195 /* Inherited follow non-inherited */
4196 b1 = ((ace1->flags & SEC_ACE_FLAG_INHERITED_ACE) != 0);
4197 b2 = ((ace2->flags & SEC_ACE_FLAG_INHERITED_ACE) != 0);
4198 if (b1 != b2) {
4199 return (b1 ? 1 : -1);
4203 * What shall we do with AUDITs and ALARMs? It's undefined. We'll
4204 * sort them after DENY and ALLOW.
4206 b1 = (ace1->type != SEC_ACE_TYPE_ACCESS_ALLOWED &&
4207 ace1->type != SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT &&
4208 ace1->type != SEC_ACE_TYPE_ACCESS_DENIED &&
4209 ace1->type != SEC_ACE_TYPE_ACCESS_DENIED_OBJECT);
4210 b2 = (ace2->type != SEC_ACE_TYPE_ACCESS_ALLOWED &&
4211 ace2->type != SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT &&
4212 ace2->type != SEC_ACE_TYPE_ACCESS_DENIED &&
4213 ace2->type != SEC_ACE_TYPE_ACCESS_DENIED_OBJECT);
4214 if (b1 != b2) {
4215 return (b1 ? 1 : -1);
4218 /* Allowed ACEs follow denied ACEs */
4219 b1 = (ace1->type == SEC_ACE_TYPE_ACCESS_ALLOWED ||
4220 ace1->type == SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT);
4221 b2 = (ace2->type == SEC_ACE_TYPE_ACCESS_ALLOWED ||
4222 ace2->type == SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT);
4223 if (b1 != b2) {
4224 return (b1 ? 1 : -1);
4228 * ACEs applying to an entity's object follow those applying to the
4229 * entity itself
4231 b1 = (ace1->type == SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT ||
4232 ace1->type == SEC_ACE_TYPE_ACCESS_DENIED_OBJECT);
4233 b2 = (ace2->type == SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT ||
4234 ace2->type == SEC_ACE_TYPE_ACCESS_DENIED_OBJECT);
4235 if (b1 != b2) {
4236 return (b1 ? 1 : -1);
4240 * If we get this far, the ACEs are similar as far as the
4241 * characteristics we typically care about (those defined by the
4242 * referenced MS document). We'll now sort by characteristics that
4243 * just seems reasonable.
4246 if (ace1->type != ace2->type) {
4247 return ace2->type - ace1->type;
4250 if (sid_compare(&ace1->trustee, &ace2->trustee)) {
4251 return sid_compare(&ace1->trustee, &ace2->trustee);
4254 if (ace1->flags != ace2->flags) {
4255 return ace1->flags - ace2->flags;
4258 if (ace1->access_mask != ace2->access_mask) {
4259 return ace1->access_mask - ace2->access_mask;
4262 if (ace1->size != ace2->size) {
4263 return ace1->size - ace2->size;
4266 return memcmp(ace1, ace2, sizeof(SEC_ACE));
4270 static void
4271 sort_acl(SEC_ACL *the_acl)
4273 uint32 i;
4274 if (!the_acl) return;
4276 qsort(the_acl->aces, the_acl->num_aces, sizeof(the_acl->aces[0]),
4277 QSORT_CAST ace_compare);
4279 for (i=1;i<the_acl->num_aces;) {
4280 if (sec_ace_equal(&the_acl->aces[i-1], &the_acl->aces[i])) {
4281 int j;
4282 for (j=i; j<the_acl->num_aces-1; j++) {
4283 the_acl->aces[j] = the_acl->aces[j+1];
4285 the_acl->num_aces--;
4286 } else {
4287 i++;
4292 /* convert a SID to a string, either numeric or username/group */
4293 static void
4294 convert_sid_to_string(struct cli_state *ipc_cli,
4295 POLICY_HND *pol,
4296 fstring str,
4297 bool numeric,
4298 DOM_SID *sid)
4300 char **domains = NULL;
4301 char **names = NULL;
4302 enum lsa_SidType *types = NULL;
4303 struct rpc_pipe_client *pipe_hnd = find_lsa_pipe_hnd(ipc_cli);
4304 TALLOC_CTX *ctx;
4306 sid_to_fstring(str, sid);
4308 if (numeric) {
4309 return; /* no lookup desired */
4312 if (!pipe_hnd) {
4313 return;
4316 /* Ask LSA to convert the sid to a name */
4318 ctx = talloc_stackframe();
4320 if (!NT_STATUS_IS_OK(rpccli_lsa_lookup_sids(pipe_hnd, ctx,
4321 pol, 1, sid, &domains,
4322 &names, &types)) ||
4323 !domains || !domains[0] || !names || !names[0]) {
4324 TALLOC_FREE(ctx);
4325 return;
4328 TALLOC_FREE(ctx);
4329 /* Converted OK */
4331 slprintf(str, sizeof(fstring) - 1, "%s%s%s",
4332 domains[0], lp_winbind_separator(),
4333 names[0]);
4336 /* convert a string to a SID, either numeric or username/group */
4337 static bool
4338 convert_string_to_sid(struct cli_state *ipc_cli,
4339 POLICY_HND *pol,
4340 bool numeric,
4341 DOM_SID *sid,
4342 const char *str)
4344 enum lsa_SidType *types = NULL;
4345 DOM_SID *sids = NULL;
4346 bool result = True;
4347 TALLOC_CTX *ctx = NULL;
4348 struct rpc_pipe_client *pipe_hnd = find_lsa_pipe_hnd(ipc_cli);
4350 if (!pipe_hnd) {
4351 return False;
4354 if (numeric) {
4355 if (strncmp(str, "S-", 2) == 0) {
4356 return string_to_sid(sid, str);
4359 result = False;
4360 goto done;
4363 ctx = talloc_stackframe();
4364 if (!NT_STATUS_IS_OK(rpccli_lsa_lookup_names(pipe_hnd, ctx,
4365 pol, 1, &str, NULL, 1, &sids,
4366 &types))) {
4367 result = False;
4368 goto done;
4371 sid_copy(sid, &sids[0]);
4372 done:
4374 TALLOC_FREE(ctx);
4375 return result;
4379 /* parse an ACE in the same format as print_ace() */
4380 static bool
4381 parse_ace(struct cli_state *ipc_cli,
4382 POLICY_HND *pol,
4383 SEC_ACE *ace,
4384 bool numeric,
4385 char *str)
4387 char *p;
4388 const char *cp;
4389 char *tok;
4390 unsigned int atype;
4391 unsigned int aflags;
4392 unsigned int amask;
4393 DOM_SID sid;
4394 SEC_ACCESS mask;
4395 const struct perm_value *v;
4396 struct perm_value {
4397 const char *perm;
4398 uint32 mask;
4400 TALLOC_CTX *frame = talloc_stackframe();
4402 /* These values discovered by inspection */
4403 static const struct perm_value special_values[] = {
4404 { "R", 0x00120089 },
4405 { "W", 0x00120116 },
4406 { "X", 0x001200a0 },
4407 { "D", 0x00010000 },
4408 { "P", 0x00040000 },
4409 { "O", 0x00080000 },
4410 { NULL, 0 },
4413 static const struct perm_value standard_values[] = {
4414 { "READ", 0x001200a9 },
4415 { "CHANGE", 0x001301bf },
4416 { "FULL", 0x001f01ff },
4417 { NULL, 0 },
4421 ZERO_STRUCTP(ace);
4422 p = strchr_m(str,':');
4423 if (!p) {
4424 TALLOC_FREE(frame);
4425 return False;
4427 *p = '\0';
4428 p++;
4429 /* Try to parse numeric form */
4431 if (sscanf(p, "%i/%i/%i", &atype, &aflags, &amask) == 3 &&
4432 convert_string_to_sid(ipc_cli, pol, numeric, &sid, str)) {
4433 goto done;
4436 /* Try to parse text form */
4438 if (!convert_string_to_sid(ipc_cli, pol, numeric, &sid, str)) {
4439 TALLOC_FREE(frame);
4440 return false;
4443 cp = p;
4444 if (!next_token_talloc(frame, &cp, &tok, "/")) {
4445 TALLOC_FREE(frame);
4446 return false;
4449 if (StrnCaseCmp(tok, "ALLOWED", strlen("ALLOWED")) == 0) {
4450 atype = SEC_ACE_TYPE_ACCESS_ALLOWED;
4451 } else if (StrnCaseCmp(tok, "DENIED", strlen("DENIED")) == 0) {
4452 atype = SEC_ACE_TYPE_ACCESS_DENIED;
4453 } else {
4454 TALLOC_FREE(frame);
4455 return false;
4458 /* Only numeric form accepted for flags at present */
4460 if (!(next_token_talloc(frame, &cp, &tok, "/") &&
4461 sscanf(tok, "%i", &aflags))) {
4462 TALLOC_FREE(frame);
4463 return false;
4466 if (!next_token_talloc(frame, &cp, &tok, "/")) {
4467 TALLOC_FREE(frame);
4468 return false;
4471 if (strncmp(tok, "0x", 2) == 0) {
4472 if (sscanf(tok, "%i", &amask) != 1) {
4473 TALLOC_FREE(frame);
4474 return false;
4476 goto done;
4479 for (v = standard_values; v->perm; v++) {
4480 if (strcmp(tok, v->perm) == 0) {
4481 amask = v->mask;
4482 goto done;
4486 p = tok;
4488 while(*p) {
4489 bool found = False;
4491 for (v = special_values; v->perm; v++) {
4492 if (v->perm[0] == *p) {
4493 amask |= v->mask;
4494 found = True;
4498 if (!found) {
4499 TALLOC_FREE(frame);
4500 return false;
4502 p++;
4505 if (*p) {
4506 TALLOC_FREE(frame);
4507 return false;
4510 done:
4511 mask = amask;
4512 init_sec_ace(ace, &sid, atype, mask, aflags);
4513 TALLOC_FREE(frame);
4514 return true;
4517 /* add an ACE to a list of ACEs in a SEC_ACL */
4518 static bool
4519 add_ace(SEC_ACL **the_acl,
4520 SEC_ACE *ace,
4521 TALLOC_CTX *ctx)
4523 SEC_ACL *newacl;
4524 SEC_ACE *aces;
4526 if (! *the_acl) {
4527 (*the_acl) = make_sec_acl(ctx, 3, 1, ace);
4528 return True;
4531 if ((aces = SMB_CALLOC_ARRAY(SEC_ACE, 1+(*the_acl)->num_aces)) == NULL) {
4532 return False;
4534 memcpy(aces, (*the_acl)->aces, (*the_acl)->num_aces * sizeof(SEC_ACE));
4535 memcpy(aces+(*the_acl)->num_aces, ace, sizeof(SEC_ACE));
4536 newacl = make_sec_acl(ctx, (*the_acl)->revision,
4537 1+(*the_acl)->num_aces, aces);
4538 SAFE_FREE(aces);
4539 (*the_acl) = newacl;
4540 return True;
4544 /* parse a ascii version of a security descriptor */
4545 static SEC_DESC *
4546 sec_desc_parse(TALLOC_CTX *ctx,
4547 struct cli_state *ipc_cli,
4548 POLICY_HND *pol,
4549 bool numeric,
4550 char *str)
4552 const char *p = str;
4553 char *tok;
4554 SEC_DESC *ret = NULL;
4555 size_t sd_size;
4556 DOM_SID *group_sid=NULL;
4557 DOM_SID *owner_sid=NULL;
4558 SEC_ACL *dacl=NULL;
4559 int revision=1;
4561 while (next_token_talloc(ctx, &p, &tok, "\t,\r\n")) {
4563 if (StrnCaseCmp(tok,"REVISION:", 9) == 0) {
4564 revision = strtol(tok+9, NULL, 16);
4565 continue;
4568 if (StrnCaseCmp(tok,"OWNER:", 6) == 0) {
4569 if (owner_sid) {
4570 DEBUG(5, ("OWNER specified more than once!\n"));
4571 goto done;
4573 owner_sid = SMB_CALLOC_ARRAY(DOM_SID, 1);
4574 if (!owner_sid ||
4575 !convert_string_to_sid(ipc_cli, pol,
4576 numeric,
4577 owner_sid, tok+6)) {
4578 DEBUG(5, ("Failed to parse owner sid\n"));
4579 goto done;
4581 continue;
4584 if (StrnCaseCmp(tok,"OWNER+:", 7) == 0) {
4585 if (owner_sid) {
4586 DEBUG(5, ("OWNER specified more than once!\n"));
4587 goto done;
4589 owner_sid = SMB_CALLOC_ARRAY(DOM_SID, 1);
4590 if (!owner_sid ||
4591 !convert_string_to_sid(ipc_cli, pol,
4592 False,
4593 owner_sid, tok+7)) {
4594 DEBUG(5, ("Failed to parse owner sid\n"));
4595 goto done;
4597 continue;
4600 if (StrnCaseCmp(tok,"GROUP:", 6) == 0) {
4601 if (group_sid) {
4602 DEBUG(5, ("GROUP specified more than once!\n"));
4603 goto done;
4605 group_sid = SMB_CALLOC_ARRAY(DOM_SID, 1);
4606 if (!group_sid ||
4607 !convert_string_to_sid(ipc_cli, pol,
4608 numeric,
4609 group_sid, tok+6)) {
4610 DEBUG(5, ("Failed to parse group sid\n"));
4611 goto done;
4613 continue;
4616 if (StrnCaseCmp(tok,"GROUP+:", 7) == 0) {
4617 if (group_sid) {
4618 DEBUG(5, ("GROUP specified more than once!\n"));
4619 goto done;
4621 group_sid = SMB_CALLOC_ARRAY(DOM_SID, 1);
4622 if (!group_sid ||
4623 !convert_string_to_sid(ipc_cli, pol,
4624 False,
4625 group_sid, tok+6)) {
4626 DEBUG(5, ("Failed to parse group sid\n"));
4627 goto done;
4629 continue;
4632 if (StrnCaseCmp(tok,"ACL:", 4) == 0) {
4633 SEC_ACE ace;
4634 if (!parse_ace(ipc_cli, pol, &ace, numeric, tok+4)) {
4635 DEBUG(5, ("Failed to parse ACL %s\n", tok));
4636 goto done;
4638 if(!add_ace(&dacl, &ace, ctx)) {
4639 DEBUG(5, ("Failed to add ACL %s\n", tok));
4640 goto done;
4642 continue;
4645 if (StrnCaseCmp(tok,"ACL+:", 5) == 0) {
4646 SEC_ACE ace;
4647 if (!parse_ace(ipc_cli, pol, &ace, False, tok+5)) {
4648 DEBUG(5, ("Failed to parse ACL %s\n", tok));
4649 goto done;
4651 if(!add_ace(&dacl, &ace, ctx)) {
4652 DEBUG(5, ("Failed to add ACL %s\n", tok));
4653 goto done;
4655 continue;
4658 DEBUG(5, ("Failed to parse security descriptor\n"));
4659 goto done;
4662 ret = make_sec_desc(ctx, revision, SEC_DESC_SELF_RELATIVE,
4663 owner_sid, group_sid, NULL, dacl, &sd_size);
4665 done:
4666 SAFE_FREE(group_sid);
4667 SAFE_FREE(owner_sid);
4669 return ret;
4673 /* Obtain the current dos attributes */
4674 static DOS_ATTR_DESC *
4675 dos_attr_query(SMBCCTX *context,
4676 TALLOC_CTX *ctx,
4677 const char *filename,
4678 SMBCSRV *srv)
4680 struct timespec create_time_ts;
4681 struct timespec write_time_ts;
4682 struct timespec access_time_ts;
4683 struct timespec change_time_ts;
4684 SMB_OFF_T size = 0;
4685 uint16 mode = 0;
4686 SMB_INO_T inode = 0;
4687 DOS_ATTR_DESC *ret;
4689 ret = TALLOC_P(ctx, DOS_ATTR_DESC);
4690 if (!ret) {
4691 errno = ENOMEM;
4692 return NULL;
4695 /* Obtain the DOS attributes */
4696 if (!smbc_getatr(context, srv, CONST_DISCARD(char *, filename),
4697 &mode, &size,
4698 &create_time_ts,
4699 &access_time_ts,
4700 &write_time_ts,
4701 &change_time_ts,
4702 &inode)) {
4703 errno = smbc_errno(context, srv->cli);
4704 DEBUG(5, ("dos_attr_query Failed to query old attributes\n"));
4705 return NULL;
4708 ret->mode = mode;
4709 ret->size = size;
4710 ret->create_time = convert_timespec_to_time_t(create_time_ts);
4711 ret->access_time = convert_timespec_to_time_t(access_time_ts);
4712 ret->write_time = convert_timespec_to_time_t(write_time_ts);
4713 ret->change_time = convert_timespec_to_time_t(change_time_ts);
4714 ret->inode = inode;
4716 return ret;
4720 /* parse a ascii version of a security descriptor */
4721 static void
4722 dos_attr_parse(SMBCCTX *context,
4723 DOS_ATTR_DESC *dad,
4724 SMBCSRV *srv,
4725 char *str)
4727 int n;
4728 const char *p = str;
4729 char *tok = NULL;
4730 TALLOC_CTX *frame = NULL;
4731 struct {
4732 const char * create_time_attr;
4733 const char * access_time_attr;
4734 const char * write_time_attr;
4735 const char * change_time_attr;
4736 } attr_strings;
4738 /* Determine whether to use old-style or new-style attribute names */
4739 if (context->internal->_full_time_names) {
4740 /* new-style names */
4741 attr_strings.create_time_attr = "CREATE_TIME";
4742 attr_strings.access_time_attr = "ACCESS_TIME";
4743 attr_strings.write_time_attr = "WRITE_TIME";
4744 attr_strings.change_time_attr = "CHANGE_TIME";
4745 } else {
4746 /* old-style names */
4747 attr_strings.create_time_attr = NULL;
4748 attr_strings.access_time_attr = "A_TIME";
4749 attr_strings.write_time_attr = "M_TIME";
4750 attr_strings.change_time_attr = "C_TIME";
4753 /* if this is to set the entire ACL... */
4754 if (*str == '*') {
4755 /* ... then increment past the first colon if there is one */
4756 if ((p = strchr(str, ':')) != NULL) {
4757 ++p;
4758 } else {
4759 p = str;
4763 frame = talloc_stackframe();
4764 while (next_token_talloc(frame, &p, &tok, "\t,\r\n")) {
4765 if (StrnCaseCmp(tok, "MODE:", 5) == 0) {
4766 long request = strtol(tok+5, NULL, 16);
4767 if (request == 0) {
4768 dad->mode = (request |
4769 (IS_DOS_DIR(dad->mode)
4770 ? FILE_ATTRIBUTE_DIRECTORY
4771 : FILE_ATTRIBUTE_NORMAL));
4772 } else {
4773 dad->mode = request;
4775 continue;
4778 if (StrnCaseCmp(tok, "SIZE:", 5) == 0) {
4779 dad->size = (SMB_OFF_T)atof(tok+5);
4780 continue;
4783 n = strlen(attr_strings.access_time_attr);
4784 if (StrnCaseCmp(tok, attr_strings.access_time_attr, n) == 0) {
4785 dad->access_time = (time_t)strtol(tok+n+1, NULL, 10);
4786 continue;
4789 n = strlen(attr_strings.change_time_attr);
4790 if (StrnCaseCmp(tok, attr_strings.change_time_attr, n) == 0) {
4791 dad->change_time = (time_t)strtol(tok+n+1, NULL, 10);
4792 continue;
4795 n = strlen(attr_strings.write_time_attr);
4796 if (StrnCaseCmp(tok, attr_strings.write_time_attr, n) == 0) {
4797 dad->write_time = (time_t)strtol(tok+n+1, NULL, 10);
4798 continue;
4801 if (attr_strings.create_time_attr != NULL) {
4802 n = strlen(attr_strings.create_time_attr);
4803 if (StrnCaseCmp(tok, attr_strings.create_time_attr,
4804 n) == 0) {
4805 dad->create_time = (time_t)strtol(tok+n+1,
4806 NULL, 10);
4807 continue;
4811 if (StrnCaseCmp(tok, "INODE:", 6) == 0) {
4812 dad->inode = (SMB_INO_T)atof(tok+6);
4813 continue;
4816 TALLOC_FREE(frame);
4819 /*****************************************************
4820 Retrieve the acls for a file.
4821 *******************************************************/
4823 static int
4824 cacl_get(SMBCCTX *context,
4825 TALLOC_CTX *ctx,
4826 SMBCSRV *srv,
4827 struct cli_state *ipc_cli,
4828 POLICY_HND *pol,
4829 char *filename,
4830 char *attr_name,
4831 char *buf,
4832 int bufsize)
4834 uint32 i;
4835 int n = 0;
4836 int n_used;
4837 bool all;
4838 bool all_nt;
4839 bool all_nt_acls;
4840 bool all_dos;
4841 bool some_nt;
4842 bool some_dos;
4843 bool exclude_nt_revision = False;
4844 bool exclude_nt_owner = False;
4845 bool exclude_nt_group = False;
4846 bool exclude_nt_acl = False;
4847 bool exclude_dos_mode = False;
4848 bool exclude_dos_size = False;
4849 bool exclude_dos_create_time = False;
4850 bool exclude_dos_access_time = False;
4851 bool exclude_dos_write_time = False;
4852 bool exclude_dos_change_time = False;
4853 bool exclude_dos_inode = False;
4854 bool numeric = True;
4855 bool determine_size = (bufsize == 0);
4856 int fnum = -1;
4857 SEC_DESC *sd;
4858 fstring sidstr;
4859 fstring name_sandbox;
4860 char *name;
4861 char *pExclude;
4862 char *p;
4863 struct timespec create_time_ts;
4864 struct timespec write_time_ts;
4865 struct timespec access_time_ts;
4866 struct timespec change_time_ts;
4867 time_t create_time = (time_t)0;
4868 time_t write_time = (time_t)0;
4869 time_t access_time = (time_t)0;
4870 time_t change_time = (time_t)0;
4871 SMB_OFF_T size = 0;
4872 uint16 mode = 0;
4873 SMB_INO_T ino = 0;
4874 struct cli_state *cli = srv->cli;
4875 struct {
4876 const char * create_time_attr;
4877 const char * access_time_attr;
4878 const char * write_time_attr;
4879 const char * change_time_attr;
4880 } attr_strings;
4881 struct {
4882 const char * create_time_attr;
4883 const char * access_time_attr;
4884 const char * write_time_attr;
4885 const char * change_time_attr;
4886 } excl_attr_strings;
4888 /* Determine whether to use old-style or new-style attribute names */
4889 if (context->internal->_full_time_names) {
4890 /* new-style names */
4891 attr_strings.create_time_attr = "CREATE_TIME";
4892 attr_strings.access_time_attr = "ACCESS_TIME";
4893 attr_strings.write_time_attr = "WRITE_TIME";
4894 attr_strings.change_time_attr = "CHANGE_TIME";
4896 excl_attr_strings.create_time_attr = "CREATE_TIME";
4897 excl_attr_strings.access_time_attr = "ACCESS_TIME";
4898 excl_attr_strings.write_time_attr = "WRITE_TIME";
4899 excl_attr_strings.change_time_attr = "CHANGE_TIME";
4900 } else {
4901 /* old-style names */
4902 attr_strings.create_time_attr = NULL;
4903 attr_strings.access_time_attr = "A_TIME";
4904 attr_strings.write_time_attr = "M_TIME";
4905 attr_strings.change_time_attr = "C_TIME";
4907 excl_attr_strings.create_time_attr = NULL;
4908 excl_attr_strings.access_time_attr = "dos_attr.A_TIME";
4909 excl_attr_strings.write_time_attr = "dos_attr.M_TIME";
4910 excl_attr_strings.change_time_attr = "dos_attr.C_TIME";
4913 /* Copy name so we can strip off exclusions (if any are specified) */
4914 strncpy(name_sandbox, attr_name, sizeof(name_sandbox) - 1);
4916 /* Ensure name is null terminated */
4917 name_sandbox[sizeof(name_sandbox) - 1] = '\0';
4919 /* Play in the sandbox */
4920 name = name_sandbox;
4922 /* If there are any exclusions, point to them and mask them from name */
4923 if ((pExclude = strchr(name, '!')) != NULL)
4925 *pExclude++ = '\0';
4928 all = (StrnCaseCmp(name, "system.*", 8) == 0);
4929 all_nt = (StrnCaseCmp(name, "system.nt_sec_desc.*", 20) == 0);
4930 all_nt_acls = (StrnCaseCmp(name, "system.nt_sec_desc.acl.*", 24) == 0);
4931 all_dos = (StrnCaseCmp(name, "system.dos_attr.*", 17) == 0);
4932 some_nt = (StrnCaseCmp(name, "system.nt_sec_desc.", 19) == 0);
4933 some_dos = (StrnCaseCmp(name, "system.dos_attr.", 16) == 0);
4934 numeric = (* (name + strlen(name) - 1) != '+');
4936 /* Look for exclusions from "all" requests */
4937 if (all || all_nt || all_dos) {
4939 /* Exclusions are delimited by '!' */
4940 for (;
4941 pExclude != NULL;
4942 pExclude = (p == NULL ? NULL : p + 1)) {
4944 /* Find end of this exclusion name */
4945 if ((p = strchr(pExclude, '!')) != NULL)
4947 *p = '\0';
4950 /* Which exclusion name is this? */
4951 if (StrCaseCmp(pExclude, "nt_sec_desc.revision") == 0) {
4952 exclude_nt_revision = True;
4954 else if (StrCaseCmp(pExclude, "nt_sec_desc.owner") == 0) {
4955 exclude_nt_owner = True;
4957 else if (StrCaseCmp(pExclude, "nt_sec_desc.group") == 0) {
4958 exclude_nt_group = True;
4960 else if (StrCaseCmp(pExclude, "nt_sec_desc.acl") == 0) {
4961 exclude_nt_acl = True;
4963 else if (StrCaseCmp(pExclude, "dos_attr.mode") == 0) {
4964 exclude_dos_mode = True;
4966 else if (StrCaseCmp(pExclude, "dos_attr.size") == 0) {
4967 exclude_dos_size = True;
4969 else if (excl_attr_strings.create_time_attr != NULL &&
4970 StrCaseCmp(pExclude,
4971 excl_attr_strings.change_time_attr) == 0) {
4972 exclude_dos_create_time = True;
4974 else if (StrCaseCmp(pExclude,
4975 excl_attr_strings.access_time_attr) == 0) {
4976 exclude_dos_access_time = True;
4978 else if (StrCaseCmp(pExclude,
4979 excl_attr_strings.write_time_attr) == 0) {
4980 exclude_dos_write_time = True;
4982 else if (StrCaseCmp(pExclude,
4983 excl_attr_strings.change_time_attr) == 0) {
4984 exclude_dos_change_time = True;
4986 else if (StrCaseCmp(pExclude, "dos_attr.inode") == 0) {
4987 exclude_dos_inode = True;
4989 else {
4990 DEBUG(5, ("cacl_get received unknown exclusion: %s\n",
4991 pExclude));
4992 errno = ENOATTR;
4993 return -1;
4998 n_used = 0;
5001 * If we are (possibly) talking to an NT or new system and some NT
5002 * attributes have been requested...
5004 if (ipc_cli && (all || some_nt || all_nt_acls)) {
5005 /* Point to the portion after "system.nt_sec_desc." */
5006 name += 19; /* if (all) this will be invalid but unused */
5008 /* ... then obtain any NT attributes which were requested */
5009 fnum = cli_nt_create(cli, filename, CREATE_ACCESS_READ);
5011 if (fnum == -1) {
5012 DEBUG(5, ("cacl_get failed to open %s: %s\n",
5013 filename, cli_errstr(cli)));
5014 errno = 0;
5015 return -1;
5018 sd = cli_query_secdesc(cli, fnum, ctx);
5020 if (!sd) {
5021 DEBUG(5,
5022 ("cacl_get Failed to query old descriptor\n"));
5023 errno = 0;
5024 return -1;
5027 cli_close(cli, fnum);
5029 if (! exclude_nt_revision) {
5030 if (all || all_nt) {
5031 if (determine_size) {
5032 p = talloc_asprintf(ctx,
5033 "REVISION:%d",
5034 sd->revision);
5035 if (!p) {
5036 errno = ENOMEM;
5037 return -1;
5039 n = strlen(p);
5040 } else {
5041 n = snprintf(buf, bufsize,
5042 "REVISION:%d",
5043 sd->revision);
5045 } else if (StrCaseCmp(name, "revision") == 0) {
5046 if (determine_size) {
5047 p = talloc_asprintf(ctx, "%d",
5048 sd->revision);
5049 if (!p) {
5050 errno = ENOMEM;
5051 return -1;
5053 n = strlen(p);
5054 } else {
5055 n = snprintf(buf, bufsize, "%d",
5056 sd->revision);
5060 if (!determine_size && n > bufsize) {
5061 errno = ERANGE;
5062 return -1;
5064 buf += n;
5065 n_used += n;
5066 bufsize -= n;
5067 n = 0;
5070 if (! exclude_nt_owner) {
5071 /* Get owner and group sid */
5072 if (sd->owner_sid) {
5073 convert_sid_to_string(ipc_cli, pol,
5074 sidstr,
5075 numeric,
5076 sd->owner_sid);
5077 } else {
5078 fstrcpy(sidstr, "");
5081 if (all || all_nt) {
5082 if (determine_size) {
5083 p = talloc_asprintf(ctx, ",OWNER:%s",
5084 sidstr);
5085 if (!p) {
5086 errno = ENOMEM;
5087 return -1;
5089 n = strlen(p);
5090 } else if (sidstr[0] != '\0') {
5091 n = snprintf(buf, bufsize,
5092 ",OWNER:%s", sidstr);
5094 } else if (StrnCaseCmp(name, "owner", 5) == 0) {
5095 if (determine_size) {
5096 p = talloc_asprintf(ctx, "%s", sidstr);
5097 if (!p) {
5098 errno = ENOMEM;
5099 return -1;
5101 n = strlen(p);
5102 } else {
5103 n = snprintf(buf, bufsize, "%s",
5104 sidstr);
5108 if (!determine_size && n > bufsize) {
5109 errno = ERANGE;
5110 return -1;
5112 buf += n;
5113 n_used += n;
5114 bufsize -= n;
5115 n = 0;
5118 if (! exclude_nt_group) {
5119 if (sd->group_sid) {
5120 convert_sid_to_string(ipc_cli, pol,
5121 sidstr, numeric,
5122 sd->group_sid);
5123 } else {
5124 fstrcpy(sidstr, "");
5127 if (all || all_nt) {
5128 if (determine_size) {
5129 p = talloc_asprintf(ctx, ",GROUP:%s",
5130 sidstr);
5131 if (!p) {
5132 errno = ENOMEM;
5133 return -1;
5135 n = strlen(p);
5136 } else if (sidstr[0] != '\0') {
5137 n = snprintf(buf, bufsize,
5138 ",GROUP:%s", sidstr);
5140 } else if (StrnCaseCmp(name, "group", 5) == 0) {
5141 if (determine_size) {
5142 p = talloc_asprintf(ctx, "%s", sidstr);
5143 if (!p) {
5144 errno = ENOMEM;
5145 return -1;
5147 n = strlen(p);
5148 } else {
5149 n = snprintf(buf, bufsize,
5150 "%s", sidstr);
5154 if (!determine_size && n > bufsize) {
5155 errno = ERANGE;
5156 return -1;
5158 buf += n;
5159 n_used += n;
5160 bufsize -= n;
5161 n = 0;
5164 if (! exclude_nt_acl) {
5165 /* Add aces to value buffer */
5166 for (i = 0; sd->dacl && i < sd->dacl->num_aces; i++) {
5168 SEC_ACE *ace = &sd->dacl->aces[i];
5169 convert_sid_to_string(ipc_cli, pol,
5170 sidstr, numeric,
5171 &ace->trustee);
5173 if (all || all_nt) {
5174 if (determine_size) {
5175 p = talloc_asprintf(
5176 ctx,
5177 ",ACL:"
5178 "%s:%d/%d/0x%08x",
5179 sidstr,
5180 ace->type,
5181 ace->flags,
5182 ace->access_mask);
5183 if (!p) {
5184 errno = ENOMEM;
5185 return -1;
5187 n = strlen(p);
5188 } else {
5189 n = snprintf(
5190 buf, bufsize,
5191 ",ACL:%s:%d/%d/0x%08x",
5192 sidstr,
5193 ace->type,
5194 ace->flags,
5195 ace->access_mask);
5197 } else if ((StrnCaseCmp(name, "acl", 3) == 0 &&
5198 StrCaseCmp(name+3, sidstr) == 0) ||
5199 (StrnCaseCmp(name, "acl+", 4) == 0 &&
5200 StrCaseCmp(name+4, sidstr) == 0)) {
5201 if (determine_size) {
5202 p = talloc_asprintf(
5203 ctx,
5204 "%d/%d/0x%08x",
5205 ace->type,
5206 ace->flags,
5207 ace->access_mask);
5208 if (!p) {
5209 errno = ENOMEM;
5210 return -1;
5212 n = strlen(p);
5213 } else {
5214 n = snprintf(buf, bufsize,
5215 "%d/%d/0x%08x",
5216 ace->type,
5217 ace->flags,
5218 ace->access_mask);
5220 } else if (all_nt_acls) {
5221 if (determine_size) {
5222 p = talloc_asprintf(
5223 ctx,
5224 "%s%s:%d/%d/0x%08x",
5225 i ? "," : "",
5226 sidstr,
5227 ace->type,
5228 ace->flags,
5229 ace->access_mask);
5230 if (!p) {
5231 errno = ENOMEM;
5232 return -1;
5234 n = strlen(p);
5235 } else {
5236 n = snprintf(buf, bufsize,
5237 "%s%s:%d/%d/0x%08x",
5238 i ? "," : "",
5239 sidstr,
5240 ace->type,
5241 ace->flags,
5242 ace->access_mask);
5245 if (!determine_size && n > bufsize) {
5246 errno = ERANGE;
5247 return -1;
5249 buf += n;
5250 n_used += n;
5251 bufsize -= n;
5252 n = 0;
5256 /* Restore name pointer to its original value */
5257 name -= 19;
5260 if (all || some_dos) {
5261 /* Point to the portion after "system.dos_attr." */
5262 name += 16; /* if (all) this will be invalid but unused */
5264 /* Obtain the DOS attributes */
5265 if (!smbc_getatr(context, srv, filename, &mode, &size,
5266 &create_time_ts,
5267 &access_time_ts,
5268 &write_time_ts,
5269 &change_time_ts,
5270 &ino)) {
5272 errno = smbc_errno(context, srv->cli);
5273 return -1;
5277 create_time = convert_timespec_to_time_t(create_time_ts);
5278 access_time = convert_timespec_to_time_t(access_time_ts);
5279 write_time = convert_timespec_to_time_t(write_time_ts);
5280 change_time = convert_timespec_to_time_t(change_time_ts);
5282 if (! exclude_dos_mode) {
5283 if (all || all_dos) {
5284 if (determine_size) {
5285 p = talloc_asprintf(ctx,
5286 "%sMODE:0x%x",
5287 (ipc_cli &&
5288 (all || some_nt)
5289 ? ","
5290 : ""),
5291 mode);
5292 if (!p) {
5293 errno = ENOMEM;
5294 return -1;
5296 n = strlen(p);
5297 } else {
5298 n = snprintf(buf, bufsize,
5299 "%sMODE:0x%x",
5300 (ipc_cli &&
5301 (all || some_nt)
5302 ? ","
5303 : ""),
5304 mode);
5306 } else if (StrCaseCmp(name, "mode") == 0) {
5307 if (determine_size) {
5308 p = talloc_asprintf(ctx, "0x%x", mode);
5309 if (!p) {
5310 errno = ENOMEM;
5311 return -1;
5313 n = strlen(p);
5314 } else {
5315 n = snprintf(buf, bufsize,
5316 "0x%x", mode);
5320 if (!determine_size && n > bufsize) {
5321 errno = ERANGE;
5322 return -1;
5324 buf += n;
5325 n_used += n;
5326 bufsize -= n;
5327 n = 0;
5330 if (! exclude_dos_size) {
5331 if (all || all_dos) {
5332 if (determine_size) {
5333 p = talloc_asprintf(
5334 ctx,
5335 ",SIZE:%.0f",
5336 (double)size);
5337 if (!p) {
5338 errno = ENOMEM;
5339 return -1;
5341 n = strlen(p);
5342 } else {
5343 n = snprintf(buf, bufsize,
5344 ",SIZE:%.0f",
5345 (double)size);
5347 } else if (StrCaseCmp(name, "size") == 0) {
5348 if (determine_size) {
5349 p = talloc_asprintf(
5350 ctx,
5351 "%.0f",
5352 (double)size);
5353 if (!p) {
5354 errno = ENOMEM;
5355 return -1;
5357 n = strlen(p);
5358 } else {
5359 n = snprintf(buf, bufsize,
5360 "%.0f",
5361 (double)size);
5365 if (!determine_size && n > bufsize) {
5366 errno = ERANGE;
5367 return -1;
5369 buf += n;
5370 n_used += n;
5371 bufsize -= n;
5372 n = 0;
5375 if (! exclude_dos_create_time &&
5376 attr_strings.create_time_attr != NULL) {
5377 if (all || all_dos) {
5378 if (determine_size) {
5379 p = talloc_asprintf(ctx,
5380 ",%s:%lu",
5381 attr_strings.create_time_attr,
5382 create_time);
5383 if (!p) {
5384 errno = ENOMEM;
5385 return -1;
5387 n = strlen(p);
5388 } else {
5389 n = snprintf(buf, bufsize,
5390 ",%s:%lu",
5391 attr_strings.create_time_attr,
5392 create_time);
5394 } else if (StrCaseCmp(name, attr_strings.create_time_attr) == 0) {
5395 if (determine_size) {
5396 p = talloc_asprintf(ctx, "%lu", create_time);
5397 if (!p) {
5398 errno = ENOMEM;
5399 return -1;
5401 n = strlen(p);
5402 } else {
5403 n = snprintf(buf, bufsize,
5404 "%lu", create_time);
5408 if (!determine_size && n > bufsize) {
5409 errno = ERANGE;
5410 return -1;
5412 buf += n;
5413 n_used += n;
5414 bufsize -= n;
5415 n = 0;
5418 if (! exclude_dos_access_time) {
5419 if (all || all_dos) {
5420 if (determine_size) {
5421 p = talloc_asprintf(ctx,
5422 ",%s:%lu",
5423 attr_strings.access_time_attr,
5424 access_time);
5425 if (!p) {
5426 errno = ENOMEM;
5427 return -1;
5429 n = strlen(p);
5430 } else {
5431 n = snprintf(buf, bufsize,
5432 ",%s:%lu",
5433 attr_strings.access_time_attr,
5434 access_time);
5436 } else if (StrCaseCmp(name, attr_strings.access_time_attr) == 0) {
5437 if (determine_size) {
5438 p = talloc_asprintf(ctx, "%lu", access_time);
5439 if (!p) {
5440 errno = ENOMEM;
5441 return -1;
5443 n = strlen(p);
5444 } else {
5445 n = snprintf(buf, bufsize,
5446 "%lu", access_time);
5450 if (!determine_size && n > bufsize) {
5451 errno = ERANGE;
5452 return -1;
5454 buf += n;
5455 n_used += n;
5456 bufsize -= n;
5457 n = 0;
5460 if (! exclude_dos_write_time) {
5461 if (all || all_dos) {
5462 if (determine_size) {
5463 p = talloc_asprintf(ctx,
5464 ",%s:%lu",
5465 attr_strings.write_time_attr,
5466 write_time);
5467 if (!p) {
5468 errno = ENOMEM;
5469 return -1;
5471 n = strlen(p);
5472 } else {
5473 n = snprintf(buf, bufsize,
5474 ",%s:%lu",
5475 attr_strings.write_time_attr,
5476 write_time);
5478 } else if (StrCaseCmp(name, attr_strings.write_time_attr) == 0) {
5479 if (determine_size) {
5480 p = talloc_asprintf(ctx, "%lu", write_time);
5481 if (!p) {
5482 errno = ENOMEM;
5483 return -1;
5485 n = strlen(p);
5486 } else {
5487 n = snprintf(buf, bufsize,
5488 "%lu", write_time);
5492 if (!determine_size && n > bufsize) {
5493 errno = ERANGE;
5494 return -1;
5496 buf += n;
5497 n_used += n;
5498 bufsize -= n;
5499 n = 0;
5502 if (! exclude_dos_change_time) {
5503 if (all || all_dos) {
5504 if (determine_size) {
5505 p = talloc_asprintf(ctx,
5506 ",%s:%lu",
5507 attr_strings.change_time_attr,
5508 change_time);
5509 if (!p) {
5510 errno = ENOMEM;
5511 return -1;
5513 n = strlen(p);
5514 } else {
5515 n = snprintf(buf, bufsize,
5516 ",%s:%lu",
5517 attr_strings.change_time_attr,
5518 change_time);
5520 } else if (StrCaseCmp(name, attr_strings.change_time_attr) == 0) {
5521 if (determine_size) {
5522 p = talloc_asprintf(ctx, "%lu", change_time);
5523 if (!p) {
5524 errno = ENOMEM;
5525 return -1;
5527 n = strlen(p);
5528 } else {
5529 n = snprintf(buf, bufsize,
5530 "%lu", change_time);
5534 if (!determine_size && n > bufsize) {
5535 errno = ERANGE;
5536 return -1;
5538 buf += n;
5539 n_used += n;
5540 bufsize -= n;
5541 n = 0;
5544 if (! exclude_dos_inode) {
5545 if (all || all_dos) {
5546 if (determine_size) {
5547 p = talloc_asprintf(
5548 ctx,
5549 ",INODE:%.0f",
5550 (double)ino);
5551 if (!p) {
5552 errno = ENOMEM;
5553 return -1;
5555 n = strlen(p);
5556 } else {
5557 n = snprintf(buf, bufsize,
5558 ",INODE:%.0f",
5559 (double) ino);
5561 } else if (StrCaseCmp(name, "inode") == 0) {
5562 if (determine_size) {
5563 p = talloc_asprintf(
5564 ctx,
5565 "%.0f",
5566 (double) ino);
5567 if (!p) {
5568 errno = ENOMEM;
5569 return -1;
5571 n = strlen(p);
5572 } else {
5573 n = snprintf(buf, bufsize,
5574 "%.0f",
5575 (double) ino);
5579 if (!determine_size && n > bufsize) {
5580 errno = ERANGE;
5581 return -1;
5583 buf += n;
5584 n_used += n;
5585 bufsize -= n;
5586 n = 0;
5589 /* Restore name pointer to its original value */
5590 name -= 16;
5593 if (n_used == 0) {
5594 errno = ENOATTR;
5595 return -1;
5598 return n_used;
5601 /*****************************************************
5602 set the ACLs on a file given an ascii description
5603 *******************************************************/
5604 static int
5605 cacl_set(TALLOC_CTX *ctx,
5606 struct cli_state *cli,
5607 struct cli_state *ipc_cli,
5608 POLICY_HND *pol,
5609 const char *filename,
5610 const char *the_acl,
5611 int mode,
5612 int flags)
5614 int fnum;
5615 int err = 0;
5616 SEC_DESC *sd = NULL, *old;
5617 SEC_ACL *dacl = NULL;
5618 DOM_SID *owner_sid = NULL;
5619 DOM_SID *group_sid = NULL;
5620 uint32 i, j;
5621 size_t sd_size;
5622 int ret = 0;
5623 char *p;
5624 bool numeric = True;
5626 /* the_acl will be null for REMOVE_ALL operations */
5627 if (the_acl) {
5628 numeric = ((p = strchr(the_acl, ':')) != NULL &&
5629 p > the_acl &&
5630 p[-1] != '+');
5632 /* if this is to set the entire ACL... */
5633 if (*the_acl == '*') {
5634 /* ... then increment past the first colon */
5635 the_acl = p + 1;
5638 sd = sec_desc_parse(ctx, ipc_cli, pol, numeric,
5639 CONST_DISCARD(char *, the_acl));
5641 if (!sd) {
5642 errno = EINVAL;
5643 return -1;
5647 /* SMBC_XATTR_MODE_REMOVE_ALL is the only caller
5648 that doesn't deref sd */
5650 if (!sd && (mode != SMBC_XATTR_MODE_REMOVE_ALL)) {
5651 errno = EINVAL;
5652 return -1;
5655 /* The desired access below is the only one I could find that works
5656 with NT4, W2KP and Samba */
5658 fnum = cli_nt_create(cli, filename, CREATE_ACCESS_READ);
5660 if (fnum == -1) {
5661 DEBUG(5, ("cacl_set failed to open %s: %s\n",
5662 filename, cli_errstr(cli)));
5663 errno = 0;
5664 return -1;
5667 old = cli_query_secdesc(cli, fnum, ctx);
5669 if (!old) {
5670 DEBUG(5, ("cacl_set Failed to query old descriptor\n"));
5671 errno = 0;
5672 return -1;
5675 cli_close(cli, fnum);
5677 switch (mode) {
5678 case SMBC_XATTR_MODE_REMOVE_ALL:
5679 old->dacl->num_aces = 0;
5680 dacl = old->dacl;
5681 break;
5683 case SMBC_XATTR_MODE_REMOVE:
5684 for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
5685 bool found = False;
5687 for (j=0;old->dacl && j<old->dacl->num_aces;j++) {
5688 if (sec_ace_equal(&sd->dacl->aces[i],
5689 &old->dacl->aces[j])) {
5690 uint32 k;
5691 for (k=j; k<old->dacl->num_aces-1;k++) {
5692 old->dacl->aces[k] =
5693 old->dacl->aces[k+1];
5695 old->dacl->num_aces--;
5696 found = True;
5697 dacl = old->dacl;
5698 break;
5702 if (!found) {
5703 err = ENOATTR;
5704 ret = -1;
5705 goto failed;
5708 break;
5710 case SMBC_XATTR_MODE_ADD:
5711 for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
5712 bool found = False;
5714 for (j=0;old->dacl && j<old->dacl->num_aces;j++) {
5715 if (sid_equal(&sd->dacl->aces[i].trustee,
5716 &old->dacl->aces[j].trustee)) {
5717 if (!(flags & SMBC_XATTR_FLAG_CREATE)) {
5718 err = EEXIST;
5719 ret = -1;
5720 goto failed;
5722 old->dacl->aces[j] = sd->dacl->aces[i];
5723 ret = -1;
5724 found = True;
5728 if (!found && (flags & SMBC_XATTR_FLAG_REPLACE)) {
5729 err = ENOATTR;
5730 ret = -1;
5731 goto failed;
5734 for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
5735 add_ace(&old->dacl, &sd->dacl->aces[i], ctx);
5738 dacl = old->dacl;
5739 break;
5741 case SMBC_XATTR_MODE_SET:
5742 old = sd;
5743 owner_sid = old->owner_sid;
5744 group_sid = old->group_sid;
5745 dacl = old->dacl;
5746 break;
5748 case SMBC_XATTR_MODE_CHOWN:
5749 owner_sid = sd->owner_sid;
5750 break;
5752 case SMBC_XATTR_MODE_CHGRP:
5753 group_sid = sd->group_sid;
5754 break;
5757 /* Denied ACE entries must come before allowed ones */
5758 sort_acl(old->dacl);
5760 /* Create new security descriptor and set it */
5761 sd = make_sec_desc(ctx, old->revision, SEC_DESC_SELF_RELATIVE,
5762 owner_sid, group_sid, NULL, dacl, &sd_size);
5764 fnum = cli_nt_create(cli, filename,
5765 WRITE_DAC_ACCESS | WRITE_OWNER_ACCESS);
5767 if (fnum == -1) {
5768 DEBUG(5, ("cacl_set failed to open %s: %s\n",
5769 filename, cli_errstr(cli)));
5770 errno = 0;
5771 return -1;
5774 if (!cli_set_secdesc(cli, fnum, sd)) {
5775 DEBUG(5, ("ERROR: secdesc set failed: %s\n", cli_errstr(cli)));
5776 ret = -1;
5779 /* Clean up */
5781 failed:
5782 cli_close(cli, fnum);
5784 if (err != 0) {
5785 errno = err;
5788 return ret;
5792 static int
5793 smbc_setxattr_ctx(SMBCCTX *context,
5794 const char *fname,
5795 const char *name,
5796 const void *value,
5797 size_t size,
5798 int flags)
5800 int ret;
5801 int ret2;
5802 SMBCSRV *srv = NULL;
5803 SMBCSRV *ipc_srv = NULL;
5804 char *server = NULL;
5805 char *share = NULL;
5806 char *user = NULL;
5807 char *password = NULL;
5808 char *workgroup = NULL;
5809 char *path = NULL;
5810 DOS_ATTR_DESC *dad = NULL;
5811 struct {
5812 const char * create_time_attr;
5813 const char * access_time_attr;
5814 const char * write_time_attr;
5815 const char * change_time_attr;
5816 } attr_strings;
5817 TALLOC_CTX *frame = talloc_stackframe();
5819 if (!context || !context->internal ||
5820 !context->internal->_initialized) {
5821 errno = EINVAL; /* Best I can think of ... */
5822 TALLOC_FREE(frame);
5823 return -1;
5826 if (!fname) {
5827 errno = EINVAL;
5828 TALLOC_FREE(frame);
5829 return -1;
5832 DEBUG(4, ("smbc_setxattr(%s, %s, %.*s)\n",
5833 fname, name, (int) size, (const char*)value));
5835 if (smbc_parse_path(frame,
5836 context,
5837 fname,
5838 &workgroup,
5839 &server,
5840 &share,
5841 &path,
5842 &user,
5843 &password,
5844 NULL)) {
5845 errno = EINVAL;
5846 TALLOC_FREE(frame);
5847 return -1;
5850 if (!user || user[0] == (char)0) {
5851 user = talloc_strdup(frame, context->user);
5852 if (!user) {
5853 errno = ENOMEM;
5854 TALLOC_FREE(frame);
5855 return -1;
5859 srv = smbc_server(frame, context, True,
5860 server, share, &workgroup, &user, &password);
5861 if (!srv) {
5862 TALLOC_FREE(frame);
5863 return -1; /* errno set by smbc_server */
5866 if (! srv->no_nt_session) {
5867 ipc_srv = smbc_attr_server(frame, context, server, share,
5868 &workgroup, &user, &password);
5869 if (! ipc_srv) {
5870 srv->no_nt_session = True;
5872 } else {
5873 ipc_srv = NULL;
5877 * Are they asking to set the entire set of known attributes?
5879 if (StrCaseCmp(name, "system.*") == 0 ||
5880 StrCaseCmp(name, "system.*+") == 0) {
5881 /* Yup. */
5882 char *namevalue =
5883 talloc_asprintf(talloc_tos(), "%s:%s",
5884 name+7, (const char *) value);
5885 if (! namevalue) {
5886 errno = ENOMEM;
5887 ret = -1;
5888 TALLOC_FREE(frame);
5889 return -1;
5892 if (ipc_srv) {
5893 ret = cacl_set(talloc_tos(), srv->cli,
5894 ipc_srv->cli, &ipc_srv->pol, path,
5895 namevalue,
5896 (*namevalue == '*'
5897 ? SMBC_XATTR_MODE_SET
5898 : SMBC_XATTR_MODE_ADD),
5899 flags);
5900 } else {
5901 ret = 0;
5904 /* get a DOS Attribute Descriptor with current attributes */
5905 dad = dos_attr_query(context, talloc_tos(), path, srv);
5906 if (dad) {
5907 /* Overwrite old with new, using what was provided */
5908 dos_attr_parse(context, dad, srv, namevalue);
5910 /* Set the new DOS attributes */
5911 if (! smbc_setatr(context, srv, path,
5912 dad->create_time,
5913 dad->access_time,
5914 dad->write_time,
5915 dad->change_time,
5916 dad->mode)) {
5918 /* cause failure if NT failed too */
5919 dad = NULL;
5923 /* we only fail if both NT and DOS sets failed */
5924 if (ret < 0 && ! dad) {
5925 ret = -1; /* in case dad was null */
5927 else {
5928 ret = 0;
5931 TALLOC_FREE(frame);
5932 return ret;
5936 * Are they asking to set an access control element or to set
5937 * the entire access control list?
5939 if (StrCaseCmp(name, "system.nt_sec_desc.*") == 0 ||
5940 StrCaseCmp(name, "system.nt_sec_desc.*+") == 0 ||
5941 StrCaseCmp(name, "system.nt_sec_desc.revision") == 0 ||
5942 StrnCaseCmp(name, "system.nt_sec_desc.acl", 22) == 0 ||
5943 StrnCaseCmp(name, "system.nt_sec_desc.acl+", 23) == 0) {
5945 /* Yup. */
5946 char *namevalue =
5947 talloc_asprintf(talloc_tos(), "%s:%s",
5948 name+19, (const char *) value);
5950 if (! ipc_srv) {
5951 ret = -1; /* errno set by smbc_server() */
5953 else if (! namevalue) {
5954 errno = ENOMEM;
5955 ret = -1;
5956 } else {
5957 ret = cacl_set(talloc_tos(), srv->cli,
5958 ipc_srv->cli, &ipc_srv->pol, path,
5959 namevalue,
5960 (*namevalue == '*'
5961 ? SMBC_XATTR_MODE_SET
5962 : SMBC_XATTR_MODE_ADD),
5963 flags);
5965 TALLOC_FREE(frame);
5966 return ret;
5970 * Are they asking to set the owner?
5972 if (StrCaseCmp(name, "system.nt_sec_desc.owner") == 0 ||
5973 StrCaseCmp(name, "system.nt_sec_desc.owner+") == 0) {
5975 /* Yup. */
5976 char *namevalue =
5977 talloc_asprintf(talloc_tos(), "%s:%s",
5978 name+19, (const char *) value);
5980 if (! ipc_srv) {
5981 ret = -1; /* errno set by smbc_server() */
5983 else if (! namevalue) {
5984 errno = ENOMEM;
5985 ret = -1;
5986 } else {
5987 ret = cacl_set(talloc_tos(), srv->cli,
5988 ipc_srv->cli, &ipc_srv->pol, path,
5989 namevalue, SMBC_XATTR_MODE_CHOWN, 0);
5991 TALLOC_FREE(frame);
5992 return ret;
5996 * Are they asking to set the group?
5998 if (StrCaseCmp(name, "system.nt_sec_desc.group") == 0 ||
5999 StrCaseCmp(name, "system.nt_sec_desc.group+") == 0) {
6001 /* Yup. */
6002 char *namevalue =
6003 talloc_asprintf(talloc_tos(), "%s:%s",
6004 name+19, (const char *) value);
6006 if (! ipc_srv) {
6007 /* errno set by smbc_server() */
6008 ret = -1;
6010 else if (! namevalue) {
6011 errno = ENOMEM;
6012 ret = -1;
6013 } else {
6014 ret = cacl_set(talloc_tos(), srv->cli,
6015 ipc_srv->cli, &ipc_srv->pol, path,
6016 namevalue, SMBC_XATTR_MODE_CHGRP, 0);
6018 TALLOC_FREE(frame);
6019 return ret;
6022 /* Determine whether to use old-style or new-style attribute names */
6023 if (context->internal->_full_time_names) {
6024 /* new-style names */
6025 attr_strings.create_time_attr = "system.dos_attr.CREATE_TIME";
6026 attr_strings.access_time_attr = "system.dos_attr.ACCESS_TIME";
6027 attr_strings.write_time_attr = "system.dos_attr.WRITE_TIME";
6028 attr_strings.change_time_attr = "system.dos_attr.CHANGE_TIME";
6029 } else {
6030 /* old-style names */
6031 attr_strings.create_time_attr = NULL;
6032 attr_strings.access_time_attr = "system.dos_attr.A_TIME";
6033 attr_strings.write_time_attr = "system.dos_attr.M_TIME";
6034 attr_strings.change_time_attr = "system.dos_attr.C_TIME";
6038 * Are they asking to set a DOS attribute?
6040 if (StrCaseCmp(name, "system.dos_attr.*") == 0 ||
6041 StrCaseCmp(name, "system.dos_attr.mode") == 0 ||
6042 (attr_strings.create_time_attr != NULL &&
6043 StrCaseCmp(name, attr_strings.create_time_attr) == 0) ||
6044 StrCaseCmp(name, attr_strings.access_time_attr) == 0 ||
6045 StrCaseCmp(name, attr_strings.write_time_attr) == 0 ||
6046 StrCaseCmp(name, attr_strings.change_time_attr) == 0) {
6048 /* get a DOS Attribute Descriptor with current attributes */
6049 dad = dos_attr_query(context, talloc_tos(), path, srv);
6050 if (dad) {
6051 char *namevalue =
6052 talloc_asprintf(talloc_tos(), "%s:%s",
6053 name+16, (const char *) value);
6054 if (! namevalue) {
6055 errno = ENOMEM;
6056 ret = -1;
6057 } else {
6058 /* Overwrite old with provided new params */
6059 dos_attr_parse(context, dad, srv, namevalue);
6061 /* Set the new DOS attributes */
6062 ret2 = smbc_setatr(context, srv, path,
6063 dad->create_time,
6064 dad->access_time,
6065 dad->write_time,
6066 dad->change_time,
6067 dad->mode);
6069 /* ret2 has True (success) / False (failure) */
6070 if (ret2) {
6071 ret = 0;
6072 } else {
6073 ret = -1;
6076 } else {
6077 ret = -1;
6080 TALLOC_FREE(frame);
6081 return ret;
6084 /* Unsupported attribute name */
6085 errno = EINVAL;
6086 TALLOC_FREE(frame);
6087 return -1;
6090 static int
6091 smbc_getxattr_ctx(SMBCCTX *context,
6092 const char *fname,
6093 const char *name,
6094 const void *value,
6095 size_t size)
6097 int ret;
6098 SMBCSRV *srv = NULL;
6099 SMBCSRV *ipc_srv = NULL;
6100 char *server = NULL;
6101 char *share = NULL;
6102 char *user = NULL;
6103 char *password = NULL;
6104 char *workgroup = NULL;
6105 char *path = NULL;
6106 struct {
6107 const char * create_time_attr;
6108 const char * access_time_attr;
6109 const char * write_time_attr;
6110 const char * change_time_attr;
6111 } attr_strings;
6112 TALLOC_CTX *frame = talloc_stackframe();
6114 if (!context || !context->internal ||
6115 !context->internal->_initialized) {
6116 errno = EINVAL; /* Best I can think of ... */
6117 TALLOC_FREE(frame);
6118 return -1;
6121 if (!fname) {
6122 errno = EINVAL;
6123 TALLOC_FREE(frame);
6124 return -1;
6127 DEBUG(4, ("smbc_getxattr(%s, %s)\n", fname, name));
6129 if (smbc_parse_path(frame,
6130 context,
6131 fname,
6132 &workgroup,
6133 &server,
6134 &share,
6135 &path,
6136 &user,
6137 &password,
6138 NULL)) {
6139 errno = EINVAL;
6140 TALLOC_FREE(frame);
6141 return -1;
6144 if (!user || user[0] == (char)0) {
6145 user = talloc_strdup(frame, context->user);
6146 if (!user) {
6147 errno = ENOMEM;
6148 TALLOC_FREE(frame);
6149 return -1;
6153 srv = smbc_server(frame, context, True,
6154 server, share, &workgroup, &user, &password);
6155 if (!srv) {
6156 TALLOC_FREE(frame);
6157 return -1; /* errno set by smbc_server */
6160 if (! srv->no_nt_session) {
6161 ipc_srv = smbc_attr_server(frame, context, server, share,
6162 &workgroup, &user, &password);
6163 if (! ipc_srv) {
6164 srv->no_nt_session = True;
6166 } else {
6167 ipc_srv = NULL;
6170 /* Determine whether to use old-style or new-style attribute names */
6171 if (context->internal->_full_time_names) {
6172 /* new-style names */
6173 attr_strings.create_time_attr = "system.dos_attr.CREATE_TIME";
6174 attr_strings.access_time_attr = "system.dos_attr.ACCESS_TIME";
6175 attr_strings.write_time_attr = "system.dos_attr.WRITE_TIME";
6176 attr_strings.change_time_attr = "system.dos_attr.CHANGE_TIME";
6177 } else {
6178 /* old-style names */
6179 attr_strings.create_time_attr = NULL;
6180 attr_strings.access_time_attr = "system.dos_attr.A_TIME";
6181 attr_strings.write_time_attr = "system.dos_attr.M_TIME";
6182 attr_strings.change_time_attr = "system.dos_attr.C_TIME";
6185 /* Are they requesting a supported attribute? */
6186 if (StrCaseCmp(name, "system.*") == 0 ||
6187 StrnCaseCmp(name, "system.*!", 9) == 0 ||
6188 StrCaseCmp(name, "system.*+") == 0 ||
6189 StrnCaseCmp(name, "system.*+!", 10) == 0 ||
6190 StrCaseCmp(name, "system.nt_sec_desc.*") == 0 ||
6191 StrnCaseCmp(name, "system.nt_sec_desc.*!", 21) == 0 ||
6192 StrCaseCmp(name, "system.nt_sec_desc.*+") == 0 ||
6193 StrnCaseCmp(name, "system.nt_sec_desc.*+!", 22) == 0 ||
6194 StrCaseCmp(name, "system.nt_sec_desc.revision") == 0 ||
6195 StrCaseCmp(name, "system.nt_sec_desc.owner") == 0 ||
6196 StrCaseCmp(name, "system.nt_sec_desc.owner+") == 0 ||
6197 StrCaseCmp(name, "system.nt_sec_desc.group") == 0 ||
6198 StrCaseCmp(name, "system.nt_sec_desc.group+") == 0 ||
6199 StrnCaseCmp(name, "system.nt_sec_desc.acl", 22) == 0 ||
6200 StrnCaseCmp(name, "system.nt_sec_desc.acl+", 23) == 0 ||
6201 StrCaseCmp(name, "system.dos_attr.*") == 0 ||
6202 StrnCaseCmp(name, "system.dos_attr.*!", 18) == 0 ||
6203 StrCaseCmp(name, "system.dos_attr.mode") == 0 ||
6204 StrCaseCmp(name, "system.dos_attr.size") == 0 ||
6205 (attr_strings.create_time_attr != NULL &&
6206 StrCaseCmp(name, attr_strings.create_time_attr) == 0) ||
6207 StrCaseCmp(name, attr_strings.access_time_attr) == 0 ||
6208 StrCaseCmp(name, attr_strings.write_time_attr) == 0 ||
6209 StrCaseCmp(name, attr_strings.change_time_attr) == 0 ||
6210 StrCaseCmp(name, "system.dos_attr.inode") == 0) {
6212 /* Yup. */
6213 ret = cacl_get(context, talloc_tos(), srv,
6214 ipc_srv == NULL ? NULL : ipc_srv->cli,
6215 &ipc_srv->pol, path,
6216 CONST_DISCARD(char *, name),
6217 CONST_DISCARD(char *, value), size);
6218 if (ret < 0 && errno == 0) {
6219 errno = smbc_errno(context, srv->cli);
6221 TALLOC_FREE(frame);
6222 return ret;
6225 /* Unsupported attribute name */
6226 errno = EINVAL;
6227 TALLOC_FREE(frame);
6228 return -1;
6232 static int
6233 smbc_removexattr_ctx(SMBCCTX *context,
6234 const char *fname,
6235 const char *name)
6237 int ret;
6238 SMBCSRV *srv = NULL;
6239 SMBCSRV *ipc_srv = NULL;
6240 char *server = NULL;
6241 char *share = NULL;
6242 char *user = NULL;
6243 char *password = NULL;
6244 char *workgroup = NULL;
6245 char *path = NULL;
6246 TALLOC_CTX *frame = talloc_stackframe();
6248 if (!context || !context->internal ||
6249 !context->internal->_initialized) {
6250 errno = EINVAL; /* Best I can think of ... */
6251 TALLOC_FREE(frame);
6252 return -1;
6255 if (!fname) {
6256 errno = EINVAL;
6257 TALLOC_FREE(frame);
6258 return -1;
6261 DEBUG(4, ("smbc_removexattr(%s, %s)\n", fname, name));
6263 if (smbc_parse_path(frame,
6264 context,
6265 fname,
6266 &workgroup,
6267 &server,
6268 &share,
6269 &path,
6270 &user,
6271 &password,
6272 NULL)) {
6273 errno = EINVAL;
6274 TALLOC_FREE(frame);
6275 return -1;
6278 if (!user || user[0] == (char)0) {
6279 user = talloc_strdup(frame, context->user);
6280 if (!user) {
6281 errno = ENOMEM;
6282 TALLOC_FREE(frame);
6283 return -1;
6287 srv = smbc_server(frame, context, True,
6288 server, share, &workgroup, &user, &password);
6289 if (!srv) {
6290 TALLOC_FREE(frame);
6291 return -1; /* errno set by smbc_server */
6294 if (! srv->no_nt_session) {
6295 ipc_srv = smbc_attr_server(frame, context, server, share,
6296 &workgroup, &user, &password);
6297 if (! ipc_srv) {
6298 srv->no_nt_session = True;
6300 } else {
6301 ipc_srv = NULL;
6304 if (! ipc_srv) {
6305 TALLOC_FREE(frame);
6306 return -1; /* errno set by smbc_attr_server */
6309 /* Are they asking to set the entire ACL? */
6310 if (StrCaseCmp(name, "system.nt_sec_desc.*") == 0 ||
6311 StrCaseCmp(name, "system.nt_sec_desc.*+") == 0) {
6313 /* Yup. */
6314 ret = cacl_set(talloc_tos(), srv->cli,
6315 ipc_srv->cli, &ipc_srv->pol, path,
6316 NULL, SMBC_XATTR_MODE_REMOVE_ALL, 0);
6317 TALLOC_FREE(frame);
6318 return ret;
6322 * Are they asking to remove one or more spceific security descriptor
6323 * attributes?
6325 if (StrCaseCmp(name, "system.nt_sec_desc.revision") == 0 ||
6326 StrCaseCmp(name, "system.nt_sec_desc.owner") == 0 ||
6327 StrCaseCmp(name, "system.nt_sec_desc.owner+") == 0 ||
6328 StrCaseCmp(name, "system.nt_sec_desc.group") == 0 ||
6329 StrCaseCmp(name, "system.nt_sec_desc.group+") == 0 ||
6330 StrnCaseCmp(name, "system.nt_sec_desc.acl", 22) == 0 ||
6331 StrnCaseCmp(name, "system.nt_sec_desc.acl+", 23) == 0) {
6333 /* Yup. */
6334 ret = cacl_set(talloc_tos(), srv->cli,
6335 ipc_srv->cli, &ipc_srv->pol, path,
6336 name + 19, SMBC_XATTR_MODE_REMOVE, 0);
6337 TALLOC_FREE(frame);
6338 return ret;
6341 /* Unsupported attribute name */
6342 errno = EINVAL;
6343 TALLOC_FREE(frame);
6344 return -1;
6347 static int
6348 smbc_listxattr_ctx(SMBCCTX *context,
6349 const char *fname,
6350 char *list,
6351 size_t size)
6354 * This isn't quite what listxattr() is supposed to do. This returns
6355 * the complete set of attribute names, always, rather than only those
6356 * attribute names which actually exist for a file. Hmmm...
6358 size_t retsize;
6359 const char supported_old[] =
6360 "system.*\0"
6361 "system.*+\0"
6362 "system.nt_sec_desc.revision\0"
6363 "system.nt_sec_desc.owner\0"
6364 "system.nt_sec_desc.owner+\0"
6365 "system.nt_sec_desc.group\0"
6366 "system.nt_sec_desc.group+\0"
6367 "system.nt_sec_desc.acl.*\0"
6368 "system.nt_sec_desc.acl\0"
6369 "system.nt_sec_desc.acl+\0"
6370 "system.nt_sec_desc.*\0"
6371 "system.nt_sec_desc.*+\0"
6372 "system.dos_attr.*\0"
6373 "system.dos_attr.mode\0"
6374 "system.dos_attr.c_time\0"
6375 "system.dos_attr.a_time\0"
6376 "system.dos_attr.m_time\0"
6378 const char supported_new[] =
6379 "system.*\0"
6380 "system.*+\0"
6381 "system.nt_sec_desc.revision\0"
6382 "system.nt_sec_desc.owner\0"
6383 "system.nt_sec_desc.owner+\0"
6384 "system.nt_sec_desc.group\0"
6385 "system.nt_sec_desc.group+\0"
6386 "system.nt_sec_desc.acl.*\0"
6387 "system.nt_sec_desc.acl\0"
6388 "system.nt_sec_desc.acl+\0"
6389 "system.nt_sec_desc.*\0"
6390 "system.nt_sec_desc.*+\0"
6391 "system.dos_attr.*\0"
6392 "system.dos_attr.mode\0"
6393 "system.dos_attr.create_time\0"
6394 "system.dos_attr.access_time\0"
6395 "system.dos_attr.write_time\0"
6396 "system.dos_attr.change_time\0"
6398 const char * supported;
6400 if (context->internal->_full_time_names) {
6401 supported = supported_new;
6402 retsize = sizeof(supported_new);
6403 } else {
6404 supported = supported_old;
6405 retsize = sizeof(supported_old);
6408 if (size == 0) {
6409 return retsize;
6412 if (retsize > size) {
6413 errno = ERANGE;
6414 return -1;
6417 /* this can't be strcpy() because there are embedded null characters */
6418 memcpy(list, supported, retsize);
6419 return retsize;
6424 * Open a print file to be written to by other calls
6427 static SMBCFILE *
6428 smbc_open_print_job_ctx(SMBCCTX *context,
6429 const char *fname)
6431 char *server = NULL;
6432 char *share = NULL;
6433 char *user = NULL;
6434 char *password = NULL;
6435 char *path = NULL;
6436 TALLOC_CTX *frame = talloc_stackframe();
6438 if (!context || !context->internal ||
6439 !context->internal->_initialized) {
6440 errno = EINVAL;
6441 TALLOC_FREE(frame);
6442 return NULL;
6445 if (!fname) {
6446 errno = EINVAL;
6447 TALLOC_FREE(frame);
6448 return NULL;
6451 DEBUG(4, ("smbc_open_print_job_ctx(%s)\n", fname));
6453 if (smbc_parse_path(frame,
6454 context,
6455 fname,
6456 NULL,
6457 &server,
6458 &share,
6459 &path,
6460 &user,
6461 &password,
6462 NULL)) {
6463 errno = EINVAL;
6464 TALLOC_FREE(frame);
6465 return NULL;
6468 /* What if the path is empty, or the file exists? */
6470 TALLOC_FREE(frame);
6471 return (context->open)(context, fname, O_WRONLY, 666);
6475 * Routine to print a file on a remote server ...
6477 * We open the file, which we assume to be on a remote server, and then
6478 * copy it to a print file on the share specified by printq.
6481 static int
6482 smbc_print_file_ctx(SMBCCTX *c_file,
6483 const char *fname,
6484 SMBCCTX *c_print,
6485 const char *printq)
6487 SMBCFILE *fid1;
6488 SMBCFILE *fid2;
6489 int bytes;
6490 int saverr;
6491 int tot_bytes = 0;
6492 char buf[4096];
6493 TALLOC_CTX *frame = talloc_stackframe();
6495 if (!c_file || !c_file->internal->_initialized || !c_print ||
6496 !c_print->internal->_initialized) {
6498 errno = EINVAL;
6499 TALLOC_FREE(frame);
6500 return -1;
6504 if (!fname && !printq) {
6506 errno = EINVAL;
6507 TALLOC_FREE(frame);
6508 return -1;
6512 /* Try to open the file for reading ... */
6514 if ((long)(fid1 = (c_file->open)(c_file, fname, O_RDONLY, 0666)) < 0) {
6515 DEBUG(3, ("Error, fname=%s, errno=%i\n", fname, errno));
6516 TALLOC_FREE(frame);
6517 return -1; /* smbc_open sets errno */
6520 /* Now, try to open the printer file for writing */
6522 if ((long)(fid2 = (c_print->open_print_job)(c_print, printq)) < 0) {
6524 saverr = errno; /* Save errno */
6525 (c_file->close_fn)(c_file, fid1);
6526 errno = saverr;
6527 TALLOC_FREE(frame);
6528 return -1;
6532 while ((bytes = (c_file->read)(c_file, fid1, buf, sizeof(buf))) > 0) {
6534 tot_bytes += bytes;
6536 if (((c_print->write)(c_print, fid2, buf, bytes)) < 0) {
6538 saverr = errno;
6539 (c_file->close_fn)(c_file, fid1);
6540 (c_print->close_fn)(c_print, fid2);
6541 errno = saverr;
6547 saverr = errno;
6549 (c_file->close_fn)(c_file, fid1); /* We have to close these anyway */
6550 (c_print->close_fn)(c_print, fid2);
6552 if (bytes < 0) {
6554 errno = saverr;
6555 TALLOC_FREE(frame);
6556 return -1;
6560 TALLOC_FREE(frame);
6561 return tot_bytes;
6566 * Routine to list print jobs on a printer share ...
6569 static int
6570 smbc_list_print_jobs_ctx(SMBCCTX *context,
6571 const char *fname,
6572 smbc_list_print_job_fn fn)
6574 SMBCSRV *srv = NULL;
6575 char *server = NULL;
6576 char *share = NULL;
6577 char *user = NULL;
6578 char *password = NULL;
6579 char *workgroup = NULL;
6580 char *path = NULL;
6581 TALLOC_CTX *frame = talloc_stackframe();
6583 if (!context || !context->internal ||
6584 !context->internal->_initialized) {
6585 errno = EINVAL;
6586 TALLOC_FREE(frame);
6587 return -1;
6590 if (!fname) {
6591 errno = EINVAL;
6592 TALLOC_FREE(frame);
6593 return -1;
6596 DEBUG(4, ("smbc_list_print_jobs(%s)\n", fname));
6598 if (smbc_parse_path(frame,
6599 context,
6600 fname,
6601 &workgroup,
6602 &server,
6603 &share,
6604 &path,
6605 &user,
6606 &password,
6607 NULL)) {
6608 errno = EINVAL;
6609 TALLOC_FREE(frame);
6610 return -1;
6613 if (!user || user[0] == (char)0) {
6614 user = talloc_strdup(frame, context->user);
6615 if (!user) {
6616 errno = ENOMEM;
6617 TALLOC_FREE(frame);
6618 return -1;
6622 srv = smbc_server(frame, context, True,
6623 server, share, &workgroup, &user, &password);
6625 if (!srv) {
6626 TALLOC_FREE(frame);
6627 return -1; /* errno set by smbc_server */
6630 if (cli_print_queue(srv->cli,
6631 (void (*)(struct print_job_info *))fn) < 0) {
6632 errno = smbc_errno(context, srv->cli);
6633 TALLOC_FREE(frame);
6634 return -1;
6637 TALLOC_FREE(frame);
6638 return 0;
6643 * Delete a print job from a remote printer share
6646 static int
6647 smbc_unlink_print_job_ctx(SMBCCTX *context,
6648 const char *fname,
6649 int id)
6651 SMBCSRV *srv = NULL;
6652 char *server = NULL;
6653 char *share = NULL;
6654 char *user = NULL;
6655 char *password = NULL;
6656 char *workgroup = NULL;
6657 char *path = NULL;
6658 int err;
6659 TALLOC_CTX *frame = talloc_stackframe();
6661 if (!context || !context->internal ||
6662 !context->internal->_initialized) {
6663 errno = EINVAL;
6664 TALLOC_FREE(frame);
6665 return -1;
6668 if (!fname) {
6669 errno = EINVAL;
6670 TALLOC_FREE(frame);
6671 return -1;
6674 DEBUG(4, ("smbc_unlink_print_job(%s)\n", fname));
6676 if (smbc_parse_path(frame,
6677 context,
6678 fname,
6679 &workgroup,
6680 &server,
6681 &share,
6682 &path,
6683 &user,
6684 &password,
6685 NULL)) {
6686 errno = EINVAL;
6687 TALLOC_FREE(frame);
6688 return -1;
6691 if (!user || user[0] == (char)0) {
6692 user = talloc_strdup(frame, context->user);
6693 if (!user) {
6694 errno = ENOMEM;
6695 TALLOC_FREE(frame);
6696 return -1;
6700 srv = smbc_server(frame, context, True,
6701 server, share, &workgroup, &user, &password);
6703 if (!srv) {
6705 TALLOC_FREE(frame);
6706 return -1; /* errno set by smbc_server */
6710 if ((err = cli_printjob_del(srv->cli, id)) != 0) {
6712 if (err < 0)
6713 errno = smbc_errno(context, srv->cli);
6714 else if (err == ERRnosuchprintjob)
6715 errno = EINVAL;
6716 TALLOC_FREE(frame);
6717 return -1;
6721 TALLOC_FREE(frame);
6722 return 0;
6727 * Get a new empty handle to fill in with your own info
6729 SMBCCTX *
6730 smbc_new_context(void)
6732 SMBCCTX *context;
6734 context = SMB_MALLOC_P(SMBCCTX);
6735 if (!context) {
6736 errno = ENOMEM;
6737 return NULL;
6740 ZERO_STRUCTP(context);
6742 context->internal = SMB_MALLOC_P(struct smbc_internal_data);
6743 if (!context->internal) {
6744 SAFE_FREE(context);
6745 errno = ENOMEM;
6746 return NULL;
6749 ZERO_STRUCTP(context->internal);
6751 /* ADD REASONABLE DEFAULTS */
6752 context->debug = 0;
6753 context->timeout = 20000; /* 20 seconds */
6755 context->options.browse_max_lmb_count = 3; /* # LMBs to query */
6756 context->options.urlencode_readdir_entries = False;/* backward compat */
6757 context->options.one_share_per_server = False;/* backward compat */
6758 context->internal->_share_mode = SMBC_SHAREMODE_DENY_NONE;
6759 /* backward compat */
6761 context->open = smbc_open_ctx;
6762 context->creat = smbc_creat_ctx;
6763 context->read = smbc_read_ctx;
6764 context->write = smbc_write_ctx;
6765 context->close_fn = smbc_close_ctx;
6766 context->unlink = smbc_unlink_ctx;
6767 context->rename = smbc_rename_ctx;
6768 context->lseek = smbc_lseek_ctx;
6769 context->stat = smbc_stat_ctx;
6770 context->fstat = smbc_fstat_ctx;
6771 context->opendir = smbc_opendir_ctx;
6772 context->closedir = smbc_closedir_ctx;
6773 context->readdir = smbc_readdir_ctx;
6774 context->getdents = smbc_getdents_ctx;
6775 context->mkdir = smbc_mkdir_ctx;
6776 context->rmdir = smbc_rmdir_ctx;
6777 context->telldir = smbc_telldir_ctx;
6778 context->lseekdir = smbc_lseekdir_ctx;
6779 context->fstatdir = smbc_fstatdir_ctx;
6780 context->ftruncate = smbc_ftruncate_ctx;
6781 context->chmod = smbc_chmod_ctx;
6782 context->utimes = smbc_utimes_ctx;
6783 context->setxattr = smbc_setxattr_ctx;
6784 context->getxattr = smbc_getxattr_ctx;
6785 context->removexattr = smbc_removexattr_ctx;
6786 context->listxattr = smbc_listxattr_ctx;
6787 context->open_print_job = smbc_open_print_job_ctx;
6788 context->print_file = smbc_print_file_ctx;
6789 context->list_print_jobs = smbc_list_print_jobs_ctx;
6790 context->unlink_print_job = smbc_unlink_print_job_ctx;
6792 context->callbacks.check_server_fn = smbc_check_server;
6793 context->callbacks.remove_unused_server_fn = smbc_remove_unused_server;
6795 smbc_default_cache_functions(context);
6797 return context;
6801 * Free a context
6803 * Returns 0 on success. Otherwise returns 1, the SMBCCTX is _not_ freed
6804 * and thus you'll be leaking memory if not handled properly.
6808 smbc_free_context(SMBCCTX *context,
6809 int shutdown_ctx)
6811 if (!context) {
6812 errno = EBADF;
6813 return 1;
6816 if (shutdown_ctx) {
6817 SMBCFILE * f;
6818 DEBUG(1,("Performing aggressive shutdown.\n"));
6820 f = context->internal->_files;
6821 while (f) {
6822 (context->close_fn)(context, f);
6823 f = f->next;
6825 context->internal->_files = NULL;
6827 /* First try to remove the servers the nice way. */
6828 if (context->callbacks.purge_cached_fn(context)) {
6829 SMBCSRV * s;
6830 SMBCSRV * next;
6831 DEBUG(1, ("Could not purge all servers, "
6832 "Nice way shutdown failed.\n"));
6833 s = context->internal->_servers;
6834 while (s) {
6835 DEBUG(1, ("Forced shutdown: %p (fd=%d)\n",
6836 s, s->cli->fd));
6837 cli_shutdown(s->cli);
6838 (context->callbacks.remove_cached_srv_fn)(context,
6840 next = s->next;
6841 DLIST_REMOVE(context->internal->_servers, s);
6842 SAFE_FREE(s);
6843 s = next;
6845 context->internal->_servers = NULL;
6848 else {
6849 /* This is the polite way */
6850 if ((context->callbacks.purge_cached_fn)(context)) {
6851 DEBUG(1, ("Could not purge all servers, "
6852 "free_context failed.\n"));
6853 errno = EBUSY;
6854 return 1;
6856 if (context->internal->_servers) {
6857 DEBUG(1, ("Active servers in context, "
6858 "free_context failed.\n"));
6859 errno = EBUSY;
6860 return 1;
6862 if (context->internal->_files) {
6863 DEBUG(1, ("Active files in context, "
6864 "free_context failed.\n"));
6865 errno = EBUSY;
6866 return 1;
6870 /* Things we have to clean up */
6871 SAFE_FREE(context->workgroup);
6872 SAFE_FREE(context->netbios_name);
6873 SAFE_FREE(context->user);
6875 DEBUG(3, ("Context %p successfully freed\n", context));
6876 SAFE_FREE(context->internal);
6877 SAFE_FREE(context);
6878 return 0;
6883 * Each time the context structure is changed, we have binary backward
6884 * compatibility issues. Instead of modifying the public portions of the
6885 * context structure to add new options, instead, we put them in the internal
6886 * portion of the context structure and provide a set function for these new
6887 * options.
6889 void
6890 smbc_option_set(SMBCCTX *context,
6891 char *option_name,
6892 ... /* option_value */)
6894 va_list ap;
6895 union {
6896 int i;
6897 bool b;
6898 smbc_get_auth_data_with_context_fn auth_fn;
6899 void *v;
6900 const char *s;
6901 } option_value;
6903 va_start(ap, option_name);
6905 if (strcmp(option_name, "debug_to_stderr") == 0) {
6907 * Log to standard error instead of standard output.
6909 option_value.b = (bool) va_arg(ap, int);
6910 context->internal->_debug_stderr = option_value.b;
6912 } else if (strcmp(option_name, "full_time_names") == 0) {
6914 * Use new-style time attribute names, e.g. WRITE_TIME rather
6915 * than the old-style names such as M_TIME. This allows also
6916 * setting/getting CREATE_TIME which was previously
6917 * unimplemented. (Note that the old C_TIME was supposed to
6918 * be CHANGE_TIME but was confused and sometimes referred to
6919 * CREATE_TIME.)
6921 option_value.b = (bool) va_arg(ap, int);
6922 context->internal->_full_time_names = option_value.b;
6924 } else if (strcmp(option_name, "open_share_mode") == 0) {
6926 * The share mode to use for files opened with
6927 * smbc_open_ctx(). The default is SMBC_SHAREMODE_DENY_NONE.
6929 option_value.i = va_arg(ap, int);
6930 context->internal->_share_mode =
6931 (smbc_share_mode) option_value.i;
6933 } else if (strcmp(option_name, "auth_function") == 0) {
6935 * Use the new-style authentication function which includes
6936 * the context.
6938 option_value.auth_fn =
6939 va_arg(ap, smbc_get_auth_data_with_context_fn);
6940 context->internal->_auth_fn_with_context =
6941 option_value.auth_fn;
6942 } else if (strcmp(option_name, "user_data") == 0) {
6944 * Save a user data handle which may be retrieved by the user
6945 * with smbc_option_get()
6947 option_value.v = va_arg(ap, void *);
6948 context->internal->_user_data = option_value.v;
6949 } else if (strcmp(option_name, "smb_encrypt_level") == 0) {
6951 * Save an encoded value for encryption level.
6952 * 0 = off, 1 = attempt, 2 = required.
6954 option_value.s = va_arg(ap, const char *);
6955 if (strcmp(option_value.s, "none") == 0) {
6956 context->internal->_smb_encryption_level = 0;
6957 } else if (strcmp(option_value.s, "request") == 0) {
6958 context->internal->_smb_encryption_level = 1;
6959 } else if (strcmp(option_value.s, "require") == 0) {
6960 context->internal->_smb_encryption_level = 2;
6964 va_end(ap);
6969 * Retrieve the current value of an option
6971 void *
6972 smbc_option_get(SMBCCTX *context,
6973 char *option_name)
6975 if (strcmp(option_name, "debug_stderr") == 0) {
6977 * Log to standard error instead of standard output.
6979 #if defined(__intptr_t_defined) || defined(HAVE_INTPTR_T)
6980 return (void *) (intptr_t) context->internal->_debug_stderr;
6981 #else
6982 return (void *) context->internal->_debug_stderr;
6983 #endif
6984 } else if (strcmp(option_name, "full_time_names") == 0) {
6986 * Use new-style time attribute names, e.g. WRITE_TIME rather
6987 * than the old-style names such as M_TIME. This allows also
6988 * setting/getting CREATE_TIME which was previously
6989 * unimplemented. (Note that the old C_TIME was supposed to
6990 * be CHANGE_TIME but was confused and sometimes referred to
6991 * CREATE_TIME.)
6993 #if defined(__intptr_t_defined) || defined(HAVE_INTPTR_T)
6994 return (void *) (intptr_t) context->internal->_full_time_names;
6995 #else
6996 return (void *) context->internal->_full_time_names;
6997 #endif
6999 } else if (strcmp(option_name, "auth_function") == 0) {
7001 * Use the new-style authentication function which includes
7002 * the context.
7004 return (void *) context->internal->_auth_fn_with_context;
7005 } else if (strcmp(option_name, "user_data") == 0) {
7007 * Save a user data handle which may be retrieved by the user
7008 * with smbc_option_get()
7010 return context->internal->_user_data;
7011 } else if (strcmp(option_name, "smb_encrypt_level") == 0) {
7013 * Return the current smb encrypt negotiate option as a string.
7015 switch (context->internal->_smb_encryption_level) {
7016 case 0:
7017 return (void *) "none";
7018 case 1:
7019 return (void *) "request";
7020 case 2:
7021 return (void *) "require";
7023 } else if (strcmp(option_name, "smb_encrypt_on") == 0) {
7025 * Return the current smb encrypt status option as a bool.
7026 * false = off, true = on. We don't know what server is
7027 * being requested, so we only return true if all servers
7028 * are using an encrypted connection.
7030 SMBCSRV *s;
7031 unsigned int num_servers = 0;
7033 for (s = context->internal->_servers; s; s = s->next) {
7034 num_servers++;
7035 if (s->cli->trans_enc_state == NULL) {
7036 return (void *)false;
7039 return (void *) (bool) (num_servers > 0);
7042 return NULL;
7047 * Initialise the library etc
7049 * We accept a struct containing handle information.
7050 * valid values for info->debug from 0 to 100,
7051 * and insist that info->fn must be non-null.
7053 SMBCCTX *
7054 smbc_init_context(SMBCCTX *context)
7056 int pid;
7057 char *user = NULL;
7058 char *home = NULL;
7060 if (!context || !context->internal) {
7061 errno = EBADF;
7062 return NULL;
7065 /* Do not initialise the same client twice */
7066 if (context->internal->_initialized) {
7067 return 0;
7070 if ((!context->callbacks.auth_fn &&
7071 !context->internal->_auth_fn_with_context) ||
7072 context->debug < 0 ||
7073 context->debug > 100) {
7075 errno = EINVAL;
7076 return NULL;
7080 if (!smbc_initialized) {
7082 * Do some library-wide intializations the first time we get
7083 * called
7085 bool conf_loaded = False;
7086 TALLOC_CTX *frame = talloc_stackframe();
7088 /* Set this to what the user wants */
7089 DEBUGLEVEL = context->debug;
7091 load_case_tables();
7093 setup_logging("libsmbclient", True);
7094 if (context->internal->_debug_stderr) {
7095 dbf = x_stderr;
7096 x_setbuf(x_stderr, NULL);
7099 /* Here we would open the smb.conf file if needed ... */
7101 in_client = True; /* FIXME, make a param */
7103 home = getenv("HOME");
7104 if (home) {
7105 char *conf = NULL;
7106 if (asprintf(&conf, "%s/.smb/smb.conf", home) > 0) {
7107 if (lp_load(conf, True, False, False, True)) {
7108 conf_loaded = True;
7109 } else {
7110 DEBUG(5, ("Could not load config file: %s\n",
7111 conf));
7113 SAFE_FREE(conf);
7117 if (!conf_loaded) {
7119 * Well, if that failed, try the get_dyn_CONFIGFILE
7120 * Which points to the standard locn, and if that
7121 * fails, silently ignore it and use the internal
7122 * defaults ...
7125 if (!lp_load(get_dyn_CONFIGFILE(), True, False, False, False)) {
7126 DEBUG(5, ("Could not load config file: %s\n",
7127 get_dyn_CONFIGFILE()));
7128 } else if (home) {
7129 char *conf;
7131 * We loaded the global config file. Now lets
7132 * load user-specific modifications to the
7133 * global config.
7135 if (asprintf(&conf,
7136 "%s/.smb/smb.conf.append",
7137 home) > 0) {
7138 if (!lp_load(conf, True, False, False, False)) {
7139 DEBUG(10,
7140 ("Could not append config file: "
7141 "%s\n",
7142 conf));
7144 SAFE_FREE(conf);
7149 load_interfaces(); /* Load the list of interfaces ... */
7151 reopen_logs(); /* Get logging working ... */
7154 * Block SIGPIPE (from lib/util_sock.c: write())
7155 * It is not needed and should not stop execution
7157 BlockSignals(True, SIGPIPE);
7159 /* Done with one-time initialisation */
7160 smbc_initialized = 1;
7162 TALLOC_FREE(frame);
7165 if (!context->user) {
7167 * FIXME: Is this the best way to get the user info?
7169 user = getenv("USER");
7170 /* walk around as "guest" if no username can be found */
7171 if (!user) context->user = SMB_STRDUP("guest");
7172 else context->user = SMB_STRDUP(user);
7175 if (!context->netbios_name) {
7177 * We try to get our netbios name from the config. If that
7178 * fails we fall back on constructing our netbios name from
7179 * our hostname etc
7181 if (global_myname()) {
7182 context->netbios_name = SMB_STRDUP(global_myname());
7184 else {
7186 * Hmmm, I want to get hostname as well, but I am too
7187 * lazy for the moment
7189 pid = sys_getpid();
7190 context->netbios_name = (char *)SMB_MALLOC(17);
7191 if (!context->netbios_name) {
7192 errno = ENOMEM;
7193 return NULL;
7195 slprintf(context->netbios_name, 16,
7196 "smbc%s%d", context->user, pid);
7200 DEBUG(1, ("Using netbios name %s.\n", context->netbios_name));
7202 if (!context->workgroup) {
7203 if (lp_workgroup()) {
7204 context->workgroup = SMB_STRDUP(lp_workgroup());
7206 else {
7207 /* TODO: Think about a decent default workgroup */
7208 context->workgroup = SMB_STRDUP("samba");
7212 DEBUG(1, ("Using workgroup %s.\n", context->workgroup));
7214 /* shortest timeout is 1 second */
7215 if (context->timeout > 0 && context->timeout < 1000)
7216 context->timeout = 1000;
7219 * FIXME: Should we check the function pointers here?
7222 context->internal->_initialized = True;
7224 return context;
7228 /* Return the verion of samba, and thus libsmbclient */
7229 const char *
7230 smbc_version(void)
7232 return samba_version_string();