Subversion Repositories HelenOS

Compare Revisions

Ignore whitespace Rev 4152 → Rev 4153

/branches/network/uspace/srv/vfs/vfs.c
28,11 → 28,11
 
/** @addtogroup fs
* @{
*/
*/
 
/**
* @file vfs.c
* @brief VFS service for HelenOS.
* @file vfs.c
* @brief VFS service for HelenOS.
*/
 
#include <ipc/ipc.h>
49,18 → 49,16
 
#define NAME "vfs"
 
#define dprintf(...) printf(__VA_ARGS__)
 
static void vfs_connection(ipc_callid_t iid, ipc_call_t *icall)
{
bool keep_on_going = 1;
 
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
72,18 → 70,38
* connection later.
*/
while (keep_on_going) {
ipc_callid_t callid;
ipc_call_t call;
 
callid = async_get_call(&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 IPC_M_CONNECT_ME_TO:
/*
* Connect the client file system to another one.
*/
/* FIXME:
* Prevent ordinary clients from connecting to file
* system servers directly. This should be solved by
* applying some security mechanisms.
*/
fs_handle = IPC_GET_ARG1(call);
phone = vfs_grab_phone(fs_handle);
(void) ipc_forward_fast(callid, phone, 0, 0, 0,
IPC_FF_NONE);
vfs_release_phone(phone);
break;
case VFS_REGISTER:
vfs_register(callid, &call);
keep_on_going = false;
/*
* Keep the connection open so that a file system can
* later ask us to connect it to another file system.
* This is necessary to support non-root mounts.
*/
break;
case VFS_MOUNT:
vfs_mount(callid, &call);
120,22 → 138,19
break;
}
}
 
/* TODO: cleanup after the client */
}
 
int main(int argc, char **argv)
{
ipcarg_t phonead;
 
printf(NAME ": HelenOS VFS server\n");
 
/*
* Initialize the list of registered file systems.
*/
list_initialize(&fs_head);
 
/*
* Initialize VFS node hash table.
*/
143,7 → 158,7
printf(NAME ": Failed to initialize VFS node hash table\n");
return ENOMEM;
}
 
/*
* Allocate and initialize the Path Lookup Buffer.
*/
153,6 → 168,7
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");
164,12 → 180,13
* Set a connectio 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.
*/
180,4 → 197,4
 
/**
* @}
*/
*/
/branches/network/uspace/srv/vfs/vfs_ops.c
28,11 → 28,11
 
/** @addtogroup fs
* @{
*/
*/
 
/**
* @file vfs_ops.c
* @brief Operations that VFS offers to its clients.
* @file vfs_ops.c
* @brief Operations that VFS offers to its clients.
*/
 
#include "vfs.h"
55,6 → 55,18
/* Forward declarations of static functions. */
static int vfs_truncate_internal(fs_handle_t, dev_handle_t, fs_index_t, size_t);
 
/** Pending mount structure. */
typedef struct {
link_t link;
char *fs_name; /**< File system name */
char *mp; /**< Mount point */
ipc_callid_t callid; /**< Call ID waiting for the mount */
ipc_callid_t rid; /**< Request ID */
dev_handle_t dev_handle; /**< Device handle */
} pending_req_t;
 
LIST_INITIALIZE(pending_req);
 
/**
* This rwlock prevents the race between a triplet-to-VFS-node resolution and a
* concurrent VFS operation which modifies the file system namespace.
67,135 → 79,43
.dev_handle = 0
};
 
void vfs_mount(ipc_callid_t rid, ipc_call_t *request)
static void vfs_mount_internal(ipc_callid_t rid, dev_handle_t dev_handle,
fs_handle_t fs_handle, char *mp)
{
dev_handle_t dev_handle;
/* Resolve the path to the mountpoint. */
vfs_lookup_res_t mp_res;
vfs_node_t *mp_node = NULL;
ipc_callid_t callid;
ipc_call_t data;
int rc;
int phone;
size_t size;
 
/*
* 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 = (dev_handle_t)IPC_GET_ARG1(*request);
 
/*
* For now, don't make use of ARG2 and ARG3, but they can be used to
* carry mount options in the future.
*/
 
