Writing an array to a file using C - c

I am creating a model of Unix v6 File system. I have tried to first allocate the available free blocks by writing it to the file and then reading the same when need. I am having a free array of 100 blocks, so when the number of free blocks goes beyond hundred, the present free array will be written to the memory block in free[0] and the new free block will be assigned to free[0]. The following is the sample code that I have written
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
void splitCommand(char**,char*, char*);
unsigned short freeArr[100];
unsigned short nfree=1,fd;
int main()
{
fd = open("v6", O_RDWR | O_CREAT | O_TRUNC, S_IREAD | S_IEXEC | S_IWRITE);
freeArr[0] = 0;
for (int i = 28; i < 5000; i++)
{
addFreeBlock(i);
}
unsigned short free1=0 ;
lseek(fd, 3127 * 512, SEEK_SET);
read(fd, &(nfree), sizeof(unsigned short));
printf("%d\n",nfree);
for(int i=0; i<nfree; i++)
{
read(fd, &free1, sizeof(unsigned short));
printf("%d\n",free1);
}
}
void addFreeBlock(int block_no)
{
if(block_no==3127)
{
int a=0;
}
if (nfree == 100)
{
lseek(fd, block_no * 512, SEEK_SET);
write(fd, &(nfree), sizeof(unsigned short)); // copy nfree into free array
write(fd, freeArr, 200);// copy free array
nfree=0;
}
freeArr[nfree] = block_no;
nfree++;
}
Consider we have 5000 blocks in total. Each block is 512 bytes long. I am using the first 27 blocks for other purposes and so I am writing the blocks from 28 to 5000.
Now after writing all the blocks, I tried reading the blocks stored at random location. When I tried reading the blocks stored at 3027, I am able to read the 100 blocks numbered from 2927,2928,2929,....,3026. But when I read the blocks stored at 3127, I am able to read only the blocks from 3027, 3028, 3029,....,3081. The remaining is just random. I also tried at some other positions. It works for some of them.
Can anyone tell me where I went wrong?

Sorry, this is not really an answer but I cannot write this in a comment. It does however answer the question somehow.
Your code works correctly, I tested with valgrind and there are no errors, but you should think about it a lot.
There is no need for a single global variable in your code, you should only use a global variable when you really know there is no better solution, this is an improved version of your exact code,
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdint.h>
uint16_t add_free_block(int fd, int block_number, uint16_t *free_blocks, uint16_t free_block_count);
int main(void)
{
uint16_t free_blocks[100];
uint16_t free_block_count;
int fd;
fd = open("v6", O_RDWR | O_CREAT | O_TRUNC, S_IREAD | S_IEXEC | S_IWRITE);
if (fd == -1)
return -1;
free_blocks[0] = 0;
free_block_count = 1;
for (int i = 28; i < 5000; i++) {
free_block_count = add_free_block(fd, i, free_blocks, free_block_count);
}
lseek(fd, 3127 * 512, SEEK_SET);
read(fd, &free_block_count, sizeof(uint16_t));
printf("%d\n", free_block_count);
for (int i = 0; i < free_block_count; i++) {
uint16_t block_number;
if (read(fd, &block_number, sizeof(uint16_t)) == sizeof(uint16_t)) {
printf("%d\n", block_number);
}
}
close(fd);
return 0;
}
uint16_t
add_free_block(int fd, int block_number, uint16_t *free_blocks, uint16_t free_block_count)
{
if (free_block_count == 100) {
lseek(fd, block_number * 512, SEEK_SET);
write(fd, &free_block_count, sizeof(uint16_t));
write(fd, free_blocks, free_block_count * sizeof(uint16_t));
free_block_count = 0;
}
free_blocks[free_block_count] = block_number;
return free_block_count + 1;
}
with 0 global variables (also, minor but important I closed the file descriptor).
You should also check other return values like write(), I didn't because I didn't want to do it all, I just wanted check what was wrong.
The real problem lies somewhere else in the rest of your program, not in this code. So please POST the real code and stop guessing what the problem is.
In general, these are my recommendations
DO NOT USE GLOBAL VARIABLES, unless you are very, very sure you MUST.
Add the function prototype.
DO NOT IGNORE compiler warnings.
ALWAYS check the return value of a function that does return, if you don't know that it does return, then 5.
READ carefully all the documentation for every function you use.

Related

Is there a maximum number of continuous pages per process in Linux? If so, how to set it to unlimited?

