diff -ruN -X dontdiff /usr/src/linux-2.4.9-e.27/fs/smbfs/ChangeLog linux-2.4.9-e.27/fs/smbfs/ChangeLog
--- /usr/src/linux-2.4.9-e.27/fs/smbfs/ChangeLog	Thu Jun 21 09:07:57 2001
+++ linux-2.4.9-e.27/fs/smbfs/ChangeLog	Sun Oct 19 18:53:11 2003
@@ -1,5 +1,47 @@
 ChangeLog for smbfs.
 
+2001-12-31 René Scharfe <l.s.r@web.de>
+
+	* inode.c: added smb_show_options to show mount options in /proc/mounts
+	* inode.c, getopt.c, getopt.h: merged flag and has_arg in struct option
+	* inode.c: use S_IRWXUGO where appropriate
+
+2001-12-22 Urban Widmark <urban@teststation.com>
+
+	* file.c, proc.c: Fix problems triggered by the "fsx test"
+
+2002-02-02 John Newbigin <jn@it.swin.edu.au>
+	* Clean up codeing style and CIFS Extensions for UNIX bugs
+	* Implement follow_link 
+
+2002-01-28 John Newbigin <jn@it.swin.edu.au>
+	* Implementation of CIFS Extensions for UNIX systems
+
+2001-09-17 Urban Widmark <urban@teststation.com>
+
+	* proc.c: Use 4096 (was 512) as the blocksize for better write
+	  performance (patch originally by Jan Kratochvil)
+	* proc.c: Skip disconnect smb, allows umount on unreachable servers.
+	* proc.c: Go back to the interruptible sleep as reconnects seem to
+	  handle it now.
+	* *.c: use autogenerated and private proto.h
+
+2000-11-22 Igor Zhbanov <bsg@uniyar.ac.ru>
+
+	* proc.c: fixed date_unix2dos for dates earlier than 01/01/1980
+	  and date_dos2unix for date==0 (from 2.2)
+
+2001-07-13 Rob Radez <rob@osinvestor.com>
+
+	* proc.c: make smb_errno return negative error values
+
+2001-07-09 Jochen Dolze <dolze@epcnet.de>
+
+	* inode.c: smb_statfs always returned success.
+	* proc.c, ioctl.c: Allow smbmount to signal failure to reconnect with
+	  a NULL argument to SMB_IOC_NEWCONN (speeds up error detection).
+	* proc.c: Add some of the missing error codes to smb_errno
+
 2001-06-12 Urban Widmark <urban@teststation.com>
 
 	* proc.c: replace the win95-flush fix with smb_seek, when needed.
diff -ruN -X dontdiff /usr/src/linux-2.4.9-e.27/fs/smbfs/Makefile linux-2.4.9-e.27/fs/smbfs/Makefile
--- /usr/src/linux-2.4.9-e.27/fs/smbfs/Makefile	Sat Feb 17 11:04:24 2001
+++ linux-2.4.9-e.27/fs/smbfs/Makefile	Sun Oct 19 18:53:11 2003
@@ -9,7 +9,7 @@
 
 O_TARGET := smbfs.o
 
-obj-y   := proc.o dir.o cache.o sock.o inode.o file.o ioctl.o getopt.o
+obj-y   := proc.o dir.o cache.o sock.o inode.o file.o ioctl.o getopt.o symlink.o
 obj-m   := $(O_TARGET)
 
 # If you want debugging output, you may add these flags to the EXTRA_CFLAGS
@@ -23,3 +23,19 @@
 #EXTRA_CFLAGS += -Werror
 
 include $(TOPDIR)/Rules.make
+
+#
+# Maintainer rules
+#
+
+# getopt.c not included. It is intentionally separate
+SRC = proc.c dir.c cache.c sock.c inode.c file.c ioctl.c symlink.c
+
+proto:
+	-rm -f proto.h
+	@echo >  proto2.h "/*"
+	@echo >> proto2.h " *  Autogenerated with cproto on: " `date`
+	@echo >> proto2.h " */"
+	@echo >> proto2.h ""
+	cproto -E "gcc -E" -e -v -I $(TOPDIR)/include -DMAKING_PROTO -D__KERNEL__ $(SRC) >> proto2.h
+	mv proto2.h proto.h
diff -ruN -X dontdiff /usr/src/linux-2.4.9-e.27/fs/smbfs/cache.c linux-2.4.9-e.27/fs/smbfs/cache.c
--- /usr/src/linux-2.4.9-e.27/fs/smbfs/cache.c	Wed Aug  6 05:05:29 2003
+++ linux-2.4.9-e.27/fs/smbfs/cache.c	Sun Oct 19 18:53:11 2003
@@ -20,6 +20,7 @@
 #include <asm/page.h>
 
 #include "smb_debug.h"
+#include "proto.h"
 
 /*
  * Force the next attempt to use the cache to be a timeout.
diff -ruN -X dontdiff /usr/src/linux-2.4.9-e.27/fs/smbfs/dir.c linux-2.4.9-e.27/fs/smbfs/dir.c
--- /usr/src/linux-2.4.9-e.27/fs/smbfs/dir.c	Wed May 16 06:40:55 2001
+++ linux-2.4.9-e.27/fs/smbfs/dir.c	Mon Nov 24 21:59:21 2003
@@ -18,6 +18,7 @@
 #include <linux/smbno.h>
 
 #include "smb_debug.h"
+#include "proto.h"
 
 static int smb_readdir(struct file *, void *, filldir_t);
 static int smb_dir_open(struct inode *, struct file *);
@@ -29,6 +30,8 @@
 static int smb_unlink(struct inode *, struct dentry *);
 static int smb_rename(struct inode *, struct dentry *,
 		      struct inode *, struct dentry *);
+static int smb_make_node(struct inode *,struct dentry *,int,int);
+static int smb_link(struct dentry *, struct inode *, struct dentry *);
 
 struct file_operations smb_dir_operations =
 {
@@ -48,6 +51,9 @@
 	rename:		smb_rename,
 	revalidate:	smb_revalidate_inode,
 	setattr:	smb_notify_change,
+	symlink:	smb_symlink,
+	mknod:		smb_make_node,
+	link:           smb_link,
 };
 
 /*
@@ -432,7 +438,7 @@
 /*
  * This code is common to all routines creating a new inode.
  */
