diff options
Diffstat (limited to 'source3/smbd/reply.c')
-rw-r--r-- | source3/smbd/reply.c | 5397 |
1 files changed, 3219 insertions, 2178 deletions
diff --git a/source3/smbd/reply.c b/source3/smbd/reply.c index b7b51775bb..fbb981781f 100644 --- a/source3/smbd/reply.c +++ b/source3/smbd/reply.c @@ -1,9 +1,9 @@ /* - Unix SMB/Netbios implementation. - Version 1.9. + Unix SMB/CIFS implementation. Main SMB reply routines - Copyright (C) Andrew Tridgell 1992-1995 - + Copyright (C) Andrew Tridgell 1992-1998 + Copyright (C) Andrew Bartlett 2001 + 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 @@ -25,222 +25,282 @@ #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 int max_send; +extern int max_recv; extern char magic_char; -extern connection_struct Connections[]; -extern files_struct Files[]; extern BOOL case_sensitive; -extern pstring sesssetup_user; -extern int Client; +extern BOOL case_preserve; +extern BOOL short_case_preserve; +extern pstring global_myname; +extern int global_oplock_break; +unsigned int smb_echo_count = 0; -/* 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)) +extern fstring remote_machine; +extern BOOL global_encrypted_passwords_negotiated; /**************************************************************************** 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; + int outsize = 4; + int msg_type = CVAL(inbuf,0); + int msg_flags = CVAL(inbuf,1); + pstring name1,name2; + + extern fstring local_machine; + int len; + char name_type = 0; + + *name1 = *name2 = 0; + + memset(outbuf,'\0',smb_size); - *name1 = *name2 = 0; + smb_setlen(outbuf,0); + + switch (msg_type) { + case 0x81: /* session request */ + SCVAL(outbuf,0,0x82); + SCVAL(outbuf,3,0); + if (name_len(inbuf+4) > 50 || + name_len(inbuf+4 + 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)); + + fstrcpy(remote_machine,name2); + remote_machine[15] = 0; + trim_string(remote_machine," "," "); + strlower(remote_machine); + alpha_strcpy(remote_machine,remote_machine,SAFE_NETBIOS_CHARS,sizeof(remote_machine)-1); + + fstrcpy(local_machine,name1); + len = strlen(local_machine); + if (len == 16) { + name_type = local_machine[15]; + local_machine[15] = 0; + } + trim_string(local_machine," "," "); + strlower(local_machine); + alpha_strcpy(local_machine,local_machine,SAFE_NETBIOS_CHARS,sizeof(local_machine)-1); + + DEBUG(2,("netbios connect: local=%s remote=%s\n", + local_machine, remote_machine )); + + if (name_type == 'R') { + /* We are being asked for a pathworks session --- + no thanks! */ + SCVAL(outbuf, 0,0x83); + break; + } - smb_setlen(outbuf,0); + /* only add the client's machine name to the list + of possibly valid usernames if we are operating + in share mode security */ + if (lp_security() == SEC_SHARE) { + add_session_user(remote_machine); + } - 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); + reload_services(True); + reopen_logs(); + + claim_connection(NULL,"",MAXSTATUS,True); + + break; + + case 0x89: /* session keepalive request + (some old clients produce this?) */ + SCVAL(outbuf,0,SMBkeepalive); + SCVAL(outbuf,3,0); + break; + + case 0x82: /* positive session response */ + case 0x83: /* negative session response */ + case 0x84: /* retarget session response */ + DEBUG(0,("Unexpected session response\n")); + break; + + case SMBkeepalive: /* session keepalive */ + default: + 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)); + + DEBUG(5,("init msg_type=0x%x msg_flags=0x%x\n", + msg_type, msg_flags)); + + return(outsize); } /**************************************************************************** - reply to a tcon + Reply to a tcon. ****************************************************************************/ -int reply_tcon(char *inbuf,char *outbuf) + +int reply_tcon(connection_struct *conn, + char *inbuf,char *outbuf, int dum_size, int dum_buffsize) { - pstring service; - pstring user; - pstring password; - pstring dev; - int connection_num; - int outsize = 0; - int uid = SVAL(inbuf,smb_uid); - int vuid; - int pwlen; + pstring service; + pstring password; + pstring dev; + int outsize = 0; + uint16 vuid = SVAL(inbuf,smb_uid); + int pwlen=0; + NTSTATUS nt_status; + char *p; + DATA_BLOB password_blob; + + START_PROFILE(SMBtcon); - *service = *user = *password = *dev = 0; + *service = *password = *dev = 0; - vuid = valid_uid(uid); - - parse_connect(inbuf,service,user,password,&pwlen,dev); + p = smb_buf(inbuf)+1; + p += srvstr_pull(inbuf, service, p, sizeof(service), -1, STR_TERMINATE) + 1; + pwlen = srvstr_pull(inbuf, password, p, sizeof(password), -1, STR_TERMINATE) + 1; + p += pwlen; + p += srvstr_pull(inbuf, dev, p, sizeof(dev), -1, STR_TERMINATE) + 1; + + p = strrchr_m(service,'\\'); + if (p) { + pstrcpy(service, p+1); + } + + password_blob = data_blob(password, pwlen+1); + + conn = make_connection(service,password_blob,dev,vuid,&nt_status); - connection_num = make_connection(service,user,password,pwlen,dev,vuid); + data_blob_clear_free(&password_blob); - if (connection_num < 0) - return(connection_error(inbuf,outbuf,connection_num)); + if (!conn) { + END_PROFILE(SMBtcon); + return ERROR_NT(nt_status); + } - outsize = set_message(outbuf,2,0,True); - SSVAL(outbuf,smb_vwv0,maxxmit); - SSVAL(outbuf,smb_vwv1,connection_num); - SSVAL(outbuf,smb_tid,connection_num); + outsize = set_message(outbuf,2,0,True); + SSVAL(outbuf,smb_vwv0,max_recv); + SSVAL(outbuf,smb_vwv1,conn->cnum); + SSVAL(outbuf,smb_tid,conn->cnum); - DEBUG(3,("%s tcon service=%s user=%s cnum=%d\n",timestring(),service,user,connection_num)); + DEBUG(3,("tcon service=%s cnum=%d\n", + service, conn->cnum)); - return(outsize); + END_PROFILE(SMBtcon); + return(outsize); } - /**************************************************************************** - reply to a tcon and X + Reply to a tcon and X. ****************************************************************************/ -int reply_tcon_and_X(char *inbuf,char *outbuf,int length,int bufsize) + +int reply_tcon_and_X(connection_struct *conn, 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); + fstring service; + DATA_BLOB password; + pstring devicename; + NTSTATUS nt_status; + uint16 vuid = SVAL(inbuf,smb_uid); + int passlen = SVAL(inbuf,smb_vwv3); + pstring path; + char *p, *q; + extern BOOL global_encrypted_passwords_negotiated; + START_PROFILE(SMBtconX); + + *service = *devicename = 0; + + /* we might have to close an old one */ + if ((SVAL(inbuf,smb_vwv2) & 0x1) && conn) { + close_cnum(conn,vuid); + } - *service = *user = *password = *devicename = 0; + if (passlen > MAX_PASS_LEN) { + return ERROR_DOS(ERRDOS,ERRbuftoosmall); + } + + if (global_encrypted_passwords_negotiated) { + password = data_blob(smb_buf(inbuf),passlen); + } else { + password = data_blob(smb_buf(inbuf),passlen+1); + /* Ensure correct termination */ + password.data[passlen]=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)); - } + p = smb_buf(inbuf) + passlen; + p += srvstr_pull(inbuf, path, p, sizeof(path), -1, STR_TERMINATE); + + /* + * the service name can be either: \\server\share + * or share directly like on the DELL PowerVault 705 + */ + if (*path=='\\') { + q = strchr_m(path+2,'\\'); + if (!q) { + END_PROFILE(SMBtconX); + return(ERROR_DOS(ERRDOS,ERRnosuchshare)); + } + fstrcpy(service,q+1); + } + else + fstrcpy(service,path); + + p += srvstr_pull(inbuf, devicename, p, sizeof(devicename), 6, STR_ASCII); - connection_num = make_connection(service,user,password,passlen,devicename,vuid); - - if (connection_num < 0) - return(connection_error(inbuf,outbuf,connection_num)); + DEBUG(4,("Got device type %s\n",devicename)); - 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); + conn = make_connection(service,password,devicename,vuid,&nt_status); + + data_blob_clear_free(&password); - CVAL(outbuf,smb_vwv0) = smb_com2; - SSVAL(outbuf,smb_vwv1,(chain_size + outsize)-4); + if (!conn) { + END_PROFILE(SMBtconX); + return ERROR_NT(nt_status); + } - strcpy(smb_buf(outbuf),devicename); + if (Protocol < PROTOCOL_NT1) { + set_message(outbuf,2,0,True); + p = smb_buf(outbuf); + p += srvstr_push(outbuf, p, devicename, -1, + STR_TERMINATE|STR_ASCII); + set_message_end(outbuf,p); + } else { + /* NT sets the fstype of IPC$ to the null string */ + char *fsname = IS_IPC(conn) ? "" : lp_fstype(SNUM(conn)); + + set_message(outbuf,3,0,True); + + p = smb_buf(outbuf); + p += srvstr_push(outbuf, p, devicename, -1, + STR_TERMINATE|STR_ASCII); + p += srvstr_push(outbuf, p, fsname, -1, + STR_TERMINATE); + + set_message_end(outbuf,p); + + /* what does setting this bit do? It is set by NT4 and + may affect the ability to autorun mounted cdroms */ + SSVAL(outbuf, smb_vwv2, SMB_SUPPORT_SEARCH_BITS| + (lp_csc_policy(SNUM(conn)) << 2)); + + init_dfsroot(conn, inbuf, outbuf); + } - if (smb_com2 != 0xFF) - outsize += chain_reply(smb_com2,inbuf,inbuf+smb_off2+4, - outbuf,outbuf+outsize, - length,bufsize); + + DEBUG(3,("tconX service=%s \n", + service)); + + /* set the incoming and outgoing tid to the just created one */ + SSVAL(inbuf,smb_tid,conn->cnum); + SSVAL(outbuf,smb_tid,conn->cnum); - return(outsize); + END_PROFILE(SMBtconX); + return chain_reply(inbuf,outbuf,length,bufsize); } @@ -249,245 +309,105 @@ int reply_tcon_and_X(char *inbuf,char *outbuf,int length,int bufsize) ****************************************************************************/ int reply_unknown(char *inbuf,char *outbuf) { - int cnum; - int type; - cnum = SVAL(inbuf,smb_tid); - type = CVAL(inbuf,smb_com); + int type; + 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)); + DEBUG(0,("unknown command type (%s): type=%d (0x%X)\n", + smb_fn_name(type), type, type)); - return(ERROR(ERRSRV,ERRunknownsmb)); + return(ERROR_DOS(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 reply_ioctl(connection_struct *conn, + char *inbuf,char *outbuf, int dum_size, int dum_buffsize) { - 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); + uint16 device = SVAL(inbuf,smb_vwv1); + uint16 function = SVAL(inbuf,smb_vwv2); + uint32 ioctl_code = (device << 16) + function; + int replysize, outsize; + char *p; + files_struct *fsp = file_fsp(inbuf,smb_vwv0); + START_PROFILE(SMBioctl); - add_session_user(user); + DEBUG(4, ("Received IOCTL (code 0x%x)\n", ioctl_code)); - - 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)) + switch (ioctl_code) { - 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; + case IOCTL_QUERY_JOB_INFO: + replysize = 32; + break; + default: + END_PROFILE(SMBioctl); + return(ERROR_DOS(ERRSRV,ERRnosupport)); } - } - - 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); + outsize = set_message(outbuf,8,replysize+1,True); + SSVAL(outbuf,smb_vwv1,replysize); /* Total data bytes returned */ + SSVAL(outbuf,smb_vwv5,replysize); /* Data bytes this buffer */ + SSVAL(outbuf,smb_vwv6,52); /* Offset to data */ + p = smb_buf(outbuf) + 1; /* Allow for alignment */ - 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); + switch (ioctl_code) + { + case IOCTL_QUERY_JOB_INFO: + SSVAL(p,0,fsp->print_jobid); /* Job number */ + srvstr_push(outbuf, p+2, global_myname, 15, STR_TERMINATE|STR_ASCII); + srvstr_push(outbuf, p+18, lp_servicename(SNUM(conn)), 13, STR_TERMINATE|STR_ASCII); + break; + } - return(outsize); + END_PROFILE(SMBioctl); + return outsize; } - /**************************************************************************** reply to a chkpth ****************************************************************************/ -int reply_chkpth(char *inbuf,char *outbuf) +int reply_chkpth(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize) { int outsize = 0; - int cnum,mode; + int mode; pstring name; BOOL ok = False; - - cnum = SVAL(inbuf,smb_tid); - - strcpy(name,smb_buf(inbuf) + 1); - unix_convert(name,cnum); + BOOL bad_path = False; + SMB_STRUCT_STAT sbuf; + START_PROFILE(SMBchkpth); + + srvstr_pull(inbuf, name, smb_buf(inbuf) + 1, sizeof(name), -1, STR_TERMINATE); + + RESOLVE_DFSPATH(name, conn, inbuf, outbuf); + + unix_convert(name,conn,0,&bad_path,&sbuf); mode = SVAL(inbuf,smb_vwv0); - if (check_name(name,cnum)) - ok = directory_exist(name,NULL); + if (check_name(name,conn)) { + if (VALID_STAT(sbuf) || vfs_stat(conn,name,&sbuf) == 0) + ok = S_ISDIR(sbuf.st_mode); + } + + if (!ok) { + /* We special case this - as when a Windows machine + is parsing a path is steps through the components + one at a time - if a component fails it expects + ERRbadpath, not ERRbadfile. + */ + if(errno == ENOENT) { + return ERROR_NT(NT_STATUS_OBJECT_PATH_NOT_FOUND); + } + + return(UNIXERROR(ERRDOS,ERRbadpath)); + } - 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)); - + + DEBUG(3,("chkpth %s mode=%d\n", name, mode)); + + END_PROFILE(SMBchkpth); return(outsize); } @@ -495,67 +415,75 @@ int reply_chkpth(char *inbuf,char *outbuf) /**************************************************************************** reply to a getatr ****************************************************************************/ -int reply_getatr(char *inbuf,char *outbuf) +int reply_getatr(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize) { pstring fname; - int cnum; int outsize = 0; - struct stat sbuf; + SMB_STRUCT_STAT sbuf; BOOL ok = False; int mode=0; - uint32 size=0; + SMB_OFF_T size=0; time_t mtime=0; - - cnum = SVAL(inbuf,smb_tid); + BOOL bad_path = False; + char *p; + START_PROFILE(SMBgetatr); - strcpy(fname,smb_buf(inbuf) + 1); - unix_convert(fname,cnum); + p = smb_buf(inbuf) + 1; + p += srvstr_pull(inbuf, fname, p, sizeof(fname), -1, STR_TERMINATE); + RESOLVE_DFSPATH(fname, conn, inbuf, outbuf); + /* 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; - } + { + mode = aHIDDEN | aDIR; + if (!CAN_WRITE(conn)) mode |= aRONLY; + size = 0; + mtime = 0; + ok = True; + } else - if (check_name(fname,cnum)) + { + unix_convert(fname,conn,0,&bad_path,&sbuf); + if (check_name(fname,conn)) + { + if (VALID_STAT(sbuf) || vfs_stat(conn,fname,&sbuf) == 0) { - 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))); + mode = dos_mode(conn,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) + { + set_bad_path_error(errno, bad_path); + END_PROFILE(SMBgetatr); 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(lp_dos_filetime_resolution(SNUM(conn)) ) + put_dos_date3(outbuf,smb_vwv1,mtime & ~1); + else + put_dos_date3(outbuf,smb_vwv1,mtime); + SIVAL(outbuf,smb_vwv3,(uint32)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 */ - } + if (Protocol >= PROTOCOL_NT1) + SSVAL(outbuf,smb_flg2,SVAL(outbuf, smb_flg2) | FLAGS2_IS_LONG_NAME); - DEBUG(3,("%s getatr name=%s mode=%d size=%d\n",timestring(),fname,mode,size)); + DEBUG( 3, ( "getatr name=%s mode=%d size=%d\n", fname, mode, (uint32)size ) ); + END_PROFILE(SMBgetatr); return(outsize); } @@ -563,37 +491,48 @@ int reply_getatr(char *inbuf,char *outbuf) /**************************************************************************** reply to a setatr ****************************************************************************/ -int reply_setatr(char *inbuf,char *outbuf) +int reply_setatr(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize) { 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); + SMB_STRUCT_STAT sbuf; + BOOL bad_path = False; + char *p; + + START_PROFILE(SMBsetatr); + + p = smb_buf(inbuf) + 1; + p += srvstr_pull(inbuf, fname, p, sizeof(fname), -1, STR_TERMINATE); + unix_convert(fname,conn,0,&bad_path,&sbuf); mode = SVAL(inbuf,smb_vwv0); mtime = make_unix_date3(inbuf+smb_vwv1); - if (directory_exist(fname,NULL)) + if (VALID_STAT_OF_DIR(sbuf)) mode |= aDIR; - if (check_name(fname,cnum)) - ok = (dos_chmod(cnum,fname,mode,NULL) == 0); + else + mode &= ~aDIR; + + if (check_name(fname,conn)) + ok = (file_chmod(conn,fname,mode,NULL) == 0); if (ok) - ok = set_filetime(fname,mtime); + ok = set_filetime(conn,fname,mtime); if (!ok) + { + set_bad_path_error(errno, bad_path); + END_PROFILE(SMBsetatr); return(UNIXERROR(ERRDOS,ERRnoaccess)); - + } + outsize = set_message(outbuf,0,0,True); - DEBUG(3,("%s setatr name=%s mode=%d\n",timestring(),fname,mode)); + DEBUG( 3, ( "setatr name=%s mode=%d\n", fname, mode ) ); + END_PROFILE(SMBsetatr); return(outsize); } @@ -601,15 +540,13 @@ int reply_setatr(char *inbuf,char *outbuf) /**************************************************************************** reply to a dskattr ****************************************************************************/ -int reply_dskattr(char *inbuf,char *outbuf) +int reply_dskattr(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize) { - int cnum; int outsize = 0; - int dfree,dsize,bsize; + SMB_BIG_UINT dfree,dsize,bsize; + START_PROFILE(SMBdskattr); - cnum = SVAL(inbuf,smb_tid); - - sys_disk_free(".",&bsize,&dfree,&dsize); + conn->vfs_ops.disk_free(conn,".",True,&bsize,&dfree,&dsize); outsize = set_message(outbuf,5,0,True); @@ -617,9 +554,10 @@ int reply_dskattr(char *inbuf,char *outbuf) 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)); - + + DEBUG(3,("dskattr dfree=%d\n", (unsigned int)dfree)); + + END_PROFILE(SMBdskattr); return(outsize); } @@ -628,15 +566,15 @@ int reply_dskattr(char *inbuf,char *outbuf) reply to a search Can be called from SMBsearch, SMBffirst or SMBfunique. ****************************************************************************/ -int reply_search(char *inbuf,char *outbuf) +int reply_search(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize) { pstring mask; pstring directory; pstring fname; - int size,mode; + SMB_OFF_T size; + int mode; time_t date; int dirtype; - int cnum; int outsize = 0; int numentries = 0; BOOL finished = False; @@ -645,12 +583,14 @@ int reply_search(char *inbuf,char *outbuf) char *p; BOOL ok = False; int status_len; - char *path; + pstring path; char status[21]; int dptr_num= -1; BOOL check_descend = False; BOOL expect_close = False; BOOL can_open = True; + BOOL bad_path = False; + START_PROFILE(SMBsearch); *mask = *directory = *fname = 0; @@ -658,197 +598,171 @@ int reply_search(char *inbuf,char *outbuf) 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)); - + p = smb_buf(inbuf) + 1; + p += srvstr_pull(inbuf, path, p, sizeof(path), -1, STR_TERMINATE); + p++; + status_len = SVAL(p, 0); + p += 2; /* 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); + { + SMB_STRUCT_STAT sbuf; + pstring dir2; - if (!check_name(directory,cnum)) - can_open = False; + pstrcpy(directory,path); + pstrcpy(dir2,path); + unix_convert(directory,conn,0,&bad_path,&sbuf); + unix_format(dir2); - 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 (!check_name(directory,conn)) + can_open = False; - if (strlen(directory) == 0) - strcpy(directory,"./"); - bzero(status,21); - CVAL(status,0) = dirtype; + p = strrchr_m(dir2,'/'); + if (p == NULL) + { + pstrcpy(mask,dir2); + *dir2 = 0; } - else + 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); + *p = 0; + pstrcpy(mask,p+1); } - /* turn strings of spaces into a . */ + p = strrchr_m(directory,'/'); + if (!p) + *directory = 0; + else + *p = 0; + + if (strlen(directory) == 0) + pstrcpy(directory,"./"); + memset((char *)status,'\0',21); + SCVAL(status,0,dirtype); + } + else { - 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); - } + memcpy(status,p,21); + dirtype = CVAL(status,0) & 0x1F; + conn->dirptr = dptr_fetch(status+12,&dptr_num); + if (!conn->dirptr) + goto SearchEmpty; + string_set(&conn->dirpath,dptr_path(dptr_num)); + fstrcpy(mask, dptr_wcard(dptr_num)); } + if (can_open) { - for (p=mask; *p; p++) + p = smb_buf(outbuf) + 3; + + ok = True; + + if (status_len == 0) + { + dptr_num = dptr_create(conn,directory,True,expect_close,SVAL(inbuf,smb_pid)); + if (dptr_num < 0) { - if (*p != '?' && *p != '*' && !isdoschar(*p)) - { - DEBUG(5,("Invalid char [%c] in search mask?\n",*p)); - *p = '?'; - } + if(dptr_num == -2) + { + set_bad_path_error(errno, bad_path); + END_PROFILE(SMBsearch); + return (UNIXERROR(ERRDOS,ERRnofids)); + } + END_PROFILE(SMBsearch); + return ERROR_DOS(ERRDOS,ERRnofids); } - } - - if (!strchr(mask,'.') && strlen(mask)>8) - { - fstring tmp; - strcpy(tmp,&mask[8]); - mask[8] = '.'; - mask[9] = 0; - strcat(mask,tmp); + dptr_set_wcard(dptr_num, strdup(mask)); } - 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)); + 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; - } - } - } - } + if (ok) + { + if ((dirtype&0x1F) == aVOLID) + { + memcpy(p,status,21); + make_dir_struct(p,"???????????",volume_label(SNUM(conn)),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", + conn->dirpath,lp_dontdescend(SNUM(conn)))); + if (in_list(conn->dirpath, lp_dontdescend(SNUM(conn)),True)) + check_descend = True; + + for (i=numentries;(i<maxentries) && !finished;i++) + { + finished = + !get_dir_entry(conn,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; + } + } + } /* if (ok ) */ + } - SearchEmpty: + SearchEmpty: if (numentries == 0 || !ok) - { - CVAL(outbuf,smb_rcls) = ERRDOS; - SSVAL(outbuf,smb_err,ERRnofiles); - } + { + SCVAL(outbuf,smb_rcls,ERRDOS); + SSVAL(outbuf,smb_err,ERRnofiles); + dptr_close(&dptr_num); + } /* 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); - } + { + SCVAL(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); + dptr_close(&dptr_num); SSVAL(outbuf,smb_vwv0,numentries); SSVAL(outbuf,smb_vwv1,3 + numentries * DIR_STRUCT_SIZE); - CVAL(smb_buf(outbuf),0) = 5; + SCVAL(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 */ - } + if (Protocol >= PROTOCOL_NT1) + SSVAL(outbuf,smb_flg2,SVAL(outbuf, smb_flg2) | FLAGS2_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)); + slprintf(directory, sizeof(directory)-1, "(%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)); + DEBUG( 4, ( "%s mask=%s path=%s dtype=%d nument=%d of %d\n", + smb_fn_name(CVAL(inbuf,smb_com)), + mask, directory, dirtype, numentries, maxentries ) ); + END_PROFILE(SMBsearch); return(outsize); } @@ -856,36 +770,41 @@ int reply_search(char *inbuf,char *outbuf) /**************************************************************************** reply to a fclose (stop directory search) ****************************************************************************/ -int reply_fclose(char *inbuf,char *outbuf) +int reply_fclose(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize) { - int cnum; int outsize = 0; int status_len; - char *path; + pstring path; char status[21]; - int dptr_num= -1; + int dptr_num= -2; + char *p; - cnum = SVAL(inbuf,smb_tid); + START_PROFILE(SMBfclose); 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)); + p = smb_buf(inbuf) + 1; + p += srvstr_pull(inbuf, path, p, sizeof(path), -1, STR_TERMINATE); + p++; + status_len = SVAL(p,0); + p += 2; + + if (status_len == 0) { + END_PROFILE(SMBfclose); + return ERROR_DOS(ERRSRV,ERRsrverror); + } - memcpy(status,smb_buf(inbuf) + 1 + strlen(path) + 4,21); + memcpy(status,p,21); if(dptr_fetch(status+12,&dptr_num)) { /* Close the dptr - we know it's gone */ - dptr_close(dptr_num); + dptr_close(&dptr_num); } SSVAL(outbuf,smb_vwv0,0); - DEBUG(3,("%s search close cnum=%d\n",timestring(),cnum)); + DEBUG(3,("search close\n")); + END_PROFILE(SMBfclose); return(outsize); } @@ -893,63 +812,71 @@ int reply_fclose(char *inbuf,char *outbuf) /**************************************************************************** reply to an open ****************************************************************************/ -int reply_open(char *inbuf,char *outbuf) + +int reply_open(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize) { pstring fname; - int cnum; - int fnum = -1; int outsize = 0; int fmode=0; int share_mode; - int size = 0; + SMB_OFF_T size = 0; time_t mtime=0; - int unixmode; + mode_t unixmode; int rmode=0; - struct stat sbuf; - - cnum = SVAL(inbuf,smb_tid); - + SMB_STRUCT_STAT sbuf; + BOOL bad_path = False; + files_struct *fsp; + int oplock_request = CORE_OPLOCK_REQUEST(inbuf); + START_PROFILE(SMBopen); + 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)); + srvstr_pull(inbuf, fname, smb_buf(inbuf)+1, sizeof(fname), -1, STR_TERMINATE); - if (!check_name(fname,cnum)) - return(UNIXERROR(ERRDOS,ERRnoaccess)); - - unixmode = unix_mode(cnum,aARCH); + RESOLVE_DFSPATH(fname, conn, inbuf, outbuf); + + unix_convert(fname,conn,0,&bad_path,&sbuf); + + unixmode = unix_mode(conn,aARCH,fname); - open_file_shared(fnum,cnum,fname,share_mode,3,unixmode,&rmode,NULL); + fsp = open_file_shared(conn,fname,&sbuf,share_mode,(FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN), + unixmode, oplock_request,&rmode,NULL); - if (!Files[fnum].open) + if (!fsp) + { + set_bad_path_error(errno, bad_path); + END_PROFILE(SMBopen); 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); + fmode = dos_mode(conn,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)); + close_file(fsp,False); + END_PROFILE(SMBopen); + return ERROR_DOS(ERRDOS,ERRnoaccess); } outsize = set_message(outbuf,7,0,True); - SSVAL(outbuf,smb_vwv0,fnum); + SSVAL(outbuf,smb_vwv0,fsp->fnum); SSVAL(outbuf,smb_vwv1,fmode); - put_dos_date3(outbuf,smb_vwv2,mtime); - SIVAL(outbuf,smb_vwv4,size); + if(lp_dos_filetime_resolution(SNUM(conn)) ) + put_dos_date3(outbuf,smb_vwv2,mtime & ~1); + else + put_dos_date3(outbuf,smb_vwv2,mtime); + SIVAL(outbuf,smb_vwv4,(uint32)size); SSVAL(outbuf,smb_vwv6,rmode); + + if (oplock_request && lp_fake_oplocks(SNUM(conn))) { + SCVAL(outbuf,smb_flg,CVAL(outbuf,smb_flg)|CORE_OPLOCK_GRANTED); + } + if(EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) + SCVAL(outbuf,smb_flg,CVAL(outbuf,smb_flg)|CORE_OPLOCK_GRANTED); + END_PROFILE(SMBopen); return(outsize); } @@ -957,165 +884,211 @@ int reply_open(char *inbuf,char *outbuf) /**************************************************************************** reply to an open and X ****************************************************************************/ -int reply_open_and_X(char *inbuf,char *outbuf,int length,int bufsize) +int reply_open_and_X(connection_struct *conn, 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); + /* Breakout the oplock request bits so we can set the + reply bits separately. */ + BOOL ex_oplock_request = EXTENDED_OPLOCK_REQUEST(inbuf); + BOOL core_oplock_request = CORE_OPLOCK_REQUEST(inbuf); + BOOL oplock_request = ex_oplock_request | core_oplock_request; #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; + mode_t unixmode; + SMB_OFF_T size=0; + int fmode=0,mtime=0,rmode=0; + SMB_STRUCT_STAT sbuf; int smb_action = 0; + BOOL bad_path = False; + files_struct *fsp; + START_PROFILE(SMBopenX); + + /* If it's an IPC, pass off the pipe handler. */ + if (IS_IPC(conn)) { + if (lp_nt_pipe_support()) { + END_PROFILE(SMBopenX); + return reply_open_pipe_and_X(conn, inbuf,outbuf,length,bufsize); + } else { + END_PROFILE(SMBopenX); + return ERROR_DOS(ERRSRV,ERRaccess); + } + } /* XXXX we need to handle passed times, sattr and flags */ + srvstr_pull(inbuf, fname, smb_buf(inbuf), sizeof(fname), -1, STR_TERMINATE); - 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)); + RESOLVE_DFSPATH(fname, conn, inbuf, outbuf); - unixmode = unix_mode(cnum,smb_attr | aARCH); + unix_convert(fname,conn,0,&bad_path,&sbuf); + + unixmode = unix_mode(conn,smb_attr | aARCH, fname); - open_file_shared(fnum,cnum,fname,smb_mode,smb_ofun,unixmode, - &rmode,&smb_action); + fsp = open_file_shared(conn,fname,&sbuf,smb_mode,smb_ofun,unixmode, + oplock_request, &rmode,&smb_action); - if (!Files[fnum].open) + if (!fsp) + { + set_bad_path_error(errno, bad_path); + END_PROFILE(SMBopenX); 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); + fmode = dos_mode(conn,fname,&sbuf); mtime = sbuf.st_mtime; if (fmode & aDIR) { - close_file(fnum); - return(ERROR(ERRDOS,ERRnoaccess)); + close_file(fsp,False); + END_PROFILE(SMBopenX); + return ERROR_DOS(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); + /* If the caller set the extended oplock request bit + and we granted one (by whatever means) - set the + correct bit for extended oplock reply. + */ + + if (ex_oplock_request && lp_fake_oplocks(SNUM(conn))) { + smb_action |= EXTENDED_OPLOCK_GRANTED; + } + + if(ex_oplock_request && EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) { + smb_action |= EXTENDED_OPLOCK_GRANTED; + } + + /* If the caller set the core oplock request bit + and we granted one (by whatever means) - set the + correct bit for core oplock reply. + */ + + if (core_oplock_request && lp_fake_oplocks(SNUM(conn))) { + SCVAL(outbuf,smb_flg,CVAL(outbuf,smb_flg)|CORE_OPLOCK_GRANTED); + } + + if(core_oplock_request && EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) { + SCVAL(outbuf,smb_flg,CVAL(outbuf,smb_flg)|CORE_OPLOCK_GRANTED); + } + + set_message(outbuf,15,0,True); + SSVAL(outbuf,smb_vwv2,fsp->fnum); SSVAL(outbuf,smb_vwv3,fmode); - put_dos_date3(outbuf,smb_vwv4,mtime); - SIVAL(outbuf,smb_vwv6,size); + if(lp_dos_filetime_resolution(SNUM(conn)) ) + put_dos_date3(outbuf,smb_vwv4,mtime & ~1); + else + put_dos_date3(outbuf,smb_vwv4,mtime); + SIVAL(outbuf,smb_vwv6,(uint32)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); + END_PROFILE(SMBopenX); + return chain_reply(inbuf,outbuf,length,bufsize); } /**************************************************************************** reply to a SMBulogoffX ****************************************************************************/ -int reply_ulogoffX(char *inbuf,char *outbuf,int length,int bufsize) +int reply_ulogoffX(connection_struct *conn, 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); + uint16 vuid = SVAL(inbuf,smb_uid); + user_struct *vuser = get_valid_user_struct(vuid); + START_PROFILE(SMBulogoffX); - invalidate_uid(uid); + if(vuser == 0) { + DEBUG(3,("ulogoff, vuser id %d does not map to user.\n", vuid)); + } - outsize = set_message(outbuf,2,0,True); - CVAL(outbuf,smb_vwv0) = smb_com2; - SSVAL(outbuf,smb_vwv1,(chain_size+outsize)-4); + /* in user level security we are supposed to close any files + open by this user */ + if ((vuser != 0) && (lp_security() != SEC_SHARE)) { + file_close_user(vuid); + } - DEBUG(3,("%s ulogoffX uid=%d\n",timestring(),uid)); + invalidate_vuid(vuid); - if (smb_com2 != 0xFF) - outsize += chain_reply(smb_com2,inbuf,inbuf+smb_off2+4, - outbuf,outbuf+outsize, - length,bufsize); + set_message(outbuf,2,0,True); - - return(outsize); + DEBUG( 3, ( "ulogoffX vuid=%d\n", vuid ) ); + + END_PROFILE(SMBulogoffX); + return chain_reply(inbuf,outbuf,length,bufsize); } /**************************************************************************** - reply to a mknew + reply to a mknew or a create ****************************************************************************/ -int reply_mknew(char *inbuf,char *outbuf) +int reply_mknew(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize) { pstring fname; - int cnum,com; - int fnum = -1; + int com; int outsize = 0; int createmode; mode_t unixmode; - + int ofun = 0; + BOOL bad_path = False; + files_struct *fsp; + int oplock_request = CORE_OPLOCK_REQUEST(inbuf); + SMB_STRUCT_STAT sbuf; + START_PROFILE(SMBcreate); + 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); + srvstr_pull(inbuf, fname, smb_buf(inbuf) + 1, sizeof(fname), -1, STR_TERMINATE); - if (createmode & aVOLID) - { + RESOLVE_DFSPATH(fname, conn, inbuf, outbuf); + + unix_convert(fname,conn,0,&bad_path,&sbuf); + + if (createmode & aVOLID) { DEBUG(0,("Attempt to create file (%s) with volid set - please report this\n",fname)); - } + } - unixmode = unix_mode(cnum,createmode); + unixmode = unix_mode(conn,createmode,fname); - 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)); + if(com == SMBmknew) + { + /* We should fail if file exists. */ + ofun = FILE_CREATE_IF_NOT_EXIST; + } + else + { + /* SMBcreate - Create if file doesn't exist, truncate if it does. */ + ofun = FILE_CREATE_IF_NOT_EXIST|FILE_EXISTS_TRUNCATE; + } - open_file(fnum,cnum,fname,O_RDWR | O_CREAT | O_TRUNC,unixmode); + /* Open file in dos compatibility share mode. */ + fsp = open_file_shared(conn,fname,&sbuf,SET_DENY_MODE(DENY_FCB)|SET_OPEN_MODE(DOS_OPEN_FCB), + ofun, unixmode, oplock_request, NULL, NULL); - if (!Files[fnum].open) + if (!fsp) + { + set_bad_path_error(errno, bad_path); + END_PROFILE(SMBcreate); 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)); - + SSVAL(outbuf,smb_vwv0,fsp->fnum); + + if (oplock_request && lp_fake_oplocks(SNUM(conn))) { + SCVAL(outbuf,smb_flg,CVAL(outbuf,smb_flg)|CORE_OPLOCK_GRANTED); + } + + if(EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) + SCVAL(outbuf,smb_flg,CVAL(outbuf,smb_flg)|CORE_OPLOCK_GRANTED); + + DEBUG( 2, ( "new file %s\n", fname ) ); + DEBUG( 3, ( "mknew %s fd=%d dmode=%d umode=%o\n", + fname, fsp->fd, createmode, (int)unixmode ) ); + + END_PROFILE(SMBcreate); return(outsize); } @@ -1123,343 +1096,561 @@ int reply_mknew(char *inbuf,char *outbuf) /**************************************************************************** reply to a create temporary file ****************************************************************************/ -int reply_ctemp(char *inbuf,char *outbuf) +int reply_ctemp(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize) { pstring fname; - pstring fname2; - int cnum; - int fnum = -1; int outsize = 0; int createmode; mode_t unixmode; - - cnum = SVAL(inbuf,smb_tid); + BOOL bad_path = False; + files_struct *fsp; + int oplock_request = CORE_OPLOCK_REQUEST(inbuf); + int tmpfd; + SMB_STRUCT_STAT sbuf; + char *p, *s; + + START_PROFILE(SMBctemp); + createmode = SVAL(inbuf,smb_vwv0); - sprintf(fname,"%s/TMXXXXXX",smb_buf(inbuf)+1); - unix_convert(fname,cnum); + srvstr_pull(inbuf, fname, smb_buf(inbuf)+1, sizeof(fname), -1, STR_TERMINATE); + pstrcat(fname,"\\TMXXXXXX"); + + RESOLVE_DFSPATH(fname, conn, inbuf, outbuf); + + unix_convert(fname,conn,0,&bad_path,&sbuf); - unixmode = unix_mode(cnum,createmode); + unixmode = unix_mode(conn,createmode,fname); - fnum = find_free_file(); - if (fnum < 0) - return(ERROR(ERRSRV,ERRnofids)); + tmpfd = smb_mkstemp(fname); + if (tmpfd == -1) { + END_PROFILE(SMBctemp); + return(UNIXERROR(ERRDOS,ERRnoaccess)); + } - if (!check_name(fname,cnum)) - return(UNIXERROR(ERRDOS,ERRnoaccess)); + vfs_stat(conn,fname,&sbuf); - strcpy(fname2,(char *)mktemp(fname)); + /* Open file in dos compatibility share mode. */ + /* We should fail if file does not exist. */ + fsp = open_file_shared(conn,fname,&sbuf, + SET_DENY_MODE(DENY_FCB)|SET_OPEN_MODE(DOS_OPEN_FCB), + FILE_EXISTS_OPEN|FILE_FAIL_IF_NOT_EXIST, + unixmode, oplock_request, NULL, NULL); - open_file(fnum,cnum,fname2,O_RDWR | O_CREAT | O_TRUNC,unixmode); + /* close fd from smb_mkstemp() */ + close(tmpfd); - if (!Files[fnum].open) + if (!fsp) { + set_bad_path_error(errno, bad_path); + END_PROFILE(SMBctemp); 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)); + outsize = set_message(outbuf,1,0,True); + SSVAL(outbuf,smb_vwv0,fsp->fnum); + + /* the returned filename is relative to the directory */ + s = strrchr_m(fname, '/'); + if (!s) { + s = fname; + } else { + s++; + } + + p = smb_buf(outbuf); + SSVALS(p, 0, -1); /* what is this? not in spec */ + SSVAL(p, 2, strlen(s)); + p += 4; + p += srvstr_push(outbuf, p, s, -1, STR_ASCII); + outsize = set_message_end(outbuf, p); + + if (oplock_request && lp_fake_oplocks(SNUM(conn))) { + SCVAL(outbuf,smb_flg,CVAL(outbuf,smb_flg)|CORE_OPLOCK_GRANTED); + } + if (EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) + SCVAL(outbuf,smb_flg,CVAL(outbuf,smb_flg)|CORE_OPLOCK_GRANTED); + + DEBUG( 2, ( "created temp file %s\n", fname ) ); + DEBUG( 3, ( "ctemp %s fd=%d dmode=%d umode=%o\n", + fname, fsp->fd, createmode, (int)unixmode ) ); + + END_PROFILE(SMBctemp); return(outsize); } +/******************************************************************* + Check if a user is allowed to rename a file. +********************************************************************/ + +static NTSTATUS can_rename(char *fname,connection_struct *conn, SMB_STRUCT_STAT *pst) +{ + int smb_action; + int access_mode; + files_struct *fsp; + + if (!CAN_WRITE(conn)) + return NT_STATUS_MEDIA_WRITE_PROTECTED; + + if (S_ISDIR(pst->st_mode)) + return NT_STATUS_OK; + + /* We need a better way to return NT status codes from open... */ + unix_ERR_class = 0; + unix_ERR_code = 0; + + fsp = open_file_shared1(conn, fname, pst, DELETE_ACCESS, SET_DENY_MODE(DENY_ALL), + (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN), 0, 0, &access_mode, &smb_action); + + if (!fsp) { + NTSTATUS ret = NT_STATUS_ACCESS_DENIED; + if (unix_ERR_class == ERRDOS && unix_ERR_code == ERRbadshare) + ret = NT_STATUS_SHARING_VIOLATION; + unix_ERR_class = 0; + unix_ERR_code = 0; + return ret; + } + close_file(fsp,False); + return NT_STATUS_OK; +} /******************************************************************* -check if a user is allowed to delete a file + Check if a user is allowed to delete a file. ********************************************************************/ -static BOOL can_delete(char *fname,int cnum,int dirtype) + +static NTSTATUS can_delete(char *fname,connection_struct *conn, 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); + SMB_STRUCT_STAT sbuf; + int fmode; + int smb_action; + int access_mode; + files_struct *fsp; + + if (!CAN_WRITE(conn)) + return NT_STATUS_MEDIA_WRITE_PROTECTED; + + if (conn->vfs_ops.lstat(conn,fname,&sbuf) != 0) + return NT_STATUS_OBJECT_NAME_NOT_FOUND; + + fmode = dos_mode(conn,fname,&sbuf); + if (fmode & aDIR) + return NT_STATUS_FILE_IS_A_DIRECTORY; + if (!lp_delete_readonly(SNUM(conn))) { + if (fmode & aRONLY) + return NT_STATUS_CANNOT_DELETE; + } + if ((fmode & ~dirtype) & (aHIDDEN | aSYSTEM)) + return NT_STATUS_CANNOT_DELETE; + + /* We need a better way to return NT status codes from open... */ + unix_ERR_class = 0; + unix_ERR_code = 0; + + fsp = open_file_shared1(conn, fname, &sbuf, DELETE_ACCESS, SET_DENY_MODE(DENY_ALL), + (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN), 0, 0, &access_mode, &smb_action); + + if (!fsp) { + NTSTATUS ret = NT_STATUS_ACCESS_DENIED; + if (unix_ERR_class == ERRDOS && unix_ERR_code == ERRbadshare) + ret = NT_STATUS_SHARING_VIOLATION; + unix_ERR_class = 0; + unix_ERR_code = 0; + return ret; + } + close_file(fsp,False); + return NT_STATUS_OK; } /**************************************************************************** - reply to a unlink + The guts of the unlink command, split out so it may be called by the NT SMB + code. ****************************************************************************/ -int reply_unlink(char *inbuf,char *outbuf) + +NTSTATUS unlink_internals(connection_struct *conn, int dirtype, char *name) { - 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; + pstring directory; + pstring mask; + char *p; + int count=0; + NTSTATUS error = NT_STATUS_OK; + BOOL has_wild; + BOOL bad_path = False; + BOOL rc = True; + SMB_STRUCT_STAT sbuf; + + *directory = *mask = 0; + + rc = unix_convert(name,conn,0,&bad_path,&sbuf); + + p = strrchr_m(name,'/'); + if (!p) { + pstrcpy(directory,"."); + pstrcpy(mask,name); + } else { + *p = 0; + pstrcpy(directory,name); + pstrcpy(mask,p+1); + } + + /* + * We should only check the mangled cache + * here if unix_convert failed. This means + * that the path in 'mask' doesn't exist + * on the file system and so we need to look + * for a possible mangle. This patch from + * Tine Smukavec <valentin.smukavec@hermes.si>. + */ + + if (!rc && mangle_is_mangled(mask)) + mangle_check_cache( mask ); + + has_wild = ms_has_wild(mask); + + if (!has_wild) { + pstrcat(directory,"/"); + pstrcat(directory,mask); + error = can_delete(directory,conn,dirtype); + if (!NT_STATUS_IS_OK(error)) return error; + + if (vfs_unlink(conn,directory) == 0) { + count++; + } + } else { + void *dirptr = NULL; + char *dname; + + if (check_name(directory,conn)) + dirptr = OpenDir(conn, directory, True); + + /* XXXX the CIFS spec says that if bit0 of the flags2 field is set then + the pattern matches against the long name, otherwise the short name + We don't implement this yet XXXX + */ + + if (dirptr) { + error = NT_STATUS_OBJECT_NAME_NOT_FOUND; + + if (strequal(mask,"????????.???")) + pstrcpy(mask,"*"); + + while ((dname = ReadDirName(dirptr))) { + pstring fname; + pstrcpy(fname,dname); + + if(!mask_match(fname, mask, case_sensitive)) continue; + + slprintf(fname,sizeof(fname)-1, "%s/%s",directory,dname); + error = can_delete(fname,conn,dirtype); + if (!NT_STATUS_IS_OK(error)) continue; + if (vfs_unlink(conn,fname) == 0) count++; + DEBUG(3,("unlink_internals: succesful unlink [%s]\n",fname)); + } + CloseDir(dirptr); + } + } + + if (count == 0 && NT_STATUS_IS_OK(error)) { + error = map_nt_error_from_unix(errno); + } - *directory = *mask = 0; + return error; +} - cnum = SVAL(inbuf,smb_tid); - dirtype = SVAL(inbuf,smb_vwv0); +/**************************************************************************** + Reply to a unlink +****************************************************************************/ + +int reply_unlink(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, + int dum_buffsize) +{ + int outsize = 0; + pstring name; + int dirtype; + NTSTATUS status; + START_PROFILE(SMBunlink); + + dirtype = SVAL(inbuf,smb_vwv0); + + srvstr_pull(inbuf, name, smb_buf(inbuf) + 1, sizeof(name), -1, STR_TERMINATE); + + RESOLVE_DFSPATH(name, conn, inbuf, outbuf); + + DEBUG(3,("reply_unlink : %s\n",name)); + + status = unlink_internals(conn, dirtype, name); + if (!NT_STATUS_IS_OK(status)) return ERROR_NT(status); + + /* + * Win2k needs a changenotify request response before it will + * update after a rename.. + */ + process_pending_change_notify_queue((time_t)0); + + outsize = set_message(outbuf,0,0,True); - strcpy(name,smb_buf(inbuf) + 1); - - DEBUG(3,("reply_unlink : %s\n",name)); - - unix_convert(name,cnum); + END_PROFILE(SMBunlink); + return outsize; +} - p = strrchr(name,'/'); - if (!p) { - strcpy(directory,"./"); - strcpy(mask,name); - } else { - *p = 0; - strcpy(directory,name); - strcpy(mask,p+1); - } +/**************************************************************************** + Fail for readbraw. +****************************************************************************/ - if (is_mangled(mask)) - check_mangled_stack(mask); +void fail_readraw(void) +{ + pstring errstr; + slprintf(errstr, sizeof(errstr)-1, "FAIL ! reply_readbraw: socket write fail (%s)", + strerror(errno) ); + exit_server(errstr); +} - has_wild = strchr(mask,'*') || strchr(mask,'?'); +/**************************************************************************** + Reply to a readbraw (core+ protocol). +****************************************************************************/ - 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; +int reply_readbraw(connection_struct *conn, char *inbuf, char *outbuf, int dum_size, int dum_buffsize) +{ + ssize_t maxcount,mincount; + size_t nread = 0; + SMB_OFF_T startpos; + char *header = outbuf; + ssize_t ret=0; + files_struct *fsp; + START_PROFILE(SMBreadbraw); + + /* + * Special check if an oplock break has been issued + * and the readraw request croses on the wire, we must + * return a zero length response here. + */ + + if(global_oplock_break) { + _smb_setlen(header,0); + if (write_data(smbd_server_fd(),header,4) != 4) + fail_readraw(); + DEBUG(5,("readbraw - oplock break finished\n")); + END_PROFILE(SMBreadbraw); + return -1; + } - if (check_name(directory,cnum)) - dirptr = OpenDir(directory); + fsp = file_fsp(inbuf,smb_vwv0); + + if (!FNUM_OK(fsp,conn) || !fsp->can_read) { + /* + * fsp could be NULL here so use the value from the packet. JRA. + */ + DEBUG(3,("fnum %d not open in readbraw - cache prime?\n",(int)SVAL(inbuf,smb_vwv0))); + _smb_setlen(header,0); + if (write_data(smbd_server_fd(),header,4) != 4) + fail_readraw(); + END_PROFILE(SMBreadbraw); + return(-1); + } - if (dirptr) - { - error = ERRbadfile; + CHECK_FSP(fsp,conn); - if (strequal(mask,"????????.???")) - strcpy(mask,"*"); + flush_write_cache(fsp, READRAW_FLUSH); - while ((dname = ReadDirName(dirptr))) - { - pstring fname; - strcpy(fname,dname); - - if(!mask_match(fname, mask, case_sensitive, False)) continue; + startpos = IVAL(inbuf,smb_vwv1); + if(CVAL(inbuf,smb_wct) == 10) { + /* + * This is a large offset (64 bit) read. + */ +#ifdef LARGE_SMB_OFF_T - 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); -} + startpos |= (((SMB_OFF_T)IVAL(inbuf,smb_vwv8)) << 32); +#else /* !LARGE_SMB_OFF_T */ -/**************************************************************************** - 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; - } + /* + * Ensure we haven't been sent a >32 bit offset. + */ + if(IVAL(inbuf,smb_vwv8) != 0) { + DEBUG(0,("readbraw - large offset (%x << 32) used and we don't support \ +64 bit offsets.\n", (unsigned int)IVAL(inbuf,smb_vwv8) )); + _smb_setlen(header,0); + if (write_data(smbd_server_fd(),header,4) != 4) + fail_readraw(); + END_PROFILE(SMBreadbraw); + return(-1); + } - 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; - } +#endif /* LARGE_SMB_OFF_T */ - nread = MIN(maxcount,size - startpos); - } + if(startpos < 0) { + DEBUG(0,("readbraw - negative 64 bit readraw offset (%.0f) !\n", (double)startpos )); + _smb_setlen(header,0); + if (write_data(smbd_server_fd(),header,4) != 4) + fail_readraw(); + END_PROFILE(SMBreadbraw); + return(-1); + } + } + maxcount = (SVAL(inbuf,smb_vwv3) & 0xFFFF); + mincount = (SVAL(inbuf,smb_vwv4) & 0xFFFF); + + /* ensure we don't overrun the packet size */ + maxcount = MIN(65535,maxcount); + maxcount = MAX(mincount,maxcount); + + if (!is_locked(fsp,conn,(SMB_BIG_UINT)maxcount,(SMB_BIG_UINT)startpos, READ_LOCK,False)) { + SMB_OFF_T size = fsp->size; + SMB_OFF_T sizeneeded = startpos + maxcount; + + if (size < sizeneeded) { + SMB_STRUCT_STAT st; + if (vfs_fstat(fsp,fsp->fd,&st) == 0) + size = st.st_size; + if (!fsp->can_write) + fsp->size = size; + } - if (nread < mincount) - nread = 0; + if (startpos >= size) + nread = 0; + else + 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)); + DEBUG( 3, ( "readbraw fnum=%d start=%.0f max=%d min=%d nread=%d\n", fsp->fnum, (double)startpos, + (int)maxcount, (int)mincount, (int)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)); + if (nread > 0) { + ret = read_file(fsp,header+4,startpos,nread); + if (ret < mincount) + ret = 0; + } -#else - ret = read_file(fnum,header+4,startpos,nread,nread,-1,False); - if (ret < mincount) ret = 0; + _smb_setlen(header,ret); + if (write_data(smbd_server_fd(),header,4+ret) != 4+ret) + fail_readraw(); - _smb_setlen(header,ret); - transfer_file(0,Client,0,header,4+ret,0); -#endif - - DEBUG(5,("readbraw finished\n")); - return -1; + DEBUG(5,("readbraw finished\n")); + END_PROFILE(SMBreadbraw); + return -1; } - /**************************************************************************** reply to a lockread (core+ protocol) ****************************************************************************/ -int reply_lockread(char *inbuf,char *outbuf) +int reply_lockread(connection_struct *conn, char *inbuf,char *outbuf, int length, int dum_buffsiz) { - 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); + ssize_t nread = -1; + char *data; + int outsize = 0; + SMB_OFF_T startpos; + size_t numtoread; + NTSTATUS status; + files_struct *fsp = file_fsp(inbuf,smb_vwv0); + START_PROFILE(SMBlockread); + + CHECK_FSP(fsp,conn); + CHECK_READ(fsp); + + release_level_2_oplocks_on_change(fsp); + + 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; + + /* + * NB. Discovered by Menny Hamburger at Mainsoft. This is a core+ + * protocol request that predates the read/write lock concept. + * Thus instead of asking for a read lock here we need to ask + * for a write lock. JRA. + */ + + status = do_lock_spin(fsp, conn, SVAL(inbuf,smb_pid), + (SMB_BIG_UINT)numtoread, (SMB_BIG_UINT)startpos, WRITE_LOCK); + + if (NT_STATUS_V(status)) { + if (lp_blocking_locks(SNUM(conn))) { + /* + * A blocking lock was requested. Package up + * this smb into a queued request and push it + * onto the blocking lock queue. + */ + if(push_blocking_lock_request(inbuf, length, -1, 0)) + END_PROFILE(SMBlockread); + return -1; + } + END_PROFILE(SMBlockread); + return ERROR_NT(status); + } - CHECK_FNUM(fnum,cnum); - CHECK_READ(fnum); - CHECK_ERROR(fnum); + nread = read_file(fsp,data,startpos,numtoread); - 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)); + if (nread < 0) { + END_PROFILE(SMBlockread); + 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,("lockread fnum=%d num=%d nread=%d\n", + fsp->fnum, (int)numtoread, (int)nread)); - 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); + END_PROFILE(SMBlockread); + return(outsize); } /**************************************************************************** reply to a read ****************************************************************************/ -int reply_read(char *inbuf,char *outbuf) + +int reply_read(connection_struct *conn, char *inbuf,char *outbuf, int size, int dum_buffsize) { - int cnum,numtoread,fnum; - int nread = 0; + size_t numtoread; + ssize_t nread = 0; char *data; - int startpos; + SMB_OFF_T startpos; int outsize = 0; - - cnum = SVAL(inbuf,smb_tid); - fnum = GETFNUM(inbuf,smb_vwv0); + files_struct *fsp = file_fsp(inbuf,smb_vwv0); + START_PROFILE(SMBread); - CHECK_FNUM(fnum,cnum); - CHECK_READ(fnum); - CHECK_ERROR(fnum); + CHECK_FSP(fsp,conn); + CHECK_READ(fsp); 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 (is_locked(fsp,conn,(SMB_BIG_UINT)numtoread,(SMB_BIG_UINT)startpos, READ_LOCK,False)) { + END_PROFILE(SMBread); + return ERROR_DOS(ERRDOS,ERRlock); + } if (numtoread > 0) - nread = read_file(fnum,data,startpos,numtoread,numtoread,-1,False); - - if (nread < 0) + nread = read_file(fsp,data,startpos,numtoread); + + if (nread < 0) { + END_PROFILE(SMBread); return(UNIXERROR(ERRDOS,ERRnoaccess)); + } outsize += nread; SSVAL(outbuf,smb_vwv0,nread); SSVAL(outbuf,smb_vwv5,nread+3); - CVAL(smb_buf(outbuf),0) = 1; + SCVAL(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)); - + DEBUG( 3, ( "read fnum=%d num=%d nread=%d\n", + fsp->fnum, (int)numtoread, (int)nread ) ); + + END_PROFILE(SMBread); return(outsize); } @@ -1467,605 +1658,813 @@ int reply_read(char *inbuf,char *outbuf) /**************************************************************************** reply to a read and X ****************************************************************************/ -int reply_read_and_X(char *inbuf,char *outbuf,int length,int bufsize) +int reply_read_and_X(connection_struct *conn, 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; + files_struct *fsp = file_fsp(inbuf,smb_vwv2); + SMB_OFF_T startpos = IVAL(inbuf,smb_vwv3); + size_t smb_maxcnt = SVAL(inbuf,smb_vwv5); + size_t smb_mincnt = SVAL(inbuf,smb_vwv6); + ssize_t nread = -1; char *data; - int outsize = 0; - BOOL ok = False; + START_PROFILE(SMBreadX); - cnum = SVAL(inbuf,smb_tid); + /* If it's an IPC, pass off the pipe handler. */ + if (IS_IPC(conn)) { + END_PROFILE(SMBreadX); + return reply_pipe_read_and_X(inbuf,outbuf,length,bufsize); + } - CHECK_FNUM(fnum,cnum); - CHECK_READ(fnum); - CHECK_ERROR(fnum); + CHECK_FSP(fsp,conn); + CHECK_READ(fsp); - outsize = set_message(outbuf,12,0,True); + 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) + if(CVAL(inbuf,smb_wct) == 12) { +#ifdef LARGE_SMB_OFF_T + /* + * This is a large offset (64 bit) read. + */ + startpos |= (((SMB_OFF_T)IVAL(inbuf,smb_vwv10)) << 32); + +#else /* !LARGE_SMB_OFF_T */ + + /* + * Ensure we haven't been sent a >32 bit offset. + */ + + if(IVAL(inbuf,smb_vwv10) != 0) { + DEBUG(0,("reply_read_and_X - large offset (%x << 32) used and we don't support \ +64 bit offsets.\n", (unsigned int)IVAL(inbuf,smb_vwv10) )); + END_PROFILE(SMBreadX); + return ERROR_DOS(ERRDOS,ERRbadaccess); + } + +#endif /* LARGE_SMB_OFF_T */ + + } + + if (is_locked(fsp,conn,(SMB_BIG_UINT)smb_maxcnt,(SMB_BIG_UINT)startpos, READ_LOCK,False)) { + END_PROFILE(SMBreadX); + return ERROR_DOS(ERRDOS,ERRlock); + } + nread = read_file(fsp,data,startpos,smb_maxcnt); + + if (nread < 0) { + END_PROFILE(SMBreadX); 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(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 com2=%d off2=%d\n", - timestring(),fnum,cnum, - smb_mincnt,smb_maxcnt,nread,smb_com2,smb_off2)); + DEBUG( 3, ( "readX fnum=%d min=%d max=%d nread=%d\n", + fsp->fnum, (int)smb_mincnt, (int)smb_maxcnt, (int)nread ) ); - 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); + END_PROFILE(SMBreadX); + 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; +int reply_writebraw(connection_struct *conn, char *inbuf,char *outbuf, int size, int dum_buffsize) +{ + ssize_t nwritten=0; + ssize_t total_written=0; + size_t numtowrite=0; + size_t tcount; + SMB_OFF_T startpos; + char *data=NULL; + BOOL write_through; + files_struct *fsp = file_fsp(inbuf,smb_vwv0); + int outsize = 0; + START_PROFILE(SMBwritebraw); + + CHECK_FSP(fsp,conn); + CHECK_WRITE(fsp); + + 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); + } - if (is_locked(fnum,cnum,tcount,startpos)) - return(ERROR(ERRDOS,ERRlock)); + /* force the error type */ + SCVAL(inbuf,smb_com,SMBwritec); + SCVAL(outbuf,smb_com,SMBwritec); - if (seek_file(fnum,startpos) != startpos) - DEBUG(0,("couldn't seek to %d in writebraw\n",startpos)); + if (is_locked(fsp,conn,(SMB_BIG_UINT)tcount,(SMB_BIG_UINT)startpos, WRITE_LOCK,False)) { + END_PROFILE(SMBwritebraw); + return(ERROR_DOS(ERRDOS,ERRlock)); + } - if (numtowrite>0) - nwritten = write_file(fnum,data,numtowrite); + if (numtowrite>0) + nwritten = write_file(fsp,data,startpos,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)); + DEBUG(3,("writebraw1 fnum=%d start=%.0f num=%d wrote=%d sync=%d\n", + fsp->fnum, (double)startpos, (int)numtowrite, (int)nwritten, (int)write_through)); - if (nwritten < numtowrite) - return(UNIXERROR(ERRHRD,ERRdiskfull)); + if (nwritten < numtowrite) { + END_PROFILE(SMBwritebraw); + return(UNIXERROR(ERRHRD,ERRdiskfull)); + } - total_written = nwritten; + 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); + /* Return a message to the redirector to tell it to send more bytes */ + SCVAL(outbuf,smb_com,SMBwritebraw); + SSVALS(outbuf,smb_vwv0,-1); + outsize = set_message(outbuf,Protocol>PROTOCOL_COREPLUS?1:0,0,True); + if (!send_smb(smbd_server_fd(),outbuf)) + exit_server("reply_writebraw: send_smb failed."); - /* Now read the raw data into the buffer and write it */ - if(read_smb_length(Client,inbuf,0) == -1) { - exit_server("secondary writebraw failed"); - } + /* Now read the raw data into the buffer and write it */ + if (read_smb_length(smbd_server_fd(),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); + /* 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)); - } + /* Set up outbuf to return the correct return */ + outsize = set_message(outbuf,1,0,True); + SCVAL(outbuf,smb_com,SMBwritec); + SSVAL(outbuf,smb_vwv0,total_written); - 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 (numtowrite != 0) { - if (nwritten < numtowrite) { - CVAL(outbuf,smb_rcls) = ERRHRD; - SSVAL(outbuf,smb_err,ERRdiskfull); - } + if (numtowrite > BUFFER_SIZE) { + DEBUG(0,("reply_writebraw: Oversize secondary write raw requested (%u). Terminating\n", + (unsigned int)numtowrite )); + exit_server("secondary writebraw failed"); + } - if (lp_syncalways(SNUM(cnum)) || write_through) - sync_file(fnum); + if (tcount > nwritten+numtowrite) { + DEBUG(3,("Client overestimated the write %d %d %d\n", + (int)tcount,(int)nwritten,(int)numtowrite)); + } - DEBUG(3,("%s writebraw2 fnum=%d cnum=%d start=%d num=%d wrote=%d\n", - timestring(),fnum,cnum,startpos,numtowrite,total_written)); + if (read_data( smbd_server_fd(), inbuf+4, numtowrite) != numtowrite ) { + DEBUG(0,("reply_writebraw: Oversize secondary write raw read failed (%s). Terminating\n", + strerror(errno) )); + exit_server("secondary writebraw failed"); + } - /* 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); + nwritten = write_file(fsp,inbuf+4,startpos+nwritten,numtowrite); - return(outsize); -} + if (nwritten < (ssize_t)numtowrite) { + SCVAL(outbuf,smb_rcls,ERRHRD); + SSVAL(outbuf,smb_err,ERRdiskfull); + } + + if (nwritten > 0) + total_written += nwritten; + } + + if ((lp_syncalways(SNUM(conn)) || write_through) && lp_strict_sync(SNUM(conn))) + sync_file(conn,fsp); + + DEBUG(3,("writebraw2 fnum=%d start=%.0f num=%d wrote=%d\n", + fsp->fnum, (double)startpos, (int)numtowrite,(int)total_written)); + + /* we won't return a status if write through is not selected - this follows what WfWg does */ + END_PROFILE(SMBwritebraw); + if (!write_through && total_written==tcount) { + +#if RABBIT_PELLET_FIX + /* + * Fix for "rabbit pellet" mode, trigger an early TCP ack by + * sending a SMBkeepalive. Thanks to DaveCB at Sun for this. JRA. + */ + if (!send_keepalive(smbd_server_fd())) + exit_server("reply_writebraw: send of keepalive failed"); +#endif + 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); +int reply_writeunlock(connection_struct *conn, char *inbuf,char *outbuf, + int size, int dum_buffsize) +{ + ssize_t nwritten = -1; + size_t numtowrite; + SMB_OFF_T startpos; + char *data; + NTSTATUS status; + files_struct *fsp = file_fsp(inbuf,smb_vwv0); + int outsize = 0; + START_PROFILE(SMBwriteunlock); + + CHECK_FSP(fsp,conn); + CHECK_WRITE(fsp); - numtowrite = SVAL(inbuf,smb_vwv1); - startpos = IVAL(inbuf,smb_vwv2); - data = smb_buf(inbuf) + 3; + 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); + if (is_locked(fsp,conn,(SMB_BIG_UINT)numtowrite,(SMB_BIG_UINT)startpos, + WRITE_LOCK,False)) { + END_PROFILE(SMBwriteunlock); + return ERROR_DOS(ERRDOS,ERRlock); + } - /* 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); + /* 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(fsp,data,startpos,numtowrite); - if (lp_syncalways(SNUM(cnum))) - sync_file(fnum); - - if(((nwritten == 0) && (numtowrite != 0))||(nwritten < 0)) - return(UNIXERROR(ERRDOS,ERRnoaccess)); + if (lp_syncalways(SNUM(conn))) + sync_file(conn,fsp); - if(!do_unlock(fnum, cnum, numtowrite, startpos, &eclass, &ecode)) - return(ERROR(eclass,ecode)); + if(((nwritten == 0) && (numtowrite != 0))||(nwritten < 0)) { + END_PROFILE(SMBwriteunlock); + return(UNIXERROR(ERRDOS,ERRnoaccess)); + } - 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); + status = do_unlock(fsp, conn, SVAL(inbuf,smb_pid), (SMB_BIG_UINT)numtowrite, + (SMB_BIG_UINT)startpos); + if (NT_STATUS_V(status)) { + END_PROFILE(SMBwriteunlock); + return ERROR_NT(status); + } + + outsize = set_message(outbuf,1,0,True); + + SSVAL(outbuf,smb_vwv0,nwritten); + + DEBUG(3,("writeunlock fnum=%d num=%d wrote=%d\n", + fsp->fnum, (int)numtowrite, (int)nwritten)); + + END_PROFILE(SMBwriteunlock); + return outsize; } /**************************************************************************** - reply to a write + Reply to a write. ****************************************************************************/ -int reply_write(char *inbuf,char *outbuf,int dum1,int dum2) + +int reply_write(connection_struct *conn, char *inbuf,char *outbuf,int size,int dum_buffsize) { - int cnum,numtowrite,fnum; - int nwritten = -1; - int outsize = 0; - int startpos; - char *data; + size_t numtowrite; + ssize_t nwritten = -1; + SMB_OFF_T startpos; + char *data; + files_struct *fsp = file_fsp(inbuf,smb_vwv0); + int outsize = 0; + START_PROFILE(SMBwrite); + + /* If it's an IPC, pass off the pipe handler. */ + if (IS_IPC(conn)) { + END_PROFILE(SMBwrite); + return reply_pipe_write(inbuf,outbuf,size,dum_buffsize); + } - dum1 = dum2 = 0; + CHECK_FSP(fsp,conn); + CHECK_WRITE(fsp); + numtowrite = SVAL(inbuf,smb_vwv1); + startpos = IVAL(inbuf,smb_vwv2); + data = smb_buf(inbuf) + 3; - cnum = SVAL(inbuf,smb_tid); - fnum = GETFNUM(inbuf,smb_vwv0); - - CHECK_FNUM(fnum,cnum); - CHECK_WRITE(fnum); - CHECK_ERROR(fnum); + if (is_locked(fsp,conn,(SMB_BIG_UINT)numtowrite,(SMB_BIG_UINT)startpos, WRITE_LOCK,False)) { + END_PROFILE(SMBwrite); + return ERROR_DOS(ERRDOS,ERRlock); + } - numtowrite = SVAL(inbuf,smb_vwv1); - startpos = IVAL(inbuf,smb_vwv2); - data = smb_buf(inbuf) + 3; + /* + * 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) { + /* + * This is actually an allocate call, and set EOF. JRA. + */ + nwritten = vfs_allocate_file_space(fsp, (SMB_OFF_T)startpos); + if (nwritten < 0) { + END_PROFILE(SMBwrite); + return ERROR_NT(NT_STATUS_DISK_FULL); + } + nwritten = vfs_set_filelen(fsp, (SMB_OFF_T)startpos); + if (nwritten < 0) { + END_PROFILE(SMBwrite); + return ERROR_NT(NT_STATUS_DISK_FULL); + } + } else + nwritten = write_file(fsp,data,startpos,numtowrite); - if (is_locked(fnum,cnum,numtowrite,startpos)) - return(ERROR(ERRDOS,ERRlock)); + if (lp_syncalways(SNUM(conn))) + sync_file(conn,fsp); - seek_file(fnum,startpos); + if(((nwritten == 0) && (numtowrite != 0))||(nwritten < 0)) { + END_PROFILE(SMBwrite); + return(UNIXERROR(ERRDOS,ERRnoaccess)); + } - /* 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); + outsize = set_message(outbuf,1,0,True); - if (lp_syncalways(SNUM(cnum))) - sync_file(fnum); - - if(((nwritten == 0) && (numtowrite != 0))||(nwritten < 0)) - return(UNIXERROR(ERRDOS,ERRnoaccess)); + SSVAL(outbuf,smb_vwv0,nwritten); - outsize = set_message(outbuf,1,0,True); + if (nwritten < (ssize_t)numtowrite) { + SCVAL(outbuf,smb_rcls,ERRHRD); + SSVAL(outbuf,smb_err,ERRdiskfull); + } - SSVAL(outbuf,smb_vwv0,nwritten); + DEBUG(3,("write fnum=%d num=%d wrote=%d\n", fsp->fnum, (int)numtowrite, (int)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); + END_PROFILE(SMBwrite); + return(outsize); } /**************************************************************************** reply to a write and X ****************************************************************************/ -int reply_write_and_X(char *inbuf,char *outbuf,int length,int bufsize) +int reply_write_and_X(connection_struct *conn, 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); + files_struct *fsp = file_fsp(inbuf,smb_vwv2); + SMB_OFF_T startpos = IVAL(inbuf,smb_vwv3); + size_t numtowrite = SVAL(inbuf,smb_vwv10); BOOL write_through = BITSETW(inbuf+smb_vwv7,0); - int cnum; - int nwritten = -1; - int outsize = 0; + ssize_t nwritten = -1; + unsigned int smb_doff = SVAL(inbuf,smb_vwv11); + unsigned int smblen = smb_len(inbuf); char *data; + BOOL large_writeX = ((CVAL(inbuf,smb_wct) == 14) && (smblen > 0xFFFF)); + START_PROFILE(SMBwriteX); - cnum = SVAL(inbuf,smb_tid); + /* If it's an IPC, pass off the pipe handler. */ + if (IS_IPC(conn)) { + END_PROFILE(SMBwriteX); + return reply_pipe_write_and_X(inbuf,outbuf,length,bufsize); + } - CHECK_FNUM(fnum,cnum); - CHECK_WRITE(fnum); - CHECK_ERROR(fnum); + CHECK_FSP(fsp,conn); + CHECK_WRITE(fsp); + + /* Deal with possible LARGE_WRITEX */ + if (large_writeX) + numtowrite |= ((((size_t)SVAL(inbuf,smb_vwv9)) & 1 )<<16); + + if(smb_doff > smblen || (smb_doff + numtowrite > smblen)) { + END_PROFILE(SMBwriteX); + return ERROR_DOS(ERRDOS,ERRbadmem); + } data = smb_base(inbuf) + smb_doff; - if (is_locked(fnum,cnum,smb_dsize,smb_offs)) - return(ERROR(ERRDOS,ERRlock)); + if(CVAL(inbuf,smb_wct) == 14) { +#ifdef LARGE_SMB_OFF_T + /* + * This is a large offset (64 bit) write. + */ + startpos |= (((SMB_OFF_T)IVAL(inbuf,smb_vwv12)) << 32); + +#else /* !LARGE_SMB_OFF_T */ + + /* + * Ensure we haven't been sent a >32 bit offset. + */ + + if(IVAL(inbuf,smb_vwv12) != 0) { + DEBUG(0,("reply_write_and_X - large offset (%x << 32) used and we don't support \ +64 bit offsets.\n", (unsigned int)IVAL(inbuf,smb_vwv12) )); + END_PROFILE(SMBwriteX); + return ERROR_DOS(ERRDOS,ERRbadaccess); + } + +#endif /* LARGE_SMB_OFF_T */ + } + + if (is_locked(fsp,conn,(SMB_BIG_UINT)numtowrite,(SMB_BIG_UINT)startpos, WRITE_LOCK,False)) { + END_PROFILE(SMBwriteX); + return ERROR_DOS(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) + if(numtowrite == 0) nwritten = 0; else - nwritten = write_file(fnum,data,smb_dsize); + nwritten = write_file(fsp,data,startpos,numtowrite); - if(((nwritten == 0) && (smb_dsize != 0))||(nwritten < 0)) + if(((nwritten == 0) && (numtowrite != 0))||(nwritten < 0)) { + END_PROFILE(SMBwriteX); return(UNIXERROR(ERRDOS,ERRnoaccess)); + } - outsize = set_message(outbuf,6,0,True); + 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; + if (large_writeX) + SSVAL(outbuf,smb_vwv4,(nwritten>>16)&1); + + if (nwritten < (ssize_t)numtowrite) { + SCVAL(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)); + DEBUG(3,("writeX fnum=%d num=%d wrote=%d\n", + fsp->fnum, (int)numtowrite, (int)nwritten)); - chain_fnum = fnum; + if (lp_syncalways(SNUM(conn)) || write_through) + sync_file(conn,fsp); - 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); + END_PROFILE(SMBwriteX); + return chain_reply(inbuf,outbuf,length,bufsize); } /**************************************************************************** reply to a lseek ****************************************************************************/ -int reply_lseek(char *inbuf,char *outbuf) + +int reply_lseek(connection_struct *conn, char *inbuf,char *outbuf, int size, int dum_buffsize) { - int cnum,fnum; - uint32 startpos; - int32 res= -1; + SMB_OFF_T startpos; + SMB_OFF_T res= -1; int mode,umode; int outsize = 0; - - cnum = SVAL(inbuf,smb_tid); - fnum = GETFNUM(inbuf,smb_vwv0); + files_struct *fsp = file_fsp(inbuf,smb_vwv0); + START_PROFILE(SMBlseek); - CHECK_FNUM(fnum,cnum); - CHECK_ERROR(fnum); + CHECK_FSP(fsp,conn); + + flush_write_cache(fsp, SEEK_FLUSH); mode = SVAL(inbuf,smb_vwv1) & 3; - startpos = IVAL(inbuf,smb_vwv2); + startpos = IVALS(inbuf,smb_vwv2); - switch (mode & 3) - { + switch (mode) { case 0: umode = SEEK_SET; break; case 1: umode = SEEK_CUR; break; case 2: umode = SEEK_END; break; default: umode = SEEK_SET; break; + } + + if((res = conn->vfs_ops.lseek(fsp,fsp->fd,startpos,umode)) == -1) { + /* + * Check for the special case where a seek before the start + * of the file sets the offset to zero. Added in the CIFS spec, + * section 4.2.7. + */ + + if(errno == EINVAL) { + SMB_OFF_T current_pos = startpos; + + if(umode == SEEK_CUR) { + + if((current_pos = conn->vfs_ops.lseek(fsp,fsp->fd,0,SEEK_CUR)) == -1) { + END_PROFILE(SMBlseek); + return(UNIXERROR(ERRDOS,ERRnoaccess)); + } + + current_pos += startpos; + + } else if (umode == SEEK_END) { + + SMB_STRUCT_STAT sbuf; + + if(vfs_fstat(fsp,fsp->fd, &sbuf) == -1) { + END_PROFILE(SMBlseek); + return(UNIXERROR(ERRDOS,ERRnoaccess)); + } + + current_pos += sbuf.st_size; + } + + if(current_pos < 0) + res = conn->vfs_ops.lseek(fsp,fsp->fd,0,SEEK_SET); } - - res = lseek(Files[fnum].fd,startpos,umode); - Files[fnum].pos = res; + + if(res == -1) { + END_PROFILE(SMBlseek); + return(UNIXERROR(ERRDOS,ERRnoaccess)); + } + } + + fsp->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)); + SIVAL(outbuf,smb_vwv0,res); + DEBUG(3,("lseek fnum=%d ofs=%.0f newpos = %.0f mode=%d\n", + fsp->fnum, (double)startpos, (double)res, mode)); + + END_PROFILE(SMBlseek); 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); +int reply_flush(connection_struct *conn, char *inbuf,char *outbuf, int size, int dum_buffsize) +{ + int outsize = set_message(outbuf,0,0,True); + files_struct *fsp = file_fsp(inbuf,smb_vwv0); + START_PROFILE(SMBflush); - DEBUG(3,("%s flush fnum=%d\n",timestring(),fnum)); - return(outsize); + CHECK_FSP(fsp,conn); + + if (!fsp) { + file_sync_all(conn); + } else { + sync_file(conn,fsp); + } + + DEBUG(3,("flush\n")); + END_PROFILE(SMBflush); + return(outsize); } /**************************************************************************** reply to a exit ****************************************************************************/ -int reply_exit(char *inbuf,char *outbuf) +int reply_exit(connection_struct *conn, + char *inbuf,char *outbuf, int dum_size, int dum_buffsize) { - int outsize = set_message(outbuf,0,0,True); - DEBUG(3,("%s exit\n",timestring())); - - return(outsize); + int outsize; + START_PROFILE(SMBexit); + outsize = set_message(outbuf,0,0,True); + + DEBUG(3,("exit\n")); + + END_PROFILE(SMBexit); + return(outsize); } /**************************************************************************** - reply to a close + Reply to a close - has to deal with closing a directory opened by NT SMB's. ****************************************************************************/ -int reply_close(char *inbuf,char *outbuf) +int reply_close(connection_struct *conn, char *inbuf,char *outbuf, int size, + int dum_buffsize) { - int fnum,cnum; - int outsize = 0; - time_t mtime; - int32 eclass = 0, err = 0; + int outsize = 0; + time_t mtime; + int32 eclass = 0, err = 0; + files_struct *fsp = NULL; + START_PROFILE(SMBclose); + + outsize = set_message(outbuf,0,0,True); + + /* If it's an IPC, pass off to the pipe handler. */ + if (IS_IPC(conn)) { + END_PROFILE(SMBclose); + return reply_pipe_close(conn, inbuf,outbuf); + } - outsize = set_message(outbuf,0,0,True); + fsp = file_fsp(inbuf,smb_vwv0); - cnum = SVAL(inbuf,smb_tid); + /* + * We can only use CHECK_FSP if we know it's not a directory. + */ - fnum = GETFNUM(inbuf,smb_vwv0); - CHECK_FNUM(fnum,cnum); + if(!fsp || (fsp->conn != conn)) { + END_PROFILE(SMBclose); + return ERROR_DOS(ERRDOS,ERRbadfid); + } - if(HAS_CACHED_ERROR(fnum)) { - eclass = Files[fnum].wbmpx_ptr->wr_errclass; - err = Files[fnum].wbmpx_ptr->wr_error; - } + if(fsp->is_directory) { + /* + * Special case - close NT SMB directory handle. + */ + DEBUG(3,("close %s fnum=%d\n", fsp->is_directory ? "directory" : "stat file open", fsp->fnum)); + close_file(fsp,True); + } else { + /* + * Close ordinary file. + */ + int close_err; + pstring file_name; + + /* Save the name for time set in close. */ + pstrcpy( file_name, fsp->fsp_name); + + DEBUG(3,("close fd=%d fnum=%d (numopen=%d)\n", + fsp->fd, fsp->fnum, + conn->num_files_open)); + + /* + * close_file() returns the unix errno if an error + * was detected on close - normally this is due to + * a disk full error. If not then it was probably an I/O error. + */ + + if((close_err = close_file(fsp,True)) != 0) { + errno = close_err; + END_PROFILE(SMBclose); + return (UNIXERROR(ERRHRD,ERRgeneral)); + } - mtime = make_unix_date3(inbuf+smb_vwv1); + /* + * Now take care of any time sent in the close. + */ - close_file(fnum); + mtime = make_unix_date3(inbuf+smb_vwv1); + + /* try and set the date */ + set_filetime(conn, file_name, mtime); - /* try and set the date */ - set_filetime(Files[fnum].name,mtime); + } - /* We have a cached error */ - if(eclass || err) - return(ERROR(eclass,err)); + /* We have a cached error */ + if(eclass || err) { + END_PROFILE(SMBclose); + return ERROR_DOS(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); + END_PROFILE(SMBclose); + return(outsize); } /**************************************************************************** reply to a writeclose (Core+ protocol) ****************************************************************************/ -int reply_writeclose(char *inbuf,char *outbuf) + +int reply_writeclose(connection_struct *conn, + char *inbuf,char *outbuf, int size, int dum_buffsize) { - int cnum,numtowrite,fnum; - int nwritten = -1; - int outsize = 0; - int startpos; - char *data; - time_t mtime; + size_t numtowrite; + ssize_t nwritten = -1; + int outsize = 0; + int close_err = 0; + SMB_OFF_T startpos; + char *data; + time_t mtime; + files_struct *fsp = file_fsp(inbuf,smb_vwv0); + START_PROFILE(SMBwriteclose); + + CHECK_FSP(fsp,conn); + CHECK_WRITE(fsp); + + 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(fsp,conn,(SMB_BIG_UINT)numtowrite,(SMB_BIG_UINT)startpos, WRITE_LOCK,False)) { + END_PROFILE(SMBwriteclose); + return ERROR_DOS(ERRDOS,ERRlock); + } - cnum = SVAL(inbuf,smb_tid); - fnum = GETFNUM(inbuf,smb_vwv0); - - CHECK_FNUM(fnum,cnum); - CHECK_WRITE(fnum); - CHECK_ERROR(fnum); + nwritten = write_file(fsp,data,startpos,numtowrite); - numtowrite = SVAL(inbuf,smb_vwv1); - startpos = IVAL(inbuf,smb_vwv2); - mtime = make_unix_date3(inbuf+smb_vwv4); - data = smb_buf(inbuf) + 1; + set_filetime(conn, fsp->fsp_name,mtime); - if (is_locked(fnum,cnum,numtowrite,startpos)) - return(ERROR(ERRDOS,ERRlock)); - - seek_file(fnum,startpos); - - nwritten = write_file(fnum,data,numtowrite); + close_err = close_file(fsp,True); - 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)); + DEBUG(3,("writeclose fnum=%d num=%d wrote=%d (numopen=%d)\n", + fsp->fnum, (int)numtowrite, (int)nwritten, + conn->num_files_open)); - if (nwritten <= 0) - return(UNIXERROR(ERRDOS,ERRnoaccess)); - - outsize = set_message(outbuf,1,0,True); + if (nwritten <= 0) { + END_PROFILE(SMBwriteclose); + return(UNIXERROR(ERRDOS,ERRnoaccess)); + } + + if(close_err != 0) { + errno = close_err; + END_PROFILE(SMBwriteclose); + return(UNIXERROR(ERRHRD,ERRgeneral)); + } + + outsize = set_message(outbuf,1,0,True); - SSVAL(outbuf,smb_vwv0,nwritten); - return(outsize); + SSVAL(outbuf,smb_vwv0,nwritten); + END_PROFILE(SMBwriteclose); + return(outsize); } /**************************************************************************** reply to a lock ****************************************************************************/ -int reply_lock(char *inbuf,char *outbuf) +int reply_lock(connection_struct *conn, + char *inbuf,char *outbuf, int length, int dum_buffsize) { - 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)); + int outsize = set_message(outbuf,0,0,True); + SMB_BIG_UINT count,offset; + NTSTATUS status; + files_struct *fsp = file_fsp(inbuf,smb_vwv0); + START_PROFILE(SMBlock); + + CHECK_FSP(fsp,conn); + + release_level_2_oplocks_on_change(fsp); + + count = (SMB_BIG_UINT)IVAL(inbuf,smb_vwv1); + offset = (SMB_BIG_UINT)IVAL(inbuf,smb_vwv3); + + DEBUG(3,("lock fd=%d fnum=%d offset=%.0f count=%.0f\n", + fsp->fd, fsp->fnum, (double)offset, (double)count)); + + status = do_lock_spin(fsp, conn, SVAL(inbuf,smb_pid), count, offset, WRITE_LOCK); + if (NT_STATUS_V(status)) { + if (lp_blocking_locks(SNUM(conn))) { + /* + * A blocking lock was requested. Package up + * this smb into a queued request and push it + * onto the blocking lock queue. + */ + if(push_blocking_lock_request(inbuf, length, -1, 0)) { + END_PROFILE(SMBlock); + return -1; + } + } + END_PROFILE(SMBlock); + return ERROR_NT(status); + } - if(!do_lock( fnum, cnum, count, offset, &eclass, &ecode)) - return (ERROR(eclass,ecode)); - - return(outsize); + END_PROFILE(SMBlock); + return(outsize); } /**************************************************************************** reply to a unlock ****************************************************************************/ -int reply_unlock(char *inbuf,char *outbuf) +int reply_unlock(connection_struct *conn, char *inbuf,char *outbuf, int size, + int dum_buffsize) { - 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); + int outsize = set_message(outbuf,0,0,True); + SMB_BIG_UINT count,offset; + NTSTATUS status; + files_struct *fsp = file_fsp(inbuf,smb_vwv0); + START_PROFILE(SMBunlock); - if(!do_unlock(fnum, cnum, count, offset, &eclass, &ecode)) - return (ERROR(eclass,ecode)); + CHECK_FSP(fsp,conn); + + count = (SMB_BIG_UINT)IVAL(inbuf,smb_vwv1); + offset = (SMB_BIG_UINT)IVAL(inbuf,smb_vwv3); + + status = do_unlock(fsp, conn, SVAL(inbuf,smb_pid), count, offset); + if (NT_STATUS_V(status)) { + END_PROFILE(SMBunlock); + return ERROR_NT(status); + } - 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); + DEBUG( 3, ( "unlock fd=%d fnum=%d offset=%.0f count=%.0f\n", + fsp->fd, fsp->fnum, (double)offset, (double)count ) ); + + END_PROFILE(SMBunlock); + return(outsize); } /**************************************************************************** reply to a tdis ****************************************************************************/ -int reply_tdis(char *inbuf,char *outbuf) +int reply_tdis(connection_struct *conn, + char *inbuf,char *outbuf, int dum_size, int dum_buffsize) { - int cnum, uid; - int outsize = set_message(outbuf,0,0,True); - - cnum = SVAL(inbuf,smb_tid); - uid = SVAL(inbuf,smb_uid); + int outsize = set_message(outbuf,0,0,True); + uint16 vuid; + START_PROFILE(SMBtdis); - Connections[cnum].used = False; + vuid = SVAL(inbuf,smb_uid); - close_cnum(cnum,uid); - - DEBUG(3,("%s tdis cnum=%d\n",timestring(),cnum)); + if (!conn) { + DEBUG(4,("Invalid connection in tdis\n")); + END_PROFILE(SMBtdis); + return ERROR_DOS(ERRSRV,ERRinvnid); + } + + conn->used = False; - return outsize; + close_cnum(conn,vuid); + + END_PROFILE(SMBtdis); + return outsize; } @@ -2073,316 +2472,438 @@ int reply_tdis(char *inbuf,char *outbuf) /**************************************************************************** reply to a echo ****************************************************************************/ -int reply_echo(char *inbuf,char *outbuf) +int reply_echo(connection_struct *conn, + char *inbuf,char *outbuf, int dum_size, int dum_buffsize) { - 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); + int smb_reverb = SVAL(inbuf,smb_vwv0); + int seq_num; + unsigned int data_len = smb_buflen(inbuf); + int outsize = set_message(outbuf,1,data_len,True); + START_PROFILE(SMBecho); - cnum = SVAL(inbuf,smb_tid); + data_len = MIN(data_len, (sizeof(inbuf)-(smb_buf(inbuf)-inbuf))); - 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); - /* 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; + } - 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); - for (seq_num =1 ; seq_num <= smb_reverb ; seq_num++) - { - SSVAL(outbuf,smb_vwv0,seq_num); + smb_setlen(outbuf,outsize - 4); - smb_setlen(outbuf,outsize - 4); + if (!send_smb(smbd_server_fd(),outbuf)) + exit_server("reply_echo: send_smb failed."); + } - send_smb(Client,outbuf); - } + DEBUG(3,("echo %d times\n", smb_reverb)); - DEBUG(3,("%s echo %d times cnum=%d\n",timestring(),smb_reverb,cnum)); + smb_echo_count++; - return -1; + END_PROFILE(SMBecho); + return -1; } /**************************************************************************** reply to a printopen ****************************************************************************/ -int reply_printopen(char *inbuf,char *outbuf) +int reply_printopen(connection_struct *conn, + char *inbuf,char *outbuf, int dum_size, int dum_buffsize) { - 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)); + int outsize = 0; + files_struct *fsp; + START_PROFILE(SMBsplopen); + + if (!CAN_PRINT(conn)) { + END_PROFILE(SMBsplopen); + return ERROR_DOS(ERRDOS,ERRnoaccess); + } - open_file(fnum,cnum,fname2,O_WRONLY | O_CREAT | O_TRUNC, - unix_mode(cnum,0)); + /* Open for exclusive use, write only. */ + fsp = print_fsp_open(conn, NULL); - if (!Files[fnum].open) - return(UNIXERROR(ERRDOS,ERRnoaccess)); + if (!fsp) { + END_PROFILE(SMBsplopen); + 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)); + outsize = set_message(outbuf,1,0,True); + SSVAL(outbuf,smb_vwv0,fsp->fnum); - return(outsize); + DEBUG(3,("openprint fd=%d fnum=%d\n", + fsp->fd, fsp->fnum)); + + END_PROFILE(SMBsplopen); + return(outsize); } /**************************************************************************** reply to a printclose ****************************************************************************/ -int reply_printclose(char *inbuf,char *outbuf) +int reply_printclose(connection_struct *conn, + char *inbuf,char *outbuf, int dum_size, int dum_buffsize) { - int fnum,cnum; - int outsize = set_message(outbuf,0,0,True); - - cnum = SVAL(inbuf,smb_tid); - fnum = GETFNUM(inbuf,smb_vwv0); + int outsize = set_message(outbuf,0,0,True); + files_struct *fsp = file_fsp(inbuf,smb_vwv0); + int close_err = 0; + START_PROFILE(SMBsplclose); - CHECK_FNUM(fnum,cnum); - CHECK_ERROR(fnum); + CHECK_FSP(fsp,conn); - if (!CAN_PRINT(cnum)) - return(ERROR(ERRDOS,ERRnoaccess)); - - close_file(fnum); + if (!CAN_PRINT(conn)) { + END_PROFILE(SMBsplclose); + return ERROR_DOS(ERRDOS,ERRnoaccess); + } - DEBUG(3,("%s printclose fd=%d fnum=%d cnum=%d\n",timestring(),Files[fnum].fd,fnum,cnum)); + DEBUG(3,("printclose fd=%d fnum=%d\n", + fsp->fd,fsp->fnum)); - return(outsize); + close_err = close_file(fsp,True); + + if(close_err != 0) { + errno = close_err; + END_PROFILE(SMBsplclose); + return(UNIXERROR(ERRHRD,ERRgeneral)); + } + + END_PROFILE(SMBsplclose); + return(outsize); } /**************************************************************************** reply to a printqueue ****************************************************************************/ -int reply_printqueue(char *inbuf,char *outbuf) +int reply_printqueue(connection_struct *conn, + char *inbuf,char *outbuf, int dum_size, int dum_buffsize) { - 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 + int outsize = set_message(outbuf,2,3,True); + int max_count = SVAL(inbuf,smb_vwv0); + int start_index = SVAL(inbuf,smb_vwv1); + START_PROFILE(SMBsplretq); + + /* we used to allow the client to get the cnum wrong, but that + is really quite gross and only worked when there was only + one printer - I think we should now only accept it if they + get it right (tridge) */ + if (!CAN_PRINT(conn)) { + END_PROFILE(SMBsplretq); + return ERROR_DOS(ERRDOS,ERRnoaccess); + } - SSVAL(outbuf,smb_vwv0,0); - SSVAL(outbuf,smb_vwv1,0); - CVAL(smb_buf(outbuf),0) = 1; - SSVAL(smb_buf(outbuf),1,0); + SSVAL(outbuf,smb_vwv0,0); + SSVAL(outbuf,smb_vwv1,0); + SCVAL(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; + DEBUG(3,("printqueue start_index=%d max_count=%d\n", + start_index, max_count)); - 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); + { + print_queue_struct *queue = NULL; + print_status_struct status; + char *p = smb_buf(outbuf) + 3; + int count = print_queue_status(SNUM(conn), &queue, &status); + 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; - } + for (i=first;i<first+num_to_get;i++) { + put_dos_date2(p,0,queue[i].time); + SCVAL(p,4,(queue[i].status==LPQ_PRINTING?2:3)); + SSVAL(p,5, queue[i].job); + SIVAL(p,7,queue[i].size); + SCVAL(p,11,0); + srvstr_push(outbuf, p+12, queue[i].fs_user, 16, STR_ASCII); + 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 (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)); + SCVAL(smb_buf(outbuf),0,1); + SSVAL(smb_buf(outbuf),1,28*count); + } - if (queue) free(queue); + SAFE_FREE(queue); - DEBUG(3,("%d entries returned in queue\n",count)); - } + DEBUG(3,("%d entries returned in queue\n",count)); + } - return(outsize); + END_PROFILE(SMBsplretq); + return(outsize); } /**************************************************************************** reply to a printwrite ****************************************************************************/ -int reply_printwrite(char *inbuf,char *outbuf) +int reply_printwrite(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize) { - int cnum,numtowrite,fnum; + int numtowrite; int outsize = set_message(outbuf,0,0,True); char *data; + files_struct *fsp = file_fsp(inbuf,smb_vwv0); + START_PROFILE(SMBsplwr); - cnum = SVAL(inbuf,smb_tid); - - if (!CAN_PRINT(cnum)) - return(ERROR(ERRDOS,ERRnoaccess)); - - fnum = GETFNUM(inbuf,smb_vwv0); + if (!CAN_PRINT(conn)) { + END_PROFILE(SMBsplwr); + return ERROR_DOS(ERRDOS,ERRnoaccess); + } - CHECK_FNUM(fnum,cnum); - CHECK_WRITE(fnum); - CHECK_ERROR(fnum); + CHECK_FSP(fsp,conn); + CHECK_WRITE(fsp); numtowrite = SVAL(smb_buf(inbuf),1); data = smb_buf(inbuf) + 3; - if (write_file(fnum,data,numtowrite) != numtowrite) + if (write_file(fsp,data,-1,numtowrite) != numtowrite) { + END_PROFILE(SMBsplwr); return(UNIXERROR(ERRDOS,ERRnoaccess)); + } + + DEBUG( 3, ( "printwrite fnum=%d num=%d\n", fsp->fnum, numtowrite ) ); - DEBUG(3,("%s printwrite fnum=%d cnum=%d num=%d\n",timestring(),fnum,cnum,numtowrite)); - + END_PROFILE(SMBsplwr); return(outsize); } /**************************************************************************** - reply to a mkdir + The guts of the mkdir command, split out so it may be called by the NT SMB + code. ****************************************************************************/ -int reply_mkdir(char *inbuf,char *outbuf) +NTSTATUS mkdir_internal(connection_struct *conn, pstring directory) { - 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); + BOOL bad_path = False; + SMB_STRUCT_STAT sbuf; + int ret= -1; + + unix_convert(directory,conn,0,&bad_path,&sbuf); + + if (check_name(directory, conn)) + ret = vfs_mkdir(conn,directory,unix_mode(conn,aDIR,directory)); + + if (ret == -1) { + NTSTATUS nterr = set_bad_path_error(errno, bad_path); + if (!NT_STATUS_IS_OK(nterr)) + return nterr; + return map_nt_error_from_unix(errno); + } + + return NT_STATUS_OK; } +/**************************************************************************** + Reply to a mkdir. +****************************************************************************/ + +int reply_mkdir(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize) +{ + pstring directory; + int outsize; + NTSTATUS status; + START_PROFILE(SMBmkdir); + + srvstr_pull(inbuf, directory, smb_buf(inbuf) + 1, sizeof(directory), -1, STR_TERMINATE); + + status = mkdir_internal(conn, directory); + if (!NT_STATUS_IS_OK(status)) + return ERROR_NT(status); + + outsize = set_message(outbuf,0,0,True); + + DEBUG( 3, ( "mkdir %s ret=%d\n", directory, outsize ) ); + + END_PROFILE(SMBmkdir); + return(outsize); +} /**************************************************************************** - reply to a rmdir + Static function used by reply_rmdir to delete an entire directory + tree recursively. Return False on ok, True on fail. ****************************************************************************/ -int reply_rmdir(char *inbuf,char *outbuf) + +static BOOL recursive_rmdir(connection_struct *conn, char *directory) +{ + char *dname = NULL; + BOOL ret = False; + void *dirptr = OpenDir(conn, directory, False); + + if(dirptr == NULL) + return True; + + while((dname = ReadDirName(dirptr))) { + pstring fullname; + SMB_STRUCT_STAT st; + + if((strcmp(dname, ".") == 0) || (strcmp(dname, "..")==0)) + continue; + + /* Construct the full name. */ + if(strlen(directory) + strlen(dname) + 1 >= sizeof(fullname)) { + errno = ENOMEM; + ret = True; + break; + } + + pstrcpy(fullname, directory); + pstrcat(fullname, "/"); + pstrcat(fullname, dname); + + if(conn->vfs_ops.lstat(conn,fullname, &st) != 0) { + ret = True; + break; + } + + if(st.st_mode & S_IFDIR) { + if(recursive_rmdir(conn, fullname)!=0) { + ret = True; + break; + } + if(vfs_rmdir(conn,fullname) != 0) { + ret = True; + break; + } + } else if(vfs_unlink(conn,fullname) != 0) { + ret = True; + break; + } + } + CloseDir(dirptr); + return ret; +} + +/**************************************************************************** + The internals of the rmdir code - called elsewhere. +****************************************************************************/ + +BOOL rmdir_internals(connection_struct *conn, char *directory) +{ + BOOL ok; + + ok = (vfs_rmdir(conn,directory) == 0); + if(!ok && ((errno == ENOTEMPTY)||(errno == EEXIST)) && lp_veto_files(SNUM(conn))) { + /* + * Check to see if the only thing in this directory are + * vetoed files/directories. If so then delete them and + * retry. If we fail to delete any of them (and we *don't* + * do a recursive delete) then fail the rmdir. + */ + BOOL all_veto_files = True; + char *dname; + void *dirptr = OpenDir(conn, directory, False); + + if(dirptr != NULL) { + int dirpos = TellDir(dirptr); + while ((dname = ReadDirName(dirptr))) { + if((strcmp(dname, ".") == 0) || (strcmp(dname, "..")==0)) + continue; + if(!IS_VETO_PATH(conn, dname)) { + all_veto_files = False; + break; + } + } + + if(all_veto_files) { + SeekDir(dirptr,dirpos); + while ((dname = ReadDirName(dirptr))) { + pstring fullname; + SMB_STRUCT_STAT st; + + if((strcmp(dname, ".") == 0) || (strcmp(dname, "..")==0)) + continue; + + /* Construct the full name. */ + if(strlen(directory) + strlen(dname) + 1 >= sizeof(fullname)) { + errno = ENOMEM; + break; + } + + pstrcpy(fullname, directory); + pstrcat(fullname, "/"); + pstrcat(fullname, dname); + + if(conn->vfs_ops.lstat(conn,fullname, &st) != 0) + break; + if(st.st_mode & S_IFDIR) { + if(lp_recursive_veto_delete(SNUM(conn))) { + if(recursive_rmdir(conn, fullname) != 0) + break; + } + if(vfs_rmdir(conn,fullname) != 0) + break; + } else if(vfs_unlink(conn,fullname) != 0) + break; + } + CloseDir(dirptr); + /* Retry the rmdir */ + ok = (vfs_rmdir(conn,directory) == 0); + } else { + CloseDir(dirptr); + } + } else { + errno = ENOTEMPTY; + } + } + + if (!ok) + DEBUG(3,("rmdir_internals: couldn't remove directory %s : %s\n", directory,strerror(errno))); + + return ok; +} + +/**************************************************************************** + Reply to a rmdir. +****************************************************************************/ + +int reply_rmdir(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize) { pstring directory; - int cnum; int outsize = 0; BOOL ok = False; + BOOL bad_path = False; + SMB_STRUCT_STAT sbuf; + START_PROFILE(SMBrmdir); + + srvstr_pull(inbuf, directory, smb_buf(inbuf) + 1, sizeof(directory), -1, STR_TERMINATE); + + RESOLVE_DFSPATH(directory, conn, inbuf, outbuf) + + unix_convert(directory,conn, NULL,&bad_path,&sbuf); - 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 (check_name(directory,conn)) + { + dptr_closepath(directory,SVAL(inbuf,smb_pid)); + ok = rmdir_internals(conn, directory); + } if (!ok) + { + set_bad_path_error(errno, bad_path); + END_PROFILE(SMBrmdir); return(UNIXERROR(ERRDOS,ERRbadpath)); - + } + outsize = set_message(outbuf,0,0,True); - DEBUG(3,("%s rmdir %s\n",timestring(),directory)); + DEBUG( 3, ( "rmdir %s\n", directory ) ); + END_PROFILE(SMBrmdir); return(outsize); } @@ -2396,26 +2917,26 @@ static BOOL resolve_wildcards(char *name1,char *name2) fstring ext1,ext2; char *p,*p2; - name1 = strrchr(name1,'/'); - name2 = strrchr(name2,'/'); + name1 = strrchr_m(name1,'/'); + name2 = strrchr_m(name2,'/'); if (!name1 || !name2) return(False); - strcpy(root1,name1); - strcpy(root2,name2); - p = strrchr(root1,'.'); + fstrcpy(root1,name1); + fstrcpy(root2,name2); + p = strrchr_m(root1,'.'); if (p) { *p = 0; - strcpy(ext1,p+1); + fstrcpy(ext1,p+1); } else { - strcpy(ext1,""); + fstrcpy(ext1,""); } - p = strrchr(root2,'.'); + p = strrchr_m(root2,'.'); if (p) { *p = 0; - strcpy(ext2,p+1); + fstrcpy(ext2,p+1); } else { - strcpy(ext2,""); + fstrcpy(ext2,""); } p = root1; @@ -2442,769 +2963,1289 @@ static BOOL resolve_wildcards(char *name1,char *name2) if (*p) p++; } - strcpy(name2,root2); + pstrcpy(name2,root2); if (ext2[0]) { - strcat(name2,"."); - strcat(name2,ext2); + pstrcat(name2,"."); + pstrcat(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 + The guts of the rename command, split out so it may be called by the NT SMB + code. ****************************************************************************/ -int reply_mv(char *inbuf,char *outbuf) + +NTSTATUS rename_internals(connection_struct *conn, char *name, char *newname, BOOL replace_if_exists) { - 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; + pstring directory; + pstring mask; + pstring newname_last_component; + char *p; + BOOL has_wild; + BOOL bad_path1 = False; + BOOL bad_path2 = False; + int count=0; + NTSTATUS error = NT_STATUS_OK; + BOOL rc = True; + SMB_STRUCT_STAT sbuf1, sbuf2; + + *directory = *mask = 0; + + rc = unix_convert(name,conn,0,&bad_path1,&sbuf1); + unix_convert(newname,conn,newname_last_component,&bad_path2,&sbuf2); + + /* + * Split the old name into directory and last component + * strings. Note that unix_convert may have stripped off a + * leading ./ from both name and newname if the rename is + * at the root of the share. We need to make sure either both + * name and newname contain a / character or neither of them do + * as this is checked in resolve_wildcards(). + */ + + p = strrchr_m(name,'/'); + if (!p) { + pstrcpy(directory,"."); + pstrcpy(mask,name); + } else { + *p = 0; + pstrcpy(directory,name); + pstrcpy(mask,p+1); + *p = '/'; /* Replace needed for exceptional test below. */ + } - *directory = *mask = 0; + /* + * We should only check the mangled cache + * here if unix_convert failed. This means + * that the path in 'mask' doesn't exist + * on the file system and so we need to look + * for a possible mangle. This patch from + * Tine Smukavec <valentin.smukavec@hermes.si>. + */ + + if (!rc && mangle_is_mangled(mask)) + mangle_check_cache( mask ); + + has_wild = ms_has_wild(mask); + + if (!has_wild) { + /* + * No wildcards - just process the one file. + */ + BOOL is_short_name = mangle_is_8_3(name, True); + + /* Add a terminating '/' to the directory name. */ + pstrcat(directory,"/"); + pstrcat(directory,mask); + + /* Ensure newname contains a '/' also */ + if(strrchr_m(newname,'/') == 0) { + pstring tmpstr; + + pstrcpy(tmpstr, "./"); + pstrcat(tmpstr, newname); + pstrcpy(newname, tmpstr); + } + + DEBUG(3,("rename_internals: case_sensitive = %d, case_preserve = %d, short case preserve = %d, \ +directory = %s, newname = %s, newname_last_component = %s, is_8_3 = %d\n", + case_sensitive, case_preserve, short_case_preserve, directory, + newname, newname_last_component, is_short_name)); + + /* + * Check for special case with case preserving and not + * case sensitive, if directory and newname are identical, + * and the old last component differs from the original + * last component only by case, then we should allow + * the rename (user is trying to change the case of the + * filename). + */ + if((case_sensitive == False) && + (((case_preserve == True) && + (is_short_name == False)) || + ((short_case_preserve == True) && + (is_short_name == True))) && + strcsequal(directory, newname)) { + pstring newname_modified_last_component; + + /* + * Get the last component of the modified name. + * Note that we guarantee that newname contains a '/' + * character above. + */ + p = strrchr_m(newname,'/'); + pstrcpy(newname_modified_last_component,p+1); + + if(strcsequal(newname_modified_last_component, + newname_last_component) == False) { + /* + * Replace the modified last component with + * the original. + */ + pstrcpy(p+1, newname_last_component); + } + } + + resolve_wildcards(directory,newname); + + /* + * The source object must exist. + */ + + if (!vfs_object_exist(conn, directory, &sbuf1)) { + DEBUG(3,("rename_internals: source doesn't exist doing rename %s -> %s\n", + directory,newname)); + + if (errno == ENOTDIR || errno == EISDIR || errno == ENOENT) { + /* + * Must return different errors depending on whether the parent + * directory existed or not. + */ + + p = strrchr_m(directory, '/'); + if (!p) + return NT_STATUS_OBJECT_NAME_NOT_FOUND; + *p = '\0'; + if (vfs_object_exist(conn, directory, NULL)) + return NT_STATUS_OBJECT_NAME_NOT_FOUND; + return NT_STATUS_OBJECT_PATH_NOT_FOUND; + } + error = map_nt_error_from_unix(errno); + DEBUG(3,("rename_internals: Error %s rename %s -> %s\n", + nt_errstr(error), directory,newname)); + + return error; + } - 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); + error = can_rename(directory,conn,&sbuf1); - p = strrchr(name,'/'); - if (!p) { - strcpy(directory,"./"); - strcpy(mask,name); - } else { - *p = 0; - strcpy(directory,name); - strcpy(mask,p+1); - } + if (!NT_STATUS_IS_OK(error)) { + DEBUG(3,("rename_internals: Error %s rename %s -> %s\n", + nt_errstr(error), directory,newname)); + return error; + } + + /* + * If the src and dest names are identical - including case, + * don't do the rename, just return success. + */ - if (is_mangled(mask)) - check_mangled_stack(mask); + if (strcsequal(directory, newname)) { + DEBUG(3,("rename_internals: identical names in rename %s - returning success\n", directory)); + return NT_STATUS_OK; + } - has_wild = strchr(mask,'*') || strchr(mask,'?'); + if(!replace_if_exists && vfs_object_exist(conn,newname,NULL)) { + DEBUG(3,("rename_internals: dest exists doing rename %s -> %s\n", + directory,newname)); + return NT_STATUS_OBJECT_NAME_COLLISION; + } - 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(conn->vfs_ops.rename(conn,directory, newname) == 0) { + DEBUG(3,("rename_internals: succeeded doing rename on %s -> %s\n", + directory,newname)); + return NT_STATUS_OK; + } - if (check_name(directory,cnum)) - dirptr = OpenDir(directory); + if (errno == ENOTDIR || errno == EISDIR) + error = NT_STATUS_OBJECT_NAME_COLLISION; + else + error = map_nt_error_from_unix(errno); + + DEBUG(3,("rename_internals: Error %s rename %s -> %s\n", + nt_errstr(error), directory,newname)); + + return error; + } else { + /* + * Wildcards - process each file that matches. + */ + void *dirptr = NULL; + char *dname; + pstring destname; + + if (check_name(directory,conn)) + dirptr = OpenDir(conn, directory, True); + + if (dirptr) { + error = NT_STATUS_OBJECT_NAME_NOT_FOUND; + + if (strequal(mask,"????????.???")) + pstrcpy(mask,"*"); + + while ((dname = ReadDirName(dirptr))) { + pstring fname; + + pstrcpy(fname,dname); + + if(!mask_match(fname, mask, case_sensitive)) + continue; + + error = NT_STATUS_ACCESS_DENIED; + slprintf(fname,sizeof(fname)-1,"%s/%s",directory,dname); + if (!vfs_object_exist(conn, fname, &sbuf1)) { + error = NT_STATUS_OBJECT_NAME_NOT_FOUND; + DEBUG(6,("rename %s failed. Error %s\n", fname, nt_errstr(error))); + continue; + } + error = can_rename(fname,conn,&sbuf1); + if (!NT_STATUS_IS_OK(error)) { + DEBUG(6,("rename %s refused\n", fname)); + continue; + } + pstrcpy(destname,newname); + + if (!resolve_wildcards(fname,destname)) { + DEBUG(6,("resolve_wildcards %s %s failed\n", + fname, destname)); + continue; + } + + if (!replace_if_exists && + vfs_file_exist(conn,destname, NULL)) { + DEBUG(6,("file_exist %s\n", destname)); + error = NT_STATUS_OBJECT_NAME_COLLISION; + continue; + } + + if (!conn->vfs_ops.rename(conn,fname,destname)) + count++; + DEBUG(3,("rename_internals: doing rename on %s -> %s\n",fname,destname)); + } + CloseDir(dirptr); + } + } + + if (count == 0 && NT_STATUS_IS_OK(error)) { + error = map_nt_error_from_unix(errno); + } + + return error; +} - if (dirptr) - { - error = ERRbadfile; +/**************************************************************************** + Reply to a mv. +****************************************************************************/ - if (strequal(mask,"????????.???")) - strcpy(mask,"*"); +int reply_mv(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, + int dum_buffsize) +{ + int outsize = 0; + pstring name; + pstring newname; + char *p; + NTSTATUS status; - while ((dname = ReadDirName(dirptr))) - { - pstring fname; - strcpy(fname,dname); - - if(!mask_match(fname, mask, case_sensitive, False)) continue; + START_PROFILE(SMBmv); - 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); + p = smb_buf(inbuf) + 1; + p += srvstr_pull(inbuf, name, p, sizeof(name), -1, STR_TERMINATE); + p++; + p += srvstr_pull(inbuf, newname, p, sizeof(newname), -1, STR_TERMINATE); + + RESOLVE_DFSPATH(name, conn, inbuf, outbuf); + RESOLVE_DFSPATH(newname, conn, inbuf, outbuf); + + DEBUG(3,("reply_mv : %s -> %s\n",name,newname)); + + status = rename_internals(conn, name, newname, False); + if (!NT_STATUS_IS_OK(status)) { + return ERROR_NT(status); + } + + /* + * Win2k needs a changenotify request response before it will + * update after a rename.. + */ + process_pending_change_notify_queue((time_t)0); + outsize = set_message(outbuf,0,0,True); - return(outsize); + END_PROFILE(SMBmv); + 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) + Copy a file as part of a reply_copy. +******************************************************************/ + +static BOOL copy_file(char *src,char *dest1,connection_struct *conn, int ofun, + int count,BOOL target_is_directory, int *err_ret) { - 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); - } + int Access,action; + SMB_STRUCT_STAT src_sbuf, sbuf2; + SMB_OFF_T ret=-1; + files_struct *fsp1,*fsp2; + pstring dest; + + *err_ret = 0; + + pstrcpy(dest,dest1); + if (target_is_directory) { + char *p = strrchr_m(src,'/'); + if (p) + p++; + else + p = src; + pstrcat(dest,"/"); + pstrcat(dest,p); + } - if (!file_exist(src,&st)) return(False); + if (!vfs_file_exist(conn,src,&src_sbuf)) + return(False); - fnum1 = find_free_file(); - if (fnum1<0) return(False); - open_file_shared(fnum1,cnum,src,(DENY_NONE<<4), - 1,0,&Access,&action); + fsp1 = open_file_shared(conn,src,&src_sbuf,SET_DENY_MODE(DENY_NONE)|SET_OPEN_MODE(DOS_OPEN_RDONLY), + (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN),0,0,&Access,&action); - if (!Files[fnum1].open) return(False); + if (!fsp1) + return(False); - if (!target_is_directory && count) - ofun = 1; + if (!target_is_directory && count) + ofun = FILE_EXISTS_OPEN; - 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 (vfs_stat(conn,dest,&sbuf2) == -1) + ZERO_STRUCTP(&sbuf2); - if (!Files[fnum2].open) { - close_file(fnum1); - return(False); - } + fsp2 = open_file_shared(conn,dest,&sbuf2,SET_DENY_MODE(DENY_NONE)|SET_OPEN_MODE(DOS_OPEN_WRONLY), + ofun,src_sbuf.st_mode,0,&Access,&action); - if ((ofun&3) == 1) { - lseek(Files[fnum2].fd,0,SEEK_END); - } + if (!fsp2) { + close_file(fsp1,False); + return(False); + } + + if ((ofun&3) == 1) { + if(conn->vfs_ops.lseek(fsp2,fsp2->fd,0,SEEK_END) == -1) { + DEBUG(0,("copy_file: error - vfs lseek returned error %s\n", strerror(errno) )); + /* + * Stop the copy from occurring. + */ + ret = -1; + src_sbuf.st_size = 0; + } + } - if (st.st_size) - ret = transfer_file(Files[fnum1].fd,Files[fnum2].fd,st.st_size,NULL,0,0); + if (src_sbuf.st_size) + ret = vfs_transfer_file(fsp1, fsp2, src_sbuf.st_size); - close_file(fnum1); - close_file(fnum2); + close_file(fsp1,False); - return(ret == st.st_size); -} + /* Ensure the modtime is set correctly on the destination file. */ + fsp2->pending_modtime = src_sbuf.st_mtime; + /* + * As we are opening fsp1 read-only we only expect + * an error on close on fsp2 if we are out of space. + * Thus we don't look at the error return from the + * close of fsp1. + */ + *err_ret = close_file(fsp2,False); + return(ret == (SMB_OFF_T)src_sbuf.st_size); +} /**************************************************************************** reply to a file copy. ****************************************************************************/ -int reply_copy(char *inbuf,char *outbuf) +int reply_copy(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize) { int outsize = 0; pstring name; - int cnum; pstring directory; pstring mask,newname; char *p; int count=0; int error = ERRnoaccess; + int err = 0; 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; + BOOL bad_path1 = False; + BOOL bad_path2 = False; + BOOL rc = True; + SMB_STRUCT_STAT sbuf1, sbuf2; + START_PROFILE(SMBcopy); *directory = *mask = 0; - cnum = SVAL(inbuf,smb_tid); - - strcpy(name,smb_buf(inbuf)); - strcpy(newname,smb_buf(inbuf) + 1 + strlen(name)); + p = smb_buf(inbuf); + p += srvstr_pull(inbuf, name, p, sizeof(name), -1, STR_TERMINATE); + p += srvstr_pull(inbuf, newname, p, sizeof(newname), -1, STR_TERMINATE); DEBUG(3,("reply_copy : %s -> %s\n",name,newname)); - if (tid2 != cnum) { + if (tid2 != conn->cnum) { /* can't currently handle inter share copies XXXX */ DEBUG(3,("Rejecting inter-share copy\n")); - return(ERROR(ERRSRV,ERRinvdevice)); + END_PROFILE(SMBcopy); + return ERROR_DOS(ERRSRV,ERRinvdevice); } - unix_convert(name,cnum); - unix_convert(newname,cnum); + RESOLVE_DFSPATH(name, conn, inbuf, outbuf); + RESOLVE_DFSPATH(newname, conn, inbuf, outbuf); - target_is_directory = directory_exist(newname,NULL); + rc = unix_convert(name,conn,0,&bad_path1,&sbuf1); + unix_convert(newname,conn,0,&bad_path2,&sbuf2); + + target_is_directory = VALID_STAT_OF_DIR(sbuf2); if ((flags&1) && target_is_directory) { - return(ERROR(ERRDOS,ERRbadfile)); + END_PROFILE(SMBcopy); + return ERROR_DOS(ERRDOS,ERRbadfile); } if ((flags&2) && !target_is_directory) { - return(ERROR(ERRDOS,ERRbadpath)); + END_PROFILE(SMBcopy); + return ERROR_DOS(ERRDOS,ERRbadpath); } - if ((flags&(1<<5)) && directory_exist(name,NULL)) { + if ((flags&(1<<5)) && VALID_STAT_OF_DIR(sbuf1)) { /* wants a tree copy! XXXX */ DEBUG(3,("Rejecting tree copy\n")); - return(ERROR(ERRSRV,ERRerror)); + END_PROFILE(SMBcopy); + return ERROR_DOS(ERRSRV,ERRerror); } - p = strrchr(name,'/'); + p = strrchr_m(name,'/'); if (!p) { - strcpy(directory,"./"); - strcpy(mask,name); + pstrcpy(directory,"./"); + pstrcpy(mask,name); } else { *p = 0; - strcpy(directory,name); - strcpy(mask,p+1); + pstrcpy(directory,name); + pstrcpy(mask,p+1); } - if (is_mangled(mask)) - check_mangled_stack(mask); + /* + * We should only check the mangled cache + * here if unix_convert failed. This means + * that the path in 'mask' doesn't exist + * on the file system and so we need to look + * for a possible mangle. This patch from + * Tine Smukavec <valentin.smukavec@hermes.si>. + */ + + if (!rc && mangle_is_mangled(mask)) + mangle_check_cache( mask ); - has_wild = strchr(mask,'*') || strchr(mask,'?'); + has_wild = ms_has_wild(mask); if (!has_wild) { - strcat(directory,"/"); - strcat(directory,mask); + pstrcat(directory,"/"); + pstrcat(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); + copy_file(directory,newname,conn,ofun, + count,target_is_directory,&err)) count++; + if(!count && err) { + errno = err; + END_PROFILE(SMBcopy); + return(UNIXERROR(ERRHRD,ERRgeneral)); + } + if (!count) exists = vfs_file_exist(conn,directory,NULL); } else { void *dirptr = NULL; char *dname; pstring destname; - if (check_name(directory,cnum)) - dirptr = OpenDir(directory); + if (check_name(directory,conn)) + dirptr = OpenDir(conn, directory, True); - if (dirptr) - { + if (dirptr) { error = ERRbadfile; if (strequal(mask,"????????.???")) - strcpy(mask,"*"); + pstrcpy(mask,"*"); - while ((dname = ReadDirName(dirptr))) - { + while ((dname = ReadDirName(dirptr))) { pstring fname; - strcpy(fname,dname); + pstrcpy(fname,dname); - if(!mask_match(fname, mask, case_sensitive, False)) continue; + if(!mask_match(fname, mask, case_sensitive)) + continue; error = ERRnoaccess; - sprintf(fname,"%s/%s",directory,dname); - strcpy(destname,newname); + slprintf(fname,sizeof(fname)-1, "%s/%s",directory,dname); + pstrcpy(destname,newname); if (resolve_wildcards(fname,destname) && - copy_file(directory,newname,cnum,ofun, - count,target_is_directory)) count++; + copy_file(fname,destname,conn,ofun, + count,target_is_directory,&err)) 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 + if(err) { + /* Error on close... */ + errno = err; + END_PROFILE(SMBcopy); + return(UNIXERROR(ERRHRD,ERRgeneral)); + } + + if (exists) { + END_PROFILE(SMBcopy); + return ERROR_DOS(ERRDOS,error); + } else + { + if((errno == ENOENT) && (bad_path1 || bad_path2)) + { + unix_ERR_class = ERRDOS; + unix_ERR_code = ERRbadpath; + } + END_PROFILE(SMBcopy); return(UNIXERROR(ERRDOS,error)); + } } outsize = set_message(outbuf,1,0,True); SSVAL(outbuf,smb_vwv0,count); + END_PROFILE(SMBcopy); return(outsize); } - - /**************************************************************************** reply to a setdir ****************************************************************************/ -int reply_setdir(char *inbuf,char *outbuf) +int reply_setdir(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize) { - int cnum,snum; + int snum; int outsize = 0; BOOL ok = False; pstring newdir; + START_PROFILE(pathworks_setdir); - 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); + snum = SNUM(conn); + if (!CAN_SETDIR(snum)) { + END_PROFILE(pathworks_setdir); + return ERROR_DOS(ERRDOS,ERRnoaccess); + } + + srvstr_pull(inbuf, newdir, smb_buf(inbuf) + 1, sizeof(newdir), -1, STR_TERMINATE); - if (strlen(newdir) == 0) - ok = True; - else - { - ok = directory_exist(newdir,NULL); - if (ok) - string_set(&Connections[cnum].connectpath,newdir); - } + if (strlen(newdir) == 0) { + ok = True; + } else { + ok = vfs_directory_exist(conn,newdir,NULL); + if (ok) { + string_set(&conn->connectpath,newdir); + } + } - if (!ok) - return(ERROR(ERRDOS,ERRbadpath)); + if (!ok) { + END_PROFILE(pathworks_setdir); + return ERROR_DOS(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)); + SCVAL(outbuf,smb_reh,CVAL(inbuf,smb_reh)); + DEBUG(3,("setdir %s\n", newdir)); + + END_PROFILE(pathworks_setdir); return(outsize); } +/**************************************************************************** + Get a lock pid, dealing with large count requests. +****************************************************************************/ + +uint16 get_lock_pid( char *data, int data_offset, BOOL large_file_format) +{ + if(!large_file_format) + return SVAL(data,SMB_LPID_OFFSET(data_offset)); + else + return SVAL(data,SMB_LARGE_LPID_OFFSET(data_offset)); +} /**************************************************************************** - reply to a lockingX request + Get a lock count, dealing with large count requests. ****************************************************************************/ -int reply_lockingX(char *inbuf,char *outbuf,int length,int bufsize) + +SMB_BIG_UINT get_lock_count( char *data, int data_offset, BOOL large_file_format) { - 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); - } + SMB_BIG_UINT count = 0; - 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)); + if(!large_file_format) { + count = (SMB_BIG_UINT)IVAL(data,SMB_LKLEN_OFFSET(data_offset)); + } else { - chain_fnum = fnum; +#if defined(HAVE_LONGLONG) + count = (((SMB_BIG_UINT) IVAL(data,SMB_LARGE_LKLEN_OFFSET_HIGH(data_offset))) << 32) | + ((SMB_BIG_UINT) IVAL(data,SMB_LARGE_LKLEN_OFFSET_LOW(data_offset))); +#else /* HAVE_LONGLONG */ - if (smb_com2 != 0xFF) - outsize += chain_reply(smb_com2,inbuf,inbuf+smb_off2+4, - outbuf,outbuf+outsize, - length,bufsize); - - chain_fnum = -1; - - return(outsize); + /* + * NT4.x seems to be broken in that it sends large file (64 bit) + * lockingX calls even if the CAP_LARGE_FILES was *not* + * negotiated. For boxes without large unsigned ints truncate the + * lock count by dropping the top 32 bits. + */ + + if(IVAL(data,SMB_LARGE_LKLEN_OFFSET_HIGH(data_offset)) != 0) { + DEBUG(3,("get_lock_count: truncating lock count (high)0x%x (low)0x%x to just low count.\n", + (unsigned int)IVAL(data,SMB_LARGE_LKLEN_OFFSET_HIGH(data_offset)), + (unsigned int)IVAL(data,SMB_LARGE_LKLEN_OFFSET_LOW(data_offset)) )); + SIVAL(data,SMB_LARGE_LKLEN_OFFSET_HIGH(data_offset),0); + } + + count = (SMB_BIG_UINT)IVAL(data,SMB_LARGE_LKLEN_OFFSET_LOW(data_offset)); +#endif /* HAVE_LONGLONG */ + } + + return count; } +#if !defined(HAVE_LONGLONG) +/**************************************************************************** + Pathetically try and map a 64 bit lock offset into 31 bits. I hate Windows :-). +****************************************************************************/ +static uint32 map_lock_offset(uint32 high, uint32 low) +{ + unsigned int i; + uint32 mask = 0; + uint32 highcopy = high; + + /* + * Try and find out how many significant bits there are in high. + */ + + for(i = 0; highcopy; i++) + highcopy >>= 1; + + /* + * We use 31 bits not 32 here as POSIX + * lock offsets may not be negative. + */ + + mask = (~0) << (31 - i); + + if(low & mask) + return 0; /* Fail. */ + + high <<= (31 - i); + + return (high|low); +} +#endif /* !defined(HAVE_LONGLONG) */ /**************************************************************************** - reply to a SMBreadbmpx (read block multiplex) request + Get a lock offset, dealing with large offset requests. ****************************************************************************/ -int reply_readbmpx(char *inbuf,char *outbuf,int length,int bufsize) + +SMB_BIG_UINT get_lock_offset( char *data, int data_offset, BOOL large_file_format, BOOL *err) { - 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; + SMB_BIG_UINT offset = 0; + + *err = False; - /* this function doesn't seem to work - disable by default */ - if (!lp_readbmpx()) - return(ERROR(ERRSRV,ERRuseSTD)); + if(!large_file_format) { + offset = (SMB_BIG_UINT)IVAL(data,SMB_LKOFF_OFFSET(data_offset)); + } else { - outsize = set_message(outbuf,8,0,True); +#if defined(HAVE_LONGLONG) + offset = (((SMB_BIG_UINT) IVAL(data,SMB_LARGE_LKOFF_OFFSET_HIGH(data_offset))) << 32) | + ((SMB_BIG_UINT) IVAL(data,SMB_LARGE_LKOFF_OFFSET_LOW(data_offset))); +#else /* HAVE_LONGLONG */ - cnum = SVAL(inbuf,smb_tid); - fnum = GETFNUM(inbuf,smb_vwv0); + /* + * NT4.x seems to be broken in that it sends large file (64 bit) + * lockingX calls even if the CAP_LARGE_FILES was *not* + * negotiated. For boxes without large unsigned ints mangle the + * lock offset by mapping the top 32 bits onto the lower 32. + */ + + if(IVAL(data,SMB_LARGE_LKOFF_OFFSET_HIGH(data_offset)) != 0) { + uint32 low = IVAL(data,SMB_LARGE_LKOFF_OFFSET_LOW(data_offset)); + uint32 high = IVAL(data,SMB_LARGE_LKOFF_OFFSET_HIGH(data_offset)); + uint32 new_low = 0; + + if((new_low = map_lock_offset(high, low)) == 0) { + *err = True; + return (SMB_BIG_UINT)-1; + } - CHECK_FNUM(fnum,cnum); - CHECK_READ(fnum); - CHECK_ERROR(fnum); + DEBUG(3,("get_lock_offset: truncating lock offset (high)0x%x (low)0x%x to offset 0x%x.\n", + (unsigned int)high, (unsigned int)low, (unsigned int)new_low )); + SIVAL(data,SMB_LARGE_LKOFF_OFFSET_HIGH(data_offset),0); + SIVAL(data,SMB_LARGE_LKOFF_OFFSET_LOW(data_offset),new_low); + } - startpos = IVAL(inbuf,smb_vwv1); - maxcount = SVAL(inbuf,smb_vwv3); - mincount = SVAL(inbuf,smb_vwv4); + offset = (SMB_BIG_UINT)IVAL(data,SMB_LARGE_LKOFF_OFFSET_LOW(data_offset)); +#endif /* HAVE_LONGLONG */ + } - data = smb_buf(outbuf); - pad = ((int)data)%4; - if (pad) pad = 4 - pad; - data += pad; + return offset; +} - max_per_packet = bufsize-(outsize+pad); - tcount = maxcount; - total_read = 0; +/**************************************************************************** + reply to a lockingX request +****************************************************************************/ - if (is_locked(fnum,cnum,maxcount,startpos)) - return(ERROR(ERRDOS,ERRlock)); +int reply_lockingX(connection_struct *conn, char *inbuf,char *outbuf,int length,int bufsize) +{ + files_struct *fsp = file_fsp(inbuf,smb_vwv2); + unsigned char locktype = CVAL(inbuf,smb_vwv3); + unsigned char oplocklevel = CVAL(inbuf,smb_vwv3+1); + uint16 num_ulocks = SVAL(inbuf,smb_vwv6); + uint16 num_locks = SVAL(inbuf,smb_vwv7); + SMB_BIG_UINT count = 0, offset = 0; + uint16 lock_pid; + int32 lock_timeout = IVAL(inbuf,smb_vwv4); + int i; + char *data; + BOOL large_file_format = (locktype & LOCKING_ANDX_LARGE_FILES)?True:False; + BOOL err; + NTSTATUS status; + + START_PROFILE(SMBlockingX); - do - { - int N = MIN(max_per_packet,tcount-total_read); - - nread = read_file(fnum,data,startpos,N,N,-1,False); + CHECK_FSP(fsp,conn); + + data = smb_buf(inbuf); - if (nread <= 0) nread = 0; + if (locktype & (LOCKING_ANDX_CANCEL_LOCK | LOCKING_ANDX_CHANGE_LOCKTYPE)) { + /* we don't support these - and CANCEL_LOCK makes w2k + and XP reboot so I don't really want to be + compatible! (tridge) */ + return ERROR_NT(NT_STATUS_NOT_SUPPORTED); + } + + /* Check if this is an oplock break on a file + we have granted an oplock on. + */ + if ((locktype & LOCKING_ANDX_OPLOCK_RELEASE)) { + /* Client can insist on breaking to none. */ + BOOL break_to_none = (oplocklevel == 0); + + DEBUG(5,("reply_lockingX: oplock break reply (%u) from client for fnum = %d\n", + (unsigned int)oplocklevel, fsp->fnum )); + + /* + * Make sure we have granted an exclusive or batch oplock on this file. + */ + + if(!EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) { + DEBUG(0,("reply_lockingX: Error : oplock break from client for fnum = %d and \ +no oplock granted on this file (%s).\n", fsp->fnum, fsp->fsp_name)); + + /* if this is a pure oplock break request then don't send a reply */ + if (num_locks == 0 && num_ulocks == 0) { + END_PROFILE(SMBlockingX); + return -1; + } else { + END_PROFILE(SMBlockingX); + return ERROR_DOS(ERRDOS,ERRlock); + } + } - if (nread < N) - tcount = total_read + nread; + if (remove_oplock(fsp, break_to_none) == False) { + DEBUG(0,("reply_lockingX: error in removing oplock on file %s\n", + fsp->fsp_name )); + } + + /* if this is a pure oplock break request then don't send a reply */ + if (num_locks == 0 && num_ulocks == 0) { + /* Sanity check - ensure a pure oplock break is not a + chained request. */ + if(CVAL(inbuf,smb_vwv0) != 0xff) + DEBUG(0,("reply_lockingX: Error : pure oplock break is a chained %d request !\n", + (unsigned int)CVAL(inbuf,smb_vwv0) )); + END_PROFILE(SMBlockingX); + return -1; + } + } - 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)); + /* + * We do this check *after* we have checked this is not a oplock break + * response message. JRA. + */ + + release_level_2_oplocks_on_change(fsp); + + /* Data now points at the beginning of the list + of smb_unlkrng structs */ + for(i = 0; i < (int)num_ulocks; i++) { + lock_pid = get_lock_pid( data, i, large_file_format); + count = get_lock_count( data, i, large_file_format); + offset = get_lock_offset( data, i, large_file_format, &err); + + /* + * There is no error code marked "stupid client bug".... :-). + */ + if(err) { + END_PROFILE(SMBlockingX); + return ERROR_DOS(ERRDOS,ERRnoaccess); + } - send_smb(Client,outbuf); + DEBUG(10,("reply_lockingX: unlock start=%.0f, len=%.0f for pid %u, file %s\n", + (double)offset, (double)count, (unsigned int)lock_pid, fsp->fsp_name )); + + status = do_unlock(fsp,conn,lock_pid,count,offset); + if (NT_STATUS_V(status)) { + END_PROFILE(SMBlockingX); + return ERROR_NT(status); + } + } - total_read += nread; - startpos += nread; - } - while (total_read < tcount); + /* Setup the timeout in seconds. */ - return(-1); -} + lock_timeout = ((lock_timeout == -1) ? -1 : lock_timeout/1000); + + /* Now do any requested locks */ + data += ((large_file_format ? 20 : 10)*num_ulocks); + + /* Data now points at the beginning of the list + of smb_lkrng structs */ + + for(i = 0; i < (int)num_locks; i++) { + lock_pid = get_lock_pid( data, i, large_file_format); + count = get_lock_count( data, i, large_file_format); + offset = get_lock_offset( data, i, large_file_format, &err); + + /* + * There is no error code marked "stupid client bug".... :-). + */ + if(err) { + END_PROFILE(SMBlockingX); + return ERROR_DOS(ERRDOS,ERRnoaccess); + } + + DEBUG(10,("reply_lockingX: lock start=%.0f, len=%.0f for pid %u, file %s timeout = %d\n", + (double)offset, (double)count, (unsigned int)lock_pid, + fsp->fsp_name, (int)lock_timeout )); + + status = do_lock_spin(fsp,conn,lock_pid, count,offset, + ((locktype & 1) ? READ_LOCK : WRITE_LOCK)); + if (NT_STATUS_V(status)) { + if ((lock_timeout != 0) && lp_blocking_locks(SNUM(conn))) { + /* + * A blocking lock was requested. Package up + * this smb into a queued request and push it + * onto the blocking lock queue. + */ + if(push_blocking_lock_request(inbuf, length, lock_timeout, i)) { + END_PROFILE(SMBlockingX); + return -1; + } + } + 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) { + /* + * Ensure we don't do a remove on the lock that just failed, + * as under POSIX rules, if we have a lock already there, we + * will delete it (and we shouldn't) ..... + */ + for(i--; i >= 0; i--) { + lock_pid = get_lock_pid( data, i, large_file_format); + count = get_lock_count( data, i, large_file_format); + offset = get_lock_offset( data, i, large_file_format, &err); + + /* + * There is no error code marked "stupid client bug".... :-). + */ + if(err) { + END_PROFILE(SMBlockingX); + return ERROR_DOS(ERRDOS,ERRnoaccess); + } + + do_unlock(fsp,conn,lock_pid,count,offset); + } + END_PROFILE(SMBlockingX); + return ERROR_NT(status); + } + set_message(outbuf,2,0,True); + + DEBUG( 3, ( "lockingX fnum=%d type=%d num_locks=%d num_ulocks=%d\n", + fsp->fnum, (unsigned int)locktype, num_locks, num_ulocks ) ); + + END_PROFILE(SMBlockingX); + return chain_reply(inbuf,outbuf,length,bufsize); +} /**************************************************************************** - reply to a SMBwritebmpx (write block multiplex primary) request + Reply to a SMBreadbmpx (read block multiplex) request. ****************************************************************************/ -int reply_writebmpx(char *inbuf,char *outbuf) + +int reply_readbmpx(connection_struct *conn, char *inbuf,char *outbuf,int length,int bufsize) { - 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); + ssize_t nread = -1; + ssize_t total_read; + char *data; + SMB_OFF_T startpos; + int outsize; + size_t maxcount; + int max_per_packet; + size_t tcount; + int pad; + files_struct *fsp = file_fsp(inbuf,smb_vwv0); + START_PROFILE(SMBreadBmpx); + + /* this function doesn't seem to work - disable by default */ + if (!lp_readbmpx()) { + END_PROFILE(SMBreadBmpx); + return ERROR_DOS(ERRSRV,ERRuseSTD); + } - CHECK_FNUM(fnum,cnum); - CHECK_WRITE(fnum); - CHECK_ERROR(fnum); + outsize = set_message(outbuf,8,0,True); - 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); + CHECK_FSP(fsp,conn); + CHECK_READ(fsp); - data = smb_base(inbuf) + smb_doff; + startpos = IVAL(inbuf,smb_vwv1); + maxcount = SVAL(inbuf,smb_vwv3); - /* 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; + data = smb_buf(outbuf); + pad = ((long)data)%4; + if (pad) + pad = 4 - pad; + data += pad; - if (is_locked(fnum,cnum,tcount,startpos)) - return(ERROR(ERRDOS,ERRlock)); + max_per_packet = bufsize-(outsize+pad); + tcount = maxcount; + total_read = 0; - seek_file(fnum,startpos); - nwritten = write_file(fnum,data,numtowrite); + if (is_locked(fsp,conn,(SMB_BIG_UINT)maxcount,(SMB_BIG_UINT)startpos, READ_LOCK,False)) { + END_PROFILE(SMBreadBmpx); + return ERROR_DOS(ERRDOS,ERRlock); + } - if(lp_syncalways(SNUM(cnum)) || write_through) - sync_file(fnum); + do { + size_t N = MIN(max_per_packet,tcount-total_read); - if(nwritten < numtowrite) - return(UNIXERROR(ERRHRD,ERRdiskfull)); + nread = read_file(fsp,data,startpos,N); - /* 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; - } + if (nread <= 0) + nread = 0; - /* 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); + if (nread < (ssize_t)N) + tcount = total_read + nread; - /* now the secondary */ - outsize = set_message(outbuf,1,0,True); - CVAL(outbuf,smb_com) = SMBwritec; - SSVAL(outbuf,smb_vwv0,nwritten); - } + 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)); - return(outsize); -} + if (!send_smb(smbd_server_fd(),outbuf)) + exit_server("reply_readbmpx: send_smb failed."); + total_read += nread; + startpos += nread; + } while (total_read < (ssize_t)tcount); + + END_PROFILE(SMBreadBmpx); + return(-1); +} /**************************************************************************** - reply to a SMBwritebs (write block multiplex secondary) request + Reply to a SMBsetattrE. ****************************************************************************/ -int reply_writebs(char *inbuf,char *outbuf) + +int reply_setattrE(connection_struct *conn, char *inbuf,char *outbuf, int size, int dum_buffsize) { - 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; + struct utimbuf unix_times; + int outsize = 0; + files_struct *fsp = file_fsp(inbuf,smb_vwv0); + START_PROFILE(SMBsetattrE); + + outsize = set_message(outbuf,0,0,True); + + if(!fsp || (fsp->conn != conn)) { + END_PROFILE(SMBgetattrE); + return ERROR_DOS(ERRDOS,ERRbadfid); + } + + /* + * 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); - cnum = SVAL(inbuf,smb_tid); - fnum = GETFNUM(inbuf,smb_vwv0); - CHECK_FNUM(fnum,cnum); - CHECK_WRITE(fnum); + /* + * Patch from Ray Frush <frush@engr.colostate.edu> + * Sometimes times are sent as zero - ignore them. + */ - tcount = SVAL(inbuf,smb_vwv1); - startpos = IVAL(inbuf,smb_vwv2); - numtowrite = SVAL(inbuf,smb_vwv6); - smb_doff = SVAL(inbuf,smb_vwv7); + if ((unix_times.actime == 0) && (unix_times.modtime == 0)) { + /* Ignore request */ + if( DEBUGLVL( 3 ) ) { + dbgtext( "reply_setattrE fnum=%d ", fsp->fnum); + dbgtext( "ignoring zero request - not setting timestamps of 0\n" ); + } + END_PROFILE(SMBsetattrE); + return(outsize); + } else if ((unix_times.actime != 0) && (unix_times.modtime == 0)) { + /* set modify time = to access time if modify time was 0 */ + unix_times.modtime = unix_times.actime; + } - data = smb_base(inbuf) + smb_doff; + /* Set the date on this file */ + if(file_utime(conn, fsp->fsp_name, &unix_times)) { + END_PROFILE(SMBsetattrE); + return ERROR_DOS(ERRDOS,ERRnoaccess); + } + + DEBUG( 3, ( "reply_setattrE fnum=%d actime=%d modtime=%d\n", + fsp->fnum, (int)unix_times.actime, (int)unix_times.modtime ) ); - /* We need to send an SMBwriteC response, not an SMBwritebs */ - CVAL(outbuf,smb_com) = SMBwritec; + END_PROFILE(SMBsetattrE); + return(outsize); +} - /* 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; +/* Back from the dead for OS/2..... JRA. */ - /* Check for an earlier error */ - if(wbms->wr_discard) - return -1; /* Just discard the packet */ +/**************************************************************************** + Reply to a SMBwritebmpx (write block multiplex primary) request. +****************************************************************************/ + +int reply_writebmpx(connection_struct *conn, char *inbuf,char *outbuf, int size, int dum_buffsize) +{ + size_t numtowrite; + ssize_t nwritten = -1; + int outsize = 0; + SMB_OFF_T startpos; + size_t tcount; + BOOL write_through; + int smb_doff; + char *data; + files_struct *fsp = file_fsp(inbuf,smb_vwv0); + START_PROFILE(SMBwriteBmpx); + + CHECK_FSP(fsp,conn); + CHECK_WRITE(fsp); + CHECK_ERROR(fsp); + + 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 */ + SCVAL(outbuf,smb_com,SMBwritec); + + if (is_locked(fsp,conn,(SMB_BIG_UINT)tcount,(SMB_BIG_UINT)startpos,WRITE_LOCK,False)) { + END_PROFILE(SMBwriteBmpx); + return(ERROR_DOS(ERRDOS,ERRlock)); + } - seek_file(fnum,startpos); - nwritten = write_file(fnum,data,numtowrite); + nwritten = write_file(fsp,data,startpos,numtowrite); - if(lp_syncalways(SNUM(cnum)) || write_through) - sync_file(fnum); + if(lp_syncalways(SNUM(conn)) || write_through) + sync_file(conn,fsp); - 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)); - } + if(nwritten < (ssize_t)numtowrite) { + END_PROFILE(SMBwriteBmpx); + return(UNIXERROR(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) { + /* 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((ssize_t)tcount > nwritten) { + write_bmpx_struct *wbms; + if(fsp->wbmpx_ptr != NULL) + wbms = fsp->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")); + END_PROFILE(SMBwriteBmpx); + return(ERROR_DOS(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; + fsp->wbmpx_ptr = wbms; + } + + /* We are returning successfully, set the message type back to + SMBwritebmpx */ + SCVAL(outbuf,smb_com,SMBwriteBmpx); + outsize = set_message(outbuf,1,0,True); - SSVAL(outbuf,smb_vwv0,wbms->wr_total_written); - send_response = True; - } + + SSVALS(outbuf,smb_vwv0,-1); /* We don't support smb_remaining */ + + DEBUG( 3, ( "writebmpx fnum=%d num=%d wrote=%d\n", + fsp->fnum, (int)numtowrite, (int)nwritten ) ); - free((char *)wbms); - Files[fnum].wbmpx_ptr = NULL; - } + if (write_through && tcount==nwritten) { + /* We need to send both a primary and a secondary response */ + smb_setlen(outbuf,outsize - 4); + if (!send_smb(smbd_server_fd(),outbuf)) + exit_server("reply_writebmpx: send_smb failed."); - if(send_response) - return(outsize); + /* Now the secondary */ + outsize = set_message(outbuf,1,0,True); + SCVAL(outbuf,smb_com,SMBwritec); + SSVAL(outbuf,smb_vwv0,nwritten); + } - return(-1); + END_PROFILE(SMBwriteBmpx); + return(outsize); } - /**************************************************************************** - reply to a SMBsetattrE + Reply to a SMBwritebs (write block multiplex secondary) request. ****************************************************************************/ -int reply_setattrE(char *inbuf,char *outbuf) + +int reply_writebs(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize) { - int cnum,fnum; - struct utimbuf unix_times; - int outsize = 0; + size_t numtowrite; + ssize_t nwritten = -1; + int outsize = 0; + SMB_OFF_T startpos; + size_t tcount; + BOOL write_through; + int smb_doff; + char *data; + write_bmpx_struct *wbms; + BOOL send_response = False; + files_struct *fsp = file_fsp(inbuf,smb_vwv0); + START_PROFILE(SMBwriteBs); + + CHECK_FSP(fsp,conn); + CHECK_WRITE(fsp); + + 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 */ + SCVAL(outbuf,smb_com,SMBwritec); + + /* This fd should have an auxiliary struct attached, + check that it does */ + wbms = fsp->wbmpx_ptr; + if(!wbms) { + END_PROFILE(SMBwriteBs); + return(-1); + } - outsize = set_message(outbuf,0,0,True); + /* If write through is set we can return errors, else we must cache them */ + write_through = wbms->wr_mode; - cnum = SVAL(inbuf,smb_tid); - fnum = GETFNUM(inbuf,smb_vwv0); + /* Check for an earlier error */ + if(wbms->wr_discard) { + END_PROFILE(SMBwriteBs); + return -1; /* Just discard the packet */ + } - CHECK_FNUM(fnum,cnum); - CHECK_ERROR(fnum); + nwritten = write_file(fsp,data,startpos,numtowrite); - /* 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); + if(lp_syncalways(SNUM(conn)) || write_through) + sync_file(conn,fsp); - /* 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)); + if (nwritten < (ssize_t)numtowrite) { + if(write_through) { + /* We are returning an error - we can delete the aux struct */ + if (wbms) + free((char *)wbms); + fsp->wbmpx_ptr = NULL; + END_PROFILE(SMBwriteBs); + return(ERROR_DOS(ERRHRD,ERRdiskfull)); + } + END_PROFILE(SMBwriteBs); + return(CACHE_ERROR(wbms,ERRHRD,ERRdiskfull)); + } - return(outsize); -} + /* 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); + fsp->wbmpx_ptr = NULL; + } + + if(send_response) { + END_PROFILE(SMBwriteBs); + return(outsize); + } + + END_PROFILE(SMBwriteBs); + return(-1); +} /**************************************************************************** - reply to a SMBgetattrE + 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); +int reply_getattrE(connection_struct *conn, char *inbuf,char *outbuf, int size, int dum_buffsize) +{ + SMB_STRUCT_STAT sbuf; + int outsize = 0; + int mode; + files_struct *fsp = file_fsp(inbuf,smb_vwv0); + START_PROFILE(SMBgetattrE); - cnum = SVAL(inbuf,smb_tid); - fnum = GETFNUM(inbuf,smb_vwv0); + outsize = set_message(outbuf,11,0,True); - CHECK_FNUM(fnum,cnum); - CHECK_ERROR(fnum); + if(!fsp || (fsp->conn != conn)) { + END_PROFILE(SMBgetattrE); + return ERROR_DOS(ERRDOS,ERRbadfid); + } - /* Do an fstat on this file */ - if(fstat(Files[fnum].fd, &sbuf)) - return(UNIXERROR(ERRDOS,ERRnoaccess)); + /* Do an fstat on this file */ + if(fsp_stat(fsp, &sbuf)) { + END_PROFILE(SMBgetattrE); + return(UNIXERROR(ERRDOS,ERRnoaccess)); + } - mode = dos_mode(cnum,Files[fnum].name,&sbuf); + mode = dos_mode(conn,fsp->fsp_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); + /* + * 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,get_create_time(&sbuf,lp_fake_dir_create_times(SNUM(conn)))); + 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,(uint32)sbuf.st_size); + SIVAL(outbuf,smb_vwv8,SMB_ROUNDUP(sbuf.st_size,1024)); + } + SSVAL(outbuf,smb_vwv10, mode); - DEBUG(3,("%s reply_getattrE fnum=%d cnum=%d\n",timestring(),fnum,cnum)); + DEBUG( 3, ( "reply_getattrE fnum=%d\n", fsp->fnum)); - return(outsize); + END_PROFILE(SMBgetattrE); + return(outsize); } - - - - - |