| 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 | 
 | 
|---|
| 24 | /****************************************************************************
 | 
|---|
| 25 |  Normalise for DOS usage.
 | 
|---|
| 26 | ****************************************************************************/
 | 
|---|
| 27 | 
 | 
|---|
| 28 | static void disk_norm(bool small_query, uint64_t *bsize,uint64_t *dfree,uint64_t *dsize)
 | 
|---|
| 29 | {
 | 
|---|
| 30 |         /* check if the disk is beyond the max disk size */
 | 
|---|
| 31 |         uint64_t maxdisksize = lp_maxdisksize();
 | 
|---|
| 32 |         if (maxdisksize) {
 | 
|---|
| 33 |                 /* convert to blocks - and don't overflow */
 | 
|---|
| 34 |                 maxdisksize = ((maxdisksize*1024)/(*bsize))*1024;
 | 
|---|
| 35 |                 if (*dsize > maxdisksize) *dsize = maxdisksize;
 | 
|---|
| 36 |                 if (*dfree > maxdisksize) *dfree = maxdisksize-1; 
 | 
|---|
| 37 |                 /* the -1 should stop applications getting div by 0
 | 
|---|
| 38 |                    errors */
 | 
|---|
| 39 |         }  
 | 
|---|
| 40 | 
 | 
|---|
| 41 |         if(small_query) {       
 | 
|---|
| 42 |                 while (*dfree > WORDMAX || *dsize > WORDMAX || *bsize < 512) {
 | 
|---|
| 43 |                         *dfree /= 2;
 | 
|---|
| 44 |                         *dsize /= 2;
 | 
|---|
| 45 |                         *bsize *= 2;
 | 
|---|
| 46 |                         /*
 | 
|---|
| 47 |                          * Force max to fit in 16 bit fields.
 | 
|---|
| 48 |                          */
 | 
|---|
| 49 |                         if (*bsize > (WORDMAX*512)) {
 | 
|---|
| 50 |                                 *bsize = (WORDMAX*512);
 | 
|---|
| 51 |                                 if (*dsize > WORDMAX)
 | 
|---|
| 52 |                                         *dsize = WORDMAX;
 | 
|---|
| 53 |                                 if (*dfree >  WORDMAX)
 | 
|---|
| 54 |                                         *dfree = WORDMAX;
 | 
|---|
| 55 |                                 break;
 | 
|---|
| 56 |                         }
 | 
|---|
| 57 |                 }
 | 
|---|
| 58 |         }
 | 
|---|
| 59 | }
 | 
|---|
| 60 | 
 | 
|---|
| 61 | 
 | 
|---|
| 62 | 
 | 
|---|
| 63 | /****************************************************************************
 | 
|---|
| 64 |  Return number of 1K blocks available on a path and total number.
 | 
|---|
| 65 | ****************************************************************************/
 | 
|---|
| 66 | 
 | 
|---|
| 67 | uint64_t sys_disk_free(connection_struct *conn, const char *path, bool small_query, 
 | 
|---|
| 68 |                               uint64_t *bsize,uint64_t *dfree,uint64_t *dsize)
 | 
