2002-04-11 13:56:38 +04:00
/*
Unix SMB / CIFS implementation .
new hash based name mangling implementation
Copyright ( C ) Andrew Tridgell 2002
2002-07-15 14:35:28 +04:00
Copyright ( C ) Simo Sorce 2002
2002-04-11 13:56:38 +04:00
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
2007-07-09 23:25:36 +04:00
the Free Software Foundation ; either version 3 of the License , or
2002-04-11 13:56:38 +04:00
( 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
2007-07-10 04:52:41 +04:00
along with this program . If not , see < http : //www.gnu.org/licenses/>.
2002-04-11 13:56:38 +04:00
*/
/*
this mangling scheme uses the following format
Annnn ~ n . AAA
where nnnnn is a base 36 hash , and A represents characters from the original string
The hash is taken of the leading part of the long filename , in uppercase
for simplicity , we only allow ascii characters in 8.3 names
*/
2002-07-15 14:35:28 +04:00
/* hash alghorithm changed to FNV1 by idra@samba.org (Simo Sorce).
* see http : //www.isthe.com/chongo/tech/comp/fnv/index.html for a
* discussion on Fowler / Noll / Vo ( FNV ) Hash by one of it ' s authors
*/
2002-04-11 13:56:38 +04:00
/*
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
NOTE NOTE NOTE ! ! !
This file deliberately uses non - multibyte string functions in many places . This
is * not * a mistake . This code is multi - byte safe , but it gets this property
through some very subtle knowledge of the way multi - byte strings are encoded
and the fact that this mangling algorithm only supports ascii characters in
8.3 names .
please don ' t convert this file to use the * _m ( ) functions ! !
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
*/
# include "includes.h"
2003-09-15 22:53:01 +04:00
# if 1
2002-04-11 14:52:59 +04:00
# define M_DEBUG(level, x) DEBUG(level, x)
# else
# define M_DEBUG(level, x)
# endif
2002-04-11 13:56:38 +04:00
2002-04-11 15:46:42 +04:00
/* these flags are used to mark characters in as having particular
properties */
2002-04-11 13:56:38 +04:00
# define FLAG_BASECHAR 1
# define FLAG_ASCII 2
# define FLAG_ILLEGAL 4
2002-04-11 14:52:59 +04:00
# define FLAG_WILDCARD 8
2002-04-11 15:46:42 +04:00
/* the "possible" flags are used as a fast way to find possible DOS
reserved filenames */
2002-04-11 14:52:59 +04:00
# define FLAG_POSSIBLE1 16
# define FLAG_POSSIBLE2 32
# define FLAG_POSSIBLE3 64
# define FLAG_POSSIBLE4 128
2002-04-11 13:56:38 +04:00
2002-04-11 15:46:42 +04:00
/* by default have a max of 4096 entries in the cache. */
2002-04-11 14:52:59 +04:00
# ifndef MANGLE_CACHE_SIZE
# define MANGLE_CACHE_SIZE 4096
# endif
2002-04-11 13:56:38 +04:00
2002-07-15 14:35:28 +04:00
# define FNV1_PRIME 0x01000193
/*the following number is a fnv1 of the string: idra@samba.org 2002 */
# define FNV1_INIT 0xa6b93095
2002-04-11 13:56:38 +04:00
/* these tables are used to provide fast tests for characters */
static unsigned char char_flags [ 256 ] ;
2002-04-11 16:14:55 +04:00
# define FLAG_CHECK(c, flag) (char_flags[(unsigned char)(c)] & (flag))
2002-10-22 00:40:23 +04:00
/*
this determines how many characters are used from the original filename
in the 8.3 mangled name . A larger value leads to a weaker hash and more collisions .
The largest possible value is 6.
*/
static unsigned mangle_prefix ;
2002-04-11 13:56:38 +04:00
/* we will use a very simple direct mapped prefix cache. The big
advantage of this cache structure is speed and low memory usage
The cache is indexed by the low - order bits of the hash , and confirmed by
hashing the resulting cache entry to match the known hash
*/
static char * * prefix_cache ;
2006-07-11 22:01:26 +04:00
static unsigned int * prefix_cache_hashes ;
2002-04-11 13:56:38 +04:00
/* these are the characters we use in the 8.3 hash. Must be 36 chars long */
2002-07-15 14:35:28 +04:00
static const char * basechars = " 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ " ;
2002-04-11 13:56:38 +04:00
static unsigned char base_reverse [ 256 ] ;
# define base_forward(v) basechars[v]
/* the list of reserved dos names - all of these are illegal */
2002-07-15 14:35:28 +04:00
static const char * reserved_names [ ] =
{ " AUX " , " LOCK$ " , " CON " , " COM1 " , " COM2 " , " COM3 " , " COM4 " ,
" LPT1 " , " LPT2 " , " LPT3 " , " NUL " , " PRN " , NULL } ;
2002-04-11 13:56:38 +04:00
/*
hash a string of the specified length . The string does not need to be
null terminated
this hash needs to be fast with a low collision rate ( what hash doesn ' t ? )
*/
2006-07-11 22:01:26 +04:00
static unsigned int mangle_hash ( const char * key , unsigned int length )
2002-04-11 13:56:38 +04:00
{
2006-07-11 22:01:26 +04:00
unsigned int value ;
unsigned int i ;
2002-04-11 13:56:38 +04:00
fstring str ;
2002-04-11 15:46:42 +04:00
/* we have to uppercase here to ensure that the mangled name
doesn ' t depend on the case of the long name . Note that this
is the only place where we need to use a multi - byte string
function */
2004-08-27 01:39:10 +04:00
length = MIN ( length , sizeof ( fstring ) - 1 ) ;
2002-04-11 13:56:38 +04:00
strncpy ( str , key , length ) ;
str [ length ] = 0 ;
strupper_m ( str ) ;
2002-04-11 15:46:42 +04:00
/* the length of a multi-byte string can change after a strupper_m */
2002-04-11 13:56:38 +04:00
length = strlen ( str ) ;
/* Set the initial value from the key size. */
2002-07-15 14:35:28 +04:00
for ( value = FNV1_INIT , i = 0 ; i < length ; i + + ) {
2006-07-11 22:01:26 +04:00
value * = ( unsigned int ) FNV1_PRIME ;
value ^ = ( unsigned int ) ( str [ i ] ) ;
2002-07-15 14:35:28 +04:00
}
2002-04-11 13:56:38 +04:00
2002-04-11 18:20:18 +04:00
/* note that we force it to a 31 bit hash, to keep within the limits
of the 36 ^ 6 mangle space */
2002-07-15 14:35:28 +04:00
return value & ~ 0x80000000 ;
2002-04-11 13:56:38 +04:00
}
/*
2002-04-11 15:46:42 +04:00
initialise ( ie . allocate ) the prefix cache
2002-04-11 13:56:38 +04:00
*/
static BOOL cache_init ( void )
{
2004-12-07 21:25:53 +03:00
if ( prefix_cache ) {
return True ;
}
2002-04-11 13:56:38 +04:00
2004-12-07 21:25:53 +03:00
prefix_cache = SMB_CALLOC_ARRAY ( char * , MANGLE_CACHE_SIZE ) ;
if ( ! prefix_cache ) {
return False ;
}
2002-04-11 13:56:38 +04:00
2006-07-11 22:01:26 +04:00
prefix_cache_hashes = SMB_CALLOC_ARRAY ( unsigned int , MANGLE_CACHE_SIZE ) ;
2004-12-07 21:25:53 +03:00
if ( ! prefix_cache_hashes ) {
2007-08-21 22:45:45 +04:00
SAFE_FREE ( prefix_cache ) ;
2004-12-07 21:25:53 +03:00
return False ;
}
2002-04-11 14:52:59 +04:00
2002-04-11 13:56:38 +04:00
return True ;
}
/*
2002-04-11 15:46:42 +04:00
insert an entry into the prefix cache . The string might not be null
terminated */
2006-07-11 22:01:26 +04:00
static void cache_insert ( const char * prefix , int length , unsigned int hash )
2002-04-11 13:56:38 +04:00
{
2002-04-11 14:52:59 +04:00
int i = hash % MANGLE_CACHE_SIZE ;
2002-04-11 13:56:38 +04:00
if ( prefix_cache [ i ] ) {
free ( prefix_cache [ i ] ) ;
}
2004-12-07 21:25:53 +03:00
prefix_cache [ i ] = SMB_STRNDUP ( prefix , length ) ;
2002-04-11 14:52:59 +04:00
prefix_cache_hashes [ i ] = hash ;
2002-04-11 13:56:38 +04:00
}
/*
lookup an entry in the prefix cache . Return NULL if not found .
*/
2006-07-11 22:01:26 +04:00
static const char * cache_lookup ( unsigned int hash )
2002-04-11 13:56:38 +04:00
{
2002-04-11 14:52:59 +04:00
int i = hash % MANGLE_CACHE_SIZE ;
2002-04-11 13:56:38 +04:00
2002-04-11 14:52:59 +04:00
if ( ! prefix_cache [ i ] | | hash ! = prefix_cache_hashes [ i ] ) {
2002-04-11 13:56:38 +04:00
return NULL ;
}
/* yep, it matched */
return prefix_cache [ i ] ;
}
/*
determine if a string is possibly in a mangled format , ignoring
case
In this algorithm , mangled names use only pure ascii characters ( no
multi - byte ) so we can avoid doing a UCS2 conversion
2002-07-15 14:35:28 +04:00
*/
2003-08-27 22:17:33 +04:00
static BOOL is_mangled_component ( const char * name , size_t len )
2002-04-11 13:56:38 +04:00
{
2003-08-27 22:17:33 +04:00
unsigned int i ;
2002-04-11 16:14:55 +04:00
2005-09-30 21:13:37 +04:00
M_DEBUG ( 10 , ( " is_mangled_component %s (len %lu) ? \n " , name , ( unsigned long ) len ) ) ;
2002-04-11 16:14:55 +04:00
/* check the length */
2003-08-27 22:17:33 +04:00
if ( len > 12 | | len < 8 )
return False ;
2002-04-11 16:14:55 +04:00
2002-08-17 19:27:10 +04:00
/* the best distinguishing characteristic is the ~ */
2003-08-27 22:17:33 +04:00
if ( name [ 6 ] ! = ' ~ ' )
return False ;
2002-08-17 19:27:10 +04:00
2002-04-11 13:56:38 +04:00
/* check extension */
2002-04-11 16:14:55 +04:00
if ( len > 8 ) {
2003-08-27 22:17:33 +04:00
if ( name [ 8 ] ! = ' . ' )
return False ;
2003-09-16 01:19:43 +04:00
for ( i = 9 ; name [ i ] & & i < len ; i + + ) {
2002-04-11 16:14:55 +04:00
if ( ! FLAG_CHECK ( name [ i ] , FLAG_ASCII ) ) {
return False ;
}
}
}
2002-10-22 00:40:23 +04:00
/* check lead characters */
for ( i = 0 ; i < mangle_prefix ; i + + ) {
if ( ! FLAG_CHECK ( name [ i ] , FLAG_ASCII ) ) {
return False ;
}
2002-04-11 16:14:55 +04:00
}
2002-04-11 13:56:38 +04:00
/* check rest of hash */
2002-04-11 16:14:55 +04:00
if ( ! FLAG_CHECK ( name [ 7 ] , FLAG_BASECHAR ) ) {
return False ;
}
2002-10-22 00:40:23 +04:00
for ( i = mangle_prefix ; i < 6 ; i + + ) {
2002-04-11 16:14:55 +04:00
if ( ! FLAG_CHECK ( name [ i ] , FLAG_BASECHAR ) ) {
return False ;
}
}
2002-04-11 13:56:38 +04:00
2005-09-30 21:13:37 +04:00
M_DEBUG ( 10 , ( " is_mangled_component %s (len %lu) -> yes \n " , name , ( unsigned long ) len ) ) ;
2002-04-11 18:03:30 +04:00
2002-04-11 13:56:38 +04:00
return True ;
}
2002-07-15 14:35:28 +04:00
/*
determine if a string is possibly in a mangled format , ignoring
case
In this algorithm , mangled names use only pure ascii characters ( no
multi - byte ) so we can avoid doing a UCS2 conversion
NOTE ! This interface must be able to handle a path with unix
directory separators . It should return true if any component is
mangled
*/
2006-07-11 22:01:26 +04:00
static BOOL is_mangled ( const char * name , const struct share_params * parm )
2002-07-15 14:35:28 +04:00
{
const char * p ;
const char * s ;
2002-09-25 19:19:00 +04:00
M_DEBUG ( 10 , ( " is_mangled %s ? \n " , name ) ) ;
2002-07-15 14:35:28 +04:00
for ( s = name ; ( p = strchr ( s , ' / ' ) ) ; s = p + 1 ) {
2003-08-27 22:17:33 +04:00
if ( is_mangled_component ( s , PTR_DIFF ( p , s ) ) ) {
2002-07-15 14:35:28 +04:00
return True ;
}
}
/* and the last part ... */
2003-08-27 22:17:33 +04:00
return is_mangled_component ( s , strlen ( s ) ) ;
2002-07-15 14:35:28 +04:00
}
2002-04-11 13:56:38 +04:00
/*
see if a filename is an allowable 8.3 name .
we are only going to allow ascii characters in 8.3 names , as this
simplifies things greatly ( it means that we know the string won ' t
get larger when converted from UNIX to DOS formats )
*/
2006-07-11 22:01:26 +04:00
static BOOL is_8_3 ( const char * name , BOOL check_case , BOOL allow_wildcards , const struct share_params * p )
2002-04-11 13:56:38 +04:00
{
int len , i ;
char * dot_p ;
/* as a special case, the names '.' and '..' are allowable 8.3 names */
if ( name [ 0 ] = = ' . ' ) {
if ( ! name [ 1 ] | | ( name [ 1 ] = = ' . ' & & ! name [ 2 ] ) ) {
return True ;
}
}
/* the simplest test is on the overall length of the
filename . Note that we deliberately use the ascii string
length ( not the multi - byte one ) as it is faster , and gives us
the result we need in this case . Using strlen_m would not
only be slower , it would be incorrect */
len = strlen ( name ) ;
2003-08-27 23:01:55 +04:00
if ( len > 12 )
return False ;
2002-04-11 13:56:38 +04:00
/* find the '.'. Note that once again we use the non-multibyte
function */
dot_p = strchr ( name , ' . ' ) ;
if ( ! dot_p ) {
/* if the name doesn't contain a '.' then its length
must be less than 8 */
if ( len > 8 ) {
return False ;
}
} else {
int prefix_len , suffix_len ;
/* if it does contain a dot then the prefix must be <=
8 and the suffix < = 3 in length */
prefix_len = PTR_DIFF ( dot_p , name ) ;
suffix_len = len - ( prefix_len + 1 ) ;
2003-11-18 04:47:27 +03:00
if ( prefix_len > 8 | | suffix_len > 3 | | suffix_len = = 0 ) {
2002-04-11 13:56:38 +04:00
return False ;
}
/* a 8.3 name cannot contain more than 1 '.' */
if ( strchr ( dot_p + 1 , ' . ' ) ) {
return False ;
}
}
/* the length are all OK. Now check to see if the characters themselves are OK */
for ( i = 0 ; name [ i ] ; i + + ) {
2002-07-15 14:35:28 +04:00
/* note that we may allow wildcard petterns! */
if ( ! FLAG_CHECK ( name [ i ] , FLAG_ASCII | ( allow_wildcards ? FLAG_WILDCARD : 0 ) ) & & name [ i ] ! = ' . ' ) {
2002-04-11 13:56:38 +04:00
return False ;
}
}
/* it is a good 8.3 name */
return True ;
}
/*
reset the mangling cache on a smb . conf reload . This only really makes sense for
mangling backends that have parameters in smb . conf , and as this backend doesn ' t
this is a NULL operation
*/
static void mangle_reset ( void )
{
/* noop */
}
/*
try to find a 8.3 name in the cache , and if found then
2007-09-08 00:57:01 +04:00
replace the string with the original long name .
2002-04-11 13:56:38 +04:00
*/
2007-09-08 00:57:01 +04:00
static BOOL lookup_name_from_8_3 ( TALLOC_CTX * ctx ,
const char * name ,
char * * pp_out , /* talloced on the given context. */
const struct share_params * p )
2002-04-11 13:56:38 +04:00
{
2006-07-11 22:01:26 +04:00
unsigned int hash , multiplier ;
2003-02-24 06:09:08 +03:00
unsigned int i ;
2002-04-11 13:56:38 +04:00
const char * prefix ;
char extension [ 4 ] ;
2007-09-08 00:57:01 +04:00
* pp_out = NULL ;
2002-04-11 13:56:38 +04:00
/* make sure that this is a mangled name from this cache */
2006-07-11 22:01:26 +04:00
if ( ! is_mangled ( name , p ) ) {
2007-09-08 00:57:01 +04:00
M_DEBUG ( 10 , ( " lookup_name_from_8_3: %s -> not mangled \n " , name ) ) ;
2002-04-11 13:56:38 +04:00
return False ;
}
/* we need to extract the hash from the 8.3 name */
hash = base_reverse [ ( unsigned char ) name [ 7 ] ] ;
2002-10-22 00:40:23 +04:00
for ( multiplier = 36 , i = 5 ; i > = mangle_prefix ; i - - ) {
2006-07-11 22:01:26 +04:00
unsigned int v = base_reverse [ ( unsigned char ) name [ i ] ] ;
2002-04-11 13:56:38 +04:00
hash + = multiplier * v ;
multiplier * = 36 ;
}
/* now look in the prefix cache for that hash */
prefix = cache_lookup ( hash ) ;
if ( ! prefix ) {
2007-09-08 00:57:01 +04:00
M_DEBUG ( 10 , ( " lookup_name_from_8_3: %s -> %08X -> not found \n " ,
name , hash ) ) ;
2002-04-11 13:56:38 +04:00
return False ;
}
/* we found it - construct the full name */
2002-07-15 14:35:28 +04:00
if ( name [ 8 ] = = ' . ' ) {
strncpy ( extension , name + 9 , 3 ) ;
extension [ 3 ] = 0 ;
} else {
extension [ 0 ] = 0 ;
}
2002-04-11 13:56:38 +04:00
if ( extension [ 0 ] ) {
2007-09-08 00:57:01 +04:00
M_DEBUG ( 10 , ( " lookup_name_from_8_3: %s -> %s.%s \n " ,
name , prefix , extension ) ) ;
* pp_out = talloc_asprintf ( ctx , " %s.%s " , prefix , extension ) ;
2002-04-11 13:56:38 +04:00
} else {
2007-09-08 00:57:01 +04:00
M_DEBUG ( 10 , ( " lookup_name_from_8_3: %s -> %s \n " , name , prefix ) ) ;
* pp_out = talloc_strdup ( ctx , prefix ) ;
}
if ( ! pp_out ) {
M_DEBUG ( 0 , ( " talloc_fail " ) ) ;
return False ;
2002-04-11 13:56:38 +04:00
}
2002-04-11 14:52:59 +04:00
2002-04-11 13:56:38 +04:00
return True ;
}
/*
look for a DOS reserved name
*/
static BOOL is_reserved_name ( const char * name )
{
2002-04-11 16:14:55 +04:00
if ( FLAG_CHECK ( name [ 0 ] , FLAG_POSSIBLE1 ) & &
FLAG_CHECK ( name [ 1 ] , FLAG_POSSIBLE2 ) & &
FLAG_CHECK ( name [ 2 ] , FLAG_POSSIBLE3 ) & &
FLAG_CHECK ( name [ 3 ] , FLAG_POSSIBLE4 ) ) {
2002-04-11 13:56:38 +04:00
/* a likely match, scan the lot */
int i ;
for ( i = 0 ; reserved_names [ i ] ; i + + ) {
int len = strlen ( reserved_names [ i ] ) ;
/* note that we match on COM1 as well as COM1.foo */
2003-10-23 03:38:20 +04:00
if ( strnequal ( name , reserved_names [ i ] , len ) & &
2002-04-11 13:56:38 +04:00
( name [ len ] = = ' . ' | | name [ len ] = = 0 ) ) {
return True ;
}
}
}
return False ;
}
/*
2002-07-15 14:35:28 +04:00
See if a filename is a legal long filename .
A filename ending in a ' . ' is not legal unless it ' s " . " or " .. " . JRA .
2005-09-29 05:27:19 +04:00
A filename ending in ' ' is not legal either . See bug id # 2769.
2002-04-11 13:56:38 +04:00
*/
2002-07-15 14:35:28 +04:00
2002-04-11 13:56:38 +04:00
static BOOL is_legal_name ( const char * name )
{
2002-07-15 14:35:28 +04:00
const char * dot_pos = NULL ;
BOOL alldots = True ;
size_t numdots = 0 ;
2002-04-11 13:56:38 +04:00
while ( * name ) {
2003-08-27 23:01:55 +04:00
if ( ( ( unsigned int ) name [ 0 ] ) > 128 & & ( name [ 1 ] ! = 0 ) ) {
/* Possible start of mb character. */
char mbc [ 2 ] ;
/*
* Note that if CH_UNIX is utf8 a string may be 3
* bytes , but this is ok as mb utf8 characters don ' t
* contain embedded ascii bytes . We are really checking
* for mb UNIX asian characters like Japanese ( SJIS ) here .
* JRA .
*/
2006-09-21 22:37:09 +04:00
if ( convert_string ( CH_UNIX , CH_UTF16LE , name , 2 , mbc , 2 , False ) = = 2 ) {
2003-08-27 23:01:55 +04:00
/* Was a good mb string. */
name + = 2 ;
continue ;
}
}
2002-04-11 16:14:55 +04:00
if ( FLAG_CHECK ( name [ 0 ] , FLAG_ILLEGAL ) ) {
2002-04-11 13:56:38 +04:00
return False ;
}
2002-07-15 14:35:28 +04:00
if ( name [ 0 ] = = ' . ' ) {
dot_pos = name ;
numdots + + ;
} else {
alldots = False ;
}
2005-09-29 05:27:19 +04:00
if ( ( name [ 0 ] = = ' ' ) & & ( name [ 1 ] = = ' \0 ' ) ) {
/* Can't end in ' ' */
return False ;
}
2002-04-11 13:56:38 +04:00
name + + ;
}
2002-07-15 14:35:28 +04:00
if ( dot_pos ) {
if ( alldots & & ( numdots = = 1 | | numdots = = 2 ) )
return True ; /* . or .. is a valid name */
/* A valid long name cannot end in '.' */
if ( dot_pos [ 1 ] = = ' \0 ' )
return False ;
}
2002-04-11 13:56:38 +04:00
return True ;
}
2007-09-08 00:57:01 +04:00
static BOOL must_mangle ( const char * name ,
const struct share_params * p )
{
if ( is_reserved_name ( name ) ) {
return True ;
}
return ! is_legal_name ( name ) ;
}
2002-04-11 13:56:38 +04:00
/*
the main forward mapping function , which converts a long filename to
a 8.3 name
if cache83 is not set then we don ' t cache the result
*/
2007-09-08 00:57:01 +04:00
static BOOL hash2_name_to_8_3 ( const char * name ,
char new_name [ 13 ] ,
BOOL cache83 ,
int default_case ,
const struct share_params * p )
2002-04-11 13:56:38 +04:00
{
char * dot_p ;
2002-10-22 00:40:23 +04:00
char lead_chars [ 7 ] ;
2002-04-11 13:56:38 +04:00
char extension [ 4 ] ;
2003-02-24 06:09:08 +03:00
unsigned int extension_length , i ;
unsigned int prefix_len ;
2006-07-11 22:01:26 +04:00
unsigned int hash , v ;
2002-04-11 13:56:38 +04:00
/* reserved names are handled specially */
if ( ! is_reserved_name ( name ) ) {
2007-09-08 00:57:01 +04:00
/* if the name is already a valid 8.3 name then we don't need to
* change anything */
if ( is_legal_name ( name ) & & is_8_3 ( name , False , False , p ) ) {
safe_strcpy ( new_name , name , 12 ) ;
return True ;
2002-04-11 13:56:38 +04:00
}
}
/* find the '.' if any */
2002-04-11 18:20:18 +04:00
dot_p = strrchr ( name , ' . ' ) ;
2002-04-11 13:56:38 +04:00
2002-04-12 14:18:46 +04:00
if ( dot_p ) {
/* if the extension contains any illegal characters or
is too long or zero length then we treat it as part
of the prefix */
for ( i = 0 ; i < 4 & & dot_p [ i + 1 ] ; i + + ) {
if ( ! FLAG_CHECK ( dot_p [ i + 1 ] , FLAG_ASCII ) ) {
dot_p = NULL ;
break ;
}
}
2007-09-08 00:57:01 +04:00
if ( i = = 0 | | i = = 4 ) {
dot_p = NULL ;
}
2002-04-12 14:18:46 +04:00
}
2002-10-22 00:40:23 +04:00
/* the leading characters in the mangled name is taken from
the first characters of the name , if they are ascii otherwise
' _ ' is used
2002-04-11 13:56:38 +04:00
*/
2002-10-22 00:40:23 +04:00
for ( i = 0 ; i < mangle_prefix & & name [ i ] ; i + + ) {
lead_chars [ i ] = name [ i ] ;
if ( ! FLAG_CHECK ( lead_chars [ i ] , FLAG_ASCII ) ) {
lead_chars [ i ] = ' _ ' ;
}
2005-12-27 23:52:36 +03:00
lead_chars [ i ] = toupper_ascii ( lead_chars [ i ] ) ;
2002-10-22 00:40:23 +04:00
}
for ( ; i < mangle_prefix ; i + + ) {
lead_chars [ i ] = ' _ ' ;
2002-04-11 13:56:38 +04:00
}
/* the prefix is anything up to the first dot */
if ( dot_p ) {
prefix_len = PTR_DIFF ( dot_p , name ) ;
} else {
prefix_len = strlen ( name ) ;
}
/* the extension of the mangled name is taken from the first 3
ascii chars after the dot */
extension_length = 0 ;
if ( dot_p ) {
for ( i = 1 ; extension_length < 3 & & dot_p [ i ] ; i + + ) {
char c = dot_p [ i ] ;
2002-04-11 16:14:55 +04:00
if ( FLAG_CHECK ( c , FLAG_ASCII ) ) {
2007-09-08 00:57:01 +04:00
extension [ extension_length + + ] =
toupper_ascii ( c ) ;
2002-04-11 13:56:38 +04:00
}
}
}
2007-09-08 00:57:01 +04:00
2002-04-11 13:56:38 +04:00
/* find the hash for this prefix */
v = hash = mangle_hash ( name , prefix_len ) ;
2002-04-11 15:46:42 +04:00
/* now form the mangled name. */
2002-10-22 00:40:23 +04:00
for ( i = 0 ; i < mangle_prefix ; i + + ) {
new_name [ i ] = lead_chars [ i ] ;
}
2002-04-11 13:56:38 +04:00
new_name [ 7 ] = base_forward ( v % 36 ) ;
2007-09-08 00:57:01 +04:00
new_name [ 6 ] = ' ~ ' ;
2002-10-22 00:40:23 +04:00
for ( i = 5 ; i > = mangle_prefix ; i - - ) {
2002-04-11 13:56:38 +04:00
v = v / 36 ;
new_name [ i ] = base_forward ( v % 36 ) ;
}
/* add the extension */
if ( extension_length ) {
new_name [ 8 ] = ' . ' ;
memcpy ( & new_name [ 9 ] , extension , extension_length ) ;
new_name [ 9 + extension_length ] = 0 ;
} else {
new_name [ 8 ] = 0 ;
}
if ( cache83 ) {
/* put it in the cache */
cache_insert ( name , prefix_len , hash ) ;
}
2007-09-08 00:57:01 +04:00
M_DEBUG ( 10 , ( " hash2_name_to_8_3: %s -> %08X -> %s (cache=%d) \n " ,
2002-04-11 18:03:30 +04:00
name , hash , new_name , cache83 ) ) ;
2002-04-11 14:52:59 +04:00
2007-09-08 00:57:01 +04:00
return True ;
2002-04-11 13:56:38 +04:00
}
2007-09-08 00:57:01 +04:00
/* initialise the flags table
2002-04-11 13:56:38 +04:00
we allow only a very restricted set of characters as ' ascii ' in this
mangling backend . This isn ' t a significant problem as modern clients
use the ' long ' filenames anyway , and those don ' t have these
2007-09-08 00:57:01 +04:00
restrictions .
2002-04-11 13:56:38 +04:00
*/
static void init_tables ( void )
{
int i ;
memset ( char_flags , 0 , sizeof ( char_flags ) ) ;
2002-10-22 00:40:23 +04:00
for ( i = 1 ; i < 128 ; i + + ) {
2005-05-25 23:25:35 +04:00
if ( i < = 0x1f ) {
/* Control characters. */
char_flags [ i ] | = FLAG_ILLEGAL ;
}
2007-09-08 00:57:01 +04:00
if ( ( i > = ' 0 ' & & i < = ' 9 ' ) | |
( i > = ' a ' & & i < = ' z ' ) | |
2002-04-11 13:56:38 +04:00
( i > = ' A ' & & i < = ' Z ' ) ) {
char_flags [ i ] | = ( FLAG_ASCII | FLAG_BASECHAR ) ;
}
2002-04-11 17:35:28 +04:00
if ( strchr ( " _-$~ " , i ) ) {
2002-04-11 13:56:38 +04:00
char_flags [ i ] | = FLAG_ASCII ;
}
if ( strchr ( " * \\ /?<>| \" : " , i ) ) {
char_flags [ i ] | = FLAG_ILLEGAL ;
}
2002-04-11 14:52:59 +04:00
if ( strchr ( " *? \" <> " , i ) ) {
char_flags [ i ] | = FLAG_WILDCARD ;
}
2002-04-11 13:56:38 +04:00
}
memset ( base_reverse , 0 , sizeof ( base_reverse ) ) ;
for ( i = 0 ; i < 36 ; i + + ) {
base_reverse [ ( unsigned char ) base_forward ( i ) ] = i ;
2007-09-08 00:57:01 +04:00
}
2002-04-11 13:56:38 +04:00
/* fill in the reserved names flags. These are used as a very
fast filter for finding possible DOS reserved filenames */
for ( i = 0 ; reserved_names [ i ] ; i + + ) {
2002-04-11 14:52:59 +04:00
unsigned char c1 , c2 , c3 , c4 ;
2002-04-11 13:56:38 +04:00
c1 = ( unsigned char ) reserved_names [ i ] [ 0 ] ;
c2 = ( unsigned char ) reserved_names [ i ] [ 1 ] ;
c3 = ( unsigned char ) reserved_names [ i ] [ 2 ] ;
2002-04-11 14:52:59 +04:00
c4 = ( unsigned char ) reserved_names [ i ] [ 3 ] ;
2002-04-11 13:56:38 +04:00
char_flags [ c1 ] | = FLAG_POSSIBLE1 ;
char_flags [ c2 ] | = FLAG_POSSIBLE2 ;
char_flags [ c3 ] | = FLAG_POSSIBLE3 ;
2002-04-11 14:52:59 +04:00
char_flags [ c4 ] | = FLAG_POSSIBLE4 ;
2005-12-27 23:52:36 +03:00
char_flags [ tolower_ascii ( c1 ) ] | = FLAG_POSSIBLE1 ;
char_flags [ tolower_ascii ( c2 ) ] | = FLAG_POSSIBLE2 ;
char_flags [ tolower_ascii ( c3 ) ] | = FLAG_POSSIBLE3 ;
char_flags [ tolower_ascii ( c4 ) ] | = FLAG_POSSIBLE4 ;
2002-04-11 14:52:59 +04:00
char_flags [ ( unsigned char ) ' . ' ] | = FLAG_POSSIBLE4 ;
2002-04-11 13:56:38 +04:00
}
}
/*
the following provides the abstraction layer to make it easier
to drop in an alternative mangling implementation */
static struct mangle_fns mangle_fns = {
2005-05-06 12:07:39 +04:00
mangle_reset ,
2002-04-11 13:56:38 +04:00
is_mangled ,
2007-09-08 00:57:01 +04:00
must_mangle ,
2002-04-11 13:56:38 +04:00
is_8_3 ,
2007-09-08 00:57:01 +04:00
lookup_name_from_8_3 ,
hash2_name_to_8_3
2002-04-11 13:56:38 +04:00
} ;
/* return the methods for this mangling implementation */
struct mangle_fns * mangle_hash2_init ( void )
{
2002-10-22 00:40:23 +04:00
/* the mangle prefix can only be in the mange 1 to 6 */
mangle_prefix = lp_mangle_prefix ( ) ;
if ( mangle_prefix > 6 ) {
mangle_prefix = 6 ;
}
if ( mangle_prefix < 1 ) {
mangle_prefix = 1 ;
}
2002-04-11 13:56:38 +04:00
init_tables ( ) ;
mangle_reset ( ) ;
if ( ! cache_init ( ) ) {
return NULL ;
}
return & mangle_fns ;
}
2005-06-23 01:20:41 +04:00
static void posix_mangle_reset ( void )
{ ; }
2006-07-11 22:01:26 +04:00
static BOOL posix_is_mangled ( const char * s , const struct share_params * p )
2005-06-23 01:20:41 +04:00
{
return False ;
}
2007-09-08 00:57:01 +04:00
static BOOL posix_must_mangle ( const char * s , const struct share_params * p )
2005-06-23 01:20:41 +04:00
{
return False ;
}
2007-09-08 00:57:01 +04:00
static BOOL posix_is_8_3 ( const char * fname ,
BOOL check_case ,
BOOL allow_wildcards ,
const struct share_params * p )
2005-06-23 01:20:41 +04:00
{
return False ;
}
2007-09-08 00:57:01 +04:00
static BOOL posix_lookup_name_from_8_3 ( TALLOC_CTX * ctx ,
const char * in ,
char * * out , /* talloced on the given context. */
const struct share_params * p )
2005-06-23 01:20:41 +04:00
{
2007-09-08 00:57:01 +04:00
return False ;
}
static BOOL posix_name_to_8_3 ( const char * in ,
char out [ 13 ] ,
BOOL cache83 ,
int default_case ,
const struct share_params * p )
{
memset ( out , ' \0 ' , 13 ) ;
return True ;
2005-06-23 01:20:41 +04:00
}
/* POSIX paths backend - no mangle. */
static struct mangle_fns posix_mangle_fns = {
2007-09-08 00:57:01 +04:00
posix_mangle_reset ,
posix_is_mangled ,
posix_must_mangle ,
posix_is_8_3 ,
posix_lookup_name_from_8_3 ,
posix_name_to_8_3
2005-06-23 01:20:41 +04:00
} ;
struct mangle_fns * posix_mangle_init ( void )
{
return & posix_mangle_fns ;
}