sos-code-article9.5/drivers/fs_fat.c (1969-12-31 16:00:00.000000000 -0800 )
sos-code-article10/drivers/fs_fat.c (2007-09-04 11:46:49.000000000 -0700 )
(New file) 
Line 1 
  /* Copyright (C) 2007 Anthoine Bourgeois
  
     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 <drivers/bochs.h>
  #include <sos/physmem.h>
  #include <sos/uaccess.h>
  
  #include <sos/fs.h>
  #include <sos/fs_nscache.h>
  
  /**
   * This drivers implements the FAT file system.
   *
   * The FAT file system is integrated in the VFS and allow on SOS the
   * capability of recording datas on a disk.
   * All VFS operations (mount, umount, read, write, seek, stat, mmap...) are
   * implemented in this file.
   *
   * This drivers haves few limitations:
   * - FAT12 not supported
   * - no time stamp on nodes
   * - no long directory entries
   * - the 'rename' VFS operation is not supported
   * Others features should be implemented in this file.
   *
   * The current drivers is implemented from the offical specification:
   * Microsoft Extensible Firmware Initiative FAT32 File System Specification.
   * The document can be found at:
   * http://www.csn.ul.ie/~mel/projects/vm/
   */
  
  /**
   * Note:
   * The FAT word have two significations.
   *
   * First, FAT is the name of the file system for File Allocation Table.
   *
   * Second, FAT is a structure. A cluster table at the begining of the
   * partition. The FAT table maps the data region of the partition by cluster
   * number.
   *
   * It will be specify explicitly if the understanding can be ambigus.
   */
  
  #include "fs_fat.h"
  
  /* ********************************************************
   * FAT structures definitions {{{
   */
  
  /** FAT Type */
  #define FAT12 12
  #define FAT16 16
  #define FAT32 32
  
  /**
   * Converts a FAT type to a string
   */
  static const char *
  sos_fat_type_str (unsigned int type)
  {
    switch (type)
      {
      case FAT12:
        return "FAT12";
      case FAT16:
        return "FAT16";
      case FAT32:
        return "FAT32";
      default:
        return "Unknown";
      }
  }
  
  /** FAT cluster limit
   *
   * The maximum number of a FAT type.
   * These constant are helpful to determine the FAT type.
   *
   * section "FAT Type Determination" in the offical spec.
   */
  #define FAT12_CLUSTER_LIMIT 4085
  #define FAT16_CLUSTER_LIMIT 65525
  
  /** FAT EOC (End Of Cluster) marker
   *
   * In this context the FAT refers to the cluster table.
   * When the cluster contains a end of file then the EOC marker is set in the
   * mapping of this cluster in the FAT.
   */
  #define FAT16_EOC   0xFFF8
  #define FAT32_EOC   0x0FFFFFF8
  
  /** FAT cluster mask
   *
   * In this context the FAT refers to the cluster table.
   * This mask is used for the cluster number in the FAT. The mask prevent
   * number overflow. We can see that the FAT16 number is a 16 bits unsigned integer
   * and the FAT32 number is a 28 bits unsigned integer. The 4 higher
   * significant bits are reserved.
   */
  #define FAT16_CLUSTER_MASK 0xFFFF
  #define FAT32_CLUSTER_MASK 0x0FFFFFFF
  
  /** FAT cluster entry size
   *
   * In this context the FAT refers to the cluster table.
   * The size is in bytes.
   */
  #define FAT16_CLUSTER_ENTRY_SIZE 2
  #define FAT32_CLUSTER_ENTRY_SIZE 4
  
  /** FAT cluster value for the first valid cluster
   *
   * The first valid data cluster have number 2.
   * It's specify in the section "FAT Data Structure" in the official spec.
   */
  #define FAT_FIRST_VALID_CLUSTER 2
  
  /** FAT cluster entry value for a free cluster
   *
   * In this context the FAT refers to the cluster table.
   * This marker specify that the cluster entry (and the cluster) is free and
   * the cluster is available for use.
   */
  #define FAT_FREE_CLUSTER_ENTRY_VALUE 0
  
  /** The FAT16 specific BPB
   *
   * BPB is the BIOS Prameter Block. The BPB is located in the first sector of
   * the partition also named Boot Sector. sos_fat16_BPB and sos_fat32_BPB are
   * specific parts of the FAT types, FAT16 and FAT32, and are include in the
   * sos_fat_boot_sector structure above. The Boot Sector is the
   * entry point of the FAT file system. It gives informations on the file
   * system.
   *
   * FAT FS sturcture:
   * __________________________________________
   * |BS/BPB|FAT struct|    data clusters     | <--- a FAT partition.
   * ------------------------------------------
   */
  struct sos_fat16_BPB {
      sos_ui8_t       BS_DrvNum;                /* Int 0x13 driver number. */
      /* Under real mode operating systems such as MS-DOS, calling INT 0x13
       * would jump into the computer's BIOS code for Low Level Disk Services,
       * which will carry out sector-based disk read or write for the program. */
      sos_ui8_t       BS_Reserved1;       /* Reserved (used by Windows NT). */
      sos_ui8_t       BS_BootSig;         /* Extended boot signature (0x29). */
      sos_ui32_t      BS_VolID;           /* Volume serial number. */
      char            BS_VolLab[11];      /* Volume label. */
      char            BS_FilSysType[8];   /* Always set to the string "FATxx   " */
  } __attribute__((packed));
  
  /** The FAT32 specific BPB */
  struct sos_fat32_BPB {
      sos_ui32_t      BPB_FATSz32;        /* This field is the FAT32 32-bit count of sectors occupied by ONE FAT */
      sos_ui16_t      BPB_ExtFlags;        /* Flags */
      sos_ui16_t      BPB_FSVer;                /* High byte is major revision number. Low byte is minor revision number. */
      sos_ui32_t      BPB_RootClus;        /* This is set to the cluster number of the first cluster of the root directory. */
      sos_ui16_t      BPB_FSInfo;                /* Sector number of FSINFO structure in the reserved area of the FAT32 volume. */
      sos_ui16_t      BPB_BkBootSec;        /* If non-zero, indicates the sector number in the reserved area of */
                                          /* the volume of a copy of the boot record. */
      char            BPB_Reserved[12];        /* Reserved for future expansion. */
  
      struct sos_fat16_BPB fat16_BPB;
  } __attribute__ ((packed));
  
  /** The FAT first sector structure */
  struct sos_fat_boot_sector {
      /* BS (Boot Sector) */
      sos_ui8_t       BS_jmpbBoot[3];        /*    jmpBoot[0] = 0xEB, jmpBoot[1] = 0x??, jmpBoot[2] = 0x90 */
                                          /* or jmpBoot[0] = 0xE9, jmpBoot[1] = 0x??, jmpBoot[2] = 0x?? */
      char            BS_OEMName[8];        /* MSWIN4.1 */
  
      /* BPB (BIOS Parameter Block) */
      sos_ui16_t      BPB_BytsPerSec;        /* Count of bytes per sector: 512, 1024, 2048 or 4096 */
      sos_ui8_t       BPB_SecPerClus;        /* Number of sectors per allocation unit: 1, 2, 4, 8, 16, 32, 64 or 128 */
      sos_ui16_t      BPB_RsvdSecCnt;        /* Number of reserved sectors in the Reserved region of the volume */
                                          /* starting at the first sector of the volume. */
      sos_ui8_t       BPB_NumFATs;        /* The count of FAT data structures on the volume. */
      sos_ui16_t      BPB_RootEntCnt;        /* For FAT32 volumes, this field must be set to 0. */
      sos_ui16_t      BPB_TotSec16;        /* For FAT32 volumes, this field must be 0. */
      sos_ui8_t       BPB_Media;                /* Media type: 0xF0, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE or 0xFF */
      sos_ui16_t      BPB_FATSz16;        /* On FAT32 volumes this field must be 0. */
      sos_ui16_t      BPB_SecPerTrk;        /* Sectors per track for interrupt 0x13. */
      sos_ui16_t      BPB_NumHeads;        /* Number of heads for interrupt 0x13. */
      sos_ui32_t      BPB_HiddSec;        /* Count of hidden sectors preceding the partition that contains this FAT volume. */
      sos_ui32_t      BPB_TotSec32;        /* This field is the new 32-bit total count of sectors on the volume. */
  
      /* BPB specific */
      union {
          struct sos_fat16_BPB fat16_BPB;
          struct sos_fat32_BPB fat32_BPB;
      } BPB_specific;
  } __attribute__ ((packed));
  
  /** Fat Directory entry structure
   *
   * There are only 2 nodes types in FAT: file and directory.
   * A FAT directory is nothing but a "file" composed of a linear list of
   * 32-byte directory entry structures.
   */
  struct sos_fat_directory_entry {
  #define IS_FAT_VALID_CHAR(c) ((c) > 0x20 && (c) != 0x22 && ((c) < 0x2A || (c) > 0x2C) && /*(c) != 0x2E &&*/ (c) != 0x2F && ((c) < 0x3A || (c) > 0x3F) && ((c) < 0x5B || (c) > 0x5D) && (c) != 0x7C)
      char        DIR_Name[11];                /* Short name */
  
      /* File attributes */
  #define ATTR_READ_ONLY  0x01
  #define ATTR_HIDDEN     0x02
  #define ATTR_SYSTEM     0x04
  #define ATTR_VOLUME_ID  0x08
  #define ATTR_DIRECTORY  0x10
  #define ATTR_ARCHIVE    0x20
  #define ATTR_LONG_NAME  (ATTR_READ_ONLY | ATTR_HIDDEN | ATTR_SYSTEM | ATTR_VOLUME_ID)
      sos_ui8_t   DIR_Attr;                /* File attributes */
      sos_ui8_t   DIR_NTRes;                /* Reserved for use by Windows NT */
      sos_ui8_t   DIR_CrtTimeTenth;        /* Millisecond stamp at file creation time */
      sos_ui16_t  DIR_CrtTime;                /* Time file was created. */
      sos_ui16_t  DIR_CrtDate;                /* Date file was created. */
      sos_ui16_t  DIR_LstAccDate;                /* Last access date */
      sos_ui16_t  DIR_FstClusHI;                /* High word of this entry's first cluster number. */
      sos_ui16_t  DIR_WrtTime;                /* Time of last write. */
      sos_ui16_t  DIR_WrtDate;                /* Date of last write. */
      sos_ui16_t  DIR_FstClusLO;                /* Low word of this entry's first cluster number. */
      sos_ui32_t  DIR_FileSize;           /* 32-bit DWORD holding this file's size in bytes. */
  } __attribute__ ((packed));
  
  /** Fat Info strcuture
   *
   * This structure is FAT32 specific. It's currently unused.
   *
   * This structure contains generals informations on the FAT file system. The
   * main information is the current state of the FAT cluster table.
   */
  struct sos_fat_info {
      sos_ui32_t  FSI_LeadSig;                /* Value 0x41615252 */
      char        FSI_Reserved1[480];        /* This field is currently reserved for future expansion. */
      sos_ui32_t  FSI_StrucSig;                /* Value 0x61417272 */
      sos_ui32_t  FSI_Free_Count;                /* Contains the last known free cluster count on the volume */
      sos_ui32_t  FSI_Nxt_Free;                /* It indicates the cluster number at which the driver
                                             should start looking for free clusters */
      char        FSI_Reserved2[12];        /* This field is currently reserved for future expansion. */
      sos_ui32_t  FSI_TrailSig;                /* Value 0xAA550000 */
  } __attribute__ ((packed));
  
  /** All structures before are given by the official spec.
   * ======================================================
   * Now starts internals SOS structures.
   */
  
  /** Fat data structure
   *
   * This structure is filled during the mount operation. These fields are
   * computed from BS/BPB like describe in the official specification. The
   * structure is fundamental for partition use in many VFS operations.
   */
  struct sos_fat_data_structure {
      sos_ui32_t  RootDirSectors;                /* Number of sectors in root directory */
      sos_ui32_t  FATSz;                        /* FAT size */
      sos_ui32_t  FirstDataSector;        /* First data sector */
      sos_ui32_t  TotSec;                        /* Total number of sector in the partition */
      sos_ui32_t  DataSec;                /* Number of data sector in the partition */
      sos_ui32_t  CountOfClusters;        /* Number of cluster in the partition */
      sos_ui32_t  FirstRootDirSecNum;        /* First sector of the root directory */
  
      sos_ui32_t  FAT_Type;                /* FAT type */
      sos_ui32_t  EndOfCluster;                /* End of cluster marker */
  
      struct sos_fat_boot_sector BootSector;
  };
  
  /** Fat mmap page list structure */
  struct sos_fat_mmap_page_list {
      struct sos_fat_mmap_page_list *next;    /* Next page of the file */
  
      sos_luoffset_t offset;              /* Offset in the file. Must be a multiple of PAGE_SIZE */
      sos_vaddr_t mmap_page;              /* The page fill of datas of the file */
  };
  
  /** Fat file informations */
  struct sos_fat_file {
      struct sos_fat_directory_entry dir_entry;
  
      /* Needed variables to map the file */
      struct sos_umem_vmm_mapped_resource mapres;
      sos_count_t num_mappings;
      struct sos_fat_mmap_page_list *list;        /* The list is ordered by offset */
  };
  
  /** The description of the "FAT FS" */
  static struct sos_fs_manager_type fat_type;
  /* }}} */
  
  /* ********************************************************
   * Helper functions {{{
   */
  
  /** Helper macro to get the first cluster of the FS node (subdir / file) as
   * referenced by a directory entry. */
  #define GET_FIRST_CLUSTER(dir_entry) \
          (((dir_entry)->DIR_FstClusHI<<16) | (dir_entry)->DIR_FstClusLO)
  
  /** Helper function to compute the first sector number
   * of the given cluster. */
  static sos_ui64_t
  sos_fat_helper_first_sector_of_cluster(
          struct sos_fat_data_structure *data_struct,
          sos_ui32_t cluster_nr)
  {
      return ((cluster_nr - FAT_FIRST_VALID_CLUSTER) * data_struct->BootSector.BPB_SecPerClus) + data_struct->FirstDataSector;
  }
  
  /** Helper function to compute the cluster containing
   * the given sector. */
  static sos_ui32_t
  sos_fat_helper_cluster_of_sector(
          struct sos_fat_data_structure *data_struct,
          sos_ui64_t sector_nr)
  {
      return ((sector_nr - data_struct->FirstDataSector) / data_struct->BootSector.BPB_SecPerClus) + FAT_FIRST_VALID_CLUSTER;
  }
  
  /** Helper function to compute the FAT offset from the FAT type.
   *
   * In this context the FAT refers to the cluster table.
   * This function compute an offset (in Bytes) in the first FAT cluster table.
   */
  static sos_ui32_t sos_fat_helper_fat_offset(
          struct sos_fat_data_structure *data_struct,
          sos_ui32_t cluster_nr)
  {
    if (data_struct->FAT_Type == FAT16) {
      return cluster_nr * FAT16_CLUSTER_ENTRY_SIZE;
    } else if (data_struct->FAT_Type == FAT32) {
      return cluster_nr * FAT32_CLUSTER_ENTRY_SIZE;
    }
  
    /** FAT12 is unsupported */
    return 0;
  }
  
  /** Helper function to compute the FAT sector number in the cluster table and its offset
   * in this sector for a given cluster. */
  static sos_ret_t
  sos_fat_helper_get_sector_and_offset(
      struct sos_fat_data_structure* data_struct,
      sos_ui32_t cluster_nr,
      sos_ui32_t *ThisFATSecNum,
      sos_ui32_t *ThisFATEntOffset)
  {
      *ThisFATSecNum = data_struct->BootSector.BPB_RsvdSecCnt +
          (sos_fat_helper_fat_offset(data_struct, cluster_nr) / data_struct->BootSector.BPB_BytsPerSec);
      *ThisFATEntOffset =
          sos_fat_helper_fat_offset(data_struct, cluster_nr) % data_struct->BootSector.BPB_BytsPerSec;
  
      return SOS_OK;
  }
  
  /** Helper function to find the first free cluster in
   * the cluster table. */
  static sos_ret_t
  sos_fat_helper_find_free_cluster(
      struct sos_blockdev_instance *block_device,
      struct sos_fat_data_structure *data_struct,
      sos_ui32_t *cluster_nr_result)
  {
      sos_ui32_t cluster_nr;
      sos_ret_t retval;
      sos_ui32_t ThisFATSecNum, ThisFATEntOffset;
      sos_ui32_t FATClusEntryVal;
      sos_size_t sector_size = data_struct->BootSector.BPB_BytsPerSec;
      sos_ui8_t *SecBuf = (sos_ui8_t *) sos_kmalloc(sector_size, 0);
      if (SecBuf == NULL) {
          return -SOS_ENOMEM;
      }
  
      for (cluster_nr = FAT_FIRST_VALID_CLUSTER;
           cluster_nr < data_struct->CountOfClusters;
           cluster_nr++) {
            sos_fat_helper_get_sector_and_offset(data_struct, cluster_nr, &ThisFATSecNum, &ThisFATEntOffset);
  
          if (cluster_nr == FAT_FIRST_VALID_CLUSTER || ThisFATEntOffset == 0) {
              /* In case of we are at beginning of a new sector in the FAT cluster table,
               * we need this sector then we read it in SecBuf variable */
              retval = sos_blockdev_kernel_read(block_device,
                                  ThisFATSecNum*data_struct->BootSector.BPB_BytsPerSec,
                                  (sos_vaddr_t)SecBuf, &sector_size);
              if (retval != SOS_OK) {
                  sos_kfree((sos_vaddr_t) SecBuf);
                  return retval;
              } else if (sector_size != data_struct->BootSector.BPB_BytsPerSec) {
                  sos_kfree((sos_vaddr_t) SecBuf);
                  return -SOS_EIO;
              }
          }
  
          /* SecBuf variable contains a sector of the FAT cluster table
           * then we can get the cluster values */
          if (data_struct->FAT_Type == FAT16) {
              FATClusEntryVal = *((sos_ui16_t *) &SecBuf[ThisFATEntOffset]);
          } else {
              FATClusEntryVal = (*((sos_ui32_t *) &SecBuf[ThisFATEntOffset])) & FAT32_CLUSTER_MASK;
          }
  
          if (FATClusEntryVal == FAT_FREE_CLUSTER_ENTRY_VALUE) {
              sos_kfree((sos_vaddr_t) SecBuf);
              *cluster_nr_result = cluster_nr;
              return SOS_OK;
          }
      }
  
      /* No cluster available */
      return -SOS_EIO;
  }
  
  /** Helper function to set a cluster entry in the FAT cluster table with value.
   *
   * In this context the FAT refers to the cluster table.
   * Updates on disk the cluster entry value in the FAT cluster table with the
   * value in parameter. The cluster number is the cluster_nr label in parameter.
   */
  static sos_ret_t
  sos_fat_helper_set_cluster_value(
          struct sos_blockdev_instance *block_device,
          struct sos_fat_data_structure *data_struct,
          sos_ui32_t cluster_nr,
          sos_ui32_t value)
  {
    sos_ret_t retval;
    sos_ui32_t fat_nr;
    sos_ui32_t ThisFATSecNum, ThisFATEntOffset;
  
    SOS_ASSERT_FATAL(cluster_nr >= FAT_FIRST_VALID_CLUSTER);
  
    sos_fat_helper_get_sector_and_offset(data_struct, cluster_nr, &ThisFATSecNum, &ThisFATEntOffset);
  
    sos_size_t sector_size = data_struct->BootSector.BPB_BytsPerSec;
    sos_ui8_t *SecBuf = (sos_ui8_t *) sos_kmalloc(sector_size, 0);
    if (SecBuf == NULL)
      return -SOS_ENOMEM;
  
    /* Write value on each entries of each FATs */
    for (fat_nr = 0;
          fat_nr < data_struct->BootSector.BPB_NumFATs;
          fat_nr++) {
      /* Read the FAT cluster table sector who contains the value to change.
       * The sector of the FAT is in SecBuf variable */
      retval = sos_blockdev_kernel_read(block_device,
                                      ((fat_nr * data_struct->FATSz) + ThisFATSecNum)*data_struct->BootSector.BPB_BytsPerSec,
                                      (sos_vaddr_t)SecBuf, &sector_size);
      if (retval != SOS_OK) {
          sos_kfree((sos_vaddr_t) SecBuf);
          return retval;
      } else if (sector_size != data_struct->BootSector.BPB_BytsPerSec) {
          sos_kfree((sos_vaddr_t) SecBuf);
          return -SOS_EIO;
      }
  
      /* Change the cluster entry value in the SecBuf variable read below */
      if (data_struct->FAT_Type == FAT16) {
          *((sos_ui16_t *) &SecBuf[ThisFATEntOffset]) = (sos_ui16_t) (FAT16_CLUSTER_MASK & value);
      } else {
          *((sos_ui32_t *) &SecBuf[ThisFATEntOffset]) =
              (*((sos_ui32_t *) &SecBuf[ThisFATEntOffset])) & (~FAT32_CLUSTER_MASK);
          *((sos_ui32_t *) &SecBuf[ThisFATEntOffset]) =
              (*((sos_ui32_t *) &SecBuf[ThisFATEntOffset])) | (FAT32_CLUSTER_MASK & value);
      }
  
      /* Write on disk the modified sector of the FAT */
      retval = sos_blockdev_kernel_write(block_device,
                              ((fat_nr * data_struct->FATSz) + ThisFATSecNum)*data_struct->BootSector.BPB_BytsPerSec,
                              (sos_vaddr_t)SecBuf, &sector_size);
      if (retval != SOS_OK) {
          return retval;
      } else if (sector_size != data_struct->BootSector.BPB_BytsPerSec) {
          return -SOS_EIO;
      }
    }
  
    sos_kfree((sos_vaddr_t) SecBuf);
  
    return SOS_OK;
  }
  
  /** Helper function to find the next cluster in the cluster list of a node
   *
   * In this context the FAT refers to the cluster table.
   * This function read the next data cluster in the FAT cluster table.
   * It reads the FAT sector of the current cluster (cluster_nr in parameter) and
   * takes the cluster value of cluster_nr to get the next cluster of the
   * file/dir. Result is return by next_cluster parameter.
   */
  static sos_ret_t
  sos_fat_helper_find_next_cluster(
          struct sos_blockdev_instance *block_device,
          struct sos_fat_data_structure *data_struct,
          sos_ui32_t cluster_nr,
          sos_ui32_t *next_cluster)
  {
    sos_ret_t retval;
    sos_ui32_t ThisFATSecNum, ThisFATEntOffset;
    sos_ui32_t FATClusEntryVal;
  
    SOS_ASSERT_FATAL(cluster_nr >= FAT_FIRST_VALID_CLUSTER);
  
    sos_size_t sector_size = data_struct->BootSector.BPB_BytsPerSec;
    sos_ui8_t *SecBuf = (sos_ui8_t *) sos_kmalloc(sector_size, 0);
    if (SecBuf == NULL)
      return -SOS_ENOMEM;
  
    sos_fat_helper_get_sector_and_offset(data_struct, cluster_nr, &ThisFATSecNum, &ThisFATEntOffset);
  
    /* Read the FAT sector who contains cluster_nr entry */
    retval = sos_blockdev_kernel_read(block_device,
                                      ThisFATSecNum*data_struct->BootSector.BPB_BytsPerSec,
                                      (sos_vaddr_t)SecBuf, &sector_size);
    if (retval != SOS_OK) {
      sos_kfree((sos_vaddr_t) SecBuf);
      return retval;
    } else if (sector_size != data_struct->BootSector.BPB_BytsPerSec) {
      sos_kfree((sos_vaddr_t) SecBuf);
      return -SOS_EIO;
    }
  
    /* Take the cluster_nr cluster value */
    if (data_struct->FAT_Type == FAT16) {
      FATClusEntryVal = *((sos_ui16_t *) &SecBuf[ThisFATEntOffset]);
    } else {
      FATClusEntryVal = (*((sos_ui32_t *) &SecBuf[ThisFATEntOffset])) & FAT32_CLUSTER_MASK;
    }
  
    SOS_ASSERT_FATAL(FATClusEntryVal != FAT_FREE_CLUSTER_ENTRY_VALUE);
  
    sos_kfree((sos_vaddr_t) SecBuf);
    *next_cluster = FATClusEntryVal;
    return SOS_OK;
  }
  
  /** Helper function to find the last cluster in the cluster list of a node
   *
   * In this context the FAT refers to the cluster table.
   * This function read cluster by cluster in the FAT cluster table.
   * It reads the FAT sector of the current cluster (cluster_nr in parameter) and
   * loops on the cluster value of cluster_nr to get the next cluster of the
   * file/dir until the end of cluster. Result is return by last_cluster parameter.
   *
   * This function is indirecly used in these VFS operations:
   * truncate, node_descructor, new_mapping, write.
   */
  static sos_ret_t
  sos_fat_helper_find_last_cluster(
          struct sos_blockdev_instance *block_device,
          struct sos_fat_data_structure *data_struct,
          sos_ui32_t cluster_nr,
          sos_ui32_t *last_cluster)
  {
    sos_ret_t retval;
    sos_ui32_t ThisFATSecNum, ThisFATEntOffset;
    sos_ui32_t FATClusEntryVal = cluster_nr;
  
    SOS_ASSERT_FATAL(cluster_nr >= FAT_FIRST_VALID_CLUSTER);
  
    sos_size_t sector_size = data_struct->BootSector.BPB_BytsPerSec;
    sos_ui8_t *SecBuf = (sos_ui8_t *) sos_kmalloc(sector_size, 0);
    if (SecBuf == NULL)
      return -SOS_ENOMEM;
  
    /* Loop on the cluster list. Start of the list: cluster_nr. End of the loop:
     * cluster value == End Of Cluster */
    do {
      cluster_nr = FATClusEntryVal;
      sos_fat_helper_get_sector_and_offset(data_struct, cluster_nr, &ThisFATSecNum, &ThisFATEntOffset);
  
      /* Read the sector of the FAT that contains cluster_nr entry */
      retval = sos_blockdev_kernel_read(block_device,
                                      ThisFATSecNum*data_struct->BootSector.BPB_BytsPerSec,
                                      (sos_vaddr_t)SecBuf, &sector_size);
      if (retval != SOS_OK) {
          sos_kfree((sos_vaddr_t) SecBuf);
          return retval;
      } else if (sector_size != data_struct->BootSector.BPB_BytsPerSec) {
          sos_kfree((sos_vaddr_t) SecBuf);
          return -SOS_EIO;
      }
  
      /* Get the cluster value */
      if (data_struct->FAT_Type == FAT16) {
          FATClusEntryVal = *((sos_ui16_t *) &SecBuf[ThisFATEntOffset]);
      } else {
          FATClusEntryVal = (*((sos_ui32_t *) &SecBuf[ThisFATEntOffset])) & FAT32_CLUSTER_MASK;
      }
  
      SOS_ASSERT_FATAL(FATClusEntryVal != FAT_FREE_CLUSTER_ENTRY_VALUE);
    } while (FATClusEntryVal != data_struct->EndOfCluster);
  
    sos_kfree((sos_vaddr_t) SecBuf);
    *last_cluster = cluster_nr;
    return SOS_OK;
  }
  
  /* Helper function to remove the last cluster of the cluster list of a node.
   *
   * This function is indirecly used in these VFS operations:
   * truncate, node_destructor.
   */
  static sos_ret_t
  sos_fat_helper_remove_last_cluster(
          struct sos_blockdev_instance *block_device,
          struct sos_fat_data_structure *data_struct,
          sos_ui32_t cluster_nr)
  {
    sos_ret_t retval;
    sos_ui32_t fat_nr;
    sos_ui32_t ThisFATSecNum, ThisFATEntOffset;
    sos_ui32_t FATClusEntryVal = cluster_nr;
    sos_ui32_t previous_cluster;
  
    SOS_ASSERT_FATAL(cluster_nr >= FAT_FIRST_VALID_CLUSTER);
  
    sos_size_t sector_size = data_struct->BootSector.BPB_BytsPerSec;
    sos_ui8_t *SecBuf = (sos_ui8_t *) sos_kmalloc(sector_size, 0);
    if (SecBuf == NULL)
      return -SOS_ENOMEM;
  
    /* Iterate on the cluster list. Start of the list: cluster_nr. End of the
     * loop: cluster value == End of cluster */
    do {
      previous_cluster = cluster_nr;
      cluster_nr = FATClusEntryVal;
      sos_fat_helper_get_sector_and_offset(data_struct, cluster_nr, &ThisFATSecNum, &ThisFATEntOffset);
  
      retval = sos_blockdev_kernel_read(block_device,
                                      ThisFATSecNum*data_struct->BootSector.BPB_BytsPerSec,
                                      (sos_vaddr_t)SecBuf, &sector_size);
      if (retval != SOS_OK) {
          sos_kfree((sos_vaddr_t) SecBuf);
          return retval;
      } else if (sector_size != data_struct->BootSector.BPB_BytsPerSec) {
          sos_kfree((sos_vaddr_t) SecBuf);
          return -SOS_EIO;
      }
  
      if (data_struct->FAT_Type == FAT16) {
          FATClusEntryVal = *((sos_ui16_t *) &SecBuf[ThisFATEntOffset]);
      } else {
          FATClusEntryVal = (*((sos_ui32_t *) &SecBuf[ThisFATEntOffset])) & FAT32_CLUSTER_MASK;
      }
  
      SOS_ASSERT_FATAL(FATClusEntryVal != FAT_FREE_CLUSTER_ENTRY_VALUE);
    } while (FATClusEntryVal != data_struct->EndOfCluster);
  
    /* Node is composed by more than one cluster? */
    if (previous_cluster != cluster_nr) {
      /* Mark the last cluster as free */
      if (data_struct->FAT_Type == FAT16) {
          *((sos_ui16_t *) &SecBuf[ThisFATEntOffset]) = FAT_FREE_CLUSTER_ENTRY_VALUE;
      } else {
          *((sos_ui32_t *) &SecBuf[ThisFATEntOffset]) =
              (*((sos_ui32_t *) &SecBuf[ThisFATEntOffset])) & (~FAT32_CLUSTER_MASK);
          *((sos_ui32_t *) &SecBuf[ThisFATEntOffset]) =
              (*((sos_ui32_t *) &SecBuf[ThisFATEntOffset])) | FAT_FREE_CLUSTER_ENTRY_VALUE;
      }
  
      /* Write the modified FAT sector in all FATs */
      for (fat_nr = 0;
          fat_nr < data_struct->BootSector.BPB_NumFATs;
          fat_nr++) {
          retval = sos_blockdev_kernel_write(block_device,
                              ((fat_nr * data_struct->FATSz) + ThisFATSecNum)*data_struct->BootSector.BPB_BytsPerSec,
                              (sos_vaddr_t)SecBuf, &sector_size);
          if (retval != SOS_OK) {
              sos_kfree((sos_vaddr_t) SecBuf);
              return retval;
          } else if (sector_size != data_struct->BootSector.BPB_BytsPerSec) {
              sos_kfree((sos_vaddr_t) SecBuf);
              return -SOS_EIO;
          }
      }
  
      /* Now mark the previous cluster as the end of the cluster list */
      sos_fat_helper_get_sector_and_offset(data_struct, previous_cluster, &ThisFATSecNum, &ThisFATEntOffset);
  
      retval = sos_blockdev_kernel_read(block_device,
                                      ThisFATSecNum*data_struct->BootSector.BPB_BytsPerSec,
                                      (sos_vaddr_t)SecBuf, &sector_size);
      if (retval != SOS_OK) {
          sos_kfree((sos_vaddr_t) SecBuf);
          return retval;
      } else if (sector_size != data_struct->BootSector.BPB_BytsPerSec) {
          sos_kfree((sos_vaddr_t) SecBuf);
          return -SOS_EIO;
      }
  
      /* Mark the cluster as EOC */
      if (data_struct->FAT_Type == FAT16) {
          *((sos_ui16_t *) &SecBuf[ThisFATEntOffset]) = FAT16_EOC;
      } else {
          *((sos_ui32_t *) &SecBuf[ThisFATEntOffset]) =
              (*((sos_ui32_t *) &SecBuf[ThisFATEntOffset])) & (~FAT32_CLUSTER_MASK);
          *((sos_ui32_t *) &SecBuf[ThisFATEntOffset]) =
              (*((sos_ui32_t *) &SecBuf[ThisFATEntOffset])) | FAT32_EOC;
      }
  
      /* Write the modified FAT sector in all FATs */
      for (fat_nr = 0;
          fat_nr < data_struct->BootSector.BPB_NumFATs;
          fat_nr++) {
          retval = sos_blockdev_kernel_write(block_device,
                                      ((fat_nr * data_struct->FATSz) + ThisFATSecNum)*data_struct->BootSector.BPB_BytsPerSec,
                                      (sos_vaddr_t)SecBuf, &sector_size);
          if (retval != SOS_OK) {
              sos_kfree((sos_vaddr_t) SecBuf);
              return retval;
          } else if (sector_size != data_struct->BootSector.BPB_BytsPerSec) {
              sos_kfree((sos_vaddr_t) SecBuf);
              return -SOS_EIO;
          }
      }
    }
  
    sos_kfree((sos_vaddr_t) SecBuf);
    return SOS_OK;
  }
  
  /** Helper function to remove all clusters of a node
   *
   * This function is indirecly used in these VFS operations:
   * node_destructor.
   */
  static sos_ret_t
  sos_fat_helper_remove_all_cluster(
          struct sos_blockdev_instance *block_device,
          struct sos_fat_data_structure *data_struct,
          sos_ui32_t first_cluster)
  {
      sos_ret_t retval;
      sos_ui32_t current_cluster, next_cluster;
  
      SOS_ASSERT_FATAL(first_cluster >= FAT_FIRST_VALID_CLUSTER);
  
      current_cluster = first_cluster;
  
      while (current_cluster != data_struct->EndOfCluster) {
          retval = sos_fat_helper_find_next_cluster(block_device,
                                              data_struct,
                                              current_cluster,
                                              &next_cluster);
          if (retval != SOS_OK) {
              return retval;
          }
  
          retval = sos_fat_helper_set_cluster_value(block_device,
                                              data_struct,
                                              current_cluster,
                                              FAT_FREE_CLUSTER_ENTRY_VALUE);
          if (retval != SOS_OK) {
              return retval;
          }
  
          current_cluster = next_cluster;
      }
  
      retval = sos_fat_helper_set_cluster_value(block_device,
                                          data_struct,
                                          current_cluster,
                                          FAT_FREE_CLUSTER_ENTRY_VALUE);
      if (retval != SOS_OK) {
          return retval;
      }
  
      return SOS_OK;
  }
  
  /** Helper function to append a new cluster at the end of
   * the cluster list of a node.
   *
   * This function is indirecly used in these VFS operations:
   * truncate, new_mapping, write.
   */
  static sos_ret_t
  sos_fat_helper_add_new_cluster(
          struct sos_blockdev_instance *block_device,
          struct sos_fat_data_structure *data_struct,
          sos_ui32_t first_cluster,
          sos_ui32_t *new_cluster)
  {
      sos_ret_t retval;
      sos_ui32_t last_cluster;
  
      SOS_ASSERT_FATAL(first_cluster >= FAT_FIRST_VALID_CLUSTER);
  
      retval = sos_fat_helper_find_last_cluster(
                  block_device,
                  data_struct,
                  first_cluster,
                  &last_cluster);
      if (retval != SOS_OK)
          return retval;
      retval = sos_fat_helper_find_free_cluster(
                  block_device,
                  data_struct,
                  new_cluster);
      if (retval != SOS_OK)
          return retval;
      sos_fat_helper_set_cluster_value(
                  block_device,
                  data_struct,
                  last_cluster,
                  *new_cluster);
      if (retval != SOS_OK)
          return retval;
      sos_fat_helper_set_cluster_value(
                  block_device,
                  data_struct,
                  *new_cluster,
                  data_struct->EndOfCluster);
      if (retval != SOS_OK)
          return retval;
  
      return SOS_OK;
  }
  
  /** Helper function read a slice of node.
   *
   * The slice to read starts at 'start_storage_location' offset of the
   * partition and has a length of 'len' bytes.
   *
   * This function is used in these VFS functions:
   * read, link and mmap.
   */
  static sos_ret_t
  sos_fat_helper_read(
          struct sos_blockdev_instance *block_device,
          struct sos_fat_data_structure *data_struct,
          sos_ui64_t start_storage_location,
          sos_vaddr_t dest_buf,
          sos_size_t * /* in/out */len)
  {
      sos_ret_t retval;
      sos_ui32_t next_cluster;
      sos_size_t clus_size = data_struct->BootSector.BPB_BytsPerSec * data_struct->BootSector.BPB_SecPerClus;
      sos_ui32_t clus_len = (*len / clus_size) + 1;
  
      int i;
      sos_size_t current_len, saved_len, total_len = 0;
      /* Read the file by cluster */
      for (i = 0; i < clus_len; i++) {
          /* The first and the last clusters are the only clusters whose can be not full */
          if (i == 0) {
              /* In the first cluster the begining can be truncate */
              current_len = clus_size - (start_storage_location % clus_size);
              if (current_len > *len)
                  current_len = *len;
          } else if (i == clus_len - 1) {
              /* In the last cluster the end can be truncate */
              current_len = *len - total_len;
              if (current_len == 0)
                  break;
          } else {
              current_len = clus_size;
          }
          saved_len = current_len;
  
          retval = sos_blockdev_kernel_read(block_device,
                                      start_storage_location,
                                      dest_buf + total_len,
                                      &current_len);
          if (retval != SOS_OK) {
              return retval;
          } else if (current_len != saved_len) {
              return -SOS_EIO;
          }
  
          total_len += current_len;
  
          if (total_len == *len)
              break;
  
          retval = sos_fat_helper_find_next_cluster(
                block_device,
                data_struct,
                sos_fat_helper_cluster_of_sector(data_struct,
                  start_storage_location / data_struct->BootSector.BPB_BytsPerSec),
                &next_cluster);
          if (retval != SOS_OK) {
              return retval;
          }
  
          start_storage_location = sos_fat_helper_first_sector_of_cluster(data_struct, next_cluster)
                   * data_struct->BootSector.BPB_BytsPerSec;
      }
  
      return SOS_OK;
  }
  
  /** Helper function to find a free entry in a directory node.
   *
   * This function iterates on the clusters list of a directory to find a free
   * directory entry. A directory is simply a sequencial list of 32-Bytes directory entry.
   * A free directory can be found when DIR_Name[0] == 0 or 0xe5.
   */
  static sos_ret_t
  sos_fat_helper_find_free_directory_entry(
          struct sos_blockdev_instance *block_device,
          struct sos_fat_data_structure *data_struct,
          sos_ui64_t start_storage_location,
          sos_ui64_t *new_storage_location)
  {
    sos_ret_t retval;
    int i, j;
    sos_ui32_t cluster_nr, sector_limit;
    sos_size_t sector_size = data_struct->BootSector.BPB_BytsPerSec;
    sos_size_t dirent_size = sizeof(struct sos_fat_directory_entry);
    struct sos_fat_directory_entry *dir_entry = (struct sos_fat_directory_entry *) sos_kmalloc(dirent_size, 0);
  
    sos_ui64_t sector_nr = start_storage_location / sector_size;
    sos_ui64_t sector_rem = start_storage_location % sector_size;
  
    if (sector_nr >= data_struct->FirstRootDirSecNum &&
          sector_nr < data_struct->FirstDataSector) {
      sector_limit = data_struct->RootDirSectors;
    } else {
      sector_limit = data_struct->BootSector.BPB_SecPerClus;
    }
  
    /* Loop on directory's clusters */
    do {
      /* Loop on the cluster's sectors */
      for (i = 0; i < sector_limit; i++) {
          /* Loop on the sector's directory entries */
          for (j = sector_rem / sizeof(struct sos_fat_directory_entry);
               j < sector_size / sizeof(struct sos_fat_directory_entry);
               j++) {
  
              retval = sos_fat_helper_read(block_device,
                                  data_struct,
                                  ((sector_nr + i) * sector_size) +
                                      (j * sizeof(struct sos_fat_directory_entry)),
                                  (sos_vaddr_t)dir_entry,
                                  &dirent_size);
              if (retval != SOS_OK) {
                  sos_kfree((sos_vaddr_t) dir_entry);
                  return retval;
              }
  
              /* A directory entry is free if DIR_Name[0] == 0xe5 or 0 */
              if ((dir_entry->DIR_Name[0] & 0xFF) == 0xE5 ||
                  (dir_entry->DIR_Name[0] & 0xFF) == 0) {
                  *new_storage_location = ((sector_nr + i) * sector_size) +
                      (j * sizeof(struct sos_fat_directory_entry));
                  sos_kfree((sos_vaddr_t) dir_entry);
                  return SOS_OK;
              }
          }
      }
  
      /* Find the next cluster of the directory */
      if (sector_nr >= data_struct->FirstRootDirSecNum &&
          sector_nr < data_struct->FirstDataSector) {
          /* Root directory is a special case */
          break;
      } else {
          cluster_nr = sos_fat_helper_cluster_of_sector(data_struct, sector_nr);
          retval = sos_fat_helper_find_next_cluster(block_device, data_struct, cluster_nr, &cluster_nr);
          if (retval != SOS_OK) {
              sos_kfree((sos_vaddr_t) dir_entry);
              return retval;
          }
      }
    } while (cluster_nr != data_struct->EndOfCluster);
  
    sos_kfree((sos_vaddr_t) dir_entry);
    /* No free entry in this directory */
    return -SOS_ENOENT;
  }
  
  /** Helper function to write an entry in the current directory. */
  static sos_ret_t
  sos_fat_helper_set_directory_entry(
          struct sos_blockdev_instance *block_device,
          sos_ui64_t storage_location,
          struct sos_fat_directory_entry *dir_entry)
  {
    sos_ret_t retval;
    sos_size_t dir_entry_size = sizeof(struct sos_fat_directory_entry);
  
    retval = sos_blockdev_kernel_write(block_device,
                                      storage_location,
                                      (sos_vaddr_t) dir_entry,
                                      &dir_entry_size);
    if (retval != SOS_OK) {
      return retval;
    } else if (dir_entry_size != sizeof(struct sos_fat_directory_entry)) {
      return -SOS_EIO;
    }
  
    return SOS_OK;
  }
  
  /** Helper function to copy from an entry in a string.
   *
   * This function will convert the FAT specific name format to the generic VFS
   * format. readdir operation use it.
   */
  static sos_ret_t
  sos_fat_helper_copy_file_name(
          struct sos_fat_directory_entry *dir_entry,
          char *name,
          sos_ui16_t *namelen)
  {
      int i = 0, j;
      char c;
  
      if (dir_entry->DIR_Name[i] == 0x05) {
          name[i] = 0xE5;
          i++;
      }
  
      for ( ; i < 8; i++) {
          c = dir_entry->DIR_Name[i];
          if (c != ' ') {
              if (IS_FAT_VALID_CHAR(c)) {
                  name[i] = dir_entry->DIR_Name[i];
              } else {
                  return -SOS_EINVAL;
              }
          } else {
              break;
          }
      }
  
      if (dir_entry->DIR_Name[8] != ' ') {
          name[i++] = '.';
      } else {
          *namelen = i;
          return SOS_OK;
      }
  
      for (j = 8; j < 11; j++) {
          c = dir_entry->DIR_Name[j];
          if (c != ' ') {
              if (IS_FAT_VALID_CHAR(c)) {
                  name[i++] = dir_entry->DIR_Name[j];
              } else {
                  return -SOS_EINVAL;
              }
          } else {
              break;
          }
      }
      *namelen = i;
  
      return SOS_OK;
  }
  
  static sos_bool_t
  fat_nsnode_same_name(const char *name1, sos_ui16_t namelen1,
                    const char *name2, sos_ui16_t namelen2);
  
  /** Helper function to compare names between a FAT's entry and a string. */
  static sos_bool_t
  sos_fat_helper_same_name(const char *name, sos_ui16_t namelen, struct sos_fat_directory_entry *dir_entry)
  {
      char name2[SOS_FS_DIRENT_NAME_MAXLEN];
      memset(name2, 0, SOS_FS_DIRENT_NAME_MAXLEN);
      sos_ui16_t namelen2 = SOS_FS_DIRENT_NAME_MAXLEN;
  
      sos_fat_helper_copy_file_name(dir_entry, name2, &namelen2);
  
      return fat_nsnode_same_name(name, namelen, name2, namelen2);
  }
  
  /** Hepler function to get the next entry in a directory.
   *
   * This function skip empty directory entry to return the first not free
   * directory entry. A directory entry shouldn't have a DIR_Name[0] == 0xe5 or
   * 0 and shouldn't be a long name entry (DIR_Attr == ATTR_LONG_NAME).
   */
  static sos_ret_t
  sos_fat_helper_get_next_directory_entry(
          struct sos_blockdev_instance *block_device,
          struct sos_fat_data_structure *data_struct,
          sos_ui64_t storage_location,
          struct sos_fat_directory_entry **dir_entry,
          sos_ui64_t *dir_storage_location,
          sos_si64_t *dir_offset)
  {
    sos_ret_t retval;
    int i, j;
    sos_ui32_t cluster_nr, sector_limit;
    sos_size_t sector_size = data_struct->BootSector.BPB_BytsPerSec;
    struct sos_fat_directory_entry *SecBuf = (struct sos_fat_directory_entry *) sos_kmalloc(sector_size, 0);
    sos_ui64_t sector_nr = storage_location / data_struct->BootSector.BPB_BytsPerSec;
    sos_ui32_t sector_rem = (storage_location % data_struct->BootSector.BPB_BytsPerSec) /
                              sizeof(struct sos_fat_directory_entry);
  
    *dir_offset = 0;
  
    if (sector_nr >= data_struct->FirstRootDirSecNum &&
        sector_nr < data_struct->FirstDataSector) {
      sector_limit = data_struct->FirstRootDirSecNum + data_struct->RootDirSectors;
    } else {
      sector_limit =
          sos_fat_helper_first_sector_of_cluster(
              data_struct,
              sos_fat_helper_cluster_of_sector(
                  data_struct,
                  sector_nr)) +
          data_struct->BootSector.BPB_SecPerClus;
    }
  
    /* loop on directory's clusters */
    do {
      /* loop on cluster's sectors */
      for (i = sector_nr; i < sector_limit; i++) {
          retval = sos_blockdev_kernel_read(block_device,
                                      i * sector_size,
                                      (sos_vaddr_t) SecBuf,
                                      &sector_size);
          if (retval != SOS_OK) {
              sos_kfree((sos_vaddr_t) SecBuf);
              return retval;
          } else if (sector_size != data_struct->BootSector.BPB_BytsPerSec) {
              sos_kfree((sos_vaddr_t) SecBuf);
              return -SOS_EIO;
          }
  
          /* loop on directory's entry in sector i */
          for (j = sector_rem; j < sector_size / sizeof(struct sos_fat_directory_entry); j++) {
              *dir_offset += sizeof(struct sos_fat_directory_entry);
              /* A directory entry is normal if DIR_Name[0] != 0xe5 and 0
               * and is not a long name directory entry */
              if ((SecBuf[j].DIR_Name[0] & 0xFF) != 0xE5 &&
                  (SecBuf[j].DIR_Name[0] & 0xFF) != 0 &&
                  SecBuf[j].DIR_Attr != ATTR_LONG_NAME) {
                  if (dir_entry != NULL) {
                      memcpy(*dir_entry, &SecBuf[j], sizeof(struct sos_fat_directory_entry));
                  }
                  *dir_storage_location = (i * sector_size) +
                                      (j * sizeof(struct sos_fat_directory_entry));
                  sos_kfree((sos_vaddr_t) SecBuf);
                  return SOS_OK;
              } else if ( (SecBuf[j].DIR_Name[0] & 0xFF) == 0 ) {
                  sos_kfree((sos_vaddr_t) SecBuf);
                  return -SOS_ENOENT;
              }
          }
      }
  
      /* Find the next cluster of the directory */
      if (sector_nr >= data_struct->FirstRootDirSecNum &&
          sector_nr < data_struct->FirstDataSector) {
          /* Root directory is a special case */
          break;
      } else {
          cluster_nr = sos_fat_helper_cluster_of_sector(data_struct, sector_nr);
          retval = sos_fat_helper_find_next_cluster(block_device, data_struct, cluster_nr, &cluster_nr);
          if (retval != SOS_OK) {
              sos_kfree((sos_vaddr_t) SecBuf);
              return retval;
          }
      }
  
      sector_rem = 0;
    } while (cluster_nr != data_struct->EndOfCluster);
  
    sos_kfree((sos_vaddr_t) SecBuf);
    /* No next entry in the directory */
    return -SOS_ENOENT;
  }
  
  /** Helper function to compute the storage location in a file
   * from an offset in this file.
   *
   * This function, with the cluster number find in the directory entry and an
   * offset (both in parameters), computes the storage location of the byte from
   * the beginning of the partition with the FAT cluster list.
   * The position is a cluster and an offset in this cluster. When cluster is
   * known, the FAT cluster list can be loop on until this cluster is find then
   * the cluster is convert in the absolut position in the partition. This
   * position is added with the offset in this cluster and the storage location
   * is find.
   */
  static sos_ret_t
  sos_fat_helper_storage_location_of_file(
          struct sos_blockdev_instance *block_device,
          struct sos_fat_data_structure *data_struct,
          sos_ui32_t first_cluster_of_file,
          sos_ui32_t offset_in_file,
          sos_ui64_t *storage_location)
  {
    sos_ret_t retval;
    sos_ui32_t ThisFATSecNum, ThisFATEntOffset;
    sos_ui32_t FATClusEntryVal = first_cluster_of_file;
    sos_ui32_t cluster_nr;
    int i;
  
    SOS_ASSERT_FATAL(first_cluster_of_file >= FAT_FIRST_VALID_CLUSTER);
  
    sos_size_t sector_size = data_struct->BootSector.BPB_BytsPerSec;
    sos_ui8_t *SecBuf = (sos_ui8_t *) sos_kmalloc(sector_size, 0);
    if (SecBuf == NULL)
      return -SOS_ENOMEM;
  
    /* Compute the cluster of the position and the offset in this cluster of the
     * position */
    sos_ui32_t cluster_offset = offset_in_file / (sector_size * data_struct->BootSector.BPB_SecPerClus);
    sos_ui32_t offset_rem = offset_in_file % (sector_size * data_struct->BootSector.BPB_SecPerClus);
  
    /* Find the cluster computed below */
    for (i = 0; i < cluster_offset; i++) {
      cluster_nr = FATClusEntryVal;
      sos_fat_helper_get_sector_and_offset(data_struct, cluster_nr, &ThisFATSecNum, &ThisFATEntOffset);
  
      retval = sos_blockdev_kernel_read(block_device,
                                      ThisFATSecNum*data_struct->BootSector.BPB_BytsPerSec,
                                      (sos_vaddr_t)SecBuf, &sector_size);
      if (retval != SOS_OK) {
          sos_kfree((sos_vaddr_t) SecBuf);
          return retval;
      } else if (sector_size != data_struct->BootSector.BPB_BytsPerSec) {
          sos_kfree((sos_vaddr_t) SecBuf);
          return -SOS_EIO;
      }
  
      if (data_struct->FAT_Type == FAT16) {
          FATClusEntryVal = *((sos_ui16_t *) &SecBuf[ThisFATEntOffset]);
      } else {
          FATClusEntryVal = (*((sos_ui32_t *) &SecBuf[ThisFATEntOffset])) & FAT32_CLUSTER_MASK;
      }
  
      SOS_ASSERT_FATAL(FATClusEntryVal != FAT_FREE_CLUSTER_ENTRY_VALUE);
  
      if (FATClusEntryVal == data_struct->EndOfCluster) {
          sos_kfree((sos_vaddr_t) SecBuf);
          return -SOS_EIO;
      }
    }
  
    /* Adds the offset in the cluster to find the storage location */
    *storage_location =
      (sos_fat_helper_first_sector_of_cluster(data_struct, FATClusEntryVal) * sector_size) + offset_rem;
  
    sos_kfree((sos_vaddr_t) SecBuf);
    return SOS_OK;
  }
  
  /** Helper function to resize a file.
   *
   * Only file can be resize.
   * With the new_size parameter, clusters to add or to delete can be compute
   * and modified as desired.
   *
   * This function is used in these VFS operations:
   * truncate, new_mapping, write.
   */
  static sos_ret_t
  sos_fat_helper_resize(
          struct sos_blockdev_instance *block_device,
          struct sos_fat_data_structure *data_struct,
          struct sos_fs_node *fs_node,
          sos_ui32_t new_size)
  {
      int i;
      sos_ret_t retval;
      struct sos_fat_directory_entry *dir_entry = fs_node->custom_data;
      sos_ui32_t first_cluster;
      sos_ui32_t old_size;
      if (dir_entry != NULL) {
          first_cluster = GET_FIRST_CLUSTER(dir_entry);
          old_size = dir_entry->DIR_FileSize;
      } else {
          first_cluster = data_struct->FirstRootDirSecNum;
          old_size = data_struct->RootDirSectors;
      }
      sos_size_t clus_size =
          (data_struct->BootSector.BPB_SecPerClus * data_struct->BootSector.BPB_BytsPerSec);
  
      /* How many clusters before and after the resize? */
      sos_ui32_t new_cluster, old_cluster, free_cluster;
      new_cluster = new_size / clus_size;
      old_cluster = old_size / clus_size;
  
      /* File should be bigger or smaller? */
      if (new_cluster > old_cluster) {
          /* Bigger */
          sos_ui32_t *clus_buf = (sos_ui32_t*)sos_kmalloc(clus_size, 0);
          if (clus_buf == NULL)
              return -SOS_ENOMEM;
  
          memset(clus_buf, 0, clus_size);
  
          for (i = 0; i < new_cluster - old_cluster; i++) {
              retval = sos_fat_helper_add_new_cluster(block_device,
                                              data_struct,
                                              first_cluster,
                                              &free_cluster);
              if (retval != SOS_OK) {
                  sos_kfree((sos_vaddr_t) clus_buf);
                  return retval;
              }
  
              /* Clean the new cluster */
              retval = sos_blockdev_kernel_write(block_device,
                                        sos_fat_helper_first_sector_of_cluster(data_struct, free_cluster) *
                                        data_struct->BootSector.BPB_BytsPerSec,
                                        (sos_vaddr_t)clus_buf,
                                        &clus_size);
              if (retval != SOS_OK) {
                  sos_kfree((sos_vaddr_t) clus_buf);
                  return retval;
              } else if (clus_size != data_struct->BootSector.BPB_SecPerClus * data_struct->BootSector.BPB_BytsPerSec) {
                  sos_kfree((sos_vaddr_t) clus_buf);
                  return -SOS_EIO;
              }
          }
  
          sos_kfree((sos_vaddr_t) clus_buf);
      } else if (old_cluster > new_cluster) {
          /* Smaller */
          for (i = 0; i < old_cluster - new_cluster; i++) {
              retval = sos_fat_helper_remove_last_cluster(block_device,
                                                  data_struct,
                                                  first_cluster);
              if (retval != SOS_OK)
                  return retval;
              if (dir_entry != NULL)
                  dir_entry->DIR_FileSize -= clus_size;
          }
      }
  
      if (dir_entry != NULL) {
          dir_entry->DIR_FileSize = new_size;
          dir_entry->DIR_Attr |= ATTR_ARCHIVE;
      }
  
      return SOS_OK;
  }
  /* }}} */
  
  /* ********************************************************
   * File mapping stuff {{{
   */
  
  inline static struct sos_fat_file *
  get_fatnode_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 sos_fat_file *)(((struct sos_fs_node *)mr->custom_data)->custom_data);
  }
  
  
  static void fat_map_ref(struct sos_umem_vmm_vr * vr)
  {
    struct sos_fat_file * fatnode = get_fatnode_of_vr(vr);
    sos_fs_ref_fsnode((struct sos_fs_node *) fatnode->mapres.custom_data);
    fatnode->num_mappings++;
  }
  
  
  static void fat_map_unref(struct sos_umem_vmm_vr * vr)
  {
    struct sos_fat_file * fatnode = get_fatnode_of_vr(vr);
  
    SOS_ASSERT_FATAL(fatnode->num_mappings > 0);
    fatnode->num_mappings --;
  
    _sos_fs_unref_fsnode((struct sos_fs_node *) fatnode->mapres.custom_data);
  }
  
  
  static void fat_map_unmap(struct sos_umem_vmm_vr * vr,
                                  sos_uaddr_t uaddr,
                                  sos_size_t size)
  {
      sos_paging_unmap_interval(uaddr, size);
  }
  
  
  static sos_ret_t fat_map_page_in(struct sos_umem_vmm_vr * vr,
                                      sos_uaddr_t uaddr,
                                      sos_bool_t write_access)
  {
    struct sos_fat_file * fatnode = get_fatnode_of_vr(vr);
    sos_luoffset_t offset = uaddr - sos_umem_vmm_get_start_of_vr(vr);
    sos_ret_t retval;
    sos_paddr_t ppage_paddr;
    sos_vaddr_t page_vaddr;
    sos_bool_t allocate_new_page = FALSE;
    struct sos_fat_mmap_page_list *elt, *previous_elt = NULL;
  
    /* The region is not allowed to be resized */
    if (SOS_PAGE_ALIGN_SUP(offset) > fatnode->dir_entry.DIR_FileSize)
      return -SOS_EFAULT;
  
    if (fatnode->list != NULL) {
      /* There are already mapping on this file */
      elt = fatnode->list;
      while (elt != NULL) {
          if (elt->offset == sos_umem_vmm_get_offset_in_resource(vr)) {
              /* page already allocated and filled */
              page_vaddr = elt->mmap_page;
              break;
          } else if (elt->offset > sos_umem_vmm_get_offset_in_resource(vr)) {
              /* Allocate a kernel page to fill */
              page_vaddr = sos_kmem_vmm_alloc(1, SOS_KMEM_VMM_MAP);
              if (page_vaddr == (sos_vaddr_t)NULL)
                  return -SOS_ENOMEM;
              allocate_new_page = TRUE;
              break;
          }
          previous_elt = elt;
          elt = elt->next;
      }
    } else {
      /* Allocate a kernel page to fill */
      page_vaddr = sos_kmem_vmm_alloc(1, SOS_KMEM_VMM_MAP);
      if (page_vaddr == (sos_vaddr_t)NULL)
          return -SOS_ENOMEM;
      allocate_new_page = TRUE;
    }
  
    struct sos_fs_node *fs_node = (struct sos_fs_node *)
      sos_umem_vmm_get_mapped_resource_of_vr(vr)->custom_data;
  
    struct sos_fat_data_structure *data_struct =
      (struct sos_fat_data_structure *)fs_node->fs->custom_data;
  
    /* Fill the mapping in the new allocated page with the data read from the file */
    if (allocate_new_page) {
      sos_size_t page_size = SOS_PAGE_SIZE;
      sos_ui64_t storage_location;
      offset = SOS_PAGE_ALIGN_INF(offset);
      retval = sos_fat_helper_storage_location_of_file(fs_node->fs->device->block_device,
                                          data_struct,
                                          GET_FIRST_CLUSTER(&fatnode->dir_entry),
                                          offset + sos_umem_vmm_get_offset_in_resource(vr),
                                          &storage_location);
      if (retval != SOS_OK) {
          sos_kmem_vmm_free(page_vaddr);
          return retval;
      }
  
      retval = sos_fat_helper_read(fs_node->fs->device->block_device,
                          data_struct,
                          storage_location,
                          page_vaddr,
                          &page_size);
      if (retval != SOS_OK) {
          sos_kmem_vmm_free(page_vaddr);
          return retval;
      }
    }
  
    /* Lookup physical kernel page */
    ppage_paddr = sos_paging_get_paddr(page_vaddr);
  
    /* Cannot access unmapped kernel pages */
    if (! ppage_paddr) {
      sos_kmem_vmm_free(page_vaddr);
      return -SOS_EFAULT;
    }
  
    /* Initialize a mmap page element and insert this element in the file mmap list */
    if (allocate_new_page) {
      elt = (struct sos_fat_mmap_page_list *) sos_kmalloc(sizeof(struct sos_fat_mmap_page_list), 0);
      if (elt == NULL) {
          sos_kmem_vmm_free(page_vaddr);
          return -SOS_ENOMEM;
      }
  
      elt->mmap_page = page_vaddr;
      elt->offset = sos_umem_vmm_get_offset_in_resource(vr);
  
      if (fatnode->list != NULL) {
          /* There is one or more element in the list */
  
          if (previous_elt == NULL) {
              /* There is no element before elt */
              elt->next = fatnode->list;
              fatnode->list = elt;
          } else {
              /* elt must be insert after previous_elt */
              elt->next = previous_elt->next;
              previous_elt->next = elt;
          }
      } else {
          /* elt is the first of the list */
          elt->next = NULL;
          fatnode->list = elt;
      }
    }
  
    /* Remap it in user space */
    return sos_paging_map(ppage_paddr,
                          SOS_PAGE_ALIGN_INF(uaddr),
                          TRUE,
                          sos_umem_vmm_get_prot_of_vr(vr));
  }
  
  
  static sos_ret_t fat_map_sync_page(struct sos_umem_vmm_vr * vr,
                                      sos_uaddr_t uaddr,
                                      sos_ui32_t flags)
  {
      struct sos_fat_file * fatnode = get_fatnode_of_vr(vr);
      sos_luoffset_t offset = SOS_PAGE_ALIGN_INF(uaddr - sos_umem_vmm_get_start_of_vr(vr));
      sos_ret_t retval;
      int i;
      sos_size_t clus_size, clus_size_write;
      sos_ui64_t storage_location;
  
      struct sos_fs_node *fs_node = (struct sos_fs_node *)
          sos_umem_vmm_get_mapped_resource_of_vr(vr)->custom_data;
  
      struct sos_fat_data_structure *data_struct =
          (struct sos_fat_data_structure *)fs_node->fs->custom_data;
  
      clus_size = clus_size_write = data_struct->BootSector.BPB_SecPerClus *
                          data_struct->BootSector.BPB_BytsPerSec;
  
      /* Sync all cluster in a memory page */
      for (i = 0; i < SOS_PAGE_SIZE / clus_size; i++) {
          /* Get the storage location of the cluster */
          retval = sos_fat_helper_storage_location_of_file(fs_node->fs->device->block_device,
                                          data_struct,
                                          GET_FIRST_CLUSTER(&fatnode->dir_entry),
                                          offset + sos_umem_vmm_get_offset_in_resource(vr) + (i * clus_size),
                                          &storage_location);
          if (retval != SOS_OK) {
              return retval;
          }
  
          /* Sync the cluster */
          sos_blockdev_kernel_write(fs_node->fs->device->block_device,
                              storage_location,
                              uaddr + (i * clus_size),
                              &clus_size_write);
          if (retval != SOS_OK) {
              return retval;
          } else if (clus_size_write != clus_size) {
              return -SOS_EIO;
          }
      }
  
      /* Unset the dirty flag */
      sos_paging_set_dirty(uaddr, FALSE);
  
      return SOS_OK;
  }
  
  
  static struct sos_umem_vmm_vr_ops fat_map_ops
    = (struct sos_umem_vmm_vr_ops){
      .ref        = fat_map_ref,
      .unref      = fat_map_unref,
      .unmap      = fat_map_unmap,
      .page_in    = fat_map_page_in,
      .sync_page  = fat_map_sync_page
    };
  
  static sos_ret_t fat_new_mapping(struct sos_umem_vmm_vr *vr)
  {
    struct sos_fat_file * fat_file = get_fatnode_of_vr(vr);
    struct sos_fs_node *fs_node = fat_file->mapres.custom_data;
    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 when the mapping size is greater than the file size */
    if (reqsize > fat_file->dir_entry.DIR_FileSize)
      {
        retval = sos_fat_helper_resize(fs_node->fs->device->block_device,
                                      fs_node->fs->custom_data,
                                      fs_node,
                                      SOS_PAGE_ALIGN_SUP(reqsize));
        if (SOS_OK != retval)
          return retval;
      }
  
    return sos_umem_vmm_set_ops_of_vr(vr, &fat_map_ops);
  }
  
  /* }}} */
  
  /* ********************************************************
   * Opened file operations {{{
   */
  
  
  static sos_ret_t
  fat_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;
  
    /* Copy the parent node */
    memcpy(*result, this, sizeof(*this));
    /* Change the owner */
    (*result)->owner = for_owner;
  
    /* If a custom_data exist, copy it too */
    if (this->custom_data != NULL) {
      struct sos_fs_dirent *dirent = (struct sos_fs_dirent*)
          sos_kmalloc(sizeof(struct sos_fs_dirent), 0);
      if (dirent == NULL)
          return -SOS_ENOMEM;
  
      /* Copy and set the custom_data of the parent */
      memcpy(dirent, this->custom_data, sizeof(*dirent));
      (*result)->custom_data = dirent;
    }
  
    return SOS_OK;
  }
  
  
  static sos_ret_t
  fat_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 sos_fs_node * fatnode;
  
    fatnode = (struct sos_fs_node*)
      sos_fs_nscache_get_fs_node(this->direntry);
  
    if (fatnode->type != SOS_FS_NODE_REGULAR_FILE)
      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 = ((struct sos_fat_directory_entry*) fatnode->custom_data)->DIR_FileSize;
        break;
  
      default:
        return -SOS_EINVAL;
      }
  
    /* Prevent the underflow of the file position */
    if (offset < -ref_offs)
      return -SOS_EINVAL;
  
    /* Update the file position */
    this->position = ref_offs + offset;
    *result_position = this->position;
  
    return SOS_OK;
  }
  
  
  static sos_ret_t fat_read(struct sos_fs_opened_file *this,
                               sos_uaddr_t dest_buf,
                               sos_size_t * /* in/out */len)
  {
    sos_ret_t retval;
    struct sos_fs_node * fatnode;
    struct sos_fat_directory_entry *dir_entry;
    struct sos_fat_data_structure *data_struct;
    sos_ui64_t storage_location;
    sos_vaddr_t kern_buf;
  
    fatnode = (struct sos_fs_node*)
      sos_fs_nscache_get_fs_node(this->direntry);
  
    data_struct = (struct sos_fat_data_structure*) fatnode->fs->custom_data;
  
    dir_entry = fatnode->custom_data;
  
    if (fatnode->type != SOS_FS_NODE_REGULAR_FILE)
      return -SOS_ENOSUP;
  
    /* Cannot read after end of file */
    if (this->position >= dir_entry->DIR_FileSize)
      {
        *len = 0;
        return SOS_OK;
      }
  
    /* Cannot read more than the file size */
    if (this->position + *len >= dir_entry->DIR_FileSize)
      *len = dir_entry->DIR_FileSize - this->position;
  
    retval = sos_fat_helper_storage_location_of_file(
                  fatnode->fs->device->block_device,
                  data_struct,
                  GET_FIRST_CLUSTER(dir_entry),
                  this->position,
                  &storage_location);
    if (retval != SOS_OK) {
      return retval;
    }
  
    kern_buf = sos_kmalloc(*len, 0);
    if (kern_buf == (sos_vaddr_t)NULL)
      return -SOS_ENOMEM;
  
    retval = sos_fat_helper_read(fatnode->fs->device->block_device,
                              data_struct,
                              storage_location,
                              kern_buf,
                              len);
    if (retval != SOS_OK) {
      sos_kfree(kern_buf);
      return retval;
    }
  
    retval = sos_memcpy_to_user(dest_buf,
                                (sos_vaddr_t)kern_buf,
                                *len);
    if (retval < 0) {
      sos_kfree((sos_vaddr_t)kern_buf);
      return retval;
    }
  
    this->position += retval;
    *len = retval;
  
    sos_kfree((sos_vaddr_t)kern_buf);
  
    return SOS_OK;
  }
  
  
  static sos_ret_t fat_write(struct sos_fs_opened_file *this,
                               sos_uaddr_t src_buf,
                               sos_size_t * /* in/out */len)
  {
    sos_ret_t retval;
    struct sos_fs_node * fatnode;
    struct sos_fat_directory_entry *dir_entry;
    struct sos_fat_data_structure *data_struct;
    sos_ui64_t storage_location;
    sos_vaddr_t kern_buf;
  
    fatnode = (struct sos_fs_node*)
      sos_fs_nscache_get_fs_node(this->direntry);
  
    dir_entry = fatnode->custom_data;
  
    data_struct = fatnode->fs->custom_data;
  
    if (fatnode->type != SOS_FS_NODE_REGULAR_FILE)
      return -SOS_ENOSUP;
  
    kern_buf = sos_kmalloc(*len, 0);
    if (kern_buf == (sos_vaddr_t)NULL)
     return -SOS_ENOMEM;
  
    if (this->position + *len >= dir_entry->DIR_FileSize)
      {
        /* Try to resize if needed */
        if ( SOS_OK != sos_fat_helper_resize(
                      fatnode->fs->device->block_device,
                      data_struct,
                      fatnode,
                      this->position + *len) ) {
              *len = dir_entry->DIR_FileSize - this->position;
          } else {
              sos_fs_mark_dirty(this);
          }
      }
  
    retval = sos_memcpy_from_user(kern_buf,
                                  src_buf,
                                  *len);
    if (retval < 0)
      {
        sos_kfree(kern_buf);
        return retval;
      }
  
    sos_size_t clus_size = data_struct->BootSector.BPB_BytsPerSec * data_struct->BootSector.BPB_SecPerClus;
    sos_ui32_t clus_pos = this->position / clus_size;
    sos_ui32_t clus_len = (this->position + *len) / clus_size;
  
    int i;
    sos_size_t current_len, saved_len, total_len = 0;
    sos_ui32_t current_pos = this->position;
    /* Write buffer by cluster */
    for (i = 0; i < clus_len - clus_pos + 1; i++) {
      /* First and last clusters can be not full clusters */
      if (i == 0) {
          /* The first cluster begining can be truncate */
          current_len = clus_size - (this->position % clus_size);
          if (current_len > *len)
              current_len = *len;
      } else if (i == clus_len - clus_pos) {
          /* The last cluster end can be truncate */
          current_len = *len - total_len;
          if (current_len == 0)
              break;
      } else {
          current_len = clus_size;
      }
      saved_len = current_len;
  
      retval = sos_fat_helper_storage_location_of_file(
                  fatnode->fs->device->block_device,
                  data_struct,
                  GET_FIRST_CLUSTER(dir_entry),
                  current_pos,
                  &storage_location);
      if (retval != SOS_OK) {
          sos_kfree(kern_buf);
          return retval;
      }
  
      retval = sos_blockdev_kernel_write(fatnode->fs->device->block_device,
                                      storage_location,
                                      kern_buf + (total_len/sizeof(sos_vaddr_t)),
                                      &current_len);
      if (retval != SOS_OK) {
          sos_kfree(kern_buf);
          return retval;
      } else if (current_len != saved_len) {
          sos_kfree(kern_buf);
          return -SOS_EIO;
      }
  
      current_pos += current_len;
      total_len += current_len;
    }
  
    dir_entry->DIR_Attr |= ATTR_ARCHIVE;
    this->position = current_pos;
    *len = total_len;
  
    /* Set the dirty flag */
    sos_fs_mark_dirty(this);
  
    sos_kfree(kern_buf);
  
    return SOS_OK;
  }
  
  
  static sos_ret_t fat_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 sos_fs_node * fat_node;
    struct sos_fat_file * fat_file;
  
    fat_node = (struct sos_fs_node*)
      sos_fs_nscache_get_fs_node(this->direntry);
  
    if (fat_node->type != SOS_FS_NODE_REGULAR_FILE)
      return -SOS_ENOSUP;
  
    fat_file = (struct sos_fat_file *) fat_node->custom_data;
  
    /* Set the dirty flag if file can be writen */
    if (access_rights & SOS_VM_MAP_PROT_WRITE)
      sos_fs_mark_dirty(this);
  
    return sos_umem_vmm_map(sos_process_get_address_space(this->owner),
                            uaddr, size, access_rights,
                            flags, &fat_file->mapres, offset);
  }
  
  
  static sos_ret_t fat_readdir(struct sos_fs_opened_file *this,
                                  struct sos_fs_dirent * result)
  {
    /* For directories, "custom_data" indicates the address
       of the last directory entry */
    sos_ret_t retval;
    struct sos_fat_directory_entry *dir_entry, *this_dir_entry;
    struct sos_fs_node * fatnode;
    struct sos_fat_data_structure *data_struct;
    struct sos_fs_dirent *fs_dirent;
    sos_ui64_t storage_location;
    sos_si64_t offset;
  
    fatnode = (struct sos_fs_node*)
      sos_fs_nscache_get_fs_node(this->direntry);
  
    SOS_ASSERT_FATAL(fatnode->type == SOS_FS_NODE_DIRECTORY);
  
    data_struct = (struct sos_fat_data_structure *) fatnode->fs->custom_data;
  
    this_dir_entry = (struct sos_fat_directory_entry *) fatnode->custom_data;
  
    dir_entry = (struct sos_fat_directory_entry*) sos_kmalloc(sizeof(struct sos_fat_directory_entry), 0);
    fs_dirent = (struct sos_fs_dirent *) this->custom_data;
  
    /* First acces on the directory entries ? */
    if (fs_dirent == NULL) {
      fs_dirent = (struct sos_fs_dirent *) sos_kmalloc(sizeof(struct sos_fs_dirent), 0);
      memset(fs_dirent, 0, sizeof(struct sos_fs_dirent));
  
      if ( this_dir_entry == NULL ) {
          storage_location = data_struct->FirstRootDirSecNum *
                          data_struct->BootSector.BPB_BytsPerSec;
      } else {
          storage_location = sos_fat_helper_first_sector_of_cluster(
              data_struct,
              GET_FIRST_CLUSTER(this_dir_entry)) *
              data_struct->BootSector.BPB_BytsPerSec;
      }
    } else {
      storage_location = fs_dirent->storage_location + sizeof(struct sos_fat_directory_entry);
    }
    retval = sos_fat_helper_get_next_directory_entry(
          fatnode->fs->device->block_device,
          data_struct,
          storage_location,
          &dir_entry,
          &storage_location,
          &offset);
    if (retval != SOS_OK) {
      sos_kfree((sos_vaddr_t) dir_entry);
      if (this->custom_data == NULL) {
          sos_kfree((sos_vaddr_t) fs_dirent);
      }
      return retval;
    }
  
    /* Update the result */
    memset(result, 0, sizeof(struct sos_fs_dirent));
    result->storage_location  = storage_location;
    result->offset_in_dirfile = this->position + offset;
    if (dir_entry->DIR_Attr & ATTR_DIRECTORY) {
      result->type = SOS_FS_NODE_DIRECTORY;
    } else {
      result->type = SOS_FS_NODE_REGULAR_FILE;
    }
    retval = sos_fat_helper_copy_file_name(dir_entry, result->name, &result->namelen);
    if (retval != SOS_OK) {
      sos_kfree((sos_vaddr_t) dir_entry);
      return retval;
    }
  
    memcpy(fs_dirent, result, sizeof(struct sos_fs_dirent));
  
    /* Update the custom data */
    this->position    += offset;
    this->custom_data = (void *) fs_dirent;
  
    sos_kfree((sos_vaddr_t) dir_entry);
  
    return SOS_OK;
  }
  
  static struct sos_fs_ops_opened_file fat_ops_opened_file =
    (struct sos_fs_ops_opened_file){
      .seek     = fat_seek,
      .read     = fat_read,
      .write    = fat_write,
      .mmap     = fat_mmap,
      /*.fcntl    = fat_fcntl*/
    };
  
  
  static struct sos_fs_ops_opened_dir fat_ops_opened_dir =
    (struct sos_fs_ops_opened_dir){
      .readdir  = fat_readdir
    };
  /* }}} */
  
  /* ********************************************************
   * FS node operations {{{
   */
  
  static sos_ret_t fat_stat_node(struct sos_fs_node * this,
                                    struct sos_fs_stat * result)
  {
    /* Establish the major/minor fields */
    result->st_rdev.device_class    = 0;
    result->st_rdev.device_instance = 0;
    /* In FAT, nodes are only file or directory, there is no special file.
       Retrieve it from the device that it is mounted
       on (might not exist...) */
    struct sos_fs_node * rootdev = this->fs->device;
    if (rootdev)
      {
          result->st_rdev.device_class    = rootdev->dev_id.device_class;
          result->st_rdev.device_instance = rootdev->dev_id.device_instance;
      }
  
    result->st_type                 = this->type;
    result->st_access_rights        = this->access_rights;
    result->st_nlink                = this->ondisk_lnk_cnt;
    /* Separate root directory and other nodes */
    if (this->custom_data != NULL) {
      result->st_size = (sos_si64_t)((struct sos_fat_directory_entry *) this->custom_data)->DIR_FileSize;
      result->st_storage_location = this->storage_location;
    } else {
      struct sos_fat_data_structure *data_struct = (struct sos_fat_data_structure *) this->fs->custom_data;
      result->st_size = 0;
      result->st_storage_location = (sos_ui64_t) data_struct->FirstRootDirSecNum * data_struct->BootSector.BPB_BytsPerSec;
    }
  
    return SOS_OK;
  }
  
  
  static sos_ret_t fat_truncate(struct sos_fs_node *this,
                                   sos_lsoffset_t length)
  {
    sos_ret_t retval;
    struct sos_fat_directory_entry *dir_entry;
    struct sos_fat_data_structure *data_struct;
    sos_size_t new_size;
  
    if ( (this->type != SOS_FS_NODE_REGULAR_FILE) )
      return -SOS_ENOSUP;
  
    new_size = (sos_size_t) length;
    dir_entry = (struct sos_fat_directory_entry*) this->custom_data;
    data_struct = (struct sos_fat_data_structure*) this->fs->custom_data;
  
    sos_ui32_t current_clus = dir_entry->DIR_FileSize /
      (data_struct->BootSector.BPB_BytsPerSec * data_struct->BootSector.BPB_SecPerClus);
  
    sos_ui32_t cluster = new_size /
      (data_struct->BootSector.BPB_BytsPerSec * data_struct->BootSector.BPB_SecPerClus);
  
    int i;
    sos_ui32_t last_cluster;
    if (dir_entry->DIR_FileSize >= new_size) {
      for (i = 0; i < current_clus - cluster; i++) {
          retval = sos_fat_helper_remove_last_cluster(
                      this->fs->device->block_device,
                      data_struct,
                      GET_FIRST_CLUSTER(dir_entry));
          if (retval != SOS_OK)
              return retval;
      }
    } else {
      for (i = 0; i < cluster - current_clus; i++) {
          retval = sos_fat_helper_add_new_cluster(
                          this->fs->device->block_device,
                          data_struct,
                          GET_FIRST_CLUSTER(dir_entry),
                          &last_cluster);
          if (retval != SOS_OK)
              return retval;
      }
    }
    dir_entry->DIR_FileSize = new_size;
    dir_entry->DIR_Attr |= ATTR_ARCHIVE;
  
    return SOS_OK;
  }
  
  /**
   * This function syncronize the in-memory data on the disk.
   * Datas to write on disk are directory entry of the node and, for files, mappings.
   */
  static sos_ret_t fat_sync_node(struct sos_fs_node *this)
  {
    sos_ret_t retval = SOS_OK;
    sos_size_t clus_size, clus_size_write;
    sos_ui64_t storage_location;
  
    if (this->custom_data != NULL && this->storage_location != 0) {
      /* Sync file's mappings */
      if (this->type == SOS_FS_NODE_REGULAR_FILE) {
          struct sos_fat_file *fat_file = (struct sos_fat_file *)this->custom_data;
  
          /* Mapping list is empty? */
          if (fat_file->list != NULL) {
              struct sos_fat_mmap_page_list *elt = fat_file->list;
  
              struct sos_fat_data_structure *data_struct =
                  (struct sos_fat_data_structure *)this->fs->custom_data;
  
              clus_size = clus_size_write = data_struct->BootSector.BPB_SecPerClus *
                          data_struct->BootSector.BPB_BytsPerSec;
  
              /* Loop on the mappings */
              while (elt != NULL) {
                  unsigned int i;
                  /* Loop on the pages of the current mapping */
                  for (i = 0; i < SOS_PAGE_SIZE / clus_size; i++) {
                      retval = sos_fat_helper_storage_location_of_file(this->fs->device->block_device,
                                          data_struct,
                                          GET_FIRST_CLUSTER(&fat_file->dir_entry),
                                          elt->offset + (i * clus_size),
                                          &storage_location);
                      if (retval != SOS_OK) {
                          return retval;
                      }
  
                      /* Write on disk the page */
                      sos_blockdev_kernel_write(this->fs->device->block_device,
                              storage_location,
                              elt->mmap_page + (i * clus_size),
                              &clus_size_write);
                      if (retval != SOS_OK) {
                          return retval;
                      } else if (clus_size_write != clus_size) {
                          return -SOS_EIO;
                      }
                  }
  
                  elt = elt->next;
              }
          }
      }
  
      /* Sync the directory entry of the node */
      retval = sos_fat_helper_set_directory_entry(this->fs->device->block_device,
                                  this->storage_location,
                                  (struct sos_fat_directory_entry *) this->custom_data);
    }
  
    return retval;
  }
  
  
  static sos_ret_t fat_chmod_node(struct sos_fs_node * this,
                                     sos_ui32_t access_rights)
  {
    struct sos_fat_directory_entry * dir_entry =
      (struct sos_fat_directory_entry*)this->custom_data;
  
    if (dir_entry != NULL) {
      if ( (access_rights & SOS_FS_READABLE) &&
              !(access_rights & SOS_FS_WRITABLE) ) {
          /* Read-Only case */
          if (! (dir_entry->DIR_Attr & ATTR_READ_ONLY)) {
              dir_entry->DIR_Attr |= ATTR_READ_ONLY;
              dir_entry->DIR_Attr |= ATTR_ARCHIVE;
          }
      } else {
          /* Read-Write case */
          if ( (access_rights & SOS_FS_READABLE) &&
              (dir_entry->DIR_Attr & ATTR_READ_ONLY) ) {
              dir_entry->DIR_Attr &= ~ATTR_READ_ONLY;
              dir_entry->DIR_Attr |= ATTR_ARCHIVE;
          }
      }
    }
  
    this->access_rights = access_rights;
  
    return SOS_OK;
  }
  
  
  static struct sos_fs_node_ops_file fat_ops_file =
    (struct sos_fs_node_ops_file){
      .truncate = fat_truncate,
      .stat     = fat_stat_node,
      .chmod    = fat_chmod_node,
      .sync     = fat_sync_node
    };
  
  
  /** Callback when nothing (in particular no sos_fs_nscache_node) make
      reference to the node */
  static sos_ret_t fat_node_destructor(struct sos_fs_node * this)
  {
    sos_ret_t retval;
    struct sos_fat_directory_entry * dir_entry =
      (struct sos_fat_directory_entry*)this->custom_data;
  
    if (dir_entry != NULL) {
      /* 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 && this->storage_location != 0)
      {
          /* Disable the directory entry of the node */
          dir_entry->DIR_Name[0] = 0xE5;
  
          /* Free allocated clusters of the file */
          retval = sos_fat_helper_remove_all_cluster(this->fs->device->block_device,
                                          this->fs->custom_data,
                                          GET_FIRST_CLUSTER(dir_entry));
          if (retval != SOS_OK) {
              return retval;
          }
  
          /* Write modified directory entry */
          retval = sos_fat_helper_set_directory_entry(this->fs->device->block_device,
                                                  this->storage_location,
                                                  dir_entry);
          if (retval != SOS_OK) {
              return retval;
          }
      }
  
      /* Free file's mappings elements */
      if (this->type == SOS_FS_NODE_REGULAR_FILE) {
          struct sos_fat_mmap_page_list *elt_tmp, *elt = ((struct sos_fat_file *) this->custom_data)->list;
          while (elt != NULL) {
              sos_kmem_vmm_free(elt->mmap_page);
              elt_tmp = elt->next;
              sos_kfree((sos_vaddr_t)elt);
              elt = elt_tmp;
          }
      }
      sos_kfree((sos_vaddr_t) dir_entry);
    }
  
    sos_kfree((sos_vaddr_t) this);
  
    return SOS_OK;
  }
  
  
  static sos_ret_t fat_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(struct sos_fs_opened_file), 0);
    if (! of)
      return -SOS_ENOMEM;
  
    memset(of, 0x0, sizeof(*of));
    of->owner      = owner;
    of->duplicate  = fat_duplicate_opened_file;
    of->open_flags = open_flags;
    of->ops_file   = &fat_ops_opened_file;
    if (this->type == SOS_FS_NODE_DIRECTORY)
      of->ops_dir  = & fat_ops_opened_dir;
  
    *result_of = of;
    return SOS_OK;
  }
  
  
  static sos_ret_t fat_close_opened_file(struct sos_fs_node * this,
                                            struct sos_fs_opened_file * of)
  {
    if (of->custom_data != NULL)
      sos_kfree((sos_vaddr_t)of->custom_data);
  
    sos_kfree((sos_vaddr_t)of);
  
    return SOS_OK;
  }
  
  static sos_ret_t fat_dir_lookup(struct sos_fs_node *this,
                                     const char * name, sos_ui16_t namelen,
                                     sos_ui64_t * result_storage_location)
  {
    sos_ret_t retval;
    sos_ui64_t sector_nr, storage_location;
    sos_si64_t offset;
    struct sos_fat_directory_entry *this_dir_entry, *dir_entry;
    struct sos_fat_data_structure *data_struct = (struct sos_fat_data_structure *) this->fs->custom_data;
  
    this_dir_entry = (struct sos_fat_directory_entry*)this->custom_data;
  
    dir_entry = (struct sos_fat_directory_entry*) sos_kmalloc(sizeof(struct sos_fat_directory_entry), 0);
    if (dir_entry == NULL)
      return -SOS_ENOMEM;
  
    if (this_dir_entry != NULL) {
      sector_nr = sos_fat_helper_first_sector_of_cluster(data_struct,
          GET_FIRST_CLUSTER(this_dir_entry));
    } else {
      sector_nr = data_struct->FirstRootDirSecNum;
    }
    storage_location = sector_nr * data_struct->BootSector.BPB_BytsPerSec;
  
    /* Loop on the directory entries */
    do {
      retval = sos_fat_helper_get_next_directory_entry(this->fs->device->block_device,
          data_struct,
          storage_location,
          &dir_entry,
          &storage_location,
          &offset);
      if (retval != SOS_OK) {
          sos_kfree((sos_vaddr_t)dir_entry);
          return retval;
      }
  
      /* Is it the good node entry? */
      if (sos_fat_helper_same_name(name, namelen, dir_entry) == TRUE)
      {
          *result_storage_location = storage_location;
          sos_kfree((sos_vaddr_t) dir_entry);
          return SOS_OK;
      }
      storage_location += sizeof(struct sos_fat_directory_entry);
    } while (retval != -SOS_ENOENT);
  
    /* No entry match */
    sos_kfree((sos_vaddr_t) dir_entry);
    return -SOS_ENOENT;
  }
  
  /** fat_link is the specific FAT VFS hard link function.
   *
   * The node 'node' in parameter will be link with the parent node 'this'. A
   * link occurs when a hard link on 'node' is needed.
   * FAT filesystem doesn't allow much than one hard link then link can only be
   * execute when a file or a directory is created.
   *
   * This function is used in these VFS operations:
   * rename, link, open (with the O_CREAT flag), creat, mkdir, symlink, mknod.
   *
   * FAT doesn't allow symlink, mknod operation.
   * rename operation doesn't work too because of the SOS implementation of this
   * operation. In SOS, rename begins by made a hard link on the old file to
   * rename and then unlink the old file. Makes a hard link on the old file is
   * forbidden.
   *
   * Others operations are allowed. This function makes many things:
   * - Check is a hard link is asked. In this case returns an error.
   * - Copy the file name in the directory entry structure.
   * - Allocate the first cluster of the file and clean it.
   * - Mark the corresponding FAT cluster entry as EOC.
   * - Create the directory entries '.' and '..' if the node is a directory.
   * - Set the new directory entry in its parent directory 'this'.
   * - Insert the new node in the nodes' hashtable.
   * - Increment the link counter of the node and its parent.
   *
   */
  static sos_ret_t fat_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)
  {
    sos_ret_t retval;
    sos_ui32_t new_cluster;
    struct sos_fat_directory_entry * dir_entry;
    struct sos_fat_data_structure *data_struct = (struct sos_fat_data_structure*)this->fs->custom_data;
  
    /* Hard link not allowed in FAT */
    if (node->storage_location != 0) {
      if (node->ondisk_lnk_cnt > 0) {
          if (islower(entry_name[0])) {
              ((struct sos_fat_directory_entry*)node->custom_data)->DIR_Name[0] = entry_name[0] - 0x20;
          } else {
              ((struct sos_fat_directory_entry*)node->custom_data)->DIR_Name[0] = entry_name[0];
          }
  
          retval = sos_fat_helper_set_directory_entry(this->fs->device->block_device,
                                                  node->storage_location,
                                                  (struct sos_fat_directory_entry *) node->custom_data);
          if (retval != SOS_OK) {
              return retval;
          }
      }
  
      if (node->ondisk_lnk_cnt == 0) {
          node->ondisk_lnk_cnt++;
          this->ondisk_lnk_cnt++;
      }
  
      return -SOS_ENOSUP;
    }
  
    if (node->type == SOS_FS_NODE_REGULAR_FILE) {
      dir_entry = &((struct sos_fat_file *)node->custom_data)->dir_entry;
    } else {
      dir_entry = (struct sos_fat_directory_entry*)sos_kmalloc(sizeof(struct sos_fat_directory_entry), 0);
      if (! dir_entry)
        return -SOS_ENOMEM;
    }
  
    memset(dir_entry, 0, sizeof(struct sos_fat_directory_entry));
  
    /* Copy the node name in the directory entry structure*/
    int i = 0, j, k;
  
    if (entry_namelen > 12) {
      sos_kfree((sos_vaddr_t) dir_entry);
      return -SOS_ENAMETOOLONG;
    }
  
    /* Case of the Kanji 0xE5 in the first character */
    if ((entry_name[i] & 0xFF) == 0xE5) {
      dir_entry->DIR_Name[i] = 0x05;
      i++;
    }
  
    /* Copy the name part of the node name */
    for (; i < 8; i++) {
      if (entry_name[i] == '.' || i >= entry_namelen) {
          break;
      }
      if (IS_FAT_VALID_CHAR(entry_name[i])) {
          if (islower(entry_name[i])) {
              dir_entry->DIR_Name[i] = entry_name[i] - 0x20;
          } else {
              dir_entry->DIR_Name[i] = entry_name[i];
          }
      } else {
          sos_kfree((sos_vaddr_t) dir_entry);
          return -SOS_EINVAL;
      }
    }
  
    if (i < entry_namelen && entry_name[i] != '.') {
      sos_kfree((sos_vaddr_t) dir_entry);
      return -SOS_ENAMETOOLONG;
    }
  
    /* Fill rest by space ' ' */
    for (k = i; k < 8; k++) {
      dir_entry->DIR_Name[k] = ' ';
    }
    i++;
  
    /* Copy the extension part of the node name */
    for (j = 0; j < 3; j++) {
      if (i >= entry_namelen)
          break;
      if (IS_FAT_VALID_CHAR(entry_name[i])) {
          if (islower(entry_name[i])) {
              dir_entry->DIR_Name[j+8] = entry_name[i] - 0x20;
          } else {
              dir_entry->DIR_Name[j+8] = entry_name[i];
          }
          i++;
      } else {
          sos_kfree((sos_vaddr_t) dir_entry);
          return -SOS_EINVAL;
      }
    }
  
    if (i < entry_namelen && j == 2) {
      sos_kfree((sos_vaddr_t) dir_entry);
      return -SOS_ENAMETOOLONG;
    }
  
    /* Fill rest by space ' ' */
    for (k = j; k < 3; k++) {
      dir_entry->DIR_Name[k+8] = ' ';
    }
  
    /* Find the first cluster of the node */
    retval = sos_fat_helper_find_free_cluster(this->fs->device->block_device, data_struct, &new_cluster);
    if (retval != SOS_OK) {
      sos_kfree((sos_vaddr_t) dir_entry);
      return retval;
    }
  
    /* Set EOC marker in the corresponding FAT cluster entry */
    retval = sos_fat_helper_set_cluster_value(
              this->fs->device->block_device,
              data_struct,
              new_cluster,
              data_struct->EndOfCluster);
  
    if (retval != SOS_OK) {
      sos_kfree((sos_vaddr_t) dir_entry);
      return retval;
    }
  
    dir_entry->DIR_FstClusHI = (new_cluster>>16) & 0xFFFF;
    dir_entry->DIR_FstClusLO = new_cluster & 0xFFFF;
  
    sos_size_t cluster_size = (data_struct->BootSector.BPB_BytsPerSec *
                              data_struct->BootSector.BPB_SecPerClus);
  
    sos_ui64_t new_cluster_storage_location =
          sos_fat_helper_first_sector_of_cluster(data_struct, new_cluster) *
          data_struct->BootSector.BPB_BytsPerSec;
  
    /* Clean the new cluster */
    sos_vaddr_t zero_buf = sos_kmalloc(cluster_size, 0);
    if (((void *)zero_buf) == NULL) {
      sos_kfree((sos_vaddr_t)dir_entry);
      return -SOS_ENOMEM;
    }
    memset((void *)zero_buf, 0, cluster_size);
    retval = sos_blockdev_kernel_write(this->fs->device->block_device,
                                      new_cluster_storage_location,
                                      zero_buf,
                                      &cluster_size);
    sos_kfree((sos_vaddr_t)zero_buf);
    if (retval != SOS_OK) {
      sos_kfree((sos_vaddr_t)dir_entry);
      return retval;
    } else if (cluster_size !=
          (data_struct->BootSector.BPB_BytsPerSec *
          data_struct->BootSector.BPB_SecPerClus)) {
      sos_kfree((sos_vaddr_t)dir_entry);
      return -SOS_EIO;
    }
  
    dir_entry->DIR_Attr = ATTR_ARCHIVE;
    if (! (node->access_rights & SOS_FS_WRITABLE) ) {
      dir_entry->DIR_Attr |= ATTR_READ_ONLY;
    }
  
    /* Write entry . and .. for a directory node */
    if (node->type == SOS_FS_NODE_DIRECTORY) {
      dir_entry->DIR_Attr |= ATTR_DIRECTORY;
  
      struct sos_fat_directory_entry dot_entry;
      memcpy(&dot_entry, dir_entry, sizeof(struct sos_fat_directory_entry));
      memcpy(dot_entry.DIR_Name, ".          ", 11);
      dot_entry.DIR_Attr = ATTR_DIRECTORY;
      retval = sos_fat_helper_set_directory_entry(this->fs->device->block_device,
                                              new_cluster_storage_location,
                                              &dot_entry);
      if (retval != SOS_OK) {
          sos_kfree((sos_vaddr_t)dir_entry);
          return retval;
      }
  
      struct sos_fat_directory_entry *dotdot_entry = this->custom_data;
      if (dotdot_entry != NULL) {
          memcpy(&dot_entry, dotdot_entry, sizeof(struct sos_fat_directory_entry));
      } else {
          memset(&dot_entry, 0, sizeof(struct sos_fat_directory_entry));
      }
      memcpy(dot_entry.DIR_Name, "..         ", 11);
      dot_entry.DIR_Attr = ATTR_DIRECTORY;
      retval = sos_fat_helper_set_directory_entry(this->fs->device->block_device,
                                                  new_cluster_storage_location + sizeof(struct sos_fat_directory_entry),
                                                  &dot_entry);
      if (retval != SOS_OK) {
          sos_kfree((sos_vaddr_t)dir_entry);
          return retval;
      }
    }
    node->custom_data = dir_entry;
  
    sos_ui64_t sector_nr;
    if (this->custom_data != NULL) {
      struct sos_fat_directory_entry * this_dir_entry = this->custom_data;
      sector_nr = sos_fat_helper_first_sector_of_cluster(data_struct,
              GET_FIRST_CLUSTER(this_dir_entry));
    } else {
      sector_nr = data_struct->FirstRootDirSecNum;
    }
  
    sos_hash_remove(node->fs->nodecache, node);
  
    /* While a storage location is used, it can't be re-used */
    struct sos_fs_node *unused_node;
    node->storage_location = sector_nr * data_struct->BootSector.BPB_BytsPerSec;
    do {
      retval = sos_fat_helper_find_free_directory_entry(
          this->fs->device->block_device,
          data_struct,
          node->storage_location,
          &node->storage_location);
      if (retval != SOS_OK) {
          sos_kfree((sos_vaddr_t) dir_entry);
          return retval;
      }
  
      unused_node = (struct sos_fs_node *) sos_hash_lookup(this->fs->nodecache, &node->storage_location);
      node->storage_location += sizeof(struct sos_fat_directory_entry);
    } while (unused_node != NULL);
  
    node->storage_location -= sizeof(struct sos_fat_directory_entry);
  
    /* Set the new directory entry in its parent directory */
    retval = sos_fat_helper_set_directory_entry(this->fs->device->block_device, node->storage_location, dir_entry);
    if (retval != SOS_OK) {
      sos_kfree((sos_vaddr_t) dir_entry);
      return retval;
    }
  
    /* Insert the node in the node hashtable */
    sos_hash_insert(node->fs->nodecache, node);
  
    /* Increment the link counter of the node and its parent */
    node->ondisk_lnk_cnt ++;
    this->ondisk_lnk_cnt ++;
  
    return SOS_OK;
  }
  
  /**
   * This function search the entry in parameter and, if it present, decrements the
   * ondisk_lnk_cnt counter for the node and its parent directory.
   */
  static sos_ret_t
  fat_unlink(struct sos_fs_node *this,
                const struct sos_process *actor,
                const char * entry_name, sos_ui16_t entry_namelen)
  {
    sos_ret_t retval;
    struct sos_fat_data_structure *data_struct = (struct sos_fat_data_structure *) this->fs->custom_data;
    struct sos_fat_directory_entry *result_dir_entry, *dir_entry = (struct sos_fat_directory_entry *) this->custom_data;
  
    result_dir_entry = (struct sos_fat_directory_entry *) sos_kmalloc(sizeof(struct sos_fat_directory_entry), 0);
    if (result_dir_entry == NULL)
      return -SOS_ENOMEM;
  
    sos_ui64_t storage_location, sector_nr;
    sos_si64_t offset;
    if (dir_entry != NULL) {
      sos_ui32_t cluster_nr = GET_FIRST_CLUSTER(dir_entry);
      sector_nr = sos_fat_helper_first_sector_of_cluster(data_struct, cluster_nr);
    } else {
      sector_nr = data_struct->FirstRootDirSecNum;
    }
  
    storage_location = sector_nr * data_struct->BootSector.BPB_BytsPerSec;
  
    /* Find the node entry in the current directory */
    do {
      /* Read the next directory entry */
      retval = sos_fat_helper_get_next_directory_entry(
                  this->fs->device->block_device,
                  data_struct,
                  storage_location,
                  &result_dir_entry,
                  &storage_location,
                  &offset);
      if (retval != SOS_OK) {
          sos_kfree((sos_vaddr_t) result_dir_entry);
          return retval;
      }
  
      /* Compare name and directory entry */
      if (sos_fat_helper_same_name(entry_name, entry_namelen, result_dir_entry) == TRUE) {
          this->ondisk_lnk_cnt--;
  
          struct sos_fs_node *node = (struct sos_fs_node *) sos_hash_lookup(this->fs->nodecache, &storage_location);
          node->ondisk_lnk_cnt--;
  
          /* The node should be destroy on the disk? */
          if (node->ondisk_lnk_cnt <= 0) {
              /* Free this directory entry */
              result_dir_entry->DIR_Name[0] = 0xE5;
  
              retval = sos_fat_helper_set_directory_entry(
                                          this->fs->device->block_device,
                                          storage_location,
                                          result_dir_entry);
              sos_kfree((sos_vaddr_t) result_dir_entry);
              if (retval != SOS_OK) {
                  return retval;
              }
          }
  
          return SOS_OK;
      }
  
      /* Next directory entry */
      storage_location += sizeof(struct sos_fat_directory_entry);
    } while (result_dir_entry->DIR_Name[0] != 0);
  
    sos_kfree((sos_vaddr_t) result_dir_entry);
  
    return -SOS_ENOENT;
  }
  
  
  static struct sos_fs_node_ops_dir fat_ops_dir
    = (struct sos_fs_node_ops_dir){
      .lookup = fat_dir_lookup,
      .link   = fat_link,
      .unlink = fat_unlink
    };
  /* }}} */
  
  /* ********************************************************
   * FS instance operations {{{
   */
  
  /**
   * This function fetch the directory entry structure and "convert" it in the
   * sos_fs_node generic VFS structure.
   */
  static sos_ret_t
  fat_fetch_node_from_disk(struct sos_fs_manager_instance * this,
                           sos_ui64_t storage_location,
                           struct sos_fs_node ** result)
  {
    sos_ret_t retval;
    struct sos_fs_node * fat_node;
    struct sos_fat_data_structure *data_struct = this->custom_data;
  
    /* clean the result in case of error */
    *result = NULL;
  
    fat_node = (struct sos_fs_node *) sos_kmalloc(sizeof(struct sos_fs_node), 0);
    if (fat_node == NULL)
      return -SOS_ENOMEM;
  
    /* Initialize node */
    memset(fat_node, 0, sizeof(struct sos_fs_node));
    fat_node->fs                   = this;
    fat_node->storage_location     = storage_location;
    fat_node->ondisk_lnk_cnt       = 1;
    fat_node->ops_file             = &fat_ops_file;
    fat_node->destructor           = fat_node_destructor;
    fat_node->new_opened_file      = fat_new_opened_file;
    fat_node->close_opened_file    = fat_close_opened_file;
  
    sos_size_t dir_entry_size = sizeof(struct sos_fat_directory_entry);
    struct sos_fat_directory_entry *dir_entry =
          (struct sos_fat_directory_entry *)
          sos_kmalloc(sizeof(struct sos_fat_directory_entry), 0);
    if (dir_entry == NULL) {
      sos_kfree((sos_vaddr_t)fat_node);
      return -SOS_ENOMEM;
    }
  
    /* Read the on disk node's informations */
    retval = sos_fat_helper_read(this->device->block_device, data_struct,
                                  storage_location, (sos_vaddr_t)dir_entry, &dir_entry_size);
    if (retval != SOS_OK){
      sos_kfree((sos_vaddr_t)fat_node);
      sos_kfree((sos_vaddr_t)dir_entry);
      return retval;
    }
  
    /* Set type and access of the node */
    fat_node->custom_data = dir_entry;
    if (dir_entry->DIR_Attr & ATTR_DIRECTORY) {
      fat_node->type = SOS_FS_NODE_DIRECTORY;
      fat_node->ops_dir = &fat_ops_dir;
    } else {
      fat_node->type = SOS_FS_NODE_REGULAR_FILE;
    }
    fat_node->access_rights = SOS_FS_READABLE | SOS_FS_EXECUTABLE;
    if (! (dir_entry->DIR_Attr & ATTR_READ_ONLY) ) {
      fat_node->access_rights |= SOS_FS_WRITABLE;
    }
  
    /* Initialize ondisk_lnk_cnt for directory.
     * For file ondisk_lnk_cnt must be 1 because
     * hard link is forbidden in FAT */
    if (fat_node->type == SOS_FS_NODE_DIRECTORY)
      {
          fat_node->ondisk_lnk_cnt--;
          sos_si64_t offset;
          int i;
          storage_location = sos_fat_helper_first_sector_of_cluster(data_struct,
                              GET_FIRST_CLUSTER(dir_entry)) *
                              data_struct->BootSector.BPB_BytsPerSec;
          /* Skip directory '.' and '..' to the ondisk_lnk_cnt counter */
          for (i = 0; i < 2; i++) {
              retval = sos_fat_helper_get_next_directory_entry(this->device->block_device,
                                                          data_struct,
                                                          storage_location,
                                                          NULL,
                                                          &storage_location,
                                                          &offset);
              if (retval != SOS_OK && retval != -SOS_ENOENT) {
                  sos_kfree((sos_vaddr_t) fat_node);
                  sos_kfree((sos_vaddr_t) dir_entry);
                  return retval;
              }
  
              storage_location += sizeof(struct sos_fat_directory_entry);
          }
  
          /* Increment ondisk_lnk_cnt on each directory entry */
          while (retval != -SOS_ENOENT) {
              fat_node->ondisk_lnk_cnt++;
  
              retval = sos_fat_helper_get_next_directory_entry(this->device->block_device,
                                                          data_struct,
                                                          storage_location,
                                                          NULL,
                                                          &storage_location,
                                                          &offset);
              if (retval != SOS_OK && retval != -SOS_ENOENT) {
                  sos_kfree((sos_vaddr_t) fat_node);
                  sos_kfree((sos_vaddr_t) dir_entry);
                  return retval;
              }
              storage_location += sizeof(struct sos_fat_directory_entry);
          }
      }
  
    /* Initialize mapping structure */
    if (fat_node->type == SOS_FS_NODE_REGULAR_FILE)
      {
        struct sos_fat_file * fat_file = (struct sos_fat_file *) sos_kmalloc(sizeof(struct sos_fat_file), 0);
        if (fat_file == NULL)
          return -SOS_ENOMEM;
  
        memcpy(&fat_file->dir_entry, dir_entry, sizeof(struct sos_fat_directory_entry));
        memset(&fat_file->mapres, 0,
          sizeof(struct sos_fat_file) - sizeof(struct sos_fat_directory_entry));
        sos_kfree((sos_vaddr_t) dir_entry);
  
        fat_file->mapres.allowed_access_rights
          = SOS_VM_MAP_PROT_READ
          | SOS_VM_MAP_PROT_WRITE
          | SOS_VM_MAP_PROT_EXEC;
        fat_file->mapres.custom_data    = fat_node;
        fat_file->mapres.mmap           = fat_new_mapping;
        fat_file->num_mappings          = 0;
  
        fat_node->custom_data = fat_file;
      }
  
    /* Set the sos_fs_node structure result */
    *result = fat_node;
  
    return SOS_OK;
  }
  
  
  /*
   * This function allocate a new node for the VFS system but this node is not
   * write on disk in this function. It will be write in the sync VFS operation.
   */
  static sos_ret_t
  fat_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 sos_fs_node * fat_node;
  
    /* clean the result in case of error */
    *result = NULL;
  
    /* Allow only DIRs or FILEs files */
    if ((type != SOS_FS_NODE_REGULAR_FILE)
        && (type != SOS_FS_NODE_DIRECTORY))
      return -SOS_ENOSUP;
  
    fat_node = (struct sos_fs_node*) sos_kmalloc(sizeof(struct sos_fs_node), 0);
    if (! fat_node)
      return -SOS_ENOMEM;
  
    memset(fat_node, 0x0, sizeof(struct sos_fs_node));
  
    /* Initialize "file" */
    fat_node->inmem_ref_cnt     = 1;
    fat_node->type              = type;
    fat_node->access_rights     = access_rights;
    fat_node->ops_file          = &fat_ops_file;
    fat_node->destructor        = fat_node_destructor;
    fat_node->new_opened_file   = fat_new_opened_file;
    fat_node->close_opened_file = fat_close_opened_file;
  
    if (type == SOS_FS_NODE_DIRECTORY)
      fat_node->ops_dir = &fat_ops_dir;
  
    /* Initialize mapping structure */
    if (type == SOS_FS_NODE_REGULAR_FILE)
      {
        struct sos_fat_file * fat_file = (struct sos_fat_file *) sos_kmalloc(sizeof(struct sos_fat_file), 0);
        if (fat_file == NULL)
          return -SOS_ENOMEM;
  
        memset(fat_file, 0, sizeof(struct sos_fat_file));
        fat_file->mapres.allowed_access_rights
          = SOS_VM_MAP_PROT_READ
          | SOS_VM_MAP_PROT_WRITE
          | SOS_VM_MAP_PROT_EXEC;
        fat_file->mapres.custom_data    = fat_node;
        fat_file->mapres.mmap           = fat_new_mapping;
        fat_file->num_mappings          = 0;
  
        fat_node->custom_data = fat_file;
      }
  
    /* Set the sos_fs_node structure result */
    *result = fat_node;
  
    return SOS_OK;
  }
  
  /*
   * By default the VFS is case sensitive but in the FAT file system the case
   * isn't. That's why we redefine the file node compare function of the VFS. This
   * function is generic: it use only VFS name and not FAT DIR_Name field.
   */
  static sos_bool_t
  fat_nsnode_same_name(const char *name1, sos_ui16_t namelen1,
                    const char *name2, sos_ui16_t namelen2)
  {
    if (!name1)
      SOS_ASSERT_FATAL(namelen1 == 0);
    if (!name2)
      SOS_ASSERT_FATAL(namelen2 == 0);
  
    if (namelen1 != namelen2)
      return FALSE;
  
    if (namelen1 == 0)
      return TRUE;
  
    int i;
    for (i = 0; i < namelen1; i++) {
      if (name1[i] == name2[i]) {
          continue;
      } else if ( (isupper(name1[i]) || islower(name1[i]))
                  && (isupper(name2[i]) || islower(name2[i])) ) {
          if (isupper(name1[i])) {
              if (name1[i] != name2[i] - 0x20) {
                  return FALSE;
              }
          } else {
              if (name1[i] - 0x20 != name2[i]) {
                  return FALSE;
              }
          }
      } else {
          return FALSE;
      }
    }
  
    return TRUE;
  }
  /*}}}*/
  
  /* ********************************************************
   * FS type (mount/umount) operations {{{
   */
  
  static sos_ret_t fat_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 sos_fs_manager_instance * fs;
    struct sos_fs_node * fsnode_root;
    struct sos_hash_table * hash;
    sos_size_t  first_sector_size;
    struct sos_fat_boot_sector *sos_fat_bs;
  
    *mounted_fs = (struct sos_fs_manager_instance*)NULL;
  
    /* Create FS node hash table */
    hash = sos_hash_create("fat 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 sos_fs_manager_instance*)
      sos_kmalloc(sizeof(struct sos_fs_manager_instance), 0);
    if (! fs)
      {
        sos_hash_dispose(hash);
        return -SOS_ENOMEM;
      }
  
    /* Initialize fs structure */
    memset(fs, 0x0, sizeof(struct sos_fs_manager_instance));
    fs->fs_type              = this;
    fs->allocate_new_node    = fat_allocate_new_node;
    fs->fetch_node_from_disk = fat_fetch_node_from_disk;
    fs->device               = device;
    /*fs->statfs             = fat_statfs;*/
    fs->nsnode_same_name     = fat_nsnode_same_name;
    fs->nodecache            = hash;
  
    /* Read boot sector */
    first_sector_size = sizeof(struct sos_fat_boot_sector);
    struct sos_fat_data_structure *data_struct =
          (struct sos_fat_data_structure*)
          sos_kmalloc(sizeof(struct sos_fat_data_structure), 0);
    retval = sos_blockdev_kernel_read(device->block_device,
                                      0 /* the FAT boot sector nr */,
                                      (sos_vaddr_t)&data_struct->BootSector,
                                      &first_sector_size);
    if ( (retval != SOS_OK) ||
         (first_sector_size != sizeof(struct sos_fat_boot_sector)) )
    {
      sos_bochs_printf("Can't read first sector of the fat partition (only %dB read on %dB)\n",
                  first_sector_size,
                  sizeof(struct sos_fat_boot_sector));
      return retval;
    }
  
    fs->custom_data = (void *)data_struct;
    sos_fat_bs = &data_struct->BootSector;
  
    /* Compute FAT fs informations */
  
    if (sos_fat_bs->BPB_FATSz16 != 0)
      data_struct->FATSz = sos_fat_bs->BPB_FATSz16;
    else
      data_struct->FATSz = sos_fat_bs->BPB_specific.fat32_BPB.BPB_FATSz32;
  
    sos_bochs_printf("FAT size: %u\nNumber FATs %u\n",
          (unsigned int)data_struct->FATSz,
          (unsigned int)sos_fat_bs->BPB_NumFATs);
  
    if (sos_fat_bs->BPB_BytsPerSec == 0) {
      sos_bochs_printf("Bytes per sector == 0 : NOT A FAT PARTITION");
      return -SOS_EIO;
    }
  
    data_struct->RootDirSectors =
      SOS_ALIGN_SUP((sos_fat_bs->BPB_RootEntCnt * 32),
          sos_fat_bs->BPB_BytsPerSec) /
      sos_fat_bs->BPB_BytsPerSec;
  
    sos_bochs_printf("Root Directory Sectors: %u\n", (unsigned int) data_struct->RootDirSectors);
  
    data_struct->FirstDataSector =
      sos_fat_bs->BPB_RsvdSecCnt +
      (sos_fat_bs->BPB_NumFATs * data_struct->FATSz) +
      data_struct->RootDirSectors;
  
    sos_bochs_printf("First Data Sector: %u\n", (unsigned int) data_struct->FirstDataSector);
  
    if (sos_fat_bs->BPB_TotSec16 != 0)
      data_struct->TotSec = sos_fat_bs->BPB_TotSec16;
    else
      data_struct->TotSec = sos_fat_bs->BPB_TotSec32;
  
    sos_bochs_printf("Total sector: %u\n", (unsigned int) data_struct->TotSec);
  
    data_struct->DataSec = data_struct->TotSec -
      (sos_fat_bs->BPB_RsvdSecCnt +
      (sos_fat_bs->BPB_NumFATs * data_struct->FATSz) +
      data_struct->RootDirSectors);
  
    sos_bochs_printf("Data sector: %u\n", (unsigned int) data_struct->DataSec);
  
    if (sos_fat_bs->BPB_SecPerClus == 0) {
      sos_bochs_printf("Sector per cluster == 0 : NOT A FAT PARTITION");
      return -SOS_EIO;
    }
  
    data_struct->CountOfClusters = data_struct->DataSec / sos_fat_bs->BPB_SecPerClus;
  
    sos_bochs_printf("Count of Clusters: %u\n", (unsigned int) data_struct->CountOfClusters);
  
    /* Store the FAT type in the data structure. */
    /* In the following code, when it says <, it does not mean <=. Note also
     * that the numbers are correct. The first number for FAT12 is 4085; the
     * second number for FAT16 is 65525. These numbers and the '<' signs are not
     * wrong.
     * This is the one and only way that FAT type is determined. There is no
     * such thing as a FAT12 volume that has more than 4084 clusters. There is
     * no such thing as a FAT16 volume that has less than 4085 clusters or more
     * than 65,524 clusters. There is no such thing as a FAT32 volume that has
     * less than 65,525 clusters. */
    if (data_struct->CountOfClusters < FAT12_CLUSTER_LIMIT) {
      data_struct->FAT_Type = FAT12;
    } else if (data_struct->CountOfClusters < FAT16_CLUSTER_LIMIT) {
      data_struct->FAT_Type = FAT16;
    } else {
      data_struct->FAT_Type = FAT32;
    }
  
    /* Store the End Of Cluster marker of the corresponding FAT type. */
    if (data_struct->FAT_Type == FAT16) {
      data_struct->EndOfCluster = FAT16_EOC;
    } else {
      data_struct->EndOfCluster = FAT32_EOC;
    }
  
    sos_bochs_printf("Volume is %s (count of clusters = %u)\n",
      sos_fat_type_str(data_struct->FAT_Type),
      (unsigned int)data_struct->CountOfClusters);
  
    if (data_struct->FAT_Type != FAT32) {
      data_struct->FirstRootDirSecNum = sos_fat_bs->BPB_RsvdSecCnt + (sos_fat_bs->BPB_NumFATs * sos_fat_bs->BPB_FATSz16);
    } else {
      data_struct->FirstRootDirSecNum = sos_fat_bs->BPB_specific.fat32_BPB.BPB_RootClus;
    }
  
    sos_bochs_printf("First Root Directory Sector Number: %u\n",
          (unsigned int) data_struct->FirstRootDirSecNum);
  
    fsnode_root = (struct sos_fs_node *) sos_kmalloc(sizeof(struct sos_fs_node), 0);
    if (fsnode_root == NULL)
      {
        sos_hash_dispose(hash);
        sos_kfree((sos_vaddr_t) fs);
        return retval;
      }
  
    /* Initialize the root directory structure */
    memset(fsnode_root, 0, sizeof(struct sos_fs_node));
    fsnode_root->fs                        = fs;
    fsnode_root->storage_location          = (data_struct->FirstRootDirSecNum
                                              * data_struct->BootSector.BPB_BytsPerSec)
                                              - sizeof(struct sos_fat_directory_entry);
    fsnode_root->inmem_ref_cnt             = 1;
    fsnode_root->type                      = SOS_FS_NODE_DIRECTORY;
    fsnode_root->access_rights             = SOS_FS_READABLE | SOS_FS_WRITABLE | SOS_FS_EXECUTABLE;
    fsnode_root->dev_id.device_class       = device->dev_id.device_class;
    fsnode_root->dev_id.device_instance    = device->dev_id.device_instance;
    fsnode_root->ops_file                  = &fat_ops_file;
    fsnode_root->ops_dir                   = &fat_ops_dir;
    fsnode_root->destructor                = fat_node_destructor;
    fsnode_root->new_opened_file           = fat_new_opened_file;
    fsnode_root->close_opened_file         = fat_close_opened_file;
    fsnode_root->custom_data               = NULL;
  
    /* Initialize ondisk_lnk_cnt for root directory. */
    sos_si64_t offset;
    sos_ui64_t storage_location = fsnode_root->storage_location;
    retval = sos_fat_helper_get_next_directory_entry(device->block_device,
                                                  data_struct,
                                                  storage_location,
                                                  NULL,
                                                  &storage_location,
                                                  &offset);
    if (retval != SOS_OK && retval != -SOS_ENOENT) {
      sos_hash_dispose(hash);
      sos_kfree((sos_vaddr_t) fs);
      sos_kfree((sos_vaddr_t) fsnode_root);
      return retval;
    }
  
    /* Count the number of entries in the root directory to initialize
     * the ondisk_lnk_cnt field. */
    while (retval != -SOS_ENOENT) {
      fsnode_root->ondisk_lnk_cnt++;
  
      storage_location += sizeof(struct sos_fat_directory_entry);
      retval = sos_fat_helper_get_next_directory_entry(device->block_device,
                                                  data_struct,
                                                  storage_location,
                                                  NULL,
                                                  &storage_location,
                                                  &offset);
      if (retval != SOS_OK && retval != -SOS_ENOENT) {
          sos_hash_dispose(hash);
          sos_kfree((sos_vaddr_t) fs);
          sos_kfree((sos_vaddr_t) fsnode_root);
          return retval;
      }
    }
  
    retval = sos_fs_register_fs_instance(fs, fsnode_root);
    sos_fs_unref_fsnode(fsnode_root);
    if (SOS_OK != retval)
      {
        sos_hash_dispose(hash);
        sos_kfree((sos_vaddr_t) fs);
        sos_kfree((sos_vaddr_t) fsnode_root);
        return retval;
      }
  
    *mounted_fs = fs;
    return SOS_OK;
  }
  
  
  static sos_ret_t fat_umount(struct sos_fs_manager_type * this,
                              struct sos_fs_manager_instance * mounted_fs)
  {
    struct sos_fat_data_structure * data_struct = (struct sos_fat_data_structure*)mounted_fs->custom_data;
  
    sos_hash_dispose(mounted_fs->nodecache);
  
    sos_kfree((sos_vaddr_t)data_struct);
  
    sos_fs_unregister_fs_instance(mounted_fs);
    sos_kfree((sos_vaddr_t)mounted_fs);
    return SOS_OK;
  }
  /* }}} */
  
  /* ********************************************************
   * FS register {{{
   */
  
  /** The initialization function of the FAT filesystem */
  sos_ret_t sos_fs_fat_subsystem_setup()
  {
    strzcpy(fat_type.name, "FAT", SOS_FS_MANAGER_NAME_MAXLEN);
    fat_type.mount  = fat_mount;
    fat_type.umount = fat_umount;
  
    return sos_fs_register_fs_type(&fat_type);
  }
  /* }}} */
  
  
 

