diff -ruN /tmp/sos-code-article9.5/drivers/mem.c sos-code-article9.5/drivers/mem.c
--- /tmp/sos-code-article9.5/drivers/mem.c	2006-01-22 12:48:28.000000000 +0100
+++ sos-code-article9.5/drivers/mem.c	2006-03-19 12:21:04.000000000 +0100
@@ -91,7 +91,7 @@
     + sos_umem_vmm_get_offset_in_resource(vr);
 
   /* Don't allow demand paging of non kernel pages */
-  if (vaddr >= SOS_PAGING_BASE_USER_ADDRESS)
+  if (! SOS_PAGING_IS_KERNEL_AREA(vaddr, 1))
     return -SOS_EFAULT;
 
   /* Lookup physical kernel page */
diff -ruN /tmp/sos-code-article9.5/extra/bootsect.S sos-code-article9.5/extra/bootsect.S
--- /tmp/sos-code-article9.5/extra/bootsect.S	2006-01-22 12:48:28.000000000 +0100
+++ sos-code-article9.5/extra/bootsect.S	2006-03-19 12:21:00.000000000 +0100
@@ -1,6 +1,6 @@
 
 /*
- * @(#) $Id: bootsect.S,v 1.12 2005/08/13 13:47:31 d2 Exp $
+ * @(#) $Id: bootsect.S 1310 2005-08-13 13:47:31Z d2 $
  * Description : Bootsecteur en syntaxe AT&T
  * Auteurs : Thomas Petazzoni & Fabrice Gautier & Emmanuel Marty
  *	     Jerome Petazzoni & Bernard Cassagne & coffeeman
diff -ruN /tmp/sos-code-article9.5/hwcore/bitmap.c sos-code-article9.5/hwcore/bitmap.c
--- /tmp/sos-code-article9.5/hwcore/bitmap.c	1970-01-01 01:00:00.000000000 +0100
+++ sos-code-article9.5/hwcore/bitmap.c	2006-03-19 12:21:02.000000000 +0100
@@ -0,0 +1,177 @@
+/* Copyright (C) 2006 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/macros.h>
+
+#include "bitmap.h"
+
+
+/**
+ * ffs: find first bit set (1 = LSb, 32 = MSb).
+ *
+ * @return 0 when none found
+ *
+ * @note: License is GPLv2, origin is Linux Kernel 2.6.14.3
+ * (@see include/asm-i386/bitops.h)
+ */
+static inline int word_ffs(sos_ui32_t x)
+{
+        if (!x)
+                return 0;
+
+        __asm__("bsfl %1,%0"
+                :"=r" (x)
+                :"rm" (x));
+        return x+1;
+}
+
+
+/** Set a 32bits mask of nbits '1' starting at LSb */
+static inline sos_ui32_t generate_lsb_mask(int nbits)
+{
+  return (1 << nbits) - 1;
+}
+
+
+sos_size_t sos_bitmap_ffs(const void * area,
+			  sos_size_t bit_length,
+			  sos_size_t _start_bit_index)
+{
+  sos_size_t start_bit_index = _start_bit_index - 1;
+  sos_size_t word_index      = start_bit_index >> 5;
+  const sos_ui32_t * word    = (const sos_ui32_t*)area;
+  sos_ui32_t mask = ~ generate_lsb_mask(start_bit_index & 31);
+
+  while (start_bit_index < bit_length)
+    {
+      sos_size_t fsb = word_ffs(word[word_index] & mask);
+
+      if (fsb > 0)
+	{
+	  sos_size_t bit_index = SOS_ALIGN_INF(start_bit_index, 32) + fsb;
+	  if (bit_index > bit_length)
+	    return 0;
+	  
+	  return bit_index;
+	}
+
+      start_bit_index = SOS_ALIGN_INF(start_bit_index, 32) + 32;
+      word_index ++;
+      mask = ~0;
+    }
+
+  return 0;
+}
+
+
+sos_size_t sos_bitmap_ffc(const void * area,
+			  sos_size_t bit_length,
+			  sos_size_t _start_bit_index)
+{
+  sos_size_t start_bit_index = _start_bit_index - 1;
+  sos_size_t word_index      = start_bit_index >> 5;
+  const sos_ui32_t * word    = (const sos_ui32_t*)area;
+  sos_ui32_t mask = ~ generate_lsb_mask(start_bit_index & 31);
+
+  while (start_bit_index < bit_length)
+    {
+      sos_size_t fcb = word_ffs((~ word[word_index]) & mask);
+
+      if (fcb > 0)
+	{
+	  sos_size_t bit_index = SOS_ALIGN_INF(start_bit_index, 32) + fcb;
+	  if (bit_index > bit_length)
+	    return 0;
+	  
+	  return bit_index;
+	}
+
+      start_bit_index = SOS_ALIGN_INF(start_bit_index, 32) + 32;
+      word_index ++;
+      mask = ~0;
+    }
+
+  return 0;
+}
+
+
+#undef ADDR
+#define ADDR (*(volatile long *) addr)
+
+/**
+ * test_and_set_bit - Set a bit and return its old value
+ * @param nr Bit to set (starting at 0 !)
+ * @param addr Address to count from
+ *
+ * @note: License is GPLv2, origin is Linux Kernel 2.6.14.3
+ * (@see include/asm-i386/bitops.h)
+ */
+static inline int test_and_set_bit(int nr, volatile unsigned long * addr)
+{
+        int oldbit;
+
+        __asm__ __volatile__(
+                "btsl %2,%1\n\tsbbl %0,%0"
+                :"=r" (oldbit),"=m" (ADDR)
+                :"Ir" (nr) : "memory");
+        return oldbit;
+}
+
+
+/**
+ * test_and_clear_bit - Clear a bit and return its old value
+ * @param nr Bit to clear (starting at 0 !)
+ * @param addr Address to count from
+ *
+ * @note: License is GPLv2, origin is Linux Kernel 2.6.14.3
+ * (@see include/asm-i386/bitops.h)
+ */
+static inline int test_and_clear_bit(int nr, volatile unsigned long * addr)
+{
+        int oldbit;
+
+        __asm__ __volatile__(
+                "btrl %2,%1\n\tsbbl %0,%0"
+                :"=r" (oldbit),"=m" (ADDR)
+                :"Ir" (nr) : "memory");
+        return oldbit;
+}
+
+
+sos_bool_t sos_bitmap_test_and_set(void * area,
+				   sos_size_t _bit_index)
+{
+  sos_ui32_t * word     = (sos_ui32_t*)area;
+  sos_size_t bit_index  = _bit_index - 1;
+  sos_size_t word_index = bit_index >> 5;
+
+  bit_index &= 31;
+  return test_and_set_bit(bit_index, word + word_index);
+}
+
+
+sos_bool_t sos_bitmap_test_and_clear(void * area,
+				     sos_size_t _bit_index)
+{
+  sos_ui32_t * word     = (sos_ui32_t*)area;
+  sos_size_t bit_index  = _bit_index - 1;
+  sos_size_t word_index = bit_index >> 5;
+
+  bit_index &= 31;
+  return test_and_clear_bit(bit_index, word + word_index);
+}
diff -ruN /tmp/sos-code-article9.5/hwcore/bitmap.h sos-code-article9.5/hwcore/bitmap.h
--- /tmp/sos-code-article9.5/hwcore/bitmap.h	1970-01-01 01:00:00.000000000 +0100
+++ sos-code-article9.5/hwcore/bitmap.h	2006-03-19 12:21:02.000000000 +0100
@@ -0,0 +1,79 @@
+/* Copyright (C) 2006 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_BITMAP_H_
+#define _SOS_BITMAP_H_
+
+
+/**
+ * @file bitmap.h
+ *
+ * Bitmap manipulation. The implementation of this API is
+ * architecture-dependent, hence its location in hwcore/
+ */
+
+#include <sos/types.h>
+#include <sos/errno.h>
+
+
+/**
+ * Return index (start at start_bit_index) of first bit set
+ * @return 0 < x <= bit_length when found, 0 when none found
+ * @note in-memory size of area MUST be a multiple of 4B (32bits) !
+ *
+ * @note not atomic
+ */
+sos_size_t sos_bitmap_ffs(const void * area,
+			  sos_size_t bit_length,
+			  sos_size_t start_bit_index /* start at 1 */);
+
+
+/**
+ * Return index (start at start_bit_index) of first bit clear
+ * @return 0 < x <= bit_length when found, 0 when none found
+ * @note in-memory size of area MUST be a multiple of 4B (32bits) !
+ *
+ * @note not atomic
+ */
+sos_size_t sos_bitmap_ffc(const void * area,
+			  sos_size_t bit_length,
+			  sos_size_t start_bit_index /* start at 1 */);
+
+
+/**
+ * Set bit at index (start at 1)
+ * @return old value of the bit
+ *
+ * @note atomic
+ */
+sos_bool_t
+sos_bitmap_test_and_set(void * area,
+			sos_size_t bit_index /* start at 1 */);
+
+
+/**
+ * Clear bit at index (start at 1)
+ * @return old value of the bit
+ *
+ * @note atomic
+ */
+sos_bool_t
+sos_bitmap_test_and_clear(void * area,
+			  sos_size_t bit_index /* start at 1 */);
+
+
+#endif /* _SOS_BITMAP_H_ */
diff -ruN /tmp/sos-code-article9.5/hwcore/paging.c sos-code-article9.5/hwcore/paging.c
--- /tmp/sos-code-article9.5/hwcore/paging.c	2006-01-22 12:48:30.000000000 +0100
+++ sos-code-article9.5/hwcore/paging.c	2006-03-19 12:21:02.000000000 +0100
@@ -954,7 +954,7 @@
 {
   SOS_ASSERT_FATAL(SOS_IS_PAGE_ALIGNED(base_address));
   SOS_ASSERT_FATAL(SOS_IS_PAGE_ALIGNED(length));
-  SOS_ASSERT_FATAL(SOS_PAGING_BASE_USER_ADDRESS <= base_address);
+  SOS_ASSERT_FATAL(SOS_PAGING_IS_USER_AREA(base_address, length));
 
   /* Mark all the pages read-only, when already mapped in physical
      memory */
diff -ruN /tmp/sos-code-article9.5/hwcore/paging.h sos-code-article9.5/hwcore/paging.h
--- /tmp/sos-code-article9.5/hwcore/paging.h	2006-01-22 12:48:30.000000000 +0100
+++ sos-code-article9.5/hwcore/paging.h	2006-03-19 12:21:02.000000000 +0100
@@ -40,11 +40,18 @@
 
 /**
  * Basic SOS virtual memory organization
- */
-/** Frontier between kernel and user space virtual addresses */
-#define SOS_PAGING_BASE_USER_ADDRESS (0x40000000) /* 1GB (must be 4MB-aligned) */
-#define SOS_PAGING_TOP_USER_ADDRESS  (0xFFFFFFFF) /* 4GB - 1B */
-#define SOS_PAGING_USER_SPACE_SIZE   (0xc0000000) /* 3GB */
+ *
+ * - Kernel space starts at the lower 4kB address (first page is not
+ *   mapped in order to catch invalid pointers)
+ * - User space starts at the 1GB address
+ */
+#define SOS_PAGING_BASE_USER_ADDRESS   (0x40000000) /* 1GB (must be 4MB-aligned) */
+#define SOS_PAGING_UPPER_USER_ADDRESS  (0xFFFFFFFF) /* 4GB - 1B */
+#define SOS_PAGING_USER_SPACE_SIZE     (SOS_PAGING_UPPER_USER_ADDRESS - \
+                                        SOS_PAGING_BASE_USER_ADDRESS + 1) /* 3GB */
+#define SOS_PAGING_IS_USER_AREA(base_addr, length) \
+  ( (SOS_PAGING_BASE_USER_ADDRESS <= (base_addr))  \
+      && ((length) <= SOS_PAGING_USER_SPACE_SIZE) )
 
 /** Length of the space reserved for the mirroring in the kernel
     virtual space */
@@ -54,6 +61,14 @@
 #define SOS_PAGING_MIRROR_VADDR \
    (SOS_PAGING_BASE_USER_ADDRESS - SOS_PAGING_MIRROR_SIZE)
 
