http://git-wip-us.apache.org/repos/asf/zookeeper/blob/eab8c1eb/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 <config.h>
-#endif
-
-#undef _GNU_SOURCE
-#define _GNU_SOURCE
-
-extern "C" {
-#include <fuse.h>
-#include <ulockmgr.h>
-}
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <dirent.h>
-#include <errno.h>
-#include <sys/time.h>
-#ifdef HAVE_SETXATTR
-#include <sys/xattr.h>
-#endif
-
-#include <getopt.h>
-
-#include <iostream>
-#include <sstream>
-#include <map>
-#include <string>
-#include <boost/utility.hpp>
-#include <boost/weak_ptr.hpp>
-
-#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<std::string> 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<ZooKeeperAdapter> 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<std::string, Handle> Map;
- /**
- Typedef of std::vector used to map handle to ZkFuseFile instances.
- */
- typedef std::vector<ZkFuseFilePtr> Files;
- /**
- Typedef of std::vector used to hold unused handles.
- */
- typedef std::vector<Handle> FreeList;
- /**
- Typedef of boost::weak_ptr to the ZkFuseHandleManager instance.
- */
- typedef boost::weak_ptr<ZkFuseHandleManager> 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<ZkFuseHandleManager> 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 "<key>: <value>" 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 "<key>: <value>" 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<ZkFuseFile *>(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<ZkFuseFile *>(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
<TRUNCATED>
|