minix/lib/libvtreefs/read.c
David van Moolenbroek cc810ee4d9 VFS/FS: replace protocol version with flag field
The main motivation for this change is that only Loris supports
multithreading, and Loris supports dynamic thread allocation, so the
number of supported threads can be implemented as a bit flag (i.e.,
either 1 or "at least as many as VFS has"). The ABI break obviates the
need to support file system versioning at this time, and several
other aspects are better implemented as flags as well. Other changes:

- replace peek/bpeek test upon mount with FS flag as well;
- mark libsffs as 64-bit file size capable;
- remove old (3.2.1) getdents support.

Change-Id: I313eace9c50ed816656c31cd47d969033d952a03
2014-02-18 11:25:02 +01:00

216 lines
5 KiB
C

/* VTreeFS - read.c - by Alen Stojanov and David van Moolenbroek */
#include "inc.h"
#include <dirent.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;
if (fs_m_in.REQ_SEEK_POS_HI != 0)
return EIO;
/* 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_LO;
/* 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_HI = 0;
fs_m_out.RES_SEEK_POS_LO = 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_HI != 0)
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_LO; ; 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;
}
len = DWORD_ALIGN(sizeof(struct dirent) + 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_off = (off_t) pos;
dent->d_reclen = len;
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_HI = 0;
fs_m_out.RES_SEEK_POS_LO = pos;
fs_m_out.RES_NBYTES = user_off;
return OK;
}