+#define SOS_PAGING_BASE_KERNEL_ADDRESS  (0x00004000) /* 16kB */
+#define SOS_PAGING_UPPER_KERNEL_ADDRESS (SOS_PAGING_MIRROR_VADDR - 1) /* 1GB - 4MB - 1B */
+#define SOS_PAGING_KERNEL_SPACE_SIZE    (SOS_PAGING_UPPER_KERNEL_ADDRESS - \
+                                         SOS_PAGING_BASE_KERNEL_ADDRESS + 1) /* 1GB - 4MB - 16kB */
+#define SOS_PAGING_IS_KERNEL_AREA(base_addr, length) \
+  ( (SOS_PAGING_BASE_KERNEL_ADDRESS <= (base_addr))  \
+      && ((length) <= SOS_PAGING_KERNEL_SPACE_SIZE) )
+
 
 /**
  * sos_paging_map flags
diff -ruN /tmp/sos-code-article9.5/Makefile sos-code-article9.5/Makefile
--- /tmp/sos-code-article9.5/Makefile	2006-01-22 12:48:32.000000000 +0100
+++ sos-code-article9.5/Makefile	2006-03-19 12:21:04.000000000 +0100
@@ -22,30 +22,30 @@
 CFLAGS  = -Wall -nostdinc -ffreestanding -DKERNEL_SOS -O -g
 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				\
-	  hwcore/cpu_context.o hwcore/cpu_context_switch.o		\
-	  hwcore/mm_context.o						\
-	  sos/kmem_vmm.o sos/kmem_slab.o sos/kmalloc.o			\
-	  sos/physmem.o sos/klibc.o					\
-	  sos/thread.o sos/kwaitq.o					\
-          sos/time.o sos/sched.o sos/ksynch.o				\
-	  sos/process.o sos/syscall.o					\
-          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					\
-	  drivers/ide.o drivers/kbd.o drivers/kbdmapfr.o		\
-	  drivers/serial.o drivers/tty.o drivers/part.o	\
-	  drivers/console.o						\
-	  sos/hash.o sos/fs.o sos/fs_nscache.o				\
-	  drivers/fs_virtfs.o sos/chardev.o sos/blkdev.o		\
-	  sos/blkcache.o sos/fs_pagecache.o				\
+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			\
+	  hwcore/cpu_context.o hwcore/cpu_context_switch.o	\
+	  hwcore/mm_context.o hwcore/bitmap.o			\
+	  sos/kmem_vmm.o sos/kmem_slab.o sos/kmalloc.o		\
+	  sos/physmem.o sos/klibc.o				\
+	  sos/thread.o sos/kwaitq.o				\
+          sos/time.o sos/sched.o sos/ksynch.o			\
+	  sos/process.o sos/syscall.o				\
+          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				\
+	  drivers/ide.o drivers/kbd.o drivers/kbdmapfr.o	\
+	  drivers/serial.o drivers/tty.o drivers/part.o		\
+	  drivers/console.o					\
+	  sos/hash.o sos/fs.o sos/fs_nscache.o			\
+	  drivers/fs_virtfs.o sos/chardev.o sos/blkdev.o	\
+	  sos/blkcache.o sos/fs_pagecache.o			\
           userland/userprogs.kimg
 
 KERNEL_OBJ   = sos.elf
diff -ruN /tmp/sos-code-article9.5/sos/binfmt_elf32.c sos-code-article9.5/sos/binfmt_elf32.c
--- /tmp/sos-code-article9.5/sos/binfmt_elf32.c	2006-01-22 12:48:30.000000000 +0100
+++ sos-code-article9.5/sos/binfmt_elf32.c	2006-03-19 12:21:03.000000000 +0100
@@ -386,7 +386,8 @@
 	  continue;
 	}
       
-      if (elf_phdrs[i].p_vaddr < SOS_PAGING_BASE_USER_ADDRESS)
+      if (! SOS_PAGING_IS_USER_AREA(elf_phdrs[i].p_vaddr,
+				    elf_phdrs[i].p_memsz) )
 	{
 	  sos_display_fatal_error("User program has an incorrect address");
 	}
diff -ruN /tmp/sos-code-article9.5/sos/errno.h sos-code-article9.5/sos/errno.h
--- /tmp/sos-code-article9.5/sos/errno.h	2006-01-22 12:48:30.000000000 +0100
+++ sos-code-article9.5/sos/errno.h	2006-03-19 12:21:03.000000000 +0100
@@ -31,22 +31,23 @@
 #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_ENOSYS       20   /* Operation not implemented */
-#define SOS_EIO          21   /* Input/output error */
+#define SOS_EAGAIN        6   /* Temporary resource exhaustion */
+#define SOS_EPERM         7   /* Mutex/files ownership error */
+#define SOS_EFAULT        8   /* Unresolved virtual memory fault */
+#define SOS_ENOENT        9   /* No such file or directory */
+#define SOS_ELOOP        10   /* symlink resolution loop / too recursive */
+#define SOS_EEXIST       11   /* File already exists */
+#define SOS_EACCES       12   /* Permission denied */
+#define SOS_ENOTDIR      13   /* Dir does not exist */
+#define SOS_ENAMETOOLONG 14
+#define SOS_EXDEV        15   /* Cannot link entries across different FS */
+#define SOS_EISDIR       16   /* Directories not allowed in operation */
+#define SOS_ENOTEMPTY    17
+#define SOS_ENODEV       18   /* No such device */
+#define SOS_EBADF        19   /* Bad file descriptor */
+#define SOS_EMFILE       20   /* Reached maximal opened file for process */
+#define SOS_ENOSYS       21   /* Operation not implemented */
+#define SOS_EIO          22   /* Input/output error */
 #define SOS_EFATAL      255 /* Internal fatal error */
 
 /* A negative value means that an error occured.  For
diff -ruN /tmp/sos-code-article9.5/sos/fs.c sos-code-article9.5/sos/fs.c
--- /tmp/sos-code-article9.5/sos/fs.c	2006-01-22 12:48:30.000000000 +0100
+++ sos-code-article9.5/sos/fs.c	2006-03-19 12:21:03.000000000 +0100
@@ -22,6 +22,7 @@
 #include <sos/list.h>
 #include <sos/kmem_slab.h>
 #include <sos/kmalloc.h>
+#include <sos/syscall.h> /* For the FCNTL commands */
 #include "chardev.h"
 
 #include "fs.h"
@@ -1221,7 +1222,7 @@
 		       sos_ui32_t req_arg /* Usually: sos_uaddr_t */)
 {
   if (! of->ops_file->fcntl)
-    return -SOS_ENOSYS;
+    return sos_fs_basic_fcntl_helper(of, req_id, req_arg);
 
   return of->ops_file->fcntl(of, req_id, req_arg);
 }
@@ -1950,6 +1951,60 @@
 }
 
 
+/* *************************************************************
+ * client FS helper functions
+ */
+sos_ret_t sos_fs_basic_fcntl_helper(struct sos_fs_opened_file * of,
+				    int req_id, sos_ui32_t req_arg)
+{
+  sos_ret_t result = -SOS_ENOSUP;
+
+  switch(req_id)
+    {
+    case SOS_FCNTL_DUPFD:
+      {
+	result = sos_fs_ref_opened_file(of);
+	if (SOS_OK != result)
+	  break;
+
+	result = sos_process_register_opened_file((struct sos_process*)of->owner,
+						  of, req_arg);
+      }
+      break;
+
+    case SOS_FCNTL_GETFD:
+      {
+	result = of->open_flags & SOS_FS_OPEN_CLOSEONEXEC;
+      }
+      break;
+
+    case SOS_FCNTL_SETFD:
+      {
+	of->open_flags &= ~SOS_FS_OPEN_CLOSEONEXEC;
+	of->open_flags |= (req_arg & SOS_FS_OPEN_CLOSEONEXEC);
+	result = SOS_OK;
+      }
+      break;
+
+    case SOS_FCNTL_GETFL:
+      {
+	result = of->open_flags;
+      }
+      break;
+
+    case SOS_FCNTL_SETFL:
+      {
+	/* Not supported */
+      }
+      break;
+
+    default:
+      break;
+    }
+
+  return result;
+}
+
 
 /* *************************************************************
  * mount/umount stuff
diff -ruN /tmp/sos-code-article9.5/sos/fs.h sos-code-article9.5/sos/fs.h
--- /tmp/sos-code-article9.5/sos/fs.h	2006-01-22 12:48:30.000000000 +0100
+++ sos-code-article9.5/sos/fs.h	2006-03-19 12:21:03.000000000 +0100
@@ -1123,6 +1123,18 @@
 				       const struct sos_process * dst_proc,
 				       struct sos_fs_opened_file ** result_of);
 
-
+/**
+ * Generic fcntl function that can be called from inside the FS's
+ * fcntl method. It will handle the following fcntl commands:
+ *    - F_DUPFD
+ *    - F_GETFD
+ *    - F_SETFD
+ *    - F_GETFL
+ *    - F_SETFL
+ * Any other command will lead to a -SOS_ENOSUP return value.
+ * It will take care not to change anything besides the fs.h structures.
+ */
+sos_ret_t sos_fs_basic_fcntl_helper(struct sos_fs_opened_file * src_of,
+				    int req_id, sos_ui32_t req_arg);
 
 #endif /* _SOS_FS_H_ */
diff -ruN /tmp/sos-code-article9.5/sos/hash.c sos-code-article9.5/sos/hash.c
--- /tmp/sos-code-article9.5/sos/hash.c	2006-01-22 12:48:31.000000000 +0100
+++ sos-code-article9.5/sos/hash.c	2006-03-19 12:21:03.000000000 +0100
@@ -66,6 +66,9 @@
 
   /** Number of buckets in this hash table */
   sos_count_t              nbuckets;
+
+  /** Number of elements currently in the hash */
+  sos_count_t              nb_elems;
   
   struct bucket bucket[0];
 };
@@ -118,6 +121,12 @@
 }
 
 
+sos_count_t sos_hash_get_nb_elts(const struct sos_hash_table * h)
+{
+  return h->nb_elems;
+}
+
+
 sos_ret_t sos_hash_dispose(struct sos_hash_table *h)
 {
   unsigned int i;
@@ -156,6 +165,7 @@
 
   list_add_head_named(h->bucket[bucket].list, h_elt, h_prev, h_next);
   h->bucket[bucket].nb_elems ++;
+  h->nb_elems ++;
 
   return SOS_OK;
 }
@@ -215,11 +225,35 @@
 
   list_delete_named(h->bucket[bucket].list, h_elt, h_prev, h_next);
   h->bucket[bucket].nb_elems --;
+  h->nb_elems --;
 
   return SOS_OK;
 }
 
 
+sos_bool_t sos_hash_walk(const struct sos_hash_table *h,
+			 sos_hash_map_func_t * iter_func,
+			 void * custom_data)
+{
+  unsigned int i;
+  for (i = 0 ; i < h->nbuckets ; i++)
+    {
+      sos_count_t nelts;
+      struct sos_hash_linkage * elt;
+
+      list_foreach_forward_named(h->bucket[i].list, elt,
+				 nelts, h_prev, h_next)
+	{
+	  if (! iter_func(custom_data,
+			  elt_for_h_linkage(h, elt)))
+	    return FALSE;
+	}
+    }
+
+  return TRUE;
+}
+
+
 unsigned long sos_hash_ui64(const void * ptr_key)
 {
   const sos_ui64_t * keyval = ptr_key;
diff -ruN /tmp/sos-code-article9.5/sos/hash.h sos-code-article9.5/sos/hash.h
--- /tmp/sos-code-article9.5/sos/hash.h	2006-01-22 12:48:31.000000000 +0100
+++ sos-code-article9.5/sos/hash.h	2006-03-19 12:21:03.000000000 +0100
@@ -41,6 +41,11 @@
 typedef sos_bool_t (sos_hash_key_eq_func_t)(const void * ptr_key1,
 					    const void * ptr_key2);
 
+/** Prototype of a key/value iteration function: when FALSE = stop now ! */
+typedef sos_bool_t (sos_hash_map_func_t)(void * custom_data,
+					 void * elt_with_key);
+
+
 /** Opaque structure */
 struct sos_hash_table;
 
@@ -57,19 +62,21 @@
  *
  * @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 hfunc The hash function (@see sos_hash_func_t). When NULL:
+ *               identity (native unsigned long keys assumed)
+ * @param eqfunc The element comparaison function (@see
+ *               sos_hash_key_eq_func_t). When NULL: native
+ *               unsigned long comparison
  * @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,eqfunc,nbuckets,\
-                        name_key_field,name_h_linkage)    \
-  _sos_hash_create_FULL(name, hfunc, eqfunc, nbuckets,           \
-                        offsetof(elt_type, name_key_field),    \
+#define sos_hash_create(name,elt_type,hfunc,eqfunc,nbuckets,    \
+                        name_key_field,name_h_linkage)          \
+  _sos_hash_create_FULL(name, hfunc, eqfunc, nbuckets,          \
+                        offsetof(elt_type, name_key_field),     \
                         offsetof(elt_type, name_h_linkage))
 
 
@@ -90,6 +97,10 @@
 		      sos_uoffset_t          offset_h_linkage);
 
 
+/** Return the number of elements in the hash table */
+sos_count_t sos_hash_get_nb_elts(const struct sos_hash_table * h);
+
+
 /** Does not free the elements themselves ! */
 sos_ret_t sos_hash_dispose(struct sos_hash_table *h);
 
@@ -116,6 +127,19 @@
 			  void *elt);
 
 
+/**
+ * Call iter_func on each of the elements in the hash table
+ * Stop when iter_func returns 0 (and returns FALSE). Otherwise return
+ * TRUE.
+ *
+ * @note iter_func must NOT block !
+ * @note No particular locking
+ */
+sos_bool_t sos_hash_walk(const struct sos_hash_table *h,
+			 sos_hash_map_func_t * iter_func,
+			 void * custom_data);
+
+
 /*
  * Common hash functions
  */
diff -ruN /tmp/sos-code-article9.5/sos/kmem_vmm.h sos-code-article9.5/sos/kmem_vmm.h
--- /tmp/sos-code-article9.5/sos/kmem_vmm.h	2006-01-22 12:48:31.000000000 +0100
+++ sos-code-article9.5/sos/kmem_vmm.h	2006-03-19 12:21:03.000000000 +0100
@@ -30,8 +30,8 @@
 #include <hwcore/paging.h>
 
 /* The base and top virtual addresses covered by the kernel allocator */
-#define SOS_KMEM_VMM_BASE 0x4000 /* 16kB */
-#define SOS_KMEM_VMM_TOP  SOS_PAGING_MIRROR_VADDR /* 1GB - 4MB */
+#define SOS_KMEM_VMM_BASE SOS_PAGING_BASE_KERNEL_ADDRESS      /* 16kB */
+#define SOS_KMEM_VMM_TOP  (SOS_PAGING_UPPER_KERNEL_ADDRESS+1) /* 1GB - 4MB */
 
 /** Opaque structure used internally and declared here for physmem.h */
 struct sos_kmem_range;
