s3-smbd: Remove the global dfree_broken variable
[vlendec/samba-autobuild/.git] / source3 / smbd / dfree.c
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 3 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, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "includes.h"
21 #include "smbd/smbd.h"
22 #include "smbd/globals.h"
23
24 /****************************************************************************
25  Normalise for DOS usage.
26 ****************************************************************************/
27
28 void disk_norm(uint64_t *bsize, uint64_t *dfree, uint64_t *dsize)
29 {
30         /* check if the disk is beyond the max disk size */
31         uint64_t maxdisksize = lp_max_disk_size();
32         if (maxdisksize) {
33                 /* convert to blocks - and don't overflow */
34                 maxdisksize = ((maxdisksize*1024)/(*bsize))*1024;
35                 if (*dsize > maxdisksize) {
36                         *dsize = maxdisksize;
37                 }
38                 if (*dfree > maxdisksize) {
39                         *dfree = maxdisksize - 1;
40                 }
41                 /* the -1 should stop applications getting div by 0
42                    errors */
43         }
44 }
45
46
47
48 /****************************************************************************
49  Return number of 1K blocks available on a path and total number.
50 ****************************************************************************/
51
52 uint64_t sys_disk_free(connection_struct *conn, const char *path,
53                        uint64_t *bsize, uint64_t *dfree, uint64_t *dsize)
54 {
55         uint64_t dfree_retval;
56         uint64_t dfree_q = 0;
57         uint64_t bsize_q = 0;
58         uint64_t dsize_q = 0;
59         const char *dfree_command;
60         static bool dfree_broken = false;
61
62         (*dfree) = (*dsize) = 0;
63         (*bsize) = 512;
64
65         /*
66          * If external disk calculation specified, use it.
67          */
68
69         dfree_command = lp_dfree_command(talloc_tos(), SNUM(conn));
70         if (dfree_command && *dfree_command) {
71                 const char *p;
72                 char **lines = NULL;
73                 char *syscmd = NULL;
74
75                 syscmd = talloc_asprintf(talloc_tos(),
76                                 "%s %s",
77                                 dfree_command,
78                                 path);
79
80                 if (!syscmd) {
81                         return (uint64_t)-1;
82                 }
83
84                 DEBUG (3, ("disk_free: Running command '%s'\n", syscmd));
85
86                 lines = file_lines_pload(syscmd, NULL);
87                 if (lines != NULL) {
88                         char *line = lines[0];
89
90                         DEBUG (3, ("Read input from dfree, \"%s\"\n", line));
91
92                         *dsize = STR_TO_SMB_BIG_UINT(line, &p);
93                         while (p && *p && isspace(*p))
94                                 p++;
95                         if (p && *p)
96                                 *dfree = STR_TO_SMB_BIG_UINT(p, &p);
97                         while (p && *p && isspace(*p))
98                                 p++;
99                         if (p && *p)
100                                 *bsize = STR_TO_SMB_BIG_UINT(p, NULL);
101                         else
102                                 *bsize = 1024;
103                         TALLOC_FREE(lines);
104                         DEBUG (3, ("Parsed output of dfree, dsize=%u, dfree=%u, bsize=%u\n",
105                                 (unsigned int)*dsize, (unsigned int)*dfree, (unsigned int)*bsize));
106
107                         if (!*dsize)
108                                 *dsize = 2048;
109                         if (!*dfree)
110                                 *dfree = 1024;
111
112                         goto dfree_done;
113                 }
114                 DEBUG (0, ("disk_free: file_lines_load() failed for "
115                            "command '%s'. Error was : %s\n",
116                            syscmd, strerror(errno) ));
117         }
118
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 (uint64_t)-1;
123         }
124
125         if (disk_quotas(path, &bsize_q, &dfree_q, &dsize_q)) {
126                 (*bsize) = bsize_q;
127                 (*dfree) = MIN(*dfree,dfree_q);
128                 (*dsize) = MIN(*dsize,dsize_q);
129         }
130
131         /* FIXME : Any reason for this assumption ? */
132         if (*bsize < 256) {
133                 DEBUG(5,("disk_free:Warning: bsize == %d < 256 . Changing to assumed correct bsize = 512\n",(int)*bsize));
134                 *bsize = 512;
135         }
136
137         if ((*dsize)<1) {
138                 if (!dfree_broken) {
139                         DEBUG(0,("WARNING: dfree is broken on this system\n"));
140                         dfree_broken=true;
141                 }
142                 *dsize = 20*1024*1024/(*bsize);
143                 *dfree = MAX(1,*dfree);
144         }
145
146 dfree_done:
147         disk_norm(bsize, dfree, dsize);
148
149         if ((*bsize) < 1024) {
150                 dfree_retval = (*dfree)/(1024/(*bsize));
151         } else {
152                 dfree_retval = ((*bsize)/1024)*(*dfree);
153         }
154
155         return(dfree_retval);
156 }
157
158 /****************************************************************************
159  Potentially returned cached dfree info.
160 ****************************************************************************/
161
162 uint64_t get_dfree_info(connection_struct *conn,
163                         const char *path,
164                         uint64_t *bsize,
165                         uint64_t *dfree,
166                         uint64_t *dsize)
167 {
168         int dfree_cache_time = lp_dfree_cache_time(SNUM(conn));
169         struct dfree_cached_info *dfc = conn->dfree_info;
170         uint64_t dfree_ret;
171
172         if (!dfree_cache_time) {
173                 return SMB_VFS_DISK_FREE(conn, path, bsize, dfree, dsize);
174         }
175
176         if (dfc && (conn->lastused - dfc->last_dfree_time < dfree_cache_time)) {
177                 /* Return cached info. */
178                 *bsize = dfc->bsize;
179                 *dfree = dfc->dfree;
180                 *dsize = dfc->dsize;
181                 return dfc->dfree_ret;
182         }
183
184         dfree_ret = SMB_VFS_DISK_FREE(conn, path, bsize, dfree, dsize);
185
186         if (dfree_ret == (uint64_t)-1) {
187                 /* Don't cache bad data. */
188                 return dfree_ret;
189         }
190
191         /* No cached info or time to refresh. */
192         if (!dfc) {
193                 dfc = talloc(conn, struct dfree_cached_info);
194                 if (!dfc) {
195                         return dfree_ret;
196                 }
197                 conn->dfree_info = dfc;
198         }
199
200         dfc->bsize = *bsize;
201         dfc->dfree = *dfree;
202         dfc->dsize = *dsize;
203         dfc->dfree_ret = dfree_ret;
204         dfc->last_dfree_time = conn->lastused;
205
206         return dfree_ret;
207 }