214 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			214 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/* VTreeFS - read.c - by Alen Stojanov and David van Moolenbroek */
 | 
						|
 | 
						|
#include "inc.h"
 | 
						|
#include <dirent.h>
 | 
						|
#include <minix/minlib.h>
 | 
						|
 | 
						|
#define GETDENTS_BUFSIZ 4096
 | 
						|
#define DWORD_ALIGN(len) (((len) + sizeof(long) - 1) & ~(sizeof(long) - 1))
 | 
						|
 | 
						|
/*===========================================================================*
 | 
						|
 *				fs_read					     *
 | 
						|
 *===========================================================================*/
 | 
						|
int fs_read(void)
 | 
						|
{
 | 
						|
	/* Read from a file.
 | 
						|
	 */
 | 
						|
	cp_grant_id_t gid;
 | 
						|
	struct inode *node;
 | 
						|
	off_t pos;
 | 
						|
	size_t len;
 | 
						|
	char *ptr;
 | 
						|
	int r;
 | 
						|
 | 
						|
	/* Try to get inode by to its inode number. */
 | 
						|
	if ((node = find_inode(fs_m_in.REQ_INODE_NR)) == NULL)
 | 
						|
		return EINVAL;
 | 
						|
 | 
						|
	/* Check whether the node is a regular file. */
 | 
						|
	if (!S_ISREG(node->i_stat.mode))
 | 
						|
		return EINVAL;
 | 
						|
 | 
						|
	/* Get the values from the request message. */
 | 
						|
	gid = fs_m_in.REQ_GRANT;
 | 
						|
	pos = fs_m_in.REQ_SEEK_POS;
 | 
						|
 | 
						|
	/* Call the read hook, if any. */
 | 
						|
	if (!is_inode_deleted(node) && vtreefs_hooks->read_hook != NULL) {
 | 
						|
		len = fs_m_in.REQ_NBYTES;
 | 
						|
 | 
						|
		/* On success, the read hook provides us with a pointer to the
 | 
						|
		 * resulting data. This avoids copying overhead.
 | 
						|
		 */
 | 
						|
		r = vtreefs_hooks->read_hook(node, pos, &ptr, &len,
 | 
						|
			get_inode_cbdata(node));
 | 
						|
 | 
						|
		assert(len <= fs_m_in.REQ_NBYTES);
 | 
						|
 | 
						|
		/* Copy the resulting data to user space. */
 | 
						|
		if (r == OK && len > 0) {
 | 
						|
			r = sys_safecopyto(fs_m_in.m_source, fs_m_in.REQ_GRANT,
 | 
						|
				0, (vir_bytes) ptr, len);
 | 
						|
		}
 | 
						|
	} else {
 | 
						|
		/* Feign an empty file. */
 | 
						|
		r = OK;
 | 
						|
		len = 0;
 | 
						|
	}
 | 
						|
 | 
						|
	if (r == OK) {
 | 
						|
		fs_m_out.RES_SEEK_POS = pos + len;
 | 
						|
		fs_m_out.RES_NBYTES = len;
 | 
						|
	}
 | 
						|
 | 
						|
	return r;
 | 
						|
}
 | 
						|
 | 
						|
/*===========================================================================*
 | 
						|
 *				fs_getdents				     *
 | 
						|
 *===========================================================================*/
 | 
						|
