Linux C ext2fs_write_inode_full failing to write - c

after successfully reading the file inode with this:
retval = ext2fs_read_inode_full(current_fs, inode, inode_buf, EXT2_INODE_SIZE(current_fs->super));
if (retval) {
fprintf(stderr, "Failed to read inode\n");
free(fs);
free(inode_buf);
return retval;
}
(At this point I have verified the inode contains the correct data of the file in question)
I immediately attempt to write it back with this :
retval = ext2fs_write_inode_full(current_fs, inode, inode_buf, EXT2_INODE_SIZE(current_fs->super));
if (retval) {
fprintf(stderr, "Failed to write inode %d\n", retval);
}
(Of course it's my intention to change some date values in the inode before writing back)
But ext2fs_write_inode_full returns an error value 2133571349.
The program executes with root privileged!

The issue turned out to be the way crtime was opening the FS with ext2fs_open.
I needed to pass the EXT2_FLAG_RW flag otherwise the open is by default read only.

Related

how to get heap start address of a elf binary

I have written a sample program in C that uses libelf to dump the different sections.
However i want to dump the heap & stack segment starting address which is only available when the process is "live" (running).
This is what i do for a binary i read from disk.
elf_fd = open(elf_fname, O_RDWR);
if(elf_fd < 0) {
fprintf(stderr, "Failed to open \"%s\" reason=%s\n", elf_fname, strerror(errno));
return -1;;
}
followed by
if(elf_version(EV_CURRENT) == EV_NONE) {
fprintf(stderr, "Failed to initialize libelf\n");
res = -1;
goto done;
}
elf->e = elf_begin(elf->fd, ELF_C_READ, NULL);
if(!elf->e) {
int err = elf_errno();
if (err != 0) {
fprintf(stderr, "Failed to open ELF file code=%d reason=%s\n", err,
elf_errmsg(err));
}
res = -1;
goto done;
}
This works fine when i read the binary image on disk
For run time what i tried is instead of doing this
That is using the elf_fd returned by open
elf_fd = open(elf_fname, O_RDWR);
if(elf_fd < 0) {
fprintf(stderr, "Failed to open \"%s\" reason=%s\n", elf_fname, strerror(errno));
return -1;;
}
I instead do this
That is i get a handle from the pid of the current process
elf_fd = pidfd_open(getpid(), 0);
if (elf_fd == -1) {
perror("pidfd_open");
fprintf(stderr, "failed to open self %d\n", elf_fd);
exit(EXIT_FAILURE);
}
It returns me a valid descriptor but when i use this descriptor with
elf->e = elf_begin(elf->fd, ELF_C_READ, NULL);
if(!elf->e) {
int err = elf_errno();
if (err != 0) {
fprintf(stderr, "Failed to open ELF file code=%d reason=%s\n", err,
elf_errmsg(err));
}
}
It says "Invalid descriptor".
Question is how can i get heap & stack base address of a live process from within it
Also yes i did also try at the very start in main call
sbrk(0) & that seems to print the heap start address but this may not always be reliable as there maybe no heap without a malloc call prior
for now it does seem to print it.
Question is how can i get heap & stack base address of a live process from within it
Note that neither heap, nor stack have anything to do with the ELF format, or libelf.
There is no such thing as "heap base address" -- most modern heap allocators will perform multiple mmap calls to obtain memory from the OS, then "dole" it out to various malloc requests.
i did also try at the very start in main call sbrk(0)
"Legacy" malloc used to obtain memory using sbrk(), but few modern ones do. If the malloc you are using does use sbrk, then calling sbrk(0) near the start of main is a usable approximation.
For the main thread stack, you would want to do the same. A good first approximation is taking &argc, and rounding it up to page boundary.
If you want to get better approximation, you could use the fact that on Linux (and possibly other ELF platforms) the kernel puts specific values on the stack before invoking the entry point. Iterating through the __environ values looking for the highest address will give a better approximation.

Linux Device Driver, kernel thread can't open file?

I'm writing a Linux Driver with Linux Kernel Modules when, user can write and when user calls close, driver has to flush content into a file in another directory with same name of the device file.
I have this problem: when a process calls close, the driver can open a file and flush all its content correctly; when the process is killed (for example, from the terminal with a kill), device driver fails to execute filp_open becuse fs->CURRENT is set to NULL. So, I was trying to start a kernel thread to do this work.
When I try opening a file in the same directory, for example filp_open("myfile"...), it works correctly. But if I have to open a file in another directory, so filp_open("dirA/myfile"), filp_open returns -2. But this not going to happen when I call filp_open from the main thread.
This is my code:
static int thread_fn(void *unused){
struct thread_data* td = (struct thread_data *) unused;
if(td == NULL)
printk(KERN_INFO "td is null\n");
struct file* filp=filp_open("/dirA/myfile",O_RDWR,0666);
if(filp == NULL || (IS_ERR(filp)))
printk(KERN_INFO "filp is null!\n");
else
printk(KERN_INFO "filp is not null!\n");
size_t filp_size = filp->f_inode->i_size;
printk(KERN_INFO "size on release: %ld\n",filp_size);
if(filp_size > td->size){
printk(KERN_INFO "truncating file\n");
truncate_setsize(filp->f_inode, td->size);
}
inode_lock(filp->f_inode);
//file_write(filp,/*file->f_pos*/0,td->data,td->size);
inode_unlock(filp->f_inode);
printk(KERN_INFO "Thread Stopping\n");
do_exit(0);
return 0;
}
This is my device_release function:
static int device_release(struct inode *inode, struct file *file)
{
if(my_data->buffer == NULL)
return -ENOMEM;
struct thread_data* td=alloc_mem(sizeof(struct thread_data));
td->filename=my_data->filename;
td->data=my_data->buffer;
td->size=my_data->size;
thread_st = kthread_run(thread_fn, (void *)td,"Thread!");
if (thread_st)
printk(KERN_INFO "Thread Created successfully\n");
else
printk(KERN_ERR "Thread creation failed\n");
return 0;
}
What's the problem? I can't understand, it could be an OS problem? I have also tried with set_fs/get_fs but it didn't work.
I have an update: if I put flag O_CREAT, filp_open doesn't return error, but file isn't created; so, kernel thread cannot operate on file?

Proper Way to write to shared memory Windows

I have 2 processes, 1 process needs to write to a shared memory segment, the other process needs to read from the shared memory segment. The writing process only writes to shared mem, the reading process only reads.
Writing process
HANDLE hMapFile;
LPCTSTR pBuf;
char* Message = "test";
int MessageSize = 5;
hMapFile = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_EXECUTE_READWRITE, 0, Size, SegmentName);
if (hMapFile == NULL)
{
printf("Could not create file mapping object (%d).\n", GetLastError());
return NULL;
}
pBuf = (LPTSTR)MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, Size);
if (pBuf == NULL)
{
printf("Could not map view of file (%d).\n", GetLastError());
//CloseHandle(hMapFile);
return NULL;
}
CopyMemory((PVOID)pBuf, Message, MessageSize);
Shortly after my reading process will read the message like so
HANDLE hMapFile;
LPCTSTR pBuf;
hMapFile = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, SegmentName);
if (hMapFile == NULL)
{
printf("Could not open file mapping object MINER PROC (%d).\n", GetLastError());
exit(0);
//return RetVal;
}
RetVal = (LPTSTR)MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, Size);
if (RetVal == NULL)
{
printf("Could not map view of file (%d).\n", GetLastError());
CloseHandle(hMapFile);
exit(0);
//return RetVal;
}
UnmapViewOfFile(hMapFile);
CloseHandle(hMapFile);
This works perfectly for a 1 time write then read. I need the writing process to be able to change the values.
I've tried this in the writing process
memset((PVOID)pBuf, '\0', strlen(OriginalMessage));
CopyMemory(pBuf, "newmessage",
strlen("newmessage") + 1);
But this gives me random crashes in the writing process, although the reading process seems to be able to read the changes fine.
I've also tried to close the segment like this from the writing process immediately after CopyMemory() call
UnmapViewOfFile(pBuf);
CloseHandle(hMapFile);
But then the memory disappears and the reading process cannot read the segment name. I also tried calling UnmapViewOfFile() & CloseHandle() then creating a new memory segment with the same name and I keep getting random crashes after that.
What is the proper way to change a shared memory value of a given shared memory segment name without crashes? Thanks.
EDIT - I'd like to add that I tested this code using the described methods (creating new shared memory segments) on 4 Win10 machine, 2 Win7 machines and I got no crashes. The crashes only occur on a Win8.1 machine. it's quite odd.
EDIT2 - also, all functions are successful in both the reading and writing processes when the crash occurs.