diff -ruN /tmp/sos-code-article9.5/sos/main.c sos-code-article9.5/sos/main.c
--- /tmp/sos-code-article9.5/sos/main.c	2006-01-22 12:48:31.000000000 +0100
+++ sos-code-article9.5/sos/main.c	2006-03-19 12:21:03.000000000 +0100
@@ -110,14 +110,15 @@
       || (cur_thr->fixup_uaccess.return_vaddr))
     {
       __label__ unforce_address_space;
-      sos_bool_t need_to_setup_mmu;
+      sos_bool_t need_to_prepare_user_space_access;
       sos_ui32_t errcode = sos_cpu_context_get_EX_info(ctxt);
 
-      /* Make sure to always stay in the interrupted thread's MMU
-	 configuration */
-      need_to_setup_mmu = (cur_thr->squatted_mm_context
-			   != sos_process_get_mm_context(cur_thr->process));
-      if (need_to_setup_mmu)
+      /* Do we already try to access an address space ? */
+      need_to_prepare_user_space_access
+	= (sos_thread_get_current()->squatted_address_space == NULL);
+
+      if (need_to_prepare_user_space_access)
+        /* No: Need to configure the interrupted thread user space */
 	sos_thread_prepare_user_space_access(NULL, 0);
 
       if (SOS_OK ==
@@ -136,7 +137,7 @@
 	  goto unforce_address_space;
 	}
 
-      if (need_to_setup_mmu)
+      if (need_to_prepare_user_space_access)
 	sos_thread_end_user_space_access();
 
       sos_bochs_printf("\e[35mTHR %p: Unresolved USER page Fault at instruction 0x%x on access to address 0x%x (info=%x)!\e[m\n",
@@ -148,7 +149,7 @@
       sos_thread_exit();
 
     unforce_address_space:
-      if (need_to_setup_mmu)
+      if (need_to_prepare_user_space_access)
 	sos_thread_end_user_space_access();
       return;
     }
@@ -292,6 +293,8 @@
   struct sos_process *proc_init;
   struct sos_thread *new_thr;
   sos_uaddr_t ustack, start_uaddr;
+  int fake_args[32]; /* Must be large enough to contain argv/envp for init */
+  sos_ret_t ret;
   struct sos_fs_opened_file * init_root, * init_cwd, * unused_of;
 
   /* Create the new process */
@@ -339,7 +342,6 @@
     }
 
 
-
   /* Map the 'init' program in user space */
   start_uaddr = sos_binfmt_elf32_map(as_init, "init");
   if (0 == start_uaddr)
@@ -349,7 +351,7 @@
     }
 
   /* Allocate the user stack */
-  ustack = (SOS_PAGING_TOP_USER_ADDRESS - SOS_DEFAULT_USER_STACK_SIZE) + 1;
+  ustack = (SOS_PAGING_UPPER_USER_ADDRESS - SOS_DEFAULT_USER_STACK_SIZE) + 1;
   retval = sos_dev_zero_map(as_init, &ustack, SOS_DEFAULT_USER_STACK_SIZE,
 			    SOS_VM_MAP_PROT_READ | SOS_VM_MAP_PROT_WRITE,
 			    /* PRIVATE */ 0);
@@ -359,12 +361,38 @@
       return -SOS_ENOMEM;
     }
 
+  /* Compute the address of the stack that will be used to initialize
+     the user thread */
+  ustack = SOS_ALIGN_INF((ustack + SOS_DEFAULT_USER_STACK_SIZE
+			  - sizeof(fake_args)), 4);
+
+  /* Build fake argv/envp arguments for the init process. See
+     userland/crt.c for the format. */
+  fake_args[0] = 0x1;                      /* argc */
+  fake_args[1] = 4 * sizeof(fake_args[0]); /* offset of argv[0] */
+  fake_args[2] = 0x0;                      /* delimiter between argv and envp */
+  fake_args[3] = 0x0;                      /* end of envp */
+  strzcpy ((char *) & fake_args[4],        /* argv[0] contents */
+	   "init", 5);
+
+  /* Copy the arguments to the user thread stack */
+  ret = sos_memcpy_to_specified_userspace (as_init,
+					   ustack,
+					   (sos_vaddr_t) fake_args,
+					   sizeof(fake_args));
+  if (sizeof(fake_args) != ret)
+    {
+      sos_bochs_printf ("sos_memcpy_to_specified_userspace() failed, returned %d\n", ret);
+      sos_process_unref(proc_init);
+      return ret;
+    }
+
   /* Now create the user thread */
   new_thr = sos_create_user_thread(NULL,
 				   proc_init,
 				   start_uaddr,
 				   0, 0,
-				   ustack + SOS_DEFAULT_USER_STACK_SIZE - 4,
+				   ustack,
 				   SOS_SCHED_PRIO_TS_LOWEST);
   if (! new_thr)
     {
diff -ruN /tmp/sos-code-article9.5/sos/process.c sos-code-article9.5/sos/process.c
--- /tmp/sos-code-article9.5/sos/process.c	2006-01-22 12:48:31.000000000 +0100
+++ sos-code-article9.5/sos/process.c	2006-03-19 12:21:03.000000000 +0100
@@ -21,6 +21,8 @@
 #include <sos/kmem_slab.h>
 #include <hwcore/irq.h>
 #include <drivers/bochs.h>
+#include <hwcore/bitmap.h>
+#include <sos/physmem.h> /* for SOS_PAGE_SIZE */
 
 #include <sos/umem_vmm.h>
 
@@ -40,6 +42,9 @@
 {
   char name[SOS_PROCESS_MAX_NAMELEN];
 
+  /** Process identifier */
+  sos_pid_t pid;
+
   /** First important resource: the address space */
   struct sos_umem_vmm_as *address_space;
 
@@ -60,12 +65,27 @@
   /** Where the current working dir of the process is */
   struct sos_fs_opened_file * cwd;
 
-  struct sos_process     *prev, *next;
+  /** To chain the processes in the global list of active processes
+      and PIDs */
+  struct sos_hash_linkage hlink_pidtable;
 };
 
 
 /** The list of processes in the system */
-static struct sos_process *process_list = NULL;
+static struct sos_hash_table * process_hash;
+
+
+/** The bitmap for PID allocation */
+static void * bitmap_pid;
+
+
+/** Number of bits in the PID bitmap */
+#define SOS_PROCESS_PID_BITMAP_NBITS \
+  (8 * SOS_PROCESS_PID_BITMAP_NPAGES * SOS_PAGE_SIZE)
+
+
+/** The PID where we start looking for a free slot */
+static sos_pid_t next_base_pid;
 
 
 /** The cache for the sos_process structures */
@@ -78,17 +98,18 @@
 void sos_process_dumplist(void)
 {
   struct sos_thread * cur_thr = sos_thread_get_current();
-  struct sos_process * proc;
-  int nb_procs;
+  int nb_procs = 0;
 
-  sos_bochs_printf("<ps>\n");
-  list_foreach(process_list, proc, nb_procs)
+  /* Internal function used to dump infos about a single process */
+  sos_bool_t process_iterator(void* custom_data,
+			      struct sos_process * proc)
     {
       struct sos_thread * thr;
       int    nb_thrs;
 
-      sos_bochs_printf("  proc@%p: '%s', %d threads, %d refs\n",
-		       proc, proc->name?proc->name:"(none)",
+      nb_procs ++;
+      sos_bochs_printf("  proc %d @%p: '%s', %d threads, %d refs\n",
+		       proc->pid, proc, proc->name?proc->name:"(none)",
 		       proc->nb_threads, proc->ref_cnt);
 
       list_foreach_forward_named(proc->thread_list, thr, nb_thrs,
@@ -103,7 +124,12 @@
 			     thr->state,
 			     (void*)sos_cpu_context_get_PC(thr->cpu_state));
 	}
+
+      return TRUE;
     }
+
+  sos_bochs_printf("<ps>\n");
+  sos_hash_walk(process_hash, (sos_hash_map_func_t*)process_iterator, NULL);
   sos_bochs_printf("  ======= %d processes =======\n", nb_procs);
   sos_bochs_printf("</ps>\n");
 }
@@ -111,6 +137,7 @@
 
 sos_ret_t sos_process_subsystem_setup()
 {
+
   /* Create the cache for the process structures */
   cache_struct_process = sos_kmem_cache_create("struct_process",
 					       sizeof(struct sos_process),
@@ -118,9 +145,93 @@
 					       0,
 					       SOS_KSLAB_CREATE_MAP
 					       | SOS_KSLAB_CREATE_ZERO);
-  if (! cache_struct_process)
+  if (NULL == cache_struct_process)
     return -SOS_ENOMEM;
 
+  /* Allocate pages for the PID bitmap */
+  bitmap_pid = (void*) sos_kmem_vmm_alloc(SOS_PROCESS_PID_BITMAP_NPAGES,
+					  SOS_KMEM_VMM_MAP);
+  if (NULL == bitmap_pid)
+    {
+      sos_kmem_cache_destroy(cache_struct_process);
+      return -SOS_ENOMEM;
+    }
+
+  /* The next process might (and actually will !) have PID 1 */
+  next_base_pid = 1;
+
+  /* Create the hash for the process list */
+  process_hash = sos_hash_create("pidtable", struct sos_process,
+				 NULL, NULL, 37,
+				 pid, hlink_pidtable);
+  if (NULL == process_hash)
+    {
+      sos_kmem_cache_destroy(cache_struct_process);
+      sos_kmem_vmm_free((sos_vaddr_t)bitmap_pid);
+      return -SOS_ENOMEM;
+    }
+
+  return SOS_OK;
+}
+
+
+/** Helper function to allocate a PID entry and bind it to the given process */
+static sos_ret_t register_process(struct sos_process * proc)
+{
+  sos_ret_t retval;
+  sos_pid_t pid;
+
+  /* First of all: look for a free PID */
+  while (TRUE)
+    {
+      pid = sos_bitmap_ffc(bitmap_pid,
+			   SOS_PROCESS_PID_BITMAP_NBITS,
+			   next_base_pid);
+      if (pid <= 0)
+	{
+	  if (next_base_pid > 1)
+	    {
+	      /* try again from the beginning */
+	      next_base_pid = 1;
+	      continue;
+	    }
+	  else
+	    /* Bad news: no PID left at all ! */
+	    return -SOS_EAGAIN;
+	}
+
+      if (! sos_bitmap_test_and_set(bitmap_pid, pid))
+	/* Could allocate it ! */
+	break;
+    }
+
+  /* Set the base PID for next process */
+  if (pid < SOS_PROCESS_PID_BITMAP_NBITS)
+    next_base_pid = pid + 1;
+  else
+    next_base_pid = 1;
+  
+  /* Now bind the process into the process hash */
+  proc->pid = pid;
+  
+  retval = sos_hash_insert(process_hash, proc);
+  if (SOS_OK != retval)
+    {
+      /* Bad news: cannot register in hash */
+      SOS_ASSERT_FATAL(sos_bitmap_test_and_clear(bitmap_pid, pid));
+      return retval;
+    }
+
+  return SOS_OK;
+}
+
+
+/** Helper function to deallocate a PID entry and unbind it to the given process */
+static sos_ret_t unregister_process(struct sos_process * proc)
+{
+  SOS_ASSERT_FATAL(SOS_OK == sos_hash_remove(process_hash, proc));
+  SOS_ASSERT_FATAL(sos_bitmap_test_and_clear(bitmap_pid, proc->pid));
+  proc->pid = 0;
   return SOS_OK;
 }
 
@@ -132,7 +243,6 @@
   sos_ui32_t flags;
   struct sos_process *proc;
 
-
   proc = (struct sos_process*) sos_kmem_cache_alloc(cache_struct_process, 0);
   if (! proc)
     return NULL;
@@ -170,7 +280,12 @@
     }
 
   if (do_copy_current_process)
-    proc->address_space = sos_umem_vmm_duplicate_current_thread_as(proc);
+    {
+      struct sos_process * myself = sos_thread_get_current()->process;
+      proc->address_space
+	= sos_umem_vmm_duplicate_as(sos_process_get_address_space(myself),
+				    proc);
+    }
   else
     proc->address_space = sos_umem_vmm_create_empty_as(proc);
 
@@ -194,12 +309,11 @@
 
   /* Add it to the global list of processes */
   sos_disable_IRQs(flags);
-  list_add_tail(process_list, proc);
+  retval = register_process(proc);
+  if (SOS_OK == retval)
+    proc->ref_cnt = 1;  /* Mark the process as referenced */
   sos_restore_IRQs(flags);
 
-  /* Mark the process as referenced */
-  proc->ref_cnt = 1;
-
  end_create_proc:
   if (SOS_OK != retval)
     {
@@ -238,6 +352,12 @@
 }
 
 
