diff options
Diffstat (limited to 'source3/smbd/reply.c')
-rw-r--r-- | source3/smbd/reply.c | 3210 |
1 files changed, 3210 insertions, 0 deletions
diff --git a/source3/smbd/reply.c b/source3/smbd/reply.c new file mode 100644 index 0000000000..b7b51775bb --- /dev/null +++ b/source3/smbd/reply.c @@ -0,0 +1,3210 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + Main SMB reply routines + Copyright (C) Andrew Tridgell 1992-1995 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +/* + This file handles most of the reply_ calls that the server + makes to handle specific protocols +*/ + + +#include "includes.h" +#include "loadparm.h" +#include "trans2.h" + +/* look in server.c for some explanation of these variables */ +extern int Protocol; +extern int DEBUGLEVEL; +extern int chain_size; +extern int maxxmit; +extern int chain_fnum; +extern char magic_char; +extern connection_struct Connections[]; +extern files_struct Files[]; +extern BOOL case_sensitive; +extern pstring sesssetup_user; +extern int Client; + +/* this macro should always be used to extract an fnum (smb_fid) from +a packet to ensure chaining works correctly */ +#define GETFNUM(buf,where) (chain_fnum!= -1?chain_fnum:SVAL(buf,where)) + + +/**************************************************************************** + reply to an special message +****************************************************************************/ +int reply_special(char *inbuf,char *outbuf) +{ + int outsize = 4; + int msg_type = CVAL(inbuf,0); + int msg_flags = CVAL(inbuf,1); + pstring name1,name2; + extern fstring remote_machine; + extern fstring local_machine; + char *p; + + *name1 = *name2 = 0; + + smb_setlen(outbuf,0); + + switch (msg_type) + { + case 0x81: /* session request */ + CVAL(outbuf,0) = 0x82; + CVAL(outbuf,3) = 0; + if (name_len(inbuf+4) > 50) + { + DEBUG(0,("Invalid name length in session request\n")); + return(0); + } + name_extract(inbuf,4,name1); + name_extract(inbuf,4 + name_len(inbuf + 4),name2); + DEBUG(2,("netbios connect: name1=%s name2=%s\n",name1,name2)); + + strcpy(remote_machine,name2); + trim_string(remote_machine," "," "); + p = strchr(remote_machine,' '); + strlower(remote_machine); + if (p) *p = 0; + + strcpy(local_machine,name1); + trim_string(local_machine," "," "); + p = strchr(local_machine,' '); + strlower(local_machine); + if (p) *p = 0; + + add_session_user(remote_machine); + + reload_services(True); + reopen_logs(); + + break; + case 0x85: /* session keepalive */ + default: + return(0); + } + + DEBUG(5,("%s init msg_type=0x%x msg_flags=0x%x\n",timestring(),msg_type,msg_flags)); + + return(outsize); +} + + +/******************************************************************* +work out what error to give to a failed connection +********************************************************************/ +static int connection_error(char *inbuf,char *outbuf,int connection_num) +{ + switch (connection_num) + { + case -8: + return(ERROR(ERRSRV,ERRnoresource)); + case -7: + return(ERROR(ERRSRV,ERRbaduid)); + case -6: + return(ERROR(ERRSRV,ERRinvdevice)); + case -5: + return(ERROR(ERRSRV,ERRinvnetname)); + case -4: + return(ERROR(ERRSRV,ERRaccess)); + case -3: + return(ERROR(ERRDOS,ERRnoipc)); + case -2: + return(ERROR(ERRSRV,ERRinvnetname)); + } + return(ERROR(ERRSRV,ERRbadpw)); +} + + +/**************************************************************************** + reply to a tcon +****************************************************************************/ +int reply_tcon(char *inbuf,char *outbuf) +{ + pstring service; + pstring user; + pstring password; + pstring dev; + int connection_num; + int outsize = 0; + int uid = SVAL(inbuf,smb_uid); + int vuid; + int pwlen; + + *service = *user = *password = *dev = 0; + + vuid = valid_uid(uid); + + parse_connect(inbuf,service,user,password,&pwlen,dev); + + connection_num = make_connection(service,user,password,pwlen,dev,vuid); + + if (connection_num < 0) + return(connection_error(inbuf,outbuf,connection_num)); + + outsize = set_message(outbuf,2,0,True); + SSVAL(outbuf,smb_vwv0,maxxmit); + SSVAL(outbuf,smb_vwv1,connection_num); + SSVAL(outbuf,smb_tid,connection_num); + + DEBUG(3,("%s tcon service=%s user=%s cnum=%d\n",timestring(),service,user,connection_num)); + + return(outsize); +} + + +/**************************************************************************** + reply to a tcon and X +****************************************************************************/ +int reply_tcon_and_X(char *inbuf,char *outbuf,int length,int bufsize) +{ + pstring service; + pstring user; + pstring password; + pstring devicename; + int connection_num; + int outsize = 0; + int uid = SVAL(inbuf,smb_uid); + int vuid; + int smb_com2 = SVAL(inbuf,smb_vwv0); + int smb_off2 = SVAL(inbuf,smb_vwv1); + int passlen = SVAL(inbuf,smb_vwv3); + + *service = *user = *password = *devicename = 0; + + /* we might have to close an old one */ + if ((SVAL(inbuf,smb_vwv2) & 0x1) != 0) + close_cnum(SVAL(inbuf,smb_tid),uid); + + vuid = valid_uid(uid); + + { + char *path; + char *p; + memcpy(password,smb_buf(inbuf),passlen); + password[passlen]=0; + path = smb_buf(inbuf) + passlen; + DEBUG(4,("parsing net-path %s, passlen=%d\n",path,passlen)); + strcpy(service,path+2); + p = strchr(service,'\\'); + if (!p) + return(ERROR(ERRSRV,ERRinvnetname)); + *p = 0; + strcpy(service,p+1); + p = strchr(service,'%'); + if (p) + { + *p++ = 0; + strcpy(user,p); + } + StrnCpy(devicename,path + strlen(path) + 1,6); + DEBUG(4,("Got device type %s\n",devicename)); + } + + connection_num = make_connection(service,user,password,passlen,devicename,vuid); + + if (connection_num < 0) + return(connection_error(inbuf,outbuf,connection_num)); + + outsize = set_message(outbuf,2,strlen(devicename)+1,True); + + DEBUG(3,("%s tconX service=%s user=%s cnum=%d\n",timestring(),service,user,connection_num)); + + /* set the incoming and outgoing tid to the just created one */ + SSVAL(inbuf,smb_tid,connection_num); + SSVAL(outbuf,smb_tid,connection_num); + + CVAL(outbuf,smb_vwv0) = smb_com2; + SSVAL(outbuf,smb_vwv1,(chain_size + outsize)-4); + + strcpy(smb_buf(outbuf),devicename); + + if (smb_com2 != 0xFF) + outsize += chain_reply(smb_com2,inbuf,inbuf+smb_off2+4, + outbuf,outbuf+outsize, + length,bufsize); + + return(outsize); +} + + +/**************************************************************************** + reply to an unknown type +****************************************************************************/ +int reply_unknown(char *inbuf,char *outbuf) +{ + int cnum; + int type; + cnum = SVAL(inbuf,smb_tid); + type = CVAL(inbuf,smb_com); + + DEBUG(0,("%s unknown command type (%s): cnum=%d type=%d (0x%X)\n", + timestring(), + smb_fn_name(type), + cnum,type,type)); + + return(ERROR(ERRSRV,ERRunknownsmb)); +} + + +/**************************************************************************** + reply to an ioctl +****************************************************************************/ +int reply_ioctl(char *inbuf,char *outbuf) +{ + DEBUG(3,("ignoring ioctl\n")); + + return(ERROR(ERRSRV,ERRnosupport)); +} + + +/**************************************************************************** +reply to a session setup command +****************************************************************************/ +int reply_sesssetup_and_X(char *inbuf,char *outbuf,int length,int bufsize) +{ + int outsize = 0; + int sess_uid; + int gid; + int smb_com2; + int smb_off2; + int smb_bufsize; + int smb_mpxmax; + int smb_vc_num; + uint32 smb_sesskey; + int smb_apasslen; + pstring smb_apasswd; + int smb_ntpasslen = 0; + pstring smb_ntpasswd; + BOOL valid_nt_password = False; + pstring user; + BOOL guest=False; + + *smb_apasswd = 0; + + sess_uid = SVAL(inbuf,smb_uid); + smb_com2 = CVAL(inbuf,smb_vwv0); + smb_off2 = SVAL(inbuf,smb_vwv1); + smb_bufsize = SVAL(inbuf,smb_vwv2); + smb_mpxmax = SVAL(inbuf,smb_vwv3); + smb_vc_num = SVAL(inbuf,smb_vwv4); + smb_sesskey = IVAL(inbuf,smb_vwv5); + + if (Protocol < PROTOCOL_NT1) { + smb_apasslen = SVAL(inbuf,smb_vwv7); + memcpy(smb_apasswd,smb_buf(inbuf),smb_apasslen); + StrnCpy(user,smb_buf(inbuf)+smb_apasslen,sizeof(user)-1); + } else { + uint16 passlen1 = SVAL(inbuf,smb_vwv7); + uint16 passlen2 = SVAL(inbuf,smb_vwv8); + BOOL doencrypt = SMBENCRYPT(); + char *p = smb_buf(inbuf); + if (passlen1 > 256) passlen1 = 0; + if (passlen2 > 256) passlen2 = 0; /* I don't know why NT gives weird + lengths sometimes */ + if(doencrypt) { + /* Save the lanman2 password and the NT md4 password. */ + smb_apasslen = passlen1; + memcpy(smb_apasswd,p,smb_apasslen); + smb_ntpasslen = passlen2; + memcpy(smb_ntpasswd,p+passlen1,smb_ntpasslen); + } else { + /* for Win95 */ + if (passlen1 > passlen2) { + smb_apasslen = passlen1; + StrnCpy(smb_apasswd,p,smb_apasslen); + } else { + smb_apasslen = passlen2; + StrnCpy(smb_apasswd,p + passlen1,smb_apasslen); + } + } + if (passlen2 == 1) { + /* apparently NT sometimes sets passlen2 to 1 when it means 0. This + tries to work around that problem */ + passlen2 = 0; + } + p += passlen1 + passlen2; + strcpy(user,p); p = skip_string(p,1); + DEBUG(3,("Domain=[%s] NativeOS=[%s] NativeLanMan=[%s]\n", + p,skip_string(p,1),skip_string(p,2))); + } + + + DEBUG(3,("sesssetupX:name=[%s]\n",user)); + + if (!*user) + strcpy(user,lp_guestaccount(-1)); + + strlower(user); + + strcpy(sesssetup_user,user); + + reload_services(True); + + add_session_user(user); + + + if (!(lp_security() == SEC_SERVER && server_validate(inbuf)) && + !check_hosts_equiv(user)) + { + + if (strequal(user,lp_guestaccount(-1)) && (*smb_apasswd == 0)) + guest = True; + + /* now check if it's a valid username/password */ + /* If an NT password was supplied try and validate with that + first. This is superior as the passwords are mixed case 128 length unicode */ + if(smb_ntpasslen && !guest) + { + if(!password_ok(user,smb_ntpasswd,smb_ntpasslen,NULL,True)) + DEBUG(0,("NT Password did not match ! Defaulting to Lanman\n")); + else + valid_nt_password = True; + } + if (!valid_nt_password && !guest && !password_ok(user,smb_apasswd,smb_apasslen,NULL,False)) + { + if (lp_security() >= SEC_USER) { +#if (GUEST_SESSSETUP == 0) + return(ERROR(ERRSRV,ERRbadpw)); +#endif +#if (GUEST_SESSSETUP == 1) + if (Get_Pwnam(user,True)) + return(ERROR(ERRSRV,ERRbadpw)); +#endif + } + if (*smb_apasswd || !Get_Pwnam(user,True)) + strcpy(user,lp_guestaccount(-1)); + DEBUG(3,("Registered username %s for guest access\n",user)); + guest = True; + } + } + + if (!Get_Pwnam(user,True)) { + DEBUG(3,("No such user %s - using guest account\n",user)); + strcpy(user,lp_guestaccount(-1)); + guest = True; + } + + if (!strequal(user,lp_guestaccount(-1)) && + lp_servicenumber(user) < 0) + { + int homes = lp_servicenumber(HOMES_NAME); + char *home = get_home_dir(user); + if (homes >= 0 && home) + lp_add_home(user,homes,home); + } + + + /* it's ok - setup a reply */ + if (Protocol < PROTOCOL_NT1) { + outsize = set_message(outbuf,3,0,True); + } else { + char *p; + outsize = set_message(outbuf,3,3,True); + p = smb_buf(outbuf); + strcpy(p,"Unix"); p = skip_string(p,1); + strcpy(p,"Samba "); strcat(p,VERSION); p = skip_string(p,1); + strcpy(p,my_workgroup()); p = skip_string(p,1); + outsize = set_message(outbuf,3,PTR_DIFF(p,smb_buf(outbuf)),False); + /* perhaps grab OS version here?? */ + } + + /* Set the correct uid in the outgoing and incoming packets + We will use this on future requests to determine which + user we should become. + */ + { + struct passwd *pw = Get_Pwnam(user,False); + if (!pw) { + DEBUG(1,("Username %s is invalid on this system\n",user)); + return(ERROR(ERRSRV,ERRbadpw)); + } + gid = pw->pw_gid; + SSVAL(outbuf,smb_uid,(uint16)pw->pw_uid); + SSVAL(inbuf,smb_uid,(uint16)pw->pw_uid); + } + + CVAL(outbuf,smb_vwv0) = smb_com2; + SSVAL(outbuf,smb_vwv1,(chain_size+outsize)-4); + + if (guest) + SSVAL(outbuf,smb_vwv2,1); + + /* register the name and uid as being validated, so further connections + to a uid can get through without a password, on the same VC */ + register_uid(SVAL(inbuf,smb_uid),gid,user,guest); + + maxxmit = MIN(maxxmit,smb_bufsize); + + if (smb_com2 != 0xFF) + outsize += chain_reply(smb_com2,inbuf,inbuf+smb_off2+4, + outbuf,outbuf+outsize, + length,bufsize); + + return(outsize); +} + + +/**************************************************************************** + reply to a chkpth +****************************************************************************/ +int reply_chkpth(char *inbuf,char *outbuf) +{ + int outsize = 0; + int cnum,mode; + pstring name; + BOOL ok = False; + + cnum = SVAL(inbuf,smb_tid); + + strcpy(name,smb_buf(inbuf) + 1); + unix_convert(name,cnum); + + mode = SVAL(inbuf,smb_vwv0); + + if (check_name(name,cnum)) + ok = directory_exist(name,NULL); + + if (!ok) + return(ERROR(ERRDOS,ERRbadpath)); + + outsize = set_message(outbuf,0,0,True); + + DEBUG(3,("%s chkpth %s cnum=%d mode=%d\n",timestring(),name,cnum,mode)); + + return(outsize); +} + + +/**************************************************************************** + reply to a getatr +****************************************************************************/ +int reply_getatr(char *inbuf,char *outbuf) +{ + pstring fname; + int cnum; + int outsize = 0; + struct stat sbuf; + BOOL ok = False; + int mode=0; + uint32 size=0; + time_t mtime=0; + + cnum = SVAL(inbuf,smb_tid); + + strcpy(fname,smb_buf(inbuf) + 1); + unix_convert(fname,cnum); + + /* dos smetimes asks for a stat of "" - it returns a "hidden directory" + under WfWg - weird! */ + if (! (*fname)) + { + mode = aHIDDEN | aDIR; + if (!CAN_WRITE(cnum)) mode |= aRONLY; + size = 0; + mtime = 0; + ok = True; + } + else + if (check_name(fname,cnum)) + { + if (sys_stat(fname,&sbuf) == 0) + { + mode = dos_mode(cnum,fname,&sbuf); + size = sbuf.st_size; + mtime = sbuf.st_mtime; + if (mode & aDIR) + size = 0; + ok = True; + } + else + DEBUG(3,("stat of %s failed (%s)\n",fname,strerror(errno))); + } + + if (!ok) + return(UNIXERROR(ERRDOS,ERRbadfile)); + + outsize = set_message(outbuf,10,0,True); + + SSVAL(outbuf,smb_vwv0,mode); + put_dos_date3(outbuf,smb_vwv1,mtime); + SIVAL(outbuf,smb_vwv3,size); + + if (Protocol >= PROTOCOL_NT1) { + char *p = strrchr(fname,'/'); + uint16 flg2 = SVAL(outbuf,smb_flg2); + if (!p) p = fname; + if (!is_8_3(fname)) + SSVAL(outbuf,smb_flg2,flg2 | 0x40); /* IS_LONG_NAME */ + } + + DEBUG(3,("%s getatr name=%s mode=%d size=%d\n",timestring(),fname,mode,size)); + + return(outsize); +} + + +/**************************************************************************** + reply to a setatr +****************************************************************************/ +int reply_setatr(char *inbuf,char *outbuf) +{ + pstring fname; + int cnum; + int outsize = 0; + BOOL ok=False; + int mode; + time_t mtime; + + cnum = SVAL(inbuf,smb_tid); + + strcpy(fname,smb_buf(inbuf) + 1); + unix_convert(fname,cnum); + + mode = SVAL(inbuf,smb_vwv0); + mtime = make_unix_date3(inbuf+smb_vwv1); + + if (directory_exist(fname,NULL)) + mode |= aDIR; + if (check_name(fname,cnum)) + ok = (dos_chmod(cnum,fname,mode,NULL) == 0); + if (ok) + ok = set_filetime(fname,mtime); + + if (!ok) + return(UNIXERROR(ERRDOS,ERRnoaccess)); + + outsize = set_message(outbuf,0,0,True); + + DEBUG(3,("%s setatr name=%s mode=%d\n",timestring(),fname,mode)); + + return(outsize); +} + + +/**************************************************************************** + reply to a dskattr +****************************************************************************/ +int reply_dskattr(char *inbuf,char *outbuf) +{ + int cnum; + int outsize = 0; + int dfree,dsize,bsize; + + cnum = SVAL(inbuf,smb_tid); + + sys_disk_free(".",&bsize,&dfree,&dsize); + + outsize = set_message(outbuf,5,0,True); + + SSVAL(outbuf,smb_vwv0,dsize); + SSVAL(outbuf,smb_vwv1,bsize/512); + SSVAL(outbuf,smb_vwv2,512); + SSVAL(outbuf,smb_vwv3,dfree); + + DEBUG(3,("%s dskattr cnum=%d dfree=%d\n",timestring(),cnum,dfree)); + + return(outsize); +} + + +/**************************************************************************** + reply to a search + Can be called from SMBsearch, SMBffirst or SMBfunique. +****************************************************************************/ +int reply_search(char *inbuf,char *outbuf) +{ + pstring mask; + pstring directory; + pstring fname; + int size,mode; + time_t date; + int dirtype; + int cnum; + int outsize = 0; + int numentries = 0; + BOOL finished = False; + int maxentries; + int i; + char *p; + BOOL ok = False; + int status_len; + char *path; + char status[21]; + int dptr_num= -1; + BOOL check_descend = False; + BOOL expect_close = False; + BOOL can_open = True; + + *mask = *directory = *fname = 0; + + /* If we were called as SMBffirst then we must expect close. */ + if(CVAL(inbuf,smb_com) == SMBffirst) + expect_close = True; + + cnum = SVAL(inbuf,smb_tid); + + outsize = set_message(outbuf,1,3,True); + maxentries = SVAL(inbuf,smb_vwv0); + dirtype = SVAL(inbuf,smb_vwv1); + path = smb_buf(inbuf) + 1; + status_len = SVAL(smb_buf(inbuf),3 + strlen(path)); + + + /* dirtype &= ~aDIR; */ + + DEBUG(5,("path=%s status_len=%d\n",path,status_len)); + + + if (status_len == 0) + { + pstring dir2; + + strcpy(directory,smb_buf(inbuf)+1); + strcpy(dir2,smb_buf(inbuf)+1); + unix_convert(directory,cnum); + unix_format(dir2); + + if (!check_name(directory,cnum)) + can_open = False; + + p = strrchr(dir2,'/'); + if (p == NULL) + {strcpy(mask,dir2);*dir2 = 0;} + else + {*p = 0;strcpy(mask,p+1);} + + p = strrchr(directory,'/'); + if (!p) + *directory = 0; + else + *p = 0; + + if (strlen(directory) == 0) + strcpy(directory,"./"); + bzero(status,21); + CVAL(status,0) = dirtype; + } + else + { + memcpy(status,smb_buf(inbuf) + 1 + strlen(path) + 4,21); + memcpy(mask,status+1,11); + mask[11] = 0; + dirtype = CVAL(status,0) & 0x1F; + Connections[cnum].dirptr = dptr_fetch(status+12,&dptr_num); + if (!Connections[cnum].dirptr) + goto SearchEmpty; + string_set(&Connections[cnum].dirpath,dptr_path(dptr_num)); + if (!case_sensitive) + strnorm(mask); + } + + /* turn strings of spaces into a . */ + { + trim_string(mask,NULL," "); + if ((p = strrchr(mask,' '))) + { + fstring ext; + strcpy(ext,p+1); + *p = 0; + trim_string(mask,NULL," "); + strcat(mask,"."); + strcat(mask,ext); + } + } + + { + for (p=mask; *p; p++) + { + if (*p != '?' && *p != '*' && !isdoschar(*p)) + { + DEBUG(5,("Invalid char [%c] in search mask?\n",*p)); + *p = '?'; + } + } + } + + if (!strchr(mask,'.') && strlen(mask)>8) + { + fstring tmp; + strcpy(tmp,&mask[8]); + mask[8] = '.'; + mask[9] = 0; + strcat(mask,tmp); + } + + DEBUG(5,("mask=%s directory=%s\n",mask,directory)); + + if (can_open) + { + p = smb_buf(outbuf) + 3; + + ok = True; + + if (status_len == 0) + { + dptr_num = dptr_create(cnum,directory,expect_close,SVAL(inbuf,smb_pid)); + if (dptr_num < 0) + return(ERROR(ERRDOS,ERRnofids)); + } + + DEBUG(4,("dptr_num is %d\n",dptr_num)); + + if (ok) + { + if ((dirtype&0x1F) == aVOLID) + { + memcpy(p,status,21); + make_dir_struct(p,"???????????",volume_label(SNUM(cnum)),0,aVOLID,0); + dptr_fill(p+12,dptr_num); + if (dptr_zero(p+12) && (status_len==0)) + numentries = 1; + else + numentries = 0; + p += DIR_STRUCT_SIZE; + } + else + { + DEBUG(8,("dirpath=<%s> dontdescend=<%s>\n",Connections[cnum].dirpath,lp_dontdescend(SNUM(cnum)))); + if (in_list(Connections[cnum].dirpath, + lp_dontdescend(SNUM(cnum)),True)) + check_descend = True; + + for (i=numentries;(i<maxentries) && !finished;i++) + { + finished = + !get_dir_entry(cnum,mask,dirtype,fname,&size,&mode,&date,check_descend); + if (!finished) + { + memcpy(p,status,21); + make_dir_struct(p,mask,fname,size,mode,date); + dptr_fill(p+12,dptr_num); + numentries++; + } + p += DIR_STRUCT_SIZE; + } + } + } + } + + + SearchEmpty: + + if (numentries == 0 || !ok) + { + CVAL(outbuf,smb_rcls) = ERRDOS; + SSVAL(outbuf,smb_err,ERRnofiles); + } + + /* If we were called as SMBffirst with smb_search_id == NULL + and no entries were found then return error and close dirptr + (X/Open spec) */ + + if(ok && expect_close && numentries == 0 && status_len == 0) + { + CVAL(outbuf,smb_rcls) = ERRDOS; + SSVAL(outbuf,smb_err,ERRnofiles); + /* Also close the dptr - we know it's gone */ + dptr_close(dptr_num); + } + + /* If we were called as SMBfunique, then we can close the dirptr now ! */ + if(dptr_num >= 0 && CVAL(inbuf,smb_com) == SMBfunique) + dptr_close(dptr_num); + + SSVAL(outbuf,smb_vwv0,numentries); + SSVAL(outbuf,smb_vwv1,3 + numentries * DIR_STRUCT_SIZE); + CVAL(smb_buf(outbuf),0) = 5; + SSVAL(smb_buf(outbuf),1,numentries*DIR_STRUCT_SIZE); + + if (Protocol >= PROTOCOL_NT1) { + uint16 flg2 = SVAL(outbuf,smb_flg2); + SSVAL(outbuf,smb_flg2,flg2 | 0x40); /* IS_LONG_NAME */ + } + + outsize += DIR_STRUCT_SIZE*numentries; + smb_setlen(outbuf,outsize - 4); + + if ((! *directory) && dptr_path(dptr_num)) + sprintf(directory,"(%s)",dptr_path(dptr_num)); + + DEBUG(4,("%s %s mask=%s path=%s cnum=%d dtype=%d nument=%d of %d\n", + timestring(), + smb_fn_name(CVAL(inbuf,smb_com)), + mask,directory,cnum,dirtype,numentries,maxentries)); + + return(outsize); +} + + +/**************************************************************************** + reply to a fclose (stop directory search) +****************************************************************************/ +int reply_fclose(char *inbuf,char *outbuf) +{ + int cnum; + int outsize = 0; + int status_len; + char *path; + char status[21]; + int dptr_num= -1; + + cnum = SVAL(inbuf,smb_tid); + + outsize = set_message(outbuf,1,0,True); + path = smb_buf(inbuf) + 1; + status_len = SVAL(smb_buf(inbuf),3 + strlen(path)); + + + if (status_len == 0) + return(ERROR(ERRSRV,ERRsrverror)); + + memcpy(status,smb_buf(inbuf) + 1 + strlen(path) + 4,21); + + if(dptr_fetch(status+12,&dptr_num)) { + /* Close the dptr - we know it's gone */ + dptr_close(dptr_num); + } + + SSVAL(outbuf,smb_vwv0,0); + + DEBUG(3,("%s search close cnum=%d\n",timestring(),cnum)); + + return(outsize); +} + + +/**************************************************************************** + reply to an open +****************************************************************************/ +int reply_open(char *inbuf,char *outbuf) +{ + pstring fname; + int cnum; + int fnum = -1; + int outsize = 0; + int fmode=0; + int share_mode; + int size = 0; + time_t mtime=0; + int unixmode; + int rmode=0; + struct stat sbuf; + + cnum = SVAL(inbuf,smb_tid); + + share_mode = SVAL(inbuf,smb_vwv0); + + strcpy(fname,smb_buf(inbuf)+1); + unix_convert(fname,cnum); + + fnum = find_free_file(); + if (fnum < 0) + return(ERROR(ERRSRV,ERRnofids)); + + if (!check_name(fname,cnum)) + return(UNIXERROR(ERRDOS,ERRnoaccess)); + + unixmode = unix_mode(cnum,aARCH); + + open_file_shared(fnum,cnum,fname,share_mode,3,unixmode,&rmode,NULL); + + if (!Files[fnum].open) + return(UNIXERROR(ERRDOS,ERRnoaccess)); + + if (fstat(Files[fnum].fd,&sbuf) != 0) { + close_file(fnum); + return(ERROR(ERRDOS,ERRnoaccess)); + } + + size = sbuf.st_size; + fmode = dos_mode(cnum,fname,&sbuf); + mtime = sbuf.st_mtime; + + if (fmode & aDIR) { + DEBUG(3,("attempt to open a directory %s\n",fname)); + close_file(fnum); + return(ERROR(ERRDOS,ERRnoaccess)); + } + + outsize = set_message(outbuf,7,0,True); + SSVAL(outbuf,smb_vwv0,fnum); + SSVAL(outbuf,smb_vwv1,fmode); + put_dos_date3(outbuf,smb_vwv2,mtime); + SIVAL(outbuf,smb_vwv4,size); + SSVAL(outbuf,smb_vwv6,rmode); + + return(outsize); +} + + +/**************************************************************************** + reply to an open and X +****************************************************************************/ +int reply_open_and_X(char *inbuf,char *outbuf,int length,int bufsize) +{ + pstring fname; + int cnum = SVAL(inbuf,smb_tid); + int fnum = -1; + int outsize = 0; + int openmode = 0; + int smb_com2 = CVAL(inbuf,smb_vwv0); + int smb_off2 = SVAL(inbuf,smb_vwv1); + int smb_mode = SVAL(inbuf,smb_vwv3); + int smb_attr = SVAL(inbuf,smb_vwv5); +#if 0 + int open_flags = SVAL(inbuf,smb_vwv2); + int smb_sattr = SVAL(inbuf,smb_vwv4); + uint32 smb_time = make_unix_date3(inbuf+smb_vwv6); +#endif + int smb_ofun = SVAL(inbuf,smb_vwv8); + int unixmode; + int size=0,fmode=0,mtime=0,rmode=0; + struct stat sbuf; + int smb_action = 0; + + /* XXXX we need to handle passed times, sattr and flags */ + + strcpy(fname,smb_buf(inbuf)); + unix_convert(fname,cnum); + + /* now add create and trunc bits */ + if (smb_ofun & 0x10) + openmode |= O_CREAT; + if ((smb_ofun & 0x3) == 2) + openmode |= O_TRUNC; + + fnum = find_free_file(); + if (fnum < 0) + return(ERROR(ERRSRV,ERRnofids)); + + if (!check_name(fname,cnum)) + return(UNIXERROR(ERRDOS,ERRnoaccess)); + + unixmode = unix_mode(cnum,smb_attr | aARCH); + + open_file_shared(fnum,cnum,fname,smb_mode,smb_ofun,unixmode, + &rmode,&smb_action); + + if (!Files[fnum].open) + return(UNIXERROR(ERRDOS,ERRnoaccess)); + + if (fstat(Files[fnum].fd,&sbuf) != 0) { + close_file(fnum); + return(ERROR(ERRDOS,ERRnoaccess)); + } + + size = sbuf.st_size; + fmode = dos_mode(cnum,fname,&sbuf); + mtime = sbuf.st_mtime; + if (fmode & aDIR) { + close_file(fnum); + return(ERROR(ERRDOS,ERRnoaccess)); + } + + outsize = set_message(outbuf,15,0,True); + CVAL(outbuf,smb_vwv0) = smb_com2; + SSVAL(outbuf,smb_vwv1,(chain_size+outsize)-4); + SSVAL(outbuf,smb_vwv2,fnum); + SSVAL(outbuf,smb_vwv3,fmode); + put_dos_date3(outbuf,smb_vwv4,mtime); + SIVAL(outbuf,smb_vwv6,size); + SSVAL(outbuf,smb_vwv8,rmode); + SSVAL(outbuf,smb_vwv11,smb_action); + + chain_fnum = fnum; + + if (smb_com2 != 0xFF) + outsize += chain_reply(smb_com2,inbuf,inbuf+smb_off2+4, + outbuf,outbuf+outsize, + length,bufsize); + + chain_fnum = -1; + + return(outsize); +} + + +/**************************************************************************** + reply to a SMBulogoffX +****************************************************************************/ +int reply_ulogoffX(char *inbuf,char *outbuf,int length,int bufsize) +{ + int outsize = 0; + int smb_com2 = CVAL(inbuf,smb_vwv0); + int smb_off2 = SVAL(inbuf,smb_vwv1); + int uid = SVAL(inbuf,smb_uid); + + invalidate_uid(uid); + + outsize = set_message(outbuf,2,0,True); + CVAL(outbuf,smb_vwv0) = smb_com2; + SSVAL(outbuf,smb_vwv1,(chain_size+outsize)-4); + + DEBUG(3,("%s ulogoffX uid=%d\n",timestring(),uid)); + + if (smb_com2 != 0xFF) + outsize += chain_reply(smb_com2,inbuf,inbuf+smb_off2+4, + outbuf,outbuf+outsize, + length,bufsize); + + + return(outsize); +} + + +/**************************************************************************** + reply to a mknew +****************************************************************************/ +int reply_mknew(char *inbuf,char *outbuf) +{ + pstring fname; + int cnum,com; + int fnum = -1; + int outsize = 0; + int createmode; + mode_t unixmode; + + com = SVAL(inbuf,smb_com); + cnum = SVAL(inbuf,smb_tid); + + createmode = SVAL(inbuf,smb_vwv0); + strcpy(fname,smb_buf(inbuf)+1); + unix_convert(fname,cnum); + + if (createmode & aVOLID) + { + DEBUG(0,("Attempt to create file (%s) with volid set - please report this\n",fname)); + } + + unixmode = unix_mode(cnum,createmode); + + if (com == SMBmknew && file_exist(fname,NULL)) + return(ERROR(ERRDOS,ERRfilexists)); + + fnum = find_free_file(); + if (fnum < 0) + return(ERROR(ERRSRV,ERRnofids)); + + if (!check_name(fname,cnum)) + return(UNIXERROR(ERRDOS,ERRnoaccess)); + + open_file(fnum,cnum,fname,O_RDWR | O_CREAT | O_TRUNC,unixmode); + + if (!Files[fnum].open) + return(UNIXERROR(ERRDOS,ERRnoaccess)); + + outsize = set_message(outbuf,1,0,True); + SSVAL(outbuf,smb_vwv0,fnum); + + DEBUG(2,("new file %s\n",fname)); + DEBUG(3,("%s mknew %s fd=%d fnum=%d cnum=%d dmode=%d umode=%o\n",timestring(),fname,Files[fnum].fd,fnum,cnum,createmode,unixmode)); + + return(outsize); +} + + +/**************************************************************************** + reply to a create temporary file +****************************************************************************/ +int reply_ctemp(char *inbuf,char *outbuf) +{ + pstring fname; + pstring fname2; + int cnum; + int fnum = -1; + int outsize = 0; + int createmode; + mode_t unixmode; + + cnum = SVAL(inbuf,smb_tid); + createmode = SVAL(inbuf,smb_vwv0); + sprintf(fname,"%s/TMXXXXXX",smb_buf(inbuf)+1); + unix_convert(fname,cnum); + + unixmode = unix_mode(cnum,createmode); + + fnum = find_free_file(); + if (fnum < 0) + return(ERROR(ERRSRV,ERRnofids)); + + if (!check_name(fname,cnum)) + return(UNIXERROR(ERRDOS,ERRnoaccess)); + + strcpy(fname2,(char *)mktemp(fname)); + + open_file(fnum,cnum,fname2,O_RDWR | O_CREAT | O_TRUNC,unixmode); + + if (!Files[fnum].open) + return(UNIXERROR(ERRDOS,ERRnoaccess)); + + outsize = set_message(outbuf,1,2 + strlen(fname2),True); + SSVAL(outbuf,smb_vwv0,fnum); + CVAL(smb_buf(outbuf),0) = 4; + strcpy(smb_buf(outbuf) + 1,fname2); + + DEBUG(2,("created temp file %s\n",fname2)); + DEBUG(3,("%s ctemp %s fd=%d fnum=%d cnum=%d dmode=%d umode=%o\n",timestring(),fname2,Files[fnum].fd,fnum,cnum,createmode,unixmode)); + + return(outsize); +} + + +/******************************************************************* +check if a user is allowed to delete a file +********************************************************************/ +static BOOL can_delete(char *fname,int cnum,int dirtype) +{ + struct stat sbuf; + int fmode; + + if (!CAN_WRITE(cnum)) return(False); + + if (sys_lstat(fname,&sbuf) != 0) return(False); + fmode = dos_mode(cnum,fname,&sbuf); + if (fmode & aDIR) return(False); + if (fmode & aRONLY) return(False); + if ((fmode & ~dirtype) & (aHIDDEN | aSYSTEM)) + return(False); + if (!check_file_sharing(cnum,fname)) return(False); + return(True); +} + +/**************************************************************************** + reply to a unlink +****************************************************************************/ +int reply_unlink(char *inbuf,char *outbuf) +{ + int outsize = 0; + pstring name; + int cnum; + int dirtype; + pstring directory; + pstring mask; + char *p; + int count=0; + int error = ERRnoaccess; + BOOL has_wild; + BOOL exists=False; + + *directory = *mask = 0; + + cnum = SVAL(inbuf,smb_tid); + dirtype = SVAL(inbuf,smb_vwv0); + + strcpy(name,smb_buf(inbuf) + 1); + + DEBUG(3,("reply_unlink : %s\n",name)); + + unix_convert(name,cnum); + + p = strrchr(name,'/'); + if (!p) { + strcpy(directory,"./"); + strcpy(mask,name); + } else { + *p = 0; + strcpy(directory,name); + strcpy(mask,p+1); + } + + if (is_mangled(mask)) + check_mangled_stack(mask); + + has_wild = strchr(mask,'*') || strchr(mask,'?'); + + if (!has_wild) { + strcat(directory,"/"); + strcat(directory,mask); + if (can_delete(directory,cnum,dirtype) && !sys_unlink(directory)) count++; + if (!count) exists = file_exist(directory,NULL); + } else { + void *dirptr = NULL; + char *dname; + + if (check_name(directory,cnum)) + dirptr = OpenDir(directory); + + if (dirptr) + { + error = ERRbadfile; + + if (strequal(mask,"????????.???")) + strcpy(mask,"*"); + + while ((dname = ReadDirName(dirptr))) + { + pstring fname; + strcpy(fname,dname); + + if(!mask_match(fname, mask, case_sensitive, False)) continue; + + error = ERRnoaccess; + sprintf(fname,"%s/%s",directory,dname); + if (!can_delete(fname,cnum,dirtype)) continue; + if (!sys_unlink(fname)) count++; + DEBUG(3,("reply_unlink : doing unlink on %s\n",fname)); + } + CloseDir(dirptr); + } + } + + if (count == 0) { + if (exists) + return(ERROR(ERRDOS,error)); + else + return(UNIXERROR(ERRDOS,error)); + } + + outsize = set_message(outbuf,0,0,True); + + return(outsize); +} + + +/**************************************************************************** + reply to a readbraw (core+ protocol) +****************************************************************************/ +int reply_readbraw(char *inbuf, char *outbuf) +{ + int cnum,maxcount,mincount,fnum; + int nread = 0; + int startpos; + char *header = outbuf; + int ret=0; + int fd; + char *fname; + + cnum = SVAL(inbuf,smb_tid); + fnum = GETFNUM(inbuf,smb_vwv0); + + startpos = IVAL(inbuf,smb_vwv1); + maxcount = SVAL(inbuf,smb_vwv3); + mincount = SVAL(inbuf,smb_vwv4); + + /* ensure we don't overrun the packet size */ + maxcount = MIN(65535,maxcount); + maxcount = MAX(mincount,maxcount); + + if (!FNUM_OK(fnum,cnum) || !Files[fnum].can_read) + { + DEBUG(3,("fnum %d not open in readbraw - cache prime?\n",fnum)); + _smb_setlen(header,0); + transfer_file(0,Client,0,header,4,0); + return(-1); + } + else + { + fd = Files[fnum].fd; + fname = Files[fnum].name; + } + + + if (!is_locked(fnum,cnum,maxcount,startpos)) + { + int size = Files[fnum].size; + int sizeneeded = startpos + maxcount; + + if (size < sizeneeded) { + struct stat st; + if (fstat(Files[fnum].fd,&st) == 0) + size = st.st_size; + if (!Files[fnum].can_write) + Files[fnum].size = size; + } + + nread = MIN(maxcount,size - startpos); + } + + if (nread < mincount) + nread = 0; + + DEBUG(3,("%s readbraw fnum=%d cnum=%d start=%d max=%d min=%d nread=%d\n", + timestring(), + fnum,cnum,startpos, + maxcount,mincount,nread)); + +#if UNSAFE_READRAW + { + int predict=0; + _smb_setlen(header,nread); + + if (!Files[fnum].can_write) + predict = read_predict(fd,startpos,header+4,NULL,nread); + + if ((nread-predict) > 0) + seek_file(fnum,startpos + predict); + + ret = transfer_file(fd,Client,nread-predict,header,4+predict, + startpos+predict); + } + + if (ret != nread+4) + DEBUG(0,("ERROR: file read failure on %s at %d for %d bytes (%d)\n", + fname,startpos,nread,ret)); + +#else + ret = read_file(fnum,header+4,startpos,nread,nread,-1,False); + if (ret < mincount) ret = 0; + + _smb_setlen(header,ret); + transfer_file(0,Client,0,header,4+ret,0); +#endif + + DEBUG(5,("readbraw finished\n")); + return -1; +} + + +/**************************************************************************** + reply to a lockread (core+ protocol) +****************************************************************************/ +int reply_lockread(char *inbuf,char *outbuf) +{ + int cnum,fnum; + int nread = -1; + char *data; + int outsize = 0; + uint32 startpos, numtoread; + int eclass; + uint32 ecode; + + cnum = SVAL(inbuf,smb_tid); + fnum = GETFNUM(inbuf,smb_vwv0); + + CHECK_FNUM(fnum,cnum); + CHECK_READ(fnum); + CHECK_ERROR(fnum); + + numtoread = SVAL(inbuf,smb_vwv1); + startpos = IVAL(inbuf,smb_vwv2); + + outsize = set_message(outbuf,5,3,True); + numtoread = MIN(BUFFER_SIZE-outsize,numtoread); + data = smb_buf(outbuf) + 3; + + if(!do_lock( fnum, cnum, numtoread, startpos, &eclass, &ecode)) + return (ERROR(eclass,ecode)); + + nread = read_file(fnum,data,startpos,numtoread,numtoread,-1,False); + + if (nread < 0) + return(UNIXERROR(ERRDOS,ERRnoaccess)); + + outsize += nread; + SSVAL(outbuf,smb_vwv0,nread); + SSVAL(outbuf,smb_vwv5,nread+3); + SSVAL(smb_buf(outbuf),1,nread); + + DEBUG(3,("%s lockread fnum=%d cnum=%d num=%d nread=%d\n",timestring(),fnum,cnum,numtoread,nread)); + + return(outsize); +} + + +/**************************************************************************** + reply to a read +****************************************************************************/ +int reply_read(char *inbuf,char *outbuf) +{ + int cnum,numtoread,fnum; + int nread = 0; + char *data; + int startpos; + int outsize = 0; + + cnum = SVAL(inbuf,smb_tid); + fnum = GETFNUM(inbuf,smb_vwv0); + + CHECK_FNUM(fnum,cnum); + CHECK_READ(fnum); + CHECK_ERROR(fnum); + + numtoread = SVAL(inbuf,smb_vwv1); + startpos = IVAL(inbuf,smb_vwv2); + + outsize = set_message(outbuf,5,3,True); + numtoread = MIN(BUFFER_SIZE-outsize,numtoread); + data = smb_buf(outbuf) + 3; + + if (is_locked(fnum,cnum,numtoread,startpos)) + return(ERROR(ERRDOS,ERRlock)); + + if (numtoread > 0) + nread = read_file(fnum,data,startpos,numtoread,numtoread,-1,False); + + if (nread < 0) + return(UNIXERROR(ERRDOS,ERRnoaccess)); + + outsize += nread; + SSVAL(outbuf,smb_vwv0,nread); + SSVAL(outbuf,smb_vwv5,nread+3); + CVAL(smb_buf(outbuf),0) = 1; + SSVAL(smb_buf(outbuf),1,nread); + + DEBUG(3,("%s read fnum=%d cnum=%d num=%d nread=%d\n",timestring(),fnum,cnum,numtoread,nread)); + + return(outsize); +} + + +/**************************************************************************** + reply to a read and X +****************************************************************************/ +int reply_read_and_X(char *inbuf,char *outbuf,int length,int bufsize) +{ + int smb_com2 = CVAL(inbuf,smb_vwv0); + int smb_off2 = SVAL(inbuf,smb_vwv1); + int fnum = GETFNUM(inbuf,smb_vwv2); + uint32 smb_offs = IVAL(inbuf,smb_vwv3); + int smb_maxcnt = SVAL(inbuf,smb_vwv5); + int smb_mincnt = SVAL(inbuf,smb_vwv6); + int cnum; + int nread = -1; + char *data; + int outsize = 0; + BOOL ok = False; + + cnum = SVAL(inbuf,smb_tid); + + CHECK_FNUM(fnum,cnum); + CHECK_READ(fnum); + CHECK_ERROR(fnum); + + outsize = set_message(outbuf,12,0,True); + data = smb_buf(outbuf); + + if (is_locked(fnum,cnum,smb_maxcnt,smb_offs)) + return(ERROR(ERRDOS,ERRlock)); + nread = read_file(fnum,data,smb_offs,smb_maxcnt,smb_maxcnt,-1,False); + ok = True; + + if (nread < 0) + return(UNIXERROR(ERRDOS,ERRnoaccess)); + + outsize += nread; + CVAL(outbuf,smb_vwv0) = smb_com2; + SSVAL(outbuf,smb_vwv1,(outsize+chain_size)-4); + SSVAL(outbuf,smb_vwv5,nread); + SSVAL(outbuf,smb_vwv6,smb_offset(data,outbuf) + chain_size); + SSVAL(smb_buf(outbuf),-2,nread); + + DEBUG(3,("%s readX fnum=%d cnum=%d min=%d max=%d nread=%d com2=%d off2=%d\n", + timestring(),fnum,cnum, + smb_mincnt,smb_maxcnt,nread,smb_com2,smb_off2)); + + chain_fnum = fnum; + + if (smb_com2 != 0xFF) + outsize += chain_reply(smb_com2,inbuf,inbuf+smb_off2+4, + outbuf,outbuf+outsize, + length,bufsize); + + chain_fnum = -1; + + return(outsize); +} + + +/**************************************************************************** + reply to a writebraw (core+ or LANMAN1.0 protocol) +****************************************************************************/ +int reply_writebraw(char *inbuf,char *outbuf) +{ + int nwritten=0; + int total_written=0; + int numtowrite=0; + int cnum,fnum; + int outsize = 0; + long startpos; + char *data=NULL; + BOOL write_through; + int tcount; + + cnum = SVAL(inbuf,smb_tid); + fnum = GETFNUM(inbuf,smb_vwv0); + + CHECK_FNUM(fnum,cnum); + CHECK_WRITE(fnum); + CHECK_ERROR(fnum); + + tcount = IVAL(inbuf,smb_vwv1); + startpos = IVAL(inbuf,smb_vwv3); + write_through = BITSETW(inbuf+smb_vwv7,0); + + /* We have to deal with slightly different formats depending + on whether we are using the core+ or lanman1.0 protocol */ + if(Protocol <= PROTOCOL_COREPLUS) { + numtowrite = SVAL(smb_buf(inbuf),-2); + data = smb_buf(inbuf); + } else { + numtowrite = SVAL(inbuf,smb_vwv10); + data = smb_base(inbuf) + SVAL(inbuf, smb_vwv11); + } + + /* force the error type */ + CVAL(inbuf,smb_com) = SMBwritec; + CVAL(outbuf,smb_com) = SMBwritec; + + if (is_locked(fnum,cnum,tcount,startpos)) + return(ERROR(ERRDOS,ERRlock)); + + if (seek_file(fnum,startpos) != startpos) + DEBUG(0,("couldn't seek to %d in writebraw\n",startpos)); + + if (numtowrite>0) + nwritten = write_file(fnum,data,numtowrite); + + DEBUG(3,("%s writebraw1 fnum=%d cnum=%d start=%d num=%d wrote=%d sync=%d\n", + timestring(),fnum,cnum,startpos,numtowrite,nwritten,write_through)); + + if (nwritten < numtowrite) + return(UNIXERROR(ERRHRD,ERRdiskfull)); + + total_written = nwritten; + + /* Return a message to the redirector to tell it + to send more bytes */ + CVAL(outbuf,smb_com) = SMBwritebraw; + SSVALS(outbuf,smb_vwv0,-1); + outsize = set_message(outbuf,Protocol>PROTOCOL_COREPLUS?1:0,0,True); + send_smb(Client,outbuf); + + /* Now read the raw data into the buffer and write it */ + if(read_smb_length(Client,inbuf,0) == -1) { + exit_server("secondary writebraw failed"); + } + + /* Even though this is not an smb message, smb_len + returns the generic length of an smb message */ + numtowrite = smb_len(inbuf); + + if (tcount > nwritten+numtowrite) { + DEBUG(3,("Client overestimated the write %d %d %d\n", + tcount,nwritten,numtowrite)); + } + + nwritten = transfer_file(Client,Files[fnum].fd,numtowrite,NULL,0, + startpos+nwritten); + total_written += nwritten; + + /* Set up outbuf to return the correct return */ + outsize = set_message(outbuf,1,0,True); + CVAL(outbuf,smb_com) = SMBwritec; + SSVAL(outbuf,smb_vwv0,total_written); + + if (nwritten < numtowrite) { + CVAL(outbuf,smb_rcls) = ERRHRD; + SSVAL(outbuf,smb_err,ERRdiskfull); + } + + if (lp_syncalways(SNUM(cnum)) || write_through) + sync_file(fnum); + + DEBUG(3,("%s writebraw2 fnum=%d cnum=%d start=%d num=%d wrote=%d\n", + timestring(),fnum,cnum,startpos,numtowrite,total_written)); + + /* we won't return a status if write through is not selected - this + follows what WfWg does */ + if (!write_through && total_written==tcount) + return(-1); + + return(outsize); +} + + +/**************************************************************************** + reply to a writeunlock (core+) +****************************************************************************/ +int reply_writeunlock(char *inbuf,char *outbuf) +{ + int cnum,fnum; + int nwritten = -1; + int outsize = 0; + char *data; + uint32 numtowrite,startpos; + int eclass; + uint32 ecode; + + cnum = SVAL(inbuf,smb_tid); + fnum = GETFNUM(inbuf,smb_vwv0); + + CHECK_FNUM(fnum,cnum); + CHECK_WRITE(fnum); + CHECK_ERROR(fnum); + + numtowrite = SVAL(inbuf,smb_vwv1); + startpos = IVAL(inbuf,smb_vwv2); + data = smb_buf(inbuf) + 3; + + if (is_locked(fnum,cnum,numtowrite,startpos)) + return(ERROR(ERRDOS,ERRlock)); + + seek_file(fnum,startpos); + + /* The special X/Open SMB protocol handling of + zero length writes is *NOT* done for + this call */ + if(numtowrite == 0) + nwritten = 0; + else + nwritten = write_file(fnum,data,numtowrite); + + if (lp_syncalways(SNUM(cnum))) + sync_file(fnum); + + if(((nwritten == 0) && (numtowrite != 0))||(nwritten < 0)) + return(UNIXERROR(ERRDOS,ERRnoaccess)); + + if(!do_unlock(fnum, cnum, numtowrite, startpos, &eclass, &ecode)) + return(ERROR(eclass,ecode)); + + outsize = set_message(outbuf,1,0,True); + + SSVAL(outbuf,smb_vwv0,nwritten); + + DEBUG(3,("%s writeunlock fnum=%d cnum=%d num=%d wrote=%d\n", + timestring(),fnum,cnum,numtowrite,nwritten)); + + return(outsize); +} + + +/**************************************************************************** + reply to a write +****************************************************************************/ +int reply_write(char *inbuf,char *outbuf,int dum1,int dum2) +{ + int cnum,numtowrite,fnum; + int nwritten = -1; + int outsize = 0; + int startpos; + char *data; + + dum1 = dum2 = 0; + + + cnum = SVAL(inbuf,smb_tid); + fnum = GETFNUM(inbuf,smb_vwv0); + + CHECK_FNUM(fnum,cnum); + CHECK_WRITE(fnum); + CHECK_ERROR(fnum); + + numtowrite = SVAL(inbuf,smb_vwv1); + startpos = IVAL(inbuf,smb_vwv2); + data = smb_buf(inbuf) + 3; + + if (is_locked(fnum,cnum,numtowrite,startpos)) + return(ERROR(ERRDOS,ERRlock)); + + seek_file(fnum,startpos); + + /* X/Open SMB protocol says that if smb_vwv1 is + zero then the file size should be extended or + truncated to the size given in smb_vwv[2-3] */ + if(numtowrite == 0) + nwritten = set_filelen(Files[fnum].fd, startpos); + else + nwritten = write_file(fnum,data,numtowrite); + + if (lp_syncalways(SNUM(cnum))) + sync_file(fnum); + + if(((nwritten == 0) && (numtowrite != 0))||(nwritten < 0)) + return(UNIXERROR(ERRDOS,ERRnoaccess)); + + outsize = set_message(outbuf,1,0,True); + + SSVAL(outbuf,smb_vwv0,nwritten); + + if (nwritten < numtowrite) { + CVAL(outbuf,smb_rcls) = ERRHRD; + SSVAL(outbuf,smb_err,ERRdiskfull); + } + + DEBUG(3,("%s write fnum=%d cnum=%d num=%d wrote=%d\n",timestring(),fnum,cnum,numtowrite,nwritten)); + + return(outsize); +} + + +/**************************************************************************** + reply to a write and X +****************************************************************************/ +int reply_write_and_X(char *inbuf,char *outbuf,int length,int bufsize) +{ + int smb_com2 = CVAL(inbuf,smb_vwv0); + int smb_off2 = SVAL(inbuf,smb_vwv1); + int fnum = GETFNUM(inbuf,smb_vwv2); + uint32 smb_offs = IVAL(inbuf,smb_vwv3); + int smb_dsize = SVAL(inbuf,smb_vwv10); + int smb_doff = SVAL(inbuf,smb_vwv11); + BOOL write_through = BITSETW(inbuf+smb_vwv7,0); + int cnum; + int nwritten = -1; + int outsize = 0; + char *data; + + cnum = SVAL(inbuf,smb_tid); + + CHECK_FNUM(fnum,cnum); + CHECK_WRITE(fnum); + CHECK_ERROR(fnum); + + data = smb_base(inbuf) + smb_doff; + + if (is_locked(fnum,cnum,smb_dsize,smb_offs)) + return(ERROR(ERRDOS,ERRlock)); + + seek_file(fnum,smb_offs); + + /* X/Open SMB protocol says that, unlike SMBwrite + if the length is zero then NO truncation is + done, just a write of zero. To truncate a file, + use SMBwrite. */ + if(smb_dsize == 0) + nwritten = 0; + else + nwritten = write_file(fnum,data,smb_dsize); + + if(((nwritten == 0) && (smb_dsize != 0))||(nwritten < 0)) + return(UNIXERROR(ERRDOS,ERRnoaccess)); + + outsize = set_message(outbuf,6,0,True); + + CVAL(outbuf,smb_vwv0) = smb_com2; + SSVAL(outbuf,smb_vwv1,(outsize+chain_size)-4); + SSVAL(outbuf,smb_vwv2,nwritten); + + if (nwritten < smb_dsize) { + CVAL(outbuf,smb_rcls) = ERRHRD; + SSVAL(outbuf,smb_err,ERRdiskfull); + } + + DEBUG(3,("%s writeX fnum=%d cnum=%d num=%d wrote=%d\n",timestring(),fnum,cnum,smb_dsize,nwritten)); + + chain_fnum = fnum; + + if (lp_syncalways(SNUM(cnum)) || write_through) + sync_file(fnum); + + if (smb_com2 != 0xFF) + outsize += chain_reply(smb_com2,inbuf,inbuf+smb_off2+4, + outbuf,outbuf+outsize, + length,bufsize); + + chain_fnum = -1; + + return(outsize); +} + + +/**************************************************************************** + reply to a lseek +****************************************************************************/ +int reply_lseek(char *inbuf,char *outbuf) +{ + int cnum,fnum; + uint32 startpos; + int32 res= -1; + int mode,umode; + int outsize = 0; + + cnum = SVAL(inbuf,smb_tid); + fnum = GETFNUM(inbuf,smb_vwv0); + + CHECK_FNUM(fnum,cnum); + CHECK_ERROR(fnum); + + mode = SVAL(inbuf,smb_vwv1) & 3; + startpos = IVAL(inbuf,smb_vwv2); + + switch (mode & 3) + { + case 0: umode = SEEK_SET; break; + case 1: umode = SEEK_CUR; break; + case 2: umode = SEEK_END; break; + default: + umode = SEEK_SET; break; + } + + res = lseek(Files[fnum].fd,startpos,umode); + Files[fnum].pos = res; + + outsize = set_message(outbuf,2,0,True); + SIVALS(outbuf,smb_vwv0,res); + + DEBUG(3,("%s lseek fnum=%d cnum=%d ofs=%d mode=%d\n",timestring(),fnum,cnum,startpos,mode)); + + return(outsize); +} + + +/**************************************************************************** + reply to a flush +****************************************************************************/ +int reply_flush(char *inbuf,char *outbuf) +{ + int cnum, fnum; + int outsize = set_message(outbuf,0,0,True); + + cnum = SVAL(inbuf,smb_tid); + fnum = GETFNUM(inbuf,smb_vwv0); + + if (fnum != 0xFFFF) { + CHECK_FNUM(fnum,cnum); + CHECK_ERROR(fnum); + } + + if (fnum == 0xFFFF) + { + int i; + for (i=0;i<MAX_OPEN_FILES;i++) + if (OPEN_FNUM(i)) + sync_file(i); + } + else + sync_file(fnum); + + DEBUG(3,("%s flush fnum=%d\n",timestring(),fnum)); + return(outsize); +} + + +/**************************************************************************** + reply to a exit +****************************************************************************/ +int reply_exit(char *inbuf,char *outbuf) +{ + int outsize = set_message(outbuf,0,0,True); + DEBUG(3,("%s exit\n",timestring())); + + return(outsize); +} + + +/**************************************************************************** + reply to a close +****************************************************************************/ +int reply_close(char *inbuf,char *outbuf) +{ + int fnum,cnum; + int outsize = 0; + time_t mtime; + int32 eclass = 0, err = 0; + + outsize = set_message(outbuf,0,0,True); + + cnum = SVAL(inbuf,smb_tid); + + fnum = GETFNUM(inbuf,smb_vwv0); + CHECK_FNUM(fnum,cnum); + + if(HAS_CACHED_ERROR(fnum)) { + eclass = Files[fnum].wbmpx_ptr->wr_errclass; + err = Files[fnum].wbmpx_ptr->wr_error; + } + + mtime = make_unix_date3(inbuf+smb_vwv1); + + close_file(fnum); + + /* try and set the date */ + set_filetime(Files[fnum].name,mtime); + + /* We have a cached error */ + if(eclass || err) + return(ERROR(eclass,err)); + + DEBUG(3,("%s close fd=%d fnum=%d cnum=%d (numopen=%d)\n", + timestring(),Files[fnum].fd,fnum,cnum, + Connections[cnum].num_files_open)); + + return(outsize); +} + + +/**************************************************************************** + reply to a writeclose (Core+ protocol) +****************************************************************************/ +int reply_writeclose(char *inbuf,char *outbuf) +{ + int cnum,numtowrite,fnum; + int nwritten = -1; + int outsize = 0; + int startpos; + char *data; + time_t mtime; + + cnum = SVAL(inbuf,smb_tid); + fnum = GETFNUM(inbuf,smb_vwv0); + + CHECK_FNUM(fnum,cnum); + CHECK_WRITE(fnum); + CHECK_ERROR(fnum); + + numtowrite = SVAL(inbuf,smb_vwv1); + startpos = IVAL(inbuf,smb_vwv2); + mtime = make_unix_date3(inbuf+smb_vwv4); + data = smb_buf(inbuf) + 1; + + if (is_locked(fnum,cnum,numtowrite,startpos)) + return(ERROR(ERRDOS,ERRlock)); + + seek_file(fnum,startpos); + + nwritten = write_file(fnum,data,numtowrite); + + close_file(fnum); + + set_filetime(Files[fnum].name,mtime); + + DEBUG(3,("%s writeclose fnum=%d cnum=%d num=%d wrote=%d (numopen=%d)\n", + timestring(),fnum,cnum,numtowrite,nwritten, + Connections[cnum].num_files_open)); + + if (nwritten <= 0) + return(UNIXERROR(ERRDOS,ERRnoaccess)); + + outsize = set_message(outbuf,1,0,True); + + SSVAL(outbuf,smb_vwv0,nwritten); + return(outsize); +} + + +/**************************************************************************** + reply to a lock +****************************************************************************/ +int reply_lock(char *inbuf,char *outbuf) +{ + int fnum,cnum; + int outsize = set_message(outbuf,0,0,True); + uint32 count,offset; + int eclass; + uint32 ecode; + + cnum = SVAL(inbuf,smb_tid); + fnum = GETFNUM(inbuf,smb_vwv0); + + CHECK_FNUM(fnum,cnum); + CHECK_ERROR(fnum); + + count = IVAL(inbuf,smb_vwv1); + offset = IVAL(inbuf,smb_vwv3); + + DEBUG(3,("%s lock fd=%d fnum=%d cnum=%d ofs=%d cnt=%d\n",timestring(),Files[fnum].fd,fnum,cnum,offset,count)); + + if(!do_lock( fnum, cnum, count, offset, &eclass, &ecode)) + return (ERROR(eclass,ecode)); + + return(outsize); +} + + +/**************************************************************************** + reply to a unlock +****************************************************************************/ +int reply_unlock(char *inbuf,char *outbuf) +{ + int fnum,cnum; + int outsize = set_message(outbuf,0,0,True); + uint32 count,offset; + int eclass; + uint32 ecode; + + cnum = SVAL(inbuf,smb_tid); + fnum = GETFNUM(inbuf,smb_vwv0); + + CHECK_FNUM(fnum,cnum); + CHECK_ERROR(fnum); + + count = IVAL(inbuf,smb_vwv1); + offset = IVAL(inbuf,smb_vwv3); + + if(!do_unlock(fnum, cnum, count, offset, &eclass, &ecode)) + return (ERROR(eclass,ecode)); + + DEBUG(3,("%s unlock fd=%d fnum=%d cnum=%d ofs=%d cnt=%d\n",timestring(),Files[fnum].fd,fnum,cnum,offset,count)); + + return(outsize); +} + + +/**************************************************************************** + reply to a tdis +****************************************************************************/ +int reply_tdis(char *inbuf,char *outbuf) +{ + int cnum, uid; + int outsize = set_message(outbuf,0,0,True); + + cnum = SVAL(inbuf,smb_tid); + uid = SVAL(inbuf,smb_uid); + + Connections[cnum].used = False; + + close_cnum(cnum,uid); + + DEBUG(3,("%s tdis cnum=%d\n",timestring(),cnum)); + + return outsize; +} + + + +/**************************************************************************** + reply to a echo +****************************************************************************/ +int reply_echo(char *inbuf,char *outbuf) +{ + int cnum; + int smb_reverb = SVAL(inbuf,smb_vwv0); + int seq_num; + int data_len = smb_buflen(inbuf); + int outsize = set_message(outbuf,1,data_len,True); + + cnum = SVAL(inbuf,smb_tid); + + if (cnum != 0xFFFF && !OPEN_CNUM(cnum)) + { + DEBUG(4,("Invalid cnum in echo (%d)\n",cnum)); + return(ERROR(ERRSRV,ERRinvnid)); + } + + /* copy any incoming data back out */ + if (data_len > 0) + memcpy(smb_buf(outbuf),smb_buf(inbuf),data_len); + + if (smb_reverb > 100) + { + DEBUG(0,("large reverb (%d)?? Setting to 100\n",smb_reverb)); + smb_reverb = 100; + } + + for (seq_num =1 ; seq_num <= smb_reverb ; seq_num++) + { + SSVAL(outbuf,smb_vwv0,seq_num); + + smb_setlen(outbuf,outsize - 4); + + send_smb(Client,outbuf); + } + + DEBUG(3,("%s echo %d times cnum=%d\n",timestring(),smb_reverb,cnum)); + + return -1; +} + + +/**************************************************************************** + reply to a printopen +****************************************************************************/ +int reply_printopen(char *inbuf,char *outbuf) +{ + pstring fname; + pstring fname2; + int cnum; + int fnum = -1; + int outsize = 0; + + *fname = *fname2 = 0; + + cnum = SVAL(inbuf,smb_tid); + + if (!CAN_PRINT(cnum)) + return(ERROR(ERRDOS,ERRnoaccess)); + + { + pstring s; + char *p; + StrnCpy(s,smb_buf(inbuf)+1,sizeof(pstring)-1); + p = s; + while (*p) + { + if (!(isalnum(*p) || strchr("._-",*p))) + *p = 'X'; + p++; + } + + if (strlen(s) > 10) s[10] = 0; + + sprintf(fname,"%s.XXXXXX",s); + } + + fnum = find_free_file(); + if (fnum < 0) + return(ERROR(ERRSRV,ERRnofids)); + + strcpy(fname2,(char *)mktemp(fname)); + + if (!check_name(fname2,cnum)) + return(ERROR(ERRDOS,ERRnoaccess)); + + open_file(fnum,cnum,fname2,O_WRONLY | O_CREAT | O_TRUNC, + unix_mode(cnum,0)); + + if (!Files[fnum].open) + return(UNIXERROR(ERRDOS,ERRnoaccess)); + + /* force it to be a print file */ + Files[fnum].print_file = True; + + outsize = set_message(outbuf,1,0,True); + SSVAL(outbuf,smb_vwv0,fnum); + + DEBUG(3,("%s openprint %s fd=%d fnum=%d cnum=%d\n",timestring(),fname2,Files[fnum].fd,fnum,cnum)); + + return(outsize); +} + + +/**************************************************************************** + reply to a printclose +****************************************************************************/ +int reply_printclose(char *inbuf,char *outbuf) +{ + int fnum,cnum; + int outsize = set_message(outbuf,0,0,True); + + cnum = SVAL(inbuf,smb_tid); + fnum = GETFNUM(inbuf,smb_vwv0); + + CHECK_FNUM(fnum,cnum); + CHECK_ERROR(fnum); + + if (!CAN_PRINT(cnum)) + return(ERROR(ERRDOS,ERRnoaccess)); + + close_file(fnum); + + DEBUG(3,("%s printclose fd=%d fnum=%d cnum=%d\n",timestring(),Files[fnum].fd,fnum,cnum)); + + return(outsize); +} + + +/**************************************************************************** + reply to a printqueue +****************************************************************************/ +int reply_printqueue(char *inbuf,char *outbuf) +{ + int cnum, uid; + int outsize = set_message(outbuf,2,3,True); + int max_count = SVAL(inbuf,smb_vwv0); + int start_index = SVAL(inbuf,smb_vwv1); + + cnum = SVAL(inbuf,smb_tid); + uid = SVAL(inbuf,smb_uid); + +/* allow checking the queue for anyone */ +#if 0 + if (!CAN_PRINT(cnum)) + return(ERROR(ERRDOS,ERRnoaccess)); +#endif + + SSVAL(outbuf,smb_vwv0,0); + SSVAL(outbuf,smb_vwv1,0); + CVAL(smb_buf(outbuf),0) = 1; + SSVAL(smb_buf(outbuf),1,0); + + DEBUG(3,("%s printqueue cnum=%d start_index=%d max_count=%d\n", + timestring(),cnum,start_index,max_count)); + + if (!OPEN_CNUM(cnum) || !Connections[cnum].printer) + { + int i; + cnum = -1; + + for (i=0;i<MAX_CONNECTIONS;i++) + if (CAN_PRINT(i) && Connections[i].printer) + cnum = i; + + if (cnum == -1) + for (i=0;i<MAX_CONNECTIONS;i++) + if (OPEN_CNUM(i)) + cnum = i; + + if (!OPEN_CNUM(cnum)) + return(ERROR(ERRSRV,ERRinvnid)); + + DEBUG(5,("connection not open or not a printer, using cnum %d\n",cnum)); + } + + if (!become_user(cnum,uid)) + return(ERROR(ERRSRV,ERRinvnid)); + + { + print_queue_struct *queue = NULL; + char *p = smb_buf(outbuf) + 3; + int count = get_printqueue(SNUM(cnum),cnum,&queue,NULL); + int num_to_get = ABS(max_count); + int first = (max_count>0?start_index:start_index+max_count+1); + int i; + + if (first >= count) + num_to_get = 0; + else + num_to_get = MIN(num_to_get,count-first); + + + for (i=first;i<first+num_to_get;i++) + { + put_dos_date2(p,0,queue[i].time); + CVAL(p,4) = (queue[i].status==LPQ_PRINTING?2:3); + SSVAL(p,5,queue[i].job); + SIVAL(p,7,queue[i].size); + CVAL(p,11) = 0; + StrnCpy(p+12,queue[i].user,16); + p += 28; + } + + if (count > 0) + { + outsize = set_message(outbuf,2,28*count+3,False); + SSVAL(outbuf,smb_vwv0,count); + SSVAL(outbuf,smb_vwv1,(max_count>0?first+count:first-1)); + CVAL(smb_buf(outbuf),0) = 1; + SSVAL(smb_buf(outbuf),1,28*count); + } + + if (queue) free(queue); + + DEBUG(3,("%d entries returned in queue\n",count)); + } + + return(outsize); +} + + +/**************************************************************************** + reply to a printwrite +****************************************************************************/ +int reply_printwrite(char *inbuf,char *outbuf) +{ + int cnum,numtowrite,fnum; + int outsize = set_message(outbuf,0,0,True); + char *data; + + cnum = SVAL(inbuf,smb_tid); + + if (!CAN_PRINT(cnum)) + return(ERROR(ERRDOS,ERRnoaccess)); + + fnum = GETFNUM(inbuf,smb_vwv0); + + CHECK_FNUM(fnum,cnum); + CHECK_WRITE(fnum); + CHECK_ERROR(fnum); + + numtowrite = SVAL(smb_buf(inbuf),1); + data = smb_buf(inbuf) + 3; + + if (write_file(fnum,data,numtowrite) != numtowrite) + return(UNIXERROR(ERRDOS,ERRnoaccess)); + + DEBUG(3,("%s printwrite fnum=%d cnum=%d num=%d\n",timestring(),fnum,cnum,numtowrite)); + + return(outsize); +} + + +/**************************************************************************** + reply to a mkdir +****************************************************************************/ +int reply_mkdir(char *inbuf,char *outbuf) +{ + pstring directory; + int cnum; + int outsize,ret= -1; + + strcpy(directory,smb_buf(inbuf) + 1); + cnum = SVAL(inbuf,smb_tid); + unix_convert(directory,cnum); + + if (check_name(directory,cnum)) + ret = sys_mkdir(directory,unix_mode(cnum,aDIR)); + + if (ret < 0) + return(UNIXERROR(ERRDOS,ERRnoaccess)); + + outsize = set_message(outbuf,0,0,True); + + DEBUG(3,("%s mkdir %s cnum=%d ret=%d\n",timestring(),directory,cnum,ret)); + + return(outsize); +} + + +/**************************************************************************** + reply to a rmdir +****************************************************************************/ +int reply_rmdir(char *inbuf,char *outbuf) +{ + pstring directory; + int cnum; + int outsize = 0; + BOOL ok = False; + + cnum = SVAL(inbuf,smb_tid); + strcpy(directory,smb_buf(inbuf) + 1); + unix_convert(directory,cnum); + + if (check_name(directory,cnum)) + { + dptr_closepath(directory,SVAL(inbuf,smb_pid)); + ok = (sys_rmdir(directory) == 0); + if (!ok) + DEBUG(3,("couldn't remove directory %s : %s\n", + directory,strerror(errno))); + } + + if (!ok) + return(UNIXERROR(ERRDOS,ERRbadpath)); + + outsize = set_message(outbuf,0,0,True); + + DEBUG(3,("%s rmdir %s\n",timestring(),directory)); + + return(outsize); +} + + +/******************************************************************* +resolve wildcards in a filename rename +********************************************************************/ +static BOOL resolve_wildcards(char *name1,char *name2) +{ + fstring root1,root2; + fstring ext1,ext2; + char *p,*p2; + + name1 = strrchr(name1,'/'); + name2 = strrchr(name2,'/'); + + if (!name1 || !name2) return(False); + + strcpy(root1,name1); + strcpy(root2,name2); + p = strrchr(root1,'.'); + if (p) { + *p = 0; + strcpy(ext1,p+1); + } else { + strcpy(ext1,""); + } + p = strrchr(root2,'.'); + if (p) { + *p = 0; + strcpy(ext2,p+1); + } else { + strcpy(ext2,""); + } + + p = root1; + p2 = root2; + while (*p2) { + if (*p2 == '?') { + *p2 = *p; + p2++; + } else { + p2++; + } + if (*p) p++; + } + + p = ext1; + p2 = ext2; + while (*p2) { + if (*p2 == '?') { + *p2 = *p; + p2++; + } else { + p2++; + } + if (*p) p++; + } + + strcpy(name2,root2); + if (ext2[0]) { + strcat(name2,"."); + strcat(name2,ext2); + } + + return(True); +} + +/******************************************************************* +check if a user is allowed to rename a file +********************************************************************/ +static BOOL can_rename(char *fname,int cnum) +{ + struct stat sbuf; + + if (!CAN_WRITE(cnum)) return(False); + + if (sys_lstat(fname,&sbuf) != 0) return(False); + if (!check_file_sharing(cnum,fname)) return(False); + + return(True); +} + +/**************************************************************************** + reply to a mv +****************************************************************************/ +int reply_mv(char *inbuf,char *outbuf) +{ + int outsize = 0; + pstring name; + int cnum; + pstring directory; + pstring mask,newname; + char *p; + int count=0; + int error = ERRnoaccess; + BOOL has_wild; + BOOL exists=False; + + *directory = *mask = 0; + + cnum = SVAL(inbuf,smb_tid); + + strcpy(name,smb_buf(inbuf) + 1); + strcpy(newname,smb_buf(inbuf) + 3 + strlen(name)); + + DEBUG(3,("reply_mv : %s -> %s\n",name,newname)); + + unix_convert(name,cnum); + unix_convert(newname,cnum); + + p = strrchr(name,'/'); + if (!p) { + strcpy(directory,"./"); + strcpy(mask,name); + } else { + *p = 0; + strcpy(directory,name); + strcpy(mask,p+1); + } + + if (is_mangled(mask)) + check_mangled_stack(mask); + + has_wild = strchr(mask,'*') || strchr(mask,'?'); + + if (!has_wild) { + strcat(directory,"/"); + strcat(directory,mask); + if (resolve_wildcards(directory,newname) && + can_rename(directory,cnum) && + !file_exist(newname,NULL) && + !sys_rename(directory,newname)) count++; + if (!count) exists = file_exist(directory,NULL); + if (!count && exists && file_exist(newname,NULL)) { + exists = True; + error = 183; + } + } else { + void *dirptr = NULL; + char *dname; + pstring destname; + + if (check_name(directory,cnum)) + dirptr = OpenDir(directory); + + if (dirptr) + { + error = ERRbadfile; + + if (strequal(mask,"????????.???")) + strcpy(mask,"*"); + + while ((dname = ReadDirName(dirptr))) + { + pstring fname; + strcpy(fname,dname); + + if(!mask_match(fname, mask, case_sensitive, False)) continue; + + error = ERRnoaccess; + sprintf(fname,"%s/%s",directory,dname); + if (!can_rename(fname,cnum)) continue; + strcpy(destname,newname); + + if (!resolve_wildcards(fname,destname)) continue; + + if (file_exist(destname,NULL)) { + error = 183; + continue; + } + if (!sys_rename(fname,destname)) count++; + DEBUG(3,("reply_mv : doing rename on %s -> %s\n",fname,destname)); + } + CloseDir(dirptr); + } + } + + if (count == 0) { + if (exists) + return(ERROR(ERRDOS,error)); + else + return(UNIXERROR(ERRDOS,error)); + } + + outsize = set_message(outbuf,0,0,True); + + return(outsize); +} + +/******************************************************************* + copy a file as part of a reply_copy + ******************************************************************/ +static BOOL copy_file(char *src,char *dest1,int cnum,int ofun, + int count,BOOL target_is_directory) +{ + int Access,action; + struct stat st; + int ret=0; + int fnum1,fnum2; + pstring dest; + + strcpy(dest,dest1); + if (target_is_directory) { + char *p = strrchr(src,'/'); + if (p) + p++; + else + p = src; + strcat(dest,"/"); + strcat(dest,p); + } + + if (!file_exist(src,&st)) return(False); + + fnum1 = find_free_file(); + if (fnum1<0) return(False); + open_file_shared(fnum1,cnum,src,(DENY_NONE<<4), + 1,0,&Access,&action); + + if (!Files[fnum1].open) return(False); + + if (!target_is_directory && count) + ofun = 1; + + fnum2 = find_free_file(); + if (fnum2<0) { + close_file(fnum1); + return(False); + } + open_file_shared(fnum2,cnum,dest,(DENY_NONE<<4)|1, + ofun,st.st_mode,&Access,&action); + + if (!Files[fnum2].open) { + close_file(fnum1); + return(False); + } + + if ((ofun&3) == 1) { + lseek(Files[fnum2].fd,0,SEEK_END); + } + + if (st.st_size) + ret = transfer_file(Files[fnum1].fd,Files[fnum2].fd,st.st_size,NULL,0,0); + + close_file(fnum1); + close_file(fnum2); + + return(ret == st.st_size); +} + + + +/**************************************************************************** + reply to a file copy. + ****************************************************************************/ +int reply_copy(char *inbuf,char *outbuf) +{ + int outsize = 0; + pstring name; + int cnum; + pstring directory; + pstring mask,newname; + char *p; + int count=0; + int error = ERRnoaccess; + BOOL has_wild; + BOOL exists=False; + int tid2 = SVAL(inbuf,smb_vwv0); + int ofun = SVAL(inbuf,smb_vwv1); + int flags = SVAL(inbuf,smb_vwv2); + BOOL target_is_directory=False; + + *directory = *mask = 0; + + cnum = SVAL(inbuf,smb_tid); + + strcpy(name,smb_buf(inbuf)); + strcpy(newname,smb_buf(inbuf) + 1 + strlen(name)); + + DEBUG(3,("reply_copy : %s -> %s\n",name,newname)); + + if (tid2 != cnum) { + /* can't currently handle inter share copies XXXX */ + DEBUG(3,("Rejecting inter-share copy\n")); + return(ERROR(ERRSRV,ERRinvdevice)); + } + + unix_convert(name,cnum); + unix_convert(newname,cnum); + + target_is_directory = directory_exist(newname,NULL); + + if ((flags&1) && target_is_directory) { + return(ERROR(ERRDOS,ERRbadfile)); + } + + if ((flags&2) && !target_is_directory) { + return(ERROR(ERRDOS,ERRbadpath)); + } + + if ((flags&(1<<5)) && directory_exist(name,NULL)) { + /* wants a tree copy! XXXX */ + DEBUG(3,("Rejecting tree copy\n")); + return(ERROR(ERRSRV,ERRerror)); + } + + p = strrchr(name,'/'); + if (!p) { + strcpy(directory,"./"); + strcpy(mask,name); + } else { + *p = 0; + strcpy(directory,name); + strcpy(mask,p+1); + } + + if (is_mangled(mask)) + check_mangled_stack(mask); + + has_wild = strchr(mask,'*') || strchr(mask,'?'); + + if (!has_wild) { + strcat(directory,"/"); + strcat(directory,mask); + if (resolve_wildcards(directory,newname) && + copy_file(directory,newname,cnum,ofun, + count,target_is_directory)) count++; + if (!count) exists = file_exist(directory,NULL); + } else { + void *dirptr = NULL; + char *dname; + pstring destname; + + if (check_name(directory,cnum)) + dirptr = OpenDir(directory); + + if (dirptr) + { + error = ERRbadfile; + + if (strequal(mask,"????????.???")) + strcpy(mask,"*"); + + while ((dname = ReadDirName(dirptr))) + { + pstring fname; + strcpy(fname,dname); + + if(!mask_match(fname, mask, case_sensitive, False)) continue; + + error = ERRnoaccess; + sprintf(fname,"%s/%s",directory,dname); + strcpy(destname,newname); + if (resolve_wildcards(fname,destname) && + copy_file(directory,newname,cnum,ofun, + count,target_is_directory)) count++; + DEBUG(3,("reply_copy : doing copy on %s -> %s\n",fname,destname)); + } + CloseDir(dirptr); + } + } + + if (count == 0) { + if (exists) + return(ERROR(ERRDOS,error)); + else + return(UNIXERROR(ERRDOS,error)); + } + + outsize = set_message(outbuf,1,0,True); + SSVAL(outbuf,smb_vwv0,count); + + return(outsize); +} + + + +/**************************************************************************** + reply to a setdir +****************************************************************************/ +int reply_setdir(char *inbuf,char *outbuf) +{ + int cnum,snum; + int outsize = 0; + BOOL ok = False; + pstring newdir; + + cnum = SVAL(inbuf,smb_tid); + + snum = Connections[cnum].service; + if (!CAN_SETDIR(snum)) + return(ERROR(ERRDOS,ERRnoaccess)); + + strcpy(newdir,smb_buf(inbuf) + 1); + strlower(newdir); + + if (strlen(newdir) == 0) + ok = True; + else + { + ok = directory_exist(newdir,NULL); + if (ok) + string_set(&Connections[cnum].connectpath,newdir); + } + + if (!ok) + return(ERROR(ERRDOS,ERRbadpath)); + + outsize = set_message(outbuf,0,0,True); + CVAL(outbuf,smb_reh) = CVAL(inbuf,smb_reh); + + DEBUG(3,("%s setdir %s cnum=%d\n",timestring(),newdir,cnum)); + + return(outsize); +} + + +/**************************************************************************** + reply to a lockingX request +****************************************************************************/ +int reply_lockingX(char *inbuf,char *outbuf,int length,int bufsize) +{ + int smb_com2 = CVAL(inbuf,smb_vwv0); + int smb_off2 = SVAL(inbuf,smb_vwv1); + int fnum = GETFNUM(inbuf,smb_vwv2); + uint16 locktype = SVAL(inbuf,smb_vwv3); + uint16 num_ulocks = SVAL(inbuf,smb_vwv6); + uint16 num_locks = SVAL(inbuf,smb_vwv7); + uint32 count, offset; + + int cnum; + int i; + char *data; + uint32 ecode=0, dummy2; + int outsize, eclass=0, dummy1; + + cnum = SVAL(inbuf,smb_tid); + + CHECK_FNUM(fnum,cnum); + CHECK_ERROR(fnum); + + data = smb_buf(inbuf); + /* Data now points at the beginning of the list + of smb_unlkrng structs */ + for(i = 0; i < (int)num_ulocks; i++) { + count = IVAL(data,SMB_LKLEN_OFFSET(i)); + offset = IVAL(data,SMB_LKOFF_OFFSET(i)); + if(!do_unlock(fnum,cnum,count,offset,&eclass, &ecode)) + return ERROR(eclass,ecode); + } + + /* Now do any requested locks */ + data += 10*num_ulocks; + /* Data now points at the beginning of the list + of smb_lkrng structs */ + for(i = 0; i < (int)num_locks; i++) { + count = IVAL(data,SMB_LKLEN_OFFSET(i)); + offset = IVAL(data,SMB_LKOFF_OFFSET(i)); + if(!do_lock(fnum,cnum,count,offset, &eclass, &ecode)) + break; + } + + /* If any of the above locks failed, then we must unlock + all of the previous locks (X/Open spec). */ + if(i != num_locks && num_locks != 0) { + for(; i >= 0; i--) { + count = IVAL(data,SMB_LKLEN_OFFSET(i)); + offset = IVAL(data,SMB_LKOFF_OFFSET(i)); + do_unlock(fnum,cnum,count,offset,&dummy1,&dummy2); + } + return ERROR(eclass,ecode); + } + + outsize = set_message(outbuf,2,0,True); + + CVAL(outbuf,smb_vwv0) = smb_com2; + SSVAL(outbuf,smb_vwv1,(outsize+chain_size)-4); + + DEBUG(3,("%s lockingX fnum=%d cnum=%d type=%d num_locks=%d num_ulocks=%d\n", + timestring(),fnum,cnum,locktype,num_locks,num_ulocks)); + + chain_fnum = fnum; + + if (smb_com2 != 0xFF) + outsize += chain_reply(smb_com2,inbuf,inbuf+smb_off2+4, + outbuf,outbuf+outsize, + length,bufsize); + + chain_fnum = -1; + + return(outsize); +} + + +/**************************************************************************** + reply to a SMBreadbmpx (read block multiplex) request +****************************************************************************/ +int reply_readbmpx(char *inbuf,char *outbuf,int length,int bufsize) +{ + int cnum,fnum; + int nread = -1; + int total_read; + char *data; + int32 startpos; + int outsize, mincount, maxcount; + int max_per_packet; + int tcount; + int pad; + + /* this function doesn't seem to work - disable by default */ + if (!lp_readbmpx()) + return(ERROR(ERRSRV,ERRuseSTD)); + + outsize = set_message(outbuf,8,0,True); + + cnum = SVAL(inbuf,smb_tid); + fnum = GETFNUM(inbuf,smb_vwv0); + + CHECK_FNUM(fnum,cnum); + CHECK_READ(fnum); + CHECK_ERROR(fnum); + + startpos = IVAL(inbuf,smb_vwv1); + maxcount = SVAL(inbuf,smb_vwv3); + mincount = SVAL(inbuf,smb_vwv4); + + data = smb_buf(outbuf); + pad = ((int)data)%4; + if (pad) pad = 4 - pad; + data += pad; + + max_per_packet = bufsize-(outsize+pad); + tcount = maxcount; + total_read = 0; + + if (is_locked(fnum,cnum,maxcount,startpos)) + return(ERROR(ERRDOS,ERRlock)); + + do + { + int N = MIN(max_per_packet,tcount-total_read); + + nread = read_file(fnum,data,startpos,N,N,-1,False); + + if (nread <= 0) nread = 0; + + if (nread < N) + tcount = total_read + nread; + + set_message(outbuf,8,nread,False); + SIVAL(outbuf,smb_vwv0,startpos); + SSVAL(outbuf,smb_vwv2,tcount); + SSVAL(outbuf,smb_vwv6,nread); + SSVAL(outbuf,smb_vwv7,smb_offset(data,outbuf)); + + send_smb(Client,outbuf); + + total_read += nread; + startpos += nread; + } + while (total_read < tcount); + + return(-1); +} + + +/**************************************************************************** + reply to a SMBwritebmpx (write block multiplex primary) request +****************************************************************************/ +int reply_writebmpx(char *inbuf,char *outbuf) +{ + int cnum,numtowrite,fnum; + int nwritten = -1; + int outsize = 0; + int32 startpos; + int tcount, write_through, smb_doff; + char *data; + + cnum = SVAL(inbuf,smb_tid); + fnum = GETFNUM(inbuf,smb_vwv0); + + CHECK_FNUM(fnum,cnum); + CHECK_WRITE(fnum); + CHECK_ERROR(fnum); + + tcount = SVAL(inbuf,smb_vwv1); + startpos = IVAL(inbuf,smb_vwv3); + write_through = BITSETW(inbuf+smb_vwv7,0); + numtowrite = SVAL(inbuf,smb_vwv10); + smb_doff = SVAL(inbuf,smb_vwv11); + + data = smb_base(inbuf) + smb_doff; + + /* If this fails we need to send an SMBwriteC response, + not an SMBwritebmpx - set this up now so we don't forget */ + CVAL(outbuf,smb_com) = SMBwritec; + + if (is_locked(fnum,cnum,tcount,startpos)) + return(ERROR(ERRDOS,ERRlock)); + + seek_file(fnum,startpos); + nwritten = write_file(fnum,data,numtowrite); + + if(lp_syncalways(SNUM(cnum)) || write_through) + sync_file(fnum); + + if(nwritten < numtowrite) + return(UNIXERROR(ERRHRD,ERRdiskfull)); + + /* If the maximum to be written to this file + is greater than what we just wrote then set + up a secondary struct to be attached to this + fd, we will use this to cache error messages etc. */ + if(tcount > nwritten) + { + write_bmpx_struct *wbms; + if(Files[fnum].wbmpx_ptr != NULL) + wbms = Files[fnum].wbmpx_ptr; /* Use an existing struct */ + else + wbms = (write_bmpx_struct *)malloc(sizeof(write_bmpx_struct)); + if(!wbms) + { + DEBUG(0,("Out of memory in reply_readmpx\n")); + return(ERROR(ERRSRV,ERRnoresource)); + } + wbms->wr_mode = write_through; + wbms->wr_discard = False; /* No errors yet */ + wbms->wr_total_written = nwritten; + wbms->wr_errclass = 0; + wbms->wr_error = 0; + Files[fnum].wbmpx_ptr = wbms; + } + + /* We are returning successfully, set the message type back to + SMBwritebmpx */ + CVAL(outbuf,smb_com) = SMBwriteBmpx; + + outsize = set_message(outbuf,1,0,True); + + SSVALS(outbuf,smb_vwv0,-1); /* We don't support smb_remaining */ + + DEBUG(3,("%s writebmpx fnum=%d cnum=%d num=%d wrote=%d\n", + timestring(),fnum,cnum,numtowrite,nwritten)); + + if (write_through && tcount==nwritten) { + /* we need to send both a primary and a secondary response */ + smb_setlen(outbuf,outsize - 4); + send_smb(Client,outbuf); + + /* now the secondary */ + outsize = set_message(outbuf,1,0,True); + CVAL(outbuf,smb_com) = SMBwritec; + SSVAL(outbuf,smb_vwv0,nwritten); + } + + return(outsize); +} + + +/**************************************************************************** + reply to a SMBwritebs (write block multiplex secondary) request +****************************************************************************/ +int reply_writebs(char *inbuf,char *outbuf) +{ + int cnum,numtowrite,fnum; + int nwritten = -1; + int outsize = 0; + int32 startpos; + int tcount, write_through, smb_doff; + char *data; + write_bmpx_struct *wbms; + BOOL send_response = False; + + cnum = SVAL(inbuf,smb_tid); + fnum = GETFNUM(inbuf,smb_vwv0); + CHECK_FNUM(fnum,cnum); + CHECK_WRITE(fnum); + + tcount = SVAL(inbuf,smb_vwv1); + startpos = IVAL(inbuf,smb_vwv2); + numtowrite = SVAL(inbuf,smb_vwv6); + smb_doff = SVAL(inbuf,smb_vwv7); + + data = smb_base(inbuf) + smb_doff; + + /* We need to send an SMBwriteC response, not an SMBwritebs */ + CVAL(outbuf,smb_com) = SMBwritec; + + /* This fd should have an auxiliary struct attached, + check that it does */ + wbms = Files[fnum].wbmpx_ptr; + if(!wbms) return(-1); + + /* If write through is set we can return errors, else we must + cache them */ + write_through = wbms->wr_mode; + + /* Check for an earlier error */ + if(wbms->wr_discard) + return -1; /* Just discard the packet */ + + seek_file(fnum,startpos); + nwritten = write_file(fnum,data,numtowrite); + + if(lp_syncalways(SNUM(cnum)) || write_through) + sync_file(fnum); + + if (nwritten < numtowrite) + { + if(write_through) { + /* We are returning an error - we can delete the aux struct */ + if (wbms) free((char *)wbms); + Files[fnum].wbmpx_ptr = NULL; + return(ERROR(ERRHRD,ERRdiskfull)); + } + return(CACHE_ERROR(wbms,ERRHRD,ERRdiskfull)); + } + + /* Increment the total written, if this matches tcount + we can discard the auxiliary struct (hurrah !) and return a writeC */ + wbms->wr_total_written += nwritten; + if(wbms->wr_total_written >= tcount) + { + if (write_through) { + outsize = set_message(outbuf,1,0,True); + SSVAL(outbuf,smb_vwv0,wbms->wr_total_written); + send_response = True; + } + + free((char *)wbms); + Files[fnum].wbmpx_ptr = NULL; + } + + if(send_response) + return(outsize); + + return(-1); +} + + +/**************************************************************************** + reply to a SMBsetattrE +****************************************************************************/ +int reply_setattrE(char *inbuf,char *outbuf) +{ + int cnum,fnum; + struct utimbuf unix_times; + int outsize = 0; + + outsize = set_message(outbuf,0,0,True); + + cnum = SVAL(inbuf,smb_tid); + fnum = GETFNUM(inbuf,smb_vwv0); + + CHECK_FNUM(fnum,cnum); + CHECK_ERROR(fnum); + + /* Convert the DOS times into unix times. Ignore create + time as UNIX can't set this. + */ + unix_times.actime = make_unix_date2(inbuf+smb_vwv3); + unix_times.modtime = make_unix_date2(inbuf+smb_vwv5); + + /* Set the date on this file */ + if(sys_utime(Files[fnum].name, &unix_times)) + return(ERROR(ERRDOS,ERRnoaccess)); + + DEBUG(3,("%s reply_setattrE fnum=%d cnum=%d\n",timestring(),fnum,cnum)); + + return(outsize); +} + + +/**************************************************************************** + reply to a SMBgetattrE +****************************************************************************/ +int reply_getattrE(char *inbuf,char *outbuf) +{ + int cnum,fnum; + struct stat sbuf; + int outsize = 0; + int mode; + + outsize = set_message(outbuf,11,0,True); + + cnum = SVAL(inbuf,smb_tid); + fnum = GETFNUM(inbuf,smb_vwv0); + + CHECK_FNUM(fnum,cnum); + CHECK_ERROR(fnum); + + /* Do an fstat on this file */ + if(fstat(Files[fnum].fd, &sbuf)) + return(UNIXERROR(ERRDOS,ERRnoaccess)); + + mode = dos_mode(cnum,Files[fnum].name,&sbuf); + + /* Convert the times into dos times. Set create + date to be last modify date as UNIX doesn't save + this */ + put_dos_date2(outbuf,smb_vwv0,sbuf.st_mtime); + put_dos_date2(outbuf,smb_vwv2,sbuf.st_atime); + put_dos_date2(outbuf,smb_vwv4,sbuf.st_mtime); + if (mode & aDIR) + { + SIVAL(outbuf,smb_vwv6,0); + SIVAL(outbuf,smb_vwv8,0); + } + else + { + SIVAL(outbuf,smb_vwv6,sbuf.st_size); + SIVAL(outbuf,smb_vwv8,ROUNDUP(sbuf.st_size,1024)); + } + SSVAL(outbuf,smb_vwv10, mode); + + DEBUG(3,("%s reply_getattrE fnum=%d cnum=%d\n",timestring(),fnum,cnum)); + + return(outsize); +} + + + + + |