Index: libsmb/clientgen.c =================================================================== --- libsmb/clientgen.c (revision 4510) +++ libsmb/clientgen.c (working copy) @@ -279,6 +279,7 @@ cli->inbuf = (char *)SMB_MALLOC(cli->bufsize+SAFETY_MARGIN); cli->oplock_handler = cli_oplock_ack; cli->case_sensitive = False; + cli->smb_rw_error = 0; cli->use_spnego = lp_client_use_spnego(); Index: libsmb/clisecdesc.c =================================================================== --- libsmb/clisecdesc.c (revision 4510) +++ libsmb/clisecdesc.c (working copy) @@ -30,6 +30,7 @@ char *rparam=NULL, *rdata=NULL; unsigned int rparam_count=0, rdata_count=0; prs_struct pd; + int pd_initialized = False; SEC_DESC *psd = NULL; SIVAL(param, 0, fnum); @@ -57,6 +58,7 @@ goto cleanup; prs_init(&pd, rdata_count, mem_ctx, UNMARSHALL); + pd_initialized = True; prs_copy_data_in(&pd, rdata, rdata_count); prs_set_offset(&pd,0); @@ -70,7 +72,9 @@ SAFE_FREE(rparam); SAFE_FREE(rdata); - prs_mem_free(&pd); + if (pd_initialized) { + prs_mem_free(&pd); + } return psd; } Index: libsmb/clilist.c =================================================================== --- libsmb/clilist.c (revision 4510) +++ libsmb/clilist.c (working copy) @@ -148,8 +148,13 @@ #if 0 int max_matches = 1366; /* Match W2k - was 512. */ #else +#if 0 /* djl */ int max_matches = 512; +#else + /* Sigh. XP Home misses files (sporadically!) if this is larger. */ + int max_matches = 24; #endif +#endif int info_level; char *p, *p2; pstring mask; @@ -196,7 +201,11 @@ SSVAL(param,0,ff_dir_handle); SSVAL(param,2,max_matches); /* max count */ SSVAL(param,4,info_level); +#if 0 /* djl */ SIVAL(param,6,0); /* ff_resume_key */ +#else + SIVAL(param,6,ff_lastname); /* ff_resume_key */ +#endif SSVAL(param,10,8+4+2); /* continue + resume required + close on end */ p = param+12; p += clistr_push(cli, param+12, mask, sizeof(param)-12, Index: libsmb/libsmbclient.c =================================================================== --- libsmb/libsmbclient.c (revision 4510) +++ libsmb/libsmbclient.c (working copy) @@ -5,7 +5,7 @@ Copyright (C) Richard Sharpe 2000, 2002 Copyright (C) John Terpstra 2000 Copyright (C) Tom Jansen (Ninja ISD) 2002 - Copyright (C) Derrell Lipman 2003 + Copyright (C) Derrell Lipman 2003, 2004 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -26,7 +26,22 @@ #include "include/libsmb_internal.h" + /* + * DOS Attribute values (used internally) + */ +typedef struct DOS_ATTR_DESC +{ + int mode; + unsigned long long size; + time_t a_time; + time_t c_time; + time_t m_time; + unsigned long long inode; +} DOS_ATTR_DESC; + + +/* * Internal flags for extended attributes */ @@ -84,84 +99,113 @@ return -1; } -static void -decode_urlpart(char *segment, size_t sizeof_segment) +/* + * smbc_urldecode() + * + * Convert strings of %xx to their single character equivalent. Each 'x' must + * be a valid hexadecimal digit, or that % sequence is left undecoded. + * + * dest may, but need not be, the same pointer as src. + * + * Returns the number of % sequences which could not be converted due to lack + * of two following hexadecimal digits. + */ +int +smbc_urldecode(char *dest, char * src, size_t max_dest_len) { - int old_length = strlen(segment); - int new_length = 0; - int new_length2 = 0; - int i = 0; - pstring new_segment; - char *new_usegment = 0; + int old_length = strlen(src); + int new_length = 0; + int new_length2 = 0; + int i = 0; + int err_count = 0; + pstring temp; + char * p; - if ( !old_length ) { - return; - } + if ( old_length == 0 ) { + return 0; + } - /* make a copy of the old one */ - new_usegment = (char*)SMB_MALLOC( old_length * 3 + 1 ); + p = temp; + while ( i < old_length ) { + unsigned char character = src[ i++ ]; - while( i < old_length ) { - int bReencode = False; - unsigned char character = segment[ i++ ]; - if ((character <= ' ') || (character > 127)) - bReencode = True; + if (character == '%') { + int a = i+1 < old_length ? hex2int( src[i] ) : -1; + int b = i+1 < old_length ? hex2int( src[i+1] ) : -1; - new_usegment [ new_length2++ ] = character; - if (character == '%' ) { - int a = i+1 < old_length ? hex2int( segment[i] ) : -1; - int b = i+1 < old_length ? hex2int( segment[i+1] ) : -1; - if ((a == -1) || (b == -1)) { /* Only replace if sequence is valid */ - /* Contains stray %, make sure to re-encode! */ - bReencode = True; - } else { - /* Valid %xx sequence */ - character = a * 16 + b; /* Replace with value of %dd */ - if (!character) - break; /* Stop at %00 */ + /* Replace valid sequence */ + if (a != -1 && b != -1) { - new_usegment [ new_length2++ ] = (unsigned char) segment[i++]; - new_usegment [ new_length2++ ] = (unsigned char) segment[i++]; - } - } - if (bReencode) { - unsigned int c = character / 16; - new_length2--; - new_usegment [ new_length2++ ] = '%'; + /* Replace valid %xx sequence with %dd */ + character = (a * 16) + b; - c += (c > 9) ? ('A' - 10) : '0'; - new_usegment[ new_length2++ ] = c; + if (character == '\0') { + break; /* Stop at %00 */ + } - c = character % 16; - c += (c > 9) ? ('A' - 10) : '0'; - new_usegment[ new_length2++ ] = c; - } + i += 2; + } else { - new_segment [ new_length++ ] = character; - } - new_segment [ new_length ] = 0; + err_count++; + } + } - free(new_usegment); + *p++ = character; + } - /* realloc it with unix charset */ - pull_utf8_allocate(&new_usegment, new_segment); + *p = '\0'; - /* this assumes (very safely) that removing %aa sequences - only shortens the string */ - strncpy(segment, new_usegment, sizeof_segment); + strncpy(dest, temp, max_dest_len); - free(new_usegment); + return err_count; } /* + * smbc_urlencode() + * + * Convert any characters not specifically allowed in a URL into their %xx + * equivalent. + * + * Returns the remaining buffer length. + */ +int +smbc_urlencode(char * dest, char * src, int max_dest_len) +{ + char hex[] = "0123456789ABCDEF"; + + for (; *src != '\0' && max_dest_len >= 3; src++) { + + if ((*src < '0' && + *src != '-' && + *src != '.') || + (*src > '9' && + *src < 'A') || + (*src > 'Z' && + *src < 'a' && + *src != '_') || + (*src > 'z')) { + *dest++ = '%'; + *dest++ = hex[(*src >> 4) & 0x0f]; + *dest++ = hex[*src & 0x0f]; + max_dest_len -= 3; + } else { + *dest++ = *src; + max_dest_len--; + } + } + + *dest++ = '\0'; + max_dest_len--; + + return max_dest_len; +} + +/* * Function to parse a path and turn it into components * * The general format of an SMB URI is explain in Christopher Hertel's CIFS * book, at http://ubiqx.org/cifs/Appendix-D.html. We accept a subset of the - * general format ("smb:" only; we do not look for "cifs:"), and expand on - * what he calls "context", herein called "options" to avoid conflict with the - * SMBCCTX context used throughout this library. We add the "mb" keyword - * which applies as follows: + * general format ("smb:" only; we do not look for "cifs:"). * * * We accept: @@ -169,37 +213,28 @@ * * Meaning of URLs: * - * smb:// show all workgroups known by the first master browser found - * smb://?mb=.any same as smb:// (i.e. without any options) + * smb:// Show all workgroups. * - * smb://?mb=.all show all workgroups known by every master browser found. - * Why might you want this? In an "appliance" application - * where the workgroup/domain being used on the local network - * is not known ahead of time, but where one wanted to - * provide network services via samba, a unique workgroup - * could be used. However, when the appliance is first - * started, the local samba instance's master browser has not - * synchronized with the other master browser(s) on the - * network (and might not synchronize for 12 minutes) and - * therefore is not aware of the workgroup/ domain names - * available on the network. This option may be used to - * overcome the problem of a libsmbclient application - * arbitrarily selecting the local (still ignorant) master - * browser to obtain its list of workgroups/domains and - * getting back a practically emmpty list. By requesting - * the list of workgroups/domains from each found master - * browser on the local network, a complete list of - * workgroups/domains can be built. + * The method of locating the list of workgroups varies + * depending upon the setting of the context variable + * context->options.browse_max_lmb_count. This value + * determine the maximum number of local master browsers to + * query for the list of workgroups. In order to ensure that + * a complete list of workgroups is obtained, all master + * browsers must be queried, but if there are many + * workgroups, the time spent querying can begin to add up. + * For small networks (not many workgroups), it is suggested + * that this variable be set to 0, indicating query all local + * master browsers. When the network has many workgroups, a + * reasonable setting for this variable might be around 3. * - * smb://?mb=name NOT YET IMPLEMENTED -- show all workgroups known by the - * master browser whose name is "name" - * * smb://name/ if name<1D> or name<1B> exists, list servers in * workgroup, else, if name<20> exists, list all shares * for server ... * - * If "options" are provided, this function returns the entire option list as - * a string, for later parsing by the caller. + * If "options" are provided, this function returns the entire option list as a + * string, for later parsing by the caller. Note that currently, no options + * are supported. */ static const char *smbc_prefix = "smb:"; @@ -246,7 +281,7 @@ p += 2; /* Skip the double slash */ /* See if any options were specified */ - if ( (q = strrchr(p, '?')) != NULL ) { + if (q = strrchr(p, '?')) { /* There are options. Null terminate here and point to them */ *q++ = '\0'; @@ -333,11 +368,11 @@ all_string_sub(path, "/", "\\", 0); decoding: - decode_urlpart(path, path_len); - decode_urlpart(server, server_len); - decode_urlpart(share, share_len); - decode_urlpart(user, user_len); - decode_urlpart(password, password_len); + (void) smbc_urldecode(path, path, path_len); + (void) smbc_urldecode(server, server, server_len); + (void) smbc_urldecode(share, share, share_len); + (void) smbc_urldecode(user, user, user_len); + (void) smbc_urldecode(password, password, password_len); return 0; } @@ -352,28 +387,8 @@ /* No options at all is always ok */ if (! *options) return 0; - /* - * For right now, we only support a very few options possibilities. - * No options are supported if server, share, or path are not empty. - * If all are empty, then we support the following two choices right - * now: - * - * mb=.any - * mb=.all - */ - if ((*server || *share || *path) && *options) { - /* Invalid: options provided with server, share, or path */ - DEBUG(1, ("Found unsupported options (%s) with non-empty server, share, or path\n", options)); - return -1; - } - - if (strcmp(options, "mb=.any") != 0 && - strcmp(options, "mb=.all") != 0) { - DEBUG(1, ("Found unsupported options (%s)\n", options)); - return -1; - } - - return 0; + /* Currently, we don't support any options. */ + return -1; } /* @@ -452,8 +467,6 @@ context->callbacks.remove_cached_srv_fn(context, srv); - SAFE_FREE(srv); - return 0; } @@ -471,7 +484,7 @@ srv = context->callbacks.get_cached_srv_fn(context, server, share, workgroup, username); - + if (!auth_called && !srv && (!username[0] || !password[0])) { context->callbacks.auth_fn(server, share, workgroup, sizeof(fstring), @@ -512,6 +525,7 @@ */ goto check_server_cache; } + return srv; } @@ -537,10 +551,14 @@ SMBCSRV *srv=NULL; struct cli_state c; struct nmb_name called, calling; + char *p; const char *server_n = server; + fstring group; pstring ipenv; struct in_addr ip; int tried_reverse = 0; + int port_try_first; + int port_try_next; zero_ip(&ip); ZERO_STRUCT(c); @@ -552,25 +570,56 @@ srv = find_server(context, server, share, workgroup, username, password); - if (srv) + + /* + * If we found a connection and we're only allowed one share per + * server... + */ + if (srv && *share != '\0' && context->options.one_share_per_server) { + + /* + * ... then if there's no current connection to the share, + * connect to it. find_server(), or rather the function + * pointed to by context->callbacks.get_cached_srv_fn which + * was called by find_server(), will have issued a tree + * disconnect if the requested share is not the same as the + * one that was already connected. + */ + if (srv->cli.cnum == (uint16) -1) { + /* Ensure we have accurate auth info */ + context->callbacks.auth_fn(server, share, + workgroup, sizeof(fstring), + username, sizeof(fstring), + password, sizeof(fstring)); + + if (! cli_send_tconX(&srv->cli, share, "?????", + password, strlen(password)+1)) { + + errno = smbc_errno(context, &srv->cli); + cli_shutdown(&srv->cli); + context->callbacks.remove_cached_srv_fn(context, srv); + srv = NULL; + } + + /* Regenerate the dev value since it's based on both server and share */ + if (srv) { + srv->dev = (dev_t)(str_checksum(server) ^ str_checksum(share)); + } + } + } + + /* If we have a connection... */ + if (srv) { + + /* ... then we're done here. Give 'em what they came for. */ return srv; + } make_nmb_name(&calling, context->netbios_name, 0x0); make_nmb_name(&called , server, 0x20); DEBUG(4,("smbc_server: server_n=[%s] server=[%s]\n", server_n, server)); -#if 0 /* djl: obsolete code? neither group nor p is used beyond here */ - if ((p=strchr_m(server_n,'#')) && - (strcmp(p+1,"1D")==0 || strcmp(p+1,"01")==0)) { - - fstrcpy(group, server_n); - p = strchr_m(group,'#'); - *p = 0; - - } -#endif - DEBUG(4,(" -> server_n=[%s] server=[%s]\n", server_n, server)); again: @@ -586,18 +635,31 @@ c.timeout = context->timeout; - /* Force use of port 139 for first try, so browse lists can work */ - c.port = 139; + /* + * Force use of port 139 for first try if share is $IPC, empty, or + * null, so browse lists can work + */ + if (share == NULL || *share == '\0' || strcmp(share, "IPC$") == 0) + { + port_try_first = 139; + port_try_next = 445; + } + else + { + port_try_first = 445; + port_try_next = 139; + } + c.port = port_try_first; + if (!cli_connect(&c, server_n, &ip)) { - /* - * Port 139 connection failed. Try port 445 to handle - * connections to newer (e.g. XP) hosts with NetBIOS disabled. - */ - c.port = 445; + + /* First connection attempt failed. Try alternate port. */ + c.port = port_try_next; + if (!cli_connect(&c, server_n, &ip)) { - cli_shutdown(&c); - errno = ENETUNREACH; + cli_shutdown(&c); + errno = ETIMEDOUT; return NULL; } } @@ -618,7 +680,7 @@ if ((rem_ip.s_addr=inet_addr(server)) == INADDR_NONE) { DEBUG(4, ("Could not convert IP address %s to struct in_addr\n", server)); - errno = ENOENT; + errno = ETIMEDOUT; return NULL; } @@ -632,7 +694,7 @@ } } - errno = ENOENT; + errno = ETIMEDOUT; return NULL; } @@ -640,7 +702,7 @@ if (!cli_negprot(&c)) { cli_shutdown(&c); - errno = ENOENT; + errno = ETIMEDOUT; return NULL; } @@ -724,10 +786,11 @@ SMBCSRV *ipc_srv=NULL; /* - * See if we've already created this special connection. Reference - * our "special" share name 'IPC$$'. + * See if we've already created this special connection. Reference our + * "special" share name '*IPC$', which is an impossible real share name + * due to the leading asterisk. */ - ipc_srv = find_server(context, server, "IPC$$", + ipc_srv = find_server(context, server, "*IPC$", workgroup, username, password); if (!ipc_srv) { @@ -793,7 +856,7 @@ errno = 0; /* let cache function set errno if it likes */ if (context->callbacks.add_cached_srv_fn(context, ipc_srv, server, - "IPC$$", + "*IPC$", workgroup, username)) { DEBUG(3, (" Failed to add server to cache\n")); @@ -1613,21 +1676,15 @@ { struct smbc_dirent *dirent; int size; - char *u_name = NULL, *u_comment = NULL; - size_t u_name_len = 0, u_comment_len = 0; + int name_len = (name == NULL ? 0 : strlen(name)); + int comment_len = (comment == NULL ? 0 : strlen(comment)); - if (name) - u_name_len = push_utf8_allocate(&u_name, name); - if (comment) - u_comment_len = push_utf8_allocate(&u_comment, comment); - /* * Allocate space for the dirent, which must be increased by the - * size of the name and the comment and 1 for the null on the comment. - * The null on the name is already accounted for. + * size of the name and the comment and 1 each for the null terminator. */ - size = sizeof(struct smbc_dirent) + u_name_len + u_comment_len + 1; + size = sizeof(struct smbc_dirent) + name_len + comment_len + 2; dirent = SMB_MALLOC(size); @@ -1674,18 +1731,15 @@ dir->dir_end->dirent = dirent; dirent->smbc_type = type; - dirent->namelen = u_name_len; - dirent->commentlen = u_comment_len; + dirent->namelen = name_len; + dirent->commentlen = comment_len; dirent->dirlen = size; - strncpy(dirent->name, (u_name?u_name:""), dirent->namelen + 1); + strncpy(dirent->name, (name?name:""), dirent->namelen + 1); dirent->comment = (char *)(&dirent->name + dirent->namelen + 1); - strncpy(dirent->comment, (u_comment?u_comment:""), dirent->commentlen + 1); + strncpy(dirent->comment, (comment?comment:""), dirent->commentlen + 1); - SAFE_FREE(u_comment); - SAFE_FREE(u_name); - return 0; } @@ -1799,14 +1853,14 @@ if (!context || !context->internal || !context->internal->_initialized) { DEBUG(4, ("no valid context\n")); - errno = EINVAL; + errno = EINVAL + 8192; return NULL; } if (!fname) { DEBUG(4, ("no valid fname\n")); - errno = EINVAL; + errno = EINVAL + 8193; return NULL; } @@ -1818,7 +1872,7 @@ password, sizeof(password), options, sizeof(options))) { DEBUG(4, ("no valid path\n")); - errno = EINVAL; + errno = EINVAL + 8194; return NULL; } @@ -1827,7 +1881,7 @@ /* Ensure the options are valid */ if (smbc_check_options(server, share, path, options)) { DEBUG(4, ("unacceptable options (%s)\n", options)); - errno = EINVAL; + errno = EINVAL + 8195; return NULL; } @@ -1853,108 +1907,11 @@ dir->file = False; dir->dir_list = dir->dir_next = dir->dir_end = NULL; - if (server[0] == (char)0 && - (! *options || strcmp(options, "mb=.any") == 0)) { - struct in_addr server_ip; - if (share[0] != (char)0 || path[0] != (char)0) { + if (server[0] == (char)0) { - errno = EINVAL; - if (dir) { - SAFE_FREE(dir->fname); - SAFE_FREE(dir); - } - return NULL; - } - - /* - * We have server and share and path empty ... so list the - * workgroups first try to get the LMB for our workgroup, and - * if that fails, try the DMB - */ - - pstrcpy(workgroup, lp_workgroup()); - - if (!find_master_ip(workgroup, &server_ip)) { - struct user_auth_info u_info; - struct cli_state *cli; - - DEBUG(4, ("Unable to find master browser for workgroup %s\n", - workgroup)); - - /* find the name of the server ... */ - pstrcpy(u_info.username, user); - pstrcpy(u_info.password, password); - - if (!(cli = get_ipc_connect_master_ip_bcast(workgroup, &u_info))) { - DEBUG(4, ("Unable to find master browser by " - "broadcast\n")); - errno = ENOENT; - return NULL; - } - - fstrcpy(server, cli->desthost); - - cli_shutdown(cli); - } else { - /* - * Do a name status query to find out the name of the - * master browser. We use <01><02>__MSBROWSE__<02>#01 if - * *#00 fails because a domain master browser will not - * respond to a wildcard query (or, at least, an NT4 - * server acting as the domain master browser will not). - * - * We might be able to use ONLY the query on MSBROWSE, but - * that's not yet been tested with all Windows versions, - * so until it is, leave the original wildcard query as - * the first choice and fall back to MSBROWSE if the - * wildcard query fails. - */ - if (!name_status_find("*", 0, 0x20, server_ip, server) && - !name_status_find(MSBROWSE, 1, 0x1b, server_ip, server)) { - errno = ENOENT; - return NULL; - } - } - - DEBUG(4, ("using workgroup %s %s\n", workgroup, server)); - - /* - * Get a connection to IPC$ on the server if we do not already - * have one - */ - - srv = smbc_server(context, server, "IPC$", workgroup, user, password); - if (!srv) { - - if (dir) { - SAFE_FREE(dir->fname); - SAFE_FREE(dir); - } - return NULL; - } - - dir->srv = srv; - dir->dir_type = SMBC_WORKGROUP; - - /* Now, list the stuff ... */ - - if (!cli_NetServerEnum(&srv->cli, workgroup, SV_TYPE_DOMAIN_ENUM, list_fn, - (void *)dir)) { - - DEBUG(1, ("Could not enumerate domains using '%s'\n", workgroup)); - if (dir) { - SAFE_FREE(dir->fname); - SAFE_FREE(dir); - } - - return NULL; - - } - } else if (server[0] == (char)0 && - (! *options || strcmp(options, "mb=.all") == 0)) { - int i; int count; + int max_lmb_count; struct ip_service *ip_list; struct ip_service server_addr; struct user_auth_info u_info; @@ -1962,7 +1919,7 @@ if (share[0] != (char)0 || path[0] != (char)0) { - errno = EINVAL; + errno = EINVAL + 8196; if (dir) { SAFE_FREE(dir->fname); SAFE_FREE(dir); @@ -1970,6 +1927,11 @@ return NULL; } + /* Determine how many local master browsers to query */ + max_lmb_count = (context->options.browse_max_lmb_count == 0 + ? INT_MAX + : context->options.browse_max_lmb_count); + pstrcpy(u_info.username, user); pstrcpy(u_info.password, password); @@ -1993,11 +1955,10 @@ count = 1; } - for (i = 0; i < count; i++) { + for (i = 0; i < count && i < max_lmb_count; i++) { DEBUG(99, ("Found master browser %s\n", inet_ntoa(ip_list[i].ip))); cli = get_ipc_connect_master_ip(&ip_list[i], workgroup, &u_info); - /* cli == NULL is the master browser refused to talk or could not be found */ if ( !cli ) @@ -2052,7 +2013,7 @@ if (path[0] != (char)0) { /* Should not have empty share with path */ - errno = EINVAL; + errno = EINVAL + 8197; if (dir) { SAFE_FREE(dir->fname); SAFE_FREE(dir); @@ -2246,12 +2207,54 @@ } +static void smbc_readdir_internal(SMBCCTX * context, + struct smbc_dirent *dest, + struct smbc_dirent *src, + int max_namebuf_len) +{ + if (context->options.urlencode_readdir_entries) { + + /* url-encode the name. get back remaining buffer space */ + max_namebuf_len = + smbc_urlencode(dest->name, src->name, max_namebuf_len); + + /* We now know the name length */ + dest->namelen = strlen(dest->name); + + /* Save the pointer to the beginning of the comment */ + dest->comment = dest->name + dest->namelen + 1; + + /* Copy the comment */ + strncpy(dest->comment, src->comment, max_namebuf_len); + + /* Ensure the comment is null terminated */ + if (max_namebuf_len > src->commentlen) { + dest->comment[src->commentlen] = '\0'; + } else { + dest->comment[max_namebuf_len - 1] = '\0'; + } + + /* Save other fields */ + dest->smbc_type = src->smbc_type; + dest->commentlen = strlen(dest->comment); + dest->dirlen = ((dest->comment + dest->commentlen + 1) - + (char *) dest); + } else { + + /* No encoding. Just copy the entry as is. */ + memcpy(dest, src, src->dirlen); + dest->comment = (char *)(&dest->name + src->namelen + 1); + } + +} + /* * Routine to get a directory entry */ struct smbc_dirent *smbc_readdir_ctx(SMBCCTX *context, SMBCFILE *dir) { + int maxlen; struct smbc_dirent *dirp, *dirent; /* Check that all is ok first ... */ @@ -2284,37 +2287,40 @@ if (!dir->dir_next) { return NULL; } - else { - dirent = dir->dir_next->dirent; - if (!dirent) { + dirent = dir->dir_next->dirent; + if (!dirent) { - errno = ENOENT; - return NULL; + errno = ENOENT; + return NULL; - } + } - /* Hmmm, do I even need to copy it? */ + dirp = (struct smbc_dirent *)context->internal->_dirent; + maxlen = (sizeof(context->internal->_dirent) - + sizeof(struct smbc_dirent)); - memcpy(context->internal->_dirent, dirent, dirent->dirlen); /* Copy the dirent */ - dirp = (struct smbc_dirent *)context->internal->_dirent; - dirp->comment = (char *)(&dirp->name + dirent->namelen + 1); - dir->dir_next = dir->dir_next->next; + smbc_readdir_internal(context, dirp, dirent, maxlen); - return (struct smbc_dirent *)context->internal->_dirent; - } + dir->dir_next = dir->dir_next->next; + return dirp; } /* * Routine to get directory entries */ -static int smbc_getdents_ctx(SMBCCTX *context, SMBCFILE *dir, struct smbc_dirent *dirp, int count) +static int smbc_getdents_ctx(SMBCCTX *context, + SMBCFILE *dir, + struct smbc_dirent *dirp, + int count) { + int rem = count; + int reqd; + int maxlen; + char *ndir = (char *)dirp; struct smbc_dir_list *dirlist; - int rem = count, reqd; - char *ndir = (char *)dirp; /* Check that all is ok first ... */ @@ -2356,9 +2362,16 @@ } - if (rem < (reqd = (sizeof(struct smbc_dirent) + dirlist->dirent->namelen + - dirlist->dirent->commentlen + 1))) { + /* Do urlencoding of next entry, if so selected */ + dirent = (struct smbc_dirent *)context->internal->_dirent; + maxlen = (sizeof(context->internal->_dirent) - + sizeof(struct smbc_dirent)); + smbc_readdir_internal(context, dirent, dirlist->dirent, maxlen); + reqd = dirent->dirlen; + + if (rem < reqd) { + if (rem < count) { /* We managed to copy something */ errno = 0; @@ -2374,12 +2387,12 @@ } - dirent = dirlist->dirent; - memcpy(ndir, dirent, reqd); /* Copy the data in ... */ ((struct smbc_dirent *)ndir)->comment = - (char *)(&((struct smbc_dirent *)ndir)->name + dirent->namelen + 1); + (char *)(&((struct smbc_dirent *)ndir)->name + + dirent->namelen + + 1); ndir += reqd; @@ -2445,27 +2458,6 @@ } - /* if (strncmp(srv->cli.dev, "IPC", 3) == 0) { - - mode = aDIR | aRONLY; - - } - else if (strncmp(srv->cli.dev, "LPT", 3) == 0) { - - if (strcmp(path, "\\") == 0) { - - mode = aDIR | aRONLY; - - } - else { - - mode = aRONLY; - smbc_stat_printjob(srv, path, &size, &m_time); - c_time = a_time = m_time; - - } - else { */ - if (!cli_mkdir(&srv->cli, path)) { errno = smbc_errno(context, &srv->cli); @@ -3230,10 +3222,93 @@ } +/* Obtain the current dos attributes */ +static DOS_ATTR_DESC *dos_attr_query(SMBCCTX *context, + TALLOC_CTX *ctx, + const char *filename, + SMBCSRV *srv) +{ + time_t m_time = 0, a_time = 0, c_time = 0; + size_t size = 0; + uint16 mode = 0; + SMB_INO_T inode = 0; + DOS_ATTR_DESC *ret; + + ret = talloc(ctx, sizeof(DOS_ATTR_DESC)); + if (!ret) { + errno = ENOMEM; + return NULL; + } + + /* Obtain the DOS attributes */ + if (!smbc_getatr(context, srv, (char *) filename, &mode, &size, + &c_time, &a_time, &m_time, &inode)) { + + errno = smbc_errno(context, &srv->cli); + DEBUG(5, ("dos_attr_query Failed to query old attributes\n")); + return NULL; + + } + + ret->mode = mode; + ret->size = size; + ret->a_time = a_time; + ret->c_time = c_time; + ret->m_time = m_time; + ret->inode = inode; + + return ret; +} + + +/* parse a ascii version of a security descriptor */ +static void dos_attr_parse(SMBCCTX *context, + DOS_ATTR_DESC *dad, + SMBCSRV *srv, + char *str) +{ + const char *p = str; + fstring tok; + + while (next_token(&p, tok, "\t,\r\n", sizeof(tok))) { + + if (StrnCaseCmp(tok, "MODE:", 5) == 0) { + dad->mode = strtol(tok+5, NULL, 16); + continue; + } + + if (StrnCaseCmp(tok, "SIZE:", 5) == 0) { + dad->size = strtoll(tok+5, NULL, 10); + continue; + } + + if (StrnCaseCmp(tok, "A_TIME:", 7) == 0) { + dad->a_time = strtoll(tok+7, NULL, 10); + continue; + } + + if (StrnCaseCmp(tok, "C_TIME:", 7) == 0) { + dad->c_time = strtoll(tok+7, NULL, 10); + continue; + } + + if (StrnCaseCmp(tok, "M_TIME:", 7) == 0) { + dad->m_time = strtoll(tok+7, NULL, 10); + continue; + } + + if (StrnCaseCmp(tok, "INODE:", 6) == 0) { + dad->inode = strtoll(tok+6, NULL, 10); + continue; + } + } +} + + /***************************************************** retrieve the acls for a file *******************************************************/ -static int cacl_get(TALLOC_CTX *ctx, struct cli_state *cli, +static int cacl_get(SMBCCTX *context, TALLOC_CTX *ctx, SMBCSRV *srv, struct cli_state *ipc_cli, POLICY_HND *pol, char *filename, char *name, char *buf, int bufsize) { @@ -3241,166 +3316,446 @@ int n = 0; int n_used; BOOL all; + BOOL all_nt; + BOOL all_dos; + BOOL some_nt; + BOOL some_dos; BOOL numeric = True; BOOL determine_size = (bufsize == 0); int fnum = -1; SEC_DESC *sd; fstring sidstr; char *p; + time_t m_time = 0, a_time = 0, c_time = 0; + size_t size = 0; + uint16 mode = 0; + SMB_INO_T ino = 0; + struct cli_state *cli = &srv->cli; - fnum = cli_nt_create(cli, filename, CREATE_ACCESS_READ); + all = (StrnCaseCmp(name, "system.*", 8) == 0); + all_nt = (StrnCaseCmp(name, "system.nt_sec_desc.*", 20) == 0); + all_dos = (StrnCaseCmp(name, "system.dos_attr.*", 17) == 0); + some_nt = (StrnCaseCmp(name, "system.nt_sec_desc.", 19) == 0); + some_dos = (StrnCaseCmp(name, "system.dos_attr.", 16) == 0); + numeric = (* (name + strlen(name) - 1) != '+'); - if (fnum == -1) { - DEBUG(5, ("cacl_get failed to open %s: %s\n", - filename, cli_errstr(cli))); - errno = 0; - return -1; - } + n_used = 0; - sd = cli_query_secdesc(cli, fnum, ctx); + /* + * If we are (possibly) talking to an NT or new system and some NT + * attributes have been requested... + */ + if (ipc_cli && (all || some_nt)) { + /* Point to the portion after "system.nt_sec_desc." */ + name += 19; /* if (all) this will be invalid but unused */ - if (!sd) { - DEBUG(5, ("cacl_get Failed to query old descriptor\n")); - errno = 0; - return -1; - } + /* ... then obtain any NT attributes which were requested */ + fnum = cli_nt_create(cli, filename, CREATE_ACCESS_READ); - cli_close(cli, fnum); + if (fnum == -1) { + DEBUG(5, ("cacl_get failed to open %s: %s\n", + filename, cli_errstr(cli))); + errno = 0; + return -1; + } - all = (*name == '*'); - numeric = (* (name + strlen(name) - 1) != '+'); + sd = cli_query_secdesc(cli, fnum, ctx); - n_used = 0; + if (!sd) { + DEBUG(5, + ("cacl_get Failed to query old descriptor\n")); + errno = 0; + return -1; + } - if (all) { - if (determine_size) { - p = talloc_asprintf(ctx, - "REVISION:%d", sd->revision); - if (!p) { - errno = ENOMEM; - return -1; + cli_close(cli, fnum); + + if (all || all_nt) { + if (determine_size) { + p = talloc_asprintf(ctx, + "REVISION:%d", + sd->revision); + if (!p) { + errno = ENOMEM; + return -1; + } + n = strlen(p); + } else { + n = snprintf(buf, bufsize, + "REVISION:%d", sd->revision); } - n = strlen(p); + } else if (StrCaseCmp(name, "revision") == 0) { + if (determine_size) { + p = talloc_asprintf(ctx, "%d", sd->revision); + if (!p) { + errno = ENOMEM; + return -1; + } + n = strlen(p); + } else { + n = snprintf(buf, bufsize, "%d", sd->revision); + } + } + + if (!determine_size && n > bufsize) { + errno = ERANGE; + return -1; + } + buf += n; + n_used += n; + bufsize -= n; + + /* Get owner and group sid */ + + if (sd->owner_sid) { + convert_sid_to_string(ipc_cli, pol, + sidstr, numeric, sd->owner_sid); } else { - n = snprintf(buf, bufsize, - "REVISION:%d", sd->revision); + fstrcpy(sidstr, ""); } - } else if (StrCaseCmp(name, "revision") == 0) { - if (determine_size) { - p = talloc_asprintf(ctx, "%d", sd->revision); - if (!p) { - errno = ENOMEM; - return -1; + + if (all || all_nt) { + if (determine_size) { + p = talloc_asprintf(ctx, ",OWNER:%s", sidstr); + if (!p) { + errno = ENOMEM; + return -1; + } + n = strlen(p); + } else { + n = snprintf(buf, bufsize, + ",OWNER:%s", sidstr); } - n = strlen(p); + } else if (StrnCaseCmp(name, "owner", 5) == 0) { + if (determine_size) { + p = talloc_asprintf(ctx, "%s", sidstr); + if (!p) { + errno = ENOMEM; + return -1; + } + n = strlen(p); + } else { + n = snprintf(buf, bufsize, "%s", sidstr); + } + } + + if (!determine_size && n > bufsize) { + errno = ERANGE; + return -1; + } + buf += n; + n_used += n; + bufsize -= n; + + if (sd->grp_sid) { + convert_sid_to_string(ipc_cli, pol, + sidstr, numeric, sd->grp_sid); } else { - n = snprintf(buf, bufsize, "%d", sd->revision); + fstrcpy(sidstr, ""); } - } - - if (!determine_size && n > bufsize) { - errno = ERANGE; - return -1; - } - buf += n; - n_used += n; - bufsize -= n; - /* Get owner and group sid */ + if (all || all_nt) { + if (determine_size) { + p = talloc_asprintf(ctx, ",GROUP:%s", sidstr); + if (!p) { + errno = ENOMEM; + return -1; + } + n = strlen(p); + } else { + n = snprintf(buf, bufsize, + ",GROUP:%s", sidstr); + } + } else if (StrnCaseCmp(name, "group", 5) == 0) { + if (determine_size) { + p = talloc_asprintf(ctx, "%s", sidstr); + if (!p) { + errno = ENOMEM; + return -1; + } + n = strlen(p); + } else { + n = snprintf(buf, bufsize, "%s", sidstr); + } + } - if (sd->owner_sid) { - convert_sid_to_string(ipc_cli, pol, - sidstr, numeric, sd->owner_sid); - } else { - fstrcpy(sidstr, ""); - } + if (!determine_size && n > bufsize) { + errno = ERANGE; + return -1; + } + buf += n; + n_used += n; + bufsize -= n; - if (all) { - if (determine_size) { - p = talloc_asprintf(ctx, ",OWNER:%s", sidstr); - if (!p) { - errno = ENOMEM; - return -1; + /* Add aces to value buffer */ + for (i = 0; sd->dacl && i < sd->dacl->num_aces; i++) { + + SEC_ACE *ace = &sd->dacl->ace[i]; + convert_sid_to_string(ipc_cli, pol, + sidstr, numeric, &ace->trustee); + + if (all || all_nt) { + if (determine_size) { + p = talloc_asprintf(ctx, + ",ACL:" + "%s:%d/%d/0x%08x", + sidstr, + ace->type, + ace->flags, + ace->info.mask); + if (!p) { + errno = ENOMEM; + return -1; + } + n = strlen(p); + } else { + n = snprintf(buf, bufsize, + ",ACL:%s:%d/%d/0x%08x", + sidstr, + ace->type, + ace->flags, + ace->info.mask); + } + } else if ((StrnCaseCmp(name, "acl", 3) == 0 && + StrCaseCmp(name + 3, sidstr) == 0) || + (StrnCaseCmp(name, "acl+", 4) == 0 && + StrCaseCmp(name + 4, sidstr) == 0)) { + if (determine_size) { + p = talloc_asprintf(ctx, + "%d/%d/0x%08x", + ace->type, + ace->flags, + ace->info.mask); + if (!p) { + errno = ENOMEM; + return -1; + } + n = strlen(p); + } else { + n = snprintf(buf, bufsize, + "%d/%d/0x%08x", + ace->type, + ace->flags, + ace->info.mask); + } } - n = strlen(p); - } else { - n = snprintf(buf, bufsize, ",OWNER:%s", sidstr); - } - } else if (StrnCaseCmp(name, "owner", 5) == 0) { - if (determine_size) { - p = talloc_asprintf(ctx, "%s", sidstr); - if (!p) { - errno = ENOMEM; + if (n > bufsize) { + errno = ERANGE; return -1; } - n = strlen(p); - } else { - n = snprintf(buf, bufsize, "%s", sidstr); + buf += n; + n_used += n; + bufsize -= n; } - } - if (!determine_size && n > bufsize) { - errno = ERANGE; - return -1; + /* Restore name pointer to its original value */ + name -= 19; } - buf += n; - n_used += n; - bufsize -= n; - if (sd->grp_sid) { - convert_sid_to_string(ipc_cli, pol, - sidstr, numeric, sd->grp_sid); - } else { - fstrcpy(sidstr, ""); - } + if (all || some_dos) { + /* Point to the portion after "system.dos_attr." */ + name += 16; /* if (all) this will be invalid but unused */ - if (all) { - if (determine_size) { - p = talloc_asprintf(ctx, ",GROUP:%s", sidstr); - if (!p) { - errno = ENOMEM; - return -1; + /* Obtain the DOS attributes */ + if (!smbc_getatr(context, srv, filename, &mode, &size, + &c_time, &a_time, &m_time, &ino)) { + + errno = smbc_errno(context, &srv->cli); + return -1; + + } + + if (all || all_dos) { + if (determine_size) { + p = talloc_asprintf(ctx, + "%sMODE:0x%x", + (ipc_cli && + (all || some_nt) + ? "," + : ""), + mode); + if (!p) { + errno = ENOMEM; + return -1; + } + n = strlen(p); + } else { + n = snprintf(buf, bufsize, + "%sMODE:0x%x", + (ipc_cli && + (all || some_nt) + ? "," + : ""), + mode); } - n = strlen(p); - } else { - n = snprintf(buf, bufsize, ",GROUP:%s", sidstr); + } else if (StrCaseCmp(name, "mode") == 0) { + if (determine_size) { + p = talloc_asprintf(ctx, "0x%x", mode); + if (!p) { + errno = ENOMEM; + return -1; + } + n = strlen(p); + } else { + n = snprintf(buf, bufsize, "0x%x", mode); + } } - } else if (StrnCaseCmp(name, "group", 5) == 0) { - if (determine_size) { - p = talloc_asprintf(ctx, "%s", sidstr); - if (!p) { - errno = ENOMEM; - return -1; + + if (!determine_size && n > bufsize) { + errno = ERANGE; + return -1; + } + buf += n; + n_used += n; + bufsize -= n; + + if (all || all_dos) { + if (determine_size) { + p = talloc_asprintf(ctx, + ",SIZE:%llu", + (unsigned long long) size); + if (!p) { + errno = ENOMEM; + return -1; + } + n = strlen(p); + } else { + n = snprintf(buf, bufsize, + ",SIZE:%llu", + (unsigned long long) size); } - n = strlen(p); - } else { - n = snprintf(buf, bufsize, "%s", sidstr); + } else if (StrCaseCmp(name, "size") == 0) { + if (determine_size) { + p = talloc_asprintf(ctx, + "%llu", + (unsigned long long) size); + if (!p) { + errno = ENOMEM; + return -1; + } + n = strlen(p); + } else { + n = snprintf(buf, bufsize, + "%llu", + (unsigned long long) size); + } } - } + + if (!determine_size && n > bufsize) { + errno = ERANGE; + return -1; + } + buf += n; + n_used += n; + bufsize -= n; - if (!determine_size && n > bufsize) { - errno = ERANGE; - return -1; - } - buf += n; - n_used += n; - bufsize -= n; + if (all || all_dos) { + if (determine_size) { + p = talloc_asprintf(ctx, + ",C_TIME:%lu", c_time); + if (!p) { + errno = ENOMEM; + return -1; + } + n = strlen(p); + } else { + n = snprintf(buf, bufsize, + ",C_TIME:%lu", c_time); + } + } else if (StrCaseCmp(name, "c_time") == 0) { + if (determine_size) { + p = talloc_asprintf(ctx, "%lu", c_time); + if (!p) { + errno = ENOMEM; + return -1; + } + n = strlen(p); + } else { + n = snprintf(buf, bufsize, "%lu", c_time); + } + } + + if (!determine_size && n > bufsize) { + errno = ERANGE; + return -1; + } + buf += n; + n_used += n; + bufsize -= n; - /* Add aces to value buffer */ - for (i = 0; sd->dacl && i < sd->dacl->num_aces; i++) { + if (all || all_dos) { + if (determine_size) { + p = talloc_asprintf(ctx, + ",A_TIME:%lu", a_time); + if (!p) { + errno = ENOMEM; + return -1; + } + n = strlen(p); + } else { + n = snprintf(buf, bufsize, + ",A_TIME:%lu", a_time); + } + } else if (StrCaseCmp(name, "a_time") == 0) { + if (determine_size) { + p = talloc_asprintf(ctx, "%lu", a_time); + if (!p) { + errno = ENOMEM; + return -1; + } + n = strlen(p); + } else { + n = snprintf(buf, bufsize, "%lu", a_time); + } + } + + if (!determine_size && n > bufsize) { + errno = ERANGE; + return -1; + } + buf += n; + n_used += n; + bufsize -= n; - SEC_ACE *ace = &sd->dacl->ace[i]; - convert_sid_to_string(ipc_cli, pol, - sidstr, numeric, &ace->trustee); + if (all || all_dos) { + if (determine_size) { + p = talloc_asprintf(ctx, + ",M_TIME:%lu", m_time); + if (!p) { + errno = ENOMEM; + return -1; + } + n = strlen(p); + } else { + n = snprintf(buf, bufsize, + ",M_TIME:%lu", m_time); + } + } else if (StrCaseCmp(name, "m_time") == 0) { + if (determine_size) { + p = talloc_asprintf(ctx, "%lu", m_time); + if (!p) { + errno = ENOMEM; + return -1; + } + n = strlen(p); + } else { + n = snprintf(buf, bufsize, "%lu", m_time); + } + } + + if (!determine_size && n > bufsize) { + errno = ERANGE; + return -1; + } + buf += n; + n_used += n; + bufsize -= n; - if (all) { + if (all || all_dos) { if (determine_size) { - p = talloc_asprintf(ctx, - ",ACL:%s:%d/%d/0x%08x", - sidstr, - ace->type, - ace->flags, - ace->info.mask); + p = talloc_asprintf(ctx, + ",INODE:%llu", + (unsigned long long) ino); if (!p) { errno = ENOMEM; return -1; @@ -3408,22 +3763,14 @@ n = strlen(p); } else { n = snprintf(buf, bufsize, - ",ACL:%s:%d/%d/0x%08x", - sidstr, - ace->type, - ace->flags, - ace->info.mask); + ",INODE:%llu", + (unsigned long long) ino); } - } else if ((StrnCaseCmp(name, "acl", 3) == 0 && - StrCaseCmp(name + 3, sidstr) == 0) || - (StrnCaseCmp(name, "acl+", 4) == 0 && - StrCaseCmp(name + 4, sidstr) == 0)) { + } else if (StrCaseCmp(name, "inode") == 0) { if (determine_size) { - p = talloc_asprintf(ctx, - "%d/%d/0x%08x", - ace->type, - ace->flags, - ace->info.mask); + p = talloc_asprintf(ctx, + "%llu", + (unsigned long long) ino); if (!p) { errno = ENOMEM; return -1; @@ -3431,23 +3778,28 @@ n = strlen(p); } else { n = snprintf(buf, bufsize, - "%d/%d/0x%08x", - ace->type, ace->flags, ace->info.mask); + "%llu", + (unsigned long long) ino); } } - if (n > bufsize) { + + if (!determine_size && n > bufsize) { errno = ERANGE; return -1; } buf += n; n_used += n; bufsize -= n; - } + /* Restore name pointer to its original value */ + name -= 16; + } + if (n_used == 0) { errno = ENOATTR; return -1; } + return n_used; } @@ -3645,12 +3997,14 @@ int flags) { int ret; + int ret2; SMBCSRV *srv; SMBCSRV *ipc_srv; fstring server, share, user, password, workgroup; pstring path; TALLOC_CTX *ctx; POLICY_HND pol; + DOS_ATTR_DESC *dad; if (!context || !context->internal || !context->internal->_initialized) { @@ -3690,11 +4044,13 @@ return -1; /* errno set by smbc_server */ } - ipc_srv = smbc_attr_server(context, server, share, - workgroup, user, password, - &pol); - if (!ipc_srv) { - return -1; + if (! srv->no_nt_session) { + ipc_srv = smbc_attr_server(context, server, share, + workgroup, user, password, + &pol); + srv->no_nt_session = True; + } else { + ipc_srv = NULL; } ctx = talloc_init("smbc_setxattr"); @@ -3704,6 +4060,71 @@ } /* + * Are they asking to set the entire set of known attributes? + */ + if (StrCaseCmp(name, "system.*") == 0 || + StrCaseCmp(name, "system.*+") == 0) { + /* Yup. */ + char *namevalue = + talloc_asprintf(ctx, "%s:%s", name+7, (char *) value); + if (! namevalue) { + errno = ENOMEM; + ret = -1; + return -1; + } + + if (ipc_srv) { + ret = cacl_set(ctx, &srv->cli, + &ipc_srv->cli, &pol, path, + namevalue, + (*namevalue == '*' + ? SMBC_XATTR_MODE_SET + : SMBC_XATTR_MODE_ADD), + flags); + } else { + ret = 0; + } + + /* get a DOS Attribute Descriptor with current attributes */ + dad = dos_attr_query(context, ctx, path, srv); + if (dad) { + /* Overwrite old with new, using what was provided */ + dos_attr_parse(context, dad, srv, namevalue); + + /* Set the new DOS attributes */ +#warning "Should try Set Path Info first, but it's not yet implemented" +#if 0 /* not yet implemented */ + if (! cli_setpathinfo(&srv->cli, path, + dad->c_time, + dad->a_time, + dad->m_time, + dad->mode)) { + if (!cli_setatr(&srv->cli, path, + dad->mode, dad->m_time)) { + errno = smbc_errno(context, &srv->cli); + } + } +#else + if (!cli_setatr(&srv->cli, path, + dad->mode, dad->m_time)) { + errno = smbc_errno(context, &srv->cli); + } +#endif + } + + /* we only fail if both NT and DOS sets failed */ + if (ret < 0 && ! dad) { + ret = -1; /* in case dad was null */ + } + else { + ret = 0; + } + + talloc_destroy(ctx); + return ret; + } + + /* * Are they asking to set an access control element or to set * the entire access control list? */ @@ -3716,7 +4137,11 @@ /* Yup. */ char *namevalue = talloc_asprintf(ctx, "%s:%s", name+19, (char *) value); - if (! namevalue) { + + if (! ipc_srv) { + ret = -1; /* errno set by smbc_server() */ + } + else if (! namevalue) { errno = ENOMEM; ret = -1; } else { @@ -3741,7 +4166,12 @@ /* Yup. */ char *namevalue = talloc_asprintf(ctx, "%s:%s", name+19, (char *) value); - if (! namevalue) { + + if (! ipc_srv) { + + ret = -1; /* errno set by smbc_server() */ + } + else if (! namevalue) { errno = ENOMEM; ret = -1; } else { @@ -3762,7 +4192,12 @@ /* Yup. */ char *namevalue = talloc_asprintf(ctx, "%s:%s", name+19, (char *) value); - if (! namevalue) { + + if (! ipc_srv) { + /* errno set by smbc_server() */ + ret = -1; + } + else if (! namevalue) { errno = ENOMEM; ret = -1; } else { @@ -3774,6 +4209,67 @@ return ret; } + /* + * Are they asking to set a DOS attribute? + */ + if (StrCaseCmp(name, "system.dos_attr.*") == 0 || + StrCaseCmp(name, "system.dos_attr.mode") == 0 || + StrCaseCmp(name, "system.dos_attr.c_time") == 0 || + StrCaseCmp(name, "system.dos_attr.a_time") == 0 || + StrCaseCmp(name, "system.dos_attr.m_time") == 0) { + + /* get a DOS Attribute Descriptor with current attributes */ + dad = dos_attr_query(context, ctx, path, srv); + if (dad) { + char *namevalue = + talloc_asprintf(ctx, "%s:%s", name+16, (char *) value); + if (! namevalue) { + errno = ENOMEM; + ret = -1; + } else { + /* Overwrite old with provided new params */ + dos_attr_parse(context, dad, srv, namevalue); + + /* Set the new DOS attributes */ +#warning "Should try Set Path Info first, but it's not yet implemented" +#if 0 /* not yet implemented */ + ret2 = cli_setpathinfo(&srv->cli, path, + dad->c_time, + dad->a_time, + dad->m_time, + dad->mode); + if (! ret2) { + ret2 = cli_setatr(&srv->cli, path, + dad->mode, + dad->m_time); + if (! ret2) { + errno = smbc_errno(context, + &srv->cli); + } + } +#else + ret2 = cli_setatr(&srv->cli, path, + dad->mode, dad->m_time); + if (! ret2) { + errno = smbc_errno(context, &srv->cli); + } +#endif + + /* ret2 has True (success) / False (failure) */ + if (ret2) { + ret = 0; + } else { + ret = -1; + } + } + } else { + ret = -1; + } + + talloc_destroy(ctx); + return ret; + } + /* Unsupported attribute name */ talloc_destroy(ctx); errno = EINVAL; @@ -3794,6 +4290,7 @@ TALLOC_CTX *ctx; POLICY_HND pol; + if (!context || !context->internal || !context->internal->_initialized) { @@ -3831,11 +4328,15 @@ return -1; /* errno set by smbc_server */ } - ipc_srv = smbc_attr_server(context, server, share, - workgroup, user, password, - &pol); - if (!ipc_srv) { - return -1; + if (! srv->no_nt_session) { + ipc_srv = smbc_attr_server(context, server, share, + workgroup, user, password, + &pol); + if (! ipc_srv) { + srv->no_nt_session = True; + } + } else { + ipc_srv = NULL; } ctx = talloc_init("smbc:getxattr"); @@ -3845,7 +4346,9 @@ } /* Are they requesting a supported attribute? */ - if (StrCaseCmp(name, "system.nt_sec_desc.*") == 0 || + if (StrCaseCmp(name, "system.*") == 0 || + StrCaseCmp(name, "system.*+") == 0 || + StrCaseCmp(name, "system.nt_sec_desc.*") == 0 || StrCaseCmp(name, "system.nt_sec_desc.*+") == 0 || StrCaseCmp(name, "system.nt_sec_desc.revision") == 0 || StrCaseCmp(name, "system.nt_sec_desc.owner") == 0 || @@ -3853,12 +4356,20 @@ StrCaseCmp(name, "system.nt_sec_desc.group") == 0 || StrCaseCmp(name, "system.nt_sec_desc.group+") == 0 || StrnCaseCmp(name, "system.nt_sec_desc.acl", 22) == 0 || - StrnCaseCmp(name, "system.nt_sec_desc.acl+", 23) == 0) { + StrnCaseCmp(name, "system.nt_sec_desc.acl+", 23) == 0 || + StrCaseCmp(name, "system.dos_attr.*") == 0 || + StrCaseCmp(name, "system.dos_attr.mode") == 0 || + StrCaseCmp(name, "system.dos_attr.size") == 0 || + StrCaseCmp(name, "system.dos_attr.c_time") == 0 || + StrCaseCmp(name, "system.dos_attr.a_time") == 0 || + StrCaseCmp(name, "system.dos_attr.m_time") == 0 || + StrCaseCmp(name, "system.dos_attr.inode") == 0) { /* Yup. */ - ret = cacl_get(ctx, &srv->cli, - &ipc_srv->cli, &pol, - (char *) path, (char *) name + 19, + ret = cacl_get(context, ctx, srv, + ipc_srv == NULL ? NULL : &ipc_srv->cli, + &pol, + (char *) path, (char *) name, (char *) value, size); if (ret < 0 && errno == 0) { errno = smbc_errno(context, &srv->cli); @@ -3923,20 +4434,19 @@ return -1; /* errno set by smbc_server */ } - ipc_srv = smbc_attr_server(context, server, share, - workgroup, user, password, - &pol); - if (!ipc_srv) { - return -1; + if (! srv->no_nt_session) { + ipc_srv = smbc_attr_server(context, server, share, + workgroup, user, password, + &pol); + srv->no_nt_session = True; + } else { + ipc_srv = NULL; } - ipc_srv = smbc_attr_server(context, server, share, - workgroup, user, password, - &pol); - if (!ipc_srv) { - return -1; + if (! ipc_srv) { + return -1; /* errno set by smbc_attr_server */ } - + ctx = talloc_init("smbc_removexattr"); if (!ctx) { errno = ENOMEM; @@ -3988,10 +4498,12 @@ { /* * This isn't quite what listxattr() is supposed to do. This returns - * the complete set of attributes, always, rather than only those + * the complete set of attribute names, always, rather than only those * attribute names which actually exist for a file. Hmmm... */ const char supported[] = + "system.*\0" + "system.*+\0" "system.nt_sec_desc.revision\0" "system.nt_sec_desc.owner\0" "system.nt_sec_desc.owner+\0" @@ -4001,6 +4513,11 @@ "system.nt_sec_desc.acl+\0" "system.nt_sec_desc.*\0" "system.nt_sec_desc.*+\0" + "system.dos_attr.*\0" + "system.dos_attr.mode\0" + "system.dos_attr.c_time\0" + "system.dos_attr.a_time\0" + "system.dos_attr.m_time\0" ; if (size == 0) { @@ -4294,37 +4811,41 @@ context->debug = 0; context->timeout = 20000; /* 20 seconds */ - context->open = smbc_open_ctx; - context->creat = smbc_creat_ctx; - context->read = smbc_read_ctx; - context->write = smbc_write_ctx; - context->close = smbc_close_ctx; - context->unlink = smbc_unlink_ctx; - context->rename = smbc_rename_ctx; - context->lseek = smbc_lseek_ctx; - context->stat = smbc_stat_ctx; - context->fstat = smbc_fstat_ctx; - context->opendir = smbc_opendir_ctx; - context->closedir = smbc_closedir_ctx; - context->readdir = smbc_readdir_ctx; - context->getdents = smbc_getdents_ctx; - context->mkdir = smbc_mkdir_ctx; - context->rmdir = smbc_rmdir_ctx; - context->telldir = smbc_telldir_ctx; - context->lseekdir = smbc_lseekdir_ctx; - context->fstatdir = smbc_fstatdir_ctx; - context->chmod = smbc_chmod_ctx; - context->utimes = smbc_utimes_ctx; - context->setxattr = smbc_setxattr_ctx; - context->getxattr = smbc_getxattr_ctx; - context->removexattr = smbc_removexattr_ctx; - context->listxattr = smbc_listxattr_ctx; - context->open_print_job = smbc_open_print_job_ctx; - context->print_file = smbc_print_file_ctx; - context->list_print_jobs = smbc_list_print_jobs_ctx; - context->unlink_print_job = smbc_unlink_print_job_ctx; + context->options.browse_max_lmb_count = 3; /* # LMBs to query */ + context->options.urlencode_readdir_entries = False;/* backward compat */ + context->options.one_share_per_server = False;/* backward compat */ - context->callbacks.check_server_fn = smbc_check_server; + context->open = smbc_open_ctx; + context->creat = smbc_creat_ctx; + context->read = smbc_read_ctx; + context->write = smbc_write_ctx; + context->close = smbc_close_ctx; + context->unlink = smbc_unlink_ctx; + context->rename = smbc_rename_ctx; + context->lseek = smbc_lseek_ctx; + context->stat = smbc_stat_ctx; + context->fstat = smbc_fstat_ctx; + context->opendir = smbc_opendir_ctx; + context->closedir = smbc_closedir_ctx; + context->readdir = smbc_readdir_ctx; + context->getdents = smbc_getdents_ctx; + context->mkdir = smbc_mkdir_ctx; + context->rmdir = smbc_rmdir_ctx; + context->telldir = smbc_telldir_ctx; + context->lseekdir = smbc_lseekdir_ctx; + context->fstatdir = smbc_fstatdir_ctx; + context->chmod = smbc_chmod_ctx; + context->utimes = smbc_utimes_ctx; + context->setxattr = smbc_setxattr_ctx; + context->getxattr = smbc_getxattr_ctx; + context->removexattr = smbc_removexattr_ctx; + context->listxattr = smbc_listxattr_ctx; + context->open_print_job = smbc_open_print_job_ctx; + context->print_file = smbc_print_file_ctx; + context->list_print_jobs = smbc_list_print_jobs_ctx; + context->unlink_print_job = smbc_unlink_print_job_ctx; + + context->callbacks.check_server_fn = smbc_check_server; context->callbacks.remove_unused_server_fn = smbc_remove_unused_server; smbc_default_cache_functions(context); @@ -4540,3 +5061,11 @@ return context; } + + +/* Return the verion of samba, and thus libsmbclient */ +const char * +smbc_version(void) +{ + return samba_version_string(); +} Index: libsmb/cliconnect.c =================================================================== --- libsmb/cliconnect.c (revision 4510) +++ libsmb/cliconnect.c (working copy) @@ -989,7 +989,12 @@ if (!cli_receive_smb(cli)) return False; - return !cli_is_error(cli); + if (cli_is_error(cli)) { + return False; + } + + cli->cnum = -1; + return True; } /**************************************************************************** @@ -1292,12 +1297,12 @@ if (getenv("LIBSMB_PROG")) { cli->fd = sock_exec(getenv("LIBSMB_PROG")); } else { - /* try 445 first, then 139 */ - int port = cli->port?cli->port:445; + /* try 139 first, then 445, so browse lists work */ + int port = cli->port?cli->port:139; cli->fd = open_socket_out(SOCK_STREAM, &cli->dest_ip, port, cli->timeout); if (cli->fd == -1 && cli->port == 0) { - port = 139; + port = 445; cli->fd = open_socket_out(SOCK_STREAM, &cli->dest_ip, port, cli->timeout); } Index: libsmb/libsmb_cache.c =================================================================== --- libsmb/libsmb_cache.c (revision 4510) +++ libsmb/libsmb_cache.c (working copy) @@ -1,3 +1,4 @@ + /* Unix SMB/CIFS implementation. SMB client library implementation (server cache) @@ -23,12 +24,8 @@ #include "includes.h" -/* - * Define this to get the real SMBCFILE and SMBCSRV structures - */ -#define _SMBC_INTERNAL #include "include/libsmbclient.h" - +#include "../include/libsmb_internal.h" /* * Structure we use if internal caching mechanism is used * nothing fancy here. @@ -115,11 +112,53 @@ /* Search the cache lines */ for (srv=((struct smbc_server_cache *)context->server_cache);srv;srv=srv->next) { + if (strcmp(server,srv->server_name) == 0 && - strcmp(share,srv->share_name) == 0 && strcmp(workgroup,srv->workgroup) == 0 && - strcmp(user, srv->username) == 0) - return srv->server; + strcmp(user, srv->username) == 0) { + + /* If the share name matches, we're cool */ + if (strcmp(share, srv->share_name) == 0) { + return srv->server; + } + + /* + * We only return an empty share name or the attribute + * server on an exact match (which would have been + * caught above). + */ + if (*share == '\0' || strcmp(share, "*IPC$") == 0) + continue; + + /* + * Never return an empty share name or the attribute + * server if it wasn't what was requested. + */ + if (*srv->share_name == '\0' || + strcmp(srv->share_name, "*IPC$") == 0) + continue; + + /* + * If we're only allowing one share per server, then + * a connection to the server (other than the + * attribute server connection) is cool. + */ + if (context->options.one_share_per_server) { + /* + * The currently connected share name + * doesn't match the requested share, so + * disconnect from the current share. + */ + if (! cli_tdis(&srv->server->cli)) { + /* Sigh. Couldn't disconnect. */ + cli_shutdown(&srv->server->cli); + context->callbacks.remove_cached_srv_fn(context, srv->server); + continue; + } + + return srv->server; + } + } } return NULL; Index: include/libsmb_internal.h =================================================================== --- include/libsmb_internal.h (revision 4510) +++ include/libsmb_internal.h (working copy) @@ -13,6 +13,7 @@ struct cli_state cli; dev_t dev; BOOL no_pathinfo2; + BOOL no_nt_session; int server_fd; SMBCSRV *next, *prev; @@ -50,9 +51,18 @@ */ int _initialized; - /** INTERNAL: dirent pointer location - */ - char _dirent[512]; + /** INTERNAL: dirent pointer location + * + * Leave room for any urlencoded filename and the comment field. + * + * We really should use sizeof(struct smbc_dirent) plus (NAME_MAX * 3) + * plus whatever the max length of a comment is, plus a couple of null + * terminators (one after the filename, one after the comment). + * + * According to , NAME_MAX is 255. Is it longer + * anyplace else? + */ + char _dirent[1024]; /** INTERNAL: server connection list */ Index: include/libsmbclient.h =================================================================== --- include/libsmbclient.h (revision 4510) +++ include/libsmbclient.h (working copy) @@ -125,6 +125,19 @@ #define SMBC_XATTR_FLAG_REPLACE 0x2 /* fail if attr does not exist */ +/* + * Mappings of the DOS mode bits, as returned by smbc_getxattr() when the + * attribute name "system.dos_attr.mode" (or "system.dos_attr.*" or + * "system.*") is specified. + */ +#define SMBC_DOS_MODE_READONLY 0x01 +#define SMBC_DOS_MODE_HIDDEN 0x02 +#define SMBC_DOS_MODE_SYSTEM 0x04 +#define SMBC_DOS_MODE_VOLUME_ID 0x08 +#define SMBC_DOS_MODE_DIRECTORY 0x10 +#define SMBC_DOS_MODE_ARCHIVE 0x20 + + #ifndef ENOATTR # define ENOATTR ENOENT /* No such attribute */ #endif @@ -349,6 +362,67 @@ */ int timeout; + /** user options selections that apply to this session + */ + struct _smbc_options { + + /* + * From how many local master browsers should the list of + * workgroups be retrieved? It can take up to 12 minutes or + * longer after a server becomes a local master browser, for + * it to have the entire browse list (the list of + * workgroups/domains) from an entire network. Since a client + * never knows which local master browser will be found first, + * the one which is found first and used to retrieve a browse + * list may have an incomplete or empty browse list. By + * requesting the browse list from multiple local master + * browsers, a more complete list can be generated. For small + * networks (few workgroups), it is recommended that this + * value be set to 0, causing the browse lists from all found + * local master browsers to be retrieved and merged. For + * networks with many workgroups, a suitable value for this + * variable is probably somewhere around 3. (Default: 3). + */ + int browse_max_lmb_count; + + /* + * There is a difference in the desired return strings from + * smbc_readdir() depending upon whether the filenames are to + * be displayed to the user, or whether they are to be + * appended to the path name passed to smbc_opendir() to call + * a further smbc_ function (e.g. open the file with + * smbc_open()). In the former case, the filename should be + * in "human readable" form. In the latter case, the smbc_ + * functions expect a URL which must be url-encoded. Those + * functions decode the URL. If, for example, smbc_readdir() + * returned a file name of "abc%20def.txt", passing a path + * with this file name attached to smbc_open() would cause + * smbc_open to attempt to open the file "abc def.txt" since + * the %20 is decoded into a space. + * + * Set this option to True if the names returned by + * smbc_readdir() should be url-encoded such that they can be + * passed back to another smbc_ call. Set it to False if the + * names returned by smbc_readdir() are to be presented to the + * user. + * + * For backwards compatibility, this option defaults to False. + */ + int urlencode_readdir_entries; + + /* + * Some Windows versions appear to have a limit to the number + * of concurrent SESSIONs and/or TREE CONNECTions. In + * one-shot programs (i.e. the program runs and then quickly + * ends, thereby shutting down all connections), it is + * probably reasonable to establish a new connection for each + * share. In long-running applications, the limitation can be + * avoided by using only a single connection to each server, + * and issuing a new TREE CONNECT when the share is accessed. + */ + int one_share_per_server; + } options; + /** callable functions for files: * For usage and return values see the smbc_* functions */ @@ -458,7 +532,6 @@ }; - /**@ingroup misc * Create a new SBMCCTX (a context). * @@ -2179,4 +2252,75 @@ } #endif +/**@ingroup directory + * Convert strings of %xx to their single character equivalent. + * + * @param dest A pointer to a buffer in which the resulting decoded + * string should be placed. This may be a pointer to the + * same buffer as src_segment. + * + * @param src A pointer to the buffer containing the URL to be decoded. + * Any %xx sequences herein are converted to their single + * character equivalent. Each 'x' must be a valid hexadecimal + * digit, or that % sequence is left undecoded. + * + * @param max_dest_len + * The size of the buffer pointed to by dest_segment. + * + * @return The number of % sequences which could not be converted + * due to lack of two following hexadecimal digits. + */ +#ifdef __cplusplus +extern "C" { +#endif +int +smbc_urldecode(char *dest, char * src, size_t max_dest_len); +#ifdef __cplusplus +} +#endif + + +/* + * Convert any characters not specifically allowed in a URL into their %xx + * equivalent. + * + * @param dest A pointer to a buffer in which the resulting encoded + * string should be placed. Unlike smbc_urldecode(), this + * must be a buffer unique from src. + * + * @param src A pointer to the buffer containing the string to be encoded. + * Any character not specifically allowed in a URL is converted + * into its hexadecimal value and encoded as %xx. + * + * @param max_dest_len + * The size of the buffer pointed to by dest_segment. + * + * @returns The remaining buffer length. + */ +#ifdef __cplusplus +extern "C" { +#endif +int +smbc_urlencode(char * dest, char * src, int max_dest_len); +#ifdef __cplusplus +} +#endif + + +/**@ingroup directory + * Return the version of the linked Samba code, and thus the version of the + * libsmbclient code. + * + * @return The version string. + */ +#ifdef __cplusplus +extern "C" { +#endif +const char * +smbc_version(void); +#ifdef __cplusplus +} +#endif + + #endif /* SMBCLIENT_H_INCLUDED */