/* 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 "trans2.h" /* look in server.c for some explanation of these variables */ extern int Protocol; extern int DEBUGLEVEL; 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 uid = SVAL(inbuf,smb_uid); int vuid; 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)); 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); strcpy(smb_buf(outbuf),devicename); return chain_reply(inbuf,outbuf,length,bufsize); } /**************************************************************************** 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 sess_uid; int gid; 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; BOOL computer_id=False; *smb_apasswd = 0; sess_uid = SVAL(inbuf,smb_uid); 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 NT_WORKAROUND if (passlen2 == 1) { /* apparently NT sometimes sets passlen2 to 1 when it means 0. This tries to work around that problem */ passlen2 = 0; } #endif 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 name ends in $ then I think it's asking about whether a */ /* computer with that name (minus the $) has access. For now */ /* say yes to everything ending in $. */ if (user[strlen(user) - 1] == '$') { computer_id = True; user[strlen(user) - 1] = '\0'; } 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 (!computer_id && 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) { set_message(outbuf,3,0,True); } else { char *p; 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,lp_workgroup()); p = skip_string(p,1); 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); } if (guest && !computer_id) 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); return chain_reply(inbuf,outbuf,length,bufsize); } /**************************************************************************** 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= 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 openmode = 0; 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; /* If it's an IPC, pass off the pipe handler. */ if (IS_IPC(cnum)) return reply_open_pipe_and_X(inbuf,outbuf,length,bufsize); /* 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)); } set_message(outbuf,15,0,True); 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; return chain_reply(inbuf,outbuf,length,bufsize); } /**************************************************************************** reply to a SMBulogoffX ****************************************************************************/ int reply_ulogoffX(char *inbuf,char *outbuf,int length,int bufsize) { int uid = SVAL(inbuf,smb_uid); invalidate_uid(uid); set_message(outbuf,2,0,True); DEBUG(3,("%s ulogoffX uid=%d\n",timestring(),uid)); return chain_reply(inbuf,outbuf,length,bufsize); } /**************************************************************************** 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 (!lp_delete_readonly(SNUM(cnum))) { 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); 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); 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); 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 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; BOOL ok = False; cnum = SVAL(inbuf,smb_tid); CHECK_FNUM(fnum,cnum); CHECK_READ(fnum); CHECK_ERROR(fnum); 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); ok = True; if (nread < 0) return(UNIXERROR(ERRDOS,ERRnoaccess)); SSVAL(outbuf,smb_vwv5,nread); SSVAL(outbuf,smb_vwv6,smb_offset(data,outbuf)); SSVAL(smb_buf(outbuf),-2,nread); DEBUG(3,("%s readX fnum=%d cnum=%d min=%d max=%d nread=%d\n", timestring(),fnum,cnum, smb_mincnt,smb_maxcnt,nread)); chain_fnum = fnum; return chain_reply(inbuf,outbuf,length,bufsize); } /**************************************************************************** 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,SMB_SECONDARY_WAIT) == -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 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; 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)); set_message(outbuf,6,0,True); 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); return chain_reply(inbuf,outbuf,length,bufsize); } /**************************************************************************** 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;iwr_errclass; err = Files[fnum].wbmpx_ptr->wr_error; } mtime = make_unix_date3(inbuf+smb_vwv1); /* try and set the date */ set_filetime(Files[fnum].name,mtime); close_file(fnum); /* 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); set_filetime(Files[fnum].name,mtime); close_file(fnum); 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); if (!OPEN_CNUM(cnum)) { DEBUG(4,("Invalid cnum in tdis (%d)\n",cnum)); return(ERROR(ERRSRV,ERRinvnid)); } 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;i0?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 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 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 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); } set_message(outbuf,2,0,True); 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; return chain_reply(inbuf,outbuf,length,bufsize); } /**************************************************************************** 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); 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); }