+sos_pid_t sos_process_get_pid(const struct sos_process *proc)
+{
+  return proc->pid;
+}
+
+
 sos_count_t sos_process_get_nb_threads(const struct sos_process *proc)
 {
   sos_count_t retval;
@@ -339,10 +459,13 @@
 
 sos_ret_t
 sos_process_register_opened_file(struct sos_process *proc,
-				 struct sos_fs_opened_file * of)
+				 struct sos_fs_opened_file * of,
+				 int start_search_at_fd)
 {
   int i;
-  for (i = 0 ; i < SOS_PROCESS_MAX_OPENED_FILES ; i++)
+  for (i = start_search_at_fd ;
+       i < SOS_PROCESS_MAX_OPENED_FILES ;
+       i++)
     if (NULL == proc->fds[i])
       {
 	proc->fds[i] = of;
@@ -417,7 +540,7 @@
       sos_restore_IRQs(flags);
       return -SOS_EBUSY;
     }
-  list_delete(process_list, proc);
+  unregister_process(proc);
   sos_restore_IRQs(flags);
 
   /* Close the file descriptors */
diff -ruN /tmp/sos-code-article9.5/sos/process.h sos-code-article9.5/sos/process.h
--- /tmp/sos-code-article9.5/sos/process.h	2006-01-22 12:48:31.000000000 +0100
+++ sos-code-article9.5/sos/process.h	2006-03-19 12:21:03.000000000 +0100
@@ -52,6 +52,10 @@
 #define SOS_DEFAULT_USER_STACK_SIZE (8 << 20)
 
 
+/** Number of pages for the pid bitmap (1 page = 32k PIDs) */
+#define SOS_PROCESS_PID_BITMAP_NPAGES  1
+
+
 /**
  * Initialization of the process subsystem
  */
@@ -93,6 +97,12 @@
 
 
 /**
+ * Return the PID of the process
+ */
+sos_pid_t sos_process_get_pid(const struct sos_process *proc);
+
+
+/**
  * Return the number of threads currently registered in the process
  */
 sos_count_t sos_process_get_nb_threads(const struct sos_process *proc);
@@ -169,7 +179,8 @@
  */
 sos_ret_t
 sos_process_register_opened_file(struct sos_process *proc,
-				 struct sos_fs_opened_file * of);
+				 struct sos_fs_opened_file * of,
+				 int start_search_at_fd);
 
 
 /**
diff -ruN /tmp/sos-code-article9.5/sos/syscall.c sos-code-article9.5/sos/syscall.c
--- /tmp/sos-code-article9.5/sos/syscall.c	2006-01-22 12:48:31.000000000 +0100
+++ sos-code-article9.5/sos/syscall.c	2006-03-19 12:21:03.000000000 +0100
@@ -20,7 +20,6 @@
 #include <sos/kmalloc.h>
 #include <sos/klibc.h>
 #include <drivers/bochs.h>
-#include <hwcore/paging.h>
 #include <sos/physmem.h>
 #include <sos/umem_vmm.h>
 #include <drivers/zero.h>
@@ -46,6 +45,7 @@
 {
   sos_ret_t retval;
 
+
   switch(syscall_id)
     {
     case SOS_SYSCALL_ID_EXIT:
@@ -82,7 +82,7 @@
 	  sos_duplicate_user_thread(NULL, new_proc,
 				    cur_thr,
 				    user_ctxt,
-				    0);
+				    0 /* fork return value for child ! */);
 	if (! new_thr)
 	  {
 	    sos_process_unref(new_proc);
@@ -95,7 +95,19 @@
 	/* Return to the "parent" thread with a value different from
 	   0. Unix says it should be the "PID" of the child. We don't
 	   have such a "PID" notion for now */
-	retval = (sos_ui32_t)new_proc;
+	retval = (sos_ui32_t)sos_process_get_pid(new_proc);
+      }
+      break;
+
+    case SOS_SYSCALL_ID_GETPID:
+      {
+	struct sos_thread *cur_thr;
+	struct sos_process * proc;
+
+	cur_thr = sos_thread_get_current();
+	proc    = cur_thr->process;
+
+	retval = (sos_ui32_t) sos_process_get_pid(proc);
       }
       break;
 
@@ -105,6 +117,8 @@
 	struct sos_process * proc;
 	struct sos_umem_vmm_as *new_as;
 	sos_uaddr_t user_str, ustack, start_uaddr;
+	sos_uaddr_t src_argaddr;
+	sos_size_t  len_args;
 	sos_size_t len;
 	char * str;
 
@@ -119,10 +133,19 @@
 	  }
 
 	/* Get the user arguments */
-	retval = sos_syscall_get2args(user_ctxt, & user_str, & len);
+	retval = sos_syscall_get4args(user_ctxt, & user_str, & len,
+				      & src_argaddr, & len_args);
 	if (SOS_OK != retval)
 	  break;
 
+	/* Don't go any further if the arg/env array is obviously too
+	   large */
+	if (SOS_DEFAULT_USER_STACK_SIZE <= len_args)
+	  {
+	    retval = -SOS_EINVAL;
+	    break;
+	  }
+
 	/* Copy the program name into kernel sppace */
 	retval = sos_strndup_from_user(& str, user_str, len + 1, 0);
 	if (SOS_OK != retval)
@@ -151,8 +174,9 @@
 
 	/* Allocate space for the user stack (8MB) */
 #define SOS_DEFAULT_USER_STACK_SIZE (8 << 20)
-	ustack = (SOS_PAGING_TOP_USER_ADDRESS - SOS_DEFAULT_USER_STACK_SIZE)
-	            + 1;
+	ustack = (SOS_PAGING_UPPER_USER_ADDRESS
+		  - SOS_DEFAULT_USER_STACK_SIZE)
+	         + 1;
 	retval = sos_dev_zero_map(new_as, &ustack, SOS_DEFAULT_USER_STACK_SIZE,
 				  SOS_VM_MAP_PROT_READ | SOS_VM_MAP_PROT_WRITE,
 				  /* PRIVATE */ 0);
@@ -163,20 +187,34 @@
 	    break;
 	  }
 
+	/* ustack now refers to the initial stack pointer of the new
+	   user thread: update it here */
+	ustack = SOS_ALIGN_INF((ustack + SOS_DEFAULT_USER_STACK_SIZE
+				- len_args), 4);
+
+	if (len_args != sos_usercpy(new_as, ustack,
+				    NULL, src_argaddr,
+				    len_args))
+	  {
+	    sos_umem_vmm_delete_as(new_as);
+	    sos_kfree((sos_vaddr_t)str);
+	    retval = -SOS_EFAULT;
+	    break;
+	  }
+
 	/* Now create the user thread */
 	new_thr = sos_create_user_thread(NULL,
 					 proc,
 					 start_uaddr,
 					 0, 0,
-					 ustack + SOS_DEFAULT_USER_STACK_SIZE
-					   - 4,
+					 ustack,
 					 SOS_SCHED_PRIO_TS_LOWEST);
 	if (! new_thr)
 	  {
 	    sos_umem_vmm_delete_as(new_as);
 	    sos_kfree((sos_vaddr_t)str);
 	    retval = -SOS_ENOMEM;
-	    break;	    
+	    break;
 	  }
 
 	sos_process_set_name(proc, str);
@@ -188,7 +226,7 @@
 	  {
 	    sos_umem_vmm_delete_as(new_as);
 	    sos_kfree((sos_vaddr_t)str);
-	    break;	    
+	    break;
 	  }
 
 	/* The current thread must exit now */
@@ -297,6 +335,7 @@
       }
       break;
 
+
     case SOS_SYSCALL_ID_MSYNC:
       {
 	sos_uaddr_t start_uaddr;
@@ -322,6 +361,7 @@
       }
       break;
 
+
     case SOS_SYSCALL_ID_NEW_THREAD:
       {
 	sos_uaddr_t start_func;
@@ -593,7 +633,7 @@
 	if (SOS_OK != retval)
 	  break;
 
-	retval = sos_process_register_opened_file(proc, of);
+	retval = sos_process_register_opened_file(proc, of, 0);
 	if (retval < 0)
 	  {
 	    sos_fs_close(of);
@@ -1437,6 +1477,6 @@
       sos_bochs_printf("Syscall: UNKNOWN[%d]\n", syscall_id);
       retval = -SOS_ENOSUP;
     }
-  
+
   return retval;
 }
diff -ruN /tmp/sos-code-article9.5/sos/syscall.h sos-code-article9.5/sos/syscall.h
--- /tmp/sos-code-article9.5/sos/syscall.h	2006-01-22 12:48:31.000000000 +0100
+++ sos-code-article9.5/sos/syscall.h	2006-03-19 12:21:03.000000000 +0100
@@ -29,7 +29,7 @@
 /**
  * The user services offered by the SOS kernel
  */
-#define SOS_SYSCALL_ID_EXIT        256 /**< Args: retval, retval=NONE */
+#define SOS_SYSCALL_ID_EXIT        67  /**< Args: retval, retval=NONE */
 
 
 /**
@@ -42,8 +42,9 @@
 /**
  * User memory mappings management
  */
+#define SOS_SYSCALL_ID_GETPID      256 /**< Args: NONE, retval=cur. proc. PID */
 #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_EXEC        258 /**< Args: ptr_exec_path strlen_exec_path addr_args len_args, 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 */
@@ -72,6 +73,12 @@
 #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 */
+/* fcntl possible commands */
+#  define SOS_FCNTL_DUPFD          0x4201
+#  define SOS_FCNTL_GETFD          0x4202
+#  define SOS_FCNTL_SETFD          0x4203
+#  define SOS_FCNTL_GETFL          0x4204
+#  define SOS_FCNTL_SETFL          0x4205
 #define SOS_SYSCALL_ID_IOCTL       569 /**< Args: fd cmd arg, retval=errno */
 
 #define SOS_SYSCALL_ID_CREAT       570 /**< Args: uaddr_path pathlen access_rights, retval=errno */
diff -ruN /tmp/sos-code-article9.5/sos/thread.c sos-code-article9.5/sos/thread.c
--- /tmp/sos-code-article9.5/sos/thread.c	2006-01-22 12:48:31.000000000 +0100
+++ sos-code-article9.5/sos/thread.c	2006-03-19 12:21:03.000000000 +0100
@@ -70,14 +70,6 @@
 static struct sos_kslab_cache *cache_thread;
 
 
-/**
- * (Forwad declaration) Helper function to change the MMU config of
- * the current executing thread. Analogous to function
- * sos_thread_change_current_mm_context() of article 7
- */
-static sos_ret_t change_current_mm_context(struct sos_mm_context *mm_ctxt);
-
-
 struct sos_thread *sos_thread_get_current()
 {
   SOS_ASSERT_FATAL(current_thread->state == SOS_THR_RUNNING);
@@ -361,16 +353,15 @@
       SOS_ASSERT_FATAL(the_thread->process != NULL);
 
       /* It should not squat any other's address space */
-      SOS_ASSERT_FATAL(the_thread->squatted_mm_context == NULL);
+      SOS_ASSERT_FATAL(the_thread->squatted_address_space == NULL);
 
       /* Perform an MMU context switch if needed */
-      sos_mm_context_switch_to(sos_process_get_mm_context(the_thread->process));
+      sos_umem_vmm_set_current_as(sos_process_get_address_space(the_thread->process));
     }
 
-  /* the_thread is a kernel thread squatting a precise address
-     space ? */
-  else if (the_thread->squatted_mm_context != NULL)
-    sos_mm_context_switch_to(the_thread->squatted_mm_context);
+  /* Restore the address space currently in use */
+  else
+      sos_umem_vmm_set_current_as(the_thread->squatted_address_space);
 }
 
 
@@ -386,9 +377,8 @@
 
   sos_kfree((sos_vaddr_t) thr->kernel_stack_base_addr);
 
-  /* If the thread squats an address space, release it */
-  if (thr->squatted_mm_context)
-    SOS_ASSERT_FATAL(SOS_OK == change_current_mm_context(NULL));
+  /* Not allowed to squat any user space at deletion time */
+  SOS_ASSERT_FATAL(NULL == thr->squatted_address_space);
 
   /* For a user thread: remove the thread from the process threads' list */
   if (thr->process)
@@ -780,34 +770,6 @@
  */
 
 
-static sos_ret_t
-change_current_mm_context(struct sos_mm_context *mm_ctxt)
-{
-  /* Retrieve the previous mm context */
-  struct sos_mm_context * prev_mm_ctxt
-    = current_thread->squatted_mm_context;
-
-  /* Update current thread's squatted mm context */
-  current_thread->squatted_mm_context = mm_ctxt;
-
-  /* Update the reference counts and switch the MMU configuration if
-     needed */
-  if (mm_ctxt != NULL)
-    {
-      sos_mm_context_ref(mm_ctxt); /* Because it is now referenced as
-				      the squatted_mm_context field of
-				      the thread */
-      sos_mm_context_switch_to(mm_ctxt);
-    }
-  else
-    sos_mm_context_unref(prev_mm_ctxt); /* Because it is not referenced as
-					   the squatted_mm_context field of
-					   the thread any more */
-
-  return SOS_OK;
-}
-
-
 sos_ret_t
 sos_thread_prepare_user_space_access(struct sos_umem_vmm_as * dest_as,
 				     sos_vaddr_t fixup_retvaddr)
@@ -823,21 +785,18 @@
 
       dest_as = sos_process_get_address_space(current_thread->process);
     }
-  else
-    /* Don't allow to access to an address space different than that
-       of the current thread if the page fault are allowed ! */
-    SOS_ASSERT_FATAL(! fixup_retvaddr);
 
   sos_disable_IRQs(flags);
-  SOS_ASSERT_FATAL(NULL == current_thread->squatted_mm_context);
+  SOS_ASSERT_FATAL(NULL == current_thread->squatted_address_space);
   SOS_ASSERT_FATAL(0 == current_thread->fixup_uaccess.return_vaddr);
 
   /* Change the MMU configuration and init the fixup return address */
-  retval = change_current_mm_context(sos_umem_vmm_get_mm_context(dest_as));
+  retval = sos_umem_vmm_set_current_as(dest_as);
   if (SOS_OK == retval)
     {
+      current_thread->squatted_address_space      = dest_as;
       current_thread->fixup_uaccess.return_vaddr  = fixup_retvaddr;
-      current_thread->fixup_uaccess.faulted_uaddr = 0;      
+      current_thread->fixup_uaccess.faulted_uaddr = 0;
     }
 
   sos_restore_IRQs(flags);