/*
* 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);
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);
return;
}
 
/* Deliver the file system name. */
char fs_name[FS_NAME_MAXLEN + 1];
(void) ipc_data_write_finalize(callid, fs_name, size);
fs_name[size] = '\0';
/*
* Wait for IPC_M_PING so that we can return an error if we don't know
* fs_name.
*/
callid = async_get_call(&data);
if (IPC_GET_METHOD(data) != IPC_M_PING) {
ipc_answer_0(callid, ENOTSUP);
ipc_answer_0(rid, ENOTSUP);
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.
*/
fs_handle_t fs_handle = fs_name_to_handle(fs_name, true);
if (!fs_handle) {
ipc_answer_0(callid, ENOENT);
ipc_answer_0(rid, ENOENT);
return;
}
 
/* Acknowledge that we know fs_name. */
ipc_answer_0(callid, EOK);
 
/* Now, we want the client to send us the mount point. */
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. */
uint8_t *buf;
buf = malloc(size + 1);
if (!buf) {
ipc_answer_0(callid, ENOMEM);
ipc_answer_0(rid, ENOMEM);
return;
}
 
/* Deliver the mount point. */
(void) ipc_data_write_finalize(callid, buf, size);
buf[size] = '\0';
 
/* Resolve the path to the mountpoint. */
vfs_lookup_res_t mp_res;
futex_down(&rootfs_futex);
if (rootfs.fs_handle) {
/* We already have the root FS. */
rwlock_write_lock(&namespace_rwlock);
if ((size == 1) && (buf[0] == '/')) {
if ((strlen(mp) == 1) && (mp[0] == '/')) {
/* Trying to mount root FS over root FS */
rwlock_write_unlock(&namespace_rwlock);
futex_up(&rootfs_futex);
free(buf);
ipc_answer_0(rid, EBUSY);
return;
}
rc = vfs_lookup_internal(buf, L_DIRECTORY, &mp_res, NULL);
rc = vfs_lookup_internal(mp, L_DIRECTORY, &mp_res, NULL);
if (rc != EOK) {
/* The lookup failed for some reason. */
rwlock_write_unlock(&namespace_rwlock);
futex_up(&rootfs_futex);
free(buf);
ipc_answer_0(rid, rc);
return;
}
mp_node = vfs_node_get(&mp_res);
if (!mp_node) {
rwlock_write_unlock(&namespace_rwlock);
futex_up(&rootfs_futex);
free(buf);
ipc_answer_0(rid, ENOMEM);
return;
}
/*
* Now we hold a reference to mp_node.
* It will be dropped upon the corresponding VFS_UNMOUNT.
204,18 → 124,17
rwlock_write_unlock(&namespace_rwlock);
} else {
/* We still don't have the root file system mounted. */
if ((size == 1) && (buf[0] == '/')) {
if ((strlen(mp) == 1) && (mp[0] == '/')) {
vfs_lookup_res_t mr_res;
vfs_node_t *mr_node;
ipcarg_t rindex;
ipcarg_t rsize;
ipcarg_t rlnkcnt;
/*
* For this simple, but important case,
* we are almost done.
*/
free(buf);
/* Tell the mountee that it is being mounted. */
phone = vfs_grab_phone(fs_handle);
228,21 → 147,22
ipc_answer_0(rid, rc);
return;
}
 
mr_res.triplet.fs_handle = fs_handle;
mr_res.triplet.dev_handle = dev_handle;
mr_res.triplet.index = (fs_index_t) rindex;
mr_res.size = (size_t) rsize;
mr_res.lnkcnt = (unsigned) rlnkcnt;
 
mr_res.type = VFS_NODE_DIRECTORY;
rootfs.fs_handle = fs_handle;
rootfs.dev_handle = dev_handle;
futex_up(&rootfs_futex);
 
/* Add reference to the mounted root. */
mr_node = vfs_node_get(&mr_res);
assert(mr_node);
 
ipc_answer_0(rid, rc);
return;
} else {
251,7 → 171,6
* being mounted first.
*/
futex_up(&rootfs_futex);
free(buf);
ipc_answer_0(rid, ENOENT);
return;
}
258,13 → 177,11
}
futex_up(&rootfs_futex);
free(buf); /* The buffer is not needed anymore. */
/*
* At this point, we have all necessary pieces: file system and device
* handles, and we know the mount point VFS node.
*/
 
phone = vfs_grab_phone(mp_res.triplet.fs_handle);
rc = async_req_4_0(phone, VFS_MOUNT,
(ipcarg_t) mp_res.triplet.dev_handle,
272,7 → 189,7
(ipcarg_t) fs_handle,
(ipcarg_t) dev_handle);
vfs_release_phone(phone);
 
if (rc != EOK) {
/* Mount failed, drop reference to mp_node. */
if (mp_node)
282,6 → 199,170
ipc_answer_0(rid, rc);
}
 
/** Process pending mount requests */
void vfs_process_pending_mount()
{
link_t *cur;
loop:
for (cur = pending_req.next; cur != &pending_req; cur = cur->next) {
pending_req_t *pr = list_get_instance(cur, pending_req_t, link);
fs_handle_t fs_handle = fs_name_to_handle(pr->fs_name, true);
if (!fs_handle)
continue;
/* Acknowledge that we know fs_name. */
ipc_answer_0(pr->callid, EOK);
/* Do the mount */
vfs_mount_internal(pr->rid, pr->dev_handle, fs_handle, pr->mp);
free(pr->fs_name);
free(pr->mp);
list_remove(cur);
free(pr);
goto loop;
}
}
 
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, EREFUSED);
free(mp);
return;
}
mp[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);
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);
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, EREFUSED);
free(mp);
return;
}
/* Deliver the file system name. */
retval = ipc_data_write_finalize(callid, fs_name, size);
if (retval != EOK) {
ipc_answer_0(rid, EREFUSED);
free(mp);
free(fs_name);
return;
}
fs_name[size] = '\0';
/*
* 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.
*/
fs_handle_t fs_handle = fs_name_to_handle(fs_name, true);
if (!fs_handle) {
if (flags & IPC_FLAG_BLOCKING) {
/* Blocking mount, add to pending list */
pending_req_t *pr = (pending_req_t *) malloc(sizeof(pending_req_t));
if (!pr) {
ipc_answer_0(callid, ENOMEM);
ipc_answer_0(rid, ENOMEM);
free(mp);
free(fs_name);
return;
}
pr->fs_name = fs_name;
pr->mp = mp;
pr->callid = callid;
pr->rid = rid;
pr->dev_handle = dev_handle;
list_append(&pr->link, &pending_req);
return;
}
ipc_answer_0(callid, ENOENT);
ipc_answer_0(rid, ENOENT);
free(mp);
free(fs_name);
return;
}
/* Acknowledge that we know fs_name. */
ipc_answer_0(callid, EOK);
/* Do the mount */
vfs_mount_internal(rid, dev_handle, fs_handle, mp);
free(mp);
free(fs_name);
}
 