sos-code-article9.5/drivers/fs_fat.h (1969-12-31 16:00:00.000000000 -0800 )
sos-code-article10/drivers/fs_fat.h (2007-03-14 11:00:05.000000000 -0700 )
(New file) 
Line 1 
  /* Copyright (C) 2007 Anthoine Bourgeois
  
     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_FAT_H_
  #define _SOS_FS_FAT_H_
  
  #include <sos/errno.h>
  
  
  /**
   * @file fs_fat.h
   *
   * Basic implementation of the FAT16/32 file system for SOS.
   */
  
  
  /**
   * Solely register the "FAT" FS type into the list of supported FS types
   */
  sos_ret_t sos_fs_fat_subsystem_setup(void);
  
  #endif
  
 

sos-code-article9.5/drivers/mem.c (2006-01-22 03:48:28.000000000 -0800 )
sos-code-article10/drivers/mem.c (2006-12-20 13:59:58.000000000 -0800 )
Line 91 
Line 91 
     + sos_umem_vmm_get_offset_in_resource(vr);     + sos_umem_vmm_get_offset_in_resource(vr);
  
   /* Don't allow demand paging of non kernel pages */   /* Don't allow demand paging of non kernel pages */
   if (vaddr >= SOS_PAGING_BASE_USER_ADDRESS)   if (! SOS_PAGING_IS_KERNEL_AREA(vaddr, 1))
  
   /* Lookup physical kernel page */   /* Lookup physical kernel page */
  
 

