root/src/add-ons/kernel/file_systems/netfs/server/SecurityContext.cpp
// SecurityContext.cpp

#include <errno.h>
#include <string.h>
#include <sys/stat.h>

#include <AutoDeleter.h>
#include <AutoLocker.h>
#include <Entry.h>
#include <HashMap.h>
#include <Message.h>
#include <Path.h>

#include "Compatibility.h"
#include "Node.h"
#include "SecurityContext.h"
#include "UserSecurityContext.h"

typedef AutoLocker<SecurityContext> ContextLocker;

// get_node_ref_for_path
static
status_t
get_node_ref_for_path(const char* path, node_ref* ref)
{
        if (!path || !ref)
                return B_BAD_VALUE;
        struct stat st;
        if (lstat(path, &st) < 0)
                return errno;
        ref->device = st.st_dev;
        ref->node = st.st_ino;
        return B_OK;
}

// #pragma mark -
// #pragma mark ----- User -----

// constructor
User::User()
        : BReferenceable(),
          BArchivable(),
          fName(),
          fPassword()
{
}

// constructor
User::User(BMessage* archive)
        : BReferenceable(),
          BArchivable(archive),
          fName(),
          fPassword()
{
        Unarchive(archive);
}

// destructor
User::~User()
{
}

// Archive
status_t
User::Archive(BMessage* archive, bool deep) const
{
        if (!archive)
                return B_BAD_VALUE;
        // name
        status_t error = B_OK;
        if (error == B_OK && fName.GetLength() > 0)
                error = archive->AddString("name", fName.GetString());
        // password
        if (error == B_OK && fPassword.GetLength() > 0)
                error = archive->AddString("password", fPassword.GetString());
        return error;
}

// Instantiate
BArchivable*
User::Instantiate(BMessage* archive)
{
        if (!validate_instantiation(archive, "User"))
                return NULL;
        return new(std::nothrow) User(archive);
}

// Init
status_t
User::Init(const char* name, const char* password)
{
        if (!name)
                return B_BAD_VALUE;
        if (!fName.SetTo(name))
                return B_NO_MEMORY;
        if (password && !fPassword.SetTo(password))
                return B_NO_MEMORY;
        return B_OK;
}

// InitCheck
status_t
User::InitCheck() const
{
        if (fName.GetLength() == 0)
                return B_NO_INIT;
        return B_OK;
}

// Unarchive
status_t
User::Unarchive(const BMessage* archive)
{
        // name
        const char* name;
        if (archive->FindString("name", &name) != B_OK)
                return B_BAD_DATA;
        fName.SetTo(name);
        // password
        const char* password;
        if (archive->FindString("password", &password) == B_OK)
                fPassword.SetTo(password);
        else
                fPassword.Unset();
        return B_OK;
}

// GetName
const char*
User::GetName() const
{
        return fName.GetString();
}

// GetPassword
const char*
User::GetPassword() const
{
        return fPassword.GetString();
}


// #pragma mark -
// #pragma mark ----- Share -----

// constructor
Share::Share()
        : BReferenceable(),
          BArchivable(),
          fName(),
          fNodeRef(),
          fPath()
{
}

// constructor
Share::Share(BMessage* archive)
        : BReferenceable(),
          BArchivable(archive),
          fName(),
          fNodeRef(),
          fPath()
{
        Unarchive(archive);
}

// destructor
Share::~Share()
{
}

// Archive
status_t
Share::Archive(BMessage* archive, bool deep) const
{
        if (!archive)
                return B_BAD_VALUE;
        // name
        status_t error = B_OK;
        if (error == B_OK && fName.GetLength() > 0)
                error = archive->AddString("name", fName.GetString());
        // path
        if (error == B_OK && fPath.GetLength() > 0)
                error = archive->AddString("path", fPath.GetString());
        return error;
}

// Instantiate
BArchivable*
Share::Instantiate(BMessage* archive)
{
        if (!validate_instantiation(archive, "Share"))
                return NULL;
        return new(std::nothrow) Share(archive);
}

// Init
status_t
Share::Init(const char* name, const node_ref& ref, const char* path)
{
        // check params
        if (!name)
                return B_BAD_VALUE;
        // if a path is not given, retrieve it
        BPath localPath;
        if (!path) {
                entry_ref entryRef(ref.device, ref.node, ".");
                status_t error = localPath.SetTo(&entryRef);
                if (error != B_OK)
                        return error;
                path = localPath.Path();
        }
        // set the attributes
        if (!fName.SetTo(name))
                return B_NO_MEMORY;
        if (!fPath.SetTo(path))
                return B_NO_MEMORY;
        fNodeRef = ref;
        return B_OK;
}

