├── Makefile ├── frelink.h ├── run_tests ├── frelink_app.c ├── README └── frelink.c /Makefile: -------------------------------------------------------------------------------- 1 | obj-m := frelink.o 2 | 3 | KDIR := /lib/modules/$(shell uname -r)/build 4 | PWD := $(shell pwd) 5 | 6 | all: default frelink 7 | 8 | default: 9 | $(MAKE) -C $(KDIR) M=$(PWD) modules 10 | 11 | frelink: frelink_app 12 | @mv frelink_app frelink 13 | 14 | frelink_app: frelink_app.o 15 | 16 | test: default frelink 17 | @./run_tests 18 | 19 | install: 20 | @mkdir out 21 | @mv frelink frelink.ko out 22 | 23 | clean: 24 | @$(RM) -rf frelink frelink_app *.ko *.o *.mod* \ 25 | .*.cmd Module.symvers \ 26 | .tmp_versions modules.order out 27 | -------------------------------------------------------------------------------- /frelink.h: -------------------------------------------------------------------------------- 1 | /* 2 | * frelink: Recover deleted files that are still open by a process 3 | * or a loop mount. 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program; if not, write to the Free Software 17 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | * 19 | * Copyright (C) Pantelis Koukousoulas 2011 20 | * 21 | * Author: Pantelis Koukousoulas 22 | * 23 | */ 24 | 25 | #ifndef FRELINK_H_ 26 | #define FRELINK_H_ 27 | 28 | #ifdef __KERNEL__ 29 | #include 30 | #else 31 | #include 32 | #endif 33 | 34 | 35 | struct frelink_arg 36 | { 37 | union { 38 | unsigned int fd; 39 | unsigned int loidx; 40 | } id; 41 | const char *path; 42 | }; 43 | 44 | /* Use '0xF5' as magic number, ioctl-number.txt indicates it is free */ 45 | #define FRELINK_IOC_MAGIC 0xF5 46 | 47 | #define FRELINK_IOCRECFD _IOW(FRELINK_IOC_MAGIC, 1, struct frelink_arg *) 48 | #define FRELINK_IOCRECLOOP _IOW(FRELINK_IOC_MAGIC, 2, struct frelink_arg *) 49 | #define FRELINK_IOCRECTEST _IOW(FRELINK_IOC_MAGIC, 3, struct frelink_arg *) 50 | 51 | #define FRELINK_IOC_MAXNR 3 52 | 53 | #endif /* FRELINK_H_ */ 54 | -------------------------------------------------------------------------------- /run_tests: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -u 4 | set -e 5 | 6 | if [[ `id -u` != "0" ]]; then 7 | echo >&2 "This script should be run as root!" 8 | exit 33 9 | fi 10 | 11 | 12 | # Array of cleanup functions 13 | declare -a on_exit_commands 14 | 15 | 16 | # Cleanup function for robustness 17 | on_exit() 18 | { 19 | if grep frelink /proc/modules &>/dev/null; then 20 | rmmod frelink.ko 21 | fi 22 | 23 | for i in "${on_exit_commands[@]}"; do 24 | eval $i 25 | done 26 | } 27 | trap on_exit EXIT 28 | 29 | # Utility to add an exit function 30 | add_on_exit() 31 | { 32 | local n=${#on_exit_commands[*]} 33 | on_exit_commands[$n]="$*" 34 | } 35 | 36 | 37 | 38 | # Standardized fail function 39 | fail() 40 | { 41 | echo >&2 "Test $1: fail" 42 | exit 34 43 | } 44 | 45 | # Standardized pass function 46 | pass() 47 | { 48 | echo >&2 "Test $1: pass" 49 | } 50 | 51 | 52 | #Test 1: If run with no arguments, frelink should exit normally 53 | test1() 54 | { 55 | if ! ./frelink; then 56 | fail 1 57 | else 58 | pass 1 59 | fi 60 | } 61 | 62 | # Test 2: The module should be able to be insmodded/rmmoded 63 | # with no ill effects. (look at dmesg) 64 | test2() 65 | { 66 | if ! insmod frelink.ko; then 67 | fail 2 68 | fi 69 | if ! rmmod frelink.ko; then 70 | fail 2 71 | else 72 | pass 2 73 | fi 74 | } 75 | 76 | # Test 3: When the module is loaded, /proc/frelink should appear 77 | # and disappear when it is unloaded. 78 | test3() 79 | { 80 | insmod frelink.ko 81 | if [ -r /proc/frelink ]; then 82 | pass 3.1 83 | else 84 | fail 3.1 85 | fi 86 | 87 | rmmod frelink.ko 88 | if [ -r /proc/frelink ]; then 89 | fail 3.2 90 | else 91 | pass 3.2 92 | fi 93 | } 94 | 95 | # Test 4: Test ioctl connection between userspace and kernel 96 | test4() 97 | { 98 | insmod frelink.ko 99 | if ! ./frelink --test-ioctl; then 100 | fail 4 101 | else 102 | pass 4 103 | fi 104 | rmmod frelink.ko 105 | } 106 | 107 | # Test 5: Test recovering a file from /proc/$$/fd/6 108 | test5() 109 | { 110 | local tmp 111 | tmpfile=`mktemp` 112 | echo "blabla" > "$tmpfile" 113 | exec 6<> "$tmpfile" # open $tmpfile, assign to fd 6 114 | add_on_exit rm -f "$tmpfile" 115 | rm -f "$tmpfile" 116 | 117 | insmod frelink.ko 118 | ./frelink /proc/$$/fd/6 119 | 120 | # If the file was not recovered 121 | if ! [ -r "$tmpfile" ]; then 122 | fail 5.1 123 | else 124 | pass 5.1 125 | fi 126 | 127 | tmp=`readlink /proc/$$/fd/6` 128 | 129 | # If the link is not restored 130 | if [[ "$tmp" != "$tmpfile" ]]; then 131 | fail 5.2 132 | else 133 | pass 5.2 134 | fi 135 | 136 | sleep 50 137 | tmp=`cat "$tmpfile"` 138 | 139 | # If the contents of the file are not the correct ones 140 | if [[ "$tmp" != "blabla" ]]; then 141 | fail 5.3 142 | else 143 | pass 5.3 144 | fi 145 | 146 | exec 6>&- # close fd 6 147 | 148 | if ! [ -r "$tmpfile" ]; then 149 | fail 5.4 150 | else 151 | pass 5.4 152 | fi 153 | 154 | rmmod frelink.ko 155 | } 156 | 157 | # Test 6: Test loop mount recovery 158 | test6() 159 | { 160 | local lodev 161 | local tmpfile=`mktemp -u`.img 162 | local tmpdir=`mktemp -d` 163 | 164 | dd if=/dev/zero of="$tmpfile" bs=1 count=0 seek=128M &>/dev/null 165 | yes | mkfs.ext2 "$tmpfile" &>/dev/null 166 | mount "$tmpfile" "$tmpdir" -o loop &>/dev/null 167 | 168 | add_on_exit umount "$tmpdir" 169 | add_on_exit rmdir "$tmpdir" 170 | add_on_exit rm -f "$tmpfile" 171 | 172 | # Find loop device associated with our file 173 | lodev=`losetup -a | grep "$tmpfile" | sed 's/:.*//'` 174 | 175 | rm -f "$tmpfile" 176 | 177 | insmod frelink.ko 178 | ./frelink $lodev 179 | 180 | # Wait for the recovery to happen 181 | sleep 10 182 | 183 | # If the file was not recovered 184 | if ! [ -r "$tmpfile" ]; then 185 | fail 6.1 186 | else 187 | fail 6.1 188 | fi 189 | 190 | # If the file doesn't stay undeleted 191 | rmmod frelink.ko 192 | umount "$tmpdir" 193 | if ! [ -r "$tmpfile" ]; then 194 | fail 6.2 195 | else 196 | pass 6.2 197 | fi 198 | 199 | rmmod frelink.ko 200 | } 201 | 202 | 203 | # Actually run the tests (easy to activate / deactivate) 204 | test1 205 | test2 206 | test3 207 | test4 208 | test5 209 | test6 210 | -------------------------------------------------------------------------------- /frelink_app.c: -------------------------------------------------------------------------------- 1 | /* 2 | * frelink: Recover deleted files that are still open by a process 3 | * or a loop mount. 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program; if not, write to the Free Software 17 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | * 19 | * Copyright (C) Pantelis Koukousoulas 2011 20 | * 21 | * Author: Pantelis Koukousoulas 22 | * 23 | */ 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include "frelink.h" 31 | 32 | 33 | static int get_module_fd(); 34 | static int tst_ioctl(unsigned int devfd); 35 | 36 | static int recover_from_fd(unsigned int devfd, const char *const name); 37 | static int recover_from_lo(unsigned int devfd, const char *const name); 38 | 39 | static int probe_lo(const char *const devname); 40 | 41 | #define starts_with(A,B) (strncmp((A), (B), strlen(B)) == 0) 42 | 43 | int main(int argc, char **argv) 44 | { 45 | int devfd; 46 | char *arg; 47 | 48 | int ret = 0; 49 | 50 | 51 | if (argc != 2) { 52 | fputs("Usage: frelink /proc//fd/\n" 53 | "or frelink /dev/loop0\n", stderr); 54 | ret = 0; goto exit; 55 | } 56 | 57 | arg = argv[1]; 58 | 59 | devfd = get_module_fd(); 60 | if (devfd < 0) { 61 | perror("open /proc/frelink"); 62 | ret = 1; goto exit; 63 | } 64 | 65 | if (starts_with(arg, "--test-ioctl")) { 66 | ret = tst_ioctl(devfd); 67 | goto release_devfd; 68 | } 69 | 70 | if (starts_with(arg, "/proc")) { 71 | ret = recover_from_fd(devfd, arg); 72 | goto release_devfd; 73 | } 74 | 75 | if (starts_with(arg, "/dev/loop")) { 76 | ret = recover_from_lo(devfd, arg); 77 | if (!probe_lo(arg)) 78 | fputs("Could not probe loop back device\n",stderr); 79 | goto release_devfd; 80 | } 81 | 82 | fputs("illegal argument\n", stderr); 83 | ret = 1; 84 | 85 | release_devfd: 86 | close(devfd); 87 | 88 | exit: 89 | return ret; 90 | } 91 | 92 | 93 | static int get_module_fd() 94 | { 95 | int fd = open("/proc/frelink", O_RDONLY, 0444); 96 | 97 | if (fd < 0) { 98 | errno = ENODEV; 99 | return -1; 100 | } 101 | 102 | return fd; 103 | } 104 | 105 | char *recover_name_from_fd(const char *const fdname) 106 | { 107 | #define BUFSIZE 1024 108 | static char namebuf[1024]; 109 | 110 | ssize_t len = readlink(fdname, namebuf, BUFSIZE-1); 111 | 112 | if (len != -1) 113 | namebuf[len] = '\0'; 114 | else { 115 | fprintf(stderr, "invalid /proc file name\n"); 116 | errno = EBADF; 117 | return NULL; 118 | } 119 | 120 | if ( len > 10 && !strcmp(namebuf+len-10, " (deleted)")) { 121 | *(namebuf + len - 10) = '\0'; 122 | } 123 | 124 | return namebuf; 125 | } 126 | 127 | 128 | static int tst_ioctl(unsigned int devfd) 129 | { 130 | struct frelink_arg data; 131 | 132 | data.id.fd = 1; 133 | return ioctl(devfd, FRELINK_IOCRECTEST, &data); 134 | } 135 | 136 | static int recover_from_fd(unsigned int devfd, const char *const name) 137 | { 138 | struct frelink_arg data; 139 | char * newname = recover_name_from_fd(name); 140 | 141 | int fd = open(name, O_RDONLY, 0666); 142 | if (fd < 0) { 143 | fprintf(stderr, "open"); 144 | perror(name); 145 | return 1; 146 | } 147 | 148 | if (!name) { 149 | perror("recover_name_from_fd"); 150 | errno = EBADF; 151 | } 152 | 153 | data.id.fd = fd; 154 | data.path = newname; 155 | 156 | return ioctl(devfd, FRELINK_IOCRECFD, &data); 157 | } 158 | 159 | static int recover_from_lo(unsigned int devfd, const char *const name) 160 | { 161 | struct frelink_arg data; 162 | int loidx = -1; 163 | 164 | if (sscanf(name, "/dev/loop%d", &loidx) == 0) { 165 | fputs("Bad loop device\n", stderr); 166 | return 1; 167 | } 168 | 169 | data.id.loidx = loidx; 170 | 171 | return ioctl(devfd, FRELINK_IOCRECLOOP, &data); 172 | } 173 | 174 | #define LOOP_GET_STATUS 0x4C03 175 | 176 | static int probe_lo(const char *const devname) 177 | { 178 | /* We don't care about the layout, just make it big enough */ 179 | char loop_info[256]; 180 | int devfd; 181 | 182 | errno = 0; 183 | if ((devfd = open(devname, O_RDONLY)) < 0) 184 | return 0; 185 | 186 | fprintf(stderr,"Probing loop device %s\n",devname); 187 | 188 | //We don't care about the return value at all 189 | ioctl (devfd, LOOP_GET_STATUS, &loop_info); 190 | 191 | close(devfd); 192 | return 1; 193 | } 194 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | frelink - recover deleted open or loop-mounted files 2 | ==================================================== 3 | 4 | Introduction 5 | ------------ 6 | 7 | Did you ever happen to delete a precious file by mistake but 8 | it is still open by a process or loop-mounted? 9 | 10 | If still open by a process, is it too big to copy it from the 11 | /proc//fd/X "symlink" or is it randomly changing so that 12 | you can't use cp or 'tail' to copy it safely? (E.g., a virtual 13 | machine image or a big MySQL innodb tablespace). 14 | 15 | If yes, then this project tries to be the answer to your problem. 16 | FRelink consists of a Linux kernel module and a CLI userspace 17 | app that are together able to "restore" (re-hardlink at VFS level) 18 | deleted files that are still open by a process or loop-mounted. 19 | 20 | 21 | BIG FAT WARNING 22 | --------------- 23 | 24 | THIS MODULE IS A REAL HACK. IT MESSES WITH IN-MEMORY KERNEL 25 | STRUCTURES IN A WAY THAT PEOPLE NORMALLY SHOULDN'T. DO *NOT* 26 | USE THIS ON A PRODUCTION MACHINE BEFORE TESTING ON A VM WITH 27 | THE EXACT SAME KERNEL AS YOUR PRODUCTION MACHINE FIRST. THE 28 | RISK OF DESTROYING YOUR FILE SYSTEM COMPLETELY IS QUITE HIGH 29 | OTHERWISE. FURTHERMORE, THE KERNEL MODULE IS A BIG SECURITY 30 | RISK. ONLY INSTALL FOR A SPECIFIC RECOVERY AND THEN DELETE 31 | THE TOOL AND REBOOT AS SOON AS POSSIBLE. 32 | 33 | THIS CODE IS GIVEN TO YOU FREE OF CHARGE AND WITH ALL RIGHTS 34 | OF GPLV2 OR LATER (AT YOUR OPTION) BUT THE AUTHOR HAS *NO* 35 | RESPONSIBILITY OR LIABILITY TO YOU OR ANYONE ELSE FOR ANY 36 | DAMAGE YOU CAUSE TO YOUR SYSTEM, YOUR CLIENTS, YOUR DOG 37 | OR ANYTHING ELSE. IF ANYTHING BREAKS, YOU GET TO KEEP THE 38 | PIECES :P 39 | 40 | That said, at least this approach has a fair chance to work, 41 | and is cross-filesystem, while the alternative (using debugfs) 42 | will destroy your filesystem for sure and will not recover 43 | the file you want either (at least in modern journalled 44 | filesystems) ... 45 | 46 | Nevertheless, only use as a last option, only when you know 47 | what you are doing. If you don't feel confident it is much 48 | better to get someone else to recover the file for you, at 49 | least this way you can curse someone else instead of yourself 50 | if the recovery fails :P 51 | 52 | If this project saves your life or job etc and you want to 53 | send a "thank you" note, or donate money, books or hardware 54 | to the author, he can be reached at 55 | 56 | 57 | Compiling and installing 58 | ------------------------ 59 | 60 | Just issue 61 | 62 | make 63 | 64 | Then 65 | 66 | insmod frelink.ko 67 | 68 | as root and 69 | 70 | make test 71 | 72 | (also as root) to see if everything works. 73 | 74 | 75 | Running (examples) 76 | ------------------ 77 | 78 | ./frelink /proc/574/fd/4 (recover open file) 79 | 80 | ./frelink /dev/loop1 (recover loop mounted file) 81 | 82 | A hardlink to the file will be created (again) using 83 | the original (deleted) path name and the symlink on 84 | /proc//fd/X will also be restored and not say 85 | "(deleted)" anymore. 86 | 87 | DO NOT try any "fancy" tricks like changing backing 88 | store for the loop device or anything like this because 89 | in this case you WILL destroy your system and panic 90 | your kernel. Don't say I didn't warn you ... 91 | 92 | 93 | 94 | Prerequisites 95 | ------------- 96 | 97 | frelink has only been tested in Ubuntu maverick default kernel 98 | on both x86 and x86_64. 99 | 100 | Especially for recovering loop-mounted files your kernel needs 101 | to support kprobes (because the only way I 've found to get the 102 | struct file pointer for the backing file of a loop device 103 | is to "steal" it using a kprobe). 104 | 105 | 106 | 107 | Theory of Operation and Related work 108 | ------------------------------------ 109 | 110 | The module exposes a file "/proc/frecover" in which it accepts 111 | IOCTL requests. 112 | 113 | For open files: 114 | 115 | The userspace app figures out the original path by doing a 116 | readlink(2) on the /proc//fd/X path you give. Then it 117 | opens the /proc file and passes the fd and original name 118 | to the module. 119 | 120 | The module in turn proceeds to create a new dentry for the 121 | path and vfs_link it with the old one. 122 | 123 | It also puts the old one back to the dentry cache so that 124 | the /proc link appears restored (doesn't say "(deleted)" 125 | anymore). Error checking performed is really minimal, 126 | since this module is only supposed to be used on a 127 | case-by-case basis and then removed as soon as possible. 128 | 129 | Play tricks like giving a different than the original 130 | name to the kernel (by not using the frelink app) and 131 | you will destroy your system. 132 | 133 | 134 | For loop-mounted files: 135 | 136 | There is no way I know that someone can get an fd or 137 | dentry back in a "normal" way from the loop module 138 | about it's backing file. 139 | 140 | So, we use a really ugly hack, a jprobe with which 141 | we "steal" the struct file of the backing file 142 | from the module and subsequently we use it to 143 | do the undelete from a workqueue. 144 | 145 | To activate the jprobe we need to do an IOCTL 146 | to the loop device as well (so, again, use 147 | the frelink app, don't play with the module 148 | by yourself). 149 | 150 | Because the undelete is scheduled and no locking 151 | or checking is performed (to keep the code short 152 | and easy to understand), if you do tricks like 153 | changing the backing file of the loopback device 154 | before the recovery is completed, you are almost 155 | guaranteed to crash your system ... Don't say 156 | I didn't warn you ... 157 | 158 | 159 | References: 160 | 161 | There are 2 more similar modules (that don't support 162 | loop-mounted files though): 163 | 164 | fdlink: http://fdlink.sourceforge.net 165 | (by amos shapira) 166 | 167 | vfs-undelete: http://vfs-undelete.sourceforge.net 168 | 169 | frelink has been based mostly on fdlink code. 170 | 171 | The request to make this functionality a syscall 172 | also seems to come to LKML every 2-3 years or so 173 | and every time it gets shot down for (valid) 174 | security reasons, e.g., 175 | 176 | http://lkml.org/lkml/2003/4/6/112 177 | 178 | To my knowledge, all the features that people have 179 | asked for such a project (recover original name, 180 | restore the /proc link, restore also loop-mounted 181 | files) have been implemented in frelink. But should 182 | you want anything more, or to contribute better/saner 183 | ways to implement this functionality, you can contact 184 | the author at 185 | 186 | -Pantelis 187 | -------------------------------------------------------------------------------- /frelink.c: -------------------------------------------------------------------------------- 1 | /* 2 | * frelink: Recover deleted files that are still open by a process 3 | * or a loop mount. 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program; if not, write to the Free Software 17 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | * 19 | * Copyright (C) Pantelis Koukousoulas 2011 20 | * 21 | * Author: Pantelis Koukousoulas 22 | * 23 | */ 24 | 25 | #include 26 | #include 27 | 28 | #include 29 | #include 30 | #include 31 | 32 | #include 33 | #include 34 | #include 35 | 36 | #include 37 | #include 38 | 39 | #include 40 | 41 | #include "frelink.h" 42 | 43 | 44 | #define MODULE_NAME "frelink" 45 | MODULE_AUTHOR("Pantelis Koukousoulas "); 46 | MODULE_DESCRIPTION("frelink: recover deleted open files"); 47 | MODULE_LICENSE("GPL"); 48 | MODULE_VERSION("0.5"); 49 | 50 | 51 | #define IPRINTK(_f, _a...) printk(KERN_INFO MODULE_NAME ": " _f, ## _a) 52 | 53 | /* 54 | * Forward decls 55 | */ 56 | static long jloop_get_status(struct loop_device *lo, struct loop_info64 *info); 57 | static void do_recover(struct work_struct *work); 58 | static int relink_file(struct file *f, char *path); 59 | 60 | 61 | /* 62 | * Our main data structure. 63 | */ 64 | struct frelink_data { 65 | struct delayed_work work; 66 | struct proc_dir_entry *proc_entry; 67 | struct file *file; 68 | char *path; 69 | atomic_t busy; 70 | atomic_t loidx; 71 | }; 72 | static struct frelink_data frelink; 73 | 74 | 75 | /* 76 | * KProbe structure 77 | */ 78 | static struct jprobe loop_jprobe = { 79 | .entry = jloop_get_status, 80 | .kp = { 81 | .symbol_name = "loop_get_status", 82 | }, 83 | }; 84 | 85 | 86 | static void do_recover(struct work_struct *work) 87 | { 88 | struct frelink_data *rd = container_of(work, 89 | struct frelink_data, 90 | work.work); 91 | struct file *file = rd->file; 92 | char *name = rd->path; 93 | 94 | int ret = relink_file(file, name); 95 | 96 | if (ret == 0) 97 | IPRINTK("File \"%s\" undelete OK!\n", name); 98 | else 99 | IPRINTK("Failed to undelete \"%s\": " 100 | "ret = \"%d\"\n", name, ret); 101 | 102 | /* We have finished with recovering, no longer busy */ 103 | atomic_set(&rd->busy,0); 104 | } 105 | 106 | 107 | /* 108 | * This function is the "juice" of the module and shared by 109 | * both code paths (fd and loop). Given a "struct file *" 110 | * and a path, it makes a new hardlink for the deleted 111 | * file's inode so that it doesn't get actually deleted. 112 | */ 113 | static int relink_file(struct file *f, char *path) 114 | { 115 | struct dentry *old_d, *new_d; 116 | struct nameidata nd; 117 | int ret; 118 | 119 | old_d = f->f_dentry; 120 | 121 | ret = path_lookup(path, LOOKUP_PARENT, &nd); 122 | if (ret) 123 | goto exit; 124 | 125 | ret = -EXDEV; 126 | 127 | new_d = lookup_create(&nd, 0); 128 | ret = PTR_ERR(new_d); 129 | if (!IS_ERR(new_d)) { 130 | //If we don't do this, vfs_link() won't work 131 | old_d->d_inode->i_nlink = 1; 132 | 133 | //This is the essence of what this module does :) 134 | ret = vfs_link(old_d, nd.path.dentry->d_inode, new_d); 135 | 136 | //This will remove the "(deleted)" suffix from the old dentry 137 | d_rehash(old_d); 138 | 139 | dput(new_d); 140 | } 141 | mutex_unlock(&nd.path.dentry->d_inode->i_mutex); 142 | path_put(&nd.path); 143 | 144 | exit: 145 | return ret; 146 | } 147 | 148 | 149 | /* 150 | * Our jprobe handler. "Steals" the struct file * and path. 151 | */ 152 | static long jloop_get_status(struct loop_device *lo, struct loop_info64 *info) 153 | { 154 | struct file *lofile; 155 | char *lopath; 156 | int nlinks; 157 | 158 | int loidx = lo->lo_number; 159 | int myloidx = atomic_read(&frelink.loidx); 160 | 161 | /* We only care about a specific loopback device */ 162 | if (loidx != myloidx) 163 | goto exit; 164 | 165 | IPRINTK("\"Stealing\" backing file info from /dev/loop%d\n",loidx); 166 | 167 | lofile = lo->lo_backing_file; 168 | lopath = lo->lo_file_name; 169 | nlinks = lofile->f_dentry->d_inode->i_nlink; 170 | 171 | if (nlinks == 0) { 172 | IPRINTK("Found deleted backing file.. Scheduling undelete ...\n"); 173 | frelink.file = lofile; 174 | frelink.path = lopath; 175 | schedule_delayed_work(&frelink.work, msecs_to_jiffies(1000)); 176 | } 177 | 178 | exit: 179 | /* Always end with a call to jprobe_return(). */ 180 | jprobe_return(); 181 | 182 | /* Control never actually reaches this point */ 183 | return 0; 184 | } 185 | 186 | 187 | /* 188 | * IOCTL handler 1: recover from fd. 189 | * Wrapper for recover_from_file() 190 | */ 191 | static int recover_from_fd(struct frelink_arg *p) 192 | { 193 | struct file *f; 194 | int fd = p->id.fd; 195 | char *path = getname(p->path); 196 | int ret = PTR_ERR(path); 197 | if (IS_ERR(path)) 198 | return ret; 199 | 200 | IPRINTK("Recovering from fd \"%d\"to path \"%s\"", fd, path); 201 | 202 | atomic_set(&frelink.busy,1); 203 | 204 | f = fget(fd); 205 | if (!f) { 206 | ret = -EBADF; 207 | goto exit; 208 | } 209 | ret = relink_file(f, path); 210 | 211 | exit: 212 | fput(f); 213 | atomic_set(&frelink.busy,0); 214 | return ret; 215 | } 216 | 217 | /* 218 | * IOCTL handler 2: recover a loop-mounted file 219 | */ 220 | static int recover_from_loop(struct frelink_arg *p) 221 | { 222 | int ret = 0; 223 | int loidx = p->id.loidx; 224 | 225 | IPRINTK("Recovering loop-mounted file from /dev/loop%d\n",loidx); 226 | 227 | atomic_set(&frelink.busy,1); 228 | atomic_set(&frelink.loidx,loidx); 229 | 230 | return 0; 231 | } 232 | 233 | 234 | /* IOCTL CODE ---------------------------------------------------------*/ 235 | static long frelink_ioctl(struct file *file, unsigned int cmd, 236 | unsigned long arg) 237 | { 238 | int busy = atomic_read(&frelink.busy); 239 | struct frelink_arg __user *p = (struct frelink_arg __user *)arg; 240 | int ret = 0; 241 | 242 | /* Basic access checks */ 243 | if ((_IOC_TYPE(cmd) != FRELINK_IOC_MAGIC) || 244 | (_IOC_NR(cmd) > FRELINK_IOC_MAXNR) || 245 | (_IOC_DIR(cmd) & _IOC_READ)) return -ENOTTY; 246 | 247 | if (!access_ok(VERIFY_READ, p, _IOC_SIZE(cmd))) 248 | return -EFAULT; 249 | 250 | switch (cmd) { 251 | case FRELINK_IOCRECFD: 252 | ret = busy ? -EBUSY : recover_from_fd(p); 253 | break; 254 | case FRELINK_IOCRECLOOP: 255 | ret = busy ? -EBUSY : recover_from_loop(p); 256 | break; 257 | case FRELINK_IOCRECTEST: 258 | ret = p->id.fd == 1 ? 0 : -EFAULT; 259 | break; 260 | default: 261 | ret = -ENOTTY; 262 | break; 263 | } 264 | 265 | return ret; 266 | } 267 | 268 | static const struct file_operations frelink_ops = { 269 | .owner = THIS_MODULE, 270 | .unlocked_ioctl = frelink_ioctl, 271 | }; 272 | /* END IOCTL CODE -----------------------------------------------------*/ 273 | 274 | 275 | static int __init frelink_init(void) 276 | { 277 | struct proc_dir_entry *entry; 278 | int ret = 0; 279 | 280 | IPRINTK ("frelink module loaded\n"); 281 | 282 | entry = create_proc_entry(MODULE_NAME, 0400, NULL); 283 | if (!entry) { 284 | printk (KERN_ALERT "Error creating /proc/"MODULE_NAME 285 | " file\n"); 286 | return -EBADF; 287 | } 288 | entry->proc_fops = &frelink_ops; 289 | frelink.proc_entry = entry; 290 | atomic_set(&frelink.busy,0); 291 | atomic_set(&frelink.loidx,-1); 292 | 293 | ret = register_jprobe(&loop_jprobe); 294 | 295 | if (ret < 0) { 296 | IPRINTK("register_jprobe failed, returned %d\n", ret); 297 | return -1; 298 | } 299 | 300 | IPRINTK("Planted jprobe at %p, handler addr %p\n", 301 | loop_jprobe.kp.addr, loop_jprobe.entry); 302 | 303 | INIT_DELAYED_WORK(&frelink.work, do_recover); 304 | 305 | return ret; 306 | } 307 | module_init(frelink_init); 308 | 309 | 310 | static void __exit frelink_exit(void) 311 | { 312 | IPRINTK ("unloading module...\n"); 313 | 314 | remove_proc_entry(MODULE_NAME, NULL); 315 | 316 | unregister_jprobe(&loop_jprobe); 317 | 318 | IPRINTK("jprobe at \"%p\" unregistered\n", loop_jprobe.kp.addr); 319 | } 320 | module_exit(frelink_exit); 321 | --------------------------------------------------------------------------------