Bus error opening and mmap'ing a file - linux

I want to create a file and map it into memory. I think that my code will work but when I run it I'm getting a "bus error". I searched google but I'm not sure how to fix the problem. Here is my code:
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/mman.h>
#include <string.h>
int main(void)
{
int file_fd,page_size;
char buffer[10]="perfect";
char *map;
file_fd=open("/tmp/test.txt",O_RDWR | O_CREAT | O_TRUNC ,(mode_t)0600);
if(file_fd == -1)
{
perror("open");
return 2;
}
page_size = getpagesize();
map = mmap(0,page_size,PROT_READ | PROT_WRITE,MAP_SHARED,file_fd,page_size);
if(map == MAP_FAILED)
{
perror("mmap");
return 3;
}
strcpy(map, buffer);
munmap(map, page_size);
close(file_fd);
return 0;
}

You are creating a new zero sized file, you can't extend the file size with mmap. You'll get a bus error when you try to write outside the content of the file.
Use e.g. fallocate() on the file descriptor to allocate room in the file.
Note that you're also passing the page_size as the offset to mmap, which doesn't seem to make much sense in your example, you'll have to first extend the file to pagesize + strlen(buffer) + 1 if you want to write buf at that location. More likely you want to start at the beginning of the file, so pass 0 as the last argument to mmap.

Related

What is the cause of the hard limit on the directory nesting depth returned by getcwd on macOS and how can it be circumvented?

On linux and macOS, directories can be nested to seemingly arbitrary depth, as demonstrated by the following C program. However, on macOS but not on linux, there seems to be a hard limit on the nesting level returned by getcwd, specifically a nesting level of 256. When that limit is reached, getcwd returns ENOENT, a rather strange error code. Where does this limit come from? Is there a way around it?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/types.h>
void fail(char *msg) { perror(msg); exit(1); }
void create_nested_dirs(int n) {
int i;
char name[10];
char cwd[10000];
if (chdir("/tmp") < 0) fail("chdir(\"/tmp\")");
for (i=2; i<=n; i++) {
sprintf(name, "%09d", i);
printf("%s\n",name);
if (mkdir(name, 0777) < 0 && errno != EEXIST) fail("mkdir");
if (chdir(name) < 0) fail("chdir(name)");
if (getcwd(cwd, sizeof(cwd)) == NULL) fail("getcwd");
printf("cwd = \"%s\" strlen(cwd)=%d\n", cwd, strlen(cwd));
}
}
int main() {
long ret = pathconf("/", _PC_PATH_MAX);
printf("PATH_MAX is %ld\n", ret);
create_nested_dirs(300);
return 0;
}
Update
The above program was updated to print the value returned by pathconf("/", _PC_PATH_MAX) and to print the length of the path returned by getcwd.
On my machine running macOS Mojave 10.14, the PATH_MAX is 1024 and the longest string correctly returned by getcwd is 2542 characters long. Then a 2552 character long directory of nesting depth 256 is created by mkdir and then after a successful chdir to that directory a getcwd fails with ENOENT.
If the sprintf(name, "%09d", i); is changed to sprintf(name, "%03d", i); the paths are considerably shorter but the getcwd still fails when the directory nesting depth reaches 256.
So the limiting factor here is the nesting depth, not PATH_MAX.
My understanding of the source code here is that the meat of the work is done by the call fcntl(fd, F_GETPATH, b) so the problem may be in fcntl.

bus error with mmap

I get bus error (core dumped) when trying to write to memory. I want to write to a binary file using mmap() and open() functions in Linux. I want to write integers from 1 to 100 in the binary file by mapping it to memory instead of writing to the file directly.
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/mman.h>
#include <string.h>
#include <stdlib.h>
#define FILE_SIZE 0x100
int main(int argc,char *argv[])
{
int fd;
void *pmap;
printf("im here");
//fd=open(argv[1],O_RDWR|O_CREAT,S_IRUSR|S_IWUSR);
fd=open("numbers.raw",O_RDWR);
if(fd == -1)
{
perror("open");
exit(1);
}
lseek(fd,FILE_SIZE+1,SEEK_SET); //checking the file length
lseek(fd,0,SEEK_SET);//points to start of the file
//create the memory mapping
pmap = mmap(0,FILE_SIZE,PROT_WRITE,MAP_SHARED,fd,0);
if(pmap == MAP_FAILED)
{
perror("mmap") ;
close(fd);
exit(1);
}
close(fd);
for(int i=1;i<=100;i++)
sprintf(pmap,"%d",i);
return 0;
}
Your comment says you are "checking the file length" but you never check the return value of that call. I'd bet it is failing since your file is not large enough, hence the bus error later.
There are multiple other unrelated mistakes in your file as well, by
the way:
Your file size assumes 0x100 bytes are enough to store 100 integers in binary. This is not the case for 64 bit systems.
You aren't actually storing binary numbers - you are storing strings of the numbers.
You aren't advancing where you write, so you write all the numbers at the start of the file, one on top of the other.