// Init
status_t
Share::Init(const char* name, const char* path)
{
        if (!name || !path)
                return B_BAD_VALUE;
        node_ref nodeRef;
        if (get_node_ref_for_path(path, &nodeRef) != B_OK) {
                nodeRef.device = -1;
                nodeRef.node = -1;
        }
        return Init(name, nodeRef, path);
}

// InitCheck
status_t
Share::InitCheck() const
{
        if (fName.GetLength() == 0 || fPath.GetLength() == 0)
                return B_NO_INIT;
        return B_OK;
}

// Unarchive
status_t
Share::Unarchive(const BMessage* archive)
{
        // name
        const char* name = NULL;
        if (archive->FindString("name", &name) != B_OK)
                return B_BAD_DATA;
        // path
        const char* path = NULL;
        if (archive->FindString("path", &path) != B_OK)
                return B_BAD_DATA;
        return Init(name, path);
}

// GetName
const char*
Share::GetName() const
{
        return fName.GetString();
}

// DoesExist
bool
Share::DoesExist() const
{
        return (fNodeRef.device >= 0);
}

// GetNodeRef
const node_ref&
Share::GetNodeRef() const
{
        return fNodeRef;
}

// GetVolumeID
dev_t
Share::GetVolumeID() const
{
        return fNodeRef.device;
}

// GetNodeID
ino_t
Share::GetNodeID() const
{
        return fNodeRef.node;
}

// GetPath
const char*
Share::GetPath() const
{
        return fPath.GetString();
}


// #pragma mark -
// #pragma mark ----- SecurityContext -----

// UserMap
struct SecurityContext::UserMap : HashMap<HashString, User*> {
};

// ShareMap
struct SecurityContext::ShareMap : HashMap<HashString, Share*> {
};

// UserPath
struct SecurityContext::UserPath {
        UserPath() {}

        UserPath(const char* path, User* user)
                : path(path),
                  user(user)
        {
        }

        UserPath(const UserPath& other)
                : path(other.path),
                  user(other.user)
        {
        }

        uint32 GetHashCode() const
        {
#ifdef B_HAIKU_64_BIT
                uint64 v = (uint64)user;
                return (path.GetHashCode() * 31) + ((uint32)(v >> 32) ^ (uint32)v);
#else
                return path.GetHashCode() * 31 + (uint32)user;
#endif
        }

        UserPath& operator=(const UserPath& other)
        {
                path = other.path;
                user = other.user;
                return *this;
        }

        bool operator==(const UserPath& other) const
        {
                return (path == other.path && user == other.user);
        }

        bool operator!=(const UserPath& other) const
        {
                return !(*this == other);
        }

        HashString      path;
        User*           user;
};

// PermissionMap
struct SecurityContext::PermissionMap
        : HashMap<SecurityContext::UserPath, Permissions> {
};

// NodePathMap
struct SecurityContext::NodePathMap : HashMap<NodeRef, HashString> {
};

// PathNodeMap
struct SecurityContext::PathNodeMap : HashMap<HashString, NodeRef> {
};

// constructor
SecurityContext::SecurityContext()
        : BArchivable(),
          BLocker("security context"),
          fUsers(new(std::nothrow) UserMap),
          fShares(new(std::nothrow) ShareMap),
          fPermissions(new(std::nothrow) PermissionMap),
          fNode2Path(new(std::nothrow) NodePathMap),
          fPath2Node(new(std::nothrow) PathNodeMap)
{
}

