r23726: Explicitly pass down the FLAGS2 field to srvstr_pull_buf. The next
[tprouty/samba.git] / source / smbd / dfree.c
index 799ff6a24cda1f3d969d4b96fa0f0e93429ffa35..c488add227a80debfeddadf043c7f4eccbd6ea4c 100644 (file)
@@ -1,6 +1,5 @@
 /* 
-   Unix SMB/Netbios implementation.
-   Version 1.9.
+   Unix SMB/CIFS implementation.
    functions to calculate the free disk space
    Copyright (C) Andrew Tridgell 1998
    
 
 #include "includes.h"
 
-
-extern int DEBUGLEVEL;
-
 /****************************************************************************
-normalise for DOS usage 
+ Normalise for DOS usage.
 ****************************************************************************/
-static void disk_norm(int *bsize,int *dfree,int *dsize)
+
+static void disk_norm(BOOL small_query, SMB_BIG_UINT *bsize,SMB_BIG_UINT *dfree,SMB_BIG_UINT *dsize)
 {
        /* check if the disk is beyond the max disk size */
-       int maxdisksize = lp_maxdisksize();
+       SMB_BIG_UINT maxdisksize = lp_maxdisksize();
        if (maxdisksize) {
                /* convert to blocks - and don't overflow */
                maxdisksize = ((maxdisksize*1024)/(*bsize))*1024;
@@ -39,162 +36,109 @@ static void disk_norm(int *bsize,int *dfree,int *dsize)
                /* the -1 should stop applications getting div by 0
                   errors */
        }  
-       
-       while (*dfree > WORDMAX || *dsize > WORDMAX || *bsize < 512) {
-               *dfree /= 2;
-               *dsize /= 2;
-               *bsize *= 2;
-               if (*bsize > WORDMAX) {
-                       *bsize = WORDMAX;
-                       if (*dsize > WORDMAX)
-                               *dsize = WORDMAX;
-                       if (*dfree >  WORDMAX)
-                               *dfree = WORDMAX;
-                       break;
-               }
-       }
-}
-
 
