/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 )
Line 64 
Line 64 
       copy the file 'fd.img' to a floppy, and boot from it       copy the file 'fd.img' to a floppy, and boot from it
  
  2nd method  2nd method
    => see extra/README to compile with the boot sector we provide (up to    => see extra/README to compile with the boot sector we provide,
       article 2 only), copy the file 'extra/sos_bsect.img' to a floppy,       copy the file 'extra/sos_bsect.img' to a floppy, and boot from
       and boot from it       it
  
 Inside a PC emulator (x86 and non-x86 hosts) Inside a PC emulator (x86 and non-x86 hosts)
Line 78 
Line 78 
 installed: 'apt-get install libsdl1.2-dev' on debian installed: 'apt-get install libsdl1.2-dev' on debian
 testing/unstable). testing/unstable).
  
  
  1/ Grub is installed on the host (x86 hosts only)  1/ Grub is installed on the host (x86 hosts only)
  - - - - - - - - - - - - - - - - - - - - - - - - -  - - - - - - - - - - - - - - - - - - - - - - - - -
  
Line 90 
Line 91 
    qemu: run 'qemu -fda fd.img'    qemu: run 'qemu -fda fd.img'
      If grub hangs while loading the kernel, please go to method 2/      If grub hangs while loading the kernel, please go to method 2/
  
  
  2/ Grub is not installed (all hosts)  2/ Grub is not installed (all hosts)
  - - - - - - - - - - - - - - - - - -  - - - - - - - - - - - - - - - - - -
  
Line 100 
Line 102 
  
    qemu: run 'qemu -fda fd.img'    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   See extra/README to generate a floppy image with the boot sector we
   provide, and:   provide, and:
Line 110 
Line 113 
  
    qemu: run 'qemu -fda extra/sos_qemu.img'    qemu: run 'qemu -fda extra/sos_qemu.img'
  
   NOTE: After article 2, this way of booting is not supported: please   NOTE: This technique assumes that INT 15H is supported by the
   use the method 2/ above.   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 NOTE : recommended versions of the tools
 ---------------------------------------- ----------------------------------------
  - OS           : Linux 2.6.11.7-d2-1 i686  - 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 make     : GNU Make 3.80  - GNU make     : GNU Make 3.80
  
  
 

/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 )
Line 17 
Line 17 
  
 CC=gcc CC=gcc
 LD=ld LD=ld
 CFLAGS  = -Wall -nostdinc -ffreestanding -DKERNEL_SOS CP=cp
 LIBGCC := $(shell $(CC) -print-libgcc-file-name) # To benefit from FP/64bits artihm. STRIP=strip
  CFLAGS  = -Wall -nostdinc -ffreestanding -DKERNEL_SOS -O
  LIBGCC  = $(shell $(CC) -print-libgcc-file-name) # To benefit from FP/64bits artihm.
 OBJECTS = bootstrap/multiboot.o                                 \ OBJECTS = bootstrap/multiboot.o                                 \
           hwcore/idt.o hwcore/gdt.o                             \           hwcore/idt.o hwcore/gdt.o                             \
           hwcore/swintr.o hwcore/swintr_wrappers.o                \           hwcore/swintr.o hwcore/swintr_wrappers.o                \
           hwcore/exception.o hwcore/exception_wrappers.o        \           hwcore/exception.o hwcore/exception_wrappers.o        \
           hwcore/irq.o hwcore/irq_wrappers.o hwcore/i8259.o        \           hwcore/irq.o hwcore/irq_wrappers.o hwcore/i8259.o        \
           hwcore/paging.o                                        \           hwcore/paging.o hwcore/i8254.o                        \
           hwcore/i8254.o drivers/x86_videomem.o drivers/bochs.o        \ 
           hwcore/mm_context.o                                        \           hwcore/mm_context.o                                        \
           sos/kmem_vmm.o sos/kmem_slab.o sos/kmalloc.o                \           sos/kmem_vmm.o sos/kmem_slab.o sos/kmalloc.o                \
Line 37 
Line 38 
           sos/assert.o sos/main.o sos/mouse_sim.o               \           sos/assert.o sos/main.o sos/mouse_sim.o               \
           sos/uaccess.o sos/calcload.o                                \           sos/uaccess.o sos/calcload.o                                \
           sos/umem_vmm.o sos/binfmt_elf32.o                        \           sos/umem_vmm.o sos/binfmt_elf32.o                        \
            drivers/x86_videomem.o drivers/bochs.o                \
           drivers/zero.o drivers/mem.o                                \           drivers/zero.o drivers/mem.o                                \
            sos/hash.o sos/fs.o sos/fs_nscache.o                        \
            drivers/fs_virtfs.o                                        \
           userland/userprogs.kimg           userland/userprogs.kimg
  
 KERNEL_OBJ   = sos.elf KERNEL_OBJ   = sos.elf
  KERNEL_LOAD  = sos.gz
 MULTIBOOT_IMAGE = fd.img MULTIBOOT_IMAGE = fd.img
 PWD := $(shell pwd) PWD := $(shell pwd)
  
 # Main target # Main target
 all: $(MULTIBOOT_IMAGE) all: $(MULTIBOOT_IMAGE)
  
 $(MULTIBOOT_IMAGE): $(KERNEL_OBJ) $(MULTIBOOT_IMAGE): $(KERNEL_LOAD)
  
  $(KERNEL_LOAD): $(KERNEL_OBJ)
          $(CP) $< $<.strip && $(STRIP) -sx $<.strip
          gzip < $<.strip > $@
  
 $(KERNEL_OBJ): $(OBJECTS) ./support/sos.lds $(KERNEL_OBJ): $(OBJECTS) ./support/sos.lds
         $(LD) $(LDFLAGS) -T ./support/sos.lds -o $@ $(OBJECTS) $(LIBGCC)         $(LD) $(LDFLAGS) -T ./support/sos.lds -o $@ $(OBJECTS) $(LIBGCC)
         -nm -C $@ | cut -d ' ' -f 1,3 > sos.map         -nm -C $@ | cut -d ' ' -f 1,3 > sos.map
Line 74 
Line 83 
  
 # Clean directory # Clean directory
 clean: clean:
         $(RM) *.img *.o mtoolsrc *~ menu.txt *.img *.elf *.bin *.map         $(RM) *.img *.o mtoolsrc *~ menu.txt *.img *.elf *.bin *.strip *.map
         $(RM) *.log *.out bochs*         $(RM) *.log *.out bochs* sos.gz
         $(RM) drivers/*.o drivers/*~         $(RM) drivers/*.o drivers/*~
         $(RM) hwcore/*.o hwcore/*~         $(RM) hwcore/*.o hwcore/*~
  
 

/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 )
Line 33 
Line 33 
    i586-gnu) is available. Can be tested on real i486/pentium    i586-gnu) is available. Can be tested on real i486/pentium
    hardware, or on any host that can run an i486/pentium PC emulator    hardware, or on any host that can run an i486/pentium PC emulator
    (bochs or qemu)    (bochs or qemu)
  - kernel loaded by grub, or by a sample bootsector (up to article 2  - kernel loaded by grub or by a sample bootsector
    ONLY) 
    even inside the kernel: no identity-mapping of the physical memory    even inside the kernel: no identity-mapping of the physical memory
    inside the kernel (allows to move virtual mappings of kernel pages    inside the kernel (allows to move virtual mappings of kernel pages
Line 54 
Line 53 
    'all' and 'clean'    'all' and 'clean'
  - bootstrap/ directory: code to load the kernel. Both the stuff  - bootstrap/ directory: code to load the kernel. Both the stuff
    needed for a multiboot-compliant loader (eg grub) AND a bootsector    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.
    systemwide header files, a set of common useful C routines    systemwide header files, a set of common useful C routines
    ("nano-klibc"), and kernel subsystems (kernel memory management,    ("nano-klibc"), and kernel subsystems (kernel memory management,
  
 

/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 )
Line 1 
Line 1 
 SOS -- Simple OS SOS -- Simple OS
 Copyright (C) 2003,2004,2005 The SOS Team (David Decotigny & Thomas Petazzoni) Copyright (C) 2003,2004,2005 The SOS Team (David Decotigny & Thomas Petazzoni)
  
 Version "Article 7 (2nd part)" -- User space virtual memory management Version "Article 8" -- Basic VFS support (read/write/mount/symlink/unlink/mmap)
                                   (mmap/exec/fork/malloc) 
    This program is free software; you can redistribute it and/or    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License    modify it under the terms of the GNU General Public License
  
 

/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 )
Line 47 
Line 47 
  
 #include <sos/types.h> #include <sos/types.h>
  
  
 /* The address of the stack of the bootstrap thread */ /* The address of the stack of the bootstrap thread */
 extern sos_vaddr_t bootstrap_stack_bottom; extern sos_vaddr_t bootstrap_stack_bottom;
 extern sos_size_t bootstrap_stack_size; extern sos_size_t bootstrap_stack_size;
  
  
 /* Types.  */ /* Types.  */
  
 /* The Multiboot header.  */ /* The Multiboot header.  */
  
 

