Second part of the fix for bug #5790 - samba returns STATUS_OBJECT_NAME_NOT_FOUND...
[jra/samba/.git] / source3 / smbd / files.c
index a4837a1a8bc9b0d6704e79b4d4d604d0b9892e68..8d06e20f810f541c5f63ec858c3baf3d7f186ecf 100644 (file)
@@ -1,12 +1,11 @@
 /* 
-   Unix SMB/Netbios implementation.
-   Version 1.9.
+   Unix SMB/CIFS implementation.
    Files[] structure handling
    Copyright (C) Andrew Tridgell 1998
    
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2 of the License, or
+   the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.
    
    This program is distributed in the hope that it will be useful,
    GNU General Public License for more details.
    
    You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software
-   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
 #include "includes.h"
 
-extern int DEBUGLEVEL;
-
 static int real_max_open_files;
 
 #define VALID_FNUM(fnum)   (((fnum) >= 0) && ((fnum) < real_max_open_files))
@@ -35,19 +31,37 @@ static files_struct *Files;
  
 /* a fsp to use when chaining */
 static files_struct *chain_fsp = NULL;
-/* a fsp to use to save when breaking an oplock. */
-static files_struct *oplock_save_chain_fsp = NULL;
 
 static int files_used;
 
+/* A singleton cache to speed up searching by dev/inode. */
+static struct fsp_singleton_cache {
+       files_struct *fsp;
+       struct file_id id;
+} fsp_fi_cache;
+
 /****************************************************************************
-  find first available file slot
+ Return a unique number identifying this fsp over the life of this pid.
 ****************************************************************************/
-files_struct *file_new(void )
+
+static unsigned long get_gen_count(void)
+{
+       static unsigned long file_gen_counter;
+
+       if ((++file_gen_counter) == 0)
+               return ++file_gen_counter;
+       return file_gen_counter;
+}
+
+/****************************************************************************
+ Find first available file slot.
+****************************************************************************/
+
+NTSTATUS file_new(connection_struct *conn, files_struct **result)
 {
        int i;
        static int first_file;
-       files_struct *fsp, *next;
+       files_struct *fsp;
 
        /* we want to give out file handles differently on each new
           connection because of a common bug in MS clients where they try to
@@ -58,39 +72,38 @@ files_struct *file_new(void )
                first_file = (sys_getpid() ^ (int)time(NULL)) % real_max_open_files;
        }
 
+       /* TODO: Port the id-tree implementation from Samba4 */
+
        i = bitmap_find(file_bmap, first_file);
        if (i == -1) {
-               /* 
-                * Before we give up, go through the open files 
-                * and see if there are any files opened with a
-                * batch oplock. If so break the oplock and then
-                * re-use that entry (if it becomes closed).
-                * This may help as NT/95 clients tend to keep
-                * files batch oplocked for quite a long time
-                * after they have finished with them.
-                */
-               for (fsp=Files;fsp;fsp=next) {
-                       next=fsp->next;
-                       if (attempt_close_oplocked_file(fsp)) {
-                               return file_new();
-                       }
-               }
-
                DEBUG(0,("ERROR! Out of file structures\n"));
-               unix_ERR_class = ERRSRV;
-               unix_ERR_code = ERRnofids;
-               return NULL;
+               /* TODO: We have to unconditionally return a DOS error here,
+                * W2k3 even returns ERRDOS/ERRnofids for ntcreate&x with
+                * NTSTATUS negotiated */
+               return NT_STATUS_TOO_MANY_OPENED_FILES;
        }
 
-       fsp = (files_struct *)malloc(sizeof(*fsp));
+       fsp = SMB_MALLOC_P(files_struct);
        if (!fsp) {
-               unix_ERR_class = ERRSRV;
-               unix_ERR_code = ERRnofids;
-               return NULL;
+               return NT_STATUS_NO_MEMORY;
        }
 
        ZERO_STRUCTP(fsp);
-       fsp->fd = -1;
+
+       fsp->fh = SMB_MALLOC_P(struct fd_handle);
+       if (!fsp->fh) {
+               SAFE_FREE(fsp);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       ZERO_STRUCTP(fsp->fh);
+
+       fsp->fh->ref_count = 1;
+       fsp->fh->fd = -1;
+
+       fsp->conn = conn;
+       fsp->fh->gen_id = get_gen_count();
+       GetTimeOfDay(&fsp->open_time);
 
        first_file = (i+1) % real_max_open_files;
 
@@ -98,6 +111,8 @@ files_struct *file_new(void )
        files_used++;
 
        fsp->fnum = i + FILE_HANDLE_OFFSET;
+       SMB_ASSERT(fsp->fnum < 65536);
+
        string_set(&fsp->fsp_name,"");
        
        DLIST_ADD(Files, fsp);
@@ -106,14 +121,22 @@ files_struct *file_new(void )
                 i, fsp->fnum, files_used));
 
        chain_fsp = fsp;