|---|
| 69 | {
 | 
|---|
| 70 |         uint64_t dfree_retval;
 | 
|---|
| 71 |         uint64_t dfree_q = 0;
 | 
|---|
| 72 |         uint64_t bsize_q = 0;
 | 
|---|
| 73 |         uint64_t dsize_q = 0;
 | 
|---|
| 74 |         const char *dfree_command;
 | 
|---|
| 75 | 
 | 
|---|
| 76 |         (*dfree) = (*dsize) = 0;
 | 
|---|
| 77 |         (*bsize) = 512;
 | 
|---|
| 78 | 
 | 
|---|
| 79 |         /*
 | 
|---|
| 80 |          * If external disk calculation specified, use it.
 | 
|---|
| 81 |          */
 | 
|---|
| 82 | 
 | 
|---|
| 83 |         dfree_command = lp_dfree_command(SNUM(conn));
 | 
|---|
| 84 |         if (dfree_command && *dfree_command) {
 | 
|---|
| 85 |                 const char *p;
 | 
|---|
| 86 |                 char **lines = NULL;
 | 
|---|
| 87 |                 char *syscmd = NULL;
 | 
|---|
| 88 | 
 | 
|---|
| 89 |                 syscmd = talloc_asprintf(talloc_tos(),
 | 
|---|
| 90 |                                 "%s %s",
 | 
|---|
| 91 |                                 dfree_command,
 | 
|---|
| 92 |                                 path);
 | 
|---|
| 93 | 
 | 
|---|
| 94 |                 if (!syscmd) {
 | 
|---|
| 95 |                         return (uint64_t)-1;
 | 
|---|
| 96 |                 }
 | 
|---|
| 97 | 
 | 
|---|
| 98 |                 DEBUG (3, ("disk_free: Running command %s\n", syscmd));
 | 
|---|
| 99 | 
 | 
|---|
| 100 |                 lines = file_lines_pload(syscmd, NULL);
 | 
|---|
| 101 |                 if (lines) {
 | 
|---|
| 102 |                         char *line = lines[0];
 | 
|---|
| 103 | 
 | 
|---|
| 104 |                         DEBUG (3, ("Read input from dfree, \"%s\"\n", line));
 | 
|---|
| 105 | 
 | 
|---|
| 106 |                         *dsize = STR_TO_SMB_BIG_UINT(line, &p);
 | 
|---|
| 107 |                         while (p && *p && isspace(*p))
 | 
|---|
| 108 |                                 p++;
 | 
|---|
| 109 |                         if (p && *p)
 | 
|---|
| 110 |                                 *dfree = STR_TO_SMB_BIG_UINT(p, &p);
 | 
|---|
| 111 |                         while (p && *p && isspace(*p))
 | 
|---|
| 112 |                                 p++;
 | 
|---|
| 113 |                         if (p && *p)
 | 
|---|
| 114 |                                 *bsize = STR_TO_SMB_BIG_UINT(p, NULL);
 | 
|---|
| 115 |                         else
 | 
|---|
| 116 |                                 *bsize = 1024;
 | 
|---|
| 117 |                         TALLOC_FREE(lines);
 | 
|---|
| 118 |                         DEBUG (3, ("Parsed output of dfree, dsize=%u, dfree=%u, bsize=%u\n",
 | 
|---|
| 119 |                                 (unsigned int)*dsize, (unsigned int)*dfree, (unsigned int)*bsize));
 | 
|---|
| 120 | 
 | 
|---|
| 121 |                         if (!*dsize)
 | 
|---|
| 122 |                                 *dsize = 2048;
 | 
|---|
| 123 |                         if (!*dfree)
 | 
|---|
| 124 |                                 *dfree = 1024;
 | 
|---|
| 125 |                 } else {
 | 
|---|
| 126 |                         DEBUG (0, ("disk_free: sys_popen() failed for command %s. Error was : %s\n",
 | 
|---|
| 127 |                                 syscmd, strerror(errno) ));
 | 
|---|
| 128 |                         if (sys_fsusage(path, dfree, dsize) != 0) {
 | 
|---|
| 129 |                                 DEBUG (0, ("disk_free: sys_fsusage() failed. Error was : %s\n",
 | 
|---|
| 130 |                                         strerror(errno) ));
 | 
|---|
| 131 |                                 return (uint64_t)-1;
 | 
|---|
| 132 |                         }
 | 
|---|
| 133 |                 }
 | 
|---|
| 134 |         } else {
 | 
|---|
| 135 |                 if (sys_fsusage(path, dfree, dsize) != 0) {
 | 
|---|
| 136 |                         DEBUG (0, ("disk_free: sys_fsusage() failed. Error was : %s\n",
 | 
|---|
| 137 |                                 strerror(errno) ));
 | 
|---|
| 138 |                         return (uint64_t)-1;
 | 
|---|
| 139 |                 }
 | 
|---|
| 140 |         }
 | 
|---|
| 141 | 
 | 
|---|
| 142 |         if (disk_quotas(path, &bsize_q, &dfree_q, &dsize_q)) {
 | 
|---|
| 143 |                 (*bsize) = bsize_q;
 | 
|---|
| 144 |                 (*dfree) = MIN(*dfree,dfree_q);
 | 
|---|
| 145 |                 (*dsize) = MIN(*dsize,dsize_q);
 | 
|---|
| 146 |         }
 | 
|---|
| 147 | 
 | 
|---|
| 148 |         /* FIXME : Any reason for this assumption ? */
 | 
|---|
| 149 |         if (*bsize < 256) {
 | 
|---|
| 150 |                 DEBUG(5,("disk_free:Warning: bsize == %d < 256 . Changing to assumed correct bsize = 512\n",(int)*bsize));
 | 
|---|
| 151 |                 *bsize = 512;
 | 
|---|
| 152 |         }
 | 
|---|
| 153 | 
 | 
|---|
| 154 |         if ((*dsize)<1) {
 | 
|---|
| 155 |                 if (!dfree_broken) {
 | 
|---|
| 156 |                         DEBUG(0,("WARNING: dfree is broken on this system\n"));
 | 
|---|
| 157 |                         dfree_broken=true;
 | 
|---|
| 158 |                 }
 | 
|---|
| 159 |                 *dsize = 20*1024*1024/(*bsize);
 | 
|---|
| 160 |                 *dfree = MAX(1,*dfree);
 | 
|---|
| 161 |         }
 | 
|---|
| 162 | 
 | 
|---|
| 163 |         disk_norm(small_query,bsize,dfree,dsize);
 | 
|---|
| 164 | 
 | 
|---|
| 165 |         if ((*bsize) < 1024) {
 | 
|---|
| 166 |                 dfree_retval = (*dfree)/(1024/(*bsize));
 | 
|---|
| 167 |         } else {
 | 
|---|
| 168 |                 dfree_retval = ((*bsize)/1024)*(*dfree);
 | 
|---|
| 169 |         }
 | 
|---|
| 170 | 
 | 
|---|
| 171 |         return(dfree_retval);
 | 
|---|
| 172 | }
 | 