/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 )
(New file) 
Line 1 
  /* 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);
  }
  
 

/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 )
(New file) 
Line 1 
  /* 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
  
 

/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 )
Line 109 
Line 109 
   zero_resource   zero_resource
     = (struct zero_mapped_resource*)     = (struct zero_mapped_resource*)
     sos_umem_vmm_get_mapped_resource_of_vr(vr)->custom_data;     sos_umem_vmm_get_mapped_resource_of_vr(vr)->custom_data;
    
   SOS_ASSERT_FATAL(zero_resource->ref_cnt > 0);   SOS_ASSERT_FATAL(zero_resource->ref_cnt > 0);
   zero_resource->ref_cnt --;   zero_resource->ref_cnt --;
Line 121 
Line 121 
       struct zero_mapped_page *zmp;       struct zero_mapped_page *zmp;
       list_collapse(zero_resource->list_mapped_pages, zmp)       list_collapse(zero_resource->list_mapped_pages, zmp)
         {         {
           /* No need to free the underlying physical pages, since they           /* Unreference the underlying physical page */
              should have been unmapped just before this unref is           sos_physmem_unref_physpage(zmp->ppage_paddr);
              called */ 
         }         }
  
Line 169 
Line 168 
                                   SOS_PAGE_ALIGN_INF(uaddr),                                   SOS_PAGE_ALIGN_INF(uaddr),
                                   TRUE,                                   TRUE,
                                   vr_prot);                                   vr_prot);
  
           return retval;           return retval;
         }         }
     }     }
Line 182 
Line 182 
       ppage_paddr = sos_physmem_ref_physpage_new(FALSE);       ppage_paddr = sos_physmem_ref_physpage_new(FALSE);
       if (! ppage_paddr)       if (! ppage_paddr)
         return -SOS_ENOMEM;         return -SOS_ENOMEM;
        
                               SOS_PAGE_ALIGN_INF(uaddr),                               SOS_PAGE_ALIGN_INF(uaddr),
                               TRUE,                               TRUE,
Line 286 
Line 286 
   zmp->ppage_paddr = ppage_paddr;   zmp->ppage_paddr = ppage_paddr;
  
   list_add_head(mr->list_mapped_pages, zmp);   list_add_head(mr->list_mapped_pages, zmp);
    sos_physmem_ref_physpage_at(ppage_paddr);
   return SOS_OK;   return SOS_OK;
 } }
  
  
 

/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 )
Line 1 
Line 1 
 OBJCOPY=objcopy 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 all: sos_qemu.img
  
Line 31 
Line 33 
 # above) because they share some symbol definitions # above) because they share some symbol definitions
 sos_bsect.elf: bootsect.o compile_kernel sos_bsect.elf: bootsect.o compile_kernel
         $(LD) --warn-common -T ./sos_bsect.lds -o $@ \         $(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: compile_kernel:
         $(MAKE) -C ..         $(MAKE) -C ..
  
 

/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 )
Line 65 
Line 65 
  - for cross-architecture compilation: see above  - for cross-architecture compilation: see above
  - cd to this extra/ directory  - cd to this extra/ directory
  - run 'make'  - run 'make'
  - the floppy image is: sos_bsect.img  - the floppy image is: sos_bsect.img for use with bochs or on a real
  NOTE : SOS will not boot correctly this way after article 2 !    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.
  
 -- --
  
 

/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 )
Line 1 
Line 1 
  
 /* /*
  * @(#) $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 $
  * Auteurs : Thomas Petazzoni & Fabrice Gautier & Emmanuel Marty  * Auteurs : Thomas Petazzoni & Fabrice Gautier & Emmanuel Marty
  *           Jerome Petazzoni & Bernard Cassagne & coffeeman  *           Jerome Petazzoni & Bernard Cassagne & coffeeman
  *           David Decotigny  *           David Decotigny (SOS integration for kernel size detection)
   *           Christopher Goyet (RAM size determination through BIOS int 15H)
  */  */
  
Line 37 
Line 38 
   *   valide   *   valide
   * - Le bootsect verifie que le processeur est du type 386+   * - Le bootsect verifie que le processeur est du type 386+
   * - Il charge le noyau depuis la disquette en memoire a partir de   * - Il charge le noyau depuis la disquette en memoire a partir de
   *   0x1000 (LOAD_ADRESS). Le noyau peut au max tenir sur   *   0x1000 (LOAD_ADRESS). La place dispo est donc 0x9f000 - 0x1000 , soit
   *   SECTORS_TO_LOAD secteurs   *   0x9E000, soit encore 1264 secteurs de 512 octets
   * - Il recopie le noyau (situe en LOAD_ADRESS) vers son adresse   * - Il recopie le noyau (situe en LOAD_ADRESS) vers son adresse
   *   finale (FINAL_ADDRESS = 2Mo). La recopie se fait sur tout l'espace   *   finale (FINAL_ADDRESS = 2Mo). La recopie se fait sur tout l'espace
Line 67 
Line 68 
         /* Pour que gas genere du 16bits, afin que ca marche en realm */         /* Pour que gas genere du 16bits, afin que ca marche en realm */
         .code16         .code16
  
 #define SECTORS_TO_LOAD 128 /* 64 ko */ /* MAX=1264 */ 
  
  * Parametres de la disquette. Comme c'est chiant de faire une  * Parametres de la disquette. Comme c'est chiant de faire une
  * procedure de detection auto, et que ca prend de la place, on fait  * procedure de detection auto, et que ca prend de la place, on fait
Line 88 
Line 87 
                                           copier le bootsecteur */                                           copier le bootsecteur */
 #define LOAD_ADRESS 0x01000            /* 1er chargement du systeme */ #define LOAD_ADRESS 0x01000            /* 1er chargement du systeme */
 #define LOAD_SEG (LOAD_ADRESS>>4)      /* Segment du 1er chargement du */ #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 /* IMPORTANT : Cette valeur DOIT etre identique a l'adresse presente
                dans sos.lds ! */                dans sos.lds ! */
Line 150 
Line 150 
         /* Efface l'ecran */         /* Efface l'ecran */
         movb $0x0, %ah         movb $0x0, %ah
         movb $0x3, %al         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 */         /* Affiche les messages d'attente */
         movw $loadkern, %si         movw $loadkern, %si
