BEntry

Derived from: BStatable

Declared in: be/storage/Entry.h

Library: libbe.so

[method summary]

The BEntry class defines objects that represent "locations" in the file system hierarchy. Each location (or entry) is given as a name within a directory. For example, when you create a BEntry thus...

   BEntry entry("/boot/home/fido");

...you're telling the BEntry object to represent the location of the file called fido within the directory "/boot/home".

A BEntry doesn't care whether the entry you tell it to represent is a plain file, a directory, or a symbolic link--it doesn't even care if the entry even exists (but we'll get to that later in "Abstract Entries"):

The most important implication of this is the object's attitude towards data. BEntries don't know how to operate on data. You can't use a BEntry to read or write a file's data or attributes. For data operations, you have to turn your BEntry into a BNode.

Nonetheless, it's often convenient to speak of a BEntry as having data; for example, the phrase "the entry's data" really means "the data that lies in the file that's located by the entry."


Talents and Abilities

A properly initialized BEntry object (we'll get to the rules of initialization later) knows the following:

A BEntry can do these things:

As mentioned above, the most important thing that a BEntry can't do is access its own data: A BEntry can't read or write data or attributes. To do these things you need a BNode object.

(Actually, this isn't entirely true: A BEntry can set the size of its data through the BStatable::SetSize() function. The function only works on plain files.)


Initializing and Traversing

To initialize a BEntry, you have to tell it which entry to represent; in other words, you have to identify a directory and a name. You can initialize a BEntry object directly...

Or you can have some other object initialize your BEntry for you, by passing the BEntry as an argument to...

In all cases (except the assignment operator) you're asked if you want to "traverse" the entry during initialization. Traversal is used to "resolve" symbolic links:

For example, let's say /boot/home/fidoLink is linked to /fido, to wit:

   $ cd /boot/home
   $ ln -s ./fido fidoLink

Now let's make a traversed BEntry for fidoLink:

   /* The second argument is the traversal bool. */
   BEntry entry("/boot/home/fidoLink", true);

If we ask for the entry's pathname...

   BPath path;
   entry.GetPath(&path);
   printf("Pathname:  %s\\n", path.Path());

...we see

   Pathname:  /boot/home/fido

In other words, the BEntry refers to fido, not fidoLink.

Traversal resolves nested links--it really wants to find a "real" file (or directory). If the entry that you're initializing to isn't a link, then the traversal flag is ignored.

When to Traverse

When should you traverse, and when not? Here are a few rules of thumbs:

Traverso Post Facto

Let's say you create a BEntry (to a symlink) without traversing, but then you decide that you do want to resolve the link. Unfortunately, you can't resolve in-place; instead, you have to initialize another BEntry using info (entry_ref or pathname) that you get from the link entry:

   BEntry entry1("/boot/home/fidoLink", false);
   BEntry entry2;
   entry_ref ref;
   
   /* First we check to see if it's a link. */
   if (entry1.IsSymLink()) {
      /* Get the link's entry_ref... */
      entry1.GetRef(&ref);
   
      /* ...and use it to initialize the other BEntry. */
      entry2.SetTo(&ref, true);
   }


Abstract Entries

As we all should know by now, a BEntry identifies a name within a specific directory. The directory that a BEntry identifies must exist, but the entry that corresponds to the name doesn't have to. In other words...

For example, the following construction creates a BEntry object based on a BDirectory and a name:

   BEntry entry(someDir, "myFile.h");

Let's assume that myFile.h doesn't exist. As long as the directory that's referred to by someDir does exist, then the construction is legal. Some of the BEntry functions (those inherited from BStatable, for instance) won't work, but the object itself is valid.

But validity doesn't equal existence:

If you want to know if a BEntry's entry actually exists, use the Exists() function.

Creating a File From an Abstract Entry

To turn an abstract BEntry into a real entry (or, more accurately, a real node), you have to specify the flavor of node that you want. There are two methods for creating a node; the first is general, the second applies to plain files only.

The General Approach.

BDirectory's CreateFile(), CreateDirectory(), and CreateSymLink() functions create nodes of the designated flavor. The functions don't take BEntry arguments directly; instead, you invoke the functions on the BEntry's directory, passing the entry's leaf name as an argument. Here we turn an abstract entry (entry) into a directory:

   BPath path;
   char name[B_FILE_NAME_LENGTH]; /* A buffer for the name. */
   BDirectory parent;  /* The parent of our entry. */
   BDirectory target_dir; /* The product of the transformation. */
   
   if (!entry.Exists()) {
      entry.GetParent(&path);
      entry.GetName(name);
      parent.SetTo(&path);
      parent.CreateDirectory(name, &dir);
   }

The Plain-File-Only Approach.

You can create a plain file by passing the BEntry to the BFile constructor or SetTo() function. To do this, you also have to add B_CREATE_FILE to the "open mode" flags:

   BFile file;
   
   if (!entry.Exists()) 
      file.SetTo(&entry, B_CREATE_FILE|B_READ_WRITE);


Subtleties and Details

The following details understand you should, particularly if you want to participate in bedevtalk.

File Descriptors

Although it's not intuitively obvious, a BEntry object does consume a file descriptor. The file descriptor is opened on the entry's directory.

Your app has a limited number of file descriptors (currently 128, max), so you may not want to cache BEntry objects as your primary means for identifying an entry. If you're going to be dealing with a lot of entries and you want to keep track of them all, it's better to cache entry_ref structures or BPath objects.

Directories are Persistent, Names Are Not

One more time: A BEntry identifies an entry as a name in a directory. As described above, the directory is maintained internally as a file descriptor; the name is simply a string. This means that...

For example, take the following BEntry...

   BEntry entry("/boot/home/lbj/footFetish.jpeg");

If the user moves the directory...

   $ cd /boot/home
   $ mv lbj jfk

The BEntry (entry) "moves" with the directory. If you print the pathname and ask if the BEntry's entry exists...

   BPath path;
   entry.GetPath(&path);
   printf("> Foot movie:  %s\\n", path.Path());
   printf("> Exists?  %s\\n", entry.Exists()?"Oui":"Non");

...you'll see this:

   > Foot movie:  /boot/home/jfk/footFetish.jpeg
   > Exists?  Oui

The same isn't so for the name portion of a BEntry. If the user now moves footFetish.jpeg...

   $ cd /boot/home/jfk
   $ mv footFetish.jpeg hammerToe.jpeg

...your BEntry will not follow the file (it doesn't "follow the data"). The object will still represent the entry called footFetish.jpeg. The BEntry will, in this case, become abstract.

Don't be confused: The BEntry only "loses track" of a renamed entry if the name change is made behind the object's back. Manipulating the entry name through the BEntry object's Rename() function (for example), doesn't baffle the object. For example:

   BPath path;
   BEntry entry("/boot/home/lbj/footFetish.jpeg");
   
   entry.Rename("hammerToe.jpeg");
   entry.GetPath(&path);
   printf("> Foot movie:  %s\\n", path.Path());
   printf("> Exists?  %s\\n", entry.Exists()?"Oui":"Non");

...and we see...

   > Foot movie:  /boot/home/lbj/hammerToe.jpeg
   > Exists?  Oui

BEntries and Locked Nodes

You can't lock an entry, but you can lock the entry's node (through BNode's Lock() function). Initializing a BEntry to point to a locked node is permitted, but the entry's directory must not be locked. If the directory is locked, the BEntry constructor and SetTo() function fail and set InitCheck() to B_BUSY.

Furthermore, the destination directories in BEntry's Rename() and MoveTo() must be unlocked for the functions to succeed. And all directories in the path to the entry must be unlocked for GetPath() to succeed.

If you get a B_BUSY error, you may want to try again--it's strongly advised that locks be held as briefly as possible.


Constructor and Destructor


BEntry()

      BEntry(const BDirectory *dir, const char *path, bool traverse = false)
      BEntry(const entry_ref *ref, bool traverse = false)
      BEntry(const char *path, bool traverse = false)
      BEntry(void)
      BEntry(const BEntry &entry)

Creates a new BEntry object that represents the entry described by the arguments. See the analogous SetTo() functions for descriptions of the flavorful constructors.

The default constructor does nothing; it should be followed by a call to SetTo().

The copy constructor points the new object to the entry that's represented by the argument. The two objects themselves maintain separate representation of the entry; in other words, they each contain their own a) file descriptor and b) string to identify the entry's a) directory and b) name.

To see if the initialization was successful, call InitCheck().


~BEntry

      virtual ~BEntry()

Closes the BEntry's file descriptor and destroys the BEntry object.


Member Functions


Exists()

      bool Exists(void) const

Returns true if the entry exists; false otherwise.


GetName(), GetPath()

      status_t GetName(char *buffer) const
      status_t GetPath(BPath *path) const

These functions return the leaf name and full pathname of the BEntry's entry. The arguments must be allocated before they're passed in.

GetName() copies the leaf name into buffer. The buffer must be large enough to accommodate the name; B_FILE_NAME_LENGTH is a 100% safe bet:

   char name[B_FILE_NAME_LENGTH];
   entry.GetName(name);

If GetName() fails, *buffer is pointed at NULL.

GetPath() takes the entry's full pathname and initializes the BPath argument with it. To retrieve the path from the BPath object, call BPath::Path():

   BPath path;
   entry.GetPath(&path);
   printf(">Entry pathname:  %s\\n", path.Path());

If GetPath() fails, the argument is Unset().

RETURN CODES


GetParent()

      status_t GetParent(BEntry *entry) const
      status_t GetParent(BDirectory *dir) const

Gets the directory, as a BEntry or BDirectory object, in which the object's entry lives. The argument must be allocated before it's passed in.

If the function is unsuccessful, the argument is Unset(). Because of this, you should be particularly careful if you're using the BEntry-argument version to destructively get a BEntry's parent:

   if (entry.GetParent(&entry) != B_OK) {
      /* you just lost 'entry' */ 
   }

This example is legal; for example, you can use destructive iteration to loop your way up to the root directory. When you reach the root ("/"), GetParent() returns B_ENTRY_NOT_FOUND:

   BEntry entry("/boot/home/fido");
   status_t err;
   char name[B_FILE_NAME_LENGTH];
   
   /* Spit out the path components backwards, one at a time. */
   do {
      entry.GetName(name);
      printf("> %s\\n", name);
   } while ((err=entry.GetParent(&entry)) == B_OK);
   
   /* Complain for reasons other than reaching the top. */
   if (err != B_ENTRY_NOT_FOUND)
      printf(">> Error: %s\\n", strerror(err));

This produces:

   > fido
   > home
   > boot
   > /

RETURN CODES


GetPath() see GetName()


GetRef()

      status_t GetRef(entry_ref *ref) const

Gets the entry_ref for the object's entry; ref must be allocated before it's passed in. As with BEntry objects, entry_ref structures can be abstract--getting a valid entry_ref does not guarantee that the entry actually exists.

If the function isn't successful, ref is unset.

RETURN CODES


GetStat()

      virtual status_t GetStat(struct stat *st) const

GetStat() returns the stat structure for the entry. The structure is copied into the st argument, which must be allocated. The BStatable object does not cache the stat structure; every time you call GetStat(), fresh stat information is retrieved.

RETURN CODES


InitCheck()

      status_t InitCheck(void) const

Returns the status of the previous construction, assignment operation, or SetTo() call.

RETURN CODES


MoveTo() see Rename()


Remove()

      status_t Remove(void)

Remove() "unlinks" the entry from its directory. The entry's node isn't destroyed until all file descriptors that are open on the node are closed. This means that if you create BFile based on a BEntry, and then Remove() the BEntry, the BFile will still be able to read and write the file's data--the BFile has no way of knowing that the entry is gone. When the BFile is deleted, the node will be destroyed as well.

Remove() does not invalidate the BEntry. It simply makes it abstract (see "Abstract Entries").

RETURN CODES


Rename(), MoveTo()

      status_t Rename(const char *path, bool clobber = false)
      status_t MoveTo(BDirectory *dir, const char *path = NULL, bool clobber = false)

These functions move the BEntry's entry and node to a new location. In both cases, the BEntry must not be abstract--you can't rename or move an abstract entry.

Rename() moves the entry to a new name, as given by path. path is usually a simple leaf name, but it can be a relative path. In the former case (simple leaf) the entry is renamed within its current directory. In the latter, the entry is moved into a subdirectory of its current directory, as given by the argument.

MoveTo() moves the entry to a different directory and optionally renames the leaf. Again, path can be a simple leaf or a relative path; in both cases, path is reckoned off of dir. If path is NULL, the entry is moved to dir, but retains its old leaf name.

If the entry's new location is already taken, the clobber argument decides whether the existing entry is removed to make way for yours. If it's true, the existing entry is removed; if it's false, the Rename() or MoveTo() function fails.

Upon success, this is updated to reflect the change to its entry. For example, when you invoke Rename() on a BEntry, the name of that specific BEntry object also changes. If the rename or move-to isn't successful, this isn't altered.

RETURN CODES


SetTo(), Unset()

      status_t SetTo(const entry_ref *ref, bool traverse = false)
      status_t SetTo(const const char *path, bool traverse = false)
      status_t SetTo(const BDirectory *dir, const char *path, bool traverse = false)
      void Unset(void)

Frees the BEntry's current entry reference, and initializes it to refer to the entry identified by the argument(s):

The traverse argument is used to resolve (or not) entries that are symlinks:

See "Initializing and Traversing " for more information.

When you initialize a BEntry, you're describing a leaf name within a directory. The directory must exist, but the leaf doesn't have to. This allows you to create a BEntry to a file that doesn't exist (yet). See "Abstract Entries " for more information.

Remember--successfully initializing a BEntry consumes a file descriptor. When you re-initialize, the old file descriptor is closed.

Unset() removes the object's association with its current entry, and sets InitCheck() to B_NO_INIT.

RETURN CODES


Unset() see SetTo()


Operators


= (assignment)

      BEntry& operator=(const BEntry &entry)

In the expression

   BEntry a = b;

BEntry a is initialized to refer to the same entry as b. To gauge the success of the assignment, you should call InitCheck() immediately afterwards. Assigning a BEntry to itself is safe.

Assigning from an uninitialized BEntry is "successful": The assigned-to BEntry will also be uninitialized (B_NO_INIT).


==, != (comparison)

         bool operator==(const BEntry &entry) const
         bool operator!=(const BEntry &entry) const

Two BEntry objects are said to be equal if they refer to the same entry (even if the entry is abstract), or if they're both uninitialized.


Global C Function


get_ref_for_path()

      status_t get_ref_for_path(const char *path, entry_ref *ref)

Returns in ref an entry_ref for the file specified by the path argument.

RETURN CODES

B_OK. The ref was returned successfully.

B_ENTRY_NOT_FOUND. The file wasn't found, or the pathname string is empty.

B_NO_MEMORY. Not enough memory.

Other file errors.






The Be Book, in lovely HTML, for BeOS Release 4.

Copyright © 1998 Be, Inc. All rights reserved.

Last modified December 14, 1998.