示例#1
0
文件: pager.c 项目: gapry/aos-1
/*
 * Internal function to read from swap file into a page table entry
 */
static void readFromSwap(int swapIndex,int pagetableIndex, int writeToSwap,L4_ThreadId_t tid,L4_Fpage_t fpage) {
  //Initialise swap if not initialised
  if(!swapInitialised) {
    initialise_swap();
  }
  struct page_token *token_val;
  
  page_table[pagetableIndex].read_bytes_transferred = 0;
  page_table[pagetableIndex].being_updated = 1;
  page_table[pagetableIndex].error_in_transfer = 0;

  for(int i=0;i<PAGESIZE/NFS_READ_SIZE;i++) {
    //Set the token value
    token_val = (struct page_token *) malloc(sizeof(struct page_token));  
    token_val -> pageIndex = pagetableIndex;
    token_val -> swapIndex = swapIndex;
    //Read will always be the last to reply so it always replies
    token_val -> send_reply = 1;
    token_val -> chunk_index = i;
    //Set bit whether the read from swap occurs after a swap write
    token_val -> writeToSwapIssued = writeToSwap;
    token_val -> destination_tid = tid;
    token_val -> destination_page = fpage;
    token_val -> source_tid = page_table[pagetableIndex].tid;
    token_val -> source_page = page_table[pagetableIndex].pageNo;
    //Issue all the nfs reads for the chunks at once
    nfs_read(swapcookie,swap_table[swapIndex].offset+i*NFS_READ_SIZE,NFS_READ_SIZE,pager_read_callback,(uintptr_t) token_val);
  }
  send = 0;
}
示例#2
0
文件: nfsfs.c 项目: dafyddcrosby/L4OS
/* Run the actual read request */
static
void
rq_read_run(NFS_ReadRequest *rq) {
	dprintf(2, "run NFS Read request\n");
	NFS_File *nf = (NFS_File *) rq->p.vnode->extra;
	nfs_read(&(nf->fh), rq->pos, rq->nbyte, read_cb, rq->p.token);
}
示例#3
0
static void read_callback (void *arg, struct nfs_client *client,
                          READ3res *result)
{
    struct http_cache_entry *e = arg;
    assert( e != NULL);

    assert (result != NULL);
    assert (result->status == NFS3_OK);
    READ3resok *res = &result->READ3res_u.resok;
    assert(res->count == res->data.data_len);

    assert (e->hbuff != NULL);
    assert (e->hbuff->data != NULL );

    assert (e->hbuff->len >= e->copied + res->data.data_len);
    memcpy (e->hbuff->data + e->copied, res->data.data_val, res->data.data_len);
    e->copied += res->data.data_len;

//    DEBUGPRINT ("got response of len %d, filesize %lu\n",
//    			res->data.data_len, e->copied);

    // free arguments
    xdr_READ3res(&xdr_free, result);

    if (!res->eof) {
        // more data to come, read the next chunk
        err_t err = nfs_read(client, e->file_handle, e->copied, MAX_NFS_READ,
                        read_callback, e);
        assert(err == ERR_OK);
        return;
    }

	/* This is the end-of-file, so deal with it. */

    e->valid = 1;
	e->loading = 0;
	decrement_buff_holder_ref (e->hbuff);

#ifdef PRELOAD_WEB_CACHE
	if (!cache_loading_phase) {
		handle_pending_list (e); /* done! */
		return;
	}

	/* This is cache loading going on... */
	printf("Copied %zu bytes for file [%s] of length: %zu\n",
				e->copied, e->name, e->hbuff->len);
	++cache_loaded_counter;
	handle_cache_load_done();

#else // PRELOAD_WEB_CACHE
	handle_pending_list(e); /* done! */
#endif // PRELOAD_WEB_CACHE
}
示例#4
0
文件: swapper.c 项目: gz/aos10
/**
 * Called by the pager. Reads a given page back into memory.
 *
 * @param page the page we wan't to read back into memory
 * @return SWAPPING_PENDING
 */