@@ -852,13 +811,15 @@
   sos_ui32_t flags;
 
   sos_disable_IRQs(flags);
-  SOS_ASSERT_FATAL(NULL != current_thread->squatted_mm_context);
+  SOS_ASSERT_FATAL(NULL != current_thread->squatted_address_space);
 
   /* Don't impose anything regarding the current MMU configuration anymore */
-  retval = change_current_mm_context(NULL);
   current_thread->fixup_uaccess.return_vaddr  = 0;
   current_thread->fixup_uaccess.faulted_uaddr = 0;
 
+  retval = sos_umem_vmm_set_current_as(NULL);
+  current_thread->squatted_address_space      = NULL;
+
   sos_restore_IRQs(flags);
   return retval;
 }
diff -ruN /tmp/sos-code-article9.5/sos/thread.h sos-code-article9.5/sos/thread.h
--- /tmp/sos-code-article9.5/sos/thread.h	2006-01-22 12:48:31.000000000 +0100
+++ sos-code-article9.5/sos/thread.h	2006-03-19 12:21:03.000000000 +0100
@@ -126,7 +126,7 @@
    * This is the SOS implementation of the Linux "Lazy TLB" and
    * address-space loaning.
    */
-  struct sos_mm_context *squatted_mm_context;
+  struct sos_umem_vmm_as *squatted_address_space;
 
   /* Data specific to each state */
   union
@@ -349,13 +349,11 @@
  * @param dest_as The address space we want to access, or NULL to
  * access current thread's address space
  *
- * @param fixup_retvaddr When != 0, then dest_as MUST BE NULL (we
- * don't allow controlled access from kernel into user space from a
- * foreign thread). In this case, the page fault handler should accept
- * page faults from the kernel in user space, and resolve them in the
- * usual way. The value in retvaddr is where the page fault handler
- * has to return to in case the page fault remains unresolved. The
- * address of the faulting address is kept in
+ * @param fixup_retvaddr When != 0, the page fault handler should
+ * accept page faults from the kernel in user space, and resolve them
+ * in the usual way. The value in retvaddr is where the page fault
+ * handler has to return to in case the page fault remains
+ * unresolved. The address of the faulting address is kept in
  * éthread->fixup_uaccess.faulted_uaddr
  *
  * @note typical values for fixup_retvaddr are obtained by "Labels as
diff -ruN /tmp/sos-code-article9.5/sos/types.h sos-code-article9.5/sos/types.h
--- /tmp/sos-code-article9.5/sos/types.h	2006-01-22 12:48:31.000000000 +0100
+++ sos-code-article9.5/sos/types.h	2006-03-19 12:21:03.000000000 +0100
@@ -49,6 +49,9 @@
 /** Generic count of objects (LARGE) */
 typedef unsigned long long int sos_lcount_t;
 
+/** Process identifiers */
+typedef unsigned int           sos_pid_t;
+
 /* Low-level sizes */
 typedef unsigned long long int sos_ui64_t; /**< 32b unsigned */
 typedef unsigned long int      sos_ui32_t; /**< 32b unsigned */
diff -ruN /tmp/sos-code-article9.5/sos/uaccess.c sos-code-article9.5/sos/uaccess.c
--- /tmp/sos-code-article9.5/sos/uaccess.c	2006-01-22 12:48:31.000000000 +0100
+++ sos-code-article9.5/sos/uaccess.c	2006-03-19 12:21:03.000000000 +0100
@@ -34,7 +34,13 @@
  ({ if (__uaccess_zero_fool_gcc) goto lbl; })
 
 
-static sos_ret_t nocheck_user_memcpy(sos_vaddr_t dest,
+/**
+ * @param user_as The user address space we want to transfer from/to
+ *
+ * @return >=0 : number of bytes successfully transferred, <0 an error code
+ */
+static sos_ret_t nocheck_user_memcpy(struct sos_umem_vmm_as * user_as,
+				     sos_vaddr_t dest,
 				     sos_vaddr_t src,
 				     sos_size_t size,
 				     sos_bool_t transfer_from_user)
@@ -50,7 +56,7 @@
   if (size <= 0)
     return 0;
 
-  retval = sos_thread_prepare_user_space_access(NULL,
+  retval = sos_thread_prepare_user_space_access(user_as,
 						(sos_vaddr_t) && catch_pgflt);
   if (SOS_OK != retval)
     return retval;
@@ -103,13 +109,14 @@
 			       sos_size_t size)
 {
   /* Make sure user is trying to access user space */
-  if (user_from < SOS_PAGING_BASE_USER_ADDRESS)
+  if (! SOS_PAGING_IS_USER_AREA(user_from, size) )
     return -SOS_EPERM;
 
-  if (user_from > SOS_PAGING_TOP_USER_ADDRESS - size)
+  /* Make sure the copy totally resides inside kernel space */
+  if (! SOS_PAGING_IS_KERNEL_AREA(kernel_to, size) )
     return -SOS_EPERM;
 
-  return nocheck_user_memcpy(kernel_to, user_from, size, TRUE);
+  return nocheck_user_memcpy(NULL, kernel_to, user_from, size, TRUE);
 }
 
 
@@ -144,14 +151,94 @@
 			     sos_vaddr_t kernel_from,
 			     sos_size_t size)
 {
+  /* Make sure the source totally resides inside kernel space */
+  if (! SOS_PAGING_IS_KERNEL_AREA(kernel_from, size) )
+    return -SOS_EPERM;
+
   /* Make sure user is trying to access user space */
-  if (user_to < SOS_PAGING_BASE_USER_ADDRESS)
+  if (! SOS_PAGING_IS_USER_AREA(user_to, size) )
     return -SOS_EPERM;
 
-  if (user_to > SOS_PAGING_TOP_USER_ADDRESS - size)
+  return nocheck_user_memcpy(NULL, user_to, kernel_from, size, FALSE);
+}
+
+
+sos_ret_t sos_memcpy_from_specified_userspace(sos_vaddr_t kernel_to,
+					      struct sos_umem_vmm_as * src_as,
+					      sos_uaddr_t user_from,
+					      sos_size_t size)
+{
+  if (NULL == src_as)
+    return -SOS_EINVAL;
+  
+  /* Make sure user is trying to access user space */
+  if (! SOS_PAGING_IS_USER_AREA(user_from, size) )
+    return -SOS_EPERM;
+
+  /* Make sure the copy totally resides inside kernel space */
+  if (! SOS_PAGING_IS_KERNEL_AREA(kernel_to, size) )
+    return -SOS_EPERM;
+
+  return nocheck_user_memcpy(src_as, kernel_to, user_from, size, TRUE);
+}
+
+
+sos_ret_t sos_memcpy_to_specified_userspace(struct sos_umem_vmm_as * dst_as,
+					    sos_uaddr_t user_to,
+					    sos_vaddr_t kernel_from,
+					    sos_size_t size)
+{
+  if (NULL == dst_as)
+    return -SOS_EINVAL;
+  
+  /* Make sure the source totally resides inside kernel space */
+  if (! SOS_PAGING_IS_KERNEL_AREA(kernel_from, size) )
+    return -SOS_EPERM;
+
+  /* Make sure user is trying to access user space */
+  if (! SOS_PAGING_IS_USER_AREA(user_to, size) )
+    return -SOS_EPERM;
+
+  return nocheck_user_memcpy(dst_as, user_to, kernel_from, size, FALSE);
+}
+
+
+sos_ret_t sos_usercpy(struct sos_umem_vmm_as * dst_as,
+		      sos_uaddr_t dst_uaddr,
+		      struct sos_umem_vmm_as * src_as,
+		      sos_uaddr_t src_uaddr,
+		      sos_size_t size)
+{
+  sos_vaddr_t kern_addr;
+  sos_ret_t retval;
+
+  if (size <= 0)
+    return 0;
+
+  /* Make sure user is trying to access user space */
+  if (! SOS_PAGING_IS_USER_AREA(src_uaddr, size) )
+    return -SOS_EPERM;
+  if (! SOS_PAGING_IS_USER_AREA(dst_uaddr, size) )
     return -SOS_EPERM;
 
-  return nocheck_user_memcpy(user_to, kernel_from, size, FALSE);
+  kern_addr = sos_kmalloc(size, 0);
+  if (! kern_addr)
+    return -SOS_ENOMEM;
+
+  retval = nocheck_user_memcpy(src_as, kern_addr, src_uaddr,
+			       size, TRUE);
+  if (retval <= 0)
+    {
+      sos_kfree((sos_vaddr_t)kern_addr);
+      return retval;
+    }
+
+  retval = nocheck_user_memcpy(dst_as, dst_uaddr, kern_addr,
+			       retval, FALSE);
+
+  sos_kfree((sos_vaddr_t)kern_addr);
+
+  return retval;
 }
 
 
@@ -167,13 +254,9 @@
     return 0;
 
   /* Make sure user is trying to access user space */
-  if (user_str < SOS_PAGING_BASE_USER_ADDRESS)
+  if (! SOS_PAGING_IS_USER_AREA(user_str, max_len) )
     return -SOS_EPERM;
 
-  /* Don't allow invalid max_len */
-  if ( (max_len < 1) || (max_len > SOS_PAGING_USER_SPACE_SIZE) )
-    return -SOS_EINVAL;
-
   retval = sos_thread_prepare_user_space_access(NULL,
 						(sos_vaddr_t) && catch_pgflt);
   if (SOS_OK != retval)
@@ -252,13 +335,16 @@
 sos_ret_t sos_strzcpy_from_user(char *kernel_to, sos_uaddr_t user_from,
 				sos_size_t max_len)
 {
+  if (max_len <= 0)
+    return -SOS_EINVAL;
+
   /* Make sure user is trying to access user space */
-  if ((sos_uaddr_t)user_from < SOS_PAGING_BASE_USER_ADDRESS)
+  if (! SOS_PAGING_IS_USER_AREA(user_from, max_len) )
     return -SOS_EPERM;
 
-  /* Don't allow invalid max_len */
-  if ( (max_len < 1) || (max_len > SOS_PAGING_USER_SPACE_SIZE) )
-    return -SOS_EINVAL;
+  /* Make sure the copy totally resides inside kernel space */
+  if (! SOS_PAGING_IS_KERNEL_AREA((sos_vaddr_t)kernel_to, max_len) )
+    return -SOS_EPERM;
 
   return nocheck_user_strzcpy(kernel_to, (const char*)user_from, max_len);
 }
@@ -267,13 +353,16 @@
 sos_ret_t sos_strzcpy_to_user(sos_uaddr_t user_to, const char *kernel_from,
 			      sos_size_t max_len)
 {
-  /* Make sure user is trying to access user space */
-  if ((sos_uaddr_t)user_to < SOS_PAGING_BASE_USER_ADDRESS)
+  if (max_len <= 0)
+    return -SOS_EINVAL;
+
+  /* Make sure the source totally resides inside kernel space */
+  if (! SOS_PAGING_IS_KERNEL_AREA((sos_vaddr_t)kernel_from, max_len) )
     return -SOS_EPERM;
 
-  /* Don't allow invalid max_len */
-  if ( (max_len < 1) || (max_len > SOS_PAGING_USER_SPACE_SIZE) )
-    return -SOS_EINVAL;
+  /* Make sure user is trying to access user space */
+  if (! SOS_PAGING_IS_USER_AREA(user_to, max_len) )
+    return -SOS_EPERM;
 
   return nocheck_user_strzcpy((char*)user_to, kernel_from, max_len);
 }
diff -ruN /tmp/sos-code-article9.5/sos/uaccess.h sos-code-article9.5/sos/uaccess.h
--- /tmp/sos-code-article9.5/sos/uaccess.h	2006-01-22 12:48:31.000000000 +0100
+++ sos-code-article9.5/sos/uaccess.h	2006-03-19 12:21:03.000000000 +0100
@@ -27,7 +27,7 @@
 
 #include <sos/types.h>
 #include <sos/errno.h>