-       
-       return fsp;
-}
 
+       /* A new fsp invalidates the positive and
+         negative fsp_fi_cache as the new fsp is pushed
+         at the start of the list and we search from
+         a cache hit to the *end* of the list. */
+
+       ZERO_STRUCT(fsp_fi_cache);
+
+       *result = fsp;
+       return NT_STATUS_OK;
+}
 
 /****************************************************************************
-close all open files for a connection
+ Close all open files for a connection.
 ****************************************************************************/
+
 void file_close_conn(connection_struct *conn)
 {
        files_struct *fsp, *next;
@@ -121,20 +144,36 @@ void file_close_conn(connection_struct *conn)
        for (fsp=Files;fsp;fsp=next) {
                next = fsp->next;
                if (fsp->conn == conn) {
-                       close_file(fsp,False); 
+                       close_file(fsp,SHUTDOWN_CLOSE); 
                }
        }
 }
 
 /****************************************************************************
-initialise file structures
+ Close all open files for a pid and a vuid.
 ****************************************************************************/
 
-#define MAX_OPEN_FUDGEFACTOR 10
+void file_close_pid(uint16 smbpid, int vuid)
+{
+       files_struct *fsp, *next;
+       
+       for (fsp=Files;fsp;fsp=next) {
+               next = fsp->next;
+               if ((fsp->file_pid == smbpid) && (fsp->vuid == vuid)) {
+                       close_file(fsp,SHUTDOWN_CLOSE); 
+               }
+       }
+}
+
+/****************************************************************************
+ Initialise file structures.
+****************************************************************************/
+
+#define MAX_OPEN_FUDGEFACTOR 20
 
 void file_init(void)
 {
-        int request_max_open_files = lp_max_open_files();
+       int request_max_open_files = lp_max_open_files();
        int real_lim;
 
        /*
@@ -146,11 +185,16 @@ void file_init(void)
 
        real_max_open_files = real_lim - MAX_OPEN_FUDGEFACTOR;
 
-        if(real_max_open_files != request_max_open_files) {
-               DEBUG(1,("file_init: Information only: requested %d \
+       if (real_max_open_files + FILE_HANDLE_OFFSET + MAX_OPEN_PIPES > 65536)
+               real_max_open_files = 65536 - FILE_HANDLE_OFFSET - MAX_OPEN_PIPES;
+
+       if(real_max_open_files != request_max_open_files) {
+               DEBUG(1,("file_init: Information only: requested %d \
 open files, %d are available.\n", request_max_open_files, real_max_open_files));
        }
 
+       SMB_ASSERT(real_max_open_files > 100);
+
        file_bmap = bitmap_allocate(real_max_open_files);
        
        if (!file_bmap) {
@@ -163,10 +207,10 @@ open files, %d are available.\n", request_max_open_files, real_max_open_files));
        set_pipe_handle_offset(real_max_open_files);
 }
 
-
 /****************************************************************************
-close files open by a specified vuid
+ Close files open by a specified vuid.
 ****************************************************************************/
+
 void file_close_user(int vuid)
 {
        files_struct *fsp, *next;
@@ -174,31 +218,75 @@ void file_close_user(int vuid)
        for (fsp=Files;fsp;fsp=next) {
                next=fsp->next;
                if (fsp->vuid == vuid) {
-                       close_file(fsp,False);
+                       close_file(fsp,SHUTDOWN_CLOSE);
                }
        }
 }
 
+/****************************************************************************
+ Debug to enumerate all open files in the smbd.
+****************************************************************************/
+
+void file_dump_open_table(void)
+{
+       int count=0;
+       files_struct *fsp;
+
+       for (fsp=Files;fsp;fsp=fsp->next,count++) {
+               DEBUG(10,("Files[%d], fnum = %d, name %s, fd = %d, gen = %lu, fileid=%s\n",
+                       count, fsp->fnum, fsp->fsp_name, fsp->fh->fd, (unsigned long)fsp->fh->gen_id,
+                         file_id_string_tos(&fsp->file_id)));
+       }
+}
+
+/****************************************************************************
+ Find a fsp given a file descriptor.
+****************************************************************************/
+
+files_struct *file_find_fd(int fd)
+{
+       int count=0;
+       files_struct *fsp;
+
+       for (fsp=Files;fsp;fsp=fsp->next,count++) {
+               if (fsp->fh->fd == fd) {
+                       if (count > 10) {
+                               DLIST_PROMOTE(Files, fsp);
+                       }
+                       return fsp;
+               }
+       }
+
+       return NULL;
+}
 
 /****************************************************************************
- Find a fsp given a device, inode and timevalue
- If this is from a kernel oplock break request then tval may be NULL.
+ Find a fsp given a device, inode and file_id.
 ****************************************************************************/
 
-files_struct *file_find_dit(SMB_DEV_T dev, SMB_INO_T inode, struct timeval *tval)
+files_struct *file_find_dif(struct file_id id, unsigned long gen_id)
 {
        int count=0;
        files_struct *fsp;
 
        for (fsp=Files;fsp;fsp=fsp->next,count++) {
-               if (fsp->fd != -1 &&
-                   fsp->dev == dev && 
-                   fsp->inode == inode &&
-                   (tval ? (fsp->open_time.tv_sec == tval->tv_sec) : True ) &&
-                   (tval ? (fsp->open_time.tv_usec == tval->tv_usec) : True )) {
+               /* We can have a fsp->fh->fd == -1 here as it could be a stat open. */
+               if (file_id_equal(&fsp->file_id, &id) &&
+                   fsp->fh->gen_id == gen_id ) {
                        if (count > 10) {
                                DLIST_PROMOTE(Files, fsp);
                        }
+                       /* Paranoia check. */
+                       if ((fsp->fh->fd == -1) &&
+                           (fsp->oplock_type != NO_OPLOCK) &&
+                           (fsp->oplock_type != FAKE_LEVEL_II_OPLOCK)) {
+                               DEBUG(0,("file_find_dif: file %s file_id = %s, gen = %u \
+oplock_type = %u is a stat open with oplock type !\n", fsp->fsp_name, 
+                                        file_id_string_tos(&fsp->file_id),
+                                        (unsigned int)fsp->fh->gen_id,
+                                        (unsigned int)fsp->oplock_type ));
+                               smb_panic("file_find_dif");
+                       }
                        return fsp;
                }
        }
@@ -214,30 +302,42 @@ files_struct *file_find_fsp(files_struct *orig_fsp)
 {
        files_struct *fsp;
 
-    for (fsp=Files;fsp;fsp=fsp->next) {
-        if (fsp == orig_fsp)
-            return fsp;
-    }
+       for (fsp=Files;fsp;fsp=fsp->next) {
+               if (fsp == orig_fsp)
+                       return fsp;
+       }
 
-    return NULL;
+       return NULL;
 }
 
 /****************************************************************************
  Find the first fsp given a device and inode.
+ We use a singleton cache here to speed up searching from getfilepathinfo
+ calls.
 ****************************************************************************/
 
-files_struct *file_find_di_first(SMB_DEV_T dev, SMB_INO_T inode)
+files_struct *file_find_di_first(struct file_id id)
 {
-    files_struct *fsp;
+       files_struct *fsp;
 
-    for (fsp=Files;fsp;fsp=fsp->next) {
-        if ( fsp->fd != -1 &&
-            fsp->dev == dev &&
-            fsp->inode == inode )
-            return fsp;
-    }
+       if (file_id_equal(&fsp_fi_cache.id, &id)) {
+               /* Positive or negative cache hit. */
+               return fsp_fi_cache.fsp;
+       }
+
+       fsp_fi_cache.id = id;
+
+       for (fsp=Files;fsp;fsp=fsp->next) {
+               if (file_id_equal(&fsp->file_id, &id)) {
+                       /* Setup positive cache. */
+                       fsp_fi_cache.fsp = fsp;
+                       return fsp;
+               }
+       }
 
-    return NULL;
+       /* Setup negative cache. */
+       fsp_fi_cache.fsp = NULL;
+       return NULL;
 }
 
 /****************************************************************************
@@ -246,58 +346,81 @@ files_struct *file_find_di_first(SMB_DEV_T dev, SMB_INO_T inode)
 
 files_struct *file_find_di_next(files_struct *start_fsp)
 {
-    files_struct *fsp;
+       files_struct *fsp;
 
-    for (fsp = start_fsp->next;fsp;fsp=fsp->next) {
-        if ( fsp->fd != -1 &&
-            fsp->dev == start_fsp->dev &&
-            fsp->inode == start_fsp->inode )
-            return fsp;
-    }
+       for (fsp = start_fsp->next;fsp;fsp=fsp->next) {
+               if (file_id_equal(&fsp->file_id, &start_fsp->file_id)) {
+                       return fsp;
+               }
+       }
 
-    return NULL;
+       return NULL;
 }
 
 /****************************************************************************
-find a fsp that is open for printing
+ Find a fsp that is open for printing.
 ****************************************************************************/
+
 files_struct *file_find_print(void)
 {
        files_struct *fsp;
 
        for (fsp=Files;fsp;fsp=fsp->next) {
-               if (fsp->print_file) return fsp;
+               if (fsp->print_file) {
+                       return fsp;
+               }
        } 
 
        return NULL;
 }
 
-
 /****************************************************************************
-sync open files on a connection
+ Sync open files on a connection.
 ****************************************************************************/
+
 void file_sync_all(connection_struct *conn)
 {
        files_struct *fsp, *next;
 
        for (fsp=Files;fsp;fsp=next) {
                next=fsp->next;
-               if ((conn == fsp->conn) && (fsp->fd != -1)) {
-                       sync_file(conn,fsp);
+               if ((conn == fsp->conn) && (fsp->fh->fd != -1)) {
+                       sync_file(conn, fsp, True /* write through */);
                }
        }
 }
 
-
 /****************************************************************************
-free up a fsp
+ Free up a fsp.
 ****************************************************************************/
+
 void file_free(files_struct *fsp)
 {
        DLIST_REMOVE(Files, fsp);
 
        string_free(&fsp->fsp_name);
 
+       if (fsp->fake_file_handle) {
+               destroy_fake_file_handle(&fsp->fake_file_handle);
+       }
+
+       if (fsp->fh->ref_count == 1) {
+               SAFE_FREE(fsp->fh);
+       } else {
+               fsp->fh->ref_count--;
+       }
+
+       if (fsp->notify) {
+               notify_remove(fsp->conn->notify_ctx, fsp);
+               TALLOC_FREE(fsp->notify);
+       }
+
+       /* Ensure this event will never fire. */
+       TALLOC_FREE(fsp->oplock_timeout);
+
+       /* Ensure this event will never fire. */
+       TALLOC_FREE(fsp->update_write_time_event);
+
        bitmap_clear(file_bmap, fsp->fnum - FILE_HANDLE_OFFSET);
        files_used--;
 
@@ -308,27 +431,34 @@ void file_free(files_struct *fsp)
           information */
        ZERO_STRUCTP(fsp);
 
-       if (fsp == chain_fsp) chain_fsp = NULL;
+       if (fsp == chain_fsp) {
+               chain_fsp = NULL;
+       }
 
-       free(fsp);
-}
+       /* Closing a file can invalidate the positive cache. */
+       if (fsp == fsp_fi_cache.fsp) {
+               ZERO_STRUCT(fsp_fi_cache);
+       }
+
+       /* Drop all remaining extensions. */
+       while (fsp->vfs_extension) {
+               vfs_remove_fsp_extension(fsp->vfs_extension->owner, fsp);
+       }
 
+       SAFE_FREE(fsp);
+}
 
 /****************************************************************************
-get a fsp from a packet given the offset of a 16 bit fnum
+ Get an fsp from a 16 bit fnum.
 ****************************************************************************/
-files_struct *file_fsp(char *buf, int where)
+
+files_struct *file_fnum(uint16 fnum)
 {
-       int fnum, count=0;
        files_struct *fsp;
-
-       if (chain_fsp) return chain_fsp;
-
-       fnum = SVAL(buf, where);
+       int count=0;
 
        for (fsp=Files;fsp;fsp=fsp->next, count++) {
                if (fsp->fnum == fnum) {
-                       chain_fsp = fsp;
                        if (count > 10) {
                                DLIST_PROMOTE(Files, fsp);
                        }
@@ -339,27 +469,79 @@ files_struct *file_fsp(char *buf, int where)
 }
 
 /****************************************************************************
- Reset the chained fsp - done at the start of a packet reply
+ Get an fsp from a packet given the offset of a 16 bit fnum.
 ****************************************************************************/
 
-void file_chain_reset(void)
+files_struct *file_fsp(uint16 fid)
 {
-       chain_fsp = NULL;
+       files_struct *fsp;
+
+       if (chain_fsp) {
+               return chain_fsp;
+       }
+
+       fsp = file_fnum(fid);
+       if (fsp) {
+               chain_fsp = fsp;
+       }
+       return fsp;
 }
 
 /****************************************************************************
-Save the chained fsp - done when about to do an oplock break.
+ Reset the chained fsp - done at the start of a packet reply.
 ****************************************************************************/
 
-void file_chain_save(void)
+void file_chain_reset(void)
 {
-       oplock_save_chain_fsp = chain_fsp;
+       chain_fsp = NULL;
 }
 
 /****************************************************************************
-Restore the chained fsp - done after an oplock break.
+ Duplicate the file handle part for a DOS or FCB open.
 ****************************************************************************/
-void file_chain_restore(void)
+
+NTSTATUS dup_file_fsp(files_struct *fsp,
+                               uint32 access_mask,
+                               uint32 share_access,
+                               uint32 create_options,
+                               files_struct **result)
 {
-       chain_fsp = oplock_save_chain_fsp;
+       NTSTATUS status;
+       files_struct *dup_fsp;
+
+       status = file_new(fsp->conn, &dup_fsp);
+
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       SAFE_FREE(dup_fsp->fh);
+
+       dup_fsp->fh = fsp->fh;
+       dup_fsp->fh->ref_count++;
+
+       dup_fsp->file_id = fsp->file_id;
+       dup_fsp->initial_allocation_size = fsp->initial_allocation_size;
+       dup_fsp->mode = fsp->mode;
+       dup_fsp->file_pid = fsp->file_pid;
+       dup_fsp->vuid = fsp->vuid;
+       dup_fsp->open_time = fsp->open_time;
+       dup_fsp->access_mask = access_mask;
+       dup_fsp->share_access = share_access;
+       dup_fsp->oplock_type = fsp->oplock_type;
+       dup_fsp->can_lock = fsp->can_lock;
+       dup_fsp->can_read = (access_mask & (FILE_READ_DATA)) ? True : False;
+       if (!CAN_WRITE(fsp->conn)) {
+               dup_fsp->can_write = False;
+       } else {
+               dup_fsp->can_write = (access_mask & (FILE_WRITE_DATA | FILE_APPEND_DATA)) ? True : False;
+       }
+       dup_fsp->print_file = fsp->print_file;
+       dup_fsp->modified = fsp->modified;
+       dup_fsp->is_directory = fsp->is_directory;
+       dup_fsp->aio_write_behind = fsp->aio_write_behind;
+        string_set(&dup_fsp->fsp_name,fsp->fsp_name);
+
+       *result = dup_fsp;
+       return NT_STATUS_OK;
 }