Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:24:16

0001 /**
0002  * @file
0003  *
0004  * @ingroup libfs_ff Fat File
0005  *
0006  * @brief General operations on "fat-file"
0007  */
0008 
0009 /*
0010  * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia
0011  * Author: Eugeny S. Mints <Eugeny.Mints@oktet.ru>
0012  *
0013  */
0014 
0015 #define MSDOS_TRACE 1
0016 
0017 #ifdef HAVE_CONFIG_H
0018 #include "config.h"
0019 #endif
0020 
0021 #include <sys/types.h>
0022 #include <sys/stat.h>
0023 #include <fcntl.h>
0024 #include <unistd.h>
0025 #include <stdarg.h>
0026 #include <errno.h>
0027 #include <stdlib.h>
0028 #include <assert.h>
0029 #include <time.h>
0030 
0031 #include <rtems/libio_.h>
0032 
0033 #include "fat.h"
0034 #include "fat_fat_operations.h"
0035 #include "fat_file.h"
0036 
0037 static inline void
0038 _hash_insert(rtems_chain_control *hash, uint32_t   key1, uint32_t   key2,
0039              fat_file_fd_t *el);
0040 
0041 static inline void
0042 _hash_delete(rtems_chain_control *hash, uint32_t   key1, uint32_t   key2,
0043              fat_file_fd_t *el);
0044 
0045 static inline int
0046 _hash_search(
0047     const fat_fs_info_t                   *fs_info,
0048     rtems_chain_control                   *hash,
0049     uint32_t                               key1,
0050     uint32_t                               key2,
0051     fat_file_fd_t                              **ret
0052 );
0053 
0054 static off_t
0055 fat_file_lseek(
0056     fat_fs_info_t                         *fs_info,
0057     fat_file_fd_t                         *fat_fd,
0058     uint32_t                               file_cln,
0059     uint32_t                              *disk_cln
0060 );
0061 
0062 /* fat_file_open --
0063  *     Open fat-file. Two hash tables are accessed by key
0064  *     constructed from cluster num and offset of the node (i.e.
0065  *     files/directories are distinguished by location on the disk).
0066  *     First, hash table("vhash") consists of fat-file descriptors corresponded
0067  *     to "valid" files is accessed. Search is made by 2 fields equal to key
0068  *     constructed. If descriptor is found in the "vhash" - return it.
0069  *     Otherwise search is made in hash table("rhash") consits of fat-file
0070  *     descriptors corresponded to "removed-but-still-open" files with the
0071  *     same keys.
0072  *     If search failed, new fat-file descriptor is added to "vhash"
0073  *     with both key fields equal to constructed key. Otherwise new fat-file
0074  *     descriptor is added to "vhash" with first key field equal to key
0075  *     constructed and the second equal to an unique (unique among all values
0076  *     of second key fields) value.
0077  *
0078  * PARAMETERS:
0079  *     fs_info  - FS info
0080  *     pos      - cluster and offset of the node
0081  *     fat_fd   - placeholder for returned fat-file descriptor
0082  *
0083  * RETURNS:
0084  *     RC_OK and pointer to opened descriptor on success, or -1 if error
0085  *     occurred (errno set appropriately)
0086  */
0087 int
0088 fat_file_open(
0089     fat_fs_info_t                         *fs_info,
0090     fat_dir_pos_t                         *dir_pos,
0091     fat_file_fd_t                        **fat_fd
0092     )
0093 {
0094     int            rc = RC_OK;
0095     fat_file_fd_t *lfat_fd = NULL;
0096     uint32_t       key = 0;
0097 
0098     /* construct key */
0099     key = fat_construct_key(fs_info, &dir_pos->sname);
0100 
0101     /* access "valid" hash table */
0102     rc = _hash_search(fs_info, fs_info->vhash, key, 0, &lfat_fd);
0103     if ( rc == RC_OK )
0104     {
0105         /* return pointer to fat_file_descriptor allocated before */
0106         (*fat_fd) = lfat_fd;
0107         lfat_fd->links_num++;
0108         return rc;
0109     }
0110 
0111     /* access "removed-but-still-open" hash table */
0112     rc = _hash_search(fs_info, fs_info->rhash, key, key, &lfat_fd);
0113 
0114     lfat_fd = (*fat_fd) = (fat_file_fd_t*)calloc(1, sizeof(fat_file_fd_t));
0115     if ( lfat_fd == NULL )
0116         rtems_set_errno_and_return_minus_one( ENOMEM );
0117 
0118     lfat_fd->links_num = 1;
0119     lfat_fd->flags &= ~FAT_FILE_REMOVED;
0120     lfat_fd->map.last_cln = FAT_UNDEFINED_VALUE;
0121 
0122     lfat_fd->dir_pos = *dir_pos;
0123 
0124     if ( rc != RC_OK )
0125         lfat_fd->ino = key;
0126     else
0127     {
0128         lfat_fd->ino = fat_get_unique_ino(fs_info);
0129 
0130         if ( lfat_fd->ino == 0 )
0131         {
0132             free((*fat_fd));
0133             /*
0134              * XXX: kernel resource is unsufficient, but not the memory,
0135              * but there is no suitable errno :(
0136              */
0137             rtems_set_errno_and_return_minus_one( ENOMEM );
0138         }
0139     }
0140     _hash_insert(fs_info->vhash, key, lfat_fd->ino, lfat_fd);
0141 
0142     /*
0143      * other fields of fat-file descriptor will be initialized on upper
0144      * level
0145      */
0146 
0147     return RC_OK;
0148 }
0149 
0150 /* fat_file_get_new_inode_for --
0151  *    Get a new inode for the fat file descriptor that is being
0152  *    moved to the new directory position.
0153  *    Firstly, it release the old inode of the fat file descriptor
0154  *    taking also care to remove the mapping from the "vhash" table
0155  *    Then it obtain a new inode corresponging to the new directory position
0156  *    inserting the new mapping into the "vhash" table.
0157  *    The function takes the responsability to update the inode number and
0158  *    the directory position stored into the fat file descriptor.
0159  *
0160  * PARAMETERS:
0161  *     fs_info      - FS info
0162  *     new_dir_pos  - the new directory position for the fat file descriptor
0163  *     fat_fd       - the fat file descriptor for which the inode has to be changed
0164  *
0165  * RETURNS:
0166  *     RC_OK or an error
0167  */
0168 int
0169 fat_file_get_new_inode_for(
0170     fat_fs_info_t                         *fs_info,
0171     fat_dir_pos_t                         *new_dir_pos,
0172     fat_file_fd_t                         *fat_fd
0173     )
0174 {
0175     fat_file_fd_t *lfat_fd = NULL;
0176     uint32_t old_key = 0;
0177     uint32_t new_key = 0;
0178     uint32_t old_inode = 0;
0179     uint32_t new_inode = 0;
0180     int rc = RC_OK;
0181 
0182     /* construct the old key to later perform the due lookups */
0183     old_key = fat_construct_key(fs_info, &fat_fd->dir_pos.sname);
0184 
0185     /* construct the new key to later perform the due lookups */
0186     new_key = fat_construct_key(fs_info, &new_dir_pos->sname);
0187 
0188     /*
0189      * access "valid" hash table with the new key,
0190      * it is expected that no file can have such key, otherwise
0191      * it means that the caller is renaming a file to an already
0192      * existing destination.
0193      */
0194     rc = _hash_search(fs_info, fs_info->vhash, new_key, 0, &lfat_fd);
0195     assert(rc != RC_OK);
0196 
0197     /*
0198      * Remove from the valid table the old mapping of the inode to the
0199      * fat file descriptor
0200      */
0201     old_inode = fat_fd->ino;
0202     _hash_delete(fs_info->vhash, old_key, old_inode, fat_fd);
0203 
0204     /* Assign the new directory position to the fat file descriptor */
0205     fat_fd->dir_pos = *new_dir_pos;
0206 
0207     /*
0208      * The old inode is no more required,
0209      * Free it in case it has been allocated by fat_get_unique_ino
0210      */
0211     if (fat_ino_is_unique(fs_info, old_inode))
0212         fat_free_unique_ino(fs_info, old_inode);
0213 
0214     /*
0215      * Check if the new key can be used as an inode or a unique
0216      * inode should be obtained because the new directory position
0217      * may be a still opened but removed file
0218      */
0219     rc = _hash_search(fs_info, fs_info->rhash, new_key, new_key, &lfat_fd);
0220     if (rc != RC_OK) new_inode = new_key;
0221     else new_inode = fat_get_unique_ino(fs_info);
0222 
0223     /*
0224      * Finally, insert the new inode in the hash table lookup and
0225      * update the inode of the fat file descriptor.
0226      */
0227     _hash_insert(fs_info->vhash, new_key, new_inode, fat_fd);
0228     fat_fd->ino = new_inode;
0229     return RC_OK;
0230 }
0231 
0232 
0233 /* fat_file_reopen --
0234  *     Increment by 1 number of links
0235  *
0236  * PARAMETERS:
0237  *     fat_fd - fat-file descriptor
0238  *
0239  * RETURNS:
0240  *     RC_OK
0241  */
0242 int
0243 fat_file_reopen(fat_file_fd_t *fat_fd)
0244 {
0245     fat_fd->links_num++;
0246     return RC_OK;
0247 }
0248 
0249 int
0250 fat_file_update(fat_fs_info_t *fs_info, fat_file_fd_t *fat_fd)
0251 {
0252     int ret_rc = RC_OK;
0253 
0254     if (!FAT_FILE_IS_REMOVED(fat_fd) &&
0255         FAT_FILE_HAS_META_DATA_CHANGED(fat_fd) &&
0256         !FAT_FD_OF_ROOT_DIR(fat_fd))
0257     {
0258         int rc;
0259 
0260         rc = fat_file_write_first_cluster_num(fs_info, fat_fd);
0261         if (rc != RC_OK)
0262             ret_rc = rc;
0263 
0264         rc = fat_file_write_file_size(fs_info, fat_fd);
0265         if (rc != RC_OK)
0266             ret_rc = rc;
0267 
0268         rc = fat_file_write_time_and_date(fs_info, fat_fd);
0269         if (rc != RC_OK)
0270             ret_rc = rc;
0271     }
0272 
0273     return ret_rc;
0274 }
0275 
0276 /* fat_file_close --
0277  *     Close fat-file. If count of links to fat-file
0278  *     descriptor is greater than 1 (i.e. somebody esle holds pointer
0279  *     to this descriptor) just decrement it. Otherwise
0280  *     do the following. If this descriptor corresponded to removed fat-file
0281  *     then free clusters contained fat-file data, delete descriptor from
0282  *     "rhash" table and free memory allocated by descriptor. If descriptor
0283  *     correspondes to non-removed fat-file and 'ino' field has value from
0284  *     unique inode numbers pool then set count of links to descriptor to zero
0285  *     and leave it in hash, otherwise delete descriptor from "vhash" and free
0286  *     memory allocated by the descriptor
0287  *
0288  * PARAMETERS:
0289  *     fs_info  - FS info
0290  *     fat_fd   - fat-file descriptor
0291  *
0292  * RETURNS:
0293  *     RC_OK, or -1 if error occurred (errno set appropriately)
0294  */
0295 int
0296 fat_file_close(
0297     fat_fs_info_t                        *fs_info,
0298     fat_file_fd_t                        *fat_fd
0299     )
0300 {
0301     int            rc = RC_OK;
0302 
0303     /*
0304      * if links_num field of fat-file descriptor is greater than 1
0305      * decrement only the count of links
0306      */
0307     if (fat_fd->links_num > 1)
0308     {
0309         fat_fd->links_num--;
0310     }
0311     else
0312     {
0313         uint32_t key = fat_construct_key(fs_info, &fat_fd->dir_pos.sname);
0314 
0315         fat_file_update(fs_info, fat_fd);
0316 
0317         if (fat_fd->flags & FAT_FILE_REMOVED)
0318         {
0319             rc = fat_file_truncate(fs_info, fat_fd, 0);
0320             if (rc == RC_OK)
0321             {
0322                 _hash_delete(fs_info->rhash, key, fat_fd->ino, fat_fd);
0323 
0324                 if (fat_ino_is_unique(fs_info, fat_fd->ino))
0325                     fat_free_unique_ino(fs_info, fat_fd->ino);
0326 
0327                 free(fat_fd);
0328             }
0329         }
0330         else
0331         {
0332             if (fat_ino_is_unique(fs_info, fat_fd->ino))
0333             {
0334                 fat_fd->links_num = 0;
0335             }
0336             else
0337             {
0338                 _hash_delete(fs_info->vhash, key, fat_fd->ino, fat_fd);
0339                 free(fat_fd);
0340             }
0341         }
0342     }
0343 
0344     /*
0345      * flush any modified "cached" buffer back to disk
0346      */
0347     rc = fat_buf_release(fs_info);
0348 
0349     return rc;
0350 }
0351 
0352 /* fat_file_read --
0353  *     Read 'count' bytes from 'start' position from fat-file. This
0354  *     interface hides the architecture of fat-file, represents it as
0355  *     linear file
0356  *
0357  * PARAMETERS:
0358  *     fs_info  - FS info
0359  *     fat_fd   - fat-file descriptor
0360  *     start    - offset in fat-file (in bytes) to read from
0361  *     count    - count of bytes to read
0362  *     buf      - buffer provided by user
0363  *
0364  * RETURNS:
0365  *     the number of bytes read on success, or -1 if error occurred (errno
0366  *     set appropriately)
0367  */
0368 ssize_t
0369 fat_file_read(
0370     fat_fs_info_t                        *fs_info,
0371     fat_file_fd_t                        *fat_fd,
0372     uint32_t                              start,
0373     uint32_t                              count,
0374     uint8_t                              *buf
0375 )
0376 {
0377     int            rc = RC_OK;
0378     ssize_t        ret = 0;
0379     uint32_t       cmpltd = 0;
0380     uint32_t       cur_cln = 0;
0381     uint32_t       cl_start = 0;
0382     uint32_t       save_cln = 0;
0383     uint32_t       ofs = 0;
0384     uint32_t       save_ofs;
0385     uint32_t       sec = 0;
0386     uint32_t       sec_peek = 0;
0387     uint32_t       byte = 0;
0388     uint32_t       c = 0;
0389     uint32_t       blk = 0;
0390     uint32_t       blk_cnt = 0;
0391 
0392     /* it couldn't be removed - otherwise cache update will be broken */
0393     if (count == 0)
0394         return cmpltd;
0395 
0396     /*
0397      * >= because start is offset and computed from 0 and file_size
0398      * computed from 1
0399      */
0400     if ( start >= fat_fd->fat_file_size )
0401         return FAT_EOF;
0402 
0403     if ((count > fat_fd->fat_file_size) ||
0404         (start > fat_fd->fat_file_size - count))
0405         count = fat_fd->fat_file_size - start;
0406 
0407     if ((FAT_FD_OF_ROOT_DIR(fat_fd)) &&
0408         (fs_info->vol.type & (FAT_FAT12 | FAT_FAT16)))
0409     {
0410         sec = fat_cluster_num_to_sector_num(fs_info, fat_fd->cln);
0411         sec += (start >> fs_info->vol.sec_log2);
0412         byte = start & (fs_info->vol.bps - 1);
0413 
0414         ret = _fat_block_read(fs_info, sec, byte, count, buf);
0415         if ( ret < 0 )
0416             return -1;
0417 
0418         return ret;
0419     }
0420 
0421     cl_start = start >> fs_info->vol.bpc_log2;
0422     save_ofs = ofs = start & (fs_info->vol.bpc - 1);
0423 
0424     rc = fat_file_lseek(fs_info, fat_fd, cl_start, &cur_cln);
0425     if (rc != RC_OK)
0426         return rc;
0427 
0428     while (count > 0)
0429     {
0430         c = MIN(count, (fs_info->vol.bpc - ofs));
0431 
0432         sec = fat_cluster_num_to_sector_num(fs_info, cur_cln);
0433 
0434         save_cln = cur_cln;
0435         rc = fat_get_fat_cluster(fs_info, cur_cln, &cur_cln);
0436         if ( rc != RC_OK )
0437             return rc;
0438 
0439         sec_peek = fat_cluster_num_to_sector_num(fs_info, cur_cln);
0440         blk = fat_sector_num_to_block_num (fs_info, sec_peek);
0441         blk_cnt = fs_info->vol.bpc >> fs_info->vol.bytes_per_block_log2;
0442         if (blk_cnt == 0)
0443             blk_cnt = 1;
0444         fat_block_peek(fs_info, blk, blk_cnt);
0445 
0446         sec += (ofs >> fs_info->vol.sec_log2);
0447         byte = ofs & (fs_info->vol.bps - 1);
0448         ret = _fat_block_read(fs_info, sec, byte, c, buf + cmpltd);
0449         if ( ret < 0 )
0450             return -1;
0451 
0452         count -= c;
0453         cmpltd += c;
0454 
0455         ofs = 0;
0456     }
0457 
0458     /* update cache */
0459     /* XXX: check this - I'm not sure :( */
0460     fat_fd->map.file_cln = cl_start +
0461                            ((save_ofs + cmpltd - 1) >> fs_info->vol.bpc_log2);
0462     fat_fd->map.disk_cln = save_cln;
0463 
0464     return cmpltd;
0465 }
0466 
0467 /* fat_is_fat12_or_fat16_root_dir --
0468  *     Returns true for FAT12 root directories respectively FAT16
0469  *     root directories. Returns false for everything else.
0470  *
0471  *  PARAMETERS:
0472  *      fat_fd        - fat-file descriptor
0473  *      volume_type   - type of fat volume: FAT_FAT12 or FAT_FAT16 or FAT_FAT32
0474  *
0475  *  RETURNS:
0476  *      true if conditions for FAT12 root directory or FAT16 root directory
0477  *      match, false if not
0478  */
0479 static bool
0480  fat_is_fat12_or_fat16_root_dir (const fat_file_fd_t *fat_fd,
0481                                  const uint8_t volume_type)
0482 {
0483     return (FAT_FD_OF_ROOT_DIR(fat_fd)) && (volume_type & (FAT_FAT12 | FAT_FAT16));
0484 }
0485 
0486 /* fat_file_write_fat32_or_non_root_dir --
0487  *     Execute fat file write for FAT32 respectively for non-root
0488  *     directories of FAT12 or FAT16
0489  *
0490  * PARAMETERS:
0491  *     fs_info          - FS info
0492  *     fat_fd           - fat-file descriptor
0493  *     start            - offset(in bytes) to write from
0494  *     count            - count
0495  *     buf              - buffer provided by user
0496  *
0497  * RETURNS:
0498  *     number of bytes actually written to the file on success, or -1 if
0499  *     error occurred (errno set appropriately)
0500  */
0501 static ssize_t
0502 fat_file_write_fat32_or_non_root_dir(
0503      fat_fs_info_t                        *fs_info,
0504      fat_file_fd_t                        *fat_fd,
0505      const uint32_t                        start,
0506      const uint32_t                        count,
0507      const uint8_t                        *buf)
0508 {
0509     int            rc = RC_OK;
0510     uint32_t       cmpltd = 0;
0511     uint32_t       cur_cln = 0;
0512     uint32_t       save_cln = 0; /* FIXME: This might be incorrect, cf. below */
0513     uint32_t       start_cln = start >> fs_info->vol.bpc_log2;
0514     uint32_t       ofs_cln = start - (start_cln << fs_info->vol.bpc_log2);
0515     uint32_t       ofs_cln_save = ofs_cln;
0516     uint32_t       bytes_to_write = count;
0517     ssize_t        ret;
0518     uint32_t       c;
0519 
0520     rc = fat_file_lseek(fs_info, fat_fd, start_cln, &cur_cln);
0521     if (RC_OK == rc)
0522     {
0523         while (   (RC_OK == rc)
0524                && (bytes_to_write > 0))
0525         {
0526             c = MIN(bytes_to_write, (fs_info->vol.bpc - ofs_cln));
0527 
0528             ret = fat_cluster_write(fs_info,
0529                                       cur_cln,
0530                                       ofs_cln,
0531                                       c,
0532                                       &buf[cmpltd]);
0533             if (0 > ret)
0534               rc = -1;
0535 
0536             if (RC_OK == rc)
0537             {
0538                 bytes_to_write -= ret;
0539                 cmpltd += ret;
0540                 save_cln = cur_cln;
0541                 if (0 < bytes_to_write)
0542                   rc = fat_get_fat_cluster(fs_info, cur_cln, &cur_cln);
0543 
0544                 ofs_cln = 0;
0545             }
0546         }
0547 
0548         /* update cache */
0549         /* XXX: check this - I'm not sure :( */
0550         fat_fd->map.file_cln = start_cln +
0551                                ((ofs_cln_save + cmpltd - 1) >> fs_info->vol.bpc_log2);
0552         fat_fd->map.disk_cln = save_cln;
0553     }
0554 
0555     if (RC_OK != rc)
0556       return rc;
0557     else
0558       return cmpltd;
0559 }
0560 
0561 /* fat_file_write --
0562  *     Write 'count' bytes of data from user supplied buffer to fat-file
0563  *     starting at offset 'start'. This interface hides the architecture
0564  *     of fat-file, represents it as linear file
0565  *
0566  * PARAMETERS:
0567  *     fs_info  - FS info
0568  *     fat_fd   - fat-file descriptor
0569  *     start    - offset(in bytes) to write from
0570  *     count    - count
0571  *     buf      - buffer provided by user
0572  *
0573  * RETURNS:
0574  *     number of bytes actually written to the file on success, or -1 if
0575  *     error occurred (errno set appropriately)
0576  */
0577 ssize_t
0578 fat_file_write(
0579     fat_fs_info_t                        *fs_info,
0580     fat_file_fd_t                        *fat_fd,
0581     uint32_t                              start,
0582     uint32_t                              count,
0583     const uint8_t                        *buf
0584     )
0585 {
0586     int            rc = RC_OK;
0587     ssize_t        ret;
0588     uint32_t       cmpltd = 0;
0589     uint32_t       byte;
0590     uint32_t       c = 0;
0591     bool           zero_fill = start > fat_fd->fat_file_size;
0592     uint32_t       cln;
0593 
0594 
0595     if ( count == 0 )
0596         return cmpltd;
0597 
0598     if (start >= fat_fd->size_limit)
0599         rtems_set_errno_and_return_minus_one(EFBIG);
0600 
0601     if (count > fat_fd->size_limit - start)
0602         count = fat_fd->size_limit - start;
0603 
0604     rc = fat_file_extend(fs_info, fat_fd, zero_fill, start + count, &c);
0605     if (RC_OK == rc)
0606     {
0607         /*
0608          * check whether there was enough room on device to locate
0609          * file of 'start + count' bytes
0610          */
0611         if (c != (start + count))
0612             count = c - start;
0613 
0614         /* for the root directory of FAT12 and FAT16 we need this special handling */
0615         if (fat_is_fat12_or_fat16_root_dir(fat_fd, fs_info->vol.type))
0616         {
0617             cln = fat_fd->cln;
0618             cln += (start >> fs_info->vol.bpc_log2);
0619             byte = start & (fs_info->vol.bpc -1);
0620 
0621             ret = fat_cluster_write(fs_info,
0622                                       cln,
0623                                       byte,
0624                                       count,
0625                                       buf);
0626             if (0 > ret)
0627               rc = -1;
0628             else
0629               cmpltd = ret;
0630         }
0631         else
0632         {
0633             ret = fat_file_write_fat32_or_non_root_dir(fs_info,
0634                                                        fat_fd,
0635                                                        start,
0636                                                        count,
0637                                                        buf);
0638             if (0 > ret)
0639               rc = -1;
0640             else
0641               cmpltd = ret;
0642         }
0643     }
0644     if (RC_OK != rc)
0645         return rc;
0646     else
0647         return cmpltd;
0648 }
0649 
0650 /* fat_file_extend --
0651  *     Extend fat-file. If new length less than current fat-file size -
0652  *     do nothing. Otherwise calculate necessary count of clusters to add,
0653  *     allocate it and add new clusters chain to the end of
0654  *     existing clusters chain.
0655  *
0656  * PARAMETERS:
0657  *     fs_info    - FS info
0658  *     fat_fd     - fat-file descriptor
0659  *     new_length - new length
0660  *     a_length   - placeholder for result - actual new length of file
0661  *
0662  * RETURNS:
0663  *     RC_OK and new length of file on success, or -1 if error occurred (errno
0664  *     set appropriately)
0665  */
0666 int
0667 fat_file_extend(
0668     fat_fs_info_t                        *fs_info,
0669     fat_file_fd_t                        *fat_fd,
0670     bool                                  zero_fill,
0671     uint32_t                              new_length,
0672     uint32_t                             *a_length
0673     )
0674 {
0675     int            rc = RC_OK;
0676     uint32_t       chain = 0;
0677     uint32_t       bytes2add = 0;
0678     uint32_t       cls2add = 0;
0679     uint32_t       old_last_cl;
0680     uint32_t       last_cl = 0;
0681     uint32_t       bytes_remain = 0;
0682     uint32_t       cls_added;
0683     ssize_t        bytes_written;
0684 
0685     *a_length = new_length;
0686 
0687     if (new_length <= fat_fd->fat_file_size)
0688         return RC_OK;
0689 
0690     if ((FAT_FD_OF_ROOT_DIR(fat_fd)) &&
0691         (fs_info->vol.type & (FAT_FAT12 | FAT_FAT16)))
0692         rtems_set_errno_and_return_minus_one( ENOSPC );
0693 
0694     bytes_remain = (fs_info->vol.bpc -
0695                    (fat_fd->fat_file_size & (fs_info->vol.bpc - 1))) &
0696                    (fs_info->vol.bpc - 1);
0697 
0698     bytes2add = new_length - fat_fd->fat_file_size;
0699 
0700     if (bytes2add > bytes_remain)
0701         bytes2add -= bytes_remain;
0702     else
0703         bytes2add = 0;
0704 
0705     if (zero_fill && bytes_remain > 0) {
0706         uint32_t start = fat_fd->fat_file_size;
0707         uint32_t cl_start = start >> fs_info->vol.bpc_log2;
0708         uint32_t ofs = start & (fs_info->vol.bpc - 1);
0709         uint32_t cur_cln;
0710 
0711         rc = fat_file_lseek(fs_info, fat_fd, cl_start, &cur_cln);
0712         if (rc != RC_OK)
0713             return rc;
0714 
0715         bytes_written = fat_cluster_set (fs_info, cur_cln, ofs, bytes_remain, 0);
0716         if (bytes_remain != bytes_written)
0717             return -1;
0718     }
0719 
0720     /*
0721      * if in last cluster allocated for the file there is enough room to
0722      * handle extention (hence we don't need to add even one cluster to the
0723      * file ) - return
0724      */
0725     if (bytes2add == 0)
0726         return RC_OK;
0727 
0728     cls2add = ((bytes2add - 1) >> fs_info->vol.bpc_log2) + 1;
0729 
0730     rc = fat_scan_fat_for_free_clusters(fs_info, &chain, cls2add,
0731                                         &cls_added, &last_cl, zero_fill);
0732 
0733     /* this means that low level I/O error occurred */
0734     if (rc != RC_OK)
0735         return rc;
0736 
0737     /* this means that no space left on device */
0738     if ((cls_added == 0) && (bytes_remain == 0))
0739         rtems_set_errno_and_return_minus_one(ENOSPC);
0740 
0741     /*  check wether we satisfied request for 'cls2add' clusters */
0742     if (cls2add != cls_added)
0743     {
0744         uint32_t missing = (cls2add - cls_added) << fs_info->vol.bpc_log2;
0745 
0746         new_length -= bytes2add < missing ? bytes2add : missing;
0747     }
0748 
0749     if (cls_added > 0)
0750     {
0751         /* add new chain to the end of existing */
0752         if ( fat_fd->fat_file_size == 0 )
0753         {
0754             fat_fd->map.disk_cln = chain;
0755             fat_fd->map.file_cln = 0;
0756             fat_file_set_first_cluster_num(fat_fd, chain);
0757         }
0758         else
0759         {
0760             if (fat_fd->map.last_cln != FAT_UNDEFINED_VALUE)
0761             {
0762                 old_last_cl = fat_fd->map.last_cln;
0763             }
0764             else
0765             {
0766                 rc = fat_file_ioctl(fs_info, fat_fd, F_CLU_NUM,
0767                                     (fat_fd->fat_file_size - 1), &old_last_cl);
0768                 if ( rc != RC_OK )
0769                 {
0770                     fat_free_fat_clusters_chain(fs_info, chain);
0771                     return rc;
0772                 }
0773             }
0774 
0775             rc = fat_set_fat_cluster(fs_info, old_last_cl, chain);
0776             if ( rc != RC_OK )
0777             {
0778                 fat_free_fat_clusters_chain(fs_info, chain);
0779                 return rc;
0780             }
0781             fat_buf_release(fs_info);
0782         }
0783 
0784         /* update number of the last cluster of the file */
0785         fat_fd->map.last_cln = last_cl;
0786 
0787         if (fat_fd->fat_file_type == FAT_DIRECTORY)
0788         {
0789             rc = fat_init_clusters_chain(fs_info, chain);
0790             if ( rc != RC_OK )
0791             {
0792                 fat_free_fat_clusters_chain(fs_info, chain);
0793                 return rc;
0794             }
0795         }
0796     }
0797 
0798     *a_length = new_length;
0799     fat_file_set_file_size(fat_fd, new_length);
0800 
0801     return RC_OK;
0802 }
0803 
0804 /* fat_file_truncate --
0805  *     Truncate fat-file. If new length greater than current fat-file size -
0806  *     do nothing. Otherwise find first cluster to free and free all clusters
0807  *     in the chain starting from this cluster.
0808  *
0809  * PARAMETERS:
0810  *     fs_info    - FS info
0811  *     fat_fd     - fat-file descriptor
0812  *     new_length - new length
0813  *
0814  * RETURNS:
0815  *     RC_OK on success, or -1 if error occurred (errno set appropriately)
0816  */
0817 int
0818 fat_file_truncate(
0819     fat_fs_info_t                        *fs_info,
0820     fat_file_fd_t                        *fat_fd,
0821     uint32_t                              new_length
0822     )
0823 {
0824     int            rc = RC_OK;
0825     uint32_t       cur_cln = 0;
0826     uint32_t       cl_start = 0;
0827     uint32_t       new_last_cln = FAT_UNDEFINED_VALUE;
0828 
0829 
0830     if ( new_length >= fat_fd->fat_file_size )
0831         return rc;
0832 
0833     assert(fat_fd->fat_file_size);
0834 
0835     cl_start = (new_length + fs_info->vol.bpc - 1) >> fs_info->vol.bpc_log2;
0836 
0837     if ((cl_start << fs_info->vol.bpc_log2) >= fat_fd->fat_file_size)
0838         return RC_OK;
0839 
0840     if (cl_start != 0)
0841     {
0842         rc = fat_file_lseek(fs_info, fat_fd, cl_start - 1, &new_last_cln);
0843         if (rc != RC_OK)
0844             return rc;
0845 
0846     }
0847 
0848     rc = fat_file_lseek(fs_info, fat_fd, cl_start, &cur_cln);
0849     if (rc != RC_OK)
0850         return rc;
0851 
0852     rc = fat_free_fat_clusters_chain(fs_info, cur_cln);
0853     if (rc != RC_OK)
0854         return rc;
0855 
0856     if (cl_start != 0)
0857     {
0858         rc = fat_set_fat_cluster(fs_info, new_last_cln, FAT_GENFAT_EOC);
0859         if ( rc != RC_OK )
0860             return rc;
0861         fat_fd->map.file_cln = cl_start - 1;
0862         fat_fd->map.disk_cln = new_last_cln;
0863         fat_fd->map.last_cln = new_last_cln;
0864     }
0865     else
0866     {
0867         fat_file_set_first_cluster_num(fat_fd, FAT_GENFAT_FREE);
0868         fat_fd->map.file_cln = FAT_UNDEFINED_VALUE;
0869         fat_fd->map.disk_cln = FAT_UNDEFINED_VALUE;
0870         fat_fd->map.last_cln = FAT_UNDEFINED_VALUE;
0871     }
0872     return RC_OK;
0873 }
0874 
0875 /* fat_file_ioctl --
0876  *     F_CLU_NUM:
0877  *         make mapping between serial number of the cluster in fat-file and
0878  *         its real number on the volume
0879  *
0880  * PARAMETERS:
0881  *     fat_fd     - fat-file descriptor
0882  *     fs_info    - FS info
0883  *     cmd        - command
0884  *     ...
0885  *
0886  * RETURNS:
0887  *     RC_OK on success, or -1 if error occurred and errno set appropriately
0888  */
0889 int
0890 fat_file_ioctl(
0891     fat_fs_info_t                        *fs_info,
0892     fat_file_fd_t                        *fat_fd,
0893     int                                   cmd,
0894     ...)
0895 {
0896     int            rc = RC_OK;
0897     uint32_t       cur_cln = 0;
0898     uint32_t       cl_start = 0;
0899     uint32_t       pos = 0;
0900     uint32_t      *ret;
0901     va_list        ap;
0902 
0903     va_start(ap, cmd);
0904 
0905     switch (cmd)
0906     {
0907         case F_CLU_NUM:
0908             pos = va_arg(ap, uint32_t);
0909             ret = va_arg(ap, uint32_t *);
0910 
0911             /* sanity check */
0912             if ( pos >= fat_fd->fat_file_size ) {
0913                 va_end(ap);
0914                 rtems_set_errno_and_return_minus_one( EIO );
0915             }
0916 
0917             if ((FAT_FD_OF_ROOT_DIR(fat_fd)) &&
0918                 (fs_info->vol.type & (FAT_FAT12 | FAT_FAT16)))
0919             {
0920                 /* cluster 0 (zero) reserved for root dir */
0921                 *ret  = 0;
0922                 rc = RC_OK;
0923                 break;
0924             }
0925 
0926             cl_start = pos >> fs_info->vol.bpc_log2;
0927 
0928             rc = fat_file_lseek(fs_info, fat_fd, cl_start, &cur_cln);
0929             if ( rc != RC_OK )
0930                 break;
0931 
0932             *ret = cur_cln;
0933             break;
0934 
0935         default:
0936             errno = EINVAL;
0937             rc = -1;
0938             break;
0939     }
0940     va_end(ap);
0941     return rc;
0942 }
0943 
0944 /* fat_file_mark_removed --
0945  *     Remove the fat-file descriptor from "valid" hash table, insert it
0946  *     into "removed-but-still-open" hash table and set up "removed" bit.
0947  *
0948  * PARAMETERS:
0949  *     fat_fd     - fat-file descriptor
0950  *     fs_info    - FS info
0951  *
0952  * RETURNS:
0953  *     None
0954  */
0955 void
0956 fat_file_mark_removed(
0957     fat_fs_info_t                        *fs_info,
0958     fat_file_fd_t                        *fat_fd
0959     )
0960 {
0961     uint32_t       key = 0;
0962 
0963     key = fat_construct_key(fs_info, &fat_fd->dir_pos.sname);
0964 
0965     _hash_delete(fs_info->vhash, key, fat_fd->ino, fat_fd);
0966 
0967     _hash_insert(fs_info->rhash, key, fat_fd->ino, fat_fd);
0968 
0969     fat_fd->flags |= FAT_FILE_REMOVED;
0970 }
0971 
0972 /* fat_file_size --
0973  *     Calculate fat-file size - fat-file is nothing that clusters chain, so
0974  *     go through all clusters in the chain and count it. Only
0975  *     special case is root directory for FAT12/16 volumes.
0976  *     This function is used only for directories which are fat-files with
0977  *     non-zero length, hence 'fat_fd->cln' always contains valid data.
0978  *     Calculated size is stored in 'fat_file_size' field of fat-file
0979  *     descriptor.
0980  *
0981  * PARAMETERS:
0982  *     fs_info  - FS info
0983  *     fat_fd   - fat-file descriptor
0984  *
0985  * RETURNS:
0986  *     RC_OK on success, or -1 if error occurred (errno set appropriately)
0987  */
0988 int
0989 fat_file_size(
0990     fat_fs_info_t                        *fs_info,
0991     fat_file_fd_t                        *fat_fd
0992     )
0993 {
0994     int            rc = RC_OK;
0995     uint32_t       cur_cln = fat_fd->cln;
0996     uint32_t       save_cln = 0;
0997 
0998     /* Have we requested root dir size for FAT12/16? */
0999     if ((FAT_FD_OF_ROOT_DIR(fat_fd)) &&
1000         (fs_info->vol.type & (FAT_FAT12 | FAT_FAT16)))
1001     {
1002         fat_fd->fat_file_size = fs_info->vol.rdir_size;
1003         return rc;
1004     }
1005 
1006     fat_fd->fat_file_size = 0;
1007 
1008     while ((cur_cln & fs_info->vol.mask) < fs_info->vol.eoc_val)
1009     {
1010         save_cln = cur_cln;
1011         rc = fat_get_fat_cluster(fs_info, cur_cln, &cur_cln);
1012         if ( rc != RC_OK )
1013             return rc;
1014 
1015         fat_fd->fat_file_size += fs_info->vol.bpc;
1016     }
1017     fat_fd->map.last_cln = save_cln;
1018     return rc;
1019 }
1020 
1021 /* hash support routines */
1022 
1023 /* _hash_insert --
1024  *     Insert elemnt into hash based on key 'key1'
1025  *
1026  * PARAMETERS:
1027  *     hash - hash element will be inserted into
1028  *     key1 - key on which insertion is based on
1029  *     key2 - not used during insertion
1030  *     el   - element to insert
1031  *
1032  * RETURNS:
1033  *     None
1034  */
1035 static inline void
1036 _hash_insert(rtems_chain_control *hash, uint32_t   key1, uint32_t   key2,
1037              fat_file_fd_t *el)
1038 {
1039     rtems_chain_append_unprotected((hash) + ((key1) % FAT_HASH_MODULE), &(el)->link);
1040 }
1041 
1042 
1043 /* _hash_delete --
1044  *     Remove element from hash
1045  *
1046  * PARAMETERS:
1047  *     hash - hash element will be removed from
1048  *     key1 - not used
1049  *     key2 - not used
1050  *     el   - element to delete
1051  *
1052  * RETURNS:
1053  *     None
1054  */
1055 static inline void
1056 _hash_delete(rtems_chain_control *hash, uint32_t   key1, uint32_t   key2,
1057              fat_file_fd_t *el)
1058 {
1059     rtems_chain_extract_unprotected(&(el)->link);
1060 }
1061 
1062 /* _hash_search --
1063  *     Search element in hash. If both keys match pointer to found element
1064  *     is returned
1065  *
1066  * PARAMETERS:
1067  *     fs_info  - FS info
1068  *     hash     - hash element will be removed from
1069  *     key1     - search key
1070  *     key2     - search key
1071  *     ret      - placeholder for result
1072  *
1073  * RETURNS:
1074  *     0 and pointer to found element on success, -1 otherwise
1075  */
1076 static inline int
1077 _hash_search(
1078     const fat_fs_info_t                   *fs_info,
1079     rtems_chain_control                   *hash,
1080     uint32_t                               key1,
1081     uint32_t                               key2,
1082     fat_file_fd_t                          **ret
1083     )
1084 {
1085     uint32_t          mod = (key1) % FAT_HASH_MODULE;
1086     rtems_chain_node *the_node = rtems_chain_first(hash + mod);
1087 
1088     for ( ; !rtems_chain_is_tail((hash) + mod, the_node) ; )
1089     {
1090         fat_file_fd_t *ffd = (fat_file_fd_t *)the_node;
1091         uint32_t       ck =  fat_construct_key(fs_info, &ffd->dir_pos.sname);
1092 
1093         if ( (key1) == ck)
1094         {
1095             if ( ((key2) == 0) || ((key2) == ffd->ino) )
1096             {
1097                 *ret = (void *)the_node;
1098                 return 0;
1099             }
1100         }
1101         the_node = the_node->next;
1102     }
1103     return -1;
1104 }
1105 
1106 static off_t
1107 fat_file_lseek(
1108     fat_fs_info_t                         *fs_info,
1109     fat_file_fd_t                         *fat_fd,
1110     uint32_t                               file_cln,
1111     uint32_t                              *disk_cln
1112     )
1113 {
1114     int rc = RC_OK;
1115 
1116     if (file_cln == fat_fd->map.file_cln)
1117         *disk_cln = fat_fd->map.disk_cln;
1118     else
1119     {
1120         uint32_t   cur_cln;
1121         uint32_t   count;
1122         uint32_t   i;
1123 
1124         if (file_cln > fat_fd->map.file_cln)
1125         {
1126             cur_cln = fat_fd->map.disk_cln;
1127             count = file_cln - fat_fd->map.file_cln;
1128         }
1129         else
1130         {
1131             cur_cln = fat_fd->cln;
1132             count = file_cln;
1133         }
1134 
1135         /* skip over the clusters */
1136         for (i = 0; i < count; i++)
1137         {
1138             rc = fat_get_fat_cluster(fs_info, cur_cln, &cur_cln);
1139             if ( rc != RC_OK )
1140                 return rc;
1141         }
1142 
1143         /* update cache */
1144         fat_fd->map.file_cln = file_cln;
1145         fat_fd->map.disk_cln = cur_cln;
1146 
1147         *disk_cln = cur_cln;
1148     }
1149     return RC_OK;
1150 }