LCOV - code coverage report
Current view: top level - source3/smbd - dfree.c (source / functions) Hit Total Coverage
Test: coverage report for master 2f515e9b Lines: 91 136 66.9 %
Date: 2024-04-21 15:09:00 Functions: 4 4 100.0 %

          Line data    Source code
       1             : /* 
       2             :    Unix SMB/CIFS implementation.
       3             :    functions to calculate the free disk space
       4             :    Copyright (C) Andrew Tridgell 1998
       5             :    
       6             :    This program is free software; you can redistribute it and/or modify
       7             :    it under the terms of the GNU General Public License as published by
       8             :    the Free Software Foundation; either version 3 of the License, or
       9             :    (at your option) any later version.
      10             :    
      11             :    This program is distributed in the hope that it will be useful,
      12             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      13             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14             :    GNU General Public License for more details.
      15             :    
      16             :    You should have received a copy of the GNU General Public License
      17             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.
      18             : */
      19             : 
      20             : #include "includes.h"
      21             : #include "smbd/smbd.h"
      22             : #include "smbd/globals.h"
      23             : #include "lib/util_file.h"
      24             : #include "lib/util/memcache.h"
      25             : 
      26             : /****************************************************************************
      27             :  Normalise for DOS usage.
      28             : ****************************************************************************/
      29             : 
      30        1645 : static void disk_norm(uint64_t *bsize, uint64_t *dfree, uint64_t *dsize)
      31             : {
      32             :         /* check if the disk is beyond the max disk size */
      33        1645 :         uint64_t maxdisksize = lp_max_disk_size();
      34        1645 :         if (maxdisksize) {
      35             :                 /* convert to blocks - and don't overflow */
      36           0 :                 maxdisksize = ((maxdisksize*1024)/(*bsize))*1024;
      37           0 :                 if (*dsize > maxdisksize) {
      38           0 :                         *dsize = maxdisksize;
      39             :                 }
      40           0 :                 if (*dfree > maxdisksize) {
      41           0 :                         *dfree = maxdisksize - 1;
      42             :                 }
      43             :                 /* the -1 should stop applications getting div by 0
      44             :                    errors */
      45             :         }
      46        1645 : }
      47             : 
      48             : 
      49             : 
      50             : /****************************************************************************
      51             :  Return number of 1K blocks available on a path and total number.
      52             : ****************************************************************************/
      53             : 
      54        1645 : static uint64_t sys_disk_free(connection_struct *conn,
      55             :                               struct smb_filename *fname,
      56             :                               uint64_t *bsize,
      57             :                               uint64_t *dfree,
      58             :                               uint64_t *dsize)
      59             : {
      60           0 :         const struct loadparm_substitution *lp_sub =
      61        1645 :                 loadparm_s3_global_substitution();
      62           0 :         uint64_t dfree_retval;
      63        1645 :         uint64_t dfree_q = 0;
      64        1645 :         uint64_t bsize_q = 0;
      65        1645 :         uint64_t dsize_q = 0;
      66           0 :         const char *dfree_command;
      67           0 :         static bool dfree_broken = false;
      68        1645 :         char *path = fname->base_name;
      69             : 
      70        1645 :         (*dfree) = (*dsize) = 0;
      71        1645 :         (*bsize) = 512;
      72             : 
      73             :         /*
      74             :          * If external disk calculation specified, use it.
      75             :          */
      76             : 
      77        1645 :         dfree_command = lp_dfree_command(talloc_tos(), lp_sub, SNUM(conn));
      78        1645 :         if (dfree_command && *dfree_command) {
      79           0 :                 const char *p;
      80          10 :                 char **lines = NULL;
      81          10 :                 char **argl = NULL;
      82             : 
      83          10 :                 argl = str_list_make_empty(talloc_tos());
      84          10 :                 str_list_add_printf(&argl, "%s", dfree_command);
      85          10 :                 str_list_add_printf(&argl, "%s", path);
      86          10 :                 if (argl == NULL) {
      87           0 :                         return (uint64_t)-1;
      88             :                 }
      89             : 
      90          10 :                 DBG_NOTICE("Running command '%s %s'\n",
      91             :                         dfree_command,
      92             :                         path);
      93             : 
      94          10 :                 lines = file_lines_ploadv(talloc_tos(), argl, NULL);
      95             : 
      96          10 :                 TALLOC_FREE(argl);
      97             : 
      98          10 :                 if (lines != NULL) {
      99          10 :                         char *line = lines[0];
     100             : 
     101          10 :                         DEBUG (3, ("Read input from dfree, \"%s\"\n", line));
     102             : 
     103          10 :                         *dsize = STR_TO_SMB_BIG_UINT(line, &p);
     104          20 :                         while (p && *p && isspace(*p))
     105          10 :                                 p++;
     106          10 :                         if (p && *p)
     107          10 :                                 *dfree = STR_TO_SMB_BIG_UINT(p, &p);
     108          20 :                         while (p && *p && isspace(*p))
     109          10 :                                 p++;
     110          10 :                         if (p && *p)
     111          10 :                                 *bsize = STR_TO_SMB_BIG_UINT(p, NULL);
     112             :                         else
     113           0 :                                 *bsize = 1024;
     114          10 :                         TALLOC_FREE(lines);
     115          10 :                         DEBUG (3, ("Parsed output of dfree, dsize=%u, dfree=%u, bsize=%u\n",
     116             :                                 (unsigned int)*dsize, (unsigned int)*dfree, (unsigned int)*bsize));
     117             : 
     118          10 :                         if (!*dsize)
     119           0 :                                 *dsize = 2048;
     120          10 :                         if (!*dfree)
     121           0 :                                 *dfree = 1024;
     122             : 
     123          10 :                         goto dfree_done;
     124             :                 }
     125           0 :                 DBG_ERR("file_lines_load() failed for "
     126             :                            "command '%s %s'. Error was : %s\n",
     127             :                            dfree_command, path, strerror(errno));
     128             :         }
     129             : 
     130        1635 :         if (SMB_VFS_DISK_FREE(conn, fname, bsize, dfree, dsize) ==
     131             :             (uint64_t)-1) {
     132           0 :                 DBG_ERR("VFS disk_free failed. Error was : %s\n",
     133             :                         strerror(errno));
     134           0 :                 return (uint64_t)-1;
     135             :         }
     136             : 
     137        1635 :         if (disk_quotas(conn, fname, &bsize_q, &dfree_q, &dsize_q)) {
     138          36 :                 uint64_t min_bsize = MIN(*bsize, bsize_q);
     139             : 
     140          36 :                 (*dfree) = (*dfree) * (*bsize) / min_bsize;
     141          36 :                 (*dsize) = (*dsize) * (*bsize) / min_bsize;
     142          36 :                 dfree_q = dfree_q * bsize_q / min_bsize;
     143          36 :                 dsize_q = dsize_q * bsize_q / min_bsize;
     144             : 
     145          36 :                 (*bsize) = min_bsize;
     146          36 :                 (*dfree) = MIN(*dfree,dfree_q);
     147          36 :                 (*dsize) = MIN(*dsize,dsize_q);
     148             :         }
     149             : 
     150             :         /* FIXME : Any reason for this assumption ? */
     151        1635 :         if (*bsize < 256) {
     152           0 :                 DEBUG(5,("disk_free:Warning: bsize == %d < 256 . Changing to assumed correct bsize = 512\n",(int)*bsize));
     153           0 :                 *bsize = 512;
     154             :         }
     155             : 
     156        1635 :         if ((*dsize)<1) {
     157           0 :                 if (!dfree_broken) {
     158           0 :                         DEBUG(0,("WARNING: dfree is broken on this system\n"));
     159           0 :                         dfree_broken=true;
     160             :                 }
     161           0 :                 *dsize = 20*1024*1024/(*bsize);
     162           0 :                 *dfree = MAX(1,*dfree);
     163             :         }
     164             : 
     165        1635 : dfree_done:
     166        1645 :         disk_norm(bsize, dfree, dsize);
     167             : 
     168        1645 :         if ((*bsize) < 1024) {
     169        1595 :                 dfree_retval = (*dfree)/(1024/(*bsize));
     170             :         } else {
     171          50 :                 dfree_retval = ((*bsize)/1024)*(*dfree);
     172             :         }
     173             : 
     174        1645 :         return(dfree_retval);
     175             : }
     176             : 
     177             : /****************************************************************************
     178             :  Potentially returned cached dfree info.
     179             : 
     180             :  Depending on the file system layout and file system features, the free space
     181             :  information can be different for different sub directories underneath a SMB
     182             :  share. Store the cache information in memcache using the query path as the
     183             :  key to accommodate this.
     184             : ****************************************************************************/
     185             : 
     186             : struct dfree_cached_info {
     187             :         time_t last_dfree_time;
     188             :         uint64_t dfree_ret;
     189             :         uint64_t bsize;
     190             :         uint64_t dfree;
     191             :         uint64_t dsize;
     192             : };
     193             : 
     194        1645 : uint64_t get_dfree_info(connection_struct *conn, struct smb_filename *fname,
     195             :                         uint64_t *bsize, uint64_t *dfree, uint64_t *dsize)
     196             : {
     197        1645 :         int dfree_cache_time = lp_dfree_cache_time(SNUM(conn));
     198        1645 :         struct dfree_cached_info *dfc = NULL;
     199        1645 :         struct dfree_cached_info dfc_new = { 0 };
     200           0 :         uint64_t dfree_ret;
     201           0 :         char tmpbuf[PATH_MAX];
     202        1645 :         char *full_path = NULL;
     203        1645 :         char *to_free = NULL;
     204        1645 :         char *key_path = NULL;
     205           0 :         size_t len;
     206           0 :         DATA_BLOB key, value;
     207           0 :         bool found;
     208             : 
     209        1645 :         if (!dfree_cache_time) {
     210        1641 :                 return sys_disk_free(conn, fname, bsize, dfree, dsize);
     211             :         }
     212             : 
     213           8 :         len = full_path_tos(conn->connectpath,
     214           4 :                             fname->base_name,
     215             :                             tmpbuf,
     216             :                             sizeof(tmpbuf),
     217             :                             &full_path,
     218             :                             &to_free);
     219           4 :         if (len == -1) {
     220           0 :                 errno = ENOMEM;
     221           0 :                 return -1;
     222             :         }
     223             : 
     224           4 :         if (VALID_STAT(fname->st) && S_ISREG(fname->st.st_ex_mode)) {
     225             :                 /*
     226             :                  * In case of a file use the parent directory to reduce number
     227             :                  * of cache entries.
     228             :                  */
     229           0 :                 bool ok;
     230             : 
     231           0 :                 ok = parent_dirname(talloc_tos(),
     232             :                                     full_path,
     233             :                                     &key_path,
     234             :                                     NULL);
     235           0 :                 TALLOC_FREE(to_free); /* We're done with full_path */
     236             : 
     237           0 :                 if (!ok) {
     238           0 :                         errno = ENOMEM;
     239           0 :                         return -1;
     240             :                 }
     241             : 
     242             :                 /*
     243             :                  * key_path is always a talloced object.
     244             :                  */
     245           0 :                 to_free = key_path;
     246             :         } else {
     247             :                 /*
     248             :                  * key_path might not be a talloced object; rely on
     249             :                  * to_free set from full_path_tos.
     250             :                  */
     251           4 :                 key_path = full_path;
     252             :         }
     253             : 
     254           4 :         key = data_blob_const(key_path, strlen(key_path));
     255           4 :         found = memcache_lookup(smbd_memcache(),
     256             :                                 DFREE_CACHE,
     257             :                                 key,
     258             :                                 &value);
     259           4 :         dfc = found ? (struct dfree_cached_info *)value.data : NULL;
     260             : 
     261           4 :         if (dfc && (conn->lastused - dfc->last_dfree_time < dfree_cache_time)) {
     262           0 :                 DBG_DEBUG("Returning dfree cache entry for %s\n", key_path);
     263           0 :                 *bsize = dfc->bsize;
     264           0 :                 *dfree = dfc->dfree;
     265           0 :                 *dsize = dfc->dsize;
     266           0 :                 dfree_ret = dfc->dfree_ret;
     267           0 :                 goto out;
     268             :         }
     269             : 
     270           4 :         dfree_ret = sys_disk_free(conn, fname, bsize, dfree, dsize);
     271             : 
     272           4 :         if (dfree_ret == (uint64_t)-1) {
     273             :                 /* Don't cache bad data. */
     274           0 :                 goto out;
     275             :         }
     276             : 
     277           4 :         DBG_DEBUG("Creating dfree cache entry for %s\n", key_path);
     278           4 :         dfc_new.bsize = *bsize;
     279           4 :         dfc_new.dfree = *dfree;
     280           4 :         dfc_new.dsize = *dsize;
     281           4 :         dfc_new.dfree_ret = dfree_ret;
     282           4 :         dfc_new.last_dfree_time = conn->lastused;
     283           4 :         memcache_add(smbd_memcache(),
     284             :                      DFREE_CACHE,
     285             :                      key,
     286             :                      data_blob_const(&dfc_new, sizeof(dfc_new)));
     287             : 
     288           4 : out:
     289           4 :         TALLOC_FREE(to_free);
     290           4 :         return dfree_ret;
     291             : }
     292             : 
     293        1208 : void flush_dfree_cache(void)
     294             : {
     295        1208 :         memcache_flush(smbd_memcache(), DFREE_CACHE);
     296        1208 : }

Generated by: LCOV version 1.14