Unix files consist of two parts: the data part and the filename part. The data part is associated with something called an ‘inode’.
The inode carries the map of where the data is, the file permissions, etc. for the data.
The filename part carries a name and an associated inode number.
The directory, as a file, is just an array of filename parts of other files. When a directory is built, it is initially populated with the filename parts of two special files: the ‘.’ and ‘..’ files. The filename part for the ‘.’ file is populated with the inode# of the directory file in which the entry has been made; ‘.’ is a hardlink to the file that implements the current directory. The filename part for the ‘..’ file is populated with the inode# of the directory file that contains the filename part of the current directory file. ‘..’ is a hardlink to the file that implements the immediate parent of the current directory.
There are restrictions on what can be hardlinked (both links must reside on the same filesystem, the source file must exist, etc.) that are not applicable to softlinks (source and target can be on separate file systems, source does not have to exist, etc.).
Softlinks have other restrictions not shared by hardlinks (additional I/O necessary to complete file access, additional storage taken up by softlink file’s data, etc.)