diff options
author | Andrew Tridgell <tridge@samba.org> | 2004-10-12 05:10:43 +0000 |
---|---|---|
committer | Gerald (Jerry) Carter <jerry@samba.org> | 2007-10-10 12:59:49 -0500 |
commit | 52f525c104ae2d17901f104cbf482395dc803cc1 (patch) | |
tree | 1ccd6d3879a0d82311f8796cb15e958b1e77ab5a | |
parent | c7130b816b29cab981646482e57cb6923ae91b4f (diff) | |
download | samba-52f525c104ae2d17901f104cbf482395dc803cc1.tar.gz samba-52f525c104ae2d17901f104cbf482395dc803cc1.tar.bz2 samba-52f525c104ae2d17901f104cbf482395dc803cc1.zip |
r2927: imported the hash2 name mangling code from Samba3 into Samba4, but
heavily modified to suit the Samba4 architecture.
Samba4 with posix backend now passes the BASE-MANGLE test
(This used to be commit ed52d69e8a065b6a8df2fb73c89be67acfdbca65)
-rw-r--r-- | source4/ntvfs/posix/pvfs_dirlist.c | 16 | ||||
-rw-r--r-- | source4/ntvfs/posix/pvfs_resolve.c | 21 | ||||
-rw-r--r-- | source4/ntvfs/posix/pvfs_shortname.c | 661 | ||||
-rw-r--r-- | source4/ntvfs/posix/vfs_posix.c | 6 | ||||
-rw-r--r-- | source4/ntvfs/posix/vfs_posix.h | 28 |
5 files changed, 693 insertions, 39 deletions
diff --git a/source4/ntvfs/posix/pvfs_dirlist.c b/source4/ntvfs/posix/pvfs_dirlist.c index 79d9a9a3fa..0482cda808 100644 --- a/source4/ntvfs/posix/pvfs_dirlist.c +++ b/source4/ntvfs/posix/pvfs_dirlist.c @@ -98,21 +98,19 @@ NTSTATUS pvfs_list(struct pvfs_state *pvfs, struct pvfs_filename *name, struct p while ((dent = readdir(odir))) { uint_t i = dir->count; const char *dname = dent->d_name; - char *short_name; - short_name = pvfs_short_name_component(pvfs, dname); - - /* check it matches the wildcard pattern */ if (ms_fnmatch(pattern, dname, - pvfs->tcon->smb_conn->negotiate.protocol) != 0 && - ms_fnmatch(pattern, short_name, pvfs->tcon->smb_conn->negotiate.protocol) != 0) { + char *short_name = pvfs_short_name_component(pvfs, dname); + if (short_name == NULL || + ms_fnmatch(pattern, short_name, + pvfs->tcon->smb_conn->negotiate.protocol) != 0) { + talloc_free(short_name); + continue; + } talloc_free(short_name); - continue; } - talloc_free(short_name); - if (dir->count >= allocated) { allocated = (allocated + 100) * 1.2; dir->names = talloc_realloc_p(dir, dir->names, const char *, allocated); diff --git a/source4/ntvfs/posix/pvfs_resolve.c b/source4/ntvfs/posix/pvfs_resolve.c index 5d6f270a42..5b1a5680ae 100644 --- a/source4/ntvfs/posix/pvfs_resolve.c +++ b/source4/ntvfs/posix/pvfs_resolve.c @@ -35,16 +35,18 @@ */ static int component_compare(struct pvfs_state *pvfs, const char *comp, const char *name) { - char *shortname; int ret; - if (StrCaseCmp(comp, name) == 0) return 0; + ret = StrCaseCmp(comp, name); - shortname = pvfs_short_name_component(pvfs, name); - - ret = StrCaseCmp(comp, shortname); + if (ret != 0) { + char *shortname = pvfs_short_name_component(pvfs, name); + if (shortname) { + ret = StrCaseCmp(comp, shortname); + talloc_free(shortname); + } + } - talloc_free(shortname); return ret; } @@ -95,6 +97,13 @@ static NTSTATUS pvfs_case_search(struct pvfs_state *pvfs, struct pvfs_filename * char *test_name; DIR *dir; struct dirent *de; + char *long_component; + + /* possibly remap from the short name cache */ + long_component = pvfs_mangled_lookup(pvfs, name, components[i]); + if (long_component) { + components[i] = long_component; + } test_name = talloc_asprintf(name, "%s/%s", partial_name, components[i]); if (!test_name) { diff --git a/source4/ntvfs/posix/pvfs_shortname.c b/source4/ntvfs/posix/pvfs_shortname.c index 81cf42e71f..818c285fbc 100644 --- a/source4/ntvfs/posix/pvfs_shortname.c +++ b/source4/ntvfs/posix/pvfs_shortname.c @@ -23,69 +23,682 @@ #include "include/includes.h" #include "vfs_posix.h" +/* + 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" +#include "vfs_posix.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 512 entries in the cache. */ +#ifndef MANGLE_CACHE_SIZE +#define MANGLE_CACHE_SIZE 512 +#endif + +#define DEFAULT_MANGLE_PREFIX 4 + #define FNV1_PRIME 0x01000193 /*the following number is a fnv1 of the string: idra@samba.org 2002 */ #define FNV1_INIT 0xa6b93095 +#define MANGLE_BASECHARS "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" + +#define FLAG_CHECK(c, flag) (ctx->char_flags[(uint8_t)(c)] & (flag)) + + /* 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 uint32_t mangle_hash(const char *key) +static uint32_t mangle_hash(struct pvfs_mangle_context *ctx, + const char *key, size_t length) { - uint32_t value; + uint32_t value = FNV1_INIT; codepoint_t c; size_t c_size; - for (value = FNV1_INIT; - (c=next_codepoint(key, &c_size)); - key += c_size) { + while (*key && length--) { + c = next_codepoint(key, &c_size); c = toupper_w(c); value *= (uint32_t)FNV1_PRIME; value ^= (uint32_t)c; + key += c_size; } - /* note that we force it to a 31 bit hash, to keep within the limits - of the 36^6 mangle space */ - return value & ~0x80000000; + return (value % ctx->mangle_modulus); } /* - return the short name for a component of a full name - TODO: this is obviously not very useful in its current form ! + insert an entry into the prefix cache. The string might not be null + terminated */ +static void cache_insert(struct pvfs_mangle_context *ctx, + const char *prefix, int length, uint32_t hash) +{ + int i = hash % MANGLE_CACHE_SIZE; + + if (ctx->prefix_cache[i]) { + talloc_free(ctx->prefix_cache[i]); + } + + ctx->prefix_cache[i] = talloc_strndup(ctx->prefix_cache, prefix, length); + ctx->prefix_cache_hashes[i] = hash; +} + +/* + lookup an entry in the prefix cache. Return NULL if not found. */ -char *pvfs_short_name_component(struct pvfs_state *pvfs, const char *name) +static const char *cache_lookup(struct pvfs_mangle_context *ctx, uint32_t hash) { - const char *basechars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; - uint32_t hash; - char c1, c2; - const char *ext; + int i = hash % MANGLE_CACHE_SIZE; + + + if (!ctx->prefix_cache[i] || hash != ctx->prefix_cache_hashes[i]) { + return NULL; + } + + /* yep, it matched */ + return ctx->prefix_cache[i]; +} - if (strlen(name) <= 12) { - return talloc_strdup(pvfs, name); + +/* + 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(struct pvfs_mangle_context *ctx, + 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<ctx->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=ctx->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; +} - hash = mangle_hash(name); - ext = strrchr(name, '.'); - c1 = basechars[(hash/36)%36]; - c2 = basechars[hash%36]; - return talloc_asprintf(pvfs, "%.5s~%c%c%.4s", name, c1, c2, ext?ext:""); +/* + 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(struct pvfs_mangle_context *ctx, 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(ctx, s, PTR_DIFF(p, s))) { + return True; + } + } + /* and the last part ... */ + return is_mangled_component(ctx, 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(struct pvfs_mangle_context *ctx, + 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; +} + + +/* + try to find a 8.3 name in the cache, and if found then + return the original long name. +*/ +static const char *check_cache(struct pvfs_mangle_context *ctx, + const char *name) +{ + uint32_t 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(ctx, name)) { + M_DEBUG(10,("check_cache: %s -> not mangled\n", name)); + return NULL; + } + + /* we need to extract the hash from the 8.3 name */ + hash = ctx->base_reverse[(unsigned char)name[7]]; + for (multiplier=36, i=5;i>=ctx->mangle_prefix;i--) { + uint32_t v = ctx->base_reverse[(unsigned char)name[i]]; + hash += multiplier * v; + multiplier *= 36; + } + + /* now look in the prefix cache for that hash */ + prefix = cache_lookup(ctx, hash); + if (!prefix) { + M_DEBUG(10,("check_cache: %s -> %08X -> not found\n", name, hash)); + return NULL; + } + + /* 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]) { + return talloc_asprintf(ctx, "%s.%s", prefix, extension); + } + + return talloc_strdup(ctx, prefix); +} + + +/* + look for a DOS reserved name +*/ +static BOOL is_reserved_name(struct pvfs_mangle_context *ctx, 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; ctx->reserved_names[i]; i++) { + int len = strlen(ctx->reserved_names[i]); + /* note that we match on COM1 as well as COM1.foo */ + if (strncasecmp(name, ctx->reserved_names[i], len) == 0 && + (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(struct pvfs_mangle_context *ctx, const char *name) +{ + const char *dot_pos = NULL; + BOOL alldots = True; + size_t numdots = 0; + + while (*name) { + size_t c_size; + codepoint_t c = next_codepoint(name, &c_size); + if (c == INVALID_CODEPOINT) { + return False; + } + /* all high chars are OK */ + if (c >= 128) { + name += c_size; + continue; + } + if (FLAG_CHECK(c, FLAG_ILLEGAL)) { + return False; + } + if (name[0] == '.') { + dot_pos = name; + numdots++; + } else { + alldots = False; + } + + name += c_size; + } + + 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 + + return NULL if we don't need to do any conversion +*/ +static char *name_map(struct pvfs_mangle_context *ctx, + const char *name, BOOL need83, BOOL cache83) +{ + char *dot_p; + char lead_chars[7]; + char extension[4]; + unsigned int extension_length, i; + unsigned int prefix_len; + uint32_t hash, v; + char *new_name; + const char *basechars = MANGLE_BASECHARS; + + /* reserved names are handled specially */ + if (!is_reserved_name(ctx, name)) { + /* if the name is already a valid 8.3 name then we don't need to + do anything */ + if (is_8_3(ctx, name, False, False)) { + return NULL; + } + + /* if the caller doesn't strictly need 8.3 then just check for illegal + filenames */ + if (!need83 && is_legal_name(ctx, name)) { + return NULL; + } + } + + /* 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<ctx->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<ctx->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(ctx, name, prefix_len); + + new_name = talloc_array_p(ctx, char, 13); + if (new_name == NULL) { + return NULL; + } + + /* now form the mangled name. */ + for (i=0;i<ctx->mangle_prefix;i++) { + new_name[i] = lead_chars[i]; + } + new_name[7] = basechars[v % 36]; + new_name[6] = '~'; + for (i=5; i>=ctx->mangle_prefix; i--) { + v = v / 36; + new_name[i] = basechars[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(ctx, name, prefix_len, hash); + } + + M_DEBUG(10,("name_map: %s -> %08X -> %s (cache=%d)\n", + name, hash, new_name, cache83)); + + return new_name; +} + + +/* 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(struct pvfs_mangle_context *ctx) +{ + const char *basechars = MANGLE_BASECHARS; + int i; + /* the list of reserved dos names - all of these are illegal */ + const char *reserved_names[] = + { "AUX", "LOCK$", "CON", "COM1", "COM2", "COM3", "COM4", + "LPT1", "LPT2", "LPT3", "NUL", "PRN", NULL }; + + + ZERO_STRUCT(ctx->char_flags); + + for (i=1;i<128;i++) { + if ((i >= '0' && i <= '9') || + (i >= 'a' && i <= 'z') || + (i >= 'A' && i <= 'Z')) { + ctx->char_flags[i] |= (FLAG_ASCII | FLAG_BASECHAR); + } + if (strchr("_-$~", i)) { + ctx->char_flags[i] |= FLAG_ASCII; + } + + if (strchr("*\\/?<>|\":", i)) { + ctx->char_flags[i] |= FLAG_ILLEGAL; + } + + if (strchr("*?\"<>", i)) { + ctx->char_flags[i] |= FLAG_WILDCARD; + } + } + + ZERO_STRUCT(ctx->base_reverse); + for (i=0;i<36;i++) { + ctx->base_reverse[(uint8_t)basechars[i]] = i; + } + + ctx->reserved_names = reserved_names; + + /* fill in the reserved names flags. These are used as a very + fast filter for finding possible DOS reserved filenames */ + for (i=0; ctx->reserved_names[i]; i++) { + unsigned char c1, c2, c3, c4; + + c1 = (unsigned char)ctx->reserved_names[i][0]; + c2 = (unsigned char)ctx->reserved_names[i][1]; + c3 = (unsigned char)ctx->reserved_names[i][2]; + c4 = (unsigned char)ctx->reserved_names[i][3]; + + ctx->char_flags[c1] |= FLAG_POSSIBLE1; + ctx->char_flags[c2] |= FLAG_POSSIBLE2; + ctx->char_flags[c3] |= FLAG_POSSIBLE3; + ctx->char_flags[c4] |= FLAG_POSSIBLE4; + ctx->char_flags[tolower(c1)] |= FLAG_POSSIBLE1; + ctx->char_flags[tolower(c2)] |= FLAG_POSSIBLE2; + ctx->char_flags[tolower(c3)] |= FLAG_POSSIBLE3; + ctx->char_flags[tolower(c4)] |= FLAG_POSSIBLE4; + + ctx->char_flags[(unsigned char)'.'] |= FLAG_POSSIBLE4; + } + + ctx->mangle_modulus = 1; + for (i=0;i<(7-ctx->mangle_prefix);i++) { + ctx->mangle_modulus *= 36; + } +} + +/* + initialise the mangling code + */ +NTSTATUS pvfs_mangle_init(struct pvfs_state *pvfs) +{ + struct pvfs_mangle_context *ctx; + + ctx = talloc_p(pvfs, struct pvfs_mangle_context); + if (ctx == NULL) { + return NT_STATUS_NO_MEMORY; + } + ctx->prefix_cache = talloc_array_p(ctx, char *, MANGLE_CACHE_SIZE); + if (ctx->prefix_cache == NULL) { + return NT_STATUS_NO_MEMORY; + } + ctx->prefix_cache_hashes = talloc_array_p(ctx, uint32_t, MANGLE_CACHE_SIZE); + if (ctx->prefix_cache_hashes == NULL) { + return NT_STATUS_NO_MEMORY; + } + + memset(ctx->prefix_cache, 0, sizeof(char *)*MANGLE_CACHE_SIZE); + memset(ctx->prefix_cache_hashes, 0, sizeof(uint32_t)*MANGLE_CACHE_SIZE); + + ctx->mangle_prefix = lp_parm_int(-1, "mangle", "prefix"); + if (ctx->mangle_prefix < 0 || ctx->mangle_prefix > 6) { + ctx->mangle_prefix = DEFAULT_MANGLE_PREFIX; + } + + init_tables(ctx); + + pvfs->mangle_ctx = ctx; + + return NT_STATUS_OK; +} + + +/* + return the short name for a component of a full name +*/ +char *pvfs_short_name_component(struct pvfs_state *pvfs, const char *name) +{ + return name_map(pvfs->mangle_ctx, name, True, True); } /* return the short name for a given entry in a directory - TODO: this is obviously not very useful in its current form ! */ -char *pvfs_short_name(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx, struct pvfs_filename *name) +const char *pvfs_short_name(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx, + struct pvfs_filename *name) { char *p = strrchr(name->full_name, '/'); char *ret = pvfs_short_name_component(pvfs, p+1); + if (ret == NULL) { + return p+1; + } talloc_steal(mem_ctx, ret); return ret; } + +/* + lookup a mangled name, returning the original long name if present + in the cache +*/ +char *pvfs_mangled_lookup(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx, + const char *name) +{ + const char *ret; + ret = check_cache(pvfs->mangle_ctx, name); + if (ret) { + return talloc_steal(mem_ctx, ret); + } + return NULL; +} diff --git a/source4/ntvfs/posix/vfs_posix.c b/source4/ntvfs/posix/vfs_posix.c index 1990f77f82..7d532c3596 100644 --- a/source4/ntvfs/posix/vfs_posix.c +++ b/source4/ntvfs/posix/vfs_posix.c @@ -57,6 +57,7 @@ static NTSTATUS pvfs_connect(struct ntvfs_module_context *ntvfs, struct pvfs_state *pvfs; struct stat st; char *base_directory; + NTSTATUS status; pvfs = talloc_p(tcon, struct pvfs_state); if (pvfs == NULL) { @@ -84,6 +85,11 @@ static NTSTATUS pvfs_connect(struct ntvfs_module_context *ntvfs, ntvfs->private_data = pvfs; + status = pvfs_mangle_init(pvfs); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + pvfs_setup_options(pvfs); return NT_STATUS_OK; diff --git a/source4/ntvfs/posix/vfs_posix.h b/source4/ntvfs/posix/vfs_posix.h index 0daacf2c33..f6faf718be 100644 --- a/source4/ntvfs/posix/vfs_posix.h +++ b/source4/ntvfs/posix/vfs_posix.h @@ -49,6 +49,8 @@ struct pvfs_state { } search; struct pvfs_file *open_files; + + struct pvfs_mangle_context *mangle_ctx; }; @@ -115,6 +117,32 @@ struct pvfs_file { uint16_t smbpid; }; +struct pvfs_mangle_context { + uint8_t char_flags[256]; + /* + 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. + */ + int mangle_prefix; + uint32_t mangle_modulus; + + /* 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 + */ + char **prefix_cache; + uint32_t *prefix_cache_hashes; + + /* this is used to reverse the base 36 mapping */ + unsigned char base_reverse[256]; + + const char **reserved_names; +}; + + /* flags to pvfs_resolve_name() */ #define PVFS_RESOLVE_NO_WILDCARD (1<<0) |