auth/credentials: don't ignore "client use kerberos" and --use-kerberos for machine...
[samba.git] / source3 / smbd / dfree.c
index 188faa2d79a251e167c267abf14ac4c5e3905c3d..89dc11293b52bc814cb4c56ab3a134b5f6b7bba9 100644 (file)
@@ -20,6 +20,8 @@
 #include "includes.h"
 #include "smbd/smbd.h"
 #include "smbd/globals.h"
+#include "lib/util_file.h"
+#include "lib/util/memcache.h"
 
 /****************************************************************************
  Normalise for DOS usage.
@@ -49,15 +51,21 @@ static void disk_norm(uint64_t *bsize, uint64_t *dfree, uint64_t *dsize)
  Return number of 1K blocks available on a path and total number.
 ****************************************************************************/
 
-uint64_t sys_disk_free(connection_struct *conn, const char *path,
-                      uint64_t *bsize, uint64_t *dfree, uint64_t *dsize)
+static uint64_t sys_disk_free(connection_struct *conn,
+                             struct smb_filename *fname,
+                             uint64_t *bsize,
+                             uint64_t *dfree,
+                             uint64_t *dsize)
 {
+       const struct loadparm_substitution *lp_sub =
+               loadparm_s3_global_substitution();
        uint64_t dfree_retval;
        uint64_t dfree_q = 0;
        uint64_t bsize_q = 0;
        uint64_t dsize_q = 0;
        const char *dfree_command;
        static bool dfree_broken = false;
+       char *path = fname->base_name;
 
        (*dfree) = (*dsize) = 0;
        (*bsize) = 512;
@@ -66,24 +74,27 @@ uint64_t sys_disk_free(connection_struct *conn, const char *path,
         * If external disk calculation specified, use it.
         */
 
-       dfree_command = lp_dfree_command(talloc_tos(), SNUM(conn));
+       dfree_command = lp_dfree_command(talloc_tos(), lp_sub, SNUM(conn));
        if (dfree_command && *dfree_command) {
                const char *p;
                char **lines = NULL;
-               char *syscmd = NULL;
+               char **argl = NULL;
 
-               syscmd = talloc_asprintf(talloc_tos(),
-                               "%s %s",
-                               dfree_command,
-                               path);
-
-               if (!syscmd) {
+               argl = str_list_make_empty(talloc_tos());
+               str_list_add_printf(&argl, "%s", dfree_command);
+               str_list_add_printf(&argl, "%s", path);
+               if (argl == NULL) {
                        return (uint64_t)-1;
                }
 
-               DEBUG (3, ("disk_free: Running command '%s'\n", syscmd));
+               DBG_NOTICE("Running command '%s %s'\n",
+                       dfree_command,
+                       path);
+
+               lines = file_lines_ploadv(talloc_tos(), argl, NULL);
+
+               TALLOC_FREE(argl);
 
-               lines = file_lines_pload(talloc_tos(), syscmd, NULL);
                if (lines != NULL) {
                        char *line = lines[0];
 
@@ -111,19 +122,19 @@ uint64_t sys_disk_free(connection_struct *conn, const char *path,
 
                        goto dfree_done;
                }
-               DEBUG (0, ("disk_free: file_lines_load() failed for "
-                          "command '%s'. Error was : %s\n",
-                          syscmd, strerror(errno) ));
+               DBG_ERR("file_lines_load() failed for "
+                          "command '%s %s'. Error was : %s\n",
+                          dfree_command, path, strerror(errno));
        }
 
-       if (SMB_VFS_DISK_FREE(conn, path, bsize, dfree, dsize) ==
+       if (SMB_VFS_DISK_FREE(conn, fname, bsize, dfree, dsize) ==
            (uint64_t)-1) {
                DBG_ERR("VFS disk_free failed. Error was : %s\n",
                        strerror(errno));
                return (uint64_t)-1;
        }
 
-       if (disk_quotas(conn, path, &bsize_q, &dfree_q, &dsize_q)) {
+       if (disk_quotas(conn, fname, &bsize_q, &dfree_q, &dsize_q)) {
                uint64_t min_bsize = MIN(*bsize, bsize_q);
 
                (*dfree) = (*dfree) * (*bsize) / min_bsize;
@@ -165,51 +176,121 @@ dfree_done:
 
 /****************************************************************************
  Potentially returned cached dfree info.
+
+ Depending on the file system layout and file system features, the free space
+ information can be different for different sub directories underneath a SMB
+ share. Store the cache information in memcache using the query path as the
+ key to accommodate this.
 ****************************************************************************/
 
-uint64_t get_dfree_info(connection_struct *conn,
-                       const char *path,
-                       uint64_t *bsize,
-                       uint64_t *dfree,
-                       uint64_t *dsize)
+struct dfree_cached_info {
+       time_t last_dfree_time;
+       uint64_t dfree_ret;
+       uint64_t bsize;
+       uint64_t dfree;
+       uint64_t dsize;
+};
+
+uint64_t get_dfree_info(connection_struct *conn, struct smb_filename *fname,
+                       uint64_t *bsize, uint64_t *dfree, uint64_t *dsize)
 {
        int dfree_cache_time = lp_dfree_cache_time(SNUM(conn));
-       struct dfree_cached_info *dfc = conn->dfree_info;
+       struct dfree_cached_info *dfc = NULL;
+       struct dfree_cached_info dfc_new = { 0 };
        uint64_t dfree_ret;
+       char tmpbuf[PATH_MAX];
+       char *full_path = NULL;
+       char *to_free = NULL;
+       char *key_path = NULL;
+       size_t len;
+       DATA_BLOB key, value;
+       bool found;
 
        if (!dfree_cache_time) {
-               return sys_disk_free(conn, path, bsize, dfree, dsize);
+               return sys_disk_free(conn, fname, bsize, dfree, dsize);
        }
 
+       len = full_path_tos(conn->connectpath,
+                           fname->base_name,
+                           tmpbuf,
+                           sizeof(tmpbuf),
+                           &full_path,
+                           &to_free);
+       if (len == -1) {
+               errno = ENOMEM;
+               return -1;
+       }
+
+       if (VALID_STAT(fname->st) && S_ISREG(fname->st.st_ex_mode)) {
+               /*
+                * In case of a file use the parent directory to reduce number
+                * of cache entries.
+                */
+               bool ok;
+
+               ok = parent_dirname(talloc_tos(),
+                                   full_path,
+                                   &key_path,
+                                   NULL);
+               TALLOC_FREE(to_free); /* We're done with full_path */
+
+               if (!ok) {
+                       errno = ENOMEM;
+                       return -1;
+               }
+
+               /*
+                * key_path is always a talloced object.
+                */
+               to_free = key_path;
+       } else {
+               /*
+                * key_path might not be a talloced object; rely on
+                * to_free set from full_path_tos.
+                */
+               key_path = full_path;
+       }
+
+       key = data_blob_const(key_path, strlen(key_path));
+       found = memcache_lookup(smbd_memcache(),
+                               DFREE_CACHE,
+                               key,
+                               &value);
+       dfc = found ? (struct dfree_cached_info *)value.data : NULL;
+
        if (dfc && (conn->lastused - dfc->last_dfree_time < dfree_cache_time)) {
-               /* Return cached info. */
+               DBG_DEBUG("Returning dfree cache entry for %s\n", key_path);
                *bsize = dfc->bsize;
                *dfree = dfc->dfree;
                *dsize = dfc->dsize;
-               return dfc->dfree_ret;
+               dfree_ret = dfc->dfree_ret;
+               goto out;
        }
 
-       dfree_ret = sys_disk_free(conn, path, bsize, dfree, dsize);
+       dfree_ret = sys_disk_free(conn, fname, bsize, dfree, dsize);
 
        if (dfree_ret == (uint64_t)-1) {
                /* Don't cache bad data. */
-               return dfree_ret;
-       }
-
-       /* No cached info or time to refresh. */
-       if (!dfc) {
-               dfc = talloc(conn, struct dfree_cached_info);
-               if (!dfc) {
-                       return dfree_ret;
-               }
-               conn->dfree_info = dfc;
+               goto out;
        }
 
-       dfc->bsize = *bsize;
-       dfc->dfree = *dfree;
-       dfc->dsize = *dsize;
-       dfc->dfree_ret = dfree_ret;
-       dfc->last_dfree_time = conn->lastused;
-
+       DBG_DEBUG("Creating dfree cache entry for %s\n", key_path);
+       dfc_new.bsize = *bsize;
+       dfc_new.dfree = *dfree;
+       dfc_new.dsize = *dsize;
+       dfc_new.dfree_ret = dfree_ret;
+       dfc_new.last_dfree_time = conn->lastused;
+       memcache_add(smbd_memcache(),
+                    DFREE_CACHE,
+                    key,
+                    data_blob_const(&dfc_new, sizeof(dfc_new)));
+
+out:
+       TALLOC_FREE(to_free);
        return dfree_ret;
 }
+
+void flush_dfree_cache(void)
+{
+       memcache_flush(smbd_memcache(), DFREE_CACHE);
+}