Author: jra
Date: 2005-05-31 19:06:45 +0000 (Tue, 31 May 2005)
New Revision: 7150

WebSVN: http://websvn.samba.org/cgi-bin/viewcvs.cgi?view=rev&root=samba&rev=7150

Log:
Fix for bug #2698. If a unicode to unix charset conversion fails (due to buggy 
iconv?) we can
be left with a filename that doesn't exist on the remote machine. If we then do 
a findnext
with this file the server gets confused and restarts from the beginning of the 
directory,
causing directory listing loops. Fix this by keeping a copy of the "raw" 
filename data and
length and using this as the argument to findnext. This won't fix the incorrect 
iconv
conversion into the finfo struct but at least it ensures that directory 
listings always
terminate. Tested against NTFS and FAT directories.
Jeremy.

Modified:
   trunk/source/libsmb/clilist.c


Changeset:
Modified: trunk/source/libsmb/clilist.c
===================================================================
--- trunk/source/libsmb/clilist.c       2005-05-31 18:36:59 UTC (rev 7149)
+++ trunk/source/libsmb/clilist.c       2005-05-31 19:06:45 UTC (rev 7150)
@@ -31,16 +31,20 @@
  by NT and 2 is used by OS/2
 ****************************************************************************/
 
-static size_t interpret_long_filename(struct cli_state *cli,
-                                  int level,char *p,file_info *finfo, uint32 
*p_resume_key)
+static size_t interpret_long_filename(struct cli_state *cli, int level,char 
*p,file_info *finfo,
+                                       uint32 *p_resume_key, DATA_BLOB 
*p_last_name_raw, uint32 *p_last_name_raw_len)
 {
        file_info finfo2;
        int len;
        char *base = p;
 
-       if (!finfo) finfo = &finfo2;
+       if (!finfo) {
+               finfo = &finfo2;
+       }
 
-       *p_resume_key = 0;
+       if (p_resume_key) {
+               *p_resume_key = 0;
+       }
        memcpy(finfo,&def_finfo,sizeof(*finfo));
 
        switch (level) {
@@ -87,7 +91,9 @@
                        size_t namelen, slen;
                        p += 4; /* next entry offset */
 
-                       *p_resume_key = IVAL(p,0);
+                       if (p_resume_key) {
+                               *p_resume_key = IVAL(p,0);
+                       }
                        p += 4; /* fileindex */
                                
                        /* these dates appear to arrive in a
@@ -134,6 +140,22 @@
                        clistr_pull(cli, finfo->name, p,
                                    sizeof(finfo->name),
                                    namelen, 0);
+
+                       /* To be robust in the face of unicode conversion 
failures
+                          we need to copy the raw bytes of the last name seen 
here.
+                          Namelen doesn't include the terminating unicode 
null, so
+                          copy it here. */
+
+                       if (p_last_name_raw && p_last_name_raw_len) {
+                               if (namelen + 2 > p_last_name_raw->length) {
+                                       memset(p_last_name_raw->data, '\0', 
sizeof(p_last_name_raw->length));
+                                       *p_last_name_raw_len = 0;
+                               } else {
+                                       memcpy(p_last_name_raw->data, p, 
namelen);
+                                       SSVAL(p_last_name_raw->data, namelen, 
0);
+                                       *p_last_name_raw_len = namelen + 2;
+                               }
+                       }
                        return (size_t)IVAL(base, 0);
                }
        }
@@ -174,6 +196,8 @@
        pstring param;
        const char *mnt;
        uint32 resume_key = 0;
+       uint32 last_name_raw_len = 0;
+       DATA_BLOB last_name_raw = data_blob(NULL, 2*sizeof(pstring));
 
        /* NT uses 260, OS/2 uses 2. Both accept 1. */
        info_level = (cli->capabilities&CAP_NT_SMBS)?260:1;
@@ -215,8 +239,12 @@
                           can miss filenames. Use last filename continue 
instead. JRA */
                        
SSVAL(param,10,(FLAG_TRANS2_FIND_REQUIRE_RESUME|FLAG_TRANS2_FIND_CLOSE_IF_END));
        /* resume required + close on end */
                        p = param+12;
-                       p += clistr_push(cli, param+12, mask, sizeof(param)-12, 
-                                        STR_TERMINATE);
+                       if (last_name_raw_len && (last_name_raw_len < 
(sizeof(param)-12))) {
+                               memcpy(p, last_name_raw.data, 
last_name_raw_len);
+                               p += last_name_raw_len;
+                       } else {
+                               p += clistr_push(cli, param+12, mask, 
sizeof(param)-12, STR_TERMINATE);
+                       }
                }
 
                param_len = PTR_DIFF(p, param);
@@ -283,7 +311,8 @@
                                /* Last entry - fixup the last offset length. */
                                SIVAL(p2,0,PTR_DIFF((rdata + data_len),p2));
                        }
-                       p2 += 
interpret_long_filename(cli,info_level,p2,&finfo,&resume_key);
+                       p2 += interpret_long_filename(cli,info_level,p2,&finfo,
+                                                       
&resume_key,&last_name_raw,&last_name_raw_len);
                }
 
                if (ff_lastname > 0) {
@@ -323,12 +352,13 @@
        mnt = cli_cm_get_mntpoint( cli );
 
        for (p=dirlist,i=0;i<total_received;i++) {
-               p += 
interpret_long_filename(cli,info_level,p,&finfo,&resume_key);
+               p += 
interpret_long_filename(cli,info_level,p,&finfo,NULL,NULL,NULL);
                fn( mnt,&finfo, Mask, state );
        }
 
-       /* free up the dirlist buffer */
+       /* free up the dirlist buffer and last name raw blob */
        SAFE_FREE(dirlist);
+       data_blob_free(&last_name_raw);
        return(total_received);
 }
 

Reply via email to