Subversion Repositories HelenOS

Compare Revisions

Ignore whitespace Rev 4684 → Rev 4687

/tags/0.4.1/uspace/srv/vfs/vfs_ops.c
0,0 → 1,1315
/*
* Copyright (c) 2008 Jakub Jermar
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* - The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
 
/** @addtogroup fs
* @{
*/
 
/**
* @file vfs_ops.c
* @brief Operations that VFS offers to its clients.
*/
 
#include "vfs.h"
#include <ipc/ipc.h>
#include <async.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <bool.h>
#include <fibril_sync.h>
#include <adt/list.h>
#include <unistd.h>
#include <ctype.h>
#include <fcntl.h>
#include <assert.h>
#include <vfs/canonify.h>
 
/* Forward declarations of static functions. */
static int vfs_truncate_internal(fs_handle_t, dev_handle_t, fs_index_t, size_t);
 
/**
* This rwlock prevents the race between a triplet-to-VFS-node resolution and a
* concurrent VFS operation which modifies the file system namespace.
*/
FIBRIL_RWLOCK_INITIALIZE(namespace_rwlock);
 
vfs_pair_t rootfs = {
.fs_handle = 0,
.dev_handle = 0
};
 
static void vfs_mount_internal(ipc_callid_t rid, dev_handle_t dev_handle,
fs_handle_t fs_handle, char *mp, char *opts)
{
vfs_lookup_res_t mp_res;
vfs_lookup_res_t mr_res;
vfs_node_t *mp_node = NULL;
vfs_node_t *mr_node;
fs_index_t rindex;
size_t rsize;
unsigned rlnkcnt;
ipcarg_t rc;
int phone;
aid_t msg;
ipc_call_t answer;
/* Resolve the path to the mountpoint. */
fibril_rwlock_write_lock(&namespace_rwlock);
if (rootfs.fs_handle) {
/* We already have the root FS. */
if (str_cmp(mp, "/") == 0) {
/* Trying to mount root FS over root FS */
fibril_rwlock_write_unlock(&namespace_rwlock);
ipc_answer_0(rid, EBUSY);
return;
}
rc = vfs_lookup_internal(mp, L_DIRECTORY, &mp_res, NULL);
if (rc != EOK) {
/* The lookup failed for some reason. */
fibril_rwlock_write_unlock(&namespace_rwlock);
ipc_answer_0(rid, rc);
return;
}
mp_node = vfs_node_get(&mp_res);
if (!mp_node) {
fibril_rwlock_write_unlock(&namespace_rwlock);
ipc_answer_0(rid, ENOMEM);
return;
}
/*
* Now we hold a reference to mp_node.
* It will be dropped upon the corresponding VFS_IN_UNMOUNT.
* This prevents the mount point from being deleted.
*/
} else {
/* We still don't have the root file system mounted. */
if (str_cmp(mp, "/") == 0) {
/*
* For this simple, but important case,
* we are almost done.
*/
/* Tell the mountee that it is being mounted. */
phone = vfs_grab_phone(fs_handle);
msg = async_send_1(phone, VFS_OUT_MOUNTED,
(ipcarg_t) dev_handle, &answer);
/* send the mount options */
rc = ipc_data_write_start(phone, (void *)opts,
str_size(opts));
if (rc != EOK) {
async_wait_for(msg, NULL);
vfs_release_phone(phone);
fibril_rwlock_write_unlock(&namespace_rwlock);
ipc_answer_0(rid, rc);
return;
}
async_wait_for(msg, &rc);
vfs_release_phone(phone);
if (rc != EOK) {
fibril_rwlock_write_unlock(&namespace_rwlock);
ipc_answer_0(rid, rc);
return;
}
 
rindex = (fs_index_t) IPC_GET_ARG1(answer);
rsize = (size_t) IPC_GET_ARG2(answer);
rlnkcnt = (unsigned) IPC_GET_ARG3(answer);
mr_res.triplet.fs_handle = fs_handle;
mr_res.triplet.dev_handle = dev_handle;
mr_res.triplet.index = rindex;
mr_res.size = rsize;
mr_res.lnkcnt = rlnkcnt;
mr_res.type = VFS_NODE_DIRECTORY;
rootfs.fs_handle = fs_handle;
rootfs.dev_handle = dev_handle;
/* Add reference to the mounted root. */
mr_node = vfs_node_get(&mr_res);
assert(mr_node);
fibril_rwlock_write_unlock(&namespace_rwlock);
ipc_answer_0(rid, rc);
return;
} else {
/*
* We can't resolve this without the root filesystem
* being mounted first.
*/
fibril_rwlock_write_unlock(&namespace_rwlock);
ipc_answer_0(rid, ENOENT);
return;
}
}
/*
* At this point, we have all necessary pieces: file system and device
* handles, and we know the mount point VFS node.
*/
int mountee_phone = vfs_grab_phone(fs_handle);
assert(mountee_phone >= 0);
 
phone = vfs_grab_phone(mp_res.triplet.fs_handle);
msg = async_send_4(phone, VFS_OUT_MOUNT,
(ipcarg_t) mp_res.triplet.dev_handle,
(ipcarg_t) mp_res.triplet.index,
(ipcarg_t) fs_handle,
(ipcarg_t) dev_handle, &answer);
/* send connection */
rc = async_req_1_0(phone, IPC_M_CONNECTION_CLONE, mountee_phone);
if (rc != EOK) {
async_wait_for(msg, NULL);
vfs_release_phone(mountee_phone);
vfs_release_phone(phone);
/* Mount failed, drop reference to mp_node. */
if (mp_node)
vfs_node_put(mp_node);
ipc_answer_0(rid, rc);
fibril_rwlock_write_unlock(&namespace_rwlock);
return;
}
 
vfs_release_phone(mountee_phone);
/* send the mount options */
rc = ipc_data_write_start(phone, (void *)opts, str_size(opts));
if (rc != EOK) {
async_wait_for(msg, NULL);
vfs_release_phone(phone);
/* Mount failed, drop reference to mp_node. */
if (mp_node)
vfs_node_put(mp_node);
fibril_rwlock_write_unlock(&namespace_rwlock);
ipc_answer_0(rid, rc);
return;
}
async_wait_for(msg, &rc);
vfs_release_phone(phone);
if (rc == EOK) {
rindex = (fs_index_t) IPC_GET_ARG1(answer);
rsize = (size_t) IPC_GET_ARG2(answer);
rlnkcnt = (unsigned) IPC_GET_ARG3(answer);
mr_res.triplet.fs_handle = fs_handle;
mr_res.triplet.dev_handle = dev_handle;
mr_res.triplet.index = rindex;
mr_res.size = rsize;
mr_res.lnkcnt = rlnkcnt;
mr_res.type = VFS_NODE_DIRECTORY;
/* Add reference to the mounted root. */
mr_node = vfs_node_get(&mr_res);
assert(mr_node);
} else {
/* Mount failed, drop reference to mp_node. */
if (mp_node)
vfs_node_put(mp_node);
}
 
ipc_answer_0(rid, rc);
fibril_rwlock_write_unlock(&namespace_rwlock);
}
 
void vfs_mount(ipc_callid_t rid, ipc_call_t *request)
{
/*
* We expect the library to do the device-name to device-handle
* translation for us, thus the device handle will arrive as ARG1
* in the request.
*/
dev_handle_t dev_handle = (dev_handle_t) IPC_GET_ARG1(*request);
/*
* Mount flags are passed as ARG2.
*/
unsigned int flags = (unsigned int) IPC_GET_ARG2(*request);
/*
* For now, don't make use of ARG3, but it can be used to
* carry mount options in the future.
*/
/* We want the client to send us the mount point. */
ipc_callid_t callid;
size_t size;
if (!ipc_data_write_receive(&callid, &size)) {
ipc_answer_0(callid, EINVAL);
ipc_answer_0(rid, EINVAL);
return;
}
/* Check whether size is reasonable wrt. the mount point. */
if ((size < 1) || (size > MAX_PATH_LEN)) {
ipc_answer_0(callid, EINVAL);
ipc_answer_0(rid, EINVAL);
return;
}
/* Allocate buffer for the mount point data being received. */
char *mp = malloc(size + 1);
if (!mp) {
ipc_answer_0(callid, ENOMEM);
ipc_answer_0(rid, ENOMEM);
return;
}
/* Deliver the mount point. */
ipcarg_t retval = ipc_data_write_finalize(callid, mp, size);
if (retval != EOK) {
ipc_answer_0(rid, retval);
free(mp);
return;
}
mp[size] = '\0';
/* Now we expect to receive the mount options. */
if (!ipc_data_write_receive(&callid, &size)) {
ipc_answer_0(callid, EINVAL);
ipc_answer_0(rid, EINVAL);
free(mp);
return;
}
 
/* Check the offered options size. */
if (size > MAX_MNTOPTS_LEN) {
ipc_answer_0(callid, EINVAL);
ipc_answer_0(rid, EINVAL);
free(mp);
return;
}
 
/* Allocate buffer for the mount options. */
char *opts = (char *) malloc(size + 1);
if (!opts) {
ipc_answer_0(callid, ENOMEM);
ipc_answer_0(rid, ENOMEM);
free(mp);
return;
}
 
/* Deliver the mount options. */
retval = ipc_data_write_finalize(callid, opts, size);
if (retval != EOK) {
ipc_answer_0(rid, retval);
free(mp);
free(opts);
return;
}
opts[size] = '\0';
/*
* Now, we expect the client to send us data with the name of the file
* system.
*/
if (!ipc_data_write_receive(&callid, &size)) {
ipc_answer_0(callid, EINVAL);
ipc_answer_0(rid, EINVAL);
free(mp);
free(opts);
return;
}
/*
* Don't receive more than is necessary for storing a full file system
* name.
*/
if ((size < 1) || (size > FS_NAME_MAXLEN)) {
ipc_answer_0(callid, EINVAL);
ipc_answer_0(rid, EINVAL);
free(mp);
free(opts);
return;
}
/*
* Allocate buffer for file system name.
*/
char *fs_name = (char *) malloc(size + 1);
if (fs_name == NULL) {
ipc_answer_0(callid, ENOMEM);
ipc_answer_0(rid, ENOMEM);
free(mp);
free(opts);
return;
}
/* Deliver the file system name. */
retval = ipc_data_write_finalize(callid, fs_name, size);
if (retval != EOK) {
ipc_answer_0(rid, retval);
free(mp);
free(opts);
free(fs_name);
return;
}
fs_name[size] = '\0';
 
/*
* Wait for IPC_M_PING so that we can return an error if we don't know
* fs_name.
*/
ipc_call_t data;
callid = async_get_call(&data);
if (IPC_GET_METHOD(data) != IPC_M_PING) {
ipc_answer_0(callid, ENOTSUP);
ipc_answer_0(rid, ENOTSUP);
free(mp);
free(opts);
free(fs_name);
return;
}
 
/*
* Check if we know a file system with the same name as is in fs_name.
* This will also give us its file system handle.
*/
fibril_mutex_lock(&fs_head_lock);
fs_handle_t fs_handle;
recheck:
fs_handle = fs_name_to_handle(fs_name, false);
if (!fs_handle) {
if (flags & IPC_FLAG_BLOCKING) {
fibril_condvar_wait(&fs_head_cv, &fs_head_lock);
goto recheck;
}
fibril_mutex_unlock(&fs_head_lock);
ipc_answer_0(callid, ENOENT);
ipc_answer_0(rid, ENOENT);
free(mp);
free(fs_name);
free(opts);
return;
}
fibril_mutex_unlock(&fs_head_lock);
/* Acknowledge that we know fs_name. */
ipc_answer_0(callid, EOK);
/* Do the mount */
vfs_mount_internal(rid, dev_handle, fs_handle, mp, opts);
free(mp);
free(fs_name);
free(opts);
}
 
