From fc93261150812a5c74f713776ccb89226a2181e8 Mon Sep 17 00:00:00 2001
From: Mike McCormack <mike_mccormack@start.com.au>
Date: Tue, 12 Mar 2002 19:24:04 +0000
Subject: [PATCH] Begin to make wine an SMB client.

---
 files/Makefile.in              |    1 +
 files/file.c                   |   10 +-
 files/smb.c                    | 1069 ++++++++++++++++++++++++++++++++
 files/smb.h                    |  100 +++
 include/wine/server_protocol.h |   46 +-
 server/Makefile.in             |    1 +
 server/protocol.def            |   28 +-
 server/request.h               |    4 +
 server/smb.c                   |  192 ++++++
 server/trace.c                 |   36 ++
 10 files changed, 1481 insertions(+), 6 deletions(-)
 create mode 100644 files/smb.c
 create mode 100644 files/smb.h
 create mode 100644 server/smb.c

diff --git a/files/Makefile.in b/files/Makefile.in
index 92127b76b2f..483914cfaa9 100644
--- a/files/Makefile.in
+++ b/files/Makefile.in
@@ -12,6 +12,7 @@ C_SRCS = \
 	drive.c \
 	file.c \
 	profile.c \
+	smb.c \
 	tape.c
 
 all: $(MODULE).o
diff --git a/files/file.c b/files/file.c
index fb28a9ae13d..0bc85acd25b 100644
--- a/files/file.c
+++ b/files/file.c
@@ -56,6 +56,8 @@
 #include "heap.h"
 #include "msdos.h"
 #include "wincon.h"
+
+#include "smb.h"
 #include "wine/debug.h"
 
 #include "wine/server.h"
@@ -74,6 +76,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(file);
 
 static HANDLE dos_handles[DOS_TABLE_SIZE];
 
+extern WINAPI HANDLE FILE_SmbOpen(LPCSTR name);
 
 /***********************************************************************
  *              FILE_ConvertOFMode
@@ -480,9 +483,8 @@ HANDLE WINAPI CreateFileA( LPCSTR filename, DWORD access, DWORD sharing,
     /* If the name still starts with '\\', it's a UNC name. */
     if (!strncmp(filename, "\\\\", 2))
     {
-        FIXME("UNC name (%s) not supported.\n", filename );
-        SetLastError( ERROR_PATH_NOT_FOUND );
-        return INVALID_HANDLE_VALUE;
+        ret = SMB_CreateFileA(filename, access, sharing, sa, creation, attributes, template );
+        goto done;
     }
 
     /* If the name contains a DOS wild card (* or ?), do no create a file */
