Derived from: BStatable
Declared in: be/nustorage/Entry.h
Library: libbe.so
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 through the entry."
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.)
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:
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 should you traverse, and when not? Here are a few rules of thumbs:
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); }
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 object 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.
And 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_NO_ERROR simply means the object is valid. |
If you want to know if a BEntry's entry actually exists, use the Exists() function.
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:
BPath path; char name[B_FILE_NAME_LENGTH]; /* A buffer for the 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); }
BFile file; if (!entry.Exists()) file.SetTo(&entry, B_CREATE_FILE|B_READ_WRITE);
The following details understand you should, particularly if you want to participate in bedevtalk.
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.
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. A consequence of this is...
The directory for a given BEntry is persistent. If you move the directory, the file descriptor, and so the BEntry, moves with it.
| ||
But the name isn't. 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.
Let's take it from the top:
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
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. Also...
The destination directories in BEntry's Rename() and MoveTo() must be unlocked for the functions to succeed.
| ||
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.
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()
Closes the BEntry's file descriptor and destroys the BEntry object.
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
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
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
status_t InitCheck(void) const
Returns the status of the previous construction, assignment operation, or SetTo() call.
RETURN CODES
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
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
status_t SetTo(const entry_ref *ref, bool traverse = TRUE) status_t SetTo(const const char *path, bool traverse = TRUE) status_t SetTo(const BDirectory *dir, const char *path, bool traverse = TRUE) void Unset(void)
Frees the BEntry's current entry reference, and initializes it to refer to the entry identified by the arument(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
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).
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 the BeOS Preview Release.
Copyright © 1997 Be, Inc. All rights reserved.
Be is a registered trademark; BeOS, BeBox, BeWare, GeekPort, the Be logo, and the BeOS logo are trademarks of Be, Inc.
Last modified July 17, 1997.