void vfs_open(ipc_callid_t rid, ipc_call_t *request)
{
if (!vfs_files_init()) {
ipc_answer_0(rid, ENOMEM);
return;
}
/*
* The POSIX interface is open(path, oflag, mode).
* We can receive oflags and mode along with the VFS_IN_OPEN call;
* the path will need to arrive in another call.
*
* We also receive one private, non-POSIX set of flags called lflag
* used to pass information to vfs_lookup_internal().
*/
int lflag = IPC_GET_ARG1(*request);
int oflag = IPC_GET_ARG2(*request);
int mode = IPC_GET_ARG3(*request);
size_t len;
 
/* Ignore mode for now. */
(void) mode;
/*
* Make sure that we are called with exactly one of L_FILE and
* L_DIRECTORY. Make sure that the user does not pass L_OPEN.
*/
if (((lflag & (L_FILE | L_DIRECTORY)) == 0) ||
((lflag & (L_FILE | L_DIRECTORY)) == (L_FILE | L_DIRECTORY)) ||
((lflag & L_OPEN) != 0)) {
ipc_answer_0(rid, EINVAL);
return;
}
if (oflag & O_CREAT)
lflag |= L_CREATE;
if (oflag & O_EXCL)
lflag |= L_EXCLUSIVE;
ipc_callid_t callid;
if (!ipc_data_write_receive(&callid, &len)) {
ipc_answer_0(callid, EINVAL);
ipc_answer_0(rid, EINVAL);
return;
}
char *path = malloc(len + 1);
if (!path) {
ipc_answer_0(callid, ENOMEM);
ipc_answer_0(rid, ENOMEM);
return;
}
int rc;
if ((rc = ipc_data_write_finalize(callid, path, len))) {
ipc_answer_0(rid, rc);
free(path);
return;
}
path[len] = '\0';
/*
* Avoid the race condition in which the file can be deleted before we
* find/create-and-lock the VFS node corresponding to the looked-up
* triplet.
*/
if (lflag & L_CREATE)
fibril_rwlock_write_lock(&namespace_rwlock);
else
fibril_rwlock_read_lock(&namespace_rwlock);
/* The path is now populated and we can call vfs_lookup_internal(). */
vfs_lookup_res_t lr;
rc = vfs_lookup_internal(path, lflag | L_OPEN, &lr, NULL);
if (rc != EOK) {
if (lflag & L_CREATE)
fibril_rwlock_write_unlock(&namespace_rwlock);
else
fibril_rwlock_read_unlock(&namespace_rwlock);
ipc_answer_0(rid, rc);
free(path);
return;
}
/* Path is no longer needed. */
free(path);
vfs_node_t *node = vfs_node_get(&lr);
if (lflag & L_CREATE)
fibril_rwlock_write_unlock(&namespace_rwlock);
else
fibril_rwlock_read_unlock(&namespace_rwlock);
/* Truncate the file if requested and if necessary. */
if (oflag & O_TRUNC) {
fibril_rwlock_write_lock(&node->contents_rwlock);
if (node->size) {
rc = vfs_truncate_internal(node->fs_handle,
node->dev_handle, node->index, 0);
if (rc) {
fibril_rwlock_write_unlock(&node->contents_rwlock);
vfs_node_put(node);
ipc_answer_0(rid, rc);
return;
}
node->size = 0;
}
fibril_rwlock_write_unlock(&node->contents_rwlock);
}
/*
* Get ourselves a file descriptor and the corresponding vfs_file_t
* structure.
*/
int fd = vfs_fd_alloc();
if (fd < 0) {
vfs_node_put(node);
ipc_answer_0(rid, fd);
return;
}
vfs_file_t *file = vfs_file_get(fd);
file->node = node;
if (oflag & O_APPEND)
file->append = true;
/*
* The following increase in reference count is for the fact that the
* file is being opened and that a file structure is pointing to it.
* It is necessary so that the file will not disappear when
* vfs_node_put() is called. The reference will be dropped by the
* respective VFS_IN_CLOSE.
*/
vfs_node_addref(node);
vfs_node_put(node);
/* Success! Return the new file descriptor to the client. */
ipc_answer_1(rid, EOK, fd);
}
 
void vfs_open_node(ipc_callid_t rid, ipc_call_t *request)
{
// FIXME: check for sanity of the supplied fs, dev and index
if (!vfs_files_init()) {
ipc_answer_0(rid, ENOMEM);
return;
}
/*
* The interface is open_node(fs, dev, index, oflag).
*/
vfs_lookup_res_t lr;
lr.triplet.fs_handle = IPC_GET_ARG1(*request);
lr.triplet.dev_handle = IPC_GET_ARG2(*request);
lr.triplet.index = IPC_GET_ARG3(*request);
int oflag = IPC_GET_ARG4(*request);
fibril_rwlock_read_lock(&namespace_rwlock);
int rc = vfs_open_node_internal(&lr);
if (rc != EOK) {
fibril_rwlock_read_unlock(&namespace_rwlock);
ipc_answer_0(rid, rc);
return;
}
vfs_node_t *node = vfs_node_get(&lr);
fibril_rwlock_read_unlock(&namespace_rwlock);
/* Truncate the file if requested and if necessary. */
if (oflag & O_TRUNC) {
fibril_rwlock_write_lock(&node->contents_rwlock);
if (node->size) {
rc = vfs_truncate_internal(node->fs_handle,
node->dev_handle, node->index, 0);
if (rc) {
fibril_rwlock_write_unlock(&node->contents_rwlock);
vfs_node_put(node);
ipc_answer_0(rid, rc);
return;
}
node->size = 0;
}
fibril_rwlock_write_unlock(&node->contents_rwlock);
}
/*
* Get ourselves a file descriptor and the corresponding vfs_file_t
* structure.
*/
int fd = vfs_fd_alloc();
if (fd < 0) {
vfs_node_put(node);
ipc_answer_0(rid, fd);
return;
}
vfs_file_t *file = vfs_file_get(fd);
file->node = node;
if (oflag & O_APPEND)
file->append = true;
/*
* The following increase in reference count is for the fact that the
* file is being opened and that a file structure is pointing to it.
* It is necessary so that the file will not disappear when
* vfs_node_put() is called. The reference will be dropped by the
* respective VFS_IN_CLOSE.
*/
vfs_node_addref(node);
vfs_node_put(node);
/* Success! Return the new file descriptor to the client. */
ipc_answer_1(rid, EOK, fd);
}
 
void vfs_sync(ipc_callid_t rid, ipc_call_t *request)
{
int fd = IPC_GET_ARG1(*request);
/* Lookup the file structure corresponding to the file descriptor. */
vfs_file_t *file = vfs_file_get(fd);
if (!file) {
ipc_answer_0(rid, ENOENT);
return;
}
/*
* Lock the open file structure so that no other thread can manipulate
* the same open file at a time.
*/
fibril_mutex_lock(&file->lock);
int fs_phone = vfs_grab_phone(file->node->fs_handle);
/* Make a VFS_OUT_SYMC request at the destination FS server. */
aid_t msg;
ipc_call_t answer;
msg = async_send_2(fs_phone, VFS_OUT_SYNC, file->node->dev_handle,
file->node->index, &answer);
 
/* Wait for reply from the FS server. */
ipcarg_t rc;
async_wait_for(msg, &rc);
vfs_release_phone(fs_phone);
fibril_mutex_unlock(&file->lock);
ipc_answer_0(rid, rc);
}
 
