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