Line 313 
Line 327 
         movl %eax, %ss         movl %eax, %ss
         movl $(stack + BOOT_STACK_SIZE), %ebp         movl $(stack + BOOT_STACK_SIZE), %ebp
         movl %ebp, %esp         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         /* Saut vers le noyau. La GDT est en place (flat mode), les
          * selecteurs aussi, a20 est ouverte, et les interruptions sont          * selecteurs aussi, a20 est ouverte, et les interruptions sont
Line 373 
Line 400 
  
      /* quelques messages */      /* quelques messages */
  
 loadkern:  .string      "-= S O S =- : The Simple Operating System \r\n" loadkern:  .string      "This is SOS\r\n"
 check:     .string      "Checking for a 386+ processor... " toobig:    .string        "Image too big\r\n"
  check:     .string      "Checking 386+ processor... "
 need386:   .string      " [FAILED]\r\n" need386:   .string      " [FAILED]\r\n"
 diskerror: .string      "Disk Error\r\n" diskerror: .string      "Disk Error\r\n"
 loading:   .string      "Loading... " loading:   .string      "Loading... "
 haltmsg:   .string      "System Halted\r\n" 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 /*** Les code/données du boot secteur se terminent ICI. le marqueur de
  * fin (aa55) est ajouté automatiquement par le script ld  * fin (aa55) est ajouté automatiquement par le script ld
  * sos_bsect.lds ***/  * sos_bsect.lds ***/
  
 

/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 )
Line 6 
Line 6 
 CC := i586-gnu-gcc CC := i586-gnu-gcc
 LD := i586-gnu-ld LD := i586-gnu-ld
 OBJCOPY := i586-gnu-objcopy OBJCOPY := i586-gnu-objcopy
 CFLAGS += -g STRIP := i586-gnu-strip
  CFLAGS += -g -O
  
 # Configuration of mtools # Configuration of mtools
  
 

/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 )
Line 37 
Line 37 
       /* The load_size symbol contains the size of the area (in       /* The load_size symbol contains the size of the area (in
        * sectors, aka 512 Bytes) that the boot sector should copy from        * sectors, aka 512 Bytes) that the boot sector should copy from
        * the disk. The bss section is not included since it uses 0        * 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) */
       LONG((__e_load - __b_load + 511) >> 9);       SHORT((__e_load - __b_load + 511) >> 9);
  
       /* At offsets 511 and 512, we set the boot sector signature (AA55h) */       /* At offsets 511 and 512, we set the boot sector signature (AA55h) */
  
 

/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 )
Line 15 
Line 15 
    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
    USA.     USA. 
 */ */
 #ifndef _SOS_i8259_H_ #ifndef _SOS_i8254_H_
 #define _SOS_i8259_H_ #define _SOS_i8254_H_
 #include <sos/errno.h> #include <sos/errno.h>
  
Line 32 
Line 32 
 /** Change timer interrupt (IRQ 0) frequency */ /** Change timer interrupt (IRQ 0) frequency */
 sos_ret_t sos_i8254_set_frequency(unsigned int freq); sos_ret_t sos_i8254_set_frequency(unsigned int freq);
  
 #endif /* _SOS_i8259_H_ */ #endif /* _SOS_i8254_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 )
Line 43 
Line 43 
   _v;                                                           \   _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_ */ #endif /* _SOS_IOPORTS_H_ */
  
 

/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 )
Line 152 
Line 152 
   mmctxt->paddr_PD = sos_paging_get_paddr(mmctxt->vaddr_PD);   mmctxt->paddr_PD = sos_paging_get_paddr(mmctxt->vaddr_PD);
   if (mmctxt->paddr_PD == 0)   if (mmctxt->paddr_PD == 0)
     {     {
        sos_kmem_cache_free((sos_vaddr_t) mmctxt->vaddr_PD);
       sos_kmem_cache_free((sos_vaddr_t) mmctxt);       sos_kmem_cache_free((sos_vaddr_t) mmctxt);
       return NULL;       return NULL;
     }     }
Line 160 
Line 161 
   if (SOS_OK != sos_paging_copy_kernel_space(mmctxt->vaddr_PD,   if (SOS_OK != sos_paging_copy_kernel_space(mmctxt->vaddr_PD,
                                              current_mm_context->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);       sos_kmem_cache_free((sos_vaddr_t) mmctxt);
       return NULL;       return NULL;
     }     }
  
 

/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 )
Line 31 
Line 31 
  */  */
 #define SOS_SWINTR_SOS_SYSCALL  0x42 #define SOS_SWINTR_SOS_SYSCALL  0x42
  
  
  
 #include <hwcore/cpu_context.h> #include <hwcore/cpu_context.h>
  
 

/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 )
Line 113 
Line 113 
   if (offset_in_prog + size_to_copy > elf32prog_resource->size)   if (offset_in_prog + size_to_copy > elf32prog_resource->size)
     size_to_copy = elf32prog_resource->size - offset_in_prog;     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       /* Allocate a new page that contains the code/data of the
          program */          program */
       ppage_paddr = sos_physmem_ref_physpage_new(FALSE);       ppage_paddr = sos_physmem_ref_physpage_new(FALSE);
Line 140 
Line 159 
       if (! (access_rights & SOS_VM_MAP_PROT_WRITE))       if (! (access_rights & SOS_VM_MAP_PROT_WRITE))
         return sos_paging_set_prot(upage_uaddr,         return sos_paging_set_prot(upage_uaddr,
                                    access_rights & ~SOS_VM_MAP_PROT_WRITE);                                    access_rights & ~SOS_VM_MAP_PROT_WRITE);
      }
        
   return retval;   return retval;
 } }
  
 

/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 )
Line 25 
Line 25 
  */  */
  
 /* Positive values of the error codes */ /* Positive values of the error codes */
 #define SOS_OK     0   /* No error */ #define SOS_OK            0   /* No error */
 #define SOS_EINVAL 1   /* Invalid argument */ #define SOS_EINVAL        1   /* Invalid argument */
 #define SOS_ENOSUP 2   /* Operation not supported */ #define SOS_ENOSUP        2   /* Operation not supported */
 #define SOS_ENOMEM 3   /* No available memory */ #define SOS_ENOMEM        3   /* No available memory */
 #define SOS_EBUSY  4   /* Object or device still in use */ #define SOS_EBUSY         4   /* Object or device still in use */
 #define SOS_EINTR  5   /* Wait/Sleep has been interrupted */ #define SOS_EINTR         5   /* Wait/Sleep has been interrupted */
 #define SOS_EPERM  6   /* Mutex/files ownership error */ #define SOS_EPERM         6   /* Mutex/files ownership error */
 #define SOS_EFAULT 7   /* Unresolved virtual memory fault */ #define SOS_EFAULT        7   /* Unresolved virtual memory fault */
 #define SOS_ENOENT 8   /* No such file or directory */ #define SOS_ENOENT        8   /* No such file or directory */
 #define SOS_EFATAL 255 /* Internal fatal error */ #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 /* A negative value means that an error occured.  For
  *  example -SOS_EINVAL means that the error was "invalid  *  example -SOS_EINVAL means that the error was "invalid
  
 

/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 )
(New file) 
Line 1 
  /* 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;
  }
  
 

/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 )
(New file) 
Line 1 
  /* 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_ */
  
 

/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 )
(New file) 
Line 1 
  /* 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) );
  }
  
 

/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 )
(New file) 
Line 1 
  /* 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_ */
  
 

/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 )
(New file) 
Line 1 
  /* 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);
  }
  
 

/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 )
(New file) 
Line 1 
  /* 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_ */
  
 

/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 )
Line 16 
Line 16 
    USA.     USA. 
 */ */
  
  #include <sos/errno.h>
  
 /* Include definitions of the multiboot standard */ /* Include definitions of the multiboot standard */
 #include <bootstrap/multiboot.h> #include <bootstrap/multiboot.h>
 #include <hwcore/idt.h> #include <hwcore/idt.h>