// constructor
SecurityContext::SecurityContext(BMessage* archive)
        : BArchivable(archive),
          fUsers(new(std::nothrow) UserMap),
          fShares(new(std::nothrow) ShareMap),
          fPermissions(new(std::nothrow) PermissionMap),
          fNode2Path(new(std::nothrow) NodePathMap),
          fPath2Node(new(std::nothrow) PathNodeMap)
{
        if (InitCheck() != B_OK)
                return;
        status_t error = B_OK;

        // users
        BMessage userArchive;
        for (int32 i = 0;
                 archive->FindMessage("users", i, &userArchive) == B_OK;
                 i++) {
                User tmpUser;
                error = tmpUser.Unarchive(&userArchive);
                if (error != B_OK)
                        return;
                error = AddUser(tmpUser.GetName(), tmpUser.GetPassword());
                if (error != B_OK)
                        return;
        }

        // shares
        BMessage shareArchive;
        for (int32 i = 0;
                 archive->FindMessage("shares", i, &shareArchive) == B_OK;
                 i++) {
                Share tmpShare;
                error = tmpShare.Unarchive(&shareArchive);
                if (error != B_OK)
                        return;
                error = AddShare(tmpShare.GetName(), tmpShare.GetPath());
                if (error != B_OK)
                        return;
        }

        // permissions
        BMessage permissionsArchive;
        if (archive->FindMessage("permissions", &permissionsArchive) != B_OK)
                return;
        #ifdef HAIKU_TARGET_PLATFORM_DANO
                const char* userName;
        #else
                char* userName;
        #endif
        type_code type;
        for (int32 userIndex = 0;
                 permissionsArchive.GetInfo(B_MESSAGE_TYPE, userIndex, &userName, &type)
                        == B_OK;
                 userIndex++) {
                User* user = FindUser(userName);
                if (!user)
                        return;
                BReference<User> userReference(user, true);
                error = permissionsArchive.FindMessage(userName, &userArchive);
                if (error != B_OK)
                        return;

                // got a user: iterate through its permissions
                #ifdef HAIKU_TARGET_PLATFORM_DANO
                        const char* path;
                #else
                        char* path;
                #endif
                for (int32 i = 0;
                         userArchive.GetInfo(B_INT32_TYPE, i, &path, &type) == B_OK;
                         i++) {
                        uint32 permissions;
                        error = userArchive.FindInt32(path, (int32*)&permissions);
                        if (error == B_OK)
                                error = SetNodePermissions(path, user, permissions);
                }
        }
}

// destructor
SecurityContext::~SecurityContext()
{
        // remove all user references
        for (UserMap::Iterator it = fUsers->GetIterator(); it.HasNext();) {
                User* user = it.Next().value;
                user->ReleaseReference();
        }

        // remove all share references
        for (ShareMap::Iterator it = fShares->GetIterator(); it.HasNext();) {
                Share* share = it.Next().value;
                share->ReleaseReference();
        }

        delete fUsers;
        delete fShares;
        delete fPermissions;
        delete fNode2Path;
        delete fPath2Node;
}

// Archive
status_t
SecurityContext::Archive(BMessage* archive, bool deep) const
{
        if (!archive)
                return B_BAD_VALUE;
        status_t error = B_OK;
        ContextLocker _(const_cast<SecurityContext*>(this));

        // users
        int32 userCount = fUsers->Size();
        for (UserMap::Iterator it = fUsers->GetIterator(); it.HasNext();) {
                User* user = it.Next().value;
                BMessage userArchive;
                error = user->Archive(&userArchive, deep);
                if (error != B_OK)
                        return error;
                error = archive->AddMessage("users", &userArchive);
                if (error != B_OK)
                        return error;
        }

        // shares
        for (ShareMap::Iterator it = fShares->GetIterator(); it.HasNext();) {
                Share* share = it.Next().value;
                BMessage shareArchive;
                error = share->Archive(&shareArchive, deep);
                if (error != B_OK)
                        return error;
                error = archive->AddMessage("shares", &shareArchive);
                if (error != B_OK)
                        return error;
        }

        // permissions
        // we slice them per user
        BMessage* tmpUserArchives = new(std::nothrow) BMessage[userCount];
        if (!tmpUserArchives)
                return B_NO_MEMORY;
        ArrayDeleter<BMessage> deleter(tmpUserArchives);
        HashMap<HashKeyPointer<User*>, BMessage*> userArchives;
        int32 i = 0;
        for (UserMap::Iterator it = fUsers->GetIterator(); it.HasNext();) {
                User* user = it.Next().value;
                error = userArchives.Put(user, tmpUserArchives + i);
                if (error != B_OK)
                        return error;
                i++;
        }

        // fill the per user archives
        for (PermissionMap::Iterator it = fPermissions->GetIterator();
                 it.HasNext();) {
                PermissionMap::Entry entry = it.Next();
                BMessage* userArchive = userArchives.Get(entry.key.user);
                error = userArchive->AddInt32(entry.key.path.GetString(),
                        entry.value.GetPermissions());
                if (error != B_OK)
                        return error;
        }

        // put the user permissions together
        BMessage permissionsArchive;
        for (UserMap::Iterator it = fUsers->GetIterator(); it.HasNext();) {
                User* user = it.Next().value;
                error = permissionsArchive.AddMessage(user->GetName(),
                        userArchives.Get(user));
                if (error != B_OK)
                        return error;
        }
        error = archive->AddMessage("permissions", &permissionsArchive);
        if (error != B_OK)
                return error;
        return B_OK;
}