void vfs_close(ipc_callid_t rid, ipc_call_t *request)
{
int fd = IPC_GET_ARG1(*request);
/* Lookup the file structure corresponding to the file descriptor. */
vfs_file_t *file = vfs_file_get(fd);
if (!file) {
ipc_answer_0(rid, ENOENT);
return;
}
/*
* Lock the open file structure so that no other thread can manipulate
* the same open file at a time.
*/
fibril_mutex_lock(&file->lock);
int fs_phone = vfs_grab_phone(file->node->fs_handle);
/* Make a VFS_OUT_CLOSE request at the destination FS server. */
aid_t msg;
ipc_call_t answer;
msg = async_send_2(fs_phone, VFS_OUT_CLOSE, file->node->dev_handle,
file->node->index, &answer);
 
/* Wait for reply from the FS server. */
ipcarg_t rc;
async_wait_for(msg, &rc);
 
vfs_release_phone(fs_phone);
fibril_mutex_unlock(&file->lock);
int retval = IPC_GET_ARG1(answer);
if (retval != EOK)
ipc_answer_0(rid, retval);
retval = vfs_fd_free(fd);
ipc_answer_0(rid, retval);
}
 
static void vfs_rdwr(ipc_callid_t rid, ipc_call_t *request, bool read)
{
 
/*
* The following code strongly depends on the fact that the files data
* structure can be only accessed by a single fibril and all file
* operations are serialized (i.e. the reads and writes cannot
* interleave and a file cannot be closed while it is being read).
*
* Additional synchronization needs to be added once the table of
* open files supports parallel access!
*/
 
int fd = IPC_GET_ARG1(*request);
/* Lookup the file structure corresponding to the file descriptor. */
vfs_file_t *file = vfs_file_get(fd);
if (!file) {
ipc_answer_0(rid, ENOENT);
return;
}
/*
* Now we need to receive a call with client's
* IPC_M_DATA_READ/IPC_M_DATA_WRITE request.
*/
ipc_callid_t callid;
int res;
if (read)
res = ipc_data_read_receive(&callid, NULL);
else
res = ipc_data_write_receive(&callid, NULL);
if (!res) {
ipc_answer_0(callid, EINVAL);
ipc_answer_0(rid, EINVAL);
return;
}
/*
* Lock the open file structure so that no other thread can manipulate
* the same open file at a time.
*/
fibril_mutex_lock(&file->lock);
 
/*
* Lock the file's node so that no other client can read/write to it at
* the same time.
*/
if (read)
fibril_rwlock_read_lock(&file->node->contents_rwlock);
else
fibril_rwlock_write_lock(&file->node->contents_rwlock);
 
if (file->node->type == VFS_NODE_DIRECTORY) {
/*
* Make sure that no one is modifying the namespace
* while we are in readdir().
*/
assert(read);
fibril_rwlock_read_lock(&namespace_rwlock);
}
int fs_phone = vfs_grab_phone(file->node->fs_handle);
/* Make a VFS_READ/VFS_WRITE request at the destination FS server. */
aid_t msg;
ipc_call_t answer;
if (!read && file->append)
file->pos = file->node->size;
msg = async_send_3(fs_phone, read ? VFS_OUT_READ : VFS_OUT_WRITE,
file->node->dev_handle, file->node->index, file->pos, &answer);
/*
* Forward the IPC_M_DATA_READ/IPC_M_DATA_WRITE request to the
* destination FS server. The call will be routed as if sent by
* ourselves. Note that call arguments are immutable in this case so we
* don't have to bother.
*/
ipc_forward_fast(callid, fs_phone, 0, 0, 0, IPC_FF_ROUTE_FROM_ME);
 
/* Wait for reply from the FS server. */
ipcarg_t rc;
async_wait_for(msg, &rc);
vfs_release_phone(fs_phone);
size_t bytes = IPC_GET_ARG1(answer);
 
if (file->node->type == VFS_NODE_DIRECTORY)
fibril_rwlock_read_unlock(&namespace_rwlock);
/* Unlock the VFS node. */
if (read)
fibril_rwlock_read_unlock(&file->node->contents_rwlock);
else {
/* Update the cached version of node's size. */
if (rc == EOK)
file->node->size = IPC_GET_ARG2(answer);
fibril_rwlock_write_unlock(&file->node->contents_rwlock);
}
/* Update the position pointer and unlock the open file. */
if (rc == EOK)
file->pos += bytes;
fibril_mutex_unlock(&file->lock);
/*
* FS server's reply is the final result of the whole operation we
* return to the client.
*/
ipc_answer_1(rid, rc, bytes);
}
 
void vfs_read(ipc_callid_t rid, ipc_call_t *request)
{
vfs_rdwr(rid, request, true);
}
 
void vfs_write(ipc_callid_t rid, ipc_call_t *request)
{
vfs_rdwr(rid, request, false);
}
 
void vfs_seek(ipc_callid_t rid, ipc_call_t *request)
{
int fd = (int) IPC_GET_ARG1(*request);
off_t off = (off_t) IPC_GET_ARG2(*request);
int whence = (int) IPC_GET_ARG3(*request);
 
 
/* Lookup the file structure corresponding to the file descriptor. */
vfs_file_t *file = vfs_file_get(fd);
if (!file) {
ipc_answer_0(rid, ENOENT);
return;
}
 
off_t newpos;
fibril_mutex_lock(&file->lock);
if (whence == SEEK_SET) {
file->pos = off;
fibril_mutex_unlock(&file->lock);
ipc_answer_1(rid, EOK, off);
return;
}
if (whence == SEEK_CUR) {
if (file->pos + off < file->pos) {
fibril_mutex_unlock(&file->lock);
ipc_answer_0(rid, EOVERFLOW);
return;
}
file->pos += off;
newpos = file->pos;
fibril_mutex_unlock(&file->lock);
ipc_answer_1(rid, EOK, newpos);
return;
}
if (whence == SEEK_END) {
fibril_rwlock_read_lock(&file->node->contents_rwlock);
size_t size = file->node->size;
fibril_rwlock_read_unlock(&file->node->contents_rwlock);
if (size + off < size) {
fibril_mutex_unlock(&file->lock);
ipc_answer_0(rid, EOVERFLOW);
return;
}
newpos = size + off;
fibril_mutex_unlock(&file->lock);
ipc_answer_1(rid, EOK, newpos);
return;
}
fibril_mutex_unlock(&file->lock);
ipc_answer_0(rid, EINVAL);
}
 
int
vfs_truncate_internal(fs_handle_t fs_handle, dev_handle_t dev_handle,
fs_index_t index, size_t size)
{
ipcarg_t rc;
int fs_phone;
fs_phone = vfs_grab_phone(fs_handle);
rc = async_req_3_0(fs_phone, VFS_OUT_TRUNCATE, (ipcarg_t)dev_handle,
(ipcarg_t)index, (ipcarg_t)size);
vfs_release_phone(fs_phone);
return (int)rc;
}
 
void vfs_truncate(ipc_callid_t rid, ipc_call_t *request)
{
int fd = IPC_GET_ARG1(*request);
size_t size = IPC_GET_ARG2(*request);
int rc;
 
vfs_file_t *file = vfs_file_get(fd);
if (!file) {
ipc_answer_0(rid, ENOENT);
return;
}
fibril_mutex_lock(&file->lock);
 
fibril_rwlock_write_lock(&file->node->contents_rwlock);
rc = vfs_truncate_internal(file->node->fs_handle,
file->node->dev_handle, file->node->index, size);
if (rc == EOK)
file->node->size = size;
fibril_rwlock_write_unlock(&file->node->contents_rwlock);
 
fibril_mutex_unlock(&file->lock);
ipc_answer_0(rid, (ipcarg_t)rc);
}
 
void vfs_fstat(ipc_callid_t rid, ipc_call_t *request)
{
int fd = IPC_GET_ARG1(*request);
size_t size = IPC_GET_ARG2(*request);
ipcarg_t rc;
 
vfs_file_t *file = vfs_file_get(fd);
if (!file) {
ipc_answer_0(rid, ENOENT);
return;
}
 
ipc_callid_t callid;
if (!ipc_data_read_receive(&callid, NULL)) {
ipc_answer_0(callid, EINVAL);
ipc_answer_0(rid, EINVAL);
return;
}
 
fibril_mutex_lock(&file->lock);
 
int fs_phone = vfs_grab_phone(file->node->fs_handle);
aid_t msg;
msg = async_send_3(fs_phone, VFS_OUT_STAT, file->node->dev_handle,
file->node->index, true, NULL);
ipc_forward_fast(callid, fs_phone, 0, 0, 0, IPC_FF_ROUTE_FROM_ME);
async_wait_for(msg, &rc);
vfs_release_phone(fs_phone);
 
fibril_mutex_unlock(&file->lock);
ipc_answer_0(rid, rc);
}
 
