source: trunk/samba-3.0.25pre1/source/libsmb/libsmbclient.c@ 5

Last change on this file since 5 was 1, checked in by Paul Smedley, 18 years ago

Initial code import

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