Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Unlink and relink a file into the filesystem

118 views
Skip to first unread message

Aleksandar Kuktin

unread,
Jul 3, 2016, 4:38:24 PM7/3/16
to
I can't find the comp.something.something group that would be more suited
for this question, and alt.os.linux has bigger traffic, and the question
is interesting, and it's on Linux, and it'll be published as FOSS so I'll
just shoot. :)

I'm making a for-the-art super-small web server in Ruby, using WEBrick
(part of the standard library). I always wanted to mess with servlets,
and I tried using Java and Jetty, but Jetty is so arcane that it all
started feeling like work after a single evening. WEBrick is nicer - you
get something that works (responds to HTTP) after maybe 15 minutes, the
entire codebase can actually fit in your head without a problem and if
you squint, you can pretend it's not written in an interpreted language.

But WEBrick is inserting a bug in my application and it's hard to get rid
of it.

First: what am I trying to do. I want to have a web server you can use to
transfer files between computers on a LAN. You fire it on one machine and
then use a regular web browser on the other machine to download files or
upload files.

Among all possible error conditions, there is a possibility that the
connection breaks before the upload completes. I want the application to
delete the partially uploaded file. But WEBrick gets in the way. The
server handles everything related to Content-Length. If the connection
breaks, the server will kill the servlet, without informing it about
anything. So, my code in the servlet that is supposed to delete the
partial upload fails.

There seems to be something that can be done with the Ruby language to
fix this (there is *some* support for destructors), but it really isn't
orthogonal, and I was wondering if someone maybe has an idea how to use
the system for this.

Here's what I was thinking: when the upload starts, create a file and
open a file handle. Then delete (unlink) the file and fill it with
content as the upload progresses. Once the upload finishes successfully,
relink the file into the filesystem.

My question is how to relink a filehandle into a filesystem. :) What
system call, or other magic? :)

Aleksandar Kuktin

unread,
Jul 3, 2016, 5:15:22 PM7/3/16
to
On Sun, 03 Jul 2016 20:38:21 +0000, Aleksandar Kuktin wrote:

> [long intro]
>
> Among all possible error conditions, there is a possibility that the
> connection breaks before the upload completes. I want the application to
> delete the partially uploaded file. But WEBrick gets in the way. The
> server handles everything related to Content-Length. If the connection
> breaks, the server will kill the servlet, without informing it about
> anything. So, my code in the servlet that is supposed to delete the
> partial upload fails.

This turns out to not be correct. By reading the code for WEBrick I
realized I had a wrong undestanding of where the code executes
(specifically, I thought the exception gets thrown in the code that calls
mine, turns out it gets thrown in WEBrick code I call). I can intercept
the exception and delete everything I need to delete before letting it
propagate further up the call stack.

> [a bit about destructors]
>
> Here's what I was thinking: when the upload starts, create a file and
> open a file handle. Then delete (unlink) the file and fill it with
> content as the upload progresses. Once the upload finishes successfully,
> relink the file into the filesystem.
>
> My question is how to relink a filehandle into a filesystem. :) What
> system call, or other magic? :)

The question still stands, however. If anyone wants to take a stab at it,
please do. :) I'm sure it'll come up again for me in a few years.

marrgol

unread,
Jul 3, 2016, 5:23:31 PM7/3/16
to
On 2016-07-03 23:15, Aleksandar Kuktin wrote:
>> My question is how to relink a filehandle into a filesystem. :) What
>> system call, or other magic? :)
>
> The question still stands

I don't think you can "relink a filehandle into a filesystem", so
just some other magic then: ;-)

https://www.linux.com/news/bring-back-deleted-files-lsof

--
mrg

Jasen Betts

unread,
Jul 5, 2016, 6:31:35 AM7/5/16
to
On 2016-07-03, marrgol <mar...@address.invalid> wrote:
> On 2016-07-03 23:15, Aleksandar Kuktin wrote:
>>> My question is how to relink a filehandle into a filesystem. :) What
>>> system call, or other magic? :)
>>
>> The question still stands
>
> I don't think you can "relink a filehandle into a filesystem", so
> just some other magic then: ;-)

You can eg: if you can access a reference to the deleted file in
/proc/{NUMBER}/fd/ you can undelete it by creating a hard link

This means used to be usable to save flash video.


--
This email has not been checked by half-arsed antivirus software

marrgol

unread,
Jul 5, 2016, 12:02:36 PM7/5/16
to
On 2016-07-05 10:17, Jasen Betts wrote:
>>>> My question is how to relink a filehandle into a filesystem. :) What
>>>> system call, or other magic? :)
>>>
>>> The question still stands
>>
>> I don't think you can "relink a filehandle into a filesystem", so
>> just some other magic then: ;-)
>
> You can eg: if you can access a reference to the deleted file in
> /proc/{NUMBER}/fd/ you can undelete it by creating a hard link

Hard link to what exactly? And how do you create a cross-device
or cross-filesystem hard link?


--
mrg

Jasen Betts

unread,
Jul 6, 2016, 2:04:41 AM7/6/16
to
I'm not sure... I can't make it work today:

echo test > /tmp/gon ## create a file
tail -f /tmp/gon & ## open the file
rm /tmp/gon ## unlink it

## try to link the inode
ln /proc/$!/fd/3 /tmp/bak ## doesn't work

ln -L /proc/$!/fd/3 /tmp/bak ## also doesn't work


## but, I can still read the content

cat /proc/$!/fd/3 ## works!

Richard Kettlewell