-
+#include <sos/umem_vmm.h>
 
 /**
  * Retrieve a bunch of data from the user space of the
@@ -55,8 +55,8 @@
 
 
 /**
- * Transfer a bunch of data to the user space of the
- * current_thread->process
+ * Transfer a bunch of data from kernel space into the user space of
+ * the current_thread->process
  *
  * @return <0 n error ! Return the number of bytes successfully copied
  * otherwise.
@@ -67,6 +67,50 @@
 
 
 /**
+ * Variant of sos_memcpy_from_user()
+ * Retrieve a bunch of data from the user space of the given src_as
+ * address space into kernel space
+ *
+ * @return NULL on error (including unresolved page fault during
+ * transfer)
+ *
+ * @note src_as MUST NOT be NULL
+ */
+sos_ret_t sos_memcpy_from_specified_userspace(sos_vaddr_t kernel_to,
+					      struct sos_umem_vmm_as * src_as,
+					      sos_uaddr_t user_from,
+					      sos_size_t size);
+
+
+/**
+ * Variant of sos_memcpy_to_user()
+ * Transfer a bunch of data from kernel space into the user space of
+ * the given dst_as address space
+ *
+ * @return <0 n error ! Return the number of bytes successfully copied
+ * otherwise.
+ *
+ * @note dst_as MUST NOT be NULL
+ */
+sos_ret_t sos_memcpy_to_specified_userspace(struct sos_umem_vmm_as * dst_as,
+					    sos_uaddr_t user_to,
+					    sos_vaddr_t kernel_from,
+					    sos_size_t size);
+
+/**
+ * Copy data from an user space into another
+ * @return The number of bytes successfuly copied
+ * @note dst_as and src_as may be NULL, in which case the current
+ * thread->process address space is used for the copy
+ */
+sos_ret_t sos_usercpy(struct sos_umem_vmm_as * dst_as,
+		      sos_uaddr_t dst_uaddr,
+		      struct sos_umem_vmm_as * src_as,
+		      sos_uaddr_t src_uaddr,
+		      sos_size_t size);
+
+
+/**
  * @return the length of the given user space string user_str
  * (excluding the trailing \0), up to max_len bytes. <0 on error
  * (unresolved page fault, etc.)
diff -ruN /tmp/sos-code-article9.5/sos/umem_vmm.c sos-code-article9.5/sos/umem_vmm.c
--- /tmp/sos-code-article9.5/sos/umem_vmm.c	2006-01-22 12:48:31.000000000 +0100
+++ sos-code-article9.5/sos/umem_vmm.c	2006-03-19 12:21:03.000000000 +0100
@@ -23,6 +23,7 @@
 #include <drivers/bochs.h>
 #include <hwcore/mm_context.h>
 #include <hwcore/paging.h>
+#include <hwcore/irq.h>
 #include <drivers/zero.h>
 
 #include "umem_vmm.h"
@@ -230,6 +231,39 @@
 }
 
 
+static struct sos_umem_vmm_as * current_address_space = NULL;
+struct sos_umem_vmm_as * sos_umem_vmm_get_current_as(void)
+{
+  return current_address_space;
+}
+
+
+sos_ret_t sos_umem_vmm_set_current_as(struct sos_umem_vmm_as * as)
+{
+  sos_ui32_t flags;
+  struct sos_umem_vmm_as *prev_as = current_address_space;
+
+  if (current_address_space == as)
+    return SOS_OK;
+
+  if (NULL != as)
+    {
+      sos_disable_IRQs(flags);
+      sos_process_ref(sos_umem_vmm_get_process(as));
+      sos_mm_context_switch_to(sos_umem_vmm_get_mm_context(as));
+      current_address_space = as;
+      sos_restore_IRQs(flags);
+    }
+  else
+    current_address_space = as;
+
+  if (prev_as)
+    sos_process_unref(sos_umem_vmm_get_process(prev_as));
+
+  return SOS_OK;
+}
+
+
 struct sos_umem_vmm_as *
 sos_umem_vmm_create_empty_as(struct sos_process *owner)
 {
@@ -252,10 +286,10 @@
 
 
 struct sos_umem_vmm_as *
-sos_umem_vmm_duplicate_current_thread_as(struct sos_process *owner)
+sos_umem_vmm_duplicate_as(struct sos_umem_vmm_as * model_as,
+			  struct sos_process *for_owner)
 {
   __label__ undo_creation;
-  struct sos_umem_vmm_as * my_as;
   struct sos_umem_vmm_vr * model_vr;
   int nb_vr;
 
@@ -264,8 +298,7 @@
   if (! new_as)
     return NULL;
 
-  my_as = sos_process_get_address_space(sos_thread_get_current()->process);
-  new_as->process = owner;
+  new_as->process = for_owner;
   list_init_named(new_as->list_vr, prev_in_as, next_in_as);
 
   /*
@@ -274,12 +307,12 @@
    * COW)
    */
   SOS_ASSERT_FATAL(SOS_OK
-		   == sos_thread_prepare_user_space_access(my_as,
+		   == sos_thread_prepare_user_space_access(model_as,
 							   (sos_vaddr_t)
 							     NULL));
 
   /* Copy the virtual regions */
-  list_foreach_named(my_as->list_vr, model_vr, nb_vr, prev_in_as, next_in_as)
+  list_foreach_named(model_as->list_vr, model_vr, nb_vr, prev_in_as, next_in_as)
     {
       struct sos_umem_vmm_vr * vr;
 
@@ -315,16 +348,16 @@
     }
 
   /* Now copy the current MMU configuration */
-  new_as->mm_context = sos_mm_context_duplicate(my_as->mm_context);
+  new_as->mm_context = sos_mm_context_duplicate(model_as->mm_context);
   if (NULL == new_as->mm_context)
     goto undo_creation;
 
   /* Correct behavior */
-  new_as->heap_start = my_as->heap_start;
-  new_as->heap_size  = my_as->heap_size;
-  new_as->phys_total = my_as->phys_total;
-  memcpy(& new_as->vm_total, & my_as->vm_total, sizeof(struct vm_usage));
-  memcpy(& new_as->vm_shrd, & my_as->vm_shrd, sizeof(struct vm_usage));
+  new_as->heap_start = model_as->heap_start;
+  new_as->heap_size  = model_as->heap_size;
+  new_as->phys_total = model_as->phys_total;
+  memcpy(& new_as->vm_total, & model_as->vm_total, sizeof(struct vm_usage));
+  memcpy(& new_as->vm_shrd, & model_as->vm_shrd, sizeof(struct vm_usage));
   SOS_ASSERT_FATAL(SOS_OK == sos_thread_end_user_space_access());
   return new_as;
 
@@ -555,9 +588,7 @@
        */
 
       /* Make sure the hint_uaddr hint is valid */
-      if (hint_uaddr < SOS_PAGING_BASE_USER_ADDRESS)
-	{ retval = -SOS_EINVAL; goto return_mmap; }
-      if (hint_uaddr > SOS_PAGING_TOP_USER_ADDRESS - size)
+      if (! SOS_PAGING_IS_USER_AREA(hint_uaddr, size) )
 	{ retval = -SOS_EINVAL; goto return_mmap; }
 
       /* Unmap any overlapped VR */
@@ -712,8 +743,8 @@
 		   sos_uaddr_t uaddr, sos_size_t size)
 {
   struct sos_umem_vmm_vr *vr, *preallocated_vr;
-  sos_bool_t need_to_setup_mmu;
   sos_bool_t used_preallocated_vr;
+  sos_bool_t need_to_change_address_space = FALSE;
 
   if (! SOS_IS_PAGE_ALIGNED(uaddr))
     return -SOS_EINVAL;
@@ -722,9 +753,7 @@
   size = SOS_PAGE_ALIGN_SUP(size);
 
   /* Make sure the uaddr is valid */
-  if (uaddr < SOS_PAGING_BASE_USER_ADDRESS)
-    return -SOS_EINVAL;
-  if (uaddr > SOS_PAGING_TOP_USER_ADDRESS - size)
+  if (! SOS_PAGING_IS_USER_AREA(uaddr, size) )
     return -SOS_EINVAL;
 
   /* In some cases, the unmapping might imply a VR to be split into
@@ -875,19 +904,27 @@
 			      uaddr, size, vr->start, vr->size);
     }
 
-  need_to_setup_mmu = (sos_thread_get_current()->squatted_mm_context
-		       != as->mm_context);
-  if (need_to_setup_mmu)
+  /* When called from mresize, the address space is already squatted,
+     so we don't have to change it again */
+  need_to_change_address_space
+    = (as != sos_thread_get_current()->squatted_address_space);
+
+  if (need_to_change_address_space)
     SOS_ASSERT_FATAL(SOS_OK
 		     == sos_thread_prepare_user_space_access(as,
 							     (sos_vaddr_t)
-							       NULL));
+							     NULL));
+
+
+  /* Begin independent sub-block */
   {
     sos_ret_t sz_unmapped = sos_paging_unmap_interval(uaddr, size);
     SOS_ASSERT_FATAL(sz_unmapped >= 0);
     as->phys_total -= sz_unmapped;
   }
-  if (need_to_setup_mmu)
+  /* End independent sub-block */
+
+  if (need_to_change_address_space)
     SOS_ASSERT_FATAL(SOS_OK == sos_thread_end_user_space_access());
 
   if (! used_preallocated_vr)
@@ -913,9 +950,7 @@
   size = SOS_PAGE_ALIGN_SUP(size);
 
   /* Make sure the uaddr is valid */
-  if (uaddr < SOS_PAGING_BASE_USER_ADDRESS)
-    return -SOS_EINVAL;
-  if (uaddr > SOS_PAGING_TOP_USER_ADDRESS - size)
+  if (! SOS_PAGING_IS_USER_AREA(uaddr, size) )
     return -SOS_EINVAL;
 
   /* Pre-allocate 2 new VRs (same reason as for unmap). Because chprot
@@ -1224,9 +1259,7 @@
   size = SOS_PAGE_ALIGN_SUP(size);
 
   /* Make sure the uaddr is valid */
-  if (uaddr < SOS_PAGING_BASE_USER_ADDRESS)
-    return -SOS_EINVAL;
-  if (uaddr > SOS_PAGING_TOP_USER_ADDRESS - size)
+  if (! SOS_PAGING_IS_USER_AREA(uaddr, size) )
     return -SOS_EINVAL;
 
   /* Go from page to page, and for each dirty page in the region, call
@@ -1287,9 +1320,7 @@
   struct sos_umem_vmm_vr *vr, *prev_vr, *next_vr;
 
   /* Make sure the new uaddr is valid */
-  if (*new_uaddr < SOS_PAGING_BASE_USER_ADDRESS)
-    return -SOS_EINVAL;
-  if (*new_uaddr > SOS_PAGING_TOP_USER_ADDRESS - new_size)
+  if (! SOS_PAGING_IS_USER_AREA(*new_uaddr, new_size) )
     return -SOS_EINVAL;
 
   old_uaddr = SOS_PAGE_ALIGN_INF(old_uaddr);
@@ -1347,9 +1378,7 @@
     must_move_vr |= TRUE;
 
   /* If VR would be out-of-user-space, it must be moved */
-  if (*new_uaddr < SOS_PAGING_BASE_USER_ADDRESS)
-    must_move_vr |= TRUE;
-  if (*new_uaddr > SOS_PAGING_TOP_USER_ADDRESS - new_size)
+  if (! SOS_PAGING_IS_USER_AREA(*new_uaddr, new_size) )
     must_move_vr |= TRUE;
 
   /* The VR must be moved but the user forbids it */
