The Storage Kit: BDirectory

Derived from: public BStore

Declared in: <storage/Directory.h>


Overview

The BDirectory class defines objects that represent directories in a file system. A directory can contain files and other directories, and is itself contained within a directory (its "parent"). As with all BStore objects, a BDirectory is useless until its ref is set.

You use BDirectory objects to browse the file system, and to create and remove files (and directories). These topics are examined in the following sections. After that it's nap time.


Browsing the File System

Directories are the essence of a hierarchical file system. By placing directories inside other directories, you increase the depth of the hierarchy (currently, the nesting can be 64 levels deep). Some of the rules that govern the Be file system hierarchy are:

Descending the Hierarchy

If we wanted to browse an entire file system, we get a root directory, and recursively ask for its contents and the contents of the directories it contains.

First, we get a root directory from a volume by calling BVolume's GetRootDirectory() function; in the example here, we get the root directory of the boot volume:

   /* We'll get the root directory of the boot volume. */
   BVolume boot_vol = boot_volume();
   BDirectory root_dir;
   
   boot_vol.GetRootDirectory(&root_dir);

Since the Be file system is acyclic, we can implement the hierarchy descent in a single recursive function. In this simple implementation we ask the argument directory for its contents (first its files, then its directories), print the name of each entry, and then re-call the function for each of its directories. The level argument is used to indent the names to make the nesting clear:

   void descend(BDirectory *dir, long nest_level)
   {
      long index = 0, nester;
      BFile a_file;
      BDirectory a_dir;
      char name_buf[B_FILE_NAME_LENGTH];

First we print the name of this directory (followed by a distinguishing slash):

      dir->GetName(name_buf);
      for (nester = 0; nester < nest_level; nester++)
         printf(" ");
      printf("%s/\\n", name_buf);

Now we get the files; GetFile() returns B_ERROR when the index argument is out-of-bounds:

      while (dir->GetFile(index++, &a_file) == B_NO_ERROR) {
         a_file.GetName(name_buf);
         for (nester = 0; nester < nest_level + 1; nester++)
            printf(" ");
         printf("%s\\n", name_buf);
      }

Finally, we call descend() for each sub-directory:

      index = 0;
      while (dir->GetDirectory(index++, &a_dir) == B_NO_ERROR) 
         descend(&a_dir, nest_level + 1);
   }

The example demonstrates the use of GetFile() and GetDirectory(). There are two versions of each of these functions: The version of each shown here gets the index'th item in the calling directory. The other version finds an item by name (see the GetFile() description for details).

Getting Many Files at a Time

GetFile() and GetDirectory() are reasonably efficient--but they're not as fast as GetFiles() and GetDirectories(). As their names imply, the latter functions retrieve more than one item at a time; each set of files that's retrieved requires fewer messages to the Storage Server, thus the retrieval is much faster than getting each file individually.

The GetFiles() function doesn't retrieve files as BFile objects; instead, it writes their record_refs into a vector that you pass (as a record_ref pointer) to the function. You then use the record_ref values to refer BFile objects to the underlying files. (This is also true, modulo store type, for GetDirectories() .)

Here, we modify the descend() function to use GetFiles() and GetDirectories():

   void descend(BDirectory *dir, long nest_level)
   {
      long index, nester;
      BFile a_file;
      BDirectory a_dir;
      long file_count, dir_count;
      record_ref *ref_vector;
      char name_buf[B_FILE_NAME_LENGTH];   
   
      dir->GetName(name_buf);
      for (nester = 0; nester < nest_level; nester++)
         printf(" ");
      printf("%s/\\n", name_buf);

We have to allocate the ref vector; rather than do it twice (once for files, once for directories), we grab enough to accommodate the larger of the two sets. The CountFiles() and CountDirectories() functions, used below, do pretty much what we expect:

      file_count = dir->CountFiles();
      dir_count = dir->CountDirectories();
      ref_vector = (record_ref *)malloc(sizeof(record_ref) * 
                               max(dir_count, file_count));

GetFiles() gets the refs for the files. The first two arguments are 1) an offset into the directory's list of files, and 2) the number of refs we want to retrieve.

      dir->GetFiles(0, file_count, ref_vector);
   
      for (index = 0; index < file_count; index++) {
         if (a_file.SetRef(ref_vector[index]) < B_NO_ERROR)
            continue;
         a_file.GetName(name_buf);
         for (nester = 0; nester < nest_level+1; nester++)
            printf(" ");
         printf("%s\\n", name_buf);
      }
   

