diff -ruN -X dontdiff /usr/src/linux-2.4.20-18.9/fs/smbfs/ChangeLog linux-2.4.20-18.9/fs/smbfs/ChangeLog
--- /usr/src/linux-2.4.20-18.9/fs/smbfs/ChangeLog	2002-02-26 06:38:09.000000000 +1100
+++ linux-2.4.20-18.9/fs/smbfs/ChangeLog	2003-06-24 23:04:19.000000000 +1000
@@ -1,5 +1,12 @@
 ChangeLog for smbfs.
 
+2003-06-24 John Newbigin <jn@it.swin.edu.au>
+	* Fix symlink bug & clean up symlink code
+
+2002-04-19 John Newbigin <jn@it.swin.edu.au>
+	* Implement hard liks
+	* add missing lock to setattr_unix
+
 2001-12-31 René Scharfe <l.s.r@web.de>
 
 	* inode.c: added smb_show_options to show mount options in /proc/mounts
@@ -10,6 +17,13 @@
 
 	* 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
diff -ruN -X dontdiff /usr/src/linux-2.4.20-18.9/fs/smbfs/dir.c linux-2.4.20-18.9/fs/smbfs/dir.c
--- /usr/src/linux-2.4.20-18.9/fs/smbfs/dir.c	2001-10-03 10:03:34.000000000 +1000
+++ linux-2.4.20-18.9/fs/smbfs/dir.c	2003-06-24 22:29:34.000000000 +1000
@@ -30,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 =
 {
@@ -49,6 +51,9 @@
 	rename:		smb_rename,
 	revalidate:	smb_revalidate_inode,
 	setattr:	smb_notify_change,
+	symlink:	smb_symlink,
+	mknod:		smb_make_node,
+	link:		smb_link,
 };
 
 /*
@@ -479,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",
@@ -498,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;
@@ -583,3 +602,63 @@
 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;
+}
+
+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;
+}
+
diff -ruN -X dontdiff /usr/src/linux-2.4.20-18.9/fs/smbfs/inode.c linux-2.4.20-18.9/fs/smbfs/inode.c
--- /usr/src/linux-2.4.20-18.9/fs/smbfs/inode.c	2002-11-29 10:53:15.000000000 +1100
+++ linux-2.4.20-18.9/fs/smbfs/inode.c	2003-06-21 21:24:38.000000000 +1000
@@ -81,6 +81,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;
@@ -188,7 +194,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 {
 			/*
@@ -587,7 +608,9 @@
 		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) { */
+			error = smb_proc_trunc(server, inode->u.smbfs_i.fileid,
 					 attr->ia_size);
 		if (error)
 			goto out;
@@ -597,6 +620,26 @@
 		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;
+		}*/
+
+		/* For now we don't want to set the size with setattr_unix */
+		attr->ia_valid &= ~ATTR_SIZE;
+
+		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
diff -ruN -X dontdiff /usr/src/linux-2.4.20-18.9/fs/smbfs/Makefile linux-2.4.20-18.9/fs/smbfs/Makefile
--- /usr/src/linux-2.4.20-18.9/fs/smbfs/Makefile	2001-10-03 10:03:34.000000000 +1000
+++ linux-2.4.20-18.9/fs/smbfs/Makefile	2003-06-21 21:24:38.000000000 +1000
@@ -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
@@ -29,7 +29,7 @@
 #
 
 # getopt.c not included. It is intentionally separate
-SRC = proc.c dir.c cache.c sock.c inode.c file.c ioctl.c
+SRC = proc.c dir.c cache.c sock.c inode.c file.c ioctl.c symlink.c
 
 proto:
 	-rm -f proto.h
diff -ruN -X dontdiff /usr/src/linux-2.4.20-18.9/fs/smbfs/proc.c linux-2.4.20-18.9/fs/smbfs/proc.c
--- /usr/src/linux-2.4.20-18.9/fs/smbfs/proc.c	2002-11-29 10:53:15.000000000 +1100
+++ linux-2.4.20-18.9/fs/smbfs/proc.c	2003-06-24 22:56:28.000000000 +1000
@@ -55,7 +55,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)
@@ -426,6 +427,131 @@
 	return (time_t)t;
 }
 
+/* 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;
+}
+
+/*
+ * 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;
+	}
+}
+
+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;
+
+	if(mode & S_IFCHR)
+		return UNIX_TYPE_CHARDEV;
+
+	if(mode & S_IFBLK)
+		return UNIX_TYPE_BLKDEV;
+
+	if(mode & S_IFIFO)
+		return UNIX_TYPE_FIFO;
+
+	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
 /* Convert the Unix UTC into NT time */
 static u64
@@ -862,6 +988,12 @@
 		}
 	}
 
+	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);
@@ -1118,12 +1250,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,
@@ -1370,6 +1509,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)
@@ -1491,25 +1640,37 @@
 	fattr->f_uid = server->mnt->uid;
 	fattr->f_gid = server->mnt->gid;
 	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 = SMB_ST_BLKSIZE;
+	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);
 	}
-	/* 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;
 }
 
@@ -1538,6 +1699,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 ...
 	 */
@@ -1613,6 +1779,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);
@@ -1723,6 +1894,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
@@ -1793,9 +2006,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;
 	}
 