sos-code-article9.5/extra/bootsect.S (2006-01-22 03:48:28.000000000 -0800 )
sos-code-article10/extra/bootsect.S (2006-12-20 13:59:58.000000000 -0800 )
Line 1 
Line 1 
  
 /* /*
  * @(#) $Id: bootsect.S,v 1.12 2005/08/13 13:47:31 d2 Exp $  * @(#) $Id: bootsect.S 1310 2005-08-13 13:47:31Z d2 $
  * Auteurs : Thomas Petazzoni & Fabrice Gautier & Emmanuel Marty  * Auteurs : Thomas Petazzoni & Fabrice Gautier & Emmanuel Marty
  *           Jerome Petazzoni & Bernard Cassagne & coffeeman  *           Jerome Petazzoni & Bernard Cassagne & coffeeman
  
 

sos-code-article9.5/hwcore/bitmap.c (1969-12-31 16:00:00.000000000 -0800 )
sos-code-article10/hwcore/bitmap.c (2006-12-20 13:59:58.000000000 -0800 )
(New file) 
Line 1 
  /* Copyright (C) 2006 David Decotigny
  
     This program is free software; you can redistribute it and/or
     modify it under the terms of the GNU General Public License
     as published by the Free Software Foundation; either version 2
     of the License, or (at your option) any later version.
     
     This program is distributed in the hope that it will be useful,
     but WITHOUT ANY WARRANTY; without even the implied warranty of
     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     GNU General Public License for more details.
     
     You should have received a copy of the GNU General Public License
     along with this program; if not, write to the Free Software
     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
     USA. 
  */
  
  #include <sos/macros.h>
  
  #include "bitmap.h"
  
  
  /**
   * ffs: find first bit set (1 = LSb, 32 = MSb).
   *
   * @return 0 when none found
   *
   * @note: License is GPLv2, origin is Linux Kernel 2.6.14.3
   * (@see include/asm-i386/bitops.h)
   */
  static inline int word_ffs(sos_ui32_t x)
  {
          if (!x)
                  return 0;
  
          __asm__("bsfl %1,%0"
                  :"=r" (x)
                  :"rm" (x));
          return x+1;
  }
  
  
  /** Set a 32bits mask of nbits '1' starting at LSb */
  static inline sos_ui32_t generate_lsb_mask(int nbits)
  {
    return (1 << nbits) - 1;
  }
  
  
  sos_size_t sos_bitmap_ffs(const void * area,
                            sos_size_t bit_length,
                            sos_size_t _start_bit_index)
  {
    sos_size_t start_bit_index = _start_bit_index - 1;
    sos_size_t word_index      = start_bit_index >> 5;
    const sos_ui32_t * word    = (const sos_ui32_t*)area;
    sos_ui32_t mask = ~ generate_lsb_mask(start_bit_index & 31);
  
    while (start_bit_index < bit_length)
      {
        sos_size_t fsb = word_ffs(word[word_index] & mask);
  
        if (fsb > 0)
          {
            sos_size_t bit_index = SOS_ALIGN_INF(start_bit_index, 32) + fsb;
            if (bit_index > bit_length)
              return 0;
            
            return bit_index;
          }
  
        start_bit_index = SOS_ALIGN_INF(start_bit_index, 32) + 32;
        word_index ++;
        mask = ~0;
      }
  
    return 0;
  }
  
  
  sos_size_t sos_bitmap_ffc(const void * area,
                            sos_size_t bit_length,
                            sos_size_t _start_bit_index)
  {
    sos_size_t start_bit_index = _start_bit_index - 1;
    sos_size_t word_index      = start_bit_index >> 5;
    const sos_ui32_t * word    = (const sos_ui32_t*)area;
    sos_ui32_t mask = ~ generate_lsb_mask(start_bit_index & 31);
  
    while (start_bit_index < bit_length)
      {
        sos_size_t fcb = word_ffs((~ word[word_index]) & mask);
  
        if (fcb > 0)
          {
            sos_size_t bit_index = SOS_ALIGN_INF(start_bit_index, 32) + fcb;
            if (bit_index > bit_length)
              return 0;
            
            return bit_index;
          }
  
        start_bit_index = SOS_ALIGN_INF(start_bit_index, 32) + 32;
        word_index ++;
        mask = ~0;
      }
  
    return 0;
  }
  
  
  #undef ADDR
  #define ADDR (*(volatile long *) addr)
  
  /**
   * test_and_set_bit - Set a bit and return its old value
   * @param nr Bit to set (starting at 0 !)
   * @param addr Address to count from
   *
   * @note: License is GPLv2, origin is Linux Kernel 2.6.14.3
   * (@see include/asm-i386/bitops.h)
   */
  static inline int test_and_set_bit(int nr, volatile unsigned long * addr)
  {
          int oldbit;
  
          __asm__ __volatile__(
                  "btsl %2,%1\n\tsbbl %0,%0"
                  :"=r" (oldbit),"=m" (ADDR)
                  :"Ir" (nr) : "memory");
          return oldbit;
  }
  
  
  /**
   * test_and_clear_bit - Clear a bit and return its old value
   * @param nr Bit to clear (starting at 0 !)
   * @param addr Address to count from
   *
   * @note: License is GPLv2, origin is Linux Kernel 2.6.14.3
   * (@see include/asm-i386/bitops.h)
   */
  static inline int test_and_clear_bit(int nr, volatile unsigned long * addr)
  {
          int oldbit;
  
          __asm__ __volatile__(
                  "btrl %2,%1\n\tsbbl %0,%0"
                  :"=r" (oldbit),"=m" (ADDR)
                  :"Ir" (nr) : "memory");
          return oldbit;
  }
  
  
  sos_bool_t sos_bitmap_test_and_set(void * area,
                                     sos_size_t _bit_index)
  {
    sos_ui32_t * word     = (sos_ui32_t*)area;
    sos_size_t bit_index  = _bit_index - 1;
    sos_size_t word_index = bit_index >> 5;
  
    bit_index &= 31;
    return test_and_set_bit(bit_index, word + word_index);
  }
  
  
  sos_bool_t sos_bitmap_test_and_clear(void * area,
                                       sos_size_t _bit_index)
  {
    sos_ui32_t * word     = (sos_ui32_t*)area;
    sos_size_t bit_index  = _bit_index - 1;
    sos_size_t word_index = bit_index >> 5;
  
    bit_index &= 31;
    return test_and_clear_bit(bit_index, word + word_index);
  }
  
 

