Generic Directory Operations
Contents
Structure
Create
Delete
Opendir
Readdir
Add a file
Remove a file
Change dir
Adding files to a directory
Locating a file
Directories impose a structure onto the file system, so that it is not just
a ``collection of files''. This is needed because a non-trivial system will
have lots of files. A directory maintains information about other files in
the file system.
The file system may be flat with just one directory.
CP/M and early MSDOS had
this structure.
A more common system is a hierarchical tree of directories and files, such
as Windows.
A variation on this is a directed acyclic graph (like a tree but a node can
have more than one parent). This is used by Unix, where one file an be ``linked''
to another so that the one file has two or more file names.
Directory operations
Directories have an internal structure, unlike a general file that is just
a sequence of bytes. It is more likely to be organised as a sequence of records
of some kind. The allowable operations must take this into account.
- create
- delete
- opendir
- closedir
- readdir
- rename
- add
- link (add a file to the dir)
- unlink (remove a file from the dir)
- change dir
- find current dir
When a directory is created, a file is made with a particular record
structure. There are usually two initial records in this file, one for the
parent directory, one for the current directory. So a directory file
contains information about about where it's parent is (for traveral
upwards) and also where it is (so it can be located for operations
such as ls .
).
Usually directories cannot be deleted if they still contain files
other than `.' and `..'. If they were, files would exist without a means
of accessing them. Usually any files in a directory must be removed first.
A directory is a file and can opened for reading or writing.
However, since it has a special structure, there are usally special
commands to open it
Rading a directory should be done in terms of the records it contains
rather than as a set of bytes.
Adding a file to a directory requires creating a new directory record
and adding it to the directory (treating it as a file). If the file is new,
the file system may also create file information space for it, such as an
inode.
Removing a file means that its entry must be removed from the directory.
If the file is no longer referenced, it's content may also be removed from
the file system. If it is still referenced, then this cannot happen.
A file can be referenced by multiple names in Unix, so it can only be removed
after all its names have been deleted.
A file that is still open by a process is in use, and cannot be removed
till all processes have finished with it.
A user process begins in a particular directory. It should be able to
navigate the directory structure using a change dir command.
If each process has its own current directory (Unix), then this will
just change the directory of the process. If there is a single current
directory (MSDOS) then changing the directory will be global.
When a directory is created it will be empty except for its parent and maybe
itself. The directory tree can be traversed up and down.
When a file is created (eg by creat
in Unix)
an entry is made for the new file in the directory.
In the MSDOS file system, directory entries are 32-byte records.
These hold the filename, the extension,
the attributes, the date of last modification and the first block and the file
size. All the principal information about the file is held in this directory
In Unix, all that kind of information is held in the inode. The directory just
holds the name of the file and the inode number.
The inode method allows multiple links to the same file, as the directories
just hold the different file names and the same inode number. A ``link count''
within the inode keeps track of how many filenames a file has. When this count
drops down to zero, the file is removed.
When a file needs to be accessed, say when it is opened for reading, its blocks
on disk have to be found. Say the file is /usr/jan/file1. In Unix, the root
directory is located at a fixed place on the disk. This directory is then read
for the entry ``usr''. When it is found, its inode is known. From the inode,
the location on disk of the directory is found. This is then read looking for
the entry for ``jan''. When it is found its inode is given. From there the
directory on disk can be found, and read for the file ``file1''. When this
is found, its inode is known and the file blocks are finally located.
Directories - Unix API
Contents
mkdir
creat
link
unlink
rmdir
chdir
getcwd
Reading dirs
Examples
Unix API
Some of the Unix API for directory operations is
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int mkdir(char *path,
mode_t mode);
int creat(char *path,
mode_t mode);
int link(char *path1, char *path2);
int unlink(char *path);
int rmdir(char *path);
int chdir(char *path);
char *getcwd(char *buf, int size);
and
#include <sys/types.h>
#include <dirent.h>
DIR *opendir(char *path);
struct dirent *readdir(DIR *dirp);
int closedir(DIR *dirp);
This creates a new directory with no initial contents (apart from `.' and
`..'). It returns -1 on failure
This creates a new file, using a new inode. It also creates a new
entry in the directory. Since this changes the directory (as a file),
the last modified times of the directory are updated.
link adds another name to an existing file. It does not make a new
inode entry, but does make a new directory entry. The link count in
the inode is increased.
This removes an entry from a directory. The link count in the inode
is decreased. If the count falls to zero, the file can be removed
once no process has it open. Removing a file from a directory
changes the last modified time of the directory (it is a file with
changed contents).
Removing a directory will fail unless the directory is empty.
This will attempt to change directory. The types of patterns that will
be accepted for directory names is not specified.
This will fill in the buffer with the current directory. This call can
actually fail: if another process has succeeded in removing the
current directory of a process.
The call
opendir()
opens a directory for reading.
The returned
DIR
value is opaque and is just passed to
other functions. It is
NULL
on error.
Under Linux, the DIR
structure is
typedef struct DIR
{
/* file descriptor */
int dd_fd;
/* offset of the next dir entry in buffer */
off_t dd_loc;
/* bytes of valid entries in buffer */
size_t dd_size;
/* -> directory buffer */
struct dirent *dd_buf;
} DIR;
The call readdir()
returns a structure that you can use.
It is specified to have one entry char *d_name
. Under Linux
it is defined as
struct dirent {
long d_ino;
__kernel_off_t d_off;
unsigned short d_reclen;
char d_name[256];
};
This creates a directory, puts a file into it and then removes it.
#include
#include
#include
int main(void)
{ int fd;
if (mkdir("mydir", 0777)
== -1)
exit(1);
if ((fd = creat("mydir/f1",
0777)) == -1)
exit(2);
close(fd);
unlink("mydir/f1");
exit(0);
}
A program to scan the current directory, printing its contents is
#include <iostream.h>
#include <dirent.h>
int main(int argc, char *argv[])
{
DIR *dirp;
struct dirent *dp;
if ((dirp = opendir(".")) == NULL)
{
cerr << "Can't open .\n";
exit(1);
}
for (dp = readdir(dirp);
dp != NULL;
dp = readdir(dirp))
cout << "File: " <<
dp->d_name << endl;
closedir(dirp);
exit(0);
}
Systems Software Home
Jan Newmarch
(http://jan.newmarch.name)
<jan@newmarch.name>
Copyright © Jan Newmarch
Last modified: Wed Nov 19 17:55:42 EST 1997