@@ -1882,6 +2111,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);
 
 	/*
@@ -2004,6 +2237,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;
 			}
 
 			/*
@@ -2097,6 +2333,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) {
@@ -2174,6 +2415,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);
@@ -2220,9 +2466,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)
@@ -2247,40 +2498,49 @@
 		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;
 }
@@ -2362,6 +2622,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);
@@ -2397,6 +2662,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)
@@ -2404,6 +2670,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);
@@ -2423,6 +2694,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);
@@ -2471,6 +2747,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);
@@ -2515,6 +2796,144 @@
 }
 
 /*
+ * 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_MAXPATHLEN+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 is 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.
  *
  * Incredibly enough, in all of SMB there is no message to allow
@@ -2534,6 +2953,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));
 
@@ -2600,3 +3024,244 @@
 	smb_unlock_server(server);
 	return result;
 }
+
+int
+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);
+	level = SMB_QUERY_FILE_UNIX_LINK;
+  retry:
+	
+	WSET(param, 0, level);
+	DSET(param, 2, 0);
+	result = smb_encode_path(server, param + 6, SMB_MAXPATHLEN+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_MAXPATHLEN+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.20-18.9/fs/smbfs/proto.h linux-2.4.20-18.9/fs/smbfs/proto.h
--- /usr/src/linux-2.4.20-18.9/fs/smbfs/proto.h	2001-10-03 10:03:34.000000000 +1000
+++ linux-2.4.20-18.9/fs/smbfs/proto.h	2003-06-25 21:04:16.000000000 +1000
@@ -1,5 +1,5 @@
 /*
- *  Autogenerated with cproto on:  Tue Oct 2 20:40:54 CEST 2001
+ *  Autogenerated with cproto on:  Wed Jun 25 21:04:11 EST 2003
  */
 
 /* proc.c */
@@ -24,16 +24,23 @@
 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_symlink(struct inode *inode, struct dentry *dentry, const char *oldname);
 /* cache.c */
 extern void smb_invalid_dir_cache(struct inode *dir);
 extern void smb_invalidate_dircache_entries(struct dentry *parent);
@@ -61,3 +68,7 @@
 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_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.20-18.9/fs/smbfs/symlink.c linux-2.4.20-18.9/fs/smbfs/symlink.c
--- /usr/src/linux-2.4.20-18.9/fs/smbfs/symlink.c	1970-01-01 10:00:00.000000000 +1000
+++ linux-2.4.20-18.9/fs/smbfs/symlink.c	2003-06-24 22:47:37.000000000 +1000
@@ -0,0 +1,82 @@
+/*
+ *  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;
+	int error;
+	DEBUG1("read link buffer len = %d\n", len);
+
+	error = -ENOMEM;
+	link = kmalloc(SMB_MAXNAMELEN + 1, GFP_KERNEL);
+	if(!link) 
+		goto out;
+
+	error = smb_proc_read_link(server_from_dentry(dentry), dentry, link, SMB_MAXNAMELEN);
+	if(error > 0) {
+		error = vfs_readlink(dentry, buffer, len, link);
+	} else {
+		error = -ENOENT;
+	}
+	kfree(link);
+out:
+	return error;
+}
+
+int
+smb_follow_link(struct dentry *dentry, struct nameidata *nd)
+{
+	char *link;
+	int error;
+	int len;
+	DEBUG1("followlink of %s/%s\n", DENTRY_PATH(dentry));
+
+	error = -ENOMEM;
+	link = kmalloc(SMB_MAXNAMELEN + 1, GFP_KERNEL);
+	if(!link) 
+		goto out;
+
+	len = smb_proc_read_link(server_from_dentry(dentry), dentry, link, SMB_MAXNAMELEN);
+	if(len > 0 && len <= SMB_MAXNAMELEN) {
+		link[len] = 0;
+		error = vfs_follow_link(nd, link);
+	} else {
+		error = -ENOENT;
+	}
+	kfree(link);
+out:
+	return error;
+}
+
+
+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.20-18.9/include/linux/smb_fs.h linux-2.4.20-18.9/include/linux/smb_fs.h
--- /usr/src/linux-2.4.20-18.9/include/linux/smb_fs.h	2003-05-29 22:37:28.000000000 +1000
+++ linux-2.4.20-18.9/include/linux/smb_fs.h	2003-06-21 21:24:53.000000000 +1000
@@ -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
diff -ruN -X dontdiff /usr/src/linux-2.4.20-18.9/include/linux/smb.h linux-2.4.20-18.9/include/linux/smb.h
--- /usr/src/linux-2.4.20-18.9/include/linux/smb.h	2003-05-29 22:37:27.000000000 +1000
+++ linux-2.4.20-18.9/include/linux/smb.h	2003-06-21 21:24:39.000000000 +1000
@@ -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.20-18.9/include/linux/smbno.h linux-2.4.20-18.9/include/linux/smbno.h
--- /usr/src/linux-2.4.20-18.9/include/linux/smbno.h	2001-10-08 09:47:43.000000000 +1000
+++ linux-2.4.20-18.9/include/linux/smbno.h	2003-06-21 21:24:39.000000000 +1000
@@ -281,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_ */