void vfs_open(ipc_callid_t rid, ipc_call_t *request)
{
if (!vfs_files_init()) {
302,6 → 383,16
int mode = IPC_GET_ARG3(*request);
size_t len;
 
/*
* Make sure that we are called with exactly one of L_FILE and
* L_DIRECTORY.
*/
if ((lflag & (L_FILE | L_DIRECTORY)) == 0 ||
(lflag & (L_FILE | L_DIRECTORY)) == (L_FILE | L_DIRECTORY)) {
ipc_answer_0(rid, EINVAL);
return;
}
 
if (oflag & O_CREAT)
lflag |= L_CREATE;
if (oflag & O_EXCL)
456,7 → 547,7
* the same open file at a time.
*/
futex_down(&file->lock);
 
/*
* Lock the file's node so that no other client can read/write to it at
* the same time.
465,6 → 556,15
rwlock_read_lock(&file->node->contents_rwlock);
else
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);
rwlock_read_lock(&namespace_rwlock);
}
int fs_phone = vfs_grab_phone(file->node->fs_handle);
490,6 → 590,9
ipcarg_t rc;
async_wait_for(msg, &rc);
size_t bytes = IPC_GET_ARG1(answer);
 
if (file->node->type == VFS_NODE_DIRECTORY)
rwlock_read_unlock(&namespace_rwlock);
/* Unlock the VFS node. */
if (read)
/branches/network/uspace/srv/vfs/vfs_register.c
28,10 → 28,10
 
/** @addtogroup fs
* @{
*/
*/
 
/**
* @file vfs_register.c
* @file vfs_register.c
* @brief
*/
 
98,30 → 98,6
return false;
}
 
/*
* Check if the FS implements mandatory VFS operations.
*/
if (info->ops[IPC_METHOD_TO_VFS_OP(VFS_LOOKUP)] != VFS_OP_DEFINED) {
dprintf("Operation VFS_LOOKUP not defined by the client.\n");
return false;
}
if (info->ops[IPC_METHOD_TO_VFS_OP(VFS_READ)] != VFS_OP_DEFINED) {
dprintf("Operation VFS_READ not defined by the client.\n");
return false;
}
/*
* Check if each operation is either not defined, defined or default.
*/
for (i = VFS_FIRST; i < VFS_LAST_CLNT; i++) {
if ((info->ops[IPC_METHOD_TO_VFS_OP(i)] != VFS_OP_NULL) &&
(info->ops[IPC_METHOD_TO_VFS_OP(i)] != VFS_OP_DEFAULT) &&
(info->ops[IPC_METHOD_TO_VFS_OP(i)] != VFS_OP_DEFINED)) {
dprintf("Operation info not understood.\n");
return false;
}
}
return true;
}
 
