C - Memory access with mmap - c

I have a hex file of 327680 characters which I'm writing to physical address 0x30000000 - 0x3004FFFF on the memory on my ARM linux system.
While reading back from the memory I'm getting a segfault after reading 64170 characters from the start address, ie at 0x3000FAAA.
If I change my starting address to 0x3000FA64, then also I get a segfault after 64170 characters.
How do I ensure data is accessed correctly if Data > 4kB (page size) ?
I'm unable to understand the exact problem, so I'm adding the snippet of my code below:
#define MAX_RANGE 327679
int fd;
FILE* fd_table=NULL;
unsigned long int count = 0 ;
void * mem;
void * aligned_vaddr;
unsigned long aligned_paddr;
uint32_t aligned_size;
unsigned long int addr_phys;
uint8_t *addr;
int g_size = 1;
unsigned long int g_paddr = 0x30000000; //Starting physical address
while((count<MAX_RANGE)){
g_paddr = addr_phys;
g_paddr &= ~(g_size - 1);
aligned_paddr = g_paddr & ~(4096 - 1);
aligned_size = g_paddr - aligned_paddr + (g_count * g_size);
aligned_size = (aligned_size + 4096 - 1) & ~(4096 - 1);
/* Align address to access size */
aligned_vaddr = mmap(NULL, aligned_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, aligned_paddr);
if (aligned_vaddr == NULL) {
printf("Error mapping address\n");
close(fd);
return 1;
}
mem = (void *)((uint32_t)aligned_vaddr + (g_paddr - aligned_paddr));
addr = mem;
fprintf(fd_table, "%02X\n",addr[0]);
addr_phys +=1; //Increment byte address
count++;
}
Note:
1. There is no error in the write process, I have verified by viewing the segfault address with memtool.
2. The address 0x30000000 onwards is not used by the system (I have ensured that in the u-boot).

Related

How to get the pagesize of a memory segment just from a virtual address?

