smbd: make canonicalize_snapshot_path() public
[samba.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 static uint64_t sys_disk_free(connection_struct *conn,
55                               struct smb_filename *fname,
56                               uint64_t *bsize,
57                               uint64_t *dfree,
58                               uint64_t *dsize)
59 {
60         const struct loadparm_substitution *lp_sub =
61                 loadparm_s3_global_substitution();
62         uint64_t dfree_retval;
63         uint64_t dfree_q = 0;
64         uint64_t bsize_q = 0;
65         uint64_t dsize_q = 0;
66         const char *dfree_command;
67         static bool dfree_broken = false;
68         char *path = fname->base_name;
69
70         (*dfree) = (*dsize) = 0;
71         (*bsize) = 512;
72
73         /*
74          * If external disk calculation specified, use it.
75          */
76
77         dfree_command = lp_dfree_command(talloc_tos(), lp_sub, SNUM(conn));
78         if (dfree_command && *dfree_command) {
79                 const char *p;
80                 char **lines = NULL;
81                 char **argl = NULL;
82
83                 argl = talloc_zero_array(talloc_tos(),
84                                         char *,
85                                         3);
86                 if (argl == NULL) {
87                         return (uint64_t)-1;
88                 }
89
90                 argl[0] = talloc_strdup(argl, dfree_command);
91                 if (argl[0] == NULL) {
92                         TALLOC_FREE(argl);
93                         return (uint64_t)-1;
94                 }
95                 argl[1] = path;
96                 argl[2] = NULL;
97
98                 DBG_NOTICE("Running command '%s %s'\n",
99                         dfree_command,
100                         path);
101
102                 lines = file_lines_ploadv(talloc_tos(), argl, NULL);
103
104                 TALLOC_FREE(argl);
105
106                 if (lines != NULL) {
107                         char *line = lines[0];
108
109                         DEBUG (3, ("Read input from dfree, \"%s\"\n", line));
110
111                         *dsize = STR_TO_SMB_BIG_UINT(line, &p);
112                         while (p && *p && isspace(*p))
113                                 p++;
114                         if (p && *p)
115                                 *dfree = STR_TO_SMB_BIG_UINT(p, &p);
116                         while (p && *p && isspace(*p))
117                                 p++;
118                         if (p && *p)
119                                 *bsize = STR_TO_SMB_BIG_UINT(p, NULL);
120                         else
121                                 *bsize = 1024;
122                         TALLOC_FREE(lines);
123                         DEBUG (3, ("Parsed output of dfree, dsize=%u, dfree=%u, bsize=%u\n",
124                                 (unsigned int)*dsize, (unsigned int)*dfree, (unsigned int)*bsize));
125
126                         if (!*dsize)
127                                 *dsize = 2048;
128                         if (!*dfree)
129                                 *dfree = 1024;
130
131                         goto dfree_done;
132                 }
133                 DBG_ERR("file_lines_load() failed for "
134                            "command '%s %s'. Error was : %s\n",
135                            dfree_command, path, strerror(errno));
136         }
137
138         if (SMB_VFS_DISK_FREE(conn, fname, bsize, dfree, dsize) ==
139             (uint64_t)-1) {
140                 DBG_ERR("VFS disk_free failed. Error was : %s\n",
141                         strerror(errno));
142                 return (uint64_t)-1;
143         }
144
145         if (disk_quotas(conn, fname, &bsize_q, &dfree_q, &dsize_q)) {
146                 uint64_t min_bsize = MIN(*bsize, bsize_q);
147
148                 (*dfree) = (*dfree) * (*bsize) / min_bsize;
149                 (*dsize) = (*dsize) * (*bsize) / min_bsize;
150                 dfree_q = dfree_q * bsize_q / min_bsize;
151                 dsize_q = dsize_q * bsize_q / min_bsize;
152
153                 (*bsize) = min_bsize;
154                 (*dfree) = MIN(*dfree,dfree_q);
155                 (*dsize) = MIN(*dsize,dsize_q);
156         }
157
158         /* FIXME : Any reason for this assumption ? */
159         if (*bsize < 256) {
160                 DEBUG(5,("disk_free:Warning: bsize == %d < 256 . Changing to assumed correct bsize = 512\n",(int)*bsize));
161                 *bsize = 512;
162         }
163
164         if ((*dsize)<1) {
165                 if (!dfree_broken) {
166                         DEBUG(0,("WARNING: dfree is broken on this system\n"));
167                         dfree_broken=true;
168                 }
169                 *dsize = 20*1024*1024/(*bsize);
170                 *dfree = MAX(1,*dfree);
171         }
172
173 dfree_done:
174         disk_norm(bsize, dfree, dsize);
175
176         if ((*bsize) < 1024) {
177                 dfree_retval = (*dfree)/(1024/(*bsize));
178         } else {
179                 dfree_retval = ((*bsize)/1024)*(*dfree);
180         }
181
182         return(dfree_retval);
183 }
184
185 /****************************************************************************
186  Potentially returned cached dfree info.
187
188  Depending on the file system layout and file system features, the free space
189  information can be different for different sub directories underneath a SMB
190  share. Store the cache information in memcache using the query path as the
191  key to accomodate this.
192 ****************************************************************************/
193
194 struct dfree_cached_info {
195         time_t last_dfree_time;
196         uint64_t dfree_ret;
197         uint64_t bsize;
198         uint64_t dfree;
199         uint64_t dsize;
200 };
201
202 uint64_t get_dfree_info(connection_struct *conn, struct smb_filename *fname,
203                         uint64_t *bsize, uint64_t *dfree, uint64_t *dsize)
204 {
205         int dfree_cache_time = lp_dfree_cache_time(SNUM(conn));
206         struct dfree_cached_info *dfc = NULL;
207         struct dfree_cached_info dfc_new = { 0 };
208         uint64_t dfree_ret;
209         char tmpbuf[PATH_MAX];
210         char *full_path = NULL;
211         char *to_free = NULL;
212         char *key_path = NULL;
213         size_t len;
214         DATA_BLOB key, value;
215         bool found;
216
217         if (!dfree_cache_time) {
218                 return sys_disk_free(conn, fname, bsize, dfree, dsize);
219         }
220
221         len = full_path_tos(conn->connectpath,
222                             fname->base_name,
223                             tmpbuf,
224                             sizeof(tmpbuf),
225                             &full_path,
226                             &to_free);
227         if (len == -1) {
228                 errno = ENOMEM;
229                 return -1;
230         }
231
232         if (VALID_STAT(fname->st) && S_ISREG(fname->st.st_ex_mode)) {
233                 /*
234                  * In case of a file use the parent directory to reduce number
235                  * of cache entries.
236                  */
237                 bool ok;
238
239                 ok = parent_dirname(talloc_tos(),
240                                     full_path,
241                                     &key_path,
242                                     NULL);
243                 TALLOC_FREE(to_free); /* We're done with full_path */
244
245                 if (!ok) {
246                         errno = ENOMEM;
247                         return -1;
248                 }
249
250                 /*
251                  * key_path is always a talloced object.
252                  */
253                 to_free = key_path;
254         } else {
255                 /*
256                  * key_path might not be a talloced object; rely on
257                  * to_free set from full_path_tos.
258                  */
259                 key_path = full_path;
260         }
261
262         key = data_blob_const(key_path, strlen(key_path));
263         found = memcache_lookup(smbd_memcache(),
264                                 DFREE_CACHE,
265                                 key,
266                                 &value);
267         dfc = found ? (struct dfree_cached_info *)value.data : NULL;
268
269         if (dfc && (conn->lastused - dfc->last_dfree_time < dfree_cache_time)) {
270                 DBG_DEBUG("Returning dfree cache entry for %s\n", key_path);
271                 *bsize = dfc->bsize;
272                 *dfree = dfc->dfree;
273                 *dsize = dfc->dsize;
274                 dfree_ret = dfc->dfree_ret;
275                 goto out;
276         }
277
278         dfree_ret = sys_disk_free(conn, fname, bsize, dfree, dsize);
279
280         if (dfree_ret == (uint64_t)-1) {
281                 /* Don't cache bad data. */
282                 goto out;
283         }
284
285         DBG_DEBUG("Creating dfree cache entry for %s\n", key_path);
286         dfc_new.bsize = *bsize;
287         dfc_new.dfree = *dfree;
288         dfc_new.dsize = *dsize;
289         dfc_new.dfree_ret = dfree_ret;
290         dfc_new.last_dfree_time = conn->lastused;
291         memcache_add(smbd_memcache(),
292                      DFREE_CACHE,
293                      key,
294                      data_blob_const(&dfc_new, sizeof(dfc_new)));
295
296 out:
297         TALLOC_FREE(to_free);
298         return dfree_ret;
299 }
300
301 void flush_dfree_cache(void)
302 {
303         memcache_flush(smbd_memcache(), DFREE_CACHE);
304 }