[GLUE] Rsync SAMBA_3_0 SVN r25598 in order to create the v3-0-test branch.
[Samba.git] / source / libsmb / libsmbclient.c
blob1633aa469cc83a661b8a1c25272c8f28dfcfbe1f
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
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
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()
137 * Convert strings of %xx to their single character equivalent. Each 'x' must
138 * be a valid hexadecimal digit, or that % sequence is left undecoded.
140 * dest may, but need not be, the same pointer as src.
142 * Returns the number of % sequences which could not be converted due to lack
143 * of two following hexadecimal digits.
146 smbc_urldecode(char *dest, char * src, size_t max_dest_len)
148 int old_length = strlen(src);
149 int i = 0;
150 int err_count = 0;
151 pstring temp;
152 char * p;
154 if ( old_length == 0 ) {
155 return 0;
158 p = temp;
159 while ( i < old_length ) {
160 unsigned char character = src[ i++ ];
162 if (character == '%') {
163 int a = i+1 < old_length ? hex2int( src[i] ) : -1;
164 int b = i+1 < old_length ? hex2int( src[i+1] ) : -1;
166 /* Replace valid sequence */
167 if (a != -1 && b != -1) {
169 /* Replace valid %xx sequence with %dd */
170 character = (a * 16) + b;
172 if (character == '\0') {
173 break; /* Stop at %00 */
176 i += 2;
177 } else {
179 err_count++;
183 *p++ = character;
186 *p = '\0';
188 strncpy(dest, temp, max_dest_len - 1);
189 dest[max_dest_len - 1] = '\0';
191 return err_count;
195 * smbc_urlencode()
197 * Convert any characters not specifically allowed in a URL into their %xx
198 * equivalent.
200 * Returns the remaining buffer length.
203 smbc_urlencode(char * dest, char * src, int max_dest_len)
205 char hex[] = "0123456789ABCDEF";
207 for (; *src != '\0' && max_dest_len >= 3; src++) {
209 if ((*src < '0' &&
210 *src != '-' &&
211 *src != '.') ||
212 (*src > '9' &&
213 *src < 'A') ||
214 (*src > 'Z' &&
215 *src < 'a' &&
216 *src != '_') ||
217 (*src > 'z')) {
218 *dest++ = '%';
219 *dest++ = hex[(*src >> 4) & 0x0f];
220 *dest++ = hex[*src & 0x0f];
221 max_dest_len -= 3;
222 } else {
223 *dest++ = *src;
224 max_dest_len--;
228 *dest++ = '\0';
229 max_dest_len--;
231 return max_dest_len;
235 * Function to parse a path and turn it into components
237 * The general format of an SMB URI is explain in Christopher Hertel's CIFS
238 * book, at http://ubiqx.org/cifs/Appendix-D.html. We accept a subset of the
239 * general format ("smb:" only; we do not look for "cifs:").
242 * We accept:
243 * smb://[[[domain;]user[:password]@]server[/share[/path[/file]]]][?options]
245 * Meaning of URLs:
247 * smb:// Show all workgroups.
249 * The method of locating the list of workgroups varies
250 * depending upon the setting of the context variable
251 * context->options.browse_max_lmb_count. This value
252 * determine the maximum number of local master browsers to
253 * query for the list of workgroups. In order to ensure that
254 * a complete list of workgroups is obtained, all master
255 * browsers must be queried, but if there are many
256 * workgroups, the time spent querying can begin to add up.
257 * For small networks (not many workgroups), it is suggested
258 * that this variable be set to 0, indicating query all local
259 * master browsers. When the network has many workgroups, a
260 * reasonable setting for this variable might be around 3.
262 * smb://name/ if name<1D> or name<1B> exists, list servers in
263 * workgroup, else, if name<20> exists, list all shares
264 * for server ...
266 * If "options" are provided, this function returns the entire option list as a
267 * string, for later parsing by the caller. Note that currently, no options
268 * are supported.
271 static const char *smbc_prefix = "smb:";
273 static int
274 smbc_parse_path(SMBCCTX *context,
275 const char *fname,
276 char *workgroup, int workgroup_len,
277 char *server, int server_len,
278 char *share, int share_len,
279 char *path, int path_len,
280 char *user, int user_len,
281 char *password, int password_len,
282 char *options, int options_len)
284 static pstring s;
285 pstring userinfo;
286 const char *p;
287 char *q, *r;
288 int len;
290 server[0] = share[0] = path[0] = user[0] = password[0] = (char)0;
293 * Assume we wont find an authentication domain to parse, so default
294 * to the workgroup in the provided context.
296 if (workgroup != NULL) {
297 strncpy(workgroup, context->workgroup, workgroup_len - 1);
298 workgroup[workgroup_len - 1] = '\0';
301 if (options != NULL && options_len > 0) {
302 options[0] = (char)0;
304 pstrcpy(s, fname);
306 /* see if it has the right prefix */
307 len = strlen(smbc_prefix);
308 if (strncmp(s,smbc_prefix,len) || (s[len] != '/' && s[len] != 0)) {
309 return -1; /* What about no smb: ? */
312 p = s + len;
314 /* Watch the test below, we are testing to see if we should exit */
316 if (strncmp(p, "//", 2) && strncmp(p, "\\\\", 2)) {
318 DEBUG(1, ("Invalid path (does not begin with smb://"));
319 return -1;
323 p += 2; /* Skip the double slash */
325 /* See if any options were specified */
326 if ((q = strrchr(p, '?')) != NULL ) {
327 /* There are options. Null terminate here and point to them */
328 *q++ = '\0';
330 DEBUG(4, ("Found options '%s'", q));
332 /* Copy the options */
333 if (options != NULL && options_len > 0) {
334 safe_strcpy(options, q, options_len - 1);
338 if (*p == (char)0)
339 goto decoding;
341 if (*p == '/') {
342 int wl = strlen(context->workgroup);
344 if (wl > 16) {
345 wl = 16;
348 strncpy(server, context->workgroup, wl);
349 server[wl] = '\0';
350 return 0;
354 * ok, its for us. Now parse out the server, share etc.
356 * However, we want to parse out [[domain;]user[:password]@] if it
357 * exists ...
360 /* check that '@' occurs before '/', if '/' exists at all */
361 q = strchr_m(p, '@');
362 r = strchr_m(p, '/');
363 if (q && (!r || q < r)) {
364 pstring username, passwd, domain;
365 const char *u = userinfo;
367 next_token_no_ltrim(&p, userinfo, "@", sizeof(fstring));
369 username[0] = passwd[0] = domain[0] = 0;
371 if (strchr_m(u, ';')) {
373 next_token_no_ltrim(&u, domain, ";", sizeof(fstring));
377 if (strchr_m(u, ':')) {
379 next_token_no_ltrim(&u, username, ":", sizeof(fstring));
381 pstrcpy(passwd, u);
384 else {
386 pstrcpy(username, u);
390 if (domain[0] && workgroup) {
391 strncpy(workgroup, domain, workgroup_len - 1);
392 workgroup[workgroup_len - 1] = '\0';
395 if (username[0]) {
396 strncpy(user, username, user_len - 1);
397 user[user_len - 1] = '\0';
400 if (passwd[0]) {
401 strncpy(password, passwd, password_len - 1);
402 password[password_len - 1] = '\0';
407 if (!next_token(&p, server, "/", sizeof(fstring))) {
409 return -1;
413 if (*p == (char)0) goto decoding; /* That's it ... */
415 if (!next_token(&p, share, "/", sizeof(fstring))) {
417 return -1;
422 * Prepend a leading slash if there's a file path, as required by
423 * NetApp filers.
425 *path = '\0';
426 if (*p != '\0') {
427 *path = '/';
428 safe_strcpy(path + 1, p, path_len - 2);
431 all_string_sub(path, "/", "\\", 0);
433 decoding:
434 (void) smbc_urldecode(path, path, path_len);
435 (void) smbc_urldecode(server, server, server_len);
436 (void) smbc_urldecode(share, share, share_len);
437 (void) smbc_urldecode(user, user, user_len);
438 (void) smbc_urldecode(password, password, password_len);
440 return 0;
444 * Verify that the options specified in a URL are valid
446 static int
447 smbc_check_options(char *server,
448 char *share,
449 char *path,
450 char *options)
452 DEBUG(4, ("smbc_check_options(): server='%s' share='%s' "
453 "path='%s' options='%s'\n",
454 server, share, path, options));
456 /* No options at all is always ok */
457 if (! *options) return 0;
459 /* Currently, we don't support any options. */
460 return -1;
464 * Convert an SMB error into a UNIX error ...
466 static int
467 smbc_errno(SMBCCTX *context,
468 struct cli_state *c)
470 int ret = cli_errno(c);
472 if (cli_is_dos_error(c)) {
473 uint8 eclass;
474 uint32 ecode;
476 cli_dos_error(c, &eclass, &ecode);
478 DEBUG(3,("smbc_error %d %d (0x%x) -> %d\n",
479 (int)eclass, (int)ecode, (int)ecode, ret));
480 } else {
481 NTSTATUS status;
483 status = cli_nt_error(c);
485 DEBUG(3,("smbc errno %s -> %d\n",
486 nt_errstr(status), ret));
489 return ret;
493 * Check a server for being alive and well.
494 * returns 0 if the server is in shape. Returns 1 on error
496 * Also useable outside libsmbclient to enable external cache
497 * to do some checks too.
499 static int
500 smbc_check_server(SMBCCTX * context,
501 SMBCSRV * server)
503 socklen_t size;
504 struct sockaddr addr;
506 size = sizeof(addr);
507 return (getpeername(server->cli->fd, &addr, &size) == -1);
511 * Remove a server from the cached server list it's unused.
512 * On success, 0 is returned. 1 is returned if the server could not be removed.
514 * Also useable outside libsmbclient
517 smbc_remove_unused_server(SMBCCTX * context,
518 SMBCSRV * srv)
520 SMBCFILE * file;
522 /* are we being fooled ? */
523 if (!context || !context->internal ||
524 !context->internal->_initialized || !srv) return 1;
527 /* Check all open files/directories for a relation with this server */
528 for (file = context->internal->_files; file; file=file->next) {
529 if (file->srv == srv) {
530 /* Still used */
531 DEBUG(3, ("smbc_remove_usused_server: "
532 "%p still used by %p.\n",
533 srv, file));
534 return 1;
538 DLIST_REMOVE(context->internal->_servers, srv);
540 cli_shutdown(srv->cli);
541 srv->cli = NULL;
543 DEBUG(3, ("smbc_remove_usused_server: %p removed.\n", srv));
545 (context->callbacks.remove_cached_srv_fn)(context, srv);
547 SAFE_FREE(srv);
549 return 0;
552 static SMBCSRV *
553 find_server(SMBCCTX *context,
554 const char *server,
555 const char *share,
556 fstring workgroup,
557 fstring username,
558 fstring password)
560 SMBCSRV *srv;
561 int auth_called = 0;
563 check_server_cache:
565 srv = (context->callbacks.get_cached_srv_fn)(context, server, share,
566 workgroup, username);
568 if (!auth_called && !srv && (!username[0] || !password[0])) {
569 if (context->internal->_auth_fn_with_context != NULL) {
570 (context->internal->_auth_fn_with_context)(
571 context,
572 server, share,
573 workgroup, sizeof(fstring),
574 username, sizeof(fstring),
575 password, sizeof(fstring));
576 } else {
577 (context->callbacks.auth_fn)(
578 server, share,
579 workgroup, sizeof(fstring),
580 username, sizeof(fstring),
581 password, sizeof(fstring));
585 * However, smbc_auth_fn may have picked up info relating to
586 * an existing connection, so try for an existing connection
587 * again ...
589 auth_called = 1;
590 goto check_server_cache;
594 if (srv) {
595 if ((context->callbacks.check_server_fn)(context, srv)) {
597 * This server is no good anymore
598 * Try to remove it and check for more possible
599 * servers in the cache
601 if ((context->callbacks.remove_unused_server_fn)(context,
602 srv)) {
604 * We could not remove the server completely,
605 * remove it from the cache so we will not get
606 * it again. It will be removed when the last
607 * file/dir is closed.
609 (context->callbacks.remove_cached_srv_fn)(context,
610 srv);
614 * Maybe there are more cached connections to this
615 * server
617 goto check_server_cache;
620 return srv;
623 return NULL;
627 * Connect to a server, possibly on an existing connection
629 * Here, what we want to do is: If the server and username
630 * match an existing connection, reuse that, otherwise, establish a
631 * new connection.
633 * If we have to create a new connection, call the auth_fn to get the
634 * info we need, unless the username and password were passed in.
637 static SMBCSRV *
638 smbc_server(SMBCCTX *context,
639 BOOL connect_if_not_found,
640 const char *server,
641 const char *share,
642 fstring workgroup,
643 fstring username,
644 fstring password)
646 SMBCSRV *srv=NULL;
647 struct cli_state *c;
648 struct nmb_name called, calling;
649 const char *server_n = server;
650 pstring ipenv;
651 struct in_addr ip;
652 int tried_reverse = 0;
653 int port_try_first;
654 int port_try_next;
655 const char *username_used;
656 NTSTATUS status;
658 zero_ip(&ip);
659 ZERO_STRUCT(c);
661 if (server[0] == 0) {
662 errno = EPERM;
663 return NULL;
666 /* Look for a cached connection */
667 srv = find_server(context, server, share,
668 workgroup, username, password);
671 * If we found a connection and we're only allowed one share per
672 * server...
674 if (srv && *share != '\0' && context->options.one_share_per_server) {
677 * ... then if there's no current connection to the share,
678 * connect to it. find_server(), or rather the function
679 * pointed to by context->callbacks.get_cached_srv_fn which
680 * was called by find_server(), will have issued a tree
681 * disconnect if the requested share is not the same as the
682 * one that was already connected.
684 if (srv->cli->cnum == (uint16) -1) {
685 /* Ensure we have accurate auth info */
686 if (context->internal->_auth_fn_with_context != NULL) {
687 (context->internal->_auth_fn_with_context)(
688 context,
689 server, share,
690 workgroup, sizeof(fstring),
691 username, sizeof(fstring),
692 password, sizeof(fstring));
693 } else {
694 (context->callbacks.auth_fn)(
695 server, share,
696 workgroup, sizeof(fstring),
697 username, sizeof(fstring),
698 password, sizeof(fstring));
701 if (! cli_send_tconX(srv->cli, share, "?????",
702 password, strlen(password)+1)) {
704 errno = smbc_errno(context, srv->cli);
705 cli_shutdown(srv->cli);
706 srv->cli = NULL;
707 (context->callbacks.remove_cached_srv_fn)(context,
708 srv);
709 srv = NULL;
713 * Regenerate the dev value since it's based on both
714 * server and share
716 if (srv) {
717 srv->dev = (dev_t)(str_checksum(server) ^
718 str_checksum(share));
723 /* If we have a connection... */
724 if (srv) {
726 /* ... then we're done here. Give 'em what they came for. */
727 return srv;
730 /* If we're not asked to connect when a connection doesn't exist... */
731 if (! connect_if_not_found) {
732 /* ... then we're done here. */
733 return NULL;
736 make_nmb_name(&calling, context->netbios_name, 0x0);
737 make_nmb_name(&called , server, 0x20);
739 DEBUG(4,("smbc_server: server_n=[%s] server=[%s]\n", server_n, server));
741 DEBUG(4,(" -> server_n=[%s] server=[%s]\n", server_n, server));
743 again:
744 slprintf(ipenv,sizeof(ipenv)-1,"HOST_%s", server_n);
746 zero_ip(&ip);
748 /* have to open a new connection */
749 if ((c = cli_initialise()) == NULL) {
750 errno = ENOMEM;
751 return NULL;
754 if (context->flags & SMB_CTX_FLAG_USE_KERBEROS) {
755 c->use_kerberos = True;
757 if (context->flags & SMB_CTX_FLAG_FALLBACK_AFTER_KERBEROS) {
758 c->fallback_after_kerberos = True;
761 c->timeout = context->timeout;
764 * Force use of port 139 for first try if share is $IPC, empty, or
765 * null, so browse lists can work
767 if (share == NULL || *share == '\0' || strcmp(share, "IPC$") == 0) {
768 port_try_first = 139;
769 port_try_next = 445;
770 } else {
771 port_try_first = 445;
772 port_try_next = 139;
775 c->port = port_try_first;
777 status = cli_connect(c, server_n, &ip);
778 if (!NT_STATUS_IS_OK(status)) {
780 /* First connection attempt failed. Try alternate port. */
781 c->port = port_try_next;
783 status = cli_connect(c, server_n, &ip);
784 if (!NT_STATUS_IS_OK(status)) {
785 cli_shutdown(c);
786 errno = ETIMEDOUT;
787 return NULL;
791 if (!cli_session_request(c, &calling, &called)) {
792 cli_shutdown(c);
793 if (strcmp(called.name, "*SMBSERVER")) {
794 make_nmb_name(&called , "*SMBSERVER", 0x20);
795 goto again;
796 } else { /* Try one more time, but ensure we don't loop */
798 /* Only try this if server is an IP address ... */
800 if (is_ipaddress(server) && !tried_reverse) {
801 fstring remote_name;
802 struct in_addr rem_ip;
804 if ((rem_ip.s_addr=inet_addr(server)) == INADDR_NONE) {
805 DEBUG(4, ("Could not convert IP address "
806 "%s to struct in_addr\n", server));
807 errno = ETIMEDOUT;
808 return NULL;
811 tried_reverse++; /* Yuck */
813 if (name_status_find("*", 0, 0, rem_ip, remote_name)) {
814 make_nmb_name(&called, remote_name, 0x20);
815 goto again;
819 errno = ETIMEDOUT;
820 return NULL;
823 DEBUG(4,(" session request ok\n"));
825 if (!cli_negprot(c)) {
826 cli_shutdown(c);
827 errno = ETIMEDOUT;
828 return NULL;
831 username_used = username;
833 if (!NT_STATUS_IS_OK(cli_session_setup(c, username_used,
834 password, strlen(password),
835 password, strlen(password),
836 workgroup))) {
838 /* Failed. Try an anonymous login, if allowed by flags. */
839 username_used = "";
841 if ((context->flags & SMBCCTX_FLAG_NO_AUTO_ANONYMOUS_LOGON) ||
842 !NT_STATUS_IS_OK(cli_session_setup(c, username_used,
843 password, 1,
844 password, 0,
845 workgroup))) {
847 cli_shutdown(c);
848 errno = EPERM;
849 return NULL;
853 DEBUG(4,(" session setup ok\n"));
855 if (!cli_send_tconX(c, share, "?????",
856 password, strlen(password)+1)) {
857 errno = smbc_errno(context, c);
858 cli_shutdown(c);
859 return NULL;
862 DEBUG(4,(" tconx ok\n"));
865 * Ok, we have got a nice connection
866 * Let's allocate a server structure.
869 srv = SMB_MALLOC_P(SMBCSRV);
870 if (!srv) {
871 errno = ENOMEM;
872 goto failed;
875 ZERO_STRUCTP(srv);
876 srv->cli = c;
877 srv->dev = (dev_t)(str_checksum(server) ^ str_checksum(share));
878 srv->no_pathinfo = False;
879 srv->no_pathinfo2 = False;
880 srv->no_nt_session = False;
882 /* now add it to the cache (internal or external) */
883 /* Let the cache function set errno if it wants to */
884 errno = 0;
885 if ((context->callbacks.add_cached_srv_fn)(context, srv,
886 server, share,
887 workgroup, username)) {
888 int saved_errno = errno;
889 DEBUG(3, (" Failed to add server to cache\n"));
890 errno = saved_errno;
891 if (errno == 0) {
892 errno = ENOMEM;
894 goto failed;
897 DEBUG(2, ("Server connect ok: //%s/%s: %p\n",
898 server, share, srv));
900 DLIST_ADD(context->internal->_servers, srv);
901 return srv;
903 failed:
904 cli_shutdown(c);
905 if (!srv) {
906 return NULL;
909 SAFE_FREE(srv);
910 return NULL;
914 * Connect to a server for getting/setting attributes, possibly on an existing
915 * connection. This works similarly to smbc_server().
917 static SMBCSRV *
918 smbc_attr_server(SMBCCTX *context,
919 const char *server,
920 const char *share,
921 fstring workgroup,
922 fstring username,
923 fstring password,
924 POLICY_HND *pol)
926 int flags;
927 struct in_addr ip;
928 struct cli_state *ipc_cli;
929 struct rpc_pipe_client *pipe_hnd;
930 NTSTATUS nt_status;
931 SMBCSRV *ipc_srv=NULL;
934 * See if we've already created this special connection. Reference
935 * our "special" share name '*IPC$', which is an impossible real share
936 * name due to the leading asterisk.
938 ipc_srv = find_server(context, server, "*IPC$",
939 workgroup, username, password);
940 if (!ipc_srv) {
942 /* We didn't find a cached connection. Get the password */
943 if (*password == '\0') {
944 /* ... then retrieve it now. */
945 if (context->internal->_auth_fn_with_context != NULL) {
946 (context->internal->_auth_fn_with_context)(
947 context,
948 server, share,
949 workgroup, sizeof(fstring),
950 username, sizeof(fstring),
951 password, sizeof(fstring));
952 } else {
953 (context->callbacks.auth_fn)(
954 server, share,
955 workgroup, sizeof(fstring),
956 username, sizeof(fstring),
957 password, sizeof(fstring));
961 flags = 0;
962 if (context->flags & SMB_CTX_FLAG_USE_KERBEROS) {
963 flags |= CLI_FULL_CONNECTION_USE_KERBEROS;
966 zero_ip(&ip);
967 nt_status = cli_full_connection(&ipc_cli,
968 global_myname(), server,
969 &ip, 0, "IPC$", "?????",
970 username, workgroup,
971 password, flags,
972 Undefined, NULL);
973 if (! NT_STATUS_IS_OK(nt_status)) {
974 DEBUG(1,("cli_full_connection failed! (%s)\n",
975 nt_errstr(nt_status)));
976 errno = ENOTSUP;
977 return NULL;
980 ipc_srv = SMB_MALLOC_P(SMBCSRV);
981 if (!ipc_srv) {
982 errno = ENOMEM;
983 cli_shutdown(ipc_cli);
984 return NULL;
987 ZERO_STRUCTP(ipc_srv);
988 ipc_srv->cli = ipc_cli;
990 if (pol) {
991 pipe_hnd = cli_rpc_pipe_open_noauth(ipc_srv->cli,
992 PI_LSARPC,
993 &nt_status);
994 if (!pipe_hnd) {
995 DEBUG(1, ("cli_nt_session_open fail!\n"));
996 errno = ENOTSUP;
997 cli_shutdown(ipc_srv->cli);
998 free(ipc_srv);
999 return NULL;
1003 * Some systems don't support
1004 * SEC_RIGHTS_MAXIMUM_ALLOWED, but NT sends 0x2000000
1005 * so we might as well do it too.
1008 nt_status = rpccli_lsa_open_policy(
1009 pipe_hnd,
1010 ipc_srv->cli->mem_ctx,
1011 True,
1012 GENERIC_EXECUTE_ACCESS,
1013 pol);
1015 if (!NT_STATUS_IS_OK(nt_status)) {
1016 errno = smbc_errno(context, ipc_srv->cli);
1017 cli_shutdown(ipc_srv->cli);
1018 return NULL;
1022 /* now add it to the cache (internal or external) */
1024 errno = 0; /* let cache function set errno if it likes */
1025 if ((context->callbacks.add_cached_srv_fn)(context, ipc_srv,
1026 server,
1027 "*IPC$",
1028 workgroup,
1029 username)) {
1030 DEBUG(3, (" Failed to add server to cache\n"));
1031 if (errno == 0) {
1032 errno = ENOMEM;
1034 cli_shutdown(ipc_srv->cli);
1035 free(ipc_srv);
1036 return NULL;
1039 DLIST_ADD(context->internal->_servers, ipc_srv);
1042 return ipc_srv;
1046 * Routine to open() a file ...
1049 static SMBCFILE *
1050 smbc_open_ctx(SMBCCTX *context,
1051 const char *fname,
1052 int flags,
1053 mode_t mode)
1055 fstring server, share, user, password, workgroup;
1056 pstring path;
1057 pstring targetpath;
1058 struct cli_state *targetcli;
1059 SMBCSRV *srv = NULL;
1060 SMBCFILE *file = NULL;
1061 int fd;
1063 if (!context || !context->internal ||
1064 !context->internal->_initialized) {
1066 errno = EINVAL; /* Best I can think of ... */
1067 return NULL;
1071 if (!fname) {
1073 errno = EINVAL;
1074 return NULL;
1078 if (smbc_parse_path(context, fname,
1079 workgroup, sizeof(workgroup),
1080 server, sizeof(server),
1081 share, sizeof(share),
1082 path, sizeof(path),
1083 user, sizeof(user),
1084 password, sizeof(password),
1085 NULL, 0)) {
1086 errno = EINVAL;
1087 return NULL;
1090 if (user[0] == (char)0) fstrcpy(user, context->user);
1092 srv = smbc_server(context, True,
1093 server, share, workgroup, user, password);
1095 if (!srv) {
1097 if (errno == EPERM) errno = EACCES;
1098 return NULL; /* smbc_server sets errno */
1102 /* Hmmm, the test for a directory is suspect here ... FIXME */
1104 if (strlen(path) > 0 && path[strlen(path) - 1] == '\\') {
1106 fd = -1;
1109 else {
1111 file = SMB_MALLOC_P(SMBCFILE);
1113 if (!file) {
1115 errno = ENOMEM;
1116 return NULL;
1120 ZERO_STRUCTP(file);
1122 /*d_printf(">>>open: resolving %s\n", path);*/
1123 if (!cli_resolve_path( "", srv->cli, path, &targetcli, targetpath))
1125 d_printf("Could not resolve %s\n", path);
1126 SAFE_FREE(file);
1127 return NULL;
1129 /*d_printf(">>>open: resolved %s as %s\n", path, targetpath);*/
1131 if ((fd = cli_open(targetcli, targetpath, flags,
1132 context->internal->_share_mode)) < 0) {
1134 /* Handle the error ... */
1136 SAFE_FREE(file);
1137 errno = smbc_errno(context, targetcli);
1138 return NULL;
1142 /* Fill in file struct */
1144 file->cli_fd = fd;
1145 file->fname = SMB_STRDUP(fname);
1146 file->srv = srv;
1147 file->offset = 0;
1148 file->file = True;
1150 DLIST_ADD(context->internal->_files, file);
1153 * If the file was opened in O_APPEND mode, all write
1154 * operations should be appended to the file. To do that,
1155 * though, using this protocol, would require a getattrE()
1156 * call for each and every write, to determine where the end
1157 * of the file is. (There does not appear to be an append flag
1158 * in the protocol.) Rather than add all of that overhead of
1159 * retrieving the current end-of-file offset prior to each
1160 * write operation, we'll assume that most append operations
1161 * will continuously write, so we'll just set the offset to
1162 * the end of the file now and hope that's adequate.
1164 * Note to self: If this proves inadequate, and O_APPEND
1165 * should, in some cases, be forced for each write, add a
1166 * field in the context options structure, for
1167 * "strict_append_mode" which would select between the current
1168 * behavior (if FALSE) or issuing a getattrE() prior to each
1169 * write and forcing the write to the end of the file (if
1170 * TRUE). Adding that capability will likely require adding
1171 * an "append" flag into the _SMBCFILE structure to track
1172 * whether a file was opened in O_APPEND mode. -- djl
1174 if (flags & O_APPEND) {
1175 if (smbc_lseek_ctx(context, file, 0, SEEK_END) < 0) {
1176 (void) smbc_close_ctx(context, file);
1177 errno = ENXIO;
1178 return NULL;
1182 return file;
1186 /* Check if opendir needed ... */
1188 if (fd == -1) {
1189 int eno = 0;
1191 eno = smbc_errno(context, srv->cli);
1192 file = (context->opendir)(context, fname);
1193 if (!file) errno = eno;
1194 return file;
1198 errno = EINVAL; /* FIXME, correct errno ? */
1199 return NULL;
1204 * Routine to create a file
1207 static int creat_bits = O_WRONLY | O_CREAT | O_TRUNC; /* FIXME: Do we need this */
1209 static SMBCFILE *
1210 smbc_creat_ctx(SMBCCTX *context,
1211 const char *path,
1212 mode_t mode)
1215 if (!context || !context->internal ||
1216 !context->internal->_initialized) {
1218 errno = EINVAL;
1219 return NULL;
1223 return smbc_open_ctx(context, path, creat_bits, mode);
1227 * Routine to read() a file ...
1230 static ssize_t
1231 smbc_read_ctx(SMBCCTX *context,
1232 SMBCFILE *file,
1233 void *buf,
1234 size_t count)
1236 int ret;
1237 fstring server, share, user, password;
1238 pstring path, targetpath;
1239 struct cli_state *targetcli;
1242 * offset:
1244 * Compiler bug (possibly) -- gcc (GCC) 3.3.5 (Debian 1:3.3.5-2) --
1245 * appears to pass file->offset (which is type off_t) differently than
1246 * a local variable of type off_t. Using local variable "offset" in
1247 * the call to cli_read() instead of file->offset fixes a problem
1248 * retrieving data at an offset greater than 4GB.
1250 off_t offset;
1252 if (!context || !context->internal ||
1253 !context->internal->_initialized) {
1255 errno = EINVAL;
1256 return -1;
1260 DEBUG(4, ("smbc_read(%p, %d)\n", file, (int)count));
1262 if (!file || !DLIST_CONTAINS(context->internal->_files, file)) {
1264 errno = EBADF;
1265 return -1;
1269 offset = file->offset;
1271 /* Check that the buffer exists ... */
1273 if (buf == NULL) {
1275 errno = EINVAL;
1276 return -1;
1280 /*d_printf(">>>read: parsing %s\n", file->fname);*/
1281 if (smbc_parse_path(context, file->fname,
1282 NULL, 0,
1283 server, sizeof(server),
1284 share, sizeof(share),
1285 path, sizeof(path),
1286 user, sizeof(user),
1287 password, sizeof(password),
1288 NULL, 0)) {
1289 errno = EINVAL;
1290 return -1;
1293 /*d_printf(">>>read: resolving %s\n", path);*/
1294 if (!cli_resolve_path("", file->srv->cli, path,
1295 &targetcli, targetpath))
1297 d_printf("Could not resolve %s\n", path);
1298 return -1;
1300 /*d_printf(">>>fstat: resolved path as %s\n", targetpath);*/
1302 ret = cli_read(targetcli, file->cli_fd, (char *)buf, offset, count);
1304 if (ret < 0) {
1306 errno = smbc_errno(context, targetcli);
1307 return -1;
1311 file->offset += ret;
1313 DEBUG(4, (" --> %d\n", ret));
1315 return ret; /* Success, ret bytes of data ... */
1320 * Routine to write() a file ...
1323 static ssize_t
1324 smbc_write_ctx(SMBCCTX *context,
1325 SMBCFILE *file,
1326 void *buf,
1327 size_t count)
1329 int ret;
1330 off_t offset;
1331 fstring server, share, user, password;
1332 pstring path, targetpath;
1333 struct cli_state *targetcli;
1335 /* First check all pointers before dereferencing them */
1337 if (!context || !context->internal ||
1338 !context->internal->_initialized) {
1340 errno = EINVAL;
1341 return -1;
1345 if (!file || !DLIST_CONTAINS(context->internal->_files, file)) {
1347 errno = EBADF;
1348 return -1;
1352 /* Check that the buffer exists ... */
1354 if (buf == NULL) {
1356 errno = EINVAL;
1357 return -1;
1361 offset = file->offset; /* See "offset" comment in smbc_read_ctx() */
1363 /*d_printf(">>>write: parsing %s\n", file->fname);*/
1364 if (smbc_parse_path(context, file->fname,
1365 NULL, 0,
1366 server, sizeof(server),
1367 share, sizeof(share),
1368 path, sizeof(path),
1369 user, sizeof(user),
1370 password, sizeof(password),
1371 NULL, 0)) {
1372 errno = EINVAL;
1373 return -1;
1376 /*d_printf(">>>write: resolving %s\n", path);*/
1377 if (!cli_resolve_path("", file->srv->cli, path,
1378 &targetcli, targetpath))
1380 d_printf("Could not resolve %s\n", path);
1381 return -1;
1383 /*d_printf(">>>write: resolved path as %s\n", targetpath);*/
1386 ret = cli_write(targetcli, file->cli_fd, 0, (char *)buf, offset, count);
1388 if (ret <= 0) {
1390 errno = smbc_errno(context, targetcli);
1391 return -1;
1395 file->offset += ret;
1397 return ret; /* Success, 0 bytes of data ... */
1401 * Routine to close() a file ...
1404 static int
1405 smbc_close_ctx(SMBCCTX *context,
1406 SMBCFILE *file)
1408 SMBCSRV *srv;
1409 fstring server, share, user, password;
1410 pstring path, targetpath;
1411 struct cli_state *targetcli;
1413 if (!context || !context->internal ||
1414 !context->internal->_initialized) {
1416 errno = EINVAL;
1417 return -1;
1421 if (!file || !DLIST_CONTAINS(context->internal->_files, file)) {
1423 errno = EBADF;
1424 return -1;
1428 /* IS a dir ... */
1429 if (!file->file) {
1431 return (context->closedir)(context, file);
1435 /*d_printf(">>>close: parsing %s\n", file->fname);*/
1436 if (smbc_parse_path(context, file->fname,
1437 NULL, 0,
1438 server, sizeof(server),
1439 share, sizeof(share),
1440 path, sizeof(path),
1441 user, sizeof(user),
1442 password, sizeof(password),
1443 NULL, 0)) {
1444 errno = EINVAL;
1445 return -1;
1448 /*d_printf(">>>close: resolving %s\n", path);*/
1449 if (!cli_resolve_path("", file->srv->cli, path,
1450 &targetcli, targetpath))
1452 d_printf("Could not resolve %s\n", path);
1453 return -1;
1455 /*d_printf(">>>close: resolved path as %s\n", targetpath);*/
1457 if (!cli_close(targetcli, file->cli_fd)) {
1459 DEBUG(3, ("cli_close failed on %s. purging server.\n",
1460 file->fname));
1461 /* Deallocate slot and remove the server
1462 * from the server cache if unused */
1463 errno = smbc_errno(context, targetcli);
1464 srv = file->srv;
1465 DLIST_REMOVE(context->internal->_files, file);
1466 SAFE_FREE(file->fname);
1467 SAFE_FREE(file);
1468 (context->callbacks.remove_unused_server_fn)(context, srv);
1470 return -1;
1474 DLIST_REMOVE(context->internal->_files, file);
1475 SAFE_FREE(file->fname);
1476 SAFE_FREE(file);
1478 return 0;
1482 * Get info from an SMB server on a file. Use a qpathinfo call first
1483 * and if that fails, use getatr, as Win95 sometimes refuses qpathinfo
1485 static BOOL
1486 smbc_getatr(SMBCCTX * context,
1487 SMBCSRV *srv,
1488 char *path,
1489 uint16 *mode,
1490 SMB_OFF_T *size,
1491 struct timespec *create_time_ts,
1492 struct timespec *access_time_ts,
1493 struct timespec *write_time_ts,
1494 struct timespec *change_time_ts,
1495 SMB_INO_T *ino)
1497 pstring fixedpath;
1498 pstring targetpath;
1499 struct cli_state *targetcli;
1500 time_t write_time;
1502 if (!context || !context->internal ||
1503 !context->internal->_initialized) {
1505 errno = EINVAL;
1506 return -1;
1510 /* path fixup for . and .. */
1511 if (strequal(path, ".") || strequal(path, ".."))
1512 pstrcpy(fixedpath, "\\");
1513 else
1515 pstrcpy(fixedpath, path);
1516 trim_string(fixedpath, NULL, "\\..");
1517 trim_string(fixedpath, NULL, "\\.");
1519 DEBUG(4,("smbc_getatr: sending qpathinfo\n"));
1521 if (!cli_resolve_path( "", srv->cli, fixedpath, &targetcli, targetpath))
1523 d_printf("Couldn't resolve %s\n", path);
1524 return False;
1527 if (!srv->no_pathinfo2 &&
1528 cli_qpathinfo2(targetcli, targetpath,
1529 create_time_ts,
1530 access_time_ts,
1531 write_time_ts,
1532 change_time_ts,
1533 size, mode, ino)) {
1534 return True;
1537 /* if this is NT then don't bother with the getatr */
1538 if (targetcli->capabilities & CAP_NT_SMBS) {
1539 errno = EPERM;
1540 return False;
1543 if (cli_getatr(targetcli, targetpath, mode, size, &write_time)) {
1545 struct timespec w_time_ts;
1547 w_time_ts = convert_time_t_to_timespec(write_time);
1549 if (write_time_ts != NULL) {
1550 *write_time_ts = w_time_ts;
1553 if (create_time_ts != NULL) {
1554 *create_time_ts = w_time_ts;
1557 if (access_time_ts != NULL) {
1558 *access_time_ts = w_time_ts;
1561 if (change_time_ts != NULL) {
1562 *change_time_ts = w_time_ts;
1565 srv->no_pathinfo2 = True;
1566 return True;
1569 errno = EPERM;
1570 return False;
1575 * Set file info on an SMB server. Use setpathinfo call first. If that
1576 * fails, use setattrE..
1578 * Access and modification time parameters are always used and must be
1579 * provided. Create time, if zero, will be determined from the actual create
1580 * time of the file. If non-zero, the create time will be set as well.
1582 * "mode" (attributes) parameter may be set to -1 if it is not to be set.
1584 static BOOL
1585 smbc_setatr(SMBCCTX * context, SMBCSRV *srv, char *path,
1586 time_t create_time,
1587 time_t access_time,
1588 time_t write_time,
1589 time_t change_time,
1590 uint16 mode)
1592 int fd;
1593 int ret;
1596 * First, try setpathinfo (if qpathinfo succeeded), for it is the
1597 * modern function for "new code" to be using, and it works given a
1598 * filename rather than requiring that the file be opened to have its
1599 * attributes manipulated.
1601 if (srv->no_pathinfo ||
1602 ! cli_setpathinfo(srv->cli, path,
1603 create_time,
1604 access_time,
1605 write_time,
1606 change_time,
1607 mode)) {
1610 * setpathinfo is not supported; go to plan B.
1612 * cli_setatr() does not work on win98, and it also doesn't
1613 * support setting the access time (only the modification
1614 * time), so in all cases, we open the specified file and use
1615 * cli_setattrE() which should work on all OS versions, and
1616 * supports both times.
1619 /* Don't try {q,set}pathinfo() again, with this server */
1620 srv->no_pathinfo = True;
1622 /* Open the file */
1623 if ((fd = cli_open(srv->cli, path, O_RDWR, DENY_NONE)) < 0) {
1625 errno = smbc_errno(context, srv->cli);
1626 return -1;
1629 /* Set the new attributes */
1630 ret = cli_setattrE(srv->cli, fd,
1631 change_time,
1632 access_time,
1633 write_time);
1635 /* Close the file */
1636 cli_close(srv->cli, fd);
1639 * Unfortunately, setattrE() doesn't have a provision for
1640 * setting the access mode (attributes). We'll have to try
1641 * cli_setatr() for that, and with only this parameter, it
1642 * seems to work on win98.
1644 if (ret && mode != (uint16) -1) {
1645 ret = cli_setatr(srv->cli, path, mode, 0);
1648 if (! ret) {
1649 errno = smbc_errno(context, srv->cli);
1650 return False;
1654 return True;
1658 * Routine to unlink() a file
1661 static int
1662 smbc_unlink_ctx(SMBCCTX *context,
1663 const char *fname)
1665 fstring server, share, user, password, workgroup;
1666 pstring path, targetpath;
1667 struct cli_state *targetcli;
1668 SMBCSRV *srv = NULL;
1670 if (!context || !context->internal ||
1671 !context->internal->_initialized) {
1673 errno = EINVAL; /* Best I can think of ... */
1674 return -1;
1678 if (!fname) {
1680 errno = EINVAL;
1681 return -1;
1685 if (smbc_parse_path(context, fname,
1686 workgroup, sizeof(workgroup),
1687 server, sizeof(server),
1688 share, sizeof(share),
1689 path, sizeof(path),
1690 user, sizeof(user),
1691 password, sizeof(password),
1692 NULL, 0)) {
1693 errno = EINVAL;
1694 return -1;
1697 if (user[0] == (char)0) fstrcpy(user, context->user);
1699 srv = smbc_server(context, True,
1700 server, share, workgroup, user, password);
1702 if (!srv) {
1704 return -1; /* smbc_server sets errno */
1708 /*d_printf(">>>unlink: resolving %s\n", path);*/
1709 if (!cli_resolve_path( "", srv->cli, path, &targetcli, targetpath))
1711 d_printf("Could not resolve %s\n", path);
1712 return -1;
1714 /*d_printf(">>>unlink: resolved path as %s\n", targetpath);*/
1716 if (!cli_unlink(targetcli, targetpath)) {
1718 errno = smbc_errno(context, targetcli);
1720 if (errno == EACCES) { /* Check if the file is a directory */
1722 int saverr = errno;
1723 SMB_OFF_T size = 0;
1724 uint16 mode = 0;
1725 struct timespec write_time_ts;
1726 struct timespec access_time_ts;
1727 struct timespec change_time_ts;
1728 SMB_INO_T ino = 0;
1730 if (!smbc_getatr(context, srv, path, &mode, &size,
1731 NULL,
1732 &access_time_ts,
1733 &write_time_ts,
1734 &change_time_ts,
1735 &ino)) {
1737 /* Hmmm, bad error ... What? */
1739 errno = smbc_errno(context, targetcli);
1740 return -1;
1743 else {
1745 if (IS_DOS_DIR(mode))
1746 errno = EISDIR;
1747 else
1748 errno = saverr; /* Restore this */
1753 return -1;
1757 return 0; /* Success ... */
1762 * Routine to rename() a file
1765 static int
1766 smbc_rename_ctx(SMBCCTX *ocontext,
1767 const char *oname,
1768 SMBCCTX *ncontext,
1769 const char *nname)
1771 fstring server1;
1772 fstring share1;
1773 fstring server2;
1774 fstring share2;
1775 fstring user1;
1776 fstring user2;
1777 fstring password1;
1778 fstring password2;
1779 fstring workgroup;
1780 pstring path1;
1781 pstring path2;
1782 pstring targetpath1;
1783 pstring targetpath2;
1784 struct cli_state *targetcli1;
1785 struct cli_state *targetcli2;
1786 SMBCSRV *srv = NULL;
1788 if (!ocontext || !ncontext ||
1789 !ocontext->internal || !ncontext->internal ||
1790 !ocontext->internal->_initialized ||
1791 !ncontext->internal->_initialized) {
1793 errno = EINVAL; /* Best I can think of ... */
1794 return -1;
1798 if (!oname || !nname) {
1800 errno = EINVAL;
1801 return -1;
1805 DEBUG(4, ("smbc_rename(%s,%s)\n", oname, nname));
1807 smbc_parse_path(ocontext, oname,
1808 workgroup, sizeof(workgroup),
1809 server1, sizeof(server1),
1810 share1, sizeof(share1),
1811 path1, sizeof(path1),
1812 user1, sizeof(user1),
1813 password1, sizeof(password1),
1814 NULL, 0);
1816 if (user1[0] == (char)0) fstrcpy(user1, ocontext->user);
1818 smbc_parse_path(ncontext, nname,
1819 NULL, 0,
1820 server2, sizeof(server2),
1821 share2, sizeof(share2),
1822 path2, sizeof(path2),
1823 user2, sizeof(user2),
1824 password2, sizeof(password2),
1825 NULL, 0);
1827 if (user2[0] == (char)0) fstrcpy(user2, ncontext->user);
1829 if (strcmp(server1, server2) || strcmp(share1, share2) ||
1830 strcmp(user1, user2)) {
1832 /* Can't rename across file systems, or users?? */
1834 errno = EXDEV;
1835 return -1;
1839 srv = smbc_server(ocontext, True,
1840 server1, share1, workgroup, user1, password1);
1841 if (!srv) {
1843 return -1;
1847 /*d_printf(">>>rename: resolving %s\n", path1);*/
1848 if (!cli_resolve_path( "", srv->cli, path1, &targetcli1, targetpath1))
1850 d_printf("Could not resolve %s\n", path1);
1851 return -1;
1853 /*d_printf(">>>rename: resolved path as %s\n", targetpath1);*/
1854 /*d_printf(">>>rename: resolving %s\n", path2);*/
1855 if (!cli_resolve_path( "", srv->cli, path2, &targetcli2, targetpath2))
1857 d_printf("Could not resolve %s\n", path2);
1858 return -1;
1860 /*d_printf(">>>rename: resolved path as %s\n", targetpath2);*/
1862 if (strcmp(targetcli1->desthost, targetcli2->desthost) ||
1863 strcmp(targetcli1->share, targetcli2->share))
1865 /* can't rename across file systems */
1867 errno = EXDEV;
1868 return -1;
1871 if (!cli_rename(targetcli1, targetpath1, targetpath2)) {
1872 int eno = smbc_errno(ocontext, targetcli1);
1874 if (eno != EEXIST ||
1875 !cli_unlink(targetcli1, targetpath2) ||
1876 !cli_rename(targetcli1, targetpath1, targetpath2)) {
1878 errno = eno;
1879 return -1;
1884 return 0; /* Success */
1889 * A routine to lseek() a file
1892 static off_t
1893 smbc_lseek_ctx(SMBCCTX *context,
1894 SMBCFILE *file,
1895 off_t offset,
1896 int whence)
1898 SMB_OFF_T size;
1899 fstring server, share, user, password;
1900 pstring path, targetpath;
1901 struct cli_state *targetcli;
1903 if (!context || !context->internal ||
1904 !context->internal->_initialized) {
1906 errno = EINVAL;
1907 return -1;
1911 if (!file || !DLIST_CONTAINS(context->internal->_files, file)) {
1913 errno = EBADF;
1914 return -1;
1918 if (!file->file) {
1920 errno = EINVAL;
1921 return -1; /* Can't lseek a dir ... */
1925 switch (whence) {
1926 case SEEK_SET:
1927 file->offset = offset;
1928 break;
1930 case SEEK_CUR:
1931 file->offset += offset;
1932 break;
1934 case SEEK_END:
1935 /*d_printf(">>>lseek: parsing %s\n", file->fname);*/
1936 if (smbc_parse_path(context, file->fname,
1937 NULL, 0,
1938 server, sizeof(server),
1939 share, sizeof(share),
1940 path, sizeof(path),
1941 user, sizeof(user),
1942 password, sizeof(password),
1943 NULL, 0)) {
1945 errno = EINVAL;
1946 return -1;
1949 /*d_printf(">>>lseek: resolving %s\n", path);*/
1950 if (!cli_resolve_path("", file->srv->cli, path,
1951 &targetcli, targetpath))
1953 d_printf("Could not resolve %s\n", path);
1954 return -1;
1956 /*d_printf(">>>lseek: resolved path as %s\n", targetpath);*/
1958 if (!cli_qfileinfo(targetcli, file->cli_fd, NULL,
1959 &size, NULL, NULL, NULL, NULL, NULL))
1961 SMB_OFF_T b_size = size;
1962 if (!cli_getattrE(targetcli, file->cli_fd,
1963 NULL, &b_size, NULL, NULL, NULL))
1965 errno = EINVAL;
1966 return -1;
1967 } else
1968 size = b_size;
1970 file->offset = size + offset;
1971 break;
1973 default:
1974 errno = EINVAL;
1975 break;
1979 return file->offset;
1984 * Generate an inode number from file name for those things that need it
1987 static ino_t
1988 smbc_inode(SMBCCTX *context,
1989 const char *name)
1992 if (!context || !context->internal ||
1993 !context->internal->_initialized) {
1995 errno = EINVAL;
1996 return -1;
2000 if (!*name) return 2; /* FIXME, why 2 ??? */
2001 return (ino_t)str_checksum(name);
2006 * Routine to put basic stat info into a stat structure ... Used by stat and
2007 * fstat below.
2010 static int
2011 smbc_setup_stat(SMBCCTX *context,
2012 struct stat *st,
2013 char *fname,
2014 SMB_OFF_T size,
2015 int mode)
2018 st->st_mode = 0;
2020 if (IS_DOS_DIR(mode)) {
2021 st->st_mode = SMBC_DIR_MODE;
2022 } else {
2023 st->st_mode = SMBC_FILE_MODE;
2026 if (IS_DOS_ARCHIVE(mode)) st->st_mode |= S_IXUSR;
2027 if (IS_DOS_SYSTEM(mode)) st->st_mode |= S_IXGRP;
2028 if (IS_DOS_HIDDEN(mode)) st->st_mode |= S_IXOTH;
2029 if (!IS_DOS_READONLY(mode)) st->st_mode |= S_IWUSR;
2031 st->st_size = size;
2032 #ifdef HAVE_STAT_ST_BLKSIZE
2033 st->st_blksize = 512;
2034 #endif
2035 #ifdef HAVE_STAT_ST_BLOCKS
2036 st->st_blocks = (size+511)/512;
2037 #endif
2038 st->st_uid = getuid();
2039 st->st_gid = getgid();
2041 if (IS_DOS_DIR(mode)) {
2042 st->st_nlink = 2;
2043 } else {
2044 st->st_nlink = 1;
2047 if (st->st_ino == 0) {
2048 st->st_ino = smbc_inode(context, fname);
2051 return True; /* FIXME: Is this needed ? */
2056 * Routine to stat a file given a name
2059 static int
2060 smbc_stat_ctx(SMBCCTX *context,
2061 const char *fname,
2062 struct stat *st)
2064 SMBCSRV *srv;
2065 fstring server;
2066 fstring share;
2067 fstring user;
2068 fstring password;
2069 fstring workgroup;
2070 pstring path;
2071 struct timespec write_time_ts;
2072 struct timespec access_time_ts;
2073 struct timespec change_time_ts;
2074 SMB_OFF_T size = 0;
2075 uint16 mode = 0;
2076 SMB_INO_T ino = 0;
2078 if (!context || !context->internal ||
2079 !context->internal->_initialized) {
2081 errno = EINVAL; /* Best I can think of ... */
2082 return -1;
2086 if (!fname) {
2088 errno = EINVAL;
2089 return -1;
2093 DEBUG(4, ("smbc_stat(%s)\n", fname));
2095 if (smbc_parse_path(context, fname,
2096 workgroup, sizeof(workgroup),
2097 server, sizeof(server),
2098 share, sizeof(share),
2099 path, sizeof(path),
2100 user, sizeof(user),
2101 password, sizeof(password),
2102 NULL, 0)) {
2103 errno = EINVAL;
2104 return -1;
2107 if (user[0] == (char)0) fstrcpy(user, context->user);
2109 srv = smbc_server(context, True,
2110 server, share, workgroup, user, password);
2112 if (!srv) {
2113 return -1; /* errno set by smbc_server */
2116 if (!smbc_getatr(context, srv, path, &mode, &size,
2117 NULL,
2118 &access_time_ts,
2119 &write_time_ts,
2120 &change_time_ts,
2121 &ino)) {
2123 errno = smbc_errno(context, srv->cli);
2124 return -1;
2128 st->st_ino = ino;
2130 smbc_setup_stat(context, st, path, size, mode);
2132 set_atimespec(st, access_time_ts);
2133 set_ctimespec(st, change_time_ts);
2134 set_mtimespec(st, write_time_ts);
2135 st->st_dev = srv->dev;
2137 return 0;
2142 * Routine to stat a file given an fd
2145 static int
2146 smbc_fstat_ctx(SMBCCTX *context,
2147 SMBCFILE *file,
2148 struct stat *st)
2150 struct timespec change_time_ts;
2151 struct timespec access_time_ts;
2152 struct timespec write_time_ts;
2153 SMB_OFF_T size;
2154 uint16 mode;
2155 fstring server;
2156 fstring share;
2157 fstring user;
2158 fstring password;
2159 pstring path;
2160 pstring targetpath;
2161 struct cli_state *targetcli;
2162 SMB_INO_T ino = 0;
2164 if (!context || !context->internal ||
2165 !context->internal->_initialized) {
2167 errno = EINVAL;
2168 return -1;
2172 if (!file || !DLIST_CONTAINS(context->internal->_files, file)) {
2174 errno = EBADF;
2175 return -1;
2179 if (!file->file) {
2181 return (context->fstatdir)(context, file, st);
2185 /*d_printf(">>>fstat: parsing %s\n", file->fname);*/
2186 if (smbc_parse_path(context, file->fname,
2187 NULL, 0,
2188 server, sizeof(server),
2189 share, sizeof(share),
2190 path, sizeof(path),
2191 user, sizeof(user),
2192 password, sizeof(password),
2193 NULL, 0)) {
2194 errno = EINVAL;
2195 return -1;
2198 /*d_printf(">>>fstat: resolving %s\n", path);*/
2199 if (!cli_resolve_path("", file->srv->cli, path,
2200 &targetcli, targetpath))
2202 d_printf("Could not resolve %s\n", path);
2203 return -1;
2205 /*d_printf(">>>fstat: resolved path as %s\n", targetpath);*/
2207 if (!cli_qfileinfo(targetcli, file->cli_fd, &mode, &size,
2208 NULL,
2209 &access_time_ts,
2210 &write_time_ts,
2211 &change_time_ts,
2212 &ino)) {
2214 time_t change_time, access_time, write_time;
2216 if (!cli_getattrE(targetcli, file->cli_fd, &mode, &size,
2217 &change_time, &access_time, &write_time)) {
2219 errno = EINVAL;
2220 return -1;
2223 change_time_ts = convert_time_t_to_timespec(change_time);
2224 access_time_ts = convert_time_t_to_timespec(access_time);
2225 write_time_ts = convert_time_t_to_timespec(write_time);
2228 st->st_ino = ino;
2230 smbc_setup_stat(context, st, file->fname, size, mode);
2232 set_atimespec(st, access_time_ts);
2233 set_ctimespec(st, change_time_ts);
2234 set_mtimespec(st, write_time_ts);
2235 st->st_dev = file->srv->dev;
2237 return 0;
2242 * Routine to open a directory
2243 * We accept the URL syntax explained in smbc_parse_path(), above.
2246 static void
2247 smbc_remove_dir(SMBCFILE *dir)
2249 struct smbc_dir_list *d,*f;
2251 d = dir->dir_list;
2252 while (d) {
2254 f = d; d = d->next;
2256 SAFE_FREE(f->dirent);
2257 SAFE_FREE(f);
2261 dir->dir_list = dir->dir_end = dir->dir_next = NULL;
2265 static int
2266 add_dirent(SMBCFILE *dir,
2267 const char *name,
2268 const char *comment,
2269 uint32 type)
2271 struct smbc_dirent *dirent;
2272 int size;
2273 int name_length = (name == NULL ? 0 : strlen(name));
2274 int comment_len = (comment == NULL ? 0 : strlen(comment));
2277 * Allocate space for the dirent, which must be increased by the
2278 * size of the name and the comment and 1 each for the null terminator.
2281 size = sizeof(struct smbc_dirent) + name_length + comment_len + 2;
2283 dirent = (struct smbc_dirent *)SMB_MALLOC(size);
2285 if (!dirent) {
2287 dir->dir_error = ENOMEM;
2288 return -1;
2292 ZERO_STRUCTP(dirent);
2294 if (dir->dir_list == NULL) {
2296 dir->dir_list = SMB_MALLOC_P(struct smbc_dir_list);
2297 if (!dir->dir_list) {
2299 SAFE_FREE(dirent);
2300 dir->dir_error = ENOMEM;
2301 return -1;
2304 ZERO_STRUCTP(dir->dir_list);
2306 dir->dir_end = dir->dir_next = dir->dir_list;
2308 else {
2310 dir->dir_end->next = SMB_MALLOC_P(struct smbc_dir_list);
2312 if (!dir->dir_end->next) {
2314 SAFE_FREE(dirent);
2315 dir->dir_error = ENOMEM;
2316 return -1;
2319 ZERO_STRUCTP(dir->dir_end->next);
2321 dir->dir_end = dir->dir_end->next;
2324 dir->dir_end->next = NULL;
2325 dir->dir_end->dirent = dirent;
2327 dirent->smbc_type = type;
2328 dirent->namelen = name_length;
2329 dirent->commentlen = comment_len;
2330 dirent->dirlen = size;
2333 * dirent->namelen + 1 includes the null (no null termination needed)
2334 * Ditto for dirent->commentlen.
2335 * The space for the two null bytes was allocated.
2337 strncpy(dirent->name, (name?name:""), dirent->namelen + 1);
2338 dirent->comment = (char *)(&dirent->name + dirent->namelen + 1);
2339 strncpy(dirent->comment, (comment?comment:""), dirent->commentlen + 1);
2341 return 0;
2345 static void
2346 list_unique_wg_fn(const char *name,
2347 uint32 type,
2348 const char *comment,
2349 void *state)
2351 SMBCFILE *dir = (SMBCFILE *)state;
2352 struct smbc_dir_list *dir_list;
2353 struct smbc_dirent *dirent;
2354 int dirent_type;
2355 int do_remove = 0;
2357 dirent_type = dir->dir_type;
2359 if (add_dirent(dir, name, comment, dirent_type) < 0) {
2361 /* An error occurred, what do we do? */
2362 /* FIXME: Add some code here */
2365 /* Point to the one just added */
2366 dirent = dir->dir_end->dirent;
2368 /* See if this was a duplicate */
2369 for (dir_list = dir->dir_list;
2370 dir_list != dir->dir_end;
2371 dir_list = dir_list->next) {
2372 if (! do_remove &&
2373 strcmp(dir_list->dirent->name, dirent->name) == 0) {
2374 /* Duplicate. End end of list need to be removed. */
2375 do_remove = 1;
2378 if (do_remove && dir_list->next == dir->dir_end) {
2379 /* Found the end of the list. Remove it. */
2380 dir->dir_end = dir_list;
2381 free(dir_list->next);
2382 free(dirent);
2383 dir_list->next = NULL;
2384 break;
2389 static void
2390 list_fn(const char *name,
2391 uint32 type,
2392 const char *comment,
2393 void *state)
2395 SMBCFILE *dir = (SMBCFILE *)state;
2396 int dirent_type;
2399 * We need to process the type a little ...
2401 * Disk share = 0x00000000
2402 * Print share = 0x00000001
2403 * Comms share = 0x00000002 (obsolete?)
2404 * IPC$ share = 0x00000003
2406 * administrative shares:
2407 * ADMIN$, IPC$, C$, D$, E$ ... are type |= 0x80000000
2410 if (dir->dir_type == SMBC_FILE_SHARE) {
2412 switch (type) {
2413 case 0 | 0x80000000:
2414 case 0:
2415 dirent_type = SMBC_FILE_SHARE;
2416 break;
2418 case 1:
2419 dirent_type = SMBC_PRINTER_SHARE;
2420 break;
2422 case 2:
2423 dirent_type = SMBC_COMMS_SHARE;
2424 break;
2426 case 3 | 0x80000000:
2427 case 3:
2428 dirent_type = SMBC_IPC_SHARE;
2429 break;
2431 default:
2432 dirent_type = SMBC_FILE_SHARE; /* FIXME, error? */
2433 break;
2436 else {
2437 dirent_type = dir->dir_type;
2440 if (add_dirent(dir, name, comment, dirent_type) < 0) {
2442 /* An error occurred, what do we do? */
2443 /* FIXME: Add some code here */
2448 static void
2449 dir_list_fn(const char *mnt,
2450 file_info *finfo,
2451 const char *mask,
2452 void *state)
2455 if (add_dirent((SMBCFILE *)state, finfo->name, "",
2456 (finfo->mode&aDIR?SMBC_DIR:SMBC_FILE)) < 0) {
2458 /* Handle an error ... */
2460 /* FIXME: Add some code ... */
2466 static int
2467 net_share_enum_rpc(struct cli_state *cli,
2468 void (*fn)(const char *name,
2469 uint32 type,
2470 const char *comment,
2471 void *state),
2472 void *state)
2474 int i;
2475 WERROR result;
2476 ENUM_HND enum_hnd;
2477 uint32 info_level = 1;
2478 uint32 preferred_len = 0xffffffff;
2479 uint32 type;
2480 SRV_SHARE_INFO_CTR ctr;
2481 fstring name = "";
2482 fstring comment = "";
2483 void *mem_ctx;
2484 struct rpc_pipe_client *pipe_hnd;
2485 NTSTATUS nt_status;
2487 /* Open the server service pipe */
2488 pipe_hnd = cli_rpc_pipe_open_noauth(cli, PI_SRVSVC, &nt_status);
2489 if (!pipe_hnd) {
2490 DEBUG(1, ("net_share_enum_rpc pipe open fail!\n"));
2491 return -1;
2494 /* Allocate a context for parsing and for the entries in "ctr" */
2495 mem_ctx = talloc_init("libsmbclient: net_share_enum_rpc");
2496 if (mem_ctx == NULL) {
2497 DEBUG(0, ("out of memory for net_share_enum_rpc!\n"));
2498 cli_rpc_pipe_close(pipe_hnd);
2499 return -1;
2502 /* Issue the NetShareEnum RPC call and retrieve the response */
2503 init_enum_hnd(&enum_hnd, 0);
2504 result = rpccli_srvsvc_net_share_enum(pipe_hnd,
2505 mem_ctx,
2506 info_level,
2507 &ctr,
2508 preferred_len,
2509 &enum_hnd);
2511 /* Was it successful? */
2512 if (!W_ERROR_IS_OK(result) || ctr.num_entries == 0) {
2513 /* Nope. Go clean up. */
2514 goto done;
2517 /* For each returned entry... */
2518 for (i = 0; i < ctr.num_entries; i++) {
2520 /* pull out the share name */
2521 rpcstr_pull_unistr2_fstring(
2522 name, &ctr.share.info1[i].info_1_str.uni_netname);
2524 /* pull out the share's comment */
2525 rpcstr_pull_unistr2_fstring(
2526 comment, &ctr.share.info1[i].info_1_str.uni_remark);
2528 /* Get the type value */
2529 type = ctr.share.info1[i].info_1.type;
2531 /* Add this share to the list */
2532 (*fn)(name, type, comment, state);
2535 done:
2536 /* Close the server service pipe */
2537 cli_rpc_pipe_close(pipe_hnd);
2539 /* Free all memory which was allocated for this request */
2540 TALLOC_FREE(mem_ctx);
2542 /* Tell 'em if it worked */
2543 return W_ERROR_IS_OK(result) ? 0 : -1;
2548 static SMBCFILE *
2549 smbc_opendir_ctx(SMBCCTX *context,
2550 const char *fname)
2552 int saved_errno;
2553 fstring server, share, user, password, options;
2554 pstring workgroup;
2555 pstring path;
2556 uint16 mode;
2557 char *p;
2558 SMBCSRV *srv = NULL;
2559 SMBCFILE *dir = NULL;
2560 struct _smbc_callbacks *cb;
2561 struct in_addr rem_ip;
2563 if (!context || !context->internal ||
2564 !context->internal->_initialized) {
2565 DEBUG(4, ("no valid context\n"));
2566 errno = EINVAL + 8192;
2567 return NULL;
2571 if (!fname) {
2572 DEBUG(4, ("no valid fname\n"));
2573 errno = EINVAL + 8193;
2574 return NULL;
2577 if (smbc_parse_path(context, fname,
2578 workgroup, sizeof(workgroup),
2579 server, sizeof(server),
2580 share, sizeof(share),
2581 path, sizeof(path),
2582 user, sizeof(user),
2583 password, sizeof(password),
2584 options, sizeof(options))) {
2585 DEBUG(4, ("no valid path\n"));
2586 errno = EINVAL + 8194;
2587 return NULL;
2590 DEBUG(4, ("parsed path: fname='%s' server='%s' share='%s' "
2591 "path='%s' options='%s'\n",
2592 fname, server, share, path, options));
2594 /* Ensure the options are valid */
2595 if (smbc_check_options(server, share, path, options)) {
2596 DEBUG(4, ("unacceptable options (%s)\n", options));
2597 errno = EINVAL + 8195;
2598 return NULL;
2601 if (user[0] == (char)0) fstrcpy(user, context->user);
2603 dir = SMB_MALLOC_P(SMBCFILE);
2605 if (!dir) {
2607 errno = ENOMEM;
2608 return NULL;
2612 ZERO_STRUCTP(dir);
2614 dir->cli_fd = 0;
2615 dir->fname = SMB_STRDUP(fname);
2616 dir->srv = NULL;
2617 dir->offset = 0;
2618 dir->file = False;
2619 dir->dir_list = dir->dir_next = dir->dir_end = NULL;
2621 if (server[0] == (char)0) {
2623 int i;
2624 int count;
2625 int max_lmb_count;
2626 struct ip_service *ip_list;
2627 struct ip_service server_addr;
2628 struct user_auth_info u_info;
2629 struct cli_state *cli;
2631 if (share[0] != (char)0 || path[0] != (char)0) {
2633 errno = EINVAL + 8196;
2634 if (dir) {
2635 SAFE_FREE(dir->fname);
2636 SAFE_FREE(dir);
2638 return NULL;
2641 /* Determine how many local master browsers to query */
2642 max_lmb_count = (context->options.browse_max_lmb_count == 0
2643 ? INT_MAX
2644 : context->options.browse_max_lmb_count);
2646 pstrcpy(u_info.username, user);
2647 pstrcpy(u_info.password, password);
2650 * We have server and share and path empty but options
2651 * requesting that we scan all master browsers for their list
2652 * of workgroups/domains. This implies that we must first try
2653 * broadcast queries to find all master browsers, and if that
2654 * doesn't work, then try our other methods which return only
2655 * a single master browser.
2658 ip_list = NULL;
2659 if (!name_resolve_bcast(MSBROWSE, 1, &ip_list, &count)) {
2661 SAFE_FREE(ip_list);
2663 if (!find_master_ip(workgroup, &server_addr.ip)) {
2665 if (dir) {
2666 SAFE_FREE(dir->fname);
2667 SAFE_FREE(dir);
2669 errno = ENOENT;
2670 return NULL;
2673 ip_list = &server_addr;
2674 count = 1;
2677 for (i = 0; i < count && i < max_lmb_count; i++) {
2678 DEBUG(99, ("Found master browser %d of %d: %s\n",
2679 i+1, MAX(count, max_lmb_count),
2680 inet_ntoa(ip_list[i].ip)));
2682 cli = get_ipc_connect_master_ip(&ip_list[i],
2683 workgroup, &u_info);
2684 /* cli == NULL is the master browser refused to talk or
2685 could not be found */
2686 if ( !cli )
2687 continue;
2689 fstrcpy(server, cli->desthost);
2690 cli_shutdown(cli);
2692 DEBUG(4, ("using workgroup %s %s\n",
2693 workgroup, server));
2696 * For each returned master browser IP address, get a
2697 * connection to IPC$ on the server if we do not
2698 * already have one, and determine the
2699 * workgroups/domains that it knows about.
2702 srv = smbc_server(context, True, server, "IPC$",
2703 workgroup, user, password);
2704 if (!srv) {
2705 continue;
2708 dir->srv = srv;
2709 dir->dir_type = SMBC_WORKGROUP;
2711 /* Now, list the stuff ... */
2713 if (!cli_NetServerEnum(srv->cli,
2714 workgroup,
2715 SV_TYPE_DOMAIN_ENUM,
2716 list_unique_wg_fn,
2717 (void *)dir)) {
2718 continue;
2722 SAFE_FREE(ip_list);
2723 } else {
2725 * Server not an empty string ... Check the rest and see what
2726 * gives
2728 if (*share == '\0') {
2729 if (*path != '\0') {
2731 /* Should not have empty share with path */
2732 errno = EINVAL + 8197;
2733 if (dir) {
2734 SAFE_FREE(dir->fname);
2735 SAFE_FREE(dir);
2737 return NULL;
2742 * We don't know if <server> is really a server name
2743 * or is a workgroup/domain name. If we already have
2744 * a server structure for it, we'll use it.
2745 * Otherwise, check to see if <server><1D>,
2746 * <server><1B>, or <server><20> translates. We check
2747 * to see if <server> is an IP address first.
2751 * See if we have an existing server. Do not
2752 * establish a connection if one does not already
2753 * exist.
2755 srv = smbc_server(context, False, server, "IPC$",
2756 workgroup, user, password);
2759 * If no existing server and not an IP addr, look for
2760 * LMB or DMB
2762 if (!srv &&
2763 !is_ipaddress(server) &&
2764 (resolve_name(server, &rem_ip, 0x1d) || /* LMB */
2765 resolve_name(server, &rem_ip, 0x1b) )) { /* DMB */
2767 fstring buserver;
2769 dir->dir_type = SMBC_SERVER;
2772 * Get the backup list ...
2774 if (!name_status_find(server, 0, 0,
2775 rem_ip, buserver)) {
2777 DEBUG(0, ("Could not get name of "
2778 "local/domain master browser "
2779 "for server %s\n", server));
2780 if (dir) {
2781 SAFE_FREE(dir->fname);
2782 SAFE_FREE(dir);
2784 errno = EPERM;
2785 return NULL;
2790 * Get a connection to IPC$ on the server if
2791 * we do not already have one
2793 srv = smbc_server(context, True,
2794 buserver, "IPC$",
2795 workgroup, user, password);
2796 if (!srv) {
2797 DEBUG(0, ("got no contact to IPC$\n"));
2798 if (dir) {
2799 SAFE_FREE(dir->fname);
2800 SAFE_FREE(dir);
2802 return NULL;
2806 dir->srv = srv;
2808 /* Now, list the servers ... */
2809 if (!cli_NetServerEnum(srv->cli, server,
2810 0x0000FFFE, list_fn,
2811 (void *)dir)) {
2813 if (dir) {
2814 SAFE_FREE(dir->fname);
2815 SAFE_FREE(dir);
2817 return NULL;
2819 } else if (srv ||
2820 (resolve_name(server, &rem_ip, 0x20))) {
2822 /* If we hadn't found the server, get one now */
2823 if (!srv) {
2824 srv = smbc_server(context, True,
2825 server, "IPC$",
2826 workgroup,
2827 user, password);
2830 if (!srv) {
2831 if (dir) {
2832 SAFE_FREE(dir->fname);
2833 SAFE_FREE(dir);
2835 return NULL;
2839 dir->dir_type = SMBC_FILE_SHARE;
2840 dir->srv = srv;
2842 /* List the shares ... */
2844 if (net_share_enum_rpc(
2845 srv->cli,
2846 list_fn,
2847 (void *) dir) < 0 &&
2848 cli_RNetShareEnum(
2849 srv->cli,
2850 list_fn,
2851 (void *)dir) < 0) {
2853 errno = cli_errno(srv->cli);
2854 if (dir) {
2855 SAFE_FREE(dir->fname);
2856 SAFE_FREE(dir);
2858 return NULL;
2861 } else {
2862 /* Neither the workgroup nor server exists */
2863 errno = ECONNREFUSED;
2864 if (dir) {
2865 SAFE_FREE(dir->fname);
2866 SAFE_FREE(dir);
2868 return NULL;
2872 else {
2874 * The server and share are specified ... work from
2875 * there ...
2877 pstring targetpath;
2878 struct cli_state *targetcli;
2880 /* We connect to the server and list the directory */
2881 dir->dir_type = SMBC_FILE_SHARE;
2883 srv = smbc_server(context, True, server, share,
2884 workgroup, user, password);
2886 if (!srv) {
2888 if (dir) {
2889 SAFE_FREE(dir->fname);
2890 SAFE_FREE(dir);
2892 return NULL;
2896 dir->srv = srv;
2898 /* Now, list the files ... */
2900 p = path + strlen(path);
2901 pstrcat(path, "\\*");
2903 if (!cli_resolve_path("", srv->cli, path,
2904 &targetcli, targetpath))
2906 d_printf("Could not resolve %s\n", path);
2907 if (dir) {
2908 SAFE_FREE(dir->fname);
2909 SAFE_FREE(dir);
2911 return NULL;
2914 if (cli_list(targetcli, targetpath,
2915 aDIR | aSYSTEM | aHIDDEN,
2916 dir_list_fn, (void *)dir) < 0) {
2918 if (dir) {
2919 SAFE_FREE(dir->fname);
2920 SAFE_FREE(dir);
2922 saved_errno = smbc_errno(context, targetcli);
2924 if (saved_errno == EINVAL) {
2926 * See if they asked to opendir something
2927 * other than a directory. If so, the
2928 * converted error value we got would have
2929 * been EINVAL rather than ENOTDIR.
2931 *p = '\0'; /* restore original path */
2933 if (smbc_getatr(context, srv, path,
2934 &mode, NULL,
2935 NULL, NULL, NULL, NULL,
2936 NULL) &&
2937 ! IS_DOS_DIR(mode)) {
2939 /* It is. Correct the error value */
2940 saved_errno = ENOTDIR;
2945 * If there was an error and the server is no
2946 * good any more...
2948 cb = &context->callbacks;
2949 if (cli_is_error(targetcli) &&
2950 (cb->check_server_fn)(context, srv)) {
2952 /* ... then remove it. */
2953 if ((cb->remove_unused_server_fn)(context,
2954 srv)) {
2956 * We could not remove the
2957 * server completely, remove
2958 * it from the cache so we
2959 * will not get it again. It
2960 * will be removed when the
2961 * last file/dir is closed.
2963 (cb->remove_cached_srv_fn)(context,
2964 srv);
2968 errno = saved_errno;
2969 return NULL;
2975 DLIST_ADD(context->internal->_files, dir);
2976 return dir;
2981 * Routine to close a directory
2984 static int
2985 smbc_closedir_ctx(SMBCCTX *context,
2986 SMBCFILE *dir)
2989 if (!context || !context->internal ||
2990 !context->internal->_initialized) {
2992 errno = EINVAL;
2993 return -1;
2997 if (!dir || !DLIST_CONTAINS(context->internal->_files, dir)) {
2999 errno = EBADF;
3000 return -1;
3004 smbc_remove_dir(dir); /* Clean it up */
3006 DLIST_REMOVE(context->internal->_files, dir);
3008 if (dir) {
3010 SAFE_FREE(dir->fname);
3011 SAFE_FREE(dir); /* Free the space too */
3014 return 0;
3018 static void
3019 smbc_readdir_internal(SMBCCTX * context,
3020 struct smbc_dirent *dest,
3021 struct smbc_dirent *src,
3022 int max_namebuf_len)
3024 if (context->options.urlencode_readdir_entries) {
3026 /* url-encode the name. get back remaining buffer space */
3027 max_namebuf_len =
3028 smbc_urlencode(dest->name, src->name, max_namebuf_len);
3030 /* We now know the name length */
3031 dest->namelen = strlen(dest->name);
3033 /* Save the pointer to the beginning of the comment */
3034 dest->comment = dest->name + dest->namelen + 1;
3036 /* Copy the comment */
3037 strncpy(dest->comment, src->comment, max_namebuf_len - 1);
3038 dest->comment[max_namebuf_len - 1] = '\0';
3040 /* Save other fields */
3041 dest->smbc_type = src->smbc_type;
3042 dest->commentlen = strlen(dest->comment);
3043 dest->dirlen = ((dest->comment + dest->commentlen + 1) -
3044 (char *) dest);
3045 } else {
3047 /* No encoding. Just copy the entry as is. */
3048 memcpy(dest, src, src->dirlen);
3049 dest->comment = (char *)(&dest->name + src->namelen + 1);
3055 * Routine to get a directory entry
3058 struct smbc_dirent *
3059 smbc_readdir_ctx(SMBCCTX *context,
3060 SMBCFILE *dir)
3062 int maxlen;
3063 struct smbc_dirent *dirp, *dirent;
3065 /* Check that all is ok first ... */
3067 if (!context || !context->internal ||
3068 !context->internal->_initialized) {
3070 errno = EINVAL;
3071 DEBUG(0, ("Invalid context in smbc_readdir_ctx()\n"));
3072 return NULL;
3076 if (!dir || !DLIST_CONTAINS(context->internal->_files, dir)) {
3078 errno = EBADF;
3079 DEBUG(0, ("Invalid dir in smbc_readdir_ctx()\n"));
3080 return NULL;
3084 if (dir->file != False) { /* FIXME, should be dir, perhaps */
3086 errno = ENOTDIR;
3087 DEBUG(0, ("Found file vs directory in smbc_readdir_ctx()\n"));
3088 return NULL;
3092 if (!dir->dir_next) {
3093 return NULL;
3096 dirent = dir->dir_next->dirent;
3097 if (!dirent) {
3099 errno = ENOENT;
3100 return NULL;
3104 dirp = (struct smbc_dirent *)context->internal->_dirent;
3105 maxlen = (sizeof(context->internal->_dirent) -
3106 sizeof(struct smbc_dirent));
3108 smbc_readdir_internal(context, dirp, dirent, maxlen);
3110 dir->dir_next = dir->dir_next->next;
3112 return dirp;
3116 * Routine to get directory entries
3119 static int
3120 smbc_getdents_ctx(SMBCCTX *context,
3121 SMBCFILE *dir,
3122 struct smbc_dirent *dirp,
3123 int count)
3125 int rem = count;
3126 int reqd;
3127 int maxlen;
3128 char *ndir = (char *)dirp;
3129 struct smbc_dir_list *dirlist;
3131 /* Check that all is ok first ... */
3133 if (!context || !context->internal ||
3134 !context->internal->_initialized) {
3136 errno = EINVAL;
3137 return -1;
3141 if (!dir || !DLIST_CONTAINS(context->internal->_files, dir)) {
3143 errno = EBADF;
3144 return -1;
3148 if (dir->file != False) { /* FIXME, should be dir, perhaps */
3150 errno = ENOTDIR;
3151 return -1;
3156 * Now, retrieve the number of entries that will fit in what was passed
3157 * We have to figure out if the info is in the list, or we need to
3158 * send a request to the server to get the info.
3161 while ((dirlist = dir->dir_next)) {
3162 struct smbc_dirent *dirent;
3164 if (!dirlist->dirent) {
3166 errno = ENOENT; /* Bad error */
3167 return -1;
3171 /* Do urlencoding of next entry, if so selected */
3172 dirent = (struct smbc_dirent *)context->internal->_dirent;
3173 maxlen = (sizeof(context->internal->_dirent) -
3174 sizeof(struct smbc_dirent));
3175 smbc_readdir_internal(context, dirent, dirlist->dirent, maxlen);
3177 reqd = dirent->dirlen;
3179 if (rem < reqd) {
3181 if (rem < count) { /* We managed to copy something */
3183 errno = 0;
3184 return count - rem;
3187 else { /* Nothing copied ... */
3189 errno = EINVAL; /* Not enough space ... */
3190 return -1;
3196 memcpy(ndir, dirent, reqd); /* Copy the data in ... */
3198 ((struct smbc_dirent *)ndir)->comment =
3199 (char *)(&((struct smbc_dirent *)ndir)->name +
3200 dirent->namelen +
3203 ndir += reqd;
3205 rem -= reqd;
3207 dir->dir_next = dirlist = dirlist -> next;
3210 if (rem == count)
3211 return 0;
3212 else
3213 return count - rem;
3218 * Routine to create a directory ...
3221 static int
3222 smbc_mkdir_ctx(SMBCCTX *context,
3223 const char *fname,
3224 mode_t mode)
3226 SMBCSRV *srv;
3227 fstring server;
3228 fstring share;
3229 fstring user;
3230 fstring password;
3231 fstring workgroup;
3232 pstring path, targetpath;
3233 struct cli_state *targetcli;
3235 if (!context || !context->internal ||
3236 !context->internal->_initialized) {
3238 errno = EINVAL;
3239 return -1;
3243 if (!fname) {
3245 errno = EINVAL;
3246 return -1;
3250 DEBUG(4, ("smbc_mkdir(%s)\n", fname));
3252 if (smbc_parse_path(context, fname,
3253 workgroup, sizeof(workgroup),
3254 server, sizeof(server),
3255 share, sizeof(share),
3256 path, sizeof(path),
3257 user, sizeof(user),
3258 password, sizeof(password),
3259 NULL, 0)) {
3260 errno = EINVAL;
3261 return -1;
3264 if (user[0] == (char)0) fstrcpy(user, context->user);
3266 srv = smbc_server(context, True,
3267 server, share, workgroup, user, password);
3269 if (!srv) {
3271 return -1; /* errno set by smbc_server */
3275 /*d_printf(">>>mkdir: resolving %s\n", path);*/
3276 if (!cli_resolve_path( "", srv->cli, path, &targetcli, targetpath))
3278 d_printf("Could not resolve %s\n", path);
3279 return -1;
3281 /*d_printf(">>>mkdir: resolved path as %s\n", targetpath);*/
3283 if (!cli_mkdir(targetcli, targetpath)) {
3285 errno = smbc_errno(context, targetcli);
3286 return -1;
3290 return 0;
3295 * Our list function simply checks to see if a directory is not empty
3298 static int smbc_rmdir_dirempty = True;
3300 static void
3301 rmdir_list_fn(const char *mnt,
3302 file_info *finfo,
3303 const char *mask,
3304 void *state)
3306 if (strncmp(finfo->name, ".", 1) != 0 &&
3307 strncmp(finfo->name, "..", 2) != 0) {
3309 smbc_rmdir_dirempty = False;
3314 * Routine to remove a directory
3317 static int
3318 smbc_rmdir_ctx(SMBCCTX *context,
3319 const char *fname)
3321 SMBCSRV *srv;
3322 fstring server;
3323 fstring share;
3324 fstring user;
3325 fstring password;
3326 fstring workgroup;
3327 pstring path;
3328 pstring targetpath;
3329 struct cli_state *targetcli;
3331 if (!context || !context->internal ||
3332 !context->internal->_initialized) {
3334 errno = EINVAL;
3335 return -1;
3339 if (!fname) {
3341 errno = EINVAL;
3342 return -1;
3346 DEBUG(4, ("smbc_rmdir(%s)\n", fname));
3348 if (smbc_parse_path(context, fname,
3349 workgroup, sizeof(workgroup),
3350 server, sizeof(server),
3351 share, sizeof(share),
3352 path, sizeof(path),
3353 user, sizeof(user),
3354 password, sizeof(password),
3355 NULL, 0))
3357 errno = EINVAL;
3358 return -1;
3361 if (user[0] == (char)0) fstrcpy(user, context->user);
3363 srv = smbc_server(context, True,
3364 server, share, workgroup, user, password);
3366 if (!srv) {
3368 return -1; /* errno set by smbc_server */
3372 /*d_printf(">>>rmdir: resolving %s\n", path);*/
3373 if (!cli_resolve_path( "", srv->cli, path, &targetcli, targetpath))
3375 d_printf("Could not resolve %s\n", path);
3376 return -1;
3378 /*d_printf(">>>rmdir: resolved path as %s\n", targetpath);*/
3381 if (!cli_rmdir(targetcli, targetpath)) {
3383 errno = smbc_errno(context, targetcli);
3385 if (errno == EACCES) { /* Check if the dir empty or not */
3387 /* Local storage to avoid buffer overflows */
3388 pstring lpath;
3390 smbc_rmdir_dirempty = True; /* Make this so ... */
3392 pstrcpy(lpath, targetpath);
3393 pstrcat(lpath, "\\*");
3395 if (cli_list(targetcli, lpath,
3396 aDIR | aSYSTEM | aHIDDEN,
3397 rmdir_list_fn, NULL) < 0) {
3399 /* Fix errno to ignore latest error ... */
3400 DEBUG(5, ("smbc_rmdir: "
3401 "cli_list returned an error: %d\n",
3402 smbc_errno(context, targetcli)));
3403 errno = EACCES;
3407 if (smbc_rmdir_dirempty)
3408 errno = EACCES;
3409 else
3410 errno = ENOTEMPTY;
3414 return -1;
3418 return 0;
3423 * Routine to return the current directory position
3426 static off_t
3427 smbc_telldir_ctx(SMBCCTX *context,
3428 SMBCFILE *dir)
3430 if (!context || !context->internal ||
3431 !context->internal->_initialized) {
3433 errno = EINVAL;
3434 return -1;
3438 if (!dir || !DLIST_CONTAINS(context->internal->_files, dir)) {
3440 errno = EBADF;
3441 return -1;
3445 if (dir->file != False) { /* FIXME, should be dir, perhaps */
3447 errno = ENOTDIR;
3448 return -1;
3452 /* See if we're already at the end. */
3453 if (dir->dir_next == NULL) {
3454 /* We are. */
3455 return -1;
3459 * We return the pointer here as the offset
3461 return (off_t)(long)dir->dir_next->dirent;
3465 * A routine to run down the list and see if the entry is OK
3468 struct smbc_dir_list *
3469 smbc_check_dir_ent(struct smbc_dir_list *list,
3470 struct smbc_dirent *dirent)
3473 /* Run down the list looking for what we want */
3475 if (dirent) {
3477 struct smbc_dir_list *tmp = list;
3479 while (tmp) {
3481 if (tmp->dirent == dirent)
3482 return tmp;
3484 tmp = tmp->next;
3490 return NULL; /* Not found, or an error */
3496 * Routine to seek on a directory
3499 static int
3500 smbc_lseekdir_ctx(SMBCCTX *context,
3501 SMBCFILE *dir,
3502 off_t offset)
3504 long int l_offset = offset; /* Handle problems of size */
3505 struct smbc_dirent *dirent = (struct smbc_dirent *)l_offset;
3506 struct smbc_dir_list *list_ent = (struct smbc_dir_list *)NULL;
3508 if (!context || !context->internal ||
3509 !context->internal->_initialized) {
3511 errno = EINVAL;
3512 return -1;
3516 if (dir->file != False) { /* FIXME, should be dir, perhaps */
3518 errno = ENOTDIR;
3519 return -1;
3523 /* Now, check what we were passed and see if it is OK ... */
3525 if (dirent == NULL) { /* Seek to the begining of the list */
3527 dir->dir_next = dir->dir_list;
3528 return 0;
3532 if (offset == -1) { /* Seek to the end of the list */
3533 dir->dir_next = NULL;
3534 return 0;
3537 /* Now, run down the list and make sure that the entry is OK */
3538 /* This may need to be changed if we change the format of the list */
3540 if ((list_ent = smbc_check_dir_ent(dir->dir_list, dirent)) == NULL) {
3542 errno = EINVAL; /* Bad entry */
3543 return -1;
3547 dir->dir_next = list_ent;
3549 return 0;
3554 * Routine to fstat a dir
3557 static int
3558 smbc_fstatdir_ctx(SMBCCTX *context,
3559 SMBCFILE *dir,
3560 struct stat *st)
3563 if (!context || !context->internal ||
3564 !context->internal->_initialized) {
3566 errno = EINVAL;
3567 return -1;
3571 /* No code yet ... */
3573 return 0;
3577 static int
3578 smbc_chmod_ctx(SMBCCTX *context,
3579 const char *fname,
3580 mode_t newmode)
3582 SMBCSRV *srv;
3583 fstring server;
3584 fstring share;
3585 fstring user;
3586 fstring password;
3587 fstring workgroup;
3588 pstring path;
3589 uint16 mode;
3591 if (!context || !context->internal ||
3592 !context->internal->_initialized) {
3594 errno = EINVAL; /* Best I can think of ... */
3595 return -1;
3599 if (!fname) {
3601 errno = EINVAL;
3602 return -1;
3606 DEBUG(4, ("smbc_chmod(%s, 0%3o)\n", fname, newmode));
3608 if (smbc_parse_path(context, fname,
3609 workgroup, sizeof(workgroup),
3610 server, sizeof(server),
3611 share, sizeof(share),
3612 path, sizeof(path),
3613 user, sizeof(user),
3614 password, sizeof(password),
3615 NULL, 0)) {
3616 errno = EINVAL;
3617 return -1;
3620 if (user[0] == (char)0) fstrcpy(user, context->user);
3622 srv = smbc_server(context, True,
3623 server, share, workgroup, user, password);
3625 if (!srv) {
3626 return -1; /* errno set by smbc_server */
3629 mode = 0;
3631 if (!(newmode & (S_IWUSR | S_IWGRP | S_IWOTH))) mode |= aRONLY;
3632 if ((newmode & S_IXUSR) && lp_map_archive(-1)) mode |= aARCH;
3633 if ((newmode & S_IXGRP) && lp_map_system(-1)) mode |= aSYSTEM;
3634 if ((newmode & S_IXOTH) && lp_map_hidden(-1)) mode |= aHIDDEN;
3636 if (!cli_setatr(srv->cli, path, mode, 0)) {
3637 errno = smbc_errno(context, srv->cli);
3638 return -1;
3641 return 0;
3644 static int
3645 smbc_utimes_ctx(SMBCCTX *context,
3646 const char *fname,
3647 struct timeval *tbuf)
3649 SMBCSRV *srv;
3650 fstring server;
3651 fstring share;
3652 fstring user;
3653 fstring password;
3654 fstring workgroup;
3655 pstring path;
3656 time_t access_time;
3657 time_t write_time;
3659 if (!context || !context->internal ||
3660 !context->internal->_initialized) {
3662 errno = EINVAL; /* Best I can think of ... */
3663 return -1;
3667 if (!fname) {
3669 errno = EINVAL;
3670 return -1;
3674 if (tbuf == NULL) {
3675 access_time = write_time = time(NULL);
3676 } else {
3677 access_time = tbuf[0].tv_sec;
3678 write_time = tbuf[1].tv_sec;
3681 if (DEBUGLVL(4))
3683 char *p;
3684 char atimebuf[32];
3685 char mtimebuf[32];
3687 strncpy(atimebuf, ctime(&access_time), sizeof(atimebuf) - 1);
3688 atimebuf[sizeof(atimebuf) - 1] = '\0';
3689 if ((p = strchr(atimebuf, '\n')) != NULL) {
3690 *p = '\0';
3693 strncpy(mtimebuf, ctime(&write_time), sizeof(mtimebuf) - 1);
3694 mtimebuf[sizeof(mtimebuf) - 1] = '\0';
3695 if ((p = strchr(mtimebuf, '\n')) != NULL) {
3696 *p = '\0';
3699 dbgtext("smbc_utimes(%s, atime = %s mtime = %s)\n",
3700 fname, atimebuf, mtimebuf);
3703 if (smbc_parse_path(context, fname,
3704 workgroup, sizeof(workgroup),
3705 server, sizeof(server),
3706 share, sizeof(share),
3707 path, sizeof(path),
3708 user, sizeof(user),
3709 password, sizeof(password),
3710 NULL, 0)) {
3711 errno = EINVAL;
3712 return -1;
3715 if (user[0] == (char)0) fstrcpy(user, context->user);
3717 srv = smbc_server(context, True,
3718 server, share, workgroup, user, password);
3720 if (!srv) {
3721 return -1; /* errno set by smbc_server */
3724 if (!smbc_setatr(context, srv, path,
3725 0, access_time, write_time, 0, 0)) {
3726 return -1; /* errno set by smbc_setatr */
3729 return 0;
3734 * Sort ACEs according to the documentation at
3735 * http://support.microsoft.com/kb/269175, at least as far as it defines the
3736 * order.
3739 static int
3740 ace_compare(SEC_ACE *ace1,
3741 SEC_ACE *ace2)
3743 BOOL b1;
3744 BOOL b2;
3746 /* If the ACEs are equal, we have nothing more to do. */
3747 if (sec_ace_equal(ace1, ace2)) {
3748 return 0;
3751 /* Inherited follow non-inherited */
3752 b1 = ((ace1->flags & SEC_ACE_FLAG_INHERITED_ACE) != 0);
3753 b2 = ((ace2->flags & SEC_ACE_FLAG_INHERITED_ACE) != 0);
3754 if (b1 != b2) {
3755 return (b1 ? 1 : -1);
3759 * What shall we do with AUDITs and ALARMs? It's undefined. We'll
3760 * sort them after DENY and ALLOW.
3762 b1 = (ace1->type != SEC_ACE_TYPE_ACCESS_ALLOWED &&
3763 ace1->type != SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT &&
3764 ace1->type != SEC_ACE_TYPE_ACCESS_DENIED &&
3765 ace1->type != SEC_ACE_TYPE_ACCESS_DENIED_OBJECT);
3766 b2 = (ace2->type != SEC_ACE_TYPE_ACCESS_ALLOWED &&
3767 ace2->type != SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT &&
3768 ace2->type != SEC_ACE_TYPE_ACCESS_DENIED &&
3769 ace2->type != SEC_ACE_TYPE_ACCESS_DENIED_OBJECT);
3770 if (b1 != b2) {
3771 return (b1 ? 1 : -1);
3774 /* Allowed ACEs follow denied ACEs */
3775 b1 = (ace1->type == SEC_ACE_TYPE_ACCESS_ALLOWED ||
3776 ace1->type == SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT);
3777 b2 = (ace2->type == SEC_ACE_TYPE_ACCESS_ALLOWED ||
3778 ace2->type == SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT);
3779 if (b1 != b2) {
3780 return (b1 ? 1 : -1);
3784 * ACEs applying to an entity's object follow those applying to the
3785 * entity itself
3787 b1 = (ace1->type == SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT ||
3788 ace1->type == SEC_ACE_TYPE_ACCESS_DENIED_OBJECT);
3789 b2 = (ace2->type == SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT ||
3790 ace2->type == SEC_ACE_TYPE_ACCESS_DENIED_OBJECT);
3791 if (b1 != b2) {
3792 return (b1 ? 1 : -1);
3796 * If we get this far, the ACEs are similar as far as the
3797 * characteristics we typically care about (those defined by the
3798 * referenced MS document). We'll now sort by characteristics that
3799 * just seems reasonable.
3802 if (ace1->type != ace2->type) {
3803 return ace2->type - ace1->type;
3806 if (sid_compare(&ace1->trustee, &ace2->trustee)) {
3807 return sid_compare(&ace1->trustee, &ace2->trustee);
3810 if (ace1->flags != ace2->flags) {
3811 return ace1->flags - ace2->flags;
3814 if (ace1->access_mask != ace2->access_mask) {
3815 return ace1->access_mask - ace2->access_mask;
3818 if (ace1->size != ace2->size) {
3819 return ace1->size - ace2->size;
3822 return memcmp(ace1, ace2, sizeof(SEC_ACE));
3826 static void
3827 sort_acl(SEC_ACL *the_acl)
3829 uint32 i;
3830 if (!the_acl) return;
3832 qsort(the_acl->aces, the_acl->num_aces, sizeof(the_acl->aces[0]),
3833 QSORT_CAST ace_compare);
3835 for (i=1;i<the_acl->num_aces;) {
3836 if (sec_ace_equal(&the_acl->aces[i-1], &the_acl->aces[i])) {
3837 int j;
3838 for (j=i; j<the_acl->num_aces-1; j++) {
3839 the_acl->aces[j] = the_acl->aces[j+1];
3841 the_acl->num_aces--;
3842 } else {
3843 i++;
3848 /* convert a SID to a string, either numeric or username/group */
3849 static void
3850 convert_sid_to_string(struct cli_state *ipc_cli,
3851 POLICY_HND *pol,
3852 fstring str,
3853 BOOL numeric,
3854 DOM_SID *sid)
3856 char **domains = NULL;
3857 char **names = NULL;
3858 enum lsa_SidType *types = NULL;
3859 struct rpc_pipe_client *pipe_hnd = find_lsa_pipe_hnd(ipc_cli);
3860 sid_to_string(str, sid);
3862 if (numeric) {
3863 return; /* no lookup desired */
3866 if (!pipe_hnd) {
3867 return;
3870 /* Ask LSA to convert the sid to a name */
3872 if (!NT_STATUS_IS_OK(rpccli_lsa_lookup_sids(pipe_hnd, ipc_cli->mem_ctx,
3873 pol, 1, sid, &domains,
3874 &names, &types)) ||
3875 !domains || !domains[0] || !names || !names[0]) {
3876 return;
3879 /* Converted OK */
3881 slprintf(str, sizeof(fstring) - 1, "%s%s%s",
3882 domains[0], lp_winbind_separator(),
3883 names[0]);
3886 /* convert a string to a SID, either numeric or username/group */
3887 static BOOL
3888 convert_string_to_sid(struct cli_state *ipc_cli,
3889 POLICY_HND *pol,
3890 BOOL numeric,
3891 DOM_SID *sid,
3892 const char *str)
3894 enum lsa_SidType *types = NULL;
3895 DOM_SID *sids = NULL;
3896 BOOL result = True;
3897 struct rpc_pipe_client *pipe_hnd = find_lsa_pipe_hnd(ipc_cli);
3899 if (!pipe_hnd) {
3900 return False;
3903 if (numeric) {
3904 if (strncmp(str, "S-", 2) == 0) {
3905 return string_to_sid(sid, str);
3908 result = False;
3909 goto done;
3912 if (!NT_STATUS_IS_OK(rpccli_lsa_lookup_names(pipe_hnd, ipc_cli->mem_ctx,
3913 pol, 1, &str, NULL, &sids,
3914 &types))) {
3915 result = False;
3916 goto done;
3919 sid_copy(sid, &sids[0]);
3920 done:
3922 return result;
3926 /* parse an ACE in the same format as print_ace() */
3927 static BOOL
3928 parse_ace(struct cli_state *ipc_cli,
3929 POLICY_HND *pol,
3930 SEC_ACE *ace,
3931 BOOL numeric,
3932 char *str)
3934 char *p;
3935 const char *cp;
3936 fstring tok;
3937 unsigned int atype;
3938 unsigned int aflags;
3939 unsigned int amask;
3940 DOM_SID sid;
3941 SEC_ACCESS mask;
3942 const struct perm_value *v;
3943 struct perm_value {
3944 const char *perm;
3945 uint32 mask;
3948 /* These values discovered by inspection */
3949 static const struct perm_value special_values[] = {
3950 { "R", 0x00120089 },
3951 { "W", 0x00120116 },
3952 { "X", 0x001200a0 },
3953 { "D", 0x00010000 },
3954 { "P", 0x00040000 },
3955 { "O", 0x00080000 },
3956 { NULL, 0 },
3959 static const struct perm_value standard_values[] = {
3960 { "READ", 0x001200a9 },
3961 { "CHANGE", 0x001301bf },
3962 { "FULL", 0x001f01ff },
3963 { NULL, 0 },
3967 ZERO_STRUCTP(ace);
3968 p = strchr_m(str,':');
3969 if (!p) return False;
3970 *p = '\0';
3971 p++;
3972 /* Try to parse numeric form */
3974 if (sscanf(p, "%i/%i/%i", &atype, &aflags, &amask) == 3 &&
3975 convert_string_to_sid(ipc_cli, pol, numeric, &sid, str)) {
3976 goto done;
3979 /* Try to parse text form */
3981 if (!convert_string_to_sid(ipc_cli, pol, numeric, &sid, str)) {
3982 return False;
3985 cp = p;
3986 if (!next_token(&cp, tok, "/", sizeof(fstring))) {
3987 return False;
3990 if (StrnCaseCmp(tok, "ALLOWED", strlen("ALLOWED")) == 0) {
3991 atype = SEC_ACE_TYPE_ACCESS_ALLOWED;
3992 } else if (StrnCaseCmp(tok, "DENIED", strlen("DENIED")) == 0) {
3993 atype = SEC_ACE_TYPE_ACCESS_DENIED;
3994 } else {
3995 return False;
3998 /* Only numeric form accepted for flags at present */
4000 if (!(next_token(&cp, tok, "/", sizeof(fstring)) &&
4001 sscanf(tok, "%i", &aflags))) {
4002 return False;
4005 if (!next_token(&cp, tok, "/", sizeof(fstring))) {
4006 return False;
4009 if (strncmp(tok, "0x", 2) == 0) {
4010 if (sscanf(tok, "%i", &amask) != 1) {
4011 return False;
4013 goto done;
4016 for (v = standard_values; v->perm; v++) {
4017 if (strcmp(tok, v->perm) == 0) {
4018 amask = v->mask;
4019 goto done;
4023 p = tok;
4025 while(*p) {
4026 BOOL found = False;
4028 for (v = special_values; v->perm; v++) {
4029 if (v->perm[0] == *p) {
4030 amask |= v->mask;
4031 found = True;
4035 if (!found) return False;
4036 p++;
4039 if (*p) {
4040 return False;
4043 done:
4044 mask = amask;
4045 init_sec_ace(ace, &sid, atype, mask, aflags);
4046 return True;
4049 /* add an ACE to a list of ACEs in a SEC_ACL */
4050 static BOOL
4051 add_ace(SEC_ACL **the_acl,
4052 SEC_ACE *ace,
4053 TALLOC_CTX *ctx)
4055 SEC_ACL *newacl;
4056 SEC_ACE *aces;
4058 if (! *the_acl) {
4059 (*the_acl) = make_sec_acl(ctx, 3, 1, ace);
4060 return True;
4063 if ((aces = SMB_CALLOC_ARRAY(SEC_ACE, 1+(*the_acl)->num_aces)) == NULL) {
4064 return False;
4066 memcpy(aces, (*the_acl)->aces, (*the_acl)->num_aces * sizeof(SEC_ACE));
4067 memcpy(aces+(*the_acl)->num_aces, ace, sizeof(SEC_ACE));
4068 newacl = make_sec_acl(ctx, (*the_acl)->revision,
4069 1+(*the_acl)->num_aces, aces);
4070 SAFE_FREE(aces);
4071 (*the_acl) = newacl;
4072 return True;
4076 /* parse a ascii version of a security descriptor */
4077 static SEC_DESC *
4078 sec_desc_parse(TALLOC_CTX *ctx,
4079 struct cli_state *ipc_cli,
4080 POLICY_HND *pol,
4081 BOOL numeric,
4082 char *str)
4084 const char *p = str;
4085 fstring tok;
4086 SEC_DESC *ret = NULL;
4087 size_t sd_size;
4088 DOM_SID *group_sid=NULL;
4089 DOM_SID *owner_sid=NULL;
4090 SEC_ACL *dacl=NULL;
4091 int revision=1;
4093 while (next_token(&p, tok, "\t,\r\n", sizeof(tok))) {
4095 if (StrnCaseCmp(tok,"REVISION:", 9) == 0) {
4096 revision = strtol(tok+9, NULL, 16);
4097 continue;
4100 if (StrnCaseCmp(tok,"OWNER:", 6) == 0) {
4101 if (owner_sid) {
4102 DEBUG(5, ("OWNER specified more than once!\n"));
4103 goto done;
4105 owner_sid = SMB_CALLOC_ARRAY(DOM_SID, 1);
4106 if (!owner_sid ||
4107 !convert_string_to_sid(ipc_cli, pol,
4108 numeric,
4109 owner_sid, tok+6)) {
4110 DEBUG(5, ("Failed to parse owner sid\n"));
4111 goto done;
4113 continue;
4116 if (StrnCaseCmp(tok,"OWNER+:", 7) == 0) {
4117 if (owner_sid) {
4118 DEBUG(5, ("OWNER specified more than once!\n"));
4119 goto done;
4121 owner_sid = SMB_CALLOC_ARRAY(DOM_SID, 1);
4122 if (!owner_sid ||
4123 !convert_string_to_sid(ipc_cli, pol,
4124 False,
4125 owner_sid, tok+7)) {
4126 DEBUG(5, ("Failed to parse owner sid\n"));
4127 goto done;
4129 continue;
4132 if (StrnCaseCmp(tok,"GROUP:", 6) == 0) {
4133 if (group_sid) {
4134 DEBUG(5, ("GROUP specified more than once!\n"));
4135 goto done;
4137 group_sid = SMB_CALLOC_ARRAY(DOM_SID, 1);
4138 if (!group_sid ||
4139 !convert_string_to_sid(ipc_cli, pol,
4140 numeric,
4141 group_sid, tok+6)) {
4142 DEBUG(5, ("Failed to parse group sid\n"));
4143 goto done;
4145 continue;
4148 if (StrnCaseCmp(tok,"GROUP+:", 7) == 0) {
4149 if (group_sid) {
4150 DEBUG(5, ("GROUP specified more than once!\n"));
4151 goto done;
4153 group_sid = SMB_CALLOC_ARRAY(DOM_SID, 1);
4154 if (!group_sid ||
4155 !convert_string_to_sid(ipc_cli, pol,
4156 False,
4157 group_sid, tok+6)) {
4158 DEBUG(5, ("Failed to parse group sid\n"));
4159 goto done;
4161 continue;
4164 if (StrnCaseCmp(tok,"ACL:", 4) == 0) {
4165 SEC_ACE ace;
4166 if (!parse_ace(ipc_cli, pol, &ace, numeric, tok+4)) {
4167 DEBUG(5, ("Failed to parse ACL %s\n", tok));
4168 goto done;
4170 if(!add_ace(&dacl, &ace, ctx)) {
4171 DEBUG(5, ("Failed to add ACL %s\n", tok));
4172 goto done;
4174 continue;
4177 if (StrnCaseCmp(tok,"ACL+:", 5) == 0) {
4178 SEC_ACE ace;
4179 if (!parse_ace(ipc_cli, pol, &ace, False, tok+5)) {
4180 DEBUG(5, ("Failed to parse ACL %s\n", tok));
4181 goto done;
4183 if(!add_ace(&dacl, &ace, ctx)) {
4184 DEBUG(5, ("Failed to add ACL %s\n", tok));
4185 goto done;
4187 continue;
4190 DEBUG(5, ("Failed to parse security descriptor\n"));
4191 goto done;
4194 ret = make_sec_desc(ctx, revision, SEC_DESC_SELF_RELATIVE,
4195 owner_sid, group_sid, NULL, dacl, &sd_size);
4197 done:
4198 SAFE_FREE(group_sid);
4199 SAFE_FREE(owner_sid);
4201 return ret;
4205 /* Obtain the current dos attributes */
4206 static DOS_ATTR_DESC *
4207 dos_attr_query(SMBCCTX *context,
4208 TALLOC_CTX *ctx,
4209 const char *filename,
4210 SMBCSRV *srv)
4212 struct timespec create_time_ts;
4213 struct timespec write_time_ts;
4214 struct timespec access_time_ts;
4215 struct timespec change_time_ts;
4216 SMB_OFF_T size = 0;
4217 uint16 mode = 0;
4218 SMB_INO_T inode = 0;
4219 DOS_ATTR_DESC *ret;
4221 ret = TALLOC_P(ctx, DOS_ATTR_DESC);
4222 if (!ret) {
4223 errno = ENOMEM;
4224 return NULL;
4227 /* Obtain the DOS attributes */
4228 if (!smbc_getatr(context, srv, CONST_DISCARD(char *, filename),
4229 &mode, &size,
4230 &create_time_ts,
4231 &access_time_ts,
4232 &write_time_ts,
4233 &change_time_ts,
4234 &inode)) {
4236 errno = smbc_errno(context, srv->cli);
4237 DEBUG(5, ("dos_attr_query Failed to query old attributes\n"));
4238 return NULL;
4242 ret->mode = mode;
4243 ret->size = size;
4244 ret->create_time = convert_timespec_to_time_t(create_time_ts);
4245 ret->access_time = convert_timespec_to_time_t(access_time_ts);
4246 ret->write_time = convert_timespec_to_time_t(write_time_ts);
4247 ret->change_time = convert_timespec_to_time_t(change_time_ts);
4248 ret->inode = inode;
4250 return ret;
4254 /* parse a ascii version of a security descriptor */
4255 static void
4256 dos_attr_parse(SMBCCTX *context,
4257 DOS_ATTR_DESC *dad,
4258 SMBCSRV *srv,
4259 char *str)
4261 int n;
4262 const char *p = str;
4263 fstring tok;
4264 struct {
4265 const char * create_time_attr;
4266 const char * access_time_attr;
4267 const char * write_time_attr;
4268 const char * change_time_attr;
4269 } attr_strings;
4271 /* Determine whether to use old-style or new-style attribute names */
4272 if (context->internal->_full_time_names) {
4273 /* new-style names */
4274 attr_strings.create_time_attr = "CREATE_TIME";
4275 attr_strings.access_time_attr = "ACCESS_TIME";
4276 attr_strings.write_time_attr = "WRITE_TIME";
4277 attr_strings.change_time_attr = "CHANGE_TIME";
4278 } else {
4279 /* old-style names */
4280 attr_strings.create_time_attr = NULL;
4281 attr_strings.access_time_attr = "A_TIME";
4282 attr_strings.write_time_attr = "M_TIME";
4283 attr_strings.change_time_attr = "C_TIME";
4286 /* if this is to set the entire ACL... */
4287 if (*str == '*') {
4288 /* ... then increment past the first colon if there is one */
4289 if ((p = strchr(str, ':')) != NULL) {
4290 ++p;
4291 } else {
4292 p = str;
4296 while (next_token(&p, tok, "\t,\r\n", sizeof(tok))) {
4298 if (StrnCaseCmp(tok, "MODE:", 5) == 0) {
4299 dad->mode = strtol(tok+5, NULL, 16);
4300 continue;
4303 if (StrnCaseCmp(tok, "SIZE:", 5) == 0) {
4304 dad->size = (SMB_OFF_T)atof(tok+5);
4305 continue;
4308 n = strlen(attr_strings.access_time_attr);
4309 if (StrnCaseCmp(tok, attr_strings.access_time_attr, n) == 0) {
4310 dad->access_time = (time_t)strtol(tok+n+1, NULL, 10);
4311 continue;
4314 n = strlen(attr_strings.change_time_attr);
4315 if (StrnCaseCmp(tok, attr_strings.change_time_attr, n) == 0) {
4316 dad->change_time = (time_t)strtol(tok+n+1, NULL, 10);
4317 continue;
4320 n = strlen(attr_strings.write_time_attr);
4321 if (StrnCaseCmp(tok, attr_strings.write_time_attr, n) == 0) {
4322 dad->write_time = (time_t)strtol(tok+n+1, NULL, 10);
4323 continue;
4326 if (attr_strings.create_time_attr != NULL) {
4327 n = strlen(attr_strings.create_time_attr);
4328 if (StrnCaseCmp(tok, attr_strings.create_time_attr,
4329 n) == 0) {
4330 dad->create_time = (time_t)strtol(tok+n+1,
4331 NULL, 10);
4332 continue;
4336 if (StrnCaseCmp(tok, "INODE:", 6) == 0) {
4337 dad->inode = (SMB_INO_T)atof(tok+6);
4338 continue;
4343 /*****************************************************
4344 Retrieve the acls for a file.
4345 *******************************************************/
4347 static int
4348 cacl_get(SMBCCTX *context,
4349 TALLOC_CTX *ctx,
4350 SMBCSRV *srv,
4351 struct cli_state *ipc_cli,
4352 POLICY_HND *pol,
4353 char *filename,
4354 char *attr_name,
4355 char *buf,
4356 int bufsize)
4358 uint32 i;
4359 int n = 0;
4360 int n_used;
4361 BOOL all;
4362 BOOL all_nt;
4363 BOOL all_nt_acls;
4364 BOOL all_dos;
4365 BOOL some_nt;
4366 BOOL some_dos;
4367 BOOL exclude_nt_revision = False;
4368 BOOL exclude_nt_owner = False;
4369 BOOL exclude_nt_group = False;
4370 BOOL exclude_nt_acl = False;
4371 BOOL exclude_dos_mode = False;
4372 BOOL exclude_dos_size = False;
4373 BOOL exclude_dos_create_time = False;
4374 BOOL exclude_dos_access_time = False;
4375 BOOL exclude_dos_write_time = False;
4376 BOOL exclude_dos_change_time = False;
4377 BOOL exclude_dos_inode = False;
4378 BOOL numeric = True;
4379 BOOL determine_size = (bufsize == 0);
4380 int fnum = -1;
4381 SEC_DESC *sd;
4382 fstring sidstr;
4383 fstring name_sandbox;
4384 char *name;
4385 char *pExclude;
4386 char *p;
4387 struct timespec create_time_ts;
4388 struct timespec write_time_ts;
4389 struct timespec access_time_ts;
4390 struct timespec change_time_ts;
4391 time_t create_time = (time_t)0;
4392 time_t write_time = (time_t)0;
4393 time_t access_time = (time_t)0;
4394 time_t change_time = (time_t)0;
4395 SMB_OFF_T size = 0;
4396 uint16 mode = 0;
4397 SMB_INO_T ino = 0;
4398 struct cli_state *cli = srv->cli;
4399 struct {
4400 const char * create_time_attr;
4401 const char * access_time_attr;
4402 const char * write_time_attr;
4403 const char * change_time_attr;
4404 } attr_strings;
4405 struct {
4406 const char * create_time_attr;
4407 const char * access_time_attr;
4408 const char * write_time_attr;
4409 const char * change_time_attr;
4410 } excl_attr_strings;
4412 /* Determine whether to use old-style or new-style attribute names */
4413 if (context->internal->_full_time_names) {
4414 /* new-style names */
4415 attr_strings.create_time_attr = "CREATE_TIME";
4416 attr_strings.access_time_attr = "ACCESS_TIME";
4417 attr_strings.write_time_attr = "WRITE_TIME";
4418 attr_strings.change_time_attr = "CHANGE_TIME";
4420 excl_attr_strings.create_time_attr = "CREATE_TIME";
4421 excl_attr_strings.access_time_attr = "ACCESS_TIME";
4422 excl_attr_strings.write_time_attr = "WRITE_TIME";
4423 excl_attr_strings.change_time_attr = "CHANGE_TIME";
4424 } else {
4425 /* old-style names */
4426 attr_strings.create_time_attr = NULL;
4427 attr_strings.access_time_attr = "A_TIME";
4428 attr_strings.write_time_attr = "M_TIME";
4429 attr_strings.change_time_attr = "C_TIME";
4431 excl_attr_strings.create_time_attr = NULL;
4432 excl_attr_strings.access_time_attr = "dos_attr.A_TIME";
4433 excl_attr_strings.write_time_attr = "dos_attr.M_TIME";
4434 excl_attr_strings.change_time_attr = "dos_attr.C_TIME";
4437 /* Copy name so we can strip off exclusions (if any are specified) */
4438 strncpy(name_sandbox, attr_name, sizeof(name_sandbox) - 1);
4440 /* Ensure name is null terminated */
4441 name_sandbox[sizeof(name_sandbox) - 1] = '\0';
4443 /* Play in the sandbox */
4444 name = name_sandbox;
4446 /* If there are any exclusions, point to them and mask them from name */
4447 if ((pExclude = strchr(name, '!')) != NULL)
4449 *pExclude++ = '\0';
4452 all = (StrnCaseCmp(name, "system.*", 8) == 0);
4453 all_nt = (StrnCaseCmp(name, "system.nt_sec_desc.*", 20) == 0);
4454 all_nt_acls = (StrnCaseCmp(name, "system.nt_sec_desc.acl.*", 24) == 0);
4455 all_dos = (StrnCaseCmp(name, "system.dos_attr.*", 17) == 0);
4456 some_nt = (StrnCaseCmp(name, "system.nt_sec_desc.", 19) == 0);
4457 some_dos = (StrnCaseCmp(name, "system.dos_attr.", 16) == 0);
4458 numeric = (* (name + strlen(name) - 1) != '+');
4460 /* Look for exclusions from "all" requests */
4461 if (all || all_nt || all_dos) {
4463 /* Exclusions are delimited by '!' */
4464 for (;
4465 pExclude != NULL;
4466 pExclude = (p == NULL ? NULL : p + 1)) {
4468 /* Find end of this exclusion name */
4469 if ((p = strchr(pExclude, '!')) != NULL)
4471 *p = '\0';
4474 /* Which exclusion name is this? */
4475 if (StrCaseCmp(pExclude, "nt_sec_desc.revision") == 0) {
4476 exclude_nt_revision = True;
4478 else if (StrCaseCmp(pExclude, "nt_sec_desc.owner") == 0) {
4479 exclude_nt_owner = True;
4481 else if (StrCaseCmp(pExclude, "nt_sec_desc.group") == 0) {
4482 exclude_nt_group = True;
4484 else if (StrCaseCmp(pExclude, "nt_sec_desc.acl") == 0) {
4485 exclude_nt_acl = True;
4487 else if (StrCaseCmp(pExclude, "dos_attr.mode") == 0) {
4488 exclude_dos_mode = True;
4490 else if (StrCaseCmp(pExclude, "dos_attr.size") == 0) {
4491 exclude_dos_size = True;
4493 else if (excl_attr_strings.create_time_attr != NULL &&
4494 StrCaseCmp(pExclude,
4495 excl_attr_strings.change_time_attr) == 0) {
4496 exclude_dos_create_time = True;
4498 else if (StrCaseCmp(pExclude,
4499 excl_attr_strings.access_time_attr) == 0) {
4500 exclude_dos_access_time = True;
4502 else if (StrCaseCmp(pExclude,
4503 excl_attr_strings.write_time_attr) == 0) {
4504 exclude_dos_write_time = True;
4506 else if (StrCaseCmp(pExclude,
4507 excl_attr_strings.change_time_attr) == 0) {
4508 exclude_dos_change_time = True;
4510 else if (StrCaseCmp(pExclude, "dos_attr.inode") == 0) {
4511 exclude_dos_inode = True;
4513 else {
4514 DEBUG(5, ("cacl_get received unknown exclusion: %s\n",
4515 pExclude));
4516 errno = ENOATTR;
4517 return -1;
4522 n_used = 0;
4525 * If we are (possibly) talking to an NT or new system and some NT
4526 * attributes have been requested...
4528 if (ipc_cli && (all || some_nt || all_nt_acls)) {
4529 /* Point to the portion after "system.nt_sec_desc." */
4530 name += 19; /* if (all) this will be invalid but unused */
4532 /* ... then obtain any NT attributes which were requested */
4533 fnum = cli_nt_create(cli, filename, CREATE_ACCESS_READ);
4535 if (fnum == -1) {
4536 DEBUG(5, ("cacl_get failed to open %s: %s\n",
4537 filename, cli_errstr(cli)));
4538 errno = 0;
4539 return -1;
4542 sd = cli_query_secdesc(cli, fnum, ctx);
4544 if (!sd) {
4545 DEBUG(5,
4546 ("cacl_get Failed to query old descriptor\n"));
4547 errno = 0;
4548 return -1;
4551 cli_close(cli, fnum);
4553 if (! exclude_nt_revision) {
4554 if (all || all_nt) {
4555 if (determine_size) {
4556 p = talloc_asprintf(ctx,
4557 "REVISION:%d",
4558 sd->revision);
4559 if (!p) {
4560 errno = ENOMEM;
4561 return -1;
4563 n = strlen(p);
4564 } else {
4565 n = snprintf(buf, bufsize,
4566 "REVISION:%d",
4567 sd->revision);
4569 } else if (StrCaseCmp(name, "revision") == 0) {
4570 if (determine_size) {
4571 p = talloc_asprintf(ctx, "%d",
4572 sd->revision);
4573 if (!p) {
4574 errno = ENOMEM;
4575 return -1;
4577 n = strlen(p);
4578 } else {
4579 n = snprintf(buf, bufsize, "%d",
4580 sd->revision);
4584 if (!determine_size && n > bufsize) {
4585 errno = ERANGE;
4586 return -1;
4588 buf += n;
4589 n_used += n;
4590 bufsize -= n;
4591 n = 0;
4594 if (! exclude_nt_owner) {
4595 /* Get owner and group sid */
4596 if (sd->owner_sid) {
4597 convert_sid_to_string(ipc_cli, pol,
4598 sidstr,
4599 numeric,
4600 sd->owner_sid);
4601 } else {
4602 fstrcpy(sidstr, "");
4605 if (all || all_nt) {
4606 if (determine_size) {
4607 p = talloc_asprintf(ctx, ",OWNER:%s",
4608 sidstr);
4609 if (!p) {
4610 errno = ENOMEM;
4611 return -1;
4613 n = strlen(p);
4614 } else if (sidstr[0] != '\0') {
4615 n = snprintf(buf, bufsize,
4616 ",OWNER:%s", sidstr);
4618 } else if (StrnCaseCmp(name, "owner", 5) == 0) {
4619 if (determine_size) {
4620 p = talloc_asprintf(ctx, "%s", sidstr);
4621 if (!p) {
4622 errno = ENOMEM;
4623 return -1;
4625 n = strlen(p);
4626 } else {
4627 n = snprintf(buf, bufsize, "%s",
4628 sidstr);
4632 if (!determine_size && n > bufsize) {
4633 errno = ERANGE;
4634 return -1;
4636 buf += n;
4637 n_used += n;
4638 bufsize -= n;
4639 n = 0;
4642 if (! exclude_nt_group) {
4643 if (sd->group_sid) {
4644 convert_sid_to_string(ipc_cli, pol,
4645 sidstr, numeric,
4646 sd->group_sid);
4647 } else {
4648 fstrcpy(sidstr, "");
4651 if (all || all_nt) {
4652 if (determine_size) {
4653 p = talloc_asprintf(ctx, ",GROUP:%s",
4654 sidstr);
4655 if (!p) {
4656 errno = ENOMEM;
4657 return -1;
4659 n = strlen(p);
4660 } else if (sidstr[0] != '\0') {
4661 n = snprintf(buf, bufsize,
4662 ",GROUP:%s", sidstr);
4664 } else if (StrnCaseCmp(name, "group", 5) == 0) {
4665 if (determine_size) {
4666 p = talloc_asprintf(ctx, "%s", sidstr);
4667 if (!p) {
4668 errno = ENOMEM;
4669 return -1;
4671 n = strlen(p);
4672 } else {
4673 n = snprintf(buf, bufsize,
4674 "%s", sidstr);
4678 if (!determine_size && n > bufsize) {
4679 errno = ERANGE;
4680 return -1;
4682 buf += n;
4683 n_used += n;
4684 bufsize -= n;
4685 n = 0;
4688 if (! exclude_nt_acl) {
4689 /* Add aces to value buffer */
4690 for (i = 0; sd->dacl && i < sd->dacl->num_aces; i++) {
4692 SEC_ACE *ace = &sd->dacl->aces[i];
4693 convert_sid_to_string(ipc_cli, pol,
4694 sidstr, numeric,
4695 &ace->trustee);
4697 if (all || all_nt) {
4698 if (determine_size) {
4699 p = talloc_asprintf(
4700 ctx,
4701 ",ACL:"
4702 "%s:%d/%d/0x%08x",
4703 sidstr,
4704 ace->type,
4705 ace->flags,
4706 ace->access_mask);
4707 if (!p) {
4708 errno = ENOMEM;
4709 return -1;
4711 n = strlen(p);
4712 } else {
4713 n = snprintf(
4714 buf, bufsize,
4715 ",ACL:%s:%d/%d/0x%08x",
4716 sidstr,
4717 ace->type,
4718 ace->flags,
4719 ace->access_mask);
4721 } else if ((StrnCaseCmp(name, "acl", 3) == 0 &&
4722 StrCaseCmp(name+3, sidstr) == 0) ||
4723 (StrnCaseCmp(name, "acl+", 4) == 0 &&
4724 StrCaseCmp(name+4, sidstr) == 0)) {
4725 if (determine_size) {
4726 p = talloc_asprintf(
4727 ctx,
4728 "%d/%d/0x%08x",
4729 ace->type,
4730 ace->flags,
4731 ace->access_mask);
4732 if (!p) {
4733 errno = ENOMEM;
4734 return -1;
4736 n = strlen(p);
4737 } else {
4738 n = snprintf(buf, bufsize,
4739 "%d/%d/0x%08x",
4740 ace->type,
4741 ace->flags,
4742 ace->access_mask);
4744 } else if (all_nt_acls) {
4745 if (determine_size) {
4746 p = talloc_asprintf(
4747 ctx,
4748 "%s%s:%d/%d/0x%08x",
4749 i ? "," : "",
4750 sidstr,
4751 ace->type,
4752 ace->flags,
4753 ace->access_mask);
4754 if (!p) {
4755 errno = ENOMEM;
4756 return -1;
4758 n = strlen(p);
4759 } else {
4760 n = snprintf(buf, bufsize,
4761 "%s%s:%d/%d/0x%08x",
4762 i ? "," : "",
4763 sidstr,
4764 ace->type,
4765 ace->flags,
4766 ace->access_mask);
4769 if (!determine_size && n > bufsize) {
4770 errno = ERANGE;
4771 return -1;
4773 buf += n;
4774 n_used += n;
4775 bufsize -= n;
4776 n = 0;
4780 /* Restore name pointer to its original value */
4781 name -= 19;
4784 if (all || some_dos) {
4785 /* Point to the portion after "system.dos_attr." */
4786 name += 16; /* if (all) this will be invalid but unused */
4788 /* Obtain the DOS attributes */
4789 if (!smbc_getatr(context, srv, filename, &mode, &size,
4790 &create_time_ts,
4791 &access_time_ts,
4792 &write_time_ts,
4793 &change_time_ts,
4794 &ino)) {
4796 errno = smbc_errno(context, srv->cli);
4797 return -1;
4801 create_time = convert_timespec_to_time_t(create_time_ts);
4802 access_time = convert_timespec_to_time_t(access_time_ts);
4803 write_time = convert_timespec_to_time_t(write_time_ts);
4804 change_time = convert_timespec_to_time_t(change_time_ts);
4806 if (! exclude_dos_mode) {
4807 if (all || all_dos) {
4808 if (determine_size) {
4809 p = talloc_asprintf(ctx,
4810 "%sMODE:0x%x",
4811 (ipc_cli &&
4812 (all || some_nt)
4813 ? ","
4814 : ""),
4815 mode);
4816 if (!p) {
4817 errno = ENOMEM;
4818 return -1;
4820 n = strlen(p);
4821 } else {
4822 n = snprintf(buf, bufsize,
4823 "%sMODE:0x%x",
4824 (ipc_cli &&
4825 (all || some_nt)
4826 ? ","
4827 : ""),
4828 mode);
4830 } else if (StrCaseCmp(name, "mode") == 0) {
4831 if (determine_size) {
4832 p = talloc_asprintf(ctx, "0x%x", mode);
4833 if (!p) {
4834 errno = ENOMEM;
4835 return -1;
4837 n = strlen(p);
4838 } else {
4839 n = snprintf(buf, bufsize,
4840 "0x%x", mode);
4844 if (!determine_size && n > bufsize) {
4845 errno = ERANGE;
4846 return -1;
4848 buf += n;
4849 n_used += n;
4850 bufsize -= n;
4851 n = 0;
4854 if (! exclude_dos_size) {
4855 if (all || all_dos) {
4856 if (determine_size) {
4857 p = talloc_asprintf(
4858 ctx,
4859 ",SIZE:%.0f",
4860 (double)size);
4861 if (!p) {
4862 errno = ENOMEM;
4863 return -1;
4865 n = strlen(p);
4866 } else {
4867 n = snprintf(buf, bufsize,
4868 ",SIZE:%.0f",
4869 (double)size);
4871 } else if (StrCaseCmp(name, "size") == 0) {
4872 if (determine_size) {
4873 p = talloc_asprintf(
4874 ctx,
4875 "%.0f",
4876 (double)size);
4877 if (!p) {
4878 errno = ENOMEM;
4879 return -1;
4881 n = strlen(p);
4882 } else {
4883 n = snprintf(buf, bufsize,
4884 "%.0f",
4885 (double)size);
4889 if (!determine_size && n > bufsize) {
4890 errno = ERANGE;
4891 return -1;
4893 buf += n;
4894 n_used += n;
4895 bufsize -= n;
4896 n = 0;
4899 if (! exclude_dos_create_time &&
4900 attr_strings.create_time_attr != NULL) {
4901 if (all || all_dos) {
4902 if (determine_size) {
4903 p = talloc_asprintf(ctx,
4904 ",%s:%lu",
4905 attr_strings.create_time_attr,
4906 create_time);
4907 if (!p) {
4908 errno = ENOMEM;
4909 return -1;
4911 n = strlen(p);
4912 } else {
4913 n = snprintf(buf, bufsize,
4914 ",%s:%lu",
4915 attr_strings.create_time_attr,
4916 create_time);
4918 } else if (StrCaseCmp(name, attr_strings.create_time_attr) == 0) {
4919 if (determine_size) {
4920 p = talloc_asprintf(ctx, "%lu", create_time);
4921 if (!p) {
4922 errno = ENOMEM;
4923 return -1;
4925 n = strlen(p);
4926 } else {
4927 n = snprintf(buf, bufsize,
4928 "%lu", create_time);
4932 if (!determine_size && n > bufsize) {
4933 errno = ERANGE;
4934 return -1;
4936 buf += n;
4937 n_used += n;
4938 bufsize -= n;
4939 n = 0;
4942 if (! exclude_dos_access_time) {
4943 if (all || all_dos) {
4944 if (determine_size) {
4945 p = talloc_asprintf(ctx,
4946 ",%s:%lu",
4947 attr_strings.access_time_attr,
4948 access_time);
4949 if (!p) {
4950 errno = ENOMEM;
4951 return -1;
4953 n = strlen(p);
4954 } else {
4955 n = snprintf(buf, bufsize,
4956 ",%s:%lu",
4957 attr_strings.access_time_attr,
4958 access_time);
4960 } else if (StrCaseCmp(name, attr_strings.access_time_attr) == 0) {
4961 if (determine_size) {
4962 p = talloc_asprintf(ctx, "%lu", access_time);
4963 if (!p) {
4964 errno = ENOMEM;
4965 return -1;
4967 n = strlen(p);
4968 } else {
4969 n = snprintf(buf, bufsize,
4970 "%lu", access_time);
4974 if (!determine_size && n > bufsize) {
4975 errno = ERANGE;
4976 return -1;
4978 buf += n;
4979 n_used += n;
4980 bufsize -= n;
4981 n = 0;
4984 if (! exclude_dos_write_time) {
4985 if (all || all_dos) {
4986 if (determine_size) {
4987 p = talloc_asprintf(ctx,
4988 ",%s:%lu",
4989 attr_strings.write_time_attr,
4990 write_time);
4991 if (!p) {
4992 errno = ENOMEM;
4993 return -1;
4995 n = strlen(p);
4996 } else {
4997 n = snprintf(buf, bufsize,
4998 ",%s:%lu",
4999 attr_strings.write_time_attr,
5000 write_time);
5002 } else if (StrCaseCmp(name, attr_strings.write_time_attr) == 0) {
5003 if (determine_size) {
5004 p = talloc_asprintf(ctx, "%lu", write_time);
5005 if (!p) {
5006 errno = ENOMEM;
5007 return -1;
5009 n = strlen(p);
5010 } else {
5011 n = snprintf(buf, bufsize,
5012 "%lu", write_time);
5016 if (!determine_size && n > bufsize) {
5017 errno = ERANGE;
5018 return -1;
5020 buf += n;
5021 n_used += n;
5022 bufsize -= n;
5023 n = 0;
5026 if (! exclude_dos_change_time) {
5027 if (all || all_dos) {
5028 if (determine_size) {
5029 p = talloc_asprintf(ctx,
5030 ",%s:%lu",
5031 attr_strings.change_time_attr,
5032 change_time);
5033 if (!p) {
5034 errno = ENOMEM;
5035 return -1;
5037 n = strlen(p);
5038 } else {
5039 n = snprintf(buf, bufsize,
5040 ",%s:%lu",
5041 attr_strings.change_time_attr,
5042 change_time);
5044 } else if (StrCaseCmp(name, attr_strings.change_time_attr) == 0) {
5045 if (determine_size) {
5046 p = talloc_asprintf(ctx, "%lu", change_time);
5047 if (!p) {
5048 errno = ENOMEM;
5049 return -1;
5051 n = strlen(p);
5052 } else {
5053 n = snprintf(buf, bufsize,
5054 "%lu", change_time);
5058 if (!determine_size && n > bufsize) {
5059 errno = ERANGE;
5060 return -1;
5062 buf += n;
5063 n_used += n;
5064 bufsize -= n;
5065 n = 0;
5068 if (! exclude_dos_inode) {
5069 if (all || all_dos) {
5070 if (determine_size) {
5071 p = talloc_asprintf(
5072 ctx,
5073 ",INODE:%.0f",
5074 (double)ino);
5075 if (!p) {
5076 errno = ENOMEM;
5077 return -1;
5079 n = strlen(p);
5080 } else {
5081 n = snprintf(buf, bufsize,
5082 ",INODE:%.0f",
5083 (double) ino);
5085 } else if (StrCaseCmp(name, "inode") == 0) {
5086 if (determine_size) {
5087 p = talloc_asprintf(
5088 ctx,
5089 "%.0f",
5090 (double) ino);
5091 if (!p) {
5092 errno = ENOMEM;
5093 return -1;
5095 n = strlen(p);
5096 } else {
5097 n = snprintf(buf, bufsize,
5098 "%.0f",
5099 (double) ino);
5103 if (!determine_size && n > bufsize) {
5104 errno = ERANGE;
5105 return -1;
5107 buf += n;
5108 n_used += n;
5109 bufsize -= n;
5110 n = 0;
5113 /* Restore name pointer to its original value */
5114 name -= 16;
5117 if (n_used == 0) {
5118 errno = ENOATTR;
5119 return -1;
5122 return n_used;
5126 /*****************************************************
5127 set the ACLs on a file given an ascii description
5128 *******************************************************/
5129 static int
5130 cacl_set(TALLOC_CTX *ctx,
5131 struct cli_state *cli,
5132 struct cli_state *ipc_cli,
5133 POLICY_HND *pol,
5134 const char *filename,
5135 const char *the_acl,
5136 int mode,
5137 int flags)
5139 int fnum;
5140 int err = 0;
5141 SEC_DESC *sd = NULL, *old;
5142 SEC_ACL *dacl = NULL;
5143 DOM_SID *owner_sid = NULL;
5144 DOM_SID *group_sid = NULL;
5145 uint32 i, j;
5146 size_t sd_size;
5147 int ret = 0;
5148 char *p;
5149 BOOL numeric = True;
5151 /* the_acl will be null for REMOVE_ALL operations */
5152 if (the_acl) {
5153 numeric = ((p = strchr(the_acl, ':')) != NULL &&
5154 p > the_acl &&
5155 p[-1] != '+');
5157 /* if this is to set the entire ACL... */
5158 if (*the_acl == '*') {
5159 /* ... then increment past the first colon */
5160 the_acl = p + 1;
5163 sd = sec_desc_parse(ctx, ipc_cli, pol, numeric,
5164 CONST_DISCARD(char *, the_acl));
5166 if (!sd) {
5167 errno = EINVAL;
5168 return -1;
5172 /* SMBC_XATTR_MODE_REMOVE_ALL is the only caller
5173 that doesn't deref sd */
5175 if (!sd && (mode != SMBC_XATTR_MODE_REMOVE_ALL)) {
5176 errno = EINVAL;
5177 return -1;
5180 /* The desired access below is the only one I could find that works
5181 with NT4, W2KP and Samba */
5183 fnum = cli_nt_create(cli, filename, CREATE_ACCESS_READ);
5185 if (fnum == -1) {
5186 DEBUG(5, ("cacl_set failed to open %s: %s\n",
5187 filename, cli_errstr(cli)));
5188 errno = 0;
5189 return -1;
5192 old = cli_query_secdesc(cli, fnum, ctx);
5194 if (!old) {
5195 DEBUG(5, ("cacl_set Failed to query old descriptor\n"));
5196 errno = 0;
5197 return -1;
5200 cli_close(cli, fnum);
5202 switch (mode) {
5203 case SMBC_XATTR_MODE_REMOVE_ALL:
5204 old->dacl->num_aces = 0;
5205 dacl = old->dacl;
5206 break;
5208 case SMBC_XATTR_MODE_REMOVE:
5209 for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
5210 BOOL found = False;
5212 for (j=0;old->dacl && j<old->dacl->num_aces;j++) {
5213 if (sec_ace_equal(&sd->dacl->aces[i],
5214 &old->dacl->aces[j])) {
5215 uint32 k;
5216 for (k=j; k<old->dacl->num_aces-1;k++) {
5217 old->dacl->aces[k] =
5218 old->dacl->aces[k+1];
5220 old->dacl->num_aces--;
5221 found = True;
5222 dacl = old->dacl;
5223 break;
5227 if (!found) {
5228 err = ENOATTR;
5229 ret = -1;
5230 goto failed;
5233 break;
5235 case SMBC_XATTR_MODE_ADD:
5236 for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
5237 BOOL found = False;
5239 for (j=0;old->dacl && j<old->dacl->num_aces;j++) {
5240 if (sid_equal(&sd->dacl->aces[i].trustee,
5241 &old->dacl->aces[j].trustee)) {
5242 if (!(flags & SMBC_XATTR_FLAG_CREATE)) {
5243 err = EEXIST;
5244 ret = -1;
5245 goto failed;
5247 old->dacl->aces[j] = sd->dacl->aces[i];
5248 ret = -1;
5249 found = True;
5253 if (!found && (flags & SMBC_XATTR_FLAG_REPLACE)) {
5254 err = ENOATTR;
5255 ret = -1;
5256 goto failed;
5259 for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
5260 add_ace(&old->dacl, &sd->dacl->aces[i], ctx);
5263 dacl = old->dacl;
5264 break;
5266 case SMBC_XATTR_MODE_SET:
5267 old = sd;
5268 owner_sid = old->owner_sid;
5269 group_sid = old->group_sid;
5270 dacl = old->dacl;
5271 break;
5273 case SMBC_XATTR_MODE_CHOWN:
5274 owner_sid = sd->owner_sid;
5275 break;
5277 case SMBC_XATTR_MODE_CHGRP:
5278 group_sid = sd->group_sid;
5279 break;
5282 /* Denied ACE entries must come before allowed ones */
5283 sort_acl(old->dacl);
5285 /* Create new security descriptor and set it */
5286 sd = make_sec_desc(ctx, old->revision, SEC_DESC_SELF_RELATIVE,
5287 owner_sid, group_sid, NULL, dacl, &sd_size);
5289 fnum = cli_nt_create(cli, filename,
5290 WRITE_DAC_ACCESS | WRITE_OWNER_ACCESS);
5292 if (fnum == -1) {
5293 DEBUG(5, ("cacl_set failed to open %s: %s\n",
5294 filename, cli_errstr(cli)));
5295 errno = 0;
5296 return -1;
5299 if (!cli_set_secdesc(cli, fnum, sd)) {
5300 DEBUG(5, ("ERROR: secdesc set failed: %s\n", cli_errstr(cli)));
5301 ret = -1;
5304 /* Clean up */
5306 failed:
5307 cli_close(cli, fnum);
5309 if (err != 0) {
5310 errno = err;
5313 return ret;
5317 static int
5318 smbc_setxattr_ctx(SMBCCTX *context,
5319 const char *fname,
5320 const char *name,
5321 const void *value,
5322 size_t size,
5323 int flags)
5325 int ret;
5326 int ret2;
5327 SMBCSRV *srv;
5328 SMBCSRV *ipc_srv;
5329 fstring server;
5330 fstring share;
5331 fstring user;
5332 fstring password;
5333 fstring workgroup;
5334 pstring path;
5335 TALLOC_CTX *ctx;
5336 POLICY_HND pol;
5337 DOS_ATTR_DESC *dad;
5338 struct {
5339 const char * create_time_attr;
5340 const char * access_time_attr;
5341 const char * write_time_attr;
5342 const char * change_time_attr;
5343 } attr_strings;
5345 if (!context || !context->internal ||
5346 !context->internal->_initialized) {
5348 errno = EINVAL; /* Best I can think of ... */
5349 return -1;
5353 if (!fname) {
5355 errno = EINVAL;
5356 return -1;
5360 DEBUG(4, ("smbc_setxattr(%s, %s, %.*s)\n",
5361 fname, name, (int) size, (const char*)value));
5363 if (smbc_parse_path(context, fname,
5364 workgroup, sizeof(workgroup),
5365 server, sizeof(server),
5366 share, sizeof(share),
5367 path, sizeof(path),
5368 user, sizeof(user),
5369 password, sizeof(password),
5370 NULL, 0)) {
5371 errno = EINVAL;
5372 return -1;
5375 if (user[0] == (char)0) fstrcpy(user, context->user);
5377 srv = smbc_server(context, True,
5378 server, share, workgroup, user, password);
5379 if (!srv) {
5380 return -1; /* errno set by smbc_server */
5383 if (! srv->no_nt_session) {
5384 ipc_srv = smbc_attr_server(context, server, share,
5385 workgroup, user, password,
5386 &pol);
5387 if (! ipc_srv) {
5388 srv->no_nt_session = True;
5390 } else {
5391 ipc_srv = NULL;
5394 ctx = talloc_init("smbc_setxattr");
5395 if (!ctx) {
5396 errno = ENOMEM;
5397 return -1;
5401 * Are they asking to set the entire set of known attributes?
5403 if (StrCaseCmp(name, "system.*") == 0 ||
5404 StrCaseCmp(name, "system.*+") == 0) {
5405 /* Yup. */
5406 char *namevalue =
5407 talloc_asprintf(ctx, "%s:%s",
5408 name+7, (const char *) value);
5409 if (! namevalue) {
5410 errno = ENOMEM;
5411 ret = -1;
5412 return -1;
5415 if (ipc_srv) {
5416 ret = cacl_set(ctx, srv->cli,
5417 ipc_srv->cli, &pol, path,
5418 namevalue,
5419 (*namevalue == '*'
5420 ? SMBC_XATTR_MODE_SET
5421 : SMBC_XATTR_MODE_ADD),
5422 flags);
5423 } else {
5424 ret = 0;
5427 /* get a DOS Attribute Descriptor with current attributes */
5428 dad = dos_attr_query(context, ctx, path, srv);
5429 if (dad) {
5430 /* Overwrite old with new, using what was provided */
5431 dos_attr_parse(context, dad, srv, namevalue);
5433 /* Set the new DOS attributes */
5434 if (! smbc_setatr(context, srv, path,
5435 dad->create_time,
5436 dad->access_time,
5437 dad->write_time,
5438 dad->change_time,
5439 dad->mode)) {
5441 /* cause failure if NT failed too */
5442 dad = NULL;
5446 /* we only fail if both NT and DOS sets failed */
5447 if (ret < 0 && ! dad) {
5448 ret = -1; /* in case dad was null */
5450 else {
5451 ret = 0;
5454 talloc_destroy(ctx);
5455 return ret;
5459 * Are they asking to set an access control element or to set
5460 * the entire access control list?
5462 if (StrCaseCmp(name, "system.nt_sec_desc.*") == 0 ||
5463 StrCaseCmp(name, "system.nt_sec_desc.*+") == 0 ||
5464 StrCaseCmp(name, "system.nt_sec_desc.revision") == 0 ||
5465 StrnCaseCmp(name, "system.nt_sec_desc.acl", 22) == 0 ||
5466 StrnCaseCmp(name, "system.nt_sec_desc.acl+", 23) == 0) {
5468 /* Yup. */
5469 char *namevalue =
5470 talloc_asprintf(ctx, "%s:%s",
5471 name+19, (const char *) value);
5473 if (! ipc_srv) {
5474 ret = -1; /* errno set by smbc_server() */
5476 else if (! namevalue) {
5477 errno = ENOMEM;
5478 ret = -1;
5479 } else {
5480 ret = cacl_set(ctx, srv->cli,
5481 ipc_srv->cli, &pol, path,
5482 namevalue,
5483 (*namevalue == '*'
5484 ? SMBC_XATTR_MODE_SET
5485 : SMBC_XATTR_MODE_ADD),
5486 flags);
5488 talloc_destroy(ctx);
5489 return ret;
5493 * Are they asking to set the owner?
5495 if (StrCaseCmp(name, "system.nt_sec_desc.owner") == 0 ||
5496 StrCaseCmp(name, "system.nt_sec_desc.owner+") == 0) {
5498 /* Yup. */
5499 char *namevalue =
5500 talloc_asprintf(ctx, "%s:%s",
5501 name+19, (const char *) value);
5503 if (! ipc_srv) {
5505 ret = -1; /* errno set by smbc_server() */
5507 else if (! namevalue) {
5508 errno = ENOMEM;
5509 ret = -1;
5510 } else {
5511 ret = cacl_set(ctx, srv->cli,
5512 ipc_srv->cli, &pol, path,
5513 namevalue, SMBC_XATTR_MODE_CHOWN, 0);
5515 talloc_destroy(ctx);
5516 return ret;
5520 * Are they asking to set the group?
5522 if (StrCaseCmp(name, "system.nt_sec_desc.group") == 0 ||
5523 StrCaseCmp(name, "system.nt_sec_desc.group+") == 0) {
5525 /* Yup. */
5526 char *namevalue =
5527 talloc_asprintf(ctx, "%s:%s",
5528 name+19, (const char *) value);
5530 if (! ipc_srv) {
5531 /* errno set by smbc_server() */
5532 ret = -1;
5534 else if (! namevalue) {
5535 errno = ENOMEM;
5536 ret = -1;
5537 } else {
5538 ret = cacl_set(ctx, srv->cli,
5539 ipc_srv->cli, &pol, path,
5540 namevalue, SMBC_XATTR_MODE_CHOWN, 0);
5542 talloc_destroy(ctx);
5543 return ret;
5546 /* Determine whether to use old-style or new-style attribute names */
5547 if (context->internal->_full_time_names) {
5548 /* new-style names */
5549 attr_strings.create_time_attr = "system.dos_attr.CREATE_TIME";
5550 attr_strings.access_time_attr = "system.dos_attr.ACCESS_TIME";
5551 attr_strings.write_time_attr = "system.dos_attr.WRITE_TIME";
5552 attr_strings.change_time_attr = "system.dos_attr.CHANGE_TIME";
5553 } else {
5554 /* old-style names */
5555 attr_strings.create_time_attr = NULL;
5556 attr_strings.access_time_attr = "system.dos_attr.A_TIME";
5557 attr_strings.write_time_attr = "system.dos_attr.M_TIME";
5558 attr_strings.change_time_attr = "system.dos_attr.C_TIME";
5562 * Are they asking to set a DOS attribute?
5564 if (StrCaseCmp(name, "system.dos_attr.*") == 0 ||
5565 StrCaseCmp(name, "system.dos_attr.mode") == 0 ||
5566 (attr_strings.create_time_attr != NULL &&
5567 StrCaseCmp(name, attr_strings.create_time_attr) == 0) ||
5568 StrCaseCmp(name, attr_strings.access_time_attr) == 0 ||
5569 StrCaseCmp(name, attr_strings.write_time_attr) == 0 ||
5570 StrCaseCmp(name, attr_strings.change_time_attr) == 0) {
5572 /* get a DOS Attribute Descriptor with current attributes */
5573 dad = dos_attr_query(context, ctx, path, srv);
5574 if (dad) {
5575 char *namevalue =
5576 talloc_asprintf(ctx, "%s:%s",
5577 name+16, (const char *) value);
5578 if (! namevalue) {
5579 errno = ENOMEM;
5580 ret = -1;
5581 } else {
5582 /* Overwrite old with provided new params */
5583 dos_attr_parse(context, dad, srv, namevalue);
5585 /* Set the new DOS attributes */
5586 ret2 = smbc_setatr(context, srv, path,
5587 dad->create_time,
5588 dad->access_time,
5589 dad->write_time,
5590 dad->change_time,
5591 dad->mode);
5593 /* ret2 has True (success) / False (failure) */
5594 if (ret2) {
5595 ret = 0;
5596 } else {
5597 ret = -1;
5600 } else {
5601 ret = -1;
5604 talloc_destroy(ctx);
5605 return ret;
5608 /* Unsupported attribute name */
5609 talloc_destroy(ctx);
5610 errno = EINVAL;
5611 return -1;
5614 static int
5615 smbc_getxattr_ctx(SMBCCTX *context,
5616 const char *fname,
5617 const char *name,
5618 const void *value,
5619 size_t size)
5621 int ret;
5622 SMBCSRV *srv;
5623 SMBCSRV *ipc_srv;
5624 fstring server;
5625 fstring share;
5626 fstring user;
5627 fstring password;
5628 fstring workgroup;
5629 pstring path;
5630 TALLOC_CTX *ctx;
5631 POLICY_HND pol;
5632 struct {
5633 const char * create_time_attr;
5634 const char * access_time_attr;
5635 const char * write_time_attr;
5636 const char * change_time_attr;
5637 } attr_strings;
5640 if (!context || !context->internal ||
5641 !context->internal->_initialized) {
5643 errno = EINVAL; /* Best I can think of ... */
5644 return -1;
5648 if (!fname) {
5650 errno = EINVAL;
5651 return -1;
5655 DEBUG(4, ("smbc_getxattr(%s, %s)\n", fname, name));
5657 if (smbc_parse_path(context, fname,
5658 workgroup, sizeof(workgroup),
5659 server, sizeof(server),
5660 share, sizeof(share),
5661 path, sizeof(path),
5662 user, sizeof(user),
5663 password, sizeof(password),
5664 NULL, 0)) {
5665 errno = EINVAL;
5666 return -1;
5669 if (user[0] == (char)0) fstrcpy(user, context->user);
5671 srv = smbc_server(context, True,
5672 server, share, workgroup, user, password);
5673 if (!srv) {
5674 return -1; /* errno set by smbc_server */
5677 if (! srv->no_nt_session) {
5678 ipc_srv = smbc_attr_server(context, server, share,
5679 workgroup, user, password,
5680 &pol);
5681 if (! ipc_srv) {
5682 srv->no_nt_session = True;
5684 } else {
5685 ipc_srv = NULL;
5688 ctx = talloc_init("smbc:getxattr");
5689 if (!ctx) {
5690 errno = ENOMEM;
5691 return -1;
5694 /* Determine whether to use old-style or new-style attribute names */
5695 if (context->internal->_full_time_names) {
5696 /* new-style names */
5697 attr_strings.create_time_attr = "system.dos_attr.CREATE_TIME";
5698 attr_strings.access_time_attr = "system.dos_attr.ACCESS_TIME";
5699 attr_strings.write_time_attr = "system.dos_attr.WRITE_TIME";
5700 attr_strings.change_time_attr = "system.dos_attr.CHANGE_TIME";
5701 } else {
5702 /* old-style names */
5703 attr_strings.create_time_attr = NULL;
5704 attr_strings.access_time_attr = "system.dos_attr.A_TIME";
5705 attr_strings.write_time_attr = "system.dos_attr.M_TIME";
5706 attr_strings.change_time_attr = "system.dos_attr.C_TIME";
5709 /* Are they requesting a supported attribute? */
5710 if (StrCaseCmp(name, "system.*") == 0 ||
5711 StrnCaseCmp(name, "system.*!", 9) == 0 ||
5712 StrCaseCmp(name, "system.*+") == 0 ||
5713 StrnCaseCmp(name, "system.*+!", 10) == 0 ||
5714 StrCaseCmp(name, "system.nt_sec_desc.*") == 0 ||
5715 StrnCaseCmp(name, "system.nt_sec_desc.*!", 21) == 0 ||
5716 StrCaseCmp(name, "system.nt_sec_desc.*+") == 0 ||
5717 StrnCaseCmp(name, "system.nt_sec_desc.*+!", 22) == 0 ||
5718 StrCaseCmp(name, "system.nt_sec_desc.revision") == 0 ||
5719 StrCaseCmp(name, "system.nt_sec_desc.owner") == 0 ||
5720 StrCaseCmp(name, "system.nt_sec_desc.owner+") == 0 ||
5721 StrCaseCmp(name, "system.nt_sec_desc.group") == 0 ||
5722 StrCaseCmp(name, "system.nt_sec_desc.group+") == 0 ||
5723 StrnCaseCmp(name, "system.nt_sec_desc.acl", 22) == 0 ||
5724 StrnCaseCmp(name, "system.nt_sec_desc.acl+", 23) == 0 ||
5725 StrCaseCmp(name, "system.dos_attr.*") == 0 ||
5726 StrnCaseCmp(name, "system.dos_attr.*!", 18) == 0 ||
5727 StrCaseCmp(name, "system.dos_attr.mode") == 0 ||
5728 StrCaseCmp(name, "system.dos_attr.size") == 0 ||
5729 (attr_strings.create_time_attr != NULL &&
5730 StrCaseCmp(name, attr_strings.create_time_attr) == 0) ||
5731 StrCaseCmp(name, attr_strings.access_time_attr) == 0 ||
5732 StrCaseCmp(name, attr_strings.write_time_attr) == 0 ||
5733 StrCaseCmp(name, attr_strings.change_time_attr) == 0 ||
5734 StrCaseCmp(name, "system.dos_attr.inode") == 0) {
5736 /* Yup. */
5737 ret = cacl_get(context, ctx, srv,
5738 ipc_srv == NULL ? NULL : ipc_srv->cli,
5739 &pol, path,
5740 CONST_DISCARD(char *, name),
5741 CONST_DISCARD(char *, value), size);
5742 if (ret < 0 && errno == 0) {
5743 errno = smbc_errno(context, srv->cli);
5745 talloc_destroy(ctx);
5746 return ret;
5749 /* Unsupported attribute name */
5750 talloc_destroy(ctx);
5751 errno = EINVAL;
5752 return -1;
5756 static int
5757 smbc_removexattr_ctx(SMBCCTX *context,
5758 const char *fname,
5759 const char *name)
5761 int ret;
5762 SMBCSRV *srv;
5763 SMBCSRV *ipc_srv;
5764 fstring server;
5765 fstring share;
5766 fstring user;
5767 fstring password;
5768 fstring workgroup;
5769 pstring path;
5770 TALLOC_CTX *ctx;
5771 POLICY_HND pol;
5773 if (!context || !context->internal ||
5774 !context->internal->_initialized) {
5776 errno = EINVAL; /* Best I can think of ... */
5777 return -1;
5781 if (!fname) {
5783 errno = EINVAL;
5784 return -1;
5788 DEBUG(4, ("smbc_removexattr(%s, %s)\n", fname, name));
5790 if (smbc_parse_path(context, fname,
5791 workgroup, sizeof(workgroup),
5792 server, sizeof(server),
5793 share, sizeof(share),
5794 path, sizeof(path),
5795 user, sizeof(user),
5796 password, sizeof(password),
5797 NULL, 0)) {
5798 errno = EINVAL;
5799 return -1;
5802 if (user[0] == (char)0) fstrcpy(user, context->user);
5804 srv = smbc_server(context, True,
5805 server, share, workgroup, user, password);
5806 if (!srv) {
5807 return -1; /* errno set by smbc_server */
5810 if (! srv->no_nt_session) {
5811 ipc_srv = smbc_attr_server(context, server, share,
5812 workgroup, user, password,
5813 &pol);
5814 if (! ipc_srv) {
5815 srv->no_nt_session = True;
5817 } else {
5818 ipc_srv = NULL;
5821 if (! ipc_srv) {
5822 return -1; /* errno set by smbc_attr_server */
5825 ctx = talloc_init("smbc_removexattr");
5826 if (!ctx) {
5827 errno = ENOMEM;
5828 return -1;
5831 /* Are they asking to set the entire ACL? */
5832 if (StrCaseCmp(name, "system.nt_sec_desc.*") == 0 ||
5833 StrCaseCmp(name, "system.nt_sec_desc.*+") == 0) {
5835 /* Yup. */
5836 ret = cacl_set(ctx, srv->cli,
5837 ipc_srv->cli, &pol, path,
5838 NULL, SMBC_XATTR_MODE_REMOVE_ALL, 0);
5839 talloc_destroy(ctx);
5840 return ret;
5844 * Are they asking to remove one or more spceific security descriptor
5845 * attributes?
5847 if (StrCaseCmp(name, "system.nt_sec_desc.revision") == 0 ||
5848 StrCaseCmp(name, "system.nt_sec_desc.owner") == 0 ||
5849 StrCaseCmp(name, "system.nt_sec_desc.owner+") == 0 ||
5850 StrCaseCmp(name, "system.nt_sec_desc.group") == 0 ||
5851 StrCaseCmp(name, "system.nt_sec_desc.group+") == 0 ||
5852 StrnCaseCmp(name, "system.nt_sec_desc.acl", 22) == 0 ||
5853 StrnCaseCmp(name, "system.nt_sec_desc.acl+", 23) == 0) {
5855 /* Yup. */
5856 ret = cacl_set(ctx, srv->cli,
5857 ipc_srv->cli, &pol, path,
5858 name + 19, SMBC_XATTR_MODE_REMOVE, 0);
5859 talloc_destroy(ctx);
5860 return ret;
5863 /* Unsupported attribute name */
5864 talloc_destroy(ctx);
5865 errno = EINVAL;
5866 return -1;
5869 static int
5870 smbc_listxattr_ctx(SMBCCTX *context,
5871 const char *fname,
5872 char *list,
5873 size_t size)
5876 * This isn't quite what listxattr() is supposed to do. This returns
5877 * the complete set of attribute names, always, rather than only those
5878 * attribute names which actually exist for a file. Hmmm...
5880 const char supported_old[] =
5881 "system.*\0"
5882 "system.*+\0"
5883 "system.nt_sec_desc.revision\0"
5884 "system.nt_sec_desc.owner\0"
5885 "system.nt_sec_desc.owner+\0"
5886 "system.nt_sec_desc.group\0"
5887 "system.nt_sec_desc.group+\0"
5888 "system.nt_sec_desc.acl.*\0"
5889 "system.nt_sec_desc.acl\0"
5890 "system.nt_sec_desc.acl+\0"
5891 "system.nt_sec_desc.*\0"
5892 "system.nt_sec_desc.*+\0"
5893 "system.dos_attr.*\0"
5894 "system.dos_attr.mode\0"
5895 "system.dos_attr.c_time\0"
5896 "system.dos_attr.a_time\0"
5897 "system.dos_attr.m_time\0"
5899 const char supported_new[] =
5900 "system.*\0"
5901 "system.*+\0"
5902 "system.nt_sec_desc.revision\0"
5903 "system.nt_sec_desc.owner\0"
5904 "system.nt_sec_desc.owner+\0"
5905 "system.nt_sec_desc.group\0"
5906 "system.nt_sec_desc.group+\0"
5907 "system.nt_sec_desc.acl.*\0"
5908 "system.nt_sec_desc.acl\0"
5909 "system.nt_sec_desc.acl+\0"
5910 "system.nt_sec_desc.*\0"
5911 "system.nt_sec_desc.*+\0"
5912 "system.dos_attr.*\0"
5913 "system.dos_attr.mode\0"
5914 "system.dos_attr.create_time\0"
5915 "system.dos_attr.access_time\0"
5916 "system.dos_attr.write_time\0"
5917 "system.dos_attr.change_time\0"
5919 const char * supported;
5921 if (context->internal->_full_time_names) {
5922 supported = supported_new;
5923 } else {
5924 supported = supported_old;
5927 if (size == 0) {
5928 return sizeof(supported);
5931 if (sizeof(supported) > size) {
5932 errno = ERANGE;
5933 return -1;
5936 /* this can't be strcpy() because there are embedded null characters */
5937 memcpy(list, supported, sizeof(supported));
5938 return sizeof(supported);
5943 * Open a print file to be written to by other calls
5946 static SMBCFILE *
5947 smbc_open_print_job_ctx(SMBCCTX *context,
5948 const char *fname)
5950 fstring server;
5951 fstring share;
5952 fstring user;
5953 fstring password;
5954 pstring path;
5956 if (!context || !context->internal ||
5957 !context->internal->_initialized) {
5959 errno = EINVAL;
5960 return NULL;
5964 if (!fname) {
5966 errno = EINVAL;
5967 return NULL;
5971 DEBUG(4, ("smbc_open_print_job_ctx(%s)\n", fname));
5973 if (smbc_parse_path(context, fname,
5974 NULL, 0,
5975 server, sizeof(server),
5976 share, sizeof(share),
5977 path, sizeof(path),
5978 user, sizeof(user),
5979 password, sizeof(password),
5980 NULL, 0)) {
5981 errno = EINVAL;
5982 return NULL;
5985 /* What if the path is empty, or the file exists? */
5987 return (context->open)(context, fname, O_WRONLY, 666);
5992 * Routine to print a file on a remote server ...
5994 * We open the file, which we assume to be on a remote server, and then
5995 * copy it to a print file on the share specified by printq.
5998 static int
5999 smbc_print_file_ctx(SMBCCTX *c_file,
6000 const char *fname,
6001 SMBCCTX *c_print,
6002 const char *printq)
6004 SMBCFILE *fid1;
6005 SMBCFILE *fid2;
6006 int bytes;
6007 int saverr;
6008 int tot_bytes = 0;
6009 char buf[4096];
6011 if (!c_file || !c_file->internal->_initialized || !c_print ||
6012 !c_print->internal->_initialized) {
6014 errno = EINVAL;
6015 return -1;
6019 if (!fname && !printq) {
6021 errno = EINVAL;
6022 return -1;
6026 /* Try to open the file for reading ... */
6028 if ((long)(fid1 = (c_file->open)(c_file, fname, O_RDONLY, 0666)) < 0) {
6030 DEBUG(3, ("Error, fname=%s, errno=%i\n", fname, errno));
6031 return -1; /* smbc_open sets errno */
6035 /* Now, try to open the printer file for writing */
6037 if ((long)(fid2 = (c_print->open_print_job)(c_print, printq)) < 0) {
6039 saverr = errno; /* Save errno */
6040 (c_file->close_fn)(c_file, fid1);
6041 errno = saverr;
6042 return -1;
6046 while ((bytes = (c_file->read)(c_file, fid1, buf, sizeof(buf))) > 0) {
6048 tot_bytes += bytes;
6050 if (((c_print->write)(c_print, fid2, buf, bytes)) < 0) {
6052 saverr = errno;
6053 (c_file->close_fn)(c_file, fid1);
6054 (c_print->close_fn)(c_print, fid2);
6055 errno = saverr;
6061 saverr = errno;
6063 (c_file->close_fn)(c_file, fid1); /* We have to close these anyway */
6064 (c_print->close_fn)(c_print, fid2);
6066 if (bytes < 0) {
6068 errno = saverr;
6069 return -1;
6073 return tot_bytes;
6078 * Routine to list print jobs on a printer share ...
6081 static int
6082 smbc_list_print_jobs_ctx(SMBCCTX *context,
6083 const char *fname,
6084 smbc_list_print_job_fn fn)
6086 SMBCSRV *srv;
6087 fstring server;
6088 fstring share;
6089 fstring user;
6090 fstring password;
6091 fstring workgroup;
6092 pstring path;
6094 if (!context || !context->internal ||
6095 !context->internal->_initialized) {
6097 errno = EINVAL;
6098 return -1;
6102 if (!fname) {
6104 errno = EINVAL;
6105 return -1;
6109 DEBUG(4, ("smbc_list_print_jobs(%s)\n", fname));
6111 if (smbc_parse_path(context, fname,
6112 workgroup, sizeof(workgroup),
6113 server, sizeof(server),
6114 share, sizeof(share),
6115 path, sizeof(path),
6116 user, sizeof(user),
6117 password, sizeof(password),
6118 NULL, 0)) {
6119 errno = EINVAL;
6120 return -1;
6123 if (user[0] == (char)0) fstrcpy(user, context->user);
6125 srv = smbc_server(context, True,
6126 server, share, workgroup, user, password);
6128 if (!srv) {
6130 return -1; /* errno set by smbc_server */
6134 if (cli_print_queue(srv->cli,
6135 (void (*)(struct print_job_info *))fn) < 0) {
6137 errno = smbc_errno(context, srv->cli);
6138 return -1;
6142 return 0;
6147 * Delete a print job from a remote printer share
6150 static int
6151 smbc_unlink_print_job_ctx(SMBCCTX *context,
6152 const char *fname,
6153 int id)
6155 SMBCSRV *srv;
6156 fstring server;
6157 fstring share;
6158 fstring user;
6159 fstring password;
6160 fstring workgroup;
6161 pstring path;
6162 int err;
6164 if (!context || !context->internal ||
6165 !context->internal->_initialized) {
6167 errno = EINVAL;
6168 return -1;
6172 if (!fname) {
6174 errno = EINVAL;
6175 return -1;
6179 DEBUG(4, ("smbc_unlink_print_job(%s)\n", fname));
6181 if (smbc_parse_path(context, fname,
6182 workgroup, sizeof(workgroup),
6183 server, sizeof(server),
6184 share, sizeof(share),
6185 path, sizeof(path),
6186 user, sizeof(user),
6187 password, sizeof(password),
6188 NULL, 0)) {
6189 errno = EINVAL;
6190 return -1;
6193 if (user[0] == (char)0) fstrcpy(user, context->user);
6195 srv = smbc_server(context, True,
6196 server, share, workgroup, user, password);
6198 if (!srv) {
6200 return -1; /* errno set by smbc_server */
6204 if ((err = cli_printjob_del(srv->cli, id)) != 0) {
6206 if (err < 0)
6207 errno = smbc_errno(context, srv->cli);
6208 else if (err == ERRnosuchprintjob)
6209 errno = EINVAL;
6210 return -1;
6214 return 0;
6219 * Get a new empty handle to fill in with your own info
6221 SMBCCTX *
6222 smbc_new_context(void)
6224 SMBCCTX *context;
6226 context = SMB_MALLOC_P(SMBCCTX);
6227 if (!context) {
6228 errno = ENOMEM;
6229 return NULL;
6232 ZERO_STRUCTP(context);
6234 context->internal = SMB_MALLOC_P(struct smbc_internal_data);
6235 if (!context->internal) {
6236 SAFE_FREE(context);
6237 errno = ENOMEM;
6238 return NULL;
6241 ZERO_STRUCTP(context->internal);
6244 /* ADD REASONABLE DEFAULTS */
6245 context->debug = 0;
6246 context->timeout = 20000; /* 20 seconds */
6248 context->options.browse_max_lmb_count = 3; /* # LMBs to query */
6249 context->options.urlencode_readdir_entries = False;/* backward compat */
6250 context->options.one_share_per_server = False;/* backward compat */
6251 context->internal->_share_mode = SMBC_SHAREMODE_DENY_NONE;
6252 /* backward compat */
6254 context->open = smbc_open_ctx;
6255 context->creat = smbc_creat_ctx;
6256 context->read = smbc_read_ctx;
6257 context->write = smbc_write_ctx;
6258 context->close_fn = smbc_close_ctx;
6259 context->unlink = smbc_unlink_ctx;
6260 context->rename = smbc_rename_ctx;
6261 context->lseek = smbc_lseek_ctx;
6262 context->stat = smbc_stat_ctx;
6263 context->fstat = smbc_fstat_ctx;
6264 context->opendir = smbc_opendir_ctx;
6265 context->closedir = smbc_closedir_ctx;
6266 context->readdir = smbc_readdir_ctx;
6267 context->getdents = smbc_getdents_ctx;
6268 context->mkdir = smbc_mkdir_ctx;
6269 context->rmdir = smbc_rmdir_ctx;
6270 context->telldir = smbc_telldir_ctx;
6271 context->lseekdir = smbc_lseekdir_ctx;
6272 context->fstatdir = smbc_fstatdir_ctx;
6273 context->chmod = smbc_chmod_ctx;
6274 context->utimes = smbc_utimes_ctx;
6275 context->setxattr = smbc_setxattr_ctx;
6276 context->getxattr = smbc_getxattr_ctx;
6277 context->removexattr = smbc_removexattr_ctx;
6278 context->listxattr = smbc_listxattr_ctx;
6279 context->open_print_job = smbc_open_print_job_ctx;
6280 context->print_file = smbc_print_file_ctx;
6281 context->list_print_jobs = smbc_list_print_jobs_ctx;
6282 context->unlink_print_job = smbc_unlink_print_job_ctx;
6284 context->callbacks.check_server_fn = smbc_check_server;
6285 context->callbacks.remove_unused_server_fn = smbc_remove_unused_server;
6287 smbc_default_cache_functions(context);
6289 return context;
6293 * Free a context
6295 * Returns 0 on success. Otherwise returns 1, the SMBCCTX is _not_ freed
6296 * and thus you'll be leaking memory if not handled properly.
6300 smbc_free_context(SMBCCTX *context,
6301 int shutdown_ctx)
6303 if (!context) {
6304 errno = EBADF;
6305 return 1;
6308 if (shutdown_ctx) {
6309 SMBCFILE * f;
6310 DEBUG(1,("Performing aggressive shutdown.\n"));
6312 f = context->internal->_files;
6313 while (f) {
6314 (context->close_fn)(context, f);
6315 f = f->next;
6317 context->internal->_files = NULL;
6319 /* First try to remove the servers the nice way. */
6320 if (context->callbacks.purge_cached_fn(context)) {
6321 SMBCSRV * s;
6322 SMBCSRV * next;
6323 DEBUG(1, ("Could not purge all servers, "
6324 "Nice way shutdown failed.\n"));
6325 s = context->internal->_servers;
6326 while (s) {
6327 DEBUG(1, ("Forced shutdown: %p (fd=%d)\n",
6328 s, s->cli->fd));
6329 cli_shutdown(s->cli);
6330 (context->callbacks.remove_cached_srv_fn)(context,
6332 next = s->next;
6333 DLIST_REMOVE(context->internal->_servers, s);
6334 SAFE_FREE(s);
6335 s = next;
6337 context->internal->_servers = NULL;
6340 else {
6341 /* This is the polite way */
6342 if ((context->callbacks.purge_cached_fn)(context)) {
6343 DEBUG(1, ("Could not purge all servers, "
6344 "free_context failed.\n"));
6345 errno = EBUSY;
6346 return 1;
6348 if (context->internal->_servers) {
6349 DEBUG(1, ("Active servers in context, "
6350 "free_context failed.\n"));
6351 errno = EBUSY;
6352 return 1;
6354 if (context->internal->_files) {
6355 DEBUG(1, ("Active files in context, "
6356 "free_context failed.\n"));
6357 errno = EBUSY;
6358 return 1;
6362 /* Things we have to clean up */
6363 SAFE_FREE(context->workgroup);
6364 SAFE_FREE(context->netbios_name);
6365 SAFE_FREE(context->user);
6367 DEBUG(3, ("Context %p succesfully freed\n", context));
6368 SAFE_FREE(context->internal);
6369 SAFE_FREE(context);
6370 return 0;
6375 * Each time the context structure is changed, we have binary backward
6376 * compatibility issues. Instead of modifying the public portions of the
6377 * context structure to add new options, instead, we put them in the internal
6378 * portion of the context structure and provide a set function for these new
6379 * options.
6381 void
6382 smbc_option_set(SMBCCTX *context,
6383 char *option_name,
6384 ... /* option_value */)
6386 va_list ap;
6387 union {
6388 int i;
6389 BOOL b;
6390 smbc_get_auth_data_with_context_fn auth_fn;
6391 void *v;
6392 } option_value;
6394 va_start(ap, option_name);
6396 if (strcmp(option_name, "debug_to_stderr") == 0) {
6398 * Log to standard error instead of standard output.
6400 option_value.b = (BOOL) va_arg(ap, int);
6401 context->internal->_debug_stderr = option_value.b;
6403 } else if (strcmp(option_name, "full_time_names") == 0) {
6405 * Use new-style time attribute names, e.g. WRITE_TIME rather
6406 * than the old-style names such as M_TIME. This allows also
6407 * setting/getting CREATE_TIME which was previously
6408 * unimplemented. (Note that the old C_TIME was supposed to
6409 * be CHANGE_TIME but was confused and sometimes referred to
6410 * CREATE_TIME.)
6412 option_value.b = (BOOL) va_arg(ap, int);
6413 context->internal->_full_time_names = option_value.b;
6415 } else if (strcmp(option_name, "open_share_mode") == 0) {
6417 * The share mode to use for files opened with
6418 * smbc_open_ctx(). The default is SMBC_SHAREMODE_DENY_NONE.
6420 option_value.i = va_arg(ap, int);
6421 context->internal->_share_mode =
6422 (smbc_share_mode) option_value.i;
6424 } else if (strcmp(option_name, "auth_function") == 0) {
6426 * Use the new-style authentication function which includes
6427 * the context.
6429 option_value.auth_fn =
6430 va_arg(ap, smbc_get_auth_data_with_context_fn);
6431 context->internal->_auth_fn_with_context =
6432 option_value.auth_fn;
6433 } else if (strcmp(option_name, "user_data") == 0) {
6435 * Save a user data handle which may be retrieved by the user
6436 * with smbc_option_get()
6438 option_value.v = va_arg(ap, void *);
6439 context->internal->_user_data = option_value.v;
6442 va_end(ap);
6447 * Retrieve the current value of an option
6449 void *
6450 smbc_option_get(SMBCCTX *context,
6451 char *option_name)
6453 if (strcmp(option_name, "debug_stderr") == 0) {
6455 * Log to standard error instead of standard output.
6457 #if defined(__intptr_t_defined) || defined(HAVE_INTPTR_T)
6458 return (void *) (intptr_t) context->internal->_debug_stderr;
6459 #else
6460 return (void *) context->internal->_debug_stderr;
6461 #endif
6462 } else if (strcmp(option_name, "full_time_names") == 0) {
6464 * Use new-style time attribute names, e.g. WRITE_TIME rather
6465 * than the old-style names such as M_TIME. This allows also
6466 * setting/getting CREATE_TIME which was previously
6467 * unimplemented. (Note that the old C_TIME was supposed to
6468 * be CHANGE_TIME but was confused and sometimes referred to
6469 * CREATE_TIME.)
6471 #if defined(__intptr_t_defined) || defined(HAVE_INTPTR_T)
6472 return (void *) (intptr_t) context->internal->_full_time_names;
6473 #else
6474 return (void *) context->internal->_full_time_names;
6475 #endif
6477 } else if (strcmp(option_name, "auth_function") == 0) {
6479 * Use the new-style authentication function which includes
6480 * the context.
6482 return (void *) context->internal->_auth_fn_with_context;
6483 } else if (strcmp(option_name, "user_data") == 0) {
6485 * Save a user data handle which may be retrieved by the user
6486 * with smbc_option_get()
6488 return context->internal->_user_data;
6491 return NULL;
6496 * Initialise the library etc
6498 * We accept a struct containing handle information.
6499 * valid values for info->debug from 0 to 100,
6500 * and insist that info->fn must be non-null.
6502 SMBCCTX *
6503 smbc_init_context(SMBCCTX *context)
6505 pstring conf;
6506 int pid;
6507 char *user = NULL;
6508 char *home = NULL;
6510 if (!context || !context->internal) {
6511 errno = EBADF;
6512 return NULL;
6515 /* Do not initialise the same client twice */
6516 if (context->internal->_initialized) {
6517 return 0;
6520 if ((!context->callbacks.auth_fn &&
6521 !context->internal->_auth_fn_with_context) ||
6522 context->debug < 0 ||
6523 context->debug > 100) {
6525 errno = EINVAL;
6526 return NULL;
6530 if (!smbc_initialized) {
6532 * Do some library-wide intializations the first time we get
6533 * called
6535 BOOL conf_loaded = False;
6537 /* Set this to what the user wants */
6538 DEBUGLEVEL = context->debug;
6540 load_case_tables();
6542 setup_logging("libsmbclient", True);
6543 if (context->internal->_debug_stderr) {
6544 dbf = x_stderr;
6545 x_setbuf(x_stderr, NULL);
6548 /* Here we would open the smb.conf file if needed ... */
6550 in_client = True; /* FIXME, make a param */
6552 home = getenv("HOME");
6553 if (home) {
6554 slprintf(conf, sizeof(conf), "%s/.smb/smb.conf", home);
6555 if (lp_load(conf, True, False, False, True)) {
6556 conf_loaded = True;
6557 } else {
6558 DEBUG(5, ("Could not load config file: %s\n",
6559 conf));
6563 if (!conf_loaded) {
6565 * Well, if that failed, try the dyn_CONFIGFILE
6566 * Which points to the standard locn, and if that
6567 * fails, silently ignore it and use the internal
6568 * defaults ...
6571 if (!lp_load(dyn_CONFIGFILE, True, False, False, False)) {
6572 DEBUG(5, ("Could not load config file: %s\n",
6573 dyn_CONFIGFILE));
6574 } else if (home) {
6576 * We loaded the global config file. Now lets
6577 * load user-specific modifications to the
6578 * global config.
6580 slprintf(conf, sizeof(conf),
6581 "%s/.smb/smb.conf.append", home);
6582 if (!lp_load(conf, True, False, False, False)) {
6583 DEBUG(10,
6584 ("Could not append config file: "
6585 "%s\n",
6586 conf));
6591 load_interfaces(); /* Load the list of interfaces ... */
6593 reopen_logs(); /* Get logging working ... */
6596 * Block SIGPIPE (from lib/util_sock.c: write())
6597 * It is not needed and should not stop execution
6599 BlockSignals(True, SIGPIPE);
6601 /* Done with one-time initialisation */
6602 smbc_initialized = 1;
6606 if (!context->user) {
6608 * FIXME: Is this the best way to get the user info?
6610 user = getenv("USER");
6611 /* walk around as "guest" if no username can be found */
6612 if (!user) context->user = SMB_STRDUP("guest");
6613 else context->user = SMB_STRDUP(user);
6616 if (!context->netbios_name) {
6618 * We try to get our netbios name from the config. If that
6619 * fails we fall back on constructing our netbios name from
6620 * our hostname etc
6622 if (global_myname()) {
6623 context->netbios_name = SMB_STRDUP(global_myname());
6625 else {
6627 * Hmmm, I want to get hostname as well, but I am too
6628 * lazy for the moment
6630 pid = sys_getpid();
6631 context->netbios_name = (char *)SMB_MALLOC(17);
6632 if (!context->netbios_name) {
6633 errno = ENOMEM;
6634 return NULL;
6636 slprintf(context->netbios_name, 16,
6637 "smbc%s%d", context->user, pid);
6641 DEBUG(1, ("Using netbios name %s.\n", context->netbios_name));
6643 if (!context->workgroup) {
6644 if (lp_workgroup()) {
6645 context->workgroup = SMB_STRDUP(lp_workgroup());
6647 else {
6648 /* TODO: Think about a decent default workgroup */
6649 context->workgroup = SMB_STRDUP("samba");
6653 DEBUG(1, ("Using workgroup %s.\n", context->workgroup));
6655 /* shortest timeout is 1 second */
6656 if (context->timeout > 0 && context->timeout < 1000)
6657 context->timeout = 1000;
6660 * FIXME: Should we check the function pointers here?
6663 context->internal->_initialized = True;
6665 return context;
6669 /* Return the verion of samba, and thus libsmbclient */
6670 const char *
6671 smbc_version(void)
6673 return samba_version_string();