5

I am reading Sobell's A Practical Guide to Linux Commands, Editors, and Shell Programming, 4e. Early on he talks about the . and .. and says that they are "synonymous with the pathnames" of the working and parent of working directories. My question is about whether I should think about these as synonyms for relative or absolute pathnames. In the end, I think my misunderstanding has to do with not understanding how Linux systems resolve pathnames.

An example: suppose I enter a command like

cd ..

Should I think of the shell as doing a substitution of .. for the absolute pathname of the parent of my current working directory? Or does the "resolution" behave differently when I give a relative pathname (as seems to be the case here because I didn't begin my path with a slash /)? To some extent I guess this question can be phrased as "is relative pathnaming a special case of absolute pathnaming in that every relative pathname is substituted for the appropriate absolute path?"

Jim L.
  • 7,997
  • 1
  • 13
  • 27
EE18
  • 233
  • 2
  • hmm, to me, .. looks obviously a relative path, just like e.g. foo.txt does. Neither can be resolved without context, the current working directory. But, since you ask "is relative pathnaming a special case of absolute pathnaming" and I wonder, what would the difference be? What would be different if it was a special case of the other, vs. if it was not a special case but something completely separate? – ilkkachu Jan 24 '24 at 18:49
  • I guess I'm wondering what the shell does with a relative path. That is, does it substitute the relative path for an absolute path and then proceed or no? I guess this is all irrelevant from a user's perspective in the end now that I think about it. @ilkkachu – EE18 Jan 24 '24 at 19:48

3 Answers3

11

. and .. and says that they are "synonymous with the pathnames" of the working and parent of working directories

This is a simplification. It's true in most cases, but not always. In most cases, if the current directory is /one/two/three, then cd . is equivalent to cd /one/two/three/. which is equivalent to cd /one/two/three, and cd .. is equivalent to cd /one/two/three/.. which is equivalent to cd /one/two. Similarly, ls . is equivalent to ls /one/two/three and ls .. is equivalent to ls /one/two.

The equivalence is not up to the shell. It's part of the kernel job to resolve . and ... Historically, . and .. were in fact ordinary directory entries and the kernel didn't have any code handling those in path traversal. The only special code for . and .. was that they were automatically created in every directory, and couldn't be modified or removed. Modern systems tend not to store . and .. as directory entries and instead have special handling in their path traversal code.

You can observe that the shell doesn't do . and .. resolution by passing a path to an external command.

$ echo ..
..
$ ls ../s*
../somefile ../someotherfile

As I wrote above, resolving relative paths is equivalent to specifying an absolute path in most cases, but not all. For example, it's possible to run an application like this:

  1. Create directories and files:

    drwx------  4 root  root 4096 Jan  1 12:00 /some
    drwx------  2 myapp root 4096 Jan  1 12:00 /some/where
    -rwx------  1 myapp root 4096 Jan  1 12:00 /some/where/myfile
    
  2. As root, change to the directory /some/where.

  3. Drop privileges to the user myapp.

  4. The application can access myfile and ./myfile, but not /some/where/myfile or ../where/myfile, since it doesn't have the permission to traverse /some.

Note that this is a general limitation of the equivalence between paths to the same file, not just about . and ...

There is a special case where .. is handled inside the shell: for the cd command only (and related commands such as pushd), the shell itself compresses /dir/.. into nothing. This includes treating e.g. cd .. as cd /one/two if the current directory is /one/two/three according to the shell. This is generally considered friendlier to humans. In particular, cd foo followed by cd .. gets you back where you started. This makes a difference when symbolic links are involved. If link is a symbolic link which is not to a subdirectory in the same directory, then link/.. is not the current directory: it's the parent directory of the target of link. This is called “logical” current directory tracking, and you can switch to “physical” current directory tracking (where the shell doesn't do any special handling of ..) with cd -P or by setting a shell option in some shells.

  • Some of this went over my head but that which I did understand was very helpful, thank you! That which I don't understand I will return to as I go further into Sobell's book :) – EE18 Jan 24 '24 at 23:10
  • As cd etc. are build-in commands in the shell it's quite obvious that any path handling they do is done in the shell. It's not really a special case for handling of .. (etc.). But I guess @Gilles know, and the point about "logical" vs. "physical" directory tracking is good to include. – Henrik supports the community Jan 25 '24 at 10:01
  • 1
    @Henriksupportsthecommunity It is a special case for .. in the shell. When you run cd foo in the shell, this calls the system call chdir("foo") with the same string except that the shell first does .. compression. – Gilles 'SO- stop being evil' Jan 25 '24 at 12:29
  • Re "cd /one/two/three/.. which is equivalent to cd /one/two" Usually. It's not if /one/two/three is a symlink – ikegami Jan 26 '24 at 05:34
