Oops. Don't break the build..
[ira/wip.git] / source3 / smbd / files.c
index d62950c568ae5fb1fe2e669f6cffaff66133d704..2bc5ba6816e0a75ed431c6239652c765364ab654 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;
+#include "smbd/globals.h"
 
 #define VALID_FNUM(fnum)   (((fnum) >= 0) && ((fnum) < real_max_open_files))
 
 #define FILE_HANDLE_OFFSET 0x1000
 
-static struct bitmap *file_bmap;
-
-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;
-
-/*
- * Indirection for file fd's. Needed as POSIX locking
- * is based on file/process, not fd/process.
- */
-static file_fd_struct *FileFd;
+/****************************************************************************
+ Return a unique number identifying this fsp over the life of this pid.
+****************************************************************************/
 
-static int files_used, fd_ptr_used;
+static unsigned long get_gen_count(void)
+{
+       if ((++file_gen_counter) == 0)
+               return ++file_gen_counter;
+       return file_gen_counter;
+}
 
 /****************************************************************************
-  find first available file slot
+ Find first available file slot.
 ****************************************************************************/
-files_struct *file_new(void )
+
+NTSTATUS file_new(struct smb_request *req, connection_struct *conn,
+                 files_struct **result)
 {
        int i;
-       static int first_file;
-       files_struct *fsp, *next;
+       files_struct *fsp;
+       NTSTATUS status;
 
        /* we want to give out file handles differently on each new
           connection because of a common bug in MS clients where they try to
@@ -61,35 +52,46 @@ files_struct *file_new(void )
           increases the chance that the errant client will get an error rather
           than causing corruption */
        if (first_file == 0) {
-               first_file = (getpid() ^ (int)time(NULL)) % real_max_open_files;
+               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"));
-               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));
-       if (!fsp) return NULL;
+       /*
+        * Make a child of the connection_struct as an fsp can't exist
+        * indepenedent of a connection.
+        */
+       fsp = talloc_zero(conn, struct files_struct);
+       if (!fsp) {
+               return NT_STATUS_NO_MEMORY;
+       }
 
-       ZERO_STRUCTP(fsp);
+       /*
+        * This can't be a child of fsp because the file_handle can be ref'd
+        * when doing a dos/fcb open, which will then share the file_handle
+        * across multiple fsps.
+        */
+       fsp->fh = talloc_zero(conn, struct fd_handle);
+       if (!fsp->fh) {
+               TALLOC_FREE(fsp);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       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;
 
@@ -97,109 +99,83 @@ files_struct *file_new(void )
        files_used++;
 
        fsp->fnum = i + FILE_HANDLE_OFFSET;
-       string_set(&fsp->fsp_name,"");
-       
+       SMB_ASSERT(fsp->fnum < 65536);
+
+       /*
+        * Create an smb_filename with "" for the base_name.  There are very
+        * few NULL checks, so make sure it's initialized with something. to
+        * be safe until an audit can be done.
+        */
+       status = create_synthetic_smb_fname(fsp, "", NULL, NULL,
+                                           &fsp->fsp_name);
+       if (!NT_STATUS_IS_OK(status)) {
+               TALLOC_FREE(fsp);
+               TALLOC_FREE(fsp->fh);
+       }
+
        DLIST_ADD(Files, fsp);
 
        DEBUG(5,("allocated file structure %d, fnum = %d (%d used)\n",
                 i, fsp->fnum, files_used));
 
-       chain_fsp = fsp;
-       
-       return fsp;
-}
-
-
-
-/****************************************************************************
-fd support routines - attempt to find an already open file by dev
-and inode - increments the ref_count of the returned file_fd_struct *.
-****************************************************************************/
-file_fd_struct *fd_get_already_open(SMB_STRUCT_STAT *sbuf)
-{
-       file_fd_struct *fd_ptr;
-
-       if(!sbuf) return NULL;
+       if (req != NULL) {
+               req->chain_fsp = fsp;
+       }
 
-       for (fd_ptr=FileFd;fd_ptr;fd_ptr=fd_ptr->next) {
-               if ((fd_ptr->ref_count > 0) &&
-                   (sbuf->st_dev == fd_ptr->dev) &&
-                   (sbuf->st_ino == fd_ptr->inode)) {
-                       fd_ptr->ref_count++;
+       /* 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. */
 
-                       DEBUG(3,("Re-used file_fd_struct dev = %x, inode = %.0f, ref_count = %d\n",
-                                (unsigned int)fd_ptr->dev, (double)fd_ptr->inode, 
-                                fd_ptr->ref_count));
+       ZERO_STRUCT(fsp_fi_cache);
 
-                       return fd_ptr;
-               }
-       }
+       conn->num_files_open++;
 
-       return NULL;
+       *result = fsp;
+       return NT_STATUS_OK;
 }
 