@@ -1531,6 +1533,8 @@ BOOL WINAPI ReadFile( HANDLE hFile, LPVOID buffer, DWORD bytesToRead,
     }
     switch(type)
     {
+    case FD_TYPE_SMB:
+        return SMB_ReadFile(hFile, buffer, bytesToRead, bytesRead, NULL);
     case FD_TYPE_CONSOLE:
 	return ReadConsoleA(hFile, buffer, bytesToRead, bytesRead, NULL);
 
diff --git a/files/smb.c b/files/smb.c
new file mode 100644
index 00000000000..2264d35d6f0
--- /dev/null
+++ b/files/smb.c
@@ -0,0 +1,1069 @@
+/*
+ * 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 <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#ifdef HAVE_SYS_ERRNO_H
+#include <sys/errno.h>
+#endif
+#include <sys/types.h>
+#include <sys/stat.h>
+#ifdef HAVE_SYS_MMAN_H
+#include <sys/mman.h>
+#endif
+#include <sys/time.h>
+#include <sys/poll.h>
+#include <time.h>
+#include <unistd.h>
+#include <utime.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <netinet/ip.h>
+#include <arpa/inet.h>
+
+#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<MAX_HOST_NAME)
+        {
+            if(*p)
+                ch = *p++&0xdf; /* add character from hostname */
+            else
+                ch = ' ';  /* add padding */
+        }
+        else
+            ch = 0;        /* add terminator */
+        buffer[len++] = ((ch&0xf0) >> 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<i; j++)
+            printf("%02x%c",buffer[j],(((j+1)%16)&&((j+1)!=j))?' ':'\n');
+    }
+
+    return i;
+}
+
+/* unc = \\hostname\share\file... */
+static BOOL UNC_SplitName(LPSTR unc, LPSTR *hostname, LPSTR *share, LPSTR *file)
+{
+    char *p;
+
+    ERR("%s\n",unc);
+
+    p = strchr(unc,'\\');
+    if(!p)
+        return FALSE;
+    p = strchr(p+1,'\\');
+    if(!p)
+        return FALSE;
+    *hostname=++p;
+
+    p = strchr(p,'\\');
+    if(!p)
+        return FALSE;
+    *p=0;
+    *share = ++p;
+
+    p = strchr(p,'\\');
+    if(!p)
+        return FALSE;
+    *p=0;
+    *file = ++p;
+
+    return TRUE;
+}
+
+static BOOL NB_Lookup(LPCSTR host, struct sockaddr_in *addr)
+{
+    int fd,on=1,r,len;
+    struct pollfd fds;
+    struct sockaddr_in sin;
+    unsigned char buffer[256];
+
+    fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
+    if(fd<0)
+        return FALSE;
+
+    r = setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &on, sizeof on);
+    if(r<0)
+        return FALSE;
+
+    if(0==inet_aton("255.255.255.255", (struct in_addr *)&sin.sin_addr.s_addr))
+    {
+        FIXME("Error getting bcast address\n");
+        return FALSE;
+    }
+    sin.sin_family = AF_INET;
+    sin.sin_port    = htons(137);
+
+    len = NB_NameReq(host,buffer,sizeof buffer);
+    if(len<=0)
+        return FALSE;
+
+    r = sendto(fd, buffer, len, 0, &sin, sizeof sin);
+    if(r<0)
+    {
+        FIXME("Error sending packet\n");
+        return FALSE;
+    }
+
+    fds.fd = fd;
+    fds.events = POLLIN;
+    fds.revents = 0;
+
+    r = poll(&fds,1,NB_TIMEOUT);
+    if(r!=1)
+        return FALSE;
+
+    close(fd);
+    TRACE("Got response!\n");
+    return TRUE;
+}
+
+#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;
+
+    ERR("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))
+    {
+        ERR("Received %d bytes\n",r);
+        ERR("%02x %02x %02x %02x\n", buffer[0],buffer[1],buffer[2],buffer[3]);
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+static BOOL NB_SendData(int fd, unsigned char *data, int size)
+{
+    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],size);
+
+    r = write(fd, buffer, NB_HDRSIZE);
+    if(r!=NB_HDRSIZE)
+        return FALSE;
+
+    r = write(fd, data, size);
+    if(r!=size)
+    {
+        ERR("write failed\n");
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+static BOOL NB_RecvData(int fd, unsigned char *data, int *outlen)
+{
+    int r,len;
+    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;
+    }
+
+    len = NBR_GETWORD(&buffer[2]);
+    r = read(fd, data, len);
+    if(len!=r)
+    {
+        ERR("Received %d bytes\n",r);
+        return FALSE;
+    }
+    *outlen = len;
+
+    return TRUE;
+}
+
+static BOOL NB_Transaction(int fd, unsigned char *buffer, int len, int *outlen)
+{
+    int r,i;
+    struct pollfd fds;
+
+    DPRINTF("Sending request:\n");
+    for(i=0; i<len; i++)
+        DPRINTF("%02X%c",buffer[i],(((i+1)!=len)&&((i+1)%16))?' ':'\n');
+
+    if(!NB_SendData(fd,buffer,len))
+        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, buffer, outlen))
+        return FALSE;
+
+    len = *outlen;
+    DPRINTF("Got response:\n");
+    for(i=0; i<len; i++)
+        DPRINTF("%02X%c",buffer[i],(((i+1)!=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)
+{
+    if(buffer[SMB_ERRCLASS]==0)
+        return STATUS_SUCCESS;
+    /* 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 buffer[0x100];
+    int buflen,len = 0;
+
+    ERR("\n");
+
+    memset(buffer,0,sizeof buffer);
+
+    len = SMB_Header(buffer, SMB_COM_NEGOTIATE, 0, 0);
+ 
+    /* parameters */
+    buffer[len++] = 0; /* no parameters */
+
+    /* command buffer */
+    buflen = strlen(SMB_ProtocolDialect)+2;  /* include type and nul byte */
+    SMB_ADDWORD(&buffer[len],buflen); len += 2;
+
+    buffer[len] = 0x02;
+    strcpy(&buffer[len+1],SMB_ProtocolDialect);
+    len += buflen;
+
+    if(!NB_Transaction(fd, buffer, len, &len))
+    {
+        ERR("Failed\n");
+        return FALSE;
+    }
+
+    /* FIXME: check response */
+    if(SMB_GetError(buffer))
+    {
+        ERR("returned error\n");
+        return FALSE;
+    }
+
+    *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 buffer[0x100];
+    int len = 0;
+    int i,pcount,bcount;
+
+    memset(buffer,0,sizeof buffer);
+
+    len = SMB_Header(buffer, SMB_COM_SESSION_SETUP_ANDX, 0, 0);
+
+    buffer[len++] = 0;    /* no parameters? */
+
+    buffer[len++] = 0xff; /* AndXCommand: secondary request */
+    buffer[len++] = 0x00; /* AndXReserved */
+    SMB_ADDWORD(&buffer[len],0); len += 2;     /* AndXOffset */
+    SMB_ADDWORD(&buffer[len],0x400); len += 2; /* MaxBufferSize */
+    SMB_ADDWORD(&buffer[len],1); len += 2;     /* MaxMpxCount */
+    SMB_ADDWORD(&buffer[len],0); len += 2;     /* VcNumber */
+    SMB_ADDWORD(&buffer[len],0); len += 2;     /* SessionKey */
+    SMB_ADDWORD(&buffer[len],0); len += 2;     /* SessionKey */
+    SMB_ADDWORD(&buffer[len],0); len += 2;     /* Password length */
+    SMB_ADDWORD(&buffer[len],0); len += 2;     /* Reserved */
+    SMB_ADDWORD(&buffer[len],0); len += 2;     /* Reserved */
+
+    /* FIXME: add name and password here */
+    buffer[len++] = 0; /* number of bytes in password */
+
+    if(!NB_Transaction(fd, buffer, len, &len))
+        return FALSE;
+
+    if(SMB_GetError(buffer))
+        return FALSE;
+
+    pcount = SMB_PARAM_COUNT(buffer);
+
+    if( (SMB_HDRSIZE+pcount*2) > len )
+    {
+        ERR("Bad parameter count %d\n",pcount);
+        return FALSE;
+    }
+
+    DPRINTF("SMB_COM_SESSION_SETUP response, %d args: ",pcount);
+    for(i=0; i<pcount; i++)
+        DPRINTF("%04x ",SMB_PARAM(buffer,i));
+    DPRINTF("\n");
+
+    bcount = SMB_BUFFER_COUNT(buffer);
+    if( (SMB_HDRSIZE+pcount*2+2+bcount) > 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<bcount; i++)
+    {
+        unsigned char ch = SMB_BUFFER(buffer,i);
+        DPRINTF("%c", isprint(ch)?ch:' ');
+    }
+    DPRINTF("\n");
+
+    *userid = SMB_GETWORD(&buffer[SMB_USERID]);
+
+    return TRUE;
+}
+
+static BOOL SMB_TreeConnect(int fd, USHORT user_id, LPCSTR share_name, USHORT *treeid)
+{
+    unsigned char buffer[0x100];
+    int len = 0,slen;
+
+    ERR("%s\n",share_name);
+
+    memset(buffer,0,sizeof buffer);
+
+    len = SMB_Header(buffer, SMB_COM_TREE_CONNECT, 0, user_id);
+
+    buffer[len++] = 4; /* parameters */
+
+    buffer[len++] = 0xff; /* AndXCommand: secondary request */
+    buffer[len++] = 0x00; /* AndXReserved */
+    SMB_ADDWORD(&buffer[len],0); len += 2; /* AndXOffset */
+    SMB_ADDWORD(&buffer[len],0); len += 2; /* Flags */
+    SMB_ADDWORD(&buffer[len],1); len += 2; /* Password length */
+    
+    /* SMB command buffer */
+    SMB_ADDWORD(&buffer[len],3); len += 2; /* command buffer len */
+    buffer[len++] = 0; /* null terminated password */
+
+    slen = strlen(share_name);
+    if(slen<(sizeof buffer-len))
+        strcpy(&buffer[len], share_name);
+    else
+        return FALSE;
+    len += slen+1;
+ 
+    /* name of the service */
+    buffer[len++] = 0;
+
+    if(!NB_Transaction(fd, buffer, len, &len))
+        return FALSE;
+
+    if(SMB_GetError(buffer))
+        return FALSE;
+
+    *treeid = SMB_GETWORD(&buffer[SMB_TREEID]);
+
+    ERR("OK, treeid = %04x\n", *treeid);
+
+    return TRUE;
+}
+
+static BOOL SMB_NtCreateOpen(int fd, USHORT tree_id, USHORT user_id, USHORT dialect,
+                              LPCSTR filename, DWORD access, DWORD sharing,
+                              LPSECURITY_ATTRIBUTES sa, DWORD creation,
+                              DWORD attributes, HANDLE template, USHORT *file_id )
+{
+    unsigned char buffer[0x100];
+    int len = 0,slen;
+
+    ERR("%s\n",filename);
+
+    memset(buffer,0,sizeof buffer);
+
+    len = SMB_Header(buffer, SMB_COM_NT_CREATE_ANDX, tree_id, user_id);
+
+    /* 0 */
+    buffer[len++] = 24; /* parameters */
+
+    buffer[len++] = 0xff; /* AndXCommand: secondary request */
+    buffer[len++] = 0x00; /* AndXReserved */
+    SMB_ADDWORD(&buffer[len],0); len += 2; /* AndXOffset */
+
+    buffer[len++] = 0;                     /* reserved */
+    slen = strlen(filename);
+    SMB_ADDWORD(&buffer[len],slen);    len += 2; /* name length */
+
+    /* 0x08 */
+    SMB_ADDDWORD(&buffer[len],0);      len += 4; /* flags */
+    SMB_ADDDWORD(&buffer[len],0);      len += 4; /* root directory fid */
+    /* 0x10 */
+    SMB_ADDDWORD(&buffer[len],access); len += 4; /* access */
+    SMB_ADDDWORD(&buffer[len],0);      len += 4; /* allocation size */
+    /* 0x18 */
+    SMB_ADDDWORD(&buffer[len],0);      len += 4; /* root directory fid */
+
+    /* 0x1c */
+    SMB_ADDDWORD(&buffer[len],0);      len += 4; /* initial allocation */
+    SMB_ADDDWORD(&buffer[len],0);      len += 4; 
+ 
+    /* 0x24 */
+    SMB_ADDDWORD(&buffer[len],attributes);      len += 4; /* ExtFileAttributes*/
+
+    /* 0x28 */
+    SMB_ADDDWORD(&buffer[len],sharing);      len += 4; /* ShareAccess */
+    
+    /* 0x2c */
+    ERR("creation = %08lx\n",creation);
+    SMB_ADDDWORD(&buffer[len],creation);      len += 4; /* CreateDisposition */
+    
+    /* 0x30 */
+    SMB_ADDDWORD(&buffer[len],creation);      len += 4; /* CreateOptions */
+    
+    /* 0x34 */
+    SMB_ADDDWORD(&buffer[len],0);      len += 4; /* Impersonation */
+
+    /* 0x38 */
+    buffer[len++] = 0;                     /* security flags */
+
+    /* 0x39 */
+    SMB_ADDWORD(&buffer[len],slen); len += 2; /* size of buffer */
+
+    if(slen<(sizeof buffer-len))
+        strcpy(&buffer[len], filename);
+    else
+        return FALSE;
+    len += slen+1;
+ 
+    /* name of the file */
+    buffer[len++] = 0;
+
+    if(!NB_Transaction(fd, buffer, len, &len))
+        return FALSE;
+
+    if(SMB_GetError(buffer))
+        return FALSE;
+
+    ERR("OK\n");
+
+    /* FIXME */
+    /* *file_id = SMB_GETWORD(&buffer[xxx]); */
+    *file_id = 0;
+    return FALSE;
+
+    return TRUE;
+}
+
+static USHORT SMB_GetMode(DWORD access, DWORD sharing)
+{
+    USHORT mode=0;
+
+    switch(access&(GENERIC_READ|GENERIC_WRITE))
+    {
+    case GENERIC_READ:
+        mode |= OF_READ;
+        break;
+    case GENERIC_WRITE:
+        mode |= OF_WRITE;
+        break;
+    case (GENERIC_READ|GENERIC_WRITE):
+        mode |= OF_READWRITE;
+        break;
+    }
+
+    switch(sharing&(FILE_SHARE_READ|FILE_SHARE_WRITE))
+    {
+    case (FILE_SHARE_READ|FILE_SHARE_WRITE):
+        mode |= OF_SHARE_DENY_NONE;
+        break;
+    case FILE_SHARE_READ:
+        mode |= OF_SHARE_DENY_WRITE;
+        break;
+    case FILE_SHARE_WRITE:
+        mode |= OF_SHARE_DENY_READ;
+        break;
+    default:
+        mode |= OF_SHARE_EXCLUSIVE;
+        break;
+    }
+
+    return mode;
+}
+
+/* inverse of FILE_ConvertOFMode */
+static BOOL SMB_OpenAndX(int fd, USHORT tree_id, USHORT user_id, USHORT dialect,
+                              LPCSTR filename, DWORD access, DWORD sharing,
+                              DWORD creation, DWORD attributes, USHORT *file_id )
+{
+    unsigned char buffer[0x100];
+    int len = 0;
+    USHORT mode;
+
+    ERR("%s\n",filename);
+
+    mode = SMB_GetMode(access,sharing);
+
+    memset(buffer,0,sizeof buffer);
+
+    len = SMB_Header(buffer, SMB_COM_OPEN_ANDX, tree_id, user_id);
+
+    /* 0 */
+    buffer[len++] = 15; /* parameters */
+    buffer[len++] = 0xff; /* AndXCommand: secondary request */
+    buffer[len++] = 0x00; /* AndXReserved */
+    SMB_ADDWORD(buffer+len,0); len+=2; /* AndXOffset */
+    SMB_ADDWORD(buffer+len,0); len+=2; /* Flags */
+    SMB_ADDWORD(buffer+len,mode); len+=2; /* desired access */
+    SMB_ADDWORD(buffer+len,0); len+=2; /* search attributes */
+    SMB_ADDWORD(buffer+len,0); len+=2;
+
+    /*FIXME: complete */
+    return FALSE;
+}
+
+static BOOL SMB_Open(int fd, USHORT tree_id, USHORT user_id, USHORT dialect,
+                              LPCSTR filename, DWORD access, DWORD sharing,
+                              DWORD creation, DWORD attributes, USHORT *file_id )
+{
+    unsigned char buffer[0x100];
+    int len = 0,slen,pcount,i;
+    USHORT mode = SMB_GetMode(access,sharing);
+
+    ERR("%s\n",filename);
+
+    memset(buffer,0,sizeof buffer);
+
+    len = SMB_Header(buffer, SMB_COM_OPEN, tree_id, user_id);
+
+    /* 0 */
+    buffer[len++] = 2; /* parameters */
+    SMB_ADDWORD(buffer+len,mode); len+=2;
+    SMB_ADDWORD(buffer+len,0);    len+=2; /* search attributes */
+
+    slen = strlen(filename)+2;   /* inc. nul and BufferFormat */
+    SMB_ADDWORD(buffer+len,slen); len+=2;
+
+    buffer[len] = 0x04;  /* BufferFormat */
+    strcpy(&buffer[len+1],filename);
+    len += slen;
+
+    if(!NB_Transaction(fd, buffer, len, &len))
+        return FALSE;
+
+    if(SMB_GetError(buffer))
+        return FALSE;
+
+    pcount = SMB_PARAM_COUNT(buffer);
+
+    if( (SMB_HDRSIZE+pcount*2) > len )
+    {
+        ERR("Bad parameter count %d\n",pcount);
+        return FALSE;
+    }
+
+    ERR("response, %d args: ",pcount);
+    for(i=0; i<pcount; i++)
+        DPRINTF("%04x ",SMB_PARAM(buffer,i));
+    DPRINTF("\n");
+
+    *file_id = SMB_PARAM(buffer,0);
+
+    ERR("file_id = %04x\n",*file_id);
+
+    return TRUE;
+}
+
+static BOOL SMB_Read(int fd, USHORT tree_id, USHORT user_id, USHORT dialect, USHORT file_id, DWORD offset, LPVOID out, USHORT count, LPUSHORT read)
+{
+    unsigned char *buffer;
+    int len,buf_size,n,i;
+
+    ERR("user %04x tree %04x file %04x count %04x offset %08lx\n",  
+        user_id, tree_id, file_id, count, offset);
+
+    buf_size = count+0x100;
+    buffer = (unsigned char *) HeapAlloc(GetProcessHeap(),0,buf_size);
+
+    memset(buffer,0,buf_size);
+
+    len = SMB_Header(buffer, SMB_COM_READ, tree_id, user_id);
+
+    buffer[len++] = 5;
+    SMB_ADDWORD(&buffer[len],file_id); len += 2;
+    SMB_ADDWORD(&buffer[len],count);   len += 2;
+    SMB_ADDDWORD(&buffer[len],offset); len += 4;
+    SMB_ADDWORD(&buffer[len],0);       len += 2; /* how many more bytes will be read */
+
+    buffer[len++] = 0;
+
+    if(!NB_Transaction(fd, buffer, len, &len))
+    {
+        HeapFree(GetProcessHeap(),0,buffer);
+        return FALSE;
+    }
+
+    if(SMB_GetError(buffer))
+    {
+        HeapFree(GetProcessHeap(),0,buffer);
+        return FALSE;
+    }
+
+    n = SMB_PARAM_COUNT(buffer);
+
+    if( (SMB_HDRSIZE+n*2) > len )
+    {
+        HeapFree(GetProcessHeap(),0,buffer);
+        ERR("Bad parameter count %d\n",n);
+        return FALSE;
+    }
+
+    ERR("response, %d args: ",n);
+    for(i=0; i<n; i++)
+        DPRINTF("%04x ",SMB_PARAM(buffer,i));
+    DPRINTF("\n");
+
+    n = SMB_PARAM(buffer,5) - 3;
+    if(n>count)
+        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;
+}
+
+WINAPI BOOL 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;
+}
+
diff --git a/files/smb.h b/files/smb.h
new file mode 100644
index 00000000000..268261f6ee2
--- /dev/null
+++ b/files/smb.h
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2002 Mike McCormack
+ *
+ * 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
+ */
+
+#ifndef __INC_SMB__
+#define __INC_SMB__
+
+#define NBR_ADDWORD(p,word) { (p)[1] = (word & 0xff); (p)[0] = ((word)>>8)&0xff; }
+#define NBR_GETWORD(p) ( (((p)[0])<<8) | ((p)[1]) )
+
+#define SMB_ADDWORD(p,word) { (p)[0] = (word & 0xff); (p)[1] = ((word)>>8)&0xff; }
+#define SMB_GETWORD(p) ( (((p)[1])<<8) | ((p)[0]) )
+#define SMB_ADDDWORD(p,w) { (p)[3]=((w)>>24)&0xff; (p)[2]=((w)>>16)&0xff; (p)[1]=((w)>>8)&0xff; (p)[0]=(w)&0xff; }
+
+#define SMB_COM_CREATE_DIRECTORY       0x00
+#define SMB_COM_DELETE_DIRECTORY       0x01
+#define SMB_COM_OPEN                   0x02
+#define SMB_COM_CREATE                 0x03
+#define SMB_COM_CLOSE                  0x04
+#define SMB_COM_FLUSH                  0x05
+#define SMB_COM_DELETE                 0x06
+#define SMB_COM_RENAME                 0x07
+#define SMB_COM_QUERY_INFORMATION      0x08
+#define SMB_COM_SET_INFORMATION        0x09
+#define SMB_COM_READ                   0x0A
+#define SMB_COM_WRITE                  0x0B
+#define SMB_COM_LOCK_BYTE_RANGE        0x0C
+#define SMB_COM_UNLOCK_BYTE_RANGE      0x0D
+#define SMB_COM_CREATE_TEMPORARY       0x0E
+#define SMB_COM_CREATE_NEW             0x0F
+#define SMB_COM_CHECK_DIRECTORY        0x10
+#define SMB_COM_PROCESS_EXIT           0x11
+#define SMB_COM_SEEK                   0x12
+#define SMB_COM_LOCK_AND_READ          0x13
+#define SMB_COM_WRITE_AND_UNLOCK       0x14
+#define SMB_COM_READ_RAW               0x1A
+#define SMB_COM_READ_MPX               0x1B
+#define SMB_COM_READ_MPX_SECONDARY     0x1C
+#define SMB_COM_WRITE_RAW              0x1D
+#define SMB_COM_WRITE_MPX              0x1E
+#define SMB_COM_WRITE_COMPLETE         0x20
+#define SMB_COM_SET_INFORMATION2       0x22
+#define SMB_COM_QUERY_INFORMATION2     0x23
+#define SMB_COM_LOCKING_ANDX           0x24
+#define SMB_COM_TRANSACTION            0x25
+#define SMB_COM_TRANSACTION_SECONDARY  0x26
+#define SMB_COM_IOCTL                  0x27
+#define SMB_COM_IOCTL_SECONDARY        0x28
+#define SMB_COM_COPY                   0x29
+#define SMB_COM_MOVE                   0x2A
+#define SMB_COM_ECHO                   0x2B
+#define SMB_COM_WRITE_AND_CLOSE        0x2C
+#define SMB_COM_OPEN_ANDX              0x2D
+#define SMB_COM_READ_ANDX              0x2E
+#define SMB_COM_WRITE_ANDX             0x2F
+#define SMB_COM_CLOSE_AND_TREE_DISC    0x31
+#define SMB_COM_TRANSACTION2           0x32
+#define SMB_COM_TRANSACTION2_SECONDARY 0x33
+#define SMB_COM_FIND_CLOSE2            0x34
+#define SMB_COM_FIND_NOTIFY_CLOSE      0x35
+#define SMB_COM_TREE_CONNECT           0x70
+#define SMB_COM_TREE_DISCONNECT        0x71
+#define SMB_COM_NEGOTIATE              0x72
+#define SMB_COM_SESSION_SETUP_ANDX     0x73
+#define SMB_COM_LOGOFF_ANDX            0x74
+#define SMB_COM_TREE_CONNECT_ANDX      0x75
+#define SMB_COM_QUERY_INFORMATION_DISK 0x80
+#define SMB_COM_SEARCH                 0x81
+#define SMB_COM_FIND                   0x82
+#define SMB_COM_FIND_UNIQUE            0x83
+#define SMB_COM_NT_TRANSACT            0xA0
+#define SMB_COM_NT_TRANSACT_SECONDARY  0xA1
+#define SMB_COM_NT_CREATE_ANDX         0xA2
+#define SMB_COM_NT_CANCEL              0xA4
+#define SMB_COM_OPEN_PRINT_FILE        0xC0
+#define SMB_COM_WRITE_PRINT_FILE       0xC1
+#define SMB_COM_CLOSE_PRINT_FILE       0xC2
+#define SMB_COM_GET_PRINT_QUEUE        0xC3
+
+extern WINAPI BOOL SMB_ReadFile(HANDLE hFile, LPVOID buffer, DWORD bytesToRead, LPDWORD bytesRead, LPOVERLAPPED lpOverlapped);
+extern HANDLE WINAPI SMB_CreateFileA( LPCSTR filename, DWORD access, DWORD sharing,
+                              LPSECURITY_ATTRIBUTES sa, DWORD creation,
+                              DWORD attributes, HANDLE template );
+
+#endif /* __INC_SMB__ */
+
diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h
index 086de667bf3..eb54534726f 100644
--- a/include/wine/server_protocol.h
+++ b/include/wine/server_protocol.h
@@ -736,7 +736,8 @@ enum fd_type
 {
     FD_TYPE_INVALID,
     FD_TYPE_DEFAULT,
-    FD_TYPE_CONSOLE
+    FD_TYPE_CONSOLE,
+    FD_TYPE_SMB
 };
 #define FD_FLAG_OVERLAPPED 0x01
 #define FD_FLAG_TIMEOUT    0x02
@@ -2321,6 +2322,41 @@ struct get_named_pipe_info_reply
 };
 
 
