It is designed to be doubly linked tree - each node contains pointers to its children. Each child contains a point to its parent. To make some operations easier each node has a link to itself.
In the simple case, a directory is nothing but a file containing a list:
inode - the number that is an index to where pointers to the data storage of the file is.
name - an ascii string to provide the name.
You can examine the inode number using "ls -i". If you want to examine a directory in detail you can use "ls -ail" which will show the hidden files (the a), the inode number (the i) and the long listing format. The result is to put the inode number as the first column. The rest come from the long listing format.
In a base structure, starting at the beginning of the filesystem, inode 1 may or may not get some odd use (additional mounts can hide things). So warnings about doing this on the root filesystem (it has lots of mounts there, so each mount shows its base inode of 1 - you have to get into deep details to see which filesystem that inode might be on).
But the simple case is:
inode 1 represents the filesystem beginning. Therefore this file is a directory, containing two entries, "." and "..". Now because this is the beginning of the tree (the root), BOTH entries point to the same inode - a possible listing (just inodes):
Other files created will add to the link count (.lost+found is nearly always there). On older systems lost+found was always inode 2. It isn't anymore because there are a number of files created during the creation of a filesystem - lost+found is one, quotas, special attributes, readahead information...
But for a simple one, I'll just stick to the minimum.
If a directory (named a) is created on the filesystem, then the new list would be:
So the directory a has been created - it is named "a" in the root directory, and has two entries:
Thus the link count for the root directory (inode 1) has to increase by 1. The inode 2 gets two links - one for the root directory entry (named a), and one for itself (the "." entry within the directory a which is inode 2.
Now regular files can also be created - so lets call it "text", and put it in the root directory. The list now appears:
This makes the number of links to the regular file "text" (inode 3 here) to be 1.
SUPPOSE, an hard link to the "text" file is created in the directory a and is called "test". The result in the directory a is:
The link count for the file test (represented by inode 3) now has the value 2. The data for the file has not been copied (that would require another inode).
If the file "text" is removed from the root directory (using the rm command), then the link count for inode 3 goes back to 1. But no data is lost - the link count is still greater than 0.
If the file "test" is removed from the directory a, then the link count for inode 3 goes to 0, and the file may be reclaimed for use. What happens is that there is a flag in the inode that says "inuse/free" and it is either set (in use) or clear (free). Until the inode is reused, you can actually recover the deleted file... but note: this just recovers the inode. Any data blocks that have been used by the file were also released. So recovery of data can still be difficult (it is a special process to go out and mark the free data blocks as being in use again...)
In the early UNIX/Linux implementations, a file rename is done by the link/unlink system calls. The link just adds a name to a directory using to pre-existing file name to get the inode number to use. The unlink system call then just removes the directory entry, thus renaming the file. The problem this had is that it allows problems to occur - what happens if the program doing the link/unlink gets interrupted between the link and unlink... you get both the original name and the new name. To prevent the race condition, a new system call was added - the rename, which has exactly the same parameters as the link system call.
I should quit now...