From 700f72453ed8dfd356a5591b9447127b5066ac4b Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Sun, 30 Apr 2000 11:04:28 +0000 Subject: - removed all our old wildcard matching code and replaced it with a call to ms_fnmatch(). This also removes all the Win9X semantics stuff and a bunch of other associated cruft. - moved the stat cache code into statcache.c - fixed the uint16 alignment requirements of ascii_to_unistr() and unistr_to_ascii() - trans2 SMB_FIND_FILE_BOTH_DIRECTORY_INFO returns the short name as unicode always (at least thats what NT4 does) - fixed some errors in the in-memory tdb code. Still ugly, but doesn't crash as much (This used to be commit 03e9cea004bbba72161a5323cf3b4556c94aed8e) --- source3/smbd/chgpasswd.c | 2 +- source3/smbd/dir.c | 2 +- source3/smbd/filename.c | 259 +---------------------------------------------- source3/smbd/reply.c | 15 ++- source3/smbd/statcache.c | 243 ++++++++++++++++++++++++++++++++++++++++++++ source3/smbd/trans2.c | 67 ++++++------ 6 files changed, 282 insertions(+), 306 deletions(-) create mode 100644 source3/smbd/statcache.c (limited to 'source3/smbd') diff --git a/source3/smbd/chgpasswd.c b/source3/smbd/chgpasswd.c index df1f92f5ce..484523bd27 100644 --- a/source3/smbd/chgpasswd.c +++ b/source3/smbd/chgpasswd.c @@ -249,7 +249,7 @@ static int expect(int master, char *issue, char *expected) nread += len; buffer[nread] = 0; - if ((match = unix_do_match(buffer, expected, False))) + if ((match = (ms_fnmatch(expected, buffer) == 0))) timeout = 200; } diff --git a/source3/smbd/dir.c b/source3/smbd/dir.c index 55d5bf132c..bd4e2a44f9 100644 --- a/source3/smbd/dir.c +++ b/source3/smbd/dir.c @@ -604,7 +604,7 @@ BOOL get_dir_entry(connection_struct *conn,char *mask,int dirtype,char *fname, if ((filename_is_mask = (strcmp(filename,mask) == 0)) || (name_map_mangle(filename,True,False,SNUM(conn)) && - mask_match(filename,mask,False,False))) + mask_match(filename,mask,False))) { if (isrootdir && (strequal(filename,"..") || strequal(filename,"."))) continue; diff --git a/source3/smbd/filename.c b/source3/smbd/filename.c index 7afa9e9986..ed1a1c3f63 100644 --- a/source3/smbd/filename.c +++ b/source3/smbd/filename.c @@ -88,228 +88,6 @@ static BOOL mangled_equal(char *name1, char *name2) return(strequal(name1,tmpname)); } -/**************************************************************************** - Stat cache code used in unix_convert. -*****************************************************************************/ - -static int global_stat_cache_lookups; -static int global_stat_cache_misses; -static int global_stat_cache_hits; - -/**************************************************************************** - Stat cache statistics code. -*****************************************************************************/ - -void print_stat_cache_statistics(void) -{ - double eff; - - if(global_stat_cache_lookups == 0) - return; - - eff = (100.0* (double)global_stat_cache_hits)/(double)global_stat_cache_lookups; - - DEBUG(0,("stat cache stats: lookups = %d, hits = %d, misses = %d, \ -stat cache was %f%% effective.\n", global_stat_cache_lookups, - global_stat_cache_hits, global_stat_cache_misses, eff )); -} - -typedef struct { - int name_len; - char names[2]; /* This is extended via malloc... */ -} stat_cache_entry; - -#define INIT_STAT_CACHE_SIZE 512 -static hash_table stat_cache; - -/**************************************************************************** - Compare a pathname to a name in the stat cache - of a given length. - Note - this code always checks that the next character in the pathname - is either a '/' character, or a '\0' character - to ensure we only - match *full* pathname components. Note we don't need to handle case - here, if we're case insensitive the stat cache orig names are all upper - case. -*****************************************************************************/ - -#if 0 /* This function unused?? */ -static BOOL stat_name_equal_len( char *stat_name, char *orig_name, int len) -{ - BOOL matched = (memcmp( stat_name, orig_name, len) == 0); - if(orig_name[len] != '/' && orig_name[len] != '\0') - return False; - - return matched; -} -#endif - -/**************************************************************************** - Add an entry into the stat cache. -*****************************************************************************/ - -static void stat_cache_add( char *full_orig_name, char *orig_translated_path) -{ - stat_cache_entry *scp; - stat_cache_entry *found_scp; - pstring orig_name; - pstring translated_path; - int namelen; - hash_element *hash_elem; - - if (!lp_stat_cache()) return; - - namelen = strlen(orig_translated_path); - - /* - * Don't cache trivial valid directory entries. - */ - if((*full_orig_name == '\0') || (strcmp(full_orig_name, ".") == 0) || - (strcmp(full_orig_name, "..") == 0)) - return; - - /* - * If we are in case insentive mode, we need to - * store names that need no translation - else, it - * would be a waste. - */ - - if(case_sensitive && (strcmp(full_orig_name, orig_translated_path) == 0)) - return; - - /* - * Remove any trailing '/' characters from the - * translated path. - */ - - pstrcpy(translated_path, orig_translated_path); - if(translated_path[namelen-1] == '/') { - translated_path[namelen-1] = '\0'; - namelen--; - } - - /* - * We will only replace namelen characters - * of full_orig_name. - * StrnCpy always null terminates. - */ - - StrnCpy(orig_name, full_orig_name, namelen); - if(!case_sensitive) - strupper( orig_name ); - - /* - * Check this name doesn't exist in the cache before we - * add it. - */ - - if ((hash_elem = hash_lookup(&stat_cache, orig_name))) { - found_scp = (stat_cache_entry *)(hash_elem->value); - if (strcmp((found_scp->names+found_scp->name_len+1), translated_path) == 0) { - return; - } else { - hash_remove(&stat_cache, hash_elem); - if((scp = (stat_cache_entry *)malloc(sizeof(stat_cache_entry)+2*namelen)) == NULL) { - DEBUG(0,("stat_cache_add: Out of memory !\n")); - return; - } - pstrcpy(scp->names, orig_name); - pstrcpy((scp->names+namelen+1), translated_path); - scp->name_len = namelen; - hash_insert(&stat_cache, (char *)scp, orig_name); - } - return; - } else { - - /* - * New entry. - */ - - if((scp = (stat_cache_entry *)malloc(sizeof(stat_cache_entry)+2*namelen)) == NULL) { - DEBUG(0,("stat_cache_add: Out of memory !\n")); - return; - } - pstrcpy(scp->names, orig_name); - pstrcpy(scp->names+namelen+1, translated_path); - scp->name_len = namelen; - hash_insert(&stat_cache, (char *)scp, orig_name); - } - - DEBUG(5,("stat_cache_add: Added entry %s -> %s\n", scp->names, (scp->names+scp->name_len+1))); -} - -/**************************************************************************** - Look through the stat cache for an entry - promote it to the top if found. - Return True if we translated (and did a scuccessful stat on) the entire name. -*****************************************************************************/ - -static BOOL stat_cache_lookup(connection_struct *conn, char *name, char *dirpath, - char **start, SMB_STRUCT_STAT *pst) -{ - stat_cache_entry *scp; - char *trans_name; - pstring chk_name; - int namelen; - hash_element *hash_elem; - char *sp; - - if (!lp_stat_cache()) - return False; - - namelen = strlen(name); - - *start = name; - global_stat_cache_lookups++; - - /* - * Don't lookup trivial valid directory entries. - */ - if((*name == '\0') || (strcmp(name, ".") == 0) || (strcmp(name, "..") == 0)) { - global_stat_cache_misses++; - return False; - } - - pstrcpy(chk_name, name); - if(!case_sensitive) - strupper( chk_name ); - - while (1) { - hash_elem = hash_lookup(&stat_cache, chk_name); - if(hash_elem == NULL) { - /* - * Didn't find it - remove last component for next try. - */ - sp = strrchr(chk_name, '/'); - if (sp) { - *sp = '\0'; - } else { - /* - * We reached the end of the name - no match. - */ - global_stat_cache_misses++; - return False; - } - if((*chk_name == '\0') || (strcmp(chk_name, ".") == 0) - || (strcmp(chk_name, "..") == 0)) { - global_stat_cache_misses++; - return False; - } - } else { - scp = (stat_cache_entry *)(hash_elem->value); - global_stat_cache_hits++; - trans_name = scp->names+scp->name_len+1; - if(conn->vfs_ops.stat(dos_to_unix(trans_name,False), pst) != 0) { - /* Discard this entry - it doesn't exist in the filesystem. */ - hash_remove(&stat_cache, hash_elem); - return False; - } - memcpy(name, trans_name, scp->name_len); - *start = &name[scp->name_len]; - if(**start == '/') - ++*start; - StrnCpy( dirpath, trans_name, name - (*start)); - return (namelen == scp->name_len); - } - } -} /**************************************************************************** This routine is called to convert names from the dos namespace to unix @@ -406,26 +184,6 @@ BOOL unix_convert(char *name,connection_struct *conn,char *saved_last_component, (!case_preserve || (is_8_3(name, False) && !short_case_preserve))) strnorm(name); - /* - * Check if it's a printer file. - */ - if (conn->printer) { - if ((! *name) || strchr(name,'/') || !is_8_3(name, True)) { - char *s; - fstring name2; - slprintf(name2,sizeof(name2)-1,"%.6s.XXXXXX",remote_machine); - - /* - * Sanitise the name. - */ - - for (s=name2 ; *s ; s++) - if (!issafe(*s)) *s = '_'; - pstrcpy(name,(char *)smbd_mktemp(name2)); - } - return(True); - } - /* * If we trimmed down to a single '\0' character * then we will be using the "." directory. @@ -471,8 +229,7 @@ BOOL unix_convert(char *name,connection_struct *conn,char *saved_last_component, !lp_strip_dot() && !use_mangled_map) return(False); - if(strchr(start,'?') || strchr(start,'*')) - name_has_wildcard = True; + name_has_wildcard = ms_has_wild(start); /* * is_mangled() was changed to look at an entire pathname, not @@ -549,7 +306,7 @@ BOOL unix_convert(char *name,connection_struct *conn,char *saved_last_component, * Try to find this part of the path in the directory. */ - if (strchr(start,'?') || strchr(start,'*') || + if (ms_has_wild(start) || !scan_directory(dirpath, start, conn, end?True:False)) { if (end) { /* @@ -752,15 +509,3 @@ static BOOL scan_directory(char *path, char *name,connection_struct *conn,BOOL d return(False); } -/*************************************************************************** ** - * Initializes or clears the stat cache. - * - * Input: none. - * Output: none. - * - * ************************************************************************** ** - */ -BOOL reset_stat_cache( void ) -{ - return hash_table_init( &stat_cache, INIT_STAT_CACHE_SIZE, (compare_function)(strcmp)); -} /* reset_stat_cache */ diff --git a/source3/smbd/reply.c b/source3/smbd/reply.c index 41c6dcb143..990a9aecb2 100644 --- a/source3/smbd/reply.c +++ b/source3/smbd/reply.c @@ -1331,9 +1331,6 @@ int reply_search(connection_struct *conn, char *inbuf,char *outbuf, int dum_size } } - /* Convert the formatted mask. (This code lives in trans2.c) */ - mask_convert(mask); - { int skip; p = mask; @@ -1936,7 +1933,7 @@ int reply_unlink(connection_struct *conn, char *inbuf,char *outbuf, int dum_size if (!rc && is_mangled(mask)) check_mangled_cache( mask ); - has_wild = strchr(mask,'*') || strchr(mask,'?'); + has_wild = ms_has_wild(mask); if (!has_wild) { pstrcat(directory,"/"); @@ -1969,7 +1966,7 @@ int reply_unlink(connection_struct *conn, char *inbuf,char *outbuf, int dum_size pstring fname; pstrcpy(fname,dname); - if(!mask_match(fname, mask, case_sensitive, False)) continue; + if(!mask_match(fname, mask, case_sensitive)) continue; error = ERRnoaccess; slprintf(fname,sizeof(fname)-1, "%s/%s",directory,dname); @@ -3508,7 +3505,7 @@ int rename_internals(connection_struct *conn, if (!rc && is_mangled(mask)) check_mangled_cache( mask ); - has_wild = strchr(mask,'*') || strchr(mask,'?'); + has_wild = ms_has_wild(mask); if (!has_wild) { /* @@ -3618,7 +3615,7 @@ int rename_internals(connection_struct *conn, pstrcpy(fname,dname); - if(!mask_match(fname, mask, case_sensitive, False)) + if(!mask_match(fname, mask, case_sensitive)) continue; error = ERRnoaccess; @@ -3847,7 +3844,7 @@ int reply_copy(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, if (!rc && is_mangled(mask)) check_mangled_cache( mask ); - has_wild = strchr(mask,'*') || strchr(mask,'?'); + has_wild = ms_has_wild(mask); if (!has_wild) { pstrcat(directory,"/"); @@ -3878,7 +3875,7 @@ int reply_copy(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, pstring fname; pstrcpy(fname,dname); - if(!mask_match(fname, mask, case_sensitive, False)) + if(!mask_match(fname, mask, case_sensitive)) continue; error = ERRnoaccess; diff --git a/source3/smbd/statcache.c b/source3/smbd/statcache.c new file mode 100644 index 0000000000..ae5dbb1ef7 --- /dev/null +++ b/source3/smbd/statcache.c @@ -0,0 +1,243 @@ +/* + Unix SMB/Netbios implementation. + Version 3.0 + stat cache code + Copyright (C) Andrew Tridgell 1992-2000 + Copyright (C) Jeremy Allison 1999-200 + + + 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 + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" + +extern BOOL case_sensitive; + + +/**************************************************************************** + Stat cache code used in unix_convert. +*****************************************************************************/ + +static int global_stat_cache_lookups; +static int global_stat_cache_misses; +static int global_stat_cache_hits; + +/**************************************************************************** + Stat cache statistics code. +*****************************************************************************/ + +void print_stat_cache_statistics(void) +{ + double eff; + + if(global_stat_cache_lookups == 0) + return; + + eff = (100.0* (double)global_stat_cache_hits)/(double)global_stat_cache_lookups; + + DEBUG(0,("stat cache stats: lookups = %d, hits = %d, misses = %d, \ +stat cache was %f%% effective.\n", global_stat_cache_lookups, + global_stat_cache_hits, global_stat_cache_misses, eff )); +} + +typedef struct { + int name_len; + char names[2]; /* This is extended via malloc... */ +} stat_cache_entry; + +#define INIT_STAT_CACHE_SIZE 512 +static hash_table stat_cache; + +/**************************************************************************** + Add an entry into the stat cache. +*****************************************************************************/ + +void stat_cache_add( char *full_orig_name, char *orig_translated_path) +{ + stat_cache_entry *scp; + stat_cache_entry *found_scp; + pstring orig_name; + pstring translated_path; + int namelen; + hash_element *hash_elem; + + if (!lp_stat_cache()) return; + + namelen = strlen(orig_translated_path); + + /* + * Don't cache trivial valid directory entries. + */ + if((*full_orig_name == '\0') || (strcmp(full_orig_name, ".") == 0) || + (strcmp(full_orig_name, "..") == 0)) + return; + + /* + * If we are in case insentive mode, we need to + * store names that need no translation - else, it + * would be a waste. + */ + + if(case_sensitive && (strcmp(full_orig_name, orig_translated_path) == 0)) + return; + + /* + * Remove any trailing '/' characters from the + * translated path. + */ + + pstrcpy(translated_path, orig_translated_path); + if(translated_path[namelen-1] == '/') { + translated_path[namelen-1] = '\0'; + namelen--; + } + + /* + * We will only replace namelen characters + * of full_orig_name. + * StrnCpy always null terminates. + */ + + StrnCpy(orig_name, full_orig_name, namelen); + if(!case_sensitive) + strupper( orig_name ); + + /* + * Check this name doesn't exist in the cache before we + * add it. + */ + + if ((hash_elem = hash_lookup(&stat_cache, orig_name))) { + found_scp = (stat_cache_entry *)(hash_elem->value); + if (strcmp((found_scp->names+found_scp->name_len+1), translated_path) == 0) { + return; + } else { + hash_remove(&stat_cache, hash_elem); + if((scp = (stat_cache_entry *)malloc(sizeof(stat_cache_entry)+2*namelen)) == NULL) { + DEBUG(0,("stat_cache_add: Out of memory !\n")); + return; + } + pstrcpy(scp->names, orig_name); + pstrcpy((scp->names+namelen+1), translated_path); + scp->name_len = namelen; + hash_insert(&stat_cache, (char *)scp, orig_name); + } + return; + } else { + + /* + * New entry. + */ + + if((scp = (stat_cache_entry *)malloc(sizeof(stat_cache_entry)+2*namelen)) == NULL) { + DEBUG(0,("stat_cache_add: Out of memory !\n")); + return; + } + pstrcpy(scp->names, orig_name); + pstrcpy(scp->names+namelen+1, translated_path); + scp->name_len = namelen; + hash_insert(&stat_cache, (char *)scp, orig_name); + } + + DEBUG(5,("stat_cache_add: Added entry %s -> %s\n", scp->names, (scp->names+scp->name_len+1))); +} + +/**************************************************************************** + Look through the stat cache for an entry - promote it to the top if found. + Return True if we translated (and did a scuccessful stat on) the entire name. +*****************************************************************************/ + +BOOL stat_cache_lookup(connection_struct *conn, char *name, char *dirpath, + char **start, SMB_STRUCT_STAT *pst) +{ + stat_cache_entry *scp; + char *trans_name; + pstring chk_name; + int namelen; + hash_element *hash_elem; + char *sp; + + if (!lp_stat_cache()) + return False; + + namelen = strlen(name); + + *start = name; + global_stat_cache_lookups++; + + /* + * Don't lookup trivial valid directory entries. + */ + if((*name == '\0') || (strcmp(name, ".") == 0) || (strcmp(name, "..") == 0)) { + global_stat_cache_misses++; + return False; + } + + pstrcpy(chk_name, name); + if(!case_sensitive) + strupper( chk_name ); + + while (1) { + hash_elem = hash_lookup(&stat_cache, chk_name); + if(hash_elem == NULL) { + /* + * Didn't find it - remove last component for next try. + */ + sp = strrchr(chk_name, '/'); + if (sp) { + *sp = '\0'; + } else { + /* + * We reached the end of the name - no match. + */ + global_stat_cache_misses++; + return False; + } + if((*chk_name == '\0') || (strcmp(chk_name, ".") == 0) + || (strcmp(chk_name, "..") == 0)) { + global_stat_cache_misses++; + return False; + } + } else { + scp = (stat_cache_entry *)(hash_elem->value); + global_stat_cache_hits++; + trans_name = scp->names+scp->name_len+1; + if(conn->vfs_ops.stat(dos_to_unix(trans_name,False), pst) != 0) { + /* Discard this entry - it doesn't exist in the filesystem. */ + hash_remove(&stat_cache, hash_elem); + return False; + } + memcpy(name, trans_name, scp->name_len); + *start = &name[scp->name_len]; + if(**start == '/') + ++*start; + StrnCpy( dirpath, trans_name, name - (*start)); + return (namelen == scp->name_len); + } + } +} + +/*************************************************************************** ** + * Initializes or clears the stat cache. + * + * Input: none. + * Output: none. + * + * ************************************************************************** ** + */ +BOOL reset_stat_cache( void ) +{ + return hash_table_init( &stat_cache, INIT_STAT_CACHE_SIZE, (compare_function)(strcmp)); +} /* reset_stat_cache */ diff --git a/source3/smbd/trans2.c b/source3/smbd/trans2.c index 4e502f767b..7ae24f9e38 100644 --- a/source3/smbd/trans2.c +++ b/source3/smbd/trans2.c @@ -287,6 +287,19 @@ static int call_trans2open(connection_struct *conn, char *inbuf, char *outbuf, return -1; } +/********************************************************* +* Routine to check if a given string matches exactly. +* as a special case a mask of "." does NOT match. That +* is required for correct wildcard semantics +* Case can be significant or not. +**********************************************************/ +static BOOL exact_match(char *str,char *mask, BOOL case_sig) +{ + if (mask[0] == '.' && mask[1] == 0) return False; + if (case_sig) return strcmp(str,mask)==0; + return strcasecmp(str,mask) == 0; +} + /**************************************************************************** get a level dependent lanman2 dir entry. ****************************************************************************/ @@ -360,7 +373,7 @@ static BOOL get_lanman2_dir_entry(connection_struct *conn, pstrcpy(fname,dname); if(!(got_match = *got_exact_match = exact_match(fname, mask, case_sensitive))) - got_match = mask_match(fname, mask, case_sensitive, True); + got_match = mask_match(fname, mask, case_sensitive); if(!got_match && !is_8_3(fname, False)) { @@ -375,7 +388,7 @@ static BOOL get_lanman2_dir_entry(connection_struct *conn, pstrcpy( newname, fname); name_map_mangle( newname, True, False, SNUM(conn)); if(!(got_match = *got_exact_match = exact_match(newname, mask, case_sensitive))) - got_match = mask_match(newname, mask, case_sensitive, True); + got_match = mask_match(newname, mask, case_sensitive); } if(got_match) @@ -509,13 +522,20 @@ static BOOL get_lanman2_dir_entry(connection_struct *conn, SIVAL(p,0,strlen(fname)); p += 4; SIVAL(p,0,0); p += 4; if (!was_8_3) { - pstrcpy(p+2,fname); - if (!name_map_mangle(p+2,True,True,SNUM(conn))) - (p+2)[12] = 0; - } else - *(p+2) = 0; - strupper(p+2); - SSVAL(p,0,strlen(p+2)); + /* NT4 always uses unicode here */ + fstring short_name, ushort_name; + int slen; + pstrcpy(short_name,fname); + name_map_mangle(short_name,True,True,SNUM(conn)); + strupper(short_name); + slen = strlen(short_name); + ascii_to_unistr(ushort_name, short_name, 24); + memcpy(p+2, ushort_name, 2*slen); + SSVAL(p, 0, 2*slen); + } else { + SSVAL(p,0,0); + *(p+2) = 0; + } p += 2 + 24; /* nameptr = p; */ pstrcpy(p,fname); p += strlen(p); @@ -591,32 +611,6 @@ static BOOL get_lanman2_dir_entry(connection_struct *conn, return(found); } -/**************************************************************************** - Convert the directory masks formated for the wire. -****************************************************************************/ - -void mask_convert( char *mask) -{ - /* - * We know mask is a pstring. - */ - char *p = mask; - while (*p) { - if (*p == '<') { - pstring expnd; - if(p[1] != '"' && p[1] != '.') { - pstrcpy( expnd, p+1 ); - *p++ = '*'; - *p = '.'; - safe_strcpy( p+1, expnd, sizeof(pstring) - (p - mask) - 2); - } else - *p = '*'; - } - if (*p == '>') *p = '?'; - if (*p == '"') *p = '.'; - p++; - } -} /**************************************************************************** Reply to a TRANS2_FINDFIRST. @@ -727,9 +721,6 @@ static int call_trans2findfirst(connection_struct *conn, if (dptr_num < 0) return(UNIXERROR(ERRDOS,ERRbadfile)); - /* Convert the formatted mask. */ - mask_convert(mask); - /* Save the wildcard match and attribs we are using on this directory - needed as lanman2 assumes these are being saved between calls */ -- cgit