+struct create_smb_request
+{
+    struct request_header __header;
+    int            fd;
+    unsigned int   tree_id;
+    unsigned int   user_id;
+    unsigned int   file_id;
+    unsigned int   dialect;
+};
+struct create_smb_reply
+{
+    struct reply_header __header;
+    handle_t       handle;
+};
+
+
+struct get_smb_info_request
+{
+    struct request_header __header;
+    handle_t       handle;
+    unsigned int   flags;
+    unsigned int   offset;
+};
+struct get_smb_info_reply
+{
+    struct reply_header __header;
+    unsigned int   tree_id;
+    unsigned int   user_id;
+    unsigned int   dialect;
+    unsigned int   file_id;
+    unsigned int   offset;
+};
+#define SMBINFO_SET_OFFSET    0x01
+
+
 
 struct create_window_request
 {
@@ -2742,6 +2778,8 @@ enum request
     REQ_wait_named_pipe,
     REQ_disconnect_named_pipe,
     REQ_get_named_pipe_info,
+    REQ_create_smb,
+    REQ_get_smb_info,
     REQ_create_window,
     REQ_link_window,
     REQ_destroy_window,
@@ -2899,6 +2937,8 @@ union generic_request
     struct wait_named_pipe_request wait_named_pipe_request;
     struct disconnect_named_pipe_request disconnect_named_pipe_request;
     struct get_named_pipe_info_request get_named_pipe_info_request;
+    struct create_smb_request create_smb_request;
+    struct get_smb_info_request get_smb_info_request;
     struct create_window_request create_window_request;
     struct link_window_request link_window_request;
     struct destroy_window_request destroy_window_request;
@@ -3054,6 +3094,8 @@ union generic_reply
     struct wait_named_pipe_reply wait_named_pipe_reply;
     struct disconnect_named_pipe_reply disconnect_named_pipe_reply;
     struct get_named_pipe_info_reply get_named_pipe_info_reply;
+    struct create_smb_reply create_smb_reply;
+    struct get_smb_info_reply get_smb_info_reply;
     struct create_window_reply create_window_reply;
     struct link_window_reply link_window_reply;
     struct destroy_window_reply destroy_window_reply;
@@ -3075,6 +3117,6 @@ union generic_reply
     struct get_window_properties_reply get_window_properties_reply;
 };
 