Line 42 
Line 44 
 #include <sos/umem_vmm.h> #include <sos/umem_vmm.h>
 #include <sos/binfmt_elf32.h> #include <sos/binfmt_elf32.h>
 #include <drivers/zero.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 /* Helper function to display each bits of a 32bits integer on the
    screen as dark or light carrets */    screen as dark or light carrets */
Line 64 
Line 67 
     }     }
 } }
  
  
 static void clk_it(int intid) static void clk_it(int intid)
 { {
Line 269 
Line 271 
 /* ====================================================================== /* ======================================================================
  * Start the "init" (userland) process  * 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;   sos_ret_t retval;
   struct sos_umem_vmm_as *as_init;   struct sos_umem_vmm_as *as_init;
   struct sos_process *proc_init;   struct sos_process *proc_init;
   struct sos_thread *new_thr;   struct sos_thread *new_thr;
   sos_uaddr_t ustack, start_uaddr;   sos_uaddr_t ustack, start_uaddr;
    struct sos_fs_opened_file * init_root, * init_cwd, * unused_of;
  
   /* Create the new process */   /* Create the new process */
   proc_init = sos_process_create("init", FALSE);   proc_init = sos_process_create("init", FALSE);
Line 283 
Line 287 
     return -SOS_ENOMEM;     return -SOS_ENOMEM;
   as_init = sos_process_get_address_space(proc_init);   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 */   /* Map the 'init' program in user space */
   start_uaddr = sos_binfmt_elf32_map(as_init, "init");   start_uaddr = sos_binfmt_elf32_map(as_init, "init");
   if (0 == start_uaddr)   if (0 == start_uaddr)
Line 290 
Line 332 
       sos_process_unref(proc_init);       sos_process_unref(proc_init);
       return -SOS_ENOENT;       return -SOS_ENOENT;
     }     }
       
   ustack = (SOS_PAGING_TOP_USER_ADDRESS - SOS_DEFAULT_USER_STACK_SIZE) + 1;   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,   retval = sos_dev_zero_map(as_init, &ustack, SOS_DEFAULT_USER_STACK_SIZE,
Line 298 
Line 340 
                             /* PRIVATE */ 0);                             /* PRIVATE */ 0);
   if (SOS_OK != retval)   if (SOS_OK != retval)
     {     {
       sos_bochs_printf("ici 2\n"); 
       return -SOS_ENOMEM;       return -SOS_ENOMEM;
     }     }
Line 312 
Line 353 
                                    SOS_SCHED_PRIO_TS_LOWEST);                                    SOS_SCHED_PRIO_TS_LOWEST);
   if (! new_thr)   if (! new_thr)
     {     {
       sos_bochs_printf("ici 3\n"); 
       return -SOS_ENOMEM;       return -SOS_ENOMEM;
     }     }
Line 325 
Line 365 
 /* ====================================================================== /* ======================================================================
  * The C entry point of our operating system  * 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;   unsigned i;
   sos_paddr_t sos_kernel_core_base_paddr, sos_kernel_core_top_paddr;   sos_paddr_t sos_kernel_core_base_paddr, sos_kernel_core_top_paddr;
   struct sos_time tick_resolution;   struct sos_time tick_resolution;
    struct sos_fs_manager_instance * rootfs;
  
   /* Grub sends us a structure, called multiboot_info_t with a lot of   /* Size of RAM above 1MB. Might be undefined ! */
      precious informations about the system, see the multiboot   unsigned long int upper_mem = 0;
      documentation for more information. */ 
   multiboot_info_t *mbi; 
   mbi = (multiboot_info_t *) addr; 
   /* Setup bochs and console, and clear the console */   /* Setup bochs and console, and clear the console */
   sos_bochs_setup();   sos_bochs_setup();
Line 345 
Line 383 
  
   /* Greetings from SOS */   /* Greetings from SOS */
   if (magic == MULTIBOOT_BOOTLOADER_MAGIC)   if (magic == MULTIBOOT_BOOTLOADER_MAGIC)
     /* Loaded with Grub */     {
     sos_x86_videomem_printf(1, 0,       /* Grub sends us a structure, called multiboot_info_t with a lot of
                             SOS_X86_VIDEO_FG_YELLOW | SOS_X86_VIDEO_BG_BLUE,          precious informations about the system, see the multiboot
                             "Welcome From GRUB to %s%c RAM is %dMB (upper mem = 0x%x kB)",          documentation for more information. */
                             "SOS article 7.5", ',',       multiboot_info_t *mbi = (multiboot_info_t *) arg;
                             (unsigned)(mbi->mem_upper >> 10) + 1, 
                             (unsigned)mbi->mem_upper);       /* 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);
      }
     /* Not loaded with grub */     /* Not loaded with grub, not from an enhanced bootsect */
                             SOS_X86_VIDEO_FG_YELLOW | SOS_X86_VIDEO_BG_BLUE,                             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 */   /* Setup CPU segmentation and IRQ subsystem */
   sos_gdt_subsystem_setup();   sos_gdt_subsystem_setup();
Line 376 
Line 436 
   tick_resolution = (struct sos_time) { .sec=0, .nanosec=10000000UL };   tick_resolution = (struct sos_time) { .sec=0, .nanosec=10000000UL };
   sos_time_subsysem_setup(& tick_resolution);   sos_time_subsysem_setup(& tick_resolution);
  
   /* We need a multiboot-compliant boot loader to get the size of the RAM */   /* We need to know the RAM size */
   if (magic != MULTIBOOT_BOOTLOADER_MAGIC)   if (upper_mem == 0)
       sos_x86_videomem_putstring(20, 0,       sos_x86_videomem_putstring(20, 0,
                                  SOS_X86_VIDEO_FG_LTRED                                  SOS_X86_VIDEO_FG_LTRED
                                    | SOS_X86_VIDEO_BG_BLUE                                    | SOS_X86_VIDEO_BG_BLUE
                                    | SOS_X86_VIDEO_FG_BLINKING,                                    | SOS_X86_VIDEO_FG_BLINKING,
                                  "I'm not loaded with Grub !");                                  "I don't know RAM size ! Load me with Grub...");
       for (;;)       for (;;)
         continue;         continue;
Line 401 
Line 461 
    * Setup physical memory management    * Setup physical memory management
    */    */
  
   /* Multiboot says: "The value returned for upper memory is maximally   SOS_ASSERT_FATAL(SOS_OK
      the address of the first upper memory hole minus 1 megabyte.". It                    == sos_physmem_subsystem_setup((upper_mem<<10) + (1<<20),
      also adds: "It is not guaranteed to be this value." aka "YMMV" ;) */                                                   &sos_kernel_core_base_paddr,
   sos_physmem_subsystem_setup((mbi->mem_upper<<10) + (1<<20),                                                   &sos_kernel_core_top_paddr));
                               & sos_kernel_core_base_paddr, 
                               & sos_kernel_core_top_paddr); 
   /*   /*
    * Switch to paged-memory mode    * Switch to paged-memory mode
Line 492 
Line 550 
      interrupt call the scheduler */      interrupt call the scheduler */
   asm volatile ("sti\n");   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   /* Start the 'init' process, which in turns launches the other
      programs */      programs */
   start_init();   start_init(rootfs);
  
    * We can safely exit from this function now, for there is already    * We can safely exit from this function now, for there is already
    * an idle Kernel thread ready to make the CPU busy working...    * an idle Kernel thread ready to make the CPU busy working...
  
 

