I want to implement a tree traversal function which prints all the contents of a given directory
in kernel . I know how to do this in user space, but my requirement is to have that in kernel space.
For that, I am looking into vfs_readdir function and a bit confused regarding its usage.
Say I will be calling my traversal function from other kernel module which means that request won’t be coming through user space. Now the question is how to call vfs_readdir and use that information to recursively parse given directory. From definition of vfs_readdir
extern int vfs_readdir(struct file *, filldir_t, void *);
I can get struct file * from the file path using functions like filp_open() and as per my understanding filldir_t is a function pointer to call back function which fills the user provided buffer pointed by void *. But in my case, I don’t need to pass any information to user back. What should i pass in void * place?
Looking into filldir function definition
static int filldir(void * __buf, const char * name, int namlen, loff_t offset, ino_t ino, unsigned int d_type);
From where are the parameters to this function are coming. My assumption is that vfs_readdir in turn calls something like file->f_op_readdir(file,but,filler);
Does this internally does something and fills in parameters to call callback function ?
Now this is one level. What should I do to recursively print all files in a given directory. I guess I need to do something in my own callback function.But I have only some information regarding the file that are passed through this call back functions like name of file,inode number etc . How would I know if it is regular file or directory using this information . I mean I don't have dentry or inode data structures regarding the file. Any suggestions how to do that?
In addition, If I want to delete a file in callback function, can I do that by using inode numbers(thats what i have in callback apart from names) how should I do that?
vfs_readdir is called, e.g., from readdir syscall implementation. It uses fillonedir as callback, which in turn can easy be understanded: its first argument is just last argument of vfs_readdir, all other arguments(name, namelen, offset, ino, d_type) are copied into user space as is.
Note, that callback for vfs_readdir is executed with inode mutex locked(inode->i_mutex). Probably you shoudn't open subdirectory incide that callback. Instead, save name of the subdirectory somewhere, and open it after vfs_readdir call. This is very similar to the way how you traverse directories tree in the user space.
Note, that since version 3.11 kernel uses another way for iterate entries in directory: iterate_dir. Its second argument combines both callback and callback-specific argument for it, and also include current position in the file. Aside from the first parameter, callback accepts same parameters as before.
As for ino argument, you cannot use it for open given inode(kernel just has no mechanism for open files by inode number). Use filp_open or similar.
Related
I am trying to declare a new variable in vvar.h and define it near my new VDSO function. So that I could use this variable in my vdso function.
I have a question about VVar. According to the description in arch/x86/include/asm/vvar.h, when I declare here a new variable as DECLARE_VVAR(0, int, count), I should use DEFINE_VVAR(type, name) to define this variable somewhere else.
The problem is after I defined this variable somewhere else, like DEFINE_VVAR(int, count), when I am trying to assign an integer value to this variable count, it is failed. This is because after kernel version 5.2 #define DEFINE_VVAR(type, name) has been changed from #define DEFINE_VVAR(type, name) type name to #define DEFINE_VVAR(type, name) type name[CS_BASES]. Right now this variable count is an integer array instead of type integer. Therefore I can't assign a integer value to it. Do you know how to fix it?
VVAR.h: https://elixir.bootlin.com/linux/v5.12/source/arch/x86/include/asm/vvar.h#L43
Typically, you cannot add a variable simply through DECLARE_VVAR macro.
The first thing you have to be aware of is that .vvar is a page of memory located inside the memory (more specifically, before .vdso) and could access by both kernel and userland. You can see this inside the linker script https://elixir.bootlin.com/linux/latest/source/arch/x86/entry/vdso/vdso-layout.lds.S. For now, kernel already has a data structure `struct video to format the data inside this page.
Second, assume you want to add a variable inside the .vvar page and access it in your new vdso function, the easiest way is to add it inside the sturct vdso structure of include/vdso/datapage.h: https://elixir.bootlin.com/linux/latest/source/include/vdso/datapage.h. After that, you can update them inside the kernel (for example, in schedule) in the same way as other vvar variables.
Second, if you want to own your own vvar page, you have to define your own vvar data structure inside the datapage.h and do not forget DEFINE_VVAR in vsyscall.h: https://elixir.bootlin.com/linux/latest/source/arch/x86/include/asm/vdso/vsyscall.h ALso, since the vvar memory layout is compact, you also need to allocate another page through linker script: https://elixir.bootlin.com/linux/latest/source/arch/x86/entry/vdso/vdso-layout.lds.S by change vvar_start = . - 4 * PAGE_SIZE; into vvar_start = . - 5 * PAGE_SIZE;
I want to use duk_pcall to do it, here is what I tried:
Firstly, I load the script file into a char* string.
Secondly, use duk_pcompile_string(ctx, 0, programBody) to compile it.
Then, I can use duk_pcall(ctx, 0) to call it once, and call duk_pop(ctx).
But when I try to use duk_pcall a second time, I failed.
Can you give me some advice about how to do it the right way?
duk_pcompile_string places the result on the TOS and duk_pcall executes the TOS (+ eventual parameters) and replaces all of that by the return value of the call.
In order to make the function callable multiple times you have to duplicate the TOS insert the required parameter and then call that using the pcall. After the call remove the result from the stack. The original function should now be at the TOS again. Start over with what is written in this paragraph, to call it again.
System::Call '${sysGetDiskFreeSpaceEx}(r0,.,,.r1)'
If i'm right, r0: directoryname, free bytes, number of bytes, number oof free bytes, but what actually means the r0,.,.,r1?
Thx for the help!
${...} is a define so when you want to know how some things work then the first thing you should do is to find out what the define does: !error "${sysGetDiskFreeSpaceEx}" will print !error: kernel32::GetDiskFreeSpaceEx(t, *l, *l, *l) i
In the System readme you will find this nugget:
PARAMS, RETURN and OPTIONS can be repeated many times in one Get/Call
line. When repeating, a lot can be omitted, and only what you wish to
change can be used. Type, source and/or destination can be omitted for
each parameter, even the return value. Options can be added or
removed. This allows you to define function prototypes and save on
some typing.
So ${sysGetDiskFreeSpaceEx} is a prototype that specifies the parameter count and types but it does not specify parameter source and destination.
What is the parameter syntax?
The parameters list is separated by commas. Each parameter is combined
of three values: type, source and destination. Type can be an integer,
a string, etc. Source, which is the source of the parameter value, can
be a NSIS register ($0, $1, $INSTDIR), the NSIS stack, a concrete
value (5, "test", etc.) or nothing (null). Destination, which is the
destination of the parameter value after the call returns, can be a
NSIS register, the NSIS stack or nothing which means no output is
required. Either one of source or destination can also be a dot (`.')
if it is not needed.
We can now expand the entire call !error 'System::Call "${sysGetDiskFreeSpaceEx}(r0,.,,.r1)"' and this gives us !error: System::Call 'kernel32::GetDiskFreeSpaceEx(t, *l, *l, *l) i(r0,.,,.r1)'
If we merge the repeated parameter definitions we get kernel32::GetDiskFreeSpaceEx(tr0, *l., *l, *l.r1)i.
So parameter 1 is a string (LPTSTR on MSDN) with the source r0 (NSIS register $0).
Parameter 2 and 3 have no source and no destination, only parameter 2 uses a . (dot) but the end result is the same; no input and no output. The only important part here is *l so the system plugin knows how large the parameter is.
The final parameter is a pointer (*) to a 64 bit number (l) with no input (.) and we request the output to be stored in $1 (r1).
The system plugin calls the native Windows API so it is often useful to look at MSDN to see what it has to say about the function you are calling.
Given that $0..$9 and $R0..$R9 are NSIS registers, the notation r0 -> r9 (resp. R10..R19 or also R0..R9) is used by the System plugin to specify the $0..$9 (resp. $R0..$R9) registers as a source and / or a destination with system API or other dll function calls.
Either one of source or destination can also be a dot (.) if it is not needed.
Look for the "Calling functions" and "Available sources and destinations" sections in the system plugin documentation.
I am trying to get the boundary for a kernel function (system calls for example). Now, if I understand correctly, I can get the start address of the interested function by reading /proc/kallsyms or System.map but I dont know how to get the end address of this function.
As you may know, /proc/kallsyms allow us to view the symbol table for Linux kernel so we can see the start address of all exported symbols. Can we use the start address of the next function to calculate the end address of the previous function? If we cannot do like this, could you suggest me another ways?
Generally, executables store only the start address of a function, as it is all that is required to call the function. You will have to infer the end address, rather than simply looking it up.
You could try to find the start address of the subsequent function, but that wouldn't always work either. Imagine the following:
void func_a() {
// do something
}
static void helper_function() {
// do something else
}
void func_b() {
// ...
helper_function();
// ...
}
You could get the address of func_a and func_b, but helper_function would not show up, because nothing needs to link to it. If you tried to use func_b as the end of func_a (assuming that the order in the compiled code in equivalent to the order in the source code, which is not guaranteed), you would end up accidentally including code that you didn't need to include - and might not find code that you need to find when inlining other functions into func_b.
So, how do we find this information? Well, if you think about it - the information does exist - all of the paths within func_a will eventually terminate (in a loop, return statement, tail call, etc), probably before helper_function begins.
You would need to parse out the code of func_a and build up a map of all of the possible code paths within it. Of course, you would need to do this anyway to inline other functions into it - so it shouldn't be too much harder to simply not care about the end address of the function.
One final note: in this example, you would have trouble finding helper_function in order to know to inline it, because the symbol wouldn't show up in kallsyms. The solution here is that you can track the call instructions in individual functions to determine what hidden functions exist that you didn't know about otherwise.
TL;DR: You can only find the end address by parsing the compiled code. You have to parse this anyway, so just do it once.
I'd like to know if it's possible (no matter why) to pass more than just a filename for existing file to stat() function, so it would not fail and return 0?
I mean like this:
struct stat mystat; char file[100];
...
if(stat(file, &mystat)==0){
//success
}
Is it possible to specify file as "existing-file_some_special_chars_maybe_some-text" and to stat() not fail on that?
stat() works on filenames, so if you're passing in something that isn't a filename, you shouldn't be surprised that it fails. You can use fstat() to get information on whatever file a file handle is pointing to, but otherwise you're stuck with just filenames.
How about just creating a list of file names and feeding them to stat() one by one?