2
Unix SMB/Netbios implementation.
3
SMB client library implementation
4
Copyright (C) Andrew Tridgell 1998
5
Copyright (C) Richard Sharpe 2000, 2002
6
Copyright (C) John Terpstra 2000
7
Copyright (C) Tom Jansen (Ninja ISD) 2002
8
Copyright (C) Derrell Lipman 2003, 2004
10
This program is free software; you can redistribute it and/or modify
11
it under the terms of the GNU General Public License as published by
12
the Free Software Foundation; either version 2 of the License, or
13
(at your option) any later version.
15
This program is distributed in the hope that it will be useful,
16
but WITHOUT ANY WARRANTY; without even the implied warranty of
17
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18
GNU General Public License for more details.
20
You should have received a copy of the GNU General Public License
21
along with this program; if not, write to the Free Software
22
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
27
#include "include/libsmb_internal.h"
31
* DOS Attribute values (used internally)
33
typedef struct DOS_ATTR_DESC {
44
* Internal flags for extended attributes
47
/* internal mode values */
48
#define SMBC_XATTR_MODE_ADD 1
49
#define SMBC_XATTR_MODE_REMOVE 2
50
#define SMBC_XATTR_MODE_REMOVE_ALL 3
51
#define SMBC_XATTR_MODE_SET 4
52
#define SMBC_XATTR_MODE_CHOWN 5
53
#define SMBC_XATTR_MODE_CHGRP 6
55
#define CREATE_ACCESS_READ READ_CONTROL_ACCESS
57
/*We should test for this in configure ... */
59
#define ENOTSUP EOPNOTSUPP
63
* Functions exported by libsmb_cache.c that we need here
65
int smbc_default_cache_functions(SMBCCTX *context);
68
* check if an element is part of the list.
69
* FIXME: Does not belong here !
70
* Can anyone put this in a macro in dlinklist.h ?
73
static int DLIST_CONTAINS(SMBCFILE * list, SMBCFILE *p) {
74
if (!p || !list) return False;
76
if (p == list) return True;
83
* Find an lsa pipe handle associated with a cli struct.
85
static struct rpc_pipe_client *
86
find_lsa_pipe_hnd(struct cli_state *ipc_cli)
88
struct rpc_pipe_client *pipe_hnd;
90
for (pipe_hnd = ipc_cli->pipe_list;
92
pipe_hnd = pipe_hnd->next) {
94
if (pipe_hnd->pipe_idx == PI_LSARPC) {
103
smbc_close_ctx(SMBCCTX *context,
106
smbc_lseek_ctx(SMBCCTX *context,
111
extern BOOL in_client;
114
* Is the logging working / configfile read ?
116
static int smbc_initialized = 0;
119
hex2int( unsigned int _char )
121
if ( _char >= 'A' && _char <='F')
122
return _char - 'A' + 10;
123
if ( _char >= 'a' && _char <='f')
124
return _char - 'a' + 10;
125
if ( _char >= '0' && _char <='9')
133
* Convert strings of %xx to their single character equivalent. Each 'x' must
134
* be a valid hexadecimal digit, or that % sequence is left undecoded.
136
* dest may, but need not be, the same pointer as src.
138
* Returns the number of % sequences which could not be converted due to lack
139
* of two following hexadecimal digits.
142
smbc_urldecode(char *dest, char * src, size_t max_dest_len)
144
int old_length = strlen(src);
150
if ( old_length == 0 ) {
155
while ( i < old_length ) {
156
unsigned char character = src[ i++ ];
158
if (character == '%') {
159
int a = i+1 < old_length ? hex2int( src[i] ) : -1;
160
int b = i+1 < old_length ? hex2int( src[i+1] ) : -1;
162
/* Replace valid sequence */
163
if (a != -1 && b != -1) {
165
/* Replace valid %xx sequence with %dd */
166
character = (a * 16) + b;
168
if (character == '\0') {
169
break; /* Stop at %00 */
184
strncpy(dest, temp, max_dest_len - 1);
185
dest[max_dest_len - 1] = '\0';
193
* Convert any characters not specifically allowed in a URL into their %xx
196
* Returns the remaining buffer length.
199
smbc_urlencode(char * dest, char * src, int max_dest_len)
201
char hex[] = "0123456789ABCDEF";
203
for (; *src != '\0' && max_dest_len >= 3; src++) {
215
*dest++ = hex[(*src >> 4) & 0x0f];
216
*dest++ = hex[*src & 0x0f];
231
* Function to parse a path and turn it into components
233
* The general format of an SMB URI is explain in Christopher Hertel's CIFS
234
* book, at http://ubiqx.org/cifs/Appendix-D.html. We accept a subset of the
235
* general format ("smb:" only; we do not look for "cifs:").
239
* smb://[[[domain;]user[:password]@]server[/share[/path[/file]]]][?options]
243
* smb:// Show all workgroups.
245
* The method of locating the list of workgroups varies
246
* depending upon the setting of the context variable
247
* context->options.browse_max_lmb_count. This value
248
* determine the maximum number of local master browsers to
249
* query for the list of workgroups. In order to ensure that
250
* a complete list of workgroups is obtained, all master
251
* browsers must be queried, but if there are many
252
* workgroups, the time spent querying can begin to add up.
253
* For small networks (not many workgroups), it is suggested
254
* that this variable be set to 0, indicating query all local
255
* master browsers. When the network has many workgroups, a
256
* reasonable setting for this variable might be around 3.
258
* smb://name/ if name<1D> or name<1B> exists, list servers in
259
* workgroup, else, if name<20> exists, list all shares
262
* If "options" are provided, this function returns the entire option list as a
263
* string, for later parsing by the caller. Note that currently, no options
267
static const char *smbc_prefix = "smb:";
270
smbc_parse_path(SMBCCTX *context,
272
char *workgroup, int workgroup_len,
273
char *server, int server_len,
274
char *share, int share_len,
275
char *path, int path_len,
276
char *user, int user_len,
277
char *password, int password_len,
278
char *options, int options_len)
286
server[0] = share[0] = path[0] = user[0] = password[0] = (char)0;
289
* Assume we wont find an authentication domain to parse, so default
290
* to the workgroup in the provided context.
292
if (workgroup != NULL) {
293
strncpy(workgroup, context->workgroup, workgroup_len - 1);
294
workgroup[workgroup_len - 1] = '\0';
297
if (options != NULL && options_len > 0) {
298
options[0] = (char)0;
302
/* see if it has the right prefix */
303
len = strlen(smbc_prefix);
304
if (strncmp(s,smbc_prefix,len) || (s[len] != '/' && s[len] != 0)) {
305
return -1; /* What about no smb: ? */
310
/* Watch the test below, we are testing to see if we should exit */
312
if (strncmp(p, "//", 2) && strncmp(p, "\\\\", 2)) {
314
DEBUG(1, ("Invalid path (does not begin with smb://"));
319
p += 2; /* Skip the double slash */
321
/* See if any options were specified */
322
if ((q = strrchr(p, '?')) != NULL ) {
323
/* There are options. Null terminate here and point to them */
326
DEBUG(4, ("Found options '%s'", q));
328
/* Copy the options */
329
if (options != NULL && options_len > 0) {
330
safe_strcpy(options, q, options_len - 1);
339
strncpy(server, context->workgroup,
340
((strlen(context->workgroup) < 16)
341
? strlen(context->workgroup)
343
server[server_len - 1] = '\0';
349
* ok, its for us. Now parse out the server, share etc.
351
* However, we want to parse out [[domain;]user[:password]@] if it
355
/* check that '@' occurs before '/', if '/' exists at all */
356
q = strchr_m(p, '@');
357
r = strchr_m(p, '/');
358
if (q && (!r || q < r)) {
359
pstring username, passwd, domain;
360
const char *u = userinfo;
362
next_token_no_ltrim(&p, userinfo, "@", sizeof(fstring));
364
username[0] = passwd[0] = domain[0] = 0;
366
if (strchr_m(u, ';')) {
368
next_token_no_ltrim(&u, domain, ";", sizeof(fstring));
372
if (strchr_m(u, ':')) {
374
next_token_no_ltrim(&u, username, ":", sizeof(fstring));
381
pstrcpy(username, u);
385
if (domain[0] && workgroup) {
386
strncpy(workgroup, domain, workgroup_len - 1);
387
workgroup[workgroup_len - 1] = '\0';
391
strncpy(user, username, user_len - 1);
392
user[user_len - 1] = '\0';
396
strncpy(password, passwd, password_len - 1);
397
password[password_len - 1] = '\0';
402
if (!next_token(&p, server, "/", sizeof(fstring))) {
408
if (*p == (char)0) goto decoding; /* That's it ... */
410
if (!next_token(&p, share, "/", sizeof(fstring))) {
416
safe_strcpy(path, p, path_len - 1);
418
all_string_sub(path, "/", "\\", 0);
421
(void) smbc_urldecode(path, path, path_len);
422
(void) smbc_urldecode(server, server, server_len);
423
(void) smbc_urldecode(share, share, share_len);
424
(void) smbc_urldecode(user, user, user_len);
425
(void) smbc_urldecode(password, password, password_len);
431
* Verify that the options specified in a URL are valid
434
smbc_check_options(char *server,
439
DEBUG(4, ("smbc_check_options(): server='%s' share='%s' "
440
"path='%s' options='%s'\n",
441
server, share, path, options));
443
/* No options at all is always ok */
444
if (! *options) return 0;
446
/* Currently, we don't support any options. */
451
* Convert an SMB error into a UNIX error ...
454
smbc_errno(SMBCCTX *context,
457
int ret = cli_errno(c);
459
if (cli_is_dos_error(c)) {
463
cli_dos_error(c, &eclass, &ecode);
465
DEBUG(3,("smbc_error %d %d (0x%x) -> %d\n",
466
(int)eclass, (int)ecode, (int)ecode, ret));
470
status = cli_nt_error(c);
472
DEBUG(3,("smbc errno %s -> %d\n",
473
nt_errstr(status), ret));
480
* Check a server for being alive and well.
481
* returns 0 if the server is in shape. Returns 1 on error
483
* Also useable outside libsmbclient to enable external cache
484
* to do some checks too.
487
smbc_check_server(SMBCCTX * context,
490
if ( send_keepalive(server->cli->fd) == False )
493
/* connection is ok */
498
* Remove a server from the cached server list it's unused.
499
* On success, 0 is returned. 1 is returned if the server could not be removed.
501
* Also useable outside libsmbclient
504
smbc_remove_unused_server(SMBCCTX * context,
509
/* are we being fooled ? */
510
if (!context || !context->internal ||
511
!context->internal->_initialized || !srv) return 1;
514
/* Check all open files/directories for a relation with this server */
515
for (file = context->internal->_files; file; file=file->next) {
516
if (file->srv == srv) {
518
DEBUG(3, ("smbc_remove_usused_server: "
519
"%p still used by %p.\n",
525
DLIST_REMOVE(context->internal->_servers, srv);
527
cli_shutdown(srv->cli);
530
DEBUG(3, ("smbc_remove_usused_server: %p removed.\n", srv));
532
context->callbacks.remove_cached_srv_fn(context, srv);
540
find_server(SMBCCTX *context,
552
srv = context->callbacks.get_cached_srv_fn(context, server, share,
553
workgroup, username);
555
if (!auth_called && !srv && (!username[0] || !password[0])) {
556
if (context->internal->_auth_fn_with_context != NULL) {
557
context->internal->_auth_fn_with_context(
560
workgroup, sizeof(fstring),
561
username, sizeof(fstring),
562
password, sizeof(fstring));
564
context->callbacks.auth_fn(
566
workgroup, sizeof(fstring),
567
username, sizeof(fstring),
568
password, sizeof(fstring));
572
* However, smbc_auth_fn may have picked up info relating to
573
* an existing connection, so try for an existing connection
577
goto check_server_cache;
582
if (context->callbacks.check_server_fn(context, srv)) {
584
* This server is no good anymore
585
* Try to remove it and check for more possible
586
* servers in the cache
588
if (context->callbacks.remove_unused_server_fn(context,
591
* We could not remove the server completely,
592
* remove it from the cache so we will not get
593
* it again. It will be removed when the last
594
* file/dir is closed.
596
context->callbacks.remove_cached_srv_fn(context,
601
* Maybe there are more cached connections to this
604
goto check_server_cache;
614
* Connect to a server, possibly on an existing connection
616
* Here, what we want to do is: If the server and username
617
* match an existing connection, reuse that, otherwise, establish a
620
* If we have to create a new connection, call the auth_fn to get the
621
* info we need, unless the username and password were passed in.
625
smbc_server(SMBCCTX *context,
626
BOOL connect_if_not_found,
635
struct nmb_name called, calling;
636
const char *server_n = server;
639
int tried_reverse = 0;
642
const char *username_used;
647
if (server[0] == 0) {
652
/* Look for a cached connection */
653
srv = find_server(context, server, share,
654
workgroup, username, password);
657
* If we found a connection and we're only allowed one share per
660
if (srv && *share != '\0' && context->options.one_share_per_server) {
663
* ... then if there's no current connection to the share,
664
* connect to it. find_server(), or rather the function
665
* pointed to by context->callbacks.get_cached_srv_fn which
666
* was called by find_server(), will have issued a tree
667
* disconnect if the requested share is not the same as the
668
* one that was already connected.
670
if (srv->cli->cnum == (uint16) -1) {
671
/* Ensure we have accurate auth info */
672
if (context->internal->_auth_fn_with_context != NULL) {
673
context->internal->_auth_fn_with_context(
676
workgroup, sizeof(fstring),
677
username, sizeof(fstring),
678
password, sizeof(fstring));
680
context->callbacks.auth_fn(
682
workgroup, sizeof(fstring),
683
username, sizeof(fstring),
684
password, sizeof(fstring));
687
if (! cli_send_tconX(srv->cli, share, "?????",
688
password, strlen(password)+1)) {
690
errno = smbc_errno(context, srv->cli);
691
cli_shutdown(srv->cli);
693
context->callbacks.remove_cached_srv_fn(context,
699
* Regenerate the dev value since it's based on both
703
srv->dev = (dev_t)(str_checksum(server) ^
704
str_checksum(share));
709
/* If we have a connection... */
712
/* ... then we're done here. Give 'em what they came for. */
716
/* If we're not asked to connect when a connection doesn't exist... */
717
if (! connect_if_not_found) {
718
/* ... then we're done here. */
722
make_nmb_name(&calling, context->netbios_name, 0x0);
723
make_nmb_name(&called , server, 0x20);
725
DEBUG(4,("smbc_server: server_n=[%s] server=[%s]\n", server_n, server));
727
DEBUG(4,(" -> server_n=[%s] server=[%s]\n", server_n, server));
730
slprintf(ipenv,sizeof(ipenv)-1,"HOST_%s", server_n);
734
/* have to open a new connection */
735
if ((c = cli_initialise()) == NULL) {
740
if (context->flags & SMB_CTX_FLAG_USE_KERBEROS) {
741
c->use_kerberos = True;
743
if (context->flags & SMB_CTX_FLAG_FALLBACK_AFTER_KERBEROS) {
744
c->fallback_after_kerberos = True;
747
c->timeout = context->timeout;
750
* Force use of port 139 for first try if share is $IPC, empty, or
751
* null, so browse lists can work
753
if (share == NULL || *share == '\0' || strcmp(share, "IPC$") == 0) {
754
port_try_first = 139;
757
port_try_first = 445;
761
c->port = port_try_first;
763
if (!cli_connect(c, server_n, &ip)) {
765
/* First connection attempt failed. Try alternate port. */
766
c->port = port_try_next;
768
if (!cli_connect(c, server_n, &ip)) {
775
if (!cli_session_request(c, &calling, &called)) {
777
if (strcmp(called.name, "*SMBSERVER")) {
778
make_nmb_name(&called , "*SMBSERVER", 0x20);
780
} else { /* Try one more time, but ensure we don't loop */
782
/* Only try this if server is an IP address ... */
784
if (is_ipaddress(server) && !tried_reverse) {
786
struct in_addr rem_ip;
788
if ((rem_ip.s_addr=inet_addr(server)) == INADDR_NONE) {
789
DEBUG(4, ("Could not convert IP address "
790
"%s to struct in_addr\n", server));
795
tried_reverse++; /* Yuck */
797
if (name_status_find("*", 0, 0, rem_ip, remote_name)) {
798
make_nmb_name(&called, remote_name, 0x20);
807
DEBUG(4,(" session request ok\n"));
809
if (!cli_negprot(c)) {
815
username_used = username;
817
if (!cli_session_setup(c, username_used,
818
password, strlen(password),
819
password, strlen(password),
822
/* Failed. Try an anonymous login, if allowed by flags. */
825
if ((context->flags & SMBCCTX_FLAG_NO_AUTO_ANONYMOUS_LOGON) ||
826
!cli_session_setup(c, username_used,
837
DEBUG(4,(" session setup ok\n"));
839
if (!cli_send_tconX(c, share, "?????",
840
password, strlen(password)+1)) {
841
errno = smbc_errno(context, c);
846
DEBUG(4,(" tconx ok\n"));
849
* Ok, we have got a nice connection
850
* Let's allocate a server structure.
853
srv = SMB_MALLOC_P(SMBCSRV);
861
srv->dev = (dev_t)(str_checksum(server) ^ str_checksum(share));
862
srv->no_pathinfo = False;
863
srv->no_pathinfo2 = False;
864
srv->no_nt_session = False;
866
/* now add it to the cache (internal or external) */
867
/* Let the cache function set errno if it wants to */
869
if (context->callbacks.add_cached_srv_fn(context, srv, server, share, workgroup, username)) {
870
int saved_errno = errno;
871
DEBUG(3, (" Failed to add server to cache\n"));
879
DEBUG(2, ("Server connect ok: //%s/%s: %p\n",
880
server, share, srv));
882
DLIST_ADD(context->internal->_servers, srv);
896
* Connect to a server for getting/setting attributes, possibly on an existing
897
* connection. This works similarly to smbc_server().
900
smbc_attr_server(SMBCCTX *context,
909
struct cli_state *ipc_cli;
910
struct rpc_pipe_client *pipe_hnd;
912
SMBCSRV *ipc_srv=NULL;
915
* See if we've already created this special connection. Reference
916
* our "special" share name '*IPC$', which is an impossible real share
917
* name due to the leading asterisk.
919
ipc_srv = find_server(context, server, "*IPC$",
920
workgroup, username, password);
923
/* We didn't find a cached connection. Get the password */
924
if (*password == '\0') {
925
/* ... then retrieve it now. */
926
if (context->internal->_auth_fn_with_context != NULL) {
927
context->internal->_auth_fn_with_context(
930
workgroup, sizeof(fstring),
931
username, sizeof(fstring),
932
password, sizeof(fstring));
934
context->callbacks.auth_fn(
936
workgroup, sizeof(fstring),
937
username, sizeof(fstring),
938
password, sizeof(fstring));
943
nt_status = cli_full_connection(&ipc_cli,
944
global_myname(), server,
945
&ip, 0, "IPC$", "?????",
949
if (! NT_STATUS_IS_OK(nt_status)) {
950
DEBUG(1,("cli_full_connection failed! (%s)\n",
951
nt_errstr(nt_status)));
956
ipc_srv = SMB_MALLOC_P(SMBCSRV);
959
cli_shutdown(ipc_cli);
963
ZERO_STRUCTP(ipc_srv);
964
ipc_srv->cli = ipc_cli;
967
pipe_hnd = cli_rpc_pipe_open_noauth(ipc_srv->cli,
971
DEBUG(1, ("cli_nt_session_open fail!\n"));
973
cli_shutdown(ipc_srv->cli);
979
* Some systems don't support
980
* SEC_RIGHTS_MAXIMUM_ALLOWED, but NT sends 0x2000000
981
* so we might as well do it too.
984
nt_status = rpccli_lsa_open_policy(
986
ipc_srv->cli->mem_ctx,
988
GENERIC_EXECUTE_ACCESS,
991
if (!NT_STATUS_IS_OK(nt_status)) {
992
errno = smbc_errno(context, ipc_srv->cli);
993
cli_shutdown(ipc_srv->cli);
998
/* now add it to the cache (internal or external) */
1000
errno = 0; /* let cache function set errno if it likes */
1001
if (context->callbacks.add_cached_srv_fn(context, ipc_srv,
1006
DEBUG(3, (" Failed to add server to cache\n"));
1010
cli_shutdown(ipc_srv->cli);
1015
DLIST_ADD(context->internal->_servers, ipc_srv);
1022
* Routine to open() a file ...
1026
smbc_open_ctx(SMBCCTX *context,
1031
fstring server, share, user, password, workgroup;
1034
struct cli_state *targetcli;
1035
SMBCSRV *srv = NULL;
1036
SMBCFILE *file = NULL;
1039
if (!context || !context->internal ||
1040
!context->internal->_initialized) {
1042
errno = EINVAL; /* Best I can think of ... */
1054
if (smbc_parse_path(context, fname,
1055
workgroup, sizeof(workgroup),
1056
server, sizeof(server),
1057
share, sizeof(share),
1060
password, sizeof(password),
1066
if (user[0] == (char)0) fstrcpy(user, context->user);
1068
srv = smbc_server(context, True,
1069
server, share, workgroup, user, password);
1073
if (errno == EPERM) errno = EACCES;
1074
return NULL; /* smbc_server sets errno */
1078
/* Hmmm, the test for a directory is suspect here ... FIXME */
1080
if (strlen(path) > 0 && path[strlen(path) - 1] == '\\') {
1087
file = SMB_MALLOC_P(SMBCFILE);
1098
/*d_printf(">>>open: resolving %s\n", path);*/
1099
if (!cli_resolve_path( "", srv->cli, path, &targetcli, targetpath))
1101
d_printf("Could not resolve %s\n", path);
1105
/*d_printf(">>>open: resolved %s as %s\n", path, targetpath);*/
1107
if ( targetcli->dfsroot )
1110
pstrcpy(temppath, targetpath);
1111
cli_dfs_make_full_path( targetpath, targetcli->desthost, targetcli->share, temppath);
1114
if ((fd = cli_open(targetcli, targetpath, flags, DENY_NONE)) < 0) {
1116
/* Handle the error ... */
1119
errno = smbc_errno(context, targetcli);
1124
/* Fill in file struct */
1127
file->fname = SMB_STRDUP(fname);
1132
DLIST_ADD(context->internal->_files, file);
1135
* If the file was opened in O_APPEND mode, all write
1136
* operations should be appended to the file. To do that,
1137
* though, using this protocol, would require a getattrE()
1138
* call for each and every write, to determine where the end
1139
* of the file is. (There does not appear to be an append flag
1140
* in the protocol.) Rather than add all of that overhead of
1141
* retrieving the current end-of-file offset prior to each
1142
* write operation, we'll assume that most append operations
1143
* will continuously write, so we'll just set the offset to
1144
* the end of the file now and hope that's adequate.
1146
* Note to self: If this proves inadequate, and O_APPEND
1147
* should, in some cases, be forced for each write, add a
1148
* field in the context options structure, for
1149
* "strict_append_mode" which would select between the current
1150
* behavior (if FALSE) or issuing a getattrE() prior to each
1151
* write and forcing the write to the end of the file (if
1152
* TRUE). Adding that capability will likely require adding
1153
* an "append" flag into the _SMBCFILE structure to track
1154
* whether a file was opened in O_APPEND mode. -- djl
1156
if (flags & O_APPEND) {
1157
if (smbc_lseek_ctx(context, file, 0, SEEK_END) < 0) {
1158
(void) smbc_close_ctx(context, file);
1168
/* Check if opendir needed ... */
1173
eno = smbc_errno(context, srv->cli);
1174
file = context->opendir(context, fname);
1175
if (!file) errno = eno;
1180
errno = EINVAL; /* FIXME, correct errno ? */
1186
* Routine to create a file
1189
static int creat_bits = O_WRONLY | O_CREAT | O_TRUNC; /* FIXME: Do we need this */
1192
smbc_creat_ctx(SMBCCTX *context,
1197
if (!context || !context->internal ||
1198
!context->internal->_initialized) {
1205
return smbc_open_ctx(context, path, creat_bits, mode);
1209
* Routine to read() a file ...
1213
smbc_read_ctx(SMBCCTX *context,
1219
fstring server, share, user, password;
1220
pstring path, targetpath;
1221
struct cli_state *targetcli;
1226
* Compiler bug (possibly) -- gcc (GCC) 3.3.5 (Debian 1:3.3.5-2) --
1227
* appears to pass file->offset (which is type off_t) differently than
1228
* a local variable of type off_t. Using local variable "offset" in
1229
* the call to cli_read() instead of file->offset fixes a problem
1230
* retrieving data at an offset greater than 4GB.
1234
if (!context || !context->internal ||
1235
!context->internal->_initialized) {
1242
DEBUG(4, ("smbc_read(%p, %d)\n", file, (int)count));
1244
if (!file || !DLIST_CONTAINS(context->internal->_files, file)) {
1251
offset = file->offset;
1253
/* Check that the buffer exists ... */
1262
/*d_printf(">>>read: parsing %s\n", file->fname);*/
1263
if (smbc_parse_path(context, file->fname,
1265
server, sizeof(server),
1266
share, sizeof(share),
1269
password, sizeof(password),
1275
/*d_printf(">>>read: resolving %s\n", path);*/
1276
if (!cli_resolve_path("", file->srv->cli, path,
1277
&targetcli, targetpath))
1279
d_printf("Could not resolve %s\n", path);
1282
/*d_printf(">>>fstat: resolved path as %s\n", targetpath);*/
1284
ret = cli_read(targetcli, file->cli_fd, buf, offset, count);
1288
errno = smbc_errno(context, targetcli);
1293
file->offset += ret;
1295
DEBUG(4, (" --> %d\n", ret));
1297
return ret; /* Success, ret bytes of data ... */
1302
* Routine to write() a file ...
1306
smbc_write_ctx(SMBCCTX *context,
1313
fstring server, share, user, password;
1314
pstring path, targetpath;
1315
struct cli_state *targetcli;
1317
/* First check all pointers before dereferencing them */
1319
if (!context || !context->internal ||
1320
!context->internal->_initialized) {
1327
if (!file || !DLIST_CONTAINS(context->internal->_files, file)) {
1334
/* Check that the buffer exists ... */
1343
offset = file->offset; /* See "offset" comment in smbc_read_ctx() */
1345
/*d_printf(">>>write: parsing %s\n", file->fname);*/
1346
if (smbc_parse_path(context, file->fname,
1348
server, sizeof(server),
1349
share, sizeof(share),
1352
password, sizeof(password),
1358
/*d_printf(">>>write: resolving %s\n", path);*/
1359
if (!cli_resolve_path("", file->srv->cli, path,
1360
&targetcli, targetpath))
1362
d_printf("Could not resolve %s\n", path);
1365
/*d_printf(">>>write: resolved path as %s\n", targetpath);*/
1368
ret = cli_write(targetcli, file->cli_fd, 0, buf, offset, count);
1372
errno = smbc_errno(context, targetcli);
1377
file->offset += ret;
1379
return ret; /* Success, 0 bytes of data ... */
1383
* Routine to close() a file ...
1387
smbc_close_ctx(SMBCCTX *context,
1391
fstring server, share, user, password;
1392
pstring path, targetpath;
1393
struct cli_state *targetcli;
1395
if (!context || !context->internal ||
1396
!context->internal->_initialized) {
1403
if (!file || !DLIST_CONTAINS(context->internal->_files, file)) {
1413
return context->closedir(context, file);
1417
/*d_printf(">>>close: parsing %s\n", file->fname);*/
1418
if (smbc_parse_path(context, file->fname,
1420
server, sizeof(server),
1421
share, sizeof(share),
1424
password, sizeof(password),
1430
/*d_printf(">>>close: resolving %s\n", path);*/
1431
if (!cli_resolve_path("", file->srv->cli, path,
1432
&targetcli, targetpath))
1434
d_printf("Could not resolve %s\n", path);
1437
/*d_printf(">>>close: resolved path as %s\n", targetpath);*/
1439
if (!cli_close(targetcli, file->cli_fd)) {
1441
DEBUG(3, ("cli_close failed on %s. purging server.\n",
1443
/* Deallocate slot and remove the server
1444
* from the server cache if unused */
1445
errno = smbc_errno(context, targetcli);
1447
DLIST_REMOVE(context->internal->_files, file);
1448
SAFE_FREE(file->fname);
1450
context->callbacks.remove_unused_server_fn(context, srv);
1456
DLIST_REMOVE(context->internal->_files, file);
1457
SAFE_FREE(file->fname);
1464
* Get info from an SMB server on a file. Use a qpathinfo call first
1465
* and if that fails, use getatr, as Win95 sometimes refuses qpathinfo
1468
smbc_getatr(SMBCCTX * context,
1480
struct cli_state *targetcli;
1482
if (!context || !context->internal ||
1483
!context->internal->_initialized) {
1490
/* path fixup for . and .. */
1491
if (strequal(path, ".") || strequal(path, ".."))
1492
pstrcpy(fixedpath, "\\");
1495
pstrcpy(fixedpath, path);
1496
trim_string(fixedpath, NULL, "\\..");
1497
trim_string(fixedpath, NULL, "\\.");
1499
DEBUG(4,("smbc_getatr: sending qpathinfo\n"));
1501
if (!cli_resolve_path( "", srv->cli, fixedpath, &targetcli, targetpath))
1503
d_printf("Couldn't resolve %s\n", path);
1507
if ( targetcli->dfsroot )
1510
pstrcpy(temppath, targetpath);
1511
cli_dfs_make_full_path(targetpath, targetcli->desthost,
1512
targetcli->share, temppath);
1515
if (!srv->no_pathinfo2 &&
1516
cli_qpathinfo2(targetcli, targetpath,
1517
c_time, a_time, m_time, NULL, size, mode, ino)) {
1521
/* if this is NT then don't bother with the getatr */
1522
if (targetcli->capabilities & CAP_NT_SMBS) {
1527
if (cli_getatr(targetcli, targetpath, mode, size, m_time)) {
1528
if (m_time != NULL) {
1529
if (a_time != NULL) *a_time = *m_time;
1530
if (c_time != NULL) *c_time = *m_time;
1532
srv->no_pathinfo2 = True;
1542
* Set file info on an SMB server. Use setpathinfo call first. If that
1543
* fails, use setattrE..
1545
* Access and modification time parameters are always used and must be
1546
* provided. Create time, if zero, will be determined from the actual create
1547
* time of the file. If non-zero, the create time will be set as well.
1549
* "mode" (attributes) parameter may be set to -1 if it is not to be set.
1552
smbc_setatr(SMBCCTX * context, SMBCSRV *srv, char *path,
1553
time_t c_time, time_t a_time, time_t m_time,
1560
* First, try setpathinfo (if qpathinfo succeeded), for it is the
1561
* modern function for "new code" to be using, and it works given a
1562
* filename rather than requiring that the file be opened to have its
1563
* attributes manipulated.
1565
if (srv->no_pathinfo ||
1566
! cli_setpathinfo(srv->cli, path, c_time, a_time, m_time, mode)) {
1569
* setpathinfo is not supported; go to plan B.
1571
* cli_setatr() does not work on win98, and it also doesn't
1572
* support setting the access time (only the modification
1573
* time), so in all cases, we open the specified file and use
1574
* cli_setattrE() which should work on all OS versions, and
1575
* supports both times.
1578
/* Don't try {q,set}pathinfo() again, with this server */
1579
srv->no_pathinfo = True;
1582
if ((fd = cli_open(srv->cli, path, O_RDWR, DENY_NONE)) < 0) {
1584
errno = smbc_errno(context, srv->cli);
1589
* Get the creat time of the file (if it wasn't provided).
1590
* We'll need it in the set call
1593
ret = cli_getattrE(srv->cli, fd,
1595
&c_time, NULL, NULL);
1600
/* If we got create time, set times */
1602
/* Some OS versions don't support create time */
1603
if (c_time == 0 || c_time == -1) {
1604
c_time = time(NULL);
1608
* For sanity sake, since there is no POSIX function
1609
* to set the create time of a file, if the existing
1610
* create time is greater than either of access time
1611
* or modification time, set create time to the
1612
* smallest of those. This ensure that the create
1613
* time of a file is never greater than its last
1614
* access or modification time.
1616
if (c_time > a_time) c_time = a_time;
1617
if (c_time > m_time) c_time = m_time;
1619
/* Set the new attributes */
1620
ret = cli_setattrE(srv->cli, fd,
1621
c_time, a_time, m_time);
1622
cli_close(srv->cli, fd);
1626
* Unfortunately, setattrE() doesn't have a provision for
1627
* setting the access mode (attributes). We'll have to try
1628
* cli_setatr() for that, and with only this parameter, it
1629
* seems to work on win98.
1631
if (ret && mode != (uint16) -1) {
1632
ret = cli_setatr(srv->cli, path, mode, 0);
1636
errno = smbc_errno(context, srv->cli);
1645
* Routine to unlink() a file
1649
smbc_unlink_ctx(SMBCCTX *context,
1652
fstring server, share, user, password, workgroup;
1653
pstring path, targetpath;
1654
struct cli_state *targetcli;
1655
SMBCSRV *srv = NULL;
1657
if (!context || !context->internal ||
1658
!context->internal->_initialized) {
1660
errno = EINVAL; /* Best I can think of ... */
1672
if (smbc_parse_path(context, fname,
1673
workgroup, sizeof(workgroup),
1674
server, sizeof(server),
1675
share, sizeof(share),
1678
password, sizeof(password),
1684
if (user[0] == (char)0) fstrcpy(user, context->user);
1686
srv = smbc_server(context, True,
1687
server, share, workgroup, user, password);
1691
return -1; /* smbc_server sets errno */
1695
/*d_printf(">>>unlink: resolving %s\n", path);*/
1696
if (!cli_resolve_path( "", srv->cli, path, &targetcli, targetpath))
1698
d_printf("Could not resolve %s\n", path);
1701
/*d_printf(">>>unlink: resolved path as %s\n", targetpath);*/
1703
if (!cli_unlink(targetcli, targetpath)) {
1705
errno = smbc_errno(context, targetcli);
1707
if (errno == EACCES) { /* Check if the file is a directory */
1712
time_t m_time = 0, a_time = 0, c_time = 0;
1715
if (!smbc_getatr(context, srv, path, &mode, &size,
1716
&c_time, &a_time, &m_time, &ino)) {
1718
/* Hmmm, bad error ... What? */
1720
errno = smbc_errno(context, targetcli);
1726
if (IS_DOS_DIR(mode))
1729
errno = saverr; /* Restore this */
1738
return 0; /* Success ... */
1743
* Routine to rename() a file
1747
smbc_rename_ctx(SMBCCTX *ocontext,
1763
pstring targetpath1;
1764
pstring targetpath2;
1765
struct cli_state *targetcli1;
1766
struct cli_state *targetcli2;
1767
SMBCSRV *srv = NULL;
1769
if (!ocontext || !ncontext ||
1770
!ocontext->internal || !ncontext->internal ||
1771
!ocontext->internal->_initialized ||
1772
!ncontext->internal->_initialized) {
1774
errno = EINVAL; /* Best I can think of ... */
1779
if (!oname || !nname) {
1786
DEBUG(4, ("smbc_rename(%s,%s)\n", oname, nname));
1788
smbc_parse_path(ocontext, oname,
1789
workgroup, sizeof(workgroup),
1790
server1, sizeof(server1),
1791
share1, sizeof(share1),
1792
path1, sizeof(path1),
1793
user1, sizeof(user1),
1794
password1, sizeof(password1),
1797
if (user1[0] == (char)0) fstrcpy(user1, ocontext->user);
1799
smbc_parse_path(ncontext, nname,
1801
server2, sizeof(server2),
1802
share2, sizeof(share2),
1803
path2, sizeof(path2),
1804
user2, sizeof(user2),
1805
password2, sizeof(password2),
1808
if (user2[0] == (char)0) fstrcpy(user2, ncontext->user);
1810
if (strcmp(server1, server2) || strcmp(share1, share2) ||
1811
strcmp(user1, user2)) {
1813
/* Can't rename across file systems, or users?? */
1820
srv = smbc_server(ocontext, True,
1821
server1, share1, workgroup, user1, password1);
1828
/*d_printf(">>>rename: resolving %s\n", path1);*/
1829
if (!cli_resolve_path( "", srv->cli, path1, &targetcli1, targetpath1))
1831
d_printf("Could not resolve %s\n", path1);
1834
/*d_printf(">>>rename: resolved path as %s\n", targetpath1);*/
1835
/*d_printf(">>>rename: resolving %s\n", path2);*/
1836
if (!cli_resolve_path( "", srv->cli, path2, &targetcli2, targetpath2))
1838
d_printf("Could not resolve %s\n", path2);
1841
/*d_printf(">>>rename: resolved path as %s\n", targetpath2);*/
1843
if (strcmp(targetcli1->desthost, targetcli2->desthost) ||
1844
strcmp(targetcli1->share, targetcli2->share))
1846
/* can't rename across file systems */
1852
if (!cli_rename(targetcli1, targetpath1, targetpath2)) {
1853
int eno = smbc_errno(ocontext, targetcli1);
1855
if (eno != EEXIST ||
1856
!cli_unlink(targetcli1, targetpath2) ||
1857
!cli_rename(targetcli1, targetpath1, targetpath2)) {
1865
return 0; /* Success */
1870
* A routine to lseek() a file
1874
smbc_lseek_ctx(SMBCCTX *context,
1880
fstring server, share, user, password;
1881
pstring path, targetpath;
1882
struct cli_state *targetcli;
1884
if (!context || !context->internal ||
1885
!context->internal->_initialized) {
1892
if (!file || !DLIST_CONTAINS(context->internal->_files, file)) {
1902
return -1; /* Can't lseek a dir ... */
1908
file->offset = offset;
1912
file->offset += offset;
1916
/*d_printf(">>>lseek: parsing %s\n", file->fname);*/
1917
if (smbc_parse_path(context, file->fname,
1919
server, sizeof(server),
1920
share, sizeof(share),
1923
password, sizeof(password),
1930
/*d_printf(">>>lseek: resolving %s\n", path);*/
1931
if (!cli_resolve_path("", file->srv->cli, path,
1932
&targetcli, targetpath))
1934
d_printf("Could not resolve %s\n", path);
1937
/*d_printf(">>>lseek: resolved path as %s\n", targetpath);*/
1939
if (!cli_qfileinfo(targetcli, file->cli_fd, NULL,
1940
&size, NULL, NULL, NULL, NULL, NULL))
1942
SMB_OFF_T b_size = size;
1943
if (!cli_getattrE(targetcli, file->cli_fd,
1944
NULL, &b_size, NULL, NULL, NULL))
1951
file->offset = size + offset;
1960
return file->offset;
1965
* Generate an inode number from file name for those things that need it
1969
smbc_inode(SMBCCTX *context,
1973
if (!context || !context->internal ||
1974
!context->internal->_initialized) {
1981
if (!*name) return 2; /* FIXME, why 2 ??? */
1982
return (ino_t)str_checksum(name);
1987
* Routine to put basic stat info into a stat structure ... Used by stat and
1992
smbc_setup_stat(SMBCCTX *context,
2001
if (IS_DOS_DIR(mode)) {
2002
st->st_mode = SMBC_DIR_MODE;
2004
st->st_mode = SMBC_FILE_MODE;
2007
if (IS_DOS_ARCHIVE(mode)) st->st_mode |= S_IXUSR;
2008
if (IS_DOS_SYSTEM(mode)) st->st_mode |= S_IXGRP;
2009
if (IS_DOS_HIDDEN(mode)) st->st_mode |= S_IXOTH;
2010
if (!IS_DOS_READONLY(mode)) st->st_mode |= S_IWUSR;
2013
#ifdef HAVE_STAT_ST_BLKSIZE
2014
st->st_blksize = 512;
2016
#ifdef HAVE_STAT_ST_BLOCKS
2017
st->st_blocks = (size+511)/512;
2019
st->st_uid = getuid();
2020
st->st_gid = getgid();
2022
if (IS_DOS_DIR(mode)) {
2028
if (st->st_ino == 0) {
2029
st->st_ino = smbc_inode(context, fname);
2032
return True; /* FIXME: Is this needed ? */
2037
* Routine to stat a file given a name
2041
smbc_stat_ctx(SMBCCTX *context,
2059
if (!context || !context->internal ||
2060
!context->internal->_initialized) {
2062
errno = EINVAL; /* Best I can think of ... */
2074
DEBUG(4, ("smbc_stat(%s)\n", fname));
2076
if (smbc_parse_path(context, fname,
2077
workgroup, sizeof(workgroup),
2078
server, sizeof(server),
2079
share, sizeof(share),
2082
password, sizeof(password),
2088
if (user[0] == (char)0) fstrcpy(user, context->user);
2090
srv = smbc_server(context, True,
2091
server, share, workgroup, user, password);
2094
return -1; /* errno set by smbc_server */
2097
if (!smbc_getatr(context, srv, path, &mode, &size,
2098
&c_time, &a_time, &m_time, &ino)) {
2100
errno = smbc_errno(context, srv->cli);
2107
smbc_setup_stat(context, st, path, size, mode);
2109
st->st_atime = a_time;
2110
st->st_ctime = c_time;
2111
st->st_mtime = m_time;
2112
st->st_dev = srv->dev;
2119
* Routine to stat a file given an fd
2123
smbc_fstat_ctx(SMBCCTX *context,
2138
struct cli_state *targetcli;
2141
if (!context || !context->internal ||
2142
!context->internal->_initialized) {
2149
if (!file || !DLIST_CONTAINS(context->internal->_files, file)) {
2158
return context->fstatdir(context, file, st);
2162
/*d_printf(">>>fstat: parsing %s\n", file->fname);*/
2163
if (smbc_parse_path(context, file->fname,
2165
server, sizeof(server),
2166
share, sizeof(share),
2169
password, sizeof(password),
2175
/*d_printf(">>>fstat: resolving %s\n", path);*/
2176
if (!cli_resolve_path("", file->srv->cli, path,
2177
&targetcli, targetpath))
2179
d_printf("Could not resolve %s\n", path);
2182
/*d_printf(">>>fstat: resolved path as %s\n", targetpath);*/
2184
if (!cli_qfileinfo(targetcli, file->cli_fd, &mode, &size,
2185
&c_time, &a_time, &m_time, NULL, &ino)) {
2186
if (!cli_getattrE(targetcli, file->cli_fd, &mode, &size,
2187
&c_time, &a_time, &m_time)) {
2196
smbc_setup_stat(context, st, file->fname, size, mode);
2198
st->st_atime = a_time;
2199
st->st_ctime = c_time;
2200
st->st_mtime = m_time;
2201
st->st_dev = file->srv->dev;
2208
* Routine to open a directory
2209
* We accept the URL syntax explained in smbc_parse_path(), above.
2213
smbc_remove_dir(SMBCFILE *dir)
2215
struct smbc_dir_list *d,*f;
2222
SAFE_FREE(f->dirent);
2227
dir->dir_list = dir->dir_end = dir->dir_next = NULL;
2232
add_dirent(SMBCFILE *dir,
2234
const char *comment,
2237
struct smbc_dirent *dirent;
2239
int name_length = (name == NULL ? 0 : strlen(name));
2240
int comment_len = (comment == NULL ? 0 : strlen(comment));
2243
* Allocate space for the dirent, which must be increased by the
2244
* size of the name and the comment and 1 each for the null terminator.
2247
size = sizeof(struct smbc_dirent) + name_length + comment_len + 2;
2249
dirent = SMB_MALLOC(size);
2253
dir->dir_error = ENOMEM;
2258
ZERO_STRUCTP(dirent);
2260
if (dir->dir_list == NULL) {
2262
dir->dir_list = SMB_MALLOC_P(struct smbc_dir_list);
2263
if (!dir->dir_list) {
2266
dir->dir_error = ENOMEM;
2270
ZERO_STRUCTP(dir->dir_list);
2272
dir->dir_end = dir->dir_next = dir->dir_list;
2276
dir->dir_end->next = SMB_MALLOC_P(struct smbc_dir_list);
2278
if (!dir->dir_end->next) {
2281
dir->dir_error = ENOMEM;
2285
ZERO_STRUCTP(dir->dir_end->next);
2287
dir->dir_end = dir->dir_end->next;
2290
dir->dir_end->next = NULL;
2291
dir->dir_end->dirent = dirent;
2293
dirent->smbc_type = type;
2294
dirent->namelen = name_length;
2295
dirent->commentlen = comment_len;
2296
dirent->dirlen = size;
2299
* dirent->namelen + 1 includes the null (no null termination needed)
2300
* Ditto for dirent->commentlen.
2301
* The space for the two null bytes was allocated.
2303
strncpy(dirent->name, (name?name:""), dirent->namelen + 1);
2304
dirent->comment = (char *)(&dirent->name + dirent->namelen + 1);
2305
strncpy(dirent->comment, (comment?comment:""), dirent->commentlen + 1);
2312
list_unique_wg_fn(const char *name,
2314
const char *comment,
2317
SMBCFILE *dir = (SMBCFILE *)state;
2318
struct smbc_dir_list *dir_list;
2319
struct smbc_dirent *dirent;
2323
dirent_type = dir->dir_type;
2325
if (add_dirent(dir, name, comment, dirent_type) < 0) {
2327
/* An error occurred, what do we do? */
2328
/* FIXME: Add some code here */
2331
/* Point to the one just added */
2332
dirent = dir->dir_end->dirent;
2334
/* See if this was a duplicate */
2335
for (dir_list = dir->dir_list;
2336
dir_list != dir->dir_end;
2337
dir_list = dir_list->next) {
2339
strcmp(dir_list->dirent->name, dirent->name) == 0) {
2340
/* Duplicate. End end of list need to be removed. */
2344
if (do_remove && dir_list->next == dir->dir_end) {
2345
/* Found the end of the list. Remove it. */
2346
dir->dir_end = dir_list;
2347
free(dir_list->next);
2349
dir_list->next = NULL;
2356
list_fn(const char *name,
2358
const char *comment,
2361
SMBCFILE *dir = (SMBCFILE *)state;
2365
* We need to process the type a little ...
2367
* Disk share = 0x00000000
2368
* Print share = 0x00000001
2369
* Comms share = 0x00000002 (obsolete?)
2370
* IPC$ share = 0x00000003
2372
* administrative shares:
2373
* ADMIN$, IPC$, C$, D$, E$ ... are type |= 0x80000000
2376
if (dir->dir_type == SMBC_FILE_SHARE) {
2379
case 0 | 0x80000000:
2381
dirent_type = SMBC_FILE_SHARE;
2385
dirent_type = SMBC_PRINTER_SHARE;
2389
dirent_type = SMBC_COMMS_SHARE;
2392
case 3 | 0x80000000:
2394
dirent_type = SMBC_IPC_SHARE;
2398
dirent_type = SMBC_FILE_SHARE; /* FIXME, error? */
2403
dirent_type = dir->dir_type;
2406
if (add_dirent(dir, name, comment, dirent_type) < 0) {
2408
/* An error occurred, what do we do? */
2409
/* FIXME: Add some code here */
2415
dir_list_fn(const char *mnt,
2421
if (add_dirent((SMBCFILE *)state, finfo->name, "",
2422
(finfo->mode&aDIR?SMBC_DIR:SMBC_FILE)) < 0) {
2424
/* Handle an error ... */
2426
/* FIXME: Add some code ... */
2433
net_share_enum_rpc(struct cli_state *cli,
2434
void (*fn)(const char *name,
2436
const char *comment,
2443
uint32 info_level = 1;
2444
uint32 preferred_len = 0xffffffff;
2446
SRV_SHARE_INFO_CTR ctr;
2448
fstring comment = "";
2450
struct rpc_pipe_client *pipe_hnd;
2453
/* Open the server service pipe */
2454
pipe_hnd = cli_rpc_pipe_open_noauth(cli, PI_SRVSVC, &nt_status);
2456
DEBUG(1, ("net_share_enum_rpc pipe open fail!\n"));
2460
/* Allocate a context for parsing and for the entries in "ctr" */
2461
mem_ctx = talloc_init("libsmbclient: net_share_enum_rpc");
2462
if (mem_ctx == NULL) {
2463
DEBUG(0, ("out of memory for net_share_enum_rpc!\n"));
2464
cli_rpc_pipe_close(pipe_hnd);
2468
/* Issue the NetShareEnum RPC call and retrieve the response */
2469
init_enum_hnd(&enum_hnd, 0);
2470
result = rpccli_srvsvc_net_share_enum(pipe_hnd,
2477
/* Was it successful? */
2478
if (!W_ERROR_IS_OK(result) || ctr.num_entries == 0) {
2479
/* Nope. Go clean up. */
2483
/* For each returned entry... */
2484
for (i = 0; i < ctr.num_entries; i++) {
2486
/* pull out the share name */
2487
rpcstr_pull_unistr2_fstring(
2488
name, &ctr.share.info1[i].info_1_str.uni_netname);
2490
/* pull out the share's comment */
2491
rpcstr_pull_unistr2_fstring(
2492
comment, &ctr.share.info1[i].info_1_str.uni_remark);
2494
/* Get the type value */
2495
type = ctr.share.info1[i].info_1.type;
2497
/* Add this share to the list */
2498
(*fn)(name, type, comment, state);
2502
/* Close the server service pipe */
2503
cli_rpc_pipe_close(pipe_hnd);
2505
/* Free all memory which was allocated for this request */
2506
TALLOC_FREE(mem_ctx);
2508
/* Tell 'em if it worked */
2509
return W_ERROR_IS_OK(result) ? 0 : -1;
2515
smbc_opendir_ctx(SMBCCTX *context,
2518
fstring server, share, user, password, options;
2523
SMBCSRV *srv = NULL;
2524
SMBCFILE *dir = NULL;
2525
struct in_addr rem_ip;
2527
if (!context || !context->internal ||
2528
!context->internal->_initialized) {
2529
DEBUG(4, ("no valid context\n"));
2530
errno = EINVAL + 8192;
2536
DEBUG(4, ("no valid fname\n"));
2537
errno = EINVAL + 8193;
2541
if (smbc_parse_path(context, fname,
2542
workgroup, sizeof(workgroup),
2543
server, sizeof(server),
2544
share, sizeof(share),
2547
password, sizeof(password),
2548
options, sizeof(options))) {
2549
DEBUG(4, ("no valid path\n"));
2550
errno = EINVAL + 8194;
2554
DEBUG(4, ("parsed path: fname='%s' server='%s' share='%s' "
2555
"path='%s' options='%s'\n",
2556
fname, server, share, path, options));
2558
/* Ensure the options are valid */
2559
if (smbc_check_options(server, share, path, options)) {
2560
DEBUG(4, ("unacceptable options (%s)\n", options));
2561
errno = EINVAL + 8195;
2565
if (user[0] == (char)0) fstrcpy(user, context->user);
2567
dir = SMB_MALLOC_P(SMBCFILE);
2579
dir->fname = SMB_STRDUP(fname);
2583
dir->dir_list = dir->dir_next = dir->dir_end = NULL;
2585
if (server[0] == (char)0) {
2590
struct ip_service *ip_list;
2591
struct ip_service server_addr;
2592
struct user_auth_info u_info;
2593
struct cli_state *cli;
2595
if (share[0] != (char)0 || path[0] != (char)0) {
2597
errno = EINVAL + 8196;
2599
SAFE_FREE(dir->fname);
2605
/* Determine how many local master browsers to query */
2606
max_lmb_count = (context->options.browse_max_lmb_count == 0
2608
: context->options.browse_max_lmb_count);
2610
pstrcpy(u_info.username, user);
2611
pstrcpy(u_info.password, password);
2614
* We have server and share and path empty but options
2615
* requesting that we scan all master browsers for their list
2616
* of workgroups/domains. This implies that we must first try
2617
* broadcast queries to find all master browsers, and if that
2618
* doesn't work, then try our other methods which return only
2619
* a single master browser.
2623
if (!name_resolve_bcast(MSBROWSE, 1, &ip_list, &count)) {
2627
if (!find_master_ip(workgroup, &server_addr.ip)) {
2630
SAFE_FREE(dir->fname);
2637
ip_list = &server_addr;
2641
for (i = 0; i < count && i < max_lmb_count; i++) {
2642
DEBUG(99, ("Found master browser %d of %d: %s\n",
2643
i+1, MAX(count, max_lmb_count),
2644
inet_ntoa(ip_list[i].ip)));
2646
cli = get_ipc_connect_master_ip(&ip_list[i],
2647
workgroup, &u_info);
2648
/* cli == NULL is the master browser refused to talk or
2649
could not be found */
2653
fstrcpy(server, cli->desthost);
2656
DEBUG(4, ("using workgroup %s %s\n",
2657
workgroup, server));
2660
* For each returned master browser IP address, get a
2661
* connection to IPC$ on the server if we do not
2662
* already have one, and determine the
2663
* workgroups/domains that it knows about.
2666
srv = smbc_server(context, True, server, "IPC$",
2667
workgroup, user, password);
2673
dir->dir_type = SMBC_WORKGROUP;
2675
/* Now, list the stuff ... */
2677
if (!cli_NetServerEnum(srv->cli,
2679
SV_TYPE_DOMAIN_ENUM,
2689
* Server not an empty string ... Check the rest and see what
2692
if (*share == '\0') {
2693
if (*path != '\0') {
2695
/* Should not have empty share with path */
2696
errno = EINVAL + 8197;
2698
SAFE_FREE(dir->fname);
2706
* We don't know if <server> is really a server name
2707
* or is a workgroup/domain name. If we already have
2708
* a server structure for it, we'll use it.
2709
* Otherwise, check to see if <server><1D>,
2710
* <server><1B>, or <server><20> translates. We check
2711
* to see if <server> is an IP address first.
2715
* See if we have an existing server. Do not
2716
* establish a connection if one does not already
2719
srv = smbc_server(context, False, server, "IPC$",
2720
workgroup, user, password);
2723
* If no existing server and not an IP addr, look for
2727
!is_ipaddress(server) &&
2728
(resolve_name(server, &rem_ip, 0x1d) || /* LMB */
2729
resolve_name(server, &rem_ip, 0x1b) )) { /* DMB */
2733
dir->dir_type = SMBC_SERVER;
2736
* Get the backup list ...
2738
if (!name_status_find(server, 0, 0,
2739
rem_ip, buserver)) {
2741
DEBUG(0, ("Could not get name of "
2742
"local/domain master browser "
2743
"for server %s\n", server));
2745
SAFE_FREE(dir->fname);
2754
* Get a connection to IPC$ on the server if
2755
* we do not already have one
2757
srv = smbc_server(context, True,
2759
workgroup, user, password);
2761
DEBUG(0, ("got no contact to IPC$\n"));
2763
SAFE_FREE(dir->fname);
2772
/* Now, list the servers ... */
2773
if (!cli_NetServerEnum(srv->cli, server,
2774
0x0000FFFE, list_fn,
2778
SAFE_FREE(dir->fname);
2784
(resolve_name(server, &rem_ip, 0x20))) {
2786
/* If we hadn't found the server, get one now */
2788
srv = smbc_server(context, True,
2796
SAFE_FREE(dir->fname);
2803
dir->dir_type = SMBC_FILE_SHARE;
2806
/* List the shares ... */
2808
if (net_share_enum_rpc(
2811
(void *) dir) < 0 &&
2817
errno = cli_errno(srv->cli);
2819
SAFE_FREE(dir->fname);
2826
/* Neither the workgroup nor server exists */
2827
errno = ECONNREFUSED;
2829
SAFE_FREE(dir->fname);
2838
* The server and share are specified ... work from
2842
struct cli_state *targetcli;
2844
/* We connect to the server and list the directory */
2845
dir->dir_type = SMBC_FILE_SHARE;
2847
srv = smbc_server(context, True, server, share,
2848
workgroup, user, password);
2853
SAFE_FREE(dir->fname);
2862
/* Now, list the files ... */
2864
p = path + strlen(path);
2865
pstrcat(path, "\\*");
2867
if (!cli_resolve_path("", srv->cli, path,
2868
&targetcli, targetpath))
2870
d_printf("Could not resolve %s\n", path);
2872
SAFE_FREE(dir->fname);
2878
if (cli_list(targetcli, targetpath,
2879
aDIR | aSYSTEM | aHIDDEN,
2880
dir_list_fn, (void *)dir) < 0) {
2883
SAFE_FREE(dir->fname);
2886
errno = smbc_errno(context, targetcli);
2888
if (errno == EINVAL) {
2890
* See if they asked to opendir something
2891
* other than a directory. If so, the
2892
* converted error value we got would have
2893
* been EINVAL rather than ENOTDIR.
2895
*p = '\0'; /* restore original path */
2897
if (smbc_getatr(context, srv, path,
2901
! IS_DOS_DIR(mode)) {
2903
/* It is. Correct the error value */
2915
DLIST_ADD(context->internal->_files, dir);
2921
* Routine to close a directory
2925
smbc_closedir_ctx(SMBCCTX *context,
2929
if (!context || !context->internal ||
2930
!context->internal->_initialized) {
2937
if (!dir || !DLIST_CONTAINS(context->internal->_files, dir)) {
2944
smbc_remove_dir(dir); /* Clean it up */
2946
DLIST_REMOVE(context->internal->_files, dir);
2950
SAFE_FREE(dir->fname);
2951
SAFE_FREE(dir); /* Free the space too */
2959
smbc_readdir_internal(SMBCCTX * context,
2960
struct smbc_dirent *dest,
2961
struct smbc_dirent *src,
2962
int max_namebuf_len)
2964
if (context->options.urlencode_readdir_entries) {
2966
/* url-encode the name. get back remaining buffer space */
2968
smbc_urlencode(dest->name, src->name, max_namebuf_len);
2970
/* We now know the name length */
2971
dest->namelen = strlen(dest->name);
2973
/* Save the pointer to the beginning of the comment */
2974
dest->comment = dest->name + dest->namelen + 1;
2976
/* Copy the comment */
2977
strncpy(dest->comment, src->comment, max_namebuf_len - 1);
2978
dest->comment[max_namebuf_len - 1] = '\0';
2980
/* Save other fields */
2981
dest->smbc_type = src->smbc_type;
2982
dest->commentlen = strlen(dest->comment);
2983
dest->dirlen = ((dest->comment + dest->commentlen + 1) -
2987
/* No encoding. Just copy the entry as is. */
2988
memcpy(dest, src, src->dirlen);
2989
dest->comment = (char *)(&dest->name + src->namelen + 1);
2995
* Routine to get a directory entry
2998
struct smbc_dirent *
2999
smbc_readdir_ctx(SMBCCTX *context,
3003
struct smbc_dirent *dirp, *dirent;
3005
/* Check that all is ok first ... */
3007
if (!context || !context->internal ||
3008
!context->internal->_initialized) {
3011
DEBUG(0, ("Invalid context in smbc_readdir_ctx()\n"));
3016
if (!dir || !DLIST_CONTAINS(context->internal->_files, dir)) {
3019
DEBUG(0, ("Invalid dir in smbc_readdir_ctx()\n"));
3024
if (dir->file != False) { /* FIXME, should be dir, perhaps */
3027
DEBUG(0, ("Found file vs directory in smbc_readdir_ctx()\n"));
3032
if (!dir->dir_next) {
3036
dirent = dir->dir_next->dirent;
3044
dirp = (struct smbc_dirent *)context->internal->_dirent;
3045
maxlen = (sizeof(context->internal->_dirent) -
3046
sizeof(struct smbc_dirent));
3048
smbc_readdir_internal(context, dirp, dirent, maxlen);
3050
dir->dir_next = dir->dir_next->next;
3056
* Routine to get directory entries
3060
smbc_getdents_ctx(SMBCCTX *context,
3062
struct smbc_dirent *dirp,
3068
char *ndir = (char *)dirp;
3069
struct smbc_dir_list *dirlist;
3071
/* Check that all is ok first ... */
3073
if (!context || !context->internal ||
3074
!context->internal->_initialized) {
3081
if (!dir || !DLIST_CONTAINS(context->internal->_files, dir)) {
3088
if (dir->file != False) { /* FIXME, should be dir, perhaps */
3096
* Now, retrieve the number of entries that will fit in what was passed
3097
* We have to figure out if the info is in the list, or we need to
3098
* send a request to the server to get the info.
3101
while ((dirlist = dir->dir_next)) {
3102
struct smbc_dirent *dirent;
3104
if (!dirlist->dirent) {
3106
errno = ENOENT; /* Bad error */
3111
/* Do urlencoding of next entry, if so selected */
3112
dirent = (struct smbc_dirent *)context->internal->_dirent;
3113
maxlen = (sizeof(context->internal->_dirent) -
3114
sizeof(struct smbc_dirent));
3115
smbc_readdir_internal(context, dirent, dirlist->dirent, maxlen);
3117
reqd = dirent->dirlen;
3121
if (rem < count) { /* We managed to copy something */
3127
else { /* Nothing copied ... */
3129
errno = EINVAL; /* Not enough space ... */
3136
memcpy(ndir, dirent, reqd); /* Copy the data in ... */
3138
((struct smbc_dirent *)ndir)->comment =
3139
(char *)(&((struct smbc_dirent *)ndir)->name +
3147
dir->dir_next = dirlist = dirlist -> next;
3158
* Routine to create a directory ...
3162
smbc_mkdir_ctx(SMBCCTX *context,
3172
pstring path, targetpath;
3173
struct cli_state *targetcli;
3175
if (!context || !context->internal ||
3176
!context->internal->_initialized) {
3190
DEBUG(4, ("smbc_mkdir(%s)\n", fname));
3192
if (smbc_parse_path(context, fname,
3193
workgroup, sizeof(workgroup),
3194
server, sizeof(server),
3195
share, sizeof(share),
3198
password, sizeof(password),
3204
if (user[0] == (char)0) fstrcpy(user, context->user);
3206
srv = smbc_server(context, True,
3207
server, share, workgroup, user, password);
3211
return -1; /* errno set by smbc_server */
3215
/*d_printf(">>>mkdir: resolving %s\n", path);*/
3216
if (!cli_resolve_path( "", srv->cli, path, &targetcli, targetpath))
3218
d_printf("Could not resolve %s\n", path);
3221
/*d_printf(">>>mkdir: resolved path as %s\n", targetpath);*/
3223
if (!cli_mkdir(targetcli, targetpath)) {
3225
errno = smbc_errno(context, targetcli);
3235
* Our list function simply checks to see if a directory is not empty
3238
static int smbc_rmdir_dirempty = True;
3241
rmdir_list_fn(const char *mnt,
3246
if (strncmp(finfo->name, ".", 1) != 0 &&
3247
strncmp(finfo->name, "..", 2) != 0) {
3249
smbc_rmdir_dirempty = False;
3254
* Routine to remove a directory
3258
smbc_rmdir_ctx(SMBCCTX *context,
3269
struct cli_state *targetcli;
3271
if (!context || !context->internal ||
3272
!context->internal->_initialized) {
3286
DEBUG(4, ("smbc_rmdir(%s)\n", fname));
3288
if (smbc_parse_path(context, fname,
3289
workgroup, sizeof(workgroup),
3290
server, sizeof(server),
3291
share, sizeof(share),
3294
password, sizeof(password),
3301
if (user[0] == (char)0) fstrcpy(user, context->user);
3303
srv = smbc_server(context, True,
3304
server, share, workgroup, user, password);
3308
return -1; /* errno set by smbc_server */
3312
/*d_printf(">>>rmdir: resolving %s\n", path);*/
3313
if (!cli_resolve_path( "", srv->cli, path, &targetcli, targetpath))
3315
d_printf("Could not resolve %s\n", path);
3318
/*d_printf(">>>rmdir: resolved path as %s\n", targetpath);*/
3321
if (!cli_rmdir(targetcli, targetpath)) {
3323
errno = smbc_errno(context, targetcli);
3325
if (errno == EACCES) { /* Check if the dir empty or not */
3327
/* Local storage to avoid buffer overflows */
3330
smbc_rmdir_dirempty = True; /* Make this so ... */
3332
pstrcpy(lpath, targetpath);
3333
pstrcat(lpath, "\\*");
3335
if (cli_list(targetcli, lpath,
3336
aDIR | aSYSTEM | aHIDDEN,
3337
rmdir_list_fn, NULL) < 0) {
3339
/* Fix errno to ignore latest error ... */
3340
DEBUG(5, ("smbc_rmdir: "
3341
"cli_list returned an error: %d\n",
3342
smbc_errno(context, targetcli)));
3347
if (smbc_rmdir_dirempty)
3363
* Routine to return the current directory position
3367
smbc_telldir_ctx(SMBCCTX *context,
3370
off_t ret_val; /* Squash warnings about cast */
3372
if (!context || !context->internal ||
3373
!context->internal->_initialized) {
3380
if (!dir || !DLIST_CONTAINS(context->internal->_files, dir)) {
3387
if (dir->file != False) { /* FIXME, should be dir, perhaps */
3395
* We return the pointer here as the offset
3397
ret_val = (off_t)(long)dir->dir_next;
3403
* A routine to run down the list and see if the entry is OK
3406
struct smbc_dir_list *
3407
smbc_check_dir_ent(struct smbc_dir_list *list,
3408
struct smbc_dirent *dirent)
3411
/* Run down the list looking for what we want */
3415
struct smbc_dir_list *tmp = list;
3419
if (tmp->dirent == dirent)
3428
return NULL; /* Not found, or an error */
3434
* Routine to seek on a directory
3438
smbc_lseekdir_ctx(SMBCCTX *context,
3442
long int l_offset = offset; /* Handle problems of size */
3443
struct smbc_dirent *dirent = (struct smbc_dirent *)l_offset;
3444
struct smbc_dir_list *list_ent = (struct smbc_dir_list *)NULL;
3446
if (!context || !context->internal ||
3447
!context->internal->_initialized) {
3454
if (dir->file != False) { /* FIXME, should be dir, perhaps */
3461
/* Now, check what we were passed and see if it is OK ... */
3463
if (dirent == NULL) { /* Seek to the begining of the list */
3465
dir->dir_next = dir->dir_list;
3470
/* Now, run down the list and make sure that the entry is OK */
3471
/* This may need to be changed if we change the format of the list */
3473
if ((list_ent = smbc_check_dir_ent(dir->dir_list, dirent)) == NULL) {
3475
errno = EINVAL; /* Bad entry */
3480
dir->dir_next = list_ent;
3487
* Routine to fstat a dir
3491
smbc_fstatdir_ctx(SMBCCTX *context,
3496
if (!context || !context->internal ||
3497
!context->internal->_initialized) {
3504
/* No code yet ... */
3511
smbc_chmod_ctx(SMBCCTX *context,
3524
if (!context || !context->internal ||
3525
!context->internal->_initialized) {
3527
errno = EINVAL; /* Best I can think of ... */
3539
DEBUG(4, ("smbc_chmod(%s, 0%3o)\n", fname, newmode));
3541
if (smbc_parse_path(context, fname,
3542
workgroup, sizeof(workgroup),
3543
server, sizeof(server),
3544
share, sizeof(share),
3547
password, sizeof(password),
3553
if (user[0] == (char)0) fstrcpy(user, context->user);
3555
srv = smbc_server(context, True,
3556
server, share, workgroup, user, password);
3559
return -1; /* errno set by smbc_server */
3564
if (!(newmode & (S_IWUSR | S_IWGRP | S_IWOTH))) mode |= aRONLY;
3565
if ((newmode & S_IXUSR) && lp_map_archive(-1)) mode |= aARCH;
3566
if ((newmode & S_IXGRP) && lp_map_system(-1)) mode |= aSYSTEM;
3567
if ((newmode & S_IXOTH) && lp_map_hidden(-1)) mode |= aHIDDEN;
3569
if (!cli_setatr(srv->cli, path, mode, 0)) {
3570
errno = smbc_errno(context, srv->cli);
3578
smbc_utimes_ctx(SMBCCTX *context,
3580
struct timeval *tbuf)
3592
if (!context || !context->internal ||
3593
!context->internal->_initialized) {
3595
errno = EINVAL; /* Best I can think of ... */
3608
a_time = m_time = time(NULL);
3610
a_time = tbuf[0].tv_sec;
3611
m_time = tbuf[1].tv_sec;
3620
strncpy(atimebuf, ctime(&a_time), sizeof(atimebuf) - 1);
3621
atimebuf[sizeof(atimebuf) - 1] = '\0';
3622
if ((p = strchr(atimebuf, '\n')) != NULL) {
3626
strncpy(mtimebuf, ctime(&m_time), sizeof(mtimebuf) - 1);
3627
mtimebuf[sizeof(mtimebuf) - 1] = '\0';
3628
if ((p = strchr(mtimebuf, '\n')) != NULL) {
3632
dbgtext("smbc_utimes(%s, atime = %s mtime = %s)\n",
3633
fname, atimebuf, mtimebuf);
3636
if (smbc_parse_path(context, fname,
3637
workgroup, sizeof(workgroup),
3638
server, sizeof(server),
3639
share, sizeof(share),
3642
password, sizeof(password),
3648
if (user[0] == (char)0) fstrcpy(user, context->user);
3650
srv = smbc_server(context, True,
3651
server, share, workgroup, user, password);
3654
return -1; /* errno set by smbc_server */
3657
if (!smbc_setatr(context, srv, path, 0, a_time, m_time, 0)) {
3658
return -1; /* errno set by smbc_setatr */
3665
/* The MSDN is contradictory over the ordering of ACE entries in an ACL.
3666
However NT4 gives a "The information may have been modified by a
3667
computer running Windows NT 5.0" if denied ACEs do not appear before
3671
ace_compare(SEC_ACE *ace1,
3674
if (sec_ace_equal(ace1, ace2))
3677
if (ace1->type != ace2->type)
3678
return ace2->type - ace1->type;
3680
if (sid_compare(&ace1->trustee, &ace2->trustee))
3681
return sid_compare(&ace1->trustee, &ace2->trustee);
3683
if (ace1->flags != ace2->flags)
3684
return ace1->flags - ace2->flags;
3686
if (ace1->info.mask != ace2->info.mask)
3687
return ace1->info.mask - ace2->info.mask;
3689
if (ace1->size != ace2->size)
3690
return ace1->size - ace2->size;
3692
return memcmp(ace1, ace2, sizeof(SEC_ACE));
3697
sort_acl(SEC_ACL *the_acl)
3700
if (!the_acl) return;
3702
qsort(the_acl->ace, the_acl->num_aces, sizeof(the_acl->ace[0]),
3703
QSORT_CAST ace_compare);
3705
for (i=1;i<the_acl->num_aces;) {
3706
if (sec_ace_equal(&the_acl->ace[i-1], &the_acl->ace[i])) {
3708
for (j=i; j<the_acl->num_aces-1; j++) {
3709
the_acl->ace[j] = the_acl->ace[j+1];
3711
the_acl->num_aces--;
3718
/* convert a SID to a string, either numeric or username/group */
3720
convert_sid_to_string(struct cli_state *ipc_cli,
3726
char **domains = NULL;
3727
char **names = NULL;
3728
enum SID_NAME_USE *types = NULL;
3729
struct rpc_pipe_client *pipe_hnd = find_lsa_pipe_hnd(ipc_cli);
3730
sid_to_string(str, sid);
3733
return; /* no lookup desired */
3740
/* Ask LSA to convert the sid to a name */
3742
if (!NT_STATUS_IS_OK(rpccli_lsa_lookup_sids(pipe_hnd, ipc_cli->mem_ctx,
3743
pol, 1, sid, &domains,
3745
!domains || !domains[0] || !names || !names[0]) {
3751
slprintf(str, sizeof(fstring) - 1, "%s%s%s",
3752
domains[0], lp_winbind_separator(),
3756
/* convert a string to a SID, either numeric or username/group */
3758
convert_string_to_sid(struct cli_state *ipc_cli,
3764
enum SID_NAME_USE *types = NULL;
3765
DOM_SID *sids = NULL;
3767
struct rpc_pipe_client *pipe_hnd = find_lsa_pipe_hnd(ipc_cli);
3774
if (strncmp(str, "S-", 2) == 0) {
3775
return string_to_sid(sid, str);
3782
if (!NT_STATUS_IS_OK(rpccli_lsa_lookup_names(pipe_hnd, ipc_cli->mem_ctx,
3783
pol, 1, &str, NULL, &sids,
3789
sid_copy(sid, &sids[0]);
3796
/* parse an ACE in the same format as print_ace() */
3798
parse_ace(struct cli_state *ipc_cli,
3808
unsigned int aflags;
3812
const struct perm_value *v;
3818
/* These values discovered by inspection */
3819
static const struct perm_value special_values[] = {
3820
{ "R", 0x00120089 },
3821
{ "W", 0x00120116 },
3822
{ "X", 0x001200a0 },
3823
{ "D", 0x00010000 },
3824
{ "P", 0x00040000 },
3825
{ "O", 0x00080000 },
3829
static const struct perm_value standard_values[] = {
3830
{ "READ", 0x001200a9 },
3831
{ "CHANGE", 0x001301bf },
3832
{ "FULL", 0x001f01ff },
3838
p = strchr_m(str,':');
3839
if (!p) return False;
3842
/* Try to parse numeric form */
3844
if (sscanf(p, "%i/%i/%i", &atype, &aflags, &amask) == 3 &&
3845
convert_string_to_sid(ipc_cli, pol, numeric, &sid, str)) {
3849
/* Try to parse text form */
3851
if (!convert_string_to_sid(ipc_cli, pol, numeric, &sid, str)) {
3856
if (!next_token(&cp, tok, "/", sizeof(fstring))) {
3860
if (StrnCaseCmp(tok, "ALLOWED", strlen("ALLOWED")) == 0) {
3861
atype = SEC_ACE_TYPE_ACCESS_ALLOWED;
3862
} else if (StrnCaseCmp(tok, "DENIED", strlen("DENIED")) == 0) {
3863
atype = SEC_ACE_TYPE_ACCESS_DENIED;
3868
/* Only numeric form accepted for flags at present */
3870
if (!(next_token(&cp, tok, "/", sizeof(fstring)) &&
3871
sscanf(tok, "%i", &aflags))) {
3875
if (!next_token(&cp, tok, "/", sizeof(fstring))) {
3879
if (strncmp(tok, "0x", 2) == 0) {
3880
if (sscanf(tok, "%i", &amask) != 1) {
3886
for (v = standard_values; v->perm; v++) {
3887
if (strcmp(tok, v->perm) == 0) {
3898
for (v = special_values; v->perm; v++) {
3899
if (v->perm[0] == *p) {
3905
if (!found) return False;
3915
init_sec_ace(ace, &sid, atype, mask, aflags);
3919
/* add an ACE to a list of ACEs in a SEC_ACL */
3921
add_ace(SEC_ACL **the_acl,
3929
(*the_acl) = make_sec_acl(ctx, 3, 1, ace);
3933
if ((aces = SMB_CALLOC_ARRAY(SEC_ACE, 1+(*the_acl)->num_aces)) == NULL) {
3936
memcpy(aces, (*the_acl)->ace, (*the_acl)->num_aces * sizeof(SEC_ACE));
3937
memcpy(aces+(*the_acl)->num_aces, ace, sizeof(SEC_ACE));
3938
newacl = make_sec_acl(ctx, (*the_acl)->revision,
3939
1+(*the_acl)->num_aces, aces);
3941
(*the_acl) = newacl;
3946
/* parse a ascii version of a security descriptor */
3948
sec_desc_parse(TALLOC_CTX *ctx,
3949
struct cli_state *ipc_cli,
3954
const char *p = str;
3956
SEC_DESC *ret = NULL;
3958
DOM_SID *grp_sid=NULL;
3959
DOM_SID *owner_sid=NULL;
3963
while (next_token(&p, tok, "\t,\r\n", sizeof(tok))) {
3965
if (StrnCaseCmp(tok,"REVISION:", 9) == 0) {
3966
revision = strtol(tok+9, NULL, 16);
3970
if (StrnCaseCmp(tok,"OWNER:", 6) == 0) {
3972
DEBUG(5, ("OWNER specified more than once!\n"));
3975
owner_sid = SMB_CALLOC_ARRAY(DOM_SID, 1);
3977
!convert_string_to_sid(ipc_cli, pol,
3979
owner_sid, tok+6)) {
3980
DEBUG(5, ("Failed to parse owner sid\n"));
3986
if (StrnCaseCmp(tok,"OWNER+:", 7) == 0) {
3988
DEBUG(5, ("OWNER specified more than once!\n"));
3991
owner_sid = SMB_CALLOC_ARRAY(DOM_SID, 1);
3993
!convert_string_to_sid(ipc_cli, pol,
3995
owner_sid, tok+7)) {
3996
DEBUG(5, ("Failed to parse owner sid\n"));
4002
if (StrnCaseCmp(tok,"GROUP:", 6) == 0) {
4004
DEBUG(5, ("GROUP specified more than once!\n"));
4007
grp_sid = SMB_CALLOC_ARRAY(DOM_SID, 1);
4009
!convert_string_to_sid(ipc_cli, pol,
4012
DEBUG(5, ("Failed to parse group sid\n"));
4018
if (StrnCaseCmp(tok,"GROUP+:", 7) == 0) {
4020
DEBUG(5, ("GROUP specified more than once!\n"));
4023
grp_sid = SMB_CALLOC_ARRAY(DOM_SID, 1);
4025
!convert_string_to_sid(ipc_cli, pol,
4028
DEBUG(5, ("Failed to parse group sid\n"));
4034
if (StrnCaseCmp(tok,"ACL:", 4) == 0) {
4036
if (!parse_ace(ipc_cli, pol, &ace, numeric, tok+4)) {
4037
DEBUG(5, ("Failed to parse ACL %s\n", tok));
4040
if(!add_ace(&dacl, &ace, ctx)) {
4041
DEBUG(5, ("Failed to add ACL %s\n", tok));
4047
if (StrnCaseCmp(tok,"ACL+:", 5) == 0) {
4049
if (!parse_ace(ipc_cli, pol, &ace, False, tok+5)) {
4050
DEBUG(5, ("Failed to parse ACL %s\n", tok));
4053
if(!add_ace(&dacl, &ace, ctx)) {
4054
DEBUG(5, ("Failed to add ACL %s\n", tok));
4060
DEBUG(5, ("Failed to parse security descriptor\n"));
4064
ret = make_sec_desc(ctx, revision, SEC_DESC_SELF_RELATIVE,
4065
owner_sid, grp_sid, NULL, dacl, &sd_size);
4069
SAFE_FREE(owner_sid);
4075
/* Obtain the current dos attributes */
4076
static DOS_ATTR_DESC *
4077
dos_attr_query(SMBCCTX *context,
4079
const char *filename,
4082
time_t m_time = 0, a_time = 0, c_time = 0;
4085
SMB_INO_T inode = 0;
4088
ret = TALLOC_P(ctx, DOS_ATTR_DESC);
4094
/* Obtain the DOS attributes */
4095
if (!smbc_getatr(context, srv, CONST_DISCARD(char *, filename),
4097
&c_time, &a_time, &m_time, &inode)) {
4099
errno = smbc_errno(context, srv->cli);
4100
DEBUG(5, ("dos_attr_query Failed to query old attributes\n"));
4107
ret->a_time = a_time;
4108
ret->c_time = c_time;
4109
ret->m_time = m_time;
4116
/* parse a ascii version of a security descriptor */
4118
dos_attr_parse(SMBCCTX *context,
4123
const char *p = str;
4126
while (next_token(&p, tok, "\t,\r\n", sizeof(tok))) {
4128
if (StrnCaseCmp(tok, "MODE:", 5) == 0) {
4129
dad->mode = strtol(tok+5, NULL, 16);
4133
if (StrnCaseCmp(tok, "SIZE:", 5) == 0) {
4134
dad->size = (SMB_OFF_T)atof(tok+5);
4138
if (StrnCaseCmp(tok, "A_TIME:", 7) == 0) {
4139
dad->a_time = (time_t)strtol(tok+7, NULL, 10);
4143
if (StrnCaseCmp(tok, "C_TIME:", 7) == 0) {
4144
dad->c_time = (time_t)strtol(tok+7, NULL, 10);
4148
if (StrnCaseCmp(tok, "M_TIME:", 7) == 0) {
4149
dad->m_time = (time_t)strtol(tok+7, NULL, 10);
4153
if (StrnCaseCmp(tok, "INODE:", 6) == 0) {
4154
dad->inode = (SMB_INO_T)atof(tok+6);
4160
/*****************************************************
4161
Retrieve the acls for a file.
4162
*******************************************************/
4165
cacl_get(SMBCCTX *context,
4168
struct cli_state *ipc_cli,
4184
BOOL exclude_nt_revision = False;
4185
BOOL exclude_nt_owner = False;
4186
BOOL exclude_nt_group = False;
4187
BOOL exclude_nt_acl = False;
4188
BOOL exclude_dos_mode = False;
4189
BOOL exclude_dos_size = False;
4190
BOOL exclude_dos_ctime = False;
4191
BOOL exclude_dos_atime = False;
4192
BOOL exclude_dos_mtime = False;
4193
BOOL exclude_dos_inode = False;
4194
BOOL numeric = True;
4195
BOOL determine_size = (bufsize == 0);
4199
fstring name_sandbox;
4203
time_t m_time = 0, a_time = 0, c_time = 0;
4207
struct cli_state *cli = srv->cli;
4209
/* Copy name so we can strip off exclusions (if any are specified) */
4210
strncpy(name_sandbox, attr_name, sizeof(name_sandbox) - 1);
4212
/* Ensure name is null terminated */
4213
name_sandbox[sizeof(name_sandbox) - 1] = '\0';
4215
/* Play in the sandbox */
4216
name = name_sandbox;
4218
/* If there are any exclusions, point to them and mask them from name */
4219
if ((pExclude = strchr(name, '!')) != NULL)
4224
all = (StrnCaseCmp(name, "system.*", 8) == 0);
4225
all_nt = (StrnCaseCmp(name, "system.nt_sec_desc.*", 20) == 0);
4226
all_nt_acls = (StrnCaseCmp(name, "system.nt_sec_desc.acl.*", 24) == 0);
4227
all_dos = (StrnCaseCmp(name, "system.dos_attr.*", 17) == 0);
4228
some_nt = (StrnCaseCmp(name, "system.nt_sec_desc.", 19) == 0);
4229
some_dos = (StrnCaseCmp(name, "system.dos_attr.", 16) == 0);
4230
numeric = (* (name + strlen(name) - 1) != '+');
4232
/* Look for exclusions from "all" requests */
4233
if (all || all_nt || all_dos) {
4235
/* Exclusions are delimited by '!' */
4238
pExclude = (p == NULL ? NULL : p + 1)) {
4240
/* Find end of this exclusion name */
4241
if ((p = strchr(pExclude, '!')) != NULL)
4246
/* Which exclusion name is this? */
4247
if (StrCaseCmp(pExclude, "nt_sec_desc.revision") == 0) {
4248
exclude_nt_revision = True;
4250
else if (StrCaseCmp(pExclude, "nt_sec_desc.owner") == 0) {
4251
exclude_nt_owner = True;
4253
else if (StrCaseCmp(pExclude, "nt_sec_desc.group") == 0) {
4254
exclude_nt_group = True;
4256
else if (StrCaseCmp(pExclude, "nt_sec_desc.acl") == 0) {
4257
exclude_nt_acl = True;
4259
else if (StrCaseCmp(pExclude, "dos_attr.mode") == 0) {
4260
exclude_dos_mode = True;
4262
else if (StrCaseCmp(pExclude, "dos_attr.size") == 0) {
4263
exclude_dos_size = True;
4265
else if (StrCaseCmp(pExclude, "dos_attr.c_time") == 0) {
4266
exclude_dos_ctime = True;
4268
else if (StrCaseCmp(pExclude, "dos_attr.a_time") == 0) {
4269
exclude_dos_atime = True;
4271
else if (StrCaseCmp(pExclude, "dos_attr.m_time") == 0) {
4272
exclude_dos_mtime = True;
4274
else if (StrCaseCmp(pExclude, "dos_attr.inode") == 0) {
4275
exclude_dos_inode = True;
4278
DEBUG(5, ("cacl_get received unknown exclusion: %s\n",
4289
* If we are (possibly) talking to an NT or new system and some NT
4290
* attributes have been requested...
4292
if (ipc_cli && (all || some_nt || all_nt_acls)) {
4293
/* Point to the portion after "system.nt_sec_desc." */
4294
name += 19; /* if (all) this will be invalid but unused */
4296
/* ... then obtain any NT attributes which were requested */
4297
fnum = cli_nt_create(cli, filename, CREATE_ACCESS_READ);
4300
DEBUG(5, ("cacl_get failed to open %s: %s\n",
4301
filename, cli_errstr(cli)));
4306
sd = cli_query_secdesc(cli, fnum, ctx);
4310
("cacl_get Failed to query old descriptor\n"));
4315
cli_close(cli, fnum);
4317
if (! exclude_nt_revision) {
4318
if (all || all_nt) {
4319
if (determine_size) {
4320
p = talloc_asprintf(ctx,
4329
n = snprintf(buf, bufsize,
4333
} else if (StrCaseCmp(name, "revision") == 0) {
4334
if (determine_size) {
4335
p = talloc_asprintf(ctx, "%d",
4343
n = snprintf(buf, bufsize, "%d",
4348
if (!determine_size && n > bufsize) {
4357
if (! exclude_nt_owner) {
4358
/* Get owner and group sid */
4359
if (sd->owner_sid) {
4360
convert_sid_to_string(ipc_cli, pol,
4365
fstrcpy(sidstr, "");
4368
if (all || all_nt) {
4369
if (determine_size) {
4370
p = talloc_asprintf(ctx, ",OWNER:%s",
4378
n = snprintf(buf, bufsize,
4379
",OWNER:%s", sidstr);
4381
} else if (StrnCaseCmp(name, "owner", 5) == 0) {
4382
if (determine_size) {
4383
p = talloc_asprintf(ctx, "%s", sidstr);
4390
n = snprintf(buf, bufsize, "%s",
4395
if (!determine_size && n > bufsize) {
4404
if (! exclude_nt_group) {
4406
convert_sid_to_string(ipc_cli, pol,
4410
fstrcpy(sidstr, "");
4413
if (all || all_nt) {
4414
if (determine_size) {
4415
p = talloc_asprintf(ctx, ",GROUP:%s",
4423
n = snprintf(buf, bufsize,
4424
",GROUP:%s", sidstr);
4426
} else if (StrnCaseCmp(name, "group", 5) == 0) {
4427
if (determine_size) {
4428
p = talloc_asprintf(ctx, "%s", sidstr);
4435
n = snprintf(buf, bufsize,
4440
if (!determine_size && n > bufsize) {
4449
if (! exclude_nt_acl) {
4450
/* Add aces to value buffer */
4451
for (i = 0; sd->dacl && i < sd->dacl->num_aces; i++) {
4453
SEC_ACE *ace = &sd->dacl->ace[i];
4454
convert_sid_to_string(ipc_cli, pol,
4458
if (all || all_nt) {
4459
if (determine_size) {
4460
p = talloc_asprintf(
4476
",ACL:%s:%d/%d/0x%08x",
4482
} else if ((StrnCaseCmp(name, "acl", 3) == 0 &&
4483
StrCaseCmp(name+3, sidstr) == 0) ||
4484
(StrnCaseCmp(name, "acl+", 4) == 0 &&
4485
StrCaseCmp(name+4, sidstr) == 0)) {
4486
if (determine_size) {
4487
p = talloc_asprintf(
4499
n = snprintf(buf, bufsize,
4505
} else if (all_nt_acls) {
4506
if (determine_size) {
4507
p = talloc_asprintf(
4509
"%s%s:%d/%d/0x%08x",
4521
n = snprintf(buf, bufsize,
4522
"%s%s:%d/%d/0x%08x",
4540
/* Restore name pointer to its original value */
4544
if (all || some_dos) {
4545
/* Point to the portion after "system.dos_attr." */
4546
name += 16; /* if (all) this will be invalid but unused */
4548
/* Obtain the DOS attributes */
4549
if (!smbc_getatr(context, srv, filename, &mode, &size,
4550
&c_time, &a_time, &m_time, &ino)) {
4552
errno = smbc_errno(context, srv->cli);
4557
if (! exclude_dos_mode) {
4558
if (all || all_dos) {
4559
if (determine_size) {
4560
p = talloc_asprintf(ctx,
4573
n = snprintf(buf, bufsize,
4581
} else if (StrCaseCmp(name, "mode") == 0) {
4582
if (determine_size) {
4583
p = talloc_asprintf(ctx, "0x%x", mode);
4590
n = snprintf(buf, bufsize,
4595
if (!determine_size && n > bufsize) {
4604
if (! exclude_dos_size) {
4605
if (all || all_dos) {
4606
if (determine_size) {
4607
p = talloc_asprintf(
4617
n = snprintf(buf, bufsize,
4621
} else if (StrCaseCmp(name, "size") == 0) {
4622
if (determine_size) {
4623
p = talloc_asprintf(
4633
n = snprintf(buf, bufsize,
4639
if (!determine_size && n > bufsize) {
4648
if (! exclude_dos_ctime) {
4649
if (all || all_dos) {
4650
if (determine_size) {
4651
p = talloc_asprintf(ctx,
4660
n = snprintf(buf, bufsize,
4661
",C_TIME:%lu", c_time);
4663
} else if (StrCaseCmp(name, "c_time") == 0) {
4664
if (determine_size) {
4665
p = talloc_asprintf(ctx, "%lu", c_time);
4672
n = snprintf(buf, bufsize,
4677
if (!determine_size && n > bufsize) {
4686
if (! exclude_dos_atime) {
4687
if (all || all_dos) {
4688
if (determine_size) {
4689
p = talloc_asprintf(ctx,
4698
n = snprintf(buf, bufsize,
4699
",A_TIME:%lu", a_time);
4701
} else if (StrCaseCmp(name, "a_time") == 0) {
4702
if (determine_size) {
4703
p = talloc_asprintf(ctx, "%lu", a_time);
4710
n = snprintf(buf, bufsize,
4715
if (!determine_size && n > bufsize) {
4724
if (! exclude_dos_mtime) {
4725
if (all || all_dos) {
4726
if (determine_size) {
4727
p = talloc_asprintf(ctx,
4736
n = snprintf(buf, bufsize,
4737
",M_TIME:%lu", m_time);
4739
} else if (StrCaseCmp(name, "m_time") == 0) {
4740
if (determine_size) {
4741
p = talloc_asprintf(ctx, "%lu", m_time);
4748
n = snprintf(buf, bufsize,
4753
if (!determine_size && n > bufsize) {
4762
if (! exclude_dos_inode) {
4763
if (all || all_dos) {
4764
if (determine_size) {
4765
p = talloc_asprintf(
4775
n = snprintf(buf, bufsize,
4779
} else if (StrCaseCmp(name, "inode") == 0) {
4780
if (determine_size) {
4781
p = talloc_asprintf(
4791
n = snprintf(buf, bufsize,
4797
if (!determine_size && n > bufsize) {
4806
/* Restore name pointer to its original value */
4819
/*****************************************************
4820
set the ACLs on a file given an ascii description
4821
*******************************************************/
4823
cacl_set(TALLOC_CTX *ctx,
4824
struct cli_state *cli,
4825
struct cli_state *ipc_cli,
4827
const char *filename,
4828
const char *the_acl,
4834
SEC_DESC *sd = NULL, *old;
4835
SEC_ACL *dacl = NULL;
4836
DOM_SID *owner_sid = NULL;
4837
DOM_SID *grp_sid = NULL;
4842
BOOL numeric = True;
4844
/* the_acl will be null for REMOVE_ALL operations */
4846
numeric = ((p = strchr(the_acl, ':')) != NULL &&
4850
/* if this is to set the entire ACL... */
4851
if (*the_acl == '*') {
4852
/* ... then increment past the first colon */
4856
sd = sec_desc_parse(ctx, ipc_cli, pol, numeric,
4857
CONST_DISCARD(char *, the_acl));
4865
/* SMBC_XATTR_MODE_REMOVE_ALL is the only caller
4866
that doesn't deref sd */
4868
if (!sd && (mode != SMBC_XATTR_MODE_REMOVE_ALL)) {
4873
/* The desired access below is the only one I could find that works
4874
with NT4, W2KP and Samba */
4876
fnum = cli_nt_create(cli, filename, CREATE_ACCESS_READ);
4879
DEBUG(5, ("cacl_set failed to open %s: %s\n",
4880
filename, cli_errstr(cli)));
4885
old = cli_query_secdesc(cli, fnum, ctx);
4888
DEBUG(5, ("cacl_set Failed to query old descriptor\n"));
4893
cli_close(cli, fnum);
4896
case SMBC_XATTR_MODE_REMOVE_ALL:
4897
old->dacl->num_aces = 0;
4898
SAFE_FREE(old->dacl->ace);
4899
SAFE_FREE(old->dacl);
4904
case SMBC_XATTR_MODE_REMOVE:
4905
for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
4908
for (j=0;old->dacl && j<old->dacl->num_aces;j++) {
4909
if (sec_ace_equal(&sd->dacl->ace[i],
4910
&old->dacl->ace[j])) {
4912
for (k=j; k<old->dacl->num_aces-1;k++) {
4914
old->dacl->ace[k+1];
4916
old->dacl->num_aces--;
4917
if (old->dacl->num_aces == 0) {
4918
SAFE_FREE(old->dacl->ace);
4919
SAFE_FREE(old->dacl);
4936
case SMBC_XATTR_MODE_ADD:
4937
for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
4940
for (j=0;old->dacl && j<old->dacl->num_aces;j++) {
4941
if (sid_equal(&sd->dacl->ace[i].trustee,
4942
&old->dacl->ace[j].trustee)) {
4943
if (!(flags & SMBC_XATTR_FLAG_CREATE)) {
4948
old->dacl->ace[j] = sd->dacl->ace[i];
4954
if (!found && (flags & SMBC_XATTR_FLAG_REPLACE)) {
4960
for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
4961
add_ace(&old->dacl, &sd->dacl->ace[i], ctx);
4967
case SMBC_XATTR_MODE_SET:
4969
owner_sid = old->owner_sid;
4970
grp_sid = old->grp_sid;
4974
case SMBC_XATTR_MODE_CHOWN:
4975
owner_sid = sd->owner_sid;
4978
case SMBC_XATTR_MODE_CHGRP:
4979
grp_sid = sd->grp_sid;
4983
/* Denied ACE entries must come before allowed ones */
4984
sort_acl(old->dacl);
4986
/* Create new security descriptor and set it */
4987
sd = make_sec_desc(ctx, old->revision, SEC_DESC_SELF_RELATIVE,
4988
owner_sid, grp_sid, NULL, dacl, &sd_size);
4990
fnum = cli_nt_create(cli, filename,
4991
WRITE_DAC_ACCESS | WRITE_OWNER_ACCESS);
4994
DEBUG(5, ("cacl_set failed to open %s: %s\n",
4995
filename, cli_errstr(cli)));
5000
if (!cli_set_secdesc(cli, fnum, sd)) {
5001
DEBUG(5, ("ERROR: secdesc set failed: %s\n", cli_errstr(cli)));
5008
cli_close(cli, fnum);
5019
smbc_setxattr_ctx(SMBCCTX *context,
5040
if (!context || !context->internal ||
5041
!context->internal->_initialized) {
5043
errno = EINVAL; /* Best I can think of ... */
5055
DEBUG(4, ("smbc_setxattr(%s, %s, %.*s)\n",
5056
fname, name, (int) size, (const char*)value));
5058
if (smbc_parse_path(context, fname,
5059
workgroup, sizeof(workgroup),
5060
server, sizeof(server),
5061
share, sizeof(share),
5064
password, sizeof(password),
5070
if (user[0] == (char)0) fstrcpy(user, context->user);
5072
srv = smbc_server(context, True,
5073
server, share, workgroup, user, password);
5075
return -1; /* errno set by smbc_server */
5078
if (! srv->no_nt_session) {
5079
ipc_srv = smbc_attr_server(context, server, share,
5080
workgroup, user, password,
5082
srv->no_nt_session = True;
5087
ctx = talloc_init("smbc_setxattr");
5094
* Are they asking to set the entire set of known attributes?
5096
if (StrCaseCmp(name, "system.*") == 0 ||
5097
StrCaseCmp(name, "system.*+") == 0) {
5100
talloc_asprintf(ctx, "%s:%s",
5101
name+7, (const char *) value);
5109
ret = cacl_set(ctx, srv->cli,
5110
ipc_srv->cli, &pol, path,
5113
? SMBC_XATTR_MODE_SET
5114
: SMBC_XATTR_MODE_ADD),
5120
/* get a DOS Attribute Descriptor with current attributes */
5121
dad = dos_attr_query(context, ctx, path, srv);
5123
/* Overwrite old with new, using what was provided */
5124
dos_attr_parse(context, dad, srv, namevalue);
5126
/* Set the new DOS attributes */
5127
if (! smbc_setatr(context, srv, path,
5133
/* cause failure if NT failed too */
5138
/* we only fail if both NT and DOS sets failed */
5139
if (ret < 0 && ! dad) {
5140
ret = -1; /* in case dad was null */
5146
talloc_destroy(ctx);
5151
* Are they asking to set an access control element or to set
5152
* the entire access control list?
5154
if (StrCaseCmp(name, "system.nt_sec_desc.*") == 0 ||
5155
StrCaseCmp(name, "system.nt_sec_desc.*+") == 0 ||
5156
StrCaseCmp(name, "system.nt_sec_desc.revision") == 0 ||
5157
StrnCaseCmp(name, "system.nt_sec_desc.acl", 22) == 0 ||
5158
StrnCaseCmp(name, "system.nt_sec_desc.acl+", 23) == 0) {
5162
talloc_asprintf(ctx, "%s:%s",
5163
name+19, (const char *) value);
5166
ret = -1; /* errno set by smbc_server() */
5168
else if (! namevalue) {
5172
ret = cacl_set(ctx, srv->cli,
5173
ipc_srv->cli, &pol, path,
5176
? SMBC_XATTR_MODE_SET
5177
: SMBC_XATTR_MODE_ADD),
5180
talloc_destroy(ctx);
5185
* Are they asking to set the owner?
5187
if (StrCaseCmp(name, "system.nt_sec_desc.owner") == 0 ||
5188
StrCaseCmp(name, "system.nt_sec_desc.owner+") == 0) {
5192
talloc_asprintf(ctx, "%s:%s",
5193
name+19, (const char *) value);
5197
ret = -1; /* errno set by smbc_server() */
5199
else if (! namevalue) {
5203
ret = cacl_set(ctx, srv->cli,
5204
ipc_srv->cli, &pol, path,
5205
namevalue, SMBC_XATTR_MODE_CHOWN, 0);
5207
talloc_destroy(ctx);
5212
* Are they asking to set the group?
5214
if (StrCaseCmp(name, "system.nt_sec_desc.group") == 0 ||
5215
StrCaseCmp(name, "system.nt_sec_desc.group+") == 0) {
5219
talloc_asprintf(ctx, "%s:%s",
5220
name+19, (const char *) value);
5223
/* errno set by smbc_server() */
5226
else if (! namevalue) {
5230
ret = cacl_set(ctx, srv->cli,
5231
ipc_srv->cli, &pol, path,
5232
namevalue, SMBC_XATTR_MODE_CHOWN, 0);
5234
talloc_destroy(ctx);
5239
* Are they asking to set a DOS attribute?
5241
if (StrCaseCmp(name, "system.dos_attr.*") == 0 ||
5242
StrCaseCmp(name, "system.dos_attr.mode") == 0 ||
5243
StrCaseCmp(name, "system.dos_attr.c_time") == 0 ||
5244
StrCaseCmp(name, "system.dos_attr.a_time") == 0 ||
5245
StrCaseCmp(name, "system.dos_attr.m_time") == 0) {
5247
/* get a DOS Attribute Descriptor with current attributes */
5248
dad = dos_attr_query(context, ctx, path, srv);
5251
talloc_asprintf(ctx, "%s:%s",
5252
name+16, (const char *) value);
5257
/* Overwrite old with provided new params */
5258
dos_attr_parse(context, dad, srv, namevalue);
5260
/* Set the new DOS attributes */
5261
ret2 = smbc_setatr(context, srv, path,
5267
/* ret2 has True (success) / False (failure) */
5278
talloc_destroy(ctx);
5282
/* Unsupported attribute name */
5283
talloc_destroy(ctx);
5289
smbc_getxattr_ctx(SMBCCTX *context,
5308
if (!context || !context->internal ||
5309
!context->internal->_initialized) {
5311
errno = EINVAL; /* Best I can think of ... */
5323
DEBUG(4, ("smbc_getxattr(%s, %s)\n", fname, name));
5325
if (smbc_parse_path(context, fname,
5326
workgroup, sizeof(workgroup),
5327
server, sizeof(server),
5328
share, sizeof(share),
5331
password, sizeof(password),
5337
if (user[0] == (char)0) fstrcpy(user, context->user);
5339
srv = smbc_server(context, True,
5340
server, share, workgroup, user, password);
5342
return -1; /* errno set by smbc_server */
5345
if (! srv->no_nt_session) {
5346
ipc_srv = smbc_attr_server(context, server, share,
5347
workgroup, user, password,
5350
srv->no_nt_session = True;
5356
ctx = talloc_init("smbc:getxattr");
5362
/* Are they requesting a supported attribute? */
5363
if (StrCaseCmp(name, "system.*") == 0 ||
5364
StrnCaseCmp(name, "system.*!", 9) == 0 ||
5365
StrCaseCmp(name, "system.*+") == 0 ||
5366
StrnCaseCmp(name, "system.*+!", 10) == 0 ||
5367
StrCaseCmp(name, "system.nt_sec_desc.*") == 0 ||
5368
StrnCaseCmp(name, "system.nt_sec_desc.*!", 21) == 0 ||
5369
StrCaseCmp(name, "system.nt_sec_desc.*+") == 0 ||
5370
StrnCaseCmp(name, "system.nt_sec_desc.*+!", 22) == 0 ||
5371
StrCaseCmp(name, "system.nt_sec_desc.revision") == 0 ||
5372
StrCaseCmp(name, "system.nt_sec_desc.owner") == 0 ||
5373
StrCaseCmp(name, "system.nt_sec_desc.owner+") == 0 ||
5374
StrCaseCmp(name, "system.nt_sec_desc.group") == 0 ||
5375
StrCaseCmp(name, "system.nt_sec_desc.group+") == 0 ||
5376
StrnCaseCmp(name, "system.nt_sec_desc.acl", 22) == 0 ||
5377
StrnCaseCmp(name, "system.nt_sec_desc.acl+", 23) == 0 ||
5378
StrCaseCmp(name, "system.dos_attr.*") == 0 ||
5379
StrnCaseCmp(name, "system.dos_attr.*!", 18) == 0 ||
5380
StrCaseCmp(name, "system.dos_attr.mode") == 0 ||
5381
StrCaseCmp(name, "system.dos_attr.size") == 0 ||
5382
StrCaseCmp(name, "system.dos_attr.c_time") == 0 ||
5383
StrCaseCmp(name, "system.dos_attr.a_time") == 0 ||
5384
StrCaseCmp(name, "system.dos_attr.m_time") == 0 ||
5385
StrCaseCmp(name, "system.dos_attr.inode") == 0) {
5388
ret = cacl_get(context, ctx, srv,
5389
ipc_srv == NULL ? NULL : ipc_srv->cli,
5391
CONST_DISCARD(char *, name),
5392
CONST_DISCARD(char *, value), size);
5393
if (ret < 0 && errno == 0) {
5394
errno = smbc_errno(context, srv->cli);
5396
talloc_destroy(ctx);
5400
/* Unsupported attribute name */
5401
talloc_destroy(ctx);
5408
smbc_removexattr_ctx(SMBCCTX *context,
5424
if (!context || !context->internal ||
5425
!context->internal->_initialized) {
5427
errno = EINVAL; /* Best I can think of ... */
5439
DEBUG(4, ("smbc_removexattr(%s, %s)\n", fname, name));
5441
if (smbc_parse_path(context, fname,
5442
workgroup, sizeof(workgroup),
5443
server, sizeof(server),
5444
share, sizeof(share),
5447
password, sizeof(password),
5453
if (user[0] == (char)0) fstrcpy(user, context->user);
5455
srv = smbc_server(context, True,
5456
server, share, workgroup, user, password);
5458
return -1; /* errno set by smbc_server */
5461
if (! srv->no_nt_session) {
5462
ipc_srv = smbc_attr_server(context, server, share,
5463
workgroup, user, password,
5465
srv->no_nt_session = True;
5471
return -1; /* errno set by smbc_attr_server */
5474
ctx = talloc_init("smbc_removexattr");
5480
/* Are they asking to set the entire ACL? */
5481
if (StrCaseCmp(name, "system.nt_sec_desc.*") == 0 ||
5482
StrCaseCmp(name, "system.nt_sec_desc.*+") == 0) {
5485
ret = cacl_set(ctx, srv->cli,
5486
ipc_srv->cli, &pol, path,
5487
NULL, SMBC_XATTR_MODE_REMOVE_ALL, 0);
5488
talloc_destroy(ctx);
5493
* Are they asking to remove one or more spceific security descriptor
5496
if (StrCaseCmp(name, "system.nt_sec_desc.revision") == 0 ||
5497
StrCaseCmp(name, "system.nt_sec_desc.owner") == 0 ||
5498
StrCaseCmp(name, "system.nt_sec_desc.owner+") == 0 ||
5499
StrCaseCmp(name, "system.nt_sec_desc.group") == 0 ||
5500
StrCaseCmp(name, "system.nt_sec_desc.group+") == 0 ||
5501
StrnCaseCmp(name, "system.nt_sec_desc.acl", 22) == 0 ||
5502
StrnCaseCmp(name, "system.nt_sec_desc.acl+", 23) == 0) {
5505
ret = cacl_set(ctx, srv->cli,
5506
ipc_srv->cli, &pol, path,
5507
name + 19, SMBC_XATTR_MODE_REMOVE, 0);
5508
talloc_destroy(ctx);
5512
/* Unsupported attribute name */
5513
talloc_destroy(ctx);
5519
smbc_listxattr_ctx(SMBCCTX *context,
5525
* This isn't quite what listxattr() is supposed to do. This returns
5526
* the complete set of attribute names, always, rather than only those
5527
* attribute names which actually exist for a file. Hmmm...
5529
const char supported[] =
5532
"system.nt_sec_desc.revision\0"
5533
"system.nt_sec_desc.owner\0"
5534
"system.nt_sec_desc.owner+\0"
5535
"system.nt_sec_desc.group\0"
5536
"system.nt_sec_desc.group+\0"
5537
"system.nt_sec_desc.acl.*\0"
5538
"system.nt_sec_desc.acl\0"
5539
"system.nt_sec_desc.acl+\0"
5540
"system.nt_sec_desc.*\0"
5541
"system.nt_sec_desc.*+\0"
5542
"system.dos_attr.*\0"
5543
"system.dos_attr.mode\0"
5544
"system.dos_attr.c_time\0"
5545
"system.dos_attr.a_time\0"
5546
"system.dos_attr.m_time\0"
5550
return sizeof(supported);
5553
if (sizeof(supported) > size) {
5558
/* this can't be strcpy() because there are embedded null characters */
5559
memcpy(list, supported, sizeof(supported));
5560
return sizeof(supported);
5565
* Open a print file to be written to by other calls
5569
smbc_open_print_job_ctx(SMBCCTX *context,
5578
if (!context || !context->internal ||
5579
!context->internal->_initialized) {
5593
DEBUG(4, ("smbc_open_print_job_ctx(%s)\n", fname));
5595
if (smbc_parse_path(context, fname,
5597
server, sizeof(server),
5598
share, sizeof(share),
5601
password, sizeof(password),
5607
/* What if the path is empty, or the file exists? */
5609
return context->open(context, fname, O_WRONLY, 666);
5614
* Routine to print a file on a remote server ...
5616
* We open the file, which we assume to be on a remote server, and then
5617
* copy it to a print file on the share specified by printq.
5621
smbc_print_file_ctx(SMBCCTX *c_file,
5633
if (!c_file || !c_file->internal->_initialized || !c_print ||
5634
!c_print->internal->_initialized) {
5641
if (!fname && !printq) {
5648
/* Try to open the file for reading ... */
5650
if ((long)(fid1 = c_file->open(c_file, fname, O_RDONLY, 0666)) < 0) {
5652
DEBUG(3, ("Error, fname=%s, errno=%i\n", fname, errno));
5653
return -1; /* smbc_open sets errno */
5657
/* Now, try to open the printer file for writing */
5659
if ((long)(fid2 = c_print->open_print_job(c_print, printq)) < 0) {
5661
saverr = errno; /* Save errno */
5662
c_file->close_fn(c_file, fid1);
5668
while ((bytes = c_file->read(c_file, fid1, buf, sizeof(buf))) > 0) {
5672
if ((c_print->write(c_print, fid2, buf, bytes)) < 0) {
5675
c_file->close_fn(c_file, fid1);
5676
c_print->close_fn(c_print, fid2);
5685
c_file->close_fn(c_file, fid1); /* We have to close these anyway */
5686
c_print->close_fn(c_print, fid2);
5700
* Routine to list print jobs on a printer share ...
5704
smbc_list_print_jobs_ctx(SMBCCTX *context,
5706
smbc_list_print_job_fn fn)
5716
if (!context || !context->internal ||
5717
!context->internal->_initialized) {
5731
DEBUG(4, ("smbc_list_print_jobs(%s)\n", fname));
5733
if (smbc_parse_path(context, fname,
5734
workgroup, sizeof(workgroup),
5735
server, sizeof(server),
5736
share, sizeof(share),
5739
password, sizeof(password),
5745
if (user[0] == (char)0) fstrcpy(user, context->user);
5747
srv = smbc_server(context, True,
5748
server, share, workgroup, user, password);
5752
return -1; /* errno set by smbc_server */
5756
if (cli_print_queue(srv->cli,
5757
(void (*)(struct print_job_info *))fn) < 0) {
5759
errno = smbc_errno(context, srv->cli);
5769
* Delete a print job from a remote printer share
5773
smbc_unlink_print_job_ctx(SMBCCTX *context,
5786
if (!context || !context->internal ||
5787
!context->internal->_initialized) {
5801
DEBUG(4, ("smbc_unlink_print_job(%s)\n", fname));
5803
if (smbc_parse_path(context, fname,
5804
workgroup, sizeof(workgroup),
5805
server, sizeof(server),
5806
share, sizeof(share),
5809
password, sizeof(password),
5815
if (user[0] == (char)0) fstrcpy(user, context->user);
5817
srv = smbc_server(context, True,
5818
server, share, workgroup, user, password);
5822
return -1; /* errno set by smbc_server */
5826
if ((err = cli_printjob_del(srv->cli, id)) != 0) {
5829
errno = smbc_errno(context, srv->cli);
5830
else if (err == ERRnosuchprintjob)
5841
* Get a new empty handle to fill in with your own info
5844
smbc_new_context(void)
5848
context = SMB_MALLOC_P(SMBCCTX);
5854
ZERO_STRUCTP(context);
5856
context->internal = SMB_MALLOC_P(struct smbc_internal_data);
5857
if (!context->internal) {
5863
ZERO_STRUCTP(context->internal);
5866
/* ADD REASONABLE DEFAULTS */
5868
context->timeout = 20000; /* 20 seconds */
5870
context->options.browse_max_lmb_count = 3; /* # LMBs to query */
5871
context->options.urlencode_readdir_entries = False;/* backward compat */
5872
context->options.one_share_per_server = False;/* backward compat */
5874
context->open = smbc_open_ctx;
5875
context->creat = smbc_creat_ctx;
5876
context->read = smbc_read_ctx;
5877
context->write = smbc_write_ctx;
5878
context->close_fn = smbc_close_ctx;
5879
context->unlink = smbc_unlink_ctx;
5880
context->rename = smbc_rename_ctx;
5881
context->lseek = smbc_lseek_ctx;
5882
context->stat = smbc_stat_ctx;
5883
context->fstat = smbc_fstat_ctx;
5884
context->opendir = smbc_opendir_ctx;
5885
context->closedir = smbc_closedir_ctx;
5886
context->readdir = smbc_readdir_ctx;
5887
context->getdents = smbc_getdents_ctx;
5888
context->mkdir = smbc_mkdir_ctx;
5889
context->rmdir = smbc_rmdir_ctx;
5890
context->telldir = smbc_telldir_ctx;
5891
context->lseekdir = smbc_lseekdir_ctx;
5892
context->fstatdir = smbc_fstatdir_ctx;
5893
context->chmod = smbc_chmod_ctx;
5894
context->utimes = smbc_utimes_ctx;
5895
context->setxattr = smbc_setxattr_ctx;
5896
context->getxattr = smbc_getxattr_ctx;
5897
context->removexattr = smbc_removexattr_ctx;
5898
context->listxattr = smbc_listxattr_ctx;
5899
context->open_print_job = smbc_open_print_job_ctx;
5900
context->print_file = smbc_print_file_ctx;
5901
context->list_print_jobs = smbc_list_print_jobs_ctx;
5902
context->unlink_print_job = smbc_unlink_print_job_ctx;
5904
context->callbacks.check_server_fn = smbc_check_server;
5905
context->callbacks.remove_unused_server_fn = smbc_remove_unused_server;
5907
smbc_default_cache_functions(context);
5915
* Returns 0 on success. Otherwise returns 1, the SMBCCTX is _not_ freed
5916
* and thus you'll be leaking memory if not handled properly.
5920
smbc_free_context(SMBCCTX *context,
5930
DEBUG(1,("Performing aggressive shutdown.\n"));
5932
f = context->internal->_files;
5934
context->close_fn(context, f);
5937
context->internal->_files = NULL;
5939
/* First try to remove the servers the nice way. */
5940
if (context->callbacks.purge_cached_fn(context)) {
5943
DEBUG(1, ("Could not purge all servers, "
5944
"Nice way shutdown failed.\n"));
5945
s = context->internal->_servers;
5947
DEBUG(1, ("Forced shutdown: %p (fd=%d)\n",
5949
cli_shutdown(s->cli);
5950
context->callbacks.remove_cached_srv_fn(context,
5953
DLIST_REMOVE(context->internal->_servers, s);
5957
context->internal->_servers = NULL;
5961
/* This is the polite way */
5962
if (context->callbacks.purge_cached_fn(context)) {
5963
DEBUG(1, ("Could not purge all servers, "
5964
"free_context failed.\n"));
5968
if (context->internal->_servers) {
5969
DEBUG(1, ("Active servers in context, "
5970
"free_context failed.\n"));
5974
if (context->internal->_files) {
5975
DEBUG(1, ("Active files in context, "
5976
"free_context failed.\n"));
5982
/* Things we have to clean up */
5983
SAFE_FREE(context->workgroup);
5984
SAFE_FREE(context->netbios_name);
5985
SAFE_FREE(context->user);
5987
DEBUG(3, ("Context %p succesfully freed\n", context));
5988
SAFE_FREE(context->internal);
5995
* Each time the context structure is changed, we have binary backward
5996
* compatibility issues. Instead of modifying the public portions of the
5997
* context structure to add new options, instead, we put them in the internal
5998
* portion of the context structure and provide a set function for these new
6002
smbc_option_set(SMBCCTX *context,
6004
... /* option_value */)
6009
smbc_get_auth_data_with_context_fn auth_fn;
6013
va_start(ap, option_name);
6015
if (strcmp(option_name, "debug_to_stderr") == 0) {
6017
* Log to standard error instead of standard output.
6019
option_value.b = (BOOL) va_arg(ap, int);
6020
context->internal->_debug_stderr = option_value.b;
6022
} else if (strcmp(option_name, "debug_to_stderr") == 0) {
6024
* Log to standard error instead of standard output.
6026
* This function used to take a third parameter,
6027
* void *option_value. Since it was a void* and we needed to
6028
* pass a boolean, a boolean value was cast to void* to be
6029
* passed in. Now that we're using a va_list to retrieve the
6030
* parameters, the casting kludge is unnecessary.
6032
* WARNING: DO NOT USE THIS OPTION.
6033
* This option is retained for backward compatibility. New
6034
* applications should use "debug_to_stderr" and properly pass
6035
* in a boolean (int) value.
6037
option_value.v = va_arg(ap, void *);
6038
context->internal->_debug_stderr =
6039
(option_value.v == NULL ? False : True);
6041
} else if (strcmp(option_name, "auth_function") == 0) {
6043
* Use the new-style authentication function which includes
6046
option_value.auth_fn =
6047
va_arg(ap, smbc_get_auth_data_with_context_fn);
6048
context->internal->_auth_fn_with_context =
6049
option_value.auth_fn;
6050
} else if (strcmp(option_name, "user_data") == 0) {
6052
* Save a user data handle which may be retrieved by the user
6053
* with smbc_option_get()
6055
option_value.v = va_arg(ap, void *);
6056
context->internal->_user_data = option_value.v;
6064
* Retrieve the current value of an option
6067
smbc_option_get(SMBCCTX *context,
6070
if (strcmp(option_name, "debug_stderr") == 0) {
6072
* Log to standard error instead of standard output.
6074
#if defined(__intptr_t_defined) || defined(HAVE_INTPTR_T)
6075
return (void *) (intptr_t) context->internal->_debug_stderr;
6077
return (void *) context->internal->_debug_stderr;
6079
} else if (strcmp(option_name, "auth_function") == 0) {
6081
* Use the new-style authentication function which includes
6084
return (void *) context->internal->_auth_fn_with_context;
6085
} else if (strcmp(option_name, "user_data") == 0) {
6087
* Save a user data handle which may be retrieved by the user
6088
* with smbc_option_get()
6090
return context->internal->_user_data;
6098
* Initialise the library etc
6100
* We accept a struct containing handle information.
6101
* valid values for info->debug from 0 to 100,
6102
* and insist that info->fn must be non-null.
6105
smbc_init_context(SMBCCTX *context)
6112
if (!context || !context->internal) {
6117
/* Do not initialise the same client twice */
6118
if (context->internal->_initialized) {
6122
if ((!context->callbacks.auth_fn &&
6123
!context->internal->_auth_fn_with_context) ||
6124
context->debug < 0 ||
6125
context->debug > 100) {
6132
if (!smbc_initialized) {
6134
* Do some library-wide intializations the first time we get
6137
BOOL conf_loaded = False;
6139
/* Set this to what the user wants */
6140
DEBUGLEVEL = context->debug;
6144
setup_logging("libsmbclient", True);
6145
if (context->internal->_debug_stderr) {
6147
x_setbuf(x_stderr, NULL);
6150
/* Here we would open the smb.conf file if needed ... */
6152
in_client = True; /* FIXME, make a param */
6154
home = getenv("HOME");
6156
slprintf(conf, sizeof(conf), "%s/.smb/smb.conf", home);
6157
if (lp_load(conf, True, False, False, True)) {
6160
DEBUG(5, ("Could not load config file: %s\n",
6167
* Well, if that failed, try the dyn_CONFIGFILE
6168
* Which points to the standard locn, and if that
6169
* fails, silently ignore it and use the internal
6173
if (!lp_load(dyn_CONFIGFILE, True, False, False, False)) {
6174
DEBUG(5, ("Could not load config file: %s\n",
6178
* We loaded the global config file. Now lets
6179
* load user-specific modifications to the
6182
slprintf(conf, sizeof(conf),
6183
"%s/.smb/smb.conf.append", home);
6184
if (!lp_load(conf, True, False, False, False)) {
6186
("Could not append config file: "
6193
load_interfaces(); /* Load the list of interfaces ... */
6195
reopen_logs(); /* Get logging working ... */
6198
* Block SIGPIPE (from lib/util_sock.c: write())
6199
* It is not needed and should not stop execution
6201
BlockSignals(True, SIGPIPE);
6203
/* Done with one-time initialisation */
6204
smbc_initialized = 1;
6208
if (!context->user) {
6210
* FIXME: Is this the best way to get the user info?
6212
user = getenv("USER");
6213
/* walk around as "guest" if no username can be found */
6214
if (!user) context->user = SMB_STRDUP("guest");
6215
else context->user = SMB_STRDUP(user);
6218
if (!context->netbios_name) {
6220
* We try to get our netbios name from the config. If that
6221
* fails we fall back on constructing our netbios name from
6224
if (global_myname()) {
6225
context->netbios_name = SMB_STRDUP(global_myname());
6229
* Hmmm, I want to get hostname as well, but I am too
6230
* lazy for the moment
6233
context->netbios_name = SMB_MALLOC(17);
6234
if (!context->netbios_name) {
6238
slprintf(context->netbios_name, 16,
6239
"smbc%s%d", context->user, pid);
6243
DEBUG(1, ("Using netbios name %s.\n", context->netbios_name));
6245
if (!context->workgroup) {
6246
if (lp_workgroup()) {
6247
context->workgroup = SMB_STRDUP(lp_workgroup());
6250
/* TODO: Think about a decent default workgroup */
6251
context->workgroup = SMB_STRDUP("samba");
6255
DEBUG(1, ("Using workgroup %s.\n", context->workgroup));
6257
/* shortest timeout is 1 second */
6258
if (context->timeout > 0 && context->timeout < 1000)
6259
context->timeout = 1000;
6262
* FIXME: Should we check the function pointers here?
6265
context->internal->_initialized = True;
6271
/* Return the verion of samba, and thus libsmbclient */
6275
return samba_version_string();