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