@@ -1464,14 +1493,10 @@
 					      sos_bool_t write_access,
 					      sos_bool_t user_access)
 {
-  struct sos_process     *process = sos_thread_get_current()->process;
   struct sos_umem_vmm_as *as;
   struct sos_umem_vmm_vr *vr;
 
-  if (! process)
-    return -SOS_EFAULT;
-
-  as = sos_process_get_address_space(process);
+  as = current_address_space;
   if (! as)
     return -SOS_EFAULT;
 
@@ -1590,9 +1615,7 @@
   struct sos_umem_vmm_vr *vr;
   int nb_vr;
 
-  if (uaddr < SOS_PAGING_BASE_USER_ADDRESS)
-    return NULL;
-  if (uaddr > SOS_PAGING_TOP_USER_ADDRESS)
+  if (! SOS_PAGING_IS_USER_AREA(uaddr, 1) )
     return NULL;
 
   list_foreach_named(as->list_vr, vr, nb_vr, prev_in_as, next_in_as)
@@ -1632,7 +1655,7 @@
   if (hint_uaddr < SOS_PAGING_BASE_USER_ADDRESS)
     hint_uaddr = SOS_PAGING_BASE_USER_ADDRESS;
 
-  if (hint_uaddr > SOS_PAGING_TOP_USER_ADDRESS - size + 1)
+  if (hint_uaddr > SOS_PAGING_UPPER_USER_ADDRESS - size + 1)
     return (sos_uaddr_t)NULL;
 
   initial_vr = vr = find_enclosing_or_next_vr(as, hint_uaddr);
@@ -1658,7 +1681,7 @@
 	  /* No: wrapping up */
 
 	  /* Is there any space before the end of user space ? */
-	  if (hint_uaddr <= SOS_PAGING_TOP_USER_ADDRESS - size)
+	  if (hint_uaddr <= SOS_PAGING_UPPER_USER_ADDRESS - size)
 	    return hint_uaddr;
 
 	  hint_uaddr = SOS_PAGING_BASE_USER_ADDRESS;
diff -ruN /tmp/sos-code-article9.5/sos/umem_vmm.h sos-code-article9.5/sos/umem_vmm.h
--- /tmp/sos-code-article9.5/sos/umem_vmm.h	2006-01-22 12:48:31.000000000 +0100
+++ sos-code-article9.5/sos/umem_vmm.h	2006-03-19 12:21:03.000000000 +0100
@@ -285,6 +285,28 @@
 
 
 /**
+ * Get the current effective address space. This may be the "normal"
+ * address space of the current thread, but not necessarilly: this
+ * might be the address space of just another process ! This may
+ * happen if a kernel thread of process P wants to access the address
+ * space of another process P2. This might also be NULL (no access to
+ * user space needed).
+ */
+struct sos_umem_vmm_as * sos_umem_vmm_get_current_as(void);
+
+
+/**
+ * Change the current effective address space, eventually
+ * reconfiguring the MMU. This will increase the owning process's
+ * reference count of the given AS, and decrease that of the previous
+ * AS (when different).
+ *
+ * @param as may be NULL (no need to access user space)
+ */
+sos_ret_t sos_umem_vmm_set_current_as(struct sos_umem_vmm_as * as);
+
+
+/**
  * Create a new, empty, address space
  *
  * @param owner The process that will own the new address space
@@ -297,17 +319,19 @@
 
 
 /**
- * Create a new address space, copy of the current thread's address
+ * Create a new address space, copy of the model_as address
  * space. All the translations belonging to private mappings are
  * marked 'read-only' to activate the "copy-on-write" semantics.
  *
- * @param owner The process that will hold the new address space
+ * @param model_as The address space to copy
+ * @param for_owner The process that will hold the new address space
  *
  * @note automatically calls
  * sos_thread_prepare_user_space_access()/sos_thread_end_user_space_access()
  */
 struct sos_umem_vmm_as *
-sos_umem_vmm_duplicate_current_thread_as(struct sos_process *owner);
+sos_umem_vmm_duplicate_as(struct sos_umem_vmm_as * model_as,
+			  struct sos_process *for_owner);
 
 
 /**
diff -ruN /tmp/sos-code-article9.5/userland/crt_asm.S sos-code-article9.5/userland/crt_asm.S
--- /tmp/sos-code-article9.5/userland/crt_asm.S	1970-01-01 01:00:00.000000000 +0100
+++ sos-code-article9.5/userland/crt_asm.S	2006-03-19 12:21:04.000000000 +0100
@@ -0,0 +1,36 @@
+/* Copyright (C) 2006  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.
+*/
+#define ASM_SOURCE 1
+
+.file "crt_asm.S"
+
+.text
+
+/* The function to call: "C" code start routine (defined in crt.c) */
+.extern _cmain
+
+/* User program entry point */
+.globl _start
+        .type   _start, @function
+_start:
+	pushl %esp
+	call _cmain /* Hello "C" world ! */
+	addl $4, %esp
+
+	/* We should never reach this line */
+	lcall *0 /* Crash the process */
diff -ruN /tmp/sos-code-article9.5/userland/crt.c sos-code-article9.5/userland/crt.c
--- /tmp/sos-code-article9.5/userland/crt.c	2006-01-22 12:48:31.000000000 +0100
+++ sos-code-article9.5/userland/crt.c	2006-03-19 12:21:04.000000000 +0100
@@ -27,22 +27,111 @@
 
 #include <hwcore/swintr.h>
 #include <string.h>
+
+#include "libc.h" /* putenv */
 #include "crt.h"
 
 /**
+ * Macro used to retrieve the 2 parameters passed to any new thread
+ * (architecture-dependent)
+ */
+#define GET_THREAD_PARAMETERS(param1, param2) \
+  asm volatile ("movl %%eax, %0 ; movl %%ebx, %1" \
+                : "=g"(param1), "=g"(param2) : : "%eax", "%ebx" )
+
+
+/**
+ * Helper function to retrieve the arg array from the parameter area
+ * sent by the parent.
+ *
+ * Format of the parent's args area:
+ *   Number of arguments
+ *   Offset of arg 0 (program's name)
+ *   Offset of arg 1
+ *   ...
+ *   Offset of arg N (last argument)
+ *   NULL
+ *   Offset of environnement variable 0
+ *   Offset of environnement variable 1
+ *   ...
+ *   Offset of environnement variable N
+ *   NULL
+ *   Contents of arg 0 with terminal \0
+ *   Contents of arg 1 with terminal \0
+ *   ...
+ *   Contents of arg N with terminal \0
+ *   Contents of env 0 with terminal \0
+ *   Contents of env 1 with terminal \0
+ *   ...
+ *   Contents of env N with terminal \0
+ *
+ * This function simply transforms the N offsets into the real
+ * addresses of the arg X contents (ie it simply adds the base address
+ * of the area).
+ */
+static void unmarshall_argv_envp(void * args,
+				 int * argc,
+				 const char ** * argv,
+				 const char ** * envp)
+{
+  addr_t * offset = (addr_t*) args;
+  *argc = 0;
+  *argv = NULL;
+
+  offset = args;
+
+  /* Get argc */
+  *argc = * (int*) offset;
+  offset ++;
+
+  /* Get base of argv */
+  *argv = (const char **) offset;
+
+  /* Walk the offsets array and transform these offsets into memory
+     addresses */
+  for ( ; 0 != *offset ; offset ++)
+    *offset += (addr_t) args;
+
+  /* Skip the NULL separating argv from envp */
+  offset ++;
+
+  /* Get base of envp */
+  *envp = (const char **) offset;
+
+  /* Walk the offsets array and transform these offsets into memory
+     addresses */
+  for ( ; 0 != *offset ; offset ++)
+    {
+      *offset += (addr_t) args;
+
+      /* Register this environment variable */
+      putenv((char*) *offset);
+    }
+}
+
+
+/**
  * Starter function !
  */
-void _start(void) __attribute__((noreturn));
-void _start(void)
+/** Function called by crt_asm.S:_start to start user program */
+void _cmain(const char* arg_env_area[]) __attribute__((noreturn));
+void _cmain(const char* arg_env_area[])
 {
+
+  /* Will hold the parameters that will be passed to main */
+  const char** argv;
+  const char** envp;
+  int argc;
+
+
   /* This starter function expects a main() function somewhere */
-  extern int main(void);
+  extern int main(int argc, char const* argv[],
+		  char const* envp[]);
 
-  /* Reset the bss section */
-  extern char _bbss, _ebss;
-  memset(& _bbss, 0x0, (& _ebss) - (& _bbss));
+  /* Setup the arguments list and the environment variables */
+  unmarshall_argv_envp (arg_env_area, & argc, & argv, & envp);
 
-  _sos_exit(main());
+  _sos_exit(main(argc, argv, envp));
 }
 
 
@@ -189,10 +278,20 @@
 }
 
 
-int _sos_exec(const char * prog)
+int _sos_getpid()
+{
+  return _sos_syscall0(SOS_SYSCALL_ID_GETPID);
+}
+
+
+int _sos_exec(const char * prog,
+	      void const* args,
+	      size_t arglen)
 {
-  return _sos_syscall2(SOS_SYSCALL_ID_EXEC, (unsigned int)prog,
-		       (unsigned int)strlen(prog));
+  return _sos_syscall4(SOS_SYSCALL_ID_EXEC, (unsigned int)prog,
+		       (unsigned int)strlen(prog),
+		       (unsigned int)args,
+		       (unsigned int)arglen);
 }
 
 
@@ -244,12 +343,10 @@
  */
 static void thread_routine(void)
 {
-  /* NOTE: variables as named registers is a gcc extension */
-  register unsigned long int reg_arg1 asm("%eax");
-  register unsigned long int reg_arg2 asm("%ebx");
+  sos_thread_func_t * func;
+  unsigned long int arg;
 
-  sos_thread_func_t * func = (sos_thread_func_t*)reg_arg1;
-  unsigned long int arg = reg_arg2;
+  GET_THREAD_PARAMETERS(func, arg);
 
   func(arg);
   _sos_exit(0);
diff -ruN /tmp/sos-code-article9.5/userland/crt.h sos-code-article9.5/userland/crt.h
--- /tmp/sos-code-article9.5/userland/crt.h	2006-01-22 12:48:31.000000000 +0100
+++ sos-code-article9.5/userland/crt.h	2006-03-19 12:21:04.000000000 +0100
@@ -105,6 +105,7 @@
  */
 int _sos_bochs_write(const char *str, unsigned length);
 
+
 /**
  * Syscall to duplicate the current running process
  */
@@ -112,10 +113,21 @@
 
 
 /**
+ * Syscall to retrieved the current process's PID
+ */
+int _sos_getpid(void);
+
+
+/**
  * Syscall to re-initialize the address space of the current process
  * with that of the program 'progname'
- */
-int _sos_exec(const char * prog);
+ *
+ * The args argument is the address of an array that contains the
+ * arguments and environnement variables for the new process.
+ */
+int _sos_exec(const char * prog,
+	      void const* args,
+	      size_t arglen);
 
 
 /**
diff -ruN /tmp/sos-code-article9.5/userland/envtest.c sos-code-article9.5/userland/envtest.c
--- /tmp/sos-code-article9.5/userland/envtest.c	1970-01-01 01:00:00.000000000 +0100
+++ sos-code-article9.5/userland/envtest.c	2006-03-19 12:21:04.000000000 +0100
@@ -0,0 +1,16 @@
+#include <libc.h>
+
+int main(int argc, char * argv[], char *envp[])
+{
+  int i;
+  char **e;
+
+  printf("Nargs: %d\n", argc);
+  for (i = 0 ; i < argc ; i ++)
+    printf("  arg[%d] = |%s|\n", i, argv[i]);
+
+  for (e = envp ; e && *e ; e ++)
+    printf("  ENV |%s|\n", *e);
+
+  return 0;
+}
diff -ruN /tmp/sos-code-article9.5/userland/fstest.c sos-code-article9.5/userland/fstest.c
--- /tmp/sos-code-article9.5/userland/fstest.c	2006-01-22 12:48:31.000000000 +0100
+++ sos-code-article9.5/userland/fstest.c	2006-03-19 12:21:04.000000000 +0100
@@ -24,7 +24,7 @@
 
 #include "fstest_utils.h"
 
-#define OPEN_BASE_FD 3
+static const int OPEN_BASE_FD = 3;
 
 /**
  * @file fstest.c
diff -ruN /tmp/sos-code-article9.5/userland/ldscript.lds sos-code-article9.5/userland/ldscript.lds
--- /tmp/sos-code-article9.5/userland/ldscript.lds	2006-01-22 12:48:32.000000000 +0100
+++ sos-code-article9.5/userland/ldscript.lds	2006-03-19 12:21:04.000000000 +0100
@@ -56,7 +56,7 @@
     }
 
     /* Beginning of the BSS section (global uninitialized data) */
-    .bss SIZEOF(.rodata) + ADDR(.rodata) :
+    .bss ALIGN(SIZEOF(.rodata) + ADDR(.rodata), 4096) :
     {   PROVIDE(bbss = .);
 	PROVIDE(_bbss = .);
 	*(.bss)
diff -ruN /tmp/sos-code-article9.5/userland/libc.c sos-code-article9.5/userland/libc.c
--- /tmp/sos-code-article9.5/userland/libc.c	2006-01-22 12:48:32.000000000 +0100
+++ sos-code-article9.5/userland/libc.c	2006-03-19 12:21:04.000000000 +0100
@@ -34,9 +34,178 @@
 }
 
 
-int exec(const char *progname)
+pid_t getpid()
 {
-  return _sos_exec(progname);
+  return _sos_getpid();
+}
+
+
+/**
+ * Helper to transform the array of argv and envp into an area
+ * suitable for the crt.c:unmarshall_argv_envp() function.
+ * @see crt.c for the format of this array.
+ */
+static void const* marshall_execve_parameters(char* const argv[],
+					      char* const envp[],
+					     /*out*/size_t * sz)
+{
+  size_t i, i_offset, count_argv, count_envp, size;
+  char * str;
+  void * result;
+  addr_t * offset;
+
+  *sz = 0;
+
+  /* Count the number of argv parameters and the size to allocate */
+  for (count_argv = 0, size = 0 ; argv && argv[count_argv] != NULL ; count_argv ++)
+    size += strlen(argv[count_argv]) + 1 /* trailing \0 */;
+
+  /* Count the number of envp parameters and the size to allocate */
+  for (count_envp = 0 ; envp && envp[count_envp] != NULL ; count_envp ++)
+    size += strlen(envp[count_envp]) + 1 /* trailing \0 */;
+
+  size += (count_argv + count_envp + 3) * sizeof(addr_t);
+  result = malloc(size);
+  if (NULL == result)
+    return NULL;
+
+  /* Build the array of offsets and the string contents */
+  offset   = (addr_t*)result;
+  str      = (char*) & offset[count_argv + count_envp + 3];
+  i_offset = 0;
+
+  /* First element of the array is the number of argv entries */
+  offset [i_offset++] = count_argv;
+
+  /* Marshall the argv array */
+  for (i = 0 ; i < count_argv ; i ++, i_offset ++)
+    {
+      char const* c;
+
+      offset[i_offset] = (void*)str - result;
+
+      for (c = argv[i] ; *c ; c ++, str ++)
+	*str = *c;
+      *str = '\0'; str ++;
+    }
+
+  /* The NULL between argv and envp */
+  offset [i_offset++] = 0;
+
+  for (i = 0 ; i < count_envp ; i ++, i_offset ++)
+    {
+      char const* c;
+
+      offset[i_offset] = (void*)str - result;
+
+      for (c = envp[i] ; *c ; c ++, str ++)
+	*str = *c;
+      *str = '\0'; str ++;
+    }
+
+  /* Final NULL */
+  offset[count_argv + count_envp + 2] = 0;
+  *sz = size;
+
+  return result;
+}
+
+
+int execve(const  char  *filename,
+	   char  *const argv [],
+	   char *const envp[])
+{
+  void const* args_area;
+  size_t args_sz;
+  int retval;
+
+  args_area = marshall_execve_parameters(argv, envp, & args_sz);
+
+  retval = _sos_exec(filename,
+		     args_area, args_sz);
+
+  if (NULL != args_area)
+    free((void*)args_area);
+
+  return retval;
+}
+
+
+int execv(const char  * filename,
+	  char  * const argv [])
+{
+  return execve(filename, argv, environ);
+}
+
+
+static int compute_nargs(va_list ap)
+{
+  int nargs = 0;
+  while (va_arg(ap, char*))
+    nargs ++;
+  return nargs;
+}
+
+
+static int vexec(const  char  *path, va_list ap, int envp_in_list)
+{
+  int retval;
+  char ** vector, **entry;
+  char *str;
+  char ** envp;
+
+  size_t nargs = compute_nargs(ap);
+
+  /* Allocate the char* vector */
+  vector = malloc((nargs + 1)*sizeof(char*));
+  if (! vector)
+    return -1;
+
+  /* Fill it */
+  entry = vector;
+  while ((str = va_arg(ap, char*)) != NULL)
+    {
+      *entry = str;
+      entry ++;
+    }
+  *entry = NULL;
+
+  if (envp_in_list)
+    envp = (char**)va_arg(ap, char*);
+  else
+    envp = environ;
+
+  retval = execve(path, vector, envp);
+  free(vector);
+  return retval;
+}
+
+
+int  execl(const  char  *path, ...)
+{
+  int retval;
+  va_list ap;
+  va_start(ap, path);
+  retval = vexec(path, ap, FALSE);
+  va_end(ap);
+  return retval;
+}
+
+
+int  execle(const  char  *path, ...)
+{
+  int retval;
+  va_list ap;
+  va_start(ap, path);
+  retval = vexec(path, ap, TRUE);
+  va_end(ap);
+  return retval;
+}
+
+
+int exec(char  * const filename)
+{
+  return execl(filename, filename, NULL);
 }
 
 
@@ -53,6 +222,224 @@
 }
 
 