/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 )
Line 293 
Line 293 
       /* Transfer the page, considered NON-FREE, to the free list */       /* Transfer the page, considered NON-FREE, to the free list */
       list_delete(nonfree_ppage, ppage_descr);       list_delete(nonfree_ppage, ppage_descr);
       physmem_nonfree_pages --;       physmem_nonfree_pages --;
        
       list_add_head(free_ppage, ppage_descr);       list_add_head(free_ppage, ppage_descr);
  
       /* Indicate that the page is now unreferenced */       /* Indicate that the page is now unreferenced */
  
 

/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 )
Line 26 
Line 26 
  
 #include "process.h" #include "process.h"
  
  #define SOS_PROCESS_MAX_OPENED_FILES  64
 #define SOS_PROCESS_MAX_NAMELEN 32 #define SOS_PROCESS_MAX_NAMELEN       32
  
 /** /**
Line 51 
Line 51 
   /** Reference counter, including threads */   /** Reference counter, including threads */
   sos_count_t            ref_cnt;   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;   struct sos_process     *prev, *next;
 }; };
  
Line 119 
Line 128 
 struct sos_process *sos_process_create(const char *name, struct sos_process *sos_process_create(const char *name,
                                        sos_bool_t do_copy_current_process)                                        sos_bool_t do_copy_current_process)
 { {
    sos_ret_t retval = SOS_OK;
   sos_ui32_t flags;   sos_ui32_t flags;
   struct sos_process *proc;   struct sos_process *proc;
  
Line 129 
Line 139 
   /* proc is already filled with 0 (cache has SOS_KSLAB_CREATE_ZERO   /* proc is already filled with 0 (cache has SOS_KSLAB_CREATE_ZERO
      flag) */      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)   if (do_copy_current_process)
     proc->address_space = sos_umem_vmm_duplicate_current_thread_as(proc);     proc->address_space = sos_umem_vmm_duplicate_current_thread_as(proc);
   else   else
Line 137 
Line 176 
   if (NULL == proc->address_space)   if (NULL == proc->address_space)
     {     {
       /* Error */       /* Error */
       sos_kmem_cache_free((sos_vaddr_t)proc);       retval = -SOS_ENOMEM;
       return NULL;       goto end_create_proc;
  
   if (!name)   if (!name)
Line 159 
Line 198 
  
   /* Mark the process as referenced */   /* Mark the process as referenced */
   proc->ref_cnt = 1;   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;   return proc;
 } }
  
Line 202 
Line 261 
 sos_ret_t sos_process_set_address_space(struct sos_process *proc, sos_ret_t sos_process_set_address_space(struct sos_process *proc,
                                         struct sos_umem_vmm_as *new_as)                                         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)   if (proc->address_space)
     {     {
       sos_ret_t retval = sos_umem_vmm_delete_as(proc->address_space);       sos_ret_t retval = sos_umem_vmm_delete_as(proc->address_space);
Line 214 
Line 281 
 } }
  
  
  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  * Restricted functions
  */  */
Line 254 
Line 398 
 { {
   sos_ui32_t flags;   sos_ui32_t flags;
   sos_ret_t retval;   sos_ret_t retval;
    int fd;
  
   SOS_ASSERT_FATAL(proc->ref_cnt > 0);   SOS_ASSERT_FATAL(proc->ref_cnt > 0);
  
Line 267 
Line 412 
   list_delete(process_list, proc);   list_delete(process_list, proc);
   sos_restore_IRQs(flags);   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 */   /* First: free the user address space */
   retval = sos_umem_vmm_delete_as(proc->address_space);   retval = sos_umem_vmm_delete_as(proc->address_space);
   SOS_ASSERT_FATAL(SOS_OK == retval);   SOS_ASSERT_FATAL(SOS_OK == retval);
  
 

/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 )
Line 37 
Line 37 
  
 #include <sos/errno.h> #include <sos/errno.h>
  
  
  * The definition of an SOS process is opaque. @see process.c  * The definition of an SOS process is opaque. @see process.c
  */  */
 struct sos_process; struct sos_process;
  
 #include <sos/thread.h> #include <sos/thread.h>
  #include <sos/fs.h>
  
  
 /** /**
Line 116 
Line 116 
 sos_process_get_address_space(const struct sos_process *proc); 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  * Restricted functions
  */  */
  
 

/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 )
Line 131 
Line 131 
     return SOS_OK;     return SOS_OK;
  
   /* Reset the CPU time used in the quantuum */   /* 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)))   if (SOS_SCHED_PRIO_IS_RT(sos_thread_get_priority(thr)))
     {     {
Line 185 
Line 185 
   /* Current (user) thread expired its time quantuum ?  A kernel   /* Current (user) thread expired its time quantuum ?  A kernel
      thread never expires because sos_sched_do_timer_tick() below      thread never expires because sos_sched_do_timer_tick() below
      won't update its user_time_spent_in_slice */      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,
       return TRUE;       return TRUE;
  
Line 203 
Line 203 
   if (thread_expired_its_quantuum(current_thread))   if (thread_expired_its_quantuum(current_thread))
     {     {
       /* Reset the CPU time used in the quantuum */       /* 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,
  
       do_yield = TRUE;       do_yield = TRUE;
Line 290 
Line 290 
                    & tick_duration);                    & tick_duration);
  
       /* Update time spent is current timeslice ONLY for a user thread */       /* 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,
     }     }
   else   else
  
 

/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 )
Line 44 
Line 44 
     case SOS_SYSCALL_ID_EXIT:     case SOS_SYSCALL_ID_EXIT:
       {       {
         unsigned int status;         unsigned int status;
  
         retval = sos_syscall_get1arg(user_ctxt, & status);         retval = sos_syscall_get1arg(user_ctxt, & status);
         if (SOS_OK != retval)         if (SOS_OK != retval)
           break;           break;
Line 94 
Line 95 
     case SOS_SYSCALL_ID_EXEC:     case SOS_SYSCALL_ID_EXEC:
       {       {
         struct sos_thread *cur_thr, *new_thr;         struct sos_thread *cur_thr, *new_thr;
          struct sos_process * proc;
         struct sos_umem_vmm_as *new_as;         struct sos_umem_vmm_as *new_as;
         sos_uaddr_t user_str, ustack, start_uaddr;         sos_uaddr_t user_str, ustack, start_uaddr;
         sos_size_t len;         sos_size_t len;
         char * str;         char * str;
  
         cur_thr = sos_thread_get_current();         cur_thr = sos_thread_get_current();
          proc    = cur_thr->process;
  
         /* Make sure the process has exactly 1 thread in it */         /* 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;             retval = -SOS_EBUSY;
             break;             break;
Line 114 
Line 117 
           break;           break;
  
         /* Copy the program name into kernel sppace */         /* Copy the program name into kernel sppace */
         str = (char*)sos_kmalloc(len + 1, 0);         retval = sos_strndup_from_user(& str, user_str, len + 1, 0);
         if (! str)         if (SOS_OK != retval)
           { 
             retval = -SOS_ENOMEM; 
             break; 
           } 
         retval = sos_strzcpy_from_user(str, user_str, len + 1); 
         if (retval < SOS_OK) 
             sos_kfree((sos_vaddr_t)str); 
           }           }
  
         /* Create a new empty address space to map the program */         /* 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);
           {           {
             sos_kfree((sos_vaddr_t)str);             sos_kfree((sos_vaddr_t)str);
Line 162 
Line 158 
  
         /* Now create the user thread */         /* Now create the user thread */
         new_thr = sos_create_user_thread(NULL,         new_thr = sos_create_user_thread(NULL,
                                          cur_thr->process,                                          proc,
                                          0, 0,                                          0, 0,
                                          ustack + SOS_DEFAULT_USER_STACK_SIZE                                          ustack + SOS_DEFAULT_USER_STACK_SIZE
Line 176 
Line 172 
             break;                         break;            
           }           }
  
         sos_process_set_name(cur_thr->process, str);         sos_process_set_name(proc, str);
         /* Switch to this address space */         /* Switch to this address space */
         retval = sos_process_set_address_space(cur_thr->process,         retval = sos_process_set_address_space(proc,
         if (SOS_OK != retval)         if (SOS_OK != retval)
           {           {
Line 195 
Line 191 
       }       }
       break;       break;
  
     case SOS_SYSCALL_ID_MMAP:     case SOS_SYSCALL_ID_FAKEMMAP:
         sos_uaddr_t ptr_hint_uaddr;         sos_uaddr_t ptr_hint_uaddr;
         sos_uaddr_t hint_uaddr;         sos_uaddr_t hint_uaddr;
Line 417 
Line 413 
             retval = -SOS_ENOMEM;             retval = -SOS_ENOMEM;
             break;                         break;            
           }           }
  
       }       }
       break;       break;
  