void vfs_stat(ipc_callid_t rid, ipc_call_t *request)
{
size_t len;
ipc_callid_t callid;
 
if (!ipc_data_write_receive(&callid, &len)) {
ipc_answer_0(callid, EINVAL);
ipc_answer_0(rid, EINVAL);
return;
}
char *path = malloc(len + 1);
if (!path) {
ipc_answer_0(callid, ENOMEM);
ipc_answer_0(rid, ENOMEM);
return;
}
int rc;
if ((rc = ipc_data_write_finalize(callid, path, len))) {
ipc_answer_0(rid, rc);
free(path);
return;
}
path[len] = '\0';
 
if (!ipc_data_read_receive(&callid, NULL)) {
free(path);
ipc_answer_0(callid, EINVAL);
ipc_answer_0(rid, EINVAL);
return;
}
 
vfs_lookup_res_t lr;
fibril_rwlock_read_lock(&namespace_rwlock);
rc = vfs_lookup_internal(path, L_NONE, &lr, NULL);
free(path);
if (rc != EOK) {
fibril_rwlock_read_unlock(&namespace_rwlock);
ipc_answer_0(callid, rc);
ipc_answer_0(rid, rc);
return;
}
vfs_node_t *node = vfs_node_get(&lr);
if (!node) {
fibril_rwlock_read_unlock(&namespace_rwlock);
ipc_answer_0(callid, ENOMEM);
ipc_answer_0(rid, ENOMEM);
return;
}
 
fibril_rwlock_read_unlock(&namespace_rwlock);
 
int fs_phone = vfs_grab_phone(node->fs_handle);
aid_t msg;
msg = async_send_3(fs_phone, VFS_OUT_STAT, node->dev_handle,
node->index, false, NULL);
ipc_forward_fast(callid, fs_phone, 0, 0, 0, IPC_FF_ROUTE_FROM_ME);
ipcarg_t rv;
async_wait_for(msg, &rv);
vfs_release_phone(fs_phone);
 
ipc_answer_0(rid, rv);
 
vfs_node_put(node);
}
 
void vfs_mkdir(ipc_callid_t rid, ipc_call_t *request)
{
int mode = IPC_GET_ARG1(*request);
 
size_t len;
ipc_callid_t callid;
 
if (!ipc_data_write_receive(&callid, &len)) {
ipc_answer_0(callid, EINVAL);
ipc_answer_0(rid, EINVAL);
return;
}
char *path = malloc(len + 1);
if (!path) {
ipc_answer_0(callid, ENOMEM);
ipc_answer_0(rid, ENOMEM);
return;
}
int rc;
if ((rc = ipc_data_write_finalize(callid, path, len))) {
ipc_answer_0(rid, rc);
free(path);
return;
}
path[len] = '\0';
 
/* Ignore mode for now. */
(void) mode;
fibril_rwlock_write_lock(&namespace_rwlock);
int lflag = L_DIRECTORY | L_CREATE | L_EXCLUSIVE;
rc = vfs_lookup_internal(path, lflag, NULL, NULL);
fibril_rwlock_write_unlock(&namespace_rwlock);
free(path);
ipc_answer_0(rid, rc);
}
 
void vfs_unlink(ipc_callid_t rid, ipc_call_t *request)
{
int lflag = IPC_GET_ARG1(*request);
 
size_t len;
ipc_callid_t callid;
 
if (!ipc_data_write_receive(&callid, &len)) {
ipc_answer_0(callid, EINVAL);
ipc_answer_0(rid, EINVAL);
return;
}
char *path = malloc(len + 1);
if (!path) {
ipc_answer_0(callid, ENOMEM);
ipc_answer_0(rid, ENOMEM);
return;
}
int rc;
if ((rc = ipc_data_write_finalize(callid, path, len))) {
ipc_answer_0(rid, rc);
free(path);
return;
}
path[len] = '\0';
fibril_rwlock_write_lock(&namespace_rwlock);
lflag &= L_DIRECTORY; /* sanitize lflag */
vfs_lookup_res_t lr;
rc = vfs_lookup_internal(path, lflag | L_UNLINK, &lr, NULL);
free(path);
if (rc != EOK) {
fibril_rwlock_write_unlock(&namespace_rwlock);
ipc_answer_0(rid, rc);
return;
}
 
/*
* The name has already been unlinked by vfs_lookup_internal().
* We have to get and put the VFS node to ensure that it is
* VFS_OUT_DESTROY'ed after the last reference to it is dropped.
*/
vfs_node_t *node = vfs_node_get(&lr);
fibril_mutex_lock(&nodes_mutex);
node->lnkcnt--;
fibril_mutex_unlock(&nodes_mutex);
fibril_rwlock_write_unlock(&namespace_rwlock);
vfs_node_put(node);
ipc_answer_0(rid, EOK);
}
 
void vfs_rename(ipc_callid_t rid, ipc_call_t *request)
{
size_t olen, nlen;
ipc_callid_t callid;
int rc;
 
/* Retrieve the old path. */
if (!ipc_data_write_receive(&callid, &olen)) {
ipc_answer_0(callid, EINVAL);
ipc_answer_0(rid, EINVAL);
return;
}
char *old = malloc(olen + 1);
if (!old) {
ipc_answer_0(callid, ENOMEM);
ipc_answer_0(rid, ENOMEM);
return;
}
if ((rc = ipc_data_write_finalize(callid, old, olen))) {
ipc_answer_0(rid, rc);
free(old);
return;
}
old[olen] = '\0';
/* Retrieve the new path. */
if (!ipc_data_write_receive(&callid, &nlen)) {
ipc_answer_0(callid, EINVAL);
ipc_answer_0(rid, EINVAL);
free(old);
return;
}
char *new = malloc(nlen + 1);
if (!new) {
ipc_answer_0(callid, ENOMEM);
ipc_answer_0(rid, ENOMEM);
free(old);
return;
}
if ((rc = ipc_data_write_finalize(callid, new, nlen))) {
ipc_answer_0(rid, rc);
free(old);
free(new);
return;
}
new[nlen] = '\0';
 
char *oldc = canonify(old, &olen);
char *newc = canonify(new, &nlen);
if (!oldc || !newc) {
ipc_answer_0(rid, EINVAL);
free(old);
free(new);
return;
}
oldc[olen] = '\0';
newc[nlen] = '\0';
if ((!str_lcmp(newc, oldc, str_length(oldc))) &&
((newc[str_length(oldc)] == '/') ||
(str_length(oldc) == 1) ||
(str_length(oldc) == str_length(newc)))) {
/*
* oldc is a prefix of newc and either
* - newc continues with a / where oldc ends, or
* - oldc was / itself, or
* - oldc and newc are equal.
*/
ipc_answer_0(rid, EINVAL);
free(old);
free(new);
return;
}
vfs_lookup_res_t old_lr;
vfs_lookup_res_t new_lr;
vfs_lookup_res_t new_par_lr;
fibril_rwlock_write_lock(&namespace_rwlock);
/* Lookup the node belonging to the old file name. */
rc = vfs_lookup_internal(oldc, L_NONE, &old_lr, NULL);
if (rc != EOK) {
fibril_rwlock_write_unlock(&namespace_rwlock);
ipc_answer_0(rid, rc);
free(old);
free(new);
return;
}
vfs_node_t *old_node = vfs_node_get(&old_lr);
if (!old_node) {
fibril_rwlock_write_unlock(&namespace_rwlock);
ipc_answer_0(rid, ENOMEM);
free(old);
free(new);
return;
}
/* Determine the path to the parent of the node with the new name. */
char *parentc = str_dup(newc);
if (!parentc) {
fibril_rwlock_write_unlock(&namespace_rwlock);
ipc_answer_0(rid, rc);
free(old);
free(new);
return;
}
char *lastsl = str_rchr(parentc + 1, '/');
if (lastsl)
*lastsl = '\0';
else
parentc[1] = '\0';
/* Lookup parent of the new file name. */
rc = vfs_lookup_internal(parentc, L_NONE, &new_par_lr, NULL);
free(parentc); /* not needed anymore */
if (rc != EOK) {
fibril_rwlock_write_unlock(&namespace_rwlock);
ipc_answer_0(rid, rc);
free(old);
free(new);
return;
}
/* Check whether linking to the same file system instance. */
if ((old_node->fs_handle != new_par_lr.triplet.fs_handle) ||
(old_node->dev_handle != new_par_lr.triplet.dev_handle)) {
fibril_rwlock_write_unlock(&namespace_rwlock);
ipc_answer_0(rid, EXDEV); /* different file systems */
free(old);
free(new);
return;
}
/* Destroy the old link for the new name. */
vfs_node_t *new_node = NULL;
rc = vfs_lookup_internal(newc, L_UNLINK, &new_lr, NULL);
switch (rc) {
case ENOENT:
/* simply not in our way */
break;
case EOK:
new_node = vfs_node_get(&new_lr);
if (!new_node) {
fibril_rwlock_write_unlock(&namespace_rwlock);
ipc_answer_0(rid, ENOMEM);
free(old);
free(new);
return;
}
fibril_mutex_lock(&nodes_mutex);
new_node->lnkcnt--;
fibril_mutex_unlock(&nodes_mutex);
break;
default:
fibril_rwlock_write_unlock(&namespace_rwlock);
ipc_answer_0(rid, ENOTEMPTY);
free(old);
free(new);
return;
}
/* Create the new link for the new name. */
rc = vfs_lookup_internal(newc, L_LINK, NULL, NULL, old_node->index);
if (rc != EOK) {
fibril_rwlock_write_unlock(&namespace_rwlock);
if (new_node)
vfs_node_put(new_node);
ipc_answer_0(rid, rc);
free(old);
free(new);
return;
}
fibril_mutex_lock(&nodes_mutex);
old_node->lnkcnt++;
fibril_mutex_unlock(&nodes_mutex);
/* Destroy the link for the old name. */
rc = vfs_lookup_internal(oldc, L_UNLINK, NULL, NULL);
if (rc != EOK) {
fibril_rwlock_write_unlock(&namespace_rwlock);
vfs_node_put(old_node);
if (new_node)
vfs_node_put(new_node);
ipc_answer_0(rid, rc);
free(old);
free(new);
return;
}
fibril_mutex_lock(&nodes_mutex);
old_node->lnkcnt--;
fibril_mutex_unlock(&nodes_mutex);
fibril_rwlock_write_unlock(&namespace_rwlock);
vfs_node_put(old_node);
if (new_node)
vfs_node_put(new_node);
free(old);
free(new);
ipc_answer_0(rid, EOK);
}
 
