moved trans2.h and nterr.h into includes.h with all our other includes
[abartlet/samba.git/.git] / source3 / smbd / trans2.c
index 550be0df41103744321c5444579999309f666ae6..4e502f767bdc13086479965e8ecdcb57895cfbae 100644 (file)
 */
 
 #include "includes.h"
-#include "trans2.h"
 
 extern int DEBUGLEVEL;
 extern int Protocol;
 extern BOOL case_sensitive;
-extern int Client;
 extern int smb_read_error;
 extern fstring local_machine;
 extern int global_oplock_break;
+extern uint32 global_client_caps;
 
 /****************************************************************************
   Send the required number of replies back.
@@ -65,7 +64,7 @@ static int send_trans2_replies(char *outbuf, int bufsize, char *params,
      the empty packet */
   if(params_to_send == 0 && data_to_send == 0)
   {
-    send_smb(Client,outbuf);
+    send_smb(smbd_server_fd(),outbuf);
     return 0;
   }
 
@@ -94,7 +93,14 @@ static int send_trans2_replies(char *outbuf, int bufsize, char *params,
     total_sent_thistime = params_to_send + data_to_send + 
                             alignment_offset + data_alignment_offset;
     /* We can never send more than useable_space */
-    total_sent_thistime = MIN(total_sent_thistime, useable_space);
+    /*
+     * Note that 'useable_space' does not include the alignment offsets,
+     * but we must include the alignment offsets in the calculation of
+     * the length of the data we send over the wire, as the alignment offsets
+     * are sent here. Fix from Marc_Jacobsen@hp.com.
+     */
+    total_sent_thistime = MIN(total_sent_thistime, useable_space+
+                               alignment_offset + data_alignment_offset);
 
     set_message(outbuf, 10, total_sent_thistime, True);
 
@@ -110,21 +116,19 @@ static int send_trans2_replies(char *outbuf, int bufsize, char *params,
     data_sent_thistime = MIN(data_sent_thistime,data_to_send);
 
     SSVAL(outbuf,smb_prcnt, params_sent_thistime);
+
+    /* smb_proff is the offset from the start of the SMB header to the
+       parameter bytes, however the first 4 bytes of outbuf are
+       the Netbios over TCP header. Thus use smb_base() to subtract
+       them from the calculation */
+
+    SSVAL(outbuf,smb_proff,((smb_buf(outbuf)+alignment_offset) - smb_base(outbuf)));
+
     if(params_sent_thistime == 0)
-    {
-      SSVAL(outbuf,smb_proff,0);
       SSVAL(outbuf,smb_prdisp,0);
-    }
     else
-    {
-      /* smb_proff is the offset from the start of the SMB header to the
-         parameter bytes, however the first 4 bytes of outbuf are
-         the Netbios over TCP header. Thus use smb_base() to subtract
-         them from the calculation */
-      SSVAL(outbuf,smb_proff,((smb_buf(outbuf)+alignment_offset) - smb_base(outbuf)));
       /* Absolute displacement of param bytes sent in this packet */
       SSVAL(outbuf,smb_prdisp,pp - params);
-    }
 
     SSVAL(outbuf,smb_drcnt, data_sent_thistime);
     if(data_sent_thistime == 0)
@@ -155,7 +159,7 @@ static int send_trans2_replies(char *outbuf, int bufsize, char *params,
           params_to_send, data_to_send, paramsize, datasize));
 
     /* Send the packet */
-    send_smb(Client,outbuf);
+    send_smb(smbd_server_fd(),outbuf);
 
     pp += params_sent_thistime;
     pd += data_sent_thistime;
@@ -216,10 +220,6 @@ static int call_trans2open(connection_struct *conn, char *inbuf, char *outbuf,
 
   unix_convert(fname,conn,0,&bad_path,NULL);
     
-  fsp = file_new();
-  if (!fsp)
-    return(ERROR(ERRSRV,ERRnofids));
-
   if (!check_name(fname,conn))
   {
     if((errno == ENOENT) && bad_path)
@@ -227,27 +227,25 @@ static int call_trans2open(connection_struct *conn, char *inbuf, char *outbuf,
       unix_ERR_class = ERRDOS;
       unix_ERR_code = ERRbadpath;
     }
-    file_free(fsp);
     return(UNIXERROR(ERRDOS,ERRnoaccess));
   }
 
-  unixmode = unix_mode(conn,open_attr | aARCH);
+  unixmode = unix_mode(conn,open_attr | aARCH, fname);
       
-  open_file_shared(fsp,conn,fname,open_mode,open_ofun,unixmode,
+  fsp = open_file_shared(conn,fname,open_mode,open_ofun,unixmode,
                   oplock_request, &rmode,&smb_action);
       
-  if (!fsp->open)
+  if (!fsp)
   {
     if((errno == ENOENT) && bad_path)
     {
       unix_ERR_class = ERRDOS;
       unix_ERR_code = ERRbadpath;
     }
-    file_free(fsp);
     return(UNIXERROR(ERRDOS,ERRnoaccess));
   }
 
-  if (sys_fstat(fsp->fd_ptr->fd,&sbuf) != 0) {
+  if (fsp->conn->vfs_ops.fstat(fsp->fd,&sbuf) != 0) {
     close_file(fsp,False);
     return(UNIXERROR(ERRDOS,ERRnoaccess));
   }
@@ -266,7 +264,7 @@ static int call_trans2open(connection_struct *conn, char *inbuf, char *outbuf,
   if(params == NULL)
     return(ERROR(ERRDOS,ERRnomem));
 
-  bzero(params,28);
+  memset((char *)params,'\0',28);
   SSVAL(params,0,fsp->fnum);
   SSVAL(params,2,fmode);
   put_dos_date2(params,4, mtime);
@@ -292,12 +290,12 @@ static int call_trans2open(connection_struct *conn, char *inbuf, char *outbuf,
 /****************************************************************************
   get a level dependent lanman2 dir entry.
 ****************************************************************************/
-static int get_lanman2_dir_entry(connection_struct *conn,
+static BOOL get_lanman2_dir_entry(connection_struct *conn,
                                 char *path_mask,int dirtype,int info_level,
                                 int requires_resume_key,
                                 BOOL dont_descend,char **ppdata, 
                                 char *base_data, int space_remaining, 
-                                BOOL *out_of_space,
+                                BOOL *out_of_space, BOOL *got_exact_match,
                                 int *last_name_off)
 {
   char *dname;
@@ -314,15 +312,13 @@ static int get_lanman2_dir_entry(connection_struct *conn,
   uint32 len;
   time_t mdate=0, adate=0, cdate=0;
   char *nameptr;
-  BOOL isrootdir = (strequal(conn->dirpath,"./") ||
-                   strequal(conn->dirpath,".") ||
-                   strequal(conn->dirpath,"/"));
   BOOL was_8_3;
   int nt_extmode; /* Used for NT connections instead of mode */
   BOOL needslash = ( conn->dirpath[strlen(conn->dirpath) -1] != '/');
 
   *fname = 0;
   *out_of_space = False;
+  *got_exact_match = False;
 
   if (!conn->dirptr)
     return(False);
@@ -340,6 +336,8 @@ static int get_lanman2_dir_entry(connection_struct *conn,
 
   while (!found)
   {
+    BOOL got_match;
+
     /* Needed if we run out of space */
     prev_dirpos = TellDir(conn->dirptr);
     dname = ReadDirName(conn->dirptr);
@@ -361,20 +359,36 @@ static int get_lanman2_dir_entry(connection_struct *conn,
 
     pstrcpy(fname,dname);      
 
-    if(mask_match(fname, mask, case_sensitive, True))
+    if(!(got_match = *got_exact_match = exact_match(fname, mask, case_sensitive)))
+      got_match = mask_match(fname, mask, case_sensitive, True);
+
+    if(!got_match && !is_8_3(fname, False)) {
+
+      /*
+       * It turns out that NT matches wildcards against
+       * both long *and* short names. This may explain some
+       * of the wildcard wierdness from old DOS clients
+       * that some people have been seeing.... JRA.
+       */
+
+      pstring newname;
+      pstrcpy( newname, fname);
+      name_map_mangle( 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, True);
+    }
+
+    if(got_match)
     {
       BOOL isdots = (strequal(fname,"..") || strequal(fname,"."));
       if (dont_descend && !isdots)
         continue;
          
-      if (isrootdir && isdots)
-        continue;
-
       pstrcpy(pathreal,conn->dirpath);
       if(needslash)
         pstrcat(pathreal,"/");
       pstrcat(pathreal,dname);
-      if (dos_stat(pathreal,&sbuf) != 0) 
+      if (conn->vfs_ops.stat(dos_to_unix(pathreal,False),&sbuf) != 0) 
       {
         DEBUG(5,("get_lanman2_dir_entry:Couldn't stat [%s] (%s)\n",pathreal,strerror(errno)));
         continue;
@@ -400,7 +414,7 @@ static int get_lanman2_dir_entry(connection_struct *conn,
     }
   }
 
-  name_map_mangle(fname,False,SNUM(conn));
+  name_map_mangle(fname,False,True,SNUM(conn));
 
   p = pdata;
   nameptr = p;
@@ -418,7 +432,7 @@ static int get_lanman2_dir_entry(connection_struct *conn,
       put_dos_date2(p,l1_fdateLastAccess,adate);
       put_dos_date2(p,l1_fdateLastWrite,mdate);
       SIVAL(p,l1_cbFile,(uint32)size);
-      SIVAL(p,l1_cbFileAlloc,ROUNDUP(size,1024));
+      SIVAL(p,l1_cbFileAlloc,SMB_ROUNDUP(size,1024));
       SSVAL(p,l1_attrFile,mode);
       SCVAL(p,l1_cchName,strlen(fname));
       pstrcpy(p + l1_achName, fname);
@@ -436,7 +450,7 @@ static int get_lanman2_dir_entry(connection_struct *conn,
       put_dos_date2(p,l2_fdateLastAccess,adate);
       put_dos_date2(p,l2_fdateLastWrite,mdate);
       SIVAL(p,l2_cbFile,(uint32)size);
-      SIVAL(p,l2_cbFileAlloc,ROUNDUP(size,1024));
+      SIVAL(p,l2_cbFileAlloc,SMB_ROUNDUP(size,1024));
       SSVAL(p,l2_attrFile,mode);
       SIVAL(p,l2_cbList,0); /* No extended attributes */
       SCVAL(p,l2_cchName,strlen(fname));
@@ -451,7 +465,7 @@ static int get_lanman2_dir_entry(connection_struct *conn,
       put_dos_date2(p,8,adate);
       put_dos_date2(p,12,mdate);
       SIVAL(p,16,(uint32)size);
-      SIVAL(p,20,ROUNDUP(size,1024));
+      SIVAL(p,20,SMB_ROUNDUP(size,1024));
       SSVAL(p,24,mode);
       SIVAL(p,26,4);
       CVAL(p,30) = strlen(fname);
@@ -470,7 +484,7 @@ static int get_lanman2_dir_entry(connection_struct *conn,
       put_dos_date2(p,8,adate);
       put_dos_date2(p,12,mdate);
       SIVAL(p,16,(uint32)size);
-      SIVAL(p,20,ROUNDUP(size,1024));
+      SIVAL(p,20,SMB_ROUNDUP(size,1024));
       SSVAL(p,24,mode);
       CVAL(p,32) = strlen(fname);
       pstrcpy(p + 33, fname);
@@ -496,7 +510,7 @@ static int get_lanman2_dir_entry(connection_struct *conn,
       SIVAL(p,0,0); p += 4;
       if (!was_8_3) {
         pstrcpy(p+2,fname);
-        if (!name_map_mangle(p+2,True,SNUM(conn)))
+        if (!name_map_mangle(p+2,True,True,SNUM(conn)))
           (p+2)[12] = 0;
       } else
         *(p+2) = 0;
@@ -573,6 +587,7 @@ static int get_lanman2_dir_entry(connection_struct *conn,
   *last_name_off = PTR_DIFF(nameptr,base_data);
   /* Advance the data pointer to the next slot */
   *ppdata = p;
+
   return(found);
 }
   
@@ -604,8 +619,9 @@ void mask_convert( char *mask)
 }
 
 /****************************************************************************
-  reply to a TRANS2_FINDFIRST
+ Reply to a TRANS2_FINDFIRST.
 ****************************************************************************/
+
 static int call_trans2findfirst(connection_struct *conn,
                                char *inbuf, char *outbuf, int bufsize,  
                                char **pparams, char **ppdata)
@@ -661,6 +677,8 @@ static int call_trans2findfirst(connection_struct *conn,
   pstrcpy(directory, params + 12); /* Complete directory path with 
                                     wildcard mask appended */
 
+  RESOLVE_FINDFIRST_DFSPATH(directory, conn, inbuf, outbuf);
+
   DEBUG(5,("path=%s\n",directory));
 
   unix_convert(directory,conn,0,&bad_path,NULL);
@@ -698,40 +716,25 @@ static int call_trans2findfirst(connection_struct *conn,
   pdata = *ppdata = Realloc(*ppdata, max_data_bytes + 1024);
   if(!*ppdata)
     return(ERROR(ERRDOS,ERRnomem));
-  bzero(pdata,max_data_bytes);
+  memset((char *)pdata,'\0',max_data_bytes + 1024);
 
   /* Realloc the params space */
   params = *pparams = Realloc(*pparams, 10);
   if(params == NULL)
     return(ERROR(ERRDOS,ERRnomem));
 
-  dptr_num = dptr_create(conn,directory, True ,SVAL(inbuf,smb_pid));
+  dptr_num = dptr_create(conn,directory, False, True ,SVAL(inbuf,smb_pid));
   if (dptr_num < 0)
     return(UNIXERROR(ERRDOS,ERRbadfile));
 
   /* Convert the formatted mask. */
   mask_convert(mask);
 
-#if 0 /* JRA */
-  /*
-   * Now we have a working mask_match in util.c, I believe
-   * we no longer need these hacks (in fact they break
-   * things). JRA. 
-   */
-
-  /* a special case for 16 bit apps */
-  if (strequal(mask,"????????.???")) pstrcpy(mask,"*");
-
-  /* handle broken clients that send us old 8.3 format */
-  string_sub(mask,"????????","*");
-  string_sub(mask,".???",".*");
-#endif /* JRA */
-
   /* 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))) {
-    dptr_close(dptr_num);
+    dptr_close(&dptr_num);
     return(ERROR(ERRDOS,ERRnomem));
   }
 
@@ -753,39 +756,49 @@ static int call_trans2findfirst(connection_struct *conn,
   out_of_space = False;
 
   for (i=0;(i<maxentries) && !finished && !out_of_space;i++)
+  {
+    BOOL got_exact_match = False;
+
+    /* this is a heuristic to avoid seeking the dirptr except when 
+       absolutely necessary. It allows for a filename of about 40 chars */
+    if (space_remaining < DIRLEN_GUESS && numentries > 0)
     {
+      out_of_space = True;
+      finished = False;
+    }
+    else
+    {
+      finished = !get_lanman2_dir_entry(conn,mask,dirtype,info_level,
+                   requires_resume_key,dont_descend,
+                   &p,pdata,space_remaining, &out_of_space, &got_exact_match,
+                   &last_name_off);
+    }
 
-      /* this is a heuristic to avoid seeking the dirptr except when 
-        absolutely necessary. It allows for a filename of about 40 chars */
-      if (space_remaining < DIRLEN_GUESS && numentries > 0)
-       {
-         out_of_space = True;
-         finished = False;
-       }
-      else
-       {
-         finished = 
-           !get_lanman2_dir_entry(conn,mask,dirtype,info_level,
-                                  requires_resume_key,dont_descend,
-                                  &p,pdata,space_remaining, &out_of_space,
-                                  &last_name_off);
-       }
+    if (finished && out_of_space)
+      finished = False;
 
-      if (finished && out_of_space)
-       finished = False;
+    if (!finished && !out_of_space)
+      numentries++;
 
-      if (!finished && !out_of_space)
-       numentries++;
-      space_remaining = max_data_bytes - PTR_DIFF(p,pdata);
-    }
+    /*
+     * As an optimisation if we know we aren't looking
+     * for a wildcard name (ie. the name matches the wildcard exactly)
+     * then we can finish on any (first) match.
+     * This speeds up large directory searches. JRA.
+     */
+
+    if(got_exact_match)
+      finished = True;
+
+    space_remaining = max_data_bytes - PTR_DIFF(p,pdata);
+  }
   
   /* Check if we can close the dirptr */
   if(close_after_first || (finished && close_if_end))
-    {
-      dptr_close(dptr_num);
-      DEBUG(5,("call_trans2findfirst - (2) closing dptr_num %d\n", dptr_num));
-      dptr_num = -1;
-    }
+  {
+    DEBUG(5,("call_trans2findfirst - (2) closing dptr_num %d\n", dptr_num));
+    dptr_close(&dptr_num);
+  }
 
   /* 
    * If there are no matching entries we must return ERRDOS/ERRbadfile - 
@@ -793,7 +806,10 @@ static int call_trans2findfirst(connection_struct *conn,
    */
 
   if(numentries == 0)
+  {
+    dptr_close(&dptr_num);
     return(ERROR(ERRDOS,ERRbadfile));
+  }
 
   /* At this point pdata points to numentries directory entries. */
 
@@ -813,6 +829,17 @@ static int call_trans2findfirst(connection_struct *conn,
            smb_fn_name(CVAL(inbuf,smb_com)), 
            mask, directory, dirtype, numentries ) );
 
+  /* 
+   * Force a name mangle here to ensure that the
+   * mask as an 8.3 name is top of the mangled cache.
+   * The reasons for this are subtle. Don't remove
+   * this code unless you know what you are doing
+   * (see PR#13758). JRA.
+   */
+
+  if(!is_8_3( mask, False))
+    name_map_mangle(mask, True, True, SNUM(conn));
+
   return(-1);
 }
 
@@ -833,7 +860,7 @@ static int call_trans2findnext(connection_struct *conn,
   int max_data_bytes = SVAL(inbuf, smb_mdrcnt);
   char *params = *pparams;
   char *pdata = *ppdata;
-  int16 dptr_num = SVAL(params,0);
+  int dptr_num = SVAL(params,0);
   int maxentries = SVAL(params,2);
   uint16 info_level = SVAL(params,4);
   uint32 resume_key = IVAL(params,6);
@@ -881,7 +908,7 @@ resume_key = %d resume name = %s continue=%d level = %d\n",
   pdata = *ppdata = Realloc( *ppdata, max_data_bytes + 1024);
   if(!*ppdata)
     return(ERROR(ERRDOS,ERRnomem));
-  bzero(pdata,max_data_bytes);
+  memset((char *)pdata,'\0',max_data_bytes + 1024);
 
   /* Realloc the params space */
   params = *pparams = Realloc(*pparams, 6*SIZEOFWORD);
@@ -958,7 +985,7 @@ resume_key = %d resume name = %s continue=%d level = %d\n",
        */
 
       if(dname != NULL)
-        name_map_mangle( dname, False, SNUM(conn));
+        name_map_mangle( dname, False, True, SNUM(conn));
 
       if(dname && strcsequal( resume_name, dname))
       {
@@ -986,7 +1013,7 @@ resume_key = %d resume name = %s continue=%d level = %d\n",
          */
 
         if(dname != NULL)
-          name_map_mangle( dname, False, SNUM(conn));
+          name_map_mangle( dname, False, True, SNUM(conn));
 
         if(dname && strcsequal( resume_name, dname))
         {
@@ -999,38 +1026,49 @@ resume_key = %d resume name = %s continue=%d level = %d\n",
   } /* end if requires_resume_key && !continue_bit */
 
   for (i=0;(i<(int)maxentries) && !finished && !out_of_space ;i++)
+  {
+    BOOL got_exact_match = False;
+
+    /* this is a heuristic to avoid seeking the dirptr except when 
+       absolutely necessary. It allows for a filename of about 40 chars */
+    if (space_remaining < DIRLEN_GUESS && numentries > 0)
     {
-      /* this is a heuristic to avoid seeking the dirptr except when 
-        absolutely necessary. It allows for a filename of about 40 chars */
-      if (space_remaining < DIRLEN_GUESS && numentries > 0)
-       {
-         out_of_space = True;
-         finished = False;
-       }
-      else
-       {
-         finished = 
-           !get_lanman2_dir_entry(conn,mask,dirtype,info_level,
-                                  requires_resume_key,dont_descend,
-                                  &p,pdata,space_remaining, &out_of_space,
-                                  &last_name_off);
-       }
+      out_of_space = True;
+      finished = False;
+    }
+    else
+    {
+      finished = !get_lanman2_dir_entry(conn,mask,dirtype,info_level,
+                   requires_resume_key,dont_descend,
+                   &p,pdata,space_remaining, &out_of_space, &got_exact_match,
+                   &last_name_off);
+    }
 
-      if (finished && out_of_space)
-       finished = False;
+    if (finished && out_of_space)
+      finished = False;
 
-      if (!finished && !out_of_space)
-       numentries++;
-      space_remaining = max_data_bytes - PTR_DIFF(p,pdata);
-    }
+    if (!finished && !out_of_space)
+      numentries++;
+
+    /*
+     * As an optimisation if we know we aren't looking
+     * for a wildcard name (ie. the name matches the wildcard exactly)
+     * then we can finish on any (first) match.
+     * This speeds up large directory searches. JRA.
+     */
+
+    if(got_exact_match)
+      finished = True;
+
+    space_remaining = max_data_bytes - PTR_DIFF(p,pdata);
+  }
   
   /* Check if we can close the dirptr */
   if(close_after_request || (finished && close_if_end))
-    {
-      dptr_close(dptr_num); /* This frees up the saved mask */
-      DEBUG(5,("call_trans2findnext: closing dptr_num = %d\n", dptr_num));
-      dptr_num = -1;
-    }
+  {
+    DEBUG(5,("call_trans2findnext: closing dptr_num = %d\n", dptr_num));
+    dptr_close(&dptr_num); /* This frees up the saved mask */
+  }
 
 
   /* Set up the return parameter block */
@@ -1060,6 +1098,7 @@ static int call_trans2qfsinfo(connection_struct *conn,
                              int length, int bufsize,
                              char **pparams, char **ppdata)
 {
+  int max_data_bytes = SVAL(inbuf, smb_mdrcnt);
   char *pdata = *ppdata;
   char *params = *pparams;
   uint16 info_level = SVAL(params,0);
@@ -1068,16 +1107,16 @@ static int call_trans2qfsinfo(connection_struct *conn,
   char *vname = volume_label(SNUM(conn));
   int snum = SNUM(conn);
   char *fstype = lp_fstype(SNUM(conn));
-  extern uint32 global_client_caps;
 
   DEBUG(3,("call_trans2qfsinfo: level = %d\n", info_level));
 
-  if(dos_stat(".",&st)!=0) {
+  if(conn->vfs_ops.stat(".",&st)!=0) {
     DEBUG(2,("call_trans2qfsinfo: stat of . failed (%s)\n", strerror(errno)));
     return (ERROR(ERRSRV,ERRinvdevice));
   }
 
-  pdata = *ppdata = Realloc(*ppdata, 1024); bzero(pdata,1024);
+  pdata = *ppdata = Realloc(*ppdata, max_data_bytes + 1024);
+  memset((char *)pdata,'\0',max_data_bytes + 1024);
 
   switch (info_level) 
   {
@@ -1085,7 +1124,7 @@ static int call_trans2qfsinfo(connection_struct *conn,
     {
       SMB_BIG_UINT dfree,dsize,bsize;
       data_len = 18;
-      sys_disk_free(".",&bsize,&dfree,&dsize); 
+      conn->vfs_ops.disk_free(".",False,&bsize,&dfree,&dsize); 
       SIVAL(pdata,l1_idFileSystem,st.st_dev);
       SIVAL(pdata,l1_cSectorUnit,bsize/512);
       SIVAL(pdata,l1_cUnit,dsize);
@@ -1114,16 +1153,22 @@ static int call_trans2qfsinfo(connection_struct *conn,
       break;
     }
     case SMB_QUERY_FS_ATTRIBUTE_INFO:
-      data_len = 12 + 2*strlen(fstype);
-      SIVAL(pdata,0,FILE_CASE_PRESERVED_NAMES); /* FS ATTRIBUTES */
+    {
+      int fstype_len;
+      SIVAL(pdata,0,FILE_CASE_PRESERVED_NAMES|FILE_CASE_SENSITIVE_SEARCH|
+                       FILE_DEVICE_IS_MOUNTED|
+            (lp_nt_acl_support() ? FILE_PERSISTENT_ACLS : 0)); /* FS ATTRIBUTES */
 #if 0 /* Old code. JRA. */
       SIVAL(pdata,0,0x4006); /* FS ATTRIBUTES == long filenames supported? */
 #endif /* Old code. */
+
       SIVAL(pdata,4,128); /* Max filename component length */
-      SIVAL(pdata,8,2*strlen(fstype));
-      PutUniCode(pdata+12,fstype);
+      fstype_len = dos_PutUniCode(pdata+12,unix_to_dos(fstype,False),sizeof(pstring), False);
+      SIVAL(pdata,8,fstype_len);
+      data_len = 12 + fstype_len;
       SSVAL(outbuf,smb_flg2,SVAL(outbuf,smb_flg2)|FLAGS2_UNICODE_STRINGS);
       break;
+    }
     case SMB_QUERY_FS_LABEL_INFO:
       data_len = 4 + strlen(vname);
       SIVAL(pdata,0,strlen(vname));
@@ -1141,26 +1186,27 @@ static int call_trans2qfsinfo(connection_struct *conn,
       /* NT4 always serves this up as unicode but expects it to be
        * delivered as ascii! (tridge && JRA)
        */
-      if (global_client_caps & CAP_NT_SMBS) {
+      if ((get_remote_arch() != RA_WIN2K) && (global_client_caps & CAP_NT_SMBS)) {
              data_len = 18 + strlen(vname);
              SIVAL(pdata,12,strlen(vname));
              pstrcpy(pdata+18,vname);      
       } else {
              data_len = 18 + 2*strlen(vname);
              SIVAL(pdata,12,strlen(vname)*2);
-             PutUniCode(pdata+18,vname);      
+             dos_PutUniCode(pdata+18,unix_to_dos(vname,False),sizeof(pstring), False);
+                 SSVAL(outbuf,smb_flg2,SVAL(outbuf,smb_flg2)|FLAGS2_UNICODE_STRINGS);
       }
 
       DEBUG(5,("call_trans2qfsinfo : SMB_QUERY_FS_VOLUME_INFO namelen = %d, vol = %s\n", 
-              strlen(vname),vname));
+              (int)strlen(vname),vname));
       break;
     case SMB_QUERY_FS_SIZE_INFO:
     {
       SMB_BIG_UINT dfree,dsize,bsize;
       data_len = 24;
-      sys_disk_free(".",&bsize,&dfree,&dsize); 
-      SIVAL(pdata,0,dsize);
-      SIVAL(pdata,8,dfree);
+      conn->vfs_ops.disk_free(".",False,&bsize,&dfree,&dsize); 
+      SBIG_UINT(pdata,0,dsize);
+      SBIG_UINT(pdata,8,dfree);
       SIVAL(pdata,16,bsize/512);
       SIVAL(pdata,20,512);
       break;
@@ -1171,12 +1217,16 @@ static int call_trans2qfsinfo(connection_struct *conn,
       SIVAL(pdata,4,0); /* characteristics */
       break;
     case SMB_MAC_QUERY_FS_INFO:
-      /*
-       * Thursby MAC extension...
-       */
-      data_len = 88;
-      SIVAL(pdata,84,0x100); /* Don't support mac... */
-      break;
+           /*
+            * Thursby MAC extension... ONLY on NTFS filesystems
+            * once we do streams then we don't need this
+            */
+           if (strequal(lp_fstype(SNUM(conn)),"NTFS")) {
+                   data_len = 88;
+                   SIVAL(pdata,84,0x100); /* Don't support mac... */
+                   break;
+           }
+           /* drop through */
     default:
       return(ERROR(ERRDOS,ERRunknownlevel));
   }
@@ -1222,6 +1272,7 @@ static int call_trans2qfilepathinfo(connection_struct *conn,
                                    char **pparams,char **ppdata,
                                    int total_data)
 {
+  int max_data_bytes = SVAL(inbuf, smb_mdrcnt);
   char *params = *pparams;
   char *pdata = *ppdata;
   uint16 tran_call = SVAL(inbuf, smb_setup0);
@@ -1234,29 +1285,68 @@ static int call_trans2qfilepathinfo(connection_struct *conn,
   char *fname;
   char *p;
   int l;
-  SMB_OFF_T pos;
+  SMB_OFF_T pos = 0;
   BOOL bad_path = False;
+  BOOL delete_pending = False;
 
   if (tran_call == TRANSACT2_QFILEINFO) {
     files_struct *fsp = file_fsp(params,0);
     info_level = SVAL(params,2);
 
-    CHECK_FSP(fsp,conn);
-    CHECK_ERROR(fsp);
+    DEBUG(3,("call_trans2qfilepathinfo: TRANSACT2_QFILEINFO: level = %d\n", info_level));
+
+    if(fsp && (fsp->is_directory || fsp->stat_open)) {
+      /*
+       * This is actually a QFILEINFO on a directory
+       * handle (returned from an NT SMB). NT5.0 seems
+       * to do this call. JRA.
+       */
+      fname = fsp->fsp_name;
+      unix_convert(fname,conn,0,&bad_path,&sbuf);
+      if (!check_name(fname,conn) || 
+          (!VALID_STAT(sbuf) && conn->vfs_ops.stat(dos_to_unix(fname,False),&sbuf))) {
+        DEBUG(3,("fileinfo of %s failed (%s)\n",fname,strerror(errno)));
+        if((errno == ENOENT) && bad_path)
+        {
+          unix_ERR_class = ERRDOS;
+          unix_ERR_code = ERRbadpath;
+        }
+        return(UNIXERROR(ERRDOS,ERRbadpath));
+      }
+
+      delete_pending = fsp->directory_delete_on_close;
 
-    fname = fsp->fsp_name;
-    if (sys_fstat(fsp->fd_ptr->fd,&sbuf) != 0) {
-      DEBUG(3,("fstat of fnum %d failed (%s)\n",fsp->fnum, strerror(errno)));
-      return(UNIXERROR(ERRDOS,ERRbadfid));
+    } else {
+      /*
+       * Original code - this is an open file.
+       */
+      CHECK_FSP(fsp,conn);
+      CHECK_ERROR(fsp);
+
+      fname = fsp->fsp_name;
+      if (fsp->conn->vfs_ops.fstat(fsp->fd,&sbuf) != 0) {
+        DEBUG(3,("fstat of fnum %d failed (%s)\n",fsp->fnum, strerror(errno)));
+        return(UNIXERROR(ERRDOS,ERRbadfid));
+      }
+      if((pos = fsp->conn->vfs_ops.lseek(fsp->fd,0,SEEK_CUR)) == -1)
+        return(UNIXERROR(ERRDOS,ERRnoaccess));
+
+      delete_pending = fsp->delete_on_close;
     }
-    pos = sys_lseek(fsp->fd_ptr->fd,0,SEEK_CUR);
   } else {
     /* qpathinfo */
     info_level = SVAL(params,0);
+
+    DEBUG(3,("call_trans2qfilepathinfo: TRANSACT2_QPATHINFO: level = %d\n", info_level));
+
     fname = &fname1[0];
     pstrcpy(fname,&params[6]);
+
+    RESOLVE_DFSPATH(fname, conn, inbuf, outbuf);
+
     unix_convert(fname,conn,0,&bad_path,&sbuf);
-    if (!check_name(fname,conn) || (!VALID_STAT(sbuf) && dos_stat(fname,&sbuf))) {
+    if (!check_name(fname,conn) || 
+        (!VALID_STAT(sbuf) && conn->vfs_ops.stat(dos_to_unix(fname,False),&sbuf))) {
       DEBUG(3,("fileinfo of %s failed (%s)\n",fname,strerror(errno)));
       if((errno == ENOENT) && bad_path)
       {
@@ -1265,7 +1355,6 @@ static int call_trans2qfilepathinfo(connection_struct *conn,
       }
       return(UNIXERROR(ERRDOS,ERRbadpath));
     }
-    pos = 0;
   }
 
 
@@ -1285,8 +1374,9 @@ static int call_trans2qfilepathinfo(connection_struct *conn,
   /* from now on we only want the part after the / */
   fname = p;
   
-  params = *pparams = Realloc(*pparams,2); bzero(params,2);
-  data_size = 1024;
+  params = *pparams = Realloc(*pparams,2);
+  memset((char *)params,'\0',2);
+  data_size = max_data_bytes + 1024;
   pdata = *ppdata = Realloc(*ppdata, data_size); 
 
   if (total_data > 0 && IVAL(pdata,0) == total_data) {
@@ -1295,7 +1385,7 @@ static int call_trans2qfilepathinfo(connection_struct *conn,
     return(ERROR(ERRDOS,ERROR_EAS_NOT_SUPPORTED));
   }
 
-  bzero(pdata,data_size);
+  memset((char *)pdata,'\0',data_size);
 
   switch (info_level) 
     {
@@ -1306,7 +1396,7 @@ static int call_trans2qfilepathinfo(connection_struct *conn,
       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)size);
-      SIVAL(pdata,l1_cbFileAlloc,ROUNDUP(size,1024));
+      SIVAL(pdata,l1_cbFileAlloc,SMB_ROUNDUP(size,1024));
       SSVAL(pdata,l1_attrFile,mode);
       SIVAL(pdata,l1_attrFile+2,4); /* this is what OS2 does */
       break;
@@ -1317,7 +1407,7 @@ static int call_trans2qfilepathinfo(connection_struct *conn,
       put_dos_date2(pdata,4,sbuf.st_atime);
       put_dos_date2(pdata,8,sbuf.st_mtime);
       SIVAL(pdata,12,(uint32)size);
-      SIVAL(pdata,16,ROUNDUP(size,1024));
+      SIVAL(pdata,16,SMB_ROUNDUP(size,1024));
       SIVAL(pdata,20,mode);
       break;
 
@@ -1370,21 +1460,33 @@ static int call_trans2qfilepathinfo(connection_struct *conn,
         /* Mangle if not already 8.3 */
         if(!is_8_3(short_name, True))
         {
-          if(!name_map_mangle(short_name,True,SNUM(conn)))
+          if(!name_map_mangle(short_name,True,True,SNUM(conn)))
             *short_name = '\0';
         }
         strupper(short_name);
         l = strlen(short_name);
-        PutUniCode(pdata + 4, short_name);
+        dos_PutUniCode(pdata + 4, unix_to_dos(short_name,False),sizeof(pstring), False);
         data_size = 4 + (2*l);
         SIVAL(pdata,0,2*l);
       }
       break;
 
     case SMB_QUERY_FILE_NAME_INFO:
+      /*
+       * The first part of this code is essential
+       * to get security descriptors to work on mapped
+       * drives. Don't ask how I discovered this unless
+       * you like hearing about me suffering.... :-). JRA.
+       */
+      if(strequal(".", fname) && (global_client_caps & CAP_UNICODE)) {
+        l = l*2;
+        SSVAL(outbuf,smb_flg2,SVAL(outbuf,smb_flg2)|FLAGS2_UNICODE_STRINGS);
+        dos_PutUniCode(pdata + 4, unix_to_dos("\\",False),sizeof(pstring), False);
+      } else {
+        pstrcpy(pdata+4,fname);
+      }
       data_size = 4 + l;
       SIVAL(pdata,0,l);
-      pstrcpy(pdata+4,fname);
       break;
 
     case SMB_QUERY_FILE_ALLOCATION_INFO:
@@ -1403,9 +1505,10 @@ static int call_trans2qfilepathinfo(connection_struct *conn,
       SOFF_T(pdata,0,size);
       SOFF_T(pdata,8,size);
       SIVAL(pdata,16,sbuf.st_nlink);
-      CVAL(pdata,20) = 0;
+      CVAL(pdata,20) = delete_pending;
       CVAL(pdata,21) = (mode&aDIR)?1:0;
       pdata += 24;
+      SINO_T(pdata,0,(SMB_INO_T)sbuf.st_ino); 
       pdata += 8; /* index number */
       pdata += 4; /* EA info */
       if (mode & aRONLY)
@@ -1413,7 +1516,7 @@ static int call_trans2qfilepathinfo(connection_struct *conn,
       else
         SIVAL(pdata,0,0xd01BF);
       pdata += 4;
-      SIVAL(pdata,0,pos); /* current offset */
+      SOFF_T(pdata,0,pos); /* current offset */
       pdata += 8;
       SIVAL(pdata,0,mode); /* is this the right sort of mode info? */
       pdata += 4;
@@ -1466,23 +1569,47 @@ static int call_trans2setfilepathinfo(connection_struct *conn,
   char *fname;
   int fd = -1;
   BOOL bad_path = False;
+  files_struct *fsp = NULL;
 
   if (!CAN_WRITE(conn))
     return(ERROR(ERRSRV,ERRaccess));
 
   if (tran_call == TRANSACT2_SETFILEINFO) {
-    files_struct *fsp = file_fsp(params,0);
+    fsp = file_fsp(params,0);
     info_level = SVAL(params,2);    
 
-    CHECK_FSP(fsp,conn);
-    CHECK_ERROR(fsp);
+    if(fsp && (fsp->is_directory || fsp->stat_open)) {
+      /*
+       * This is actually a SETFILEINFO on a directory
+       * handle (returned from an NT SMB). NT5.0 seems
+       * to do this call. JRA.
+       */
+      fname = fsp->fsp_name;
+      unix_convert(fname,conn,0,&bad_path,&st);
+      if (!check_name(fname,conn) || 
+          (!VALID_STAT(st) && conn->vfs_ops.stat(dos_to_unix(fname,False),&st))) {
+        DEBUG(3,("fileinfo of %s failed (%s)\n",fname,strerror(errno)));
+        if((errno == ENOENT) && bad_path)
+        {
+          unix_ERR_class = ERRDOS;
+          unix_ERR_code = ERRbadpath;
+        }
+        return(UNIXERROR(ERRDOS,ERRbadpath));
+      }
+    } else {
+      /*
+       * Original code - this is an open file.
+       */
+      CHECK_FSP(fsp,conn);
+      CHECK_ERROR(fsp);
 
-    fname = fsp->fsp_name;
-    fd = fsp->fd_ptr->fd;
+      fname = fsp->fsp_name;
+      fd = fsp->fd;
 
-    if(sys_fstat(fd,&st)!=0) {
-      DEBUG(3,("fstat of %s failed (%s)\n", fname, strerror(errno)));
-      return(UNIXERROR(ERRDOS,ERRbadpath));
+      if (fsp->conn->vfs_ops.fstat(fd,&st) != 0) {
+        DEBUG(3,("fstat of fnum %d failed (%s)\n",fsp->fnum, strerror(errno)));
+        return(UNIXERROR(ERRDOS,ERRbadfid));
+      }
     }
   } else {
     /* set path info */
@@ -1500,7 +1627,7 @@ static int call_trans2setfilepathinfo(connection_struct *conn,
       return(UNIXERROR(ERRDOS,ERRbadpath));
     }
  
-    if(!VALID_STAT(st) && dos_stat(fname,&st)!=0) {
+    if(!VALID_STAT(st) && conn->vfs_ops.stat(dos_to_unix(fname,False),&st)!=0) {
       DEBUG(3,("stat of %s failed (%s)\n", fname, strerror(errno)));
       if((errno == ENOENT) && bad_path)
       {
@@ -1515,10 +1642,12 @@ static int call_trans2setfilepathinfo(connection_struct *conn,
           tran_call,fname,info_level,total_data));
 
   /* Realloc the parameter and data sizes */
-  params = *pparams = Realloc(*pparams,2); SSVAL(params,0,0);
+  params = *pparams = Realloc(*pparams,2);
   if(params == NULL)
     return(ERROR(ERRDOS,ERRnomem));
 
+  SSVAL(params,0,0);
+
   size = st.st_size;
   tvs.modtime = st.st_mtime;
   tvs.actime = st.st_atime;
@@ -1566,14 +1695,25 @@ static int call_trans2setfilepathinfo(connection_struct *conn,
 
     case SMB_SET_FILE_BASIC_INFO:
     {
+      /* Patch to do this correctly from Paul Eggert <eggert@twinsun.com>. */
+      time_t write_time;
+      time_t changed_time;
+
       /* Ignore create time at offset pdata. */
 
       /* access time */
       tvs.actime = interpret_long_date(pdata+8);
 
-      /* write time + changed time, combined. */
-      tvs.modtime=MAX(interpret_long_date(pdata+16),
-                      interpret_long_date(pdata+24));
+      write_time = interpret_long_date(pdata+16);
+      changed_time = interpret_long_date(pdata+24);
+
+      tvs.modtime = MIN(write_time, changed_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 0 /* Needs more testing... */
       /* Test from Luke to prevent Win95 from
@@ -1588,6 +1728,26 @@ static int call_trans2setfilepathinfo(connection_struct *conn,
       break;
     }
 
+    /*
+     * NT seems to use this call with a size of zero
+     * to mean truncate the file. JRA.
+     */
+
+    case SMB_SET_FILE_ALLOCATION_INFO:
+    {
+      SMB_OFF_T newsize = IVAL(pdata,0);
+#ifdef LARGE_SMB_OFF_T
+      newsize |= (((SMB_OFF_T)IVAL(pdata,4)) << 32);
+#else /* LARGE_SMB_OFF_T */
+      if (IVAL(pdata,4) != 0)  /* more than 32 bits? */
+         return(ERROR(ERRDOS,ERRunknownlevel));
+#endif /* LARGE_SMB_OFF_T */
+      DEBUG(10,("call_trans2setfilepathinfo: Set file allocation info for file %s to %.0f\n", fname, (double)newsize ));
+      if(newsize == 0)
+        size = 0;
+      break;
+    }
+
     case SMB_SET_FILE_END_OF_FILE_INFO:
     {
       size = IVAL(pdata,0);
@@ -1597,27 +1757,141 @@ static int call_trans2setfilepathinfo(connection_struct *conn,
       if (IVAL(pdata,4) != 0)  /* more than 32 bits? */
          return(ERROR(ERRDOS,ERRunknownlevel));
 #endif /* LARGE_SMB_OFF_T */
+      DEBUG(10,("call_trans2setfilepathinfo: Set end of file info for file %s to %.0f\n", fname, (double)size ));
       break;
     }
 
-    case SMB_SET_FILE_ALLOCATION_INFO:
-      break; /* We don't need to do anything for this call. */
-
     case SMB_SET_FILE_DISPOSITION_INFO: /* Set delete on close for open file. */
     {
-      if (tran_call == TRANSACT2_SETFILEINFO) {
-        files_struct *fsp = file_fsp(params,0);
+      if ((tran_call == TRANSACT2_SETFILEINFO) && (fsp != NULL))
+      {
+        BOOL delete_on_close = (CVAL(pdata,0) ? True : False);
+
         if(fsp->is_directory)
-          return(ERROR(ERRDOS,ERRnoaccess));
-        /*
-         * TODO - check here is this means set
-         * this flag bit on all open files that
-         * reference this particular dev/inode pair.
-         * If so we'll need to search the open
-         * file entries here and set this flag on 
-         * all of them that match. JRA.
-         */
-        fsp->delete_on_close = CVAL(pdata,0);
+        {
+          fsp->directory_delete_on_close = delete_on_close;
+          DEBUG(10, ("call_trans2setfilepathinfo: %s delete on close flag for fnum = %d, directory %s\n",
+                delete_on_close ? "Added" : "Removed", fsp->fnum, fsp->fsp_name ));
+
+        }
+       else if(fsp->stat_open)
+       {
+          DEBUG(10, ("call_trans2setfilepathinfo: %s delete on close flag for fnum = %d, stat open %s\n",
+                delete_on_close ? "Added" : "Removed", fsp->fnum, fsp->fsp_name ));
+       }
+        else
+        {
+
+          /*
+           * We can only set the delete on close flag if
+           * the share mode contained ALLOW_SHARE_DELETE
+           */
+
+          if(!GET_ALLOW_SHARE_DELETE(fsp->share_mode))
+            return(ERROR(ERRDOS,ERRnoaccess));
+
+          /*
+           * If the flag has been set then
+           * modify the share mode entry for all files we have open
+           * on this device and inode to tell other smbds we have 
+           * changed the delete on close flag.
+           */
+
+          if(delete_on_close && !GET_DELETE_ON_CLOSE_FLAG(fsp->share_mode))
+          {
+            int i;
+            files_struct *iterate_fsp;
+            SMB_DEV_T dev = fsp->dev;
+            SMB_INO_T inode = fsp->inode;
+            int num_share_modes;
+            share_mode_entry *current_shares = NULL;
+
+            if (lock_share_entry_fsp(fsp) == False)
+              return(ERROR(ERRDOS,ERRnoaccess));
+
+            /*
+             * Before we allow this we need to ensure that all current opens
+             * on the file have the GET_ALLOW_SHARE_DELETE flag set. If they
+             * do not then we deny this (as we are essentially deleting the
+             * file at this point.
+             */
+
+            num_share_modes = get_share_modes(conn, dev, inode, &current_shares);
+            for(i = 0; i < num_share_modes; i++)
+            {
+              if(!GET_ALLOW_SHARE_DELETE(current_shares[i].share_mode))
+              {
+                DEBUG(5,("call_trans2setfilepathinfo: refusing to set delete on close flag for fnum = %d, \
+file %s as a share exists that was not opened with FILE_DELETE access.\n",
+                      fsp->fnum, fsp->fsp_name ));
+                /*
+                 * Release the lock.
+                 */
+
+                unlock_share_entry_fsp(fsp);
+
+                /*
+                 * current_shares was malloced by get_share_modes - free it here.
+                 */
+
+                free((char *)current_shares);
+
+                /*
+                 * Even though share violation would be more appropriate here,
+                 * return ERRnoaccess as that's what NT does.
+                 */
+
+                return(ERROR(ERRDOS,ERRnoaccess));
+              }
+            }
+
+            /*
+             * current_shares was malloced by get_share_modes - free it here.
+             */
+
+            free((char *)current_shares);
+
+            DEBUG(10,("call_trans2setfilepathinfo: %s delete on close flag for fnum = %d, file %s\n",
+                 delete_on_close ? "Adding" : "Removing", fsp->fnum, fsp->fsp_name ));
+
+            /*
+             * Go through all files we have open on the same device and
+             * inode (hanging off the same hash bucket) and set the DELETE_ON_CLOSE_FLAG.
+             * Other smbd's that have this file open will have to fend for themselves. We
+             * take care of this (rare) case in close_file(). See the comment there.
+             */
+
+            for(iterate_fsp = file_find_di_first(dev, inode); iterate_fsp;
+                                  iterate_fsp = file_find_di_next(iterate_fsp))
+            {
+              int new_share_mode = (delete_on_close ? 
+                                    (iterate_fsp->share_mode | DELETE_ON_CLOSE_FLAG) :
+                                    (iterate_fsp->share_mode & ~DELETE_ON_CLOSE_FLAG) );
+
+              DEBUG(10,("call_trans2setfilepathinfo: Changing share mode for fnum %d, file %s \
+dev = %x, inode = %.0f from %x to %x\n", 
+                    iterate_fsp->fnum, iterate_fsp->fsp_name, (unsigned int)dev, 
+                    (double)inode, iterate_fsp->share_mode, new_share_mode ));
+
+              if(modify_share_mode(iterate_fsp, new_share_mode, iterate_fsp->oplock_type)==False)
+                DEBUG(0,("call_trans2setfilepathinfo: failed to change delete on close for fnum %d, \
+dev = %x, inode = %.0f\n", iterate_fsp->fnum, (unsigned int)dev, (double)inode));
+            }
+
+            /*
+             * Set the delete on close flag in the reference
+             * counted struct. Delete when the last reference
+             * goes away.
+             */
+           fsp->delete_on_close = delete_on_close;
+
+           unlock_share_entry_fsp(fsp);
+
+           DEBUG(10, ("call_trans2setfilepathinfo: %s delete on close flag for fnum = %d, file %s\n",
+                 delete_on_close ? "Added" : "Removed", fsp->fnum, fsp->fsp_name ));
+
+          } /* end if(delete_on_close && !GET_DELETE_ON_CLOSE_FLAG(fsp->share_mode)) */
+        } /* end if is_directory. */
       } else
         return(ERROR(ERRDOS,ERRunknownlevel));
       break;
@@ -1629,50 +1903,85 @@ static int call_trans2setfilepathinfo(connection_struct *conn,
     }
   }
 
+  /* get some defaults (no modifications) if any info is zero or -1. */
+  if (tvs.actime == (time_t)0 || tvs.actime == (time_t)-1)
+    tvs.actime = st.st_atime;
+
+  if (tvs.modtime == (time_t)0 || tvs.modtime == (time_t)-1)
+    tvs.modtime = st.st_mtime;
+
   DEBUG(6,("actime: %s " , ctime(&tvs.actime)));
   DEBUG(6,("modtime: %s ", ctime(&tvs.modtime)));
   DEBUG(6,("size: %.0f ", (double)size));
   DEBUG(6,("mode: %x\n"  , mode));
 
-  /* get some defaults (no modifications) if any info is zero. */
-  if (!tvs.actime) tvs.actime = st.st_atime;
-  if (!tvs.modtime) tvs.modtime = st.st_mtime;
-  if (!size) size = st.st_size;
+  if(!((info_level == SMB_SET_FILE_END_OF_FILE_INFO) ||
+     (info_level == SMB_SET_FILE_ALLOCATION_INFO))) {
+    /*
+     * Only do this test if we are not explicitly
+     * changing the size of a file.
+     */
+    if (!size)
+      size = st.st_size;
+  }
 
   /* Try and set the times, size and mode of this file -
      if they are different from the current values
    */
-  if (st.st_mtime != tvs.modtime || st.st_atime != tvs.actime)
-  {
-    if(file_utime(conn, fname, &tvs)!=0)
-    {
-      return(UNIXERROR(ERRDOS,ERRnoaccess));
+  if (st.st_mtime != tvs.modtime || st.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 code. 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 (mode != dos_mode(conn, fname, &st) && file_chmod(conn, fname, mode, NULL))
-  {
-    DEBUG(2,("chmod of %s failed (%s)\n", fname, strerror(errno)));
-    return(UNIXERROR(ERRDOS,ERRnoaccess));
+  if ((mode != 0) && (mode != dos_mode(conn, fname, &st))) {
+
+    DEBUG(10,("call_trans2setfilepathinfo: file %s : setting dos mode %x\n",
+          fname, mode ));
+
+    if(file_chmod(conn, fname, mode, NULL)) {
+      DEBUG(2,("chmod of %s failed (%s)\n", fname, strerror(errno)));
+      return(UNIXERROR(ERRDOS,ERRnoaccess));
+    }
   }
 
-  if(size != st.st_size)
-  {
-    if (fd == -1)
-    {
-      fd = dos_open(fname,O_RDWR,0);
+  if(size != st.st_size) {
+
+    DEBUG(10,("call_trans2setfilepathinfo: file %s : setting new size to %.0f\n",
+          fname, (double)size ));
+
+    if (fd == -1) {
+      fd = conn->vfs_ops.open(dos_to_unix(fname,False),O_RDWR,0);
       if (fd == -1)
-      {
         return(UNIXERROR(ERRDOS,ERRbadpath));
-      }
-      set_filelen(fd, size);
-      close(fd);
-    }
-    else
-    {
-      set_filelen(fd, size);
+      set_filelen(fd, size); /* tpot vfs */
+      conn->vfs_ops.close(fd);
+    } else {
+        set_filelen(fd, size); /* tpot vfs */
     }
+
+    if(fsp)
+      set_filelen_write_cache(fsp, size);
   }
 
   SSVAL(params,0,0);
@@ -1703,7 +2012,8 @@ static int call_trans2mkdir(connection_struct *conn,
 
   unix_convert(directory,conn,0,&bad_path,NULL);
   if (check_name(directory,conn))
-    ret = dos_mkdir(directory,unix_mode(conn,aDIR));
+    ret = conn->vfs_ops.mkdir(dos_to_unix(directory,False),
+                             unix_mode(conn,aDIR,directory));
   
   if(ret < 0)
     {
@@ -1797,6 +2107,47 @@ static int call_trans2findnotifynext(connection_struct *conn,
   return(-1);
 }
 
+/****************************************************************************
+  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, char** ppdata)
+{
+  char *params = *pparams;
+  enum remote_arch_types ra_type = get_remote_arch();
+  BOOL NT_arch = ((ra_type == RA_WINNT) || (ra_type == RA_WIN2K));
+  pstring pathname;
+  int reply_size = 0;
+  int max_referral_level = SVAL(params,0);
+
+
+  DEBUG(10,("call_trans2getdfsreferral\n"));
+
+  if(!lp_host_msdfs())
+    return(ERROR(ERRDOS,ERRbadfunc));
+
+  /* if pathname is in UNICODE, convert to DOS */
+  /* NT always sends in UNICODE, may not set UNICODE flag */
+  if(NT_arch || (SVAL(inbuf,smb_flg2) & FLAGS2_UNICODE_STRINGS))
+    {
+      unistr_to_dos(pathname, &params[2]);
+      DEBUG(10,("UNICODE referral for %s\n",pathname));
+    }
+  else
+    pstrcpy(pathname,&params[2]);
+
+  if((reply_size = setup_dfs_referral(pathname,max_referral_level,ppdata)) < 0)
+    return(ERROR(ERRDOS,ERRbadfile));
+    
+  SSVAL(outbuf,smb_flg2,SVAL(outbuf,smb_flg2) | FLAGS2_UNICODE_STRINGS | 
+       FLAGS2_DFS_PATHNAMES);
+  send_trans2_replies(outbuf,bufsize,0,0,*ppdata,reply_size);
+
+  return(-1);
+}
+
+
 /****************************************************************************
   reply to a SMBfindclose (stop trans2 directory search)
 ****************************************************************************/
@@ -1804,11 +2155,11 @@ int reply_findclose(connection_struct *conn,
                    char *inbuf,char *outbuf,int length,int bufsize)
 {
        int outsize = 0;
-       int16 dptr_num=SVALS(inbuf,smb_vwv0);
+       int dptr_num=SVALS(inbuf,smb_vwv0);
 
        DEBUG(3,("reply_findclose, dptr_num = %d\n", dptr_num));
 
-       dptr_close(dptr_num);
+       dptr_close(&dptr_num);
 
        outsize = set_message(outbuf,0,0,True);
 
@@ -1926,7 +2277,7 @@ int reply_trans2(connection_struct *conn,
                /* We need to send an interim response then receive the rest
                   of the parameter/data bytes */
                outsize = set_message(outbuf,0,0,True);
-               send_smb(Client,outbuf);
+               send_smb(smbd_server_fd(),outbuf);
 
                while (num_data_sofar < total_data || 
                       num_params_sofar < total_params) {
@@ -2030,6 +2381,11 @@ int reply_trans2(connection_struct *conn,
                outsize = call_trans2mkdir(conn, inbuf, outbuf, length, 
                                           bufsize, &params, &data);
                break;
+
+       case TRANSACT2_GET_DFS_REFERRAL:
+               outsize = call_trans2getdfsreferral(conn,inbuf,outbuf,length,
+                                                   bufsize, &params, &data);
+               break;
        default:
                /* Error in request */
                DEBUG(2,("Unknown request %d in trans2 call\n", tran_call));