The following code will generate errno 12 cannot allocate memory
#include <sys/mman.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <memory.h>
#include <errno.h>
int main()
{
char* p;
for (size_t i = 0; i < 0x10000; i++)
{
char* addr = (char*)0xAAA00000000uL - i * 0x2000;
p = mmap(addr, 0x1000,
PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (p != addr) {
printf("%lu %d\n", i, errno);
getchar();
return 1;
}
memset(p, 'A' + (i % 26), 0x1000);
}
return 0;
}
The output is 65510 12 on my machine.
However, if we change size of each page from 0x1000 to 0x2000, the allocation will be successful, even if it is using more memory.
The only difference I think is the number of continuous pages, is there a limitation on this? If so, how to set it to unlimited?
It seems that setting /proc/sys/vm/max_map_count to a larger number solves the problem.
Reference: How much memory could vm use

Using mmap to reverse a text file in place -- getting bus error

I thought i had it figured out but i'm getting a bus error. All it has to do is take some text file, use mmap and then reverse the contents without a temp file. What i did was map it, and then erase the file and write it from memory by starting at the end of the mmap pointer. This worked when I did it with cout, but for some reason doing it to a file i get the error.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/io.h>
#include <sys/mman.h>
main(int argc, char *argv[])
{
unsigned char *f, *g;
int size;
struct stat s;
const char * file_name = argv[1];
int fd = open(argv[1], O_RDONLY);
int status = fstat(fd, &s);
size = s.st_size;
int i;
f = (char *) mmap (0, size, PROT_READ, MAP_PRIVATE, fd, 0);
//g = (char *) mmap (0, size, PROT_READ, MAP_PRIVATE, fd, 0);
for(i = 0; i < size; i++) {
char c;
c = f[i];
putchar(c);
}
//ABOVE THIS WORKS
// int z = 0;
//while(f[z] != NULL) {
//z++;
// printf("%d", z);
// }
int x;
int y = 0;
close(fd);
FILE *f1;
f1 = fopen(argv[1], "w+");
for(x = size - 1; x >= 0; x--)
{
char c;
c = f[x];
fputc(c, f1);
}
}
Because you fopened the file with w, you truncated the file to 0 length. The mmap man page says that:
The effect of changing the size of the underlying file of a mapping on the pages that correspond to added or removed regions of the file is unspecified.
Anyways, it seems to me that you should call mmap with PROT_WRITE also, so that you can just reverse the array f in memory. Then you don't have to open the file again. Make sure to use MMAP_SHARED, and to also call munmap() after you are finished modifying the shared memory. You need MMAP_SHARED because with MMAP_PRIVATE:
Updates to the mapping are not visible to other processes mapping the same file, and are not carried through to the underlying file.
You should call munmap() because:
The file may not actually be updated until msync(2) or munmap() is called.
If you exit the program without calling munmap(), the memory will automatically be unmapped for you. But it's a good habit to close/free/unmap things yourself instead of just exiting.
(Edit: Thanks Adam Rosenfield and EOF for the corrections to my original answer.)

write file by mmap, but when I use fread, the second time read error data

When I use mmap and memcpy to write a file, and then I use fread to read the data.
Below is my code, The problem is the first time i can read the a, but the second time i can't read a.
I guess there is something like seek position in fread function, when I use memcpy to write file, It may change the seek position.
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>
int main()
{
int fd = open("./aa", O_CREAT | O_RDWR | O_TRUNC, 0644);
FILE* f = fopen("./aa", "r");
if (ftruncate(fd, 1024) < 0) {
printf("ftruncate error\n");
}
void* base;
if ((base = mmap(NULL, 1024, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) {
printf("mmap error\n");
}
char* file_ptr = (char *)base;
char buffer[256];
char scratch[256];
buffer[0] = 'a';
memcpy(file_ptr, buffer, 1);
file_ptr += 1;
size_t n = fread(scratch, 1, 1, f);
printf("size n %zu\n", n); // this output size n 1
printf("scratch %c\n", scratch[0]); // this output scratch a
memcpy(file_ptr, buffer, 1);
file_ptr += 1;
n = fread(scratch, 1, 1, f);
printf("size n %zu\n", n); // this output size n 1
printf("scratch %c\n", scratch[0]); // but this output scratch
return 0;
}
The output is :
size n 1
scratch a
size n 1
scratch
First of all, #wildplasser is right, your program may work, but if you go on mixing mmap and stdio you'll need to make sure that writes done via mmap get committed (use the msync() function) and that fread isn't buffering stale data (fseek()ing to the current position should do the trick).
Coming to your question: your program doesn't print "scratch", it prints "scratch \0" :)
Seriously, what you do is initialize the size of the "aa" file via ftruncate(), which is the same as filling the missing bytes up to 1024 '\0'; you write an 'a', and read it; then you read another character, and you get one of the NULs.
Try printing the ascii character of scratch[0] and you'll see it's zero; if you're still not convinced, try adding something like
for(i = 0; i < 6; i++)
file_ptr[i] = "QWERTY"[i];
right before the first memcpy and see what happens.

Writing struct to mapped memory file (mmap)

I have a problem to write struct into a mapped memory file.
I have two file namely mmap.write.c and mmap.read.c, and in these files, I'm writing an integer to a file and reading it from file.
When I want to write struct and read it, I could not think about that since in line 32 of mmap.write.c
sprintf((char*) file_memory, "%d\n", i);
and in line 25 of mmap.read.c
sscanf (file_memory, "%d", &integer);
There is no difference to write and read integer/double/float/char etc. since I can put pattern as second argument "%d" for integer. But what I will write here to indicate struct? That is my main problem.
The struct that I want to write and read:
#define CHANNELS 20
typedef dataholder struct {
int value[CHANNELS];
time_t time;
int hash;
}dataholder;
mmap.read.c
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#include "mmap.h"
#define FILE_LENGTH 0x10000
int main (int argc, char* const argv[])
{
int fd;
void* file_memory;
int integer;
/* Open the file. */
fd = open (argv[1], O_RDWR, S_IRUSR | S_IWUSR);
printf("file opened\n");
/* Create the memory mapping. */
file_memory = mmap (0, FILE_LENGTH, PROT_READ | PROT_WRITE,
MAP_SHARED, fd, 0);
printf("memfile opened\n");
close (fd);
printf("file closed\n");
/* Read the integer, print it out, and double it. */
while(1) {
sscanf (file_memory, "%d", &integer);
printf ("value: %d\n", integer);
usleep(100000);
}
//sprintf ((char*) file_memory, "%d\n", 2 * integer);
/* Release the memory (unnecessary because the program exits). */
munmap (file_memory, FILE_LENGTH);
return 0;
}
mmap.write.c
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <time.h>
#include <unistd.h>
#include "mmap.h"
#define FILE_LENGTH 0x10000
/* Return a uniformly random number in the range [low,high]. */
int random_range (unsigned const low, unsigned const high)
{
unsigned const range = high - low + 1;
return low + (int) (((double) range) * rand () / (RAND_MAX + 1.0));
}
int main (int argc, char* const argv[])
{
int fd, i;
void* file_memory;
/* Seed the random number generator. */
srand (time (NULL));
/* Prepare a file large enough to hold an unsigned integer. */
fd = open (argv[1], O_RDWR | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR);
//lseek (fd, FILE_LENGTH+1, SEEK_SET);
write (fd, "", 1);
//lseek (fd, 0, SEEK_SET);
/* Create the memory mapping. */
file_memory = mmap (0, FILE_LENGTH, PROT_WRITE, MAP_SHARED, fd, 0);
close (fd);
/* Write a random integer to memory-mapped area. */
for(i=0; i<10000; i++) {
sprintf((char*) file_memory, "%d\n", i);
//goto a;
usleep(100000);
}
a:
/* Release the memory (unnecessary because the program exits). */
munmap (file_memory, FILE_LENGTH);
return 0;
}
Thanks a lot in advance.
First of all you have to keep track of where in the memory you want to write, second you have to remember that the mapped memory is just like any other pointer to memory. The last bit is important, as this means you can use normal array indexing to access the memory, or use functions such as memcpy to copy into the memory.
To write a structure, you have three choices:
Write the structure as-is, like in a binary file. This will mean you have to memcpy the structure to a specified position.
Write the structure, field-by-field, as text using e.g. sprintf to the correct position.
Treat the memory as one large string, and do e.g. sprintf of each field into a temporary buffer, then strcat to add it to the memory.
The simplest way is to just use a pointer:
dataholder *dh = file_memory;
/* now you can access dh->value, dh->time, dh->hash */
Since this struct doesn't contain any pointers, if you need to copy it in or out, you can just assign it, like:
dataholder dh_other = *dh;
or
*dh = dh_other;

Reading bytes from /dev/random fails

I have a piece of code written in POSIX compliant C and it doesn't seem to work correctly. The goal is to read from /dev/random, the interface to the Linux/BSD/Darwin kernel's random number generator and output the written byte to a file. I'm not quite sure what I'm overlooking as I'm sure I've covered every ground. Anyway, here it is:
int incinerate(int number, const char * names[]) {
if (number == 0) {
// this shouldn't happen, but if it does, print an error message
fprintf(stderr, "Incinerator: no input files\n");
return 1;
}
// declare some stuff we'll be using
long long lengthOfFile = 0, bytesRead = 0;
int myRandomInteger;
// open the random file block device
int zeroPoint = open("/dev/random", O_RDONLY);
// start looping through and nuking files
for (int i = 1; i < number; i++) {
int filePoint = open(names[i], O_WRONLY);
// get the file size
struct stat st;
stat(names[i], &st);
lengthOfFile = st.st_size;
printf("The size of the file is %llu bytes.\n", lengthOfFile);
while (lengthOfFile != bytesRead) {
read(zeroPoint, &myRandomInteger, sizeof myRandomInteger);
write(filePoint, (const void*) myRandomInteger, sizeof(myRandomInteger));
bytesRead++;
}
close(filePoint);
}
return 0;
}
Any ideas? This is being developed on OS X but I see no reason why it shouldn't also work on Linux or FreeBSD.
If it helps, I've included the following headers:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
Instead of
write(filePoint, (const void*) myRandomInteger, sizeof(myRandomInteger));
you surely meant to write
write(filePoint, (const void*) &myRandomInteger, sizeof(myRandomInteger));
didn't you? If you use the random bytes read from /dev/random as a pointer, you're almost certain to encounter a segfault sooner or later.

Resources