sos-code-article9.5/hwcore/bitmap.h (1969-12-31 16:00:00.000000000 -0800 )
sos-code-article10/hwcore/bitmap.h (2006-12-20 13:59:58.000000000 -0800 )
(New file) 
Line 1 
  /* Copyright (C) 2006 David Decotigny
  
     This program is free software; you can redistribute it and/or
     modify it under the terms of the GNU General Public License
     as published by the Free Software Foundation; either version 2
     of the License, or (at your option) any later version.
     
     This program is distributed in the hope that it will be useful,
     but WITHOUT ANY WARRANTY; without even the implied warranty of
     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     GNU General Public License for more details.
     
     You should have received a copy of the GNU General Public License
     along with this program; if not, write to the Free Software
     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
     USA. 
  */
  #ifndef _SOS_BITMAP_H_
  #define _SOS_BITMAP_H_
  
  
  /**
   * @file bitmap.h
   *
   * Bitmap manipulation. The implementation of this API is
   * architecture-dependent, hence its location in hwcore/
   */
  
  #include <sos/types.h>
  #include <sos/errno.h>
  
  
  /**
   * Return index (start at start_bit_index) of first bit set
   * @return 0 < x <= bit_length when found, 0 when none found
   * @note in-memory size of area MUST be a multiple of 4B (32bits) !
   *
   * @note not atomic
   */
  sos_size_t sos_bitmap_ffs(const void * area,
                            sos_size_t bit_length,
                            sos_size_t start_bit_index /* start at 1 */);
  
  
  /**
   * Return index (start at start_bit_index) of first bit clear
   * @return 0 < x <= bit_length when found, 0 when none found
   * @note in-memory size of area MUST be a multiple of 4B (32bits) !
   *
   * @note not atomic
   */
  sos_size_t sos_bitmap_ffc(const void * area,
                            sos_size_t bit_length,
                            sos_size_t start_bit_index /* start at 1 */);
  
  
  /**
   * Set bit at index (start at 1)
   * @return old value of the bit
   *
   * @note atomic
   */
  sos_bool_t
  sos_bitmap_test_and_set(void * area,
                          sos_size_t bit_index /* start at 1 */);
  
  
  /**
   * Clear bit at index (start at 1)
   * @return old value of the bit
   *
   * @note atomic
   */
  sos_bool_t
  sos_bitmap_test_and_clear(void * area,
                            sos_size_t bit_index /* start at 1 */);
  
  
  #endif /* _SOS_BITMAP_H_ */
  
 