Line 456 
Line 453 
       }       }
       break;       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:     case SOS_SYSCALL_ID_BOCHS_WRITE:
       {       {
         sos_uaddr_t user_str;         sos_uaddr_t user_str;
Line 465 
Line 1302 
         if (SOS_OK != retval)         if (SOS_OK != retval)
           break;           break;
  
         str = (char*)sos_kmalloc(len + 1, 0);         retval = sos_strndup_from_user(& str, user_str, len + 1, 0);
         if (str)         if (SOS_OK == retval)
             retval = sos_strzcpy_from_user(str, user_str, len+1);             sos_bochs_printf("THR 0x%x: ",
             str[len] = '\0';                              (unsigned)sos_thread_get_current());
             if (SOS_OK == retval)             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; 
               } 
           }           }
         else 
           retval = -SOS_ENOMEM; 
       break;       break;
  
Line 504 
Line 1334 
         if (SOS_OK != retval)         if (SOS_OK != retval)
           break;           break;
  
         str = (char*)sos_kmalloc(len + 1, 0);         str = (char*)sos_kmalloc(len, 0);
           {           {
             int i;             int i;
  
 

/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 )
Line 44 
Line 44 
  */  */
 #define SOS_SYSCALL_ID_FORK        257 /**< Args: NONE, retval={father:child_pd, child:0} */ #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, 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_MPROTECT    261 /**< Args: start_uaddr size access_rights, 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 */ #define SOS_SYSCALL_ID_MRESIZE     262 /**< Args: old_uaddr old_size ptr_new_uaddr new_size flags, retval=errno */
Line 55 
Line 55 
  */  */
 #define SOS_SYSCALL_ID_BRK         263 /**< Args: 0/new_top_heap, retval=top_heap */ #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 */ #define SOS_SYSCALL_ID_BOCHS_WRITE 43  /**< Args: string, retval=num_printed */
  
  
  
 

/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 )
Line 136 
Line 136 
       struct sos_sched_queue *rdy_queue;       struct sos_sched_queue *rdy_queue;
       struct sos_thread     *rdy_prev, *rdy_next;       struct sos_thread     *rdy_prev, *rdy_next;
     } ready;     } ready;
  
     struct 
     { 
       struct sos_time user_time_spent_in_slice; 
     } running; 
  
    struct sos_time user_time_spent_in_slice;
  
  
   /**   /**
    * When a thread in kernel mode is accessing the user space, it may    * When a thread in kernel mode is accessing the user space, it may
  
 

/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 )
Line 46 
Line 46 
  
 /** Generic count of objects */ /** Generic count of objects */
 typedef unsigned int           sos_count_t; typedef unsigned int           sos_count_t;
  /** Generic count of objects (LARGE) */
  typedef unsigned long long int sos_lcount_t;
  
 /* Low-level sizes */ /* Low-level sizes */
 typedef unsigned long long int sos_ui64_t; /**< 32b unsigned */ typedef unsigned long long int sos_ui64_t; /**< 32b unsigned */
  
 

/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 )
Line 47 
Line 47 
  
   KEEP_LABEL(catch_pgflt);   KEEP_LABEL(catch_pgflt);
  
    if (size <= 0)
      return 0;
  
   retval = sos_thread_prepare_user_space_access(NULL,   retval = sos_thread_prepare_user_space_access(NULL,
                                                 (sos_vaddr_t) && catch_pgflt);                                                 (sos_vaddr_t) && catch_pgflt);
   if (SOS_OK != retval)   if (SOS_OK != retval)
Line 110 
Line 113 
 } }
  
  
  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_ret_t sos_memcpy_to_user(sos_uaddr_t user_to,
                              sos_vaddr_t kernel_from,                              sos_vaddr_t kernel_from,
                              sos_size_t size)                              sos_size_t size)
Line 133 
Line 163 
  
   KEEP_LABEL(catch_pgflt);   KEEP_LABEL(catch_pgflt);
  
    if (max_len <= 0)
      return 0;
  
   /* Make sure user is trying to access user space */   /* Make sure user is trying to access user space */
   if (user_str < SOS_PAGING_BASE_USER_ADDRESS)   if (user_str < SOS_PAGING_BASE_USER_ADDRESS)
     return -SOS_EPERM;     return -SOS_EPERM;
Line 179 
Line 212 
  
   KEEP_LABEL(catch_pgflt);   KEEP_LABEL(catch_pgflt);
  
    if (len <= 0)
      return 0;
  
   retval = sos_thread_prepare_user_space_access(NULL,   retval = sos_thread_prepare_user_space_access(NULL,
                                                 (sos_vaddr_t) && catch_pgflt);                                                 (sos_vaddr_t) && catch_pgflt);
   if (SOS_OK != retval)   if (SOS_OK != retval)
  
 

