r6146: Added OS/2 EA support in trans2_open and trans2_mkdir. Fixed in nttrans_create.
[samba.git] / source / smbd / trans2.c
index 9f5ef09454ad8dbfd324940b4c68498e20c96544..a68b165cb0088fd81cd74a7eaf3d97d93806ecc9 100644 (file)
 
 #include "includes.h"
 
-extern int Protocol;
-extern BOOL case_sensitive;
+extern enum protocol_types Protocol;
 extern int smb_read_error;
-extern fstring local_machine;
 extern int global_oplock_break;
 extern uint32 global_client_caps;
 extern struct current_user current_user;
 
 #define get_file_size(sbuf) ((sbuf).st_size)
+#define DIR_ENTRY_SAFETY_MARGIN 4096
 
-/* given a stat buffer return the allocated size on disk, taking into
-   account sparse files */
-SMB_BIG_UINT get_allocation_size(files_struct *fsp, SMB_STRUCT_STAT *sbuf)
+/********************************************************************
+ Roundup a value to the nearest allocation roundup size boundary.
+ Only do this for Windows clients.
+********************************************************************/
+
+SMB_BIG_UINT smb_roundup(connection_struct *conn, SMB_BIG_UINT val)
+{
+       SMB_BIG_UINT rval = lp_allocation_roundup_size(SNUM(conn));
+
+       /* Only roundup for Windows clients. */
+       enum remote_arch_types ra_type = get_remote_arch();
+       if (rval && (ra_type != RA_SAMBA) && (ra_type != RA_CIFSFS)) {
+               val = SMB_ROUNDUP(val,rval);
+       }
+       return val;
+}
+
+/********************************************************************
+ Given a stat buffer return the allocated size on disk, taking into
+ account sparse files.
+********************************************************************/
+
+SMB_BIG_UINT get_allocation_size(connection_struct *conn, files_struct *fsp, SMB_STRUCT_STAT *sbuf)
 {
        SMB_BIG_UINT ret;
+
 #if defined(HAVE_STAT_ST_BLOCKS) && defined(STAT_ST_BLOCKSIZE)
        ret = (SMB_BIG_UINT)STAT_ST_BLOCKSIZE * (SMB_BIG_UINT)sbuf->st_blocks;
 #else
        ret = (SMB_BIG_UINT)get_file_size(*sbuf);
 #endif
+
        if (!ret && fsp && fsp->initial_allocation_size)
                ret = fsp->initial_allocation_size;
-       ret = SMB_ROUNDUP(ret,SMB_ROUNDUP_ALLOCATION_SIZE);
-       return ret;
+
+       return smb_roundup(conn, ret);
 }
 
 /****************************************************************************
  Utility functions for dealing with extended attributes.
 ****************************************************************************/
 
