packages feed

linux-file-extents (empty) → 0.1.0.0

raw patch · 4 files changed

+306/−0 lines, 4 filesdep +basedep +unixsetup-changed

Dependencies added: base, unix

Files

+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2014, Marios Titas++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++    * 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.++    * Neither the name of Marios Titas nor the names of other+      contributors may be used to endorse or promote products derived+      from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT+OWNER 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.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ System/Linux/FileExtents.hsc view
@@ -0,0 +1,249 @@+------------------------------------------------------------------------------+-- |+-- Module      : System.Linux.FileExtents+--+-- Stability   : provisional+-- Portability : non-portable (requires Linux)+--+-- This module can be used to retrieve information about how a+-- particular file is stored on disk (i.e. the file fragmentation).+-- It accomplishes that by directly calling the FIEMAP ioctl provided by+-- recent versions of the Linux kernel. This ioctl is specific to Linux+-- and therefore this module is not portable.+--+-- For more information about the FIEMAP ioctl see @filesystems/fiemap.txt@+-- in the kernel documentation.+--+------------------------------------------------------------------------------+++module System.Linux.FileExtents+    ( -- * Extent flags+      -- |See @filesystems/fiemap.txt@ in the kernel documentation for a more+      -- detailed description of each of these flags.+      ExtentFlags+    , efLast+    , efUnknown+    , efDelalloc+    , efEncoded+    , efDataEncrypted+    , efNotAligned+    , efDataInline+    , efDataTail+    , efUnwritten+    , efMerged+    , efShared+    -- * Extents+    , Extent(..)+    -- * Request flags+    , Flags(..)+    , defaultFlags+    -- * Getting extent information+    , getExtentsFd+    , getExtents+    , getExtentCountFd+    , getExtentCount+    ) where++import Control.Monad+import Control.Exception+import Data.Maybe++import Foreign hiding (void)+import Foreign.C+import System.Posix.Types+import System.Posix.IO++#include <sys/ioctl.h>+#include <linux/fs.h>+#include <linux/fiemap.h>++--------------------------------------------------------------------------------+-- extent flags++type ExtentFlags = Word32++-- |Last extent in file.+efLast          :: ExtentFlags+efLast          = #const FIEMAP_EXTENT_LAST++-- |Data location unknown.+efUnknown       :: ExtentFlags+efUnknown       = #const FIEMAP_EXTENT_UNKNOWN++-- |Location still pending.+efDelalloc      :: ExtentFlags+efDelalloc      = #const FIEMAP_EXTENT_DELALLOC++-- |Data cannot be read while fs is unmounted.+efEncoded       :: ExtentFlags+efEncoded       = #const FIEMAP_EXTENT_ENCODED++-- |Data is encrypted by fs.+efDataEncrypted :: ExtentFlags+efDataEncrypted = #const FIEMAP_EXTENT_DATA_ENCRYPTED++-- |Extent offsets may not be block aligned.+efNotAligned    :: ExtentFlags+efNotAligned    = #const FIEMAP_EXTENT_NOT_ALIGNED++-- |Data mixed with metadata.+efDataInline    :: ExtentFlags+efDataInline    = #const FIEMAP_EXTENT_DATA_INLINE++-- |Multiple files in block.+efDataTail      :: ExtentFlags+efDataTail      = #const FIEMAP_EXTENT_DATA_TAIL++-- |Space allocated, but no data (i.e. zero).+efUnwritten     :: ExtentFlags+efUnwritten     = #const FIEMAP_EXTENT_UNWRITTEN++-- |File does not natively support extents. Result merged for efficiency.+efMerged        :: ExtentFlags+efMerged        = #const FIEMAP_EXTENT_MERGED++-- |Space shared with other files.+efShared        :: ExtentFlags+efShared        = #const FIEMAP_EXTENT_SHARED++--------------------------------------------------------------------------------+-- extent type++-- |Description of a single extent. All offsets and lengths are in bytes.+data Extent = Extent+    { extLogical :: Word64    -- ^ Offset relative to the beginning of the file.+    , extPhysical :: Word64   -- ^ Offset relative to the beginning of the underlying block device.+    , extLength :: Word64     -- ^ The length of the extent.+    , extFlags :: ExtentFlags -- ^ Flags for this extent.+    }+  deriving (Show, Eq)++instance Storable Extent where+    sizeOf _ = #size struct fiemap_extent+    alignment _ = alignment (undefined :: Int)+    peek ptr = do+        extLogical_  <- (#peek struct fiemap_extent, fe_logical ) ptr+        extPhysical_ <- (#peek struct fiemap_extent, fe_physical) ptr+        extLength_   <- (#peek struct fiemap_extent, fe_length  ) ptr+        extFlags_    <- (#peek struct fiemap_extent, fe_flags   ) ptr+        return (Extent extLogical_ extPhysical_ extLength_ extFlags_)+    poke ptr ext = do+        memset (castPtr ptr) 0 (#size struct fiemap_extent)+        (#poke struct fiemap_extent, fe_logical ) ptr (extLogical ext)+        (#poke struct fiemap_extent, fe_physical) ptr (extPhysical ext)+        (#poke struct fiemap_extent, fe_length  ) ptr (extLength ext)+        (#poke struct fiemap_extent, fe_flags   ) ptr (extFlags ext)++--------------------------------------------------------------------------------+-- request flags++-- |Request flags.+data Flags = Flags+    { fSync :: Bool  -- ^ Sync the file before requesting its extents.+    , fXattr :: Bool -- ^ Retrieve the extents of the inode's extended attribute lookup tree, instead of its data tree.+    }+  deriving (Show, Eq)++-- |Default values for the request flags. Both 'fSync' and 'fXattr' are set+-- to False.+defaultFlags :: Flags+defaultFlags = Flags False False++encodeFlags :: Flags -> Word32+encodeFlags f =+    (if fSync f then (#const FIEMAP_FLAG_SYNC) else 0)+      .|.+    (if fXattr f then (#const FIEMAP_FLAG_XATTR) else 0)++--------------------------------------------------------------------------------+-- get extents++-- | Retrieve the list of all extents associated with the file+-- referenced by the file descriptor. Extents returned mirror those on disk+-- - that is, the logical offset of the first returned extent may start+-- before the requested range, and the last returned extent may end after+-- the end of the requested range.+--+-- Note: 'getExtentsFd' might call the FIEMAP ioctl multiple times in order to+-- retrieve all the extents of the file. This is necessary when the file+-- has too many fragments. If the file is modified in the meantime, the+-- returned list might be inconsistent.+getExtentsFd+    :: Flags+    -> Fd+    -> Maybe (Word64, Word64) -- ^ The range (offset and length) within the file to look extents for. Use 'Nothing' for the entire file.+    -> IO [Extent]+getExtentsFd flags (Fd fd) range =+    allocaBytes allocSize $ \fiemap -> do+        let (start, len) = fromMaybe (0, maxBound) range+        memset (castPtr fiemap) 0 (#size struct fiemap)+        l <- getExtentsFd' start len fiemap+        return (concat l)+  where+    getExtentsFd' start len fiemap = do+        (#poke struct fiemap, fm_start       ) fiemap start+        (#poke struct fiemap, fm_length      ) fiemap len+        (#poke struct fiemap, fm_flags       ) fiemap flags'+        (#poke struct fiemap, fm_extent_count) fiemap maxExtentCount+        throwErrnoIfMinus1_ "getExtentsFd" $ ioctl fd (#const FS_IOC_FIEMAP) fiemap+        mappedExtents <- (#peek struct fiemap, fm_mapped_extents) fiemap :: IO Word32+        let extentsPtr = fiemap `plusPtr` (#offset struct fiemap, fm_extents)+        extents <- peekArray (fromIntegral mappedExtents) extentsPtr+        case extents of+            (_ : _) | mappedExtents == maxExtentCount+                    , lExt <- last extents+                    , lExtEnd <- extLogical lExt + extLength lExt+                    , bytesLeft <- start + len - lExtEnd+                    , bytesLeft > 0 -> do+                more <- getExtentsFd' lExtEnd bytesLeft fiemap+                return (extents : more)+            _ -> return [extents]+    flags' = encodeFlags flags+    maxExtentCount :: Word32+    maxExtentCount = (fromIntegral allocSize - (#size struct fiemap)) `quot` (#size struct fiemap_extent);+    allocSize = 16 * 1024++-- |Like 'getExtentsFd' except that it operates on file paths instead of+-- file descriptors.+getExtents :: Flags -> FilePath -> Maybe (Word64, Word64) -> IO [Extent]+getExtents flags path range = do+    bracket (openFd path ReadOnly Nothing defaultFileFlags) closeFd $ \fd ->+        getExtentsFd flags fd range++--------------------------------------------------------------------------------+-- get extent count++-- |Like 'getExtentsFd' except that it returns the number of extents+-- instead of a list.+getExtentCountFd :: Flags -> Fd -> Maybe (Word64, Word64) -> IO Word32+getExtentCountFd flags (Fd fd) range = do+    let (start, len) = fromMaybe (0, maxBound) range+    allocaBytes (#size struct fiemap) $ \fiemap -> do+        memset (castPtr fiemap) 0 (#size struct fiemap)+        (#poke struct fiemap, fm_start       ) fiemap start+        (#poke struct fiemap, fm_length      ) fiemap len+        (#poke struct fiemap, fm_flags       ) fiemap flags'+        (#poke struct fiemap, fm_extent_count) fiemap (0 :: Word32)+        throwErrnoIfMinus1_ "getExtentCountFd" $ ioctl fd (#const FS_IOC_FIEMAP) fiemap+        #{peek struct fiemap, fm_mapped_extents} fiemap+  where+    flags' = encodeFlags flags++-- |Like 'getExtents' except that it returns the number of extents+-- instead of a list.+getExtentCount :: Flags -> FilePath -> Maybe (Word64, Word64) -> IO Word32+getExtentCount flags path range = do+    bracket (openFd path ReadOnly Nothing defaultFileFlags) closeFd $ \fd ->+        getExtentCountFd flags fd range++--------------------------------------------------------------------------------+-- auxiliary stuff++foreign import ccall unsafe ioctl :: CInt -> CULong -> Ptr a -> IO CInt++foreign import ccall unsafe "string.h memset"+    c_memset :: Ptr a -> CInt -> CSize -> IO (Ptr a)++memset :: Ptr a -> Word8 -> CSize -> IO ()+memset p b l = void $ c_memset p (fromIntegral b) l
+ linux-file-extents.cabal view
@@ -0,0 +1,25 @@+name:                linux-file-extents+version:             0.1.0.0+synopsis:            Retrieve file fragmentation information under Linux+description:+  This package provides a simple wrapper around the Linux FIEMAP ioctl.+  It can be used to retrieve a complete list of all the extents of any given+  file, i.e. the area of the disk where the file is actually stored. This is+  similar to the @filefrag@ command line tool provided by the @e2fsprogs@+  package.+homepage:            https://github.com/redneb/linux-file-extents+bug-reports:         https://github.com/redneb/linux-file-extents/issues+license:             BSD3+license-file:        LICENSE+author:              Marios Titas <rednebΑΤgmxDΟΤcom>+maintainer:          Marios Titas <rednebΑΤgmxDΟΤcom>+category:            System, Filesystem, FFI+build-type:          Simple+cabal-version:       >=1.10++library+  exposed-modules:     System.Linux.FileExtents+  build-depends:       base >=4.6 && <5, unix >=2.6+  build-tools:         hsc2hs+  default-language:    Haskell2010+  ghc-options:         -Wall