Merge branch 'master' of ssh://git.samba.org/data/git/samba
[samba.git] / source3 / smbd / vfs.c
index 78939881d35e1dffe82fc39b2cc7bd9ee61d7b7f..df5a39eea2e064c2d2ce2f546f077930014df784 100644 (file)
@@ -23,6 +23,7 @@
 */
 
 #include "includes.h"
+#include "smbd/globals.h"
 
 #undef DBGC_CLASS
 #define DBGC_CLASS DBGC_VFS
@@ -31,12 +32,10 @@ static_decl_vfs;
 
 struct vfs_init_function_entry {
        char *name;
-       vfs_op_tuple *vfs_op_tuples;
+       const vfs_op_tuple *vfs_op_tuples;
        struct vfs_init_function_entry *prev, *next;
 };
 
-static struct vfs_init_function_entry *backends = NULL;
-
 /****************************************************************************
     maintain the list of available backends
 ****************************************************************************/
@@ -55,7 +54,7 @@ static struct vfs_init_function_entry *vfs_find_backend_entry(const char *name)
        return NULL;
 }
 
-NTSTATUS smb_register_vfs(int version, const char *name, vfs_op_tuple *vfs_op_tuples)
+NTSTATUS smb_register_vfs(int version, const char *name, const vfs_op_tuple *vfs_op_tuples)
 {
        struct vfs_init_function_entry *entry = backends;
 
@@ -110,13 +109,13 @@ static inline void vfs_set_operation(struct vfs_ops * vfs, vfs_op_type which,
 
 bool vfs_init_custom(connection_struct *conn, const char *vfs_object)
 {
-       vfs_op_tuple *ops;
+       const vfs_op_tuple *ops;
        char *module_path = NULL;
        char *module_name = NULL;
        char *module_param = NULL, *p;
        int i;
        vfs_handle_struct *handle;
-       struct vfs_init_function_entry *entry;
+       const struct vfs_init_function_entry *entry;
        
        if (!conn||!vfs_object||!vfs_object[0]) {
                DEBUG(0,("vfs_init_custon() called with NULL pointer or emtpy vfs_object!\n"));
@@ -177,7 +176,7 @@ bool vfs_init_custom(connection_struct *conn, const char *vfs_object)
                goto fail;
        }
 
-       handle = TALLOC_ZERO_P(conn->mem_ctx,vfs_handle_struct);
+       handle = TALLOC_ZERO_P(connvfs_handle_struct);
        if (!handle) {
                DEBUG(0,("TALLOC_ZERO() failed!\n"));
                goto fail;
@@ -185,7 +184,7 @@ bool vfs_init_custom(connection_struct *conn, const char *vfs_object)
        memcpy(&handle->vfs_next, &conn->vfs, sizeof(struct vfs_ops));
        handle->conn = conn;
        if (module_param) {
-               handle->param = talloc_strdup(conn->mem_ctx, module_param);
+               handle->param = talloc_strdup(conn, module_param);
        }
        DLIST_ADD(conn->vfs_handles, handle);
 
@@ -232,7 +231,7 @@ void *vfs_add_fsp_extension_notype(vfs_handle_struct *handle, files_struct *fsp,
        }
 
        ext = (struct vfs_fsp_data *)TALLOC_ZERO(
-               handle->conn->mem_ctx, sizeof(struct vfs_fsp_data) + ext_size);
+               handle->conn, sizeof(struct vfs_fsp_data) + ext_size);
        if (ext == NULL) {
                return NULL;
        }
@@ -263,19 +262,31 @@ void vfs_remove_fsp_extension(vfs_handle_struct *handle, files_struct *fsp)
        }
 }
 
-void *vfs_fetch_fsp_extension(vfs_handle_struct *handle, files_struct *fsp)
+void *vfs_memctx_fsp_extension(vfs_handle_struct *handle, files_struct *fsp)
 {
        struct vfs_fsp_data *head;
 
        for (head = fsp->vfs_extension; head; head = head->next) {
                if (head->owner == handle) {
-                       return EXT_DATA_AREA(head);
+                       return head;
                }
        }
 
        return NULL;
 }
 
+void *vfs_fetch_fsp_extension(vfs_handle_struct *handle, files_struct *fsp)
+{
+       struct vfs_fsp_data *head;
+
+       head = (struct vfs_fsp_data *)vfs_memctx_fsp_extension(handle, fsp);
+       if (head != NULL) {
+               return EXT_DATA_AREA(head);
+       }
+
+       return NULL;
+}
+
 #undef EXT_DATA_AREA
 
 /*****************************************************************
@@ -377,8 +388,8 @@ ssize_t vfs_read_data(files_struct *fsp, char *buf, size_t byte_count)
 
        while (total < byte_count)
        {
-               ssize_t ret = SMB_VFS_READ(fsp, fsp->fh->fd, buf + total,
-                                       byte_count - total);
+               ssize_t ret = SMB_VFS_READ(fsp, buf + total,
+                                          byte_count - total);
 
                if (ret == 0) return total;
                if (ret == -1) {
@@ -399,7 +410,7 @@ ssize_t vfs_pread_data(files_struct *fsp, char *buf,
 
        while (total < byte_count)
        {
-               ssize_t ret = SMB_VFS_PREAD(fsp, fsp->fh->fd, buf + total,
+               ssize_t ret = SMB_VFS_PREAD(fsp, buf + total,
                                        byte_count - total, offset + total);
 
                if (ret == 0) return total;
@@ -433,13 +444,12 @@ ssize_t vfs_write_data(struct smb_request *req,
                req->unread_bytes = 0;
                return SMB_VFS_RECVFILE(smbd_server_fd(),
                                        fsp,
-                                       fsp->fh->fd,
                                        (SMB_OFF_T)-1,
                                        N);
        }
 
        while (total < N) {
-               ret = SMB_VFS_WRITE(fsp,fsp->fh->fd,buffer + total,N - total);
+               ret = SMB_VFS_WRITE(fsp, buffer + total, N - total);
 
                if (ret == -1)
                        return -1;
@@ -467,14 +477,13 @@ ssize_t vfs_pwrite_data(struct smb_request *req,
                req->unread_bytes = 0;
                return SMB_VFS_RECVFILE(smbd_server_fd(),
                                        fsp,
-                                       fsp->fh->fd,
                                        offset,
                                        N);
        }
 
        while (total < N) {
-               ret = SMB_VFS_PWRITE(fsp, fsp->fh->fd, buffer + total,
-                                N - total, offset + total);
+               ret = SMB_VFS_PWRITE(fsp, buffer + total, N - total,
+                                    offset + total);
 
                if (ret == -1)
                        return -1;
@@ -491,13 +500,13 @@ ssize_t vfs_pwrite_data(struct smb_request *req,
  Returns 0 on success, -1 on failure.
 ****************************************************************************/
 
-int vfs_allocate_file_space(files_struct *fsp, SMB_BIG_UINT len)
+int vfs_allocate_file_space(files_struct *fsp, uint64_t len)
 {
        int ret;
        SMB_STRUCT_STAT st;
        connection_struct *conn = fsp->conn;
-       SMB_BIG_UINT space_avail;
-       SMB_BIG_UINT bsize,dfree,dsize;
+       uint64_t space_avail;
+       uint64_t bsize,dfree,dsize;
 
        release_level_2_oplocks_on_change(fsp);
 
@@ -513,21 +522,21 @@ int vfs_allocate_file_space(files_struct *fsp, SMB_BIG_UINT len)
                return -1;
        }
 
-       ret = SMB_VFS_FSTAT(fsp,fsp->fh->fd,&st);
+       ret = SMB_VFS_FSTAT(fsp, &st);
        if (ret == -1)
                return ret;
 
-       if (len == (SMB_BIG_UINT)st.st_size)
+       if (len == (uint64_t)st.st_size)
                return 0;
 
-       if (len < (SMB_BIG_UINT)st.st_size) {
+       if (len < (uint64_t)st.st_size) {
                /* Shrink - use ftruncate. */
 
                DEBUG(10,("vfs_allocate_file_space: file %s, shrink. Current size %.0f\n",
                                fsp->fsp_name, (double)st.st_size ));
 
                flush_write_cache(fsp, SIZECHANGE_FLUSH);
-               if ((ret = SMB_VFS_FTRUNCATE(fsp, fsp->fh->fd, (SMB_OFF_T)len)) != -1) {
+               if ((ret = SMB_VFS_FTRUNCATE(fsp, (SMB_OFF_T)len)) != -1) {
                        set_filelen_write_cache(fsp, len);
                }
                return ret;
@@ -541,7 +550,7 @@ int vfs_allocate_file_space(files_struct *fsp, SMB_BIG_UINT len)
        len -= st.st_size;
        len /= 1024; /* Len is now number of 1k blocks needed. */
        space_avail = get_dfree_info(conn,fsp->fsp_name,False,&bsize,&dfree,&dsize);
-       if (space_avail == (SMB_BIG_UINT)-1) {
+       if (space_avail == (uint64_t)-1) {
                return -1;
        }
 
@@ -569,7 +578,7 @@ int vfs_set_filelen(files_struct *fsp, SMB_OFF_T len)
        release_level_2_oplocks_on_change(fsp);
        DEBUG(10,("vfs_set_filelen: ftruncate %s to len %.0f\n", fsp->fsp_name, (double)len));
        flush_write_cache(fsp, SIZECHANGE_FLUSH);
-       if ((ret = SMB_VFS_FTRUNCATE(fsp, fsp->fh->fd, len)) != -1) {
+       if ((ret = SMB_VFS_FTRUNCATE(fsp, len)) != -1) {
                set_filelen_write_cache(fsp, len);
                notify_fname(fsp->conn, NOTIFY_ACTION_MODIFIED,
                             FILE_NOTIFY_CHANGE_SIZE
@@ -587,7 +596,6 @@ int vfs_set_filelen(files_struct *fsp, SMB_OFF_T len)
  Returns 0 on success, -1 on failure.
 ****************************************************************************/
 
-static char *sparse_buf;
 #define SPARSE_BUF_WRITE_SIZE (32*1024)
 
 int vfs_fill_sparse(files_struct *fsp, SMB_OFF_T len)
@@ -600,7 +608,7 @@ int vfs_fill_sparse(files_struct *fsp, SMB_OFF_T len)
        ssize_t pwrite_ret;
 
        release_level_2_oplocks_on_change(fsp);
-       ret = SMB_VFS_FSTAT(fsp,fsp->fh->fd,&st);
+       ret = SMB_VFS_FSTAT(fsp, &st);
        if (ret == -1) {
                return ret;
        }
@@ -629,7 +637,7 @@ int vfs_fill_sparse(files_struct *fsp, SMB_OFF_T len)
        while (total < num_to_write) {
                size_t curr_write_size = MIN(SPARSE_BUF_WRITE_SIZE, (num_to_write - total));
 
-               pwrite_ret = SMB_VFS_PWRITE(fsp, fsp->fh->fd, sparse_buf, curr_write_size, offset + total);
+               pwrite_ret = SMB_VFS_PWRITE(fsp, sparse_buf, curr_write_size, offset + total);
                if (pwrite_ret == -1) {
                        DEBUG(10,("vfs_fill_sparse: SMB_VFS_PWRITE for file %s failed with error %s\n",
                                fsp->fsp_name, strerror(errno) ));
@@ -650,25 +658,24 @@ int vfs_fill_sparse(files_struct *fsp, SMB_OFF_T len)
  Transfer some data (n bytes) between two file_struct's.
 ****************************************************************************/
 
-static files_struct *in_fsp;
-static files_struct *out_fsp;
-
-static ssize_t read_fn(int fd, void *buf, size_t len)
+static ssize_t vfs_read_fn(void *file, void *buf, size_t len)
 {
-       return SMB_VFS_READ(in_fsp, fd, buf, len);
+       struct files_struct *fsp = (struct files_struct *)file;
+
+       return SMB_VFS_READ(fsp, buf, len);
 }
 
-static ssize_t write_fn(int fd, const void *buf, size_t len)
+static ssize_t vfs_write_fn(void *file, const void *buf, size_t len)
 {
-       return SMB_VFS_WRITE(out_fsp, fd, buf, len);
+       struct files_struct *fsp = (struct files_struct *)file;
+
+       return SMB_VFS_WRITE(fsp, buf, len);
 }
 
 SMB_OFF_T vfs_transfer_file(files_struct *in, files_struct *out, SMB_OFF_T n)
 {
-       in_fsp = in;
-       out_fsp = out;
-
-       return transfer_file_internal(in_fsp->fh->fd, out_fsp->fh->fd, n, read_fn, write_fn);
+       return transfer_file_internal((void *)in, (void *)out, n,
+                                     vfs_read_fn, vfs_write_fn);
 }
 
 /*******************************************************************
@@ -709,7 +716,6 @@ char *vfs_readdirname(connection_struct *conn, void *p)
 int vfs_ChDir(connection_struct *conn, const char *path)
 {
        int res;
-       static char *LastDir = NULL;
 
        if (!LastDir) {
                LastDir = SMB_STRDUP("");
@@ -731,156 +737,98 @@ int vfs_ChDir(connection_struct *conn, const char *path)
        return(res);
 }
 
-/* number of list structures for a caching GetWd function. */
-#define MAX_GETWDCACHE (50)
-
-static struct {
-       SMB_DEV_T dev; /* These *must* be compatible with the types returned in a stat() call. */
-       SMB_INO_T inode; /* These *must* be compatible with the types returned in a stat() call. */
-       char *path; /* The pathname. */
-       bool valid;
-} ino_list[MAX_GETWDCACHE];
-
-extern bool use_getwd_cache;
-
-/****************************************************************************
- Prompte a ptr (to make it recently used)
-****************************************************************************/
-
-static void array_promote(char *array,int elsize,int element)
-{
-       char *p;
-       if (element == 0)
-               return;
-
-       p = (char *)SMB_MALLOC(elsize);
-
-       if (!p) {
-               DEBUG(5,("array_promote: malloc fail\n"));
-               return;
-       }
-
-       memcpy(p,array + element * elsize, elsize);
-       memmove(array + elsize,array,elsize*element);
-       memcpy(array,p,elsize);
-       SAFE_FREE(p);
-}
-
 /*******************************************************************
  Return the absolute current directory path - given a UNIX pathname.
  Note that this path is returned in DOS format, not UNIX
  format. Note this can be called with conn == NULL.
 ********************************************************************/
 
+struct getwd_cache_key {
+       SMB_DEV_T dev;
+       SMB_INO_T ino;
+};
+
 char *vfs_GetWd(TALLOC_CTX *ctx, connection_struct *conn)
 {
-#ifdef PATH_MAX
         char s[PATH_MAX+1];
-#else
-       pstring s;
-#endif
-       static bool getwd_cache_init = False;
        SMB_STRUCT_STAT st, st2;
-       int i;
-       char *ret = NULL;
+       char *result;
+       DATA_BLOB cache_value;
+       struct getwd_cache_key key;
 
        *s = 0;
 
-       if (!use_getwd_cache) {
- nocache:
-               ret = SMB_VFS_GETWD(conn,s);
-               if (!ret) {
-                       DEBUG(0,("vfs_GetWd: SMB_VFS_GETWD call failed, "
-                               "errno %s\n",strerror(errno)));
-                       return NULL;
-               }
-               return talloc_strdup(ctx, ret);
-       }
-
-       /* init the cache */
-       if (!getwd_cache_init) {
-               getwd_cache_init = True;
-               for (i=0;i<MAX_GETWDCACHE;i++) {
-                       string_set(&ino_list[i].path,"");
-                       ino_list[i].valid = False;
-               }
+       if (!lp_getwd_cache()) {
+               goto nocache;
        }
 
-       /*  Get the inode of the current directory, if this doesn't work we're
-               in trouble :-) */
+       SET_STAT_INVALID(st);
 
        if (SMB_VFS_STAT(conn, ".",&st) == -1) {
-               /* Known to fail for root: the directory may be
-                * NFS-mounted and exported with root_squash (so has no root access). */
+               /*
+                * Known to fail for root: the directory may be NFS-mounted
+                * and exported with root_squash (so has no root access).
+                */
                DEBUG(1,("vfs_GetWd: couldn't stat \".\" error %s "
-                       "(NFS problem ?)\n",
-                       strerror(errno) ));
+                        "(NFS problem ?)\n", strerror(errno) ));
                goto nocache;
        }
 
+       ZERO_STRUCT(key); /* unlikely, but possible padding */
+       key.dev = st.st_dev;
+       key.ino = st.st_ino;
 
-       for (i=0; i<MAX_GETWDCACHE; i++) {
-               if (ino_list[i].valid) {
-
-                       /*  If we have found an entry with a matching inode and dev number
-                               then find the inode number for the directory in the cached string.
-                               If this agrees with that returned by the stat for the current
-                               directory then all is o.k. (but make sure it is a directory all
-                               the same...) */
-
-                       if (st.st_ino == ino_list[i].inode && st.st_dev == ino_list[i].dev) {
-                               if (SMB_VFS_STAT(conn,ino_list[i].path,&st2) == 0) {
-                                       if (st.st_ino == st2.st_ino && st.st_dev == st2.st_dev &&
-                                                       (st2.st_mode & S_IFMT) == S_IFDIR) {
-
-                                               ret = talloc_strdup(ctx,
-                                                       ino_list[i].path);
-
-                                               /* promote it for future use */
-                                               array_promote((char *)&ino_list[0],sizeof(ino_list[0]),i);
-                                               if (ret == NULL) {
-                                                       errno = ENOMEM;
-                                               }
-                                               return ret;
-                                       } else {
-                                               /*  If the inode is different then something's changed,
-                                                       scrub the entry and start from scratch. */
-                                               ino_list[i].valid = False;
-                                       }
-                               }
-                       }
-               }
+       if (!memcache_lookup(smbd_memcache(), GETWD_CACHE,
+                            data_blob_const(&key, sizeof(key)),
+                            &cache_value)) {
+               goto nocache;
        }
 
-       /*  We don't have the information to hand so rely on traditional
-        *  methods. The very slow getcwd, which spawns a process on some
-        *  systems, or the not quite so bad getwd. */
+       SMB_ASSERT((cache_value.length > 0)
+                  && (cache_value.data[cache_value.length-1] == '\0'));
 
-       if (!SMB_VFS_GETWD(conn,s)) {
-               DEBUG(0,("vfs_GetWd: SMB_VFS_GETWD call failed, errno %s\n",
-                               strerror(errno)));
-               return (NULL);
+       if ((SMB_VFS_STAT(conn, (char *)cache_value.data, &st2) == 0)
+           && (st.st_dev == st2.st_dev) && (st.st_ino == st2.st_ino)
+           && (S_ISDIR(st.st_mode))) {
+               /*
+                * Ok, we're done
+                */
+               result = talloc_strdup(ctx, (char *)cache_value.data);
+               if (result == NULL) {
+                       errno = ENOMEM;
+               }
+               return result;
        }
 
-       ret = talloc_strdup(ctx,s);
+ nocache:
+
+       /*
+        * We don't have the information to hand so rely on traditional
+        * methods. The very slow getcwd, which spawns a process on some
+        * systems, or the not quite so bad getwd.
+        */
 
-       DEBUG(5,("vfs_GetWd %s, inode %.0f, dev %.0f\n",
-                               s,(double)st.st_ino,(double)st.st_dev));
+       if (!SMB_VFS_GETWD(conn,s)) {
+               DEBUG(0, ("vfs_GetWd: SMB_VFS_GETWD call failed: %s\n",
+                         strerror(errno)));
+               return NULL;
+       }
 
-       /* add it to the cache */
-       i = MAX_GETWDCACHE - 1;
-       string_set(&ino_list[i].path,s);
-       ino_list[i].dev = st.st_dev;
-       ino_list[i].inode = st.st_ino;
-       ino_list[i].valid = True;
+       if (lp_getwd_cache() && VALID_STAT(st)) {
+               ZERO_STRUCT(key); /* unlikely, but possible padding */
+               key.dev = st.st_dev;
+               key.ino = st.st_ino;
 
-       /* put it at the top of the list */
-       array_promote((char *)&ino_list[0],sizeof(ino_list[0]),i);
+               memcache_add(smbd_memcache(), GETWD_CACHE,
+                            data_blob_const(&key, sizeof(key)),
+                            data_blob_const(s, strlen(s)+1));
+       }
 
-       if (ret == NULL) {
+       result = talloc_strdup(ctx, s);
+       if (result == NULL) {
                errno = ENOMEM;
        }
-       return ret;
+       return result;
 }
 
 /*******************************************************************
@@ -893,11 +841,7 @@ NTSTATUS check_reduced_name(connection_struct *conn, const char *fname)
 #ifdef REALPATH_TAKES_NULL
        bool free_resolved_name = True;
 #else
-#ifdef PATH_MAX
         char resolved_name_buf[PATH_MAX+1];
-#else
-        pstring resolved_name_buf;
-#endif
        bool free_resolved_name = False;
 #endif
        char *resolved_name = NULL;
@@ -919,14 +863,13 @@ NTSTATUS check_reduced_name(connection_struct *conn, const char *fname)
                                return map_nt_error_from_unix(errno);
                        case ENOENT:
                        {
-                               TALLOC_CTX *tmp_ctx = talloc_stackframe();
+                               TALLOC_CTX *ctx = talloc_tos();
                                char *tmp_fname = NULL;
                                char *last_component = NULL;
                                /* Last component didn't exist. Remove it and try and canonicalise the directory. */
 
-                               tmp_fname = talloc_strdup(tmp_ctx, fname);
+                               tmp_fname = talloc_strdup(ctx, fname);
                                if (!tmp_fname) {
-                                       TALLOC_FREE(tmp_ctx);
                                        return NT_STATUS_NO_MEMORY;
                                }
                                p = strrchr_m(tmp_fname, '/');
@@ -935,10 +878,9 @@ NTSTATUS check_reduced_name(connection_struct *conn, const char *fname)
                                        last_component = p;
                                } else {
                                        last_component = tmp_fname;
-                                       tmp_fname = talloc_strdup(tmp_ctx,
+                                       tmp_fname = talloc_strdup(ctx,
                                                        ".");
                                        if (!tmp_fname) {
-                                               TALLOC_FREE(tmp_ctx);
                                                return NT_STATUS_NO_MEMORY;
                                        }
                                }
@@ -950,15 +892,13 @@ NTSTATUS check_reduced_name(connection_struct *conn, const char *fname)
 #endif
                                if (!resolved_name) {
                                        DEBUG(3,("reduce_name: couldn't get realpath for %s\n", fname));
-                                       TALLOC_FREE(tmp_ctx);
                                        return map_nt_error_from_unix(errno);
                                }
-                               tmp_fname = talloc_asprintf(tmp_ctx,
+                               tmp_fname = talloc_asprintf(ctx,
                                                "%s/%s",
                                                resolved_name,
                                                last_component);
                                if (!tmp_fname) {
-                                       TALLOC_FREE(tmp_ctx);
                                        return NT_STATUS_NO_MEMORY;
                                }
 #ifdef REALPATH_TAKES_NULL
@@ -969,14 +909,9 @@ NTSTATUS check_reduced_name(connection_struct *conn, const char *fname)
                                        return NT_STATUS_NO_MEMORY;
                                }
 #else
-#ifdef PATH_MAX
                                safe_strcpy(resolved_name_buf, tmp_fname, PATH_MAX);
-#else
-                               pstrcpy(resolved_name_buf, tmp_fname);
-#endif
                                resolved_name = resolved_name_buf;
 #endif
-                               TALLOC_FREE(tmp_ctx);
                                break;
                        }
                        default: