In this article, I am going to describe the process of development of a driver module to hide files in Linux OS (you can read the article about Hide Driver for Windows OS here). Besides, I will touch upon such questions:
- Virtual File System (VFS)
- The work with inode and dentry structures
The article concerns the Linux kernel version 2.6.32 because other kernel versions can have the modified API, different from the one used in examples or in the build system. Article is meant for people that already have some experience Linux driver development.
Creation of a simple Linux driver was described in this Linux driver tutorial.
The Virtual File System is the software layer in the kernel that provides the file system interface to user space programs. It also provides an abstraction within the kernel that allows different file system implementations to coexist. 
The VFS implements
chmod and similar system calls. The
pathname parameter, which is passed to them, is used by the VFS to search through the directory entry cache (also known as the dentry cache or dcache). This provides a very fast look-up mechanism to translate a
filename) into a specific dentry. Dentries live in RAM and are never saved to disk: they exist only for performance purposes. 
An individual dentry usually has a pointer to an inode. Inodes are file system objects such as regular files, directories. They live either on the disk (for block device file systems) or in the memory (for pseudo file systems). Inodes, which live on the disk, are copied into the memory when required, and inode changes are written back to disk. Several dentries can point to a single inode (hard links, for example, do this). Each file is represented by its own inode structure. 
Each inode structure has its inode number that is unique for the currently mounted file system.
Our driver will hook inode and file operations for the specified file and its parent directory. For this purpose, we will change pointers on inode and file operation structures to our own functions. That will allow us to hide file even from the system.
filldir() function for the file parent directory
To hide a file from the system we will hook
filldir() function for parent directory. This function is called from
readdir() function that displays files in the directory. The second and third parameters of this function are name and name length of the file that is displayed. To change the call of
filldir() we will create our own
readdir() function and call our
filldir() function from it.
g_parent_dentry is a pointer to a parent directory dentry that we will use to search specified file inode in our
As we need to override only
readdir() function from
file_operations structure for the parent directory, we will create static
This code creates
file_operations structure, where we will override
readdir() function by the custom
Such syntax tells us that any member of the structure that you don't explicitly assign will be initialized to
gcc. [3, chapter 4.1.1.]
We can use
d_lookup() function, which returns dentry of the file in the current directory.
d_lookup() receives dentry of the parent directory with file and
qstr structure, which contains name and name length of the file. We will save dentry of the parent directory in the
readdir() function. We can create
qstr structure and fill it in
So, having dentry of the currently displayed file, we can compare its inode number with inode number of the file(s), which we want to hide (we will get them later). If file is hidden,
filldir() function returns 0. If file isn’t hidden, we call original
g_inode_numbers – array of inode numbers for files, which we will hide.
New file operations structure
filldir() function allows us only to hide file from the directory. But system calls, for example
write operations, for the file they will work. To prevent it, we will change inode and file operations for the file. Our driver will return -2 value for all operations, that means: “Specified file doesn’t exist.”
Function to hook inode operations, file operations and get inode number of the specified file
We will use
path_lookup() function to hook inode and file operations. This function fills
nameidata structure for the file specified as the first parameter.
After we’ve got filled
nameidata structure, we allocate memory for arrays with old inode pointers and inode number.
g_old_inode_pointer is array of pointers to original inode structure for hidden files.
g_old_fop_pointer is array of pointers to original
file_operations structure for hidden files.
g_old_iop_pointer is array of pointers to original
inode_opearations structure for hidden files.
g_old_parent_inode_pointer is array of pointers to original inode structure for parent directory that contains hidden files.
g_old_parent_fop_pointer is array of pointers to original
file_operations structure for parent directory that contains hidden files.
Nameidata structure contains dentry of the specified path. Using received dentry, we can operate with pointers to file and inode operations. After we get dentry and inode structures for specified path, we save old pointers and change them to the pointers to our structures.
Function to backup inode pointers of the specified file
After the file has been hidden, it would be great to have a possibility to restore it visibility in the system. We can organize it by calling a function that restores pointers to the original inode and file operations after driver is deleted from the system.
Device file for driver
As yet, we have driver that hides file by specified path, but how to pass file path to the driver from user mode? We will use device file for that. You can learn detailed information about device file and registering it for character device from above-mentioned article “A Simple Driver for Linux OS”. For our device file, we will implement
device_file_write() function that is called when data is written to the file from user space.
In this function, we will apply
strncpy_from_user()to copy file path string from user space to kernel space and remove “\n” from the end of the string, if it exists as some console commands can append it.
After this operations, we will have file path string in kernel space that we can pass to our
hook_functions(), which hides file by specified path.
Hide driver build system
To build hide driver, we can use simple example of
makefile from “A Simple Driver for Linux OS” article.
This make file will create target HideFiles.ko using main.odevice_file.o module.o.
Make load command will mount our driver into the system.
Make unload will remove HideFiles.ko module from the system.
Driver to hide files in action
First of all, let’s create an empty test file in our test directory (for example, file name: testfile, file path: ~/test/testfile).
Now let’s open console and execute
makecommand in the directory with driver sources.
After driver has been built, we execute
make load command that inserts our module into the system as device.
$>sudo make load
make load command is successfully executed, we can see our driver in /proc/devices file.
$>cat /proc/devices Character devices: 1 mem 4 /dev/vc/0 4 tty 4 ttyS … 250 Hide_Driver
Now we will create device file for our device with major number 250 and minor number 0
$>sudo mknod /dev/HideDriver c 250 0
Finally let’s hide our test file from the system:
$>sudo sh -c "echo ~/test/testfile>/dev/HideDriver"
Checking our file:
$>ls ~/test total 0 $>cat ~/test/testfile cat: ~/test/testfile: No such file or directory
Now let’s remove our driver from the system and check our test file:
$>sudo make unload $>ls ~/test Testfile
As you can see, we successfully hid our Testfile from the system and then restored it.
- “Overview of the Linux Virtual File System” by Richard Gooch.
- “A simple linux driver for Linux OS”.
- Peter Jay Salzman Ori Pomerantz The Linux Kernel Module Programming Guide
Download sample sources Hide-Driver-for-Linux (ZIP, 4.6 KB)