The Storage Kit: BNode

Derived from: BStatable

Declared in: be/StorageKit/Node.h

Library: libbe.so


Overview

The BNode class gives you access to the data that a file system entity (a file, directory, or symbolic link) contains. There are two parts to this data:There's the "data portion" itself... ...and then there are the node's "attributes".

The content of the data portion depends on the node's flavor:

The content of the attributes, on the other hand, isn't qualified by the node's flavor: Any node can contain any set of attributes.


Nodes are Dumb

Keep in mind that the concept of a "node" designates the data parts (data and attributes) of a file system entity (a file, directory, or link). Contrast this with an "entry," which designates the entity's location within the file system: For example, you can write to a "node" (but not an entry), and you can rename an "entry" (but not a node).

This isn't just a conceptual crutch, it's the law...

Nodes really don't know where they're located.

For example, you can't ask a node for its name, or for the identity of its parent. This has some serious implications, the most important of which is...

If you need to store a reference to a file (or directory, or symbolic link), don't store the node--in other words, don't cache the BNode object. Instead, store the information that you used to create the BNode (typically, a pathname or entry_ref structure).

Now that we've got that straight, we'll relax the rules a bit:

BDirectory objects are node/entry hybrids. A BDirectory does know its own name (and parent, and so on).

This doesn't really change the "store the info" rule. Even if you're dealing exclusively with BDirectory objects, you should keep the generative information around. The primary reason for this is...


The "Node Pool" is Limited (File Descriptors)

Every BNode object consumes a "file descriptor." Your application can only maintain 128 file descriptors at a time. Because of this limit, you shouldn't keep BNodes around that you don't need. Keep in mind that BEntry objects also consumes file descriptors (one per object).


Derived Classes and their Uses

BNode has three derived classes: BFile, BDirectory, and BSymLink. The derived classes define functions that let you access the node's data portion in the appropriate style; for example...

If you want to (sensibly) look at a file or directory's data portion, you must create an instance of the appropriate derived class. In other words, if you want to browse a directory, you have to create a BDirectory instance; if you want to write to a file, you create a BFile. Symbolic links are

Be aware that it's not (always) an error to create an instance of the "wrong" derived class; setting a BFile to a symbolic link, for example, will traverse the link such that the BFile opens the file that the symbolic link is linked to. See the individual derived class specifications for more information.


BNode Instances

In practice, you almost always want to create an instance of one of the BNode-derived classes; but if, for whatever reason, you find yourself holding a BNode instance, here's what you'll be able to do with it:

Read and write attributes. The attribute-accessing functions ( ReadAttr(), WriteAttr(), and so on) are general--where they work at all, they work without regard for the node's flavor. Thus, you don't need an instance of a specific derived class to read and write attributes. (Keep in mind that only files and directories --but not symbolic links--can contain attributes.)

