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. |
62,161 → 74,48 |
RWLOCK_INITIALIZE(namespace_rwlock); |
|
futex_t rootfs_futex = FUTEX_INITIALIZER; |
vfs_triplet_t rootfs = { |
vfs_pair_t rootfs = { |
.fs_handle = 0, |
.dev_handle = 0, |
.index = 0, |
.dev_handle = 0 |
}; |
|
static int |
lookup_root(fs_handle_t fs_handle, dev_handle_t dev_handle, |
vfs_lookup_res_t *result) |
static void vfs_mount_internal(ipc_callid_t rid, dev_handle_t dev_handle, |
fs_handle_t fs_handle, char *mp) |
{ |
vfs_pair_t altroot = { |
.fs_handle = fs_handle, |
.dev_handle = dev_handle, |
}; |
|
return vfs_lookup_internal("/", L_DIRECTORY, result, &altroot); |
} |
|
void vfs_mount(ipc_callid_t rid, ipc_call_t *request) |
{ |
dev_handle_t dev_handle; |
/* Resolve the path to the mountpoint. */ |
vfs_lookup_res_t mp_res; |
vfs_node_t *mp_node = NULL; |
int rc; |
int phone; |
|
/* |
* 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. |
*/ |
|
ipc_callid_t callid; |
size_t size; |
|
/* |
* 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'; |
|
/* |
* 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(rid, ENOENT); |
return; |
} |
|
/* 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'; |
|
/* |
* Lookup the root node of the filesystem being mounted. |
* In this case, we don't need to take the namespace_futex as the root |
* node cannot be removed. However, we do take a reference to it so |
* that we can track how many times it has been mounted. |
*/ |
vfs_lookup_res_t mr_res; |
rc = lookup_root(fs_handle, dev_handle, &mr_res); |
if (rc != EOK) { |
free(buf); |
ipc_answer_0(rid, rc); |
return; |
} |
vfs_node_t *mr_node = vfs_node_get(&mr_res); |
if (!mr_node) { |
free(buf); |
ipc_answer_0(rid, ENOMEM); |
return; |
} |
|
/* Finally, we need to 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); |
vfs_node_put(mr_node); |
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); |
vfs_node_put(mr_node); /* failed -> drop reference */ |
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); |
vfs_node_put(mr_node); /* failed -> drop reference */ |
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. |
225,29 → 124,45 |
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); |
|
/* Inform the mount point about the root mount. */ |
phone = vfs_grab_phone(mr_res.triplet.fs_handle); |
rc = async_req_5_0(phone, VFS_MOUNT, |
(ipcarg_t) mr_res.triplet.dev_handle, |
(ipcarg_t) mr_res.triplet.index, |
(ipcarg_t) mr_res.triplet.fs_handle, |
(ipcarg_t) mr_res.triplet.dev_handle, |
(ipcarg_t) mr_res.triplet.index); |
/* Tell the mountee that it is being mounted. */ |
phone = vfs_grab_phone(fs_handle); |
rc = async_req_1_3(phone, VFS_MOUNTED, |
(ipcarg_t) dev_handle, &rindex, &rsize, &rlnkcnt); |
vfs_release_phone(phone); |
|
if (rc == EOK) |
rootfs = mr_res.triplet; |
else |
vfs_node_put(mr_node); |
|
|
if (rc != EOK) { |
futex_up(&rootfs_futex); |
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 { |
256,8 → 171,6 |
* being mounted first. |
*/ |
futex_up(&rootfs_futex); |
free(buf); |
vfs_node_put(mr_node); /* failed -> drop reference */ |
ipc_answer_0(rid, ENOENT); |
return; |
} |
264,30 → 177,21 |
} |
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 and also the root node |
* of the file system being mounted. |
* handles, and we know the mount point VFS node. |
*/ |
|
/** |
* @todo |
* Add more IPC parameters so that we can send mount mode/flags. |
*/ |
|
phone = vfs_grab_phone(mp_res.triplet.fs_handle); |
rc = async_req_5_0(phone, VFS_MOUNT, |
rc = async_req_4_0(phone, VFS_MOUNT, |
(ipcarg_t) mp_res.triplet.dev_handle, |
(ipcarg_t) mp_res.triplet.index, |
(ipcarg_t) mr_res.triplet.fs_handle, |
(ipcarg_t) mr_res.triplet.dev_handle, |
(ipcarg_t) mr_res.triplet.index); |
(ipcarg_t) fs_handle, |
(ipcarg_t) dev_handle); |
vfs_release_phone(phone); |
|
|
if (rc != EOK) { |
/* Mount failed, drop references to mr_node and mp_node. */ |
vfs_node_put(mr_node); |
/* Mount failed, drop reference to mp_node. */ |
if (mp_node) |
vfs_node_put(mp_node); |
} |
295,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()) { |
315,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) |
422,12 → 500,8 |
void vfs_close(ipc_callid_t rid, ipc_call_t *request) |
{ |
int fd = IPC_GET_ARG1(*request); |
if (fd >= MAX_OPEN_FILES) { |
ipc_answer_0(rid, EBADF); |
return; |
} |
vfs_fd_free(fd); |
ipc_answer_0(rid, EOK); |
int rc = vfs_fd_free(fd); |
ipc_answer_0(rid, rc); |
} |
|
static void vfs_rdwr(ipc_callid_t rid, ipc_call_t *request, bool read) |
444,7 → 518,7 |
*/ |
|
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) { |
451,7 → 525,7 |
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. |
467,7 → 541,7 |
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. |
483,6 → 557,15 |
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); |
|
/* Make a VFS_READ/VFS_WRITE request at the destination FS server. */ |
500,14 → 583,17 |
* don't have to bother. |
*/ |
ipc_forward_fast(callid, fs_phone, 0, 0, 0, IPC_FF_ROUTE_FROM_ME); |
|
|
vfs_release_phone(fs_phone); |
|
|
/* Wait for reply from the FS server. */ |
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) |
rwlock_read_unlock(&file->node->contents_rwlock); |
517,12 → 603,12 |
file->node->size = IPC_GET_ARG2(answer); |
rwlock_write_unlock(&file->node->contents_rwlock); |
} |
|
|
/* Update the position pointer and unlock the open file. */ |
if (rc == EOK) |
file->pos += bytes; |
futex_up(&file->lock); |
|
|
/* |
* FS server's reply is the final result of the whole operation we |
* return to the client. |