+/**
+ * Max number of environment variables
+ */
+#define SOS_MAX_ENVVARS 1024
+
+char **environ = NULL;
+
+
+/**
+ * Compare the keys of two strings of the form "key=val"
+ */
+static int equal_key(const char *e1,
+		     const char *e2)
+{
+  for ( ; e1 && (*e1) && e2 && (*e2) && (*e1 == *e2) ; e1++, e2++)
+    if (*e1 == '=')
+      return TRUE;
+
+  return FALSE;
+}
+
+
+/**
+ * Helper function to register an environment variable
+ */
+static int registerenv(char * string, int can_overwrite)
+{
+  int i;
+  char ** overwritten;
+
+  /* The first time: allocate the environ table */
+  if (! environ)
+    {
+      environ = malloc(SOS_MAX_ENVVARS * sizeof(char*));
+      if (! environ)
+	return 0;
+
+      memset(environ, 0x0, SOS_MAX_ENVVARS * sizeof(char*));
+    }
+
+  /* Walk the environment variables */
+  overwritten = NULL;
+  for (i = 0 ; i < SOS_MAX_ENVVARS ; i ++)
+    {
+      /* Reached end of list ? */
+      if (! environ[i])
+	break;
+
+      /* Variable already present ? */
+      if (equal_key(environ[i], string))
+	overwritten = & environ[i];
+    }
+
+  if (overwritten)
+    {
+      /* Not allowed to overwrite it ! */
+      if (! can_overwrite)
+	return -1;
+
+      /* Overwrite allowed: do it now */
+      free(*overwritten);
+      *overwritten = string;
+      return 0;
+    }
+
+  /* Must add the variable */
+
+  /* No place left ? */
+  if (i >= SOS_MAX_ENVVARS)
+    return -1;
+
+  /* Ok, add it at the end */
+  environ[i] = string;
+  return 0;
+}
+
+
+/**
+ * @return TRUE when the key of the "keyvalpair" pair is "key"
+ */
+static int key_is(char * keyvalpair, const char * key,
+		  char ** /*out*/value)
+{
+  for ( ; keyvalpair && *keyvalpair && key && *key ; keyvalpair ++, key ++)
+    if (*key != *keyvalpair)
+      break;
+
+  if (value)
+    *value = keyvalpair + 1;
+
+  return (keyvalpair && (*keyvalpair == '=') && key && (*key == '\0'));
+}
+
+
+int putenv(char * string)
+{
+  char * str;
+  if (! string)
+    return -1;
+
+  str = strdup(string);
+  if (! str)
+    return -1;
+
+  return registerenv(string, TRUE);
+}
+
+
+int setenv(const char *name, const char *value, int overwrite)
+{
+  char * str, * c;
+  size_t sz_name, sz_value;
+
+  if (!name || !value)
+    return -1;
+
+  /* Allocate space for the "name=value" string */
+  sz_name = strlen(name);
+  sz_value = strlen(value);
+  str = malloc(sz_name + 1 + sz_value + 1);
+  if (! str)
+    return -1;
+
+  /* sprintf(str, "%s=%s", name, value); */
+  c = str;
+  *c = '\0';
+  strzcpy(c, name, sz_name+1); c += sz_name;
+  strzcpy(c, "=", 2); c += 1;
+  strzcpy(c, value, sz_value+1);
+
+  return registerenv(str, overwrite);
+}
+
+
+char *getenv(const char *name)
+{
+  int i;
+
+  if (! environ)
+    return NULL;
+
+  for (i = 0 ; i < SOS_MAX_ENVVARS ; i ++)
+    {
+      char *value;
+
+      /* Reached end of list ? */
+      if (! environ[i])
+	return NULL;
+
+      /* Variable already present ? */
+      if (key_is(environ[i], name, & value))
+	return value;
+    }
+
+  return NULL;
+}
+
+
+void unsetenv(const char *name)
+{
+  int i;
+  char ** entry, ** last_entry;
+
+  if (! environ)
+    return;
+
+  /* Walk the environment variables */
+  entry = last_entry = NULL;
+  for (i = 0 ; i < SOS_MAX_ENVVARS ; i ++)
+    {
+      /* Reached end of list ? */
+      if (! environ[i])
+	break;
+
+      /* Variable already present ? */
+      if (key_is(environ[i], name, NULL))
+	entry = & environ[i];
+      else if (entry)
+	last_entry = & environ[i];
+    }
+
+  /* Found nothing ! */
+  if (! entry)
+    return;
+
+  /* Found it: erase it... */
+  free(*entry);
+  *entry = NULL;
+
+  /* ... and replace it with the last entry */
+  if (last_entry)
+    {
+      *entry = *last_entry;
+      *last_entry = NULL;
+    }
+}
+
+
+int clearenv(void)
+{
+  int i;
+
+  if (! environ)
+    return 0;
+
+  for (i = 0 ; i < SOS_MAX_ENVVARS ; i ++)
+    {
+      if (! environ[i])
+	break;
+
+      free(environ[i]);
+      environ[i] = NULL;
+    }
+
+  return 0;
+}
+
+
 
 int munmap(void * start, size_t length)
 {
diff -ruN /tmp/sos-code-article9.5/userland/libc.h sos-code-article9.5/userland/libc.h
--- /tmp/sos-code-article9.5/userland/libc.h	2006-01-22 12:48:32.000000000 +0100
+++ sos-code-article9.5/userland/libc.h	2006-03-19 12:21:04.000000000 +0100
@@ -40,10 +40,28 @@
 
 
 /**
+ * Retrieves the current process's PID
+ */
+pid_t getpid(void);
+
+
+/**
  * Function to re-initialize the address space of the current process
  * with that of the program 'progname'
  */
-int exec(const char *progname);
+int execve(const  char  *filename,
+	   char  *const argv [],
+	   char *const envp[]);
+int execv(const char  * filename,
+	  char  * const argv []);
+int execl(const  char  *path, ...);
+int execle(const  char  *path, ...);
+
+
+/**
+ * Equivalent to execl(filename, filename);
+ */
+int exec(char * const filename);
 
 
 int nanosleep(unsigned long int sec,
@@ -54,6 +72,18 @@
 
 
 /**
+ * Environment variables management API
+ */
+extern char **environ; /** Needed by execl */
+
+int putenv(char * string);
+int setenv(const char *name, const char *value, int overwrite);
+char *getenv(const char *name);
+void unsetenv(const char *name);
+int clearenv(void);
+
+
+/**
  * These flags (for mmap API) MUST be consistent with those defined in
  * paging.h and umem_vmm.h
  */
diff -ruN /tmp/sos-code-article9.5/userland/Makefile sos-code-article9.5/userland/Makefile
--- /tmp/sos-code-article9.5/userland/Makefile	2006-01-22 12:48:31.000000000 +0100
+++ sos-code-article9.5/userland/Makefile	2006-03-19 12:21:04.000000000 +0100
@@ -29,8 +29,8 @@
 
 -include .mkvars
 
-PROGS := init myprog1 myprog2 myprog3 myprog4 myprog5 myprog6 \
-         myprog7 myprog8 myprog9 myprog10 myprog11 myprog12   \
+PROGS := init myprog1 myprog2 myprog3 myprog4 myprog5 myprog6		\
+         envtest myprog7 myprog8 myprog9 myprog10 myprog11 myprog12	\
 	 myprog13 myprog14 banner fstest chartest blktest shell
 
 # Build dependencies of the programs
@@ -39,7 +39,7 @@
 chartest: fstest_utils.o
 blktest: fstest_utils.o
 shell: fstest_utils.o
-$(PROGS) : % : %.o crt.o libc.a
+$(PROGS) : % : %.o crt.o crt_asm.o libc.a
 
 PWD := $(shell pwd | sed 's/"/\\\"/g;s/\$$/\\\$$/g')
 
diff -ruN /tmp/sos-code-article9.5/userland/shell.c sos-code-article9.5/userland/shell.c
--- /tmp/sos-code-article9.5/userland/shell.c	2006-01-22 12:48:31.000000000 +0100
+++ sos-code-article9.5/userland/shell.c	2006-03-19 12:21:04.000000000 +0100
@@ -303,8 +303,10 @@
       printf("Could not fork (%d)\n", retval);
       return -1;
     }
-  retval = exec(argv[1]);
+  printf("Created new process %d\n", getpid());
+  retval = execv(argv[1], & argv[1]);
   printf("Could not exec %s: error %d\n", argv[1], retval);
+  exit(0);
   return 0;
 }
 
@@ -605,10 +607,23 @@
   char **argv;
 
   for (c = cmd; *c != '\0'; c++)
-    if (*c == ' ')
-      argc++;
+    {
+      if (*c == ' ')
+	{
+	  /* Skip all spaces */
+	  while (*c && *c == ' ')
+	    c++;
+
+	  /* Reached the end of the command line ? */
+	  if (! *c)
+	    break;
+
+	  argc++;
+	}
+    }
 
-  argv = malloc (argc * sizeof(char*));
+  argv = malloc ((argc+1) * sizeof(char*));
+  argv[argc] = NULL; /* To be compatible with execv(e) */
   if (argv == NULL)
     return;
 
@@ -619,6 +634,9 @@
 	c++;
       *c = '\0';
       c++;
+      /* Skip spaces */
+      while (*c && *c == ' ')
+	c++;
     }
 
   if (! strcmp (argv[0], "uname"))
@@ -676,7 +694,28 @@
     {
       shell_partdump (argc, argv);
     }
-
+  else if (! strcmp(argv[0], "getenv"))
+    {
+      if (argc > 1)
+	printf("%s\n", getenv(argv[1]));
+      else
+	printf("Variable name missing\n");
+    }
+  else if (! strcmp(argv[0], "setenv"))
+    {
+      if (argc > 2)
+	printf("retval=%d\n",
+	       setenv(argv[1], argv[2], TRUE));
+      else
+	printf("Variable name/value missing\n");
+    }
+  else if (! strcmp(argv[0], "unsetenv"))
+    {
+      if (argc > 1)
+	unsetenv(argv[1]);
+      else
+	printf("Variable name missing\n");
+    }
   else
     printf ("Command not found\n");
 
diff -ruN /tmp/sos-code-article9.5/userland/string.c sos-code-article9.5/userland/string.c
--- /tmp/sos-code-article9.5/userland/string.c	2006-01-22 12:48:32.000000000 +0100
+++ sos-code-article9.5/userland/string.c	2006-03-19 12:21:04.000000000 +0100
@@ -18,6 +18,7 @@
    USA. 
 */
 #include "string.h"
+#include "libc.h"
 
 /* For an optimized version, see BSD sources ;) */
 void *memcpy(void *dst0, const void *src0, register unsigned int size)
@@ -115,6 +116,34 @@
   return res;
 }
 
+
+char *strndup(const char *s, size_t n)
+{
+  char * result;
+  n = strnlen(s, n);
+  result = malloc(n+1);
+  if (! result)
+    return NULL;
+
+  strzcpy(result, s, n+1);
+  return result;
+}
+
+
+char *strdup(const char *s)
+{
+  char * result;
+  size_t n = strlen(s);
+
+  result = malloc(n+1);
+  if (! result)
+    return NULL;
+
+  strzcpy(result, s, n+1);
+  return result;
+}
+
+
 int strcmp(register const char *s1, register const char *s2)
 {
   while (*s1 == *s2++)
@@ -208,5 +237,3 @@
 {
   return strtol(nptr, NULL, 10);
 }
-
-
diff -ruN /tmp/sos-code-article9.5/userland/string.h sos-code-article9.5/userland/string.h
--- /tmp/sos-code-article9.5/userland/string.h	2006-01-22 12:48:32.000000000 +0100
+++ sos-code-article9.5/userland/string.h	2006-03-19 12:21:04.000000000 +0100
@@ -53,12 +53,14 @@
  */
 char *strzcat (char *dest, const char *src,
                const size_t len);
+
+char *strndup(const char *s, size_t n);
+char *strdup(const char *s);
  
 int strcmp(register const char *s1, register const char *s2 );
 int strncmp(register const char *s1, register const char *s2,
 	    register int len );
 
-
 #define islower(c)  (('a' <= (c)) && ((c) <= 'z'))
 #define isupper(c)  (('A' <= (c)) && ((c) <= 'Z'))
 #define isdigit(c)  (('0' <= (c)) && ((c) <= '9'))
@@ -72,5 +74,4 @@
 long long atoll(const char *nptr);
 long atol(const char *nptr);
 
-
 #endif /* _SOS_LIBC_STRING_H_ */
diff -ruN /tmp/sos-code-article9.5/userland/types.h sos-code-article9.5/userland/types.h
--- /tmp/sos-code-article9.5/userland/types.h	2006-01-22 12:48:32.000000000 +0100
+++ sos-code-article9.5/userland/types.h	2006-03-19 12:21:04.000000000 +0100
@@ -25,8 +25,9 @@
 
 typedef unsigned int size_t;
 
-typedef int pid_t;
+typedef unsigned int addr_t;
 typedef int ptrdiff_t;
+typedef int pid_t;
 typedef signed long int off_t;
 typedef signed long long int loff_t;
 
