Caveat: The following assumes that appB is fast enough to do this (i.e. there is a bit of a race condition).
appB can create a hard link to the file entry under a different name. This will prevent appA from deleting the file (see man 2 link
):
link("appAfile","appBfile");
Now, if appA deletes [or renames] the file, it is still accessible via appBfile
When a file is created for the first time, it creates a directory entry for the file and what is called an inode.
Inodes have a reference count. When an inode is created initially, it has a reference count of 1. When a program opens a file, the inode's reference count is incremented, and when a program closes the file descriptor, the inode's reference count is decremented.
When appA deletes the file (via unlink(2)
), the directory entry is removed and the inode's reference count is decremented. If appA still has the file open, the inode's contents are not removed until appA closes the open file descriptor (i.e. the reference count must go to 0).
The directory entry is [more or less] just: filename|inode-number
. It is the inode that has information such as file size, list of blocks that are the data for the file, permissions, etc.
inodes are in a separate table in the filesystem, indexed by inode-number
.
If appB is able to open appAfile
before appA renames or deletes it (more properly, it unlinks the directory entry, which is why the syscall is unlink
instead of (e.g.) delete
), the data is still accessible because when appB did the open, the inode's reference count was incremented. That is, the refcount is now 2. If appA deletes the file, the inode's refcount is decremented to 1. It is still non-zero, so the data is readable by appB.
As long as appB holds an open descriptor, the data will remain. But, no other app could access it because the directory entry is lost. And, when appB closes the file, the inode's refcount goes to 0 and the data blocks are reclaimed.
When appB does the hardlink operation, it creates a second directory entry with the same inode number and increments that inode's refcount. That is, for a given inode, if no programs hold open file descriptors, the inode's refcount is the number of directory entries that have links to it. This is normally 1, but after appB creates a hard link, the refcount will be 2. Many such hardlinks can be created (using different "alias" names) and the inode's refcount is incremented accordingly.
This allows the file to persist even if appA has deleted it, closed the file descriptor, and appB has closed its file descriptor. The inode will have a refcount of 1 that comes from appBfile
. If appB had an open descriptor, the refcount would be 2 (which goes back to 1 when appB closes the file).
Note that if appA renames the directory entry (e.g. rename(appAfile,appAfile2)
), the rename does not decrement the refcount. appB might have trouble finding it under the new name, but the data would still exist (i.e. the inode has not been deleted).
So, the inode remains as long as an app has an open descriptor, or there is a directory entry with the inode's inode-number. In other words, at any given time, an inode's refcount is the sum of the number of directory entries that are linked to it + the number of file descriptors that are open on it. To delete/remove the inode data, the refcount must go to 0.