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
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 0213 9 , USA .
*/
/*
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 ;
2002-04-11 14:52:59 +04:00
static u32 * 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 ? )
*/
static u32 mangle_hash ( const char * key , unsigned length )
{
u32 value ;
u32 i ;
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 */
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 + + ) {
value * = ( u32 ) FNV1_PRIME ;
value ^ = ( u32 ) ( str [ i ] ) ;
}
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 )
{
if ( prefix_cache ) return True ;
2002-10-15 09:58:32 +04:00
prefix_cache = calloc ( MANGLE_CACHE_SIZE , sizeof ( char * ) ) ;
2002-04-11 13:56:38 +04:00
if ( ! prefix_cache ) return False ;
2002-10-15 09:58:32 +04:00
prefix_cache_hashes = calloc ( MANGLE_CACHE_SIZE , sizeof ( u32 ) ) ;
2002-04-11 14:52:59 +04:00
if ( ! prefix_cache_hashes ) return False ;
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 */
2002-04-11 13:56:38 +04:00
static void cache_insert ( const char * prefix , int length , u32 hash )
{
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 ] ) ;
}
prefix_cache [ i ] = 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 .
*/
static const char * cache_lookup ( u32 hash )
{
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
2003-08-27 22:21:26 +04:00
M_DEBUG ( 10 , ( " is_mangled_component %s (len %u) ? \n " , name , ( unsigned int ) 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 ;
2002-04-11 16:14:55 +04:00
for ( i = 9 ; name [ i ] ; i + + ) {
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
2003-08-27 22:17:33 +04:00
M_DEBUG ( 10 , ( " is_mangled_component %s (len %u) -> yes \n " , name , ( unsigned int ) 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
*/
static BOOL is_mangled ( const char * name )
{
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 )
*/
2002-07-15 14:35:28 +04:00
static BOOL is_8_3 ( const char * name , BOOL check_case , BOOL allow_wildcards )
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 ) ;
if ( prefix_len > 8 | | suffix_len > 3 ) {
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
replace the string with the original long name .
The filename must be able to hold at least sizeof ( fstring )
*/
static BOOL check_cache ( char * name )
{
u32 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 ] ;
/* make sure that this is a mangled name from this cache */
if ( ! is_mangled ( name ) ) {
2002-09-25 19:19:00 +04:00
M_DEBUG ( 10 , ( " check_cache: %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 - - ) {
2002-04-11 13:56:38 +04:00
u32 v = base_reverse [ ( unsigned char ) name [ i ] ] ;
hash + = multiplier * v ;
multiplier * = 36 ;
}
/* now look in the prefix cache for that hash */
prefix = cache_lookup ( hash ) ;
if ( ! prefix ) {
2002-09-25 19:19:00 +04:00
M_DEBUG ( 10 , ( " check_cache: %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 ] ) {
2002-09-25 19:19:00 +04:00
M_DEBUG ( 10 , ( " check_cache: %s -> %s.%s \n " , name , prefix , extension ) ) ;
2002-04-11 13:56:38 +04:00
slprintf ( name , sizeof ( fstring ) , " %s.%s " , prefix , extension ) ;
} else {
2002-09-25 19:19:00 +04:00
M_DEBUG ( 10 , ( " check_cache: %s -> %s \n " , name , prefix ) ) ;
2002-04-11 13:56:38 +04:00
fstrcpy ( name , prefix ) ;
}
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 */
if ( strncasecmp ( name , reserved_names [ i ] , len ) = = 0 & &
( 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 .
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 ] ;
/*
* We know the following will return 2 bytes . What
* we need to know was if errno was set .
* 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 .
*/
errno = 0 ;
convert_string ( CH_UNIX , CH_UCS2 , name , 2 , mbc , 2 ) ;
if ( ! errno ) {
/* 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 ;
}
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 ;
}
/*
the main forward mapping function , which converts a long filename to
a 8.3 name
if need83 is not set then we only do the mangling if the name is illegal
as a long name
if cache83 is not set then we don ' t cache the result
the name parameter must be able to hold 13 bytes
*/
2003-03-18 01:56:13 +03:00
static void name_map ( fstring name , BOOL need83 , BOOL cache83 )
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 ;
2002-04-11 13:56:38 +04:00
u32 hash , v ;
char new_name [ 13 ] ;
/* reserved names are handled specially */
if ( ! is_reserved_name ( name ) ) {
/* if the name is already a valid 8.3 name then we don't need to
do anything */
2002-07-15 14:35:28 +04:00
if ( is_8_3 ( name , False , False ) ) {
return ;
2002-04-11 13:56:38 +04:00
}
/* if the caller doesn't strictly need 8.3 then just check for illegal
filenames */
if ( ! need83 & & is_legal_name ( name ) ) {
2002-07-15 14:35:28 +04:00
return ;
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 ;
}
}
if ( i = = 0 | | i = = 4 ) dot_p = NULL ;
}
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 ] = ' _ ' ;
}
lead_chars [ i ] = toupper ( lead_chars [ i ] ) ;
}
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 ) ) {
2002-04-11 14:52:59 +04:00
extension [ extension_length + + ] = toupper ( c ) ;
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 ) ;
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 ) ;
}
2002-09-25 19:19:00 +04:00
M_DEBUG ( 10 , ( " name_map: %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
2002-04-11 13:56:38 +04:00
/* and overwrite the old name */
fstrcpy ( name , new_name ) ;
/* all done, we've managed to mangle it */
}
/* initialise the flags table
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
restrictions .
*/
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 + + ) {
2002-04-11 13:56:38 +04:00
if ( ( i > = ' 0 ' & & i < = ' 9 ' ) | |
( i > = ' a ' & & i < = ' z ' ) | |
( 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 ;
}
/* 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 ;
2002-04-11 13:56:38 +04:00
char_flags [ tolower ( c1 ) ] | = FLAG_POSSIBLE1 ;
char_flags [ tolower ( c2 ) ] | = FLAG_POSSIBLE2 ;
char_flags [ tolower ( c3 ) ] | = FLAG_POSSIBLE3 ;
2002-04-11 14:52:59 +04:00
char_flags [ tolower ( c4 ) ] | = FLAG_POSSIBLE4 ;
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 = {
is_mangled ,
is_8_3 ,
mangle_reset ,
check_cache ,
name_map
} ;
/* 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 ;
}