source: branches/samba-3.0/source/libsmb/libsmbclient.c@ 175

Last change on this file since 175 was 165, checked in by Paul Smedley, 16 years ago

Add 'missing' 3.0.34 diffs

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