|---|
| 173 | 
 | 
|---|
| 174 | /****************************************************************************
 | 
|---|
| 175 |  Potentially returned cached dfree info.
 | 
|---|
| 176 | ****************************************************************************/
 | 
|---|
| 177 | 
 | 
|---|
| 178 | uint64_t get_dfree_info(connection_struct *conn,
 | 
|---|
| 179 |                         const char *path,
 | 
|---|
| 180 |                         bool small_query,
 | 
|---|
| 181 |                         uint64_t *bsize,
 | 
|---|
| 182 |                         uint64_t *dfree,
 | 
|---|
| 183 |                         uint64_t *dsize)
 | 
|---|
| 184 | {
 | 
|---|
| 185 |         int dfree_cache_time = lp_dfree_cache_time(SNUM(conn));
 | 
|---|
| 186 |         struct dfree_cached_info *dfc = conn->dfree_info;
 | 
|---|
| 187 |         uint64_t dfree_ret;
 | 
|---|
| 188 | 
 | 
|---|
| 189 |         if (!dfree_cache_time) {
 | 
|---|
| 190 |                 return SMB_VFS_DISK_FREE(conn,path,small_query,bsize,dfree,dsize);
 | 
|---|
| 191 |         }
 | 
|---|
| 192 | 
 | 
|---|
| 193 |         if (dfc && (conn->lastused - dfc->last_dfree_time < dfree_cache_time)) {
 | 
|---|
| 194 |                 /* Return cached info. */
 | 
|---|
| 195 |                 *bsize = dfc->bsize;
 | 
|---|
| 196 |                 *dfree = dfc->dfree;
 | 
|---|
| 197 |                 *dsize = dfc->dsize;
 | 
|---|
| 198 |                 return dfc->dfree_ret;
 | 
|---|
| 199 |         }
 | 
|---|
| 200 | 
 | 
|---|
| 201 |         dfree_ret = SMB_VFS_DISK_FREE(conn,path,small_query,bsize,dfree,dsize);
 | 
|---|
| 202 | 
 | 
|---|
| 203 |         if (dfree_ret == (uint64_t)-1) {
 | 
|---|
| 204 |                 /* Don't cache bad data. */
 | 
|---|
| 205 |                 return dfree_ret;
 | 
|---|
| 206 |         }
 | 
|---|
| 207 | 
 | 
|---|
| 208 |         /* No cached info or time to refresh. */
 | 
|---|
| 209 |         if (!dfc) {
 | 
|---|
| 210 |                 dfc = TALLOC_P(conn, struct dfree_cached_info);
 | 
|---|
| 211 |                 if (!dfc) {
 | 
|---|
| 212 |                         return dfree_ret;
 | 
|---|
| 213 |                 }
 | 
|---|
| 214 |                 conn->dfree_info = dfc;
 | 
|---|
| 215 |         }
 | 
|---|
| 216 | 
 | 
|---|
| 217 |         dfc->bsize = *bsize;
 | 
|---|
| 218 |         dfc->dfree = *dfree;
 | 
|---|
| 219 |         dfc->dsize = *dsize;
 | 
|---|
| 220 |         dfc->dfree_ret = dfree_ret;
 | 
|---|
| 221 |         dfc->last_dfree_time = conn->lastused;
 | 
|---|
| 222 | 
 | 
|---|
| 223 |         return dfree_ret;
 | 
|---|
| 224 | }
 | 
|---|