-#define SERVER_PROTOCOL_VERSION 72
+#define SERVER_PROTOCOL_VERSION 73
 
 #endif /* __WINE_WINE_SERVER_PROTOCOL_H */
diff --git a/server/Makefile.in b/server/Makefile.in
index 1bcfaaa3752..d62c3d954be 100644
--- a/server/Makefile.in
+++ b/server/Makefile.in
@@ -31,6 +31,7 @@ C_SRCS = \
 	select.c \
 	semaphore.c \
 	serial.c \
+	smb.c \
 	snapshot.c \
 	sock.c \
 	thread.c \
diff --git a/server/protocol.def b/server/protocol.def
index ad8292e2a5d..6cc06b5c51d 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -563,7 +563,8 @@ enum fd_type
 {
     FD_TYPE_INVALID,
     FD_TYPE_DEFAULT,
-    FD_TYPE_CONSOLE
+    FD_TYPE_CONSOLE,
+    FD_TYPE_SMB
 };
 #define FD_FLAG_OVERLAPPED 0x01
 #define FD_FLAG_TIMEOUT    0x02
@@ -1634,6 +1635,31 @@ enum message_type
 @END
 
 
+@REQ(create_smb)
+    int            fd;
+    unsigned int   tree_id;
+    unsigned int   user_id;
+    unsigned int   file_id;
+    unsigned int   dialect;
+@REPLY
+    handle_t       handle;
+@END
+
+
+@REQ(get_smb_info)
+    handle_t       handle;
+    unsigned int   flags;
+    unsigned int   offset;
+@REPLY
+    unsigned int   tree_id;
+    unsigned int   user_id;
+    unsigned int   dialect;
+    unsigned int   file_id;
+    unsigned int   offset;
+@END
+#define SMBINFO_SET_OFFSET    0x01
+
+
 /* Create a window */
 @REQ(create_window)
     user_handle_t  parent;      /* parent window */