/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 )
Line 42 
Line 42 
  
  
 /** /**
   * 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  * Transfer a bunch of data to the user space of the
  * current_thread->process  * current_thread->process
  *  *
  
 

/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 )
Line 104 
Line 104 
     fi     fi
   done   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   # Try to guess with locate
   if [ ! -d "$GRUBDIR" ] ; then   if [ ! -d "$GRUBDIR" ] ; then
     GRUBDIR=`locate stage2 | head -1 | xargs dirname 2>/dev/null`      GRUBDIR=`locate stage2 | head -1 | xargs dirname 2>/dev/null`
  
   # Look for a correct sbin/grub   # Look for a correct sbin/grub
Line 146 
Line 151 
   mmd $1/system   mmd $1/system
   mmd $1/modules   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
 install (fd0)/boot/grub/stage1 (fd0) (fd0)/boot/grub/stage2 p (fd0)/boot/grub/menu.txt install (fd0)/boot/grub/stage1 (fd0) (fd0)/boot/grub/stage2 p (fd0)/boot/grub/menu.txt
 quit quit
  
 

/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 )
Line 17 
Line 17 
  
 CC=gcc CC=gcc
 AR=ar AR=ar
  CP=cp
  STRIP=strip
 OBJCOPY=objcopy OBJCOPY=objcopy
 CFLAGS  = -Wall -nostdinc -ffreestanding -I. -I.. CFLAGS  = -Wall -nostdinc -ffreestanding -I. -I.. -O
 LIBGCC := $(shell $(CC) -print-libgcc-file-name) # To benefit from FP/64bits artihm. LIBGCC  = $(shell $(CC) -print-libgcc-file-name) # To benefit from FP/64bits artihm.
  
 # Main target # Main target
Line 29 
Line 31 
  
 PROGS := init myprog1 myprog2 myprog3 myprog4 myprog5 myprog6 \ PROGS := init myprog1 myprog2 myprog3 myprog4 myprog5 myprog6 \
          myprog7 myprog8 myprog9 myprog10 myprog11 myprog12   \          myprog7 myprog8 myprog9 myprog10 myprog11 myprog12   \
          myprog13 myprog14 banner          myprog13 myprog14 banner fstest
 # Build dependencies of the programs # Build dependencies of the programs
  fstest: fstest_utils.o
 $(PROGS) : % : %.o crt.o libc.a $(PROGS) : % : %.o crt.o libc.a
  
 PWD := $(shell pwd) PWD := $(shell pwd)
Line 58 
Line 61 
            echo "  = { \"$$f\", &_begin_userprog$$i, &_end_userprog$$i };"  \            echo "  = { \"$$f\", &_begin_userprog$$i, &_end_userprog$$i };"  \
                 >> .userprog$$i.c ;                                         \                 >> .userprog$$i.c ;                                         \
            $(CC) $(CFLAGS) -c .userprog$$i.c -o .userprog$$i.o ;            \            $(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   \
            echo "  . = ALIGN(4096);" >> .userprogs.lds ;                    \            echo "  . = ALIGN(4096);" >> .userprogs.lds ;                    \
            echo "  _begin_userprog$$i = .;" >> .userprogs.lds ;             \            echo "  _begin_userprog$$i = .;" >> .userprogs.lds ;             \
Line 87 
Line 91 
  
 # Clean directory # Clean directory
 clean: clean:
         $(RM) *.o *.a *~ $(PROGS) *.kimg         $(RM) *.o *.a *~ $(PROGS) *.kimg *.strip
  
 

/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 )
Line 121 
Line 121 
 int main() int main()
 { {
   /* Map the x86 text-mode framebuffer in user space */   /* Map the x86 text-mode framebuffer in user space */
   video = mmap(0, 4096,   video = fakemmap(0, 4096,
                PROT_READ | PROT_WRITE,                    PROT_READ | PROT_WRITE,
                MAP_SHARED,                    MAP_SHARED,
                "/dev/mem", 0xb8000);                    "/dev/mem", 0xb8000);
   p1.top_line  = 3;   p1.top_line  = 3;
   p1.str       = str1;   p1.str       = str1;
Line 142 
Line 142 
   p2.direction = -1;   p2.direction = -1;
   _sos_new_thread((sos_thread_func_t*)banner_thread, (void*) & p2, 8192);   _sos_new_thread((sos_thread_func_t*)banner_thread, (void*) & p2, 8192);
  
   _sos_nanosleep(3, 0); 
  
 } }
  
 

/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 )
Line 196 
Line 196 
 } }
  
  
 int _sos_mmap(void ** ptr_hint_addr, size_t len, int prot, int flags, int _sos_fakemmap(void ** ptr_hint_addr, size_t len, int prot, int flags,
               const char *resource_path, loff_t offset)                   const char *resource_path, loff_t offset)
   return _sos_syscall7(SOS_SYSCALL_ID_MMAP,   return _sos_syscall7(SOS_SYSCALL_ID_FAKEMMAP,
                        (unsigned int)resource_path,                        (unsigned int)resource_path,
                        /* offs64_hi */(offset >> 32),                        /* offs64_hi */(offset >> 32),
Line 282 
Line 282 
   return (void*)_sos_syscall1(SOS_SYSCALL_ID_BRK,   return (void*)_sos_syscall1(SOS_SYSCALL_ID_BRK,
                               (unsigned)new_top_address);                               (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);
  }
  
 

/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 )
Line 122 
Line 122 
  * Syscall to map the given resource. Preliminary version without file  * Syscall to map the given resource. Preliminary version without file
  * system support  * system support
  */  */
 int _sos_mmap(void ** ptr_hint_addr, size_t len, int prot, int flags, int _sos_fakemmap(void ** ptr_hint_addr, size_t len, int prot, int flags,
               const char *resource_path, loff_t offset);                   const char *resource_path, loff_t offset);
  
 /** /**
Line 170 
Line 170 
  */  */
 void * _sos_brk(void * new_top_address); 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_ */ #endif /* _SOS_USER_CRT_H_ */
  
 

/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 )
(New file) 
Line 1 
  /* 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;
  }
  
 

/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 )
(New file) 
Line 1 
  /* 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);
  }
  
 

/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 )
(New file) 
Line 1 
  /* 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 = "PASSED"; \
       static char * _str_failed = "FAILED"; \
       int RETVAL = (int)(code); \
       int _verdict = (int)(condition); \
       bochs_printf("L%d %s: %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_ */
  
 

/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 )
Line 27 
Line 27 
  
 int main() int main()
 { {
    bochs_printf("init: Welcome in userland !\n");
  
   /* launch the tests */   /* launch the tests */
   if (fork() == 0)   if (fork() == 0)
     exec("myprog7");     exec("fstest");
  
   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"); 
   if (fork() == 0)   if (fork() == 0)
     exec("banner");     exec("banner");
  
 

/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 )
Line 18 
Line 18 
  
 #include "crt.h" #include "crt.h"
 #include "libc.h" #include "libc.h"
  #include "stdarg.h"
  
  
 void * mmap(void *start, size_t length, int prot , int flags, void * fakemmap(void *start, size_t length, int prot , int flags,
             const char *path, loff_t offset)                 const char *path, loff_t offset)
   /* At kernel side, offset is considered positive */   /* At kernel side, offset is considered positive */
   if (offset < 0)   if (offset < 0)
     return NULL;     return NULL;
  
   if (0 != _sos_mmap(& start, length, prot, flags, path, offset))   if (0 != _sos_fakemmap(& start, length, prot, flags, path, offset))
  
   return start;   return start;
Line 102 
Line 103 
   retval = malloc_heap_top;   retval = malloc_heap_top;
   malloc_heap_top += size;   malloc_heap_top += size;
  
    _sos_brk(malloc_heap_top);
   return retval;   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);
  }
  
 

/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 )
Line 30 
Line 30 
 /** /**
  * The most important function of a C program ! ;)  * 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  * 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  * Function to re-initialize the address space of the current process
  * with that of the program 'progname'  * 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  * These flags (for mmap API) MUST be consistent with those defined in
  * paging.h and umem_vmm.h  * paging.h and umem_vmm.h
Line 64 
Line 71 
  *  - the offset is a signed 64bits, and MUST be >= 0 (error otherwise)  *  - the offset is a signed 64bits, and MUST be >= 0 (error otherwise)
  *  - no errno support  *  - no errno support
  */  */
 void * mmap(void *start, size_t length, int prot , int flags, void * fakemmap(void *start, size_t length, int prot , int flags,
             const char *path, loff_t offset);                 const char *path, loff_t offset);
  
 /** /**
  * Unmap the given user address interval  * 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  * 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")));
  
  
Line 110 
Line 119 
 /** @note MT UNSAFE */ /** @note MT UNSAFE */
 void * malloc (size_t size); 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_ */ #endif /* _SOS_USER_LIBC_H_ */
  
 

/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 )
Line 34 
Line 34 
 { {
   char * zoup;   char * zoup;
  
   zoup = mmap((void*)4096, 8*1024*1024,   zoup = fakemmap((void*)4096, 8*1024*1024,
               PROT_READ | PROT_WRITE,                   PROT_READ | PROT_WRITE,
               MAP_SHARED,                   MAP_SHARED,
               "/dev/zero", 34);                   "/dev/zero", 34);
  
   /* Do some forks to complicate things */   /* Do some forks to complicate things */
  
 

/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 )
Line 37 
Line 37 
   void * moved;   void * moved;
   char * zoup;   char * zoup;
  
   zoup = mmap((void*)4096, 8*1024*1024,   zoup = fakemmap((void*)4096, 8*1024*1024,
               PROT_READ | PROT_WRITE,                   PROT_READ | PROT_WRITE,
               MAP_SHARED,                   MAP_SHARED,
               "/dev/zero", 34);                   "/dev/zero", 34);
  
   /* Do some forks to complicate things */   /* Do some forks to complicate things */
  
 

/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 )
Line 24 
Line 24 
  
  
 /** /**
  * @file myprog10.c  * @file myprog12.c
  * /dev/mem & /dev/kmem tests  * /dev/mem & /dev/kmem tests
  */  */
