Thursday, May 17, 2007
Re-linking an unlinked file with fsdb
Given that you have an unlinked file on a file system, how do you relink it into the file system?
One of the things you can do is just work around the problem. You could just copy /proc/XXX/fd/Y, which gets you the contents of the file but not the actual file itself.
What you really want to be able to do is "ln /proc/XXX/fd/Y /some/dir/foo". Theoretically, there's no reason why this shouldn't work, given that you know the inode number of the unlinked file within its file system via /proc/XXX/fd/Y, but it doesn't currently work to do this.
I tried the following with fsdb (on Solaris on a UFS file system), and it worked to relink the file into the file system. Doing something like this isn't generally advisable, as it involves manipulating the disk directly. This bypasses the file-caching mechanism in the kernel, so you'll end up with a disk image that doesn't match what the kernel thinks it should look like. In my case, things were fine after a reboot and fsck, but I may simply have gotten lucky.
(I was prompted to try this after discovering that it's possible to unlink(1M) a non-empty directory. I was curious to see whether or not the inodes in that directory were orphaned by doing so, and it turns out that they are. Given that I had a file system with orphaned inodes, I wanted to see if I could remedy the situation. I was mostly interested to see if I could fix the live file system, which I was unfortunately unable to do.)
The unlinked file that I'm concerned about existed in the chadfoo directory. (Not that this really matters, given that the scope of the name space for these inode numbers is /var, but I want to link it back to its original location.)
The plan is to create an empty file ("bigfile") in /var/tmp/chadfoo and use fsdb to change the inode number for that particular directory entry to be that of the unlinked file. So that I don't lose the inode of the empty file, I create a second link to it, "bigfile2", and I fix link counts afterwards so that everything's kosher.
Here, I look at the directory entries for inode 4970 (/var/tmp/chadfoo):
And then I set the inode number for directory entry number 2 to be that of the unlinked file (decimal 19037):
What I've done doesn't change the link counts, though, so I need to do this manually:
And at this point, I reboot into single user, fsck /var (which shows a few problems to be corrected), and things are fine.
One of the things you can do is just work around the problem. You could just copy /proc/XXX/fd/Y, which gets you the contents of the file but not the actual file itself.
What you really want to be able to do is "ln /proc/XXX/fd/Y /some/dir/foo". Theoretically, there's no reason why this shouldn't work, given that you know the inode number of the unlinked file within its file system via /proc/XXX/fd/Y, but it doesn't currently work to do this.
I tried the following with fsdb (on Solaris on a UFS file system), and it worked to relink the file into the file system. Doing something like this isn't generally advisable, as it involves manipulating the disk directly. This bypasses the file-caching mechanism in the kernel, so you'll end up with a disk image that doesn't match what the kernel thinks it should look like. In my case, things were fine after a reboot and fsck, but I may simply have gotten lucky.
(I was prompted to try this after discovering that it's possible to unlink(1M) a non-empty directory. I was curious to see whether or not the inodes in that directory were orphaned by doing so, and it turns out that they are. Given that I had a file system with orphaned inodes, I wanted to see if I could remedy the situation. I was mostly interested to see if I could fix the live file system, which I was unfortunately unable to do.)
# fsdb -o w /dev/md/rdsk/d1 fsdb of /dev/md/rdsk/d1 (Opened for write) -- last mounted on /var fs_clean is currently set to FSLOG fs_state consistent (fs_clean CAN be trusted) /dev/md/rdsk/d1 > :cd tmp /dev/md/rdsk/d1 > :ls -l /tmp: i#: b6 ./ i#: 2 ../ i#: 4d9a .java/ i#: 1279 SMB--0A40081A-044_0 i#: 4dbd autosave/ i#: 4970 chadfoo/ i#: 127b cifs_0 i#: 127c host_0 i#: 4d9e mancache/ i#: 1277 named.pid i#: 1276 named.run i#: 127a smb--0a40081a-044_0 /dev/md/rdsk/d1 >
The unlinked file that I'm concerned about existed in the chadfoo directory. (Not that this really matters, given that the scope of the name space for these inode numbers is /var, but I want to link it back to its original location.)
The plan is to create an empty file ("bigfile") in /var/tmp/chadfoo and use fsdb to change the inode number for that particular directory entry to be that of the unlinked file. So that I don't lose the inode of the empty file, I create a second link to it, "bigfile2", and I fix link counts afterwards so that everything's kosher.
Here, I look at the directory entries for inode 4970 (/var/tmp/chadfoo):
/dev/md/rdsk/d1 > 4970:inode; 0:dir?d i#: 4970 . /dev/md/rdsk/d1 > 4970:inode; 1:dir?d i#: b6 .. /dev/md/rdsk/d1 > 4970:inode; 2:dir?d i#: 4a62 bigfile /dev/md/rdsk/d1 > 4970:inode; 3:dir?d i#: 4a62 bigfile2 /dev/md/rdsk/d1 >
And then I set the inode number for directory entry number 2 to be that of the unlinked file (decimal 19037):
/dev/md/rdsk/d1 > 4970:inode; 2:dir=0t19037 i#: 4a5d bigfile /dev/md/rdsk/d1 >
What I've done doesn't change the link counts, though, so I need to do this manually:
/dev/md/rdsk/d1 > 4a62:inode?i i#: 4a62 md: ----rw-r--r-- uid: 2d85 gid: a ln: 2 bs: 0 sz : c_flags : 0 0 accessed: Wed May 16 11:12:22 2007 modified: Wed May 16 11:12:22 2007 created : Wed May 16 11:13:02 2007 /dev/md/rdsk/d1 > :ln=1 i#: 4a62 md: ----rw-r--r-- uid: 2d85 gid: a ln: 1 bs: 0 sz : c_flags : 0 0 accessed: Wed May 16 11:12:22 2007 modified: Wed May 16 11:12:22 2007 created : Wed May 16 11:13:02 2007 /dev/md/rdsk/d1 > 4a5d:inode?i i#: 4a5d md: ----rw-r--r-- uid: 2d85 gid: a ln: 0 bs: 200410 sz : c_flags : 0 40000000 db#0: 28e00 db#1: 28e48 db#2: 28e50 db#3: 28e58 db#4: 28e60 db#5: 28e68 db#6: 28e70 db#7: 28e78 db#8: 28e80 db#9: 28e88 db#a: 28e90 db#b: 28e98 ib#0: 204748 ib#1: cc808 accessed: Wed May 16 10:39:20 2007 modified: Wed May 16 10:39:44 2007 created : Wed May 16 10:39:44 2007 /dev/md/rdsk/d1 > :ln=1 i#: 4a5d md: ----rw-r--r-- uid: 2d85 gid: a ln: 1 bs: 200410 sz : c_flags : 0 40000000 db#0: 28e00 db#1: 28e48 db#2: 28e50 db#3: 28e58 db#4: 28e60 db#5: 28e68 db#6: 28e70 db#7: 28e78 db#8: 28e80 db#9: 28e88 db#a: 28e90 db#b: 28e98 ib#0: 204748 ib#1: cc808 accessed: Wed May 16 10:39:20 2007 modified: Wed May 16 10:39:44 2007 created : Wed May 16 10:39:44 2007 /dev/md/rdsk/d1 >
And at this point, I reboot into single user, fsck /var (which shows a few problems to be corrected), and things are fine.