/* * Copyright (C) 2002 Mike McCormack * * CIFS implementation for WINE * * This is a WINE's implementation of the Common Internet File System * * for specification see: * * http://www.codefx.com/CIFS_Explained.htm * http://www.ubiqx.org/cifs/rfc-draft/rfc1002.html * http://www.ubiqx.org/cifs/rfc-draft/draft-leach-cifs-v1-spec-02.html * http://ubiqx.org/cifs/ * http://www.samba.org * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * * FIXME: * * - There is a race condition when two threads try to read from the same * SMB handle. Either we need to lock the SMB handle for the time we * use it in the client, or do all reading and writing to the socket * fd in the server. * * - Each new handle opens up a new connection to the SMB server. This * is not ideal, since operations can be multiplexed on one socket. For * this to work properly we would need to have some way of discovering * connections that are already open. * * - All access is currently anonymous. Password protected shares cannot * be accessed. We need some way of organising passwords, storing them * in the config file, or putting up a dialog box for the user. * * - We don't deal with SMB dialects at all. * * - SMB supports passing unicode over the wire, should use this if possible. * * - Implement ability to read named pipes over the network. Would require * integrate this code with the named pipes code in the server, and * possibly implementing some support for security tokens. */ #include "config.h" #include "wine/port.h" #include #include #include #include #include #include #include #ifdef HAVE_SYS_ERRNO_H #include #endif #include #include #ifdef HAVE_SYS_MMAN_H #include #endif #ifdef HAVE_SYS_TIME_H # include #endif #ifdef HAVE_SYS_POLL_H # include #endif #include #ifdef HAVE_UNISTD_H # include #endif #ifdef HAVE_UTIME_H # include #endif #ifdef HAVE_SYS_SOCKET_H # include #endif #include #ifdef HAVE_NETINET_IN_SYSTM_H #include #endif #ifdef HAVE_NETINET_IN_H #include #endif #ifdef HAVE_NETINET_IP_H #include #endif #ifdef HAVE_ARPA_INET_H #include #endif #ifdef HAVE_NETDB_H #include #endif #include "winerror.h" #include "windef.h" #include "winbase.h" #include "file.h" #include "heap.h" #include "smb.h" #include "wine/server.h" #include "wine/debug.h" WINE_DEFAULT_DEBUG_CHANNEL(file); #define MAX_HOST_NAME 15 #define NB_TIMEOUT 10000 USHORT SMB_MultiplexId = 0; struct NB_Buffer { unsigned char *buffer; int len; }; static int netbios_name(const char *p, unsigned char *buffer) { char ch; int i,len=0; buffer[len++]=' '; for(i=0; i<=MAX_HOST_NAME; i++) { if(i> 4) + 'A'; buffer[len++] = (ch&0x0f) + 'A'; } buffer[len++] = 0; /* add second terminator */ return len; } static DWORD NB_NameReq(LPCSTR host, unsigned char *buffer, int len) { int trn = 1234,i=0; NBR_ADDWORD(&buffer[i],trn); i+=2; NBR_ADDWORD(&buffer[i],0x0110); i+=2; NBR_ADDWORD(&buffer[i],0x0001); i+=2; NBR_ADDWORD(&buffer[i],0x0000); i+=2; NBR_ADDWORD(&buffer[i],0x0000); i+=2; NBR_ADDWORD(&buffer[i],0x0000); i+=2; i += netbios_name(host,&buffer[i]); NBR_ADDWORD(&buffer[i],0x0020); i+=2; NBR_ADDWORD(&buffer[i],0x0001); i+=2; TRACE("packet is %d bytes in length\n",i); { int j; for(j=0; jsin_addr, &buffer[58], sizeof addr->sin_addr); close(fd); return TRUE; err: close(fd); return FALSE; } #define NB_FIRST 0x40 #define NB_HDRSIZE 4 #define NB_SESSION_MSG 0x00 #define NB_SESSION_REQ 0x81 /* RFC 1002, section 4.3.2 */ static BOOL NB_SessionReq(int fd, char *called, char *calling) { unsigned char buffer[0x100]; int len = 0,r; struct pollfd fds; TRACE("called %s, calling %s\n",called,calling); buffer[0] = NB_SESSION_REQ; buffer[1] = NB_FIRST; netbios_name(called, &buffer[NB_HDRSIZE]); len += 34; netbios_name(calling, &buffer[NB_HDRSIZE+len]); len += 34; NBR_ADDWORD(&buffer[2],len); /* for(i=0; i<(len+NB_HDRSIZE); i++) DPRINTF("%02X%c",buffer[i],(((i+1)!=(len+4))&&((i+1)%16))?' ':'\n'); */ r = write(fd,buffer,len+4); if(r<0) { ERR("Write failed\n"); return FALSE; } fds.fd = fd; fds.events = POLLIN; fds.revents = 0; r = poll(&fds,1,NB_TIMEOUT); if(r!=1) { ERR("Poll failed\n"); return FALSE; } r = read(fd, buffer, NB_HDRSIZE); if((r!=NB_HDRSIZE) || (buffer[0]!=0x82)) { TRACE("Received %d bytes\n",r); TRACE("%02x %02x %02x %02x\n", buffer[0],buffer[1],buffer[2],buffer[3]); return FALSE; } return TRUE; } static BOOL NB_SendData(int fd, struct NB_Buffer *out) { unsigned char buffer[NB_HDRSIZE]; int r; /* CHECK: is it always OK to do this in two writes? */ /* perhaps use scatter gather sendmsg instead? */ buffer[0] = NB_SESSION_MSG; buffer[1] = NB_FIRST; NBR_ADDWORD(&buffer[2],out->len); r = write(fd, buffer, NB_HDRSIZE); if(r!=NB_HDRSIZE) return FALSE; r = write(fd, out->buffer, out->len); if(r!=out->len) { ERR("write failed\n"); return FALSE; } return TRUE; } static BOOL NB_RecvData(int fd, struct NB_Buffer *rx) { int r; unsigned char buffer[NB_HDRSIZE]; r = read(fd, buffer, NB_HDRSIZE); if((r!=NB_HDRSIZE) || (buffer[0]!=NB_SESSION_MSG)) { ERR("Received %d bytes\n",r); return FALSE; } rx->len = NBR_GETWORD(&buffer[2]); rx->buffer = HeapAlloc(GetProcessHeap(), 0, rx->len); if(!rx->buffer) return FALSE; r = read(fd, rx->buffer, rx->len); if(rx->len!=r) { TRACE("Received %d bytes\n",r); HeapFree(GetProcessHeap(), 0, rx->buffer); rx->buffer = 0; rx->len = 0; return FALSE; } return TRUE; } static BOOL NB_Transaction(int fd, struct NB_Buffer *in, struct NB_Buffer *out) { int r; struct pollfd fds; if(TRACE_ON(file)) { int i; DPRINTF("Sending request:\n"); for(i=0; ilen; i++) DPRINTF("%02X%c",in->buffer[i],(((i+1)!=in->len)&&((i+1)%16))?' ':'\n'); } if(!NB_SendData(fd,in)) return FALSE; fds.fd = fd; fds.events = POLLIN; fds.revents = 0; r = poll(&fds,1,NB_TIMEOUT); if(r!=1) { ERR("Poll failed\n"); return FALSE; } if(!NB_RecvData(fd, out)) return FALSE; if(TRACE_ON(file)) { int i; DPRINTF("Got response:\n"); for(i=0; ilen; i++) DPRINTF("%02X%c",out->buffer[i],(((i+1)!=out->len)&&((i+1)%16))?' ':'\n'); } return TRUE; } #define SMB_ADDHEADER(b,l) { b[(l)++]=0xff; b[(l)++]='S'; b[(l)++]='M'; b[(l)++]='B'; } #define SMB_ADDERRINFO(b,l) { b[(l)++]=0; b[(l)++]=0; b[(l)++]=0; b[(l)++]=0; } #define SMB_ADDPADSIG(b,l) { memset(&b[l],0,12); l+=12; } #define SMB_ERRCLASS 5 #define SMB_ERRCODE 7 #define SMB_TREEID 24 #define SMB_PROCID 26 #define SMB_USERID 28 #define SMB_PLEXID 30 #define SMB_PCOUNT 32 #define SMB_HDRSIZE 33 static DWORD SMB_GetError(unsigned char *buffer) { char *err_class; switch(buffer[SMB_ERRCLASS]) { case 0: return STATUS_SUCCESS; case 1: err_class = "DOS"; break; case 2: err_class = "net server"; break; case 3: err_class = "hardware"; break; case 0xff: err_class = "smb"; break; default: err_class = "unknown"; break; } ERR("%s error %d \n",err_class, buffer[SMB_ERRCODE]); /* FIXME: return propper error codes */ return STATUS_INVALID_PARAMETER; } static int SMB_Header(unsigned char *buffer, unsigned char command, USHORT tree_id, USHORT user_id) { int len = 0; DWORD id; /* 0 */ SMB_ADDHEADER(buffer,len); /* 4 */ buffer[len++] = command; /* 5 */ SMB_ADDERRINFO(buffer,len) /* 9 */ buffer[len++] = 0x00; /* flags */ SMB_ADDWORD(&buffer[len],1); len += 2; /* flags2 */ /* 12 */ SMB_ADDPADSIG(buffer,len) /* 24 */ SMB_ADDWORD(&buffer[len],tree_id); len += 2; /* treeid */ id = GetCurrentThreadId(); SMB_ADDWORD(&buffer[len],id); len += 2; /* process id */ SMB_ADDWORD(&buffer[len],user_id); len += 2; /* user id */ SMB_ADDWORD(&buffer[len],SMB_MultiplexId); len += 2; /* multiplex id */ SMB_MultiplexId++; return len; } static const char *SMB_ProtocolDialect = "NT LM 0.12"; /* = "Windows for Workgroups 3.1a"; */ /* FIXME: support multiple SMB dialects */ static BOOL SMB_NegotiateProtocol(int fd, USHORT *dialect) { unsigned char buf[0x100]; int buflen = 0; struct NB_Buffer tx, rx; TRACE("\n"); memset(buf,0,sizeof buf); tx.buffer = buf; tx.len = SMB_Header(tx.buffer, SMB_COM_NEGOTIATE, 0, 0); /* parameters */ tx.buffer[tx.len++] = 0; /* no parameters */ /* command buffer */ buflen = strlen(SMB_ProtocolDialect)+2; /* include type and nul byte */ SMB_ADDWORD(&tx.buffer[tx.len],buflen); tx.len += 2; tx.buffer[tx.len] = 0x02; strcpy(&tx.buffer[tx.len+1],SMB_ProtocolDialect); tx.len += buflen; rx.buffer = NULL; rx.len = 0; if(!NB_Transaction(fd, &tx, &rx)) { ERR("Failed\n"); return FALSE; } if(!rx.buffer) return FALSE; /* FIXME: check response */ if(SMB_GetError(rx.buffer)) { ERR("returned error\n"); HeapFree(GetProcessHeap(),0,rx.buffer); return FALSE; } HeapFree(GetProcessHeap(),0,rx.buffer); *dialect = 0; return TRUE; } #define SMB_PARAM_COUNT(buffer) ((buffer)[SMB_PCOUNT]) #define SMB_PARAM(buffer,n) SMB_GETWORD(&(buffer)[SMB_HDRSIZE+2*(n)]) #define SMB_BUFFER_COUNT(buffer) SMB_GETWORD(buffer+SMB_HDRSIZE+2*SMB_PARAM_COUNT(buffer)) #define SMB_BUFFER(buffer,n) ((buffer)[SMB_HDRSIZE + 2*SMB_PARAM_COUNT(buffer) + 2 + (n) ]) static BOOL SMB_SessionSetup(int fd, USHORT *userid) { unsigned char buf[0x100]; int pcount,bcount; struct NB_Buffer rx, tx; memset(buf,0,sizeof buf); tx.buffer = buf; tx.len = SMB_Header(tx.buffer, SMB_COM_SESSION_SETUP_ANDX, 0, 0); tx.buffer[tx.len++] = 0; /* no parameters? */ tx.buffer[tx.len++] = 0xff; /* AndXCommand: secondary request */ tx.buffer[tx.len++] = 0x00; /* AndXReserved */ SMB_ADDWORD(&tx.buffer[tx.len],0); /* AndXOffset */ tx.len += 2; SMB_ADDWORD(&tx.buffer[tx.len],0x400); /* MaxBufferSize */ tx.len += 2; SMB_ADDWORD(&tx.buffer[tx.len],1); /* MaxMpxCount */ tx.len += 2; SMB_ADDWORD(&tx.buffer[tx.len],0); /* VcNumber */ tx.len += 2; SMB_ADDWORD(&tx.buffer[tx.len],0); /* SessionKey */ tx.len += 2; SMB_ADDWORD(&tx.buffer[tx.len],0); /* SessionKey */ tx.len += 2; SMB_ADDWORD(&tx.buffer[tx.len],0); /* Password length */ tx.len += 2; SMB_ADDWORD(&tx.buffer[tx.len],0); /* Reserved */ tx.len += 2; SMB_ADDWORD(&tx.buffer[tx.len],0); /* Reserved */ tx.len += 2; /* FIXME: add name and password here */ tx.buffer[tx.len++] = 0; /* number of bytes in password */ rx.buffer = NULL; rx.len = 0; if(!NB_Transaction(fd, &tx, &rx)) return FALSE; if(!rx.buffer) return FALSE; if(SMB_GetError(rx.buffer)) goto done; pcount = SMB_PARAM_COUNT(rx.buffer); if( (SMB_HDRSIZE+pcount*2) > rx.len ) { ERR("Bad parameter count %d\n",pcount); goto done; } if(TRACE_ON(file)) { int i; DPRINTF("SMB_COM_SESSION_SETUP response, %d args: ",pcount); for(i=0; i rx.len ) { ERR("parameter count %x, buffer count %x, len %x\n",pcount,bcount,rx.len); goto done; } if(TRACE_ON(file)) { int i; DPRINTF("response buffer %d bytes: ",bcount); for(i=0; i rx.len ) { ERR("Bad parameter count %d\n",pcount); return FALSE; } TRACE("response, %d args: ",pcount); for(i=0; i rx.len ) { HeapFree(GetProcessHeap(),0,rx.buffer); HeapFree(GetProcessHeap(),0,tx.buffer); ERR("Bad parameter count %d\n",n); return FALSE; } TRACE("response, %d args: ",n); for(i=0; icount) n=count; memcpy( out, &SMB_BUFFER(rx.buffer,3), n); TRACE("Read %d bytes\n",n); *read = n; HeapFree(GetProcessHeap(),0,tx.buffer); HeapFree(GetProcessHeap(),0,rx.buffer); return TRUE; } /* * setup_count : number of USHORTs in the setup string */ struct SMB_Trans2Info { struct NB_Buffer buf; unsigned char *setup; int setup_count; unsigned char *params; int param_count; unsigned char *data; int data_count; }; /* * Do an SMB transaction * * This function allocates memory in the recv structure. It is * the caller's responsibility to free the memory if it finds * that recv->buf.buffer is nonzero. */ static BOOL SMB_Transaction2(int fd, int tree_id, int user_id, struct SMB_Trans2Info *send, struct SMB_Trans2Info *recv) { int buf_size; const int retmaxparams = 0xf000; const int retmaxdata = 1024; const int retmaxsetup = 0; /* FIXME */ const int flags = 0; const int timeout = 0; int param_ofs, data_ofs; struct NB_Buffer tx; BOOL ret = FALSE; buf_size = 0x100 + send->setup_count*2 + send->param_count + send->data_count ; tx.buffer = (unsigned char *) HeapAlloc(GetProcessHeap(),0,buf_size); tx.len = SMB_Header(tx.buffer, SMB_COM_TRANSACTION2, tree_id, user_id); tx.buffer[tx.len++] = 14 + send->setup_count; SMB_ADDWORD(&tx.buffer[tx.len],send->param_count); /* total param bytes sent */ tx.len += 2; SMB_ADDWORD(&tx.buffer[tx.len],send->data_count); /* total data bytes sent */ tx.len += 2; SMB_ADDWORD(&tx.buffer[tx.len],retmaxparams); /*max parameter bytes to return */ tx.len += 2; SMB_ADDWORD(&tx.buffer[tx.len],retmaxdata); /* max data bytes to return */ tx.len += 2; tx.buffer[tx.len++] = retmaxsetup; tx.buffer[tx.len++] = 0; /* reserved1 */ SMB_ADDWORD(&tx.buffer[tx.len],flags); /* flags */ tx.len += 2; SMB_ADDDWORD(&tx.buffer[tx.len],timeout); /* timeout */ tx.len += 4; SMB_ADDWORD(&tx.buffer[tx.len],0); /* reserved2 */ tx.len += 2; SMB_ADDWORD(&tx.buffer[tx.len],send->param_count); /* parameter count - this buffer */ tx.len += 2; param_ofs = tx.len; /* parameter offset */ tx.len += 2; SMB_ADDWORD(&tx.buffer[tx.len],send->data_count); /* data count */ tx.len += 2; data_ofs = tx.len; /* data offset */ tx.len += 2; tx.buffer[tx.len++] = send->setup_count; /* setup count */ tx.buffer[tx.len++] = 0; /* reserved3 */ memcpy(&tx.buffer[tx.len], send->setup, send->setup_count*2); /* setup */ tx.len += send->setup_count*2; /* add string here when implementing SMB_COM_TRANS */ SMB_ADDWORD(&tx.buffer[param_ofs], tx.len); memcpy(&tx.buffer[tx.len], send->params, send->param_count); /* parameters */ tx.len += send->param_count; if(tx.len%2) tx.len ++; /* pad2 */ SMB_ADDWORD(&tx.buffer[data_ofs], tx.len); if(send->data_count && send->data) { memcpy(&tx.buffer[tx.len], send->data, send->data_count); /* data */ tx.len += send->data_count; } recv->buf.buffer = NULL; recv->buf.len = 0; if(!NB_Transaction(fd, &tx, &recv->buf)) goto done; if(!recv->buf.buffer) goto done; if(SMB_GetError(recv->buf.buffer)) goto done; /* reuse these two offsets to check the received message */ param_ofs = SMB_PARAM(recv->buf.buffer,4); data_ofs = SMB_PARAM(recv->buf.buffer,7); if( (recv->param_count + param_ofs) > recv->buf.len ) goto done; if( (recv->data_count + data_ofs) > recv->buf.len ) goto done; TRACE("Success\n"); recv->setup = NULL; recv->setup_count = 0; recv->param_count = SMB_PARAM(recv->buf.buffer,0); recv->params = &recv->buf.buffer[param_ofs]; recv->data_count = SMB_PARAM(recv->buf.buffer,6); recv->data = &recv->buf.buffer[data_ofs]; /* TRACE("%d words\n",SMB_PARAM_COUNT(recv->buf.buffer)); TRACE("total parameters = %d\n",SMB_PARAM(recv->buf.buffer,0)); TRACE("total data = %d\n",SMB_PARAM(recv->buf.buffer,1)); TRACE("parameters = %d\n",SMB_PARAM(recv->buf.buffer,3)); TRACE("parameter offset = %d\n",SMB_PARAM(recv->buf.buffer,4)); TRACE("param displace = %d\n",SMB_PARAM(recv->buf.buffer,5)); TRACE("data count = %d\n",SMB_PARAM(recv->buf.buffer,6)); TRACE("data offset = %d\n",SMB_PARAM(recv->buf.buffer,7)); TRACE("data displace = %d\n",SMB_PARAM(recv->buf.buffer,8)); */ ret = TRUE; done: if(tx.buffer) HeapFree(GetProcessHeap(),0,tx.buffer); return ret; } static BOOL SMB_SetupFindFirst(struct SMB_Trans2Info *send, LPSTR filename) { int search_attribs = FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM; int search_count = 10; int flags = 0; int infolevel = 0x104; /* SMB_FILE_BOTH_DIRECTORY_INFO */ int storagetype = 0; int len, buf_size; memset(send,0,sizeof send); send->setup_count = 1; send->setup = HeapAlloc(GetProcessHeap(),0,send->setup_count*2); if(!send->setup) return FALSE; buf_size = 0x10 + lstrlenA(filename); send->params = HeapAlloc(GetProcessHeap(),0,buf_size); if(!send->params) { HeapFree(GetProcessHeap(),0,send->setup); return FALSE; } SMB_ADDWORD(send->setup,TRANS2_FIND_FIRST2); len = 0; memset(send->params,0,buf_size); SMB_ADDWORD(&send->params[len],search_attribs); len += 2; SMB_ADDWORD(&send->params[len],search_count); len += 2; SMB_ADDWORD(&send->params[len],flags); len += 2; SMB_ADDWORD(&send->params[len],infolevel); len += 2; SMB_ADDDWORD(&send->params[len],storagetype); len += 4; strcpy(&send->params[len],filename); len += lstrlenA(filename)+1; send->param_count = len; send->data = NULL; send->data_count = 0; return TRUE; } static SMB_DIR *SMB_Trans2FindFirst(int fd, USHORT tree_id, USHORT user_id, USHORT dialect, LPSTR filename ) { int num; BOOL ret; /* char *filename = "\\*"; */ struct SMB_Trans2Info send, recv; SMB_DIR *smbdir = NULL; TRACE("patern = %s\n",filename); if(!SMB_SetupFindFirst(&send, filename)) return FALSE; memset(&recv,0,sizeof recv); ret = SMB_Transaction2(fd, tree_id, user_id, &send, &recv); HeapFree(GetProcessHeap(),0,send.params); HeapFree(GetProcessHeap(),0,send.setup); if(!ret) goto done; if(recv.setup_count) goto done; if(recv.param_count != 10) goto done; num = SMB_GETWORD(&recv.params[2]); TRACE("Success, search id: %d\n",num); if(SMB_GETWORD(&recv.params[4])) FIXME("need to read more!\n"); smbdir = HeapAlloc(GetProcessHeap(),0,sizeof(*smbdir)); if(smbdir) { int i, ofs=0; smbdir->current = 0; smbdir->num_entries = num; smbdir->entries = HeapAlloc(GetProcessHeap(), 0, sizeof(unsigned char*)*num); if(!smbdir->entries) goto done; smbdir->buffer = recv.buf.buffer; /* save to free later */ for(i=0; ientries[i] = &recv.data[ofs]; if(TRACE_ON(file)) { int j; for(j=0; jrecv.data_count) goto done; } ret = TRUE; } done: if(!ret) { if( recv.buf.buffer ) HeapFree(GetProcessHeap(),0,recv.buf.buffer); if( smbdir ) { if( smbdir->entries ) HeapFree(GetProcessHeap(),0,smbdir->entries); HeapFree(GetProcessHeap(),0,smbdir); } smbdir = NULL; } return smbdir; } static int SMB_GetSocket(LPCSTR host) { int fd=-1,r; struct sockaddr_in sin; struct hostent *he; TRACE("host %s\n",host); he = gethostbyname(host); if(he) { memcpy(&sin.sin_addr,he->h_addr, sizeof (sin.sin_addr)); goto connect; } if(NB_Lookup(host,&sin)) goto connect; /* FIXME: resolve by WINS too */ ERR("couldn't resolve SMB host %s\n", host); return -1; connect: sin.sin_family = AF_INET; sin.sin_port = htons(139); /* netbios session */ fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); if(fd<0) return fd; { unsigned char *x = (unsigned char *)&sin.sin_addr; TRACE("Connecting to %d.%d.%d.%d ...\n", x[0],x[1],x[2],x[3]); } r = connect(fd, &sin, sizeof sin); if(!NB_SessionReq(fd, "*SMBSERVER", "WINE")) { close(fd); return -1; } return fd; } static BOOL SMB_LoginAndConnect(int fd, LPCSTR host, LPCSTR share, USHORT *tree_id, USHORT *user_id, USHORT *dialect) { LPSTR name=NULL; TRACE("host %s share %s\n",host,share); if(!SMB_NegotiateProtocol(fd, dialect)) return FALSE; if(!SMB_SessionSetup(fd, user_id)) return FALSE; name = HeapAlloc(GetProcessHeap(),0,strlen(host)+strlen(share)+5); if(!name) return FALSE; sprintf(name,"\\\\%s\\%s",host,share); if(!SMB_TreeConnect(fd,*user_id,name,tree_id)) { HeapFree(GetProcessHeap(),0,name); return FALSE; } return TRUE; } static HANDLE SMB_RegisterFile( int fd, USHORT tree_id, USHORT user_id, USHORT dialect, USHORT file_id) { int r; HANDLE ret; wine_server_send_fd( fd ); SERVER_START_REQ( create_smb ) { req->tree_id = tree_id; req->user_id = user_id; req->file_id = file_id; req->dialect = 0; req->fd = fd; SetLastError(0); r = wine_server_call_err( req ); ret = reply->handle; } SERVER_END_REQ; if(!r) TRACE("created wineserver smb object, handle = %04x\n",ret); else SetLastError( ERROR_PATH_NOT_FOUND ); return ret; } HANDLE WINAPI SMB_CreateFileW( LPCWSTR uncname, DWORD access, DWORD sharing, LPSECURITY_ATTRIBUTES sa, DWORD creation, DWORD attributes, HANDLE template ) { int fd; USHORT tree_id=0, user_id=0, dialect=0, file_id=0; LPSTR name,host,share,file; HANDLE handle = INVALID_HANDLE_VALUE; INT len; len = WideCharToMultiByte(CP_ACP, 0, uncname, -1, NULL, 0, NULL, NULL); name = HeapAlloc(GetProcessHeap(), 0, len); if(!name) return handle; WideCharToMultiByte(CP_ACP, 0, uncname, -1, name, len, NULL, NULL); if( !UNC_SplitName(name, &host, &share, &file) ) { HeapFree(GetProcessHeap(),0,name); return handle; } TRACE("server is %s, share is %s, file is %s\n", host, share, file); fd = SMB_GetSocket(host); if(fd < 0) goto done; if(!SMB_LoginAndConnect(fd, host, share, &tree_id, &user_id, &dialect)) goto done; #if 0 if(!SMB_NtCreateOpen(fd, tree_id, user_id, dialect, file, access, sharing, sa, creation, attributes, template, &file_id )) { close(fd); ERR("CreateOpen failed\n"); goto done; } #endif if(!SMB_Open(fd, tree_id, user_id, dialect, file, access, sharing, creation, attributes, &file_id )) { close(fd); ERR("CreateOpen failed\n"); goto done; } handle = SMB_RegisterFile(fd, tree_id, user_id, dialect, file_id); if(!handle) { ERR("register failed\n"); close(fd); } done: HeapFree(GetProcessHeap(),0,name); return handle; } static BOOL SMB_GetSmbInfo(HANDLE hFile, USHORT *tree_id, USHORT *user_id, USHORT *dialect, USHORT *file_id, LPDWORD offset) { int r; SERVER_START_REQ( get_smb_info ) { req->handle = hFile; req->flags = 0; SetLastError(0); r = wine_server_call_err( req ); if(tree_id) *tree_id = reply->tree_id; if(user_id) *user_id = reply->user_id; if(file_id) *file_id = reply->file_id; if(dialect) *dialect = reply->dialect; if(offset) *offset = reply->offset; } SERVER_END_REQ; return !r; } static BOOL SMB_SetOffset(HANDLE hFile, DWORD offset) { int r; TRACE("offset = %08lx\n",offset); SERVER_START_REQ( get_smb_info ) { req->handle = hFile; req->flags = SMBINFO_SET_OFFSET; req->offset = offset; SetLastError(0); r = wine_server_call_err( req ); /* if(offset) *offset = reply->offset; */ } SERVER_END_REQ; return !r; } BOOL WINAPI SMB_ReadFile(HANDLE hFile, LPVOID buffer, DWORD bytesToRead, LPDWORD bytesRead, LPOVERLAPPED lpOverlapped) { int fd; DWORD total, count, offset; USHORT user_id, tree_id, dialect, file_id, read; BOOL r=TRUE; TRACE("%04x %p %ld %p\n", hFile, buffer, bytesToRead, bytesRead); if(!SMB_GetSmbInfo(hFile, &tree_id, &user_id, &dialect, &file_id, &offset)) return FALSE; fd = FILE_GetUnixHandle(hFile, GENERIC_READ); if(fd<0) return FALSE; total = 0; while(1) { count = bytesToRead - total; if(count>0x400) count = 0x400; if(count==0) break; read = 0; r = SMB_Read(fd, tree_id, user_id, dialect, file_id, offset, buffer, count, &read); if(!r) break; if(!read) break; total += read; buffer = (char*)buffer + read; offset += read; if(total>=bytesToRead) break; } close(fd); if(bytesRead) *bytesRead = total; if(!SMB_SetOffset(hFile, offset)) return FALSE; return r; } SMB_DIR* WINAPI SMB_FindFirst(LPCWSTR name) { int fd = -1; LPSTR host,share,file; USHORT tree_id=0, user_id=0, dialect=0; SMB_DIR *ret = NULL; LPSTR filename; DWORD len; TRACE("Find %s\n",debugstr_w(name)); len = WideCharToMultiByte( CP_ACP, 0, name, -1, NULL, 0, NULL, NULL ); filename = HeapAlloc(GetProcessHeap(),0,len); if(!filename) return ret; WideCharToMultiByte( CP_ACP, 0, name, -1, filename, len, NULL, NULL ); if( !UNC_SplitName(filename, &host, &share, &file) ) goto done; fd = SMB_GetSocket(host); if(fd < 0) goto done; if(!SMB_LoginAndConnect(fd, host, share, &tree_id, &user_id, &dialect)) goto done; TRACE("server is %s, share is %s, file is %s\n", host, share, file); ret = SMB_Trans2FindFirst(fd, tree_id, user_id, dialect, file); done: /* disconnect */ if(fd != -1) close(fd); if(filename) HeapFree(GetProcessHeap(),0,filename); return ret; } BOOL WINAPI SMB_FindNext(SMB_DIR *dir, WIN32_FIND_DATAW *data ) { unsigned char *ent; int len, fnlen; TRACE("%d of %d\n",dir->current,dir->num_entries); if(dir->current >= dir->num_entries) return FALSE; memset(data, 0, sizeof *data); ent = dir->entries[dir->current]; len = SMB_GETDWORD(&ent[0]); if(len<0x5e) return FALSE; memcpy(&data->ftCreationTime, &ent[8], 8); memcpy(&data->ftLastAccessTime, &ent[0x10], 8); memcpy(&data->ftLastWriteTime, &ent[0x18], 8); data->nFileSizeHigh = SMB_GETDWORD(&ent[0x30]); data->nFileSizeLow = SMB_GETDWORD(&ent[0x34]); data->dwFileAttributes = SMB_GETDWORD(&ent[0x38]); /* copy the long filename */ fnlen = SMB_GETDWORD(&ent[0x3c]); if ( fnlen > (sizeof data->cFileName/sizeof(WCHAR)) ) return FALSE; MultiByteToWideChar( CP_ACP, 0, &ent[0x5e], fnlen, data->cFileName, sizeof(data->cFileName)/sizeof(WCHAR) ); /* copy the short filename */ if ( ent[0x44] > (sizeof data->cAlternateFileName/sizeof(WCHAR)) ) return FALSE; MultiByteToWideChar( CP_ACP, 0, &ent[0x5e + len], ent[0x44], data->cAlternateFileName, sizeof(data->cAlternateFileName)/sizeof(WCHAR) ); dir->current++; return TRUE; } BOOL WINAPI SMB_CloseDir(SMB_DIR *dir) { HeapFree(GetProcessHeap(),0,dir->buffer); HeapFree(GetProcessHeap(),0,dir->entries); memset(dir,0,sizeof *dir); HeapFree(GetProcessHeap(),0,dir); return TRUE; }