Linux can have both standard 4KiB page memory and 1GiB (huge) paged memory (and 2MiB pages, but I don't know if anyone uses that).
Is there a standard call to get the page size from an arbitrary virtual address? The pointer could be pointing to 4K pages or huge pages.
The problem at hand is to sanity (assert(...)) check arguments to a function that requires the base address and size of the region needs to be multiples of the page size, to be handed to mbind. But the page size varies on the system. Without sanity checking, the return value of mbind just gives Invalid argument which is not helpful for debugging.
I've looked at this answer How to get linux kernel page size programmatically but it gives answers that assume that the entire system is the same, and they are also compile time constants. Also getpagesize() does the same and it is deprecated anyways.
This is related to the MMU, see https://unix.stackexchange.com/questions/128213/how-is-page-size-determined-in-virtual-address-space and normally the page size is equal for the entire system / kernel, it is determined during kernel compilation
I realize one way of doing it is to scrape /proc/self/maps and somewhere in there is a keyword that indicates if a memory range has huge pages or not. I don't know how portable that is (man page for /proc doesn't say what it is, but I've seen it). But that seems a heavy handed way of just looking up a pointer to get the page size. And then on top of that, I don't think it indicates page size, just whether or not it is "huge pages".
I am not 100% i understand your requirements correctly, but i 'll give it a try.
There is an interesting function posted here by user Ciro Santilli, pagemap_get_entry. It uses the /proc/[pid]/pagemap interface to get the page table entry (pte) that corresponds to the virtual address you give as input. From the pte, you get the pfn (physical frame number) where the virtual address is mapped. Having this function, we can use the following logic to find out if a virtual address is mapped to 4K, 2M or 1G physical page:
First, get the address of the 1G virtual page where the virtual address of interest belongs. Call pagemap_get_entry with that virtual address and if the returned pfn is 218-aligned, then assume we are on a 1G physical page (218 is used because we assume size of physical frame to be 4K=212 bytes and 218*212=230=1GiB).
Else, get the address of the 2M virtual page inside which the virtual address falls. Call pagemap_get_entry with that and if the returned pfn is 29-aligned, then assume we are inside a 2M physical page (again 29*212=221=2MiB).
Else, assume that virtual address is mapped in RAM with 4K physical page.
With code, i hope it would be something like that (part of linked post is reposted here for completeness):
#define _XOPEN_SOURCE 700
#include <fcntl.h> /* open */
#include <stdint.h> /* uint64_t */
#include <stdio.h> /* printf */
#include <stdlib.h> /* size_t, malloc */
#include <unistd.h> /* pread, sysconf, getpid */
#include <sys/types.h> /* getpid */
#include <string.h> /* memset */
typedef struct {
uint64_t pfn : 55;
unsigned int soft_dirty : 1;
unsigned int file_page : 1;
unsigned int swapped : 1;
unsigned int present : 1;
} PagemapEntry;
/* Parse the pagemap entry for the given virtual address.
*
* #param[out] entry the parsed entry
* #param[in] pagemap_fd file descriptor to an open /proc/pid/pagemap file
* #param[in] vaddr virtual address to get entry for
* #return 0 for success, 1 for failure
*/
int pagemap_get_entry(PagemapEntry *entry, int pagemap_fd, uintptr_t vaddr)
{
size_t nread;
ssize_t ret;
uint64_t data;
uintptr_t vpn;
vpn = vaddr / sysconf(_SC_PAGE_SIZE);
nread = 0;
while (nread < sizeof(data)) {
ret = pread(pagemap_fd, ((uint8_t*)&data) + nread, sizeof(data) - nread,
vpn * sizeof(data) + nread);
nread += ret;
if (ret <= 0) {
return 1;
}
}
entry->pfn = data & (((uint64_t)1 << 55) - 1);
entry->soft_dirty = (data >> 55) & 1;
entry->file_page = (data >> 61) & 1;
entry->swapped = (data >> 62) & 1;
entry->present = (data >> 63) & 1;
return 0;
}
int main()
{
unsigned long long PAGE_SIZE_1G = 1024*1024*1024;
unsigned long long PAGE_SIZE_2M = 2*1024*1024;
unsigned long long PAGE_SIZE_4K = 4*1024;
uint64_t pfn_1g, pfn_2m, pfn_4k, pfn_original;
char * arr = (char *)malloc(4*PAGE_SIZE_1G * sizeof(char));
if (arr == NULL) {
printf("malloc\n");
return 1;
}
memset(arr, 1, 4*PAGE_SIZE_1G);
uintptr_t vaddr = (uintptr_t)arr + 1024*1025*1026; // get a random virtual address
PagemapEntry entry;
uintptr_t vaddr_1g_aligned = vaddr & ~(PAGE_SIZE_1G - 1);
uintptr_t vaddr_2m_aligned = vaddr & ~(PAGE_SIZE_2M - 1);
uintptr_t vaddr_4k_aligned = vaddr & ~(PAGE_SIZE_4K - 1);
printf("Virtual address of interest %jx\n", (uintmax_t) vaddr);
printf("1G-aligned virtual address %jx\n", (uintmax_t) vaddr_1g_aligned);
printf("2M-aligned virtual address %jx\n", (uintmax_t) vaddr_2m_aligned);
printf("4K-aligned virtual address %jx\n", (uintmax_t) vaddr_4k_aligned);
char pagemap_file[BUFSIZ];
int pagemap_fd;
pid_t pid = getpid();
snprintf(pagemap_file, sizeof(pagemap_file), "/proc/%ju/pagemap", (uintmax_t)pid);
pagemap_fd = open(pagemap_file, O_RDONLY);
if (pagemap_fd < 0) {
return 1;
}
if (pagemap_get_entry(&entry, pagemap_fd, vaddr_1g_aligned)) {
printf("pagemap_get_entry\n");
return 1;
}
pfn_1g = entry.pfn;
if (pagemap_get_entry(&entry, pagemap_fd, vaddr_2m_aligned)) {
printf("pagemap_get_entry\n");
return 1;
}
pfn_2m = entry.pfn;
if (pagemap_get_entry(&entry, pagemap_fd, vaddr_4k_aligned)) {
printf("pagemap_get_entry\n");
return 1;
}
pfn_4k = entry.pfn;
if (pagemap_get_entry(&entry, pagemap_fd, vaddr)) {
printf("pagemap_get_entry\n");
return 1;
}
pfn_original = entry.pfn;
printf("pfn of 1G-alignment: %jx\n", (uintmax_t) pfn_1g);
printf("pfn of 2M-alignment: %jx\n", (uintmax_t) pfn_2m);
printf("pfn of 4K-alignment: %jx\n", (uintmax_t) pfn_4k);
printf("pfn of original address: %jx\n", (uintmax_t) pfn_original);
if ((pfn_1g != 0) && (pfn_1g % (1 << 18) == 0)) {
printf("Virtual address is mapped to 1G physical page\n");
}
else if ((pfn_2m != 0) && (pfn_2m % (1 << 9) == 0)) {
printf("Virtual address is mapped to 2M physical page\n");
}
else {
printf("Virtual address is mapped to 4K physical page\n");
}
return 0;
}
As original poster explains, you have to run this program with sudo, because of read access to /proc/<pid>/pagemap.
In my system that supports only 2M and 4K page sizes, i get the followings:
root#debian # cat /sys/kernel/mm/transparent_hugepages/enabled
always madvise [never]
root#debian # ./physical_page_size
Virtual address of interest 7f4f9d01a810
1G-aligned virtual address 7f4f80000000
2M-aligned virtual address 7f4f9d000000
4K-aligned virtual address 7f4f9d01a000
pfn of 1G-alignment: 1809fa
pfn of 2M-alignment: 1639fa
pfn of 4K-alignment: 163a14
pfn of original address: 163a14
Virtual address is mapped to 4K physical page
root#debian # echo "always" > /sys/kernel/mm/transparent_hugepages/enabled
root#debian # ./physical_page_size
Virtual address of interest 7f978d0d2810
1G-aligned virtual address 7f9780000000
2M-aligned virtual address 7f978d000000
4K-aligned virtual address 7f978d0d2000
pfn of 1G-alignment: 137a00
pfn of 2M-alignment: 145a00
pfn of 4K-alignment: 145ad2
pfn of original address: 145ad2
Virtual address is mapped to 2M physical page
Also, i have to mention that when the program reports 1G or 2M physical page size, it is not guaranteed that this is the case, however is very highly possible.
Finally, i see that your problem is with mbind. Again, i am not sure i understand it correctly or if this is a valid suggestion, but maybe you could try all possible page sizes starting from smallest until the call succeeds.
int wrapper(void *start, unsigned long size)
{
unsigned long long PAGE_SIZE_4K = 4*1024;
unsigned long long PAGE_SIZE_2M = 2*1024*1024;
unsigned long long PAGE_SIZE_1G = 1024*1024*1024;
void *start_4k = (void *)((unsigned long) start & ~(PAGE_SIZE_4K-1));
void *start_2m = (void *)((unsigned long) start & ~(PAGE_SIZE_2M-1));
void *start_1g = (void *)((unsigned long) start & ~(PAGE_SIZE_1G-1));
unsigned long size_4k, size_2m, size_1g;
if (size % PAGE_SIZE_4K != 0) {
size_4k = size - (size % PAGE_SIZE_4K) + PAGE_SIZE_4K;
}
if (size % PAGE_SIZE_2M != 0) {
size_2m = size - (size % PAGE_SIZE_2M) + PAGE_SIZE_2M;
}
if (size % PAGE_SIZE_1G != 0) {
size_1g = size - (size % PAGE_SIZE_1G) + PAGE_SIZE_1G;
}
if (mbind(start_4k, size_4k, .....) == 0) {
return 0;
}
if (mbind(start_2m, size_2m, .....) == 0) {
return 0;
}
if (mbind(start_1g, size_1g, .....) == 0) {
return 0;
}
return 1;
}

retrieve information from a structure with ptrace

Here, I explain my problem, I am a beginner on the ptrace function and I would like to succeed in recovering the hard information of a structure.
For example with this command, I will have strace -e trace = fstat ls
a line: fstat (3, {st_mode = ..., st_size = ...}
and I would like to successfully retrieve the contents of the structure (st_mode) and (st_size).
I try this but to no avail:
int buffer(unsigned long long addr, pid_t child, size_t size, void *buffer)
{
size_t byte = 0;
size_t data;
unsigned long tmp;
while (byte < size) {
tmp = ptrace(PTRACE_PEEKDATA, child, addr + byte);
if ((size - byte) / sizeof(tmp))
data = sizeof(tmp);
else
data = size % sizeof(tmp);
memcpy((void *)(buffer + byte), &tmp, data);
byte += data;
}
}
and in params :
struct stat stat_i;
buffer(addr, pid, sizeof(stat_i), &stat_i);
printf("%lu", stat_i.st_size); -> fake value :/
Thank'ks !
From the man page,
PTRACE_PEEKTEXT, PTRACE_PEEKDATA
Read a word at the address addr in the tracee's memory,
returning the word as the result of the ptrace() call. Linux
does not have separate text and data address spaces, so these
two requests are currently equivalent. (data is ignored; but
see NOTES.)
Thus you must understand that tmp would hold the actually value that was read.
Your checks are wrong - you should set errno = 0 before the call and then check if it has changed. If it has - you've got an error. If it hasn't - you can be assured that tmp has the word from the remote process.
Try something like this:
int buffer(unsigned long long addr, pid_t child, size_t size, void *buffer)
{
size_t byte = 0;
size_t data;
unsigned long tmp;
// support for word aligned sizes only
if (size % sizeof(long) != 0)
return -1;
long * buffer_int = (long*) buffer;
while (byte < size) {
errno = 0;
tmp = ptrace(PTRACE_PEEKDATA, child, addr + byte);
if (errno)
return -1;
buffer_int[byte / sizeof(long)] = tmp;
byte += sizeof(long);
}
}

realloc overwrite variable (Xilinx SDK on a Zynq SoC (Cortex A9))

As mentioned I have a Zynq SoC (ZC706 Eval Board) and I'm trying to read an image from the SD Card. To do this I'm using the FatFs lib (http://elm-chan.org/fsw/ff/00index_e.html).
In my code I read 4096 Byte from the file and save it to a buffer. After that i copy the buffer to an unsigned char pointer that size I increase after every read operation.
Then I'm using realloc, the for loop in the copyU32ArrayToUnsignedCharArray function 'failed' because the size variable is overwritten by the out array.
Code that overwrite the "size" in the copyU32ArrayToUnsignedCharArray function:
u32 buffer[1024];
unsigned char *img = NULL;
bytesreaded = 0;
for (;;) {
br=0;
fr = f_read(&fil, buffer, sizeof(buffer), &br); /* Read a chunk of source file */
if (fr || br == 0)
break; /* error or eof */
img = realloc(img,br);
copyU32ArrayToUnsignedCharArray(buffer, &img[bytesreaded], br/4); // /4 because u32(32 bit) in to unsigned char(8 bit)
bytesreaded += br; // update readed bytes
}
The code that worked:
u32 buffer[1024];
unsigned char *img = NULL;
img = malloc(512*512*3+100);
bytesreaded = 0;
for (;;) {
br=0;
fr = f_read(&fil, buffer, sizeof(buffer), &br); /* Read a chunk of source file */
if (fr || br == 0)
break; /* error or eof */
copyU32ArrayToUnsignedCharArray(buffer, &img[bytesreaded], br/4); // /4 because u32(32 bit) in to unsigned char(8 bit)
bytesreaded += br; // update readed bytes
}
The copyU32ArrayToUnsignedCharArray function:
void copyU32ArrayToUnsignedCharArray(u32 *in, unsigned char* out, uint size){
int i,x;
x = 0;
for (i = 0; i < size; i++) {
if(size != 1024)
break;
in[i] = Xil_In32BE(&in[i]);
out[x] = (u32) in[i] >> 24;
out[x + 1] = (u32) in[i] >> 16 & 0x00FF;
out[x + 2] = (u32) in[i] >> 8 & 0x0000FF;
out[x + 3] = (u32) in[i] & 0x000000FF;
x += 4;
}
}
I want to use realloc because I don't know how big the image will be that I read.
Update:
Some further information to the code that doesn't work. I debugged it and the pointer to *img isn't null, so the realloc was successfully. If I'm using gdb the following things happen in the copyU32ArrayToUnsignedCharArray function:
- pointer to the variable "out" is 0x001125a8
- the address of the "size" variable is 0x0011309c (the value that is stored at this location is correct)
- the space in memory between this two variables is 0xaf4 = 2804 dec (difference of the two addresses)
- if the for loop within the copyU32ArrayToUnsignedCharArray function reached i=702 and x=2808 the size variable is changed to another value
Sincerely,
Arno
I solved the problem with the hint from Notlikethat. The problem was the small heap size. Increasing the heap is done by editing the linker script file

Linux kernel: zap_page_range() duration

zap_page_range is defined in mm/memory.c. Here is how it is defined:
/*
* remove user pages in a given range.
*/
void zap_page_range(struct mm_struct *mm, unsigned long address, unsigned long size)
{
mmu_gather_t *tlb;
pgd_t * dir;
unsigned long start = address, end = address + size;
int freed = 0;
dir = pgd_offset(mm, address);
/*
* This is a long-lived spinlock. That's fine.
* There's no contention, because the page table
* lock only protects against kswapd anyway, and
* even if kswapd happened to be looking at this
* process we _want_ it to get stuck.
*/
if (address >= end)
BUG();
spin_lock(&mm->page_table_lock);
flush_cache_range(mm, address, end);
tlb = tlb_gather_mmu(mm);
do {
freed += zap_pmd_range(tlb, dir, address, end - address);
address = (address + PGDIR_SIZE) & PGDIR_MASK;
dir++;
} while (address && (address < end));
/* this will flush any remaining tlb entries */
tlb_finish_mmu(tlb, start, end);
/*
* Update rss for the mm_struct (not necessarily current->mm)
* Notice that rss is an unsigned long.
*/
if (mm->rss > freed)
mm->rss -= freed;
else
mm->rss = 0;
spin_unlock(&mm->page_table_lock);
}
For some reason, I want this function to be executed as long as possible. Can anyone tell me how to do this? If the size is really big, will it take longer?

XV6 crashes when trying to implement triple indirection in xv6 operating system

The original xv6-rev7 operating system contains:
12 directed blocks
1 indirect blcok(points to 128 blocks)
This means we have 140 blocks.
Each block's size is 512KB ==> 512 * 140 = 71,680 ~= 70KB is the limit of file's size in xv6.
I want to implemet triple indirect access in xv6 in order to support files with size of 40MB.
In order to do it I will need to implement double indirect before the triple indirect.
So I took 2 directed blocks from the 12 I had.
1 for the double indirect and the other for the triple indirect.
This is what I have right now:
Direct: 10 blocks
Single indirect: 128
Double indirect: 128*128
Triple indirect: 4*128*128 (I am using 4 instead of 128 because this is enough for 40MB)
This is why #define NDIRECT 10 and uint addrs[NDIRECT+3];
File's size limit = (10 + 128 + 128*128 + 4*128*128)*512kb = 42,013,696 ~= 42MB
So I understand the concept.
The implementation of the triple-indirection is in function bmap in file fs.c.
This is how it looks:
For some reason when I am trying to create file with size of 8.5MB it fails:
I am using bochs emulator
I am also not sure to what values I need to change in mkfs.c:
int nblocks = 20985;
int nlog = LOGSIZE;
int ninodes = 200;
int size = 21029;
fs.h:
// On-disk file system format.
// Both the kernel and user programs use this header file.
// Block 0 is unused.
// Block 1 is super block.
// Blocks 2 through sb.ninodes/IPB hold inodes.
// Then free bitmap blocks holding sb.size bits.
// Then sb.nblocks data blocks.
// Then sb.nlog log blocks.
#define ROOTINO 1 // root i-number
#define BSIZE 512 // block size
// File system super block
struct superblock {
uint size; // Size of file system image (blocks)
uint nblocks; // Number of data blocks
uint ninodes; // Number of inodes.
uint nlog; // Number of log blocks
};
#define NDIRECT 10
#define NINDIRECT (BSIZE / sizeof(uint))
#define MAXFILE (NDIRECT + NINDIRECT + NINDIRECT*NINDIRECT + 4*NINDIRECT*NINDIRECT)
// On-disk inode structure
struct dinode {
short type; // File type
short major; // Major device number (T_DEV only)
short minor; // Minor device number (T_DEV only)
short nlink; // Number of links to inode in file system
uint size; // Size of file (bytes)
uint addrs[NDIRECT+3]; // Data block addresses
};
// Inodes per block.
#define IPB (BSIZE / sizeof(struct dinode))
// Block containing inode i
#define IBLOCK(i) ((i) / IPB + 2)
// Bitmap bits per block
#define BPB (BSIZE*8)
// Block containing bit for block b
#define BBLOCK(b, ninodes) (b/BPB + (ninodes)/IPB + 3)
// Directory is a file containing a sequence of dirent structures.
#define DIRSIZ 14
struct dirent {
ushort inum;
char name[DIRSIZ];
};
fs.c:
// Return the disk block address of the nth block in inode ip.
// If there is no such block, bmap allocates one.
static uint
bmap(struct inode *ip, uint bn)
{
uint addr, *a;
struct buf *bp;
if(bn < NDIRECT){
if((addr = ip->addrs[bn]) == 0)
ip->addrs[bn] = addr = balloc(ip->dev);
return addr;
}
bn -= NDIRECT;
if(bn < NINDIRECT){
// Load indirect block, allocating if necessary.
if((addr = ip->addrs[NDIRECT]) == 0)
ip->addrs[NDIRECT] = addr = balloc(ip->dev);
bp = bread(ip->dev, addr);
a = (uint*)bp->data;
if((addr = a[bn]) == 0){
a[bn] = addr = balloc(ip->dev);
log_write(bp);
}
brelse(bp);
return addr;
}
/* Double indirect */
bn -= NINDIRECT;
if(bn < NINDIRECT*NINDIRECT){
// Load 2nd indirect block, allocating if necessary.
if((addr = ip->addrs[NDIRECT+1]) == 0) // 2d block. NDIRECT+1 is to get the index vector
ip->addrs[NDIRECT+1] = addr = balloc(ip->dev);
bp = bread(ip->dev, addr);
a = (uint*)bp->data;
if ((addr = a[bn/(NINDIRECT)]) == 0) { /* get index for 1st
indirection. (NINDIRECT is 128) */
a[bn/(NINDIRECT)] = addr = balloc(ip->dev);
log_write(bp);
}
brelse(bp); /* release the double indirect table
(main level) */
bp = bread(ip->dev, addr);
a = (uint*)bp->data;
if ((addr = a[bn%(NINDIRECT)]) == 0) { /* get the 2nd level table */
a[bn%(NINDIRECT)] = addr = balloc(ip->dev);
log_write(bp);
}
brelse(bp);
return addr;
}
/* Triple indirect */
bn -= NINDIRECT*NINDIRECT;
if(bn < 4*NINDIRECT*NINDIRECT){
// Load 3rd indirect block, allocating if necessary.
if((addr = ip->addrs[NDIRECT+2]) == 0) // 3d block. NDIRECT+2 is to get the index vector
ip->addrs[NDIRECT+2] = addr = balloc(ip->dev);
bp = bread(ip->dev, addr);
a = (uint*)bp->data;
if ((addr = a[bn/(NINDIRECT*4)]) == 0) { /* get index for 2st
indirection. (NINDIRECT is 128) */
a[bn/(NINDIRECT*4)] = addr = balloc(ip->dev);
log_write(bp);
}
brelse(bp);
bp = bread(ip->dev, addr);
a = (uint*)bp->data;
if ((addr = a[bn/(NINDIRECT*NINDIRECT*4)]) == 0) {
a[bn/(NINDIRECT*NINDIRECT*4)] = addr = balloc(ip->dev);
log_write(bp);
}
brelse(bp);
if ((addr = a[bn%(NINDIRECT*NINDIRECT*4)]) == 0) {
a[bn%(NINDIRECT*NINDIRECT*4)] = addr = balloc(ip->dev);
log_write(bp);
}
brelse(bp);
return addr;
}
panic("bmap: out of range");
}
mkfs.c:
#define stat xv6_stat // avoid clash with host struct stat
#include "types.h"
#include "fs.h"
#include "stat.h"
#include "param.h"
int nblocks = 20985;
int nlog = LOGSIZE;
int ninodes = 200;
int size = 21029;
bigfile.c:
#include "types.h"
#include "stat.h"
#include "user.h"
#include "fcntl.h"
void
help()
{
printf(1, "usage:\nfiles <name> <letter> <num>\n"
"e.g. nfiles foo a 40\n creates a file foo, with 40 times the letter a\n");
}
void
num2str(int i, char str[3])
{
str[2]=i%10+'0';
i=i/10;
str[1]=i%10+'0';
i=i/10;
str[0]=i%10+'0';
i=i/10;
}
#define BUF_SZ 512
int
main(int argc, char *argv[])
{
int i, count, fd, n;
// char *name;
// char c;
char buf[BUF_SZ];
if (argc !=4) {
help();
exit();
}
count = atoi(argv[3]);
if((fd=open(argv[1], O_CREATE|O_RDWR))<0) {
printf(2,"Failed to open file: %s\n", argv[1]);
exit();
}
for (i=0; i<BUF_SZ;i++)
buf[i]=argv[2][0];
for (i=0; i<count/BUF_SZ;i++)
if ((n=write(fd,buf,BUF_SZ)) != BUF_SZ)
{
printf(2,"Failed 1 to Write count=%d\n",i*BUF_SZ);
exit();
}
for (i=0; i<count%BUF_SZ;i++)
if ((n=write(fd,argv[2],1)) != 1)
{
printf(2,"Failed 2 to Write count=%d\n",count-i);
exit();
}
exit();
}
1.The number of nblocks which is defined in mkfs.c is insufficient.
int nblocks = 20985;
int nlog = LOGSIZE;
int ninodes = 200;
int size = 21029;
You have defined:
#define MAXFILE (NDIRECT + NINDIRECT + NINDIRECT*NINDIRECT + 4*NINDIRECT*NINDIRECT
which equals: 10+128+128^2+4*128^2 = 82058.
Just pick a number of nblocks which is greater than 82058, and update size accordingly.
2.In your bmap() function, in the triple indirection code, your first level of indirection is to a four-entry array (as you mentioned in your diagram).
Once you know to which of these four entries you should access, you're back with the double indirection problem, which you already solved.
So, in order to know which of the four entries you should access, you can use this:
if((addr = a[bn/(NINDIRECT*NINDIRECT)]) == 0){
a[bn/(NINDIRECT*NINDIRECT)] = addr = balloc(ip->dev);
log_write(bp);
}
You then could decrease bn like so:
bn -= ((NINDIRECT*NINDIRECT)*(bn/(NINDIRECT*NINDIRECT)));
and solve the double indirection problem again.

Resources