// Instantiate
BArchivable*
SecurityContext::Instantiate(BMessage* archive)
{
        if (!validate_instantiation(archive, "SecurityContext"))
                return NULL;
        return new(std::nothrow) SecurityContext(archive);
}


// InitCheck
status_t
SecurityContext::InitCheck() const
{
        if (!fUsers || !fShares || !fPermissions || !fNode2Path || !fPath2Node)
                return B_NO_MEMORY;

        if (fUsers->InitCheck() != B_OK)
                return fUsers->InitCheck();

        if (fShares->InitCheck() != B_OK)
                return fShares->InitCheck();

        if (fPermissions->InitCheck() != B_OK)
                return fPermissions->InitCheck();

        if (fNode2Path->InitCheck() != B_OK)
                return fNode2Path->InitCheck();

        if (fPath2Node->InitCheck() != B_OK)
                return fPath2Node->InitCheck();

        return B_OK;
}

// AddUser
//
// The caller gets a reference, if _user is not NULL.
status_t
SecurityContext::AddUser(const char* name, const char* password, User** _user)
{
        if (!name)
                return B_BAD_VALUE;

        // check, if the user does already exist
        ContextLocker _(this);
        if (fUsers->Get(name))
                return B_BAD_VALUE;

        // create a the user
        User* user = new(std::nothrow) User;
        if (!user)
                return B_NO_MEMORY;
        BReference<User> userReference(user, true);
        status_t error = user->Init(name, password);
        if (error != B_OK)
                return error;

        // add the user
        error = fUsers->Put(name, user);
        if (error != B_OK)
                return error;

        userReference.Detach();
        if (_user) {
                *_user = user;
                user->AcquireReference();
        }
        return B_OK;
}

// RemoveUser
//
// The caller gets a reference, if _user is not NULL.
status_t
SecurityContext::RemoveUser(const char* name, User** _user)
{
        if (!name)
                return B_BAD_VALUE;

        ContextLocker _(this);

        // get the user
        User* user = FindUser(name);
        if (!user)
                return B_ENTRY_NOT_FOUND;
        BReference<User> userReference(user, true);

        // remove it
        status_t error = RemoveUser(user);
        if (error == B_OK && _user) {
                *_user = user;
                user->AcquireReference();
        }

        return error;
}

// RemoveUser
status_t
SecurityContext::RemoveUser(User* user)
{
        if (!user)
                return B_BAD_VALUE;

        ContextLocker _(this);

        // find and remove it
        if (fUsers->Get(user->GetName()) != user)
                return B_BAD_VALUE;
        fUsers->Remove(user->GetName());

        // remove all permission entries for this user
        for (PermissionMap::Iterator it = fPermissions->GetIterator();
                 it.HasNext();) {
                PermissionMap::Entry entry = it.Next();
                if (entry.key.user == user)
                        fPermissions->Remove(it);
        }

        // surrender our user reference
        user->ReleaseReference();

        return B_OK;
}

// FindUser
//
// The caller gets a reference.
User*
SecurityContext::FindUser(const char* name)
{
        if (!name)
                return NULL;

        ContextLocker _(this);
        User* user = fUsers->Get(name);
        if (user)
                user->AcquireReference();
        return user;
}

// AuthenticateUser
//
// The caller gets a reference.
status_t
SecurityContext::AuthenticateUser(const char* name, const char* password,
        User** _user)
{
        if (!_user)
                return B_BAD_VALUE;

        // find user
        ContextLocker _(this);
        User* user = FindUser(name);
        if (!user)
                return B_PERMISSION_DENIED;
        BReference<User> userReference(user, true);

        // check password
        if (user->GetPassword()) {
                if (!password || strcmp(user->GetPassword(), password) != 0)
                        return B_PERMISSION_DENIED;
        } else if (password)
                return B_PERMISSION_DENIED;

        *_user = user;
        userReference.Detach();
        return B_OK;
}

// CountUsers
int32
SecurityContext::CountUsers()
{
        ContextLocker _(this);
        return fUsers->Size();
}