diff --git a/server/request.h b/server/request.h
index 07aed4082b3..9a1350efbe9 100644
--- a/server/request.h
+++ b/server/request.h
@@ -232,6 +232,8 @@ DECL_HANDLER(connect_named_pipe);
 DECL_HANDLER(wait_named_pipe);
 DECL_HANDLER(disconnect_named_pipe);
 DECL_HANDLER(get_named_pipe_info);
+DECL_HANDLER(create_smb);
+DECL_HANDLER(get_smb_info);
 DECL_HANDLER(create_window);
 DECL_HANDLER(link_window);
 DECL_HANDLER(destroy_window);
@@ -388,6 +390,8 @@ static const req_handler req_handlers[REQ_NB_REQUESTS] =
     (req_handler)req_wait_named_pipe,
     (req_handler)req_disconnect_named_pipe,
     (req_handler)req_get_named_pipe_info,
+    (req_handler)req_create_smb,
+    (req_handler)req_get_smb_info,
     (req_handler)req_create_window,
     (req_handler)req_link_window,
     (req_handler)req_destroy_window,
diff --git a/server/smb.c b/server/smb.c
new file mode 100644
index 00000000000..ab0e84d2a77
--- /dev/null
+++ b/server/smb.c
@@ -0,0 +1,192 @@
+/*
+ * Server-side smb network file management
+ *
+ * Copyright (C) 1998 Alexandre Julliard
+ * Copyright (C) 2000, 2001, 2002 Mike McCormack
+ *
+ * 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: if you can't find something to fix, 
+ *          you're not looking hard enough
+ */
+
+#include "config.h"
+
+#include <assert.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#ifdef HAVE_SYS_ERRNO_H
+#include <sys/errno.h>
+#endif
+#include <sys/time.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+#include <utime.h>
+#include <termios.h>
+#include <sys/ioctl.h>
+
+#include "winerror.h"
+#include "winbase.h"
+
+#include "handle.h"
+#include "thread.h"
+#include "request.h"
+
+static void smb_dump( struct object *obj, int verbose );
+static int smb_get_fd( struct object *obj );
+static int smb_get_info( struct object *obj, struct get_file_info_reply *reply, int *flags );
+static int smb_get_poll_events( struct object *obj );
+static void destroy_smb(struct object *obj);
+
+struct smb
+{
+    struct object       obj;
+    unsigned int        tree_id;
+    unsigned int        user_id;
+    unsigned int        dialect;
+    unsigned int        file_id;
+    unsigned int        offset;
+};
+
+static const struct object_ops smb_ops =
+{
+    sizeof(struct smb),        /* size */
+    smb_dump,                  /* dump */
+    default_poll_add_queue,    /* add_queue */
+    default_poll_remove_queue, /* remove_queue */
+    default_poll_signaled,     /* signaled */
+    no_satisfied,              /* satisfied */
+    smb_get_poll_events,       /* get_poll_events */
+    default_poll_event,        /* poll_event */
+    smb_get_fd,                /* get_fd */
+    no_flush,                  /* flush */
+    smb_get_info,              /* get_file_info */
+    NULL,                      /* queue_async */
+    destroy_smb                /* destroy */
+};
+
+static void destroy_smb( struct object *obj)
+{
+    /* struct smb *smb = (struct smb *)obj; */
+    assert( obj->ops == &smb_ops );
+}
+
+static void smb_dump( struct object *obj, int verbose )
+{
+    struct smb *smb = (struct smb *)obj;
+    assert( obj->ops == &smb_ops );
+    fprintf( stderr, "smb file with socket fd=%d \n", smb->obj.fd );
+}
+
+struct smb *get_smb_obj( struct process *process, handle_t handle, unsigned int access )
+{
+    return (struct smb *)get_handle_obj( process, handle, access, &smb_ops );
+}
+
+static int smb_get_poll_events( struct object *obj )
+{
+    /* struct smb *smb = (struct smb *)obj; */
+    int events = 0;
+    assert( obj->ops == &smb_ops );
+
+    events |= POLLIN;
+
+    /* fprintf(stderr,"poll events are %04x\n",events); */
+
+    return events;
+}
+
+static int smb_get_fd( struct object *obj )
+{
+    struct smb *smb = (struct smb *)obj;
+    assert( obj->ops == &smb_ops );
+    return smb->obj.fd;
+}
+
+static int smb_get_info( struct object *obj, struct get_file_info_reply *reply, int *flags )
+{
+    /* struct smb *smb = (struct smb *) obj; */
+    assert( obj->ops == &smb_ops );
+
+    if (reply)
+    {
+        reply->type        = FILE_TYPE_CHAR;
+        reply->attr        = 0;
+        reply->access_time = 0;
+        reply->write_time  = 0;
+        reply->size_high   = 0;
+        reply->size_low    = 0;
+        reply->links       = 0;
+        reply->index_high  = 0;
+        reply->index_low   = 0;
+        reply->serial      = 0;
+    }
+
+    *flags = 0;
+
+    return FD_TYPE_SMB;
+}
+
+/* create a smb */
+DECL_HANDLER(create_smb)
+{
+    struct smb *smb;
+    int fd;
+
+    reply->handle = 0;
+
+    fd = thread_get_inflight_fd( current, req->fd );
+    if (fd == -1)
+    {
+        set_error( STATUS_INVALID_HANDLE );
+        return;
+    }
+
+    smb = alloc_object( &smb_ops, fd );
+    if (smb)
+    {
+        smb->tree_id = req->tree_id;
+        smb->user_id = req->user_id;
+        smb->dialect = req->dialect;
+        smb->file_id = req->file_id;
+        smb->offset = 0;
+        reply->handle = alloc_handle( current->process, smb, GENERIC_READ, 0);
+        release_object( smb );
+    }
+}
+
+DECL_HANDLER(get_smb_info)
+{
+    struct smb *smb;
+
+    if ((smb = get_smb_obj( current->process, req->handle, 0 )))
+    {
+        if(req->flags & SMBINFO_SET_OFFSET)
+            smb->offset = req->offset;
+
+        reply->tree_id = smb->tree_id;
+        reply->user_id = smb->user_id;
+        reply->dialect = smb->dialect;
+        reply->file_id = smb->file_id;
+        reply->offset  = smb->offset;
+
+        release_object( smb );
+    }
+}
+
diff --git a/server/trace.c b/server/trace.c
index c4c2879ff1d..2e36fe4736f 100644
--- a/server/trace.c
+++ b/server/trace.c
@@ -1843,6 +1843,36 @@ static void dump_get_named_pipe_info_reply( const struct get_named_pipe_info_rep
     fprintf( stderr, " insize=%08x", req->insize );
 }
 
