Path lookup in linux kernel 4.15 - linux

I'm trying to write a simple kernel module, just for didactic purpose.
In particular, what is giving me headaches is the path lookup.
I'd like to have a sysfs entry and in the store operation I want to receive a buffer and I want to save it only if it's a valid path. So I tried to use the vfs_path_lookup Exported Symbols from here namei.c. But even inserting a valid path, it prints me the ENOENT error.
This is my code:
Into the header:
extern int vfs_path_lookup(struct dentry *, struct vfsmount *, const char *, unsigned int, struct path *path);
Into the module store function:
ssize_t path_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count){
int err = vfs_path_lookup(current->fs->root.dentry, current->fs->root.mnt, buf, LOOKUP_DIRECTORY, base_path);
printk(KERN_DEBUG "%s\n", __FUNCTION__);
printk(KERN_DEBUG "Received: %s err is %d\n", buf, err);
strncpy(base_addr, buf, MAX_FILENAME_SIZE);
printk(KERN_DEBUG "Base Addr: %s\n", base_addr);
return strlen(base_addr);
}
What I tried to write into the file is the string "/home/osboxes/Documents" that is an existing directory. I suspect that I didn't get the real usage of the function, maybe something with flags. Thank you in advance for your help.
Edit:
This is the example requested into the comments (Thanks again)
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/namei.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/kobject.h>
#include <linux/sysfs.h>
#define MAX_FILENAME_SIZE 256
struct path *base_path ;
struct kobject *conf_kobj;
char base_addr[MAX_FILENAME_SIZE] = "/home/osboxes/Documenti";
struct kobj_attribute *get_attribute(char *name, umode_t mode, ssize_t (*show)(struct kobject *, struct kobj_attribute *, char *), ssize_t (*store)(struct kobject *, struct kobj_attribute *,
const char *, size_t));
ssize_t path_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf){
printk(KERN_DEBUG "%s\n", __FUNCTION__);
return sprintf(buf, "%s", base_addr);
}
ssize_t path_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count){
int err = kern_path(buf, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, base_path);
printk(KERN_DEBUG "%s\n", __FUNCTION__);
printk(KERN_DEBUG "Received: %s err is %d\n", buf, err);
strncpy(base_addr, buf, MAX_FILENAME_SIZE);
printk(KERN_DEBUG "Base Addr: %s\n", base_addr);
return strlen(base_addr);
}
static int __init init_mymodule(void)
{
struct kobj_attribute *path_attr;
printk(KERN_DEBUG "Module inserted\n");
path_attr = get_attribute(base_addr, 0666, path_show, path_store);
base_path = kmalloc(sizeof(struct path), GFP_KERNEL);
conf_kobj = kobject_create_and_add("conf", kernel_kobj);
sysfs_create_file(conf_kobj, &path_attr->attr);
return 0;
}
struct kobj_attribute *get_attribute(char *name, umode_t mode, ssize_t (*show)(struct kobject *kobj, struct kobj_attribute *attr, char *buf), ssize_t (*store)(struct kobject *kobj, struct kobj_attribute *attr,
const char *buf, size_t count))
{
struct kobj_attribute *attribute = kmalloc(sizeof(struct kobj_attribute), GFP_KERNEL);
attribute->attr.name = name;
attribute->attr.mode = mode;
if(show)
attribute->show = show;
if(store)
attribute->store = store;
return attribute;
}
static void __exit exit_mymodule(void)
{
kobject_put(conf_kobj);
kfree(base_addr);
printk(KERN_INFO "Module removed\n");
}
module_init(init_mymodule);
module_exit(exit_mymodule);
MODULE_LICENSE("GPL");
I also replaced vfs_path_lookup with kern_path as #Tsyvarev suggested.
The error comes when I write into the terminal
echo "/home/osboxes/Documenti" > /sys/kernel/conf/path
When I use the address into the "buf" variable, in this case it's hardcoded, but doesn't work.

I solved the problem, I was storing an invalid path because the "store" method adds a newline character at the end of the string. Thank you all

Related

How to find out available space in linux kernel space?

My need is to add a available space check in vfs_writeev. if available space on VFS is under 1GB, then forbidden the writting operation.
So my question is, how to find out available space in linux kernel struct ?
Any reply will be appreciated, thank you !
ssize_t vfs_writev(struct file *file, const struct iovec __user *vec,
unsigned long vlen, loff_t *pos, int flags)
{
struct iovec iovstack[UIO_FASTIOV];
struct iovec *iov = iovstack;
struct iov_iter iter;
ssize_t ret;
ret = import_iovec(WRITE, vec, vlen, ARRAY_SIZE(iovstack), &iov, &iter);
if (ret >= 0) {
file_start_write(file);
ret = do_iter_write(file, &iter, pos, flags);
file_end_write(file);
kfree(iov);
}
return ret;
}

