minix/lib/libsffs/read.c
Lionel Sambuc 56350a991b Message types for VFS read, write & peek
All of these requests share the same message type as at least one server
manages those requests in the same handler, just by checking the actual
type of the request, and then acting upon it.

Change-Id: I17337b4c67ae209523574c22ccc108cf5f1e65e9
2014-07-28 17:05:29 +02:00

236 lines
5.3 KiB
C

/* This file contains file and directory reading file system call handlers.
*
* The entry points into this file are:
* do_read perform the READ file system call
* do_getdents perform the GETDENTS file system call
*
* Created:
* April 2009 (D.C. van Moolenbroek)
*/
#include "inc.h"
#include <dirent.h>
#define DWORD_ALIGN(len) (((len) + sizeof(long) - 1) & ~(sizeof(long) - 1))
/*===========================================================================*
* do_read *
*===========================================================================*/
int do_read(void)
{
/* Read data from a file.
*/
struct inode *ino;
off_t pos;
size_t count, size;
vir_bytes off;
char *ptr;
int r, chunk;
if ((ino = find_inode(m_in.m_vfs_fs_readwrite.inode)) == NULL)
return EINVAL;
if (IS_DIR(ino)) return EISDIR;
if ((r = get_handle(ino)) != OK)
return r;
pos = m_in.m_vfs_fs_readwrite.seek_pos;
count = m_in.m_vfs_fs_readwrite.nbytes;
assert(count > 0);
/* Use the buffer from below to eliminate extra copying. */
size = sffs_table->t_readbuf(&ptr);
off = 0;
while (count > 0) {
chunk = MIN(count, size);
if ((r = sffs_table->t_read(ino->i_file, ptr, chunk, pos)) <= 0)
break;
chunk = r;
r = sys_safecopyto(m_in.m_source, m_in.m_vfs_fs_readwrite.grant, off,
(vir_bytes) ptr, chunk);
if (r != OK)
break;
count -= chunk;
off += chunk;
pos += chunk;
}
if (r < 0)
return r;
m_out.m_fs_vfs_readwrite.seek_pos = pos;
m_out.m_fs_vfs_readwrite.nbytes = off;
return OK;
}
/*===========================================================================*
* do_getdents *
*===========================================================================*/
int do_getdents(void)
{
/* Retrieve directory entries.
*/
char name[NAME_MAX+1];
struct inode *ino, *child;
struct dirent *dent;
struct sffs_attr attr;
size_t len, off, user_off, user_left;
off_t pos;
int r, namelen;
/* must be at least sizeof(struct dirent) + NAME_MAX */
static char buf[BLOCK_SIZE];
attr.a_mask = SFFS_ATTR_MODE;
if ((ino = find_inode(m_in.m_vfs_fs_getdents.inode)) == NULL)
return EINVAL;
if(m_in.m_vfs_fs_getdents.seek_pos >= ULONG_MAX) return EINVAL;
if (!IS_DIR(ino)) return ENOTDIR;
/* We are going to need at least one free inode to store children in. */
if (!have_free_inode()) return ENFILE;
/* If we don't have a directory handle yet, get one now. */
if ((r = get_handle(ino)) != OK)
return r;
off = 0;
user_off = 0;
user_left = m_in.m_vfs_fs_getdents.mem_size;
/* We use the seek position as file index number. The first position is for
* the "." entry, the second position is for the ".." entry, and the next
* position numbers each represent a file in the directory.
*/
for (pos = m_in.m_vfs_fs_getdents.seek_pos; ; pos++) {
/* Determine which inode and name to use for this entry.
* We have no idea whether the host will give us "." and/or "..",
* so generate our own and skip those from the host.
*/
if (pos == 0) {
/* Entry for ".". */
child = ino;
strcpy(name, ".");
get_inode(child);
}
else if (pos == 1) {
/* Entry for "..", but only when there is a parent. */
if (ino->i_parent == NULL)
continue;
child = ino->i_parent;
strcpy(name, "..");
get_inode(child);
}
else {
/* Any other entry, not being "." or "..". */
r = sffs_table->t_readdir(ino->i_dir, pos - 2, name,
sizeof(name), &attr);
if (r != OK) {
/* No more entries? Then close the handle and stop. */
if (r == ENOENT) {
put_handle(ino);
break;
}
/* FIXME: what if the error is ENAMETOOLONG? */
return r;
}
if (!strcmp(name, ".") || !strcmp(name, ".."))
continue;
if ((child = lookup_dentry(ino, name)) == NULL) {
child = get_free_inode();
/* We were promised a free inode! */
assert(child != NULL);
child->i_flags = MODE_TO_DIRFLAG(attr.a_mode);
add_dentry(ino, name, child);
}
}
/* record length incl. alignment. */
namelen = strlen(name);
len = _DIRENT_RECLEN(dent, namelen);
/* Is the user buffer too small to store another record?
* Note that we will be rerequesting the same dentry upon a subsequent
* getdents call this way, but we really need the name length for this.
*/
if (user_off + off + len > user_left) {
put_inode(child);
/* 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(m_in.m_source, m_in.m_vfs_fs_getdents.grant,
user_off, (vir_bytes) buf, off);
if (r != OK) {
put_inode(child);
return r;
}
user_off += off;
user_left -= off;
off = 0;
}
/* Fill in the actual directory entry. */
dent = (struct dirent *) &buf[off];
dent->d_ino = INODE_NR(child);
dent->d_reclen = len;
dent->d_namlen = namelen;
dent->d_type = IS_DIR(child) ? DT_DIR : DT_REG;
strcpy(dent->d_name, name);
off += len;
put_inode(child);
}
/* If there is anything left in our own buffer, copy that out now. */
if (off > 0) {
r = sys_safecopyto(m_in.m_source, m_in.m_vfs_fs_getdents.grant, user_off,
(vir_bytes) buf, off);
if (r != OK)
return r;
user_off += off;
}
m_out.m_fs_vfs_getdents.seek_pos = pos;
m_out.m_fs_vfs_getdents.nbytes = user_off;
return OK;
}