Line 38 
Line 38 
  
   /* Map the x86 text framebuffer into this process address space in   /* Map the x86 text framebuffer into this process address space in
      shared mode */      shared mode */
   zoup = mmap(0, 4096, PROT_READ | PROT_WRITE,   zoup = fakemmap(0, 4096, PROT_READ | PROT_WRITE,
               MAP_SHARED,                   MAP_SHARED,
               "/dev/mem", 0xb8000);                   "/dev/mem", 0xb8000);
   _sos_syscall1(4004, (unsigned)"Apres mmap video");   _sos_syscall1(4004, (unsigned)"Apres mmap video");
  
Line 55 
Line 55 
  
   /* Map the x86 text framebuffer into this process address space in   /* Map the x86 text framebuffer into this process address space in
      PRIVATE mode */      PRIVATE mode */
   zoup = mmap(0, 4096, PROT_READ | PROT_WRITE,   zoup = fakemmap(0, 4096, PROT_READ | PROT_WRITE,
               0 /* Private */,                   0 /* Private */,
               "/dev/mem", 0xb8000);                   "/dev/mem", 0xb8000);
   _sos_syscall1(4004, (unsigned)"Apres mmap video");   _sos_syscall1(4004, (unsigned)"Apres mmap video");
  
Line 77 
Line 77 
      "private" mode here, this way we can do whatever we like in this      "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      area. If we'd mapped it read/write, this would overwrite kernel
      data/code, causing a crash sooner or later. */      data/code, causing a crash sooner or later. */
   zoup = mmap(0, 100*4096, PROT_READ | PROT_WRITE,   zoup = fakemmap(0, 100*4096, PROT_READ | PROT_WRITE,
               0 /* private */,                   0 /* private */,
               "/dev/kmem", 0x00201000);                   "/dev/kmem", 0x00201000);
   _sos_syscall1(4004, (unsigned)"Apres mmap kernel");   _sos_syscall1(4004, (unsigned)"Apres mmap kernel");
  
  
 

/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 )
Line 39 
Line 39 
 { {
   char * zoup;   char * zoup;
  
   zoup = mmap(0, 8192,   zoup = fakemmap(0, 8192,
               PROT_READ | PROT_WRITE,                   PROT_READ | PROT_WRITE,
               MAP_SHARED,                   MAP_SHARED,
               "/dev/zero", 34);                   "/dev/zero", 34);
  
   /* Do some forks to complicate things */   /* Do some forks to complicate things */
  
 

/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 )
Line 32 
Line 32 
   int i;   int i;
   char *zoup;   char *zoup;
  
   zoup = mmap((void*)4096, 8*1024*1024,   zoup = fakemmap((void*)4096, 8*1024*1024,
               PROT_READ | PROT_WRITE,                   PROT_READ | PROT_WRITE,
               MAP_SHARED,                   MAP_SHARED,
               "/dev/zero", 0x123456789012345ULL);                   "/dev/zero", 0x123456789012345ULL);
  
   switch (fork())   switch (fork())
  
 

/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 )
Line 22 
Line 22 
 #include <debug.h> #include <debug.h>
  
 /** /**
  * @file myprog7.c  * @file myprog9.c
  * mmap/munmap tests  * mmap/munmap tests
  *  *
Line 34 
Line 34 
 { {
   char *zoup;   char *zoup;
  
   zoup = mmap((void*)4096, 8*1024*1024,   zoup = fakemmap((void*)4096, 8*1024*1024,
               PROT_READ | PROT_WRITE,                   PROT_READ | PROT_WRITE,
               MAP_SHARED,                   MAP_SHARED,
               "/dev/zero", 34);                   "/dev/zero", 34);
  
   /* Do a fork() here to complicate things */   /* Do a fork() here to complicate things */
  
 

/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 )
Line 27 
Line 27 
  
 typedef int pid_t; typedef int pid_t;
 typedef int ptrdiff_t; typedef int ptrdiff_t;
  typedef signed long int off_t;
 typedef signed long long int loff_t; typedef signed long long int loff_t;
  
  typedef unsigned int mode_t;
  typedef unsigned long long int dev_t;
  
 #ifndef NULL #ifndef NULL
 #  define NULL ((void*)0) #  define NULL ((void*)0)
 #endif #endif
  
  #ifndef TRUE
  #  define TRUE (!0)
  #endif
  
  #ifndef FALSE
  #  define FALSE (!TRUE)
  #endif
  
 #endif /* _SOS_LIBC_TYPES_H_ */ #endif /* _SOS_LIBC_TYPES_H_ */


Legend:
 
identical lines
Removed from old 
changed lines
 Added in new

File list:
    
../sos-code-article8/INSTALL
    ../sos-code-article8/Makefile
    ../sos-code-article8/README
    ../sos-code-article8/VERSION
    ../sos-code-article8/bootstrap/multiboot.h
    ../sos-code-article8/drivers/fs_virtfs.c
    ../sos-code-article8/drivers/fs_virtfs.h
    ../sos-code-article8/drivers/zero.c
    ../sos-code-article8/extra/Makefile
    ../sos-code-article8/extra/README
    ../sos-code-article8/extra/bootsect.S
    ../sos-code-article8/extra/dot.mkvars
    ../sos-code-article8/extra/sos_bsect.lds
    ../sos-code-article8/hwcore/i8254.h
    ../sos-code-article8/hwcore/ioports.h
    ../sos-code-article8/hwcore/mm_context.c
    ../sos-code-article8/hwcore/swintr.h
    ../sos-code-article8/sos/binfmt_elf32.c
    ../sos-code-article8/sos/errno.h
    ../sos-code-article8/sos/fs.c
    ../sos-code-article8/sos/fs.h
    ../sos-code-article8/sos/fs_nscache.c
    ../sos-code-article8/sos/fs_nscache.h
    ../sos-code-article8/sos/hash.c
    ../sos-code-article8/sos/hash.h
    ../sos-code-article8/sos/main.c
    ../sos-code-article8/sos/physmem.c
    ../sos-code-article8/sos/process.c
    ../sos-code-article8/sos/process.h
    ../sos-code-article8/sos/sched.c
    ../sos-code-article8/sos/syscall.c
    ../sos-code-article8/sos/syscall.h
    ../sos-code-article8/sos/thread.h
    ../sos-code-article8/sos/types.h
    ../sos-code-article8/sos/uaccess.c
    ../sos-code-article8/sos/uaccess.h
    ../sos-code-article8/support/build_image.sh
    ../sos-code-article8/userland/Makefile
    ../sos-code-article8/userland/banner.c
    ../sos-code-article8/userland/crt.c
    ../sos-code-article8/userland/crt.h
    ../sos-code-article8/userland/fstest.c
    ../sos-code-article8/userland/fstest_utils.c
    ../sos-code-article8/userland/fstest_utils.h
    ../sos-code-article8/userland/init.c
    ../sos-code-article8/userland/libc.c
    ../sos-code-article8/userland/libc.h
    ../sos-code-article8/userland/myprog10.c
    ../sos-code-article8/userland/myprog11.c
    ../sos-code-article8/userland/myprog12.c
    ../sos-code-article8/userland/myprog14.c
    ../sos-code-article8/userland/myprog8.c
    ../sos-code-article8/userland/myprog9.c
    ../sos-code-article8/userland/types.h