int swap_in(page_queue_item* page) {
	assert(page != NULL);
	file_table_entry* swap_fd = get_process(root_thread_g)->filetable[SWAP_FD];
	assert(swap_fd != NULL);

	page->to_swap = 0; // to keep track of how many bytes are read
	TAILQ_INSERT_TAIL(&swapping_pages_head, page, entries);

	nfs_read(&swap_fd->file->nfs_handle, page->swap_offset+page->to_swap, BATCH_SIZE, &swap_read_callback, (int)page);

	return SWAPPING_PENDING;
}
示例#5
0
ssize_t read(int fd, void *buf, size_t count)
{
	if (nfs_fd_list[fd].is_nfs == 1) {
		int ret;

		LD_NFS_DPRINTF(9, "read(fd:%d count:%d)", fd, (int)count);
		if ((ret = nfs_read(nfs_fd_list[fd].nfs, nfs_fd_list[fd].fh,
				count, buf)) < 0) {
			errno = -ret;
			return -1;
		}
		return ret;
	}
	return real_read(fd, buf, count);
}
示例#6
0
文件: swapper.c 项目: gz/aos10
/**
 * Read Callback for the NFS library if we're reading a page back into memory.
 * Again we split the NFS calls up and always read BATCH_SIZE bytes per call.
 * Note that once the page is competely swapped in it is inserted back into
 * the page queue which holds all the active pages at any given time.
 *
 * @param token pointer to the page item
 * @param bytes_read should always be BATCH_SIZE
 * @param data pointer to the data
 */
static void swap_read_callback(uintptr_t token, int status, fattr_t *attr, int bytes_read, char *data) {

	page_queue_item* page = (page_queue_item*) token;
	page_table_entry* pte = pager_table_lookup(page->tid, page->virtual_address);
	file_table_entry* swap_fd = get_process(root_thread_g)->filetable[SWAP_FD];

	if(page->process_deleted) {
		TAILQ_REMOVE(&swapping_pages_head, page, entries);
		free(page);
		return;
	}

	switch (status) {
		case NFS_OK:
		{
			assert(bytes_read == BATCH_SIZE);

			memcpy( ((char*)pte->address_ptr)+page->to_swap, data, bytes_read);
			page->to_swap += bytes_read;

			// swapping in complete
			if(page->to_swap == PAGESIZE) {
				// restart the thread because the page is in memory again
				TAILQ_REMOVE(&swapping_pages_head, page, entries);
	    		TAILQ_INSERT_TAIL(&active_pages_head, page, entries);
				send_ipc_reply(page->tid, L4_PAGEFAULT, 0);
			}
			else {
				// read next batch
				nfs_read(&swap_fd->file->nfs_handle, page->swap_offset+page->to_swap, BATCH_SIZE, &swap_read_callback, (int)page);
			}

		}
		break;

		default:
			dprintf(0, "%s: Bad NFS status (%d) from callback.\n", __FUNCTION__, status);
			assert(FALSE);
			// We could probably try to restart swapping here but since it failed before
			// we don't see much point in this.
		break;
	}

}
示例#7
0
//keep alive the filehandles nfs connection
//by blindly doing a read 32bytes - seek back to where
//we were before
void CNFSConnection::keepAlive(std::string _exportPath, struct nfsfh  *_pFileHandle)
{
  uint64_t offset = 0;
  char buffer[32];
  // this also refreshs the last accessed time for the context
  // true forces a cachehit regardless the context is timedout
  // on this call we are sure its not timedout even if the last accessed
  // time suggests it.
  struct nfs_context *pContext = getContextFromMap(_exportPath, true);
  
  if (!pContext)// this should normally never happen - paranoia
    pContext = m_pNfsContext;
  
  XBMC->Log(ADDON::LOG_NOTICE, "NFS: sending keep alive after %i s.",KEEP_ALIVE_TIMEOUT/2);
  PLATFORM::CLockObject lock(*this);
  nfs_lseek(pContext, _pFileHandle, 0, SEEK_CUR, &offset);
  nfs_read(pContext, _pFileHandle, 32, buffer);
  nfs_lseek(pContext, _pFileHandle, offset, SEEK_SET, &offset);
}
示例#8
0
/*
 * Read a small block of data from the input file.
 */
