File: DiskSpace.hsc

package info (click to toggle)
haskell-disk-free-space 0.1.0.1-9
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 56 kB
  • sloc: makefile: 4; haskell: 2
file content (92 lines) | stat: -rw-r--r-- 3,410 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
{-# LANGUAGE CPP #-}

{- |
Module      : System.DiskSpace

Stability   : provisional
Portability : portable
-}

module System.DiskSpace
    ( DiskUsage(..)
    , getDiskUsage
    , getAvailSpace
    ) where

#ifndef CABAL_OS_WINDOWS

import Foreign
import Foreign.C

#include <sys/statvfs.h>

foreign import ccall safe statvfs :: CString -> Ptr a -> IO CInt

type FsBlkCnt = #type fsblkcnt_t

getDiskUsage path =
    withCString path $ \cPath ->
        allocaBytes (#size struct statvfs) $ \stat -> do
            throwErrnoPathIfMinus1_ "getDiskUsage" path $ statvfs cPath stat
            bsize  <- (#peek struct statvfs, f_bsize ) stat :: IO CULong
            frsize <- (#peek struct statvfs, f_frsize) stat :: IO CULong
            blocks <- (#peek struct statvfs, f_blocks) stat :: IO FsBlkCnt
            bfree  <- (#peek struct statvfs, f_bfree ) stat :: IO FsBlkCnt
            bavail <- (#peek struct statvfs, f_bavail) stat :: IO FsBlkCnt
            let frsize' = fromIntegral frsize
            return DiskUsage
                { diskTotal = frsize' * fromIntegral blocks
                , diskFree  = frsize' * fromIntegral bfree
                , diskAvail = frsize' * fromIntegral bavail
                , blockSize = fromIntegral bsize
                }

#else

import System.Win32 (getDiskFreeSpace)

getDiskUsage path = do
    (lpSectorsPerCluster,
     lpBytesPerSector,
     lpNumberOfFreeClusters,
     lpTotalNumberOfClusters)
        <- getDiskFreeSpace (Just path)
    let bs = fromIntegral lpSectorsPerCluster * fromIntegral lpBytesPerSector
        bs' = fromIntegral bs
        to = bs' * fromIntegral lpTotalNumberOfClusters
        av = bs' * fromIntegral lpNumberOfFreeClusters
    return (DiskUsage to av av bs)

#endif

-- | Disk usage information. All fields are in bytes.
data DiskUsage = DiskUsage
    { diskTotal :: Integer -- ^ The total size of the file system.
    , diskFree  :: Integer -- ^ The amount of free space. You probably want to
                           --   use 'diskAvail' instead.
    , diskAvail :: Integer -- ^ The amount of space available to the user.
                           --   Might be less than 'diskFree'. On Windows,
                           --   this is always equal to 'diskFree'.
                           --   This is what most tools report as free
                           --   space (e.g. the unix @df@ tool).
    , blockSize :: Int     -- ^ The optimal block size for I/O in this volume.
                           --   Some operating systems report incorrect values
                           --   for this field.
    }
  deriving (Show, Eq)

-- | Retrieve disk usage information about a volume. The volume is
-- specified with the @FilePath@ argument. The path can refer to the root
-- directory or any other directory inside the volume.
-- Unix systems also accept arbitrary files, but this
-- does not work under Windows and therefore should be avoided if
-- portability is desired.
getDiskUsage :: FilePath -> IO DiskUsage

-- | A convenience function that directly returns the 'diskAvail' field from
-- the result of 'getDiskUsage'. If a large amount of data is to be written
-- in a directory, calling this function for that directory can be used to
-- determine whether the operation will fail because of insufficient disk
-- space.
getAvailSpace :: FilePath -> IO Integer
getAvailSpace = fmap diskAvail . getDiskUsage