// GetUsers
status_t
SecurityContext::GetUsers(BMessage* users)
{
        if (!users)
                return B_BAD_VALUE;

        ContextLocker _(this);

        // iterate through all users and add their names to the message
        for (UserMap::Iterator it = fUsers->GetIterator(); it.HasNext();) {
                User* user = it.Next().value;
                status_t error = users->AddString("users", user->GetName());
                if (error != B_OK)
                        return error;
        }

        return B_OK;
}

// AddShare
//
// The caller gets a reference, if _share is not NULL.
status_t
SecurityContext::AddShare(const char* name, const node_ref& ref, Share** _share)
{
        if (!name)
                return B_BAD_VALUE;

        // check, if the share does already exist
        ContextLocker _(this);
        if (fShares->Get(name))
                return B_BAD_VALUE;

        // create a the share
        Share* share = new(std::nothrow) Share;
        if (!share)
                return B_NO_MEMORY;
        BReference<Share> shareReference(share, true);
        status_t error = share->Init(name, ref);
        if (error != B_OK)
                return error;

        // add the share
        error = fShares->Put(name, share);
        if (error != B_OK)
                return error;

        shareReference.Detach();
        if (_share) {
                *_share = share;
                share->AcquireReference();
        }
        return B_OK;
}

// AddShare
//
// The caller gets a reference, if _share is not NULL.
status_t
SecurityContext::AddShare(const char* name, const char* path, Share** _share)
{
        if (!name)
                return B_BAD_VALUE;

        // check, if the share does already exist
        ContextLocker _(this);
        if (fShares->Get(name))
                return B_BAD_VALUE;

        // create a the share
        Share* share = new(std::nothrow) Share;
        if (!share)
                return B_NO_MEMORY;
        BReference<Share> shareReference(share, true);
        status_t error = share->Init(name, path);
        if (error != B_OK)
                return error;

        // add the share
        error = fShares->Put(name, share);
        if (error != B_OK)
                return error;

        shareReference.Detach();
        if (_share) {
                *_share = share;
                share->AcquireReference();
        }
        return B_OK;
}

// RemoveShare
//
// The caller gets a reference, if _share is not NULL.
status_t
SecurityContext::RemoveShare(const char* name, Share** _share)
{
        if (!name)
                return B_BAD_VALUE;

        ContextLocker _(this);

        // get the share
        Share* share = FindShare(name);
        if (!share)
                return B_ENTRY_NOT_FOUND;
        BReference<Share> shareReference(share, true);

        // remove it
        status_t error = RemoveShare(share);
        if (error == B_OK && _share) {
                *_share = share;
                share->AcquireReference();
        }

        return error;
}

// RemoveShare
status_t
SecurityContext::RemoveShare(Share* share)
{
        if (!share)
                return B_BAD_VALUE;

        ContextLocker _(this);

        // find and remove it
        if (fShares->Get(share->GetName()) != share)
                return B_BAD_VALUE;
        fShares->Remove(share->GetName());

        // surrender our share reference
        share->ReleaseReference();

        return B_OK;
}

// FindShare
//
// The caller gets a reference.
Share*
SecurityContext::FindShare(const char* name)
{
        if (!name)
                return NULL;

        ContextLocker _(this);
        Share* share = fShares->Get(name);
        if (share)
                share->AcquireReference();
        return share;
}

// CountShares
int32
SecurityContext::CountShares()
{
        ContextLocker _(this);
        return fShares->Size();
}

// GetShares
status_t
SecurityContext::GetShares(BMessage* shares)
{
        if (!shares)
                return B_BAD_VALUE;

        ContextLocker _(this);

        // iterate through all shares and add their names to the message
        for (ShareMap::Iterator it = fShares->GetIterator(); it.HasNext();) {
                Share* share = it.Next().value;
                // add name
                status_t error = shares->AddString("shares", share->GetName());
                if (error != B_OK)
                        return error;

                // add path
                error = shares->AddString("paths", share->GetPath());
                if (error != B_OK)
                        return error;
        }

        return B_OK;
}

// SetNodePermissions
status_t
SecurityContext::SetNodePermissions(const node_ref& ref, User* user,
        Permissions permissions)
{
        if (!user)
                return B_BAD_VALUE;

        ContextLocker _(this);
        // check, whether we know the user
        if (fUsers->Get(user->GetName()) != user)
                return B_BAD_VALUE;

        HashString path;
        status_t error = _AddNodePath(ref, &path);
        if (error != B_OK)
                return error;
        return fPermissions->Put(UserPath(path.GetString(), user), permissions);
}