/**
* @}
*/
/tags/0.4.1/uspace/srv/vfs/vfs.c
0,0 → 1,186
/*
* Copyright (c) 2008 Jakub Jermar
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* - The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
 
/** @addtogroup fs
* @{
*/
 
/**
* @file vfs.c
* @brief VFS service for HelenOS.
*/
 
#include <ipc/ipc.h>
#include <ipc/services.h>
#include <async.h>
#include <errno.h>
#include <stdio.h>
#include <bool.h>
#include <string.h>
#include <as.h>
#include <atomic.h>
#include "vfs.h"
 
#define NAME "vfs"
 
static void vfs_connection(ipc_callid_t iid, ipc_call_t *icall)
{
bool keep_on_going = true;
 
/*
* The connection was opened via the IPC_CONNECT_ME_TO call.
* This call needs to be answered.
*/
ipc_answer_0(iid, EOK);
/*
* Here we enter the main connection fibril loop.
* The logic behind this loop and the protocol is that we'd like to keep
* each connection open until the client hangs up. When the client hangs
* up, we will free its VFS state. The act of hanging up the connection
* by the client is equivalent to client termination because we cannot
* distinguish one from the other. On the other hand, the client can
* hang up arbitrarily if it has no open files and reestablish the
* connection later.
*/
while (keep_on_going) {
ipc_call_t call;
ipc_callid_t callid = async_get_call(&call);
fs_handle_t fs_handle;
int phone;
switch (IPC_GET_METHOD(call)) {
case IPC_M_PHONE_HUNGUP:
keep_on_going = false;
break;
case VFS_IN_REGISTER:
vfs_register(callid, &call);
keep_on_going = false;
break;
case VFS_IN_MOUNT:
vfs_mount(callid, &call);
break;
case VFS_IN_OPEN:
vfs_open(callid, &call);
break;
case VFS_IN_OPEN_NODE:
vfs_open_node(callid, &call);
break;
case VFS_IN_CLOSE:
vfs_close(callid, &call);
break;
case VFS_IN_READ:
vfs_read(callid, &call);
break;
case VFS_IN_WRITE:
vfs_write(callid, &call);
break;
case VFS_IN_SEEK:
vfs_seek(callid, &call);
break;
case VFS_IN_TRUNCATE:
vfs_truncate(callid, &call);
break;
case VFS_IN_FSTAT:
vfs_fstat(callid, &call);
break;
case VFS_IN_STAT:
vfs_stat(callid, &call);
break;
case VFS_IN_MKDIR:
vfs_mkdir(callid, &call);
break;
case VFS_IN_UNLINK:
vfs_unlink(callid, &call);
break;
case VFS_IN_RENAME:
vfs_rename(callid, &call);
break;
case VFS_IN_SYNC:
vfs_sync(callid, &call);
break;
default:
ipc_answer_0(callid, ENOTSUP);
break;
}
}
/* TODO: cleanup after the client */
}
 
int main(int argc, char **argv)
{
printf(NAME ": HelenOS VFS server\n");
/*
* Initialize VFS node hash table.
*/
if (!vfs_nodes_init()) {
printf(NAME ": Failed to initialize VFS node hash table\n");
return ENOMEM;
}
/*
* Allocate and initialize the Path Lookup Buffer.
*/
plb = as_get_mappable_page(PLB_SIZE);
if (!plb) {
printf(NAME ": Cannot allocate a mappable piece of address space\n");
return ENOMEM;
}
if (as_area_create(plb, PLB_SIZE, AS_AREA_READ | AS_AREA_WRITE |
AS_AREA_CACHEABLE) != plb) {
printf(NAME ": Cannot create address space area\n");
return ENOMEM;
}
memset(plb, 0, PLB_SIZE);
/*
* Set a connection handling function/fibril.
*/
async_set_client_connection(vfs_connection);
 
/*
* Register at the naming service.
*/
ipcarg_t phonead;
ipc_connect_to_me(PHONE_NS, SERVICE_VFS, 0, 0, &phonead);
/*
* Start accepting connections.
*/
printf(NAME ": Accepting connections\n");
async_manager();
return 0;
}
 
/**
* @}
*/
/tags/0.4.1/uspace/srv/vfs/vfs.h
0,0 → 1,218
/*
* Copyright (c) 2008 Jakub Jermar
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* - The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
 
/** @addtogroup fs
* @{
*/
 
#ifndef VFS_VFS_H_
#define VFS_VFS_H_
 
#include <ipc/ipc.h>
#include <adt/list.h>
#include <fibril_sync.h>
#include <sys/types.h>
#include <devmap.h>
#include <bool.h>
#include <ipc/vfs.h>
 
// FIXME: according to CONFIG_DEBUG
// #define dprintf(...) printf(__VA_ARGS__)
 
#define dprintf(...)
 
/**
* A structure like this will be allocated for each registered file system.
*/
typedef struct {
link_t fs_link;
vfs_info_t vfs_info;
fs_handle_t fs_handle;
fibril_mutex_t phone_lock;
ipcarg_t phone;
} fs_info_t;
 
/**
* VFS_PAIR uniquely represents a file system instance.
*/
#define VFS_PAIR \
fs_handle_t fs_handle; \
dev_handle_t dev_handle;
 
/**
* VFS_TRIPLET uniquely identifies a file system node (e.g. directory, file) but
* doesn't contain any state. For a stateful structure, see vfs_node_t.
*
* @note fs_handle, dev_handle and index are meant to be returned in one
* IPC reply.
*/
#define VFS_TRIPLET \
VFS_PAIR; \
fs_index_t index;
 
typedef struct {
VFS_PAIR;
} vfs_pair_t;
 
typedef struct {
VFS_TRIPLET;
} vfs_triplet_t;
 
typedef enum vfs_node_type {
VFS_NODE_UNKNOWN,
VFS_NODE_FILE,
VFS_NODE_DIRECTORY,
} vfs_node_type_t;
 
typedef struct {
vfs_triplet_t triplet;
vfs_node_type_t type;
size_t size;
unsigned lnkcnt;
} vfs_lookup_res_t;
 
/**
* Instances of this type represent an active, in-memory VFS node and any state
* which may be associated with it.
*/
typedef struct {
VFS_TRIPLET; /**< Identity of the node. */
 
/**
* Usage counter. This includes, but is not limited to, all vfs_file_t
* structures that reference this node.
*/
unsigned refcnt;
/** Number of names this node has in the file system namespace. */
unsigned lnkcnt;
 
link_t nh_link; /**< Node hash-table link. */
 
vfs_node_type_t type; /**< Partial info about the node type. */
 
size_t size; /**< Cached size if the node is a file. */
 
/**
* Holding this rwlock prevents modifications of the node's contents.
*/
fibril_rwlock_t contents_rwlock;
} vfs_node_t;
 
/**
* Instances of this type represent an open file. If the file is opened by more
* than one task, there will be a separate structure allocated for each task.
*/
typedef struct {
/** Serializes access to this open file. */
fibril_mutex_t lock;
 
vfs_node_t *node;
/** Number of file handles referencing this file. */
unsigned refcnt;
 
/** Append on write. */
bool append;
 
/** Current position in the file. */
off_t pos;
} vfs_file_t;
 
extern fibril_mutex_t nodes_mutex;
 
extern fibril_condvar_t fs_head_cv;
extern fibril_mutex_t fs_head_lock;
extern link_t fs_head; /**< List of registered file systems. */
 
extern vfs_pair_t rootfs; /**< Root file system. */
 
/** Each instance of this type describes one path lookup in progress. */
typedef struct {
link_t plb_link; /**< Active PLB entries list link. */
unsigned index; /**< Index of the first character in PLB. */
size_t len; /**< Number of characters in this PLB entry. */
} plb_entry_t;
 
extern fibril_mutex_t plb_mutex;/**< Mutex protecting plb and plb_head. */
extern uint8_t *plb; /**< Path Lookup Buffer */
extern link_t plb_head; /**< List of active PLB entries. */
 
#define MAX_MNTOPTS_LEN 256
 
/** Holding this rwlock prevents changes in file system namespace. */
extern fibril_rwlock_t namespace_rwlock;
 
extern int vfs_grab_phone(fs_handle_t);
extern void vfs_release_phone(int);
 
extern fs_handle_t fs_name_to_handle(char *, bool);
 
extern int vfs_lookup_internal(char *, int, vfs_lookup_res_t *,
vfs_pair_t *, ...);
extern int vfs_open_node_internal(vfs_lookup_res_t *);
 
extern bool vfs_nodes_init(void);
extern vfs_node_t *vfs_node_get(vfs_lookup_res_t *);
extern void vfs_node_put(vfs_node_t *);
 
#define MAX_OPEN_FILES 128
 
extern bool vfs_files_init(void);
extern vfs_file_t *vfs_file_get(int);
extern int vfs_fd_alloc(void);
extern int vfs_fd_free(int);
 
extern void vfs_file_addref(vfs_file_t *);
extern void vfs_file_delref(vfs_file_t *);
 
extern void vfs_node_addref(vfs_node_t *);
extern void vfs_node_delref(vfs_node_t *);
 
