diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,17 @@
 # Revision history for libfuse3
 
+## 0.2.0.0 -- 2022-09-10
+
+### Breaking changes
+
+* Fixed the type of `FileStat.blockCount` from `CBlkSize` to `CBlkCnt`.
+
+### Other changes
+
+* Added support for `base-4.17.0.0` (ghc-9.4).
+* Added support for `unix-2.8.0.0`.
+* Removed a dependency to `linux-xattr` from the example `passthrough`, replacing with a hand-written implementation (#21).
+
 ## 0.1.2.1 -- 2022-05-20
 
 * Enabled build for Haskell Stack ([#16](https://github.com/matil019/haskell-libfuse3/pull/16), thanks to @modotte)
diff --git a/configure b/configure
--- a/configure
+++ b/configure
@@ -1,6 +1,6 @@
 #! /bin/sh
 # Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.71 for Haskell libfuse3 package 0.1.2.1.
+# Generated by GNU Autoconf 2.71 for Haskell libfuse3 package 0.2.0.0.
 #
 #
 # Copyright (C) 1992-1996, 1998-2017, 2020-2021 Free Software Foundation,
@@ -607,8 +607,8 @@
 # Identity of this package.
 PACKAGE_NAME='Haskell libfuse3 package'
 PACKAGE_TARNAME='haskell-libfuse3-package'
-PACKAGE_VERSION='0.1.2.1'
-PACKAGE_STRING='Haskell libfuse3 package 0.1.2.1'
+PACKAGE_VERSION='0.2.0.0'
+PACKAGE_STRING='Haskell libfuse3 package 0.2.0.0'
 PACKAGE_BUGREPORT=''
 PACKAGE_URL=''
 
@@ -1232,7 +1232,7 @@
   # Omit some internal or obsolete options to make the list less imposing.
   # This message is too long to be a string in the A/UX 3.1 sh.
   cat <<_ACEOF
-\`configure' configures Haskell libfuse3 package 0.1.2.1 to adapt to many kinds of systems.
+\`configure' configures Haskell libfuse3 package 0.2.0.0 to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -1295,7 +1295,7 @@
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of Haskell libfuse3 package 0.1.2.1:";;
+     short | recursive ) echo "Configuration of Haskell libfuse3 package 0.2.0.0:";;
    esac
   cat <<\_ACEOF
 
@@ -1383,7 +1383,7 @@
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
-Haskell libfuse3 package configure 0.1.2.1
+Haskell libfuse3 package configure 0.2.0.0
 generated by GNU Autoconf 2.71
 
 Copyright (C) 2021 Free Software Foundation, Inc.
@@ -1519,7 +1519,7 @@
 This file contains any messages produced by compilers while
 running configure, to aid debugging if configure makes a mistake.
 
-It was created by Haskell libfuse3 package $as_me 0.1.2.1, which was
+It was created by Haskell libfuse3 package $as_me 0.2.0.0, which was
 generated by GNU Autoconf 2.71.  Invocation command line was
 
   $ $0$ac_configure_args_raw
@@ -3916,7 +3916,7 @@
 # report actual input values of CONFIG_FILES etc. instead of their
 # values after options handling.
 ac_log="
-This file was extended by Haskell libfuse3 package $as_me 0.1.2.1, which was
+This file was extended by Haskell libfuse3 package $as_me 0.2.0.0, which was
 generated by GNU Autoconf 2.71.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
@@ -3980,7 +3980,7 @@
 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
 ac_cs_config='$ac_cs_config_escaped'
 ac_cs_version="\\
-Haskell libfuse3 package config.status 0.1.2.1
+Haskell libfuse3 package config.status 0.2.0.0
 configured by $0, generated by GNU Autoconf 2.71,
   with options \\"\$ac_cs_config\\"
 
diff --git a/configure.ac b/configure.ac
--- a/configure.ac
+++ b/configure.ac
@@ -1,4 +1,4 @@
-AC_INIT([Haskell libfuse3 package], [0.1.2.1])
+AC_INIT([Haskell libfuse3 package], [0.2.0.0])
 
 # Safety check: Ensure that we are in the correct source directory.
 AC_CONFIG_SRCDIR([libfuse3.cabal])
diff --git a/example/XAttr.hs b/example/XAttr.hs
new file mode 100644
--- /dev/null
+++ b/example/XAttr.hs
@@ -0,0 +1,74 @@
+{-# LANGUAGE CApiFFI #-}
+module XAttr where
+
+import Control.Monad (when)
+import Data.ByteString (ByteString)
+import Foreign (Ptr, allocaBytes, castPtr, nullPtr)
+import Foreign.C (CInt(CInt), CSize(CSize), CString, peekCStringLen, throwErrnoIfMinus1, throwErrnoIfMinus1_, withCString)
+import System.LibFuse3 (SetxattrFlag(SetxattrCreate, SetxattrDefault, SetxattrReplace))
+import System.Posix.Types (CSsize(CSsize))
+
+import qualified Data.ByteString as B
+import qualified Data.ByteString.Unsafe as BU
+
+foreign import ccall "lgetxattr"
+  c_lgetxattr :: CString -> CString -> Ptr () -> CSize -> IO CSsize
+
+foreign import ccall "llistxattr"
+  c_llistxattr :: CString -> CString -> CSize -> IO CSsize
+
+foreign import ccall "lremovexattr"
+  c_lremovexattr :: CString -> CString -> IO CInt
+
+foreign import ccall "lsetxattr"
+  c_lsetxattr :: CString -> CString -> Ptr () -> CSize -> CInt -> IO CInt
+
+foreign import capi "sys/xattr.h value XATTR_CREATE"
+  c_XATTR_CREATE :: CInt
+
+foreign import capi "sys/xattr.h value XATTR_REPLACE"
+  c_XATTR_REPLACE :: CInt
+
+get :: FilePath -> String -> IO ByteString
+get path name =
+  withCString path $ \cpath ->
+  withCString name $ \cname -> do
+    len <- throwErrnoIfMinus1 "lgetxattr" $ c_lgetxattr cpath cname nullPtr 0
+    allocaBytes (fromIntegral len) $ \cvalue -> do
+      len2 <- throwErrnoIfMinus1 "lgetxattr" $ c_lgetxattr cpath cname cvalue (fromIntegral len)
+      -- fail-fast for the sake of simplicity; serious code should retry C calls
+      when (len /= len2) $
+        fail "The size of the attribute value changed between lgetxattr calls"
+      B.packCStringLen (castPtr cvalue, fromIntegral len2)
+
+list :: FilePath -> IO [String]
+list path =
+  withCString path $ \cpath -> do
+    len <- throwErrnoIfMinus1 "llistxattr" $ c_llistxattr cpath nullPtr 0
+    allocaBytes (fromIntegral len) $ \cvalue -> do
+      len2 <- throwErrnoIfMinus1 "llistxattr" $ c_llistxattr cpath cvalue (fromIntegral len)
+      -- fail-fast for the sake of simplicity; serious code should retry C calls
+      when (len /= len2) $
+        fail "The size of the attribute value changed between llistxattr calls"
+      bs <- BU.unsafePackCStringLen (castPtr cvalue, fromIntegral len2)
+      -- use peekCStringLen to make sure we use the same encoding as withCString
+      traverse (\b -> BU.unsafeUseAsCStringLen b peekCStringLen) $ B.split 0
+        -- remove the last byte, which is NUL, to avoid returning an extra empty String
+        $ B.init bs
+
+remove :: FilePath -> String -> IO ()
+remove path name =
+  withCString path $ \cpath ->
+  withCString name $ \cname ->
+  throwErrnoIfMinus1_ "lremovexattr" $ c_lremovexattr cpath cname
+
+set :: FilePath -> String -> ByteString -> SetxattrFlag -> IO ()
+set path name value flags =
+  withCString path $ \cpath ->
+  withCString name $ \cname ->
+  BU.unsafeUseAsCStringLen value $ \(cvalue, csize) ->
+  throwErrnoIfMinus1_ "lsetxattr" $ c_lsetxattr cpath cname (castPtr cvalue) (fromIntegral csize) (toCFlags flags)
+  where
+  toCFlags SetxattrDefault = 0
+  toCFlags SetxattrCreate = c_XATTR_CREATE
+  toCFlags SetxattrReplace = c_XATTR_REPLACE
diff --git a/example/passthrough.hs b/example/passthrough.hs
--- a/example/passthrough.hs
+++ b/example/passthrough.hs
@@ -23,13 +23,13 @@
 import Foreign.C (CInt(CInt), CSize(CSize), CUInt(CUInt), Errno(Errno), eIO, eOK, eOPNOTSUPP)
 import System.IO (SeekMode, hPrint, stderr)
 import System.LibFuse3
-import System.Linux.XAttr (lCreateXAttr, lGetXAttr, lListXAttr, lRemoveXAttr, lReplaceXAttr, lSetXAttr)
 import System.Posix.Directory (closeDirStream, createDirectory, openDirStream, readDirStream, removeDirectory)
 import System.Posix.Files (createDevice, createLink, createNamedPipe, createSymbolicLink, readSymbolicLink, removeLink, rename, setFdSize, setFileCreationMask, setFileMode, setFileSize, setSymbolicLinkOwnerAndGroup, setSymbolicLinkTimesHiRes)
 import System.Posix.IO (OpenFileFlags, OpenMode(WriteOnly), closeFd, defaultFileFlags, exclusive, fdSeek, openFd)
 import System.Posix.Types (ByteCount, COff(COff), CSsize(CSsize), DeviceID, Fd(Fd), FileMode, FileOffset, GroupID, UserID)
 
 import qualified System.LibFuse3.FuseConfig as FuseConfig
+import qualified XAttr
 
 foreign import ccall "posix_fallocate"
   c_posix_fallocate :: CInt -> COff -> COff -> IO CInt
@@ -143,21 +143,16 @@
   Errno <$> c_posix_fallocate fd offset len
 
 xmpSetxattr :: FilePath -> String -> ByteString -> SetxattrFlag -> IO Errno
-xmpSetxattr path name value flag = tryErrno_ $ go path name value
-  where
-  go = case flag of
-    SetxattrDefault -> lSetXAttr
-    SetxattrCreate  -> lCreateXAttr
-    SetxattrReplace -> lReplaceXAttr
+xmpSetxattr path name value flag = tryErrno_ $ XAttr.set path name value flag
 
 xmpGetxattr :: FilePath -> String -> IO (Either Errno ByteString)
-xmpGetxattr path name = tryErrno $ lGetXAttr path name
+xmpGetxattr path name = tryErrno $ XAttr.get path name
 
 xmpListxattr :: FilePath -> IO (Either Errno [String])
-xmpListxattr path = tryErrno $ lListXAttr path
+xmpListxattr path = tryErrno $ XAttr.list path
 
 xmpRemovexattr :: FilePath -> String -> IO Errno
-xmpRemovexattr path name = tryErrno_ $ lRemoveXAttr path name
+xmpRemovexattr path name = tryErrno_ $ XAttr.remove path name
 
 xmpCopyFileRange :: Fd -> FileOffset -> Fd -> FileOffset -> ByteCount -> CUInt -> IO (Either Errno CSsize)
 xmpCopyFileRange (Fd fdIn) offIn (Fd fdOut) offOut len flags =
diff --git a/libfuse3.cabal b/libfuse3.cabal
--- a/libfuse3.cabal
+++ b/libfuse3.cabal
@@ -3,7 +3,7 @@
 -- For further documentation, see http://haskell.org/cabal/users-guide/
 
 name:                libfuse3
-version:             0.1.2.1
+version:             0.2.0.0
 synopsis:            A Haskell binding for libfuse-3.x
 description:         Bindings for libfuse, the FUSE userspace reference implementation, of version 3.x. Compatible with Linux.
 -- bug-reports:
@@ -41,13 +41,12 @@
                        System.LibFuse3.Utils
   -- other-modules:
   -- other-extensions:
-  build-depends:       base >=4.11 && <4.17
+  build-depends:       base >=4.11 && <4.18
                      , bytestring >=0.10.8 && <0.12
                      , clock ==0.8.*
                      , resourcet ==1.2.*
                      , time >=1.6 && <1.14
-                     -- TODO when upgrading `unix`, check whether System.LibFuse3.FileStat can be replaced with System.Posix.Files.FileStatus
-                     , unix ==2.7.*
+                     , unix >=2.7 && <2.9
   pkgconfig-depends:   fuse3
   hs-source-dirs:      src
   include-dirs:        include
@@ -116,13 +115,13 @@
     build-depends:       libfuse3, base
                        , bytestring
                        , clock
-                       , linux-xattr ==0.1.*
                        , time
                        , unix
   else
     buildable: False
   main-is:             passthrough.hs
   other-modules:       CLoff
+                       XAttr
   -- other-extensions:
   hs-source-dirs:      example
   default-language:    Haskell2010
diff --git a/src/System/LibFuse3/FileStat.hsc b/src/System/LibFuse3/FileStat.hsc
--- a/src/System/LibFuse3/FileStat.hsc
+++ b/src/System/LibFuse3/FileStat.hsc
@@ -10,7 +10,7 @@
 import System.Posix.Error (throwErrnoPathIfMinus1Retry_)
 import System.Posix.Internals (c_fstat, lstat, withFilePath)
 import System.Posix.Types
-  ( CBlkSize
+  ( CBlkCnt
   , DeviceID
   , Fd(Fd)
   , FileID
@@ -31,6 +31,8 @@
 --
 --   - Has an extra field `blockCount`.
 --
+--       - An equivalent accessor @fileBlocks@ was added in unix-2.8.0.0, but it is a @Maybe@.
+--
 --   - Provides an exact representation (`TimeSpec`) of the time fields without converting to `Date.Time.Clock.POSIX.POSIXTime`.
 --
 --       - This assumes that the @struct stat@ has @st_atim@, @st_mtim@ and @st_ctim@ fields.
@@ -59,7 +61,7 @@
   , -- | Total size, in bytes. @st_size@
     fileSize :: FileOffset
   , -- | Number of 512B blocks allocated. @st_blocks@
-    blockCount :: CBlkSize -- see also: https://github.com/haskell/unix/pull/78/files
+    blockCount :: CBlkCnt
   -- these assumes Linux >= 2.6
   , -- | Time of last access. @st_atim@
     accessTimeHiRes :: TimeSpec
diff --git a/src/System/LibFuse3/Internal.hsc b/src/System/LibFuse3/Internal.hsc
--- a/src/System/LibFuse3/Internal.hsc
+++ b/src/System/LibFuse3/Internal.hsc
@@ -50,7 +50,7 @@
 import System.LibFuse3.Utils (pokeCStringLen0, testBitSet, unErrno)
 import System.Posix.Directory (changeWorkingDirectory)
 import System.Posix.Files (blockSpecialMode, characterSpecialMode, directoryMode, namedPipeMode, regularFileMode, socketMode, symbolicLinkMode)
-import System.Posix.IO (OpenFileFlags(OpenFileFlags), OpenMode(ReadOnly, ReadWrite, WriteOnly))
+import System.Posix.IO (OpenFileFlags, OpenMode(ReadOnly, ReadWrite, WriteOnly), defaultFileFlags)
 import System.Posix.Internals (c_access, peekFilePath, withFilePath)
 import System.Posix.Process (createSession)
 import System.Posix.Types (ByteCount, COff(COff), CSsize, DeviceID, FileMode, FileOffset, GroupID, UserID)
@@ -495,12 +495,10 @@
   peekOpenFileFlagsAndMode :: Ptr C.FuseFileInfo -> IO (OpenFileFlags, OpenMode)
   peekOpenFileFlagsAndMode pFuseFileInfo = do
     (flags :: CInt) <- (#peek struct fuse_file_info, flags) pFuseFileInfo
-    let openFileFlags = OpenFileFlags
-          { append   = testBitSet flags (#const O_APPEND)
-          , nonBlock = testBitSet flags (#const O_NONBLOCK)
-          , trunc    = testBitSet flags (#const O_TRUNC)
-          , exclusive = False
-          , noctty    = False
+    let openFileFlags = defaultFileFlags
+          { System.Posix.IO.append   = testBitSet flags (#const O_APPEND)
+          , System.Posix.IO.nonBlock = testBitSet flags (#const O_NONBLOCK)
+          , System.Posix.IO.trunc    = testBitSet flags (#const O_TRUNC)
           }
         openMode
           | testBitSet flags (#const O_RDWR)   = ReadWrite
diff --git a/src/System/LibFuse3/Internal/Resource.hs b/src/System/LibFuse3/Internal/Resource.hs
--- a/src/System/LibFuse3/Internal/Resource.hs
+++ b/src/System/LibFuse3/Internal/Resource.hs
@@ -27,7 +27,9 @@
         throwIO e
       stateCleanupChecked Nothing istate
     -- cleanup actions are discarded because the child will run them
-    exitImmediately ExitSuccess
+    _ <- exitImmediately ExitSuccess
+    -- this @undefined@ is required because in unix<2.8 @exitImmediately@ returns @IO ()@
+    -- instead of @IO a@
     undefined
 
 -- | `callocBytes` with `free` associated as a cleanup action.
