What Happens to an Open File Handle on Linux If the Pointed File Gets Moved or Deleted

What happens to an open file handle on Linux if the pointed file gets moved or deleted

If the file is moved (in the same filesystem) or renamed, then the file handle remains open and can still be used to read and write the file.

If the file is deleted, the file handle remains open and can still be used (This is not what some people expect). The file will not really be deleted until the last handle is closed.

If the file is replaced by a new file, it depends exactly how. If the file's contents are overwritten, the file handle will still be valid and access the new content. If the existing file is unlinked and a new one created with the same name or, if a new file is moved onto the existing file using rename(), it's the same as deletion (see above) - that is, the file handle will continue to refer to the original version of the file.

In general, once the file is open, the file is open, and nobody changing the directory structure can change that - they can move, rename the file, or put something else in its place, it simply remains open.

In Unix there is no delete, only unlink(), which makes sense as it doesn't necessarily delete the file - just removes the link from the directory.


If on the other hand the underlying device disappears (e.g. USB unplug) then the file handle won't be valid any more and is likely to give IO/error on any operation. You still have to close it though. This is going to be true even if the device is plugged back in, as it's not sensible to keep a file open in this case.

What happens to an open file handle on Solaris if the pointed file gets moved or deleted?

Yes. Linux emulates the Unix/Solaris behavior of the file remaining allocated and the file contents being available until it is closed by all processes that have it open. In other words, Linux/Solaris/UNIX all behave the same.

If a file has been deleted, but a process still holds a file handle, can another process access it?

Yes it can. Via /proc/$pid/fd/$fd.

Example:

#include <unistd.h>
#include <fcntl.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
int fd;
if(0>(fd = open("DELETED", O_CREAT|O_RDWR|O_CLOEXEC, 0600))) return perror("open"),1;
static char const msg[]="got me\n";
(void)write(fd, msg, sizeof(msg));
if(0>(unlink("DELETED"))) return perror("unlink"),1;
char buf[128];
sprintf(buf,"cat /proc/%ld/fd/%d", (long)getpid(), fd);
system(buf);

}

(Here I'm accessing it from a(n indirect) child process, but this is not a requirement. It works from unrelated processes as well.)

The /proc/$pid/fd/$fd items appear as symlinks in the filesystem.

They usually point to the name the file was opened as but when the file is deleted, the original link target has a " (deleted)" appended to it as in

lrwx------ 1 petr petr 64 Aug 19 12:45 /proc/32027/fd/3 -> '/home/petr/DELETED (deleted)'

yet in spite of such a target being nonexistent such a proc symlink works (through some dark kernel magic, presumably).

When some process has a file open, what will unlink() do?

1) The file handles inherited by processes via execve will remain open until explicitly closed or the process exits.

2) unlink will not block. It will simply remove the path and decrement the reference count of the hard-linked file, at which point the filesystem may remove the referenced file and free the space associated with it once the file is no longer opened by any process. unlink will return 0 unless there was an I/O or permissions error, etc.

What really happens when deleting file

This is offtopic and more than one question, but:

A softlink in Linux is not connected to an inode. It only have the name of the file (see the size of the softlink? it is the length of the name is links to!). Even renaming the original will break the link.

[bart@localhost link]$ touch foo
[bart@localhost link]$ ln -s foo bar
[bart@localhost link]$ ls -la
lrwxrwxrwx 1 bart bart 3 Dec 13 21:09 bar -> foo
-rw-rw-r-- 1 bart bart 0 Dec 13 21:09 foo
[bart@localhost link]$ mv foo foo2
[bart@localhost link]$ ls -la
lrwxrwxrwx 1 bart bart 3 Dec 13 21:09 bar -> foo
-rw-rw-r-- 1 bart bart 0 Dec 13 21:09 foo2
[bart@localhost link]$ cat bar
cat: bar: No such file or directory

An inode is gone if you delete a file.

Can a hard link ever point to a deleted file?

Consider an example,

 $ touch aFile.txt
$ ls -i aFile.txt # -i is the option to look at the inode of a file
2621520 aFile.txt

$ ln aFile.txt 2File.txt # Hardlink the file to another one
$ ls -i 2File.txt
2621520 2File.txt # inode is pointing to the same location

$ rm aFile.txt # Original file gets deleted
$ ls 2File.txt
2File.txt

$ ls -i 2File.txt # inode survives and still pointing to the same location
2621520 2File.txt

Read here more on inodes.

EDIT:
stat can show you the number of hardlinks of a file. You can use -c '%h' option to view that:

# after the hardlink to 2File.txt
$ stat -c '%h' aFile.txt
2

Read potentially deleted file in Bash

You cannot have that guarantee in bash (that the output is either the entire file prefixed by its size, or else -1), since, as you mentioned something can happen between the two commands (and processes).

BTW, that file might be truncated by some other process (doing ftruncate(2)...), so you cannot have any guarantee on getting the "totality" of the content.

You might consider using advisory locking (e.g. with flock(2) or lockf(3) ...; consider also flock(1) in a shell script), which works well only when all programs changing that file agree on that locking (so you need to adopt a whole system convention).

Perhaps you want to use some RDBMS server providing ACID-idity guarantees.

But I also wonder what will happen if the file is deleted during the execution of cat "$file". Will it be fully/partially outputted, is there a risk to read unrelated characters if $file get overwritten on the drive?

No. If you have some process running cat (probably the /bin/cat program, see cat(1)) that process keeps an opened file descriptor on the $file. So the data won't be released (or rewritten) as long as some opened file descriptor refers to that file.

Perhaps you could write a simple C program (which runs in the same process, in contrast to several commands in some shell script) which opens the file, uses fstat(2) (perhaps via fileno(3) if you use stdio functions) on the opened file descriptor, and loops to copy its content. That does not protect you from hostile ftruncate(2) done by other processes during that copy.

If you don't care about truncation or overwriting and only about premature rm (or unlink(2)) you might use a temporary additional hard link. Perhaps as simple as:

 newhardlink=".newhardlink$$"
ln "$file" "$newhardlink"
stat --printf="%s" "$newhardlink"
cat "$newhardlink"
rm "$newhardlink"

If you are afraid of different filesystems, the you might do

 mydir=$(dirname "$file")
newhardlink="$mydir/.newhardlink$$"

instead of newhardlink=".newhardlink$$" and you could play trap tricks to have the final cleanup rm "$newhardlink" done in all cases.

Be also aware of inotify(7) (probably an overkill for your situation)

Better yet, change the way ffmpeg is started so that it uses some temporary file (see mktemp(1), mkstemp(3))....

Or use chepner subshell trick and in that subshell stat --printf="%s" -L /dev/stdin just before the cat



Related Topics



Leave a reply



Submit