extern void vfs_register(ipc_callid_t, ipc_call_t *);
extern void vfs_mount(ipc_callid_t, ipc_call_t *);
extern void vfs_open(ipc_callid_t, ipc_call_t *);
extern void vfs_open_node(ipc_callid_t, ipc_call_t *);
extern void vfs_sync(ipc_callid_t, ipc_call_t *);
extern void vfs_close(ipc_callid_t, ipc_call_t *);
extern void vfs_read(ipc_callid_t, ipc_call_t *);
extern void vfs_write(ipc_callid_t, ipc_call_t *);
extern void vfs_seek(ipc_callid_t, ipc_call_t *);
extern void vfs_truncate(ipc_callid_t, ipc_call_t *);
extern void vfs_fstat(ipc_callid_t, ipc_call_t *);
extern void vfs_fstat(ipc_callid_t, ipc_call_t *);
extern void vfs_stat(ipc_callid_t, ipc_call_t *);
extern void vfs_mkdir(ipc_callid_t, ipc_call_t *);
extern void vfs_unlink(ipc_callid_t, ipc_call_t *);
extern void vfs_rename(ipc_callid_t, ipc_call_t *);
 
#endif
 
/**
* @}
*/
/tags/0.4.1/uspace/srv/vfs/vfs_node.c
0,0 → 1,235
/*
* Copyright (c) 2008 Jakub Jermar
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* - The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
 
/** @addtogroup fs
* @{
*/
 
/**
* @file vfs_node.c
* @brief Various operations on VFS nodes have their home in this file.
*/
 
#include "vfs.h"
#include <stdlib.h>
#include <string.h>
#include <fibril_sync.h>
#include <adt/hash_table.h>
#include <assert.h>
#include <async.h>
#include <errno.h>
 
/** Mutex protecting the VFS node hash table. */
FIBRIL_MUTEX_INITIALIZE(nodes_mutex);
 
#define NODES_BUCKETS_LOG 8
#define NODES_BUCKETS (1 << NODES_BUCKETS_LOG)
 
/** VFS node hash table containing all active, in-memory VFS nodes. */
hash_table_t nodes;
 
#define KEY_FS_HANDLE 0
#define KEY_DEV_HANDLE 1
#define KEY_INDEX 2
 
static hash_index_t nodes_hash(unsigned long []);
static int nodes_compare(unsigned long [], hash_count_t, link_t *);
static void nodes_remove_callback(link_t *);
 
/** VFS node hash table operations. */
hash_table_operations_t nodes_ops = {
.hash = nodes_hash,
.compare = nodes_compare,
.remove_callback = nodes_remove_callback
};
 
/** Initialize the VFS node hash table.
*
* @return Return true on success, false on failure.
*/
bool vfs_nodes_init(void)
{
return hash_table_create(&nodes, NODES_BUCKETS, 3, &nodes_ops);
}
 
static inline void _vfs_node_addref(vfs_node_t *node)
{
node->refcnt++;
}
 
/** Increment reference count of a VFS node.
*
* @param node VFS node that will have its refcnt incremented.
*/
void vfs_node_addref(vfs_node_t *node)
{
fibril_mutex_lock(&nodes_mutex);
_vfs_node_addref(node);
fibril_mutex_unlock(&nodes_mutex);
}
 
/** Decrement reference count of a VFS node.
*
* This function handles the case when the reference count drops to zero.
*
* @param node VFS node that will have its refcnt decremented.
*/
void vfs_node_delref(vfs_node_t *node)
{
bool free_vfs_node = false;
bool free_fs_node = false;
 
fibril_mutex_lock(&nodes_mutex);
if (node->refcnt-- == 1) {
/*
* We are dropping the last reference to this node.
* Remove it from the VFS node hash table.
*/
unsigned long key[] = {
[KEY_FS_HANDLE] = node->fs_handle,
[KEY_DEV_HANDLE] = node->dev_handle,
[KEY_INDEX] = node->index
};
hash_table_remove(&nodes, key, 3);
free_vfs_node = true;
if (!node->lnkcnt)
free_fs_node = true;
}
fibril_mutex_unlock(&nodes_mutex);
 
if (free_fs_node) {
/*
* The node is not visible in the file system namespace.
* Free up its resources.
*/
int phone = vfs_grab_phone(node->fs_handle);
ipcarg_t rc;
rc = async_req_2_0(phone, VFS_OUT_DESTROY,
(ipcarg_t)node->dev_handle, (ipcarg_t)node->index);
assert(rc == EOK);
vfs_release_phone(phone);
}
if (free_vfs_node)
free(node);
}
 
/** Find VFS node.
*
* This function will try to lookup the given triplet in the VFS node hash
* table. In case the triplet is not found there, a new VFS node is created.
* In any case, the VFS node will have its reference count incremented. Every
* node returned by this call should be eventually put back by calling
* vfs_node_put() on it.
*
* @param result Populated lookup result structure.
*
* @return VFS node corresponding to the given triplet.
*/
vfs_node_t *vfs_node_get(vfs_lookup_res_t *result)
{
unsigned long key[] = {
[KEY_FS_HANDLE] = result->triplet.fs_handle,
[KEY_DEV_HANDLE] = result->triplet.dev_handle,
[KEY_INDEX] = result->triplet.index
};
link_t *tmp;
vfs_node_t *node;
 
fibril_mutex_lock(&nodes_mutex);
tmp = hash_table_find(&nodes, key);
if (!tmp) {
node = (vfs_node_t *) malloc(sizeof(vfs_node_t));
if (!node) {
fibril_mutex_unlock(&nodes_mutex);
return NULL;
}
memset(node, 0, sizeof(vfs_node_t));
node->fs_handle = result->triplet.fs_handle;
node->dev_handle = result->triplet.dev_handle;
node->index = result->triplet.index;
node->size = result->size;
node->lnkcnt = result->lnkcnt;
node->type = result->type;
link_initialize(&node->nh_link);
fibril_rwlock_initialize(&node->contents_rwlock);
hash_table_insert(&nodes, key, &node->nh_link);
} else {
node = hash_table_get_instance(tmp, vfs_node_t, nh_link);
if (node->type == VFS_NODE_UNKNOWN &&
result->type != VFS_NODE_UNKNOWN) {
/* Upgrade the node type. */
node->type = result->type;
}
}
 
assert(node->size == result->size);
assert(node->lnkcnt == result->lnkcnt);
assert(node->type == result->type || result->type == VFS_NODE_UNKNOWN);
 
_vfs_node_addref(node);
fibril_mutex_unlock(&nodes_mutex);
 
return node;
}
 
/** Return VFS node when no longer needed by the caller.
*
* This function will remove the reference on the VFS node created by
* vfs_node_get(). This function can only be called as a closing bracket to the
* preceding vfs_node_get() call.
*
* @param node VFS node being released.
*/
void vfs_node_put(vfs_node_t *node)
{
vfs_node_delref(node);
}
 
hash_index_t nodes_hash(unsigned long key[])
{
hash_index_t a = key[KEY_FS_HANDLE] << (NODES_BUCKETS_LOG / 4);
hash_index_t b = (a | key[KEY_DEV_HANDLE]) << (NODES_BUCKETS_LOG / 2);
return (b | key[KEY_INDEX]) & (NODES_BUCKETS - 1);
}
 
int nodes_compare(unsigned long key[], hash_count_t keys, link_t *item)
{
vfs_node_t *node = hash_table_get_instance(item, vfs_node_t, nh_link);
return (node->fs_handle == key[KEY_FS_HANDLE]) &&
(node->dev_handle == key[KEY_DEV_HANDLE]) &&
(node->index == key[KEY_INDEX]);
}
 
void nodes_remove_callback(link_t *item)
{
}
 
/**
* @}
*/
/tags/0.4.1/uspace/srv/vfs/vfs_lookup.c
0,0 → 1,232
/*
* Copyright (c) 2008 Jakub Jermar
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* - The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
 
/** @addtogroup fs
* @{
*/
 
/**
* @file vfs_lookup.c
* @brief
*/
 
#include "vfs.h"
#include <ipc/ipc.h>
#include <async.h>
#include <errno.h>
#include <string.h>
#include <stdarg.h>
#include <bool.h>
#include <fibril_sync.h>
#include <adt/list.h>
#include <vfs/canonify.h>
 
#define min(a, b) ((a) < (b) ? (a) : (b))
 
FIBRIL_MUTEX_INITIALIZE(plb_mutex);
LIST_INITIALIZE(plb_head); /**< PLB entry ring buffer. */
uint8_t *plb = NULL;
 
