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