unread,
Jul 6, 2016, 4:40:22 AM7/6/16
to
Jasen Betts <ja...@xnet.co.nz> writes:
> On 2016-07-05, marrgol <mar...@address.invalid> wrote:
>> On 2016-07-05 10:17, Jasen Betts wrote:
>>>>>> My question is how to relink a filehandle into a filesystem. :) What
>>>>>> system call, or other magic? :)
>>>>>
>>>>> The question still stands
>>>>
>>>> I don't think you can "relink a filehandle into a filesystem", so
>>>> just some other magic then: ;-)
>>>
>>> You can eg: if you can access a reference to the deleted file in
>>> /proc/{NUMBER}/fd/ you can undelete it by creating a hard link
>>
>> Hard link to what exactly? And how do you create a cross-device
>> or cross-filesystem hard link?
>
> I'm not sure... I can't make it work today:
>
> echo test > /tmp/gon ## create a file
> tail -f /tmp/gon & ## open the file
> rm /tmp/gon ## unlink it
>
> ## try to link the inode
> ln /proc/$!/fd/3 /tmp/bak ## doesn't work
>
> ln -L /proc/$!/fd/3 /tmp/bak ## also doesn't work

link() does not follow symbolic links. If you try to hardlink a name
which resolves to a symbolic link you just get another name for the same
symbolic link. Notice the inode numbers and counts below:

$ touch a
$ ln -s a b
$ ln b c
$ ls -ldi a b c
2359598 -rw-rw-r-- 1 richard richard 0 Jul 6 09:29 a
2360014 lrwxrwxrwx 2 richard richard 1 Jul 6 09:29 b -> a
2360014 lrwxrwxrwx 2 richard richard 1 Jul 6 09:29 c -> a

If you try to hard link to a psuedo-file in /proc/fd/... then you’re
attempting to hard-link across filesystems, which does not work.

You can play some daft games with bind mounts though:

$ echo whatever > a
$ cat a
whatever
$ sleep 10000 < a
^Z
[1]+ Stopped sleep 10000 < a
$ %&
[1]+ sleep 10000 < a &
$ rm a
$ ls -l /proc/9429/fd/0
lr-x------ 1 richard richard 64 Jul 6 09:33 /proc/9429/fd/0 -> /home/richard/a (deleted)
$ touch b
$ cat b
$ sudo mount -o bind /proc/9429/fd/0 b
$ cat b
whatever
$ kill 9429
[1]+ Terminated sleep 10000 < a
$ ls -l /proc/9429/fd/0
ls: cannot access ‘/proc/9429/fd/0’: No such file or directory
$ cat b
whatever
$ sudo umount b
$ cat b
$

A solution to the OPs original problem, as I understand it, is: write
the partial uploads to temporary files. When upload completes rename
them into their required name. Use a cronjob or something similar that
periodically cleans up stale temporary files. Put the PID of the
uploading process in the name so you can avoid deleting live ones, or
maybe just assume anything older than (for instance) a day is stale.

--
http://www.greenend.org.uk/rjk/

Jasen Betts

unread,
Jul 6, 2016, 6:01:27 PM7/6/16
to
On 2016-07-06, Richard Kettlewell <inv...@invalid.invalid> wrote:
> Jasen Betts <ja...@xnet.co.nz> writes:
>> On 2016-07-05, marrgol <mar...@address.invalid> wrote:
>>> On 2016-07-05 10:17, Jasen Betts wrote:
>>>>
>>>>> I don't think you can "relink a filehandle into a filesystem", so
>>>>> just some other magic then: ;-)
>>>>
>>>> You can eg: if you can access a reference to the deleted file in
>>>> /proc/{NUMBER}/fd/ you can undelete it by creating a hard link
>>>
>>> Hard link to what exactly? And how do you create a cross-device
>>> or cross-filesystem hard link?
>>
>> I'm not sure... I can't make it work today:
>>
>> echo test > /tmp/gon ## create a file
>> tail -f /tmp/gon & ## open the file
>> rm /tmp/gon ## unlink it
>>
>> ## try to link the inode
>> ln /proc/$!/fd/3 /tmp/bak ## doesn't work
>>
>> ln -L /proc/$!/fd/3 /tmp/bak ## also doesn't work
>
> link() does not follow symbolic links. If you try to hardlink a name
> which resolves to a symbolic link you just get another name for the same
> symbolic link. Notice the inode numbers and counts below:
>
> $ touch a
> $ ln -s a b
> $ ln b c
> $ ls -ldi a b c

it can follow symlinks.

$ touch a
$ ln -s a b
$ ln b c
$ ln -L c d
$ ls -ldi [a-d]
2490433 -rw-r--r-- 2 jasen jasen 0 Jul 7 08:56 a
2490435 lrwxrwxrwx 2 jasen jasen 1 Jul 7 08:56 b -> a
2490435 lrwxrwxrwx 2 jasen jasen 1 Jul 7 08:56 c -> a
2490433 -rw-r--r-- 2 jasen jasen 0 Jul 7 08:56 d

> If you try to hard link to a psuedo-file in /proc/fd/... then you’re
> attempting to hard-link across filesystems, which does not work.

/proc/$!/fd/3 is some sort of link to the original file, so it should
serve as sufficient reference to the original, but it seems
link(1) can't follow it. The linkat(2) man page has some interesting
suff but I don't have time to experiment with it. It mentions that
AT_EMPTY_PATH may not work if link count is zero which probably
means it won't work for me.

ln -L /proc/$!/fd/3 /tmp/bak

does work _before_ the original is unlinked (if the filesystem matches),
so it seems that link-count is the problem, and suggests that 'ln -L'
may be using linkat(2) with AT_EMPTY_PATH.

> You can play some daft games with bind mounts though:

> $ sudo mount -o bind /proc/9429/fd/0 b

cool!
0 new messages