r23844: Add patch series from Volker (after review and consultation).
authorJeremy Allison <jra@samba.org>
Wed, 11 Jul 2007 22:39:11 +0000 (22:39 +0000)
committerGerald (Jerry) Carter <jerry@samba.org>
Wed, 10 Oct 2007 17:28:34 +0000 (12:28 -0500)
0001-Save-a-strdup-in-stat_cache_add.patch
0002-Use-ISDOT-and-ISDOTDOT.patch
0003-Move-fname_equal-around.patch
0004-unix_convert-pstring-dirpath-char.patch
0005-Ignore-.o-files.patch
0006-Get-rid-of-pstrings-inside-unix_convert.patch
0007-revert-pstring-unix_convert.patch
0008-Make-name-an-allocated-pstring-inside-unix_convert.patch
0009-Pass-explicit-pstring-to-mangle_check_cache.patch
0010-Don-t-overwrite-orig_path-unnecessarily.patch
0011-Defer-allocating-name.patch
0012-Make-sure-dirpath-is-always-correctly-allocated.patch
0013-Remove-one-pstring-dependency-in-unix_convert.patch
0014-Remove-more-name-pstring-dependencies.patch
0015-Hide-the-nasty-API-of-mangle_check_cache-in-mangle_c.patch
0016-name-does-not-need-to-be-pstring-size-anymore.patch
0017-Make-use-of-ISDOT-and-ISDOTDOT.patch
0018-Remove-pstring-from-stat_cache_lookup.patch
0019-Add-my-copyright.patch

To remove pstrings from statcache and unix_convert.

Jeremy.
(This used to be commit ea6ef368891af24164d2e76700c405a82b3dfc19)

source3/smbd/filename.c
source3/smbd/mangle.c
source3/smbd/statcache.c

index 737ed5a89c93e9928a16787dd0e195d34339f981..fd7727c594746d686be0b32ac4a346dff1c5b94d 100644 (file)
@@ -4,6 +4,7 @@
    Copyright (C) Andrew Tridgell 1992-1998
    Copyright (C) Jeremy Allison 1999-2004
    Copyright (C) Ying Chen 2000
+   Copyright (C) Volker Lendecke 2007
    
    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
 
 #include "includes.h"
 
