Correct fix for bug 6781 - Cannot rename subfolders in Explorer view with recent...
[ira/wip.git] / source3 / smbd / files.c
index 547206815ed7bf360dc623cca07f18b0aad1b298..bf216050b8c4d62aa9e9d431ebf6b30550454347 100644 (file)
@@ -5,7 +5,7 @@
    
    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"
-
-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;
-
-static int files_used;
-
-/* A singleton cache to speed up searching by dev/inode. */
-static struct fsp_singleton_cache {
-       files_struct *fsp;
-       SMB_DEV_T dev;
-       SMB_INO_T inode;
-} fsp_fi_cache;
-
 /****************************************************************************
  Return a unique number identifying this fsp over the life of this pid.
 ****************************************************************************/
 
 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;
@@ -61,11 +39,12 @@ static unsigned long get_gen_count(void)
  Find first available file slot.
 ****************************************************************************/
 
-files_struct *file_new(connection_struct *conn)
+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
@@ -76,41 +55,42 @@ files_struct *file_new(connection_struct *conn)
                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(conn);
-                       }
-               }
-
                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 = SMB_MALLOC_P(files_struct);
+       /*
+        * 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) {
-               unix_ERR_class = ERRSRV;
-               unix_ERR_code = ERRnofids;
-               return NULL;
+               return NT_STATUS_NO_MEMORY;
        }
 
-       ZERO_STRUCTP(fsp);
-       fsp->fd = -1;
+       /*
+        * 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->file_id = get_gen_count();
+       fsp->fh->gen_id = get_gen_count();
        GetTimeOfDay(&fsp->open_time);
 
        first_file = (i+1) % real_max_open_files;
@@ -121,21 +101,38 @@ files_struct *file_new(connection_struct *conn)
        fsp->fnum = i + FILE_HANDLE_OFFSET;
        SMB_ASSERT(fsp->fnum < 65536);
 
-       string_set(&fsp->fsp_name,"");
-       
+       /*
+        * 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;
-
-       /* A new fsp invalidates a negative fsp_fi_cache. */
-       if (fsp_fi_cache.fsp == NULL) {
-               ZERO_STRUCT(fsp_fi_cache);
+       if (req != NULL) {
+               req->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);
+
+       conn->num_files_open++;
+
+       *result = fsp;
+       return NT_STATUS_OK;
 }
 
 /****************************************************************************
@@ -149,23 +146,23 @@ 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(NULL, fsp, SHUTDOWN_CLOSE);
                }
        }
 }
 
 /****************************************************************************
- Close all open files for a pid.
+ Close all open files for a pid and a vuid.
 ****************************************************************************/
 
-void file_close_pid(uint16 smbpid)
+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) {
-                       close_file(fsp,False); 
+               if ((fsp->file_pid == smbpid) && (fsp->vuid == vuid)) {
+                       close_file(NULL, fsp, SHUTDOWN_CLOSE);
                }
        }
 }
@@ -205,11 +202,6 @@ open files, %d are available.\n", request_max_open_files, 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);
 }
 
 /****************************************************************************
@@ -223,20 +215,47 @@ 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(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;
+}
+
+/****************************************************************************
+ 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, fileid = %lu, dev = %x, inode = %.0f\n",
-                       count, fsp->fnum, fsp->fsp_name, fsp->fd, (unsigned long)fsp->file_id,
-                       (unsigned int)fsp->dev, (double)fsp->inode ));
+               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)));
        }
 }
 
@@ -250,7 +269,7 @@ files_struct *file_find_fd(int fd)
        files_struct *fsp;
 
        for (fsp=Files;fsp;fsp=fsp->next,count++) {
-               if (fsp->fd == fd) {
+               if (fsp->fh->fd == fd) {
                        if (count > 10) {
                                DLIST_PROMOTE(Files, fsp);
                        }
@@ -265,26 +284,30 @@ files_struct *file_find_fd(int fd)
  Find a fsp given a device, inode and file_id.
 ****************************************************************************/
 