/** Perform a path lookup.
*
* @param path Path to be resolved; it must be a NULL-terminated
* string.
* @param lflag Flags to be used during lookup.
* @param result Empty structure where the lookup result will be stored.
* Can be NULL.
* @param altroot If non-empty, will be used instead of rootfs as the root
* of the whole VFS tree.
*
* @return EOK on success or an error code from errno.h.
*
*/
int vfs_lookup_internal(char *path, int lflag, vfs_lookup_res_t *result,
vfs_pair_t *altroot, ...)
{
vfs_pair_t *root;
 
if (altroot)
root = altroot;
else
root = &rootfs;
 
if (!root->fs_handle)
return ENOENT;
size_t len;
path = canonify(path, &len);
if (!path)
return EINVAL;
fs_index_t index = 0;
if (lflag & L_LINK) {
va_list ap;
 
va_start(ap, altroot);
index = va_arg(ap, fs_index_t);
va_end(ap);
}
fibril_mutex_lock(&plb_mutex);
 
plb_entry_t entry;
link_initialize(&entry.plb_link);
entry.len = len;
 
off_t first; /* the first free index */
off_t last; /* the last free index */
 
if (list_empty(&plb_head)) {
first = 0;
last = PLB_SIZE - 1;
} else {
plb_entry_t *oldest = list_get_instance(plb_head.next,
plb_entry_t, plb_link);
plb_entry_t *newest = list_get_instance(plb_head.prev,
plb_entry_t, plb_link);
 
first = (newest->index + newest->len) % PLB_SIZE;
last = (oldest->index - 1) % PLB_SIZE;
}
 
if (first <= last) {
if ((last - first) + 1 < len) {
/*
* The buffer cannot absorb the path.
*/
fibril_mutex_unlock(&plb_mutex);
return ELIMIT;
}
} else {
if (PLB_SIZE - ((first - last) + 1) < len) {
/*
* The buffer cannot absorb the path.
*/
fibril_mutex_unlock(&plb_mutex);
return ELIMIT;
}
}
 
/*
* We know the first free index in PLB and we also know that there is
* enough space in the buffer to hold our path.
*/
 
entry.index = first;
entry.len = len;
 
/*
* Claim PLB space by inserting the entry into the PLB entry ring
* buffer.
*/
list_append(&entry.plb_link, &plb_head);
fibril_mutex_unlock(&plb_mutex);
 
/*
* Copy the path into PLB.
*/
size_t cnt1 = min(len, (PLB_SIZE - first) + 1);
size_t cnt2 = len - cnt1;
memcpy(&plb[first], path, cnt1);
memcpy(plb, &path[cnt1], cnt2);
 
ipc_call_t answer;
int phone = vfs_grab_phone(root->fs_handle);
aid_t req = async_send_5(phone, VFS_OUT_LOOKUP, (ipcarg_t) first,
(ipcarg_t) (first + len - 1) % PLB_SIZE,
(ipcarg_t) root->dev_handle, (ipcarg_t) lflag, (ipcarg_t) index,
&answer);
ipcarg_t rc;
async_wait_for(req, &rc);
vfs_release_phone(phone);
fibril_mutex_lock(&plb_mutex);
list_remove(&entry.plb_link);
/*
* Erasing the path from PLB will come handy for debugging purposes.
*/
memset(&plb[first], 0, cnt1);
memset(plb, 0, cnt2);
fibril_mutex_unlock(&plb_mutex);
 
if ((rc == EOK) && (result)) {
result->triplet.fs_handle = (fs_handle_t) IPC_GET_ARG1(answer);
result->triplet.dev_handle = (dev_handle_t) IPC_GET_ARG2(answer);
result->triplet.index = (fs_index_t) IPC_GET_ARG3(answer);
result->size = (size_t) IPC_GET_ARG4(answer);
result->lnkcnt = (unsigned) IPC_GET_ARG5(answer);
if (lflag & L_FILE)
result->type = VFS_NODE_FILE;
else if (lflag & L_DIRECTORY)
result->type = VFS_NODE_DIRECTORY;
else
result->type = VFS_NODE_UNKNOWN;
}
 
return rc;
}
 
/** Perform a node open operation.
*
* @return EOK on success or an error code from errno.h.
*
*/
int vfs_open_node_internal(vfs_lookup_res_t *result)
{
int phone = vfs_grab_phone(result->triplet.fs_handle);
ipc_call_t answer;
aid_t req = async_send_2(phone, VFS_OUT_OPEN_NODE,
(ipcarg_t) result->triplet.dev_handle,
(ipcarg_t) result->triplet.index, &answer);
ipcarg_t rc;
async_wait_for(req, &rc);
vfs_release_phone(phone);
if (rc == EOK) {
result->size = (size_t) IPC_GET_ARG1(answer);
result->lnkcnt = (unsigned) IPC_GET_ARG2(answer);
if (IPC_GET_ARG3(answer) & L_FILE)
result->type = VFS_NODE_FILE;
else if (IPC_GET_ARG3(answer) & L_DIRECTORY)
result->type = VFS_NODE_DIRECTORY;
else
result->type = VFS_NODE_UNKNOWN;
}
return rc;
}
 
/**
* @}
*/
/tags/0.4.1/uspace/srv/vfs/vfs_register.c
0,0 → 1,355
/*
* Copyright (c) 2008 Jakub Jermar
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* - The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
 
/** @addtogroup fs
* @{
*/
 
/**
* @file vfs_register.c
* @brief
*/
 
#include <ipc/ipc.h>
#include <ipc/services.h>
#include <async.h>
#include <fibril.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <bool.h>
#include <fibril_sync.h>
#include <adt/list.h>
#include <as.h>
#include <assert.h>
#include <atomic.h>
#include "vfs.h"
 
FIBRIL_CONDVAR_INITIALIZE(fs_head_cv);
FIBRIL_MUTEX_INITIALIZE(fs_head_lock);
LIST_INITIALIZE(fs_head);
 
atomic_t fs_handle_next = {
.count = 1
};
 
/** Verify the VFS info structure.
*
* @param info Info structure to be verified.
*
* @return Non-zero if the info structure is sane, zero otherwise.
*/
static bool vfs_info_sane(vfs_info_t *info)
{
int i;
 
/*
* Check if the name is non-empty and is composed solely of ASCII
* characters [a-z]+[a-z0-9_-]*.
*/
if (!islower(info->name[0])) {
dprintf("The name doesn't start with a lowercase character.\n");
return false;
}
for (i = 1; i < FS_NAME_MAXLEN; i++) {
if (!(islower(info->name[i]) || isdigit(info->name[i])) &&
(info->name[i] != '-') && (info->name[i] != '_')) {
if (info->name[i] == '\0') {
break;
} else {
dprintf("The name contains illegal "
"characters.\n");
return false;
}
}
}
/*
* This check is not redundant. It ensures that the name is
* NULL-terminated, even if FS_NAME_MAXLEN characters are used.
*/
if (info->name[i] != '\0') {
dprintf("The name is not properly NULL-terminated.\n");
return false;
}
return true;
}
 
/** VFS_REGISTER protocol function.
*
* @param rid Hash of the call with the request.
* @param request Call structure with the request.
*/
void vfs_register(ipc_callid_t rid, ipc_call_t *request)
{
ipc_callid_t callid;
ipc_call_t call;
int rc;
size_t size;
 
dprintf("Processing VFS_REGISTER request received from %p.\n",
request->in_phone_hash);
 
/*
* The first call has to be IPC_M_DATA_SEND in which we receive the
* VFS info structure from the client FS.
*/
if (!ipc_data_write_receive(&callid, &size)) {
/*
* The client doesn't obey the same protocol as we do.
*/
dprintf("Receiving of VFS info failed.\n");
ipc_answer_0(callid, EINVAL);
ipc_answer_0(rid, EINVAL);
return;
}
dprintf("VFS info received, size = %d\n", size);
/*
* We know the size of the VFS info structure. See if the client
* understands this easy concept too.
*/
if (size != sizeof(vfs_info_t)) {
/*
* The client is sending us something, which cannot be
* the info structure.
*/
dprintf("Received VFS info has bad size.\n");
ipc_answer_0(callid, EINVAL);
ipc_answer_0(rid, EINVAL);
return;
}
 
/*
* Allocate and initialize a buffer for the fs_info structure.
*/
fs_info_t *fs_info;
fs_info = (fs_info_t *) malloc(sizeof(fs_info_t));
if (!fs_info) {
dprintf("Could not allocate memory for FS info.\n");
ipc_answer_0(callid, ENOMEM);
ipc_answer_0(rid, ENOMEM);
return;
}
link_initialize(&fs_info->fs_link);
fibril_mutex_initialize(&fs_info->phone_lock);
rc = ipc_data_write_finalize(callid, &fs_info->vfs_info, size);
if (rc != EOK) {
dprintf("Failed to deliver the VFS info into our AS, rc=%d.\n",
rc);
free(fs_info);
ipc_answer_0(callid, rc);
ipc_answer_0(rid, rc);
return;
}
 
dprintf("VFS info delivered.\n");
if (!vfs_info_sane(&fs_info->vfs_info)) {
free(fs_info);
ipc_answer_0(callid, EINVAL);
ipc_answer_0(rid, EINVAL);
return;
}
fibril_mutex_lock(&fs_head_lock);
 
/*
* Check for duplicit registrations.
*/
if (fs_name_to_handle(fs_info->vfs_info.name, false)) {
/*
* We already register a fs like this.
*/
dprintf("FS is already registered.\n");
fibril_mutex_unlock(&fs_head_lock);
free(fs_info);
ipc_answer_0(callid, EEXISTS);
ipc_answer_0(rid, EEXISTS);
return;
}
 
/*
* Add fs_info to the list of registered FS's.
*/
dprintf("Inserting FS into the list of registered file systems.\n");
list_append(&fs_info->fs_link, &fs_head);
/*
* Now we want the client to send us the IPC_M_CONNECT_TO_ME call so
* that a callback connection is created and we have a phone through
* which to forward VFS requests to it.
*/
callid = async_get_call(&call);
if (IPC_GET_METHOD(call) != IPC_M_CONNECT_TO_ME) {
dprintf("Unexpected call, method = %d\n", IPC_GET_METHOD(call));
list_remove(&fs_info->fs_link);
fibril_mutex_unlock(&fs_head_lock);
free(fs_info);
ipc_answer_0(callid, EINVAL);
ipc_answer_0(rid, EINVAL);
return;
}
fs_info->phone = IPC_GET_ARG5(call);
ipc_answer_0(callid, EOK);
 
dprintf("Callback connection to FS created.\n");
 
/*
* The client will want us to send him the address space area with PLB.
*/
 
if (!ipc_share_in_receive(&callid, &size)) {
dprintf("Unexpected call, method = %d\n", IPC_GET_METHOD(call));
list_remove(&fs_info->fs_link);
fibril_mutex_unlock(&fs_head_lock);
ipc_hangup(fs_info->phone);
free(fs_info);
ipc_answer_0(callid, EINVAL);
ipc_answer_0(rid, EINVAL);
return;
}
/*
* We can only send the client address space area PLB_SIZE bytes long.
*/
if (size != PLB_SIZE) {
dprintf("Client suggests wrong size of PFB, size = %d\n", size);
list_remove(&fs_info->fs_link);
fibril_mutex_unlock(&fs_head_lock);
ipc_hangup(fs_info->phone);
free(fs_info);
ipc_answer_0(callid, EINVAL);
ipc_answer_0(rid, EINVAL);
return;
}
 
/*
* Commit to read-only sharing the PLB with the client.
*/
(void) ipc_share_in_finalize(callid, plb,
AS_AREA_READ | AS_AREA_CACHEABLE);
 
dprintf("Sharing PLB.\n");
 
/*
* That was it. The FS has been registered.
* In reply to the VFS_REGISTER request, we assign the client file
* system a global file system handle.
*/
fs_info->fs_handle = (fs_handle_t) atomic_postinc(&fs_handle_next);
ipc_answer_1(rid, EOK, (ipcarg_t) fs_info->fs_handle);
fibril_condvar_broadcast(&fs_head_cv);
fibril_mutex_unlock(&fs_head_lock);
dprintf("\"%.*s\" filesystem successfully registered, handle=%d.\n",
FS_NAME_MAXLEN, fs_info->vfs_info.name, fs_info->fs_handle);
}
 
