简体   繁体   中英

Why can a directory be removed when my shell is using that directory?

I don't know if what I encountered was a bug or is intended behavior, but luckily I figured out pretty quick what was going on.

I had my shell cd'd inside a subdirectory of the git repo, and I performed a git rebase -i squash operation in which the commits involved included the creation of this directory.

After that operation completed without incident, the shell became in an orphaned state where the git status (neither in the zsh theme helper RPROMPT nor when run) indicated that I wasn't even in a git repository anymore.

Once I ran cd .. everything was fine, and it all made sense. During the course of the rebasing operation, Git had rm 'd the directory that I was in (at the first step of the rebase) and subsequently put it back. Since it got put back, I could also have ran cd $(pwd) to go there in one step.

This is some confusing behavior. Although, it is not clear that anything was technically done wrong by git. My question is would this be a bug in git or should users be expected to know how to deal with this situation?

Also, the wider, actual root question: Why is it permitted to remove a directory that my shell is in, when it is not permitted to eject a disk if I have a shell on a mounted directory? It seems inconsistent to me.

Case in point: fuser <directory> shows current directory uses. If a program is "on" a directory it is "using" it.

Terminal 1:

$ cd tmp/
$ mkdir test
$ cd test

Terminal 2:

$ rmdir tmp/test 

Terminal 1:

$ ls 
sh: 0: getcwd() failed: No such file or directory

Inconsistent, yes. But allowed.

PS. And this has nothing to do with git .

To answer your first question:

It is not a bug, it is how Unix systems, including Linux, have always worked.

There's even some nice text in the POSIX spec :

[EBUSY] The directory to be removed is currently in use by the system or some process and the implementation considers this to be an error.

Namely, some implementation (eg Windows...) can call it an error, but most implementations. namely Unix variants, do not.

The way this is implemented in a filesystem is by having the processes hold a reference on the object which represents the directory. Another thing that holds such a reference is the parent directory. git rebase has removed the latter reference, but the former remains. Even if the directory is re-created, it is a new filesystem object, whereas your shell was holding a reference to the old object.

That's why cd .. and cd $(pwd) still work - they re-lookup the directory and grab the new reference, and release the old reference.

Officially the old object is not cleaned up until the old reference is released. That means that the metadata of the directory is not removed from the disk until the process releases the old working directory.

To answer your second question:

In order to eject a drive you have to unmount the mountpoint, and as above, the processes you find with fuser hold references to that mountpoint.

As with directory references, a amount point cannot be cleaned up until all references to it have been removed. That is why you cannot eject a drive which has references.

The consistent thing to do would be to allow detaching the filesystem from the directory tree without actually cleaning up. You still won't be able to eject the drive, but at least you can use the directory tree as you wish.

Well, that is actually possible to do with umount --lazy .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM