diff --git a/ChangeLog b/ChangeLog
new file mode 100644
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1,3 @@
+v0.1.1.0
+
+	* Support defraging file ranges.
diff --git a/System/Linux/Btrfs.hsc b/System/Linux/Btrfs.hsc
--- a/System/Linux/Btrfs.hsc
+++ b/System/Linux/Btrfs.hsc
@@ -11,6 +11,7 @@
 -}
 
 {-# LANGUAGE CPP #-}
+{-# LANGUAGE RecordWildCards #-}
 
 #if BTRFS_RAW_PATHS
 ##define FILEPATH RawFilePath
@@ -21,7 +22,7 @@
 #endif
     (
     -- * Basic types
-      FileSize, ObjectType, ObjectId, InodeNum, SubvolId
+      FileSize, ObjectType, ObjectId, InodeNum, SubvolId, CompressionType(..)
     -- * File cloning
     , cloneFd, clone, cloneNew
     , cloneRangeFd, cloneRange
@@ -47,6 +48,8 @@
     , getSubvolByReceivedUuidFd, getSubvolByReceivedUuid
     -- * Defragging
     , defragFd, defrag
+    , DefragRangeArgs(..), defaultDefragRangeArgs
+    , defragRangeFd, defragRange
     -- * Sync
     , syncFd, sync
     , startSyncFd, startSync
@@ -90,9 +93,9 @@
 import System.Linux.Btrfs.Time
 import System.Linux.Btrfs.UUID
 
-#include <linux/btrfs.h>
-#define __IOCTL_
+#include <btrfs/ioctl.h>
 #include <btrfs/ctree.h>
+#include <missing.h>
 
 #include <linux/fs.h>
 
@@ -111,6 +114,9 @@
 
 type SubvolId = ObjectId
 
+data CompressionType = Zlib | LZO
+    deriving (Show, Read, Eq, Enum, Bounded)
+
 --------------------------------------------------------------------------------
 
 cloneFd :: Fd -> Fd -> IO ()
@@ -325,7 +331,7 @@
 snapshot
     :: FILEPATH -- ^ The source subvolume.
     -> FILEPATH -- ^ The destination subvolume (must not exist).
-    -> Bool     -- ^ Make the subvolume read-only?
+    -> Bool     -- ^ Create a read-only snapshot?
     -> IO ()
 snapshot srcPath dstPath readOnly =
     withFd srcPath ReadOnly $ \srcFd ->
@@ -680,6 +686,62 @@
 defrag :: FILEPATH -> IO ()
 defrag path = withFd path ReadWrite defragFd
 
+-- | Argument to the 'defragRange' operation.
+data DefragRangeArgs = DefragRangeArgs
+    { draStart :: FileSize
+        -- ^ Beginning of the defrag range.
+    , draLength :: FileSize
+        -- ^ Number of bytes to defrag, use 'maxBound' to say all.
+    , draExtentThreshold :: Word32
+        -- ^ Any extent bigger than this size will be considered already
+        -- defragged. Use 0 to take the kernel default, use 1 to say every
+        -- single extent must be rewritten.
+    , draCompress :: Maybe CompressionType
+        -- ^ Compress the file while defragmenting.
+    , draFlush :: Bool
+        -- ^ Flush data to disk immediately after defragmenting.
+    }
+
+-- | Defaults for 'defragRange'. Selects the entire file, no compression,
+-- and no flushing.
+defaultDefragRangeArgs :: DefragRangeArgs
+defaultDefragRangeArgs = DefragRangeArgs
+    { draStart = 0
+    , draLength = maxBound
+    , draExtentThreshold = 0
+    , draCompress = Nothing
+    , draFlush = False
+    }
+
+defragRangeFd :: Fd -> DefragRangeArgs -> IO ()
+defragRangeFd fd DefragRangeArgs{..} =
+    allocaBytesZero (#size struct btrfs_ioctl_defrag_range_args) $ \args -> do
+        (#poke struct btrfs_ioctl_defrag_range_args, start        ) args draStart
+        (#poke struct btrfs_ioctl_defrag_range_args, len          ) args draLength
+        (#poke struct btrfs_ioctl_defrag_range_args, flags        ) args flags
+        (#poke struct btrfs_ioctl_defrag_range_args, extent_thresh) args draExtentThreshold
+        (#poke struct btrfs_ioctl_defrag_range_args, compress_type) args comp_type
+        throwErrnoIfMinus1_ "defragRangeFd" $
+            withBlockSIGVTALRM $ -- this is probably a bad idea
+                ioctl fd (#const BTRFS_IOC_DEFRAG_RANGE) args
+  where
+    flags = comp_flags .|. if draFlush then (#const BTRFS_DEFRAG_RANGE_START_IO) else 0
+    comp_flags :: Word64
+    comp_type :: Word32
+    (comp_flags, comp_type) =
+        case draCompress of
+            Nothing -> (0, 0)
+            Just Zlib -> ((#const BTRFS_DEFRAG_RANGE_COMPRESS), (#const BTRFS_COMPRESS_ZLIB))
+            Just LZO  -> ((#const BTRFS_DEFRAG_RANGE_COMPRESS), (#const BTRFS_COMPRESS_LZO))
+
+-- | Defrag a range within a single file.
+--
+-- Note: calls the @BTRFS_IOC_DEFRAG_RANGE@ @ioctl@.
+defragRange :: FILEPATH -> DefragRangeArgs -> IO ()
+defragRange path args =
+    withFd path ReadWrite $ \fd ->
+        defragRangeFd fd args
+
 --------------------------------------------------------------------------------
 
 syncFd :: Fd -> IO ()
@@ -772,7 +834,7 @@
     fspathSize = 2 * 1024 + (#size struct btrfs_data_container)
 
 -- | Find the file path(s) given an inode number. Returns a list of file paths
--- and an integer indicating the number of path found but not included in
+-- and an integer indicating the number of paths found but not included in
 -- the resulting list. This is because of a limitation in the kernel (it
 -- will not return an arbitrarily large list). The paths returned are
 -- relative to the root of the subvolume.
@@ -925,7 +987,7 @@
         else do
             let shPtr' = itemPtr `plusPtr` fromIntegral (shLen sh)
             loopItems shPtr' (itemsFound - 1)
-    -- items are index by keys which are (objectId, iType, offset)
+    -- items are indexed by keys which are (objectId, iType, offset)
     -- they are returned in lexicographical order wrt the keys
     nextKey (objectId, iType, offset)
         | offset   < maxBound         = Just (objectId, iType, offset + 1)
diff --git a/System/Linux/Btrfs/ByteString.hsc b/System/Linux/Btrfs/ByteString.hsc
--- a/System/Linux/Btrfs/ByteString.hsc
+++ b/System/Linux/Btrfs/ByteString.hsc
@@ -11,6 +11,7 @@
 -}
 
 {-# LANGUAGE CPP #-}
+{-# LANGUAGE RecordWildCards #-}
 
 #if BTRFS_RAW_PATHS
 ##define FILEPATH RawFilePath
@@ -21,7 +22,7 @@
 #endif
     (
     -- * Basic types
-      FileSize, ObjectType, ObjectId, InodeNum, SubvolId
+      FileSize, ObjectType, ObjectId, InodeNum, SubvolId, CompressionType(..)
     -- * File cloning
     , cloneFd, clone, cloneNew
     , cloneRangeFd, cloneRange
@@ -47,6 +48,8 @@
     , getSubvolByReceivedUuidFd, getSubvolByReceivedUuid
     -- * Defragging
     , defragFd, defrag
+    , DefragRangeArgs(..), defaultDefragRangeArgs
+    , defragRangeFd, defragRange
     -- * Sync
     , syncFd, sync
     , startSyncFd, startSync
@@ -90,9 +93,9 @@
 import System.Linux.Btrfs.Time
 import System.Linux.Btrfs.UUID
 
-#include <linux/btrfs.h>
-#define __IOCTL_
+#include <btrfs/ioctl.h>
 #include <btrfs/ctree.h>
+#include <missing.h>
 
 #include <linux/fs.h>
 
@@ -111,6 +114,9 @@
 
 type SubvolId = ObjectId
 
+data CompressionType = Zlib | LZO
+    deriving (Show, Read, Eq, Enum, Bounded)
+
 --------------------------------------------------------------------------------
 
 cloneFd :: Fd -> Fd -> IO ()
@@ -325,7 +331,7 @@
 snapshot
     :: FILEPATH -- ^ The source subvolume.
     -> FILEPATH -- ^ The destination subvolume (must not exist).
-    -> Bool     -- ^ Make the subvolume read-only?
+    -> Bool     -- ^ Create a read-only snapshot?
     -> IO ()
 snapshot srcPath dstPath readOnly =
     withFd srcPath ReadOnly $ \srcFd ->
@@ -680,6 +686,62 @@
 defrag :: FILEPATH -> IO ()
 defrag path = withFd path ReadWrite defragFd
 
+-- | Argument to the 'defragRange' operation.
+data DefragRangeArgs = DefragRangeArgs
+    { draStart :: FileSize
+        -- ^ Beginning of the defrag range.
+    , draLength :: FileSize
+        -- ^ Number of bytes to defrag, use 'maxBound' to say all.
+    , draExtentThreshold :: Word32
+        -- ^ Any extent bigger than this size will be considered already
+        -- defragged. Use 0 to take the kernel default, use 1 to say every
+        -- single extent must be rewritten.
+    , draCompress :: Maybe CompressionType
+        -- ^ Compress the file while defragmenting.
+    , draFlush :: Bool
+        -- ^ Flush data to disk immediately after defragmenting.
+    }
+
+-- | Defaults for 'defragRange'. Selects the entire file, no compression,
+-- and no flushing.
+defaultDefragRangeArgs :: DefragRangeArgs
+defaultDefragRangeArgs = DefragRangeArgs
+    { draStart = 0
+    , draLength = maxBound
+    , draExtentThreshold = 0
+    , draCompress = Nothing
+    , draFlush = False
+    }
+
+defragRangeFd :: Fd -> DefragRangeArgs -> IO ()
+defragRangeFd fd DefragRangeArgs{..} =
+    allocaBytesZero (#size struct btrfs_ioctl_defrag_range_args) $ \args -> do
+        (#poke struct btrfs_ioctl_defrag_range_args, start        ) args draStart
+        (#poke struct btrfs_ioctl_defrag_range_args, len          ) args draLength
+        (#poke struct btrfs_ioctl_defrag_range_args, flags        ) args flags
+        (#poke struct btrfs_ioctl_defrag_range_args, extent_thresh) args draExtentThreshold
+        (#poke struct btrfs_ioctl_defrag_range_args, compress_type) args comp_type
+        throwErrnoIfMinus1_ "defragRangeFd" $
+            withBlockSIGVTALRM $ -- this is probably a bad idea
+                ioctl fd (#const BTRFS_IOC_DEFRAG_RANGE) args
+  where
+    flags = comp_flags .|. if draFlush then (#const BTRFS_DEFRAG_RANGE_START_IO) else 0
+    comp_flags :: Word64
+    comp_type :: Word32
+    (comp_flags, comp_type) =
+        case draCompress of
+            Nothing -> (0, 0)
+            Just Zlib -> ((#const BTRFS_DEFRAG_RANGE_COMPRESS), (#const BTRFS_COMPRESS_ZLIB))
+            Just LZO  -> ((#const BTRFS_DEFRAG_RANGE_COMPRESS), (#const BTRFS_COMPRESS_LZO))
+
+-- | Defrag a range within a single file.
+--
+-- Note: calls the @BTRFS_IOC_DEFRAG_RANGE@ @ioctl@.
+defragRange :: FILEPATH -> DefragRangeArgs -> IO ()
+defragRange path args =
+    withFd path ReadWrite $ \fd ->
+        defragRangeFd fd args
+
 --------------------------------------------------------------------------------
 
 syncFd :: Fd -> IO ()
@@ -772,7 +834,7 @@
     fspathSize = 2 * 1024 + (#size struct btrfs_data_container)
 
 -- | Find the file path(s) given an inode number. Returns a list of file paths
--- and an integer indicating the number of path found but not included in
+-- and an integer indicating the number of paths found but not included in
 -- the resulting list. This is because of a limitation in the kernel (it
 -- will not return an arbitrarily large list). The paths returned are
 -- relative to the root of the subvolume.
@@ -925,7 +987,7 @@
         else do
             let shPtr' = itemPtr `plusPtr` fromIntegral (shLen sh)
             loopItems shPtr' (itemsFound - 1)
-    -- items are index by keys which are (objectId, iType, offset)
+    -- items are indexed by keys which are (objectId, iType, offset)
     -- they are returned in lexicographical order wrt the keys
     nextKey (objectId, iType, offset)
         | offset   < maxBound         = Just (objectId, iType, offset + 1)
diff --git a/System/Linux/Btrfs/UUID.hs b/System/Linux/Btrfs/UUID.hs
--- a/System/Linux/Btrfs/UUID.hs
+++ b/System/Linux/Btrfs/UUID.hs
@@ -13,7 +13,6 @@
 import Foreign.Ptr (castPtr)
 import Foreign.C.Types (CInt)
 
--- | A @UUID@ is stored as two big-endian 'Word64's.
 data UUID = UUID Word64 Word64
   deriving (Eq, Ord)
 
@@ -22,6 +21,7 @@
         showParen (p > 9) $
             showString "fromString " . shows (toString u)
 
+-- | A @UUID@ is stored as two big-endian 'Word64's.
 instance Storable UUID where
     sizeOf _ = 16
     alignment _ = alignment (undefined :: CInt)
diff --git a/btrfs.cabal b/btrfs.cabal
--- a/btrfs.cabal
+++ b/btrfs.cabal
@@ -1,5 +1,5 @@
 name:                btrfs
-version:             0.1.0.1
+version:             0.1.1.0
 synopsis:            Bindings to the btrfs API
 description:
   This package provides low-level bindings to the btrfs API (i.e. the
@@ -7,7 +7,7 @@
   supported, including functions needed to work with subvolumes/snapshots
   as well as file cloning.
   .
-  In order to build this package, you need to @linux-headers 3.9@ or newer.
+  In order to build this package, @linux-headers@ need to be installed.
   .
   Warning: btrfs is still considered experimental. This module is also
   experimental and may contain serious bugs that may result in data loss.
@@ -23,6 +23,8 @@
 cabal-version:       >=1.10
 
 extra-source-files:
+  ChangeLog
+  include/missing.h
   include/btrfs/ctree.h
   include/btrfs/extent-cache.h
   include/btrfs/extent_io.h
diff --git a/include/btrfs/ctree.h b/include/btrfs/ctree.h
--- a/include/btrfs/ctree.h
+++ b/include/btrfs/ctree.h
@@ -40,6 +40,8 @@
 struct btrfs_free_space_ctl;
 #define BTRFS_MAGIC 0x4D5F53665248425FULL /* ascii _BHRfS_M, no null */
 
+#define BTRFS_MAX_MIRRORS 3
+
 #define BTRFS_MAX_LEVEL 8
 
 #define BTRFS_COMPAT_EXTENT_TREE_V0
@@ -943,6 +945,7 @@
 	struct btrfs_root *chunk_root;
 	struct btrfs_root *dev_root;
 	struct btrfs_root *csum_root;
+	struct btrfs_root *quota_root;
 
 	struct rb_root fs_root_tree;
 
@@ -988,6 +991,7 @@
 	unsigned int readonly:1;
 	unsigned int on_restoring:1;
 	unsigned int is_chunk_recover:1;
+	unsigned int quota_enabled:1;
 
 	int (*free_extent_hook)(struct btrfs_trans_handle *trans,
 				struct btrfs_root *root,
@@ -2382,4 +2386,12 @@
 int btrfs_lookup_uuid_subvol_item(int fd, const u8 *uuid, u64 *subvol_id);
 int btrfs_lookup_uuid_received_subvol_item(int fd, const u8 *uuid,
 					   u64 *subvol_id);
+
+static inline int is_fstree(u64 rootid)
+{
+	if (rootid == BTRFS_FS_TREE_OBJECTID ||
+	    (signed long long)rootid >= (signed long long)BTRFS_FIRST_FREE_OBJECTID)
+		return 1;
+	return 0;
+}
 #endif
diff --git a/include/btrfs/ioctl.h b/include/btrfs/ioctl.h
--- a/include/btrfs/ioctl.h
+++ b/include/btrfs/ioctl.h
@@ -194,7 +194,9 @@
 
 	__u64 flags;
 
-	__u64 unused[8];
+	__u64 limit;
+
+	__u64 unused[7];
 } __attribute__ ((__packed__));
 
 struct btrfs_balance_progress {
diff --git a/include/btrfs/kerncompat.h b/include/btrfs/kerncompat.h
--- a/include/btrfs/kerncompat.h
+++ b/include/btrfs/kerncompat.h
@@ -28,7 +28,11 @@
 #include <assert.h>
 #include <stddef.h>
 #include <linux/types.h>
+#include <stdint.h>
 
+#define ptr_to_u64(x)	((u64)(uintptr_t)x)
+#define u64_to_ptr(x)	((void *)(uintptr_t)x)
+
 #ifndef READ
 #define READ 0
 #define WRITE 1
@@ -235,7 +239,7 @@
 
 #define BUG_ON(c) assert(!(c))
 #define WARN_ON(c) assert(!(c))
-
+#define	ASSERT(c) assert(c)
 
 #define container_of(ptr, type, member) ({                      \
         const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
diff --git a/include/missing.h b/include/missing.h
new file mode 100644
--- /dev/null
+++ b/include/missing.h
@@ -0,0 +1,48 @@
+/*
+ * Unfortunately, btrfs/ioctl.h (from btrfs-progs) and linux/btrfs.h (from
+ * linux-headers) have some differences. We put here some parts that are
+ * missing * from btrfs/ioctl.h.
+ */
+
+#ifndef MISSING_H
+#define MISSING_H
+#include <linux/types.h>
+#include <linux/ioctl.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define BTRFS_SAME_DATA_DIFFERS	1
+
+struct btrfs_ioctl_same_extent_info {
+	__s64 fd;		/* in - destination file */
+	__u64 logical_offset;	/* in - start of extent in destination */
+	__u64 bytes_deduped;	/* out - total # of bytes we were able
+				 * to dedupe from this file */
+	/* status of this dedupe operation:
+	 * 0 if dedup succeeds
+	 * < 0 for error
+	 * == BTRFS_SAME_DATA_DIFFERS if data differs
+	 */
+	__s32 status;		/* out - see above description */
+	__u32 reserved;
+};
+
+struct btrfs_ioctl_same_args {
+	__u64 logical_offset;	/* in - start of extent in source */
+	__u64 length;		/* in - length of extent */
+	__u16 dest_count;	/* in - total elements in info array */
+	__u16 reserved1;
+	__u32 reserved2;
+	struct btrfs_ioctl_same_extent_info info[0];
+};
+
+#define BTRFS_IOC_FILE_EXTENT_SAME _IOWR(BTRFS_IOCTL_MAGIC, 54, \
+					 struct btrfs_ioctl_same_args)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/make-bytestring.hs b/make-bytestring.hs
--- a/make-bytestring.hs
+++ b/make-bytestring.hs
@@ -13,6 +13,8 @@
     withFile "System/Linux/Btrfs/ByteString.hsc" WriteMode $ \oHdl -> do
         hPutStrLn oHdl "#define BTRFS_RAW_PATHS 1"
         withFile "System/Linux/Btrfs.hsc" ReadMode $ \iHdl -> do
-            _ <- hGetLine iHdl -- skip the first line
+            s <- hGetLine iHdl -- skip the first line
+            unless (s == "#define BTRFS_RAW_PATHS 0") $
+                fail "the first line does not have the expected contents"
             contents <- hGetContents iHdl
             hPutStr oHdl contents