302,6 → 278,11
dprintf("\"%.*s\" filesystem successfully registered, handle=%d.\n",
FS_NAME_MAXLEN, fs_info->vfs_info.name, fs_info->fs_handle);
/* Process pending mount requests possibly waiting
* for this filesystem implementation.
*/
vfs_process_pending_mount();
}
 
/** For a given file system handle, implement policy for allocating a phone.
/branches/network/uspace/srv/vfs/vfs.h
47,8 → 47,6
 
#define VFS_FIRST IPC_FIRST_USER_METHOD
 
#define IPC_METHOD_TO_VFS_OP(m) ((m) - VFS_FIRST)
 
/* Basic types. */
typedef int16_t fs_handle_t;
typedef int16_t dev_handle_t;
81,32 → 79,16
VFS_LAST_SRV, /* keep this the last member of this enum */
} vfs_request_srv_t;
 
 
/**
* An instance of this structure is associated with a particular FS operation.
* It tells VFS if the FS supports the operation or maybe if a default one
* should be used.
*/
typedef enum {
VFS_OP_NULL = 0,
VFS_OP_DEFAULT,
VFS_OP_DEFINED
} vfs_op_t;
 
#define FS_NAME_MAXLEN 20
 
/**
* A structure like this is passed to VFS by each individual FS upon its
* registration. It assosiates a human-readable identifier with each
* registered FS. More importantly, through this structure, the FS announces
* what operations it supports.
* registered FS.
*/
typedef struct {
/** Unique identifier of the fs. */
char name[FS_NAME_MAXLEN + 1];
/** Operations. */
vfs_op_t ops[VFS_LAST_CLNT - VFS_FIRST];
} vfs_info_t;
 
/**
190,8 → 172,15
*/
#define L_PARENT 64
 
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;
213,6 → 202,9
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. */
 
/**
290,6 → 282,7
extern void vfs_node_addref(vfs_node_t *);
extern void vfs_node_delref(vfs_node_t *);
 
extern void vfs_process_pending_mount(void);
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 *);
/branches/network/uspace/srv/vfs/vfs_node.c
175,15 → 175,22
node->index = result->triplet.index;
node->size = result->size;
node->lnkcnt = result->lnkcnt;
node->type = result->type;
link_initialize(&node->nh_link);
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);
futex_up(&nodes_futex);
/branches/network/uspace/srv/vfs/vfs_lookup.c
182,6 → 182,12
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;
/branches/network/uspace/srv/vfs/Makefile
32,6 → 32,7
 
LIBC_PREFIX = ../../lib/libc
SOFTINT_PREFIX = ../../lib/softint
 
include $(LIBC_PREFIX)/Makefile.toolchain
 
LIBS = $(LIBC_PREFIX)/libc.a
57,13 → 58,13
-include Makefile.depend
 
clean:
-rm -f $(OUTPUT) $(OUTPUT).map $(OUTPUT).disasm Makefile.depend
-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/$(ARCH)/_link.ld $(OBJECTS) $(LIBS) $(LFLAGS) -o $@ -Map $(OUTPUT).map
$(LD) -T $(LIBC_PREFIX)/arch/$(UARCH)/_link.ld $(OBJECTS) $(LIBS) $(LFLAGS) -o $@ -Map $(OUTPUT).map
 
disasm: $(OUTPUT).disasm