r12758: r12127@cabra: derrell | 2006-01-03 15:22:18 -0500
[idra/samba.git] / source3 / libsmb / clilist.c
index e7272176288a9d44ccba2ff06a2cd710c12fd096..48780e28dfa9637a870cb8dfd9e61363e13fdf39 100644 (file)
@@ -1,6 +1,5 @@
 /* 
-   Unix SMB/Netbios implementation.
-   Version 3.0
+   Unix SMB/CIFS implementation.
    client directory list routines
    Copyright (C) Andrew Tridgell 1994-1998
    
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
 
-#define NO_SYSLOG
-
 #include "includes.h"
 
+extern file_info def_finfo;
 
 /****************************************************************************
-interpret a long filename structure - this is mostly guesses at the moment
-The length of the structure is returned
-The structure of a long filename depends on the info level. 260 is used
-by NT and 2 is used by OS/2
+ Interpret a long filename structure - this is mostly guesses at the moment.
+ The length of the structure is returned
+ The structure of a long filename depends on the info level. 260 is used
+ by NT and 2 is used by OS/2
 ****************************************************************************/
-static int interpret_long_filename(struct cli_state *cli,
-                                  int level,char *p,file_info *finfo)
+
+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)
 {
-       extern file_info def_finfo;
        file_info finfo2;
        int len;
        char *base = p;
 
-       if (!finfo) finfo = &finfo2;
+       if (!finfo) {
+               finfo = &finfo2;
+       }
 
+       if (p_resume_key) {
+               *p_resume_key = 0;
+       }
        memcpy(finfo,&def_finfo,sizeof(*finfo));
 
-       switch (level)
-               {
+       switch (level) {
                case 1: /* OS/2 understands this */
                        /* these dates are converted to GMT by
                            make_unix_date */
-                       finfo->ctime = make_unix_date2(p+4);
-                       finfo->atime = make_unix_date2(p+8);
-                       finfo->mtime = make_unix_date2(p+12);
+                       finfo->ctime = cli_make_unix_date2(cli, p+4);
+                       finfo->atime = cli_make_unix_date2(cli, p+8);
+                       finfo->mtime = cli_make_unix_date2(cli, p+12);
                        finfo->size = IVAL(p,16);
                        finfo->mode = CVAL(p,24);
                        len = CVAL(p, 26);
                        p += 27;
                        p += clistr_align_in(cli, p, 0);
+                       /* the len+2 below looks strange but it is
+                          important to cope with the differences
+                          between win2000 and win9x for this call
+                          (tridge) */
                        p += clistr_pull(cli, finfo->name, p,
-                                   sizeof(finfo->name),
-                                   len
-                                   STR_TERMINATE);
+                                        sizeof(finfo->name),
+                                        len+2
+                                        STR_TERMINATE);
                        return PTR_DIFF(p, base);
 
                case 2: /* this is what OS/2 uses mostly */
                        /* these dates are converted to GMT by
                            make_unix_date */
-                       finfo->ctime = make_unix_date2(p+4);
-                       finfo->atime = make_unix_date2(p+8);
-                       finfo->mtime = make_unix_date2(p+12);
+                       finfo->ctime = cli_make_unix_date2(cli, p+4);
+                       finfo->atime = cli_make_unix_date2(cli, p+8);
+                       finfo->mtime = cli_make_unix_date2(cli, p+12);
                        finfo->size = IVAL(p,16);
                        finfo->mode = CVAL(p,24);
                        len = CVAL(p, 30);
                        p += 31;
+                       /* check for unisys! */
                        p += clistr_pull(cli, finfo->name, p,
                                         sizeof(finfo->name),
                                         len, 
@@ -79,8 +86,12 @@ static int interpret_long_filename(struct cli_state *cli,
                        
                case 260: /* NT uses this, but also accepts 2 */
                {
-                       int namelen, slen;
+                       size_t namelen, slen;
                        p += 4; /* next entry offset */
+
+                       if (p_resume_key) {
+                               *p_resume_key = IVAL(p,0);
+                       }
                        p += 4; /* fileindex */
                                
                        /* these dates appear to arrive in a
@@ -98,13 +109,20 @@ static int interpret_long_filename(struct cli_state *cli,
                           cheap to calculate, I suppose, as
                           no DST tables will be needed */
                        
-                       finfo->ctime = interpret_long_date(p); p += 8;
-                       finfo->atime = interpret_long_date(p); p += 8;
-                       finfo->mtime = interpret_long_date(p); p += 8; p += 8;
-                       finfo->size = IVAL(p,0); p += 8;
+                       finfo->ctime = interpret_long_date(p);
+                       p += 8;
+                       finfo->atime = interpret_long_date(p);
+                       p += 8;
+                       finfo->mtime = interpret_long_date(p);
+                       p += 8;
+                       p += 8;
+                       finfo->size = IVAL2_TO_SMB_BIG_UINT(p,0);
+                       p += 8;
                        p += 8; /* alloc size */
-                       finfo->mode = CVAL(p,0); p += 4;
-                       namelen = IVAL(p,0); p += 4;
+                       finfo->mode = CVAL(p,0);
+                       p += 4;
+                       namelen = IVAL(p,0);
+                       p += 4;
                        p += 4; /* EA size */
                        slen = SVAL(p, 0);
                        p += 2; 
@@ -114,28 +132,44 @@ static int interpret_long_filename(struct cli_state *cli,
                                if (p[1] == 0 && namelen > 1) flags |= STR_UNICODE;
                                clistr_pull(cli, finfo->short_name, p,
                                            sizeof(finfo->short_name),
-                                           24, flags);
+                                           slen, flags);
                        }
                        p += 24; /* short name? */        
                        clistr_pull(cli, finfo->name, p,
                                    sizeof(finfo->name),
                                    namelen, 0);
-                       return SVAL(base, 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);
                }
+       }
        
        DEBUG(1,("Unknown long filename format %d\n",level));
-       return(SVAL(p,0));
+       return (size_t)IVAL(base,0);
 }
 
-
 /****************************************************************************
-  do a directory listing, calling fn on each file found
-  ****************************************************************************/
+ Do a directory listing, calling fn on each file found.
+****************************************************************************/
+
 int cli_list_new(struct cli_state *cli,const char *Mask,uint16 attribute, 
-                void (*fn)(file_info *, const char *, void *), void *state)
+                void (*fn)(const char *, file_info *, const char *, void *), void *state)
 {
-       int max_matches = 512;
+        int max_matches = 1366;
        int info_level;
        char *p, *p2;
        pstring mask;
@@ -151,14 +185,24 @@ int cli_list_new(struct cli_state *cli,const char *Mask,uint16 attribute,
        int ff_dir_handle=0;
        int loop_count = 0;
        char *rparam=NULL, *rdata=NULL;
-       int param_len, data_len;        
+       unsigned int param_len, data_len;       
        uint16 setup;
        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;
-
-       pstrcpy(mask,Mask);
+       
+       /* when getting a directory listing from a 2k dfs root share, 
+          we have to include the full path (\server\share\mask) here */
+          
+       if ( cli->dfsroot )
+               pstr_sprintf( mask, "\\%s\\%s\\%s", cli->desthost, cli->share, Mask );
+       else
+               pstrcpy(mask,Mask);
        
        while (ff_eos == 0) {
                loop_count++;
@@ -171,22 +215,30 @@ int cli_list_new(struct cli_state *cli,const char *Mask,uint16 attribute,
                        setup = TRANSACT2_FINDFIRST;
                        SSVAL(param,0,attribute); /* attribute */
                        SSVAL(param,2,max_matches); /* max count */
-                       SSVAL(param,4,4+2);     /* resume required + close on end */
+                       SSVAL(param,4,(FLAG_TRANS2_FIND_REQUIRE_RESUME|FLAG_TRANS2_FIND_CLOSE_IF_END)); /* resume required + close on end */
                        SSVAL(param,6,info_level); 
                        SIVAL(param,8,0);
                        p = param+12;
-                       p += clistr_push(cli, param+12, mask, -1
+                       p += clistr_push(cli, param+12, mask, sizeof(param)-12
                                         STR_TERMINATE);
                } else {
                        setup = TRANSACT2_FINDNEXT;
                        SSVAL(param,0,ff_dir_handle);
                        SSVAL(param,2,max_matches); /* max count */
                        SSVAL(param,4,info_level); 
-                       SIVAL(param,6,0); /* ff_resume_key */
-                       SSVAL(param,10,8+4+2);  /* continue + resume required + close on end */
+                       /* For W2K servers serving out FAT filesystems we *must* set the
+                          resume key. If it's not FAT then it's returned as zero. */
+                       SIVAL(param,6,resume_key); /* ff_resume_key */
+                       /* NB. *DON'T* use continue here. If you do it seems that W2K and bretheren
+                          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, -1, 
-                                        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);
@@ -197,7 +249,12 @@ int cli_list_new(struct cli_state *cli,const char *Mask,uint16 attribute,
                                    &setup, 1, 0,           /* setup, length, max */
                                    param, param_len, 10,   /* param, length, max */
                                    NULL, 0, 
-                                   cli->max_xmit /* data, length, max */
+#if 0
+                                   /* w2k value. */
+                                   MIN(16384,cli->max_xmit) /* data, length, max. */
+#else
+                                   cli->max_xmit           /* data, length, max. */
+#endif
                                    )) {
                        break;
                }
@@ -210,16 +267,25 @@ int cli_list_new(struct cli_state *cli,const char *Mask,uint16 attribute,
                           it gives ERRSRV/ERRerror temprarily */
                        uint8 eclass;
                        uint32 ecode;
+
+                       SAFE_FREE(rdata);
+                       SAFE_FREE(rparam);
+
                        cli_dos_error(cli, &eclass, &ecode);
-                       if (eclass != ERRSRV || ecode != ERRerror) break;
-                       msleep(100);
+                       if (eclass != ERRSRV || ecode != ERRerror)
+                               break;
+                       smb_msleep(100);
                        continue;
                }
 
-                if (cli_is_error(cli) || !rdata || !rparam) 
-                        return -1;
+                if (cli_is_error(cli) || !rdata || !rparam) {
+                       SAFE_FREE(rdata);
+                       SAFE_FREE(rparam);
+                       break;
+               }
 
-               if (total_received == -1) total_received = 0;
+               if (total_received == -1)
+                       total_received = 0;
 
                /* parse out some important return info */
                p = rparam;
@@ -234,108 +300,115 @@ int cli_list_new(struct cli_state *cli,const char *Mask,uint16 attribute,
                        ff_lastname = SVAL(p,6);
                }
 
-               if (ff_searchcount == 0) 
+               if (ff_searchcount == 0) {
+                       SAFE_FREE(rdata);
+                       SAFE_FREE(rparam);
                        break;
+               }
 
                /* point to the data bytes */
                p = rdata;
 
                /* we might need the lastname for continuations */
+               for (p2=p,i=0;i<ff_searchcount;i++) {
+                       if ((info_level == 260) && (i == ff_searchcount-1)) {
+                               /* 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,&last_name_raw,&last_name_raw_len);
+
+                       if (!First && *mask && strcsequal(finfo.name, mask)) {
+                               DEBUG(0,("Error: Looping in FIND_NEXT as name %s has already been seen?\n",
+                                       finfo.name));
+                               ff_eos = 1;
+                               break;
+                       }
+               }
+
                if (ff_lastname > 0) {
-                       switch(info_level)
-                               {
-                               case 260:
-                                       clistr_pull(cli, mask, p+ff_lastname,
-                                                   sizeof(mask), 
-                                                   data_len-ff_lastname,
-                                                   STR_TERMINATE);
-                                       break;
-                               case 1:
-                                       clistr_pull(cli, mask, p+ff_lastname+1,
-                                                   sizeof(mask), 
-                                                   -1,
-                                                   STR_TERMINATE);
-                                       break;
-                               }
+                       pstrcpy(mask, finfo.name);
                } else {
                        pstrcpy(mask,"");
                }
+
+               /* grab the data for later use */
                /* and add them to the dirlist pool */
-               tdl = Realloc(dirlist,dirlist_len + data_len);
+               tdl = SMB_REALLOC(dirlist,dirlist_len + data_len);
 
                if (!tdl) {
                        DEBUG(0,("cli_list_new: Failed to expand dirlist\n"));
+                       SAFE_FREE(rdata);
+                       SAFE_FREE(rparam);
                        break;
+               } else {
+                       dirlist = tdl;
                }
-               else dirlist = tdl;
-
-               /* put in a length for the last entry, to ensure we can chain entries 
-                  into the next packet */
-               for (p2=p,i=0;i<(ff_searchcount-1);i++)
-                       p2 += interpret_long_filename(cli,info_level,p2,NULL);
-               SSVAL(p2,0,data_len - PTR_DIFF(p2,p));
 
-               /* grab the data for later use */
                memcpy(dirlist+dirlist_len,p,data_len);
                dirlist_len += data_len;
 
                total_received += ff_searchcount;
 
-               if (rdata) free(rdata); rdata = NULL;
-               if (rparam) free(rparam); rparam = NULL;
-               
+               SAFE_FREE(rdata);
+               SAFE_FREE(rparam);
+
                DEBUG(3,("received %d entries (eos=%d)\n",
                         ff_searchcount,ff_eos));
 
-               if (ff_searchcount > 0) loop_count = 0;
+               if (ff_searchcount > 0)
+                       loop_count = 0;
 
                First = False;
        }
 
+       mnt = cli_cm_get_mntpoint( cli );
+
        for (p=dirlist,i=0;i<total_received;i++) {
-               p += interpret_long_filename(cli,info_level,p,&finfo);
-               fn(&finfo, Mask, state);
+               p += interpret_long_filename(cli,info_level,p,&finfo,NULL,NULL,NULL);
+               fn( mnt,&finfo, Mask, state );
        }
 
-       /* free up the dirlist buffer */
-       if (dirlist) free(dirlist);
+       /* free up the dirlist buffer and last name raw blob */
+       SAFE_FREE(dirlist);
+       data_blob_free(&last_name_raw);
        return(total_received);
 }
 
-
-
 /****************************************************************************
-interpret a short filename structure
-The length of the structure is returned
+ Interpret a short filename structure.
+ The length of the structure is returned.
 ****************************************************************************/
+
 static int interpret_short_filename(struct cli_state *cli, char *p,file_info *finfo)
 {
-       extern file_info def_finfo;
 
        *finfo = def_finfo;
 
        finfo->mode = CVAL(p,21);
        
        /* this date is converted to GMT by make_unix_date */
-       finfo->ctime = make_unix_date(p+22);
+       finfo->ctime = cli_make_unix_date(cli, p+22);
        finfo->mtime = finfo->atime = finfo->ctime;
        finfo->size = IVAL(p,26);
        clistr_pull(cli, finfo->name, p+30, sizeof(finfo->name), 12, STR_ASCII);
-       if (strcmp(finfo->name, "..") && strcmp(finfo->name, "."))
-               fstrcpy(finfo->short_name,finfo->name);
-       
+       if (strcmp(finfo->name, "..") && strcmp(finfo->name, ".")) {
+               strncpy(finfo->short_name,finfo->name, sizeof(finfo->short_name)-1);
+               finfo->short_name[sizeof(finfo->short_name)-1] = '\0';
+       }
+
        return(DIR_STRUCT_SIZE);
 }
 
 
 /****************************************************************************
-  do a directory listing, calling fn on each file found
-  this uses the old SMBsearch interface. It is needed for testing Samba,
-  but should otherwise not be used
-  ****************************************************************************/
+ Do a directory listing, calling fn on each file found.
+ this uses the old SMBsearch interface. It is needed for testing Samba,
+ but should otherwise not be used.
+****************************************************************************/
+
 int cli_list_old(struct cli_state *cli,const char *Mask,uint16 attribute, 
-                void (*fn)(file_info *, const char *, void *), void *state)
+                void (*fn)(const char *, file_info *, const char *, void *), void *state)
 {
        char *p;
        int received = 0;
@@ -357,7 +430,7 @@ int cli_list_old(struct cli_state *cli,const char *Mask,uint16 attribute,
 
                set_message(cli->outbuf,2,0,True);
 
-               CVAL(cli->outbuf,smb_com) = SMBsearch;
+               SCVAL(cli->outbuf,smb_com,SMBsearch);
 
                SSVAL(cli->outbuf,smb_tid,cli->cnum);
                cli_setup_packet(cli);
@@ -389,11 +462,11 @@ int cli_list_old(struct cli_state *cli,const char *Mask,uint16 attribute,
 
                first = False;
 
-               tdl = Realloc(dirlist,(num_received + received)*DIR_STRUCT_SIZE);
+               tdl = SMB_REALLOC(dirlist,(num_received + received)*DIR_STRUCT_SIZE);
 
                if (!tdl) {
                        DEBUG(0,("cli_list_old: failed to expand dirlist"));
-                       if (dirlist) free(dirlist);
+                       SAFE_FREE(dirlist);
                        return 0;
                }
                else dirlist = tdl;
@@ -407,7 +480,7 @@ int cli_list_old(struct cli_state *cli,const char *Mask,uint16 attribute,
                
                num_received += received;
                
-               if (CVAL(cli->inbuf,smb_rcls) != 0) break;
+               if (cli_is_error(cli)) break;
        }
 
        if (!first) {
@@ -415,7 +488,7 @@ int cli_list_old(struct cli_state *cli,const char *Mask,uint16 attribute,
                memset(cli->inbuf,'\0',smb_size);
 
                set_message(cli->outbuf,2,0,True);
-               CVAL(cli->outbuf,smb_com) = SMBfclose;
+               SCVAL(cli->outbuf,smb_com,SMBfclose);
                SSVAL(cli->outbuf,smb_tid,cli->cnum);
                cli_setup_packet(cli);
 
@@ -435,30 +508,29 @@ int cli_list_old(struct cli_state *cli,const char *Mask,uint16 attribute,
                cli_setup_bcc(cli, p);
                cli_send_smb(cli);
                if (!cli_receive_smb(cli)) {
-                       DEBUG(0,("Error closing search: %s\n",smb_errstr(cli->inbuf)));
+                       DEBUG(0,("Error closing search: %s\n",cli_errstr(cli)));
                }
        }
 
        for (p=dirlist,i=0;i<num_received;i++) {
                file_info finfo;
                p += interpret_short_filename(cli, p,&finfo);
-               fn(&finfo, Mask, state);
+               fn("\\", &finfo, Mask, state);
        }
 
-       if (dirlist) free(dirlist);
+       SAFE_FREE(dirlist);
        return(num_received);
 }
 
-
 /****************************************************************************
-  do a directory listing, calling fn on each file found
-  this auto-switches between old and new style
-  ****************************************************************************/
+ Do a directory listing, calling fn on each file found.
+ This auto-switches between old and new style.
+****************************************************************************/
+
 int cli_list(struct cli_state *cli,const char *Mask,uint16 attribute, 
-            void (*fn)(file_info *, const char *, void *), void *state)
+            void (*fn)(const char *, file_info *, const char *, void *), void *state)
 {
-       if (cli->protocol <= PROTOCOL_LANMAN1) {
+       if (cli->protocol <= PROTOCOL_LANMAN1)
                return cli_list_old(cli, Mask, attribute, fn, state);
-       }
        return cli_list_new(cli, Mask, attribute, fn, state);
 }