# The Storage Kit: BEntry

Derived from: BStatable

Declared in: be/storage/Entry.h

Library: libbe.so

## Overview

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"):

- All the BEntry cares about is a name in a directory.

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:

- Location info. A BEntry knows its own (leaf) name (`GetName()`), its full pathname (`GetPath()`), and the identity of its parent directory (`GetParent()`).

- BStatable info. As a descendant of BStatable, a BEntry can return statistical information about the entry's data--its size, creation date, owner, and so on.

- `entry_ref identifier.` A BEntry can return the `entry_ref` that globally identifies the entry (`GetRef()`).

A BEntry can do these things:

- Perform hierarchical operations. A BEntry can change the name of its entry (`Rename()`), move it to another directory (`Move()`), and remove it from the file hierarchy (`Remove()`).

- Initialize BNode objects. The constructors and `SetTo()` initializers for BNode and its children (BFile, BDirectory, and BSymLink) accept BEntry arguments.

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...

- during construction,
- through the `SetTo()` function,
- or through the assignment operator.

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

- BDirectory's `FindEntry()` or `GetEntry()` function,

- BEntryList's `GetNextEntry()` function (implemented by BDirectory and BQuery).

- BEntry's `GetParent()` function.

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:

- If you traverse: The BEntry will point to the entry that the symbolic link is linked to.

- If you don't traverse: The BEntry will point to the symbolic link itself.

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:

- If somebody hands you a file reference--if your app gets a `RefsReceived()` message--then you probably want to traverse the entry.

- If you're pawing over the contents of a directory (through BDirectory's `GetNextEntry()`), then you probably don't want to traverse.

- If you're looking at the result of a query (through BQuery's `GetNextEntry()`), then you almost certainly don't want to traverse. The query finds entries that satisfy certain criteria; if a symbolic link is in the list, it's because the link itself was a winner. If the linked-to file is also a winner, it will show up on its own.

#### 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...

- A BEntry can represent a file that doesn't exist. The entry is said to be "abstract."

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:

- `SetTo()` and `InitCheck() do` not tell you if a BEntry's entry actually exists. Don't be confused; a return value of `B_OK` simply means the object is valid.

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...

- The directory for a given BEntry is persistent. If you move the directory, the file descriptor, and so the BEntry, moves with it.

- The name isn't persistent. If the user renames the leaf that a BEntry is pointing to, the BEntry will become abstract.

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

- `B_NO_ERROR`. The information was successfully retrieved.
- `B_NO_INIT`. The BEntry isn't initialized.
- `B_BUSY` (`GetPath()` only). A directory in the entry's path is locked.

### 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_NO_ERROR) {
          /* 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_NO_ERROR);

       /* 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

- `B_NO_ERROR`. The information was successfully retrieved.
- `B_NO_INIT`. This BEntry isn't initialized.
- `B_ENTRY_NOT_FOUND`. Attempt to get the parent of the root directory.
- `B_NO_MORE_FDS`. Couldn't get another file descriptor.

### 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

- `B_NO_ERROR`. The `entry_ref` was successfully retrieved.
- `B_NO_INIT`. This object isn't initialized.
- `B_NO_MEMORY`. Storage for the `entry_ref`'s name couldn't be allocated.

### 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

- `B_NO_ERROR`. Success.
- `B_NO_MEMORY`. Couldn't get the necessary resources to complete the transaction.
- `B_BAD_VALUE`. The entry doesn't exist (abstract entry).

### InitCheck()

          status_t InitCheck(void) const

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

RETURN CODES

- `B_NO_ERROR`. The initialization was successful.
- `B_NO_INIT`. The object is uninitialized (this includes `Unset()`).
- See `SetTo()` for other errors.

### 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

- `B_NO_ERROR`. Success.
- `B_NO_INIT`. The BEntry is not initialized.
- `B_BUSY`. The entry's directory is locked.

### 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

- `B_NO_ERROR`. Success.
- `B_NO_INIT`. The BEntry is not initialized.
- `B_ENTRY_NOT_FOUND`. A directory to the new location doesn't exist, or this is an abstract entry.
- `B_FILE_EXISTS`. The new location is already taken (and you're not clobbering).
- `B_BUSY`. The directory that you're moving the entry into is locked.

### 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):

- In the ref version, the BEntry is initialized to refer to the given `entry_ref`.

- In the path version, path can be absolute or relative, and can contain "." and ".." elements. If path is relative, it's reckoned off of the current working directory.

- In the dir/path version, path must be relative. It's reckoned off of the directory given by dir.

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

- If traverse is `true`, the link is resolved.
- If traverse is `false`, the BEntry refers to the link itself.

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

- `B_NO_ERROR`. The BEntry was successfully initialized.
- `B_BAD_VALUE`. Bad argument value; uninitialized ref or dir.
- `B_ENTRY_NOT_FOUND`. A directory in the path to the entry doesn't exist.
- `B_BUSY`. The entry's directory is locked.

### 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.

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

Copyright © 1998 Be, Inc. All rights reserved.

Last modified March 26, 1998.