sos-code-article9.5/hwcore/paging.c (2006-01-22 03:48:30.000000000 -0800 )
sos-code-article10/hwcore/paging.c (2006-12-20 13:59:58.000000000 -0800 )
Line 954 
Line 954 
 { {
   SOS_ASSERT_FATAL(SOS_IS_PAGE_ALIGNED(base_address));   SOS_ASSERT_FATAL(SOS_IS_PAGE_ALIGNED(base_address));
   SOS_ASSERT_FATAL(SOS_IS_PAGE_ALIGNED(length));   SOS_ASSERT_FATAL(SOS_IS_PAGE_ALIGNED(length));
   SOS_ASSERT_FATAL(SOS_PAGING_BASE_USER_ADDRESS <= base_address);   SOS_ASSERT_FATAL(SOS_PAGING_IS_USER_AREA(base_address, length));
   /* Mark all the pages read-only, when already mapped in physical   /* Mark all the pages read-only, when already mapped in physical
      memory */      memory */
  
 

sos-code-article9.5/hwcore/paging.h (2006-01-22 03:48:30.000000000 -0800 )
sos-code-article10/hwcore/paging.h (2006-12-20 13:59:58.000000000 -0800 )
Line 40 
Line 40 
  
 /** /**
  * Basic SOS virtual memory organization  * Basic SOS virtual memory organization
   *
   * - Kernel space starts at the lower 4kB address (first page is not
   *   mapped in order to catch invalid pointers)
   * - User space starts at the 1GB address
  */  */
 /** Frontier between kernel and user space virtual addresses */ 
 #define SOS_PAGING_TOP_USER_ADDRESS  (0xFFFFFFFF) /* 4GB - 1B */ #define SOS_PAGING_UPPER_USER_ADDRESS  (0xFFFFFFFF) /* 4GB - 1B */
 #define SOS_PAGING_USER_SPACE_SIZE   (0xc0000000) /* 3GB */ #define SOS_PAGING_USER_SPACE_SIZE     (SOS_PAGING_UPPER_USER_ADDRESS - \
                                          SOS_PAGING_BASE_USER_ADDRESS + 1) /* 3GB */
  #define SOS_PAGING_IS_USER_AREA(base_addr, length) \
    ( (SOS_PAGING_BASE_USER_ADDRESS <= (base_addr))  \
        && ((length) <= SOS_PAGING_USER_SPACE_SIZE) )
 /** Length of the space reserved for the mirroring in the kernel /** Length of the space reserved for the mirroring in the kernel
     virtual space */     virtual space */
Line 54 
Line 61 
 #define SOS_PAGING_MIRROR_VADDR \ #define SOS_PAGING_MIRROR_VADDR \
    (SOS_PAGING_BASE_USER_ADDRESS - SOS_PAGING_MIRROR_SIZE)    (SOS_PAGING_BASE_USER_ADDRESS - SOS_PAGING_MIRROR_SIZE)
  
  #define SOS_PAGING_BASE_KERNEL_ADDRESS  (0x00004000) /* 16kB */
  #define SOS_PAGING_UPPER_KERNEL_ADDRESS (SOS_PAGING_MIRROR_VADDR - 1) /* 1GB - 4MB - 1B */
  #define SOS_PAGING_KERNEL_SPACE_SIZE    (SOS_PAGING_UPPER_KERNEL_ADDRESS - \
                                           SOS_PAGING_BASE_KERNEL_ADDRESS + 1) /* 1GB - 4MB - 16kB */
  #define SOS_PAGING_IS_KERNEL_AREA(base_addr, length) \
    ( (SOS_PAGING_BASE_KERNEL_ADDRESS <= (base_addr))  \
        && ((length) <= SOS_PAGING_KERNEL_SPACE_SIZE) )
  
  
 /** /**
  * sos_paging_map flags  * sos_paging_map flags
  
 

sos-code-article9.5/Makefile (2006-01-22 03:48:32.000000000 -0800 )
sos-code-article10/Makefile (2007-01-16 11:36:45.000000000 -0800 )
Line 19 
Line 19 
 LD=ld LD=ld
 CP=cp CP=cp
 STRIP=strip STRIP=strip
 CFLAGS  = -Wall -nostdinc -ffreestanding -DKERNEL_SOS -O -g CFLAGS  = -Wall -nostdinc -ffreestanding -DKERNEL_SOS -g
 LDFLAGS = --warn-common -nostdlib LDFLAGS = --warn-common -nostdlib
 OBJECTS = bootstrap/multiboot.o                                         \ OBJECTS = bootstrap/multiboot.o                                         \
Line 29 
Line 29 
           hwcore/irq.o hwcore/irq_wrappers.o hwcore/i8259.o                \           hwcore/irq.o hwcore/irq_wrappers.o hwcore/i8259.o                \
           hwcore/paging.o hwcore/i8254.o                                \           hwcore/paging.o hwcore/i8254.o                                \
           hwcore/cpu_context.o hwcore/cpu_context_switch.o                \           hwcore/cpu_context.o hwcore/cpu_context_switch.o                \
           hwcore/mm_context.o                                                \           hwcore/mm_context.o hwcore/bitmap.o                        \
           sos/physmem.o sos/klibc.o                                        \           sos/physmem.o sos/klibc.o                                        \
           sos/thread.o sos/kwaitq.o                                        \           sos/thread.o sos/kwaitq.o                                        \
Line 44 
Line 44 
           drivers/serial.o drivers/tty.o drivers/part.o        \           drivers/serial.o drivers/tty.o drivers/part.o        \
           drivers/console.o                                                \           drivers/console.o                                                \
           sos/hash.o sos/fs.o sos/fs_nscache.o                                \           sos/hash.o sos/fs.o sos/fs_nscache.o                                \
           drivers/fs_virtfs.o sos/chardev.o sos/blkdev.o                \           drivers/fs_virtfs.o drivers/fs_fat.o                         \
            sos/chardev.o sos/blkdev.o                                \
           userland/userprogs.kimg           userland/userprogs.kimg
  
  
 

sos-code-article9.5/sos/binfmt_elf32.c (2006-01-22 03:48:30.000000000 -0800 )
sos-code-article10/sos/binfmt_elf32.c (2006-12-20 13:59:58.000000000 -0800 )
Line 386 
Line 386 
           continue;           continue;
         }         }
              
       if (elf_phdrs[i].p_vaddr < SOS_PAGING_BASE_USER_ADDRESS)       if (! SOS_PAGING_IS_USER_AREA(elf_phdrs[i].p_vaddr,
                                      elf_phdrs[i].p_memsz) )
           sos_display_fatal_error("User program has an incorrect address");           sos_display_fatal_error("User program has an incorrect address");
         }         }
  
 

sos-code-article9.5/sos/blkdev.c (2006-01-22 03:48:31.000000000 -0800 )
sos-code-article10/sos/blkdev.c (2007-01-14 02:05:45.000000000 -0800 )
Line 163 
Line 163 
 static sos_ret_t static sos_ret_t
 blockdev_use_instance(struct sos_blockdev_instance * blockdev) blockdev_use_instance(struct sos_blockdev_instance * blockdev)
 { {
    SOS_ASSERT_FATAL(blockdev != NULL);
   SOS_ASSERT_FATAL(blockdev->ref_cnt > 0);   SOS_ASSERT_FATAL(blockdev->ref_cnt > 0);
   blockdev->ref_cnt ++;   blockdev->ref_cnt ++;
   return SOS_OK;   return SOS_OK;
  
 

sos-code-article9.5/sos/errno.h (2006-01-22 03:48:30.000000000 -0800 )
sos-code-article10/sos/errno.h (2006-12-20 13:59:58.000000000 -0800 )
Line 31 
Line 31 
 #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_EAGAIN        6   /* Temporary resource exhaustion */
 #define SOS_EFAULT        7   /* Unresolved virtual memory fault */ #define SOS_EPERM         7   /* Mutex/files ownership error */
 #define SOS_ENOENT        8   /* No such file or directory */ #define SOS_EFAULT        8   /* Unresolved virtual memory fault */
 #define SOS_ELOOP         9   /* symlink resolution loop / too recursive */ #define SOS_ENOENT        9   /* No such file or directory */
 #define SOS_EEXIST       10   /* File already exists */ #define SOS_ELOOP        10   /* symlink resolution loop / too recursive */
 #define SOS_EACCES       11   /* Permission denied */ #define SOS_EEXIST       11   /* File already exists */
 #define SOS_ENOTDIR      12   /* Dir does not exist */ #define SOS_EACCES       12   /* Permission denied */
 #define SOS_ENAMETOOLONG 13 #define SOS_ENOTDIR      13   /* Dir does not exist */
 #define SOS_EXDEV        14   /* Cannot link entries across different FS */ #define SOS_ENAMETOOLONG 14
 #define SOS_EISDIR       15   /* Directories not allowed in operation */ #define SOS_EXDEV        15   /* Cannot link entries across different FS */
 #define SOS_ENOTEMPTY    16 #define SOS_EISDIR       16   /* Directories not allowed in operation */
 #define SOS_ENODEV       17   /* No such device */ #define SOS_ENOTEMPTY    17
 #define SOS_EBADF        18   /* Bad file descriptor */ #define SOS_ENODEV       18   /* No such device */
 #define SOS_EMFILE       19   /* Reached maximal opened file for process */ #define SOS_EBADF        19   /* Bad file descriptor */
 #define SOS_ENOSYS       20   /* Operation not implemented */ #define SOS_EMFILE       20   /* Reached maximal opened file for process */
 #define SOS_EIO          21   /* Input/output error */ #define SOS_ENOSYS       21   /* Operation not implemented */
  #define SOS_EIO          22   /* Input/output error */
  
 /* A negative value means that an error occured.  For /* A negative value means that an error occured.  For
  
 

sos-code-article9.5/sos/fs.c (2006-01-22 03:48:30.000000000 -0800 )
sos-code-article10/sos/fs.c (2007-04-15 13:02:42.000000000 -0700 )
Line 22 
Line 22 
 #include <sos/list.h> #include <sos/list.h>
 #include <sos/kmem_slab.h> #include <sos/kmem_slab.h>
 #include <sos/kmalloc.h> #include <sos/kmalloc.h>
  #include <sos/syscall.h> /* For the FCNTL commands */
 #include "chardev.h" #include "chardev.h"
  
 #include "fs.h" #include "fs.h"
Line 648 
Line 649 
   struct sos_fs_pathname first_component, remaining;   struct sos_fs_pathname first_component, remaining;
   sos_bool_t slashes_after_first_component = FALSE;   sos_bool_t slashes_after_first_component = FALSE;
  
    *result_nsnode = NULL;
  
   parent_fsnode = sos_fs_nscache_get_fs_node(parent_nsnode);   parent_fsnode = sos_fs_nscache_get_fs_node(parent_nsnode);
   if (parent_fsnode->type != SOS_FS_NODE_DIRECTORY)   if (parent_fsnode->type != SOS_FS_NODE_DIRECTORY)
     return -SOS_ENOTDIR;     return -SOS_ENOTDIR;
Line 1221 
Line 1224 
                        sos_ui32_t req_arg /* Usually: sos_uaddr_t */)                        sos_ui32_t req_arg /* Usually: sos_uaddr_t */)
 { {
   if (! of->ops_file->fcntl)   if (! of->ops_file->fcntl)
     return -SOS_ENOSYS;     return sos_fs_basic_fcntl_helper(of, req_id, req_arg);
   return of->ops_file->fcntl(of, req_id, req_arg);   return of->ops_file->fcntl(of, req_id, req_arg);
 } }
Line 1950 
Line 1953 
 } }
  
  
  /* *************************************************************
   * client FS helper functions
   */
  sos_ret_t sos_fs_basic_fcntl_helper(struct sos_fs_opened_file * of,
                                      int req_id, sos_ui32_t req_arg)
  {
    sos_ret_t result = -SOS_ENOSUP;
  
    switch(req_id)
      {
      case SOS_FCNTL_DUPFD:
        {
          result = sos_fs_ref_opened_file(of);
          if (SOS_OK != result)
            break;
  
          result = sos_process_register_opened_file((struct sos_process*)of->owner,
                                                    of, req_arg);
        }
        break;
  
      case SOS_FCNTL_GETFD:
        {
          result = of->open_flags & SOS_FS_OPEN_CLOSEONEXEC;
        }
        break;
  
      case SOS_FCNTL_SETFD:
        {
          of->open_flags &= ~SOS_FS_OPEN_CLOSEONEXEC;
          of->open_flags |= (req_arg & SOS_FS_OPEN_CLOSEONEXEC);
          result = SOS_OK;
        }
        break;
  
      case SOS_FCNTL_GETFL:
        {
          result = of->open_flags;
        }
        break;
  
      case SOS_FCNTL_SETFL:
        {
          /* Not supported */
        }
        break;
  
      default:
        break;
      }
  
    return result;
  }
  
  
 /* ************************************************************* /* *************************************************************
  * mount/umount stuff  * mount/umount stuff
  
 

sos-code-article9.5/sos/fs.h (2006-01-22 03:48:30.000000000 -0800 )
sos-code-article10/sos/fs.h (2007-04-08 14:34:26.000000000 -0700 )
Line 475 
Line 475 
    * Called when a process opens the node    * Called when a process opens the node
    *    *
    * @note Mandatory, may block. Appropriate locking MUST be implemented    * @note Mandatory, may block. Appropriate locking MUST be implemented
    * @note FS-specific EXCPET for device special files (char &    * @note FS-specific EXCEPT for device special files (char &
    * chardev/blockdev subsystems    * chardev/blockdev subsystems
    * @note As a consequence, FS code can safely assume that "this" is    * @note As a consequence, FS code can safely assume that "this" is
Line 487 
Line 487 
                                struct sos_fs_opened_file ** result_of);                                struct sos_fs_opened_file ** result_of);
  
   /**   /**
    * Called when a process opens the node    * Called when a process closes the node
    * @note Mandatory, may block. Appropriate locking MUST be implemented    * @note Mandatory, may block. Appropriate locking MUST be implemented
    * @note FS-specific EXCEPT for device special files (char &    * @note FS-specific EXCEPT for device special files (char &
Line 1123 
Line 1123 
                                        const struct sos_process * dst_proc,                                        const struct sos_process * dst_proc,
                                        struct sos_fs_opened_file ** result_of);                                        struct sos_fs_opened_file ** result_of);
  
  /**
   * Generic fcntl function that can be called from inside the FS's
   * fcntl method. It will handle the following fcntl commands:
   *    - F_DUPFD
   *    - F_GETFD
   *    - F_SETFD
   *    - F_GETFL
   *    - F_SETFL
   * Any other command will lead to a -SOS_ENOSUP return value.
   * It will take care not to change anything besides the fs.h structures.
   */
  sos_ret_t sos_fs_basic_fcntl_helper(struct sos_fs_opened_file * src_of,
                                      int req_id, sos_ui32_t req_arg);
 #endif /* _SOS_FS_H_ */ #endif /* _SOS_FS_H_ */
  
 

sos-code-article9.5/sos/hash.c (2006-01-22 03:48:31.000000000 -0800 )
sos-code-article10/sos/hash.c (2006-12-20 13:59:58.000000000 -0800 )
Line 67 
Line 67 
   /** Number of buckets in this hash table */   /** Number of buckets in this hash table */
   sos_count_t              nbuckets;   sos_count_t              nbuckets;
      
    /** Number of elements currently in the hash */
    sos_count_t              nb_elems;
    
   struct bucket bucket[0];   struct bucket bucket[0];
 }; };
  
Line 118 
Line 121 
 } }
  
  
  sos_count_t sos_hash_get_nb_elts(const struct sos_hash_table * h)
  {
    return h->nb_elems;
  }
  
  
 sos_ret_t sos_hash_dispose(struct sos_hash_table *h) sos_ret_t sos_hash_dispose(struct sos_hash_table *h)
 { {
   unsigned int i;   unsigned int i;
Line 156 
Line 165 
  
   list_add_head_named(h->bucket[bucket].list, h_elt, h_prev, h_next);   list_add_head_named(h->bucket[bucket].list, h_elt, h_prev, h_next);
   h->bucket[bucket].nb_elems ++;   h->bucket[bucket].nb_elems ++;
    h->nb_elems ++;
  
   return SOS_OK;   return SOS_OK;
 } }
Line 215 
Line 225 
  
   list_delete_named(h->bucket[bucket].list, h_elt, h_prev, h_next);   list_delete_named(h->bucket[bucket].list, h_elt, h_prev, h_next);
   h->bucket[bucket].nb_elems --;   h->bucket[bucket].nb_elems --;
    h->nb_elems --;
  
   return SOS_OK;   return SOS_OK;
 } }
  
  
  sos_bool_t sos_hash_walk(const struct sos_hash_table *h,
                           sos_hash_map_func_t * iter_func,
                           void * custom_data)
  {
    unsigned int i;
    for (i = 0 ; i < h->nbuckets ; i++)
      {
        sos_count_t nelts;
        struct sos_hash_linkage * elt;
  
        list_foreach_forward_named(h->bucket[i].list, elt,
                                   nelts, h_prev, h_next)
          {
            if (! iter_func(custom_data,
                            elt_for_h_linkage(h, elt)))
              return FALSE;
          }
      }
  
    return TRUE;
  }
  
  
 unsigned long sos_hash_ui64(const void * ptr_key) unsigned long sos_hash_ui64(const void * ptr_key)
 { {
   const sos_ui64_t * keyval = ptr_key;   const sos_ui64_t * keyval = ptr_key;
  
 

sos-code-article9.5/sos/hash.h (2006-01-22 03:48:31.000000000 -0800 )
sos-code-article10/sos/hash.h (2006-12-20 13:59:58.000000000 -0800 )
Line 41 
Line 41 
 typedef sos_bool_t (sos_hash_key_eq_func_t)(const void * ptr_key1, typedef sos_bool_t (sos_hash_key_eq_func_t)(const void * ptr_key1,
                                             const void * ptr_key2);                                             const void * ptr_key2);
  
  /** Prototype of a key/value iteration function: when FALSE = stop now ! */
  typedef sos_bool_t (sos_hash_map_func_t)(void * custom_data,
                                           void * elt_with_key);
  
  
 /** Opaque structure */ /** Opaque structure */
 struct sos_hash_table; struct sos_hash_table;
  
Line 57 
Line 62 
  *  *
  * @param name The name of the hash table (debug)  * @param name The name of the hash table (debug)
  * @param elt_type The type of the elements  * @param elt_type The type of the elements
  * @param hfunc The hash function (@see sos_hash_func_t)  * @param hfunc The hash function (@see sos_hash_func_t). When NULL:
  * @param hfunc The element comparaison function (@see  *               identity (native unsigned long keys assumed)
  *              sos_hash_key_eq_func_t)  * @param eqfunc The element comparaison function (@see
   *               sos_hash_key_eq_func_t). When NULL: native
   *               unsigned long comparison
  * @param name_key_field The name of the field in the element type  * @param name_key_field The name of the field in the element type
  *                       that holds the key  *                       that holds the key
Line 90 
Line 97 
                       sos_uoffset_t          offset_h_linkage);                       sos_uoffset_t          offset_h_linkage);
  
  
  /** Return the number of elements in the hash table */
  sos_count_t sos_hash_get_nb_elts(const struct sos_hash_table * h);
  
  
 /** Does not free the elements themselves ! */ /** Does not free the elements themselves ! */
 sos_ret_t sos_hash_dispose(struct sos_hash_table *h); sos_ret_t sos_hash_dispose(struct sos_hash_table *h);
  
Line 116 
Line 127 
                           void *elt);                           void *elt);
  
  
  /**
   * Call iter_func on each of the elements in the hash table
   * Stop when iter_func returns 0 (and returns FALSE). Otherwise return
   * TRUE.
   *
   * @note iter_func must NOT block !
   * @note No particular locking
   */
  sos_bool_t sos_hash_walk(const struct sos_hash_table *h,
                           sos_hash_map_func_t * iter_func,
                           void * custom_data);
  
  
 /* /*
  * Common hash functions  * Common hash functions
  */  */
  
 

sos-code-article9.5/sos/klibc.h (2006-01-22 03:48:31.000000000 -0800 )
sos-code-article10/sos/klibc.h (2007-01-20 08:37:52.000000000 -0800 )
Line 38 
Line 38 
 void *memset(void *dst, register int c, register unsigned int length ) ; void *memset(void *dst, register int c, register unsigned int length ) ;
 int memcmp(const void *s1, const void *s2, sos_size_t n); int memcmp(const void *s1, const void *s2, sos_size_t n);
  
  #define islower(c)  (('a' <= (c)) && ((c) <= 'z'))
  #define isupper(c)  (('A' <= (c)) && ((c) <= 'Z'))
  #define isdigit(c)  (('0' <= (c)) && ((c) <= '9'))
  #define isspace(c)  (((c) == ' ') || ((c) == '\t') || \
                      ((c) == '\f') || ((c) == '\n') || \
                      ((c) == '\r') || ((c) == '\v'))
  #define isprint(c)  ((' ' <= (c)) && ((c) <= '~'))
  
 unsigned int strlen( register const char *str) ; unsigned int strlen( register const char *str) ;
 unsigned int strnlen(const char * s, sos_size_t maxlen); unsigned int strnlen(const char * s, sos_size_t maxlen);
  
  
 

sos-code-article9.5/sos/kmem_vmm.h (2006-01-22 03:48:31.000000000 -0800 )
sos-code-article10/sos/kmem_vmm.h (2006-12-20 13:59:58.000000000 -0800 )
Line 30 
Line 30 
 #include <hwcore/paging.h> #include <hwcore/paging.h>
  
 /* The base and top virtual addresses covered by the kernel allocator */ /* The base and top virtual addresses covered by the kernel allocator */
 #define SOS_KMEM_VMM_BASE 0x4000 /* 16kB */ #define SOS_KMEM_VMM_BASE SOS_PAGING_BASE_KERNEL_ADDRESS      /* 16kB */
 #define SOS_KMEM_VMM_TOP  SOS_PAGING_MIRROR_VADDR /* 1GB - 4MB */ #define SOS_KMEM_VMM_TOP  (SOS_PAGING_UPPER_KERNEL_ADDRESS+1) /* 1GB - 4MB */
 /** Opaque structure used internally and declared here for physmem.h */ /** Opaque structure used internally and declared here for physmem.h */
 struct sos_kmem_range; struct sos_kmem_range;
  
 

sos-code-article9.5/sos/main.c (2006-01-22 03:48:31.000000000 -0800 )
sos-code-article10/sos/main.c (2007-07-18 09:50:44.000000000 -0700 )
Line 46 
Line 46 
 #include <drivers/zero.h> #include <drivers/zero.h>
 #include <sos/fs.h> #include <sos/fs.h>
 #include <drivers/fs_virtfs.h> #include <drivers/fs_virtfs.h>
  #include <drivers/fs_fat.h>
 #include <drivers/devices.h> #include <drivers/devices.h>
 #include <drivers/mem.h> #include <drivers/mem.h>
 #include <drivers/tty.h> #include <drivers/tty.h>
Line 110 
Line 111 
       || (cur_thr->fixup_uaccess.return_vaddr))       || (cur_thr->fixup_uaccess.return_vaddr))
     {     {
       __label__ unforce_address_space;       __label__ unforce_address_space;
       sos_bool_t need_to_setup_mmu;       sos_bool_t need_to_prepare_user_space_access;
  
       /* Make sure to always stay in the interrupted thread's MMU       /* Do we already try to access an address space ? */
          configuration */       need_to_prepare_user_space_access
       need_to_setup_mmu = (cur_thr->squatted_mm_context         = (sos_thread_get_current()->squatted_address_space == NULL);
                            != sos_process_get_mm_context(cur_thr->process)); 
       if (need_to_setup_mmu)       if (need_to_prepare_user_space_access)
          /* No: Need to configure the interrupted thread user space */
  
       if (SOS_OK ==       if (SOS_OK ==
Line 136 
Line 138 
           goto unforce_address_space;           goto unforce_address_space;
         }         }
  
       if (need_to_setup_mmu)       if (need_to_prepare_user_space_access)
  
       sos_bochs_printf("\e[35mTHR %p: Unresolved USER page Fault at instruction 0x%x on access to address 0x%x (info=%x)!\e[m\n",       sos_bochs_printf("\e[35mTHR %p: Unresolved USER page Fault at instruction 0x%x on access to address 0x%x (info=%x)!\e[m\n",
Line 148 
Line 150 
       sos_thread_exit();       sos_thread_exit();
  
     unforce_address_space:     unforce_address_space:
       if (need_to_setup_mmu)       if (need_to_prepare_user_space_access)
       return;       return;
     }     }
Line 292 
Line 294 
   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;
    int fake_args[32]; /* Must be large enough to contain argv/envp for init */
    sos_ret_t ret;
   struct sos_fs_opened_file * init_root, * init_cwd, * unused_of;   struct sos_fs_opened_file * init_root, * init_cwd, * unused_of;
  
   /* Create the new process */   /* Create the new process */
Line 339 
Line 343 
     }     }
  
  
  
   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 349 
Line 352 
     }     }
  
   /* Allocate the user stack */   /* Allocate the user stack */
   ustack = (SOS_PAGING_TOP_USER_ADDRESS - SOS_DEFAULT_USER_STACK_SIZE) + 1;   ustack = (SOS_PAGING_UPPER_USER_ADDRESS - SOS_DEFAULT_USER_STACK_SIZE) + 1;
                             SOS_VM_MAP_PROT_READ | SOS_VM_MAP_PROT_WRITE,                             SOS_VM_MAP_PROT_READ | SOS_VM_MAP_PROT_WRITE,
                             /* PRIVATE */ 0);                             /* PRIVATE */ 0);
Line 359 
Line 362 
       return -SOS_ENOMEM;       return -SOS_ENOMEM;
     }     }
  
    /* Compute the address of the stack that will be used to initialize
       the user thread */
    ustack = SOS_ALIGN_INF((ustack + SOS_DEFAULT_USER_STACK_SIZE
                            - sizeof(fake_args)), 4);
  
    /* Build fake argv/envp arguments for the init process. See
       userland/crt.c for the format. */
    fake_args[0] = 0x1;                      /* argc */
    fake_args[1] = 4 * sizeof(fake_args[0]); /* offset of argv[0] */
    fake_args[2] = 0x0;                      /* delimiter between argv and envp */
    fake_args[3] = 0x0;                      /* end of envp */
    strzcpy ((char *) & fake_args[4],        /* argv[0] contents */
             "init", 5);
  
    /* Copy the arguments to the user thread stack */
    ret = sos_memcpy_to_specified_userspace (as_init,
                                             ustack,
                                             (sos_vaddr_t) fake_args,
                                             sizeof(fake_args));
    if (sizeof(fake_args) != ret)
      {
        sos_bochs_printf ("sos_memcpy_to_specified_userspace() failed, returned %d\n", ret);
        sos_process_unref(proc_init);
        return ret;
      }
  
   /* Now create the user thread */   /* Now create the user thread */
   new_thr = sos_create_user_thread(NULL,   new_thr = sos_create_user_thread(NULL,
                                    proc_init,                                    proc_init,
                                    start_uaddr,                                    start_uaddr,
                                    0, 0,                                    0, 0,
                                    ustack + SOS_DEFAULT_USER_STACK_SIZE - 4,                                    ustack,
   if (! new_thr)   if (! new_thr)
     {     {
Line 414 
Line 443 
       sos_x86_videomem_printf(1, 0,       sos_x86_videomem_printf(1, 0,
                               SOS_X86_VIDEO_FG_YELLOW | SOS_X86_VIDEO_BG_BLUE,                               SOS_X86_VIDEO_FG_YELLOW | SOS_X86_VIDEO_BG_BLUE,
                               "Welcome From GRUB to %s%c RAM is %dMB (upper mem = 0x%x kB)",                               "Welcome From GRUB to %s%c RAM is %dMB (upper mem = 0x%x kB)",
                               "SOS article 9.5", ',',                               "SOS article 10", ',',
                               (unsigned)upper_mem);                               (unsigned)upper_mem);
     }     }
Line 425 
Line 454 
       sos_x86_videomem_printf(1, 0,       sos_x86_videomem_printf(1, 0,
                               SOS_X86_VIDEO_FG_YELLOW | SOS_X86_VIDEO_BG_BLUE,                               SOS_X86_VIDEO_FG_YELLOW | SOS_X86_VIDEO_BG_BLUE,
                               "Welcome to %s%c RAM is %dMB (upper mem = 0x%x kB)",                               "Welcome to %s%c RAM is %dMB (upper mem = 0x%x kB)",
                               "SOS article 9.5", ',',                               "SOS article 10", ',',
                               (unsigned)upper_mem);                               (unsigned)upper_mem);
     }     }
Line 433 
Line 462 
     /* Not loaded with grub, not from an enhanced bootsect */     /* Not loaded with grub, not from an enhanced bootsect */
     sos_x86_videomem_printf(1, 0,     sos_x86_videomem_printf(1, 0,
                             SOS_X86_VIDEO_FG_YELLOW | SOS_X86_VIDEO_BG_BLUE,                             SOS_X86_VIDEO_FG_YELLOW | SOS_X86_VIDEO_BG_BLUE,
                             "Welcome to SOS article 9.5");                             "Welcome to SOS article 10");
   sos_bochs_putstring("Message in a bochs: This is SOS article 9.5.\n");   sos_bochs_putstring("Message in a bochs: This is SOS article 10.\n");
   /* Setup CPU segmentation and IRQ subsystem */   /* Setup CPU segmentation and IRQ subsystem */
   sos_gdt_subsystem_setup();   sos_gdt_subsystem_setup();