Get stat information. The BStatable functions can be invoked on any flavor of node (the one flavor-specific restriction is that BStatable::SetSize() doesn't work on directories).

Lock the node. This prevents other "agents" (other objects, other apps, the user) from accessing certain parts of the node (where the "parts" depends on the node's flavor). See Node Locking.


Converting a BNode to an Instance of a Derived Class

This section describes situations and presents solutions to problems that are a bit esoteric. If you never create direct instances of BNode (and you never have to), then you should skip this and go to Node Locking .

But there may be times when you find yourself holding on to a BNode (instance) that you want to convert into a BFile, BDirectory, or BSymLink...

Currently, unfortunately, you can't go directly from a BNode instance to an instance of BFile, BDirectory, or BSymLink.

There are solutions, however...

Converting to BDirectory

Converting from a BNode to a BDirectory, while not transparent, is pretty simple:

Grab the node_ref out of the BNode and pass it to the BDirectory constructor or SetTo() function. Regard this (admittedly overblown) example function:

   void Node2Directory(BNode *node, BDirectory *dir)
   {
      node_ref nref;
   
      /* For the sake of good form, we'll check the node flavor 
       * before converting (but see the next comment).  
        */
      if (!node || !dir || !node.IsDirectory()) {
         dir.Unset();
         return;
      }
   
      node.GetNodeRef(&nref);
   
      /* Set the BDirectory.  If nref isn't a directory node,
       * the SetTo() will fail.  Thus, our previous 
       * IsDirectory() call isn't necessary.
       */
       dir.SetTo(&nref);
   }

Converting to BFile or BSymLink

Converting a BNode instance to a BFile or BSymLink isn't as neat as the foregoing. Instead, you have to cache the information that you used to initialize the BNode (in the first place) and then reuse it to create the BFile or BSymLink.

For example, let's say you receive an entry_ref . You turn it into a BNode, but then decide you need the data-writing power of a BFile. If, in the meantime, you lost the original entry_ref, you're sunk--there's nothing you can do.


Node Locking

Another feature provided by the BNode class is "node locking": Through BNode's Lock() function you can restrict access to the node. The lock is removed when Unlock() is called, or when the BNode object is deleted.

Node-locking is somewhat flavor-sensitive:

  • Locking a file or symbolic link locks the data and the attributes.
  • Locking a directory locks the attributes only.
  • Locking a node does not lock the entry:

    You can't "lock out" entry operations, such as rename, move, and remove. Even if you have a node locked, the entry that acts as the "container" for that node could disappear.

    In general, you should try to avoid locking your nodes. If you must lock, try to make it brief. The primary reason (and, pretty much, the only reason) to lock is if separate elements in the data and/or attributes must be kept in a consistent state. In such a case, you should hold the lock just long enough to ensure consistency.

    You shouldn't use locks to "privatize" data. Locking isn't meant to be used as a "heightened permissions bit."


    Constructor and Destructor


    BNode()

    
          BNode(const entry_ref *ref)
          BNode(const BEntry *entry)
          BNode(const char *path)
          BNode(const BDirectory *dir, const char *path)
    
          BNode()
          BNode(const BNode &node)

    Creates a new BNode object that's initialized to represent a specific file system node. To retrieve the status of the initialization, call InitCheck() immediately after constructing the object:

       BNode node("/boot/lbj/FidoOnFire.gif");
       if (node.InitCheck() != B_NO_ERROR)
          /* The object wasn't initialized. */

    A successfully initialized BNode object creates a "file descriptor" through which the object reads and writes the node's data and attributes. You can only have 128 file descriptors at a time (per application). The object's file descriptor is closed when the object is deleted, reset (through SetTo() ), or unset (Unset()).


    ~BNode()

    
          virtual ~BNode()

    Frees the object's file descriptor and destroys the object.


    Member Functions


    GetAttrInfo()

    
          status_t GetAttrInfo(const char *attr, attr_info *buf) const

    <forthcoming>


    GetNextAttrName(), RewindAttrs()

    
          status_t GetNextAttrName(char *buf) 
    
          status_t RewindAttrs(void) 

    <forthcoming>


    InitCheck()

    
          status_t InitCheck(void) const

    Returns the status of the most recent initialization.

    RETURN VALUES

  • B_NO_ERROR. The object was successfully initialized.
  • B_NO_INIT. The object hasn't been initialized yet.
  • See the SetTo() function for a list other return values.

  • Lock(), Unlock()

    
          status_t Lock(void)
          status_t Unlock(void)

    Locks and unlocks the BNode's node. While the node is locked, no other object can access the node's data or attributes. In the case of a directory, only the attributes are locked.

    See "Node Locking," above.

    RETURN VALUES

  • <forthcoming>

  • ReadAttr(), WriteAttr(), RemoveAttr()

    
          ssize_t ReadAttr(const char *name,
             type_code type,
             off_t offset, 
             void *buffer, 
             size_t length)
    
          ssize_t WriteAttr(const char *name,
             type_code type,
             off_t offset, 
             const void *buffer, 
             size_t length)
    
          status_t  RemoveAttr(const char *attr)

    <forthcoming>


    SetTo(), Unset()

    
          status_t SetTo(const entry_ref *ref)
          status_t SetTo(const BEntry *entry)
          status_t SetTo(const char *path)
          status_t SetTo(BDirectory *dir, const char *path)
    
          void Unset(void)

    Closes the BNode's current file descriptor and opens it on the node (of the entry) that's designated by the arguments.

    BNodes never traverse symbolic links. If the designated entry is a symbolic link, the BNode will open the link's node.

    Unset() closes the BNode's file descriptor and sets InitCheck() to B_NO_INIT .

    RETURN VALUES

  • B_NO_ERROR. All is well.
  • ENOENT. The designated entry doesn't exist.
  • B_BAD_VALUE. Uninitialized or malformed argument.

  • Operators


    = (assignment)

    
          BNode& operator=(const BNode &node)

    In the expression

       BNode a = b;

    BNode a is initialized to refer to the same node as b. To gauge the success of the assignment, you should call InitCheck() immediately afterwards. It's safe to assign a BNode to itself.


    ==, != (comparison)

    
             bool operator==(const BNode &node) const
             bool operator!=(const BNode &node) const

    Two BNode objects are said to be equal if they're set to the same node, or if they're both B_NO_INIT.






    The Be Book, HTML Edition, for Developer Release 9 of the Be OS.

    Copyright © 1997 Be, Inc. All rights reserved.

    Be, the Be logo, BeBox, BeOS, BeWare, and GeekPort are trademarks of Be, Inc.

    Last modified