diff options
Diffstat (limited to 'source/smbd/trans2.c')
-rw-r--r-- | source/smbd/trans2.c | 1592 |
1 files changed, 998 insertions, 594 deletions
diff --git a/source/smbd/trans2.c b/source/smbd/trans2.c index 9d02123cf87..c075ae6c1be 100644 --- a/source/smbd/trans2.c +++ b/source/smbd/trans2.c @@ -2,7 +2,7 @@ Unix SMB/Netbios implementation. Version 1.9. SMB transaction2 handling - Copyright (C) Jeremy Allison 1994 + Copyright (C) Jeremy Allison 1994-1998 Extensively modified by Andrew Tridgell, 1995 @@ -22,15 +22,15 @@ */ #include "includes.h" -#include "loadparm.h" #include "trans2.h" extern int DEBUGLEVEL; extern int Protocol; -extern connection_struct Connections[]; -extern files_struct Files[]; extern BOOL case_sensitive; extern int Client; +extern int smb_read_error; +extern fstring local_machine; +extern int global_oplock_break; /**************************************************************************** Send the required number of replies back. @@ -39,22 +39,23 @@ extern int Client; HACK ! Always assumes smb_setup field is zero. ****************************************************************************/ static int send_trans2_replies(char *outbuf, int bufsize, char *params, - int paramsize, char *pdata, int datasize) + int paramsize, char *pdata, int datasize) { - /* As we are using a protocol > LANMAN1 then the maxxmit + /* As we are using a protocol > LANMAN1 then the max_send variable must have been set in the sessetupX call. This takes precedence over the max_xmit field in the global struct. These different max_xmit variables should be merged as this is now too confusing */ - extern int maxxmit; + extern int max_send; int data_to_send = datasize; int params_to_send = paramsize; int useable_space; char *pp = params; char *pd = pdata; int params_sent_thistime, data_sent_thistime, total_sent_thistime; - int alignment_offset = 1; + int alignment_offset = 3; + int data_alignment_offset = 0; /* Initially set the wcnt area to be 10 - this is true for all trans2 replies */ @@ -63,93 +64,113 @@ static int send_trans2_replies(char *outbuf, int bufsize, char *params, /* If there genuinely are no parameters or data to send just send the empty packet */ if(params_to_send == 0 && data_to_send == 0) - { - send_smb(Client,outbuf); - return 0; - } + { + send_smb(Client,outbuf); + return 0; + } + + /* When sending params and data ensure that both are nicely aligned */ + /* Only do this alignment when there is also data to send - else + can cause NT redirector problems. */ + if (((params_to_send % 4) != 0) && (data_to_send != 0)) + data_alignment_offset = 4 - (params_to_send % 4); /* Space is bufsize minus Netbios over TCP header minus SMB header */ - /* The + 1 is to align the param and data bytes on an even byte + /* The alignment_offset is to align the param bytes on an even byte boundary. NT 4.0 Beta needs this to work correctly. */ - useable_space = bufsize - ((smb_buf(outbuf)+alignment_offset) - outbuf); - useable_space = MIN(useable_space, maxxmit); /* XXX is this needed? correct? */ + useable_space = bufsize - ((smb_buf(outbuf)+ + alignment_offset+data_alignment_offset) - + outbuf); - while( params_to_send || data_to_send) - { - /* Calculate whether we will totally or partially fill this packet */ - total_sent_thistime = params_to_send + data_to_send + alignment_offset; - total_sent_thistime = MIN(total_sent_thistime, useable_space); + /* useable_space can never be more than max_send minus the + alignment offset. */ + useable_space = MIN(useable_space, + max_send - (alignment_offset+data_alignment_offset)); - set_message(outbuf, 10, total_sent_thistime, True); - /* Set total params and data to be sent */ - SSVAL(outbuf,smb_tprcnt,paramsize); - SSVAL(outbuf,smb_tdrcnt,datasize); + while (params_to_send || data_to_send) + { + /* Calculate whether we will totally or partially fill this packet */ + total_sent_thistime = params_to_send + data_to_send + + alignment_offset + data_alignment_offset; + /* We can never send more than useable_space */ + total_sent_thistime = MIN(total_sent_thistime, useable_space); - /* Calculate how many parameters and data we can fit into - this packet. Parameters get precedence */ + set_message(outbuf, 10, total_sent_thistime, True); - params_sent_thistime = MIN(params_to_send,useable_space); - data_sent_thistime = useable_space - params_sent_thistime; - data_sent_thistime = MIN(data_sent_thistime,data_to_send); + /* Set total params and data to be sent */ + SSVAL(outbuf,smb_tprcnt,paramsize); + SSVAL(outbuf,smb_tdrcnt,datasize); - SSVAL(outbuf,smb_prcnt, params_sent_thistime); - if(params_sent_thistime == 0) - { - SSVAL(outbuf,smb_proff,0); - SSVAL(outbuf,smb_prdisp,0); - } else { - /* smb_proff is the offset from the start of the SMB header to the - parameter bytes, however the first 4 bytes of outbuf are - the Netbios over TCP header. Thus use smb_base() to subtract - them from the calculation */ - SSVAL(outbuf,smb_proff,((smb_buf(outbuf)+alignment_offset) - smb_base(outbuf))); - /* Absolute displacement of param bytes sent in this packet */ - SSVAL(outbuf,smb_prdisp,pp - params); - } + /* Calculate how many parameters and data we can fit into + this packet. Parameters get precedence */ - SSVAL(outbuf,smb_drcnt, data_sent_thistime); - if(data_sent_thistime == 0) - { - SSVAL(outbuf,smb_droff,0); - SSVAL(outbuf,smb_drdisp, 0); - } else { - /* The offset of the data bytes is the offset of the - parameter bytes plus the number of parameters being sent this time */ - SSVAL(outbuf,smb_droff,((smb_buf(outbuf)+alignment_offset) - - smb_base(outbuf)) + params_sent_thistime); - SSVAL(outbuf,smb_drdisp, pd - pdata); - } + params_sent_thistime = MIN(params_to_send,useable_space); + data_sent_thistime = useable_space - params_sent_thistime; + data_sent_thistime = MIN(data_sent_thistime,data_to_send); - /* Copy the param bytes into the packet */ - if(params_sent_thistime) - memcpy((smb_buf(outbuf)+alignment_offset),pp,params_sent_thistime); - /* Copy in the data bytes */ - if(data_sent_thistime) - memcpy(smb_buf(outbuf)+alignment_offset+params_sent_thistime,pd,data_sent_thistime); + SSVAL(outbuf,smb_prcnt, params_sent_thistime); + if(params_sent_thistime == 0) + { + SSVAL(outbuf,smb_proff,0); + SSVAL(outbuf,smb_prdisp,0); + } + else + { + /* smb_proff is the offset from the start of the SMB header to the + parameter bytes, however the first 4 bytes of outbuf are + the Netbios over TCP header. Thus use smb_base() to subtract + them from the calculation */ + SSVAL(outbuf,smb_proff,((smb_buf(outbuf)+alignment_offset) - smb_base(outbuf))); + /* Absolute displacement of param bytes sent in this packet */ + SSVAL(outbuf,smb_prdisp,pp - params); + } - DEBUG(9,("t2_rep: params_sent_thistime = %d, data_sent_thistime = %d, useable_space = %d\n", - params_sent_thistime, data_sent_thistime, useable_space)); - DEBUG(9,("t2_rep: params_to_send = %d, data_to_send = %d, paramsize = %d, datasize = %d\n", - params_to_send, data_to_send, paramsize, datasize)); + SSVAL(outbuf,smb_drcnt, data_sent_thistime); + if(data_sent_thistime == 0) + { + SSVAL(outbuf,smb_droff,0); + SSVAL(outbuf,smb_drdisp, 0); + } + else + { + /* The offset of the data bytes is the offset of the + parameter bytes plus the number of parameters being sent this time */ + SSVAL(outbuf,smb_droff,((smb_buf(outbuf)+alignment_offset) - + smb_base(outbuf)) + params_sent_thistime + data_alignment_offset); + SSVAL(outbuf,smb_drdisp, pd - pdata); + } - /* Send the packet */ - send_smb(Client,outbuf); + /* Copy the param bytes into the packet */ + if(params_sent_thistime) + memcpy((smb_buf(outbuf)+alignment_offset),pp,params_sent_thistime); + /* Copy in the data bytes */ + if(data_sent_thistime) + memcpy(smb_buf(outbuf)+alignment_offset+params_sent_thistime+ + data_alignment_offset,pd,data_sent_thistime); - pp += params_sent_thistime; - pd += data_sent_thistime; + DEBUG(9,("t2_rep: params_sent_thistime = %d, data_sent_thistime = %d, useable_space = %d\n", + params_sent_thistime, data_sent_thistime, useable_space)); + DEBUG(9,("t2_rep: params_to_send = %d, data_to_send = %d, paramsize = %d, datasize = %d\n", + params_to_send, data_to_send, paramsize, datasize)); - params_to_send -= params_sent_thistime; - data_to_send -= data_sent_thistime; + /* Send the packet */ + send_smb(Client,outbuf); - /* Sanity check */ - if(params_to_send < 0 || data_to_send < 0) - { - DEBUG(2,("send_trans2_replies failed sanity check pts = %d, dts = %d\n!!!", - params_to_send, data_to_send)); - return -1; - } + pp += params_sent_thistime; + pd += data_sent_thistime; + + params_to_send -= params_sent_thistime; + data_to_send -= data_sent_thistime; + + /* Sanity check */ + if(params_to_send < 0 || data_to_send < 0) + { + DEBUG(0,("send_trans2_replies failed sanity check pts = %d, dts = %d\n!!!", + params_to_send, data_to_send)); + return -1; } + } return 0; } @@ -158,12 +179,14 @@ static int send_trans2_replies(char *outbuf, int bufsize, char *params, /**************************************************************************** reply to a TRANSACT2_OPEN ****************************************************************************/ -static int call_trans2open(char *inbuf, char *outbuf, int bufsize, int cnum, - char **pparams, char **ppdata) +static int call_trans2open(connection_struct *conn, char *inbuf, char *outbuf, + int bufsize, + char **pparams, char **ppdata) { char *params = *pparams; int16 open_mode = SVAL(params, 2); int16 open_attr = SVAL(params,6); + BOOL oplock_request = (((SVAL(params,0)|(1<<1))>>1) | ((SVAL(params,0)|(1<<2))>>1)); #if 0 BOOL return_additional_info = BITSETW(params,0); int16 open_sattr = SVAL(params, 4); @@ -175,49 +198,66 @@ static int call_trans2open(char *inbuf, char *outbuf, int bufsize, int cnum, int16 namelen = strlen(pname)+1; pstring fname; - int fnum = -1; - int unixmode; - int size=0,fmode=0,mtime=0,rmode; - int32 inode = 0; - struct stat sbuf; + mode_t unixmode; + SMB_OFF_T size=0; + int fmode=0,mtime=0,rmode; + SMB_INO_T inode = 0; + SMB_STRUCT_STAT sbuf; int smb_action = 0; + BOOL bad_path = False; + files_struct *fsp; StrnCpy(fname,pname,namelen); - DEBUG(3,("trans2open %s cnum=%d mode=%d attr=%d ofun=%d size=%d\n", - fname,cnum,open_mode, open_attr, open_ofun, open_size)); + DEBUG(3,("trans2open %s mode=%d attr=%d ofun=%d size=%d\n", + fname,open_mode, open_attr, open_ofun, open_size)); /* XXXX we need to handle passed times, sattr and flags */ - unix_convert(fname,cnum); + unix_convert(fname,conn,0,&bad_path,NULL); - fnum = find_free_file(); - if (fnum < 0) + fsp = file_new(); + if (!fsp) return(ERROR(ERRSRV,ERRnofids)); - if (!check_name(fname,cnum)) + if (!check_name(fname,conn)) + { + if((errno == ENOENT) && bad_path) + { + unix_ERR_class = ERRDOS; + unix_ERR_code = ERRbadpath; + } + file_free(fsp); return(UNIXERROR(ERRDOS,ERRnoaccess)); + } - unixmode = unix_mode(cnum,open_attr | aARCH); - + unixmode = unix_mode(conn,open_attr | aARCH); - open_file_shared(fnum,cnum,fname,open_mode,open_ofun,unixmode, - &rmode,&smb_action); + open_file_shared(fsp,conn,fname,open_mode,open_ofun,unixmode, + oplock_request, &rmode,&smb_action); - if (!Files[fnum].open) + if (!fsp->open) + { + if((errno == ENOENT) && bad_path) + { + unix_ERR_class = ERRDOS; + unix_ERR_code = ERRbadpath; + } + file_free(fsp); return(UNIXERROR(ERRDOS,ERRnoaccess)); + } - if (fstat(Files[fnum].fd,&sbuf) != 0) { - close_file(fnum); - return(ERROR(ERRDOS,ERRnoaccess)); + if (sys_fstat(fsp->fd_ptr->fd,&sbuf) != 0) { + close_file(fsp,False); + return(UNIXERROR(ERRDOS,ERRnoaccess)); } size = sbuf.st_size; - fmode = dos_mode(cnum,fname,&sbuf); + fmode = dos_mode(conn,fname,&sbuf); mtime = sbuf.st_mtime; inode = sbuf.st_ino; if (fmode & aDIR) { - close_file(fnum); + close_file(fsp,False); return(ERROR(ERRDOS,ERRnoaccess)); } @@ -227,13 +267,20 @@ static int call_trans2open(char *inbuf, char *outbuf, int bufsize, int cnum, return(ERROR(ERRDOS,ERRnomem)); bzero(params,28); - SSVAL(params,0,fnum); + SSVAL(params,0,fsp->fnum); SSVAL(params,2,fmode); put_dos_date2(params,4, mtime); - SIVAL(params,8, size); + SIVAL(params,8, (uint32)size); SSVAL(params,12,rmode); + if (oplock_request && lp_fake_oplocks(SNUM(conn))) { + smb_action |= EXTENDED_OPLOCK_GRANTED; + } + SSVAL(params,18,smb_action); + /* + * WARNING - this may need to be changed if SMB_INO_T <> 4 bytes. + */ SIVAL(params,20,inode); /* Send the required number of replies */ @@ -245,7 +292,8 @@ static int call_trans2open(char *inbuf, char *outbuf, int bufsize, int cnum, /**************************************************************************** get a level dependent lanman2 dir entry. ****************************************************************************/ -static int get_lanman2_dir_entry(int cnum,char *path_mask,int dirtype,int info_level, +static int get_lanman2_dir_entry(connection_struct *conn, + char *path_mask,int dirtype,int info_level, int requires_resume_key, BOOL dont_descend,char **ppdata, char *base_data, int space_remaining, @@ -254,140 +302,146 @@ static int get_lanman2_dir_entry(int cnum,char *path_mask,int dirtype,int info_l { char *dname; BOOL found = False; - struct stat sbuf; + SMB_STRUCT_STAT sbuf; pstring mask; pstring pathreal; pstring fname; - BOOL matched; char *p, *pdata = *ppdata; - int reskey=0, prev_dirpos=0; + uint32 reskey=0; + int prev_dirpos=0; int mode=0; - uint32 size=0,len; - uint32 mdate=0, adate=0, cdate=0; - char *name_ptr; - BOOL isrootdir = (strequal(Connections[cnum].dirpath,"./") || - strequal(Connections[cnum].dirpath,".") || - strequal(Connections[cnum].dirpath,"/")); + SMB_OFF_T size = 0; + uint32 len; + time_t mdate=0, adate=0, cdate=0; + char *nameptr; + BOOL isrootdir = (strequal(conn->dirpath,"./") || + strequal(conn->dirpath,".") || + strequal(conn->dirpath,"/")); BOOL was_8_3; + int nt_extmode; /* Used for NT connections instead of mode */ + BOOL needslash = ( conn->dirpath[strlen(conn->dirpath) -1] != '/'); *fname = 0; *out_of_space = False; - if (!Connections[cnum].dirptr) + if (!conn->dirptr) return(False); p = strrchr(path_mask,'/'); if(p != NULL) - { - if(p[1] == '\0') - strcpy(mask,"*.*"); - else - strcpy(mask, p+1); - } + { + if(p[1] == '\0') + pstrcpy(mask,"*.*"); + else + pstrcpy(mask, p+1); + } else - strcpy(mask, path_mask); + pstrcpy(mask, path_mask); while (!found) - { - /* Needed if we run out of space */ - prev_dirpos = TellDir(Connections[cnum].dirptr); - dname = ReadDirName(Connections[cnum].dirptr); + { + /* Needed if we run out of space */ + prev_dirpos = TellDir(conn->dirptr); + dname = ReadDirName(conn->dirptr); - reskey = TellDir(Connections[cnum].dirptr); + /* + * Due to bugs in NT client redirectors we are not using + * resume keys any more - set them to zero. + * Check out the related comments in findfirst/findnext. + * JRA. + */ - DEBUG(6,("get_lanman2_dir_entry:readdir on dirptr 0x%x now at offset %d\n", - Connections[cnum].dirptr,TellDir(Connections[cnum].dirptr))); - - if (!dname) - return(False); + reskey = 0; - matched = False; + DEBUG(8,("get_lanman2_dir_entry:readdir on dirptr 0x%lx now at offset %d\n", + (long)conn->dirptr,TellDir(conn->dirptr))); + + if (!dname) + return(False); - strcpy(fname,dname); + pstrcpy(fname,dname); - if(mask_match(fname, mask, case_sensitive, True)) - { - BOOL isdots = (strequal(fname,"..") || strequal(fname,".")); - if (dont_descend && !isdots) - continue; + if(mask_match(fname, mask, case_sensitive, True)) + { + BOOL isdots = (strequal(fname,"..") || strequal(fname,".")); + if (dont_descend && !isdots) + continue; - if (isrootdir && isdots) - continue; - - strcpy(pathreal,Connections[cnum].dirpath); - strcat(pathreal,"/"); - strcat(pathreal,fname); - if (sys_stat(pathreal,&sbuf) != 0) - { - DEBUG(5,("get_lanman2_dir_entry:Couldn't stat [%s] (%s)\n",pathreal,strerror(errno))); - continue; - } - - mode = dos_mode(cnum,pathreal,&sbuf); - - if (((mode & ~dirtype) & (aHIDDEN | aSYSTEM | aDIR)) != 0) - { - DEBUG(5,("[%s] attribs didn't match %x\n",fname,dirtype)); - continue; - } - size = sbuf.st_size; - mdate = sbuf.st_mtime; - adate = sbuf.st_atime; - cdate = sbuf.st_ctime; - if(mode & aDIR) - size = 0; - - DEBUG(5,("get_lanman2_dir_entry found %s fname=%s\n",pathreal,fname)); + if (isrootdir && isdots) + continue; + + pstrcpy(pathreal,conn->dirpath); + if(needslash) + pstrcat(pathreal,"/"); + pstrcat(pathreal,dname); + if (dos_stat(pathreal,&sbuf) != 0) + { + DEBUG(5,("get_lanman2_dir_entry:Couldn't stat [%s] (%s)\n",pathreal,strerror(errno))); + continue; + } + + mode = dos_mode(conn,pathreal,&sbuf); + + if (!dir_check_ftype(conn,mode,&sbuf,dirtype)) { + DEBUG(5,("[%s] attribs didn't match %x\n",fname,dirtype)); + continue; + } + + size = sbuf.st_size; + mdate = sbuf.st_mtime; + adate = sbuf.st_atime; + cdate = get_create_time(&sbuf,lp_fake_dir_create_times(SNUM(conn))); + if(mode & aDIR) + size = 0; + + DEBUG(5,("get_lanman2_dir_entry found %s fname=%s\n",pathreal,fname)); - found = True; - } + found = True; } + } - -#ifndef KANJI - unix2dos_format(fname, True); -#endif + name_map_mangle(fname,False,SNUM(conn)); p = pdata; - name_ptr = p; + nameptr = p; - name_map_mangle(fname,False,SNUM(cnum)); + nt_extmode = mode ? mode : FILE_ATTRIBUTE_NORMAL; switch (info_level) - { + { case 1: if(requires_resume_key) { - SIVAL(p,0,reskey); - p += 4; + SIVAL(p,0,reskey); + p += 4; } put_dos_date2(p,l1_fdateCreation,cdate); put_dos_date2(p,l1_fdateLastAccess,adate); put_dos_date2(p,l1_fdateLastWrite,mdate); - SIVAL(p,l1_cbFile,size); + SIVAL(p,l1_cbFile,(uint32)size); SIVAL(p,l1_cbFileAlloc,ROUNDUP(size,1024)); SSVAL(p,l1_attrFile,mode); SCVAL(p,l1_cchName,strlen(fname)); - strcpy(p + l1_achName, fname); - name_ptr = p + l1_achName; + pstrcpy(p + l1_achName, fname); + nameptr = p + l1_achName; p += l1_achName + strlen(fname) + 1; break; case 2: /* info_level 2 */ if(requires_resume_key) { - SIVAL(p,0,reskey); - p += 4; + SIVAL(p,0,reskey); + p += 4; } put_dos_date2(p,l2_fdateCreation,cdate); put_dos_date2(p,l2_fdateLastAccess,adate); put_dos_date2(p,l2_fdateLastWrite,mdate); - SIVAL(p,l2_cbFile,size); + SIVAL(p,l2_cbFile,(uint32)size); SIVAL(p,l2_cbFileAlloc,ROUNDUP(size,1024)); SSVAL(p,l2_attrFile,mode); SIVAL(p,l2_cbList,0); /* No extended attributes */ SCVAL(p,l2_cchName,strlen(fname)); - strcpy(p + l2_achName, fname); - name_ptr = p + l2_achName; + pstrcpy(p + l2_achName, fname); + nameptr = p + l2_achName; p += l2_achName + strlen(fname) + 1; break; @@ -396,36 +450,36 @@ static int get_lanman2_dir_entry(int cnum,char *path_mask,int dirtype,int info_l put_dos_date2(p,4,cdate); put_dos_date2(p,8,adate); put_dos_date2(p,12,mdate); - SIVAL(p,16,size); + SIVAL(p,16,(uint32)size); SIVAL(p,20,ROUNDUP(size,1024)); SSVAL(p,24,mode); SIVAL(p,26,4); CVAL(p,30) = strlen(fname); - strcpy(p+31, fname); - name_ptr = p+31; + pstrcpy(p+31, fname); + nameptr = p+31; p += 31 + strlen(fname) + 1; break; case 4: if(requires_resume_key) { - SIVAL(p,0,reskey); - p += 4; + SIVAL(p,0,reskey); + p += 4; } SIVAL(p,0,33+strlen(fname)+1); put_dos_date2(p,4,cdate); put_dos_date2(p,8,adate); put_dos_date2(p,12,mdate); - SIVAL(p,16,size); + SIVAL(p,16,(uint32)size); SIVAL(p,20,ROUNDUP(size,1024)); SSVAL(p,24,mode); CVAL(p,32) = strlen(fname); - strcpy(p + 33, fname); - name_ptr = p+33; + pstrcpy(p + 33, fname); + nameptr = p+33; p += 33 + strlen(fname) + 1; break; case SMB_FIND_FILE_BOTH_DIRECTORY_INFO: - was_8_3 = is_8_3(fname); + was_8_3 = is_8_3(fname, True); len = 94+strlen(fname); len = (len + 3) & ~3; SIVAL(p,0,len); p += 4; @@ -434,26 +488,23 @@ static int get_lanman2_dir_entry(int cnum,char *path_mask,int dirtype,int info_l put_long_date(p,adate); p += 8; put_long_date(p,mdate); p += 8; put_long_date(p,mdate); p += 8; - SIVAL(p,0,size); p += 8; - SIVAL(p,0,size); p += 8; - SIVAL(p,0,mode); p += 4; + SOFF_T(p,0,size); + SOFF_T(p,8,size); + p += 16; + SIVAL(p,0,nt_extmode); p += 4; SIVAL(p,0,strlen(fname)); p += 4; SIVAL(p,0,0); p += 4; if (!was_8_3) { -#ifndef KANJI - strcpy(p+2,unix2dos_format(fname,False)); -#else - strcpy(p+2,fname); -#endif - if (!name_map_mangle(p+2,True,SNUM(cnum))) - (p+2)[12] = 0; + pstrcpy(p+2,fname); + if (!name_map_mangle(p+2,True,SNUM(conn))) + (p+2)[12] = 0; } else - *(p+2) = 0; + *(p+2) = 0; strupper(p+2); SSVAL(p,0,strlen(p+2)); p += 2 + 24; - /* name_ptr = p; */ - strcpy(p,fname); p += strlen(p); + /* nameptr = p; */ + pstrcpy(p,fname); p += strlen(p); p = pdata + len; break; @@ -466,11 +517,12 @@ static int get_lanman2_dir_entry(int cnum,char *path_mask,int dirtype,int info_l put_long_date(p,adate); p += 8; put_long_date(p,mdate); p += 8; put_long_date(p,mdate); p += 8; - SIVAL(p,0,size); p += 8; - SIVAL(p,0,size); p += 8; - SIVAL(p,0,mode); p += 4; + SOFF_T(p,0,size); + SOFF_T(p,8,size); + p += 16; + SIVAL(p,0,nt_extmode); p += 4; SIVAL(p,0,strlen(fname)); p += 4; - strcpy(p,fname); + pstrcpy(p,fname); p = pdata + len; break; @@ -484,12 +536,13 @@ static int get_lanman2_dir_entry(int cnum,char *path_mask,int dirtype,int info_l put_long_date(p,adate); p += 8; put_long_date(p,mdate); p += 8; put_long_date(p,mdate); p += 8; - SIVAL(p,0,size); p += 8; - SIVAL(p,0,size); p += 8; - SIVAL(p,0,mode); p += 4; + SOFF_T(p,0,size); + SOFF_T(p,8,size); + p += 16; + SIVAL(p,0,nt_extmode); p += 4; SIVAL(p,0,strlen(fname)); p += 4; SIVAL(p,0,0); p += 4; - strcpy(p,fname); + pstrcpy(p,fname); p = pdata + len; break; @@ -499,7 +552,7 @@ static int get_lanman2_dir_entry(int cnum,char *path_mask,int dirtype,int info_l SIVAL(p,0,len); p += 4; SIVAL(p,0,reskey); p += 4; SIVAL(p,0,strlen(fname)); p += 4; - strcpy(p,fname); + pstrcpy(p,fname); p = pdata + len; break; @@ -510,24 +563,52 @@ static int get_lanman2_dir_entry(int cnum,char *path_mask,int dirtype,int info_l if (PTR_DIFF(p,pdata) > space_remaining) { /* Move the dirptr back to prev_dirpos */ - SeekDir(Connections[cnum].dirptr, prev_dirpos); + SeekDir(conn->dirptr, prev_dirpos); *out_of_space = True; DEBUG(9,("get_lanman2_dir_entry: out of space\n")); return False; /* Not finished - just out of space */ } /* Setup the last_filename pointer, as an offset from base_data */ - *last_name_off = PTR_DIFF(name_ptr,base_data); + *last_name_off = PTR_DIFF(nameptr,base_data); /* Advance the data pointer to the next slot */ *ppdata = p; return(found); } /**************************************************************************** + Convert the directory masks formated for the wire. +****************************************************************************/ + +void mask_convert( char *mask) +{ + /* + * We know mask is a pstring. + */ + char *p = mask; + while (*p) { + if (*p == '<') { + pstring expnd; + if(p[1] != '"' && p[1] != '.') { + pstrcpy( expnd, p+1 ); + *p++ = '*'; + *p = '.'; + safe_strcpy( p+1, expnd, sizeof(pstring) - (p - mask) - 2); + } else + *p = '*'; + } + if (*p == '>') *p = '?'; + if (*p == '"') *p = '.'; + p++; + } +} + +/**************************************************************************** reply to a TRANS2_FINDFIRST ****************************************************************************/ -static int call_trans2findfirst(char *inbuf, char *outbuf, int bufsize, int cnum, - char **pparams, char **ppdata) +static int call_trans2findfirst(connection_struct *conn, + char *inbuf, char *outbuf, int bufsize, + char **pparams, char **ppdata) { /* We must be careful here that we don't return more than the allowed number of data bytes. If this means returning fewer than @@ -554,6 +635,7 @@ static int call_trans2findfirst(char *inbuf, char *outbuf, int bufsize, int cnum BOOL dont_descend = False; BOOL out_of_space = False; int space_remaining; + BOOL bad_path = False; *directory = *mask = 0; @@ -576,22 +658,38 @@ static int call_trans2findfirst(char *inbuf, char *outbuf, int bufsize, int cnum return(ERROR(ERRDOS,ERRunknownlevel)); } - strcpy(directory, params + 12); /* Complete directory path with + pstrcpy(directory, params + 12); /* Complete directory path with wildcard mask appended */ DEBUG(5,("path=%s\n",directory)); - unix_convert(directory,cnum); - if(!check_name(directory,cnum)) { - return(ERROR(ERRDOS,ERRbadpath)); + unix_convert(directory,conn,0,&bad_path,NULL); + if(!check_name(directory,conn)) { + if((errno == ENOENT) && bad_path) + { + unix_ERR_class = ERRDOS; + unix_ERR_code = ERRbadpath; + } + +#if 0 + /* Ugly - NT specific hack - maybe not needed ? (JRA) */ + if((errno == ENOTDIR) && (Protocol >= PROTOCOL_NT1) && + (get_remote_arch() == RA_WINNT)) + { + unix_ERR_class = ERRDOS; + unix_ERR_code = ERRbaddirectory; + } +#endif + + return(UNIXERROR(ERRDOS,ERRbadpath)); } p = strrchr(directory,'/'); if(p == NULL) { - strcpy(mask,directory); - strcpy(directory,"./"); + pstrcpy(mask,directory); + pstrcpy(directory,"./"); } else { - strcpy(mask,p+1); + pstrcpy(mask,p+1); *p = 0; } @@ -607,27 +705,27 @@ static int call_trans2findfirst(char *inbuf, char *outbuf, int bufsize, int cnum if(params == NULL) return(ERROR(ERRDOS,ERRnomem)); - dptr_num = dptr_create(cnum,directory, True ,SVAL(inbuf,smb_pid)); + dptr_num = dptr_create(conn,directory, True ,SVAL(inbuf,smb_pid)); if (dptr_num < 0) - return(ERROR(ERRDOS,ERRbadpath)); + return(UNIXERROR(ERRDOS,ERRbadfile)); + + /* Convert the formatted mask. */ + mask_convert(mask); + +#if 0 /* JRA */ + /* + * Now we have a working mask_match in util.c, I believe + * we no longer need these hacks (in fact they break + * things). JRA. + */ - /* convert the formatted masks */ - { - p = mask; - while (*p) { - if (*p == '<') *p = '*'; - if (*p == '>') *p = '?'; - if (*p == '"') *p = '.'; - p++; - } - } - /* a special case for 16 bit apps */ - if (strequal(mask,"????????.???")) strcpy(mask,"*"); + if (strequal(mask,"????????.???")) pstrcpy(mask,"*"); /* handle broken clients that send us old 8.3 format */ string_sub(mask,"????????","*"); string_sub(mask,".???",".*"); +#endif /* JRA */ /* Save the wildcard match and attribs we are using on this directory - needed as lanman2 assumes these are being saved between calls */ @@ -646,8 +744,8 @@ static int call_trans2findfirst(char *inbuf, char *outbuf, int bufsize, int cnum a different TRANS2 call. */ DEBUG(8,("dirpath=<%s> dontdescend=<%s>\n", - Connections[cnum].dirpath,lp_dontdescend(SNUM(cnum)))); - if (in_list(Connections[cnum].dirpath,lp_dontdescend(SNUM(cnum)),case_sensitive)) + conn->dirpath,lp_dontdescend(SNUM(conn)))); + if (in_list(conn->dirpath,lp_dontdescend(SNUM(conn)),case_sensitive)) dont_descend = True; p = pdata; @@ -667,7 +765,7 @@ static int call_trans2findfirst(char *inbuf, char *outbuf, int bufsize, int cnum else { finished = - !get_lanman2_dir_entry(cnum,mask,dirtype,info_level, + !get_lanman2_dir_entry(conn,mask,dirtype,info_level, requires_resume_key,dont_descend, &p,pdata,space_remaining, &out_of_space, &last_name_off); @@ -689,6 +787,14 @@ static int call_trans2findfirst(char *inbuf, char *outbuf, int bufsize, int cnum dptr_num = -1; } + /* + * If there are no matching entries we must return ERRDOS/ERRbadfile - + * from observation of NT. + */ + + if(numentries == 0) + return(ERROR(ERRDOS,ERRbadfile)); + /* At this point pdata points to numentries directory entries. */ /* Set up the return parameter block */ @@ -701,12 +807,11 @@ static int call_trans2findfirst(char *inbuf, char *outbuf, int bufsize, int cnum send_trans2_replies( outbuf, bufsize, params, 10, pdata, PTR_DIFF(p,pdata)); if ((! *directory) && dptr_path(dptr_num)) - sprintf(directory,"(%s)",dptr_path(dptr_num)); + slprintf(directory,sizeof(directory)-1, "(%s)",dptr_path(dptr_num)); - DEBUG(4,("%s %s mask=%s directory=%s cnum=%d dirtype=%d numentries=%d\n", - timestring(), - smb_fn_name(CVAL(inbuf,smb_com)), - mask,directory,cnum,dirtype,numentries)); + DEBUG( 4, ( "%s mask=%s directory=%s dirtype=%d numentries=%d\n", + smb_fn_name(CVAL(inbuf,smb_com)), + mask, directory, dirtype, numentries ) ); return(-1); } @@ -715,8 +820,10 @@ static int call_trans2findfirst(char *inbuf, char *outbuf, int bufsize, int cnum /**************************************************************************** reply to a TRANS2_FINDNEXT ****************************************************************************/ -static int call_trans2findnext(char *inbuf, char *outbuf, int length, int bufsize, - int cnum, char **pparams, char **ppdata) +static int call_trans2findnext(connection_struct *conn, + char *inbuf, char *outbuf, + int length, int bufsize, + char **pparams, char **ppdata) { /* We must be careful here that we don't return more than the allowed number of data bytes. If this means returning fewer than @@ -734,6 +841,7 @@ static int call_trans2findnext(char *inbuf, char *outbuf, int length, int bufsiz BOOL close_if_end = BITSETW(params+10,1); BOOL requires_resume_key = BITSETW(params+10,2); BOOL continue_bit = BITSETW(params+10,3); + pstring resume_name; pstring mask; pstring directory; char *p; @@ -745,11 +853,15 @@ static int call_trans2findnext(char *inbuf, char *outbuf, int length, int bufsiz BOOL out_of_space = False; int space_remaining; - *mask = *directory = 0; + *mask = *directory = *resume_name = 0; - DEBUG(3,("call_trans2findnext: dirhandle = %d, max_data_bytes = %d, maxentries = %d, close_after_request=%d, close_if_end = %d requires_resume_key = %d resume_key = %d continue=%d level = %d\n", + pstrcpy( resume_name, params+12); + + DEBUG(3,("call_trans2findnext: dirhandle = %d, max_data_bytes = %d, maxentries = %d, \ +close_after_request=%d, close_if_end = %d requires_resume_key = %d \ +resume_key = %d resume name = %s continue=%d level = %d\n", dptr_num, max_data_bytes, maxentries, close_after_request, close_if_end, - requires_resume_key, resume_key, continue_bit, info_level)); + requires_resume_key, resume_key, resume_name, continue_bit, info_level)); switch (info_level) { @@ -777,41 +889,114 @@ static int call_trans2findnext(char *inbuf, char *outbuf, int length, int bufsiz return(ERROR(ERRDOS,ERRnomem)); /* Check that the dptr is valid */ - if(!(Connections[cnum].dirptr = dptr_fetch_lanman2(params, dptr_num))) + if(!(conn->dirptr = dptr_fetch_lanman2(dptr_num))) return(ERROR(ERRDOS,ERRnofiles)); - string_set(&Connections[cnum].dirpath,dptr_path(dptr_num)); + string_set(&conn->dirpath,dptr_path(dptr_num)); /* Get the wildcard mask from the dptr */ if((p = dptr_wcard(dptr_num))== NULL) { DEBUG(2,("dptr_num %d has no wildcard\n", dptr_num)); return (ERROR(ERRDOS,ERRnofiles)); } - strcpy(mask, p); - strcpy(directory,Connections[cnum].dirpath); + pstrcpy(mask, p); + pstrcpy(directory,conn->dirpath); /* Get the attr mask from the dptr */ dirtype = dptr_attr(dptr_num); - DEBUG(3,("dptr_num is %d, mask = %s, attr = %x, dirptr=(0x%X,%d)\n", + DEBUG(3,("dptr_num is %d, mask = %s, attr = %x, dirptr=(0x%lX,%d)\n", dptr_num, mask, dirtype, - Connections[cnum].dirptr, - TellDir(Connections[cnum].dirptr))); + (long)conn->dirptr, + TellDir(conn->dirptr))); /* We don't need to check for VOL here as this is returned by a different TRANS2 call. */ - DEBUG(8,("dirpath=<%s> dontdescend=<%s>\n",Connections[cnum].dirpath,lp_dontdescend(SNUM(cnum)))); - if (in_list(Connections[cnum].dirpath,lp_dontdescend(SNUM(cnum)),case_sensitive)) + DEBUG(8,("dirpath=<%s> dontdescend=<%s>\n",conn->dirpath,lp_dontdescend(SNUM(conn)))); + if (in_list(conn->dirpath,lp_dontdescend(SNUM(conn)),case_sensitive)) dont_descend = True; p = pdata; space_remaining = max_data_bytes; out_of_space = False; - /* If we have a resume key - seek to the correct position. */ - if(requires_resume_key && !continue_bit) - SeekDir(Connections[cnum].dirptr, resume_key); + /* + * Seek to the correct position. We no longer use the resume key but + * depend on the last file name instead. + */ + if(requires_resume_key && *resume_name && !continue_bit) + { + /* + * Fix for NT redirector problem triggered by resume key indexes + * changing between directory scans. We now return a resume key of 0 + * and instead look for the filename to continue from (also given + * to us by NT/95/smbfs/smbclient). If no other scans have been done between the + * findfirst/findnext (as is usual) then the directory pointer + * should already be at the correct place. Check this by scanning + * backwards looking for an exact (ie. case sensitive) filename match. + * If we get to the beginning of the directory and haven't found it then scan + * forwards again looking for a match. JRA. + */ + + int current_pos, start_pos; + char *dname = NULL; + void *dirptr = conn->dirptr; + start_pos = TellDir(dirptr); + for(current_pos = start_pos; current_pos >= 0; current_pos--) + { + DEBUG(7,("call_trans2findnext: seeking to pos %d\n", current_pos)); + + SeekDir(dirptr, current_pos); + dname = ReadDirName(dirptr); + + /* + * Remember, name_map_mangle is called by + * get_lanman2_dir_entry(), so the resume name + * could be mangled. Ensure we do the same + * here. + */ + + if(dname != NULL) + name_map_mangle( dname, False, SNUM(conn)); + + if(dname && strcsequal( resume_name, dname)) + { + SeekDir(dirptr, current_pos+1); + DEBUG(7,("call_trans2findnext: got match at pos %d\n", current_pos+1 )); + break; + } + } + + /* + * Scan forward from start if not found going backwards. + */ + + if(current_pos < 0) + { + DEBUG(7,("call_trans2findnext: notfound: seeking to pos %d\n", start_pos)); + SeekDir(dirptr, start_pos); + for(current_pos = start_pos; (dname = ReadDirName(dirptr)) != NULL; SeekDir(dirptr,++current_pos)) + { + /* + * Remember, name_map_mangle is called by + * get_lanman2_dir_entry(), so the resume name + * could be mangled. Ensure we do the same + * here. + */ + + if(dname != NULL) + name_map_mangle( dname, False, SNUM(conn)); + + if(dname && strcsequal( resume_name, dname)) + { + SeekDir(dirptr, current_pos+1); + DEBUG(7,("call_trans2findnext: got match at pos %d\n", current_pos+1 )); + break; + } + } /* end for */ + } /* end if current_pos */ + } /* end if requires_resume_key && !continue_bit */ for (i=0;(i<(int)maxentries) && !finished && !out_of_space ;i++) { @@ -825,7 +1010,7 @@ static int call_trans2findnext(char *inbuf, char *outbuf, int length, int bufsiz else { finished = - !get_lanman2_dir_entry(cnum,mask,dirtype,info_level, + !get_lanman2_dir_entry(conn,mask,dirtype,info_level, requires_resume_key,dont_descend, &p,pdata,space_remaining, &out_of_space, &last_name_off); @@ -857,12 +1042,11 @@ static int call_trans2findnext(char *inbuf, char *outbuf, int length, int bufsiz send_trans2_replies( outbuf, bufsize, params, 8, pdata, PTR_DIFF(p,pdata)); if ((! *directory) && dptr_path(dptr_num)) - sprintf(directory,"(%s)",dptr_path(dptr_num)); + slprintf(directory,sizeof(directory)-1, "(%s)",dptr_path(dptr_num)); - DEBUG(3,("%s %s mask=%s directory=%s cnum=%d dirtype=%d numentries=%d\n", - timestring(), - smb_fn_name(CVAL(inbuf,smb_com)), - mask,directory,cnum,dirtype,numentries)); + DEBUG( 3, ( "%s mask=%s directory=%s dirtype=%d numentries=%d\n", + smb_fn_name(CVAL(inbuf,smb_com)), + mask, directory, dirtype, numentries ) ); return(-1); } @@ -870,19 +1054,25 @@ static int call_trans2findnext(char *inbuf, char *outbuf, int length, int bufsiz /**************************************************************************** reply to a TRANS2_QFSINFO (query filesystem info) ****************************************************************************/ -static int call_trans2qfsinfo(char *inbuf, char *outbuf, int length, int bufsize, - int cnum, char **pparams, char **ppdata) + +static int call_trans2qfsinfo(connection_struct *conn, + char *inbuf, char *outbuf, + int length, int bufsize, + char **pparams, char **ppdata) { char *pdata = *ppdata; char *params = *pparams; uint16 info_level = SVAL(params,0); int data_len; - struct stat st; - char *vname = volume_label(SNUM(cnum)); - - DEBUG(3,("call_trans2qfsinfo: cnum = %d, level = %d\n", cnum, info_level)); + SMB_STRUCT_STAT st; + char *vname = volume_label(SNUM(conn)); + int snum = SNUM(conn); + char *fstype = lp_fstype(SNUM(conn)); + extern uint32 global_client_caps; + + DEBUG(3,("call_trans2qfsinfo: level = %d\n", info_level)); - if(sys_stat(".",&st)!=0) { + if(dos_stat(".",&st)!=0) { DEBUG(2,("call_trans2qfsinfo: stat of . failed (%s)\n", strerror(errno))); return (ERROR(ERRSRV,ERRinvdevice)); } @@ -890,61 +1080,91 @@ static int call_trans2qfsinfo(char *inbuf, char *outbuf, int length, int bufsize pdata = *ppdata = Realloc(*ppdata, 1024); bzero(pdata,1024); switch (info_level) - { + { case 1: - { - int dfree,dsize,bsize; - data_len = 18; - sys_disk_free(".",&bsize,&dfree,&dsize); - SIVAL(pdata,l1_idFileSystem,st.st_dev); - SIVAL(pdata,l1_cSectorUnit,bsize/512); - SIVAL(pdata,l1_cUnit,dsize); - SIVAL(pdata,l1_cUnitAvail,dfree); - SSVAL(pdata,l1_cbSector,512); - DEBUG(5,("call_trans2qfsinfo : bsize=%d, id=%x, cSectorUnit=%d, cUnit=%d, cUnitAvail=%d, cbSector=%d\n", - bsize, st.st_dev, bsize/512, dsize, dfree, 512)); - break; + { + SMB_BIG_UINT dfree,dsize,bsize; + data_len = 18; + sys_disk_free(".",&bsize,&dfree,&dsize); + SIVAL(pdata,l1_idFileSystem,st.st_dev); + SIVAL(pdata,l1_cSectorUnit,bsize/512); + SIVAL(pdata,l1_cUnit,dsize); + SIVAL(pdata,l1_cUnitAvail,dfree); + SSVAL(pdata,l1_cbSector,512); + DEBUG(5,("call_trans2qfsinfo : bsize=%u, id=%x, cSectorUnit=%u, cUnit=%u, cUnitAvail=%u, cbSector=%d\n", + (unsigned int)bsize, (unsigned int)st.st_dev, ((unsigned int)bsize)/512, (unsigned int)dsize, + (unsigned int)dfree, 512)); + break; } case 2: { /* Return volume name */ int volname_len = MIN(strlen(vname),11); data_len = l2_vol_szVolLabel + volname_len + 1; - put_dos_date2(pdata,l2_vol_fdateCreation,st.st_ctime); + /* + * Add volume serial number - hash of a combination of + * the called hostname and the service name. + */ + SIVAL(pdata,0,str_checksum(lp_servicename(snum)) ^ (str_checksum(local_machine)<<16) ); SCVAL(pdata,l2_vol_cch,volname_len); StrnCpy(pdata+l2_vol_szVolLabel,vname,volname_len); - DEBUG(5,("call_trans2qfsinfo : time = %x, namelen = %d, name = %s\n",st.st_ctime,volname_len, + DEBUG(5,("call_trans2qfsinfo : time = %x, namelen = %d, name = %s\n", + (unsigned)st.st_ctime, volname_len, pdata+l2_vol_szVolLabel)); break; } case SMB_QUERY_FS_ATTRIBUTE_INFO: - data_len = 12 + 2*strlen(FSTYPE_STRING); + data_len = 12 + 2*strlen(fstype); + SIVAL(pdata,0,FILE_CASE_PRESERVED_NAMES); /* FS ATTRIBUTES */ +#if 0 /* Old code. JRA. */ SIVAL(pdata,0,0x4006); /* FS ATTRIBUTES == long filenames supported? */ +#endif /* Old code. */ SIVAL(pdata,4,128); /* Max filename component length */ - SIVAL(pdata,8,2*strlen(FSTYPE_STRING)); - PutUniCode(pdata+12,FSTYPE_STRING); + SIVAL(pdata,8,2*strlen(fstype)); + PutUniCode(pdata+12,fstype); + SSVAL(outbuf,smb_flg2,SVAL(outbuf,smb_flg2)|FLAGS2_UNICODE_STRINGS); break; case SMB_QUERY_FS_LABEL_INFO: data_len = 4 + strlen(vname); SIVAL(pdata,0,strlen(vname)); - strcpy(pdata+4,vname); + pstrcpy(pdata+4,vname); break; case SMB_QUERY_FS_VOLUME_INFO: - data_len = 17 + strlen(vname); - SIVAL(pdata,12,strlen(vname)); - strcpy(pdata+17,vname); + + /* + * Add volume serial number - hash of a combination of + * the called hostname and the service name. + */ + SIVAL(pdata,8,str_checksum(lp_servicename(snum)) ^ + (str_checksum(local_machine)<<16)); + + /* NT4 always serves this up as unicode but expects it to be + * delivered as ascii! (tridge && JRA) + */ + if (global_client_caps & CAP_NT_SMBS) { + data_len = 18 + strlen(vname); + SIVAL(pdata,12,strlen(vname)); + pstrcpy(pdata+18,vname); + } else { + data_len = 18 + 2*strlen(vname); + SIVAL(pdata,12,strlen(vname)*2); + PutUniCode(pdata+18,vname); + } + + DEBUG(5,("call_trans2qfsinfo : SMB_QUERY_FS_VOLUME_INFO namelen = %d, vol = %s\n", + strlen(vname),vname)); break; case SMB_QUERY_FS_SIZE_INFO: - { - int dfree,dsize,bsize; - data_len = 24; - sys_disk_free(".",&bsize,&dfree,&dsize); - SIVAL(pdata,0,dsize); - SIVAL(pdata,8,dfree); - SIVAL(pdata,16,bsize/512); - SIVAL(pdata,20,512); - } + { + SMB_BIG_UINT dfree,dsize,bsize; + data_len = 24; + sys_disk_free(".",&bsize,&dfree,&dsize); + SIVAL(pdata,0,dsize); + SIVAL(pdata,8,dfree); + SIVAL(pdata,16,bsize/512); + SIVAL(pdata,20,512); break; + } case SMB_QUERY_FS_DEVICE_INFO: data_len = 8; SIVAL(pdata,0,0); /* dev type */ @@ -952,12 +1172,13 @@ static int call_trans2qfsinfo(char *inbuf, char *outbuf, int length, int bufsize break; default: return(ERROR(ERRDOS,ERRunknownlevel)); - } + } send_trans2_replies( outbuf, bufsize, params, 0, pdata, data_len); - DEBUG(4,("%s %s info_level =%d\n",timestring(),smb_fn_name(CVAL(inbuf,smb_com)), info_level)); + DEBUG( 4, ( "%s info_level = %d\n", + smb_fn_name(CVAL(inbuf,smb_com)), info_level) ); return -1; } @@ -965,15 +1186,17 @@ static int call_trans2qfsinfo(char *inbuf, char *outbuf, int length, int bufsize /**************************************************************************** reply to a TRANS2_SETFSINFO (set filesystem info) ****************************************************************************/ -static int call_trans2setfsinfo(char *inbuf, char *outbuf, int length, int bufsize, - int cnum, char **pparams, char **ppdata) +static int call_trans2setfsinfo(connection_struct *conn, + char *inbuf, char *outbuf, int length, + int bufsize, + char **pparams, char **ppdata) { /* Just say yes we did it - there is nothing that can be set here so it doesn't matter. */ int outsize; DEBUG(3,("call_trans2setfsinfo\n")); - if (!CAN_WRITE(cnum)) + if (!CAN_WRITE(conn)) return(ERROR(ERRSRV,ERRaccess)); outsize = set_message(outbuf,10,0,True); @@ -982,10 +1205,13 @@ static int call_trans2setfsinfo(char *inbuf, char *outbuf, int length, int bufsi } /**************************************************************************** - reply to a TRANS2_QFILEINFO (query file info by fileid) + Reply to a TRANS2_QFILEPATHINFO or TRANSACT2_QFILEINFO (query file info by + file name or file id). ****************************************************************************/ -static int call_trans2qfilepathinfo(char *inbuf, char *outbuf, int length, - int bufsize,int cnum, + +static int call_trans2qfilepathinfo(connection_struct *conn, + char *inbuf, char *outbuf, int length, + int bufsize, char **pparams,char **ppdata, int total_data) { @@ -994,36 +1220,42 @@ static int call_trans2qfilepathinfo(char *inbuf, char *outbuf, int length, uint16 tran_call = SVAL(inbuf, smb_setup0); uint16 info_level; int mode=0; - int size=0; + SMB_OFF_T size=0; unsigned int data_size; - struct stat sbuf; + SMB_STRUCT_STAT sbuf; pstring fname1; char *fname; char *p; - int l,pos; - + int l; + SMB_OFF_T pos; + BOOL bad_path = False; if (tran_call == TRANSACT2_QFILEINFO) { - int16 fnum = SVAL(params,0); + files_struct *fsp = file_fsp(params,0); info_level = SVAL(params,2); - CHECK_FNUM(fnum,cnum); - CHECK_ERROR(fnum); + CHECK_FSP(fsp,conn); + CHECK_ERROR(fsp); - fname = Files[fnum].name; - if (fstat(Files[fnum].fd,&sbuf) != 0) { - DEBUG(3,("fstat of fnum %d failed (%s)\n",fnum, strerror(errno))); + fname = fsp->fsp_name; + if (sys_fstat(fsp->fd_ptr->fd,&sbuf) != 0) { + DEBUG(3,("fstat of fnum %d failed (%s)\n",fsp->fnum, strerror(errno))); return(UNIXERROR(ERRDOS,ERRbadfid)); } - pos = lseek(Files[fnum].fd,0,SEEK_CUR); + pos = sys_lseek(fsp->fd_ptr->fd,0,SEEK_CUR); } else { /* qpathinfo */ info_level = SVAL(params,0); fname = &fname1[0]; - strcpy(fname,¶ms[6]); - unix_convert(fname,cnum); - if (!check_name(fname,cnum) || sys_stat(fname,&sbuf)) { + pstrcpy(fname,¶ms[6]); + unix_convert(fname,conn,0,&bad_path,&sbuf); + if (!check_name(fname,conn) || (!VALID_STAT(sbuf) && dos_stat(fname,&sbuf))) { DEBUG(3,("fileinfo of %s failed (%s)\n",fname,strerror(errno))); + if((errno == ENOENT) && bad_path) + { + unix_ERR_class = ERRDOS; + unix_ERR_code = ERRbadpath; + } return(UNIXERROR(ERRDOS,ERRbadpath)); } pos = 0; @@ -1039,9 +1271,12 @@ static int call_trans2qfilepathinfo(char *inbuf, char *outbuf, int length, else p++; l = strlen(p); - mode = dos_mode(cnum,fname,&sbuf); + mode = dos_mode(conn,fname,&sbuf); size = sbuf.st_size; if (mode & aDIR) size = 0; + + /* from now on we only want the part after the / */ + fname = p; params = *pparams = Realloc(*pparams,2); bzero(params,2); data_size = 1024; @@ -1050,42 +1285,36 @@ static int call_trans2qfilepathinfo(char *inbuf, char *outbuf, int length, if (total_data > 0 && IVAL(pdata,0) == total_data) { /* uggh, EAs for OS2 */ DEBUG(4,("Rejecting EA request with total_data=%d\n",total_data)); -#if 0 - SSVAL(params,0,ERROR_EAS_NOT_SUPPORTED); - send_trans2_replies(outbuf, bufsize, params, 2, *ppdata, 0); - return(-1); -#else return(ERROR(ERRDOS,ERROR_EAS_NOT_SUPPORTED)); -#endif } bzero(pdata,data_size); switch (info_level) { - case 1: - case 2: + case SMB_INFO_STANDARD: + case SMB_INFO_QUERY_EA_SIZE: data_size = (info_level==1?22:26); - put_dos_date2(pdata,l1_fdateCreation,sbuf.st_ctime); + put_dos_date2(pdata,l1_fdateCreation,get_create_time(&sbuf,lp_fake_dir_create_times(SNUM(conn)))); put_dos_date2(pdata,l1_fdateLastAccess,sbuf.st_atime); - put_dos_date2(pdata,l1_fdateLastWrite,sbuf.st_mtime); - SIVAL(pdata,l1_cbFile,size); + put_dos_date2(pdata,l1_fdateLastWrite,sbuf.st_mtime); /* write time */ + SIVAL(pdata,l1_cbFile,(uint32)size); SIVAL(pdata,l1_cbFileAlloc,ROUNDUP(size,1024)); SSVAL(pdata,l1_attrFile,mode); SIVAL(pdata,l1_attrFile+2,4); /* this is what OS2 does */ break; - case 3: + case SMB_INFO_QUERY_EAS_FROM_LIST: data_size = 24; - put_dos_date2(pdata,0,sbuf.st_ctime); + put_dos_date2(pdata,0,get_create_time(&sbuf,lp_fake_dir_create_times(SNUM(conn)))); put_dos_date2(pdata,4,sbuf.st_atime); put_dos_date2(pdata,8,sbuf.st_mtime); - SIVAL(pdata,12,size); + SIVAL(pdata,12,(uint32)size); SIVAL(pdata,16,ROUNDUP(size,1024)); SIVAL(pdata,20,mode); break; - case 4: + case SMB_INFO_QUERY_ALL_EAS: data_size = 4; SIVAL(pdata,0,data_size); break; @@ -1094,18 +1323,29 @@ static int call_trans2qfilepathinfo(char *inbuf, char *outbuf, int length, return(ERROR(ERRDOS,ERRbadfunc)); /* os/2 needs this */ case SMB_QUERY_FILE_BASIC_INFO: - data_size = 36; - put_long_date(pdata,sbuf.st_ctime); + data_size = 36; /* w95 returns 40 bytes not 36 - why ?. */ + put_long_date(pdata,get_create_time(&sbuf,lp_fake_dir_create_times(SNUM(conn)))); put_long_date(pdata+8,sbuf.st_atime); - put_long_date(pdata+16,sbuf.st_mtime); - put_long_date(pdata+24,sbuf.st_mtime); + put_long_date(pdata+16,sbuf.st_mtime); /* write time */ + put_long_date(pdata+24,sbuf.st_mtime); /* change time */ SIVAL(pdata,32,mode); + + DEBUG(5,("SMB_QFBI - ")); + { + time_t create_time = get_create_time(&sbuf,lp_fake_dir_create_times(SNUM(conn))); + DEBUG(5,("create: %s ", ctime(&create_time))); + } + DEBUG(5,("access: %s ", ctime(&sbuf.st_atime))); + DEBUG(5,("write: %s ", ctime(&sbuf.st_mtime))); + DEBUG(5,("change: %s ", ctime(&sbuf.st_mtime))); + DEBUG(5,("mode: %x\n", mode)); + break; case SMB_QUERY_FILE_STANDARD_INFO: data_size = 22; - SIVAL(pdata,0,size); - SIVAL(pdata,8,size); + SOFF_T(pdata,0,size); + SOFF_T(pdata,8,size); SIVAL(pdata,16,sbuf.st_nlink); CVAL(pdata,20) = 0; CVAL(pdata,21) = (mode&aDIR)?1:0; @@ -1115,27 +1355,46 @@ static int call_trans2qfilepathinfo(char *inbuf, char *outbuf, int length, data_size = 4; break; - case SMB_QUERY_FILE_NAME_INFO: + /* Get the 8.3 name - used if NT SMB was negotiated. */ case SMB_QUERY_FILE_ALT_NAME_INFO: + { + pstring short_name; + pstrcpy(short_name,p); + /* Mangle if not already 8.3 */ + if(!is_8_3(short_name, True)) + { + if(!name_map_mangle(short_name,True,SNUM(conn))) + *short_name = '\0'; + } + strupper(short_name); + l = strlen(short_name); + PutUniCode(pdata + 4, short_name); + data_size = 4 + (2*l); + SIVAL(pdata,0,2*l); + } + break; + + case SMB_QUERY_FILE_NAME_INFO: data_size = 4 + l; SIVAL(pdata,0,l); - strcpy(pdata+4,fname); + pstrcpy(pdata+4,fname); break; + case SMB_QUERY_FILE_ALLOCATION_INFO: case SMB_QUERY_FILE_END_OF_FILEINFO: data_size = 8; - SIVAL(pdata,0,size); + SOFF_T(pdata,0,size); break; case SMB_QUERY_FILE_ALL_INFO: - put_long_date(pdata,sbuf.st_ctime); + put_long_date(pdata,get_create_time(&sbuf,lp_fake_dir_create_times(SNUM(conn)))); put_long_date(pdata+8,sbuf.st_atime); - put_long_date(pdata+16,sbuf.st_mtime); - put_long_date(pdata+24,sbuf.st_mtime); + put_long_date(pdata+16,sbuf.st_mtime); /* write time */ + put_long_date(pdata+24,sbuf.st_mtime); /* change time */ SIVAL(pdata,32,mode); pdata += 40; - SIVAL(pdata,0,size); - SIVAL(pdata,8,size); + SOFF_T(pdata,0,size); + SOFF_T(pdata,8,size); SIVAL(pdata,16,sbuf.st_nlink); CVAL(pdata,20) = 0; CVAL(pdata,21) = (mode&aDIR)?1:0; @@ -1143,9 +1402,9 @@ static int call_trans2qfilepathinfo(char *inbuf, char *outbuf, int length, pdata += 8; /* index number */ pdata += 4; /* EA info */ if (mode & aRONLY) - SIVAL(pdata,0,0xA9); + SIVAL(pdata,0,0xA9); else - SIVAL(pdata,0,0xd01BF); + SIVAL(pdata,0,0xd01BF); pdata += 4; SIVAL(pdata,0,pos); /* current offset */ pdata += 8; @@ -1153,19 +1412,24 @@ static int call_trans2qfilepathinfo(char *inbuf, char *outbuf, int length, pdata += 4; pdata += 4; /* alignment */ SIVAL(pdata,0,l); - strcpy(pdata+4,fname); + pstrcpy(pdata+4,fname); pdata += 4 + l; data_size = PTR_DIFF(pdata,(*ppdata)); break; +#if 0 + /* NT4 server just returns "invalid query" to this - if we try to answer + it then NTws gets a BSOD! (tridge) */ case SMB_QUERY_FILE_STREAM_INFO: data_size = 24 + l; SIVAL(pdata,0,pos); SIVAL(pdata,4,size); SIVAL(pdata,12,size); SIVAL(pdata,20,l); - strcpy(pdata+24,fname); + pstrcpy(pdata+24,fname); break; +#endif + default: return(ERROR(ERRDOS,ERRunknownlevel)); } @@ -1178,8 +1442,9 @@ static int call_trans2qfilepathinfo(char *inbuf, char *outbuf, int length, /**************************************************************************** reply to a TRANS2_SETFILEINFO (set file info by fileid) ****************************************************************************/ -static int call_trans2setfilepathinfo(char *inbuf, char *outbuf, int length, - int bufsize, int cnum, char **pparams, +static int call_trans2setfilepathinfo(connection_struct *conn, + char *inbuf, char *outbuf, int length, + int bufsize, char **pparams, char **ppdata, int total_data) { char *params = *pparams; @@ -1187,42 +1452,55 @@ static int call_trans2setfilepathinfo(char *inbuf, char *outbuf, int length, uint16 tran_call = SVAL(inbuf, smb_setup0); uint16 info_level; int mode=0; - int size=0; + SMB_OFF_T size=0; struct utimbuf tvs; - struct stat st; + SMB_STRUCT_STAT st; pstring fname1; char *fname; int fd = -1; + BOOL bad_path = False; - if (!CAN_WRITE(cnum)) + if (!CAN_WRITE(conn)) return(ERROR(ERRSRV,ERRaccess)); if (tran_call == TRANSACT2_SETFILEINFO) { - int16 fnum = SVAL(params,0); + files_struct *fsp = file_fsp(params,0); info_level = SVAL(params,2); - CHECK_FNUM(fnum,cnum); - CHECK_ERROR(fnum); + CHECK_FSP(fsp,conn); + CHECK_ERROR(fsp); - fname = Files[fnum].name; - fd = Files[fnum].fd; + fname = fsp->fsp_name; + fd = fsp->fd_ptr->fd; - if(fstat(fd,&st)!=0) { + if(sys_fstat(fd,&st)!=0) { DEBUG(3,("fstat of %s failed (%s)\n", fname, strerror(errno))); - return(ERROR(ERRDOS,ERRbadpath)); + return(UNIXERROR(ERRDOS,ERRbadpath)); } } else { /* set path info */ info_level = SVAL(params,0); fname = fname1; - strcpy(fname,¶ms[6]); - unix_convert(fname,cnum); - if(!check_name(fname, cnum)) - return(ERROR(ERRDOS,ERRbadpath)); - - if(sys_stat(fname,&st)!=0) { + pstrcpy(fname,¶ms[6]); + unix_convert(fname,conn,0,&bad_path,&st); + if(!check_name(fname, conn)) + { + if((errno == ENOENT) && bad_path) + { + unix_ERR_class = ERRDOS; + unix_ERR_code = ERRbadpath; + } + return(UNIXERROR(ERRDOS,ERRbadpath)); + } + + if(!VALID_STAT(st) && dos_stat(fname,&st)!=0) { DEBUG(3,("stat of %s failed (%s)\n", fname, strerror(errno))); - return(ERROR(ERRDOS,ERRbadpath)); + if((errno == ENOENT) && bad_path) + { + unix_ERR_class = ERRDOS; + unix_ERR_code = ERRbadpath; + } + return(UNIXERROR(ERRDOS,ERRbadpath)); } } @@ -1237,42 +1515,42 @@ static int call_trans2setfilepathinfo(char *inbuf, char *outbuf, int length, size = st.st_size; tvs.modtime = st.st_mtime; tvs.actime = st.st_atime; - mode = dos_mode(cnum,fname,&st); + mode = dos_mode(conn,fname,&st); if (total_data > 0 && IVAL(pdata,0) == total_data) { /* uggh, EAs for OS2 */ DEBUG(4,("Rejecting EA request with total_data=%d\n",total_data)); - SSVAL(params,0,ERROR_EAS_NOT_SUPPORTED); - - send_trans2_replies(outbuf, bufsize, params, 2, *ppdata, 0); - - return(-1); + return(ERROR(ERRDOS,ERROR_EAS_NOT_SUPPORTED)); } switch (info_level) + { + case SMB_INFO_STANDARD: + case SMB_INFO_QUERY_EA_SIZE: { - case 1: + /* access time */ tvs.actime = make_unix_date2(pdata+l1_fdateLastAccess); - tvs.modtime = make_unix_date2(pdata+l1_fdateLastWrite); - mode = SVAL(pdata,l1_attrFile); - size = IVAL(pdata,l1_cbFile); - break; - case 2: - tvs.actime = make_unix_date2(pdata+l1_fdateLastAccess); + /* write time */ tvs.modtime = make_unix_date2(pdata+l1_fdateLastWrite); + mode = SVAL(pdata,l1_attrFile); size = IVAL(pdata,l1_cbFile); break; + } - case 3: + /* XXXX um, i don't think this is right. + it's also not in the cifs6.txt spec. + */ + case SMB_INFO_QUERY_EAS_FROM_LIST: tvs.actime = make_unix_date2(pdata+8); tvs.modtime = make_unix_date2(pdata+12); size = IVAL(pdata,16); mode = IVAL(pdata,24); break; - case 4: + /* XXXX nor this. not in cifs6.txt, either. */ + case SMB_INFO_QUERY_ALL_EAS: tvs.actime = make_unix_date2(pdata+8); tvs.modtime = make_unix_date2(pdata+12); size = IVAL(pdata,16); @@ -1280,48 +1558,112 @@ static int call_trans2setfilepathinfo(char *inbuf, char *outbuf, int length, break; case SMB_SET_FILE_BASIC_INFO: - pdata += 8; /* create time */ - tvs.actime = interpret_long_date(pdata); pdata += 8; - tvs.modtime=MAX(interpret_long_date(pdata),interpret_long_date(pdata+8)); - pdata += 16; - mode = IVAL(pdata,0); + { + /* Ignore create time at offset pdata. */ + + /* access time */ + tvs.actime = interpret_long_date(pdata+8); + + /* write time + changed time, combined. */ + tvs.modtime=MAX(interpret_long_date(pdata+16), + interpret_long_date(pdata+24)); + +#if 0 /* Needs more testing... */ + /* Test from Luke to prevent Win95 from + setting incorrect values here. + */ + if (tvs.actime < tvs.modtime) + return(ERROR(ERRDOS,ERRnoaccess)); +#endif /* Needs more testing... */ + + /* attributes */ + mode = IVAL(pdata,32); break; + } case SMB_SET_FILE_END_OF_FILE_INFO: - if (IVAL(pdata,4) != 0) /* more than 32 bits? */ - return(ERROR(ERRDOS,ERRunknownlevel)); + { size = IVAL(pdata,0); +#ifdef LARGE_SMB_OFF_T + size |= (((SMB_OFF_T)IVAL(pdata,4)) << 32); +#else /* LARGE_SMB_OFF_T */ + if (IVAL(pdata,4) != 0) /* more than 32 bits? */ + return(ERROR(ERRDOS,ERRunknownlevel)); +#endif /* LARGE_SMB_OFF_T */ break; + } + + case SMB_SET_FILE_ALLOCATION_INFO: + break; /* We don't need to do anything for this call. */ + + case SMB_SET_FILE_DISPOSITION_INFO: /* Set delete on close for open file. */ + { + if (tran_call == TRANSACT2_SETFILEINFO) { + files_struct *fsp = file_fsp(params,0); + if(fsp->is_directory) + return(ERROR(ERRDOS,ERRnoaccess)); + /* + * TODO - check here is this means set + * this flag bit on all open files that + * reference this particular dev/inode pair. + * If so we'll need to search the open + * file entries here and set this flag on + * all of them that match. JRA. + */ + fsp->delete_on_close = CVAL(pdata,0); + } else + return(ERROR(ERRDOS,ERRunknownlevel)); + break; + } - case SMB_SET_FILE_DISPOSITION_INFO: /* not supported yet */ - case SMB_SET_FILE_ALLOCATION_INFO: /* not supported yet */ default: + { return(ERROR(ERRDOS,ERRunknownlevel)); } + } + DEBUG(6,("actime: %s " , ctime(&tvs.actime))); + DEBUG(6,("modtime: %s ", ctime(&tvs.modtime))); + DEBUG(6,("size: %.0f ", (double)size)); + DEBUG(6,("mode: %x\n" , mode)); + /* get some defaults (no modifications) if any info is zero. */ if (!tvs.actime) tvs.actime = st.st_atime; if (!tvs.modtime) tvs.modtime = st.st_mtime; if (!size) size = st.st_size; - /* Try and set the times, size and mode of this file - if they are different - from the current values */ - if(st.st_mtime != tvs.modtime || st.st_atime != tvs.actime) { - if(sys_utime(fname, &tvs)!=0) - return(ERROR(ERRDOS,ERRnoaccess)); + /* Try and set the times, size and mode of this file - + if they are different from the current values + */ + if (st.st_mtime != tvs.modtime || st.st_atime != tvs.actime) + { + if(file_utime(conn, fname, &tvs)!=0) + { + return(UNIXERROR(ERRDOS,ERRnoaccess)); + } } - if(mode != dos_mode(cnum,fname,&st) && dos_chmod(cnum,fname,mode,NULL)) { + + /* check the mode isn't different, before changing it */ + if (mode != dos_mode(conn, fname, &st) && file_chmod(conn, fname, mode, NULL)) + { DEBUG(2,("chmod of %s failed (%s)\n", fname, strerror(errno))); - return(ERROR(ERRDOS,ERRnoaccess)); + return(UNIXERROR(ERRDOS,ERRnoaccess)); } - if(size != st.st_size) { - if (fd == -1) { - fd = sys_open(fname,O_RDWR,0); + + if(size != st.st_size) + { + if (fd == -1) + { + fd = dos_open(fname,O_RDWR,0); if (fd == -1) - return(ERROR(ERRDOS,ERRbadpath)); + { + return(UNIXERROR(ERRDOS,ERRbadpath)); + } set_filelen(fd, size); close(fd); - } else { + } + else + { set_filelen(fd, size); } } @@ -1336,27 +1678,34 @@ static int call_trans2setfilepathinfo(char *inbuf, char *outbuf, int length, /**************************************************************************** reply to a TRANS2_MKDIR (make directory with extended attributes). ****************************************************************************/ -static int call_trans2mkdir(char *inbuf, char *outbuf, int length, int bufsize, - int cnum, char **pparams, char **ppdata) +static int call_trans2mkdir(connection_struct *conn, + char *inbuf, char *outbuf, int length, int bufsize, + char **pparams, char **ppdata) { char *params = *pparams; pstring directory; int ret = -1; + BOOL bad_path = False; - if (!CAN_WRITE(cnum)) + if (!CAN_WRITE(conn)) return(ERROR(ERRSRV,ERRaccess)); - strcpy(directory, ¶ms[4]); + pstrcpy(directory, ¶ms[4]); DEBUG(3,("call_trans2mkdir : name = %s\n", directory)); - unix_convert(directory,cnum); - if (check_name(directory,cnum)) - ret = sys_mkdir(directory,unix_mode(cnum,aDIR)); + unix_convert(directory,conn,0,&bad_path,NULL); + if (check_name(directory,conn)) + ret = dos_mkdir(directory,unix_mode(conn,aDIR)); if(ret < 0) { DEBUG(5,("call_trans2mkdir error (%s)\n", strerror(errno))); + if((errno == ENOENT) && bad_path) + { + unix_ERR_class = ERRDOS; + unix_ERR_code = ERRbadpath; + } return(UNIXERROR(ERRDOS,ERRnoaccess)); } @@ -1376,8 +1725,10 @@ static int call_trans2mkdir(char *inbuf, char *outbuf, int length, int bufsize, reply to a TRANS2_FINDNOTIFYFIRST (start monitoring a directory for changes) We don't actually do this - we just send a null response. ****************************************************************************/ -static int call_trans2findnotifyfirst(char *inbuf, char *outbuf, int length, int bufsize, - int cnum, char **pparams, char **ppdata) +static int call_trans2findnotifyfirst(connection_struct *conn, + char *inbuf, char *outbuf, + int length, int bufsize, + char **pparams, char **ppdata) { static uint16 fnf_handle = 257; char *params = *pparams; @@ -1417,8 +1768,10 @@ static int call_trans2findnotifyfirst(char *inbuf, char *outbuf, int length, int reply to a TRANS2_FINDNOTIFYNEXT (continue monitoring a directory for changes). Currently this does nothing. ****************************************************************************/ -static int call_trans2findnotifynext(char *inbuf, char *outbuf, int length, int bufsize, - int cnum, char **pparams, char **ppdata) +static int call_trans2findnotifynext(connection_struct *conn, + char *inbuf, char *outbuf, + int length, int bufsize, + char **pparams, char **ppdata) { char *params = *pparams; @@ -1440,207 +1793,258 @@ static int call_trans2findnotifynext(char *inbuf, char *outbuf, int length, int /**************************************************************************** reply to a SMBfindclose (stop trans2 directory search) ****************************************************************************/ -int reply_findclose(char *inbuf,char *outbuf,int length,int bufsize) +int reply_findclose(connection_struct *conn, + char *inbuf,char *outbuf,int length,int bufsize) { - int cnum; - int outsize = 0; - uint16 dptr_num=SVAL(inbuf,smb_vwv0); - - cnum = SVAL(inbuf,smb_tid); + int outsize = 0; + int16 dptr_num=SVALS(inbuf,smb_vwv0); - DEBUG(3,("reply_findclose, cnum = %d, dptr_num = %d\n", cnum, dptr_num)); + DEBUG(3,("reply_findclose, dptr_num = %d\n", dptr_num)); - dptr_close(dptr_num); + dptr_close(dptr_num); - outsize = set_message(outbuf,0,0,True); + outsize = set_message(outbuf,0,0,True); - DEBUG(3,("%s SMBfindclose cnum=%d, dptr_num = %d\n",timestring(),cnum,dptr_num)); + DEBUG(3,("SMBfindclose dptr_num = %d\n", dptr_num)); - return(outsize); + return(outsize); } /**************************************************************************** reply to a SMBfindnclose (stop FINDNOTIFYFIRST directory search) ****************************************************************************/ -int reply_findnclose(char *inbuf,char *outbuf,int length,int bufsize) +int reply_findnclose(connection_struct *conn, + char *inbuf,char *outbuf,int length,int bufsize) { - int cnum; - int outsize = 0; - int dptr_num= -1; + int outsize = 0; + int dptr_num= -1; + + dptr_num = SVAL(inbuf,smb_vwv0); - cnum = SVAL(inbuf,smb_tid); - dptr_num = SVAL(inbuf,smb_vwv0); + DEBUG(3,("reply_findnclose, dptr_num = %d\n", dptr_num)); - DEBUG(3,("reply_findnclose, cnum = %d, dptr_num = %d\n", cnum, dptr_num)); + /* We never give out valid handles for a + findnotifyfirst - so any dptr_num is ok here. + Just ignore it. */ - /* We never give out valid handles for a - findnotifyfirst - so any dptr_num is ok here. - Just ignore it. */ + outsize = set_message(outbuf,0,0,True); - outsize = set_message(outbuf,0,0,True); + DEBUG(3,("SMB_findnclose dptr_num = %d\n", dptr_num)); - DEBUG(3,("%s SMB_findnclose cnum=%d, dptr_num = %d\n",timestring(),cnum,dptr_num)); - - return(outsize); + return(outsize); } /**************************************************************************** reply to a SMBtranss2 - just ignore it! ****************************************************************************/ -int reply_transs2(char *inbuf,char *outbuf,int length,int bufsize) +int reply_transs2(connection_struct *conn, + char *inbuf,char *outbuf,int length,int bufsize) { - DEBUG(4,("Ignoring transs2 of length %d\n",length)); - return(-1); + DEBUG(4,("Ignoring transs2 of length %d\n",length)); + return(-1); } /**************************************************************************** reply to a SMBtrans2 ****************************************************************************/ -int reply_trans2(char *inbuf,char *outbuf,int length,int bufsize) +int reply_trans2(connection_struct *conn, + char *inbuf,char *outbuf,int length,int bufsize) { - int outsize = 0; - int cnum = SVAL(inbuf,smb_tid); - unsigned int total_params = SVAL(inbuf, smb_tpscnt); - unsigned int total_data =SVAL(inbuf, smb_tdscnt); + int outsize = 0; + unsigned int total_params = SVAL(inbuf, smb_tpscnt); + unsigned int total_data =SVAL(inbuf, smb_tdscnt); #if 0 - unsigned int max_param_reply = SVAL(inbuf, smb_mprcnt); - unsigned int max_data_reply = SVAL(inbuf, smb_mdrcnt); - unsigned int max_setup_fields = SVAL(inbuf, smb_msrcnt); - BOOL close_tid = BITSETW(inbuf+smb_flags,0); - BOOL no_final_response = BITSETW(inbuf+smb_flags,1); - int32 timeout = IVALS(inbuf,smb_timeout); + unsigned int max_param_reply = SVAL(inbuf, smb_mprcnt); + unsigned int max_data_reply = SVAL(inbuf, smb_mdrcnt); + unsigned int max_setup_fields = SVAL(inbuf, smb_msrcnt); + BOOL close_tid = BITSETW(inbuf+smb_flags,0); + BOOL no_final_response = BITSETW(inbuf+smb_flags,1); + int32 timeout = IVALS(inbuf,smb_timeout); #endif - unsigned int suwcnt = SVAL(inbuf, smb_suwcnt); - unsigned int tran_call = SVAL(inbuf, smb_setup0); - char *params = NULL, *data = NULL; - int num_params, num_params_sofar, num_data, num_data_sofar; + unsigned int suwcnt = SVAL(inbuf, smb_suwcnt); + unsigned int tran_call = SVAL(inbuf, smb_setup0); + char *params = NULL, *data = NULL; + int num_params, num_params_sofar, num_data, num_data_sofar; - outsize = set_message(outbuf,0,0,True); + if(global_oplock_break && (tran_call == TRANSACT2_OPEN)) { + /* Queue this open message as we are the process of an + * oplock break. */ - /* All trans2 messages we handle have smb_sucnt == 1 - ensure this - is so as a sanity check */ - if(suwcnt != 1 ) - { - DEBUG(2,("Invalid smb_sucnt in trans2 call\n")); - return(ERROR(ERRSRV,ERRerror)); - } + DEBUG(2,("reply_trans2: queueing message trans2open due to being ")); + DEBUGADD(2,( "in oplock break state.\n")); + + push_oplock_pending_smb_message(inbuf, length); + return -1; + } + + outsize = set_message(outbuf,0,0,True); + + /* All trans2 messages we handle have smb_sucnt == 1 - ensure this + is so as a sanity check */ + if (suwcnt != 1) { + DEBUG(2,("Invalid smb_sucnt in trans2 call\n")); + return(ERROR(ERRSRV,ERRerror)); + } - /* Allocate the space for the maximum needed parameters and data */ - if (total_params > 0) - params = (char *)malloc(total_params); - if (total_data > 0) - data = (char *)malloc(total_data); + /* Allocate the space for the maximum needed parameters and data */ + if (total_params > 0) + params = (char *)malloc(total_params); + if (total_data > 0) + data = (char *)malloc(total_data); - if ((total_params && !params) || (total_data && !data)) - { - DEBUG(2,("Out of memory in reply_trans2\n")); - return(ERROR(ERRDOS,ERRnomem)); - } - - /* Copy the param and data bytes sent with this request into - the params buffer */ - num_params = num_params_sofar = SVAL(inbuf,smb_pscnt); - num_data = num_data_sofar = SVAL(inbuf, smb_dscnt); - - memcpy( params, smb_base(inbuf) + SVAL(inbuf, smb_psoff), num_params); - memcpy( data, smb_base(inbuf) + SVAL(inbuf, smb_dsoff), num_data); - - if(num_data_sofar < total_data || num_params_sofar < total_params) - { - /* We need to send an interim response then receive the rest - of the parameter/data bytes */ - outsize = set_message(outbuf,0,0,True); - send_smb(Client,outbuf); + if ((total_params && !params) || (total_data && !data)) { + DEBUG(2,("Out of memory in reply_trans2\n")); + if(params) + free(params); + if(data) + free(data); + return(ERROR(ERRDOS,ERRnomem)); + } - while( num_data_sofar < total_data || num_params_sofar < total_params) - { - receive_smb(Client,inbuf, 0); - - /* Ensure this is still a trans2 packet (sanity check) */ - if(CVAL(inbuf, smb_com) != SMBtranss2) - { - outsize = set_message(outbuf,0,0,True); - DEBUG(2,("Invalid secondary trans2 packet\n")); - free(params); - free(data); - return(ERROR(ERRSRV,ERRerror)); - } + /* Copy the param and data bytes sent with this request into + the params buffer */ + num_params = num_params_sofar = SVAL(inbuf,smb_pscnt); + num_data = num_data_sofar = SVAL(inbuf, smb_dscnt); + + if (num_params > total_params || num_data > total_data) + exit_server("invalid params in reply_trans2"); + + if(params) + memcpy( params, smb_base(inbuf) + SVAL(inbuf, smb_psoff), num_params); + if(data) + memcpy( data, smb_base(inbuf) + SVAL(inbuf, smb_dsoff), num_data); + + if(num_data_sofar < total_data || num_params_sofar < total_params) { + /* We need to send an interim response then receive the rest + of the parameter/data bytes */ + outsize = set_message(outbuf,0,0,True); + send_smb(Client,outbuf); + + while (num_data_sofar < total_data || + num_params_sofar < total_params) { + BOOL ret; + + ret = receive_next_smb(inbuf,bufsize,SMB_SECONDARY_WAIT); + + if ((ret && + (CVAL(inbuf, smb_com) != SMBtranss2)) || !ret) { + outsize = set_message(outbuf,0,0,True); + if(ret) + DEBUG(0,("reply_trans2: Invalid secondary trans2 packet\n")); + else + DEBUG(0,("reply_trans2: %s in getting secondary trans2 response.\n", + (smb_read_error == READ_ERROR) ? "error" : "timeout" )); + if(params) + free(params); + if(data) + free(data); + return(ERROR(ERRSRV,ERRerror)); + } - /* Revise total_params and total_data in case they have changed downwards */ - total_params = SVAL(inbuf, smb_tpscnt); - total_data = SVAL(inbuf, smb_tdscnt); - num_params_sofar += (num_params = SVAL(inbuf,smb_spscnt)); - num_data_sofar += ( num_data = SVAL(inbuf, smb_sdscnt)); - memcpy( ¶ms[ SVAL(inbuf, smb_spsdisp)], - smb_base(inbuf) + SVAL(inbuf, smb_spsoff), num_params); - memcpy( &data[SVAL(inbuf, smb_sdsdisp)], - smb_base(inbuf)+ SVAL(inbuf, smb_sdsoff), num_data); + /* Revise total_params and total_data in case + they have changed downwards */ + total_params = SVAL(inbuf, smb_tpscnt); + total_data = SVAL(inbuf, smb_tdscnt); + num_params_sofar += (num_params = SVAL(inbuf,smb_spscnt)); + num_data_sofar += ( num_data = SVAL(inbuf, smb_sdscnt)); + if (num_params_sofar > total_params || num_data_sofar > total_data) + exit_server("data overflow in trans2"); + + memcpy( ¶ms[ SVAL(inbuf, smb_spsdisp)], + smb_base(inbuf) + SVAL(inbuf, smb_spsoff), num_params); + memcpy( &data[SVAL(inbuf, smb_sdsdisp)], + smb_base(inbuf)+ SVAL(inbuf, smb_sdsoff), num_data); + } + } + + if (Protocol >= PROTOCOL_NT1) { + uint16 flg2 = SVAL(outbuf,smb_flg2); + SSVAL(outbuf,smb_flg2,flg2 | 0x40); /* IS_LONG_NAME */ } - } - - if (Protocol >= PROTOCOL_NT1) { - uint16 flg2 = SVAL(outbuf,smb_flg2); - SSVAL(outbuf,smb_flg2,flg2 | 0x40); /* IS_LONG_NAME */ - } - - /* Now we must call the relevant TRANS2 function */ - switch(tran_call) - { - case TRANSACT2_OPEN: - outsize = call_trans2open(inbuf, outbuf, bufsize, cnum, ¶ms, &data); - break; - case TRANSACT2_FINDFIRST: - outsize = call_trans2findfirst(inbuf, outbuf, bufsize, cnum, ¶ms, &data); - break; - case TRANSACT2_FINDNEXT: - outsize = call_trans2findnext(inbuf, outbuf, length, bufsize, cnum, ¶ms, &data); - break; - case TRANSACT2_QFSINFO: - outsize = call_trans2qfsinfo(inbuf, outbuf, length, bufsize, cnum, ¶ms, &data); - break; - case TRANSACT2_SETFSINFO: - outsize = call_trans2setfsinfo(inbuf, outbuf, length, bufsize, cnum, ¶ms, &data); - break; - case TRANSACT2_QPATHINFO: - case TRANSACT2_QFILEINFO: - outsize = call_trans2qfilepathinfo(inbuf, outbuf, length, bufsize, cnum, ¶ms, &data, total_data); - break; - case TRANSACT2_SETPATHINFO: - case TRANSACT2_SETFILEINFO: - outsize = call_trans2setfilepathinfo(inbuf, outbuf, length, bufsize, cnum, ¶ms, &data, total_data); - break; - case TRANSACT2_FINDNOTIFYFIRST: - outsize = call_trans2findnotifyfirst(inbuf, outbuf, length, bufsize, cnum, ¶ms, &data); - break; - case TRANSACT2_FINDNOTIFYNEXT: - outsize = call_trans2findnotifynext(inbuf, outbuf, length, bufsize, cnum, ¶ms, &data); - break; - case TRANSACT2_MKDIR: - outsize = call_trans2mkdir(inbuf, outbuf, length, bufsize, cnum, ¶ms, &data); - break; - default: - /* Error in request */ - DEBUG(2,("%s Unknown request %d in trans2 call\n",timestring(), tran_call)); - if(params) - free(params); - if(data) - free(data); - return (ERROR(ERRSRV,ERRerror)); - } - /* As we do not know how many data packets will need to be - returned here the various call_trans2xxxx calls - must send their own. Thus a call_trans2xxx routine only - returns a value other than -1 when it wants to send - an error packet. - */ - - if(params) - free(params); - if(data) - free(data); - return outsize; /* If a correct response was needed the call_trans2xxx - calls have already sent it. If outsize != -1 then it is - returning an error packet. */ + /* Now we must call the relevant TRANS2 function */ + switch(tran_call) { + case TRANSACT2_OPEN: + outsize = call_trans2open(conn, + inbuf, outbuf, bufsize, + ¶ms, &data); + break; + + case TRANSACT2_FINDFIRST: + outsize = call_trans2findfirst(conn, inbuf, outbuf, + bufsize, ¶ms, &data); + break; + + case TRANSACT2_FINDNEXT: + outsize = call_trans2findnext(conn, inbuf, outbuf, + length, bufsize, + ¶ms, &data); + break; + + case TRANSACT2_QFSINFO: + outsize = call_trans2qfsinfo(conn, inbuf, outbuf, + length, bufsize, ¶ms, + &data); + break; + + case TRANSACT2_SETFSINFO: + outsize = call_trans2setfsinfo(conn, inbuf, outbuf, + length, bufsize, + ¶ms, &data); + break; + + case TRANSACT2_QPATHINFO: + case TRANSACT2_QFILEINFO: + outsize = call_trans2qfilepathinfo(conn, inbuf, outbuf, + length, bufsize, + ¶ms, &data, total_data); + break; + case TRANSACT2_SETPATHINFO: + case TRANSACT2_SETFILEINFO: + outsize = call_trans2setfilepathinfo(conn, inbuf, outbuf, + length, bufsize, + ¶ms, &data, + total_data); + break; + + case TRANSACT2_FINDNOTIFYFIRST: + outsize = call_trans2findnotifyfirst(conn, inbuf, outbuf, + length, bufsize, + ¶ms, &data); + break; + + case TRANSACT2_FINDNOTIFYNEXT: + outsize = call_trans2findnotifynext(conn, inbuf, outbuf, + length, bufsize, + ¶ms, &data); + break; + case TRANSACT2_MKDIR: + outsize = call_trans2mkdir(conn, inbuf, outbuf, length, + bufsize, ¶ms, &data); + break; + default: + /* Error in request */ + DEBUG(2,("Unknown request %d in trans2 call\n", tran_call)); + if(params) + free(params); + if(data) + free(data); + return (ERROR(ERRSRV,ERRerror)); + } + + /* As we do not know how many data packets will need to be + returned here the various call_trans2xxxx calls + must send their own. Thus a call_trans2xxx routine only + returns a value other than -1 when it wants to send + an error packet. + */ + + if(params) + free(params); + if(data) + free(data); + return outsize; /* If a correct response was needed the + call_trans2xxx calls have already sent + it. If outsize != -1 then it is returning */ } |