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/****************************************************************************
24normalise for DOS usage
25****************************************************************************/
26static void disk_norm(BOOL small_query, SMB_BIG_UINT *bsize,SMB_BIG_UINT *dfree,SMB_BIG_UINT *dsize)
27{
28	/* check if the disk is beyond the max disk size */
29	SMB_BIG_UINT maxdisksize = lp_maxdisksize();
30	if (maxdisksize) {
31		/* convert to blocks - and don't overflow */
32		maxdisksize = ((maxdisksize*1024)/(*bsize))*1024;
33		if (*dsize > maxdisksize) *dsize = maxdisksize;
34		if (*dfree > maxdisksize) *dfree = maxdisksize-1;
35		/* the -1 should stop applications getting div by 0
36		   errors */
37	}
38
39	if(small_query) {
40		while (*dfree > WORDMAX || *dsize > WORDMAX || *bsize < 512) {
41			*dfree /= 2;
42			*dsize /= 2;
43			*bsize *= 2;
44			/*
45			 * Force max to fit in 16 bit fields.
46			 */
47			if (*bsize > (WORDMAX*512)) {
48				*bsize = (WORDMAX*512);
49				if (*dsize > WORDMAX)
50					*dsize = WORDMAX;
51				if (*dfree >  WORDMAX)
52					*dfree = WORDMAX;
53				break;
54			}
55		}
56	}
57}
58
59
60
61/****************************************************************************
62  return number of 1K blocks available on a path and total number
63****************************************************************************/
64
65static SMB_BIG_UINT disk_free(const char *path, BOOL small_query,
66                              SMB_BIG_UINT *bsize,SMB_BIG_UINT *dfree,SMB_BIG_UINT *dsize)
67{
68	int dfree_retval;
69	SMB_BIG_UINT dfree_q = 0;
70	SMB_BIG_UINT bsize_q = 0;
71	SMB_BIG_UINT dsize_q = 0;
72	char *dfree_command;
73
74	(*dfree) = (*dsize) = 0;
75	(*bsize) = 512;
76
77	/*
78	 * If external disk calculation specified, use it.
79	 */
80
81	dfree_command = lp_dfree_command();
82	if (dfree_command && *dfree_command) {
83		const char *p;
84		char **lines;
85		pstring syscmd;
86
87		slprintf(syscmd, sizeof(syscmd)-1, "%s %s", dfree_command, path);
88		DEBUG (3, ("disk_free: Running command %s\n", syscmd));
89
90		lines = file_lines_pload(syscmd, NULL);
91		if (lines) {
92			char *line = lines[0];
93
94			DEBUG (3, ("Read input from dfree, \"%s\"\n", line));
95
96			*dsize = STR_TO_SMB_BIG_UINT(line, &p);
97			while (p && *p && isspace(*p))
98				p++;
99			if (p && *p)
100				*dfree = STR_TO_SMB_BIG_UINT(p, &p);
101			while (p && *p && isspace(*p))
102				p++;
103			if (p && *p)
104				*bsize = STR_TO_SMB_BIG_UINT(p, NULL);
105			else
106				*bsize = 1024;
107			file_lines_free(lines);
108			DEBUG (3, ("Parsed output of dfree, dsize=%u, dfree=%u, bsize=%u\n",
109				(unsigned int)*dsize, (unsigned int)*dfree, (unsigned int)*bsize));
110
111			if (!*dsize)
112				*dsize = 2048;
113			if (!*dfree)
114				*dfree = 1024;
115		} else {
116			DEBUG (0, ("disk_free: sys_popen() failed for command %s. Error was : %s\n",
117				syscmd, strerror(errno) ));
118			if (sys_fsusage(path, dfree, dsize) != 0) {
119				DEBUG (0, ("disk_free: sys_fsusage() failed. Error was : %s\n",
120					strerror(errno) ));
121				return (SMB_BIG_UINT)-1;
122			}
123		}
124	} else {
125		if (sys_fsusage(path, dfree, dsize) != 0) {
126			DEBUG (0, ("disk_free: sys_fsusage() failed. Error was : %s\n",
127				strerror(errno) ));
128			return (SMB_BIG_UINT)-1;
129		}
130	}
131
132	if (disk_quotas(path, &bsize_q, &dfree_q, &dsize_q)) {
133		(*bsize) = bsize_q;
134		(*dfree) = MIN(*dfree,dfree_q);
135		(*dsize) = MIN(*dsize,dsize_q);
136	}
137
138	/* FIXME : Any reason for this assumption ? */
139	if (*bsize < 256) {
140		DEBUG(5,("disk_free:Warning: bsize == %d < 256 . Changing to assumed correct bsize = 512\n",(int)*bsize));
141		*bsize = 512;
142	}
143
144	if ((*dsize)<1) {
145		static int done;
146		if (!done) {
147			DEBUG(0,("WARNING: dfree is broken on this system\n"));
148			done=1;
149		}
150		*dsize = 20*1024*1024/(*bsize);
151		*dfree = MAX(1,*dfree);
152	}
153
154	disk_norm(small_query,bsize,dfree,dsize);
155
156	if ((*bsize) < 1024) {
157		dfree_retval = (*dfree)/(1024/(*bsize));
158	} else {
159		dfree_retval = ((*bsize)/1024)*(*dfree);
160	}
161
162	return(dfree_retval);
163}
164
165
166/****************************************************************************
167wrap it to get filenames right
168****************************************************************************/
169SMB_BIG_UINT sys_disk_free(const char *path, BOOL small_query,
170                           SMB_BIG_UINT *bsize,SMB_BIG_UINT *dfree,SMB_BIG_UINT *dsize)
171{
172	return disk_free(path,small_query, bsize,dfree,dsize);
173}
174