#include "includes.h"
#if 1
#define M_DEBUG(level, x) DEBUG(level, x)
#else
#define M_DEBUG(level, x)
#endif
#define FLAG_BASECHAR 1
#define FLAG_ASCII 2
#define FLAG_ILLEGAL 4
#define FLAG_WILDCARD 8
#define FLAG_POSSIBLE1 16
#define FLAG_POSSIBLE2 32
#define FLAG_POSSIBLE3 64
#define FLAG_POSSIBLE4 128
#ifndef MANGLE_CACHE_SIZE
#define MANGLE_CACHE_SIZE 4096
#endif
#define FNV1_PRIME 0x01000193
#define FNV1_INIT 0xa6b93095
static unsigned char char_flags[256];
#define FLAG_CHECK(c, flag) (char_flags[(unsigned char)(c)] & (flag))
static unsigned mangle_prefix;
static char **prefix_cache;
static u32 *prefix_cache_hashes;
static const char *basechars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
static unsigned char base_reverse[256];
#define base_forward(v) basechars[v]
static const char *reserved_names[] =
{ "AUX", "LOCK$", "CON", "COM1", "COM2", "COM3", "COM4",
"LPT1", "LPT2", "LPT3", "NUL", "PRN", NULL };
static u32 mangle_hash(const char *key, unsigned int length)
{
u32 value;
u32 i;
fstring str;
length = MIN(length,sizeof(fstring)-1);
strncpy(str, key, length);
str[length] = 0;
strupper_m(str);
length = strlen(str);
for (value = FNV1_INIT, i=0; i < length; i++) {
value *= (u32)FNV1_PRIME;
value ^= (u32)(str[i]);
}
return value & ~0x80000000;
}
static BOOL cache_init(void)
{
if (prefix_cache) {
return True;
}
prefix_cache = SMB_CALLOC_ARRAY(char *,MANGLE_CACHE_SIZE);
if (!prefix_cache) {
return False;
}
prefix_cache_hashes = SMB_CALLOC_ARRAY(u32, MANGLE_CACHE_SIZE);
if (!prefix_cache_hashes) {
return False;
}
return True;
}
static void cache_insert(const char *prefix, int length, u32 hash)
{
int i = hash % MANGLE_CACHE_SIZE;
if (prefix_cache[i]) {
free(prefix_cache[i]);
}
prefix_cache[i] = SMB_STRNDUP(prefix, length);
prefix_cache_hashes[i] = hash;
}
static const char *cache_lookup(u32 hash)
{
int i = hash % MANGLE_CACHE_SIZE;
if (!prefix_cache[i] || hash != prefix_cache_hashes[i]) {
return NULL;
}
return prefix_cache[i];
}
static BOOL is_mangled_component(const char *name, size_t len)
{
unsigned int i;
M_DEBUG(10,("is_mangled_component %s (len %u) ?\n", name, (unsigned int)len));
if (len > 12 || len < 8)
return False;
if (name[6] != '~')
return False;
if (len > 8) {
if (name[8] != '.')
return False;
for (i=9; name[i] && i < len; i++) {
if (! FLAG_CHECK(name[i], FLAG_ASCII)) {
return False;
}
}
}
for (i=0;i<mangle_prefix;i++) {
if (! FLAG_CHECK(name[i], FLAG_ASCII)) {
return False;
}
}
if (! FLAG_CHECK(name[7], FLAG_BASECHAR)) {
return False;
}
for (i=mangle_prefix;i<6;i++) {
if (! FLAG_CHECK(name[i], FLAG_BASECHAR)) {
return False;
}
}
M_DEBUG(10,("is_mangled_component %s (len %u) -> yes\n", name, (unsigned int)len));
return True;
}
static BOOL is_mangled(const char *name)
{
const char *p;
const char *s;
M_DEBUG(10,("is_mangled %s ?\n", name));
for (s=name; (p=strchr(s, '/')); s=p+1) {
if (is_mangled_component(s, PTR_DIFF(p, s))) {
return True;
}
}
return is_mangled_component(s,strlen(s));
}
static BOOL is_8_3(const char *name, BOOL check_case, BOOL allow_wildcards)
{
int len, i;
char *dot_p;
if (name[0] == '.') {
if (!name[1] || (name[1] == '.' && !name[2])) {
return True;
}
}
len = strlen(name);
if (len > 12)
return False;
dot_p = strchr(name, '.');
if (!dot_p) {
if (len > 8) {
return False;
}
} else {
int prefix_len, suffix_len;
prefix_len = PTR_DIFF(dot_p, name);
suffix_len = len - (prefix_len+1);
if (prefix_len > 8 || suffix_len > 3 || suffix_len == 0) {
return False;
}
if (strchr(dot_p+1, '.')) {
return False;
}
}
for (i=0; name[i]; i++) {
if (!FLAG_CHECK(name[i], FLAG_ASCII|(allow_wildcards ? FLAG_WILDCARD : 0)) && name[i] != '.') {
return False;
}
}
return True;
}
static void mangle_reset(void)
{
}
static BOOL check_cache(char *name, size_t maxlen)
{
u32 hash, multiplier;
unsigned int i;
const char *prefix;
char extension[4];
if (!is_mangled(name)) {
M_DEBUG(10,("check_cache: %s -> not mangled\n", name));
return False;
}
hash = base_reverse[(unsigned char)name[7]];
for (multiplier=36, i=5;i>=mangle_prefix;i--) {
u32 v = base_reverse[(unsigned char)name[i]];
hash += multiplier * v;
multiplier *= 36;
}
prefix = cache_lookup(hash);
if (!prefix) {
M_DEBUG(10,("check_cache: %s -> %08X -> not found\n", name, hash));
return False;
}
if (name[8] == '.') {
strncpy(extension, name+9, 3);
extension[3] = 0;
} else {
extension[0] = 0;
}
if (extension[0]) {
M_DEBUG(10,("check_cache: %s -> %s.%s\n", name, prefix, extension));
slprintf(name, maxlen, "%s.%s", prefix, extension);
} else {
M_DEBUG(10,("check_cache: %s -> %s\n", name, prefix));
safe_strcpy(name, prefix, maxlen);
}
return True;
}
static BOOL is_reserved_name(const char *name)
{
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)) {
int i;
for (i=0; reserved_names[i]; i++) {
int len = strlen(reserved_names[i]);
if (strnequal(name, reserved_names[i], len) &&
(name[len] == '.' || name[len] == 0)) {
return True;
}
}
}
return False;
}
static BOOL is_legal_name(const char *name)
{
const char *dot_pos = NULL;
BOOL alldots = True;
size_t numdots = 0;
while (*name) {
if (((unsigned int)name[0]) > 128 && (name[1] != 0)) {
char mbc[2];
if (convert_string(CH_UNIX, CH_UCS2, name, 2, mbc, 2, False) == 2) {
name += 2;
continue;
}
}
if (FLAG_CHECK(name[0], FLAG_ILLEGAL)) {
return False;
}
if (name[0] == '.') {
dot_pos = name;
numdots++;
} else {
alldots = False;
}
name++;
}
if (dot_pos) {
if (alldots && (numdots == 1 || numdots == 2))
return True;
if (dot_pos[1] == '\0')
return False;
}
return True;
}
static void name_map(fstring name, BOOL need83, BOOL cache83, int default_case)
{
char *dot_p;
char lead_chars[7];
char extension[4];
unsigned int extension_length, i;
unsigned int prefix_len;
u32 hash, v;
char new_name[13];
if (!is_reserved_name(name)) {
if (is_8_3(name, False, False)) {
return;
}
if (!need83 && is_legal_name(name)) {
return;
}
}
dot_p = strrchr(name, '.');
if (dot_p) {
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;
}
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] = '_';
}
if (dot_p) {
prefix_len = PTR_DIFF(dot_p, name);
} else {
prefix_len = strlen(name);
}
extension_length = 0;
if (dot_p) {
for (i=1; extension_length < 3 && dot_p[i]; i++) {
char c = dot_p[i];
if (FLAG_CHECK(c, FLAG_ASCII)) {
extension[extension_length++] = toupper(c);
}
}
}
v = hash = mangle_hash(name, prefix_len);
for (i=0;i<mangle_prefix;i++) {
new_name[i] = lead_chars[i];
}
new_name[7] = base_forward(v % 36);
new_name[6] = '~';
for (i=5; i>=mangle_prefix; i--) {
v = v / 36;
new_name[i] = base_forward(v % 36);
}
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) {
cache_insert(name, prefix_len, hash);
}
M_DEBUG(10,("name_map: %s -> %08X -> %s (cache=%d)\n",
name, hash, new_name, cache83));
fstrcpy(name, new_name);
}
static void init_tables(void)
{
int i;
memset(char_flags, 0, sizeof(char_flags));
for (i=1;i<128;i++) {
if ((i >= '0' && i <= '9') ||
(i >= 'a' && i <= 'z') ||
(i >= 'A' && i <= 'Z')) {
char_flags[i] |= (FLAG_ASCII | FLAG_BASECHAR);
}
if (strchr("_-$~", i)) {
char_flags[i] |= FLAG_ASCII;
}
if (strchr("*\\/?<>|\":", i)) {
char_flags[i] |= FLAG_ILLEGAL;
}
if (strchr("*?\"<>", i)) {
char_flags[i] |= FLAG_WILDCARD;
}
}
memset(base_reverse, 0, sizeof(base_reverse));
for (i=0;i<36;i++) {
base_reverse[(unsigned char)base_forward(i)] = i;
}
for (i=0; reserved_names[i]; i++) {
unsigned char c1, c2, c3, c4;
c1 = (unsigned char)reserved_names[i][0];
c2 = (unsigned char)reserved_names[i][1];
c3 = (unsigned char)reserved_names[i][2];
c4 = (unsigned char)reserved_names[i][3];
char_flags[c1] |= FLAG_POSSIBLE1;
char_flags[c2] |= FLAG_POSSIBLE2;
char_flags[c3] |= FLAG_POSSIBLE3;
char_flags[c4] |= FLAG_POSSIBLE4;
char_flags[tolower(c1)] |= FLAG_POSSIBLE1;
char_flags[tolower(c2)] |= FLAG_POSSIBLE2;
char_flags[tolower(c3)] |= FLAG_POSSIBLE3;
char_flags[tolower(c4)] |= FLAG_POSSIBLE4;
char_flags[(unsigned char)'.'] |= FLAG_POSSIBLE4;
}
}
static struct mangle_fns mangle_fns = {
is_mangled,
is_8_3,
mangle_reset,
check_cache,
name_map
};
struct mangle_fns *mangle_hash2_init(void)
{
mangle_prefix = lp_mangle_prefix();
if (mangle_prefix > 6) {
mangle_prefix = 6;
}
if (mangle_prefix < 1) {
mangle_prefix = 1;
}
init_tables();
mangle_reset();
if (!cache_init()) {
return NULL;
}
return &mangle_fns;
}