source: trunk/samba/source/libsmb/libsmbclient.c@ 22

Last change on this file since 22 was 22, checked in by Yuri Dario, 18 years ago

Source code upgrade to 3.0.25pre2.

File size: 202.5 KB
Line 
1/*
2 Unix SMB/Netbios implementation.
3 SMB client library implementation
4 Copyright (C) Andrew Tridgell 1998
5 Copyright (C) Richard Sharpe 2000, 2002
6 Copyright (C) John Terpstra 2000
7 Copyright (C) Tom Jansen (Ninja ISD) 2002
8 Copyright (C) Derrell Lipman 2003, 2004
9
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.
14
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.
19
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.
23*/
24
25#include "includes.h"
26
27#include "include/libsmb_internal.h"
28
29struct smbc_dirent *smbc_readdir_ctx(SMBCCTX *context, SMBCFILE *dir);
30struct smbc_dir_list *smbc_check_dir_ent(struct smbc_dir_list *list,
31 struct smbc_dirent *dirent);
32
33/*
34 * DOS Attribute values (used internally)
35 */
36typedef 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;
45
46
47/*
48 * Internal flags for extended attributes
49 */
50
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
58
59#define CREATE_ACCESS_READ READ_CONTROL_ACCESS
60
61/*We should test for this in configure ... */
62#ifndef ENOTSUP
63#define ENOTSUP EOPNOTSUPP
64#endif
65
66/*
67 * Functions exported by libsmb_cache.c that we need here
68 */
69int smbc_default_cache_functions(SMBCCTX *context);
70
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
76 */
77static 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;
84}
85
86/*
87 * Find an lsa pipe handle associated with a cli struct.
88 */
89static struct rpc_pipe_client *
90find_lsa_pipe_hnd(struct cli_state *ipc_cli)
91{
92 struct rpc_pipe_client *pipe_hnd;
93
94 for (pipe_hnd = ipc_cli->pipe_list;
95 pipe_hnd;
96 pipe_hnd = pipe_hnd->next) {
97
98 if (pipe_hnd->pipe_idx == PI_LSARPC) {
99 return pipe_hnd;
100 }
101 }
102
103 return NULL;
104}
105
106static int
107smbc_close_ctx(SMBCCTX *context,
108 SMBCFILE *file);
109static off_t
110smbc_lseek_ctx(SMBCCTX *context,
111 SMBCFILE *file,
112 off_t offset,
113 int whence);
114
115extern BOOL in_client;
116
117/*
118 * Is the logging working / configfile read ?
119 */
120static int smbc_initialized = 0;
121
122static int
123hex2int( unsigned int _char )
124{
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;
132}
133
134/*
135 * smbc_urldecode()
136 *
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.
139 *
140 * dest may, but need not be, the same pointer as src.
141 *
142 * Returns the number of % sequences which could not be converted due to lack
143 * of two following hexadecimal digits.
144 */
145int
146smbc_urldecode(char *dest, char * src, size_t max_dest_len)
147{
148 int old_length = strlen(src);
149 int i = 0;
150 int err_count = 0;
151 pstring temp;
152 char * p;
153
154 if ( old_length == 0 ) {
155 return 0;
156 }
157
158 p = temp;
159 while ( i < old_length ) {
160 unsigned char character = src[ i++ ];
161
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;
165
166 /* Replace valid sequence */
167 if (a != -1 && b != -1) {
168
169 /* Replace valid %xx sequence with %dd */
170 character = (a * 16) + b;
171
172 if (character == '\0') {
173 break; /* Stop at %00 */
174 }
175
176 i += 2;
177 } else {
178
179 err_count++;
180 }
181 }
182
183 *p++ = character;
184 }
185
186 *p = '\0';
187
188 strncpy(dest, temp, max_dest_len - 1);
189 dest[max_dest_len - 1] = '\0';
190
191 return err_count;
192}
193
194/*
195 * smbc_urlencode()
196 *
197 * Convert any characters not specifically allowed in a URL into their %xx
198 * equivalent.
199 *
200 * Returns the remaining buffer length.
201 */
202int
203smbc_urlencode(char * dest, char * src, int max_dest_len)
204{
205 char hex[] = "0123456789ABCDEF";
206
207 for (; *src != '\0' && max_dest_len >= 3; src++) {
208
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--;
225 }
226 }
227
228 *dest++ = '\0';
229 max_dest_len--;
230
231 return max_dest_len;
232}
233
234/*
235 * Function to parse a path and turn it into components
236 *
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:").
240 *
241 *
242 * We accept:
243 * smb://[[[domain;]user[:password]@]server[/share[/path[/file]]]][?options]
244 *
245 * Meaning of URLs:
246 *
247 * smb:// Show all workgroups.
248 *
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.
261 *
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 ...
265 *
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.
269 */
270
271static const char *smbc_prefix = "smb:";
272
273static int
274smbc_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)
283{
284 static pstring s;
285 pstring userinfo;
286 const char *p;
287 char *q, *r;
288 int len;
289
290 server[0] = share[0] = path[0] = user[0] = password[0] = (char)0;
291
292 /*
293 * Assume we wont find an authentication domain to parse, so default
294 * to the workgroup in the provided context.
295 */
296 if (workgroup != NULL) {
297 strncpy(workgroup, context->workgroup, workgroup_len - 1);
298 workgroup[workgroup_len - 1] = '\0';
299 }
300
301 if (options != NULL && options_len > 0) {
302 options[0] = (char)0;
303 }
304 pstrcpy(s, fname);
305
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: ? */
310 }
311
312 p = s + len;
313
314 /* Watch the test below, we are testing to see if we should exit */
315
316 if (strncmp(p, "//", 2) && strncmp(p, "\\\\", 2)) {
317
318 DEBUG(1, ("Invalid path (does not begin with smb://"));
319 return -1;
320
321 }
322
323 p += 2; /* Skip the double slash */
324
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';
329
330 DEBUG(4, ("Found options '%s'", q));
331
332 /* Copy the options */
333 if (options != NULL && options_len > 0) {
334 safe_strcpy(options, q, options_len - 1);
335 }
336 }
337
338 if (*p == (char)0)
339 goto decoding;
340
341 if (*p == '/') {
342
343 strncpy(server, context->workgroup,
344 ((strlen(context->workgroup) < 16)
345 ? strlen(context->workgroup)
346 : 16));
347 server[server_len - 1] = '\0';
348 return 0;
349
350 }
351
352 /*
353 * ok, its for us. Now parse out the server, share etc.
354 *
355 * However, we want to parse out [[domain;]user[:password]@] if it
356 * exists ...
357 */
358
359 /* check that '@' occurs before '/', if '/' exists at all */
360 q = strchr_m(p, '@');
361 r = strchr_m(p, '/');
362 if (q && (!r || q < r)) {
363 pstring username, passwd, domain;
364 const char *u = userinfo;
365
366 next_token_no_ltrim(&p, userinfo, "@", sizeof(fstring));
367
368 username[0] = passwd[0] = domain[0] = 0;
369
370 if (strchr_m(u, ';')) {
371
372 next_token_no_ltrim(&u, domain, ";", sizeof(fstring));
373
374 }
375
376 if (strchr_m(u, ':')) {
377
378 next_token_no_ltrim(&u, username, ":", sizeof(fstring));
379
380 pstrcpy(passwd, u);
381
382 }
383 else {
384
385 pstrcpy(username, u);
386
387 }
388
389 if (domain[0] && workgroup) {
390 strncpy(workgroup, domain, workgroup_len - 1);
391 workgroup[workgroup_len - 1] = '\0';
392 }
393
394 if (username[0]) {
395 strncpy(user, username, user_len - 1);
396 user[user_len - 1] = '\0';
397 }
398
399 if (passwd[0]) {
400 strncpy(password, passwd, password_len - 1);
401 password[password_len - 1] = '\0';
402 }
403
404 }
405
406 if (!next_token(&p, server, "/", sizeof(fstring))) {
407
408 return -1;
409
410 }
411
412 if (*p == (char)0) goto decoding; /* That's it ... */
413
414 if (!next_token(&p, share, "/", sizeof(fstring))) {
415
416 return -1;
417
418 }
419
420 /*
421 * Prepend a leading slash if there's a file path, as required by
422 * NetApp filers.
423 */
424 *path = '\0';
425 if (*p != '\0') {
426 *path = '/';
427 safe_strcpy(path + 1, p, path_len - 2);
428 }
429
430 all_string_sub(path, "/", "\\", 0);
431
432 decoding:
433 (void) smbc_urldecode(path, path, path_len);
434 (void) smbc_urldecode(server, server, server_len);
435 (void) smbc_urldecode(share, share, share_len);
436 (void) smbc_urldecode(user, user, user_len);
437 (void) smbc_urldecode(password, password, password_len);
438
439 return 0;
440}
441
442/*
443 * Verify that the options specified in a URL are valid
444 */
445static int
446smbc_check_options(char *server,
447 char *share,
448 char *path,
449 char *options)
450{
451 DEBUG(4, ("smbc_check_options(): server='%s' share='%s' "
452 "path='%s' options='%s'\n",
453 server, share, path, options));
454
455 /* No options at all is always ok */
456 if (! *options) return 0;
457
458 /* Currently, we don't support any options. */
459 return -1;
460}
461
462/*
463 * Convert an SMB error into a UNIX error ...
464 */
465static int
466smbc_errno(SMBCCTX *context,
467 struct cli_state *c)
468{
469 int ret = cli_errno(c);
470
471 if (cli_is_dos_error(c)) {
472 uint8 eclass;
473 uint32 ecode;
474
475 cli_dos_error(c, &eclass, &ecode);
476
477 DEBUG(3,("smbc_error %d %d (0x%x) -> %d\n",
478 (int)eclass, (int)ecode, (int)ecode, ret));
479 } else {
480 NTSTATUS status;
481
482 status = cli_nt_error(c);
483
484 DEBUG(3,("smbc errno %s -> %d\n",
485 nt_errstr(status), ret));
486 }
487
488 return ret;
489}
490
491/*
492 * Check a server for being alive and well.
493 * returns 0 if the server is in shape. Returns 1 on error
494 *
495 * Also useable outside libsmbclient to enable external cache
496 * to do some checks too.
497 */
498static int
499smbc_check_server(SMBCCTX * context,
500 SMBCSRV * server)
501{
502 if ( send_keepalive(server->cli->fd) == False )
503 return 1;
504
505 /* connection is ok */
506 return 0;
507}
508
509/*
510 * Remove a server from the cached server list it's unused.
511 * On success, 0 is returned. 1 is returned if the server could not be removed.
512 *
513 * Also useable outside libsmbclient
514 */
515int
516smbc_remove_unused_server(SMBCCTX * context,
517 SMBCSRV * srv)
518{
519 SMBCFILE * file;
520
521 /* are we being fooled ? */
522 if (!context || !context->internal ||
523 !context->internal->_initialized || !srv) return 1;
524
525
526 /* Check all open files/directories for a relation with this server */
527 for (file = context->internal->_files; file; file=file->next) {
528 if (file->srv == srv) {
529 /* Still used */
530 DEBUG(3, ("smbc_remove_usused_server: "
531 "%p still used by %p.\n",
532 srv, file));
533 return 1;
534 }
535 }
536
537 DLIST_REMOVE(context->internal->_servers, srv);
538
539 cli_shutdown(srv->cli);
540 srv->cli = NULL;
541
542 DEBUG(3, ("smbc_remove_usused_server: %p removed.\n", srv));
543
544 context->callbacks.remove_cached_srv_fn(context, srv);
545
546 SAFE_FREE(srv);
547
548 return 0;
549}
550
551static SMBCSRV *
552find_server(SMBCCTX *context,
553 const char *server,
554 const char *share,
555 fstring workgroup,
556 fstring username,
557 fstring password)
558{
559 SMBCSRV *srv;
560 int auth_called = 0;
561
562 check_server_cache:
563
564 srv = context->callbacks.get_cached_srv_fn(context, server, share,
565 workgroup, username);
566
567 if (!auth_called && !srv && (!username[0] || !password[0])) {
568 if (context->internal->_auth_fn_with_context != NULL) {
569 context->internal->_auth_fn_with_context(
570 context,
571 server, share,
572 workgroup, sizeof(fstring),
573 username, sizeof(fstring),
574 password, sizeof(fstring));
575 } else {
576 context->callbacks.auth_fn(
577 server, share,
578 workgroup, sizeof(fstring),
579 username, sizeof(fstring),
580 password, sizeof(fstring));
581 }
582
583 /*
584 * However, smbc_auth_fn may have picked up info relating to
585 * an existing connection, so try for an existing connection
586 * again ...
587 */
588 auth_called = 1;
589 goto check_server_cache;
590
591 }
592
593 if (srv) {
594 if (context->callbacks.check_server_fn(context, srv)) {
595 /*
596 * This server is no good anymore
597 * Try to remove it and check for more possible
598 * servers in the cache
599 */
600 if (context->callbacks.remove_unused_server_fn(context,
601 srv)) {
602 /*
603 * We could not remove the server completely,
604 * remove it from the cache so we will not get
605 * it again. It will be removed when the last
606 * file/dir is closed.
607 */
608 context->callbacks.remove_cached_srv_fn(context,
609 srv);
610 }
611
612 /*
613 * Maybe there are more cached connections to this
614 * server
615 */
616 goto check_server_cache;
617 }
618
619 return srv;
620 }
621
622 return NULL;
623}
624
625/*
626 * Connect to a server, possibly on an existing connection
627 *
628 * Here, what we want to do is: If the server and username
629 * match an existing connection, reuse that, otherwise, establish a
630 * new connection.
631 *
632 * If we have to create a new connection, call the auth_fn to get the
633 * info we need, unless the username and password were passed in.
634 */
635
636static SMBCSRV *
637smbc_server(SMBCCTX *context,
638 BOOL connect_if_not_found,
639 const char *server,
640 const char *share,
641 fstring workgroup,
642 fstring username,
643 fstring password)
644{
645 SMBCSRV *srv=NULL;
646 struct cli_state *c;
647 struct nmb_name called, calling;
648 const char *server_n = server;
649 pstring ipenv;
650 struct in_addr ip;
651 int tried_reverse = 0;
652 int port_try_first;
653 int port_try_next;
654 const char *username_used;
655
656 zero_ip(&ip);
657 ZERO_STRUCT(c);
658
659 if (server[0] == 0) {
660 errno = EPERM;
661 return NULL;
662 }
663
664 /* Look for a cached connection */
665 srv = find_server(context, server, share,
666 workgroup, username, password);
667
668 /*
669 * If we found a connection and we're only allowed one share per
670 * server...
671 */
672 if (srv && *share != '\0' && context->options.one_share_per_server) {
673
674 /*
675 * ... then if there's no current connection to the share,
676 * connect to it. find_server(), or rather the function
677 * pointed to by context->callbacks.get_cached_srv_fn which
678 * was called by find_server(), will have issued a tree
679 * disconnect if the requested share is not the same as the
680 * one that was already connected.
681 */
682 if (srv->cli->cnum == (uint16) -1) {
683 /* Ensure we have accurate auth info */
684 if (context->internal->_auth_fn_with_context != NULL) {
685 context->internal->_auth_fn_with_context(
686 context,
687 server, share,
688 workgroup, sizeof(fstring),
689 username, sizeof(fstring),
690 password, sizeof(fstring));
691 } else {
692 context->callbacks.auth_fn(
693 server, share,
694 workgroup, sizeof(fstring),
695 username, sizeof(fstring),
696 password, sizeof(fstring));
697 }
698
699 if (! cli_send_tconX(srv->cli, share, "?????",
700 password, strlen(password)+1)) {
701
702 errno = smbc_errno(context, srv->cli);
703 cli_shutdown(srv->cli);
704 srv->cli = NULL;
705 context->callbacks.remove_cached_srv_fn(context,
706 srv);
707 srv = NULL;
708 }
709
710 /*
711 * Regenerate the dev value since it's based on both
712 * server and share
713 */
714 if (srv) {
715 srv->dev = (dev_t)(str_checksum(server) ^
716 str_checksum(share));
717 }
718 }
719 }
720
721 /* If we have a connection... */
722 if (srv) {
723
724 /* ... then we're done here. Give 'em what they came for. */
725 return srv;
726 }
727
728 /* If we're not asked to connect when a connection doesn't exist... */
729 if (! connect_if_not_found) {
730 /* ... then we're done here. */
731 return NULL;
732 }
733
734 make_nmb_name(&calling, context->netbios_name, 0x0);
735 make_nmb_name(&called , server, 0x20);
736
737 DEBUG(4,("smbc_server: server_n=[%s] server=[%s]\n", server_n, server));
738
739 DEBUG(4,(" -> server_n=[%s] server=[%s]\n", server_n, server));
740
741 again:
742 slprintf(ipenv,sizeof(ipenv)-1,"HOST_%s", server_n);
743
744 zero_ip(&ip);
745
746 /* have to open a new connection */
747 if ((c = cli_initialise()) == NULL) {
748 errno = ENOMEM;
749 return NULL;
750 }
751
752 if (context->flags & SMB_CTX_FLAG_USE_KERBEROS) {
753 c->use_kerberos = True;
754 }
755 if (context->flags & SMB_CTX_FLAG_FALLBACK_AFTER_KERBEROS) {
756 c->fallback_after_kerberos = True;
757 }
758
759 c->timeout = context->timeout;
760
761 /*
762 * Force use of port 139 for first try if share is $IPC, empty, or
763 * null, so browse lists can work
764 */
765 if (share == NULL || *share == '\0' || strcmp(share, "IPC$") == 0) {
766 port_try_first = 139;
767 port_try_next = 445;
768 } else {
769 port_try_first = 445;
770 port_try_next = 139;
771 }
772
773 c->port = port_try_first;
774
775 if (!cli_connect(c, server_n, &ip)) {
776
777 /* First connection attempt failed. Try alternate port. */
778 c->port = port_try_next;
779
780 if (!cli_connect(c, server_n, &ip)) {
781 cli_shutdown(c);
782 errno = ETIMEDOUT;
783 return NULL;
784 }
785 }
786
787 if (!cli_session_request(c, &calling, &called)) {
788 cli_shutdown(c);
789 if (strcmp(called.name, "*SMBSERVER")) {
790 make_nmb_name(&called , "*SMBSERVER", 0x20);
791 goto again;
792 } else { /* Try one more time, but ensure we don't loop */
793
794 /* Only try this if server is an IP address ... */
795
796 if (is_ipaddress(server) && !tried_reverse) {
797 fstring remote_name;
798 struct in_addr rem_ip;
799
800 if ((rem_ip.s_addr=inet_addr(server)) == INADDR_NONE) {
801 DEBUG(4, ("Could not convert IP address "
802 "%s to struct in_addr\n", server));
803 errno = ETIMEDOUT;
804 return NULL;
805 }
806
807 tried_reverse++; /* Yuck */
808
809 if (name_status_find("*", 0, 0, rem_ip, remote_name)) {
810 make_nmb_name(&called, remote_name, 0x20);
811 goto again;
812 }
813 }
814 }
815 errno = ETIMEDOUT;
816 return NULL;
817 }
818
819 DEBUG(4,(" session request ok\n"));
820
821 if (!cli_negprot(c)) {
822 cli_shutdown(c);
823 errno = ETIMEDOUT;
824 return NULL;
825 }
826
827 username_used = username;
828
829 if (!NT_STATUS_IS_OK(cli_session_setup(c, username_used,
830 password, strlen(password),
831 password, strlen(password),
832 workgroup))) {
833
834 /* Failed. Try an anonymous login, if allowed by flags. */
835 username_used = "";
836
837 if ((context->flags & SMBCCTX_FLAG_NO_AUTO_ANONYMOUS_LOGON) ||
838 !NT_STATUS_IS_OK(cli_session_setup(c, username_used,
839 password, 1,
840 password, 0,
841 workgroup))) {
842
843 cli_shutdown(c);
844 errno = EPERM;
845 return NULL;
846 }
847 }
848
849 DEBUG(4,(" session setup ok\n"));
850
851 if (!cli_send_tconX(c, share, "?????",
852 password, strlen(password)+1)) {
853 errno = smbc_errno(context, c);
854 cli_shutdown(c);
855 return NULL;
856 }
857
858 DEBUG(4,(" tconx ok\n"));
859
860 /*
861 * Ok, we have got a nice connection
862 * Let's allocate a server structure.
863 */
864
865 srv = SMB_MALLOC_P(SMBCSRV);
866 if (!srv) {
867 errno = ENOMEM;
868 goto failed;
869 }
870
871 ZERO_STRUCTP(srv);
872 srv->cli = c;
873 srv->dev = (dev_t)(str_checksum(server) ^ str_checksum(share));
874 srv->no_pathinfo = False;
875 srv->no_pathinfo2 = False;
876 srv->no_nt_session = False;
877
878 /* now add it to the cache (internal or external) */
879 /* Let the cache function set errno if it wants to */
880 errno = 0;
881 if (context->callbacks.add_cached_srv_fn(context, srv, server, share, workgroup, username)) {
882 int saved_errno = errno;
883 DEBUG(3, (" Failed to add server to cache\n"));
884 errno = saved_errno;
885 if (errno == 0) {
886 errno = ENOMEM;
887 }
888 goto failed;
889 }
890
891 DEBUG(2, ("Server connect ok: //%s/%s: %p\n",
892 server, share, srv));
893
894 DLIST_ADD(context->internal->_servers, srv);
895 return srv;
896
897 failed:
898 cli_shutdown(c);
899 if (!srv) {
900 return NULL;
901 }
902
903 SAFE_FREE(srv);
904 return NULL;
905}
906
907/*
908 * Connect to a server for getting/setting attributes, possibly on an existing
909 * connection. This works similarly to smbc_server().
910 */
911static SMBCSRV *
912smbc_attr_server(SMBCCTX *context,
913 const char *server,
914 const char *share,
915 fstring workgroup,
916 fstring username,
917 fstring password,
918 POLICY_HND *pol)
919{
920 struct in_addr ip;
921 struct cli_state *ipc_cli;
922 struct rpc_pipe_client *pipe_hnd;
923 NTSTATUS nt_status;
924 SMBCSRV *ipc_srv=NULL;
925
926 /*
927 * See if we've already created this special connection. Reference
928 * our "special" share name '*IPC$', which is an impossible real share
929 * name due to the leading asterisk.
930 */
931 ipc_srv = find_server(context, server, "*IPC$",
932 workgroup, username, password);
933 if (!ipc_srv) {
934
935 /* We didn't find a cached connection. Get the password */
936 if (*password == '\0') {
937 /* ... then retrieve it now. */
938 if (context->internal->_auth_fn_with_context != NULL) {
939 context->internal->_auth_fn_with_context(
940 context,
941 server, share,
942 workgroup, sizeof(fstring),
943 username, sizeof(fstring),
944 password, sizeof(fstring));
945 } else {
946 context->callbacks.auth_fn(
947 server, share,
948 workgroup, sizeof(fstring),
949 username, sizeof(fstring),
950 password, sizeof(fstring));
951 }
952 }
953
954 zero_ip(&ip);
955 nt_status = cli_full_connection(&ipc_cli,
956 global_myname(), server,
957 &ip, 0, "IPC$", "?????",
958 username, workgroup,
959 password, 0,
960 Undefined, NULL);
961 if (! NT_STATUS_IS_OK(nt_status)) {
962 DEBUG(1,("cli_full_connection failed! (%s)\n",
963 nt_errstr(nt_status)));
964 errno = ENOTSUP;
965 return NULL;
966 }
967
968 ipc_srv = SMB_MALLOC_P(SMBCSRV);
969 if (!ipc_srv) {
970 errno = ENOMEM;
971 cli_shutdown(ipc_cli);
972 return NULL;
973 }
974
975 ZERO_STRUCTP(ipc_srv);
976 ipc_srv->cli = ipc_cli;
977
978 if (pol) {
979 pipe_hnd = cli_rpc_pipe_open_noauth(ipc_srv->cli,
980 PI_LSARPC,
981 &nt_status);
982 if (!pipe_hnd) {
983 DEBUG(1, ("cli_nt_session_open fail!\n"));
984 errno = ENOTSUP;
985 cli_shutdown(ipc_srv->cli);
986 free(ipc_srv);
987 return NULL;
988 }
989
990 /*
991 * Some systems don't support
992 * SEC_RIGHTS_MAXIMUM_ALLOWED, but NT sends 0x2000000
993 * so we might as well do it too.
994 */
995
996 nt_status = rpccli_lsa_open_policy(
997 pipe_hnd,
998 ipc_srv->cli->mem_ctx,
999 True,
1000 GENERIC_EXECUTE_ACCESS,
1001 pol);
1002
1003 if (!NT_STATUS_IS_OK(nt_status)) {
1004 errno = smbc_errno(context, ipc_srv->cli);
1005 cli_shutdown(ipc_srv->cli);
1006 return NULL;
1007 }
1008 }
1009
1010 /* now add it to the cache (internal or external) */
1011
1012 errno = 0; /* let cache function set errno if it likes */
1013 if (context->callbacks.add_cached_srv_fn(context, ipc_srv,
1014 server,
1015 "*IPC$",
1016 workgroup,
1017 username)) {
1018 DEBUG(3, (" Failed to add server to cache\n"));
1019 if (errno == 0) {
1020 errno = ENOMEM;
1021 }
1022 cli_shutdown(ipc_srv->cli);
1023 free(ipc_srv);
1024 return NULL;
1025 }
1026
1027 DLIST_ADD(context->internal->_servers, ipc_srv);
1028 }
1029
1030 return ipc_srv;
1031}
1032
1033/*
1034 * Routine to open() a file ...
1035 */
1036
1037static SMBCFILE *
1038smbc_open_ctx(SMBCCTX *context,
1039 const char *fname,
1040 int flags,
1041 mode_t mode)
1042{
1043 fstring server, share, user, password, workgroup;
1044 pstring path;
1045 pstring targetpath;
1046 struct cli_state *targetcli;
1047 SMBCSRV *srv = NULL;
1048 SMBCFILE *file = NULL;
1049 int fd;
1050
1051 if (!context || !context->internal ||
1052 !context->internal->_initialized) {
1053
1054 errno = EINVAL; /* Best I can think of ... */
1055 return NULL;
1056
1057 }
1058
1059 if (!fname) {
1060
1061 errno = EINVAL;
1062 return NULL;
1063
1064 }
1065
1066 if (smbc_parse_path(context, fname,
1067 workgroup, sizeof(workgroup),
1068 server, sizeof(server),
1069 share, sizeof(share),
1070 path, sizeof(path),
1071 user, sizeof(user),
1072 password, sizeof(password),
1073 NULL, 0)) {
1074 errno = EINVAL;
1075 return NULL;
1076 }
1077
1078 if (user[0] == (char)0) fstrcpy(user, context->user);
1079
1080 srv = smbc_server(context, True,
1081 server, share, workgroup, user, password);
1082
1083 if (!srv) {
1084
1085 if (errno == EPERM) errno = EACCES;
1086 return NULL; /* smbc_server sets errno */
1087
1088 }
1089
1090 /* Hmmm, the test for a directory is suspect here ... FIXME */
1091
1092 if (strlen(path) > 0 && path[strlen(path) - 1] == '\\') {
1093
1094 fd = -1;
1095
1096 }
1097 else {
1098
1099 file = SMB_MALLOC_P(SMBCFILE);
1100
1101 if (!file) {
1102
1103 errno = ENOMEM;
1104 return NULL;
1105
1106 }
1107
1108 ZERO_STRUCTP(file);
1109
1110 /*d_printf(">>>open: resolving %s\n", path);*/
1111 if (!cli_resolve_path( "", srv->cli, path, &targetcli, targetpath))
1112 {
1113 d_printf("Could not resolve %s\n", path);
1114 SAFE_FREE(file);
1115 return NULL;
1116 }
1117 /*d_printf(">>>open: resolved %s as %s\n", path, targetpath);*/
1118
1119 if ((fd = cli_open(targetcli, targetpath, flags,
1120 context->internal->_share_mode)) < 0) {
1121
1122 /* Handle the error ... */
1123
1124 SAFE_FREE(file);
1125 errno = smbc_errno(context, targetcli);
1126 return NULL;
1127
1128 }
1129
1130 /* Fill in file struct */
1131
1132 file->cli_fd = fd;
1133 file->fname = SMB_STRDUP(fname);
1134 file->srv = srv;
1135 file->offset = 0;
1136 file->file = True;
1137
1138 DLIST_ADD(context->internal->_files, file);
1139
1140 /*
1141 * If the file was opened in O_APPEND mode, all write
1142 * operations should be appended to the file. To do that,
1143 * though, using this protocol, would require a getattrE()
1144 * call for each and every write, to determine where the end
1145 * of the file is. (There does not appear to be an append flag
1146 * in the protocol.) Rather than add all of that overhead of
1147 * retrieving the current end-of-file offset prior to each
1148 * write operation, we'll assume that most append operations
1149 * will continuously write, so we'll just set the offset to
1150 * the end of the file now and hope that's adequate.
1151 *
1152 * Note to self: If this proves inadequate, and O_APPEND
1153 * should, in some cases, be forced for each write, add a
1154 * field in the context options structure, for
1155 * "strict_append_mode" which would select between the current
1156 * behavior (if FALSE) or issuing a getattrE() prior to each
1157 * write and forcing the write to the end of the file (if
1158 * TRUE). Adding that capability will likely require adding
1159 * an "append" flag into the _SMBCFILE structure to track
1160 * whether a file was opened in O_APPEND mode. -- djl
1161 */
1162 if (flags & O_APPEND) {
1163 if (smbc_lseek_ctx(context, file, 0, SEEK_END) < 0) {
1164 (void) smbc_close_ctx(context, file);
1165 errno = ENXIO;
1166 return NULL;
1167 }
1168 }
1169
1170 return file;
1171
1172 }
1173
1174 /* Check if opendir needed ... */
1175
1176 if (fd == -1) {
1177 int eno = 0;
1178
1179 eno = smbc_errno(context, srv->cli);
1180 file = context->opendir(context, fname);
1181 if (!file) errno = eno;
1182 return file;
1183
1184 }
1185
1186 errno = EINVAL; /* FIXME, correct errno ? */
1187 return NULL;
1188
1189}
1190
1191/*
1192 * Routine to create a file
1193 */
1194
1195static int creat_bits = O_WRONLY | O_CREAT | O_TRUNC; /* FIXME: Do we need this */
1196
1197static SMBCFILE *
1198smbc_creat_ctx(SMBCCTX *context,
1199 const char *path,
1200 mode_t mode)
1201{
1202
1203 if (!context || !context->internal ||
1204 !context->internal->_initialized) {
1205
1206 errno = EINVAL;
1207 return NULL;
1208
1209 }
1210
1211 return smbc_open_ctx(context, path, creat_bits, mode);
1212}
1213
1214/*
1215 * Routine to read() a file ...
1216 */
1217
1218static ssize_t
1219smbc_read_ctx(SMBCCTX *context,
1220 SMBCFILE *file,
1221 void *buf,
1222 size_t count)
1223{
1224 int ret;
1225 fstring server, share, user, password;
1226 pstring path, targetpath;
1227 struct cli_state *targetcli;
1228
1229 /*
1230 * offset:
1231 *
1232 * Compiler bug (possibly) -- gcc (GCC) 3.3.5 (Debian 1:3.3.5-2) --
1233 * appears to pass file->offset (which is type off_t) differently than
1234 * a local variable of type off_t. Using local variable "offset" in
1235 * the call to cli_read() instead of file->offset fixes a problem
1236 * retrieving data at an offset greater than 4GB.
1237 */
1238 off_t offset;
1239
1240 if (!context || !context->internal ||
1241 !context->internal->_initialized) {
1242
1243 errno = EINVAL;
1244 return -1;
1245
1246 }
1247
1248 DEBUG(4, ("smbc_read(%p, %d)\n", file, (int)count));
1249
1250 if (!file || !DLIST_CONTAINS(context->internal->_files, file)) {
1251
1252 errno = EBADF;
1253 return -1;
1254
1255 }
1256
1257 offset = file->offset;
1258
1259 /* Check that the buffer exists ... */
1260
1261 if (buf == NULL) {
1262
1263 errno = EINVAL;
1264 return -1;
1265
1266 }
1267
1268 /*d_printf(">>>read: parsing %s\n", file->fname);*/
1269 if (smbc_parse_path(context, file->fname,
1270 NULL, 0,
1271 server, sizeof(server),
1272 share, sizeof(share),
1273 path, sizeof(path),
1274 user, sizeof(user),
1275 password, sizeof(password),
1276 NULL, 0)) {
1277 errno = EINVAL;
1278 return -1;
1279 }
1280
1281 /*d_printf(">>>read: resolving %s\n", path);*/
1282 if (!cli_resolve_path("", file->srv->cli, path,
1283 &targetcli, targetpath))
1284 {
1285 d_printf("Could not resolve %s\n", path);
1286 return -1;
1287 }
1288 /*d_printf(">>>fstat: resolved path as %s\n", targetpath);*/
1289
1290 ret = cli_read(targetcli, file->cli_fd, (char *)buf, offset, count);
1291
1292 if (ret < 0) {
1293
1294 errno = smbc_errno(context, targetcli);
1295 return -1;
1296
1297 }
1298
1299 file->offset += ret;
1300
1301 DEBUG(4, (" --> %d\n", ret));
1302
1303 return ret; /* Success, ret bytes of data ... */
1304
1305}
1306
1307/*
1308 * Routine to write() a file ...
1309 */
1310
1311static ssize_t
1312smbc_write_ctx(SMBCCTX *context,
1313 SMBCFILE *file,
1314 void *buf,
1315 size_t count)
1316{
1317 int ret;
1318 off_t offset;
1319 fstring server, share, user, password;
1320 pstring path, targetpath;
1321 struct cli_state *targetcli;
1322
1323 /* First check all pointers before dereferencing them */
1324
1325 if (!context || !context->internal ||
1326 !context->internal->_initialized) {
1327
1328 errno = EINVAL;
1329 return -1;
1330
1331 }
1332
1333 if (!file || !DLIST_CONTAINS(context->internal->_files, file)) {
1334
1335 errno = EBADF;
1336 return -1;
1337
1338 }
1339
1340 /* Check that the buffer exists ... */
1341
1342 if (buf == NULL) {
1343
1344 errno = EINVAL;
1345 return -1;
1346
1347 }
1348
1349 offset = file->offset; /* See "offset" comment in smbc_read_ctx() */
1350
1351 /*d_printf(">>>write: parsing %s\n", file->fname);*/
1352 if (smbc_parse_path(context, file->fname,
1353 NULL, 0,
1354 server, sizeof(server),
1355 share, sizeof(share),
1356 path, sizeof(path),
1357 user, sizeof(user),
1358 password, sizeof(password),
1359 NULL, 0)) {
1360 errno = EINVAL;
1361 return -1;
1362 }
1363
1364 /*d_printf(">>>write: resolving %s\n", path);*/
1365 if (!cli_resolve_path("", file->srv->cli, path,
1366 &targetcli, targetpath))
1367 {
1368 d_printf("Could not resolve %s\n", path);
1369 return -1;
1370 }
1371 /*d_printf(">>>write: resolved path as %s\n", targetpath);*/
1372
1373
1374 ret = cli_write(targetcli, file->cli_fd, 0, (char *)buf, offset, count);
1375
1376 if (ret <= 0) {
1377
1378 errno = smbc_errno(context, targetcli);
1379 return -1;
1380
1381 }
1382
1383 file->offset += ret;
1384
1385 return ret; /* Success, 0 bytes of data ... */
1386}
1387
1388/*
1389 * Routine to close() a file ...
1390 */
1391
1392static int
1393smbc_close_ctx(SMBCCTX *context,
1394 SMBCFILE *file)
1395{
1396 SMBCSRV *srv;
1397 fstring server, share, user, password;
1398 pstring path, targetpath;
1399 struct cli_state *targetcli;
1400
1401 if (!context || !context->internal ||
1402 !context->internal->_initialized) {
1403
1404 errno = EINVAL;
1405 return -1;
1406
1407 }
1408
1409 if (!file || !DLIST_CONTAINS(context->internal->_files, file)) {
1410
1411 errno = EBADF;
1412 return -1;
1413
1414 }
1415
1416 /* IS a dir ... */
1417 if (!file->file) {
1418
1419 return context->closedir(context, file);
1420
1421 }
1422
1423 /*d_printf(">>>close: parsing %s\n", file->fname);*/
1424 if (smbc_parse_path(context, file->fname,
1425 NULL, 0,
1426 server, sizeof(server),
1427 share, sizeof(share),
1428 path, sizeof(path),
1429 user, sizeof(user),
1430 password, sizeof(password),
1431 NULL, 0)) {
1432 errno = EINVAL;
1433 return -1;
1434 }
1435
1436 /*d_printf(">>>close: resolving %s\n", path);*/
1437 if (!cli_resolve_path("", file->srv->cli, path,
1438 &targetcli, targetpath))
1439 {
1440 d_printf("Could not resolve %s\n", path);
1441 return -1;
1442 }
1443 /*d_printf(">>>close: resolved path as %s\n", targetpath);*/
1444
1445 if (!cli_close(targetcli, file->cli_fd)) {
1446
1447 DEBUG(3, ("cli_close failed on %s. purging server.\n",
1448 file->fname));
1449 /* Deallocate slot and remove the server
1450 * from the server cache if unused */
1451 errno = smbc_errno(context, targetcli);
1452 srv = file->srv;
1453 DLIST_REMOVE(context->internal->_files, file);
1454 SAFE_FREE(file->fname);
1455 SAFE_FREE(file);
1456 context->callbacks.remove_unused_server_fn(context, srv);
1457
1458 return -1;
1459
1460 }
1461
1462 DLIST_REMOVE(context->internal->_files, file);
1463 SAFE_FREE(file->fname);
1464 SAFE_FREE(file);
1465
1466 return 0;
1467}
1468
1469/*
1470 * Get info from an SMB server on a file. Use a qpathinfo call first
1471 * and if that fails, use getatr, as Win95 sometimes refuses qpathinfo
1472 */
1473static BOOL
1474smbc_getatr(SMBCCTX * context,
1475 SMBCSRV *srv,
1476 char *path,
1477 uint16 *mode,
1478 SMB_OFF_T *size,
1479 struct timespec *create_time_ts,
1480 struct timespec *access_time_ts,
1481 struct timespec *write_time_ts,
1482 struct timespec *change_time_ts,
1483 SMB_INO_T *ino)
1484{
1485 pstring fixedpath;
1486 pstring targetpath;
1487 struct cli_state *targetcli;
1488 time_t write_time;
1489
1490 if (!context || !context->internal ||
1491 !context->internal->_initialized) {
1492
1493 errno = EINVAL;
1494 return -1;
1495
1496 }
1497
1498 /* path fixup for . and .. */
1499 if (strequal(path, ".") || strequal(path, ".."))
1500 pstrcpy(fixedpath, "\\");
1501 else
1502 {
1503 pstrcpy(fixedpath, path);
1504 trim_string(fixedpath, NULL, "\\..");
1505 trim_string(fixedpath, NULL, "\\.");
1506 }
1507 DEBUG(4,("smbc_getatr: sending qpathinfo\n"));
1508
1509 if (!cli_resolve_path( "", srv->cli, fixedpath, &targetcli, targetpath))
1510 {
1511 d_printf("Couldn't resolve %s\n", path);
1512 return False;
1513 }
1514
1515 if (!srv->no_pathinfo2 &&
1516 cli_qpathinfo2(targetcli, targetpath,
1517 create_time_ts,
1518 access_time_ts,
1519 write_time_ts,
1520 change_time_ts,
1521 size, mode, ino)) {
1522 return True;
1523 }
1524
1525 /* if this is NT then don't bother with the getatr */
1526 if (targetcli->capabilities & CAP_NT_SMBS) {
1527 errno = EPERM;
1528 return False;
1529 }
1530
1531 if (cli_getatr(targetcli, targetpath, mode, size, &write_time)) {
1532
1533 struct timespec w_time_ts;
1534
1535 w_time_ts = convert_time_t_to_timespec(write_time);
1536
1537 if (write_time_ts != NULL) {
1538 *write_time_ts = w_time_ts;
1539 }
1540
1541 if (create_time_ts != NULL) {
1542 *create_time_ts = w_time_ts;
1543 }
1544
1545 if (access_time_ts != NULL) {
1546 *access_time_ts = w_time_ts;
1547 }
1548
1549 if (change_time_ts != NULL) {
1550 *change_time_ts = w_time_ts;
1551 }
1552
1553 srv->no_pathinfo2 = True;
1554 return True;
1555 }
1556
1557 errno = EPERM;
1558 return False;
1559
1560}
1561
1562/*
1563 * Set file info on an SMB server. Use setpathinfo call first. If that
1564 * fails, use setattrE..
1565 *
1566 * Access and modification time parameters are always used and must be
1567 * provided. Create time, if zero, will be determined from the actual create
1568 * time of the file. If non-zero, the create time will be set as well.
1569 *
1570 * "mode" (attributes) parameter may be set to -1 if it is not to be set.
1571 */
1572static BOOL
1573smbc_setatr(SMBCCTX * context, SMBCSRV *srv, char *path,
1574 time_t create_time,
1575 time_t access_time,
1576 time_t write_time,
1577 time_t change_time,
1578 uint16 mode)
1579{
1580 int fd;
1581 int ret;
1582
1583 /*
1584 * First, try setpathinfo (if qpathinfo succeeded), for it is the
1585 * modern function for "new code" to be using, and it works given a
1586 * filename rather than requiring that the file be opened to have its
1587 * attributes manipulated.
1588 */
1589 if (srv->no_pathinfo ||
1590 ! cli_setpathinfo(srv->cli, path,
1591 create_time,
1592 access_time,
1593 write_time,
1594 change_time,
1595 mode)) {
1596
1597 /*
1598 * setpathinfo is not supported; go to plan B.
1599 *
1600 * cli_setatr() does not work on win98, and it also doesn't
1601 * support setting the access time (only the modification
1602 * time), so in all cases, we open the specified file and use
1603 * cli_setattrE() which should work on all OS versions, and
1604 * supports both times.
1605 */
1606
1607 /* Don't try {q,set}pathinfo() again, with this server */
1608 srv->no_pathinfo = True;
1609
1610 /* Open the file */
1611 if ((fd = cli_open(srv->cli, path, O_RDWR, DENY_NONE)) < 0) {
1612
1613 errno = smbc_errno(context, srv->cli);
1614 return -1;
1615 }
1616
1617 /* Set the new attributes */
1618 ret = cli_setattrE(srv->cli, fd,
1619 change_time,
1620 access_time,
1621 write_time);
1622
1623 /* Close the file */
1624 cli_close(srv->cli, fd);
1625
1626 /*
1627 * Unfortunately, setattrE() doesn't have a provision for
1628 * setting the access mode (attributes). We'll have to try
1629 * cli_setatr() for that, and with only this parameter, it
1630 * seems to work on win98.
1631 */
1632 if (ret && mode != (uint16) -1) {
1633 ret = cli_setatr(srv->cli, path, mode, 0);
1634 }
1635
1636 if (! ret) {
1637 errno = smbc_errno(context, srv->cli);
1638 return False;
1639 }
1640 }
1641
1642 return True;
1643}
1644
1645 /*
1646 * Routine to unlink() a file
1647 */
1648
1649static int
1650smbc_unlink_ctx(SMBCCTX *context,
1651 const char *fname)
1652{
1653 fstring server, share, user, password, workgroup;
1654 pstring path, targetpath;
1655 struct cli_state *targetcli;
1656 SMBCSRV *srv = NULL;
1657
1658 if (!context || !context->internal ||
1659 !context->internal->_initialized) {
1660
1661 errno = EINVAL; /* Best I can think of ... */
1662 return -1;
1663
1664 }
1665
1666 if (!fname) {
1667
1668 errno = EINVAL;
1669 return -1;
1670
1671 }
1672
1673 if (smbc_parse_path(context, fname,
1674 workgroup, sizeof(workgroup),
1675 server, sizeof(server),
1676 share, sizeof(share),
1677 path, sizeof(path),
1678 user, sizeof(user),
1679 password, sizeof(password),
1680 NULL, 0)) {
1681 errno = EINVAL;
1682 return -1;
1683 }
1684
1685 if (user[0] == (char)0) fstrcpy(user, context->user);
1686
1687 srv = smbc_server(context, True,
1688 server, share, workgroup, user, password);
1689
1690 if (!srv) {
1691
1692 return -1; /* smbc_server sets errno */
1693
1694 }
1695
1696 /*d_printf(">>>unlink: resolving %s\n", path);*/
1697 if (!cli_resolve_path( "", srv->cli, path, &targetcli, targetpath))
1698 {
1699 d_printf("Could not resolve %s\n", path);
1700 return -1;
1701 }
1702 /*d_printf(">>>unlink: resolved path as %s\n", targetpath);*/
1703
1704 if (!cli_unlink(targetcli, targetpath)) {
1705
1706 errno = smbc_errno(context, targetcli);
1707
1708 if (errno == EACCES) { /* Check if the file is a directory */
1709
1710 int saverr = errno;
1711 SMB_OFF_T size = 0;
1712 uint16 mode = 0;
1713 struct timespec write_time_ts;
1714 struct timespec access_time_ts;
1715 struct timespec change_time_ts;
1716 SMB_INO_T ino = 0;
1717
1718 if (!smbc_getatr(context, srv, path, &mode, &size,
1719 NULL,
1720 &access_time_ts,
1721 &write_time_ts,
1722 &change_time_ts,
1723 &ino)) {
1724
1725 /* Hmmm, bad error ... What? */
1726
1727 errno = smbc_errno(context, targetcli);
1728 return -1;
1729
1730 }
1731 else {
1732
1733 if (IS_DOS_DIR(mode))
1734 errno = EISDIR;
1735 else
1736 errno = saverr; /* Restore this */
1737
1738 }
1739 }
1740
1741 return -1;
1742
1743 }
1744
1745 return 0; /* Success ... */
1746
1747}
1748
1749/*
1750 * Routine to rename() a file
1751 */
1752
1753static int
1754smbc_rename_ctx(SMBCCTX *ocontext,
1755 const char *oname,
1756 SMBCCTX *ncontext,
1757 const char *nname)
1758{
1759 fstring server1;
1760 fstring share1;
1761 fstring server2;
1762 fstring share2;
1763 fstring user1;
1764 fstring user2;
1765 fstring password1;
1766 fstring password2;
1767 fstring workgroup;
1768 pstring path1;
1769 pstring path2;
1770 pstring targetpath1;
1771 pstring targetpath2;
1772 struct cli_state *targetcli1;
1773 struct cli_state *targetcli2;
1774 SMBCSRV *srv = NULL;
1775
1776 if (!ocontext || !ncontext ||
1777 !ocontext->internal || !ncontext->internal ||
1778 !ocontext->internal->_initialized ||
1779 !ncontext->internal->_initialized) {
1780
1781 errno = EINVAL; /* Best I can think of ... */
1782 return -1;
1783
1784 }
1785
1786 if (!oname || !nname) {
1787
1788 errno = EINVAL;
1789 return -1;
1790
1791 }
1792
1793 DEBUG(4, ("smbc_rename(%s,%s)\n", oname, nname));
1794
1795 smbc_parse_path(ocontext, oname,
1796 workgroup, sizeof(workgroup),
1797 server1, sizeof(server1),
1798 share1, sizeof(share1),
1799 path1, sizeof(path1),
1800 user1, sizeof(user1),
1801 password1, sizeof(password1),
1802 NULL, 0);
1803
1804 if (user1[0] == (char)0) fstrcpy(user1, ocontext->user);
1805
1806 smbc_parse_path(ncontext, nname,
1807 NULL, 0,
1808 server2, sizeof(server2),
1809 share2, sizeof(share2),
1810 path2, sizeof(path2),
1811 user2, sizeof(user2),
1812 password2, sizeof(password2),
1813 NULL, 0);
1814
1815 if (user2[0] == (char)0) fstrcpy(user2, ncontext->user);
1816
1817 if (strcmp(server1, server2) || strcmp(share1, share2) ||
1818 strcmp(user1, user2)) {
1819
1820 /* Can't rename across file systems, or users?? */
1821
1822 errno = EXDEV;
1823 return -1;
1824
1825 }
1826
1827 srv = smbc_server(ocontext, True,
1828 server1, share1, workgroup, user1, password1);
1829 if (!srv) {
1830
1831 return -1;
1832
1833 }
1834
1835 /*d_printf(">>>rename: resolving %s\n", path1);*/
1836 if (!cli_resolve_path( "", srv->cli, path1, &targetcli1, targetpath1))
1837 {
1838 d_printf("Could not resolve %s\n", path1);
1839 return -1;
1840 }
1841 /*d_printf(">>>rename: resolved path as %s\n", targetpath1);*/
1842 /*d_printf(">>>rename: resolving %s\n", path2);*/
1843 if (!cli_resolve_path( "", srv->cli, path2, &targetcli2, targetpath2))
1844 {
1845 d_printf("Could not resolve %s\n", path2);
1846 return -1;
1847 }
1848 /*d_printf(">>>rename: resolved path as %s\n", targetpath2);*/
1849
1850 if (strcmp(targetcli1->desthost, targetcli2->desthost) ||
1851 strcmp(targetcli1->share, targetcli2->share))
1852 {
1853 /* can't rename across file systems */
1854
1855 errno = EXDEV;
1856 return -1;
1857 }
1858
1859 if (!cli_rename(targetcli1, targetpath1, targetpath2)) {
1860 int eno = smbc_errno(ocontext, targetcli1);
1861
1862 if (eno != EEXIST ||
1863 !cli_unlink(targetcli1, targetpath2) ||
1864 !cli_rename(targetcli1, targetpath1, targetpath2)) {
1865
1866 errno = eno;
1867 return -1;
1868
1869 }
1870 }
1871
1872 return 0; /* Success */
1873
1874}
1875
1876/*
1877 * A routine to lseek() a file
1878 */
1879
1880static off_t
1881smbc_lseek_ctx(SMBCCTX *context,
1882 SMBCFILE *file,
1883 off_t offset,
1884 int whence)
1885{
1886 SMB_OFF_T size;
1887 fstring server, share, user, password;
1888 pstring path, targetpath;
1889 struct cli_state *targetcli;
1890
1891 if (!context || !context->internal ||
1892 !context->internal->_initialized) {
1893
1894 errno = EINVAL;
1895 return -1;
1896
1897 }
1898
1899 if (!file || !DLIST_CONTAINS(context->internal->_files, file)) {
1900
1901 errno = EBADF;
1902 return -1;
1903
1904 }
1905
1906 if (!file->file) {
1907
1908 errno = EINVAL;
1909 return -1; /* Can't lseek a dir ... */
1910
1911 }
1912
1913 switch (whence) {
1914 case SEEK_SET:
1915 file->offset = offset;
1916 break;
1917
1918 case SEEK_CUR:
1919 file->offset += offset;
1920 break;
1921
1922 case SEEK_END:
1923 /*d_printf(">>>lseek: parsing %s\n", file->fname);*/
1924 if (smbc_parse_path(context, file->fname,
1925 NULL, 0,
1926 server, sizeof(server),
1927 share, sizeof(share),
1928 path, sizeof(path),
1929 user, sizeof(user),
1930 password, sizeof(password),
1931 NULL, 0)) {
1932
1933 errno = EINVAL;
1934 return -1;
1935 }
1936
1937 /*d_printf(">>>lseek: resolving %s\n", path);*/
1938 if (!cli_resolve_path("", file->srv->cli, path,
1939 &targetcli, targetpath))
1940 {
1941 d_printf("Could not resolve %s\n", path);
1942 return -1;
1943 }
1944 /*d_printf(">>>lseek: resolved path as %s\n", targetpath);*/
1945
1946 if (!cli_qfileinfo(targetcli, file->cli_fd, NULL,
1947 &size, NULL, NULL, NULL, NULL, NULL))
1948 {
1949 SMB_OFF_T b_size = size;
1950 if (!cli_getattrE(targetcli, file->cli_fd,
1951 NULL, &b_size, NULL, NULL, NULL))
1952 {
1953 errno = EINVAL;
1954 return -1;
1955 } else
1956 size = b_size;
1957 }
1958 file->offset = size + offset;
1959 break;
1960
1961 default:
1962 errno = EINVAL;
1963 break;
1964
1965 }
1966
1967 return file->offset;
1968
1969}
1970
1971/*
1972 * Generate an inode number from file name for those things that need it
1973 */
1974
1975static ino_t
1976smbc_inode(SMBCCTX *context,
1977 const char *name)
1978{
1979
1980 if (!context || !context->internal ||
1981 !context->internal->_initialized) {
1982
1983 errno = EINVAL;
1984 return -1;
1985
1986 }
1987
1988 if (!*name) return 2; /* FIXME, why 2 ??? */
1989 return (ino_t)str_checksum(name);
1990
1991}
1992
1993/*
1994 * Routine to put basic stat info into a stat structure ... Used by stat and
1995 * fstat below.
1996 */
1997
1998static int
1999smbc_setup_stat(SMBCCTX *context,
2000 struct stat *st,
2001 char *fname,
2002 SMB_OFF_T size,
2003 int mode)
2004{
2005
2006 st->st_mode = 0;
2007
2008 if (IS_DOS_DIR(mode)) {
2009 st->st_mode = SMBC_DIR_MODE;
2010 } else {
2011 st->st_mode = SMBC_FILE_MODE;
2012 }
2013
2014 if (IS_DOS_ARCHIVE(mode)) st->st_mode |= S_IXUSR;
2015 if (IS_DOS_SYSTEM(mode)) st->st_mode |= S_IXGRP;
2016 if (IS_DOS_HIDDEN(mode)) st->st_mode |= S_IXOTH;
2017 if (!IS_DOS_READONLY(mode)) st->st_mode |= S_IWUSR;
2018
2019 st->st_size = size;
2020#ifdef HAVE_STAT_ST_BLKSIZE
2021 st->st_blksize = 512;
2022#endif
2023#ifdef HAVE_STAT_ST_BLOCKS
2024 st->st_blocks = (size+511)/512;
2025#endif
2026 st->st_uid = getuid();
2027 st->st_gid = getgid();
2028
2029 if (IS_DOS_DIR(mode)) {
2030 st->st_nlink = 2;
2031 } else {
2032 st->st_nlink = 1;
2033 }
2034
2035 if (st->st_ino == 0) {
2036 st->st_ino = smbc_inode(context, fname);
2037 }
2038
2039 return True; /* FIXME: Is this needed ? */
2040
2041}
2042
2043/*
2044 * Routine to stat a file given a name
2045 */
2046
2047static int
2048smbc_stat_ctx(SMBCCTX *context,
2049 const char *fname,
2050 struct stat *st)
2051{
2052 SMBCSRV *srv;
2053 fstring server;
2054 fstring share;
2055 fstring user;
2056 fstring password;
2057 fstring workgroup;
2058 pstring path;
2059 struct timespec write_time_ts;
2060 struct timespec access_time_ts;
2061 struct timespec change_time_ts;
2062 SMB_OFF_T size = 0;
2063 uint16 mode = 0;
2064 SMB_INO_T ino = 0;
2065
2066 if (!context || !context->internal ||
2067 !context->internal->_initialized) {
2068
2069 errno = EINVAL; /* Best I can think of ... */
2070 return -1;
2071
2072 }
2073
2074 if (!fname) {
2075
2076 errno = EINVAL;
2077 return -1;
2078
2079 }
2080
2081 DEBUG(4, ("smbc_stat(%s)\n", fname));
2082
2083 if (smbc_parse_path(context, fname,
2084 workgroup, sizeof(workgroup),
2085 server, sizeof(server),
2086 share, sizeof(share),
2087 path, sizeof(path),
2088 user, sizeof(user),
2089 password, sizeof(password),
2090 NULL, 0)) {
2091 errno = EINVAL;
2092 return -1;
2093 }
2094
2095 if (user[0] == (char)0) fstrcpy(user, context->user);
2096
2097 srv = smbc_server(context, True,
2098 server, share, workgroup, user, password);
2099
2100 if (!srv) {
2101 return -1; /* errno set by smbc_server */
2102 }
2103
2104 if (!smbc_getatr(context, srv, path, &mode, &size,
2105 NULL,
2106 &access_time_ts,
2107 &write_time_ts,
2108 &change_time_ts,
2109 &ino)) {
2110
2111 errno = smbc_errno(context, srv->cli);
2112 return -1;
2113
2114 }
2115
2116 st->st_ino = ino;
2117
2118 smbc_setup_stat(context, st, path, size, mode);
2119
2120 set_atimespec(st, access_time_ts);
2121 set_ctimespec(st, change_time_ts);
2122 set_mtimespec(st, write_time_ts);
2123 st->st_dev = srv->dev;
2124
2125 return 0;
2126
2127}
2128
2129/*
2130 * Routine to stat a file given an fd
2131 */
2132
2133static int
2134smbc_fstat_ctx(SMBCCTX *context,
2135 SMBCFILE *file,
2136 struct stat *st)
2137{
2138 struct timespec change_time_ts;
2139 struct timespec access_time_ts;
2140 struct timespec write_time_ts;
2141 SMB_OFF_T size;
2142 uint16 mode;
2143 fstring server;
2144 fstring share;
2145 fstring user;
2146 fstring password;
2147 pstring path;
2148 pstring targetpath;
2149 struct cli_state *targetcli;
2150 SMB_INO_T ino = 0;
2151
2152 if (!context || !context->internal ||
2153 !context->internal->_initialized) {
2154
2155 errno = EINVAL;
2156 return -1;
2157
2158 }
2159
2160 if (!file || !DLIST_CONTAINS(context->internal->_files, file)) {
2161
2162 errno = EBADF;
2163 return -1;
2164
2165 }
2166
2167 if (!file->file) {
2168
2169 return context->fstatdir(context, file, st);
2170
2171 }
2172
2173 /*d_printf(">>>fstat: parsing %s\n", file->fname);*/
2174 if (smbc_parse_path(context, file->fname,
2175 NULL, 0,
2176 server, sizeof(server),
2177 share, sizeof(share),
2178 path, sizeof(path),
2179 user, sizeof(user),
2180 password, sizeof(password),
2181 NULL, 0)) {
2182 errno = EINVAL;
2183 return -1;
2184 }
2185
2186 /*d_printf(">>>fstat: resolving %s\n", path);*/
2187 if (!cli_resolve_path("", file->srv->cli, path,
2188 &targetcli, targetpath))
2189 {
2190 d_printf("Could not resolve %s\n", path);
2191 return -1;
2192 }
2193 /*d_printf(">>>fstat: resolved path as %s\n", targetpath);*/
2194
2195 if (!cli_qfileinfo(targetcli, file->cli_fd, &mode, &size,
2196 NULL,
2197 &access_time_ts,
2198 &write_time_ts,
2199 &change_time_ts,
2200 &ino)) {
2201
2202 time_t change_time, access_time, write_time;
2203
2204 if (!cli_getattrE(targetcli, file->cli_fd, &mode, &size,
2205 &change_time, &access_time, &write_time)) {
2206
2207 errno = EINVAL;
2208 return -1;
2209 }
2210
2211 change_time_ts = convert_time_t_to_timespec(change_time);
2212 access_time_ts = convert_time_t_to_timespec(access_time);
2213 write_time_ts = convert_time_t_to_timespec(write_time);
2214 }
2215
2216 st->st_ino = ino;
2217
2218 smbc_setup_stat(context, st, file->fname, size, mode);
2219
2220 set_atimespec(st, access_time_ts);
2221 set_ctimespec(st, change_time_ts);
2222 set_mtimespec(st, write_time_ts);
2223 st->st_dev = file->srv->dev;
2224
2225 return 0;
2226
2227}
2228
2229/*
2230 * Routine to open a directory
2231 * We accept the URL syntax explained in smbc_parse_path(), above.
2232 */
2233
2234static void
2235smbc_remove_dir(SMBCFILE *dir)
2236{
2237 struct smbc_dir_list *d,*f;
2238
2239 d = dir->dir_list;
2240 while (d) {
2241
2242 f = d; d = d->next;
2243
2244 SAFE_FREE(f->dirent);
2245 SAFE_FREE(f);
2246
2247 }
2248
2249 dir->dir_list = dir->dir_end = dir->dir_next = NULL;
2250
2251}
2252
2253static int
2254add_dirent(SMBCFILE *dir,
2255 const char *name,
2256 const char *comment,
2257 uint32 type)
2258{
2259 struct smbc_dirent *dirent;
2260 int size;
2261 int name_length = (name == NULL ? 0 : strlen(name));
2262 int comment_len = (comment == NULL ? 0 : strlen(comment));
2263
2264 /*
2265 * Allocate space for the dirent, which must be increased by the
2266 * size of the name and the comment and 1 each for the null terminator.
2267 */
2268
2269 size = sizeof(struct smbc_dirent) + name_length + comment_len + 2;
2270
2271 dirent = (struct smbc_dirent *)SMB_MALLOC(size);
2272
2273 if (!dirent) {
2274
2275 dir->dir_error = ENOMEM;
2276 return -1;
2277
2278 }
2279
2280 ZERO_STRUCTP(dirent);
2281
2282 if (dir->dir_list == NULL) {
2283
2284 dir->dir_list = SMB_MALLOC_P(struct smbc_dir_list);
2285 if (!dir->dir_list) {
2286
2287 SAFE_FREE(dirent);
2288 dir->dir_error = ENOMEM;
2289 return -1;
2290
2291 }
2292 ZERO_STRUCTP(dir->dir_list);
2293
2294 dir->dir_end = dir->dir_next = dir->dir_list;
2295 }
2296 else {
2297
2298 dir->dir_end->next = SMB_MALLOC_P(struct smbc_dir_list);
2299
2300 if (!dir->dir_end->next) {
2301
2302 SAFE_FREE(dirent);
2303 dir->dir_error = ENOMEM;
2304 return -1;
2305
2306 }
2307 ZERO_STRUCTP(dir->dir_end->next);
2308
2309 dir->dir_end = dir->dir_end->next;
2310 }
2311
2312 dir->dir_end->next = NULL;
2313 dir->dir_end->dirent = dirent;
2314
2315 dirent->smbc_type = type;
2316 dirent->namelen = name_length;
2317 dirent->commentlen = comment_len;
2318 dirent->dirlen = size;
2319
2320 /*
2321 * dirent->namelen + 1 includes the null (no null termination needed)
2322 * Ditto for dirent->commentlen.
2323 * The space for the two null bytes was allocated.
2324 */
2325 strncpy(dirent->name, (name?name:""), dirent->namelen + 1);
2326 dirent->comment = (char *)(&dirent->name + dirent->namelen + 1);
2327 strncpy(dirent->comment, (comment?comment:""), dirent->commentlen + 1);
2328
2329 return 0;
2330
2331}
2332
2333static void
2334list_unique_wg_fn(const char *name,
2335 uint32 type,
2336 const char *comment,
2337 void *state)
2338{
2339 SMBCFILE *dir = (SMBCFILE *)state;
2340 struct smbc_dir_list *dir_list;
2341 struct smbc_dirent *dirent;
2342 int dirent_type;
2343 int do_remove = 0;
2344
2345 dirent_type = dir->dir_type;
2346
2347 if (add_dirent(dir, name, comment, dirent_type) < 0) {
2348
2349 /* An error occurred, what do we do? */
2350 /* FIXME: Add some code here */
2351 }
2352
2353 /* Point to the one just added */
2354 dirent = dir->dir_end->dirent;
2355
2356 /* See if this was a duplicate */
2357 for (dir_list = dir->dir_list;
2358 dir_list != dir->dir_end;
2359 dir_list = dir_list->next) {
2360 if (! do_remove &&
2361 strcmp(dir_list->dirent->name, dirent->name) == 0) {
2362 /* Duplicate. End end of list need to be removed. */
2363 do_remove = 1;
2364 }
2365
2366 if (do_remove && dir_list->next == dir->dir_end) {
2367 /* Found the end of the list. Remove it. */
2368 dir->dir_end = dir_list;
2369 free(dir_list->next);
2370 free(dirent);
2371 dir_list->next = NULL;
2372 break;
2373 }
2374 }
2375}
2376
2377static void
2378list_fn(const char *name,
2379 uint32 type,
2380 const char *comment,
2381 void *state)
2382{
2383 SMBCFILE *dir = (SMBCFILE *)state;
2384 int dirent_type;
2385
2386 /*
2387 * We need to process the type a little ...
2388 *
2389 * Disk share = 0x00000000
2390 * Print share = 0x00000001
2391 * Comms share = 0x00000002 (obsolete?)
2392 * IPC$ share = 0x00000003
2393 *
2394 * administrative shares:
2395 * ADMIN$, IPC$, C$, D$, E$ ... are type |= 0x80000000
2396 */
2397
2398 if (dir->dir_type == SMBC_FILE_SHARE) {
2399
2400 switch (type) {
2401 case 0 | 0x80000000:
2402 case 0:
2403 dirent_type = SMBC_FILE_SHARE;
2404 break;
2405
2406 case 1:
2407 dirent_type = SMBC_PRINTER_SHARE;
2408 break;
2409
2410 case 2:
2411 dirent_type = SMBC_COMMS_SHARE;
2412 break;
2413
2414 case 3 | 0x80000000:
2415 case 3:
2416 dirent_type = SMBC_IPC_SHARE;
2417 break;
2418
2419 default:
2420 dirent_type = SMBC_FILE_SHARE; /* FIXME, error? */
2421 break;
2422 }
2423 }
2424 else {
2425 dirent_type = dir->dir_type;
2426 }
2427
2428 if (add_dirent(dir, name, comment, dirent_type) < 0) {
2429
2430 /* An error occurred, what do we do? */
2431 /* FIXME: Add some code here */
2432
2433 }
2434}
2435
2436static void
2437dir_list_fn(const char *mnt,
2438 file_info *finfo,
2439 const char *mask,
2440 void *state)
2441{
2442
2443 if (add_dirent((SMBCFILE *)state, finfo->name, "",
2444 (finfo->mode&aDIR?SMBC_DIR:SMBC_FILE)) < 0) {
2445
2446 /* Handle an error ... */
2447
2448 /* FIXME: Add some code ... */
2449
2450 }
2451
2452}
2453
2454static int
2455net_share_enum_rpc(struct cli_state *cli,
2456 void (*fn)(const char *name,
2457 uint32 type,
2458 const char *comment,
2459 void *state),
2460 void *state)
2461{
2462 int i;
2463 WERROR result;
2464 ENUM_HND enum_hnd;
2465 uint32 info_level = 1;
2466 uint32 preferred_len = 0xffffffff;
2467 uint32 type;
2468 SRV_SHARE_INFO_CTR ctr;
2469 fstring name = "";
2470 fstring comment = "";
2471 void *mem_ctx;
2472 struct rpc_pipe_client *pipe_hnd;
2473 NTSTATUS nt_status;
2474
2475 /* Open the server service pipe */
2476 pipe_hnd = cli_rpc_pipe_open_noauth(cli, PI_SRVSVC, &nt_status);
2477 if (!pipe_hnd) {
2478 DEBUG(1, ("net_share_enum_rpc pipe open fail!\n"));
2479 return -1;
2480 }
2481
2482 /* Allocate a context for parsing and for the entries in "ctr" */
2483 mem_ctx = talloc_init("libsmbclient: net_share_enum_rpc");
2484 if (mem_ctx == NULL) {
2485 DEBUG(0, ("out of memory for net_share_enum_rpc!\n"));
2486 cli_rpc_pipe_close(pipe_hnd);
2487 return -1;
2488 }
2489
2490 /* Issue the NetShareEnum RPC call and retrieve the response */
2491 init_enum_hnd(&enum_hnd, 0);
2492 result = rpccli_srvsvc_net_share_enum(pipe_hnd,
2493 mem_ctx,
2494 info_level,
2495 &ctr,
2496 preferred_len,
2497 &enum_hnd);
2498
2499 /* Was it successful? */
2500 if (!W_ERROR_IS_OK(result) || ctr.num_entries == 0) {
2501 /* Nope. Go clean up. */
2502 goto done;
2503 }
2504
2505 /* For each returned entry... */
2506 for (i = 0; i < ctr.num_entries; i++) {
2507
2508 /* pull out the share name */
2509 rpcstr_pull_unistr2_fstring(
2510 name, &ctr.share.info1[i].info_1_str.uni_netname);
2511
2512 /* pull out the share's comment */
2513 rpcstr_pull_unistr2_fstring(
2514 comment, &ctr.share.info1[i].info_1_str.uni_remark);
2515
2516 /* Get the type value */
2517 type = ctr.share.info1[i].info_1.type;
2518
2519 /* Add this share to the list */
2520 (*fn)(name, type, comment, state);
2521 }
2522
2523done:
2524 /* Close the server service pipe */
2525 cli_rpc_pipe_close(pipe_hnd);
2526
2527 /* Free all memory which was allocated for this request */
2528 TALLOC_FREE(mem_ctx);
2529
2530 /* Tell 'em if it worked */
2531 return W_ERROR_IS_OK(result) ? 0 : -1;
2532}
2533
2534
2535
2536static SMBCFILE *
2537smbc_opendir_ctx(SMBCCTX *context,
2538 const char *fname)
2539{
2540 int saved_errno;
2541 fstring server, share, user, password, options;
2542 pstring workgroup;
2543 pstring path;
2544 uint16 mode;
2545 char *p;
2546 SMBCSRV *srv = NULL;
2547 SMBCFILE *dir = NULL;
2548 struct _smbc_callbacks *cb;
2549 struct in_addr rem_ip;
2550
2551 if (!context || !context->internal ||
2552 !context->internal->_initialized) {
2553 DEBUG(4, ("no valid context\n"));
2554 errno = EINVAL + 8192;
2555 return NULL;
2556
2557 }
2558
2559 if (!fname) {
2560 DEBUG(4, ("no valid fname\n"));
2561 errno = EINVAL + 8193;
2562 return NULL;
2563 }
2564
2565 if (smbc_parse_path(context, fname,
2566 workgroup, sizeof(workgroup),
2567 server, sizeof(server),
2568 share, sizeof(share),
2569 path, sizeof(path),
2570 user, sizeof(user),
2571 password, sizeof(password),
2572 options, sizeof(options))) {
2573 DEBUG(4, ("no valid path\n"));
2574 errno = EINVAL + 8194;
2575 return NULL;
2576 }
2577
2578 DEBUG(4, ("parsed path: fname='%s' server='%s' share='%s' "
2579 "path='%s' options='%s'\n",
2580 fname, server, share, path, options));
2581
2582 /* Ensure the options are valid */
2583 if (smbc_check_options(server, share, path, options)) {
2584 DEBUG(4, ("unacceptable options (%s)\n", options));
2585 errno = EINVAL + 8195;
2586 return NULL;
2587 }
2588
2589 if (user[0] == (char)0) fstrcpy(user, context->user);
2590
2591 dir = SMB_MALLOC_P(SMBCFILE);
2592
2593 if (!dir) {
2594
2595 errno = ENOMEM;
2596 return NULL;
2597
2598 }
2599
2600 ZERO_STRUCTP(dir);
2601
2602 dir->cli_fd = 0;
2603 dir->fname = SMB_STRDUP(fname);
2604 dir->srv = NULL;
2605 dir->offset = 0;
2606 dir->file = False;
2607 dir->dir_list = dir->dir_next = dir->dir_end = NULL;
2608
2609 if (server[0] == (char)0) {
2610
2611 int i;
2612 int count;
2613 int max_lmb_count;
2614 struct ip_service *ip_list;
2615 struct ip_service server_addr;
2616 struct user_auth_info u_info;
2617 struct cli_state *cli;
2618
2619 if (share[0] != (char)0 || path[0] != (char)0) {
2620
2621 errno = EINVAL + 8196;
2622 if (dir) {
2623 SAFE_FREE(dir->fname);
2624 SAFE_FREE(dir);
2625 }
2626 return NULL;
2627 }
2628
2629 /* Determine how many local master browsers to query */
2630 max_lmb_count = (context->options.browse_max_lmb_count == 0
2631 ? INT_MAX
2632 : context->options.browse_max_lmb_count);
2633
2634 pstrcpy(u_info.username, user);
2635 pstrcpy(u_info.password, password);
2636
2637 /*
2638 * We have server and share and path empty but options
2639 * requesting that we scan all master browsers for their list
2640 * of workgroups/domains. This implies that we must first try
2641 * broadcast queries to find all master browsers, and if that
2642 * doesn't work, then try our other methods which return only
2643 * a single master browser.
2644 */
2645
2646 ip_list = NULL;
2647 if (!name_resolve_bcast(MSBROWSE, 1, &ip_list, &count)) {
2648
2649 SAFE_FREE(ip_list);
2650
2651 if (!find_master_ip(workgroup, &server_addr.ip)) {
2652
2653 if (dir) {
2654 SAFE_FREE(dir->fname);
2655 SAFE_FREE(dir);
2656 }
2657 errno = ENOENT;
2658 return NULL;
2659 }
2660
2661 ip_list = &server_addr;
2662 count = 1;
2663 }
2664
2665 for (i = 0; i < count && i < max_lmb_count; i++) {
2666 DEBUG(99, ("Found master browser %d of %d: %s\n",
2667 i+1, MAX(count, max_lmb_count),
2668 inet_ntoa(ip_list[i].ip)));
2669
2670 cli = get_ipc_connect_master_ip(&ip_list[i],
2671 workgroup, &u_info);
2672 /* cli == NULL is the master browser refused to talk or
2673 could not be found */
2674 if ( !cli )
2675 continue;
2676
2677 fstrcpy(server, cli->desthost);
2678 cli_shutdown(cli);
2679
2680 DEBUG(4, ("using workgroup %s %s\n",
2681 workgroup, server));
2682
2683 /*
2684 * For each returned master browser IP address, get a
2685 * connection to IPC$ on the server if we do not
2686 * already have one, and determine the
2687 * workgroups/domains that it knows about.
2688 */
2689
2690 srv = smbc_server(context, True, server, "IPC$",
2691 workgroup, user, password);
2692 if (!srv) {
2693 continue;
2694 }
2695
2696 dir->srv = srv;
2697 dir->dir_type = SMBC_WORKGROUP;
2698
2699 /* Now, list the stuff ... */
2700
2701 if (!cli_NetServerEnum(srv->cli,
2702 workgroup,
2703 SV_TYPE_DOMAIN_ENUM,
2704 list_unique_wg_fn,
2705 (void *)dir)) {
2706 continue;
2707 }
2708 }
2709
2710 SAFE_FREE(ip_list);
2711 } else {
2712 /*
2713 * Server not an empty string ... Check the rest and see what
2714 * gives
2715 */
2716 if (*share == '\0') {
2717 if (*path != '\0') {
2718
2719 /* Should not have empty share with path */
2720 errno = EINVAL + 8197;
2721 if (dir) {
2722 SAFE_FREE(dir->fname);
2723 SAFE_FREE(dir);
2724 }
2725 return NULL;
2726
2727 }
2728
2729 /*
2730 * We don't know if <server> is really a server name
2731 * or is a workgroup/domain name. If we already have
2732 * a server structure for it, we'll use it.
2733 * Otherwise, check to see if <server><1D>,
2734 * <server><1B>, or <server><20> translates. We check
2735 * to see if <server> is an IP address first.
2736 */
2737
2738 /*
2739 * See if we have an existing server. Do not
2740 * establish a connection if one does not already
2741 * exist.
2742 */
2743 srv = smbc_server(context, False, server, "IPC$",
2744 workgroup, user, password);
2745
2746 /*
2747 * If no existing server and not an IP addr, look for
2748 * LMB or DMB
2749 */
2750 if (!srv &&
2751 !is_ipaddress(server) &&
2752 (resolve_name(server, &rem_ip, 0x1d) || /* LMB */
2753 resolve_name(server, &rem_ip, 0x1b) )) { /* DMB */
2754
2755 fstring buserver;
2756
2757 dir->dir_type = SMBC_SERVER;
2758
2759 /*
2760 * Get the backup list ...
2761 */
2762 if (!name_status_find(server, 0, 0,
2763 rem_ip, buserver)) {
2764
2765 DEBUG(0, ("Could not get name of "
2766 "local/domain master browser "
2767 "for server %s\n", server));
2768 if (dir) {
2769 SAFE_FREE(dir->fname);
2770 SAFE_FREE(dir);
2771 }
2772 errno = EPERM;
2773 return NULL;
2774
2775 }
2776
2777 /*
2778 * Get a connection to IPC$ on the server if
2779 * we do not already have one
2780 */
2781 srv = smbc_server(context, True,
2782 buserver, "IPC$",
2783 workgroup, user, password);
2784 if (!srv) {
2785 DEBUG(0, ("got no contact to IPC$\n"));
2786 if (dir) {
2787 SAFE_FREE(dir->fname);
2788 SAFE_FREE(dir);
2789 }
2790 return NULL;
2791
2792 }
2793
2794 dir->srv = srv;
2795
2796 /* Now, list the servers ... */
2797 if (!cli_NetServerEnum(srv->cli, server,
2798 0x0000FFFE, list_fn,
2799 (void *)dir)) {
2800
2801 if (dir) {
2802 SAFE_FREE(dir->fname);
2803 SAFE_FREE(dir);
2804 }
2805 return NULL;
2806 }
2807 } else if (srv ||
2808 (resolve_name(server, &rem_ip, 0x20))) {
2809
2810 /* If we hadn't found the server, get one now */
2811 if (!srv) {
2812 srv = smbc_server(context, True,
2813 server, "IPC$",
2814 workgroup,
2815 user, password);
2816 }
2817
2818 if (!srv) {
2819 if (dir) {
2820 SAFE_FREE(dir->fname);
2821 SAFE_FREE(dir);
2822 }
2823 return NULL;
2824
2825 }
2826
2827 dir->dir_type = SMBC_FILE_SHARE;
2828 dir->srv = srv;
2829
2830 /* List the shares ... */
2831
2832 if (net_share_enum_rpc(
2833 srv->cli,
2834 list_fn,
2835 (void *) dir) < 0 &&
2836 cli_RNetShareEnum(
2837 srv->cli,
2838 list_fn,
2839 (void *)dir) < 0) {
2840
2841 errno = cli_errno(srv->cli);
2842 if (dir) {
2843 SAFE_FREE(dir->fname);
2844 SAFE_FREE(dir);
2845 }
2846 return NULL;
2847
2848 }
2849 } else {
2850 /* Neither the workgroup nor server exists */
2851 errno = ECONNREFUSED;
2852 if (dir) {
2853 SAFE_FREE(dir->fname);
2854 SAFE_FREE(dir);
2855 }
2856 return NULL;
2857 }
2858
2859 }
2860 else {
2861 /*
2862 * The server and share are specified ... work from
2863 * there ...
2864 */
2865 pstring targetpath;
2866 struct cli_state *targetcli;
2867
2868 /* We connect to the server and list the directory */
2869 dir->dir_type = SMBC_FILE_SHARE;
2870
2871 srv = smbc_server(context, True, server, share,
2872 workgroup, user, password);
2873
2874 if (!srv) {
2875
2876 if (dir) {
2877 SAFE_FREE(dir->fname);
2878 SAFE_FREE(dir);
2879 }
2880 return NULL;
2881
2882 }
2883
2884 dir->srv = srv;
2885
2886 /* Now, list the files ... */
2887
2888 p = path + strlen(path);
2889 pstrcat(path, "\\*");
2890
2891 if (!cli_resolve_path("", srv->cli, path,
2892 &targetcli, targetpath))
2893 {
2894 d_printf("Could not resolve %s\n", path);
2895 if (dir) {
2896 SAFE_FREE(dir->fname);
2897 SAFE_FREE(dir);
2898 }
2899 return NULL;
2900 }
2901
2902 if (cli_list(targetcli, targetpath,
2903 aDIR | aSYSTEM | aHIDDEN,
2904 dir_list_fn, (void *)dir) < 0) {
2905
2906 if (dir) {
2907 SAFE_FREE(dir->fname);
2908 SAFE_FREE(dir);
2909 }
2910 saved_errno = smbc_errno(context, targetcli);
2911
2912 if (saved_errno == EINVAL) {
2913 /*
2914 * See if they asked to opendir something
2915 * other than a directory. If so, the
2916 * converted error value we got would have
2917 * been EINVAL rather than ENOTDIR.
2918 */
2919 *p = '\0'; /* restore original path */
2920
2921 if (smbc_getatr(context, srv, path,
2922 &mode, NULL,
2923 NULL, NULL, NULL, NULL,
2924 NULL) &&
2925 ! IS_DOS_DIR(mode)) {
2926
2927 /* It is. Correct the error value */
2928 saved_errno = ENOTDIR;
2929 }
2930 }
2931
2932 /*
2933 * If there was an error and the server is no
2934 * good any more...
2935 */
2936 cb = &context->callbacks;
2937 if (cli_is_error(targetcli) &&
2938 cb->check_server_fn(context, srv)) {
2939
2940 /* ... then remove it. */
2941 if (cb->remove_unused_server_fn(context,
2942 srv)) {
2943 /*
2944 * We could not remove the server
2945 * completely, remove it from the
2946 * cache so we will not get it
2947 * again. It will be removed when the
2948 * last file/dir is closed.
2949 */
2950 cb->remove_cached_srv_fn(context, srv);
2951 }
2952 }
2953
2954 errno = saved_errno;
2955 return NULL;
2956 }
2957 }
2958
2959 }
2960
2961 DLIST_ADD(context->internal->_files, dir);
2962 return dir;
2963
2964}
2965
2966/*
2967 * Routine to close a directory
2968 */
2969
2970static int
2971smbc_closedir_ctx(SMBCCTX *context,
2972 SMBCFILE *dir)
2973{
2974
2975 if (!context || !context->internal ||
2976 !context->internal->_initialized) {
2977
2978 errno = EINVAL;
2979 return -1;
2980
2981 }
2982
2983 if (!dir || !DLIST_CONTAINS(context->internal->_files, dir)) {
2984
2985 errno = EBADF;
2986 return -1;
2987
2988 }
2989
2990 smbc_remove_dir(dir); /* Clean it up */
2991
2992 DLIST_REMOVE(context->internal->_files, dir);
2993
2994 if (dir) {
2995
2996 SAFE_FREE(dir->fname);
2997 SAFE_FREE(dir); /* Free the space too */
2998 }
2999
3000 return 0;
3001
3002}
3003
3004static void
3005smbc_readdir_internal(SMBCCTX * context,
3006 struct smbc_dirent *dest,
3007 struct smbc_dirent *src,
3008 int max_namebuf_len)
3009{
3010 if (context->options.urlencode_readdir_entries) {
3011
3012 /* url-encode the name. get back remaining buffer space */
3013 max_namebuf_len =
3014 smbc_urlencode(dest->name, src->name, max_namebuf_len);
3015
3016 /* We now know the name length */
3017 dest->namelen = strlen(dest->name);
3018
3019 /* Save the pointer to the beginning of the comment */
3020 dest->comment = dest->name + dest->namelen + 1;
3021
3022 /* Copy the comment */
3023 strncpy(dest->comment, src->comment, max_namebuf_len - 1);
3024 dest->comment[max_namebuf_len - 1] = '\0';
3025
3026 /* Save other fields */
3027 dest->smbc_type = src->smbc_type;
3028 dest->commentlen = strlen(dest->comment);
3029 dest->dirlen = ((dest->comment + dest->commentlen + 1) -
3030 (char *) dest);
3031 } else {
3032
3033 /* No encoding. Just copy the entry as is. */
3034 memcpy(dest, src, src->dirlen);
3035 dest->comment = (char *)(&dest->name + src->namelen + 1);
3036 }
3037
3038}
3039
3040/*
3041 * Routine to get a directory entry
3042 */
3043
3044struct smbc_dirent *
3045smbc_readdir_ctx(SMBCCTX *context,
3046 SMBCFILE *dir)
3047{
3048 int maxlen;
3049 struct smbc_dirent *dirp, *dirent;
3050
3051 /* Check that all is ok first ... */
3052
3053 if (!context || !context->internal ||
3054 !context->internal->_initialized) {
3055
3056 errno = EINVAL;
3057 DEBUG(0, ("Invalid context in smbc_readdir_ctx()\n"));
3058 return NULL;
3059
3060 }
3061
3062 if (!dir || !DLIST_CONTAINS(context->internal->_files, dir)) {
3063
3064 errno = EBADF;
3065 DEBUG(0, ("Invalid dir in smbc_readdir_ctx()\n"));
3066 return NULL;
3067
3068 }
3069
3070 if (dir->file != False) { /* FIXME, should be dir, perhaps */
3071
3072 errno = ENOTDIR;
3073 DEBUG(0, ("Found file vs directory in smbc_readdir_ctx()\n"));
3074 return NULL;
3075
3076 }
3077
3078 if (!dir->dir_next) {
3079 return NULL;
3080 }
3081
3082 dirent = dir->dir_next->dirent;
3083 if (!dirent) {
3084
3085 errno = ENOENT;
3086 return NULL;
3087
3088 }
3089
3090 dirp = (struct smbc_dirent *)context->internal->_dirent;
3091 maxlen = (sizeof(context->internal->_dirent) -
3092 sizeof(struct smbc_dirent));
3093
3094 smbc_readdir_internal(context, dirp, dirent, maxlen);
3095
3096 dir->dir_next = dir->dir_next->next;
3097
3098 return dirp;
3099}
3100
3101/*
3102 * Routine to get directory entries
3103 */
3104
3105static int
3106smbc_getdents_ctx(SMBCCTX *context,
3107 SMBCFILE *dir,
3108 struct smbc_dirent *dirp,
3109 int count)
3110{
3111 int rem = count;
3112 int reqd;
3113 int maxlen;
3114 char *ndir = (char *)dirp;
3115 struct smbc_dir_list *dirlist;
3116
3117 /* Check that all is ok first ... */
3118
3119 if (!context || !context->internal ||
3120 !context->internal->_initialized) {
3121
3122 errno = EINVAL;
3123 return -1;
3124
3125 }
3126
3127 if (!dir || !DLIST_CONTAINS(context->internal->_files, dir)) {
3128
3129 errno = EBADF;
3130 return -1;
3131
3132 }
3133
3134 if (dir->file != False) { /* FIXME, should be dir, perhaps */
3135
3136 errno = ENOTDIR;
3137 return -1;
3138
3139 }
3140
3141 /*
3142 * Now, retrieve the number of entries that will fit in what was passed
3143 * We have to figure out if the info is in the list, or we need to
3144 * send a request to the server to get the info.
3145 */
3146
3147 while ((dirlist = dir->dir_next)) {
3148 struct smbc_dirent *dirent;
3149
3150 if (!dirlist->dirent) {
3151
3152 errno = ENOENT; /* Bad error */
3153 return -1;
3154
3155 }
3156
3157 /* Do urlencoding of next entry, if so selected */
3158 dirent = (struct smbc_dirent *)context->internal->_dirent;
3159 maxlen = (sizeof(context->internal->_dirent) -
3160 sizeof(struct smbc_dirent));
3161 smbc_readdir_internal(context, dirent, dirlist->dirent, maxlen);
3162
3163 reqd = dirent->dirlen;
3164
3165 if (rem < reqd) {
3166
3167 if (rem < count) { /* We managed to copy something */
3168
3169 errno = 0;
3170 return count - rem;
3171
3172 }
3173 else { /* Nothing copied ... */
3174
3175 errno = EINVAL; /* Not enough space ... */
3176 return -1;
3177
3178 }
3179
3180 }
3181
3182 memcpy(ndir, dirent, reqd); /* Copy the data in ... */
3183
3184 ((struct smbc_dirent *)ndir)->comment =
3185 (char *)(&((struct smbc_dirent *)ndir)->name +
3186 dirent->namelen +
3187 1);
3188
3189 ndir += reqd;
3190
3191 rem -= reqd;
3192
3193 dir->dir_next = dirlist = dirlist -> next;
3194 }
3195
3196 if (rem == count)
3197 return 0;
3198 else
3199 return count - rem;
3200
3201}
3202
3203/*
3204 * Routine to create a directory ...
3205 */
3206
3207static int
3208smbc_mkdir_ctx(SMBCCTX *context,
3209 const char *fname,
3210 mode_t mode)
3211{
3212 SMBCSRV *srv;
3213 fstring server;
3214 fstring share;
3215 fstring user;
3216 fstring password;
3217 fstring workgroup;
3218 pstring path, targetpath;
3219 struct cli_state *targetcli;
3220
3221 if (!context || !context->internal ||
3222 !context->internal->_initialized) {
3223
3224 errno = EINVAL;
3225 return -1;
3226
3227 }
3228
3229 if (!fname) {
3230
3231 errno = EINVAL;
3232 return -1;
3233
3234 }
3235
3236 DEBUG(4, ("smbc_mkdir(%s)\n", fname));
3237
3238 if (smbc_parse_path(context, fname,
3239 workgroup, sizeof(workgroup),
3240 server, sizeof(server),
3241 share, sizeof(share),
3242 path, sizeof(path),
3243 user, sizeof(user),
3244 password, sizeof(password),
3245 NULL, 0)) {
3246 errno = EINVAL;
3247 return -1;
3248 }
3249
3250 if (user[0] == (char)0) fstrcpy(user, context->user);
3251
3252 srv = smbc_server(context, True,
3253 server, share, workgroup, user, password);
3254
3255 if (!srv) {
3256
3257 return -1; /* errno set by smbc_server */
3258
3259 }
3260
3261 /*d_printf(">>>mkdir: resolving %s\n", path);*/
3262 if (!cli_resolve_path( "", srv->cli, path, &targetcli, targetpath))
3263 {
3264 d_printf("Could not resolve %s\n", path);
3265 return -1;
3266 }
3267 /*d_printf(">>>mkdir: resolved path as %s\n", targetpath);*/
3268
3269 if (!cli_mkdir(targetcli, targetpath)) {
3270
3271 errno = smbc_errno(context, targetcli);
3272 return -1;
3273
3274 }
3275
3276 return 0;
3277
3278}
3279
3280/*
3281 * Our list function simply checks to see if a directory is not empty
3282 */
3283
3284static int smbc_rmdir_dirempty = True;
3285
3286static void
3287rmdir_list_fn(const char *mnt,
3288 file_info *finfo,
3289 const char *mask,
3290 void *state)
3291{
3292 if (strncmp(finfo->name, ".", 1) != 0 &&
3293 strncmp(finfo->name, "..", 2) != 0) {
3294
3295 smbc_rmdir_dirempty = False;
3296 }
3297}
3298
3299/*
3300 * Routine to remove a directory
3301 */
3302
3303static int
3304smbc_rmdir_ctx(SMBCCTX *context,
3305 const char *fname)
3306{
3307 SMBCSRV *srv;
3308 fstring server;
3309 fstring share;
3310 fstring user;
3311 fstring password;
3312 fstring workgroup;
3313 pstring path;
3314 pstring targetpath;
3315 struct cli_state *targetcli;
3316
3317 if (!context || !context->internal ||
3318 !context->internal->_initialized) {
3319
3320 errno = EINVAL;
3321 return -1;
3322
3323 }
3324
3325 if (!fname) {
3326
3327 errno = EINVAL;
3328 return -1;
3329
3330 }
3331
3332 DEBUG(4, ("smbc_rmdir(%s)\n", fname));
3333
3334 if (smbc_parse_path(context, fname,
3335 workgroup, sizeof(workgroup),
3336 server, sizeof(server),
3337 share, sizeof(share),
3338 path, sizeof(path),
3339 user, sizeof(user),
3340 password, sizeof(password),
3341 NULL, 0))
3342 {
3343 errno = EINVAL;
3344 return -1;
3345 }
3346
3347 if (user[0] == (char)0) fstrcpy(user, context->user);
3348
3349 srv = smbc_server(context, True,
3350 server, share, workgroup, user, password);
3351
3352 if (!srv) {
3353
3354 return -1; /* errno set by smbc_server */
3355
3356 }
3357
3358 /*d_printf(">>>rmdir: resolving %s\n", path);*/
3359 if (!cli_resolve_path( "", srv->cli, path, &targetcli, targetpath))
3360 {
3361 d_printf("Could not resolve %s\n", path);
3362 return -1;
3363 }
3364 /*d_printf(">>>rmdir: resolved path as %s\n", targetpath);*/
3365
3366
3367 if (!cli_rmdir(targetcli, targetpath)) {
3368
3369 errno = smbc_errno(context, targetcli);
3370
3371 if (errno == EACCES) { /* Check if the dir empty or not */
3372
3373 /* Local storage to avoid buffer overflows */
3374 pstring lpath;
3375
3376 smbc_rmdir_dirempty = True; /* Make this so ... */
3377
3378 pstrcpy(lpath, targetpath);
3379 pstrcat(lpath, "\\*");
3380
3381 if (cli_list(targetcli, lpath,
3382 aDIR | aSYSTEM | aHIDDEN,
3383 rmdir_list_fn, NULL) < 0) {
3384
3385 /* Fix errno to ignore latest error ... */
3386 DEBUG(5, ("smbc_rmdir: "
3387 "cli_list returned an error: %d\n",
3388 smbc_errno(context, targetcli)));
3389 errno = EACCES;
3390
3391 }
3392
3393 if (smbc_rmdir_dirempty)
3394 errno = EACCES;
3395 else
3396 errno = ENOTEMPTY;
3397
3398 }
3399
3400 return -1;
3401
3402 }
3403
3404 return 0;
3405
3406}
3407
3408/*
3409 * Routine to return the current directory position
3410 */
3411
3412static off_t
3413smbc_telldir_ctx(SMBCCTX *context,
3414 SMBCFILE *dir)
3415{
3416 off_t ret_val; /* Squash warnings about cast */
3417
3418 if (!context || !context->internal ||
3419 !context->internal->_initialized) {
3420
3421 errno = EINVAL;
3422 return -1;
3423
3424 }
3425
3426 if (!dir || !DLIST_CONTAINS(context->internal->_files, dir)) {
3427
3428 errno = EBADF;
3429 return -1;
3430
3431 }
3432
3433 if (dir->file != False) { /* FIXME, should be dir, perhaps */
3434
3435 errno = ENOTDIR;
3436 return -1;
3437
3438 }
3439
3440 /*
3441 * We return the pointer here as the offset
3442 */
3443 ret_val = (off_t)(long)dir->dir_next;
3444 return ret_val;
3445
3446}
3447
3448/*
3449 * A routine to run down the list and see if the entry is OK
3450 */
3451
3452struct smbc_dir_list *
3453smbc_check_dir_ent(struct smbc_dir_list *list,
3454 struct smbc_dirent *dirent)
3455{
3456
3457 /* Run down the list looking for what we want */
3458
3459 if (dirent) {
3460
3461 struct smbc_dir_list *tmp = list;
3462
3463 while (tmp) {
3464
3465 if (tmp->dirent == dirent)
3466 return tmp;
3467
3468 tmp = tmp->next;
3469
3470 }
3471
3472 }
3473
3474 return NULL; /* Not found, or an error */
3475
3476}
3477
3478
3479/*
3480 * Routine to seek on a directory
3481 */
3482
3483static int
3484smbc_lseekdir_ctx(SMBCCTX *context,
3485 SMBCFILE *dir,
3486 off_t offset)
3487{
3488 long int l_offset = offset; /* Handle problems of size */
3489 struct smbc_dirent *dirent = (struct smbc_dirent *)l_offset;
3490 struct smbc_dir_list *list_ent = (struct smbc_dir_list *)NULL;
3491
3492 if (!context || !context->internal ||
3493 !context->internal->_initialized) {
3494
3495 errno = EINVAL;
3496 return -1;
3497
3498 }
3499
3500 if (dir->file != False) { /* FIXME, should be dir, perhaps */
3501
3502 errno = ENOTDIR;
3503 return -1;
3504
3505 }
3506
3507 /* Now, check what we were passed and see if it is OK ... */
3508
3509 if (dirent == NULL) { /* Seek to the begining of the list */
3510
3511 dir->dir_next = dir->dir_list;
3512 return 0;
3513
3514 }
3515
3516 /* Now, run down the list and make sure that the entry is OK */
3517 /* This may need to be changed if we change the format of the list */
3518
3519 if ((list_ent = smbc_check_dir_ent(dir->dir_list, dirent)) == NULL) {
3520
3521 errno = EINVAL; /* Bad entry */
3522 return -1;
3523
3524 }
3525
3526 dir->dir_next = list_ent;
3527
3528 return 0;
3529
3530}
3531
3532/*
3533 * Routine to fstat a dir
3534 */
3535
3536static int
3537smbc_fstatdir_ctx(SMBCCTX *context,
3538 SMBCFILE *dir,
3539 struct stat *st)
3540{
3541
3542 if (!context || !context->internal ||
3543 !context->internal->_initialized) {
3544
3545 errno = EINVAL;
3546 return -1;
3547
3548 }
3549
3550 /* No code yet ... */
3551
3552 return 0;
3553
3554}
3555
3556static int
3557smbc_chmod_ctx(SMBCCTX *context,
3558 const char *fname,
3559 mode_t newmode)
3560{
3561 SMBCSRV *srv;
3562 fstring server;
3563 fstring share;
3564 fstring user;
3565 fstring password;
3566 fstring workgroup;
3567 pstring path;
3568 uint16 mode;
3569
3570 if (!context || !context->internal ||
3571 !context->internal->_initialized) {
3572
3573 errno = EINVAL; /* Best I can think of ... */
3574 return -1;
3575
3576 }
3577
3578 if (!fname) {
3579
3580 errno = EINVAL;
3581 return -1;
3582
3583 }
3584
3585 DEBUG(4, ("smbc_chmod(%s, 0%3o)\n", fname, newmode));
3586
3587 if (smbc_parse_path(context, fname,
3588 workgroup, sizeof(workgroup),
3589 server, sizeof(server),
3590 share, sizeof(share),
3591 path, sizeof(path),
3592 user, sizeof(user),
3593 password, sizeof(password),
3594 NULL, 0)) {
3595 errno = EINVAL;
3596 return -1;
3597 }
3598
3599 if (user[0] == (char)0) fstrcpy(user, context->user);
3600
3601 srv = smbc_server(context, True,
3602 server, share, workgroup, user, password);
3603
3604 if (!srv) {
3605 return -1; /* errno set by smbc_server */
3606 }
3607
3608 mode = 0;
3609
3610 if (!(newmode & (S_IWUSR | S_IWGRP | S_IWOTH))) mode |= aRONLY;
3611 if ((newmode & S_IXUSR) && lp_map_archive(-1)) mode |= aARCH;
3612 if ((newmode & S_IXGRP) && lp_map_system(-1)) mode |= aSYSTEM;
3613 if ((newmode & S_IXOTH) && lp_map_hidden(-1)) mode |= aHIDDEN;
3614
3615 if (!cli_setatr(srv->cli, path, mode, 0)) {
3616 errno = smbc_errno(context, srv->cli);
3617 return -1;
3618 }
3619
3620 return 0;
3621}
3622
3623static int
3624smbc_utimes_ctx(SMBCCTX *context,
3625 const char *fname,
3626 struct timeval *tbuf)
3627{
3628 SMBCSRV *srv;
3629 fstring server;
3630 fstring share;
3631 fstring user;
3632 fstring password;
3633 fstring workgroup;
3634 pstring path;
3635 time_t access_time;
3636 time_t write_time;
3637
3638 if (!context || !context->internal ||
3639 !context->internal->_initialized) {
3640
3641 errno = EINVAL; /* Best I can think of ... */
3642 return -1;
3643
3644 }
3645
3646 if (!fname) {
3647
3648 errno = EINVAL;
3649 return -1;
3650
3651 }
3652
3653 if (tbuf == NULL) {
3654 access_time = write_time = time(NULL);
3655 } else {
3656 access_time = tbuf[0].tv_sec;
3657 write_time = tbuf[1].tv_sec;
3658 }
3659
3660 if (DEBUGLVL(4))
3661 {
3662 char *p;
3663 char atimebuf[32];
3664 char mtimebuf[32];
3665
3666 strncpy(atimebuf, ctime(&access_time), sizeof(atimebuf) - 1);
3667 atimebuf[sizeof(atimebuf) - 1] = '\0';
3668 if ((p = strchr(atimebuf, '\n')) != NULL) {
3669 *p = '\0';
3670 }
3671
3672 strncpy(mtimebuf, ctime(&write_time), sizeof(mtimebuf) - 1);
3673 mtimebuf[sizeof(mtimebuf) - 1] = '\0';
3674 if ((p = strchr(mtimebuf, '\n')) != NULL) {
3675 *p = '\0';
3676 }
3677
3678 dbgtext("smbc_utimes(%s, atime = %s mtime = %s)\n",
3679 fname, atimebuf, mtimebuf);
3680 }
3681
3682 if (smbc_parse_path(context, fname,
3683 workgroup, sizeof(workgroup),
3684 server, sizeof(server),
3685 share, sizeof(share),
3686 path, sizeof(path),
3687 user, sizeof(user),
3688 password, sizeof(password),
3689 NULL, 0)) {
3690 errno = EINVAL;
3691 return -1;
3692 }
3693
3694 if (user[0] == (char)0) fstrcpy(user, context->user);
3695
3696 srv = smbc_server(context, True,
3697 server, share, workgroup, user, password);
3698
3699 if (!srv) {
3700 return -1; /* errno set by smbc_server */
3701 }
3702
3703 if (!smbc_setatr(context, srv, path,
3704 0, access_time, write_time, 0, 0)) {
3705 return -1; /* errno set by smbc_setatr */
3706 }
3707
3708 return 0;
3709}
3710
3711
3712/* The MSDN is contradictory over the ordering of ACE entries in an ACL.
3713 However NT4 gives a "The information may have been modified by a
3714 computer running Windows NT 5.0" if denied ACEs do not appear before
3715 allowed ACEs. */
3716
3717static int
3718ace_compare(SEC_ACE *ace1,
3719 SEC_ACE *ace2)
3720{
3721 if (sec_ace_equal(ace1, ace2))
3722 return 0;
3723
3724 if (ace1->type != ace2->type)
3725 return ace2->type - ace1->type;
3726
3727 if (sid_compare(&ace1->trustee, &ace2->trustee))
3728 return sid_compare(&ace1->trustee, &ace2->trustee);
3729
3730 if (ace1->flags != ace2->flags)
3731 return ace1->flags - ace2->flags;
3732
3733 if (ace1->access_mask != ace2->access_mask)
3734 return ace1->access_mask - ace2->access_mask;
3735
3736 if (ace1->size != ace2->size)
3737 return ace1->size - ace2->size;
3738
3739 return memcmp(ace1, ace2, sizeof(SEC_ACE));
3740}
3741
3742
3743static void
3744sort_acl(SEC_ACL *the_acl)
3745{
3746 uint32 i;
3747 if (!the_acl) return;
3748
3749 qsort(the_acl->aces, the_acl->num_aces, sizeof(the_acl->aces[0]),
3750 QSORT_CAST ace_compare);
3751
3752 for (i=1;i<the_acl->num_aces;) {
3753 if (sec_ace_equal(&the_acl->aces[i-1], &the_acl->aces[i])) {
3754 int j;
3755 for (j=i; j<the_acl->num_aces-1; j++) {
3756 the_acl->aces[j] = the_acl->aces[j+1];
3757 }
3758 the_acl->num_aces--;
3759 } else {
3760 i++;
3761 }
3762 }
3763}
3764
3765/* convert a SID to a string, either numeric or username/group */
3766static void
3767convert_sid_to_string(struct cli_state *ipc_cli,
3768 POLICY_HND *pol,
3769 fstring str,
3770 BOOL numeric,
3771 DOM_SID *sid)
3772{
3773 char **domains = NULL;
3774 char **names = NULL;
3775 enum lsa_SidType *types = NULL;
3776 struct rpc_pipe_client *pipe_hnd = find_lsa_pipe_hnd(ipc_cli);
3777 sid_to_string(str, sid);
3778
3779 if (numeric) {
3780 return; /* no lookup desired */
3781 }
3782
3783 if (!pipe_hnd) {
3784 return;
3785 }
3786
3787 /* Ask LSA to convert the sid to a name */
3788
3789 if (!NT_STATUS_IS_OK(rpccli_lsa_lookup_sids(pipe_hnd, ipc_cli->mem_ctx,
3790 pol, 1, sid, &domains,
3791 &names, &types)) ||
3792 !domains || !domains[0] || !names || !names[0]) {
3793 return;
3794 }
3795
3796 /* Converted OK */
3797
3798 slprintf(str, sizeof(fstring) - 1, "%s%s%s",
3799 domains[0], lp_winbind_separator(),
3800 names[0]);
3801}
3802
3803/* convert a string to a SID, either numeric or username/group */
3804static BOOL
3805convert_string_to_sid(struct cli_state *ipc_cli,
3806 POLICY_HND *pol,
3807 BOOL numeric,
3808 DOM_SID *sid,
3809 const char *str)
3810{
3811 enum lsa_SidType *types = NULL;
3812 DOM_SID *sids = NULL;
3813 BOOL result = True;
3814 struct rpc_pipe_client *pipe_hnd = find_lsa_pipe_hnd(ipc_cli);
3815
3816 if (!pipe_hnd) {
3817 return False;
3818 }
3819
3820 if (numeric) {
3821 if (strncmp(str, "S-", 2) == 0) {
3822 return string_to_sid(sid, str);
3823 }
3824
3825 result = False;
3826 goto done;
3827 }
3828
3829 if (!NT_STATUS_IS_OK(rpccli_lsa_lookup_names(pipe_hnd, ipc_cli->mem_ctx,
3830 pol, 1, &str, NULL, &sids,
3831 &types))) {
3832 result = False;
3833 goto done;
3834 }
3835
3836 sid_copy(sid, &sids[0]);
3837 done:
3838
3839 return result;
3840}
3841
3842
3843/* parse an ACE in the same format as print_ace() */
3844static BOOL
3845parse_ace(struct cli_state *ipc_cli,
3846 POLICY_HND *pol,
3847 SEC_ACE *ace,
3848 BOOL numeric,
3849 char *str)
3850{
3851 char *p;
3852 const char *cp;
3853 fstring tok;
3854 unsigned int atype;
3855 unsigned int aflags;
3856 unsigned int amask;
3857 DOM_SID sid;
3858 SEC_ACCESS mask;
3859 const struct perm_value *v;
3860 struct perm_value {
3861 const char *perm;
3862 uint32 mask;
3863 };
3864
3865 /* These values discovered by inspection */
3866 static const struct perm_value special_values[] = {
3867 { "R", 0x00120089 },
3868 { "W", 0x00120116 },
3869 { "X", 0x001200a0 },
3870 { "D", 0x00010000 },
3871 { "P", 0x00040000 },
3872 { "O", 0x00080000 },
3873 { NULL, 0 },
3874 };
3875
3876 static const struct perm_value standard_values[] = {
3877 { "READ", 0x001200a9 },
3878 { "CHANGE", 0x001301bf },
3879 { "FULL", 0x001f01ff },
3880 { NULL, 0 },
3881 };
3882
3883
3884 ZERO_STRUCTP(ace);
3885 p = strchr_m(str,':');
3886 if (!p) return False;
3887 *p = '\0';
3888 p++;
3889 /* Try to parse numeric form */
3890
3891 if (sscanf(p, "%i/%i/%i", &atype, &aflags, &amask) == 3 &&
3892 convert_string_to_sid(ipc_cli, pol, numeric, &sid, str)) {
3893 goto done;
3894 }
3895
3896 /* Try to parse text form */
3897
3898 if (!convert_string_to_sid(ipc_cli, pol, numeric, &sid, str)) {
3899 return False;
3900 }
3901
3902 cp = p;
3903 if (!next_token(&cp, tok, "/", sizeof(fstring))) {
3904 return False;
3905 }
3906
3907 if (StrnCaseCmp(tok, "ALLOWED", strlen("ALLOWED")) == 0) {
3908 atype = SEC_ACE_TYPE_ACCESS_ALLOWED;
3909 } else if (StrnCaseCmp(tok, "DENIED", strlen("DENIED")) == 0) {
3910 atype = SEC_ACE_TYPE_ACCESS_DENIED;
3911 } else {
3912 return False;
3913 }
3914
3915 /* Only numeric form accepted for flags at present */
3916
3917 if (!(next_token(&cp, tok, "/", sizeof(fstring)) &&
3918 sscanf(tok, "%i", &aflags))) {
3919 return False;
3920 }
3921
3922 if (!next_token(&cp, tok, "/", sizeof(fstring))) {
3923 return False;
3924 }
3925
3926 if (strncmp(tok, "0x", 2) == 0) {
3927 if (sscanf(tok, "%i", &amask) != 1) {
3928 return False;
3929 }
3930 goto done;
3931 }
3932
3933 for (v = standard_values; v->perm; v++) {
3934 if (strcmp(tok, v->perm) == 0) {
3935 amask = v->mask;
3936 goto done;
3937 }
3938 }
3939
3940 p = tok;
3941
3942 while(*p) {
3943 BOOL found = False;
3944
3945 for (v = special_values; v->perm; v++) {
3946 if (v->perm[0] == *p) {
3947 amask |= v->mask;
3948 found = True;
3949 }
3950 }
3951
3952 if (!found) return False;
3953 p++;
3954 }
3955
3956 if (*p) {
3957 return False;
3958 }
3959
3960 done:
3961 mask = amask;
3962 init_sec_ace(ace, &sid, atype, mask, aflags);
3963 return True;
3964}
3965
3966/* add an ACE to a list of ACEs in a SEC_ACL */
3967static BOOL
3968add_ace(SEC_ACL **the_acl,
3969 SEC_ACE *ace,
3970 TALLOC_CTX *ctx)
3971{
3972 SEC_ACL *newacl;
3973 SEC_ACE *aces;
3974
3975 if (! *the_acl) {
3976 (*the_acl) = make_sec_acl(ctx, 3, 1, ace);
3977 return True;
3978 }
3979
3980 if ((aces = SMB_CALLOC_ARRAY(SEC_ACE, 1+(*the_acl)->num_aces)) == NULL) {
3981 return False;
3982 }
3983 memcpy(aces, (*the_acl)->aces, (*the_acl)->num_aces * sizeof(SEC_ACE));
3984 memcpy(aces+(*the_acl)->num_aces, ace, sizeof(SEC_ACE));
3985 newacl = make_sec_acl(ctx, (*the_acl)->revision,
3986 1+(*the_acl)->num_aces, aces);
3987 SAFE_FREE(aces);
3988 (*the_acl) = newacl;
3989 return True;
3990}
3991
3992
3993/* parse a ascii version of a security descriptor */
3994static SEC_DESC *
3995sec_desc_parse(TALLOC_CTX *ctx,
3996 struct cli_state *ipc_cli,
3997 POLICY_HND *pol,
3998 BOOL numeric,
3999 char *str)
4000{
4001 const char *p = str;
4002 fstring tok;
4003 SEC_DESC *ret = NULL;
4004 size_t sd_size;
4005 DOM_SID *group_sid=NULL;
4006 DOM_SID *owner_sid=NULL;
4007 SEC_ACL *dacl=NULL;
4008 int revision=1;
4009
4010 while (next_token(&p, tok, "\t,\r\n", sizeof(tok))) {
4011
4012 if (StrnCaseCmp(tok,"REVISION:", 9) == 0) {
4013 revision = strtol(tok+9, NULL, 16);
4014 continue;
4015 }
4016
4017 if (StrnCaseCmp(tok,"OWNER:", 6) == 0) {
4018 if (owner_sid) {
4019 DEBUG(5, ("OWNER specified more than once!\n"));
4020 goto done;
4021 }
4022 owner_sid = SMB_CALLOC_ARRAY(DOM_SID, 1);
4023 if (!owner_sid ||
4024 !convert_string_to_sid(ipc_cli, pol,
4025 numeric,
4026 owner_sid, tok+6)) {
4027 DEBUG(5, ("Failed to parse owner sid\n"));
4028 goto done;
4029 }
4030 continue;
4031 }
4032
4033 if (StrnCaseCmp(tok,"OWNER+:", 7) == 0) {
4034 if (owner_sid) {
4035 DEBUG(5, ("OWNER specified more than once!\n"));
4036 goto done;
4037 }
4038 owner_sid = SMB_CALLOC_ARRAY(DOM_SID, 1);
4039 if (!owner_sid ||
4040 !convert_string_to_sid(ipc_cli, pol,
4041 False,
4042 owner_sid, tok+7)) {
4043 DEBUG(5, ("Failed to parse owner sid\n"));
4044 goto done;
4045 }
4046 continue;
4047 }
4048
4049 if (StrnCaseCmp(tok,"GROUP:", 6) == 0) {
4050 if (group_sid) {
4051 DEBUG(5, ("GROUP specified more than once!\n"));
4052 goto done;
4053 }
4054 group_sid = SMB_CALLOC_ARRAY(DOM_SID, 1);
4055 if (!group_sid ||
4056 !convert_string_to_sid(ipc_cli, pol,
4057 numeric,
4058 group_sid, tok+6)) {
4059 DEBUG(5, ("Failed to parse group sid\n"));
4060 goto done;
4061 }
4062 continue;
4063 }
4064
4065 if (StrnCaseCmp(tok,"GROUP+:", 7) == 0) {
4066 if (group_sid) {
4067 DEBUG(5, ("GROUP specified more than once!\n"));
4068 goto done;
4069 }
4070 group_sid = SMB_CALLOC_ARRAY(DOM_SID, 1);
4071 if (!group_sid ||
4072 !convert_string_to_sid(ipc_cli, pol,
4073 False,
4074 group_sid, tok+6)) {
4075 DEBUG(5, ("Failed to parse group sid\n"));
4076 goto done;
4077 }
4078 continue;
4079 }
4080
4081 if (StrnCaseCmp(tok,"ACL:", 4) == 0) {
4082 SEC_ACE ace;
4083 if (!parse_ace(ipc_cli, pol, &ace, numeric, tok+4)) {
4084 DEBUG(5, ("Failed to parse ACL %s\n", tok));
4085 goto done;
4086 }
4087 if(!add_ace(&dacl, &ace, ctx)) {
4088 DEBUG(5, ("Failed to add ACL %s\n", tok));
4089 goto done;
4090 }
4091 continue;
4092 }
4093
4094 if (StrnCaseCmp(tok,"ACL+:", 5) == 0) {
4095 SEC_ACE ace;
4096 if (!parse_ace(ipc_cli, pol, &ace, False, tok+5)) {
4097 DEBUG(5, ("Failed to parse ACL %s\n", tok));
4098 goto done;
4099 }
4100 if(!add_ace(&dacl, &ace, ctx)) {
4101 DEBUG(5, ("Failed to add ACL %s\n", tok));
4102 goto done;
4103 }
4104 continue;
4105 }
4106
4107 DEBUG(5, ("Failed to parse security descriptor\n"));
4108 goto done;
4109 }
4110
4111 ret = make_sec_desc(ctx, revision, SEC_DESC_SELF_RELATIVE,
4112 owner_sid, group_sid, NULL, dacl, &sd_size);
4113
4114 done:
4115 SAFE_FREE(group_sid);
4116 SAFE_FREE(owner_sid);
4117
4118 return ret;
4119}
4120
4121
4122/* Obtain the current dos attributes */
4123static DOS_ATTR_DESC *
4124dos_attr_query(SMBCCTX *context,
4125 TALLOC_CTX *ctx,
4126 const char *filename,
4127 SMBCSRV *srv)
4128{
4129 struct timespec create_time_ts;
4130 struct timespec write_time_ts;
4131 struct timespec access_time_ts;
4132 struct timespec change_time_ts;
4133 SMB_OFF_T size = 0;
4134 uint16 mode = 0;
4135 SMB_INO_T inode = 0;
4136 DOS_ATTR_DESC *ret;
4137
4138 ret = TALLOC_P(ctx, DOS_ATTR_DESC);
4139 if (!ret) {
4140 errno = ENOMEM;
4141 return NULL;
4142 }
4143
4144 /* Obtain the DOS attributes */
4145 if (!smbc_getatr(context, srv, CONST_DISCARD(char *, filename),
4146 &mode, &size,
4147 &create_time_ts,
4148 &access_time_ts,
4149 &write_time_ts,
4150 &change_time_ts,
4151 &inode)) {
4152
4153 errno = smbc_errno(context, srv->cli);
4154 DEBUG(5, ("dos_attr_query Failed to query old attributes\n"));
4155 return NULL;
4156
4157 }
4158
4159 ret->mode = mode;
4160 ret->size = size;
4161 ret->create_time = convert_timespec_to_time_t(create_time_ts);
4162 ret->access_time = convert_timespec_to_time_t(access_time_ts);
4163 ret->write_time = convert_timespec_to_time_t(write_time_ts);
4164 ret->change_time = convert_timespec_to_time_t(change_time_ts);
4165 ret->inode = inode;
4166
4167 return ret;
4168}
4169
4170
4171/* parse a ascii version of a security descriptor */
4172static void
4173dos_attr_parse(SMBCCTX *context,
4174 DOS_ATTR_DESC *dad,
4175 SMBCSRV *srv,
4176 char *str)
4177{
4178 int n;
4179 const char *p = str;
4180 fstring tok;
4181 struct {
4182 const char * create_time_attr;
4183 const char * access_time_attr;
4184 const char * write_time_attr;
4185 const char * change_time_attr;
4186 } attr_strings;
4187
4188 /* Determine whether to use old-style or new-style attribute names */
4189 if (context->internal->_full_time_names) {
4190 /* new-style names */
4191 attr_strings.create_time_attr = "CREATE_TIME";
4192 attr_strings.access_time_attr = "ACCESS_TIME";
4193 attr_strings.write_time_attr = "WRITE_TIME";
4194 attr_strings.change_time_attr = "CHANGE_TIME";
4195 } else {
4196 /* old-style names */
4197 attr_strings.create_time_attr = NULL;
4198 attr_strings.access_time_attr = "A_TIME";
4199 attr_strings.write_time_attr = "M_TIME";
4200 attr_strings.change_time_attr = "C_TIME";
4201 }
4202
4203 /* if this is to set the entire ACL... */
4204 if (*str == '*') {
4205 /* ... then increment past the first colon if there is one */
4206 if ((p = strchr(str, ':')) != NULL) {
4207 ++p;
4208 } else {
4209 p = str;
4210 }
4211 }
4212
4213 while (next_token(&p, tok, "\t,\r\n", sizeof(tok))) {
4214
4215 if (StrnCaseCmp(tok, "MODE:", 5) == 0) {
4216 dad->mode = strtol(tok+5, NULL, 16);
4217 continue;
4218 }
4219
4220 if (StrnCaseCmp(tok, "SIZE:", 5) == 0) {
4221 dad->size = (SMB_OFF_T)atof(tok+5);
4222 continue;
4223 }
4224
4225 n = strlen(attr_strings.access_time_attr);
4226 if (StrnCaseCmp(tok, attr_strings.access_time_attr, n) == 0) {
4227 dad->access_time = (time_t)strtol(tok+n+1, NULL, 10);
4228 continue;
4229 }
4230
4231 n = strlen(attr_strings.change_time_attr);
4232 if (StrnCaseCmp(tok, attr_strings.change_time_attr, n) == 0) {
4233 dad->change_time = (time_t)strtol(tok+n+1, NULL, 10);
4234 continue;
4235 }
4236
4237 n = strlen(attr_strings.write_time_attr);
4238 if (StrnCaseCmp(tok, attr_strings.write_time_attr, n) == 0) {
4239 dad->write_time = (time_t)strtol(tok+n+1, NULL, 10);
4240 continue;
4241 }
4242
4243 if (attr_strings.create_time_attr != NULL) {
4244 n = strlen(attr_strings.create_time_attr);
4245 if (StrnCaseCmp(tok, attr_strings.create_time_attr,
4246 n) == 0) {
4247 dad->create_time = (time_t)strtol(tok+n+1,
4248 NULL, 10);
4249 continue;
4250 }
4251 }
4252
4253 if (StrnCaseCmp(tok, "INODE:", 6) == 0) {
4254 dad->inode = (SMB_INO_T)atof(tok+6);
4255 continue;
4256 }
4257 }
4258}
4259
4260/*****************************************************
4261 Retrieve the acls for a file.
4262*******************************************************/
4263
4264static int
4265cacl_get(SMBCCTX *context,
4266 TALLOC_CTX *ctx,
4267 SMBCSRV *srv,
4268 struct cli_state *ipc_cli,
4269 POLICY_HND *pol,
4270 char *filename,
4271 char *attr_name,
4272 char *buf,
4273 int bufsize)
4274{
4275 uint32 i;
4276 int n = 0;
4277 int n_used;
4278 BOOL all;
4279 BOOL all_nt;
4280 BOOL all_nt_acls;
4281 BOOL all_dos;
4282 BOOL some_nt;
4283 BOOL some_dos;
4284 BOOL exclude_nt_revision = False;
4285 BOOL exclude_nt_owner = False;
4286 BOOL exclude_nt_group = False;
4287 BOOL exclude_nt_acl = False;
4288 BOOL exclude_dos_mode = False;
4289 BOOL exclude_dos_size = False;
4290 BOOL exclude_dos_create_time = False;
4291 BOOL exclude_dos_access_time = False;
4292 BOOL exclude_dos_write_time = False;
4293 BOOL exclude_dos_change_time = False;
4294 BOOL exclude_dos_inode = False;
4295 BOOL numeric = True;
4296 BOOL determine_size = (bufsize == 0);
4297 int fnum = -1;
4298 SEC_DESC *sd;
4299 fstring sidstr;
4300 fstring name_sandbox;
4301 char *name;
4302 char *pExclude;
4303 char *p;
4304 struct timespec create_time_ts;
4305 struct timespec write_time_ts;
4306 struct timespec access_time_ts;
4307 struct timespec change_time_ts;
4308 time_t create_time = (time_t)0;
4309 time_t write_time = (time_t)0;
4310 time_t access_time = (time_t)0;
4311 time_t change_time = (time_t)0;
4312 SMB_OFF_T size = 0;
4313 uint16 mode = 0;
4314 SMB_INO_T ino = 0;
4315 struct cli_state *cli = srv->cli;
4316 struct {
4317 const char * create_time_attr;
4318 const char * access_time_attr;
4319 const char * write_time_attr;
4320 const char * change_time_attr;
4321 } attr_strings;
4322 struct {
4323 const char * create_time_attr;
4324 const char * access_time_attr;
4325 const char * write_time_attr;
4326 const char * change_time_attr;
4327 } excl_attr_strings;
4328
4329 /* Determine whether to use old-style or new-style attribute names */
4330 if (context->internal->_full_time_names) {
4331 /* new-style names */
4332 attr_strings.create_time_attr = "CREATE_TIME";
4333 attr_strings.access_time_attr = "ACCESS_TIME";
4334 attr_strings.write_time_attr = "WRITE_TIME";
4335 attr_strings.change_time_attr = "CHANGE_TIME";
4336
4337 excl_attr_strings.create_time_attr = "CREATE_TIME";
4338 excl_attr_strings.access_time_attr = "ACCESS_TIME";
4339 excl_attr_strings.write_time_attr = "WRITE_TIME";
4340 excl_attr_strings.change_time_attr = "CHANGE_TIME";
4341 } else {
4342 /* old-style names */
4343 attr_strings.create_time_attr = NULL;
4344 attr_strings.access_time_attr = "A_TIME";
4345 attr_strings.write_time_attr = "M_TIME";
4346 attr_strings.change_time_attr = "C_TIME";
4347
4348 excl_attr_strings.create_time_attr = NULL;
4349 excl_attr_strings.access_time_attr = "dos_attr.A_TIME";
4350 excl_attr_strings.write_time_attr = "dos_attr.M_TIME";
4351 excl_attr_strings.change_time_attr = "dos_attr.C_TIME";
4352 }
4353
4354 /* Copy name so we can strip off exclusions (if any are specified) */
4355 strncpy(name_sandbox, attr_name, sizeof(name_sandbox) - 1);
4356
4357 /* Ensure name is null terminated */
4358 name_sandbox[sizeof(name_sandbox) - 1] = '\0';
4359
4360 /* Play in the sandbox */
4361 name = name_sandbox;
4362
4363 /* If there are any exclusions, point to them and mask them from name */
4364 if ((pExclude = strchr(name, '!')) != NULL)
4365 {
4366 *pExclude++ = '\0';
4367 }
4368
4369 all = (StrnCaseCmp(name, "system.*", 8) == 0);
4370 all_nt = (StrnCaseCmp(name, "system.nt_sec_desc.*", 20) == 0);
4371 all_nt_acls = (StrnCaseCmp(name, "system.nt_sec_desc.acl.*", 24) == 0);
4372 all_dos = (StrnCaseCmp(name, "system.dos_attr.*", 17) == 0);
4373 some_nt = (StrnCaseCmp(name, "system.nt_sec_desc.", 19) == 0);
4374 some_dos = (StrnCaseCmp(name, "system.dos_attr.", 16) == 0);
4375 numeric = (* (name + strlen(name) - 1) != '+');
4376
4377 /* Look for exclusions from "all" requests */
4378 if (all || all_nt || all_dos) {
4379
4380 /* Exclusions are delimited by '!' */
4381 for (;
4382 pExclude != NULL;
4383 pExclude = (p == NULL ? NULL : p + 1)) {
4384
4385 /* Find end of this exclusion name */
4386 if ((p = strchr(pExclude, '!')) != NULL)
4387 {
4388 *p = '\0';
4389 }
4390
4391 /* Which exclusion name is this? */
4392 if (StrCaseCmp(pExclude, "nt_sec_desc.revision") == 0) {
4393 exclude_nt_revision = True;
4394 }
4395 else if (StrCaseCmp(pExclude, "nt_sec_desc.owner") == 0) {
4396 exclude_nt_owner = True;
4397 }
4398 else if (StrCaseCmp(pExclude, "nt_sec_desc.group") == 0) {
4399 exclude_nt_group = True;
4400 }
4401 else if (StrCaseCmp(pExclude, "nt_sec_desc.acl") == 0) {
4402 exclude_nt_acl = True;
4403 }
4404 else if (StrCaseCmp(pExclude, "dos_attr.mode") == 0) {
4405 exclude_dos_mode = True;
4406 }
4407 else if (StrCaseCmp(pExclude, "dos_attr.size") == 0) {
4408 exclude_dos_size = True;
4409 }
4410 else if (excl_attr_strings.create_time_attr != NULL &&
4411 StrCaseCmp(pExclude,
4412 excl_attr_strings.change_time_attr) == 0) {
4413 exclude_dos_create_time = True;
4414 }
4415 else if (StrCaseCmp(pExclude,
4416 excl_attr_strings.access_time_attr) == 0) {
4417 exclude_dos_access_time = True;
4418 }
4419 else if (StrCaseCmp(pExclude,
4420 excl_attr_strings.write_time_attr) == 0) {
4421 exclude_dos_write_time = True;
4422 }
4423 else if (StrCaseCmp(pExclude,
4424 excl_attr_strings.change_time_attr) == 0) {
4425 exclude_dos_change_time = True;
4426 }
4427 else if (StrCaseCmp(pExclude, "dos_attr.inode") == 0) {
4428 exclude_dos_inode = True;
4429 }
4430 else {
4431 DEBUG(5, ("cacl_get received unknown exclusion: %s\n",
4432 pExclude));
4433 errno = ENOATTR;
4434 return -1;
4435 }
4436 }
4437 }
4438
4439 n_used = 0;
4440
4441 /*
4442 * If we are (possibly) talking to an NT or new system and some NT
4443 * attributes have been requested...
4444 */
4445 if (ipc_cli && (all || some_nt || all_nt_acls)) {
4446 /* Point to the portion after "system.nt_sec_desc." */
4447 name += 19; /* if (all) this will be invalid but unused */
4448
4449 /* ... then obtain any NT attributes which were requested */
4450 fnum = cli_nt_create(cli, filename, CREATE_ACCESS_READ);
4451
4452 if (fnum == -1) {
4453 DEBUG(5, ("cacl_get failed to open %s: %s\n",
4454 filename, cli_errstr(cli)));
4455 errno = 0;
4456 return -1;
4457 }
4458
4459 sd = cli_query_secdesc(cli, fnum, ctx);
4460
4461 if (!sd) {
4462 DEBUG(5,
4463 ("cacl_get Failed to query old descriptor\n"));
4464 errno = 0;
4465 return -1;
4466 }
4467
4468 cli_close(cli, fnum);
4469
4470 if (! exclude_nt_revision) {
4471 if (all || all_nt) {
4472 if (determine_size) {
4473 p = talloc_asprintf(ctx,
4474 "REVISION:%d",
4475 sd->revision);
4476 if (!p) {
4477 errno = ENOMEM;
4478 return -1;
4479 }
4480 n = strlen(p);
4481 } else {
4482 n = snprintf(buf, bufsize,
4483 "REVISION:%d",
4484 sd->revision);
4485 }
4486 } else if (StrCaseCmp(name, "revision") == 0) {
4487 if (determine_size) {
4488 p = talloc_asprintf(ctx, "%d",
4489 sd->revision);
4490 if (!p) {
4491 errno = ENOMEM;
4492 return -1;
4493 }
4494 n = strlen(p);
4495 } else {
4496 n = snprintf(buf, bufsize, "%d",
4497 sd->revision);
4498 }
4499 }
4500
4501 if (!determine_size && n > bufsize) {
4502 errno = ERANGE;
4503 return -1;
4504 }
4505 buf += n;
4506 n_used += n;
4507 bufsize -= n;
4508 }
4509
4510 if (! exclude_nt_owner) {
4511 /* Get owner and group sid */
4512 if (sd->owner_sid) {
4513 convert_sid_to_string(ipc_cli, pol,
4514 sidstr,
4515 numeric,
4516 sd->owner_sid);
4517 } else {
4518 fstrcpy(sidstr, "");
4519 }
4520
4521 if (all || all_nt) {
4522 if (determine_size) {
4523 p = talloc_asprintf(ctx, ",OWNER:%s",
4524 sidstr);
4525 if (!p) {
4526 errno = ENOMEM;
4527 return -1;
4528 }
4529 n = strlen(p);
4530 } else {
4531 n = snprintf(buf, bufsize,
4532 ",OWNER:%s", sidstr);
4533 }
4534 } else if (StrnCaseCmp(name, "owner", 5) == 0) {
4535 if (determine_size) {
4536 p = talloc_asprintf(ctx, "%s", sidstr);
4537 if (!p) {
4538 errno = ENOMEM;
4539 return -1;
4540 }
4541 n = strlen(p);
4542 } else {
4543 n = snprintf(buf, bufsize, "%s",
4544 sidstr);
4545 }
4546 }
4547
4548 if (!determine_size && n > bufsize) {
4549 errno = ERANGE;
4550 return -1;
4551 }
4552 buf += n;
4553 n_used += n;
4554 bufsize -= n;
4555 }
4556
4557 if (! exclude_nt_group) {
4558 if (sd->group_sid) {
4559 convert_sid_to_string(ipc_cli, pol,
4560 sidstr, numeric,
4561 sd->group_sid);
4562 } else {
4563 fstrcpy(sidstr, "");
4564 }
4565
4566 if (all || all_nt) {
4567 if (determine_size) {
4568 p = talloc_asprintf(ctx, ",GROUP:%s",
4569 sidstr);
4570 if (!p) {
4571 errno = ENOMEM;
4572 return -1;
4573 }
4574 n = strlen(p);
4575 } else {
4576 n = snprintf(buf, bufsize,
4577 ",GROUP:%s", sidstr);
4578 }
4579 } else if (StrnCaseCmp(name, "group", 5) == 0) {
4580 if (determine_size) {
4581 p = talloc_asprintf(ctx, "%s", sidstr);
4582 if (!p) {
4583 errno = ENOMEM;
4584 return -1;
4585 }
4586 n = strlen(p);
4587 } else {
4588 n = snprintf(buf, bufsize,
4589 "%s", sidstr);
4590 }
4591 }
4592
4593 if (!determine_size && n > bufsize) {
4594 errno = ERANGE;
4595 return -1;
4596 }
4597 buf += n;
4598 n_used += n;
4599 bufsize -= n;
4600 }
4601
4602 if (! exclude_nt_acl) {
4603 /* Add aces to value buffer */
4604 for (i = 0; sd->dacl && i < sd->dacl->num_aces; i++) {
4605
4606 SEC_ACE *ace = &sd->dacl->aces[i];
4607 convert_sid_to_string(ipc_cli, pol,
4608 sidstr, numeric,
4609 &ace->trustee);
4610
4611 if (all || all_nt) {
4612 if (determine_size) {
4613 p = talloc_asprintf(
4614 ctx,
4615 ",ACL:"
4616 "%s:%d/%d/0x%08x",
4617 sidstr,
4618 ace->type,
4619 ace->flags,
4620 ace->access_mask);
4621 if (!p) {
4622 errno = ENOMEM;
4623 return -1;
4624 }
4625 n = strlen(p);
4626 } else {
4627 n = snprintf(
4628 buf, bufsize,
4629 ",ACL:%s:%d/%d/0x%08x",
4630 sidstr,
4631 ace->type,
4632 ace->flags,
4633 ace->access_mask);
4634 }
4635 } else if ((StrnCaseCmp(name, "acl", 3) == 0 &&
4636 StrCaseCmp(name+3, sidstr) == 0) ||
4637 (StrnCaseCmp(name, "acl+", 4) == 0 &&
4638 StrCaseCmp(name+4, sidstr) == 0)) {
4639 if (determine_size) {
4640 p = talloc_asprintf(
4641 ctx,
4642 "%d/%d/0x%08x",
4643 ace->type,
4644 ace->flags,
4645 ace->access_mask);
4646 if (!p) {
4647 errno = ENOMEM;
4648 return -1;
4649 }
4650 n = strlen(p);
4651 } else {
4652 n = snprintf(buf, bufsize,
4653 "%d/%d/0x%08x",
4654 ace->type,
4655 ace->flags,
4656 ace->access_mask);
4657 }
4658 } else if (all_nt_acls) {
4659 if (determine_size) {
4660 p = talloc_asprintf(
4661 ctx,
4662 "%s%s:%d/%d/0x%08x",
4663 i ? "," : "",
4664 sidstr,
4665 ace->type,
4666 ace->flags,
4667 ace->access_mask);
4668 if (!p) {
4669 errno = ENOMEM;
4670 return -1;
4671 }
4672 n = strlen(p);
4673 } else {
4674 n = snprintf(buf, bufsize,
4675 "%s%s:%d/%d/0x%08x",
4676 i ? "," : "",
4677 sidstr,
4678 ace->type,
4679 ace->flags,
4680 ace->access_mask);
4681 }
4682 }
4683 if (n > bufsize) {
4684 errno = ERANGE;
4685 return -1;
4686 }
4687 buf += n;
4688 n_used += n;
4689 bufsize -= n;
4690 }
4691 }
4692
4693 /* Restore name pointer to its original value */
4694 name -= 19;
4695 }
4696
4697 if (all || some_dos) {
4698 /* Point to the portion after "system.dos_attr." */
4699 name += 16; /* if (all) this will be invalid but unused */
4700
4701 /* Obtain the DOS attributes */
4702 if (!smbc_getatr(context, srv, filename, &mode, &size,
4703 &create_time_ts,
4704 &access_time_ts,
4705 &write_time_ts,
4706 &change_time_ts,
4707 &ino)) {
4708
4709 errno = smbc_errno(context, srv->cli);
4710 return -1;
4711
4712 }
4713
4714 create_time = convert_timespec_to_time_t(create_time_ts);
4715 access_time = convert_timespec_to_time_t(access_time_ts);
4716 write_time = convert_timespec_to_time_t(write_time_ts);
4717 change_time = convert_timespec_to_time_t(change_time_ts);
4718
4719 if (! exclude_dos_mode) {
4720 if (all || all_dos) {
4721 if (determine_size) {
4722 p = talloc_asprintf(ctx,
4723 "%sMODE:0x%x",
4724 (ipc_cli &&
4725 (all || some_nt)
4726 ? ","
4727 : ""),
4728 mode);
4729 if (!p) {
4730 errno = ENOMEM;
4731 return -1;
4732 }
4733 n = strlen(p);
4734 } else {
4735 n = snprintf(buf, bufsize,
4736 "%sMODE:0x%x",
4737 (ipc_cli &&
4738 (all || some_nt)
4739 ? ","
4740 : ""),
4741 mode);
4742 }
4743 } else if (StrCaseCmp(name, "mode") == 0) {
4744 if (determine_size) {
4745 p = talloc_asprintf(ctx, "0x%x", mode);
4746 if (!p) {
4747 errno = ENOMEM;
4748 return -1;
4749 }
4750 n = strlen(p);
4751 } else {
4752 n = snprintf(buf, bufsize,
4753 "0x%x", mode);
4754 }
4755 }
4756
4757 if (!determine_size && n > bufsize) {
4758 errno = ERANGE;
4759 return -1;
4760 }
4761 buf += n;
4762 n_used += n;
4763 bufsize -= n;
4764 }
4765
4766 if (! exclude_dos_size) {
4767 if (all || all_dos) {
4768 if (determine_size) {
4769 p = talloc_asprintf(
4770 ctx,
4771 ",SIZE:%.0f",
4772 (double)size);
4773 if (!p) {
4774 errno = ENOMEM;
4775 return -1;
4776 }
4777 n = strlen(p);
4778 } else {
4779 n = snprintf(buf, bufsize,
4780 ",SIZE:%.0f",
4781 (double)size);
4782 }
4783 } else if (StrCaseCmp(name, "size") == 0) {
4784 if (determine_size) {
4785 p = talloc_asprintf(
4786 ctx,
4787 "%.0f",
4788 (double)size);
4789 if (!p) {
4790 errno = ENOMEM;
4791 return -1;
4792 }
4793 n = strlen(p);
4794 } else {
4795 n = snprintf(buf, bufsize,
4796 "%.0f",
4797 (double)size);
4798 }
4799 }
4800
4801 if (!determine_size && n > bufsize) {
4802 errno = ERANGE;
4803 return -1;
4804 }
4805 buf += n;
4806 n_used += n;
4807 bufsize -= n;
4808 }
4809
4810 if (! exclude_dos_create_time &&
4811 attr_strings.create_time_attr != NULL) {
4812 if (all || all_dos) {
4813 if (determine_size) {
4814 p = talloc_asprintf(ctx,
4815 ",%s:%lu",
4816 attr_strings.create_time_attr,
4817 create_time);
4818 if (!p) {
4819 errno = ENOMEM;
4820 return -1;
4821 }
4822 n = strlen(p);
4823 } else {
4824 n = snprintf(buf, bufsize,
4825 ",%s:%lu",
4826 attr_strings.create_time_attr,
4827 create_time);
4828 }
4829 } else if (StrCaseCmp(name, attr_strings.create_time_attr) == 0) {
4830 if (determine_size) {
4831 p = talloc_asprintf(ctx, "%lu", create_time);
4832 if (!p) {
4833 errno = ENOMEM;
4834 return -1;
4835 }
4836 n = strlen(p);
4837 } else {
4838 n = snprintf(buf, bufsize,
4839 "%lu", create_time);
4840 }
4841 }
4842
4843 if (!determine_size && n > bufsize) {
4844 errno = ERANGE;
4845 return -1;
4846 }
4847 buf += n;
4848 n_used += n;
4849 bufsize -= n;
4850 }
4851
4852 if (! exclude_dos_access_time) {
4853 if (all || all_dos) {
4854 if (determine_size) {
4855 p = talloc_asprintf(ctx,
4856 ",%s:%lu",
4857 attr_strings.access_time_attr,
4858 access_time);
4859 if (!p) {
4860 errno = ENOMEM;
4861 return -1;
4862 }
4863 n = strlen(p);
4864 } else {
4865 n = snprintf(buf, bufsize,
4866 ",%s:%lu",
4867 attr_strings.access_time_attr,
4868 access_time);
4869 }
4870 } else if (StrCaseCmp(name, attr_strings.access_time_attr) == 0) {
4871 if (determine_size) {
4872 p = talloc_asprintf(ctx, "%lu", access_time);
4873 if (!p) {
4874 errno = ENOMEM;
4875 return -1;
4876 }
4877 n = strlen(p);
4878 } else {
4879 n = snprintf(buf, bufsize,
4880 "%lu", access_time);
4881 }
4882 }
4883
4884 if (!determine_size && n > bufsize) {
4885 errno = ERANGE;
4886 return -1;
4887 }
4888 buf += n;
4889 n_used += n;
4890 bufsize -= n;
4891 }
4892
4893 if (! exclude_dos_write_time) {
4894 if (all || all_dos) {
4895 if (determine_size) {
4896 p = talloc_asprintf(ctx,
4897 ",%s:%lu",
4898 attr_strings.write_time_attr,
4899 write_time);
4900 if (!p) {
4901 errno = ENOMEM;
4902 return -1;
4903 }
4904 n = strlen(p);
4905 } else {
4906 n = snprintf(buf, bufsize,
4907 ",%s:%lu",
4908 attr_strings.write_time_attr,
4909 write_time);
4910 }
4911 } else if (StrCaseCmp(name, attr_strings.write_time_attr) == 0) {
4912 if (determine_size) {
4913 p = talloc_asprintf(ctx, "%lu", write_time);
4914 if (!p) {
4915 errno = ENOMEM;
4916 return -1;
4917 }
4918 n = strlen(p);
4919 } else {
4920 n = snprintf(buf, bufsize,
4921 "%lu", write_time);
4922 }
4923 }
4924
4925 if (!determine_size && n > bufsize) {
4926 errno = ERANGE;
4927 return -1;
4928 }
4929 buf += n;
4930 n_used += n;
4931 bufsize -= n;
4932 }
4933
4934 if (! exclude_dos_change_time) {
4935 if (all || all_dos) {
4936 if (determine_size) {
4937 p = talloc_asprintf(ctx,
4938 ",%s:%lu",
4939 attr_strings.change_time_attr,
4940 change_time);
4941 if (!p) {
4942 errno = ENOMEM;
4943 return -1;
4944 }
4945 n = strlen(p);
4946 } else {
4947 n = snprintf(buf, bufsize,
4948 ",%s:%lu",
4949 attr_strings.change_time_attr,
4950 change_time);
4951 }
4952 } else if (StrCaseCmp(name, attr_strings.change_time_attr) == 0) {
4953 if (determine_size) {
4954 p = talloc_asprintf(ctx, "%lu", change_time);
4955 if (!p) {
4956 errno = ENOMEM;
4957 return -1;
4958 }
4959 n = strlen(p);
4960 } else {
4961 n = snprintf(buf, bufsize,
4962 "%lu", change_time);
4963 }
4964 }
4965
4966 if (!determine_size && n > bufsize) {
4967 errno = ERANGE;
4968 return -1;
4969 }
4970 buf += n;
4971 n_used += n;
4972 bufsize -= n;
4973 }
4974
4975 if (! exclude_dos_inode) {
4976 if (all || all_dos) {
4977 if (determine_size) {
4978 p = talloc_asprintf(
4979 ctx,
4980 ",INODE:%.0f",
4981 (double)ino);
4982 if (!p) {
4983 errno = ENOMEM;
4984 return -1;
4985 }
4986 n = strlen(p);
4987 } else {
4988 n = snprintf(buf, bufsize,
4989 ",INODE:%.0f",
4990 (double) ino);
4991 }
4992 } else if (StrCaseCmp(name, "inode") == 0) {
4993 if (determine_size) {
4994 p = talloc_asprintf(
4995 ctx,
4996 "%.0f",
4997 (double) ino);
4998 if (!p) {
4999 errno = ENOMEM;
5000 return -1;
5001 }
5002 n = strlen(p);
5003 } else {
5004 n = snprintf(buf, bufsize,
5005 "%.0f",
5006 (double) ino);
5007 }
5008 }
5009
5010 if (!determine_size && n > bufsize) {
5011 errno = ERANGE;
5012 return -1;
5013 }
5014 buf += n;
5015 n_used += n;
5016 bufsize -= n;
5017 }
5018
5019 /* Restore name pointer to its original value */
5020 name -= 16;
5021 }
5022
5023 if (n_used == 0) {
5024 errno = ENOATTR;
5025 return -1;
5026 }
5027
5028 return n_used;
5029}
5030
5031
5032/*****************************************************
5033set the ACLs on a file given an ascii description
5034*******************************************************/
5035static int
5036cacl_set(TALLOC_CTX *ctx,
5037 struct cli_state *cli,
5038 struct cli_state *ipc_cli,
5039 POLICY_HND *pol,
5040 const char *filename,
5041 const char *the_acl,
5042 int mode,
5043 int flags)
5044{
5045 int fnum;
5046 int err = 0;
5047 SEC_DESC *sd = NULL, *old;
5048 SEC_ACL *dacl = NULL;
5049 DOM_SID *owner_sid = NULL;
5050 DOM_SID *group_sid = NULL;
5051 uint32 i, j;
5052 size_t sd_size;
5053 int ret = 0;
5054 char *p;
5055 BOOL numeric = True;
5056
5057 /* the_acl will be null for REMOVE_ALL operations */
5058 if (the_acl) {
5059 numeric = ((p = strchr(the_acl, ':')) != NULL &&
5060 p > the_acl &&
5061 p[-1] != '+');
5062
5063 /* if this is to set the entire ACL... */
5064 if (*the_acl == '*') {
5065 /* ... then increment past the first colon */
5066 the_acl = p + 1;
5067 }
5068
5069 sd = sec_desc_parse(ctx, ipc_cli, pol, numeric,
5070 CONST_DISCARD(char *, the_acl));
5071
5072 if (!sd) {
5073 errno = EINVAL;
5074 return -1;
5075 }
5076 }
5077
5078 /* SMBC_XATTR_MODE_REMOVE_ALL is the only caller
5079 that doesn't deref sd */
5080
5081 if (!sd && (mode != SMBC_XATTR_MODE_REMOVE_ALL)) {
5082 errno = EINVAL;
5083 return -1;
5084 }
5085
5086 /* The desired access below is the only one I could find that works
5087 with NT4, W2KP and Samba */
5088
5089 fnum = cli_nt_create(cli, filename, CREATE_ACCESS_READ);
5090
5091 if (fnum == -1) {
5092 DEBUG(5, ("cacl_set failed to open %s: %s\n",
5093 filename, cli_errstr(cli)));
5094 errno = 0;
5095 return -1;
5096 }
5097
5098 old = cli_query_secdesc(cli, fnum, ctx);
5099
5100 if (!old) {
5101 DEBUG(5, ("cacl_set Failed to query old descriptor\n"));
5102 errno = 0;
5103 return -1;
5104 }
5105
5106 cli_close(cli, fnum);
5107
5108 switch (mode) {
5109 case SMBC_XATTR_MODE_REMOVE_ALL:
5110 old->dacl->num_aces = 0;
5111 SAFE_FREE(old->dacl->aces);
5112 SAFE_FREE(old->dacl);
5113 old->dacl = NULL;
5114 dacl = old->dacl;
5115 break;
5116
5117 case SMBC_XATTR_MODE_REMOVE:
5118 for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
5119 BOOL found = False;
5120
5121 for (j=0;old->dacl && j<old->dacl->num_aces;j++) {
5122 if (sec_ace_equal(&sd->dacl->aces[i],
5123 &old->dacl->aces[j])) {
5124 uint32 k;
5125 for (k=j; k<old->dacl->num_aces-1;k++) {
5126 old->dacl->aces[k] =
5127 old->dacl->aces[k+1];
5128 }
5129 old->dacl->num_aces--;
5130 if (old->dacl->num_aces == 0) {
5131 SAFE_FREE(old->dacl->aces);
5132 SAFE_FREE(old->dacl);
5133 old->dacl = NULL;
5134 }
5135 found = True;
5136 dacl = old->dacl;
5137 break;
5138 }
5139 }
5140
5141 if (!found) {
5142 err = ENOATTR;
5143 ret = -1;
5144 goto failed;
5145 }
5146 }
5147 break;
5148
5149 case SMBC_XATTR_MODE_ADD:
5150 for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
5151 BOOL found = False;
5152
5153 for (j=0;old->dacl && j<old->dacl->num_aces;j++) {
5154 if (sid_equal(&sd->dacl->aces[i].trustee,
5155 &old->dacl->aces[j].trustee)) {
5156 if (!(flags & SMBC_XATTR_FLAG_CREATE)) {
5157 err = EEXIST;
5158 ret = -1;
5159 goto failed;
5160 }
5161 old->dacl->aces[j] = sd->dacl->aces[i];
5162 ret = -1;
5163 found = True;
5164 }
5165 }
5166
5167 if (!found && (flags & SMBC_XATTR_FLAG_REPLACE)) {
5168 err = ENOATTR;
5169 ret = -1;
5170 goto failed;
5171 }
5172
5173 for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
5174 add_ace(&old->dacl, &sd->dacl->aces[i], ctx);
5175 }
5176 }
5177 dacl = old->dacl;
5178 break;
5179
5180 case SMBC_XATTR_MODE_SET:
5181 old = sd;
5182 owner_sid = old->owner_sid;
5183 group_sid = old->group_sid;
5184 dacl = old->dacl;
5185 break;
5186
5187 case SMBC_XATTR_MODE_CHOWN:
5188 owner_sid = sd->owner_sid;
5189 break;
5190
5191 case SMBC_XATTR_MODE_CHGRP:
5192 group_sid = sd->group_sid;
5193 break;
5194 }
5195
5196 /* Denied ACE entries must come before allowed ones */
5197 sort_acl(old->dacl);
5198
5199 /* Create new security descriptor and set it */
5200 sd = make_sec_desc(ctx, old->revision, SEC_DESC_SELF_RELATIVE,
5201 owner_sid, group_sid, NULL, dacl, &sd_size);
5202
5203 fnum = cli_nt_create(cli, filename,
5204 WRITE_DAC_ACCESS | WRITE_OWNER_ACCESS);
5205
5206 if (fnum == -1) {
5207 DEBUG(5, ("cacl_set failed to open %s: %s\n",
5208 filename, cli_errstr(cli)));
5209 errno = 0;
5210 return -1;
5211 }
5212
5213 if (!cli_set_secdesc(cli, fnum, sd)) {
5214 DEBUG(5, ("ERROR: secdesc set failed: %s\n", cli_errstr(cli)));
5215 ret = -1;
5216 }
5217
5218 /* Clean up */
5219
5220 failed:
5221 cli_close(cli, fnum);
5222
5223 if (err != 0) {
5224 errno = err;
5225 }
5226
5227 return ret;
5228}
5229
5230
5231static int
5232smbc_setxattr_ctx(SMBCCTX *context,
5233 const char *fname,
5234 const char *name,
5235 const void *value,
5236 size_t size,
5237 int flags)
5238{
5239 int ret;
5240 int ret2;
5241 SMBCSRV *srv;
5242 SMBCSRV *ipc_srv;
5243 fstring server;
5244 fstring share;
5245 fstring user;
5246 fstring password;
5247 fstring workgroup;
5248 pstring path;
5249 TALLOC_CTX *ctx;
5250 POLICY_HND pol;
5251 DOS_ATTR_DESC *dad;
5252 struct {
5253 const char * create_time_attr;
5254 const char * access_time_attr;
5255 const char * write_time_attr;
5256 const char * change_time_attr;
5257 } attr_strings;
5258
5259 if (!context || !context->internal ||
5260 !context->internal->_initialized) {
5261
5262 errno = EINVAL; /* Best I can think of ... */
5263 return -1;
5264
5265 }
5266
5267 if (!fname) {
5268
5269 errno = EINVAL;
5270 return -1;
5271
5272 }
5273
5274 DEBUG(4, ("smbc_setxattr(%s, %s, %.*s)\n",
5275 fname, name, (int) size, (const char*)value));
5276
5277 if (smbc_parse_path(context, fname,
5278 workgroup, sizeof(workgroup),
5279 server, sizeof(server),
5280 share, sizeof(share),
5281 path, sizeof(path),
5282 user, sizeof(user),
5283 password, sizeof(password),
5284 NULL, 0)) {
5285 errno = EINVAL;
5286 return -1;
5287 }
5288
5289 if (user[0] == (char)0) fstrcpy(user, context->user);
5290
5291 srv = smbc_server(context, True,
5292 server, share, workgroup, user, password);
5293 if (!srv) {
5294 return -1; /* errno set by smbc_server */
5295 }
5296
5297 if (! srv->no_nt_session) {
5298 ipc_srv = smbc_attr_server(context, server, share,
5299 workgroup, user, password,
5300 &pol);
5301 srv->no_nt_session = True;
5302 } else {
5303 ipc_srv = NULL;
5304 }
5305
5306 ctx = talloc_init("smbc_setxattr");
5307 if (!ctx) {
5308 errno = ENOMEM;
5309 return -1;
5310 }
5311
5312 /*
5313 * Are they asking to set the entire set of known attributes?
5314 */
5315 if (StrCaseCmp(name, "system.*") == 0 ||
5316 StrCaseCmp(name, "system.*+") == 0) {
5317 /* Yup. */
5318 char *namevalue =
5319 talloc_asprintf(ctx, "%s:%s",
5320 name+7, (const char *) value);
5321 if (! namevalue) {
5322 errno = ENOMEM;
5323 ret = -1;
5324 return -1;
5325 }
5326
5327 if (ipc_srv) {
5328 ret = cacl_set(ctx, srv->cli,
5329 ipc_srv->cli, &pol, path,
5330 namevalue,
5331 (*namevalue == '*'
5332 ? SMBC_XATTR_MODE_SET
5333 : SMBC_XATTR_MODE_ADD),
5334 flags);
5335 } else {
5336 ret = 0;
5337 }
5338
5339 /* get a DOS Attribute Descriptor with current attributes */
5340 dad = dos_attr_query(context, ctx, path, srv);
5341 if (dad) {
5342 /* Overwrite old with new, using what was provided */
5343 dos_attr_parse(context, dad, srv, namevalue);
5344
5345 /* Set the new DOS attributes */
5346 if (! smbc_setatr(context, srv, path,
5347 dad->create_time,
5348 dad->access_time,
5349 dad->write_time,
5350 dad->change_time,
5351 dad->mode)) {
5352
5353 /* cause failure if NT failed too */
5354 dad = NULL;
5355 }
5356 }
5357
5358 /* we only fail if both NT and DOS sets failed */
5359 if (ret < 0 && ! dad) {
5360 ret = -1; /* in case dad was null */
5361 }
5362 else {
5363 ret = 0;
5364 }
5365
5366 talloc_destroy(ctx);
5367 return ret;
5368 }
5369
5370 /*
5371 * Are they asking to set an access control element or to set
5372 * the entire access control list?
5373 */
5374 if (StrCaseCmp(name, "system.nt_sec_desc.*") == 0 ||
5375 StrCaseCmp(name, "system.nt_sec_desc.*+") == 0 ||
5376 StrCaseCmp(name, "system.nt_sec_desc.revision") == 0 ||
5377 StrnCaseCmp(name, "system.nt_sec_desc.acl", 22) == 0 ||
5378 StrnCaseCmp(name, "system.nt_sec_desc.acl+", 23) == 0) {
5379
5380 /* Yup. */
5381 char *namevalue =
5382 talloc_asprintf(ctx, "%s:%s",
5383 name+19, (const char *) value);
5384
5385 if (! ipc_srv) {
5386 ret = -1; /* errno set by smbc_server() */
5387 }
5388 else if (! namevalue) {
5389 errno = ENOMEM;
5390 ret = -1;
5391 } else {
5392 ret = cacl_set(ctx, srv->cli,
5393 ipc_srv->cli, &pol, path,
5394 namevalue,
5395 (*namevalue == '*'
5396 ? SMBC_XATTR_MODE_SET
5397 : SMBC_XATTR_MODE_ADD),
5398 flags);
5399 }
5400 talloc_destroy(ctx);
5401 return ret;
5402 }
5403
5404 /*
5405 * Are they asking to set the owner?
5406 */
5407 if (StrCaseCmp(name, "system.nt_sec_desc.owner") == 0 ||
5408 StrCaseCmp(name, "system.nt_sec_desc.owner+") == 0) {
5409
5410 /* Yup. */
5411 char *namevalue =
5412 talloc_asprintf(ctx, "%s:%s",
5413 name+19, (const char *) value);
5414
5415 if (! ipc_srv) {
5416
5417 ret = -1; /* errno set by smbc_server() */
5418 }
5419 else if (! namevalue) {
5420 errno = ENOMEM;
5421 ret = -1;
5422 } else {
5423 ret = cacl_set(ctx, srv->cli,
5424 ipc_srv->cli, &pol, path,
5425 namevalue, SMBC_XATTR_MODE_CHOWN, 0);
5426 }
5427 talloc_destroy(ctx);
5428 return ret;
5429 }
5430
5431 /*
5432 * Are they asking to set the group?
5433 */
5434 if (StrCaseCmp(name, "system.nt_sec_desc.group") == 0 ||
5435 StrCaseCmp(name, "system.nt_sec_desc.group+") == 0) {
5436
5437 /* Yup. */
5438 char *namevalue =
5439 talloc_asprintf(ctx, "%s:%s",
5440 name+19, (const char *) value);
5441
5442 if (! ipc_srv) {
5443 /* errno set by smbc_server() */
5444 ret = -1;
5445 }
5446 else if (! namevalue) {
5447 errno = ENOMEM;
5448 ret = -1;
5449 } else {
5450 ret = cacl_set(ctx, srv->cli,
5451 ipc_srv->cli, &pol, path,
5452 namevalue, SMBC_XATTR_MODE_CHOWN, 0);
5453 }
5454 talloc_destroy(ctx);
5455 return ret;
5456 }
5457
5458 /* Determine whether to use old-style or new-style attribute names */
5459 if (context->internal->_full_time_names) {
5460 /* new-style names */
5461 attr_strings.create_time_attr = "system.dos_attr.CREATE_TIME";
5462 attr_strings.access_time_attr = "system.dos_attr.ACCESS_TIME";
5463 attr_strings.write_time_attr = "system.dos_attr.WRITE_TIME";
5464 attr_strings.change_time_attr = "system.dos_attr.CHANGE_TIME";
5465 } else {
5466 /* old-style names */
5467 attr_strings.create_time_attr = NULL;
5468 attr_strings.access_time_attr = "system.dos_attr.A_TIME";
5469 attr_strings.write_time_attr = "system.dos_attr.M_TIME";
5470 attr_strings.change_time_attr = "system.dos_attr.C_TIME";
5471 }
5472
5473 /*
5474 * Are they asking to set a DOS attribute?
5475 */
5476 if (StrCaseCmp(name, "system.dos_attr.*") == 0 ||
5477 StrCaseCmp(name, "system.dos_attr.mode") == 0 ||
5478 (attr_strings.create_time_attr != NULL &&
5479 StrCaseCmp(name, attr_strings.create_time_attr) == 0) ||
5480 StrCaseCmp(name, attr_strings.access_time_attr) == 0 ||
5481 StrCaseCmp(name, attr_strings.write_time_attr) == 0 ||
5482 StrCaseCmp(name, attr_strings.change_time_attr) == 0) {
5483
5484 /* get a DOS Attribute Descriptor with current attributes */
5485 dad = dos_attr_query(context, ctx, path, srv);
5486 if (dad) {
5487 char *namevalue =
5488 talloc_asprintf(ctx, "%s:%s",
5489 name+16, (const char *) value);
5490 if (! namevalue) {
5491 errno = ENOMEM;
5492 ret = -1;
5493 } else {
5494 /* Overwrite old with provided new params */
5495 dos_attr_parse(context, dad, srv, namevalue);
5496
5497 /* Set the new DOS attributes */
5498 ret2 = smbc_setatr(context, srv, path,
5499 dad->create_time,
5500 dad->access_time,
5501 dad->write_time,
5502 dad->change_time,
5503 dad->mode);
5504
5505 /* ret2 has True (success) / False (failure) */
5506 if (ret2) {
5507 ret = 0;
5508 } else {
5509 ret = -1;
5510 }
5511 }
5512 } else {
5513 ret = -1;
5514 }
5515
5516 talloc_destroy(ctx);
5517 return ret;
5518 }
5519
5520 /* Unsupported attribute name */
5521 talloc_destroy(ctx);
5522 errno = EINVAL;
5523 return -1;
5524}
5525
5526static int
5527smbc_getxattr_ctx(SMBCCTX *context,
5528 const char *fname,
5529 const char *name,
5530 const void *value,
5531 size_t size)
5532{
5533 int ret;
5534 SMBCSRV *srv;
5535 SMBCSRV *ipc_srv;
5536 fstring server;
5537 fstring share;
5538 fstring user;
5539 fstring password;
5540 fstring workgroup;
5541 pstring path;
5542 TALLOC_CTX *ctx;
5543 POLICY_HND pol;
5544 struct {
5545 const char * create_time_attr;
5546 const char * access_time_attr;
5547 const char * write_time_attr;
5548 const char * change_time_attr;
5549 } attr_strings;
5550
5551
5552 if (!context || !context->internal ||
5553 !context->internal->_initialized) {
5554
5555 errno = EINVAL; /* Best I can think of ... */
5556 return -1;
5557
5558 }
5559
5560 if (!fname) {
5561
5562 errno = EINVAL;
5563 return -1;
5564
5565 }
5566
5567 DEBUG(4, ("smbc_getxattr(%s, %s)\n", fname, name));
5568
5569 if (smbc_parse_path(context, fname,
5570 workgroup, sizeof(workgroup),
5571 server, sizeof(server),
5572 share, sizeof(share),
5573 path, sizeof(path),
5574 user, sizeof(user),
5575 password, sizeof(password),
5576 NULL, 0)) {
5577 errno = EINVAL;
5578 return -1;
5579 }
5580
5581 if (user[0] == (char)0) fstrcpy(user, context->user);
5582
5583 srv = smbc_server(context, True,
5584 server, share, workgroup, user, password);
5585 if (!srv) {
5586 return -1; /* errno set by smbc_server */
5587 }
5588
5589 if (! srv->no_nt_session) {
5590 ipc_srv = smbc_attr_server(context, server, share,
5591 workgroup, user, password,
5592 &pol);
5593 if (! ipc_srv) {
5594 srv->no_nt_session = True;
5595 }
5596 } else {
5597 ipc_srv = NULL;
5598 }
5599
5600 ctx = talloc_init("smbc:getxattr");
5601 if (!ctx) {
5602 errno = ENOMEM;
5603 return -1;
5604 }
5605
5606 /* Determine whether to use old-style or new-style attribute names */
5607 if (context->internal->_full_time_names) {
5608 /* new-style names */
5609 attr_strings.create_time_attr = "system.dos_attr.CREATE_TIME";
5610 attr_strings.access_time_attr = "system.dos_attr.ACCESS_TIME";
5611 attr_strings.write_time_attr = "system.dos_attr.WRITE_TIME";
5612 attr_strings.change_time_attr = "system.dos_attr.CHANGE_TIME";
5613 } else {
5614 /* old-style names */
5615 attr_strings.create_time_attr = NULL;
5616 attr_strings.access_time_attr = "system.dos_attr.A_TIME";
5617 attr_strings.write_time_attr = "system.dos_attr.M_TIME";
5618 attr_strings.change_time_attr = "system.dos_attr.C_TIME";
5619 }
5620
5621 /* Are they requesting a supported attribute? */
5622 if (StrCaseCmp(name, "system.*") == 0 ||
5623 StrnCaseCmp(name, "system.*!", 9) == 0 ||
5624 StrCaseCmp(name, "system.*+") == 0 ||
5625 StrnCaseCmp(name, "system.*+!", 10) == 0 ||
5626 StrCaseCmp(name, "system.nt_sec_desc.*") == 0 ||
5627 StrnCaseCmp(name, "system.nt_sec_desc.*!", 21) == 0 ||
5628 StrCaseCmp(name, "system.nt_sec_desc.*+") == 0 ||
5629 StrnCaseCmp(name, "system.nt_sec_desc.*+!", 22) == 0 ||
5630 StrCaseCmp(name, "system.nt_sec_desc.revision") == 0 ||
5631 StrCaseCmp(name, "system.nt_sec_desc.owner") == 0 ||
5632 StrCaseCmp(name, "system.nt_sec_desc.owner+") == 0 ||
5633 StrCaseCmp(name, "system.nt_sec_desc.group") == 0 ||
5634 StrCaseCmp(name, "system.nt_sec_desc.group+") == 0 ||
5635 StrnCaseCmp(name, "system.nt_sec_desc.acl", 22) == 0 ||
5636 StrnCaseCmp(name, "system.nt_sec_desc.acl+", 23) == 0 ||
5637 StrCaseCmp(name, "system.dos_attr.*") == 0 ||
5638 StrnCaseCmp(name, "system.dos_attr.*!", 18) == 0 ||
5639 StrCaseCmp(name, "system.dos_attr.mode") == 0 ||
5640 StrCaseCmp(name, "system.dos_attr.size") == 0 ||
5641 (attr_strings.create_time_attr != NULL &&
5642 StrCaseCmp(name, attr_strings.create_time_attr) == 0) ||
5643 StrCaseCmp(name, attr_strings.access_time_attr) == 0 ||
5644 StrCaseCmp(name, attr_strings.write_time_attr) == 0 ||
5645 StrCaseCmp(name, attr_strings.change_time_attr) == 0 ||
5646 StrCaseCmp(name, "system.dos_attr.inode") == 0) {
5647
5648 /* Yup. */
5649 ret = cacl_get(context, ctx, srv,
5650 ipc_srv == NULL ? NULL : ipc_srv->cli,
5651 &pol, path,
5652 CONST_DISCARD(char *, name),
5653 CONST_DISCARD(char *, value), size);
5654 if (ret < 0 && errno == 0) {
5655 errno = smbc_errno(context, srv->cli);
5656 }
5657 talloc_destroy(ctx);
5658 return ret;
5659 }
5660
5661 /* Unsupported attribute name */
5662 talloc_destroy(ctx);
5663 errno = EINVAL;
5664 return -1;
5665}
5666
5667
5668static int
5669smbc_removexattr_ctx(SMBCCTX *context,
5670 const char *fname,
5671 const char *name)
5672{
5673 int ret;
5674 SMBCSRV *srv;
5675 SMBCSRV *ipc_srv;
5676 fstring server;
5677 fstring share;
5678 fstring user;
5679 fstring password;
5680 fstring workgroup;
5681 pstring path;
5682 TALLOC_CTX *ctx;
5683 POLICY_HND pol;
5684
5685 if (!context || !context->internal ||
5686 !context->internal->_initialized) {
5687
5688 errno = EINVAL; /* Best I can think of ... */
5689 return -1;
5690
5691 }
5692
5693 if (!fname) {
5694
5695 errno = EINVAL;
5696 return -1;
5697
5698 }
5699
5700 DEBUG(4, ("smbc_removexattr(%s, %s)\n", fname, name));
5701
5702 if (smbc_parse_path(context, fname,
5703 workgroup, sizeof(workgroup),
5704 server, sizeof(server),
5705 share, sizeof(share),
5706 path, sizeof(path),
5707 user, sizeof(user),
5708 password, sizeof(password),
5709 NULL, 0)) {
5710 errno = EINVAL;
5711 return -1;
5712 }
5713
5714 if (user[0] == (char)0) fstrcpy(user, context->user);
5715
5716 srv = smbc_server(context, True,
5717 server, share, workgroup, user, password);
5718 if (!srv) {
5719 return -1; /* errno set by smbc_server */
5720 }
5721
5722 if (! srv->no_nt_session) {
5723 ipc_srv = smbc_attr_server(context, server, share,
5724 workgroup, user, password,
5725 &pol);
5726 srv->no_nt_session = True;
5727 } else {
5728 ipc_srv = NULL;
5729 }
5730
5731 if (! ipc_srv) {
5732 return -1; /* errno set by smbc_attr_server */
5733 }
5734
5735 ctx = talloc_init("smbc_removexattr");
5736 if (!ctx) {
5737 errno = ENOMEM;
5738 return -1;
5739 }
5740
5741 /* Are they asking to set the entire ACL? */
5742 if (StrCaseCmp(name, "system.nt_sec_desc.*") == 0 ||
5743 StrCaseCmp(name, "system.nt_sec_desc.*+") == 0) {
5744
5745 /* Yup. */
5746 ret = cacl_set(ctx, srv->cli,
5747 ipc_srv->cli, &pol, path,
5748 NULL, SMBC_XATTR_MODE_REMOVE_ALL, 0);
5749 talloc_destroy(ctx);
5750 return ret;
5751 }
5752
5753 /*
5754 * Are they asking to remove one or more spceific security descriptor
5755 * attributes?
5756 */
5757 if (StrCaseCmp(name, "system.nt_sec_desc.revision") == 0 ||
5758 StrCaseCmp(name, "system.nt_sec_desc.owner") == 0 ||
5759 StrCaseCmp(name, "system.nt_sec_desc.owner+") == 0 ||
5760 StrCaseCmp(name, "system.nt_sec_desc.group") == 0 ||
5761 StrCaseCmp(name, "system.nt_sec_desc.group+") == 0 ||
5762 StrnCaseCmp(name, "system.nt_sec_desc.acl", 22) == 0 ||
5763 StrnCaseCmp(name, "system.nt_sec_desc.acl+", 23) == 0) {
5764
5765 /* Yup. */
5766 ret = cacl_set(ctx, srv->cli,
5767 ipc_srv->cli, &pol, path,
5768 name + 19, SMBC_XATTR_MODE_REMOVE, 0);
5769 talloc_destroy(ctx);
5770 return ret;
5771 }
5772
5773 /* Unsupported attribute name */
5774 talloc_destroy(ctx);
5775 errno = EINVAL;
5776 return -1;
5777}
5778
5779static int
5780smbc_listxattr_ctx(SMBCCTX *context,
5781 const char *fname,
5782 char *list,
5783 size_t size)
5784{
5785 /*
5786 * This isn't quite what listxattr() is supposed to do. This returns
5787 * the complete set of attribute names, always, rather than only those
5788 * attribute names which actually exist for a file. Hmmm...
5789 */
5790 const char supported_old[] =
5791 "system.*\0"
5792 "system.*+\0"
5793 "system.nt_sec_desc.revision\0"
5794 "system.nt_sec_desc.owner\0"
5795 "system.nt_sec_desc.owner+\0"
5796 "system.nt_sec_desc.group\0"
5797 "system.nt_sec_desc.group+\0"
5798 "system.nt_sec_desc.acl.*\0"
5799 "system.nt_sec_desc.acl\0"
5800 "system.nt_sec_desc.acl+\0"
5801 "system.nt_sec_desc.*\0"
5802 "system.nt_sec_desc.*+\0"
5803 "system.dos_attr.*\0"
5804 "system.dos_attr.mode\0"
5805 "system.dos_attr.c_time\0"
5806 "system.dos_attr.a_time\0"
5807 "system.dos_attr.m_time\0"
5808 ;
5809 const char supported_new[] =
5810 "system.*\0"
5811 "system.*+\0"
5812 "system.nt_sec_desc.revision\0"
5813 "system.nt_sec_desc.owner\0"
5814 "system.nt_sec_desc.owner+\0"
5815 "system.nt_sec_desc.group\0"
5816 "system.nt_sec_desc.group+\0"
5817 "system.nt_sec_desc.acl.*\0"
5818 "system.nt_sec_desc.acl\0"
5819 "system.nt_sec_desc.acl+\0"
5820 "system.nt_sec_desc.*\0"
5821 "system.nt_sec_desc.*+\0"
5822 "system.dos_attr.*\0"
5823 "system.dos_attr.mode\0"
5824 "system.dos_attr.create_time\0"
5825 "system.dos_attr.access_time\0"
5826 "system.dos_attr.write_time\0"
5827 "system.dos_attr.change_time\0"
5828 ;
5829 const char * supported;
5830
5831 if (context->internal->_full_time_names) {
5832 supported = supported_new;
5833 } else {
5834 supported = supported_old;
5835 }
5836
5837 if (size == 0) {
5838 return sizeof(supported);
5839 }
5840
5841 if (sizeof(supported) > size) {
5842 errno = ERANGE;
5843 return -1;
5844 }
5845
5846 /* this can't be strcpy() because there are embedded null characters */
5847 memcpy(list, supported, sizeof(supported));
5848 return sizeof(supported);
5849}
5850
5851
5852/*
5853 * Open a print file to be written to by other calls
5854 */
5855
5856static SMBCFILE *
5857smbc_open_print_job_ctx(SMBCCTX *context,
5858 const char *fname)
5859{
5860 fstring server;
5861 fstring share;
5862 fstring user;
5863 fstring password;
5864 pstring path;
5865
5866 if (!context || !context->internal ||
5867 !context->internal->_initialized) {
5868
5869 errno = EINVAL;
5870 return NULL;
5871
5872 }
5873
5874 if (!fname) {
5875
5876 errno = EINVAL;
5877 return NULL;
5878
5879 }
5880
5881 DEBUG(4, ("smbc_open_print_job_ctx(%s)\n", fname));
5882
5883 if (smbc_parse_path(context, fname,
5884 NULL, 0,
5885 server, sizeof(server),
5886 share, sizeof(share),
5887 path, sizeof(path),
5888 user, sizeof(user),
5889 password, sizeof(password),
5890 NULL, 0)) {
5891 errno = EINVAL;
5892 return NULL;
5893 }
5894
5895 /* What if the path is empty, or the file exists? */
5896
5897 return context->open(context, fname, O_WRONLY, 666);
5898
5899}
5900
5901/*
5902 * Routine to print a file on a remote server ...
5903 *
5904 * We open the file, which we assume to be on a remote server, and then
5905 * copy it to a print file on the share specified by printq.
5906 */
5907
5908static int
5909smbc_print_file_ctx(SMBCCTX *c_file,
5910 const char *fname,
5911 SMBCCTX *c_print,
5912 const char *printq)
5913{
5914 SMBCFILE *fid1;
5915 SMBCFILE *fid2;
5916 int bytes;
5917 int saverr;
5918 int tot_bytes = 0;
5919 char buf[4096];
5920
5921 if (!c_file || !c_file->internal->_initialized || !c_print ||
5922 !c_print->internal->_initialized) {
5923
5924 errno = EINVAL;
5925 return -1;
5926
5927 }
5928
5929 if (!fname && !printq) {
5930
5931 errno = EINVAL;
5932 return -1;
5933
5934 }
5935
5936 /* Try to open the file for reading ... */
5937
5938 if ((long)(fid1 = c_file->open(c_file, fname, O_RDONLY, 0666)) < 0) {
5939
5940 DEBUG(3, ("Error, fname=%s, errno=%i\n", fname, errno));
5941 return -1; /* smbc_open sets errno */
5942
5943 }
5944
5945 /* Now, try to open the printer file for writing */
5946
5947 if ((long)(fid2 = c_print->open_print_job(c_print, printq)) < 0) {
5948
5949 saverr = errno; /* Save errno */
5950 c_file->close_fn(c_file, fid1);
5951 errno = saverr;
5952 return -1;
5953
5954 }
5955
5956 while ((bytes = c_file->read(c_file, fid1, buf, sizeof(buf))) > 0) {
5957
5958 tot_bytes += bytes;
5959
5960 if ((c_print->write(c_print, fid2, buf, bytes)) < 0) {
5961
5962 saverr = errno;
5963 c_file->close_fn(c_file, fid1);
5964 c_print->close_fn(c_print, fid2);
5965 errno = saverr;
5966
5967 }
5968
5969 }
5970
5971 saverr = errno;
5972
5973 c_file->close_fn(c_file, fid1); /* We have to close these anyway */
5974 c_print->close_fn(c_print, fid2);
5975
5976 if (bytes < 0) {
5977
5978 errno = saverr;
5979 return -1;
5980
5981 }
5982
5983 return tot_bytes;
5984
5985}
5986
5987/*
5988 * Routine to list print jobs on a printer share ...
5989 */
5990
5991static int
5992smbc_list_print_jobs_ctx(SMBCCTX *context,
5993 const char *fname,
5994 smbc_list_print_job_fn fn)
5995{
5996 SMBCSRV *srv;
5997 fstring server;
5998 fstring share;
5999 fstring user;
6000 fstring password;
6001 fstring workgroup;
6002 pstring path;
6003
6004 if (!context || !context->internal ||
6005 !context->internal->_initialized) {
6006
6007 errno = EINVAL;
6008 return -1;
6009
6010 }
6011
6012 if (!fname) {
6013
6014 errno = EINVAL;
6015 return -1;
6016
6017 }
6018
6019 DEBUG(4, ("smbc_list_print_jobs(%s)\n", fname));
6020
6021 if (smbc_parse_path(context, fname,
6022 workgroup, sizeof(workgroup),
6023 server, sizeof(server),
6024 share, sizeof(share),
6025 path, sizeof(path),
6026 user, sizeof(user),
6027 password, sizeof(password),
6028 NULL, 0)) {
6029 errno = EINVAL;
6030 return -1;
6031 }
6032
6033 if (user[0] == (char)0) fstrcpy(user, context->user);
6034
6035 srv = smbc_server(context, True,
6036 server, share, workgroup, user, password);
6037
6038 if (!srv) {
6039
6040 return -1; /* errno set by smbc_server */
6041
6042 }
6043
6044 if (cli_print_queue(srv->cli,
6045 (void (*)(struct print_job_info *))fn) < 0) {
6046
6047 errno = smbc_errno(context, srv->cli);
6048 return -1;
6049
6050 }
6051
6052 return 0;
6053
6054}
6055
6056/*
6057 * Delete a print job from a remote printer share
6058 */
6059
6060static int
6061smbc_unlink_print_job_ctx(SMBCCTX *context,
6062 const char *fname,
6063 int id)
6064{
6065 SMBCSRV *srv;
6066 fstring server;
6067 fstring share;
6068 fstring user;
6069 fstring password;
6070 fstring workgroup;
6071 pstring path;
6072 int err;
6073
6074 if (!context || !context->internal ||
6075 !context->internal->_initialized) {
6076
6077 errno = EINVAL;
6078 return -1;
6079
6080 }
6081
6082 if (!fname) {
6083
6084 errno = EINVAL;
6085 return -1;
6086
6087 }
6088
6089 DEBUG(4, ("smbc_unlink_print_job(%s)\n", fname));
6090
6091 if (smbc_parse_path(context, fname,
6092 workgroup, sizeof(workgroup),
6093 server, sizeof(server),
6094 share, sizeof(share),
6095 path, sizeof(path),
6096 user, sizeof(user),
6097 password, sizeof(password),
6098 NULL, 0)) {
6099 errno = EINVAL;
6100 return -1;
6101 }
6102
6103 if (user[0] == (char)0) fstrcpy(user, context->user);
6104
6105 srv = smbc_server(context, True,
6106 server, share, workgroup, user, password);
6107
6108 if (!srv) {
6109
6110 return -1; /* errno set by smbc_server */
6111
6112 }
6113
6114 if ((err = cli_printjob_del(srv->cli, id)) != 0) {
6115
6116 if (err < 0)
6117 errno = smbc_errno(context, srv->cli);
6118 else if (err == ERRnosuchprintjob)
6119 errno = EINVAL;
6120 return -1;
6121
6122 }
6123
6124 return 0;
6125
6126}
6127
6128/*
6129 * Get a new empty handle to fill in with your own info
6130 */
6131SMBCCTX *
6132smbc_new_context(void)
6133{
6134 SMBCCTX *context;
6135
6136 context = SMB_MALLOC_P(SMBCCTX);
6137 if (!context) {
6138 errno = ENOMEM;
6139 return NULL;
6140 }
6141
6142 ZERO_STRUCTP(context);
6143
6144 context->internal = SMB_MALLOC_P(struct smbc_internal_data);
6145 if (!context->internal) {
6146 SAFE_FREE(context);
6147 errno = ENOMEM;
6148 return NULL;
6149 }
6150
6151 ZERO_STRUCTP(context->internal);
6152
6153
6154 /* ADD REASONABLE DEFAULTS */
6155 context->debug = 0;
6156 context->timeout = 20000; /* 20 seconds */
6157
6158 context->options.browse_max_lmb_count = 3; /* # LMBs to query */
6159 context->options.urlencode_readdir_entries = False;/* backward compat */
6160 context->options.one_share_per_server = False;/* backward compat */
6161 context->internal->_share_mode = SMBC_SHAREMODE_DENY_NONE;
6162 /* backward compat */
6163
6164 context->open = smbc_open_ctx;
6165 context->creat = smbc_creat_ctx;
6166 context->read = smbc_read_ctx;
6167 context->write = smbc_write_ctx;
6168 context->close_fn = smbc_close_ctx;
6169 context->unlink = smbc_unlink_ctx;
6170 context->rename = smbc_rename_ctx;
6171 context->lseek = smbc_lseek_ctx;
6172 context->stat = smbc_stat_ctx;
6173 context->fstat = smbc_fstat_ctx;
6174 context->opendir = smbc_opendir_ctx;
6175 context->closedir = smbc_closedir_ctx;
6176 context->readdir = smbc_readdir_ctx;
6177 context->getdents = smbc_getdents_ctx;
6178 context->mkdir = smbc_mkdir_ctx;
6179 context->rmdir = smbc_rmdir_ctx;
6180 context->telldir = smbc_telldir_ctx;
6181 context->lseekdir = smbc_lseekdir_ctx;
6182 context->fstatdir = smbc_fstatdir_ctx;
6183 context->chmod = smbc_chmod_ctx;
6184 context->utimes = smbc_utimes_ctx;
6185 context->setxattr = smbc_setxattr_ctx;
6186 context->getxattr = smbc_getxattr_ctx;
6187 context->removexattr = smbc_removexattr_ctx;
6188 context->listxattr = smbc_listxattr_ctx;
6189 context->open_print_job = smbc_open_print_job_ctx;
6190 context->print_file = smbc_print_file_ctx;
6191 context->list_print_jobs = smbc_list_print_jobs_ctx;
6192 context->unlink_print_job = smbc_unlink_print_job_ctx;
6193
6194 context->callbacks.check_server_fn = smbc_check_server;
6195 context->callbacks.remove_unused_server_fn = smbc_remove_unused_server;
6196
6197 smbc_default_cache_functions(context);
6198
6199 return context;
6200}
6201
6202/*
6203 * Free a context
6204 *
6205 * Returns 0 on success. Otherwise returns 1, the SMBCCTX is _not_ freed
6206 * and thus you'll be leaking memory if not handled properly.
6207 *
6208 */
6209int
6210smbc_free_context(SMBCCTX *context,
6211 int shutdown_ctx)
6212{
6213 if (!context) {
6214 errno = EBADF;
6215 return 1;
6216 }
6217
6218 if (shutdown_ctx) {
6219 SMBCFILE * f;
6220 DEBUG(1,("Performing aggressive shutdown.\n"));
6221
6222 f = context->internal->_files;
6223 while (f) {
6224 context->close_fn(context, f);
6225 f = f->next;
6226 }
6227 context->internal->_files = NULL;
6228
6229 /* First try to remove the servers the nice way. */
6230 if (context->callbacks.purge_cached_fn(context)) {
6231 SMBCSRV * s;
6232 SMBCSRV * next;
6233 DEBUG(1, ("Could not purge all servers, "
6234 "Nice way shutdown failed.\n"));
6235 s = context->internal->_servers;
6236 while (s) {
6237 DEBUG(1, ("Forced shutdown: %p (fd=%d)\n",
6238 s, s->cli->fd));
6239 cli_shutdown(s->cli);
6240 context->callbacks.remove_cached_srv_fn(context,
6241 s);
6242 next = s->next;
6243 DLIST_REMOVE(context->internal->_servers, s);
6244 SAFE_FREE(s);
6245 s = next;
6246 }
6247 context->internal->_servers = NULL;
6248 }
6249 }
6250 else {
6251 /* This is the polite way */
6252 if (context->callbacks.purge_cached_fn(context)) {
6253 DEBUG(1, ("Could not purge all servers, "
6254 "free_context failed.\n"));
6255 errno = EBUSY;
6256 return 1;
6257 }
6258 if (context->internal->_servers) {
6259 DEBUG(1, ("Active servers in context, "
6260 "free_context failed.\n"));
6261 errno = EBUSY;
6262 return 1;
6263 }
6264 if (context->internal->_files) {
6265 DEBUG(1, ("Active files in context, "
6266 "free_context failed.\n"));
6267 errno = EBUSY;
6268 return 1;
6269 }
6270 }
6271
6272 /* Things we have to clean up */
6273 SAFE_FREE(context->workgroup);
6274 SAFE_FREE(context->netbios_name);
6275 SAFE_FREE(context->user);
6276
6277 DEBUG(3, ("Context %p succesfully freed\n", context));
6278 SAFE_FREE(context->internal);
6279 SAFE_FREE(context);
6280 return 0;
6281}
6282
6283
6284/*
6285 * Each time the context structure is changed, we have binary backward
6286 * compatibility issues. Instead of modifying the public portions of the
6287 * context structure to add new options, instead, we put them in the internal
6288 * portion of the context structure and provide a set function for these new
6289 * options.
6290 */
6291void
6292smbc_option_set(SMBCCTX *context,
6293 char *option_name,
6294 ... /* option_value */)
6295{
6296 va_list ap;
6297 union {
6298 int i;
6299 BOOL b;
6300 smbc_get_auth_data_with_context_fn auth_fn;
6301 void *v;
6302 } option_value;
6303
6304 va_start(ap, option_name);
6305
6306 if (strcmp(option_name, "debug_to_stderr") == 0) {
6307 /*
6308 * Log to standard error instead of standard output.
6309 */
6310 option_value.b = (BOOL) va_arg(ap, int);
6311 context->internal->_debug_stderr = option_value.b;
6312
6313 } else if (strcmp(option_name, "full_time_names") == 0) {
6314 /*
6315 * Use new-style time attribute names, e.g. WRITE_TIME rather
6316 * than the old-style names such as M_TIME. This allows also
6317 * setting/getting CREATE_TIME which was previously
6318 * unimplemented. (Note that the old C_TIME was supposed to
6319 * be CHANGE_TIME but was confused and sometimes referred to
6320 * CREATE_TIME.)
6321 */
6322 option_value.b = (BOOL) va_arg(ap, int);
6323 context->internal->_full_time_names = option_value.b;
6324
6325 } else if (strcmp(option_name, "open_share_mode") == 0) {
6326 /*
6327 * The share mode to use for files opened with
6328 * smbc_open_ctx(). The default is SMBC_SHAREMODE_DENY_NONE.
6329 */
6330 option_value.i = va_arg(ap, int);
6331 context->internal->_share_mode =
6332 (smbc_share_mode) option_value.i;
6333
6334 } else if (strcmp(option_name, "auth_function") == 0) {
6335 /*
6336 * Use the new-style authentication function which includes
6337 * the context.
6338 */
6339 option_value.auth_fn =
6340 va_arg(ap, smbc_get_auth_data_with_context_fn);
6341 context->internal->_auth_fn_with_context =
6342 option_value.auth_fn;
6343 } else if (strcmp(option_name, "user_data") == 0) {
6344 /*
6345 * Save a user data handle which may be retrieved by the user
6346 * with smbc_option_get()
6347 */
6348 option_value.v = va_arg(ap, void *);
6349 context->internal->_user_data = option_value.v;
6350 }
6351
6352 va_end(ap);
6353}
6354
6355
6356/*
6357 * Retrieve the current value of an option
6358 */
6359void *
6360smbc_option_get(SMBCCTX *context,
6361 char *option_name)
6362{
6363 if (strcmp(option_name, "debug_stderr") == 0) {
6364 /*
6365 * Log to standard error instead of standard output.
6366 */
6367#if defined(__intptr_t_defined) || defined(HAVE_INTPTR_T)
6368 return (void *) (intptr_t) context->internal->_debug_stderr;
6369#else
6370 return (void *) context->internal->_debug_stderr;
6371#endif
6372 } else if (strcmp(option_name, "full_time_names") == 0) {
6373 /*
6374 * Use new-style time attribute names, e.g. WRITE_TIME rather
6375 * than the old-style names such as M_TIME. This allows also
6376 * setting/getting CREATE_TIME which was previously
6377 * unimplemented. (Note that the old C_TIME was supposed to
6378 * be CHANGE_TIME but was confused and sometimes referred to
6379 * CREATE_TIME.)
6380 */
6381#if defined(__intptr_t_defined) || defined(HAVE_INTPTR_T)
6382 return (void *) (intptr_t) context->internal->_full_time_names;
6383#else
6384 return (void *) context->internal->_full_time_names;
6385#endif
6386
6387 } else if (strcmp(option_name, "auth_function") == 0) {
6388 /*
6389 * Use the new-style authentication function which includes
6390 * the context.
6391 */
6392 return (void *) context->internal->_auth_fn_with_context;
6393 } else if (strcmp(option_name, "user_data") == 0) {
6394 /*
6395 * Save a user data handle which may be retrieved by the user
6396 * with smbc_option_get()
6397 */
6398 return context->internal->_user_data;
6399 }
6400
6401 return NULL;
6402}
6403
6404
6405/*
6406 * Initialise the library etc
6407 *
6408 * We accept a struct containing handle information.
6409 * valid values for info->debug from 0 to 100,
6410 * and insist that info->fn must be non-null.
6411 */
6412SMBCCTX *
6413smbc_init_context(SMBCCTX *context)
6414{
6415 pstring conf;
6416 int pid;
6417 char *user = NULL;
6418 char *home = NULL;
6419
6420 if (!context || !context->internal) {
6421 errno = EBADF;
6422 return NULL;
6423 }
6424
6425 /* Do not initialise the same client twice */
6426 if (context->internal->_initialized) {
6427 return 0;
6428 }
6429
6430 if ((!context->callbacks.auth_fn &&
6431 !context->internal->_auth_fn_with_context) ||
6432 context->debug < 0 ||
6433 context->debug > 100) {
6434
6435 errno = EINVAL;
6436 return NULL;
6437
6438 }
6439
6440 if (!smbc_initialized) {
6441 /*
6442 * Do some library-wide intializations the first time we get
6443 * called
6444 */
6445 BOOL conf_loaded = False;
6446
6447 /* Set this to what the user wants */
6448 DEBUGLEVEL = context->debug;
6449
6450 load_case_tables();
6451
6452 setup_logging("libsmbclient", True);
6453 if (context->internal->_debug_stderr) {
6454 dbf = x_stderr;
6455 x_setbuf(x_stderr, NULL);
6456 }
6457
6458 /* Here we would open the smb.conf file if needed ... */
6459
6460 in_client = True; /* FIXME, make a param */
6461
6462 home = getenv("HOME");
6463 if (home) {
6464 slprintf(conf, sizeof(conf), "%s/.smb/smb.conf", home);
6465 if (lp_load(conf, True, False, False, True)) {
6466 conf_loaded = True;
6467 } else {
6468 DEBUG(5, ("Could not load config file: %s\n",
6469 conf));
6470 }
6471 }
6472
6473 if (!conf_loaded) {
6474 /*
6475 * Well, if that failed, try the dyn_CONFIGFILE
6476 * Which points to the standard locn, and if that
6477 * fails, silently ignore it and use the internal
6478 * defaults ...
6479 */
6480
6481 if (!lp_load(dyn_CONFIGFILE, True, False, False, False)) {
6482 DEBUG(5, ("Could not load config file: %s\n",
6483 dyn_CONFIGFILE));
6484 } else if (home) {
6485 /*
6486 * We loaded the global config file. Now lets
6487 * load user-specific modifications to the
6488 * global config.
6489 */
6490 slprintf(conf, sizeof(conf),
6491 "%s/.smb/smb.conf.append", home);
6492 if (!lp_load(conf, True, False, False, False)) {
6493 DEBUG(10,
6494 ("Could not append config file: "
6495 "%s\n",
6496 conf));
6497 }
6498 }
6499 }
6500
6501 load_interfaces(); /* Load the list of interfaces ... */
6502
6503 reopen_logs(); /* Get logging working ... */
6504
6505 /*
6506 * Block SIGPIPE (from lib/util_sock.c: write())
6507 * It is not needed and should not stop execution
6508 */
6509 BlockSignals(True, SIGPIPE);
6510
6511 /* Done with one-time initialisation */
6512 smbc_initialized = 1;
6513
6514 }
6515
6516 if (!context->user) {
6517 /*
6518 * FIXME: Is this the best way to get the user info?
6519 */
6520 user = getenv("USER");
6521 /* walk around as "guest" if no username can be found */
6522 if (!user) context->user = SMB_STRDUP("guest");
6523 else context->user = SMB_STRDUP(user);
6524 }
6525
6526 if (!context->netbios_name) {
6527 /*
6528 * We try to get our netbios name from the config. If that
6529 * fails we fall back on constructing our netbios name from
6530 * our hostname etc
6531 */
6532 if (global_myname()) {
6533 context->netbios_name = SMB_STRDUP(global_myname());
6534 }
6535 else {
6536 /*
6537 * Hmmm, I want to get hostname as well, but I am too
6538 * lazy for the moment
6539 */
6540 pid = sys_getpid();
6541 context->netbios_name = (char *)SMB_MALLOC(17);
6542 if (!context->netbios_name) {
6543 errno = ENOMEM;
6544 return NULL;
6545 }
6546 slprintf(context->netbios_name, 16,
6547 "smbc%s%d", context->user, pid);
6548 }
6549 }
6550
6551 DEBUG(1, ("Using netbios name %s.\n", context->netbios_name));
6552
6553 if (!context->workgroup) {
6554 if (lp_workgroup()) {
6555 context->workgroup = SMB_STRDUP(lp_workgroup());
6556 }
6557 else {
6558 /* TODO: Think about a decent default workgroup */
6559 context->workgroup = SMB_STRDUP("samba");
6560 }
6561 }
6562
6563 DEBUG(1, ("Using workgroup %s.\n", context->workgroup));
6564
6565 /* shortest timeout is 1 second */
6566 if (context->timeout > 0 && context->timeout < 1000)
6567 context->timeout = 1000;
6568
6569 /*
6570 * FIXME: Should we check the function pointers here?
6571 */
6572
6573 context->internal->_initialized = True;
6574
6575 return context;
6576}
6577
6578
6579/* Return the verion of samba, and thus libsmbclient */
6580const char *
6581smbc_version(void)
6582{
6583 return samba_version_string();
6584}
Note: See TracBrowser for help on using the repository browser.