From commits-return-6763-archive-asf-public=cust-asf.ponee.io@zookeeper.apache.org Mon Aug 6 14:13:36 2018 Return-Path: X-Original-To: archive-asf-public@cust-asf.ponee.io Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by mx-eu-01.ponee.io (Postfix) with SMTP id E86591807AC for ; Mon, 6 Aug 2018 14:13:32 +0200 (CEST) Received: (qmail 47882 invoked by uid 500); 6 Aug 2018 12:13:30 -0000 Mailing-List: contact commits-help@zookeeper.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@zookeeper.apache.org Delivered-To: mailing list commits@zookeeper.apache.org Received: (qmail 44796 invoked by uid 99); 6 Aug 2018 12:13:28 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 06 Aug 2018 12:13:28 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id 41804E0A96; Mon, 6 Aug 2018 12:13:27 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: andor@apache.org To: commits@zookeeper.apache.org Date: Mon, 06 Aug 2018 12:13:59 -0000 Message-Id: <5a18be0a07c94c57badc116e585149ee@git.apache.org> In-Reply-To: <13bf07ba1f73429dae991fd13f50d7a5@git.apache.org> References: <13bf07ba1f73429dae991fd13f50d7a5@git.apache.org> X-Mailer: ASF-Git Admin Mailer Subject: [34/45] zookeeper git commit: ZOOKEEPER-3030: MAVEN MIGRATION - Step 1.3 - move contrib directories http://git-wip-us.apache.org/repos/asf/zookeeper/blob/b0df8fe1/src/contrib/zkfuse/src/zkfuse.cc ---------------------------------------------------------------------- diff --git a/src/contrib/zkfuse/src/zkfuse.cc b/src/contrib/zkfuse/src/zkfuse.cc deleted file mode 100644 index 6a82168..0000000 --- a/src/contrib/zkfuse/src/zkfuse.cc +++ /dev/null @@ -1,4492 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define FUSE_USE_VERSION 26 - -#ifdef HAVE_CONFIG_H -#include -#endif - -#undef _GNU_SOURCE -#define _GNU_SOURCE - -extern "C" { -#include -#include -} -#include -#include -#include -#include -#include -#include -#include -#ifdef HAVE_SETXATTR -#include -#endif - -#include - -#include -#include -#include -#include -#include -#include - -#include "log.h" -#include "mutex.h" -#include "zkadapter.h" - -#define ZOOKEEPER_ROOT_CHILDREN_WATCH_BUG - -/** - Typedef for ZooKeeperAdapter::Data. -*/ -typedef std::string Data; -/** - Typedef for ZooKeeperAdapter::NodeNames. -*/ -typedef vector NodeNames; - -#define MAX_DATA_SIZE 1024; - -DEFINE_LOGGER(LOG, "zkfuse"); - -inline -uint64_t millisecsToSecs(uint64_t millisecs) -{ - return millisecs / 1000; -} -inline -uint64_t secsToMillisecs(uint64_t secs) -{ - return secs * 1000; -} -inline -uint64_t nanosecsToMillisecs(uint64_t nanosecs) -{ - return nanosecs / 1000000; -} -inline -uint64_t timespecToMillisecs(const struct timespec & ts) -{ - return secsToMillisecs(ts.tv_sec) + nanosecsToMillisecs(ts.tv_nsec); -} - -typedef boost::shared_ptr ZooKeeperAdapterSharedPtr; - -/** - * ZkFuseCommon - holds immutable configuration objects. - * - * No locks are required to access these objects. - * A ZkFuseCommon instance is considered to be a data object and may be copied. - */ -class ZkFuseCommon -{ - private: - /** - References the ZooKeeperAdapter instance to be used. - */ - ZooKeeperAdapterSharedPtr _zkAdapter; - /** - Path to the ZooKeeper root node. - */ - std::string _rootPathName; - /** - Name used to access data "file" when the ZK node has - children. - */ - std::string _dataFileName; - /** - Suffix added to path components to force interpretation of - path components as directory. This is usually only required - for the last component. For example, ZkFuse may consider - a leaf node a regular file, e.g. /a/b/c/leaf. The suffix - can be used to create child under this node, e.g. - mkdir /a/b/c/leaf{forceDirSuffix}/new_leaf. - */ - std::string _forceDirSuffix; - /** - Prefix common to all metadata nodes created by ZkFuse. - */ - std::string _metadataNamePrefix; - /** - Path component name that identifies a directory metadata node. - A directory metadata node is currently empty. It is used by ZkFuse - to create a child when mkdir is used. This prevents ZkFuse - from interpreting the new child as a regular file. - */ - std::string _dirMetadataName; - /** - Path component name that identifies a regular file metadata node. - A regular metadata node holds metadata required to implement - Posix regular file semantics, such as setting mtime. - */ - std::string _regMetadataName; - /** - Number of not-in-use nodes to cache. - */ - unsigned _cacheSize; - /** - Assume this userid owns all nodes. - */ - const uid_t _uid; - /** - Assume this groupid owns all nodes. - */ - const gid_t _gid; - /** - Blocksize used to calculate number of blocks used for stat. - */ - const unsigned _blkSize; - - public: - /** - Constructor. - */ - ZkFuseCommon() - : _zkAdapter(), - _rootPathName("/"), - _dataFileName(), - _forceDirSuffix(), - _metadataNamePrefix(".zkfuse."), - _dirMetadataName(_metadataNamePrefix + "dir"), - _regMetadataName(_metadataNamePrefix + "file"), - _cacheSize(256), - _uid(geteuid()), - _gid(getegid()), - _blkSize(8192) - { - } - /** - Get root path name. Always "/". - \see _rootPathName - */ - const std::string & getRootPathName() const - { - return _rootPathName; - } - /** - Get dataFileName - the name for synthesized files to access - ZooKeeper node data. - \see _dataFileName - */ - const std::string & getDataFileName() const - { - return _dataFileName; - } - /** - Set dataFileName. - \see getDataFileName - \see _dataFileName - */ - void setDataFileName(const std::string & dataFileName) - { - _dataFileName = dataFileName; - } - /** - Get metadataNamePrefix - the common prefix for all ZkFuse created - metadata ZooKeeper nodes. - \see _metadataNamePrefix - */ - const std::string & getMetadataNamePrefix() const - { - return _metadataNamePrefix; - } - /** - Get forceDirSuffix - the suffix added to a path component to force - the path component to be treated like a directory. - \see _forceDirSuffix - */ - const std::string & getForceDirSuffix() const - { - return _forceDirSuffix; - } - /** - Set forceDirSuffix. - \see getForceDirSuffix - \see _forceDirSuffix - */ - void setForceDirSuffix(const std::string & forceDirSuffix) - { - _forceDirSuffix = forceDirSuffix; - } - /** - Get dirMetadataName - path component name of all directory - metadata ZooKeeper nodes. - \see _dirMetadataname - */ - const std::string & getDirMetadataName() const - { - return _dirMetadataName; - } - /** - Get regMetadataName - path component name of all regular file - metadata ZooKeeper nodes. - \see _regMetadataname - */ - const std::string & getRegMetadataName() const - { - return _regMetadataName; - } - /** - Get number of not-in-use ZkFuseFile instances to to cache. - \see _cacheSize - */ - unsigned getCacheSize() const - { - return _cacheSize; - } - /** - Set cache size. - \see getCacheSize - \see _cacheSize - */ - void setCacheSize(unsigned v) - { - _cacheSize = v; - } - /** - Get userid. - \see _uid - */ - uid_t getUid() const - { - return _uid; - } - /** - Get groupid. - \see _gid - */ - gid_t getGid() const - { - return _gid; - } - /** - Get block size. - \see _blkSize - */ - unsigned getBlkSize() const - { - return _blkSize; - } - /** - Get ZooKeeperAdapter. - \see _zkAdapter. - */ - const ZooKeeperAdapterSharedPtr & getZkAdapter() const - { - return _zkAdapter; - } - /** - Set ZooKeeperAdapter. - \see _zkAdaptor - */ - void setZkAdapter(const ZooKeeperAdapterSharedPtr & zkAdapter) - { - _zkAdapter = zkAdapter; - } -}; - -/** - ZkFuseNameType - identifies the type of the ZkFuse path. - */ -enum ZkFuseNameType { - /** - ZkFuse path is not syntheiszed. - ZkFuse should use its default rules to determine the Posix representation - of the path. - */ - ZkFuseNameDefaultType = 0, - /** - ZkFuse path is synthesized and identifies the data part of a - ZooKeeper node, i.e. Posix regular file semantics is expected. - */ - ZkFuseNameRegType = 1, - /** - ZkFuse path is synthesized and identifies the chidlren part of a - ZooKeeper node, i.e. Posix directory semantics is expected. - */ - ZkFuseNameDirType = 2 -}; - -class ZkFuseFile; - -typedef ZkFuseFile * ZkFuseFilePtr; - -class ZkFuseHandleManagerFactory; - -/** - ZkFuseHandleManager - keeps track of all the ZkFuseFile instances - allocated by a ZkFuseHandleManager instance and provides them - with a handle that can be used by FUSE. - - It maps a ZooKeeper path to a handle and a handle to a ZkFuse instance. - It also implements the methods that takes path names as arguments, such - as open, mknod, rmdir, and rename. - - Memory management - - References ZkFuseFile instances using regular pointers - Smart pointer is not used because reference counts are needed to - determine how many time a node is opened as a regular file or - directory. This also avoids circular smart pointer references. - - Each ZkFuseFile instance holds a reference to its ZkFuseHandleManager - using a boost::shared_ptr. This ensures that the ZkFuseHandleManager - instance that has the handle for the ZkFuseFile instance does not - get garbage collected while the ZkFuseFile instance exists. - - Concurrency control - - Except for the immutable ZkFuseCommon, all other member variables - are protected by _mutex. - - A method in this class can hold _mutex when it directly or - indirectly invokes ZkFuseFile methods. A ZkFuseFile method that holds - a ZkFuseFile instance _mutex cannot invoke a ZkFuseHandleManager - method that acquires the ZkFuseHandleManager instance's _mutex. - Otherwise, this may cause a dead lock. - - Methods that with names that begin with "_" do not acquire _mutex. - They are usually called by public methods that acquire and hold _mutex. - */ -class ZkFuseHandleManager : boost::noncopyable -{ - private: - /** - Typedef of handle, which is an int. - */ - typedef int Handle; - /** - Typedef of std::map used to map path to handle. - */ - typedef std::map Map; - /** - Typedef of std::vector used to map handle to ZkFuseFile instances. - */ - typedef std::vector Files; - /** - Typedef of std::vector used to hold unused handles. - */ - typedef std::vector FreeList; - /** - Typedef of boost::weak_ptr to the ZkFuseHandleManager instance. - */ - typedef boost::weak_ptr WeakPtr; - - /* Only ZkFuseHandleManagerFactory can create instances of this class */ - friend class ZkFuseHandleManagerFactory; - - /** - Contains common configuration. - Immutable so that it can be accessed without locks. - */ - const ZkFuseCommon _common; - /** - Maps a path name to a Handle. - */ - Map _map; - /** - Maps a handle to a ZkFuseFile instances. - Also holds pointers to all known ZkFuseFile instances. - An element may point to an allocated ZkFuseFile instance or be NULL. - - An allocated ZkFuseFile instance may be in one of the following states: - - in-use - Currently open, i.e. the ZkFuseFile instance's reference count - greater than 0. - - in-cache - Not currently open, i.e. the ZkFuseFile instances's - reference count is 0. - */ - Files _files; - /** - List of free'ed handles. - */ - FreeList _freeList; - /** - Mutex used to protect this instance. - */ - mutable zkfuse::Mutex _mutex; - /** - Count of number of in-use entries. - It used to calculate number of cached nodes. - Number cached nodes is (_files.size() - _numInUse). - */ - unsigned _numInUse; - /** - WeakPtr to myself. - */ - WeakPtr _thisWeakPtr; - - /** - Obtain a handle for the given path. - - If path is not known, then allocate a new handle and increment - _numInUse, and set newFile to true. The allocated - ZkFuseFile instance's reference count should be 1. - - If path is known, increase the corresponding - ZkFuseFile instance's reference count. - - \return the allocated handle. - \param path the path to lookup. - \param newFile indicates whether a new handle has been allocated. - */ - Handle allocate(const std::string & path, bool & newFile); - - /** - Constructor. - - \param common the immutable common configuration. - \param reserve number of elements to pre-allocate for - _files and _freeList. - */ - ZkFuseHandleManager( - const ZkFuseCommon & common, - const unsigned reserve) - : _common(common), - _files(), - _freeList(), - _mutex(), - _numInUse(0) - { - _files.reserve(reserve); - _files[0] = NULL; /* 0 never allocated */ - _files.resize(1); - _freeList.reserve(reserve); - } - - public: - /** - Typedef for boost::shared_ptr for this ZkFuseHandleManager class. - */ - typedef boost::shared_ptr SharedPtr; - - /** - Destructor. - */ - ~ZkFuseHandleManager() - { - } - /** - Get the ZkFuseFile instance for a handle. - - \return the ZkFuseFile instance identified by the handle. - \param handle get ZkFuseFile instance for this handle. - */ - ZkFuseFilePtr getFile(Handle handle) const - { - AutoLock lock(_mutex); - return _files[handle]; - } - /** - Get the immutable common configuration. - - \return the common configuration instance. - */ - const ZkFuseCommon & getCommon() const - { - return _common; - } - /** - Deallocate a previously allocated handle. - This decrements the reference count of the corresponding - ZkFuseFile instance. If the reference count becomes zero, - decrement _numInUse. It may also cause the ZkFuseFile instance - to be reclaimed if there are too many cached ZkFuseFile instances. - - The ZkFuseFile instance should be reclaimed if the number of - unused ZkFuseFile instances exceeds the configured cache size, i.e. - (_files.size() - _numInUse) > _common.getCacheSize() - and the ZkFuseFile instance has a reference count of zero. - - Reclaiming a ZkFuseFile instance involves removing the ZkFuseFile - instance's path to handle mapping from _map and the handle to the - ZkFuseFile instance mapping from _files, adding the handle to - the _freeList, and finally deleting the ZkFuseFile instance. - - \param handle the handle that should be deallocated. - */ - void deallocate(Handle handle); - /** - Handles ZooKeeper session events. - It invokes the known ZkFuseFile instances to let them know - that their watches will no longer be valid. - */ - void eventReceived(const ZKWatcherEvent & event); - /** - Get data from the specified the ZooKeeper path. - - \return 0 if successful, otherwise return negative errno. - \param path the path of the ZooKeeper node. - \param data return data read. - */ - int getData(const std::string & path, Data & data); - /** - Set data into the specified ZooKeeper path. - - \return 0 if successful, otherwise return negative errno. - \param path the path of the ZooKeeper node. - \param data the data to be written. - \param exists set to true if this path exists. - \param doFlush set to true if new data should be flushed to ZooKeeper. - */ - int setData(const std::string & path, - const Data & data, - bool exists, - bool doFlush); - /** - Create a ZooKeeper node to represent a ZkFuse file or directory. - - \return handle if successful, otherwise return negative errno. - \param path to create. - \param mode should be either S_IFDIR for directory or - S_IFREG for regular file. - \param mayExist if set and the ZooKeeper node already exist, return - valid handle instead of -EEXIST. - \param created returns whether a new ZooKeeper node had been created. - */ - int mknod(const std::string & path, - mode_t mode, - bool mayExist, - bool & created); - /** - Open a ZooKeeper node. - - The justCreated argument is used to differentiate if the _deleted flag - of the ZkFuseFile instance is to be trusted (i.e. the path - does not exist in ZooKeeper.) The _deleted flag is trusted - if the ZkFuseFile instance is known to exist in ZooKeeper after - invoking ZooKeeper with the path. - - If justCreated is true, then the ZkFuseFile instance was just created. - The ZkFuseFile constructor sets the _deleted flag to true because - path is not known to exist and hence should not be accessed. - The justCreated flag will force the ZkFuseFile instance to invoke - ZooKeeper to determine if the path exists. - - \return handle if successful, otherwise return negative errno. - \param path the path to open. - \param justCreated indicates if this is newly created ZkFuseFile instance. - */ - int open(const std::string & path, bool justCreated); - /** - Remove a ZkFuse directory. - - If force is not set, then the ZooKeeper node will be removed only - if it has no data and no child nodes except ZkFuse metadata nodes. - - \return 0 if successful, otherwise return negative errno. - \param path the path to remove. - \param force force removal, i.e. bypass checks. - */ - int rmdir(const char * path, bool force = false); - /** - Make a ZkFuse directory. - - ZkFuse represents a ZooKeeper node with no data and no children - as a regular file. In order to differentiate a newly created - directory from an empty regular file, mkdir will create a directory - metadata node as a child of the directory. - - \return 0 if successful, otherwise return negative errno. - \param path the path of the directory to create. - \param mode create directory with this mode - (mode currently not implemented). - */ - int mkdir(const char * path, mode_t mode); - /** - Remove a ZkFuse regular file. - - A file is the abstraction for the data part of a ZooKeeper node. - - If ZkFuse represents a ZooKeeper node as a directory, the data part - of the node is represented by synthesizing a name for this file. This - synthesized name is visible through readdir if the ZooKeeper node's - data is not empty. Removing such a file is done by truncating - the ZooKeeper node's data to 0 length. - - If ZkFuse represents a ZooKeeper node as a file, then removing the - is done by removing the ZooKeeper node (and its metadata). - - \return 0 if successful, otherwise return negative errno. - \param path the path of the file to remove. - */ - int unlink(const char * path); - /** - Get attributes of a ZkFuse regular file or directory. - - \return 0 if successful, otherwise return negative errno. - \param path get attributes for this path - \param stbuf store attributes here. - */ - int getattr(const char * path, struct stat & stbuf); - /** - Rename a ZkFuse regular file. - - It creates a new ZooKeeper node at toPath, copies data and file - metadata from the ZooKeeper node at fromPath to the new node, - and deletes the current ZooKeeper node. If the current ZooKeeper - node is not deleted if the new ZooKeeper node cannot be created - or the data copy fails. - - It cannot be used to rename a directory. - - \return 0 if successful, otherwise return negative errno. - \param fromPath the current path. - \param toPath rename to this path. - */ - int rename(const char * fromPath, const char * toPath); - /** - Add a child ZooKeeper path to the children information cache - of the ZkFuseFile instance that caches the parent ZooKeeper node. - - This is used to add a child path after a new ZooKeeper node has - been created to the children information cache of the parent - ZooKeeper node. This is needed because waiting for the children - changed event to update the cache may result in inconsistent local - views of the changes. - \see removeChildFromParent - - \parama childPath the path of the child ZooKeeper node. - */ - void addChildToParent(const std::string & childPath) const; - /** - Remove a child ZooKeeper path from the children information cache - of the ZkFuseFile instance that caches the parent ZooKeeper node. - - For example, this should happen whenever a path is deleted. - This child information cache of the parent will eventually be - invalidated by watches. However, the delivery of the children - change event may come after the next access and thus provide - the client with an inconsistent view. One example is that - client deletes the last file in a directory, but the children - changed event is not delivered before the client invokes rmdir. - to remove the parent. In this case, the rmdir fails because - the cached children information of the parent indicates the - "directory" is not empty. - - \param childPath the path of the child ZooKeeper node. - */ - void removeChildFromParent(const std::string & childPath) const; - /** - Return the path for the parent of the specified ZooKeeper path. - - \return the parent path. - \param childPath the child path. - */ - std::string getParentPath(const std::string & childPath) const; - /** - Return the ZooKeeper path from a ZkFuse path. - - The ZkFuse path may be a synthesized path. For example, a synthesized - path is required to access the data part of a ZooKeeper node's - data when ZkFuse represents the ZooKeeper node as directory. - A synthesized path is also required to create a child ZooKeeper node - under a ZooKeeper node that is represented by a regular file. - - \return the ZooKeeper path for path. - \param path the ZkFuse path, which may be a synthesized path. - \param nameType indicate whether the ZkFuse path is synthesized and - whether the synthesized ZkFuse path identifies a - directory or a regular file. - */ - std::string getZkPath(const char * path, ZkFuseNameType & nameType) const; -}; - -/** - ZkFuseHandleManagerFactory - factory for ZkFuseHandleManager. - - This is the only way to create a ZkFuseHandleManager instance. - to make sure that _thisWeakPtr of the instance is intialized - after the instance is created. - */ -class ZkFuseHandleManagerFactory -{ - public: - /** - Create an instance of ZkFuseHandleManager. - - \return the created ZkFuseHandleManager instance. - \param common the common configuration. - \param reserve initially reserve space for this number of handles. - */ - static ZkFuseHandleManager::SharedPtr create( - const ZkFuseCommon & common, - unsigned reserve = 1000) - { - ZkFuseHandleManager::SharedPtr manager - (new ZkFuseHandleManager(common, reserve)); - manager->_thisWeakPtr = manager; - return manager; - } -}; - -/** - ZkFuseAutoHandle - automatically closes handle. - - It holds an opened handle and automatically closes this handle - when it is destroyed. This enables code that open a handle - to be exception safe. - */ -class ZkFuseAutoHandle -{ - private: - /** - Typedef for Handle which is an int. - */ - typedef int Handle; - /** - Holds a reference to the ZkFuseHandlerManager instance that - allocated the handle. - */ - ZkFuseHandleManager::SharedPtr _manager; - /** - The handle that should be closed when this instance is destroyed. - A valid handle has value that is equal or greater than 0. - A negative value indicates an error condition, usually the value - is a negative errno. - */ - Handle _handle; - /** - Caches a reference to the ZkFuseFile instance with this handle. - This is a performance optimization so that _manager.getFile(_handle) - is only called once when the handle is initialized. - */ - ZkFuseFilePtr _file; - - /** - Initialize reference to the ZkFuseFile instance with this handle. - */ - void _initFile() - { - if (_handle >= 0) { - _file = _manager->getFile(_handle); - } else { - _file = NULL; - } - } - - public: - /** - Constructor - takes an previously opened handle. - - \param manager the ZkFuseHandleManager instance who allocated the handle. - \param handle the handle. - */ - ZkFuseAutoHandle( - const ZkFuseHandleManager::SharedPtr & manager, - int handle) - : _manager(manager), - _handle(handle), - _file() - { - _initFile(); - } - /** - Constructor - open path and remember handle. - - \param manager the ZkFuseHandleManager instance who allocated the handle. - \param path open this path and remember its handle in this instance. - */ - ZkFuseAutoHandle( - const ZkFuseHandleManager::SharedPtr & manager, - const std::string & path) - : _manager(manager), - _handle(_manager->open(path, false)), - _file() - { - _initFile(); - } - /** - Constructor - create path and remember handle. - - The creation mode indicates whether the path identifies a regular file - or a directory. - - \param manager the ZkFuseHandleManager instance who allocated the handle. - \param path create this path and remember its handle in this instance. - \param mode the creation mode for the path, should be either - S_IFDIR or S_IFDIR. - \param mayExist, if set and the path already exists, - then the ZkFuseAutoHandle will hold the handle - for the path instead of -EEXIST. - If not set and the path does not exist, then the handle - be -EEXIST. - */ - ZkFuseAutoHandle( - const ZkFuseHandleManager::SharedPtr & manager, - const std::string & path, - mode_t mode, - bool mayExist) - : _manager(manager), - _handle(-1), - _file() - { - bool created; - _handle = _manager->mknod(path, mode, mayExist, created); - _initFile(); - } - /** - Destructor - closes the handle. - */ - ~ZkFuseAutoHandle() - { - reset(); - } - /** - Get the handle. - \see _handle - */ - int get() const - { - return _handle; - } - /** - Get the ZkFuseFile instance of the handle. - \see _file - */ - ZkFuseFilePtr getFile() const - { - return _file; - } - /** - Forget the handle, don't close the handle. - */ - void release() - { - _handle = -1; - _file = NULL; - } - /** - Change the remembered handle. - - It will close the current handle (if valid). - */ - void reset(int handle = -1); -}; - -/** - ZkFuseStat - C++ wrapper for ZooKeeper Stat. - - This wrapper provides ZooKeeper Stat will constructors that - initializes the instance variables of Stat. - */ -class ZkFuseStat : public Stat -{ - public: - /** - Constructor - clear instance variables. - */ - ZkFuseStat() - { - clear(); - } - /** - Destructor - do nothing. - */ - ~ZkFuseStat() - { - } - /** - Clear instance variables. - */ - void clear() - { - czxid = 0; - mzxid = 0; - ctime = 0; - mtime = 0; - version = 0; - cversion = 0; - aversion = 0; - } -}; - -/** - ZkFuseFile - an instance encapsulates the runtime state of an allocated - ZooKeeper node. - - Memory management - - Referenced by the ZkFuseHandleManager that created this instance. - - Uses boost::shared_ptr to reference the ZkFuseHandleManager that - created this instance. This makes sure that this ZkFuseHandleManager - instance cannot be deleted when it has allocated ZkFuseFile instances. - - A ZkFuseHandleManager deletes itself if it can be reclaimed. - It can be reclaimed if it has no watches, its reference count is zero, - and the ZkFuseHandleManager instance would have more than the - configured number of cached ZkFuseFile instances. - - A ZkFuseFile instance cannot be deleted if it has active watches on - its ZooKeeper node. When one of its watches fires, the ZkFuseFile - instance must exist because one of its methods will be invoked - to process the event. If the ZkFuseFile instance has been deleted, - the method will access previously freed memory. - - Concurrency control - - _mutex protects the instance variables of an instance. - - Callers should assume that a public method will acquire _mutex. - - Methods of this class may not hold _mutex while invoking an - ZkFuseHandleManager instance. - - Methods that with names that begin with "_" do not acquire _mutex. - They are usually called by public methods that acquire and hold _mutex. -*/ -class ZkFuseFile : boost::noncopyable -{ - public: - /** - Maximum size for the data part of a ZooKeeper node. - */ - static const unsigned maxDataFileSize = MAX_DATA_SIZE; - - private: - /** - Mode returned by getattr for a ZkFuse directory. - */ - static const mode_t dirMode = (S_IFDIR | 0777); - /** - Mode returned by getattr for a ZkFuse regular file. - */ - static const mode_t regMode = (S_IFREG | 0777); - - /** - References the ZkFuseHandleManager that created this instance. - */ - ZkFuseHandleManager::SharedPtr _manager; - /** - Handle for this instance. - */ - const int _handle; - /** - Path of the ZooKeeper node represented by this instance. - */ - const std::string _path; - /** - Mutex that protects the instance variables of this instance. - */ - mutable zkfuse::Mutex _mutex; - /** - Reference count for this instance, i.e. the number of opens - minus the number of closes. - */ - int _refCount; - /** - Indicates whether the ZooKeeper node exist. - This flag allows caching of deleted ZooKeeper node to avoid - repeated ZooKeeper lookups for a non-existent path, and avoid - using cached information. - - Its value is true if - - it is verified to exist (by calling ZooKeeper), or - - it is existence is unknown because ZooKeeper has not been - invoked to verify its path's existence. - */ - bool _deleted; - /** - Count of current number directory opens minus directory closes. - */ - int _openDirCount; - /** - Indicates whether cached children information is valid. - - It is true if the cached children information is valid. - */ - bool _initializedChildren; - /** - Indicates whether there is an outstanding children watch. - - It is true if it has an outstanding children watch. - */ - bool _hasChildrenListener; - /** - Cached children information. - - The cache is valid if _initializedChildren is true. - */ - NodeNames _children; - - /** - Indicates whether the cached data is valid. - - It is true if the cached data and ZooKeeper Stat are valid. - */ - bool _initializedData; - /** - Indicates whether there is an outstanding data watch. - - It is true if it has an outstanding data watch. - */ - bool _hasDataListener; - /** - Indicates whether the cached data (_activeData) has been modified. - - It is true if the cached data has been modified. - */ - bool _dirtyData; - /** - Currently active data. - - To maintain atomicity of updates and emulate Posix semantics, - when a ZkFuse file remains open, the same data will be accessed - by the file's clients. The data will be flushed to ZooKeeper when - the flush method is called. The flush method may be called - explicitly by a client or implicitly when the ZkFuse file is no - longer currently open. - - _activeData and _activeStat stores the data and ZooKeeper Stat - that will be accessed by the file's clients. - - If there are changes when the ZkFuse file is open, new data is - cached as latest data (by _latestData and _latestStat). - */ - Data _activeData; - /** - Currently active ZooKeeper Stat. - \see _activeData - */ - ZkFuseStat _activeStat; - /** - Latest data. - This is either the same as _activeData or it is newer. It is newer - is it has been updated by event triggered by a data watch. - */ - Data _latestData; - /** - Latest ZooKeeper data. - This is either the same as _activeStat or it is newer. It is newer - is it has been updated by event triggered by a data watch. - */ - ZkFuseStat _latestStat; - - /** - Get userid. - - \return the userid. - */ - uid_t _getUid() const - { - return _manager->getCommon().getUid(); - } - /** - Get groupid. - - \return the groupid. - */ - gid_t _getGid() const - { - return _manager->getCommon().getGid(); - } - /** - Get block size. - - \return the block size. - */ - unsigned _getBlkSize() const - { - return _manager->getCommon().getBlkSize(); - } - /** - Get number of children, include metadata children in the count. - - \return the number of children including metadata children. - */ - unsigned _numChildrenIncludeMeta() const - { - unsigned count = _children.size(); - LOG_DEBUG(LOG, "numChildrenIncludeMeta() returns %u", count); - return count; - } - /** - Get number of children, exclude metadata children in the count. - - \return the number of children excluding metadata children. - */ - unsigned _numChildrenExcludeMeta() const - { - unsigned count = 0; - for (NodeNames::const_iterator it = _children.begin(); - it != _children.end(); - it++) { - if (!_isMeta(*it)) { - count++; - } - } - LOG_DEBUG(LOG, "numChildrenExcludeMeta() returns %u", count); - return count; - } - /** - Whether the ZooKeeper node has children, include metadata - children. - - \return true if it has children including metadata children. - */ - bool _hasChildrenIncludeMeta() const - { - return _numChildrenIncludeMeta() != 0; - } - /** - Return true if the ZooKeeper node has children, include metadata - children. - - \return true if it has children excluding metadata children. - */ - bool _hasChildrenExcludeMeta() const - { - return _numChildrenExcludeMeta() != 0; - } - /** - Whether the ZooKeeper node has data. - - \return true if _activeData is not empty. - */ - bool _hasData() const - { - return _activeData.empty() == false; - } - /** - Whether the ZooKeeper node has child with the specified path. - - \return true if the ZooKeeper node has a child with the specified path. - \param childPath the path of the child. - */ - bool _hasChildPath(const std::string & childPath) const - { - bool hasChild = - std::find(_children.begin(), _children.end(), childPath) - != _children.end(); - LOG_DEBUG(LOG, "hasChild(childPath %s) returns %d", - childPath.c_str(), hasChild); - return hasChild; - } - /** - Whether the given path component is a ZkFuse synthesized path - component. - - A ZkFuse synthesized path component will begin with - the metadataNamePrefix obtained from the common configuration. - \see _metadataNamePrefix - - \return true if the path component is a ZkFuse synthesized path - component. - \param childName the path component to check if it is synthesized by - ZkFuse. - */ - bool _isMeta(const std::string & childName) const - { - bool isMeta; - const std::string & prefix = - _manager->getCommon().getMetadataNamePrefix(); - unsigned offset = - (_path.length() > 1 ? - _path.length() + 1 : - 1 /* special case for root dir */ ); - unsigned minLength = offset + prefix.length(); - if (childName.length() < minLength || - childName.compare(offset, prefix.length(), prefix) != 0) { - isMeta = false; - } else { - isMeta = true; - } - LOG_DEBUG(LOG, "isMeta(childName %s) returns %d", - childName.c_str(), isMeta); - return isMeta; - } - /** - Build a path for a specific child of the ZooKeeper node. - - This is done by appending "/" (unless it is the ZooKeeper node - is the root node) and the name of the child. - - \return the path for the specified child of the ZooKeeper node. - \param name the name of the child. - */ - std::string _getChildPath(const std::string & name) const - { - return buildChildPath(_path, name); - } - /** - Whether the ZooKeeper node has a regular file metadata child node. - - \return true if the ZooKeeper node has a regular file metadata child - node. - */ - bool _hasRegMetadata() const - { - bool res = _hasChildPath( - _getChildPath(_manager->getCommon().getRegMetadataName())); - LOG_DEBUG(LOG, "hasRegMetadata() returns %d", res); - return res; - } - /** - Whether the ZooKeeper node has a directory metadata child node. - - \return true if the ZooKeeper node has a directory metadata child - node. - */ - bool _hasDirMetadata() const - { - bool res = _hasChildPath( - _getChildPath(_manager->getCommon().getDirMetadataName())); - LOG_DEBUG(LOG, "hasDirMetadata() returns %d", res); - return res; - } - /** - Whether ZkFuse should present the ZooKeeper node as a ZkFuse regular - file. - - It should be a ZkFuse regular file it has no children or its - only children is its regular file metadata child node. - - \return true if the Zookeeper node should be presented as a ZkFuse - regular file. - */ - bool _isReg() const - { - unsigned numChildrenIncludeMeta = _numChildrenIncludeMeta(); - bool res = - (numChildrenIncludeMeta == 0) || - (numChildrenIncludeMeta == 1 && _hasRegMetadata() == true); - LOG_DEBUG(LOG, "isReg() returns %d", res); - return res; - } - /** - Whether ZkFuse should present the ZooKeeper node as a ZkFuse directory. - - It should be a ZkFuse directory if it should not be presented as - a ZkFuse regular directory. - \see _isReg - - \return true if the Zookeeper node should be presented as a ZkFuse - directory. - */ - bool _isDir() const - { - return !_isReg(); - } - /** - Whether ZkFuse should present the ZooKeeper node as a ZkFuse regular - file by taking into account the specified ZkFuseNameType. - - The ZkFuseNameType may override the default ZkFuse presentation of - a ZooKeeper node. - - \return true if ZkFuse should present the ZooKeeper node as a ZkFuse - regular file. - \param nameType specifies the ZkFuseNameType. - \param doLock whether _mutex should be acquired, it should be true - if the caller did not acquire _mutex. - */ - bool _isRegNameType(ZkFuseNameType nameType, bool doLock = false) const - { - bool res; - switch (nameType) { - case ZkFuseNameRegType: - res = true; - break; - case ZkFuseNameDirType: - res = false; - break; - case ZkFuseNameDefaultType: - default: - if (doLock) { - AutoLock lock(_mutex); - res = _isReg(); - } else { - res = _isReg(); - } - break; - } - LOG_DEBUG(LOG, "isRegNameType(nameType %d) returns %d", - int(nameType), res); - return res; - } - /** - Whether ZkFuse should present the ZooKeeper node as a ZkFuse - directory by taking into account the specified ZkFuseNameType. - - The ZkFuseNameType may override the default ZkFuse presentation of - a ZooKeeper node. - - \return true if ZkFuse should present the ZooKeeper node as a ZkFuse - directory. - \param nameType specifies the ZkFuseNameType. - \param doLock whether _mutex should be acquired, it should be true - if the caller did not acquire _mutex. - */ - bool _isDirNameType(ZkFuseNameType nameType, bool doLock = false) const - { - bool res; - switch (nameType) { - case ZkFuseNameRegType: - res = false; - break; - case ZkFuseNameDirType: - res = true; - break; - case ZkFuseNameDefaultType: - default: - if (doLock) { - AutoLock lock(_mutex); - res = _isDir(); - } else { - res = _isDir(); - } - break; - } - LOG_DEBUG(LOG, "isDirNameType(nameType %d) returns %d", - int(nameType), res); - return res; - } - /** - ZkFuse regular file metadata. - */ - struct Metadata { - /** - Version of the ZooKeeper node data that this metadata is good for. - */ - uint32_t version; - /** - Acces time in milliseconds. - */ - uint64_t atime; - /** - Modified time in milliseconds. - */ - uint64_t mtime; - - /** - Constructor. - */ - Metadata() - : version(0), - atime(0), - mtime(0) - { - } - }; - /** - Encode Metadata into Data so that it can be stored in a metadata - ZooKeeper node. - - Each Metadata attribute is encoded as ": " on single line - terminated by newline. - - \param meta the input Metadata. - \param data the output Data after encoding. - */ - void _encodeMetadata(const Metadata & meta, Data & data) const - { - LOG_DEBUG(LOG, "encodeMetadata()"); - std::ostringstream oss; - oss << "version: " << meta.version << endl - << "atime: " << meta.atime << endl - << "mtime: " << meta.mtime << endl; - data = oss.str(); - } - /** - Decode Data from a metadata child ZooKeeper node into Metadata. - - Data is a stream of ": " records separated by newline. - - \param data the input Data. - \param meta the output Metadata after decoding. - */ - void _decodeMetadata(const Data & data, Metadata & meta) const - { - LOG_DEBUG(LOG, "decodeMetadata(data %s)", data.c_str()); - std::istringstream iss(data); - char key[128]; - char value[1024]; - while (!iss.eof()) { - key[0] = 0; - value[0] = 0; - iss.get(key, sizeof(key), ' '); - if (iss.eof()) { - break; - } - iss.ignore(32, ' '); - iss.getline(value, sizeof(value)); - LOG_DEBUG(LOG, "key %s value %s", key, value); - if (strcmp(key, "version:") == 0) { - unsigned long long v = strtoull(value, NULL, 0); - LOG_DEBUG(LOG, "version: %llu", v); - meta.version = v; - } - else if (strcmp(key, "atime:") == 0) { - unsigned long long v = strtoull(value, NULL, 0); - LOG_DEBUG(LOG, "atime: %llu", v); - meta.atime = v; - } - else if (strcmp(key, "mtime:") == 0) { - unsigned long long v = strtoull(value, NULL, 0); - LOG_DEBUG(LOG, "mtime: %llu", v); - meta.mtime = v; - } - else { - LOG_WARN(LOG, "decodeMetadata: path %s unknown key %s %s\n", - _path.c_str(), key, value); - } - } - LOG_DEBUG(LOG, "decodeMetadata done"); - } - /** - Flush data to the ZooKeeper node. - - If cached active data has been modified, flush it to the ZooKeeper node. - Returns -EIO if the data cannot be written because the cached active - data is not the expected version, i.e. ZooKeeper returns ZBADVERSION. - -EIO may also indicate a more general failure, such as unable to - communicate with ZooKeeper. - - \return 0 if successful, otherwise negative errno. - */ - int _flush() - { - LOG_DEBUG(LOG, "flush() path %s", _path.c_str()); - - int res = 0; - try { - if (_dirtyData) { - LOG_DEBUG(LOG, "is dirty, active version %d", - _activeStat.version); - _manager->getCommon().getZkAdapter()-> - setNodeData(_path, _activeData, _activeStat.version); - /* assumes version always increments by one if successful */ - _deleted = false; - _activeStat.version++; - _dirtyData = false; - res = 0; - } - else { - LOG_DEBUG(LOG, "not dirty"); - res = 0; - } - } catch (const ZooKeeperException & e) { - if (e.getZKErrorCode() == ZBADVERSION) { - LOG_ERROR(LOG, "flush %s bad version, was %d", - _path.c_str(), _activeStat.version); - res = -EIO; - } - else { - LOG_ERROR(LOG, "flush %s exception %s", - _path.c_str(), e.what()); - res = -EIO; - } - } - - LOG_DEBUG(LOG, "flush returns %d", res); - return res; - } - /** - Truncate or expand the size of the cached active data. - - This method only changes the size of the cached active data. - This change is committed to ZooKeeper when the cached data - is written to the ZooKeeper node by flush(). - - Return -EFBIG is the requested size exceeds the maximum. - - \return 0 if successful, otherwise negative errno. - \param size the requested size. - */ - int _truncate(off_t size) - { - LOG_DEBUG(LOG, "truncate(size %zu) path %s", size, _path.c_str()); - - int res = 0; - - if (!_isInitialized()) { - LOG_DEBUG(LOG, "not initialized"); - res = -EIO; - } - else if (size > _activeData.size()) { - if (size > maxDataFileSize) { - LOG_DEBUG(LOG, "size > maxDataFileSize"); - res = -EFBIG; - } else { - LOG_DEBUG(LOG, "increase to size"); - _activeData.insert(_activeData.begin() + - (size - _activeData.size()), 0); - _dirtyData = true; - res = 0; - } - } - else if (size < _activeData.size()) { - LOG_DEBUG(LOG, "decrease to size"); - _activeData.resize(size); - _dirtyData = true; - res = 0; - } - else { - LOG_DEBUG(LOG, "do nothing, same size"); - } - - LOG_DEBUG(LOG, "truncate returns %d", res); - return res; - } - /** - Remove a ZkFuse directory. - - If force is true, then the ZooKeeper node and its decendants - will be deleted. - - If force is false, then this method implements the semantics - of removing a ZkFuse directory. It will delete the ZooKeeper node - only if the ZooKeeper node have no data and no non-metadata - children. - - Return -ENOTDIR if the ZooKeeper node is not considered - to be a directory (after taking into consideration the specified - ZkFuseNameType). - - Return -ENOTEMPTY if the ZooKeeper node has data or it has - non-metadata children. - - Return -ENOENT if the ZooKeeper cannot be deleted, usually this - is because it does not exist. - - \return 0 if successful, otherwise negative errno. - \param nameType the ZkFuseNameType of the path used to specify the - directory to be removed. It influences whether ZkFuse - considers the ZooKeeper node to be a regular file or - directory. \see ZkFuseNameType - \param force set to true to bypass ZkFuse rmdir semantic check. - */ - int _rmdir(ZkFuseNameType nameType, bool force) - { - LOG_DEBUG(LOG, "rmdir(nameType %d, force %d) path %s", - int(nameType), force, _path.c_str()); - - int res = 0; - try { - if (!force && !_isDirNameType(nameType)) { - LOG_DEBUG(LOG, "failed because not directory"); - res = -ENOTDIR; - } - else if (!force && _hasData()) { - /* rmdir cannot occur if there non-empty "data file" */ - LOG_DEBUG(LOG, "failed because node has data"); - res = -ENOTEMPTY; - } - else if (!force && _hasChildrenExcludeMeta()) { - /* rmdir cannot occur if there are "subdirs" */ - LOG_DEBUG(LOG, "failed because node has children"); - res = -ENOTEMPTY; - } - else { - LOG_DEBUG(LOG, "delete node"); - bool deleted = _manager->getCommon().getZkAdapter()-> - deleteNode(_path, true); - if (deleted) { - _deleted = true; - _clearChildren(); - res = 0; - } else { - /* TODO: differentiate delete error conditions, - * e.g. access permission, not exists, ... ? - */ - LOG_DEBUG(LOG, "delete failed"); - res = -ENOENT; - } - } - } catch (const std::exception & e) { - LOG_ERROR(LOG, "rmdir %s exception %s", _path.c_str(), e.what()); - res = -EIO; - } - - LOG_DEBUG(LOG, "rmdir returns %d", res); - return res; - } - /** - Remove a ZkFuse regular file. - - This method implements the semantics of removing a ZkFuse regular file. - - If the ZkFuse regular file represents the data part of the - ZooKeeper node which is presented as a ZkFuse directory, - the regular file is virtually deleted by truncating the - ZooKeeper node's data. Readdir will not synthesize a regular - file entry for the data part of a ZooKeeper node if - the ZooKeeper node has no data. - - If the ZkFuse regular file represents the data part of the - ZooKeeper node which is presented as a ZkFuse regular file, - the ZooKeeper node and its decendants are deleted. - - Returns -EISDIR if the ZkFuse regular file cannot be deleted - because ZkFuse consider it to be a directory. - - \return 0 if successful, otherwise negative errno. - \param nameType the ZkFuseNameType of the path used to specify the - directory to be removed. It influences whether ZkFuse - considers the ZooKeeper node to be a regular file or - directory. \see ZkFuseNameType - */ - int _unlink(ZkFuseNameType nameType) - { - LOG_DEBUG(LOG, "unlink(nameType %d) path %s", - int(nameType), _path.c_str()); - - int res = 0; - switch (nameType) { - case ZkFuseNameRegType: - if (_isDir()) { - res = _truncate(0); - } else { - res = _rmdir(nameType, true); - } - break; - case ZkFuseNameDirType: - res = -EISDIR; - break; - case ZkFuseNameDefaultType: - default: - if (_isReg()) { - res = _rmdir(nameType, true); - } else { - res = -EISDIR; - } - break; - } - - LOG_DEBUG(LOG, "unlink returns %d", res); - return res; - } - /** - Whether cached children and data are valid. - - \return true if cached children and data are valid. - */ - bool _isInitialized() const - { - return _initializedChildren && _initializedData; - } - /** - Clear and invalidate cached children information. - */ - void _clearChildren() - { - _initializedChildren = false; - _children.clear(); - } - /** - Clear and invalidate cached data. - */ - void _clearData() - { - _initializedData = false; - _dirtyData = false; - _activeData.clear(); - _activeStat.clear(); - _latestData.clear(); - _latestStat.clear(); - } - /** - Whether the ZkFuseFile instance is a zombie. - - It is a zombie if it is not currently open, i.e. its reference count - is 0. - */ - bool _isZombie() const - { - return (_refCount == 0); - } - /** - Whether the ZkFuseFile instance is currently opened as a regular file - only once. - - It is used to determine when the cached data can be replaced with - the latest data. \see _activeData. - - \return true if its currently opened as a regular file only once. - */ - bool _isOnlyRegOpen() const - { - return ((_refCount - _openDirCount) == 1); - } - /** - Get attributes without accessing metadata. - - The atime and mtime returned does not take into consideration - overrides present in a matadata file. - - \return 0 if successful, otherwise negative errno. - \param stbuf return attributes here. - \param nameType specifies the ZkFuseNameType of the ZkFuse path used - to get attributes. It influences whether the directory - or regular file attributes are returned. - */ - int _getattrNoMetaAccess(struct stat & stbuf, ZkFuseNameType nameType) const - { - int res = 0; - if (_deleted) { - LOG_DEBUG(LOG, "deleted"); - res = -ENOENT; - } - else if (!_isInitialized()) { - LOG_DEBUG(LOG, "not initialized"); - res = -EIO; - } - else { - assert(_isInitialized()); - bool isRegular = _isRegNameType(nameType); - if (isRegular) { - LOG_DEBUG(LOG, "regular"); - stbuf.st_mode = regMode; - stbuf.st_nlink = 1; - stbuf.st_size = _activeData.size(); - } else { - LOG_DEBUG(LOG, "directory"); - stbuf.st_mode = dirMode; - stbuf.st_nlink = - _children.size() + (_activeData.empty() ? 0 : 1); - stbuf.st_size = stbuf.st_nlink; - } - stbuf.st_uid = _getUid(); - stbuf.st_gid = _getGid(); - /* IMPORTANT: - * Conversion to secs from millisecs must occur before - * assigning to st_atime, st_mtime, and st_ctime. Otherwise - * truncating from 64-bit to 32-bit will cause lost of - * most significant 32-bits before converting to secs. - */ - stbuf.st_atime = millisecsToSecs(_activeStat.mtime); - stbuf.st_mtime = millisecsToSecs(_activeStat.mtime); - stbuf.st_ctime = millisecsToSecs(_activeStat.ctime); - stbuf.st_blksize = _getBlkSize(); - stbuf.st_blocks = - (stbuf.st_size + stbuf.st_blksize - 1) / stbuf.st_blksize; - res = 0; - } - return res; - } - /** - Get the context that should be registered with the data and - children watches. - - The returned context is a pointer to the ZkFuseFile instance - cast to the desired ContextType. - - \return the context. - */ - ZooKeeperAdapter::ContextType _getZkContext() const - { - return (ZooKeeperAdapter::ContextType) NULL; - } - - /** - DataListener - listener that listens for ZooKeeper data events - and calls dataEventReceived on the ZkFuseFile instance - identified by the event context. - \see dataEventReceived - */ - class DataListener : public ZKEventListener { - public: - /** - Received a data event and invoke ZkFuseFile instance obtained from - event context to handle the event. - */ - virtual void eventReceived(const ZKEventSource & source, - const ZKWatcherEvent & event) - { - assert(event.getContext() != 0); - ZkFuseFile * file = static_cast(event.getContext()); - file->dataEventReceived(event); - } - }; - - /** - DataListener - listener that listens for ZooKeeper children events - and calls childrenEventReceived on the ZkFuseFile instance - identified by the event context. - \see childrenEventReceived - */ - class ChildrenListener : public ZKEventListener { - public: - /** - Received a children event and invoke ZkFuseFile instance obtained from - event context to handle the event. - */ - virtual void eventReceived(const ZKEventSource & source, - const ZKWatcherEvent & event) - { - assert(event.getContext() != 0); - ZkFuseFile * file = static_cast(event.getContext()); - file->childrenEventReceived(event); - } - }; - - /** - Globally shared DataListener. - */ - static DataListener _dataListener; - /** - Globally shared ChildrenListener. - */ - static ChildrenListener _childrenListener; - - public: - /** - Constructor. - - Sets reference count to one, i.e. it has been constructed because - a client is trying to open the path. \see _refCount. - Sets deleted to true. \see _deleted. - Sets number of currently directory opens to zero. \see _openDirCount. - Invalidate cach for children information and data. - - \param manager the ZkFuseHandleManager instance who is creating this - ZkFuseFile instance. - \param handle the handle assigned by the ZkFuseHandleManager instance - for this ZkFuseFile instance. - \param path the ZooKeeper path represented by this ZkFuseFile instance. - */ - ZkFuseFile(const ZkFuseHandleManager::SharedPtr & manager, - const int handle, - const std::string & path) - : _manager(manager), - _handle(handle), - _path(path), - _mutex(), - _refCount(1), - _deleted(true), - /* children stuff */ - _openDirCount(0), - _initializedChildren(false), - _hasChildrenListener(false), - _children(), - /* data stuff */ - _initializedData(false), - _hasDataListener(false), - _dirtyData(false), - _activeData(), - _activeStat(), - _latestData(), - _latestStat() - { - LOG_DEBUG(LOG, "constructor() path %s", _path.c_str()); - } - /** - Destructor. - */ - ~ZkFuseFile() - { - LOG_DEBUG(LOG, "destructor() path %s", _path.c_str()); - - assert(_isZombie()); - _clearChildren(); - _clearData(); - } - /** - Whether the ZooKeeper node represented by this ZkFuseFile instance - has been deleted. - \see _deleted - - \return true if it is deleted. - */ - bool isDeleted() const - { - AutoLock lock(_mutex); - return _deleted; - } - /** - Return the path of the ZooKeeper node represented by this ZkFuseFile - instance. - \see _path. - - \return the ZooKeeper node's path. - */ - const string & getPath() const - { - return _path; - } - /** - Add a childPath to the children information cache. - - \return 0 if successful, otherwise return negative errno. - \param childPath the ZooKeeper path of the child. - */ - int addChild(const std::string & childPath) - { - LOG_DEBUG(LOG, "addChild(childPath %s) path %s", - childPath.c_str(), _path.c_str()); - - int res = 0; - { - AutoLock lock(_mutex); - if (_initializedChildren) { - NodeNames::iterator it = - std::find(_children.begin(), _children.end(), childPath); - if (it == _children.end()) { - LOG_DEBUG(LOG, "child not found, adding child path"); - _children.push_back(childPath); - res = 0; - } - else { - LOG_DEBUG(LOG, "child found"); - res = -EEXIST; - } - } - } - - LOG_DEBUG(LOG, "addChild returns %d", res); - return res; - } - /** - Remove a childPath from the children information cache. - - \return 0 if successful, otherwise return negative errno. - \param childPath the ZooKeeper path of the child. - */ - int removeChild(const std::string & childPath) - { - LOG_DEBUG(LOG, "removeChild(childPath %s) path %s", - childPath.c_str(), _path.c_str()); - - int res = 0; - { - AutoLock lock(_mutex); - if (_initializedChildren) { - NodeNames::iterator it = - std::find(_children.begin(), _children.end(), childPath); - if (it != _children.end()) { - LOG_DEBUG(LOG, "child found"); - _children.erase(it); - res = 0; - } - else { - LOG_DEBUG(LOG, "child not found"); - res = -ENOENT; - } - } - } - - LOG_DEBUG(LOG, "removeChild returns %d", res); - return res; - } - /** - Invalidate the cached children information and cached data. - \see _clearChildren - \see _clearData - - \param clearChildren set to true to invalidate children information cache. - \param clearData set to true to invalidate data cache. - */ - void clear(bool clearChildren = true, bool clearData = true) - { - LOG_DEBUG(LOG, "clear(clearChildren %d, clearData %d) path %s", - clearChildren, clearData, _path.c_str()); - - { - AutoLock lock(_mutex); - if (clearChildren) { - _clearChildren(); - } - if (clearData) { - _clearData(); - } - } - } - /** - Whether reference count is zero. - \see _refCount - - \return true if reference count is zero. - */ - bool isZombie() const - { - AutoLock lock(_mutex); - - return (_refCount == 0); - } - /** - Increment the reference count of the ZkFuseFile instance. - - This method may be called by a ZkFuseFileManager instance while - holding the ZkFuseFileManager's _mutex. To avoid deadlocks, - this methods must never invoke a ZkFuseFileManager instance - directly or indirectly while holding the ZkFuseFile instance's - _mutex. - \see _refCount - - \return the post-increment reference count. - \param count value to increment the reference count by. - */ - int incRefCount(int count = 1) - { - LOG_DEBUG(LOG, "incRefCount(count %d) path %s", count, _path.c_str()); - - int res = 0; - { - AutoLock lock(_mutex); - _refCount += count; - assert(_refCount >= 0); - res = _refCount; - } - - LOG_DEBUG(LOG, "incRefCount returns %d", res); - return res; - } - /** - Decrement the reference count of the ZkFuseFile instance. - - This method may be called by a ZkFuseFileManager instance while - holding the ZkFuseFileManager's _mutex. To avoid deadlocks, - this methods must never invoke a ZkFuseFileManager instance - directly or indirectly while holding the ZkFuseFile instance's - _mutex. - \see _refCount - - \return the post-decrement reference count. - \param count value to decrement the reference count by. - */ - int decRefCount(int count = 1) - { - return incRefCount(-count); - } - /** - Increment the count of number times the ZkFuseFile instance has - been opened as a directory. - - This count is incremented by opendir and decremented by releasedir. - \see _openDirCount. - - \return the post-increment count. - \param count the value to increment the count by. - */ - int incOpenDirCount(int count = 1) - { - LOG_DEBUG(LOG, "incOpenDirCount(count %d) path %s", - count, _path.c_str()); - - int res = 0; - { - AutoLock lock(_mutex); - _openDirCount += count; - assert(_openDirCount >= 0); - res = _openDirCount; - assert(_openDirCount <= _refCount); - } - - LOG_DEBUG(LOG, "incOpenDirCount returns %d", res); - return res; - - } - /** - Decrement the count of number times the ZkFuseFile instance has - been opened as a directory. - - This count is incremented by opendir and decremented by releasedir. - \see _openDirCount. - - \return the post-decrement count. - \param count the value to decrement the count by. - */ - int decOpenDirCount(int count = 1) - { - return incOpenDirCount(-count); - } - /** - Whether ZkFuse should present the ZooKeeper node as a ZkFuse - directory by taking into account the specified ZkFuseNameType. - - The ZkFuseNameType may override the default ZkFuse presentation of - a ZooKeeper node. - \see _isDirNameType - - \return true if ZkFuse should present the ZooKeeper node as a ZkFuse - directory. - \param nameType specifies the ZkFuseNameType. - */ - bool isDirNameType(ZkFuseNameType nameType) const - { - return _isDirNameType(nameType, true); - } - /** - Whether ZkFuse should present the ZooKeeper node as a ZkFuse - regular file by taking into account the specified ZkFuseNameType. - - The ZkFuseNameType may override the default ZkFuse presentation of - a ZooKeeper node. - \see _isRegNameType - - \return true if ZkFuse should present the ZooKeeper node as a ZkFuse - regular file. - \param nameType specifies the ZkFuseNameType. - */ - bool isRegNameType(ZkFuseNameType nameType) const - { - return _isRegNameType(nameType, true); - } - /** - Get the active data. - \see _activeData - - \param data return data here. - */ - void getData(Data & data) const - { - AutoLock lock(_mutex); - - data = _activeData; - } - /** - Set the active data. - \see _activeData - - Return -EFBIG is the data to be written is bigger than the maximum - permitted size (and no data is written). - - \return 0 if successful, otherwise return negative errno. - \param data set to this data. - \param doFlush whether to flush the data to the ZooKeeper node. - */ - int setData(const Data & data, bool doFlush) - { - LOG_DEBUG(LOG, "setData(doFlush %d) path %s", doFlush, _path.c_str()); - int res = 0; - - if (data.size() > maxDataFileSize) { - res = -EFBIG; - } - else { - AutoLock lock(_mutex); - _activeData = data; - _dirtyData = true; - if (doFlush) { - res = _flush(); - } - } - - LOG_DEBUG(LOG, "setData() returns %d", res); - return res; - } - /** - Update the children information and the data caches as needed. - - This method is invoked when a ZkFuse regular file or directory - implemented by this ZkFuseFile instance is opened, e.g. - using open or opendir. It attempts to: - - make sure that the cache has valid children information - - register for watches for changes if no previous watches have - been registered. - - The newFile flag indicates if the ZkFuseFile instance has just - been constructed and that ZooKeeper has not been contacted to - determine if the ZooKeeper path for this file really exist. - When a ZkFuseFile instance is created, the _deleted flag is set to - true because it is safer to assume that the ZooKeeper node does - not exist. The newFile flag causes the _deleted flag to be - ignored and ZooKeeper to be contacted to update the caches. - - If the newFile flag is false, then the ZkFuseFile instance is - currently open and have been opened before. Hence, these previous - opens should have contacted ZooKeeper and would like learned from - ZooKeeper whether the ZooKeeper path exists. Therefore, - the _deleted flag should be trustworthy, i.e. it has accurate - information on whether the ZooKeeper path actually exists. - - \return 0 if successful, otherwise return negative errno. - \param newFile set to true if the ZkFuseFile instance is newly created. - */ - int update(bool newFile) - { - LOG_DEBUG(LOG, "update(newFile %d) path %s", newFile, _path.c_str()); - - int res = 0; - { - AutoLock lock(_mutex); - - /* At this point, cannot be zombie. - */ - assert(!_isZombie()); - if (!newFile && _deleted) { - /* Deleted file, don't bother to update caches */ - LOG_DEBUG(LOG, "deleted, not new file"); - res = -ENOENT; - } - else { - try { - LOG_DEBUG(LOG, "initialized children %d, data %d", - _initializedChildren, _initializedData); - LOG_DEBUG(LOG, "has children watch %d, data watch %d", - _hasChildrenListener, _hasDataListener); - /* - * Children handling starts here. - * If don't have children listener, - * then must establish listener. - * If don't have cached children information, - * then must get children information. - * It just happens, that the same ZooKeeper API - * is used for both. - */ - if (_initializedChildren == false || - _hasChildrenListener == false -#ifdef ZOOKEEPER_ROOT_CHILDREN_WATCH_BUG - /* HACK for root node because changes to children - * on a root node does not cause children watches to - * fire. - */ - || _path.length() == 1 -#endif // ZOOKEEPER_ROOT_CHILDREN_WATCH_BUG - ) { - LOG_DEBUG(LOG, "update children"); - NodeNames children; - _manager->getCommon().getZkAdapter()-> - getNodeChildren( children, _path, - &_childrenListener, _getZkContext()); - _hasChildrenListener = true; - LOG_DEBUG(LOG, "update children done"); - _children.swap(children); - _initializedChildren = true; - /* Since getNodeChildren is successful, the - * path must exist */ - _deleted = false; - } - else { - /* Children information is fresh since - * it is initialized and and have been - * updated by listener. - */ - } - /* - * Data handling starts here. - */ - assert(newFile == false || _isOnlyRegOpen()); - if (!_isOnlyRegOpen()) { - /* If is already currently opened by someone, - * then don't update data with latest from ZooKeeper, - * use current active data (which may be initialized - * or not). - * \see _activeData - */ - LOG_DEBUG(LOG, "node currently in-use, no data update"); - } - else { - /* If not opened/reopened by someone else, - * then perform more comprehensive checks of - * to make data and listener is setup correctly. - * If don't have data listener, - * then must establish listener. - * If don't have cached data, - * then must get data. - * It just happens, that the same ZooKeeper API - * is used for both. - */ - LOG_DEBUG(LOG, "node first use or reuse"); - if (_initializedData == false || - _hasDataListener == false) { - /* Don't have any data for now or need to register - * for callback */ - LOG_DEBUG(LOG, "update data"); - _latestData = - _manager->getCommon().getZkAdapter()-> - getNodeData(_path, &_dataListener, - _getZkContext(), - &_latestStat); - _hasDataListener = true; - LOG_DEBUG(LOG, - "update data done, latest version %d", - _latestStat.version); - /* Since getNodeData is successful, the - * path must exist. */ - _deleted = false; - } - else { - /* Data is fresh since it is initialized and - * and have been updated by listener. - */ - } - /* Update active data to the same as the most - * recently acquire data. - */ - _activeData = _latestData; - _activeStat = _latestStat; - _initializedData = true; - _dirtyData = false; - LOG_DEBUG(LOG, "update set active version %d", - _activeStat.version); - } - res = 0; - } catch (const ZooKeeperException & e) { - /* May have ZNONODE exception if path does exist. */ - if (e.getZKErrorCode() == ZNONODE) { - LOG_DEBUG(LOG, "update %s exception %s", - _path.c_str(), e.what()); - /* Path does not exist, set _deleted, - * clear children information cache - */ - _deleted = true; - _clearChildren(); - res = -ENOENT; - } else { - LOG_ERROR(LOG, "update %s exception %s", - _path.c_str(), e.what()); - res = -EIO; - } - } - } - } - - LOG_DEBUG(LOG, "update returns %d", res); - return res; - } - /** - Process a data event. - - This method may: - - Invalidate the data cache. - - Invoke ZooKeeper to update the data cache and register a new - data watch so that the cache can be kept in-sync with the - ZooKeeper node's data. - - This method does not change the active data. Active data will be - changed to a later version by update() at the appropriate time. - \see update. - */ - void dataEventReceived(const ZKWatcherEvent & event) - { - bool reclaim = false; - int eventType = event.getType(); - int eventState = event.getState(); - - /* - IMPORTANT: - - Do not mark ZkFuseFile instance as deleted when a ZOO_DELETED_EVENT - is received without checking with ZooKeeper. An example of - problematic sequence would be: - - 1. Create node. - 2. Set data and watch. - 3. Delete node. - 4. Create node. - 5. Deleted event received. - - It is a bug to mark the ZkFuseFile instance as deleted after - step 5 because the node exists. - - Therefore, this method should always contact ZooKeeper to keep the - data cache (and deleted status) up-to-date if necessary. - */ - LOG_DEBUG(LOG, "dataEventReceived() path %s, type %d, state %d", - _path.c_str(), eventType, eventState); - { - AutoLock lock(_mutex); - - _hasDataListener = false; - /* If zombie, then invalidate cached data. - * This clears _initializedData and eliminate - * the need to get the latest data from ZooKeeper and - * re-register data watch. - */ - if (_isZombie() && _initializedData) { - LOG_DEBUG(LOG, "invalidate data"); - _clearData(); - } - else if ((_refCount - _openDirCount) > 0) { - /* Don't invalidate cached data because clients of currently - * open files don't expect the data to change from under them. - * If data acted upon by these clients have become stale, - * then the clients will get an error when ZkFuse attempts to - * flush dirty data. The clients will not get error - * notification if they don't modify the stale data. - * - * If data cache is cleared here, then the following code - * to update data cache and re-register data watch will not - * be executed and may result in the cached data being - * out-of-sync with ZooKeeper. - */ - LOG_WARN(LOG, - "%s data has changed while in-use, " - "type %d, state %d, refCount %d", - _path.c_str(), eventType, eventState, _refCount); - } - /* If cache was valid and still connected - * then get the latest data from ZooKeeper - * and re-register data watch. This is required to keep - * the data cache in-sync with ZooKeeper. - */ - if (_initializedData && - eventState == ZOO_CONNECTED_STATE - ) { - try { - LOG_DEBUG(LOG, "register data watcher"); - _latestData = - _manager->getCommon().getZkAdapter()-> - getNodeData(_path, &_dataListener, _getZkContext(), - &_latestStat); - _hasDataListener = true; - LOG_DEBUG(LOG, - "get data done, version %u, cversion %u done", - _latestStat.version, _latestStat.cversion); - _deleted = false; - } catch (const ZooKeeperException & e) { - if (e.getZKErrorCode() == ZNONODE) { - _deleted = true; - _clearChildren(); - } - LOG_ERROR(LOG, "dataEventReceived %s exception %s", - _path.c_str(), e.what()); - } - } - } - LOG_DEBUG(LOG, "dataEventReceived return %d", reclaim); - } - /** - Process a children event. - - This method may: - - Invalidate the children information cache. - - Invoke ZooKeeper to update the children cache and register a new - data watch so that the cache can be kept in-sync with the - ZooKeeper node's children information. - */ - void childrenEventReceived(const ZKWatcherEvent & event) - { - bool reclaim = false; - int eventType = event.getType(); - int eventState = event.getState(); - - LOG_DEBUG(LOG, "childrenEventReceived() path %s, type %d, state %d", - _path.c_str(), eventType, eventState); - { - AutoLock lock(_mutex); - - _hasChildrenListener = false; - /* If zombie or disconnected, then invalidate cached children - * information. This clears _initializedChildren and eliminate - * the need to get the latest children information and - * re-register children watch. - */ - if (_initializedChildren && - (_isZombie() || eventState != ZOO_CONNECTED_STATE)) { - LOG_DEBUG(LOG, "invalidate children"); - _clearChildren(); - } - else if (_initializedChildren) { - /* Keep cached children information so that we have some - * children information if get new children information - * fails. If there is failure, then on next open, - * update() will attempt again to get children information - * again because _hasChildrenListener will be false. - * - * If children information cache is cleared here, then - * the following code to update children information cache - * and re-register children watch will not be executed - * and may result in the cached children information being - * out-of-sync with ZooKeeper. - * - * The children cache will be cleared if unable to - * get children and re-establish watch. - */ - LOG_WARN(LOG, - "%s children has changed while in-use, " - "type %d, state %d, refCount %d", - _path.c_str(), eventType, eventState, _refCount); - } - /* If children cache was valid and still connected, - * then get the latest children information from ZooKeeper - * and re-register children watch. This is required to - * keep the children information cache in-sync with ZooKeeper. - */ - if (_initializedChildren && - eventState == ZOO_CONNECTED_STATE - ) { - /* Should try to keep the cache in-sync, register call - * callback again and get current children. - */ - try { - LOG_DEBUG(LOG, "update children"); - NodeNames children; - _manager->getCommon().getZkAdapter()-> - getNodeChildren(children, _path, - &_childrenListener, _getZkContext()); - _hasChildrenListener = true; - LOG_DEBUG(LOG, "update children done"); - _children.swap(children); - _deleted = false; - } catch (const ZooKeeperException & e) { - if (e.getZKErrorCode() == ZNONODE) { - _deleted = true; - _clearChildren(); - } - LOG_ERROR(LOG, "childrenEventReceived %s exception %s", - _path.c_str(), e.what()); - _children.clear(); - } - } - } - LOG_DEBUG(LOG, "childrenEventReceived returns %d", reclaim); - } - /** - Truncate or expand the size of the cached active data. - - This method only changes the size of the cached active data. - This change is committed to ZooKeeper when the cached data - is written to the ZooKeeper node by flush(). - - Return -EFBIG is the requested size exceeds the maximum. - - \return 0 if successful, otherwise negative errno. - \param size the requested size. - */ - int truncate(off_t size) - { - int res = 0; - - { - AutoLock lock(_mutex); - res = _truncate(size); - } - - return res; - } - /** - Copy range of active data into specified output buffer. - - \return if successful, return number of bytes copied, otherwise - return negative errno. - \param buf address of the output buffer. - \param size size of the output buffer and desired number of bytes to copy. - \param offset offset into active data to start copying from. - */ - int read(char *buf, size_t size, off_t offset) const - { - LOG_DEBUG(LOG, "read(size %zu, off_t %zu) path %s", - size, offset, _path.c_str()); - - int res = 0; - - { - AutoLock lock(_mutex); - if (!_initializedData) { - LOG_DEBUG(LOG, "not initialized"); - res = -EIO; - } - else { - off_t fileSize = _activeData.size(); - if (offset > fileSize) { - LOG_DEBUG(LOG, "offset > fileSize %zu", fileSize); - res = 0; - } - else { - if (offset + size > fileSize) { - size = fileSize - offset; - LOG_DEBUG(LOG, - "reducing read size to %zu for fileSize %zu", - size, fileSize); - } - copy(_activeData.begin() + offset, - _activeData.begin() + offset + size, - buf); - res = size; - } - } - } - - LOG_DEBUG(LOG, "read returns %d", res); - return res; - } - /** - Copy buffer content to active data. - - \return if successful, return number of bytes copied, otherwise - return negative errno. - \param buf address of the buffer. - \param size size of the input buffer and desired number of bytes to copy. - \param offset offset into a