Line 574 
Line 603 
  
  
   SOS_ASSERT_FATAL(SOS_OK == sos_fs_virtfs_subsystem_setup());   SOS_ASSERT_FATAL(SOS_OK == sos_fs_virtfs_subsystem_setup());
    SOS_ASSERT_FATAL(SOS_OK == sos_fs_fat_subsystem_setup());
   SOS_ASSERT_FATAL(SOS_OK == sos_fs_subsystem_setup(NULL,   SOS_ASSERT_FATAL(SOS_OK == sos_fs_subsystem_setup(NULL,
                                                     "virtfs",                                                     "virtfs",
                                                     NULL,                                                     NULL,
  
 

sos-code-article9.5/sos/process.c (2006-01-22 03:48:31.000000000 -0800 )
sos-code-article10/sos/process.c (2007-03-12 12:39:44.000000000 -0700 )
Line 21 
Line 21 
 #include <sos/kmem_slab.h> #include <sos/kmem_slab.h>
 #include <hwcore/irq.h> #include <hwcore/irq.h>
 #include <drivers/bochs.h> #include <drivers/bochs.h>
  #include <hwcore/bitmap.h>
  #include <sos/physmem.h> /* for SOS_PAGE_SIZE */
  
 #include <sos/umem_vmm.h> #include <sos/umem_vmm.h>
  
Line 40 
Line 42 
 { {
   char name[SOS_PROCESS_MAX_NAMELEN];   char name[SOS_PROCESS_MAX_NAMELEN];
  
    /** Process identifier */
    sos_pid_t pid;
  
   /** First important resource: the address space */   /** First important resource: the address space */
   struct sos_umem_vmm_as *address_space;   struct sos_umem_vmm_as *address_space;
  
Line 60 
Line 65 
   /** Where the current working dir of the process is */   /** Where the current working dir of the process is */
   struct sos_fs_opened_file * cwd;   struct sos_fs_opened_file * cwd;
  
   struct sos_process     *prev, *next;   /** To chain the processes in the global list of active processes
        and PIDs */
    struct sos_hash_linkage hlink_pidtable;
  
  
 /** The list of processes in the system */ /** The list of processes in the system */
 static struct sos_process *process_list = NULL; static struct sos_hash_table * process_hash;
  
  
  /** The bitmap for PID allocation */
  static void * bitmap_pid;
  
  
  /** Number of bits in the PID bitmap */
  #define SOS_PROCESS_PID_BITMAP_NBITS \
    (8 * SOS_PROCESS_PID_BITMAP_NPAGES * SOS_PAGE_SIZE)
  
  
  /** The PID where we start looking for a free slot */
  static sos_pid_t next_base_pid;
  
 /** The cache for the sos_process structures */ /** The cache for the sos_process structures */
Line 78 
Line 98 
 void sos_process_dumplist(void) void sos_process_dumplist(void)
 { {
   struct sos_thread * cur_thr = sos_thread_get_current();   struct sos_thread * cur_thr = sos_thread_get_current();
   struct sos_process * proc;   int nb_procs = 0;
   int nb_procs; 
   sos_bochs_printf("<ps>\n");   /* Internal function used to dump infos about a single process */
   list_foreach(process_list, proc, nb_procs)   sos_bool_t process_iterator(void* custom_data,
                                struct sos_process * proc)
       struct sos_thread * thr;       struct sos_thread * thr;
       int    nb_thrs;       int    nb_thrs;
  
       sos_bochs_printf("  proc@%p: '%s', %d threads, %d refs\n",       nb_procs ++;
                        proc, proc->name?proc->name:"(none)",       sos_bochs_printf("  proc %d @%p: '%s', %d threads, %d refs\n",
                         proc->pid, proc, proc->name?proc->name:"(none)",
  
       list_foreach_forward_named(proc->thread_list, thr, nb_thrs,       list_foreach_forward_named(proc->thread_list, thr, nb_thrs,
Line 103 
Line 124 
                              thr->state,                              thr->state,
                              (void*)sos_cpu_context_get_PC(thr->cpu_state));                              (void*)sos_cpu_context_get_PC(thr->cpu_state));
         }         }
  
        return TRUE;
     }     }
  
    sos_bochs_printf("<ps>\n");
    sos_hash_walk(process_hash, (sos_hash_map_func_t*)process_iterator, NULL);
   sos_bochs_printf("  ======= %d processes =======\n", nb_procs);   sos_bochs_printf("  ======= %d processes =======\n", nb_procs);
   sos_bochs_printf("</ps>\n");   sos_bochs_printf("</ps>\n");
 } }
Line 111 
Line 137 
  
 sos_ret_t sos_process_subsystem_setup() sos_ret_t sos_process_subsystem_setup()
 { {
  
   /* Create the cache for the process structures */   /* Create the cache for the process structures */
   cache_struct_process = sos_kmem_cache_create("struct_process",   cache_struct_process = sos_kmem_cache_create("struct_process",
                                                sizeof(struct sos_process),                                                sizeof(struct sos_process),
Line 118 
Line 145 
                                                0,                                                0,
                                                SOS_KSLAB_CREATE_MAP                                                SOS_KSLAB_CREATE_MAP
                                                | SOS_KSLAB_CREATE_ZERO);                                                | SOS_KSLAB_CREATE_ZERO);
   if (! cache_struct_process)   if (NULL == cache_struct_process)
      return -SOS_ENOMEM;
  
    /* Allocate pages for the PID bitmap */
    bitmap_pid = (void*) sos_kmem_vmm_alloc(SOS_PROCESS_PID_BITMAP_NPAGES,
                                            SOS_KMEM_VMM_MAP);
    if (NULL == bitmap_pid)
      {
        sos_kmem_cache_destroy(cache_struct_process);
        return -SOS_ENOMEM;
      }
  
    /* The next process might (and actually will !) have PID 1 */
    next_base_pid = 1;
  
    /* Create the hash for the process list */
    process_hash = sos_hash_create("pidtable", struct sos_process,
                                   NULL, NULL, 37,
                                   pid, hlink_pidtable);
    if (NULL == process_hash)
      {
        sos_kmem_cache_destroy(cache_struct_process);
        sos_kmem_vmm_free((sos_vaddr_t)bitmap_pid);
      }
  
    return SOS_OK;
  }
  
  
  /** Helper function to allocate a PID entry and bind it to the given process */
  static sos_ret_t register_process(struct sos_process * proc)
  {
    sos_ret_t retval;
    sos_pid_t pid;
  
    /* First of all: look for a free PID */
    while (TRUE)
      {
        pid = sos_bitmap_ffc(bitmap_pid,
                             SOS_PROCESS_PID_BITMAP_NBITS,
                             next_base_pid);
        if (pid <= 0)
          {
            if (next_base_pid > 1)
              {
                /* try again from the beginning */
                next_base_pid = 1;
                continue;
              }
            else
              /* Bad news: no PID left at all ! */
              return -SOS_EAGAIN;
          }
  
        if (! sos_bitmap_test_and_set(bitmap_pid, pid))
          /* Could allocate it ! */
          break;
      }
  
    /* Set the base PID for next process */
    if (pid < SOS_PROCESS_PID_BITMAP_NBITS)
      next_base_pid = pid + 1;
    else
      next_base_pid = 1;
    
    /* Now bind the process into the process hash */
    proc->pid = pid;
    
    retval = sos_hash_insert(process_hash, proc);
    if (SOS_OK != retval)
      {
        /* Bad news: cannot register in hash */
        SOS_ASSERT_FATAL(sos_bitmap_test_and_clear(bitmap_pid, pid));
        return retval;
      }
  
    return SOS_OK;
  }
  
  
  /** Helper function to deallocate a PID entry and unbind it to the given process */
  static sos_ret_t unregister_process(struct sos_process * proc)
  {
    SOS_ASSERT_FATAL(SOS_OK == sos_hash_remove(process_hash, proc));
    SOS_ASSERT_FATAL(sos_bitmap_test_and_clear(bitmap_pid, proc->pid));
    proc->pid = 0;
   return SOS_OK;   return SOS_OK;
 } }
  
Line 132 
Line 243 
   sos_ui32_t flags;   sos_ui32_t flags;
   struct sos_process *proc;   struct sos_process *proc;
  
  
   if (! proc)   if (! proc)
     return NULL;     return NULL;
Line 170 
Line 280 
     }     }
  
   if (do_copy_current_process)   if (do_copy_current_process)
     proc->address_space = sos_umem_vmm_duplicate_current_thread_as(proc);     {
        struct sos_process * myself = sos_thread_get_current()->process;
        proc->address_space
          = sos_umem_vmm_duplicate_as(sos_process_get_address_space(myself),
                                      proc);
      }
     proc->address_space = sos_umem_vmm_create_empty_as(proc);     proc->address_space = sos_umem_vmm_create_empty_as(proc);
  
Line 194 
Line 309 
  
   /* Add it to the global list of processes */   /* Add it to the global list of processes */
   sos_disable_IRQs(flags);   sos_disable_IRQs(flags);
   list_add_tail(process_list, proc);   retval = register_process(proc);
    if (SOS_OK == retval)
      proc->ref_cnt = 1;  /* Mark the process as referenced */
  
   /* Mark the process as referenced */ 
   proc->ref_cnt = 1; 
  
   if (SOS_OK != retval)   if (SOS_OK != retval)
     {     {
Line 238 
Line 352 
 } }
  
  
  sos_pid_t sos_process_get_pid(const struct sos_process *proc)
  {
    return proc->pid;
  }
  
  
 sos_count_t sos_process_get_nb_threads(const struct sos_process *proc) sos_count_t sos_process_get_nb_threads(const struct sos_process *proc)
 { {
   sos_count_t retval;   sos_count_t retval;
Line 339 
Line 459 
  
 sos_ret_t sos_ret_t
 sos_process_register_opened_file(struct sos_process *proc, sos_process_register_opened_file(struct sos_process *proc,
                                  struct sos_fs_opened_file * of)                                  struct sos_fs_opened_file * of,
                                   int start_search_at_fd)
   int i;   int i;
   for (i = 0 ; i < SOS_PROCESS_MAX_OPENED_FILES ; i++)   for (i = start_search_at_fd ;
         i < SOS_PROCESS_MAX_OPENED_FILES ;
         i++)
       {       {
         proc->fds[i] = of;         proc->fds[i] = of;
Line 417 
Line 540 
       sos_restore_IRQs(flags);       sos_restore_IRQs(flags);
       return -SOS_EBUSY;       return -SOS_EBUSY;
     }     }
   list_delete(process_list, proc);   unregister_process(proc);
  
   /* Close the file descriptors */   /* Close the file descriptors */
  
 

sos-code-article9.5/sos/process.h (2006-01-22 03:48:31.000000000 -0800 )
sos-code-article10/sos/process.h (2006-12-20 13:59:58.000000000 -0800 )
Line 52 
Line 52 
 #define SOS_DEFAULT_USER_STACK_SIZE (8 << 20) #define SOS_DEFAULT_USER_STACK_SIZE (8 << 20)
  
  
  /** Number of pages for the pid bitmap (1 page = 32k PIDs) */
  #define SOS_PROCESS_PID_BITMAP_NPAGES  1
  
  
 /** /**
  * Initialization of the process subsystem  * Initialization of the process subsystem
  */  */
Line 93 
Line 97 
  
  
 /** /**
   * Return the PID of the process
   */
  sos_pid_t sos_process_get_pid(const struct sos_process *proc);
  
  
  /**
  * Return the number of threads currently registered in the process  * Return the number of threads currently registered in the process
  */  */
 sos_count_t sos_process_get_nb_threads(const struct sos_process *proc); sos_count_t sos_process_get_nb_threads(const struct sos_process *proc);
Line 169 
Line 179 
  */  */
 sos_ret_t sos_ret_t
 sos_process_register_opened_file(struct sos_process *proc, sos_process_register_opened_file(struct sos_process *proc,
                                  struct sos_fs_opened_file * of);                                  struct sos_fs_opened_file * of,
                                   int start_search_at_fd);
  
 /** /**
  
 

sos-code-article9.5/sos/syscall.c (2006-01-22 03:48:31.000000000 -0800 )
sos-code-article10/sos/syscall.c (2006-12-20 13:59:58.000000000 -0800 )
Line 20 
Line 20 
 #include <sos/kmalloc.h> #include <sos/kmalloc.h>
 #include <sos/klibc.h> #include <sos/klibc.h>
 #include <drivers/bochs.h> #include <drivers/bochs.h>
 #include <hwcore/paging.h> 
 #include <sos/umem_vmm.h> #include <sos/umem_vmm.h>
 #include <drivers/zero.h> #include <drivers/zero.h>
Line 46 
Line 45 
 { {
   sos_ret_t retval;   sos_ret_t retval;
  
  
   switch(syscall_id)   switch(syscall_id)
     {     {
     case SOS_SYSCALL_ID_EXIT:     case SOS_SYSCALL_ID_EXIT:
Line 82 
Line 82 
           sos_duplicate_user_thread(NULL, new_proc,           sos_duplicate_user_thread(NULL, new_proc,
                                     cur_thr,                                     cur_thr,
                                     user_ctxt,                                     user_ctxt,
                                     0);                                     0 /* fork return value for child ! */);
           {           {
             sos_process_unref(new_proc);             sos_process_unref(new_proc);
Line 95 
Line 95 
         /* Return to the "parent" thread with a value different from         /* Return to the "parent" thread with a value different from
            0. Unix says it should be the "PID" of the child. We don't            0. Unix says it should be the "PID" of the child. We don't
            have such a "PID" notion for now */            have such a "PID" notion for now */
         retval = (sos_ui32_t)new_proc;         retval = (sos_ui32_t)sos_process_get_pid(new_proc);
        }
        break;
  
      case SOS_SYSCALL_ID_GETPID:
        {
          struct sos_thread *cur_thr;
          struct sos_process * proc;
  
          cur_thr = sos_thread_get_current();
          proc    = cur_thr->process;
  
          retval = (sos_ui32_t) sos_process_get_pid(proc);
       break;       break;
  
Line 105 
Line 117 
         struct sos_process * proc;         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_uaddr_t src_argaddr;
          sos_size_t  len_args;
         sos_size_t len;         sos_size_t len;
         char * str;         char * str;
  
Line 119 
Line 133 
           }           }
  
         /* Get the user arguments */         /* Get the user arguments */
         retval = sos_syscall_get2args(user_ctxt, & user_str, & len);         retval = sos_syscall_get4args(user_ctxt, & user_str, & len,
                                        & src_argaddr, & len_args);
           break;           break;
  
          /* Don't go any further if the arg/env array is obviously too
             large */
          if (SOS_DEFAULT_USER_STACK_SIZE <= len_args)
            {
              retval = -SOS_EINVAL;
              break;
            }
  
         /* Copy the program name into kernel sppace */         /* Copy the program name into kernel sppace */
         retval = sos_strndup_from_user(& str, user_str, len + 1, 0);         retval = sos_strndup_from_user(& str, user_str, len + 1, 0);
         if (SOS_OK != retval)         if (SOS_OK != retval)
Line 151 
Line 174 
  
         /* Allocate space for the user stack (8MB) */         /* Allocate space for the user stack (8MB) */
 #define SOS_DEFAULT_USER_STACK_SIZE (8 << 20) #define SOS_DEFAULT_USER_STACK_SIZE (8 << 20)
         ustack = (SOS_PAGING_TOP_USER_ADDRESS - SOS_DEFAULT_USER_STACK_SIZE)         ustack = (SOS_PAGING_UPPER_USER_ADDRESS
                    - SOS_DEFAULT_USER_STACK_SIZE)
         retval = sos_dev_zero_map(new_as, &ustack, SOS_DEFAULT_USER_STACK_SIZE,         retval = sos_dev_zero_map(new_as, &ustack, SOS_DEFAULT_USER_STACK_SIZE,
                                   SOS_VM_MAP_PROT_READ | SOS_VM_MAP_PROT_WRITE,                                   SOS_VM_MAP_PROT_READ | SOS_VM_MAP_PROT_WRITE,
Line 163 
Line 187 
             break;             break;
           }           }
  
          /* ustack now refers to the initial stack pointer of the new
             user thread: update it here */
          ustack = SOS_ALIGN_INF((ustack + SOS_DEFAULT_USER_STACK_SIZE
                                  - len_args), 4);
  
          if (len_args != sos_usercpy(new_as, ustack,
                                      NULL, src_argaddr,
                                      len_args))
            {
              sos_umem_vmm_delete_as(new_as);
              sos_kfree((sos_vaddr_t)str);
              retval = -SOS_EFAULT;
              break;
            }
  
         /* Now create the user thread */         /* Now create the user thread */
         new_thr = sos_create_user_thread(NULL,         new_thr = sos_create_user_thread(NULL,
                                          proc,                                          proc,
                                          start_uaddr,                                          start_uaddr,
                                          0, 0,                                          0, 0,
                                          ustack + SOS_DEFAULT_USER_STACK_SIZE                                          ustack,
                                            - 4, 
         if (! new_thr)         if (! new_thr)
           {           {
Line 297 
Line 335 
       }       }
       break;       break;
  
  
     case SOS_SYSCALL_ID_MSYNC:     case SOS_SYSCALL_ID_MSYNC:
       {       {
         sos_uaddr_t start_uaddr;         sos_uaddr_t start_uaddr;
Line 322 
Line 361 
       }       }
       break;       break;
  
  
     case SOS_SYSCALL_ID_NEW_THREAD:     case SOS_SYSCALL_ID_NEW_THREAD:
       {       {
         sos_uaddr_t start_func;         sos_uaddr_t start_func;
Line 593 
Line 633 
         if (SOS_OK != retval)         if (SOS_OK != retval)
           break;           break;
  
         retval = sos_process_register_opened_file(proc, of);         retval = sos_process_register_opened_file(proc, of, 0);
           {           {
             sos_fs_close(of);             sos_fs_close(of);
  
 

sos-code-article9.5/sos/syscall.h (2006-01-22 03:48:31.000000000 -0800 )
sos-code-article10/sos/syscall.h (2006-12-20 13:59:58.000000000 -0800 )
Line 29 
Line 29 
 /** /**
  * The user services offered by the SOS kernel  * The user services offered by the SOS kernel
  */  */
 #define SOS_SYSCALL_ID_EXIT        256 /**< Args: retval, retval=NONE */ #define SOS_SYSCALL_ID_EXIT        67  /**< Args: retval, retval=NONE */
  
 /** /**
Line 42 
Line 42 
 /** /**
  * User memory mappings management  * User memory mappings management
  */  */
  #define SOS_SYSCALL_ID_GETPID      256 /**< Args: NONE, retval=cur. proc. PID */
 #define SOS_SYSCALL_ID_FORK        257 /**< Args: NONE, retval={father:child_pd, child:0} */ #define SOS_SYSCALL_ID_FORK        257 /**< Args: NONE, retval={father:child_pd, child:0} */
 #define SOS_SYSCALL_ID_EXEC        258 /**< Args: ptr_exec_path strlen_exec_path, retval=errno */ #define SOS_SYSCALL_ID_EXEC        258 /**< Args: ptr_exec_path strlen_exec_path addr_args len_args, retval=errno */
 #define SOS_SYSCALL_ID_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 72 
Line 73 
 #define SOS_SYSCALL_ID_FSMMAP      566 /**< Args: &hint len prot flags fd uoffs64_hi uoffs64_lo, 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_FSYNC       567 /**< Args: fd, retval=errno */
 #define SOS_SYSCALL_ID_FCNTL       568 /**< Args: fd cmd arg, retval=errno */ #define SOS_SYSCALL_ID_FCNTL       568 /**< Args: fd cmd arg, retval=errno */
  /* fcntl possible commands */
  #  define SOS_FCNTL_DUPFD          0x4201
  #  define SOS_FCNTL_GETFD          0x4202
  #  define SOS_FCNTL_SETFD          0x4203
  #  define SOS_FCNTL_GETFL          0x4204
  #  define SOS_FCNTL_SETFL          0x4205
 #define SOS_SYSCALL_ID_IOCTL       569 /**< Args: fd cmd arg, retval=errno */ #define SOS_SYSCALL_ID_IOCTL       569 /**< Args: fd cmd arg, retval=errno */
  
 #define SOS_SYSCALL_ID_CREAT       570 /**< Args: uaddr_path pathlen access_rights, retval=errno */ #define SOS_SYSCALL_ID_CREAT       570 /**< Args: uaddr_path pathlen access_rights, retval=errno */
  
 

sos-code-article9.5/sos/thread.c (2006-01-22 03:48:31.000000000 -0800 )
sos-code-article10/sos/thread.c (2006-12-20 13:59:58.000000000 -0800 )
Line 70 
Line 70 
 static struct sos_kslab_cache *cache_thread; static struct sos_kslab_cache *cache_thread;
  
  
 /** 
  * (Forwad declaration) Helper function to change the MMU config of 
  * the current executing thread. Analogous to function 
  * sos_thread_change_current_mm_context() of article 7 
  */ 
 static sos_ret_t change_current_mm_context(struct sos_mm_context *mm_ctxt); 
  
  
 { {
   SOS_ASSERT_FATAL(current_thread->state == SOS_THR_RUNNING);   SOS_ASSERT_FATAL(current_thread->state == SOS_THR_RUNNING);
Line 361 
Line 353 
       SOS_ASSERT_FATAL(the_thread->process != NULL);       SOS_ASSERT_FATAL(the_thread->process != NULL);
  
       /* It should not squat any other's address space */       /* It should not squat any other's address space */
       SOS_ASSERT_FATAL(the_thread->squatted_mm_context == NULL);       SOS_ASSERT_FATAL(the_thread->squatted_address_space == NULL);
       /* Perform an MMU context switch if needed */       /* Perform an MMU context switch if needed */
       sos_mm_context_switch_to(sos_process_get_mm_context(the_thread->process));       sos_umem_vmm_set_current_as(sos_process_get_address_space(the_thread->process));
  
   /* the_thread is a kernel thread squatting a precise address   /* Restore the address space currently in use */
      space ? */   else
   else if (the_thread->squatted_mm_context != NULL)       sos_umem_vmm_set_current_as(the_thread->squatted_address_space);
     sos_mm_context_switch_to(the_thread->squatted_mm_context); 
  
  
Line 386 
Line 377 
  
   sos_kfree((sos_vaddr_t) thr->kernel_stack_base_addr);   sos_kfree((sos_vaddr_t) thr->kernel_stack_base_addr);
  
   /* If the thread squats an address space, release it */   /* Not allowed to squat any user space at deletion time */
   if (thr->squatted_mm_context)   SOS_ASSERT_FATAL(NULL == thr->squatted_address_space);
     SOS_ASSERT_FATAL(SOS_OK == change_current_mm_context(NULL)); 
   /* For a user thread: remove the thread from the process threads' list */   /* For a user thread: remove the thread from the process threads' list */
   if (thr->process)   if (thr->process)
Line 780 
Line 770 
  */  */
  
  
 static sos_ret_t 
 change_current_mm_context(struct sos_mm_context *mm_ctxt) 
 { 
   /* Retrieve the previous mm context */ 
   struct sos_mm_context * prev_mm_ctxt 
     = current_thread->squatted_mm_context; 
  
   /* Update current thread's squatted mm context */ 
   current_thread->squatted_mm_context = mm_ctxt; 
  
   /* Update the reference counts and switch the MMU configuration if 
      needed */ 
   if (mm_ctxt != NULL) 
     { 
       sos_mm_context_ref(mm_ctxt); /* Because it is now referenced as 
                                       the squatted_mm_context field of 
                                       the thread */ 
       sos_mm_context_switch_to(mm_ctxt); 
     } 
   else 
     sos_mm_context_unref(prev_mm_ctxt); /* Because it is not referenced as 
                                            the squatted_mm_context field of 
                                            the thread any more */ 
  
   return SOS_OK; 
 } 
  
  
 sos_thread_prepare_user_space_access(struct sos_umem_vmm_as * dest_as, sos_thread_prepare_user_space_access(struct sos_umem_vmm_as * dest_as,
                                      sos_vaddr_t fixup_retvaddr)                                      sos_vaddr_t fixup_retvaddr)
Line 823 
Line 785 
  
       dest_as = sos_process_get_address_space(current_thread->process);       dest_as = sos_process_get_address_space(current_thread->process);
     }     }
   else 
     /* Don't allow to access to an address space different than that 
        of the current thread if the page fault are allowed ! */ 
     SOS_ASSERT_FATAL(! fixup_retvaddr); 
   sos_disable_IRQs(flags);   sos_disable_IRQs(flags);
   SOS_ASSERT_FATAL(NULL == current_thread->squatted_mm_context);   SOS_ASSERT_FATAL(NULL == current_thread->squatted_address_space);
  
   /* Change the MMU configuration and init the fixup return address */   /* Change the MMU configuration and init the fixup return address */
   retval = change_current_mm_context(sos_umem_vmm_get_mm_context(dest_as));   retval = sos_umem_vmm_set_current_as(dest_as);
     {     {
        current_thread->squatted_address_space      = dest_as;
       current_thread->fixup_uaccess.return_vaddr  = fixup_retvaddr;       current_thread->fixup_uaccess.return_vaddr  = fixup_retvaddr;
       current_thread->fixup_uaccess.faulted_uaddr = 0;             current_thread->fixup_uaccess.faulted_uaddr = 0;      
     }     }
Line 852 
Line 811 
   sos_ui32_t flags;   sos_ui32_t flags;
  
   sos_disable_IRQs(flags);   sos_disable_IRQs(flags);
   SOS_ASSERT_FATAL(NULL != current_thread->squatted_mm_context);   SOS_ASSERT_FATAL(NULL != current_thread->squatted_address_space);
   /* Don't impose anything regarding the current MMU configuration anymore */   /* Don't impose anything regarding the current MMU configuration anymore */
   retval = change_current_mm_context(NULL); 
   current_thread->fixup_uaccess.faulted_uaddr = 0;   current_thread->fixup_uaccess.faulted_uaddr = 0;
  
    retval = sos_umem_vmm_set_current_as(NULL);
    current_thread->squatted_address_space      = NULL;
  
   sos_restore_IRQs(flags);   sos_restore_IRQs(flags);
   return retval;   return retval;
 } }
  
 

sos-code-article9.5/sos/thread.h (2006-01-22 03:48:31.000000000 -0800 )
sos-code-article10/sos/thread.h (2006-12-20 13:59:58.000000000 -0800 )
Line 126 
Line 126 
    * This is the SOS implementation of the Linux "Lazy TLB" and    * This is the SOS implementation of the Linux "Lazy TLB" and
    * address-space loaning.    * address-space loaning.
    */    */
   struct sos_mm_context *squatted_mm_context;   struct sos_umem_vmm_as *squatted_address_space;
   /* Data specific to each state */   /* Data specific to each state */
   union   union
Line 349 
Line 349 
  * @param dest_as The address space we want to access, or NULL to  * @param dest_as The address space we want to access, or NULL to
  * access current thread's address space  * access current thread's address space
  *  *
  * @param fixup_retvaddr When != 0, then dest_as MUST BE NULL (we  * @param fixup_retvaddr When != 0, the page fault handler should
  * don't allow controlled access from kernel into user space from a  * accept page faults from the kernel in user space, and resolve them
  * foreign thread). In this case, the page fault handler should accept  * in the usual way. The value in retvaddr is where the page fault
  * page faults from the kernel in user space, and resolve them in the  * handler has to return to in case the page fault remains
  * usual way. The value in retvaddr is where the page fault handler  * unresolved. The address of the faulting address is kept in
  * has to return to in case the page fault remains unresolved. The 
  * address of the faulting address is kept in 
  *  *
  * @note typical values for fixup_retvaddr are obtained by "Labels as  * @note typical values for fixup_retvaddr are obtained by "Labels as
  
 

sos-code-article9.5/sos/types.h (2006-01-22 03:48:31.000000000 -0800 )
sos-code-article10/sos/types.h (2006-12-20 13:59:58.000000000 -0800 )
Line 49 
Line 49 
 /** Generic count of objects (LARGE) */ /** Generic count of objects (LARGE) */
 typedef unsigned long long int sos_lcount_t; typedef unsigned long long int sos_lcount_t;
  
  /** Process identifiers */
  typedef unsigned int           sos_pid_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 */
 typedef unsigned long int      sos_ui32_t; /**< 32b unsigned */ typedef unsigned long int      sos_ui32_t; /**< 32b unsigned */
  
 

sos-code-article9.5/sos/uaccess.c (2006-01-22 03:48:31.000000000 -0800 )
sos-code-article10/sos/uaccess.c (2006-12-20 13:59:58.000000000 -0800 )
Line 34 
Line 34 
  ({ if (__uaccess_zero_fool_gcc) goto lbl; })  ({ if (__uaccess_zero_fool_gcc) goto lbl; })
  
  
 static sos_ret_t nocheck_user_memcpy(sos_vaddr_t dest, /**
   * @param user_as The user address space we want to transfer from/to
   *
   * @return >=0 : number of bytes successfully transferred, <0 an error code
   */
  static sos_ret_t nocheck_user_memcpy(struct sos_umem_vmm_as * user_as,
                                       sos_vaddr_t dest,
                                      sos_size_t size,                                      sos_size_t size,
                                      sos_bool_t transfer_from_user)                                      sos_bool_t transfer_from_user)
Line 50 
Line 56 
   if (size <= 0)   if (size <= 0)
     return 0;     return 0;
  
   retval = sos_thread_prepare_user_space_access(NULL,   retval = sos_thread_prepare_user_space_access(user_as,
   if (SOS_OK != retval)   if (SOS_OK != retval)
     return retval;     return retval;
Line 103 
Line 109 
                                sos_size_t size)                                sos_size_t size)
 { {
   /* Make sure user is trying to access user space */   /* Make sure user is trying to access user space */
   if (user_from < SOS_PAGING_BASE_USER_ADDRESS)   if (! SOS_PAGING_IS_USER_AREA(user_from, size) )
  
   if (user_from > SOS_PAGING_TOP_USER_ADDRESS - size)   /* Make sure the copy totally resides inside kernel space */
    if (! SOS_PAGING_IS_KERNEL_AREA(kernel_to, size) )
  
   return nocheck_user_memcpy(kernel_to, user_from, size, TRUE);   return nocheck_user_memcpy(NULL, kernel_to, user_from, size, TRUE);
  
  
Line 144 
Line 151 
                              sos_vaddr_t kernel_from,                              sos_vaddr_t kernel_from,
                              sos_size_t size)                              sos_size_t size)
 { {
    /* Make sure the source totally resides inside kernel space */
    if (! SOS_PAGING_IS_KERNEL_AREA(kernel_from, size) )
      return -SOS_EPERM;
  
    /* Make sure user is trying to access user space */
    if (! SOS_PAGING_IS_USER_AREA(user_to, size) )
      return -SOS_EPERM;
  
    return nocheck_user_memcpy(NULL, user_to, kernel_from, size, FALSE);
  }
  
  
  sos_ret_t sos_memcpy_from_specified_userspace(sos_vaddr_t kernel_to,
                                                struct sos_umem_vmm_as * src_as,
                                                sos_uaddr_t user_from,
                                                sos_size_t size)
  {
    if (NULL == src_as)
      return -SOS_EINVAL;
    
    /* Make sure user is trying to access user space */
    if (! SOS_PAGING_IS_USER_AREA(user_from, size) )
      return -SOS_EPERM;
  
    /* Make sure the copy totally resides inside kernel space */
    if (! SOS_PAGING_IS_KERNEL_AREA(kernel_to, size) )
      return -SOS_EPERM;
  
    return nocheck_user_memcpy(src_as, kernel_to, user_from, size, TRUE);
  }
  
  
  sos_ret_t sos_memcpy_to_specified_userspace(struct sos_umem_vmm_as * dst_as,
                                              sos_uaddr_t user_to,
                                              sos_vaddr_t kernel_from,
                                              sos_size_t size)
  {
    if (NULL == dst_as)
      return -SOS_EINVAL;
    
    /* Make sure the source totally resides inside kernel space */
    if (! SOS_PAGING_IS_KERNEL_AREA(kernel_from, size) )
      return -SOS_EPERM;
  
   /* Make sure user is trying to access user space */   /* Make sure user is trying to access user space */
   if (user_to < SOS_PAGING_BASE_USER_ADDRESS)   if (! SOS_PAGING_IS_USER_AREA(user_to, size) )
  
   if (user_to > SOS_PAGING_TOP_USER_ADDRESS - size)   return nocheck_user_memcpy(dst_as, user_to, kernel_from, size, FALSE);
  }
  
  
  sos_ret_t sos_usercpy(struct sos_umem_vmm_as * dst_as,
                        sos_uaddr_t dst_uaddr,
                        struct sos_umem_vmm_as * src_as,
                        sos_uaddr_t src_uaddr,
                        sos_size_t size)
  {
    sos_vaddr_t kern_addr;
    sos_ret_t retval;
  
    if (size <= 0)
      return 0;
  
    /* Make sure user is trying to access user space */
    if (! SOS_PAGING_IS_USER_AREA(src_uaddr, size) )
    if (! SOS_PAGING_IS_USER_AREA(dst_uaddr, size) )
      return -SOS_EPERM;
  
    kern_addr = sos_kmalloc(size, 0);
    if (! kern_addr)
      return -SOS_ENOMEM;
  
    retval = nocheck_user_memcpy(src_as, kern_addr, src_uaddr,
                                 size, TRUE);
    if (retval <= 0)
      {
        sos_kfree((sos_vaddr_t)kern_addr);
        return retval;
      }
  
    retval = nocheck_user_memcpy(dst_as, dst_uaddr, kern_addr,
                                 retval, FALSE);
  
   return nocheck_user_memcpy(user_to, kernel_from, size, FALSE);   sos_kfree((sos_vaddr_t)kern_addr);
  
    return retval;
  
  
Line 167 
Line 254 
     return 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 (! SOS_PAGING_IS_USER_AREA(user_str, max_len) )
  
   /* Don't allow invalid max_len */ 
   if ( (max_len < 1) || (max_len > SOS_PAGING_USER_SPACE_SIZE) ) 
     return -SOS_EINVAL; 
  
                                                 (sos_vaddr_t) && catch_pgflt);                                                 (sos_vaddr_t) && catch_pgflt);
   if (SOS_OK != retval)   if (SOS_OK != retval)
Line 252 
Line 335 
 sos_ret_t sos_strzcpy_from_user(char *kernel_to, sos_uaddr_t user_from, sos_ret_t sos_strzcpy_from_user(char *kernel_to, sos_uaddr_t user_from,
                                 sos_size_t max_len)                                 sos_size_t max_len)
 { {
    if (max_len <= 0)
      return -SOS_EINVAL;
  
   /* Make sure user is trying to access user space */   /* Make sure user is trying to access user space */
   if ((sos_uaddr_t)user_from < SOS_PAGING_BASE_USER_ADDRESS)   if (! SOS_PAGING_IS_USER_AREA(user_from, max_len) )
  
   /* Don't allow invalid max_len */   /* Make sure the copy totally resides inside kernel space */
   if ( (max_len < 1) || (max_len > SOS_PAGING_USER_SPACE_SIZE) )   if (! SOS_PAGING_IS_KERNEL_AREA((sos_vaddr_t)kernel_to, max_len) )
     return -SOS_EINVAL;     return -SOS_EPERM;
   return nocheck_user_strzcpy(kernel_to, (const char*)user_from, max_len);   return nocheck_user_strzcpy(kernel_to, (const char*)user_from, max_len);
 } }
Line 267 
Line 353 
 sos_ret_t sos_strzcpy_to_user(sos_uaddr_t user_to, const char *kernel_from, sos_ret_t sos_strzcpy_to_user(sos_uaddr_t user_to, const char *kernel_from,
                               sos_size_t max_len)                               sos_size_t max_len)
 { {
   /* Make sure user is trying to access user space */   if (max_len <= 0)
   if ((sos_uaddr_t)user_to < SOS_PAGING_BASE_USER_ADDRESS)     return -SOS_EINVAL;
  
    /* Make sure the source totally resides inside kernel space */
    if (! SOS_PAGING_IS_KERNEL_AREA((sos_vaddr_t)kernel_from, max_len) )
  
   /* Don't allow invalid max_len */   /* Make sure user is trying to access user space */
   if ( (max_len < 1) || (max_len > SOS_PAGING_USER_SPACE_SIZE) )   if (! SOS_PAGING_IS_USER_AREA(user_to, max_len) )
     return -SOS_EINVAL;     return -SOS_EPERM;
   return nocheck_user_strzcpy((char*)user_to, kernel_from, max_len);   return nocheck_user_strzcpy((char*)user_to, kernel_from, max_len);
 } }
  
 

sos-code-article9.5/sos/uaccess.h (2006-01-22 03:48:31.000000000 -0800 )
sos-code-article10/sos/uaccess.h (2006-12-20 13:59:58.000000000 -0800 )
Line 27 
Line 27 
  
 #include <sos/types.h> #include <sos/types.h>
 #include <sos/errno.h> #include <sos/errno.h>
  #include <sos/umem_vmm.h>
 /** /**
  * Retrieve a bunch of data from the user space of the  * Retrieve a bunch of data from the user space of the
Line 55 
Line 55 
  
  
 /** /**
  * Transfer a bunch of data to the user space of the  * Transfer a bunch of data from kernel space into the user space of
  * current_thread->process  * the current_thread->process
  * @return <0 n error ! Return the number of bytes successfully copied  * @return <0 n error ! Return the number of bytes successfully copied
  * otherwise.  * otherwise.
Line 67 
Line 67 
  
  
 /** /**
   * Variant of sos_memcpy_from_user()
   * Retrieve a bunch of data from the user space of the given src_as
   * address space into kernel space
   *
   * @return NULL on error (including unresolved page fault during
   * transfer)
   *
   * @note src_as MUST NOT be NULL
   */
  sos_ret_t sos_memcpy_from_specified_userspace(sos_vaddr_t kernel_to,
                                                struct sos_umem_vmm_as * src_as,
                                                sos_uaddr_t user_from,
                                                sos_size_t size);
  
  
  /**
   * Variant of sos_memcpy_to_user()
   * Transfer a bunch of data from kernel space into the user space of
   * the given dst_as address space
   *
   * @return <0 n error ! Return the number of bytes successfully copied
   * otherwise.
   *
   * @note dst_as MUST NOT be NULL
   */
  sos_ret_t sos_memcpy_to_specified_userspace(struct sos_umem_vmm_as * dst_as,
                                              sos_uaddr_t user_to,
                                              sos_vaddr_t kernel_from,
                                              sos_size_t size);
  
  /**
   * Copy data from an user space into another
   * @return The number of bytes successfuly copied
   * @note dst_as and src_as may be NULL, in which case the current
   * thread->process address space is used for the copy
   */
  sos_ret_t sos_usercpy(struct sos_umem_vmm_as * dst_as,
                        sos_uaddr_t dst_uaddr,
                        struct sos_umem_vmm_as * src_as,
                        sos_uaddr_t src_uaddr,
                        sos_size_t size);
  
  
  /**
  * @return the length of the given user space string user_str  * @return the length of the given user space string user_str
  * (excluding the trailing \0), up to max_len bytes. <0 on error  * (excluding the trailing \0), up to max_len bytes. <0 on error
  * (unresolved page fault, etc.)  * (unresolved page fault, etc.)
  
 

sos-code-article9.5/sos/umem_vmm.c (2006-01-22 03:48:31.000000000 -0800 )
sos-code-article10/sos/umem_vmm.c (2006-12-20 13:59:58.000000000 -0800 )
Line 23 
Line 23 
 #include <drivers/bochs.h> #include <drivers/bochs.h>
 #include <hwcore/mm_context.h> #include <hwcore/mm_context.h>
 #include <hwcore/paging.h> #include <hwcore/paging.h>
  #include <hwcore/irq.h>
 #include <drivers/zero.h> #include <drivers/zero.h>
  
 #include "umem_vmm.h" #include "umem_vmm.h"
Line 230 
Line 231 
 } }
  
  
  static struct sos_umem_vmm_as * current_address_space = NULL;
  struct sos_umem_vmm_as * sos_umem_vmm_get_current_as(void)
  {
    return current_address_space;
  }
  
  
  sos_ret_t sos_umem_vmm_set_current_as(struct sos_umem_vmm_as * as)
  {
    sos_ui32_t flags;
    struct sos_umem_vmm_as *prev_as = current_address_space;
  
    if (current_address_space == as)
      return SOS_OK;
  
    if (NULL != as)
      {
        sos_disable_IRQs(flags);
        sos_process_ref(sos_umem_vmm_get_process(as));
        sos_mm_context_switch_to(sos_umem_vmm_get_mm_context(as));
        current_address_space = as;
        sos_restore_IRQs(flags);
      }
    else
      current_address_space = as;
  
    if (prev_as)
      sos_process_unref(sos_umem_vmm_get_process(prev_as));
  
    return SOS_OK;
  }
  
  
 struct sos_umem_vmm_as * struct sos_umem_vmm_as *
 sos_umem_vmm_create_empty_as(struct sos_process *owner) sos_umem_vmm_create_empty_as(struct sos_process *owner)
 { {
Line 252 
Line 286 
  
  
 struct sos_umem_vmm_as * struct sos_umem_vmm_as *
 sos_umem_vmm_duplicate_current_thread_as(struct sos_process *owner) sos_umem_vmm_duplicate_as(struct sos_umem_vmm_as * model_as,
                            struct sos_process *for_owner)
   __label__ undo_creation;   __label__ undo_creation;
   struct sos_umem_vmm_as * my_as; 
   int nb_vr;   int nb_vr;
  
Line 264 
Line 298 
   if (! new_as)   if (! new_as)
     return NULL;     return NULL;
  
   my_as = sos_process_get_address_space(sos_thread_get_current()->process);   new_as->process = for_owner;
   new_as->process = owner; 
  
   /*   /*
Line 274 
Line 307 
    * COW)    * COW)
    */    */
   SOS_ASSERT_FATAL(SOS_OK   SOS_ASSERT_FATAL(SOS_OK
                    == sos_thread_prepare_user_space_access(my_as,                    == sos_thread_prepare_user_space_access(model_as,
                                                              NULL));                                                              NULL));
  
   /* Copy the virtual regions */   /* Copy the virtual regions */
   list_foreach_named(my_as->list_vr, model_vr, nb_vr, prev_in_as, next_in_as)   list_foreach_named(model_as->list_vr, model_vr, nb_vr, prev_in_as, next_in_as)
       struct sos_umem_vmm_vr * vr;       struct sos_umem_vmm_vr * vr;
  
Line 315 
Line 348 
     }     }
  
   /* Now copy the current MMU configuration */   /* Now copy the current MMU configuration */
   new_as->mm_context = sos_mm_context_duplicate(my_as->mm_context);   new_as->mm_context = sos_mm_context_duplicate(model_as->mm_context);
     goto undo_creation;     goto undo_creation;
  
   /* Correct behavior */   /* Correct behavior */
   new_as->heap_start = my_as->heap_start;   new_as->heap_start = model_as->heap_start;
   new_as->heap_size  = my_as->heap_size;   new_as->heap_size  = model_as->heap_size;
   new_as->phys_total = my_as->phys_total;   new_as->phys_total = model_as->phys_total;
   memcpy(& new_as->vm_total, & my_as->vm_total, sizeof(struct vm_usage));   memcpy(& new_as->vm_total, & model_as->vm_total, sizeof(struct vm_usage));
   memcpy(& new_as->vm_shrd, & my_as->vm_shrd, sizeof(struct vm_usage));   memcpy(& new_as->vm_shrd, & model_as->vm_shrd, sizeof(struct vm_usage));
   return new_as;   return new_as;
  
Line 555 
Line 588 
        */        */
  
       /* Make sure the hint_uaddr hint is valid */       /* Make sure the hint_uaddr hint is valid */
       if (hint_uaddr < SOS_PAGING_BASE_USER_ADDRESS)       if (! SOS_PAGING_IS_USER_AREA(hint_uaddr, size) )
         { retval = -SOS_EINVAL; goto return_mmap; } 
       if (hint_uaddr > SOS_PAGING_TOP_USER_ADDRESS - size) 
  
       /* Unmap any overlapped VR */       /* Unmap any overlapped VR */
Line 712 
Line 743 
                    sos_uaddr_t uaddr, sos_size_t size)                    sos_uaddr_t uaddr, sos_size_t size)
 { {
   struct sos_umem_vmm_vr *vr, *preallocated_vr;   struct sos_umem_vmm_vr *vr, *preallocated_vr;
   sos_bool_t need_to_setup_mmu; 
    sos_bool_t need_to_change_address_space = FALSE;
  
   if (! SOS_IS_PAGE_ALIGNED(uaddr))   if (! SOS_IS_PAGE_ALIGNED(uaddr))
     return -SOS_EINVAL;     return -SOS_EINVAL;
Line 722 
Line 753 
   size = SOS_PAGE_ALIGN_SUP(size);   size = SOS_PAGE_ALIGN_SUP(size);
  
   /* Make sure the uaddr is valid */   /* Make sure the uaddr is valid */
   if (uaddr < SOS_PAGING_BASE_USER_ADDRESS)   if (! SOS_PAGING_IS_USER_AREA(uaddr, size) )
     return -SOS_EINVAL; 
   if (uaddr > SOS_PAGING_TOP_USER_ADDRESS - size) 
  
   /* In some cases, the unmapping might imply a VR to be split into   /* In some cases, the unmapping might imply a VR to be split into
Line 875 
Line 904 
                               uaddr, size, vr->start, vr->size);                               uaddr, size, vr->start, vr->size);
     }     }
  
   need_to_setup_mmu = (sos_thread_get_current()->squatted_mm_context   /* When called from mresize, the address space is already squatted,
                        != as->mm_context);      so we don't have to change it again */
   if (need_to_setup_mmu)   need_to_change_address_space
      = (as != sos_thread_get_current()->squatted_address_space);
  
    if (need_to_change_address_space)
                      == sos_thread_prepare_user_space_access(as,                      == sos_thread_prepare_user_space_access(as,
                                                              (sos_vaddr_t)                                                              (sos_vaddr_t)
                                                                NULL));                                                                NULL));
  
  
    /* Begin independent sub-block */
   {   {
     sos_ret_t sz_unmapped = sos_paging_unmap_interval(uaddr, size);     sos_ret_t sz_unmapped = sos_paging_unmap_interval(uaddr, size);
     SOS_ASSERT_FATAL(sz_unmapped >= 0);     SOS_ASSERT_FATAL(sz_unmapped >= 0);
     as->phys_total -= sz_unmapped;     as->phys_total -= sz_unmapped;
   }   }
   if (need_to_setup_mmu)   /* End independent sub-block */
  
    if (need_to_change_address_space)
  
   if (! used_preallocated_vr)   if (! used_preallocated_vr)
Line 913 
Line 950 
   size = SOS_PAGE_ALIGN_SUP(size);   size = SOS_PAGE_ALIGN_SUP(size);
  
   /* Make sure the uaddr is valid */   /* Make sure the uaddr is valid */
   if (uaddr < SOS_PAGING_BASE_USER_ADDRESS)   if (! SOS_PAGING_IS_USER_AREA(uaddr, size) )
     return -SOS_EINVAL; 
   if (uaddr > SOS_PAGING_TOP_USER_ADDRESS - size) 
  
   /* Pre-allocate 2 new VRs (same reason as for unmap). Because chprot   /* Pre-allocate 2 new VRs (same reason as for unmap). Because chprot
Line 1224 
Line 1259 
   size = SOS_PAGE_ALIGN_SUP(size);   size = SOS_PAGE_ALIGN_SUP(size);
  
   /* Make sure the uaddr is valid */   /* Make sure the uaddr is valid */
   if (uaddr < SOS_PAGING_BASE_USER_ADDRESS)   if (! SOS_PAGING_IS_USER_AREA(uaddr, size) )
     return -SOS_EINVAL; 
   if (uaddr > SOS_PAGING_TOP_USER_ADDRESS - size) 
  
   /* Go from page to page, and for each dirty page in the region, call   /* Go from page to page, and for each dirty page in the region, call
Line 1287 
Line 1320 
   struct sos_umem_vmm_vr *vr, *prev_vr, *next_vr;   struct sos_umem_vmm_vr *vr, *prev_vr, *next_vr;
  
   /* Make sure the new uaddr is valid */   /* Make sure the new uaddr is valid */
   if (*new_uaddr < SOS_PAGING_BASE_USER_ADDRESS)   if (! SOS_PAGING_IS_USER_AREA(*new_uaddr, new_size) )
     return -SOS_EINVAL; 
   if (*new_uaddr > SOS_PAGING_TOP_USER_ADDRESS - new_size) 
  
   old_uaddr = SOS_PAGE_ALIGN_INF(old_uaddr);   old_uaddr = SOS_PAGE_ALIGN_INF(old_uaddr);
Line 1347 
Line 1378 
     must_move_vr |= TRUE;     must_move_vr |= TRUE;
  
   /* If VR would be out-of-user-space, it must be moved */   /* If VR would be out-of-user-space, it must be moved */
   if (*new_uaddr < SOS_PAGING_BASE_USER_ADDRESS)   if (! SOS_PAGING_IS_USER_AREA(*new_uaddr, new_size) )
     must_move_vr |= TRUE; 
   if (*new_uaddr > SOS_PAGING_TOP_USER_ADDRESS - new_size) 
  
   /* The VR must be moved but the user forbids it */   /* The VR must be moved but the user forbids it */
Line 1464 
Line 1493 
                                               sos_bool_t write_access,                                               sos_bool_t write_access,
                                               sos_bool_t user_access)                                               sos_bool_t user_access)
 { {
   struct sos_process     *process = sos_thread_get_current()->process; 
   struct sos_umem_vmm_vr *vr;   struct sos_umem_vmm_vr *vr;
  
   if (! process)   as = current_address_space;
     return -SOS_EFAULT; 
  
   as = sos_process_get_address_space(process); 
     return -SOS_EFAULT;     return -SOS_EFAULT;
  
Line 1590 
Line 1615 
   struct sos_umem_vmm_vr *vr;   struct sos_umem_vmm_vr *vr;
   int nb_vr;   int nb_vr;
  
   if (uaddr < SOS_PAGING_BASE_USER_ADDRESS)   if (! SOS_PAGING_IS_USER_AREA(uaddr, 1) )
     return NULL; 
   if (uaddr > SOS_PAGING_TOP_USER_ADDRESS) 
  
   list_foreach_named(as->list_vr, vr, nb_vr, prev_in_as, next_in_as)   list_foreach_named(as->list_vr, vr, nb_vr, prev_in_as, next_in_as)
Line 1632 
Line 1655 
   if (hint_uaddr < SOS_PAGING_BASE_USER_ADDRESS)   if (hint_uaddr < SOS_PAGING_BASE_USER_ADDRESS)
     hint_uaddr = SOS_PAGING_BASE_USER_ADDRESS;     hint_uaddr = SOS_PAGING_BASE_USER_ADDRESS;
  
   if (hint_uaddr > SOS_PAGING_TOP_USER_ADDRESS - size + 1)   if (hint_uaddr > SOS_PAGING_UPPER_USER_ADDRESS - size + 1)
  
   initial_vr = vr = find_enclosing_or_next_vr(as, hint_uaddr);   initial_vr = vr = find_enclosing_or_next_vr(as, hint_uaddr);
Line 1658 
Line 1681 
           /* No: wrapping up */           /* No: wrapping up */
  
           /* Is there any space before the end of user space ? */           /* Is there any space before the end of user space ? */
           if (hint_uaddr <= SOS_PAGING_TOP_USER_ADDRESS - size)           if (hint_uaddr <= SOS_PAGING_UPPER_USER_ADDRESS - size)
  
           hint_uaddr = SOS_PAGING_BASE_USER_ADDRESS;           hint_uaddr = SOS_PAGING_BASE_USER_ADDRESS;
  
 

sos-code-article9.5/sos/umem_vmm.h (2006-01-22 03:48:31.000000000 -0800 )
sos-code-article10/sos/umem_vmm.h (2006-12-20 13:59:58.000000000 -0800 )
Line 285 
Line 285 
  
  
 /** /**
   * Get the current effective address space. This may be the "normal"
   * address space of the current thread, but not necessarilly: this
   * might be the address space of just another process ! This may
   * happen if a kernel thread of process P wants to access the address
   * space of another process P2. This might also be NULL (no access to
   * user space needed).
   */
  struct sos_umem_vmm_as * sos_umem_vmm_get_current_as(void);
  
  
  /**
   * Change the current effective address space, eventually
   * reconfiguring the MMU. This will increase the owning process's
   * reference count of the given AS, and decrease that of the previous
   * AS (when different).
   *
   * @param as may be NULL (no need to access user space)
   */
  sos_ret_t sos_umem_vmm_set_current_as(struct sos_umem_vmm_as * as);
  
  
  /**
  * Create a new, empty, address space  * Create a new, empty, address space
  *  *
  * @param owner The process that will own the new address space  * @param owner The process that will own the new address space
Line 297 
Line 319 
  
  
 /** /**
  * Create a new address space, copy of the current thread's address  * Create a new address space, copy of the model_as address
  * marked 'read-only' to activate the "copy-on-write" semantics.  * marked 'read-only' to activate the "copy-on-write" semantics.
  *  *
  * @param owner The process that will hold the new address space  * @param model_as The address space to copy
   * @param for_owner The process that will hold the new address space
  * @note automatically calls  * @note automatically calls
  * sos_thread_prepare_user_space_access()/sos_thread_end_user_space_access()  * sos_thread_prepare_user_space_access()/sos_thread_end_user_space_access()
  */  */
 struct sos_umem_vmm_as * struct sos_umem_vmm_as *
 sos_umem_vmm_duplicate_current_thread_as(struct sos_process *owner); sos_umem_vmm_duplicate_as(struct sos_umem_vmm_as * model_as,
                            struct sos_process *for_owner);
  
 /** /**
  
 

sos-code-article9.5/userland/crt_asm.S (1969-12-31 16:00:00.000000000 -0800 )
sos-code-article10/userland/crt_asm.S (2006-12-20 13:59:58.000000000 -0800 )
(New file) 
Line 1 
  /* Copyright (C) 2006  David Decotigny
  
     This program is free software; you can redistribute it and/or
     modify it under the terms of the GNU General Public License
     as published by the Free Software Foundation; either version 2
     of the License, or (at your option) any later version.
  
     This program is distributed in the hope that it will be useful,
     but WITHOUT ANY WARRANTY; without even the implied warranty of
     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     GNU General Public License for more details.
  
     You should have received a copy of the GNU General Public License
     along with this program; if not, write to the Free Software
     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
     USA.
  */
  #define ASM_SOURCE 1
  
  .file "crt_asm.S"
  
  .text
  
  /* The function to call: "C" code start routine (defined in crt.c) */
  .extern _cmain
  
  /* User program entry point */
  .globl _start
          .type   _start, @function
  _start:
          pushl %esp
          call _cmain /* Hello "C" world ! */
          addl $4, %esp
  
          /* We should never reach this line */
          lcall *0 /* Crash the process */
  
 

sos-code-article9.5/userland/crt.c (2006-01-22 03:48:31.000000000 -0800 )
sos-code-article10/userland/crt.c (2006-12-20 13:59:58.000000000 -0800 )
Line 27 
Line 27 
  
 #include <hwcore/swintr.h> #include <hwcore/swintr.h>
 #include <string.h> #include <string.h>
  
  #include "libc.h" /* putenv */
 #include "crt.h" #include "crt.h"
  
 /** /**
   * Macro used to retrieve the 2 parameters passed to any new thread
   * (architecture-dependent)
   */
  #define GET_THREAD_PARAMETERS(param1, param2) \
    asm volatile ("movl %%eax, %0 ; movl %%ebx, %1" \
                  : "=g"(param1), "=g"(param2) : : "%eax", "%ebx" )
  
  
  /**
   * Helper function to retrieve the arg array from the parameter area
   * sent by the parent.
   *
   * Format of the parent's args area:
   *   Number of arguments
   *   Offset of arg 0 (program's name)
   *   Offset of arg 1
   *   ...
   *   Offset of arg N (last argument)
   *   NULL
   *   Offset of environnement variable 0
   *   Offset of environnement variable 1
   *   ...
   *   Offset of environnement variable N
   *   NULL
   *   Contents of arg 0 with terminal \0
   *   Contents of arg 1 with terminal \0
   *   ...
   *   Contents of arg N with terminal \0
   *   Contents of env 0 with terminal \0
   *   Contents of env 1 with terminal \0
   *   ...
   *   Contents of env N with terminal \0
   *
   * This function simply transforms the N offsets into the real
   * addresses of the arg X contents (ie it simply adds the base address
   * of the area).
   */
  static void unmarshall_argv_envp(void * args,
                                   int * argc,
                                   const char ** * argv,
                                   const char ** * envp)
  {
    addr_t * offset = (addr_t*) args;
    *argc = 0;
    *argv = NULL;
  
    offset = args;
  
    /* Get argc */
    *argc = * (int*) offset;
    offset ++;
  
    /* Get base of argv */
    *argv = (const char **) offset;
  
    /* Walk the offsets array and transform these offsets into memory
       addresses */
    for ( ; 0 != *offset ; offset ++)
      *offset += (addr_t) args;
  
    /* Skip the NULL separating argv from envp */
    offset ++;
  
    /* Get base of envp */
    *envp = (const char **) offset;
  
    /* Walk the offsets array and transform these offsets into memory
       addresses */
    for ( ; 0 != *offset ; offset ++)
      {
        *offset += (addr_t) args;
  
        /* Register this environment variable */
        putenv((char*) *offset);
      }
  }
  
  
  /**
  * Starter function !  * Starter function !
  */  */
 void _start(void) __attribute__((noreturn)); /** Function called by crt_asm.S:_start to start user program */
 void _start(void) void _cmain(const char* arg_env_area[]) __attribute__((noreturn));
  void _cmain(const char* arg_env_area[])
  
    /* Will hold the parameters that will be passed to main */
    const char** argv;
    const char** envp;
    int argc;
  
  
   /* This starter function expects a main() function somewhere */   /* This starter function expects a main() function somewhere */
   extern int main(void);   extern int main(int argc, char const* argv[],
                    char const* envp[]);
   /* Reset the bss section */   /* Setup the arguments list and the environment variables */
   extern char _bbss, _ebss;   unmarshall_argv_envp (arg_env_area, & argc, & argv, & envp);
   memset(& _bbss, 0x0, (& _ebss) - (& _bbss)); 
   _sos_exit(main());   _sos_exit(main(argc, argv, envp));
  
  
Line 189 
Line 278 
 } }
  
  
 int _sos_exec(const char * prog) int _sos_getpid()
  {
    return _sos_syscall0(SOS_SYSCALL_ID_GETPID);
  }
  
  
  int _sos_exec(const char * prog,
                void const* args,
                size_t arglen)
   return _sos_syscall2(SOS_SYSCALL_ID_EXEC, (unsigned int)prog,   return _sos_syscall4(SOS_SYSCALL_ID_EXEC, (unsigned int)prog,
                        (unsigned int)strlen(prog));                        (unsigned int)strlen(prog),
                         (unsigned int)args,
                         (unsigned int)arglen);
  
  
Line 244 
Line 343 
  */  */
 static void thread_routine(void) static void thread_routine(void)
 { {
   /* NOTE: variables as named registers is a gcc extension */   sos_thread_func_t * func;
   register unsigned long int reg_arg1 asm("%eax");   unsigned long int arg;
   register unsigned long int reg_arg2 asm("%ebx"); 
   sos_thread_func_t * func = (sos_thread_func_t*)reg_arg1;   GET_THREAD_PARAMETERS(func, arg);
   unsigned long int arg = reg_arg2; 
   func(arg);   func(arg);
   _sos_exit(0);   _sos_exit(0);
  
 

sos-code-article9.5/userland/crt.h (2006-01-22 03:48:31.000000000 -0800 )
sos-code-article10/userland/crt.h (2006-12-20 13:59:58.000000000 -0800 )
Line 105 
Line 105 
  */  */
 int _sos_bochs_write(const char *str, unsigned length); int _sos_bochs_write(const char *str, unsigned length);
  
  
 /** /**
  * Syscall to duplicate the current running process  * Syscall to duplicate the current running process
  */  */
Line 112 
Line 113 
  
  
 /** /**
   * Syscall to retrieved the current process's PID
   */
  int _sos_getpid(void);
  
  
  /**
  * Syscall to re-initialize the address space of the current process  * Syscall to re-initialize the address space of the current process
  * with that of the program 'progname'  * with that of the program 'progname'
  */  *
 int _sos_exec(const char * prog);  * The args argument is the address of an array that contains the
   * arguments and environnement variables for the new process.
   */
  int _sos_exec(const char * prog,
                void const* args,
                size_t arglen);
  
 /** /**
  
 

sos-code-article9.5/userland/envtest.c (1969-12-31 16:00:00.000000000 -0800 )
sos-code-article10/userland/envtest.c (2006-12-20 13:59:58.000000000 -0800 )
(New file) 
Line 1 
  #include <libc.h>
  
  int main(int argc, char * argv[], char *envp[])
  {
    int i;
    char **e;
  
    printf("Nargs: %d\n", argc);
    for (i = 0 ; i < argc ; i ++)
      printf("  arg[%d] = |%s|\n", i, argv[i]);
  
    for (e = envp ; e && *e ; e ++)
      printf("  ENV |%s|\n", *e);
  
    return 0;
  }
  
 

sos-code-article9.5/userland/fstest.c (2006-01-22 03:48:31.000000000 -0800 )
sos-code-article10/userland/fstest.c (2007-04-09 06:11:21.000000000 -0700 )
Line 24 
Line 24 
  
 #include "fstest_utils.h" #include "fstest_utils.h"
  
 #define OPEN_BASE_FD 3 static const int OPEN_BASE_FD = 3;
 /** /**
  * @file fstest.c  * @file fstest.c
  
 

sos-code-article9.5/userland/fstestfat.c (1969-12-31 16:00:00.000000000 -0800 )
sos-code-article10/userland/fstestfat.c (2007-05-22 10:47:47.000000000 -0700 )
(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"
  
  static const int OPEN_BASE_FD = 3;
  
  /**
   * @file fstestfat.c
   *
   * File-system tests
   */
  
  int main(void)
  {
    int fd, len;
    char buff[256];
  
    bochs_printf("Hi from fstest FAT\n");
  
    ls("/", 1, 0);
    ls(".", 1, 0);
    ls("", 1, 0);
    ls(0, 1, 0);
  
    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 == OPEN_BASE_FD + 0);
  
    TEST_EXPECT_CONDITION(fd = open("/", O_RDONLY),
                          RETVAL == OPEN_BASE_FD + 1);
  
    TEST_EXPECT_CONDITION(fd = open("/", O_WRONLY),
                          RETVAL == OPEN_BASE_FD + 2);
  
    TEST_EXPECT_CONDITION(close(OPEN_BASE_FD + 1),
                          RETVAL == 0);
    
    TEST_EXPECT_CONDITION(fd = open("/", O_WRONLY),
                          RETVAL == OPEN_BASE_FD + 1);
  
    TEST_EXPECT_CONDITION(fd = open("//", O_WRONLY),
                          RETVAL == OPEN_BASE_FD + 3);
  
    TEST_EXPECT_CONDITION(fd = open("////////", O_WRONLY),
                          RETVAL == OPEN_BASE_FD + 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 == OPEN_BASE_FD + 5);
  
    TEST_EXPECT_CONDITION(fd = open("tutu.txt", O_RDWR),
                          RETVAL < 0);
  
    ls(".", 1, 0);
  
    TEST_EXPECT_CONDITION(fd = open("tutu.txt", O_RDWR | O_CREAT,
                                    S_IRUSR | S_IWUSR),
                          RETVAL == OPEN_BASE_FD + 6);
    
    ls(".", 1, 0);
  
    TEST_EXPECT_CONDITION(fd = open("tutu.txt", O_RDWR | O_CREAT),
                          RETVAL == OPEN_BASE_FD + 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 == OPEN_BASE_FD + 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 == OPEN_BASE_FD + 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 == OPEN_BASE_FD + 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 == OPEN_BASE_FD + 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, 0);
  
    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, 0);
  #if 1
    /* Open r/w, create read-only */
  
    TEST_EXPECT_CONDITION(fd = open("toto2.txt", O_RDWR | O_CREAT, S_IRUSR),
                          RETVAL == OPEN_BASE_FD + 12);
  #endif
    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 == OPEN_BASE_FD + 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 == OPEN_BASE_FD + 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, 0);
    TEST_EXPECT_CONDITION(fd = open("shrd.txt", O_RDONLY),
                          RETVAL == OPEN_BASE_FD + 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, 0);
  
    /*
     * ioctl / fcntl
     */
  
    TEST_EXPECT_CONDITION(fcntl(fd, 2, 3), RETVAL < 0); /* Not supported
                                                           by FAT */
    TEST_EXPECT_CONDITION(ioctl(fd, 2, 3), RETVAL < 0); /* Not supported
                                                           by FAT */
  
    ls(".", 1, 0);
  
    /*
     * 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*/  
    /* Hard link not supported by FAT */
    TEST_EXPECT_CONDITION(link("toto4.txt", "toto5.txt"), RETVAL < 0); /*ENOSUP*/
    ls(".", 1, 0);
    TEST_EXPECT_CONDITION(chmod("toto4.txt", S_IRUSR | S_IWUSR), RETVAL == 0);
    ls(".", 1, 0);
  
    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); /* ENOENT */
    TEST_EXPECT_CONDITION(creat("toto4.txt", S_IRWXALL), RETVAL < 0); /*EEXIST*/
  
    ls(".", 1, 0);
  
    /* Change symlinks */
    /* Symlink not supported by FAT */
    TEST_EXPECT_CONDITION(symlink("toto4.txt", "toto5.txt"), RETVAL < 0); /*ENOSUP*/
    TEST_EXPECT_CONDITION(symlink("toto4.txt", "toto.txt"), RETVAL < 0); /*EEXIST*/
  
    ls(".", 1, 0);
  
    /* 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, 0);
  
    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, 0);
  
    TEST_EXPECT_CONDITION(mkdir("yoplait1", S_IRWXALL), RETVAL == 0);
    /* FAT not supported rename because SOS' rename implementation use link 
     * and link is not by FAT FS */
    TEST_EXPECT_CONDITION(rename("yoplait1", "mouf1"), RETVAL < 0); /*ENOSUP*/
    TEST_EXPECT_CONDITION(fd = open("yoplait1/a", O_RDWR | O_CREAT, S_IRWXALL),
                          RETVAL == OPEN_BASE_FD + 14);
    TEST_EXPECT_CONDITION(unlink("yoplait1/a"), RETVAL == 0);
    TEST_EXPECT_CONDITION(rmdir("yoplait1"), RETVAL == 0);
  
    TEST_EXPECT_CONDITION(mkdir("yoplait2", S_IRWXALL), RETVAL == 0);
    TEST_EXPECT_CONDITION(rename("yoplait2", "mouf2"), RETVAL < 0); /*ENOSUP*/
    TEST_EXPECT_CONDITION(fd = open("yoplait2/a", O_RDWR | O_CREAT, S_IRWXALL),
                          RETVAL == OPEN_BASE_FD + 15);
    TEST_EXPECT_CONDITION(rmdir("yoplait2"), RETVAL < 0);
  
    TEST_EXPECT_CONDITION(mkdir("yoplait3", S_IRWXALL), RETVAL == 0);
    TEST_EXPECT_CONDITION(rename("yoplait3", "mouf3"), RETVAL < 0); /*ENOSUP*/
    TEST_EXPECT_CONDITION(creat("yoplait3/a", S_IRWXALL), RETVAL == 0);
    TEST_EXPECT_CONDITION(unlink("yoplait3/a"), RETVAL == 0);
    TEST_EXPECT_CONDITION(rmdir("yoplait3"), RETVAL == 0);
  
    TEST_EXPECT_CONDITION(mkdir("yoplait4", S_IRWXALL), RETVAL == 0);
    TEST_EXPECT_CONDITION(rename("yoplait4", "mouf4"), RETVAL < 0); /*ENOSUP*/
    TEST_EXPECT_CONDITION(creat("yoplait4/a", S_IRWXALL), RETVAL == 0);
    TEST_EXPECT_CONDITION(rmdir("yoplait4"), RETVAL < 0);
  
    ls(".", 1, 0);
  
    /* Not supported by FAT */
    TEST_EXPECT_CONDITION(mknod("dev1", S_IRWXALL, S_IFCHR, 420, 268),
                          RETVAL < 0); /*ENOSUP*/
    /* Make sure the device cannot be opened (no device should be
       associated to it */
    TEST_EXPECT_CONDITION(open("dev1", O_RDONLY), RETVAL < 0); /*EEXIST*/
    TEST_EXPECT_CONDITION(unlink("dev1"), RETVAL < 0); /*EEXIST*/
  
    ls(".", 1, 0);
    ls("..", 1, 0);
    ls("../", 1, 0);
    ls("/..", 1, 0);
  
    /* 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, 0);
    ls("yo1", 1, 0);
  
    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 == OPEN_BASE_FD + 16);
  
    ls(".", 1, 0);
    ls("yo1", 1, 0);
  
    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, 0);
  
    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 == OPEN_BASE_FD + 17);
  
    ls(".", 1, 0);
  
    TEST_EXPECT_CONDITION(mkdir("yo4", S_IWUSR), RETVAL == 0);
    TEST_EXPECT_CONDITION(fd = open("yo4/titi1.txt", O_RDONLY), RETVAL < 0);
    /* FAT FS is always executable, then yo4 is S_IWUSR | S_IXUSR */
    TEST_EXPECT_CONDITION(fd = open("yo4/titi1.txt", O_RDONLY | O_CREAT,
                                    S_IRUSR), RETVAL == OPEN_BASE_FD + 18);
  
    ls(".", 1, 0);
  
    TEST_EXPECT_CONDITION(chdir("nowhere"), RETVAL < 0);
    TEST_EXPECT_CONDITION(chdir("yo1"), RETVAL == 0);
  
    ls(".", 1, 0);
    ls("/", 1, 0);
    ls("..", 1, 0);
    ls("../../../../", 1, 0);
    ls("/../../../../", 1, 0);
  
    /* Test chroot */
  
    TEST_EXPECT_CONDITION(chroot("nowhere"), RETVAL < 0);
    TEST_EXPECT_CONDITION(chroot("."), RETVAL == 0);
    ls(".", 1, 0);
    ls("/", 1, 0);
    ls("..", 1, 0);
    ls("../../../../", 1, 0);
    ls("/../../../../", 1, 0);
  
    /* 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, 0);
    ls("mnt", 1, 0);
    TEST_EXPECT_CONDITION(mount(NULL, "mnt", "virtfs", 0, NULL), RETVAL == 0);
    ls(".", 1, 0);
    ls("mnt", 1, 0);
    TEST_EXPECT_CONDITION(mkdir("mnt/dir_mnt", S_IRWXALL), RETVAL == 0);
    ls(".", 1, 0);
    ls("mnt", 1, 0);
  
    /* Make sure we cannot umount if the FS is in use */
    TEST_EXPECT_CONDITION(fd = open("mnt/dir_mnt", O_DIRECTORY),
                          RETVAL == OPEN_BASE_FD + 19);
    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, 0);
    ls("mnt", 1, 0);
    
    /* 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("dir_mnt", O_DIRECTORY),
                              RETVAL == OPEN_BASE_FD + 19);
        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, 0);
    ls("mnt", 1, 0);
    TEST_EXPECT_CONDITION(umount("mnt"), RETVAL == 0);
    ls(".", 1, 0);
    ls("mnt", 1, 0);
    TEST_EXPECT_CONDITION(umount("mnt"), RETVAL < 0);
  
    /*
     * Mountchain exploration
     */
    TEST_EXPECT_CONDITION(mkdir("mnt2", S_IRWXALL), RETVAL == 0);
    TEST_EXPECT_CONDITION(mkdir("mnt2/nothing", S_IRWXALL), RETVAL == 0);
    TEST_EXPECT_CONDITION(mount(NULL, "mnt2", "virtfs", 0, NULL), RETVAL == 0);
    TEST_EXPECT_CONDITION(mkdir("mnt2/mntpt-1", S_IRWXALL), RETVAL == 0);
    TEST_EXPECT_CONDITION(mount(NULL, "mnt2", "virtfs", 0, NULL), RETVAL == 0);
    TEST_EXPECT_CONDITION(mkdir("mnt2/mntpt-2", S_IRWXALL), RETVAL == 0);
    ls(".", 1, 0);
    ls("mnt2", 1, 0);
    TEST_EXPECT_CONDITION(umount("mnt2"), RETVAL == 0);
    ls(".", 1, 0);
    ls("mnt2", 1, 0);
    TEST_EXPECT_CONDITION(umount("mnt2"), RETVAL == 0);
    ls(".", 1, 0);
    ls("mnt2", 1, 0);
    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 == OPEN_BASE_FD + 19);
    ls(".", 1, 0);
    TEST_EXPECT_CONDITION(unlink("toto8.txt"), RETVAL == 0);
    ls(".", 1, 0);
  
    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, 0);
  
    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, 0);
  
    TEST_EXPECT_CONDITION(mkdir("plotch", S_IRWXALL), RETVAL == 0);
    TEST_EXPECT_CONDITION(fd = open("plotch/a", O_RDWR | O_CREAT, S_IRWXALL),
                          RETVAL == OPEN_BASE_FD + 20);
    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, 0);
  
    /* Invalid char space in the name */
    TEST_EXPECT_CONDITION(mkdir("this is ", S_IRWXALL), RETVAL < 0); /*EINVAL*/
    /* Name too long */
    TEST_EXPECT_CONDITION(mkdir("nametoolongwithoutspacechar", S_IRWXALL), RETVAL < 0); /*ENAMETOOLONG*/
    /* Invalid char space in the name and name too long */
    TEST_EXPECT_CONDITION(mkdir("this is / a long path", S_IRWXALL), RETVAL < 0); /*ENAMETOOLONG*/
    /* Invalid char space in the name and name too long */
    TEST_EXPECT_CONDITION(rmdir("this is / a long path"), RETVAL < 0); /*EEXIST*/
    /* Name too long */
    TEST_EXPECT_CONDITION(rmdir("nametoolongwithoutspacechar"), RETVAL < 0); /*EEXIST*/
    /* Invalid char space in the name */
    TEST_EXPECT_CONDITION(rmdir("this is "), RETVAL < 0); /*EEXIST*/
    ls(".", 1, 0);
  
    /*
     * Unlink/link files while they are in use
     */
  
    TEST_EXPECT_CONDITION(fd = open("toto8.txt", O_RDWR | O_CREAT,
                                    S_IRUSR | S_IWUSR),
                          RETVAL == OPEN_BASE_FD + 20);
    ls(".", 1, 0);
    TEST_EXPECT_CONDITION(link("toto8.txt", "toto9.txt"), RETVAL < 0); /*ENOSUP*/
    TEST_EXPECT_CONDITION(unlink("toto8.txt"), RETVAL == 0);
    ls(".", 1, 0);
  
    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);
    ls(".", 1, 0);
  
    /*
     * Rename files while they are in use
     */
  
    TEST_EXPECT_CONDITION(fd = open("toto8.txt", O_RDWR | O_CREAT,
                                    S_IRUSR | S_IWUSR),
                          RETVAL == OPEN_BASE_FD + 21);
    ls(".", 1, 0);
    TEST_EXPECT_CONDITION(rename("toto8.txt", "toto9.txt"), RETVAL < 0); /*ENOSUP*/
    ls(".", 1, 0);
  
    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 == OPEN_BASE_FD + 21);
    TEST_EXPECT_CONDITION(close(fd), RETVAL == 0);
  
    /* Rename */
    ls(".", 1, 0);
    TEST_EXPECT_CONDITION(rename("/mnt/subdir0", "subdir42"), RETVAL < 0); /*ENOSUP*/
    ls(".", 1, 0);
    TEST_EXPECT_CONDITION(rename("titi1.txt", "subdir42"), RETVAL < 0); /*ENOSUP*/
    ls(".", 1, 0);
    TEST_EXPECT_CONDITION(rename("titi1.txt", "subdir42/titi.txt"), RETVAL < 0); /*ENOSUP*/
  
    /*
     * 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 == OPEN_BASE_FD + 21);
    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, 0);
    TEST_EXPECT_CONDITION(unlink("mmap.txt"), RETVAL == 0);
    ls(".", 1, 0);
  
    /* 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 == OPEN_BASE_FD + 22);
    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, 0);
  
    /* Basic use */
    TEST_EXPECT_CONDITION(creat("mmap.txt", S_IRUSR | S_IWUSR), RETVAL == 0);
    if (fork() == 0)
      {
        char *shrd;
        TEST_EXPECT_CONDITION(fd = open("mmap.txt", O_RDWR | O_CREAT,
                                        S_IRUSR | S_IWUSR),
                              RETVAL == OPEN_BASE_FD + 23);
        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 == OPEN_BASE_FD + 23);
        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, 0);
  
    bochs_printf("Bye from fstest FAT\n");
    ls(".", 1, 0);
  
    return 0;
  }
  
 

