mirror of
https://github.com/samba-team/samba.git
synced 2024-12-31 17:18:04 +03:00
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 ed52d69e8a
)
This commit is contained in:
parent
c7130b816b
commit
52f525c104
@ -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);
|
||||
|
@ -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);
|
||||
if (ret != 0) {
|
||||
char *shortname = pvfs_short_name_component(pvfs, name);
|
||||
if (shortname) {
|
||||
ret = StrCaseCmp(comp, shortname);
|
||||
talloc_free(shortname);
|
||||
}
|
||||
}
|
||||
|
||||
ret = StrCaseCmp(comp, 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) {
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
static const char *cache_lookup(struct pvfs_mangle_context *ctx, uint32_t hash)
|
||||
{
|
||||
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];
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
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
|
||||
TODO: this is obviously not very useful in its current form !
|
||||
*/
|
||||
char *pvfs_short_name_component(struct pvfs_state *pvfs, const char *name)
|
||||
{
|
||||
const char *basechars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
uint32_t hash;
|
||||
char c1, c2;
|
||||
const char *ext;
|
||||
|
||||
if (strlen(name) <= 12) {
|
||||
return talloc_strdup(pvfs, name);
|
||||
}
|
||||
|
||||
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:"");
|
||||
|
||||
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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user