Now do the same for directories:

      dir->GetDirectories(0, dir_count, ref_vector);
   
      for (index = 0; index < dir_count; index++) {
         if (a_dir.SetRef(ref_vector[index]) < B_NO_ERROR)
            continue;
         descend(&a_dir, nest_level + 1);
      }   
      /* Don't forget to free the vector. */
      free(ref_vector);
   }

Path Names and File Names

Although record_ref s are the common currency for finding and accessing files in the file system, it's also possible to get around using path names and file names. The BDirectory class provides a number of functions that operate on names:


Modifying the File System

The BDirectory class provides functions that let you create new files and add them to the files system, and remove existing files.

  • To create a new file, you call the Create() function.

  • To remove an existing file, you call Remove().
  • While BDirectory's Remove() is the only way to programatically remove an item from the file system, files can be created as copies of other files through BFile's CopyTo() function. You can't copy a directory.


    Constructor and Destructor


    BDirectory()

          BDirectory(record_ref ref)
          BDirectory(void)

    The two BDirectory constructors create and return pointers to newly created BDirectory objects. The version that takes a record_ref argument attempts to refer the new object to the argument; the no-argument version creates an unreferenced object. In the latter case, you must set the BDirectory's ref in a subsequent manipulation. This you can do thus:


    ~BDirectory()

          virtual ~BDirectory(void)

    Destroys the BDirectory object; this doesn't remove the directory that the object corresponds to. (To remove a directory, use BDirectory's Remove() function; note that you can't remove a volume's root directory.)


    Member Functions


    Contains()

          bool Contains(const char *name)

    Looks in the BDirectory for a file or directory named name. If the item is found, the function returns TRUE , otherwise it returns FALSE. If you need to know whether the item is a file or a directory, you should follow this call (if it returns TRUE) with a call to IsDirectory(), passing the same name:

       if (aDir->Contains("Something"))
          if (aDir->IsDirectory("Something"))
             /* It's a directory. */
          else
             /* It's a file. */

    See also: IsDirectory() , GetFile(), GetDirectory()


    CountDirectories() see CountFiles()


    CountFiles(), CountDirectories() , CountStores()

          long CountFiles(void)
          long CountDirectories(void)
    
          long CountStores(void)

    Returns a count of the number of files, directories, or both that are contained in this BDirectory.

    See also: GetFile(), GetFiles()


    CountStores() see CountFiles()


    Create()

          long Create(const char *newName,
             BStore *newItem,
             const char *tableName = NULL,
             store_creation_hook *hookFunc = NULL,
             void *hookData = NULL)

    Creates a new file system item, names it name, and adds it to the directory represented by this BDirectory. The ref of the newItem argument is set to represent the added item. newItem must either be a BFile or BDirectory object--the object's class dictates whether the function will create a file or a directory.

    The other three arguments (tableName, hookFunc, and hookData) are infrequently used--you should only need them if you want your file system records to conform to a custom tables. See The File Creation Hook (in the BStore class) for more information.

    The function returns B_NO_ERROR if the item was successfully created.


    GetDirectory() see GetFile()


    GetFile(), GetDirectory()

          long GetFile(const char *name, BFile *file)
          long GetFile(long index, BFile *file)
    
          long GetDirectory(const char *name, BDirectory *dir)
    
          long GetDirectory(long *index, BDirectory *dir)

    Looks for the designated file or directory (contained in this BDirectory) and, if it's found, sets the second argument's ref to represent it. The second argument must point to an allocated object--these functions won't allocate it for you.

    The name versions of the functions search for the appropriate item with the given name. For example, the call

       BFile *aFile = new BFile();
       if (aDir->GetFile("something", aFile) < B_NO_ERROR)
          /* Not found. */

    looks for a file named "something". It ignores directories. Similarly, the GetDirectory() function looks for a named directory and ignores files. As implied by the example, the function returns B_NO_ERROR if the named item was found.

    The index versions return the index'th file or directory. For example, this

       if (aDir->GetFile(0, aFile) < B_NO_ERROR)
          ...

    gets the first file, while this

       BDirectory *aSubDir = new BDirectory();
       if (aDir->GetDirectory(0, aSubDir) < B_NO_ERROR)
          ...

    gets the first directory.

    The index versions return a less-than-B_NO_ERROR value if the index is out-of-bounds.

    See also: Contains(), IsDirectory(), GetFiles()


    GetFiles(), GetDirectories() , GetStores()

          long GetFiles(long index, long count, record_ref *refVector)
          long GetDirectories(long index, long count, record_ref *refVector)
    
          long GetStores(long index, long count, record_ref *refVector)

    These functions retrieve a vector of refs that identify some number of files, directories, or both within the this BDirectory. The index and count arguments tell the functions where, within a list of items, to start plucking refs and how many refs to pluck; the plucked refs are placed in refVector. For example, GetFiles() makes a list of all the file refs in this BDirectory; it then places, in refVector, the index'th through the (index+count)'th refs.

    If you set index and count such that all or part of the desired range is out-of-bounds, these functions don't complain: They retrieve as many refs as are in-bounds and return those to you. Thus, the number of refs that are passed back to you may be less than the number you asked for.

    You must allocate refVector before you pass it into these functions. It's the caller's responsibility to free the vector.

    The functions return B_ERROR if the BDirectory's ref hasn't been set. Otherwise, they return B_NO_ERROR.

    See Getting Many Files at a Time for an example of the use of GetFiles() and GetDirectories() .

    See also: GetFile()


    GetRefForPath()

          long GetRefForPath(const char *pathName, record_ref *ref)

    Searches for the files that's named by the given path name. If the file is found, it's ref is placed in ref (which must be allocated before it's passed in).

    If the path is relative, the search starts at this BDirectory (the path name is appended to the path that leads to this object). If it's absolute, the search starts at the root directory. In the  absolute case, the receiving BDirectory doesn't figure into the search: An absolute path name search invoked on any BDirectory object yields the same result.

    Path names are constructed by concatenating directory and file names separated by slashes ("/"). Absolute path names have an initial slash; relative path names don't. Keep in mind that an absolute path name must include the root directory name.

    Warning: This function fails if the path name ends in a slash, even if it otherwise identifies a legitimate directory.

    The function returns B_NO_ERROR if a ref was successfully found; otherwise, it returns B_ERROR. Note that the BDirectory's ref must be set for this function to succeed, even if the path name is absolute.


    IsDirectory()

          bool IsDirectory(const char *name)

    Returns TRUE if the BDirectory contains a directory named name; if the object doesn't contain an item with that name, if the item is a file, or if other impediments obtain, the function returns FALSE .

    See also: Contains()


    Remove()

          long Remove(BStore *anItem)

    Removes the given item from the object's directory, removes the item's record from the database, and frees the (disk) space that it was using. If anItem is a BFile, the object is closed before it's removed. The item must be a member of the target BDirectory.

    You can't remove a volume's root directory (it doesn't have a parent, so there's no way to try). Also, you can't remove a directory that isn't empty.

    The function returns B_NO_ERROR if the item was successfully removed; otherwise, it returns B_ERROR.




    The Be Book, HTML Edition, for Developer Release 8 of the Be Operating System.

    Copyright © 1996 Be, Inc. All rights reserved.

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

    Last modified September 6, 1996.