int fs_getdents(void)
 | 
						|
{
 | 
						|
	/* Retrieve directory entries.
 | 
						|
	 */
 | 
						|
	struct inode *node, *child = NULL;
 | 
						|
	struct dirent *dent;
 | 
						|
	char *name;
 | 
						|
	size_t len, off, user_off, user_left;
 | 
						|
	off_t pos;
 | 
						|
	int r, skip, get_next, indexed;
 | 
						|
	static char buf[GETDENTS_BUFSIZ];
 | 
						|
 | 
						|
	if (fs_m_in.REQ_SEEK_POS >= ULONG_MAX)
 | 
						|
		return EIO;
 | 
						|
 | 
						|
	if ((node = find_inode(fs_m_in.REQ_INODE_NR)) == NULL)
 | 
						|
		return EINVAL;
 | 
						|
 | 
						|
	off = 0;
 | 
						|
	user_off = 0;
 | 
						|
	user_left = fs_m_in.REQ_MEM_SIZE;
 | 
						|
	indexed = node->i_indexed;
 | 
						|
	get_next = FALSE;
 | 
						|
	child = NULL;
 | 
						|
 | 
						|
	/* Call the getdents hook, if any, to "refresh" the directory. */
 | 
						|
	if (!is_inode_deleted(node) && vtreefs_hooks->getdents_hook != NULL) {
 | 
						|
		r = vtreefs_hooks->getdents_hook(node, get_inode_cbdata(node));
 | 
						|
		if (r != OK) return r;
 | 
						|
	}
 | 
						|
 | 
						|
	for (pos = fs_m_in.REQ_SEEK_POS; ; pos++) {
 | 
						|
		/* Determine which inode and name to use for this entry. */
 | 
						|
		if (pos == 0) {
 | 
						|
			/* The "." entry. */
 | 
						|
			child = node;
 | 
						|
			name = ".";
 | 
						|
		}
 | 
						|
		else if (pos == 1) {
 | 
						|
			/* The ".." entry. */
 | 
						|
			child = get_parent_inode(node);
 | 
						|
			if (child == NULL)
 | 
						|
				child = node;
 | 
						|
			name = "..";
 | 
						|
		}
 | 
						|
		else if (pos - 2 < indexed) {
 | 
						|
			/* All indexed entries. */
 | 
						|
			child = get_inode_by_index(node, pos - 2);
 | 
						|
 | 
						|
			/* If there is no inode with this particular index,
 | 
						|
			 * continue with the next index number.
 | 
						|
			 */
 | 
						|
			if (child == NULL) continue;
 | 
						|
 | 
						|
			name = child->i_name;
 | 
						|
		}
 | 
						|
		else {
 | 
						|
			/* All non-indexed entries. */
 | 
						|
 | 
						|
			/* If this is the first loop iteration, first get to
 | 
						|
			 * the non-indexed child identified by the current
 | 
						|
			 * position.
 | 
						|
			 */
 | 
						|
			if (get_next == FALSE) {
 | 
						|
				skip = pos - indexed - 2;
 | 
						|
				child = get_first_inode(node);
 | 
						|
 | 
						|
				/* Skip indexed children. */
 | 
						|
				while (child != NULL &&
 | 
						|
						child->i_index != NO_INDEX)
 | 
						|
					child = get_next_inode(child);
 | 
						|
 | 
						|
				/* Skip to the right position. */
 | 
						|
				while (child != NULL && skip-- > 0)
 | 
						|
					child = get_next_inode(child);
 | 
						|
 | 
						|
				get_next = TRUE;
 | 
						|
			}
 | 
						|
			else {
 | 
						|
				child = get_next_inode(child);
 | 
						|
			}
 | 
						|
 | 
						|
			/* No more children? Then stop. */
 | 
						|
			if (child == NULL)
 | 
						|
				break;
 | 
						|
 | 
						|
			assert(!is_inode_deleted(child));
 | 
						|
 | 
						|
			name = child->i_name;
 | 
						|
		}
 | 
						|
 | 
						|
		/* record length incl. alignment. */
 | 
						|
                len = _DIRENT_RECLEN(dent, strlen(name));
 | 
						|
 | 
						|
		/* Is the user buffer too small to store another record? */
 | 
						|
		if (user_off + off + len > user_left) {
 | 
						|
			/* Is the user buffer too small for even a single
 | 
						|
			 * record?
 | 
						|
			 */
 | 
						|
			if (user_off == 0 && off == 0)
 | 
						|
				return EINVAL;
 | 
						|
 | 
						|
			break;
 | 
						|
		}
 | 
						|
 | 
						|
		/* If our own buffer cannot contain the new record, copy out
 | 
						|
		 * first.
 | 
						|
		 */
 | 
						|
		if (off + len > sizeof(buf)) {
 | 
						|
			r = sys_safecopyto(fs_m_in.m_source, fs_m_in.REQ_GRANT,
 | 
						|
				user_off, (vir_bytes) buf, off);
 | 
						|
			if (r != OK) return r;
 | 
						|
 | 
						|
			user_off += off;
 | 
						|
			user_left -= off;
 | 
						|
			off = 0;
 | 
						|
		}
 | 
						|
 | 
						|
		/* Fill in the actual directory entry. */
 | 
						|
		dent = (struct dirent *) &buf[off];
 | 
						|
		dent->d_ino = (ino_t) get_inode_number(child);
 | 
						|
		dent->d_reclen = len;
 | 
						|
		dent->d_type = fs_mode_to_type(child->i_stat.mode);
 | 
						|
		dent->d_namlen = strlen(name);
 | 
						|
		strcpy(dent->d_name, name);
 | 
						|
 | 
						|
		off += len;
 | 
						|
	}
 | 
						|
 | 
						|
	/* If there is anything left in our own buffer, copy that out now. */
 | 
						|
	if (off > 0) {
 | 
						|
		r = sys_safecopyto(fs_m_in.m_source, fs_m_in.REQ_GRANT,
 | 
						|
			user_off, (vir_bytes) buf, off);
 | 
						|
		if (r != OK)
 | 
						|
			return r;
 | 
						|
 | 
						|
		user_off += off;
 | 
						|
	}
 | 
						|
 | 
						|
	fs_m_out.RES_SEEK_POS = pos;
 | 
						|
	fs_m_out.RES_NBYTES = user_off;
 | 
						|
 | 
						|
	return OK;
 | 
						|
}
 |