int
fileread(oskit_addr_t file_ofs, void *buf, oskit_size_t size,
	 oskit_size_t *out_actual)
{
	int n, bytes_read;
	int offset;
	char *bufp = buf;
	int chunk_size;
	int readahead;
#ifdef  CYCLECOUNTS
	unsigned long long before_cycles;
#endif

	bytes_read = 0;
	offset = file_ofs;
	while (size > 0) {
		chunk_size = min(NFS_READ_SIZE, size);
		if (kimg_readahead < 0)
			readahead = (size / NFS_READ_SIZE) - 1;
		else
			readahead = kimg_readahead;

		GETCSTAMP(before_cycles);
		n = nfs_read(ipaddr, nfs_port,
			     filefh, offset, chunk_size, bufp, readahead);
		UPDATECSTAMP(total_readwait_cycles, before_cycles);

		if (n < 0) {
			printf("Unable to read text: %s\n", nfs_strerror(n));
			return 1;	/* XXX Should be an EX_ constant */
		}
		if (n == 0)
			break;		/* hit eof */
		offset += n;
		bufp += n;
		size -= n;
		bytes_read += n;
	}

        *out_actual = bytes_read;
        return 0;
}
示例#9
0
void
nfsload(length)
{
	int err, read_size;

	while (length > 0) {
		read_size = length > NFS_READ_SIZE ?
				NFS_READ_SIZE : length;
		if ((err = nfs_read(ARP_ROOTSERVER, root_nfs_port,
			&kernel_handle, offset, read_size, loadpoint)) !=
				read_size) {
			if (err < 0) {
				printf("Unable to read data: ");
				nfs_err(err);
			}
			longjmp(jmp_bootmenu, 1);
		}
		loadpoint += err;
		length -= err;
		offset += err;
	}
}
示例#10
0
static void lookup_callback (void *arg, struct nfs_client *client,
                            LOOKUP3res *result)
{
    LOOKUP3resok *resok = &result->LOOKUP3res_u.resok;
    err_t r;
    struct http_cache_entry *e = arg;

    DEBUGPRINT ("inside lookup_callback_file for file %s\n", e->name);
    assert(result != NULL );

    /* initiate a read for every regular file */
    if ( result->status == NFS3_OK &&
        resok->obj_attributes.attributes_follow &&
        resok->obj_attributes.post_op_attr_u.attributes.type == NF3REG) {

    	/* FIXME: check if the file is recently modified or not. */
		// resok->obj_attributes.post_op_attr_u.attributes.ctime;


        DEBUGPRINT("Copying %s of size %lu\n", e->name,
                    resok->obj_attributes.post_op_attr_u.attributes.size );

        /* Store the nfs directory handle in current_session (cs) */
        nfs_copyfh (&e->file_handle, resok->object);
        /* GLOBAL: Storing the global reference for cache entry */
        /* NOTE: this memory is freed in reset_cache_entry() */

        /* Allocate memory for holding the file-content */
        /* Allocate the buff_holder */
        e->hbuff = allocate_buff_holder(
            resok->obj_attributes.post_op_attr_u.attributes.size );
        /* NOTE: this memory will be freed by decrement_buff_holder_ref */

        /* increment buff_holder reference */
        increment_buff_holder_ref (e->hbuff);

        e->hbuff->len = resok->obj_attributes.post_op_attr_u.attributes.size;

        /* Set the size of the how much data is read till now. */
        e->copied = 0;

        r = nfs_read (client, e->file_handle, 0, MAX_NFS_READ,
                read_callback, e);
        assert (r == ERR_OK);

        // free arguments
        xdr_LOOKUP3res(&xdr_free, result);
        return;
    }

    /* Most probably the file does not exist */
    DEBUGPRINT ("Error: file [%s] does not exist, or wrong type\n", e->name);

#ifdef PRELOAD_WEB_CACHE
    if (cache_loading_phase){
    	++cache_loading_probs;
    	handle_cache_load_done();
    	return;
    }
#endif // PRELOAD_WEB_CACHE

    if (e->conn == NULL) {
    	/* No connection is waiting for this loading */
    	return;
    }

    /*	as file does not exist, send all the http_conns to error page. */
	error_cache->conn = e->conn;
	error_cache->last = e->last;
	handle_pending_list (error_cache); /* done! */

    /* free this cache entry as it is pointing to invalid page */
    e->conn = NULL;
    e->last = NULL;
    delete_cacheline_from_cachelist (e);
    if (e->name != NULL ) free(e->name);
    free(e);

    // free arguments
    xdr_LOOKUP3res(&xdr_free, result);
    return;
} /* end function: lookup_callback_file */
示例#11
0
/**************************************************************************
LOAD - Try to get booted
**************************************************************************/
load()
{
	char	*p,*q;
	char	cfg[64];
	int	root_mount_port;
	int	swap_nfs_port;
	int	swap_mount_port;
	char	cmd_line[80];
	int	err, read_size, i;
	long	addr, broadcast;
	int	swsize;
	unsigned long pad;

	config_buffer[0]='\0'; /* clear; bootp might fill this up */
/* Initialize this early on */

        nfsdiskless.root_args.rsize = 8192;
        nfsdiskless.root_args.wsize = 8192;
        nfsdiskless.swap_args.rsize = 8192;
        nfsdiskless.swap_args.wsize = 8192;
        nfsdiskless.root_args.sotype = SOCK_DGRAM;
        nfsdiskless.root_args.flags = (NFSMNT_WSIZE | NFSMNT_RSIZE | 
				       NFSMNT_RESVPORT);
        nfsdiskless.swap_args.sotype = SOCK_DGRAM;
        nfsdiskless.swap_args.flags = (NFSMNT_WSIZE | NFSMNT_RSIZE | 
				       NFSMNT_RESVPORT);


		/* Find a server to get BOOTP reply from */
	if (!arptable[ARP_CLIENT].ipaddr || !arptable[ARP_SERVER].ipaddr) {
		printf("\nSearching for server...\n");
		if (!bootp()) {
			printf("No Server found.\n");
			longjmp(jmp_bootmenu,1);
		}
	}
	printf("My IP %I, Server IP %I, GW IP %I\n",
		arptable[ARP_CLIENT].ipaddr,
		arptable[ARP_SERVER].ipaddr,
		arptable[ARP_GATEWAY].ipaddr);

#ifdef MDEBUG
	printf("\n=>>"); getchar();
#endif

	/*** check if have got info from bootp ***/
	if (config_buffer[0])
		goto cfg_done;
#ifndef NO_TFTP
	/* Now use TFTP to load configuration file */
	sprintf(cfg,"/tftpboot/freebsd.%I",arptable[ARP_CLIENT].ipaddr);
	if (tftp(cfg) || tftp(cfg+10))
		goto cfg_done;
	cfg[17]='\0';
	if (tftp(cfg) || tftp(cfg+10))
		goto cfg_done;
	sprintf(cfg,"/tftpboot/cfg.%I",arptable[ARP_CLIENT].ipaddr);
	if (tftp(cfg) || tftp(cfg+10))
		goto cfg_done;
#endif
	/* not found; using default values... */
	sprintf(config_buffer,"rootfs %I:/usr/diskless_root",
		arptable[ARP_SERVER].ipaddr);
	printf("Unable to load config file, guessing:\n\t%s\n",
		config_buffer);

cfg_done:
#ifdef MDEBUG
	printf("\n=>>"); getchar();
#endif

	p = config_buffer;
	while(*p) {
		q = cmd_line;
		while ((*p != '\n') && (*p)) *(q++) = *(p++);
		*q = 0;
		printf("%s\n",cmd_line);
		execute(cmd_line);
		if (*p) p++;
	}

#ifdef MDEBUG
	printf("\n=>>"); getchar();
#endif

		/* Check to make sure we've got a rootfs */
	if (!arptable[ARP_ROOTSERVER].ipaddr) {
		printf("No ROOT filesystem server!\n");
		longjmp(jmp_bootmenu,1);
	}

		/* Fill in nfsdiskless.myif */
	sprintf(&nfsdiskless.myif.ifra_name,eth_driver);
        nfsdiskless.myif.ifra_addr.sa_len = sizeof(struct sockaddr);
        nfsdiskless.myif.ifra_addr.sa_family = AF_INET;
	addr = htonl(arptable[ARP_CLIENT].ipaddr);
	bcopy(&addr, &nfsdiskless.myif.ifra_addr.sa_data[2], 4);
	broadcast = (addr & netmask) | ~netmask;
	nfsdiskless.myif.ifra_broadaddr.sa_len = sizeof(struct sockaddr);
	nfsdiskless.myif.ifra_broadaddr.sa_family = AF_INET;
	bcopy(&broadcast, &nfsdiskless.myif.ifra_broadaddr.sa_data[2], 4);
	addr = htonl(arptable[ARP_GATEWAY].ipaddr);
	if (addr) {
		nfsdiskless.mygateway.sin_len = sizeof(struct sockaddr);
		nfsdiskless.mygateway.sin_family = AF_INET;
		bcopy(&addr, &nfsdiskless.mygateway.sin_addr, 4);
	} else {
		nfsdiskless.mygateway.sin_len = 0;
	}
	nfsdiskless.myif.ifra_mask.sa_len = sizeof(struct sockaddr);
	nfsdiskless.myif.ifra_mask.sa_family = AF_UNSPEC;
	bcopy(&netmask, &nfsdiskless.myif.ifra_mask.sa_data[2], 4);

	rpc_id = currticks();

		/* Lookup NFS/MOUNTD ports for SWAP using PORTMAP */
	if (arptable[ARP_SWAPSERVER].ipaddr) {
		char swapfs_fh[32], swapfile[32];
		swap_nfs_port = rpclookup(ARP_SWAPSERVER, PROG_NFS, 2);
		swap_mount_port = rpclookup(ARP_SWAPSERVER, PROG_MOUNT, 1);
		if ((swap_nfs_port == -1) || (swap_mount_port == -1)) {
			printf("Unable to get SWAP NFS/MOUNT ports\n");
			longjmp(jmp_bootmenu,1);
		}
		if (err = nfs_mount(ARP_SWAPSERVER, swap_mount_port,
			nfsdiskless.swap_hostnam, &swapfs_fh)) {
			printf("Unable to mount SWAP filesystem: ");
			nfs_err(err);
			longjmp(jmp_bootmenu,1);
		}
		sprintf(swapfile,"swap.%I",arptable[ARP_CLIENT].ipaddr);
		if (err = nfs_lookup(ARP_SWAPSERVER, swap_nfs_port,
			&swapfs_fh, swapfile, &nfsdiskless.swap_fh, &swsize)) {
			printf("Unable to open %s: ",swapfile);
			nfs_err(err);
			longjmp(jmp_bootmenu,1);
		}
		if (!nfsdiskless.swap_nblks) {
		  nfsdiskless.swap_nblks = swsize / 1024;
		  printf("Swap size is: %d blocks\n",nfsdiskless.swap_nblks);
		}
		nfsdiskless.swap_saddr.sin_len = sizeof(struct sockaddr_in);
		nfsdiskless.swap_saddr.sin_family = AF_INET;
		nfsdiskless.swap_saddr.sin_port = htons(swap_nfs_port);
		nfsdiskless.swap_saddr.sin_addr.s_addr =
			htonl(arptable[ARP_SWAPSERVER].ipaddr);
        	nfsdiskless.swap_args.timeo = 10;
        	nfsdiskless.swap_args.retrans = 100;
	}

		/* Lookup NFS/MOUNTD ports for ROOT using PORTMAP */
	root_nfs_port = rpclookup(ARP_ROOTSERVER, PROG_NFS, 2);
	root_mount_port = rpclookup(ARP_ROOTSERVER, PROG_MOUNT, 1);
	if ((root_nfs_port == -1) || (root_mount_port == -1)) {
		printf("Unable to get ROOT NFS/MOUNT ports\n");
		longjmp(jmp_bootmenu,1);
	}
	if (err = nfs_mount(ARP_ROOTSERVER, root_mount_port,
		nfsdiskless.root_hostnam, &nfsdiskless.root_fh)) {
		printf("Unable to mount ROOT filesystem: ");
		nfs_err(err);
		longjmp(jmp_bootmenu,1);
	}
	nfsdiskless.root_saddr.sin_len = sizeof(struct sockaddr_in);
	nfsdiskless.root_saddr.sin_family = AF_INET;
	nfsdiskless.root_saddr.sin_port = htons(root_nfs_port);
	nfsdiskless.root_saddr.sin_addr.s_addr =
		htonl(arptable[ARP_ROOTSERVER].ipaddr);
        nfsdiskless.root_args.timeo = 10;
        nfsdiskless.root_args.retrans = 100;
	nfsdiskless.root_time = 0;

	if (err = nfs_lookup(ARP_ROOTSERVER, root_nfs_port,
		&nfsdiskless.root_fh, *kernel == '/' ? kernel+1 : kernel,
		&kernel_handle, NULL)) {
		printf("Unable to open %s: ",kernel);
		nfs_err(err);
		longjmp(jmp_bootmenu,1);
	}

		/* Load the kernel using NFS */
	printf("Loading %s...\n",kernel);
	if ((err = nfs_read(ARP_ROOTSERVER, root_nfs_port, &kernel_handle, 0,
		sizeof(struct exec), &head)) < 0) {
		printf("Unable to read %s: ",kernel);
		nfs_err(err);
		longjmp(jmp_bootmenu,1);
	}
	if (N_BADMAG(head)) {
		printf("Bad executable format!\n");
		longjmp(jmp_bootmenu, 1);
	}
	loadpoint = (char *)(head.a_entry & 0x00FFFFFF);
	offset = N_TXTOFF(head);
	printf("text=0x%X, ",head.a_text);
#ifdef	PC98
	set_twiddle_max(8);
#endif
	nfsload(head.a_text);
	while (((int)loadpoint) & PAGE_MASK)
		*(loadpoint++) = 0;

	printf("data=0x%X, ",head.a_data);
	nfsload(head.a_data);

	printf("bss=0x%X, ",head.a_bss);
	while(head.a_bss--) *(loadpoint++) = 0;

	while (((int)loadpoint) & PAGE_MASK)
		*(loadpoint++) = 0;

	bootinfo.bi_symtab = (int) loadpoint;

	p = (char*)&head.a_syms;
	for (i=0;i<sizeof(head.a_syms);i++)
		*loadpoint++ = *p++;

	printf("symbols=[+0x%x+0x%x", sizeof(head.a_syms), head.a_syms);
	
	nfsload(head.a_syms);
	i = sizeof(int);
	p = loadpoint;
	nfsload(i);
	i = *(int*)p;
	printf("+0x%x]\n", i);
	i -= sizeof(int);
	nfsload(i);
	bootinfo.bi_esymtab = (int) loadpoint;

	printf("entry=0x%X.\n",head.a_entry);

		/* Jump to kernel */
	bootinfo.bi_version = BOOTINFO_VERSION;
	bootinfo.bi_kernelname = kernel;
	bootinfo.bi_nfs_diskless = &nfsdiskless;
	bootinfo.bi_size = sizeof bootinfo;
	kernelentry = (void *)(head.a_entry & 0x00FFFFFF);
	(*kernelentry)(howto|RB_BOOTINFO,NODEV,0,0,0,&bootinfo,0,0,0);
	printf("*** %s execute failure ***\n",kernel);
}
示例#12
0
/**************************************************************************
NFS - Download extended BOOTP data, or kernel image from NFS server
**************************************************************************/
int nfs(const char *name, int (*fnc)(unsigned char *, unsigned int, unsigned int, int))
{
	static int recursion = 0;
	int sport;
	int err, namelen = strlen(name);
	char dirname[300], *fname;
	char dirfh[NFS_FHSIZE];		/* file handle of directory */
	char filefh[NFS_FHSIZE];	/* file handle of kernel image */
	unsigned int block;
	int rlen, size, offs, len;
	struct rpc_t *rpc;

	rx_qdrain();

	sport = oport++;
	if (oport > START_OPORT+OPORT_SWEEP) {
		oport = START_OPORT;
	}
	if ( name != dirname ) {
		memcpy(dirname, name, namelen + 1);
	}
	recursion = 0;
nfssymlink:
	if ( recursion > NFS_MAXLINKDEPTH ) {
		printf ( "\nRecursion: More than %d symlinks followed. Abort.\n", NFS_MAXLINKDEPTH );
		return	0;
	}
	recursion++;
	fname = dirname + (namelen - 1);
	while (fname >= dirname) {
		if (*fname == '/') {
			*fname = '\0';
			fname++;
			break;
		}
		fname--;
	}
	if (fname < dirname) {
		printf("can't parse file name %s\n", name);
		return 0;
	}

	if (mount_port == -1) {
		mount_port = rpc_lookup(ARP_SERVER, PROG_MOUNT, 1, sport);
	}
	if (nfs_port == -1) {
		nfs_port = rpc_lookup(ARP_SERVER, PROG_NFS, 2, sport);
	}
	if (nfs_port == -1 || mount_port == -1) {
		printf("can't get nfs/mount ports from portmapper\n");
		return 0;
	}


	err = nfs_mount(ARP_SERVER, mount_port, dirname, dirfh, sport);
	if (err) {
		printf("mounting %s: ", dirname);
		nfs_printerror(err);
		/* just to be sure... */
		nfs_umountall(ARP_SERVER);
		return 0;
	}

	err = nfs_lookup(ARP_SERVER, nfs_port, dirfh, fname, filefh, sport);
	if (err) {
		printf("looking up %s: ", fname);
		nfs_printerror(err);
		nfs_umountall(ARP_SERVER);
		return 0;
	}

	offs = 0;
	block = 1;	/* blocks are numbered starting from 1 */
	size = -1;	/* will be set properly with the first reply */
	len = NFS_READ_SIZE;	/* first request is always full size */
	do {
		err = nfs_read(ARP_SERVER, nfs_port, filefh, offs, len, sport);
                if ((err <= -NFSERR_ISDIR)&&(err >= -NFSERR_INVAL) && (offs == 0)) {
			// An error occured. NFS servers tend to sending
			// errors 21 / 22 when symlink instead of real file
			// is requested. So check if it's a symlink!
			block = nfs_readlink(ARP_SERVER, nfs_port, dirfh, dirname,
			                filefh, sport);
			if ( 0 == block ) {
				printf("\nLoading symlink:%s ..",dirname);
				goto nfssymlink;
			}
			nfs_printerror(err);
			nfs_umountall(ARP_SERVER);
			return 0;
		}
		if (err) {
			printf("reading at offset %d: ", offs);
			nfs_printerror(err);
			nfs_umountall(ARP_SERVER);
			return 0;
		}

		rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];

		/* size must be found out early to allow EOF detection */
		if (size == -1) {
			size = ntohl(rpc->u.reply.data[6]);
		}
		rlen = ntohl(rpc->u.reply.data[18]);
		if (rlen > len) {
			rlen = len;	/* shouldn't happen...  */
		}

		err = fnc((char *)&rpc->u.reply.data[19], block, rlen,
			(offs+rlen == size));
		if (err <= 0) {
			nfs_umountall(ARP_SERVER);
			return err;
		}

		block++;
		offs += rlen;
		/* last request is done with matching requested read size */
		if (size-offs < NFS_READ_SIZE) {
			len = size-offs;
		}
	} while (len != 0);
	/* len == 0 means that all the file has been read */
	return 1;
}