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
) |
|
|
|
| /* 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, §or_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, §or_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, §or_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, §or_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, §or_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, §or_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, §or_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, §or_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, §or_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, |
| ¤t_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, |
| §or_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, §or_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)), |
| ¤t_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/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
) |
|
|
|
| /* 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; |
| } |
| |