+static void dump_create_smb_request( const struct create_smb_request *req )
+{
+    fprintf( stderr, " fd=%d,", req->fd );
+    fprintf( stderr, " tree_id=%08x,", req->tree_id );
+    fprintf( stderr, " user_id=%08x,", req->user_id );
+    fprintf( stderr, " file_id=%08x,", req->file_id );
+    fprintf( stderr, " dialect=%08x", req->dialect );
+}
+
+static void dump_create_smb_reply( const struct create_smb_reply *req )
+{
+    fprintf( stderr, " handle=%d", req->handle );
+}
+
+static void dump_get_smb_info_request( const struct get_smb_info_request *req )
+{
+    fprintf( stderr, " handle=%d,", req->handle );
+    fprintf( stderr, " flags=%08x,", req->flags );
+    fprintf( stderr, " offset=%08x", req->offset );
+}
+
+static void dump_get_smb_info_reply( const struct get_smb_info_reply *req )
+{
+    fprintf( stderr, " tree_id=%08x,", req->tree_id );
+    fprintf( stderr, " user_id=%08x,", req->user_id );
+    fprintf( stderr, " dialect=%08x,", req->dialect );
+    fprintf( stderr, " file_id=%08x,", req->file_id );
+    fprintf( stderr, " offset=%08x", req->offset );
+}
+
 static void dump_create_window_request( const struct create_window_request *req )
 {
     fprintf( stderr, " parent=%08x,", req->parent );
@@ -2193,6 +2223,8 @@ static const dump_func req_dumpers[REQ_NB_REQUESTS] = {
     (dump_func)dump_wait_named_pipe_request,
     (dump_func)dump_disconnect_named_pipe_request,
     (dump_func)dump_get_named_pipe_info_request,
+    (dump_func)dump_create_smb_request,
+    (dump_func)dump_get_smb_info_request,
     (dump_func)dump_create_window_request,
     (dump_func)dump_link_window_request,
     (dump_func)dump_destroy_window_request,
@@ -2346,6 +2378,8 @@ static const dump_func reply_dumpers[REQ_NB_REQUESTS] = {
     (dump_func)0,
     (dump_func)0,
     (dump_func)dump_get_named_pipe_info_reply,
+    (dump_func)dump_create_smb_reply,
+    (dump_func)dump_get_smb_info_reply,
     (dump_func)dump_create_window_reply,
     (dump_func)dump_link_window_reply,
     (dump_func)0,
@@ -2499,6 +2533,8 @@ static const char * const req_names[REQ_NB_REQUESTS] = {
     "wait_named_pipe",
     "disconnect_named_pipe",
     "get_named_pipe_info",
+    "create_smb",
+    "get_smb_info",
     "create_window",
     "link_window",
     "destroy_window",
-- 
GitLab