libsmb/clilist.c

説明を見る。
00001 /* 
00002    Unix SMB/CIFS implementation.
00003    client directory list routines
00004    Copyright (C) Andrew Tridgell 1994-1998
00005    
00006    This program is free software; you can redistribute it and/or modify
00007    it under the terms of the GNU General Public License as published by
00008    the Free Software Foundation; either version 2 of the License, or
00009    (at your option) any later version.
00010    
00011    This program is distributed in the hope that it will be useful,
00012    but WITHOUT ANY WARRANTY; without even the implied warranty of
00013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014    GNU General Public License for more details.
00015    
00016    You should have received a copy of the GNU General Public License
00017    along with this program; if not, write to the Free Software
00018    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00019 */
00020 
00021 #include "includes.h"
00022 
00023 extern file_info def_finfo;
00024 
00025 /****************************************************************************
00026  Interpret a long filename structure - this is mostly guesses at the moment.
00027  The length of the structure is returned
00028  The structure of a long filename depends on the info level. 260 is used
00029  by NT and 2 is used by OS/2
00030 ****************************************************************************/
00031 
00032 static size_t interpret_long_filename(struct cli_state *cli, int level,char *p,file_info *finfo,
00033                                         uint32 *p_resume_key, DATA_BLOB *p_last_name_raw, uint32 *p_last_name_raw_len)
00034 {
00035         file_info finfo2;
00036         int len;
00037         char *base = p;
00038 
00039         if (!finfo) {
00040                 finfo = &finfo2;
00041         }
00042 
00043         if (p_resume_key) {
00044                 *p_resume_key = 0;
00045         }
00046         memcpy(finfo,&def_finfo,sizeof(*finfo));
00047         finfo->cli = cli;
00048 
00049         switch (level) {
00050                 case 1: /* OS/2 understands this */
00051                         /* these dates are converted to GMT by
00052                            make_unix_date */
00053                         finfo->ctime_ts = convert_time_t_to_timespec(cli_make_unix_date2(cli, p+4));
00054                         finfo->atime_ts = convert_time_t_to_timespec(cli_make_unix_date2(cli, p+8));
00055                         finfo->mtime_ts = convert_time_t_to_timespec(cli_make_unix_date2(cli, p+12));
00056                         finfo->size = IVAL(p,16);
00057                         finfo->mode = CVAL(p,24);
00058                         len = CVAL(p, 26);
00059                         p += 27;
00060                         p += clistr_align_in(cli, p, 0);
00061                         /* the len+2 below looks strange but it is
00062                            important to cope with the differences
00063                            between win2000 and win9x for this call
00064                            (tridge) */
00065                         p += clistr_pull(cli, finfo->name, p,
00066                                          sizeof(finfo->name),
00067                                          len+2, 
00068                                          STR_TERMINATE);
00069                         return PTR_DIFF(p, base);
00070 
00071                 case 2: /* this is what OS/2 uses mostly */
00072                         /* these dates are converted to GMT by
00073                            make_unix_date */
00074                         finfo->ctime_ts = convert_time_t_to_timespec(cli_make_unix_date2(cli, p+4));
00075                         finfo->atime_ts = convert_time_t_to_timespec(cli_make_unix_date2(cli, p+8));
00076                         finfo->mtime_ts = convert_time_t_to_timespec(cli_make_unix_date2(cli, p+12));
00077                         finfo->size = IVAL(p,16);
00078                         finfo->mode = CVAL(p,24);
00079                         len = CVAL(p, 30);
00080                         p += 31;
00081                         /* check for unisys! */
00082                         p += clistr_pull(cli, finfo->name, p,
00083                                          sizeof(finfo->name),
00084                                          len, 
00085                                          STR_NOALIGN);
00086                         return PTR_DIFF(p, base) + 1;
00087                         
00088                 case 260: /* NT uses this, but also accepts 2 */
00089                 {
00090                         size_t namelen, slen;
00091                         p += 4; /* next entry offset */
00092 
00093                         if (p_resume_key) {
00094                                 *p_resume_key = IVAL(p,0);
00095                         }
00096                         p += 4; /* fileindex */
00097                                 
00098                         /* Offset zero is "create time", not "change time". */
00099                         p += 8;
00100                         finfo->atime_ts = interpret_long_date(p);
00101                         p += 8;
00102                         finfo->mtime_ts = interpret_long_date(p);
00103                         p += 8;
00104                         finfo->ctime_ts = interpret_long_date(p);
00105                         p += 8;
00106                         finfo->size = IVAL2_TO_SMB_BIG_UINT(p,0);
00107                         p += 8;
00108                         p += 8; /* alloc size */
00109                         finfo->mode = CVAL(p,0);
00110                         p += 4;
00111                         namelen = IVAL(p,0);
00112                         p += 4;
00113                         p += 4; /* EA size */
00114                         slen = SVAL(p, 0);
00115                         p += 2; 
00116                         {
00117                                 /* stupid NT bugs. grr */
00118                                 int flags = 0;
00119                                 if (p[1] == 0 && namelen > 1) flags |= STR_UNICODE;
00120                                 clistr_pull(cli, finfo->short_name, p,
00121                                             sizeof(finfo->short_name),
00122                                             slen, flags);
00123                         }
00124                         p += 24; /* short name? */        
00125                         clistr_pull(cli, finfo->name, p,
00126                                     sizeof(finfo->name),
00127                                     namelen, 0);
00128 
00129                         /* To be robust in the face of unicode conversion failures
00130                            we need to copy the raw bytes of the last name seen here.
00131                            Namelen doesn't include the terminating unicode null, so
00132                            copy it here. */
00133 
00134                         if (p_last_name_raw && p_last_name_raw_len) {
00135                                 if (namelen + 2 > p_last_name_raw->length) {
00136                                         memset(p_last_name_raw->data, '\0', sizeof(p_last_name_raw->length));
00137                                         *p_last_name_raw_len = 0;
00138                                 } else {
00139                                         memcpy(p_last_name_raw->data, p, namelen);
00140                                         SSVAL(p_last_name_raw->data, namelen, 0);
00141                                         *p_last_name_raw_len = namelen + 2;
00142                                 }
00143                         }
00144                         return (size_t)IVAL(base, 0);
00145                 }
00146         }
00147         
00148         DEBUG(1,("Unknown long filename format %d\n",level));
00149         return (size_t)IVAL(base,0);
00150 }
00151 
00152 /****************************************************************************
00153  Do a directory listing, calling fn on each file found.
00154 ****************************************************************************/
00155 
00156 int cli_list_new(struct cli_state *cli,const char *Mask,uint16 attribute, 
00157                  void (*fn)(const char *, file_info *, const char *, void *), void *state)
00158 {
00159 #if 1
00160         int max_matches = 1366; /* Match W2k - was 512. */
00161 #else
00162         int max_matches = 512;
00163 #endif
00164         int info_level;
00165         char *p, *p2;
00166         pstring mask;
00167         file_info finfo;
00168         int i;
00169         char *dirlist = NULL;
00170         int dirlist_len = 0;
00171         int total_received = -1;
00172         BOOL First = True;
00173         int ff_searchcount=0;
00174         int ff_eos=0;
00175         int ff_dir_handle=0;
00176         int loop_count = 0;
00177         char *rparam=NULL, *rdata=NULL;
00178         unsigned int param_len, data_len;       
00179         uint16 setup;
00180         pstring param;
00181         const char *mnt;
00182         uint32 resume_key = 0;
00183         uint32 last_name_raw_len = 0;
00184         DATA_BLOB last_name_raw = data_blob(NULL, 2*sizeof(pstring));
00185 
00186         /* NT uses 260, OS/2 uses 2. Both accept 1. */
00187         info_level = (cli->capabilities&CAP_NT_SMBS)?260:1;
00188         
00189         pstrcpy(mask,Mask);
00190         
00191         while (ff_eos == 0) {
00192                 loop_count++;
00193                 if (loop_count > 200) {
00194                         DEBUG(0,("Error: Looping in FIND_NEXT??\n"));
00195                         break;
00196                 }
00197 
00198                 if (First) {
00199                         setup = TRANSACT2_FINDFIRST;
00200                         SSVAL(param,0,attribute); /* attribute */
00201                         SSVAL(param,2,max_matches); /* max count */
00202                         SSVAL(param,4,(FLAG_TRANS2_FIND_REQUIRE_RESUME|FLAG_TRANS2_FIND_CLOSE_IF_END)); /* resume required + close on end */
00203                         SSVAL(param,6,info_level); 
00204                         SIVAL(param,8,0);
00205                         p = param+12;
00206                         p += clistr_push(cli, param+12, mask, sizeof(param)-12, 
00207                                          STR_TERMINATE);
00208                 } else {
00209                         setup = TRANSACT2_FINDNEXT;
00210                         SSVAL(param,0,ff_dir_handle);
00211                         SSVAL(param,2,max_matches); /* max count */
00212                         SSVAL(param,4,info_level); 
00213                         /* For W2K servers serving out FAT filesystems we *must* set the
00214                            resume key. If it's not FAT then it's returned as zero. */
00215                         SIVAL(param,6,resume_key); /* ff_resume_key */
00216                         /* NB. *DON'T* use continue here. If you do it seems that W2K and bretheren
00217                            can miss filenames. Use last filename continue instead. JRA */
00218                         SSVAL(param,10,(FLAG_TRANS2_FIND_REQUIRE_RESUME|FLAG_TRANS2_FIND_CLOSE_IF_END));        /* resume required + close on end */
00219                         p = param+12;
00220                         if (last_name_raw_len && (last_name_raw_len < (sizeof(param)-12))) {
00221                                 memcpy(p, last_name_raw.data, last_name_raw_len);
00222                                 p += last_name_raw_len;
00223                         } else {
00224                                 p += clistr_push(cli, param+12, mask, sizeof(param)-12, STR_TERMINATE);
00225                         }
00226                 }
00227 
00228                 param_len = PTR_DIFF(p, param);
00229 
00230                 if (!cli_send_trans(cli, SMBtrans2, 
00231                                     NULL,                   /* Name */
00232                                     -1, 0,                  /* fid, flags */
00233                                     &setup, 1, 0,           /* setup, length, max */
00234                                     param, param_len, 10,   /* param, length, max */
00235                                     NULL, 0, 
00236 #if 0
00237                                     /* w2k value. */
00238                                     MIN(16384,cli->max_xmit) /* data, length, max. */
00239 #else
00240                                     cli->max_xmit           /* data, length, max. */
00241 #endif
00242                                     )) {
00243                         break;
00244                 }
00245 
00246                 if (!cli_receive_trans(cli, SMBtrans2, 
00247                                        &rparam, &param_len,
00248                                        &rdata, &data_len) &&
00249                     cli_is_dos_error(cli)) {
00250                         /* We need to work around a Win95 bug - sometimes
00251                            it gives ERRSRV/ERRerror temprarily */
00252                         uint8 eclass;
00253                         uint32 ecode;
00254 
00255                         SAFE_FREE(rdata);
00256                         SAFE_FREE(rparam);
00257 
00258                         cli_dos_error(cli, &eclass, &ecode);
00259 
00260                         /*
00261                          * OS/2 might return "no more files",
00262                          * which just tells us, that searchcount is zero
00263                          * in this search.
00264                          * Guenter Kukkukk <linux@kukkukk.com>
00265                          */
00266 
00267                         if (eclass == ERRDOS && ecode == ERRnofiles) {
00268                                 ff_searchcount = 0;
00269                                 cli_reset_error(cli);
00270                                 break;
00271                         }
00272 
00273                         if (eclass != ERRSRV || ecode != ERRerror)
00274                                 break;
00275                         smb_msleep(100);
00276                         continue;
00277                 }
00278 
00279                 if (cli_is_error(cli) || !rdata || !rparam) {
00280                         SAFE_FREE(rdata);
00281                         SAFE_FREE(rparam);
00282                         break;
00283                 }
00284 
00285                 if (total_received == -1)
00286                         total_received = 0;
00287 
00288                 /* parse out some important return info */
00289                 p = rparam;
00290                 if (First) {
00291                         ff_dir_handle = SVAL(p,0);
00292                         ff_searchcount = SVAL(p,2);
00293                         ff_eos = SVAL(p,4);
00294                 } else {
00295                         ff_searchcount = SVAL(p,0);
00296                         ff_eos = SVAL(p,2);
00297                 }
00298 
00299                 if (ff_searchcount == 0) {
00300                         SAFE_FREE(rdata);
00301                         SAFE_FREE(rparam);
00302                         break;
00303                 }
00304 
00305                 /* point to the data bytes */
00306                 p = rdata;
00307 
00308                 /* we might need the lastname for continuations */
00309                 for (p2=p,i=0;i<ff_searchcount;i++) {
00310                         if ((info_level == 260) && (i == ff_searchcount-1)) {
00311                                 /* Last entry - fixup the last offset length. */
00312                                 SIVAL(p2,0,PTR_DIFF((rdata + data_len),p2));
00313                         }
00314                         p2 += interpret_long_filename(cli,info_level,p2,&finfo,
00315                                                         &resume_key,&last_name_raw,&last_name_raw_len);
00316 
00317                         if (!First && *mask && strcsequal(finfo.name, mask)) {
00318                                 DEBUG(0,("Error: Looping in FIND_NEXT as name %s has already been seen?\n",
00319                                         finfo.name));
00320                                 ff_eos = 1;
00321                                 break;
00322                         }
00323                 }
00324 
00325                 if (ff_searchcount > 0) {
00326                         pstrcpy(mask, finfo.name);
00327                 } else {
00328                         pstrcpy(mask,"");
00329                 }
00330 
00331                 /* grab the data for later use */
00332                 /* and add them to the dirlist pool */
00333                 dirlist = (char *)SMB_REALLOC(dirlist,dirlist_len + data_len);
00334 
00335                 if (!dirlist) {
00336                         DEBUG(0,("cli_list_new: Failed to expand dirlist\n"));
00337                         SAFE_FREE(rdata);
00338                         SAFE_FREE(rparam);
00339                         break;
00340                 }
00341 
00342                 memcpy(dirlist+dirlist_len,p,data_len);
00343                 dirlist_len += data_len;
00344 
00345                 total_received += ff_searchcount;
00346 
00347                 SAFE_FREE(rdata);
00348                 SAFE_FREE(rparam);
00349 
00350                 DEBUG(3,("received %d entries (eos=%d)\n",
00351                          ff_searchcount,ff_eos));
00352 
00353                 if (ff_searchcount > 0)
00354                         loop_count = 0;
00355 
00356                 First = False;
00357         }
00358 
00359         mnt = cli_cm_get_mntpoint( cli );
00360 
00361         /* see if the server disconnected or the connection otherwise failed */
00362         if (cli_is_error(cli)) {
00363                 total_received = -1;
00364         } else {
00365                 /* no connection problem.  let user function add each entry */
00366                 for (p=dirlist,i=0;i<total_received;i++) {
00367                         p += interpret_long_filename(cli, info_level, p,
00368                                                      &finfo,NULL,NULL,NULL);
00369                         fn( mnt,&finfo, Mask, state );
00370                 }
00371         }
00372 
00373         /* free up the dirlist buffer and last name raw blob */
00374         SAFE_FREE(dirlist);
00375         data_blob_free(&last_name_raw);
00376         return(total_received);
00377 }
00378 
00379 /****************************************************************************
00380  Interpret a short filename structure.
00381  The length of the structure is returned.
00382 ****************************************************************************/
00383 
00384 static int interpret_short_filename(struct cli_state *cli, char *p,file_info *finfo)
00385 {
00386 
00387         *finfo = def_finfo;
00388 
00389         finfo->cli = cli;
00390         finfo->mode = CVAL(p,21);
00391         
00392         /* this date is converted to GMT by make_unix_date */
00393         finfo->ctime_ts.tv_sec = cli_make_unix_date(cli, p+22);
00394         finfo->ctime_ts.tv_nsec = 0;
00395         finfo->mtime_ts.tv_sec = finfo->atime_ts.tv_sec = finfo->ctime_ts.tv_sec;
00396         finfo->mtime_ts.tv_nsec = finfo->atime_ts.tv_nsec = 0;
00397         finfo->size = IVAL(p,26);
00398         clistr_pull(cli, finfo->name, p+30, sizeof(finfo->name), 12, STR_ASCII);
00399         if (strcmp(finfo->name, "..") && strcmp(finfo->name, ".")) {
00400                 strncpy(finfo->short_name,finfo->name, sizeof(finfo->short_name)-1);
00401                 finfo->short_name[sizeof(finfo->short_name)-1] = '\0';
00402         }
00403 
00404         return(DIR_STRUCT_SIZE);
00405 }
00406 
00407 
00408 /****************************************************************************
00409  Do a directory listing, calling fn on each file found.
00410  this uses the old SMBsearch interface. It is needed for testing Samba,
00411  but should otherwise not be used.
00412 ****************************************************************************/
00413 
00414 int cli_list_old(struct cli_state *cli,const char *Mask,uint16 attribute, 
00415                  void (*fn)(const char *, file_info *, const char *, void *), void *state)
00416 {
00417         char *p;
00418         int received = 0;
00419         BOOL first = True;
00420         char status[21];
00421         int num_asked = (cli->max_xmit - 100)/DIR_STRUCT_SIZE;
00422         int num_received = 0;
00423         int i;
00424         char *dirlist = NULL;
00425         pstring mask;
00426         
00427         ZERO_ARRAY(status);
00428 
00429         pstrcpy(mask,Mask);
00430   
00431         while (1) {
00432                 memset(cli->outbuf,'\0',smb_size);
00433                 memset(cli->inbuf,'\0',smb_size);
00434 
00435                 set_message(cli->outbuf,2,0,True);
00436 
00437                 SCVAL(cli->outbuf,smb_com,SMBsearch);
00438 
00439                 SSVAL(cli->outbuf,smb_tid,cli->cnum);
00440                 cli_setup_packet(cli);
00441 
00442                 SSVAL(cli->outbuf,smb_vwv0,num_asked);
00443                 SSVAL(cli->outbuf,smb_vwv1,attribute);
00444   
00445                 p = smb_buf(cli->outbuf);
00446                 *p++ = 4;
00447       
00448                 p += clistr_push(cli, p, first?mask:"", -1, STR_TERMINATE);
00449                 *p++ = 5;
00450                 if (first) {
00451                         SSVAL(p,0,0);
00452                         p += 2;
00453                 } else {
00454                         SSVAL(p,0,21);
00455                         p += 2;
00456                         memcpy(p,status,21);
00457                         p += 21;
00458                 }
00459 
00460                 cli_setup_bcc(cli, p);
00461                 cli_send_smb(cli);
00462                 if (!cli_receive_smb(cli)) break;
00463 
00464                 received = SVAL(cli->inbuf,smb_vwv0);
00465                 if (received <= 0) break;
00466 
00467                 first = False;
00468 
00469                 dirlist = (char *)SMB_REALLOC(
00470                         dirlist,(num_received + received)*DIR_STRUCT_SIZE);
00471                 if (!dirlist) {
00472                         DEBUG(0,("cli_list_old: failed to expand dirlist"));
00473                         return 0;
00474                 }
00475 
00476                 p = smb_buf(cli->inbuf) + 3;
00477 
00478                 memcpy(dirlist+num_received*DIR_STRUCT_SIZE,
00479                        p,received*DIR_STRUCT_SIZE);
00480                 
00481                 memcpy(status,p + ((received-1)*DIR_STRUCT_SIZE),21);
00482                 
00483                 num_received += received;
00484                 
00485                 if (cli_is_error(cli)) break;
00486         }
00487 
00488         if (!first) {
00489                 memset(cli->outbuf,'\0',smb_size);
00490                 memset(cli->inbuf,'\0',smb_size);
00491 
00492                 set_message(cli->outbuf,2,0,True);
00493                 SCVAL(cli->outbuf,smb_com,SMBfclose);
00494                 SSVAL(cli->outbuf,smb_tid,cli->cnum);
00495                 cli_setup_packet(cli);
00496 
00497                 SSVAL(cli->outbuf, smb_vwv0, 0); /* find count? */
00498                 SSVAL(cli->outbuf, smb_vwv1, attribute);
00499 
00500                 p = smb_buf(cli->outbuf);
00501                 *p++ = 4;
00502                 fstrcpy(p, "");
00503                 p += strlen(p) + 1;
00504                 *p++ = 5;
00505                 SSVAL(p, 0, 21);
00506                 p += 2;
00507                 memcpy(p,status,21);
00508                 p += 21;
00509                 
00510                 cli_setup_bcc(cli, p);
00511                 cli_send_smb(cli);
00512                 if (!cli_receive_smb(cli)) {
00513                         DEBUG(0,("Error closing search: %s\n",cli_errstr(cli)));
00514                 }
00515         }
00516 
00517         for (p=dirlist,i=0;i<num_received;i++) {
00518                 file_info finfo;
00519                 p += interpret_short_filename(cli, p,&finfo);
00520                 fn("\\", &finfo, Mask, state);
00521         }
00522 
00523         SAFE_FREE(dirlist);
00524         return(num_received);
00525 }
00526 
00527 /****************************************************************************
00528  Do a directory listing, calling fn on each file found.
00529  This auto-switches between old and new style.
00530 ****************************************************************************/
00531 
00532 int cli_list(struct cli_state *cli,const char *Mask,uint16 attribute, 
00533              void (*fn)(const char *, file_info *, const char *, void *), void *state)
00534 {
00535         if (cli->protocol <= PROTOCOL_LANMAN1)
00536                 return cli_list_old(cli, Mask, attribute, fn, state);
00537         return cli_list_new(cli, Mask, attribute, fn, state);
00538 }

Sambaに対してSat Aug 29 21:23:04 2009に生成されました。  doxygen 1.4.7