-
-
 /****************************************************************************
-fd support routines - attempt to find a empty slot in the FileFd array.
-Increments the ref_count of the returned entry.
+ Close all open files for a connection.
 ****************************************************************************/
-file_fd_struct *fd_get_new(void)
-{
-       extern struct current_user current_user;
-       file_fd_struct *fd_ptr;
 
-       fd_ptr = (file_fd_struct *)malloc(sizeof(*fd_ptr));
-       if (!fd_ptr) {
-          DEBUG(0,("ERROR! malloc fail for file_fd struct.\n"));
-          return NULL;
-       }
-       
-       ZERO_STRUCTP(fd_ptr);
+void file_close_conn(connection_struct *conn)
+{
+       files_struct *fsp, *next;
        
-       fd_ptr->dev = (SMB_DEV_T)-1;
-       fd_ptr->inode = (SMB_INO_T)-1;
-       fd_ptr->fd = -1;
-       fd_ptr->fd_readonly = -1;
-       fd_ptr->fd_writeonly = -1;
-       fd_ptr->real_open_flags = -1;
-       fd_add_to_uid_cache(fd_ptr, (uid_t)current_user.uid);
-       fd_ptr->ref_count++;
-
-       fd_ptr_used++;
-
-       DLIST_ADD(FileFd, fd_ptr);
-
-       DEBUG(5,("allocated fd_ptr structure (%d used)\n", fd_ptr_used));
-
-       return fd_ptr;
+       for (fsp=Files;fsp;fsp=next) {
+               next = fsp->next;
+               if (fsp->conn == conn) {
+                       close_file(NULL, fsp, SHUTDOWN_CLOSE);
+               }
+       }
 }
 
-
 /****************************************************************************
-close all open files for a connection
+ Close all open files for a pid and a vuid.
 ****************************************************************************/
-void file_close_conn(connection_struct *conn)
+
+void file_close_pid(uint16 smbpid, int vuid)
 {
        files_struct *fsp, *next;
        
        for (fsp=Files;fsp;fsp=next) {
                next = fsp->next;
-               if (fsp->conn == conn && fsp->open) {
-                       close_file(fsp,False); 
+               if ((fsp->file_pid == smbpid) && (fsp->vuid == vuid)) {
+                       close_file(NULL, fsp, SHUTDOWN_CLOSE);
                }
        }
 }
 
 /****************************************************************************
-initialise file structures
+ Initialise file structures.
 ****************************************************************************/
 
-#define MAX_OPEN_FUDGEFACTOR 10
+#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;
 
        /*
@@ -211,57 +187,89 @@ 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) {
                exit_server("out of memory in file_init");
        }
-       
-       /*
-        * Ensure that pipe_handle_oppset is set correctly.
-        */
-       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;
 
        for (fsp=Files;fsp;fsp=next) {
                next=fsp->next;
-               if ((fsp->vuid == vuid) && fsp->open) {
-                       close_file(fsp,False);
+               if (fsp->vuid == vuid) {
+                       close_file(NULL, fsp, SHUTDOWN_CLOSE);
                }
        }
 }
 
+/*
+ * Walk the files table until "fn" returns non-NULL
+ */
+
+struct files_struct *file_walk_table(
+       struct files_struct *(*fn)(struct files_struct *fsp,
+                                  void *private_data),
+       void *private_data)
+{
+       struct files_struct *fsp, *next;
+
+       for (fsp = Files; fsp; fsp = next) {
+               struct files_struct *ret;
+               next = fsp->next;
+               ret = fn(fsp, private_data);
+               if (ret != NULL) {
+                       return ret;
+               }
+       }
+       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.
+ Debug to enumerate all open files in the smbd.
 ****************************************************************************/
 
-files_struct *file_find_dit(SMB_DEV_T dev, SMB_INO_T inode, struct timeval *tval)
+void file_dump_open_table(void)
 {
        int count=0;
        files_struct *fsp;
 
        for (fsp=Files;fsp;fsp=fsp->next,count++) {
-               if (fsp->open && 
-                       fsp->fd_ptr != NULL &&
-                   fsp->fd_ptr->dev == dev && 
-                   fsp->fd_ptr->inode == inode &&
-                   (tval ? (fsp->open_time.tv_sec == tval->tv_sec) : True ) &&
-                   (tval ? (fsp->open_time.tv_usec == tval->tv_usec) : True )) {
+               DEBUG(10,("Files[%d], fnum = %d, name %s, fd = %d, gen = %lu, "
+                         "fileid=%s\n", count, fsp->fnum, fsp_str_dbg(fsp),
+                         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);
                        }
@@ -273,136 +281,258 @@ files_struct *file_find_dit(SMB_DEV_T dev, SMB_INO_T inode, struct timeval *tval
 }
 
 /****************************************************************************
- Find the first fsp given a device and inode.
+ Find a fsp given a device, inode and file_id.
 ****************************************************************************/
 
-files_struct *file_find_di_first(SMB_DEV_T dev, SMB_INO_T inode)
+files_struct *file_find_dif(struct file_id id, unsigned long gen_id)
 {
-    files_struct *fsp;
+       int count=0;
+       files_struct *fsp;
 
-    for (fsp=Files;fsp;fsp=fsp->next) {
-        if (fsp->open &&
-                       fsp->fd_ptr != NULL &&
-            fsp->fd_ptr->dev == dev &&
-            fsp->fd_ptr->inode == inode )
-            return fsp;
-    }
+       for (fsp=Files;fsp;fsp=fsp->next,count++) {
+               /* 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_str_dbg(fsp),
+                                        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;
+               }
+       }
 
-    return NULL;
+       return NULL;
 }
 
 /****************************************************************************
Find the next fsp having the same device and inode.
Check if an fsp still exists.
 ****************************************************************************/
 
-files_struct *file_find_di_next(files_struct *start_fsp)
+files_struct *file_find_fsp(files_struct *orig_fsp)
 {
-    files_struct *fsp;
+       files_struct *fsp;
 
-    for (fsp = start_fsp->next;fsp;fsp=fsp->next) {
-        if (fsp->open &&
-                       fsp->fd_ptr != NULL &&
-            fsp->fd_ptr->dev == start_fsp->fd_ptr->dev &&
-            fsp->fd_ptr->inode == start_fsp->fd_ptr->inode )
-            return fsp;
-    }
+       for (fsp=Files;fsp;fsp=fsp->next) {
+               if (fsp == orig_fsp)
+                       return fsp;
+       }
 
-    return NULL;
+       return NULL;
 }
 
 /****************************************************************************
-find a fsp that is open for printing
+ 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_print(void)
+
+files_struct *file_find_di_first(struct file_id id)
 {
        files_struct *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 (fsp->open && fsp->print_file) return fsp;
-       } 
+               if (file_id_equal(&fsp->file_id, &id)) {
+                       /* Setup positive cache. */
+                       fsp_fi_cache.fsp = fsp;
+                       return fsp;
+               }
+       }
 
+       /* Setup negative cache. */
+       fsp_fi_cache.fsp = NULL;
        return NULL;
 }
 
-
 /****************************************************************************
-sync open files on a connection
+ Find the next fsp having the same device and inode.
 ****************************************************************************/
-void file_sync_all(connection_struct *conn)
+
+files_struct *file_find_di_next(files_struct *start_fsp)
 {
-       files_struct *fsp, *next;
+       files_struct *fsp;
 
-       for (fsp=Files;fsp;fsp=next) {
-               next=fsp->next;
-               if (fsp->open && (conn == fsp->conn) && (fsp->fd_ptr != NULL)) {
-                       conn->vfs_ops.fsync(fsp->fd_ptr->fd);
+       for (fsp = start_fsp->next;fsp;fsp=fsp->next) {
+               if (file_id_equal(&fsp->file_id, &start_fsp->file_id)) {
+                       return fsp;
                }
        }
+
+       return NULL;
 }
 
+/****************************************************************************
+ 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;
+               }
+       } 
+
+       return NULL;
+}
 
 /****************************************************************************
-free up a fd_ptr
+ Find any fsp open with a pathname below that of an already open path.
 ****************************************************************************/
-void fd_ptr_free(file_fd_struct *fd_ptr)
+
+bool file_find_subpath(files_struct *dir_fsp)
 {
-       DLIST_REMOVE(FileFd, fd_ptr);
+       files_struct *fsp;
+       size_t dlen;
+       char *d_fullname = NULL;
+
+       d_fullname = talloc_asprintf(talloc_tos(), "%s/%s",
+                                    dir_fsp->conn->connectpath,
+                                    dir_fsp->fsp_name->base_name);
 
-       fd_ptr_used--;
+       if (!d_fullname) {
+               return false;
+       }
 
-       DEBUG(5,("freed fd_ptr structure (%d used)\n", fd_ptr_used));
+       dlen = strlen(d_fullname);
 
-       /* paranoia */
-       ZERO_STRUCTP(fd_ptr);
+       for (fsp=Files;fsp;fsp=fsp->next) {
+               char *d1_fullname;
 
-       free(fd_ptr);
+               if (fsp == dir_fsp) {
+                       continue;
+               }
+
+               d1_fullname = talloc_asprintf(talloc_tos(),
+                                       "%s/%s",
+                                       fsp->conn->connectpath,
+                                       fsp->fsp_name->base_name);
+
+               /*
+                * If the open file has a path that is a longer
+                * component, then it's a subpath.
+                */
+               if (strnequal(d_fullname, d1_fullname, dlen) &&
+                               (d1_fullname[dlen] == '/')) {
+                       TALLOC_FREE(d1_fullname);
+                       TALLOC_FREE(d_fullname);
+                       return true;
+               }
+               TALLOC_FREE(d1_fullname);
+       }
+
+       TALLOC_FREE(d_fullname);
+       return false;
 }
 
+/****************************************************************************
+ 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->fh->fd != -1)) {
+                       sync_file(conn, fsp, True /* write through */);
+               }
+       }
+}
 
 /****************************************************************************
-free up a fsp
+ Free up a fsp.
 ****************************************************************************/
-void file_free(files_struct *fsp)
+
+void file_free(struct smb_request *req, files_struct *fsp)
 {
        DLIST_REMOVE(Files, fsp);
 
-       string_free(&fsp->fsp_name);
+       TALLOC_FREE(fsp->fake_file_handle);
 
-       if ((fsp->fd_ptr != NULL) && fsp->fd_ptr->ref_count == 0) {
-               fd_ptr_free(fsp->fd_ptr);
+       if (fsp->fh->ref_count == 1) {
+               TALLOC_FREE(fsp->fh);
+       } else {
+               fsp->fh->ref_count--;
        }
 
+       if (fsp->notify) {
+               if (fsp->is_directory) {
+                       notify_remove_onelevel(fsp->conn->notify_ctx,
+                                              &fsp->file_id, fsp);
+               }
+               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--;
 
        DEBUG(5,("freed files structure %d (%d used)\n",
                 fsp->fnum, files_used));
 
-       /* this is paranoia, just in case someone tries to reuse the 
+       fsp->conn->num_files_open--;
+
+       if ((req != NULL) && (fsp == req->chain_fsp)) {
+               req->chain_fsp = NULL;
+       }
+
+       /* 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);
+       }
+
+       /* this is paranoia, just in case someone tries to reuse the
           information */
        ZERO_STRUCTP(fsp);
 
-       if (fsp == chain_fsp) chain_fsp = NULL;
-
-       free(fsp);
+       /* fsp->fsp_name is a talloc child and is free'd automatically. */
+       TALLOC_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);
                        }
@@ -413,27 +543,76 @@ 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(struct smb_request *req, uint16 fid)
 {
-       chain_fsp = NULL;
+       files_struct *fsp;
+
+       if ((req != NULL) && (req->chain_fsp != NULL)) {
+               return req->chain_fsp;
+       }
+
+       fsp = file_fnum(fid);
+       if ((fsp != NULL) && (req != NULL)) {
+               req->chain_fsp = fsp;
+       }
+       return fsp;
 }
 
 /****************************************************************************
-Save the chained fsp - done when about to do an oplock break.
+ Duplicate the file handle part for a DOS or FCB open.
 ****************************************************************************/
 
-void file_chain_save(void)
+NTSTATUS dup_file_fsp(struct smb_request *req, files_struct *from,
+                     uint32 access_mask, uint32 share_access,
+                     uint32 create_options, files_struct *to)
 {
-       oplock_save_chain_fsp = chain_fsp;
+       TALLOC_FREE(to->fh);
+
+       to->fh = from->fh;
+       to->fh->ref_count++;
+
+       to->file_id = from->file_id;
+       to->initial_allocation_size = from->initial_allocation_size;
+       to->mode = from->mode;
+       to->file_pid = from->file_pid;
+       to->vuid = from->vuid;
+       to->open_time = from->open_time;
+       to->access_mask = access_mask;
+       to->share_access = share_access;
+       to->oplock_type = from->oplock_type;
+       to->can_lock = from->can_lock;
+       to->can_read = (access_mask & (FILE_READ_DATA)) ? True : False;
+       if (!CAN_WRITE(from->conn)) {
+               to->can_write = False;
+       } else {
+               to->can_write = (access_mask & (FILE_WRITE_DATA | FILE_APPEND_DATA)) ? True : False;
+       }
+       to->print_file = from->print_file;
+       to->modified = from->modified;
+       to->is_directory = from->is_directory;
+       to->aio_write_behind = from->aio_write_behind;
+       return fsp_set_smb_fname(to, from->fsp_name);
 }
 
-/****************************************************************************
-Restore the chained fsp - done after an oplock break.
-****************************************************************************/
-void file_chain_restore(void)
+/**
+ * The only way that the fsp->fsp_name field should ever be set.
+ */
+NTSTATUS fsp_set_smb_fname(struct files_struct *fsp,
+                          const struct smb_filename *smb_fname_in)
 {
-       chain_fsp = oplock_save_chain_fsp;
+       NTSTATUS status;
+       struct smb_filename *smb_fname_new;
+
+       status = copy_smb_filename(fsp, smb_fname_in, &smb_fname_new);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       TALLOC_FREE(fsp->fsp_name);
+       fsp->fsp_name = smb_fname_new;
+
+       return NT_STATUS_OK;
 }