diff -ruN /tmp/sos-code-article7.5/INSTALL ../sos-code-article8/INSTALL
--- /tmp/sos-code-article7.5/INSTALL	2005-04-27 20:17:12.000000000 +0200
+++ ../sos-code-article8/INSTALL	2005-07-01 16:39:50.000000000 +0200
@@ -64,9 +64,9 @@
       copy the file 'fd.img' to a floppy, and boot from it
 
  2nd method
-   => see extra/README to compile with the boot sector we provide (up to
-      article 2 only), copy the file 'extra/sos_bsect.img' to a floppy,
-      and boot from it
+   => see extra/README to compile with the boot sector we provide,
+      copy the file 'extra/sos_bsect.img' to a floppy, and boot from
+      it
 
 
 Inside a PC emulator (x86 and non-x86 hosts)
@@ -78,6 +78,7 @@
 installed: 'apt-get install libsdl1.2-dev' on debian
 testing/unstable).
 
+
  1/ Grub is installed on the host (x86 hosts only)
  - - - - - - - - - - - - - - - - - - - - - - - - -
 
@@ -90,6 +91,7 @@
    qemu: run 'qemu -fda fd.img'
      If grub hangs while loading the kernel, please go to method 2/
 
+
  2/ Grub is not installed (all hosts)
  - - - - - - - - - - - - - - - - - -
 
@@ -100,8 +102,9 @@
 
    qemu: run 'qemu -fda fd.img'
 
- 3/ Bonus: boot with the bootsector we provide (all hosts, up to art. 2 ONLY !)
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+ 3/ Bonus: boot with the bootsector we provide (all hosts)
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   
   See extra/README to generate a floppy image with the boot sector we
   provide, and:
@@ -110,14 +113,16 @@
 
    qemu: run 'qemu -fda extra/sos_qemu.img'
 
-  NOTE: After article 2, this way of booting is not supported: please
-  use the method 2/ above.
+  NOTE: This technique assumes that INT 15H is supported by the
+  machine's BIOS. This should be OK for the vast majority of targets
+  (bochs, qemu, recent machines), but we do not guarantee it. In case
+  of doubt, please use Grub.
 
 
 NOTE : recommended versions of the tools
 ----------------------------------------
  - OS           : Linux 2.6.11.7-d2-1 i686
- - gcc          : gcc (GCC) 3.3.5 (Debian 1:3.3.5-8)
+ - gcc          : gcc (GCC) 3.3.6 (Debian 1:3.3.6-5)
  - GNU binutils : GNU ld version 2.15
  - GNU make     : GNU Make 3.80
 
diff -ruN /tmp/sos-code-article7.5/Makefile ../sos-code-article8/Makefile
--- /tmp/sos-code-article7.5/Makefile	2005-04-27 20:17:12.000000000 +0200
+++ ../sos-code-article8/Makefile	2005-07-01 16:39:47.000000000 +0200
@@ -17,16 +17,17 @@
 
 CC=gcc
 LD=ld
-CFLAGS  = -Wall -nostdinc -ffreestanding -DKERNEL_SOS
-LIBGCC := $(shell $(CC) -print-libgcc-file-name) # To benefit from FP/64bits artihm.
+CP=cp
+STRIP=strip
+CFLAGS  = -Wall -nostdinc -ffreestanding -DKERNEL_SOS -O
+LIBGCC  = $(shell $(CC) -print-libgcc-file-name) # To benefit from FP/64bits artihm.
 LDFLAGS = --warn-common -nostdlib
 OBJECTS = bootstrap/multiboot.o					\
           hwcore/idt.o hwcore/gdt.o				\
 	  hwcore/swintr.o hwcore/swintr_wrappers.o		\
 	  hwcore/exception.o hwcore/exception_wrappers.o	\
 	  hwcore/irq.o hwcore/irq_wrappers.o hwcore/i8259.o	\
-	  hwcore/paging.o					\
-	  hwcore/i8254.o drivers/x86_videomem.o drivers/bochs.o	\
+	  hwcore/paging.o hwcore/i8254.o			\
 	  hwcore/cpu_context.o hwcore/cpu_context_switch.o	\
 	  hwcore/mm_context.o					\
 	  sos/kmem_vmm.o sos/kmem_slab.o sos/kmalloc.o		\
@@ -37,19 +38,27 @@
           sos/assert.o sos/main.o sos/mouse_sim.o		\
 	  sos/uaccess.o sos/calcload.o				\
 	  sos/umem_vmm.o sos/binfmt_elf32.o			\
+	  drivers/x86_videomem.o drivers/bochs.o		\
 	  drivers/zero.o drivers/mem.o				\
+	  sos/hash.o sos/fs.o sos/fs_nscache.o			\
+	  drivers/fs_virtfs.o					\
           userland/userprogs.kimg
 
 KERNEL_OBJ   = sos.elf
+KERNEL_LOAD  = sos.gz
 MULTIBOOT_IMAGE = fd.img
 PWD := $(shell pwd)
 
 # Main target
 all: $(MULTIBOOT_IMAGE)
 
-$(MULTIBOOT_IMAGE): $(KERNEL_OBJ)
+$(MULTIBOOT_IMAGE): $(KERNEL_LOAD)
 	./support/build_image.sh $@ $<
 
+$(KERNEL_LOAD): $(KERNEL_OBJ)
+	$(CP) $< $<.strip && $(STRIP) -sx $<.strip
+	gzip < $<.strip > $@
+
 $(KERNEL_OBJ): $(OBJECTS) ./support/sos.lds
 	$(LD) $(LDFLAGS) -T ./support/sos.lds -o $@ $(OBJECTS) $(LIBGCC)
 	-nm -C $@ | cut -d ' ' -f 1,3 > sos.map
@@ -74,8 +83,8 @@
 
 # Clean directory
 clean:
-	$(RM) *.img *.o mtoolsrc *~ menu.txt *.img *.elf *.bin *.map
-	$(RM) *.log *.out bochs*
+	$(RM) *.img *.o mtoolsrc *~ menu.txt *.img *.elf *.bin *.strip *.map
+	$(RM) *.log *.out bochs* sos.gz
 	$(RM) bootstrap/*.o bootstrap/*~
 	$(RM) drivers/*.o drivers/*~
 	$(RM) hwcore/*.o hwcore/*~
diff -ruN /tmp/sos-code-article7.5/README ../sos-code-article8/README
--- /tmp/sos-code-article7.5/README	2005-04-27 20:17:12.000000000 +0200
+++ ../sos-code-article8/README	2005-07-01 16:39:50.000000000 +0200
@@ -33,8 +33,7 @@
    i586-gnu) is available. Can be tested on real i486/pentium
    hardware, or on any host that can run an i486/pentium PC emulator
    (bochs or qemu)
- - kernel loaded by grub, or by a sample bootsector (up to article 2
-   ONLY)
+ - kernel loaded by grub or by a sample bootsector
  - clear separation of physical memory and virtual memory concepts,
    even inside the kernel: no identity-mapping of the physical memory
    inside the kernel (allows to move virtual mappings of kernel pages
@@ -54,7 +53,7 @@
    'all' and 'clean'
  - bootstrap/ directory: code to load the kernel. Both the stuff
    needed for a multiboot-compliant loader (eg grub) AND a bootsector
-   are provided. The bootsector may only be used up to article 2.
+   are provided.
  - sos/ directory: the entry routine for the kernel (main.c), various
    systemwide header files, a set of common useful C routines
    ("nano-klibc"), and kernel subsystems (kernel memory management,
diff -ruN /tmp/sos-code-article7.5/VERSION ../sos-code-article8/VERSION
--- /tmp/sos-code-article7.5/VERSION	2005-04-27 20:17:12.000000000 +0200
+++ ../sos-code-article8/VERSION	2005-07-01 16:39:48.000000000 +0200
@@ -1,8 +1,7 @@
 SOS -- Simple OS
 Copyright (C) 2003,2004,2005 The SOS Team (David Decotigny & Thomas Petazzoni)
 
-Version "Article 7 (2nd part)" -- User space virtual memory management
-                                  (mmap/exec/fork/malloc)
+Version "Article 8" -- Basic VFS support (read/write/mount/symlink/unlink/mmap)
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
diff -ruN /tmp/sos-code-article7.5/bootstrap/multiboot.h ../sos-code-article8/bootstrap/multiboot.h
--- /tmp/sos-code-article7.5/bootstrap/multiboot.h	2005-04-27 20:17:12.000000000 +0200
+++ ../sos-code-article8/bootstrap/multiboot.h	2005-07-01 16:39:47.000000000 +0200
@@ -47,10 +47,12 @@
 
 #include <sos/types.h>
 
+
 /* The address of the stack of the bootstrap thread */
 extern sos_vaddr_t bootstrap_stack_bottom;
 extern sos_size_t bootstrap_stack_size;
 
+
 /* Types.  */
 
 /* The Multiboot header.  */
diff -ruN /tmp/sos-code-article7.5/drivers/fs_virtfs.c ../sos-code-article8/drivers/fs_virtfs.c
--- /tmp/sos-code-article7.5/drivers/fs_virtfs.c	1970-01-01 01:00:00.000000000 +0100
+++ ../sos-code-article8/drivers/fs_virtfs.c	2005-07-01 16:39:47.000000000 +0200
@@ -0,0 +1,1017 @@
+/* Copyright (C) 2005 David Decotigny
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License
+   as published by the Free Software Foundation; either version 2
+   of the License, or (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+   USA. 
+*/
+
+#include <sos/kmalloc.h>
+#include <sos/klibc.h>
+#include <sos/assert.h>
+#include <sos/list.h>
+#include <sos/ksynch.h>
+#include <sos/uaccess.h>
+#include <sos/physmem.h>
+
+#include <sos/fs.h>
+#include <sos/fs_nscache.h>
+
+#include "fs_virtfs.h"
+
+/**
+ * @file fs_virtfs.c
+ *
+ * All the sos_fs_nodes and their contents are stored in kernel
+ * memory. Thus, with "virtfs", the "disk" is actually the kernel
+ * memory. In a sos_fs_node, the storage_location field corresponds to
+ * the kernel address of the corresponding struct sos_fs_node.
+ *
+ * Basic kernel-memory virtual FS. Highly UNDER-optimized filesystem:
+ * each time we resize a file, a complete reallocation is done. This
+ * somehow complicates the file mapping, as we cannot do this anyhow
+ * as long as the file is mapped because the kernel address of the
+ * file contents must NOT change. For that reason, we forbid to resize
+ * a file as soon as it is mapped and the file's contents are aligned
+ * on a page boundary in kernel memory (use of sos_kmem_vmm_alloc
+ * instead of sos_kmalloc).
+ */
+
+
+/**
+ * A directory entry, used in virtfs_node of type "directory". This is
+ * as simple as a sos_fs_node with a name.
+ */
+struct virtfs_direntry
+{
+  char * name;
+  struct sos_fs_node * fsnode;
+
+  /**
+   * Used by readdir to be resilient against creat/mkdir/unlink/rmdir
+   * between 2 successive readdirs. Each time a new child to a node is
+   * allocated, a new "creation_order" is uniquely attributed. 64bits
+   * should be large enough.
+   */
+  sos_lcount_t creation_order;
+
+  struct virtfs_direntry * sibling_prev, * sibling_next;
+};
+
+
+/**
+ * Structure of a FS file or dir for virtfs. Virtfs only supports
+ * regular files, directories or symbolic links:
+ *  - regular files correspond to an area in kernel memory
+ *  - directories correspond to a list of virtfs_direntry
+ *
+ * Structural inheritance of sos_fs_node: "super" is the ancestor (in
+ * "OOP" terms). The "super" field might not be the first field: from
+ * a sos_fs_node, the corresponding virtfs_node is given by the
+ * sos_fs_node::custom_data field
+ */
+struct virtfs_node
+{
+  /** The ancestor */
+  struct sos_fs_node super;
+
+  union
+  {
+    /* A file */
+    struct
+    {
+      /* Contents of the file */
+      sos_size_t size;
+      void * data;
+
+      /* Yes, the file can be mapped ! */
+      struct sos_umem_vmm_mapped_resource mapres;
+      sos_count_t num_mappings; /**< Forbids the region to be resized
+				   while it's being mapped, because this
+				   woould cause the underlying data area
+				   to be moved */
+    } file;
+
+    /* A directory */
+    struct
+    {
+      /** the children nodes are inserted in FIFO order. This is
+	  important for the readdir function to be resilient against
+	  mkdir/rmdir/creat/unlink */
+      struct virtfs_direntry * list_entries;
+
+      /** Used by readdir to remember the last unique "creation order"
+	  attributed */
+      sos_lcount_t top_creation_order;
+    } dir;
+  }; /* Anonymous union (gcc extension) */
+
+  /* The virtfs nodes are chained */
+  struct virtfs_node * prev, * next;
+};
+
+
+/**
+ * A virtfs FS instance
+ *
+ * Structural inheritance of sos_fs_manager_instance: "super" is the
+ * ancestor (in "OOP" terms). The "super" field might not be the first
+ * field: from a sos_fs_manager_instance, the corresponding
+ * virtfs_instance is given by the
+ * sos_fs_manager_instance::custom_data field
+ */
+struct virtfs_instance
+{
+  /** Ancestor */
+  struct sos_fs_manager_instance super;
+
+  /** For the serialization of virtfs "disk" (ie kernel memory)
+      accesses */
+  struct sos_kmutex lock;
+
+  /** The list of virtfs nodes "on disk" (ie in kernel memory) */
+  struct virtfs_node * list_fsnodes;
+};
+
+
+/** The description of the "Virtual FS" */
+static struct sos_fs_manager_type virtfs_type;
+
+
+/* ********** Forward declarations */
+static sos_ret_t virtfs_mount(struct sos_fs_manager_type * this,
+			      struct sos_fs_node * device,
+			      const char * args,
+			      struct sos_fs_manager_instance ** mounted_fs);
+
+
+static sos_ret_t virtfs_umount(struct sos_fs_manager_type * this,
+			       struct sos_fs_manager_instance * mounted_fs);
+
+
+static sos_ret_t virtfs_new_mapping(struct sos_umem_vmm_vr *vr);
+
+
+sos_ret_t sos_fs_virtfs_subsystem_setup()
+{
+  strzcpy(virtfs_type.name, "virtfs", SOS_FS_MANAGER_NAME_MAXLEN);
+  virtfs_type.mount  = virtfs_mount;
+  virtfs_type.umount = virtfs_umount;
+
+  return sos_fs_register_fs_type(& virtfs_type);
+}
+
+
+/* ********************************************************
+ * Helper functions
+ */
+
+
+/* Helper function to serialize "disk" accesses */
+inline static void virtfs_lock(struct virtfs_node *a_node)
+{
+  struct virtfs_instance * fs = (struct virtfs_instance*)a_node->super.fs->custom_data;
+  sos_kmutex_lock(& fs->lock, NULL);
+}
+
+
+/* Helper function to serialize "disk" accesses */
+inline static void virtfs_unlock(struct virtfs_node *a_node)
+{
+  struct virtfs_instance * fs = (struct virtfs_instance*)a_node->super.fs->custom_data;
+  sos_kmutex_unlock(& fs->lock);
+}
+
+
+/* Helper function to resize the given virts node (ie kernel memory
+   reallocation) */
+static sos_ret_t virtfs_resize(struct virtfs_node *this,
+			       sos_size_t new_size)
+{
+  void * new_data = NULL;
+
+  if (this->file.size == new_size)
+    return SOS_OK;
+
+  /* Don't allow to resize the region when the file is being mapped */
+  if (this->file.num_mappings > 0)
+    return -SOS_EBUSY;
+
+  if (new_size > 0)
+    {
+      /* Use kmem_vmm_alloc instead of kmalloc to make sure the data
+	 WILL be page-aligned (needed by file mapping stuff) */
+      sos_ui32_t npages = SOS_PAGE_ALIGN_SUP(new_size) / SOS_PAGE_SIZE;
+      new_data = (void*)sos_kmem_vmm_alloc(npages,
+					   SOS_KMEM_VMM_MAP);
+      if (! new_data)
+	return -SOS_OK;
+    }
+
+  /* Copy the data to its new location */
+  if (this->file.size < new_size)
+    {
+      if (this->file.size > 0)
+	memcpy(new_data, this->file.data, this->file.size);
+      if (new_size > this->file.size)
+	memset(new_data + this->file.size, 0x0,
+	       new_size - this->file.size);
+    }
+  else if (new_size > 0)
+    memcpy(new_data, this->file.data, new_size);
+
+  if (this->file.data)
+    sos_kfree((sos_vaddr_t)this->file.data);
+  this->file.data = new_data;
+  this->file.size = new_size;
+
+  return SOS_OK;
+}
+
+
+/* ********************************************************
+ * Opened file operations
+ */
+
+
+static sos_ret_t
+virtfs_duplicate_opened_file(struct sos_fs_opened_file *this,
+			     const struct sos_process  * for_owner,
+			     struct sos_fs_opened_file **result)
+{
+  *result = (struct sos_fs_opened_file*)
+    sos_kmalloc(sizeof(struct sos_fs_opened_file), 0);
+  if (! *result)
+    return -SOS_ENOMEM;
+
+  memcpy(*result, this, sizeof(*this));
+  (*result)->owner = for_owner;
+  return SOS_OK;
+}
+
+
+static sos_ret_t
+virtfs_seek(struct sos_fs_opened_file *this,
+	    sos_lsoffset_t offset,
+	    sos_seek_whence_t whence,
+	    /* out */ sos_lsoffset_t * result_position)
+{
+  sos_lsoffset_t ref_offs;
+  struct virtfs_node * virtfsnode;
+
+  virtfsnode = (struct virtfs_node*)
+    sos_fs_nscache_get_fs_node(this->direntry)->custom_data;
+
+  if ( (virtfsnode->super.type != SOS_FS_NODE_REGULAR_FILE)
+       && (virtfsnode->super.type != SOS_FS_NODE_SYMLINK))
+    return -SOS_ENOSUP;
+
+  *result_position = this->position;
+  switch (whence)
+    {
+    case SOS_SEEK_SET:
+      ref_offs = 0;
+      break;
+
+    case SOS_SEEK_CUR:
+      ref_offs = this->position;
+      break;
+
+    case SOS_SEEK_END:
+      ref_offs = virtfsnode->file.size;
+      break;
+
+    default:
+      return -SOS_EINVAL;
+    }
+
+  if (offset < -ref_offs)
+    return -SOS_EINVAL;
+  
+  this->position = ref_offs + offset;
+  *result_position = this->position;
+  return SOS_OK;
+}
+
+
+static sos_ret_t virtfs_read(struct sos_fs_opened_file *this,
+			     sos_uaddr_t dest_buf,
+			     sos_size_t * /* in/out */len)
+{
+  sos_ret_t retval;
+  struct virtfs_node * virtfsnode;
+
+  virtfsnode = (struct virtfs_node*)
+    sos_fs_nscache_get_fs_node(this->direntry)->custom_data;
+
+  if ( (virtfsnode->super.type != SOS_FS_NODE_REGULAR_FILE)
+       && (virtfsnode->super.type != SOS_FS_NODE_SYMLINK))
+    return -SOS_ENOSUP;
+
+  if (this->position >= virtfsnode->file.size)
+    {
+      *len = 0;
+      return SOS_OK;
+    }
+
+  virtfs_lock(virtfsnode);
+
+  if (this->position + *len >= virtfsnode->file.size)
+    *len = virtfsnode->file.size - this->position;
+
+  retval = sos_memcpy_to_user(dest_buf,
+			      ((sos_vaddr_t)virtfsnode->file.data)
+			      + this->position,
+			      *len);
+  if (retval < 0)
+    {
+      virtfs_unlock(virtfsnode);
+      return retval;
+    }
+
+  this->position += retval;
+  *len = retval;
+
+  virtfs_unlock(virtfsnode);
+
+  return SOS_OK;
+}
+
+
+static sos_ret_t virtfs_write(struct sos_fs_opened_file *this,
+			     sos_uaddr_t src_buf,
+			     sos_size_t * /* in/out */len)
+{
+  sos_ret_t retval;
+  struct virtfs_node * virtfsnode;
+
+  virtfsnode = (struct virtfs_node*)
+    sos_fs_nscache_get_fs_node(this->direntry)->custom_data;
+
+  if ( (virtfsnode->super.type != SOS_FS_NODE_REGULAR_FILE)
+       && (virtfsnode->super.type != SOS_FS_NODE_SYMLINK))
+    return -SOS_ENOSUP;
+
+  virtfs_lock(virtfsnode);
+  if (this->position + *len >= virtfsnode->file.size)
+    {
+      /* Try to resize if needed */
+      if (SOS_OK != virtfs_resize(virtfsnode, this->position + *len))
+	*len = virtfsnode->file.size - this->position;
+    }
+
+  retval = sos_memcpy_from_user(((sos_vaddr_t)virtfsnode->file.data)
+				  + this->position,
+				src_buf,
+				*len);
+  if (retval < 0)
+    {
+      virtfs_unlock(virtfsnode);
+      return retval;
+    }
+
+  this->position += retval;
+  *len = retval;
+
+  virtfs_unlock(virtfsnode);
+
+  return SOS_OK;
+}
+
+
+static sos_ret_t virtfs_mmap(struct sos_fs_opened_file *this,
+			     sos_uaddr_t *uaddr, sos_size_t size,
+			     sos_ui32_t access_rights,
+			     sos_ui32_t flags,
+			     sos_luoffset_t offset)
+{
+  struct virtfs_node * virtfsnode;
+
+  virtfsnode = (struct virtfs_node*)
+    sos_fs_nscache_get_fs_node(this->direntry)->custom_data;
+
+  if (virtfsnode->super.type != SOS_FS_NODE_REGULAR_FILE)
+    return -SOS_ENOSUP;
+
+  return sos_umem_vmm_map(sos_process_get_address_space(this->owner),
+			  uaddr, size, access_rights,
+			  flags, & virtfsnode->file.mapres, offset);
+}
+
+
+static sos_ret_t virtfs_readdir(struct sos_fs_opened_file *this,
+				struct sos_fs_dirent * result)
+{
+  /* For directories, "position" indicates the "creation_order" of the
+     last readdir operation, and "custom_data" indicates the address
+     of the last directory entry */
+
+  struct virtfs_direntry * direntry, * next_direntry;
+  struct virtfs_node * virtfsnode;
+  int nb;
+
+  virtfsnode = (struct virtfs_node*)
+    sos_fs_nscache_get_fs_node(this->direntry)->custom_data;
+  next_direntry = NULL;
+
+  /* If the "generation" of the node did not change, the next direntry
+     is the next in the list. We can safely do this because the list
+     of direntries is sorted in increasing creation_order. */
+  if ((this->generation == virtfsnode->super.generation)
+      && (this->custom_data != NULL))
+    {
+      direntry = (struct virtfs_direntry*)this->custom_data;
+      next_direntry = direntry->sibling_next;
+
+      /* Did we go past the end of the list ? */
+      if (next_direntry == list_get_head_named(virtfsnode->dir.list_entries,
+					       sibling_prev, sibling_next))
+	next_direntry = NULL;
+    }
+  else
+    /* Otherwise we have to lookup the next entry manually */
+    {
+      /* Lookup the entry that has next creation_order */
+      next_direntry = NULL;
+      list_foreach_forward_named(virtfsnode->dir.list_entries,
+				 direntry, nb,
+				 sibling_prev, sibling_next)
+	{
+	  if (direntry->creation_order <= this->position)
+	    continue;
+
+	  if (! next_direntry)
+	    {
+	      next_direntry = direntry;
+	      continue;
+	    }
+	  
+	  if (direntry->creation_order < next_direntry->creation_order)
+	    next_direntry = direntry;
+	}
+    }
+
+  if (! next_direntry)
+    {
+      this->custom_data = NULL;
+      this->position    = 0;
+      return -SOS_ENOENT;
+    }
+
+  /* Update the result */
+  result->storage_location  = ((sos_vaddr_t)next_direntry->fsnode);
+  result->offset_in_dirfile = next_direntry->creation_order;
+  result->type              = next_direntry->fsnode->type;
+  result->namelen           = strnlen(next_direntry->name,
+				      SOS_FS_DIRENT_NAME_MAXLEN);
+  strzcpy(result->name, next_direntry->name, SOS_FS_DIRENT_NAME_MAXLEN);
+
+  /* Update the custom data */
+  this->position    = next_direntry->creation_order;
+  this->custom_data = next_direntry;
+
+  return SOS_OK;
+}
+
+
+static struct sos_fs_ops_opened_file virtfs_ops_opened_file =
+  (struct sos_fs_ops_opened_file){
+    .seek     = virtfs_seek,
+    .read     = virtfs_read,
+    .write    = virtfs_write,
+    .mmap     = virtfs_mmap
+  };
+
+
+static struct sos_fs_ops_opened_dir virtfs_ops_opened_dir =
+  (struct sos_fs_ops_opened_dir){
+    .readdir  = virtfs_readdir
+  };
+
+
+/* ********************************************************
+ * FS node operations
+ */
+
+static sos_ret_t virtfs_stat_node(struct sos_fs_node * this,
+				  struct sos_fs_stat * result)
+{
+  struct virtfs_node * virtfsnode = (struct virtfs_node*)this->custom_data;
+  result->st_type             = this->type;
+  result->st_storage_location = this->storage_location;
+  result->st_access_rights    = this->access_rights;
+  result->st_nlink            = this->ondisk_lnk_cnt;
+  if (this->type == SOS_FS_NODE_REGULAR_FILE)
+    result->st_size           = virtfsnode->file.size;
+  else
+    result->st_size           = 0;
+  return SOS_OK;
+}
+
+
+static sos_ret_t virtfs_truncate(struct sos_fs_node *this,
+				 sos_lsoffset_t length)
+{
+  sos_ret_t retval;
+  struct virtfs_node * virtfsnode;
+
+  virtfsnode = (struct virtfs_node*) this->custom_data;
+
+  if ( (virtfsnode->super.type != SOS_FS_NODE_REGULAR_FILE)
+       && (virtfsnode->super.type != SOS_FS_NODE_SYMLINK))
+    return -SOS_ENOSUP;
+  
+  virtfs_lock(virtfsnode);
+  retval = virtfs_resize(virtfsnode, length);
+  virtfs_unlock(virtfsnode);
+
+  return retval;
+}
+
+
+static sos_ret_t virtfs_sync_node(struct sos_fs_node *this)
+{
+  /* No backing store for virtfs */
+  return SOS_OK;
+}
+
+
+static sos_ret_t virtfs_chmod_node(struct sos_fs_node * this,
+				   sos_ui32_t access_rights)
+{
+  this->access_rights = access_rights;
+  return SOS_OK;
+}
+
+
+/** Callback when nothing (in particular no sos_fs_nscache_node) make
+    reference to the node */
+static sos_ret_t virtfs_node_destructor(struct sos_fs_node * this)
+{
+  /* This callback is called only when the fsnode is not needed
+     anymore by any process or by the kernel. But the node must remain
+     stored "on disk" as long as the FS is mounted AND the node is
+     still "on disk" */
+  if (this->ondisk_lnk_cnt <= 0)
+    {
+      struct virtfs_instance * virtfs = (struct virtfs_instance*)this->fs->custom_data;
+      struct virtfs_node * virtfsnode = (struct virtfs_node*)this->custom_data;
+
+      list_delete(virtfs->list_fsnodes, virtfsnode);
+      sos_kfree((sos_vaddr_t) this->custom_data);
+    }
+
+  return SOS_OK;
+}
+
+
+static struct sos_fs_node_ops_file virtfs_ops_file =
+  (struct sos_fs_node_ops_file){
+    .truncate = virtfs_truncate,
+    .stat     = virtfs_stat_node,
+    .sync     = virtfs_sync_node,
+    .chmod    = virtfs_chmod_node
+  };
+
+
+static sos_ret_t virtfs_new_opened_file(struct sos_fs_node * this,
+					const struct sos_process * owner,
+					sos_ui32_t open_flags,
+					struct sos_fs_opened_file ** result_of)
+{
+  struct sos_fs_opened_file * of
+    = (struct sos_fs_opened_file*)sos_kmalloc(sizeof(*of), 0);
+  if (! of)
+    return -SOS_ENOMEM;
+
+  memset(of, 0x0, sizeof(*of));
+  of->owner      = owner;
+  of->duplicate  = virtfs_duplicate_opened_file;
+  of->open_flags = open_flags;
+  of->ops_file   = & virtfs_ops_opened_file;
+  if (this->type == SOS_FS_NODE_DIRECTORY)
+    of->ops_dir  = & virtfs_ops_opened_dir;
+
+  *result_of = of;
+  return SOS_OK;
+}
+
+
+static sos_ret_t virtfs_close_opened_file(struct sos_fs_node * this,
+					  struct sos_fs_opened_file * of)
+{
+  sos_kfree((sos_vaddr_t)of);
+  return SOS_OK;
+}
+
+
+static sos_ret_t virtfs_symlink_expand(struct sos_fs_node *this,
+				       char const  ** target,
+				       sos_size_t * target_len)
+{
+  struct virtfs_node * virtfsnode = (struct virtfs_node*)this->custom_data;
+  
+  *target = virtfsnode->file.data;
+  *target_len = virtfsnode->file.size;
+
+  return SOS_OK;
+}
+
+
+static struct sos_fs_node_ops_symlink virtfs_ops_symlink
+  = (struct sos_fs_node_ops_symlink){
+    .expand = virtfs_symlink_expand
+  };
+
+
+static sos_ret_t virtfs_dir_lookup(struct sos_fs_node *this,
+				   const char * name, sos_ui16_t namelen,
+				   sos_ui64_t * result_storage_location)
+{
+  struct virtfs_node * virtfsnode = (struct virtfs_node*)this->custom_data;
+  struct virtfs_direntry * direntry;
+  int nbentries;
+  
+  list_foreach_forward_named(virtfsnode->dir.list_entries,
+			     direntry, nbentries,
+			     sibling_prev, sibling_next)
+    {
+      if (!memcmp(name, direntry->name, namelen) && !direntry->name[namelen])
+	{
+	  *result_storage_location = direntry->fsnode->storage_location;
+	  return SOS_OK;
+	}
+    }
+
+  return -SOS_ENOENT;
+}
+
+
+static sos_ret_t virtfs_link(struct sos_fs_node *this,
+			     const struct sos_process *actor,
+			     const char * entry_name, sos_ui16_t entry_namelen,
+			     struct sos_fs_node * node)
+{
+  struct virtfs_node * parent = (struct virtfs_node*)this->custom_data;
+  struct virtfs_direntry * direntry;
+
+  direntry = (struct virtfs_direntry*)sos_kmalloc(sizeof(*direntry), 0);
+  if (! direntry)
+    return -SOS_ENOMEM;
+
+  direntry->name = (char*)sos_kmalloc(entry_namelen + 1, 0);
+  if (! direntry->name)
+    {
+      sos_kfree((sos_vaddr_t)direntry->name);
+      return -SOS_ENOMEM;
+    }
+
+  memcpy(direntry->name, entry_name, entry_namelen);
+  direntry->name[entry_namelen] = '\0';
+
+  direntry->fsnode = node;
+  node->ondisk_lnk_cnt ++;
+  this->ondisk_lnk_cnt ++;
+  list_add_tail_named(parent->dir.list_entries, direntry,
+		      sibling_prev, sibling_next);
+
+  /* Update the index of the new entry in order for the next readdirs
+     to fetch this new entry */
+  parent->dir.top_creation_order ++;
+  direntry->creation_order = parent->dir.top_creation_order;
+
+  return SOS_OK;
+}
+
+
+static sos_ret_t
+virtfs_unlink(struct sos_fs_node *this,
+	      const struct sos_process *actor,
+	      const char * entry_name, sos_ui16_t entry_namelen)
+{
+  struct virtfs_node * parent = (struct virtfs_node*)this->custom_data;
+  struct virtfs_direntry * direntry;
+  int nbentries;
+  
+  list_foreach_forward_named(parent->dir.list_entries,
+			     direntry, nbentries,
+			     sibling_prev, sibling_next)
+    {
+      if (!memcmp(entry_name, direntry->name, entry_namelen)
+	  && !direntry->name[entry_namelen])
+	{
+	  list_delete_named(parent->dir.list_entries, direntry,
+			    sibling_prev, sibling_next);
+	  direntry->fsnode->ondisk_lnk_cnt --;
+	  this->ondisk_lnk_cnt --;
+	  sos_kfree((sos_vaddr_t)direntry);
+	  return SOS_OK;
+	}
+    }
+
+  return -SOS_ENOENT;
+}
+
+
+static struct sos_fs_node_ops_dir virtfs_ops_dir
+  = (struct sos_fs_node_ops_dir){
+    .lookup = virtfs_dir_lookup,
+    .link   = virtfs_link,
+    .unlink = virtfs_unlink
+  };
+
+
+/* ********************************************************
+ * FS instance operations
+ */
+
+
+/** Simulate the access to a node located on disk. In virtfs, the
+    "disk" is directly the kernel memory, so the
+    "sos_fs_node::storage_location field corresponds to a kernel
+    address */
+static sos_ret_t
+virtfs_fetch_node_from_disk(struct sos_fs_manager_instance * this,
+			    sos_ui64_t storage_location,
+			    struct sos_fs_node ** result)
+{
+  /* The "disk" is simply the ram */
+  struct virtfs_node * virtfsnode;
+
+  virtfsnode = (struct virtfs_node *)((sos_vaddr_t)storage_location);
+  *result = & virtfsnode->super;
+
+  return SOS_OK;
+}
+
+
+static sos_ret_t
+virtfs_allocate_new_node(struct sos_fs_manager_instance * this,
+			 sos_fs_node_type_t type,
+			 const struct sos_process * creator,
+			 sos_ui32_t access_rights,
+			 sos_ui32_t flags,
+			 struct sos_fs_node ** result)
+{
+  struct virtfs_node * virtfsnode;
+
+  /* Allow only DIRs or FILEs */
+  if ((type != SOS_FS_NODE_REGULAR_FILE)
+      && (type != SOS_FS_NODE_SYMLINK)
+      && (type != SOS_FS_NODE_DIRECTORY))
+    return -SOS_ENOSUP;
+
+  virtfsnode = (struct virtfs_node*) sos_kmalloc(sizeof(*virtfsnode), 0);
+  if (! virtfsnode)
+    return -SOS_ENOMEM;
+
+  memset(virtfsnode, 0x0, sizeof(*virtfsnode));
+  *result = & virtfsnode->super;
+
+  /* Initialize "file" */
+  (*result)->inmem_ref_cnt     = 1;
+  (*result)->custom_data       = virtfsnode;
+  (*result)->storage_location  = (sos_ui64_t)((sos_vaddr_t)virtfsnode);
+  (*result)->type              = type;
+  (*result)->access_rights     = access_rights;
+  (*result)->destructor        = virtfs_node_destructor;
+  (*result)->new_opened_file   = virtfs_new_opened_file;
+  (*result)->close_opened_file = virtfs_close_opened_file;
+  (*result)->ops_file          = & virtfs_ops_file;
+  if (type == SOS_FS_NODE_SYMLINK)
+    (*result)->ops_symlink = & virtfs_ops_symlink;
+  else if (type == SOS_FS_NODE_DIRECTORY)
+    (*result)->ops_dir = & virtfs_ops_dir;
+
+  /* Initialize mapping structure */
+  if (type == SOS_FS_NODE_REGULAR_FILE)
+    {
+      virtfsnode->file.mapres.allowed_access_rights 
+	= SOS_VM_MAP_PROT_READ
+	| SOS_VM_MAP_PROT_WRITE
+	| SOS_VM_MAP_PROT_EXEC;
+      virtfsnode->file.mapres.custom_data    = virtfsnode;
+      virtfsnode->file.mapres.mmap           = virtfs_new_mapping;
+    }
+
+  list_add_tail(((struct virtfs_instance*)this->custom_data)->list_fsnodes,
+		virtfsnode);
+
+  return SOS_OK;
+}
+
+
+/* ********************************************************
+ * FS type (mount/umount) operations
+ */
+
+static sos_ret_t virtfs_mount(struct sos_fs_manager_type * this,
+			      struct sos_fs_node * device,
+			      const char * args,
+			      struct sos_fs_manager_instance ** mounted_fs)
+{
+  sos_ret_t retval;
+  struct virtfs_instance * fs;
+  struct sos_fs_node * fsnode_root;
+  struct sos_hash_table * hash;
+
+  *mounted_fs = (struct sos_fs_manager_instance*)NULL;
+
+  /* Create FS node hash table */
+  hash = sos_hash_create("virtfs H", struct sos_fs_node,
+			 sos_hash_ui64,
+			 sos_hash_key_eq_ui64, 17,
+			 storage_location, hlink_nodecache);
+  if (! hash)
+    return -SOS_ENOMEM;
+
+  fs = (struct virtfs_instance*)
+    sos_kmalloc(sizeof(struct virtfs_instance), 0);
+  if (! fs)
+    {
+      sos_hash_dispose(hash);
+      return -SOS_ENOMEM;
+    }
+
+  memset(fs, 0x0, sizeof(struct virtfs_instance));
+  retval = sos_kmutex_init(& fs->lock, "virtfs", SOS_KWQ_ORDER_FIFO);
+  if (SOS_OK != retval)
+    {
+      sos_hash_dispose(hash);
+      sos_kfree((sos_vaddr_t) fs);
+      return retval;
+    }
+  fs->super.custom_data          = fs;
+  fs->super.fs_type              = this;
+  fs->super.allocate_new_node    = virtfs_allocate_new_node;
+  fs->super.fetch_node_from_disk = virtfs_fetch_node_from_disk;
+  fs->super.nodecache            = hash;
+
+  retval = virtfs_allocate_new_node(& fs->super, SOS_FS_NODE_DIRECTORY,
+				    NULL,
+				    SOS_FS_READABLE | SOS_FS_WRITABLE
+				    | SOS_FS_EXECUTABLE,
+				    0,
+				    & fsnode_root);
+  if (SOS_OK != retval)
+    {
+      sos_hash_dispose(hash);
+      sos_kmutex_dispose(& fs->lock);
+      sos_kfree((sos_vaddr_t) fs);
+      return retval;
+    }
+
+  retval = sos_fs_register_fs_instance(& fs->super, fsnode_root);
+  sos_fs_unref_fsnode(fsnode_root);
+  if (SOS_OK != retval)
+    {
+      sos_hash_dispose(hash);
+      sos_kmutex_dispose(& fs->lock);
+      sos_kfree((sos_vaddr_t) fs);
+      return retval;
+    }
+
+  *mounted_fs = & fs->super;
+  return SOS_OK;
+}
+
+
+static sos_ret_t virtfs_umount(struct sos_fs_manager_type * this,
+			       struct sos_fs_manager_instance * mounted_fs)
+{
+  struct virtfs_instance * virtfs = (struct virtfs_instance*)mounted_fs->custom_data;
+
+  sos_hash_dispose(virtfs->super.nodecache);
+  while (! list_is_empty(virtfs->list_fsnodes))
+    {
+      struct virtfs_node * virtfsnode = list_pop_head(virtfs->list_fsnodes);
+      
+      if (virtfsnode->super.type == SOS_FS_NODE_REGULAR_FILE)
+	{
+	  if (virtfsnode->file.size > 0)
+	    sos_kfree((sos_vaddr_t) virtfsnode->file.data);
+	}
+      else if (virtfsnode->super.type == SOS_FS_NODE_DIRECTORY)
+	{
+	  while (! list_is_empty_named(virtfsnode->dir.list_entries,
+				       sibling_prev, sibling_next))
+	    {
+	      struct virtfs_direntry * direntry
+		= list_pop_head_named(virtfsnode->dir.list_entries,
+				      sibling_prev, sibling_next);
+	      
+	      sos_kfree((sos_vaddr_t)direntry->name);
+	      sos_kfree((sos_vaddr_t)direntry);
+	    }
+	}
+
+      sos_kfree((sos_vaddr_t)virtfsnode);
+    }
+
+  sos_fs_unregister_fs_instance(& virtfs->super);
+  sos_kmutex_dispose(& virtfs->lock);
+  sos_kfree((sos_vaddr_t)virtfs);
+  return SOS_OK;
+}
+
+
+/* ********************************************************
+ * File mapping stuff
+ */
+inline static struct virtfs_node *
+get_virtfsnode_of_vr(struct sos_umem_vmm_vr * vr)
+{
+  struct sos_umem_vmm_mapped_resource *mr
+    = sos_umem_vmm_get_mapped_resource_of_vr(vr);
+
+  return (struct virtfs_node *)mr->custom_data;
+}
+
+
+static void virtfs_map_ref(struct sos_umem_vmm_vr * vr)
+{
+  struct virtfs_node * virtfsnode = get_virtfsnode_of_vr(vr);
+  sos_fs_ref_fsnode(& virtfsnode->super);
+  virtfsnode->file.num_mappings ++;
+}
+
+
+static void virtfs_map_unref(struct sos_umem_vmm_vr * vr)
+{
+  struct virtfs_node * virtfsnode = get_virtfsnode_of_vr(vr);
+
+  SOS_ASSERT_FATAL(virtfsnode->file.num_mappings > 0);
+  virtfsnode->file.num_mappings --;
+
+  _sos_fs_unref_fsnode(& virtfsnode->super);
+}
+
+
+static sos_ret_t virtfs_map_page_in(struct sos_umem_vmm_vr * vr,
+				    sos_uaddr_t uaddr,
+				    sos_bool_t write_access)
+{
+  struct virtfs_node * virtfsnode = get_virtfsnode_of_vr(vr);
+  sos_luoffset_t offset = uaddr - sos_umem_vmm_get_start_of_vr(vr);
+  sos_ret_t retval = SOS_OK;
+  sos_paddr_t ppage_paddr;
+
+  /* The region is not allowed to be resized */
+  if (SOS_PAGE_ALIGN_SUP(offset) > virtfsnode->file.size)
+    return -SOS_EFAULT;
+
+  /* Lookup physical kernel page */
+  ppage_paddr = sos_paging_get_paddr(SOS_PAGE_ALIGN_INF(virtfsnode->file.data
+							+ offset));
+
+  /* Cannot access unmapped kernel pages */
+  if (! ppage_paddr)
+    return -SOS_EFAULT;
+  
+  /* Remap it in user space */
+  retval = sos_paging_map(ppage_paddr,
+			  SOS_PAGE_ALIGN_INF(uaddr),
+			  TRUE,
+			  sos_umem_vmm_get_prot_of_vr(vr));
+
+  return retval;
+}
+
+
+static struct sos_umem_vmm_vr_ops virtfs_map_ops
+  = (struct sos_umem_vmm_vr_ops){
+    .ref     = virtfs_map_ref,
+    .unref   = virtfs_map_unref,
+    .page_in = virtfs_map_page_in
+  };
+
+static sos_ret_t virtfs_new_mapping(struct sos_umem_vmm_vr *vr)
+{
+  struct virtfs_node * virtfsnode = get_virtfsnode_of_vr(vr);
+  sos_size_t reqsize;
+  sos_ret_t retval;
+
+  reqsize  = sos_umem_vmm_get_offset_in_resource(vr);
+  reqsize += sos_umem_vmm_get_size_of_vr(vr);
+
+  /* Resize the region NOW */
+  if (reqsize > virtfsnode->file.size)
+    {
+      retval = virtfs_resize(virtfsnode,
+			     SOS_PAGE_ALIGN_SUP(reqsize));
+      if (SOS_OK != retval)
+	return retval;
+    }
+
+  return sos_umem_vmm_set_ops_of_vr(vr, &virtfs_map_ops);
+}
diff -ruN /tmp/sos-code-article7.5/drivers/fs_virtfs.h ../sos-code-article8/drivers/fs_virtfs.h
--- /tmp/sos-code-article7.5/drivers/fs_virtfs.h	1970-01-01 01:00:00.000000000 +0100
+++ ../sos-code-article8/drivers/fs_virtfs.h	2005-07-01 16:39:47.000000000 +0200
@@ -0,0 +1,40 @@
+/* Copyright (C) 2005 David Decotigny
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License
+   as published by the Free Software Foundation; either version 2
+   of the License, or (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+   USA. 
+*/
+#ifndef _SOS_FS_VIRTFS_H_
+#define _SOS_FS_VIRTFS_H_
+
+#include <sos/errno.h>
+
+
+/**
+ * @file fs_virtfs.h
+ *
+ * Fake test file system. All the sos_fs_nodes and their contents are
+ * stored in kernel memory. Thus, with "virtfs", the "disk" is
+ * actually the kernel memory. In a sos_fs_node, the storage_location
+ * field corresponds to the kernel address of the corresponding struct
+ * sos_fs_node.
+ */
+
+
+/**
+ * Solely register the "virtfs" FS type into the list of supported FS types
+ */
+sos_ret_t sos_fs_virtfs_subsystem_setup();
+
+#endif
diff -ruN /tmp/sos-code-article7.5/drivers/zero.c ../sos-code-article8/drivers/zero.c
--- /tmp/sos-code-article7.5/drivers/zero.c	2005-04-27 20:17:13.000000000 +0200
+++ ../sos-code-article8/drivers/zero.c	2005-07-01 16:39:47.000000000 +0200
@@ -109,7 +109,7 @@
   zero_resource
     = (struct zero_mapped_resource*)
     sos_umem_vmm_get_mapped_resource_of_vr(vr)->custom_data;
-  
+
   /* Decrement ref coutner */
   SOS_ASSERT_FATAL(zero_resource->ref_cnt > 0);
   zero_resource->ref_cnt --;
@@ -121,9 +121,8 @@
       struct zero_mapped_page *zmp;
       list_collapse(zero_resource->list_mapped_pages, zmp)
 	{
-	  /* No need to free the underlying physical pages, since they
-	     should have been unmapped just before this unref is
-	     called */
+	  /* Unreference the underlying physical page */
+	  sos_physmem_unref_physpage(zmp->ppage_paddr);
 	  sos_kfree((sos_vaddr_t)zmp);
 	}
 
@@ -169,6 +168,7 @@
 				  SOS_PAGE_ALIGN_INF(uaddr),
 				  TRUE,
 				  vr_prot);
+
 	  return retval;
 	}
     }
@@ -182,7 +182,7 @@
       ppage_paddr = sos_physmem_ref_physpage_new(FALSE);
       if (! ppage_paddr)
 	return -SOS_ENOMEM;
-      
+
       retval = sos_paging_map(ppage_paddr,
 			      SOS_PAGE_ALIGN_INF(uaddr),
 			      TRUE,
@@ -286,6 +286,7 @@
   zmp->ppage_paddr = ppage_paddr;
 
   list_add_head(mr->list_mapped_pages, zmp);
+  sos_physmem_ref_physpage_at(ppage_paddr);
   return SOS_OK;
 }
 
diff -ruN /tmp/sos-code-article7.5/extra/Makefile ../sos-code-article8/extra/Makefile
--- /tmp/sos-code-article7.5/extra/Makefile	2005-04-27 20:17:13.000000000 +0200
+++ ../sos-code-article8/extra/Makefile	2005-07-01 16:39:47.000000000 +0200
@@ -1,4 +1,6 @@
 OBJCOPY=objcopy
+LIBGCC := $(shell $(CC) -print-libgcc-file-name) # To benefit from FP/64bits artihm.
+EXTRA := $(shell [ -f ../userland/userprogs.kimg ] && echo ../userland/userprogs.kimg)
 
 all: sos_qemu.img
 
@@ -31,7 +33,8 @@
 # above) because they share some symbol definitions
 sos_bsect.elf: bootsect.o compile_kernel
 	$(LD) --warn-common -T ./sos_bsect.lds -o $@ \
-		bootsect.o $(wildcard ../hwcore/*.o ../drivers/*.o ../sos/*.o)
+		bootsect.o $(wildcard ../hwcore/*.o ../drivers/*.o ../sos/*.o)\
+		$(EXTRA) $(LIBGCC)
 
 compile_kernel:
 	$(MAKE) -C ..
diff -ruN /tmp/sos-code-article7.5/extra/README ../sos-code-article8/extra/README
--- /tmp/sos-code-article7.5/extra/README	2005-04-27 20:17:13.000000000 +0200
+++ ../sos-code-article8/extra/README	2005-07-01 16:39:48.000000000 +0200
@@ -65,8 +65,15 @@
  - for cross-architecture compilation: see above
  - cd to this extra/ directory
  - run 'make'
- - the floppy image is: sos_bsect.img
- NOTE : SOS will not boot correctly this way after article 2 !
+ - the floppy image is: sos_bsect.img for use with bochs or on a real
+   floppy disk
+   to use the image under qemu: use sos_qemu.img
+
+ NOTE : From article 2 onward, be warned that using this bootsect
+        might lead to system crashes. This would be because the
+        solution we use to retrieve the RAM size might not work
+        properly on some systems (BIOS buggy or more than 1G RAM). THE
+        best way to boot SOS is always to use Grub.
 
 
 --
diff -ruN /tmp/sos-code-article7.5/extra/bootsect.S ../sos-code-article8/extra/bootsect.S
--- /tmp/sos-code-article7.5/extra/bootsect.S	2005-04-27 20:17:13.000000000 +0200
+++ ../sos-code-article8/extra/bootsect.S	2005-07-01 16:39:47.000000000 +0200
@@ -1,10 +1,11 @@
 
 /*
- * @(#) $Id: bootsect.S,v 1.8 2004/11/20 16:00:11 d2 Exp $
+ * @(#) $Id: bootsect.S,v 1.11 2005/04/28 21:55:35 d2 Exp $
  * Description : Bootsecteur en syntaxe AT&T
  * Auteurs : Thomas Petazzoni & Fabrice Gautier & Emmanuel Marty
  *	     Jerome Petazzoni & Bernard Cassagne & coffeeman
- *	     David Decotigny
+ *	     David Decotigny (SOS integration for kernel size detection)
+ *           Christopher Goyet (RAM size determination through BIOS int 15H)
  * Bug reports to kos-misc@enix.org
  */
 
@@ -37,8 +38,8 @@
   *   valide
   * - Le bootsect verifie que le processeur est du type 386+
   * - Il charge le noyau depuis la disquette en memoire a partir de
-  *   0x1000 (LOAD_ADRESS). Le noyau peut au max tenir sur
-  *   SECTORS_TO_LOAD secteurs
+  *   0x1000 (LOAD_ADRESS). La place dispo est donc 0x9f000 - 0x1000 , soit
+  *   0x9E000, soit encore 1264 secteurs de 512 octets
   * - Il passe en pmode flat (apres ouverture a20)
   * - Il recopie le noyau (situe en LOAD_ADRESS) vers son adresse
   *   finale (FINAL_ADDRESS = 2Mo). La recopie se fait sur tout l'espace
@@ -67,8 +68,6 @@
 	/* Pour que gas genere du 16bits, afin que ca marche en realm */
 	.code16
 
-#define SECTORS_TO_LOAD 128 /* 64 ko */ /* MAX=1264 */
-
 /*
  * Parametres de la disquette. Comme c'est chiant de faire une
  * procedure de detection auto, et que ca prend de la place, on fait
@@ -88,7 +87,8 @@
 					  copier le bootsecteur */
 #define LOAD_ADRESS 0x01000  	       /* 1er chargement du systeme */
 #define LOAD_SEG (LOAD_ADRESS>>4)      /* Segment du 1er chargement du */
-#define MAX_KERN_LEN COPY_ADRESS-LOAD_ADRESS /* Taille noyau maxi */
+#define MAX_KERN_LEN (COPY_ADRESS-LOAD_ADRESS) /* Taille noyau maxi */
+#define MAX_KERN_SECTS ((MAX_KERN_LEN + 511) / 512) /* Nbre de secteurs maxi */
 
 /* IMPORTANT : Cette valeur DOIT etre identique a l'adresse presente
 	       dans sos.lds ! */
@@ -150,7 +150,21 @@
 	/* Efface l'ecran */
 	movb $0x0, %ah
 	movb $0x3, %al
-	int 	$0x10
+	int  $0x10
+
+	/* Verifie que le noyau n'est pas trop gros a charger */
+	cmpw $MAX_KERN_SECTS, (load_size)
+	jb sizeOk
+	movw $toobig, %si
+	call message
+	call halt
+
+sizeOk:	
+	/* Recupere la taille de la RAM */
+	mov $0xE801, %ax
+	int $0x15
+	movw %ax, (memsize1)
+	movw %bx, (memsize2)
 
 	/* Affiche les messages d'attente */
 	movw $loadkern, %si
@@ -313,6 +327,19 @@
 	movl %eax, %ss
 	movl $(stack + BOOT_STACK_SIZE), %ebp
 	movl %ebp, %esp
+
+/* passe les arguments a sos */                                       
+	xor %eax, %eax
+	xor %ebx, %ebx
+	movw (COPY_ADRESS+(memsize2)), %ax /*eax = num de block de 64KB apres 16MB*/
+	movw (COPY_ADRESS+(memsize1)), %bx /*ebx = num de block de 1KB entre 1MB et 16MB*/
+	movl $0x40, %ecx /*ecx=64 */
+	mul %ecx
+	add %ebx, %eax
+	pushl %eax  /* valeur de addr */
+	pushl $0x42244224 /* valeur de magic pour indiquer qu'on a pousse
+	                     la taille de la RAM sur la pile */
+	pushl $0 /* normalement call fait un push eip, mais la on a un jmp*/
 	
 	/* Saut vers le noyau. La GDT est en place (flat mode), les
  	 * selecteurs aussi, a20 est ouverte, et les interruptions sont
@@ -373,14 +400,19 @@
 
      /* quelques messages */
 
-loadkern:  .string      "-= S O S =- : The Simple Operating System \r\n"
-check:     .string      "Checking for a 386+ processor... "
+loadkern:  .string      "This is SOS\r\n"
+toobig:	   .string	"Image too big\r\n"
+check:     .string      "Checking 386+ processor... "
 found386:  .string      " [OK]\r\n"
 need386:   .string      " [FAILED]\r\n"
 diskerror: .string	"Disk Error\r\n"
 loading:   .string	"Loading... "
 haltmsg:   .string	"System Halted\r\n"
 
+     /* Variables pour stocker la taille de la RAM (int 0x15) */
+memsize1:  .long        0
+memsize2:  .long        0
+
 /*** Les code/données du boot secteur se terminent ICI. le marqueur de
  * fin (aa55) est ajouté automatiquement par le script ld
  * sos_bsect.lds ***/
diff -ruN /tmp/sos-code-article7.5/extra/dot.mkvars ../sos-code-article8/extra/dot.mkvars
--- /tmp/sos-code-article7.5/extra/dot.mkvars	2005-04-27 20:17:13.000000000 +0200
+++ ../sos-code-article8/extra/dot.mkvars	2005-07-01 16:39:48.000000000 +0200
@@ -6,7 +6,8 @@
 CC := i586-gnu-gcc
 LD := i586-gnu-ld
 OBJCOPY := i586-gnu-objcopy
-CFLAGS += -g
+STRIP := i586-gnu-strip
+CFLAGS += -g -O
 LIBGCC := $(shell $(CC) -print-libgcc-file-name) # To benefit from FP/64bits artihm.
 
 # Configuration of mtools
diff -ruN /tmp/sos-code-article7.5/extra/sos_bsect.lds ../sos-code-article8/extra/sos_bsect.lds
--- /tmp/sos-code-article7.5/extra/sos_bsect.lds	2005-04-27 20:17:14.000000000 +0200
+++ ../sos-code-article8/extra/sos_bsect.lds	2005-07-01 16:39:48.000000000 +0200
@@ -37,9 +37,12 @@
       /* The load_size symbol contains the size of the area (in
        * sectors, aka 512 Bytes) that the boot sector should copy from
        * the disk. The bss section is not included since it uses 0
-       * bytes on disk */
+       * bytes on disk. This variable is short (16b) because it is used
+       * by the bootsector in real mode, and this is enough because
+       * our boot sector cannot transfer more than 1264 sectors (see
+       * bootsector sources) */
       load_size = .;
-      LONG((__e_load - __b_load + 511) >> 9);
+      SHORT((__e_load - __b_load + 511) >> 9);
       /* ---> This is equivalent to ceil( (__e_load - __b_load) / 512 ) */
 
       /* At offsets 511 and 512, we set the boot sector signature (AA55h) */
diff -ruN /tmp/sos-code-article7.5/hwcore/i8254.h ../sos-code-article8/hwcore/i8254.h
--- /tmp/sos-code-article7.5/hwcore/i8254.h	2005-04-27 20:17:15.000000000 +0200
+++ ../sos-code-article8/hwcore/i8254.h	2005-07-01 16:39:48.000000000 +0200
@@ -15,8 +15,8 @@
    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
    USA. 
 */
-#ifndef _SOS_i8259_H_
-#define _SOS_i8259_H_
+#ifndef _SOS_i8254_H_
+#define _SOS_i8254_H_
 
 #include <sos/errno.h>
 
@@ -32,4 +32,4 @@
 /** Change timer interrupt (IRQ 0) frequency */
 sos_ret_t sos_i8254_set_frequency(unsigned int freq);
 
-#endif /* _SOS_i8259_H_ */
+#endif /* _SOS_i8254_H_ */
diff -ruN /tmp/sos-code-article7.5/hwcore/ioports.h ../sos-code-article8/hwcore/ioports.h
--- /tmp/sos-code-article7.5/hwcore/ioports.h	2005-04-27 20:17:15.000000000 +0200
+++ ../sos-code-article8/hwcore/ioports.h	2005-07-01 16:39:48.000000000 +0200
@@ -43,4 +43,23 @@
   _v;                                                           \
 })
 
+// write value (word) on port
+#define outw(value, port)					\
+  __asm__ volatile (	       					\
+	"outw %w0,%w1"						\
+	::"a" (value),"Nd" (port)				\
+	)							\
+
+// read one word from port
+#define inw(port)						\
+({								\
+  unsigned int _v;						\
+  __asm__ volatile (						\
+	"inw %w1,%w0"						\
+	:"=a" (_v)						\
+	:"Nd" (port)						\
+	);							\
+  _v;								\
+})
+
 #endif /* _SOS_IOPORTS_H_ */
diff -ruN /tmp/sos-code-article7.5/hwcore/mm_context.c ../sos-code-article8/hwcore/mm_context.c
--- /tmp/sos-code-article7.5/hwcore/mm_context.c	2005-04-27 20:17:15.000000000 +0200
+++ ../sos-code-article8/hwcore/mm_context.c	2005-07-01 16:39:48.000000000 +0200
@@ -152,6 +152,7 @@
   mmctxt->paddr_PD = sos_paging_get_paddr(mmctxt->vaddr_PD);
   if (mmctxt->paddr_PD == 0)
     {
+      sos_kmem_cache_free((sos_vaddr_t) mmctxt->vaddr_PD);
       sos_kmem_cache_free((sos_vaddr_t) mmctxt);
       return NULL;
     }
@@ -160,6 +161,7 @@
   if (SOS_OK != sos_paging_copy_kernel_space(mmctxt->vaddr_PD,
 					     current_mm_context->vaddr_PD))
     {
+      sos_kmem_cache_free((sos_vaddr_t) mmctxt->vaddr_PD);
       sos_kmem_cache_free((sos_vaddr_t) mmctxt);
       return NULL;
     }
diff -ruN /tmp/sos-code-article7.5/hwcore/swintr.h ../sos-code-article8/hwcore/swintr.h
--- /tmp/sos-code-article7.5/hwcore/swintr.h	2005-04-27 20:17:16.000000000 +0200
+++ ../sos-code-article8/hwcore/swintr.h	2005-07-01 16:39:48.000000000 +0200
@@ -31,7 +31,6 @@
  */
 #define SOS_SWINTR_SOS_SYSCALL  0x42
 
-
 #if defined(KERNEL_SOS) && !defined(ASM_SOURCE)
 
 #include <hwcore/cpu_context.h>
diff -ruN /tmp/sos-code-article7.5/sos/binfmt_elf32.c ../sos-code-article8/sos/binfmt_elf32.c
--- /tmp/sos-code-article7.5/sos/binfmt_elf32.c	2005-04-27 20:17:18.000000000 +0200
+++ ../sos-code-article8/sos/binfmt_elf32.c	2005-07-01 16:39:49.000000000 +0200
@@ -113,6 +113,25 @@
   if (offset_in_prog + size_to_copy > elf32prog_resource->size)
     size_to_copy = elf32prog_resource->size - offset_in_prog;
 
+  /* If the source page is also aligned, simply remap the kernel area
+     into user space */
+  if (SOS_IS_PAGE_ALIGNED(elf32prog_resource->vaddr + offset_in_prog))
+    {
+      sos_vaddr_t kern_vaddr = elf32prog_resource->vaddr + offset_in_prog;
+
+      ppage_paddr = sos_paging_get_paddr(kern_vaddr);
+
+      /* Remap it in user space, in read-only mode (to force COW) */
+      retval = sos_paging_map(ppage_paddr,
+			      upage_uaddr,
+			      TRUE,
+			      access_rights & ~SOS_VM_MAP_PROT_WRITE);
+      SOS_ASSERT_FATAL(SOS_OK == retval);
+    }
+
+  /* Otherwise we need to allocate a new page */
+  else
+    {
       /* Allocate a new page that contains the code/data of the
 	 program */
       ppage_paddr = sos_physmem_ref_physpage_new(FALSE);
@@ -140,6 +159,7 @@
       if (! (access_rights & SOS_VM_MAP_PROT_WRITE))
 	return sos_paging_set_prot(upage_uaddr,
 				   access_rights & ~SOS_VM_MAP_PROT_WRITE);
+    }
    
   return retval;
 }
diff -ruN /tmp/sos-code-article7.5/sos/errno.h ../sos-code-article8/sos/errno.h
--- /tmp/sos-code-article7.5/sos/errno.h	2005-04-27 20:17:16.000000000 +0200
+++ ../sos-code-article8/sos/errno.h	2005-07-01 16:39:49.000000000 +0200
@@ -25,16 +25,27 @@
  */
 
 /* Positive values of the error codes */
-#define SOS_OK     0   /* No error */
-#define SOS_EINVAL 1   /* Invalid argument */
-#define SOS_ENOSUP 2   /* Operation not supported */
-#define SOS_ENOMEM 3   /* No available memory */
-#define SOS_EBUSY  4   /* Object or device still in use */
-#define SOS_EINTR  5   /* Wait/Sleep has been interrupted */
-#define SOS_EPERM  6   /* Mutex/files ownership error */
-#define SOS_EFAULT 7   /* Unresolved virtual memory fault */
-#define SOS_ENOENT 8   /* No such file or directory */
-#define SOS_EFATAL 255 /* Internal fatal error */
+#define SOS_OK            0   /* No error */
+#define SOS_EINVAL        1   /* Invalid argument */
+#define SOS_ENOSUP        2   /* Operation not supported */
+#define SOS_ENOMEM        3   /* No available memory */
+#define SOS_EBUSY         4   /* Object or device still in use */
+#define SOS_EINTR         5   /* Wait/Sleep has been interrupted */
+#define SOS_EPERM         6   /* Mutex/files ownership error */
+#define SOS_EFAULT        7   /* Unresolved virtual memory fault */
+#define SOS_ENOENT        8   /* No such file or directory */
+#define SOS_ELOOP         9   /* symlink resolution loop / too recursive */
+#define SOS_EEXIST       10   /* File already exists */
+#define SOS_EACCES       11   /* Permission denied */
+#define SOS_ENOTDIR      12   /* Dir does not exist */
+#define SOS_ENAMETOOLONG 13
+#define SOS_EXDEV        14   /* Cannot link entries across different FS */
+#define SOS_EISDIR       15   /* Directories not allowed in operation */
+#define SOS_ENOTEMPTY    16
+#define SOS_ENODEV       17   /* No such device */
+#define SOS_EBADF        18   /* Bad file descriptor */
+#define SOS_EMFILE       19   /* Reached maximal opened file for process */
+#define SOS_EFATAL      255 /* Internal fatal error */
 
 /* A negative value means that an error occured.  For
  *  example -SOS_EINVAL means that the error was "invalid
diff -ruN /tmp/sos-code-article7.5/sos/fs.c ../sos-code-article8/sos/fs.c
--- /tmp/sos-code-article7.5/sos/fs.c	1970-01-01 01:00:00.000000000 +0100
+++ ../sos-code-article8/sos/fs.c	2005-07-01 16:39:49.000000000 +0200
@@ -0,0 +1,2098 @@
+/* Copyright (C) 2005      David Decotigny
+   Copyright (C) 2000-2005 The KOS Team (Thomas Petazzoni, David
+                           Decotigny, Julien Munier)
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License
+   as published by the Free Software Foundation; either version 2
+   of the License, or (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+   USA. 
+*/
+
+#include <sos/assert.h>
+#include <sos/list.h>
+#include <sos/kmem_slab.h>
+#include <sos/kmalloc.h>
+
+#include "fs.h"
+
+
+/** List of available filesystems registered in the system */
+static struct sos_fs_manager_type * fs_list = NULL;
+
+/** Last UID delivered for the FS instances */
+static sos_ui64_t last_fs_instance_uid;
+
+
+/* **********************************************************
+ * Forward declarations
+ */
+static sos_ret_t fs_fetch_node(struct sos_fs_manager_instance *fs,
+			       sos_ui64_t storage_location,
+			       struct sos_fs_node ** result_fsnode);
+
+static sos_ret_t
+fs_allocate_node(struct sos_fs_manager_instance * fs,
+		 sos_fs_node_type_t type,
+		 sos_ui32_t flags,
+		 const struct sos_process * creator,
+		 sos_ui32_t access_rights,
+		 struct sos_fs_node ** result_fsnode);
+
+static sos_ret_t mark_dirty_fsnode(struct sos_fs_node * fsnode,
+				   sos_bool_t force_sync);
+
+static sos_ret_t sos_fs_sync_node(struct sos_fs_node * fsnode);
+
+static sos_ret_t sos_fs_sync_fs(struct sos_fs_manager_instance * fs);
+
+static sos_ret_t
+fs_lookup_node(const struct sos_fs_pathname * path,
+	       sos_bool_t follow_symlinks,
+	       const struct sos_fs_nscache_node * root_nsnode,
+	       const struct sos_fs_nscache_node * start_nsnode,
+	       struct sos_fs_nscache_node ** result_nsnode,
+	       struct sos_fs_pathname * result_remaining_path,
+	       int lookup_recursion_level);
+
+static sos_ret_t
+fs_resolve_symlink(const struct sos_fs_nscache_node * root_nsnode,
+		   const struct sos_fs_nscache_node * symlink_nsnode,
+		   struct sos_fs_nscache_node ** target_nsnode,
+		   int lookup_recursion_level);
+
+static sos_ret_t
+fs_register_child_node(const struct sos_process * creator,
+		       struct sos_fs_nscache_node * parent_nsnode,
+		       const struct sos_fs_pathname * name,
+		       struct sos_fs_node * fsnode,
+		       sos_ui32_t flags,
+		       struct sos_fs_nscache_node ** result_nsnode);
+
+static sos_ret_t
+fs_create_child_node(struct sos_fs_nscache_node * parent_nsnode,
+		     const struct sos_fs_pathname * name,
+		     sos_fs_node_type_t type,
+		     sos_ui32_t flags,
+		     const struct sos_process * creator,
+		     sos_ui32_t access_rights,
+		     struct sos_fs_nscache_node ** result_nsnode);
+
+static sos_ret_t
+fs_connect_existing_child_node(const struct sos_process * creator,
+			       struct sos_fs_nscache_node * parent_nsnode,
+			       const struct sos_fs_pathname * name,
+			       struct sos_fs_nscache_node * nsnode);
+
+static sos_ret_t
+fs_create_node(const struct sos_fs_pathname * _path,
+	       const struct sos_process * creator,
+	       sos_ui32_t access_rights,
+	       sos_fs_node_type_t type,
+	       struct sos_fs_nscache_node ** result_nsnode);
+
+static sos_ret_t
+fs_remove_node(const struct sos_process * actor,
+	       struct sos_fs_nscache_node * nsnode);
+
+
+/* **********************************************************
+ * Basic low-level memory & co related functions
+ */
+
+sos_ret_t sos_fs_subsystem_setup(const char * root_device,
+				 const char * fsname,
+				 const char * mount_args,
+				 struct sos_fs_manager_instance ** result_rootfs)
+{
+  sos_ret_t retval;
+  struct sos_fs_manager_type * fs_type;
+  struct sos_fs_manager_instance * new_fs;
+  struct sos_fs_node * rdev_fsnode;
+  int nb_fstypes;
+
+  /* root_device is ignored for now */
+  rdev_fsnode    = NULL;
+
+  last_fs_instance_uid = 0;
+  *result_rootfs = NULL;
+
+  retval = sos_fs_nscache_subsystem_setup();
+  if (SOS_OK != retval)
+    return retval;
+
+  /* Look for the FS manager type */
+  list_foreach(fs_list, fs_type, nb_fstypes)
+    {
+      if (! strcmp(fsname, fs_type->name))
+	break;
+    }
+  if (! list_foreach_early_break(fs_list, fs_type, nb_fstypes))
+    return -SOS_ENODEV;
+
+  retval = fs_type->mount(fs_type,
+			  rdev_fsnode,
+			  mount_args, & new_fs);
+  if (SOS_OK != retval)
+    {
+      if (rdev_fsnode)
+	sos_fs_unref_fsnode(rdev_fsnode);
+      return retval;
+    }
+
+  /* Update some reserved fields */
+  sos_fs_nscache_get_fs_node(new_fs->root)->fs = new_fs;
+
+  *result_rootfs = new_fs;
+  return SOS_OK;
+}
+
+
+sos_ret_t sos_fs_ref_fsnode(struct sos_fs_node * fsnode)
+{
+  fsnode->inmem_ref_cnt ++;
+  return SOS_OK;
+}
+
+
+sos_ret_t _sos_fs_unref_fsnode(struct sos_fs_node * node)
+{
+  SOS_ASSERT_FATAL(node->inmem_ref_cnt > 0);
+
+  /* Commit the changes the the FS when the last reference is being
+     removed */
+  if ((node->inmem_ref_cnt == 1) && (node->dirty))
+    {
+      SOS_ASSERT_FATAL(SOS_OK == sos_fs_sync_node(node));
+    }
+      
+  node->inmem_ref_cnt --;
+
+  if (node->inmem_ref_cnt == 0)
+    {
+      sos_hash_remove(node->fs->nodecache, node);
+      node->destructor(node);
+    }
+
+  return SOS_OK;
+}
+
+
+sos_ret_t sos_fs_ref_opened_file(struct sos_fs_opened_file * of)
+{
+  of->ref_cnt ++;
+  return SOS_OK;
+}
+
+
+sos_ret_t _sos_fs_unref_opened_file(struct sos_fs_opened_file ** _of)
+{
+  struct sos_fs_opened_file * of = *_of;
+  *_of = NULL;
+
+  SOS_ASSERT_FATAL(of->ref_cnt > 0);
+  of->ref_cnt --;
+
+  if (0 == of->ref_cnt)
+    {
+      sos_ret_t retval;
+      struct sos_fs_nscache_node * nsnode = of->direntry;
+      struct sos_fs_node * fsnode = sos_fs_nscache_get_fs_node(nsnode);
+
+      retval = fsnode->close_opened_file(fsnode, of);
+      if (SOS_OK != retval)
+	return retval;
+      
+      return sos_fs_nscache_unref_node(nsnode);
+    }
+
+  return SOS_OK;
+}
+
+
+/* **********************************************************
+ * Some helper functions
+ */
+
+/** Fetch the given fsnode from hash first, or from disk when not in
+    hash */
+static sos_ret_t fs_fetch_node(struct sos_fs_manager_instance *fs,
+			       sos_ui64_t storage_location,
+			       struct sos_fs_node ** result_fsnode)
+{
+  sos_ret_t retval;
+
+  /* If it is already loaded in memory, no need to look further */
+  *result_fsnode = (struct sos_fs_node*)
+    sos_hash_lookup(fs->nodecache,
+		    & storage_location);
+  if (*result_fsnode)
+    return SOS_OK;
+
+  /* Otherwise, call the appropriate method of the FS */
+  retval = fs->fetch_node_from_disk(fs, storage_location, result_fsnode);
+  if (SOS_OK != retval)
+    return retval;
+
+  (*result_fsnode)->generation    = 0;
+  sos_hash_insert(fs->nodecache, *result_fsnode);
+  return SOS_OK;
+}
+
+
+/**
+ * Helper function to allocate a new on-disk node
+ */
+static sos_ret_t
+fs_allocate_node(struct sos_fs_manager_instance * fs,
+		 sos_fs_node_type_t type,
+		 sos_ui32_t flags,
+		 const struct sos_process * creator,
+		 sos_ui32_t access_rights,
+		 struct sos_fs_node ** result_fsnode)
+{
+  sos_ret_t retval;
+
+  /* Make sure FS is writable ! */
+  if (fs->flags & SOS_FS_MOUNT_READONLY)
+    return -SOS_EPERM;
+
+  /* Allocate the node on disk */
+  retval = fs->allocate_new_node(fs, type,
+				 creator, access_rights,
+				 flags,
+				 result_fsnode);
+  if (SOS_OK != retval)
+    return retval;
+
+  /* Update some resrved fields */
+  (*result_fsnode)->fs = fs;
+
+  /* insert it in the node cache */
+  retval = sos_hash_insert(fs->nodecache, *result_fsnode);
+  if (SOS_OK != retval)
+    {
+      sos_fs_unref_fsnode(*result_fsnode);
+      return retval;
+    }
+
+  /* Success: Consider the node as dirty */
+  mark_dirty_fsnode(*result_fsnode, FALSE);
+  return retval;
+}
+
+
+/** Helper function to add the given node in the dirty list, or to
+    write it directly to the disk if the system is mounted in SYNC
+    mode */
+static sos_ret_t mark_dirty_fsnode(struct sos_fs_node * fsnode,
+				   sos_bool_t force_sync)
+{
+  sos_ret_t retval;
+  sos_bool_t was_dirty = fsnode->dirty;
+
+  fsnode->dirty = TRUE;
+  fsnode->generation ++;
+  retval = SOS_OK;
+
+  /* If the fsnode is newly dirty, add it to the dirty list of the
+     FS */
+  if (!was_dirty && fsnode->dirty)
+    list_add_tail_named(fsnode->fs->dirty_nodes, fsnode,
+			prev_dirty, next_dirty);
+
+  if (force_sync || (fsnode->fs->flags & SOS_FS_MOUNT_SYNC))
+    {
+      /* Commit the node changes to the FS */
+      if (SOS_OK == sos_fs_sync_node(fsnode))
+	{
+	  /* Commit the FS changes to the device */
+	  if (SOS_OK
+	      == fsnode->fs->device->ops_file->sync(fsnode->fs->device))
+	    return SOS_OK;
+
+	  /* We got a problem: FORCE re-add the node to the dirty list */
+	  was_dirty = FALSE;
+	  fsnode->dirty = TRUE;
+	  retval = -SOS_EBUSY;
+	}
+    }
+
+  return retval;
+}
+
+
+/** Remove the given node from the dirty list of the FS */
+static sos_ret_t sos_fs_sync_node(struct sos_fs_node * fsnode)
+{
+  sos_ret_t retval;
+
+  if (! fsnode->dirty)
+    return SOS_OK;
+
+  retval = fsnode->ops_file->sync(fsnode);
+  if (SOS_OK != retval)
+    return retval;
+
+  /* Remove it from the dirty list */
+  list_delete_named(fsnode->fs->dirty_nodes, fsnode,
+		    prev_dirty, next_dirty);
+  fsnode->dirty = FALSE;
+
+  return SOS_OK;
+}
+
+
+/** Collapse the whole dirty list of the FS and commit the changes to
+    the underlying device */
+static sos_ret_t sos_fs_sync_fs(struct sos_fs_manager_instance * fs)
+{
+  struct sos_fs_node * fsnode;
+  while (NULL != (fsnode = list_get_head_named(fs->dirty_nodes,
+					       prev_dirty, next_dirty)))
+    {
+      sos_ret_t retval = sos_fs_sync_node(fsnode);
+      if (SOS_OK != retval)
+	return retval;
+    }
+  
+  if (NULL != fs->device)
+      return fs->device->ops_file->sync(fs->device);
+
+  return SOS_OK;
+}
+
+
+/**
+ * Resolve the given symlink: return the nsnode for the destination
+ * of the symlink, or error status for dangling symlinks
+ *
+ * @note result_nsnode is a NEW reference to the node. It should be
+ * unreferenced when unused
+ */
+static sos_ret_t
+fs_resolve_symlink(const struct sos_fs_nscache_node * root_nsnode,
+		   const struct sos_fs_nscache_node * symlink_nsnode,
+		   struct sos_fs_nscache_node ** target_nsnode,
+		   int lookup_recursion_level)
+{
+  sos_ret_t retval;
+  const struct sos_fs_nscache_node * start_nsnode;
+  struct sos_fs_node * symlink_fsnode;
+  struct sos_fs_pathname path;
+  struct sos_fs_pathname remaining;
+
+  symlink_fsnode = sos_fs_nscache_get_fs_node(symlink_nsnode);
+  retval = symlink_fsnode->ops_symlink->expand(symlink_fsnode,
+					       & path.contents,
+					       & path.length);
+  if (SOS_OK != retval)
+    return retval;
+  if (path.length <= 0)
+    return -SOS_ENOENT;
+
+  /* Absolute path for target ? */
+  if (path.contents[0] == '/')
+    start_nsnode = root_nsnode;
+  else
+    {
+      retval = sos_fs_nscache_get_parent(symlink_nsnode,
+					 (struct sos_fs_nscache_node**)& start_nsnode);
+      if (SOS_OK != retval)
+	return retval;
+    }
+
+  retval = fs_lookup_node(& path, TRUE, root_nsnode, start_nsnode,
+			  target_nsnode,
+			  & remaining, lookup_recursion_level);
+  if (SOS_OK != retval)
+    return retval;
+
+  /* The target of the symlink could not be completely opened ! */
+  if (remaining.length != 0)
+    {
+      sos_fs_nscache_unref_node(*target_nsnode);
+      return -SOS_ENOENT;
+    }
+
+  return SOS_OK;
+}
+
+
+#define MAX_LOOKUP_RECURSION_LEVEL 5
+
+
+/**
+ * @return OK in any case, except if 1/ a symlink could not be
+ * resolved, or 2/ a path "a/b" is given where "a" is not a directory,
+ * or 3/ a fsnode that should exist could not be retrieved from disk.
+ *
+ * @param result_remaining_path contains the path that could not be resolved
+ *
+ * @param result_nsnode a NEW reference to the farthest node that
+ * could be resolved. It should be unreferenced when unused
+ */
+static sos_ret_t
+fs_lookup_node(const struct sos_fs_pathname * path,
+	       sos_bool_t follow_symlinks,
+	       const struct sos_fs_nscache_node * root_nsnode,
+	       const struct sos_fs_nscache_node * start_nsnode,
+	       struct sos_fs_nscache_node ** result_nsnode,
+	       struct sos_fs_pathname * result_remaining_path,
+	       int lookup_recursion_level)
+{
+  sos_ret_t retval;
+  struct sos_fs_nscache_node * current_nsnode;
+
+  /* Make sure we did not go too deep while resolving symlinks */
+  lookup_recursion_level ++;
+  if (lookup_recursion_level > MAX_LOOKUP_RECURSION_LEVEL)
+    {
+      return -SOS_ELOOP;
+    }
+
+  if (path->length <= 0)
+    return -SOS_ENOENT;
+
+  *result_nsnode = NULL;
+  memcpy(result_remaining_path, path, sizeof(*path));
+
+  current_nsnode = (struct sos_fs_nscache_node *)start_nsnode;
+  sos_fs_nscache_ref_node(current_nsnode);
+  while (1)
+    {
+      struct sos_fs_pathname current_component, remaining;
+      struct sos_fs_nscache_node * next_nsnode = NULL;
+      sos_bool_t slashes_after_first_component;
+
+/*       dbg_dump_pathname("Before", result_remaining_path); */
+
+      /* Extract the next component of the path */
+      slashes_after_first_component
+	= sos_fs_pathname_split_path(result_remaining_path,
+				     & current_component, & remaining);
+/*       dbg_dump_pathname("After", result_remaining_path); */
+/*       dbg_dump_pathname("Cur", & current_component); */
+/*       dbg_dump_pathname("Rem", & remaining); */
+/*       sos_bochs_printf("Slash after=%d\n", slashes_after_first_component); */
+
+      /* Could resolve the whole path ? */
+      if (current_component.length == 0)
+	{
+	  /* Ok, fine, we got it ! */
+	  memcpy(result_remaining_path, & remaining, sizeof(remaining));
+	  *result_nsnode = current_nsnode;	  
+	  return SOS_OK;
+	}
+
+      /* Otherwise: make sure we reached a DIR node */
+      if (sos_fs_nscache_get_fs_node(current_nsnode)->type
+	  != SOS_FS_NODE_DIRECTORY)
+	{
+	  sos_fs_nscache_unref_node(current_nsnode);
+	  return -SOS_ENOENT;
+	}
+      
+      /* Make sure this directory is "executable" */
+      if (! (sos_fs_nscache_get_fs_node(current_nsnode)->access_rights
+	     & SOS_FS_EXECUTABLE) )
+	{
+	  sos_fs_nscache_unref_node(current_nsnode);
+	  return -SOS_EACCES;
+	}
+
+      /* If we can find the entry in the namespace cache, it is really
+	 fine ! */
+      retval = sos_fs_nscache_lookup(current_nsnode,
+				     & current_component,
+				     root_nsnode,
+				     & next_nsnode);
+      if (SOS_OK != retval)
+	{
+	  struct sos_fs_node * current_fsnode, * next_fsnode;
+	  sos_ui64_t storage_location;
+
+	  /*
+	   * Not found in the namespace cache. Must read from the
+	   * disk...
+	   */
+	  current_fsnode = sos_fs_nscache_get_fs_node(current_nsnode);
+
+	  retval = current_fsnode->ops_dir
+	    ->lookup(current_fsnode,
+		     current_component.contents,
+		     current_component.length,
+		     & storage_location);
+	  if (SOS_OK != retval)
+	    {
+	      /* Well, we cannot go further, stop here */
+	      *result_nsnode = current_nsnode;
+	      return SOS_OK;
+	    }
+
+	  /* Now retrieve this node from disk or from the cache into
+	     memory */
+	  retval = fs_fetch_node(current_fsnode->fs,
+				 storage_location, & next_fsnode);
+	  if (SOS_OK != retval)
+	    {
+	      sos_fs_nscache_unref_node(current_nsnode);
+	      return retval;
+	    }
+      
+	  /* Integrate it in the nscache */
+	  retval = sos_fs_nscache_add_new_child_node(current_nsnode,
+						     & current_component,
+						     next_fsnode,
+						     & next_nsnode);
+	  sos_fs_nscache_unref_node(current_nsnode);
+	  if (SOS_OK != retval)
+	    return retval;
+	}
+      else
+	sos_fs_nscache_unref_node(current_nsnode);
+
+      /* Reaching a symlink ? */
+      if (sos_fs_nscache_get_fs_node(next_nsnode)->type
+	  == SOS_FS_NODE_SYMLINK)
+	{
+	  /* Expand the link only for non-terminal nodes, or for the
+	     terminal node only if follow_symlinks is TRUE */
+	  if ( (remaining.length != 0)
+	       || follow_symlinks )
+	    {
+	      struct sos_fs_nscache_node * symlink_target;
+
+	      retval = fs_resolve_symlink(root_nsnode, next_nsnode,
+					  & symlink_target,
+					  lookup_recursion_level);
+	      sos_fs_nscache_unref_node(next_nsnode);
+	      if (SOS_OK != retval)
+		return retval; /* Dangling symlink */
+
+	      next_nsnode = symlink_target;
+	    }
+	}
+      
+      /* Make sure there was no slash after this component, unless
+	 this component is a directory */
+      if (slashes_after_first_component
+	  &&
+	  ( sos_fs_nscache_get_fs_node(next_nsnode)->type
+	    != SOS_FS_NODE_DIRECTORY) )
+	{
+	  sos_fs_nscache_unref_node(next_nsnode);
+	  return -SOS_ENOTDIR;
+	}
+
+      /* Ok, fine, we got it, update the path we still have to explore */
+      memcpy(result_remaining_path, & remaining, sizeof(remaining));
+      current_nsnode = next_nsnode;
+    }
+
+  sos_display_fatal_error("Should not get there");
+  return -SOS_EFATAL;
+}
+
+
+/**
+ * It is assumed that parent does not already have a child with the
+ * given name. We make sure that the "path" is a single entity (ie
+ * not "a/b")
+ * @return Error if fsnode is not on the same FS as parent_nsnode
+ */
+static sos_ret_t
+fs_register_child_node(const struct sos_process * creator,
+		       struct sos_fs_nscache_node * parent_nsnode,
+		       const struct sos_fs_pathname * name,
+		       struct sos_fs_node * fsnode,
+		       sos_ui32_t flags,
+		       struct sos_fs_nscache_node ** result_nsnode)
+{
+  sos_ret_t retval;
+  struct sos_fs_node * parent_fsnode;
+  struct sos_fs_pathname first_component, remaining;
+  sos_bool_t slashes_after_first_component = FALSE;
+
+  parent_fsnode = sos_fs_nscache_get_fs_node(parent_nsnode);
+  if (parent_fsnode->type != SOS_FS_NODE_DIRECTORY)
+    return -SOS_ENOTDIR;
+
+  if (name->length <= 0)
+    return -SOS_EINVAL;
+
+  slashes_after_first_component
+    = sos_fs_pathname_split_path(name, & first_component, & remaining);
+
+  if (fsnode->type != SOS_FS_NODE_DIRECTORY)
+    {
+      /* Make sure the given name is exactly a single path component
+	 (ie no '/') */
+      if (slashes_after_first_component)
+	return -SOS_EINVAL;
+    }
+  else
+    {
+      /* Make sure there aren't any other component behind the '/'s, if
+	 any */
+      if (remaining.length > 0)
+	return -SOS_EINVAL;
+    }
+
+  /* Make sure the parent directory is writeable */
+  if (! (parent_fsnode->access_rights & SOS_FS_WRITABLE) )
+    return -SOS_EACCES;
+
+  /* Make sure that the entries are located on the same FS */
+  if (fsnode->fs != parent_fsnode->fs)
+    return -SOS_EXDEV;
+
+  /* Make sure that the nsnode won't be destroyed */
+  sos_fs_nscache_ref_node(parent_nsnode);
+
+  /* Allocate the node in directory */
+  retval = parent_fsnode->ops_dir->link(parent_fsnode,
+					creator,
+					first_component.contents,
+					first_component.length,
+					fsnode);
+  if (SOS_OK != retval)
+    {
+      sos_fs_nscache_unref_node(parent_nsnode);
+      return retval;
+    }
+
+  /* Success: Consider the directory as dirty */
+  mark_dirty_fsnode(parent_fsnode, FALSE);
+
+  /* Allocate the node in nscache cache */
+  retval = sos_fs_nscache_add_new_child_node(parent_nsnode, & first_component,
+					     fsnode, result_nsnode);
+
+  sos_fs_nscache_unref_node(parent_nsnode);
+  return retval;
+}
+
+
+/** It is assumed that parent does not already have a child with the
+    given name. We make sure that the "path" is a single entity (ie
+    not "a/b"). Return a NEW reference to the newly-created NS node */
+static sos_ret_t
+fs_create_child_node(struct sos_fs_nscache_node * parent_nsnode,
+		     const struct sos_fs_pathname * name,
+		     sos_fs_node_type_t type,
+		     sos_ui32_t flags,
+		     const struct sos_process * creator,
+		     sos_ui32_t access_rights,
+		     struct sos_fs_nscache_node ** result_nsnode)
+{
+  sos_ret_t retval;
+  struct sos_fs_node * fsnode, * parent_fsnode;
+
+  parent_fsnode = sos_fs_nscache_get_fs_node(parent_nsnode);
+  if (parent_fsnode->type != SOS_FS_NODE_DIRECTORY)
+    return -SOS_ENOTDIR;
+
+  /* Make sure that the nsnode won't be destroyed */
+  sos_fs_nscache_ref_node(parent_nsnode);
+
+  retval = fs_allocate_node(parent_fsnode->fs, type, flags, creator,
+			    access_rights, & fsnode);
+  if (SOS_OK != retval)
+    {
+      sos_fs_nscache_unref_node(parent_nsnode);
+      return retval;
+    }
+
+  retval = fs_register_child_node(creator,
+				  parent_nsnode, name, fsnode, flags,
+				  result_nsnode);
+  sos_fs_nscache_unref_node(parent_nsnode);
+
+  /* The function does not need it anymore */
+  sos_fs_unref_fsnode(fsnode);
+
+  return retval;
+}
+
+
+/**
+ * It is assumed that parent does not already have a child with the
+ * given name, and that the new child does not have a parent yet. We
+ * make sure that the "path" is a single entity (ie not "a/b") @return
+ * Error if fsnode is not on the same FS as parent_nsnode
+ */
+static sos_ret_t
+fs_connect_existing_child_node(const struct sos_process * creator,
+			       struct sos_fs_nscache_node * parent_nsnode,
+			       const struct sos_fs_pathname * name,
+			       struct sos_fs_nscache_node * nsnode)
+{
+  sos_ret_t retval;
+  struct sos_fs_node * parent_fsnode, * fsnode;
+  struct sos_fs_pathname first_component, remaining;
+  sos_bool_t slashes_after_first_component = FALSE;
+
+  fsnode = sos_fs_nscache_get_fs_node(nsnode);
+  parent_fsnode = sos_fs_nscache_get_fs_node(parent_nsnode);
+  if (parent_fsnode->type != SOS_FS_NODE_DIRECTORY)
+    return -SOS_ENOTDIR;
+
+  if (name->length <= 0)
+    return -SOS_EINVAL;
+
+  slashes_after_first_component
+    = sos_fs_pathname_split_path(name, & first_component, & remaining);
+
+  if (fsnode->type != SOS_FS_NODE_DIRECTORY)
+    {
+      /* Make sure the given name is exactly a single path component
+	 (ie no '/') */
+      if (slashes_after_first_component)
+	return -SOS_EINVAL;
+    }
+  else
+    {
+      /* Make sure there aren't any other component behind the '/'s, if
+	 any */
+      if (remaining.length > 0)
+	return -SOS_EINVAL;
+    }
+
+  /* Make sure the parent directory is writeable */
+  if (! (parent_fsnode->access_rights & SOS_FS_WRITABLE) )
+    return -SOS_EACCES;
+
+  /* Make sure that the entries are located on the same FS */
+  if (fsnode->fs != parent_fsnode->fs)
+    return -SOS_EXDEV;
+
+  /* Make sure that the nsnode won't be destroyed */
+  sos_fs_nscache_ref_node(parent_nsnode);
+
+  /* Allocate the node in directory */
+  retval = parent_fsnode->ops_dir->link(parent_fsnode,
+					creator,
+					first_component.contents,
+					first_component.length,
+					fsnode);
+  if (SOS_OK != retval)
+    {
+      sos_fs_nscache_unref_node(parent_nsnode);
+      return retval;
+    }
+
+  /* Success: Consider the directory as dirty */
+  mark_dirty_fsnode(parent_fsnode, FALSE);
+
+  /* Allocate the node in nscache cache */
+  retval = sos_fs_nscache_add_existing_child_node(parent_nsnode,
+						  & first_component,
+						  nsnode);
+  sos_fs_nscache_unref_node(parent_nsnode);
+  return retval;
+}
+
+
+/** Return a new reference to the new node inserted unless
+    result_nsnode is NULL */
+static sos_ret_t
+fs_create_node(const struct sos_fs_pathname * _path,
+	       const struct sos_process * creator,
+	       sos_ui32_t access_rights,
+	       sos_fs_node_type_t type,
+	       struct sos_fs_nscache_node ** result_nsnode)
+{
+  sos_ret_t retval;
+  struct sos_fs_pathname path;
+  struct sos_fs_nscache_node *nsnode, *new_nsnode;
+
+  path.contents = _path->contents;
+  path.length   = _path->length;
+
+  if (path.length <= 0)
+    return -SOS_ENOENT;
+
+  if (path.contents[0] == '/')
+    nsnode = sos_process_get_root(creator)->direntry;
+  else
+    nsnode = sos_process_get_cwd(creator)->direntry;
+
+  retval = fs_lookup_node(& path,
+			  TRUE,
+			  sos_process_get_root(creator)->direntry,
+			  nsnode,
+			  & nsnode,
+			  & path,
+			  0);
+  if (SOS_OK != retval)
+    return retval;
+
+  if (path.length <= 0)
+    {
+      /* Found the exact match ! */
+      sos_fs_nscache_unref_node(nsnode);
+      return -SOS_EEXIST;
+    }
+
+  /* Create a new entry in the file system */
+  retval = fs_create_child_node(nsnode,
+				& path,
+				type,
+				/* flags */0,
+				creator, access_rights,
+				& new_nsnode);
+  sos_fs_nscache_unref_node(nsnode);
+
+  /* node not needed by this function ? */
+  if (NULL == result_nsnode)
+    sos_fs_nscache_unref_node(new_nsnode);
+  else
+    *result_nsnode = new_nsnode;
+
+  return retval;
+}
+
+
+static sos_ret_t
+fs_remove_node(const struct sos_process * actor,
+	       struct sos_fs_nscache_node * nsnode)
+{
+  sos_ret_t retval;
+  struct sos_fs_nscache_node * parent_nsnode;
+  struct sos_fs_node * parent_fsnode;
+  struct sos_fs_pathname childname;
+
+  /* Refuse to do anything if this is the root of a mounted FS */
+  if (nsnode == sos_fs_nscache_get_fs_node(nsnode)->fs->root)
+    return -SOS_EBUSY;
+
+  retval = sos_fs_nscache_get_parent(nsnode, & parent_nsnode);
+  if (SOS_OK != retval)
+    return retval;
+  parent_fsnode = sos_fs_nscache_get_fs_node(parent_nsnode);
+
+  /* Make sure FS is writable ! */
+  if (parent_fsnode->fs->flags & SOS_FS_MOUNT_READONLY)
+    return -SOS_EPERM;
+
+  /* Make sure the parent directory is writeable */
+  if (! (parent_fsnode->access_rights & SOS_FS_WRITABLE) )
+    return -SOS_EACCES;
+
+  sos_fs_nscache_ref_node(parent_nsnode);
+
+  sos_fs_nscache_get_name(nsnode, & childname);
+  retval = parent_fsnode->ops_dir->unlink(parent_fsnode, actor,
+					  childname.contents,
+					  childname.length);
+  if (SOS_OK == retval)
+    sos_fs_nscache_disconnect_node(nsnode);
+
+  /* Unallocate the node */
+  if (SOS_OK == retval)
+    mark_dirty_fsnode(parent_fsnode, FALSE);
+
+  sos_fs_nscache_unref_node(parent_nsnode);
+  return retval;
+}
+
+
+/* **********************************************************
+ * Exported functions
+ */
+
+sos_ret_t sos_fs_new_opened_file(const struct sos_process * owner,
+				 struct sos_fs_nscache_node * nsnode,
+				 sos_ui32_t open_flags,
+				 struct sos_fs_opened_file ** result_of)
+{
+  sos_ret_t retval;
+  struct sos_fs_node * fsnode = sos_fs_nscache_get_fs_node(nsnode);
+
+  retval = fsnode->new_opened_file(fsnode, owner, open_flags, result_of);
+  if (SOS_OK != retval)
+    {
+      sos_fs_nscache_unref_node(nsnode);
+      return retval;
+    }
+  (*result_of)->ref_cnt    = 1;
+  (*result_of)->generation = 1;
+
+  retval = sos_fs_nscache_register_opened_file(nsnode, *result_of);
+  if (SOS_OK != retval)
+    {
+      fsnode->close_opened_file(fsnode, *result_of);
+      return retval;
+    }
+
+  (*result_of)->open_flags = open_flags;
+  return SOS_OK;
+}
+
+
+sos_ret_t
+sos_fs_duplicate_opened_file(struct sos_fs_opened_file * src_of,
+			     const struct sos_process * dst_proc,
+			     struct sos_fs_opened_file ** result_of)
+{
+  sos_ret_t retval = src_of->duplicate(src_of, dst_proc, result_of);
+  if (SOS_OK != retval)
+    return retval;
+
+  (*result_of)->ref_cnt    = 1;
+  (*result_of)->generation = 1;
+  retval = sos_fs_nscache_register_opened_file(src_of->direntry, *result_of);
+  if (SOS_OK != retval)
+    {
+      struct sos_fs_node * fsnode = sos_fs_nscache_get_fs_node(src_of->direntry);
+      fsnode->close_opened_file(fsnode, *result_of);
+      return retval;
+    }
+
+  return retval;
+}
+
+
+sos_ret_t sos_fs_open(const struct sos_process *owner,
+		      const char *_path,
+		      sos_size_t _pathlen,
+		      sos_ui32_t open_flags,
+		      sos_ui32_t creat_access_rights,
+		      struct sos_fs_opened_file ** of)
+{
+  sos_ret_t retval;
+  struct sos_fs_nscache_node *nsnode;
+  struct sos_fs_node * fsnode;
+  struct sos_fs_pathname path;
+
+  /* O_DIR | O_CREAT combination not supported */
+  if ((open_flags & SOS_FS_OPEN_DIRECTORY)
+      && (open_flags & SOS_FS_OPEN_CREAT))
+    return -SOS_EINVAL;
+
+  if (_pathlen <= 0)
+    return -SOS_ENOENT;
+
+  path.contents = _path;
+  path.length   = _pathlen;
+
+  if (path.contents[0] == '/')
+    nsnode = sos_process_get_root(owner)->direntry;
+  else
+    nsnode = sos_process_get_cwd(owner)->direntry;
+
+  retval = fs_lookup_node(& path,
+			  ! (open_flags & SOS_FS_OPEN_NOFOLLOW),
+			  sos_process_get_root(owner)->direntry,
+			  nsnode,
+			  & nsnode,
+			  & path,
+			  0);
+  if (SOS_OK != retval)
+    return retval;
+
+  if (path.length <= 0)
+    {
+      /* Found the exact match ! */
+      if (open_flags & SOS_FS_OPEN_EXCL)
+	{
+	  sos_fs_nscache_unref_node(nsnode);
+	  return -SOS_EEXIST;
+	}
+
+      fsnode = sos_fs_nscache_get_fs_node(nsnode);
+      if ((open_flags & SOS_FS_OPEN_DIRECTORY)
+	  && (fsnode->type != SOS_FS_NODE_DIRECTORY))
+	{
+	  sos_fs_nscache_unref_node(nsnode);
+	  return -SOS_ENOTDIR;
+	}
+    }
+  else
+    {
+      struct sos_fs_nscache_node * parent_nsnode = nsnode;
+
+      /* Did not find an exact match. Should create the node ! */
+      if (! (open_flags & SOS_FS_OPEN_CREAT))
+	{
+	  sos_fs_nscache_unref_node(parent_nsnode);
+	  return -SOS_ENOENT;
+	}
+
+      /* Create a new entry in the file system */
+      retval = fs_create_child_node(parent_nsnode,
+				    & path,
+				    SOS_FS_NODE_REGULAR_FILE,
+				    open_flags,
+				    owner,
+				    creat_access_rights,
+				    & nsnode);
+      sos_fs_nscache_unref_node(parent_nsnode);
+      if (SOS_OK != retval)
+	{
+	  return retval;
+	}
+
+      fsnode = sos_fs_nscache_get_fs_node(nsnode);
+    }
+
+  /* Recompute access rights */
+  open_flags &= ~(SOS_FS_OPEN_CREAT
+		  | SOS_FS_OPEN_EXCL
+		  | SOS_FS_OPEN_NOFOLLOW
+		  | SOS_FS_OPEN_DIRECTORY);
+  if (! (fsnode->access_rights & SOS_FS_WRITABLE))
+    open_flags &= ~(SOS_FS_OPEN_WRITE);
+  if (! (fsnode->access_rights & SOS_FS_READABLE))
+    open_flags &= ~(SOS_FS_OPEN_READ);
+  if (fsnode->fs->flags & SOS_FS_MOUNT_READONLY)
+    open_flags &= ~(SOS_FS_OPEN_READ);
+
+  /*
+   * Ok, open it right now !
+   */
+  retval = sos_fs_new_opened_file(owner, nsnode, open_flags, of);
+
+  sos_fs_nscache_unref_node(nsnode);
+  return retval;
+}
+
+
+sos_ret_t sos_fs_close(struct sos_fs_opened_file * of)
+{
+  return sos_fs_unref_opened_file(of);
+}
+
+
+sos_ret_t sos_fs_mark_dirty(struct sos_fs_opened_file * of)
+{
+  struct sos_fs_node * fsnode = sos_fs_nscache_get_fs_node(of->direntry);
+
+  /* This function should never get called if the FS is read-only */
+  SOS_ASSERT_FATAL(! (fsnode->fs->flags & SOS_FS_MOUNT_READONLY));
+
+  return mark_dirty_fsnode(fsnode, of->open_flags & SOS_FS_OPEN_SYNC);
+}
+
+
+sos_ret_t sos_fs_read(struct sos_fs_opened_file * of,
+		      sos_uaddr_t dest_buf,
+		      sos_size_t * /* in/ou */len)
+{
+  if (! (of->open_flags & SOS_FS_OPEN_READ))
+    return -SOS_EPERM;
+
+  if (! of->ops_file->read)
+    return -SOS_ENOSUP;
+
+  return of->ops_file->read(of, dest_buf, len);
+}
+
+
+sos_ret_t sos_fs_write(struct sos_fs_opened_file * of,
+		       sos_uaddr_t src_buf,
+		       sos_size_t * /* in/out */len)
+{
+  if (! (of->open_flags & SOS_FS_OPEN_WRITE))
+    return -SOS_EPERM;
+
+  if (! of->ops_file->write)
+    return -SOS_ENOSUP;
+
+  return of->ops_file->write(of, src_buf, len);
+}
+
+
+sos_ret_t sos_fs_seek(struct sos_fs_opened_file *of,
+		      sos_lsoffset_t offset,
+		      sos_seek_whence_t whence,
+		      sos_lsoffset_t * result_position)
+{
+  if (! of->ops_file->seek)
+    return -SOS_ENOSUP;
+
+  return of->ops_file->seek(of, offset, whence, result_position);
+}
+
+
+sos_ret_t sos_fs_ftruncate(struct sos_fs_opened_file *of,
+			   sos_lsoffset_t length)
+{
+  sos_ret_t retval;
+  struct sos_fs_node * fsnode = sos_fs_nscache_get_fs_node(of->direntry);
+
+  if (! (of->open_flags & SOS_FS_OPEN_WRITE))
+    return -SOS_EPERM;
+
+  if (! fsnode->ops_file->truncate)
+    return -SOS_ENOSUP;
+
+  retval = fsnode->ops_file->truncate(fsnode, length);
+  if (SOS_OK == retval)
+    mark_dirty_fsnode(fsnode, FALSE);
+
+  return retval;
+}
+
+
+sos_ret_t sos_fs_mmap(struct sos_fs_opened_file *of,
+		      sos_uaddr_t *uaddr, sos_size_t size,
+		      sos_ui32_t access_rights,
+		      sos_ui32_t flags,
+		      sos_luoffset_t offset)
+{
+  sos_ui32_t required_access = 0;
+  struct sos_fs_node * fsnode = sos_fs_nscache_get_fs_node(of->direntry);
+
+  if (! of->ops_file->mmap)
+    return -SOS_ENOSUP;
+
+  /* Translate VM requested rights into FS equivalent */
+  if (access_rights & SOS_VM_MAP_PROT_READ)
+    required_access |= SOS_FS_OPEN_READ;
+  if ( (access_rights & SOS_VM_MAP_PROT_WRITE) && (flags & SOS_VR_MAP_SHARED) )
+    required_access |= SOS_FS_OPEN_WRITE;
+  if (access_rights & SOS_VM_MAP_PROT_EXEC)
+    required_access |= SOS_FS_OPEN_READ;
+
+  /* Make sure that the opened file allowed this access */
+  if ((of->open_flags & required_access) != required_access)
+    return -SOS_EPERM;
+
+  if ( (access_rights & SOS_VM_MAP_PROT_EXEC)
+       && (fsnode->fs->flags & SOS_FS_MOUNT_NOEXEC) )
+    return -SOS_EPERM;
+
+  return of->ops_file->mmap(of, uaddr, size, access_rights, flags, offset);  
+}
+
+
+sos_ret_t sos_fs_fcntl(struct sos_fs_opened_file *of,
+		       int req_id,
+		       sos_ui32_t req_arg /* Usually: sos_uaddr_t */)
+{
+  if (! of->ops_file->fcntl)
+    return -SOS_ENOSUP;
+
+  return of->ops_file->fcntl(of, req_id, req_arg);
+}
+
+
+sos_ret_t sos_fs_readdir(struct sos_fs_opened_file * of,
+			 struct sos_fs_dirent * result)
+{
+  struct sos_fs_node * fsnode = sos_fs_nscache_get_fs_node(of->direntry);
+
+  if (fsnode->type != SOS_FS_NODE_DIRECTORY)
+    return -SOS_ENOTDIR;
+
+  return of->ops_dir->readdir(of, result);
+}
+
+
+sos_ret_t sos_fs_creat(const struct sos_process * creator,
+		       const char * _path,
+		       sos_size_t _pathlen,
+		       sos_ui32_t access_rights)
+{
+  struct sos_fs_pathname path;
+
+  path.contents = _path;
+  path.length   = _pathlen;
+
+  return fs_create_node(& path, creator, access_rights,
+			SOS_FS_NODE_REGULAR_FILE, NULL);
+}
+
+
+sos_ret_t sos_fs_link(const struct sos_process * creator,
+		      const char * _old_path,
+		      sos_size_t _old_pathlen,
+		      const char * _new_path,
+		      sos_size_t _new_pathlen)
+{
+  sos_ret_t retval;
+  struct sos_fs_nscache_node *old_nsnode, *dest_parent_nsnode, *new_nsnode;
+  struct sos_fs_node * fsnode;
+  struct sos_fs_pathname old_path, new_path;
+
+  if (_old_pathlen <= 0)
+    return -SOS_ENOENT;
+  if (_new_pathlen <= 0)
+    return -SOS_ENOENT;
+
+  /* Resolve target FS node using "old_path" */
+  old_path.contents = _old_path;
+  old_path.length   = _old_pathlen;
+
+  if (old_path.contents[0] == '/')
+    old_nsnode = sos_process_get_root(creator)->direntry;
+  else
+    old_nsnode = sos_process_get_cwd(creator)->direntry;
+
+  retval = fs_lookup_node(& old_path,
+			  FALSE /* don't follow symlink */,
+			  sos_process_get_root(creator)->direntry,
+			  old_nsnode,
+			  & old_nsnode,
+			  & old_path,
+			  0);
+  if (SOS_OK != retval)
+    return retval;
+
+  if (old_path.length > 0)
+    {
+      /* Could not resolve full path ! */
+      sos_fs_nscache_unref_node(old_nsnode);
+      return -SOS_ENOENT;
+    }
+
+  fsnode = sos_fs_nscache_get_fs_node(old_nsnode);
+
+  /* Not allowed to link directories ! */
+  if (fsnode->type == SOS_FS_NODE_DIRECTORY)
+    {
+      sos_fs_nscache_unref_node(old_nsnode);
+      return -SOS_ENOENT;
+    }
+
+  /* Resolve destination path */
+  new_path.contents = _new_path;
+  new_path.length   = _new_pathlen;
+
+  if (new_path.contents[0] == '/')
+    dest_parent_nsnode = sos_process_get_root(creator)->direntry;
+  else
+    dest_parent_nsnode = sos_process_get_cwd(creator)->direntry;
+
+  retval = fs_lookup_node(& new_path,
+			  TRUE /* Follow symlink */,
+			  sos_process_get_root(creator)->direntry,
+			  dest_parent_nsnode,
+			  & dest_parent_nsnode,
+			  & new_path,
+			  0);
+  if (SOS_OK != retval)
+    {
+      sos_fs_nscache_unref_node(old_nsnode);
+      return retval;
+    }
+
+  if (new_path.length == 0)
+    {
+      /* Found the exact match ! Not allowed to overwrite it ! */
+      sos_fs_nscache_unref_node(dest_parent_nsnode);
+      sos_fs_nscache_unref_node(old_nsnode);
+      return -SOS_EEXIST;
+    }
+
+  /* Create a new entry in the file system */
+  retval = fs_register_child_node(creator, dest_parent_nsnode, & new_path,
+				  fsnode, 0,
+				  & new_nsnode);
+
+  sos_fs_nscache_unref_node(dest_parent_nsnode);
+  sos_fs_nscache_unref_node(old_nsnode);
+  sos_fs_nscache_unref_node(new_nsnode);
+  return retval;
+}
+
+
+sos_ret_t sos_fs_rename(const struct sos_process * actor,
+			const char * _old_path,
+			sos_size_t _old_pathlen,
+			const char * _new_path,
+			sos_size_t _new_pathlen)
+{
+  sos_ret_t retval;
+  struct sos_fs_nscache_node *old_nsnode, *old_parent_nsnode,
+    *dest_parent_nsnode, *replaced_nsnode;
+  struct sos_fs_pathname old_name, replaced_name;
+  struct sos_fs_pathname old_path, new_path;
+
+  old_nsnode = old_parent_nsnode = dest_parent_nsnode = replaced_nsnode = NULL;
+
+  if (_old_pathlen <= 0)
+    return -SOS_ENOENT;
+  if (_new_pathlen <= 0)
+    return -SOS_ENOENT;
+
+  /* Resolve target FS node using "old_path" */
+  old_path.contents = _old_path;
+  old_path.length   = _old_pathlen;
+
+  if (old_path.contents[0] == '/')
+    old_nsnode = sos_process_get_root(actor)->direntry;
+  else
+    old_nsnode = sos_process_get_cwd(actor)->direntry;
+
+  retval = fs_lookup_node(& old_path,
+			  FALSE /* don't follow symlink */,
+			  sos_process_get_root(actor)->direntry,
+			  old_nsnode,
+			  & old_nsnode,
+			  & old_path,
+			  0);
+  if (SOS_OK != retval)
+    return retval;
+
+  if (old_path.length > 0)
+    {
+      /* Could not resolve full path ! */
+      sos_fs_nscache_unref_node(old_nsnode);
+      return -SOS_ENOENT;
+    }
+
+  /* Not allowed to rename mountpoints ! */
+  if (sos_fs_nscache_is_mountnode(old_nsnode))
+    {
+      sos_fs_nscache_unref_node(old_nsnode);
+      return -SOS_ENOENT;
+    }
+
+  /* Keep a reference on this node's parent, in case we must undo the
+     rename */
+  retval = sos_fs_nscache_get_parent(old_nsnode, & old_parent_nsnode);
+  if (SOS_OK != retval)
+    {
+      sos_fs_nscache_unref_node(old_nsnode);
+      return retval;
+    }
+  sos_fs_nscache_ref_node(old_parent_nsnode);
+
+  /* Resolve destination path */
+  replaced_nsnode   = NULL;
+  new_path.contents = _new_path;
+  new_path.length   = _new_pathlen;
+
+  if (new_path.contents[0] == '/')
+    dest_parent_nsnode = sos_process_get_root(actor)->direntry;
+  else
+    dest_parent_nsnode = sos_process_get_cwd(actor)->direntry;
+
+  retval = fs_lookup_node(& new_path,
+			  TRUE /* Follow symlink */,
+			  sos_process_get_root(actor)->direntry,
+			  dest_parent_nsnode,
+			  & dest_parent_nsnode,
+			  & new_path,
+			  0);
+  if (SOS_OK != retval)
+    {
+      goto undo_rename;
+    }
+
+  /* Nothing to do ? */
+  if (old_nsnode == dest_parent_nsnode)
+    {
+      sos_fs_nscache_unref_node(old_nsnode);
+      sos_fs_nscache_unref_node(old_parent_nsnode);
+      sos_fs_nscache_unref_node(dest_parent_nsnode);
+      return SOS_OK;
+    }
+
+  /* Remove old nsnode from file ns cache */
+  sos_fs_nscache_get_name(old_nsnode, & old_name);
+  retval = fs_remove_node(actor, old_nsnode);
+  if (SOS_OK != retval)
+    {
+      sos_fs_nscache_unref_node(old_nsnode);
+      sos_fs_nscache_unref_node(old_parent_nsnode);
+      sos_fs_nscache_unref_node(dest_parent_nsnode);
+      return -SOS_ENOENT;
+    }
+
+  if (new_path.length == 0)
+    {
+      /* Found the exact match ! We disconnect it from the namespace,
+	 but keep it aside */
+
+      /* Not allowed to replace directories */
+      if (sos_fs_nscache_get_fs_node(dest_parent_nsnode)->type
+	  == SOS_FS_NODE_DIRECTORY)
+	{
+	  retval = -SOS_EBUSY;
+	  goto undo_rename;
+	}
+
+      replaced_nsnode = dest_parent_nsnode;
+      dest_parent_nsnode = NULL;
+
+      /* Retrieve the parent of the node to replace */
+      retval = sos_fs_nscache_get_parent(replaced_nsnode,
+					 & dest_parent_nsnode);
+      if (SOS_OK != retval)
+	{
+	  dest_parent_nsnode = replaced_nsnode;
+	  goto undo_rename;
+	}
+
+      sos_fs_nscache_ref_node(dest_parent_nsnode);
+
+      /* Disconnect this node from its parent */
+      sos_fs_nscache_get_name(replaced_nsnode, & replaced_name);
+      retval = fs_remove_node(actor, replaced_nsnode);
+      if (SOS_OK != retval)
+	goto undo_rename;
+    }
+
+  /* Create a new entry in the file system */
+  retval = fs_connect_existing_child_node(actor, dest_parent_nsnode,
+					  & new_path,
+					  old_nsnode);
+  if (SOS_OK != retval)
+    goto undo_rename;
+
+  sos_fs_nscache_unref_node(old_nsnode);
+  sos_fs_nscache_unref_node(old_parent_nsnode);
+  sos_fs_nscache_unref_node(dest_parent_nsnode);
+  if (NULL != replaced_nsnode)
+    sos_fs_nscache_unref_node(replaced_nsnode);
+
+  return retval;
+
+ undo_rename:
+
+  /* Handle special case: the node replaced something. In case the
+     previous operation failed, we try to reinsert the replaced
+     node */
+  if (NULL != replaced_nsnode)
+    {
+      fs_connect_existing_child_node(actor, dest_parent_nsnode,
+				     & replaced_name,
+				     replaced_nsnode);
+      sos_fs_nscache_unref_node(replaced_nsnode);
+    }
+
+  fs_connect_existing_child_node(actor, old_parent_nsnode,
+				 & old_name,
+				 old_nsnode);
+  sos_fs_nscache_unref_node(old_nsnode);
+  sos_fs_nscache_unref_node(old_parent_nsnode);
+
+  if (NULL != dest_parent_nsnode)
+    sos_fs_nscache_unref_node(dest_parent_nsnode);
+
+  return retval;
+}
+
+
+sos_ret_t sos_fs_unlink(const struct sos_process * actor,
+			const char * _path,
+			sos_size_t _pathlen)
+{
+  sos_ret_t retval;
+  struct sos_fs_pathname path;
+  struct sos_fs_nscache_node * nsnode;
+
+  path.contents = _path;
+  path.length   = _pathlen;
+
+  if (path.length <= 0)
+    return -SOS_ENOENT;
+
+  if (path.contents[0] == '/')
+    nsnode = sos_process_get_root(actor)->direntry;
+  else
+    nsnode = sos_process_get_cwd(actor)->direntry;
+
+  retval = fs_lookup_node(& path, FALSE,
+			  sos_process_get_root(actor)->direntry,
+			  nsnode,
+			  & nsnode, & path, 0);
+  if (SOS_OK != retval)
+    return retval;
+
+  /* Make sure the whole path has been resolved */
+  if (path.length > 0)
+    {
+      sos_fs_nscache_unref_node(nsnode);
+      return -SOS_ENOENT;
+    }
+
+  if (sos_fs_nscache_get_fs_node(nsnode)->type == SOS_FS_NODE_DIRECTORY)
+    retval = -SOS_EISDIR;
+  else
+    retval = fs_remove_node(actor, nsnode);
+
+  sos_fs_nscache_unref_node(nsnode);
+  return retval;
+}
+
+
+sos_ret_t sos_fs_symlink(const struct sos_process * creator,
+			 const char * _path,
+			 sos_size_t _pathlen,
+			 sos_uaddr_t symlink_target,
+			 sos_size_t symlink_target_len)
+{
+  sos_ret_t retval;
+  struct sos_fs_pathname path;
+  struct sos_fs_node * fsnode;
+  struct sos_fs_nscache_node * symlink;
+  struct sos_fs_opened_file * tmp_of;
+  sos_size_t len;
+
+  path.contents = _path;
+  path.length   = _pathlen;
+
+  retval = fs_create_node(& path, creator, SOS_FS_S_IRWXALL,
+			  SOS_FS_NODE_SYMLINK, & symlink);
+  if (SOS_OK != retval)
+    return retval;
+
+  /* Artificially open the symlink to change its contents */
+  fsnode = sos_fs_nscache_get_fs_node(symlink);
+  retval = fsnode->new_opened_file(fsnode, creator,
+				   SOS_FS_OPEN_WRITE, & tmp_of);
+  if (SOS_OK != retval)
+    {
+      fs_remove_node(creator, symlink);
+      sos_fs_nscache_unref_node(symlink);
+      return retval;
+    }
+
+  tmp_of->ref_cnt = 1;
+  retval = sos_fs_nscache_register_opened_file(symlink, tmp_of);
+  if (SOS_OK != retval)
+    {
+      fsnode->close_opened_file(fsnode, tmp_of);
+      fs_remove_node(creator, symlink);
+      sos_fs_nscache_unref_node(symlink);
+      return retval;
+    }
+
+  len = symlink_target_len;
+  retval = sos_fs_write(tmp_of, symlink_target, & len);
+  mark_dirty_fsnode(fsnode, FALSE);
+  fsnode->close_opened_file(fsnode, tmp_of);
+  
+  if ((SOS_OK != retval) || (len != symlink_target_len))
+    {
+      fs_remove_node(creator, symlink);
+      if (SOS_OK == retval)
+	retval = -SOS_ENAMETOOLONG;
+    }
+
+  sos_fs_nscache_unref_node(symlink);
+  return retval;
+}
+
+
+sos_ret_t sos_fs_mkdir(const struct sos_process * creator,
+		       const char * _path,
+		       sos_size_t _pathlen,
+		       sos_ui32_t access_rights)
+{
+  struct sos_fs_pathname path;
+
+  path.contents = _path;
+  path.length   = _pathlen;
+
+  return fs_create_node(& path, creator, access_rights,
+			SOS_FS_NODE_DIRECTORY, NULL);
+}
+
+
+sos_ret_t sos_fs_rmdir(const struct sos_process * actor,
+		       const char * _path,
+		       sos_size_t _pathlen)
+{
+  sos_ret_t retval;
+  struct sos_fs_pathname path;
+  struct sos_fs_nscache_node * nsnode;
+
+  path.contents = _path;
+  path.length   = _pathlen;
+
+  if (path.length <= 0)
+    return -SOS_ENOENT;
+
+  if (path.contents[0] == '/')
+    nsnode = sos_process_get_root(actor)->direntry;
+  else
+    nsnode = sos_process_get_cwd(actor)->direntry;
+
+  retval = fs_lookup_node(& path, FALSE,
+			  sos_process_get_root(actor)->direntry,
+			  nsnode,
+			  & nsnode, & path, 0);
+  if (SOS_OK != retval)
+    return retval;
+
+  /* Make sure the whole path has been resolved */
+  if (path.length > 0)
+    {
+      sos_fs_nscache_unref_node(nsnode);
+      return -SOS_ENOENT;
+    }
+
+  /* Cannot rmdir non-dir nodes */
+  if (sos_fs_nscache_get_fs_node(nsnode)->type != SOS_FS_NODE_DIRECTORY)
+    retval = -SOS_ENOTDIR;
+
+  /* Cannot remove directory if it is still used by somebody else */
+  else if (sos_fs_nscache_get_ref_cnt(nsnode) > 1)
+    retval = -SOS_EBUSY;
+
+  /* Cannot remove directory if it is still has children stored on
+     disk */
+  else if (sos_fs_nscache_get_fs_node(nsnode)->ondisk_lnk_cnt > 1)
+    retval = -SOS_ENOTEMPTY;
+
+  /* Otherwise, yes : suppress the node on disk */
+  else
+    retval = fs_remove_node(actor, nsnode);
+
+  sos_fs_nscache_unref_node(nsnode);
+  return retval;
+}
+
+
+sos_ret_t sos_fs_stat(const struct sos_process * actor,
+		      const char * _path,
+		      sos_size_t _pathlen,
+		      int nofollow,
+		      struct sos_fs_stat * result)
+{
+  sos_ret_t retval;
+  struct sos_fs_pathname path;
+  struct sos_fs_nscache_node * nsnode;
+  struct sos_fs_node * fsnode;
+
+  path.contents = _path;
+  path.length   = _pathlen;
+
+  if (path.length <= 0)
+    return -SOS_ENOENT;
+
+  if (path.contents[0] == '/')
+    nsnode = sos_process_get_root(actor)->direntry;
+  else
+    nsnode = sos_process_get_cwd(actor)->direntry;
+
+  retval = fs_lookup_node(& path, (nofollow != 0),
+			  sos_process_get_root(actor)->direntry,
+			  nsnode,
+			  & nsnode, & path, 0);
+  if (SOS_OK != retval)
+    return retval;
+
+  /* Make sure the whole path has been resolved */
+  if (path.length > 0)
+    {
+      sos_fs_nscache_unref_node(nsnode);
+      return -SOS_ENOENT;
+    }
+
+  fsnode = sos_fs_nscache_get_fs_node(nsnode);
+  retval = fsnode->ops_file->stat(fsnode, result);
+  
+  sos_fs_nscache_unref_node(nsnode);
+  return retval;
+}
+
+
+sos_ret_t sos_fs_fsync(struct sos_fs_opened_file * of)
+{
+  struct sos_fs_node * fsnode = sos_fs_nscache_get_fs_node(of->direntry);
+  return fsnode->ops_file->sync(fsnode);
+}
+
+
+sos_ret_t sos_fs_chmod(const struct sos_process * actor,
+		       const char * _path,
+		       sos_size_t _pathlen,
+		       sos_ui32_t access_rights)
+{
+  sos_ret_t retval;
+  struct sos_fs_pathname path;
+  struct sos_fs_nscache_node * nsnode;
+  struct sos_fs_node * fsnode;
+
+  path.contents = _path;
+  path.length   = _pathlen;
+
+  if (path.length <= 0)
+    return -SOS_ENOENT;
+
+  if (path.contents[0] == '/')
+    nsnode = sos_process_get_root(actor)->direntry;
+  else
+    nsnode = sos_process_get_cwd(actor)->direntry;
+
+  retval = fs_lookup_node(& path, TRUE,
+			  sos_process_get_root(actor)->direntry,
+			  nsnode,
+			  & nsnode, & path, 0);
+  if (SOS_OK != retval)
+    return retval;
+
+  /* Make sure the whole path has been resolved */
+  if (path.length > 0)
+    {
+      sos_fs_nscache_unref_node(nsnode);
+      return -SOS_ENOENT;
+    }
+
+  fsnode = sos_fs_nscache_get_fs_node(nsnode);
+  retval = fsnode->ops_file->chmod(fsnode, access_rights);
+  if (SOS_OK == retval)
+    mark_dirty_fsnode(fsnode, FALSE);
+  
+  sos_fs_nscache_unref_node(nsnode);
+  return retval;
+}
+
+
+sos_ret_t sos_fs_vfstat(const struct sos_process * actor,
+			const char * _path,
+			sos_size_t _pathlen,
+			struct sos_fs_statfs * result)
+{
+  sos_ret_t retval;
+  struct sos_fs_pathname path;
+  struct sos_fs_nscache_node * nsnode;
+  struct sos_fs_node * fsnode;
+
+  path.contents = _path;
+  path.length   = _pathlen;
+
+  if (path.length <= 0)
+    return -SOS_ENOENT;
+
+  if (path.contents[0] == '/')
+    nsnode = sos_process_get_root(actor)->direntry;
+  else
+    nsnode = sos_process_get_cwd(actor)->direntry;
+
+  retval = fs_lookup_node(& path, FALSE,
+			  sos_process_get_root(actor)->direntry,
+			  nsnode,
+			  & nsnode, & path, 0);
+  if (SOS_OK != retval)
+    return retval;
+
+  /* Make sure the whole path has been resolved */
+  if (path.length > 0)
+    {
+      sos_fs_nscache_unref_node(nsnode);
+      return -SOS_ENOENT;
+    }
+
+  fsnode = sos_fs_nscache_get_fs_node(nsnode);
+  if (fsnode->fs->statfs)
+    retval = fsnode->fs->statfs(fsnode->fs, result);
+  else
+    retval = -SOS_ENOSUP;
+  
+  sos_fs_nscache_unref_node(nsnode);
+  return retval;  
+}
+
+
+/** This function is resilient against mounting/unmounting of other FS */
+sos_ret_t sos_fs_sync_all_fs()
+{
+  int dummy = 0;
+  sos_ui64_t uid = 0;
+
+  while (1)
+    {
+      /* Iterate over the FS types */
+      struct sos_fs_manager_type * fstype;
+      int ntype;
+      list_foreach(fs_list, fstype, ntype)
+	{
+	  /* Iterate over the FS instances */
+	  struct sos_fs_manager_instance * fs;
+	  int ninst;
+
+	  list_foreach(fstype->instances, fs, ninst)
+	    {
+	      if (fs->uid <= uid)
+		continue;
+
+	      uid = fs->uid;
+	      sos_fs_sync_fs(fs);
+
+	      /* We must NOT continue the loops because the
+		 prev/next/current fs types/instances might have been
+		 removed or added (sync blocks, by definition) ! */
+	      goto lookup_next_fs;
+	    }
+	}
+
+    lookup_next_fs:
+      /* Loop over */
+      dummy ++;
+    }
+
+  return SOS_OK;
+}
+
+
+
+/* *************************************************************
+ * mount/umount stuff
+ */
+
+
+sos_ret_t sos_fs_register_fs_instance(struct sos_fs_manager_instance * fs,
+				      struct sos_fs_node * root_fsnode)
+{
+  sos_ret_t retval;
+  struct sos_fs_nscache_node * nsnode_root;
+
+  retval = sos_fs_nscache_create_mounted_root(root_fsnode, & nsnode_root);
+  if (SOS_OK != retval)
+    return retval;
+
+  fs->uid  = ++last_fs_instance_uid;
+  fs->root = nsnode_root;
+  sos_hash_insert(fs->nodecache, root_fsnode);
+
+  list_add_tail(fs->fs_type->instances, fs);
+  return SOS_OK;
+}
+
+
+sos_ret_t sos_fs_unregister_fs_instance(struct sos_fs_manager_instance * fs)
+{
+  fs->uid = 0;
+  list_delete(fs->fs_type->instances, fs);
+  return SOS_OK;
+}
+
+
+sos_ret_t sos_fs_register_fs_type(struct sos_fs_manager_type * fstype)
+{
+  struct sos_fs_manager_type * iterator;
+  int nbtypes;
+
+  list_foreach_forward(fs_list, iterator, nbtypes)
+    {
+      if (! strncmp(fstype->name, iterator->name, SOS_FS_MANAGER_NAME_MAXLEN))
+	return -SOS_EEXIST;
+    }
+
+  list_add_tail(fs_list, fstype);
+  return SOS_OK;
+}
+
+
+sos_ret_t sos_fs_unregister_fs_type(struct sos_fs_manager_type * fstype)
+{
+  struct sos_fs_manager_type * iterator;
+  int nbtypes;
+
+  if (! list_is_empty(fstype->instances))
+    return -SOS_EBUSY;
+
+  list_foreach_forward(fs_list, iterator, nbtypes)
+    {
+      if (! strncmp(fstype->name, iterator->name, SOS_FS_MANAGER_NAME_MAXLEN))
+	{
+	  list_delete(fs_list, fstype);
+	  return SOS_OK;
+	}
+    }
+
+  return -SOS_EINVAL;
+}
+
+
+/**
+ * _src_path may be empty
+ */
+sos_ret_t sos_fs_mount(struct sos_process * actor,
+		       const char * _src_path,
+		       sos_size_t _src_pathlen,
+		       const char * _dst_path,
+		       sos_size_t _dst_pathlen,
+		       const char * fsname,
+		       sos_ui32_t mountflags,
+		       const char * args,
+		       struct sos_fs_manager_instance ** result_fs)
+{
+  sos_ret_t retval;
+  struct sos_fs_pathname src_path, dst_path;
+  struct sos_fs_nscache_node * src_nsnode, * dst_nsnode;
+  struct sos_fs_manager_type * fs_type;
+  struct sos_fs_manager_instance * new_fs;
+  int nb_fstypes;
+
+  if (_dst_pathlen <= 0)
+    return -SOS_ENOENT;
+
+  /* Look for the FS manager type */
+  list_foreach(fs_list, fs_type, nb_fstypes)
+    {
+      if (! strcmp(fsname, fs_type->name))
+	break;
+    }
+  if (! list_foreach_early_break(fs_list, fs_type, nb_fstypes))
+    return -SOS_ENODEV;
+
+  src_path.contents = _src_path;
+  src_path.length   = _src_pathlen;
+
+  /* Compute the start_nsnode for the source */
+  if (src_path.length <= 0)
+    src_nsnode = NULL;
+  else if (src_path.contents[0] == '/')
+    src_nsnode = sos_process_get_root(actor)->direntry;
+  else
+    src_nsnode = sos_process_get_cwd(actor)->direntry;
+
+  /* Lookup the source node */
+  if (src_nsnode)
+    {
+      retval = fs_lookup_node(& src_path, TRUE,
+			      sos_process_get_root(actor)->direntry,
+			      src_nsnode,
+			      & src_nsnode, & src_path, 0);
+      if (SOS_OK != retval)
+	return retval;
+
+      /* Make sure the whole path has been resolved */
+      if (src_path.length > 0)
+	{
+	  sos_fs_nscache_unref_node(src_nsnode);
+	  return -SOS_ENOENT;
+	}
+    }
+
+  dst_path.contents = _dst_path;
+  dst_path.length   = _dst_pathlen;
+
+  /* Compute the start_nsnode for the destination */
+  if (dst_path.contents[0] == '/')
+    dst_nsnode = sos_process_get_root(actor)->direntry;
+  else
+    dst_nsnode = sos_process_get_cwd(actor)->direntry;
+
+  /* Lookup the destination node */
+  retval = fs_lookup_node(& dst_path, TRUE,
+			  sos_process_get_root(actor)->direntry,
+			  dst_nsnode,
+			  & dst_nsnode, & dst_path, 0);
+  if ((SOS_OK != retval) || (dst_path.length > 0))
+    {
+      if (src_nsnode)
+	sos_fs_nscache_unref_node(src_nsnode);
+      if (dst_path.length > 0)
+	retval = -SOS_ENOENT;
+      return retval;
+    }
+
+  /* Actually call the mount callback of the FS */
+  retval
+    = fs_type->mount(fs_type,
+		     (src_nsnode)?sos_fs_nscache_get_fs_node(src_nsnode):NULL,
+		     args, & new_fs);
+  if (SOS_OK != retval)
+    {
+      if (src_nsnode)
+	sos_fs_nscache_unref_node(src_nsnode);
+      sos_fs_nscache_unref_node(dst_nsnode);
+      return retval;
+    }
+
+  /* Make sure the nodecache was created */
+  SOS_ASSERT_FATAL(NULL != new_fs->nodecache);
+  SOS_ASSERT_FATAL(NULL != new_fs->root);
+
+  /* Update some reserved fields */
+  sos_fs_nscache_get_fs_node(new_fs->root)->fs = new_fs;
+
+  /* Register the mountpoint in the nscache */
+  retval = sos_fs_nscache_mount(dst_nsnode, new_fs->root);
+  SOS_ASSERT_FATAL(SOS_OK == retval);
+
+  /* Un-reference the temporary nsnodes */
+  if (src_nsnode)
+    sos_fs_nscache_unref_node(src_nsnode);
+  sos_fs_nscache_unref_node(dst_nsnode);
+
+  if (result_fs)
+    *result_fs = new_fs;
+
+  return SOS_OK;
+}
+
+
+sos_ret_t sos_fs_umount(struct sos_process * actor,
+			const char * _mounted_root_path,
+			sos_size_t _mounted_root_pathlen)
+{
+  sos_ret_t retval;
+  struct sos_fs_pathname mounted_root_path;
+  struct sos_fs_nscache_node * mounted_root_nsnode;
+  struct sos_fs_manager_instance * fs;
+
+  if (_mounted_root_pathlen <= 0)
+    return -SOS_ENOENT;
+
+  mounted_root_path.contents = _mounted_root_path;
+  mounted_root_path.length   = _mounted_root_pathlen;
+
+  /* Compute the start_nsnode for the mounted_root */
+  if (mounted_root_path.contents[0] == '/')
+    mounted_root_nsnode = sos_process_get_root(actor)->direntry;
+  else
+    mounted_root_nsnode = sos_process_get_cwd(actor)->direntry;
+
+  /* Lookup the mounted_root node */
+  retval = fs_lookup_node(& mounted_root_path, TRUE,
+			  sos_process_get_root(actor)->direntry,
+			  mounted_root_nsnode,
+			  & mounted_root_nsnode, & mounted_root_path, 0);
+  if (SOS_OK != retval)
+    return retval;
+
+  /* Make sure the whole path has been resolved */
+  if (mounted_root_path.length > 0)
+    {
+      sos_fs_nscache_unref_node(mounted_root_nsnode);
+      return -SOS_ENOENT;
+    }
+
+  /* Make sure this node is the real root of the FS */
+  fs = sos_fs_nscache_get_fs_node(mounted_root_nsnode)->fs;
+  if (fs->root != mounted_root_nsnode)
+    {
+      sos_fs_nscache_unref_node(mounted_root_nsnode);
+      return -SOS_ENOENT;
+    }
+
+  /* Disconnect this FS mounted_root from namespace cache */
+  retval = sos_fs_nscache_umount(mounted_root_nsnode);
+
+  /* Mounted_Root not needed anymore */
+  sos_fs_nscache_unref_node(mounted_root_nsnode);
+  if (SOS_OK != retval)
+    return retval;
+
+  fs->root = NULL;
+
+  /* Flush any changes to disk */
+  retval = sos_fs_sync_fs(fs);
+  if (SOS_OK != retval)
+    {
+      return retval;
+    }
+
+  retval = fs->fs_type->umount(fs->fs_type, fs);
+  return retval;
+}
diff -ruN /tmp/sos-code-article7.5/sos/fs.h ../sos-code-article8/sos/fs.h
--- /tmp/sos-code-article7.5/sos/fs.h	1970-01-01 01:00:00.000000000 +0100
+++ ../sos-code-article8/sos/fs.h	2005-07-01 16:39:49.000000000 +0200
@@ -0,0 +1,1030 @@
+/* Copyright (C) 2005      David Decotigny
+   Copyright (C) 2000-2005 The KOS Team (Thomas Petazzoni, David
+                           Decotigny, Julien Munier)
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License
+   as published by the Free Software Foundation; either version 2
+   of the License, or (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+   USA. 
+*/
+#ifndef _SOS_FS_H_
+#define _SOS_FS_H_
+
+
+/**
+ * @file fs.h
+ *
+ * (Virtual) Filesystem management.
+ *
+ * SOS provides a complete Unix-like file system service. Supported
+ * features of this service are:
+ *   - mountpoints
+ *   - generic file system support (FS) through object-oriented
+ *     specialization (so-called VFS)
+ *   - hard & symbolic links
+ *   - regular files and directories
+ *   - block and character device special files (from article 9 onward)
+ *   - file mapping
+ *   - basic permission management ("rwx" only, no notion of owner)
+ *   - chroot
+ *   - separation of disk node and namespace notions allowing hard links
+ *     and to move/rename/remove files or directories that are in use
+ *   - basic FS interface (open/read/seek/creat/mkdir/rename/link
+ *     / symlink/chmod/mount/fcntl/ioctl...)
+ *   - deferred writes (ie file caching). @see sync(3)
+ *
+ * Among the unsupported features:
+ *   - no user-based permission (uid/gid, ACLS)
+ *   - no modification / access time accounting
+ *   - no Fifo/socket special files (yet)
+ *   - no generic support library for common fcntl commands
+ *     (F_SETOWN/GETLEASE/NOTIFY, etc.)
+ *   - no flock-style functions
+ *
+ * Rationale
+ * =========
+ * The VFS layer is based on 3 central concepts:
+ *
+ *  - The meta-information for each file stored on disk (struct
+ *    sos_fs_node for SOS, inode for Unix)
+ *
+ *    It is sufficient to know where this meta-information is located
+ *    on disk (a simple sector number most of the time) to build the
+ *    corresponding struct sos_fs_node into memory and to retrieve the
+ *    data of the file from the disk
+ *
+ *    For example, consider that we know a sector that holds the meta
+ *    information (ie size, permissions) is located at sector 64589 on
+ *    disk. By retrieving this meta information directly from disk, we
+ *    can build the struct sos_fs_node, which would (for example) tell
+ *    that the corresponding file spans (for example) over sectors
+ *    4345, 5645, 4539 and 6575, is 1.7kB long
+ *
+ *    Everything is stored this way on the disk, even the
+ *    directories. From the disk contents' point of view, a directory
+ *    is simply a file whose contents represent a list of mappings
+ *    "name" -> meta-information location
+ *
+ *  - One or many nodes in the file hierarchy pointing to this data
+ *    (struct sos_fs_nscache_node for SOS, struct dentry for Linux). This
+ *    tells that the entry "toto" in directory "/home/zorglub"
+ *    corresponds to the given struct sos_fs_node
+ *
+ *    Actually, with the struct sos_fs_node above, we can reach any
+ *    file in the system. However, dealing with mountpoints requires
+ *    an intermediary data structure because a directory on a disk
+ *    cannot make reference to children struct sos_fs_node on other
+ *    disk. This is one of the reasons why there is this struct
+ *    sos_fs_nscache_node. Another reason is that we kind-of "cache" the
+ *    most used struct sos_fs_node: those that lead from the global
+ *    root ("/") to the files and directories currently being used
+ *    (hence the name "nscache" for "namespace cache"). This speeds-up
+ *    the path-resolving process (aka "lookup"), as the most-used path
+ *    are already in-memory and the struct sos_fs_node are already
+ *    in-memory too.
+ *
+ *    A struct sos_fs_nscache_node can have at most 1 parent (the ".."
+ *    entry). It can also have 0 parent in case the node is being used
+ *    by a process (file is opened or mapped), but the file is
+ *    actually "removed", ie un-reachable from any directory.
+ *
+ *    Many such structures can reference the same underlying struct
+ *    sos_fs_node, which enables the support of "hard links".
+ *
+ *  - The "opened file" strucures. They store the information
+ *    pertaining to a particular usage of a file. The most important
+ *    thing they store is the "file pointer", which holds the
+ *    location in the file where the next read/write operation should
+ *    start
+ *
+ *    Each process has at least 2 such opened files: its "current
+ *    working directory" (RTFM chdir) and its "process root" (RTFM
+ *    chroot). Those are heritated across fork() and can be changed by
+ *    appropriate syscalls (resp. chdir/chroot). The main "root" of
+ *    the system is the process root of the "init" process. The usual
+ *    opened files (think of open() and opendir()) are stored in the
+ *    file descriptor array (fds[]). This is the index in this array
+ *    that is commonly called a "file descriptor".
+ *
+ *
+ * The whole VFS layer comprises a series of objects that can be
+ * specialized to implement different FS support (fat, ext2, ffs, ...):
+ *
+ *  - The notion of "file system manager", which basically is a
+ *    container to a FS name (eg "FAT", "EXT2", etc...) and a series of
+ *    functions responsible for initializing a particular "mounting" of
+ *    a FS (the "mount" method). This is SOS's struct sos_fs_manager_type
+ *
+ *  - The notion of "file system instance" which contains the data
+ *    proper to a particular mounting of an FS. Its most important job
+ *    is to allocate new struct sos_fs_node on disk, or to retrieve the
+ *    meta-information (ie struct sos_fs_node) located at the given
+ *    location on disk. This is roughly THE primary physical interface
+ *    between the VFS and the disks. This is SOS's struct
+ *    sos_fs_manager_instance, aka the Linux's superblock
+ *
+ *    For each struct sos_fs_node that it allocates, or that is loads
+ *    from disk into memory, this "instance manager" is responsible
+ *    for inidicating the functions that implement the FS-dedicated
+ *    routine such as read/write/mmap/ioctl/... for this precise node.
+ *
+ *    The nodes (struct sos_fs_node) of a struct
+ *    sos_fs_manager_instance that are currently loaded in memory are
+ *    stored in a hash-table. The key of this map is the location of the
+ *    meta-information on disk. That way, it is very fast to look for
+ *    the given meta-information whose location on disk is knows: if
+ *    it has already been loaded into memory, its memory address is
+ *    quickly resolved thanks to this hash table.
+ */
+
+#include <sos/types.h>
+#include <sos/errno.h>
+#include <sos/hash.h>
+#include <sos/umem_vmm.h>
+
+/* Forward declarations (structures defined in this file) */
+struct sos_fs_manager_type;
+struct sos_fs_manager_instance;
+struct sos_fs_statfs;
+struct sos_fs_node;
+struct sos_fs_opened_file;
+struct sos_fs_stat;
+
+#include "fs_nscache.h"
+
+/**
+ * The type of filesystem object.
+ *
+ * Each struct sos_fs_node has a type. Here are the supported types.
+ */
+typedef enum {
+  SOS_FS_NODE_REGULAR_FILE = 0x42,
+  SOS_FS_NODE_DIRECTORY    = 0x24,
+  SOS_FS_NODE_SYMLINK      = 0x84,
+} sos_fs_node_type_t;
+
+
+#define SOS_FS_MANAGER_NAME_MAXLEN 32
+/**
+ * Description of a supported Filesystem type.
+ *
+ * These descriptions are listed in an internal list (see
+ * fs.c:fs_list), and each time we want to mount a FS, we precise a
+ * name (eg "FAT", "EXT2", ...). The VFS will look for this name into
+ * the list of supported filesystem types, and, when found, call its
+ * sos_fs_manager_type::mount() method.
+ *
+ * New filesystem types are registered using sos_fs_register_fs_type()
+ */
+struct sos_fs_manager_type
+{
+  char name[SOS_FS_MANAGER_NAME_MAXLEN];
+
+  /**
+   * Responsible for making sure the underlying device (if any) really
+   * stores the correct filesystem format, for creating the hash of fs
+   * nodes and for calling sos_fs_register_fs_instance
+   *
+   * @param device May be NULL
+   *
+   * @note mandatory, may block
+   */
+  sos_ret_t (*mount)(struct sos_fs_manager_type * this,
+		     struct sos_fs_node * device,
+		     const char * args,
+		     struct sos_fs_manager_instance ** mounted_fs);
+  
+  /**
+   * Responsible for de-allocating the hash of fs nodes and for
+   * calling sos_fs_unregister_fs_instance
+   *
+   * @note mandatory, may block
+   */
+  sos_ret_t (*umount)(struct sos_fs_manager_type * this,
+		      struct sos_fs_manager_instance * mounted_fs);
+
+  /** Free of use */
+  void * custom_data;
+
+  /** List of filesystem instances of this type currently mounted
+      somewhere in the system */
+  struct sos_fs_manager_instance * instances;
+
+  /** Linkage for the list of filesystem types registered in the
+      system */
+  struct sos_fs_manager_type *prev, *next;
+};
+
+
+/**
+ * Data related to a particular "mounting" of a file system. A so-called "superblock" under Linux
+ *
+ * This holds the FUNDAMENTAL functions responsible for loading struct
+ * sos_fs_node from disk, or for allocating thom on disk. It also
+ * holds the hash-table of struct sos_fs_node already loaded into
+ * memory.
+ */
+struct sos_fs_manager_instance
+{
+  /**
+   * @note Publicly readable. Written only by sos_fs_manager_type::mount()
+   */
+  struct sos_fs_manager_type * fs_type;
+
+  /**
+   * Usually, a filesystem relies on a device (disk, network, ram,
+   * ...) to fetch its data. This is the location of the device.
+   *
+   * @note Publicly readable. Written only by fs.c
+   */
+  struct sos_fs_node * device;
+
+#define SOS_FS_MOUNT_SYNC     (1 << 0)
+#define SOS_FS_MOUNT_READONLY (1 << 1)
+#define SOS_FS_MOUNT_NOEXEC   (1 << 2)
+  /**
+   * Is this FS read-only, without EXEC file permission, write-through
+   * ? Or-red combination of the SOS_FS_MOUNT_ flags
+   *
+   * @note Publicly readable. Written only by fs.c
+   */
+  sos_ui32_t flags;
+
+  /**
+   * The namespace node that is the root of THIS file system mounting
+   *
+   * @note Publicly readable. Written only by fs.c
+   */
+  struct sos_fs_nscache_node * root;
+
+  /**
+   * List of dirty nodes. These are the nodes that need to be written
+   * back to disk. With FS supporting deferred-writes, the
+   * sos_fs_sync() function will use this list to flush the dirty
+   * nodes back to disk.
+   *
+   * @note Reserved to fs.c
+   */
+  struct sos_fs_node * dirty_nodes;
+
+  /**
+   * Build a fresh new FS node at the given location. This implies
+   * the allocation of a new sos_fs_node structure in memory
+   *
+   * @note Mandatory, may block. Appropriate locking MUST be implemented
+   */
+  sos_ret_t (*fetch_node_from_disk)(struct sos_fs_manager_instance * this,
+				    sos_ui64_t storage_location,
+				    struct sos_fs_node ** result);
+
+  /**
+   * Build a fresh new FS node ON THE DISK of the given type (dir,
+   * plain file, symlink, ...), completely empty ; return a newly
+   * allocated IN-MEMORY node structure representing it
+   *
+   * @param open_creat_flags is the open_flags parameter passed to
+   * sos_fs_open() when O_CREAT is set. 0 when allocated trough
+   * creat/mkdir/mknod/symlink
+   *
+   * @note Mandatory, may block. Appropriate locking MUST be implemented
+   */
+  sos_ret_t (*allocate_new_node)(struct sos_fs_manager_instance * this,
+				 sos_fs_node_type_t type,
+				 const struct sos_process * creator,
+				 sos_ui32_t access_rights,
+				 sos_ui32_t open_creat_flags,
+				 struct sos_fs_node ** result);
+
+  /**
+   * Return filesystem status (RTFM df)
+   *
+   * @note Optional, may block. Appropriate locking MUST be implemented
+   */
+  sos_ret_t (*statfs)(struct sos_fs_manager_instance * this,
+		      struct sos_fs_statfs * result);
+
+  /**
+   * Comparison callback called when looking for file/dirs in the
+   * namespace cache. Normally, a usual lexicographical comparison is
+   * done (when this function points to NULL). But for some FS, it
+   * might be useful to use another comparison function (eg for
+   * case-insensitive FS)
+   *
+   * @note Optional (may be NULL), must NOT block
+   */
+  sos_bool_t (*nsnode_same_name)(const char * name1, sos_ui16_t namelen1,
+				 const char * name2, sos_ui16_t namelen2);
+
+  /**
+   * Hash table of the struct sos_fs_node of this filesystem instance
+   * loaded in memory: key=storage_location, element=sos_fs_node
+   */
+  struct sos_hash_table * nodecache;
+
+  /**
+   * Unique identifier of this FS (used in sync method, updated by
+   * fs.c). This enables sync_all_fs to be resilient to mount/umount
+   * and (un)register_fs_type/instance
+   */
+  sos_ui64_t uid;
+
+  void * custom_data;
+
+  /** Linkage for the list of instances for the underlying fs type */
+  struct sos_fs_manager_instance * prev, * next;
+};
+
+
+/**
+ * The CENTRAL data structure of the whole thing. A so-called "inode"
+ *
+ * This represents the meta-information related to a file on disk: its
+ * permission, where its data is located. Actually, in SOS, these
+ * information are not stored in this structure. Instead, we define a
+ * series of methods in this structure that MUST be implemented by the
+ * FS and that realize the higher level operations needed by the
+ * OS. These operations will rely on the meta-information that the FS
+ * code MUST define and manage by itself (hence the
+ * sos_fs_node::custom_data field).
+ */
+struct sos_fs_node
+{
+  /**
+   * An struct sos_fs_node always belong to exactly ONE file system
+   */
+  struct sos_fs_manager_instance * fs;
+
+  /**
+   * The so-called "inode": location of this node inside the FS
+   * instance. Updated by struct
+   * sos_fs_manager_instance::fetch_node_from_disk()
+   */
+  sos_ui64_t storage_location;
+
+  /**
+   * Number of ON-DISK links to this node.
+   *
+   * - For everything but directories: the number of hard links to the file
+   * - For directories: 1 + the number of children nodes
+   *
+   * @note Publicly readable. Written only by
+   * sos_fs_node_ops_dir::link() and sos_fs_node_ops_dir::unlink()
+   */
+  sos_count_t ondisk_lnk_cnt;
+
+  /**
+   * Number of IN-MEMORY nscache_nodes referencing this FS node.
+   *
+   * Corresponds to the number of struct sos_fs_nscache_node pointing
+   * to this node. This could be as much as ondisk_lnk_cnt + 1, but is
+   * usually less
+   *
+   * @note Reserved to fs.c
+   */
+  sos_count_t inmem_ref_cnt;
+
+  /**
+   * Directory, symlink, ...
+   *
+   * @see sos_fs_node_type_t
+   *
+   * @note Publicly readable. Written only by fs.c
+   */
+  sos_fs_node_type_t type;
+
+#define SOS_FS_READABLE          00400
+#define SOS_FS_WRITABLE          00200
+#define SOS_FS_EXECUTABLE        00100
+  /**
+   * read/write, ... @see the SOS_FS_*ABLE flags
+   * @note Publicly readable. Written only by fs.c
+   */
+  sos_ui32_t access_rights;
+
+  /**
+   * @note Reserved to fs.c
+   */
+  sos_bool_t dirty;
+
+  /**
+   * Incremented each time one of the opened files for this node is
+   * modified
+   * @note Publicly readable. Written only by fs.c
+   */
+  sos_lcount_t generation;
+
+  /** Operations common to all node types */
+  struct sos_fs_node_ops_file  *ops_file;
+
+  /** Operations specific to some node types */
+  union
+  {
+    /** when type == SOS_FS_NODE_DIRECTORY */
+    struct sos_fs_node_ops_dir      *ops_dir;
+
+    /** when type == SOS_FS_NODE_SYMLINK */
+    struct sos_fs_node_ops_symlink  *ops_symlink;
+  }; /* Anonymous union (gcc extension) */
+
+
+  /**
+   * Simply free this FS node from the kernel memory: it does NOT
+   * mean that the corresponding on-disk node is free ! Actually, the
+   * corresponding ON-DISK node is free iff ondisk_lnk_cnt == 0. No
+   * need to sync anything to disk, as the VFS will sync the node
+   * before calling this method
+   *
+   * @note Mandatory, may block, no special locking needed
+   */
+  sos_ret_t (*destructor)(struct sos_fs_node * this);
+
+  /**
+   * Called when a process opens the node
+   *
+   * @note Mandatory, may block. Appropriate locking MUST be implemented
+   */
+  sos_ret_t (*new_opened_file)(struct sos_fs_node * this,
+			       const struct sos_process * owner,
+			       sos_ui32_t open_flags,
+			       struct sos_fs_opened_file ** result_of);
+
+  /**
+   * Called when a process opens the node
+   *
+   * @note Mandatory, may block. Appropriate locking MUST be implemented
+   */
+  sos_ret_t (*close_opened_file)(struct sos_fs_node * this,
+				 struct sos_fs_opened_file * of);
+
+  /**
+   * This should hold the meta information for this node as needed by
+   * the FS instance.
+   */
+  void * custom_data;
+
+  /** Hash linkage entry for this FS node in the nodecache
+     dictionary */
+  struct sos_hash_linkage hlink_nodecache;
+
+  /** Linkage to list the dirty nodes of the given FS */
+  struct sos_fs_node *prev_dirty, *next_dirty;
+};
+
+
+
+/**
+ * The list of methods implementing the basic VFS operations on the
+ * given struct sos_fs_node
+ *
+ * @see sos_fs_node::ops_file
+ */
+struct sos_fs_node_ops_file
+{
+  /**
+   * Change size of file
+   *
+   * @note Optional, may block. Appropriate locking MUST be implemented
+   */
+  sos_ret_t (*truncate)(struct sos_fs_node *this,
+			sos_lsoffset_t length);
+
+  /**
+   * Retrieve the status (eg size) of the file
+   *
+   * @note Mandatory, may block. Appropriate locking MUST be implemented
+   */
+  sos_ret_t (*stat)(struct sos_fs_node * this,
+		    struct sos_fs_stat * result);
+
+  /**
+   * Flush any change to disk
+   *
+   * @note Mandatory, may block. Appropriate locking MUST be implemented
+   */
+  sos_ret_t (*sync)(struct sos_fs_node *this);
+
+  /**
+   * Change the sos_fs_node::access_rights attribute
+   *
+   * @note Mandatory, may block. Appropriate locking MUST be implemented
+   */
+  sos_ret_t (*chmod)(struct sos_fs_node * this,
+		     sos_ui32_t new_access_rights);
+};
+
+
+/**
+ * The list of methods implementing the basic VFS symlink operations
+ *
+ * @see sos_fs_node::ops_symlink
+ */
+struct sos_fs_node_ops_symlink
+{
+  /**
+   * Used by the _kernel_ to resolve the symlinks. To change/create a
+   * symlink target, it is needed only from userland: the read/write
+   * methods are made for this
+   *
+   * @param target Pointer to the string representing the target's
+   * path, allocated for the fs_node's lifetime !
+   *
+   * @note Mandatory, may block. Appropriate locking MUST be implemented
+   */
+  sos_ret_t (*expand)(struct sos_fs_node *this,
+		      char const  ** target,
+		      sos_size_t * target_len);
+};
+
+
+/**
+ * The list of methods implementing the basic VFS directory operations
+ *
+ * @see sos_fs_node::ops_dir
+ */
+struct sos_fs_node_ops_dir
+{
+  /**
+   * Look for the on-disk location of the sos_fs_node having the given
+   * name
+   *
+   * @note Mandatory, may block. Appropriate locking MUST be implemented
+   */
+  sos_ret_t (*lookup)(struct sos_fs_node *this,
+		      const char * name, sos_ui16_t namelen,
+		      sos_ui64_t * result_storage_location);
+
+  /**
+   * Add a new reference in the current sos_fs_node to the on-disk
+   * location of the given sos_fs_node
+   *
+   * @note Responsible for updating this->ondisk_lnk_cnt
+   * @note Mandatory for writable directories, may block. Appropriate
+   * locking MUST be implemented
+   */
+  sos_ret_t (*link)(struct sos_fs_node *this,
+		    const struct sos_process *actor,
+		    const char * entry_name, sos_ui16_t entry_namelen,
+		    struct sos_fs_node * node);
+
+  /**
+   * Remove the entry in the current sos_fs_node for the on-disk
+   * location with the given name
+   *
+   * @note Responsible for updating this->ondisk_lnk_cnt
+   * @note Mandatory for writable directories, may block. Appropriate
+   * locking MUST be implemented
+   */
+  sos_ret_t (*unlink)(struct sos_fs_node *this,
+		      const struct sos_process *actor,
+		      const char * entry_name, sos_ui16_t entry_namelen);
+};
+
+
+/**
+ * The data structure holding information and method related to a
+ * particular usage of a file. A so-called "struct file"
+ *
+ * This represents the kernel structure behind a "file descriptor" or
+ * behind a chdir/chroot. Among other things, it holds the methods
+ * responsible for reading/writing into the file, and for moving the
+ * file pointer (see @sos_fs_opened_file::position) inside it.
+ */
+struct sos_fs_opened_file
+{
+  /** The process that opened the file/dir */
+  const struct sos_process * owner;
+
+  /**
+   * The reference to the sos_fs_nscache_node and, hence, to the underlying sos_fs_node.
+   *
+   * Used to cache the in-memory fs nodes
+   */
+  struct sos_fs_nscache_node * direntry;
+
+  /** Use for memory-management */
+  sos_count_t ref_cnt;
+
+  /**
+   * Always > 0 (ie max size = 2^63-1 = 9.2 10^18). We make it
+   * "signed" here to limit its range. Because we must be able to
+   * seek to the begining of the file with SEEK_END and a negative
+   * offset, so the max size of the file must be reachable by a lseek
+   * offset
+   *
+   * @note reserved to filesystem instance code. Not modified nor used
+   * by fs.c
+   */
+  sos_lsoffset_t     position;
+
+  /**
+   * Incremented each time this opened file is modified
+   *
+   * Used to implement a readdir method resilient to
+   * creat/mkdir/rmdir/unlink
+   */
+  sos_lcount_t generation;
+
+  /**
+   * @see SOS_FS_OPEN_* flags
+   */
+  sos_ui32_t open_flags;
+
+  /** Operations common to all node types */
+  struct sos_fs_ops_opened_file * ops_file;
+
+  /** Operations specific to some node types */
+  union
+  {
+    /** when direntry->fs_node->type == SOS_FS_NODE_DIRECTORY */
+    struct sos_fs_ops_opened_dir      * ops_dir;
+  }; /* Anonymous union (gcc extension) */
+
+  /**
+   * Called upon fork() to duplicate all the opened files
+   */
+  sos_ret_t (*duplicate)(struct sos_fs_opened_file *this,
+			 const struct sos_process  * for_owner,
+			 struct sos_fs_opened_file **result);
+
+  void * custom_data;
+};
+
+
+/**
+ * Reference position for sos_fs_seek
+ */
+typedef enum { SOS_SEEK_SET=42,
+	       SOS_SEEK_CUR=24,
+	       SOS_SEEK_END=84 } sos_seek_whence_t;
+/**
+ * The list of methods implementing the basic VFS opened file
+ * operations
+ *
+ * See the Unix manual pages, they basically form the interfaces to to
+ * these functions
+ *
+ * @see sos_fs_opened_file::ops_file
+ */
+struct sos_fs_ops_opened_file
+{
+  /**
+   * @note Mandatory, may block. Appropriate locking MUST be implemented
+   * @note Please call sos_fs_mark_dirty() if disk contents is changed
+   */
+  sos_ret_t (*seek)(struct sos_fs_opened_file *this,
+		    sos_lsoffset_t offset,
+		    sos_seek_whence_t whence,
+		    /* out */ sos_lsoffset_t * result_position);
+
+  /**
+   * @note Mandatory, may block. Appropriate locking MUST be implemented
+   * @note Please call sos_fs_mark_dirty() if disk contents is changed
+   */
+  sos_ret_t (*read)(struct sos_fs_opened_file *this,
+		    sos_uaddr_t dest_buf,
+		    sos_size_t * /* in/out */len);
+
+  /**
+   * @note Optional (might be NULL), may block. Appropriate locking
+   * MUST be implemented
+   * @note Please call sos_fs_mark_dirty() if disk contents is changed
+   */
+  sos_ret_t (*write)(struct sos_fs_opened_file *this,
+		     sos_uaddr_t src_buf,
+		     sos_size_t * /* in/out */len);
+
+  /**
+   * @note Optional (might be NULL), may block. Appropriate locking
+   * MUST be implemented
+   * @note Please call sos_fs_mark_dirty() if disk contents is changed
+   */
+  sos_ret_t (*mmap)(struct sos_fs_opened_file *this,
+		    sos_uaddr_t *uaddr, sos_size_t size,
+		    sos_ui32_t access_rights,
+		    sos_ui32_t flags,
+		    sos_luoffset_t offset);
+
+  /**
+   * @note Optional (might be NULL), may block. Appropriate locking
+   * MUST be implemented
+   * @note Please call sos_fs_mark_dirty() if disk contents is changed
+   */
+  sos_ret_t (*fcntl)(struct sos_fs_opened_file *this,
+		     int req_id,
+		     sos_ui32_t req_arg /* Usually: sos_uaddr_t */);
+};
+
+
+/** Data structure that is to be filled by readdir */
+struct sos_fs_dirent
+{
+  sos_ui64_t storage_location;
+  sos_si64_t offset_in_dirfile;
+  sos_ui32_t type;
+  sos_ui16_t namelen;
+
+#define SOS_FS_DIRENT_NAME_MAXLEN 128
+  char       name[SOS_FS_DIRENT_NAME_MAXLEN];
+};
+
+
+/**
+ * The list of methods implementing the basic VFS opened directory
+ * operations
+ *
+ * @see sos_fs_opened_file::ops_file
+ */
+struct sos_fs_ops_opened_dir
+{
+  /**
+   * Each time it is called, responsible for filling the sos_fs_dirent
+   * structure, return -SOS_ENOENT when done.
+   *
+   * @note Mandatory, may block. Appropriate locking MUST be implemented
+   * @note Please call sos_fs_mark_dirty() if disk contents is changed
+   */
+  sos_ret_t (*readdir)(struct sos_fs_opened_file *this,
+		       struct sos_fs_dirent * result);
+};
+
+
+
+/**
+ * Used by the stat calls
+ *
+ * @see sos_fs_node_ops_file::stat
+ */
+struct sos_fs_stat
+{
+  sos_fs_node_type_t     st_type;
+  sos_ui64_t             st_storage_location;
+  sos_ui32_t             st_access_rights;
+  sos_count_t            st_nlink;
+  sos_si64_t             st_size;
+};
+
+
+/**
+ * Used by the statvfs calls
+ *
+ * @see sos_fs_manager_instance::statfs
+ */
+struct sos_fs_statfs
+{
+  sos_size_t             f_sz_total;  /**< Total size */
+  sos_size_t             f_sz_free;   /**< Size left on device */
+  sos_count_t            f_node_total;/**< Total allocatable FS nodes */
+  sos_count_t            f_node_avail;/**< Number of available free FS nodes */
+  sos_ui32_t             f_flags;
+};
+
+
+/**
+ * Must be called AFTER the FS manager types needed to mount the root
+ * filesystem have been registered
+ */
+sos_ret_t sos_fs_subsystem_setup(const char * root_device,
+				 const char * fs_type,
+				 const char * mount_args,
+				 struct sos_fs_manager_instance ** result_rootfs);
+
+
+/*  ***************************************************************
+ * The Following functions are relatively standard
+ *
+ * @see Unix manual pages for details
+ */
+
+
+/**
+ * mount a file system
+ *
+ * @param actor process calling mount
+ * @param _src_path(len) may be NULL (as for virtfs or /proc)
+ * @fsname the name of the filesystem type to mount
+ * @args any args passed to the sos_fs_manager_type::mount method
+ * @result_fs the resulting filesystem instance
+ */
+sos_ret_t sos_fs_mount(struct sos_process * actor,
+		       const char * _src_path,
+		       sos_size_t _src_pathlen,
+		       const char * _dst_path,
+		       sos_size_t _dst_pathlen,
+		       const char * fsname,
+		       sos_ui32_t mountflags,
+		       const char * args,
+		       struct sos_fs_manager_instance ** /*out*/result_fs);
+
+/**
+ * unmount the filesystem at the given location
+ */
+sos_ret_t sos_fs_umount(struct sos_process * actor,
+			const char * _mountpoint_path,
+			sos_size_t _mountpoint_pathlen);
+
+/**
+ * Flush all the dirty nodes of all the FS to disk
+ */
+sos_ret_t sos_fs_sync_all_fs();
+
+/**
+ * Retrieve filesystem status, or return -SOS_ENOSUP if filesystem
+ * cannot report this
+ */
+sos_ret_t sos_fs_vfstat(const struct sos_process * actor,
+			const char * _path,
+			sos_size_t _pathlen,
+			struct sos_fs_statfs * result);
+
+/**
+ * Open flags
+ */
+#define SOS_FS_OPEN_EXCL        (1 << 0)
+#define SOS_FS_OPEN_CREAT       (1 << 1)
+#define SOS_FS_OPEN_NOFOLLOW    (1 << 2)
+#define SOS_FS_OPEN_DIRECTORY   (1 << 3) /* Incompatible with CREAT */
+#define SOS_FS_OPEN_SYNC        (1 << 4)
+#define SOS_FS_OPEN_KEEPONEXEC  (1 << 5) /* By default, files are closed
+					    upon an exec() */
+
+#define SOS_FS_OPEN_READ        (1 << 16)
+#define SOS_FS_OPEN_WRITE       (1 << 17)
+
+
+/**
+ * FS access rights
+ */
+#define SOS_FS_S_IRUSR   00400
+#define SOS_FS_S_IWUSR   00200
+#define SOS_FS_S_IXUSR   00100
+
+#define SOS_FS_S_IRWXALL 07777 /* For symlinks */
+
+sos_ret_t sos_fs_open(const struct sos_process *owner,
+		      const char *_path,
+		      sos_size_t _pathlen,
+		      sos_ui32_t open_flags,
+		      sos_ui32_t creat_access_rights,
+		      struct sos_fs_opened_file ** of);
+
+sos_ret_t sos_fs_close(struct sos_fs_opened_file * of);
+
+sos_ret_t sos_fs_read(struct sos_fs_opened_file * of,
+		      sos_uaddr_t dest_buf,
+		      sos_size_t * /* in/ou */len);
+
+sos_ret_t sos_fs_readdir(struct sos_fs_opened_file * of,
+			 struct sos_fs_dirent * result);
+
+sos_ret_t sos_fs_write(struct sos_fs_opened_file * of,
+		       sos_uaddr_t src_buf,
+		       sos_size_t * /* in/out */len);
+
+sos_ret_t sos_fs_seek(struct sos_fs_opened_file *of,
+		      sos_lsoffset_t offset,
+		      sos_seek_whence_t whence,
+		      sos_lsoffset_t * result_position);
+
+sos_ret_t sos_fs_ftruncate(struct sos_fs_opened_file *of,
+			   sos_lsoffset_t length);
+
+sos_ret_t sos_fs_mmap(struct sos_fs_opened_file *of,
+		      sos_uaddr_t *uaddr, sos_size_t size,
+		      sos_ui32_t access_rights,
+		      sos_ui32_t flags,
+		      sos_luoffset_t offset);
+
+sos_ret_t sos_fs_fsync(struct sos_fs_opened_file * of);
+
+sos_ret_t sos_fs_fcntl(struct sos_fs_opened_file *of,
+		       int req_id,
+		       sos_ui32_t req_arg /* Usually: sos_uaddr_t */);
+
+sos_ret_t sos_fs_creat(const struct sos_process * creator,
+		       const char * _path,
+		       sos_size_t _pathlen,
+		       sos_ui32_t access_rights);
+
+sos_ret_t sos_fs_link(const struct sos_process * creator,
+		      const char * _old_path,
+		      sos_size_t _old_pathlen,
+		      const char * _dest_path,
+		      sos_size_t _dest_pathlen);
+
+sos_ret_t sos_fs_rename(const struct sos_process * creator,
+			const char * _old_path,
+			sos_size_t _old_pathlen,
+			const char * _dest_path,
+			sos_size_t _dest_pathlen);
+
+sos_ret_t sos_fs_unlink(const struct sos_process * actor,
+			const char * _path,
+			sos_size_t _pathlen);
+
+sos_ret_t sos_fs_symlink(const struct sos_process * creator,
+			 const char * _path,
+			 sos_size_t _pathlen,
+			 sos_uaddr_t symlink_target,
+			 sos_size_t symlink_target_len);
+
+sos_ret_t sos_fs_mkdir(const struct sos_process * creator,
+		       const char * _path,
+		       sos_size_t _pathlen,
+		       sos_ui32_t access_rights);
+
+sos_ret_t sos_fs_rmdir(const struct sos_process * actor,
+		       const char * _path,
+		       sos_size_t _pathlen);
+
+sos_ret_t sos_fs_chmod(const struct sos_process * actor,
+		       const char * _path,
+		       sos_size_t _pathlen,
+		       sos_ui32_t access_rights);
+
+sos_ret_t sos_fs_stat(const struct sos_process * actor,
+		      const char * _path,
+		      sos_size_t _pathlen,
+		      int nofollow,
+		      struct sos_fs_stat * result);
+
+
+/* ***************************************************************
+ * Restricted functions reserved to FS code and block/char devices
+ */
+
+/**
+ * Function to be called when proposing a new File system type
+ */
+sos_ret_t sos_fs_register_fs_type(struct sos_fs_manager_type * fstype);
+sos_ret_t sos_fs_unregister_fs_type(struct sos_fs_manager_type * fstype);
+
+/**
+ * Marthe given file as dirty, for FS supporting deferred write access
+ * mode
+ */
+sos_ret_t sos_fs_mark_dirty(struct sos_fs_opened_file * of);
+
+/**
+ * Helper function to be called from the mount() method of the FS
+ * instance code. Responsible for creating and updating the "root"
+ * field of the FS instance structure and for connecting this FS in
+ * the nscache
+ * @param root_fsnode The root of the FS being mounted
+ */
+sos_ret_t sos_fs_register_fs_instance(struct sos_fs_manager_instance * fs,
+				      struct sos_fs_node * root_fsnode);
+
+/**
+ * Helper function to be called from the umount() method of the FS
+ * instance code. Responsible for unregistering the instance from the
+ * FS type's instances list and for disconnecting this mountpoint in
+ * the nscache.
+ */
+sos_ret_t sos_fs_unregister_fs_instance(struct sos_fs_manager_instance * fs);
+
+
+/* ***************************************************************
+ * Restricted functions reserved to syscall.c
+ */
+sos_ret_t sos_fs_ref_opened_file(struct sos_fs_opened_file * of);
+sos_ret_t _sos_fs_unref_opened_file(struct sos_fs_opened_file ** of);
+#define sos_fs_unref_opened_file(f) _sos_fs_unref_opened_file(&(f))
+
+
+/* ***************************************************************
+ * Restricted functions to be used only by fs_nscache.c
+ */
+
+sos_ret_t sos_fs_ref_fsnode(struct sos_fs_node * fsnode);
+
+sos_ret_t _sos_fs_unref_fsnode(struct sos_fs_node * fsnode);
+#define sos_fs_unref_fsnode(n) \
+  ({ sos_ret_t __retval = _sos_fs_unref_fsnode(n); (n)=NULL; __retval; })
+
+
+/* ***************************************************************
+ * Restricted functions reserved to process.c and main.c:start_init()
+ */
+sos_ret_t sos_fs_new_opened_file(const struct sos_process * proc,
+				 struct sos_fs_nscache_node * nsnode,
+				 sos_ui32_t open_flags,
+				 struct sos_fs_opened_file ** result_of);
+
+
+sos_ret_t sos_fs_duplicate_opened_file(struct sos_fs_opened_file * src_of,
+				       const struct sos_process * dst_proc,
+				       struct sos_fs_opened_file ** result_of);
+
+
+
+#endif /* _SOS_FS_H_ */
diff -ruN /tmp/sos-code-article7.5/sos/fs_nscache.c ../sos-code-article8/sos/fs_nscache.c
--- /tmp/sos-code-article7.5/sos/fs_nscache.c	1970-01-01 01:00:00.000000000 +0100
+++ ../sos-code-article8/sos/fs_nscache.c	2005-07-01 16:39:49.000000000 +0200
@@ -0,0 +1,527 @@
+/* Copyright (C) 2005      David Decotigny
+   Copyright (C) 2000-2005 The KOS Team (Thomas Petazzoni, David
+                           Decotigny, Julien Munier)
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License
+   as published by the Free Software Foundation; either version 2
+   of the License, or (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+   USA. 
+*/
+
+#include <sos/assert.h>
+#include <sos/list.h>
+#include <sos/kmem_slab.h>
+#include <sos/kmalloc.h>
+
+#include "fs_nscache.h"
+
+
+/**
+ * A so-called "dentry" / "nsnode" structure. Used to make the
+ * "in-memory" representation of the file system files/dir/devices
+ * that have been used up to now
+ */
+struct sos_fs_nscache_node
+{
+  /** The reference to the associated sos_fs_node */
+  struct sos_fs_node     *fs_node;
+
+  struct sos_fs_pathname name;
+
+  /** Number of references to that node, reference from parent (if
+      any) is EXCLUDED */
+  sos_count_t ref_cnt;
+
+  /*
+   * Ued to chain the mounted filesystem
+   */
+
+  /** If this node is a mountpoint (a file system is mounted on it):
+      reference to the filesystem mounted on it */
+  struct sos_fs_nscache_node *mounted_root;
+  /** If this node is the root of a mounted filesystem: reference to
+      the mountpoint where it is mounted on */
+  struct sos_fs_nscache_node *mountpoint;
+
+  /** ".." */
+  struct sos_fs_nscache_node *parent;
+
+  /** List of the already known children */
+  struct sos_fs_nscache_node *children;
+  
+  /** Other children for the same parent */
+  struct sos_fs_nscache_node *siblings_prev, *siblings_next;
+};
+
+
+/** The cache of nscache_node objects */
+static struct sos_kslab_cache * cache_of_nscache_nodes;
+
+
+sos_ret_t sos_fs_nscache_subsystem_setup()
+{
+  cache_of_nscache_nodes
+    = sos_kmem_cache_create("fs_nscache",
+			    sizeof(struct sos_fs_nscache_node),
+			    3, 0,
+			    SOS_KSLAB_CREATE_MAP | SOS_KSLAB_CREATE_ZERO);
+  if (! cache_of_nscache_nodes)
+    return -SOS_ENOMEM;
+
+  return SOS_OK;
+};
+
+
+sos_bool_t
+sos_fs_pathname_eat_slashes(const struct sos_fs_pathname * path,
+			    struct sos_fs_pathname * result)
+{
+  sos_bool_t retval = FALSE;
+
+  result->contents = path->contents;
+  result->length   = path->length;
+  while (result->length > 0)
+    {
+      if (*result->contents != '/')
+	break;
+
+      result->contents ++;
+      result->length --;
+      retval = TRUE;
+    }
+
+  if(result->length <= 0)
+    result->contents = NULL;
+
+  return retval;
+}
+
+
+static sos_bool_t
+sos_fs_pathname_eat_non_slashes(const struct sos_fs_pathname * path,
+				struct sos_fs_pathname * result)
+{
+  sos_bool_t retval = FALSE;
+
+  result->contents = path->contents;
+  result->length   = path->length;
+  while (result->length > 0)
+    {
+      if (*result->contents == '/')
+	break;
+
+      result->contents ++;
+      result->length --;
+      retval = TRUE;
+    }
+
+  if(result->length <= 0)
+    result->contents = NULL;
+
+  return retval;
+}
+
+
+sos_bool_t
+sos_fs_pathname_split_path(const struct sos_fs_pathname * path,
+			   struct sos_fs_pathname * result_first_component,
+			   struct sos_fs_pathname * result_remaining_path)
+{
+  result_first_component->contents = path->contents;
+  result_first_component->length   = path->length;
+
+  /* Skip any leading slash */
+  sos_fs_pathname_eat_slashes(result_first_component,
+			      result_first_component);
+
+  /* Extract the first component */
+  sos_fs_pathname_eat_non_slashes(result_first_component,
+				  result_remaining_path);
+  SOS_ASSERT_FATAL(result_remaining_path->length >= 0);
+  result_first_component->length -= result_remaining_path->length;
+
+  /* Return true if there is something left (at least one slash) */
+  return (result_remaining_path->length > 0);
+}
+
+
+sos_bool_t fs_pathname_iseq(const struct sos_fs_pathname * p1,
+			    const struct sos_fs_pathname * p2)
+{
+  if (!p1->contents)
+    SOS_ASSERT_FATAL(p1->length == 0);
+  if (!p2->contents)
+    SOS_ASSERT_FATAL(p2->length == 0);
+
+  if (p1->length != p2->length)
+    return FALSE;
+
+  if (p1->length == 0)
+    return TRUE;
+
+  return (0 == memcmp(p1->contents, p2->contents, p1->length));
+}
+
+
+#define fs_pathname_isstr(str,path) \
+  ({ struct sos_fs_pathname _s; _s.contents = str; _s.length = sizeof(str)-1; \
+     fs_pathname_iseq(&_s, (path)); })
+
+
+struct sos_fs_node *
+sos_fs_nscache_get_fs_node(const struct sos_fs_nscache_node * nsnode)
+{
+  return nsnode->fs_node;
+}
+
+
+sos_ret_t
+sos_fs_nscache_get_parent(const struct sos_fs_nscache_node * nsnode,
+			  struct sos_fs_nscache_node ** result_parent)
+{
+  *result_parent = nsnode->parent;
+  if (*result_parent)
+    return SOS_OK;
+  return -SOS_ENOENT;
+}
+
+
+sos_ret_t
+sos_fs_nscache_get_name(const struct sos_fs_nscache_node * nsnode,
+			struct sos_fs_pathname * result_pathname)
+{
+  result_pathname->contents = nsnode->name.contents;
+  result_pathname->length   = nsnode->name.length;
+  return SOS_OK;
+}
+
+
+sos_ret_t
+sos_fs_nscache_get_ref_cnt(const struct sos_fs_nscache_node * nsnode)
+{
+  return nsnode->ref_cnt;
+}
+
+
+sos_ret_t
+sos_fs_nscache_lookup(struct sos_fs_nscache_node * cur_nsnode,
+		      const struct sos_fs_pathname * node_name,
+		      const struct sos_fs_nscache_node * root_nsnode,
+		      struct sos_fs_nscache_node ** result_nsnode)
+{
+  if (fs_pathname_isstr(".", node_name))
+    {
+      *result_nsnode = cur_nsnode;
+    }
+  else if (fs_pathname_isstr("..", node_name))
+    {
+      /* Effectively go up only if we did not reach a root node */
+      if (cur_nsnode == root_nsnode) /* did reach chroot */
+	{
+	  /* Simply stay here */
+	  *result_nsnode = cur_nsnode;
+	}
+      else
+	{
+	  /* If current node is a mounted FS, rewind the mountpoint
+	     chain */
+	  for ( ; cur_nsnode->mountpoint ; cur_nsnode = cur_nsnode->mountpoint)
+	    /* nop */ ;
+
+	  /* Now go up to parent */
+	  SOS_ASSERT_FATAL(NULL != cur_nsnode->parent);
+	  *result_nsnode = cur_nsnode->parent;
+	}
+
+      /* Update the nscache_node result */
+      sos_fs_nscache_ref_node(*result_nsnode);
+      return SOS_OK;
+    }
+  else
+    {
+      /* Normal lookup: we iterate over the list of children nscache
+	 nodes */
+      int nb_children;
+      struct sos_fs_nscache_node * child;
+
+      /* Lookup the child node with the correct name, if any */
+      list_foreach_named(cur_nsnode->children,
+			 child, nb_children,
+			 siblings_prev, siblings_next)
+	{
+	  struct sos_fs_node * fs_node = cur_nsnode->fs_node;
+	  
+	  if (fs_node->fs->nsnode_same_name)
+	    {
+	      if (fs_node->fs->
+		    nsnode_same_name(child->name.contents,
+				     child->name.length,
+				     node_name->contents,
+				     node_name->length))
+		break;
+	    }
+	  else
+	    if (fs_pathname_iseq(& child->name,
+				 node_name))
+	      break;
+	}
+
+      /* Did not find it ! */
+      if (! list_foreach_early_break(cur_nsnode->children,
+				     child, nb_children))
+	return -SOS_ENOENT;
+
+      /* Yes, found it ! */
+      *result_nsnode = child;
+    }
+
+  /* Found it. Now, Follow the mountpoint chain, if any */
+  for ( ; (*result_nsnode)->mounted_root ;
+	*result_nsnode = (*result_nsnode)->mounted_root)
+    /* nop */ ;
+
+  /* Update the nscache_node result */
+  sos_fs_nscache_ref_node(*result_nsnode);
+
+  return SOS_OK;
+}
+
+
+sos_ret_t sos_fs_nscache_ref_node(struct sos_fs_nscache_node * nsnode)
+{
+  SOS_ASSERT_FATAL(nsnode->ref_cnt > 0);
+  nsnode->ref_cnt ++;
+  return SOS_OK;
+}
+
+
+/* Eventually collapses a whole list of nsnodes (non recursive) */
+sos_ret_t _sos_fs_nscache_unref_node(struct sos_fs_nscache_node ** nsnode)
+{
+  struct sos_fs_nscache_node * to_delete = NULL, *node;
+
+  node = *nsnode;
+  *nsnode = NULL;
+
+  while (node)
+    {
+      /* Unreference this node */
+      SOS_ASSERT_FATAL(node->ref_cnt > 0);
+      node->ref_cnt --;
+
+      /* Is it a good candidate for deletion ? */
+      if (node->ref_cnt > 0)
+	break; /* No */
+
+      if (node->parent)
+	{
+	  struct sos_fs_nscache_node * parent = node->parent;
+
+	  SOS_ASSERT_FATAL(node->parent->ref_cnt >= 1);
+
+	  list_delete_named(parent->children, node,
+			    siblings_prev, siblings_next);
+	  /* The parent lost one child: next iteration will decrement
+	     ths parent's ref cnt */
+	}
+
+      /* Add to the list of elements to suppress */
+      list_add_tail_named(to_delete, node, siblings_prev, siblings_next);
+
+      /* Now look if parent (if any) can be destroyed */
+      node = node->parent;
+    }
+
+  /* Now destroy all the elements gathered */
+  while (! list_is_empty_named(to_delete, siblings_prev, siblings_next))
+    {
+      node = list_pop_head_named(to_delete, siblings_prev, siblings_next);
+      sos_fs_unref_fsnode(node->fs_node);
+      sos_kfree((sos_vaddr_t)node);
+    }
+
+  return SOS_OK;
+}
+
+
+sos_ret_t
+sos_fs_nscache_add_new_child_node(struct sos_fs_nscache_node * parent,
+				  const struct sos_fs_pathname * node_name,
+				  struct sos_fs_node * fsnode,
+				  struct sos_fs_nscache_node ** result_nsnode)
+{
+  struct sos_fs_nscache_node * nsnode;
+
+  /* Allocate a new nscache node from slab */
+  nsnode = (struct sos_fs_nscache_node*)
+    sos_kmem_cache_alloc(cache_of_nscache_nodes,
+			 SOS_KSLAB_ALLOC_ATOMIC);
+  if (! nsnode)
+    return -SOS_ENOMEM;
+
+  /* Allocate a new memory chunk to hold the node's name */
+  if (node_name && (node_name->length > 0))
+    {
+      char * contents = (char*) sos_kmalloc(node_name->length,
+					    SOS_KMALLOC_ATOMIC);
+      if (! contents)
+	{
+	  sos_kfree((sos_vaddr_t)nsnode);
+	  return -SOS_ENOMEM;
+	}
+
+      memcpy(contents, node_name->contents, node_name->length);
+      nsnode->name.contents = contents;
+      nsnode->name.length   = node_name->length;
+    }
+
+  /* Now initialize the new node's fields */
+  nsnode->ref_cnt = 1;
+  sos_fs_ref_fsnode(fsnode);
+  nsnode->fs_node = fsnode;
+
+  /* Register this node as a child of its parent, if any */
+  nsnode->parent  = parent;
+  if (parent)
+    {
+      sos_fs_nscache_ref_node(parent);
+      list_add_head_named(parent->children, nsnode,
+			  siblings_prev, siblings_next);
+    }
+
+  *result_nsnode = nsnode;
+  return SOS_OK;
+}
+
+
+sos_ret_t
+sos_fs_nscache_add_existing_child_node(struct sos_fs_nscache_node * parent,
+				       const struct sos_fs_pathname * node_name,
+				       struct sos_fs_nscache_node * nsnode)
+{
+  SOS_ASSERT_FATAL(nsnode->parent == NULL);
+
+  /* If the node already had a name, suppress it */
+  if (NULL != nsnode->name.contents)
+    {
+      sos_kfree((sos_vaddr_t)nsnode->name.contents);
+    }
+  memset(& nsnode->name, 0x0, sizeof(struct sos_fs_pathname));
+
+  /* Allocate a new memory chunk to hold the node's name */
+  if (node_name && (node_name->length > 0))
+    {
+      char * contents = (char*) sos_kmalloc(node_name->length,
+					    SOS_KMALLOC_ATOMIC);
+      if (! contents)
+	{
+	  sos_kfree((sos_vaddr_t)nsnode);
+	  return -SOS_ENOMEM;
+	}
+
+      memcpy(contents, node_name->contents, node_name->length);
+      nsnode->name.contents = contents;
+      nsnode->name.length   = node_name->length;
+    }
+
+
+  /* Register this node as a child of its parent, if any */
+  nsnode->parent  = parent;
+  if (parent)
+    {
+      sos_fs_nscache_ref_node(parent);
+      list_add_head_named(parent->children, nsnode,
+			  siblings_prev, siblings_next);
+    }
+  return SOS_OK;
+}
+
+
+sos_ret_t
+sos_fs_nscache_disconnect_node(struct sos_fs_nscache_node * nsnode)
+{
+  if (! nsnode->parent)
+    return SOS_OK;
+
+  list_delete_named(nsnode->parent->children, nsnode,
+		    siblings_prev, siblings_next);
+  sos_fs_nscache_unref_node(nsnode->parent);
+  nsnode->parent = NULL;
+
+  return SOS_OK;
+}
+
+
+sos_ret_t
+sos_fs_nscache_register_opened_file(struct sos_fs_nscache_node * nsnode,
+				    struct sos_fs_opened_file * of)
+{
+  of->direntry = nsnode;
+  sos_fs_nscache_ref_node(nsnode);
+  return SOS_OK;
+}
+
+
+sos_ret_t
+sos_fs_nscache_mount(struct sos_fs_nscache_node * mountpoint,
+		     struct sos_fs_nscache_node * mounted_root)
+{
+  SOS_ASSERT_FATAL(NULL == mountpoint->mounted_root);
+  SOS_ASSERT_FATAL(NULL == mounted_root->mountpoint);
+  mountpoint->mounted_root = mounted_root;
+  mounted_root->mountpoint = mountpoint;
+  sos_fs_nscache_ref_node(mountpoint);
+  sos_fs_nscache_ref_node(mounted_root);
+  
+  return SOS_OK;
+}
+
+
+sos_ret_t
+sos_fs_nscache_umount(struct sos_fs_nscache_node * mounted_root)
+{
+  struct sos_fs_manager_instance *fs;
+
+  SOS_ASSERT_FATAL(NULL != mounted_root->mountpoint);
+  SOS_ASSERT_FATAL(mounted_root->mountpoint->mounted_root == mounted_root);
+
+  /* No FS should be mounted on the mounted root to umount */
+  SOS_ASSERT_FATAL(NULL == mounted_root->mounted_root);
+
+  /* The mounted root should have its own reference, plus a reference
+     from the mountpoint and from the fs instance */
+  SOS_ASSERT_FATAL(mounted_root->ref_cnt >= 3);
+  if (mounted_root->ref_cnt != 3)
+    return -SOS_EBUSY;
+
+  fs = mounted_root->fs_node->fs;
+  SOS_ASSERT_FATAL(NULL != fs);
+  SOS_ASSERT_FATAL(fs->root == mounted_root);
+
+  /* Undo the mountpoint <-> mounted_root mutual reference */
+  mounted_root->mountpoint->mounted_root = NULL;
+  sos_fs_nscache_unref_node(mounted_root->mountpoint);
+  mounted_root->mountpoint = NULL;
+  sos_fs_nscache_unref_node(mounted_root);
+
+  /* Undo the fs->root -> mounted_root reference */
+  sos_fs_nscache_unref_node(fs->root);
+
+  return SOS_OK;
+}
+
+sos_bool_t
+sos_fs_nscache_is_mountnode(const struct sos_fs_nscache_node * nsnode)
+{
+  return ( (NULL != nsnode->mounted_root) || (NULL != nsnode->mountpoint) );
+}
diff -ruN /tmp/sos-code-article7.5/sos/fs_nscache.h ../sos-code-article8/sos/fs_nscache.h
--- /tmp/sos-code-article7.5/sos/fs_nscache.h	1970-01-01 01:00:00.000000000 +0100
+++ ../sos-code-article8/sos/fs_nscache.h	2005-07-01 16:39:49.000000000 +0200
@@ -0,0 +1,284 @@
+/* Copyright (C) 2005      David Decotigny
+   Copyright (C) 2000-2005 The KOS Team (Thomas Petazzoni, David
+                           Decotigny, Julien Munier)
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License
+   as published by the Free Software Foundation; either version 2
+   of the License, or (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+   USA. 
+*/
+#ifndef _SOS_FS_NSCACHE_H_
+#define _SOS_FS_NSCACHE_H_
+
+
+/**
+ * @file fs_nscache.h
+ *
+ * FS Namespace cache (aka file hierarchy) management. Internal API
+ * reserved to fs.c and to the FS managers ! See fs.c for details and
+ * role of this subsystem in the whole VFS.
+ *
+ * We keep the usual filesystem semantics of a "file hierarchy":
+ *
+ *        parent0
+ *        /     \
+ *    child1    child2
+ *   /     \         \
+ * child1a child1b   child2a
+ *
+ * The system allows that different children actually reference the
+ * same "on-disk" node (sos_fs_node). For example: child1a and child2a
+ * might reference the same sos_fs_node: this represents a so-called
+ * "hard link".
+ *
+ * The functions of this module are in charge of updating the nscache
+ * nodes and their reference count. They don't influence the other
+ * subsystems (apart from the sos_fs_nscache_unref_node() function
+ * which can unreference the underlying sos_fs_node).
+ *
+ * Note: only the nscache nodes that are actually used or those that
+ * are their parents (ie in the path from these nodes to the global
+ * root) will remain in memory. The others will be destroyed as soon
+ * as they are not needed anymore. For example, il I do a
+ * stat("/mnt/toto/titi.txt", & st), all the nscache nodes from "/" to
+ * "titi.txt" will be allocated, the stat performed, and all of them
+ * will be destroyed. We could imagine a real "cache" here to avoid
+ * these bursts of allocations/deallocations, by keeping the last
+ * accessed nodes aside when they are not referenced anymore (in a
+ * hash table for example, the key being {parent nscache node address,
+ * child name}).
+ *
+ * Note about mountpoints: When a FS is mounted in, say "/mnt", the
+ * nscache node of the new FS is registered neither as its child nor
+ * as its parent, but as a kind of "brother" of /mnt. As seen from the
+ * global root ("/"), "mnt" in a direct child and the mounted root is
+ * its brother. But, once mounted, as seen from a child node
+ * "/mnt/toto", the mounted root is seen as the direct parent of
+ * /mnt/toto and "mnt" is seen as its brother. That is, each time we
+ * try to resolve (lookup) the children on a mountpoint, we must
+ * "follow" the mountchain. In the previous example, multiple
+ * successive FS could be mounted on the same "/mnt".
+ */
+
+#include <sos/types.h>
+#include <sos/errno.h>
+
+/**
+ * Opaque structure defined in fs_nscache.c
+ *
+ * Essentially contains:
+ *  - a name (allocated in-place)
+ *  - a reference to the associated FS node (struct sos_fs_node)
+ *  - a reference to the parent nscache node (if any)
+ *  - a list of pointers to the children nscache nodes (for directories)
+ */
+struct sos_fs_nscache_node;
+
+#include "fs.h"
+
+
+/**
+ * Support for non-0 terminated strings (Pascal-style). Useful to
+ * prevent from altering the contents of the string in order to split
+ * pathnames into components (@see sos_fs_pathname_split_path)
+ */
+struct sos_fs_pathname
+{
+  const char * contents;
+  sos_size_t length;
+};
+
+
+sos_ret_t sos_fs_nscache_subsystem_setup();
+
+
+/**
+ * Lookup the given entry in the given nsnode. The lookup is limited
+ * to the children entries that are already in memory. When this
+ * lookup fails, this simply means that the entry is not already in
+ * memory, and has to be resolved using disk accesses (@see
+ * fs_lookup_node in fs.c)
+ *
+ * @param cur_nsnode The node in which we are looking for the entry
+ * @param root_node The base node beyond which lookup must not go (to
+ * support chroot): a kind of "barrier"
+ *
+ * @param result_nsnode The nsnode for the given entry (set only when
+ * the return value is SOS_OK)
+ *
+ * @return error if the entry could not be found in the nsnode
+ * directory. OK otherwise, and *result_nsnode is set.
+ *
+ * @note The symlinks are NOT expanded. The mountpoints ARE followed.
+ * @note result_nsnode is a NEW reference to the node. It should be
+ * unreferenced when unused
+ */
+sos_ret_t
+sos_fs_nscache_lookup(struct sos_fs_nscache_node * cur_nsnode,
+		      const struct sos_fs_pathname * node_name,
+		      const struct sos_fs_nscache_node * root_nsnode,
+		      struct sos_fs_nscache_node ** result_nsnode);
+
+
+/**
+ * Add a new child node for the given parent, for the given fs_node
+ *
+ * @param parent might be NULL, meaning that the node is the root of a
+ * mounted filesystem
+ *
+ * @note The new node has the value 0 for the opened_file and
+ * mount_chain counters
+ * @note result_nsnode is a NEW reference to the node. It should be
+ * unreferenced when unused
+ */
+sos_ret_t
+sos_fs_nscache_add_new_child_node(struct sos_fs_nscache_node * parent,
+				  const struct sos_fs_pathname * node_name,
+				  struct sos_fs_node * fsnode,
+				  struct sos_fs_nscache_node ** result_nsnode);
+
+
+/**
+ * Add a new child node for the given parent, for the given already
+ * existing nsnode (with no parent !)
+ *
+ * @param parent can not be NULL
+ *
+ * @note nsnode should NOT have any parent
+ */
+sos_ret_t
+sos_fs_nscache_add_existing_child_node(struct sos_fs_nscache_node * parent,
+				       const struct sos_fs_pathname * node_name,
+				       struct sos_fs_nscache_node * nsnode);
+
+
+/**
+ * Disconnect the given node from its parent, if any
+ * @note reference count of nsnode is NOT modified
+ */
+sos_ret_t
+sos_fs_nscache_disconnect_node(struct sos_fs_nscache_node * nsnode);
+
+
+/**
+ * Register the given root of a new file system (mounted_root) in the
+ * mountpoint chain located at mountpoint, ie build the mountchain.
+ */
+sos_ret_t
+sos_fs_nscache_mount(struct sos_fs_nscache_node * mountpoint,
+		     struct sos_fs_nscache_node * mounted_root);
+
+
+/**
+ * Break the mountchain at the given mounted root, making sure that
+ * this nscache node is not reference by any opened file or child node
+ * anymore.
+ */
+sos_ret_t
+sos_fs_nscache_umount(struct sos_fs_nscache_node * mounted_root);
+
+
+/** Return true if the node is involved in any mountchain */
+sos_bool_t
+sos_fs_nscache_is_mountnode(const struct sos_fs_nscache_node * nsnode);
+
+
+/*
+ * Accessor functions
+ */
+
+
+/**
+ * Return the FS node of the given nscache node.
+ *
+ * @note The FS node returned is NOT newly referenced
+ */
+struct sos_fs_node *
+sos_fs_nscache_get_fs_node(const struct sos_fs_nscache_node * nsnode);
+
+
+/**
+ * Return the parent nscache node of the given nscache node.
+ *
+ * @note The nscache node returned is NOT newly referenced
+ */
+sos_ret_t
+sos_fs_nscache_get_parent(const struct sos_fs_nscache_node * nsnode,
+			  struct sos_fs_nscache_node ** result_parent);
+
+
+sos_ret_t
+sos_fs_nscache_get_name(const struct sos_fs_nscache_node * nsnode,
+			struct sos_fs_pathname * result_pathname);
+
+
+/**
+ * Return the value of the reference count for the given nscache node
+ */
+sos_ret_t
+sos_fs_nscache_get_ref_cnt(const struct sos_fs_nscache_node * nsnode);
+
+
+sos_ret_t
+sos_fs_nscache_register_opened_file(struct sos_fs_nscache_node * nsnode,
+				    struct sos_fs_opened_file * of);
+
+
+sos_ret_t sos_fs_nscache_ref_node(struct sos_fs_nscache_node * nsnode);
+
+
+sos_ret_t _sos_fs_nscache_unref_node(struct sos_fs_nscache_node ** nsnode);
+#define sos_fs_nscache_unref_node(n) _sos_fs_nscache_unref_node(& (n))
+
+
+/*
+ * Functions reserved to sos_fs_manager_type::mount() and
+ * sos_fs_manager_type::umount()
+ */
+#define sos_fs_nscache_create_mounted_root(fsnode,result_nsnode) \
+  sos_fs_nscache_add_new_child_node(NULL, NULL, (fsnode), (result_nsnode))
+
+
+/*
+ * Pathname manipulation functions
+ */
+
+sos_bool_t fs_pathname_iseq(const struct sos_fs_pathname * p1,
+			    const struct sos_fs_pathname * p2);
+
+/**
+ * Remove any leading slash from the path
+ *
+ * @Return TRUE when slashes were found at the begining
+ */
+sos_bool_t sos_fs_pathname_eat_slashes(const struct sos_fs_pathname * path,
+				       struct sos_fs_pathname * result);
+
+/**
+ * Transform "a/b/c" into { first_component="a" remaining_path="/b/c" }
+ * Transform "/a/b/c" into { first_component="a" remaining_path="/b/c" }
+ * Transform "////a////b/c" into { first_component="a" remaining_path="////b/c" }
+ * Transform "a" into { first_component="a" remaining_path="" }
+ * Transform "/a" into { first_component="a" remaining_path="" }
+ * Transform "a/" into { first_component="a" remaining_path="" }
+ * Transform "/a/" into { first_component="a" remaining_path="" }
+ *
+ * @Return TRUE when slashes after first component were found. In the
+ * previous example: true everywhere except for the path "a" and "/a"
+ */
+sos_bool_t
+sos_fs_pathname_split_path(const struct sos_fs_pathname * path,
+			   struct sos_fs_pathname * result_first_component,
+			   struct sos_fs_pathname * result_remaining_path);
+
+#endif /* _SOS_FS_NSCACHE_H_ */
diff -ruN /tmp/sos-code-article7.5/sos/hash.c ../sos-code-article8/sos/hash.c
--- /tmp/sos-code-article7.5/sos/hash.c	1970-01-01 01:00:00.000000000 +0100
+++ ../sos-code-article8/sos/hash.c	2005-07-01 16:39:49.000000000 +0200
@@ -0,0 +1,237 @@
+/* Copyright (C) 2005 David Decotigny
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License
+   as published by the Free Software Foundation; either version 2
+   of the License, or (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+   USA. 
+*/
+
+#include <sos/kmalloc.h>
+#include <sos/klibc.h>
+#include <sos/list.h>
+#include <sos/assert.h>
+
+#include "hash.h"
+
+#define SOS_HASH_NAME_MAXLEN 32
+
+
+/**
+ * @file hash.c
+ *
+ * A hash table is simply a table of lists: each list hash a "bucket
+ * index". Each list contains the element for which the hash of the
+ * key is equal to the bucket index (modulo the number of buckets).
+ */
+
+
+/**
+ * Structure of one list of elements
+ */
+struct bucket
+{
+  sos_count_t nb_elems;
+  struct sos_hash_linkage * list;
+};
+
+
+/**
+ * The table of buckets, ie the hash itself
+ */
+struct sos_hash_table
+{
+  char name[SOS_HASH_NAME_MAXLEN];
+
+  /** Hash function */
+  sos_hash_func_t        * key_hasher;
+
+  /** Key comparison function */
+  sos_hash_key_eq_func_t * key_iseq;
+
+  /** Memory-offset of the key in the element structure */
+  sos_uoffset_t            offset_h_key;
+
+  /** Memory-offset of the hash linkage in the element structure */
+  sos_uoffset_t            offset_h_linkage;
+
+  /** Number of buckets in this hash table */
+  sos_count_t              nbuckets;
+  
+  struct bucket bucket[0];
+};
+
+
+/** From the address of the given element, access to its hash_linkage
+    structrure */
+#define h_linkage_of_elt(h,elt) \
+  ( (struct sos_hash_linkage*) \
+    ( ((unsigned long)(elt)) + (h)->offset_h_linkage) )
+
+
+/** From the address of the given element, access to its pointer to
+    the key */
+#define h_keyptr_of_elt(h,elt) \
+  ( (void*) \
+    ( ((unsigned long)(elt)) + (h)->offset_h_key) )
+
+
+/** From the given hash linkage structure address, retrieve the
+    address of the surronding element */
+#define elt_for_h_linkage(h,linkage) \
+  ( (void*) \
+    ( ((unsigned long)(linkage)) - (h)->offset_h_linkage) )
+
+
+struct sos_hash_table *
+_sos_hash_create_FULL(const char             *name,
+		      sos_hash_func_t        *key_hasher,
+		      sos_hash_key_eq_func_t *key_iseq,
+		      sos_count_t            nbuckets,
+		      sos_uoffset_t          offset_h_key,
+		      sos_uoffset_t          offset_h_linkage)
+{
+  struct sos_hash_table * h;
+  h = (struct sos_hash_table*)
+    sos_kmalloc(sizeof(struct sos_hash_table)
+		+ nbuckets*sizeof(struct bucket), 0);
+
+  memset(h, 0x0,
+	 sizeof(struct sos_hash_table) + nbuckets*sizeof(struct bucket));
+  h->key_hasher       = key_hasher;
+  h->key_iseq         = key_iseq;
+  h->offset_h_linkage = offset_h_linkage;
+  h->offset_h_key     = offset_h_key;
+  h->nbuckets         = nbuckets;
+  strzcpy(h->name, name, SOS_HASH_NAME_MAXLEN);
+
+  return h;
+}
+
+
+sos_ret_t sos_hash_dispose(struct sos_hash_table *h)
+{
+  int i;
+  for (i = 0 ; i < h->nbuckets ; i++)
+    {
+      struct sos_hash_linkage * elt;
+
+      list_collapse_named(h->bucket[i].list, elt, h_prev, h_next)
+	{
+	  elt->h_prev = elt->h_next = NULL;
+	}
+    }
+
+  return sos_kfree((sos_vaddr_t)h);
+}
+
+
+sos_ret_t sos_hash_insert(struct sos_hash_table *h,
+			  void *elt_with_key)
+{
+  struct sos_hash_linkage * h_elt;
+  sos_uoffset_t bucket;
+
+  h_elt = h_linkage_of_elt(h, elt_with_key);
+  if (h_elt->h_prev || h_elt->h_next)
+    return -SOS_EBUSY;
+
+  if (h->key_hasher)
+    bucket = h->key_hasher(h_keyptr_of_elt(h, elt_with_key)) % h->nbuckets;
+  else
+    {
+      /* The key is assumed to be an integer */
+      unsigned long * keyval = h_keyptr_of_elt(h, elt_with_key);
+      bucket = *keyval % h->nbuckets;
+    }
+
+  list_add_head_named(h->bucket[bucket].list, h_elt, h_prev, h_next);
+  h->bucket[bucket].nb_elems ++;
+
+  return SOS_OK;
+}
+
+
+void * sos_hash_lookup(struct sos_hash_table *h,
+		       const void * ptr_key)
+{
+  struct sos_hash_linkage * h_elt;
+  sos_uoffset_t bucket;
+  int nb;
+
+  if (h->key_hasher)
+    bucket = h->key_hasher(ptr_key) % h->nbuckets;
+  else
+    {
+      /* The key is assumed to be an integer */
+      const unsigned long * keyval = ptr_key;
+      bucket = *keyval % h->nbuckets;
+    }
+
+  list_foreach_forward_named(h->bucket[bucket].list, h_elt, nb, h_prev, h_next)
+    {
+      void * elt         = elt_for_h_linkage(h, h_elt);
+      void * elt_ptr_key = h_keyptr_of_elt(h, elt);
+
+      if (ptr_key == elt_ptr_key)
+	  return elt;
+
+      if (! h->key_iseq)
+	continue;
+
+      if (h->key_iseq(ptr_key, elt_ptr_key))
+	return elt;
+    }
+
+  return NULL;
+}
+
+
+sos_ret_t sos_hash_remove(struct sos_hash_table *h,
+			  void * elt)
+{
+  struct sos_hash_linkage * h_elt;
+  sos_uoffset_t bucket;
+
+  h_elt = h_linkage_of_elt(h, elt);
+  SOS_ASSERT_FATAL(h_elt->h_prev && h_elt->h_next);
+
+  if (h->key_hasher)
+    bucket = h->key_hasher(h_keyptr_of_elt(h, elt)) % h->nbuckets;
+  else
+    {
+      unsigned long * keyval = h_keyptr_of_elt(h, elt);
+      bucket = *keyval % h->nbuckets;
+    }
+
+  list_delete_named(h->bucket[bucket].list, h_elt, h_prev, h_next);
+  h->bucket[bucket].nb_elems --;
+
+  return SOS_OK;
+}
+
+
+unsigned long sos_hash_ui64(const void * ptr_key)
+{
+  const sos_ui64_t * keyval = ptr_key;
+  return ((*keyval) * 302954987) & 0xffffffff;
+}
+
+
+sos_bool_t sos_hash_key_eq_ui64(const void * ptr_key1,
+				const void * ptr_key2)
+{
+  const sos_ui64_t * keyval1 = ptr_key1;
+  const sos_ui64_t * keyval2 = ptr_key2;
+  
+  return (*keyval1 == *keyval2);
+}
diff -ruN /tmp/sos-code-article7.5/sos/hash.h ../sos-code-article8/sos/hash.h
--- /tmp/sos-code-article7.5/sos/hash.h	1970-01-01 01:00:00.000000000 +0100
+++ ../sos-code-article8/sos/hash.h	2005-07-01 16:39:49.000000000 +0200
@@ -0,0 +1,128 @@
+/* Copyright (C) 2005 David Decotigny
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License
+   as published by the Free Software Foundation; either version 2
+   of the License, or (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+   USA. 
+*/
+#ifndef _SOS_HASH_H_
+#define _SOS_HASH_H_
+
+#include <sos/types.h>
+#include <sos/errno.h>
+#include <sos/macros.h>
+
+/**
+ * hash.h
+ *
+ * Hash table implementation. The key and the element structure is
+ * user-definable. Each element must embed:
+ *  - the key
+ *  - a sos_hash_linkage structure
+ *
+ * The DANGER is that the key value must NOT be changed while the
+ * element is inserted in the hash
+ */
+
+/** Prototype of a hash function */
+typedef unsigned long (sos_hash_func_t)(const void * ptr_key);
+
+/** Prototype of a key comparison function */
+typedef sos_bool_t (sos_hash_key_eq_func_t)(const void * ptr_key1,
+					    const void * ptr_key2);
+
+/** Opaque structure */
+struct sos_hash_table;
+
+/** This structure must be embedded in the elements to be insterted in
+    the hah */
+struct sos_hash_linkage
+{
+  struct sos_hash_linkage * h_prev, * h_next;
+};
+
+
+/**
+ * Creation of a hash table
+ *
+ * @param name The name of the hash table (debug)
+ * @param elt_type The type of the elements
+ * @param hfunc The hash function (@see sos_hash_func_t)
+ * @param hfunc The element comparaison function (@see
+ *              sos_hash_key_eq_func_t)
+ * @param nbuckets The number of bucks in the hash
+ * @param name_key_field The name of the field in the element type
+ *                       that holds the key
+ * @param name_key_field The name of the field in the element type
+ *                       that hold the prev/next hash linkage data
+ */
+#define sos_hash_create(name,elt_type,hfunc,hcmp,nbuckets,\
+                        name_key_field,name_h_linkage)    \
+  _sos_hash_create_FULL(name, hfunc, hcmp, nbuckets,           \
+                        offsetof(elt_type, name_key_field),    \
+                        offsetof(elt_type, name_h_linkage))
+
+
+/**
+ * @note Real hash creation function called by the sos_hash_create
+ * macro
+ *
+ * @param key_hasher When NULL: the value of the hash is directly the
+ *                   pointer address
+ * @param key_compare When NULL: compare directly the pointer addresses
+ */
+struct sos_hash_table *
+_sos_hash_create_FULL(const char             *name,
+		      sos_hash_func_t        *key_hasher,
+		      sos_hash_key_eq_func_t *key_iseq,
+		      sos_count_t            nbuckets,
+		      sos_uoffset_t          offset_h_key,
+		      sos_uoffset_t          offset_h_linkage);
+
+
+/** Does not free the elements themselves ! */
+sos_ret_t sos_hash_dispose(struct sos_hash_table *h);
+
+
+/**
+ * Insert the element in the hash, associating it with the key that it
+ * embeds
+ *
+ * Makes sure the element is not already in the hash */
+sos_ret_t sos_hash_insert(struct sos_hash_table *h,
+			  void *elt_with_key);
+
+
+/** Look for the element stored in the hash that has the key given by
+    ptr_key */
+void * sos_hash_lookup(struct sos_hash_table *h,
+		       const void * ptr_key);
+
+
+/** Remove an element from the hash, previously returned by
+    sos_hash_lookup */
+sos_ret_t sos_hash_remove(struct sos_hash_table *h,
+			  void *elt);
+
+
+/*
+ * Common hash functions
+ */
+
+/* key = 64bits integer */
+unsigned long sos_hash_ui64(const void * ptr_key);
+sos_bool_t sos_hash_key_eq_ui64(const void * ptr_key1,
+				const void * ptr_key2);
+
+
+#endif /* _SOS_HASH_H_ */
diff -ruN /tmp/sos-code-article7.5/sos/main.c ../sos-code-article8/sos/main.c
--- /tmp/sos-code-article7.5/sos/main.c	2005-04-27 20:17:17.000000000 +0200
+++ ../sos-code-article8/sos/main.c	2005-07-01 16:39:49.000000000 +0200
@@ -16,6 +16,8 @@
    USA. 
 */
 
+#include <sos/errno.h>
+
 /* Include definitions of the multiboot standard */
 #include <bootstrap/multiboot.h>
 #include <hwcore/idt.h>
@@ -42,7 +44,8 @@
 #include <sos/umem_vmm.h>
 #include <sos/binfmt_elf32.h>
 #include <drivers/zero.h>
-
+#include <sos/fs.h>
+#include <drivers/fs_virtfs.h>
 
 /* Helper function to display each bits of a 32bits integer on the
    screen as dark or light carrets */
@@ -64,7 +67,6 @@
     }
 }
 
-
 /* Clock IRQ handler */
 static void clk_it(int intid)
 {
@@ -269,13 +271,15 @@
 /* ======================================================================
  * Start the "init" (userland) process
  */
-static sos_ret_t start_init()
+static sos_ret_t
+start_init(struct sos_fs_manager_instance * rootfs)
 {
   sos_ret_t retval;
   struct sos_umem_vmm_as *as_init;
   struct sos_process *proc_init;
   struct sos_thread *new_thr;
   sos_uaddr_t ustack, start_uaddr;
+  struct sos_fs_opened_file * init_root, * init_cwd, * unused_of;
 
   /* Create the new process */
   proc_init = sos_process_create("init", FALSE);
@@ -283,6 +287,44 @@
     return -SOS_ENOMEM;
   as_init = sos_process_get_address_space(proc_init);
 
+
+  /*
+   * Setup the root and CWD directories of the process. The root of
+   * this process will correspond to the "global" root of the whole
+   * system since all the future processes will duplicate it !
+   */
+  retval = sos_fs_new_opened_file(proc_init, rootfs->root,
+				  SOS_FS_OPEN_READ | SOS_FS_OPEN_WRITE,
+				  & init_root);
+  if (SOS_OK != retval)
+    {
+      sos_process_unref(proc_init);
+      return -SOS_ENOENT;
+    }
+
+  /* Duplicate the root file to set the current working directory of
+     the init process */
+  retval = sos_fs_duplicate_opened_file(init_root, proc_init,
+					& init_cwd);
+  if (SOS_OK != retval)
+    {
+      sos_fs_close(init_root);
+      sos_process_unref(proc_init);
+      return -SOS_ENOENT;
+    }
+
+  /* Now update the process ! */
+  if ( ( SOS_OK != sos_process_chroot(proc_init, init_root, & unused_of) )
+       || ( SOS_OK != sos_process_chdir(proc_init, init_cwd, & unused_of) ) )
+    {
+      sos_fs_close(init_root);
+      sos_fs_close(init_cwd);
+      sos_process_chroot(proc_init, NULL, & unused_of);
+      sos_process_chdir(proc_init, NULL, & unused_of);
+      sos_process_unref(proc_init);
+      return -SOS_ENOENT;
+    }
+
   /* Map the 'init' program in user space */
   start_uaddr = sos_binfmt_elf32_map(as_init, "init");
   if (0 == start_uaddr)
@@ -290,7 +332,7 @@
       sos_process_unref(proc_init);
       return -SOS_ENOENT;
     }
-     
+
   /* Allocate the user stack */
   ustack = (SOS_PAGING_TOP_USER_ADDRESS - SOS_DEFAULT_USER_STACK_SIZE) + 1;
   retval = sos_dev_zero_map(as_init, &ustack, SOS_DEFAULT_USER_STACK_SIZE,
@@ -298,7 +340,6 @@
 			    /* PRIVATE */ 0);
   if (SOS_OK != retval)
     {
-      sos_bochs_printf("ici 2\n");
       sos_process_unref(proc_init);
       return -SOS_ENOMEM;
     }
@@ -312,7 +353,6 @@
 				   SOS_SCHED_PRIO_TS_LOWEST);
   if (! new_thr)
     {
-      sos_bochs_printf("ici 3\n");
       sos_process_unref(proc_init);
       return -SOS_ENOMEM;
     }
@@ -325,17 +365,15 @@
 /* ======================================================================
  * The C entry point of our operating system
  */
-void sos_main(unsigned long magic, unsigned long addr)
+void sos_main(unsigned long magic, unsigned long arg)
 {
   unsigned i;
   sos_paddr_t sos_kernel_core_base_paddr, sos_kernel_core_top_paddr;
   struct sos_time tick_resolution;
+  struct sos_fs_manager_instance * rootfs;
 
-  /* Grub sends us a structure, called multiboot_info_t with a lot of
-     precious informations about the system, see the multiboot
-     documentation for more information. */
-  multiboot_info_t *mbi;
-  mbi = (multiboot_info_t *) addr;
+  /* Size of RAM above 1MB. Might be undefined ! */
+  unsigned long int upper_mem = 0;
 
   /* Setup bochs and console, and clear the console */
   sos_bochs_setup();
@@ -345,20 +383,42 @@
 
   /* Greetings from SOS */
   if (magic == MULTIBOOT_BOOTLOADER_MAGIC)
-    /* Loaded with Grub */
-    sos_x86_videomem_printf(1, 0,
-			    SOS_X86_VIDEO_FG_YELLOW | SOS_X86_VIDEO_BG_BLUE,
-			    "Welcome From GRUB to %s%c RAM is %dMB (upper mem = 0x%x kB)",
-			    "SOS article 7.5", ',',
-			    (unsigned)(mbi->mem_upper >> 10) + 1,
-			    (unsigned)mbi->mem_upper);
+    {
+      /* Grub sends us a structure, called multiboot_info_t with a lot of
+	 precious informations about the system, see the multiboot
+	 documentation for more information. */
+      multiboot_info_t *mbi = (multiboot_info_t *) arg;
+
+      /* Multiboot says: "The value returned for upper memory is
+	 maximally the address of the first upper memory hole minus 1
+	 megabyte.". It also adds: "It is not guaranteed to be this
+	 value." aka "YMMV" ;) */
+      upper_mem = mbi->mem_upper;
+      sos_x86_videomem_printf(1, 0,
+			      SOS_X86_VIDEO_FG_YELLOW | SOS_X86_VIDEO_BG_BLUE,
+			      "Welcome From GRUB to %s%c RAM is %dMB (upper mem = 0x%x kB)",
+			      "SOS article 8", ',',
+			      (unsigned)(upper_mem >> 10) + 1,
+			      (unsigned)upper_mem);
+    }
+  else if (magic == 0x42244224)
+    {
+      /* Loaded with SOS bootsect */
+      upper_mem = arg;
+      sos_x86_videomem_printf(1, 0,
+			      SOS_X86_VIDEO_FG_YELLOW | SOS_X86_VIDEO_BG_BLUE,
+			      "Welcome to %s%c RAM is %dMB (upper mem = 0x%x kB)",
+			      "SOS article 8", ',',
+			      (unsigned)(upper_mem >> 10) + 1,
+			      (unsigned)upper_mem);
+    }
   else
-    /* Not loaded with grub */
+    /* Not loaded with grub, not from an enhanced bootsect */
     sos_x86_videomem_printf(1, 0,
 			    SOS_X86_VIDEO_FG_YELLOW | SOS_X86_VIDEO_BG_BLUE,
-			    "Welcome to SOS article 7.5");
+			    "Welcome to SOS article 8");
 
-  sos_bochs_putstring("Message in a bochs: This is SOS article 7.5.\n");
+  sos_bochs_putstring("Message in a bochs: This is SOS article 8.\n");
 
   /* Setup CPU segmentation and IRQ subsystem */
   sos_gdt_subsystem_setup();
@@ -376,14 +436,14 @@
   tick_resolution = (struct sos_time) { .sec=0, .nanosec=10000000UL };
   sos_time_subsysem_setup(& tick_resolution);
 
-  /* We need a multiboot-compliant boot loader to get the size of the RAM */
-  if (magic != MULTIBOOT_BOOTLOADER_MAGIC)
+  /* We need to know the RAM size */
+  if (upper_mem == 0)
     {
       sos_x86_videomem_putstring(20, 0,
 				 SOS_X86_VIDEO_FG_LTRED
 				   | SOS_X86_VIDEO_BG_BLUE
 				   | SOS_X86_VIDEO_FG_BLINKING,
-				 "I'm not loaded with Grub !");
+				 "I don't know RAM size ! Load me with Grub...");
       /* STOP ! */
       for (;;)
 	continue;
@@ -401,12 +461,10 @@
    * Setup physical memory management
    */
 
-  /* Multiboot says: "The value returned for upper memory is maximally
-     the address of the first upper memory hole minus 1 megabyte.". It
-     also adds: "It is not guaranteed to be this value." aka "YMMV" ;) */
-  sos_physmem_subsystem_setup((mbi->mem_upper<<10) + (1<<20),
-			      & sos_kernel_core_base_paddr,
-			      & sos_kernel_core_top_paddr);
+  SOS_ASSERT_FATAL(SOS_OK
+		   == sos_physmem_subsystem_setup((upper_mem<<10) + (1<<20),
+						  &sos_kernel_core_base_paddr,
+						  &sos_kernel_core_top_paddr));
   
   /*
    * Switch to paged-memory mode
@@ -492,10 +550,17 @@
      interrupt call the scheduler */
   asm volatile ("sti\n");
 
+
+  SOS_ASSERT_FATAL(SOS_OK == sos_fs_virtfs_subsystem_setup());
+  SOS_ASSERT_FATAL(SOS_OK == sos_fs_subsystem_setup(NULL,
+						    "virtfs",
+						    NULL,
+						    & rootfs));
+
+
   /* Start the 'init' process, which in turns launches the other
      programs */
-  start_init();
-
+  start_init(rootfs);
   /*
    * We can safely exit from this function now, for there is already
    * an idle Kernel thread ready to make the CPU busy working...
diff -ruN /tmp/sos-code-article7.5/sos/physmem.c ../sos-code-article8/sos/physmem.c
--- /tmp/sos-code-article7.5/sos/physmem.c	2005-04-27 20:17:17.000000000 +0200
+++ ../sos-code-article8/sos/physmem.c	2005-07-01 16:39:49.000000000 +0200
@@ -293,6 +293,7 @@
       /* Transfer the page, considered NON-FREE, to the free list */
       list_delete(nonfree_ppage, ppage_descr);
       physmem_nonfree_pages --;
+      
       list_add_head(free_ppage, ppage_descr);
 
       /* Indicate that the page is now unreferenced */
diff -ruN /tmp/sos-code-article7.5/sos/process.c ../sos-code-article8/sos/process.c
--- /tmp/sos-code-article7.5/sos/process.c	2005-04-27 20:17:18.000000000 +0200
+++ ../sos-code-article8/sos/process.c	2005-07-01 16:39:49.000000000 +0200
@@ -26,8 +26,8 @@
 
 #include "process.h"
 
-
-#define SOS_PROCESS_MAX_NAMELEN 32
+#define SOS_PROCESS_MAX_OPENED_FILES  64
+#define SOS_PROCESS_MAX_NAMELEN       32
 
 
 /**
@@ -51,6 +51,15 @@
   /** Reference counter, including threads */
   sos_count_t            ref_cnt;
 
+  /** The array of opened file descriptors */
+  struct sos_fs_opened_file * fds[SOS_PROCESS_MAX_OPENED_FILES];
+
+  /** Where the root of the process is (for chroot support). May be NULL */
+  struct sos_fs_opened_file * root;
+
+  /** Where the current working dir of the process is */
+  struct sos_fs_opened_file * cwd;
+
   struct sos_process     *prev, *next;
 };
 
@@ -119,6 +128,7 @@
 struct sos_process *sos_process_create(const char *name,
 				       sos_bool_t do_copy_current_process)
 {
+  sos_ret_t retval = SOS_OK;
   sos_ui32_t flags;
   struct sos_process *proc;
 
@@ -129,6 +139,35 @@
   /* proc is already filled with 0 (cache has SOS_KSLAB_CREATE_ZERO
      flag) */
 
+  /* Copy the file descriptors when needed */
+  if (do_copy_current_process)
+    {
+      struct sos_process * myself = sos_thread_get_current()->process;
+      int fd;
+
+      for (fd = 0 ; fd < SOS_PROCESS_MAX_OPENED_FILES ; fd++)
+	if (NULL != myself->fds[fd])
+	  {
+	    retval = sos_fs_duplicate_opened_file(myself->fds[fd],
+						  proc,
+						  & proc->fds[fd]);
+	    if (SOS_OK != retval)
+	      goto end_create_proc;
+	  }
+
+      retval = sos_fs_duplicate_opened_file(myself->root,
+					    proc,
+					    & proc->root);
+      if (SOS_OK != retval)
+	goto end_create_proc;
+
+      retval = sos_fs_duplicate_opened_file(myself->cwd,
+					    proc,
+					    & proc->cwd);
+      if (SOS_OK != retval)
+	goto end_create_proc;
+    }
+
   if (do_copy_current_process)
     proc->address_space = sos_umem_vmm_duplicate_current_thread_as(proc);
   else
@@ -137,8 +176,8 @@
   if (NULL == proc->address_space)
     {
       /* Error */
-      sos_kmem_cache_free((sos_vaddr_t)proc);
-      return NULL;
+      retval = -SOS_ENOMEM;
+      goto end_create_proc;
     }
 
   if (!name)
@@ -159,6 +198,26 @@
 
   /* Mark the process as referenced */
   proc->ref_cnt = 1;
+
+ end_create_proc:
+  if (SOS_OK != retval)
+    {
+      int fd;
+
+      /* Close the file descriptors */
+      for (fd = 0 ; fd < SOS_PROCESS_MAX_OPENED_FILES ; fd++)
+	if (NULL != proc->fds[fd])
+	  sos_fs_close(proc->fds[fd]);
+
+      if (proc->root)
+	sos_fs_close(proc->root);
+      if (proc->cwd)
+	sos_fs_close(proc->cwd);
+
+      sos_kmem_cache_free((sos_vaddr_t) proc);
+      proc = NULL;
+    }
+
   return proc;
 }
 
@@ -202,6 +261,14 @@
 sos_ret_t sos_process_set_address_space(struct sos_process *proc,
 					struct sos_umem_vmm_as *new_as)
 {
+  int fd;
+
+  /* Close the FD that are not allowed to be duplicated */
+  for (fd = 0 ; fd < SOS_PROCESS_MAX_OPENED_FILES ; fd++)
+    if ( (NULL != proc->fds[fd])
+	 && (! (proc->fds[fd]->open_flags & SOS_FS_OPEN_KEEPONEXEC)) )
+      sos_fs_close(proc->fds[fd]);
+
   if (proc->address_space)
     {
       sos_ret_t retval = sos_umem_vmm_delete_as(proc->address_space);
@@ -214,6 +281,83 @@
 }
 
 
+struct sos_fs_opened_file *
+sos_process_get_root(const struct sos_process *proc)
+{
+  return proc->root;
+}
+
+
+struct sos_fs_opened_file *
+sos_process_get_cwd(const struct sos_process *proc)
+{
+  return proc->cwd;
+}
+
+
+struct sos_fs_opened_file *
+sos_process_get_opened_file(const struct sos_process *proc,
+			    int fd)
+{
+  if ((fd < 0) || (fd >= SOS_PROCESS_MAX_OPENED_FILES))
+    return NULL;
+  return proc->fds[fd];
+}
+
+
+sos_ret_t
+sos_process_chroot(struct sos_process *proc,
+		   struct sos_fs_opened_file * new_root,
+		   struct sos_fs_opened_file ** old_root)
+{
+  *old_root = proc->root;
+  proc->root = new_root;
+
+  return SOS_OK;
+}
+
+
+sos_ret_t
+sos_process_chdir(struct sos_process *proc,
+		  struct sos_fs_opened_file * new_cwd,
+		  struct sos_fs_opened_file ** old_cwd)
+{
+  *old_cwd = proc->cwd;
+  proc->cwd = new_cwd;
+
+  return SOS_OK;
+}
+
+
+sos_ret_t
+sos_process_register_opened_file(struct sos_process *proc,
+				 struct sos_fs_opened_file * of)
+{
+  int i;
+  for (i = 0 ; i < SOS_PROCESS_MAX_OPENED_FILES ; i++)
+    if (NULL == proc->fds[i])
+      {
+	proc->fds[i] = of;
+	return i;
+      }
+
+  return -SOS_EMFILE;
+}
+
+
+sos_ret_t
+sos_process_unregister_opened_file(struct sos_process *proc,
+				   int fd)
+{  
+  if ((fd < 0) || (fd >= SOS_PROCESS_MAX_OPENED_FILES))
+    return -SOS_EBADF;
+
+  proc->fds[fd] = NULL;
+  return SOS_OK;
+}
+
+
+
 /* ***************************************************
  * Restricted functions
  */
@@ -254,6 +398,7 @@
 {
   sos_ui32_t flags;
   sos_ret_t retval;
+  int fd;
 
   SOS_ASSERT_FATAL(proc->ref_cnt > 0);
 
@@ -267,6 +412,14 @@
   list_delete(process_list, proc);
   sos_restore_IRQs(flags);
 
+  /* Close the file descriptors */
+  for (fd = 0 ; fd < SOS_PROCESS_MAX_OPENED_FILES ; fd++)
+    if (NULL != proc->fds[fd])
+      sos_fs_close(proc->fds[fd]);
+  
+  sos_fs_close(proc->root);
+  sos_fs_close(proc->cwd);
+
   /* First: free the user address space */
   retval = sos_umem_vmm_delete_as(proc->address_space);
   SOS_ASSERT_FATAL(SOS_OK == retval);
diff -ruN /tmp/sos-code-article7.5/sos/process.h ../sos-code-article8/sos/process.h
--- /tmp/sos-code-article7.5/sos/process.h	2005-04-27 20:17:18.000000000 +0200
+++ ../sos-code-article8/sos/process.h	2005-07-01 16:39:49.000000000 +0200
@@ -37,13 +37,13 @@
 
 #include <sos/errno.h>
 
-
 /**
  * The definition of an SOS process is opaque. @see process.c
  */
 struct sos_process;
 
 #include <sos/thread.h>
+#include <sos/fs.h>
 
 
 /**
@@ -116,6 +116,72 @@
 sos_process_get_address_space(const struct sos_process *proc);
 
 
+/**
+ * Retrieve the root FS node of the process
+ *
+ * @return NULL on error
+ */
+struct sos_fs_opened_file *
+sos_process_get_root(const struct sos_process *proc);
+
+
+/**
+ * Retrieve the current working dir of the process
+ *
+ * @return NULL on error
+ */
+struct sos_fs_opened_file *
+sos_process_get_cwd(const struct sos_process *proc);
+
+
+/**
+ * Retrieve the opened file structure corresponding to the given FD
+ *
+ * @return NULL on error
+ */
+struct sos_fs_opened_file *
+sos_process_get_opened_file(const struct sos_process *proc,
+			    int fd);
+
+
+/**
+ * Change the root directory for the process
+ */
+sos_ret_t
+sos_process_chroot(struct sos_process *proc,
+		   struct sos_fs_opened_file * new_root,
+		   struct sos_fs_opened_file ** old_root);
+
+
+/**
+ * Change the working directory of the process
+ */
+sos_ret_t
+sos_process_chdir(struct sos_process *proc,
+		  struct sos_fs_opened_file * new_cwd,
+		  struct sos_fs_opened_file ** old_cwd);
+
+
+/**
+ * Allocate a new file descriptor for file
+ *
+ * @return >=0 on success, <0 on error (errno)
+ */
+sos_ret_t
+sos_process_register_opened_file(struct sos_process *proc,
+				 struct sos_fs_opened_file * of);
+
+
+/**
+ * Free the given file descriptor
+ *
+ * @return >=0 on success, <0 on error (errno)
+ */
+sos_ret_t
+sos_process_unregister_opened_file(struct sos_process *proc,
+				   int fd);
+
+
 /* ***************************************************
  * Restricted functions
  */
diff -ruN /tmp/sos-code-article7.5/sos/sched.c ../sos-code-article8/sos/sched.c
--- /tmp/sos-code-article7.5/sos/sched.c	2005-04-27 20:17:17.000000000 +0200
+++ ../sos-code-article8/sos/sched.c	2005-07-01 16:39:49.000000000 +0200
@@ -131,7 +131,7 @@
     return SOS_OK;
 
   /* Reset the CPU time used in the quantuum */
-  memset(& thr->running.user_time_spent_in_slice, 0x0, sizeof(struct sos_time));
+  memset(& thr->user_time_spent_in_slice, 0x0, sizeof(struct sos_time));
 
   if (SOS_SCHED_PRIO_IS_RT(sos_thread_get_priority(thr)))
     {
@@ -185,7 +185,7 @@
   /* Current (user) thread expired its time quantuum ?  A kernel
      thread never expires because sos_sched_do_timer_tick() below
      won't update its user_time_spent_in_slice */
-  if (sos_time_cmp(& thr->running.user_time_spent_in_slice,
+  if (sos_time_cmp(& thr->user_time_spent_in_slice,
 		   & time_slice[prio]) >= 0)
       return TRUE;
 
@@ -203,7 +203,7 @@
   if (thread_expired_its_quantuum(current_thread))
     {
       /* Reset the CPU time used in the quantuum */
-      memset(& current_thread->running.user_time_spent_in_slice,
+      memset(& current_thread->user_time_spent_in_slice,
 	     0x0, sizeof(struct sos_time));
 
       do_yield = TRUE;
@@ -290,7 +290,7 @@
 		   & tick_duration);
 
       /* Update time spent is current timeslice ONLY for a user thread */
-      sos_time_inc(& interrupted_thread->running.user_time_spent_in_slice,
+      sos_time_inc(& interrupted_thread->user_time_spent_in_slice,
 		   & tick_duration);
     }
   else
diff -ruN /tmp/sos-code-article7.5/sos/syscall.c ../sos-code-article8/sos/syscall.c
--- /tmp/sos-code-article7.5/sos/syscall.c	2005-04-27 20:17:18.000000000 +0200
+++ ../sos-code-article8/sos/syscall.c	2005-07-01 16:39:49.000000000 +0200
@@ -44,6 +44,7 @@
     case SOS_SYSCALL_ID_EXIT:
       {
 	unsigned int status;
+
 	retval = sos_syscall_get1arg(user_ctxt, & status);
 	if (SOS_OK != retval)
 	  break;
@@ -94,15 +95,17 @@
     case SOS_SYSCALL_ID_EXEC:
       {
 	struct sos_thread *cur_thr, *new_thr;
+	struct sos_process * proc;
 	struct sos_umem_vmm_as *new_as;
 	sos_uaddr_t user_str, ustack, start_uaddr;
 	sos_size_t len;
 	char * str;
 
 	cur_thr = sos_thread_get_current();
+	proc    = cur_thr->process;
 
 	/* Make sure the process has exactly 1 thread in it */
-	if (sos_process_get_nb_threads(cur_thr->process) != 1)
+	if (sos_process_get_nb_threads(proc) != 1)
 	  {
 	    retval = -SOS_EBUSY;
 	    break;
@@ -114,21 +117,14 @@
 	  break;
 
 	/* Copy the program name into kernel sppace */
-	str = (char*)sos_kmalloc(len + 1, 0);
-	if (! str)
-	  {
-	    retval = -SOS_ENOMEM;
-	    break;
-	  }
-	retval = sos_strzcpy_from_user(str, user_str, len + 1);
-	if (retval < SOS_OK)
+	retval = sos_strndup_from_user(& str, user_str, len + 1, 0);
+	if (SOS_OK != retval)
 	  {
-	    sos_kfree((sos_vaddr_t)str);
 	    break;
 	  }
 
 	/* Create a new empty address space to map the program */
-	new_as = sos_umem_vmm_create_empty_as(cur_thr->process);
+	new_as = sos_umem_vmm_create_empty_as(proc);
 	if (! new_as)
 	  {
 	    sos_kfree((sos_vaddr_t)str);
@@ -162,7 +158,7 @@
 
 	/* Now create the user thread */
 	new_thr = sos_create_user_thread(NULL,
-					 cur_thr->process,
+					 proc,
 					 start_uaddr,
 					 0, 0,
 					 ustack + SOS_DEFAULT_USER_STACK_SIZE
@@ -176,10 +172,10 @@
 	    break;	    
 	  }
 
-	sos_process_set_name(cur_thr->process, str);
+	sos_process_set_name(proc, str);
 
 	/* Switch to this address space */
-	retval = sos_process_set_address_space(cur_thr->process,
+	retval = sos_process_set_address_space(proc,
 					       new_as);
 	if (SOS_OK != retval)
 	  {
@@ -195,7 +191,7 @@
       }
       break;
 
-    case SOS_SYSCALL_ID_MMAP:
+    case SOS_SYSCALL_ID_FAKEMMAP:
       {
 	sos_uaddr_t ptr_hint_uaddr;
 	sos_uaddr_t hint_uaddr;
@@ -417,6 +413,7 @@
 	    retval = -SOS_ENOMEM;
 	    break;	    
 	  }
+
       }
       break;
 
@@ -456,6 +453,846 @@
       }
       break;
 
+      
+      /**
+       * File system interface
+       */
+    case SOS_SYSCALL_ID_MOUNT:
+      {
+	sos_uaddr_t user_src;
+	sos_size_t srclen;
+	const char * kernel_src = NULL;
+	sos_uaddr_t user_dst;
+	sos_size_t dstlen;
+	const char * kernel_dst;
+	sos_ui32_t mountflags;
+	sos_uaddr_t user_fstype;
+	char * kernel_fstype;
+	sos_uaddr_t user_args;
+	char * kernel_args = NULL;
+	struct sos_process * proc;
+
+	proc = sos_thread_get_current()->process;
+	retval = sos_syscall_get7args(user_ctxt,
+				      (unsigned int*)& user_src,
+				      (unsigned int*)& srclen,
+				      (unsigned int*)& user_dst,
+				      (unsigned int*)& dstlen,
+				      (unsigned int*)& user_fstype,
+				      (unsigned int*)& mountflags,
+				      (unsigned int*)& user_args);
+	if (SOS_OK != retval)
+	  break;
+
+	if (user_src != (sos_uaddr_t)NULL)
+	  {
+	    retval = sos_memdup_from_user((sos_vaddr_t*) &kernel_src, user_src, srclen, 0);
+	    if (SOS_OK != retval)
+	      break;
+	  }
+
+	retval = sos_memdup_from_user((sos_vaddr_t*) &kernel_dst, user_dst, dstlen, 0);
+	if (SOS_OK != retval)
+	  {
+	    if (kernel_src)
+	      sos_kfree((sos_vaddr_t)kernel_src);
+	    break;
+	  }
+
+	retval = sos_strndup_from_user(& kernel_fstype, user_fstype, 256, 0);
+	if (SOS_OK != retval)
+	  {
+	    if (kernel_src)
+	      sos_kfree((sos_vaddr_t)kernel_src);
+	    sos_kfree((sos_vaddr_t)kernel_dst);
+	    break;
+	  }
+
+	if (user_args != (sos_uaddr_t)NULL)
+	  {
+	    retval = sos_strndup_from_user(& kernel_args, user_args, 1024, 0);
+	    if (SOS_OK != retval)
+	      {
+		if (kernel_src)
+		  sos_kfree((sos_vaddr_t)kernel_src);
+		sos_kfree((sos_vaddr_t)kernel_dst);
+		sos_kfree((sos_vaddr_t)kernel_fstype);
+		break;
+	      }
+	  }
+
+	retval = sos_fs_mount(proc, kernel_src, srclen,
+			      kernel_dst, dstlen,
+			      kernel_fstype,
+			      mountflags,
+			      kernel_args,
+			      NULL);
+	if (kernel_src)
+	  sos_kfree((sos_vaddr_t)kernel_src);
+	sos_kfree((sos_vaddr_t)kernel_dst);
+	sos_kfree((sos_vaddr_t)kernel_fstype);
+	if (kernel_args)
+	  sos_kfree((sos_vaddr_t)kernel_args);
+      }
+      break;
+
+    case SOS_SYSCALL_ID_UMOUNT:
+      {
+	sos_uaddr_t user_str;
+	sos_size_t  len;
+	char * path;
+	struct sos_process * proc;
+	
+	proc = sos_thread_get_current()->process;
+	retval = sos_syscall_get2args(user_ctxt,
+				      (unsigned int*)& user_str,
+				      (unsigned int*)& len);
+	if (SOS_OK != retval)
+	  break;
+	
+	retval = sos_memdup_from_user((sos_vaddr_t*) &path, user_str, len, 0);
+	if (SOS_OK != retval)
+	  break;
+	
+	retval = sos_fs_umount(proc,
+			       path, len);
+	sos_kfree((sos_vaddr_t)path);
+      }
+      break;
+
+    case SOS_SYSCALL_ID_SYNC:
+      {
+	sos_fs_sync_all_fs();
+	retval = SOS_OK;
+      }
+      break;
+
+    case SOS_SYSCALL_ID_VFSTAT64:
+      {
+	sos_uaddr_t user_str;
+	sos_size_t  len;
+	sos_uaddr_t user_vfstat_struct;
+	struct sos_fs_statfs kernel_vfstat_struct;
+	char * path;
+	struct sos_process * proc;
+
+	proc = sos_thread_get_current()->process;
+	retval = sos_syscall_get3args(user_ctxt,
+				      (unsigned int*)& user_str,
+				      (unsigned int*)& len,
+				      (unsigned int*)& user_vfstat_struct);
+	if (SOS_OK != retval)
+	  break;
+
+	retval = sos_memdup_from_user((sos_vaddr_t*) &path, user_str, len, 0);
+	if (SOS_OK != retval)
+	  break;
+
+	retval = sos_fs_vfstat(proc, path, len, & kernel_vfstat_struct);
+	sos_kfree((sos_vaddr_t)path);
+	if (SOS_OK != retval)
+	  break;
+
+	if (sizeof(kernel_vfstat_struct)
+	    != sos_memcpy_to_user(user_vfstat_struct,
+				  (sos_vaddr_t) & kernel_vfstat_struct,
+				  sizeof(kernel_vfstat_struct)))
+	  retval = -SOS_EFAULT;
+      }
+      break;
+
+    case SOS_SYSCALL_ID_OPEN:
+      {
+	sos_uaddr_t user_str;
+	sos_size_t  len;
+	sos_ui32_t  open_flags;
+	sos_ui32_t  access_rights;
+	char * path;
+	struct sos_fs_opened_file * of;
+	struct sos_process * proc;
+
+	proc = sos_thread_get_current()->process;
+	retval = sos_syscall_get4args(user_ctxt,
+				      (unsigned int*)& user_str,
+				      (unsigned int*)& len,
+				      (unsigned int*)& open_flags,
+				      (unsigned int*)& access_rights);
+	if (SOS_OK != retval)
+	  break;
+
+	retval = sos_memdup_from_user((sos_vaddr_t*) &path, user_str, len, 0);
+	if (SOS_OK != retval)
+	  break;
+
+	retval = sos_fs_open(proc,
+			     path, len,
+			     open_flags,
+			     access_rights,
+			     & of);
+	sos_kfree((sos_vaddr_t)path);
+	if (SOS_OK != retval)
+	  break;
+
+	retval = sos_process_register_opened_file(proc, of);
+	if (retval < 0)
+	  {
+	    sos_fs_close(of);
+	    break;
+	  }
+      }
+      break;
+
+    case SOS_SYSCALL_ID_CLOSE:
+      {
+	struct sos_fs_opened_file * of;
+	struct sos_process * proc;
+	int fd;
+
+	proc = sos_thread_get_current()->process;
+	retval = sos_syscall_get1arg(user_ctxt,
+				     (unsigned int*)& fd);
+	if (SOS_OK != retval)
+	  break;
+
+	of = sos_process_get_opened_file(proc, fd);
+	if (NULL == of)
+	  {
+	    retval = -SOS_EBADF;
+	    break;
+	  }
+
+	retval = sos_process_unregister_opened_file(proc, fd);
+	if (SOS_OK != retval)
+	  break;
+
+	retval = sos_fs_close(of);
+      }
+      break;
+
+    case SOS_SYSCALL_ID_READ:
+      {
+	struct sos_fs_opened_file * of;
+	struct sos_process * proc;
+	sos_uaddr_t uaddr_buf;
+	sos_uaddr_t uaddr_buflen;
+	sos_size_t kernel_buflen;
+	int fd;
+
+	proc = sos_thread_get_current()->process;
+	retval = sos_syscall_get3args(user_ctxt,
+				      (unsigned int*)& fd,
+				      (unsigned int*)& uaddr_buf,
+				      (unsigned int*)& uaddr_buflen);
+	if (SOS_OK != retval)
+	  break;
+
+	/* Retrieve the value for "buflen" */
+	retval = sos_memcpy_from_user((sos_vaddr_t)& kernel_buflen,
+				      uaddr_buflen,
+				      sizeof(kernel_buflen));
+	if (sizeof(kernel_buflen) != retval)
+	  {
+	    retval = -SOS_EFAULT;
+	    break;
+	  }
+
+	of = sos_process_get_opened_file(proc, fd);
+	if (NULL == of)
+	  {
+	    retval = -SOS_EBADF;
+	    break;
+	  }
+
+	/* Do the actual reading */
+	retval = sos_fs_read(of, uaddr_buf, & kernel_buflen);
+	
+	/* Send successful number of bytes read to user */
+	sos_memcpy_to_user(uaddr_buflen,
+			   (sos_vaddr_t)& kernel_buflen,
+			   sizeof(kernel_buflen));
+      }
+      break;
+
+    case SOS_SYSCALL_ID_READDIR:
+      {
+	struct sos_fs_opened_file * of;
+	struct sos_process * proc;
+	sos_uaddr_t uaddr_direntry;
+	struct sos_fs_dirent direntry;
+	int fd;
+
+	proc = sos_thread_get_current()->process;
+	retval = sos_syscall_get2args(user_ctxt,
+				      (unsigned int*)& fd,
+				      (unsigned int*)& uaddr_direntry);
+	if (SOS_OK != retval)
+	  break;
+
+	of = sos_process_get_opened_file(proc, fd);
+	if (NULL == of)
+	  {
+	    retval = -SOS_EBADF;
+	    break;
+	  }
+
+	/* Do the actual readdir */
+	retval = sos_fs_readdir(of, & direntry);
+	if (SOS_OK != retval)
+	  break;
+	
+	/* Send direntry structure to user */
+	if (sizeof(direntry) != sos_memcpy_to_user(uaddr_direntry,
+						   (sos_vaddr_t)& direntry,
+						   sizeof(direntry)))
+	  retval = -SOS_EFAULT;
+      }
+      break;
+
+    case SOS_SYSCALL_ID_WRITE:
+      {
+	struct sos_fs_opened_file * of;
+	struct sos_process * proc;
+	sos_uaddr_t uaddr_buf;
+	sos_uaddr_t uaddr_buflen;
+	sos_size_t kernel_buflen;
+	int fd;
+
+	proc = sos_thread_get_current()->process;
+	retval = sos_syscall_get3args(user_ctxt,
+				      (unsigned int*)& fd,
+				      (unsigned int*)& uaddr_buf,
+				      (unsigned int*)& uaddr_buflen);
+	if (SOS_OK != retval)
+	  break;
+
+	/* Retrieve the value for "buflen" */
+	retval = sos_memcpy_from_user((sos_vaddr_t)& kernel_buflen,
+				      uaddr_buflen,
+				      sizeof(kernel_buflen));
+	if (sizeof(kernel_buflen) != retval)
+	  {
+	    retval = -SOS_EFAULT;
+	    break;
+	  }
+
+	of = sos_process_get_opened_file(proc, fd);
+	if (NULL == of)
+	  {
+	    retval = -SOS_EBADF;
+	    break;
+	  }
+
+	/* Do the actual writing */
+	retval = sos_fs_write(of, uaddr_buf, & kernel_buflen);
+	
+	/* Send successful number of bytes written to user */
+	sos_memcpy_to_user(uaddr_buflen,
+			   (sos_vaddr_t)& kernel_buflen,
+			   sizeof(kernel_buflen));
+      }
+      break;
+
+    case SOS_SYSCALL_ID_SEEK64:
+      {
+	struct sos_fs_opened_file * of;
+	struct sos_process * proc;
+	sos_uaddr_t uaddr_offset;
+	sos_seek_whence_t whence;
+	sos_lsoffset_t kernel_offset, result_position;
+	int fd;
+
+	proc = sos_thread_get_current()->process;
+	retval = sos_syscall_get3args(user_ctxt,
+				      (unsigned int*)& fd,
+				      (unsigned int*)& uaddr_offset,
+				      (unsigned int*)& whence);
+	if (SOS_OK != retval)
+	  break;
+
+	/* Retrieve the value for "buflen" */
+	retval = sos_memcpy_from_user((sos_vaddr_t)& kernel_offset,
+				      uaddr_offset,
+				      sizeof(kernel_offset));
+	if (sizeof(kernel_offset) != retval)
+	  {
+	    retval = -SOS_EFAULT;
+	    break;
+	  }
+
+	of = sos_process_get_opened_file(proc, fd);
+	if (NULL == of)
+	  {
+	    retval = -SOS_EBADF;
+	    break;
+	  }
+
+	/* Do the actual seek */
+	retval = sos_fs_seek(of, kernel_offset, whence, & result_position);
+	
+	/* Send successful number of bytes written to user */
+	sos_memcpy_to_user(uaddr_offset,
+			   (sos_vaddr_t)& result_position,
+			   sizeof(kernel_offset));
+      }
+      break;
+
+    case SOS_SYSCALL_ID_FTRUNCATE64:
+      {
+	struct sos_fs_opened_file * of;
+	struct sos_process * proc;
+	sos_lsoffset_t length;
+	int fd;
+
+	proc = sos_thread_get_current()->process;
+	retval = sos_syscall_get2args(user_ctxt,
+				      (unsigned int*)& fd,
+				      (unsigned int*)& length);
+	if (SOS_OK != retval)
+	  break;
+
+	of = sos_process_get_opened_file(proc, fd);
+	if (NULL == of)
+	  {
+	    retval = -SOS_EBADF;
+	    break;
+	  }
+
+	/* Do the actual trunc */
+	retval = sos_fs_ftruncate(of, length);
+      }
+      break;
+
+    case SOS_SYSCALL_ID_FSMMAP:
+      {
+	sos_uaddr_t ptr_hint_uaddr;
+	sos_uaddr_t hint_uaddr;
+	sos_size_t  length;
+	sos_ui32_t  prot;
+	sos_ui32_t  flags;
+	int         fd;
+	sos_ui32_t  offs64_hi;
+	sos_ui32_t  offs64_lo;
+	sos_luoffset_t offset_in_resource;
+	struct sos_fs_opened_file * of;
+	struct sos_process * proc;
+	
+	proc = sos_thread_get_current()->process;
+	retval = sos_syscall_get7args(user_ctxt,
+				      (unsigned int*)& ptr_hint_uaddr,
+				      (unsigned int*)& length,
+				      (unsigned int*)& prot,
+				      (unsigned int*)& flags,
+				      (unsigned int*)& fd,
+				      (unsigned int*)& offs64_hi,
+				      (unsigned int*)& offs64_lo);
+	if (SOS_OK != retval)
+	  break;
+
+	of = sos_process_get_opened_file(proc, fd);
+	if (NULL == of)
+	  {
+	    retval = -SOS_EBADF;
+	    break;
+	  }
+
+	/* Compute 64 bits offset value */
+	offset_in_resource   = offs64_hi;
+	offset_in_resource <<= 32;
+	offset_in_resource  |= offs64_lo;
+
+	retval = sos_memcpy_from_user((sos_vaddr_t)& hint_uaddr,
+				      ptr_hint_uaddr,
+				      sizeof(hint_uaddr));
+	if (sizeof(hint_uaddr) != retval)
+	  {
+	    retval = -SOS_EFAULT;
+	    break;
+	  }
+
+	retval = sos_fs_mmap(of, & hint_uaddr, length, prot, flags,
+			     offset_in_resource);
+	if (SOS_OK == retval)
+	  {
+	    if (sizeof(hint_uaddr)
+		!= sos_memcpy_to_user(ptr_hint_uaddr,
+				      (sos_vaddr_t)& hint_uaddr,
+				      sizeof(hint_uaddr)))
+	      {
+		sos_umem_vmm_unmap(sos_process_get_address_space(proc),
+				   hint_uaddr, length);
+		retval = -SOS_EFAULT;
+	      }
+	  }
+
+      }
+      break;
+
+    case SOS_SYSCALL_ID_FSYNC:
+      {
+	struct sos_fs_opened_file * of;
+	struct sos_process * proc;
+	int fd;
+
+	proc = sos_thread_get_current()->process;
+	retval = sos_syscall_get1arg(user_ctxt,
+				     (unsigned int*)& fd);
+	if (SOS_OK != retval)
+	  break;
+
+	of = sos_process_get_opened_file(proc, fd);
+	if (NULL == of)
+	  {
+	    retval = -SOS_EBADF;
+	    break;
+	  }
+
+	/* Do the actual sync */
+	retval = sos_fs_fsync(of);
+      }
+      break;
+
+    case SOS_SYSCALL_ID_FCNTL:
+      {
+	struct sos_fs_opened_file * of;
+	struct sos_process * proc;
+	sos_ui32_t cmd, arg;
+	int fd;
+
+	proc = sos_thread_get_current()->process;
+	retval = sos_syscall_get3args(user_ctxt,
+				      (unsigned int*)& fd,
+				      (unsigned int*)& cmd,
+				      (unsigned int*)& arg);
+	if (SOS_OK != retval)
+	  break;
+
+	of = sos_process_get_opened_file(proc, fd);
+	if (NULL == of)
+	  {
+	    retval = -SOS_EBADF;
+	    break;
+	  }
+
+	/* Do the actual fcntl */
+	retval = sos_fs_fcntl(of, cmd, arg);
+      }
+      break;
+
+    case SOS_SYSCALL_ID_CREAT:
+      {
+	sos_uaddr_t user_str;
+	sos_size_t  len;
+	sos_ui32_t  access_rights;
+	char * path;
+	struct sos_process * proc;
+
+	proc = sos_thread_get_current()->process;
+	retval = sos_syscall_get3args(user_ctxt,
+				      (unsigned int*)& user_str,
+				      (unsigned int*)& len,
+				      (unsigned int*)& access_rights);
+	if (SOS_OK != retval)
+	  break;
+
+	retval = sos_memdup_from_user((sos_vaddr_t*) &path, user_str, len, 0);
+	if (SOS_OK != retval)
+	  break;
+
+	retval = sos_fs_creat(proc,
+			      path, len,
+			      access_rights);
+	sos_kfree((sos_vaddr_t)path);
+      }
+      break;
+
+    case SOS_SYSCALL_ID_LINK:
+    case SOS_SYSCALL_ID_RENAME:
+      {
+	sos_uaddr_t user_oldpath, user_newpath;
+	sos_size_t  oldpathlen, newpathlen;
+	char * kernel_oldpath, * kernel_newpath;
+	struct sos_process * proc;
+
+	proc = sos_thread_get_current()->process;
+	retval = sos_syscall_get4args(user_ctxt,
+				      (unsigned int*)& user_oldpath,
+				      (unsigned int*)& oldpathlen,
+				      (unsigned int*)& user_newpath,
+				      (unsigned int*)& newpathlen);
+	if (SOS_OK != retval)
+	  break;
+
+	retval = sos_memdup_from_user((sos_vaddr_t*) &kernel_oldpath,
+				      user_oldpath,
+				      oldpathlen, 0);
+	if (SOS_OK != retval)
+	  break;
+
+	retval = sos_memdup_from_user((sos_vaddr_t*) &kernel_newpath,
+				      user_newpath,
+				      newpathlen, 0);
+	if (SOS_OK != retval)
+	  {
+	    sos_kfree((sos_vaddr_t) kernel_oldpath);
+	    break;
+	  }
+
+	if (syscall_id == SOS_SYSCALL_ID_LINK)
+	  retval = sos_fs_link(proc,
+			       kernel_oldpath, oldpathlen,
+			       kernel_newpath, newpathlen);
+	else
+	  retval = sos_fs_rename(proc,
+				 kernel_oldpath, oldpathlen,
+				 kernel_newpath, newpathlen);
+	sos_kfree((sos_vaddr_t)kernel_oldpath);
+	sos_kfree((sos_vaddr_t)kernel_newpath);
+      }
+      break;
+
+    case SOS_SYSCALL_ID_UNLINK:
+      {
+	sos_uaddr_t user_str;
+	sos_size_t  len;
+	char * path;
+	struct sos_process * proc;
+
+	proc = sos_thread_get_current()->process;
+	retval = sos_syscall_get2args(user_ctxt,
+				      (unsigned int*)& user_str,
+				      (unsigned int*)& len);
+	if (SOS_OK != retval)
+	  break;
+
+	retval = sos_memdup_from_user((sos_vaddr_t*) &path, user_str, len, 0);
+	if (SOS_OK != retval)
+	  break;
+
+	retval = sos_fs_unlink(proc,
+			       path, len);
+	sos_kfree((sos_vaddr_t)path);
+      }
+      break;
+
+    case SOS_SYSCALL_ID_SYMLINK:
+      {
+	sos_uaddr_t user_path, user_targetpath;
+	sos_size_t  pathlen, targetpathlen;
+	char * kernel_path;
+	struct sos_process * proc;
+
+	proc = sos_thread_get_current()->process;
+	retval = sos_syscall_get4args(user_ctxt,
+				      (unsigned int*)& user_path,
+				      (unsigned int*)& pathlen,
+				      (unsigned int*)& user_targetpath,
+				      (unsigned int*)& targetpathlen);
+	if (SOS_OK != retval)
+	  break;
+
+	retval = sos_memdup_from_user((sos_vaddr_t*) &kernel_path,
+				      user_path,
+				      pathlen, 0);
+	if (SOS_OK != retval)
+	  break;
+
+	retval = sos_fs_symlink(proc,
+				kernel_path, pathlen,
+				user_targetpath, targetpathlen);
+	sos_kfree((sos_vaddr_t)kernel_path);
+      }
+      break;
+
+    case SOS_SYSCALL_ID_MKDIR:
+      {
+	sos_uaddr_t user_str;
+	sos_size_t  len;
+	sos_ui32_t  access_rights;
+	char * path;
+	struct sos_process * proc;
+
+	proc = sos_thread_get_current()->process;
+	retval = sos_syscall_get3args(user_ctxt,
+				      (unsigned int*)& user_str,
+				      (unsigned int*)& len,
+				      (unsigned int*)& access_rights);
+	if (SOS_OK != retval)
+	  break;
+
+	retval = sos_memdup_from_user((sos_vaddr_t*) &path, user_str, len, 0);
+	if (SOS_OK != retval)
+	  break;
+
+	retval = sos_fs_mkdir(proc,
+			      path, len, access_rights);
+	sos_kfree((sos_vaddr_t)path);
+      }
+      break;
+
+    case SOS_SYSCALL_ID_RMDIR:
+      {
+	sos_uaddr_t user_str;
+	sos_size_t  len;
+	char * path;
+	struct sos_process * proc;
+
+	proc = sos_thread_get_current()->process;
+	retval = sos_syscall_get2args(user_ctxt,
+				      (unsigned int*)& user_str,
+				      (unsigned int*)& len);
+	if (SOS_OK != retval)
+	  break;
+
+	retval = sos_memdup_from_user((sos_vaddr_t*) &path, user_str, len, 0);
+	if (SOS_OK != retval)
+	  break;
+
+	retval = sos_fs_rmdir(proc, path, len);
+	sos_kfree((sos_vaddr_t)path);
+      }
+      break;
+
+    case SOS_SYSCALL_ID_CHMOD:
+      {
+	sos_uaddr_t user_str;
+	sos_size_t  len;
+	sos_ui32_t  access_rights;
+	char * path;
+	struct sos_process * proc;
+
+	proc = sos_thread_get_current()->process;
+	retval = sos_syscall_get3args(user_ctxt,
+				      (unsigned int*)& user_str,
+				      (unsigned int*)& len,
+				      (unsigned int*)& access_rights);
+	if (SOS_OK != retval)
+	  break;
+
+	retval = sos_memdup_from_user((sos_vaddr_t*) &path, user_str, len, 0);
+	if (SOS_OK != retval)
+	  break;
+
+	retval = sos_fs_chmod(proc, path, len, access_rights);
+	sos_kfree((sos_vaddr_t)path);
+      }
+      break;
+
+    case SOS_SYSCALL_ID_STAT64:
+      {
+	sos_uaddr_t user_str;
+	sos_size_t  len;
+	sos_uaddr_t user_stat_struct;
+	struct sos_fs_stat kernel_stat_struct;
+	int nofollow;
+	char * path;
+	struct sos_process * proc;
+
+	proc = sos_thread_get_current()->process;
+	retval = sos_syscall_get4args(user_ctxt,
+				      (unsigned int*)& user_str,
+				      (unsigned int*)& len,
+				      (unsigned int*)& nofollow,
+				      (unsigned int*)& user_stat_struct);
+	if (SOS_OK != retval)
+	  break;
+
+	retval = sos_memdup_from_user((sos_vaddr_t*) &path, user_str, len, 0);
+	if (SOS_OK != retval)
+	  break;
+
+	retval = sos_fs_stat(proc, path, len, nofollow, & kernel_stat_struct);
+	sos_kfree((sos_vaddr_t)path);
+	if (SOS_OK != retval)
+	  break;
+
+	if (sizeof(kernel_stat_struct)
+	    != sos_memcpy_to_user(user_stat_struct,
+				  (sos_vaddr_t) & kernel_stat_struct,
+				  sizeof(kernel_stat_struct)))
+	  retval = -SOS_EFAULT;
+      }
+      break;
+
+    case SOS_SYSCALL_ID_CHROOT:
+    case SOS_SYSCALL_ID_CHDIR:
+      {
+	sos_uaddr_t user_str;
+	sos_size_t  len;
+	char * path;
+	struct sos_fs_opened_file * of, * old_of;
+	struct sos_process * proc;
+
+	proc = sos_thread_get_current()->process;
+	retval = sos_syscall_get2args(user_ctxt,
+				      (unsigned int*)& user_str,
+				      (unsigned int*)& len);
+	if (SOS_OK != retval)
+	  break;
+
+	retval = sos_memdup_from_user((sos_vaddr_t*) &path, user_str, len, 0);
+	if (SOS_OK != retval)
+	  break;
+
+	retval = sos_fs_open(proc,
+			     path, len,
+			     SOS_FS_OPEN_DIRECTORY,
+			     SOS_FS_OPEN_READ,
+			     & of);
+	sos_kfree((sos_vaddr_t)path);
+	if (SOS_OK != retval)
+	  break;
+
+	if (syscall_id == SOS_SYSCALL_ID_CHROOT)
+	  retval = sos_process_chroot(proc, of, & old_of);
+	else
+	  retval = sos_process_chdir(proc, of, & old_of);
+
+	if (retval < 0)
+	  {
+	    sos_fs_close(of);
+	    break;
+	  }
+
+	sos_fs_close(old_of);
+      }
+      break;      
+
+    case SOS_SYSCALL_ID_FCHDIR:
+      {
+	struct sos_fs_opened_file * of, * new_of, * old_of;
+	struct sos_process * proc;
+	int fd;
+
+	proc = sos_thread_get_current()->process;
+	retval = sos_syscall_get1arg(user_ctxt,
+				     (unsigned int*)& fd);
+	if (SOS_OK != retval)
+	  break;
+
+	of = sos_process_get_opened_file(proc, fd);
+	if (NULL == of)
+	  {
+	    retval = -SOS_EBADF;
+	    break;
+	  }
+
+	/* Duplicate this FD */
+	retval = sos_fs_duplicate_opened_file(of, proc, & new_of);
+	if (SOS_OK != retval)
+	  break;
+
+	/* Do the actual chdir */
+	retval = sos_process_chdir(proc, new_of, & old_of);
+	if (retval < 0)
+	  {
+	    sos_fs_close(new_of);
+	    break;
+	  }
+
+	sos_fs_close(old_of);
+      }
+      break;
+
     case SOS_SYSCALL_ID_BOCHS_WRITE:
       {
 	sos_uaddr_t user_str;
@@ -465,22 +1302,15 @@
 	if (SOS_OK != retval)
 	  break;
 
-	str = (char*)sos_kmalloc(len + 1, 0);
-	if (str)
+	retval = sos_strndup_from_user(& str, user_str, len + 1, 0);
+	if (SOS_OK == retval)
 	  {
-	    retval = sos_strzcpy_from_user(str, user_str, len+1);
-	    str[len] = '\0';
-	    if (SOS_OK == retval)
-	      {
-		sos_bochs_printf("THR 0x%x: ",
-				 (unsigned)sos_thread_get_current());
-		retval = sos_bochs_putstring(str);
-		retval = len;
-	      }
+	    sos_bochs_printf("THR 0x%x: ",
+			     (unsigned)sos_thread_get_current());
+	    retval = sos_bochs_putstring(str);
+	    retval = len;
 	    sos_kfree((sos_vaddr_t)str);
 	  }
-	else
-	  retval = -SOS_ENOMEM;
       }
       break;
 
@@ -504,7 +1334,7 @@
 	if (SOS_OK != retval)
 	  break;
 
-	str = (char*)sos_kmalloc(len + 1, 0);
+	str = (char*)sos_kmalloc(len, 0);
 	if (str)
 	  {
 	    int i;
diff -ruN /tmp/sos-code-article7.5/sos/syscall.h ../sos-code-article8/sos/syscall.h
--- /tmp/sos-code-article7.5/sos/syscall.h	2005-04-27 20:17:18.000000000 +0200
+++ ../sos-code-article8/sos/syscall.h	2005-07-01 16:39:49.000000000 +0200
@@ -44,7 +44,7 @@
  */
 #define SOS_SYSCALL_ID_FORK        257 /**< Args: NONE, retval={father:child_pd, child:0} */
 #define SOS_SYSCALL_ID_EXEC        258 /**< Args: ptr_exec_path strlen_exec_path, retval=errno */
-#define SOS_SYSCALL_ID_MMAP        259 /**< Args: &hint len prot flags fd uoffs64_hi uoffs64_lo, retval=errno */
+#define SOS_SYSCALL_ID_FAKEMMAP    259 /**< Args: &hint len prot flags fd uoffs64_hi uoffs64_lo, retval=errno */
 #define SOS_SYSCALL_ID_MUNMAP      260 /**< Args: start_uaddr size, retval=errno */
 #define SOS_SYSCALL_ID_MPROTECT    261 /**< Args: start_uaddr size access_rights, retval=errno */
 #define SOS_SYSCALL_ID_MRESIZE     262 /**< Args: old_uaddr old_size ptr_new_uaddr new_size flags, retval=errno */
@@ -55,6 +55,42 @@
  */
 #define SOS_SYSCALL_ID_BRK         263 /**< Args: 0/new_top_heap, retval=top_heap */
 
+/**
+ * File system interface
+ */
+#define SOS_SYSCALL_ID_MOUNT       555 /**< Args: uaddr_src srclen uaddr_dst dstlen uaddr_fstype flags uaddr_args, retval=errno */
+#define SOS_SYSCALL_ID_UMOUNT      556 /**< Args: uaddr_path pathlen, retval=errno */
+#define SOS_SYSCALL_ID_SYNC        557 /**< Args: none, retval=errno */
+#define SOS_SYSCALL_ID_VFSTAT64    558 /**< Args: uaddr_path pathlen uaddr_vfstat_struct, retval=errno */
+
+#define SOS_SYSCALL_ID_OPEN        559 /**< Args: path pathlen flags access_rights, retval=fd */
+#define SOS_SYSCALL_ID_CLOSE       560 /**< Args: fd, retval=errno */
+#define SOS_SYSCALL_ID_READ        561 /**< Args: fd uaddr_buf uaddr_buflen, retval=errno */
+#define SOS_SYSCALL_ID_READDIR     562 /**< Args: fd uaddr_dirent, retval=errno */
+#define SOS_SYSCALL_ID_WRITE       563 /**< Args: fd uaddr_buf uaddr_buflen, retval=errno */
+#define SOS_SYSCALL_ID_SEEK64      564 /**< Args: fd uaddr_offset whence, retval=errno */
+#define SOS_SYSCALL_ID_FTRUNCATE64 565 /**< Args: fd length, retval=errno */
+#define SOS_SYSCALL_ID_FSMMAP      566 /**< Args: &hint len prot flags fd uoffs64_hi uoffs64_lo, retval=errno */
+#define SOS_SYSCALL_ID_FSYNC       567 /**< Args: fd, retval=errno */
+#define SOS_SYSCALL_ID_FCNTL       568 /**< Args: fd cmd arg, retval=errno */
+
+#define SOS_SYSCALL_ID_CREAT       570 /**< Args: uaddr_path pathlen access_rights, retval=errno */
+#define SOS_SYSCALL_ID_LINK        571 /**< Args: uaddr_oldpath oldpathlen uaddr_newpath newpathlen, retval=errno */
+#define SOS_SYSCALL_ID_RENAME      572 /**< Args: uaddr_oldpath oldpathlen uaddr_newpath newpathlen, retval=errno */
+#define SOS_SYSCALL_ID_UNLINK      573 /**< Args: uaddr_path pathlen, retval=errno */
+#define SOS_SYSCALL_ID_SYMLINK     574 /**< Args: uaddr_path pathlen uaddr_target targetlen, retval=errno */
+
+#define SOS_SYSCALL_ID_MKDIR       576 /**< Args: uaddr_path pathlen access_rights, retval=errno */
+#define SOS_SYSCALL_ID_RMDIR       577 /**< Args: uaddr_path pathlen, retval=errno */
+
+#define SOS_SYSCALL_ID_CHMOD       578 /**< Args: uaddr_path pathlen access_rights, retval=errno */
+#define SOS_SYSCALL_ID_STAT64      579 /**< Args: uaddr_path pathlen nofollow uaddr_stat_struct, retval=errno */
+
+#define SOS_SYSCALL_ID_CHROOT      580 /**< Args: uaddr_path pathlen, retval=errno */
+#define SOS_SYSCALL_ID_CHDIR       581 /**< Args: uaddr_path pathlen, retval=errno */
+#define SOS_SYSCALL_ID_FCHDIR      582 /**< Args: fd, retval=errno */
+
+
 #define SOS_SYSCALL_ID_BOCHS_WRITE 43  /**< Args: string, retval=num_printed */
 
 
diff -ruN /tmp/sos-code-article7.5/sos/thread.h ../sos-code-article8/sos/thread.h
--- /tmp/sos-code-article7.5/sos/thread.h	2005-04-27 20:17:17.000000000 +0200
+++ ../sos-code-article8/sos/thread.h	2005-07-01 16:39:49.000000000 +0200
@@ -136,13 +136,10 @@
       struct sos_sched_queue *rdy_queue;
       struct sos_thread     *rdy_prev, *rdy_next;
     } ready;
-
-    struct
-    {
-      struct sos_time user_time_spent_in_slice;
-    } running;
   }; /* Anonymous union (gcc extenion) */
 
+  struct sos_time user_time_spent_in_slice;
+
 
   /**
    * When a thread in kernel mode is accessing the user space, it may
diff -ruN /tmp/sos-code-article7.5/sos/types.h ../sos-code-article8/sos/types.h
--- /tmp/sos-code-article7.5/sos/types.h	2005-04-27 20:17:17.000000000 +0200
+++ ../sos-code-article8/sos/types.h	2005-07-01 16:39:49.000000000 +0200
@@ -46,6 +46,8 @@
 
 /** Generic count of objects */
 typedef unsigned int           sos_count_t;
+/** Generic count of objects (LARGE) */
+typedef unsigned long long int sos_lcount_t;
 
 /* Low-level sizes */
 typedef unsigned long long int sos_ui64_t; /**< 32b unsigned */
diff -ruN /tmp/sos-code-article7.5/sos/uaccess.c ../sos-code-article8/sos/uaccess.c
--- /tmp/sos-code-article7.5/sos/uaccess.c	2005-04-27 20:17:17.000000000 +0200
+++ ../sos-code-article8/sos/uaccess.c	2005-07-01 16:39:49.000000000 +0200
@@ -47,6 +47,9 @@
 
   KEEP_LABEL(catch_pgflt);
 
+  if (size <= 0)
+    return 0;
+
   retval = sos_thread_prepare_user_space_access(NULL,
 						(sos_vaddr_t) && catch_pgflt);
   if (SOS_OK != retval)
@@ -110,6 +113,33 @@
 }
 
 
+sos_ret_t sos_memdup_from_user(sos_vaddr_t * kernel_to, sos_uaddr_t from_user,
+			       sos_size_t length,
+			       sos_ui32_t kmalloc_flags)
+{
+  sos_ret_t retval;
+
+  if (length <= 0)
+    return 0;
+
+  *kernel_to = sos_kmalloc(length, kmalloc_flags);
+  if (NULL == (void*) *kernel_to)
+    return -SOS_ENOMEM;
+
+  retval = sos_memcpy_from_user(*kernel_to, from_user, length);
+  if (length != retval)
+    {
+      sos_kfree((sos_vaddr_t)*kernel_to);
+      *kernel_to = (sos_vaddr_t) NULL;
+      retval = -SOS_EFAULT;
+    }
+  else
+    retval = SOS_OK;
+
+  return retval;
+}
+
+
 sos_ret_t sos_memcpy_to_user(sos_uaddr_t user_to,
 			     sos_vaddr_t kernel_from,
 			     sos_size_t size)
@@ -133,6 +163,9 @@
 
   KEEP_LABEL(catch_pgflt);
 
+  if (max_len <= 0)
+    return 0;
+
   /* Make sure user is trying to access user space */
   if (user_str < SOS_PAGING_BASE_USER_ADDRESS)
     return -SOS_EPERM;
@@ -179,6 +212,9 @@
 
   KEEP_LABEL(catch_pgflt);
 
+  if (len <= 0)
+    return 0;
+
   retval = sos_thread_prepare_user_space_access(NULL,
 						(sos_vaddr_t) && catch_pgflt);
   if (SOS_OK != retval)
diff -ruN /tmp/sos-code-article7.5/sos/uaccess.h ../sos-code-article8/sos/uaccess.h
--- /tmp/sos-code-article7.5/sos/uaccess.h	2005-04-27 20:17:17.000000000 +0200
+++ ../sos-code-article8/sos/uaccess.h	2005-07-01 16:39:49.000000000 +0200
@@ -42,6 +42,19 @@
 
 
 /**
+ * Retrieve a bunch of data from the user space of the
+ * current_thread->process and copy it in a newly allocated kernel
+ * area
+ *
+ * @return NULL on error (including unresolved page fault during
+ * transfer)
+ */
+sos_ret_t sos_memdup_from_user(sos_vaddr_t * kernel_to, sos_uaddr_t from_user,
+			       sos_size_t length,
+			       sos_ui32_t kmalloc_flags);
+
+
+/**
  * Transfer a bunch of data to the user space of the
  * current_thread->process
  *
diff -ruN /tmp/sos-code-article7.5/support/build_image.sh ../sos-code-article8/support/build_image.sh
--- /tmp/sos-code-article7.5/support/build_image.sh	2005-04-27 20:17:19.000000000 +0200
+++ ../sos-code-article8/support/build_image.sh	2005-07-01 16:39:50.000000000 +0200
@@ -104,9 +104,14 @@
     fi
   done
 
+  # Try to guess with whereis (Credits to Karim Dridi)
+  if [ ! -d "$GRUBDIR" ] ; then
+    GRUBDIR=`whereis grub | awk '{ print "find "$3" -name stage2" }' | sh | xargs dirname 2>/dev/null`
+  fi
+
   # Try to guess with locate
   if [ ! -d "$GRUBDIR" ] ; then
-    GRUBDIR=`locate stage2 | head -1 | xargs dirname 2>/dev/null`
+     GRUBDIR=`locate stage2 | head -1 | xargs dirname 2>/dev/null`
   fi
 
   # Look for a correct sbin/grub
@@ -146,7 +151,7 @@
   mmd $1/system
   mmd $1/modules
 
-  $SBIN_GRUB --batch <<EOT 1>/dev/null 2>/dev/null || exit 1
+  $SBIN_GRUB --batch --no-floppy <<EOT 1>/dev/null 2>/dev/null || exit 1
 device (fd0) $IMG_FNAME
 install (fd0)/boot/grub/stage1 (fd0) (fd0)/boot/grub/stage2 p (fd0)/boot/grub/menu.txt
 quit
diff -ruN /tmp/sos-code-article7.5/userland/Makefile ../sos-code-article8/userland/Makefile
--- /tmp/sos-code-article7.5/userland/Makefile	2005-04-27 20:17:19.000000000 +0200
+++ ../sos-code-article8/userland/Makefile	2005-07-01 16:39:50.000000000 +0200
@@ -17,9 +17,11 @@
 
 CC=gcc
 AR=ar
+CP=cp
+STRIP=strip
 OBJCOPY=objcopy
-CFLAGS  = -Wall -nostdinc -ffreestanding -I. -I..
-LIBGCC := $(shell $(CC) -print-libgcc-file-name) # To benefit from FP/64bits artihm.
+CFLAGS  = -Wall -nostdinc -ffreestanding -I. -I.. -O
+LIBGCC  = $(shell $(CC) -print-libgcc-file-name) # To benefit from FP/64bits artihm.
 LDFLAGS = -Wl,--warn-common -nostdlib -Wl,-Tldscript.lds
 
 # Main target
@@ -29,9 +31,10 @@
 
 PROGS := init myprog1 myprog2 myprog3 myprog4 myprog5 myprog6 \
          myprog7 myprog8 myprog9 myprog10 myprog11 myprog12   \
-	 myprog13 myprog14 banner
+	 myprog13 myprog14 banner fstest
 
 # Build dependencies of the programs
+fstest: fstest_utils.o
 $(PROGS) : % : %.o crt.o libc.a
 
 PWD := $(shell pwd)
@@ -58,7 +61,8 @@
 	   echo "  = { \"$$f\", &_begin_userprog$$i, &_end_userprog$$i };"  \
                 >> .userprog$$i.c ;                                         \
 	   $(CC) $(CFLAGS) -c .userprog$$i.c -o .userprog$$i.o ;            \
-           $(OBJCOPY) --add-section .userprog$$i=$$f .userprog$$i.o         \
+	   $(CP) $$f $$f.strip && $(STRIP) -sx $$f.strip ;                  \
+           $(OBJCOPY) --add-section .userprog$$i=$$f.strip .userprog$$i.o   \
                 .userprog$$i.kimg ;                                         \
            echo "  . = ALIGN(4096);" >> .userprogs.lds ;                    \
            echo "  _begin_userprog$$i = .;" >> .userprogs.lds ;             \
@@ -87,5 +91,5 @@
 
 # Clean directory
 clean:
-	$(RM) *.o *.a *~ $(PROGS) *.kimg
+	$(RM) *.o *.a *~ $(PROGS) *.kimg *.strip
 	$(RM) .userprog*
diff -ruN /tmp/sos-code-article7.5/userland/banner.c ../sos-code-article8/userland/banner.c
--- /tmp/sos-code-article7.5/userland/banner.c	2005-04-27 20:17:20.000000000 +0200
+++ ../sos-code-article8/userland/banner.c	2005-07-01 16:39:50.000000000 +0200
@@ -121,10 +121,10 @@
 int main()
 {
   /* Map the x86 text-mode framebuffer in user space */
-  video = mmap(0, 4096,
-	       PROT_READ | PROT_WRITE,
-	       MAP_SHARED,
-	       "/dev/mem", 0xb8000);
+  video = fakemmap(0, 4096,
+		   PROT_READ | PROT_WRITE,
+		   MAP_SHARED,
+		   "/dev/mem", 0xb8000);
 
   p1.top_line  = 3;
   p1.str       = str1;
@@ -142,7 +142,5 @@
   p2.direction = -1;
   _sos_new_thread((sos_thread_func_t*)banner_thread, (void*) & p2, 8192);
 
-  _sos_nanosleep(3, 0);
-
   return 0;
 }
diff -ruN /tmp/sos-code-article7.5/userland/crt.c ../sos-code-article8/userland/crt.c
--- /tmp/sos-code-article7.5/userland/crt.c	2005-04-27 20:17:19.000000000 +0200
+++ ../sos-code-article8/userland/crt.c	2005-07-01 16:39:50.000000000 +0200
@@ -196,10 +196,10 @@
 }
 
 
-int _sos_mmap(void ** ptr_hint_addr, size_t len, int prot, int flags,
-	      const char *resource_path, loff_t offset)
+int _sos_fakemmap(void ** ptr_hint_addr, size_t len, int prot, int flags,
+		  const char *resource_path, loff_t offset)
 {
-  return _sos_syscall7(SOS_SYSCALL_ID_MMAP,
+  return _sos_syscall7(SOS_SYSCALL_ID_FAKEMMAP,
 		       (unsigned int)ptr_hint_addr, len, prot, flags,
 		       (unsigned int)resource_path,
 		       /* offs64_hi */(offset >> 32),
@@ -282,3 +282,265 @@
   return (void*)_sos_syscall1(SOS_SYSCALL_ID_BRK,
 			      (unsigned)new_top_address);
 }
+
+
+int _sos_mount(const char *source, const char *target,
+	       const char *filesystemtype, unsigned long mountflags,
+	       const char *args)
+{
+  if (!target || !filesystemtype)
+    return -1;
+
+  return _sos_syscall7(SOS_SYSCALL_ID_MOUNT,
+		       (unsigned int)source, source?strlen(source):0,
+		       (unsigned int)target, strlen(target),
+		       (unsigned int)filesystemtype,
+		       mountflags,
+		       (unsigned int)args);
+}
+
+
+int _sos_umount(const char *target)
+{
+  if (!target)
+    return -1;
+
+  return _sos_syscall2(SOS_SYSCALL_ID_UMOUNT,
+		       (unsigned int)target,
+		       strlen(target));
+}
+
+
+void _sos_sync(void)
+{
+  _sos_syscall0(SOS_SYSCALL_ID_SYNC);
+}
+
+
+int _sos_statvfs(const char *path, struct statvfs *buf)
+{
+  if (! path)
+    return -1;
+    
+  return _sos_syscall3(SOS_SYSCALL_ID_VFSTAT64,
+		       (unsigned)path,
+		       strlen(path),
+		       (unsigned)buf);
+}
+
+
+int _sos_open(const char * pathname, int flags, int mode)
+{
+  if (! pathname)
+    return -1;
+    
+  return _sos_syscall4(SOS_SYSCALL_ID_OPEN,
+		       (unsigned)pathname,
+		       strlen(pathname),
+		       flags,
+		       mode);
+}
+
+
+int _sos_close(int fd)
+{
+  return _sos_syscall1(SOS_SYSCALL_ID_CLOSE, fd);
+}
+
+
+int _sos_read(int fd, char * buf, size_t * len)
+{
+  return _sos_syscall3(SOS_SYSCALL_ID_READ, fd,
+		       (unsigned int) buf,
+		       (unsigned int) len);
+}
+
+
+int _sos_write(int fd, const char * buf, size_t * len)
+{
+  return _sos_syscall3(SOS_SYSCALL_ID_WRITE, fd,
+		       (unsigned int) buf,
+		       (unsigned int) len);
+}
+
+
+int _sos_seek64(int fd, loff_t * offset, int whence)
+{
+  return _sos_syscall3(SOS_SYSCALL_ID_SEEK64, fd,
+		       (unsigned int)offset,
+		       (unsigned int)whence);
+}
+
+
+int _sos_fmmap(void ** ptr_hint_addr, size_t len, int prot, int flags,
+	       int fd, loff_t offset)
+{
+  return _sos_syscall7(SOS_SYSCALL_ID_FSMMAP,
+		       (unsigned int)ptr_hint_addr, len, prot, flags,
+		       (unsigned int)fd,
+		       /* offs64_hi */(offset >> 32),
+		       /* offs64_lo */(offset & 0xffffffff));
+}
+
+
+int _sos_ftruncate64(int fd, loff_t length)
+{
+  return _sos_syscall2(SOS_SYSCALL_ID_FTRUNCATE64, fd,
+		       (unsigned int)length);  
+}
+
+
+int _sos_fcntl(int fd, int cmd, int arg)
+{
+  return _sos_syscall3(SOS_SYSCALL_ID_FCNTL, fd,
+		       (unsigned int)cmd,
+		       (unsigned int)arg);
+}
+
+
+int _sos_creat(const char *pathname, int mode)
+{
+  if (! pathname)
+    return -1;
+
+  return _sos_syscall3(SOS_SYSCALL_ID_CREAT,
+		       (unsigned int)pathname,
+		       strlen(pathname),
+		       mode);
+}
+
+
+int _sos_link (const char *oldpath, const char *newpath)
+{
+  if (!oldpath || !newpath)
+    return -1;
+
+  return _sos_syscall4(SOS_SYSCALL_ID_LINK,
+		       (unsigned int)oldpath,
+		       strlen(oldpath),
+		       (unsigned int)newpath,
+		       strlen(newpath));
+}
+
+
+int _sos_unlink(const char *pathname)
+{
+  if (! pathname)
+    return -1;
+
+  return _sos_syscall2(SOS_SYSCALL_ID_UNLINK,
+		       (unsigned int)pathname,
+		       strlen(pathname));
+}
+
+
+int _sos_rename (const char *oldpath, const char *newpath)
+{
+  if (!oldpath || !newpath)
+    return -1;
+
+  return _sos_syscall4(SOS_SYSCALL_ID_RENAME,
+		       (unsigned int)oldpath,
+		       strlen(oldpath),
+		       (unsigned int)newpath,
+		       strlen(newpath));
+}
+
+
+int _sos_symlink(const char *target, const char *path)
+{
+  if (!path || !target)
+    return -1;
+
+  return _sos_syscall4(SOS_SYSCALL_ID_SYMLINK,
+		       (unsigned int)path,
+		       strlen(path),
+		       (unsigned int)target,
+		       strlen(target));
+}
+
+
+struct dirent; /* Forward declaration */
+int _sos_readdir(int fd, struct dirent * dirent)
+{
+  return _sos_syscall2(SOS_SYSCALL_ID_READDIR,
+		       fd,
+		       (unsigned int)dirent);
+}
+
+
+int _sos_mkdir(const char *pathname, mode_t mode)
+{
+  if (!pathname)
+    return -1;
+
+  return _sos_syscall3(SOS_SYSCALL_ID_MKDIR,
+		       (unsigned int)pathname,
+		       strlen(pathname),
+		       mode);
+}
+
+
+int _sos_rmdir(const char *pathname)
+{
+  if (!pathname)
+    return -1;
+
+  return _sos_syscall2(SOS_SYSCALL_ID_RMDIR,
+		       (unsigned int)pathname,
+		       strlen(pathname));
+}
+
+
+int _sos_chmod(const char *pathname, mode_t mode)
+{
+  if (!pathname)
+    return -1;
+
+  return _sos_syscall3(SOS_SYSCALL_ID_CHMOD,
+		       (unsigned int)pathname,
+		       strlen(pathname),
+		       mode);
+}
+
+
+int _sos_stat(const char *pathname, int nofollow, struct stat * st)
+{
+  if (!pathname || !st)
+    return -1;
+
+  return _sos_syscall4(SOS_SYSCALL_ID_STAT64,
+		       (unsigned int)pathname,
+		       strlen(pathname),
+		       nofollow,
+		       (unsigned int)st);
+}
+
+
+int _sos_chroot(const char *dirname)
+{
+  if (!dirname)
+    return -1;
+
+  return _sos_syscall2(SOS_SYSCALL_ID_CHROOT,
+		       (unsigned int)dirname,
+		       strlen(dirname));
+}
+
+
+int _sos_chdir(const char *dirname)
+{
+  if (!dirname)
+    return -1;
+
+  return _sos_syscall2(SOS_SYSCALL_ID_CHDIR,
+		       (unsigned int)dirname,
+		       strlen(dirname));
+}
+
+
+int _sos_fchdir(int fd)
+{
+  return _sos_syscall1(SOS_SYSCALL_ID_FCHDIR,
+		       (unsigned int)fd);
+}
diff -ruN /tmp/sos-code-article7.5/userland/crt.h ../sos-code-article8/userland/crt.h
--- /tmp/sos-code-article7.5/userland/crt.h	2005-04-27 20:17:19.000000000 +0200
+++ ../sos-code-article8/userland/crt.h	2005-07-01 16:39:50.000000000 +0200
@@ -122,8 +122,8 @@
  * Syscall to map the given resource. Preliminary version without file
  * system support
  */
-int _sos_mmap(void ** ptr_hint_addr, size_t len, int prot, int flags,
-	      const char *resource_path, loff_t offset);
+int _sos_fakemmap(void ** ptr_hint_addr, size_t len, int prot, int flags,
+		  const char *resource_path, loff_t offset);
 
 
 /**
@@ -170,4 +170,46 @@
  */
 void * _sos_brk(void * new_top_address);
 
+
+int _sos_mount(const char *source, const char *target,
+	       const char *filesystemtype, unsigned long mountflags,
+	       const char *data);
+int _sos_umount(const char *target);
+
+void _sos_sync(void);
+
+struct statvfs; /**< Forward declaration */
+int _sos_statvfs(const char *path, struct statvfs *buf);
+
+int _sos_open(const char * pathname, int flags, int mode);
+int _sos_close(int fd);
+
+int _sos_read(int fd, char * buf, size_t * len);
+int _sos_write(int fd, const char * buf, size_t * len);
+int _sos_seek64(int fd, loff_t * offset, int whence);
+int _sos_ftruncate64(int fd, loff_t length);
+int _sos_fmmap(void ** ptr_hint_addr, size_t len, int prot, int flags,
+	       int fd, loff_t offset);
+int _sos_fcntl(int fd, int cmd, int arg);
+
+struct dirent; /* Forward declaration */
+int _sos_readdir(int fd, struct dirent * dirent);
+
+int _sos_creat(const char *pathname, int mode);
+int _sos_link (const char *oldpath, const char *newpath);
+int _sos_unlink(const char *pathname);
+int _sos_rename (const char *oldpath, const char *newpath);
+int _sos_symlink(const char *target, const char *path);
+
+int _sos_mkdir(const char *pathname, mode_t mode);
+int _sos_rmdir(const char *pathname);
+
+int _sos_chmod(const char *pathname, mode_t mode);
+
+struct stat; /**< forward declaration */
+int _sos_stat(const char *pathname, int nofollow, struct stat * st);
+
+int _sos_chroot(const char *dirname);
+int _sos_fchdir(int fd);
+
 #endif /* _SOS_USER_CRT_H_ */
diff -ruN /tmp/sos-code-article7.5/userland/fstest.c ../sos-code-article8/userland/fstest.c
--- /tmp/sos-code-article7.5/userland/fstest.c	1970-01-01 01:00:00.000000000 +0100
+++ ../sos-code-article8/userland/fstest.c	2005-07-01 16:39:50.000000000 +0200
@@ -0,0 +1,819 @@
+/* Copyright (C) 2005 David Decotigny
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License
+   as published by the Free Software Foundation; either version 2
+   of the License, or (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+   USA. 
+*/
+
+#include <crt.h>
+#include <libc.h>
+#include <stdarg.h>
+#include <string.h>
+#include <debug.h>
+
+#include "fstest_utils.h"
+
+/**
+ * @file fstest.c
+ *
+ * File-system tests
+ */
+
+int main()
+{
+  int fd, len;
+  char buff[256];
+
+  bochs_printf("Hi from fstest\n");
+
+  ls("/", 1, 1);
+  ls(".", 1, 1);
+  ls("", 1, 1);
+  ls(0, 1, 1);
+
+  TEST_EXPECT_CONDITION(fd = open("", 0),
+			RETVAL < 0);
+
+  TEST_EXPECT_CONDITION(fd = open(0, 0),
+			RETVAL < 0);
+
+  TEST_EXPECT_CONDITION(fd = open("/", O_RDWR),
+			RETVAL == 0);
+
+  TEST_EXPECT_CONDITION(fd = open("/", O_RDONLY),
+			RETVAL == 1);
+
+  TEST_EXPECT_CONDITION(fd = open("/", O_WRONLY),
+			RETVAL == 2);
+
+  TEST_EXPECT_CONDITION(close(1),
+			RETVAL == 0);
+  
+  TEST_EXPECT_CONDITION(fd = open("/", O_WRONLY),
+			RETVAL == 1);
+
+  TEST_EXPECT_CONDITION(fd = open("//", O_WRONLY),
+			RETVAL == 3);
+
+  TEST_EXPECT_CONDITION(fd = open("////////", O_WRONLY),
+			RETVAL == 4);
+
+  TEST_EXPECT_CONDITION(fd = open("/does not exist", O_WRONLY),
+			RETVAL < 0);
+
+  TEST_EXPECT_CONDITION(fd = open("////does not exist", O_WRONLY),
+			RETVAL < 0);
+
+  TEST_EXPECT_CONDITION(fd = open("/does not exist/", O_WRONLY),
+			RETVAL < 0);
+
+  TEST_EXPECT_CONDITION(fd = open("////does not exist/", O_WRONLY),
+			RETVAL < 0);
+
+  TEST_EXPECT_CONDITION(fd = open("/does not exist////", O_WRONLY),
+			RETVAL < 0);
+
+  TEST_EXPECT_CONDITION(fd = open("////does not exist/////", O_WRONLY),
+			RETVAL < 0);
+
+  TEST_EXPECT_CONDITION(fd = open("does not exist", O_WRONLY),
+			RETVAL < 0);
+
+  TEST_EXPECT_CONDITION(fd = open("does not exist/", O_WRONLY),
+			RETVAL < 0);
+
+  TEST_EXPECT_CONDITION(fd = open("does not exist////", O_WRONLY),
+			RETVAL);
+
+  TEST_EXPECT_CONDITION(fd = open("/does not exist/ab c d", O_WRONLY),
+			RETVAL < 0);
+
+  TEST_EXPECT_CONDITION(fd = open("////does not exist/ab c d", O_WRONLY),
+			RETVAL < 0);
+
+  TEST_EXPECT_CONDITION(fd = open("/does not exist////ab c d", O_WRONLY),
+			RETVAL < 0);
+
+  TEST_EXPECT_CONDITION(fd = open("////does not exist/////ab c d", O_WRONLY),
+			RETVAL < 0);
+
+  TEST_EXPECT_CONDITION(fd = open("does not exist", O_WRONLY),
+			RETVAL < 0);
+
+  TEST_EXPECT_CONDITION(fd = open("does not exist/ab c d", O_WRONLY),
+			RETVAL < 0);
+
+  TEST_EXPECT_CONDITION(fd = open("does not exist////ab c d", O_WRONLY),
+			RETVAL < 0);
+
+  TEST_EXPECT_CONDITION(fd = open("/", O_RDWR),
+			RETVAL == 5);
+
+  TEST_EXPECT_CONDITION(fd = open("/tutu.txt", O_RDWR),
+			RETVAL < 0);
+
+  ls("/", 1, 1);
+
+  TEST_EXPECT_CONDITION(fd = open("/tutu.txt", O_RDWR | O_CREAT,
+				  S_IRUSR | S_IWUSR),
+			RETVAL == 6);
+  
+  ls("/", 1, 1);
+
+  TEST_EXPECT_CONDITION(fd = open("tutu.txt", O_RDWR | O_CREAT),
+			RETVAL == 7);
+
+  /* O_EXCL with an already-existing file */
+  TEST_EXPECT_CONDITION(fd = open("tutu.txt", O_RDWR | O_CREAT | O_EXCL),
+			RETVAL < 0);
+
+  TEST_EXPECT_CONDITION(fd = open("/toto.txt", O_RDWR | O_CREAT | O_EXCL,
+				  S_IRWXALL),
+			RETVAL == 8);
+
+  /* O_EXCL with an already-existing file */
+  TEST_EXPECT_CONDITION(fd = open("toto.txt", O_RDWR | O_CREAT | O_EXCL),
+			RETVAL < 0);
+
+  TEST_EXPECT_CONDITION(fd = open("toto.txt", O_RDWR | O_CREAT,
+				  S_IRUSR | S_IWUSR),
+			RETVAL == 9);
+
+  /* Trailing slash on non-dir entries */
+  TEST_EXPECT_CONDITION(fd = open("toto.txt/", O_RDWR), RETVAL < 0);
+  TEST_EXPECT_CONDITION(fd = open("notdir/", O_RDWR | O_CREAT, S_IRWXALL),
+			RETVAL < 0);
+  TEST_EXPECT_CONDITION(fd = open("notdir/", O_RDWR), RETVAL < 0);
+
+  /* Substring match */
+  TEST_EXPECT_CONDITION(fd = open("toto1.txt", O_RDWR),
+			RETVAL < 0);
+  TEST_EXPECT_CONDITION(fd = open("toto1.tx", O_RDWR | O_CREAT, S_IWUSR),
+			RETVAL == 10);
+
+  /* Substring match */
+  TEST_EXPECT_CONDITION(fd = open("toto.tx", O_RDWR),
+			RETVAL < 0);
+  TEST_EXPECT_CONDITION(fd = open("toto.tx", O_RDWR | O_CREAT,
+				  S_IRUSR | S_IWUSR),
+			RETVAL == 11);
+
+  /*
+   * read/write/seek
+   */
+
+  TEST_EXPECT_CONDITION(len = read(fd, buff, 256),
+			RETVAL == 0);
+
+  TEST_EXPECT_CONDITION(lseek(fd, 0, SEEK_SET),
+			RETVAL == 0);
+
+  strzcpy(buff, "Bonjour !", 256);
+  TEST_EXPECT_CONDITION(len = write(fd, buff, 10),
+			RETVAL == 10);
+
+  ls("/", 1, 1);
+
+  TEST_EXPECT_CONDITION(lseek(fd, 0, SEEK_SET),
+			RETVAL == 0);
+
+  strzcpy(buff, "Garbage garbage garbage", 256);
+  TEST_EXPECT_CONDITION(len = read(fd, buff, 256),
+			RETVAL == 10);
+  bochs_printf("read s='%s'\n", buff);
+  TEST_EXPECT_CONDITION(strcmp("Bonjour !", buff), RETVAL ==0);
+  TEST_EXPECT_CONDITION(lseek(fd, 0, SEEK_CUR), RETVAL == 10);
+
+  /*
+   * truncate
+   */
+
+  TEST_EXPECT_CONDITION(ftruncate(fd, 3), RETVAL == 0);
+  
+  /* The current position should not have changed */
+  TEST_EXPECT_CONDITION(lseek(fd, 0, SEEK_CUR), RETVAL == 10);
+
+  /* Make sure we cannot read anything because we get past the end of
+     the file */
+  strzcpy(buff, "Garbage garbage garbage", 256);
+  TEST_EXPECT_CONDITION(len = read(fd, buff, 256), RETVAL == 0);
+
+  /* Now get back at the begining of the file */
+  TEST_EXPECT_CONDITION(lseek(fd, 0, SEEK_SET), RETVAL == 0);
+
+  /* Make sure that we can read something with the correct first 3
+     characters */
+  strzcpy(buff, "Garbage garbage garbage", 256);
+  TEST_EXPECT_CONDITION(len = read(fd, buff, 256), RETVAL == 3);
+  bochs_printf("read s='%s'\n", buff);
+  TEST_EXPECT_CONDITION(strncmp("Bon", buff, len), RETVAL == 0);
+
+  /*
+   * open mode
+   */
+
+  ls("/", 1, 1);
+
+
+  /* Open r/w, create read-only */
+
+  TEST_EXPECT_CONDITION(fd = open("toto2.txt", O_RDWR | O_CREAT, S_IRUSR),
+			RETVAL == 12);
+
+  strzcpy(buff, "Garbage garbage garbage", 256);
+  TEST_EXPECT_CONDITION(len = read(fd, buff, 256),
+			RETVAL == 0);
+
+  TEST_EXPECT_CONDITION(lseek(fd, 0, SEEK_SET), RETVAL == 0);
+
+  strzcpy(buff, "Permission denied", 256);
+  TEST_EXPECT_CONDITION(len = write(fd, buff, 10),
+			RETVAL < 0); /* Permission denied ! */
+
+  TEST_EXPECT_CONDITION(lseek(fd, 0, SEEK_SET), RETVAL == 0);
+
+  strzcpy(buff, "Garbage garbage garbage", 256);
+  TEST_EXPECT_CONDITION(len = read(fd, buff, 256), RETVAL == 0);
+
+  /* Open read-only, create r/w */
+
+  TEST_EXPECT_CONDITION(fd = open("toto3.txt", O_RDONLY | O_CREAT,
+				  S_IRUSR | S_IWUSR),
+			RETVAL == 13);
+
+  strzcpy(buff, "Garbage garbage garbage", 256);
+  TEST_EXPECT_CONDITION(len = read(fd, buff, 256), RETVAL == 0);
+
+  TEST_EXPECT_CONDITION(lseek(fd, 0, SEEK_SET), RETVAL == 0);  
+
+  strzcpy(buff, "Permission denied 2", 256);
+  TEST_EXPECT_CONDITION(len = write(fd, buff, 10),
+			RETVAL < 0); /* Permission denied ! */
+
+  TEST_EXPECT_CONDITION(lseek(fd, 0, SEEK_SET), RETVAL == 0);  
+
+  strzcpy(buff, "Garbage garbage garbage", 256);
+  TEST_EXPECT_CONDITION(len = read(fd, buff, 256), RETVAL == 0);
+
+  /* Create another process that chdirs in it */
+  if (fork() == 0)
+    {
+      bochs_printf("Hello from child\n");
+      TEST_EXPECT_CONDITION(fd = open("shrd.txt", O_RDWR | O_CREAT, S_IRWXALL),
+			    RETVAL == 14);
+      strzcpy(buff, "Hello from child !", 256);
+      TEST_EXPECT_CONDITION(len = write(fd, buff, 19),
+			    RETVAL == 19);
+      TEST_EXPECT_CONDITION(close(fd), RETVAL == 0);
+      bochs_printf("Bye from child\n");
+      return 0;
+    }
+
+  bochs_printf("Father sleeping\n");
+  nanosleep(1, 0);
+  ls("/", 1, 1);
+  TEST_EXPECT_CONDITION(fd = open("shrd.txt", O_RDONLY),
+			RETVAL == 14);
+  strzcpy(buff, "Garbage garbage garbage", 256);
+  TEST_EXPECT_CONDITION(len = read(fd, buff, 256),
+			RETVAL == 19);
+  bochs_printf("read s='%s'\n", buff);
+  TEST_EXPECT_CONDITION(strncmp("Hello from child !", buff, len),
+			RETVAL == 0);
+  TEST_EXPECT_CONDITION(close(fd), RETVAL == 0);
+  TEST_EXPECT_CONDITION(unlink("shrd.txt"), RETVAL == 0);
+  ls("/", 1, 1);
+
+  /*
+   * ioctl / fcntl
+   */
+
+  TEST_EXPECT_CONDITION(fcntl(fd, 2, 3), RETVAL < 0); /* Not supported
+							 by virtfs */
+
+  ls("/", 1, 1);
+
+  /*
+   * creat/link/unlink/symlink
+   */
+  TEST_EXPECT_CONDITION(creat("toto4.txt", S_IRUSR), RETVAL == 0);
+  TEST_EXPECT_CONDITION(creat("toto4.txt", S_IRWXALL), RETVAL < 0); /*EEXIST*/
+  TEST_EXPECT_CONDITION(link("toto4.txt", "toto5.txt"), RETVAL == 0);
+  TEST_EXPECT_CONDITION(link("toto4.txt", "toto5.txt"), RETVAL < 0); /*EEXIST*/
+  TEST_EXPECT_CONDITION(link("toto4.txt", "toto.txt"), RETVAL < 0); /*EEXIST*/
+  ls("/", 1, 1);
+  TEST_EXPECT_CONDITION(chmod("toto5.txt", S_IRUSR | S_IWUSR), RETVAL == 0);
+  ls("/", 1, 1);
+
+  TEST_EXPECT_CONDITION(fd = open("toto5.txt", O_RDWR), RETVAL == 14);
+  strzcpy(buff, "Garbage garbage garbage", 256);
+  TEST_EXPECT_CONDITION(len = read(fd, buff, 256), RETVAL == 0);
+
+  TEST_EXPECT_CONDITION(lseek(fd, 0, SEEK_SET), RETVAL == 0);  
+
+  strzcpy(buff, "Hello world from toto5", 256);
+  TEST_EXPECT_CONDITION(len = write(fd, buff, 24), RETVAL == 24);
+
+  TEST_EXPECT_CONDITION(lseek(fd, 0, SEEK_SET), RETVAL == 0);  
+
+  strzcpy(buff, "Garbage garbage garbage", 256);
+  TEST_EXPECT_CONDITION(len = read(fd, buff, 256), RETVAL == 24);
+  bochs_printf("read s='%s'\n", buff);
+
+  TEST_EXPECT_CONDITION(fd = open("toto4.txt", O_RDWR), RETVAL == 15);
+  strzcpy(buff, "Garbage garbage garbage", 256);
+  TEST_EXPECT_CONDITION(len = read(fd, buff, 256), RETVAL == 24);
+  bochs_printf("read s='%s'\n", buff);
+
+  ls("/", 1, 1);
+
+  TEST_EXPECT_CONDITION(link("dangling", "toto42.txt"), RETVAL < 0); /*ENOENT*/
+  TEST_EXPECT_CONDITION(unlink("toto4.txt"), RETVAL == 0);
+  TEST_EXPECT_CONDITION(unlink("toto42.txt"), RETVAL < 0); /* ENOENT */
+  TEST_EXPECT_CONDITION(unlink("toto4.txt"), RETVAL < 0); /* ENOENT */
+  TEST_EXPECT_CONDITION(creat("toto4.txt", S_IWUSR | S_IRUSR), RETVAL == 0);
+  TEST_EXPECT_CONDITION(unlink("toto5.txt/"), RETVAL < 0); /* EISDIR ? */
+  TEST_EXPECT_CONDITION(rmdir("toto5.txt/"), RETVAL < 0); /* ENOTDIR ? */
+  TEST_EXPECT_CONDITION(rmdir("toto5.txt"), RETVAL < 0); /* ENOTDIR ? */
+  TEST_EXPECT_CONDITION(unlink("toto5.txt"), RETVAL == 0);
+  TEST_EXPECT_CONDITION(unlink("toto5.txt"), RETVAL < 0); /* ENOENT */
+  TEST_EXPECT_CONDITION(creat("toto4.txt", S_IRWXALL), RETVAL < 0); /*EEXIST*/
+
+  ls("/", 1, 1);
+
+  /* Change symlinks */
+  TEST_EXPECT_CONDITION(symlink("toto4.txt", "toto5.txt"), RETVAL == 0);
+  TEST_EXPECT_CONDITION(symlink("toto4.txt", "toto5.txt"), RETVAL < 0); /*EEXIST*/
+  TEST_EXPECT_CONDITION(symlink("toto4.txt", "toto.txt"), RETVAL < 0); /*EEXIST*/
+
+  ls("/", 1, 1);
+
+  TEST_EXPECT_CONDITION(fd = open("toto5.txt", O_RDWR), RETVAL == 16);
+  strzcpy(buff, "Garbage garbage garbage", 256);
+  TEST_EXPECT_CONDITION(len = read(fd, buff, 256), RETVAL == 0);
+
+  TEST_EXPECT_CONDITION(lseek(fd, 0, SEEK_SET), RETVAL == 0);  
+
+  strzcpy(buff, "Hello world from toto5", 256);
+  TEST_EXPECT_CONDITION(len = write(fd, buff, 24), RETVAL == 24);
+
+  TEST_EXPECT_CONDITION(lseek(fd, 0, SEEK_SET), RETVAL == 0);  
+
+  strzcpy(buff, "Garbage garbage garbage", 256);
+  TEST_EXPECT_CONDITION(len = read(fd, buff, 256), RETVAL == 24);
+  bochs_printf("read s='%s'\n", buff);
+  TEST_EXPECT_CONDITION(strcmp(buff, "Hello world from toto5"), RETVAL == 0);
+
+  TEST_EXPECT_CONDITION(fd = open("toto4.txt", O_RDWR), RETVAL == 17);
+  strzcpy(buff, "Garbage garbage garbage", 256);
+  TEST_EXPECT_CONDITION(len = read(fd, buff, 256), RETVAL == 24);
+  bochs_printf("read s='%s'\n", buff);
+  TEST_EXPECT_CONDITION(strcmp(buff, "Hello world from toto5"), RETVAL == 0);
+
+
+  TEST_EXPECT_CONDITION(symlink("dangling", "toto6.txt"), RETVAL == 0);
+  TEST_EXPECT_CONDITION(symlink("dangling", "toto6.txt"), RETVAL < 0); /*EEXIST*/
+
+  TEST_EXPECT_CONDITION(fd = open("toto6.txt", O_RDWR), RETVAL < 0);
+  TEST_EXPECT_CONDITION(fd = open("toto6.txt", O_RDWR | O_NOFOLLOW), RETVAL == 18);
+  strzcpy(buff, "Garbage garbage garbage", 256);
+  TEST_EXPECT_CONDITION(len = read(fd, buff, 256), RETVAL == 8);
+  bochs_printf("read s='%s'\n", buff);
+  TEST_EXPECT_CONDITION(strncmp(buff, "dangling", len), RETVAL == 0);
+
+  ls("/", 1, 1);
+
+  /* mkdir/rmdir */
+
+  TEST_EXPECT_CONDITION(mkdir("yo1", S_IRUSR | S_IXUSR), RETVAL == 0);
+  TEST_EXPECT_CONDITION(mkdir("yo1", S_IRWXALL), RETVAL < 0); /*EEXIST*/
+
+  ls("/", 1, 1);
+
+  TEST_EXPECT_CONDITION(unlink("yo1"), RETVAL < 0); /*EISDIR*/
+  TEST_EXPECT_CONDITION(unlink("yo1/"), RETVAL < 0); /*EISDIR*/
+  TEST_EXPECT_CONDITION(rmdir("yo1"), RETVAL == 0);
+  TEST_EXPECT_CONDITION(rmdir("yo1"), RETVAL < 0); /*ENOENT*/
+  TEST_EXPECT_CONDITION(rmdir("yoda"), RETVAL < 0); /*ENOENT*/
+
+  ls("/", 1, 1);
+
+  TEST_EXPECT_CONDITION(mkdir("yoplait1", S_IRWXALL), RETVAL == 0);
+  TEST_EXPECT_CONDITION(rename("yoplait1", "mouf1"), RETVAL == 0);
+  TEST_EXPECT_CONDITION(fd = open("mouf1/a", O_RDWR | O_CREAT, S_IRWXALL), RETVAL == 19);
+  TEST_EXPECT_CONDITION(unlink("mouf1/a"), RETVAL == 0);
+  TEST_EXPECT_CONDITION(rmdir("mouf1"), RETVAL == 0);
+
+  TEST_EXPECT_CONDITION(mkdir("yoplait2", S_IRWXALL), RETVAL == 0);
+  TEST_EXPECT_CONDITION(rename("yoplait2", "mouf2"), RETVAL == 0);
+  TEST_EXPECT_CONDITION(fd = open("mouf2/a", O_RDWR | O_CREAT, S_IRWXALL), RETVAL == 20);
+  TEST_EXPECT_CONDITION(rmdir("mouf2"), RETVAL < 0);
+
+  TEST_EXPECT_CONDITION(mkdir("yoplait3", S_IRWXALL), RETVAL == 0);
+  TEST_EXPECT_CONDITION(rename("yoplait3", "mouf3"), RETVAL == 0);
+  TEST_EXPECT_CONDITION(creat("mouf3/a", S_IRWXALL), RETVAL == 0);
+  TEST_EXPECT_CONDITION(unlink("mouf3/a"), RETVAL == 0);
+  TEST_EXPECT_CONDITION(rmdir("mouf3"), RETVAL == 0);
+
+  TEST_EXPECT_CONDITION(mkdir("yoplait4", S_IRWXALL), RETVAL == 0);
+  TEST_EXPECT_CONDITION(rename("yoplait4", "mouf4"), RETVAL == 0);
+  TEST_EXPECT_CONDITION(creat("mouf4/a", S_IRWXALL), RETVAL == 0);
+  TEST_EXPECT_CONDITION(rmdir("mouf4"), RETVAL < 0);
+
+  ls("/", 1, 1);
+
+  ls("/", 1, 1);
+  ls("..", 1, 1);
+  ls("../", 1, 1);
+  ls("/..", 1, 1);
+
+  /* subdirs */
+  TEST_EXPECT_CONDITION(fd = open("yo1/titi1.txt", O_RDONLY), RETVAL < 0);
+  TEST_EXPECT_CONDITION(fd = open("yo1/titi1.txt", O_RDONLY | O_CREAT, S_IRUSR), RETVAL < 0);
+
+  ls("/", 1, 1);
+  ls("/yo1", 1, 1);
+
+  TEST_EXPECT_CONDITION(mkdir("yo1", S_IRWXALL), RETVAL == 0);
+  TEST_EXPECT_CONDITION(fd = open("yo1/titi1.txt", O_RDONLY), RETVAL < 0);
+  TEST_EXPECT_CONDITION(fd = open("yo1/titi1.txt", O_RDONLY | O_CREAT, S_IRUSR), RETVAL == 21);
+
+  ls("/", 1, 1);
+  ls("/yo1", 1, 1);
+
+  TEST_EXPECT_CONDITION(mkdir("yo2", S_IRUSR | S_IXUSR), RETVAL == 0);
+  TEST_EXPECT_CONDITION(fd = open("yo2/titi1.txt", O_RDONLY), RETVAL < 0);
+  TEST_EXPECT_CONDITION(fd = open("yo2/titi1.txt", O_RDONLY | O_CREAT, S_IRUSR), RETVAL < 0);
+
+  ls("/", 1, 1);
+
+  TEST_EXPECT_CONDITION(mkdir("yo3", S_IWUSR | S_IXUSR), RETVAL == 0);
+  TEST_EXPECT_CONDITION(fd = open("yo3/titi1.txt", O_RDONLY), RETVAL < 0);
+  TEST_EXPECT_CONDITION(fd = open("yo3/titi1.txt", O_RDONLY | O_CREAT, S_IRUSR), RETVAL == 22);
+
+  ls("/", 1, 1);
+
+  TEST_EXPECT_CONDITION(mkdir("yo4", S_IWUSR), RETVAL == 0);
+  TEST_EXPECT_CONDITION(fd = open("yo4/titi1.txt", O_RDONLY), RETVAL < 0);
+  TEST_EXPECT_CONDITION(fd = open("yo4/titi1.txt", O_RDONLY | O_CREAT, S_IRUSR), RETVAL < 0);
+
+  ls("/", 1, 1);
+
+  TEST_EXPECT_CONDITION(chdir("nowhere"), RETVAL < 0);
+  TEST_EXPECT_CONDITION(chdir("yo1"), RETVAL == 0);
+
+  ls(".", 1, 1);
+  ls("/", 1, 1);
+  ls("..", 1, 1);
+  ls("../../../../", 1, 1);
+  ls("/../../../../", 1, 1);
+
+  /* Test chroot */
+
+  TEST_EXPECT_CONDITION(chroot("nowhere"), RETVAL < 0);
+  TEST_EXPECT_CONDITION(chroot("."), RETVAL == 0);
+  ls(".", 1, 1);
+  ls("/", 1, 1);
+  ls("..", 1, 1);
+  ls("../../../../", 1, 1);
+  ls("/../../../../", 1, 1);
+
+  /* mount */
+  TEST_EXPECT_CONDITION(mount(NULL, "nowhere", NULL, 0, NULL), RETVAL < 0);
+  TEST_EXPECT_CONDITION(mount(NULL, "nowhere", "yoplait", 0, NULL), RETVAL < 0);
+  TEST_EXPECT_CONDITION(mount(NULL, "nowhere", "virtfs", 0, NULL), RETVAL < 0);
+
+  TEST_EXPECT_CONDITION(mkdir("mnt", S_IRWXALL), RETVAL == 0);
+  TEST_EXPECT_CONDITION(mkdir("mnt/subdir0", S_IRWXALL), RETVAL == 0);
+  ls("/", 1, 1);
+  TEST_EXPECT_CONDITION(mount(NULL, "mnt", "virtfs", 0, NULL), RETVAL == 0);
+  ls("/", 1, 1);
+  TEST_EXPECT_CONDITION(mkdir("mnt/subdir_mounted", S_IRWXALL), RETVAL == 0);
+  ls("/", 1, 1);
+
+  /* Make sure we cannot umount if the FS is in use */
+  TEST_EXPECT_CONDITION(fd = open("mnt/subdir_mounted", O_DIRECTORY), RETVAL == 23);
+  TEST_EXPECT_CONDITION(umount("mnt"), RETVAL < 0);
+  TEST_EXPECT_CONDITION(close(fd), RETVAL == 0);
+
+  /* Make sure we cannot umount if the FS is in use */
+  TEST_EXPECT_CONDITION(chdir("mnt"), RETVAL == 0);
+  TEST_EXPECT_CONDITION(umount("/mnt"), RETVAL < 0);
+  TEST_EXPECT_CONDITION(chdir(".."), RETVAL == 0);
+  ls(".", 1, 1);
+  
+  /* Create another process that chdirs in it */
+  if (fork() == 0)
+    {
+      bochs_printf("Hello from child\n");
+      TEST_EXPECT_CONDITION(chdir("mnt"), RETVAL == 0);
+      TEST_EXPECT_CONDITION(fd = open("subdir_mounted", O_DIRECTORY), RETVAL == 23);
+      bochs_printf("Child sleeping...\n");
+      nanosleep(2, 0);
+      bochs_printf("Bye from child\n");
+      return 0;
+    }
+  else
+    {
+      bochs_printf("Father sleeping\n");
+      nanosleep(1, 0);
+      bochs_printf("Father trying to umount, should fail (a process chdir'ed in it)\n");
+      TEST_EXPECT_CONDITION(umount("mnt"), RETVAL < 0);
+      bochs_printf("Father Resuming normal operation in 3s...\n");
+      nanosleep(3, 0);
+    }
+
+  TEST_EXPECT_CONDITION(umount(NULL), RETVAL < 0);
+  TEST_EXPECT_CONDITION(umount("nowhere"), RETVAL < 0);
+  ls("/", 1, 1);
+  TEST_EXPECT_CONDITION(umount("mnt"), RETVAL == 0);
+  ls("/", 1, 1);
+  TEST_EXPECT_CONDITION(umount("mnt"), RETVAL < 0);
+
+  /*
+   * Mountchain exploration
+   */
+  TEST_EXPECT_CONDITION(mkdir("/mnt2", S_IRWXALL), RETVAL == 0);
+  TEST_EXPECT_CONDITION(mkdir("/mnt2/nothing-mounted", S_IRWXALL), RETVAL == 0);
+  TEST_EXPECT_CONDITION(mount(NULL, "mnt2", "virtfs", 0, NULL), RETVAL == 0);
+  TEST_EXPECT_CONDITION(mkdir("/mnt2/mountpoint-1", S_IRWXALL), RETVAL == 0);
+  TEST_EXPECT_CONDITION(mount(NULL, "mnt2", "virtfs", 0, NULL), RETVAL == 0);
+  TEST_EXPECT_CONDITION(mkdir("/mnt2/mountpoint-2", S_IRWXALL), RETVAL == 0);
+  ls("/", 1, 1);
+  TEST_EXPECT_CONDITION(umount("mnt2"), RETVAL == 0);
+  ls("/", 1, 1);
+  TEST_EXPECT_CONDITION(umount("mnt2"), RETVAL == 0);
+  ls("/", 1, 1);
+  TEST_EXPECT_CONDITION(umount("mnt2"), RETVAL < 0);
+
+  /*
+   * Erasing files while they are in use
+   */
+
+  TEST_EXPECT_CONDITION(fd = open("toto8.txt", O_RDWR | O_CREAT,
+				  S_IRUSR | S_IWUSR),
+			RETVAL == 23);
+  ls("/", 1, 1);
+  TEST_EXPECT_CONDITION(unlink("toto8.txt"), RETVAL == 0);
+  ls("/", 1, 1);
+
+  strzcpy(buff, "Garbage garbage garbage", 256);
+  TEST_EXPECT_CONDITION(len = read(fd, buff, 256), RETVAL == 0);
+
+  TEST_EXPECT_CONDITION(lseek(fd, 0, SEEK_SET), RETVAL == 0);  
+
+  strzcpy(buff, "Hello world from toto8", 256);
+  TEST_EXPECT_CONDITION(len = write(fd, buff, 24), RETVAL == 24);
+
+  TEST_EXPECT_CONDITION(lseek(fd, 0, SEEK_SET), RETVAL == 0);  
+
+  strzcpy(buff, "Garbage garbage garbage", 256);
+  TEST_EXPECT_CONDITION(len = read(fd, buff, 256), RETVAL == 24);
+  bochs_printf("read s='%s'\n", buff);
+
+  /*
+   * rmdir on still used dirs
+   */
+  TEST_EXPECT_CONDITION(mkdir("plotch", S_IRWXALL), RETVAL == 0);
+  TEST_EXPECT_CONDITION(chdir("plotch"), RETVAL == 0);
+  TEST_EXPECT_CONDITION(rmdir("../plotch"), RETVAL < 0);
+  TEST_EXPECT_CONDITION(chdir(".."), RETVAL == 0);
+  TEST_EXPECT_CONDITION(rmdir("plotch"), RETVAL == 0);
+  ls("/", 1, 1);
+
+  TEST_EXPECT_CONDITION(mkdir("plotch", S_IRWXALL), RETVAL == 0);
+  TEST_EXPECT_CONDITION(creat("plotch/a", S_IRWXALL), RETVAL == 0);
+  TEST_EXPECT_CONDITION(rmdir("plotch"), RETVAL < 0);
+  TEST_EXPECT_CONDITION(unlink("plotch/a"), RETVAL == 0);
+  TEST_EXPECT_CONDITION(rmdir("plotch"), RETVAL == 0);
+  ls("/", 1, 1);
+
+  TEST_EXPECT_CONDITION(mkdir("plotch", S_IRWXALL), RETVAL == 0);
+  TEST_EXPECT_CONDITION(fd = open("plotch/a", O_RDWR | O_CREAT, S_IRWXALL),
+			RETVAL == 24);
+  TEST_EXPECT_CONDITION(rmdir("plotch"), RETVAL < 0);
+  TEST_EXPECT_CONDITION(unlink("plotch/a"), RETVAL == 0);
+  TEST_EXPECT_CONDITION(rmdir("plotch"), RETVAL == 0);
+  TEST_EXPECT_CONDITION(close(fd), RETVAL == 0);
+  ls("/", 1, 1);
+
+  TEST_EXPECT_CONDITION(mkdir("this is ", S_IRWXALL), RETVAL == 0);
+  TEST_EXPECT_CONDITION(mkdir("this is / a long path", S_IRWXALL), RETVAL == 0);
+  TEST_EXPECT_CONDITION(mkdir("this is / a long path/tothe", S_IRWXALL), RETVAL == 0);
+  TEST_EXPECT_CONDITION(mkdir("this is / a long path/tothe/destination ", S_IRWXALL), RETVAL == 0);
+  TEST_EXPECT_CONDITION(mkdir("this is / a long path/tothe/destination / directory", S_IRWXALL), RETVAL == 0);
+  TEST_EXPECT_CONDITION(fd = open("this is / a long path/tothe/destination / directory/a", O_RDWR | O_CREAT, S_IRWXALL),
+			RETVAL == 24);
+  TEST_EXPECT_CONDITION(rmdir("this is / a long path/tothe/destination / directory"), RETVAL < 0);
+  TEST_EXPECT_CONDITION(unlink("this is / a long path/tothe/destination / directory/a"), RETVAL == 0);
+  TEST_EXPECT_CONDITION(rmdir("this is / a long path/tothe/destination / directory"), RETVAL == 0);
+  TEST_EXPECT_CONDITION(rmdir("this is / a long path/tothe/destination / directory/"), RETVAL < 0);
+  TEST_EXPECT_CONDITION(rmdir("this is / a long path/tothe/destination "), RETVAL == 0);
+  TEST_EXPECT_CONDITION(rmdir("this is / a long path/tothe/"), RETVAL == 0);
+  TEST_EXPECT_CONDITION(rmdir("this is / a long path"), RETVAL == 0);
+  TEST_EXPECT_CONDITION(rmdir("this is "), RETVAL == 0);
+  TEST_EXPECT_CONDITION(close(fd), RETVAL == 0);
+  ls("/", 1, 1);
+
+  /*
+   * Unlink/link files while they are in use
+   */
+
+  TEST_EXPECT_CONDITION(fd = open("toto8.txt", O_RDWR | O_CREAT,
+				  S_IRUSR | S_IWUSR),
+			RETVAL == 24);
+  ls("/", 1, 1);
+  TEST_EXPECT_CONDITION(link("toto8.txt", "toto9.txt"), RETVAL == 0);
+  TEST_EXPECT_CONDITION(unlink("toto8.txt"), RETVAL == 0);
+  ls("/", 1, 1);
+
+  strzcpy(buff, "Garbage garbage garbage", 256);
+  TEST_EXPECT_CONDITION(len = read(fd, buff, 256), RETVAL == 0);
+
+  TEST_EXPECT_CONDITION(lseek(fd, 0, SEEK_SET), RETVAL == 0);  
+
+  strzcpy(buff, "Hello world from toto8", 256);
+  TEST_EXPECT_CONDITION(len = write(fd, buff, 24), RETVAL == 24);
+
+  TEST_EXPECT_CONDITION(lseek(fd, 0, SEEK_SET), RETVAL == 0);  
+
+  strzcpy(buff, "Garbage garbage garbage", 256);
+  TEST_EXPECT_CONDITION(len = read(fd, buff, 256), RETVAL == 24);
+  bochs_printf("read s='%s'\n", buff);
+
+  TEST_EXPECT_CONDITION(fd = open("toto8.txt", O_RDWR), RETVAL < 0);
+  TEST_EXPECT_CONDITION(fd = open("toto9.txt", O_RDWR), RETVAL == 25);
+
+  strzcpy(buff, "Garbage garbage garbage", 256);
+  TEST_EXPECT_CONDITION(len = read(fd, buff, 256), RETVAL == 24);
+  bochs_printf("read s='%s'\n", buff);
+  TEST_EXPECT_CONDITION(unlink("toto9.txt"), RETVAL == 0);
+  ls("/", 1, 1);
+
+  /*
+   * Rename files while they are in use
+   */
+
+  TEST_EXPECT_CONDITION(fd = open("toto8.txt", O_RDWR | O_CREAT,
+				  S_IRUSR | S_IWUSR),
+			RETVAL == 26);
+  ls("/", 1, 1);
+  TEST_EXPECT_CONDITION(rename("toto8.txt", "toto9.txt"), RETVAL == 0);
+  ls("/", 1, 1);
+
+  strzcpy(buff, "Garbage garbage garbage", 256);
+  TEST_EXPECT_CONDITION(len = read(fd, buff, 256), RETVAL == 0);
+
+  TEST_EXPECT_CONDITION(lseek(fd, 0, SEEK_SET), RETVAL == 0);  
+
+  strzcpy(buff, "Hello world from toto8", 256);
+  TEST_EXPECT_CONDITION(len = write(fd, buff, 24), RETVAL == 24);
+
+  TEST_EXPECT_CONDITION(lseek(fd, 0, SEEK_SET), RETVAL == 0);  
+
+  strzcpy(buff, "Garbage garbage garbage", 256);
+  TEST_EXPECT_CONDITION(len = read(fd, buff, 256), RETVAL == 24);
+  bochs_printf("read s='%s'\n", buff);
+  TEST_EXPECT_CONDITION(close(fd), RETVAL == 0);
+
+  TEST_EXPECT_CONDITION(fd = open("toto8.txt", O_RDWR), RETVAL < 0);
+  TEST_EXPECT_CONDITION(fd = open("toto9.txt", O_RDWR), RETVAL == 26);
+
+  strzcpy(buff, "Garbage garbage garbage", 256);
+  TEST_EXPECT_CONDITION(len = read(fd, buff, 256), RETVAL == 24);
+  bochs_printf("read s='%s'\n", buff);
+  TEST_EXPECT_CONDITION(unlink("toto9.txt"), RETVAL == 0);
+  TEST_EXPECT_CONDITION(close(fd), RETVAL == 0);
+
+  /* Rename */
+  ls("/", 1, 1);
+  TEST_EXPECT_CONDITION(rename("/mnt/subdir0", "subdir42"), RETVAL == 0);
+  ls("/", 1, 1);
+  TEST_EXPECT_CONDITION(rename("titi1.txt", "subdir42"), RETVAL < 0);
+  ls("/", 1, 1);
+  TEST_EXPECT_CONDITION(rename("titi1.txt", "subdir42/titi.txt"), RETVAL == 0);
+
+  /* Rename a dir being used */
+  TEST_EXPECT_CONDITION(chdir("subdir42"), RETVAL == 0);
+  ls(".", 1, 1);
+  ls("..", 1, 1);
+  ls("/", 1, 1);
+  TEST_EXPECT_CONDITION(rename("../subdir42", "../subdir000"), RETVAL == 0);
+  ls(".", 1, 1);
+  ls("/", 1, 1);
+  ls("..", 1, 1);
+
+  /*
+   * test mmap
+   */
+
+  /* Common use: shared file suppressed as soon as possible */
+  TEST_EXPECT_CONDITION(fd = open("mmap.txt", O_RDWR | O_CREAT,
+				  S_IRUSR | S_IWUSR),
+			RETVAL == 26);
+  if (fork() == 0)
+    {
+      char *shrd;
+      TEST_EXPECT_CONDITION(shrd = mmap(NULL, 4096, PROT_READ | PROT_WRITE,
+					MAP_SHARED, fd, 4096),
+			    shrd != NULL);
+      nanosleep(1, 0);
+      strzcpy(shrd, "Hello1 from the child (shared mapping) !", 4096);
+      return 0;
+    }
+  else
+    {
+      char *shrd;
+      TEST_EXPECT_CONDITION(shrd = mmap(NULL, 4096, PROT_READ | PROT_WRITE,
+					MAP_SHARED, fd, 4096),
+			    shrd != NULL);
+      strzcpy(shrd, "Garbage garbage garbage", 256);
+      nanosleep(2, 0);
+      bochs_printf("Father woken up\n");
+      bochs_printf("Read string from child: '%s'\n", shrd);
+      TEST_EXPECT_CONDITION(strcmp(shrd, "Hello1 from the child (shared mapping) !"),
+			    RETVAL == 0);
+      munmap(shrd, 8192);
+    }
+  ls("/", 1, 1);
+  TEST_EXPECT_CONDITION(unlink("mmap.txt"), RETVAL == 0);
+  ls("/", 1, 1);
+
+  /* Common use: shared file suppressed as soon as possible */
+  TEST_EXPECT_CONDITION(fd = open("mmap.txt", O_RDWR | O_CREAT,
+				  S_IRUSR | S_IWUSR),
+			RETVAL == 27);
+  TEST_EXPECT_CONDITION(unlink("mmap.txt"), RETVAL == 0);
+  if (fork() == 0)
+    {
+      char *shrd;
+      TEST_EXPECT_CONDITION(shrd = mmap(NULL, 4096, PROT_READ | PROT_WRITE,
+					MAP_SHARED, fd, 4096),
+			    shrd != NULL);
+      nanosleep(1, 0);
+      strzcpy(shrd, "Hello2 from the child (shared mapping) !", 4096);
+      return 0;
+    }
+  else
+    {
+      char *shrd;
+      TEST_EXPECT_CONDITION(shrd = mmap(NULL, 4096, PROT_READ | PROT_WRITE,
+					MAP_SHARED, fd, 4096),
+			    shrd != NULL);
+      strzcpy(shrd, "Garbage garbage garbage", 256);
+      nanosleep(2, 0);
+      bochs_printf("Father woken up\n");
+      bochs_printf("Read string from child: '%s'\n", shrd);
+      TEST_EXPECT_CONDITION(strcmp(shrd, "Hello2 from the child (shared mapping) !"),
+			    RETVAL == 0);
+    }
+  ls("/", 1, 1);
+
+  /* Basic use */
+  TEST_EXPECT_CONDITION(creat("mmap.txt", S_IRWXALL), RETVAL == 0);
+  if (fork() == 0)
+    {
+      char *shrd;
+      TEST_EXPECT_CONDITION(fd = open("mmap.txt", O_RDWR | O_CREAT,
+				      S_IRUSR | S_IWUSR),
+			    RETVAL == 28);
+      TEST_EXPECT_CONDITION(shrd = mmap(NULL, 4096, PROT_READ | PROT_WRITE,
+					MAP_SHARED, fd, 4096),
+			    shrd != NULL);
+      nanosleep(1, 0);
+      strzcpy(shrd, "Hello3 from the child (shared mapping) !", 4096);
+      return 0;
+    }
+  else
+    {
+      char *shrd;
+      TEST_EXPECT_CONDITION(fd = open("mmap.txt", O_RDWR | O_CREAT,
+				      S_IRUSR | S_IWUSR),
+			    RETVAL == 28);
+      TEST_EXPECT_CONDITION(shrd = mmap(NULL, 4096, PROT_READ | PROT_WRITE,
+					MAP_SHARED, fd, 4096),
+			    shrd != NULL);
+      strzcpy(shrd, "Garbage garbage garbage", 256);
+      nanosleep(2, 0);
+      bochs_printf("Father woken up\n");
+      bochs_printf("Read string from child: '%s'\n", shrd);
+      TEST_EXPECT_CONDITION(strcmp(shrd, "Hello3 from the child (shared mapping) !"),
+			    RETVAL == 0);
+    }
+  ls("/", 1, 1);
+  
+  bochs_printf("Bye from fstest\n");
+  ls("/", 1, 1);
+  return 0;
+}
diff -ruN /tmp/sos-code-article7.5/userland/fstest_utils.c ../sos-code-article8/userland/fstest_utils.c
--- /tmp/sos-code-article7.5/userland/fstest_utils.c	1970-01-01 01:00:00.000000000 +0100
+++ ../sos-code-article8/userland/fstest_utils.c	2005-07-01 16:39:50.000000000 +0200
@@ -0,0 +1,139 @@
+/* Copyright (C) 2005  David Decotigny
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License
+   as published by the Free Software Foundation; either version 2
+   of the License, or (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+   USA. 
+*/
+
+#include <libc.h>
+#include <debug.h>
+
+#include "fstest_utils.h"
+
+
+/** Helper functions that dumps the contents of the current working
+    directory of the process */
+static void cwd_ls(int detailed, int recursive, int reclevel)
+{
+  char tab[256], *c;
+  int i;
+  struct dirent * dirent;
+  DIR * here;
+
+  here = opendir(".");
+  if (! here)
+    return;
+
+  /* Build initial tabulation */
+  if (recursive)
+    {
+      for (c = tab, i = 0 ; (i < reclevel) && (i < sizeof(tab)/2) ; i++)
+	{
+	  *c++ = ' ';
+	  *c++ = ' ';
+	}
+      *c++ = '\0';
+    }
+  else
+    *tab = '\0';
+
+  while ((dirent = readdir(here)) != NULL)
+    {
+      char entrychar;
+      char * entrysuffix;
+
+      switch(dirent->type)
+	{
+	case S_IFREG: entrychar='-'; entrysuffix=""; break;
+	case S_IFDIR: entrychar='d'; entrysuffix="/"; break;
+	case S_IFLNK: entrychar='l'; entrysuffix="@"; break;
+	default: entrychar='?'; entrysuffix="?!?"; break;
+	}
+
+      if (detailed)
+	{
+	  struct stat stat;
+	  char target_name[SOS_FS_DIRENT_NAME_MAXLEN];
+	  
+	  if (lstat(dirent->name, & stat))
+	    continue;
+
+	  *target_name = '\0';
+	  if (stat.st_type==S_IFLNK)
+	    {
+	      int fd = open(dirent->name, O_RDONLY | O_NOFOLLOW);
+	      if (fd >= 0)
+		{
+		  int len = read(fd, target_name, sizeof(target_name) - 1);
+		  if (len < 0)
+		    *target_name='\0';
+		  else
+		    target_name[len] = '\0';
+		  close(fd);
+		}
+	    }
+
+	  bochs_printf("%s%c%c%c%c %lld %s%s%s%s (location=0x%llx)\n",
+		       tab, entrychar,
+		       (stat.st_access_rights&S_IRUSR)?'r':'-',
+		       (stat.st_access_rights&S_IWUSR)?'w':'-',
+		       (stat.st_access_rights&S_IXUSR)?'x':'-',
+		       stat.st_size,
+		       dirent->name,
+		       entrysuffix,
+		       (stat.st_type==S_IFLNK)?" -> ":"",
+		       target_name,
+		       stat.st_storage_location);
+	}
+      else
+	  bochs_printf("%s%s%s\n",
+		       tab, dirent->name, entrysuffix);
+
+      /* Next iteration */
+      if (recursive)
+	{
+	  int fd_here = dirfd(here);
+	  if (chdir(dirent->name))
+	    continue;
+	  cwd_ls(detailed, recursive, reclevel+1);
+	  if(fchdir(fd_here))
+            {
+		closedir(here);
+	        return;
+            }
+	}
+    }
+  closedir(here);
+}
+
+
+void ls(const char * path, int detailed, int recursive)
+{
+  int fd_here = open(".", O_RDONLY | O_DIRECTORY);
+  if (fd_here < 0)
+    return;
+
+  if (chdir(path))
+    {
+      close(fd_here);
+      return;
+    }
+
+  bochs_printf("----------- Contents of %s:\n", path);
+  cwd_ls(detailed, recursive, 1);
+  bochs_printf("---------------------------\n");
+
+  fchdir(fd_here);
+  close(fd_here);
+}
diff -ruN /tmp/sos-code-article7.5/userland/fstest_utils.h ../sos-code-article8/userland/fstest_utils.h
--- /tmp/sos-code-article7.5/userland/fstest_utils.h	1970-01-01 01:00:00.000000000 +0100
+++ ../sos-code-article8/userland/fstest_utils.h	2005-07-01 16:39:50.000000000 +0200
@@ -0,0 +1,41 @@
+/* Copyright (C) 2005  David Decotigny
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License
+   as published by the Free Software Foundation; either version 2
+   of the License, or (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+   USA. 
+*/
+#ifndef _SOS_FSTEST_UTILS_H_
+#define _SOS_FSTEST_UTILS_H_
+
+/**
+ * @file fstest.h
+ *
+ * Macro and support functions for the fstest programs
+ */
+
+#define TEST_EXPECT_CONDITION(code,condition) \
+  ({ static char * _str_ok = "[32mPASSED[m"; \
+     static char * _str_failed = "[31;1mFAILED[m"; \
+     int RETVAL = (int)(code); \
+     int _verdict = (int)(condition); \
+     bochs_printf("L%d [34m%s[m: %s %s (retval=%p aka %d)\n", \
+       __LINE__, #code, #condition, \
+       (_verdict)?_str_ok:_str_failed, (void*)RETVAL, RETVAL); \
+     _verdict; })
+
+
+/** Dump the list of files to the bochs debugging output */
+void ls(const char * path, int detailed, int recursive);
+
+#endif /* _SOS_FSTEST_UTILS_H_ */
diff -ruN /tmp/sos-code-article7.5/userland/init.c ../sos-code-article8/userland/init.c
--- /tmp/sos-code-article7.5/userland/init.c	2005-04-27 20:17:19.000000000 +0200
+++ ../sos-code-article8/userland/init.c	2005-07-01 16:39:50.000000000 +0200
@@ -27,31 +27,11 @@
 
 int main()
 {
+  bochs_printf("init: Welcome in userland !\n");
 
   /* launch the tests */
   if (fork() == 0)
-    exec("myprog7");
-
-  if (fork() == 0)
-    exec("myprog8");
-
-  if (fork() == 0)
-    exec("myprog9");
-
-  if (fork() == 0)
-    exec("myprog10");
-
-  if (fork() == 0)
-    exec("myprog11");
-
-  if (fork() == 0)
-    exec("myprog12"); 
- 
-  if (fork() == 0)
-    exec("myprog13");
-
-  if (fork() == 0)
-    exec("myprog14");
+    exec("fstest");
 
   if (fork() == 0)
     exec("banner");
diff -ruN /tmp/sos-code-article7.5/userland/libc.c ../sos-code-article8/userland/libc.c
--- /tmp/sos-code-article7.5/userland/libc.c	2005-04-27 20:17:19.000000000 +0200
+++ ../sos-code-article8/userland/libc.c	2005-07-01 16:39:50.000000000 +0200
@@ -18,16 +18,17 @@
 
 #include "crt.h"
 #include "libc.h"
+#include "stdarg.h"
 
 
-void * mmap(void *start, size_t length, int prot , int flags,
-	    const char *path, loff_t offset)
+void * fakemmap(void *start, size_t length, int prot , int flags,
+		const char *path, loff_t offset)
 {
   /* At kernel side, offset is considered positive */
   if (offset < 0)
     return NULL;
 
-  if (0 != _sos_mmap(& start, length, prot, flags, path, offset))
+  if (0 != _sos_fakemmap(& start, length, prot, flags, path, offset))
     return NULL;
 
   return start;
@@ -102,5 +103,139 @@
   retval = malloc_heap_top;
   malloc_heap_top += size;
 
+  _sos_brk(malloc_heap_top);
   return retval;
 }
+
+
+void free(void *ptr)
+{
+  //  bochs_printf("Free ignored (not implemented yet)\n");
+}
+
+
+int open(const char *pathname, int flags, /* mode_t mode */...)
+{
+  va_list ap;
+  unsigned int mode = 0;
+ 
+  va_start(ap, flags);
+  if (flags & O_CREAT)
+    mode = va_arg(ap, unsigned int);
+  va_end(ap);
+ 
+  return _sos_open(pathname, flags, mode);
+}
+
+
+int read(int fd, char * buf, size_t len)
+{
+  int retval = _sos_read(fd, buf, & len);
+  if (retval < 0)
+    return retval;
+  return len;
+}
+
+
+int write(int fd, const char * buf, size_t len)
+{
+  int retval = _sos_write(fd, buf, & len);
+  if (retval < 0)
+    return retval;
+  return len;
+}
+
+
+off_t lseek(int fd, off_t offset, int whence)
+{
+  loff_t result = offset;
+  int retval = _sos_seek64(fd, & result, whence);
+  if (retval < 0)
+    return retval;
+  return result;
+}
+
+
+loff_t lseek64(int fd, loff_t offset, int whence)
+{
+  loff_t result = offset;
+  int retval = _sos_seek64(fd, & result, whence);
+  if (retval < 0)
+    return retval;
+  return result;
+}
+
+
+void * mmap(void *start, size_t length, int prot , int flags,
+	    int fd, loff_t offset)
+{
+  /* At kernel side, offset is considered positive */
+  if (offset < 0)
+    return NULL;
+
+  if (0 != _sos_fmmap(& start, length, prot, flags, fd, offset))
+    return NULL;
+
+  return start;
+}
+
+
+int ftruncate(int fd, off_t length)
+{
+  return _sos_ftruncate64(fd, length);
+}
+
+
+struct sos_DIR_struct
+{
+  int fd;
+  struct dirent dirent;
+};
+
+
+DIR *opendir(const char *name)
+{
+  DIR * result = malloc(sizeof(DIR));
+  if (! result)
+    return NULL;
+
+  result->fd = _sos_open(name, O_DIRECTORY | O_RDONLY, 0);
+  return result;
+}
+
+
+int dirfd(const DIR * dir)
+{
+  if (dir)
+    return dir->fd;
+  return -1;
+}
+
+
+struct dirent *readdir(DIR *dir)
+{
+  int retval = _sos_readdir(dir->fd, & dir->dirent);
+  if (retval < 0)
+    return NULL;
+  return & dir->dirent;
+}
+
+
+int closedir(DIR *dir)
+{
+  close(dir->fd);
+  free(dir);
+  return 0;
+}
+
+
+int stat(const char *file_name, struct stat *buf)
+{
+  return _sos_stat(file_name, TRUE, buf);
+}
+
+
+int lstat(const char *file_name, struct stat *buf)
+{
+  return _sos_stat(file_name, FALSE, buf);
+}
diff -ruN /tmp/sos-code-article7.5/userland/libc.h ../sos-code-article8/userland/libc.h
--- /tmp/sos-code-article7.5/userland/libc.h	2005-04-27 20:17:19.000000000 +0200
+++ ../sos-code-article8/userland/libc.h	2005-07-01 16:39:50.000000000 +0200
@@ -30,22 +30,29 @@
 /**
  * The most important function of a C program ! ;)
  */
-void exit (int status) __attribute__((noreturn, alias("_sos_exit")));
+void exit (int status)
+     __attribute__((noreturn, alias("_sos_exit")));
 
 
 /**
  * Function to duplicate the current running process
  */
-pid_t fork(void)  __attribute__ ((alias("_sos_fork")));
+pid_t fork(void)
+     __attribute__ ((alias("_sos_fork")));
 
 
 /**
  * Function to re-initialize the address space of the current process
  * with that of the program 'progname'
  */
-int exec(const char *progname) __attribute__ ((alias("_sos_exec")));
+int exec(const char *progname)
+     __attribute__ ((alias("_sos_exec")));
 
 
+int nanosleep(unsigned long int sec,
+	      unsigned long int nanosec)
+     __attribute__ ((alias("_sos_nanosleep")));
+
 /**
  * These flags (for mmap API) MUST be consistent with those defined in
  * paging.h and umem_vmm.h
@@ -64,20 +71,22 @@
  *  - the offset is a signed 64bits, and MUST be >= 0 (error otherwise)
  *  - no errno support
  */
-void * mmap(void *start, size_t length, int prot , int flags,
-	    const char *path, loff_t offset);
+void * fakemmap(void *start, size_t length, int prot , int flags,
+		const char *path, loff_t offset);
 
 
 /**
  * Unmap the given user address interval
  */
-int munmap(void * start, size_t length) __attribute__ ((alias("_sos_munmap")));
+int munmap(void * start, size_t length)
+     __attribute__ ((alias("_sos_munmap")));
 
 
 /**
  * Change the access permissions of the given user address interval
  */
-int mprotect(const void *addr, size_t len, int prot) __attribute__ ((alias("_sos_mprotect")));
+int mprotect(const void *addr, size_t len, int prot)
+     __attribute__ ((alias("_sos_mprotect")));
 
 
 
@@ -110,5 +119,138 @@
 /** @note MT UNSAFE */
 void * malloc (size_t size);
 
+/** @note Does nothing (not implemented yet) */
+void free(void *ptr);
+
+
+/*
+ * Filesystem subsystem
+ */
+
+int mount(const char *source, const char *target,
+	  const char *filesystemtype, unsigned long mountflags,
+	  const char *data)
+     __attribute__ ((alias("_sos_mount")));
+int umount(const char *target)
+     __attribute__ ((alias("_sos_umount")));
+void sync(void)
+     __attribute__ ((alias("_sos_sync")));
+
+struct statvfs
+{
+  size_t        f_sz_total;  /**< Total size */
+  size_t        f_sz_free;   /**< Size left on device */
+  unsigned int  f_node_total;/**< Total allocatable FS nodes */
+  unsigned int  f_node_avail;/**< Number of available free FS nodes */
+  unsigned int  f_flags;
+};
+
+int statvfs(const char *path, struct statvfs *buf)
+     __attribute__ ((alias("_sos_statvfs")));
+
+#define O_EXCL      (1 << 0)
+#define O_CREAT     (1 << 1)
+#define O_NOFOLLOW  (1 << 2)
+#define O_DIRECTORY (1 << 3) /* Incompatible with CREAT */
+#define O_SYNC      (1 << 4)
+
+#define O_RDONLY    (1 << 16)
+#define O_WRONLY    (1 << 17)
+#define O_RDWR      (O_RDONLY | O_WRONLY)
+
+/**
+ * FS access rights. Same as in kernel fs.h
+ */
+#define S_IRUSR     00400
+#define S_IWUSR     00200
+#define S_IXUSR     00100
+#define S_IRWXALL   07777 /* For symlinks */
+
+int open(const char *pathname, int flags, /* mode_t mode */...);
+
+int close(int fd)
+     __attribute__ ((alias("_sos_close")));
+
+int read(int fd, char * buf, size_t len);
+int write(int fd, const char * buf, size_t len);
+
+/* Same as sos_seek_whence_t in kernel fs.h */
+#define SEEK_SET 42
+#define SEEK_CUR 24
+#define SEEK_END 84
+off_t lseek(int fd, off_t offset, int whence);
+loff_t lseek64(int fd, loff_t offset, int whence);
+void * mmap(void *start, size_t length, int prot , int flags,
+	    int fd, loff_t offset);
+int ftruncate(int fd, off_t length);
+int ftruncate64(int fd, loff_t length)
+     __attribute__ ((alias("_sos_ftruncate64")));
+int fcntl(int fd, int cmd, int arg)
+     __attribute__ ((alias("_sos_fcntl")));
+
+int creat(const char *pathname, mode_t mode)
+     __attribute__ ((alias("_sos_creat")));
+int link (const char *oldpath, const char *newpath)
+     __attribute__ ((alias("_sos_link")));
+int unlink(const char *pathname)
+     __attribute__ ((alias("_sos_unlink")));
+int rename(const char *oldpath, const char *newpath)
+     __attribute__ ((alias("_sos_rename")));
+int symlink(const char *target, const char *path)
+     __attribute__ ((alias("_sos_symlink")));
+
+int mkdir(const char *pathname, mode_t mode)
+     __attribute__ ((alias("_sos_mkdir")));
+int rmdir(const char *pathname)
+     __attribute__ ((alias("_sos_rmdir")));
+
+int chmod(const char *path, mode_t mode)
+     __attribute__ ((alias("_sos_chmod")));
+
+struct dirent
+{
+  unsigned long long int storage_location;
+  unsigned long long int offset_in_dirfile;
+
+/* Same as sos_fs_node_type_t in kernel fs.h */
+#define S_IFREG 0x42
+#define S_IFDIR 0x24
+#define S_IFLNK 0x84
+  unsigned int type;
+  unsigned short namelen;
+
+#define SOS_FS_DIRENT_NAME_MAXLEN 128
+  char       name[SOS_FS_DIRENT_NAME_MAXLEN];
+};
+
+/* Forward declaration (defined in libc.c) */
+struct sos_DIR_struct;
+#define DIR struct sos_DIR_struct
+
+DIR *opendir(const char *name);
+int dirfd(const DIR * dir);
+struct dirent *readdir(DIR *dir);
+int closedir(DIR *dir);
+
+struct stat
+{
+  int                    st_type;
+  unsigned long long int st_storage_location;
+  int                    st_access_rights;
+  unsigned int           st_nlink;
+  signed long long int   st_size;
+};
+
+int stat(const char *file_name, struct stat *buf);
+int lstat(const char *file_name, struct stat *buf);
+
+int chroot(const char *path)
+     __attribute__ ((alias("_sos_chroot")));
+int chdir(const char *path)
+     __attribute__ ((alias("_sos_chdir")));
+int fchdir(int fd)
+     __attribute__ ((alias("_sos_fchdir")));
+
+
 
 #endif /* _SOS_USER_LIBC_H_ */
diff -ruN /tmp/sos-code-article7.5/userland/myprog10.c ../sos-code-article8/userland/myprog10.c
--- /tmp/sos-code-article7.5/userland/myprog10.c	2005-04-27 20:17:20.000000000 +0200
+++ ../sos-code-article8/userland/myprog10.c	2005-07-01 16:39:50.000000000 +0200
@@ -34,10 +34,10 @@
 {
   char * zoup;
 
-  zoup = mmap((void*)4096, 8*1024*1024,
-	      PROT_READ | PROT_WRITE,
-	      MAP_SHARED,
-	      "/dev/zero", 34);
+  zoup = fakemmap((void*)4096, 8*1024*1024,
+		  PROT_READ | PROT_WRITE,
+		  MAP_SHARED,
+		  "/dev/zero", 34);
   bochs_printf("mapped @%x\n", (unsigned)zoup);
 
   /* Do some forks to complicate things */
diff -ruN /tmp/sos-code-article7.5/userland/myprog11.c ../sos-code-article8/userland/myprog11.c
--- /tmp/sos-code-article7.5/userland/myprog11.c	2005-04-27 20:17:20.000000000 +0200
+++ ../sos-code-article8/userland/myprog11.c	2005-07-01 16:39:50.000000000 +0200
@@ -37,10 +37,10 @@
   void * moved;
   char * zoup;
 
-  zoup = mmap((void*)4096, 8*1024*1024,
-	      PROT_READ | PROT_WRITE,
-	      MAP_SHARED,
-	      "/dev/zero", 34);
+  zoup = fakemmap((void*)4096, 8*1024*1024,
+		  PROT_READ | PROT_WRITE,
+		  MAP_SHARED,
+		  "/dev/zero", 34);
   bochs_printf("mapped @%x\n", (unsigned)zoup);
 
   /* Do some forks to complicate things */
diff -ruN /tmp/sos-code-article7.5/userland/myprog12.c ../sos-code-article8/userland/myprog12.c
--- /tmp/sos-code-article7.5/userland/myprog12.c	2005-04-27 20:17:20.000000000 +0200
+++ ../sos-code-article8/userland/myprog12.c	2005-07-01 16:39:50.000000000 +0200
@@ -24,7 +24,7 @@
 
 
 /**
- * @file myprog10.c
+ * @file myprog12.c
  *
  * /dev/mem & /dev/kmem tests
  */
@@ -38,9 +38,9 @@
 
   /* Map the x86 text framebuffer into this process address space in
      shared mode */
-  zoup = mmap(0, 4096, PROT_READ | PROT_WRITE,
-	      MAP_SHARED,
-	      "/dev/mem", 0xb8000);
+  zoup = fakemmap(0, 4096, PROT_READ | PROT_WRITE,
+		  MAP_SHARED,
+		  "/dev/mem", 0xb8000);
   bochs_printf("mapped mem @%x\n", (unsigned)zoup);  
   _sos_syscall1(4004, (unsigned)"Apres mmap video");
 
@@ -55,9 +55,9 @@
 
   /* Map the x86 text framebuffer into this process address space in
      PRIVATE mode */
-  zoup = mmap(0, 4096, PROT_READ | PROT_WRITE,
-	      0 /* Private */,
-	      "/dev/mem", 0xb8000);
+  zoup = fakemmap(0, 4096, PROT_READ | PROT_WRITE,
+		  0 /* Private */,
+		  "/dev/mem", 0xb8000);
   bochs_printf("mapped mem @%x\n", (unsigned)zoup);  
   _sos_syscall1(4004, (unsigned)"Apres mmap video");
 
@@ -77,9 +77,9 @@
      "private" mode here, this way we can do whatever we like in this
      area. If we'd mapped it read/write, this would overwrite kernel
      data/code, causing a crash sooner or later. */
-  zoup = mmap(0, 100*4096, PROT_READ | PROT_WRITE,
-	      0 /* private */,
-	      "/dev/kmem", 0x00201000);
+  zoup = fakemmap(0, 100*4096, PROT_READ | PROT_WRITE,
+		  0 /* private */,
+		  "/dev/kmem", 0x00201000);
   bochs_printf("mapped kmem @%x\n", (unsigned)zoup);  
   _sos_syscall1(4004, (unsigned)"Apres mmap kernel");
 
diff -ruN /tmp/sos-code-article7.5/userland/myprog14.c ../sos-code-article8/userland/myprog14.c
--- /tmp/sos-code-article7.5/userland/myprog14.c	2005-04-27 20:17:19.000000000 +0200
+++ ../sos-code-article8/userland/myprog14.c	2005-07-01 16:39:50.000000000 +0200
@@ -39,10 +39,10 @@
 {
   char * zoup;
 
-  zoup = mmap(0, 8192,
-	      PROT_READ | PROT_WRITE,
-	      MAP_SHARED,
-	      "/dev/zero", 34);
+  zoup = fakemmap(0, 8192,
+		  PROT_READ | PROT_WRITE,
+		  MAP_SHARED,
+		  "/dev/zero", 34);
   bochs_printf("mapped @%x\n", (unsigned)zoup);
 
   /* Do some forks to complicate things */
diff -ruN /tmp/sos-code-article7.5/userland/myprog8.c ../sos-code-article8/userland/myprog8.c
--- /tmp/sos-code-article7.5/userland/myprog8.c	2005-04-27 20:17:20.000000000 +0200
+++ ../sos-code-article8/userland/myprog8.c	2005-07-01 16:39:50.000000000 +0200
@@ -32,10 +32,10 @@
   int i;
   char *zoup;
 
-  zoup = mmap((void*)4096, 8*1024*1024,
-	      PROT_READ | PROT_WRITE,
-	      MAP_SHARED,
-	      "/dev/zero", 0x123456789012345ULL);
+  zoup = fakemmap((void*)4096, 8*1024*1024,
+		  PROT_READ | PROT_WRITE,
+		  MAP_SHARED,
+		  "/dev/zero", 0x123456789012345ULL);
   bochs_printf("mapped @%x\n", (unsigned)zoup);
 
   switch (fork())
diff -ruN /tmp/sos-code-article7.5/userland/myprog9.c ../sos-code-article8/userland/myprog9.c
--- /tmp/sos-code-article7.5/userland/myprog9.c	2005-04-27 20:17:20.000000000 +0200
+++ ../sos-code-article8/userland/myprog9.c	2005-07-01 16:39:50.000000000 +0200
@@ -22,7 +22,7 @@
 #include <debug.h>
 
 /**
- * @file myprog7.c
+ * @file myprog9.c
  *
  * mmap/munmap tests
  *
@@ -34,10 +34,10 @@
 {
   char *zoup;
 
-  zoup = mmap((void*)4096, 8*1024*1024,
-	      PROT_READ | PROT_WRITE,
-	      MAP_SHARED,
-	      "/dev/zero", 34);
+  zoup = fakemmap((void*)4096, 8*1024*1024,
+		  PROT_READ | PROT_WRITE,
+		  MAP_SHARED,
+		  "/dev/zero", 34);
   bochs_printf("mapped @%x\n", (unsigned)zoup);
 
   /* Do a fork() here to complicate things */
diff -ruN /tmp/sos-code-article7.5/userland/types.h ../sos-code-article8/userland/types.h
--- /tmp/sos-code-article7.5/userland/types.h	2005-04-27 20:17:20.000000000 +0200
+++ ../sos-code-article8/userland/types.h	2005-07-01 16:39:50.000000000 +0200
@@ -27,10 +27,22 @@
 
 typedef int pid_t;
 typedef int ptrdiff_t;
+typedef signed long int off_t;
 typedef signed long long int loff_t;
 
+typedef unsigned int mode_t;
+typedef unsigned long long int dev_t;
+
 #ifndef NULL
 #  define NULL ((void*)0)
 #endif
 
+#ifndef TRUE
+#  define TRUE (!0)
+#endif
+
+#ifndef FALSE
+#  define FALSE (!TRUE)
+#endif
+
 #endif /* _SOS_LIBC_TYPES_H_ */
