summaryrefslogtreecommitdiffstats
path: root/source/smbd/mangle_hash2.c
diff options
context:
space:
mode:
Diffstat (limited to 'source/smbd/mangle_hash2.c')
-rw-r--r--source/smbd/mangle_hash2.c709
1 files changed, 0 insertions, 709 deletions
diff --git a/source/smbd/mangle_hash2.c b/source/smbd/mangle_hash2.c
deleted file mode 100644
index 62087e7e593..00000000000
--- a/source/smbd/mangle_hash2.c
+++ /dev/null
@@ -1,709 +0,0 @@
-/*
- Unix SMB/CIFS implementation.
- new hash based name mangling implementation
- Copyright (C) Andrew Tridgell 2002
- Copyright (C) Simo Sorce 2002
-
- 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.
-*/
-
-/*
- 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
- */
-
- /* 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
- */
-
-/*
- ===============================================================================
- 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"
-
-#if 1
-#define M_DEBUG(level, x) DEBUG(level, x)
-#else
-#define M_DEBUG(level, x)
-#endif
-
-/* these flags are used to mark characters in as having particular
- properties */
-#define FLAG_BASECHAR 1
-#define FLAG_ASCII 2
-#define FLAG_ILLEGAL 4
-#define FLAG_WILDCARD 8
-
-/* the "possible" flags are used as a fast way to find possible DOS
- reserved filenames */
-#define FLAG_POSSIBLE1 16
-#define FLAG_POSSIBLE2 32
-#define FLAG_POSSIBLE3 64
-#define FLAG_POSSIBLE4 128
-
-/* by default have a max of 4096 entries in the cache. */
-#ifndef MANGLE_CACHE_SIZE
-#define MANGLE_CACHE_SIZE 4096
-#endif
-
-#define FNV1_PRIME 0x01000193
-/*the following number is a fnv1 of the string: idra@samba.org 2002 */
-#define FNV1_INIT 0xa6b93095
-
-/* these tables are used to provide fast tests for characters */
-static unsigned char char_flags[256];
-
-#define FLAG_CHECK(c, flag) (char_flags[(unsigned char)(c)] & (flag))
-
-/*
- 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;
-
-/* 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;
-static u32 *prefix_cache_hashes;
-
-/* these are the characters we use in the 8.3 hash. Must be 36 chars long */
-static const char *basechars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
-static unsigned char base_reverse[256];
-#define base_forward(v) basechars[v]
-
-/* the list of reserved dos names - all of these are illegal */
-static const char *reserved_names[] =
-{ "AUX", "LOCK$", "CON", "COM1", "COM2", "COM3", "COM4",
- "LPT1", "LPT2", "LPT3", "NUL", "PRN", NULL };
-
-/*
- 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;
-
- /* 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 */
- strncpy(str, key, length);
- str[length] = 0;
- strupper_m(str);
-
- /* the length of a multi-byte string can change after a strupper_m */
- length = strlen(str);
-
- /* Set the initial value from the key size. */
- for (value = FNV1_INIT, i=0; i < length; i++) {
- value *= (u32)FNV1_PRIME;
- value ^= (u32)(str[i]);
- }
-
- /* note that we force it to a 31 bit hash, to keep within the limits
- of the 36^6 mangle space */
- return value & ~0x80000000;
-}
-
-/*
- initialise (ie. allocate) the prefix cache
- */
-static BOOL cache_init(void)
-{
- if (prefix_cache) return True;
-
- prefix_cache = calloc(MANGLE_CACHE_SIZE, sizeof(char *));
- if (!prefix_cache) return False;
-
- prefix_cache_hashes = calloc(MANGLE_CACHE_SIZE, sizeof(u32));
- if (!prefix_cache_hashes) return False;
-
- return True;
-}
-
-/*
- insert an entry into the prefix cache. The string might not be null
- terminated */
-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] = strndup(prefix, length);
- prefix_cache_hashes[i] = hash;
-}
-
-/*
- lookup an entry in the prefix cache. Return NULL if not found.
-*/
-static const char *cache_lookup(u32 hash)
-{
- int i = hash % MANGLE_CACHE_SIZE;
-
- if (!prefix_cache[i] || hash != prefix_cache_hashes[i]) {
- 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
- */
-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));
-
- /* check the length */
- if (len > 12 || len < 8)
- return False;
-
- /* the best distinguishing characteristic is the ~ */
- if (name[6] != '~')
- return False;
-
- /* check extension */
- 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;
- }
- }
- }
-
- /* check lead characters */
- for (i=0;i<mangle_prefix;i++) {
- if (! FLAG_CHECK(name[i], FLAG_ASCII)) {
- return False;
- }
- }
-
- /* check rest of hash */
- 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;
-}
-
-
-
-/*
- 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;
-
- 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;
- }
- }
-
- /* and the last part ... */
- return is_mangled_component(s,strlen(s));
-}
-
-
-/*
- 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)
-*/
-static BOOL is_8_3(const char *name, BOOL check_case, BOOL allow_wildcards)
-{
- 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);
- if (len > 12)
- return False;
-
- /* 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 || suffix_len == 0) {
- 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++) {
- /* note that we may allow wildcard petterns! */
- if (!FLAG_CHECK(name[i], FLAG_ASCII|(allow_wildcards ? FLAG_WILDCARD : 0)) && name[i] != '.') {
- 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;
- unsigned int i;
- const char *prefix;
- char extension[4];
-
- /* make sure that this is a mangled name from this cache */
- if (!is_mangled(name)) {
- M_DEBUG(10,("check_cache: %s -> not mangled\n", name));
- return False;
- }
-
- /* we need to extract the hash from the 8.3 name */
- 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;
- }
-
- /* now look in the prefix cache for that hash */
- prefix = cache_lookup(hash);
- if (!prefix) {
- M_DEBUG(10,("check_cache: %s -> %08X -> not found\n", name, hash));
- return False;
- }
-
- /* we found it - construct the full name */
- 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, sizeof(fstring), "%s.%s", prefix, extension);
- } else {
- M_DEBUG(10,("check_cache: %s -> %s\n", name, prefix));
- fstrcpy(name, prefix);
- }
-
- return True;
-}
-
-
-/*
- look for a DOS reserved name
-*/
-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)) {
- /* 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 (strnequal(name, reserved_names[i], len) &&
- (name[len] == '.' || name[len] == 0)) {
- return True;
- }
- }
- }
-
- return False;
-}
-
-/*
- See if a filename is a legal long filename.
- A filename ending in a '.' is not legal unless it's "." or "..". JRA.
-*/
-
-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)) {
- /* 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.
- */
- if (convert_string(CH_UNIX, CH_UCS2, name, 2, mbc, 2, False) == 2) {
- /* Was a good mb string. */
- 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; /* . or .. is a valid name */
-
- /* A valid long name cannot end in '.' */
- if (dot_pos[1] == '\0')
- return False;
- }
-
- 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
-*/
-static void name_map(fstring name, BOOL need83, BOOL cache83)
-{
- 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];
-
- /* 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 */
- if (is_8_3(name, False, False)) {
- return;
- }
-
- /* if the caller doesn't strictly need 8.3 then just check for illegal
- filenames */
- if (!need83 && is_legal_name(name)) {
- return;
- }
- }
-
- /* find the '.' if any */
- dot_p = strrchr(name, '.');
-
- 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;
- }
-
- /* the leading characters in the mangled name is taken from
- the first characters of the name, if they are ascii otherwise
- '_' is used
- */
- 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] = '_';
- }
-
- /* 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];
- if (FLAG_CHECK(c, FLAG_ASCII)) {
- extension[extension_length++] = toupper(c);
- }
- }
- }
-
- /* find the hash for this prefix */
- v = hash = mangle_hash(name, prefix_len);
-
- /* now form the mangled name. */
- 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);
- }
-
- /* 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);
- }
-
- M_DEBUG(10,("name_map: %s -> %08X -> %s (cache=%d)\n",
- name, hash, new_name, cache83));
-
- /* 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));
-
- 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;
- }
-
- /* 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++) {
- 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;
- }
-}
-
-
-/*
- 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)
-{
- /* 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;
- }
-
- init_tables();
- mangle_reset();
-
- if (!cache_init()) {
- return NULL;
- }
-
- return &mangle_fns;
-}