sos-code-article9.5/userland/ldscript.lds (2006-01-22 03:48:32.000000000 -0800 )
sos-code-article10/userland/ldscript.lds (2006-12-20 13:59:58.000000000 -0800 )
Line 56 
Line 56 
     }     }
  
     /* Beginning of the BSS section (global uninitialized data) */     /* Beginning of the BSS section (global uninitialized data) */
     .bss SIZEOF(.rodata) + ADDR(.rodata) :     .bss ALIGN(SIZEOF(.rodata) + ADDR(.rodata), 4096) :
         PROVIDE(_bbss = .);         PROVIDE(_bbss = .);
         *(.bss)         *(.bss)
  
 

sos-code-article9.5/userland/libc.c (2006-01-22 03:48:32.000000000 -0800 )
sos-code-article10/userland/libc.c (2006-12-20 13:59:58.000000000 -0800 )
Line 34 
Line 34 
 } }
  
  
 int exec(const char *progname) pid_t getpid()
   return _sos_exec(progname);   return _sos_getpid();
  }
  
  
  /**
   * Helper to transform the array of argv and envp into an area
   * suitable for the crt.c:unmarshall_argv_envp() function.
   * @see crt.c for the format of this array.
   */
  static void const* marshall_execve_parameters(char* const argv[],
                                                char* const envp[],
                                               /*out*/size_t * sz)
  {
    size_t i, i_offset, count_argv, count_envp, size;
    char * str;
    void * result;
    addr_t * offset;
  
    *sz = 0;
  
    /* Count the number of argv parameters and the size to allocate */
    for (count_argv = 0, size = 0 ; argv && argv[count_argv] != NULL ; count_argv ++)
      size += strlen(argv[count_argv]) + 1 /* trailing \0 */;
  
    /* Count the number of envp parameters and the size to allocate */
    for (count_envp = 0 ; envp && envp[count_envp] != NULL ; count_envp ++)
      size += strlen(envp[count_envp]) + 1 /* trailing \0 */;
  
    size += (count_argv + count_envp + 3) * sizeof(addr_t);
    result = malloc(size);
    if (NULL == result)
      return NULL;
  
    /* Build the array of offsets and the string contents */
    offset   = (addr_t*)result;
    str      = (char*) & offset[count_argv + count_envp + 3];
    i_offset = 0;
  
    /* First element of the array is the number of argv entries */
    offset [i_offset++] = count_argv;
  
    /* Marshall the argv array */
    for (i = 0 ; i < count_argv ; i ++, i_offset ++)
      {
        char const* c;
  
        offset[i_offset] = (void*)str - result;
  
        for (c = argv[i] ; *c ; c ++, str ++)
          *str = *c;
        *str = '\0'; str ++;
      }
  
    /* The NULL between argv and envp */
    offset [i_offset++] = 0;
  
    for (i = 0 ; i < count_envp ; i ++, i_offset ++)
      {
        char const* c;
  
        offset[i_offset] = (void*)str - result;
  
        for (c = envp[i] ; *c ; c ++, str ++)
          *str = *c;
        *str = '\0'; str ++;
      }
  
    /* Final NULL */
    offset[count_argv + count_envp + 2] = 0;
    *sz = size;
  
    return result;
  }
  
  
  int execve(const  char  *filename,
             char  *const argv [],
             char *const envp[])
  {
    void const* args_area;
    size_t args_sz;
    int retval;
  
    args_area = marshall_execve_parameters(argv, envp, & args_sz);
  
    retval = _sos_exec(filename,
                       args_area, args_sz);
  
    if (NULL != args_area)
      free((void*)args_area);
  
    return retval;
  }
  
  
  int execv(const char  * filename,
            char  * const argv [])
  {
    return execve(filename, argv, environ);
  }
  
  
  static int compute_nargs(va_list ap)
  {
    int nargs = 0;
    while (va_arg(ap, char*))
      nargs ++;
    return nargs;
  }
  
  
  static int vexec(const  char  *path, va_list ap, int envp_in_list)
  {
    int retval;
    char ** vector, **entry;
    char *str;
    char ** envp;
  
    size_t nargs = compute_nargs(ap);
  
    /* Allocate the char* vector */
    vector = malloc((nargs + 1)*sizeof(char*));
    if (! vector)
      return -1;
  
    /* Fill it */
    entry = vector;
    while ((str = va_arg(ap, char*)) != NULL)
      {
        *entry = str;
        entry ++;
      }
    *entry = NULL;
  
    if (envp_in_list)
      envp = (char**)va_arg(ap, char*);
    else
      envp = environ;
  
    retval = execve(path, vector, envp);
    free(vector);
    return retval;
  }
  
  
  int  execl(const  char  *path, ...)
  {
    int retval;
    va_list ap;
    va_start(ap, path);
    retval = vexec(path, ap, FALSE);
    va_end(ap);
    return retval;
  }
  
  
  int  execle(const  char  *path, ...)
  {
    int retval;
    va_list ap;
    va_start(ap, path);
    retval = vexec(path, ap, TRUE);
    va_end(ap);
    return retval;
  }
  
  
  int exec(char  * const filename)
  {
    return execl(filename, filename, NULL);
  
  
Line 53 
Line 222 
 } }
  
  
  /**
   * Max number of environment variables
   */
  #define SOS_MAX_ENVVARS 1024
  
  char **environ = NULL;
  
  
  /**
   * Compare the keys of two strings of the form "key=val"
   */
  static int equal_key(const char *e1,
                       const char *e2)
  {
    for ( ; e1 && (*e1) && e2 && (*e2) && (*e1 == *e2) ; e1++, e2++)
      if (*e1 == '=')
        return TRUE;
  
    return FALSE;
  }
  
  
  /**
   * Helper function to register an environment variable
   */
  static int registerenv(char * string, int can_overwrite)
  {
    int i;
    char ** overwritten;
  
    /* The first time: allocate the environ table */
    if (! environ)
      {
        environ = malloc(SOS_MAX_ENVVARS * sizeof(char*));
        if (! environ)
          return 0;
  
        memset(environ, 0x0, SOS_MAX_ENVVARS * sizeof(char*));
      }
  
    /* Walk the environment variables */
    overwritten = NULL;
    for (i = 0 ; i < SOS_MAX_ENVVARS ; i ++)
      {
        /* Reached end of list ? */
        if (! environ[i])
          break;
  
        /* Variable already present ? */
        if (equal_key(environ[i], string))
          overwritten = & environ[i];
      }
  
    if (overwritten)
      {
        /* Not allowed to overwrite it ! */
        if (! can_overwrite)
          return -1;
  
        /* Overwrite allowed: do it now */
        free(*overwritten);
        *overwritten = string;
        return 0;
      }
  
    /* Must add the variable */
  
    /* No place left ? */
    if (i >= SOS_MAX_ENVVARS)
      return -1;
  
    /* Ok, add it at the end */
    environ[i] = string;
    return 0;
  }
  
  
  /**
   * @return TRUE when the key of the "keyvalpair" pair is "key"
   */
  static int key_is(char * keyvalpair, const char * key,
                    char ** /*out*/value)
  {
    for ( ; keyvalpair && *keyvalpair && key && *key ; keyvalpair ++, key ++)
      if (*key != *keyvalpair)
        break;
  
    if (value)
      *value = keyvalpair + 1;
  
    return (keyvalpair && (*keyvalpair == '=') && key && (*key == '\0'));
  }
  
  
  int putenv(char * string)
  {
    char * str;
    if (! string)
      return -1;
  
    str = strdup(string);
    if (! str)
      return -1;
  
    return registerenv(string, TRUE);
  }
  
  
  int setenv(const char *name, const char *value, int overwrite)
  {
    char * str, * c;
    size_t sz_name, sz_value;
  
    if (!name || !value)
      return -1;
  
    /* Allocate space for the "name=value" string */
    sz_name = strlen(name);
    sz_value = strlen(value);
    str = malloc(sz_name + 1 + sz_value + 1);
    if (! str)
      return -1;
  
    /* sprintf(str, "%s=%s", name, value); */
    c = str;
    *c = '\0';
    strzcpy(c, name, sz_name+1); c += sz_name;
    strzcpy(c, "=", 2); c += 1;
    strzcpy(c, value, sz_value+1);
  
    return registerenv(str, overwrite);
  }
  
  
  char *getenv(const char *name)
  {
    int i;
  
    if (! environ)
      return NULL;
  
    for (i = 0 ; i < SOS_MAX_ENVVARS ; i ++)
      {
        char *value;
  
        /* Reached end of list ? */
        if (! environ[i])
          return NULL;
  
        /* Variable already present ? */
        if (key_is(environ[i], name, & value))
          return value;
      }
  
    return NULL;
  }
  
  
  void unsetenv(const char *name)
  {
    int i;
    char ** entry, ** last_entry;
  
    if (! environ)
      return;
  
    /* Walk the environment variables */
    entry = last_entry = NULL;
    for (i = 0 ; i < SOS_MAX_ENVVARS ; i ++)
      {
        /* Reached end of list ? */
        if (! environ[i])
          break;
  
        /* Variable already present ? */
        if (key_is(environ[i], name, NULL))
          entry = & environ[i];
        else if (entry)
          last_entry = & environ[i];
      }
  
    /* Found nothing ! */
    if (! entry)
      return;
  
    /* Found it: erase it... */
    free(*entry);
    *entry = NULL;
  
    /* ... and replace it with the last entry */
    if (last_entry)
      {
        *entry = *last_entry;
        *last_entry = NULL;
      }
  }
  
  
  int clearenv(void)
  {
    int i;
  
    if (! environ)
      return 0;
  
    for (i = 0 ; i < SOS_MAX_ENVVARS ; i ++)
      {
        if (! environ[i])
          break;
  
        free(environ[i]);
        environ[i] = NULL;
      }
  
    return 0;
  }
  
  
  
 int munmap(void * start, size_t length) int munmap(void * start, size_t length)
 { {
  
 

sos-code-article9.5/userland/libc.h (2006-01-22 03:48:32.000000000 -0800 )
sos-code-article10/userland/libc.h (2006-12-20 13:59:58.000000000 -0800 )
Line 40 
Line 40 
  
  
 /** /**
   * Retrieves the current process's PID
   */
  pid_t getpid(void);
  
  
  /**
  * 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); int execve(const  char  *filename,
             char  *const argv [],
             char *const envp[]);
  int execv(const char  * filename,
            char  * const argv []);
  int execl(const  char  *path, ...);
  int execle(const  char  *path, ...);
  
  
  /**
   * Equivalent to execl(filename, filename);
   */
  int exec(char * const filename);
  
 int nanosleep(unsigned long int sec, int nanosleep(unsigned long int sec,
Line 54 
Line 72 
  
  
 /** /**
   * Environment variables management API
   */
  extern char **environ; /** Needed by execl */
  
  int putenv(char * string);
  int setenv(const char *name, const char *value, int overwrite);
  char *getenv(const char *name);
  void unsetenv(const char *name);
  int clearenv(void);
  
  
  /**
  * These flags (for mmap API) MUST be consistent with those defined in  * These flags (for mmap API) MUST be consistent with those defined in
  * paging.h and umem_vmm.h  * paging.h and umem_vmm.h
  */  */
  
 

sos-code-article9.5/userland/Makefile (2006-01-22 03:48:31.000000000 -0800 )
sos-code-article10/userland/Makefile (2007-04-18 11:23:21.000000000 -0700 )
Line 30 
Line 30 
 -include .mkvars -include .mkvars
  
 PROGS := init myprog1 myprog2 myprog3 myprog4 myprog5 myprog6 \ PROGS := init myprog1 myprog2 myprog3 myprog4 myprog5 myprog6 \
          myprog7 myprog8 myprog9 myprog10 myprog11 myprog12   \          envtest myprog7 myprog8 myprog9 myprog10 myprog11 myprog12     \
          myprog13 myprog14 banner fstest chartest blktest shell          myprog13 myprog14 banner fstest fstestfat chartest blktest shell
 # Build dependencies of the programs # Build dependencies of the programs
 init: fstest_utils.o init: fstest_utils.o
 fstest: fstest_utils.o fstest: fstest_utils.o
  fstestfat: fstest_utils.o
 chartest: fstest_utils.o chartest: fstest_utils.o
 blktest: fstest_utils.o blktest: fstest_utils.o
 shell: fstest_utils.o shell: fstest_utils.o
 $(PROGS) : % : %.o crt.o libc.a $(PROGS) : % : %.o crt.o crt_asm.o libc.a
 PWD := $(shell pwd | sed 's/"/\\\"/g;s/\$$/\\\$$/g') PWD := $(shell pwd | sed 's/"/\\\"/g;s/\$$/\\\$$/g')
  
  
 

sos-code-article9.5/userland/shell.c (2006-01-22 03:48:31.000000000 -0800 )
sos-code-article10/userland/shell.c (2007-07-19 13:19:15.000000000 -0700 )
Line 33 
Line 33 
  
 static int shell_uname (int argc, char *argv[]) static int shell_uname (int argc, char *argv[])
 { {
   printf ("SOS article 9.5\n");   printf ("SOS article 10\n");
 } }
  
Line 157 
Line 157 
   close(fd_here);   close(fd_here);
 } }
  
  static int shell_chdir(int argc, char *argv[])
  {
    int ret;
  
    ret = chdir(argv[1]);
  
    if (ret < 0)
      printf("Cannot chdir '%s': %d\n", argv[1], ret);
  
    return 0;
  }
  
 static int shell_touch (int argc, char *argv[]) static int shell_touch (int argc, char *argv[])
 { {
   return creat (argv[1], S_IRUSR | S_IWUSR);   return creat (argv[1], S_IRUSR | S_IWUSR);
Line 164 
Line 176 
  
 static int shell_mkdir (int argc, char *argv[]) static int shell_mkdir (int argc, char *argv[])
 { {
   return mkdir (argv[1], S_IRUSR | S_IWUSR | S_IXUSR);   int retval;
  
    retval = mkdir (argv[1], S_IRUSR | S_IWUSR | S_IXUSR);
  
    if (retval != 0)
      printf("Cannot mkdir %s (%d)\n", argv[1], retval);
    return retval;
  }
  
  static int shell_rmdir (int argc, char *argv[])
  {
    int ret;
  
    ret = rmdir (argv[1]);
  
    if (ret < 0)
      printf("Cannot rmdir '%s': %d\n", argv[1], ret);
  
    return 0;
  
 static int shell_cat (int argc, char *argv[]) static int shell_cat (int argc, char *argv[])
Line 303 
Line 333 
       printf("Could not fork (%d)\n", retval);       printf("Could not fork (%d)\n", retval);
       return -1;       return -1;
     }     }
   retval = exec(argv[1]);   printf("Created new process %d\n", getpid());
    retval = execv(argv[1], & argv[1]);
    exit(0);
   return 0;   return 0;
 } }
  
Line 597 
Line 629 
   return 0;   return 0;
 } }
  
  static int shell_mount (int argc, char *argv[])
  {
    /*char * end = NULL;*/
    int retval = 0;
    int flags = 0;
  
    if (strncmp(argv[1], "-t", 2)) {
      /*flags = strtol(argv[1], &end, 10);*/
      if (argc == 8) {
          retval = mount(argv[4], argv[5], argv[3], flags, argv[7]);
      } else {
          if (argc == 6) {
              retval = mount(argv[4], argv[5], argv[3], flags, NULL);
          } else {
              printf("mount [FLAGS] -t TYPE DEVICE MOUNT_DIR [-o ARGS]\n");
          }
      }
    } else {
      if (argc == 7) {
          retval = mount(argv[3], argv[4], argv[2], 0, argv[6]);
      } else {
          if (argc == 5) {
              retval = mount(argv[3], argv[4], argv[2], 0, NULL);
          } else {
              printf("mount [FLAGS] -t TYPE DEVICE MOUNT_DIR [-o ARGS]\n");
          }
      }
    }
  
    if (retval != 0)
      printf("Cannot mount %s (%d)\n", argv[2], retval);
    return retval;
  }
  
  static int shell_umount (int argc, char *argv[])
  {
    int retval = 0;
  
    if (argc == 2)
      retval = umount(argv[1]);
    else
      printf("umount DIR\n");
  
    if (retval != 0)
      printf("Cannot umount %s (%d)\n", argv[1], retval);
    return retval;
  }
  
  static int shell_mmap (int argc, char *argv[])
  {
    /*
     * test mmap
     */
    int fd, ret;
  
    /* Common use: shared file suppressed as soon as possible */
    fd = open("mmap.txt", O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
  
    if (fork() == 0)
      {
        char *shrd;
        shrd = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 4096);
        if (shrd == NULL)
          bochs_printf("shrd children error\n");
  
        nanosleep(1, 0);
        strzcpy(shrd, "Hello1 from the child (shared mapping) !", 4096);
        exit(0);
      }
    else
      {
        char *shrd;
        shrd = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 4096);
        if (shrd == NULL)
          bochs_printf("shrd father error\n");
  
        strzcpy(shrd, "Garbage garbage garbage", 256);
        nanosleep(2, 0);
        bochs_printf("Father woken up\n");
        bochs_printf("Read string from child: '%s'\n", shrd);
        if (strcmp(shrd, "Hello1 from the child (shared mapping) !") == 0)
          bochs_printf("TEST1 OK\n");
        munmap(shrd, 8192);
      }
    ret = close(fd);
    if (ret == 0)
      bochs_printf("close OK\n");
    ret = unlink("mmap.txt");
    if (ret == 0)
      bochs_printf("delete mmap.txt OK\n");
  
    shell_ls_internal(1, 0, 1);
  
    /* Common use: shared file suppressed as soon as possible */
    fd = open("mmap.txt", O_RDWR | O_CREAT,
                                    S_IRUSR | S_IWUSR);
    ret = unlink("mmap.txt");
    if (ret == 0)
      bochs_printf("delete mmap.txt OK 2\n");
    if (fork() == 0)
      {
        char *shrd;
        shrd = mmap(NULL, 4096, PROT_READ | PROT_WRITE,
                                          MAP_SHARED, fd, 4096);
        if (shrd == NULL)
          bochs_printf("shrd children error\n");
  
        nanosleep(1, 0);
        strzcpy(shrd, "Hello2 from the child (shared mapping) !", 4096);
        exit(0);
      }
    else
      {
        char *shrd;
        shrd = mmap(NULL, 4096, PROT_READ | PROT_WRITE,
                                          MAP_SHARED, fd, 4096);
        if (shrd == NULL)
          bochs_printf("shrd father error\n");
  
        strzcpy(shrd, "Garbage garbage garbage", 256);
        nanosleep(2, 0);
        bochs_printf("Father woken up\n");
        bochs_printf("Read string from child: '%s'\n", shrd);
        if (strcmp(shrd, "Hello2 from the child (shared mapping) !") == 0)
          bochs_printf("Test 2 OK\n");
        /*msync(shrd, 4096, MAP_SHARED);*/
        munmap(shrd, 8192);
      }
    ret = close(fd);
    if (ret == 0)
      bochs_printf("close OK\n");
  
    shell_ls_internal(1, 0, 1);
  
    /* Basic use */
    ret = creat("mmap.txt", S_IRUSR | S_IWUSR);
    if (ret == 0)
      bochs_printf("creat OK\n");
    if (fork() == 0)
      {
        char *shrd;
        fd = open("mmap.txt", O_RDWR | O_CREAT,
                                        S_IRUSR | S_IWUSR);
  
        shrd = mmap(NULL, 4096, PROT_READ | PROT_WRITE,
                                          MAP_SHARED, fd, 4096);
        if (shrd == NULL)
          bochs_printf("shrd children error\n");
        nanosleep(1, 0);
        strzcpy(shrd, "Hello3 from the child (shared mapping) !", 4096);
        exit(0);
      }
    else
      {
        char *shrd;
        fd = open("mmap.txt", O_RDWR | O_CREAT,
                                        S_IRUSR | S_IWUSR);
  
        shrd = mmap(NULL, 4096, PROT_READ | PROT_WRITE,
                                          MAP_SHARED, fd, 4096);
        if (shrd == NULL)
          bochs_printf("shrd father error\n");
  
        strzcpy(shrd, "Garbage garbage garbage", 256);
        nanosleep(2, 0);
        bochs_printf("Father woken up\n");
        bochs_printf("Read string from child: '%s'\n", shrd);
        if (strcmp(shrd, "Hello3 from the child (shared mapping) !") == 0)
          bochs_printf("TEST3 OK\n");
          
        munmap(shrd, 8192);
        ret = close(fd);
        if (ret == 0)
          bochs_printf("close OK\n");
      }
    shell_ls_internal(1, 0, 1);
  
      return 0;
  }
  
 void command_exec (char * cmd) void command_exec (char * cmd)
 { {
Line 605 
Line 816 
   char **argv;   char **argv;
  
   for (c = cmd; *c != '\0'; c++)   for (c = cmd; *c != '\0'; c++)
      {
     if (*c == ' ')     if (*c == ' ')
          {
            /* Skip all spaces */
            while (*c && *c == ' ')
              c++;
  
            /* Reached the end of the command line ? */
            if (! *c)
              break;
  
       argc++;       argc++;
          }
      }
  
   argv = malloc (argc * sizeof(char*));   argv = malloc ((argc+1) * sizeof(char*));
    argv[argc] = NULL; /* To be compatible with execv(e) */
     return;     return;
  
Line 619 
Line 843 
         c++;         c++;
       *c = '\0';       *c = '\0';
       c++;       c++;
        /* Skip spaces */
        while (*c && *c == ' ')
          c++;
     }     }
  
   if (! strcmp (argv[0], "uname"))   if (! strcmp (argv[0], "uname"))
