LCOV - code coverage report
Current view: top level - source3/smbd - dfree.c (source / functions) Hit Total Coverage
Test: coverage report for master 6248eab5 Lines: 93 129 72.1 %
Date: 2021-08-25 13:27:56 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        1447 : 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        1447 :         uint64_t maxdisksize = lp_max_disk_size();
      34        1447 :         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        1447 : }
      47             : 
      48             : 
      49             : 
      50             : /****************************************************************************
      51             :  Return number of 1K blocks available on a path and total number.
      52             : ****************************************************************************/
      53             : 
      54        1447 : 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        1447 :         const struct loadparm_substitution *lp_sub =
      61             :                 loadparm_s3_global_substitution();
      62             :         uint64_t dfree_retval;
      63        1447 :         uint64_t dfree_q = 0;
      64        1447 :         uint64_t bsize_q = 0;
      65        1447 :         uint64_t dsize_q = 0;
      66             :         const char *dfree_command;
      67             :         static bool dfree_broken = false;
      68        1447 :         char *path = fname->base_name;
      69             : 
      70        1447 :         (*dfree) = (*dsize) = 0;
      71        1447 :         (*bsize) = 512;
      72             : 
      73             :         /*
      74             :          * If external disk calculation specified, use it.
      75             :          */
      76             : 
      77        1447 :         dfree_command = lp_dfree_command(talloc_tos(), lp_sub, SNUM(conn));
      78        1447 :         if (dfree_command && *dfree_command) {
      79             :                 const char *p;
      80          10 :                 char **lines = NULL;
      81          10 :                 char **argl = NULL;
      82             : 
      83          10 :                 argl = talloc_zero_array(talloc_tos(),
      84             :                                         char *,
      85             :                                         3);
      86          10 :                 if (argl == NULL) {
      87           0 :                         return (uint64_t)-1;
      88             :                 }
      89             : 
      90          10 :                 argl[0] = talloc_strdup(argl, dfree_command);
      91          10 :                 if (argl[0] == NULL) {
      92           0 :                         TALLOC_FREE(argl);
      93           0 :                         return (uint64_t)-1;
      94             :                 }
      95          10 :                 argl[1] = path;
      96          10 :                 argl[2] = NULL;
      97             : 
      98          10 :                 DBG_NOTICE("Running command '%s %s'\n",
      99             :                         dfree_command,
     100             :                         path);
     101             : 
     102          10 :                 lines = file_lines_ploadv(talloc_tos(), argl, NULL);
     103             : 
     104          10 :                 TALLOC_FREE(argl);
     105             : 
     106          10 :                 if (lines != NULL) {
     107          10 :                         char *line = lines[0];
     108             : 
     109          10 :                         DEBUG (3, ("Read input from dfree, \"%s\"\n", line));
     110             : 
     111          10 :                         *dsize = STR_TO_SMB_BIG_UINT(line, &p);
     112          30 :                         while (p && *p && isspace(*p))
     113          10 :                                 p++;
     114          10 :                         if (p && *p)
     115          10 :                                 *dfree = STR_TO_SMB_BIG_UINT(p, &p);
     116          30 :                         while (p && *p && isspace(*p))
     117          10 :                                 p++;
     118          10 :                         if (p && *p)
     119          10 :                                 *bsize = STR_TO_SMB_BIG_UINT(p, NULL);
     120             :                         else
     121           0 :                                 *bsize = 1024;
     122          10 :                         TALLOC_FREE(lines);
     123          10 :                         DEBUG (3, ("Parsed output of dfree, dsize=%u, dfree=%u, bsize=%u\n",
     124             :                                 (unsigned int)*dsize, (unsigned int)*dfree, (unsigned int)*bsize));
     125             : 
     126          10 :                         if (!*dsize)
     127           0 :                                 *dsize = 2048;
     128          10 :                         if (!*dfree)
     129           0 :                                 *dfree = 1024;
     130             : 
     131          20 :                         goto dfree_done;
     132             :                 }
     133           0 :                 DBG_ERR("file_lines_load() failed for "
     134             :                            "command '%s %s'. Error was : %s\n",
     135             :                            dfree_command, path, strerror(errno));
     136             :         }
     137             : 
     138        1437 :         if (SMB_VFS_DISK_FREE(conn, fname, bsize, dfree, dsize) ==
     139             :             (uint64_t)-1) {
     140           0 :                 DBG_ERR("VFS disk_free failed. Error was : %s\n",
     141             :                         strerror(errno));
     142           0 :                 return (uint64_t)-1;
     143             :         }
     144             : 
     145        1437 :         if (disk_quotas(conn, fname, &bsize_q, &dfree_q, &dsize_q)) {
     146          36 :                 uint64_t min_bsize = MIN(*bsize, bsize_q);
     147             : 
     148          36 :                 (*dfree) = (*dfree) * (*bsize) / min_bsize;
     149          36 :                 (*dsize) = (*dsize) * (*bsize) / min_bsize;
     150          36 :                 dfree_q = dfree_q * bsize_q / min_bsize;
     151          36 :                 dsize_q = dsize_q * bsize_q / min_bsize;
     152             : 
     153          36 :                 (*bsize) = min_bsize;
     154          36 :                 (*dfree) = MIN(*dfree,dfree_q);
     155          36 :                 (*dsize) = MIN(*dsize,dsize_q);
     156             :         }
     157             : 
     158             :         /* FIXME : Any reason for this assumption ? */
     159        1437 :         if (*bsize < 256) {
     160           0 :                 DEBUG(5,("disk_free:Warning: bsize == %d < 256 . Changing to assumed correct bsize = 512\n",(int)*bsize));
     161           0 :                 *bsize = 512;
     162             :         }
     163             : 
     164        1437 :         if ((*dsize)<1) {
     165           0 :                 if (!dfree_broken) {
     166           0 :                         DEBUG(0,("WARNING: dfree is broken on this system\n"));
     167           0 :                         dfree_broken=true;
     168             :                 }
     169           0 :                 *dsize = 20*1024*1024/(*bsize);
     170           0 :                 *dfree = MAX(1,*dfree);
     171             :         }
     172             : 
     173        2884 : dfree_done:
     174        1447 :         disk_norm(bsize, dfree, dsize);
     175             : 
     176        1447 :         if ((*bsize) < 1024) {
     177        1397 :                 dfree_retval = (*dfree)/(1024/(*bsize));
     178             :         } else {
     179          50 :                 dfree_retval = ((*bsize)/1024)*(*dfree);
     180             :         }
     181             : 
     182        1447 :         return(dfree_retval);
     183             : }
     184             : 
     185             : /****************************************************************************
     186             :  Potentially returned cached dfree info.
     187             : 
     188             :  Depending on the file system layout and file system features, the free space
     189             :  information can be different for different sub directories underneath a SMB
     190             :  share. Store the cache information in memcache using the query path as the
     191             :  key to accomodate this.
     192             : ****************************************************************************/
     193             : 
     194             : struct dfree_cached_info {
     195             :         time_t last_dfree_time;
     196             :         uint64_t dfree_ret;
     197             :         uint64_t bsize;
     198             :         uint64_t dfree;
     199             :         uint64_t dsize;
     200             : };
     201             : 
     202        1447 : uint64_t get_dfree_info(connection_struct *conn, struct smb_filename *fname,
     203             :                         uint64_t *bsize, uint64_t *dfree, uint64_t *dsize)
     204             : {
     205        1447 :         int dfree_cache_time = lp_dfree_cache_time(SNUM(conn));
     206        1447 :         struct dfree_cached_info *dfc = NULL;
     207        1447 :         struct dfree_cached_info dfc_new = { 0 };
     208             :         uint64_t dfree_ret;
     209             :         char tmpbuf[PATH_MAX];
     210        1447 :         char *full_path = NULL;
     211        1447 :         char *to_free = NULL;
     212        1447 :         char *key_path = NULL;
     213             :         size_t len;
     214             :         DATA_BLOB key, value;
     215             :         bool found;
     216             : 
     217        1447 :         if (!dfree_cache_time) {
     218        1443 :                 return sys_disk_free(conn, fname, bsize, dfree, dsize);
     219             :         }
     220             : 
     221           4 :         len = full_path_tos(conn->connectpath,
     222           4 :                             fname->base_name,
     223             :                             tmpbuf,
     224             :                             sizeof(tmpbuf),
     225             :                             &full_path,
     226             :                             &to_free);
     227           4 :         if (len == -1) {
     228           0 :                 errno = ENOMEM;
     229           0 :                 return -1;
     230             :         }
     231             : 
     232           4 :         if (VALID_STAT(fname->st) && S_ISREG(fname->st.st_ex_mode)) {
     233             :                 /*
     234             :                  * In case of a file use the parent directory to reduce number
     235             :                  * of cache entries.
     236             :                  */
     237             :                 bool ok;
     238             : 
     239           0 :                 ok = parent_dirname(talloc_tos(),
     240             :                                     full_path,
     241             :                                     &key_path,
     242             :                                     NULL);
     243           0 :                 TALLOC_FREE(to_free); /* We're done with full_path */
     244             : 
     245           0 :                 if (!ok) {
     246           0 :                         errno = ENOMEM;
     247           0 :                         return -1;
     248             :                 }
     249             : 
     250             :                 /*
     251             :                  * key_path is always a talloced object.
     252             :                  */
     253           0 :                 to_free = key_path;
     254             :         } else {
     255             :                 /*
     256             :                  * key_path might not be a talloced object; rely on
     257             :                  * to_free set from full_path_tos.
     258             :                  */
     259           4 :                 key_path = full_path;
     260             :         }
     261             : 
     262           4 :         key = data_blob_const(key_path, strlen(key_path));
     263           4 :         found = memcache_lookup(smbd_memcache(),
     264             :                                 DFREE_CACHE,
     265             :                                 key,
     266             :                                 &value);
     267           4 :         dfc = found ? (struct dfree_cached_info *)value.data : NULL;
     268             : 
     269           4 :         if (dfc && (conn->lastused - dfc->last_dfree_time < dfree_cache_time)) {
     270           0 :                 DBG_DEBUG("Returning dfree cache entry for %s\n", key_path);
     271           0 :                 *bsize = dfc->bsize;
     272           0 :                 *dfree = dfc->dfree;
     273           0 :                 *dsize = dfc->dsize;
     274           0 :                 dfree_ret = dfc->dfree_ret;
     275           0 :                 goto out;
     276             :         }
     277             : 
     278           4 :         dfree_ret = sys_disk_free(conn, fname, bsize, dfree, dsize);
     279             : 
     280           4 :         if (dfree_ret == (uint64_t)-1) {
     281             :                 /* Don't cache bad data. */
     282           0 :                 goto out;
     283             :         }
     284             : 
     285           4 :         DBG_DEBUG("Creating dfree cache entry for %s\n", key_path);
     286           4 :         dfc_new.bsize = *bsize;
     287           4 :         dfc_new.dfree = *dfree;
     288           4 :         dfc_new.dsize = *dsize;
     289           4 :         dfc_new.dfree_ret = dfree_ret;
     290           4 :         dfc_new.last_dfree_time = conn->lastused;
     291           4 :         memcache_add(smbd_memcache(),
     292             :                      DFREE_CACHE,
     293             :                      key,
     294             :                      data_blob_const(&dfc_new, sizeof(dfc_new)));
     295             : 
     296           4 : out:
     297           4 :         TALLOC_FREE(to_free);
     298           4 :         return dfree_ret;
     299             : }
     300             : 
     301        1188 : void flush_dfree_cache(void)
     302             : {
     303        1188 :         memcache_flush(smbd_memcache(), DFREE_CACHE);
     304        1188 : }

Generated by: LCOV version 1.13