-static int
+int
 smb_instantiate(struct dentry *dentry, __u16 fileid, int have_id)
 {
 	struct smb_sb_info *server = server_from_dentry(dentry);
@@ -452,8 +458,7 @@
 	if (!inode)
 		goto out_no_inode;
 
-	if (have_id)
-	{
+	if (have_id) {
 		inode->u.smbfs_i.fileid = fileid;
 		inode->u.smbfs_i.access = SMB_O_RDWR;
 		inode->u.smbfs_i.open = server->generation;
@@ -465,8 +470,7 @@
 out_no_inode:
 	error = -EACCES;
 out_close:
-	if (have_id)
-	{
+	if (have_id) {
 		PARANOIA("%s/%s failed, error=%d, closing %u\n",
 			 DENTRY_PATH(dentry), error, fileid);
 		smb_close_fileid(dentry, fileid);
@@ -480,12 +484,19 @@
 {
 	__u16 fileid;
 	int error;
+	struct iattr attr;
 
 	VERBOSE("creating %s/%s, mode=%d\n", DENTRY_PATH(dentry), mode);
 
 	smb_invalid_dir_cache(dir);
 	error = smb_proc_create(dentry, 0, CURRENT_TIME, &fileid);
 	if (!error) {
+		if(server_from_dentry(dentry)->opt.capabilities & SMB_CAP_UNIX) {
+			/* Set attributes for new directory */
+			attr.ia_valid = ATTR_MODE;
+			attr.ia_mode = mode;
+			error = smb_proc_setattr_unix(dentry, &attr);
+		}
 		error = smb_instantiate(dentry, fileid, 1);
 	} else {
 		PARANOIA("%s/%s failed, error=%d\n",
@@ -499,10 +510,17 @@
 smb_mkdir(struct inode *dir, struct dentry *dentry, int mode)
 {
 	int error;
+	struct iattr attr;
 
 	smb_invalid_dir_cache(dir);
 	error = smb_proc_mkdir(dentry);
 	if (!error) {
+		if(server_from_dentry(dentry)->opt.capabilities & SMB_CAP_UNIX) {
+			/* Set attributes for new directory */
+			attr.ia_valid = ATTR_MODE;
+			attr.ia_mode = mode;
+			error = smb_proc_setattr_unix(dentry, &attr);
+		}
 		error = smb_instantiate(dentry, 0, 0);
 	}
 	return error;
@@ -562,12 +580,10 @@
 	 */
 	if (old_dentry->d_inode)
 		smb_close(old_dentry->d_inode);
-	if (new_dentry->d_inode)
-	{
+	if (new_dentry->d_inode) {
 		smb_close(new_dentry->d_inode);
 		error = smb_proc_unlink(new_dentry);
-		if (error)
-		{
+		if (error) {
 			VERBOSE("unlink %s/%s, error=%d\n",
 				DENTRY_PATH(new_dentry), error);
 			goto out;
@@ -579,11 +595,56 @@
 	smb_invalid_dir_cache(old_dir);
 	smb_invalid_dir_cache(new_dir);
 	error = smb_proc_mv(old_dentry, new_dentry);
-	if (!error)
-	{
+	if (!error) {
 		smb_renew_times(old_dentry);
 		smb_renew_times(new_dentry);
 	}
 out:
 	return error;
 }
+
+static
+int smb_make_node(struct inode *inode,struct dentry *dentry,int mode,int dev)
+{
+	int error = -EPERM;
+
+	if(S_ISCHR(mode)) {
+		DEBUG1("Request to make a char device node\n");
+	}
+	else if(S_ISBLK(mode)) {
+		DEBUG1("Request to make a block device node\n");
+	}
+	else if(S_ISFIFO(mode)) {
+		DEBUG1("Request to make a fifo node\n");
+	}
+	else if(S_ISSOCK(mode)) {
+		DEBUG1("Request to make a socket node\n");
+	}
+	else {
+		DEBUG1("Request to make a unsupported node\n");
+	}
+
+	return error;
+}
+
+/*
+ * dentry = existing file
+ * new_dentry = new file
+ */
+static int smb_link(struct dentry *dentry, struct inode *dir, struct dentry *new_dentry)
+{
+	int error;
+
+        DEBUG1("smb_link\n");
+        DEBUG1("dentry1 %s/%s\n", DENTRY_PATH(dentry));
+        DEBUG1("dentry2 %s/%s\n", DENTRY_PATH(new_dentry));
+        
+	smb_invalid_dir_cache(dir);
+        error = smb_proc_link(server_from_dentry(dentry), dentry, new_dentry);
+	if (!error) {
+		smb_renew_times(dentry);
+		error = smb_instantiate(new_dentry, 0, 0);
+	}
+	return error;
+}
+
diff -ruN -X dontdiff /usr/src/linux-2.4.9-e.27/fs/smbfs/file.c linux-2.4.9-e.27/fs/smbfs/file.c
--- /usr/src/linux-2.4.9-e.27/fs/smbfs/file.c	Mon Aug 13 04:13:59 2001
+++ linux-2.4.9-e.27/fs/smbfs/file.c	Sun Oct 19 18:53:11 2003
@@ -24,6 +24,8 @@
 #include <linux/smb_fs.h>
 
 #include "smb_debug.h"
+#include "proto.h"
+
 
 static int
 smb_fsync(struct file *file, struct dentry * dentry, int datasync)
@@ -269,7 +271,6 @@
 static int smb_prepare_write(struct file *file, struct page *page, 
 			     unsigned offset, unsigned to)
 {
-	kmap(page);
 	return 0;
 }
 
@@ -282,7 +283,6 @@
 	lock_kernel();
 	status = smb_updatepage(file, page, offset, to-offset);
 	unlock_kernel();
-	kunmap(page);
 	return status;
 }
 
@@ -348,8 +348,14 @@
 smb_file_release(struct inode *inode, struct file * file)
 {
 	lock_kernel();
-	if (!--inode->u.smbfs_i.openers)
+	if (!--inode->u.smbfs_i.openers) {
+		/* We must flush any dirty pages now as we won't be able to
+		   write anything after close. mmap can trigger this.
+		   "openers" should perhaps include mmap'ers ... */
+		filemap_fdatasync(inode->i_mapping);
+		filemap_fdatawait(inode->i_mapping);
 		smb_close(inode);
+	}
 	unlock_kernel();
 	return 0;
 }
diff -ruN -X dontdiff /usr/src/linux-2.4.9-e.27/fs/smbfs/getopt.c linux-2.4.9-e.27/fs/smbfs/getopt.c
--- /usr/src/linux-2.4.9-e.27/fs/smbfs/getopt.c	Sat Apr 28 07:10:32 2001
+++ linux-2.4.9-e.27/fs/smbfs/getopt.c	Sun Oct 19 18:53:11 2003
@@ -46,7 +46,7 @@
 
 	for (i = 0; opts[i].name != NULL; i++) {
 		if (!strcmp(opts[i].name, token)) {
-			if (opts[i].has_arg && (!val || !*val)) {
+			if (!opts[i].flag && (!val || !*val)) {
 				printk("%s: the %s option requires an argument\n",
 				       caller, token);
 				return -1;
diff -ruN -X dontdiff /usr/src/linux-2.4.9-e.27/fs/smbfs/getopt.h linux-2.4.9-e.27/fs/smbfs/getopt.h
--- /usr/src/linux-2.4.9-e.27/fs/smbfs/getopt.h	Tue Aug 15 06:31:10 2000
+++ linux-2.4.9-e.27/fs/smbfs/getopt.h	Sun Oct 19 18:53:11 2003
@@ -3,7 +3,6 @@
 
 struct option {
 	const char *name;
-	int has_arg;
 	unsigned long flag;
 	int val;
 };
diff -ruN -X dontdiff /usr/src/linux-2.4.9-e.27/fs/smbfs/inode.c linux-2.4.9-e.27/fs/smbfs/inode.c
--- /usr/src/linux-2.4.9-e.27/fs/smbfs/inode.c	Wed Aug  6 05:05:32 2003
+++ linux-2.4.9-e.27/fs/smbfs/inode.c	Sun Oct 19 18:53:11 2003
@@ -22,6 +22,7 @@
 #include <linux/dcache.h>
 #include <linux/smp_lock.h>
 #include <linux/nls.h>
+/* #include <linux/seq_file.h> */
 
 #include <linux/smb_fs.h>
 #include <linux/smbno.h>
@@ -32,6 +33,7 @@
 
 #include "smb_debug.h"
 #include "getopt.h"
+#include "proto.h"
 
 /* Always pick a default string */
 #ifdef CONFIG_SMB_NLS_REMOTE
@@ -40,9 +42,12 @@
 #define SMB_NLS_REMOTE ""
 #endif
 
+#define SMB_TTL_DEFAULT 1000
+
 static void smb_delete_inode(struct inode *);
 static void smb_put_super(struct super_block *);
 static int  smb_statfs(struct super_block *, struct statfs *);
+/* static int  smb_show_options(struct seq_file *, struct vfsmount *); */
 
 static struct super_operations smb_sops =
 {
@@ -50,6 +55,7 @@
 	delete_inode:	smb_delete_inode,
 	put_super:	smb_put_super,
 	statfs:		smb_statfs,
+/*	show_options:	smb_show_options, */
 };
 
 
@@ -59,7 +65,7 @@
 {
 	struct inode *result;
 
-	DEBUG1("smb_iget: %p\n", fattr);
+	VERBOSE("smb_iget: %p\n", fattr);
 
 	result = new_inode(sb);
 	if (!result)
@@ -74,6 +80,12 @@
 	} else if (S_ISDIR(result->i_mode)) {
 		result->i_op = &smb_dir_inode_operations;
 		result->i_fop = &smb_dir_operations;
+	} else if(S_ISLNK(result->i_mode)) {
+		DEBUG1("iget a symlink\n");
+		result->i_op = &smb_link_inode_operations;
+	} else {
+		DEBUG1("iget special node type\n");
+		init_special_inode(result, result->i_mode, fattr->f_rdev);
 	}
 	insert_inode_hash(result);
 	return result;
@@ -181,7 +193,22 @@
 		 * Check whether the type part of the mode changed,
 		 * and don't update the attributes if it did.
 		 */
-		if ((inode->i_mode & S_IFMT) == (fattr.f_mode & S_IFMT)) {
+
+		/* 
+		 * Don't dick with the root inode
+		 */
+		VERBOSE("inode no %ld\n", inode->i_ino);
+		if(inode->i_ino == 2)
+		{
+			return error;
+		}
+		if(S_ISLNK(inode->i_mode)) {
+			/*
+			 * We don't need to do anything here because the vfs will
+			 * call follow_link to find the target.
+			 */
+
+		} else if ((inode->i_mode & S_IFMT) == (fattr.f_mode & S_IFMT)) {
 			smb_set_inode_attr(inode, &fattr);
 		} else {
 			/*
@@ -225,7 +252,7 @@
 	struct inode *inode = dentry->d_inode;
 	int error = 0;
 
-	DEBUG1("smb_revalidate_inode\n");
+	VERBOSE("smb_revalidate_inode\n");
 	lock_kernel();
 
 	/*
@@ -258,21 +285,20 @@
 	clear_inode(ino);
 }
 
-/* FIXME: flags and has_arg could probably be merged. */
-struct option opts[] = {
-	{ "version",	1, 0, 'v' },
-	{ "win95",	0, SMB_MOUNT_WIN95, 1 },
-	{ "oldattr",	0, SMB_MOUNT_OLDATTR, 1 },
-	{ "dirattr",	0, SMB_MOUNT_DIRATTR, 1 },
-	{ "case",	0, SMB_MOUNT_CASE, 1 },
-	{ "uid",	1, 0, 'u' },
-	{ "gid",	1, 0, 'g' },
-	{ "file_mode",	1, 0, 'f' },
-	{ "dir_mode",	1, 0, 'd' },
-	{ "iocharset",	1, 0, 'i' },
-	{ "codepage",	1, 0, 'c' },
-	{ "ttl",	1, 0, 't' },
-	{ NULL,		0, 0, 0}
+static struct option opts[] = {
+	{ "version",	0, 'v' },
+	{ "win95",	SMB_MOUNT_WIN95, 1 },
+	{ "oldattr",	SMB_MOUNT_OLDATTR, 1 },
+	{ "dirattr",	SMB_MOUNT_DIRATTR, 1 },
+	{ "case",	SMB_MOUNT_CASE, 1 },
+	{ "uid",	0, 'u' },
+	{ "gid",	0, 'g' },
+	{ "file_mode",	0, 'f' },
+	{ "dir_mode",	0, 'd' },
+	{ "iocharset",	0, 'i' },
+	{ "codepage",	0, 'c' },
+	{ "ttl",	0, 't' },
+	{ NULL,		0, 0}
 };
 
 static int
@@ -309,12 +335,10 @@
 			mnt->gid = value;
 			break;
 		case 'f':
-			mnt->file_mode = value & (S_IRWXU | S_IRWXG | S_IRWXO);
-			mnt->file_mode |= S_IFREG;
+			mnt->file_mode = (value & S_IRWXUGO) | S_IFREG;
 			break;
 		case 'd':
-			mnt->dir_mode = value & (S_IRWXU | S_IRWXG | S_IRWXO);
-			mnt->dir_mode |= S_IFDIR;
+			mnt->dir_mode = (value & S_IRWXUGO) | S_IFDIR;
 			break;
 		case 'i':
 			strncpy(mnt->codepage.local_name, optarg, 
@@ -337,6 +361,47 @@
 	return c;
 }
 
+/*
+ * smb_show_options() is for displaying mount options in /proc/mounts.
+ * It tries to avoid showing settings that were not changed from their
+ * defaults.
+ */
+#if 0
+static int
+smb_show_options(struct seq_file *s, struct vfsmount *m)
+{
+	struct smb_mount_data_kernel *mnt = m->mnt_sb->u.smbfs_sb.mnt;
+	int i;
+
+	for (i = 0; opts[i].name != NULL; i++)
+		if (mnt->flags & opts[i].flag)
+			seq_printf(s, ",%s", opts[i].name);
+
+	if (mnt->uid != 0)
+		seq_printf(s, ",uid=%d", mnt->uid);
+	if (mnt->gid != 0)
+		seq_printf(s, ",gid=%d", mnt->gid);
+	if (mnt->mounted_uid != 0)
+		seq_printf(s, ",mounted_uid=%d", mnt->mounted_uid);
+
+	/* 
+	 * Defaults for file_mode and dir_mode are unknown to us; they
+	 * depend on the current umask of the user doing the mount.
+	 */
+	seq_printf(s, ",file_mode=%04o", mnt->file_mode & S_IRWXUGO);
+	seq_printf(s, ",dir_mode=%04o", mnt->dir_mode & S_IRWXUGO);
+
+	if (strcmp(mnt->codepage.local_name, CONFIG_NLS_DEFAULT))
+		seq_printf(s, ",iocharset=%s", mnt->codepage.local_name);
+	if (strcmp(mnt->codepage.remote_name, SMB_NLS_REMOTE))
+		seq_printf(s, ",codepage=%s", mnt->codepage.remote_name);
+
+	if (mnt->ttl != SMB_TTL_DEFAULT)
+		seq_printf(s, ",ttl=%d", mnt->ttl);
+
+	return 0;
+}
+#endif
 
 static void
 smb_put_super(struct super_block *sb)
@@ -344,7 +409,6 @@
 	struct smb_sb_info *server = &(sb->u.smbfs_sb);
 
 	if (server->sock_file) {
-		smb_proc_disconnect(server);
 		smb_dont_catch_keepalive(server);
 		fput(server->sock_file);
 	}
@@ -353,23 +417,24 @@
 	       kill_proc(server->conn_pid, SIGTERM, 1);
 
 	smb_kfree(server->mnt);
-	smb_kfree(sb->u.smbfs_sb.temp_buf);
+	smb_kfree(server->temp_buf);
 	if (server->packet)
 		smb_vfree(server->packet);
 
-	if(sb->u.smbfs_sb.remote_nls) {
-		unload_nls(sb->u.smbfs_sb.remote_nls);
-		sb->u.smbfs_sb.remote_nls = NULL;
-	}
-	if(sb->u.smbfs_sb.local_nls) {
-		unload_nls(sb->u.smbfs_sb.local_nls);
-		sb->u.smbfs_sb.local_nls = NULL;
+	if (server->remote_nls) {
+		unload_nls(server->remote_nls);
+		server->remote_nls = NULL;
+	}
+	if (server->local_nls) {
+		unload_nls(server->local_nls);
+		server->local_nls = NULL;
 	}
 }
 
 struct super_block *
 smb_read_super(struct super_block *sb, void *raw_data, int silent)
 {
+	struct smb_sb_info *server = &sb->u.smbfs_sb;
 	struct smb_mount_data_kernel *mnt;
 	struct smb_mount_data *oldmnt;
 	struct inode *root_inode;
@@ -389,34 +454,34 @@
 	sb->s_magic = SMB_SUPER_MAGIC;
 	sb->s_op = &smb_sops;
 
-	sb->u.smbfs_sb.mnt = NULL;
-	sb->u.smbfs_sb.sock_file = NULL;
-	init_MUTEX(&sb->u.smbfs_sb.sem);
-	init_waitqueue_head(&sb->u.smbfs_sb.wait);
-	sb->u.smbfs_sb.conn_pid = 0;
-	sb->u.smbfs_sb.state = CONN_INVALID; /* no connection yet */
-	sb->u.smbfs_sb.generation = 0;
-	sb->u.smbfs_sb.packet_size = smb_round_length(SMB_INITIAL_PACKET_SIZE);
-	sb->u.smbfs_sb.packet = smb_vmalloc(sb->u.smbfs_sb.packet_size);
-	if (!sb->u.smbfs_sb.packet)
+	server->mnt = NULL;
+	server->sock_file = NULL;
+	init_MUTEX(&server->sem);
+	init_waitqueue_head(&server->wait);
+	server->conn_pid = 0;
+	server->state = CONN_INVALID; /* no connection yet */
+	server->generation = 0;
+	server->packet_size = smb_round_length(SMB_INITIAL_PACKET_SIZE);
+	server->packet = smb_vmalloc(server->packet_size);
+	if (!server->packet)
 		goto out_no_mem;
 
 	/* Allocate the global temp buffer */
-	sb->u.smbfs_sb.temp_buf = smb_kmalloc(2*SMB_MAXPATHLEN+20, GFP_KERNEL);
-	if (!sb->u.smbfs_sb.temp_buf)
+	server->temp_buf = smb_kmalloc(2*SMB_MAXPATHLEN+20, GFP_KERNEL);
+	if (!server->temp_buf)
 		goto out_no_temp;
 
 	/* Setup NLS stuff */
-	sb->u.smbfs_sb.remote_nls = NULL;
-	sb->u.smbfs_sb.local_nls = NULL;
-	sb->u.smbfs_sb.name_buf = sb->u.smbfs_sb.temp_buf + SMB_MAXPATHLEN + 20;
+	server->remote_nls = NULL;
+	server->local_nls = NULL;
+	server->name_buf = server->temp_buf + SMB_MAXPATHLEN + 20;
 
 	/* Allocate the mount data structure */
 	/* FIXME: merge this with the other malloc and get a whole page? */
 	mnt = smb_kmalloc(sizeof(struct smb_mount_data_kernel), GFP_KERNEL);
 	if (!mnt)
 		goto out_no_mount;
-	sb->u.smbfs_sb.mnt = mnt;
+	server->mnt = mnt;
 
 	memset(mnt, 0, sizeof(struct smb_mount_data_kernel));
 	strncpy(mnt->codepage.local_name, CONFIG_NLS_DEFAULT,
@@ -424,7 +489,7 @@
 	strncpy(mnt->codepage.remote_name, SMB_NLS_REMOTE,
 		SMB_NLS_MAXNAMELEN);
 
-	mnt->ttl = 1000;
+	mnt->ttl = SMB_TTL_DEFAULT;
 	if (ver == SMB_MOUNT_OLDVERSION) {
 		mnt->version = oldmnt->version;
 
@@ -433,12 +498,8 @@
 		mnt->uid = oldmnt->uid;
 		mnt->gid = oldmnt->gid;
 
-		mnt->file_mode =
-			oldmnt->file_mode & (S_IRWXU | S_IRWXG | S_IRWXO);
-		mnt->dir_mode =
-			oldmnt->dir_mode & (S_IRWXU | S_IRWXG | S_IRWXO);
-		mnt->file_mode |= S_IFREG;
-		mnt->dir_mode  |= S_IFDIR;
+		mnt->file_mode = (oldmnt->file_mode & S_IRWXUGO) | S_IFREG;
+		mnt->dir_mode = (oldmnt->dir_mode & S_IRWXUGO) | S_IFDIR;
 
 		mnt->flags = (oldmnt->file_mode >> 9);
 	} else {
@@ -447,9 +508,7 @@
 
 		mnt->mounted_uid = current->uid;
 	}
-	smb_setcodepage(&sb->u.smbfs_sb, &mnt->codepage);
-	if (!sb->u.smbfs_sb.convert)
-		PARANOIA("convert funcptr was NULL!\n");
+	smb_setcodepage(server, &mnt->codepage);
 
 	/*
 	 * Display the enabled options
@@ -463,7 +522,7 @@
 	/*
 	 * Keep the super block locked while we get the root inode.
 	 */
-	smb_init_root_dirent(&(sb->u.smbfs_sb), &root);
+	smb_init_root_dirent(server, &root);
 	root_inode = smb_iget(sb, &root);
 	if (!root_inode)
 		goto out_no_root;
@@ -478,13 +537,13 @@
 out_no_root:
 	iput(root_inode);
 out_bad_option:
-	smb_kfree(sb->u.smbfs_sb.mnt);
+	smb_kfree(server->mnt);
 out_no_mount:
-	smb_kfree(sb->u.smbfs_sb.temp_buf);
+	smb_kfree(server->temp_buf);
 out_no_temp:
-	smb_vfree(sb->u.smbfs_sb.packet);
+	smb_vfree(server->packet);
 out_no_mem:
-	if (!sb->u.smbfs_sb.mnt)
+	if (!server->mnt)
 		printk(KERN_ERR "smb_read_super: allocation failure\n");
 	goto out_fail;
 out_wrong_data:
@@ -499,11 +558,11 @@
 static int
 smb_statfs(struct super_block *sb, struct statfs *buf)
 {
-	smb_proc_dskattr(sb, buf);
+	int result = smb_proc_dskattr(sb, buf);
 
 	buf->f_type = SMB_SUPER_MAGIC;
 	buf->f_namelen = SMB_MAXPATHLEN;
-	return 0;
+	return result;
 }
 
 int
@@ -511,7 +570,7 @@
 {
 	struct inode *inode = dentry->d_inode;
 	struct smb_sb_info *server = server_from_dentry(dentry);
-	unsigned int mask = (S_IFREG | S_IFDIR | S_IRWXU | S_IRWXG | S_IRWXO);
+	unsigned int mask = (S_IFREG | S_IFDIR | S_IRWXUGO);
 	int error, changed, refresh = 0;
 	struct smb_fattr fattr;
 
@@ -532,15 +591,22 @@
 	if ((attr->ia_valid & ATTR_MODE) && (attr->ia_mode & ~mask))
 		goto out;
 
-	if ((attr->ia_valid & ATTR_SIZE) != 0)
-	{
+	if ((attr->ia_valid & ATTR_SIZE) != 0) {
 		VERBOSE("changing %s/%s, old size=%ld, new size=%ld\n",
 			DENTRY_PATH(dentry),
 			(long) inode->i_size, (long) attr->ia_size);
+
+		filemap_fdatasync(inode->i_mapping);
+		filemap_fdatawait(inode->i_mapping);
+
 		error = smb_open(dentry, O_WRONLY);
 		if (error)
 			goto out;
-		error = smb_proc_trunc(server, inode->u.smbfs_i.fileid,
+
+		if(!(server_from_dentry(dentry))->opt.capabilities & SMB_CAP_UNIX) {
+			attr->ia_valid ^= ATTR_SIZE;
+		}
+			error = smb_proc_trunc(server, inode->u.smbfs_i.fileid,
 					 attr->ia_size);
 		if (error)
 			goto out;
@@ -550,6 +616,23 @@
 		refresh = 1;
 	}
 
+
+	if((server_from_dentry(dentry))->opt.capabilities & SMB_CAP_UNIX) {
+	
+		/*if ((attr->ia_valid & ATTR_SIZE) != 0) {
+			error = vmtruncate(inode, attr->ia_size);
+			if (error)
+				goto out;
+		}*/
+
+		error = smb_proc_setattr_unix(dentry, attr);
+		if (error)
+			goto out;
+		refresh = 1;
+
+		goto out; /* this way we don't upset the indent of the original code*/
+	};
+
 	/*
 	 * Initialize the fattr and check for changed fields.
 	 * Note: CTIME under SMB is creation time rather than
@@ -558,20 +641,17 @@
 	smb_get_inode_attr(inode, &fattr);
 
 	changed = 0;
-	if ((attr->ia_valid & ATTR_MTIME) != 0)
-	{
+	if ((attr->ia_valid & ATTR_MTIME) != 0) {
 		fattr.f_mtime = attr->ia_mtime;
 		changed = 1;
 	}
-	if ((attr->ia_valid & ATTR_ATIME) != 0)
-	{
+	if ((attr->ia_valid & ATTR_ATIME) != 0) {
 		fattr.f_atime = attr->ia_atime;
 		/* Earlier protocols don't have an access time */
 		if (server->opt.protocol >= SMB_PROTOCOL_LANMAN2)
 			changed = 1;
 	}
-	if (changed)
-	{
+	if (changed) {
 		error = smb_proc_settime(dentry, &fattr);
 		if (error)
 			goto out;
@@ -582,27 +662,22 @@
 	 * Check for mode changes ... we're extremely limited in
 	 * what can be set for SMB servers: just the read-only bit.
 	 */
-	if ((attr->ia_valid & ATTR_MODE) != 0)
-	{
+	if ((attr->ia_valid & ATTR_MODE) != 0) {
 		VERBOSE("%s/%s mode change, old=%x, new=%x\n",
 			DENTRY_PATH(dentry), fattr.f_mode, attr->ia_mode);
 		changed = 0;
-		if (attr->ia_mode & S_IWUSR)
-		{
-			if (fattr.attr & aRONLY)
-			{
+		if (attr->ia_mode & S_IWUSR) {
+			if (fattr.attr & aRONLY) {
 				fattr.attr &= ~aRONLY;
 				changed = 1;
 			}
 		} else {
-			if (!(fattr.attr & aRONLY))
-			{
+			if (!(fattr.attr & aRONLY)) {
 				fattr.attr |= aRONLY;
 				changed = 1;
 			}
 		}
-		if (changed)
-		{
+		if (changed) {
 			error = smb_proc_setattr(dentry, &fattr);
 			if (error)
 				goto out;
@@ -617,6 +692,7 @@
 	return error;
 }
 
+
 #ifdef DEBUG_SMB_MALLOC
 int smb_malloced;
 int smb_current_kmalloced;
diff -ruN -X dontdiff /usr/src/linux-2.4.9-e.27/fs/smbfs/ioctl.c linux-2.4.9-e.27/fs/smbfs/ioctl.c
--- /usr/src/linux-2.4.9-e.27/fs/smbfs/ioctl.c	Wed May 16 06:40:55 2001
+++ linux-2.4.9-e.27/fs/smbfs/ioctl.c	Sun Oct 19 18:53:11 2003
@@ -19,6 +19,8 @@
 
 #include <asm/uaccess.h>
 
+#include "proto.h"
+
 int
 smb_ioctl(struct inode *inode, struct file *filp,
 	  unsigned int cmd, unsigned long arg)
@@ -37,9 +39,11 @@
 		break;
 
 	case SMB_IOC_NEWCONN:
-		/* require an argument == smb_conn_opt, else it is EINVAL */
-		if (!arg)
+		/* arg is smb_conn_opt, or NULL if no connection was made */
+		if (!arg) {
+			result = smb_wakeup(server);
 			break;
+		}
 
 		result = -EFAULT;
 		if (!copy_from_user(&opt, (void *)arg, sizeof(opt)))
diff -ruN -X dontdiff /usr/src/linux-2.4.9-e.27/fs/smbfs/proc.c linux-2.4.9-e.27/fs/smbfs/proc.c
--- /usr/src/linux-2.4.9-e.27/fs/smbfs/proc.c	Wed Aug  6 05:05:29 2003
+++ linux-2.4.9-e.27/fs/smbfs/proc.c	Sun Oct 19 18:53:12 2003
@@ -23,16 +23,18 @@
 #include <linux/smb_mount.h>
 
 #include <asm/string.h>
+#include <asm/div64.h>
 
 #include "smb_debug.h"
+#include "proto.h"
 
 
 /* Features. Undefine if they cause problems, this should perhaps be a
    config option. */
 #define SMBFS_POSIX_UNLINK 1
 
-/* Allow smb_retry to be interrupted. Not sure of the benefit ... */
-/* #define SMB_RETRY_INTR */
+/* Allow smb_retry to be interrupted. */
+#define SMB_RETRY_INTR
 
 #define SMB_VWV(packet)  ((packet) + SMB_HEADER_LEN)
 #define SMB_CMD(packet)  (*(packet+8))
@@ -43,6 +45,9 @@
 #define SMB_DIRINFO_SIZE 43
 #define SMB_STATUS_SIZE  21
 
+#define SMB_ST_BLKSIZE	(PAGE_SIZE)
+#define SMB_ST_BLKSHIFT	(PAGE_SHIFT)
+
 static int
 smb_proc_setattr_ext(struct smb_sb_info *, struct inode *,
 		     struct smb_fattr *);
@@ -53,7 +58,8 @@
 smb_proc_do_getattr(struct smb_sb_info *server, struct dentry *dir,
 		    struct smb_fattr *fattr);
 
-
+int
+smb_proc_query_cifsunix(struct smb_sb_info *server);
 
 static void
 str_upper(char *name, int len)
@@ -162,8 +168,7 @@
 	return n;
 }
 
-static int setcodepage(struct smb_sb_info *server,
-		       struct nls_table **p, char *name)
+static int setcodepage(struct nls_table **p, char *name)
 {
 	struct nls_table *nls;
 
@@ -185,16 +190,20 @@
 /* Handles all changes to codepage settings. */
 int smb_setcodepage(struct smb_sb_info *server, struct smb_nls_codepage *cp)
 {
-	int n;
+	int n = 0;
 
 	smb_lock_server(server);
 
-	n = setcodepage(server, &server->local_nls, cp->local_name);
+	/* Don't load any nls_* at all, if no remote is requested */
+	if (!*cp->remote_name)
+		goto out;
+
+	n = setcodepage(&server->local_nls, cp->local_name);
 	if (n != 0)
 		goto out;
-	n = setcodepage(server, &server->remote_nls, cp->remote_name);
+	n = setcodepage(&server->remote_nls, cp->remote_name);
 	if (n != 0)
-		setcodepage(server, &server->local_nls, NULL);
+		setcodepage(&server->local_nls, NULL);
 
 out:
 	if (server->local_nls != NULL && server->remote_nls != NULL)
@@ -213,7 +222,7 @@
 /*                                                                           */
 /*****************************************************************************/
 
-__u8 *
+static __u8 *
 smb_encode_smb_length(__u8 * p, __u32 len)
 {
 	*p = 0;
@@ -358,7 +367,9 @@
 	int month, year;
 	time_t secs;
 
-	month = ((date >> 5) & 15) - 1;
+	/* first subtract and mask after that... Otherwise, if
+	   date == 0, bad things happen */
+	month = ((date >> 5) - 1) & 15;
 	year = date >> 9;
 	secs = (time & 31) * 2 + 60 * ((time >> 5) & 63) + (time >> 11) * 3600 + 86400 *
 	    ((date & 31) - 1 + day_n[month] + (year / 4) + year * 365 - ((year & 3) == 0 &&
@@ -377,6 +388,9 @@
 	int day, year, nl_day, month;
 
 	unix_date = utc2local(server, unix_date);
+	if (unix_date < 315532800)
+		unix_date = 315532800;
+
 	*time = (unix_date % 60) / 2 +
 		(((unix_date / 60) % 60) << 5) +
 		(((unix_date / 3600) % 24) << 11);
@@ -400,57 +414,145 @@
 
 /* The following are taken from fs/ntfs/util.c */
 
+#define NTFS_TIME_OFFSET ((u64)(369*365 + 89) * 24 * 3600 * 10000000)
+
 /*
  * Convert the NT UTC (based 1601-01-01, in hundred nanosecond units)
  * into Unix UTC (based 1970-01-01, in seconds).
- *
- * This is very gross because
- * 1: We must do 64-bit division on a 32-bit machine
- * 2: We can't use libgcc for long long operations in the kernel
- * 3: Floating point math in the kernel would corrupt user data
  */
 static time_t
-smb_ntutc2unixutc(struct smb_sb_info *server, u64 ntutc)
+smb_ntutc2unixutc(u64 ntutc)
 {
-	const unsigned int D = 10000000;
-	unsigned int H = (unsigned int)(ntutc >> 32);
-	unsigned int L = (unsigned int)ntutc;
-	unsigned int numerator2;
-	unsigned int lowseconds;
-	unsigned int result;
+	/* FIXME: what about the timezone difference? */
+	/* Subtract the NTFS time offset, then convert to 1s intervals. */
+	u64 t = ntutc - NTFS_TIME_OFFSET;
+	do_div(t, 10000000);
+	return (time_t)t;
+}
 
-	/*
-	 * It is best to subtract 0x019db1ded53e8000 first.
-	 * Then the 1601-based date becomes a 1970-based date.
-	 */
-	if (L < (unsigned)0xd53e8000) H--;
-	L -= (unsigned)0xd53e8000;
-	H -= (unsigned)0x019db1de;
+/* Convert the Unix UTC into NT time */
+static u64
+smb_unixutc2ntutc(time_t t)
+{
+	/* Note: timezone conversion is probably wrong. */
+	return ((u64)t) * 10000000 + NTFS_TIME_OFFSET;
+}
 
-	/*
-	 * Now divide 64-bit numbers on a 32-bit machine :-)
-	 * With the subtraction already done, the result fits in 32 bits.
-	 * The numerator fits in 56 bits and the denominator fits
-	 * in 24 bits, so we can shift by 8 bits to make this work.
-	 */
+/*
+ * This function will create the flags for the mode of the file
+ * It should do security checks on what kind of files it will allow
+ *
+ * TODO What type of file has a 0 S_IFMT ??? perhaps we could use that...
+ */
+static int smb_filetype_to_mode(u32 filetype)
+{
+	switch(filetype) {
+		case UNIX_TYPE_FILE:
+			return S_IFREG;
+
+		case UNIX_TYPE_DIR:
+			return S_IFDIR;
+
+		case UNIX_TYPE_SYMLINK:
+			return S_IFLNK;
+
+		case UNIX_TYPE_CHARDEV:
+			return S_IFCHR;
+
+		case UNIX_TYPE_BLKDEV:
+			return S_IFBLK;
+
+		case UNIX_TYPE_FIFO:
+			return S_IFIFO;
+
+		case UNIX_TYPE_SOCKET:
+			return S_IFSOCK;
+
+		case UNIX_TYPE_UNKNOWN:
+		default:
+			DEBUG1("unknown type %d\n", filetype);
+			return S_IFREG;
+	}
+}
 
-	numerator2  = (H<<8) | (L>>24);
-	result      = (numerator2 / D);   /* shifted 24 right!! */
-	lowseconds  = result << 24;
+static u32 smb_filetype_from_mode(int mode)
+{
+	if(mode & S_IFREG)
+		return UNIX_TYPE_FILE;
+
+	if(mode & S_IFDIR)
+		return UNIX_TYPE_DIR;
+
+	if(mode & S_IFLNK)
+		return UNIX_TYPE_SYMLINK;
 
-	numerator2  = ((numerator2-result*D)<<8) | ((L>>16)&0xff);
-	result      = (numerator2 / D);   /* shifted 16 right!! */
-	lowseconds |= result << 16;
+	if(mode & S_IFCHR)
+		return UNIX_TYPE_CHARDEV;
 
-	numerator2  = ((numerator2-result*D)<<8) | ((L>>8)&0xff);
-	result      = (numerator2 / D);   /* shifted 8 right!! */
-	lowseconds |= result << 8;
+	if(mode & S_IFBLK)
+		return UNIX_TYPE_BLKDEV;
 
-	numerator2  = ((numerator2-result*D)<<8) | (L&0xff);
-	result      = (numerator2 / D);   /* not shifted */
-	lowseconds |= result;
+	if(mode & S_IFIFO)
+		return UNIX_TYPE_FIFO;
 
-	return lowseconds;
+	if(mode & S_IFSOCK)
+		return UNIX_TYPE_SOCKET;
+
+	return UNIX_TYPE_UNKNOWN;
+}
+
+/*
+ * This function will decode the 'standard' permissions into Linux
+ * permissions.  It should do security checks to make sure that we
+ * don't do silly things like make devices owned by users.
+ *
+ * TODO To do this it needs more info.
+ */
+static int smb_permissions_to_mode(u64 permissions)
+{
+	int mode = 0;
+
+	if(permissions & UNIX_X_OTH) mode |= S_IXOTH;
+	if(permissions & UNIX_R_OTH) mode |= S_IROTH;
+	if(permissions & UNIX_W_OTH) mode |= S_IWOTH;
+
+	if(permissions & UNIX_X_GRP) mode |= S_IXGRP;
+	if(permissions & UNIX_R_GRP) mode |= S_IRGRP;
+	if(permissions & UNIX_W_GRP) mode |= S_IWGRP;
+
+	if(permissions & UNIX_X_USR) mode |= S_IXUSR;
+	if(permissions & UNIX_R_USR) mode |= S_IRUSR;
+	if(permissions & UNIX_W_USR) mode |= S_IWUSR;
+
+	/* these could pose security issues.... */
+	if(permissions & UNIX_STICKY) mode |= S_ISVTX;
+	if(permissions & UNIX_SET_GID) mode |= S_ISGID;
+	if(permissions & UNIX_SET_UID) mode |= S_ISUID;
+
+	return mode;
+}
+
+static u64 smb_permissions_from_mode(int mode)
+{
+	u64 perm = 0;
+
+	if(mode & S_IXOTH) perm |= UNIX_X_OTH;
+	if(mode & S_IROTH) perm |= UNIX_R_OTH;
+	if(mode & S_IWOTH) perm |= UNIX_W_OTH;
+
+	if(mode & S_IXGRP) perm |= UNIX_X_GRP;
+	if(mode & S_IRGRP) perm |= UNIX_R_GRP;
+	if(mode & S_IWGRP) perm |= UNIX_W_GRP;
+
+	if(mode & S_IXUSR) perm |= UNIX_X_USR;
+	if(mode & S_IRUSR) perm |= UNIX_R_USR;
+	if(mode & S_IWUSR) perm |= UNIX_W_USR;
+
+	if(mode & S_ISVTX) perm |= UNIX_STICKY;
+	if(mode & S_ISUID) perm |= UNIX_SET_UID;
+	if(mode & S_ISGID) perm |= UNIX_SET_GID;
+	
+	return perm;
 }
 
 #if 0
@@ -459,7 +561,7 @@
 smb_unixutc2ntutc(struct smb_sb_info *server, time_t t)
 {
 	/* Note: timezone conversion is probably wrong. */
-	return ((utc2local(server, t) + (u64)(369*365+89)*24*3600) * 10000000);
+	return ((u64)utc2local(server, t)) * 10000000 + NTFS_TIME_OFFSET;
 }
 #endif
 
@@ -569,6 +671,9 @@
 	return size;
 }
 
+/*
+ * Convert SMB error codes to -E... errno values.
+ */
 int
 smb_errno(struct smb_sb_info *server)
 {
@@ -579,110 +684,117 @@
 	VERBOSE("errcls %d  code %d  from command 0x%x\n",
 		errcls, error, SMB_CMD(server->packet));
 
-	if (errcls == ERRDOS)
-		switch (error)
-		{
+	if (errcls == ERRDOS) {
+		switch (error) {
 		case ERRbadfunc:
-			return EINVAL;
+			return -EINVAL;
 		case ERRbadfile:
 		case ERRbadpath:
-			return ENOENT;
+			return -ENOENT;
 		case ERRnofids:
-			return EMFILE;
+			return -EMFILE;
 		case ERRnoaccess:
-			return EACCES;
+			return -EACCES;
 		case ERRbadfid:
-			return EBADF;
+			return -EBADF;
 		case ERRbadmcb:
-			return EREMOTEIO;
+			return -EREMOTEIO;
 		case ERRnomem:
-			return ENOMEM;
+			return -ENOMEM;
 		case ERRbadmem:
-			return EFAULT;
+			return -EFAULT;
 		case ERRbadenv:
 		case ERRbadformat:
-			return EREMOTEIO;
+			return -EREMOTEIO;
 		case ERRbadaccess:
-			return EACCES;
+			return -EACCES;
 		case ERRbaddata:
-			return E2BIG;
+			return -E2BIG;
 		case ERRbaddrive:
-			return ENXIO;
+			return -ENXIO;
 		case ERRremcd:
-			return EREMOTEIO;
+			return -EREMOTEIO;
 		case ERRdiffdevice:
-			return EXDEV;
-		case ERRnofiles:	/* Why is this mapped to 0?? */
-			return 0;
+			return -EXDEV;
+		case ERRnofiles:
+			return -ENOENT;
 		case ERRbadshare:
-			return ETXTBSY;
+			return -ETXTBSY;
 		case ERRlock:
-			return EDEADLK;
+			return -EDEADLK;
 		case ERRfilexists:
-			return EEXIST;
-		case 87:		/* should this map to 0?? */
-			return 0;	/* Unknown error!! */
-		case 123:		/* Invalid name?? e.g. .tmp* */
-			return ENOENT;
-		case 145:		/* Win NT 4.0: non-empty directory? */
-			return ENOTEMPTY;
-			/* This next error seems to occur on an mv when
-			 * the destination exists */
-		case 183:
-			return EEXIST;
+			return -EEXIST;
+		case ERROR_INVALID_PARAMETER:
+			return -EINVAL;
+		case ERROR_DISK_FULL:
+			return -ENOSPC;
+		case ERROR_INVALID_NAME:
+			return -ENOENT;
+		case ERROR_DIR_NOT_EMPTY:
+			return -ENOTEMPTY;
+		case ERROR_NOT_LOCKED:
+                       return -ENOLCK;
+		case ERROR_ALREADY_EXISTS:
+			return -EEXIST;
 		default:
 			class = "ERRDOS";
 			goto err_unknown;
-	} else if (errcls == ERRSRV)
-		switch (error)
-		{
+		}
+	} else if (errcls == ERRSRV) {
+		switch (error) {
 		/* N.B. This is wrong ... EIO ? */
 		case ERRerror:
-			return ENFILE;
+			return -ENFILE;
 		case ERRbadpw:
-			return EINVAL;
+			return -EINVAL;
 		case ERRbadtype:
-			return EIO;
+			return -EIO;
 		case ERRaccess:
-			return EACCES;
+			return -EACCES;
 		/*
 		 * This is a fatal error, as it means the "tree ID"
 		 * for this connection is no longer valid. We map
 		 * to a special error code and get a new connection.
 		 */
 		case ERRinvnid:
-			return EBADSLT;
+			return -EBADSLT;
 		default:
 			class = "ERRSRV";
 			goto err_unknown;
-	} else if (errcls == ERRHRD)
-		switch (error)
-		{
+		}
+	} else if (errcls == ERRHRD) {
+		switch (error) {
 		case ERRnowrite:
-			return EROFS;
+			return -EROFS;
 		case ERRbadunit:
-			return ENODEV;
+			return -ENODEV;
 		case ERRnotready:
-			return EUCLEAN;
+			return -EUCLEAN;
 		case ERRbadcmd:
 		case ERRdata:
-			return EIO;
+			return -EIO;
 		case ERRbadreq:
-			return ERANGE;
+			return -ERANGE;
 		case ERRbadshare:
-			return ETXTBSY;
+			return -ETXTBSY;
 		case ERRlock:
-			return EDEADLK;
+			return -EDEADLK;
+		case ERRdiskfull:
+			return -ENOSPC;
 		default:
 			class = "ERRHRD";
 			goto err_unknown;
-	} else if (errcls == ERRCMD)
+		}
+	} else if (errcls == ERRCMD) {
 		class = "ERRCMD";
+	} else if (errcls == SUCCESS) {
+		return 0;	/* This is the only valid 0 return */
+	}
 
 err_unknown:
 	printk(KERN_ERR "smb_errno: class %s, code %d from command 0x%x\n",
 	       class, error, SMB_CMD(server->packet));
-	return EIO;
+	return -EIO;
 }
 
 /*
@@ -799,12 +911,10 @@
 	}
 
 	/*
-	 * Check for server errors.  The current smb_errno() routine
-	 * is squashing some error codes, but I don't think this is
-	 * correct: after a server error the packet won't be valid.
+	 * Check for server errors.
 	 */
 	if (s->rcls != 0) {
-		result = -smb_errno(s);
+		result = smb_errno(s);
 		if (!result)
 			printk(KERN_DEBUG "smb_request_ok: rcls=%d, err=%d mapped to 0\n",
 				s->rcls, s->err);
@@ -898,19 +1008,31 @@
 		}
 	}
 
+	if(server->opt.capabilities & SMB_CAP_UNIX)
+	{
+		DEBUG1("using UNIX_CIFS extensions\n");
+		smb_proc_query_cifsunix(server);
+	}
+
 out:
 	smb_unlock_server(server);
+	smb_wakeup(server);
+	return error;
 
+out_putf:
+	fput(filp);
+	goto out;
+}
+
+int
+smb_wakeup(struct smb_sb_info *server)
+{
 #ifdef SMB_RETRY_INTR
 	wake_up_interruptible(&server->wait);
 #else
 	wake_up(&server->wait);
 #endif
-	return error;
-
-out_putf:
-	fput(filp);
-	goto out;
+	return 0;
 }
 
 /* smb_setup_header: We completely set up the packet. You only have to
@@ -1152,12 +1274,19 @@
 		 * If the file is open with write permissions,
 		 * update the time stamps to sync mtime and atime.
 		 */
-		if ((server->opt.protocol >= SMB_PROTOCOL_LANMAN2) &&
-		    !(ino->u.smbfs_i.access == SMB_O_RDONLY))
-		{
-			struct smb_fattr fattr;
-			smb_get_inode_attr(ino, &fattr);
-			smb_proc_setattr_ext(server, ino, &fattr);
+		if(server->opt.capabilities & SMB_CAP_UNIX) {
+			/*
+			 * There is nothing to do here?
+			 */
+		}
+		else {
+			if ((server->opt.protocol >= SMB_PROTOCOL_LANMAN2) &&
+			    !(ino->u.smbfs_i.access == SMB_O_RDONLY))
+			{
+				struct smb_fattr fattr;
+				smb_get_inode_attr(ino, &fattr);
+				smb_proc_setattr_ext(server, ino, &fattr);
+			}
 		}
 
 		result = smb_proc_close(server, ino->u.smbfs_i.fileid,
@@ -1404,6 +1533,16 @@
 	int result;
 	struct smb_fattr fattr;
 
+	if(server->opt.capabilities & SMB_CAP_UNIX)
+	{
+		/*
+		 * Perhaps we should call setattr_unix?
+		 * The correct solution it to configure the server to have
+		 * "delete readonly = Yes"
+		 */
+		PARANOIA("We should not be here because we are UNIX\n");
+	}
+
 	/* first get current attribute */
 	result = smb_proc_do_getattr(server, dentry, &fattr);
 	if (result < 0)
@@ -1524,27 +1663,37 @@
 	fattr->f_nlink = 1;
 	fattr->f_uid = server->mnt->uid;
 	fattr->f_gid = server->mnt->gid;
-	fattr->f_blksize = 512;
+	fattr->f_blksize = SMB_ST_BLKSIZE;
+	fattr->f_unix = 0;
 }
 
 static void
 smb_finish_dirent(struct smb_sb_info *server, struct smb_fattr *fattr)
 {
-	fattr->f_mode = server->mnt->file_mode;
-	if (fattr->attr & aDIR)
-	{
-		fattr->f_mode = server->mnt->dir_mode;
-		fattr->f_size = 512;
-	}
-	/* Check the read-only flag */
-	if (fattr->attr & aRONLY)
-		fattr->f_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
-
-	fattr->f_blocks = 0;
-	if ((fattr->f_blksize != 0) && (fattr->f_size != 0))
-	{
-		fattr->f_blocks =
-		    (fattr->f_size - 1) / fattr->f_blksize + 1;
+	if(fattr->f_unix) {
+		/* mask out suid and sgid */
+		fattr->f_mode &= ~(S_ISGID | S_ISUID);
+
+		/* convert nodes into regular files...*/
+		/*if(S_ISCHR(fattr->f_mode) | S_ISBLK(fattr->f_mode)) {
+			fattr->f_mode &= ~S_IFMT;
+			fattr->f_mode |= S_IFREG;
+		}*/
+	} else {
+		fattr->f_mode = server->mnt->file_mode;
+		if (fattr->attr & aDIR)
+		{
+			fattr->f_mode = server->mnt->dir_mode;
+			fattr->f_size = SMB_ST_BLKSIZE;
+		}
+		/* Check the read-only flag */
+		if (fattr->attr & aRONLY)
+			fattr->f_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
+
+		/* How many 512 byte blocks do we need for this file? */
+		fattr->f_blocks = 0;
+		if (fattr->f_size != 0)
+			fattr->f_blocks = 1 + ((fattr->f_size-1) >> 9);
 	}
 	return;
 }
@@ -1557,6 +1706,7 @@
 	fattr->f_ino = 2; /* traditional root inode number */
 	fattr->f_mtime = CURRENT_TIME;
 	smb_finish_dirent(server, fattr);
+	DEBUG1("root inode mode = %07o\n", fattr->f_mode);
 }
 
 /*
@@ -1574,6 +1724,11 @@
 {
 	int len;
 
+	if(server->opt.capabilities & SMB_CAP_UNIX)
+	{
+		PARANOIA("We should not be here because we are UNIX\n");
+	}
+
 	/*
 	 * SMB doesn't have a concept of inode numbers ...
 	 */
@@ -1649,6 +1804,11 @@
 	static struct qstr mask = { "*.*", 3, 0 };
 	unsigned char *last_status;
 
+	if(server->opt.capabilities & SMB_CAP_UNIX)
+	{
+		PARANOIA("We should not be here because we are UNIX\n");
+	}
+
 	VERBOSE("%s/%s\n", DENTRY_PATH(dir));
 
 	smb_lock_server(server);
@@ -1759,6 +1919,48 @@
 	return result;
 }
 
+void smb_decode_unix_basic(struct smb_fattr *fattr, char *p)
+{
+	__u64 devmajor, devminor;
+
+	fattr->f_unix = 1;
+	fattr->f_mode = 0;
+	/* 0 L file size in bytes */
+	fattr->f_size = LVAL(p, 0);
+		
+	/* 8 L file size on disk in bytes (block count) */
+	fattr->f_blocks = LVAL(p, 8);
+
+	/* times. */
+	fattr->f_ctime = smb_ntutc2unixutc(LVAL(p, 16));
+	fattr->f_atime = smb_ntutc2unixutc(LVAL(p, 24));
+	fattr->f_mtime = smb_ntutc2unixutc(LVAL(p, 32));
+
+	/* 40 L uid */
+	fattr->f_uid = LVAL(p, 40); 
+	/* 48 L gid */
+	fattr->f_gid = LVAL(p, 48); 
+		
+	/* 56 W file type enum */
+	fattr->f_mode |= smb_filetype_to_mode(WVAL(p, 56));
+
+	if(S_ISBLK(fattr->f_mode) || S_ISCHR(fattr->f_mode))
+	{
+		/* 60 L devmajor */
+		devmajor = LVAL(p, 60);
+		/* 68 L devminor */
+		devminor = LVAL(p, 68);
+
+		fattr->f_rdev = ((devmajor & 0xFF) << 8) | (devminor & 0xFF);
+	}
+	/* 76 L unique ID (inode) */
+	/* 84 L permissions */
+	fattr->f_mode |= smb_permissions_to_mode(LVAL(p, 84));
+
+	/* 92 L link count */
+
+}
+
 /*
  * Interpret a long filename structure using the specified info level:
  *   level 1 for anything below NT1 protocol
@@ -1818,9 +2020,9 @@
 		if (len && qname->name[len-1] == '\0')
 			len--;
 
-		fattr->f_ctime = smb_ntutc2unixutc(server, LVAL(p, 8));
-		fattr->f_atime = smb_ntutc2unixutc(server, LVAL(p, 16));
-		fattr->f_mtime = smb_ntutc2unixutc(server, LVAL(p, 24));
+		fattr->f_ctime = smb_ntutc2unixutc(LVAL(p, 8));
+		fattr->f_atime = smb_ntutc2unixutc(LVAL(p, 16));
+		fattr->f_mtime = smb_ntutc2unixutc(LVAL(p, 24));
 		/* change time (32) */
 		fattr->f_size = DVAL(p, 40);
 		/* alloc size (48) */
@@ -1829,9 +2031,25 @@
 		VERBOSE("info 260 at %p, len=%d, name=%.*s\n",
 			p, len, len, qname->name);
 		break;
+
+	case SMB_FIND_FILE_UNIX:
+		result = p + WVAL(p, 0);
+		qname->name = p + 108;
+
+		len = strlen(qname->name);
+		/* TODO should we check the length?? */
+
+		p += 8;
+		smb_decode_unix_basic(fattr, p);
+		VERBOSE("info decoded FILE_UNIX at %p, len=%d, name=%.*s\n",
+			p, len, len, qname->name);
+		break;
+	
 	default:
 		PARANOIA("Unknown info level %d\n", level);
 		result = p + WVAL(p, 0);
+
+		/* TODO debug this.  I got an oops */
 		goto out;
 	}
 
@@ -1918,6 +2136,10 @@
 	if (server->opt.protocol < SMB_PROTOCOL_NT1)
 		info_level = 1;
 
+	if(server->opt.capabilities & SMB_CAP_UNIX) {
+		info_level = SMB_FILE_FILE_UNIX;
+	}
+
 	smb_lock_server(server);
 
 	/*
@@ -1992,7 +2214,7 @@
                 }
 
 		if (server->rcls != 0) {
-			result = -smb_errno(server);
+			result = smb_errno(server);
 			PARANOIA("name=%s, result=%d, rcls=%d, err=%d\n",
 				 mask, result, server->rcls, server->err);
 			break;
@@ -2040,6 +2262,9 @@
 				if (ff_lastname + 1 + mask_len > resp_data_len)
 					mask_len = resp_data_len - ff_lastname - 1;
 				break;
+			case SMB_FIND_FILE_UNIX:
+				mask_len = resp_data_len - ff_lastname;
+				break;
 			}
 
 			/*
@@ -2084,7 +2309,6 @@
 				if (qname.name[1] == '.' && qname.len == 2)
 					continue;
 			}
-
 			if (!smb_fill_cache(filp, dirent, filldir, ctl, 
 					    &qname, &fattr))
 				;	/* stop reading? */
@@ -2133,6 +2357,11 @@
 	int resp_param_len = 0;
 	int mask_len, result;
 
+	if(server->opt.capabilities & SMB_CAP_UNIX)
+	{
+		PARANOIA("We should not be here because we are UNIX\n");
+	}
+
 retry:
 	mask_len = smb_encode_path(server, mask, SMB_MAXPATHLEN+1, dentry, NULL);
 	if (mask_len < 0) {
@@ -2158,7 +2387,7 @@
 	}
 	if (server->rcls != 0)
 	{ 
-		result = -smb_errno(server);
+		result = smb_errno(server);
 #ifdef SMBFS_PARANOIA
 		if (result != -ENOENT)
 			PARANOIA("error for %s, rcls=%d, err=%d\n",
@@ -2210,6 +2439,11 @@
 	int result;
 	char *p;
 
+	if(server->opt.capabilities & SMB_CAP_UNIX)
+	{
+		PARANOIA("We should not be here because we are UNIX\n");
+	}
+
       retry:
 	p = smb_setup_header(server, SMBgetatr, 0, 0);
 	result = smb_simple_encode_path(server, &p, dir, NULL);
@@ -2256,9 +2490,14 @@
 	int resp_data_len = 0;
 	int resp_param_len = 0;
 	int result;
+	int level = 1; /* Info level SMB_INFO_STANDARD */
 
+	if(server->opt.capabilities & SMB_CAP_UNIX) {
+		level = SMB_QUERY_FILE_UNIX_BASIC;
+	}
       retry:
-	WSET(param, 0, 1);	/* Info level SMB_INFO_STANDARD */
+	
+	WSET(param, 0, level);
 	DSET(param, 2, 0);
 	result = smb_encode_path(server, param+6, SMB_MAXPATHLEN+1, dir, NULL);
 	if (result < 0)
@@ -2279,44 +2518,53 @@
 	{
 		VERBOSE("for %s: result=%d, rcls=%d, err=%d\n",
 			&param[6], result, server->rcls, server->err);
-		result = -smb_errno(server);
+		result = smb_errno(server);
 		goto out;
 	}
 	result = -ENOENT;
-	if (resp_data_len < 22)
-	{
-		PARANOIA("not enough data for %s, len=%d\n",
-			 &param[6], resp_data_len);
-		goto out;
-	}
+	if(level == 1) {
+		if (resp_data_len < 22) {
+			PARANOIA("not enough data for %s, len=%d\n",
+				 &param[6], resp_data_len);
+			goto out;
+		}
 
-	/*
-	 * Kludge alert: Win 95 swaps the date and time field,
-	 * contrary to the CIFS docs and Win NT practice.
-	 */
-	if (server->mnt->flags & SMB_MOUNT_WIN95) {
-		off_date = 2;
-		off_time = 0;
-	}
-	date = WVAL(resp_data, off_date);
-	time = WVAL(resp_data, off_time);
-	attr->f_ctime = date_dos2unix(server, date, time);
-
-	date = WVAL(resp_data, 4 + off_date);
-	time = WVAL(resp_data, 4 + off_time);
-	attr->f_atime = date_dos2unix(server, date, time);
-
-	date = WVAL(resp_data, 8 + off_date);
-	time = WVAL(resp_data, 8 + off_time);
-	attr->f_mtime = date_dos2unix(server, date, time);
+		/*
+		 * Kludge alert: Win 95 swaps the date and time field,
+		 * contrary to the CIFS docs and Win NT practice.
+		 */
+		if (server->mnt->flags & SMB_MOUNT_WIN95) {
+			off_date = 2;
+			off_time = 0;
+		}
+		date = WVAL(resp_data, off_date);
+		time = WVAL(resp_data, off_time);
+		attr->f_ctime = date_dos2unix(server, date, time);
+
+		date = WVAL(resp_data, 4 + off_date);
+		time = WVAL(resp_data, 4 + off_time);
+		attr->f_atime = date_dos2unix(server, date, time);
+
+		date = WVAL(resp_data, 8 + off_date);
+		time = WVAL(resp_data, 8 + off_time);
+		attr->f_mtime = date_dos2unix(server, date, time);
 #ifdef SMBFS_DEBUG_TIMESTAMP
-	printk(KERN_DEBUG "getattr_trans2: %s/%s, date=%x, time=%x, mtime=%ld\n",
-	       DENTRY_PATH(dir), date, time, attr->f_mtime);
+		printk(KERN_DEBUG "getattr_trans2: %s/%s, date=%x, time=%x, mtime=%ld\n",
+		       DENTRY_PATH(dir), date, time, attr->f_mtime);
 #endif
-	attr->f_size = DVAL(resp_data, 12);
-	attr->attr = WVAL(resp_data, 20);
-	result = 0;
+		attr->f_size = DVAL(resp_data, 12);
+		attr->attr = WVAL(resp_data, 20);
+		result = 0;
+
+	} else if(level == SMB_QUERY_FILE_UNIX_BASIC) {
+		smb_decode_unix_basic(attr, resp_data);
+		result = 0;
+
 
+	} else {
+		PARANOIA("unknown info level\n");
+		result = -ENOENT;
+	}
 out:
 	return result;
 }
@@ -2398,6 +2646,11 @@
 	char *p;
 	int result;
 
+	if(server->opt.capabilities & SMB_CAP_UNIX)
+	{
+		PARANOIA("We should not be here because we are UNIX\n");
+	}
+
       retry:
 	p = smb_setup_header(server, SMBsetatr, 8, 0);
 	WSET(server->packet, smb_vwv0, attr);
@@ -2433,6 +2686,7 @@
  * Because of bugs in the trans2 setattr messages, we must set
  * attributes and timestamps separately. The core SMBsetatr
  * message seems to be the only reliable way to set attributes.
+ *
  */
 int
 smb_proc_setattr(struct dentry *dir, struct smb_fattr *fattr)
@@ -2440,6 +2694,11 @@
 	struct smb_sb_info *server = server_from_dentry(dir);
 	int result;
 
+	if(server->opt.capabilities & SMB_CAP_UNIX)
+	{
+		PARANOIA("We should not be here because we are UNIX\n");
+	}
+
 	VERBOSE("setting %s/%s, open=%d\n", 
 		DENTRY_PATH(dir), smb_is_open(dir->d_inode));
 	smb_lock_server(server);
@@ -2459,6 +2718,11 @@
 	__u16 date, time;
 	int result;
 
+	if(server->opt.capabilities & SMB_CAP_UNIX)
+	{
+		PARANOIA("We should not be here because we are UNIX\n");
+	}
+
       retry:
 	smb_setup_header(server, SMBsetattrE, 7, 0);
 	WSET(server->packet, smb_vwv0, inode->u.smbfs_i.fileid);
@@ -2507,6 +2771,11 @@
 	int result;
 	char data[26];
 
+	if(server->opt.capabilities & SMB_CAP_UNIX)
+	{
+		PARANOIA("We should not be here because we are UNIX\n");
+	}
+
       retry:
 	WSET(param, 0, 1);	/* Info level SMB_INFO_STANDARD */
 	DSET(param, 2, 0);
@@ -2544,12 +2813,150 @@
 	}
 	result = 0;
 	if (server->rcls != 0)
-		result = -smb_errno(server);
+		result = smb_errno(server);
+
+out:
+	return result;
+}
+
+/*
+ * TODO Note: we must lock the server ourself
+ * ATTR_MODE      0x001
+ * ATTR_UID       0x002
+ * ATTR_GID       0x004
+ * ATTR_SIZE      0x008
+ * ATTR_ATIME     0x010
+ * ATTR_MTIME     0x020
+ * ATTR_CTIME     0x040
+ * ATTR_ATIME_SET 0x080
+ * ATTR_MTIME_SET 0x100
+ * ATTR_FORCE     0x200	
+ * ATTR_ATTR_FLAG 0x400
+ */
+
+extern int
+smb_proc_setattr_unix(struct dentry *dentry, struct iattr *attr)
+{
+	u64 nttime;
+	char *p, *param;
+	unsigned char *resp_data = NULL;
+	unsigned char *resp_param = NULL;
+	int resp_data_len = 0;
+	int resp_param_len = 0;
+	int result;
+	char data[100];
+	struct smb_sb_info *server;
+	
+	server = server_from_dentry(dentry);
+	smb_lock_server(server);
+	param = server->temp_buf;
+
+	DEBUG1("valid flags = 0x%04x\n", attr->ia_valid);
+
+      retry:
+	WSET(param, 0, SMB_SET_FILE_UNIX_BASIC);
+	DSET(param, 2, 0);
+	result = smb_encode_path(server, param + 6, SMB_MAXNAMELEN+1, dentry, NULL);
+	if (result < 0)
+		goto out;
+	p = param + 6 + result;
+
+	if(attr->ia_valid & ATTR_SIZE) {
+		/*DEBUG1("setting size = %d\n", attr->ia_size);*/
+		/* 0 L file size in bytes */
+		LSET(data, 0, attr->ia_size);
+		/* 8 L file size on disk in bytes (block count) */
+		LSET(data, 8, 0); /* can't set anyway */
+	} else {
+		LSET(data, 0, SMB_SIZE_NO_CHANGE);
+		LSET(data, 8, SMB_SIZE_NO_CHANGE); 
+	}
+		
+	
+	/* times.  TODO check the conversion function it the correct one
+	 */
+	/* we can't set ctime but we might as well pass this to the server
+	 * and let it ignore it
+	 */
+	if(attr->ia_valid & ATTR_CTIME) {
+		VERBOSE("setting ctime = %d\n", attr->ia_ctime);
+		nttime = smb_unixutc2ntutc(attr->ia_ctime);
+		LSET(data, 16, nttime);
+	} else {
+		LSET(data, 16, SMB_TIME_NO_CHANGE);
+	}
+
+	if(attr->ia_valid & ATTR_ATIME) {
+		VERBOSE("setting atime = %d\n", attr->ia_atime);
+		nttime = smb_unixutc2ntutc(attr->ia_atime);
+		LSET(data, 24, nttime);
+	} else {
+		LSET(data, 24, SMB_TIME_NO_CHANGE);
+	}
+
+	if(attr->ia_valid & ATTR_MTIME) {
+		VERBOSE("setting mtime = %d\n", attr->ia_mtime);
+		nttime = smb_unixutc2ntutc(attr->ia_mtime);
+		LSET(data, 32, nttime);
+	} else {
+		LSET(data, 32, SMB_TIME_NO_CHANGE);
+	}
+	
+	if(attr->ia_valid & ATTR_UID) {
+		/* 40 L uid */
+		LSET(data, 40, attr->ia_uid);
+	} else {
+		LSET(data, 40, SMB_UID_NO_CHANGE);
+	}
+
+	if(attr->ia_valid & ATTR_GID) {
+		/* 48 L gid */
+		LSET(data, 48, attr->ia_gid); 
+	} else {
+		LSET(data, 48, SMB_GID_NO_CHANGE); 
+	}
+		
+	/* 56 W file type enum */
+	LSET(data, 56, smb_filetype_from_mode(attr->ia_mode));
+
+	/* 60 L devmajor */
+	LSET(data, 60, 0);
+	/* 68 L devminor */
+	LSET(data, 68, 0);
+	/* 76 L unique ID (inode) */
+	LSET(data, 76, 0);
+
+	if(attr->ia_valid & ATTR_MODE) {
+		DEBUG1("setting mode = %07o\n", attr->ia_mode);
+		/* 84 L permissions */
+		LSET(data, 84, smb_permissions_from_mode(attr->ia_mode));
+	} else {
+		LSET(data, 84, SMB_MODE_NO_CHANGE);
+	}
+
+	/* 92 L link count */
+	LSET(data, 92, 0);
+
+	result = smb_trans2_request(server, TRANSACT2_SETPATHINFO,
+				    sizeof(data), data, p - param, param,
+				    &resp_data_len, &resp_data,
+				    &resp_param_len, &resp_param);
+	if (result < 0)
+	{
+		if (smb_retry(server))
+			goto retry;
+		goto out;
+	}
+	result = 0;
+	if (server->rcls != 0)
+		result = smb_errno(server);
 
 out:
+	smb_unlock_server(server);
 	return result;
 }
 
+
 /*
  * Set the modify and access timestamps for a file.
  *
@@ -2570,6 +2977,11 @@
 	struct inode *inode = dentry->d_inode;
 	int result;
 
+	if(server->opt.capabilities & SMB_CAP_UNIX)
+	{
+		PARANOIA("We should not be here because we are UNIX\n");
+	}
+
 	VERBOSE("setting %s/%s, open=%d\n",
 		DENTRY_PATH(dentry), smb_is_open(inode));
 
@@ -2613,6 +3025,7 @@
 	struct smb_sb_info *server = &(sb->u.smbfs_sb);
 	int result;
 	char *p;
+	long unit;
 
 	smb_lock_server(server);
 
@@ -2625,9 +3038,10 @@
 		goto out;
 	}
 	p = SMB_VWV(server->packet);
-	attr->f_blocks = WVAL(p, 0);
-	attr->f_bsize  = WVAL(p, 2) * WVAL(p, 4);
-	attr->f_bavail = attr->f_bfree = WVAL(p, 6);
+	unit = (WVAL(p, 2) * WVAL(p, 4)) >> SMB_ST_BLKSHIFT;
+	attr->f_blocks = WVAL(p, 0) * unit;
+	attr->f_bsize  = SMB_ST_BLKSIZE;
+	attr->f_bavail = attr->f_bfree = WVAL(p, 6) * unit;
 	result = 0;
 
 out:
@@ -2636,12 +3050,245 @@
 }
 
 int
-smb_proc_disconnect(struct smb_sb_info *server)
+smb_proc_read_link(struct smb_sb_info *server, struct dentry *dentry, char *buffer, int len)
 {
+	char *p, *param = server->temp_buf;
+	unsigned char *resp_data = NULL;
+	unsigned char *resp_param = NULL;
+	int resp_data_len = 0;
+	int resp_param_len = 0;
 	int result;
+	int level;
+	
+	if(!(server->opt.capabilities & SMB_CAP_UNIX))
+	{
+		PARANOIA("We should not be here because we are not UNIX\n");
+	}
+
+	DEBUG1("readlink of %s/%s\n", DENTRY_PATH(dentry));
+
 	smb_lock_server(server);
-	smb_setup_header(server, SMBtdis, 0, 0);
-	result = smb_request_ok(server, SMBtdis, 0, 0);
+	level = SMB_QUERY_FILE_UNIX_LINK;
+  retry:
+	
+	WSET(param, 0, level);
+	DSET(param, 2, 0);
+	result = smb_encode_path(server, param + 6, SMB_MAXNAMELEN+1, dentry, NULL);
+	if (result < 0)
+		goto out;
+	p = param + 6 + result;
+
+	result = smb_trans2_request(server, TRANSACT2_QPATHINFO,
+				    0, NULL, p - param, param,
+				    &resp_data_len, &resp_data,
+				    &resp_param_len, &resp_param);
+	DEBUG1("for %s: result=%d, rcls=%d, err=%d\n",
+		&param[6], result, server->rcls, server->err);
+	if (result < 0) {
+		if (smb_retry(server))
+			goto retry;
+		goto out;
+	}
+	if (server->rcls != 0) {
+		VERBOSE("for %s: result=%d, rcls=%d, err=%d\n",
+			&param[6], result, server->rcls, server->err);
+		result = smb_errno(server);
+		goto out;
+	}
+
+	/* copy data up to the \0 or buffer length */
+	result = 0;
+	while(result < len && resp_data[result]) {
+		buffer[result] = resp_data[result];
+		result++;
+	}
+	buffer[result] = 0;
+
+out:
 	smb_unlock_server(server);
 	return result;
 }
+
+
+/*
+ * Create a symlink object called dentry which points to oldpath.
+ *
+ * We need to lock server
+ *
+ * samba does not permit dangling links but returns a suitable error message
+ *
+ */
+extern int smb_proc_symlink(struct smb_sb_info *server, struct dentry *dentry,const char *oldpath)
+{
+	char *p, *param = server->temp_buf;
+	unsigned char *resp_data = NULL;
+	unsigned char *resp_param = NULL;
+	int resp_data_len = 0;
+	int resp_param_len = 0;
+	int result;
+	int level = SMB_SET_FILE_UNIX_LINK;
+	int oldpathlen = 0;
+
+	if(!(server->opt.capabilities & SMB_CAP_UNIX))
+	{
+		PARANOIA("We should not be here because we are not UNIX\n");
+	}
+
+	smb_lock_server(server);
+	oldpathlen = strlen(oldpath);
+  retry:
+	WSET(param, 0, level);
+	DSET(param, 2, 0);
+	result = smb_encode_path(server, param + 6, SMB_MAXNAMELEN+1, dentry, NULL);
+	if (result < 0)
+		goto out;
+	p = param + 6 + result;
+
+
+	result = smb_trans2_request(server, TRANSACT2_SETPATHINFO,
+				    oldpathlen + 1, (char *)oldpath, p - param, param,
+				    &resp_data_len, &resp_data,
+				    &resp_param_len, &resp_param);
+	DEBUG1("for %s: result=%d, rcls=%d, err=%d\n",
+		&param[6], result, server->rcls, server->err);
+	if (result < 0) {
+		if (smb_retry(server))
+			goto retry;
+		goto out;
+	}
+	if (server->rcls != 0) {
+		VERBOSE("for %s: result=%d, rcls=%d, err=%d\n",
+			&param[6], result, server->rcls, server->err);
+		result = smb_errno(server);
+		goto out;
+	}
+
+	result = 0;
+
+out:
+	smb_unlock_server(server);
+	return result;
+}
+
+/*
+ * Create a hard link object called new_dentry which points to dentry.
+ *
+ * We need to lock server
+ *
+ */
+extern int smb_proc_link(struct smb_sb_info *server, struct dentry *dentry, struct dentry *new_dentry)
+{
+       char *p, *param = server->temp_buf;
+       unsigned char *resp_data = NULL;
+       unsigned char *resp_param = NULL;
+       int resp_data_len = 0;
+       int resp_param_len = 0;
+       int result;
+       int level = SMB_SET_FILE_UNIX_HLINK;
+       int newpathlen = 0;
+       char newpath[SMB_MAXPATHLEN+1];
+
+       if(!(server->opt.capabilities & SMB_CAP_UNIX))
+       {
+               PARANOIA("We should not be here because we are not UNIX\n");
+       }
+
+       smb_lock_server(server);
+
+       newpathlen = smb_encode_path(server, newpath, SMB_MAXPATHLEN+1, dentry, NULL);
+
+       DEBUG1("newpathlen = %d newpath=\"%s\"\n", newpathlen, newpath);
+retry:
+       WSET(param, 0, level);
+       DSET(param, 2, 0);
+       result = smb_encode_path(server, param + 6, SMB_MAXPATHLEN+1, new_dentry, NULL);
+       if (result < 0)
+               goto out;
+       p = param + 6 + result;
+       DEBUG1("pathlen = %d oldpath=\"%s\"\n", result, param + 6);
+
+
+       result = smb_trans2_request(server, TRANSACT2_SETPATHINFO,
+                                   newpathlen + 1, (char *)newpath, p - param, param,
+                                  &resp_data_len, &resp_data,
+                                   &resp_param_len, &resp_param);
+       DEBUG1("for %s: result=%d, rcls=%d, err=%d\n",
+               &param[6], result, server->rcls, server->err);
+	if (result < 0) {
+	if (smb_retry(server))
+			goto retry;
+		goto out;
+	}
+    if (server->rcls != 0) {
+	    VERBOSE("for %s: result=%d, rcls=%d, err=%d\n",
+                      &param[6], result, server->rcls, server->err);
+		result = smb_errno(server);
+		goto out;
+    }
+
+	result = 0;
+
+out:
+	smb_unlock_server(server);
+	return result;
+}
+
+
+/*
+ * We are called with the server locked
+ */
+int
+smb_proc_query_cifsunix(struct smb_sb_info *server)
+{
+	char *param = server->temp_buf;
+	unsigned char *resp_data = NULL;
+	unsigned char *resp_param = NULL;
+	int resp_data_len = 0;
+	int resp_param_len = 0;
+	int result;
+	int level;
+	int major, minor;
+	u64 caps;
+	
+	if(!(server->opt.capabilities & SMB_CAP_UNIX)) {
+		PARANOIA("We should not be here because we are not UNIX\n");
+	}
+
+	DEBUG1("SMB_QUERY_CIFS_UNIX_INFO\n");
+
+	level = SMB_QUERY_CIFS_UNIX_INFO;
+  retry:
+	
+	WSET(param, 0, level);
+
+	result = smb_trans2_request(server, TRANSACT2_QFSINFO,
+				    0, NULL, 2, param,
+				    &resp_data_len, &resp_data,
+				    &resp_param_len, &resp_param);
+	if (result < 0) {
+		if (smb_retry(server))
+			goto retry;
+		goto out;
+	}
+	VERBOSE("resp_data_len = %d\n", resp_data_len);
+
+	if(resp_data_len < 12)
+	{
+		DEBUG1("Not enough data\n");
+		goto out;
+	}
+
+	major = WVAL(resp_data, 0);
+	minor = WVAL(resp_data, 2);
+
+	
+	DEBUG1("Server implements \"CIFS Extensions for UNIX systems v%d.%d\"\n", major, minor);
+
+	caps = LVAL(resp_data, 4);
+	DEBUG1("Server capabilities 0x%016llx\n", caps);
+
+out:
+	return result;
+}
+
+
diff -ruN -X dontdiff /usr/src/linux-2.4.9-e.27/fs/smbfs/proto.h linux-2.4.9-e.27/fs/smbfs/proto.h
--- /usr/src/linux-2.4.9-e.27/fs/smbfs/proto.h	Thu Jan  1 10:00:00 1970
+++ linux-2.4.9-e.27/fs/smbfs/proto.h	Mon Nov 24 22:00:43 2003
@@ -0,0 +1,76 @@
+/*
+ *  Autogenerated with cproto on:  Wed Apr 10 07:04:32 EST 2002
+ */
+
+/* proc.c */
+extern int smb_setcodepage(struct smb_sb_info *server, struct smb_nls_codepage *cp);
+extern __u32 smb_len(__u8 *p);
+extern int smb_get_rsize(struct smb_sb_info *server);
+extern int smb_get_wsize(struct smb_sb_info *server);
+extern int smb_errno(struct smb_sb_info *server);
+extern int smb_newconn(struct smb_sb_info *server, struct smb_conn_opt *opt);
+extern int smb_wakeup(struct smb_sb_info *server);
+extern __u8 *smb_setup_header(struct smb_sb_info *server, __u8 command, __u16 wct, __u16 bcc);
+extern int smb_open(struct dentry *dentry, int wish);
+extern int smb_close(struct inode *ino);
+extern int smb_close_fileid(struct dentry *dentry, __u16 fileid);
+extern int smb_proc_read(struct inode *inode, off_t offset, int count, char *data);
+extern int smb_proc_write(struct inode *inode, off_t offset, int count, const char *data);
+extern int smb_proc_create(struct dentry *dentry, __u16 attr, time_t ctime, __u16 *fileid);
+extern int smb_proc_mv(struct dentry *old_dentry, struct dentry *new_dentry);
+extern int smb_proc_mkdir(struct dentry *dentry);
+extern int smb_proc_rmdir(struct dentry *dentry);
+extern int smb_proc_unlink(struct dentry *dentry);
+extern int smb_proc_flush(struct smb_sb_info *server, __u16 fileid);
+extern int smb_proc_trunc(struct smb_sb_info *server, __u16 fid, __u32 length);
+extern void smb_init_root_dirent(struct smb_sb_info *server, struct smb_fattr *fattr);
+extern void smb_decode_unix_basic(struct smb_fattr *fattr, char *p);
+extern int smb_proc_readdir(struct file *filp, void *dirent, filldir_t filldir, struct smb_cache_control *ctl);
+extern int smb_proc_getattr(struct dentry *dir, struct smb_fattr *fattr);
+extern int smb_proc_setattr(struct dentry *dir, struct smb_fattr *fattr);
+extern int smb_proc_setattr_unix(struct dentry *dentry, struct iattr *attr);
+extern int smb_proc_settime(struct dentry *dentry, struct smb_fattr *fattr);
+extern int smb_proc_dskattr(struct super_block *sb, struct statfs *attr);
+extern int smb_proc_read_link(struct smb_sb_info *server, struct dentry *dentry, char *buffer, int len);
+extern int smb_proc_symlink(struct smb_sb_info *server, struct dentry *dentry, const char *oldpath);
+extern int smb_proc_link(struct smb_sb_info *server, struct dentry *dentry, struct dentry *new_dentry);
+extern int smb_proc_query_cifsunix(struct smb_sb_info *server);
+/* dir.c */
+extern struct file_operations smb_dir_operations;
+extern struct inode_operations smb_dir_inode_operations;
+extern void smb_new_dentry(struct dentry *dentry);
+extern void smb_renew_times(struct dentry *dentry);
+extern int smb_instantiate(struct dentry *dentry, __u16 fileid, int have_id);
+
+/* cache.c */
+extern void smb_invalid_dir_cache(struct inode *dir);
+extern void smb_invalidate_dircache_entries(struct dentry *parent);
+extern struct dentry *smb_dget_fpos(struct dentry *dentry, struct dentry *parent, unsigned long fpos);
+extern int smb_fill_cache(struct file *filp, void *dirent, filldir_t filldir, struct smb_cache_control *ctrl, struct qstr *qname, struct smb_fattr *entry);
+/* sock.c */
+extern int smb_valid_socket(struct inode *inode);
+extern int smb_catch_keepalive(struct smb_sb_info *server);
+extern int smb_dont_catch_keepalive(struct smb_sb_info *server);
+extern void smb_close_socket(struct smb_sb_info *server);
+extern int smb_round_length(int len);
+extern int smb_request(struct smb_sb_info *server);
+extern int smb_trans2_request(struct smb_sb_info *server, __u16 trans2_command, int ldata, unsigned char *data, int lparam, unsigned char *param, int *lrdata, unsigned char **rdata, int *lrparam, unsigned char **rparam);
+/* inode.c */
+extern struct inode *smb_iget(struct super_block *sb, struct smb_fattr *fattr);
+extern void smb_get_inode_attr(struct inode *inode, struct smb_fattr *fattr);
+extern void smb_set_inode_attr(struct inode *inode, struct smb_fattr *fattr);
+extern void smb_invalidate_inodes(struct smb_sb_info *server);
+extern int smb_revalidate_inode(struct dentry *dentry);
+extern struct super_block *smb_read_super(struct super_block *sb, void *raw_data, int silent);
+extern int smb_notify_change(struct dentry *dentry, struct iattr *attr);
+/* file.c */
+extern struct address_space_operations smb_file_aops;
+extern struct file_operations smb_file_operations;
+extern struct inode_operations smb_file_inode_operations;
+/* ioctl.c */
+extern int smb_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg);
+/* symlink.c */
+extern int smb_read_link(struct dentry *dentry, char *buffer, int len);
+extern int smb_symlink(struct inode *inode, struct dentry *dentry, const char *oldname);
+extern int smb_follow_link(struct dentry *dentry, struct nameidata *nd);
+extern struct inode_operations smb_link_inode_operations;
diff -ruN -X dontdiff /usr/src/linux-2.4.9-e.27/fs/smbfs/sock.c linux-2.4.9-e.27/fs/smbfs/sock.c
--- /usr/src/linux-2.4.9-e.27/fs/smbfs/sock.c	Wed May 16 06:40:55 2001
+++ linux-2.4.9-e.27/fs/smbfs/sock.c	Sun Oct 19 18:53:12 2003
@@ -27,6 +27,7 @@
 #include <asm/uaccess.h>
 
 #include "smb_debug.h"
+#include "proto.h"
 
 
 static int
@@ -218,7 +219,7 @@
 		server->data_ready = NULL;
 		goto out;
 	}
-	DEBUG1("sk->d_r = %x, server->d_r = %x\n",
+	VERBOSE("sk->d_r = %x, server->d_r = %x\n",
 		 (unsigned int) (sk->data_ready),
 		 (unsigned int) (server->data_ready));
 
@@ -265,7 +266,7 @@
 		       "server->data_ready == NULL\n");
 		goto out;
 	}
-	DEBUG1("smb_dont_catch_keepalive: sk->d_r = %x, server->d_r = %x\n",
+	VERBOSE("smb_dont_catch_keepalive: sk->d_r = %x, server->d_r = %x\n",
 	       (unsigned int) (sk->data_ready),
 	       (unsigned int) (server->data_ready));
 
@@ -674,7 +675,7 @@
 	 */
 	if (server->rcls) {
 		int error = smb_errno(server);
-		if (error == EBADSLT) {
+		if (error == -EBADSLT) {
 			printk(KERN_ERR "smb_request: tree ID invalid\n");
 			result = error;
 			goto bad_conn;
@@ -866,7 +867,7 @@
 	 */
 	if (server->rcls) {
 		int error = smb_errno(server);
-		if (error == EBADSLT) {
+		if (error == -EBADSLT) {
 			printk(KERN_ERR "smb_request: tree ID invalid\n");
 			result = error;
 			goto bad_conn;
diff -ruN -X dontdiff /usr/src/linux-2.4.9-e.27/fs/smbfs/symlink.c linux-2.4.9-e.27/fs/smbfs/symlink.c
--- /usr/src/linux-2.4.9-e.27/fs/smbfs/symlink.c	Thu Jan  1 10:00:00 1970
+++ linux-2.4.9-e.27/fs/smbfs/symlink.c	Mon Nov 24 21:57:33 2003
@@ -0,0 +1,80 @@
+/*
+ *  symlink.c
+ *
+ *  Copyright (C) 2002 by John Newbigin
+ *
+ *  Please add a note about your changes to smbfs in the ChangeLog file.
+ */
+
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/fcntl.h>
+#include <linux/stat.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/pagemap.h>
+#include <linux/smp_lock.h>
+
+#include <asm/uaccess.h>
+#include <asm/system.h>
+
+#include <linux/smbno.h>
+#include <linux/smb_fs.h>
+
+#include "smb_debug.h"
+#include "proto.h"
+
+int
+smb_read_link(struct dentry *dentry, char *buffer, int len)
+{
+	char link[256];
+	int r;
+	DEBUG1("read link buffer len = %d\n", len);
+
+	r = smb_proc_read_link(server_from_dentry(dentry), dentry, link, sizeof(link) - 1);
+	if(r > 0) {
+		return vfs_readlink(dentry, buffer, len, link);
+	} else {
+		return -ENOENT;
+	}
+}
+
+int smb_symlink(struct inode *inode,struct dentry *dentry,const char *oldname)
+{
+	int error;
+	
+	DEBUG1("create symlink %s -> %s/%s\n", oldname, DENTRY_PATH(dentry));
+
+	/* create a symlink object called dentry, pointing to oldname */
+	error = smb_proc_symlink(server_from_dentry(dentry), dentry, oldname);
+	if(!error)
+	{
+		error = smb_instantiate(dentry, 0, 0);
+	}
+	return error;
+}
+
+int
+smb_follow_link(struct dentry *dentry, struct nameidata *nd)
+{
+	char link[256];
+	int len;
+	DEBUG1("followlink of %s/%s\n", DENTRY_PATH(dentry));
+
+	len = smb_proc_read_link(server_from_dentry(dentry), dentry, link, sizeof(link) - 1);
+	if(len > 0) {
+		link[len] = 0;
+		return vfs_follow_link(nd, link);
+	} else {
+		return -ENOENT;
+	}
+}
+
+
+struct inode_operations smb_link_inode_operations =
+{
+	readlink:       smb_read_link,
+	follow_link:    smb_follow_link,
+};
+
diff -ruN -X dontdiff /usr/src/linux-2.4.9-e.27/include/linux/smb.h linux-2.4.9-e.27/include/linux/smb.h
--- /usr/src/linux-2.4.9-e.27/include/linux/smb.h	Wed Aug  6 05:06:52 2003
+++ linux-2.4.9-e.27/include/linux/smb.h	Sun Oct 19 18:53:12 2003
@@ -91,6 +91,7 @@
 	time_t		f_ctime;
 	unsigned long	f_blksize;
 	unsigned long	f_blocks;
+	int			f_unix;
 };
 
 enum smb_conn_state {
diff -ruN -X dontdiff /usr/src/linux-2.4.9-e.27/include/linux/smb_fs.h linux-2.4.9-e.27/include/linux/smb_fs.h
--- /usr/src/linux-2.4.9-e.27/include/linux/smb_fs.h	Thu Jun 21 09:07:57 2001
+++ linux-2.4.9-e.27/include/linux/smb_fs.h	Sun Oct 19 19:18:08 2003
@@ -112,7 +112,7 @@
 #define SMB_CAP_NT_FIND          0x0200
 #define SMB_CAP_DFS              0x1000
 #define SMB_CAP_LARGE_READX      0x4000
-
+#define SMB_CAP_UNIX             0x800000
 
 /*
  * This is the time we allow an inode, dentry or dir cache to live. It is bad
@@ -161,84 +161,6 @@
 }
 
 
-/* FIXME! the prototype list is probably not correct. Automate? */
-
-/* linux/fs/smbfs/file.c */
-extern struct inode_operations smb_file_inode_operations;
-extern struct file_operations smb_file_operations;
-extern struct address_space_operations smb_file_aops;
-
-/* linux/fs/smbfs/dir.c */
-extern struct inode_operations smb_dir_inode_operations;
-extern struct file_operations smb_dir_operations;
-void smb_new_dentry(struct dentry *dentry);
-void smb_renew_times(struct dentry *);
-
-/* linux/fs/smbfs/ioctl.c */
-int smb_ioctl (struct inode *, struct file *, unsigned int, unsigned long);
-
-/* linux/fs/smbfs/inode.c */
-struct super_block *smb_read_super(struct super_block *, void *, int);
-void smb_get_inode_attr(struct inode *, struct smb_fattr *);
-void smb_set_inode_attr(struct inode *, struct smb_fattr *);
-void smb_invalidate_inodes(struct smb_sb_info *);
-int  smb_revalidate_inode(struct dentry *);
-int  smb_notify_change(struct dentry *, struct iattr *);
-struct inode *smb_iget(struct super_block *, struct smb_fattr *);
-
-/* linux/fs/smbfs/proc.c */
-int smb_setcodepage(struct smb_sb_info *server, struct smb_nls_codepage *cp);
-__u32 smb_len(unsigned char *);
-__u8 *smb_setup_header(struct smb_sb_info *, __u8, __u16, __u16);
-int smb_get_rsize(struct smb_sb_info *);
-int smb_get_wsize(struct smb_sb_info *);
-int smb_newconn(struct smb_sb_info *, struct smb_conn_opt *);
-int smb_errno(struct smb_sb_info *);
-int smb_close(struct inode *);
-int smb_close_fileid(struct dentry *, __u16);
-int smb_open(struct dentry *, int);
-int smb_proc_read(struct inode *, off_t, int, char *);
-int smb_proc_write(struct inode *, off_t, int, const char *);
-int smb_proc_create(struct dentry *, __u16, time_t, __u16 *);
-int smb_proc_mv(struct dentry *, struct dentry *);
-int smb_proc_mkdir(struct dentry *);
-int smb_proc_rmdir(struct dentry *);
-int smb_proc_unlink(struct dentry *);
-int smb_proc_readdir(struct file *filp, void *dirent, filldir_t filldir,
-		     struct smb_cache_control *ctl);
-int smb_proc_getattr(struct dentry *, struct smb_fattr *);
-int smb_proc_setattr(struct dentry *, struct smb_fattr *);
-int smb_proc_settime(struct dentry *, struct smb_fattr *);
-int smb_proc_dskattr(struct super_block *, struct statfs *);
-int smb_proc_disconnect(struct smb_sb_info *);
-int smb_proc_trunc(struct smb_sb_info *, __u16, __u32);
-int smb_proc_flush(struct smb_sb_info *, __u16);
-void smb_init_root_dirent(struct smb_sb_info *, struct smb_fattr *);
-
-/* linux/fs/smbfs/sock.c */
-int smb_round_length(int);
-int smb_valid_socket(struct inode *);
-void smb_close_socket(struct smb_sb_info *);
-int smb_request(struct smb_sb_info *server);
-int smb_catch_keepalive(struct smb_sb_info *server);
-int smb_dont_catch_keepalive(struct smb_sb_info *server);
-int smb_trans2_request(struct smb_sb_info *server, __u16 trans2_command,
-		       int ldata, unsigned char *data,
-		       int lparam, unsigned char *param,
-		       int *lrdata, unsigned char **rdata,
-		       int *lrparam, unsigned char **rparam);
-
-/* fs/smbfs/cache.c */
-
-void smb_invalid_dir_cache(struct inode * dir);
-void smb_invalidate_dircache_entries(struct dentry *parent);
-struct dentry * smb_dget_fpos(struct dentry *dentry, struct dentry *parent, 
-			      unsigned long fpos);
-int smb_fill_cache(struct file *filp, void *dirent, filldir_t filldir,
-		   struct smb_cache_control *ctrl, struct qstr *qname,
-		   struct smb_fattr *entry);
-
-
 #endif /* __KERNEL__ */
 
 #endif /* _LINUX_SMB_FS_H */
diff -ruN -X dontdiff /usr/src/linux-2.4.9-e.27/include/linux/smbno.h linux-2.4.9-e.27/include/linux/smbno.h
--- /usr/src/linux-2.4.9-e.27/include/linux/smbno.h	Tue Nov 25 05:30:40 1997
+++ linux-2.4.9-e.27/include/linux/smbno.h	Sun Oct 19 18:53:12 2003
@@ -39,14 +39,20 @@
 #define ERRbadshare 32          /* Share mode on file conflict with open mode */
 #define ERRlock 33              /* Lock request conflicts with existing lock */
 #define ERRfilexists 80         /* File in operation already exists */
-#define ERRundocumented1 123    /* Invalid name?? e.g. .tmp* */
 #define ERRbadpipe 230          /* Named pipe invalid */
 #define ERRpipebusy 231         /* All instances of pipe are busy */
 #define ERRpipeclosing 232      /* named pipe close in progress */
 #define ERRnotconnected 233     /* No process on other end of named pipe */
 #define ERRmoredata 234         /* More data to be returned */
-#define ERROR_EAS_DIDNT_FIT 275 /* Extended attributes didn't fit */
-#define ERROR_EAS_NOT_SUPPORTED 282 /* Extended attributes not supported */
+
+#define ERROR_INVALID_PARAMETER	 87
+#define ERROR_DISK_FULL		112
+#define ERROR_INVALID_NAME	123
+#define ERROR_DIR_NOT_EMPTY	145
+#define ERROR_NOT_LOCKED	158
+#define ERROR_ALREADY_EXISTS	183  /* see also 80 ? */
+#define ERROR_EAS_DIDNT_FIT	275 /* Extended attributes didn't fit */
+#define ERROR_EAS_NOT_SUPPORTED	282 /* Extended attributes not supported */
 
 /* Error codes for the ERRSRV class */
 
@@ -275,4 +281,74 @@
 #define TRANSACT2_FINDNOTIFYNEXT  12
 #define TRANSACT2_MKDIR           13
 
+
+/* UNIX stuff  (from samba trans2.h) */
+
+#define MIN_UNIX_INFO_LEVEL 0x200
+#define MAX_UNIX_INFO_LEVEL 0x2FF
+
+#define SMB_QUERY_FILE_UNIX_BASIC 0x200
+#define SMB_QUERY_FILE_UNIX_LINK  0x201
+#define SMB_QUERY_FILE_UNIX_HLINK 0x202
+
+#define SMB_SET_FILE_UNIX_BASIC   0x200
+
+#define SMB_FILE_FILE_UNIX        0x202
+
+#define SMB_QUERY_CIFS_UNIX_INFO  0x200
+
+#define SMB_MODE_NO_CHANGE        0xFFFFFFFF     /* file mode value which */
+                                                 /* means "don't change it" */
+#define SMB_UID_NO_CHANGE         0xFFFFFFFF
+#define SMB_GID_NO_CHANGE         0xFFFFFFFF
+
+#define SMB_TIME_NO_CHANGE        0xFFFFFFFFFFFFFFFF
+#define SMB_SIZE_NO_CHANGE        0xFFFFFFFFFFFFFFFF
+
+/* UNIX filetype mappings. */
+
+#define UNIX_TYPE_FILE 0
+#define UNIX_TYPE_DIR 1
+#define UNIX_TYPE_SYMLINK 2
+#define UNIX_TYPE_CHARDEV 3
+#define UNIX_TYPE_BLKDEV 4
+#define UNIX_TYPE_FIFO 5
+#define UNIX_TYPE_SOCKET 6
+#define UNIX_TYPE_UNKNOWN 0xFFFFFFFF
+
+/*
+ * Oh this is fun. "Standard UNIX permissions" has no
+ * meaning in POSIX. We need to define the mapping onto
+ * and off the wire as this was not done in the original HP
+ * spec. JRA.
+ */
+
+#define UNIX_X_OTH			0000001
+#define UNIX_W_OTH			0000002
+#define UNIX_R_OTH			0000004
+#define UNIX_X_GRP			0000010
+#define UNIX_W_GRP                      0000020
+#define UNIX_R_GRP                      0000040
+#define UNIX_X_USR                      0000100
+#define UNIX_W_USR                      0000200
+#define UNIX_R_USR                      0000400
+#define UNIX_STICKY                     0001000
+#define UNIX_SET_GID                    0002000
+#define UNIX_SET_UID                    0004000
+
+/* Masks for the above */
+#define UNIX_OTH_MASK                   0000007
+#define UNIX_GRP_MASK                   0000070
+#define UNIX_USR_MASK                   0000700
+#define UNIX_PERM_MASK                  0000777
+#define UNIX_EXTRA_MASK                 0007000
+#define UNIX_ALL_MASK                   0007777
+
+#define SMB_QUERY_FILE_UNIX_LINK       0x201
+#define SMB_SET_FILE_UNIX_LINK         0x201
+#define SMB_SET_FILE_UNIX_HLINK        0x203
+
+#define SMB_FIND_FILE_UNIX             0x202
+
+
 #endif /* _SMBNO_H_ */