-/* Return the number of TOSIZE-byte blocks used by
-   BLOCKS FROMSIZE-byte blocks, rounding away from zero.
-   TOSIZE must be positive.  Return -1 if FROMSIZE is not positive.  */
-static int adjust_blocks(int blocks, int fromsize, int tosize)
-{
-       if (tosize <= 0 || fromsize <= 0) {
-               return -1;
+       if(small_query) {       
+               while (*dfree > WORDMAX || *dsize > WORDMAX || *bsize < 512) {
+                       *dfree /= 2;
+                       *dsize /= 2;
+                       *bsize *= 2;
+                       /*
+                        * Force max to fit in 16 bit fields.
+                        */
+                       if (*bsize > (WORDMAX*512)) {
+                               *bsize = (WORDMAX*512);
+                               if (*dsize > WORDMAX)
+                                       *dsize = WORDMAX;
+                               if (*dfree >  WORDMAX)
+                                       *dfree = WORDMAX;
+                               break;
+                       }
+               }
        }
-
-       if (fromsize == tosize) /* e.g., from 512 to 512 */
-               return blocks;
-       else if (fromsize > tosize)     /* e.g., from 2048 to 512 */
-               return blocks * (fromsize / tosize);
-       else                            /* e.g., from 256 to 512 */
-               return (blocks + (blocks < 0 ? -1 : 1)) / (tosize / fromsize);
 }
 
-/* this does all of the system specific guff to get the free disk space.
-   It is derived from code in the GNU fileutils package, but has been
-   considerably mangled for use here 
 
-   results are returned in *dfree and *dsize, in 512 byte units
-*/
-static int fsusage(const char *path, int *dfree, int *dsize)
-{
-#ifdef STAT_STATFS3_OSF1
-#define CONVERT_BLOCKS(B) adjust_blocks ((B), fsd.f_fsize, 512)
-       struct statfs fsd;
-
-       if (statfs (path, &fsd, sizeof (struct statfs)) != 0)
-               return -1;
-#endif /* STAT_STATFS3_OSF1 */
-       
-#ifdef STAT_STATFS2_FS_DATA    /* Ultrix */
-#define CONVERT_BLOCKS(B) adjust_blocks ((B), 1024, 512)       
-       struct fs_data fsd;
-       
-       if (statfs (path, &fsd) != 1)
-               return -1;
-       
-       (*dsize) = CONVERT_BLOCKS (fsd.fd_req.btot);
-       (*dfree) = CONVERT_BLOCKS (fsd.fd_req.bfreen);
-#endif /* STAT_STATFS2_FS_DATA */
-       
-#ifdef STAT_STATFS2_BSIZE      /* 4.3BSD, SunOS 4, HP-UX, AIX */
-#define CONVERT_BLOCKS(B) adjust_blocks ((B), fsd.f_bsize, 512)
-       struct statfs fsd;
-       
-       if (statfs (path, &fsd) < 0)
-               return -1;
-       
-#ifdef STATFS_TRUNCATES_BLOCK_COUNTS
-       /* In SunOS 4.1.2, 4.1.3, and 4.1.3_U1, the block counts in the
-          struct statfs are truncated to 2GB.  These conditions detect that
-          truncation, presumably without botching the 4.1.1 case, in which
-          the values are not truncated.  The correct counts are stored in
-          undocumented spare fields.  */
-       if (fsd.f_blocks == 0x1fffff && fsd.f_spare[0] > 0) {
-               fsd.f_blocks = fsd.f_spare[0];
-               fsd.f_bfree = fsd.f_spare[1];
-               fsd.f_bavail = fsd.f_spare[2];
-       }
-#endif /* STATFS_TRUNCATES_BLOCK_COUNTS */
-#endif /* STAT_STATFS2_BSIZE */
-       
-
-#ifdef STAT_STATFS2_FSIZE      /* 4.4BSD */
-#define CONVERT_BLOCKS(B) adjust_blocks ((B), fsd.f_fsize, 512)
-       
-       struct statfs fsd;
-       
-       if (statfs (path, &fsd) < 0)
-               return -1;
-#endif /* STAT_STATFS2_FSIZE */
-       
-#ifdef STAT_STATFS4            /* SVR3, Dynix, Irix, AIX */
-# if _AIX || defined(_CRAY)
-#  define CONVERT_BLOCKS(B) adjust_blocks ((B), fsd.f_bsize, 512)
-#  ifdef _CRAY
-#   define f_bavail f_bfree
-#  endif
-# else
-#  define CONVERT_BLOCKS(B) (B)
-#  ifndef _SEQUENT_            /* _SEQUENT_ is DYNIX/ptx */
-#   ifndef DOLPHIN             /* DOLPHIN 3.8.alfa/7.18 has f_bavail */
-#    define f_bavail f_bfree
-#   endif
-#  endif
-# endif
-       
-       struct statfs fsd;
-
-       if (statfs (path, &fsd, sizeof fsd, 0) < 0)
-               return -1;
-       /* Empirically, the block counts on most SVR3 and SVR3-derived
-          systems seem to always be in terms of 512-byte blocks,
-          no matter what value f_bsize has.  */
-
-#endif /* STAT_STATFS4 */
-
-#ifdef STAT_STATVFS            /* SVR4 */
-# define CONVERT_BLOCKS(B) \
-       adjust_blocks ((B), fsd.f_frsize ? fsd.f_frsize : fsd.f_bsize, 512)
-
-       struct statvfs fsd;
-
-       if (statvfs (path, &fsd) < 0)
-               return -1;
-       /* f_frsize isn't guaranteed to be supported.  */
-
-#endif /* STAT_STATVFS */
-
-#ifndef CONVERT_BLOCKS
-       /* we don't have any dfree code! */
-       return -1;
-#else
-#if !defined(STAT_STATFS2_FS_DATA)
-       /* !Ultrix */
-       (*dsize) = CONVERT_BLOCKS (fsd.f_blocks);
-       (*dfree) = CONVERT_BLOCKS (fsd.f_bavail);
-#endif /* not STAT_STATFS2_FS_DATA */
-#endif
-
-       return 0;
-}
 
 /****************************************************************************
-  return number of 1K blocks available on a path and total number 
+ Return number of 1K blocks available on a path and total number.
 ****************************************************************************/
-static int disk_free(char *path,int *bsize,int *dfree,int *dsize)
+
+SMB_BIG_UINT sys_disk_free(connection_struct *conn, const char *path, BOOL small_query, 
+                              SMB_BIG_UINT *bsize,SMB_BIG_UINT *dfree,SMB_BIG_UINT *dsize)
 {
-       int dfree_retval;
+       SMB_BIG_UINT dfree_retval;
+       SMB_BIG_UINT dfree_q = 0;
+       SMB_BIG_UINT bsize_q = 0;
+       SMB_BIG_UINT dsize_q = 0;
+       const char *dfree_command;
 
        (*dfree) = (*dsize) = 0;
        (*bsize) = 512;
 
-       fsusage(path, dfree, dsize);
+       /*
+        * If external disk calculation specified, use it.
+        */
+
+       dfree_command = lp_dfree_command(SNUM(conn));
+       if (dfree_command && *dfree_command) {
+               const char *p;
+               char **lines;
+               pstring syscmd;
+
+               slprintf(syscmd, sizeof(syscmd)-1, "%s %s", dfree_command, path);
+               DEBUG (3, ("disk_free: Running command %s\n", syscmd));
+
+               lines = file_lines_pload(syscmd, NULL);
+               if (lines) {
+                       char *line = lines[0];
+
+                       DEBUG (3, ("Read input from dfree, \"%s\"\n", line));
+
+                       *dsize = STR_TO_SMB_BIG_UINT(line, &p);
+                       while (p && *p && isspace(*p))
+                               p++;
+                       if (p && *p)
+                               *dfree = STR_TO_SMB_BIG_UINT(p, &p);
+                       while (p && *p && isspace(*p))
+                               p++;
+                       if (p && *p)
+                               *bsize = STR_TO_SMB_BIG_UINT(p, NULL);
+                       else
+                               *bsize = 1024;
+                       file_lines_free(lines);
+                       DEBUG (3, ("Parsed output of dfree, dsize=%u, dfree=%u, bsize=%u\n",
+                               (unsigned int)*dsize, (unsigned int)*dfree, (unsigned int)*bsize));
+
+                       if (!*dsize)
+                               *dsize = 2048;
+                       if (!*dfree)
+                               *dfree = 1024;
+               } else {
+                       DEBUG (0, ("disk_free: sys_popen() failed for command %s. Error was : %s\n",
+                               syscmd, strerror(errno) ));
+                       if (sys_fsusage(path, dfree, dsize) != 0) {
+                               DEBUG (0, ("disk_free: sys_fsusage() failed. Error was : %s\n",
+                                       strerror(errno) ));
+                               return (SMB_BIG_UINT)-1;
+                       }
+               }
+       } else {
+               if (sys_fsusage(path, dfree, dsize) != 0) {
+                       DEBUG (0, ("disk_free: sys_fsusage() failed. Error was : %s\n",
+                               strerror(errno) ));
+                       return (SMB_BIG_UINT)-1;
+               }
+       }
+
+       if (disk_quotas(path, &bsize_q, &dfree_q, &dsize_q)) {
+               (*bsize) = bsize_q;
+               (*dfree) = MIN(*dfree,dfree_q);
+               (*dsize) = MIN(*dsize,dsize_q);
+       }
 
+       /* FIXME : Any reason for this assumption ? */
        if (*bsize < 256) {
+               DEBUG(5,("disk_free:Warning: bsize == %d < 256 . Changing to assumed correct bsize = 512\n",(int)*bsize));
                *bsize = 512;
        }
 
@@ -208,7 +152,7 @@ static int disk_free(char *path,int *bsize,int *dfree,int *dsize)
                *dfree = MAX(1,*dfree);
        }
 
-       disk_norm(bsize,dfree,dsize);
+       disk_norm(small_query,bsize,dfree,dsize);
 
        if ((*bsize) < 1024) {
                dfree_retval = (*dfree)/(1024/(*bsize));
@@ -219,13 +163,54 @@ static int disk_free(char *path,int *bsize,int *dfree,int *dsize)
        return(dfree_retval);
 }
 
-
 /****************************************************************************
-wrap it to get filenames right
+ Potentially returned cached dfree info.
 ****************************************************************************/
-int sys_disk_free(char *path,int *bsize,int *dfree,int *dsize)
+
+SMB_BIG_UINT get_dfree_info(connection_struct *conn,
+                       const char *path,
+                       BOOL small_query,
+                       SMB_BIG_UINT *bsize,
+                       SMB_BIG_UINT *dfree,
+                       SMB_BIG_UINT *dsize)
 {
-       return(disk_free(dos_to_unix(path,False),bsize,dfree,dsize));
-}
+       int dfree_cache_time = lp_dfree_cache_time(SNUM(conn));
+       struct dfree_cached_info *dfc = conn->dfree_info;
+       SMB_BIG_UINT dfree_ret;
+
+       if (!dfree_cache_time) {
+               return SMB_VFS_DISK_FREE(conn,path,small_query,bsize,dfree,dsize);
+       }
 
+       if (dfc && (conn->lastused - dfc->last_dfree_time < dfree_cache_time)) {
+               /* Return cached info. */
+               *bsize = dfc->bsize;
+               *dfree = dfc->dfree;
+               *dsize = dfc->dsize;
+               return dfc->dfree_ret;
+       }
 
+       dfree_ret = SMB_VFS_DISK_FREE(conn,path,small_query,bsize,dfree,dsize);
+
+       if (dfree_ret == (SMB_BIG_UINT)-1) {
+               /* Don't cache bad data. */
+               return dfree_ret;
+       }
+
+       /* No cached info or time to refresh. */
+       if (!dfc) {
+               dfc = TALLOC_P(conn->mem_ctx, struct dfree_cached_info);
+               if (!dfc) {
+                       return dfree_ret;
+               }
+               conn->dfree_info = dfc;
+       }
+
+       dfc->bsize = *bsize;
+       dfc->dfree = *dfree;
+       dfc->dsize = *dsize;
+       dfc->dfree_ret = dfree_ret;
+       dfc->last_dfree_time = conn->lastused;
+
+       return dfree_ret;
+}