Line 632 
Line 859 
         shell_ls (".", 1, 0);         shell_ls (".", 1, 0);
     }     }
  
    else if (! strcmp (argv[0], "cd"))
      {
        shell_chdir(argc, argv);
      }
  
   else if (! strcmp (argv[0], "touch"))   else if (! strcmp (argv[0], "touch"))
     {     {
       shell_touch (argc, argv);       shell_touch (argc, argv);
Line 642 
Line 874 
       shell_mkdir (argc, argv);       shell_mkdir (argc, argv);
     }     }
  
    else if (! strcmp (argv[0], "rmdir"))
      {
        shell_rmdir (argc, argv);
      }
  
   else if (! strcmp (argv[0], "cat"))   else if (! strcmp (argv[0], "cat"))
     {     {
       shell_cat (argc, argv);       shell_cat (argc, argv);
Line 676 
Line 913 
     {     {
       shell_partdump (argc, argv);       shell_partdump (argc, argv);
     }     }
    else if (! strcmp (argv[0], "mount"))
      {
        shell_mount (argc, argv);
      }
    else if (! strcmp (argv[0], "umount"))
      {
        shell_umount (argc, argv);
      }
    else if (! strcmp (argv[0], "mmap"))
      {
        shell_mmap (argc, argv);
      }
    else if (! strcmp(argv[0], "getenv"))
      {
        if (argc > 1)
          printf("%s\n", getenv(argv[1]));
        else
          printf("Variable name missing\n");
      }
    else if (! strcmp(argv[0], "setenv"))
      {
        if (argc > 2)
          printf("retval=%d\n",
                 setenv(argv[1], argv[2], TRUE));
        else
          printf("Variable name/value missing\n");
      }
    else if (! strcmp(argv[0], "unsetenv"))
      {
        if (argc > 1)
          unsetenv(argv[1]);
        else
          printf("Variable name missing\n");
      }
     printf ("Command not found\n");     printf ("Command not found\n");
  
  
 

sos-code-article9.5/userland/string.c (2006-01-22 03:48:32.000000000 -0800 )
sos-code-article10/userland/string.c (2006-12-20 13:59:58.000000000 -0800 )
Line 18 
Line 18 
    USA.     USA. 
 */ */
 #include "string.h" #include "string.h"
  #include "libc.h"
  
 /* For an optimized version, see BSD sources ;) */ /* For an optimized version, see BSD sources ;) */
 void *memcpy(void *dst0, const void *src0, register unsigned int size) void *memcpy(void *dst0, const void *src0, register unsigned int size)
Line 115 
Line 116 
   return res;   return res;
 } }
  
  
  char *strndup(const char *s, size_t n)
  {
    char * result;
    n = strnlen(s, n);
    result = malloc(n+1);
    if (! result)
      return NULL;
  
    strzcpy(result, s, n+1);
    return result;
  }
  
  
  char *strdup(const char *s)
  {
    char * result;
    size_t n = strlen(s);
  
    result = malloc(n+1);
    if (! result)
      return NULL;
  
    strzcpy(result, s, n+1);
    return result;
  }
  
  
 int strcmp(register const char *s1, register const char *s2) int strcmp(register const char *s1, register const char *s2)
 { {
   while (*s1 == *s2++)   while (*s1 == *s2++)
Line 208 
Line 237 
 { {
   return strtol(nptr, NULL, 10);   return strtol(nptr, NULL, 10);
 } }
  
  
 

sos-code-article9.5/userland/string.h (2006-01-22 03:48:32.000000000 -0800 )
sos-code-article10/userland/string.h (2006-12-20 13:59:58.000000000 -0800 )
Line 54 
Line 54 
 char *strzcat (char *dest, const char *src, char *strzcat (char *dest, const char *src,
                const size_t len);                const size_t len);
    
  char *strndup(const char *s, size_t n);
  char *strdup(const char *s);
   
 int strcmp(register const char *s1, register const char *s2 ); int strcmp(register const char *s1, register const char *s2 );
 int strncmp(register const char *s1, register const char *s2, int strncmp(register const char *s1, register const char *s2,
             register int len );             register int len );
  
  
 #define isupper(c)  (('A' <= (c)) && ((c) <= 'Z')) #define isupper(c)  (('A' <= (c)) && ((c) <= 'Z'))
 #define isdigit(c)  (('0' <= (c)) && ((c) <= '9')) #define isdigit(c)  (('0' <= (c)) && ((c) <= '9'))
Line 72 
Line 74 
 long long atoll(const char *nptr); long long atoll(const char *nptr);
 long atol(const char *nptr); long atol(const char *nptr);
  
  
  
 

sos-code-article9.5/userland/types.h (2006-01-22 03:48:32.000000000 -0800 )
sos-code-article10/userland/types.h (2006-12-20 13:59:58.000000000 -0800 )
Line 25 
Line 25 
  
 typedef unsigned int size_t; typedef unsigned int size_t;
  
 typedef int pid_t; typedef unsigned int addr_t;
  typedef int pid_t;
 typedef signed long int off_t; typedef signed long int off_t;
 typedef signed long long int loff_t; typedef signed long long int loff_t;
  


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

File list:
    
sos-code-article10/drivers/fs_fat.c
    sos-code-article10/drivers/fs_fat.h
    sos-code-article10/drivers/mem.c
    sos-code-article10/extra/bootsect.S
    sos-code-article10/hwcore/bitmap.c
    sos-code-article10/hwcore/bitmap.h
    sos-code-article10/hwcore/paging.c
    sos-code-article10/hwcore/paging.h
    sos-code-article10/Makefile
    sos-code-article10/sos/binfmt_elf32.c
    sos-code-article10/sos/blkdev.c
    sos-code-article10/sos/errno.h
    sos-code-article10/sos/fs.c
    sos-code-article10/sos/fs.h
    sos-code-article10/sos/hash.c
    sos-code-article10/sos/hash.h
    sos-code-article10/sos/klibc.h
    sos-code-article10/sos/kmem_vmm.h
    sos-code-article10/sos/main.c
    sos-code-article10/sos/process.c
    sos-code-article10/sos/process.h
    sos-code-article10/sos/syscall.c
    sos-code-article10/sos/syscall.h
    sos-code-article10/sos/thread.c
    sos-code-article10/sos/thread.h
    sos-code-article10/sos/types.h
    sos-code-article10/sos/uaccess.c
    sos-code-article10/sos/uaccess.h
    sos-code-article10/sos/umem_vmm.c
    sos-code-article10/sos/umem_vmm.h
    sos-code-article10/userland/crt_asm.S
    sos-code-article10/userland/crt.c
    sos-code-article10/userland/crt.h
    sos-code-article10/userland/envtest.c
    sos-code-article10/userland/fstest.c
    sos-code-article10/userland/fstestfat.c
    sos-code-article10/userland/ldscript.lds
    sos-code-article10/userland/libc.c
    sos-code-article10/userland/libc.h
    sos-code-article10/userland/Makefile
    sos-code-article10/userland/shell.c
    sos-code-article10/userland/string.c
    sos-code-article10/userland/string.h
    sos-code-article10/userland/types.h