/** For a given file system handle, implement policy for allocating a phone.
*
* @param handle File system handle.
*
* @return Phone over which a multi-call request can be safely
* sent. Return 0 if no phone was found.
*/
int vfs_grab_phone(fs_handle_t handle)
{
int phone;
 
/*
* For now, we don't try to be very clever and very fast. We simply
* lookup the phone in the fs_head list and duplicate it. The duplicate
* phone will be returned to the client and the client will use it for
* communication. In the future, we should cache the connections so
* that they do not have to be reestablished over and over again.
*/
fibril_mutex_lock(&fs_head_lock);
link_t *cur;
fs_info_t *fs;
for (cur = fs_head.next; cur != &fs_head; cur = cur->next) {
fs = list_get_instance(cur, fs_info_t, fs_link);
if (fs->fs_handle == handle) {
fibril_mutex_unlock(&fs_head_lock);
fibril_mutex_lock(&fs->phone_lock);
phone = ipc_connect_me_to(fs->phone, 0, 0, 0);
fibril_mutex_unlock(&fs->phone_lock);
 
assert(phone > 0);
return phone;
}
}
fibril_mutex_unlock(&fs_head_lock);
return 0;
}
 
/** Tell VFS that the phone is not needed anymore.
*
* @param phone Phone to FS task.
*/
void vfs_release_phone(int phone)
{
/* TODO: implement connection caching */
ipc_hangup(phone);
}
 
/** Convert file system name to its handle.
*
* @param name File system name.
* @param lock If true, the function will lock and unlock the
* fs_head_lock.
*
* @return File system handle or zero if file system not found.
*/
fs_handle_t fs_name_to_handle(char *name, bool lock)
{
int handle = 0;
if (lock)
fibril_mutex_lock(&fs_head_lock);
link_t *cur;
for (cur = fs_head.next; cur != &fs_head; cur = cur->next) {
fs_info_t *fs = list_get_instance(cur, fs_info_t, fs_link);
if (str_cmp(fs->vfs_info.name, name) == 0) {
handle = fs->fs_handle;
break;
}
}
if (lock)
fibril_mutex_unlock(&fs_head_lock);
return handle;
}
 
/**
* @}
*/
/tags/0.4.1/uspace/srv/vfs/vfs_file.c
0,0 → 1,175
/*
* Copyright (c) 2007 Jakub Jermar
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* - The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
 
/** @addtogroup fs
* @{
*/
 
/**
* @file vfs_file.c
* @brief Various operations on files have their home in this file.
*/
 
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <bool.h>
#include <fibril.h>
#include <fibril_sync.h>
#include "vfs.h"
 
/**
* This is a per-connection table of open files.
* Our assumption is that each client opens only one connection and therefore
* there is one table of open files per task. However, this may not be the case
* and the client can open more connections to VFS. In that case, there will be
* several tables and several file handle name spaces per task. Besides of this,
* the functionality will stay unchanged. So unless the client knows what it is
* doing, it should open one connection to VFS only.
*
* Allocation of the open files table is deferred until the client makes the
* first VFS_OPEN operation.
*
* This resource being per-connection and, in the first place, per-fibril, we
* don't need to protect it by a mutex.
*/
fibril_local vfs_file_t **files = NULL;
 
/** Initialize the table of open files. */
bool vfs_files_init(void)
{
if (!files) {
files = malloc(MAX_OPEN_FILES * sizeof(vfs_file_t *));
if (!files)
return false;
memset(files, 0, MAX_OPEN_FILES * sizeof(vfs_file_t *));
}
return true;
}
 
/** Allocate a file descriptor.
*
* @return First available file descriptor or a negative error
* code.
*/
int vfs_fd_alloc(void)
{
if (!vfs_files_init())
return ENOMEM;
unsigned int i;
for (i = 0; i < MAX_OPEN_FILES; i++) {
if (!files[i]) {
files[i] = (vfs_file_t *) malloc(sizeof(vfs_file_t));
if (!files[i])
return ENOMEM;
memset(files[i], 0, sizeof(vfs_file_t));
fibril_mutex_initialize(&files[i]->lock);
vfs_file_addref(files[i]);
return (int) i;
}
}
return EMFILE;
}
 
/** Release file descriptor.
*
* @param fd File descriptor being released.
*
* @return EOK on success or EBADF if fd is an invalid file
* descriptor.
*/
int vfs_fd_free(int fd)
{
if (!vfs_files_init())
return ENOMEM;
if ((fd < 0) || (fd >= MAX_OPEN_FILES) || (files[fd] == NULL))
return EBADF;
vfs_file_delref(files[fd]);
files[fd] = NULL;
return EOK;
}
 
/** Increment reference count of VFS file structure.
*
* @param file File structure that will have reference count
* incremented.
*/
void vfs_file_addref(vfs_file_t *file)
{
/*
* File structures are per-connection, so no-one, except the current
* fibril, should have a reference to them. This is the reason we don't
* do any synchronization here.
*/
file->refcnt++;
}
 
/** Decrement reference count of VFS file structure.
*
* @param file File structure that will have reference count
* decremented.
*/
void vfs_file_delref(vfs_file_t *file)
{
if (file->refcnt-- == 1) {
/*
* Lost the last reference to a file, need to drop our reference
* to the underlying VFS node.
*/
vfs_node_delref(file->node);
free(file);
}
}
 
/** Find VFS file structure for a given file descriptor.
*
* @param fd File descriptor.
*
* @return VFS file structure corresponding to fd.
*/
vfs_file_t *vfs_file_get(int fd)
{
if (!vfs_files_init())
return NULL;
if ((fd >= 0) && (fd < MAX_OPEN_FILES))
return files[fd];
return NULL;
}
 
/**
* @}
*/
/tags/0.4.1/uspace/srv/vfs/Makefile
0,0 → 1,81
#
# Copyright (c) 2006 Martin Decky
# Copyright (c) 2008 Jakub Jermar
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# - Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# - Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# - The name of the author may not be used to endorse or promote products
# derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
 
## Setup toolchain
#
 
LIBC_PREFIX = ../../lib/libc
SOFTINT_PREFIX = ../../lib/softint
 
include $(LIBC_PREFIX)/Makefile.toolchain
 
LIBS = $(LIBC_PREFIX)/libc.a
 
## Sources
#
 
OUTPUT = vfs
SOURCES = \
vfs.c \
vfs_node.c \
vfs_file.c \
vfs_ops.c \
vfs_lookup.c \
vfs_register.c
 
OBJECTS := $(addsuffix .o,$(basename $(SOURCES)))
 
.PHONY: all clean depend disasm
 
all: $(OUTPUT) $(OUTPUT).disasm
 
-include Makefile.depend
 
clean:
-rm -f $(OUTPUT) $(OUTPUT).map $(OUTPUT).disasm Makefile.depend $(OBJECTS)
 
depend:
$(CC) $(DEFS) $(CFLAGS) -M $(SOURCES) > Makefile.depend
 
$(OUTPUT): $(OBJECTS) $(LIBS)
$(LD) -T $(LIBC_PREFIX)/arch/$(UARCH)/_link.ld $(OBJECTS) $(LIBS) $(LFLAGS) -o $@ -Map $(OUTPUT).map
 
disasm: $(OUTPUT).disasm
 
$(OUTPUT).disasm: $(OUTPUT)
$(OBJDUMP) -d $< > $@
 
%.o: %.S
$(CC) $(DEFS) $(AFLAGS) $(CFLAGS) -D__ASM__ -c $< -o $@
 
%.o: %.s
$(AS) $(AFLAGS) $< -o $@
 
%.o: %.c
$(CC) $(DEFS) $(CFLAGS) -c $< -o $@