-static BOOL scan_directory(connection_struct *conn, const char *path, char *name,size_t maxlength);
-
-/****************************************************************************
- Check if two filenames are equal.
- This needs to be careful about whether we are case sensitive.
-****************************************************************************/
-
-static BOOL fname_equal(const char *name1, const char *name2, BOOL case_sensitive)
-{
-       /* Normal filename handling */
-       if (case_sensitive)
-               return(strcmp(name1,name2) == 0);
-
-       return(strequal(name1,name2));
-}
+static BOOL scan_directory(connection_struct *conn, const char *path,
+                          char *name, char **found_name);
 
 /****************************************************************************
  Mangle the 2nd name and check if it is then equal to the first name.
@@ -117,22 +105,21 @@ for nlinks = 0, which can never be true for any file).
 ****************************************************************************/
 
 NTSTATUS unix_convert(connection_struct *conn,
-                       pstring name,
+                       pstring orig_path,
                        BOOL allow_wcard_last_component,
-                       char *saved_last_component, 
+                       char *saved_last_component,
                        SMB_STRUCT_STAT *pst)
 {
        SMB_STRUCT_STAT st;
        char *start, *end;
-       pstring dirpath;
-       pstring orig_path;
+       char *dirpath = NULL;
+       char *name = NULL;
        BOOL component_was_mangled = False;
        BOOL name_has_wildcard = False;
+       NTSTATUS result;
 
        SET_STAT_INVALID(*pst);
 
-       *dirpath = 0;
-
        if(saved_last_component) {
                *saved_last_component = 0;
        }
@@ -143,19 +130,19 @@ NTSTATUS unix_convert(connection_struct *conn,
                return NT_STATUS_OK;
        }
 
-       DEBUG(5, ("unix_convert called on file \"%s\"\n", name));
+       DEBUG(5, ("unix_convert called on file \"%s\"\n", orig_path));
 
-       /* 
+       /*
         * Conversion to basic unix format is already done in check_path_syntax().
         */
 
-       /* 
+       /*
         * Names must be relative to the root of the service - any leading /.
         * and trailing /'s should have been trimmed by check_path_syntax().
         */
 
 #ifdef DEVELOPER
-       SMB_ASSERT(*name != '/');
+       SMB_ASSERT(*orig_path != '/');
 #endif
 
        /*
@@ -166,23 +153,27 @@ NTSTATUS unix_convert(connection_struct *conn,
         * As we know this is valid we can return true here.
         */
 
-       if (!*name) {
-               name[0] = '.';
-               name[1] = '\0';
+       if (!*orig_path) {
+               if (!(name = SMB_STRDUP("."))) {
+                       result = NT_STATUS_NO_MEMORY;
+                       goto fail;
+               }
                if (SMB_VFS_STAT(conn,name,&st) == 0) {
                        *pst = st;
                }
                DEBUG(5,("conversion finished \"\" -> %s\n",name));
-               return NT_STATUS_OK;
+               goto done;
        }
 
-       if (name[0] == '.' && (name[1] == '/' || name[1] == '\0')) {
+       if (orig_path[0] == '.' && (orig_path[1] == '/' || orig_path[1] == '\0')) {
                /* Start of pathname can't be "." only. */
-               if (name[1] == '\0' || name[2] == '\0') {
-                       return NT_STATUS_OBJECT_NAME_INVALID;
+               if (orig_path[1] == '\0' || orig_path[2] == '\0') {
+                       result = NT_STATUS_OBJECT_NAME_INVALID;
                } else {
-                       return determine_path_error(&name[2], allow_wcard_last_component);
+                       result =determine_path_error(
+                               &orig_path[2], allow_wcard_last_component);
                }
+               goto fail;
        }
 
        /*
@@ -190,14 +181,19 @@ NTSTATUS unix_convert(connection_struct *conn,
         */
 
        if(saved_last_component) {
-               end = strrchr_m(name, '/');
+               end = strrchr_m(orig_path, '/');
                if (end) {
                        pstrcpy(saved_last_component, end + 1);
                } else {
-                       pstrcpy(saved_last_component, name);
+                       pstrcpy(saved_last_component, orig_path);
                }
        }
 
+       if (!(name = SMB_STRDUP(orig_path))) {
+               DEBUG(0, ("strdup failed\n"));
+               return NT_STATUS_NO_MEMORY;
+       }
+
        /*
         * Large directory fix normalization. If we're case sensitive, and
         * the case preserving parameters are set to "no", normalize the case of
@@ -210,16 +206,27 @@ NTSTATUS unix_convert(connection_struct *conn,
        if (conn->case_sensitive && !conn->case_preserve && !conn->short_case_preserve) {
                strnorm(name, lp_defaultcase(SNUM(conn)));
        }
-       
+
        start = name;
-       pstrcpy(orig_path, name);
 
-       if(!conn->case_sensitive && stat_cache_lookup(conn, name, dirpath, &start, &st)) {
+       if(!conn->case_sensitive
+          && stat_cache_lookup(conn, &name, &dirpath, &start, &st)) {
                *pst = st;
-               return NT_STATUS_OK;
+               goto done;
+       }
+
+       /*
+        * Make sure "dirpath" is an allocated string, we use this for
+        * building the directories with asprintf and free it.
+        */
+
+       if ((dirpath == NULL) && (!(dirpath = SMB_STRDUP("")))) {
+               DEBUG(0, ("strdup failed\n"));
+               result = NT_STATUS_NO_MEMORY;
+               goto fail;
        }
 
-       /* 
+       /*
         * stat the name - if it exists then we are all done!
         */
 
@@ -239,24 +246,24 @@ NTSTATUS unix_convert(connection_struct *conn,
                stat_cache_add(orig_path, name, conn->case_sensitive);
                DEBUG(5,("conversion finished %s -> %s\n",orig_path, name));
                *pst = st;
-               return NT_STATUS_OK;
+               goto done;
        }
 
        DEBUG(5,("unix_convert begin: name = %s, dirpath = %s, start = %s\n", name, dirpath, start));
 
-       /* 
+       /*
         * A special case - if we don't have any mangling chars and are case
         * sensitive then searching won't help.
         */
 
-       if (conn->case_sensitive && 
+       if (conn->case_sensitive &&
                        !mangle_is_mangled(name, conn->params) &&
                        !*lp_mangled_map(conn->params)) {
-               return NT_STATUS_OK;
+               goto done;
        }
 
-       /* 
-        * is_mangled() was changed to look at an entire pathname, not 
+       /*
+        * is_mangled() was changed to look at an entire pathname, not
         * just a component. JRA.
         */
 
@@ -264,23 +271,23 @@ NTSTATUS unix_convert(connection_struct *conn,
                component_was_mangled = True;
        }
 
-       /* 
-        * Now we need to recursively match the name against the real 
+       /*
+        * Now we need to recursively match the name against the real
         * directory structure.
         */
 
-       /* 
+       /*
         * Match each part of the path name separately, trying the names
         * as is first, then trying to scan the directory for matching names.
         */
 
        for (; start ; start = (end?end+1:(char *)NULL)) {
-               /* 
+               /*
                 * Pinpoint the end of this section of the filename.
                 */
                end = strchr(start, '/'); /* mb safe. '/' can't be in any encoded char. */
 
-               /* 
+               /*
                 * Chop the name at this point.
                 */
                if (end) {
@@ -296,9 +303,12 @@ NTSTATUS unix_convert(connection_struct *conn,
                if (ISDOT(start)) {
                        if (!end)  {
                                /* Error code at the end of a pathname. */
-                               return NT_STATUS_OBJECT_NAME_INVALID;
+                               result = NT_STATUS_OBJECT_NAME_INVALID;
+                       } else {
+                               result = determine_path_error(end+1,
+                                               allow_wcard_last_component);
                        }
-                       return determine_path_error(end+1, allow_wcard_last_component);
+                       goto fail;
                }
 
                /* The name cannot have a wildcard if it's not
@@ -308,15 +318,17 @@ NTSTATUS unix_convert(connection_struct *conn,
 
                /* Wildcard not valid anywhere. */
                if (name_has_wildcard && !allow_wcard_last_component) {
-                       return NT_STATUS_OBJECT_NAME_INVALID;
+                       result = NT_STATUS_OBJECT_NAME_INVALID;
+                       goto fail;
                }
 
                /* Wildcards never valid within a pathname. */
                if (name_has_wildcard && end) {
-                       return NT_STATUS_OBJECT_NAME_INVALID;
+                       result = NT_STATUS_OBJECT_NAME_INVALID;
+                       goto fail;
                }
 
-               /* 
+               /*
                 * Check if the name exists up to this point.
                 */
 
@@ -331,14 +343,15 @@ NTSTATUS unix_convert(connection_struct *conn,
                                 */
                                DEBUG(5,("Not a dir %s\n",start));
                                *end = '/';
-                               /* 
+                               /*
                                 * We need to return the fact that the intermediate
                                 * name resolution failed. This is used to return an
                                 * error of ERRbadpath rather than ERRbadfile. Some
                                 * Windows applications depend on the difference between
                                 * these two errors.
                                 */
-                               return NT_STATUS_OBJECT_PATH_NOT_FOUND;
+                               result = NT_STATUS_OBJECT_PATH_NOT_FOUND;
+                               goto fail;
                        }
 
                        if (!end) {
@@ -352,20 +365,10 @@ NTSTATUS unix_convert(connection_struct *conn,
                        }
 
                } else {
-                       pstring rest;
+                       char *found_name = NULL;
 
                        /* Stat failed - ensure we don't use it. */
                        SET_STAT_INVALID(st);
-                       *rest = 0;
-
-                       /*
-                        * Remember the rest of the pathname so it can be restored
-                        * later.
-                        */
-
-                       if (end) {
-                               pstrcpy(rest,end+1);
-                       }
 
                        /* Reset errno so we can detect directory open errors. */
                        errno = 0;
@@ -375,7 +378,9 @@ NTSTATUS unix_convert(connection_struct *conn,
                         */
 
                        if (name_has_wildcard || 
-                           !scan_directory(conn, dirpath, start, sizeof(pstring) - 1 - (start - name))) {
+                           !scan_directory(conn, dirpath, start, &found_name)) {
+                               char *unmangled;
+
                                if (end) {
                                        /*
                                         * An intermediate part of the name can't be found.
@@ -383,7 +388,7 @@ NTSTATUS unix_convert(connection_struct *conn,
                                        DEBUG(5,("Intermediate not found %s\n",start));
                                        *end = '/';
 
-                                       /* 
+                                       /*
                                         * We need to return the fact that the intermediate
                                         * name resolution failed. This is used to return an
                                         * error of ERRbadpath rather than ERRbadfile. Some
@@ -395,19 +400,25 @@ NTSTATUS unix_convert(connection_struct *conn,
                                           in the filename walk. */
 
                                        if (errno == ENOENT || errno == ENOTDIR) {
-                                               return NT_STATUS_OBJECT_PATH_NOT_FOUND;
+                                               result = NT_STATUS_OBJECT_PATH_NOT_FOUND;
+                                       }
+                                       else {
+                                               result = map_nt_error_from_unix(errno);
                                        }
-                                       return map_nt_error_from_unix(errno);
+                                       goto fail;
                                }
-             
+
                                /* ENOENT is the only valid error here. */
                                if (errno != ENOENT) {
                                        /* ENOENT and ENOTDIR both map to NT_STATUS_OBJECT_PATH_NOT_FOUND
                                           in the filename walk. */
                                        if (errno == ENOTDIR) {
-                                               return NT_STATUS_OBJECT_PATH_NOT_FOUND;
+                                               result = NT_STATUS_OBJECT_PATH_NOT_FOUND;
+                                       }
+                                       else {
+                                               result = map_nt_error_from_unix(errno);
                                        }
-                                       return map_nt_error_from_unix(errno);
+                                       goto fail;
                                }
 
                                /*
@@ -427,26 +438,79 @@ NTSTATUS unix_convert(connection_struct *conn,
                                 * base of the filename.
                                 */
 
-                               if (mangle_is_mangled(start, conn->params)) {
-                                       mangle_check_cache( start, sizeof(pstring) - 1 - (start - name), conn->params);
+                               if (mangle_is_mangled(start, conn->params)
+                                   && mangle_check_cache_alloc(start, &unmangled,
+                                                               conn->params)) {
+                                       char *tmp;
+                                       size_t start_ofs = start - name;
+
+                                       if (*dirpath != '\0') {
+                                               asprintf(&tmp, "%s/%s", dirpath,
+                                                        unmangled);
+                                               SAFE_FREE(unmangled);
+                                       }
+                                       else {
+                                               tmp = unmangled;
+                                       }
+                                       if (tmp == NULL) {
+                                               DEBUG(0, ("malloc failed\n"));
+                                               result = NT_STATUS_NO_MEMORY;
+                                       }
+                                       SAFE_FREE(name);
+                                       name = tmp;
+                                       start = name + start_ofs;
+                                       end = start + strlen(start);
                                }
 
                                DEBUG(5,("New file %s\n",start));
-                               return NT_STATUS_OK;
+                               goto done;
                        }
 
-                       /* 
+
+                       /*
                         * Restore the rest of the string. If the string was mangled the size
                         * may have changed.
                         */
                        if (end) {
-                               end = start + strlen(start);
-                               if (!safe_strcat(start, "/", sizeof(pstring) - 1 - (start - name)) ||
-                                   !safe_strcat(start, rest, sizeof(pstring) - 1 - (start - name))) {
-                                       return map_nt_error_from_unix(ENAMETOOLONG);
+                               char *tmp;
+                               size_t start_ofs = start - name;
+
+                               if (*dirpath != '\0') {
+                                       asprintf(&tmp, "%s/%s/%s", dirpath,
+                                                found_name, end+1);
                                }
+                               else {
+                                       asprintf(&tmp, "%s/%s", found_name,
+                                                end+1);
+                               }
+                               if (tmp == NULL) {
+                                       DEBUG(0, ("asprintf failed\n"));
+                                       result = NT_STATUS_NO_MEMORY;
+                               }
+                               SAFE_FREE(name);
+                               name = tmp;
+                               start = name + start_ofs;
+                               end = start + strlen(found_name);
                                *end = '\0';
                        } else {
+                               char *tmp;
+                               size_t start_ofs = start - name;
+
+                               if (*dirpath != '\0') {
+                                       asprintf(&tmp, "%s/%s", dirpath,
+                                                found_name);
+                               }
+                               else {
+                                       tmp = SMB_STRDUP(found_name);
+                               }
+                               if (tmp == NULL) {
+                                       DEBUG(0, ("malloc failed\n"));
+                                       result = NT_STATUS_NO_MEMORY;
+                               }
+                               SAFE_FREE(name);
+                               name = tmp;
+                               start = name + start_ofs;
+
                                /*
                                 * We just scanned for, and found the end of the path.
                                 * We must return a valid stat struct if it exists.
@@ -459,40 +523,56 @@ NTSTATUS unix_convert(connection_struct *conn,
                                        SET_STAT_INVALID(st);
                                }
                        }
+
+                       SAFE_FREE(found_name);
                } /* end else */
 
 #ifdef DEVELOPER
                if (VALID_STAT(st) && get_delete_on_close_flag(file_id_sbuf(&st))) {
-                       return NT_STATUS_DELETE_PENDING;
+                       result = NT_STATUS_DELETE_PENDING;
+                       goto fail;
                }
 #endif
 
                /* 
                 * Add to the dirpath that we have resolved so far.
                 */
-               if (*dirpath) {
-                       pstrcat(dirpath,"/");
-               }
 
-               pstrcat(dirpath,start);
+               if (*dirpath != '\0') {
+                       char *tmp;
+
+                       if (asprintf(&tmp, "%s/%s", dirpath, start) == -1) {
+                               DEBUG(0, ("asprintf failed\n"));
+                               return NT_STATUS_NO_MEMORY;
+                       }
+                       SAFE_FREE(dirpath);
+                       dirpath = tmp;
+               }
+               else {
+                       SAFE_FREE(dirpath);
+                       if (!(dirpath = SMB_STRDUP(start))) {
+                               DEBUG(0, ("strdup failed\n"));
+                               return NT_STATUS_NO_MEMORY;
+                       }
+               }
 
                /*
                 * Don't cache a name with mangled or wildcard components
                 * as this can change the size.
                 */
-               
+
                if(!component_was_mangled && !name_has_wildcard) {
                        stat_cache_add(orig_path, dirpath, conn->case_sensitive);
                }
-       
-               /* 
+
+               /*
                 * Restore the / that we wiped out earlier.
                 */
                if (end) {
                        *end = '/';
                }
        }
-  
+
        /*
         * Don't cache a name with mangled or wildcard components
         * as this can change the size.
@@ -502,12 +582,19 @@ NTSTATUS unix_convert(connection_struct *conn,
                stat_cache_add(orig_path, name, conn->case_sensitive);
        }
 
-       /* 
+       /*
         * The name has been resolved.
         */
 
        DEBUG(5,("conversion finished %s -> %s\n",orig_path, name));
-       return NT_STATUS_OK;
+
+ done:
+       result = NT_STATUS_OK;
+       pstrcpy(orig_path, name);
+ fail:
+       SAFE_FREE(name);
+       SAFE_FREE(dirpath);
+       return result;
 }
 
 /****************************************************************************
@@ -538,22 +625,38 @@ NTSTATUS check_name(connection_struct *conn, const pstring name)
        return NT_STATUS_OK;
 }
 
+/****************************************************************************
+ Check if two filenames are equal.
+ This needs to be careful about whether we are case sensitive.
+****************************************************************************/
+
+static BOOL fname_equal(const char *name1, const char *name2, BOOL case_sensitive)
+{
+       /* Normal filename handling */
+       if (case_sensitive)
+               return(strcmp(name1,name2) == 0);
+
+       return(strequal(name1,name2));
+}
+
 /****************************************************************************
  Scan a directory to find a filename, matching without case sensitivity.
  If the name looks like a mangled name then try via the mangling functions
 ****************************************************************************/
 
-static BOOL scan_directory(connection_struct *conn, const char *path, char *name, size_t maxlength)
+static BOOL scan_directory(connection_struct *conn, const char *path,
+                          char *name, char **found_name)
 {
        struct smb_Dir *cur_dir;
        const char *dname;
        BOOL mangled;
+       char *unmangled_name = NULL;
        long curpos;
 
        mangled = mangle_is_mangled(name, conn->params);
 
        /* handle null paths */
-       if (*path == 0)
+       if ((path == NULL) || (*path == 0))
                path = ".";
 
        /*
@@ -572,12 +675,15 @@ static BOOL scan_directory(connection_struct *conn, const char *path, char *name
         */
 
        if (mangled && !conn->case_sensitive) {
-               mangled = !mangle_check_cache( name, maxlength, conn->params);
+               mangled = !mangle_check_cache_alloc(name, &unmangled_name,
+                                                   conn->params);
+               name = unmangled_name;
        }
 
        /* open the directory */
        if (!(cur_dir = OpenDir(conn, path, NULL, 0))) {
                DEBUG(3,("scan dir didn't open dir [%s]\n",path));
+               SAFE_FREE(unmangled_name);
                return(False);
        }
 
@@ -603,12 +709,14 @@ static BOOL scan_directory(connection_struct *conn, const char *path, char *name
 
                if ((mangled && mangled_equal(name,dname,conn->params)) || fname_equal(name, dname, conn->case_sensitive)) {
                        /* we've found the file, change it's name and return */
-                       safe_strcpy(name, dname, maxlength);
+                       *found_name = SMB_STRDUP(dname);
+                       SAFE_FREE(unmangled_name);
                        CloseDir(cur_dir);
                        return(True);
                }
        }
 
+       SAFE_FREE(unmangled_name);
        CloseDir(cur_dir);
        errno = ENOENT;
        return(False);
index d7f2eb8db2663438b29f883fc255e8ec8c5e8953..f69c940539d46f45a446213cfc98128b4ad8f02c 100644 (file)
@@ -112,6 +112,21 @@ BOOL mangle_check_cache(char *s, size_t maxlen,
        return mangle_fns->check_cache(s, maxlen, p);
 }
 
+BOOL mangle_check_cache_alloc(const char *name, char **presult,
+                             const struct share_params *p)
+{
+       pstring tmp;
+       char *result;
+       pstrcpy(tmp, name);
+
+       if (!mangle_check_cache(tmp, sizeof(pstring), p)
+           || !(result = SMB_STRDUP(tmp))) {
+               return False;
+       }
+       *presult = result;
+       return True;
+}
+
 /* 
    map a long filename to a 8.3 name. 
  */
index 51e8c0417a3fa6ed0452d67d51eabc5d8150002d..24cfe3beb8060f687f8f9cc78ccf56c1041cb0e5 100644 (file)
@@ -4,6 +4,7 @@
    Copyright (C) Andrew Tridgell 1992-2000
    Copyright (C) Jeremy Allison 1999-2004
    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2003
+   Copyright (C) Volker Lendecke 2007
    
    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
@@ -39,9 +40,8 @@ static TDB_CONTEXT *tdb_stat_cache;
  *
  */
 
-void stat_cache_add( const char *full_orig_name, const char *orig_translated_path, BOOL case_sensitive)
+void stat_cache_add( const char *full_orig_name, const char *translated_path, BOOL case_sensitive)
 {
-       char *translated_path;
        size_t translated_path_length;
        TDB_DATA data_val;
        char *original_path;
@@ -61,10 +61,10 @@ void stat_cache_add( const char *full_orig_name, const char *orig_translated_pat
         * Don't cache trivial valid directory entries such as . and ..
         */
 
-       if((*full_orig_name == '\0') || (full_orig_name[0] == '.' && 
-                               ((full_orig_name[1] == '\0') ||
-                                (full_orig_name[1] == '.' && full_orig_name[2] == '\0'))))
+       if ((*full_orig_name == '\0')
+           || ISDOT(full_orig_name) || ISDOTDOT(full_orig_name)) {
                return;
+       }
 
        /*
         * If we are in case insentive mode, we don't need to
@@ -72,7 +72,7 @@ void stat_cache_add( const char *full_orig_name, const char *orig_translated_pat
         * would be a waste.
         */
 
-       if(case_sensitive && (strcmp(full_orig_name, orig_translated_path) == 0))
+       if (case_sensitive && (strcmp(full_orig_name, translated_path) == 0))
                return;
 
        /*
@@ -80,14 +80,15 @@ void stat_cache_add( const char *full_orig_name, const char *orig_translated_pat
         * translated path.
         */
 
-       translated_path = SMB_STRDUP(orig_translated_path);
-       if (!translated_path)
-               return;
+       /*
+        * To save a strdup we don't necessarily 0-terminate the translated
+        * path in the tdb. Instead, we do it directly after the tdb_fetch in
+        * stat_cache_lookup.
+        */
 
        translated_path_length = strlen(translated_path);
 
        if(translated_path[translated_path_length-1] == '/') {
-               translated_path[translated_path_length-1] = '\0';
                translated_path_length--;
        }
 
@@ -98,7 +99,6 @@ void stat_cache_add( const char *full_orig_name, const char *orig_translated_pat
        }
 
        if (!original_path) {
-               SAFE_FREE(translated_path);
                return;
        }
 
@@ -114,7 +114,6 @@ void stat_cache_add( const char *full_orig_name, const char *orig_translated_pat
                        DEBUG(0, ("OOPS - tried to store stat cache entry for weird length paths [%s] %lu and [%s] %lu)!\n",
                                  original_path, (unsigned long)original_path_length, translated_path, (unsigned long)translated_path_length));
                        SAFE_FREE(original_path);
-                       SAFE_FREE(translated_path);
                        return;
                }
 
@@ -140,7 +139,6 @@ void stat_cache_add( const char *full_orig_name, const char *orig_translated_pat
        }
 
        SAFE_FREE(original_path);
-       SAFE_FREE(translated_path);
 }
 
 /**
@@ -148,7 +146,8 @@ void stat_cache_add( const char *full_orig_name, const char *orig_translated_pat
  *
  * @param conn    A connection struct to do the stat() with.
  * @param name    The path we are attempting to cache, modified by this routine
- *                to be correct as far as the cache can tell us
+ *                to be correct as far as the cache can tell us. We assume that
+ *               it is a malloc'ed string, we free it if necessary.
  * @param dirpath The path as far as the stat cache told us.
  * @param start   A pointer into name, for where to 'start' in fixing the rest of the name up.
  * @param psd     A stat buffer, NOT from the cache, but just a side-effect.
@@ -157,7 +156,7 @@ void stat_cache_add( const char *full_orig_name, const char *orig_translated_pat
  *
  */
 
-BOOL stat_cache_lookup(connection_struct *conn, pstring name, pstring dirpath, 
+BOOL stat_cache_lookup(connection_struct *conn, char **pname, char **dirpath,
                       char **start, SMB_STRUCT_STAT *pst)
 {
        char *chk_name;
@@ -167,10 +166,12 @@ BOOL stat_cache_lookup(connection_struct *conn, pstring name, pstring dirpath,
        char *translated_path;
        size_t translated_path_length;
        TDB_DATA data_val;
+       char *name;
 
        if (!lp_stat_cache())
                return False;
  
+       name = *pname;
        namelen = strlen(name);
 
        *start = name;
@@ -180,10 +181,9 @@ BOOL stat_cache_lookup(connection_struct *conn, pstring name, pstring dirpath,
        /*
         * Don't lookup trivial valid directory entries.
         */
-       if((*name == '\0') || (name[0] == '.' && 
-                               ((name[1] == '\0') ||
-                                (name[1] == '.' && name[1] == '\0'))))
+       if ((*name == '\0') || ISDOT(name) || ISDOTDOT(name)) {
                return False;
+       }
 
        if (conn->case_sensitive) {
                chk_name = SMB_STRDUP(name);
@@ -250,6 +250,12 @@ BOOL stat_cache_lookup(connection_struct *conn, pstring name, pstring dirpath,
        translated_path = (char *)data_val.dptr;
        translated_path_length = data_val.dsize - 1;
 
+       /*
+        * In stat_cache_add we did not necessarily 0-terminate the translated
+        * path. Do it here, where we do have a freshly malloc'ed blob.
+        */
+       translated_path[translated_path_length] = '\0';
+
        DEBUG(10,("stat_cache_lookup: lookup succeeded for name [%s] "
                  "-> [%s]\n", chk_name, translated_path ));
        DO_PROFILE_INC(statcache_hits);
@@ -264,31 +270,43 @@ BOOL stat_cache_lookup(connection_struct *conn, pstring name, pstring dirpath,
 
        if (!sizechanged) {
                memcpy(name, translated_path,
-                      MIN(sizeof(pstring)-1, translated_path_length));
-       } else if (num_components == 0) {
-               pstrcpy(name, translated_path);
-       } else {
-               char *sp;
-
-               sp = strnrchr_m(name, '/', num_components);
-               if (sp) {
-                       pstring last_component;
-                       pstrcpy(last_component, sp);
-                       pstrcpy(name, translated_path);
-                       pstrcat(name, last_component);
+                      MIN(namelen, translated_path_length));
+       }
+       else {
+               if (num_components == 0) {
+                       name = SMB_STRNDUP(translated_path,
+                                          translated_path_length);
                } else {
-                       pstrcpy(name, translated_path);
+                       char *sp;
+
+                       sp = strnrchr_m(name, '/', num_components);
+                       if (sp) {
+                               asprintf(&name, "%.*s%s",
+                                        (int)translated_path_length,
+                                        translated_path, sp);
+                       } else {
+                               name = SMB_STRNDUP(translated_path,
+                                                  translated_path_length);
+                       }
                }
+               if (name == NULL) {
+                       /*
+                        * TODO: Get us out of here with a real error message
+                        */
+                       smb_panic("malloc failed");
+               }
+               SAFE_FREE(*pname);
+               *pname = name;
        }
 
+
        /* set pointer for 'where to start' on fixing the rest of the name */
        *start = &name[translated_path_length];
        if (**start == '/')
                ++*start;
 
-       pstrcpy(dirpath, translated_path);
+       *dirpath = translated_path;
        SAFE_FREE(chk_name);
-       SAFE_FREE(data_val.dptr);
        return (namelen == translated_path_length);
 }