-struct ea_list {
-       struct ea_list *next, *prev;
-       struct ea_struct ea;
+static const char *prohibited_ea_names[] = {
+       SAMBA_POSIX_INHERITANCE_EA_NAME,
+       SAMBA_XATTR_DOS_ATTRIB,
+       NULL
 };
 
-static 
+/****************************************************************************
+ Refuse to allow clients to overwrite our private xattrs.
+****************************************************************************/
+
+static BOOL samba_private_attr_name(const char *unix_ea_name)
+{
+       int i;
+
+       for (i = 0; prohibited_ea_names[i]; i++) {
+               if (strequal( prohibited_ea_names[i], unix_ea_name))
+                       return True;
+       }
+       return False;
+}
+
+/****************************************************************************
+ Get one EA value. Fill in a struct ea_struct.
+****************************************************************************/
+
+static BOOL get_ea_value(TALLOC_CTX *mem_ctx, connection_struct *conn, files_struct *fsp,
+                               const char *fname, char *ea_name, struct ea_struct *pea)
+{
+       /* Get the value of this xattr. Max size is 64k. */
+       size_t attr_size = 256;
+       char *val = NULL;
+       ssize_t sizeret;
+
+ again:
+
+       val = TALLOC_REALLOC_ARRAY(mem_ctx, val, char, attr_size);
+       if (!val) {
+               return False;
+       }
+
+       if (fsp && fsp->fd != -1) {
+               sizeret = SMB_VFS_FGETXATTR(fsp, fsp->fd, ea_name, val, attr_size);
+       } else {
+               sizeret = SMB_VFS_GETXATTR(conn, fname, ea_name, val, attr_size);
+       }
+
+       if (sizeret == -1 && errno == ERANGE && attr_size != 65536) {
+               attr_size = 65536;
+               goto again;
+       }
+
+       if (sizeret == -1) {
+               return False;
+       }
+
+       DEBUG(10,("get_ea_value: EA %s is of length %d: ", ea_name, sizeret));
+       dump_data(10, val, sizeret);
+
+       pea->flags = 0;
+       if (strnequal(ea_name, "user.", 5)) {
+               pea->name = &ea_name[5];
+       } else {
+               pea->name = ea_name;
+       }
+       pea->value.data = (unsigned char *)val;
+       pea->value.length = (size_t)sizeret;
+       return True;
+}
+
+/****************************************************************************
+ Return a linked list of the total EA's. Plus the total size
+****************************************************************************/
+
+static struct ea_list *get_ea_list_from_file(TALLOC_CTX *mem_ctx, connection_struct *conn, files_struct *fsp,
+                                       const char *fname, size_t *pea_total_len)
+{
+       /* Get a list of all xattrs. Max namesize is 64k. */
+       size_t ea_namelist_size = 1024;
+       char *ea_namelist;
+       char *p;
+       ssize_t sizeret;
+       int i;
+       struct ea_list *ea_list_head = NULL;
+
+       *pea_total_len = 0;
+
+       if (!lp_ea_support(SNUM(conn))) {
+               return NULL;
+       }
+
+       for (i = 0, ea_namelist = TALLOC(mem_ctx, ea_namelist_size); i < 6;
+                       ea_namelist = TALLOC_REALLOC_ARRAY(mem_ctx, ea_namelist, char, ea_namelist_size), i++) {
+               if (fsp && fsp->fd != -1) {
+                       sizeret = SMB_VFS_FLISTXATTR(fsp, fsp->fd, ea_namelist, ea_namelist_size);
+               } else {
+                       sizeret = SMB_VFS_LISTXATTR(conn, fname, ea_namelist, ea_namelist_size);
+               }
+
+               if (sizeret == -1 && errno == ERANGE) {
+                       ea_namelist_size *= 2;
+               } else {
+                       break;
+               }
+       }
+
+       if (sizeret == -1)
+               return NULL;
+
+       DEBUG(10,("get_ea_list_from_file: ea_namelist size = %d\n", sizeret ));
+
+       if (sizeret) {
+               for (p = ea_namelist; p - ea_namelist < sizeret; p += strlen(p) + 1) {
+                       struct ea_list *listp, *tmp;
+
+                       if (strnequal(p, "system.", 7) || samba_private_attr_name(p))
+                               continue;
+               
+                       listp = TALLOC_P(mem_ctx, struct ea_list);
+                       if (!listp)
+                               return NULL;
+
+                       if (!get_ea_value(mem_ctx, conn, fsp, fname, p, &listp->ea)) {
+                               return NULL;
+                       }
+
+                       {
+                               fstring dos_ea_name;
+                               push_ascii_fstring(dos_ea_name, listp->ea.name);
+                               *pea_total_len += 4 + strlen(dos_ea_name) + 1 + listp->ea.value.length;
+                               DEBUG(10,("get_ea_list_from_file: total_len = %u, %s, val len = %u\n",
+                                       *pea_total_len, dos_ea_name,
+                                       (unsigned int)listp->ea.value.length ));
+                       }
+                       DLIST_ADD_END(ea_list_head, listp, tmp);
+               }
+               /* Add on 4 for total length. */
+               if (*pea_total_len) {
+                       *pea_total_len += 4;
+               }
+       }
+
+       DEBUG(10,("get_ea_list_from_file: total_len = %u\n", *pea_total_len));
+       return ea_list_head;
+}
+
+/****************************************************************************
+ Fill a qfilepathinfo buffer with EA's. Returns the length of the buffer
+ that was filled.
+****************************************************************************/
+
+static unsigned int fill_ea_buffer(TALLOC_CTX *mem_ctx, char *pdata, unsigned int total_data_size,
+       connection_struct *conn, struct ea_list *ea_list)
+{
+       unsigned int ret_data_size = 4;
+       char *p = pdata;
+
+       SMB_ASSERT(total_data_size >= 4);
+
+       if (!lp_ea_support(SNUM(conn))) {
+               SIVAL(pdata,4,0);
+               return 4;
+       }
+
+       for (p = pdata + 4; ea_list; ea_list = ea_list->next) {
+               size_t dos_namelen;
+               fstring dos_ea_name;
+               push_ascii_fstring(dos_ea_name, ea_list->ea.name);
+               dos_namelen = strlen(dos_ea_name);
+               if (dos_namelen > 255 || dos_namelen == 0) {
+                       break;
+               }
+               if (ea_list->ea.value.length > 65535) {
+                       break;
+               }
+               if (4 + dos_namelen + 1 + ea_list->ea.value.length > total_data_size) {
+                       break;
+               }
+
+               /* We know we have room. */
+               SCVAL(p,0,ea_list->ea.flags);
+               SCVAL(p,1,dos_namelen);
+               SSVAL(p,2,ea_list->ea.value.length);
+               fstrcpy(p+4, dos_ea_name);
+               memcpy( p + 4 + dos_namelen + 1, ea_list->ea.value.data, ea_list->ea.value.length);
+
+               total_data_size -= 4 + dos_namelen + 1 + ea_list->ea.value.length;
+               p += 4 + dos_namelen + 1 + ea_list->ea.value.length;
+       }
+
+       ret_data_size = PTR_DIFF(p, pdata);
+       DEBUG(10,("fill_ea_buffer: data_size = %u\n", ret_data_size ));
+       SIVAL(pdata,0,ret_data_size);
+       return ret_data_size;
+}
+
+static unsigned int estimate_ea_size(connection_struct *conn, files_struct *fsp, const char *fname)
+{
+       size_t total_ea_len = 0;
+       TALLOC_CTX *mem_ctx = NULL;
+
+       if (!lp_ea_support(SNUM(conn))) {
+               return 0;
+       }
+       mem_ctx = talloc_init("estimate_ea_size");
+       (void)get_ea_list_from_file(mem_ctx, conn, fsp, fname, &total_ea_len);
+       talloc_destroy(mem_ctx);
+       return total_ea_len;
+}
+
+/****************************************************************************
+ Ensure the EA name is case insensitive by matching any existing EA name.
+****************************************************************************/
+
+static void canonicalize_ea_name(connection_struct *conn, files_struct *fsp, const char *fname, fstring unix_ea_name)
+{
+       size_t total_ea_len;
+       TALLOC_CTX *mem_ctx = talloc_init("canonicalize_ea_name");
+       struct ea_list *ea_list = get_ea_list_from_file(mem_ctx, conn, fsp, fname, &total_ea_len);
+
+       for (; ea_list; ea_list = ea_list->next) {
+               if (strequal(&unix_ea_name[5], ea_list->ea.name)) {
+                       DEBUG(10,("canonicalize_ea_name: %s -> %s\n",
+                               &unix_ea_name[5], ea_list->ea.name));
+                       safe_strcpy(&unix_ea_name[5], ea_list->ea.name, sizeof(fstring)-6);
+                       break;
+               }
+       }
+       talloc_destroy(mem_ctx);
+}
+
+/****************************************************************************
+ Set or delete an extended attribute.
+****************************************************************************/
+
+NTSTATUS set_ea(connection_struct *conn, files_struct *fsp, const char *fname, struct ea_list *ea_list)
+{
+       if (!lp_ea_support(SNUM(conn))) {
+               return NT_STATUS_EAS_NOT_SUPPORTED;
+       }
+
+       for (;ea_list; ea_list = ea_list->next) {
+               int ret;
+               fstring unix_ea_name;
+
+               fstrcpy(unix_ea_name, "user."); /* All EA's must start with user. */
+               fstrcat(unix_ea_name, ea_list->ea.name);
+
+               canonicalize_ea_name(conn, fsp, fname, unix_ea_name);
+
+               DEBUG(10,("set_ea: ea_name %s ealen = %u\n", unix_ea_name, ea_list->ea.value.length));
+
+               if (samba_private_attr_name(unix_ea_name)) {
+                       DEBUG(10,("set_ea: ea name %s is a private Samba name.\n", unix_ea_name));
+                       return NT_STATUS_ACCESS_DENIED;
+               }
+
+               if (ea_list->ea.value.length == 0) {
+                       /* Remove the attribute. */
+                       if (fsp && (fsp->fd != -1)) {
+                               DEBUG(10,("set_ea: deleting ea name %s on file %s by file descriptor.\n",
+                                       unix_ea_name, fsp->fsp_name));
+                               ret = SMB_VFS_FREMOVEXATTR(fsp, fsp->fd, unix_ea_name);
+                       } else {
+                               DEBUG(10,("set_ea: deleting ea name %s on file %s.\n",
+                                       unix_ea_name, fname));
+                               ret = SMB_VFS_REMOVEXATTR(conn, fname, unix_ea_name);
+                       }
+#ifdef ENOATTR
+                       /* Removing a non existent attribute always succeeds. */
+                       if (ret == -1 && errno == ENOATTR) {
+                               DEBUG(10,("set_ea: deleting ea name %s didn't exist - succeeding by default.\n",
+                                               unix_ea_name));
+                               ret = 0;
+                       }
+#endif
+               } else {
+                       if (fsp && (fsp->fd != -1)) {
+                               DEBUG(10,("set_ea: setting ea name %s on file %s by file descriptor.\n",
+                                       unix_ea_name, fsp->fsp_name));
+                               ret = SMB_VFS_FSETXATTR(fsp, fsp->fd, unix_ea_name,
+                                                       ea_list->ea.value.data, ea_list->ea.value.length, 0);
+                       } else {
+                               DEBUG(10,("set_ea: setting ea name %s on file %s.\n",
+                                       unix_ea_name, fname));
+                               ret = SMB_VFS_SETXATTR(conn, fname, unix_ea_name,
+                                                       ea_list->ea.value.data, ea_list->ea.value.length, 0);
+                       }
+               }
+
+               if (ret == -1) {
+#ifdef ENOTSUP
+                       if (errno == ENOTSUP) {
+                               return NT_STATUS_EAS_NOT_SUPPORTED;
+                       }
+#endif
+                       return map_nt_error_from_unix(errno);
+               }
+
+       }
+       return NT_STATUS_OK;
+}
+/****************************************************************************
+ Read a list of EA names from an incoming data buffer. Create an ea_list with them.
+****************************************************************************/
+
+static struct ea_list *read_ea_name_list(TALLOC_CTX *ctx, const char *pdata, size_t data_size)
+{
+       struct ea_list *ea_list_head = NULL;
+       size_t offset = 0;
+
+       while (offset + 2 < data_size) {
+               struct ea_list *tmp;
+               struct ea_list *eal = TALLOC_ZERO_P(ctx, struct ea_list);
+               unsigned int namelen = CVAL(pdata,offset);
+
+               offset++; /* Go past the namelen byte. */
+
+               /* integer wrap paranioa. */
+               if ((offset + namelen < offset) || (offset + namelen < namelen) ||
+                               (offset > data_size) || (namelen > data_size) ||
+                               (offset + namelen >= data_size)) {
+                       break;
+               }
+               /* Ensure the name is null terminated. */
+               if (pdata[offset + namelen] != '\0') {
+                       return NULL;
+               }
+               pull_ascii_talloc(ctx, &eal->ea.name, &pdata[offset]);
+               if (!eal->ea.name) {
+                       return NULL;
+               }
+
+               offset += (namelen + 1); /* Go past the name + terminating zero. */
+               DLIST_ADD_END(ea_list_head, eal, tmp);
+               DEBUG(10,("read_ea_name_list: read ea name %s\n", eal->ea.name));
+       }
+
+       return ea_list_head;
+}
+
+/****************************************************************************
+ Read one EA list entry from the buffer.
+****************************************************************************/
+
+struct ea_list *read_ea_list_entry(TALLOC_CTX *ctx, const char *pdata, size_t data_size, size_t *pbytes_used)
+{
+       struct ea_list *eal = TALLOC_ZERO_P(ctx, struct ea_list);
+       uint16 val_len;
+       unsigned int namelen;
+
+       if (!eal) {
+               return NULL;
+       }
+
+       if (data_size < 6) {
+               return NULL;
+       }
+
+       eal->ea.flags = CVAL(pdata,0);
+       namelen = CVAL(pdata,1);
+       val_len = SVAL(pdata,2);
+
+       if (4 + namelen + 1 + val_len > data_size) {
+               return NULL;
+       }
+
+       /* Ensure the name is null terminated. */
+       if (pdata[namelen + 4] != '\0') {
+               return NULL;
+       }
+       pull_ascii_talloc(ctx, &eal->ea.name, pdata + 4);
+       if (!eal->ea.name) {
+               return NULL;
+       }
+
+       eal->ea.value = data_blob(NULL, (size_t)val_len + 1);
+       if (!eal->ea.value.data) {
+               return NULL;
+       }
+
+       memcpy(eal->ea.value.data, pdata + 4 + namelen + 1, val_len);
+
+       /* Ensure we're null terminated just in case we print the value. */
+       eal->ea.value.data[val_len] = '\0';
+       /* But don't count the null. */
+       eal->ea.value.length--;
+
+       if (pbytes_used) {
+               *pbytes_used = 4 + namelen + 1 + val_len;
+       }
+
+       DEBUG(10,("read_ea_list_entry: read ea name %s\n", eal->ea.name));
+       dump_data(10, eal->ea.value.data, eal->ea.value.length);
+
+       return eal;
+}
+
+/****************************************************************************
+ Read a list of EA names and data from an incoming data buffer. Create an ea_list with them.
+****************************************************************************/
+
+static struct ea_list *read_ea_list(TALLOC_CTX *ctx, const char *pdata, size_t data_size)
+{
+       struct ea_list *ea_list_head = NULL;
+       size_t offset = 0;
+       size_t bytes_used = 0;
+
+       while (offset < data_size) {
+               struct ea_list *tmp;
+               struct ea_list *eal = read_ea_list_entry(ctx, pdata + offset, data_size - offset, &bytes_used);
+
+               if (!eal) {
+                       return NULL;
+               }
+
+               DLIST_ADD_END(ea_list_head, eal, tmp);
+               offset += bytes_used;
+       }
+
+       return ea_list_head;
+}
+
+/****************************************************************************
+ Count the total EA size needed.
+****************************************************************************/
+
+static size_t ea_list_size(struct ea_list *ealist)
+{
+       fstring dos_ea_name;
+       struct ea_list *listp;
+       size_t ret = 0;
+
+       for (listp = ealist; listp; listp = listp->next) {
+               push_ascii_fstring(dos_ea_name, listp->ea.name);
+               ret += 4 + strlen(dos_ea_name) + 1 + listp->ea.value.length;
+       }
+       /* Add on 4 for total length. */
+       if (ret) {
+               ret += 4;
+       }
+
+       return ret;
+}
+
+/****************************************************************************
+ Return a union of EA's from a file list and a list of names.
+ The TALLOC context for the two lists *MUST* be identical as we steal
+ memory from one list to add to another. JRA.
+****************************************************************************/
+
+static struct ea_list *ea_list_union(struct ea_list *name_list, struct ea_list *file_list, size_t *total_ea_len)
+{
+       struct ea_list *nlistp, *flistp;
+
+       for (nlistp = name_list; nlistp; nlistp = nlistp->next) {
+               for (flistp = file_list; flistp; flistp = flistp->next) {
+                       if (strequal(nlistp->ea.name, flistp->ea.name)) {
+                               break;
+                       }
+               }
+
+               if (flistp) {
+                       /* Copy the data from this entry. */
+                       nlistp->ea.flags = flistp->ea.flags;
+                       nlistp->ea.value = flistp->ea.value;
+               } else {
+                       /* Null entry. */
+                       nlistp->ea.flags = 0;
+                       ZERO_STRUCT(nlistp->ea.value);
+               }
+       }
+
+       *total_ea_len = ea_list_size(name_list);
+       return name_list;
+}
+
 /****************************************************************************
   Send the required number of replies back.
   We assume all fields other than the data fields are
@@ -215,9 +706,11 @@ static int send_trans2_replies(char *outbuf,
 ****************************************************************************/
 
 static int call_trans2open(connection_struct *conn, char *inbuf, char *outbuf, int bufsize,  
-                          char **pparams, int total_params, char **ppdata, int total_data)
+                               char **pparams, int total_params, char **ppdata, int total_data,
+                               unsigned int max_data_bytes)
 {
        char *params = *pparams;
+       char *pdata = *ppdata;
        int16 open_mode;
        int16 open_attr;
        BOOL oplock_request;
@@ -230,7 +723,6 @@ static int call_trans2open(connection_struct *conn, char *inbuf, char *outbuf, i
        int32 open_size;
        char *pname;
        pstring fname;
-       mode_t unixmode;
        SMB_OFF_T size=0;
        int fmode=0,mtime=0,rmode;
        SMB_INO_T inode = 0;
@@ -238,14 +730,17 @@ static int call_trans2open(connection_struct *conn, char *inbuf, char *outbuf, i
        int smb_action = 0;
        BOOL bad_path = False;
        files_struct *fsp;
+       TALLOC_CTX *ctx = NULL;
+       struct ea_list *ea_list = NULL;
        NTSTATUS status;
 
        /*
         * Ensure we have enough parameters to perform the operation.
         */
 
-       if (total_params < 29)
-               return(ERROR_DOS(ERRDOS,ERRinvalidparam));
+       if (total_params < 29) {
+               return ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+       }
 
        open_mode = SVAL(params, 2);
        open_attr = SVAL(params,6);
@@ -262,28 +757,62 @@ static int call_trans2open(connection_struct *conn, char *inbuf, char *outbuf, i
        if (IS_IPC(conn))
                return(ERROR_DOS(ERRSRV,ERRaccess));
 
-       srvstr_get_path(inbuf, fname, pname, sizeof(fname), -1, STR_TERMINATE, &status);
+       srvstr_get_path(inbuf, fname, pname, sizeof(fname), -1, STR_TERMINATE, &status, False);
        if (!NT_STATUS_IS_OK(status)) {
                return ERROR_NT(status);
        }
 
-       DEBUG(3,("trans2open %s mode=%d attr=%d ofun=%d size=%d\n",
+       DEBUG(3,("call_trans2open %s mode=%d attr=%d ofun=%d size=%d\n",
                fname,open_mode, open_attr, open_ofun, open_size));
 
        /* XXXX we need to handle passed times, sattr and flags */
 
        unix_convert(fname,conn,0,&bad_path,&sbuf);
+       if (bad_path) {
+               return ERROR_NT(NT_STATUS_OBJECT_PATH_NOT_FOUND);
+       }
     
        if (!check_name(fname,conn)) {
                return set_bad_path_error(errno, bad_path, outbuf, ERRDOS,ERRnoaccess);
        }
 
-       unixmode = unix_mode(conn,open_attr | aARCH, fname);
-      
-       fsp = open_file_shared(conn,fname,&sbuf,open_mode,open_ofun,unixmode,
+       /* Any data in this call is an EA list. */
+       if (total_data && !lp_ea_support(SNUM(conn))) {
+               return ERROR_NT(NT_STATUS_EAS_NOT_SUPPORTED);
+       }
+
+       if (total_data) {
+               if (total_data < 10) {
+                       return ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+               }
+
+               if (IVAL(pdata,0) > total_data) {
+                       DEBUG(10,("call_trans2open: bad total data size (%u) > %u\n",
+                               IVAL(pdata,0), (unsigned int)total_data));
+                       return ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+               }
+
+               ctx = talloc_init("TRANS2_OPEN_SET_EA");
+               if (!ctx) {
+                       return ERROR_NT(NT_STATUS_NO_MEMORY);
+               }
+               ea_list = read_ea_list(ctx, pdata + 4, total_data - 4);
+               if (!ea_list) {
+                       talloc_destroy(ctx);
+                       return ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+               }
+       }
+
+       fsp = open_file_shared(conn,fname,&sbuf,open_mode,open_ofun,(uint32)open_attr,
                oplock_request, &rmode,&smb_action);
       
        if (!fsp) {
+               talloc_destroy(ctx);
+               if (open_was_deferred(SVAL(inbuf,smb_mid))) {
+                       /* We have re-scheduled this call. */
+                       clear_cached_errors();
+                       return -1;
+               }
                return set_bad_path_error(errno, bad_path, outbuf, ERRDOS,ERRnoaccess);
        }
 
@@ -292,14 +821,25 @@ static int call_trans2open(connection_struct *conn, char *inbuf, char *outbuf, i
        mtime = sbuf.st_mtime;
        inode = sbuf.st_ino;
        if (fmode & aDIR) {
+               talloc_destroy(ctx);
                close_file(fsp,False);
                return(ERROR_DOS(ERRDOS,ERRnoaccess));
        }
 
+       if (total_data && smb_action == FILE_WAS_CREATED) {
+               status = set_ea(conn, fsp, fname, ea_list);
+               talloc_destroy(ctx);
+               if (!NT_STATUS_IS_OK(status)) {
+                       close_file(fsp,False);
+                       return ERROR_NT(status);
+               }
+       }
+
        /* Realloc the size of parameters and data we will return */
-       params = Realloc(*pparams, 28);
-       if( params == NULL )
-               return(ERROR_DOS(ERRDOS,ERRnomem));
+       params = SMB_REALLOC(*pparams, 28);
+       if( params == NULL ) {
+               return ERROR_NT(NT_STATUS_NO_MEMORY);
+       }
        *pparams = params;
 
        memset((char *)params,'\0',28);
@@ -382,32 +922,6 @@ static uint32 unix_filetype(mode_t mode)
        return UNIX_TYPE_UNKNOWN;
 }
 
-/****************************************************************************
- Return the major devicenumber for UNIX extensions.
-****************************************************************************/
-
-static uint32 unix_dev_major(SMB_DEV_T dev)
-{
-#if defined(HAVE_DEVICE_MAJOR_FN)
-       return (uint32)major(dev);
-#else
-       return (uint32)(dev >> 8);
-#endif
-}
-
-/****************************************************************************
- Return the minor devicenumber for UNIX extensions.
-****************************************************************************/
-
-static uint32 unix_dev_minor(SMB_DEV_T dev)
-{
-#if defined(HAVE_DEVICE_MINOR_FN)
-       return (uint32)minor(dev);
-#else
-       return (uint32)(dev & 0xff);
-#endif
-}
-
 /****************************************************************************
  Map wire perms onto standard UNIX permissions. Obey share restrictions.
 ****************************************************************************/
@@ -446,25 +960,10 @@ static mode_t unix_perms_from_wire( connection_struct *conn, SMB_STRUCT_STAT *ps
                /* Apply mode mask */
                ret &= lp_create_mask(SNUM(conn));
                /* Add in force bits */
-               ret |= lp_force_create_mode(SNUM(conn));
-       }
-
-       return ret;
-}
-
-/****************************************************************************
- Checks for SMB_TIME_NO_CHANGE and if not found calls interpret_long_date.
-****************************************************************************/
-
-time_t interpret_long_unix_date(char *p)
-{
-       DEBUG(1,("interpret_long_unix_date\n"));
-       if(IVAL(p,0) == SMB_TIME_NO_CHANGE_LO &&
-          IVAL(p,4) == SMB_TIME_NO_CHANGE_HI) {
-               return -1;
-       } else {
-               return interpret_long_date(p);
+               ret |= lp_force_create_mode(SNUM(conn));
        }
+
+       return ret;
 }
 
 /****************************************************************************
@@ -478,7 +977,7 @@ static BOOL get_lanman2_dir_entry(connection_struct *conn,
                                 BOOL dont_descend,char **ppdata, 
                                 char *base_data, int space_remaining, 
                                 BOOL *out_of_space, BOOL *got_exact_match,
-                                int *last_name_off)
+                                int *last_entry_off, struct ea_list *name_list, TALLOC_CTX *ea_ctx)
 {
        const char *dname;
        BOOL found = False;
@@ -488,13 +987,14 @@ static BOOL get_lanman2_dir_entry(connection_struct *conn,
        pstring fname;
        char *p, *q, *pdata = *ppdata;
        uint32 reskey=0;
-       int prev_dirpos=0;
+       long prev_dirpos=0;
        int mode=0;
        SMB_OFF_T file_size = 0;
        SMB_BIG_UINT allocation_size = 0;
        uint32 len;
        time_t mdate=0, adate=0, cdate=0;
        char *nameptr;
+       char *last_entry_ptr;
        BOOL was_8_3;
        int nt_extmode; /* Used for NT connections instead of mode */
        BOOL needslash = ( conn->dirpath[strlen(conn->dirpath) -1] != '/');
@@ -515,12 +1015,12 @@ static BOOL get_lanman2_dir_entry(connection_struct *conn,
        } else
                pstrcpy(mask, path_mask);
 
+
        while (!found) {
                BOOL got_match;
-
                /* Needed if we run out of space */
-               prev_dirpos = TellDir(conn->dirptr);
-               dname = ReadDirName(conn->dirptr);
+               long curr_dirpos = prev_dirpos = dptr_TellDir(conn->dirptr);
+               dname = dptr_ReadDirName(conn->dirptr,&curr_dirpos,&sbuf);
 
                /*
                 * Due to bugs in NT client redirectors we are not using
@@ -531,16 +1031,16 @@ static BOOL get_lanman2_dir_entry(connection_struct *conn,
 
                reskey = 0;
 
-               DEBUG(8,("get_lanman2_dir_entry:readdir on dirptr 0x%lx now at offset %d\n",
-                       (long)conn->dirptr,TellDir(conn->dirptr)));
+               DEBUG(8,("get_lanman2_dir_entry:readdir on dirptr 0x%lx now at offset %ld\n",
+                       (long)conn->dirptr,curr_dirpos));
       
                if (!dname) 
                        return(False);
 
                pstrcpy(fname,dname);      
 
-               if(!(got_match = *got_exact_match = exact_match(fname, mask, case_sensitive)))
-                       got_match = mask_match(fname, mask, case_sensitive);
+               if(!(got_match = *got_exact_match = exact_match(fname, mask, conn->case_sensitive)))
+                       got_match = mask_match(fname, mask, conn->case_sensitive);
 
                if(!got_match && !mangle_is_8_3(fname, False)) {
 
@@ -554,8 +1054,8 @@ static BOOL get_lanman2_dir_entry(connection_struct *conn,
                        pstring newname;
                        pstrcpy( newname, fname);
                        mangle_map( newname, True, False, SNUM(conn));
-                       if(!(got_match = *got_exact_match = exact_match(newname, mask, case_sensitive)))
-                               got_match = mask_match(newname, mask, case_sensitive);
+                       if(!(got_match = *got_exact_match = exact_match(newname, mask, conn->case_sensitive)))
+                               got_match = mask_match(newname, mask, conn->case_sensitive);
                }
 
                if(got_match) {
@@ -574,7 +1074,7 @@ static BOOL get_lanman2_dir_entry(connection_struct *conn,
                                                pathreal,strerror(errno)));
                                        continue;
                                }
-                       } else if (SMB_VFS_STAT(conn,pathreal,&sbuf) != 0) {
+                       } else if (!VALID_STAT(sbuf) && SMB_VFS_STAT(conn,pathreal,&sbuf) != 0) {
 
                                /* Needed to show the msdfs symlinks as 
                                 * directories */
@@ -597,13 +1097,13 @@ static BOOL get_lanman2_dir_entry(connection_struct *conn,
 
                        mode = dos_mode(conn,pathreal,&sbuf);
 
-                       if (!dir_check_ftype(conn,mode,&sbuf,dirtype)) {
+                       if (!dir_check_ftype(conn,mode,dirtype)) {
                                DEBUG(5,("[%s] attribs didn't match %x\n",fname,dirtype));
                                continue;
                        }
 
                        file_size = get_file_size(sbuf);
-                       allocation_size = get_allocation_size(NULL,&sbuf);
+                       allocation_size = get_allocation_size(conn,NULL,&sbuf);
                        mdate = sbuf.st_mtime;
                        adate = sbuf.st_atime;
                        cdate = get_create_time(&sbuf,lp_fake_dir_create_times(SNUM(conn)));
@@ -614,8 +1114,13 @@ static BOOL get_lanman2_dir_entry(connection_struct *conn,
                                adate &= ~1;
                        }
 
-                       if(mode & aDIR)
+                       if(mode & aDIR) {
+                               /* This is necessary, as otherwise the
+                                * desktop.ini file in this folder is
+                                * ignored */
+                               mode |= (lp_profile_acls(SNUM(conn)) ? aRONLY : 0);
                                file_size = 0;
+                       }
 
                        DEBUG(5,("get_lanman2_dir_entry found %s fname=%s\n",pathreal,fname));
          
@@ -626,12 +1131,13 @@ static BOOL get_lanman2_dir_entry(connection_struct *conn,
        mangle_map(fname,False,True,SNUM(conn));
 
        p = pdata;
-       nameptr = p;
+       last_entry_ptr = p;
 
        nt_extmode = mode ? mode : FILE_ATTRIBUTE_NORMAL;
 
        switch (info_level) {
-               case SMB_INFO_STANDARD:
+               case SMB_FIND_INFO_STANDARD:
+                       DEBUG(10,("get_lanman2_dir_entry: SMB_FIND_INFO_STANDARD\n"));
                        if(requires_resume_key) {
                                SIVAL(p,0,reskey);
                                p += 4;
@@ -646,14 +1152,24 @@ static BOOL get_lanman2_dir_entry(connection_struct *conn,
                        nameptr = p;
                        p += align_string(outbuf, p, 0);
                        len = srvstr_push(outbuf, p, fname, -1, STR_TERMINATE);
-                       if (SVAL(outbuf, smb_flg2) & FLAGS2_UNICODE_STRINGS)
-                               SCVAL(nameptr, -1, len-2);
-                       else
-                               SCVAL(nameptr, -1, len-1);
+                       if (SVAL(outbuf, smb_flg2) & FLAGS2_UNICODE_STRINGS) {
+                               if (len > 2) {
+                                       SCVAL(nameptr, -1, len - 2);
+                               } else {
+                                       SCVAL(nameptr, -1, 0);
+                               }
+                       } else {
+                               if (len > 1) {
+                                       SCVAL(nameptr, -1, len - 1);
+                               } else {
+                                       SCVAL(nameptr, -1, 0);
+                               }
+                       }
                        p += len;
                        break;
 
-               case SMB_INFO_QUERY_EA_SIZE:
+               case SMB_FIND_EA_SIZE:
+                       DEBUG(10,("get_lanman2_dir_entry: SMB_FIND_EA_SIZE\n"));
                        if(requires_resume_key) {
                                SIVAL(p,0,reskey);
                                p += 4;
@@ -664,18 +1180,90 @@ static BOOL get_lanman2_dir_entry(connection_struct *conn,
                        SIVAL(p,l2_cbFile,(uint32)file_size);
                        SIVAL(p,l2_cbFileAlloc,(uint32)allocation_size);
                        SSVAL(p,l2_attrFile,mode);
-                       SIVAL(p,l2_cbList,0); /* No extended attributes */
+                       {
+                               unsigned int ea_size = estimate_ea_size(conn, NULL, pathreal);
+                               SIVAL(p,l2_cbList,ea_size); /* Extended attributes */
+                       }
                        p += l2_achName;
-                       nameptr = p;
+                       nameptr = p - 1;
                        len = srvstr_push(outbuf, p, fname, -1, STR_TERMINATE | STR_NOALIGN);
-                       if (SVAL(outbuf, smb_flg2) & FLAGS2_UNICODE_STRINGS)
-                               SCVAL(nameptr, -1, len-2);
-                       else
-                               SCVAL(nameptr, -1, len-1);
+                       if (SVAL(outbuf, smb_flg2) & FLAGS2_UNICODE_STRINGS) {
+                               if (len > 2) {
+                                       len -= 2;
+                               } else {
+                                       len = 0;
+                               }
+                       } else {
+                               if (len > 1) {
+                                       len -= 1;
+                               } else {
+                                       len = 0;
+                               }
+                       }
+                       SCVAL(nameptr,0,len);
                        p += len;
+                       SCVAL(p,0,0); p += 1; /* Extra zero byte ? - why.. */
+                       break;
+
+               case SMB_FIND_EA_LIST:
+               {
+                       struct ea_list *file_list = NULL;
+                       size_t ea_len = 0;
+
+                       DEBUG(10,("get_lanman2_dir_entry: SMB_FIND_EA_LIST\n"));
+                       if (!name_list) {
+                               return False;
+                       }
+                       if(requires_resume_key) {
+                               SIVAL(p,0,reskey);
+                               p += 4;
+                       }
+                       put_dos_date2(p,l2_fdateCreation,cdate);
+                       put_dos_date2(p,l2_fdateLastAccess,adate);
+                       put_dos_date2(p,l2_fdateLastWrite,mdate);
+                       SIVAL(p,l2_cbFile,(uint32)file_size);
+                       SIVAL(p,l2_cbFileAlloc,(uint32)allocation_size);
+                       SSVAL(p,l2_attrFile,mode);
+                       p += l2_cbList; /* p now points to the EA area. */
+
+                       file_list = get_ea_list_from_file(ea_ctx, conn, NULL, pathreal, &ea_len);
+                       name_list = ea_list_union(name_list, file_list, &ea_len);
+
+                       /* We need to determine if this entry will fit in the space available. */
+                       /* Max string size is 255 bytes. */
+                       if (PTR_DIFF(p + 255 + ea_len,pdata) > space_remaining) {
+                               /* Move the dirptr back to prev_dirpos */
+                               dptr_SeekDir(conn->dirptr, prev_dirpos);
+                               *out_of_space = True;
+                               DEBUG(9,("get_lanman2_dir_entry: out of space\n"));
+                               return False; /* Not finished - just out of space */
+                       }
+
+                       /* Push the ea_data followed by the name. */
+                       p += fill_ea_buffer(ea_ctx, p, space_remaining, conn, name_list);
+                       nameptr = p;
+                       len = srvstr_push(outbuf, p + 1, fname, -1, STR_TERMINATE | STR_NOALIGN);
+                       if (SVAL(outbuf, smb_flg2) & FLAGS2_UNICODE_STRINGS) {
+                               if (len > 2) {
+                                       len -= 2;
+                               } else {
+                                       len = 0;
+                               }
+                       } else {
+                               if (len > 1) {
+                                       len -= 1;
+                               } else {
+                                       len = 0;
+                               }
+                       }
+                       SCVAL(nameptr,0,len);
+                       p += len + 1;
+                       SCVAL(p,0,0); p += 1; /* Extra zero byte ? - why.. */
                        break;
+               }
 
                case SMB_FIND_FILE_BOTH_DIRECTORY_INFO:
+                       DEBUG(10,("get_lanman2_dir_entry: SMB_FIND_FILE_BOTH_DIRECTORY_INFO\n"));
                        was_8_3 = mangle_is_8_3(fname, True);
                        p += 4;
                        SIVAL(p,0,reskey); p += 4;
@@ -683,12 +1271,15 @@ static BOOL get_lanman2_dir_entry(connection_struct *conn,
                        put_long_date(p,adate); p += 8;
                        put_long_date(p,mdate); p += 8;
                        put_long_date(p,mdate); p += 8;
-                       SOFF_T(p,0,file_size);
-                       SOFF_T(p,8,allocation_size);
-                       p += 16;
+                       SOFF_T(p,0,file_size); p += 8;
+                       SOFF_T(p,0,allocation_size); p += 8;
                        SIVAL(p,0,nt_extmode); p += 4;
-                       q = p; p += 4;
-                       SIVAL(p,0,0); p += 4;
+                       q = p; p += 4; /* q is placeholder for name length. */
+                       {
+                               unsigned int ea_size = estimate_ea_size(conn, NULL, pathreal);
+                               SIVAL(p,0,ea_size); /* Extended attributes */
+                               p += 4;
+                       }
                        /* Clear the short name buffer. This is
                         * IMPORTANT as not doing so will trigger
                         * a Win2k client bug. JRA.
@@ -716,20 +1307,19 @@ static BOOL get_lanman2_dir_entry(connection_struct *conn,
                        break;
 
                case SMB_FIND_FILE_DIRECTORY_INFO:
+                       DEBUG(10,("get_lanman2_dir_entry: SMB_FIND_FILE_DIRECTORY_INFO\n"));
                        p += 4;
                        SIVAL(p,0,reskey); p += 4;
                        put_long_date(p,cdate); p += 8;
                        put_long_date(p,adate); p += 8;
                        put_long_date(p,mdate); p += 8;
                        put_long_date(p,mdate); p += 8;
-                       SOFF_T(p,0,file_size);
-                       SOFF_T(p,8,allocation_size);
-                       p += 16;
+                       SOFF_T(p,0,file_size); p += 8;
+                       SOFF_T(p,0,allocation_size); p += 8;
                        SIVAL(p,0,nt_extmode); p += 4;
-                       p += 4;
-                       len = srvstr_push(outbuf, p, fname, -1, STR_TERMINATE_ASCII);
-                       SIVAL(p, -4, len);
-                       p += len;
+                       len = srvstr_push(outbuf, p + 4, fname, -1, STR_TERMINATE_ASCII);
+                       SIVAL(p,0,len);
+                       p += 4 + len;
                        len = PTR_DIFF(p, pdata);
                        len = (len + 3) & ~3;
                        SIVAL(pdata,0,len);
@@ -737,22 +1327,25 @@ static BOOL get_lanman2_dir_entry(connection_struct *conn,
                        break;
       
                case SMB_FIND_FILE_FULL_DIRECTORY_INFO:
+                       DEBUG(10,("get_lanman2_dir_entry: SMB_FIND_FILE_FULL_DIRECTORY_INFO\n"));
                        p += 4;
                        SIVAL(p,0,reskey); p += 4;
                        put_long_date(p,cdate); p += 8;
                        put_long_date(p,adate); p += 8;
                        put_long_date(p,mdate); p += 8;
                        put_long_date(p,mdate); p += 8;
-                       SOFF_T(p,0,file_size); 
-                       SOFF_T(p,8,allocation_size);
-                       p += 16;
-                       SIVAL(p,0,nt_extmode);
-                       p += 4;
-
-                       SIVAL(p,4,0); /* ea size */
-                       len = srvstr_push(outbuf, p+8, fname, -1, STR_TERMINATE_ASCII);
-                       SIVAL(p, 0, len);
-                       p += 8 + len;
+                       SOFF_T(p,0,file_size); p += 8;
+                       SOFF_T(p,0,allocation_size); p += 8;
+                       SIVAL(p,0,nt_extmode); p += 4;
+                       q = p; p += 4; /* q is placeholder for name length. */
+                       {
+                               unsigned int ea_size = estimate_ea_size(conn, NULL, pathreal);
+                               SIVAL(p,0,ea_size); /* Extended attributes */
+                               p +=4;
+                       }
+                       len = srvstr_push(outbuf, p, fname, -1, STR_TERMINATE_ASCII);
+                       SIVAL(q, 0, len);
+                       p += len;
 
                        len = PTR_DIFF(p, pdata);
                        len = (len + 3) & ~3;
@@ -761,6 +1354,7 @@ static BOOL get_lanman2_dir_entry(connection_struct *conn,
                        break;
 
                case SMB_FIND_FILE_NAMES_INFO:
+                       DEBUG(10,("get_lanman2_dir_entry: SMB_FIND_FILE_NAMES_INFO\n"));
                        p += 4;
                        SIVAL(p,0,reskey); p += 4;
                        p += 4;
@@ -775,29 +1369,37 @@ static BOOL get_lanman2_dir_entry(connection_struct *conn,
                        p = pdata + len;
                        break;
 
-               case SMB_FIND_FILE_LEVEL_261:
+               case SMB_FIND_ID_FULL_DIRECTORY_INFO:
+                       DEBUG(10,("get_lanman2_dir_entry: SMB_FIND_ID_FULL_DIRECTORY_INFO\n"));
                        p += 4;
                        SIVAL(p,0,reskey); p += 4;
                        put_long_date(p,cdate); p += 8;
                        put_long_date(p,adate); p += 8;
                        put_long_date(p,mdate); p += 8;
                        put_long_date(p,mdate); p += 8;
-                       SOFF_T(p,0,file_size); 
-                       SOFF_T(p,8,allocation_size);
-                       p += 16;
-                       SIVAL(p,0,nt_extmode);
-                       p += 4;
-                       len = srvstr_push(outbuf, p + 20, fname, -1, STR_TERMINATE_ASCII);
-                       SIVAL(p, 0, len);
-                       memset(p+4,'\0',16); /* EA size. Unknown 0 1 2 */
-                       p += 20 + len; /* Strlen, EA size. Unknown 0 1 2, string itself */
+                       SOFF_T(p,0,file_size); p += 8;
+                       SOFF_T(p,0,allocation_size); p += 8;
+                       SIVAL(p,0,nt_extmode); p += 4;
+                       q = p; p += 4; /* q is placeholder for name length. */
+                       {
+                               unsigned int ea_size = estimate_ea_size(conn, NULL, pathreal);
+                               SIVAL(p,0,ea_size); /* Extended attributes */
+                               p +=4;
+                       }
+                       SIVAL(p,0,0); p += 4; /* Unknown - reserved ? */
+                       SIVAL(p,0,sbuf.st_dev); p += 4;
+                       SIVAL(p,0,sbuf.st_ino); p += 4;
+                       len = srvstr_push(outbuf, p, fname, -1, STR_TERMINATE_ASCII);
+                       SIVAL(q, 0, len);
+                       p += len; 
                        len = PTR_DIFF(p, pdata);
                        len = (len + 3) & ~3;
                        SIVAL(pdata,0,len);
                        p = pdata + len;
                        break;
 
-               case SMB_FIND_FILE_LEVEL_262:
+               case SMB_FIND_ID_BOTH_DIRECTORY_INFO:
+                       DEBUG(10,("get_lanman2_dir_entry: SMB_FIND_ID_BOTH_DIRECTORY_INFO\n"));
                        was_8_3 = mangle_is_8_3(fname, True);
                        p += 4;
                        SIVAL(p,0,reskey); p += 4;
@@ -805,12 +1407,15 @@ static BOOL get_lanman2_dir_entry(connection_struct *conn,
                        put_long_date(p,adate); p += 8;
                        put_long_date(p,mdate); p += 8;
                        put_long_date(p,mdate); p += 8;
-                       SOFF_T(p,0,file_size);
-                       SOFF_T(p,8,allocation_size);
-                       p += 16;
+                       SOFF_T(p,0,file_size); p += 8;
+                       SOFF_T(p,0,allocation_size); p += 8;
                        SIVAL(p,0,nt_extmode); p += 4;
-                       q = p; p += 4;
-                       SIVAL(p,0,0); p += 4;
+                       q = p; p += 4; /* q is placeholder for name length */
+                       {
+                               unsigned int ea_size = estimate_ea_size(conn, NULL, pathreal);
+                               SIVAL(p,0,ea_size); /* Extended attributes */
+                               p +=4;
+                       }
                        /* Clear the short name buffer. This is
                         * IMPORTANT as not doing so will trigger
                         * a Win2k client bug. JRA.
@@ -827,9 +1432,10 @@ static BOOL get_lanman2_dir_entry(connection_struct *conn,
                                SSVAL(p,0,0);
                                *(p+2) = 0;
                        }
-                       p += 2 + 24;
-                       memset(p, '\0', 10); /* 2 4 byte unknowns plus a zero reserved. */
-                       p += 10;
+                       p += 26;
+                       SSVAL(p,0,0); p += 2; /* Reserved ? */
+                       SIVAL(p,0,sbuf.st_dev); p += 4;
+                       SIVAL(p,0,sbuf.st_ino); p += 4;
                        len = srvstr_push(outbuf, p, fname, -1, STR_TERMINATE_ASCII);
                        SIVAL(q,0,len);
                        p += len;
@@ -842,6 +1448,7 @@ static BOOL get_lanman2_dir_entry(connection_struct *conn,
                /* CIFS UNIX Extension. */
 
                case SMB_FIND_FILE_UNIX:
+                       DEBUG(10,("get_lanman2_dir_entry: SMB_FIND_FILE_UNIX\n"));
                        p+= 4;
                        SIVAL(p,0,reskey); p+= 4;    /* Used for continuing search. */
 
@@ -849,10 +1456,10 @@ static BOOL get_lanman2_dir_entry(connection_struct *conn,
                        SOFF_T(p,0,get_file_size(sbuf));             /* File size 64 Bit */
                        p+= 8;
 
-                       SOFF_T(p,0,get_allocation_size(NULL,&sbuf)); /* Number of bytes used on disk - 64 Bit */
+                       SOFF_T(p,0,get_allocation_size(conn,NULL,&sbuf)); /* Number of bytes used on disk - 64 Bit */
                        p+= 8;
 
-                       put_long_date(p,sbuf.st_ctime);       /* Creation Time 64 Bit */
+                       put_long_date(p,sbuf.st_ctime);       /* Inode change Time 64 Bit */
                        put_long_date(p+8,sbuf.st_atime);     /* Last access time 64 Bit */
                        put_long_date(p+16,sbuf.st_mtime);    /* Last modification time 64 Bit */
                        p+= 24;
@@ -905,14 +1512,14 @@ static BOOL get_lanman2_dir_entry(connection_struct *conn,
 
        if (PTR_DIFF(p,pdata) > space_remaining) {
                /* Move the dirptr back to prev_dirpos */
-               SeekDir(conn->dirptr, prev_dirpos);
+               dptr_SeekDir(conn->dirptr, prev_dirpos);
                *out_of_space = True;
                DEBUG(9,("get_lanman2_dir_entry: out of space\n"));
                return False; /* Not finished - just out of space */
        }
 
-       /* Setup the last_filename pointer, as an offset from base_data */
-       *last_name_off = PTR_DIFF(nameptr,base_data);
+       /* Setup the last entry pointer, as an offset from base_data */
+       *last_entry_off = PTR_DIFF(last_entry_ptr,base_data);
        /* Advance the data pointer to the next slot */
        *ppdata = p;
 
@@ -924,26 +1531,27 @@ static BOOL get_lanman2_dir_entry(connection_struct *conn,
 ****************************************************************************/
 
 static int call_trans2findfirst(connection_struct *conn, char *inbuf, char *outbuf, int bufsize,  
-                               char **pparams, int total_params, char **ppdata, int total_data)
+                               char **pparams, int total_params, char **ppdata, int total_data,
+                               unsigned int max_data_bytes)
 {
        /* We must be careful here that we don't return more than the
                allowed number of data bytes. If this means returning fewer than
                maxentries then so be it. We assume that the redirector has
                enough room for the fixed number of parameter bytes it has
                requested. */
-       uint32 max_data_bytes = SVAL(inbuf, smb_mdrcnt);
        char *params = *pparams;
        char *pdata = *ppdata;
        int dirtype = SVAL(params,0);
        int maxentries = SVAL(params,2);
-       BOOL close_after_first = BITSETW(params+4,0);
-       BOOL close_if_end = BITSETW(params+4,1);
-       BOOL requires_resume_key = BITSETW(params+4,2);
+       uint16 findfirst_flags = SVAL(params,4);
+       BOOL close_after_first = (findfirst_flags & FLAG_TRANS2_FIND_CLOSE);
+       BOOL close_if_end = (findfirst_flags & FLAG_TRANS2_FIND_CLOSE_IF_END);
+       BOOL requires_resume_key = (findfirst_flags & FLAG_TRANS2_FIND_REQUIRE_RESUME);
        int info_level = SVAL(params,6);
        pstring directory;
        pstring mask;
-       char *p, *wcard;
-       int last_name_off=0;
+       char *p;
+       int last_entry_off=0;
        int dptr_num = -1;
        int numentries = 0;
        int i;
@@ -953,27 +1561,36 @@ static int call_trans2findfirst(connection_struct *conn, char *inbuf, char *outb
        int space_remaining;
        BOOL bad_path = False;
        SMB_STRUCT_STAT sbuf;
+       TALLOC_CTX *ea_ctx = NULL;
+       struct ea_list *ea_list = NULL;
        NTSTATUS ntstatus = NT_STATUS_OK;
 
-       if (total_params < 12)
-               return(ERROR_DOS(ERRDOS,ERRinvalidparam));
+       if (total_params < 12) {
+               return ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+       }
 
        *directory = *mask = 0;
 
        DEBUG(3,("call_trans2findfirst: dirtype = %d, maxentries = %d, close_after_first=%d, \
-close_if_end = %d requires_resume_key = %d level = %d, max_data_bytes = %d\n",
+close_if_end = %d requires_resume_key = %d level = 0x%x, max_data_bytes = %d\n",
                dirtype, maxentries, close_after_first, close_if_end, requires_resume_key,
                info_level, max_data_bytes));
-  
+
+       if (!maxentries) {
+               /* W2K3 seems to treat zero as 1. */
+               maxentries = 1;
+       }
        switch (info_level) {
-               case SMB_INFO_STANDARD:
-               case SMB_INFO_QUERY_EA_SIZE:
+               case SMB_FIND_INFO_STANDARD:
+               case SMB_FIND_EA_SIZE:
+               case SMB_FIND_EA_LIST:
                case SMB_FIND_FILE_DIRECTORY_INFO:
                case SMB_FIND_FILE_FULL_DIRECTORY_INFO:
                case SMB_FIND_FILE_NAMES_INFO:
                case SMB_FIND_FILE_BOTH_DIRECTORY_INFO:
-               case SMB_FIND_FILE_LEVEL_261:
-               case SMB_FIND_FILE_LEVEL_262:
+               case SMB_FIND_ID_FULL_DIRECTORY_INFO:
+               case SMB_FIND_ID_BOTH_DIRECTORY_INFO:
                        break;
                case SMB_FIND_FILE_UNIX:
                        if (!lp_unix_extensions())
@@ -983,14 +1600,17 @@ close_if_end = %d requires_resume_key = %d level = %d, max_data_bytes = %d\n",
                        return(ERROR_DOS(ERRDOS,ERRunknownlevel));
        }
 
-       srvstr_get_path(inbuf, directory, params+12, sizeof(directory), -1, STR_TERMINATE, &ntstatus);
+       srvstr_get_path(inbuf, directory, params+12, sizeof(directory), -1, STR_TERMINATE, &ntstatus, True);
        if (!NT_STATUS_IS_OK(ntstatus)) {
                return ERROR_NT(ntstatus);
        }
 
-       RESOLVE_FINDFIRST_DFSPATH(directory, conn, inbuf, outbuf);
+       RESOLVE_DFSPATH_WCARD(directory, conn, inbuf, outbuf);
 
        unix_convert(directory,conn,0,&bad_path,&sbuf);
+       if (bad_path) {
+               return ERROR_NT(NT_STATUS_OBJECT_PATH_NOT_FOUND);
+       }
        if(!check_name(directory,conn)) {
                return set_bad_path_error(errno, bad_path, outbuf, ERRDOS,ERRbadpath);
        }
@@ -1010,41 +1630,75 @@ close_if_end = %d requires_resume_key = %d level = %d, max_data_bytes = %d\n",
 
        DEBUG(5,("dir=%s, mask = %s\n",directory, mask));
 
-       pdata = Realloc(*ppdata, max_data_bytes + 1024);
-       if( pdata == NULL )
-               return(ERROR_DOS(ERRDOS,ERRnomem));
+       if (info_level == SMB_FIND_EA_LIST) {
+               uint32 ea_size;
+
+               if (total_data < 4) {
+                       return ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+               }
+
+               ea_size = IVAL(pdata,0);
+               if (ea_size != total_data) {
+                       DEBUG(4,("call_trans2findfirst: Rejecting EA request with incorrect \
+total_data=%u (should be %u)\n", (unsigned int)total_data, (unsigned int)IVAL(pdata,0) ));
+                       return ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+               }
+
+               if (!lp_ea_support(SNUM(conn))) {
+                       return ERROR_DOS(ERRDOS,ERReasnotsupported);
+               }
+                                                                                                                                                        
+               if ((ea_ctx = talloc_init("findnext_ea_list")) == NULL) {
+                       return ERROR_NT(NT_STATUS_NO_MEMORY);
+               }
+                                                                                                                                                        
+               /* Pull out the list of names. */
+               ea_list = read_ea_name_list(ea_ctx, pdata + 4, ea_size - 4);
+               if (!ea_list) {
+                       talloc_destroy(ea_ctx);
+                       return ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+               }
+       }
+
+       pdata = SMB_REALLOC(*ppdata, max_data_bytes + DIR_ENTRY_SAFETY_MARGIN);
+       if( pdata == NULL ) {
+               talloc_destroy(ea_ctx);
+               return ERROR_NT(NT_STATUS_NO_MEMORY);
+       }
 
        *ppdata = pdata;
-       memset((char *)pdata,'\0',max_data_bytes + 1024);
+       memset((char *)pdata,'\0',max_data_bytes + DIR_ENTRY_SAFETY_MARGIN);
 
        /* Realloc the params space */
-       params = Realloc(*pparams, 10);
-       if (params == NULL)
-               return ERROR_DOS(ERRDOS,ERRnomem);
+       params = SMB_REALLOC(*pparams, 10);
+       if (params == NULL) {
+               talloc_destroy(ea_ctx);
+               return ERROR_NT(NT_STATUS_NO_MEMORY);
+       }
        *pparams = params;
 
        dptr_num = dptr_create(conn,directory, False, True ,SVAL(inbuf,smb_pid));
-       if (dptr_num < 0)
+       if (dptr_num < 0) {
+               talloc_destroy(ea_ctx);
                return(UNIXERROR(ERRDOS,ERRbadfile));
+       }
 
        /* Save the wildcard match and attribs we are using on this directory - 
                needed as lanman2 assumes these are being saved between calls */
 
-       if(!(wcard = strdup(mask))) {
+       if (!dptr_set_wcard_and_attributes(dptr_num, mask, dirtype)) {
                dptr_close(&dptr_num);
-               return ERROR_DOS(ERRDOS,ERRnomem);
+               talloc_destroy(ea_ctx);
+               return ERROR_NT(NT_STATUS_NO_MEMORY);
        }
 
-       dptr_set_wcard(dptr_num, wcard);
-       dptr_set_attr(dptr_num, dirtype);
-
-       DEBUG(4,("dptr_num is %d, wcard = %s, attr = %d\n",dptr_num, wcard, dirtype));
+       DEBUG(4,("dptr_num is %d, wcard = %s, attr = %d\n",dptr_num, mask, dirtype));
 
        /* We don't need to check for VOL here as this is returned by 
                a different TRANS2 call. */
   
        DEBUG(8,("dirpath=<%s> dontdescend=<%s>\n", conn->dirpath,lp_dontdescend(SNUM(conn))));
-       if (in_list(conn->dirpath,lp_dontdescend(SNUM(conn)),case_sensitive))
+       if (in_list(conn->dirpath,lp_dontdescend(SNUM(conn)),conn->case_sensitive))
                dont_descend = True;
     
        p = pdata;
@@ -1065,7 +1719,7 @@ close_if_end = %d requires_resume_key = %d level = %d, max_data_bytes = %d\n",
                                        mask,dirtype,info_level,
                                        requires_resume_key,dont_descend,
                                        &p,pdata,space_remaining, &out_of_space, &got_exact_match,
-                                       &last_name_off);
+                                       &last_entry_off, ea_list, ea_ctx);
                }
 
                if (finished && out_of_space)
@@ -1087,6 +1741,8 @@ close_if_end = %d requires_resume_key = %d level = %d, max_data_bytes = %d\n",
                space_remaining = max_data_bytes - PTR_DIFF(p,pdata);
        }
   
+       talloc_destroy(ea_ctx);
+
        /* Check if we can close the dirptr */
        if(close_after_first || (finished && close_if_end)) {
                DEBUG(5,("call_trans2findfirst - (2) closing dptr_num %d\n", dptr_num));
@@ -1095,12 +1751,18 @@ close_if_end = %d requires_resume_key = %d level = %d, max_data_bytes = %d\n",
 
        /* 
         * If there are no matching entries we must return ERRDOS/ERRbadfile - 
-        * from observation of NT.
+        * from observation of NT. NB. This changes to ERRDOS,ERRnofiles if
+        * the protocol level is less than NT1. Tested with smbclient. JRA.
+        * This should fix the OS/2 client bug #2335.
         */
 
        if(numentries == 0) {
                dptr_close(&dptr_num);
-               return ERROR_DOS(ERRDOS,ERRbadfile);
+               if (Protocol < PROTOCOL_NT1) {
+                       return ERROR_DOS(ERRDOS,ERRnofiles);
+               } else {
+                       return ERROR_BOTH(NT_STATUS_NO_SUCH_FILE,ERRDOS,ERRbadfile);
+               }
        }
 
        /* At this point pdata points to numentries directory entries. */
@@ -1110,7 +1772,7 @@ close_if_end = %d requires_resume_key = %d level = %d, max_data_bytes = %d\n",
        SSVAL(params,2,numentries);
        SSVAL(params,4,finished);
        SSVAL(params,6,0); /* Never an EA error */
-       SSVAL(params,8,last_name_off);
+       SSVAL(params,8,last_entry_off);
 
        send_trans2_replies( outbuf, bufsize, params, 10, pdata, PTR_DIFF(p,pdata));
 
@@ -1140,45 +1802,59 @@ close_if_end = %d requires_resume_key = %d level = %d, max_data_bytes = %d\n",
 ****************************************************************************/
 
 static int call_trans2findnext(connection_struct *conn, char *inbuf, char *outbuf, int length, int bufsize,
-                                       char **pparams, int total_params, char **ppdata, int total_data)
+                                       char **pparams, int total_params, char **ppdata, int total_data,
+                                       unsigned int max_data_bytes)
 {
        /* We must be careful here that we don't return more than the
                allowed number of data bytes. If this means returning fewer than
                maxentries then so be it. We assume that the redirector has
                enough room for the fixed number of parameter bytes it has
                requested. */
-       int max_data_bytes = SVAL(inbuf, smb_mdrcnt);
        char *params = *pparams;
        char *pdata = *ppdata;
        int dptr_num = SVAL(params,0);
        int maxentries = SVAL(params,2);
        uint16 info_level = SVAL(params,4);
        uint32 resume_key = IVAL(params,6);
-       BOOL close_after_request = BITSETW(params+10,0);
-       BOOL close_if_end = BITSETW(params+10,1);
-       BOOL requires_resume_key = BITSETW(params+10,2);
-       BOOL continue_bit = BITSETW(params+10,3);
+       uint16 findnext_flags = SVAL(params,10);
+       BOOL close_after_request = (findnext_flags & FLAG_TRANS2_FIND_CLOSE);
+       BOOL close_if_end = (findnext_flags & FLAG_TRANS2_FIND_CLOSE_IF_END);
+       BOOL requires_resume_key = (findnext_flags & FLAG_TRANS2_FIND_REQUIRE_RESUME);
+       BOOL continue_bit = (findnext_flags & FLAG_TRANS2_FIND_CONTINUE);
        pstring resume_name;
        pstring mask;
        pstring directory;
        char *p;
        uint16 dirtype;
        int numentries = 0;
-       int i, last_name_off=0;
+       int i, last_entry_off=0;
        BOOL finished = False;
        BOOL dont_descend = False;
        BOOL out_of_space = False;
        int space_remaining;
+       TALLOC_CTX *ea_ctx = NULL;
+       struct ea_list *ea_list = NULL;
        NTSTATUS ntstatus = NT_STATUS_OK;
 
-       if (total_params < 12)
-               return(ERROR_DOS(ERRDOS,ERRinvalidparam));
+       if (total_params < 12) {
+               return ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+       }
 
        *mask = *directory = *resume_name = 0;
 
-       srvstr_get_path(inbuf, resume_name, params+12, sizeof(resume_name), -1, STR_TERMINATE, &ntstatus);
+       srvstr_get_path(inbuf, resume_name, params+12, sizeof(resume_name), -1, STR_TERMINATE, &ntstatus, True);
        if (!NT_STATUS_IS_OK(ntstatus)) {
-               return ERROR_NT(ntstatus);
+               /* Win9x or OS/2 can send a resume name of ".." or ".". This will cause the parser to
+                  complain (it thinks we're asking for the directory above the shared
+                  path or an invalid name). Catch this as the resume name is only compared, never used in
+                  a file access. JRA. */
+               if (NT_STATUS_EQUAL(ntstatus,NT_STATUS_OBJECT_PATH_SYNTAX_BAD)) {
+                       pstrcpy(resume_name, "..");
+               } else if (NT_STATUS_EQUAL(ntstatus,NT_STATUS_OBJECT_NAME_INVALID)) {
+                       pstrcpy(resume_name, ".");
+               } else {
+                       return ERROR_NT(ntstatus);
+               }
        }
 
        DEBUG(3,("call_trans2findnext: dirhandle = %d, max_data_bytes = %d, maxentries = %d, \
@@ -1187,9 +1863,15 @@ resume_key = %d resume name = %s continue=%d level = %d\n",
                dptr_num, max_data_bytes, maxentries, close_after_request, close_if_end, 
                requires_resume_key, resume_key, resume_name, continue_bit, info_level));
 
+       if (!maxentries) {
+               /* W2K3 seems to treat zero as 1. */
+               maxentries = 1;
+       }
+
        switch (info_level) {
-               case SMB_INFO_STANDARD:
-               case SMB_INFO_QUERY_EA_SIZE:
+               case SMB_FIND_INFO_STANDARD:
+               case SMB_FIND_EA_SIZE:
+               case SMB_FIND_EA_LIST:
                case SMB_FIND_FILE_DIRECTORY_INFO:
                case SMB_FIND_FILE_FULL_DIRECTORY_INFO:
                case SMB_FIND_FILE_NAMES_INFO:
@@ -1203,29 +1885,66 @@ resume_key = %d resume name = %s continue=%d level = %d\n",
                        return ERROR_DOS(ERRDOS,ERRunknownlevel);
        }
 
-       pdata = Realloc( *ppdata, max_data_bytes + 1024);
-       if(pdata == NULL)
-               return ERROR_DOS(ERRDOS,ERRnomem);
+       if (info_level == SMB_FIND_EA_LIST) {
+               uint32 ea_size;
+
+               if (total_data < 4) {
+                       return ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+               }
+
+               ea_size = IVAL(pdata,0);
+               if (ea_size != total_data) {
+                       DEBUG(4,("call_trans2findnext: Rejecting EA request with incorrect \
+total_data=%u (should be %u)\n", (unsigned int)total_data, (unsigned int)IVAL(pdata,0) ));
+                       return ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+               }
+                                                                                                                                                     
+               if (!lp_ea_support(SNUM(conn))) {
+                       return ERROR_DOS(ERRDOS,ERReasnotsupported);
+               }
+                                                                                                                                                     
+               if ((ea_ctx = talloc_init("findnext_ea_list")) == NULL) {
+                       return ERROR_NT(NT_STATUS_NO_MEMORY);
+               }
+
+               /* Pull out the list of names. */
+               ea_list = read_ea_name_list(ea_ctx, pdata + 4, ea_size - 4);
+               if (!ea_list) {
+                       talloc_destroy(ea_ctx);
+                       return ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+               }
+       }
+
+       pdata = SMB_REALLOC( *ppdata, max_data_bytes + DIR_ENTRY_SAFETY_MARGIN);
+       if(pdata == NULL) {
+               talloc_destroy(ea_ctx);
+               return ERROR_NT(NT_STATUS_NO_MEMORY);
+       }
 
        *ppdata = pdata;
-       memset((char *)pdata,'\0',max_data_bytes + 1024);
+       memset((char *)pdata,'\0',max_data_bytes + DIR_ENTRY_SAFETY_MARGIN);
 
        /* Realloc the params space */
-       params = Realloc(*pparams, 6*SIZEOFWORD);
-       if( params == NULL )
-               return ERROR_DOS(ERRDOS,ERRnomem);
+       params = SMB_REALLOC(*pparams, 6*SIZEOFWORD);
+       if( params == NULL ) {
+               talloc_destroy(ea_ctx);
+               return ERROR_NT(NT_STATUS_NO_MEMORY);
+       }
 
        *pparams = params;
 
        /* Check that the dptr is valid */
-       if(!(conn->dirptr = dptr_fetch_lanman2(dptr_num)))
+       if(!(conn->dirptr = dptr_fetch_lanman2(dptr_num))) {
+               talloc_destroy(ea_ctx);
                return ERROR_DOS(ERRDOS,ERRnofiles);
+       }
 
        string_set(&conn->dirpath,dptr_path(dptr_num));
 
        /* Get the wildcard mask from the dptr */
        if((p = dptr_wcard(dptr_num))== NULL) {
                DEBUG(2,("dptr_num %d has no wildcard\n", dptr_num));
+               talloc_destroy(ea_ctx);
                return ERROR_DOS(ERRDOS,ERRnofiles);
        }
 
@@ -1235,16 +1954,16 @@ resume_key = %d resume name = %s continue=%d level = %d\n",
        /* Get the attr mask from the dptr */
        dirtype = dptr_attr(dptr_num);
 
-       DEBUG(3,("dptr_num is %d, mask = %s, attr = %x, dirptr=(0x%lX,%d)\n",
+       DEBUG(3,("dptr_num is %d, mask = %s, attr = %x, dirptr=(0x%lX,%ld)\n",
                dptr_num, mask, dirtype, 
                (long)conn->dirptr,
-               TellDir(conn->dirptr)));
+               dptr_TellDir(conn->dirptr)));
 
        /* We don't need to check for VOL here as this is returned by 
                a different TRANS2 call. */
 
        DEBUG(8,("dirpath=<%s> dontdescend=<%s>\n",conn->dirpath,lp_dontdescend(SNUM(conn))));
-       if (in_list(conn->dirpath,lp_dontdescend(SNUM(conn)),case_sensitive))
+       if (in_list(conn->dirpath,lp_dontdescend(SNUM(conn)),conn->case_sensitive))
                dont_descend = True;
     
        p = pdata;
@@ -1256,82 +1975,31 @@ resume_key = %d resume name = %s continue=%d level = %d\n",
         * depend on the last file name instead.
         */
 
-       if(requires_resume_key && *resume_name && !continue_bit) {
+       if(*resume_name && !continue_bit) {
+               SMB_STRUCT_STAT st;
 
+               long current_pos = 0;
                /*
-                * Fix for NT redirector problem triggered by resume key indexes
-                * changing between directory scans. We now return a resume key of 0
-                * and instead look for the filename to continue from (also given
-                * to us by NT/95/smbfs/smbclient). If no other scans have been done between the
-                * findfirst/findnext (as is usual) then the directory pointer
-                * should already be at the correct place. Check this by scanning
-                * backwards looking for an exact (ie. case sensitive) filename match. 
-                * If we get to the beginning of the directory and haven't found it then scan
-                * forwards again looking for a match. JRA.
+                * Remember, mangle_map is called by
+                * get_lanman2_dir_entry(), so the resume name
+                * could be mangled. Ensure we check the unmangled name.
                 */
 
-               int current_pos, start_pos;
-               const char *dname = NULL;
-               pstring dname_pstring;
-               void *dirptr = conn->dirptr;
-               start_pos = TellDir(dirptr);
-               for(current_pos = start_pos; current_pos >= 0; current_pos--) {
-                       DEBUG(7,("call_trans2findnext: seeking to pos %d\n", current_pos));
-
-                       SeekDir(dirptr, current_pos);
-                       dname = ReadDirName(dirptr);
-                       if (dname) {
-                               /*
-                                * Remember, mangle_map is called by
-                                * get_lanman2_dir_entry(), so the resume name
-                                * could be mangled. Ensure we do the same
-                                * here.
-                                */
-                               
-                               /* make sure we get a copy that mangle_map can modify */
-
-                               pstrcpy(dname_pstring, dname);
-                               mangle_map( dname_pstring, False, True, SNUM(conn));
-                               
-                               if(strcsequal( resume_name, dname_pstring)) {
-                                       SeekDir(dirptr, current_pos+1);
-                                       DEBUG(7,("call_trans2findnext: got match at pos %d\n", current_pos+1 ));
-                                       break;
-                               }
-                       }
+               if (mangle_is_mangled(resume_name)) {
+                       mangle_check_cache(resume_name, sizeof(resume_name)-1);
                }
 
                /*
-                * Scan forward from start if not found going backwards.
+                * Fix for NT redirector problem triggered by resume key indexes
+                * changing between directory scans. We now return a resume key of 0
+                * and instead look for the filename to continue from (also given
+                * to us by NT/95/smbfs/smbclient). If no other scans have been done between the
+                * findfirst/findnext (as is usual) then the directory pointer
+                * should already be at the correct place.
                 */
 
-               if(current_pos < 0) {
-                       DEBUG(7,("call_trans2findnext: notfound: seeking to pos %d\n", start_pos));
-                       SeekDir(dirptr, start_pos);
-                       for(current_pos = start_pos; (dname = ReadDirName(dirptr)) != NULL; SeekDir(dirptr,++current_pos)) {
-
-                               /*
-                                * Remember, mangle_map is called by
-                                * get_lanman2_dir_entry(), so the resume name
-                                * could be mangled. Ensure we do the same
-                                * here.
-                                */
-
-                               if(dname) {
-                                       /* make sure we get a copy that mangle_map can modify */
-                                       
-                                       pstrcpy(dname_pstring, dname);
-                                       mangle_map(dname_pstring, False, True, SNUM(conn));
-
-                                       if(strcsequal( resume_name, dname_pstring)) {
-                                               SeekDir(dirptr, current_pos+1);
-                                               DEBUG(7,("call_trans2findnext: got match at pos %d\n", current_pos+1 ));
-                                               break;
-                                       }
-                               }
-                       } /* end for */
-               } /* end if current_pos */
-       } /* end if requires_resume_key && !continue_bit */
+               finished = !dptr_SearchDir(conn->dirptr, resume_name, &current_pos, &st);
+       } /* end if resume_name && !continue_bit */
 
        for (i=0;(i<(int)maxentries) && !finished && !out_of_space ;i++) {
                BOOL got_exact_match = False;
@@ -1347,7 +2015,7 @@ resume_key = %d resume name = %s continue=%d level = %d\n",
                                                mask,dirtype,info_level,
                                                requires_resume_key,dont_descend,
                                                &p,pdata,space_remaining, &out_of_space, &got_exact_match,
-                                               &last_name_off);
+                                               &last_entry_off, ea_list, ea_ctx);
                }
 
                if (finished && out_of_space)
@@ -1369,6 +2037,8 @@ resume_key = %d resume name = %s continue=%d level = %d\n",
                space_remaining = max_data_bytes - PTR_DIFF(p,pdata);
        }
   
+       talloc_destroy(ea_ctx);
+
        /* Check if we can close the dirptr */
        if(close_after_request || (finished && close_if_end)) {
                DEBUG(5,("call_trans2findnext: closing dptr_num = %d\n", dptr_num));
@@ -1379,7 +2049,7 @@ resume_key = %d resume name = %s continue=%d level = %d\n",
        SSVAL(params,0,numentries);
        SSVAL(params,2,finished);
        SSVAL(params,4,0); /* Never an EA error */
-       SSVAL(params,6,last_name_off);
+       SSVAL(params,6,last_entry_off);
 
        send_trans2_replies( outbuf, bufsize, params, 8, pdata, PTR_DIFF(p,pdata));
 
@@ -1397,11 +2067,10 @@ resume_key = %d resume name = %s continue=%d level = %d\n",
  Reply to a TRANS2_QFSINFO (query filesystem info).
 ****************************************************************************/
 
-static int call_trans2qfsinfo(connection_struct *conn, char *inbuf, char *outbuf, 
-                       int length, int bufsize,
-                       char **pparams, int total_params, char **ppdata, int total_data)
+static int call_trans2qfsinfo(connection_struct *conn, char *inbuf, char *outbuf, int length, int bufsize,
+                                       char **pparams, int total_params, char **ppdata, int total_data,
+                                       unsigned int max_data_bytes)
 {
-       int max_data_bytes = SVAL(inbuf, smb_mdrcnt);
        char *pdata = *ppdata;
        char *params = *pparams;
        uint16 info_level = SVAL(params,0);
@@ -1419,19 +2088,23 @@ static int call_trans2qfsinfo(connection_struct *conn, char *inbuf, char *outbuf
                return ERROR_DOS(ERRSRV,ERRinvdevice);
        }
 
-       pdata = Realloc(*ppdata, max_data_bytes + 1024);
-       if ( pdata == NULL )
-               return ERROR_DOS(ERRDOS,ERRnomem);
+       pdata = SMB_REALLOC(*ppdata, max_data_bytes + DIR_ENTRY_SAFETY_MARGIN);
+       if ( pdata == NULL ) {
+               return ERROR_NT(NT_STATUS_NO_MEMORY);
+       }
 
        *ppdata = pdata;
-       memset((char *)pdata,'\0',max_data_bytes + 1024);
+       memset((char *)pdata,'\0',max_data_bytes + DIR_ENTRY_SAFETY_MARGIN);
 
        switch (info_level) {
                case SMB_INFO_ALLOCATION:
                {
                        SMB_BIG_UINT dfree,dsize,bsize,block_size,sectors_per_unit,bytes_per_sector;
                        data_len = 18;
-                       SMB_VFS_DISK_FREE(conn,".",False,&bsize,&dfree,&dsize); 
+                       if (SMB_VFS_DISK_FREE(conn,".",False,&bsize,&dfree,&dsize) == (SMB_BIG_UINT)-1) {
+                               return(UNIXERROR(ERRHRD,ERRgeneral));
+                       }
+
                        block_size = lp_block_size(snum);
                        if (bsize < block_size) {
                                SMB_BIG_UINT factor = block_size/bsize;
@@ -1466,8 +2139,14 @@ cBytesSector=%u, cUnitTotal=%u, cUnitAvail=%d\n", (unsigned int)st.st_dev, (unsi
                         * Add volume serial number - hash of a combination of
                         * the called hostname and the service name.
                         */
-                       SIVAL(pdata,0,str_checksum(lp_servicename(snum)) ^ (str_checksum(local_machine)<<16) );
-                       len = srvstr_push(outbuf, pdata+l2_vol_szVolLabel, vname, -1, STR_NOALIGN);
+                       SIVAL(pdata,0,str_checksum(lp_servicename(snum)) ^ (str_checksum(get_local_machine_name())<<16) );
+                       /*
+                        * Win2k3 and previous mess this up by sending a name length
+                        * one byte short. I believe only older clients (OS/2 Win9x) use
+                        * this call so try fixing this by adding a terminating null to
+                        * the pushed string. The change here was adding the STR_TERMINATE. JRA.
+                        */
+                       len = srvstr_push(outbuf, pdata+l2_vol_szVolLabel, vname, -1, STR_NOALIGN|STR_TERMINATE);
                        SCVAL(pdata,l2_vol_cch,len);
                        data_len = l2_vol_szVolLabel + len;
                        DEBUG(5,("call_trans2qfsinfo : time = %x, namelen = %d, name = %s\n",
@@ -1509,7 +2188,7 @@ cBytesSector=%u, cUnitTotal=%u, cUnitAvail=%d\n", (unsigned int)st.st_dev, (unsi
                         * the called hostname and the service name.
                         */
                        SIVAL(pdata,8,str_checksum(lp_servicename(snum)) ^ 
-                               (str_checksum(local_machine)<<16));
+                               (str_checksum(get_local_machine_name())<<16));
 
                        len = srvstr_push(outbuf, pdata+18, vname, -1, STR_UNICODE);
                        SIVAL(pdata,12,len);
@@ -1523,7 +2202,9 @@ cBytesSector=%u, cUnitTotal=%u, cUnitAvail=%d\n", (unsigned int)st.st_dev, (unsi
                {
                        SMB_BIG_UINT dfree,dsize,bsize,block_size,sectors_per_unit,bytes_per_sector;
                        data_len = 24;
-                       SMB_VFS_DISK_FREE(conn,".",False,&bsize,&dfree,&dsize);
+                       if (SMB_VFS_DISK_FREE(conn,".",False,&bsize,&dfree,&dsize) == (SMB_BIG_UINT)-1) {
+                               return(UNIXERROR(ERRHRD,ERRgeneral));
+                       }
                        block_size = lp_block_size(snum);
                        if (bsize < block_size) {
                                SMB_BIG_UINT factor = block_size/bsize;
@@ -1553,7 +2234,9 @@ cBytesSector=%u, cUnitTotal=%u, cUnitAvail=%d\n", (unsigned int)bsize, (unsigned
                {
                        SMB_BIG_UINT dfree,dsize,bsize,block_size,sectors_per_unit,bytes_per_sector;
                        data_len = 32;
-                       SMB_VFS_DISK_FREE(conn,".",False,&bsize,&dfree,&dsize);
+                       if (SMB_VFS_DISK_FREE(conn,".",False,&bsize,&dfree,&dsize) == (SMB_BIG_UINT)-1) {
+                               return(UNIXERROR(ERRHRD,ERRgeneral));
+                       }
                        block_size = lp_block_size(snum);
                        if (bsize < block_size) {
                                SMB_BIG_UINT factor = block_size/bsize;
@@ -1623,7 +2306,7 @@ cBytesSector=%u, cUnitTotal=%u, cUnitAvail=%d\n", (unsigned int)bsize, (unsigned
                        fsp.fd = -1;
                        
                        /* access check */
-                       if (conn->admin_user != True) {
+                       if (current_user.uid != 0) {
                                DEBUG(0,("set_user_quota: access_denied service [%s] user [%s]\n",
                                        lp_servicename(SNUM(conn)),conn->user));
                                return ERROR_DOS(ERRDOS,ERRnoaccess);
@@ -1674,7 +2357,7 @@ cBytesSector=%u, cUnitTotal=%u, cUnitAvail=%d\n", (unsigned int)bsize, (unsigned
                        data_len = 12;
                        SSVAL(pdata,0,CIFS_UNIX_MAJOR_VERSION);
                        SSVAL(pdata,2,CIFS_UNIX_MINOR_VERSION);
-                       SBIG_UINT(pdata,4,((SMB_BIG_UINT)0)); /* No capabilities for now... */
+                       SBIG_UINT(pdata,4,((SMB_BIG_UINT)CIFS_UNIX_POSIX_ACLS_CAP)); /* We have POSIX ACLs. */
                        break;
 
                case SMB_MAC_QUERY_FS_INFO:
@@ -1705,9 +2388,9 @@ cBytesSector=%u, cUnitTotal=%u, cUnitAvail=%d\n", (unsigned int)bsize, (unsigned
  Reply to a TRANS2_SETFSINFO (set filesystem info).
 ****************************************************************************/
 
-static int call_trans2setfsinfo(connection_struct *conn,
-                               char *inbuf, char *outbuf, int length, int bufsize,
-                               char **pparams, int total_params, char **ppdata, int total_data)
+static int call_trans2setfsinfo(connection_struct *conn, char *inbuf, char *outbuf, int length, int bufsize,
+                                       char **pparams, int total_params, char **ppdata, int total_data,
+                                       unsigned int max_data_bytes)
 {
        char *pdata = *ppdata;
        char *params = *pparams;
@@ -1721,7 +2404,7 @@ static int call_trans2setfsinfo(connection_struct *conn,
        DEBUG(10,("call_trans2setfsinfo: SET_FS_QUOTA: for service [%s]\n",lp_servicename(SNUM(conn))));
 
        /* access check */
-       if ((conn->admin_user != True)||!CAN_WRITE(conn)) {
+       if ((current_user.uid != 0)||!CAN_WRITE(conn)) {
                DEBUG(0,("set_user_quota: access_denied service [%s] user [%s]\n",
                        lp_servicename(SNUM(conn)),conn->user));
                return ERROR_DOS(ERRSRV,ERRaccess);
@@ -1731,7 +2414,7 @@ static int call_trans2setfsinfo(connection_struct *conn,
        if (total_params < 4) {
                DEBUG(0,("call_trans2setfsinfo: requires total_params(%d) >= 4 bytes!\n",
                        total_params));
-               return ERROR_DOS(ERRDOS,ERRinvalidparam);
+               return ERROR_NT(NT_STATUS_INVALID_PARAMETER);
        }
 
        fsp = file_fsp(params,0);
@@ -1815,8 +2498,8 @@ static int call_trans2setfsinfo(connection_struct *conn,
 #endif /* HAVE_SYS_QUOTAS */
 
 /****************************************************************************
*  Utility function to set bad path error.
- ****************************************************************************/
+ Utility function to set bad path error.
+****************************************************************************/
 
 int set_bad_path_error(int err, BOOL bad_path, char *outbuf, int def_class, uint32 def_code)
 {
@@ -1833,17 +2516,132 @@ int set_bad_path_error(int err, BOOL bad_path, char *outbuf, int def_class, uint
        return UNIXERROR(def_class,def_code);
 }
 
+#if defined(HAVE_POSIX_ACLS)
+/****************************************************************************
+ Utility function to count the number of entries in a POSIX acl.
+****************************************************************************/
+
+static unsigned int count_acl_entries(connection_struct *conn, SMB_ACL_T posix_acl)
+{
+       unsigned int ace_count = 0;
+       int entry_id = SMB_ACL_FIRST_ENTRY;
+       SMB_ACL_ENTRY_T entry;
+
+       while ( posix_acl && (SMB_VFS_SYS_ACL_GET_ENTRY(conn, posix_acl, entry_id, &entry) == 1)) {
+               /* get_next... */
+               if (entry_id == SMB_ACL_FIRST_ENTRY) {
+                       entry_id = SMB_ACL_NEXT_ENTRY;
+               }
+               ace_count++;
+       }
+       return ace_count;
+}
+
+/****************************************************************************
+ Utility function to marshall a POSIX acl into wire format.
+****************************************************************************/
+
+static BOOL marshall_posix_acl(connection_struct *conn, char *pdata, SMB_STRUCT_STAT *pst, SMB_ACL_T posix_acl)
+{
+       int entry_id = SMB_ACL_FIRST_ENTRY;
+       SMB_ACL_ENTRY_T entry;
+
+       while ( posix_acl && (SMB_VFS_SYS_ACL_GET_ENTRY(conn, posix_acl, entry_id, &entry) == 1)) {
+               SMB_ACL_TAG_T tagtype;
+               SMB_ACL_PERMSET_T permset;
+               unsigned char perms = 0;
+               unsigned int own_grp;
+
+               /* get_next... */
+               if (entry_id == SMB_ACL_FIRST_ENTRY) {
+                       entry_id = SMB_ACL_NEXT_ENTRY;
+               }
+
+               if (SMB_VFS_SYS_ACL_GET_TAG_TYPE(conn, entry, &tagtype) == -1) {
+                       DEBUG(0,("marshall_posix_acl: SMB_VFS_SYS_ACL_GET_TAG_TYPE failed.\n"));
+                       return False;
+               }
+
+               if (SMB_VFS_SYS_ACL_GET_PERMSET(conn, entry, &permset) == -1) {
+                       DEBUG(0,("marshall_posix_acl: SMB_VFS_SYS_ACL_GET_PERMSET failed.\n"));
+                       return False;
+               }
+
+               perms |= (SMB_VFS_SYS_ACL_GET_PERM(conn, permset, SMB_ACL_READ) ? SMB_POSIX_ACL_READ : 0);
+               perms |= (SMB_VFS_SYS_ACL_GET_PERM(conn, permset, SMB_ACL_WRITE) ? SMB_POSIX_ACL_WRITE : 0);
+               perms |= (SMB_VFS_SYS_ACL_GET_PERM(conn, permset, SMB_ACL_EXECUTE) ? SMB_POSIX_ACL_EXECUTE : 0);
+
+               SCVAL(pdata,1,perms);
+
+               switch (tagtype) {
+                       case SMB_ACL_USER_OBJ:
+                               SCVAL(pdata,0,SMB_POSIX_ACL_USER_OBJ);
+                               own_grp = (unsigned int)pst->st_uid;
+                               SIVAL(pdata,2,own_grp);
+                               SIVAL(pdata,6,0);
+                               break;
+                       case SMB_ACL_USER:
+                               {
+                                       uid_t *puid = (uid_t *)SMB_VFS_SYS_ACL_GET_QUALIFIER(conn, entry);
+                                       if (!puid) {
+                                               DEBUG(0,("marshall_posix_acl: SMB_VFS_SYS_ACL_GET_QUALIFIER failed.\n"));
+                                       }
+                                       own_grp = (unsigned int)*puid;
+                                       SMB_VFS_SYS_ACL_FREE_QUALIFIER(conn, (void *)puid,tagtype);
+                                       SCVAL(pdata,0,SMB_POSIX_ACL_USER);
+                                       SIVAL(pdata,2,own_grp);
+                                       SIVAL(pdata,6,0);
+                                       break;
+                               }
+                       case SMB_ACL_GROUP_OBJ:
+                               SCVAL(pdata,0,SMB_POSIX_ACL_GROUP_OBJ);
+                               own_grp = (unsigned int)pst->st_gid;
+                               SIVAL(pdata,2,own_grp);
+                               SIVAL(pdata,6,0);
+                               break;
+                       case SMB_ACL_GROUP:
+                               {
+                                       gid_t *pgid= (gid_t *)SMB_VFS_SYS_ACL_GET_QUALIFIER(conn, entry);
+                                       if (!pgid) {
+                                               DEBUG(0,("marshall_posix_acl: SMB_VFS_SYS_ACL_GET_QUALIFIER failed.\n"));
+                                       }
+                                       own_grp = (unsigned int)*pgid;
+                                       SMB_VFS_SYS_ACL_FREE_QUALIFIER(conn, (void *)pgid,tagtype);
+                                       SCVAL(pdata,0,SMB_POSIX_ACL_GROUP);
+                                       SIVAL(pdata,2,own_grp);
+                                       SIVAL(pdata,6,0);
+                                       break;
+                               }
+                       case SMB_ACL_MASK:
+                               SCVAL(pdata,0,SMB_POSIX_ACL_MASK);
+                               SIVAL(pdata,2,0xFFFFFFFF);
+                               SIVAL(pdata,6,0xFFFFFFFF);
+                               break;
+                       case SMB_ACL_OTHER:
+                               SCVAL(pdata,0,SMB_POSIX_ACL_OTHER);
+                               SIVAL(pdata,2,0xFFFFFFFF);
+                               SIVAL(pdata,6,0xFFFFFFFF);
+                               break;
+                       default:
+                               DEBUG(0,("marshall_posix_acl: unknown tagtype.\n"));
+                               return False;
+               }
+               pdata += SMB_POSIX_ACL_ENTRY_SIZE;
+       }
+
+       return True;
+}
+#endif
+
 /****************************************************************************
  Reply to a TRANS2_QFILEPATHINFO or TRANSACT2_QFILEINFO (query file info by
  file name or file id).
 ****************************************************************************/
 
-static int call_trans2qfilepathinfo(connection_struct *conn,
-                                   char *inbuf, char *outbuf, int length, 
-                                   int bufsize,
-                                       char **pparams, int total_params, char **ppdata, int total_data)
+static int call_trans2qfilepathinfo(connection_struct *conn, char *inbuf, char *outbuf, int length, int bufsize,
+                                       char **pparams, int total_params, char **ppdata, int total_data,
+                                       unsigned int max_data_bytes)
 {
-       int max_data_bytes = SVAL(inbuf, smb_mdrcnt);
        char *params = *pparams;
        char *pdata = *ppdata;
        uint16 tran_call = SVAL(inbuf, smb_setup0);
@@ -1851,7 +2649,7 @@ static int call_trans2qfilepathinfo(connection_struct *conn,
        int mode=0;
        SMB_OFF_T file_size=0;
        SMB_BIG_UINT allocation_size=0;
-       unsigned int data_size;
+       unsigned int data_size = 0;
        unsigned int param_size = 2;
        SMB_STRUCT_STAT sbuf;
        pstring fname, dos_fname;
@@ -1864,14 +2662,19 @@ static int call_trans2qfilepathinfo(connection_struct *conn,
        int len;
        time_t c_time;
        files_struct *fsp = NULL;
+       TALLOC_CTX *ea_ctx = NULL;
+       struct ea_list *ea_list = NULL;
        uint32 desired_access = 0x12019F; /* Default - GENERIC_EXECUTE mapping from Windows */
 
        if (!params)
                return ERROR_NT(NT_STATUS_INVALID_PARAMETER);
 
+       ZERO_STRUCT(sbuf);
+
        if (tran_call == TRANSACT2_QFILEINFO) {
-               if (total_params < 4)
-                       return(ERROR_DOS(ERRDOS,ERRinvalidparam));
+               if (total_params < 4) {
+                       return ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+               }
 
                fsp = file_fsp(params,0);
                info_level = SVAL(params,2);
@@ -1884,11 +2687,7 @@ static int call_trans2qfilepathinfo(connection_struct *conn,
                         */
                                                
                        pstrcpy(fname, fsp->fsp_name);
-                       unix_convert(fname,conn,0,&bad_path,&sbuf);
-                       if (!check_name(fname,conn)) {
-                               DEBUG(3,("call_trans2qfilepathinfo: fileinfo of %s failed for fake_file(%s)\n",fname,strerror(errno)));
-                               return set_bad_path_error(errno, bad_path, outbuf, ERRDOS,ERRbadpath);
-                       }
+                       /* We know this name is ok, it's already passed the checks. */
                        
                } else if(fsp && (fsp->is_directory || fsp->fd == -1)) {
                        /*
@@ -1896,12 +2695,8 @@ static int call_trans2qfilepathinfo(connection_struct *conn,
                         * handle (returned from an NT SMB). NT5.0 seems
                         * to do this call. JRA.
                         */
+                       /* We know this name is ok, it's already passed the checks. */
                        pstrcpy(fname, fsp->fsp_name);
-                       unix_convert(fname,conn,0,&bad_path,&sbuf);
-                       if (!check_name(fname,conn)) {
-                               DEBUG(3,("call_trans2qfilepathinfo: fileinfo of %s failed (%s)\n",fname,strerror(errno)));
-                               return set_bad_path_error(errno, bad_path, outbuf, ERRDOS,ERRbadpath);
-                       }
                  
                        if (INFO_LEVEL_IS_UNIX(info_level)) {
                                /* Always do lstat for UNIX calls. */
@@ -1909,12 +2704,12 @@ static int call_trans2qfilepathinfo(connection_struct *conn,
                                        DEBUG(3,("call_trans2qfilepathinfo: SMB_VFS_LSTAT of %s failed (%s)\n",fname,strerror(errno)));
                                        return set_bad_path_error(errno, bad_path, outbuf, ERRDOS,ERRbadpath);
                                }
-                       } else if (!VALID_STAT(sbuf) && SMB_VFS_STAT(conn,fname,&sbuf)) {
+                       } else if (SMB_VFS_STAT(conn,fname,&sbuf)) {
                                DEBUG(3,("call_trans2qfilepathinfo: SMB_VFS_STAT of %s failed (%s)\n",fname,strerror(errno)));
                                return set_bad_path_error(errno, bad_path, outbuf, ERRDOS,ERRbadpath);
                        }
 
-                       delete_pending = fsp->directory_delete_on_close;
+                       delete_pending = fsp->is_directory ? fsp->directory_delete_on_close : 0;
                } else {
                        /*
                         * Original code - this is an open file.
@@ -1934,14 +2729,15 @@ static int call_trans2qfilepathinfo(connection_struct *conn,
                NTSTATUS status = NT_STATUS_OK;
 
                /* qpathinfo */
-               if (total_params < 6)
-                       return(ERROR_DOS(ERRDOS,ERRinvalidparam));
+               if (total_params < 6) {
+                       return ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+               }
 
                info_level = SVAL(params,0);
 
                DEBUG(3,("call_trans2qfilepathinfo: TRANSACT2_QPATHINFO: level = %d\n", info_level));
 
-               srvstr_get_path(inbuf, fname, &params[6], sizeof(fname), -1, STR_TERMINATE, &status);
+               srvstr_get_path(inbuf, fname, &params[6], sizeof(fname), -1, STR_TERMINATE, &status, False);
                if (!NT_STATUS_IS_OK(status)) {
                        return ERROR_NT(status);
                }
@@ -1949,6 +2745,9 @@ static int call_trans2qfilepathinfo(connection_struct *conn,
                RESOLVE_DFSPATH(fname, conn, inbuf, outbuf);
 
                unix_convert(fname,conn,0,&bad_path,&sbuf);
+               if (bad_path) {
+                       return ERROR_NT(NT_STATUS_OBJECT_PATH_NOT_FOUND);
+               }
                if (!check_name(fname,conn)) {
                        DEBUG(3,("call_trans2qfilepathinfo: fileinfo of %s failed (%s)\n",fname,strerror(errno)));
                        return set_bad_path_error(errno, bad_path, outbuf, ERRDOS,ERRbadpath);
@@ -1984,35 +2783,82 @@ static int call_trans2qfilepathinfo(connection_struct *conn,
 
        fullpathname = fname;
        file_size = get_file_size(sbuf);
-       allocation_size = get_allocation_size(fsp,&sbuf);
-       if (mode & aDIR)
+       allocation_size = get_allocation_size(conn,fsp,&sbuf);
+       if (mode & aDIR) {
+               /* This is necessary, as otherwise the desktop.ini file in
+                * this folder is ignored */
+               mode |= (lp_profile_acls(SNUM(conn)) ? aRONLY : 0);
                file_size = 0;
+       }
+
+       /* Pull any EA list from the data portion. */
+       if (info_level == SMB_INFO_QUERY_EAS_FROM_LIST) {
+               uint32 ea_size;
+
+               if (total_data < 4) {
+                       return ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+               }
+               ea_size = IVAL(pdata,0);
 
-       params = Realloc(*pparams,2);
-       if (params == NULL)
-         return ERROR_DOS(ERRDOS,ERRnomem);
+               if (total_data > 0 && ea_size != total_data) {
+                       DEBUG(4,("call_trans2qfilepathinfo: Rejecting EA request with incorrect \
+total_data=%u (should be %u)\n", (unsigned int)total_data, (unsigned int)IVAL(pdata,0) ));
+                       return ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+               }
+
+               if (!lp_ea_support(SNUM(conn))) {
+                       return ERROR_DOS(ERRDOS,ERReasnotsupported);
+               }
+
+               if ((ea_ctx = talloc_init("ea_list")) == NULL) {
+                       return ERROR_NT(NT_STATUS_NO_MEMORY);
+               }
+
+               /* Pull out the list of names. */
+               ea_list = read_ea_name_list(ea_ctx, pdata + 4, ea_size - 4);
+               if (!ea_list) {
+                       talloc_destroy(ea_ctx);
+                       return ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+               }
+       }
+
+       params = SMB_REALLOC(*pparams,2);
+       if (params == NULL) {
+               talloc_destroy(ea_ctx);
+               return ERROR_NT(NT_STATUS_NO_MEMORY);
+       }
        *pparams = params;
        memset((char *)params,'\0',2);
-       data_size = max_data_bytes + 1024;
-       pdata = Realloc(*ppdata, data_size); 
-       if ( pdata == NULL )
-               return ERROR_DOS(ERRDOS,ERRnomem);
-       *ppdata = pdata;
-
-       if (total_data > 0 && IVAL(pdata,0) == total_data) {
-               /* uggh, EAs for OS2 */
-               DEBUG(4,("Rejecting EA request with total_data=%d\n",total_data));
-               return ERROR_DOS(ERRDOS,ERReasnotsupported);
+       data_size = max_data_bytes + DIR_ENTRY_SAFETY_MARGIN;
+       pdata = SMB_REALLOC(*ppdata, data_size); 
+       if ( pdata == NULL ) {
+               talloc_destroy(ea_ctx);
+               return ERROR_NT(NT_STATUS_NO_MEMORY);
        }
+       *ppdata = pdata;
 
        memset((char *)pdata,'\0',data_size);
 
        c_time = get_create_time(&sbuf,lp_fake_dir_create_times(SNUM(conn)));
 
+       if (fsp) {
+               if (fsp->pending_modtime) {
+                       /* the pending modtime overrides the current modtime */
+                       sbuf.st_mtime = fsp->pending_modtime;
+               }
+       } else {
+               /* Do we have this path open ? */
+               files_struct *fsp1 = file_find_di_first(sbuf.st_dev, sbuf.st_ino);
+               if (fsp1 && fsp1->pending_modtime) {
+                       /* the pending modtime overrides the current modtime */
+                       sbuf.st_mtime = fsp1->pending_modtime;
+               }
+       }
+
        if (lp_dos_filetime_resolution(SNUM(conn))) {
                c_time &= ~1;
                sbuf.st_atime &= ~1;
-               sbuf.st_mtime &= ~1;
+               sbuf.st_ctime &= ~1;
                sbuf.st_mtime &= ~1;
        }
 
@@ -2027,18 +2873,33 @@ static int call_trans2qfilepathinfo(connection_struct *conn,
 
        switch (info_level) {
                case SMB_INFO_STANDARD:
+                       DEBUG(10,("call_trans2qfilepathinfo: SMB_INFO_STANDARD\n"));
+                       data_size = 22;
+                       put_dos_date2(pdata,l1_fdateCreation,c_time);
+                       put_dos_date2(pdata,l1_fdateLastAccess,sbuf.st_atime);
+                       put_dos_date2(pdata,l1_fdateLastWrite,sbuf.st_mtime); /* write time */
+                       SIVAL(pdata,l1_cbFile,(uint32)file_size);
+                       SIVAL(pdata,l1_cbFileAlloc,(uint32)allocation_size);
+                       SSVAL(pdata,l1_attrFile,mode);
+                       break;
+
                case SMB_INFO_QUERY_EA_SIZE:
-                       data_size = (info_level==1?22:26);
+               {
+                       unsigned int ea_size = estimate_ea_size(conn, fsp, fname);
+                       DEBUG(10,("call_trans2qfilepathinfo: SMB_INFO_QUERY_EA_SIZE\n"));
+                       data_size = 26;
                        put_dos_date2(pdata,l1_fdateCreation,c_time);
                        put_dos_date2(pdata,l1_fdateLastAccess,sbuf.st_atime);
                        put_dos_date2(pdata,l1_fdateLastWrite,sbuf.st_mtime); /* write time */
                        SIVAL(pdata,l1_cbFile,(uint32)file_size);
                        SIVAL(pdata,l1_cbFileAlloc,(uint32)allocation_size);
                        SSVAL(pdata,l1_attrFile,mode);
-                       SIVAL(pdata,l1_attrFile+2,0); /* this is what win2003 does */
+                       SIVAL(pdata,l1_attrFile+2,ea_size);
                        break;
+               }
 
                case SMB_INFO_IS_NAME_VALID:
+                       DEBUG(10,("call_trans2qfilepathinfo: SMB_INFO_IS_NAME_VALID\n"));
                        if (tran_call == TRANSACT2_QFILEINFO) {
                                /* os/2 needs this ? really ?*/      
                                return ERROR_DOS(ERRDOS,ERRbadfunc); 
@@ -2048,26 +2909,60 @@ static int call_trans2qfilepathinfo(connection_struct *conn,
                        break;
                        
                case SMB_INFO_QUERY_EAS_FROM_LIST:
-                       data_size = 24;
-                       put_dos_date2(pdata,0,c_time);
-                       put_dos_date2(pdata,4,sbuf.st_atime);
-                       put_dos_date2(pdata,8,sbuf.st_mtime);
-                       SIVAL(pdata,12,(uint32)file_size);
-                       SIVAL(pdata,16,(uint32)allocation_size);
-                       SIVAL(pdata,20,mode);
+               {
+                       size_t total_ea_len = 0;
+                       struct ea_list *ea_file_list = NULL;
+
+                       DEBUG(10,("call_trans2qfilepathinfo: SMB_INFO_QUERY_EAS_FROM_LIST\n"));
+
+                       ea_file_list = get_ea_list_from_file(ea_ctx, conn, fsp, fname, &total_ea_len);
+                       ea_list = ea_list_union(ea_list, ea_file_list, &total_ea_len);
+
+                       if (!ea_list || (total_ea_len > data_size)) {
+                               talloc_destroy(ea_ctx);
+                               data_size = 4;
+                               SIVAL(pdata,0,4);   /* EA List Length must be set to 4 if no EA's. */
+                               break;
+                       }
+
+                       data_size = fill_ea_buffer(ea_ctx, pdata, data_size, conn, ea_list);
+                       talloc_destroy(ea_ctx);
                        break;
+               }
 
                case SMB_INFO_QUERY_ALL_EAS:
-                       data_size = 4;
-                       SIVAL(pdata,0,0); /* ea size */
+               {
+                       /* We have data_size bytes to put EA's into. */
+                       size_t total_ea_len = 0;
+
+                       DEBUG(10,("call_trans2qfilepathinfo: SMB_INFO_QUERY_ALL_EAS\n"));
+
+                       ea_ctx = talloc_init("ea_ctx");
+                       if (!ea_ctx) {
+                               return ERROR_NT(NT_STATUS_NO_MEMORY);
+                       }
+
+                       ea_list = get_ea_list_from_file(ea_ctx, conn, fsp, fname, &total_ea_len);
+                       if (!ea_list || (total_ea_len > data_size)) {
+                               talloc_destroy(ea_ctx);
+                               data_size = 4;
+                               SIVAL(pdata,0,4);   /* EA List Length must be set to 4 if no EA's. */
+                               break;
+                       }
+
+                       data_size = fill_ea_buffer(ea_ctx, pdata, data_size, conn, ea_list);
+                       talloc_destroy(ea_ctx);
                        break;
+               }
 
                case SMB_FILE_BASIC_INFORMATION:
                case SMB_QUERY_FILE_BASIC_INFO:
 
-                       if (info_level == SMB_QUERY_FILE_BASIC_INFO)
+                       if (info_level == SMB_QUERY_FILE_BASIC_INFO) {
+                               DEBUG(10,("call_trans2qfilepathinfo: SMB_QUERY_FILE_BASIC_INFO\n"));
                                data_size = 36; /* w95 returns 40 bytes not 36 - why ?. */
-                       else {
+                       } else {
+                               DEBUG(10,("call_trans2qfilepathinfo: SMB_FILE_BASIC_INFORMATION\n"));
                                data_size = 40;
                                SIVAL(pdata,36,0);
                        }
@@ -2092,6 +2987,7 @@ static int call_trans2qfilepathinfo(connection_struct *conn,
                case SMB_FILE_STANDARD_INFORMATION:
                case SMB_QUERY_FILE_STANDARD_INFO:
 
+                       DEBUG(10,("call_trans2qfilepathinfo: SMB_FILE_STANDARD_INFORMATION\n"));
                        data_size = 24;
                        SOFF_T(pdata,0,allocation_size);
                        SOFF_T(pdata,8,file_size);
@@ -2105,8 +3001,13 @@ static int call_trans2qfilepathinfo(connection_struct *conn,
 
                case SMB_FILE_EA_INFORMATION:
                case SMB_QUERY_FILE_EA_INFO:
+               {
+                       unsigned int ea_size = estimate_ea_size(conn, fsp, fname);
+                       DEBUG(10,("call_trans2qfilepathinfo: SMB_FILE_EA_INFORMATION\n"));
                        data_size = 4;
+                       SIVAL(pdata,0,ea_size);
                        break;
+               }
 
                /* Get the 8.3 name - used if NT SMB was negotiated. */
                case SMB_QUERY_FILE_ALT_NAME_INFO:
@@ -2114,6 +3015,7 @@ static int call_trans2qfilepathinfo(connection_struct *conn,
                {
                        pstring short_name;
 
+                       DEBUG(10,("call_trans2qfilepathinfo: SMB_FILE_ALTERNATE_NAME_INFORMATION\n"));
                        pstrcpy(short_name,base_name);
                        /* Mangle if not already 8.3 */
                        if(!mangle_is_8_3(short_name, True)) {
@@ -2130,24 +3032,30 @@ static int call_trans2qfilepathinfo(connection_struct *conn,
                          this must be *exactly* right for ACLs on mapped drives to work
                         */
                        len = srvstr_push(outbuf, pdata+4, dos_fname, -1, STR_UNICODE);
+                       DEBUG(10,("call_trans2qfilepathinfo: SMB_QUERY_FILE_NAME_INFO\n"));
                        data_size = 4 + len;
                        SIVAL(pdata,0,len);
                        break;
 
                case SMB_FILE_ALLOCATION_INFORMATION:
                case SMB_QUERY_FILE_ALLOCATION_INFO:
+                       DEBUG(10,("call_trans2qfilepathinfo: SMB_FILE_ALLOCATION_INFORMATION\n"));
                        data_size = 8;
                        SOFF_T(pdata,0,allocation_size);
                        break;
 
                case SMB_FILE_END_OF_FILE_INFORMATION:
                case SMB_QUERY_FILE_END_OF_FILEINFO:
+                       DEBUG(10,("call_trans2qfilepathinfo: SMB_FILE_END_OF_FILE_INFORMATION\n"));
                        data_size = 8;
                        SOFF_T(pdata,0,file_size);
                        break;
 
                case SMB_QUERY_FILE_ALL_INFO:
                case SMB_FILE_ALL_INFORMATION:
+               {
+                       unsigned int ea_size = estimate_ea_size(conn, fsp, fname);
+                       DEBUG(10,("call_trans2qfilepathinfo: SMB_FILE_ALL_INFORMATION\n"));
                        put_long_date(pdata,c_time);
                        put_long_date(pdata+8,sbuf.st_atime);
                        put_long_date(pdata+16,sbuf.st_mtime); /* write time */
@@ -2163,13 +3071,14 @@ static int call_trans2qfilepathinfo(connection_struct *conn,
                        SCVAL(pdata,20,delete_pending);
                        SCVAL(pdata,21,(mode&aDIR)?1:0);
                        pdata += 24;
+                       SIVAL(pdata,0,ea_size);
                        pdata += 4; /* EA info */
                        len = srvstr_push(outbuf, pdata+4, dos_fname, -1, STR_UNICODE);
                        SIVAL(pdata,0,len);
                        pdata += 4 + len;
                        data_size = PTR_DIFF(pdata,(*ppdata));
                        break;
-
+               }
                case SMB_FILE_INTERNAL_INFORMATION:
                        /* This should be an index number - looks like
                           dev/ino to me :-) 
@@ -2177,12 +3086,14 @@ static int call_trans2qfilepathinfo(connection_struct *conn,
                           I think this causes us to fail the IFSKIT
                           BasicFileInformationTest. -tpot */
 
+                       DEBUG(10,("call_trans2qfilepathinfo: SMB_FILE_INTERNAL_INFORMATION\n"));
                        SIVAL(pdata,0,sbuf.st_dev);
                        SIVAL(pdata,4,sbuf.st_ino);
                        data_size = 8;
                        break;
 
                case SMB_FILE_ACCESS_INFORMATION:
+                       DEBUG(10,("call_trans2qfilepathinfo: SMB_FILE_ACCESS_INFORMATION\n"));
                        SIVAL(pdata,0,desired_access);
                        data_size = 4;
                        break;
@@ -2192,27 +3103,32 @@ static int call_trans2qfilepathinfo(connection_struct *conn,
                        {
                                size_t byte_len;
                                byte_len = dos_PutUniCode(pdata+4,dos_fname,max_data_bytes,False);
+                               DEBUG(10,("call_trans2qfilepathinfo: SMB_FILE_NAME_INFORMATION\n"));
                                SIVAL(pdata,0,byte_len);
                                data_size = 4 + byte_len;
                                break;
                        }
 
                case SMB_FILE_DISPOSITION_INFORMATION:
+                       DEBUG(10,("call_trans2qfilepathinfo: SMB_FILE_DISPOSITION_INFORMATION\n"));
                        data_size = 1;
                        SCVAL(pdata,0,delete_pending);
                        break;
 
                case SMB_FILE_POSITION_INFORMATION:
+                       DEBUG(10,("call_trans2qfilepathinfo: SMB_FILE_POSITION_INFORMATION\n"));
                        data_size = 8;
                        SOFF_T(pdata,0,pos);
                        break;
 
                case SMB_FILE_MODE_INFORMATION:
+                       DEBUG(10,("call_trans2qfilepathinfo: SMB_FILE_MODE_INFORMATION\n"));
                        SIVAL(pdata,0,mode);
                        data_size = 4;
                        break;
 
                case SMB_FILE_ALIGNMENT_INFORMATION:
+                       DEBUG(10,("call_trans2qfilepathinfo: SMB_FILE_ALIGNMENT_INFORMATION\n"));
                        SIVAL(pdata,0,0); /* No alignment needed. */
                        data_size = 4;
                        break;
@@ -2226,6 +3142,7 @@ static int call_trans2qfilepathinfo(connection_struct *conn,
                case SMB_QUERY_FILE_STREAM_INFO:
 #endif
                case SMB_FILE_STREAM_INFORMATION:
+                       DEBUG(10,("call_trans2qfilepathinfo: SMB_FILE_STREAM_INFORMATION\n"));
                        if (mode & aDIR) {
                                data_size = 0;
                        } else {
@@ -2241,6 +3158,7 @@ static int call_trans2qfilepathinfo(connection_struct *conn,
 
                case SMB_QUERY_COMPRESSION_INFO:
                case SMB_FILE_COMPRESSION_INFORMATION:
+                       DEBUG(10,("call_trans2qfilepathinfo: SMB_FILE_COMPRESSION_INFORMATION\n"));
                        SOFF_T(pdata,0,file_size);
                        SIVAL(pdata,8,0); /* ??? */
                        SIVAL(pdata,12,0); /* ??? */
@@ -2248,6 +3166,7 @@ static int call_trans2qfilepathinfo(connection_struct *conn,
                        break;
 
                case SMB_FILE_NETWORK_OPEN_INFORMATION:
+                       DEBUG(10,("call_trans2qfilepathinfo: SMB_FILE_NETWORK_OPEN_INFORMATION\n"));
                        put_long_date(pdata,c_time);
                        put_long_date(pdata+8,sbuf.st_atime);
                        put_long_date(pdata+16,sbuf.st_mtime); /* write time */
@@ -2260,6 +3179,7 @@ static int call_trans2qfilepathinfo(connection_struct *conn,
                        break;
 
                case SMB_FILE_ATTRIBUTE_TAG_INFORMATION:
+                       DEBUG(10,("call_trans2qfilepathinfo: SMB_FILE_ATTRIBUTE_TAG_INFORMATION\n"));
                        SIVAL(pdata,0,mode);
                        SIVAL(pdata,4,0);
                        data_size = 8;
@@ -2271,12 +3191,13 @@ static int call_trans2qfilepathinfo(connection_struct *conn,
 
                case SMB_QUERY_FILE_UNIX_BASIC:
 
+                       DEBUG(10,("call_trans2qfilepathinfo: SMB_QUERY_FILE_UNIX_BASIC\n"));
                        DEBUG(4,("call_trans2qfilepathinfo: st_mode=%o\n",(int)sbuf.st_mode));
 
                        SOFF_T(pdata,0,get_file_size(sbuf));             /* File size 64 Bit */
                        pdata += 8;
 
-                       SOFF_T(pdata,0,get_allocation_size(fsp,&sbuf)); /* Number of bytes used on disk - 64 Bit */
+                       SOFF_T(pdata,0,get_allocation_size(conn,fsp,&sbuf)); /* Number of bytes used on disk - 64 Bit */
                        pdata += 8;
 
                        put_long_date(pdata,sbuf.st_ctime);       /* Creation Time 64 Bit */
@@ -2330,6 +3251,7 @@ static int call_trans2qfilepathinfo(connection_struct *conn,
                        {
                                pstring buffer;
 
+                               DEBUG(10,("call_trans2qfilepathinfo: SMB_QUERY_FILE_UNIX_LINK\n"));
 #ifdef S_ISLNK
                                if(!S_ISLNK(sbuf.st_mode))
                                        return(UNIXERROR(ERRSRV,ERRbadlink));
@@ -2347,6 +3269,85 @@ static int call_trans2qfilepathinfo(connection_struct *conn,
                                break;
                        }
 
+#if defined(HAVE_POSIX_ACLS)
+               case SMB_QUERY_POSIX_ACL:
+                       {
+                               SMB_ACL_T file_acl = NULL;
+                               SMB_ACL_T def_acl = NULL;
+                               uint16 num_file_acls = 0;
+                               uint16 num_def_acls = 0;
+
+                               if (fsp && !fsp->is_directory && (fsp->fd != -1)) {
+                                       file_acl = SMB_VFS_SYS_ACL_GET_FD(fsp, fsp->fd);
+                               } else {
+                                       file_acl = SMB_VFS_SYS_ACL_GET_FILE(conn, fname, SMB_ACL_TYPE_ACCESS);
+                               }
+
+                               if (file_acl == NULL && no_acl_syscall_error(errno)) {
+                                       DEBUG(5,("call_trans2qfilepathinfo: ACLs not implemented on filesystem containing %s\n",
+                                               fname ));
+                                       return ERROR_NT(NT_STATUS_NOT_IMPLEMENTED);
+                               }
+
+                               if (S_ISDIR(sbuf.st_mode)) {
+                                       if (fsp && fsp->is_directory) {
+                                               def_acl = SMB_VFS_SYS_ACL_GET_FILE(conn, fsp->fsp_name, SMB_ACL_TYPE_DEFAULT);
+                                       } else {
+                                               def_acl = SMB_VFS_SYS_ACL_GET_FILE(conn, fname, SMB_ACL_TYPE_DEFAULT);
+                                       }
+                                       def_acl = free_empty_sys_acl(conn, def_acl);
+                               }
+
+                               num_file_acls = count_acl_entries(conn, file_acl);
+                               num_def_acls = count_acl_entries(conn, def_acl);
+
+                               if ( data_size < (num_file_acls + num_def_acls)*SMB_POSIX_ACL_ENTRY_SIZE + SMB_POSIX_ACL_HEADER_SIZE) {
+                                       DEBUG(5,("call_trans2qfilepathinfo: data_size too small (%u) need %u\n",
+                                               data_size,
+                                               (unsigned int)((num_file_acls + num_def_acls)*SMB_POSIX_ACL_ENTRY_SIZE +
+                                                       SMB_POSIX_ACL_HEADER_SIZE) ));
+                                       if (file_acl) {
+                                               SMB_VFS_SYS_ACL_FREE_ACL(conn, file_acl);
+                                       }
+                                       if (def_acl) {
+                                               SMB_VFS_SYS_ACL_FREE_ACL(conn, def_acl);
+                                       }
+                                       return ERROR_NT(NT_STATUS_BUFFER_TOO_SMALL);
+                               }
+
+                               SSVAL(pdata,0,SMB_POSIX_ACL_VERSION);
+                               SSVAL(pdata,2,num_file_acls);
+                               SSVAL(pdata,4,num_def_acls);
+                               if (!marshall_posix_acl(conn, pdata + SMB_POSIX_ACL_HEADER_SIZE, &sbuf, file_acl)) {
+                                       if (file_acl) {
+                                               SMB_VFS_SYS_ACL_FREE_ACL(conn, file_acl);
+                                       }
+                                       if (def_acl) {
+                                               SMB_VFS_SYS_ACL_FREE_ACL(conn, def_acl);
+                                       }
+                                       return ERROR_NT(NT_STATUS_INTERNAL_ERROR);
+                               }
+                               if (!marshall_posix_acl(conn, pdata + SMB_POSIX_ACL_HEADER_SIZE + (num_file_acls*SMB_POSIX_ACL_ENTRY_SIZE), &sbuf, def_acl)) {
+                                       if (file_acl) {
+                                               SMB_VFS_SYS_ACL_FREE_ACL(conn, file_acl);
+                                       }
+                                       if (def_acl) {
+                                               SMB_VFS_SYS_ACL_FREE_ACL(conn, def_acl);
+                                       }
+                                       return ERROR_NT(NT_STATUS_INTERNAL_ERROR);
+                               }
+
+                               if (file_acl) {
+                                       SMB_VFS_SYS_ACL_FREE_ACL(conn, file_acl);
+                               }
+                               if (def_acl) {
+                                       SMB_VFS_SYS_ACL_FREE_ACL(conn, def_acl);
+                               }
+                               data_size = (num_file_acls + num_def_acls)*SMB_POSIX_ACL_ENTRY_SIZE + SMB_POSIX_ACL_HEADER_SIZE;
+                               break;
+                       }
+#endif
+
                default:
                        return ERROR_DOS(ERRDOS,ERRunknownlevel);
        }
@@ -2362,25 +3363,40 @@ static int call_trans2qfilepathinfo(connection_struct *conn,
  open_file_shared. JRA.
 ****************************************************************************/
 
-NTSTATUS set_delete_on_close_internal(files_struct *fsp, BOOL delete_on_close)
+NTSTATUS set_delete_on_close_internal(files_struct *fsp, BOOL delete_on_close, uint32 dosmode)
 {
-       /*
-        * Only allow delete on close for writable shares.
-        */
+       if (delete_on_close) {
+               /*
+                * Only allow delete on close for writable files.
+                */
+
+               if (!lp_delete_readonly(SNUM(fsp->conn))) {
+                       if (dosmode & aRONLY) {
+                               DEBUG(10,("set_delete_on_close_internal: file %s delete on close flag set but file attribute is readonly.\n",
+                                       fsp->fsp_name ));
+                               return NT_STATUS_CANNOT_DELETE;
+                       }
+               }
+
+               /*
+                * Only allow delete on close for writable shares.
+                */
 
-       if (delete_on_close && !CAN_WRITE(fsp->conn)) {
-               DEBUG(10,("set_delete_on_close_internal: file %s delete on close flag set but write access denied on share.\n",
+               if (!CAN_WRITE(fsp->conn)) {
+                       DEBUG(10,("set_delete_on_close_internal: file %s delete on close flag set but write access denied on share.\n",
                                fsp->fsp_name ));
-                               return NT_STATUS_ACCESS_DENIED;
-       }
-       /*
-        * Only allow delete on close for files/directories opened with delete intent.
-        */
+                       return NT_STATUS_ACCESS_DENIED;
+               }
+
+               /*
+                * Only allow delete on close for files/directories opened with delete intent.
+                */
 
-       if (delete_on_close && !(fsp->desired_access & DELETE_ACCESS)) {
-               DEBUG(10,("set_delete_on_close_internal: file %s delete on close flag set but delete access denied.\n",
+               if (!(fsp->desired_access & DELETE_ACCESS)) {
+                       DEBUG(10,("set_delete_on_close_internal: file %s delete on close flag set but delete access denied.\n",
                                fsp->fsp_name ));
-                               return NT_STATUS_ACCESS_DENIED;
+                       return NT_STATUS_ACCESS_DENIED;
+               }
        }
 
        if(fsp->is_directory) {
@@ -2417,7 +3433,7 @@ NTSTATUS set_delete_on_close_over_all(files_struct *fsp, BOOL delete_on_close)
                return NT_STATUS_ACCESS_DENIED;
 
        if (!modify_delete_flag(fsp->dev, fsp->inode, delete_on_close)) {
-               DEBUG(0,("set_delete_on_close_internal: failed to change delete on close flag for file %s\n",
+               DEBUG(0,("set_delete_on_close_over_all: failed to change delete on close flag for file %s\n",
                        fsp->fsp_name ));
                unlock_share_entry_fsp(fsp);
                return NT_STATUS_ACCESS_DENIED;
@@ -2427,97 +3443,36 @@ NTSTATUS set_delete_on_close_over_all(files_struct *fsp, BOOL delete_on_close)
        return NT_STATUS_OK;
 }
 
-/****************************************************************************
- Returns true if this pathname is within the share, and thus safe.
-****************************************************************************/
-
-static int ensure_link_is_safe(connection_struct *conn, const char *link_dest_in, char *link_dest_out)
-{
-#ifdef PATH_MAX
-       char resolved_name[PATH_MAX+1];
-#else
-       pstring resolved_name;
-#endif
-       fstring last_component;
-       pstring link_dest;
-       pstring link_test;
-       char *p;
-       BOOL bad_path = False;
-       SMB_STRUCT_STAT sbuf;
-
-       pstrcpy(link_dest, link_dest_in);
-       unix_convert(link_dest,conn,0,&bad_path,&sbuf);
-
-       /* Store the UNIX converted path. */
-       pstrcpy(link_dest_out, link_dest);
-
-       p = strrchr(link_dest, '/');
-       if (p) {
-               fstrcpy(last_component, p+1);
-               *p = '\0';
-       } else {
-               fstrcpy(last_component, link_dest);
-               pstrcpy(link_dest, "./");
-       }
-               
-       if (SMB_VFS_REALPATH(conn,link_dest,resolved_name) == NULL)
-               return -1;
-
-       pstrcpy(link_dest, resolved_name);
-       pstrcat(link_dest, "/");
-       pstrcat(link_dest, last_component);
-
-       if (*link_dest != '/') {
-               /* Relative path. */
-               pstrcpy(link_test, conn->connectpath);
-               pstrcat(link_test, "/");
-               pstrcat(link_test, link_dest);
-       } else {
-               pstrcpy(link_test, link_dest);
-       }
-
-       /*
-        * Check if the link is within the share.
-        */
-
-       if (strncmp(conn->connectpath, link_test, strlen(conn->connectpath))) {
-               errno = EACCES;
-               return -1;
-       }
-       return 0;
-}
-
 /****************************************************************************
  Set a hard link (called by UNIX extensions and by NT rename with HARD link
  code.
 ****************************************************************************/
 
-NTSTATUS hardlink_internals(connection_struct *conn, char *name, char *newname)
+NTSTATUS hardlink_internals(connection_struct *conn, char *oldname, char *newname)
 {
-       BOOL bad_path_src = False;
-       BOOL bad_path_dest = False;
+       BOOL bad_path_oldname = False;
+       BOOL bad_path_newname = False;
        SMB_STRUCT_STAT sbuf1, sbuf2;
-       BOOL rc, rcdest;
-       pstring last_component_src;
-       pstring last_component_dest;
+       pstring last_component_oldname;
+       pstring last_component_newname;
        NTSTATUS status = NT_STATUS_OK;
 
        ZERO_STRUCT(sbuf1);
        ZERO_STRUCT(sbuf2);
 
        /* No wildcards. */
-       if (ms_has_wild(name) || ms_has_wild(newname)) {
+       if (ms_has_wild(newname) || ms_has_wild(oldname)) {
                return NT_STATUS_OBJECT_PATH_SYNTAX_BAD;
        }
 
-       rc = unix_convert(name,conn,last_component_src,&bad_path_src,&sbuf1);
-       if (!rc && bad_path_src) {
+       unix_convert(oldname,conn,last_component_oldname,&bad_path_oldname,&sbuf1);
+       if (bad_path_oldname) {
                return NT_STATUS_OBJECT_PATH_NOT_FOUND;
        }
 
        /* Quick check for "." and ".." */
-       if (last_component_src[0] == '.') {
-               if (!last_component_src[1] || (last_component_src[1] == '.' && !last_component_src[2])) {
+       if (last_component_oldname[0] == '.') {
+               if (!last_component_oldname[1] || (last_component_oldname[1] == '.' && !last_component_oldname[2])) {
                        return NT_STATUS_OBJECT_NAME_INVALID;
                }
        }
@@ -2527,37 +3482,46 @@ NTSTATUS hardlink_internals(connection_struct *conn, char *name, char *newname)
                return NT_STATUS_OBJECT_NAME_NOT_FOUND;
        }
 
-       rcdest = unix_convert(newname,conn,last_component_dest,&bad_path_dest,&sbuf2);
-       if (!rcdest && bad_path_dest) {
+       if (!check_name(oldname,conn)) {
+               return NT_STATUS_ACCESS_DENIED;
+       }
+
+       unix_convert(newname,conn,last_component_newname,&bad_path_newname,&sbuf2);
+       if (bad_path_newname) {
                return NT_STATUS_OBJECT_PATH_NOT_FOUND;
        }
 
        /* Quick check for "." and ".." */
-       if (last_component_dest[0] == '.') {
-               if (!last_component_dest[1] || (last_component_dest[1] == '.' && !last_component_dest[2])) {
+       if (last_component_newname[0] == '.') {
+               if (!last_component_newname[1] || (last_component_newname[1] == '.' && !last_component_newname[2])) {
                        return NT_STATUS_OBJECT_NAME_INVALID;
                }
        }
 
-       /* Disallow if already exists. */
+       /* Disallow if newname already exists. */
        if (VALID_STAT(sbuf2)) {
                return NT_STATUS_OBJECT_NAME_COLLISION;
        }
 
+       if (!check_name(newname,conn)) {
+               return NT_STATUS_ACCESS_DENIED;
+       }
+
        /* No links from a directory. */
        if (S_ISDIR(sbuf1.st_mode)) {
                return NT_STATUS_FILE_IS_A_DIRECTORY;
        }
 
-       if (ensure_link_is_safe(conn, newname, newname) != 0)
+       /* Ensure this is within the share. */
+       if (!reduce_name(conn, oldname) != 0)
                return NT_STATUS_ACCESS_DENIED;
 
-       DEBUG(10,("hardlink_internals: doing hard link %s -> %s\n", name, newname ));
+       DEBUG(10,("hardlink_internals: doing hard link %s -> %s\n", newname, oldname ));
 
-       if (SMB_VFS_LINK(conn,name,newname) != 0) {
+       if (SMB_VFS_LINK(conn,oldname,newname) != 0) {
                status = map_nt_error_from_unix(errno);
-               DEBUG(3,("hardlink_internals: Error %s link %s -> %s\n",
-                                nt_errstr(status), name,newname));
+               DEBUG(3,("hardlink_internals: Error %s hard link %s -> %s\n",
+                                nt_errstr(status), newname, oldname));
        }
 
        return status;
@@ -2567,9 +3531,9 @@ NTSTATUS hardlink_internals(connection_struct *conn, char *name, char *newname)
  Reply to a TRANS2_SETFILEINFO (set file info by fileid).
 ****************************************************************************/
 
-static int call_trans2setfilepathinfo(connection_struct *conn,
-                                       char *inbuf, char *outbuf, int length, int bufsize,
-                                       char **pparams, int total_params, char **ppdata, int total_data)
+static int call_trans2setfilepathinfo(connection_struct *conn, char *inbuf, char *outbuf, int length, int bufsize,
+                                       char **pparams, int total_params, char **ppdata, int total_data,
+                                       unsigned int max_data_bytes)
 {
        char *params = *pparams;
        char *pdata = *ppdata;
@@ -2591,9 +3555,12 @@ static int call_trans2setfilepathinfo(connection_struct *conn,
        if (!params)
                return ERROR_NT(NT_STATUS_INVALID_PARAMETER);
 
+       ZERO_STRUCT(sbuf);
+
        if (tran_call == TRANSACT2_SETFILEINFO) {
-               if (total_params < 4)
-                       return(ERROR_DOS(ERRDOS,ERRinvalidparam));
+               if (total_params < 4) {
+                       return ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+               }
 
                fsp = file_fsp(params,0);
                info_level = SVAL(params,2);    
@@ -2605,8 +3572,7 @@ static int call_trans2setfilepathinfo(connection_struct *conn,
                         * to do this call. JRA.
                         */
                        pstrcpy(fname, fsp->fsp_name);
-                       unix_convert(fname,conn,0,&bad_path,&sbuf);
-                       if (!check_name(fname,conn) || (!VALID_STAT(sbuf))) {
+                       if (SMB_VFS_STAT(conn,fname,&sbuf) != 0) {
                                DEBUG(3,("call_trans2setfilepathinfo: fileinfo of %s failed (%s)\n",fname,strerror(errno)));
                                return set_bad_path_error(errno, bad_path, outbuf, ERRDOS,ERRbadpath);
                        }
@@ -2640,15 +3606,19 @@ static int call_trans2setfilepathinfo(connection_struct *conn,
                }
        } else {
                /* set path info */
-               if (total_params < 6)
-                       return(ERROR_DOS(ERRDOS,ERRinvalidparam));
+               if (total_params < 6) {
+                       return ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+               }
 
                info_level = SVAL(params,0);    
-               srvstr_get_path(inbuf, fname, &params[6], sizeof(fname), -1, STR_TERMINATE, &status);
+               srvstr_get_path(inbuf, fname, &params[6], sizeof(fname), -1, STR_TERMINATE, &status, False);
                if (!NT_STATUS_IS_OK(status)) {
                        return ERROR_NT(status);
                }
                unix_convert(fname,conn,0,&bad_path,&sbuf);
+               if (bad_path) {
+                       return ERROR_NT(NT_STATUS_OBJECT_PATH_NOT_FOUND);
+               }
 
                /*
                 * For CIFS UNIX extensions the target name may not exist.
@@ -2677,15 +3647,16 @@ static int call_trans2setfilepathinfo(connection_struct *conn,
        DEBUG(3,("call_trans2setfilepathinfo(%d) %s (fnum %d) info_level=%d totdata=%d\n",
                tran_call,fname, fsp ? fsp->fnum : -1, info_level,total_data));
 
-       /* Realloc the parameter and data sizes */
-       params = Realloc(*pparams,2);
-       if(params == NULL)
-               return ERROR_DOS(ERRDOS,ERRnomem);
+       /* Realloc the parameter size */
+       params = SMB_REALLOC(*pparams,2);
+       if(params == NULL) {
+               return ERROR_NT(NT_STATUS_NO_MEMORY);
+       }
        *pparams = params;
 
        SSVAL(params,0,0);
 
-       if (fsp) {
+       if (fsp && fsp->pending_modtime) {
                /* the pending modtime overrides the current modtime */
                sbuf.st_mtime = fsp->pending_modtime;
        }
@@ -2702,8 +3673,9 @@ static int call_trans2setfilepathinfo(connection_struct *conn,
        switch (info_level) {
                case SMB_INFO_STANDARD:
                {
-                       if (total_data < 12)
-                               return(ERROR_DOS(ERRDOS,ERRinvalidparam));
+                       if (total_data < 12) {
+                               return ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+                       }
 
                        /* access time */
                        tvs.actime = make_unix_date2(pdata+l1_fdateLastAccess);
@@ -2713,14 +3685,46 @@ static int call_trans2setfilepathinfo(connection_struct *conn,
                }
 
                case SMB_INFO_SET_EA:
-                       return(ERROR_DOS(ERRDOS,ERReasnotsupported));
+               {
+                       struct ea_list *ea_list = NULL;
+                       TALLOC_CTX *ctx = NULL;
+
+                       if (total_data < 10) {
+                               return ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+                       }
+
+                       if (IVAL(pdata,0) > total_data) {
+                               DEBUG(10,("call_trans2setfilepathinfo: bad total data size (%u) > %u\n",
+                                       IVAL(pdata,0), (unsigned int)total_data));
+                               return ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+                       }
+
+                       ctx = talloc_init("SMB_INFO_SET_EA");
+                       if (!ctx) {
+                               return ERROR_NT(NT_STATUS_NO_MEMORY);
+                       }
+                       ea_list = read_ea_list(ctx, pdata + 4, total_data - 4);
+                       if (!ea_list) {
+                               talloc_destroy(ctx);
+                               return ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+                       }
+                       status = set_ea(conn, fsp, fname, ea_list);
+                       talloc_destroy(ctx);
+
+                       if (!NT_STATUS_IS_OK(status)) {
+                               return ERROR_NT(status);
+                       }
+                       break;
+               }
 
+#if 0
+               /* The following 2 info levels are only valid on query, not set. Remove them. JRA. */
                /* XXXX um, i don't think this is right.
                        it's also not in the cifs6.txt spec.
                */
                case SMB_INFO_QUERY_EAS_FROM_LIST:
                        if (total_data < 28)
-                               return(ERROR_DOS(ERRDOS,ERRinvalidparam));
+                               return ERROR_NT(NT_STATUS_INVALID_PARAMETER);
 
                        tvs.actime = make_unix_date2(pdata+8);
                        tvs.modtime = make_unix_date2(pdata+12);
@@ -2731,13 +3735,14 @@ static int call_trans2setfilepathinfo(connection_struct *conn,
                /* XXXX nor this.  not in cifs6.txt, either. */
                case SMB_INFO_QUERY_ALL_EAS:
                        if (total_data < 28)
-                               return(ERROR_DOS(ERRDOS,ERRinvalidparam));
+                               return ERROR_NT(NT_STATUS_INVALID_PARAMETER);
 
                        tvs.actime = make_unix_date2(pdata+8);
                        tvs.modtime = make_unix_date2(pdata+12);
                        size = IVAL(pdata,16);
                        dosmode = IVAL(pdata,24);
                        break;
+#endif
 
                case SMB_SET_FILE_BASIC_INFO:
                case SMB_FILE_BASIC_INFORMATION:
@@ -2746,8 +3751,9 @@ static int call_trans2setfilepathinfo(connection_struct *conn,
                        time_t write_time;
                        time_t changed_time;
 
-                       if (total_data < 36)
-                               return(ERROR_DOS(ERRDOS,ERRinvalidparam));
+                       if (total_data < 36) {
+                               return ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+                       }
 
                        /* Ignore create time at offset pdata. */
 
@@ -2759,13 +3765,13 @@ static int call_trans2setfilepathinfo(connection_struct *conn,
 
                        tvs.modtime = MIN(write_time, changed_time);
 
-                       if (write_time > tvs.modtime && write_time != 0xffffffff) {
+                       if (write_time > tvs.modtime && write_time != (time_t)-1) {
                                tvs.modtime = write_time;
                        }
                        /* Prefer a defined time to an undefined one. */
-                       if (tvs.modtime == (time_t)0 || tvs.modtime == (time_t)-1)
-                               tvs.modtime = (write_time == (time_t)0 || write_time == (time_t)-1
-                                       ? changed_time : write_time);
+                       if (null_mtime(tvs.modtime)) {
+                               tvs.modtime = null_mtime(write_time) ? changed_time : write_time;
+                       }
 
                        /* attributes */
                        dosmode = IVAL(pdata,32);
@@ -2778,8 +3784,9 @@ static int call_trans2setfilepathinfo(connection_struct *conn,
                        int ret = -1;
                        SMB_BIG_UINT allocation_size;
 
-                       if (total_data < 8)
-                               return(ERROR_DOS(ERRDOS,ERRinvalidparam));
+                       if (total_data < 8) {
+                               return ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+                       }
 
                        allocation_size = (SMB_BIG_UINT)IVAL(pdata,0);
 #ifdef LARGE_SMB_OFF_T
@@ -2791,8 +3798,9 @@ static int call_trans2setfilepathinfo(connection_struct *conn,
                        DEBUG(10,("call_trans2setfilepathinfo: Set file allocation info for file %s to %.0f\n",
                                        fname, (double)allocation_size ));
 
-                       if (allocation_size)
-                               allocation_size = SMB_ROUNDUP(allocation_size,SMB_ROUNDUP_ALLOCATION_SIZE);
+                       if (allocation_size) {
+                               allocation_size = smb_roundup(conn, allocation_size);
+                       }
 
                        if(allocation_size != get_file_size(sbuf)) {
                                SMB_STRUCT_STAT new_sbuf;
@@ -2818,7 +3826,8 @@ static int call_trans2setfilepathinfo(connection_struct *conn,
                                        new_fsp = open_file_shared1(conn, fname, &sbuf,FILE_WRITE_DATA,
                                                                        SET_OPEN_MODE(DOS_OPEN_RDWR),
                                                                        (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN),
-                                                                       0, 0, &access_mode, &action);
+                                                                       FILE_ATTRIBUTE_NORMAL,
+                                                                       INTERNAL_OPEN_ONLY, &access_mode, &action);
  
                                        if (new_fsp == NULL)
                                                return(UNIXERROR(ERRDOS,ERRbadpath));
@@ -2850,8 +3859,9 @@ static int call_trans2setfilepathinfo(connection_struct *conn,
                case SMB_FILE_END_OF_FILE_INFORMATION:
                case SMB_SET_FILE_END_OF_FILE_INFO:
                {
-                       if (total_data < 8)
-                               return(ERROR_DOS(ERRDOS,ERRinvalidparam));
+                       if (total_data < 8) {
+                               return ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+                       }
 
                        size = IVAL(pdata,0);
 #ifdef LARGE_SMB_OFF_T
@@ -2869,8 +3879,9 @@ static int call_trans2setfilepathinfo(connection_struct *conn,
                {
                        BOOL delete_on_close;
 
-                       if (total_data < 1)
-                               return(ERROR_DOS(ERRDOS,ERRinvalidparam));
+                       if (total_data < 1) {
+                               return ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+                       }
 
                        delete_on_close = (CVAL(pdata,0) ? True : False);
 
@@ -2881,15 +3892,17 @@ static int call_trans2setfilepathinfo(connection_struct *conn,
                        if (fsp == NULL)
                                return(UNIXERROR(ERRDOS,ERRbadfid));
 
-                       status = set_delete_on_close_internal(fsp, delete_on_close);
+                       status = set_delete_on_close_internal(fsp, delete_on_close, dosmode);
  
-                       if (NT_STATUS_V(status) !=  NT_STATUS_V(NT_STATUS_OK))
+                       if (!NT_STATUS_IS_OK(status)) {
                                return ERROR_NT(status);
+                       }
 
                        /* The set is across all open files on this dev/inode pair. */
                        status =set_delete_on_close_over_all(fsp, delete_on_close);
-                       if (NT_STATUS_V(status) !=  NT_STATUS_V(NT_STATUS_OK))
+                       if (!NT_STATUS_IS_OK(status)) {
                                return ERROR_NT(status);
+                       }
 
                        break;
                }
@@ -2898,8 +3911,9 @@ static int call_trans2setfilepathinfo(connection_struct *conn,
                {
                        SMB_BIG_UINT position_information;
 
-                       if (total_data < 8)
-                               return(ERROR_DOS(ERRDOS,ERRinvalidparam));
+                       if (total_data < 8) {
+                               return ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+                       }
 
                        position_information = (SMB_BIG_UINT)IVAL(pdata,0);
 #ifdef LARGE_SMB_OFF_T
@@ -2915,6 +3929,26 @@ static int call_trans2setfilepathinfo(connection_struct *conn,
                        break;
                }
 
+               /* From tridge Samba4 : 
+                * MODE_INFORMATION in setfileinfo (I have no
+                * idea what "mode information" on a file is - it takes a value of 0,
+                * 2, 4 or 6. What could it be?).
+                */
+
+               case SMB_FILE_MODE_INFORMATION:
+               {
+                       uint32 mode;
+
+                       if (total_data < 4) {
+                               return ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+                       }
+                       mode = IVAL(pdata,0);
+                       if (mode != 0 && mode != 2 && mode != 4 && mode != 6) {
+                               return ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+                       }
+                       break;
+               }
+
                /*
                 * CIFS UNIX extensions.
                 */
@@ -2923,8 +3957,9 @@ static int call_trans2setfilepathinfo(connection_struct *conn,
                {
                        uint32 raw_unixmode;
 
-                       if (total_data < 100)
-                               return(ERROR_DOS(ERRDOS,ERRinvalidparam));
+                       if (total_data < 100) {
+                               return ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+                       }
 
                        if(IVAL(pdata, 0) != SMB_SIZE_NO_CHANGE_LO &&
                           IVAL(pdata, 4) != SMB_SIZE_NO_CHANGE_HI) {
@@ -2937,8 +3972,8 @@ static int call_trans2setfilepathinfo(connection_struct *conn,
 #endif /* LARGE_SMB_OFF_T */
                        }
                        pdata+=24;          /* ctime & st_blocks are not changed */
-                       tvs.actime = interpret_long_unix_date(pdata); /* access_time */
-                       tvs.modtime = interpret_long_unix_date(pdata+8); /* modification_time */
+                       tvs.actime = interpret_long_date(pdata); /* access_time */
+                       tvs.modtime = interpret_long_date(pdata+8); /* modification_time */
                        pdata+=16;
                        set_owner = (uid_t)IVAL(pdata,0);
                        pdata += 8;
@@ -2960,24 +3995,26 @@ size = %.0f, uid = %u, gid = %u, raw perms = 0%o\n",
                                 * a new info level should be used for mknod. JRA.
                                 */
 
-#if !defined(HAVE_MAKEDEV_FN)
-                               return(ERROR_DOS(ERRDOS,ERRnoaccess));
-#else /* HAVE_MAKEDEV_FN */
                                uint32 file_type = IVAL(pdata,0);
+#if defined(HAVE_MAKEDEV)
                                uint32 dev_major = IVAL(pdata,4);
                                uint32 dev_minor = IVAL(pdata,12);
+#endif
 
                                uid_t myuid = geteuid();
                                gid_t mygid = getegid();
-                               SMB_DEV_T dev;
+                               SMB_DEV_T dev = (SMB_DEV_T)0;
 
                                if (tran_call == TRANSACT2_SETFILEINFO)
                                        return(ERROR_DOS(ERRDOS,ERRnoaccess));
 
-                               if (raw_unixmode == SMB_MODE_NO_CHANGE)
-                                       return(ERROR_DOS(ERRDOS,ERRinvalidparam));
+                               if (raw_unixmode == SMB_MODE_NO_CHANGE) {
+                                       return ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+                               }
 
+#if defined(HAVE_MAKEDEV)
                                dev = makedev(dev_major, dev_minor);
+#endif
 
                                /* We can only create as the owner/group we are. */
 
@@ -2986,15 +4023,36 @@ size = %.0f, uid = %u, gid = %u, raw perms = 0%o\n",
                                if ((set_grp != mygid) && (set_grp != (gid_t)SMB_GID_NO_CHANGE))
                                        return(ERROR_DOS(ERRDOS,ERRnoaccess));
 
-                               if (file_type != UNIX_TYPE_CHARDEV && file_type != UNIX_TYPE_BLKDEV &&
-                                               file_type != UNIX_TYPE_FIFO)
-                                       return(ERROR_DOS(ERRDOS,ERRnoaccess));
+                               switch (file_type) {
+#if defined(S_IFIFO)
+                                       case UNIX_TYPE_FIFO:
+                                               unixmode |= S_IFIFO;
+                                               break;
+#endif
+#if defined(S_IFSOCK)
+                                       case UNIX_TYPE_SOCKET:
+                                               unixmode |= S_IFSOCK;
+                                               break;
+#endif
+#if defined(S_IFCHR)
+                                       case UNIX_TYPE_CHARDEV:
+                                               unixmode |= S_IFCHR;
+                                               break;
+#endif
+#if defined(S_IFBLK)
+                                       case UNIX_TYPE_BLKDEV:
+                                               unixmode |= S_IFBLK;
+                                               break;
+#endif
+                                       default:
+                                               return(ERROR_DOS(ERRDOS,ERRnoaccess));
+                               }
 
                                DEBUG(10,("call_trans2setfilepathinfo: SMB_SET_FILE_UNIX_BASIC doing mknod dev %.0f mode \
 0%o for file %s\n", (double)dev, unixmode, fname ));
 
                                /* Ok - do the mknod. */
-                               if (SMB_VFS_MKNOD(conn,dos_to_unix_static(fname), unixmode, dev) != 0)
+                               if (SMB_VFS_MKNOD(conn,fname, unixmode, dev) != 0)
                                        return(UNIXERROR(ERRDOS,ERRnoaccess));
 
                                inherit_access_acl(conn, fname, unixmode);
@@ -3002,8 +4060,6 @@ size = %.0f, uid = %u, gid = %u, raw perms = 0%o\n",
                                SSVAL(params,0,0);
                                send_trans2_replies(outbuf, bufsize, params, 2, *ppdata, 0);
                                return(-1);
-#endif /* HAVE_MAKEDEV_FN */
-
                        }
 
                        /*
@@ -3043,25 +4099,46 @@ size = %.0f, uid = %u, gid = %u, raw perms = 0%o\n",
 
                case SMB_SET_FILE_UNIX_LINK:
                {
-                       pstring link_dest;
+                       pstring link_target;
+                       char *newname = fname;
+
                        /* Set a symbolic link. */
                        /* Don't allow this if follow links is false. */
 
                        if (!lp_symlinks(SNUM(conn)))
                                return(ERROR_DOS(ERRDOS,ERRnoaccess));
 
-                       srvstr_get_path(inbuf, link_dest, pdata, sizeof(link_dest), -1, STR_TERMINATE, &status);
-                       if (!NT_STATUS_IS_OK(status)) {
-                               return ERROR_NT(status);
-                       }
+                       srvstr_pull(inbuf, link_target, pdata, sizeof(link_target), -1, STR_TERMINATE);
 
-                       if (ensure_link_is_safe(conn, link_dest, link_dest) != 0)
-                               return(UNIXERROR(ERRDOS,ERRnoaccess));
+                       /* !widelinks forces the target path to be within the share. */
+                       /* This means we can interpret the target as a pathname. */
+                       if (!lp_widelinks(SNUM(conn))) {
+                               pstring rel_name;
+                               char *last_dirp = NULL;
+
+                               unix_format(link_target);
+                               if (*link_target == '/') {
+                                       /* No absolute paths allowed. */
+                                       return(UNIXERROR(ERRDOS,ERRnoaccess));
+                               }
+                               pstrcpy(rel_name, newname);
+                               last_dirp = strrchr_m(rel_name, '/');
+                               if (last_dirp) {
+                                       last_dirp[1] = '\0';
+                               } else {
+                                       pstrcpy(rel_name, "./");
+                               }
+                               pstrcat(rel_name, link_target);
+
+                               if (!check_name(rel_name, conn)) {
+                                       return(UNIXERROR(ERRDOS,ERRnoaccess));
+                               }
+                       }
 
                        DEBUG(10,("call_trans2setfilepathinfo: SMB_SET_FILE_UNIX_LINK doing symlink %s -> %s\n",
-                               fname, link_dest ));
+                               fname, link_target ));
 
-                       if (SMB_VFS_SYMLINK(conn,link_dest,fname) != 0)
+                       if (SMB_VFS_SYMLINK(conn,link_target,newname) != 0)
                                return(UNIXERROR(ERRDOS,ERRnoaccess));
                        SSVAL(params,0,0);
                        send_trans2_replies(outbuf, bufsize, params, 2, *ppdata, 0);
@@ -3070,18 +4147,19 @@ size = %.0f, uid = %u, gid = %u, raw perms = 0%o\n",
 
                case SMB_SET_FILE_UNIX_HLINK:
                {
-                       pstring link_dest;
+                       pstring oldname;
+                       char *newname = fname;
 
                        /* Set a hard link. */
-                       srvstr_get_path(inbuf, link_dest, pdata, sizeof(link_dest), -1, STR_TERMINATE, &status);
+                       srvstr_get_path(inbuf, oldname, pdata, sizeof(oldname), -1, STR_TERMINATE, &status, False);
                        if (!NT_STATUS_IS_OK(status)) {
                                return ERROR_NT(status);
                        }
 
                        DEBUG(10,("call_trans2setfilepathinfo: SMB_SET_FILE_UNIX_LINK doing hard link %s -> %s\n",
-                               fname, link_dest ));
+                               fname, oldname));
 
-                       status = hardlink_internals(conn, fname, link_dest);
+                       status = hardlink_internals(conn, oldname, newname);
                        if (!NT_STATUS_IS_OK(status)) {
                                return ERROR_NT(status);
                        }
@@ -3100,13 +4178,14 @@ size = %.0f, uid = %u, gid = %u, raw perms = 0%o\n",
                        pstring base_name;
                        char *p;
 
-                       if (total_data < 12)
-                               return(ERROR_DOS(ERRDOS,ERRinvalidparam));
+                       if (total_data < 12) {
+                               return ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+                       }
 
                        overwrite = (CVAL(pdata,0) ? True : False);
                        root_fid = IVAL(pdata,4);
                        len = IVAL(pdata,8);
-                       srvstr_get_path(inbuf, newname, &pdata[12], sizeof(newname), len, 0, &status);
+                       srvstr_get_path(inbuf, newname, &pdata[12], sizeof(newname), len, 0, &status, False);
                        if (!NT_STATUS_IS_OK(status)) {
                                return ERROR_NT(status);
                        }
@@ -3129,7 +4208,7 @@ size = %.0f, uid = %u, gid = %u, raw perms = 0%o\n",
                        if (fsp) {
                                DEBUG(10,("call_trans2setfilepathinfo: SMB_FILE_RENAME_INFORMATION (fnum %d) %s -> %s\n",
                                        fsp->fnum, fsp->fsp_name, base_name ));
-                               status = rename_internals_fsp(conn, fsp, base_name, overwrite);
+                               status = rename_internals_fsp(conn, fsp, base_name, 0, overwrite);
                        } else {
                                DEBUG(10,("call_trans2setfilepathinfo: SMB_FILE_RENAME_INFORMATION %s -> %s\n",
                                        fname, newname ));
@@ -3143,16 +4222,71 @@ size = %.0f, uid = %u, gid = %u, raw perms = 0%o\n",
                        send_trans2_replies(outbuf, bufsize, params, 2, *ppdata, 0);
                        return(-1);
                }
+
+#if defined(HAVE_POSIX_ACLS)
+               case SMB_SET_POSIX_ACL:
+               {
+                       uint16 posix_acl_version;
+                       uint16 num_file_acls;
+                       uint16 num_def_acls;
+                       BOOL valid_file_acls = True;
+                       BOOL valid_def_acls = True;
+
+                       if (total_data < SMB_POSIX_ACL_HEADER_SIZE) {
+                               return ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+                       }
+                       posix_acl_version = SVAL(pdata,0);
+                       num_file_acls = SVAL(pdata,2);
+                       num_def_acls = SVAL(pdata,4);
+
+                       if (num_file_acls == SMB_POSIX_IGNORE_ACE_ENTRIES) {
+                               valid_file_acls = False;
+                               num_file_acls = 0;
+                       }
+
+                       if (num_def_acls == SMB_POSIX_IGNORE_ACE_ENTRIES) {
+                               valid_def_acls = False;
+                               num_def_acls = 0;
+                       }
+
+                       if (posix_acl_version != SMB_POSIX_ACL_VERSION) {
+                               return ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+                       }
+
+                       if (total_data < SMB_POSIX_ACL_HEADER_SIZE +
+                                       (num_file_acls+num_def_acls)*SMB_POSIX_ACL_ENTRY_SIZE) {
+                               return ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+                       }
+
+                       if (valid_file_acls && !set_unix_posix_acl(conn, fsp, fname, num_file_acls,
+                                       pdata + SMB_POSIX_ACL_HEADER_SIZE)) {
+                               return(UNIXERROR(ERRDOS,ERRnoaccess));
+                       }
+
+                       if (valid_def_acls && !set_unix_posix_default_acl(conn, fname, &sbuf, num_def_acls,
+                                       pdata + SMB_POSIX_ACL_HEADER_SIZE +
+                                       (num_file_acls*SMB_POSIX_ACL_ENTRY_SIZE))) {
+                               return(UNIXERROR(ERRDOS,ERRnoaccess));
+                       }
+
+                       SSVAL(params,0,0);
+                       send_trans2_replies(outbuf, bufsize, params, 2, *ppdata, 0);
+                       return(-1);
+               }
+#endif
+
                default:
                        return ERROR_DOS(ERRDOS,ERRunknownlevel);
        }
 
        /* get some defaults (no modifications) if any info is zero or -1. */
-       if (tvs.actime == (time_t)0 || tvs.actime == (time_t)-1)
+       if (null_mtime(tvs.actime)) {
                tvs.actime = sbuf.st_atime;
+       }
 
-       if (tvs.modtime == (time_t)0 || tvs.modtime == (time_t)-1)
+       if (null_mtime(tvs.modtime)) {
                tvs.modtime = sbuf.st_mtime;
+       }
 
        DEBUG(6,("actime: %s " , ctime(&tvs.actime)));
        DEBUG(6,("modtime: %s ", ctime(&tvs.modtime)));
@@ -3184,41 +4318,19 @@ size = %.0f, uid = %u, gid = %u, raw perms = 0%o\n",
         * Try and set the times, size and mode of this file -
         * if they are different from the current values
         */
-       if (sbuf.st_mtime != tvs.modtime || sbuf.st_atime != tvs.actime) {
-               if(fsp != NULL) {
-                       /*
-                        * This was a setfileinfo on an open file.
-                        * NT does this a lot. It's actually pointless
-                        * setting the time here, as it will be overwritten
-                        * on the next write, so we save the request
-                        * away and will set it on file close. JRA.
-                        */
-
-                       if (tvs.modtime != (time_t)0 && tvs.modtime != (time_t)-1) {
-                               DEBUG(10,("call_trans2setfilepathinfo: setting pending modtime to %s\n", ctime(&tvs.modtime) ));
-                               fsp->pending_modtime = tvs.modtime;
-                       }
-
-               } else {
-
-                       DEBUG(10,("call_trans2setfilepathinfo: setting utimes to modified values.\n"));
-
-                       if(file_utime(conn, fname, &tvs)!=0)
-                               return(UNIXERROR(ERRDOS,ERRnoaccess));
-               }
-       }
 
        /* check the mode isn't different, before changing it */
        if ((dosmode != 0) && (dosmode != dos_mode(conn, fname, &sbuf))) {
 
                DEBUG(10,("call_trans2setfilepathinfo: file %s : setting dos mode %x\n", fname, dosmode ));
 
-               if(file_chmod(conn, fname, dosmode, NULL)) {
-                       DEBUG(2,("chmod of %s failed (%s)\n", fname, strerror(errno)));
+               if(file_set_dosmode(conn, fname, dosmode, &sbuf, False)) {
+                       DEBUG(2,("file_set_dosmode of %s failed (%s)\n", fname, strerror(errno)));
                        return(UNIXERROR(ERRDOS,ERRnoaccess));
                }
        }
 
+       /* Now the size. */
        if (size != get_file_size(sbuf)) {
 
                int ret;
@@ -3244,7 +4356,8 @@ size = %.0f, uid = %u, gid = %u, raw perms = 0%o\n",
                        new_fsp = open_file_shared(conn, fname, &sbuf,
                                                SET_OPEN_MODE(DOS_OPEN_RDWR),
                                                (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN),
-                                               0, 0, &access_mode, &action);
+                                               FILE_ATTRIBUTE_NORMAL,
+                                               INTERNAL_OPEN_ONLY, &access_mode, &action);
        
                        if (new_fsp == NULL)
                                return(UNIXERROR(ERRDOS,ERRbadpath));
@@ -3258,6 +4371,34 @@ size = %.0f, uid = %u, gid = %u, raw perms = 0%o\n",
                        return (UNIXERROR(ERRHRD,ERRdiskfull));
        }
 
+       /*
+        * Finally the times.
+        */
+       if (sbuf.st_mtime != tvs.modtime || sbuf.st_atime != tvs.actime) {
+               if(fsp != NULL) {
+                       /*
+                        * This was a setfileinfo on an open file.
+                        * NT does this a lot. We also need to 
+                        * set the time here, as it can be read by 
+                        * FindFirst/FindNext and with the patch for bug #2045
+                        * in smbd/fileio.c it ensures that this timestamp is
+                        * kept sticky even after a write. We save the request
+                        * away and will set it on file close and after a write. JRA.
+                        */
+
+                       if (tvs.modtime != (time_t)0 && tvs.modtime != (time_t)-1) {
+                               DEBUG(10,("call_trans2setfilepathinfo: setting pending modtime to %s\n", ctime(&tvs.modtime) ));
+                               fsp_set_pending_modtime(fsp, tvs.modtime);
+                       }
+
+               }
+               DEBUG(10,("call_trans2setfilepathinfo: setting utimes to modified values.\n"));
+
+               if(file_utime(conn, fname, &tvs)!=0) {
+                       return(UNIXERROR(ERRDOS,ERRnoaccess));
+               }
+       }
+
        SSVAL(params,0,0);
        send_trans2_replies(outbuf, bufsize, params, 2, *ppdata, 0);
   
@@ -3268,24 +4409,28 @@ size = %.0f, uid = %u, gid = %u, raw perms = 0%o\n",
  Reply to a TRANS2_MKDIR (make directory with extended attributes).
 ****************************************************************************/
 
-static int call_trans2mkdir(connection_struct *conn,
-                           char *inbuf, char *outbuf, int length, int bufsize,
-                               char **pparams, int total_params, char **ppdata, int total_data)
+static int call_trans2mkdir(connection_struct *conn, char *inbuf, char *outbuf, int length, int bufsize,
+                                       char **pparams, int total_params, char **ppdata, int total_data,
+                                       unsigned int max_data_bytes)
 {
        char *params = *pparams;
+       char *pdata = *ppdata;
        pstring directory;
        int ret = -1;
        SMB_STRUCT_STAT sbuf;
        BOOL bad_path = False;
        NTSTATUS status = NT_STATUS_OK;
+       TALLOC_CTX *ctx = NULL;
+       struct ea_list *ea_list = NULL;
 
        if (!CAN_WRITE(conn))
                return ERROR_DOS(ERRSRV,ERRaccess);
 
-       if (total_params < 4)
-               return(ERROR_DOS(ERRDOS,ERRinvalidparam));
+       if (total_params < 4) {
+               return ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+       }
 
-       srvstr_get_path(inbuf, directory, &params[4], sizeof(directory), -1, STR_TERMINATE, &status);
+       srvstr_get_path(inbuf, directory, &params[4], sizeof(directory), -1, STR_TERMINATE, &status, False);
        if (!NT_STATUS_IS_OK(status)) {
                return ERROR_NT(status);
        }
@@ -3293,18 +4438,61 @@ static int call_trans2mkdir(connection_struct *conn,
        DEBUG(3,("call_trans2mkdir : name = %s\n", directory));
 
        unix_convert(directory,conn,0,&bad_path,&sbuf);
-       if (check_name(directory,conn))
-               ret = vfs_MkDir(conn,directory,unix_mode(conn,aDIR,directory));
+       if (bad_path) {
+               return ERROR_NT(NT_STATUS_OBJECT_PATH_NOT_FOUND);
+       }
+
+       /* Any data in this call is an EA list. */
+       if (total_data && !lp_ea_support(SNUM(conn))) {
+               return ERROR_NT(NT_STATUS_EAS_NOT_SUPPORTED);
+       }
+
+       if (total_data) {
+               if (total_data < 10) {
+                       return ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+               }
+
+               if (IVAL(pdata,0) > total_data) {
+                       DEBUG(10,("call_trans2mkdir: bad total data size (%u) > %u\n",
+                               IVAL(pdata,0), (unsigned int)total_data));
+                       return ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+               }
+
+               ctx = talloc_init("TRANS2_MKDIR_SET_EA");
+               if (!ctx) {
+                       return ERROR_NT(NT_STATUS_NO_MEMORY);
+               }
+               ea_list = read_ea_list(ctx, pdata + 4, total_data - 4);
+               if (!ea_list) {
+                       talloc_destroy(ctx);
+                       return ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+               }
+       }
+
+       if (check_name(directory,conn)) {
+               ret = vfs_MkDir(conn,directory,unix_mode(conn,aDIR,directory,True));
+       }
   
        if(ret < 0) {
+               talloc_destroy(ctx);
                DEBUG(5,("call_trans2mkdir error (%s)\n", strerror(errno)));
                return set_bad_path_error(errno, bad_path, outbuf, ERRDOS,ERRnoaccess);
        }
 
+       /* Try and set any given EA. */
+       if (total_data) {
+               status = set_ea(conn, NULL, directory, ea_list);
+               talloc_destroy(ctx);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return ERROR_NT(status);
+               }
+       }
+
        /* Realloc the parameter and data sizes */
-       params = Realloc(*pparams,2);
-       if(params == NULL)
-               return ERROR_DOS(ERRDOS,ERRnomem);
+       params = SMB_REALLOC(*pparams,2);
+       if(params == NULL) {
+               return ERROR_NT(NT_STATUS_NO_MEMORY);
+       }
        *pparams = params;
 
        SSVAL(params,0,0);
@@ -3319,16 +4507,17 @@ static int call_trans2mkdir(connection_struct *conn,
  We don't actually do this - we just send a null response.
 ****************************************************************************/
 
-static int call_trans2findnotifyfirst(connection_struct *conn,
-                                       char *inbuf, char *outbuf, int length, int bufsize,
-                                       char **pparams, int total_params, char **ppdata, int total_data)
+static int call_trans2findnotifyfirst(connection_struct *conn, char *inbuf, char *outbuf, int length, int bufsize,
+                                       char **pparams, int total_params, char **ppdata, int total_data,
+                                       unsigned int max_data_bytes)
 {
        static uint16 fnf_handle = 257;
        char *params = *pparams;
        uint16 info_level;
 
-       if (total_params < 6)
-               return(ERROR_DOS(ERRDOS,ERRinvalidparam));
+       if (total_params < 6) {
+               return ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+       }
 
        info_level = SVAL(params,4);
        DEBUG(3,("call_trans2findnotifyfirst - info_level %d\n", info_level));
@@ -3342,9 +4531,10 @@ static int call_trans2findnotifyfirst(connection_struct *conn,
        }
 
        /* Realloc the parameter and data sizes */
-       params = Realloc(*pparams,6);
-       if(params == NULL) 
-               return ERROR_DOS(ERRDOS,ERRnomem);
+       params = SMB_REALLOC(*pparams,6);
+       if(params == NULL) {
+               return ERROR_NT(NT_STATUS_NO_MEMORY);
+       }
        *pparams = params;
 
        SSVAL(params,0,fnf_handle);
@@ -3366,18 +4556,19 @@ static int call_trans2findnotifyfirst(connection_struct *conn,
  changes). Currently this does nothing.
 ****************************************************************************/
 
-static int call_trans2findnotifynext(connection_struct *conn,
-                                       char *inbuf, char *outbuf, int length, int bufsize,
-                                       char **pparams, int total_params, char **ppdata, int total_data)
+static int call_trans2findnotifynext(connection_struct *conn, char *inbuf, char *outbuf, int length, int bufsize,
+                                       char **pparams, int total_params, char **ppdata, int total_data,
+                                       unsigned int max_data_bytes)
 {
        char *params = *pparams;
 
        DEBUG(3,("call_trans2findnotifynext\n"));
 
        /* Realloc the parameter and data sizes */
-       params = Realloc(*pparams,4);
-       if(params == NULL)
-               return ERROR_DOS(ERRDOS,ERRnomem);
+       params = SMB_REALLOC(*pparams,4);
+       if(params == NULL) {
+               return ERROR_NT(NT_STATUS_NO_MEMORY);
+       }
        *pparams = params;
 
        SSVAL(params,0,0); /* No changes */
@@ -3392,9 +4583,9 @@ static int call_trans2findnotifynext(connection_struct *conn,
  Reply to a TRANS2_GET_DFS_REFERRAL - Shirish Kalele <kalele@veritas.com>.
 ****************************************************************************/
 
-static int call_trans2getdfsreferral(connection_struct *conn, char* inbuf,
-                                       char* outbuf, int length, int bufsize,
-                                       char **pparams, int total_params, char **ppdata, int total_data)
+static int call_trans2getdfsreferral(connection_struct *conn, char* inbuf, char* outbuf, int length, int bufsize,
+                                       char **pparams, int total_params, char **ppdata, int total_data,
+                                       unsigned int max_data_bytes)
 {
        char *params = *pparams;
        pstring pathname;
@@ -3403,8 +4594,9 @@ static int call_trans2getdfsreferral(connection_struct *conn, char* inbuf,
 
        DEBUG(10,("call_trans2getdfsreferral\n"));
 
-       if (total_params < 2)
-               return(ERROR_DOS(ERRDOS,ERRinvalidparam));
+       if (total_params < 2) {
+               return ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+       }
 
        max_referral_level = SVAL(params,0);
 
@@ -3428,9 +4620,9 @@ static int call_trans2getdfsreferral(connection_struct *conn, char* inbuf,
  Reply to a TRANS2_IOCTL - used for OS/2 printing.
 ****************************************************************************/
 
-static int call_trans2ioctl(connection_struct *conn, char* inbuf,
-                                       char* outbuf, int length, int bufsize,
-                                       char **pparams, int total_params, char **ppdata, int total_data)
+static int call_trans2ioctl(connection_struct *conn, char* inbuf, char* outbuf, int length, int bufsize,
+                                       char **pparams, int total_params, char **ppdata, int total_data,
+                                       unsigned int max_data_bytes)
 {
        char *pdata = *ppdata;
        files_struct *fsp = file_fsp(inbuf,smb_vwv15);
@@ -3442,9 +4634,10 @@ static int call_trans2ioctl(connection_struct *conn, char* inbuf,
 
        if ((SVAL(inbuf,(smb_setup+4)) == LMCAT_SPL) &&
                        (SVAL(inbuf,(smb_setup+6)) == LMFUNC_GETJOBID)) {
-               pdata = Realloc(*ppdata, 32);
-               if(pdata == NULL)
-                       return ERROR_DOS(ERRDOS,ERRnomem);
+               pdata = SMB_REALLOC(*ppdata, 32);
+               if(pdata == NULL) {
+                       return ERROR_NT(NT_STATUS_NO_MEMORY);
+               }
                *ppdata = pdata;
 
                /* NOTE - THIS IS ASCII ONLY AT THE MOMENT - NOT SURE IF OS/2
@@ -3534,9 +4727,9 @@ int reply_trans2(connection_struct *conn,
        int outsize = 0;
        unsigned int total_params = SVAL(inbuf, smb_tpscnt);
        unsigned int total_data =SVAL(inbuf, smb_tdscnt);
+       unsigned int max_data_bytes = SVAL(inbuf, smb_mdrcnt);
 #if 0
        unsigned int max_param_reply = SVAL(inbuf, smb_mprcnt);
-       unsigned int max_data_reply = SVAL(inbuf, smb_mdrcnt);
        unsigned int max_setup_fields = SVAL(inbuf, smb_msrcnt);
        BOOL close_tid = BITSETW(inbuf+smb_flags,0);
        BOOL no_final_response = BITSETW(inbuf+smb_flags,1);
@@ -3587,22 +4780,22 @@ int reply_trans2(connection_struct *conn,
                        DEBUG(2,("Invalid smb_sucnt in trans2 call(%u)\n",suwcnt));
                        DEBUG(2,("Transaction is %d\n",tran_call));
                        END_PROFILE(SMBtrans2);
-                       ERROR_DOS(ERRDOS,ERRinvalidparam);
+                       return ERROR_NT(NT_STATUS_INVALID_PARAMETER);
                }
        }
     
        /* Allocate the space for the maximum needed parameters and data */
        if (total_params > 0)
-               params = (char *)malloc(total_params);
+               params = (char *)SMB_MALLOC(total_params);
        if (total_data > 0)
-               data = (char *)malloc(total_data);
+               data = (char *)SMB_MALLOC(total_data);
   
        if ((total_params && !params)  || (total_data && !data)) {
                DEBUG(2,("Out of memory in reply_trans2\n"));
                SAFE_FREE(params);
                SAFE_FREE(data); 
                END_PROFILE(SMBtrans2);
-               return ERROR_DOS(ERRDOS,ERRnomem);
+               return ERROR_NT(NT_STATUS_NO_MEMORY);
        }
 
        /* Copy the param and data bytes sent with this request into
@@ -3691,7 +4884,7 @@ int reply_trans2(connection_struct *conn,
                                goto bad_param;
                        
                        if (num_params) {
-                               if (param_disp + num_params >= total_params)
+                               if (param_disp + num_params > total_params)
                                        goto bad_param;
                                if ((param_disp + num_params < param_disp) ||
                                                (param_disp + num_params < num_params))
@@ -3707,7 +4900,7 @@ int reply_trans2(connection_struct *conn,
                                memcpy( &params[param_disp], smb_base(inbuf) + param_off, num_params);
                        }
                        if (num_data) {
-                               if (data_disp + num_data >= total_data)
+                               if (data_disp + num_data > total_data)
                                        goto bad_param;
                                if ((data_disp + num_data < data_disp) ||
                                                (data_disp + num_data < num_data))
@@ -3734,28 +4927,28 @@ int reply_trans2(connection_struct *conn,
        case TRANSACT2_OPEN:
                START_PROFILE_NESTED(Trans2_open);
                outsize = call_trans2open(conn, inbuf, outbuf, bufsize, 
-                                         &params, total_params, &data, total_data);
+                                         &params, total_params, &data, total_data, max_data_bytes);
                END_PROFILE_NESTED(Trans2_open);
                break;
 
        case TRANSACT2_FINDFIRST:
                START_PROFILE_NESTED(Trans2_findfirst);
                outsize = call_trans2findfirst(conn, inbuf, outbuf, bufsize,
-                                         &params, total_params, &data, total_data);
+                                         &params, total_params, &data, total_data, max_data_bytes);
                END_PROFILE_NESTED(Trans2_findfirst);
                break;
 
        case TRANSACT2_FINDNEXT:
                START_PROFILE_NESTED(Trans2_findnext);
                outsize = call_trans2findnext(conn, inbuf, outbuf, length, bufsize, 
-                                         &params, total_params, &data, total_data);
+                                         &params, total_params, &data, total_data, max_data_bytes);
                END_PROFILE_NESTED(Trans2_findnext);
                break;
 
        case TRANSACT2_QFSINFO:
                START_PROFILE_NESTED(Trans2_qfsinfo);
                outsize = call_trans2qfsinfo(conn, inbuf, outbuf, length, bufsize,
-                                         &params, total_params, &data, total_data);
+                                         &params, total_params, &data, total_data, max_data_bytes);
                END_PROFILE_NESTED(Trans2_qfsinfo);
            break;
 
@@ -3763,7 +4956,7 @@ int reply_trans2(connection_struct *conn,
        case TRANSACT2_SETFSINFO:
                START_PROFILE_NESTED(Trans2_setfsinfo);
                outsize = call_trans2setfsinfo(conn, inbuf, outbuf, length, bufsize, 
-                                         &params, total_params, &data, total_data);
+                                         &params, total_params, &data, total_data, max_data_bytes);
                END_PROFILE_NESTED(Trans2_setfsinfo);
                break;
 #endif
@@ -3771,47 +4964,47 @@ int reply_trans2(connection_struct *conn,
        case TRANSACT2_QFILEINFO:
                START_PROFILE_NESTED(Trans2_qpathinfo);
                outsize = call_trans2qfilepathinfo(conn, inbuf, outbuf, length, bufsize, 
-                                         &params, total_params, &data, total_data);
+                                         &params, total_params, &data, total_data, max_data_bytes);
                END_PROFILE_NESTED(Trans2_qpathinfo);
                break;
        case TRANSACT2_SETPATHINFO:
        case TRANSACT2_SETFILEINFO:
                START_PROFILE_NESTED(Trans2_setpathinfo);
                outsize = call_trans2setfilepathinfo(conn, inbuf, outbuf, length, bufsize, 
-                                         &params, total_params, &data, total_data);
+                                         &params, total_params, &data, total_data, max_data_bytes);
                END_PROFILE_NESTED(Trans2_setpathinfo);
                break;
 
        case TRANSACT2_FINDNOTIFYFIRST:
                START_PROFILE_NESTED(Trans2_findnotifyfirst);
                outsize = call_trans2findnotifyfirst(conn, inbuf, outbuf, length, bufsize, 
-                                         &params, total_params, &data, total_data);
+                                         &params, total_params, &data, total_data, max_data_bytes);
                END_PROFILE_NESTED(Trans2_findnotifyfirst);
                break;
 
        case TRANSACT2_FINDNOTIFYNEXT:
                START_PROFILE_NESTED(Trans2_findnotifynext);
                outsize = call_trans2findnotifynext(conn, inbuf, outbuf, length, bufsize, 
-                                         &params, total_params, &data, total_data);
+                                         &params, total_params, &data, total_data, max_data_bytes);
                END_PROFILE_NESTED(Trans2_findnotifynext);
                break;
        case TRANSACT2_MKDIR:
                START_PROFILE_NESTED(Trans2_mkdir);
                outsize = call_trans2mkdir(conn, inbuf, outbuf, length, bufsize,
-                                         &params, total_params, &data, total_data);
+                                         &params, total_params, &data, total_data, max_data_bytes);
                END_PROFILE_NESTED(Trans2_mkdir);
                break;
 
        case TRANSACT2_GET_DFS_REFERRAL:
                START_PROFILE_NESTED(Trans2_get_dfs_referral);
                outsize = call_trans2getdfsreferral(conn,inbuf,outbuf,length, bufsize,
-                                         &params, total_params, &data, total_data);
+                                         &params, total_params, &data, total_data, max_data_bytes);
                END_PROFILE_NESTED(Trans2_get_dfs_referral);
                break;
        case TRANSACT2_IOCTL:
                START_PROFILE_NESTED(Trans2_ioctl);
                outsize = call_trans2ioctl(conn,inbuf,outbuf,length, bufsize,
-                                         &params, total_params, &data, total_data);
+                                         &params, total_params, &data, total_data, max_data_bytes);
                END_PROFILE_NESTED(Trans2_ioctl);
                break;
        default: