/* * 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 */ #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 #include #include #include #include #include #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 #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; 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; ERR("packet is %d bytes in length\n",i); { int j; for(j=0; j len ) { ERR("Bad parameter count %d\n",pcount); return FALSE; } DPRINTF("SMB_COM_SESSION_SETUP response, %d args: ",pcount); for(i=0; i len ) { ERR("parameter count %x, buffer count %x, len %x\n",pcount,bcount,len); return FALSE; } DPRINTF("response buffer %d bytes: ",bcount); for(i=0; i len ) { ERR("Bad parameter count %d\n",pcount); return FALSE; } ERR("response, %d args: ",pcount); for(i=0; i len ) { HeapFree(GetProcessHeap(),0,buffer); ERR("Bad parameter count %d\n",n); return FALSE; } ERR("response, %d args: ",n); for(i=0; icount) n=count; memcpy( out, &SMB_BUFFER(buffer,3), n); ERR("Read %d bytes\n",n); *read = n; HeapFree(GetProcessHeap(),0,buffer); return TRUE; } static int SMB_LoginAndConnect(LPCSTR host, LPCSTR share, USHORT *tree_id, USHORT *user_id, USHORT *dialect) { int fd=-1,r; struct sockaddr_in sin; LPSTR name=NULL; ERR("host %s share %s\n",host,share); /* FIXME: use various lookup methods */ if(0) NB_Lookup(host,&sin); else { if(0==inet_aton("127.0.0.1", (struct in_addr *)&sin.sin_addr.s_addr)) { FIXME("Error getting localhost address\n"); SetLastError( ERROR_PATH_NOT_FOUND ); return INVALID_HANDLE_VALUE; } sin.sin_family = AF_INET; sin.sin_port = htons(139); /* netbios session */ } fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); if(fd<0) goto fail; ERR("Connecting...\n"); r = connect(fd, &sin, sizeof sin); if(r<0) goto fail; if(!NB_SessionReq(fd, "*SMBSERVER", "WINE")) goto fail; if(!SMB_NegotiateProtocol(fd, dialect)) goto fail; if(!SMB_SessionSetup(fd, user_id)) goto fail; name = HeapAlloc(GetProcessHeap(),0,strlen(host)+strlen(share)+5); if(!name) goto fail; sprintf(name,"\\\\%s\\%s",host,share); if(!SMB_TreeConnect(fd,*user_id,name,tree_id)) goto fail; HeapFree(GetProcessHeap(),0,name); return fd; fail: if(name) HeapFree(GetProcessHeap(),0,name); ERR("Failed\n"); if(fd>=0) close(fd); return -1; } 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) ERR("created wineserver smb object, handle = %04x\n",ret); else SetLastError( ERROR_PATH_NOT_FOUND ); return ret; } HANDLE WINAPI SMB_CreateFileA( LPCSTR 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 = 0; name = HeapAlloc(GetProcessHeap(),0,lstrlenA(uncname)); if(!name) return -1; lstrcpyA(name,uncname); if( !UNC_SplitName(name, &host, &share, &file) ) { HeapFree(GetProcessHeap(),0,name); return handle; } ERR("server is %s, share is %s, file is %s\n", host, share, file); fd = SMB_LoginAndConnect(host, share, &tree_id, &user_id, &dialect); if(fd < 0) { HeapFree(GetProcessHeap(),0,name); return handle; } #if 0 if(!SMB_NtCreateOpen(fd, tree_id, user_id, dialect, file, access, sharing, sa, creation, attributes, template, &file_id )) { close(fd); HeapFree(GetProcessHeap(),0,name); ERR("CreateOpen failed\n"); return handle; } #endif if(!SMB_Open(fd, tree_id, user_id, dialect, file, access, sharing, creation, attributes, &file_id )) { close(fd); HeapFree(GetProcessHeap(),0,name); ERR("CreateOpen failed\n"); return handle; } HeapFree(GetProcessHeap(),0,name); handle = SMB_RegisterFile(fd, tree_id, user_id, dialect, file_id); if(!handle) { ERR("register failed\n"); close(fd); } 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; ERR("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; ERR("%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 += read; offset += read; if(total>=bytesToRead) break; } close(fd); if(bytesRead) *bytesRead = total; if(!SMB_SetOffset(hFile, offset)) return FALSE; return r; }