smbd: Factor out file_has_read_oplocks()
[bbaumbach/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 #include "lib/util_file.h"
24 #include "lib/util/memcache.h"
25
26 /****************************************************************************
27  Normalise for DOS usage.
28 ****************************************************************************/
29
30 static void disk_norm(uint64_t *bsize, uint64_t *dfree, uint64_t *dsize)
31 {
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;
39                 }
40                 if (*dfree > maxdisksize) {
41                         *dfree = maxdisksize - 1;
42                 }
43                 /* the -1 should stop applications getting div by 0
44                    errors */
45         }
46 }
47
48
49
50 /****************************************************************************
51  Return number of 1K blocks available on a path and total number.
52 ****************************************************************************/
53
54 uint64_t sys_disk_free(connection_struct *conn, struct smb_filename *fname,
55                        uint64_t *bsize, uint64_t *dfree, uint64_t *dsize)
56 {
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;
64
65         (*dfree) = (*dsize) = 0;
66         (*bsize) = 512;
67
68         /*
69          * If external disk calculation specified, use it.
70          */
71
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;
77
78                 syscmd = talloc_asprintf(talloc_tos(),
79                                 "%s %s",
80                                 dfree_command,
81                                 path);
82
83                 if (!syscmd) {
84                         return (uint64_t)-1;
85                 }
86
87                 DEBUG (3, ("disk_free: Running command '%s'\n", syscmd));
88
89                 lines = file_lines_pload(talloc_tos(), syscmd, NULL);
90                 if (lines != NULL) {
91                         char *line = lines[0];
92
93                         DEBUG (3, ("Read input from dfree, \"%s\"\n", line));
94
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));
109
110                         if (!*dsize)
111                                 *dsize = 2048;
112                         if (!*dfree)
113                                 *dfree = 1024;
114
115                         goto dfree_done;
116                 }
117                 DEBUG (0, ("disk_free: file_lines_load() failed for "
118                            "command '%s'. Error was : %s\n",
119                            syscmd, strerror(errno) ));
120         }
121
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;
127         }
128
129         if (disk_quotas(conn, fname, &bsize_q, &dfree_q, &dsize_q)) {
130                 uint64_t min_bsize = MIN(*bsize, bsize_q);
131
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;
136
137                 (*bsize) = min_bsize;
138                 (*dfree) = MIN(*dfree,dfree_q);
139                 (*dsize) = MIN(*dsize,dsize_q);
140         }
141
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;
146         }
147
148         if ((*dsize)<1) {
149                 if (!dfree_broken) {
150                         DEBUG(0,("WARNING: dfree is broken on this system\n"));
151                         dfree_broken=true;
152                 }
153                 *dsize = 20*1024*1024/(*bsize);
154                 *dfree = MAX(1,*dfree);
155         }
156
157 dfree_done:
158         disk_norm(bsize, dfree, dsize);
159
160         if ((*bsize) < 1024) {
161                 dfree_retval = (*dfree)/(1024/(*bsize));
162         } else {
163                 dfree_retval = ((*bsize)/1024)*(*dfree);
164         }
165
166         return(dfree_retval);
167 }
168
169 /****************************************************************************
170  Potentially returned cached dfree info.
171
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 ****************************************************************************/
177
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;
184 };
185
186 uint64_t get_dfree_info(connection_struct *conn, struct smb_filename *fname,
187                         uint64_t *bsize, uint64_t *dfree, uint64_t *dsize)
188 {
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;
200
201         if (!dfree_cache_time) {
202                 return sys_disk_free(conn, fname, bsize, dfree, dsize);
203         }
204
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;
214         }
215
216         if (VALID_STAT(fname->st) && S_ISREG(fname->st.st_ex_mode)) {
217                 /*
218                  * In case of a file use the parent directory to reduce number
219                  * of cache entries.
220                  */
221                 bool ok;
222
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 */
228
229                 if (!ok) {
230                         errno = ENOMEM;
231                         return -1;
232                 }
233
234                 /*
235                  * key_path is always a talloced object.
236                  */
237                 to_free = key_path;
238         } else {
239                 /*
240                  * key_path might not be a talloced object; rely on
241                  * to_free set from full_path_tos.
242                  */
243                 key_path = full_path;
244         }
245
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;
252
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;
260         }
261
262         dfree_ret = sys_disk_free(conn, fname, bsize, dfree, dsize);
263
264         if (dfree_ret == (uint64_t)-1) {
265                 /* Don't cache bad data. */
266                 goto out;
267         }
268
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)));
279
280 out:
281         TALLOC_FREE(to_free);
282         return dfree_ret;
283 }
284
285 void flush_dfree_cache(void)
286 {
287         memcache_flush(smbd_memcache(), DFREE_CACHE);
288 }