Add a in-memory cache
[nivanova/samba-autobuild/.git] / source3 / smbd / vfs.c
index e862710b6c4ae9a5c1f1e576d3d7d48568e20ee5..45d0788117c160dd298acfe226da56eb61d294ed 100644 (file)
@@ -418,11 +418,26 @@ ssize_t vfs_pread_data(files_struct *fsp, char *buf,
  Write data to a fd on the vfs.
 ****************************************************************************/
 
-ssize_t vfs_write_data(files_struct *fsp,const char *buffer,size_t N)
+ssize_t vfs_write_data(struct smb_request *req,
+                       files_struct *fsp,
+                       const char *buffer,
+                       size_t N)
 {
        size_t total=0;
        ssize_t ret;
 
+       if (req && req->unread_bytes) {
+               SMB_ASSERT(req->unread_bytes == N);
+               /* VFS_RECVFILE must drain the socket
+                * before returning. */
+               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);
 
@@ -436,12 +451,27 @@ ssize_t vfs_write_data(files_struct *fsp,const char *buffer,size_t N)
        return (ssize_t)total;
 }
 
-ssize_t vfs_pwrite_data(files_struct *fsp,const char *buffer,
-                size_t N, SMB_OFF_T offset)
+ssize_t vfs_pwrite_data(struct smb_request *req,
+                       files_struct *fsp,
+                       const char *buffer,
+                       size_t N,
+                       SMB_OFF_T offset)
 {
        size_t total=0;
        ssize_t ret;
 
+       if (req && req->unread_bytes) {
+               SMB_ASSERT(req->unread_bytes == N);
+               /* VFS_RECVFILE must drain the socket
+                * before returning. */
+               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);
@@ -701,156 +731,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) {
+       if (!memcache_lookup(smbd_memcache(), GETWD_CACHE,
+                            data_blob_const(&key, sizeof(key)),
+                            &cache_value)) {
+               goto nocache;
+       }
 
-                                               ret = talloc_strdup(ctx,
-                                                       ino_list[i].path);
+       SMB_ASSERT((cache_value.length > 0)
+                  && (cache_value.data[cache_value.length-1] == '\0'));
 
-                                               /* 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 ((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;
        }
 
-       /*  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. */
+ 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.
+        */
 
        if (!SMB_VFS_GETWD(conn,s)) {
-               DEBUG(0,("vfs_GetWd: SMB_VFS_GETWD call failed, errno %s\n",
-                               strerror(errno)));
-               return (NULL);
+               DEBUG(0, ("vfs_GetWd: SMB_VFS_GETWD call failed: %s\n",
+                         strerror(errno)));
+               return NULL;
        }
 
-       ret = talloc_strdup(ctx,s);
-
-       DEBUG(5,("vfs_GetWd %s, inode %.0f, dev %.0f\n",
-                               s,(double)st.st_ino,(double)st.st_dev));
-
-       /* 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;
 }
 
 /*******************************************************************
@@ -863,11 +835,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;
@@ -939,11 +907,7 @@ 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);