From ea5113cf0c44b5616f5eef1f11fdb2bc1ef0e7ec Mon Sep 17 00:00:00 2001 From: Yu-zh Date: Tue, 30 Mar 2021 01:07:30 -0400 Subject: [PATCH 1/3] api: add zooGetAcl --- cbits/hs_zk.c | 9 ++++++++ include/hs_zk.h | 4 ++++ src/ZooKeeper.hs | 39 ++++++++++++++++++++++++++++++++++ src/ZooKeeper/Internal/FFI.hsc | 18 ++++++++++++++++ test/Spec.hs | 7 ++++++ 5 files changed, 77 insertions(+) diff --git a/cbits/hs_zk.c b/cbits/hs_zk.c index 5b36a89..376bef6 100644 --- a/cbits/hs_zk.c +++ b/cbits/hs_zk.c @@ -370,6 +370,15 @@ int hs_zoo_aget_acl(zhandle_t* zh, const char* path, HsStablePtr mvar, return zoo_aget_acl(zh, path, hs_acl_completion_fn, completion); } +int hs_zoo_aset_acl(zhandle_t* zh, const char* path, int version, + struct ACL_vector* acl, HsStablePtr mvar, + HsInt cap, hs_void_completion_t* completion) { + completion->mvar = mvar; + completion->cap = cap; + return zoo_aset_acl(zh, path, version, acl, hs_void_completion_fn, + completion); +} + int hs_zoo_amulti(zhandle_t* zh, int count, const zoo_op_t* ops, zoo_op_result_t* results, HsStablePtr mvar, HsInt cap, hs_void_completion_t* void_completion) { diff --git a/include/hs_zk.h b/include/hs_zk.h index 2a20a30..a7c51a5 100644 --- a/include/hs_zk.h +++ b/include/hs_zk.h @@ -126,6 +126,10 @@ zhandle_t* hs_zookeeper_init(HsStablePtr mvar, HsInt cap, int hs_zoo_aget_acl(zhandle_t* zh, const char* path, HsStablePtr mvar, HsInt cap, hs_acl_completion_t* completion); +int hs_zoo_aset_acl(zhandle_t* zh, const char* path, int version, + struct ACL_vector* acl, HsStablePtr mvar, HsInt cap, + hs_void_completion_t* completion); + int hs_zoo_acreate(zhandle_t* zh, const char* path, const char* value, HsInt offset, HsInt valuelen, const struct ACL_vector* acl, int mode, HsStablePtr mvar, HsInt cap, diff --git a/src/ZooKeeper.hs b/src/ZooKeeper.hs index 77a47b4..42d9ada 100644 --- a/src/ZooKeeper.hs +++ b/src/ZooKeeper.hs @@ -18,6 +18,7 @@ module ZooKeeper , zooExists , zooWatchExists , zooGetAcl + , zooSetAcl , zooMulti , zooCreateOpInit @@ -488,6 +489,44 @@ zooGetAcl zh path = CBytes.withCBytesUnsafe path $ \path' -> do cfunc = I.c_hs_zoo_aget_acl zh path' in E.throwZooErrorIfLeft =<< I.withZKAsync csize I.peekRet I.peekData cfunc +-- | Sets the acl associated with a node. +-- +-- Throw one of the following exceptions on failure: +-- +-- ZBADARGUMENTS - invalid input parameters +-- ZINVALIDSTATE - zhandle state is either ZOO_SESSION_EXPIRED_STATE or ZOO_AUTH_FAILED_STATE +-- ZMARSHALLINGERROR - failed to marshall a request; possibly, out of memory +zooSetAcl + :: HasCallStack + => T.ZHandle + -- ^ The zookeeper handle obtained by a call to 'zookeeperResInit' + -> CBytes + -- ^ The name of the node. Expressed as a file name with slashes + -- separating ancestors of the node. + -> Maybe T.AclVector + -- ^ The acl to be set on the path + -> Maybe CInt + -- ^ The expected version of the path + -> IO T.VoidCompletion + -- ^ The result when the request completes + -- + -- Throw one of the following exceptions if the request completes failed: + -- + -- * ZNONODE the node does not exist. + -- * ZNOAUTH the client does not have permission. + -- * ZINVALIDACL invalid ACL specified + -- * ZBADVERSION expected version does not match actual version. +zooSetAcl zh path m_acl m_version = CBytes.withCBytesUnsafe path $ \path' -> do + let csize = I.csize @T.VoidCompletion + version = fromMaybe (-1) m_version + case m_acl of + Just acl -> do + let cfunc = I.c_hs_zoo_aset_acl zh path' version acl + E.throwZooErrorIfLeft =<< I.withZKAsync csize I.peekRet I.peekData cfunc + Nothing -> do + let cfunc = I.c_hs_zoo_aset_acl' zh path' version nullPtr + E.throwZooErrorIfLeft =<< I.withZKAsync csize I.peekRet I.peekData cfunc + ------------------------------------------------------------------------------- -- | Atomically commits multiple zookeeper operations. diff --git a/src/ZooKeeper/Internal/FFI.hsc b/src/ZooKeeper/Internal/FFI.hsc index e0bb07b..ba3d232 100644 --- a/src/ZooKeeper/Internal/FFI.hsc +++ b/src/ZooKeeper/Internal/FFI.hsc @@ -71,6 +71,24 @@ foreign import ccall unsafe "hs_zk.h hs_zoo_aget_acl" -> StablePtr PrimMVar -> Int -> Ptr AclCompletion -> IO CInt +foreign import ccall unsafe "hs_zk.h hs_zoo_aset_acl" + c_hs_zoo_aset_acl + :: ZHandle + -> BA## Word8 + -> CInt + -> AclVector + -> StablePtr PrimMVar -> Int -> Ptr VoidCompletion + -> IO CInt + +foreign import ccall unsafe "hs_zk.h hs_zoo_aset_acl" + c_hs_zoo_aset_acl' + :: ZHandle + -> BA## Word8 + -> CInt + -> Ptr CChar + -> StablePtr PrimMVar -> Int -> Ptr VoidCompletion + -> IO CInt + foreign import ccall unsafe "hs_zk.h hs_zoo_acreate" c_hs_zoo_acreate :: ZHandle diff --git a/test/Spec.hs b/test/Spec.hs index 0685b15..0000a8c 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -86,6 +86,13 @@ opSpec zh = do compactZooPerms . aclPerms . head . aclCompletionAcls <$> zooGetAcl zh "/acl2" `shouldReturn` ZooPermAll + describe "ZooKeeper.zooSetAcl" $ do + it "set acl permission" $ do + void $ zooCreate zh "/acl3" Nothing zooOpenAclUnsafe ZooEphemeral + void $ zooSetAcl zh "/acl3" (Just zooReadAclUnsafe) Nothing + compactZooPerms . aclPerms . head . aclCompletionAcls <$> zooGetAcl zh "/acl3" + `shouldReturn` ZooPermRead + propSpec :: ZHandle -> Spec propSpec zh = do describe "ZooKeeper.zooState" $ do From 23a4c29670d8bb453a1e3a7b7b25314c9c7a83f8 Mon Sep 17 00:00:00 2001 From: Yu-zh Date: Tue, 6 Apr 2021 14:54:05 -0400 Subject: [PATCH 2/3] allocating memory for acl vectors --- src/ZooKeeper.hs | 45 ++++++++++++++++++++++++++++++-- src/ZooKeeper/Internal/Types.hsc | 9 ++++--- src/ZooKeeper/Types.hs | 2 ++ 3 files changed, 51 insertions(+), 5 deletions(-) diff --git a/src/ZooKeeper.hs b/src/ZooKeeper.hs index 42d9ada..fe320bf 100644 --- a/src/ZooKeeper.hs +++ b/src/ZooKeeper.hs @@ -19,6 +19,8 @@ module ZooKeeper , zooWatchExists , zooGetAcl , zooSetAcl + , unsafeAllocaZooAcl + , fromAclList , zooMulti , zooCreateOpInit @@ -38,13 +40,14 @@ module ZooKeeper import Control.Concurrent (forkIO, myThreadId, newEmptyMVar, takeMVar, threadCapability) import Control.Exception (mask_, onException) -import Control.Monad (void, when, zipWithM, (<=<)) +import Control.Monad (void, when, zipWithM, (<=<), forM_) import Data.Bifunctor (first) import Data.Maybe (fromMaybe) import Foreign.C (CInt) import Foreign.ForeignPtr (mallocForeignPtrBytes, touchForeignPtr, withForeignPtr) import Foreign.Ptr (Ptr, nullPtr, plusPtr) +import Foreign (Word8, Storable(pokeByteOff, peekByteOff, sizeOf, peek)) import GHC.Conc (newStablePtrPrimMVar) import GHC.Stack (HasCallStack, callStack) import Z.Data.CBytes (CBytes) @@ -527,6 +530,44 @@ zooSetAcl zh path m_acl m_version = CBytes.withCBytesUnsafe path $ \path' -> do let cfunc = I.c_hs_zoo_aset_acl' zh path' version nullPtr E.throwZooErrorIfLeft =<< I.withZKAsync csize I.peekRet I.peekData cfunc +unsafeAllocaZooAcl :: I.ZooAcl -> IO Z.ByteArray +unsafeAllocaZooAcl (I.ZooAcl perms scheme id) = do + mba <- Z.newPinnedByteArray I.sizeOfZooAcl + mba_scheme@(Z.MutableByteArray mba_scheme#) <- Z.newPinnedByteArray (CBytes.length scheme) + mba_id@(Z.MutableByteArray mba_id#) <- Z.newPinnedByteArray (CBytes.length id) + let ptr_scheme = Z.mutableByteArrayContents mba_scheme + ptr_id = Z.mutableByteArrayContents mba_id + -- ptr = Z.mutableByteArrayContents mba + _ <- CBytes.pokeMBACBytes mba_scheme# 0 scheme + _ <- CBytes.pokeMBACBytes mba_id# 0 id + _ <- Z.writeByteArray mba 0 $ I.fromZooPerms perms + _ <- Z.writeByteArray mba 1 ptr_scheme + -- equivalently, we could poke the pointer + -- _ <- pokeByteOff ptr 8 ptr_scheme + -- peek_scheme_ptr <- peekByteOff ptr 8 + -- peek_scheme <- CBytes.fromCString peek_scheme_ptr + -- print peek_scheme + _ <- Z.writeByteArray mba 2 ptr_id + Z.freezeByteArray mba 0 I.sizeOfZooAcl + +fromAclList :: [I.ZooAcl] -> IO I.AclVector +fromAclList acls = do + let len = length acls + mba_data@(Z.MutableByteArray mba_data#) <- Z.newPinnedByteArray (I.sizeOfZooAcl * len) + forM_ (zip [0..len-1] acls) $ \(idx, acl) -> do + ba_acl <- unsafeAllocaZooAcl acl + Z.copyByteArray mba_data (idx * I.sizeOfZooAcl) ba_acl 0 I.sizeOfZooAcl + -- let ptr_acl = Z.byteArrayContents ba_acl + -- _ <- Z.writeByteArray mba_data idx ptr_acl + return () + let ptr_data = Z.mutableByteArrayContents mba_data + mba@(Z.MutableByteArray mba#) <- Z.newPinnedByteArray (sizeOf len + sizeOf ptr_data) + Z.writeByteArray mba 0 len -- should be 8 bytes + -- writeByteArray calculates the offset based on the type of the elements to write instead of bytes + -- so we rely on the fact that pointers and integers have the same length + Z.writeByteArray mba 1 ptr_data + return . I.AclVector . Z.castPtr . Z.mutableByteArrayContents $ mba + ------------------------------------------------------------------------------- -- | Atomically commits multiple zookeeper operations. @@ -538,7 +579,7 @@ zooMulti => T.ZHandle -- ^ The zookeeper handle obtained by a call to 'zookeeperResInit' -> [T.ZooOp] - -- ^ An list of operations to commit + -- ^ A list of operations to commit -> IO [T.ZooOpResult] zooMulti zh ops = do let len = length ops diff --git a/src/ZooKeeper/Internal/Types.hsc b/src/ZooKeeper/Internal/Types.hsc index 76e0b7b..19df89c 100644 --- a/src/ZooKeeper/Internal/Types.hsc +++ b/src/ZooKeeper/Internal/Types.hsc @@ -88,7 +88,7 @@ data ZooAcl = ZooAcl { aclPerms :: [ZooPerm] , aclIdScheme :: CBytes , aclId :: CBytes - } deriving Show + } deriving (Show, Eq) {-# INLINE sizeOfZooAcl #-} sizeOfZooAcl :: Int @@ -99,8 +99,11 @@ peekZooAcl ptr = do perms <- toZooPerms <$> (#peek acl_t, perms) ptr scheme_ptr <- (#peek acl_t, id.scheme) ptr id_ptr <- (#peek acl_t, id.id) ptr - scheme <- CBytes.fromCString scheme_ptr <* free scheme_ptr - acl_id <- CBytes.fromCString id_ptr <* free id_ptr + -- scheme <- CBytes.fromCString scheme_ptr <* free scheme_ptr + -- acl_id <- CBytes.fromCString id_ptr <* free id_ptr + -- we can't do the free for some reason I don't know + scheme <- CBytes.fromCString scheme_ptr + acl_id <- CBytes.fromCString id_ptr return $ ZooAcl perms scheme acl_id -- TODO diff --git a/src/ZooKeeper/Types.hs b/src/ZooKeeper/Types.hs index 43813a5..f6b0f05 100644 --- a/src/ZooKeeper/Types.hs +++ b/src/ZooKeeper/Types.hs @@ -10,6 +10,8 @@ module ZooKeeper.Types , I.zooReadAclUnsafe , I.zooCreatorAllAcl , I.ZooAcl (..) + , I.toAclList + , I.peekZooAcl , I.HsWatcherCtx (..) From 74c7defe78811ec05ae094669de11ee71623bffd Mon Sep 17 00:00:00 2001 From: Yu-zh Date: Tue, 6 Apr 2021 18:54:34 -0400 Subject: [PATCH 3/3] added a simple test --- src/ZooKeeper.hs | 43 +++++++++++++------------------- src/ZooKeeper/Internal/Types.hsc | 8 +++--- test/Spec.hs | 11 ++++++++ 3 files changed, 33 insertions(+), 29 deletions(-) diff --git a/src/ZooKeeper.hs b/src/ZooKeeper.hs index fe320bf..5335bc9 100644 --- a/src/ZooKeeper.hs +++ b/src/ZooKeeper.hs @@ -47,7 +47,7 @@ import Foreign.C (CInt) import Foreign.ForeignPtr (mallocForeignPtrBytes, touchForeignPtr, withForeignPtr) import Foreign.Ptr (Ptr, nullPtr, plusPtr) -import Foreign (Word8, Storable(pokeByteOff, peekByteOff, sizeOf, peek)) +import Foreign (Storable(sizeOf)) import GHC.Conc (newStablePtrPrimMVar) import GHC.Stack (HasCallStack, callStack) import Z.Data.CBytes (CBytes) @@ -531,40 +531,33 @@ zooSetAcl zh path m_acl m_version = CBytes.withCBytesUnsafe path $ \path' -> do E.throwZooErrorIfLeft =<< I.withZKAsync csize I.peekRet I.peekData cfunc unsafeAllocaZooAcl :: I.ZooAcl -> IO Z.ByteArray -unsafeAllocaZooAcl (I.ZooAcl perms scheme id) = do +unsafeAllocaZooAcl (I.ZooAcl acl_perms acl_scheme acl_id) = do mba <- Z.newPinnedByteArray I.sizeOfZooAcl - mba_scheme@(Z.MutableByteArray mba_scheme#) <- Z.newPinnedByteArray (CBytes.length scheme) - mba_id@(Z.MutableByteArray mba_id#) <- Z.newPinnedByteArray (CBytes.length id) + mba_scheme@(Z.MutableByteArray mba_scheme#) <- Z.newPinnedByteArray (CBytes.length acl_scheme) + mba_id@(Z.MutableByteArray mba_id#) <- Z.newPinnedByteArray (CBytes.length acl_id) let ptr_scheme = Z.mutableByteArrayContents mba_scheme ptr_id = Z.mutableByteArrayContents mba_id - -- ptr = Z.mutableByteArrayContents mba - _ <- CBytes.pokeMBACBytes mba_scheme# 0 scheme - _ <- CBytes.pokeMBACBytes mba_id# 0 id - _ <- Z.writeByteArray mba 0 $ I.fromZooPerms perms - _ <- Z.writeByteArray mba 1 ptr_scheme - -- equivalently, we could poke the pointer - -- _ <- pokeByteOff ptr 8 ptr_scheme - -- peek_scheme_ptr <- peekByteOff ptr 8 - -- peek_scheme <- CBytes.fromCString peek_scheme_ptr - -- print peek_scheme - _ <- Z.writeByteArray mba 2 ptr_id + CBytes.pokeMBACBytes mba_scheme# 0 acl_scheme + CBytes.pokeMBACBytes mba_id# 0 acl_id + Z.writeByteArray mba 0 $ I.fromZooPerms acl_perms + Z.writeByteArray mba 1 ptr_scheme + Z.writeByteArray mba 2 ptr_id Z.freezeByteArray mba 0 I.sizeOfZooAcl fromAclList :: [I.ZooAcl] -> IO I.AclVector fromAclList acls = do let len = length acls - mba_data@(Z.MutableByteArray mba_data#) <- Z.newPinnedByteArray (I.sizeOfZooAcl * len) - forM_ (zip [0..len-1] acls) $ \(idx, acl) -> do + mba_data <- Z.newPinnedByteArray (I.sizeOfZooAcl * len) + forM_ (zip [0..] acls) $ \(idx, acl) -> do ba_acl <- unsafeAllocaZooAcl acl - Z.copyByteArray mba_data (idx * I.sizeOfZooAcl) ba_acl 0 I.sizeOfZooAcl - -- let ptr_acl = Z.byteArrayContents ba_acl - -- _ <- Z.writeByteArray mba_data idx ptr_acl - return () + Z.copyByteArray mba_data (idx * I.sizeOfZooAcl) ba_acl 0 I.sizeOfZooAcl let ptr_data = Z.mutableByteArrayContents mba_data - mba@(Z.MutableByteArray mba#) <- Z.newPinnedByteArray (sizeOf len + sizeOf ptr_data) - Z.writeByteArray mba 0 len -- should be 8 bytes - -- writeByteArray calculates the offset based on the type of the elements to write instead of bytes - -- so we rely on the fact that pointers and integers have the same length + -- sizeOf len = 8 and sizeOf ptr_data = 8 + mba <- Z.newPinnedByteArray (sizeOf len + sizeOf ptr_data) + Z.writeByteArray mba 0 len + -- writeByteArray calculates the offset based on the type of the elements to + -- write instead of bytes. So we rely on the fact that pointers and integers + -- have the same length Z.writeByteArray mba 1 ptr_data return . I.AclVector . Z.castPtr . Z.mutableByteArrayContents $ mba diff --git a/src/ZooKeeper/Internal/Types.hsc b/src/ZooKeeper/Internal/Types.hsc index 19df89c..b7fe172 100644 --- a/src/ZooKeeper/Internal/Types.hsc +++ b/src/ZooKeeper/Internal/Types.hsc @@ -107,8 +107,8 @@ peekZooAcl ptr = do return $ ZooAcl perms scheme acl_id -- TODO -unsafeAllocaZooAcl :: ZooAcl -> IO Z.ByteArray -unsafeAllocaZooAcl = undefined +-- unsafeAllocaZooAcl :: ZooAcl -> IO Z.ByteArray +-- unsafeAllocaZooAcl = undefined -- FIXME: consider this -- data AclVector = AclVector (Ptr ()) | AclList [ZooAcl] @@ -136,8 +136,8 @@ toAclList (AclVector ptr) = do peekZooAcl data_ptr' -- TODO -fromAclList :: [ZooAcl] -> IO AclVector -fromAclList = undefined +-- fromAclList :: [ZooAcl] -> IO AclVector +-- fromAclList = undefined ------------------------------------------------------------------------------- diff --git a/test/Spec.hs b/test/Spec.hs index 0000a8c..40fe565 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -22,6 +22,17 @@ main = withResource client $ \zh -> do hspec $ opSpec zh hspec $ multiOpSpec zh hspec $ propSpec zh + hspec aclSpec + +aclSpec :: Spec +aclSpec = do + describe "test acl memory allocation" $ + it "copy ZooAcl back and forth" $ do + let acl1 = ZooAcl [ZooPermRead, ZooPermWrite] "scheme1" "id1" + acl2 = ZooAcl [ZooPermCreate, ZooPermDelete] "scheme2" "id2" + acls = [acl1, acl2] + ptr <- fromAclList acls + toAclList ptr `shouldReturn` acls opSpec :: ZHandle -> Spec opSpec zh = do