Rewrote free disk space checking code

Moving the portability handling into a small C library cleans up things
a lot, avoiding the pain of unpacking structs from inside haskell code.
This commit is contained in:
Joey Hess 2012-03-22 17:09:54 -04:00
parent f1398b5583
commit e38a839a80
13 changed files with 124 additions and 237 deletions

View file

@ -36,7 +36,7 @@ import qualified Git
import qualified Annex import qualified Annex
import qualified Annex.Queue import qualified Annex.Queue
import qualified Annex.Branch import qualified Annex.Branch
import Utility.StatFS import Utility.DiskFree
import Utility.FileMode import Utility.FileMode
import qualified Utility.Url as Url import qualified Utility.Url as Url
import Types.Key import Types.Key
@ -44,7 +44,6 @@ import Utility.DataUnits
import Utility.CopyFile import Utility.CopyFile
import Config import Config
import Annex.Exception import Annex.Exception
import qualified Build.SysConfig
{- Checks if a given key's content is currently present. -} {- Checks if a given key's content is currently present. -}
inAnnex :: Key -> Annex Bool inAnnex :: Key -> Annex Bool
@ -176,22 +175,19 @@ checkDiskSpace = checkDiskSpace' 0
checkDiskSpace' :: Integer -> Key -> Annex () checkDiskSpace' :: Integer -> Key -> Annex ()
checkDiskSpace' adjustment key = do checkDiskSpace' adjustment key = do
reserve <- getDiskReserve True reserve <- getDiskReserve
stats <- inRepo $ getFileSystemStats .gitAnnexDir free <- inRepo $ getDiskFree . gitAnnexDir
case (cancheck, stats, keySize key) of case (free, keySize key) of
(False, _, _) -> return () (Just have, Just need) ->
(_, Nothing, _) -> return ()
(_, _, Nothing) -> return ()
(_, Just (FileSystemStats { fsStatBytesAvailable = have }), Just need) ->
when (need + reserve > have + adjustment) $ when (need + reserve > have + adjustment) $
needmorespace (need + reserve - have - adjustment) needmorespace (need + reserve - have - adjustment)
_ -> return ()
where where
needmorespace n = unlessM (Annex.getState Annex.force) $ needmorespace n = unlessM (Annex.getState Annex.force) $
error $ "not enough free space, need " ++ error $ "not enough free space, need " ++
roughSize storageUnits True n ++ roughSize storageUnits True n ++
" more" ++ forcemsg " more" ++ forcemsg
forcemsg = " (use --force to override this check or adjust annex.diskreserve)" forcemsg = " (use --force to override this check or adjust annex.diskreserve)"
cancheck = Build.SysConfig.statfs_sanity_checked == Just True
{- Moves a file into .git/annex/objects/ {- Moves a file into .git/annex/objects/
- -

View file

@ -10,12 +10,8 @@ import Control.Applicative
import Build.TestConfig import Build.TestConfig
import Utility.SafeCommand import Utility.SafeCommand
tests :: Bool -> [TestCase] tests :: [TestCase]
tests True = cabaltests ++ common tests =
tests False = common
common :: [TestCase]
common =
[ TestCase "version" getVersion [ TestCase "version" getVersion
, TestCase "git" $ requireCmd "git" "git --version >/dev/null" , TestCase "git" $ requireCmd "git" "git --version >/dev/null"
, TestCase "git version" getGitVersion , TestCase "git version" getGitVersion
@ -32,11 +28,6 @@ common =
, TestCase "ssh connection caching" getSshConnectionCaching , TestCase "ssh connection caching" getSshConnectionCaching
] ++ shaTestCases [1, 256, 512, 224, 384] ] ++ shaTestCases [1, 256, 512, 224, 384]
cabaltests :: [TestCase]
cabaltests =
[ TestCase "StatFS" testStatFSDummy
]
shaTestCases :: [Int] -> [TestCase] shaTestCases :: [Int] -> [TestCase]
shaTestCases l = map make l shaTestCases l = map make l
where make n = where make n =
@ -81,10 +72,6 @@ getSshConnectionCaching :: Test
getSshConnectionCaching = Config "sshconnectioncaching" . BoolConfig <$> getSshConnectionCaching = Config "sshconnectioncaching" . BoolConfig <$>
boolSystem "sh" [Param "-c", Param "ssh -o ControlPersist=yes -V >/dev/null 2>/dev/null"] boolSystem "sh" [Param "-c", Param "ssh -o ControlPersist=yes -V >/dev/null 2>/dev/null"]
testStatFSDummy :: Test
testStatFSDummy =
return $ Config "statfs_sanity_checked" $ MaybeBoolConfig Nothing
{- Set up cabal file with version. -} {- Set up cabal file with version. -}
cabalSetup :: IO () cabalSetup :: IO ()
cabalSetup = do cabalSetup = do

View file

@ -22,7 +22,7 @@ import qualified Git
import qualified Annex import qualified Annex
import Command import Command
import Utility.DataUnits import Utility.DataUnits
import Utility.StatFS import Utility.DiskFree
import Annex.Content import Annex.Content
import Types.Key import Types.Key
import Backend import Backend
@ -30,7 +30,6 @@ import Logs.UUID
import Logs.Trust import Logs.Trust
import Remote import Remote
import Config import Config
import qualified Build.SysConfig
-- a named computation that produces a statistic -- a named computation that produces a statistic
type Stat = StatState (Maybe (String, StatState String)) type Stat = StatState (Maybe (String, StatState String))
@ -173,19 +172,16 @@ bloom_info = stat "bloom filter size" $ json id $ do
disk_size :: Stat disk_size :: Stat
disk_size = stat "available local disk space" $ json id $ lift $ disk_size = stat "available local disk space" $ json id $ lift $
if Build.SysConfig.statfs_sanity_checked == Just True calcfree
then calcfree <$> getDiskReserve
<$> getDiskReserve False <*> inRepo (getDiskFree . gitAnnexDir)
<*> inRepo (getFileSystemStats . gitAnnexDir)
else return unknown
where where
calcfree reserve (Just (FileSystemStats { fsStatBytesAvailable = have })) = calcfree reserve (Just have) =
roughSize storageUnits True $ nonneg $ have - reserve roughSize storageUnits True $ nonneg $ have - reserve
calcfree _ _ = unknown calcfree _ _ = "unknown"
nonneg x nonneg x
| x >= 0 = x | x >= 0 = x
| otherwise = 0 | otherwise = 0
unknown = "unknown"
backend_usage :: Stat backend_usage :: Stat
backend_usage = stat "backend usage" $ nojson $ backend_usage = stat "backend usage" $ nojson $

View file

@ -12,7 +12,6 @@ import qualified Git
import qualified Git.Config import qualified Git.Config
import qualified Git.Command import qualified Git.Command
import qualified Annex import qualified Annex
import qualified Build.SysConfig
import Utility.DataUnits import Utility.DataUnits
type ConfigKey = String type ConfigKey = String
@ -92,19 +91,8 @@ getTrustLevel :: Git.Repo -> Annex (Maybe String)
getTrustLevel r = fromRepo $ Git.Config.getMaybe $ remoteConfig r "trustlevel" getTrustLevel r = fromRepo $ Git.Config.getMaybe $ remoteConfig r "trustlevel"
{- Gets annex.diskreserve setting. -} {- Gets annex.diskreserve setting. -}
getDiskReserve :: Bool -> Annex Integer getDiskReserve :: Annex Integer
getDiskReserve sanitycheck = do getDiskReserve = fromMaybe megabyte . readSize dataUnits
r <- getConfig "diskreserve" "" <$> getConfig "diskreserve" ""
when sanitycheck $ check r
return $ fromMaybe megabyte $ readSize dataUnits r
where where
megabyte = 1000000 megabyte = 1000000
check r
| not (null r) && not cancheck = do
unlessM (Annex.getState Annex.force) $
error $ "You have configured a diskreserve of "
++ r ++
" but disk space checking is not working"
return ()
| otherwise = return ()
cancheck = Build.SysConfig.statfs_sanity_checked == Just True

View file

@ -1,6 +1,6 @@
PREFIX=/usr PREFIX=/usr
IGNORE=-ignore-package monads-fd IGNORE=-ignore-package monads-fd
BASEFLAGS=-Wall $(IGNORE) -outputdir tmp BASEFLAGS=-Wall $(IGNORE) -outputdir tmp -IUtility
GHCFLAGS=-O2 $(BASEFLAGS) GHCFLAGS=-O2 $(BASEFLAGS)
ifdef PROFILE ifdef PROFILE
@ -11,7 +11,8 @@ GHCMAKE=ghc $(GHCFLAGS) --make
bins=git-annex bins=git-annex
mans=git-annex.1 git-annex-shell.1 mans=git-annex.1 git-annex-shell.1
sources=Build/SysConfig.hs Utility/StatFS.hs Utility/Touch.hs sources=Build/SysConfig.hs Utility/Touch.hs
clibs=Utility/diskfree.o
all=$(bins) $(mans) docs all=$(bins) $(mans) docs
@ -28,15 +29,16 @@ sources: $(sources)
fast: GHCFLAGS=$(BASEFLAGS) fast: GHCFLAGS=$(BASEFLAGS)
fast: $(bins) fast: $(bins)
Build/SysConfig.hs: configure.hs Build/TestConfig.hs Utility/StatFS.hs Build/SysConfig.hs: configure.hs Build/TestConfig.hs Build/Configure.hs
$(GHCMAKE) configure $(GHCMAKE) configure
./configure ./configure
%.hs: %.hsc %.hs: %.hsc
hsc2hs $< hsc2hs $<
$(bins): $(sources)
$(GHCMAKE) $@ git-annex: $(sources) $(clibs)
$(GHCMAKE) $@ $(clibs)
git-annex.1: doc/git-annex.mdwn git-annex.1: doc/git-annex.mdwn
./mdwn2man git-annex 1 doc/git-annex.mdwn > git-annex.1 ./mdwn2man git-annex 1 doc/git-annex.mdwn > git-annex.1
@ -92,7 +94,7 @@ docs: $(mans)
clean: clean:
rm -rf tmp $(bins) $(mans) test configure *.tix .hpc $(sources) \ rm -rf tmp $(bins) $(mans) test configure *.tix .hpc $(sources) \
doc/.ikiwiki html dist doc/.ikiwiki html dist $(clibs)
# Workaround for cabal sdist not running Setup hooks, so I cannot # Workaround for cabal sdist not running Setup hooks, so I cannot
# generate a file list there. # generate a file list there.

32
Utility/DiskFree.hs Normal file
View file

@ -0,0 +1,32 @@
{- disk free space checking
-
- Copyright 2012 Joey Hess <joey@kitenet.net>
-
- Licensed under the GNU GPL version 3 or higher.
-}
{-# LANGUAGE ForeignFunctionInterface #-}
module Utility.DiskFree ( getDiskFree ) where
import Common
import Foreign.C.Types
import Foreign.C.String
import Foreign.C.Error
foreign import ccall unsafe "diskfree.h diskfree" c_diskfree
:: CString -> IO CULLong
getDiskFree :: String -> IO (Maybe Integer)
getDiskFree path = withFilePath path $ \c_path -> do
free <- c_diskfree c_path
ifM (safeErrno <$> getErrno)
( return $ Just $ toInteger free
, do
Errno i <- getErrno
print i
return Nothing
)
where
safeErrno (Errno v) = v == 0

View file

@ -1,128 +0,0 @@
-----------------------------------------------------------------------------
-- |
--
-- (This code originally comes from xmobar)
--
-- Module : StatFS
-- Copyright : (c) Jose A Ortega Ruiz
-- License : BSD-3-clause
--
-- All rights reserved.
--
-- Redistribution and use in source and binary forms, with or without
-- modification, are permitted provided that the following conditions
-- are met:
--
-- 1. Redistributions of source code must retain the above copyright
-- notice, this list of conditions and the following disclaimer.
-- 2. Redistributions in binary form must reproduce the above copyright
-- notice, this list of conditions and the following disclaimer in the
-- documentation and/or other materials provided with the distribution.
-- 3. Neither the name of the author nor the names of his contributors
-- may be used to endorse or promote products derived from this software
-- without specific prior written permission.
--
-- THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
-- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-- ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
-- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-- SUCH DAMAGE.
--
-- Maintainer : Jose A Ortega Ruiz <jao@gnu.org>
-- Stability : unstable
-- Portability : unportable
--
-- A binding to C's statvfs(2)
--
-----------------------------------------------------------------------------
{-# LANGUAGE CPP, ForeignFunctionInterface, EmptyDataDecls #-}
module Utility.StatFS ( FileSystemStats(..), getFileSystemStats ) where
import Utility.FileSystemEncoding
import Foreign
import Foreign.C.Types
import Foreign.C.String
#if defined (__FreeBSD__) || defined (__FreeBSD_kernel__) || defined (__APPLE__)
# include <sys/param.h>
# include <sys/mount.h>
#else
#if defined (__linux__)
#include <sys/vfs.h>
#else
#define UNKNOWN
#endif
#endif
data FileSystemStats = FileSystemStats {
fsStatBlockSize :: Integer
-- ^ Optimal transfer block size.
, fsStatBlockCount :: Integer
-- ^ Total data blocks in file system.
, fsStatByteCount :: Integer
-- ^ Total bytes in file system.
, fsStatBytesFree :: Integer
-- ^ Free bytes in file system.
, fsStatBytesAvailable :: Integer
-- ^ Free bytes available to non-superusers.
, fsStatBytesUsed :: Integer
-- ^ Bytes used.
} deriving (Show, Eq)
data CStatfs
#ifdef UNKNOWN
#warning free space checking code not available for this OS
#else
#if defined(__APPLE__)
foreign import ccall unsafe "sys/mount.h statfs64"
#else
#if defined(__FreeBSD__) || defined (__FreeBSD_kernel__)
foreign import ccall unsafe "sys/mount.h statfs"
#else
foreign import ccall unsafe "sys/vfs.h statfs64"
#endif
#endif
c_statfs :: CString -> Ptr CStatfs -> IO CInt
#endif
toI :: CULong -> Integer
toI = toInteger
getFileSystemStats :: String -> IO (Maybe FileSystemStats)
getFileSystemStats path =
#ifdef UNKNOWN
return Nothing
#else
allocaBytes (#size struct statfs) $ \vfs ->
withFilePath path $ \cpath -> do
res <- c_statfs cpath vfs
if res == -1 then return Nothing
else do
bsize <- (#peek struct statfs, f_bsize) vfs
bcount <- (#peek struct statfs, f_blocks) vfs
bfree <- (#peek struct statfs, f_bfree) vfs
bavail <- (#peek struct statfs, f_bavail) vfs
let bpb = toI bsize
let stats = FileSystemStats
{ fsStatBlockSize = bpb
, fsStatBlockCount = toI bcount
, fsStatByteCount = toI bcount * bpb
, fsStatBytesFree = toI bfree * bpb
, fsStatBytesAvailable = toI bavail * bpb
, fsStatBytesUsed = toI (bcount - bfree) * bpb
}
if fsStatBlockCount stats == 0 || fsStatBlockSize stats == 0
then return Nothing
else return $ Just stats
#endif

61
Utility/diskfree.c Normal file
View file

@ -0,0 +1,61 @@
/* disk free space checking, C mini-library
*
* Copyright 2012 Joey Hess <joey@kitenet.net>
*
* Licensed under the GNU GPL version 3 or higher.
*/
/* Include appropriate headers for the OS, and define what will be used to
* check the free space. */
#if defined(__APPLE__)
# include <sys/param.h>
# include <sys/mount.h>
# define STATSTRUCT statfs
# define STATCALL statfs64
#else
#if defined (__FreeBSD__) || defined (__FreeBSD_kernel__)
# include <sys/param.h>
# include <sys/mount.h>
# define STATSTRUCT statfs
# define STATCALL statfs
#else
#if defined (__linux__)
# include <sys/statvfs.h>
# define STATSTRUCT statvfs
# define STATCALL statvfs
#else
# warning free space checking code not available for this OS
# define UNKNOWN
#endif
#endif
#endif
#include <errno.h>
/* Checks the amount of disk that is available to regular (non-root) users.
* (If there's an error, or this is not supported,
* returns 0 and sets errno to nonzero.)
*/
unsigned long long int diskfree(const char *path) {
#ifdef UNKNOWN
errno = 1;
return 0;
#else
unsigned long long int available, blocksize;
struct STATSTRUCT buf;
errno = 0;
if (STATCALL(path, &buf) != 0)
return 0; /* errno is set */
available = buf.f_bavail;
blocksize = buf.f_bsize;
return available * blocksize;
#endif
}
/*
main () {
printf("%lli\n", diskfree("."));
}
*/

1
Utility/diskfree.h Normal file
View file

@ -0,0 +1 @@
unsigned long long int diskfree(const char *path);

View file

@ -1,23 +1,6 @@
{- configure program -} {- configure program -}
import Data.Maybe import Build.Configure
import qualified Build.Configure as Configure
import Build.TestConfig
import Utility.StatFS
tests :: [TestCase]
tests = [ TestCase "StatFS" testStatFS
] ++ Configure.tests False
{- This test cannot be included in Build.Configure due to needing
- Utility/StatFS.hs to be built, which it is not when "cabal configure"
- is run. -}
testStatFS :: Test
testStatFS = do
s <- getFileSystemStats "."
return $ Config "statfs_sanity_checked" $
MaybeBoolConfig $ Just $ isJust s
main :: IO () main :: IO ()
main = Configure.run tests main = run tests

5
debian/changelog vendored
View file

@ -1,8 +1,7 @@
git-annex (3.20120316) UNRELEASED; urgency=low git-annex (3.20120316) UNRELEASED; urgency=low
* Improve detection of inability to check free disk space. * Rewrote free disk space checking code, moving the portability
* status: Prints available local disk space, or shows if git-annex handling into a small C library.
doesn't know.
-- Joey Hess <joeyh@debian.org> Wed, 21 Mar 2012 21:19:16 -0400 -- Joey Hess <joeyh@debian.org> Wed, 21 Mar 2012 21:19:16 -0400

30
debian/copyright vendored
View file

@ -7,33 +7,3 @@ License: GPL-3+
The full text of version 3 of the GPL is distributed as doc/GPL in The full text of version 3 of the GPL is distributed as doc/GPL in
this package's source, or in /usr/share/common-licenses/GPL-3 on this package's source, or in /usr/share/common-licenses/GPL-3 on
Debian systems. Debian systems.
Files: Utility/StatFS.hsc
Copyright: Jose A Ortega Ruiz <jao@gnu.org>
License: BSD-3-clause
-- All rights reserved.
--
-- Redistribution and use in source and binary forms, with or without
-- modification, are permitted provided that the following conditions
-- are met:
--
-- 1. Redistributions of source code must retain the above copyright
-- notice, this list of conditions and the following disclaimer.
-- 2. Redistributions in binary form must reproduce the above copyright
-- notice, this list of conditions and the following disclaimer in the
-- documentation and/or other materials provided with the distribution.
-- 3. Neither the name of the author nor the names of his contributors
-- may be used to endorse or promote products derived from this software
-- without specific prior written permission.
--
-- THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
-- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-- ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
-- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-- SUCH DAMAGE.

View file

@ -1,5 +1,5 @@
Name: git-annex Name: git-annex
Version: 3.20120315 Version: 3.20120316
Cabal-Version: >= 1.6 Cabal-Version: >= 1.6
License: GPL License: GPL
Maintainer: Joey Hess <joey@kitenet.net> Maintainer: Joey Hess <joey@kitenet.net>