Monitoring file changes using select() within a loop

I am trying to write a program that will constantly keep track of the changes in a file and do several actions accordingly. I am using inotify and select within a loop to track file modifications in a non-blocking manner. The basic structure of the file tracking portion of my program is as follows.
#include <cstdio>
#include <signal.h>
#include <limits.h>
#include <sys/inotify.h>
#include <fcntl.h>
#include <iostream>
#include <fstream>
#include <string>
int main( int argc, char **argv )
{
const char *filename = "input.txt";
int inotfd = inotify_init();
char buffer[1];
int watch_desc = inotify_add_watch(inotfd, filename, IN_MODIFY);
size_t bufsiz = sizeof(struct inotify_event) + 1;
struct inotify_event* event = ( struct inotify_event * ) &buffer[0];
fd_set rfds;
FD_ZERO (&rfds);
struct timeval timeout;
while(1)
{
/*select() intitialisation.*/
FD_SET(inotfd,&rfds); //keyboard to be listened
timeout.tv_sec = 10;
timeout.tv_usec = 0;
int res=select(FD_SETSIZE,&rfds,NULL,NULL,&timeout);
FD_ZERO(&rfds);
printf("File Changed\n");
}
}
I checked the select manual page and reset the fd_set descriptor each time select() returns. However, whenever I modify the file (input.txt), this code just loops infinitely. I not very experienced using inotify and select, so, I am sure if the problem is with the way I use inotify or select. I would appreciate any hints and recommentations.
you have to read the contents of the buffer after the select returns. if the select() finds data in the buffer, it returns. so, perform read() on that file descriptor (inotfd). read call reads the data and returns amount of bytes it read. now, the buffer is empty and in the next iteration, the select() call waits until any data is available in the buffer.
while(1)
{
// ...
char pBuf[1024];
res=select(FD_SETSIZE,&rfds,NULL,NULL,&timeout);
read(inotfd,&pBuf, BUF_SIZE);
// ...
}

Why "ls" is not colored after forkpty()

Why output of ls executed here is not colored?
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <pty.h>
#include <sys/wait.h>
int main(int argc, char **argv ) {
termios termp; winsize winp;
int amaster; char name[128];
if (forkpty(&amaster, name, &termp, &winp) == 0) {
system("ls"); // "ls --color" will work here!
return 0;
}
wait(0);
char buf[128]; int size;
while (1) {
size = read(amaster, buf, 127);
if (size <= 0) break;
buf[size] = 0;
printf("%s", buf);
}
return 0;
}
According to man (and ls.c that I am inspecting) it should be colored if isatty() returns true. After forkpty() it must be true. Besides, ls DOES output in columnized mode in this example! Which means it feels it has tty as output.
Of course I do not want only ls to output color, but an arbitrary program to feel that it has real color enabled tty behind.
I just wrote a simple test:
#include <unistd.h>
int main() {
printf("%i%i%i%i%i\n", isatty(0), isatty(1), isatty(2), isatty(3), isatty(4));
}
and call it in a child part of forkpty, and it displays 11100, which means ls should be colored!
OK, as it seems the fact that ls produces no color output has nothing to do with forkpty(). It is just not color enabled by default. But now, maybe that's another question, why it is not color if it just checks isatty()?

Ordering file location on linux partition

I have a process which processes a lot of files (~96,000 files, ~12 TB data). Several runs of the process has left the files scattered about the drive. Each iteration in the process, uses several files. This leads to a lot of whipsawing around the disk collecting the files.
Ideally, I would like the process to write the files it uses in order, so that the next run will read them in order (file sizes change). Is there a way to hint at a physical ordering/grouping, short of writing to the raw partition?
Any other suggestions would be helpful.
Thanks
There are two system calls you might lookup: fadvise64, fallocate tell the kernel how you intend to read or write a given file.
Another tip is the "Orlov block allocator" (Wikipedia, LWN) affects the way the kernel will allocate new directories and file-entries.
In the end I decided not to worry about writing the files in any particular ordering. Instead, before I started a run, I would figure out where the first block of each file was located, and then sort the file processing order by first block location. Not perfect, but it did make a big difference in processing times.
Here's the C code I used to get the first block of supplied file list I adapted it from example code I found online (can't seem to find the original source).
#include <stdio.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <assert.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <linux/fs.h>
//
// Get the first block for each file passed to stdin,
// write filename & first block for each file to stdout
//
int main(int argc, char **argv) {
int fd;
int block;
char fname[512];
while(fgets(fname, 511, stdin) != NULL) {
fname[strlen(fname) - 1] = '\0';
assert(fd=open(fname, O_RDONLY));
block = 0;
if (ioctl(fd, FIBMAP, &block)) {
printf("FIBMAP ioctl failed - errno: %s\n", strerror(errno));
}
printf("%010d, %s\n", block, fname);
close(fd);
}
return 0;
}

Resources