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