2 Unix SMB/CIFS implementation.
3 functions to calculate the free disk space
4 Copyright (C) Andrew Tridgell 1998
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.
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.
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/>.
21 #include "smbd/smbd.h"
22 #include "smbd/globals.h"
23 #include "lib/util_file.h"
24 #include "lib/util/memcache.h"
26 /****************************************************************************
27 Normalise for DOS usage.
28 ****************************************************************************/
30 static void disk_norm(uint64_t *bsize
, uint64_t *dfree
, uint64_t *dsize
)
32 /* check if the disk is beyond the max disk size */
33 uint64_t maxdisksize
= lp_max_disk_size();
35 /* convert to blocks - and don't overflow */
36 maxdisksize
= ((maxdisksize
*1024)/(*bsize
))*1024;
37 if (*dsize
> maxdisksize
) {
40 if (*dfree
> maxdisksize
) {
41 *dfree
= maxdisksize
- 1;
43 /* the -1 should stop applications getting div by 0
50 /****************************************************************************
51 Return number of 1K blocks available on a path and total number.
52 ****************************************************************************/
54 static uint64_t sys_disk_free(connection_struct
*conn
,
55 struct smb_filename
*fname
,
60 const struct loadparm_substitution
*lp_sub
=
61 loadparm_s3_global_substitution();
62 uint64_t dfree_retval
;
66 const char *dfree_command
;
67 static bool dfree_broken
= false;
68 char *path
= fname
->base_name
;
70 (*dfree
) = (*dsize
) = 0;
74 * If external disk calculation specified, use it.
77 dfree_command
= lp_dfree_command(talloc_tos(), lp_sub
, SNUM(conn
));
78 if (dfree_command
&& *dfree_command
) {
83 argl
= str_list_make_empty(talloc_tos());
84 str_list_add_printf(&argl
, "%s", dfree_command
);
85 str_list_add_printf(&argl
, "%s", path
);
90 DBG_NOTICE("Running command '%s %s'\n",
94 lines
= file_lines_ploadv(talloc_tos(), argl
, NULL
);
99 char *line
= lines
[0];
101 DEBUG (3, ("Read input from dfree, \"%s\"\n", line
));
103 *dsize
= STR_TO_SMB_BIG_UINT(line
, &p
);
104 while (p
&& *p
&& isspace(*p
))
107 *dfree
= STR_TO_SMB_BIG_UINT(p
, &p
);
108 while (p
&& *p
&& isspace(*p
))
111 *bsize
= STR_TO_SMB_BIG_UINT(p
, NULL
);
115 DEBUG (3, ("Parsed output of dfree, dsize=%u, dfree=%u, bsize=%u\n",
116 (unsigned int)*dsize
, (unsigned int)*dfree
, (unsigned int)*bsize
));
125 DBG_ERR("file_lines_load() failed for "
126 "command '%s %s'. Error was : %s\n",
127 dfree_command
, path
, strerror(errno
));
130 if (SMB_VFS_DISK_FREE(conn
, fname
, bsize
, dfree
, dsize
) ==
132 DBG_ERR("VFS disk_free failed. Error was : %s\n",
137 if (disk_quotas(conn
, fname
, &bsize_q
, &dfree_q
, &dsize_q
)) {
138 uint64_t min_bsize
= MIN(*bsize
, bsize_q
);
140 (*dfree
) = (*dfree
) * (*bsize
) / min_bsize
;
141 (*dsize
) = (*dsize
) * (*bsize
) / min_bsize
;
142 dfree_q
= dfree_q
* bsize_q
/ min_bsize
;
143 dsize_q
= dsize_q
* bsize_q
/ min_bsize
;
145 (*bsize
) = min_bsize
;
146 (*dfree
) = MIN(*dfree
,dfree_q
);
147 (*dsize
) = MIN(*dsize
,dsize_q
);
150 /* FIXME : Any reason for this assumption ? */
152 DEBUG(5,("disk_free:Warning: bsize == %d < 256 . Changing to assumed correct bsize = 512\n",(int)*bsize
));
158 DEBUG(0,("WARNING: dfree is broken on this system\n"));
161 *dsize
= 20*1024*1024/(*bsize
);
162 *dfree
= MAX(1,*dfree
);
166 disk_norm(bsize
, dfree
, dsize
);
168 if ((*bsize
) < 1024) {
169 dfree_retval
= (*dfree
)/(1024/(*bsize
));
171 dfree_retval
= ((*bsize
)/1024)*(*dfree
);
174 return(dfree_retval
);
177 /****************************************************************************
178 Potentially returned cached dfree info.
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 accomodate this.
184 ****************************************************************************/
186 struct dfree_cached_info
{
187 time_t last_dfree_time
;
194 uint64_t get_dfree_info(connection_struct
*conn
, struct smb_filename
*fname
,
195 uint64_t *bsize
, uint64_t *dfree
, uint64_t *dsize
)
197 int dfree_cache_time
= lp_dfree_cache_time(SNUM(conn
));
198 struct dfree_cached_info
*dfc
= NULL
;
199 struct dfree_cached_info dfc_new
= { 0 };
201 char tmpbuf
[PATH_MAX
];
202 char *full_path
= NULL
;
203 char *to_free
= NULL
;
204 char *key_path
= NULL
;
206 DATA_BLOB key
, value
;
209 if (!dfree_cache_time
) {
210 return sys_disk_free(conn
, fname
, bsize
, dfree
, dsize
);
213 len
= full_path_tos(conn
->connectpath
,
224 if (VALID_STAT(fname
->st
) && S_ISREG(fname
->st
.st_ex_mode
)) {
226 * In case of a file use the parent directory to reduce number
231 ok
= parent_dirname(talloc_tos(),
235 TALLOC_FREE(to_free
); /* We're done with full_path */
243 * key_path is always a talloced object.
248 * key_path might not be a talloced object; rely on
249 * to_free set from full_path_tos.
251 key_path
= full_path
;
254 key
= data_blob_const(key_path
, strlen(key_path
));
255 found
= memcache_lookup(smbd_memcache(),
259 dfc
= found
? (struct dfree_cached_info
*)value
.data
: NULL
;
261 if (dfc
&& (conn
->lastused
- dfc
->last_dfree_time
< dfree_cache_time
)) {
262 DBG_DEBUG("Returning dfree cache entry for %s\n", key_path
);
266 dfree_ret
= dfc
->dfree_ret
;
270 dfree_ret
= sys_disk_free(conn
, fname
, bsize
, dfree
, dsize
);
272 if (dfree_ret
== (uint64_t)-1) {
273 /* Don't cache bad data. */
277 DBG_DEBUG("Creating dfree cache entry for %s\n", key_path
);
278 dfc_new
.bsize
= *bsize
;
279 dfc_new
.dfree
= *dfree
;
280 dfc_new
.dsize
= *dsize
;
281 dfc_new
.dfree_ret
= dfree_ret
;
282 dfc_new
.last_dfree_time
= conn
->lastused
;
283 memcache_add(smbd_memcache(),
286 data_blob_const(&dfc_new
, sizeof(dfc_new
)));
289 TALLOC_FREE(to_free
);
293 void flush_dfree_cache(void)
295 memcache_flush(smbd_memcache(), DFREE_CACHE
);