change value of struct inside a struct

The secondexample below is what I would like to achieve in the firstcode written up. I want to ask how many 'dati' wants to insert then I have to do a malloc and I want to ask and save each dato of every struct so, firstly cdfM of the struct maestra and so on... How can I do that? How should I create the array with the malloc and how what should I do to save all the infos?
thx
#include <stdio.h>
#include <stdlib.h>
#define MAX 30
struct dati{
struct maestra{
char cdfM[MAX];
char nomeM[MAX];
char cognomeM[MAX];
char specializzazioneM[MAX];
}
struct classe{
int id;
char nome[MAX];
char colore[MAX];
}
struct insegnamento{
int idclasseI;
char cfmaestra[MAX];
}
struct bambino{
char cdfB[MAX];
char nomeB[MAX];
char cognomeB[MAX];
char dataNascita[MAX];
int idclasseB;
char dataIscrizione[MAX];
}
}
int main () {
struct dati *p,*q,*r,*s;
struct maestra **p;
struct classe **q;
struct insegnamento **r;
struct bambino **s;
return 0;
}
#include <stdio.h>
#include <stdlib.h>
#define MAX 30
struct studente{
char nomeCognome[MAX];
int numeroMatricola;
float percentualeEP;
};
int main() {
struct studente *p;
int m, i;
printf("Quanti studenti devi inserire: \n");
scanf("%d", &m);
p = malloc(m*sizeof(int));
for (i=0; i < m; i++){
printf("Inserisci i dati dell'utente numero %d\n",i+1);
printf("Inserisci il nome cognome: \n");
scanf(" ");
fgets((p+i)->nomeCognome, MAX, stdin);
printf("Inserisci la matricola: \n");
scanf("%d",&(p+i)->numeroMatricola);
printf("Inserisci la percentuale di esami passati: \n");
scanf("%f", &(p+i)->percentualeEP);
}

How to create thunk in x64?

I've found nice example how to create thunk for closure, but it's 32-bit version:
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
struct env {
int x;
};
struct __attribute__((packed)) thunk {
unsigned char push;
struct env * env_addr;
unsigned char call;
signed long call_offset;
unsigned char add_esp[3];
unsigned char ret;
};
struct thunk default_thunk = {0x68, 0, 0xe8, 0, {0x83, 0xc4, 0x04}, 0xc3};
typedef void (* cfunc)();
struct thunk * make_thunk(struct env * env, void * code)
{
struct thunk * thunk = (struct thunk *)mmap(0,sizeof(struct thunk), PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
*thunk = default_thunk;
thunk->env_addr = env;
thunk->call_offset = code - (void *)&thunk->add_esp[0]; // Pretty!
mprotect(thunk,sizeof(struct thunk), PROT_EXEC);
return thunk;
}
void block(struct env * env) {
env->x += 1;
printf ("block: x is %d\n", env->x);
}
cfunc foo (int x)
{
struct env * env = (struct env *)malloc(sizeof(struct env));
env->x = x;
printf ("x is %d\n",env->x);
return (cfunc)make_thunk(env,(void *)&block);
}
int main() {
cfunc c = foo(5);
c();
c();
}
How can I rewrite it for 64-bit version?
I'm using Linux x86_64. I've been able to cross-compile it with gcc -m32, which worked perfectly.
The code below is designed to be used with GCC on Linux and should support 32 and 64 bit compilation.
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
struct env {
int x;
};
#if __x86_64__
struct __attribute__((packed)) thunk {
unsigned char mov[2];
struct env * env_addr;
unsigned char movrax[2];
void (*call_address)();
unsigned char jmp[2];
};
struct thunk default_thunk = {{0x48, 0xbf}, 0x0, {0x48, 0xb8}, 0x0, {0xff, 0xe0} };
#elif __i386__
struct __attribute__((packed)) thunk {
unsigned char push;
struct env * env_addr;
unsigned char call;
signed long call_offset;
unsigned char add_esp[3];
unsigned char ret;
};
struct thunk default_thunk = {0x68, 0, 0xe8, 0, {0x83, 0xc4, 0x04}, 0xc3};
#else
#error Architecture unsupported
#endif
typedef void (* cfunc)();
struct thunk * make_thunk(struct env * env, void * code)
{
struct thunk * thunk = (struct thunk *)mmap(0,sizeof(struct thunk),
PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
*thunk = default_thunk;
#if __x86_64__
thunk->env_addr = env;
thunk->call_address = code; /* Pretty! */
#else
thunk->env_addr = env;
thunk->call_offset = code - (void *)&thunk->add_esp[0]; /* Pretty! */
#endif
mprotect(thunk,sizeof(struct thunk), PROT_EXEC);
return thunk;
}
void block(struct env * env) {
env->x += 1;
printf ("block: x is %d\n", env->x);
}
cfunc foo (int x)
{
struct env * env = (struct env *)malloc(sizeof(struct env));
env->x = x;
printf ("x is %d\n",env->x);
return (cfunc)make_thunk(env,(void *)&block);
}
int main() {
cfunc c = foo(5);
c();
c();
return 0;
}
Assuming that the OS is using System V 64bit ABI (Which Linux uses) calling convention then the first parameter that will be passed to the function will be in register %rdi. Then we just have to mov the environment address (env_addr) to %rdi and then do a call. The call uses an indirect jump to an absolute location through %rax. So the instruction sequence looks like (at&t syntax):
mov $env_addr, %rdi
movabs $call_pointer, %rax
jmpq *%rax # Tail call instead of call/retq

Global initialization of structure

How can I initialize struct sockaddr_in globally. Third variable IN_ADDR sin_addr is a nested structure.
struct sockaddr_in{
short sin_family;
unsigned short sin_port;
IN_ADDR sin_addr;
char sin_zero[8];
} addr;
struct in_addr {
union {
struct { u_char s_b1,s_b2,s_b3,s_b4; } S_un_b;
struct { u_short s_w1,s_w2; } S_un_w;
u_long S_addr;
} S_un;
};
Point 1
You cannot initalize struct sockaddr_in itself. You need to have a variable of this type, i.e, addr (as in your case) which you can initialize.
Point 2
You can use an inilitalizer list in brace-enclosed form to initialize the global variable. Also, you can make use of the .identifier option to initalize a member explicitly.
Assuming IN_ADDR is defined as
typedef struct sockaddr_in IN_ADDR;
you can write something like
struct sockaddr_in{
short sin_family;
unsigned short sin_port;
IN_ADDR sin_addr;
char sin_zero[8];
} addr = {AF_INET, 2015, .sin_addr.S_un.S_addr = 1234567890, .sin_zero = {1,2,3,4,5,6,7}};
to initilialize addr.
Here, the .sin_addr.S_un.S_addr is used to denote to the particular variable to be initializeed.
Using your structure/union definition ... here is a sample program ...
#include <stdio.h>
typedef unsigned char u_char;
typedef unsigned short u_short;
typedef unsigned long u_long;
typedef struct in_addr
{
union
{
struct { u_char s_b1,s_b2,s_b3,s_b4; } S_un_b;
struct { u_short s_w1,s_w2; } S_un_w;
u_long S_addr;
}S_un;
}IN_ADDR;
IN_ADDR u = // ====> Either do something like this ...
{
.S_un.S_un_b = {'a', 'b', 'c', 'd'}
//.S_un.S_un_w = {1, 2},
//.S_un.S_addr = 121212
};
struct sockaddr_in
{
short sin_family;
unsigned short sin_port;
IN_ADDR sin_addr;
char sin_zero[8];
}addr = { // ==============> Or do something like this.
10,
20,
.sin_addr.S_un.S_addr=12345,
"SOCKET"
};
int main()
{
printf("sin_family = %d\n", addr.sin_family);
printf("sin_port = %d\n", addr.sin_port);
printf("sin_addr.S_un.S_addr = %ld\n", addr.sin_addr.S_un.S_addr);
printf("sin_zero = %s\n", addr.sin_zero);
printf("%c %c %c %c\n", u.S_un.S_un_b.s_b1, u.S_un.S_un_b.s_b2, u.S_un.S_un_b.s_b3, u.S_un.S_un_b.s_b4);
//printf("%d %d\n", u.S_un.S_un_w.s_w1, u.S_un.S_un_w.s_w2);
//printf("%ld\n", u.S_un.S_addr);
return 0;
}

Changing a struct value within a function with c++

I wanted to create a quick quadtree in c++ but found that when I change a value of a struct within a function the value reverts. Also within the recursive function I wont some of the data created to be global. How do I do this?
#include <iostream>
struct a{
unsigned int value;
struct a * leaf[4];
};
void build(struct a root, unsigned int depth){
depth++;
root.value = 0;
struct a leaf0;
root.leaf[0] = &leaf0;
struct a leaf1;
root.leaf[1] = &leaf1;
struct a leaf2;
root.leaf[2] = &leaf2;
struct a leaf3;
root.leaf[3] = &leaf3;
if (depth != 5) {
build(*root.leaf[0], depth);
build(*root.leaf[1], depth);
build(*root.leaf[2], depth);
build(*root.leaf[3], depth);
}
}
int main(int argc, const char * argv[])
{
struct a root;
root.value = 364;
build(root, 0);
std::cout << root.value;
return 0;
}
You must pass the address of the struct to your function (which should accept a pointer to the struct):
void build(struct a *root, unsigned int depth) {
...
}
int main(int argc, const char * argv[])
...
build(&root, 0);
}

Resources