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 uint64_t sys_disk_free(connection_struct
*conn
, struct smb_filename
*fname
,
55 uint64_t *bsize
, uint64_t *dfree
, uint64_t *dsize
)
57 uint64_t dfree_retval
;
61 const char *dfree_command
;
62 static bool dfree_broken
= false;
63 const char *path
= fname
->base_name
;
65 (*dfree
) = (*dsize
) = 0;
69 * If external disk calculation specified, use it.
72 dfree_command
= lp_dfree_command(talloc_tos(), SNUM(conn
));
73 if (dfree_command
&& *dfree_command
) {
78 syscmd
= talloc_asprintf(talloc_tos(),
87 DEBUG (3, ("disk_free: Running command '%s'\n", syscmd
));
89 lines
= file_lines_pload(talloc_tos(), syscmd
, NULL
);
91 char *line
= lines
[0];
93 DEBUG (3, ("Read input from dfree, \"%s\"\n", line
));
95 *dsize
= STR_TO_SMB_BIG_UINT(line
, &p
);
96 while (p
&& *p
&& isspace(*p
))
99 *dfree
= STR_TO_SMB_BIG_UINT(p
, &p
);
100 while (p
&& *p
&& isspace(*p
))
103 *bsize
= STR_TO_SMB_BIG_UINT(p
, NULL
);
107 DEBUG (3, ("Parsed output of dfree, dsize=%u, dfree=%u, bsize=%u\n",
108 (unsigned int)*dsize
, (unsigned int)*dfree
, (unsigned int)*bsize
));
117 DEBUG (0, ("disk_free: file_lines_load() failed for "
118 "command '%s'. Error was : %s\n",
119 syscmd
, strerror(errno
) ));
122 if (SMB_VFS_DISK_FREE(conn
, fname
, bsize
, dfree
, dsize
) ==
124 DBG_ERR("VFS disk_free failed. Error was : %s\n",
129 if (disk_quotas(conn
, fname
, &bsize_q
, &dfree_q
, &dsize_q
)) {
130 uint64_t min_bsize
= MIN(*bsize
, bsize_q
);
132 (*dfree
) = (*dfree
) * (*bsize
) / min_bsize
;
133 (*dsize
) = (*dsize
) * (*bsize
) / min_bsize
;
134 dfree_q
= dfree_q
* bsize_q
/ min_bsize
;
135 dsize_q
= dsize_q
* bsize_q
/ min_bsize
;
137 (*bsize
) = min_bsize
;
138 (*dfree
) = MIN(*dfree
,dfree_q
);
139 (*dsize
) = MIN(*dsize
,dsize_q
);
142 /* FIXME : Any reason for this assumption ? */
144 DEBUG(5,("disk_free:Warning: bsize == %d < 256 . Changing to assumed correct bsize = 512\n",(int)*bsize
));
150 DEBUG(0,("WARNING: dfree is broken on this system\n"));
153 *dsize
= 20*1024*1024/(*bsize
);
154 *dfree
= MAX(1,*dfree
);
158 disk_norm(bsize
, dfree
, dsize
);
160 if ((*bsize
) < 1024) {
161 dfree_retval
= (*dfree
)/(1024/(*bsize
));
163 dfree_retval
= ((*bsize
)/1024)*(*dfree
);
166 return(dfree_retval
);
169 /****************************************************************************
170 Potentially returned cached dfree info.
172 Depending on the file system layout and file system features, the free space
173 information can be different for different sub directories underneath a SMB
174 share. Store the cache information in memcache using the query path as the
175 key to accomodate this.
176 ****************************************************************************/
178 struct dfree_cached_info
{
179 time_t last_dfree_time
;
186 uint64_t get_dfree_info(connection_struct
*conn
, struct smb_filename
*fname
,
187 uint64_t *bsize
, uint64_t *dfree
, uint64_t *dsize
)
189 int dfree_cache_time
= lp_dfree_cache_time(SNUM(conn
));
190 struct dfree_cached_info
*dfc
= NULL
;
191 struct dfree_cached_info dfc_new
= { 0 };
193 char tmpbuf
[PATH_MAX
];
194 char *full_path
= NULL
;
195 char *to_free
= NULL
;
196 char *key_path
= NULL
;
198 DATA_BLOB key
, value
;
201 if (!dfree_cache_time
) {
202 return sys_disk_free(conn
, fname
, bsize
, dfree
, dsize
);
205 len
= full_path_tos(conn
->connectpath
,
216 if (VALID_STAT(fname
->st
) && S_ISREG(fname
->st
.st_ex_mode
)) {
218 * In case of a file use the parent directory to reduce number
223 ok
= parent_dirname(talloc_tos(),
227 TALLOC_FREE(to_free
); /* We're done with full_path */
235 * key_path is always a talloced object.
240 * key_path might not be a talloced object; rely on
241 * to_free set from full_path_tos.
243 key_path
= full_path
;
246 key
= data_blob_const(key_path
, strlen(key_path
));
247 found
= memcache_lookup(smbd_memcache(),
251 dfc
= found
? (struct dfree_cached_info
*)value
.data
: NULL
;
253 if (dfc
&& (conn
->lastused
- dfc
->last_dfree_time
< dfree_cache_time
)) {
254 DBG_DEBUG("Returning dfree cache entry for %s\n", key_path
);
258 dfree_ret
= dfc
->dfree_ret
;
262 dfree_ret
= sys_disk_free(conn
, fname
, bsize
, dfree
, dsize
);
264 if (dfree_ret
== (uint64_t)-1) {
265 /* Don't cache bad data. */
269 DBG_DEBUG("Creating dfree cache entry for %s\n", key_path
);
270 dfc_new
.bsize
= *bsize
;
271 dfc_new
.dfree
= *dfree
;
272 dfc_new
.dsize
= *dsize
;
273 dfc_new
.dfree_ret
= dfree_ret
;
274 dfc_new
.last_dfree_time
= conn
->lastused
;
275 memcache_add(smbd_memcache(),
278 data_blob_const(&dfc_new
, sizeof(dfc_new
)));
281 TALLOC_FREE(to_free
);
285 void flush_dfree_cache(void)
287 memcache_flush(smbd_memcache(), DFREE_CACHE
);