-files_struct *file_find_dif(SMB_DEV_T dev, SMB_INO_T inode, unsigned long file_id)
+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++) {
-               /* We can have a fsp->fd == -1 here as it could be a stat open. */
-               if (fsp->dev == dev && 
-                   fsp->inode == inode &&
-                   fsp->file_id == file_id ) {
+               /* 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->fd == -1 && fsp->oplock_type != NO_OPLOCK) {
-                               DEBUG(0,("file_find_dif: file %s dev = %x, inode = %.0f, file_id = %u \
-oplock_type = %u is a stat open with oplock type !\n", fsp->fsp_name, (unsigned int)fsp->dev,
-                                               (double)fsp->inode, (unsigned int)fsp->file_id,
-                                               (unsigned int)fsp->oplock_type ));
-                               smb_panic("file_find_dif\n");
+                       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;
                }
@@ -315,22 +338,19 @@ files_struct *file_find_fsp(files_struct *orig_fsp)
  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;
 
-       if (fsp_fi_cache.dev == dev && fsp_fi_cache.inode == inode) {
+       if (file_id_equal(&fsp_fi_cache.id, &id)) {
                /* Positive or negative cache hit. */
                return fsp_fi_cache.fsp;
        }
 
-       fsp_fi_cache.dev = dev;
-       fsp_fi_cache.inode = inode;
+       fsp_fi_cache.id = id;
 
        for (fsp=Files;fsp;fsp=fsp->next) {
-               if ( fsp->fd != -1 &&
-                               fsp->dev == dev &&
-                               fsp->inode == inode ) {
+               if (file_id_equal(&fsp->file_id, &id)) {
                        /* Setup positive cache. */
                        fsp_fi_cache.fsp = fsp;
                        return fsp;
@@ -351,10 +371,9 @@ files_struct *file_find_di_next(files_struct *start_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 )
+               if (file_id_equal(&fsp->file_id, &start_fsp->file_id)) {
                        return fsp;
+               }
        }
 
        return NULL;
@@ -378,24 +397,58 @@ files_struct *file_find_print(void)
 }
 
 /****************************************************************************
Set a pending modtime across all files with a given dev/ino pair.
Find any fsp open with a pathname below that of an already open path.
 ****************************************************************************/
 
-void fsp_set_pending_modtime(files_struct *tfsp, time_t pmod)
+bool file_find_subpath(files_struct *dir_fsp)
 {
        files_struct *fsp;
+       size_t dlen;
+       char *d_fullname = NULL;
+       bool ret = false;
+
+       d_fullname = talloc_asprintf(talloc_tos(), "%s/%s",
+                                    dir_fsp->conn->connectpath,
+                                    dir_fsp->fsp_name->base_name);
 
-       if (null_mtime(pmod)) {
-               return;
+       if (!d_fullname) {
+               goto out;
        }
 
-       for (fsp = Files;fsp;fsp=fsp->next) {
-               if ( fsp->fd != -1 &&
-                               fsp->dev == tfsp->dev &&
-                               fsp->inode == tfsp->inode ) {
-                       fsp->pending_modtime = pmod;
+       dlen = strlen(d_fullname);
+
+       for (fsp=Files;fsp;fsp=fsp->next) {
+               char *d1_fullname;
+
+               if (fsp == dir_fsp) {
+                       continue;
                }
-       }
+
+               d1_fullname = talloc_asprintf(talloc_tos(),
+                                       "%s/%s",
+                                       fsp->conn->connectpath,
+                                       fsp->fsp_name->base_name);
+
+               if (strnequal(d_fullname, d1_fullname, dlen)) {
+                       /*
+                        * If the open file is a second file handle to the
+                        * same name or is a stream on the original file, then
+                        * don't return true.
+                        */
+                       if (d1_fullname[dlen] != '/') {
+                               TALLOC_FREE(d1_fullname);
+                               continue;
+                       }
+
+                       TALLOC_FREE(d1_fullname);
+                       ret = true;
+                       goto out;
+               }
+               TALLOC_FREE(d1_fullname);
+       } 
+ out:
+       TALLOC_FREE(d_fullname);
+       return ret;
 }
 
 /****************************************************************************
@@ -408,8 +461,8 @@ void file_sync_all(connection_struct *conn)
 
        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 */);
                }
        }
 }
@@ -418,28 +471,43 @@ void file_sync_all(connection_struct *conn)
  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->fake_file_handle) {
-               destroy_fake_file_handle(&fsp->fake_file_handle);
+       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 
-          information */
-       ZERO_STRUCTP(fsp);
+       fsp->conn->num_files_open--;
 
-       if (fsp == chain_fsp) {
-               chain_fsp = NULL;
+       if ((req != NULL) && (fsp == req->chain_fsp)) {
+               req->chain_fsp = NULL;
        }
 
        /* Closing a file can invalidate the positive cache. */
@@ -447,28 +515,30 @@ void file_free(files_struct *fsp)
                ZERO_STRUCT(fsp_fi_cache);
        }
 
-       SAFE_FREE(fsp);
+       /* 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);
+
+       /* 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;
-
-       if (!buf)
-               return NULL;
-       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);
                        }
@@ -479,28 +549,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;
 }