r25117: The mega-patch Jerry was waiting for. Remove all pstrings from
[tprouty/samba.git] / source / smbd / dir.c
index 2795b2a24b29fa10e1fdf1ec19a75f0f8bd98135..a7b1b020b8357b60cd5684e404a4a73fd7478253 100644 (file)
@@ -1,21 +1,21 @@
-/* 
+/*
    Unix SMB/CIFS implementation.
    Directory handling routines
    Copyright (C) Andrew Tridgell 1992-1998
-   
+   Copyright (C) Jeremy Allison 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
-   the Free Software Foundation; either version 2 of the License, or
+   the Free Software Foundation; either version 3 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.
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
 #include "includes.h"
@@ -33,8 +33,6 @@ extern struct current_user current_user;
 
 /* Make directory handle internals available. */
 
-#define NAME_CACHE_SIZE 100
-
 struct name_cache_entry {
        char *name;
        long offset;
@@ -45,6 +43,7 @@ struct smb_Dir {
        SMB_STRUCT_DIR *dir;
        long offset;
        char *dir_path;
+       size_t name_cache_size;
        struct name_cache_entry *name_cache;
        unsigned int name_cache_index;
        unsigned int file_number;
@@ -74,15 +73,25 @@ static int dirhandles_open = 0;
  Make a dir struct.
 ****************************************************************************/
 
-void make_dir_struct(char *buf, const char *mask, const char *fname,SMB_OFF_T size,uint32 mode,time_t date, BOOL uc)
-{  
+BOOL make_dir_struct(TALLOC_CTX *ctx,
+                       char *buf,
+                       const char *mask,
+                       const char *fname,
+                       SMB_OFF_T size,
+                       uint32 mode,
+                       time_t date,
+                       BOOL uc)
+{
        char *p;
-       pstring mask2;
+       char *mask2 = talloc_strdup(ctx, mask);
 
-       pstrcpy(mask2,mask);
+       if (!mask2) {
+               return False;
+       }
 
-       if ((mode & aDIR) != 0)
+       if ((mode & aDIR) != 0) {
                size = 0;
+       }
 
        memset(buf+1,' ',11);
        if ((p = strchr_m(mask2,'.')) != NULL) {
@@ -90,8 +99,9 @@ void make_dir_struct(char *buf, const char *mask, const char *fname,SMB_OFF_T si
                push_ascii(buf+1,mask2,8, 0);
                push_ascii(buf+9,p+1,3, 0);
                *p = '.';
-       } else
+       } else {
                push_ascii(buf+1,mask2,11, 0);
+       }
 
        memset(buf+21,'\0',DIR_STRUCT_SIZE-21);
        SCVAL(buf,21,mode);
@@ -102,6 +112,7 @@ void make_dir_struct(char *buf, const char *mask, const char *fname,SMB_OFF_T si
           Strange, but verified on W2K3. Needed for OS/2. JRA. */
        push_ascii(buf+30,fname,12, uc ? STR_UPPER : 0);
        DEBUG(8,("put name [%s] from [%s] into dir struct\n",buf+30, fname));
+       return True;
 }
 
 /****************************************************************************
@@ -240,7 +251,7 @@ static void dptr_close_internal(struct dptr_struct *dptr)
 
        DLIST_REMOVE(dirptrs, dptr);
 
-       /* 
+       /*
         * Free the dnum in the bitmap. Remember the dnum value is always 
         * biased by one with respect to the bitmap.
         */
@@ -382,12 +393,11 @@ static void dptr_close_oldest(BOOL old)
  wcard must not be zero.
 ****************************************************************************/
 
-NTSTATUS dptr_create(connection_struct *conn, pstring path, BOOL old_handle, BOOL expect_close,uint16 spid,
+NTSTATUS dptr_create(connection_struct *conn, const char *path, BOOL old_handle, BOOL expect_close,uint16 spid,
                const char *wcard, BOOL wcard_has_wild, uint32 attr, struct dptr_struct **dptr_ret)
 {
        struct dptr_struct *dptr = NULL;
        struct smb_Dir *dir_hnd;
-        const char *dir2;
        NTSTATUS status;
 
        DEBUG(5,("dptr_create dir=%s\n", path));
@@ -401,17 +411,12 @@ NTSTATUS dptr_create(connection_struct *conn, pstring path, BOOL old_handle, BOO
                return status;
        }
 
-       /* use a const pointer from here on */
-       dir2 = path;
-       if (!*dir2)
-               dir2 = ".";
-
-       dir_hnd = OpenDir(conn, dir2, wcard, attr);
+       dir_hnd = OpenDir(conn, path, wcard, attr);
        if (!dir_hnd) {
                return map_nt_error_from_unix(errno);
        }
 
-       string_set(&conn->dirpath,dir2);
+       string_set(&conn->dirpath,path);
 
        if (dirhandles_open >= MAX_OPEN_DIRECTORIES) {
                dptr_idleoldest();
@@ -490,7 +495,7 @@ NTSTATUS dptr_create(connection_struct *conn, pstring path, BOOL old_handle, BOO
 
        dptr->dnum += 1; /* Always bias the dnum by one - no zero dnums allowed. */
 
-       string_set(&dptr->path,dir2);
+       string_set(&dptr->path,path);
        dptr->conn = conn;
        dptr->dir_hnd = dir_hnd;
        dptr->spid = spid;
@@ -571,7 +576,10 @@ static const char *dptr_normal_ReadDirName(struct dptr_struct *dptr, long *poffs
  Return the next visible file name, skipping veto'd and invisible files.
 ****************************************************************************/
 
-const char *dptr_ReadDirName(struct dptr_struct *dptr, long *poffset, SMB_STRUCT_STAT *pst)
+const char *dptr_ReadDirName(TALLOC_CTX *ctx,
+                       struct dptr_struct *dptr,
+                       long *poffset,
+                       SMB_STRUCT_STAT *pst)
 {
        SET_STAT_INVALID(*pst);
 
@@ -586,7 +594,7 @@ const char *dptr_ReadDirName(struct dptr_struct *dptr, long *poffset, SMB_STRUCT
        }
 
        if (!dptr->did_stat) {
-               pstring pathreal;
+               char *pathreal = NULL;
 
                /* We know the stored wcard contains no wildcard characters. See if we can match
                   with a stat call. If we can't, then set did_stat to true to
@@ -610,14 +618,19 @@ const char *dptr_ReadDirName(struct dptr_struct *dptr, long *poffset, SMB_STRUCT
                        return dptr->wcard;
                }
 
-               pstrcpy(pathreal,dptr->path);
-               pstrcat(pathreal,"/");
-               pstrcat(pathreal,dptr->wcard);
+               pathreal = talloc_asprintf(ctx,
+                                       "%s/%s",
+                                       dptr->path,
+                                       dptr->wcard);
+               if (!pathreal) {
+                       return NULL;
+               }
 
                if (SMB_VFS_STAT(dptr->conn,pathreal,pst) == 0) {
                        /* We need to set the underlying dir_hnd offset to -1 also as
                           this function is usually called with the output from TellDir. */
                        dptr->dir_hnd->offset = *poffset = END_OF_DIRECTORY_OFFSET;
+                       TALLOC_FREE(pathreal);
                        return dptr->wcard;
                } else {
                        /* If we get any other error than ENOENT or ENOTDIR
@@ -626,10 +639,13 @@ const char *dptr_ReadDirName(struct dptr_struct *dptr, long *poffset, SMB_STRUCT
                                /* We need to set the underlying dir_hdn offset to -1 also as
                                   this function is usually called with the output from TellDir. */
                                dptr->dir_hnd->offset = *poffset = END_OF_DIRECTORY_OFFSET;
+                               TALLOC_FREE(pathreal);
                                return dptr->wcard;
                        }
                }
 
+               TALLOC_FREE(pathreal);
+
                /* In case sensitive mode we don't search - we know if it doesn't exist 
                   with a stat we will fail. */
 
@@ -760,45 +776,59 @@ BOOL dir_check_ftype(connection_struct *conn, uint32 mode, uint32 dirtype)
        return True;
 }
 
-static BOOL mangle_mask_match(connection_struct *conn, fstring filename, char *mask)
+static BOOL mangle_mask_match(connection_struct *conn,
+               const char *filename,
+               const char *mask)
 {
-       mangle_map(filename,True,False,conn->params);
-       return mask_match_search(filename,mask,False);
+       char mname[13];
+
+       if (!name_to_8_3(filename,mname,False,conn->params)) {
+               return False;
+       }
+       return mask_match_search(mname,mask,False);
 }
 
 /****************************************************************************
  Get an 8.3 directory entry.
 ****************************************************************************/
 
-BOOL get_dir_entry(connection_struct *conn,char *mask,uint32 dirtype, pstring fname,
-                   SMB_OFF_T *size,uint32 *mode,time_t *date,BOOL check_descend)
+BOOL get_dir_entry(TALLOC_CTX *ctx,
+               connection_struct *conn,
+               const char *mask,
+               uint32 dirtype,
+               char **pp_fname_out,
+               SMB_OFF_T *size,
+               uint32 *mode,
+               time_t *date,
+               BOOL check_descend)
 {
-       const char *dname;
+       const char *dname = NULL;
        BOOL found = False;
        SMB_STRUCT_STAT sbuf;
-       pstring path;
-       pstring pathreal;
-       pstring filename;
+       char *pathreal = NULL;
+       const char *filename = NULL;
        BOOL needslash;
 
-       *path = *pathreal = *filename = 0;
+       *pp_fname_out = NULL;
 
        needslash = ( conn->dirpath[strlen(conn->dirpath) -1] != '/');
 
-       if (!conn->dirptr)
+       if (!conn->dirptr) {
                return(False);
+       }
 
        while (!found) {
                long curoff = dptr_TellDir(conn->dirptr);
-               dname = dptr_ReadDirName(conn->dirptr, &curoff, &sbuf);
+               dname = dptr_ReadDirName(ctx, conn->dirptr, &curoff, &sbuf);
 
                DEBUG(6,("readdir on dirptr 0x%lx now at offset %ld\n",
                        (long)conn->dirptr,TellDir(conn->dirptr->dir_hnd)));
-      
-               if (dname == NULL) 
+
+               if (dname == NULL) {
                        return(False);
-      
-               pstrcpy(filename,dname);      
+               }
+
+               filename = dname;
 
                /* notice the special *.* handling. This appears to be the only difference
                        between the wildcard handling in this routine and in the trans2 routines.
@@ -807,39 +837,65 @@ BOOL get_dir_entry(connection_struct *conn,char *mask,uint32 dirtype, pstring fn
                if ((strcmp(mask,"*.*") == 0) ||
                    mask_match_search(filename,mask,False) ||
                    mangle_mask_match(conn,filename,mask)) {
+                       char mname[13];
+
+                       if (!mangle_is_8_3(filename, False, conn->params)) {
+                               if (!name_to_8_3(filename,mname,False,
+                                          conn->params)) {
+                                       continue;
+                               }
+                               filename = mname;
+                       }
+
+                       if (needslash) {
+                               pathreal = talloc_asprintf(ctx,
+                                               "%s/%s",
+                                               conn->dirpath,
+                                               dname);
+                       } else {
+                               pathreal = talloc_asprintf(ctx,
+                                               "%s%s",
+                                               conn->dirpath,
+                                               dname);
+                       }
+                       if (!pathreal) {
+                               return False;
+                       }
 
-                       if (!mangle_is_8_3(filename, False, conn->params))
-                               mangle_map(filename,True,False,
-                                          conn->params);
-
-                       pstrcpy(fname,filename);
-                       *path = 0;
-                       pstrcpy(path,conn->dirpath);
-                       if(needslash)
-                               pstrcat(path,"/");
-                       pstrcpy(pathreal,path);
-                       pstrcat(path,fname);
-                       pstrcat(pathreal,dname);
                        if (!VALID_STAT(sbuf) && (SMB_VFS_STAT(conn, pathreal, &sbuf)) != 0) {
-                               DEBUG(5,("Couldn't stat 1 [%s]. Error = %s\n",path, strerror(errno) ));
+                               DEBUG(5,("Couldn't stat 1 [%s]. Error = %s\n",
+                                       pathreal, strerror(errno) ));
+                               TALLOC_FREE(pathreal);
                                continue;
                        }
-         
+
                        *mode = dos_mode(conn,pathreal,&sbuf);
 
                        if (!dir_check_ftype(conn,*mode,dirtype)) {
                                DEBUG(5,("[%s] attribs 0x%x didn't match 0x%x\n",filename,(unsigned int)*mode,(unsigned int)dirtype));
+                               TALLOC_FREE(pathreal);
                                continue;
                        }
 
                        *size = sbuf.st_size;
                        *date = sbuf.st_mtime;
 
-                       DEBUG(3,("get_dir_entry mask=[%s] found %s fname=%s\n",mask, pathreal,fname));
+                       DEBUG(3,("get_dir_entry mask=[%s] found %s "
+                               "fname=%s (%s)\n",
+                               mask,
+                               pathreal,
+                               dname,
+                               filename));
 
                        found = True;
 
+                       *pp_fname_out = talloc_strdup(ctx, filename);
+                       if (!*pp_fname_out) {
+                               return False;
+                       }
+
                        DirCacheAdd(conn->dirptr->dir_hnd, dname, curoff);
+                       TALLOC_FREE(pathreal);
                }
        }
 
@@ -879,7 +935,7 @@ static BOOL user_can_read_file(connection_struct *conn, char *name, SMB_STRUCT_S
        /* Pseudo-open the file (note - no fd's created). */
 
        if(S_ISDIR(pst->st_mode)) {
-                status = open_directory(conn, name, pst,
+                status = open_directory(conn, NULL, name, pst,
                        READ_CONTROL_ACCESS,
                        FILE_SHARE_READ|FILE_SHARE_WRITE,
                        FILE_OPEN,
@@ -887,7 +943,7 @@ static BOOL user_can_read_file(connection_struct *conn, char *name, SMB_STRUCT_S
                        FILE_ATTRIBUTE_DIRECTORY,
                        NULL, &fsp);
        } else {
-               status = open_file_stat(conn, name, pst, &fsp);
+               status = open_file_stat(conn, NULL, name, pst, &fsp);
        }
 
        if (!NT_STATUS_IS_OK(status)) {
@@ -943,7 +999,7 @@ static BOOL user_can_write_file(connection_struct *conn, char *name, SMB_STRUCT_
        if(S_ISDIR(pst->st_mode)) {
                return True;
        } else {
-               status = open_file_ntcreate(conn, name, pst,
+               status = open_file_ntcreate(conn, NULL, name, pst,
                        FILE_WRITE_ATTRIBUTES,
                        FILE_SHARE_READ|FILE_SHARE_WRITE,
                        FILE_OPEN,
@@ -1026,10 +1082,7 @@ BOOL is_visible_file(connection_struct *conn, const char *dir_path, const char *
                /* If it's a dfs symlink, ignore _hide xxxx_ options */
                if (lp_host_msdfs() &&
                                lp_msdfs_root(SNUM(conn)) &&
-                                       /* We get away with NULL talloc ctx here as
-                                          we're not interested in the link contents
-                                          so we have nothing to free. */
-                               is_msdfs_link(NULL, conn, entry, NULL, NULL, NULL)) {
+                               is_msdfs_link(conn, entry, NULL)) {
                        SAFE_FREE(entry);
                        return True;
                }
@@ -1064,12 +1117,14 @@ BOOL is_visible_file(connection_struct *conn, const char *dir_path, const char *
 struct smb_Dir *OpenDir(connection_struct *conn, const char *name, const char *mask, uint32 attr)
 {
        struct smb_Dir *dirp = SMB_MALLOC_P(struct smb_Dir);
+
        if (!dirp) {
                return NULL;
        }
        ZERO_STRUCTP(dirp);
 
        dirp->conn = conn;
+       dirp->name_cache_size = lp_directory_name_cache_size(SNUM(conn));
 
        dirp->dir_path = SMB_STRDUP(name);
        if (!dirp->dir_path) {
@@ -1081,9 +1136,14 @@ struct smb_Dir *OpenDir(connection_struct *conn, const char *name, const char *m
                goto fail;
        }
 
-       dirp->name_cache = SMB_CALLOC_ARRAY(struct name_cache_entry, NAME_CACHE_SIZE);
-       if (!dirp->name_cache) {
-               goto fail;
+       if (dirp->name_cache_size) {
+               dirp->name_cache = SMB_CALLOC_ARRAY(struct name_cache_entry,
+                               dirp->name_cache_size);
+               if (!dirp->name_cache) {
+                       goto fail;
+               }
+       } else {
+               dirp->name_cache = NULL;
        }
 
        dirhandles_open++;
@@ -1116,7 +1176,7 @@ int CloseDir(struct smb_Dir *dirp)
        }
        SAFE_FREE(dirp->dir_path);
        if (dirp->name_cache) {
-               for (i = 0; i < NAME_CACHE_SIZE; i++) {
+               for (i = 0; i < dirp->name_cache_size; i++) {
                        SAFE_FREE(dirp->name_cache[i].name);
                }
        }
@@ -1191,7 +1251,7 @@ void SeekDir(struct smb_Dir *dirp, long offset)
        if (offset != dirp->offset) {
                if (offset == START_OF_DIRECTORY_OFFSET) {
                        RewindDir(dirp, &offset);
-                       /* 
+                       /*
                         * Ok we should really set the file number here
                         * to 1 to enable ".." to be returned next. Trouble
                         * is I'm worried about callers using SeekDir(dirp,0)
@@ -1232,7 +1292,12 @@ void DirCacheAdd(struct smb_Dir *dirp, const char *name, long offset)
 {
        struct name_cache_entry *e;
 
-       dirp->name_cache_index = (dirp->name_cache_index+1) % NAME_CACHE_SIZE;
+       if (!dirp->name_cache_size || !dirp->name_cache) {
+               return;
+       }
+
+       dirp->name_cache_index = (dirp->name_cache_index+1) %
+                                       dirp->name_cache_size;
        e = &dirp->name_cache[dirp->name_cache_index];
        SAFE_FREE(e->name);
        e->name = SMB_STRDUP(name);
@@ -1251,20 +1316,22 @@ BOOL SearchDir(struct smb_Dir *dirp, const char *name, long *poffset)
        connection_struct *conn = dirp->conn;
 
        /* Search back in the name cache. */
-       for (i = dirp->name_cache_index; i >= 0; i--) {
-               struct name_cache_entry *e = &dirp->name_cache[i];
-               if (e->name && (conn->case_sensitive ? (strcmp(e->name, name) == 0) : strequal(e->name, name))) {
-                       *poffset = e->offset;
-                       SeekDir(dirp, e->offset);
-                       return True;
+       if (dirp->name_cache_size && dirp->name_cache) {
+               for (i = dirp->name_cache_index; i >= 0; i--) {
+                       struct name_cache_entry *e = &dirp->name_cache[i];
+                       if (e->name && (conn->case_sensitive ? (strcmp(e->name, name) == 0) : strequal(e->name, name))) {
+                               *poffset = e->offset;
+                               SeekDir(dirp, e->offset);
+                               return True;
+                       }
                }
-       }
-       for (i = NAME_CACHE_SIZE-1; i > dirp->name_cache_index; i--) {
-               struct name_cache_entry *e = &dirp->name_cache[i];
-               if (e->name && (conn->case_sensitive ? (strcmp(e->name, name) == 0) : strequal(e->name, name))) {
-                       *poffset = e->offset;
-                       SeekDir(dirp, e->offset);
-                       return True;
+               for (i = dirp->name_cache_size - 1; i > dirp->name_cache_index; i--) {
+                       struct name_cache_entry *e = &dirp->name_cache[i];
+                       if (e->name && (conn->case_sensitive ? (strcmp(e->name, name) == 0) : strequal(e->name, name))) {
+                               *poffset = e->offset;
+                               SeekDir(dirp, e->offset);
+                               return True;
+                       }
                }
        }