// SetNodePermissions
status_t
SecurityContext::SetNodePermissions(const char* path, User* user,
        Permissions permissions)
{
        if (!user || !path)
                return B_BAD_VALUE;

        ContextLocker _(this);
        // check, whether we know the user
        if (fUsers->Get(user->GetName()) != user)
                return B_BAD_VALUE;

        _AddNodePath(path);
        return fPermissions->Put(UserPath(path, user), permissions);
}

// ClearNodePermissions
void
SecurityContext::ClearNodePermissions(const node_ref& ref, User* user)
{
        ContextLocker _(this);
        HashString path;
        status_t error = _AddNodePath(ref, &path);
        if (error != B_OK)
                return;

        if (user) {
                fPermissions->Remove(UserPath(path.GetString(), user));
        } else {
                for (UserMap::Iterator it = fUsers->GetIterator(); it.HasNext();)
                        fPermissions->Remove(UserPath(path.GetString(), it.Next().value));
        }
}

// ClearNodePermissions
void
SecurityContext::ClearNodePermissions(const char* path, User* user)
{
        if (!path)
                return;

        ContextLocker _(this);
        _AddNodePath(path);

        if (user) {
                fPermissions->Remove(UserPath(path, user));
        } else {
                for (UserMap::Iterator it = fUsers->GetIterator(); it.HasNext();)
                        fPermissions->Remove(UserPath(path, it.Next().value));
        }
}

// GetNodePermissions
Permissions
SecurityContext::GetNodePermissions(const node_ref& ref, User* user)
{
        if (!user)
                return Permissions();

        ContextLocker _(this);
        HashString path;
        status_t error = _AddNodePath(ref, &path);
        if (error != B_OK)
                return Permissions();

        return fPermissions->Get(UserPath(path.GetString(), user));
}

// GetNodePermissions
Permissions
SecurityContext::GetNodePermissions(const char* path, User* user)
{
        if (!user || !path)
                return Permissions();

        ContextLocker _(this);
        _AddNodePath(path);

        return fPermissions->Get(UserPath(path, user));
}

// GetUserSecurityContext
status_t
SecurityContext::GetUserSecurityContext(User* user,
        UserSecurityContext* userContext)
{
        if (!userContext)
                return B_BAD_VALUE;

        status_t error = userContext->Init(user);
        if (error != B_OK)
                return error;

        // iterate through all permission entries and add the ones whose user
        // matches
        ContextLocker _(this);
        for (PermissionMap::Iterator it = fPermissions->GetIterator();
                 it.HasNext();) {
                PermissionMap::Entry entry = it.Next();
                node_ref ref;
                if (entry.key.user == user
                        && _GetNodeForPath(entry.key.path.GetString(), &ref)) {
                        error = userContext->AddNode(ref.device, ref.node, entry.value);
                        if (error != B_OK)
                                return error;
                }
        }
        return B_OK;
}

// _AddNodePath
status_t
SecurityContext::_AddNodePath(const char* path, node_ref* _ref)
{
        if (!fPath2Node->ContainsKey(path)) {
                node_ref ref;
                status_t error = get_node_ref_for_path(path, &ref);
                if (error == B_OK)
                        error = _EnterNodePath(path, ref);
                if (error != B_OK)
                        return error;
        }

        if (_ref)
                *_ref = fPath2Node->Get(path);
        return B_OK;
}

// _AddNodePath
status_t
SecurityContext::_AddNodePath(const node_ref& ref, HashString* _path)
{
        if (!fNode2Path->ContainsKey(ref)) {
                BPath path;
                entry_ref entryRef(ref.device, ref.node, ".");
                status_t error = path.SetTo(&entryRef);
                if (error == B_OK)
                        error = _EnterNodePath(path.Path(), ref);
                if (error != B_OK)
                        return error;
        }

        if (_path)
                *_path = fNode2Path->Get(ref);
        return B_OK;
}

// _EnterNodePath
status_t
SecurityContext::_EnterNodePath(const char* path, const node_ref& ref)
{
        status_t error = fNode2Path->Put(ref, path);
        if (error == B_OK) {
                error = fPath2Node->Put(path, ref);
                if (error != B_OK)
                        fNode2Path->Remove(ref);
        }
        return error;
}

// _GetNodeForPath
bool
SecurityContext::_GetNodeForPath(const char* path, node_ref* ref)
{
        if (path && fPath2Node->ContainsKey(path)) {
                if (ref)
                        *ref = fPath2Node->Get(path);
                return true;
        }
        return false;
}