0

My question is about whether I should think about these as synonyms for relative or absolute pathnames

I would suggest to skip this stage and go straight to thinking about these just like any other directory, because that is what they are in all manners relevant to you (unless you are a kernel or filesystem-module developer).

Gilles' answer is fine and well, and I see no particular wrong information in it.

That said: it is absolutely possible and useful to view "." and ".." as bog-standard directories. Whatever you do with them in scripts or applications works just like they were directories (and as Gilles says, in some past they actually were).

Most importantly: you have to keep the concept of an inode separate from that of a directory entry in your head. An inode describes where "something" is stored in the file system (in a quite physical manner, i.e. the very disk blocks). The directory entry is a more abstract representation containing a human-readable name, the corresponding inode, attributes, dates and such.

A directory itself can be fined like an internally handled file which has some representation for listing those entries (this is simplified of course). The important bit is that the actual storage is very separated from where the info about where it's stored.

If I type the following, I can see the inodes of those directories (first column):

 ~/tmp$ ls -ilaO
total 8
 16326296 drwxr-xr-x    8 me  staff  -  256 Jan 15 14:02 .
  1129613 drwxr-xr-x+ 112 me  staff  - 3584 Jan 19 17:25 ..
 16326815 drwxr-xr-x    2 me  staff  -   64 Jul 27  2020 a_directory
126333718 -rw-r--r--    1 me  staff  -    0 Jan 15 14:07 a_file

Going "upstairs", I can check how tmp is represented in its parent directory:

 ~$ ls -ilaO -d tmp
16326296 drwxr-xr-x  8 me  staff  - 256 Jan 15 14:02 tmp

tmp in the parent directory points to the same inode as . inside tmp, as expected.

Checking the . of the parent directory:

 ~$ ls -ilaO -d .
1129613 drwxr-xr-x+ 112 me  staff  - 3584 Jan 19 17:25 .

reveals that the inode of . in the parent is the same as the inode of the .. entry in the child directory, as expected.

I can use . and .. in almost all places where I could use the name of every other directory in the system. If there is a difference at all, this is because the command I am using has special logic implemented for some reason (for example for security reasons - a web server could enforce that a path can never contain .. so someone cannot "escape" a security rule and type in an URL like http://domain/index.html/../../../../etc/passwd to get the password file).

Other programs (like bash) might have some kind of magic handle ".." special. For example, when I do a cd .. the bash knows that my current path is not /where/i/was/before/.. but just /where/i/was. This answers your question about how to think about the shell: you can imagine that it just does what it thinks you want to achieve, to make it easier for you. Other programs which have no special logic for .. can usually and easily work with .. anywhere in their paths, and will be blithely unaware of them - everything will just work fine since .. is a path name just like any other path name.

AnoE
  • 575
0

Resolution of . and .. happens in kernel. The kernel initializes dnode entries as you descend through a path. The current directory dnode entry has a pointer to its parent directory dnode entry.

It can be observed that the filesystem code is not in play here. If you corrupt a directory's .. entry to refer to somewhere else, the only way to actually see this is via a handcrafted NFSv2 client. This is so much true that if you corrupt a filesystem so that there are two paths to a directory, .. still works correctly and you will descend back down the path you came in on.

Some shells handle cd .. specially by pealing the last component off the path as text, which results in different behavior when the current directory path is a symbolic link. The shell doesn't know which arguments are filename arguments, and .. is not a glob pattern; this can easily be seen by doing echo .. and observing what comes back.

Source: I developed a filesystem for my master's thesis.

Joshua
  • 1,893