first pass at updating head branch to be to be the same as the SAMBA_2_0 branch
[mimir/samba.git] / source3 / smbd / nttrans.c
index 70023e240764c98e9083b0fcf05a09cfc8aad7f4..9615c5ada25afb7ab68cb38ae4f8edb3624ff4ee 100644 (file)
@@ -37,17 +37,14 @@ static void remove_pending_change_notify_requests_by_mid(int mid);
 static char *known_nt_pipes[] = {
   "\\LANMAN",
   "\\srvsvc",
-  "\\svcctl",
   "\\samr",
   "\\wkssvc",
-  "\\browser",
   "\\NETLOGON",
   "\\ntlsa",
   "\\ntsvcs",
   "\\lsass",
   "\\lsarpc",
   "\\winreg",
-  "\\spoolss",
   NULL
 };
 
@@ -58,7 +55,7 @@ static char *known_nt_pipes[] = {
  HACK ! Always assumes smb_setup field is zero.
 ****************************************************************************/
 
-static int send_nt_replies(char *outbuf, int bufsize, char *params,
+static int send_nt_replies(char *inbuf, char *outbuf, int bufsize, uint32 nt_error, char *params,
                            int paramsize, char *pdata, int datasize)
 {
   extern int max_send;
@@ -78,6 +75,13 @@ static int send_nt_replies(char *outbuf, int bufsize, char *params,
 
   set_message(outbuf,18,0,True);
 
+  if(nt_error != 0) {
+    /* NT Error. */
+    SSVAL(outbuf,smb_flg2, SVAL(outbuf,smb_flg2) | FLAGS2_32_BIT_ERROR_CODES);
+
+    ERROR(0,nt_error);
+  }
+
   /* 
    * If there genuinely are no parameters or data to send just send
    * the empty packet.
@@ -355,18 +359,25 @@ static int map_create_disposition( uint32 create_disposition)
  Utility function to map share modes.
 ****************************************************************************/
 
-static int map_share_mode( char *fname, uint32 desired_access, uint32 share_access, uint32 file_attributes)
+static int map_share_mode( BOOL *pstat_open_only, char *fname,
+                                                       uint32 desired_access, uint32 share_access, uint32 file_attributes)
 {
   int smb_open_mode = -1;
 
-  switch( desired_access & (FILE_READ_DATA|FILE_WRITE_DATA) ) {
+  *pstat_open_only = False;
+
+  switch( desired_access & (FILE_READ_DATA|FILE_WRITE_DATA|FILE_APPEND_DATA) ) {
   case FILE_READ_DATA:
     smb_open_mode = DOS_OPEN_RDONLY;
     break;
   case FILE_WRITE_DATA:
+  case FILE_APPEND_DATA:
+  case FILE_WRITE_DATA|FILE_APPEND_DATA:
     smb_open_mode = DOS_OPEN_WRONLY;
     break;
   case FILE_READ_DATA|FILE_WRITE_DATA:
+  case FILE_READ_DATA|FILE_WRITE_DATA|FILE_APPEND_DATA:
+  case FILE_READ_DATA|FILE_APPEND_DATA:
     smb_open_mode = DOS_OPEN_RDWR;
     break;
   }
@@ -386,8 +397,12 @@ static int map_share_mode( char *fname, uint32 desired_access, uint32 share_acce
    */
 
   if (smb_open_mode == -1) {
+       if(desired_access == WRITE_DAC_ACCESS || desired_access == READ_CONTROL_ACCESS)
+               *pstat_open_only = True;
+
     if(desired_access & (DELETE_ACCESS|WRITE_DAC_ACCESS|WRITE_OWNER_ACCESS|
                               FILE_EXECUTE|FILE_READ_ATTRIBUTES|
+                              FILE_READ_EA|FILE_WRITE_EA|SYSTEM_SECURITY_ACCESS|
                               FILE_WRITE_ATTRIBUTES|READ_CONTROL_ACCESS))
       smb_open_mode = DOS_OPEN_RDONLY;
     else {
@@ -464,11 +479,40 @@ static time_t fail_time;
 
 void fail_next_srvsvc_open(void)
 {
+  /* Check client is WinNT proper; Win2K doesn't like Jeremy's hack - matty */
+  if (get_remote_arch() != RA_WINNT)
+    return;
+
   fail_next_srvsvc = True;
   fail_time = time(NULL);
   DEBUG(10,("fail_next_srvsvc_open: setting up timeout close of \\srvsvc pipe for print fix.\n"));
 }
 
+/*
+ * HACK alert.... see above - JRA.
+ */
+
+BOOL should_fail_next_srvsvc_open(const char *pipename)
+{
+
+  DEBUG(10,("should_fail_next_srvsvc_open: fail = %d, pipe = %s\n",
+    (int)fail_next_srvsvc, pipename));
+
+  if(fail_next_srvsvc && (time(NULL) > fail_time + HACK_FAIL_TIME)) {
+    fail_next_srvsvc = False;
+    fail_time = (time_t)0;
+    DEBUG(10,("should_fail_next_srvsvc_open: End of timeout close of \\srvsvc pipe for print fix.\n"));
+  }
+
+  if(fail_next_srvsvc && strequal(pipename, "srvsvc")) {
+    fail_next_srvsvc = False;
+    DEBUG(10,("should_fail_next_srvsvc_open: Deliberately failing open of \\srvsvc pipe for print fix.\n"));
+    return True;
+  }
+  return False;
+}
+
+
 /****************************************************************************
  Reply to an NT create and X call on a pipe.
 ****************************************************************************/
@@ -487,32 +531,15 @@ static int nt_open_pipe(char *fname, connection_struct *conn,
                if( strequal(fname,known_nt_pipes[i]))
                        break;
     
-       /*
-        * HACK alert.... see above - JRA.
-        */
-
-       if(fail_next_srvsvc && (time(NULL) > fail_time + HACK_FAIL_TIME)) {
-               fail_next_srvsvc = False;
-               fail_time = (time_t)0;
-               DEBUG(10,("nt_open_pipe: End of timeout close of \\srvsvc pipe for print fix.\n"));
-       }
-
-       if(fail_next_srvsvc && strequal(fname, "\\srvsvc")) {
-               fail_next_srvsvc = False;
-               DEBUG(10,("nt_open_pipe: Deliberately failing open of \\srvsvc pipe for print fix.\n"));
-               return(ERROR(ERRSRV,ERRaccess));
-       }
-
-       /*
-        * End hack alert.... see above - JRA.
-        */
-
        if ( known_nt_pipes[i] == NULL )
                return(ERROR(ERRSRV,ERRaccess));
     
        /* Strip \\ off the name. */
        fname++;
     
+       if(should_fail_next_srvsvc_open(fname))
+               return (ERROR(ERRSRV,ERRaccess));
+
        DEBUG(3,("nt_open_pipe: Known pipe %s opening.\n", fname));
 
        p = open_rpc_pipe_p(fname, conn, vuid);
@@ -547,7 +574,7 @@ int reply_ntcreate_and_X(connection_struct *conn,
        /* Breakout the oplock request bits so we can set the
           reply bits separately. */
        int oplock_request = 0;
-    mode_t unixmode;
+       mode_t unixmode;
        int pnum = -1;
        int fmode=0,rmode=0;
        SMB_OFF_T file_len = 0;
@@ -556,6 +583,7 @@ int reply_ntcreate_and_X(connection_struct *conn,
        BOOL bad_path = False;
        files_struct *fsp=NULL;
        char *p = NULL;
+       BOOL stat_open_only = False;
 
        /* 
         * We need to construct the open_and_X ofun value from the
@@ -576,9 +604,24 @@ int reply_ntcreate_and_X(connection_struct *conn,
       files_struct *dir_fsp = file_fsp(inbuf,smb_ntcreate_RootDirectoryFid);
       size_t dir_name_len;
 
-      if(!dir_fsp || !dir_fsp->is_directory)
+      if(!dir_fsp)
         return(ERROR(ERRDOS,ERRbadfid));
 
+      if(!dir_fsp->is_directory) {
+        /* 
+         * Check to see if this is a mac fork of some kind.
+         */
+
+        get_filename(&fname[0], inbuf, smb_buf(inbuf)-inbuf, 
+                   smb_buflen(inbuf),fname_len);
+
+        if( fname[0] == ':') {
+          SSVAL(outbuf, smb_flg2, FLAGS2_32_BIT_ERROR_CODES);
+          return(ERROR(0, 0xc0000000|NT_STATUS_OBJECT_PATH_NOT_FOUND));
+        }
+        return(ERROR(ERRDOS,ERRbadfid));
+      }
+
       /*
        * Copy in the base directory name.
        */
@@ -600,26 +643,17 @@ int reply_ntcreate_and_X(connection_struct *conn,
 
       get_filename(&fname[dir_name_len], inbuf, smb_buf(inbuf)-inbuf, 
                    smb_buflen(inbuf),fname_len);
-#if 0
-      StrnCpy(&fname[dir_name_len], smb_buf(inbuf),fname_len);
-      fname[dir_name_len+fname_len] = '\0';
-#endif
 
     } else {
       
       get_filename(fname, inbuf, smb_buf(inbuf)-inbuf, 
                    smb_buflen(inbuf),fname_len);
-
-#if 0
-         StrnCpy(fname,smb_buf(inbuf),fname_len);
-      fname[fname_len] = '\0';
-#endif
     }
        
        /* If it's an IPC, use the pipe handler. */
 
-       if (IS_IPC(conn) && lp_nt_pipe_support() && lp_security() != SEC_SHARE)
-       {
+       if (IS_IPC(conn) && lp_nt_pipe_support()) {
+
                int ret = nt_open_pipe(fname, conn, inbuf, outbuf, &pnum);
                if(ret != 0)
                        return ret;
@@ -654,11 +688,13 @@ int reply_ntcreate_and_X(connection_struct *conn,
      * desired access and the share access.
         */
        
-       if((smb_open_mode = map_share_mode(fname, desired_access, 
+       if((smb_open_mode = map_share_mode(&stat_open_only, fname, desired_access, 
                                           share_access, 
-                                          file_attributes)) == -1) {
+                                          file_attributes)) == -1)
                return(ERROR(ERRDOS,ERRbadaccess));
-       }
+
+       oplock_request = (flags & REQUEST_OPLOCK) ? EXCLUSIVE_OPLOCK : 0;
+       oplock_request |= (flags & REQUEST_BATCH_OPLOCK) ? BATCH_OPLOCK : 0;
 
        /*
         * Ordinary file or directory.
@@ -670,11 +706,7 @@ int reply_ntcreate_and_X(connection_struct *conn,
                
        set_posix_case_semantics(file_attributes);
                
-       if (!unix_dfs_convert(fname,conn,0,&bad_path,NULL))
-       {
-               SSVAL(outbuf, smb_flg2, FLAGS2_32_BIT_ERROR_CODES);
-               return(ERROR(0, 0xc0000000|NT_STATUS_PATH_NOT_COVERED));
-       }
+       unix_convert(fname,conn,0,&bad_path,NULL);
                
        fsp = file_new();
        if (!fsp) {
@@ -696,9 +728,6 @@ int reply_ntcreate_and_X(connection_struct *conn,
                
        unixmode = unix_mode(conn,smb_attr | aARCH);
     
-       oplock_request = (flags & REQUEST_OPLOCK) ? EXCLUSIVE_OPLOCK : 0;
-       oplock_request |= (flags & REQUEST_BATCH_OPLOCK) ? BATCH_OPLOCK : 0;
-
        /* 
         * If it's a request for a directory open, deal with it separately.
         */
@@ -706,8 +735,8 @@ int reply_ntcreate_and_X(connection_struct *conn,
        if(create_options & FILE_DIRECTORY_FILE) {
                oplock_request = 0;
                
-               open_directory(fsp, conn, fname, smb_ofun, unixmode, 
-                              &smb_action);
+               open_directory(fsp, conn, fname, smb_ofun, 
+                              unixmode, &smb_action);
                        
                restore_case_semantics(file_attributes);
 
@@ -733,13 +762,12 @@ int reply_ntcreate_and_X(connection_struct *conn,
                 * before issuing an oplock break request to
                 * our client. JRA.  */
 
-               open_file_shared(fsp,conn,fname,smb_open_mode,
-                                smb_ofun,unixmode,
-                                oplock_request,&rmode,&smb_action);
+               open_file_shared(fsp,conn,fname,smb_open_mode,
+                                smb_ofun,unixmode, oplock_request,&rmode,&smb_action);
 
                if (!fsp->open) { 
-                       /* We cheat here. The only case we
-                        * care about is a directory rename,
+                       /* We cheat here. There are two cases we
+                        * care about. One is a directory rename,
                         * where the NT client will attempt to
                         * open the source directory for
                         * DELETE access. Note that when the
@@ -752,21 +780,54 @@ int reply_ntcreate_and_X(connection_struct *conn,
                         * will generate an EISDIR error, so
                         * we can catch this here and open a
                         * pseudo handle that is flagged as a
-                        * directory. JRA.  */
+                        * directory. The second is an open
+                        * for a permissions read only, which
+                        * we handle in the open_file_stat case. JRA.
+                        */
 
                        if(errno == EISDIR) {
+
+                               /*
+                                * Fail the open if it was explicitly a non-directory file.
+                                */
+
+                               if (create_options & FILE_NON_DIRECTORY_FILE) {
+                                       file_free(fsp);
+                                       restore_case_semantics(file_attributes);
+                                       SSVAL(outbuf, smb_flg2, FLAGS2_32_BIT_ERROR_CODES);
+                                       return(ERROR(0, 0xc0000000|NT_STATUS_FILE_IS_A_DIRECTORY));
+                               }
+       
                                oplock_request = 0;
+                               open_directory(fsp, conn, fname, smb_ofun, unixmode, &smb_action);
                                
-                               open_directory(fsp, conn, fname, 
-                                              smb_ofun, unixmode, 
-                                              &smb_action);
-                               
                                if(!fsp->open) {
                                        file_free(fsp);
                                        restore_case_semantics(file_attributes);
                                        return(UNIXERROR(ERRDOS,ERRnoaccess));
                                }
+#ifdef EROFS
+                       } else if (((errno == EACCES) || (errno == EROFS)) && stat_open_only) {
+#else /* !EROFS */
+                       } else if (errno == EACCES && stat_open_only) {
+#endif
+                               /*
+                                * We couldn't open normally and all we want
+                                * are the permissions. Try and do a stat open.
+                                */
+
+                               oplock_request = 0;
+
+                               open_file_stat(fsp,conn,fname,smb_open_mode,&sbuf,&smb_action);
+
+                               if(!fsp->open) {
+                                       file_free(fsp);
+                                       restore_case_semantics(file_attributes);
+                                       return(UNIXERROR(ERRDOS,ERRnoaccess));
+                               }
+
                        } else {
+
                                if((errno == ENOENT) && bad_path) {
                                        unix_ERR_class = ERRDOS;
                                        unix_ERR_code = ERRbadpath;
@@ -782,15 +843,13 @@ int reply_ntcreate_and_X(connection_struct *conn,
        }
                
        if(fsp->is_directory) {
-               if(fsp->conn->vfs_ops.stat(dos_to_unix(fsp->fsp_name, False),
-                                          &sbuf) != 0) {
-                       close_directory(fsp);
+               if(dos_stat(fsp->fsp_name, &sbuf) != 0) {
+                       close_file(fsp,True);
                        restore_case_semantics(file_attributes);
                        return(ERROR(ERRDOS,ERRnoaccess));
                }
        } else {
-               if (fsp->conn->vfs_ops.fstat(fsp->fd_ptr->fd,&sbuf) 
-                   != 0) {
+               if (!fsp->stat_open && sys_fstat(fsp->fd_ptr->fd,&sbuf) != 0) {
                        close_file(fsp,False);
                        restore_case_semantics(file_attributes);
                        return(ERROR(ERRDOS,ERRnoaccess));
@@ -817,9 +876,9 @@ int reply_ntcreate_and_X(connection_struct *conn,
        if (oplock_request && lp_fake_oplocks(SNUM(conn)))
                smb_action |= EXTENDED_OPLOCK_GRANTED;
        
-       if(oplock_request && fsp->granted_oplock)
+       if(oplock_request && EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type))
                smb_action |= EXTENDED_OPLOCK_GRANTED;
-       
+
        set_message(outbuf,34,0,True);
        
        p = outbuf + smb_vwv2;
@@ -828,8 +887,14 @@ int reply_ntcreate_and_X(connection_struct *conn,
         * Currently as we don't support level II oplocks we just report
         * exclusive & batch here.
         */
+
+    if (smb_action & EXTENDED_OPLOCK_GRANTED)  
+               SCVAL(p,0, BATCH_OPLOCK_RETURN);
+       else if (LEVEL_II_OPLOCK_TYPE(fsp->oplock_type))
+        SCVAL(p,0, LEVEL_II_OPLOCK_RETURN);
+       else
+               SCVAL(p,0,NO_OPLOCK_RETURN);
        
-       SCVAL(p,0, (smb_action & EXTENDED_OPLOCK_GRANTED ? 1 : 0));
        p++;
        SSVAL(p,0,fsp->fnum);
        p += 2;
@@ -861,6 +926,7 @@ int reply_ntcreate_and_X(connection_struct *conn,
 /****************************************************************************
  Reply to a NT_TRANSACT_CREATE call (needs to process SD's).
 ****************************************************************************/
+
 static int call_nt_transact_create(connection_struct *conn,
                                   char *inbuf, char *outbuf, int length, 
                                    int bufsize, 
@@ -893,6 +959,7 @@ static int call_nt_transact_create(connection_struct *conn,
   BOOL bad_path = False;
   files_struct *fsp = NULL;
   char *p = NULL;
+  BOOL stat_open_only = False;
 
   /* 
    * We need to construct the open_and_X ofun value from the
@@ -914,8 +981,24 @@ static int call_nt_transact_create(connection_struct *conn,
     files_struct *dir_fsp = file_fsp(params,4);
     size_t dir_name_len;
 
-    if(!dir_fsp || !dir_fsp->is_directory)
+    if(!dir_fsp)
+        return(ERROR(ERRDOS,ERRbadfid));
+
+    if(!dir_fsp->is_directory) {
+      /*
+       * Check to see if this is a mac fork of some kind.
+       */
+
+      StrnCpy(fname,params+53,fname_len);
+      fname[fname_len] = '\0';
+
+      if( fname[0] == ':') {
+          SSVAL(outbuf, smb_flg2, FLAGS2_32_BIT_ERROR_CODES);
+          return(ERROR(0, 0xc0000000|NT_STATUS_OBJECT_PATH_NOT_FOUND));
+      }
+
       return(ERROR(ERRDOS,ERRbadfid));
+    }
 
     /*
      * Copy in the base directory name.
@@ -951,17 +1034,26 @@ static int call_nt_transact_create(connection_struct *conn,
       return ret;
     smb_action = FILE_WAS_OPENED;
   } else {
+
+    /*
+     * Now contruct the smb_open_mode value from the desired access
+     * and the share access.
+     */
+
+    if((smb_open_mode = map_share_mode( &stat_open_only, fname, desired_access,
+                                        share_access, file_attributes)) == -1)
+      return(ERROR(ERRDOS,ERRbadaccess));
+
+    oplock_request = (flags & REQUEST_OPLOCK) ? EXCLUSIVE_OPLOCK : 0;
+    oplock_request |= (flags & REQUEST_BATCH_OPLOCK) ? BATCH_OPLOCK : 0;
+
     /*
      * Check if POSIX semantics are wanted.
      */
 
     set_posix_case_semantics(file_attributes);
 
-       if (!unix_dfs_convert(fname,conn,0,&bad_path,NULL))
-       {
-               SSVAL(outbuf, smb_flg2, FLAGS2_32_BIT_ERROR_CODES);
-               return(ERROR(0, 0xc0000000|NT_STATUS_PATH_NOT_COVERED));
-       }
+    unix_convert(fname,conn,0,&bad_path,NULL);
     
     fsp = file_new();
     if (!fsp) {
@@ -983,17 +1075,6 @@ static int call_nt_transact_create(connection_struct *conn,
   
     unixmode = unix_mode(conn,smb_attr | aARCH);
     
-    oplock_request = (flags & REQUEST_OPLOCK) ? EXCLUSIVE_OPLOCK : 0;
-    oplock_request |= (flags & REQUEST_BATCH_OPLOCK) ? BATCH_OPLOCK : 0;
-
-    /*
-     * Now contruct the smb_open_mode value from the desired access
-     * and the share access.
-     */
-
-    if((smb_open_mode = map_share_mode( fname, desired_access, share_access, file_attributes)) == -1)
-      return(ERROR(ERRDOS,ERRbadaccess));
-
     /*
      * If it's a request for a directory open, deal with it separately.
      */
@@ -1012,37 +1093,96 @@ static int call_nt_transact_create(connection_struct *conn,
 
       if(!fsp->open) {
         file_free(fsp);
+        restore_case_semantics(file_attributes);
         return(UNIXERROR(ERRDOS,ERRnoaccess));
       }
+
+      if(dos_stat(fsp->fsp_name, &sbuf) != 0) {
+        close_file(fsp,True);
+        restore_case_semantics(file_attributes);
+        return(ERROR(ERRDOS,ERRnoaccess));
+      }
+
     } else {
 
       /*
        * Ordinary file case.
        */
 
-      open_file_shared(fsp,conn,fname,smb_open_mode,smb_ofun,
-                      unixmode,oplock_request,&rmode,&smb_action);
+      open_file_shared(fsp,conn,fname,smb_open_mode,smb_ofun,unixmode,
+                       oplock_request,&rmode,&smb_action);
 
       if (!fsp->open) { 
-        if((errno == ENOENT) && bad_path) {
-          unix_ERR_class = ERRDOS;
-          unix_ERR_code = ERRbadpath;
-        }
-        file_free(fsp);
 
-        restore_case_semantics(file_attributes);
+               if(errno == EISDIR) {
 
-        return(UNIXERROR(ERRDOS,ERRnoaccess));
-      } 
-  
-      if (fsp->conn->vfs_ops.fstat(fsp->fd_ptr->fd,&sbuf) != 0) {
-        close_file(fsp,False);
+                       /*
+                        * Fail the open if it was explicitly a non-directory file.
+                        */
 
-        restore_case_semantics(file_attributes);
+                       if (create_options & FILE_NON_DIRECTORY_FILE) {
+                               file_free(fsp);
+                               restore_case_semantics(file_attributes);
+                               SSVAL(outbuf, smb_flg2, FLAGS2_32_BIT_ERROR_CODES);
+                               return(ERROR(0, 0xc0000000|NT_STATUS_FILE_IS_A_DIRECTORY));
+                       }
+       
+                       oplock_request = 0;
+                       open_directory(fsp, conn, fname, smb_ofun, unixmode, &smb_action);
+                               
+                       if(!fsp->open) {
+                               file_free(fsp);
+                               restore_case_semantics(file_attributes);
+                               return(UNIXERROR(ERRDOS,ERRnoaccess));
+                       }
+#ifdef EROFS
+               } else if (((errno == EACCES) || (errno == EROFS)) && stat_open_only) {
+#else /* !EROFS */
+               } else if (errno == EACCES && stat_open_only) {
+#endif
 
-        return(ERROR(ERRDOS,ERRnoaccess));
+                       /*
+                        * We couldn't open normally and all we want
+                        * are the permissions. Try and do a stat open.
+                        */
+
+                       oplock_request = 0;
+
+                       open_file_stat(fsp,conn,fname,smb_open_mode,&sbuf,&smb_action);
+
+                       if(!fsp->open) {
+                               file_free(fsp);
+                               restore_case_semantics(file_attributes);
+                               return(UNIXERROR(ERRDOS,ERRnoaccess));
+                       }
+               } else {
+
+                       if((errno == ENOENT) && bad_path) {
+                               unix_ERR_class = ERRDOS;
+                               unix_ERR_code = ERRbadpath;
+                       }
+                       file_free(fsp);
+
+                       restore_case_semantics(file_attributes);
+
+                       return(UNIXERROR(ERRDOS,ERRnoaccess));
+               }
       } 
   
+      if(fsp->is_directory) {
+          if(dos_stat(fsp->fsp_name, &sbuf) != 0) {
+              close_file(fsp,True);
+              restore_case_semantics(file_attributes);
+              return(ERROR(ERRDOS,ERRnoaccess));
+          }
+      } else {
+          if (!fsp->stat_open && sys_fstat(fsp->fd_ptr->fd,&sbuf) != 0) {
+              close_file(fsp,False);
+              restore_case_semantics(file_attributes);
+              return(ERROR(ERRDOS,ERRnoaccess));
+          } 
+      }
       file_len = sbuf.st_size;
       fmode = dos_mode(conn,fname,&sbuf);
       if(fmode == 0)
@@ -1063,7 +1203,7 @@ static int call_nt_transact_create(connection_struct *conn,
       if (oplock_request && lp_fake_oplocks(SNUM(conn)))
         smb_action |= EXTENDED_OPLOCK_GRANTED;
   
-      if(oplock_request && fsp->granted_oplock)
+      if(oplock_request && EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type))
         smb_action |= EXTENDED_OPLOCK_GRANTED;
     }
   }
@@ -1075,8 +1215,16 @@ static int call_nt_transact_create(connection_struct *conn,
   if(params == NULL)
     return(ERROR(ERRDOS,ERRnomem));
 
+  memset((char *)params,'\0',69);
+
   p = params;
-  SCVAL(p,0, (smb_action & EXTENDED_OPLOCK_GRANTED ? 1 : 0));
+  if (smb_action & EXTENDED_OPLOCK_GRANTED)    
+       SCVAL(p,0, BATCH_OPLOCK_RETURN);
+  else if (LEVEL_II_OPLOCK_TYPE(fsp->oplock_type))
+    SCVAL(p,0, LEVEL_II_OPLOCK_RETURN);
+  else
+       SCVAL(p,0,NO_OPLOCK_RETURN);
+       
   p += 2;
   if (IS_IPC(conn)) {
          SSVAL(p,0,pnum);
@@ -1119,7 +1267,7 @@ static int call_nt_transact_create(connection_struct *conn,
   }
 
   /* Send the required number of replies */
-  send_nt_replies(outbuf, bufsize, params, 69, *ppdata, 0);
+  send_nt_replies(inbuf, outbuf, bufsize, 0, params, 69, *ppdata, 0);
 
   return -1;
 }
@@ -1156,6 +1304,7 @@ int reply_nttranss(connection_struct *conn,
 /****************************************************************************
  Reply to an NT transact rename command.
 ****************************************************************************/
+
 static int call_nt_transact_rename(connection_struct *conn,
                                   char *inbuf, char *outbuf, int length, 
                                    int bufsize,
@@ -1179,7 +1328,7 @@ static int call_nt_transact_rename(connection_struct *conn,
     /*
      * Rename was successful.
      */
-    send_nt_replies(outbuf, bufsize, NULL, 0, NULL, 0);
+    send_nt_replies(inbuf, outbuf, bufsize, 0, NULL, 0, NULL, 0);
 
     DEBUG(3,("nt transact rename from = %s, to = %s succeeded.\n", 
           fsp->fsp_name, new_name));
@@ -1190,6 +1339,18 @@ static int call_nt_transact_rename(connection_struct *conn,
   return(outsize);
 }
    
+/****************************************************************************
+ This is the structure to keep the information needed to
+ determine if a directory has changed.
+*****************************************************************************/
+
+typedef struct {
+  time_t modify_time; /* Info from the directory we're monitoring. */ 
+  time_t status_time; /* Info from the directory we're monitoring. */
+  time_t total_time; /* Total time of all directory entries - don't care if it wraps. */
+  unsigned int num_entries; /* Zero or the number of files in the directory. */
+} change_hash_data;
+
 /****************************************************************************
  This is the structure to queue to implement NT change
  notify. It consists of smb_size bytes stored from the
@@ -1201,9 +1362,9 @@ typedef struct {
   ubi_slNode msg_next;
   files_struct *fsp;
   connection_struct *conn;
+  uint32 flags;
   time_t next_check_time;
-  time_t modify_time; /* Info from the directory we're monitoring. */ 
-  time_t status_time; /* Info from the directory we're monitoring. */
+  change_hash_data change_data;
   char request_buf[smb_size];
 } change_notify_buf;
 
@@ -1243,9 +1404,93 @@ static void change_notify_reply_packet(char *inbuf, int error_class, uint32 erro
   send_smb(Client,outbuf);
 }
 
+/****************************************************************************
+ Create the hash we will use to determine if the contents changed.
+*****************************************************************************/
+
+static BOOL create_directory_notify_hash( change_notify_buf *cnbp, change_hash_data *change_data)
+{
+  SMB_STRUCT_STAT st;
+  files_struct *fsp = cnbp->fsp;
+
+  memset((char *)change_data, '\0', sizeof(change_data));
+
+  /* 
+   * Store the current timestamp on the directory we are monitoring.
+   */
+
+  if(dos_stat(fsp->fsp_name, &st) < 0) {
+    DEBUG(0,("create_directory_notify_hash: Unable to stat name = %s. \
+Error was %s\n", fsp->fsp_name, strerror(errno) ));
+    return False;
+  }
+  change_data->modify_time = st.st_mtime;
+  change_data->status_time = st.st_ctime;
+
+  /*
+   * If we are to watch for changes that are only stored
+   * in inodes of files, not in the directory inode, we must
+   * scan the directory and produce a unique identifier with
+   * which we can determine if anything changed. We use the
+   * modify and change times from all the files in the
+   * directory, added together (ignoring wrapping if it's
+   * larger than the max time_t value).
+   */
+
+  if(cnbp->flags & (FILE_NOTIFY_CHANGE_SIZE|FILE_NOTIFY_CHANGE_LAST_WRITE)) {
+    pstring full_name;
+    char *p;
+    char *fname;
+    size_t remaining_len;
+    size_t fullname_len;
+    void *dp = OpenDir(cnbp->conn, fsp->fsp_name, True);
+
+    if(dp == NULL) {
+      DEBUG(0,("create_directory_notify_hash: Unable to open directory = %s. \
+Error was %s\n", fsp->fsp_name, strerror(errno) ));
+      return False;
+    }
+
+    change_data->num_entries = 0;
+
+    pstrcpy(full_name, fsp->fsp_name);
+    pstrcat(full_name, "/");
+
+    fullname_len = strlen(full_name);
+    remaining_len = sizeof(full_name) - fullname_len - 1;
+    p = &full_name[fullname_len];
+
+    while ((fname = ReadDirName(dp))) {
+      if(strequal(fname, ".") || strequal(fname, ".."))
+        continue;
+
+      change_data->num_entries++;
+      safe_strcpy( p, fname, remaining_len);
+
+      memset(&st, '\0', sizeof(st));
+
+      /*
+       * Do the stat - but ignore errors.
+       */
+
+      if(dos_stat(full_name, &st) < 0) {
+        DEBUG(5,("create_directory_notify_hash: Unable to stat content file = %s. \
+Error was %s\n", fsp->fsp_name, strerror(errno) ));
+      }
+      change_data->total_time += (st.st_mtime + st.st_ctime);
+    }
+
+    CloseDir(dp);
+  }
+
+  return True;
+}
+
 /****************************************************************************
  Delete entries by fnum from the change notify pending queue.
 *****************************************************************************/
+
 void remove_pending_change_notify_requests_by_fid(files_struct *fsp)
 {
   change_notify_buf *cnbp = (change_notify_buf *)ubi_slFirst( &change_notify_queue );
@@ -1274,7 +1519,34 @@ static void remove_pending_change_notify_requests_by_mid(int mid)
 
   while(cnbp != NULL) {
     if(SVAL(cnbp->request_buf,smb_mid) == mid) {
-      change_notify_reply_packet(cnbp->request_buf,0,NT_STATUS_CANCELLED);
+      change_notify_reply_packet(cnbp->request_buf,0,0xC0000000 |NT_STATUS_CANCELLED);
+      free((char *)ubi_slRemNext( &change_notify_queue, prev));
+      cnbp = (change_notify_buf *)(prev ? ubi_slNext(prev) : ubi_slFirst(&change_notify_queue));
+      continue;
+    }
+
+    prev = cnbp;
+    cnbp = (change_notify_buf *)ubi_slNext(cnbp);
+  }
+}
+
+/****************************************************************************
+ Delete entries by filename and cnum from the change notify pending queue.
+ Always send reply.
+*****************************************************************************/
+
+void remove_pending_change_notify_requests_by_filename(files_struct *fsp)
+{
+  change_notify_buf *cnbp = (change_notify_buf *)ubi_slFirst( &change_notify_queue );
+  change_notify_buf *prev = NULL;
+
+  while(cnbp != NULL) {
+    /*
+     * We know it refers to the same directory if the connection number and
+     * the filename are identical.
+     */
+    if((cnbp->fsp->conn == fsp->conn) && strequal(cnbp->fsp->fsp_name,fsp->fsp_name)) {
+      change_notify_reply_packet(cnbp->request_buf,0,0xC0000000 |NT_STATUS_CANCELLED);
       free((char *)ubi_slRemNext( &change_notify_queue, prev));
       cnbp = (change_notify_buf *)(prev ? ubi_slNext(prev) : ubi_slFirst(&change_notify_queue));
       continue;
@@ -1287,18 +1559,20 @@ static void remove_pending_change_notify_requests_by_mid(int mid)
 
 /****************************************************************************
  Process the change notify queue. Note that this is only called as root.
+ Returns True if there are still outstanding change notify requests on the
+ queue.
 *****************************************************************************/
 
-void process_pending_change_notify_queue(time_t t)
+BOOL process_pending_change_notify_queue(time_t t)
 {
   change_notify_buf *cnbp = (change_notify_buf *)ubi_slFirst( &change_notify_queue );
   change_notify_buf *prev = NULL;
 
   if(cnbp == NULL)
-    return;
+    return False;
 
   if(cnbp->next_check_time >= t)
-    return;
+    return True;
 
   /*
    * It's time to check. Go through the queue and see if
@@ -1306,8 +1580,7 @@ void process_pending_change_notify_queue(time_t t)
    */
 
   while((cnbp != NULL) && (cnbp->next_check_time <= t)) {
-    SMB_STRUCT_STAT st;
-    files_struct *fsp = cnbp->fsp;
+    change_hash_data change_data;
     connection_struct *conn = cnbp->conn;
     uint16 vuid = (lp_security() == SEC_SHARE) ? UID_FIELD_INVALID : 
                   SVAL(cnbp->request_buf,smb_uid);
@@ -1343,9 +1616,9 @@ void process_pending_change_notify_queue(time_t t)
       continue;
     }
 
-    if(fsp->conn->vfs_ops.stat(dos_to_unix(fsp->fsp_name, False), &st) < 0) {
-      DEBUG(0,("process_pending_change_notify_queue: Unable to stat directory %s. \
-Error was %s.\n", fsp->fsp_name, strerror(errno) ));
+    if(!create_directory_notify_hash( cnbp, &change_data)) {
+      DEBUG(0,("process_pending_change_notify_queue: Unable to create change data for \
+directory %s\n", cnbp->fsp->fsp_name ));
       /*
        * Remove the entry and return an error to the client.
        */
@@ -1356,13 +1629,12 @@ Error was %s.\n", fsp->fsp_name, strerror(errno) ));
       continue;
     }
 
-    if(cnbp->modify_time != st.st_mtime ||
-       cnbp->status_time != st.st_ctime) {
+    if(memcmp( (char *)&cnbp->change_data, (char *)&change_data, sizeof(change_data))) {
       /*
        * Remove the entry and return a change notify to the client.
        */
-      DEBUG(5,("process_pending_change_notify_queue: directory name = %s changed\n",
-            fsp->fsp_name ));
+      DEBUG(5,("process_pending_change_notify_queue: directory name = %s changed.\n",
+            cnbp->fsp->fsp_name ));
       change_notify_reply_packet(cnbp->request_buf,0,NT_STATUS_NOTIFY_ENUM_DIR);
       free((char *)ubi_slRemNext( &change_notify_queue, prev));
       cnbp = (change_notify_buf *)(prev ? ubi_slNext(prev) : ubi_slFirst(&change_notify_queue));
@@ -1378,22 +1650,34 @@ Error was %s.\n", fsp->fsp_name, strerror(errno) ));
     prev = cnbp;
     cnbp = (change_notify_buf *)ubi_slNext(cnbp);
   }
+
+  return (cnbp != NULL);
+}
+
+/****************************************************************************
+ Return true if there are pending change notifies.
+****************************************************************************/
+
+BOOL change_notifies_pending(void)
+{
+  change_notify_buf *cnbp = (change_notify_buf *)ubi_slFirst( &change_notify_queue );
+  return (cnbp != NULL);
 }
 
 /****************************************************************************
  Reply to a notify change - queue the request and 
  don't allow a directory to be opened.
 ****************************************************************************/
+
 static int call_nt_transact_notify_change(connection_struct *conn,
-                                         char *inbuf, char *outbuf, int length,
+                                          char *inbuf, char *outbuf, int length,
                                           int bufsize, 
                                           char **ppsetup, 
-                                         char **ppparams, char **ppdata)
+                                          char **ppparams, char **ppdata)
 {
   char *setup = *ppsetup;
   files_struct *fsp;
   change_notify_buf *cnbp;
-  SMB_STRUCT_STAT st;
 
   fsp = file_fsp(setup,4);
 
@@ -1414,28 +1698,22 @@ static int call_nt_transact_notify_change(connection_struct *conn,
    */
 
   if((cnbp = (change_notify_buf *)malloc(sizeof(change_notify_buf))) == NULL) {
-    DEBUG(0,("call_nt_transact_notify_change: Malloc fail (2) !\n" ));
+    DEBUG(0,("call_nt_transact_notify_change: malloc fail !\n" ));
     return -1;
   }
 
-  /* 
-   * Store the current timestamp on the directory we are monitoring.
-   */
+  memset((char *)cnbp, '\0', sizeof(change_notify_buf));
 
-  if(fsp->conn->vfs_ops.stat(dos_to_unix(fsp->fsp_name, False), &st) < 0) {
-    DEBUG(0,("call_nt_transact_notify_change: Unable to stat name = %s. \
-Error was %s\n", fsp->fsp_name, strerror(errno) ));
-    free((char *)cnbp);
-    return(UNIXERROR(ERRDOS,ERRbadfid));
-  }
   memcpy(cnbp->request_buf, inbuf, smb_size);
   cnbp->fsp = fsp;
   cnbp->conn = conn;
-  cnbp->modify_time = st.st_mtime;
-  cnbp->status_time = st.st_ctime;
-
   cnbp->next_check_time = time(NULL) + lp_change_notify_timeout();
+  cnbp->flags = IVAL(setup, 0);
+
+  if(!create_directory_notify_hash( cnbp, &cnbp->change_data )) {
+    free((char *)cnbp);
+    return(UNIXERROR(ERRDOS,ERRbadfid));
+  }
 
   /*
    * Adding to the tail enables us to check only
@@ -1452,43 +1730,661 @@ name = %s\n", fsp->fsp_name ));
 }
 
 /****************************************************************************
- Reply to query a security descriptor - currently this is not implemented (it
- is planned to be though).
+ Map unix perms to NT.
 ****************************************************************************/
-static int call_nt_transact_query_security_desc(connection_struct *conn,
-                                               char *inbuf, char *outbuf, 
-                                               int length, 
-                                                int bufsize, 
-                                                char **ppsetup, char **ppparams, char **ppdata)
+
+static SEC_ACCESS map_unix_perms( int *pacl_type, mode_t perm, int r_mask, int w_mask, int x_mask, BOOL is_directory)
 {
-  static BOOL logged_message = False;
+       SEC_ACCESS sa;
+       uint32 nt_mask = 0;
 
-  if(!logged_message) {
-    DEBUG(0,("call_nt_transact_query_security_desc: Currently not implemented.\n"));
-    logged_message = True; /* Only print this once... */
-  }
+       *pacl_type = SEC_ACE_TYPE_ACCESS_ALLOWED;
+
+       if((perm & (r_mask|w_mask|x_mask)) == (r_mask|w_mask|x_mask)) {
+               nt_mask = UNIX_ACCESS_RWX;
+       } else if((perm & (r_mask|w_mask|x_mask)) == 0) {
+               nt_mask = UNIX_ACCESS_NONE;
+       } else {
+               nt_mask |= (perm & r_mask) ? UNIX_ACCESS_R : 0;
+               if(is_directory)
+                       nt_mask |= (perm & w_mask) ? UNIX_ACCESS_W : 0;
+               else
+                       nt_mask |= (perm & w_mask) ? UNIX_ACCESS_W : 0;
+               nt_mask |= (perm & x_mask) ? UNIX_ACCESS_X : 0;
+       }
+       init_sec_access(&sa,nt_mask);
+       return sa;
+}
+
+/****************************************************************************
+ Reply to query a security descriptor from an fsp. If it succeeds it allocates
+ the space for the return elements and returns True.
+****************************************************************************/
+
+static size_t get_nt_acl(files_struct *fsp, SEC_DESC **ppdesc)
+{
+  extern DOM_SID global_sam_sid;
+  extern DOM_SID global_sid_World;
+  SMB_STRUCT_STAT sbuf;
+  SEC_ACE ace_list[6];
+  DOM_SID owner_sid;
+  DOM_SID group_sid;
+  size_t sec_desc_size;
+  SEC_ACL *psa = NULL;
+  SEC_ACCESS owner_access;
+  int owner_acl_type;
+  SEC_ACCESS group_access;
+  int grp_acl_type;
+  SEC_ACCESS other_access;
+  int other_acl_type;
+  int num_acls = 0;
+  *ppdesc = NULL;
+
+  if(!lp_nt_acl_support()) {
+    sid_copy( &owner_sid, &global_sid_World);
+    sid_copy( &group_sid, &global_sid_World);
+  } else {
+
+    if(fsp->is_directory || fsp->fd_ptr == NULL) {
+      if(dos_stat(fsp->fsp_name, &sbuf) != 0) {
+        return 0;
+      }
+    } else {
+      if(sys_fstat(fsp->fd_ptr->fd,&sbuf) != 0) {
+        return 0;
+      }
+    }
+
+    /*
+     * Get the owner, group and world SIDs.
+     */
+
+    sid_copy(&owner_sid, &global_sam_sid);
+    sid_copy(&group_sid, &global_sam_sid);
+    sid_append_rid(&owner_sid, pdb_uid_to_user_rid(sbuf.st_uid));
+    sid_append_rid(&group_sid, pdb_gid_to_group_rid(sbuf.st_gid));
+
+    /*
+     * Create the generic 3 element UNIX acl.
+     */
+
+    owner_access = map_unix_perms(&owner_acl_type, sbuf.st_mode,
+                                                       S_IRUSR, S_IWUSR, S_IXUSR, fsp->is_directory);
+    group_access = map_unix_perms(&grp_acl_type, sbuf.st_mode,
+                                                       S_IRGRP, S_IWGRP, S_IXGRP, fsp->is_directory);
+    other_access = map_unix_perms(&other_acl_type, sbuf.st_mode,
+                                                       S_IROTH, S_IWOTH, S_IXOTH, fsp->is_directory);
+
+    if(owner_access.mask)
+      init_sec_ace(&ace_list[num_acls++], &owner_sid, owner_acl_type,
+                   owner_access, 0);
+
+    if(group_access.mask)
+      init_sec_ace(&ace_list[num_acls++], &group_sid, grp_acl_type,
+                   group_access, 0);
+
+    if(other_access.mask)
+      init_sec_ace(&ace_list[num_acls++], &global_sid_World, other_acl_type,
+                   other_access, 0);
+
+    if(fsp->is_directory) {
+      /*
+       * For directory ACLs we also add in the inherited permissions
+       * ACE entries. These are the permissions a file would get when
+       * being created in the directory.
+       */
+      mode_t mode = unix_mode( fsp->conn, FILE_ATTRIBUTE_ARCHIVE);
+
+      owner_access = map_unix_perms(&owner_acl_type, mode,
+                            S_IRUSR, S_IWUSR, S_IXUSR, fsp->is_directory);
+      group_access = map_unix_perms(&grp_acl_type, mode,
+                            S_IRGRP, S_IWGRP, S_IXGRP, fsp->is_directory);
+      other_access = map_unix_perms(&other_acl_type, mode,
+                            S_IROTH, S_IWOTH, S_IXOTH, fsp->is_directory);
+
+      if(owner_access.mask)
+        init_sec_ace(&ace_list[num_acls++], &owner_sid, owner_acl_type,
+                     owner_access, SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_INHERIT_ONLY);
+
+      if(group_access.mask)
+        init_sec_ace(&ace_list[num_acls++], &group_sid, grp_acl_type,
+                     group_access, SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_INHERIT_ONLY);
+
+      if(other_access.mask)
+        init_sec_ace(&ace_list[num_acls++], &global_sid_World, other_acl_type,
+                     other_access, SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_INHERIT_ONLY);
+    }
+
+    if(num_acls)
+      if((psa = make_sec_acl( 3, num_acls, ace_list)) == NULL) {
+        DEBUG(0,("get_nt_acl: Unable to malloc space for acl.\n"));
+        return 0;
+      }
+  }
+
+  *ppdesc = make_standard_sec_desc( &owner_sid, &group_sid, psa, &sec_desc_size);
+
+  if(!*ppdesc) {
+    DEBUG(0,("get_nt_acl: Unable to malloc space for security descriptor.\n"));
+    sec_desc_size = 0;
+  }
+
+  free_sec_acl(&psa);
+
+  return sec_desc_size;
+}
 
-  return(ERROR(ERRSRV,ERRnosupport));
-}
-   
 /****************************************************************************
- Reply to set a security descriptor - currently this is not implemented (it
- is planned to be though).
+ Reply to query a security descriptor - currently this is not implemented (it
+ is planned to be though). Right now it just returns the same thing NT would
+ when queried on a FAT filesystem. JRA.
 ****************************************************************************/
+
+static int call_nt_transact_query_security_desc(connection_struct *conn,
+                                                char *inbuf, char *outbuf, 
+                                                int length, int bufsize, 
+                                                char **ppsetup, char **ppparams, char **ppdata)
+{
+  uint32 max_data_count = IVAL(inbuf,smb_nt_MaxDataCount);
+  char *params = *ppparams;
+  char *data = *ppdata;
+  prs_struct pd;
+  SEC_DESC *psd;
+  size_t sec_desc_size;
+
+  files_struct *fsp = file_fsp(params,0);
+
+  if(!fsp)
+    return(ERROR(ERRDOS,ERRbadfid));
+
+  DEBUG(3,("call_nt_transact_query_security_desc: file = %s\n", fsp->fsp_name ));
+
+  params = *ppparams = Realloc(*ppparams, 4);
+  if(params == NULL)
+    return(ERROR(ERRDOS,ERRnomem));
+
+  /*
+   * Get the permissions to return.
+   */
+
+  if((sec_desc_size = get_nt_acl(fsp, &psd)) == 0)
+    return(UNIXERROR(ERRDOS,ERRnoaccess));
+
+  DEBUG(3,("call_nt_transact_query_security_desc: sec_desc_size = %d.\n",(int)sec_desc_size));
+
+  SIVAL(params,0,(uint32)sec_desc_size);
+
+  if(max_data_count < sec_desc_size) {
+
+    free_sec_desc(&psd);
+
+    send_nt_replies(inbuf, outbuf, bufsize, 0xC0000000|NT_STATUS_BUFFER_TOO_SMALL,
+                    params, 4, *ppdata, 0);
+    return -1;
+  }
+
+  /*
+   * Allocate the data we will point this at.
+   */
+
+  data = *ppdata = Realloc(*ppdata, sec_desc_size);
+  if(data == NULL) {
+    free_sec_desc(&psd);
+    return(ERROR(ERRDOS,ERRnomem));
+  }
+
+  memset(data, '\0', sec_desc_size);
+
+  /*
+   * Init the parse struct we will marshall into.
+   */
+
+  prs_init(&pd, 0, 4, MARSHALL);
+
+  /*
+   * Setup the prs_struct to point at the memory we just
+   * allocated.
+   */
+       
+  prs_give_memory( &pd, data, (uint32)sec_desc_size, False);
+
+  /*
+   * Finally, linearize into the outgoing buffer.
+   */
+
+  if(!sec_io_desc( "sd data", &psd, &pd, 1)) {
+    free_sec_desc(&psd);
+    DEBUG(0,("call_nt_transact_query_security_desc: Error in marshalling \
+security descriptor.\n"));
+    /*
+     * Return access denied for want of a better error message..
+     */ 
+    return(UNIXERROR(ERRDOS,ERRnoaccess));
+  }
+
+  /*
+   * Now we can delete the security descriptor.
+   */
+
+  free_sec_desc(&psd);
+
+  send_nt_replies(inbuf, outbuf, bufsize, 0, params, 4, data, (int)sec_desc_size);
+  return -1;
+}
+
+/****************************************************************************
+ Validate a SID.
+****************************************************************************/
+
+static BOOL validate_unix_sid( DOM_SID *psid, uint32 *prid, DOM_SID *sd_sid)
+{
+  extern DOM_SID global_sam_sid;
+  DOM_SID sid;
+
+  if(!sd_sid) {
+    DEBUG(5,("validate_unix_sid: sid missing.\n"));
+    return False;
+  }
+
+  sid_copy(psid, sd_sid);
+  sid_copy(&sid, sd_sid);
+
+  if(!sid_split_rid(&sid, prid)) {
+    DEBUG(5,("validate_unix_sid: cannot get RID from sid.\n"));
+    return False;
+  }
+
+  if(!sid_equal( &sid, &global_sam_sid)) {
+    DEBUG(5,("validate_unix_sid: sid is not ours.\n"));
+    return False;
+  }
+
+  return True;
+}
+
+/****************************************************************************
+ Map NT perms to UNIX.
+****************************************************************************/
+
+#define FILE_SPECIFIC_READ_BITS (FILE_READ_DATA|FILE_READ_EA|FILE_READ_ATTRIBUTES)
+#define FILE_SPECIFIC_WRITE_BITS (FILE_WRITE_DATA|FILE_APPEND_DATA|FILE_WRITE_EA|FILE_WRITE_ATTRIBUTES)
+#define FILE_SPECIFIC_EXECUTE_BITS (FILE_EXECUTE)
+
+static mode_t map_nt_perms( SEC_ACCESS sec_access, int type)
+{
+  mode_t mode = 0;
+
+  switch(type) {
+  case S_IRUSR:
+    if(sec_access.mask & GENERIC_ALL_ACCESS)
+      mode = S_IRUSR|S_IWUSR|S_IXUSR;
+    else {
+      mode |= (sec_access.mask & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IRUSR : 0;
+      mode |= (sec_access.mask & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWUSR : 0;
+      mode |= (sec_access.mask & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXUSR : 0;
+    }
+    break;
+  case S_IRGRP:
+    if(sec_access.mask & GENERIC_ALL_ACCESS)
+      mode = S_IRGRP|S_IWGRP|S_IXGRP;
+    else {
+      mode |= (sec_access.mask & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IRGRP : 0;
+      mode |= (sec_access.mask & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWGRP : 0;
+      mode |= (sec_access.mask & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXGRP : 0;
+    }
+    break;
+  case S_IROTH:
+    if(sec_access.mask & GENERIC_ALL_ACCESS)
+      mode = S_IROTH|S_IWOTH|S_IXOTH;
+    else {
+      mode |= (sec_access.mask & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IROTH : 0;
+      mode |= (sec_access.mask & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWOTH : 0;
+      mode |= (sec_access.mask & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXOTH : 0;
+    }
+    break;
+  }
+
+  return mode;
+}
+
+/****************************************************************************
+ Unpack a SEC_DESC into a owner, group and set of UNIX permissions.
+****************************************************************************/
+
+static BOOL unpack_nt_permissions(uid_t *puser, gid_t *pgrp, mode_t *pmode, uint32 security_info_sent,
+                                  SEC_DESC *psd, BOOL is_directory)
+{
+  extern DOM_SID global_sid_World;
+  DOM_SID owner_sid;
+  DOM_SID grp_sid;
+  uint32 owner_rid;
+  uint32 grp_rid;
+  SEC_ACL *dacl = psd->dacl;
+  int i;
+
+  *pmode = 0;
+  *puser = (uid_t)-1;
+  *pgrp = (gid_t)-1;
+
+  if(security_info_sent == 0) {
+    DEBUG(0,("unpack_unix_permissions: no security info sent !\n"));
+    return False;
+  }
+
+  /*
+   * Validate the owner and group SID's.
+   */
+
+  memset(&owner_sid, '\0', sizeof(owner_sid));
+  memset(&grp_sid, '\0', sizeof(grp_sid));
+
+  DEBUG(5,("unpack_unix_permissions: validating owner_sid.\n"));
+
+  /*
+   * Don't immediately fail if the owner sid cannot be validated.
+   * This may be a group chown only set.
+   */
+
+  if(!validate_unix_sid( &owner_sid, &owner_rid, psd->owner_sid))
+    DEBUG(3,("unpack_unix_permissions: unable to validate owner sid.\n"));
+  else if(security_info_sent & OWNER_SECURITY_INFORMATION)
+    *puser = pdb_user_rid_to_uid(owner_rid);
+
+  /*
+   * Don't immediately fail if the group sid cannot be validated.
+   * This may be an owner chown only set.
+   */
+
+  if(!validate_unix_sid( &grp_sid, &grp_rid, psd->grp_sid))
+    DEBUG(3,("unpack_unix_permissions: unable to validate group sid.\n"));
+  else if(security_info_sent & GROUP_SECURITY_INFORMATION)
+    *pgrp = pdb_user_rid_to_gid(grp_rid);
+
+  /*
+   * If no DACL then this is a chown only security descriptor.
+   */
+
+  if(!(security_info_sent & DACL_SECURITY_INFORMATION) || !dacl) {
+    *pmode = 0;
+    return True;
+  }
+
+  /*
+   * Now go through the DACL and ensure that
+   * any owner/group sids match.
+   */
+
+  for(i = 0; i < dacl->num_aces; i++) {
+    DOM_SID ace_sid;
+    SEC_ACE *psa = &dacl->ace_list[i];
+
+    if((psa->type != SEC_ACE_TYPE_ACCESS_ALLOWED) &&
+       (psa->type != SEC_ACE_TYPE_ACCESS_DENIED)) {
+      DEBUG(3,("unpack_unix_permissions: unable to set anything but an ALLOW or DENY ACE.\n"));
+      return False;
+    }
+
+    /*
+     * Ignore or remove bits we don't care about on a directory ACE.
+     */
+
+    if(is_directory) {
+      if(psa->flags & SEC_ACE_FLAG_INHERIT_ONLY) {
+        DEBUG(3,("unpack_unix_permissions: ignoring inherit only ACE.\n"));
+        continue;
+      }
+
+      psa->flags &= ~(SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT);
+    }
+
+    if(psa->flags != 0) {
+      DEBUG(1,("unpack_unix_permissions: unable to set ACE flags (%x).\n", 
+            (unsigned int)psa->flags));
+      return False;
+    }
+
+    /*
+     * The security mask may be UNIX_ACCESS_NONE which should map into
+     * no permissions (we overload the WRITE_OWNER bit for this) or it
+     * should be one of the ALL/EXECUTE/READ/WRITE bits. Arrange for this
+     * to be so. Any other bits override the UNIX_ACCESS_NONE bit.
+     */
+
+    psa->info.mask &= (GENERIC_ALL_ACCESS|GENERIC_EXECUTE_ACCESS|GENERIC_WRITE_ACCESS|
+                     GENERIC_READ_ACCESS|UNIX_ACCESS_NONE|FILE_ALL_ATTRIBUTES);
+
+    if(psa->info.mask != UNIX_ACCESS_NONE)
+      psa->info.mask &= ~UNIX_ACCESS_NONE;
+
+    sid_copy(&ace_sid, &psa->sid);
+
+    if(sid_equal(&ace_sid, &owner_sid)) {
+      /*
+       * Map the desired permissions into owner perms.
+       */
+
+      if(psa->type == SEC_ACE_TYPE_ACCESS_ALLOWED)
+        *pmode |= map_nt_perms( psa->info, S_IRUSR);
+      else
+        *pmode &= ~(map_nt_perms( psa->info, S_IRUSR));
+
+    } else if( sid_equal(&ace_sid, &grp_sid)) {
+      /*
+       * Map the desired permissions into group perms.
+       */
+
+      if(psa->type == SEC_ACE_TYPE_ACCESS_ALLOWED)
+        *pmode |= map_nt_perms( psa->info, S_IRGRP);
+      else
+        *pmode &= ~(map_nt_perms( psa->info, S_IRGRP));
+
+    } else if( sid_equal(&ace_sid, &global_sid_World)) {
+      /*
+       * Map the desired permissions into other perms.
+       */
+
+      if(psa->type == SEC_ACE_TYPE_ACCESS_ALLOWED)
+        *pmode |= map_nt_perms( psa->info, S_IROTH);
+      else
+        *pmode &= ~(map_nt_perms( psa->info, S_IROTH));
+
+    } else {
+      DEBUG(0,("unpack_unix_permissions: unknown SID used in ACL.\n"));
+      return False;
+    }
+  }
+
+  return True;
+}
+
+/****************************************************************************
+ Reply to set a security descriptor. Map to UNIX perms.
+****************************************************************************/
+
 static int call_nt_transact_set_security_desc(connection_struct *conn,
-                                             char *inbuf, char *outbuf, 
-                                             int length,
-                                              int bufsize, 
-                                              char **ppsetup, 
-                                             char **ppparams, char **ppdata)
+                                                                       char *inbuf, char *outbuf, int length,
+                                                                       int bufsize, char **ppsetup, 
+                                                                       char **ppparams, char **ppdata)
 {
-  static BOOL logged_message = False;
+  uint32 total_parameter_count = IVAL(inbuf, smb_nts_TotalParameterCount);
+  char *params= *ppparams;
+  char *data = *ppdata;
+  prs_struct pd;
+  SEC_DESC *psd = NULL;
+  uint32 total_data_count = (uint32)IVAL(inbuf, smb_nts_TotalDataCount);
+  uid_t user = (uid_t)-1;
+  gid_t grp = (gid_t)-1;
+  mode_t perms = 0;
+  SMB_STRUCT_STAT sbuf;
+  files_struct *fsp = NULL;
+  uint32 security_info_sent = 0;
+  BOOL got_dacl = False;
 
-  if(!logged_message) {
-    DEBUG(0,("call_nt_transact_set_security_desc: Currently not implemented.\n"));
-    logged_message = True; /* Only print this once... */
+  if(!lp_nt_acl_support())
+    return(UNIXERROR(ERRDOS,ERRnoaccess));
+
+  if(total_parameter_count < 8)
+    return(ERROR(ERRDOS,ERRbadfunc));
+
+  if((fsp = file_fsp(params,0)) == NULL)
+    return(ERROR(ERRDOS,ERRbadfid));
+
+  security_info_sent = IVAL(params,4);
+
+  DEBUG(3,("call_nt_transact_set_security_desc: file = %s, sent 0x%x\n", fsp->fsp_name,
+       (unsigned int)security_info_sent ));
+
+  /*
+   * Init the parse struct we will unmarshall from.
+   */
+
+  prs_init(&pd, 0, 4, UNMARSHALL);
+
+  /*
+   * Setup the prs_struct to point at the memory we just
+   * allocated.
+   */
+       
+  prs_give_memory( &pd, data, total_data_count, False);
+
+  /*
+   * Finally, unmarshall from the data buffer.
+   */
+
+  if(!sec_io_desc( "sd data", &psd, &pd, 1)) {
+    free_sec_desc(&psd);
+    DEBUG(0,("call_nt_transact_set_security_desc: Error in unmarshalling \
+security descriptor.\n"));
+    /*
+     * Return access denied for want of a better error message..
+     */ 
+    return(UNIXERROR(ERRDOS,ERRnoaccess));
   }
-  return(ERROR(ERRSRV,ERRnosupport));
+
+  /*
+   * Unpack the user/group/world id's and permissions.
+   */
+
+  if(!unpack_nt_permissions( &user, &grp, &perms, security_info_sent, psd, fsp->is_directory)) {
+    free_sec_desc(&psd);
+    return(UNIXERROR(ERRDOS,ERRnoaccess));
+  }
+
+  if (psd->dacl != NULL)
+    got_dacl = True;
+
+  free_sec_desc(&psd);
+
+  /*
+   * Get the current state of the file.
+   */
+
+  if(fsp->is_directory) {
+    if(dos_stat(fsp->fsp_name, &sbuf) != 0) {
+      return(UNIXERROR(ERRDOS,ERRnoaccess));
+    }
+  } else {
+
+    int ret;
+
+    if(fsp->fd_ptr == NULL)
+      ret = dos_stat(fsp->fsp_name, &sbuf);
+    else
+      ret = sys_fstat(fsp->fd_ptr->fd,&sbuf);
+
+    if(ret != 0) {
+      return(UNIXERROR(ERRDOS,ERRnoaccess));
+    }
+  }
+
+  /*
+   * Do we need to chown ?
+   */
+
+  if((user != (uid_t)-1 || grp != (uid_t)-1) && (sbuf.st_uid != user || sbuf.st_gid != grp)) {
+
+    DEBUG(3,("call_nt_transact_set_security_desc: chown %s. uid = %u, gid = %u.\n",
+          fsp->fsp_name, (unsigned int)user, (unsigned int)grp ));
+
+    if(dos_chown( fsp->fsp_name, user, grp) == -1) {
+      DEBUG(3,("call_nt_transact_set_security_desc: chown %s, %u, %u failed. Error = %s.\n",
+            fsp->fsp_name, (unsigned int)user, (unsigned int)grp, strerror(errno) ));
+      return(UNIXERROR(ERRDOS,ERRnoaccess));
+    }
+
+    /*
+     * Recheck the current state of the file, which may have changed.
+     * (suid/sgid bits, for instance)
+     */
+
+    if(fsp->is_directory) {
+      if(dos_stat(fsp->fsp_name, &sbuf) != 0) {
+        return(UNIXERROR(ERRDOS,ERRnoaccess));
+      }
+    } else {
+
+      int ret;
+    
+      if(fsp->fd_ptr == NULL)
+        ret = dos_stat(fsp->fsp_name, &sbuf);
+      else
+        ret = sys_fstat(fsp->fd_ptr->fd,&sbuf);
+  
+      if(ret != 0)
+        return(UNIXERROR(ERRDOS,ERRnoaccess));
+    }
+  }
+
+  /*
+   * Only change security if we got a DACL.
+   */
+
+  if((security_info_sent & DACL_SECURITY_INFORMATION) && got_dacl) {
+
+    /*
+     * Check to see if we need to change anything.
+     * Enforce limits on modified bits *only*. Don't enforce masks
+        * on bits not changed by the user.
+     */
+
+    if(fsp->is_directory) {
+
+      perms &= (lp_dir_security_mask(SNUM(conn)) | sbuf.st_mode);
+      perms |= (lp_force_dir_security_mode(SNUM(conn)) & ( perms ^ sbuf.st_mode ));
+
+    } else {
+
+      perms &= (lp_security_mask(SNUM(conn)) | sbuf.st_mode); 
+      perms |= (lp_force_security_mode(SNUM(conn)) & ( perms ^ sbuf.st_mode ));
+
+    }
+
+    /*
+     * Preserve special bits.
+     */
+
+    perms |= (sbuf.st_mode & ~0777);
+
+    /*
+     * Do we need to chmod ?
+     */
+
+    if(sbuf.st_mode != perms) {
+
+      DEBUG(3,("call_nt_transact_set_security_desc: chmod %s. perms = 0%o.\n",
+            fsp->fsp_name, (unsigned int)perms ));
+
+      if(dos_chmod( fsp->fsp_name, perms) == -1) {
+        DEBUG(3,("call_nt_transact_set_security_desc: chmod %s, 0%o failed. Error = %s.\n",
+              fsp->fsp_name, (unsigned int)perms, strerror(errno) ));
+        return(UNIXERROR(ERRDOS,ERRnoaccess));
+      }
+    }
+  }
+
+  send_nt_replies(inbuf, outbuf, bufsize, 0, NULL, 0, NULL, 0);
+  return -1;
 }
    
 /****************************************************************************
@@ -1675,7 +2571,6 @@ due to being in oplock break state.\n" ));
                                                     length, bufsize, 
                                                      &setup, &params, &data);
       break;
-
   default:
          /* Error in request */
          DEBUG(0,("reply_nttrans: Unknown request %d in nttrans call\n", function_code));