s3: Rename server_event_context() to global_event_context()
[Samba.git] / source3 / smbd / dfree.c
blob05f6d6988c1cee8567fc18987b6d1756eeeeface
1 /*
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/>.
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"
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();
34 if (maxdisksize) {
35 /* convert to blocks - and don't overflow */
36 maxdisksize = ((maxdisksize*1024)/(*bsize))*1024;
37 if (*dsize > maxdisksize) {
38 *dsize = maxdisksize;
40 if (*dfree > maxdisksize) {
41 *dfree = maxdisksize - 1;
43 /* the -1 should stop applications getting div by 0
44 errors */
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;
58 uint64_t dfree_q = 0;
59 uint64_t bsize_q = 0;
60 uint64_t dsize_q = 0;
61 const char *dfree_command;
62 static bool dfree_broken = false;
63 const char *path = fname->base_name;
65 (*dfree) = (*dsize) = 0;
66 (*bsize) = 512;
69 * If external disk calculation specified, use it.
72 dfree_command = lp_dfree_command(talloc_tos(), SNUM(conn));
73 if (dfree_command && *dfree_command) {
74 const char *p;
75 char **lines = NULL;
76 char *syscmd = NULL;
78 syscmd = talloc_asprintf(talloc_tos(),
79 "%s %s",
80 dfree_command,
81 path);
83 if (!syscmd) {
84 return (uint64_t)-1;
87 DEBUG (3, ("disk_free: Running command '%s'\n", syscmd));
89 lines = file_lines_pload(talloc_tos(), syscmd, NULL);
90 if (lines != 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))
97 p++;
98 if (p && *p)
99 *dfree = STR_TO_SMB_BIG_UINT(p, &p);
100 while (p && *p && isspace(*p))
101 p++;
102 if (p && *p)
103 *bsize = STR_TO_SMB_BIG_UINT(p, NULL);
104 else
105 *bsize = 1024;
106 TALLOC_FREE(lines);
107 DEBUG (3, ("Parsed output of dfree, dsize=%u, dfree=%u, bsize=%u\n",
108 (unsigned int)*dsize, (unsigned int)*dfree, (unsigned int)*bsize));
110 if (!*dsize)
111 *dsize = 2048;
112 if (!*dfree)
113 *dfree = 1024;
115 goto dfree_done;
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) ==
123 (uint64_t)-1) {
124 DBG_ERR("VFS disk_free failed. Error was : %s\n",
125 strerror(errno));
126 return (uint64_t)-1;
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 ? */
143 if (*bsize < 256) {
144 DEBUG(5,("disk_free:Warning: bsize == %d < 256 . Changing to assumed correct bsize = 512\n",(int)*bsize));
145 *bsize = 512;
148 if ((*dsize)<1) {
149 if (!dfree_broken) {
150 DEBUG(0,("WARNING: dfree is broken on this system\n"));
151 dfree_broken=true;
153 *dsize = 20*1024*1024/(*bsize);
154 *dfree = MAX(1,*dfree);
157 dfree_done:
158 disk_norm(bsize, dfree, dsize);
160 if ((*bsize) < 1024) {
161 dfree_retval = (*dfree)/(1024/(*bsize));
162 } else {
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;
180 uint64_t dfree_ret;
181 uint64_t bsize;
182 uint64_t dfree;
183 uint64_t dsize;
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 };
192 uint64_t dfree_ret;
193 char tmpbuf[PATH_MAX];
194 char *full_path = NULL;
195 char *to_free = NULL;
196 char *key_path = NULL;
197 size_t len;
198 DATA_BLOB key, value;
199 bool found;
201 if (!dfree_cache_time) {
202 return sys_disk_free(conn, fname, bsize, dfree, dsize);
205 len = full_path_tos(conn->connectpath,
206 fname->base_name,
207 tmpbuf,
208 sizeof(tmpbuf),
209 &full_path,
210 &to_free);
211 if (len == -1) {
212 errno = ENOMEM;
213 return -1;
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
219 * of cache entries.
221 bool ok;
223 ok = parent_dirname(talloc_tos(),
224 full_path,
225 &key_path,
226 NULL);
227 TALLOC_FREE(to_free); /* We're done with full_path */
229 if (!ok) {
230 errno = ENOMEM;
231 return -1;
235 * key_path is always a talloced object.
237 to_free = key_path;
238 } else {
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(),
248 DFREE_CACHE,
249 key,
250 &value);
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);
255 *bsize = dfc->bsize;
256 *dfree = dfc->dfree;
257 *dsize = dfc->dsize;
258 dfree_ret = dfc->dfree_ret;
259 goto out;
262 dfree_ret = sys_disk_free(conn, fname, bsize, dfree, dsize);
264 if (dfree_ret == (uint64_t)-1) {
265 /* Don't cache bad data. */
266 goto out;
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(),
276 DFREE_CACHE,
277 key,
278 data_blob_const(&dfc_new, sizeof(dfc_new)));
280 out:
281 TALLOC_FREE(to_free);
282 return dfree_ret;
285 void flush_dfree_cache(void)
287 memcache_flush(smbd_memcache(), DFREE_CACHE);