Bus Error when writing to mmaped data

When I first made this project last semester, the code worked fine. Now I get a bus error when the mmapped memory to share between processes is being written to and I'm not sure why it is not working anymore.
Account_Info *mapData()
{
int fd;
//open/create file with read and write permission and check return value
if ((fd = open("accounts", O_RDWR|O_CREAT, 0644)) == -1)
{
perror("Unable to open account list file.");
exit(0);
}
//map data to be shared with different processes
Account_Info *accounts = mmap((void*)0, (size_t) 100*(sizeof(Account_Info)), PROT_WRITE,
MAP_SHARED, fd, 0);
int count= 0;
//loop to initialize values of Account_Info struct
while (count != 20)
{
//bus error occurs here
accounts[count].CurrBalance= 0;
accounts[count].flag = 0;
int i = 0;
while (i != 100)
{
//place NULL terminator into each element of AccName
accounts[count].AccName[i]= '\0';
i++;
}
count++;
}
close(fd);
return accounts;
}
A documented cause for SIGBUS with mmap is
Attempted access to a portion of the buffer that does not correspond to the file (for example, beyond the end of the file, including the case where another process has truncated the file).
My guess is that the accounts file didn't exist, so open with O_CREAT created it. But it has zero size, so any attempt to read or write through the mapping will fault. You need to fill the file with enough zeroes (or something else) to cover the mapping, for example using ftruncate.
You will get SIGBUS if you attempt to write past the mapped region of the file.
Chances are pretty good that your backing store file accounts is truncated/too short. (e.g.) if the file has space for 10 struct entries and you write to the 11th, you'll get SIGBUS
Do an fstat to get st_size and compare this against the length parameter you're giving to mmap
You may want to consider using ftruncate to extend the file before doing mmap

