Back to home page

LXR

 
 

    


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

0001 /**
0002  * @file
0003  *
0004  * @ingroup libfs_msdos MSDOS FileSystem
0005  *
0006  * @brief MSDOS Directory Handlers Implementation
0007  */
0008 
0009 /*
0010  *  Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia
0011  *  Author: Eugeny S. Mints <Eugeny.Mints@oktet.ru>
0012  *
0013  *  Modifications to support UTF-8 in the file system are
0014  *  Copyright (c) 2013 embedded brains GmbH & Co. KG
0015  *
0016  *  The license and distribution terms for this file may be
0017  *  found in the file LICENSE in this distribution or at
0018  *  http://www.rtems.org/license/LICENSE.
0019  */
0020 #ifdef HAVE_CONFIG_H
0021 #include "config.h"
0022 #endif
0023 
0024 #include <ctype.h>
0025 #include <stdlib.h>
0026 #include <unistd.h>
0027 #include <errno.h>
0028 #include <rtems/libio_.h>
0029 #include <sys/types.h>
0030 #include <sys/stat.h>
0031 
0032 #include <dirent.h>
0033 
0034 #include "fat.h"
0035 #include "fat_fat_operations.h"
0036 #include "fat_file.h"
0037 
0038 #include "msdos.h"
0039 
0040 
0041 
0042 /*  msdos_dir_read --
0043  *      This routine will read the next directory entry based on the directory
0044  *      offset. The offset should be equal to -n- time the size of an
0045  *      individual dirent structure. If n is not an integer multiple of the
0046  *      sizeof a dirent structure, an integer division will be performed to
0047  *      determine directory entry that will be returned in the buffer. Count
0048  *      should reflect -m- times the sizeof dirent bytes to be placed in the
0049  *      buffer.
0050  *      If there are not -m- dirent elements from the current directory
0051  *      position to the end of the exisiting file, the remaining entries will
0052  *      be placed in the buffer and the returned value will be equal to
0053  *      -m actual- times the size of a directory entry.
0054  *
0055  * PARAMETERS:
0056  *     iop    - file control block
0057  *     buffer - buffer provided by user
0058  *     count  - count of bytes to read
0059  *
0060  * RETURNS:
0061  *     the number of bytes read on success, or -1 if error occurred (errno
0062  *     set apropriately).
0063  */
0064 ssize_t
0065 msdos_dir_read(rtems_libio_t *iop, void *buffer, size_t count)
0066 {
0067     int                rc = RC_OK;
0068     int                eno = 0;
0069     msdos_fs_info_t   *fs_info = iop->pathinfo.mt_entry->fs_info;
0070     rtems_dosfs_convert_control *converter = fs_info->converter;
0071     const rtems_dosfs_convert_handler *convert_handler = converter->handler;
0072     fat_file_fd_t     *fat_fd = iop->pathinfo.node_access;
0073     fat_file_fd_t     *tmp_fat_fd = NULL;
0074     struct dirent      tmp_dirent;
0075     size_t             lfn_len = 0;
0076     uint16_t          *lfn_buf = converter->buffer.data;
0077     char              *sfn_buf = converter->buffer.data;
0078     const size_t       buf_size = converter->buffer.size;
0079     uint32_t           start = 0;
0080     ssize_t            ret = 0;
0081     ssize_t            cmpltd = 0;
0082     uint32_t           j = 0, i = 0;
0083     uint32_t           bts2rd = 0;
0084     uint32_t           cur_cln = 0;
0085     uint32_t           lfn_start = FAT_FILE_SHORT_NAME;
0086     uint8_t            lfn_checksum = 0;
0087     int                lfn_entries = 0;
0088     bool               is_first_entry;
0089 
0090     msdos_fs_lock(fs_info);
0091 
0092     /*
0093      * cast start and count - protect against using sizes that are not exact
0094      * multiples of the -dirent- size. These could result in unexpected
0095      * results
0096      */
0097     start = iop->offset / sizeof(struct dirent);
0098     count = (count / sizeof(struct dirent)) * sizeof(struct dirent);
0099 
0100     /*
0101      * optimization: we know that root directory for FAT12/16 volumes is
0102      * sequential set of sectors and any cluster is sequential set of sectors
0103      * too, so read such set of sectors is quick operation for low-level IO
0104      * layer.
0105      */
0106     bts2rd = (FAT_FD_OF_ROOT_DIR(fat_fd) &&
0107              (fs_info->fat.vol.type & (FAT_FAT12 | FAT_FAT16))) ?
0108              fat_fd->fat_file_size                              :
0109              fs_info->fat.vol.bpc;
0110 
0111     while (count > 0 && cmpltd >= 0)
0112     {
0113         /*
0114          * fat-file is already opened by open call, so read it
0115          * Always read directory fat-file from the beggining because of MSDOS
0116          * directories feature :( - we should count elements currently
0117          * present in the directory because there may be holes :)
0118          */
0119         ret = fat_file_read(&fs_info->fat, fat_fd, (j * bts2rd),
0120                             bts2rd, fs_info->cl_buf);
0121         if (ret < MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE)
0122         {
0123             msdos_fs_unlock(fs_info);
0124             rtems_set_errno_and_return_minus_one(EIO);
0125         }
0126 
0127         for (i = 0; i < ret && cmpltd >= 0; i += MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE)
0128         {
0129             char* entry = (char*) fs_info->cl_buf + i;
0130 
0131             /*
0132              * Is this directory from here on empty ?
0133              */
0134             if ((*MSDOS_DIR_ENTRY_TYPE(entry)) ==
0135                 MSDOS_THIS_DIR_ENTRY_AND_REST_EMPTY)
0136             {
0137                 msdos_fs_unlock(fs_info);
0138                 return cmpltd;
0139             }
0140 
0141             /* Is the directory entry empty */
0142             if ((*MSDOS_DIR_ENTRY_TYPE(entry)) == MSDOS_THIS_DIR_ENTRY_EMPTY)
0143                 continue;
0144 
0145             /* Is the directory entry empty a volume label */
0146             if (((*MSDOS_DIR_ATTR(entry)) & MSDOS_ATTR_VOLUME_ID) &&
0147                 ((*MSDOS_DIR_ATTR(entry) & MSDOS_ATTR_LFN_MASK) != MSDOS_ATTR_LFN))
0148                 continue;
0149 
0150             /*
0151              * Check the attribute to see if the entry is for a long file
0152              * name.
0153              */
0154             if ((*MSDOS_DIR_ATTR(entry) & MSDOS_ATTR_LFN_MASK) ==
0155                 MSDOS_ATTR_LFN)
0156             {
0157                 int offset_lfn;
0158 
0159                 /*
0160                  * Is this is the first entry of a LFN ?
0161                  */
0162                 if (lfn_start == FAT_FILE_SHORT_NAME)
0163                 {
0164                     is_first_entry = true;
0165                     /*
0166                      * The first entry must have the last long entry flag set.
0167                      */
0168                     if ((*MSDOS_DIR_ENTRY_TYPE(entry) &
0169                          MSDOS_LAST_LONG_ENTRY) == 0)
0170                         continue;
0171 
0172                     /*
0173                      * Remember the start location of the long file name.
0174                      */
0175                     lfn_start =
0176                       ((j * bts2rd) + i) / MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE;
0177 
0178                     /*
0179                      * Get the number of entries so we can count down and
0180                      * also the checksum of the short entry.
0181                      */
0182                     lfn_entries = (*MSDOS_DIR_ENTRY_TYPE(entry) &
0183                                    MSDOS_LAST_LONG_ENTRY_MASK);
0184                     lfn_len = 0;
0185                     lfn_checksum = *MSDOS_DIR_LFN_CHECKSUM(entry);
0186                     memset (tmp_dirent.d_name, 0, sizeof(tmp_dirent.d_name));
0187                 }
0188                 else
0189                     is_first_entry = false;
0190 
0191                 /*
0192                  * If the entry number or the check sum do not match
0193                  * forget this series of long directory entries. These could
0194                  * be orphaned entries depending on the history of the
0195                  * disk.
0196                  */
0197                 if ((lfn_entries != (*MSDOS_DIR_ENTRY_TYPE(entry) &
0198                                      MSDOS_LAST_LONG_ENTRY_MASK)) ||
0199                     (lfn_checksum != *MSDOS_DIR_LFN_CHECKSUM(entry)))
0200                 {
0201                     lfn_start = FAT_FILE_SHORT_NAME;
0202                     continue;
0203                 }
0204 
0205                 /*
0206                  * Extract the file name into the directory entry. The data is
0207                  * stored in UNICODE characters (16bit). No translation is
0208                  * done for the possibly partial entry.
0209                  * Once all entries have been assembled to a UTF-16 file name,
0210                  * this file name will get converted to UTF-8.
0211                  *
0212                  * The DOS maximum length is 255 characters without the
0213                  * trailing nul character. We need to range check the length to
0214                  * fit in the directory entry name field.
0215                  */
0216 
0217                 lfn_entries--;
0218                 offset_lfn = lfn_entries * MSDOS_LFN_LEN_PER_ENTRY;
0219                 lfn_len += msdos_get_utf16_string_from_long_entry (
0220                   entry,
0221                   &lfn_buf[offset_lfn],
0222                   buf_size - offset_lfn,
0223                   is_first_entry
0224                 );
0225             }
0226             else
0227             {
0228                 fat_dir_pos_t dir_pos;
0229 
0230                 /*
0231                  * Skip active entries until get the entry to start from.
0232                  */
0233                 if (start)
0234                 {
0235                     lfn_start = FAT_FILE_SHORT_NAME;
0236                     start--;
0237                     continue;
0238                 }
0239 
0240 #ifdef DT_DIR
0241                 if ((*MSDOS_DIR_ATTR(entry)) & MSDOS_ATTR_DIRECTORY)
0242                 {
0243                     tmp_dirent.d_type = DT_DIR;
0244                 }
0245                 else
0246                 {
0247                     tmp_dirent.d_type = DT_REG;
0248                 }
0249 #endif
0250 
0251                 /*
0252                  * Move the entry to the return buffer
0253                  *
0254                  * unfortunately there is no method to extract ino except to
0255                  * open fat-file descriptor :( ... so, open it
0256                  */
0257 
0258                 /* get number of cluster we are working with */
0259                 rc = fat_file_ioctl(&fs_info->fat, fat_fd, F_CLU_NUM,
0260                                     j * bts2rd, &cur_cln);
0261                 if (rc != RC_OK)
0262                 {
0263                     msdos_fs_unlock(fs_info);
0264                     return rc;
0265                 }
0266 
0267                 fat_dir_pos_init(&dir_pos);
0268                 dir_pos.sname.cln = cur_cln;
0269                 dir_pos.sname.ofs = i;
0270                 rc = fat_file_open(&fs_info->fat, &dir_pos, &tmp_fat_fd);
0271                 if (rc != RC_OK)
0272                 {
0273                     msdos_fs_unlock(fs_info);
0274                     return rc;
0275                 }
0276 
0277                 /* fill in dirent structure */
0278                 /* XXX: from what and in what d_off should be computed ?! */
0279                 tmp_dirent.d_off = start + cmpltd;
0280                 tmp_dirent.d_reclen = sizeof(struct dirent);
0281                 tmp_dirent.d_ino = tmp_fat_fd->ino;
0282 
0283                 /*
0284                  * If a long file name check if the correct number of entries
0285                  * have been found and if the checksum is correct and if it is
0286                  * convertable to utf8 string.  If not return the short file
0287                  * name.
0288                  */
0289                 if (lfn_start != FAT_FILE_SHORT_NAME)
0290                 {
0291                     if (lfn_entries == 0 &&
0292                         lfn_checksum == msdos_lfn_checksum(entry)) {
0293                         size_t len = sizeof(tmp_dirent.d_name) - 1;
0294 
0295                         eno = (*convert_handler->utf16_to_utf8) (
0296                             converter,
0297                             lfn_buf,
0298                             lfn_len,
0299                             (uint8_t *) &tmp_dirent.d_name[0],
0300                             &len);
0301                         if (eno == 0) {
0302                             tmp_dirent.d_namlen = len;
0303                             tmp_dirent.d_name[len] = '\0';
0304                         } else {
0305                             lfn_start = FAT_FILE_SHORT_NAME;
0306                         }
0307                     } else {
0308                         lfn_start = FAT_FILE_SHORT_NAME;
0309                     }
0310                 }
0311 
0312                 if (lfn_start == FAT_FILE_SHORT_NAME) {
0313                     size_t len = sizeof(tmp_dirent.d_name) - 1;
0314 
0315                     /*
0316                      * convert dir entry from fixed 8+3 format (without dot)
0317                      * to 0..8 + 1dot + 0..3 format
0318                      */
0319                     tmp_dirent.d_namlen = msdos_format_dirent_with_dot(
0320                         sfn_buf, entry); /* src text */
0321                     eno = (*convert_handler->codepage_to_utf8) (
0322                         converter,
0323                         sfn_buf,
0324                         tmp_dirent.d_namlen,
0325                         (uint8_t *) &tmp_dirent.d_name[0],
0326                         &len);
0327                     if ( 0 == eno ) {
0328                       tmp_dirent.d_namlen = len;
0329                       tmp_dirent.d_name[len] = '\0';
0330                     } else {
0331                         cmpltd = -1;
0332                         errno  = eno;
0333                     }
0334                 }
0335 
0336                 if ( cmpltd >= 0 ) {
0337                     memcpy(buffer + cmpltd, &tmp_dirent, sizeof(struct dirent));
0338 
0339                     iop->offset = iop->offset + sizeof(struct dirent);
0340                     cmpltd += (sizeof(struct dirent));
0341                     count -= (sizeof(struct dirent));
0342                 }
0343 
0344                 /* inode number extracted, close fat-file */
0345                 rc = fat_file_close(&fs_info->fat, tmp_fat_fd);
0346                 if (rc != RC_OK)
0347                 {
0348                     msdos_fs_unlock(fs_info);
0349                     return rc;
0350                 }
0351             }
0352 
0353             if (count <= 0)
0354                 break;
0355         }
0356         j++;
0357     }
0358 
0359     msdos_fs_unlock(fs_info);
0360     return cmpltd;
0361 }
0362 
0363 /* msdos_dir_write --
0364  *     no write for directory
0365  */
0366 
0367 /* msdos_dir_stat --
0368  *
0369  * This routine will obtain the following information concerning the current
0370  * directory:
0371  *     st_dev      device id
0372  *     st_ino      node serial number :)
0373  *     st_mode     mode extracted from the node
0374  *     st_size     total size in bytes
0375  *     st_blksize  blocksize for filesystem I/O
0376  *     st_blocks   number of blocks allocated
0377  *     stat_mtime  time of last modification
0378  *
0379  * PARAMETERS:
0380  *     loc - this directory
0381  *     buf - stat buffer provided by user
0382  *
0383  * RETURNS:
0384  *     RC_OK and filled stat buffer on success, or -1 if error occurred (errno
0385  *     set apropriately).
0386  */
0387 int
0388 msdos_dir_stat(
0389     const rtems_filesystem_location_info_t *loc,
0390     struct stat *buf
0391 )
0392 {
0393     msdos_fs_info_t   *fs_info = loc->mt_entry->fs_info;
0394     fat_file_fd_t     *fat_fd = loc->node_access;
0395 
0396     msdos_fs_lock(fs_info);
0397 
0398     buf->st_dev = fs_info->fat.vol.dev;
0399     buf->st_ino = fat_fd->ino;
0400     buf->st_mode  = S_IFDIR | S_IRWXU | S_IRWXG | S_IRWXO;
0401     buf->st_rdev = 0ll;
0402     buf->st_size = fat_fd->fat_file_size;
0403     buf->st_blocks = fat_fd->fat_file_size >> FAT_SECTOR512_BITS;
0404     buf->st_blksize = fs_info->fat.vol.bps;
0405     buf->st_atime = fat_fd->mtime;
0406     buf->st_ctime = fat_fd->ctime;
0407     buf->st_mtime = fat_fd->mtime;
0408 
0409     msdos_fs_unlock(fs_info);
0410     return RC_OK;
0411 }
0412 
0413 /* msdos_dir_truncate --
0414  *     No truncate for directory.
0415  *
0416  * PARAMETERS:
0417  *
0418  * RETURNS:
0419  *
0420  */