Read/Write struct to fifo in C

I'm trying to pass structs between processes using named pipes. I got stuck at trying to open the pipe non-blocking mode. Here's my code for writing to the fifo:
void writeUpdate() {
// Create fifo for writing updates:
strcpy(fifo_write, routing_table->routerName);
// Check if fifo exists:
if(access(fifo_write, F_OK) == -1 )
fd_write = mkfifo(fifo_write, 0777);
else if(access(fifo_write, F_OK) == 0) {
printf("writeUpdate: FIFO %s already exists\n", fifo_write);
//fd_write = open(fifo_write, O_WRONLY|O_NONBLOCK);
}
fd_write = open(fifo_write, O_WRONLY|O_NONBLOCK);
if(fd_write < 0)
perror("Create fifo error");
else {
int num_bytes = write(fd_write, routing_table, sizeof(routing_table));
if(num_bytes == 0)
printf("Nothing was written to FIFO %s\n", fifo_write);
printf("Wrote %d bytes. Sizeof struct: %d\n", num_bytes,sizeof(routing_table)+1);
}
close(fd_write);
}
routing_table is a pointer to my struct, it's allocated, so there's no prob with the name of the fifo or smth like that.
If I open the fifo without the O_NONBLOCK option, it writes smth for the first time, but then it blocks because I'm having troubles reading the struct too. And after the first time, the initial fifo is created, but other fifo's appear, named '.', '..'.
With O_NONBLOCK option set, it creates the fifo but always throws an error: 'No such device or address'. Any idea why this happens? Thanks.
EDIT: Ok, so I'm clear now about opening the fifo, but I have another problem, in fact reading/writing the struct to the fifo was my issue to start with. My code to read the struct:
void readUpdate() {
struct rttable *updateData;
allocate();
strcpy(fifo_read, routing_table->table[0].router);
// Check if fifo exists:
if(access(fifo_read, F_OK) == -1 )
fd_read = mkfifo(fifo_read, 777);
else if(access(fifo_read, F_OK) == 0) {
printf("ReadUpdate: FIFO %s already exists\n Reading from %s\n", fifo_read, fifo_read);
}
fd_read = open(fifo_read, O_RDONLY|O_NONBLOCK);
int num_bytes = read(fd_read, updateData, sizeof(updateData));
close(fd_read);
if(num_bytes > 0) {
if(updateData == NULL)
printf("Read data is null: yes");
else
printf("Read from fifo: %s %d\n", updateData->routerName, num_bytes);
int result = unlink(fifo_read);
if(result < 0)
perror("Unlink fifo error\n");
else {
printf("Unlinking successful for fifo %s\n", fifo_read);
printf("Updating table..\n");
//update(updateData);
print_table_update(updateData);
}
} else
printf("Nothing was read from FIFO %s\n", fifo_read);
}
It opens the fifo and tries to read, but it seems like nothing is in the fifo, although in writeUpdate the first time it says it wrote 4 bytes (this seems wrong too). At reading, first time around it prints 'a' and then num_bytes is always <=0.
I've looked around and only found this example, with simple write/read, is there smth more needed when writing a struct?
My struct looks like this:
typedef struct distance_table {
char dest[20]; //destination network
char router[20]; // via router..
int distance;
} distance_table;
typedef struct rttable {
char routerName[10];
char networkName[20];
struct distance_table table[50];
int nrRouters;
} rttable;
struct rttable *routing_table;
"No such device or address" is the ENXIO error message. If you look at the open man page, you'll see that this error is reported in particular if:
O_NONBLOCK | O_WRONLY is set, the named file is a FIFO and no process
has the file open for reading. (...)
which is exactly your situation. So the behavior you are seeing is normal: you can't write (without blocking) to a pipe that has no readers. The kernel won't buffer your messages if nothing is connected to the pipe for reading.
So make sure you start the "consumer(s)" before your "producer", or remove the non-blocking option on the producer.
BTW: using access is, in most circumstances, opening yourself to time of check to time of use issues. Don't use it. Try the mkfifo - if it works, you're good. If it fails with EEXISTS, you're good too. If it fails otherwise, clean up and bail out.
For the second part of your question, it really depends completely on how exactly the data you are trying to send is structured. Serializing a random struct in C is not easy at all, especially if it contains variable data (like char *s for example).
If you struct contains only primitive types (and no pointers), and both sides are on the same machine (and compiled with the same compiler), then a raw write on one side and read on the other of the whole struct should work.
You can look at C - Serialization techniques for more complex data types for example.
Concerning your specific example: you're getting mixed up between pointers to your structs and plain structs.
On the write side you have:
int num_bytes = write(fd_write, routing_table, sizeof(routing_table));
This is incorrect since routing_table is a pointer. You need:
int num_bytes = write(fd_write, routing_table, sizeof(*routing_table));
// or sizeof(struct rttable)
Same thing on the read side. On the receiving size you're also not allocating updateData as far as I can tell. You need to do that too (with malloc, and remember to free it).
struct rttable *updateData = malloc(sizeof(struct rrtable));

Resources